taskRunner.js 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", {
  3. value: true
  4. });
  5. exports.TaskRunner = void 0;
  6. var _utilsBundle = require("playwright-core/lib/utilsBundle");
  7. var _utils = require("playwright-core/lib/utils");
  8. var _sigIntWatcher = require("./sigIntWatcher");
  9. var _util = require("../util");
  10. /**
  11. * Copyright (c) Microsoft Corporation.
  12. *
  13. * Licensed under the Apache License, Version 2.0 (the "License");
  14. * you may not use this file except in compliance with the License.
  15. * You may obtain a copy of the License at
  16. *
  17. * http://www.apache.org/licenses/LICENSE-2.0
  18. *
  19. * Unless required by applicable law or agreed to in writing, software
  20. * distributed under the License is distributed on an "AS IS" BASIS,
  21. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  22. * See the License for the specific language governing permissions and
  23. * limitations under the License.
  24. */
  25. class TaskRunner {
  26. constructor(reporter, globalTimeoutForError) {
  27. this._tasks = [];
  28. this._reporter = void 0;
  29. this._hasErrors = false;
  30. this._interrupted = false;
  31. this._isTearDown = false;
  32. this._globalTimeoutForError = void 0;
  33. this._reporter = reporter;
  34. this._globalTimeoutForError = globalTimeoutForError;
  35. }
  36. addTask(name, task) {
  37. this._tasks.push({
  38. name,
  39. task
  40. });
  41. }
  42. async run(context, deadline, cancelPromise) {
  43. const {
  44. status,
  45. cleanup
  46. } = await this.runDeferCleanup(context, deadline, cancelPromise);
  47. const teardownStatus = await cleanup();
  48. return status === 'passed' ? teardownStatus : status;
  49. }
  50. async runDeferCleanup(context, deadline, cancelPromise = new _utils.ManualPromise()) {
  51. const sigintWatcher = new _sigIntWatcher.SigIntWatcher();
  52. const timeoutWatcher = new TimeoutWatcher(deadline);
  53. const teardownRunner = new TaskRunner(this._reporter, this._globalTimeoutForError);
  54. teardownRunner._isTearDown = true;
  55. let currentTaskName;
  56. const taskLoop = async () => {
  57. for (const {
  58. name,
  59. task
  60. } of this._tasks) {
  61. currentTaskName = name;
  62. if (this._interrupted) break;
  63. (0, _utilsBundle.debug)('pw:test:task')(`"${name}" started`);
  64. const errors = [];
  65. const softErrors = [];
  66. try {
  67. var _task$setup;
  68. teardownRunner._tasks.unshift({
  69. name: `teardown for ${name}`,
  70. task: {
  71. setup: task.teardown
  72. }
  73. });
  74. await ((_task$setup = task.setup) === null || _task$setup === void 0 ? void 0 : _task$setup.call(task, context, errors, softErrors));
  75. } catch (e) {
  76. (0, _utilsBundle.debug)('pw:test:task')(`error in "${name}": `, e);
  77. errors.push((0, _util.serializeError)(e));
  78. } finally {
  79. for (const error of [...softErrors, ...errors]) {
  80. var _this$_reporter$onErr, _this$_reporter;
  81. (_this$_reporter$onErr = (_this$_reporter = this._reporter).onError) === null || _this$_reporter$onErr === void 0 ? void 0 : _this$_reporter$onErr.call(_this$_reporter, error);
  82. }
  83. if (errors.length) {
  84. if (!this._isTearDown) this._interrupted = true;
  85. this._hasErrors = true;
  86. }
  87. }
  88. (0, _utilsBundle.debug)('pw:test:task')(`"${name}" finished`);
  89. }
  90. };
  91. await Promise.race([taskLoop(), cancelPromise, sigintWatcher.promise(), timeoutWatcher.promise]);
  92. sigintWatcher.disarm();
  93. timeoutWatcher.disarm();
  94. // Prevent subsequent tasks from running.
  95. this._interrupted = true;
  96. let status = 'passed';
  97. if (sigintWatcher.hadSignal() || cancelPromise !== null && cancelPromise !== void 0 && cancelPromise.isDone()) {
  98. status = 'interrupted';
  99. } else if (timeoutWatcher.timedOut()) {
  100. var _this$_reporter$onErr2, _this$_reporter2;
  101. (_this$_reporter$onErr2 = (_this$_reporter2 = this._reporter).onError) === null || _this$_reporter$onErr2 === void 0 ? void 0 : _this$_reporter$onErr2.call(_this$_reporter2, {
  102. message: `Timed out waiting ${this._globalTimeoutForError / 1000}s for the ${currentTaskName} to run`
  103. });
  104. status = 'timedout';
  105. } else if (this._hasErrors) {
  106. status = 'failed';
  107. }
  108. cancelPromise === null || cancelPromise === void 0 ? void 0 : cancelPromise.resolve();
  109. // Note that upon hitting deadline, we "run cleanup", but it exits immediately
  110. // because of the same deadline. Essentially, we're not performing any cleanup.
  111. const cleanup = () => teardownRunner.runDeferCleanup(context, deadline).then(r => r.status);
  112. return {
  113. status,
  114. cleanup
  115. };
  116. }
  117. }
  118. exports.TaskRunner = TaskRunner;
  119. class TimeoutWatcher {
  120. constructor(deadline) {
  121. this._timedOut = false;
  122. this.promise = new _utils.ManualPromise();
  123. this._timer = void 0;
  124. if (!deadline) return;
  125. if (deadline - (0, _utils.monotonicTime)() <= 0) {
  126. this._timedOut = true;
  127. this.promise.resolve();
  128. return;
  129. }
  130. this._timer = setTimeout(() => {
  131. this._timedOut = true;
  132. this.promise.resolve();
  133. }, deadline - (0, _utils.monotonicTime)());
  134. }
  135. timedOut() {
  136. return this._timedOut;
  137. }
  138. disarm() {
  139. clearTimeout(this._timer);
  140. }
  141. }