feat: add candidate FreeCAD naming SDK and attachment oracles
Build and verify the candidate-only FreeCAD naming bridge and OCCT worker path, including three-stage StringHasher restoration. Add Datum, ShapeBinder, attachment-mode, and PartDesign structure oracles plus offline SDK build plans and CI boundary checks.
This commit is contained in:
@@ -1,12 +1,21 @@
|
||||
import test from 'node:test'
|
||||
import assert from 'node:assert/strict'
|
||||
import { readFile } from 'node:fs/promises'
|
||||
import { resolve } from 'node:path'
|
||||
import { mkdtemp, readFile, rm, writeFile } from 'node:fs/promises'
|
||||
import { join, resolve } from 'node:path'
|
||||
import { tmpdir } from 'node:os'
|
||||
import { inspectWasmStaticArchive } from '../scripts/freecad-naming-sdk-lib.mjs'
|
||||
|
||||
const root = resolve(new URL('..', import.meta.url).pathname)
|
||||
|
||||
const arArchive = (member, bytes) => {
|
||||
const field = (value, length) => String(value).padEnd(length, ' ')
|
||||
const header = `${field(`${member}/`, 16)}${field(0, 12)}${field(0, 6)}${field(0, 6)}${field('100644', 8)}${field(bytes.length, 10)}` + '`\n'
|
||||
return Buffer.concat([Buffer.from('!<arch>\n'), Buffer.from(header), bytes, ...(bytes.length % 2 ? [Buffer.from('\n')] : [])])
|
||||
}
|
||||
|
||||
test('Qt wasm Core build pins the released QtBase source and project Emscripten baseline', async () => {
|
||||
const source = await readFile(resolve(root, 'scripts/build-qt6-wasm-core.sh'), 'utf8')
|
||||
const freecadQt = await readFile(resolve(root, 'scripts/build-qt6-freecad-wasm.sh'), 'utf8')
|
||||
assert.match(source, /QT_VERSION="6\.8\.2"/)
|
||||
assert.match(source, /012043ce6d411e6e8a91fdc4e05e6bedcfa10fcb1347d3c33908f7fdd10dfe05/)
|
||||
assert.match(source, /Emscripten 3\.1\.69/)
|
||||
@@ -14,19 +23,45 @@ test('Qt wasm Core build pins the released QtBase source and project Emscripten
|
||||
assert.match(source, /WEB_FREECAD_OFFLINE/)
|
||||
assert.match(source, /cmake --install "\$\{QT_BUILD_DIR\}\/src\/corelib"/)
|
||||
assert.doesNotMatch(source, /cmake --install[^\n]+\|\| true/)
|
||||
assert.match(freecadQt, /Core Concurrent Network Xml/)
|
||||
assert.match(freecadQt, /feature-thread/)
|
||||
assert.match(freecadQt, /mkspecs\/wasm-emscripten/)
|
||||
assert.match(freecadQt, /inspectWasmStaticArchive/)
|
||||
})
|
||||
|
||||
test('FreeCAD source probe pins the exact private source inputs and does not export the production ABI', async () => {
|
||||
const build = await readFile(resolve(root, 'scripts/build-freecad-naming-source-probe.sh'), 'utf8')
|
||||
const smoke = await readFile(resolve(root, 'native/freecad-naming-probe/smoke-test.mjs'), 'utf8')
|
||||
const candidate = await readFile(resolve(root, 'native/freecad-naming-probe/abi-candidate-smoke.ts'), 'utf8')
|
||||
assert.match(build, /0108fd4b4850cc46e625b60e53cea7a7bbe69f8d/)
|
||||
assert.match(build, /73e0e60a9d6ee06851252e2071ebdd58698f8f99733903232529b3a194164435/)
|
||||
assert.match(build, /90173aba5f331ac9453589833f7e5fc63c5c80a35f1600dbc71211606b1c9dbc/)
|
||||
assert.match(build, /fd32f35c9c2a0c21a60fa634ec37ec594d6f331924c5bba7fdbbcb351c3b612c/)
|
||||
assert.match(build, /8f02d27a8b672e9a56764f85e394265b4fcf023aba097f040b03dcb38bc393d2/)
|
||||
assert.match(build, /StringHasher\.cpp/)
|
||||
assert.match(build, /Base\/Handle\.cpp/)
|
||||
assert.match(build, /f5448d9de693e319460d5e3ef52c63a54e0948963e4b92786aac38c77bdcd7a5/)
|
||||
assert.match(build, /fc3223857ca6d2990d2b48f437f75841cdf23e1e79fa18ef18296f40acd3cdc3/)
|
||||
assert.match(build, /e1ceaedb624688ddffa6a834c1b32539e8ba5bbdc0ec31401ea087e6a9c3eb52/)
|
||||
assert.match(build, /MappedElement\.cpp/)
|
||||
assert.match(build, /ElementNamingUtils\.cpp/)
|
||||
assert.match(build, /ElementMap\.cpp/)
|
||||
assert.match(build, /libFreeCADPrivateNamingProbe\.a/)
|
||||
assert.match(build, /emar rcs/)
|
||||
assert.match(build, /git -C "\$\{FREECAD_SOURCE_DIR\}" diff --quiet/)
|
||||
assert.doesNotMatch(build, /public\/native\/freecad-naming-probe/)
|
||||
for (const callback of ['freecadNamingAbiVersion', 'freecadNamingCapabilitiesJson', 'freecadNamingEvidenceJson']) {
|
||||
assert.match(smoke, new RegExp(callback))
|
||||
}
|
||||
assert.match(smoke, /productionWorkerLinked: false/)
|
||||
assert.match(smoke, /freecadPrivateStringHasherSourceProbe/)
|
||||
assert.match(smoke, /freecadPrivateElementMapSourceProbe/)
|
||||
assert.match(smoke, /archive\.memberCount !== 7/)
|
||||
assert.match(smoke, /productionEligible: false/)
|
||||
assert.match(candidate, /captureFreeCadPrivateNamingEvidence/)
|
||||
assert.match(candidate, /parseElementMap2/)
|
||||
assert.match(candidate, /productionCallbacksExported: false/)
|
||||
assert.match(candidate, /productionWorkerLinked: false/)
|
||||
})
|
||||
|
||||
test('private naming boundary checker cross-validates all authoritative status files', async () => {
|
||||
@@ -35,8 +70,148 @@ test('private naming boundary checker cross-validates all authoritative status f
|
||||
'config/freecad-sketcher-partdesign-abi-contract.json',
|
||||
'config/compatibility-matrix.json',
|
||||
'config/freecad-web-exact-parity-plan.json',
|
||||
'config/freecad-private-naming-source-readiness.json',
|
||||
'config/freecad-naming-sdk-plan.json',
|
||||
]) assert.match(source, new RegExp(path.replaceAll('.', '\\.')))
|
||||
assert.match(source, /shippedWorkerImplementation !== 'not-linked'/)
|
||||
assert.match(source, /task\.status !== 'in_progress'/)
|
||||
assert.match(source, /systemExactEvaluation\?\.exact !== false/)
|
||||
assert.match(source, /production\?\.workerLinked !== false/)
|
||||
assert.match(source, /candidateAbi\?\.scope !== 'isolated-non-production'/)
|
||||
assert.match(source, /callback\.startsWith\('freecadNamingCandidate'\)/)
|
||||
assert.match(source, /FreeCAD private naming Worker bridge/)
|
||||
assert.match(source, /sdkPlan\.productionPublication !== false/)
|
||||
})
|
||||
|
||||
test('SDK readiness verifies the complete candidate wasm closure and keeps it pre-production', async () => {
|
||||
const plan = JSON.parse(await readFile(resolve(root, 'config/freecad-naming-sdk-plan.json'), 'utf8'))
|
||||
const readiness = await readFile(resolve(root, 'scripts/check-freecad-naming-sdk-readiness.mjs'), 'utf8')
|
||||
const sdkBuildPlan = await readFile(resolve(root, 'config/freecad-wasm-sdk-build-plan.json'), 'utf8')
|
||||
const sdkBootstrap = await readFile(resolve(root, 'config/freecad-wasm-sdk-bootstrap.cmake'), 'utf8')
|
||||
const sdkCompat = await readFile(resolve(root, 'config/freecad-wasm-sdk-compat.h'), 'utf8')
|
||||
const sdkExecinfo = await readFile(resolve(root, 'config/wasm-compat/execinfo.h'), 'utf8')
|
||||
const sdkToolchain = await readFile(resolve(root, 'config/emscripten-freecad-candidate-toolchain.cmake'), 'utf8')
|
||||
const sdkConfigure = await readFile(resolve(root, 'scripts/configure-freecad-naming-sdk-candidate.mjs'), 'utf8')
|
||||
const sdkBuild = await readFile(resolve(root, 'scripts/build-freecad-naming-sdk-candidate.sh'), 'utf8')
|
||||
const sdkOverlay = await readFile(resolve(root, 'scripts/prepare-freecad-occt8-overlay.sh'), 'utf8')
|
||||
const sdkCheck = await readFile(resolve(root, 'scripts/check-freecad-naming-sdk.mjs'), 'utf8')
|
||||
const sdkManifest = await readFile(resolve(root, 'scripts/generate-freecad-naming-sdk-manifest.mjs'), 'utf8')
|
||||
const bridgeBuild = await readFile(resolve(root, 'scripts/build-freecad-naming-bridge-candidate.sh'), 'utf8')
|
||||
const bridgeSmoke = await readFile(resolve(root, 'native/freecad-naming-bridge/smoke-test.ts'), 'utf8')
|
||||
const bridgeCheck = await readFile(resolve(root, 'scripts/check-freecad-naming-bridge-candidate.mjs'), 'utf8')
|
||||
const workerBuild = await readFile(resolve(root, 'native/occt-history/build.sh'), 'utf8')
|
||||
const workerSmoke = await readFile(resolve(root, 'native/occt-history/freecad-naming-candidate-smoke.ts'), 'utf8')
|
||||
const chromeRun = await readFile(resolve(root, 'scripts/run-chrome-freecad-naming-worker-candidate.mjs'), 'utf8')
|
||||
const chromeCheck = await readFile(resolve(root, 'scripts/check-chrome-freecad-naming-worker-candidate.mjs'), 'utf8')
|
||||
assert.equal(plan.productionPublication, false)
|
||||
assert.deepEqual(plan.libraries.map(({ name }) => name), ['FreeCADBase', 'FreeCADApp', 'Part', 'QtCore', 'Python'])
|
||||
assert.deepEqual(plan.linkDependencies.map(({ name }) => name), ['QtConcurrent', 'QtNetwork', 'QtXml', 'QtBundledPcre2', 'QtBundledZLIB', 'yaml-cpp', 'ICUCommon', 'ICUI18N', 'ICUData', 'XercesC', 'BoostProgramOptions', 'BoostRegex', 'BoostThread', 'BoostDateTime', 'BoostAtomic', 'PythonMpdecimal', 'PythonExpat', 'PythonHaclSha2', 'PythonZlib', 'PythonBzip2', 'PythonSqlite3'])
|
||||
assert.equal(plan.compileOptions.cxxStandard, 'c++20')
|
||||
assert.equal(plan.compileOptions.pthread, true)
|
||||
assert.deepEqual(plan.runtimeAssets.map(({ name }) => name), ['PythonWasmStdlib'])
|
||||
assert.match(readiness, /inspectPlannedLibrary/)
|
||||
assert.match(readiness, /publishToWorker: false/)
|
||||
assert.match(readiness, /inspectPlannedRuntimeAsset/)
|
||||
assert.match(sdkCheck, /inspectWasmStaticArchive/)
|
||||
assert.match(sdkCheck, /hostAdapterSha256/)
|
||||
assert.match(sdkCheck, /forceIncludeSha256/)
|
||||
assert.match(sdkCheck, /PythonWasmStdlib/)
|
||||
assert.match(sdkManifest, /runtimeAssets/)
|
||||
assert.match(sdkManifest, /productionPublication: false/)
|
||||
assert.match(sdkBuildPlan, /candidate-only-freecad-wasm-sdk-build/)
|
||||
assert.match(sdkBuildPlan, /productionPublication": false/)
|
||||
assert.match(sdkBootstrap, /FREECAD_WASM_EMBED_TRANSLATIONS OFF/)
|
||||
assert.match(sdkBootstrap, /function\(qt_add_translation output_variable\)/)
|
||||
assert.match(sdkBootstrap, /add_compile_definitions\(__linux__=1\)/)
|
||||
assert.match(sdkBootstrap, /FREECAD_WASM_OCCT_COMPAT_INCLUDE/)
|
||||
assert.match(sdkBootstrap, /TopTools_ListOfShape\.hxx/)
|
||||
assert.match(sdkBootstrap, /set\(UNIX FALSE\)/)
|
||||
assert.match(sdkToolchain, /Platform\/Emscripten\.cmake/)
|
||||
assert.match(sdkToolchain, /set\(UNIX FALSE\)/)
|
||||
assert.match(sdkToolchain, /CMAKE_CXX_CREATE_SHARED_LIBRARY/)
|
||||
assert.match(sdkBootstrap, /freecad-wasm-sdk-compat\.h/)
|
||||
assert.match(sdkCompat, /#include <TopTools_ListOfShape\.hxx>/)
|
||||
assert.match(sdkCompat, /#include <TopTools_HSequenceOfShape\.hxx>/)
|
||||
assert.match(sdkCompat, /#define systemTimeZone\(\) UTC/)
|
||||
assert.doesNotMatch(sdkExecinfo, /backtrace\s*\(/)
|
||||
assert.match(sdkConfigure, /Qt6_DIR/)
|
||||
assert.match(sdkConfigure, /Python3_INCLUDE_DIRS/)
|
||||
assert.match(sdkConfigure, /FREECAD_WASM_OFFLINE_SYSROOT/)
|
||||
assert.match(sdkConfigure, /src\/Deprecated\/NCollectionAliases/)
|
||||
assert.match(sdkConfigure, /source-occt8/)
|
||||
assert.match(sdkOverlay, /migrate_raise_to_throw\.py/)
|
||||
assert.match(sdkOverlay, /replace_typedefs\.py/)
|
||||
assert.match(sdkOverlay, /collected_typedefs\.json/)
|
||||
assert.match(sdkOverlay, /TopTools_ListIteratorOfListOfShape\.hxx\|TopTools_ListOfShape\.hxx/)
|
||||
assert.match(sdkOverlay, /removed_iterator_headers/)
|
||||
assert.match(sdkOverlay, /e\.DynamicType\(\)->get_type_name\(\)\|e\.ExceptionType\(\)/)
|
||||
assert.match(sdkOverlay, /legacy_exception_rtti/)
|
||||
assert.match(sdkOverlay, /BRepLProp_CurveTool::FirstParameter\(adapt\)\|adapt\.FirstParameter\(\)/)
|
||||
assert.match(sdkOverlay, /legacy_curve_tool/)
|
||||
assert.match(sdkOverlay, /Geom2dLProp_CLProps2d\.hxx>\|#include <GeomLProp_CLProps\.hxx>/)
|
||||
assert.match(sdkOverlay, /legacy_geom2d_lprop/)
|
||||
assert.match(sdkOverlay, /std::vector<TopoShape>\{e1, e2, e3, e4\}/)
|
||||
assert.match(sdkOverlay, /ambiguous_shape_mapper_calls/)
|
||||
assert.match(sdkOverlay, /Standard_Failure inherits std::exception/)
|
||||
assert.match(sdkOverlay, /unreachable_occt_exception_fallbacks/)
|
||||
assert.match(sdkOverlay, /worktree add --detach/)
|
||||
assert.match(sdkOverlay, /git -C "\$\{FREECAD_SOURCE_DIR\}" diff --quiet/)
|
||||
assert.match(sdkConfigure, /FreeCADBase|BUILD_PART/)
|
||||
assert.match(sdkConfigure, /callbacks: \[\]/)
|
||||
assert.match(sdkBuild, /cmake --build "\$\{BUILD_DIR\}" --target Part/)
|
||||
assert.match(sdkBuild, /libFreeCADBase\.a/)
|
||||
assert.match(sdkBuild, /ElementMap\.cpp\.o/)
|
||||
assert.match(sdkBuild, /productionManifestGenerated: false/)
|
||||
assert.match(sdkBuild, /availability: 'unavailable'/)
|
||||
assert.match(bridgeBuild, /libHacl_Hash_SHA2\.a/)
|
||||
assert.match(bridgeBuild, /--preload-file/)
|
||||
assert.match(bridgeBuild, /FREECAD_NAMING_BRIDGE_REPORT/)
|
||||
assert.match(bridgeBuild, /public\/native\/freecad-naming-bridge/)
|
||||
assert.match(bridgeSmoke, /freecad-private-naming-isolated-bridge-pass/)
|
||||
assert.match(bridgeSmoke, /thirdStage/)
|
||||
assert.match(bridgeSmoke, /tamperedHasherRejected/)
|
||||
assert.match(bridgeSmoke, /inconsistentTablesRejected/)
|
||||
assert.match(bridgeSmoke, /productionWorkerLinked: false/)
|
||||
assert.match(bridgeCheck, /thirdStage\?\.stringHasherEntries <= report\.chainedStage\.stringHasherEntries/)
|
||||
assert.match(bridgeCheck, /stale artifact evidence/)
|
||||
assert.match(workerBuild, /OCCT_HISTORY_PUBLISH/)
|
||||
assert.match(workerBuild, /m\.runtimeAssets/)
|
||||
assert.match(workerBuild, /m\.compileOptions\.definitions/)
|
||||
assert.match(workerBuild, /OCCT_CXX_FLAGS\+=" -pthread"/)
|
||||
assert.match(workerSmoke, /booleanHistoryFromStep/)
|
||||
assert.match(workerSmoke, /thirdStage/)
|
||||
assert.match(workerSmoke, /invalidHistoryRejected: true/)
|
||||
assert.match(chromeRun, /crossOriginIsolated/)
|
||||
assert.match(chromeRun, /thirdStage/)
|
||||
assert.match(chromeRun, /productionWorkerLinked: false/)
|
||||
assert.match(chromeCheck, /thirdStage\?\.stringHasherEntries <= report\.chainedStage\.stringHasherEntries/)
|
||||
assert.match(chromeCheck, /stale artifact evidence/)
|
||||
const fixtureDir = await mkdtemp(join(tmpdir(), 'freecad-naming-sdk-'))
|
||||
try {
|
||||
const wasmArchive = join(fixtureDir, 'libwasm.a')
|
||||
const hostArchive = join(fixtureDir, 'libhost.a')
|
||||
await writeFile(wasmArchive, arArchive('wasm.o', Buffer.from([0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00])))
|
||||
await writeFile(hostArchive, arArchive('host.o', Buffer.from([0x7f, 0x45, 0x4c, 0x46, 0x02, 0x01, 0x01, 0x00])))
|
||||
const wasm = await inspectWasmStaticArchive(wasmArchive)
|
||||
assert.equal(wasm.target, 'wasm32-emscripten')
|
||||
assert.equal(wasm.wasmObjectCount, 1)
|
||||
await assert.rejects(inspectWasmStaticArchive(hostArchive), /host ELF object/)
|
||||
} finally {
|
||||
await rm(fixtureDir, { recursive: true, force: true })
|
||||
}
|
||||
})
|
||||
|
||||
test('real wasm and offline smoke lanes rebuild and execute the source prerequisite', async () => {
|
||||
const real = await readFile(resolve(root, 'scripts/run-real-verification.mjs'), 'utf8')
|
||||
const offline = await readFile(resolve(root, 'scripts/offline-resource-lib.mjs'), 'utf8')
|
||||
for (const source of [real, offline]) {
|
||||
assert.match(source, /build:freecad-naming-source-probe/)
|
||||
assert.match(source, /test:freecad-naming-source-probe/)
|
||||
assert.match(source, /check:freecad-naming-sdk-readiness/)
|
||||
assert.match(source, /check:freecad-wasm-sdk-build-plan/)
|
||||
}
|
||||
assert.match(real, /build:qt6-wasm-core/)
|
||||
assert.match(real, /probe:freecad-attachment-modes/)
|
||||
assert.match(real, /name\.startsWith\('test:chrome-'\)/)
|
||||
assert.match(real, /requestedPhase === 'execute' \? chromeExecution : chromeChecks/)
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user