76 lines
4.6 KiB
JavaScript
76 lines
4.6 KiB
JavaScript
import fs from 'node:fs';
|
|
import { loadWasmProbe } from './wasm-probe-runtime.mjs';
|
|
import { writeBusinessReport } from './business-report.mjs';
|
|
|
|
const cases=[];
|
|
let kernel;
|
|
const record=(name,input,actual,passed)=>cases.push({input,actual,assertions:[{name,passed:Boolean(passed)}]});
|
|
try {
|
|
({kernel}=await loadWasmProbe('web/public/kernel','FreeCADWebBimRuntimeProbe'));
|
|
const python=source=>{
|
|
const value=JSON.parse(kernel.ccall('fc_web_bim_test_python','string',['string'],[
|
|
'import FreeCAD as App, Part, json, IfcOpenShellNative\n'+source]));
|
|
if(value.ok===false) throw Error(value.error);
|
|
return value;
|
|
};
|
|
const fixture=fs.readFileSync(new URL('../freecad-source/build-web/ifcopenshell-native/fixtures/ColumnPSetsOfSets.ifc',import.meta.url));
|
|
const original=python(`
|
|
import base64
|
|
with open('/tmp/semantic-independent.ifc','wb') as stream:
|
|
stream.write(base64.b64decode(${JSON.stringify(fixture.toString('base64'))}))
|
|
d=App.newDocument('SemanticSource')
|
|
IfcOpenShellNative.insert('/tmp/semantic-independent.ifc',d.Name)
|
|
def snapshot(document):
|
|
return [{'guid':o.IfcGlobalId,'type':o.IfcType,'label':o.Label,
|
|
'properties':json.loads(o.IfcPropertySets) if 'IfcPropertySets' in o.PropertiesList else [],
|
|
'volume':o.Shape.Volume if o.isDerivedFrom('Part::Feature') else None,
|
|
'children':sorted(c.IfcGlobalId for c in o.Group if 'IfcGlobalId' in c.PropertiesList) if 'Group' in o.PropertiesList else []}
|
|
for o in document.Objects if 'IfcGlobalId' in o.PropertiesList]
|
|
result=json.dumps({'document':d.Name,'objects':snapshot(d)})
|
|
`);
|
|
const exports=[];
|
|
for(let attempt=0;attempt<2;attempt++) {
|
|
const value=python(`
|
|
d=App.getDocument(${JSON.stringify(original.document)})
|
|
IfcOpenShellNative.exportDocument(d.Name,'/tmp/semantic-independent-output.ifc')
|
|
r=App.newDocument('SemanticRead${attempt}')
|
|
IfcOpenShellNative.insert('/tmp/semantic-independent-output.ifc',r.Name)
|
|
result=json.dumps({'document':r.Name,'objects':[{'guid':o.IfcGlobalId,'type':o.IfcType,'label':o.Label,
|
|
'properties':json.loads(o.IfcPropertySets) if 'IfcPropertySets' in o.PropertiesList else [],
|
|
'volume':o.Shape.Volume if o.isDerivedFrom('Part::Feature') else None,
|
|
'children':sorted(c.IfcGlobalId for c in o.Group if 'IfcGlobalId' in c.PropertiesList) if 'Group' in o.PropertiesList else []}
|
|
for o in r.Objects if 'IfcGlobalId' in o.PropertiesList]})
|
|
App.closeDocument(r.Name)
|
|
`);
|
|
exports.push(value);
|
|
}
|
|
const source=original.objects.find(o=>o.volume>0);
|
|
for(const [attempt,value] of exports.entries()) {
|
|
const output=value.objects.find(o=>o.volume>0);
|
|
record('Export preserves the real source IFC class',{attempt,type:source.type},output?.type,output?.type===source.type);
|
|
record('Export preserves the source GlobalId across repeated exports',{attempt,guid:source.guid},output?.guid,output?.guid===source.guid);
|
|
record('Export preserves typed IFC property sets',{attempt,properties:source.properties},output?.properties,
|
|
source.properties.length>0&&JSON.stringify(output?.properties)===JSON.stringify(source.properties));
|
|
const originalGroups=original.objects.filter(o=>o.volume===null).sort((a,b)=>a.guid.localeCompare(b.guid));
|
|
const outputGroups=value.objects.filter(o=>o.volume===null).sort((a,b)=>a.guid.localeCompare(b.guid));
|
|
const hierarchy=rows=>rows.map(({guid,type,label,children})=>({guid,type,label,children}));
|
|
record('Export preserves spatial hierarchy identities and relationships',{attempt,hierarchy:hierarchy(originalGroups)},hierarchy(outputGroups),
|
|
originalGroups.length>0&&JSON.stringify(hierarchy(originalGroups))===JSON.stringify(hierarchy(outputGroups)));
|
|
record('Semantic export preserves native volume and units',{attempt,volume:source.volume},output?.volume,
|
|
Math.abs((output?.volume??NaN)-source.volume)<1);
|
|
}
|
|
python(`
|
|
import os
|
|
for d in list(App.listDocuments()): App.closeDocument(d)
|
|
for path in ['/tmp/semantic-independent.ifc','/tmp/semantic-independent-output.ifc']:
|
|
if os.path.exists(path): os.remove(path)
|
|
result=json.dumps({'documents':list(App.listDocuments())})
|
|
`);
|
|
} catch(error) {
|
|
record('Independent native semantic verification completes',{fixture:'ColumnPSetsOfSets.ifc'},String(error.stack??error),false);
|
|
} finally {kernel?.PThread?.terminateAllThreads?.();}
|
|
const passed=cases.length>=10&&cases.every(c=>c.assertions.every(a=>a.passed));
|
|
await writeBusinessReport({schema:'freecad-web-independent-semantic-verification/v1',status:passed?'passed':'failed',cases,
|
|
scope:'Independent source identity, hierarchy, typed properties and geometry checks. Full semantic acceptance also requires every declared proof area.'});
|
|
process.exit(passed?0:1);
|