108 lines
6.3 KiB
JavaScript
108 lines
6.3 KiB
JavaScript
import assert from 'node:assert/strict'
|
|
import { readFile } from 'node:fs/promises'
|
|
import test from 'node:test'
|
|
|
|
const report = JSON.parse(await readFile(new URL('../config/freecad-cam-path-oracle.json', import.meta.url), 'utf8'))
|
|
|
|
const commandGcode = (object) => object.commands.map((command) => command.gcode)
|
|
const objectByName = (objects, name) => objects.find((object) => object.name === name)
|
|
|
|
test('native Profile oracle is a locked FreeCAD algorithm result', () => {
|
|
assert.equal(report.profileAlgorithm.operationTypeId, 'Path::FeaturePython')
|
|
assert.equal(report.profileAlgorithm.pathPropertyType, 'Path::PropertyPath')
|
|
assert.equal(report.profileAlgorithm.commandCount, 32)
|
|
assert.equal(report.profileAlgorithm.cuttingCommandCount, 20)
|
|
assert.equal(report.profileAlgorithm.normalizationToleranceMm, 0.01)
|
|
assert.equal(report.profileAlgorithm.commandSha256, '5fcc36e3fc3c022599096f4110c890401f285b5878030de7c481786470a7a455')
|
|
assert.deepEqual(report.profileAlgorithm.variants.map((variant) => ({ id: variant.id, useComp: variant.useComp, hash: variant.commandSha256 })), [
|
|
{ id: 'outside-cw-tool-comp', useComp: true, hash: '5fcc36e3fc3c022599096f4110c890401f285b5878030de7c481786470a7a455' },
|
|
{ id: 'outside-cw-no-comp', useComp: false, hash: 'c4c8bc58c4b21bc7be8796ff5d074fbe02656e31a26cc91ee65a67537a7d240a' },
|
|
])
|
|
})
|
|
|
|
test('native Helix oracle locks all four cut-direction combinations', () => {
|
|
assert.equal(report.helixAlgorithm.fixture, 'Mod/CAM/CAMTests/test_holes00.fcstd')
|
|
assert.equal(report.helixAlgorithm.baseSubElementCount, 9)
|
|
assert.equal(report.helixAlgorithm.toolDiameterMm, 0.9)
|
|
assert.deepEqual(report.helixAlgorithm.scenarios.map((scenario) => ({
|
|
id: scenario.id,
|
|
direction: scenario.direction,
|
|
arcs: scenario.arcNames,
|
|
arcCount: scenario.arcCommandCount,
|
|
hash: scenario.commandSha256,
|
|
})), [
|
|
{ id: 'inside-conventional', direction: 'CW', arcs: ['G2'], arcCount: 1260, hash: '6499583c26dbf01694abc0a98fdbeb9d240cce0f73d32634284ddfa132030e0b' },
|
|
{ id: 'outside-climb', direction: 'CW', arcs: ['G2'], arcCount: 1260, hash: '37ee85fe360bfa82235f1b7aefce31cb7150900bb8d6f08751741fce6df18626' },
|
|
{ id: 'inside-climb', direction: 'CCW', arcs: ['G3'], arcCount: 1260, hash: 'f2fd26ca11be5efb037f809fb65949d24956f6ce330f87d152d2674770a27bd8' },
|
|
{ id: 'outside-conventional', direction: 'CCW', arcs: ['G3'], arcCount: 1260, hash: '0220f90245a74a50c1a54cb501439d14e7370d9d9d34fcc89dccdaff345688b7' },
|
|
])
|
|
})
|
|
|
|
test('Path Feature and FeaturePython survive native save-open-mutate-save-open', () => {
|
|
for (const name of ['NativePath', 'PythonPath']) {
|
|
const before = objectByName(report.fcstdRoundTrip.sourceBeforeClose, name)
|
|
const reopened = objectByName(report.fcstdRoundTrip.sourceReopened, name)
|
|
const mutated = objectByName(report.fcstdRoundTrip.mutatedReopened, name)
|
|
assert.deepEqual(commandGcode(reopened), commandGcode(before))
|
|
assert.deepEqual(commandGcode(mutated).slice(0, 5), commandGcode(before))
|
|
assert.equal(mutated.commandCount, 6)
|
|
assert.equal(mutated.commands.at(-1).name, name === 'NativePath' ? 'M5' : 'M2')
|
|
}
|
|
})
|
|
|
|
test('Web byte-transparent proxy reopens natively without changing Path commands', () => {
|
|
assert.equal(report.fcstdRoundTrip.webTransparentPreservation.byteExact, true)
|
|
assert.equal(report.fcstdRoundTrip.webTransparentPreservation.nativeReopenVerified, true)
|
|
for (const name of ['NativePath', 'PythonPath']) {
|
|
const native = objectByName(report.fcstdRoundTrip.sourceReopened, name)
|
|
const webPreserved = objectByName(report.fcstdRoundTrip.webTransparentPreservation.objects, name)
|
|
assert.deepEqual(commandGcode(webPreserved), commandGcode(native))
|
|
}
|
|
assert.equal(report.claims.webEditablePathPropertyCodec, 'verified-native-path-feature')
|
|
assert.equal(report.boundary.exactParityClaim, false)
|
|
})
|
|
|
|
test('Web Path::PropertyPath edit is reopened by FreeCAD while FeaturePython stays blocked', () => {
|
|
const editable = report.fcstdRoundTrip.webEditablePathProperty
|
|
assert.equal(editable.objectName, 'NativePath')
|
|
assert.equal(editable.sourceCommandCount, 5)
|
|
assert.equal(editable.editedCommandCount, 6)
|
|
assert.deepEqual(editable.lastCommand, { name: 'M3', parameters: { S: 12000 } })
|
|
assert.equal(editable.nativeReopenVerified, true)
|
|
assert.equal(editable.featurePythonExecutionBlocked, true)
|
|
})
|
|
|
|
test('FeaturePython Path::PropertyPath resource editing is explicit and non-executing', () => {
|
|
const editable = report.fcstdRoundTrip.webEditableFeaturePythonPathProperty
|
|
assert.equal(editable.objectName, 'PythonPath')
|
|
assert.equal(editable.sourceCommandCount, 5)
|
|
assert.equal(editable.editedCommandCount, 6)
|
|
assert.deepEqual(editable.lastCommand, { name: 'M2', parameters: {} })
|
|
assert.equal(editable.nativeReopenVerified, true)
|
|
assert.equal(editable.scriptExecution, 'blocked')
|
|
assert.equal(editable.optInRequired, true)
|
|
assert.equal(report.claims.webEditableFeaturePythonPathResourceCodec, 'verified-safe-resource-only-opt-in')
|
|
})
|
|
|
|
test('Qt oracle records live CAM actions from the active workbench', () => {
|
|
assert.equal(report.qtDynamicOracle.activeWorkbench, 'CAMWorkbench')
|
|
assert.ok(report.qtDynamicOracle.camCommandCount >= 50)
|
|
assert.ok(report.qtDynamicOracle.requiredCommands.every((command) => (
|
|
command.selectedModel.registered
|
|
&& command.selectedModel.actionCount >= 1
|
|
&& command.selectedModel.text.length > 0
|
|
&& command.selectedModel.iconPresent
|
|
)))
|
|
assert.ok(report.qtDynamicOracle.requiredCommands.some((command) => command.selectionChangesEnabledState))
|
|
})
|
|
|
|
test('Qt oracle records CAM Task panel open, focus fields, and OK/Cancel close semantics', () => {
|
|
const lifecycles = report.qtDynamicOracle.taskLifecycles
|
|
assert.equal(report.qtDynamicOracle.taskLifecycleVerified, true)
|
|
assert.ok(lifecycles.length >= 3)
|
|
assert.ok(lifecycles.every((lifecycle) => lifecycle.dialogOpened && lifecycle.dialogClosed && lifecycle.buttonClicked))
|
|
assert.ok(lifecycles.every((lifecycle) => lifecycle.active.buttonBoxCount === 1 && lifecycle.active.fieldCount >= 10))
|
|
assert.ok(lifecycles.every((lifecycle) => lifecycle.active.buttonTexts.some((button) => button.text === 'OK') && lifecycle.active.buttonTexts.some((button) => button.text === 'Cancel')))
|
|
assert.deepEqual(new Set(lifecycles.map((lifecycle) => lifecycle.action)), new Set(['accept', 'cancel']))
|
|
})
|