velocious 1.0.652 → 1.0.654

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. package/README.md +14 -1
  2. package/build/database/query/model-class-query.js +3 -1
  3. package/build/database/query/where-hash.js +12 -1
  4. package/build/database/query/where-in.js +48 -0
  5. package/build/database/query/where-model-class-hash.js +34 -1
  6. package/build/environment-handlers/node/cli/commands/test.js +2 -1
  7. package/build/src/database/query/model-class-query.js +5 -2
  8. package/build/src/database/query/where-hash.d.ts.map +1 -1
  9. package/build/src/database/query/where-hash.js +14 -2
  10. package/build/src/database/query/where-in.d.ts +22 -0
  11. package/build/src/database/query/where-in.d.ts.map +1 -0
  12. package/build/src/database/query/where-in.js +40 -0
  13. package/build/src/database/query/where-model-class-hash.d.ts +11 -0
  14. package/build/src/database/query/where-model-class-hash.d.ts.map +1 -1
  15. package/build/src/database/query/where-model-class-hash.js +33 -2
  16. package/build/src/environment-handlers/node/cli/commands/test.d.ts.map +1 -1
  17. package/build/src/environment-handlers/node/cli/commands/test.js +4 -2
  18. package/build/src/testing/test-runner.d.ts +23 -0
  19. package/build/src/testing/test-runner.d.ts.map +1 -1
  20. package/build/src/testing/test-runner.js +29 -2
  21. package/build/src/testing/velocious-runner-reporter.d.ts +7 -0
  22. package/build/src/testing/velocious-runner-reporter.d.ts.map +1 -1
  23. package/build/src/testing/velocious-runner-reporter.js +54 -12
  24. package/build/testing/test-runner.js +33 -1
  25. package/build/testing/velocious-runner-reporter.js +52 -11
  26. package/package.json +4 -4
  27. package/scripts/browser-test-session.js +21 -23
  28. package/src/database/query/model-class-query.js +3 -1
  29. package/src/database/query/where-hash.js +12 -1
  30. package/src/database/query/where-in.js +48 -0
  31. package/src/database/query/where-model-class-hash.js +34 -1
  32. package/src/environment-handlers/node/cli/commands/test.js +2 -1
  33. package/src/testing/test-runner.js +33 -1
  34. package/src/testing/velocious-runner-reporter.js +52 -11
@@ -56,6 +56,13 @@ export default class VelociousRunnerReporter {
56
56
  testData: import("./test-runner.js").TestData;
57
57
  testDescription: string;
58
58
  }): Promise<void>;
59
+ /**
60
+ * Prints complete primary and secondary stacks once, including cyclic cause graphs.
61
+ * @param {unknown} error - Thrown value at the reporting boundary.
62
+ * @param {Set<Error>} [reported] - Error identities already printed.
63
+ * @returns {void}
64
+ */
65
+ printErrorCauses(error: unknown, reported?: Set<Error>): void;
59
66
  /**
60
67
  * Emits one legacy event and awaits listeners in registration order.
61
68
  * @param {string} eventName - Event name.
@@ -1 +1 @@
1
- {"version":3,"file":"velocious-runner-reporter.d.ts","sourceRoot":"","sources":["../../../src/testing/velocious-runner-reporter.js"],"names":[],"mappings":"AAQI,YAA+D,sBAAsB,GAA3E,OAAO,2BAA2B,EAAE,eAAe,CAAwB;AACrF,YAA+D,sBAAsB,GAA3E,OAAO,2BAA2B,EAAE,eAAe,CAAwB;AAkBzF,2EAA2E;AAC3E,qBAAa,wBAAyB,SAAQ,KAAK;CAAG;AAEtD,MAAM,CAAC,OAAO,OAAO,uBAAuB;IAQnC,UAAU;IACf,iGAAiG;IAC5F,qBAAqB,EADf,OAAO,CAAC,sBAAsB,EAAE,OAAO,kBAAkB,EAAE,oBAAoB,EAAE,CAAC;IAE7F,iDAAiD;IAC5C,UAAU,EADJ,sBAAsB,GAAG,SAAS;IAV/C;;;;OAIG;IACH,YAAY,EAAC,UAAU,EAAE,GAAG,QAAQ,EAAC,EAFlC;QAAiD,UAAU,EAAnD,OAAO,kBAAkB,EAAE,OAAO,CAC5C;KACoC,EAOpC;IAED;;;;OAIG;IACG,OAAO,CAAC,KAAK,EAHR,OAAO,2BAA2B,EAAE,WAG5B,GAFN,OAAO,CAAC,IAAI,CAAC,CAuBzB;IAED;;;;OAIG;IACG,kBAAkB,CAAC,KAAK,EAHnB,OAAO,2BAA2B,EAAE,WAGjB,GAFjB,OAAO,CAAC,IAAI,CAAC,CA+FzB;IAED;;;;OAIG;IACG,eAAe,CAAC,KAAK,EAHhB,OAAO,2BAA2B,EAAE,WAGpB,GAFd,OAAO,CAAC,IAAI,CAAC,CAmDzB;IAED;;;;;;;;;;;OAWG;IACG,gBAAgB,CAAC,EAAC,qBAAqB,EAAE,YAAY,EAAE,KAAK,EAAE,WAAW,EAAE,QAAQ,EAAE,QAAQ,EAAE,eAAe,EAAC,EATlH;QAAgE,qBAAqB,EAA7E,OAAO,kBAAkB,EAAE,oBAAoB,EAAE,CACzD;QAAuB,YAAY,EAA3B,MAAM,EAAE,CAChB;QAA4C,KAAK,EAAzC,UAAU,CAAC,OAAO,IAAI,CAAC,KAAK,CAAC,CACrC;QAAqB,WAAW,EAAxB,MAAM,CACd;QAAkD,QAAQ,EAAlD,OAAO,kBAAkB,EAAE,QAAQ,CAC3C;QAAkD,QAAQ,EAAlD,OAAO,kBAAkB,EAAE,QAAQ,CAC3C;QAAqB,eAAe,EAA5B,MAAM,CACd;KAEkH,GAFxG,OAAO,CAAC,IAAI,CAAC,CAmCzB;IAED;;;;;OAKG;IACG,SAAS,CAAC,SAAS,EAJd,MAIc,EAAE,OAAO,EAHvB,MAGuB,GAFrB,OAAO,CAAC,IAAI,CAAC,CAIzB;CACF"}
1
+ {"version":3,"file":"velocious-runner-reporter.d.ts","sourceRoot":"","sources":["../../../src/testing/velocious-runner-reporter.js"],"names":[],"mappings":"AAOI,YAA+D,sBAAsB,GAA3E,OAAO,2BAA2B,EAAE,eAAe,CAAwB;AACrF,YAA+D,sBAAsB,GAA3E,OAAO,2BAA2B,EAAE,eAAe,CAAwB;AAoBzF,2EAA2E;AAC3E,qBAAa,wBAAyB,SAAQ,KAAK;CAAG;AAEtD,MAAM,CAAC,OAAO,OAAO,uBAAuB;IAQnC,UAAU;IACf,iGAAiG;IAC5F,qBAAqB,EADf,OAAO,CAAC,sBAAsB,EAAE,OAAO,kBAAkB,EAAE,oBAAoB,EAAE,CAAC;IAE7F,iDAAiD;IAC5C,UAAU,EADJ,sBAAsB,GAAG,SAAS;IAV/C;;;;OAIG;IACH,YAAY,EAAC,UAAU,EAAE,GAAG,QAAQ,EAAC,EAFlC;QAAiD,UAAU,EAAnD,OAAO,kBAAkB,EAAE,OAAO,CAC5C;KACoC,EAOpC;IAED;;;;OAIG;IACG,OAAO,CAAC,KAAK,EAHR,OAAO,2BAA2B,EAAE,WAG5B,GAFN,OAAO,CAAC,IAAI,CAAC,CA2CzB;IAED;;;;OAIG;IACG,kBAAkB,CAAC,KAAK,EAHnB,OAAO,2BAA2B,EAAE,WAGjB,GAFjB,OAAO,CAAC,IAAI,CAAC,CA+FzB;IAED;;;;OAIG;IACG,eAAe,CAAC,KAAK,EAHhB,OAAO,2BAA2B,EAAE,WAGpB,GAFd,OAAO,CAAC,IAAI,CAAC,CAmDzB;IAED;;;;;;;;;;;OAWG;IACG,gBAAgB,CAAC,EAAC,qBAAqB,EAAE,YAAY,EAAE,KAAK,EAAE,WAAW,EAAE,QAAQ,EAAE,QAAQ,EAAE,eAAe,EAAC,EATlH;QAAgE,qBAAqB,EAA7E,OAAO,kBAAkB,EAAE,oBAAoB,EAAE,CACzD;QAAuB,YAAY,EAA3B,MAAM,EAAE,CAChB;QAA4C,KAAK,EAAzC,UAAU,CAAC,OAAO,IAAI,CAAC,KAAK,CAAC,CACrC;QAAqB,WAAW,EAAxB,MAAM,CACd;QAAkD,QAAQ,EAAlD,OAAO,kBAAkB,EAAE,QAAQ,CAC3C;QAAkD,QAAQ,EAAlD,OAAO,kBAAkB,EAAE,QAAQ,CAC3C;QAAqB,eAAe,EAA5B,MAAM,CACd;KAEkH,GAFxG,OAAO,CAAC,IAAI,CAAC,CA4BzB;IAED;;;;;OAKG;IACH,gBAAgB,CAAC,KAAK,EAJX,OAIW,EAAE,QAAQ,AAH7B,CACA,EADQ,GAAG,CAAC,KAAK,CAGwB,GAF/B,IAAI,CAqBhB;IAED;;;;;OAKG;IACG,SAAS,CAAC,SAAS,EAJd,MAIc,EAAE,OAAO,EAHvB,MAGuB,GAFrB,OAAO,CAAC,IAAI,CAAC,CAIzB;CACF"}
@@ -1,6 +1,5 @@
1
1
  // @ts-check
2
2
  import { addTrackedStackToError } from "../utils/with-tracked-stack.js";
3
- import BacktraceCleaner from "../utils/backtrace-cleaner-node.js";
4
3
  import picocolors from "picocolors";
5
4
  import restArgsError from "../utils/rest-args-error.js";
6
5
  import { testEvents } from "./test.js";
@@ -18,6 +17,10 @@ function errorFromPackageRecord(errorRecord) {
18
17
  error.name = errorRecord.name;
19
18
  if (errorRecord.stack)
20
19
  error.stack = errorRecord.stack;
20
+ if (errorRecord.cause)
21
+ error.cause = errorFromPackageRecord(errorRecord.cause);
22
+ if (errorRecord.terminalResource)
23
+ Object.assign(error, { terminalResource: errorRecord.terminalResource });
21
24
  return error;
22
25
  }
23
26
  /** Stops package traversal after framework-owned connection quarantine. */
@@ -59,8 +62,27 @@ export default class VelociousRunnerReporter {
59
62
  await this.reportTestEvent(event);
60
63
  return;
61
64
  }
62
- if (event.type === "run:finish")
65
+ if (event.type === "test:not-run") {
66
+ const test = this.testRunner.findTestDeclaration(event.test.fullName);
67
+ if (!test)
68
+ throw new Error(`Package not-run result did not match a declaration: ${event.test.fullName}`);
69
+ this.testRunner.recordNotRunTest(event.test);
70
+ this.testRunner.completeTestDeclaration(test);
71
+ console.error(`Not run: ${event.test.fullName} (terminal resource failure in ${event.test.reason.fullName})`);
72
+ await this.emitEvent("testNotRun", {
73
+ configuration: this.testRunner.getConfiguration(),
74
+ test: event.test,
75
+ testRunner: this.testRunner
76
+ });
77
+ return;
78
+ }
79
+ if (event.type === "run:finish") {
63
80
  this.testRunner.recordPackageResult(event.result);
81
+ for (const failure of event.result.errors) {
82
+ console.error(`Suite ${failure.phase} failed: ${failure.suite}`);
83
+ this.printErrorCauses(errorFromPackageRecord(failure.error));
84
+ }
85
+ }
64
86
  }
65
87
  /**
66
88
  * Projects attempt failure/retry events while retaining the raw thrown value.
@@ -83,7 +105,7 @@ export default class VelociousRunnerReporter {
83
105
  const failed = outcome?.failed ?? Boolean(attempt.error);
84
106
  const error = outcome?.error;
85
107
  const retriesUsed = Math.min(attempt.attemptNumber, retryCount);
86
- const willRetry = failed && !outcome?.abortRemainingTests && attempt.attemptNumber <= retryCount;
108
+ const willRetry = failed && !event.terminalFailure && !outcome?.abortRemainingTests && attempt.attemptNumber <= retryCount;
87
109
  const { descriptions, testDescription } = this.testRunner.testMetadata(test);
88
110
  const compatibility = this.testRunner.testData(test);
89
111
  if (failed) {
@@ -221,14 +243,7 @@ export default class VelociousRunnerReporter {
221
243
  const consoleOutput = testRunner.buildConsoleOutput(attemptConsoleOutputs);
222
244
  if (error instanceof Error) {
223
245
  console.error(picocolors.red(`${leftPadding} Test failed: ${error.message}`));
224
- addTrackedStackToError(error);
225
- const backtraceCleaner = new BacktraceCleaner(error);
226
- const cleanedStack = backtraceCleaner.getCleanedStack();
227
- const stackLines = cleanedStack?.split("\n");
228
- if (stackLines) {
229
- for (const stackLine of stackLines)
230
- console.error(picocolors.red(`${leftPadding} ${stackLine}`));
231
- }
246
+ this.printErrorCauses(error);
232
247
  }
233
248
  else {
234
249
  console.error(picocolors.red(`${leftPadding} Test failed with a ${typeof error}: ${String(error)}`));
@@ -246,6 +261,33 @@ export default class VelociousRunnerReporter {
246
261
  });
247
262
  testRunner.printRerunCommand({ descriptions, testDescription, testData, leftPadding });
248
263
  }
264
+ /**
265
+ * Prints complete primary and secondary stacks once, including cyclic cause graphs.
266
+ * @param {unknown} error - Thrown value at the reporting boundary.
267
+ * @param {Set<Error>} [reported] - Error identities already printed.
268
+ * @returns {void}
269
+ */
270
+ printErrorCauses(error, reported = new Set()) {
271
+ if (!(error instanceof Error)) {
272
+ console.error(String(error));
273
+ return;
274
+ }
275
+ if (reported.has(error))
276
+ return;
277
+ reported.add(error);
278
+ addTrackedStackToError(error);
279
+ console.error(error.stack || `${error.name}: ${error.message}`);
280
+ if (error.cause !== undefined) {
281
+ console.error("Caused by:");
282
+ this.printErrorCauses(error.cause, reported);
283
+ }
284
+ if (error instanceof AggregateError) {
285
+ for (const secondary of error.errors) {
286
+ console.error("Related failure:");
287
+ this.printErrorCauses(secondary, reported);
288
+ }
289
+ }
290
+ }
249
291
  /**
250
292
  * Emits one legacy event and awaits listeners in registration order.
251
293
  * @param {string} eventName - Event name.
@@ -257,4 +299,4 @@ export default class VelociousRunnerReporter {
257
299
  await listener(payload);
258
300
  }
259
301
  }
260
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidmVsb2Npb3VzLXJ1bm5lci1yZXBvcnRlci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy90ZXN0aW5nL3ZlbG9jaW91cy1ydW5uZXItcmVwb3J0ZXIuanMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsWUFBWTtBQUVaLE9BQU8sRUFBRSxzQkFBc0IsRUFBRSxNQUFNLGdDQUFnQyxDQUFBO0FBQ3ZFLE9BQU8sZ0JBQWdCLE1BQU0sb0NBQW9DLENBQUE7QUFDakUsT0FBTyxVQUFVLE1BQU0sWUFBWSxDQUFBO0FBQ25DLE9BQU8sYUFBYSxNQUFNLDZCQUE2QixDQUFBO0FBQ3ZELE9BQU8sRUFBRSxVQUFVLEVBQUUsTUFBTSxXQUFXLENBQUE7QUFFdEMsNEZBQTRGO0FBQzVGLDRGQUE0RjtBQUU1Rjs7OztHQUlHO0FBQ0gsU0FBUyxzQkFBc0IsQ0FBQyxXQUFXO0lBQ3pDLE1BQU0sS0FBSyxHQUFHLFdBQVcsQ0FBQyxNQUFNO1FBQzlCLENBQUMsQ0FBQyxJQUFJLGNBQWMsQ0FBQyxXQUFXLENBQUMsTUFBTSxDQUFDLEdBQUcsQ0FBQyxzQkFBc0IsQ0FBQyxFQUFFLFdBQVcsQ0FBQyxPQUFPLENBQUM7UUFDekYsQ0FBQyxDQUFDLElBQUksS0FBSyxDQUFDLFdBQVcsQ0FBQyxPQUFPLENBQUMsQ0FBQTtJQUVsQyxLQUFLLENBQUMsSUFBSSxHQUFHLFdBQVcsQ0FBQyxJQUFJLENBQUE7SUFDN0IsSUFBSSxXQUFXLENBQUMsS0FBSztRQUFFLEtBQUssQ0FBQyxLQUFLLEdBQUcsV0FBVyxDQUFDLEtBQUssQ0FBQTtJQUV0RCxPQUFPLEtBQUssQ0FBQTtBQUNkLENBQUM7QUFFRCwyRUFBMkU7QUFDM0UsTUFBTSxPQUFPLHdCQUF5QixTQUFRLEtBQUs7Q0FBRztBQUV0RCxNQUFNLENBQUMsT0FBTyxPQUFPLHVCQUF1QjtJQUMxQzs7OztPQUlHO0lBQ0gsWUFBWSxFQUFDLFVBQVUsRUFBRSxHQUFHLFFBQVEsRUFBQztRQUNuQyxhQUFh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QUNMLFFBQVE7WUFDUixRQUFRO1lBQ1IsZUFBZTtZQUNmLFVBQVU7U0FDWCxDQUFDLENBQUE7UUFFRixVQUFVLENBQUMsaUJBQWlCLENBQUMsRUFBQyxZQUFZLEVBQUUsZUFBZSxFQUFFLFFBQVEsRUFBRSxXQUFXLEVBQUMsQ0FBQyxDQUFBO0lBQ3RGLENBQUM7SUFFRDs7Ozs7T0FLRztJQUNILEtBQUssQ0FBQyxTQUFTLENBQUMsU0FBUyxFQUFFLE9BQU87UUFDaEMsS0FBSyxNQUFNLFFBQVEsSUFBSSxVQUFVLENBQUMsU0FBUyxDQUFDLFNBQVMsQ0FBQztZQUFFLE1BQU0sUUFBUSxDQUFDLE9BQU8sQ0FBQyxDQUFBO0lBQ2pGLENBQUM7Q0FDRiIsInNvdXJjZXNDb250ZW50IjpbIi8vIEB0cy1jaGVja1xuXG5pbXBvcnQgeyBhZGRUcmFja2VkU3RhY2tUb0Vycm9yIH0gZnJvbSBcIi4uL3V0aWxzL3dpdGgtdHJhY2tlZC1zdGFjay5qc1wiXG5pbXBvcnQgQmFja3RyYWNlQ2xlYW5lciBmcm9tIFwiLi4vdXRpbHMvYmFja3RyYWNlLWNsZWFuZXItbm9kZS5qc1wiXG5pbXBvcnQgcGljb2NvbG9ycyBmcm9tIFwicGljb2NvbG9yc1wiXG5pbXBvcnQgcmVzdEFyZ3NFcnJvciBmcm9tIFwiLi4vdXRpbHMvcmVzdC1hcmdzLWVycm9yLmpzXCJcbmltcG9ydCB7IHRlc3RFdmVudHMgfSBmcm9tIFwiLi90ZXN0LmpzXCJcblxuLyoqIEB0eXBlZGVmIHtpbXBvcnQoXCJAdmVsb2Npb3VzL3Rlc3RpbmcvcnVubmVyXCIpLlRlc3REZWNsYXJhdGlvbn0gUGFja2FnZVRlc3REZWNsYXJhdGlvbiAqL1xuLyoqIEB0eXBlZGVmIHtpbXBvcnQoXCJAdmVsb2Npb3VzL3Rlc3RpbmcvcnVubmVyXCIpLlRlc3RFcnJvclJlY29yZH0gUGFja2FnZVRlc3RFcnJvclJlY29yZCAqL1xuXG4vKipcbiAqIFJlc3RvcmVzIHRoZSBFcnJvciBzaGFwZSBzZXJpYWxpemVkIGJ5IHRoZSBwYWNrYWdlIHJ1bm5lci5cbiAqIEBwYXJhbSB7UGFja2FnZVRlc3RFcnJvclJlY29yZH0gZXJyb3JSZWNvcmQgLSBTZXJpYWxpemVkIHBhY2thZ2UgZmFpbHVyZS5cbiAqIEByZXR1cm5zIHtFcnJvcn0gLSBFcnJvciBjb21wYXRpYmxlIHdpdGggVmVsb2Npb3VzJ3MgbGVnYWN5IHJlcG9ydGVyIGNvbnRyYWN0LlxuICovXG5mdW5jdGlvbiBlcnJvckZyb21QYWNrYWdlUmVjb3JkKGVycm9yUmVjb3JkKSB7XG4gIGNvbnN0IGVycm9yID0gZXJyb3JSZWNvcmQuZXJyb3JzXG4gICAgPyBuZXcgQWdncmVnYXRlRXJyb3IoZXJyb3JSZWNvcmQuZXJyb3JzLm1hcChlcnJvckZyb21QYWNrYWdlUmVjb3JkKSwgZXJyb3JSZWNvcmQubWVzc2FnZSlcbiAgICA6IG5ldyBFcnJvcihlcnJvclJlY29yZC5tZXNzYWdlKVxuXG4gIGVycm9yLm5hbWUgPSBlcnJvclJlY29yZC5uYW1lXG4gIGlmIChlcnJvclJlY29yZC5zdGFjaykgZXJyb3Iuc3RhY2sgPSBlcnJvclJlY29yZC5zdGFja1xuXG4gIHJldHVybiBlcnJvclxufVxuXG4vKiogU3RvcHMgcGFja2FnZSB0cmF2ZXJzYWwgYWZ0ZXIgZnJhbWV3b3JrLW93bmVkIGNvbm5lY3Rpb24gcXVhcmFudGluZS4gKi9cbmV4cG9ydCBjbGFzcyBBYm9ydFJlbWFpbmluZ1Rlc3RzRXJyb3IgZXh0ZW5kcyBFcnJvciB7fVxuXG5leHBvcnQgZGVmYXVsdCBjbGFzcyBWZWxvY2lvdXNSdW5uZXJSZXBvcnRlciB7XG4gIC8qKlxuICAgKiBDcmVhdGVzIHRoZSBsZWdhY3kgZXZlbnQgYW5kIHJlc3VsdCBwcm9qZWN0aW9uIGFkYXB0ZXIuXG4gICAqIEBwYXJhbSB7b2JqZWN0fSBhcmdzIC0gQ29uc3RydWN0b3IgYXJndW1lbnRzLlxuICAgKiBAcGFyYW0ge2ltcG9ydChcIi4vdGVzdC1ydW5uZXIuanNcIikuZGVmYXVsdH0gYXJncy50ZXN0UnVubmVyIC0gT3duaW5nIFZlbG9jaW91cyBydW5uZXIuXG4gICAqL1xuICBjb25zdHJ1Y3Rvcih7dGVzdFJ1bm5lciwgLi4ucmVzdEFyZ3N9KSB7XG4gICAgcmVzdEFyZ3NFcnJvcihyZXN0QXJncylcbiAgICB0aGlzLnRlc3RSdW5uZXIgPSB0ZXN0UnVubmVyXG4gICAgLyoqIEB0eXBlIHtXZWFrTWFwPFBhY2thZ2VUZXN0RGVjbGFyYXRpb24sIGltcG9ydChcIi4vdGVzdC1ydW5uZXIuanNcIikuQXR0ZW1wdENvbnNvbGVPdXRwdXRbXT59ICovXG4gICAgdGhpcy5hdHRlbXB0Q29uc29sZU91dHB1dHMgPSBuZXcgV2Vha01hcCgpXG4gICAgLyoqIEB0eXBlIHtQYWNrYWdlVGVzdERlY2xhcmF0aW9uIHwgdW5kZWZpbmVkfSAqL1xuICAgIHRoaXMuYWN0aXZlVGVzdCA9IHVuZGVmaW5lZFxuICB9XG5cbiAgLyoqXG4gICAqIFRyYW5zbGF0ZXMgb25lIGF3YWl0ZWQgcGFja2FnZSBydW5uZXIgZXZlbnQgaW50byB0aGUgbGVnYWN5IGNvbnRyYWN0LlxuICAgKiBAcGFyYW0ge2ltcG9ydChcIkB2ZWxvY2lvdXMvdGVzdGluZy9ydW5uZXJcIikuUnVubmVyRXZlbnR9IGV2ZW50IC0gU3RydWN0dXJlZCBwYWNrYWdlIGV2ZW50LlxuICAgKiBAcmV0dXJucyB7UHJvbWlzZTx2b2lkPn0gLSBSZXNvbHZlcyBhZnRlciBsZWdhY3kgbGlzdGVuZXJzIGZpbmlzaC5cbiAgICovXG4gIGFzeW5jIG9uRXZlbnQoZXZlbnQpIHtcbiAgICBpZiAoZXZlbnQudHlwZSA9PT0gXCJ0ZXN0OnN0YXJ0XCIpIHtcbiAgICAgIHRoaXMuYWN0aXZlVGVzdCA9IHRoaXMudGVzdFJ1bm5lci5maW5kVGVzdERlY2xhcmF0aW9uKGV2ZW50LmZ1bGxOYW1lKVxuICAgICAgaWYgKHRoaXMuYWN0aXZlVGVzdCkge1xuICAgICAgICBjb25zdCBtZXRhZGF0YSA9IHRoaXMudGVzdFJ1bm5lci50ZXN0TWV0YWRhdGEodGhpcy5hY3RpdmVUZXN0KVxuICAgICAgICBjb25zb2xlLmxvZyhgJHtcIiBcIi5yZXBlYXQobWV0YWRhdGEuZGVzY3JpcHRpb25zLmxlbmd0aCAqIDIpfWl0ICR7bWV0YWRhdGEudGVzdERlc2NyaXB0aW9ufWApXG4gICAgICB9XG4gICAgICByZXR1cm5cbiAgICB9XG5cbiAgICBpZiAoZXZlbnQudHlwZSA9PT0gXCJhdHRlbXB0OmZpbmlzaFwiKSB7XG4gICAgICBhd2FpdCB0aGlzLnJlcG9ydEF0dGVtcHRFdmVudChldmVudClcbiAgICAgIHJldHVyblxuICAgIH1cblxuICAgIGlmIChldmVudC50eXBlID09PSBcInRlc3Q6ZmluaXNoXCIpIHtcbiAgICAgIGF3YWl0IHRoaXMucmVwb3J0VGVzdEV2ZW50KGV2ZW50KVxuICAgICAgcmV0dXJuXG4gICAgfVxuXG4gICAgaWYgKGV2ZW50LnR5cGUgPT09IFwicnVuOmZpbmlzaFwiKSB0aGlzLnRlc3RSdW5uZXIucmVjb3JkUGFja2FnZVJlc3VsdChldmVudC5yZXN1bHQpXG4gIH1cblxuICAvKipcbiAgICogUHJvamVjdHMgYXR0ZW1wdCBmYWlsdXJlL3JldHJ5IGV2ZW50cyB3aGlsZSByZXRhaW5pbmcgdGhlIHJhdyB0aHJvd24gdmFsdWUuXG4gICAqIEBwYXJhbSB7aW1wb3J0KFwiQHZlbG9jaW91cy90ZXN0aW5nL3J1bm5lclwiKS5SdW5uZXJFdmVudH0gZXZlbnQgLSBBdHRlbXB0IGV2ZW50LlxuICAgKiBAcmV0dXJucyB7UHJvbWlzZTx2b2lkPn0gLSBSZXNvbHZlcyBhZnRlciBsaXN0ZW5lcnMgZmluaXNoLlxuICAgKi9cbiAgYXN5bmMgcmVwb3J0QXR0ZW1wdEV2ZW50KGV2ZW50KSB7XG4gICAgY29uc3QgdGVzdCA9IHRoaXMuYWN0aXZlVGVzdCB8fCB0aGlzLnRlc3RSdW5uZXIuZmluZFRlc3REZWNsYXJhdGlvbihldmVudC5mdWxsTmFtZSlcblxuICAgIGlmICghdGVzdCkgdGhyb3cgbmV3IEVycm9yKGBQYWNrYWdlIHJ1bm5lciBhdHRlbXB0IGRpZCBub3QgbWF0Y2ggYSBkZWNsYXJhdGlvbjogJHtldmVudC5mdWxsTmFtZX1gKVxuXG4gICAgLy8gTmFycm93cyB0aGUgc3RydWN0dXJlZCBldmVudCBwYXlsb2FkIGZvciB0aGlzIGV2ZW50IGRpc2NyaW1pbmF0b3IuXG4gICAgY29uc3QgYXR0ZW1wdCA9IC8qKiBAdHlwZSB7aW1wb3J0KFwiQHZlbG9jaW91cy90ZXN0aW5nL3J1bm5lclwiKS5UZXN0QXR0ZW1wdFJlc3VsdH0gKi8gKGV2ZW50LmF0dGVtcHQpXG4gICAgY29uc3Qgb3V0Y29tZSA9IHRoaXMudGVzdFJ1bm5lci5hdHRlbXB0T3V0Y29tZSh0ZXN0LCBhdHRlbXB0LmF0dGVtcHROdW1iZXIpXG4gICAgY29uc3QgYXR0ZW1wdENvbnNvbGVPdXRwdXRzID0gdGhpcy5hdHRlbXB0Q29uc29sZU91dHB1dHMuZ2V0KHRlc3QpIHx8IFtdXG5cbiAgICBpZiAoYXR0ZW1wdC5jb25zb2xlT3V0cHV0KSB7XG4gICAgICBhdHRlbXB0Q29uc29sZU91dHB1dHMucHVzaCh7YXR0ZW1wdE51bWJlcjogYXR0ZW1wdC5hdHRlbXB0TnVtYmVyLCBvdXRwdXQ6IGF0dGVtcHQuY29uc29sZU91dHB1dC50cmltRW5kKCl9KVxuICAgICAgdGhpcy5hdHRlbXB0Q29uc29sZU91dHB1dHMuc2V0KHRlc3QsIGF0dGVtcHRDb25zb2xlT3V0cHV0cylcbiAgICB9XG5cbiAgICBjb25zdCByZXRyeUNvdW50ID0gdGhpcy50ZXN0UnVubmVyLnJldHJ5Q291bnQodGVzdClcbiAgICBjb25zdCBmYWlsZWQgPSBvdXRjb21lPy5mYWlsZWQgPz8gQm9vbGVhbihhdHRlbXB0LmVycm9yKVxuICAgIGNvbnN0IGVycm9yID0gb3V0Y29tZT8uZXJyb3JcbiAgICBjb25zdCByZXRyaWVzVXNlZCA9IE1hdGgubWluKGF0dGVtcHQuYXR0ZW1wdE51bWJlciwgcmV0cnlDb3VudClcbiAgICBjb25zdCB3aWxsUmV0cnkgPSBmYWlsZWQgJiYgIW91dGNvbWU/LmFib3J0UmVtYWluaW5nVGVzdHMgJiYgYXR0ZW1wdC5hdHRlbXB0TnVtYmVyIDw9IHJldHJ5Q291bnRcbiAgICBjb25zdCB7ZGVzY3JpcHRpb25zLCB0ZXN0RGVzY3JpcHRpb259ID0gdGhpcy50ZXN0UnVubmVyLnRlc3RNZXRhZGF0YSh0ZXN0KVxuICAgIGNvbnN0IGNvbXBhdGliaWxpdHkgPSB0aGlzLnRlc3RSdW5uZXIudGVzdERhdGEodGVzdClcblxuICAgIGlmIChmYWlsZWQpIHtcbiAgICAgIGF3YWl0IHRoaXMuZW1pdEV2ZW50KFwidGVzdEF0dGVtcHRGYWlsZWRcIiwge1xuICAgICAgICBjb25maWd1cmF0aW9uOiB0aGlzLnRlc3RSdW5uZXIuZ2V0Q29uZmlndXJhdGlvbigpLFxuICAgICAgICBkZXNjcmlwdGlvbnMsXG4gICAgICAgIGVycm9yLFxuICAgICAgICBhdHRlbXB0TnVtYmVyOiBhdHRlbXB0LmF0dGVtcHROdW1iZXIsXG4gICAgICAgIG5leHRBdHRlbXB0OiB3aWxsUmV0cnkgPyBhdHRlbXB0LmF0dGVtcHROdW1iZXIgKyAxIDogdW5kZWZpbmVkLFxuICAgICAgICByZXRyaWVzVXNlZCxcbiAgICAgICAgcmV0cnlDb3VudCxcbiAgICAgICAgdGVzdEFyZ3M6IGNvbXBhdGliaWxpdHkudGVzdEFyZ3MsXG4gICAgICAgIHRlc3REYXRhOiBjb21wYXRpYmlsaXR5LnRlc3REYXRhLFxuICAgICAgICB0ZXN0RGVzY3JpcHRpb24sXG4gICAgICAgIHRlc3RSdW5uZXI6IHRoaXMudGVzdFJ1bm5lcixcbiAgICAgICAgd2lsbFJldHJ5XG4gICAgICB9KVxuICAgIH1cblxuICAgIGlmICh3aWxsUmV0cnkpIHtcbiAgICAgIGNvbnNvbGUud2FybihwaWNvY29sb3JzLnJlZChgJHtcIiBcIi5yZXBlYXQoZGVzY3JpcHRpb25zLmxlbmd0aCAqIDIpfSAgUmV0cnlpbmcgKCR7cmV0cmllc1VzZWR9LyR7cmV0cnlDb3VudH0pIGFmdGVyIGVycm9yOiAke2Vycm9yIGluc3RhbmNlb2YgRXJyb3IgPyBlcnJvci5tZXNzYWdlIDogU3RyaW5nKGVycm9yKX1gKSlcbiAgICAgIGF3YWl0IHRoaXMuZW1pdEV2ZW50KFwidGVzdFJldHJ5aW5nXCIsIHtcbiAgICAgICAgY29uZmlndXJhdGlvbjogdGhpcy50ZXN0UnVubmVyLmdldENvbmZpZ3VyYXRpb24oKSxcbiAgICAgICAgZGVzY3JpcHRpb25zLFxuICAgICAgICBlcnJvcixcbiAgICAgICAgbmV4dEF0dGVtcHQ6IGF0dGVtcHQuYXR0ZW1wdE51bWJlciArIDEsXG4gICAgICAgIHJldHJpZXNVc2VkLFxuICAgICAgICByZXRyeUNvdW50LFxuICAgICAgICB0ZXN0QXJnczogY29tcGF0aWJpbGl0eS50ZXN0QXJncyxcbiAgICAgICAgdGVzdERhdGE6IGNvbXBhdGliaWxpdHkudGVzdERhdGEsXG4gICAgICAgIHRlc3REZXNjcmlwdGlvbixcbiAgICAgICAgdGVzdFJ1bm5lcjogdGhpcy50ZXN0UnVubmVyXG4gICAgICB9KVxuICAgIH1cblxuICAgIGlmIChhdHRlbXB0LmF0dGVtcHROdW1iZXIgPiAxKSB7XG4gICAgICBhd2FpdCB0aGlzLmVtaXRFdmVudChcInRlc3RSZXRyaWVkXCIsIHtcbiAgICAgICAgY29uZmlndXJhdGlvbjogdGhpcy50ZXN0UnVubmVyLmdldENvbmZpZ3VyYXRpb24oKSxcbiAgICAgICAgZGVzY3JpcHRpb25zLFxuICAgICAgICBlcnJvcixcbiAgICAgICAgYXR0ZW1wdE51bWJlcjogYXR0ZW1wdC5hdHRlbXB0TnVtYmVyLFxuICAgICAgICByZXRyaWVzVXNlZCxcbiAgICAgICAgcmV0cnlDb3VudCxcbiAgICAgICAgdGVzdEFyZ3M6IGNvbXBhdGliaWxpdHkudGVzdEFyZ3MsXG4gICAgICAgIHRlc3REYXRhOiBjb21wYXRpYmlsaXR5LnRlc3REYXRhLFxuICAgICAgICB0ZXN0RGVzY3JpcHRpb24sXG4gICAgICAgIHRlc3RSdW5uZXI6IHRoaXMudGVzdFJ1bm5lclxuICAgICAgfSlcbiAgICB9XG5cbiAgICBpZiAob3V0Y29tZT8uYWJvcnRSZW1haW5pbmdUZXN0cykge1xuICAgICAgY29uc3QgbWV0YWRhdGEgPSB0aGlzLnRlc3RSdW5uZXIudGVzdE1ldGFkYXRhKHRlc3QpXG5cbiAgICAgIHRoaXMudGVzdFJ1bm5lci5yZWNvcmRUZXN0RHVyYXRpb24oe1xuICAgICAgICBkdXJhdGlvbk1zOiBhdHRlbXB0LmR1cmF0aW9uTXMsXG4gICAgICAgIGZpbGVQYXRoOiBjb21wYXRpYmlsaXR5LnRlc3REYXRhLmZpbGVQYXRoID8/IFwiPHVua25vd24+XCIsXG4gICAgICAgIGZ1bGxEZXNjcmlwdGlvbjogbWV0YWRhdGEuZnVsbERlc2NyaXB0aW9uLFxuICAgICAgICBsaW5lOiBjb21wYXRpYmlsaXR5LnRlc3REYXRhLmxpbmUgPz8gMFxuICAgICAgfSlcbiAgICAgIGF3YWl0IHRoaXMucmVwb3J0RmFpbGVkVGVzdCh7XG4gICAgICAgIGF0dGVtcHRDb25zb2xlT3V0cHV0cyxcbiAgICAgICAgZGVzY3JpcHRpb25zLFxuICAgICAgICBlcnJvcixcbiAgICAgICAgbGVmdFBhZGRpbmc6IFwiIFwiLnJlcGVhdChkZXNjcmlwdGlvbnMubGVuZ3RoICogMiksXG4gICAgICAgIHRlc3RBcmdzOiBjb21wYXRpYmlsaXR5LnRlc3RBcmdzLFxuICAgICAgICB0ZXN0RGF0YTogY29tcGF0aWJpbGl0eS50ZXN0RGF0YSxcbiAgICAgICAgdGVzdERlc2NyaXB0aW9uXG4gICAgICB9KVxuICAgICAgdGhpcy50ZXN0UnVubmVyLmNvbXBsZXRlVGVzdERlY2xhcmF0aW9uKHRlc3QpXG4gICAgICB0aGlzLmFjdGl2ZVRlc3QgPSB1bmRlZmluZWRcbiAgICAgIHRocm93IG5ldyBBYm9ydFJlbWFpbmluZ1Rlc3RzRXJyb3IoXCJWZWxvY2lvdXMgcXVhcmFudGluZWQgYW4gYXR0ZW1wdC1vd25lZCBkYXRhYmFzZSBjb25uZWN0aW9uXCIpXG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIFByb2plY3RzIGZpbmFsIHBhY2thZ2UgcmVzdWx0IGFjY291bnRpbmcgYW5kIGZhaWx1cmVzLlxuICAgKiBAcGFyYW0ge2ltcG9ydChcIkB2ZWxvY2lvdXMvdGVzdGluZy9ydW5uZXJcIikuUnVubmVyRXZlbnR9IGV2ZW50IC0gVGVzdCByZXN1bHQgZXZlbnQuXG4gICAqIEByZXR1cm5zIHtQcm9taXNlPHZvaWQ+fSAtIFJlc29sdmVzIGFmdGVyIGxpc3RlbmVycyBmaW5pc2guXG4gICAqL1xuICBhc3luYyByZXBvcnRUZXN0RXZlbnQoZXZlbnQpIHtcbiAgICAvLyBOYXJyb3dzIHRoZSBzdHJ1Y3R1cmVkIGV2ZW50IHBheWxvYWQgZm9yIHRoaXMgZXZlbnQgZGlzY3JpbWluYXRvci5cbiAgICBjb25zdCBwYWNrYWdlVGVzdFJlc3VsdCA9IC8qKiBAdHlwZSB7aW1wb3J0KFwiQHZlbG9jaW91cy90ZXN0aW5nL3J1bm5lclwiKS5UZXN0UmVzdWx0fSAqLyAoZXZlbnQudGVzdClcbiAgICBjb25zdCB0ZXN0ID0gdGhpcy5hY3RpdmVUZXN0IHx8IHRoaXMudGVzdFJ1bm5lci5maW5kVGVzdERlY2xhcmF0aW9uKHBhY2thZ2VUZXN0UmVzdWx0LmZ1bGxOYW1lKVxuXG4gICAgaWYgKCF0ZXN0KSB0aHJvdyBuZXcgRXJyb3IoYFBhY2thZ2UgcnVubmVyIHJlc3VsdCBkaWQgbm90IG1hdGNoIGEgZGVjbGFyYXRpb246ICR7cGFja2FnZVRlc3RSZXN1bHQuZnVsbE5hbWV9YClcblxuICAgIGNvbnN0IG1ldGFkYXRhID0gdGhpcy50ZXN0UnVubmVyLnRlc3RNZXRhZGF0YSh0ZXN0KVxuICAgIGNvbnN0IGNvbXBhdGliaWxpdHkgPSB0aGlzLnRlc3RSdW5uZXIudGVzdERhdGEodGVzdClcbiAgICBjb25zdCBkdXJhdGlvbk1zID0gcGFja2FnZVRlc3RSZXN1bHQuYXR0ZW1wdHMucmVkdWNlKCh0b3RhbCwgYXR0ZW1wdCkgPT4gdG90YWwgKyBhdHRlbXB0LmR1cmF0aW9uTXMsIDApXG5cbiAgICBpZiAocGFja2FnZVRlc3RSZXN1bHQuYXR0ZW1wdHMubGVuZ3RoID4gMCkge1xuICAgICAgdGhpcy50ZXN0UnVubmVyLnJlY29yZFRlc3REdXJhdGlvbih7XG4gICAgICAgIGR1cmF0aW9uTXMsXG4gICAgICAgIGZpbGVQYXRoOiBjb21wYXRpYmlsaXR5LnRlc3REYXRhLmZpbGVQYXRoID8/IFwiPHVua25vd24+XCIsXG4gICAgICAgIGZ1bGxEZXNjcmlwdGlvbjogbWV0YWRhdGEuZnVsbERlc2NyaXB0aW9uLFxuICAgICAgICBsaW5lOiBjb21wYXRpYmlsaXR5LnRlc3REYXRhLmxpbmUgPz8gMFxuICAgICAgfSlcbiAgICB9XG5cbiAgICBpZiAocGFja2FnZVRlc3RSZXN1bHQuc3RhdHVzID09PSBcInBhc3NlZFwiKSB7XG4gICAgICB0aGlzLnRlc3RSdW5uZXIucmVjb3JkU3VjY2Vzc2Z1bFRlc3QoKVxuICAgIH0gZWxzZSB7XG4gICAgICBjb25zdCBmaW5hbEF0dGVtcHQgPSBwYWNrYWdlVGVzdFJlc3VsdC5hdHRlbXB0cy5hdCgtMSlcbiAgICAgIGNvbnN0IG91dGNvbWUgPSBmaW5hbEF0dGVtcHRcbiAgICAgICAgPyB0aGlzLnRlc3RSdW5uZXIuYXR0ZW1wdE91dGNvbWUodGVzdCwgZmluYWxBdHRlbXB0LmF0dGVtcHROdW1iZXIpXG4gICAgICAgIDogdW5kZWZpbmVkXG4gICAgICBjb25zdCBzZXR1cEZhaWx1cmUgPSB0aGlzLnRlc3RSdW5uZXIuc2V0dXBGYWlsdXJlT3V0Y29tZUZvcih0ZXN0KVxuICAgICAgY29uc3QgZXJyb3IgPSBvdXRjb21lPy5mYWlsZWRcbiAgICAgICAgPyBvdXRjb21lLmVycm9yXG4gICAgICAgIDogc2V0dXBGYWlsdXJlLmZhaWxlZFxuICAgICAgICAgID8gc2V0dXBGYWlsdXJlLmVycm9yXG4gICAgICAgICAgOiBwYWNrYWdlVGVzdFJlc3VsdC5lcnJvclxuICAgICAgICAgICAgPyBlcnJvckZyb21QYWNrYWdlUmVjb3JkKHBhY2thZ2VUZXN0UmVzdWx0LmVycm9yKVxuICAgICAgICAgICAgOiB1bmRlZmluZWRcblxuICAgICAgYXdhaXQgdGhpcy5yZXBvcnRGYWlsZWRUZXN0KHtcbiAgICAgICAgYXR0ZW1wdENvbnNvbGVPdXRwdXRzOiB0aGlzLmF0dGVtcHRDb25zb2xlT3V0cHV0cy5nZXQodGVzdCkgfHwgW10sXG4gICAgICAgIGRlc2NyaXB0aW9uczogbWV0YWRhdGEuZGVzY3JpcHRpb25zLFxuICAgICAgICBlcnJvcixcbiAgICAgICAgbGVmdFBhZGRpbmc6IFwiIFwiLnJlcGVhdChtZXRhZGF0YS5kZXNjcmlwdGlvbnMubGVuZ3RoICogMiksXG4gICAgICAgIHRlc3RBcmdzOiBjb21wYXRpYmlsaXR5LnRlc3RBcmdzLFxuICAgICAgICB0ZXN0RGF0YTogY29tcGF0aWJpbGl0eS50ZXN0RGF0YSxcbiAgICAgICAgdGVzdERlc2NyaXB0aW9uOiBtZXRhZGF0YS50ZXN0RGVzY3JpcHRpb25cbiAgICAgIH0pXG4gICAgfVxuXG4gICAgdGhpcy50ZXN0UnVubmVyLmNvbXBsZXRlVGVzdERlY2xhcmF0aW9uKHRlc3QpXG4gICAgdGhpcy5hY3RpdmVUZXN0ID0gdW5kZWZpbmVkXG4gIH1cblxuICAvKipcbiAgICogUmVjb3JkcyBhbmQgZW1pdHMgb25lIGZpbmFsIGZhaWxlZCB0ZXN0IHJlc3VsdC5cbiAgICogQHBhcmFtIHtvYmplY3R9IGFyZ3MgLSBGaW5hbCBmYWlsdXJlIG1ldGFkYXRhLlxuICAgKiBAcGFyYW0ge2ltcG9ydChcIi4vdGVzdC1ydW5uZXIuanNcIikuQXR0ZW1wdENvbnNvbGVPdXRwdXRbXX0gYXJncy5hdHRlbXB0Q29uc29sZU91dHB1dHMgLSBDYXB0dXJlZCBvdXRwdXQgYWNyb3NzIGF0dGVtcHRzLlxuICAgKiBAcGFyYW0ge3N0cmluZ1tdfSBhcmdzLmRlc2NyaXB0aW9ucyAtIFBhcmVudCBkZXNjcmlwdGlvbiBzdGFjay5cbiAgICogQHBhcmFtIHtSZXR1cm5UeXBlPHR5cGVvZiBKU09OLnBhcnNlPn0gYXJncy5lcnJvciAtIFJhdyBmaW5hbCBmYWlsdXJlLlxuICAgKiBAcGFyYW0ge3N0cmluZ30gYXJncy5sZWZ0UGFkZGluZyAtIENvbnNvbGUgaW5kZW50YXRpb24uXG4gICAqIEBwYXJhbSB7aW1wb3J0KFwiLi90ZXN0LXJ1bm5lci5qc1wiKS5UZXN0QXJnc30gYXJncy50ZXN0QXJncyAtIFN0YWJsZSB0ZXN0IGFyZ3VtZW50cy5cbiAgICogQHBhcmFtIHtpbXBvcnQoXCIuL3Rlc3QtcnVubmVyLmpzXCIpLlRlc3REYXRhfSBhcmdzLnRlc3REYXRhIC0gVGVzdCByZWdpc3RyYXRpb24uXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBhcmdzLnRlc3REZXNjcmlwdGlvbiAtIFRlc3QgZGVzY3JpcHRpb24uXG4gICAqIEByZXR1cm5zIHtQcm9taXNlPHZvaWQ+fSAtIFJlc29sdmVzIGFmdGVyIHRoZSBmaW5hbC1mYWlsdXJlIGxpc3RlbmVyIGNvbXBsZXRlcy5cbiAgICovXG4gIGFzeW5jIHJlcG9ydEZhaWxlZFRlc3Qoe2F0dGVtcHRDb25zb2xlT3V0cHV0cywgZGVzY3JpcHRpb25zLCBlcnJvciwgbGVmdFBhZGRpbmcsIHRlc3RBcmdzLCB0ZXN0RGF0YSwgdGVzdERlc2NyaXB0aW9ufSkge1xuICAgIGNvbnN0IHRlc3RSdW5uZXIgPSB0aGlzLnRlc3RSdW5uZXJcbiAgICBjb25zdCBjb25zb2xlT3V0cHV0ID0gdGVzdFJ1bm5lci5idWlsZENvbnNvbGVPdXRwdXQoYXR0ZW1wdENvbnNvbGVPdXRwdXRzKVxuXG4gICAgaWYgKGVycm9yIGluc3RhbmNlb2YgRXJyb3IpIHtcbiAgICAgIGNvbnNvbGUuZXJyb3IocGljb2NvbG9ycy5yZWQoYCR7bGVmdFBhZGRpbmd9ICBUZXN0IGZhaWxlZDogJHtlcnJvci5tZXNzYWdlfWApKVxuICAgICAgYWRkVHJhY2tlZFN0YWNrVG9FcnJvcihlcnJvcilcblxuICAgICAgY29uc3QgYmFja3RyYWNlQ2xlYW5lciA9IG5ldyBCYWNrdHJhY2VDbGVhbmVyKGVycm9yKVxuICAgICAgY29uc3QgY2xlYW5lZFN0YWNrID0gYmFja3RyYWNlQ2xlYW5lci5nZXRDbGVhbmVkU3RhY2soKVxuICAgICAgY29uc3Qgc3RhY2tMaW5lcyA9IGNsZWFuZWRTdGFjaz8uc3BsaXQoXCJcXG5cIilcblxuICAgICAgaWYgKHN0YWNrTGluZXMpIHtcbiAgICAgICAgZm9yIChjb25zdCBzdGFja0xpbmUgb2Ygc3RhY2tMaW5lcykgY29uc29sZS5lcnJvcihwaWNvY29sb3JzLnJlZChgJHtsZWZ0UGFkZGluZ30gICR7c3RhY2tMaW5lfWApKVxuICAgICAgfVxuICAgIH0gZWxzZSB7XG4gICAgICBjb25zb2xlLmVycm9yKHBpY29jb2xvcnMucmVkKGAke2xlZnRQYWRkaW5nfSAgVGVzdCBmYWlsZWQgd2l0aCBhICR7dHlwZW9mIGVycm9yfTogJHtTdHJpbmcoZXJyb3IpfWApKVxuICAgIH1cblxuICAgIHRlc3RSdW5uZXIucHJpbnRGYWlsZWRDb25zb2xlT3V0cHV0KHtjb25zb2xlT3V0cHV0LCBsZWZ0UGFkZGluZ30pXG4gICAgdGVzdFJ1bm5lci5yZWNvcmRGYWlsZWRUZXN0KHtkZXNjcmlwdGlvbnMsIGVycm9yLCBjb25zb2xlT3V0cHV0LCB0ZXN0RGF0YSwgdGVzdERlc2NyaXB0aW9ufSlcblxuICAgIGF3YWl0IHRoaXMuZW1pdEV2ZW50KFwidGVzdEZhaWxlZFwiLCB7XG4gICAgICBjb25maWd1cmF0aW9uOiB0ZXN0UnVubmVyLmdldENvbmZpZ3VyYXRpb24oKSxcbiAgICAgIGRlc2NyaXB0aW9ucyxcbiAgICAgIGVycm9yLFxuICAgICAgdGVzdEFyZ3MsXG4gICAgICB0ZXN0RGF0YSxcbiAgICAgIHRlc3REZXNjcmlwdGlvbixcbiAgICAgIHRlc3RSdW5uZXJcbiAgICB9KVxuXG4gICAgdGVzdFJ1bm5lci5wcmludFJlcnVuQ29tbWFuZCh7ZGVzY3JpcHRpb25zLCB0ZXN0RGVzY3JpcHRpb24sIHRlc3REYXRhLCBsZWZ0UGFkZGluZ30pXG4gIH1cblxuICAvKipcbiAgICogRW1pdHMgb25lIGxlZ2FjeSBldmVudCBhbmQgYXdhaXRzIGxpc3RlbmVycyBpbiByZWdpc3RyYXRpb24gb3JkZXIuXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBldmVudE5hbWUgLSBFdmVudCBuYW1lLlxuICAgKiBAcGFyYW0ge29iamVjdH0gcGF5bG9hZCAtIEV2ZW50IHBheWxvYWQuXG4gICAqIEByZXR1cm5zIHtQcm9taXNlPHZvaWQ+fSAtIFJlc29sdmVzIHdoZW4gYWxsIGxpc3RlbmVycyBjb21wbGV0ZS5cbiAgICovXG4gIGFzeW5jIGVtaXRFdmVudChldmVudE5hbWUsIHBheWxvYWQpIHtcbiAgICBmb3IgKGNvbnN0IGxpc3RlbmVyIG9mIHRlc3RFdmVudHMubGlzdGVuZXJzKGV2ZW50TmFtZSkpIGF3YWl0IGxpc3RlbmVyKHBheWxvYWQpXG4gIH1cbn1cbiJdfQ==
302
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidmVsb2Npb3VzLXJ1bm5lci1yZXBvcnRlci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy90ZXN0aW5nL3ZlbG9jaW91cy1ydW5uZXItcmVwb3J0ZXIuanMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsWUFBWTtBQUVaLE9BQU8sRUFBRSxzQkFBc0IsRUFBRSxNQUFNLGdDQUFnQyxDQUFBO0FBQ3ZFLE9BQU8sVUFBVSxNQUFNLFlBQVksQ0FBQTtBQUNuQyxPQUFPLGFBQWEsTUFBTSw2QkFBNkIsQ0FBQTtBQUN2RCxPQUFPLEVBQUUsVUFBVSxFQUFFLE1BQU0sV0FBVyxDQUFBO0FBRXRDLDRGQUE0RjtBQUM1Riw0RkFBNEY7QUFFNUY7Ozs7R0FJRztBQUNILFNBQVMsc0JBQXNCLENBQUMsV0FBVztJQUN6QyxNQUFNLEtBQUssR0FBRyxXQUFXLENBQUMsTUFBTTtRQUM5QixDQUFDLENBQUMsSUFBSSxjQUFjLENBQUMsV0FBVyxDQUFDLE1BQU0sQ0FBQyxHQUFHLENBQUMsc0JBQXNCLENBQUMsRUFBRSxXQUFXLENBQUMsT0FBTyxDQUFDO1FBQ3pGLENBQUMsQ0FBQyxJQUFJLEtBQUssQ0FBQyxXQUFXLENBQUMsT0FBTyxDQUFDLENBQUE7SUFFbEMsS0FBSyxDQUFDLElBQUksR0FBRyxXQUFXLENBQUMsSUFBSSxDQUFBO0lBQzdCLElBQUksV0FBVyxDQUFDLEtBQUs7UUFBRSxLQUFLLENBQUMsS0FBSyxHQUFHLFdBQVcsQ0FBQyxLQUFLLENBQUE7SUFDdEQsSUFBSSxXQUFXLENBQUMsS0FBSztRQUFFLEtBQUssQ0FBQyxLQUFLLEdBQUcsc0JBQXNCLENBQUMsV0FBVyxDQUFDLEtBQUssQ0FBQyxDQUFBO0lBQzlFLElBQUksV0FBVyxDQUFDLGdCQUFnQjtRQUFFLE1BQU0sQ0FBQyxNQUFNLENBQUMsS0FBSyxFQUFFLEVBQUMsZ0JBQWdCLEVBQUUsV0FBVyxDQUFDLGdCQUFnQixFQUFDLENBQUMsQ0FBQTtJQUV4RyxPQUFPLEtB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LFVBQVUsQ0FBQyxTQUFTLENBQUMsU0FBUyxDQUFDO1lBQUUsTUFBTSxRQUFRLENBQUMsT0FBTyxDQUFDLENBQUE7SUFDakYsQ0FBQztDQUNGIiwic291cmNlc0NvbnRlbnQiOlsiLy8gQHRzLWNoZWNrXG5cbmltcG9ydCB7IGFkZFRyYWNrZWRTdGFja1RvRXJyb3IgfSBmcm9tIFwiLi4vdXRpbHMvd2l0aC10cmFja2VkLXN0YWNrLmpzXCJcbmltcG9ydCBwaWNvY29sb3JzIGZyb20gXCJwaWNvY29sb3JzXCJcbmltcG9ydCByZXN0QXJnc0Vycm9yIGZyb20gXCIuLi91dGlscy9yZXN0LWFyZ3MtZXJyb3IuanNcIlxuaW1wb3J0IHsgdGVzdEV2ZW50cyB9IGZyb20gXCIuL3Rlc3QuanNcIlxuXG4vKiogQHR5cGVkZWYge2ltcG9ydChcIkB2ZWxvY2lvdXMvdGVzdGluZy9ydW5uZXJcIikuVGVzdERlY2xhcmF0aW9ufSBQYWNrYWdlVGVzdERlY2xhcmF0aW9uICovXG4vKiogQHR5cGVkZWYge2ltcG9ydChcIkB2ZWxvY2lvdXMvdGVzdGluZy9ydW5uZXJcIikuVGVzdEVycm9yUmVjb3JkfSBQYWNrYWdlVGVzdEVycm9yUmVjb3JkICovXG5cbi8qKlxuICogUmVzdG9yZXMgdGhlIEVycm9yIHNoYXBlIHNlcmlhbGl6ZWQgYnkgdGhlIHBhY2thZ2UgcnVubmVyLlxuICogQHBhcmFtIHtQYWNrYWdlVGVzdEVycm9yUmVjb3JkfSBlcnJvclJlY29yZCAtIFNlcmlhbGl6ZWQgcGFja2FnZSBmYWlsdXJlLlxuICogQHJldHVybnMge0Vycm9yfSAtIEVycm9yIGNvbXBhdGlibGUgd2l0aCBWZWxvY2lvdXMncyBsZWdhY3kgcmVwb3J0ZXIgY29udHJhY3QuXG4gKi9cbmZ1bmN0aW9uIGVycm9yRnJvbVBhY2thZ2VSZWNvcmQoZXJyb3JSZWNvcmQpIHtcbiAgY29uc3QgZXJyb3IgPSBlcnJvclJlY29yZC5lcnJvcnNcbiAgICA/IG5ldyBBZ2dyZWdhdGVFcnJvcihlcnJvclJlY29yZC5lcnJvcnMubWFwKGVycm9yRnJvbVBhY2thZ2VSZWNvcmQpLCBlcnJvclJlY29yZC5tZXNzYWdlKVxuICAgIDogbmV3IEVycm9yKGVycm9yUmVjb3JkLm1lc3NhZ2UpXG5cbiAgZXJyb3IubmFtZSA9IGVycm9yUmVjb3JkLm5hbWVcbiAgaWYgKGVycm9yUmVjb3JkLnN0YWNrKSBlcnJvci5zdGFjayA9IGVycm9yUmVjb3JkLnN0YWNrXG4gIGlmIChlcnJvclJlY29yZC5jYXVzZSkgZXJyb3IuY2F1c2UgPSBlcnJvckZyb21QYWNrYWdlUmVjb3JkKGVycm9yUmVjb3JkLmNhdXNlKVxuICBpZiAoZXJyb3JSZWNvcmQudGVybWluYWxSZXNvdXJjZSkgT2JqZWN0LmFzc2lnbihlcnJvciwge3Rlcm1pbmFsUmVzb3VyY2U6IGVycm9yUmVjb3JkLnRlcm1pbmFsUmVzb3VyY2V9KVxuXG4gIHJldHVybiBlcnJvclxufVxuXG4vKiogU3RvcHMgcGFja2FnZSB0cmF2ZXJzYWwgYWZ0ZXIgZnJhbWV3b3JrLW93bmVkIGNvbm5lY3Rpb24gcXVhcmFudGluZS4gKi9cbmV4cG9ydCBjbGFzcyBBYm9ydFJlbWFpbmluZ1Rlc3RzRXJyb3IgZXh0ZW5kcyBFcnJvciB7fVxuXG5leHBvcnQgZGVmYXVsdCBjbGFzcyBWZWxvY2lvdXNSdW5uZXJSZXBvcnRlciB7XG4gIC8qKlxuICAgKiBDcmVhdGVzIHRoZSBsZWdhY3kgZXZlbnQgYW5kIHJlc3VsdCBwcm9qZWN0aW9uIGFkYXB0ZXIuXG4gICAqIEBwYXJhbSB7b2JqZWN0fSBhcmdzIC0gQ29uc3RydWN0b3IgYXJndW1lbnRzLlxuICAgKiBAcGFyYW0ge2ltcG9ydChcIi4vdGVzdC1ydW5uZXIuanNcIikuZGVmYXVsdH0gYXJncy50ZXN0UnVubmVyIC0gT3duaW5nIFZlbG9jaW91cyBydW5uZXIuXG4gICAqL1xuICBjb25zdHJ1Y3Rvcih7dGVzdFJ1bm5lciwgLi4ucmVzdEFyZ3N9KSB7XG4gICAgcmVzdEFyZ3NFcnJvcihyZXN0QXJncylcbiAgICB0aGlzLnRlc3RSdW5uZXIgPSB0ZXN0UnVubmVyXG4gICAgLyoqIEB0eXBlIHtXZWFrTWFwPFBhY2thZ2VUZXN0RGVjbGFyYXRpb24sIGltcG9ydChcIi4vdGVzdC1ydW5uZXIuanNcIikuQXR0ZW1wdENvbnNvbGVPdXRwdXRbXT59ICovXG4gICAgdGhpcy5hdHRlbXB0Q29uc29sZU91dHB1dHMgPSBuZXcgV2Vha01hcCgpXG4gICAgLyoqIEB0eXBlIHtQYWNrYWdlVGVzdERlY2xhcmF0aW9uIHwgdW5kZWZpbmVkfSAqL1xuICAgIHRoaXMuYWN0aXZlVGVzdCA9IHVuZGVmaW5lZFxuICB9XG5cbiAgLyoqXG4gICAqIFRyYW5zbGF0ZXMgb25lIGF3YWl0ZWQgcGFja2FnZSBydW5uZXIgZXZlbnQgaW50byB0aGUgbGVnYWN5IGNvbnRyYWN0LlxuICAgKiBAcGFyYW0ge2ltcG9ydChcIkB2ZWxvY2lvdXMvdGVzdGluZy9ydW5uZXJcIikuUnVubmVyRXZlbnR9IGV2ZW50IC0gU3RydWN0dXJlZCBwYWNrYWdlIGV2ZW50LlxuICAgKiBAcmV0dXJucyB7UHJvbWlzZTx2b2lkPn0gLSBSZXNvbHZlcyBhZnRlciBsZWdhY3kgbGlzdGVuZXJzIGZpbmlzaC5cbiAgICovXG4gIGFzeW5jIG9uRXZlbnQoZXZlbnQpIHtcbiAgICBpZiAoZXZlbnQudHlwZSA9PT0gXCJ0ZXN0OnN0YXJ0XCIpIHtcbiAgICAgIHRoaXMuYWN0aXZlVGVzdCA9IHRoaXMudGVzdFJ1bm5lci5maW5kVGVzdERlY2xhcmF0aW9uKGV2ZW50LmZ1bGxOYW1lKVxuICAgICAgaWYgKHRoaXMuYWN0aXZlVGVzdCkge1xuICAgICAgICBjb25zdCBtZXRhZGF0YSA9IHRoaXMudGVzdFJ1bm5lci50ZXN0TWV0YWRhdGEodGhpcy5hY3RpdmVUZXN0KVxuICAgICAgICBjb25zb2xlLmxvZyhgJHtcIiBcIi5yZXBlYXQobWV0YWRhdGEuZGVzY3JpcHRpb25zLmxlbmd0aCAqIDIpfWl0ICR7bWV0YWRhdGEudGVzdERlc2NyaXB0aW9ufWApXG4gICAgICB9XG4gICAgICByZXR1cm5cbiAgICB9XG5cbiAgICBpZiAoZXZlbnQudHlwZSA9PT0gXCJhdHRlbXB0OmZpbmlzaFwiKSB7XG4gICAgICBhd2FpdCB0aGlzLnJlcG9ydEF0dGVtcHRFdmVudChldmVudClcbiAgICAgIHJldHVyblxuICAgIH1cblxuICAgIGlmIChldmVudC50eXBlID09PSBcInRlc3Q6ZmluaXNoXCIpIHtcbiAgICAgIGF3YWl0IHRoaXMucmVwb3J0VGVzdEV2ZW50KGV2ZW50KVxuICAgICAgcmV0dXJuXG4gICAgfVxuXG4gICAgaWYgKGV2ZW50LnR5cGUgPT09IFwidGVzdDpub3QtcnVuXCIpIHtcbiAgICAgIGNvbnN0IHRlc3QgPSB0aGlzLnRlc3RSdW5uZXIuZmluZFRlc3REZWNsYXJhdGlvbihldmVudC50ZXN0LmZ1bGxOYW1lKVxuICAgICAgaWYgKCF0ZXN0KSB0aHJvdyBuZXcgRXJyb3IoYFBhY2thZ2Ugbm90LXJ1biByZXN1bHQgZGlkIG5vdCBtYXRjaCBhIGRlY2xhcmF0aW9uOiAke2V2ZW50LnRlc3QuZnVsbE5hbWV9YClcbiAgICAgIHRoaXMudGVzdFJ1bm5lci5yZWNvcmROb3RSdW5UZXN0KGV2ZW50LnRlc3QpXG4gICAgICB0aGlzLnRlc3RSdW5uZXIuY29tcGxldGVUZXN0RGVjbGFyYXRpb24odGVzdClcbiAgICAgIGNvbnNvbGUuZXJyb3IoYE5vdCBydW46ICR7ZXZlbnQudGVzdC5mdWxsTmFtZX0gKHRlcm1pbmFsIHJlc291cmNlIGZhaWx1cmUgaW4gJHtldmVudC50ZXN0LnJlYXNvbi5mdWxsTmFtZX0pYClcbiAgICAgIGF3YWl0IHRoaXMuZW1pdEV2ZW50KFwidGVzdE5vdFJ1blwiLCB7XG4gICAgICAgIGNvbmZpZ3VyYXRpb246IHRoaXMudGVzdFJ1bm5lci5nZXRDb25maWd1cmF0aW9uKCksXG4gICAgICAgIHRlc3Q6IGV2ZW50LnRlc3QsXG4gICAgICAgIHRlc3RSdW5uZXI6IHRoaXMudGVzdFJ1bm5lclxuICAgICAgfSlcbiAgICAgIHJldHVyblxuICAgIH1cblxuICAgIGlmIChldmVudC50eXBlID09PSBcInJ1bjpmaW5pc2hcIikge1xuICAgICAgdGhpcy50ZXN0UnVubmVyLnJlY29yZFBhY2thZ2VSZXN1bHQoZXZlbnQucmVzdWx0KVxuICAgICAgZm9yIChjb25zdCBmYWlsdXJlIG9mIGV2ZW50LnJlc3VsdC5lcnJvcnMpIHtcbiAgICAgICAgY29uc29sZS5lcnJvcihgU3VpdGUgJHtmYWlsdXJlLnBoYXNlfSBmYWlsZWQ6ICR7ZmFpbHVyZS5zdWl0ZX1gKVxuICAgICAgICB0aGlzLnByaW50RXJyb3JDYXVzZXMoZXJyb3JGcm9tUGFja2FnZVJlY29yZChmYWlsdXJlLmVycm9yKSlcbiAgICAgIH1cbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogUHJvamVjdHMgYXR0ZW1wdCBmYWlsdXJlL3JldHJ5IGV2ZW50cyB3aGlsZSByZXRhaW5pbmcgdGhlIHJhdyB0aHJvd24gdmFsdWUuXG4gICAqIEBwYXJhbSB7aW1wb3J0KFwiQHZlbG9jaW91cy90ZXN0aW5nL3J1bm5lclwiKS5SdW5uZXJFdmVudH0gZXZlbnQgLSBBdHRlbXB0IGV2ZW50LlxuICAgKiBAcmV0dXJucyB7UHJvbWlzZTx2b2lkPn0gLSBSZXNvbHZlcyBhZnRlciBsaXN0ZW5lcnMgZmluaXNoLlxuICAgKi9cbiAgYXN5bmMgcmVwb3J0QXR0ZW1wdEV2ZW50KGV2ZW50KSB7XG4gICAgY29uc3QgdGVzdCA9IHRoaXMuYWN0aXZlVGVzdCB8fCB0aGlzLnRlc3RSdW5uZXIuZmluZFRlc3REZWNsYXJhdGlvbihldmVudC5mdWxsTmFtZSlcblxuICAgIGlmICghdGVzdCkgdGhyb3cgbmV3IEVycm9yKGBQYWNrYWdlIHJ1bm5lciBhdHRlbXB0IGRpZCBub3QgbWF0Y2ggYSBkZWNsYXJhdGlvbjogJHtldmVudC5mdWxsTmFtZX1gKVxuXG4gICAgLy8gTmFycm93cyB0aGUgc3RydWN0dXJlZCBldmVudCBwYXlsb2FkIGZvciB0aGlzIGV2ZW50IGRpc2NyaW1pbmF0b3IuXG4gICAgY29uc3QgYXR0ZW1wdCA9IC8qKiBAdHlwZSB7aW1wb3J0KFwiQHZlbG9jaW91cy90ZXN0aW5nL3J1bm5lclwiKS5UZXN0QXR0ZW1wdFJlc3VsdH0gKi8gKGV2ZW50LmF0dGVtcHQpXG4gICAgY29uc3Qgb3V0Y29tZSA9IHRoaXMudGVzdFJ1bm5lci5hdHRlbXB0T3V0Y29tZSh0ZXN0LCBhdHRlbXB0LmF0dGVtcHROdW1iZXIpXG4gICAgY29uc3QgYXR0ZW1wdENvbnNvbGVPdXRwdXRzID0gdGhpcy5hdHRlbXB0Q29uc29sZU91dHB1dHMuZ2V0KHRlc3QpIHx8IFtdXG5cbiAgICBpZiAoYXR0ZW1wdC5jb25zb2xlT3V0cHV0KSB7XG4gICAgICBhdHRlbXB0Q29uc29sZU91dHB1dHMucHVzaCh7YXR0ZW1wdE51bWJlcjogYXR0ZW1wdC5hdHRlbXB0TnVtYmVyLCBvdXRwdXQ6IGF0dGVtcHQuY29uc29sZU91dHB1dC50cmltRW5kKCl9KVxuICAgICAgdGhpcy5hdHRlbXB0Q29uc29sZU91dHB1dHMuc2V0KHRlc3QsIGF0dGVtcHRDb25zb2xlT3V0cHV0cylcbiAgICB9XG5cbiAgICBjb25zdCByZXRyeUNvdW50ID0gdGhpcy50ZXN0UnVubmVyLnJldHJ5Q291bnQodGVzdClcbiAgICBjb25zdCBmYWlsZWQgPSBvdXRjb21lPy5mYWlsZWQgPz8gQm9vbGVhbihhdHRlbXB0LmVycm9yKVxuICAgIGNvbnN0IGVycm9yID0gb3V0Y29tZT8uZXJyb3JcbiAgICBjb25zdCByZXRyaWVzVXNlZCA9IE1hdGgubWluKGF0dGVtcHQuYXR0ZW1wdE51bWJlciwgcmV0cnlDb3VudClcbiAgICBjb25zdCB3aWxsUmV0cnkgPSBmYWlsZWQgJiYgIWV2ZW50LnRlcm1pbmFsRmFpbHVyZSAmJiAhb3V0Y29tZT8uYWJvcnRSZW1haW5pbmdUZXN0cyAmJiBhdHRlbXB0LmF0dGVtcHROdW1iZXIgPD0gcmV0cnlDb3VudFxuICAgIGNvbnN0IHtkZXNjcmlwdGlvbnMsIHRlc3REZXNjcmlwdGlvbn0gPSB0aGlzLnRlc3RSdW5uZXIudGVzdE1ldGFkYXRhKHRlc3QpXG4gICAgY29uc3QgY29tcGF0aWJpbGl0eSA9IHRoaXMudGVzdFJ1bm5lci50ZXN0RGF0YSh0ZXN0KVxuXG4gICAgaWYgKGZhaWxlZCkge1xuICAgICAgYXdhaXQgdGhpcy5lbWl0RXZlbnQoXCJ0ZXN0QXR0ZW1wdEZhaWxlZFwiLCB7XG4gICAgICAgIGNvbmZpZ3VyYXRpb246IHRoaXMudGVzdFJ1bm5lci5nZXRDb25maWd1cmF0aW9uKCksXG4gICAgICAgIGRlc2NyaXB0aW9ucyxcbiAgICAgICAgZXJyb3IsXG4gICAgICAgIGF0dGVtcHROdW1iZXI6IGF0dGVtcHQuYXR0ZW1wdE51bWJlcixcbiAgICAgICAgbmV4dEF0dGVtcHQ6IHdpbGxSZXRyeSA/IGF0dGVtcHQuYXR0ZW1wdE51bWJlciArIDEgOiB1bmRlZmluZWQsXG4gICAgICAgIHJldHJpZXNVc2VkLFxuICAgICAgICByZXRyeUNvdW50LFxuICAgICAgICB0ZXN0QXJnczogY29tcGF0aWJpbGl0eS50ZXN0QXJncyxcbiAgICAgICAgdGVzdERhdGE6IGNvbXBhdGliaWxpdHkudGVzdERhdGEsXG4gICAgICAgIHRlc3REZXNjcmlwdGlvbixcbiAgICAgICAgdGVzdFJ1bm5lcjogdGhpcy50ZXN0UnVubmVyLFxuICAgICAgICB3aWxsUmV0cnlcbiAgICAgIH0pXG4gICAgfVxuXG4gICAgaWYgKHdpbGxSZXRyeSkge1xuICAgICAgY29uc29sZS53YXJuKHBpY29jb2xvcnMucmVkKGAke1wiIFwiLnJlcGVhdChkZXNjcmlwdGlvbnMubGVuZ3RoICogMil9ICBSZXRyeWluZyAoJHtyZXRyaWVzVXNlZH0vJHtyZXRyeUNvdW50fSkgYWZ0ZXIgZXJyb3I6ICR7ZXJyb3IgaW5zdGFuY2VvZiBFcnJvciA/IGVycm9yLm1lc3NhZ2UgOiBTdHJpbmcoZXJyb3IpfWApKVxuICAgICAgYXdhaXQgdGhpcy5lbWl0RXZlbnQoXCJ0ZXN0UmV0cnlpbmdcIiwge1xuICAgICAgICBjb25maWd1cmF0aW9uOiB0aGlzLnRlc3RSdW5uZXIuZ2V0Q29uZmlndXJhdGlvbigpLFxuICAgICAgICBkZXNjcmlwdGlvbnMsXG4gICAgICAgIGVycm9yLFxuICAgICAgICBuZXh0QXR0ZW1wdDogYXR0ZW1wdC5hdHRlbXB0TnVtYmVyICsgMSxcbiAgICAgICAgcmV0cmllc1VzZWQsXG4gICAgICAgIHJldHJ5Q291bnQsXG4gICAgICAgIHRlc3RBcmdzOiBjb21wYXRpYmlsaXR5LnRlc3RBcmdzLFxuICAgICAgICB0ZXN0RGF0YTogY29tcGF0aWJpbGl0eS50ZXN0RGF0YSxcbiAgICAgICAgdGVzdERlc2NyaXB0aW9uLFxuICAgICAgICB0ZXN0UnVubmVyOiB0aGlzLnRlc3RSdW5uZXJcbiAgICAgIH0pXG4gICAgfVxuXG4gICAgaWYgKGF0dGVtcHQuYXR0ZW1wdE51bWJlciA+IDEpIHtcbiAgICAgIGF3YWl0IHRoaXMuZW1pdEV2ZW50KFwidGVzdFJldHJpZWRcIiwge1xuICAgICAgICBjb25maWd1cmF0aW9uOiB0aGlzLnRlc3RSdW5uZXIuZ2V0Q29uZmlndXJhdGlvbigpLFxuICAgICAgICBkZXNjcmlwdGlvbnMsXG4gICAgICAgIGVycm9yLFxuICAgICAgICBhdHRlbXB0TnVtYmVyOiBhdHRlbXB0LmF0dGVtcHROdW1iZXIsXG4gICAgICAgIHJldHJpZXNVc2VkLFxuICAgICAgICByZXRyeUNvdW50LFxuICAgICAgICB0ZXN0QXJnczogY29tcGF0aWJpbGl0eS50ZXN0QXJncyxcbiAgICAgICAgdGVzdERhdGE6IGNvbXBhdGliaWxpdHkudGVzdERhdGEsXG4gICAgICAgIHRlc3REZXNjcmlwdGlvbixcbiAgICAgICAgdGVzdFJ1bm5lcjogdGhpcy50ZXN0UnVubmVyXG4gICAgICB9KVxuICAgIH1cblxuICAgIGlmIChvdXRjb21lPy5hYm9ydFJlbWFpbmluZ1Rlc3RzKSB7XG4gICAgICBjb25zdCBtZXRhZGF0YSA9IHRoaXMudGVzdFJ1bm5lci50ZXN0TWV0YWRhdGEodGVzdClcblxuICAgICAgdGhpcy50ZXN0UnVubmVyLnJlY29yZFRlc3REdXJhdGlvbih7XG4gICAgICAgIGR1cmF0aW9uTXM6IGF0dGVtcHQuZHVyYXRpb25NcyxcbiAgICAgICAgZmlsZVBhdGg6IGNvbXBhdGliaWxpdHkudGVzdERhdGEuZmlsZVBhdGggPz8gXCI8dW5rbm93bj5cIixcbiAgICAgICAgZnVsbERlc2NyaXB0aW9uOiBtZXRhZGF0YS5mdWxsRGVzY3JpcHRpb24sXG4gICAgICAgIGxpbmU6IGNvbXBhdGliaWxpdHkudGVzdERhdGEubGluZSA/PyAwXG4gICAgICB9KVxuICAgICAgYXdhaXQgdGhpcy5yZXBvcnRGYWlsZWRUZXN0KHtcbiAgICAgICAgYXR0ZW1wdENvbnNvbGVPdXRwdXRzLFxuICAgICAgICBkZXNjcmlwdGlvbnMsXG4gICAgICAgIGVycm9yLFxuICAgICAgICBsZWZ0UGFkZGluZzogXCIgXCIucmVwZWF0KGRlc2NyaXB0aW9ucy5sZW5ndGggKiAyKSxcbiAgICAgICAgdGVzdEFyZ3M6IGNvbXBhdGliaWxpdHkudGVzdEFyZ3MsXG4gICAgICAgIHRlc3REYXRhOiBjb21wYXRpYmlsaXR5LnRlc3REYXRhLFxuICAgICAgICB0ZXN0RGVzY3JpcHRpb25cbiAgICAgIH0pXG4gICAgICB0aGlzLnRlc3RSdW5uZXIuY29tcGxldGVUZXN0RGVjbGFyYXRpb24odGVzdClcbiAgICAgIHRoaXMuYWN0aXZlVGVzdCA9IHVuZGVmaW5lZFxuICAgICAgdGhyb3cgbmV3IEFib3J0UmVtYWluaW5nVGVzdHNFcnJvcihcIlZlbG9jaW91cyBxdWFyYW50aW5lZCBhbiBhdHRlbXB0LW93bmVkIGRhdGFiYXNlIGNvbm5lY3Rpb25cIilcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogUHJvamVjdHMgZmluYWwgcGFja2FnZSByZXN1bHQgYWNjb3VudGluZyBhbmQgZmFpbHVyZXMuXG4gICAqIEBwYXJhbSB7aW1wb3J0KFwiQHZlbG9jaW91cy90ZXN0aW5nL3J1bm5lclwiKS5SdW5uZXJFdmVudH0gZXZlbnQgLSBUZXN0IHJlc3VsdCBldmVudC5cbiAgICogQHJldHVybnMge1Byb21pc2U8dm9pZD59IC0gUmVzb2x2ZXMgYWZ0ZXIgbGlzdGVuZXJzIGZpbmlzaC5cbiAgICovXG4gIGFzeW5jIHJlcG9ydFRlc3RFdmVudChldmVudCkge1xuICAgIC8vIE5hcnJvd3MgdGhlIHN0cnVjdHVyZWQgZXZlbnQgcGF5bG9hZCBmb3IgdGhpcyBldmVudCBkaXNjcmltaW5hdG9yLlxuICAgIGNvbnN0IHBhY2thZ2VUZXN0UmVzdWx0ID0gLyoqIEB0eXBlIHtpbXBvcnQoXCJAdmVsb2Npb3VzL3Rlc3RpbmcvcnVubmVyXCIpLlRlc3RSZXN1bHR9ICovIChldmVudC50ZXN0KVxuICAgIGNvbnN0IHRlc3QgPSB0aGlzLmFjdGl2ZVRlc3QgfHwgdGhpcy50ZXN0UnVubmVyLmZpbmRUZXN0RGVjbGFyYXRpb24ocGFja2FnZVRlc3RSZXN1bHQuZnVsbE5hbWUpXG5cbiAgICBpZiAoIXRlc3QpIHRocm93IG5ldyBFcnJvcihgUGFja2FnZSBydW5uZXIgcmVzdWx0IGRpZCBub3QgbWF0Y2ggYSBkZWNsYXJhdGlvbjogJHtwYWNrYWdlVGVzdFJlc3VsdC5mdWxsTmFtZX1gKVxuXG4gICAgY29uc3QgbWV0YWRhdGEgPSB0aGlzLnRlc3RSdW5uZXIudGVzdE1ldGFkYXRhKHRlc3QpXG4gICAgY29uc3QgY29tcGF0aWJpbGl0eSA9IHRoaXMudGVzdFJ1bm5lci50ZXN0RGF0YSh0ZXN0KVxuICAgIGNvbnN0IGR1cmF0aW9uTXMgPSBwYWNrYWdlVGVzdFJlc3VsdC5hdHRlbXB0cy5yZWR1Y2UoKHRvdGFsLCBhdHRlbXB0KSA9PiB0b3RhbCArIGF0dGVtcHQuZHVyYXRpb25NcywgMClcblxuICAgIGlmIChwYWNrYWdlVGVzdFJlc3VsdC5hdHRlbXB0cy5sZW5ndGggPiAwKSB7XG4gICAgICB0aGlzLnRlc3RSdW5uZXIucmVjb3JkVGVzdER1cmF0aW9uKHtcbiAgICAgICAgZHVyYXRpb25NcyxcbiAgICAgICAgZmlsZVBhdGg6IGNvbXBhdGliaWxpdHkudGVzdERhdGEuZmlsZVBhdGggPz8gXCI8dW5rbm93bj5cIixcbiAgICAgICAgZnVsbERlc2NyaXB0aW9uOiBtZXRhZGF0YS5mdWxsRGVzY3JpcHRpb24sXG4gICAgICAgIGxpbmU6IGNvbXBhdGliaWxpdHkudGVzdERhdGEubGluZSA/PyAwXG4gICAgICB9KVxuICAgIH1cblxuICAgIGlmIChwYWNrYWdlVGVzdFJlc3VsdC5zdGF0dXMgPT09IFwicGFzc2VkXCIpIHtcbiAgICAgIHRoaXMudGVzdFJ1bm5lci5yZWNvcmRTdWNjZXNzZnVsVGVzdCgpXG4gICAgfSBlbHNlIHtcbiAgICAgIGNvbnN0IGZpbmFsQXR0ZW1wdCA9IHBhY2thZ2VUZXN0UmVzdWx0LmF0dGVtcHRzLmF0KC0xKVxuICAgICAgY29uc3Qgb3V0Y29tZSA9IGZpbmFsQXR0ZW1wdFxuICAgICAgICA/IHRoaXMudGVzdFJ1bm5lci5hdHRlbXB0T3V0Y29tZSh0ZXN0LCBmaW5hbEF0dGVtcHQuYXR0ZW1wdE51bWJlcilcbiAgICAgICAgOiB1bmRlZmluZWRcbiAgICAgIGNvbnN0IHNldHVwRmFpbHVyZSA9IHRoaXMudGVzdFJ1bm5lci5zZXR1cEZhaWx1cmVPdXRjb21lRm9yKHRlc3QpXG4gICAgICBjb25zdCBlcnJvciA9IG91dGNvbWU/LmZhaWxlZFxuICAgICAgICA/IG91dGNvbWUuZXJyb3JcbiAgICAgICAgOiBzZXR1cEZhaWx1cmUuZmFpbGVkXG4gICAgICAgICAgPyBzZXR1cEZhaWx1cmUuZXJyb3JcbiAgICAgICAgICA6IHBhY2thZ2VUZXN0UmVzdWx0LmVycm9yXG4gICAgICAgICAgICA/IGVycm9yRnJvbVBhY2thZ2VSZWNvcmQocGFja2FnZVRlc3RSZXN1bHQuZXJyb3IpXG4gICAgICAgICAgICA6IHVuZGVmaW5lZFxuXG4gICAgICBhd2FpdCB0aGlzLnJlcG9ydEZhaWxlZFRlc3Qoe1xuICAgICAgICBhdHRlbXB0Q29uc29sZU91dHB1dHM6IHRoaXMuYXR0ZW1wdENvbnNvbGVPdXRwdXRzLmdldCh0ZXN0KSB8fCBbXSxcbiAgICAgICAgZGVzY3JpcHRpb25zOiBtZXRhZGF0YS5kZXNjcmlwdGlvbnMsXG4gICAgICAgIGVycm9yLFxuICAgICAgICBsZWZ0UGFkZGluZzogXCIgXCIucmVwZWF0KG1ldGFkYXRhLmRlc2NyaXB0aW9ucy5sZW5ndGggKiAyKSxcbiAgICAgICAgdGVzdEFyZ3M6IGNvbXBhdGliaWxpdHkudGVzdEFyZ3MsXG4gICAgICAgIHRlc3REYXRhOiBjb21wYXRpYmlsaXR5LnRlc3REYXRhLFxuICAgICAgICB0ZXN0RGVzY3JpcHRpb246IG1ldGFkYXRhLnRlc3REZXNjcmlwdGlvblxuICAgICAgfSlcbiAgICB9XG5cbiAgICB0aGlzLnRlc3RSdW5uZXIuY29tcGxldGVUZXN0RGVjbGFyYXRpb24odGVzdClcbiAgICB0aGlzLmFjdGl2ZVRlc3QgPSB1bmRlZmluZWRcbiAgfVxuXG4gIC8qKlxuICAgKiBSZWNvcmRzIGFuZCBlbWl0cyBvbmUgZmluYWwgZmFpbGVkIHRlc3QgcmVzdWx0LlxuICAgKiBAcGFyYW0ge29iamVjdH0gYXJncyAtIEZpbmFsIGZhaWx1cmUgbWV0YWRhdGEuXG4gICAqIEBwYXJhbSB7aW1wb3J0KFwiLi90ZXN0LXJ1bm5lci5qc1wiKS5BdHRlbXB0Q29uc29sZU91dHB1dFtdfSBhcmdzLmF0dGVtcHRDb25zb2xlT3V0cHV0cyAtIENhcHR1cmVkIG91dHB1dCBhY3Jvc3MgYXR0ZW1wdHMuXG4gICAqIEBwYXJhbSB7c3RyaW5nW119IGFyZ3MuZGVzY3JpcHRpb25zIC0gUGFyZW50IGRlc2NyaXB0aW9uIHN0YWNrLlxuICAgKiBAcGFyYW0ge1JldHVyblR5cGU8dHlwZW9mIEpTT04ucGFyc2U+fSBhcmdzLmVycm9yIC0gUmF3IGZpbmFsIGZhaWx1cmUuXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBhcmdzLmxlZnRQYWRkaW5nIC0gQ29uc29sZSBpbmRlbnRhdGlvbi5cbiAgICogQHBhcmFtIHtpbXBvcnQoXCIuL3Rlc3QtcnVubmVyLmpzXCIpLlRlc3RBcmdzfSBhcmdzLnRlc3RBcmdzIC0gU3RhYmxlIHRlc3QgYXJndW1lbnRzLlxuICAgKiBAcGFyYW0ge2ltcG9ydChcIi4vdGVzdC1ydW5uZXIuanNcIikuVGVzdERhdGF9IGFyZ3MudGVzdERhdGEgLSBUZXN0IHJlZ2lzdHJhdGlvbi5cbiAgICogQHBhcmFtIHtzdHJpbmd9IGFyZ3MudGVzdERlc2NyaXB0aW9uIC0gVGVzdCBkZXNjcmlwdGlvbi5cbiAgICogQHJldHVybnMge1Byb21pc2U8dm9pZD59IC0gUmVzb2x2ZXMgYWZ0ZXIgdGhlIGZpbmFsLWZhaWx1cmUgbGlzdGVuZXIgY29tcGxldGVzLlxuICAgKi9cbiAgYXN5bmMgcmVwb3J0RmFpbGVkVGVzdCh7YXR0ZW1wdENvbnNvbGVPdXRwdXRzLCBkZXNjcmlwdGlvbnMsIGVycm9yLCBsZWZ0UGFkZGluZywgdGVzdEFyZ3MsIHRlc3REYXRhLCB0ZXN0RGVzY3JpcHRpb259KSB7XG4gICAgY29uc3QgdGVzdFJ1bm5lciA9IHRoaXMudGVzdFJ1bm5lclxuICAgIGNvbnN0IGNvbnNvbGVPdXRwdXQgPSB0ZXN0UnVubmVyLmJ1aWxkQ29uc29sZU91dHB1dChhdHRlbXB0Q29uc29sZU91dHB1dHMpXG5cbiAgICBpZiAoZXJyb3IgaW5zdGFuY2VvZiBFcnJvcikge1xuICAgICAgY29uc29sZS5lcnJvcihwaWNvY29sb3JzLnJlZChgJHtsZWZ0UGFkZGluZ30gIFRlc3QgZmFpbGVkOiAke2Vycm9yLm1lc3NhZ2V9YCkpXG4gICAgICB0aGlzLnByaW50RXJyb3JDYXVzZXMoZXJyb3IpXG5cbiAgICB9IGVsc2Uge1xuICAgICAgY29uc29sZS5lcnJvcihwaWNvY29sb3JzLnJlZChgJHtsZWZ0UGFkZGluZ30gIFRlc3QgZmFpbGVkIHdpdGggYSAke3R5cGVvZiBlcnJvcn06ICR7U3RyaW5nKGVycm9yKX1gKSlcbiAgICB9XG5cbiAgICB0ZXN0UnVubmVyLnByaW50RmFpbGVkQ29uc29sZU91dHB1dCh7Y29uc29sZU91dHB1dCwgbGVmdFBhZGRpbmd9KVxuICAgIHRlc3RSdW5uZXIucmVjb3JkRmFpbGVkVGVzdCh7ZGVzY3JpcHRpb25zLCBlcnJvciwgY29uc29sZU91dHB1dCwgdGVzdERhdGEsIHRlc3REZXNjcmlwdGlvbn0pXG5cbiAgICBhd2FpdCB0aGlzLmVtaXRFdmVudChcInRlc3RGYWlsZWRcIiwge1xuICAgICAgY29uZmlndXJhdGlvbjogdGVzdFJ1bm5lci5nZXRDb25maWd1cmF0aW9uKCksXG4gICAgICBkZXNjcmlwdGlvbnMsXG4gICAgICBlcnJvcixcbiAgICAgIHRlc3RBcmdzLFxuICAgICAgdGVzdERhdGEsXG4gICAgICB0ZXN0RGVzY3JpcHRpb24sXG4gICAgICB0ZXN0UnVubmVyXG4gICAgfSlcblxuICAgIHRlc3RSdW5uZXIucHJpbnRSZXJ1bkNvbW1hbmQoe2Rlc2NyaXB0aW9ucywgdGVzdERlc2NyaXB0aW9uLCB0ZXN0RGF0YSwgbGVmdFBhZGRpbmd9KVxuICB9XG5cbiAgLyoqXG4gICAqIFByaW50cyBjb21wbGV0ZSBwcmltYXJ5IGFuZCBzZWNvbmRhcnkgc3RhY2tzIG9uY2UsIGluY2x1ZGluZyBjeWNsaWMgY2F1c2UgZ3JhcGhzLlxuICAgKiBAcGFyYW0ge3Vua25vd259IGVycm9yIC0gVGhyb3duIHZhbHVlIGF0IHRoZSByZXBvcnRpbmcgYm91bmRhcnkuXG4gICAqIEBwYXJhbSB7U2V0PEVycm9yPn0gW3JlcG9ydGVkXSAtIEVycm9yIGlkZW50aXRpZXMgYWxyZWFkeSBwcmludGVkLlxuICAgKiBAcmV0dXJucyB7dm9pZH1cbiAgICovXG4gIHByaW50RXJyb3JDYXVzZXMoZXJyb3IsIHJlcG9ydGVkID0gbmV3IFNldCgpKSB7XG4gICAgaWYgKCEoZXJyb3IgaW5zdGFuY2VvZiBFcnJvcikpIHtcbiAgICAgIGNvbnNvbGUuZXJyb3IoU3RyaW5nKGVycm9yKSlcbiAgICAgIHJldHVyblxuICAgIH1cbiAgICBpZiAocmVwb3J0ZWQuaGFzKGVycm9yKSkgcmV0dXJuXG4gICAgcmVwb3J0ZWQuYWRkKGVycm9yKVxuICAgIGFkZFRyYWNrZWRTdGFja1RvRXJyb3IoZXJyb3IpXG4gICAgY29uc29sZS5lcnJvcihlcnJvci5zdGFjayB8fCBgJHtlcnJvci5uYW1lfTogJHtlcnJvci5tZXNzYWdlfWApXG4gICAgaWYgKGVycm9yLmNhdXNlICE9PSB1bmRlZmluZWQpIHtcbiAgICAgIGNvbnNvbGUuZXJyb3IoXCJDYXVzZWQgYnk6XCIpXG4gICAgICB0aGlzLnByaW50RXJyb3JDYXVzZXMoZXJyb3IuY2F1c2UsIHJlcG9ydGVkKVxuICAgIH1cbiAgICBpZiAoZXJyb3IgaW5zdGFuY2VvZiBBZ2dyZWdhdGVFcnJvcikge1xuICAgICAgZm9yIChjb25zdCBzZWNvbmRhcnkgb2YgZXJyb3IuZXJyb3JzKSB7XG4gICAgICAgIGNvbnNvbGUuZXJyb3IoXCJSZWxhdGVkIGZhaWx1cmU6XCIpXG4gICAgICAgIHRoaXMucHJpbnRFcnJvckNhdXNlcyhzZWNvbmRhcnksIHJlcG9ydGVkKVxuICAgICAgfVxuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBFbWl0cyBvbmUgbGVnYWN5IGV2ZW50IGFuZCBhd2FpdHMgbGlzdGVuZXJzIGluIHJlZ2lzdHJhdGlvbiBvcmRlci5cbiAgICogQHBhcmFtIHtzdHJpbmd9IGV2ZW50TmFtZSAtIEV2ZW50IG5hbWUuXG4gICAqIEBwYXJhbSB7b2JqZWN0fSBwYXlsb2FkIC0gRXZlbnQgcGF5bG9hZC5cbiAgICogQHJldHVybnMge1Byb21pc2U8dm9pZD59IC0gUmVzb2x2ZXMgd2hlbiBhbGwgbGlzdGVuZXJzIGNvbXBsZXRlLlxuICAgKi9cbiAgYXN5bmMgZW1pdEV2ZW50KGV2ZW50TmFtZSwgcGF5bG9hZCkge1xuICAgIGZvciAoY29uc3QgbGlzdGVuZXIgb2YgdGVzdEV2ZW50cy5saXN0ZW5lcnMoZXZlbnROYW1lKSkgYXdhaXQgbGlzdGVuZXIocGF5bG9hZClcbiAgfVxufVxuIl19
@@ -142,6 +142,8 @@ import VelociousTestArguments from "./velocious-test-arguments.js"
142
142
  * @property {import("../database/pool/base.js").TestSharedConnectionRegistration | undefined} sharedRegistration - Physical-key shared registration once published.
143
143
  */
144
144
 
145
+ const testingPackageDirectory = path.dirname(fileURLToPath(import.meta.resolve("@velocious/testing/package.json")))
146
+
145
147
  /**
146
148
  * Runs to file slug.
147
149
  * @param {string} value - Value to sanitize.
@@ -197,6 +199,8 @@ export default class TestRunner {
197
199
  this._successfulTests = 0
198
200
  this._testsCount = 0
199
201
  this._failedTestDetails = []
202
+ /** @type {import("@velocious/testing/runner").NonRunTestResult[]} */
203
+ this._notRunTestDetails = []
200
204
  /** @type {{fullDescription: string, filePath: string, line: number} | null} */
201
205
  this._lastTestContext = null
202
206
  /** @type {Array<{fullDescription: string, filePath: string, line: number, durationMs: number}>} */
@@ -1007,6 +1011,25 @@ export default class TestRunner {
1007
1011
  return this._failedTests
1008
1012
  }
1009
1013
 
1014
+ /**
1015
+ * Counts selected tests blocked by a terminal resource.
1016
+ * @returns {number} - Selected tests not executed because a shared resource failed.
1017
+ */
1018
+ getNotRunTests() { return this._notRunTestDetails.length }
1019
+
1020
+ /**
1021
+ * Returns runtime non-run attribution.
1022
+ * @returns {import("@velocious/testing/runner").NonRunTestResult[]} - Runtime non-run details with the originating failure.
1023
+ */
1024
+ getNotRunTestDetails() { return this._notRunTestDetails }
1025
+
1026
+ /**
1027
+ * Records a selected test that did not execute.
1028
+ * @param {import("@velocious/testing/runner").NonRunTestResult} result - Terminal-resource non-run record.
1029
+ * @returns {void}
1030
+ */
1031
+ recordNotRunTest(result) { this._notRunTestDetails.push(result) }
1032
+
1010
1033
  /**
1011
1034
  * Runs get failed test details.
1012
1035
  * @returns {FailedTestDetail[]} - Failed test details.
@@ -1087,6 +1110,14 @@ export default class TestRunner {
1087
1110
  return this._packageResult?.tests.length ?? this._testDurations.length
1088
1111
  }
1089
1112
 
1113
+ /**
1114
+ * Distinguishes an empty selection from a failure before selected cases execute.
1115
+ * @returns {boolean} - Whether selection matched no declarations.
1116
+ */
1117
+ hasNoMatches() {
1118
+ return this._packageResult?.noMatches === true
1119
+ }
1120
+
1090
1121
  /**
1091
1122
  * Returns the tests recorded during the run, slowest first.
1092
1123
  * @param {number} [limit] - Maximum number of tests to return (0 returns all).
@@ -1105,6 +1136,7 @@ export default class TestRunner {
1105
1136
  async prepare() {
1106
1137
  this.anyTestsFocussed = false
1107
1138
  this._failedTests = 0
1139
+ this._notRunTestDetails = []
1108
1140
  this._successfulTests = 0
1109
1141
  this._testsCount = 0
1110
1142
  this._abortRemainingTests = false
@@ -1177,7 +1209,7 @@ export default class TestRunner {
1177
1209
 
1178
1210
  if (portablePath.endsWith("/src/testing/test-runner.js")) continue
1179
1211
  if (portablePath.endsWith("/src/testing/test.js")) continue
1180
- if (portablePath.includes("/node_modules/@velocious/testing/")) continue
1212
+ if (resolvedFilePath.startsWith(`${testingPackageDirectory}${path.sep}`)) continue
1181
1213
 
1182
1214
  return {filePath: resolvedFilePath, line: Number(match[2])}
1183
1215
  }
@@ -1,7 +1,6 @@
1
1
  // @ts-check
2
2
 
3
3
  import { addTrackedStackToError } from "../utils/with-tracked-stack.js"
4
- import BacktraceCleaner from "../utils/backtrace-cleaner-node.js"
5
4
  import picocolors from "picocolors"
6
5
  import restArgsError from "../utils/rest-args-error.js"
7
6
  import { testEvents } from "./test.js"
@@ -21,6 +20,8 @@ function errorFromPackageRecord(errorRecord) {
21
20
 
22
21
  error.name = errorRecord.name
23
22
  if (errorRecord.stack) error.stack = errorRecord.stack
23
+ if (errorRecord.cause) error.cause = errorFromPackageRecord(errorRecord.cause)
24
+ if (errorRecord.terminalResource) Object.assign(error, {terminalResource: errorRecord.terminalResource})
24
25
 
25
26
  return error
26
27
  }
@@ -68,7 +69,27 @@ export default class VelociousRunnerReporter {
68
69
  return
69
70
  }
70
71
 
71
- if (event.type === "run:finish") this.testRunner.recordPackageResult(event.result)
72
+ if (event.type === "test:not-run") {
73
+ const test = this.testRunner.findTestDeclaration(event.test.fullName)
74
+ if (!test) throw new Error(`Package not-run result did not match a declaration: ${event.test.fullName}`)
75
+ this.testRunner.recordNotRunTest(event.test)
76
+ this.testRunner.completeTestDeclaration(test)
77
+ console.error(`Not run: ${event.test.fullName} (terminal resource failure in ${event.test.reason.fullName})`)
78
+ await this.emitEvent("testNotRun", {
79
+ configuration: this.testRunner.getConfiguration(),
80
+ test: event.test,
81
+ testRunner: this.testRunner
82
+ })
83
+ return
84
+ }
85
+
86
+ if (event.type === "run:finish") {
87
+ this.testRunner.recordPackageResult(event.result)
88
+ for (const failure of event.result.errors) {
89
+ console.error(`Suite ${failure.phase} failed: ${failure.suite}`)
90
+ this.printErrorCauses(errorFromPackageRecord(failure.error))
91
+ }
92
+ }
72
93
  }
73
94
 
74
95
  /**
@@ -95,7 +116,7 @@ export default class VelociousRunnerReporter {
95
116
  const failed = outcome?.failed ?? Boolean(attempt.error)
96
117
  const error = outcome?.error
97
118
  const retriesUsed = Math.min(attempt.attemptNumber, retryCount)
98
- const willRetry = failed && !outcome?.abortRemainingTests && attempt.attemptNumber <= retryCount
119
+ const willRetry = failed && !event.terminalFailure && !outcome?.abortRemainingTests && attempt.attemptNumber <= retryCount
99
120
  const {descriptions, testDescription} = this.testRunner.testMetadata(test)
100
121
  const compatibility = this.testRunner.testData(test)
101
122
 
@@ -245,15 +266,8 @@ export default class VelociousRunnerReporter {
245
266
 
246
267
  if (error instanceof Error) {
247
268
  console.error(picocolors.red(`${leftPadding} Test failed: ${error.message}`))
248
- addTrackedStackToError(error)
249
-
250
- const backtraceCleaner = new BacktraceCleaner(error)
251
- const cleanedStack = backtraceCleaner.getCleanedStack()
252
- const stackLines = cleanedStack?.split("\n")
269
+ this.printErrorCauses(error)
253
270
 
254
- if (stackLines) {
255
- for (const stackLine of stackLines) console.error(picocolors.red(`${leftPadding} ${stackLine}`))
256
- }
257
271
  } else {
258
272
  console.error(picocolors.red(`${leftPadding} Test failed with a ${typeof error}: ${String(error)}`))
259
273
  }
@@ -274,6 +288,33 @@ export default class VelociousRunnerReporter {
274
288
  testRunner.printRerunCommand({descriptions, testDescription, testData, leftPadding})
275
289
  }
276
290
 
291
+ /**
292
+ * Prints complete primary and secondary stacks once, including cyclic cause graphs.
293
+ * @param {unknown} error - Thrown value at the reporting boundary.
294
+ * @param {Set<Error>} [reported] - Error identities already printed.
295
+ * @returns {void}
296
+ */
297
+ printErrorCauses(error, reported = new Set()) {
298
+ if (!(error instanceof Error)) {
299
+ console.error(String(error))
300
+ return
301
+ }
302
+ if (reported.has(error)) return
303
+ reported.add(error)
304
+ addTrackedStackToError(error)
305
+ console.error(error.stack || `${error.name}: ${error.message}`)
306
+ if (error.cause !== undefined) {
307
+ console.error("Caused by:")
308
+ this.printErrorCauses(error.cause, reported)
309
+ }
310
+ if (error instanceof AggregateError) {
311
+ for (const secondary of error.errors) {
312
+ console.error("Related failure:")
313
+ this.printErrorCauses(secondary, reported)
314
+ }
315
+ }
316
+ }
317
+
277
318
  /**
278
319
  * Emits one legacy event and awaits listeners in registration order.
279
320
  * @param {string} eventName - Event name.
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "velocious": "build/bin/velocious.js"
4
4
  },
5
5
  "name": "velocious",
6
- "version": "1.0.652",
6
+ "version": "1.0.654",
7
7
  "main": "build/index.js",
8
8
  "types": "build/index.d.ts",
9
9
  "files": [
@@ -91,7 +91,7 @@
91
91
  "ws": "^8.21.3"
92
92
  },
93
93
  "peerDependencies": {
94
- "@velocious/testing": "0.0.9",
94
+ "@velocious/testing": "^0.0.12",
95
95
  "react": ">=18",
96
96
  "smtp-connection": "^4.0.2",
97
97
  "sqlite": "^5.1.1",
@@ -121,7 +121,7 @@
121
121
  "@types/react-dom": "^19.2.3",
122
122
  "@types/strftime": "^0.9.8",
123
123
  "@types/ws": "^8.18.1",
124
- "@velocious/testing": "0.0.9",
124
+ "@velocious/testing": "0.0.12",
125
125
  "chokidar-cli": "^3.0.0",
126
126
  "cpy-cli": "^7.0.0",
127
127
  "esbuild": "^0.28.0",
@@ -142,7 +142,7 @@
142
142
  "selenium-webdriver": "^4.39.0",
143
143
  "sqlite": "^5.1.1",
144
144
  "sqlite3": "^6.0.1",
145
- "system-testing": "^1.0.108",
145
+ "system-testing": "^1.0.137",
146
146
  "tedious": "^20.0.0",
147
147
  "typescript": "^7.0.2",
148
148
  "uniqunize": "^1.0.3"
@@ -7,6 +7,7 @@ import { createRequire } from "node:module"
7
7
  import path from "node:path"
8
8
  import { promisify } from "node:util"
9
9
  import timeout from "awaitery/build/timeout.js"
10
+ import { resolveChromeRuntime } from "system-testing/build/chrome-runtime-manager.js"
10
11
  import SystemTest from "system-testing/build/system-test.js"
11
12
 
12
13
  /**
@@ -33,13 +34,12 @@ import SystemTest from "system-testing/build/system-test.js"
33
34
  /**
34
35
  * @typedef {object} BrowserTestSystemTestConfig
35
36
  * @property {boolean} debug - Whether SystemTest should emit debug output.
36
- * @property {{type: "selenium", options: {chromeArguments: string[], chromeBinaryPath: string}}} driver - Selenium driver config.
37
+ * @property {{type: "selenium", options: {chromeArguments: string[], chromeBinaryPath: string, chromedriverPath: string}}} driver - Selenium driver config.
37
38
  * @property {string} httpHost - HTTP host for the browser-test app.
38
39
  * @property {number} httpPort - HTTP port for the browser-test app.
39
40
  */
40
41
 
41
42
  const require = createRequire(import.meta.url)
42
- const { binaryPaths } = require("selenium-webdriver/common/seleniumManager.js")
43
43
  const { CancellationError, waitForServer } = require("selenium-webdriver/http/util.js")
44
44
  const { findFreePort } = require("selenium-webdriver/net/portprober.js")
45
45
  const execFileAsync = promisify(execFile)
@@ -97,30 +97,24 @@ async function assertExecutable(executablePath, description) {
97
97
  }
98
98
 
99
99
  /**
100
- * Resolves, validates, and persists the exact Chrome/ChromeDriver pair.
100
+ * Resolves, validates, and persists the exact Chrome-for-Testing runtime pair.
101
101
  * @param {object} [args] - Prewarm options.
102
- * @param {() => {browserPath?: string, driverPath?: string}} [args.binaryPathsResolver] - Selenium path resolver.
103
102
  * @param {string} [args.manifestPath] - Runtime manifest path.
103
+ * @param {() => Promise<{chromeBinaryPath: string, chromedriverPath: string} | undefined>} [args.runtimeResolver] - System-testing runtime resolver.
104
104
  * @param {(executablePath: string) => Promise<string>} [args.versionReader] - Executable version reader.
105
105
  * @returns {Promise<BrowserTestChromeRuntime>} - Prewarmed runtime.
106
106
  */
107
107
  export async function prewarmBrowserTestChromeRuntime({
108
- binaryPathsResolver = () => binaryPaths([
109
- "--browser",
110
- "chrome",
111
- "--language-binding",
112
- "javascript",
113
- "--output",
114
- "json",
115
- "--avoid-browser-download"
116
- ]),
117
108
  manifestPath = defaultManifestPath(),
109
+ runtimeResolver = resolveChromeRuntime,
118
110
  versionReader = readExecutableVersion
119
111
  } = {}) {
120
- const { browserPath, driverPath } = binaryPathsResolver()
112
+ const chromeRuntime = await runtimeResolver()
113
+ const browserPath = chromeRuntime?.chromeBinaryPath
114
+ const driverPath = chromeRuntime?.chromedriverPath
121
115
 
122
- if (!browserPath) throw new Error("Selenium Manager did not resolve a Chrome executable")
123
- if (!driverPath) throw new Error("Selenium Manager did not resolve a ChromeDriver executable")
116
+ if (!browserPath) throw new Error("System-testing did not resolve a Chrome executable")
117
+ if (!driverPath) throw new Error("System-testing did not resolve a ChromeDriver executable")
124
118
 
125
119
  await assertExecutable(browserPath, "Chrome")
126
120
  await assertExecutable(driverPath, "ChromeDriver")
@@ -143,7 +137,7 @@ export async function prewarmBrowserTestChromeRuntime({
143
137
  const browserVersion = parseExecutableVersion(browserVersionOutput, "Chrome")
144
138
  const driverVersion = parseExecutableVersion(driverVersionOutput, "ChromeDriver")
145
139
 
146
- if (browserVersion.split(".")[0] !== driverVersion.split(".")[0]) {
140
+ if (browserVersion !== driverVersion) {
147
141
  throw new Error(`Chrome ${browserVersion} is incompatible with ChromeDriver ${driverVersion}`)
148
142
  }
149
143
 
@@ -218,14 +212,14 @@ function browserTestChromeArguments() {
218
212
 
219
213
  /**
220
214
  * Builds the SystemTest factory used by BrowserTestSession so the Selenium builder path
221
- * receives the pinned Chrome binary and the required headless container Chrome arguments.
215
+ * receives the pinned Chrome runtime and the required headless container Chrome arguments.
222
216
  * @param {object} [args] - Factory options.
223
217
  * @param {string[]} [args.chromeArguments] - Chrome launch arguments.
224
218
  * @param {boolean} [args.debug] - Whether SystemTest should emit debug output.
225
219
  * @param {string} [args.httpHost] - HTTP host for the browser-test app.
226
220
  * @param {number} [args.httpPort] - HTTP port for the browser-test app.
227
221
  * @param {(config: BrowserTestSystemTestConfig) => BrowserTestSystemTest} [args.systemTestCurrent] - SystemTest factory.
228
- * @returns {(args: {browserPath: string, remoteUrl: string}) => BrowserTestSystemTest} - SystemTest factory.
222
+ * @returns {(args: {browserPath: string, driverPath: string, remoteUrl: string}) => BrowserTestSystemTest} - SystemTest factory.
229
223
  */
230
224
  export function buildBrowserTestSystemTestFactory({
231
225
  chromeArguments = browserTestChromeArguments(),
@@ -234,11 +228,11 @@ export function buildBrowserTestSystemTestFactory({
234
228
  httpPort = 1984,
235
229
  systemTestCurrent = SystemTest.current
236
230
  } = {}) {
237
- return ({browserPath}) => systemTestCurrent({
231
+ return ({browserPath, driverPath}) => systemTestCurrent({
238
232
  debug,
239
233
  driver: {
240
234
  type: "selenium",
241
- options: {chromeArguments, chromeBinaryPath: browserPath}
235
+ options: {chromeArguments, chromeBinaryPath: browserPath, chromedriverPath: driverPath}
242
236
  },
243
237
  httpHost,
244
238
  httpPort
@@ -428,7 +422,7 @@ export class BrowserTestSession {
428
422
  * @param {(runtime: BrowserTestChromeRuntime) => Promise<BrowserTestChromeDriverService>} [args.chromeDriverServiceFactory] - Driver service factory.
429
423
  * @param {(phase: string) => Promise<string>} [args.processSnapshot] - Process snapshot callback.
430
424
  * @param {BrowserTestChromeRuntime} args.runtime - Chrome runtime.
431
- * @param {(args: {browserPath: string, remoteUrl: string}) => BrowserTestSystemTest} args.systemTestFactory - SystemTest factory.
425
+ * @param {(args: {browserPath: string, driverPath: string, remoteUrl: string}) => BrowserTestSystemTest} args.systemTestFactory - SystemTest factory.
432
426
  */
433
427
  constructor({
434
428
  chromeDriverServiceFactory = async (runtime) => new ManagedChromeDriverProcess(runtime),
@@ -463,7 +457,11 @@ export class BrowserTestSession {
463
457
  this.startupPhase = "starting ChromeDriver service"
464
458
  this.remoteUrl = await this.chromeDriverService.start()
465
459
  process.env.SELENIUM_REMOTE_URL = this.remoteUrl
466
- this.systemTest = this.systemTestFactory({browserPath: this.runtime.browserPath, remoteUrl: this.remoteUrl})
460
+ this.systemTest = this.systemTestFactory({
461
+ browserPath: this.runtime.browserPath,
462
+ driverPath: this.runtime.driverPath,
463
+ remoteUrl: this.remoteUrl
464
+ })
467
465
  this.startupPhase = "creating Selenium WebDriver session"
468
466
  await this.systemTest.start()
469
467
 
@@ -1468,7 +1468,9 @@ function splitWhereHash({hash, modelClass}) {
1468
1468
  const isNested = isPlainObject(value)
1469
1469
  const relationship = getRelationshipByName(modelClass, key)
1470
1470
 
1471
- if (isNested) {
1471
+ if (isNested && !relationship && resolveColumnName(modelClass, key)) {
1472
+ resolvedHash[key] = value
1473
+ } else if (isNested) {
1472
1474
  if (relationship) {
1473
1475
  const rawTargetModelClass = relationship.getTargetModelClass()
1474
1476
  if (!rawTargetModelClass) {
@@ -1,6 +1,7 @@
1
1
  // @ts-check
2
2
 
3
3
  import WhereBase from "./where-base.js"
4
+ import WhereIn from "./where-in.js"
4
5
 
5
6
  /**
6
7
  * VelociousDatabaseQueryWhereHash class.
@@ -50,7 +51,17 @@ export default class VelociousDatabaseQueryWhereHash extends WhereBase {
50
51
  if (index > 0) sql += " AND "
51
52
  sql += "1=0"
52
53
  } else if (!Array.isArray(whereValue) && whereValue !== null && typeof whereValue == "object") {
53
- sql += this._whereSQLFromHash(whereValue, whereKey, index)
54
+ if (tableName && "in" in whereValue) {
55
+ if (index > 0) sql += " AND "
56
+
57
+ sql += WhereIn.toSql({
58
+ columnSql: `${options.quoteTableName(tableName)}.${options.quoteColumnName(whereKey)}`,
59
+ options,
60
+ values: WhereIn.values(whereValue)
61
+ })
62
+ } else {
63
+ sql += this._whereSQLFromHash(whereValue, whereKey, index)
64
+ }
54
65
  } else {
55
66
  if (index > 0) sql += " AND "
56
67