@fluidframework/test-utils 2.0.0-internal.3.0.1 → 2.0.0-internal.3.1.0
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.
- package/.eslintrc.js +8 -10
- package/README.md +41 -11
- package/api-extractor.json +2 -2
- package/dist/DriverWrappers.d.ts.map +1 -1
- package/dist/DriverWrappers.js.map +1 -1
- package/dist/TestConfigs.d.ts.map +1 -1
- package/dist/TestConfigs.js +3 -2
- package/dist/TestConfigs.js.map +1 -1
- package/dist/TestSummaryUtils.d.ts +1 -1
- package/dist/TestSummaryUtils.d.ts.map +1 -1
- package/dist/TestSummaryUtils.js +8 -6
- package/dist/TestSummaryUtils.js.map +1 -1
- package/dist/containerUtils.d.ts.map +1 -1
- package/dist/containerUtils.js +3 -1
- package/dist/containerUtils.js.map +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/interfaces.d.ts.map +1 -1
- package/dist/interfaces.js.map +1 -1
- package/dist/loaderContainerTracker.d.ts.map +1 -1
- package/dist/loaderContainerTracker.js +37 -27
- package/dist/loaderContainerTracker.js.map +1 -1
- package/dist/localCodeLoader.d.ts.map +1 -1
- package/dist/localCodeLoader.js.map +1 -1
- package/dist/localLoader.d.ts.map +1 -1
- package/dist/localLoader.js.map +1 -1
- package/dist/packageVersion.d.ts +1 -1
- package/dist/packageVersion.js +1 -1
- package/dist/packageVersion.js.map +1 -1
- package/dist/retry.d.ts.map +1 -1
- package/dist/retry.js.map +1 -1
- package/dist/testContainerRuntimeFactory.d.ts.map +1 -1
- package/dist/testContainerRuntimeFactory.js +2 -1
- package/dist/testContainerRuntimeFactory.js.map +1 -1
- package/dist/testFluidObject.d.ts.map +1 -1
- package/dist/testFluidObject.js +7 -3
- package/dist/testFluidObject.js.map +1 -1
- package/dist/testObjectProvider.d.ts +4 -1
- package/dist/testObjectProvider.d.ts.map +1 -1
- package/dist/testObjectProvider.js +28 -11
- package/dist/testObjectProvider.js.map +1 -1
- package/dist/timeoutUtils.d.ts.map +1 -1
- package/dist/timeoutUtils.js +4 -3
- package/dist/timeoutUtils.js.map +1 -1
- package/package.json +121 -119
- package/prettier.config.cjs +1 -1
- package/src/DriverWrappers.ts +40 -37
- package/src/TestConfigs.ts +9 -7
- package/src/TestSummaryUtils.ts +120 -115
- package/src/containerUtils.ts +18 -16
- package/src/index.ts +27 -23
- package/src/interfaces.ts +10 -7
- package/src/loaderContainerTracker.ts +627 -565
- package/src/localCodeLoader.ts +85 -77
- package/src/localLoader.ts +24 -24
- package/src/packageVersion.ts +1 -1
- package/src/retry.ts +31 -25
- package/src/testContainerRuntimeFactory.ts +59 -56
- package/src/testFluidObject.ts +168 -152
- package/src/testObjectProvider.ts +445 -384
- package/src/timeoutUtils.ts +174 -154
- package/tsconfig.json +9 -16
package/src/timeoutUtils.ts
CHANGED
|
@@ -16,185 +16,205 @@ export const defaultTimeoutDurationMs = 250;
|
|
|
16
16
|
const timeBuffer = 15; // leave 15 ms leeway for finish processing
|
|
17
17
|
|
|
18
18
|
class TestTimeout {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
19
|
+
private timeout: number = 0;
|
|
20
|
+
private timer: NodeJS.Timeout | undefined;
|
|
21
|
+
private readonly deferred: Deferred<void>;
|
|
22
|
+
private rejected = false;
|
|
23
|
+
|
|
24
|
+
private static instance: TestTimeout = new TestTimeout();
|
|
25
|
+
public static reset(runnable: Mocha.Runnable) {
|
|
26
|
+
TestTimeout.clear();
|
|
27
|
+
TestTimeout.instance.resetTimer(runnable);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
public static clear() {
|
|
31
|
+
if (TestTimeout.instance.rejected) {
|
|
32
|
+
TestTimeout.instance = new TestTimeout();
|
|
33
|
+
} else {
|
|
34
|
+
TestTimeout.instance.clearTimer();
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
public static getInstance() {
|
|
39
|
+
return TestTimeout.instance;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
public async getPromise() {
|
|
43
|
+
return this.deferred.promise;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
public getTimeout() {
|
|
47
|
+
return this.timeout;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
private constructor() {
|
|
51
|
+
this.deferred = new Deferred();
|
|
52
|
+
// Ignore rejection for timeout promise if no one is waiting for it.
|
|
53
|
+
this.deferred.promise.catch(() => {});
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
private resetTimer(runnable: Mocha.Runnable) {
|
|
57
|
+
assert(!this.timer, "clearTimer should have been called before reset");
|
|
58
|
+
assert(!this.deferred.isCompleted, "can't reset a completed TestTimeout");
|
|
59
|
+
|
|
60
|
+
// Check the test timeout setting
|
|
61
|
+
const timeout = runnable.timeout();
|
|
62
|
+
if (!(Number.isFinite(timeout) && timeout > 0)) {
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// subtract a buffer
|
|
67
|
+
this.timeout = Math.max(timeout - timeBuffer, 1);
|
|
68
|
+
|
|
69
|
+
// Set up timer to reject near the test timeout.
|
|
70
|
+
this.timer = setTimeout(() => {
|
|
71
|
+
this.deferred.reject(this);
|
|
72
|
+
this.rejected = true;
|
|
73
|
+
}, this.timeout);
|
|
74
|
+
}
|
|
75
|
+
private clearTimer() {
|
|
76
|
+
if (this.timer) {
|
|
77
|
+
clearTimeout(this.timer);
|
|
78
|
+
this.timer = undefined;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
79
81
|
}
|
|
80
82
|
|
|
81
83
|
// only register if we are running with mocha-test-setup loaded
|
|
82
84
|
if (globalThis.getMochaModule !== undefined) {
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
85
|
+
// patching resetTimeout and clearTimeout on the runnable object
|
|
86
|
+
// so we can track when test timeout are enforced
|
|
87
|
+
const mochaModule = globalThis.getMochaModule() as typeof Mocha;
|
|
88
|
+
const runnablePrototype = mochaModule.Runnable.prototype;
|
|
89
|
+
// eslint-disable-next-line @typescript-eslint/unbound-method
|
|
90
|
+
const oldResetTimeoutFunc = runnablePrototype.resetTimeout;
|
|
91
|
+
runnablePrototype.resetTimeout = function (this: Mocha.Runnable) {
|
|
92
|
+
oldResetTimeoutFunc.call(this);
|
|
93
|
+
TestTimeout.reset(this);
|
|
94
|
+
};
|
|
95
|
+
// eslint-disable-next-line @typescript-eslint/unbound-method
|
|
96
|
+
const oldClearTimeoutFunc = runnablePrototype.clearTimeout;
|
|
97
|
+
runnablePrototype.clearTimeout = function (this: Mocha.Runnable) {
|
|
98
|
+
TestTimeout.clear();
|
|
99
|
+
oldClearTimeoutFunc.call(this);
|
|
100
|
+
};
|
|
99
101
|
}
|
|
100
102
|
|
|
101
103
|
export interface TimeoutWithError {
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
104
|
+
/**
|
|
105
|
+
* Timeout duration in milliseconds, if it is great than 0 and not Infinity
|
|
106
|
+
* If it is undefined, then it will use test timeout if we are in side the test function
|
|
107
|
+
* Otherwise, there is no timeout
|
|
108
|
+
*/
|
|
109
|
+
durationMs?: number;
|
|
110
|
+
reject?: true;
|
|
111
|
+
errorMsg?: string;
|
|
110
112
|
}
|
|
111
113
|
export interface TimeoutWithValue<T = void> {
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
114
|
+
/**
|
|
115
|
+
* Timeout duration in milliseconds, if it is great than 0 and not Infinity
|
|
116
|
+
* If it is undefined, then it will use test timeout if we are in side the test function
|
|
117
|
+
* Otherwise, there is no timeout
|
|
118
|
+
*/
|
|
119
|
+
durationMs?: number;
|
|
120
|
+
reject: false;
|
|
121
|
+
value: T;
|
|
120
122
|
}
|
|
121
123
|
|
|
122
|
-
export type PromiseExecutor<T = void> = (
|
|
124
|
+
export type PromiseExecutor<T = void> = (
|
|
125
|
+
resolve: (value: T | PromiseLike<T>) => void,
|
|
126
|
+
reject: (reason?: any) => void,
|
|
127
|
+
) => void;
|
|
123
128
|
|
|
124
129
|
export async function timeoutAwait<T = void>(
|
|
125
|
-
|
|
126
|
-
|
|
130
|
+
promise: PromiseLike<T>,
|
|
131
|
+
timeoutOptions: TimeoutWithError | TimeoutWithValue<T> = {},
|
|
127
132
|
) {
|
|
128
|
-
|
|
133
|
+
return Promise.race([promise, timeoutPromise<T>(() => {}, timeoutOptions)]);
|
|
129
134
|
}
|
|
130
135
|
|
|
131
136
|
export async function ensureContainerConnected(container: Container): Promise<void> {
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
137
|
+
if (!container.connected) {
|
|
138
|
+
return timeoutPromise((resolve) => container.once("connected", () => resolve()));
|
|
139
|
+
}
|
|
135
140
|
}
|
|
136
141
|
|
|
137
142
|
// Create a promise based on the timeout options
|
|
138
143
|
async function getTimeoutPromise<T = void>(
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
144
|
+
executor: (
|
|
145
|
+
resolve: (value: T | PromiseLike<T>) => void,
|
|
146
|
+
reject: (reason?: any) => void,
|
|
147
|
+
) => void,
|
|
148
|
+
timeoutOptions: TimeoutWithError | TimeoutWithValue<T>,
|
|
149
|
+
err: Error | undefined,
|
|
142
150
|
) {
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
151
|
+
const timeout = timeoutOptions.durationMs ?? 0;
|
|
152
|
+
if (timeout <= 0 || !Number.isFinite(timeout)) {
|
|
153
|
+
return new Promise(executor);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
return new Promise<T>((resolve, reject) => {
|
|
157
|
+
const timeoutRejections = () => {
|
|
158
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
159
|
+
const errorObject = err!;
|
|
160
|
+
errorObject.message = `${errorObject.message} (${timeout}ms)`;
|
|
161
|
+
reject(err);
|
|
162
|
+
};
|
|
163
|
+
const timer = setTimeout(
|
|
164
|
+
() =>
|
|
165
|
+
timeoutOptions.reject === false
|
|
166
|
+
? resolve(timeoutOptions.value)
|
|
167
|
+
: timeoutRejections(),
|
|
168
|
+
timeout,
|
|
169
|
+
);
|
|
170
|
+
|
|
171
|
+
executor(
|
|
172
|
+
(value) => {
|
|
173
|
+
clearTimeout(timer);
|
|
174
|
+
resolve(value);
|
|
175
|
+
},
|
|
176
|
+
(reason) => {
|
|
177
|
+
clearTimeout(timer);
|
|
178
|
+
reject(reason);
|
|
179
|
+
},
|
|
180
|
+
);
|
|
181
|
+
});
|
|
169
182
|
}
|
|
170
183
|
|
|
171
184
|
// Create a promise based on test timeout and the timeout options
|
|
172
185
|
export async function timeoutPromise<T = void>(
|
|
173
|
-
|
|
174
|
-
|
|
186
|
+
executor: (
|
|
187
|
+
resolve: (value: T | PromiseLike<T>) => void,
|
|
188
|
+
reject: (reason?: any) => void,
|
|
189
|
+
) => void,
|
|
190
|
+
timeoutOptions: TimeoutWithError | TimeoutWithValue<T> = {},
|
|
175
191
|
): Promise<T> {
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
192
|
+
// create the timeout error outside the async task, so its callstack includes
|
|
193
|
+
// the original call site, this makes it easier to debug
|
|
194
|
+
const err =
|
|
195
|
+
timeoutOptions.reject === false
|
|
196
|
+
? undefined
|
|
197
|
+
: new Error(timeoutOptions.errorMsg ?? "Timed out");
|
|
198
|
+
const executorPromise = getTimeoutPromise(executor, timeoutOptions, err);
|
|
199
|
+
|
|
200
|
+
const currentTestTimeout = TestTimeout.getInstance();
|
|
201
|
+
if (currentTestTimeout === undefined) {
|
|
202
|
+
return executorPromise;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
return Promise.race([executorPromise, currentTestTimeout.getPromise()]).catch((e) => {
|
|
206
|
+
if (e === currentTestTimeout) {
|
|
207
|
+
if (timeoutOptions.reject !== false) {
|
|
208
|
+
// If the rejection is because of the timeout then
|
|
209
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
210
|
+
const errorObject = err!;
|
|
211
|
+
errorObject.message = `${
|
|
212
|
+
timeoutOptions.errorMsg ?? "Test timed out"
|
|
213
|
+
} (${currentTestTimeout.getTimeout()}ms)`;
|
|
214
|
+
throw errorObject;
|
|
215
|
+
}
|
|
216
|
+
return timeoutOptions.value;
|
|
217
|
+
}
|
|
218
|
+
throw e;
|
|
219
|
+
}) as Promise<T>;
|
|
200
220
|
}
|
package/tsconfig.json
CHANGED
|
@@ -1,18 +1,11 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
"mocha",
|
|
12
|
-
"node"
|
|
13
|
-
]
|
|
14
|
-
},
|
|
15
|
-
"include": [
|
|
16
|
-
"src/**/*"
|
|
17
|
-
]
|
|
2
|
+
"extends": "@fluidframework/build-common/ts-common-config.json",
|
|
3
|
+
"exclude": ["src/test/**/*"],
|
|
4
|
+
"compilerOptions": {
|
|
5
|
+
"rootDir": "./src",
|
|
6
|
+
"outDir": "./dist",
|
|
7
|
+
"composite": true,
|
|
8
|
+
"types": ["mocha", "node"],
|
|
9
|
+
},
|
|
10
|
+
"include": ["src/**/*"],
|
|
18
11
|
}
|