DAG: execute recompute levels concurrently
This commit is contained in:
@@ -277,6 +277,50 @@ test('generation-aware recompute cancels an older run before accepting its resul
|
||||
version = 2
|
||||
})
|
||||
|
||||
test('recompute executes independent dependency levels concurrently and merges in stable order', async () => {
|
||||
const document: DocumentSnapshot = {
|
||||
...recomputeDocumentFixture(),
|
||||
tree: [
|
||||
{ id: 'root', label: 'Root', type: 'feature', state: 'dirty' },
|
||||
{ id: 'left', label: 'Left', type: 'feature', state: 'dirty' },
|
||||
{ id: 'right', label: 'Right', type: 'feature', state: 'dirty' },
|
||||
],
|
||||
objects: [
|
||||
{ id: 'root', typeId: 'Part::Feature', properties: [] },
|
||||
{ id: 'left', typeId: 'Part::Feature', properties: [] },
|
||||
{ id: 'right', typeId: 'Part::Feature', properties: [] },
|
||||
],
|
||||
dependencies: [
|
||||
{ sourceId: 'left', targetId: 'root', relation: 'link' },
|
||||
{ sourceId: 'right', targetId: 'root', relation: 'link' },
|
||||
],
|
||||
recompute: {
|
||||
generation: 0,
|
||||
status: 'idle',
|
||||
objectStates: { root: 'touched', left: 'touched', right: 'touched' },
|
||||
dirtyObjects: ['root'],
|
||||
order: [],
|
||||
errors: [],
|
||||
},
|
||||
}
|
||||
let active = 0
|
||||
let maximumActive = 0
|
||||
const coordinator = new RecomputeCoordinator(async (object) => {
|
||||
active += 1
|
||||
maximumActive = Math.max(maximumActive, active)
|
||||
await new Promise((resolve) => setTimeout(resolve, 5))
|
||||
active -= 1
|
||||
return { status: 'success', updatedObject: object }
|
||||
}, () => 1)
|
||||
|
||||
const result = await coordinator.run(document)
|
||||
assert.equal(result.status, 'completed')
|
||||
assert.equal(maximumActive, 2)
|
||||
assert.deepEqual(result.order, ['root', 'left', 'right'])
|
||||
assert.deepEqual(result.completed, ['root', 'left', 'right'])
|
||||
assert.deepEqual(result.levels, [['root'], ['left', 'right']])
|
||||
})
|
||||
|
||||
test('recompute rejects results from an obsolete document version', async () => {
|
||||
let version = 1
|
||||
const coordinator = new RecomputeCoordinator(async () => {
|
||||
@@ -395,6 +439,17 @@ test('manifest commands without a Bitbybit executor are explicitly unsupported',
|
||||
assert.equal(facade.getState().document.version, before)
|
||||
})
|
||||
|
||||
test('implemented commands enforce their FreeCAD workbench preconditions', () => {
|
||||
const facade = createMockFacade()
|
||||
facade.gui.workbench.setActive('Sketcher')
|
||||
assert.equal(facade.gui.command.getState('create-body').status, 'disabled')
|
||||
assert.match(facade.gui.command.getState('create-body').reason || '', /Part Design/)
|
||||
assert.equal(facade.gui.command.getState('new-sketch').status, 'enabled')
|
||||
facade.gui.workbench.setActive('Part Design')
|
||||
assert.equal(facade.gui.command.getState('new-sketch').status, 'disabled')
|
||||
assert.equal(facade.gui.command.getState('pocket').status, 'enabled')
|
||||
})
|
||||
|
||||
test('command events carry a document-scoped request context', () => {
|
||||
const facade = createMockFacade()
|
||||
const events: string[] = []
|
||||
|
||||
Reference in New Issue
Block a user