@fluidframework/test-utils 2.0.0-internal.7.2.2 → 2.0.0-internal.7.4.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.
Files changed (92) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/api-extractor-lint.json +13 -0
  3. package/api-extractor.json +3 -3
  4. package/api-report/test-utils.api.md +83 -74
  5. package/dist/DriverWrappers.d.ts +3 -0
  6. package/dist/DriverWrappers.d.ts.map +1 -1
  7. package/dist/DriverWrappers.js +3 -0
  8. package/dist/DriverWrappers.js.map +1 -1
  9. package/dist/TestConfigs.d.ts +4 -1
  10. package/dist/TestConfigs.d.ts.map +1 -1
  11. package/dist/TestConfigs.js +3 -0
  12. package/dist/TestConfigs.js.map +1 -1
  13. package/dist/TestSummaryUtils.d.ts +4 -2
  14. package/dist/TestSummaryUtils.d.ts.map +1 -1
  15. package/dist/TestSummaryUtils.js +3 -0
  16. package/dist/TestSummaryUtils.js.map +1 -1
  17. package/dist/containerUtils.d.ts +17 -0
  18. package/dist/containerUtils.d.ts.map +1 -1
  19. package/dist/containerUtils.js +41 -1
  20. package/dist/containerUtils.js.map +1 -1
  21. package/dist/index.d.ts +2 -2
  22. package/dist/index.d.ts.map +1 -1
  23. package/dist/index.js +4 -1
  24. package/dist/index.js.map +1 -1
  25. package/dist/interfaces.d.ts +6 -0
  26. package/dist/interfaces.d.ts.map +1 -1
  27. package/dist/interfaces.js.map +1 -1
  28. package/dist/loaderContainerTracker.d.ts +3 -0
  29. package/dist/loaderContainerTracker.d.ts.map +1 -1
  30. package/dist/loaderContainerTracker.js +3 -0
  31. package/dist/loaderContainerTracker.js.map +1 -1
  32. package/dist/localCodeLoader.d.ts +7 -0
  33. package/dist/localCodeLoader.d.ts.map +1 -1
  34. package/dist/localCodeLoader.js +1 -0
  35. package/dist/localCodeLoader.js.map +1 -1
  36. package/dist/localLoader.d.ts +2 -0
  37. package/dist/localLoader.d.ts.map +1 -1
  38. package/dist/localLoader.js +2 -0
  39. package/dist/localLoader.js.map +1 -1
  40. package/dist/packageVersion.d.ts +1 -1
  41. package/dist/packageVersion.js +1 -1
  42. package/dist/packageVersion.js.map +1 -1
  43. package/dist/retry.d.ts +1 -0
  44. package/dist/retry.d.ts.map +1 -1
  45. package/dist/retry.js +1 -0
  46. package/dist/retry.js.map +1 -1
  47. package/dist/test-utils-alpha.d.ts +140 -0
  48. package/dist/test-utils-beta.d.ts +228 -0
  49. package/dist/test-utils-public.d.ts +228 -0
  50. package/dist/test-utils-untrimmed.d.ts +993 -0
  51. package/dist/testContainerRuntimeFactory.d.ts +2 -0
  52. package/dist/testContainerRuntimeFactory.d.ts.map +1 -1
  53. package/dist/testContainerRuntimeFactory.js +35 -12
  54. package/dist/testContainerRuntimeFactory.js.map +1 -1
  55. package/dist/testContainerRuntimeFactoryWithDefaultDataStore.d.ts +1 -0
  56. package/dist/testContainerRuntimeFactoryWithDefaultDataStore.d.ts.map +1 -1
  57. package/dist/testContainerRuntimeFactoryWithDefaultDataStore.js +10 -2
  58. package/dist/testContainerRuntimeFactoryWithDefaultDataStore.js.map +1 -1
  59. package/dist/testFluidObject.d.ts +5 -0
  60. package/dist/testFluidObject.d.ts.map +1 -1
  61. package/dist/testFluidObject.js +6 -3
  62. package/dist/testFluidObject.js.map +1 -1
  63. package/dist/testObjectProvider.d.ts +287 -37
  64. package/dist/testObjectProvider.d.ts.map +1 -1
  65. package/dist/testObjectProvider.js +310 -25
  66. package/dist/testObjectProvider.js.map +1 -1
  67. package/dist/timeoutUtils.d.ts +11 -0
  68. package/dist/timeoutUtils.d.ts.map +1 -1
  69. package/dist/timeoutUtils.js +5 -0
  70. package/dist/timeoutUtils.js.map +1 -1
  71. package/dist/tsdoc-metadata.json +1 -1
  72. package/lib/test-utils-alpha.d.ts +140 -0
  73. package/lib/test-utils-beta.d.ts +228 -0
  74. package/lib/test-utils-public.d.ts +228 -0
  75. package/lib/test-utils-untrimmed.d.ts +993 -0
  76. package/package.json +63 -35
  77. package/src/DriverWrappers.ts +3 -0
  78. package/src/TestConfigs.ts +4 -1
  79. package/src/TestSummaryUtils.ts +9 -2
  80. package/src/containerUtils.ts +42 -0
  81. package/src/index.ts +7 -1
  82. package/src/interfaces.ts +6 -0
  83. package/src/loaderContainerTracker.ts +3 -0
  84. package/src/localCodeLoader.ts +7 -0
  85. package/src/localLoader.ts +2 -0
  86. package/src/packageVersion.ts +1 -1
  87. package/src/retry.ts +1 -0
  88. package/src/testContainerRuntimeFactory.ts +40 -13
  89. package/src/testContainerRuntimeFactoryWithDefaultDataStore.ts +11 -0
  90. package/src/testFluidObject.ts +10 -10
  91. package/src/testObjectProvider.ts +524 -47
  92. package/src/timeoutUtils.ts +11 -0
@@ -39,6 +39,9 @@ const defaultCodeDetails: IFluidCodeDetails = {
39
39
  config: {},
40
40
  };
41
41
 
42
+ /**
43
+ * @internal
44
+ */
42
45
  export interface IOpProcessingController {
43
46
  processIncoming(...containers: IContainer[]): Promise<void>;
44
47
  processOutgoing(...containers: IContainer[]): Promise<void>;
@@ -46,16 +49,86 @@ export interface IOpProcessingController {
46
49
  resumeProcessing(...containers: IContainer[]): void;
47
50
  }
48
51
 
52
+ /**
53
+ * @internal
54
+ */
49
55
  export interface ITestObjectProvider {
56
+ /**
57
+ * Indicates which type of test object provider is being used.
58
+ */
59
+ type: "TestObjectProvider" | "TestObjectProviderWithVersionedLoad";
60
+
61
+ /**
62
+ * The document id to retrieve or create containers
63
+ */
64
+ documentId: string;
65
+
66
+ /**
67
+ * Creates the document service after extracting different endpoints URLs from a resolved URL.
68
+ */
69
+ documentServiceFactory: IDocumentServiceFactory;
70
+
71
+ /**
72
+ * Test driver used to create the IDocumentServiceFactory. Varies depending on the test type.
73
+ */
74
+ driver: ITestDriver;
75
+
76
+ /**
77
+ * Logger used to track expected and unexpected events.
78
+ */
79
+ logger: EventAndErrorTrackingLogger | undefined;
80
+
81
+ /**
82
+ * Used to create a url for the created container with any data store path given in the relative url.
83
+ */
84
+ urlResolver: IUrlResolver;
85
+
86
+ /**
87
+ * Default IFluidCodeDetails used to create containers.
88
+ */
89
+ defaultCodeDetails: IFluidCodeDetails;
90
+
91
+ /**
92
+ * Contains functions to pause/resume op processing.
93
+ */
94
+ opProcessingController: IOpProcessingController;
95
+
96
+ /**
97
+ * Represents the entry point for a Fluid container.
98
+ */
50
99
  createFluidEntryPoint: (testContainerConfig?: ITestContainerConfig) => fluidEntryPoint;
100
+
101
+ /**
102
+ * Create a loader. Containers created/loaded through this loader will be added to the OpProcessingController.
103
+ *
104
+ * Only the version of the loader will vary based on compat config. The version of
105
+ * containerRuntime/dataRuntime used in fluidEntryPoint will be used as is from what is passed in.
106
+ *
107
+ * @param packageEntries - list of code details and fluidEntryPoint pairs.
108
+ */
51
109
  createLoader(
52
110
  packageEntries: Iterable<[IFluidCodeDetails, fluidEntryPoint]>,
53
111
  loaderProps?: Partial<ILoaderProps>,
54
112
  ): IHostLoader;
113
+
114
+ /**
115
+ * Create a container using a default document id and code details.
116
+ * Container created is automatically added to the OpProcessingController to manage op flow
117
+ *
118
+ * Only the version of the loader will vary based on compat config. The version of
119
+ * containerRuntime/dataRuntime used in fluidEntryPoint will be used as is from what is passed in.
120
+ *
121
+ * @param packageEntries - list of code details and fluidEntryPoint pairs.
122
+ */
123
+
55
124
  createContainer(
56
125
  entryPoint: fluidEntryPoint,
57
126
  loaderProps?: Partial<ILoaderProps>,
58
127
  ): Promise<IContainer>;
128
+
129
+ /**
130
+ * Loads a container using the default document id
131
+ */
59
132
  loadContainer(
60
133
  entryPoint: fluidEntryPoint,
61
134
  loaderProps?: Partial<ILoaderProps>,
@@ -63,39 +136,62 @@ export interface ITestObjectProvider {
63
136
  ): Promise<IContainer>;
64
137
 
65
138
  /**
66
- * Used to create a test Container. The Loader/ContainerRuntime/DataRuntime might be different versioned.
67
- * In generateLocalCompatTest(), this Container and its runtime will be arbitrarily-versioned.
139
+ * Make a test loader. Containers created/loaded through this loader will be added to the OpProcessingController.
140
+ * The version of the loader/containerRuntime/dataRuntime may vary based on compat config of the current run
141
+ * @param testContainerConfig - optional configuring the test Container
68
142
  */
69
143
  makeTestLoader(testContainerConfig?: ITestContainerConfig): IHostLoader;
144
+
145
+ /**
146
+ * Make a container using a default document id and code details
147
+ * Container loaded is automatically added to the OpProcessingController to manage op flow
148
+ * @param testContainerConfig - optional configuring the test Container
149
+ */
70
150
  makeTestContainer(testContainerConfig?: ITestContainerConfig): Promise<IContainer>;
151
+
152
+ /**
153
+ * Load a container using a default document id and code details.
154
+ * IContainer loaded is automatically added to the OpProcessingController to manage op flow
155
+ * @param testContainerConfig - optional configuring the test Container
156
+ * @param requestHeader - optional headers to be supplied to the loader
157
+ */
71
158
  loadTestContainer(
72
159
  testContainerConfig?: ITestContainerConfig,
73
160
  requestHeader?: IRequestHeader,
74
161
  ): Promise<IContainer>;
162
+
75
163
  /**
76
- *
77
- * @param url - Resolved container URL
164
+ * Update the document ID from the resolved container's URL and reset the ID property
78
165
  */
79
166
  updateDocumentId(url: IResolvedUrl | undefined): void;
80
167
 
81
- logger: ITelemetryBaseLogger;
82
- documentServiceFactory: IDocumentServiceFactory;
83
- urlResolver: IUrlResolver;
84
- defaultCodeDetails: IFluidCodeDetails;
85
- opProcessingController: IOpProcessingController;
86
-
168
+ /**
169
+ * Make sure all the tracked containers are synchronized.
170
+ */
87
171
  ensureSynchronized(timeoutDuration?: number): Promise<void>;
88
- reset(): void;
89
172
 
90
- documentId: string;
91
- driver: ITestDriver;
173
+ /**
174
+ * Reset the tracker, closing all containers and stop tracking them.
175
+ */
176
+ resetLoaderContainerTracker(syncSummarizerClients?: boolean);
177
+
178
+ /**
179
+ * Resets and closes all tracked containers and class states.
180
+ */
181
+ reset(): void;
92
182
  }
93
183
 
184
+ /**
185
+ * @internal
186
+ */
94
187
  export enum DataObjectFactoryType {
95
188
  Primed, // default
96
189
  Test,
97
190
  }
98
191
 
192
+ /**
193
+ * @internal
194
+ */
99
195
  export interface ITestContainerConfig {
100
196
  /** TestFluidDataObject instead of PrimedDataStore */
101
197
  fluidDataObjectType?: DataObjectFactoryType;
@@ -113,11 +209,28 @@ export interface ITestContainerConfig {
113
209
  loaderProps?: Partial<ILoaderProps>;
114
210
  }
115
211
 
212
+ /**
213
+ * @internal
214
+ */
116
215
  export const createDocumentId = (): string => uuid();
117
216
 
118
- interface IDocumentIdStrategy {
217
+ /**
218
+ * Used to retrieve, update, and reset document id based on the type of driver being used.
219
+ *
220
+ * @internal
221
+ */
222
+ export interface IDocumentIdStrategy {
223
+ /**
224
+ * Get document id
225
+ */
119
226
  get(): string;
227
+ /**
228
+ * Update the document ID from the resolved container's URL and reset the ID property
229
+ */
120
230
  update(resolvedUrl?: IResolvedUrl): void;
231
+ /**
232
+ * Reset document id to a new document id
233
+ */
121
234
  reset(): void;
122
235
  }
123
236
 
@@ -155,6 +268,7 @@ function getDocumentIdStrategy(type?: TestDriverTypes): IDocumentIdStrategy {
155
268
  * It also tracks all unexpected errors.
156
269
  * At any point you call reportAndClearTrackedEvents which will provide all unexpected errors, and
157
270
  * any expected events that have not occurred.
271
+ * @internal
158
272
  */
159
273
  export class EventAndErrorTrackingLogger implements ITelemetryBaseLogger {
160
274
  /**
@@ -243,8 +357,13 @@ export class EventAndErrorTrackingLogger implements ITelemetryBaseLogger {
243
357
 
244
358
  /**
245
359
  * Shared base class for test object provider. Contain code for loader and container creation and loading
360
+ * @internal
246
361
  */
247
362
  export class TestObjectProvider implements ITestObjectProvider {
363
+ /**
364
+ * {@inheritDoc ITestObjectProvider."type"}
365
+ */
366
+ public readonly type = "TestObjectProvider";
248
367
  private _loaderContainerTracker = new LoaderContainerTracker();
249
368
  private _documentServiceFactory: IDocumentServiceFactory | undefined;
250
369
  private _urlResolver: IUrlResolver | undefined;
@@ -259,8 +378,14 @@ export class TestObjectProvider implements ITestObjectProvider {
259
378
  * and factory for TestFluidObject
260
379
  */
261
380
  constructor(
262
- public readonly LoaderConstructor: typeof Loader,
381
+ private readonly LoaderConstructor: typeof Loader,
382
+ /**
383
+ * {@inheritDoc ITestObjectProvider.driver}
384
+ */
263
385
  public readonly driver: ITestDriver,
386
+ /**
387
+ * {@inheritDoc ITestObjectProvider.createFluidEntryPoint}
388
+ */
264
389
  public readonly createFluidEntryPoint: (
265
390
  testContainerConfig?: ITestContainerConfig,
266
391
  ) => fluidEntryPoint,
@@ -268,7 +393,10 @@ export class TestObjectProvider implements ITestObjectProvider {
268
393
  this._documentIdStrategy = getDocumentIdStrategy(driver.type);
269
394
  }
270
395
 
271
- get logger(): EventAndErrorTrackingLogger {
396
+ /**
397
+ * {@inheritDoc ITestObjectProvider.logger}
398
+ */
399
+ public get logger(): EventAndErrorTrackingLogger {
272
400
  if (this._logger === undefined) {
273
401
  this._logger = new EventAndErrorTrackingLogger(
274
402
  createChildLogger({
@@ -287,43 +415,53 @@ export class TestObjectProvider implements ITestObjectProvider {
287
415
  return this._logger;
288
416
  }
289
417
 
290
- set logger(logger: EventAndErrorTrackingLogger) {
418
+ private set logger(logger: EventAndErrorTrackingLogger) {
291
419
  this._logger = logger;
292
420
  }
293
421
 
294
- get documentServiceFactory() {
422
+ /**
423
+ * {@inheritDoc ITestObjectProvider.documentServiceFactory}
424
+ */
425
+ public get documentServiceFactory() {
295
426
  if (!this._documentServiceFactory) {
296
427
  this._documentServiceFactory = this.driver.createDocumentServiceFactory();
297
428
  }
298
429
  return this._documentServiceFactory;
299
430
  }
300
431
 
301
- get urlResolver() {
432
+ /**
433
+ * {@inheritDoc ITestObjectProvider.urlResolver}
434
+ */
435
+ public get urlResolver() {
302
436
  if (!this._urlResolver) {
303
437
  this._urlResolver = this.driver.createUrlResolver();
304
438
  }
305
439
  return this._urlResolver;
306
440
  }
307
441
 
308
- get documentId() {
442
+ /**
443
+ * {@inheritDoc ITestObjectProvider.documentId}
444
+ */
445
+ public get documentId() {
309
446
  return this._documentIdStrategy.get();
310
447
  }
311
448
 
312
- get defaultCodeDetails() {
449
+ /**
450
+ * {@inheritDoc ITestObjectProvider.defaultCodeDetails}
451
+ */
452
+ public get defaultCodeDetails() {
313
453
  return defaultCodeDetails;
314
454
  }
315
455
 
316
- get opProcessingController(): IOpProcessingController {
456
+ /**
457
+ * {@inheritDoc ITestObjectProvider.opProcessingController}
458
+ */
459
+ public get opProcessingController(): IOpProcessingController {
317
460
  return this._loaderContainerTracker;
318
461
  }
319
462
 
320
463
  /**
321
- * Create a loader. Containers created/loaded through this loader will be added to the OpProcessingController.
322
- *
323
- * Only the version of the loader will vary based on compat config. The version of
324
- * containerRuntime/dataRuntime used in fluidEntryPoint will be used as is from what is passed in.
325
- *
326
- * @param packageEntries - list of code details and fluidEntryPoint pairs.
464
+ * {@inheritDoc ITestObjectProvider.createLoader}
327
465
  */
328
466
  public createLoader(
329
467
  packageEntries: Iterable<[IFluidCodeDetails, fluidEntryPoint]>,
@@ -346,13 +484,7 @@ export class TestObjectProvider implements ITestObjectProvider {
346
484
  }
347
485
 
348
486
  /**
349
- * Create a container using a default document id and code details.
350
- * Container created is automatically added to the OpProcessingController to manage op flow
351
- *
352
- * Only the version of the loader will vary based on compat config. The version of
353
- * containerRuntime/dataRuntime used in fluidEntryPoint will be used as is from what is passed in.
354
- *
355
- * @param packageEntries - list of code details and fluidEntryPoint pairs.
487
+ * {@inheritDoc ITestObjectProvider.createContainer}
356
488
  */
357
489
  public async createContainer(entryPoint: fluidEntryPoint, loaderProps?: Partial<ILoaderProps>) {
358
490
  if (this._documentCreated) {
@@ -373,6 +505,9 @@ export class TestObjectProvider implements ITestObjectProvider {
373
505
  return container;
374
506
  }
375
507
 
508
+ /**
509
+ * {@inheritDoc ITestObjectProvider.loadContainer}
510
+ */
376
511
  public async loadContainer(
377
512
  entryPoint: fluidEntryPoint,
378
513
  loaderProps?: Partial<ILoaderProps>,
@@ -390,9 +525,7 @@ export class TestObjectProvider implements ITestObjectProvider {
390
525
  }
391
526
 
392
527
  /**
393
- * Make a test loader. Containers created/loaded through this loader will be added to the OpProcessingController.
394
- * The version of the loader/containerRuntime/dataRuntime may vary based on compat config of the current run
395
- * @param testContainerConfig - optional configuring the test Container
528
+ * {@inheritDoc ITestObjectProvider.makeTestLoader}
396
529
  */
397
530
  public makeTestLoader(testContainerConfig?: ITestContainerConfig) {
398
531
  return this.createLoader(
@@ -402,9 +535,7 @@ export class TestObjectProvider implements ITestObjectProvider {
402
535
  }
403
536
 
404
537
  /**
405
- * Make a container using a default document id and code details
406
- * Container loaded is automatically added to the OpProcessingController to manage op flow
407
- * @param testContainerConfig - optional configuring the test Container
538
+ * {@inheritDoc ITestObjectProvider.makeTestContainer}
408
539
  */
409
540
  public async makeTestContainer(
410
541
  testContainerConfig?: ITestContainerConfig,
@@ -428,10 +559,7 @@ export class TestObjectProvider implements ITestObjectProvider {
428
559
  }
429
560
 
430
561
  /**
431
- * Load a container using a default document id and code details.
432
- * IContainer loaded is automatically added to the OpProcessingController to manage op flow
433
- * @param testContainerConfig - optional configuring the test Container
434
- * @param requestHeader - optional headers to be supplied to the loader
562
+ * {@inheritDoc ITestObjectProvider.loadTestContainer}
435
563
  */
436
564
  public async loadTestContainer(
437
565
  testContainerConfig?: ITestContainerConfig,
@@ -445,6 +573,9 @@ export class TestObjectProvider implements ITestObjectProvider {
445
573
  return container;
446
574
  }
447
575
 
576
+ /**
577
+ * {@inheritDoc ITestObjectProvider.reset}
578
+ */
448
579
  public reset() {
449
580
  this._loaderContainerTracker.reset();
450
581
  this._documentServiceFactory = undefined;
@@ -458,11 +589,340 @@ export class TestObjectProvider implements ITestObjectProvider {
458
589
  this._documentCreated = false;
459
590
  }
460
591
 
592
+ /**
593
+ * {@inheritDoc ITestObjectProvider.ensureSynchronized}
594
+ */
595
+ public async ensureSynchronized(): Promise<void> {
596
+ return this._loaderContainerTracker.ensureSynchronized();
597
+ }
598
+
599
+ private async waitContainerToCatchUp(container: IContainer) {
600
+ // The original waitContainerToCatchUp() from container loader uses either Container.resume()
601
+ // or Container.connect() as part of its implementation. However, resume() was deprecated
602
+ // and eventually replaced with connect(). To avoid issues during LTS compatibility testing
603
+ // with older container versions issues, we use resume() when connect() is unavailable.
604
+ if ((container as any).connect === undefined) {
605
+ (container as any).connect = (container as any).resume;
606
+ }
607
+
608
+ return waitContainerToCatchUp_original(container);
609
+ }
610
+
611
+ /**
612
+ * {@inheritDoc ITestObjectProvider.updateDocumentId}
613
+ */
614
+ public updateDocumentId(resolvedUrl: IResolvedUrl | undefined) {
615
+ this._documentIdStrategy.update(resolvedUrl);
616
+ }
617
+
618
+ /**
619
+ * {@inheritDoc ITestObjectProvider.resetLoaderContainerTracker}
620
+ */
621
+ public resetLoaderContainerTracker(syncSummarizerClients: boolean = false) {
622
+ this._loaderContainerTracker.reset();
623
+ this._loaderContainerTracker = new LoaderContainerTracker(syncSummarizerClients);
624
+ }
625
+ }
626
+
627
+ /**
628
+ * Implements {@link ITestObjectProvider}, but uses different versions to create and load containers.
629
+ *
630
+ * @internal
631
+ */
632
+ export class TestObjectProviderWithVersionedLoad implements ITestObjectProvider {
633
+ /**
634
+ * {@inheritDoc ITestObjectProvider."type"}
635
+ */
636
+ public readonly type = "TestObjectProviderWithVersionedLoad";
637
+ private _loaderContainerTracker = new LoaderContainerTracker();
638
+ private _logger: EventAndErrorTrackingLogger | undefined;
639
+ private readonly _documentIdStrategy: IDocumentIdStrategy;
640
+ private _documentServiceFactory: IDocumentServiceFactory | undefined;
641
+ private _urlResolver: IUrlResolver | undefined;
642
+ // Since documentId doesn't change we can only create/make one container. Call the load functions instead.
643
+ private _documentCreated = false;
644
+
645
+ /**
646
+ * `_loadCount` is used to alternate which version we load the next container with.
647
+ * loadCount is even then we will load with the "create" version, and if odd we load with the "load" version.
648
+ * After each test we will reset loadCount to 0 to ensure we always create the first container with the create version.
649
+ *
650
+ * Note: This will only affect tests that load a container more than two times.
651
+ */
652
+
653
+ private _loadCount: number = 0;
654
+
655
+ constructor(
656
+ private readonly LoaderConstructorForCreating: typeof Loader,
657
+ private readonly LoaderConstructorForLoading: typeof Loader,
658
+ private readonly driverForCreating: ITestDriver,
659
+ private readonly driverForLoading: ITestDriver,
660
+ private readonly createFluidEntryPointForCreating: (
661
+ testContainerConfig?: ITestContainerConfig,
662
+ ) => fluidEntryPoint,
663
+ private readonly createFluidEntryPointForLoading: (
664
+ testContainerConfig?: ITestContainerConfig,
665
+ ) => fluidEntryPoint,
666
+ ) {
667
+ this._documentIdStrategy = getDocumentIdStrategy(driverForCreating.type);
668
+ }
669
+
670
+ /**
671
+ * {@inheritDoc ITestObjectProvider.logger}
672
+ */
673
+ public get logger(): EventAndErrorTrackingLogger {
674
+ if (this._logger === undefined) {
675
+ this._logger = new EventAndErrorTrackingLogger(
676
+ createChildLogger({
677
+ logger: getTestLogger?.(),
678
+ }),
679
+ );
680
+ }
681
+ return this._logger;
682
+ }
683
+
684
+ /**
685
+ * {@inheritDoc ITestObjectProvider.documentServiceFactory}
686
+ */
687
+ public get documentServiceFactory() {
688
+ if (!this._documentServiceFactory) {
689
+ this._documentServiceFactory = this.driverForCreating.createDocumentServiceFactory();
690
+ }
691
+ return this._documentServiceFactory;
692
+ }
693
+
694
+ /**
695
+ * {@inheritDoc ITestObjectProvider.urlResolver}
696
+ */
697
+ public get urlResolver() {
698
+ if (!this._urlResolver) {
699
+ this._urlResolver = this.driverForCreating.createUrlResolver();
700
+ }
701
+ return this._urlResolver;
702
+ }
703
+
704
+ /**
705
+ * {@inheritDoc ITestObjectProvider.documentId}
706
+ */
707
+ public get documentId() {
708
+ return this._documentIdStrategy.get();
709
+ }
710
+
711
+ /**
712
+ * {@inheritDoc ITestObjectProvider.defaultCodeDetails}
713
+ */
714
+ public get defaultCodeDetails() {
715
+ return defaultCodeDetails;
716
+ }
717
+
718
+ /**
719
+ * {@inheritDoc ITestObjectProvider.opProcessingController}
720
+ */
721
+ public get opProcessingController(): IOpProcessingController {
722
+ return this._loaderContainerTracker;
723
+ }
724
+
725
+ /**
726
+ * {@inheritDoc ITestObjectProvider.driver}
727
+ */
728
+ public get driver(): ITestDriver {
729
+ return this.nextLoaderShouldCreate() ? this.driverForCreating : this.driverForLoading;
730
+ }
731
+
732
+ /**
733
+ * {@inheritDoc ITestObjectProvider.createFluidEntryPoint}
734
+ */
735
+ public get createFluidEntryPoint(): (
736
+ testContainerConfig?: ITestContainerConfig,
737
+ ) => fluidEntryPoint {
738
+ return this.nextLoaderShouldCreate()
739
+ ? this.createFluidEntryPointForCreating
740
+ : this.createFluidEntryPointForLoading;
741
+ }
742
+
743
+ private createLoaderForCreating(
744
+ packageEntries: Iterable<[IFluidCodeDetails, fluidEntryPoint]>,
745
+ loaderProps?: Partial<ILoaderProps>,
746
+ ) {
747
+ const logger = createMultiSinkLogger({
748
+ loggers: [this.logger, loaderProps?.logger],
749
+ });
750
+
751
+ const loader = new this.LoaderConstructorForCreating({
752
+ ...loaderProps,
753
+ logger,
754
+ codeLoader: loaderProps?.codeLoader ?? new LocalCodeLoader(packageEntries),
755
+ urlResolver: loaderProps?.urlResolver ?? this.urlResolver,
756
+ documentServiceFactory:
757
+ loaderProps?.documentServiceFactory ?? this.documentServiceFactory,
758
+ });
759
+
760
+ this._loaderContainerTracker.add(loader);
761
+ return loader;
762
+ }
763
+
764
+ private createLoaderForLoading(
765
+ packageEntries: Iterable<[IFluidCodeDetails, fluidEntryPoint]>,
766
+ loaderProps?: Partial<ILoaderProps>,
767
+ ) {
768
+ const logger = createMultiSinkLogger({
769
+ loggers: [this.logger, loaderProps?.logger],
770
+ });
771
+
772
+ const loader = new this.LoaderConstructorForLoading({
773
+ ...loaderProps,
774
+ logger,
775
+ codeLoader: loaderProps?.codeLoader ?? new LocalCodeLoader(packageEntries),
776
+ urlResolver: loaderProps?.urlResolver ?? this.urlResolver,
777
+ documentServiceFactory:
778
+ loaderProps?.documentServiceFactory ?? this.documentServiceFactory,
779
+ });
780
+
781
+ this._loaderContainerTracker.add(loader);
782
+ return loader;
783
+ }
784
+
785
+ /**
786
+ * {@inheritDoc ITestObjectProvider.createLoader}
787
+ */
788
+ public createLoader(
789
+ packageEntries: Iterable<[IFluidCodeDetails, fluidEntryPoint]>,
790
+ loaderProps?: Partial<ILoaderProps>,
791
+ ) {
792
+ return this.nextLoaderShouldCreate(/** increment */ true)
793
+ ? this.createLoaderForCreating(packageEntries, loaderProps)
794
+ : this.createLoaderForLoading(packageEntries, loaderProps);
795
+ }
796
+
797
+ /**
798
+ * {@inheritDoc ITestObjectProvider.createContainer}
799
+ */
800
+ public async createContainer(entryPoint: fluidEntryPoint, loaderProps?: Partial<ILoaderProps>) {
801
+ if (this._documentCreated) {
802
+ throw new Error(
803
+ "Only one container/document can be created. To load the container/document use loadContainer",
804
+ );
805
+ }
806
+ const loader = this.createLoader([[defaultCodeDetails, entryPoint]], loaderProps);
807
+ const container = await createAndAttachContainer(
808
+ defaultCodeDetails,
809
+ loader,
810
+ this.driverForCreating.createCreateNewRequest(this.documentId),
811
+ );
812
+ this._documentCreated = true;
813
+ // r11s driver will generate a new ID for the new container.
814
+ // update the document ID with the actual ID of the attached container.
815
+ this._documentIdStrategy.update(container.resolvedUrl);
816
+ return container;
817
+ }
818
+
819
+ /**
820
+ * {@inheritDoc ITestObjectProvider.loadContainer}
821
+ */
822
+ public async loadContainer(
823
+ entryPoint: fluidEntryPoint,
824
+ loaderProps?: Partial<ILoaderProps>,
825
+ requestHeader?: IRequestHeader,
826
+ ): Promise<IContainer> {
827
+ const driver = this.nextLoaderShouldCreate()
828
+ ? this.driverForCreating
829
+ : this.driverForLoading;
830
+ const loader = this.createLoader([[defaultCodeDetails, entryPoint]], loaderProps);
831
+ return this.resolveContainer(loader, requestHeader, driver);
832
+ }
833
+
834
+ private async resolveContainer(
835
+ loader: ILoader,
836
+ headers?: IRequestHeader,
837
+ driver?: ITestDriver,
838
+ ) {
839
+ return loader.resolve({
840
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
841
+ url: await driver!.createContainerUrl(this.documentId),
842
+ headers,
843
+ });
844
+ }
845
+
846
+ /**
847
+ * {@inheritDoc ITestObjectProvider.makeTestLoader}
848
+ */
849
+ public makeTestLoader(testContainerConfig?: ITestContainerConfig) {
850
+ return this.createLoader(
851
+ [[defaultCodeDetails, this.createFluidEntryPoint(testContainerConfig)]],
852
+ testContainerConfig?.loaderProps,
853
+ );
854
+ }
855
+
856
+ /**
857
+ * {@inheritDoc ITestObjectProvider.makeTestContainer}
858
+ */
859
+ public async makeTestContainer(
860
+ testContainerConfig?: ITestContainerConfig,
861
+ ): Promise<IContainer> {
862
+ if (this._documentCreated) {
863
+ throw new Error(
864
+ "Only one container/document can be created. To load the container/document use loadTestContainer",
865
+ );
866
+ }
867
+ const loader = this.createLoader(
868
+ [[defaultCodeDetails, this.createFluidEntryPoint(testContainerConfig)]],
869
+ testContainerConfig?.loaderProps,
870
+ );
871
+ const container = await createAndAttachContainer(
872
+ defaultCodeDetails,
873
+ loader,
874
+ this.driverForCreating.createCreateNewRequest(this.documentId),
875
+ );
876
+ this._documentCreated = true;
877
+ // r11s driver will generate a new ID for the new container.
878
+ // update the document ID with the actual ID of the attached container.
879
+ this._documentIdStrategy.update(container.resolvedUrl);
880
+ return container;
881
+ }
882
+
883
+ /**
884
+ * {@inheritDoc ITestObjectProvider.loadTestContainer}
885
+ */
886
+ public async loadTestContainer(
887
+ testContainerConfig?: ITestContainerConfig,
888
+ requestHeader?: IRequestHeader,
889
+ ): Promise<IContainer> {
890
+ // Keep track of which Loader we are about to use so we can pass the correct driver through
891
+ const driver = this.nextLoaderShouldCreate()
892
+ ? this.driverForCreating
893
+ : this.driverForLoading;
894
+ const loader = this.makeTestLoader(testContainerConfig);
895
+ const container = await this.resolveContainer(loader, requestHeader, driver);
896
+ await this.waitContainerToCatchUp(container);
897
+
898
+ return container;
899
+ }
900
+
901
+ /**
902
+ * {@inheritDoc ITestObjectProvider.reset}
903
+ */
904
+ public reset() {
905
+ this._loadCount = 0;
906
+ this._loaderContainerTracker.reset();
907
+ this._logger = undefined;
908
+ this._documentServiceFactory = undefined;
909
+ this._urlResolver = undefined;
910
+ this._documentIdStrategy.reset();
911
+ const logError = getUnexpectedLogErrorException(this._logger);
912
+ if (logError) {
913
+ throw logError;
914
+ }
915
+ this._documentCreated = false;
916
+ }
917
+
918
+ /**
919
+ * {@inheritDoc ITestObjectProvider.ensureSynchronized}
920
+ */
461
921
  public async ensureSynchronized(): Promise<void> {
462
922
  return this._loaderContainerTracker.ensureSynchronized();
463
923
  }
464
924
 
465
- public async waitContainerToCatchUp(container: IContainer) {
925
+ private async waitContainerToCatchUp(container: IContainer) {
466
926
  // The original waitContainerToCatchUp() from container loader uses either Container.resume()
467
927
  // or Container.connect() as part of its implementation. However, resume() was deprecated
468
928
  // and eventually replaced with connect(). To avoid issues during LTS compatibility testing
@@ -474,16 +934,33 @@ export class TestObjectProvider implements ITestObjectProvider {
474
934
  return waitContainerToCatchUp_original(container);
475
935
  }
476
936
 
477
- updateDocumentId(resolvedUrl: IResolvedUrl | undefined) {
937
+ /**
938
+ * {@inheritDoc ITestObjectProvider.updateDocumentId}
939
+ */
940
+ public updateDocumentId(resolvedUrl: IResolvedUrl | undefined) {
478
941
  this._documentIdStrategy.update(resolvedUrl);
479
942
  }
480
943
 
944
+ /**
945
+ * {@inheritDoc ITestObjectProvider.resetLoaderContainerTracker}
946
+ */
481
947
  public resetLoaderContainerTracker(syncSummarizerClients: boolean = false) {
482
948
  this._loaderContainerTracker.reset();
483
949
  this._loaderContainerTracker = new LoaderContainerTracker(syncSummarizerClients);
484
950
  }
951
+
952
+ private nextLoaderShouldCreate(increment: boolean = false): boolean {
953
+ const shouldCreate = this._loadCount % 2 === 0;
954
+ if (increment) {
955
+ this._loadCount++;
956
+ }
957
+ return shouldCreate;
958
+ }
485
959
  }
486
960
 
961
+ /**
962
+ * @internal
963
+ */
487
964
  export function getUnexpectedLogErrorException(
488
965
  logger: EventAndErrorTrackingLogger | undefined,
489
966
  prefix?: string,