226 lines
5.7 KiB
JavaScript
226 lines
5.7 KiB
JavaScript
getJasmineRequireObj().TreeProcessor = function(j$) {
|
|
'use strict';
|
|
|
|
const defaultMin = Infinity;
|
|
const defaultMax = 1 - Infinity;
|
|
|
|
// Transforms the suite tree into an execution tree, which represents the set
|
|
// of specs and suites to be run in the order they are to be run in.
|
|
class TreeProcessor {
|
|
#tree;
|
|
#runnableIds;
|
|
#orderChildren;
|
|
#excludeNode;
|
|
#stats;
|
|
|
|
constructor(attrs) {
|
|
this.#tree = attrs.tree;
|
|
this.#runnableIds = attrs.runnableIds;
|
|
|
|
this.#orderChildren =
|
|
attrs.orderChildren ||
|
|
function(node) {
|
|
return node.children;
|
|
};
|
|
this.#excludeNode =
|
|
attrs.excludeNode ||
|
|
function(node) {
|
|
return false;
|
|
};
|
|
}
|
|
|
|
processTree() {
|
|
this.#stats = {};
|
|
this.#processNode(this.#tree, true);
|
|
const result = new ExecutionTree(this.#tree, this.#stats);
|
|
this.#stats = null;
|
|
return result;
|
|
}
|
|
|
|
#runnableIndex(id) {
|
|
for (let i = 0; i < this.#runnableIds.length; i++) {
|
|
if (this.#runnableIds[i] === id) {
|
|
return i;
|
|
}
|
|
}
|
|
}
|
|
|
|
#processNode(node, parentExcluded) {
|
|
const executableIndex = this.#runnableIndex(node.id);
|
|
|
|
if (executableIndex !== undefined) {
|
|
parentExcluded = false;
|
|
}
|
|
|
|
if (!node.children) {
|
|
const excluded = parentExcluded || this.#excludeNode(node);
|
|
this.#stats[node.id] = {
|
|
excluded: excluded,
|
|
willExecute: !excluded && !node.markedPending,
|
|
segments: [
|
|
{
|
|
index: 0,
|
|
owner: node,
|
|
nodes: [node],
|
|
min: startingMin(executableIndex),
|
|
max: startingMax(executableIndex)
|
|
}
|
|
]
|
|
};
|
|
} else {
|
|
let hasExecutableChild = false;
|
|
|
|
const orderedChildren = this.#orderChildren(node);
|
|
|
|
for (let i = 0; i < orderedChildren.length; i++) {
|
|
const child = orderedChildren[i];
|
|
this.#processNode(child, parentExcluded);
|
|
const childStats = this.#stats[child.id];
|
|
hasExecutableChild = hasExecutableChild || childStats.willExecute;
|
|
}
|
|
|
|
this.#stats[node.id] = {
|
|
excluded: parentExcluded,
|
|
willExecute: hasExecutableChild
|
|
};
|
|
|
|
segmentChildren(node, orderedChildren, this.#stats, executableIndex);
|
|
|
|
if (this.#stats[node.id].segments.length > 1) {
|
|
throw new Error('Invalid order: would split up a suite');
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
class ExecutionTree {
|
|
#stats;
|
|
|
|
constructor(topSuite, stats) {
|
|
Object.defineProperty(this, 'topSuite', {
|
|
writable: false,
|
|
value: topSuite
|
|
});
|
|
this.#stats = stats;
|
|
}
|
|
|
|
childrenOfTopSuite() {
|
|
return this.childrenOfSuite(this.topSuite);
|
|
}
|
|
|
|
childrenOfSuite(suite) {
|
|
const segmentChildren = this.#stats[suite.id].segments[0].nodes;
|
|
return segmentChildren.map(function(child) {
|
|
if (child.owner.children) {
|
|
return { suite: child.owner };
|
|
} else {
|
|
return { spec: child.owner };
|
|
}
|
|
});
|
|
}
|
|
|
|
isExcluded(node) {
|
|
const nodeStats = this.#stats[node.id];
|
|
return node.children ? !nodeStats.willExecute : nodeStats.excluded;
|
|
}
|
|
|
|
numExcludedSpecs(node) {
|
|
if (!node) {
|
|
return this.numExcludedSpecs(this.topSuite);
|
|
} else if (node.children) {
|
|
let result = 0;
|
|
|
|
for (const child of node.children) {
|
|
result += this.numExcludedSpecs(child);
|
|
}
|
|
|
|
return result;
|
|
} else {
|
|
const nodeStats = this.#stats[node.id];
|
|
return nodeStats.willExecute ? 0 : 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
function segmentChildren(node, orderedChildren, stats, executableIndex) {
|
|
let currentSegment = {
|
|
index: 0,
|
|
owner: node,
|
|
nodes: [],
|
|
min: startingMin(executableIndex),
|
|
max: startingMax(executableIndex)
|
|
},
|
|
result = [currentSegment],
|
|
lastMax = defaultMax,
|
|
orderedChildSegments = orderChildSegments(orderedChildren, stats);
|
|
|
|
function isSegmentBoundary(minIndex) {
|
|
return (
|
|
lastMax !== defaultMax &&
|
|
minIndex !== defaultMin &&
|
|
lastMax < minIndex - 1
|
|
);
|
|
}
|
|
|
|
for (let i = 0; i < orderedChildSegments.length; i++) {
|
|
const childSegment = orderedChildSegments[i],
|
|
maxIndex = childSegment.max,
|
|
minIndex = childSegment.min;
|
|
|
|
if (isSegmentBoundary(minIndex)) {
|
|
currentSegment = {
|
|
index: result.length,
|
|
owner: node,
|
|
nodes: [],
|
|
min: defaultMin,
|
|
max: defaultMax
|
|
};
|
|
result.push(currentSegment);
|
|
}
|
|
|
|
currentSegment.nodes.push(childSegment);
|
|
currentSegment.min = Math.min(currentSegment.min, minIndex);
|
|
currentSegment.max = Math.max(currentSegment.max, maxIndex);
|
|
lastMax = maxIndex;
|
|
}
|
|
|
|
stats[node.id].segments = result;
|
|
}
|
|
|
|
function orderChildSegments(children, stats) {
|
|
const specifiedOrder = [],
|
|
unspecifiedOrder = [];
|
|
|
|
for (let i = 0; i < children.length; i++) {
|
|
const child = children[i],
|
|
segments = stats[child.id].segments;
|
|
|
|
for (let j = 0; j < segments.length; j++) {
|
|
const seg = segments[j];
|
|
|
|
if (seg.min === defaultMin) {
|
|
unspecifiedOrder.push(seg);
|
|
} else {
|
|
specifiedOrder.push(seg);
|
|
}
|
|
}
|
|
}
|
|
|
|
specifiedOrder.sort(function(a, b) {
|
|
return a.min - b.min;
|
|
});
|
|
|
|
return specifiedOrder.concat(unspecifiedOrder);
|
|
}
|
|
|
|
function startingMin(executableIndex) {
|
|
return executableIndex === undefined ? defaultMin : executableIndex;
|
|
}
|
|
|
|
function startingMax(executableIndex) {
|
|
return executableIndex === undefined ? defaultMax : executableIndex;
|
|
}
|
|
|
|
return TreeProcessor;
|
|
};
|