@fluidframework/test-utils 2.0.0-internal.4.0.5 → 2.0.0-internal.4.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/CHANGELOG.md +5 -0
- package/dist/loaderContainerTracker.d.ts +30 -15
- package/dist/loaderContainerTracker.d.ts.map +1 -1
- package/dist/loaderContainerTracker.js +136 -57
- package/dist/loaderContainerTracker.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/testObjectProvider.d.ts +3 -0
- package/dist/testObjectProvider.d.ts.map +1 -1
- package/dist/testObjectProvider.js +28 -9
- package/dist/testObjectProvider.js.map +1 -1
- package/package.json +27 -23
- package/src/loaderContainerTracker.ts +167 -63
- package/src/packageVersion.ts +1 -1
- package/src/testObjectProvider.ts +53 -10
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
ISequencedDocumentMessage,
|
|
12
12
|
MessageType,
|
|
13
13
|
} from "@fluidframework/protocol-definitions";
|
|
14
|
+
import { waitForContainerConnection } from "./containerUtils";
|
|
14
15
|
import { debug } from "./debug";
|
|
15
16
|
import { IOpProcessingController } from "./testObjectProvider";
|
|
16
17
|
import { timeoutAwait, timeoutPromise } from "./timeoutUtils";
|
|
@@ -24,6 +25,7 @@ interface ContainerRecord {
|
|
|
24
25
|
|
|
25
26
|
// LoaderContainerTracker paused state
|
|
26
27
|
paused: boolean;
|
|
28
|
+
pauseP?: Promise<void>; // promise for for the pause that is in progress
|
|
27
29
|
|
|
28
30
|
// Tracking trailing no-op that may or may be acked by the server so we can discount them
|
|
29
31
|
// See issue #5629
|
|
@@ -162,57 +164,51 @@ export class LoaderContainerTracker implements IOpProcessingController {
|
|
|
162
164
|
// REVIEW: do we need to unpatch the loaders?
|
|
163
165
|
}
|
|
164
166
|
|
|
165
|
-
/**
|
|
166
|
-
* Ensure all tracked containers are synchronized
|
|
167
|
-
*/
|
|
168
|
-
public async ensureSynchronized(...containers: IContainer[]): Promise<void> {
|
|
169
|
-
await this.processSynchronized(undefined, ...containers);
|
|
170
|
-
}
|
|
171
|
-
|
|
172
167
|
/**
|
|
173
168
|
* Ensure all tracked containers are synchronized with a time limit
|
|
169
|
+
*
|
|
170
|
+
* @deprecated - this method is equivalent to @see {@link LoaderContainerTracker.ensureSynchronized}, please configure the test timeout instead
|
|
174
171
|
*/
|
|
175
172
|
public async ensureSynchronizedWithTimeout?(
|
|
176
173
|
timeoutDuration: number | undefined,
|
|
177
174
|
...containers: IContainer[]
|
|
178
175
|
) {
|
|
179
|
-
await this.
|
|
176
|
+
await this.ensureSynchronized(...containers);
|
|
180
177
|
}
|
|
181
178
|
|
|
182
179
|
/**
|
|
183
180
|
* Make sure all the tracked containers are synchronized.
|
|
184
181
|
*
|
|
185
182
|
* No isDirty (non-readonly) containers
|
|
186
|
-
*
|
|
187
183
|
* No extra clientId in quorum of any container that is not tracked and still opened.
|
|
188
|
-
*
|
|
189
184
|
* - i.e. no pending Join/Leave message.
|
|
190
|
-
*
|
|
191
185
|
* No unresolved proposal (minSeqNum \>= lastProposalSeqNum)
|
|
192
|
-
*
|
|
193
186
|
* lastSequenceNumber of all container is the same
|
|
194
|
-
*
|
|
195
187
|
* clientSequenceNumberObserved is the same as clientSequenceNumber sent
|
|
196
|
-
*
|
|
197
188
|
* - this overlaps with !isDirty, but include task scheduler ops.
|
|
198
|
-
*
|
|
199
189
|
* - Trailing NoOp is tracked and don't count as pending ops.
|
|
190
|
+
*
|
|
191
|
+
* Containers that are already pause will resume process and paused again once
|
|
192
|
+
* everything is synchronized. Containers that aren't paused will remain unpaused when this
|
|
193
|
+
* function returns.
|
|
200
194
|
*/
|
|
201
|
-
|
|
202
|
-
timeoutDuration: number | undefined,
|
|
203
|
-
...containers: IContainer[]
|
|
204
|
-
) {
|
|
195
|
+
public async ensureSynchronized(...containers: IContainer[]): Promise<void> {
|
|
205
196
|
const resumed = this.resumeProcessing(...containers);
|
|
206
197
|
|
|
207
|
-
let waitingSequenceNumberSynchronized
|
|
198
|
+
let waitingSequenceNumberSynchronized: string | undefined;
|
|
208
199
|
// eslint-disable-next-line no-constant-condition
|
|
209
200
|
while (true) {
|
|
201
|
+
// yield a turn to allow side effect of resuming or the ops we just processed execute before we check
|
|
202
|
+
await new Promise<void>((resolve) => {
|
|
203
|
+
setTimeout(resolve, 0);
|
|
204
|
+
});
|
|
205
|
+
|
|
210
206
|
const containersToApply = this.getContainers(containers);
|
|
211
207
|
if (containersToApply.length === 0) {
|
|
212
208
|
break;
|
|
213
209
|
}
|
|
214
210
|
|
|
215
|
-
// Ignore readonly dirty containers, because it can't sent
|
|
211
|
+
// Ignore readonly dirty containers, because it can't sent ops and nothing can be done about it being dirty
|
|
216
212
|
const dirtyContainers = containersToApply.filter((c) => {
|
|
217
213
|
const { deltaManager, isDirty } = c;
|
|
218
214
|
return deltaManager.readOnlyInfo.readonly !== true && isDirty;
|
|
@@ -221,20 +217,22 @@ export class LoaderContainerTracker implements IOpProcessingController {
|
|
|
221
217
|
// Wait for all the leave messages
|
|
222
218
|
const pendingClients = this.getPendingClients(containersToApply);
|
|
223
219
|
if (pendingClients.length === 0) {
|
|
224
|
-
|
|
220
|
+
const needSync = this.needSequenceNumberSynchronize(containersToApply);
|
|
221
|
+
if (needSync === undefined) {
|
|
225
222
|
// done, we are in sync
|
|
226
223
|
break;
|
|
227
224
|
}
|
|
228
|
-
if (
|
|
229
|
-
//
|
|
230
|
-
waitingSequenceNumberSynchronized =
|
|
231
|
-
debugWait(
|
|
232
|
-
await timeoutAwait(this.waitForAnyInboundOps(containersToApply), {
|
|
233
|
-
errorMsg: "Timeout on waiting for sequence number synchronized",
|
|
234
|
-
});
|
|
225
|
+
if (waitingSequenceNumberSynchronized !== needSync.reason) {
|
|
226
|
+
// Don't repeat writing to console if it is the same reason
|
|
227
|
+
waitingSequenceNumberSynchronized = needSync.reason;
|
|
228
|
+
debugWait(needSync.message);
|
|
235
229
|
}
|
|
230
|
+
// Wait for one inbounds ops which might change the state of things
|
|
231
|
+
await timeoutAwait(this.waitForAnyInboundOps(containersToApply), {
|
|
232
|
+
errorMsg: `Timeout on ${needSync.message}`,
|
|
233
|
+
});
|
|
236
234
|
} else {
|
|
237
|
-
waitingSequenceNumberSynchronized =
|
|
235
|
+
waitingSequenceNumberSynchronized = undefined;
|
|
238
236
|
await timeoutAwait(this.waitForPendingClients(pendingClients), {
|
|
239
237
|
errorMsg: "Timeout on waiting for pending join or leave op",
|
|
240
238
|
});
|
|
@@ -242,12 +240,9 @@ export class LoaderContainerTracker implements IOpProcessingController {
|
|
|
242
240
|
} else {
|
|
243
241
|
// Wait for all the containers to be saved
|
|
244
242
|
debugWait(
|
|
245
|
-
`Waiting container to be saved ${
|
|
246
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
247
|
-
(c) => this.containers.get(c)!.index,
|
|
248
|
-
)}`,
|
|
243
|
+
`Waiting container to be saved ${this.containerIndexStrings(dirtyContainers)}`,
|
|
249
244
|
);
|
|
250
|
-
waitingSequenceNumberSynchronized =
|
|
245
|
+
waitingSequenceNumberSynchronized = undefined;
|
|
251
246
|
await Promise.all(
|
|
252
247
|
dirtyContainers.map(async (c) =>
|
|
253
248
|
Promise.race([
|
|
@@ -259,11 +254,6 @@ export class LoaderContainerTracker implements IOpProcessingController {
|
|
|
259
254
|
),
|
|
260
255
|
);
|
|
261
256
|
}
|
|
262
|
-
|
|
263
|
-
// yield a turn to allow side effect of the ops we just processed execute before we check again
|
|
264
|
-
await new Promise<void>((resolve) => {
|
|
265
|
-
setTimeout(resolve, 0);
|
|
266
|
-
});
|
|
267
257
|
}
|
|
268
258
|
|
|
269
259
|
// Pause all container that was resumed
|
|
@@ -319,48 +309,78 @@ export class LoaderContainerTracker implements IOpProcessingController {
|
|
|
319
309
|
*
|
|
320
310
|
* @param containersToApply - the set of containers to check
|
|
321
311
|
*/
|
|
322
|
-
private
|
|
312
|
+
private needSequenceNumberSynchronize(containersToApply: IContainer[]) {
|
|
313
|
+
// If there is a pending proposal, wait for it to be accepted
|
|
314
|
+
const minSeqNum = containersToApply[0].deltaManager.minimumSequenceNumber;
|
|
315
|
+
if (minSeqNum < this.lastProposalSeqNum) {
|
|
316
|
+
return {
|
|
317
|
+
reason: "Proposal",
|
|
318
|
+
message: `waiting for MSN to advance to proposal at sequence number ${this.lastProposalSeqNum}`,
|
|
319
|
+
};
|
|
320
|
+
}
|
|
321
|
+
|
|
323
322
|
// clientSequenceNumber check detects ops in flight, both on the wire and in the outbound queue
|
|
324
323
|
// We need both client sequence number and isDirty check because:
|
|
325
324
|
// - Currently isDirty flag ignores ops for task scheduler, so we need the client sequence number check
|
|
326
325
|
// - But isDirty flags include ops during forceReadonly and disconnected, because we don't submit
|
|
327
326
|
// the ops in the first place, clientSequenceNumber is not assigned
|
|
328
327
|
|
|
329
|
-
const
|
|
328
|
+
const containerWithInflightOps = containersToApply.filter((container) => {
|
|
330
329
|
if (container.deltaManager.readOnlyInfo.readonly === true) {
|
|
331
330
|
// Ignore readonly container. the clientSeqNum and clientSeqNumObserved might be out of sync
|
|
332
331
|
// because we transition to readonly when outbound is not empty or the in transit op got lost
|
|
333
|
-
return
|
|
332
|
+
return false;
|
|
334
333
|
}
|
|
335
334
|
// Note that in read only mode, the op won't be submitted
|
|
336
335
|
let deltaManager = container.deltaManager as any;
|
|
337
336
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
338
337
|
const { trailingNoOps } = this.containers.get(container)!;
|
|
339
|
-
// Back-compat: clientSequenceNumber
|
|
338
|
+
// Back-compat: lastSubmittedClientId/clientSequenceNumber/clientSequenceNumberObserved moved to ConnectionManager in 0.53
|
|
340
339
|
if (!("clientSequenceNumber" in deltaManager)) {
|
|
341
340
|
deltaManager = deltaManager.connectionManager;
|
|
342
341
|
}
|
|
343
342
|
assert("clientSequenceNumber" in deltaManager, "no clientSequenceNumber");
|
|
344
343
|
assert("clientSequenceNumberObserved" in deltaManager, "no clientSequenceNumber");
|
|
344
|
+
// If last submittedClientId isn't the current clientId, then we haven't send any ops
|
|
345
345
|
return (
|
|
346
|
-
deltaManager.
|
|
347
|
-
|
|
346
|
+
deltaManager.lastSubmittedClientId === container.clientId &&
|
|
347
|
+
deltaManager.clientSequenceNumber !==
|
|
348
|
+
(deltaManager.clientSequenceNumberObserved as number) + trailingNoOps
|
|
348
349
|
);
|
|
349
350
|
});
|
|
350
351
|
|
|
351
|
-
if (
|
|
352
|
-
return
|
|
352
|
+
if (containerWithInflightOps.length !== 0) {
|
|
353
|
+
return {
|
|
354
|
+
reason: "InflightOps",
|
|
355
|
+
message: `waiting for containers with inflight ops: ${this.containerIndexStrings(
|
|
356
|
+
containerWithInflightOps,
|
|
357
|
+
)}`,
|
|
358
|
+
};
|
|
353
359
|
}
|
|
354
360
|
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
361
|
+
// Check to see if all the container has process the same number of ops.
|
|
362
|
+
const maxSeqNum = Math.max(
|
|
363
|
+
...containersToApply.map((c) => c.deltaManager.lastSequenceNumber),
|
|
364
|
+
);
|
|
365
|
+
const containerWithPendingIncoming = containersToApply.filter(
|
|
366
|
+
(c) => c.deltaManager.lastSequenceNumber !== maxSeqNum,
|
|
367
|
+
);
|
|
368
|
+
if (containerWithPendingIncoming.length !== 0) {
|
|
369
|
+
return {
|
|
370
|
+
reason: "Pending",
|
|
371
|
+
message: `waiting for containers with pending incoming ops up to sequence number ${maxSeqNum}: ${this.containerIndexStrings(
|
|
372
|
+
containerWithPendingIncoming,
|
|
373
|
+
)}`,
|
|
374
|
+
};
|
|
359
375
|
}
|
|
376
|
+
return undefined;
|
|
377
|
+
}
|
|
360
378
|
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
379
|
+
private containerIndexStrings(containers: IContainer[]) {
|
|
380
|
+
return containers.map(
|
|
381
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
382
|
+
(c) => this.containers.get(c)!.index,
|
|
383
|
+
);
|
|
364
384
|
}
|
|
365
385
|
|
|
366
386
|
/**
|
|
@@ -433,6 +453,10 @@ export class LoaderContainerTracker implements IOpProcessingController {
|
|
|
433
453
|
const containersToApply = this.getContainers(containers);
|
|
434
454
|
for (const container of containersToApply) {
|
|
435
455
|
const record = this.containers.get(container);
|
|
456
|
+
assert(
|
|
457
|
+
record?.pauseP === undefined,
|
|
458
|
+
"Cannot resume container while pausing is in progress",
|
|
459
|
+
);
|
|
436
460
|
if (record?.paused === true) {
|
|
437
461
|
debugWait(`${record.index}: container resumed`);
|
|
438
462
|
container.deltaManager.inbound.resume();
|
|
@@ -447,26 +471,98 @@ export class LoaderContainerTracker implements IOpProcessingController {
|
|
|
447
471
|
/**
|
|
448
472
|
* Pause all queue activities on the containers given, or all tracked containers
|
|
449
473
|
* Any containers given that is not tracked will be ignored.
|
|
474
|
+
*
|
|
475
|
+
* When a container is paused, it is assumed that we want fine grain control over op
|
|
476
|
+
* sequencing. This function will prepare the container and force it into write mode to
|
|
477
|
+
* avoid missing join messages or change the sequence of event when switching from read to
|
|
478
|
+
* write mode.
|
|
450
479
|
*/
|
|
451
480
|
public async pauseProcessing(...containers: IContainer[]) {
|
|
452
|
-
const
|
|
481
|
+
const waitP: Promise<void>[] = [];
|
|
453
482
|
const containersToApply = this.getContainers(containers);
|
|
454
483
|
for (const container of containersToApply) {
|
|
455
484
|
const record = this.containers.get(container);
|
|
456
485
|
if (record !== undefined && !record.paused) {
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
record.
|
|
486
|
+
if (record.pauseP === undefined) {
|
|
487
|
+
record.pauseP = this.pauseContainer(container, record);
|
|
488
|
+
}
|
|
489
|
+
waitP.push(record.pauseP);
|
|
461
490
|
}
|
|
462
491
|
}
|
|
463
|
-
await Promise.all(
|
|
492
|
+
await Promise.all(waitP);
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
/**
|
|
496
|
+
* When a container is paused, it is assumed that we want fine grain control over op
|
|
497
|
+
* sequencing. This function will prepare the container and force it into write mode to
|
|
498
|
+
* avoid missing join messages or change the sequence of event when switching from read to
|
|
499
|
+
* write mode.
|
|
500
|
+
*
|
|
501
|
+
* @param container - the container to pause
|
|
502
|
+
* @param record - the record for the container
|
|
503
|
+
*/
|
|
504
|
+
private async pauseContainer(container: IContainer, record: ContainerRecord) {
|
|
505
|
+
debugWait(`${record.index}: pausing container`);
|
|
506
|
+
assert(!container.deltaManager.outbound.paused, "Container should not be paused yet");
|
|
507
|
+
assert(!container.deltaManager.inbound.paused, "Container should not be paused yet");
|
|
508
|
+
|
|
509
|
+
// Pause outbound
|
|
510
|
+
debugWait(`${record.index}: pausing container outbound queues`);
|
|
511
|
+
await container.deltaManager.outbound.pause();
|
|
512
|
+
|
|
513
|
+
// Ensure the container is connected first.
|
|
514
|
+
if (container.connectionState !== ConnectionState.Connected) {
|
|
515
|
+
debugWait(`${record.index}: Wait for container connection`);
|
|
516
|
+
await waitForContainerConnection(container);
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
// Check if the container is in write mode
|
|
520
|
+
if (!container.deltaManager.active) {
|
|
521
|
+
let proposalP: Promise<boolean> | undefined;
|
|
522
|
+
if (container.deltaManager.outbound.idle) {
|
|
523
|
+
// Need to generate an op to force write mode
|
|
524
|
+
debugWait(`${record.index}: container force write connection`);
|
|
525
|
+
const maybeContainer = container as Partial<IContainer>;
|
|
526
|
+
const codeProposal = maybeContainer.getLoadedCodeDetails
|
|
527
|
+
? // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
528
|
+
container.getLoadedCodeDetails()!
|
|
529
|
+
: (container as any).chaincodePackage;
|
|
530
|
+
|
|
531
|
+
proposalP = container.proposeCodeDetails(codeProposal);
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
// Wait for nack
|
|
535
|
+
debugWait(`${record.index}: Wait for container disconnect`);
|
|
536
|
+
container.deltaManager.outbound.resume();
|
|
537
|
+
await new Promise<void>((resolve) => container.once("disconnected", resolve));
|
|
538
|
+
const accepted = proposalP ? await proposalP : false;
|
|
539
|
+
assert(!accepted, "A proposal in read mode should be rejected");
|
|
540
|
+
await container.deltaManager.outbound.pause();
|
|
541
|
+
|
|
542
|
+
// Ensure the container is reconnect.
|
|
543
|
+
if (container.connectionState !== ConnectionState.Connected) {
|
|
544
|
+
debugWait(`${record.index}: Wait for container reconnection`);
|
|
545
|
+
await waitForContainerConnection(container);
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
debugWait(`${record.index}: pausing container inbound queues`);
|
|
550
|
+
|
|
551
|
+
// Pause inbound
|
|
552
|
+
await container.deltaManager.inbound.pause();
|
|
553
|
+
|
|
554
|
+
debugWait(`${record.index}: container paused`);
|
|
555
|
+
|
|
556
|
+
record.pauseP = undefined;
|
|
557
|
+
record.paused = true;
|
|
464
558
|
}
|
|
465
559
|
|
|
466
560
|
/**
|
|
467
561
|
* Pause all queue activities on all tracked containers, and resume only
|
|
468
562
|
* inbound to process ops until it is idle. All queues are left in the paused state
|
|
469
|
-
* after the function
|
|
563
|
+
* after the function.
|
|
564
|
+
*
|
|
565
|
+
* Pausing will switch the container to write mode. See `pauseProcessing`
|
|
470
566
|
*/
|
|
471
567
|
public async processIncoming(...containers: IContainer[]) {
|
|
472
568
|
return this.processQueue(containers, (container) => container.deltaManager.inbound);
|
|
@@ -475,7 +571,9 @@ export class LoaderContainerTracker implements IOpProcessingController {
|
|
|
475
571
|
/**
|
|
476
572
|
* Pause all queue activities on all tracked containers, and resume only
|
|
477
573
|
* outbound to process ops until it is idle. All queues are left in the paused state
|
|
478
|
-
* after the function
|
|
574
|
+
* after the function.
|
|
575
|
+
*
|
|
576
|
+
* Pausing will switch the container to write mode. See `pauseProcessing`
|
|
479
577
|
*/
|
|
480
578
|
public async processOutgoing(...containers: IContainer[]) {
|
|
481
579
|
return this.processQueue(containers, (container) => container.deltaManager.outbound);
|
|
@@ -492,9 +590,15 @@ export class LoaderContainerTracker implements IOpProcessingController {
|
|
|
492
590
|
const resumed: IDeltaQueue<U>[] = [];
|
|
493
591
|
|
|
494
592
|
const containersToApply = this.getContainers(containers);
|
|
593
|
+
|
|
495
594
|
const inflightTracker = new Map<IContainer, number>();
|
|
496
595
|
const cleanup: (() => void)[] = [];
|
|
497
596
|
for (const container of containersToApply) {
|
|
597
|
+
assert(
|
|
598
|
+
container.deltaManager.active,
|
|
599
|
+
"Container should be connected in write mode already",
|
|
600
|
+
);
|
|
601
|
+
|
|
498
602
|
const queue = getQueue(container);
|
|
499
603
|
|
|
500
604
|
// track the outgoing ops (if any) to make sure they make the round trip to at least to the same client
|
package/src/packageVersion.ts
CHANGED
|
@@ -3,7 +3,13 @@
|
|
|
3
3
|
* Licensed under the MIT License.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
IContainer,
|
|
8
|
+
IHostLoader,
|
|
9
|
+
IFluidCodeDetails,
|
|
10
|
+
LoaderHeader,
|
|
11
|
+
ILoader,
|
|
12
|
+
} from "@fluidframework/container-definitions";
|
|
7
13
|
import {
|
|
8
14
|
ITelemetryGenericEvent,
|
|
9
15
|
ITelemetryBaseLogger,
|
|
@@ -107,6 +113,9 @@ export interface ITestContainerConfig {
|
|
|
107
113
|
|
|
108
114
|
/** Loader options for the loader used to create containers */
|
|
109
115
|
loaderProps?: Partial<ILoaderProps>;
|
|
116
|
+
|
|
117
|
+
/** Temporary flag: simulate read connection using delay connection, default is true */
|
|
118
|
+
simulateReadConnectionUsingDelay?: boolean;
|
|
110
119
|
}
|
|
111
120
|
|
|
112
121
|
export const createDocumentId = (): string => uuid();
|
|
@@ -377,10 +386,44 @@ export class TestObjectProvider implements ITestObjectProvider {
|
|
|
377
386
|
requestHeader?: IRequestHeader,
|
|
378
387
|
): Promise<IContainer> {
|
|
379
388
|
const loader = this.createLoader([[defaultCodeDetails, entryPoint]], loaderProps);
|
|
380
|
-
return
|
|
389
|
+
return this.resolveContainer(loader, requestHeader);
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
private async resolveContainer(
|
|
393
|
+
loader: ILoader,
|
|
394
|
+
requestHeader?: IRequestHeader,
|
|
395
|
+
delay: boolean = true,
|
|
396
|
+
) {
|
|
397
|
+
// Once AB#3889 is done to switch default connection mode to "read" on load, we don't need
|
|
398
|
+
// to load "delayed" across the board. Remove the following code.
|
|
399
|
+
const delayConnection =
|
|
400
|
+
delay &&
|
|
401
|
+
(requestHeader === undefined || requestHeader[LoaderHeader.reconnect] !== false);
|
|
402
|
+
const headers: IRequestHeader | undefined = delayConnection
|
|
403
|
+
? {
|
|
404
|
+
[LoaderHeader.loadMode]: { deltaConnection: "delayed" },
|
|
405
|
+
...requestHeader,
|
|
406
|
+
}
|
|
407
|
+
: requestHeader;
|
|
408
|
+
|
|
409
|
+
const container = await loader.resolve({
|
|
381
410
|
url: await this.driver.createContainerUrl(this.documentId),
|
|
382
|
-
headers
|
|
411
|
+
headers,
|
|
383
412
|
});
|
|
413
|
+
|
|
414
|
+
// Once AB#3889 is done to switch default connection mode to "read" on load, we don't need
|
|
415
|
+
// to load "delayed" across the board. Remove the following code.
|
|
416
|
+
if (delayConnection) {
|
|
417
|
+
// Older version may not have connect/disconnect. It was add in PR#9439, and available >= 0.59.1000
|
|
418
|
+
const maybeContainer = container as Partial<IContainer>;
|
|
419
|
+
if (maybeContainer.connect !== undefined) {
|
|
420
|
+
container.connect();
|
|
421
|
+
} else {
|
|
422
|
+
// back compat. Remove when we don't support < 0.59.1000
|
|
423
|
+
(container as any).resume();
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
return container;
|
|
384
427
|
}
|
|
385
428
|
|
|
386
429
|
/**
|
|
@@ -432,10 +475,12 @@ export class TestObjectProvider implements ITestObjectProvider {
|
|
|
432
475
|
requestHeader?: IRequestHeader,
|
|
433
476
|
): Promise<IContainer> {
|
|
434
477
|
const loader = this.makeTestLoader(testContainerConfig);
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
478
|
+
|
|
479
|
+
const container = await this.resolveContainer(
|
|
480
|
+
loader,
|
|
481
|
+
requestHeader,
|
|
482
|
+
testContainerConfig?.simulateReadConnectionUsingDelay,
|
|
483
|
+
);
|
|
439
484
|
await this.waitContainerToCatchUp(container);
|
|
440
485
|
|
|
441
486
|
return container;
|
|
@@ -455,9 +500,7 @@ export class TestObjectProvider implements ITestObjectProvider {
|
|
|
455
500
|
}
|
|
456
501
|
|
|
457
502
|
public async ensureSynchronized(timeoutDuration?: number): Promise<void> {
|
|
458
|
-
return this._loaderContainerTracker.
|
|
459
|
-
? this._loaderContainerTracker.ensureSynchronizedWithTimeout(timeoutDuration)
|
|
460
|
-
: this._loaderContainerTracker.ensureSynchronized();
|
|
503
|
+
return this._loaderContainerTracker.ensureSynchronized();
|
|
461
504
|
}
|
|
462
505
|
|
|
463
506
|
public async waitContainerToCatchUp(container: IContainer) {
|