40 lines
2.9 KiB
JavaScript
40 lines
2.9 KiB
JavaScript
const finite = (value) => typeof value === 'number' && Number.isFinite(value)
|
|
const toleranceFor = (scenario, path) => path.includes('boundingBox') ? scenario.tolerance.linear : scenario.tolerance.scalar
|
|
const domainFor = (path) => path.includes('boundingBox') ? 'shape' : ['solids', 'faces', 'edges', 'vertices'].some((name) => path.endsWith(`.${name}`)) ? 'topology' : ['volume', 'area'].some((name) => path.endsWith(`.${name}`)) ? 'shape' : path.includes('objects') || path.includes('tree') ? 'objectTree' : path.includes('properties') ? 'properties' : 'identity'
|
|
|
|
export function compareSemanticResult(scenario, actual, { source = 'FreeCAD', target = 'Web' } = {}) {
|
|
const differences = []
|
|
const comparedDomains = new Set()
|
|
const compare = (expected, received, path) => {
|
|
const domain = domainFor(path)
|
|
comparedDomains.add(domain)
|
|
if (typeof expected === 'number') {
|
|
if (!finite(received) || Math.abs(expected - received) > toleranceFor(scenario, path)) differences.push({ kind: 'numeric', classification: 'unknown', domain, path, expected, actual: received, tolerance: toleranceFor(scenario, path) })
|
|
return
|
|
}
|
|
if (Array.isArray(expected)) {
|
|
if (!Array.isArray(received) || received.length !== expected.length) differences.push({ kind: 'structure', classification: 'unknown', domain, path, expected: `array(${expected.length})`, actual: Array.isArray(received) ? `array(${received.length})` : typeof received })
|
|
else expected.forEach((value, index) => compare(value, received[index], `${path}[${index}]`))
|
|
return
|
|
}
|
|
if (expected && typeof expected === 'object') {
|
|
if (!received || typeof received !== 'object' || Array.isArray(received)) { differences.push({ kind: 'structure', classification: 'unknown', domain, path, expected: 'object', actual: typeof received }); return }
|
|
for (const [key, value] of Object.entries(expected)) compare(value, received[key], `${path}.${key}`)
|
|
return
|
|
}
|
|
if (received !== expected) differences.push({ kind: 'value', classification: 'unknown', domain, path, expected, actual: received })
|
|
}
|
|
compare(scenario.expected, actual, scenario.id)
|
|
const allowedRuntimeFields = new Set(['schemaVersion', 'fixtureId', 'freecadVersion', 'freecadVersion', 'runtimeStatus'])
|
|
for (const key of Object.keys(actual || {})) if (!Object.prototype.hasOwnProperty.call(scenario.expected || {}, key) && !allowedRuntimeFields.has(key)) differences.push({ kind: 'field', classification: 'unknown', domain: domainFor(key), path: `${scenario.id}.${key}`, expected: 'absent', actual: actual[key] })
|
|
return {
|
|
schemaVersion: 1,
|
|
fixtureId: scenario.id,
|
|
source,
|
|
target,
|
|
status: differences.length === 0 ? 'pass' : 'fail-unknown-difference',
|
|
domains: Object.fromEntries(['identity', 'shape', 'topology', 'objectTree', 'properties'].map((domain) => [domain, comparedDomains.has(domain) ? 'compared' : 'no-fixture-field'])),
|
|
differences,
|
|
}
|
|
}
|