Remove support for excution orders that re-enter suites

This commit is contained in:
Steve Gravrock
2025-09-27 13:33:25 -07:00
parent 4598e4049c
commit c2ce55580c
7 changed files with 42 additions and 272 deletions

View File

@@ -11336,8 +11336,7 @@ getJasmineRequireObj().TreeProcessor = function(j$) {
const defaultMax = 1 - Infinity;
// Transforms the suite tree into an execution tree, which represents the set
// of specs and (possibly interleaved) suites to be run in the order they are
// to be run in.
// of specs and suites to be run in the order they are to be run in.
class TreeProcessor {
#tree;
#runnableIds;
@@ -11419,15 +11418,7 @@ getJasmineRequireObj().TreeProcessor = function(j$) {
segmentChildren(node, orderedChildren, this.#stats, executableIndex);
if (this.#stats[node.id].segments.length > 1) {
if (node.canBeReentered()) {
j$.getEnv().deprecated(
'The specified spec/suite order splits up a suite, running unrelated specs in the middle of it. This will become an error in a future release.'
);
} else {
throw new Error(
'Invalid order: would cause a beforeAll or afterAll to be run multiple times'
);
}
throw new Error('Invalid order: would split up a suite');
}
}
}
@@ -11445,15 +11436,14 @@ getJasmineRequireObj().TreeProcessor = function(j$) {
}
childrenOfTopSuite() {
return this.childrenOfSuiteSegment(this.topSuite, 0);
return this.childrenOfSuite(this.topSuite);
}
childrenOfSuiteSegment(suite, segmentNumber) {
const segmentChildren = this.#stats[suite.id].segments[segmentNumber]
.nodes;
childrenOfSuite(suite) {
const segmentChildren = this.#stats[suite.id].segments[0].nodes;
return segmentChildren.map(function(child) {
if (child.owner.children) {
return { suite: child.owner, segmentNumber: child.index };
return { suite: child.owner };
} else {
return { spec: child.owner };
}
@@ -11574,7 +11564,7 @@ getJasmineRequireObj().TreeRunner = function(j$) {
async execute() {
this.#hasFailures = false;
await new Promise(resolve => {
this.#executeSuiteSegment(this.#executionTree.topSuite, 0, resolve);
this.#executeSuite(this.#executionTree.topSuite, resolve);
});
return { hasFailures: this.#hasFailures };
}
@@ -11584,7 +11574,7 @@ getJasmineRequireObj().TreeRunner = function(j$) {
return {
fn: done => {
if (node.suite) {
this.#executeSuiteSegment(node.suite, node.segmentNumber, done);
this.#executeSuite(node.suite, done);
} else {
this._executeSpec(node.spec, done);
}
@@ -11674,7 +11664,7 @@ getJasmineRequireObj().TreeRunner = function(j$) {
return fns;
}
#executeSuiteSegment(suite, segmentNumber, done) {
#executeSuite(suite, done) {
const isTopSuite = suite === this.#executionTree.topSuite;
const isExcluded = this.#executionTree.isExcluded(suite);
let befores = [];
@@ -11696,7 +11686,7 @@ getJasmineRequireObj().TreeRunner = function(j$) {
const children = isTopSuite
? this.#executionTree.childrenOfTopSuite()
: this.#executionTree.childrenOfSuiteSegment(suite, segmentNumber);
: this.#executionTree.childrenOfSuite(suite);
const queueableFns = [
...befores,
...this.#wrapNodes(children),

View File

@@ -470,137 +470,6 @@ describe('Runner', function() {
await expectAsync(promise).toBePending();
});
it('runs a suite multiple times if the order specified leaves and re-enters it', async function() {
const spec1 = new StubSpec();
const spec2 = new StubSpec();
const spec3 = new StubSpec();
const spec4 = new StubSpec();
const spec5 = new StubSpec();
const reentered = new StubSuite({ children: [spec1, spec2, spec3] });
const topSuite = new StubSuite({ children: [reentered, spec4, spec5] });
const subject = makeRunner(topSuite);
spyOn(jasmineUnderTest.getEnv(), 'deprecated');
const promise = subject.execute([
spec1.id,
spec4.id,
spec2.id,
spec5.id,
spec3.id
]);
await Promise.resolve();
expect(runQueue).toHaveBeenCalledTimes(1);
const queueableFns = runQueue.calls.mostRecent().args[0].queueableFns;
queueableFns[0].fn();
expect(runQueue.calls.mostRecent().args[0].queueableFns.length).toBe(2);
runQueue.calls.mostRecent().args[0].queueableFns[1].fn('done');
expect(
privateUnderTest.TreeRunner.prototype._executeSpec
).toHaveBeenCalledWith(spec1, 'done');
queueableFns[1].fn('done');
expect(
privateUnderTest.TreeRunner.prototype._executeSpec
).toHaveBeenCalledWith(spec4, 'done');
queueableFns[2].fn();
expect(runQueue.calls.count()).toBe(3);
expect(runQueue.calls.mostRecent().args[0].queueableFns.length).toBe(2);
runQueue.calls.mostRecent().args[0].queueableFns[1].fn('done');
expect(
privateUnderTest.TreeRunner.prototype._executeSpec
).toHaveBeenCalledWith(spec2, 'done');
queueableFns[3].fn('done');
expect(
privateUnderTest.TreeRunner.prototype._executeSpec
).toHaveBeenCalledWith(spec5, 'done');
queueableFns[4].fn();
expect(runQueue.calls.count()).toBe(4);
expect(runQueue.calls.mostRecent().args[0].queueableFns.length).toBe(2);
runQueue.calls.mostRecent().args[0].queueableFns[1].fn('done');
expect(
privateUnderTest.TreeRunner.prototype._executeSpec
).toHaveBeenCalledWith(spec3, 'done');
await expectAsync(promise).toBePending();
});
it('runs a parent of a suite with multiple segments correctly', async function() {
const spec1 = new StubSpec();
const spec2 = new StubSpec();
const spec3 = new StubSpec();
const spec4 = new StubSpec();
const spec5 = new StubSpec();
const parent = new StubSuite({ children: [spec1, spec2, spec3] });
const grandparent = new StubSuite({ children: [parent] });
const topSuite = new StubSuite({ children: [grandparent, spec4, spec5] });
const subject = makeRunner(topSuite);
spyOn(jasmineUnderTest.getEnv(), 'deprecated');
const promise = subject.execute([
spec1.id,
spec4.id,
spec2.id,
spec5.id,
spec3.id
]);
await Promise.resolve();
expect(runQueue).toHaveBeenCalledTimes(1);
const queueableFns = runQueue.calls.mostRecent().args[0].queueableFns;
expect(queueableFns.length).toBe(5);
queueableFns[0].fn();
expect(runQueue.calls.count()).toBe(2);
expect(runQueue.calls.mostRecent().args[0].queueableFns.length).toBe(2);
runQueue.calls.mostRecent().args[0].queueableFns[1].fn();
expect(runQueue.calls.count()).toBe(3);
runQueue.calls.mostRecent().args[0].queueableFns[1].fn('done');
expect(
privateUnderTest.TreeRunner.prototype._executeSpec
).toHaveBeenCalledWith(spec1, 'done');
queueableFns[1].fn('done');
expect(
privateUnderTest.TreeRunner.prototype._executeSpec
).toHaveBeenCalledWith(spec4, 'done');
queueableFns[2].fn();
expect(runQueue.calls.count()).toBe(4);
expect(runQueue.calls.mostRecent().args[0].queueableFns.length).toBe(2);
runQueue.calls.mostRecent().args[0].queueableFns[1].fn();
expect(runQueue.calls.count()).toBe(5);
runQueue.calls.mostRecent().args[0].queueableFns[1].fn('done');
expect(
privateUnderTest.TreeRunner.prototype._executeSpec
).toHaveBeenCalledWith(spec2, 'done');
queueableFns[3].fn('done');
expect(
privateUnderTest.TreeRunner.prototype._executeSpec
).toHaveBeenCalledWith(spec5, 'done');
queueableFns[4].fn();
expect(runQueue.calls.count()).toBe(6);
expect(runQueue.calls.mostRecent().args[0].queueableFns.length).toBe(2);
runQueue.calls.mostRecent().args[0].queueableFns[1].fn();
expect(runQueue.calls.count()).toBe(7);
runQueue.calls.mostRecent().args[0].queueableFns[1].fn('done');
expect(
privateUnderTest.TreeRunner.prototype._executeSpec
).toHaveBeenCalledWith(spec3, 'done');
await expectAsync(promise).toBePending();
});
it('runs large segments of nodes in the order they were declared', async function() {
const specs = [];

View File

@@ -135,11 +135,9 @@ describe('TreeProcessor', function() {
const result = processor.processTree();
expect(result.childrenOfTopSuite()).toEqual([
{ suite: node, segmentNumber: 0 }
]);
expect(result.childrenOfTopSuite()).toEqual([{ suite: node }]);
expect(result.isExcluded(node)).toEqual(true);
expect(result.childrenOfSuiteSegment(node, 0)).toEqual([{ spec: leaf1 }]);
expect(result.childrenOfSuite(node)).toEqual([{ spec: leaf1 }]);
expect(result.isExcluded(node)).toEqual(true);
});
@@ -167,37 +165,37 @@ describe('TreeProcessor', function() {
expect(result.isExcluded(parent)).toEqual(false);
expect(result.childrenOfTopSuite()).toEqual([
{ suite: parent, segmentNumber: 0 },
{ suite: parentOfPendings, segmentNumber: 0 }
{ suite: parent },
{ suite: parentOfPendings }
]);
expect(result.isExcluded(parentOfPendings)).toEqual(true);
expect(result.childrenOfSuiteSegment(parentOfPendings, 0)).toEqual([
{ suite: childless, segmentNumber: 0 },
{ suite: pendingNode, segmentNumber: 0 }
expect(result.childrenOfSuite(parentOfPendings)).toEqual([
{ suite: childless },
{ suite: pendingNode }
]);
expect(result.isExcluded(childless)).toEqual(true);
expect(result.childrenOfSuiteSegment(childless, 0)).toEqual([]);
expect(result.childrenOfSuite(childless)).toEqual([]);
expect(result.isExcluded(pendingLeaf)).toEqual(false);
expect(result.isExcluded(executableLeaf)).toEqual(false);
expect(result.isExcluded(parent)).toEqual(false);
expect(result.childrenOfSuiteSegment(parent, 0)).toEqual([
expect(result.childrenOfSuite(parent)).toEqual([
{ spec: pendingLeaf },
{ spec: executableLeaf }
]);
expect(result.isExcluded(pendingNode)).toEqual(true);
expect(result.childrenOfSuiteSegment(pendingNode, 0)).toEqual([
expect(result.childrenOfSuite(pendingNode)).toEqual([
{ spec: childOfPending }
]);
expect(result.isExcluded(childOfPending)).toEqual(false);
});
it('throws if the specified order would re-enter a node that does not allow re-entry', function() {
it('throws if the specified order would re-enter a node', function() {
const leaf1 = new Leaf(),
leaf2 = new Leaf(),
leaf3 = new Leaf(),
@@ -210,29 +208,7 @@ describe('TreeProcessor', function() {
expect(function() {
processor.processTree();
}).toThrowError(
'Invalid order: would cause a beforeAll or afterAll to be run multiple times'
);
});
it('does not throw if a node being re-entered allows re-entry', function() {
const leaf1 = new Leaf();
const leaf2 = new Leaf();
const leaf3 = new Leaf();
const reentered = new Node({ children: [leaf1, leaf2] });
const root = new Node({ children: [reentered, leaf3] });
const processor = new privateUnderTest.TreeProcessor({
tree: root,
runnableIds: [leaf1.id, leaf3.id, leaf2.id]
});
const env = jasmineUnderTest.getEnv();
spyOn(env, 'deprecated');
processor.processTree();
expect(env.deprecated).toHaveBeenCalledWith(
'The specified spec/suite order splits up a suite, running unrelated specs in the middle of it. This will become an error in a future release.'
);
}).toThrowError('Invalid order: would split up a suite');
});
it("does not throw if a node which can't be re-entered is only entered once", function() {

View File

@@ -180,7 +180,7 @@ describe('TreeRunner', function() {
childrenOfTopSuite() {
return [{ suite }];
},
childrenOfSuiteSegment() {
childrenOfSuite() {
return [];
},
isExcluded() {
@@ -240,7 +240,7 @@ describe('TreeRunner', function() {
childrenOfTopSuite() {
return [{ suite: failingSuite }, { suite: passingSuite }];
},
childrenOfSuiteSegment() {
childrenOfSuite() {
return [];
},
isExcluded() {
@@ -308,7 +308,7 @@ describe('TreeRunner', function() {
childrenOfTopSuite() {
return [{ suite }];
},
childrenOfSuiteSegment() {
childrenOfSuite() {
return [{ spec }];
},
isExcluded() {
@@ -371,7 +371,7 @@ describe('TreeRunner', function() {
childrenOfTopSuite() {
return [{ suite }];
},
childrenOfSuiteSegment() {
childrenOfSuite() {
return [{ spec }];
},
isExcluded() {
@@ -519,7 +519,7 @@ describe('TreeRunner', function() {
childrenOfTopSuite() {
return [{ suite }];
},
childrenOfSuiteSegment() {
childrenOfSuite() {
return [{ spec }];
},
isExcluded() {
@@ -588,7 +588,7 @@ describe('TreeRunner', function() {
childrenOfTopSuite() {
return [{ suite }];
},
childrenOfSuiteSegment() {
childrenOfSuite() {
return [{ spec }];
},
isExcluded() {

View File

@@ -601,37 +601,7 @@ describe('spec running', function() {
]);
});
it('re-enters suites that have no *Alls', async function() {
const actions = [];
let spec1;
let spec2;
let spec3;
env.describe('top', function() {
spec1 = env.it('spec1', function() {
actions.push('spec1');
});
spec2 = env.it('spec2', function() {
actions.push('spec2');
});
});
spec3 = env.it('spec3', function() {
actions.push('spec3');
});
spyOn(jasmineUnderTest.getEnv(), 'deprecated');
await env.execute([spec2.id, spec3.id, spec1.id]);
expect(actions).toEqual(['spec2', 'spec3', 'spec1']);
expect(jasmineUnderTest.getEnv().deprecated).toHaveBeenCalledWith(
'The specified spec/suite order splits up a suite, running unrelated specs in the middle of it. This will become an error in a future release.'
);
});
it('refuses to re-enter suites with a beforeAll', async function() {
it('refuses to re-enter suites', async function() {
const actions = [];
let spec1;
let spec2;
@@ -654,34 +624,9 @@ describe('spec running', function() {
});
const promise = env.execute([spec2.id, spec3.id, spec1.id]);
await expectAsync(promise).toBeRejectedWithError(/beforeAll/);
expect(actions).toEqual([]);
});
it('refuses to re-enter suites with a afterAll', async function() {
const actions = [];
let spec1;
let spec2;
let spec3;
env.describe('top', function() {
env.afterAll(function() {});
spec1 = env.it('spec1', function() {
actions.push('spec1');
});
spec2 = env.it('spec2', function() {
actions.push('spec2');
});
});
spec3 = env.it('spec3', function() {
actions.push('spec3');
});
const promise = env.execute([spec2.id, spec3.id, spec1.id]);
await expectAsync(promise).toBeRejectedWithError(/afterAll/);
await expectAsync(promise).toBeRejectedWithError(
'Invalid order: would split up a suite'
);
expect(actions).toEqual([]);
});

View File

@@ -3,8 +3,7 @@ getJasmineRequireObj().TreeProcessor = function(j$) {
const defaultMax = 1 - Infinity;
// Transforms the suite tree into an execution tree, which represents the set
// of specs and (possibly interleaved) suites to be run in the order they are
// to be run in.
// of specs and suites to be run in the order they are to be run in.
class TreeProcessor {
#tree;
#runnableIds;
@@ -86,15 +85,7 @@ getJasmineRequireObj().TreeProcessor = function(j$) {
segmentChildren(node, orderedChildren, this.#stats, executableIndex);
if (this.#stats[node.id].segments.length > 1) {
if (node.canBeReentered()) {
j$.getEnv().deprecated(
'The specified spec/suite order splits up a suite, running unrelated specs in the middle of it. This will become an error in a future release.'
);
} else {
throw new Error(
'Invalid order: would cause a beforeAll or afterAll to be run multiple times'
);
}
throw new Error('Invalid order: would split up a suite');
}
}
}
@@ -112,15 +103,14 @@ getJasmineRequireObj().TreeProcessor = function(j$) {
}
childrenOfTopSuite() {
return this.childrenOfSuiteSegment(this.topSuite, 0);
return this.childrenOfSuite(this.topSuite);
}
childrenOfSuiteSegment(suite, segmentNumber) {
const segmentChildren = this.#stats[suite.id].segments[segmentNumber]
.nodes;
childrenOfSuite(suite) {
const segmentChildren = this.#stats[suite.id].segments[0].nodes;
return segmentChildren.map(function(child) {
if (child.owner.children) {
return { suite: child.owner, segmentNumber: child.index };
return { suite: child.owner };
} else {
return { spec: child.owner };
}

View File

@@ -24,7 +24,7 @@ getJasmineRequireObj().TreeRunner = function(j$) {
async execute() {
this.#hasFailures = false;
await new Promise(resolve => {
this.#executeSuiteSegment(this.#executionTree.topSuite, 0, resolve);
this.#executeSuite(this.#executionTree.topSuite, resolve);
});
return { hasFailures: this.#hasFailures };
}
@@ -34,7 +34,7 @@ getJasmineRequireObj().TreeRunner = function(j$) {
return {
fn: done => {
if (node.suite) {
this.#executeSuiteSegment(node.suite, node.segmentNumber, done);
this.#executeSuite(node.suite, done);
} else {
this._executeSpec(node.spec, done);
}
@@ -124,7 +124,7 @@ getJasmineRequireObj().TreeRunner = function(j$) {
return fns;
}
#executeSuiteSegment(suite, segmentNumber, done) {
#executeSuite(suite, done) {
const isTopSuite = suite === this.#executionTree.topSuite;
const isExcluded = this.#executionTree.isExcluded(suite);
let befores = [];
@@ -146,7 +146,7 @@ getJasmineRequireObj().TreeRunner = function(j$) {
const children = isTopSuite
? this.#executionTree.childrenOfTopSuite()
: this.#executionTree.childrenOfSuiteSegment(suite, segmentNumber);
: this.#executionTree.childrenOfSuite(suite);
const queueableFns = [
...befores,
...this.#wrapNodes(children),