Files
Web_FreeCAD_Bitbybit/scripts/check-freecad-property-linklisthidden-roundtrip.mjs
wangdequan 7bd7ff5055
Some checks failed
real-verification / chrome (push) Has been cancelled
real-verification / freecad-oracle (push) Has been cancelled
real-verification / wasm (push) Has been cancelled
feat: close hidden link property evidence
2026-08-17 17:31:34 -04:00

27 lines
5.8 KiB
JavaScript

import { createHash } from 'node:crypto'
import { isDeepStrictEqual } from 'node:util'
import { readFile, stat } from 'node:fs/promises'
import { resolve } from 'node:path'
const root = resolve(new URL('..', import.meta.url).pathname)
const report = JSON.parse(await readFile(resolve(root, 'config/freecad-property-linklisthidden-roundtrip.json'), 'utf8'))
const fail = (message) => { throw new Error(`FreeCAD PropertyLinkListHidden roundtrip check failed: ${message}`) }
if (report.schemaVersion !== 1 || report.status !== 'pass' || report.baselineId !== 'freecad-1.1.1-property-linklisthidden-roundtrip' || report.freecadVersion !== '1.1.1' || report.gitCommit !== '0108fd4b4850cc46e625b60e53cea7a7bbe69f8d' || report.propertyType !== 'App::PropertyLinkListHidden') fail('baseline is invalid')
const initial = ['ExportA', 'ExportB']
const target = ['ExportB', 'ExportA', 'ExportB']
const objectSet = [{ name: 'SketchProbe', typeId: 'Sketcher::SketchObject' }, { name: 'ExportA', typeId: 'Part::Feature' }, { name: 'ExportB', typeId: 'Part::Feature' }]
for (const [phase, snapshot, value] of [['nativeInitial', report.nativeInitial, initial], ['nativeReopened', report.nativeAfter?.reopened, target], ['nativeResaved', report.nativeAfter?.resaved, target]]) {
if (!isDeepStrictEqual(snapshot?.objectSet, objectSet) || snapshot.object?.name !== 'SketchProbe' || snapshot.object.typeId !== 'Sketcher::SketchObject' || snapshot.object.propertyName !== 'Exports' || snapshot.object.propertyTypeId !== 'App::PropertyLinkListHidden' || snapshot.object.group !== 'Sketch' || !isDeepStrictEqual(snapshot.object.value, value) || !isDeepStrictEqual(snapshot.object.propertyStatus, ['26']) || !isDeepStrictEqual(snapshot.object.editorMode, ['Hidden']) || !isDeepStrictEqual(snapshot.object.state, ['Up-to-date']) || snapshot.object.statusString !== 'Valid' || !isDeepStrictEqual(snapshot.object.outList, [])) fail(`${phase} native hidden-link semantics changed`)
const ownerShape = snapshot.object.shape
if (ownerShape?.isNull !== true || ownerShape.valid !== false || ownerShape.shapeType !== 'Null' || ownerShape.solids !== 0 || ownerShape.faces !== 0 || ownerShape.edges !== 0 || ownerShape.vertices !== 0 || ownerShape.brepSha256 !== null) fail(`${phase} owner Shape boundary changed`)
const box = snapshot.targets?.ExportA
const cylinder = snapshot.targets?.ExportB
if (box?.typeId !== 'Part::Feature' || !isDeepStrictEqual(box.inList, []) || box.shape?.isNull !== false || box.shape.valid !== true || box.shape.shapeType !== 'Solid' || box.shape.solids !== 1 || box.shape.faces !== 6 || box.shape.edges !== 12 || box.shape.vertices !== 8 || box.shape.area !== 52 || box.shape.volume !== 24 || !/^[0-9a-f]{64}$/.test(box.shape.brepSha256)) fail(`${phase} box target changed`)
if (cylinder?.typeId !== 'Part::Feature' || !isDeepStrictEqual(cylinder.inList, []) || cylinder.shape?.isNull !== false || cylinder.shape.valid !== true || cylinder.shape.shapeType !== 'Solid' || cylinder.shape.solids !== 1 || cylinder.shape.faces !== 3 || cylinder.shape.edges !== 3 || cylinder.shape.vertices !== 2 || cylinder.shape.area !== 61.261056745 || cylinder.shape.volume !== 35.342917353 || !/^[0-9a-f]{64}$/.test(cylinder.shape.brepSha256)) fail(`${phase} cylinder target changed`)
}
if (report.web.before?.typeId !== 'App::PropertyLinkListHidden' || report.web.before.element !== 'LinkList' || report.web.after?.typeId !== 'App::PropertyLinkListHidden' || report.web.after.element !== 'LinkList' || report.web.targetMarkedTouched !== true || !isDeepStrictEqual(report.web.initialDependencyTargets, initial) || !isDeepStrictEqual(report.web.webDependencyTargets, initial) || report.web.hiddenDependencyMetadataPreserved !== true || report.web.webOutputMatches !== true || report.web.objectSetPreserved !== true || report.web.semanticObjectsPreserved !== true || report.web.opaqueEntriesPreserved !== true || !Number.isSafeInteger(report.web.opaquePathsPreserved) || report.web.opaquePathsPreserved < 1) fail('Web edit did not preserve the native archive boundary')
if (!isDeepStrictEqual(report.classification?.requestedValue, target) || !isDeepStrictEqual(report.classification.webValue, target) || !isDeepStrictEqual(report.classification.reopenedValue, target) || !isDeepStrictEqual(report.classification.resavedValue, target) || report.classification.nativeOutputMatches !== true || report.classification.hiddenLinksPreserved !== true || !isDeepStrictEqual(report.classification.nativeResavedDependencyTargets, target) || report.classification.nativeDependencyMetadataNormalized !== true || report.classification.targetShapeStructurePreserved !== true || report.classification.ownerShapePreserved !== true || report.classification.unknownSemanticDrift !== false || report.classification.zeroUnknownDrift !== true || report.classification.nativeTargetBrepHashes?.length !== 3) fail('roundtrip classification is incomplete')
for (const hashes of report.classification.nativeTargetBrepHashes) if (!/^[0-9a-f]{64}$/.test(hashes.ExportA) || !/^[0-9a-f]{64}$/.test(hashes.ExportB)) fail('native target BREP evidence is incomplete')
for (const archive of Object.values(report.archives ?? {})) { const path = resolve(root, archive.path ?? ''); const content = await readFile(path); if ((await stat(path)).size !== archive.bytes || createHash('sha256').update(content).digest('hex') !== archive.sha256) fail(`archive is stale: ${archive.path}`) }
console.log(JSON.stringify({ status: 'freecad-property-linklisthidden-roundtrip-pass', values: { nativeInitial: initial, webEdited: report.classification.webValue, nativeReopened: report.classification.reopenedValue, nativeResaved: report.classification.resavedValue }, hiddenLinksPreserved: report.classification.hiddenLinksPreserved, targetShapeStructurePreserved: report.classification.targetShapeStructurePreserved, opaquePathsPreserved: report.web.opaquePathsPreserved, zeroUnknownDrift: report.classification.zeroUnknownDrift }, null, 2))