@matter/testing 0.13.0-alpha.0-20250327-8b06e5a79 → 0.13.0-alpha.0-20250328-6dde053de

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 (52) hide show
  1. package/dist/cjs/chip/chip.d.ts +4 -0
  2. package/dist/cjs/chip/chip.d.ts.map +1 -1
  3. package/dist/cjs/chip/chip.js +2 -1
  4. package/dist/cjs/chip/chip.js.map +1 -1
  5. package/dist/cjs/chip/python-test.js +11 -11
  6. package/dist/cjs/chip/python-test.js.map +1 -1
  7. package/dist/cjs/chip/state.d.ts +4 -0
  8. package/dist/cjs/chip/state.d.ts.map +1 -1
  9. package/dist/cjs/chip/state.js +61 -32
  10. package/dist/cjs/chip/state.js.map +1 -1
  11. package/dist/cjs/docker/container.d.ts +1 -0
  12. package/dist/cjs/docker/container.d.ts.map +1 -1
  13. package/dist/cjs/docker/container.js +3 -0
  14. package/dist/cjs/docker/container.js.map +1 -1
  15. package/dist/cjs/mocha.d.ts.map +1 -1
  16. package/dist/cjs/mocha.js +5 -2
  17. package/dist/cjs/mocha.js.map +1 -1
  18. package/dist/cjs/print-report.d.ts +1 -0
  19. package/dist/cjs/print-report.d.ts.map +1 -1
  20. package/dist/cjs/test-descriptor.d.ts +1 -0
  21. package/dist/cjs/test-descriptor.d.ts.map +1 -1
  22. package/dist/cjs/test-descriptor.js.map +1 -1
  23. package/dist/esm/chip/chip.d.ts +4 -0
  24. package/dist/esm/chip/chip.d.ts.map +1 -1
  25. package/dist/esm/chip/chip.js +2 -1
  26. package/dist/esm/chip/chip.js.map +1 -1
  27. package/dist/esm/chip/python-test.js +11 -11
  28. package/dist/esm/chip/python-test.js.map +1 -1
  29. package/dist/esm/chip/state.d.ts +4 -0
  30. package/dist/esm/chip/state.d.ts.map +1 -1
  31. package/dist/esm/chip/state.js +62 -33
  32. package/dist/esm/chip/state.js.map +1 -1
  33. package/dist/esm/docker/container.d.ts +1 -0
  34. package/dist/esm/docker/container.d.ts.map +1 -1
  35. package/dist/esm/docker/container.js +3 -0
  36. package/dist/esm/docker/container.js.map +1 -1
  37. package/dist/esm/mocha.d.ts.map +1 -1
  38. package/dist/esm/mocha.js +5 -2
  39. package/dist/esm/mocha.js.map +1 -1
  40. package/dist/esm/print-report.d.ts +1 -0
  41. package/dist/esm/print-report.d.ts.map +1 -1
  42. package/dist/esm/test-descriptor.d.ts +1 -0
  43. package/dist/esm/test-descriptor.d.ts.map +1 -1
  44. package/dist/esm/test-descriptor.js.map +1 -1
  45. package/package.json +2 -2
  46. package/src/chip/chip.ts +6 -0
  47. package/src/chip/matter-js-pics.properties +7 -1
  48. package/src/chip/python-test.ts +17 -19
  49. package/src/chip/state.ts +72 -33
  50. package/src/docker/container.ts +7 -0
  51. package/src/mocha.ts +6 -2
  52. package/src/test-descriptor.ts +1 -0
@@ -168,27 +168,13 @@ function spiffy(line: string) {
168
168
  * or the test will not run. So we must extract these arguments to pass into the script.
169
169
  *
170
170
  * A program defining mandatory arguments to itself seems silly but we work with what we've got amiright?
171
- *
172
- * We read the entire configuration but all we currently extract are arguments to the first run that aren't
173
- * "boilerplate" arguments that we don't need.
174
- *
175
- * We also use this opportunity to extract PICS which are returned programmatically. So we use a regexp which has high
176
- * "eww" factory, but the alternative would be invoking Python and instantiating, which would not be fast.
177
171
  */
178
172
  async function createCommand(descriptor: TestFileDescriptor, subject: Subject, extraArgs: string[]) {
179
- const { path, config } = descriptor;
180
-
181
- const command = ["python3", path, ...Constants.PythonRunnerArgs];
182
-
183
- if (config) {
184
- let args = config["script-args"] as string;
185
- if (typeof args === "string") {
186
- args = args.replace(
187
- /--(?:storage-path|commissioning-method|discriminator|passcode|trace-to|PICS)\s+\S+\s+/g,
188
- "",
189
- );
190
- command.push(...args.trim().split(/\s+/));
191
- }
173
+ const command = ["python3", descriptor.path, ...Constants.PythonRunnerArgs];
174
+
175
+ const args = scriptArgsOf(descriptor);
176
+ if (args !== undefined) {
177
+ command.push(...args);
192
178
  }
193
179
 
194
180
  command.push(...extraArgs);
@@ -200,3 +186,15 @@ async function createCommand(descriptor: TestFileDescriptor, subject: Subject, e
200
186
 
201
187
  return command;
202
188
  }
189
+
190
+ function scriptArgsOf(descriptor: TestFileDescriptor) {
191
+ const scriptArgs = descriptor.config?.["script-args"];
192
+ if (typeof scriptArgs !== "string") {
193
+ return;
194
+ }
195
+
196
+ return scriptArgs
197
+ .replace(/--(?:storage-path|commissioning-method|discriminator|passcode|trace-to|PICS)\s+\S+\s+/g, "")
198
+ .trim()
199
+ .split(/\s+/);
200
+ }
package/src/chip/state.ts CHANGED
@@ -4,7 +4,7 @@
4
4
  * SPDX-License-Identifier: Apache-2.0
5
5
  */
6
6
 
7
- import { Package } from "#tools";
7
+ import { ansi, Package } from "#tools";
8
8
  import { BackchannelCommand } from "../device/backchannel.js";
9
9
  import { Subject } from "../device/subject.js";
10
10
  import { Test } from "../device/test.js";
@@ -28,13 +28,14 @@ import { YamlTest } from "./yaml-test.js";
28
28
  */
29
29
  const Values = {
30
30
  isInitialized: false,
31
- maybeRunner: undefined as TestRunner | undefined,
32
- maybeMocha: undefined as Mocha | undefined,
33
- maybeSubject: undefined as Subject.Factory | undefined,
34
- maybeTest: undefined as Test | undefined,
35
- maybeContainer: undefined as Container | undefined,
36
- maybePics: undefined as PicsFile | undefined,
37
- maybeTests: undefined as TestDescriptor.Filesystem | undefined,
31
+ runner: undefined as TestRunner | undefined,
32
+ mocha: undefined as Mocha | undefined,
33
+ subject: undefined as Subject.Factory | undefined,
34
+ test: undefined as Test | undefined,
35
+ mainContainer: undefined as Container | undefined,
36
+ mdnsContainer: undefined as Container | undefined,
37
+ pics: undefined as PicsFile | undefined,
38
+ tests: undefined as TestDescriptor.Filesystem | undefined,
38
39
  initializedSubjects: new WeakSet<Subject>(),
39
40
  activeSubject: undefined as Subject | undefined,
40
41
  singleUseSubject: false,
@@ -43,7 +44,7 @@ const Values = {
43
44
  subjects: new Map<Subject.Factory, Record<string, Subject>>(),
44
45
  snapshots: new Map<Subject, {}>(),
45
46
  containerLifecycleInstalled: false,
46
- tests: new Map<TestDescriptor, Test>(),
47
+ testMap: new Map<TestDescriptor, Test>(),
47
48
  };
48
49
 
49
50
  /**
@@ -51,7 +52,7 @@ const Values = {
51
52
  */
52
53
  export const State = {
53
54
  get container() {
54
- const container = Values.maybeContainer;
55
+ const container = Values.mainContainer;
55
56
 
56
57
  if (container === undefined) {
57
58
  throw new Error("Docker container is not initialized");
@@ -61,11 +62,11 @@ export const State = {
61
62
  },
62
63
 
63
64
  set runner(runner: TestRunner) {
64
- Values.maybeRunner = runner;
65
+ Values.runner = runner;
65
66
  },
66
67
 
67
68
  get runner() {
68
- const runner = Values.maybeRunner;
69
+ const runner = Values.runner;
69
70
 
70
71
  if (runner === undefined) {
71
72
  throw new Error("No test runner configured");
@@ -75,11 +76,11 @@ export const State = {
75
76
  },
76
77
 
77
78
  set mocha(mocha: Mocha) {
78
- Values.maybeMocha = mocha;
79
+ Values.mocha = mocha;
79
80
  },
80
81
 
81
82
  get mocha() {
82
- const mocha = Values.maybeMocha;
83
+ const mocha = Values.mocha;
83
84
 
84
85
  if (mocha === undefined) {
85
86
  throw new Error("No mocha instance configured");
@@ -89,11 +90,11 @@ export const State = {
89
90
  },
90
91
 
91
92
  set subject(subject: Subject.Factory) {
92
- Values.maybeSubject = subject;
93
+ Values.subject = subject;
93
94
  },
94
95
 
95
96
  get subject() {
96
- const subject = Values.maybeSubject;
97
+ const subject = Values.subject;
97
98
 
98
99
  if (subject === undefined) {
99
100
  throw new Error("no default subject configured");
@@ -103,26 +104,26 @@ export const State = {
103
104
  },
104
105
 
105
106
  get pics() {
106
- if (Values.maybePics === undefined) {
107
+ if (Values.pics === undefined) {
107
108
  throw new Error("PICS not initialized");
108
109
  }
109
110
 
110
- return Values.maybePics;
111
+ return Values.pics;
111
112
  },
112
113
 
113
114
  get tests() {
114
- if (Values.maybeTests === undefined) {
115
+ if (Values.tests === undefined) {
115
116
  throw new Error("CHIP test descriptor not loaded");
116
117
  }
117
- return Values.maybeTests;
118
+ return Values.tests;
118
119
  },
119
120
 
120
121
  get test() {
121
- if (Values.maybeTest === undefined) {
122
+ if (Values.test === undefined) {
122
123
  throw new Error("No active test");
123
124
  }
124
125
 
125
- return Values.maybeTest;
126
+ return Values.test;
126
127
  },
127
128
 
128
129
  get isInitialized() {
@@ -138,7 +139,23 @@ export const State = {
138
139
  }
139
140
 
140
141
  const { progress } = State.runner;
141
- return await progress.run(`Initializing containers`, initialize);
142
+
143
+ progress.update("Initializing containers");
144
+ try {
145
+ const result = await initialize();
146
+
147
+ const imageVersion = formatSha(await State.container.imageId);
148
+ const chipVersion = formatSha(await State.container.read("/etc/chip-version"));
149
+
150
+ progress.success(
151
+ `Initialized containers from image ${ansi.bold(imageVersion)} for CHIP ${ansi.bold(chipVersion)}`,
152
+ );
153
+
154
+ return result;
155
+ } catch (e) {
156
+ progress.failure("Initializing containers");
157
+ throw e;
158
+ }
142
159
  },
143
160
 
144
161
  /**
@@ -187,11 +204,11 @@ export const State = {
187
204
  const subject = Values.activeSubject!;
188
205
 
189
206
  try {
190
- Values.maybeTest = test;
207
+ Values.test = test;
191
208
  await beforeTest(subject, test);
192
209
  await test.invoke(subject, reporter.beginStep.bind(reporter), args);
193
210
  } finally {
194
- Values.maybeTest = undefined;
211
+ Values.test = undefined;
195
212
  }
196
213
  },
197
214
 
@@ -238,10 +255,10 @@ export const State = {
238
255
  identifier = maybeDescriptor;
239
256
  }
240
257
 
241
- let test = Values.tests.get(identifier);
258
+ let test = Values.testMap.get(identifier);
242
259
  if (!test) {
243
260
  test = createTest(identifier);
244
- Values.tests.set(identifier, test);
261
+ Values.testMap.set(identifier, test);
245
262
  }
246
263
 
247
264
  return test;
@@ -347,6 +364,21 @@ export const State = {
347
364
  Values.activeSubject = undefined;
348
365
  }
349
366
  },
367
+
368
+ /**
369
+ * Clear the MDNS cache.
370
+ */
371
+ async clearMdns() {
372
+ if (!Values.mdnsContainer) {
373
+ throw new Error("Cannot reset MDNS because MDNS container is not initialized");
374
+ }
375
+
376
+ // Active subjects will not be discoverable after we clear DNS
377
+ await this.deactivateSubject();
378
+
379
+ // Clear DNS
380
+ await Values.mdnsContainer.exec("/bin/mdns-clear");
381
+ },
350
382
  };
351
383
 
352
384
  /**
@@ -387,12 +419,12 @@ async function configureContainer() {
387
419
  command: ["/usr/bin/dbus-daemon", "--nopidfile", "--system", "--nofork"],
388
420
  });
389
421
 
390
- await composition.add({
422
+ Values.mdnsContainer = await composition.add({
391
423
  name: "mdns",
392
- command: ["/usr/sbin/avahi-daemon"],
424
+ command: ["/bin/mdns-run"],
393
425
  });
394
426
 
395
- Values.maybeContainer = await composition.add({
427
+ Values.mainContainer = await composition.add({
396
428
  name: "chip",
397
429
  recreate: true,
398
430
  });
@@ -404,7 +436,7 @@ async function configureContainer() {
404
436
  console.error("Error terminating containers:", e);
405
437
  }
406
438
 
407
- Values.maybeContainer = undefined;
439
+ Values.mainContainer = undefined;
408
440
  });
409
441
  }
410
442
 
@@ -419,7 +451,7 @@ async function configurePics() {
419
451
  const overrides = new PicsFile(testing.resolve(Constants.localPicsOverrideFile));
420
452
  pics.patch(overrides);
421
453
 
422
- Values.maybePics = pics;
454
+ Values.pics = pics;
423
455
 
424
456
  await State.container.write(ContainerPaths.matterJsPics, pics.toString());
425
457
  }
@@ -435,7 +467,7 @@ async function configureTests() {
435
467
  if (!Array.isArray(descriptor.members)) {
436
468
  throw new Error(`CHIP test descriptor has no members`);
437
469
  }
438
- Values.maybeTests = TestDescriptor.Filesystem(descriptor);
470
+ Values.tests = TestDescriptor.Filesystem(descriptor);
439
471
  }
440
472
 
441
473
  /**
@@ -528,3 +560,10 @@ function createTest(descriptor: TestDescriptor) {
528
560
  throw new Error(`Cannot implement CHIP test ${descriptor.name} of kind ${descriptor.kind}`);
529
561
  }
530
562
  }
563
+
564
+ function formatSha(sha: string) {
565
+ if (sha.startsWith("sha256:")) {
566
+ sha = sha.substring(7);
567
+ }
568
+ return ansi.bold(sha.substring(0, 12));
569
+ }
@@ -19,6 +19,9 @@ import { Terminal } from "./terminal.js";
19
19
  */
20
20
  export interface Container {
21
21
  docker: Docker;
22
+
23
+ imageId: Promise<string>;
24
+
22
25
  start(): Promise<void>;
23
26
  kill(): Promise<void>;
24
27
  remove(force?: boolean): Promise<void>;
@@ -226,6 +229,10 @@ function adaptContainer(docker: Docker, ct: Dockerode.Container): Container {
226
229
  return {
227
230
  docker,
228
231
 
232
+ get imageId() {
233
+ return ct.inspect().then(info => info.Image);
234
+ },
235
+
229
236
  async start() {
230
237
  await DockerError.adapt(ct.start());
231
238
  },
package/src/mocha.ts CHANGED
@@ -221,7 +221,7 @@ export function adaptReporter(
221
221
  });
222
222
 
223
223
  runner.on(RUNNER.EVENT_TEST_BEGIN, test => {
224
- if (updateStats) {
224
+ if (updateStats && test.descriptor) {
225
225
  test.descriptor.runAt = new Date();
226
226
  }
227
227
  logs = (test as any).logs = [];
@@ -230,13 +230,17 @@ export function adaptReporter(
230
230
 
231
231
  if (updateStats) {
232
232
  runner.on(RUNNER.EVENT_TEST_PASS, test => {
233
+ if (!test.descriptor) {
234
+ return;
235
+ }
236
+
233
237
  test.descriptor.durationMs = test.duration;
234
238
  test.descriptor.passed = true;
235
239
  });
236
240
  }
237
241
 
238
242
  runner.on(RUNNER.EVENT_TEST_FAIL, (test, error) => {
239
- if (updateStats) {
243
+ if (updateStats && test.descriptor) {
240
244
  test.descriptor.durationMs = test.duration;
241
245
  test.descriptor.passed = false;
242
246
  }
@@ -22,6 +22,7 @@ export interface TestDescriptor {
22
22
  description?: string;
23
23
  pics?: string;
24
24
  config?: Record<string, unknown>;
25
+ startUncommissioned?: boolean;
25
26
  runAt?: Date;
26
27
  passed?: boolean;
27
28
  durationMs?: number;