Files
Web_FreeCAD_Bitbybit/scripts/check-freecad-property-fileincluded-mutation.mjs
wangdequan d11566403d
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 ordered pairs and property codec batches
2026-08-17 04:58:46 -04:00

33 lines
5.1 KiB
JavaScript

import { isDeepStrictEqual } from 'node:util'
import { readFile } from 'node:fs/promises'
import { resolve } from 'node:path'
const root = resolve(new URL('..', import.meta.url).pathname)
const [report, inventory] = await Promise.all([
readFile(resolve(root, 'config/freecad-property-fileincluded-mutation.json'), 'utf8').then(JSON.parse),
readFile(resolve(root, 'config/freecad-property-fileincluded-inventory.json'), 'utf8').then(JSON.parse),
])
const fail = (message) => { throw new Error(`FreeCAD PropertyFileIncluded mutation check failed: ${message}`) }
if (report.schemaVersion !== 1 || report.status !== 'pass' || report.baselineId !== 'freecad-1.1.1-property-fileincluded-mutation' || report.freecadVersion !== '1.1.1' || report.gitCommit !== '0108fd4b4850cc46e625b60e53cea7a7bbe69f8d' || report.propertyType !== 'App::PropertyFileIncluded' || report.caseCount !== 20 || report.cases?.length !== 20) fail('baseline or case count is invalid')
const keys = report.cases.map(({ objectTypeId, propertyName }) => `${objectTypeId}.${propertyName}`)
if (!isDeepStrictEqual(keys, inventory.records.map(({ objectTypeId, propertyName }) => `${objectTypeId}.${propertyName}`))) fail('mutation cases do not cover the locked inventory')
for (const [index, entry] of report.cases.entries()) {
const record = inventory.records[index]
const expectedStatus = record.status.map(String)
const expectedEditor = isDeepStrictEqual(record.status, ['ReadOnly']) ? ['ReadOnly'] : []
const snapshots = [entry.before, entry.edited, entry.editedRecompute?.snapshot, entry.restored, entry.restoredRecompute?.snapshot]
if (!entry.pathStable || !entry.objectSetStable || entry.beforeSource.sha256 === entry.editedSource.sha256 || entry.beforeSource.bytes === entry.editedSource.bytes && entry.beforeSource.sha256 === entry.editedSource.sha256) fail(`${keys[index]} did not produce a distinct edited resource`)
if (entry.beforeSource.sha256 !== entry.before.sha256 || entry.editedSource.sha256 !== entry.edited.sha256 || entry.beforeSource.sha256 !== entry.restored.sha256 || entry.beforeSource.bytes !== entry.before.bytes || entry.editedSource.bytes !== entry.edited.bytes || entry.restored.bytes !== entry.beforeSource.bytes) fail(`${keys[index]} byte/hash mutation or restoration changed`)
if (entry.before.baseName !== entry.archiveName || entry.edited.baseName !== entry.archiveName || entry.restored.baseName !== entry.archiveName || snapshots.some((snapshot) => snapshot.propertyTypeId !== 'App::PropertyFileIncluded' || snapshot.writeBits !== 0 || snapshot.underTransientDir !== true || !isDeepStrictEqual(snapshot.propertyStatus, expectedStatus) || !isDeepStrictEqual(snapshot.editorMode, expectedEditor) || snapshot.exists !== true)) fail(`${keys[index]} path/permission/status metadata changed`)
if (entry.editedRecompute.result !== !isDeepStrictEqual(expectedStatus, ['27']) || entry.restoredRecompute.result !== !isDeepStrictEqual(expectedStatus, ['27'])) fail(`${keys[index]} recompute result changed`)
if (isDeepStrictEqual(expectedStatus, ['27'])) {
if (!isDeepStrictEqual(entry.before.objectState, ['Up-to-date']) || !isDeepStrictEqual(entry.edited.objectState, ['Up-to-date']) || !isDeepStrictEqual(entry.restored.objectState, ['Up-to-date']) || entry.edited.statusString !== 'Valid' || entry.restored.statusString !== 'Valid') fail(`${keys[index]} PropOutput touch suppression changed`)
} else {
if (!isDeepStrictEqual(entry.before.objectState, ['Up-to-date']) || entry.before.statusString !== 'Valid' || !isDeepStrictEqual(entry.edited.objectState, ['Touched']) || !isDeepStrictEqual(entry.restored.objectState, ['Touched']) || entry.edited.statusString !== 'Touched' || entry.restored.statusString !== 'Touched') fail(`${keys[index]} setter touch sequence changed`)
if (!isDeepStrictEqual(entry.editedRecompute.snapshot.objectState, ['Up-to-date']) || entry.editedRecompute.snapshot.statusString !== 'Valid' || !isDeepStrictEqual(entry.restoredRecompute.snapshot.objectState, ['Up-to-date']) || entry.restoredRecompute.snapshot.statusString !== 'Valid') fail(`${keys[index]} recompute recovery changed`)
}
const shapeExpected = entry.objectTypeId === 'Sketcher::SketchObjectSF'
if (entry.restored.shape?.applicable !== shapeExpected || shapeExpected && (entry.before.shape.isNull !== true || entry.edited.shape.isNull !== true || entry.restored.shape.isNull !== true || entry.restored.shape.edges !== 0)) fail(`${keys[index]} Shape boundary changed`)
}
console.log(JSON.stringify({ status: 'freecad-property-fileincluded-mutation-pass', propertyType: report.propertyType, caseCount: report.caseCount, editedCases: report.cases.filter(({ before, edited }) => before.sha256 !== edited.sha256).length, restoredCases: report.cases.filter(({ before, restored }) => before.sha256 === restored.sha256).length, stablePaths: report.cases.filter(({ pathStable }) => pathStable).length, stableObjects: report.cases.filter(({ objectSetStable }) => objectSetStable).length, outputTouchSuppressed: report.cases.find(({ propertyName, objectTypeId }) => objectTypeId === 'TechDraw::DrawSVGTemplate' && propertyName === 'PageResult')?.editedRecompute.result === false }, null, 2))