@opensumi/ide-testing 2.21.13 → 2.22.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 (109) hide show
  1. package/lib/browser/components/testing.explorer.tree.d.ts.map +1 -1
  2. package/lib/browser/components/testing.explorer.tree.js +7 -5
  3. package/lib/browser/components/testing.explorer.tree.js.map +1 -1
  4. package/lib/browser/components/testing.view.js +1 -1
  5. package/lib/browser/components/testing.view.js.map +1 -1
  6. package/lib/browser/icons/icons.d.ts +1 -0
  7. package/lib/browser/icons/icons.d.ts.map +1 -1
  8. package/lib/browser/icons/icons.js +15 -14
  9. package/lib/browser/icons/icons.js.map +1 -1
  10. package/lib/browser/index.js.map +1 -1
  11. package/lib/browser/outputPeek/test-output-peek.js +9 -9
  12. package/lib/browser/outputPeek/test-output-peek.js.map +1 -1
  13. package/lib/browser/outputPeek/test-peek-message.service.js.map +1 -1
  14. package/lib/browser/outputPeek/test-peek-opener.service.js +1 -1
  15. package/lib/browser/outputPeek/test-peek-opener.service.js.map +1 -1
  16. package/lib/browser/outputPeek/test-peek-widget.js +1 -1
  17. package/lib/browser/outputPeek/test-peek-widget.js.map +1 -1
  18. package/lib/browser/outputPeek/test-tree-container.js +2 -2
  19. package/lib/browser/outputPeek/test-tree-container.js.map +1 -1
  20. package/lib/browser/test-contextkey.service.js.map +1 -1
  21. package/lib/browser/test-decorations.js +19 -19
  22. package/lib/browser/test-decorations.js.map +1 -1
  23. package/lib/browser/test-profile.service.js.map +1 -1
  24. package/lib/browser/test-tree-view.model.d.ts +1 -0
  25. package/lib/browser/test-tree-view.model.d.ts.map +1 -1
  26. package/lib/browser/test-tree-view.model.js +13 -10
  27. package/lib/browser/test-tree-view.model.js.map +1 -1
  28. package/lib/browser/test.result.service.d.ts +1 -1
  29. package/lib/browser/test.result.service.d.ts.map +1 -1
  30. package/lib/browser/test.result.service.js +5 -5
  31. package/lib/browser/test.result.service.js.map +1 -1
  32. package/lib/browser/test.service.d.ts +4 -0
  33. package/lib/browser/test.service.d.ts.map +1 -1
  34. package/lib/browser/test.service.js +31 -4
  35. package/lib/browser/test.service.js.map +1 -1
  36. package/lib/browser/testing.contribution.d.ts.map +1 -1
  37. package/lib/browser/testing.contribution.js +21 -10
  38. package/lib/browser/testing.contribution.js.map +1 -1
  39. package/lib/common/commands.d.ts +1 -0
  40. package/lib/common/commands.d.ts.map +1 -1
  41. package/lib/common/commands.js +6 -1
  42. package/lib/common/commands.js.map +1 -1
  43. package/lib/common/constants.js +12 -12
  44. package/lib/common/constants.js.map +1 -1
  45. package/lib/common/getComputedState.d.ts +1 -1
  46. package/lib/common/getComputedState.d.ts.map +1 -1
  47. package/lib/common/getComputedState.js +1 -1
  48. package/lib/common/getComputedState.js.map +1 -1
  49. package/lib/common/index.d.ts +5 -0
  50. package/lib/common/index.d.ts.map +1 -1
  51. package/lib/common/index.js.map +1 -1
  52. package/lib/common/observableValue.d.ts +14 -0
  53. package/lib/common/observableValue.d.ts.map +1 -0
  54. package/lib/common/observableValue.js +23 -0
  55. package/lib/common/observableValue.js.map +1 -0
  56. package/lib/common/test-result.d.ts +2 -2
  57. package/lib/common/test-result.d.ts.map +1 -1
  58. package/lib/common/test-result.js +19 -19
  59. package/lib/common/test-result.js.map +1 -1
  60. package/lib/common/testCollection.d.ts +9 -8
  61. package/lib/common/testCollection.d.ts.map +1 -1
  62. package/lib/common/testCollection.js +27 -27
  63. package/lib/common/testCollection.js.map +1 -1
  64. package/lib/common/testId.js +21 -21
  65. package/lib/common/testId.js.map +1 -1
  66. package/lib/common/testingStates.js +11 -11
  67. package/lib/common/testingStates.js.map +1 -1
  68. package/lib/common/testingUri.d.ts +1 -1
  69. package/lib/common/testingUri.d.ts.map +1 -1
  70. package/lib/common/testingUri.js +16 -16
  71. package/lib/common/testingUri.js.map +1 -1
  72. package/package.json +14 -13
  73. package/src/browser/components/testing.explorer.tree.tsx +129 -0
  74. package/src/browser/components/testing.module.less +26 -0
  75. package/src/browser/components/testing.view.tsx +35 -0
  76. package/src/browser/icons/icons.less +31 -0
  77. package/src/browser/icons/icons.ts +31 -0
  78. package/src/browser/index.ts +51 -0
  79. package/src/browser/outputPeek/test-message-container.tsx +183 -0
  80. package/src/browser/outputPeek/test-output-peek.ts +282 -0
  81. package/src/browser/outputPeek/test-peek-message.service.ts +15 -0
  82. package/src/browser/outputPeek/test-peek-opener.service.ts +131 -0
  83. package/src/browser/outputPeek/test-peek-widget.less +86 -0
  84. package/src/browser/outputPeek/test-peek-widget.tsx +127 -0
  85. package/src/browser/outputPeek/test-tree-container.tsx +171 -0
  86. package/src/browser/test-contextkey.service.ts +24 -0
  87. package/src/browser/test-decorations.ts +567 -0
  88. package/src/browser/test-profile.service.ts +66 -0
  89. package/src/browser/test-tree-view.model.ts +304 -0
  90. package/src/browser/test.result.service.ts +193 -0
  91. package/src/browser/test.service.ts +190 -0
  92. package/src/browser/testing.contribution.ts +471 -0
  93. package/src/browser/theme.less +6 -0
  94. package/src/common/commands.ts +78 -0
  95. package/src/common/constants.ts +57 -0
  96. package/src/common/contextKeys.ts +0 -0
  97. package/src/common/getComputedState.ts +136 -0
  98. package/src/common/index.ts +55 -0
  99. package/src/common/observableValue.ts +27 -0
  100. package/src/common/test-profile.ts +25 -0
  101. package/src/common/test-result.ts +328 -0
  102. package/src/common/testCollection.ts +680 -0
  103. package/src/common/testId.ts +191 -0
  104. package/src/common/testing-view.ts +28 -0
  105. package/src/common/testingPeekOpener.ts +31 -0
  106. package/src/common/testingStates.ts +92 -0
  107. package/src/common/testingUri.ts +101 -0
  108. package/src/common/tree-view.model.ts +41 -0
  109. package/src/index.ts +1 -0
@@ -0,0 +1,680 @@
1
+ /* ---------------------------------------------------------------------------------------------
2
+ * Copyright (c) Microsoft Corporation. All rights reserved.
3
+ * Licensed under the MIT License. See License.txt in the project root for license information.
4
+ *--------------------------------------------------------------------------------------------*/
5
+ // Some code copied and modified from https://github.com/microsoft/vscode/tree/main/src/vs/workbench/contrib/testing/common
6
+
7
+ import { Uri as URI, IRange, IPosition, Emitter, map, IMarkdownString } from '@opensumi/ide-core-common';
8
+
9
+ export type TestsDiffOp =
10
+ | [op: TestDiffOpType.Add, item: InternalTestItem]
11
+ | [op: TestDiffOpType.Update, item: ITestItemUpdate]
12
+ | [op: TestDiffOpType.Remove, itemId: string]
13
+ | [op: TestDiffOpType.Retire, itemId: string]
14
+ | [op: TestDiffOpType.IncrementPendingExtHosts, amount: number]
15
+ | [op: TestDiffOpType.AddTag, tag: ITestTagDisplayInfo]
16
+ | [op: TestDiffOpType.RemoveTag, id: string];
17
+
18
+ /**
19
+ * Request from the ext host or main thread to indicate that tests have
20
+ * changed. It's assumed that any item upserted *must* have its children
21
+ * previously also upserted, or upserted as part of the same operation.
22
+ * Children that no longer exist in an upserted item will be removed.
23
+ */
24
+ export type TestsDiff = TestsDiffOp[];
25
+
26
+ /**
27
+ * TestItem-like shape, butm with an ID and children as strings.
28
+ */
29
+ export interface InternalTestItem {
30
+ /** Controller ID from whence this test came */
31
+ controllerId: string;
32
+ /** Expandability state */
33
+ expand: TestItemExpandState;
34
+ /** Parent ID, if any */
35
+ parent: string | null;
36
+ /** Raw test item properties */
37
+ item: ITestItem;
38
+ }
39
+
40
+ export const enum TestDiffOpType {
41
+ /** Adds a new test (with children) */
42
+ Add,
43
+ /** Shallow-updates an existing test */
44
+ Update,
45
+ /** Removes a test (and all its children) */
46
+ Remove,
47
+ /** Changes the number of controllers who are yet to publish their collection roots. */
48
+ IncrementPendingExtHosts,
49
+ /** Retires a test/result */
50
+ Retire,
51
+ /** Add a new test tag */
52
+ AddTag,
53
+ /** Remove a test tag */
54
+ RemoveTag,
55
+ }
56
+
57
+ export const enum TestResultState {
58
+ Unset = 0,
59
+ Queued = 1,
60
+ Running = 2,
61
+ Passed = 3,
62
+ Failed = 4,
63
+ Skipped = 5,
64
+ Errored = 6,
65
+ }
66
+
67
+ export const enum TestRunProfileBitset {
68
+ Run = 1 << 1,
69
+ Debug = 1 << 2,
70
+ Coverage = 1 << 3,
71
+ HasNonDefaultProfile = 1 << 4,
72
+ HasConfigurable = 1 << 5,
73
+ }
74
+
75
+ /**
76
+ * List of all test run profile bitset values.
77
+ */
78
+ export const testRunProfileBitsetList = [
79
+ TestRunProfileBitset.Run,
80
+ TestRunProfileBitset.Debug,
81
+ TestRunProfileBitset.Coverage,
82
+ TestRunProfileBitset.HasNonDefaultProfile,
83
+ ];
84
+
85
+ /**
86
+ * DTO for a controller's run profiles.
87
+ */
88
+ export interface ITestRunProfile {
89
+ controllerId: string;
90
+ profileId: number;
91
+ label: string;
92
+ group: TestRunProfileBitset;
93
+ isDefault: boolean;
94
+ tag: string | null;
95
+ hasConfigurationHandler: boolean;
96
+ }
97
+
98
+ /**
99
+ * A fully-resolved request to run tests, passsed between the main thread
100
+ * and extension host.
101
+ */
102
+ export interface ResolvedTestRunRequest {
103
+ targets: {
104
+ testIds: string[];
105
+ controllerId: string;
106
+ profileGroup: TestRunProfileBitset;
107
+ profileId: number;
108
+ }[];
109
+ exclude?: string[];
110
+ isAutoRun?: boolean;
111
+ }
112
+
113
+ /**
114
+ * Request to the main thread to run a set of tests.
115
+ */
116
+ export interface ExtensionRunTestsRequest {
117
+ id: string;
118
+ include: string[];
119
+ exclude: string[];
120
+ controllerId: string;
121
+ profile?: { group: TestRunProfileBitset; id: number };
122
+ persist: boolean;
123
+ }
124
+
125
+ /**
126
+ * Request from the main thread to run tests for a single controller.
127
+ */
128
+ export interface RunTestForControllerRequest {
129
+ runId: string;
130
+ controllerId: string;
131
+ profileId: number;
132
+ excludeExtIds: string[];
133
+ testIds: string[];
134
+ }
135
+
136
+ /**
137
+ * Location with a fully-instantiated Range and URI.
138
+ */
139
+ export interface IRichLocation {
140
+ range: IRange;
141
+ uri: URI;
142
+ }
143
+
144
+ export const enum TestMessageType {
145
+ Error,
146
+ Info,
147
+ }
148
+
149
+ export interface ILocationDto {
150
+ uri: URI;
151
+ range: IRange;
152
+ }
153
+
154
+ export interface ITestErrorMessage {
155
+ message: string | IMarkdownString;
156
+ type: TestMessageType.Error;
157
+ expected: string | undefined;
158
+ actual: string | undefined;
159
+ location: IRichLocation | undefined;
160
+ }
161
+
162
+ export type SerializedTestErrorMessage = Omit<ITestErrorMessage, 'location'> & { location?: ILocationDto };
163
+
164
+ export interface ITestOutputMessage {
165
+ message: string;
166
+ type: TestMessageType.Info;
167
+ offset: number;
168
+ location: IRichLocation | undefined;
169
+ }
170
+
171
+ export type SerializedTestOutputMessage = Omit<ITestOutputMessage, 'location'> & { location?: ILocationDto };
172
+
173
+ export type SerializedTestMessage = SerializedTestErrorMessage | SerializedTestOutputMessage;
174
+
175
+ export type ITestMessage = ITestErrorMessage | ITestOutputMessage;
176
+
177
+ export interface ITestTaskState {
178
+ state: TestResultState;
179
+ duration: number | undefined;
180
+ messages: ITestMessage[];
181
+ }
182
+
183
+ export interface ITestRunTask {
184
+ id: string;
185
+ name: string | undefined;
186
+ running: boolean;
187
+ }
188
+
189
+ export interface ITestTag {
190
+ id: string;
191
+ }
192
+
193
+ export interface ITestTagDisplayInfo {
194
+ id: string;
195
+ ctrlLabel: string;
196
+ }
197
+
198
+ /**
199
+ * A partial update made to an existing InternalTestItem.
200
+ */
201
+ export interface ITestItemUpdate {
202
+ extId: string;
203
+ expand?: TestItemExpandState;
204
+ item?: Partial<ITestItem>;
205
+ }
206
+
207
+ /**
208
+ * The TestItem from .d.ts, as a plain object without children.
209
+ */
210
+ export interface ITestItem {
211
+ /** ID of the test given by the test controller */
212
+ extId: string;
213
+ label: string;
214
+ tags: string[];
215
+ busy?: boolean;
216
+ children?: never;
217
+ uri?: URI;
218
+ range: IRange | null;
219
+ description: string | null;
220
+ error: string | IMarkdownString | null;
221
+ sortText: string | null;
222
+ }
223
+
224
+ export const enum TestItemExpandState {
225
+ NotExpandable,
226
+ Expandable,
227
+ BusyExpanding,
228
+ Expanded,
229
+ }
230
+
231
+ export const applyTestItemUpdate = (internal: InternalTestItem | ITestItemUpdate, patch: ITestItemUpdate) => {
232
+ if (patch.expand !== undefined) {
233
+ internal.expand = patch.expand;
234
+ }
235
+ if (patch.item !== undefined) {
236
+ internal.item = internal.item ? Object.assign(internal.item, patch.item) : patch.item;
237
+ }
238
+ };
239
+
240
+ /**
241
+ * Test result item used in the main thread.
242
+ */
243
+ export interface TestResultItem extends InternalTestItem {
244
+ /** State of this test in various tasks */
245
+ tasks: ITestTaskState[];
246
+ /** State of this test as a computation of its tasks */
247
+ ownComputedState: TestResultState;
248
+ /** Computed state based on children */
249
+ computedState: TestResultState;
250
+ /** True if the test is outdated */
251
+ retired: boolean;
252
+ /** Max duration of the item's tasks (if run directly) */
253
+ ownDuration?: number;
254
+ }
255
+
256
+ export type SerializedTestResultItem = Omit<TestResultItem, 'children' | 'expandable' | 'retired'> & {
257
+ children: string[];
258
+ retired: undefined;
259
+ };
260
+
261
+ /**
262
+ * Test results serialized for transport and storage.
263
+ */
264
+ export interface ISerializedTestResults {
265
+ /** ID of these test results */
266
+ id: string;
267
+ /** Time the results were compelted */
268
+ completedAt: number;
269
+ /** Subset of test result items */
270
+ items: SerializedTestResultItem[];
271
+ /** Tasks involved in the run. */
272
+ tasks: { id: string; name: string | undefined; messages: ITestOutputMessage[] }[];
273
+ /** Human-readable name of the test run. */
274
+ name: string;
275
+ /** Test trigger informaton */
276
+ request: ResolvedTestRunRequest;
277
+ }
278
+
279
+ export interface ITestCoverage {
280
+ files: IFileCoverage[];
281
+ }
282
+
283
+ export interface ICoveredCount {
284
+ covered: number;
285
+ total: number;
286
+ }
287
+
288
+ export interface IFileCoverage {
289
+ uri: URI;
290
+ statement: ICoveredCount;
291
+ branch?: ICoveredCount;
292
+ function?: ICoveredCount;
293
+ details?: CoverageDetails[];
294
+ }
295
+
296
+ export const enum DetailType {
297
+ Function,
298
+ Statement,
299
+ }
300
+
301
+ export type CoverageDetails = IFunctionCoverage | IStatementCoverage;
302
+
303
+ export interface IBranchCoverage {
304
+ count: number;
305
+ location?: IRange | IPosition;
306
+ }
307
+
308
+ export interface IFunctionCoverage {
309
+ type: DetailType.Function;
310
+ count: number;
311
+ location?: IRange | IPosition;
312
+ }
313
+
314
+ export interface IStatementCoverage {
315
+ type: DetailType.Statement;
316
+ count: number;
317
+ location: IRange | IPosition;
318
+ branches?: IBranchCoverage[];
319
+ }
320
+
321
+ /**
322
+ * Context for actions taken in the test explorer view.
323
+ */
324
+ export interface ITestItemContext {
325
+ /** Marshalling marker */
326
+ $mid: 13 /** TestItemContext */;
327
+ /** Tests and parents from the root to the current items */
328
+ tests: InternalTestItem[];
329
+ }
330
+
331
+ /**
332
+ * @private
333
+ */
334
+ export interface IncrementalTestCollectionItem extends InternalTestItem {
335
+ children: Set<string>;
336
+ }
337
+
338
+ /**
339
+ * The IncrementalChangeCollector is used in the IncrementalTestCollection
340
+ * and called with diff changes as they're applied. This is used in the
341
+ * ext host to create a cohesive change event from a diff.
342
+ */
343
+ export class IncrementalChangeCollector<T> {
344
+ /**
345
+ * A node was added.
346
+ */
347
+ public add(node: T): void {}
348
+
349
+ /**
350
+ * A node in the collection was updated.
351
+ */
352
+ public update(node: T): void {}
353
+
354
+ /**
355
+ * A node was removed.
356
+ */
357
+ public remove(node: T, isNestedOperation: boolean): void {}
358
+
359
+ /**
360
+ * Called when the diff has been applied.
361
+ */
362
+ public complete(): void {}
363
+ }
364
+
365
+ /**
366
+ * Maintains tests in this extension host sent from the main thread.
367
+ */
368
+ export abstract class AbstractIncrementalTestCollection<T extends IncrementalTestCollectionItem> {
369
+ private readonly _tags = new Map<string, ITestTagDisplayInfo>();
370
+
371
+ /**
372
+ * Map of item IDs to test item objects.
373
+ */
374
+ protected readonly items = new Map<string, T>();
375
+
376
+ /**
377
+ * ID of test root items.
378
+ */
379
+ protected readonly roots = new Set<T>();
380
+
381
+ /**
382
+ * Number of 'busy' controllers.
383
+ */
384
+ protected busyControllerCount = 0;
385
+
386
+ /**
387
+ * Number of pending roots.
388
+ */
389
+ protected pendingRootCount = 0;
390
+
391
+ /**
392
+ * Known test tags.
393
+ */
394
+ public readonly tags: ReadonlyMap<string, ITestTagDisplayInfo> = this._tags;
395
+
396
+ /**
397
+ * Applies the diff to the collection.
398
+ */
399
+ public apply(diff: TestsDiff) {
400
+ const changes = this.createChangeCollector();
401
+
402
+ for (const op of diff) {
403
+ switch (op[0]) {
404
+ case TestDiffOpType.Add: {
405
+ const internalTest = op[1];
406
+ if (!internalTest.parent) {
407
+ const created = this.createItem(internalTest);
408
+ this.roots.add(created);
409
+ this.items.set(internalTest.item.extId, created);
410
+ changes.add(created);
411
+ } else if (this.items.has(internalTest.parent)) {
412
+ const parent = this.items.get(internalTest.parent)!;
413
+ parent.children.add(internalTest.item.extId);
414
+ const created = this.createItem(internalTest, parent);
415
+ this.items.set(internalTest.item.extId, created);
416
+ changes.add(created);
417
+ }
418
+
419
+ if (internalTest.expand === TestItemExpandState.BusyExpanding) {
420
+ this.busyControllerCount++;
421
+ }
422
+ break;
423
+ }
424
+
425
+ case TestDiffOpType.Update: {
426
+ const patch = op[1];
427
+ const existing = this.items.get(patch.extId);
428
+ if (!existing) {
429
+ break;
430
+ }
431
+
432
+ if (patch.expand !== undefined) {
433
+ if (existing.expand === TestItemExpandState.BusyExpanding) {
434
+ this.busyControllerCount--;
435
+ }
436
+ if (patch.expand === TestItemExpandState.BusyExpanding) {
437
+ this.busyControllerCount++;
438
+ }
439
+ }
440
+
441
+ applyTestItemUpdate(existing, patch);
442
+ changes.update(existing);
443
+ break;
444
+ }
445
+
446
+ case TestDiffOpType.Remove: {
447
+ const toRemove = this.items.get(op[1]);
448
+ if (!toRemove) {
449
+ break;
450
+ }
451
+
452
+ if (toRemove.parent) {
453
+ const parent = this.items.get(toRemove.parent)!;
454
+ parent.children.delete(toRemove.item.extId);
455
+ } else {
456
+ this.roots.delete(toRemove);
457
+ }
458
+
459
+ const queue: Iterable<string>[] = [[op[1]]];
460
+ while (queue.length) {
461
+ for (const itemId of queue.pop()!) {
462
+ const existing = this.items.get(itemId);
463
+ if (existing) {
464
+ queue.push(existing.children);
465
+ this.items.delete(itemId);
466
+ changes.remove(existing, existing !== toRemove);
467
+
468
+ if (existing.expand === TestItemExpandState.BusyExpanding) {
469
+ this.busyControllerCount--;
470
+ }
471
+ }
472
+ }
473
+ }
474
+ break;
475
+ }
476
+
477
+ case TestDiffOpType.Retire:
478
+ this.retireTest(op[1]);
479
+ break;
480
+
481
+ case TestDiffOpType.IncrementPendingExtHosts:
482
+ this.updatePendingRoots(op[1]);
483
+ break;
484
+
485
+ case TestDiffOpType.AddTag:
486
+ this._tags.set(op[1].id, op[1]);
487
+ break;
488
+
489
+ case TestDiffOpType.RemoveTag:
490
+ this._tags.delete(op[1]);
491
+ break;
492
+ }
493
+ }
494
+
495
+ changes.complete();
496
+ }
497
+
498
+ /**
499
+ * Called when the extension signals a test result should be retired.
500
+ */
501
+ protected retireTest(testId: string) {
502
+ // no-op
503
+ }
504
+
505
+ /**
506
+ * Updates the number of test root sources who are yet to report. When
507
+ * the total pending test roots reaches 0, the roots for all controllers
508
+ * will exist in the collection.
509
+ */
510
+ public updatePendingRoots(delta: number) {
511
+ this.pendingRootCount += delta;
512
+ }
513
+
514
+ /**
515
+ * Called before a diff is applied to create a new change collector.
516
+ */
517
+ protected createChangeCollector() {
518
+ return new IncrementalChangeCollector<T>();
519
+ }
520
+
521
+ /**
522
+ * Creates a new item for the collection from the internal test item.
523
+ */
524
+ protected abstract createItem(internal: InternalTestItem, parent?: T): T;
525
+ }
526
+
527
+ export class MainThreadTestCollection extends AbstractIncrementalTestCollection<IncrementalTestCollectionItem> {
528
+ private busyProvidersChangeEmitter = new Emitter<number>();
529
+ private retireTestEmitter = new Emitter<string>();
530
+ private expandPromises = new WeakMap<
531
+ IncrementalTestCollectionItem,
532
+ {
533
+ pendingLvl: number;
534
+ doneLvl: number;
535
+ prom: Promise<void>;
536
+ }
537
+ >();
538
+
539
+ /**
540
+ * @inheritdoc
541
+ */
542
+ public get busyProviders() {
543
+ return this.busyControllerCount;
544
+ }
545
+
546
+ /**
547
+ * @inheritdoc
548
+ */
549
+ public get rootItems() {
550
+ return this.roots;
551
+ }
552
+
553
+ /**
554
+ * @inheritdoc
555
+ */
556
+ public get all() {
557
+ return this.getIterator();
558
+ }
559
+
560
+ public get rootIds() {
561
+ return map(this.roots.values(), (r) => r.item.extId);
562
+ }
563
+
564
+ public readonly onBusyProvidersChange = this.busyProvidersChangeEmitter.event;
565
+ public readonly onDidRetireTest = this.retireTestEmitter.event;
566
+
567
+ constructor(private readonly expandActual: (id: string, levels: number) => Promise<void>) {
568
+ super();
569
+ }
570
+
571
+ /**
572
+ * @inheritdoc
573
+ */
574
+ public expand(testId: string, levels: number): Promise<void> {
575
+ const test = this.items.get(testId);
576
+ if (!test) {
577
+ return Promise.resolve();
578
+ }
579
+
580
+ // simple cache to avoid duplicate/unnecessary expansion calls
581
+ const existing = this.expandPromises.get(test);
582
+ if (existing && existing.pendingLvl >= levels) {
583
+ return existing.prom;
584
+ }
585
+
586
+ const prom = this.expandActual(test.item.extId, levels);
587
+ const record = { doneLvl: existing ? existing.doneLvl : -1, pendingLvl: levels, prom };
588
+ this.expandPromises.set(test, record);
589
+
590
+ return prom.then(() => {
591
+ record.doneLvl = levels;
592
+ });
593
+ }
594
+
595
+ /**
596
+ * @inheritdoc
597
+ */
598
+ public getNodeById(id: string) {
599
+ return this.items.get(id);
600
+ }
601
+
602
+ /**
603
+ * @inheritdoc
604
+ */
605
+ public getReviverDiff() {
606
+ const ops: TestsDiff = [[TestDiffOpType.IncrementPendingExtHosts, this.pendingRootCount]];
607
+
608
+ const queue = [this.rootIds];
609
+ while (queue.length) {
610
+ for (const child of queue.pop()!) {
611
+ const item = this.items.get(child)!;
612
+ ops.push([
613
+ TestDiffOpType.Add,
614
+ {
615
+ controllerId: item.controllerId,
616
+ expand: item.expand,
617
+ item: item.item,
618
+ parent: item.parent,
619
+ },
620
+ ]);
621
+ queue.push(item.children);
622
+ }
623
+ }
624
+
625
+ return ops;
626
+ }
627
+
628
+ /**
629
+ * Applies the diff to the collection.
630
+ */
631
+ public override apply(diff: TestsDiff) {
632
+ const prevBusy = this.busyControllerCount;
633
+ super.apply(diff);
634
+
635
+ if (prevBusy !== this.busyControllerCount) {
636
+ this.busyProvidersChangeEmitter.fire(this.busyControllerCount);
637
+ }
638
+ }
639
+
640
+ /**
641
+ * Clears everything from the collection, and returns a diff that applies
642
+ * that action.
643
+ */
644
+ public clear() {
645
+ const ops: TestsDiff = [];
646
+ for (const root of this.roots) {
647
+ ops.push([TestDiffOpType.Remove, root.item.extId]);
648
+ }
649
+
650
+ this.roots.clear();
651
+ this.items.clear();
652
+
653
+ return ops;
654
+ }
655
+
656
+ /**
657
+ * @override
658
+ */
659
+ protected createItem(internal: InternalTestItem): IncrementalTestCollectionItem {
660
+ return { ...internal, children: new Set() };
661
+ }
662
+
663
+ /**
664
+ * @override
665
+ */
666
+ protected override retireTest(testId: string) {
667
+ this.retireTestEmitter.fire(testId);
668
+ }
669
+
670
+ private *getIterator() {
671
+ const queue = [this.rootIds];
672
+ while (queue.length) {
673
+ for (const id of queue.pop()!) {
674
+ const node = this.getNodeById(id)!;
675
+ yield node;
676
+ queue.push(node.children);
677
+ }
678
+ }
679
+ }
680
+ }