@matter/testing 0.13.1-alpha.0-20250501-80c86b03e → 0.13.1-alpha.0-20250504-87f265a2e
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/bin/test.js +1 -2
- package/dist/cjs/chip/chip.d.ts +7 -2
- package/dist/cjs/chip/chip.d.ts.map +1 -1
- package/dist/cjs/chip/chip.js +20 -15
- package/dist/cjs/chip/chip.js.map +1 -1
- package/dist/cjs/chip/command-pipe.d.ts +2 -1
- package/dist/cjs/chip/command-pipe.d.ts.map +1 -1
- package/dist/cjs/chip/command-pipe.js +5 -2
- package/dist/cjs/chip/command-pipe.js.map +1 -1
- package/dist/cjs/chip/config.d.ts +2 -2
- package/dist/cjs/chip/config.d.ts.map +1 -1
- package/dist/cjs/chip/config.js +7 -6
- package/dist/cjs/chip/config.js.map +1 -1
- package/dist/cjs/chip/container-command-pipe.d.ts +2 -1
- package/dist/cjs/chip/container-command-pipe.d.ts.map +1 -1
- package/dist/cjs/chip/container-command-pipe.js +26 -24
- package/dist/cjs/chip/container-command-pipe.js.map +1 -1
- package/dist/cjs/chip/index.d.ts +1 -1
- package/dist/cjs/chip/index.d.ts.map +1 -1
- package/dist/cjs/chip/index.js +1 -1
- package/dist/cjs/chip/index.js.map +1 -1
- package/dist/cjs/chip/{pics-expression.d.ts → pics/expression.d.ts} +2 -2
- package/dist/cjs/chip/pics/expression.d.ts.map +1 -0
- package/dist/cjs/chip/{pics-expression.js → pics/expression.js} +6 -6
- package/dist/cjs/chip/pics/expression.js.map +6 -0
- package/dist/cjs/chip/{pics-file.d.ts → pics/file.d.ts} +10 -5
- package/dist/cjs/chip/pics/file.d.ts.map +1 -0
- package/dist/cjs/chip/{pics-file.js → pics/file.js} +21 -13
- package/dist/cjs/chip/pics/file.js.map +6 -0
- package/dist/cjs/chip/pics/index.d.ts +10 -0
- package/dist/cjs/chip/pics/index.d.ts.map +1 -0
- package/dist/cjs/chip/pics/index.js +27 -0
- package/dist/cjs/chip/pics/index.js.map +6 -0
- package/dist/cjs/chip/pics/source.d.ts +52 -0
- package/dist/cjs/chip/pics/source.d.ts.map +1 -0
- package/dist/cjs/chip/pics/source.js +117 -0
- package/dist/cjs/chip/pics/source.js.map +6 -0
- package/dist/cjs/chip/pics/values.d.ts +9 -0
- package/dist/cjs/chip/pics/values.d.ts.map +1 -0
- package/dist/cjs/chip/pics/values.js +22 -0
- package/dist/cjs/chip/pics/values.js.map +6 -0
- package/dist/cjs/chip/python-test.d.ts.map +1 -1
- package/dist/cjs/chip/python-test.js +14 -7
- package/dist/cjs/chip/python-test.js.map +1 -1
- package/dist/cjs/chip/state.d.ts +3 -2
- package/dist/cjs/chip/state.d.ts.map +1 -1
- package/dist/cjs/chip/state.js +38 -25
- package/dist/cjs/chip/state.js.map +1 -1
- package/dist/cjs/chip/yaml-test.d.ts +1 -1
- package/dist/cjs/chip/yaml-test.d.ts.map +1 -1
- package/dist/cjs/chip/yaml-test.js +5 -1
- package/dist/cjs/chip/yaml-test.js.map +1 -1
- package/dist/cjs/cli.d.ts.map +1 -1
- package/dist/cjs/cli.js +18 -3
- package/dist/cjs/cli.js.map +2 -2
- package/dist/cjs/device/subject.d.ts +4 -0
- package/dist/cjs/device/subject.d.ts.map +1 -1
- package/dist/cjs/failure-reporter.js +4 -6
- package/dist/cjs/failure-reporter.js.map +1 -1
- package/dist/cjs/print-report.d.ts +3 -0
- package/dist/cjs/print-report.d.ts.map +1 -1
- package/dist/cjs/print-report.js +13 -16
- package/dist/cjs/print-report.js.map +2 -2
- package/dist/cjs/test-descriptor.d.ts +18 -13
- package/dist/cjs/test-descriptor.d.ts.map +1 -1
- package/dist/cjs/test-descriptor.js +48 -49
- package/dist/cjs/test-descriptor.js.map +2 -2
- package/dist/esm/chip/chip.d.ts +7 -2
- package/dist/esm/chip/chip.d.ts.map +1 -1
- package/dist/esm/chip/chip.js +20 -15
- package/dist/esm/chip/chip.js.map +1 -1
- package/dist/esm/chip/command-pipe.d.ts +2 -1
- package/dist/esm/chip/command-pipe.d.ts.map +1 -1
- package/dist/esm/chip/command-pipe.js +5 -2
- package/dist/esm/chip/command-pipe.js.map +1 -1
- package/dist/esm/chip/config.d.ts +2 -2
- package/dist/esm/chip/config.d.ts.map +1 -1
- package/dist/esm/chip/config.js +7 -6
- package/dist/esm/chip/config.js.map +1 -1
- package/dist/esm/chip/container-command-pipe.d.ts +2 -1
- package/dist/esm/chip/container-command-pipe.d.ts.map +1 -1
- package/dist/esm/chip/container-command-pipe.js +26 -24
- package/dist/esm/chip/container-command-pipe.js.map +1 -1
- package/dist/esm/chip/index.d.ts +1 -1
- package/dist/esm/chip/index.d.ts.map +1 -1
- package/dist/esm/chip/index.js +1 -1
- package/dist/esm/chip/{pics-expression.d.ts → pics/expression.d.ts} +2 -2
- package/dist/esm/chip/pics/expression.d.ts.map +1 -0
- package/dist/esm/chip/{pics-expression.js → pics/expression.js} +3 -3
- package/dist/esm/chip/pics/expression.js.map +6 -0
- package/dist/esm/chip/{pics-file.d.ts → pics/file.d.ts} +10 -5
- package/dist/esm/chip/pics/file.d.ts.map +1 -0
- package/dist/esm/chip/{pics-file.js → pics/file.js} +18 -10
- package/dist/esm/chip/pics/file.js.map +6 -0
- package/dist/esm/chip/pics/index.d.ts +10 -0
- package/dist/esm/chip/pics/index.d.ts.map +1 -0
- package/dist/esm/chip/pics/index.js +10 -0
- package/dist/esm/chip/pics/index.js.map +6 -0
- package/dist/esm/chip/pics/source.d.ts +52 -0
- package/dist/esm/chip/pics/source.d.ts.map +1 -0
- package/dist/esm/chip/pics/source.js +97 -0
- package/dist/esm/chip/pics/source.js.map +6 -0
- package/dist/esm/chip/pics/values.d.ts +9 -0
- package/dist/esm/chip/pics/values.d.ts.map +1 -0
- package/dist/esm/chip/pics/values.js +6 -0
- package/dist/esm/chip/pics/values.js.map +6 -0
- package/dist/esm/chip/python-test.d.ts.map +1 -1
- package/dist/esm/chip/python-test.js +14 -7
- package/dist/esm/chip/python-test.js.map +1 -1
- package/dist/esm/chip/state.d.ts +3 -2
- package/dist/esm/chip/state.d.ts.map +1 -1
- package/dist/esm/chip/state.js +39 -26
- package/dist/esm/chip/state.js.map +1 -1
- package/dist/esm/chip/yaml-test.d.ts +1 -1
- package/dist/esm/chip/yaml-test.d.ts.map +1 -1
- package/dist/esm/chip/yaml-test.js +5 -1
- package/dist/esm/chip/yaml-test.js.map +1 -1
- package/dist/esm/cli.d.ts.map +1 -1
- package/dist/esm/cli.js +19 -4
- package/dist/esm/cli.js.map +2 -2
- package/dist/esm/device/subject.d.ts +4 -0
- package/dist/esm/device/subject.d.ts.map +1 -1
- package/dist/esm/failure-reporter.js +4 -6
- package/dist/esm/failure-reporter.js.map +1 -1
- package/dist/esm/print-report.d.ts +3 -0
- package/dist/esm/print-report.d.ts.map +1 -1
- package/dist/esm/print-report.js +12 -15
- package/dist/esm/print-report.js.map +2 -2
- package/dist/esm/test-descriptor.d.ts +18 -13
- package/dist/esm/test-descriptor.d.ts.map +1 -1
- package/dist/esm/test-descriptor.js +48 -49
- package/dist/esm/test-descriptor.js.map +2 -2
- package/package.json +2 -2
- package/src/chip/chip.ts +31 -17
- package/src/chip/command-pipe.ts +6 -2
- package/src/chip/config.ts +9 -9
- package/src/chip/container-command-pipe.ts +28 -30
- package/src/chip/index.ts +1 -1
- package/src/chip/matter-js-pics.properties +7 -4
- package/src/chip/{pics-expression.ts → pics/expression.ts} +4 -3
- package/src/chip/{pics-file.ts → pics/file.ts} +30 -13
- package/src/chip/pics/index.ts +10 -0
- package/src/chip/pics/source.ts +163 -0
- package/src/chip/pics/values.ts +9 -0
- package/src/chip/python-test.ts +18 -11
- package/src/chip/state.ts +56 -33
- package/src/chip/yaml-test.ts +6 -1
- package/src/cli.ts +29 -5
- package/src/device/subject.ts +4 -0
- package/src/failure-reporter.ts +4 -4
- package/src/print-report.ts +13 -18
- package/src/test-descriptor.ts +70 -61
- package/src/tsconfig.json +6 -2
- package/dist/cjs/chip/pics-expression.d.ts.map +0 -1
- package/dist/cjs/chip/pics-expression.js.map +0 -6
- package/dist/cjs/chip/pics-file.d.ts.map +0 -1
- package/dist/cjs/chip/pics-file.js.map +0 -6
- package/dist/esm/chip/pics-expression.d.ts.map +0 -1
- package/dist/esm/chip/pics-expression.js.map +0 -6
- package/dist/esm/chip/pics-file.d.ts.map +0 -1
- package/dist/esm/chip/pics-file.js.map +0 -6
|
@@ -19,21 +19,22 @@ export class ContainerCommandPipe extends CommandPipe {
|
|
|
19
19
|
#deactivate?: () => void;
|
|
20
20
|
#stopped?: Promise<void>;
|
|
21
21
|
|
|
22
|
-
constructor(container: Container, subject: BackchannelCommand.Subject
|
|
23
|
-
super(subject,
|
|
22
|
+
constructor(container: Container, subject: BackchannelCommand.Subject) {
|
|
23
|
+
super(subject, "/command-pipe.fifo");
|
|
24
24
|
this.#container = container;
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
override async initialize() {
|
|
28
|
-
//
|
|
29
|
-
// agents. This would overwrite our FIFO, however, so we symlink to the real FIFO to prevent it from being
|
|
30
|
-
// overwritten
|
|
28
|
+
// We create a single FIFO for the container and symlink to the hard-coded name expected by CHIP
|
|
31
29
|
await this.#container.createPipe(FIFO_PATH);
|
|
32
|
-
await this.#container.exec(["ln", "-sf", "/command-pipe.fifo", this.filename]);
|
|
33
30
|
|
|
34
31
|
this.#stopped = this.#processCommands();
|
|
35
32
|
}
|
|
36
33
|
|
|
34
|
+
async installForApp(name: string) {
|
|
35
|
+
await this.#container.exec(["ln", "-sf", this.filename, ContainerCommandPipe.filenameFor(name)]);
|
|
36
|
+
}
|
|
37
|
+
|
|
37
38
|
override async close() {
|
|
38
39
|
this.#deactivate?.();
|
|
39
40
|
this.#deactivate = undefined;
|
|
@@ -43,39 +44,36 @@ export class ContainerCommandPipe extends CommandPipe {
|
|
|
43
44
|
}
|
|
44
45
|
|
|
45
46
|
async #processCommands() {
|
|
46
|
-
let
|
|
47
|
+
let commands: Terminal<string> | undefined;
|
|
47
48
|
try {
|
|
48
|
-
|
|
49
|
+
commands = await this.#container.exec(
|
|
50
|
+
["bash", "-c", `while true; do cat ${FIFO_PATH}; done`],
|
|
51
|
+
Terminal.StdoutLine,
|
|
52
|
+
);
|
|
49
53
|
|
|
50
|
-
const
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
let deactivator;
|
|
54
|
-
const deactivated = new Promise<void>(resolve => {
|
|
55
|
-
deactivator = this.#deactivate = resolve;
|
|
56
|
-
});
|
|
54
|
+
const deactivated = new Promise<void>(resolve => {
|
|
55
|
+
this.#deactivate = resolve;
|
|
56
|
+
});
|
|
57
57
|
|
|
58
|
-
|
|
58
|
+
const iterator = commands[Symbol.asyncIterator]();
|
|
59
59
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
} finally {
|
|
68
|
-
if (this.#deactivate === deactivator) {
|
|
69
|
-
this.#deactivate = undefined;
|
|
70
|
-
}
|
|
60
|
+
while (true) {
|
|
61
|
+
const command = await Promise.race([
|
|
62
|
+
deactivated,
|
|
63
|
+
iterator.next().then(result => result.value as string),
|
|
64
|
+
]);
|
|
65
|
+
if (command === undefined) {
|
|
66
|
+
break;
|
|
71
67
|
}
|
|
68
|
+
this.onData(command);
|
|
72
69
|
}
|
|
70
|
+
await deactivated;
|
|
73
71
|
} finally {
|
|
74
|
-
if (
|
|
72
|
+
if (commands) {
|
|
75
73
|
try {
|
|
76
|
-
await
|
|
74
|
+
await commands.close();
|
|
77
75
|
} catch (e) {
|
|
78
|
-
console.warn(`Error closing
|
|
76
|
+
console.warn(`Error closing command proxy for ${this.filename}:`, e);
|
|
79
77
|
}
|
|
80
78
|
}
|
|
81
79
|
|
package/src/chip/index.ts
CHANGED
|
@@ -24,7 +24,7 @@ DGGEN.S.A0006=0
|
|
|
24
24
|
DGGEN.S.A0007=0
|
|
25
25
|
|
|
26
26
|
# We do not support the "TestTrigger" command
|
|
27
|
-
|
|
27
|
+
DGGEN.S.C00.Rsp=0
|
|
28
28
|
|
|
29
29
|
# We do not support the "TimeSnapshotResponse" command
|
|
30
30
|
DGGEN.S.C03.Rsp=0
|
|
@@ -40,9 +40,12 @@ DGGEN.S.F00=0
|
|
|
40
40
|
# We do not support the optional Battery Fault Change event
|
|
41
41
|
PS.S.E01=0
|
|
42
42
|
|
|
43
|
-
# We
|
|
43
|
+
# We don't support ScanMaxTimeSeconds or ConnectMaxTimeSeconds attributes
|
|
44
44
|
CNET.S.A0002..3=0
|
|
45
45
|
|
|
46
|
+
# We don't support ethernet networking
|
|
47
|
+
CNET.S.F02=0
|
|
48
|
+
|
|
46
49
|
# We do not provide a Taglist on Descriptor cluster
|
|
47
50
|
DESC.S.F00=0
|
|
48
51
|
|
|
@@ -55,8 +58,8 @@ BOOL.S.E00=1
|
|
|
55
58
|
# We support "reachable" event on Basic Information
|
|
56
59
|
BINFO.S.A0011=1
|
|
57
60
|
|
|
58
|
-
# We
|
|
59
|
-
|
|
61
|
+
# We do not support ConfigurationVersion attribute on Basic Information
|
|
62
|
+
BINFO.S.A0018=0
|
|
60
63
|
|
|
61
64
|
# No level related features for CO2 measurement
|
|
62
65
|
CDOCONC.S.F01..3=0
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* SPDX-License-Identifier: Apache-2.0
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import { PicsFile } from "./
|
|
7
|
+
import { PicsFile } from "./file.js";
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
10
|
* A set of PICS specifiers, each of which must be true for the set to apply.
|
|
@@ -112,7 +112,8 @@ function parse(definition: string) {
|
|
|
112
112
|
|
|
113
113
|
const result = parseExpr();
|
|
114
114
|
|
|
115
|
-
|
|
115
|
+
// Note - allow garbage trailing "(" as this seems to be a way to provide a hint as to purpose of expression
|
|
116
|
+
if (tokenizer.peek() !== undefined && tokenizer.peek()?.kind !== "(") {
|
|
116
117
|
throw new InvalidPicsExpressionError(definition, tokenizer.peek());
|
|
117
118
|
}
|
|
118
119
|
|
|
@@ -191,7 +192,7 @@ function test(ast: Ast, file: PicsFile): boolean {
|
|
|
191
192
|
return !test(ast.operand, file);
|
|
192
193
|
|
|
193
194
|
case "name":
|
|
194
|
-
return file.values[ast.name] ===
|
|
195
|
+
return file.values[ast.name] === 1;
|
|
195
196
|
|
|
196
197
|
case "&":
|
|
197
198
|
return test(ast.lhs, file) && test(ast.rhs, file);
|
|
@@ -4,10 +4,10 @@
|
|
|
4
4
|
* SPDX-License-Identifier: Apache-2.0
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import {
|
|
7
|
+
import { PicsValues } from "./values.js";
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
|
-
*
|
|
10
|
+
* In-memory Matter PICS file.
|
|
11
11
|
*
|
|
12
12
|
* Supports extended syntax for defining ranges of values of the form "NAMExx..yy=*" where xx and yy are hexadecimal
|
|
13
13
|
* numbers specifying the start and end of a range (inclusive). These are expanded in {@link patch} which modifies the
|
|
@@ -18,13 +18,14 @@ import { readFileSync, writeFileSync } from "node:fs";
|
|
|
18
18
|
*/
|
|
19
19
|
export class PicsFile {
|
|
20
20
|
#lines: string[];
|
|
21
|
-
#values?:
|
|
21
|
+
#values?: PicsValues;
|
|
22
22
|
|
|
23
|
-
constructor(
|
|
24
|
-
if (
|
|
25
|
-
|
|
23
|
+
constructor(lines?: string | string[]) {
|
|
24
|
+
if (typeof lines === "string") {
|
|
25
|
+
this.#lines = lines.split("\n").map(l => l.trim());
|
|
26
|
+
} else {
|
|
27
|
+
this.#lines = lines ?? [];
|
|
26
28
|
}
|
|
27
|
-
this.#lines = pathOrBody.split("\n").map(l => l.trim());
|
|
28
29
|
}
|
|
29
30
|
|
|
30
31
|
get lines() {
|
|
@@ -33,7 +34,7 @@ export class PicsFile {
|
|
|
33
34
|
|
|
34
35
|
get values() {
|
|
35
36
|
if (!this.#values) {
|
|
36
|
-
const values = {} as
|
|
37
|
+
const values = {} as PicsValues;
|
|
37
38
|
for (const line of this.lines) {
|
|
38
39
|
parseLine(line, values);
|
|
39
40
|
}
|
|
@@ -51,7 +52,7 @@ export class PicsFile {
|
|
|
51
52
|
|
|
52
53
|
const newLines = new Array<string>();
|
|
53
54
|
for (const line of this.lines) {
|
|
54
|
-
const lineValues = {} as
|
|
55
|
+
const lineValues = {} as PicsValues;
|
|
55
56
|
if (!parseLine(line, lineValues)) {
|
|
56
57
|
newLines.push(line);
|
|
57
58
|
continue;
|
|
@@ -75,13 +76,15 @@ export class PicsFile {
|
|
|
75
76
|
this.#values = undefined;
|
|
76
77
|
this.#lines = newLines;
|
|
77
78
|
}
|
|
79
|
+
}
|
|
78
80
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
+
export namespace PicsFile {
|
|
82
|
+
export interface Values {
|
|
83
|
+
[key: string]: 0 | 1;
|
|
81
84
|
}
|
|
82
85
|
}
|
|
83
86
|
|
|
84
|
-
function parseLine(line: string, values:
|
|
87
|
+
function parseLine(line: string, values: PicsValues): boolean {
|
|
85
88
|
line = line.trim();
|
|
86
89
|
if (line.startsWith("#")) {
|
|
87
90
|
return false;
|
|
@@ -92,7 +95,21 @@ function parseLine(line: string, values: Record<string, string>): boolean {
|
|
|
92
95
|
return false;
|
|
93
96
|
}
|
|
94
97
|
|
|
95
|
-
const [, key,
|
|
98
|
+
const [, key, valueStr] = valueMatch;
|
|
99
|
+
let value: 0 | 1;
|
|
100
|
+
switch (valueStr) {
|
|
101
|
+
case "0":
|
|
102
|
+
value = 0;
|
|
103
|
+
break;
|
|
104
|
+
|
|
105
|
+
case "1":
|
|
106
|
+
value = 1;
|
|
107
|
+
break;
|
|
108
|
+
|
|
109
|
+
default:
|
|
110
|
+
return false;
|
|
111
|
+
}
|
|
112
|
+
|
|
96
113
|
const rangeMatch = key.match(/^(\S+)\.\.([\da-f]+)$/i);
|
|
97
114
|
if (!rangeMatch) {
|
|
98
115
|
values[key] = value;
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2022-2025 Project CHIP Authors
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { Package } from "#tools";
|
|
8
|
+
import { readFile } from "node:fs/promises";
|
|
9
|
+
import { isAbsolute } from "node:path";
|
|
10
|
+
import { State } from "../state.js";
|
|
11
|
+
import { PicsFile } from "./file.js";
|
|
12
|
+
|
|
13
|
+
const dataCache = new WeakMap<PicsSource, PicsFile>();
|
|
14
|
+
const filenameCache = new WeakMap<PicsFile, string>();
|
|
15
|
+
|
|
16
|
+
let nextFileNo = 1;
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Source of PICS values.
|
|
20
|
+
*/
|
|
21
|
+
export type PicsSource =
|
|
22
|
+
| PicsSource.Composite
|
|
23
|
+
| PicsSource.ChipFile
|
|
24
|
+
| PicsSource.LocalFile
|
|
25
|
+
| PicsSource.Lines
|
|
26
|
+
| PicsSource.Values;
|
|
27
|
+
|
|
28
|
+
export namespace PicsSource {
|
|
29
|
+
/**
|
|
30
|
+
* Load a {@link PicsFile} defined by a {@link PicsSource}.
|
|
31
|
+
*
|
|
32
|
+
* Caches results so a source always returns the same {@link PicsFile} instance.
|
|
33
|
+
*/
|
|
34
|
+
export async function load(source: PicsSource): Promise<PicsFile> {
|
|
35
|
+
let file = dataCache.get(source);
|
|
36
|
+
if (file) {
|
|
37
|
+
return file;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
switch (source.kind) {
|
|
41
|
+
case "composite":
|
|
42
|
+
for (const subsource of source.sources) {
|
|
43
|
+
const sourceFile = await load(subsource);
|
|
44
|
+
if (file) {
|
|
45
|
+
file.patch(sourceFile);
|
|
46
|
+
} else {
|
|
47
|
+
file = sourceFile;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
if (!file) {
|
|
51
|
+
file = new PicsFile();
|
|
52
|
+
}
|
|
53
|
+
break;
|
|
54
|
+
|
|
55
|
+
case "chip":
|
|
56
|
+
file = new PicsFile(await State.container.read(source.name));
|
|
57
|
+
break;
|
|
58
|
+
|
|
59
|
+
case "file":
|
|
60
|
+
file = new PicsFile(await readFile(resolve(source.name), "utf-8"));
|
|
61
|
+
break;
|
|
62
|
+
|
|
63
|
+
case "lines":
|
|
64
|
+
file = new PicsFile(source.lines.split("\n").map(l => l.trim()));
|
|
65
|
+
break;
|
|
66
|
+
|
|
67
|
+
case "values":
|
|
68
|
+
file = new PicsFile(Object.entries(source.values).map(([key, value]) => `${key}=${value}`));
|
|
69
|
+
break;
|
|
70
|
+
|
|
71
|
+
default:
|
|
72
|
+
throw new Error(`Invalid PICS source kind "${(source as { kind: unknown }).kind}"`);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
dataCache.set(source, file);
|
|
76
|
+
|
|
77
|
+
return file;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Save a {@link PicsFile} to the a {@link ChipFile} or {@link LocalFile}.
|
|
82
|
+
*/
|
|
83
|
+
export async function save(target: ChipFile | LocalFile, file: PicsFile): Promise<void> {
|
|
84
|
+
switch (target.kind) {
|
|
85
|
+
case "chip":
|
|
86
|
+
await State.container.write(target.name, file.toString());
|
|
87
|
+
break;
|
|
88
|
+
|
|
89
|
+
case "file":
|
|
90
|
+
await State.container.write(resolve(target.name), file.toString());
|
|
91
|
+
break;
|
|
92
|
+
|
|
93
|
+
default:
|
|
94
|
+
throw new Error(`Invalid PICS target kind "${(target as { kind: unknown }).kind}"`);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Install a {@link PicsSource} into the test container.
|
|
100
|
+
*
|
|
101
|
+
* Returns the name of the file in the container.
|
|
102
|
+
*
|
|
103
|
+
* Results are cached so the same source always returns the same filename.
|
|
104
|
+
*/
|
|
105
|
+
export async function install(file: PicsFile): Promise<string> {
|
|
106
|
+
let filename = filenameCache.get(file);
|
|
107
|
+
if (filename) {
|
|
108
|
+
return filename;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
filename = `/pics-${nextFileNo++}.properties`;
|
|
112
|
+
|
|
113
|
+
try {
|
|
114
|
+
filenameCache.set(file, filename);
|
|
115
|
+
await save({ kind: "chip", name: filename }, file);
|
|
116
|
+
} catch (e) {
|
|
117
|
+
filenameCache.delete(file);
|
|
118
|
+
throw e;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
return filename;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
export interface Composite {
|
|
125
|
+
kind: "composite";
|
|
126
|
+
sources: Source[];
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export interface ChipFile {
|
|
130
|
+
kind: "chip";
|
|
131
|
+
name: string;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
export interface LocalFile {
|
|
135
|
+
kind: "file";
|
|
136
|
+
name: string;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
export interface Lines {
|
|
140
|
+
kind: "lines";
|
|
141
|
+
lines: string;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export interface Values {
|
|
145
|
+
kind: "values";
|
|
146
|
+
values: Record<string, 0 | 1>;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
export type Source = ChipFile | LocalFile | Lines | Values;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function resolve(path: string): string {
|
|
153
|
+
if (isAbsolute(path)) {
|
|
154
|
+
return path;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const testing = Package.tools.findPackage("@matter/testing");
|
|
158
|
+
if (testing.hasFile(path)) {
|
|
159
|
+
return testing.resolve(path);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
return resolve(path);
|
|
163
|
+
}
|
package/src/chip/python-test.ts
CHANGED
|
@@ -11,6 +11,7 @@ import { Terminal } from "../docker/terminal.js";
|
|
|
11
11
|
import { TestFileDescriptor } from "../test-descriptor.js";
|
|
12
12
|
import { parseStep } from "./chip-test-common.js";
|
|
13
13
|
import { Constants, ContainerPaths } from "./config.js";
|
|
14
|
+
import { PicsSource } from "./pics/source.js";
|
|
14
15
|
|
|
15
16
|
export class PythonTest extends BaseTest {
|
|
16
17
|
/**
|
|
@@ -173,11 +174,12 @@ async function createCommand(descriptor: TestFileDescriptor, subject: Subject, e
|
|
|
173
174
|
const command = ["python3", descriptor.path, ...Constants.PythonRunnerArgs];
|
|
174
175
|
|
|
175
176
|
const args = scriptArgsOf(descriptor);
|
|
176
|
-
if (args !== undefined) {
|
|
177
|
-
command.push(...args);
|
|
178
|
-
}
|
|
179
177
|
|
|
180
|
-
command.push(...extraArgs);
|
|
178
|
+
command.push(...args, ...extraArgs);
|
|
179
|
+
|
|
180
|
+
if (!command.includes("--PICS")) {
|
|
181
|
+
command.push("--PICS", await PicsSource.install(subject.pics));
|
|
182
|
+
}
|
|
181
183
|
|
|
182
184
|
const qrCodePos = command.indexOf("--qr-code");
|
|
183
185
|
if (qrCodePos !== -1) {
|
|
@@ -188,13 +190,18 @@ async function createCommand(descriptor: TestFileDescriptor, subject: Subject, e
|
|
|
188
190
|
}
|
|
189
191
|
|
|
190
192
|
function scriptArgsOf(descriptor: TestFileDescriptor) {
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
193
|
+
let args: string[] | undefined;
|
|
194
|
+
|
|
195
|
+
const predefined = descriptor.config?.["script-args"];
|
|
196
|
+
if (typeof predefined === "string") {
|
|
197
|
+
args = predefined.trim().split(/\s+/);
|
|
198
|
+
} else if (Array.isArray(predefined)) {
|
|
199
|
+
args = [...predefined];
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
if (descriptor.subpath) {
|
|
203
|
+
(args ?? (args = [])).push("--test-case", descriptor.subpath);
|
|
194
204
|
}
|
|
195
205
|
|
|
196
|
-
return
|
|
197
|
-
.replace(/--(?:storage-path|commissioning-method|discriminator|passcode|trace-to|PICS)\s+\S+\s+/g, "")
|
|
198
|
-
.trim()
|
|
199
|
-
.split(/\s+/);
|
|
206
|
+
return args ?? [];
|
|
200
207
|
}
|
package/src/chip/state.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* SPDX-License-Identifier: Apache-2.0
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import { ansi
|
|
7
|
+
import { ansi } 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";
|
|
@@ -15,12 +15,13 @@ import { Image } from "../docker/image.js";
|
|
|
15
15
|
import { Volume } from "../docker/volume.js";
|
|
16
16
|
import { afterRun, beforeRun } from "../mocha.js";
|
|
17
17
|
import type { TestRunner } from "../runner.js";
|
|
18
|
-
import { TestDescriptor, TestFileDescriptor } from "../test-descriptor.js";
|
|
18
|
+
import { RootTestDescriptor, TestDescriptor, TestFileDescriptor } from "../test-descriptor.js";
|
|
19
19
|
import { AccessoryServer } from "./accessory-server.js";
|
|
20
20
|
import type { chip } from "./chip.js";
|
|
21
21
|
import { Constants, ContainerPaths } from "./config.js";
|
|
22
22
|
import { ContainerCommandPipe } from "./container-command-pipe.js";
|
|
23
|
-
import { PicsFile } from "./pics
|
|
23
|
+
import { PicsFile } from "./pics/file.js";
|
|
24
|
+
import { PicsSource } from "./pics/source.js";
|
|
24
25
|
import { PythonTest } from "./python-test.js";
|
|
25
26
|
import { YamlTest } from "./yaml-test.js";
|
|
26
27
|
|
|
@@ -35,18 +36,19 @@ const Values = {
|
|
|
35
36
|
test: undefined as Test | undefined,
|
|
36
37
|
mainContainer: undefined as Container | undefined,
|
|
37
38
|
mdnsContainer: undefined as Container | undefined,
|
|
38
|
-
|
|
39
|
+
defaultPics: undefined as PicsFile | undefined,
|
|
40
|
+
defaultPicsFilename: undefined as string | undefined,
|
|
39
41
|
tests: undefined as TestDescriptor.Filesystem | undefined,
|
|
40
42
|
initializedSubjects: new WeakSet<Subject>(),
|
|
41
43
|
activeSubject: undefined as Subject | undefined,
|
|
42
44
|
singleUseSubject: false,
|
|
43
|
-
activePipes: new Set<string>(),
|
|
44
45
|
closers: Array<() => Promise<void>>(),
|
|
45
46
|
subjects: new Map<Subject.Factory, Record<string, Subject>>(),
|
|
46
47
|
snapshots: new Map<Subject, {}>(),
|
|
47
48
|
containerLifecycleInstalled: false,
|
|
48
49
|
testMap: new Map<TestDescriptor, Test>(),
|
|
49
50
|
pullBeforeTesting: true,
|
|
51
|
+
commandPipe: undefined as ContainerCommandPipe | undefined,
|
|
50
52
|
};
|
|
51
53
|
|
|
52
54
|
/**
|
|
@@ -113,12 +115,20 @@ export const State = {
|
|
|
113
115
|
Values.pullBeforeTesting = value;
|
|
114
116
|
},
|
|
115
117
|
|
|
116
|
-
get
|
|
117
|
-
if (Values.
|
|
118
|
+
get defaultPics() {
|
|
119
|
+
if (Values.defaultPics === undefined) {
|
|
118
120
|
throw new Error("PICS not initialized");
|
|
119
121
|
}
|
|
120
122
|
|
|
121
|
-
return Values.
|
|
123
|
+
return Values.defaultPics;
|
|
124
|
+
},
|
|
125
|
+
|
|
126
|
+
get defaultPicsFilename() {
|
|
127
|
+
if (Values.defaultPicsFilename === undefined) {
|
|
128
|
+
throw new Error("PICS not initialized");
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
return Values.defaultPicsFilename;
|
|
122
132
|
},
|
|
123
133
|
|
|
124
134
|
get tests() {
|
|
@@ -152,7 +162,7 @@ export const State = {
|
|
|
152
162
|
|
|
153
163
|
progress.update("Initializing containers");
|
|
154
164
|
try {
|
|
155
|
-
|
|
165
|
+
await initialize();
|
|
156
166
|
|
|
157
167
|
const image = await State.container.image;
|
|
158
168
|
const info = await image.inspect();
|
|
@@ -163,8 +173,6 @@ export const State = {
|
|
|
163
173
|
progress.success(
|
|
164
174
|
`Initialized CHIP ${ansi.bold(chipCommit)} image ${ansi.bold(imageVersion)} for ${ansi.bold(arch)}`,
|
|
165
175
|
);
|
|
166
|
-
|
|
167
|
-
return result;
|
|
168
176
|
} catch (e) {
|
|
169
177
|
progress.failure("Initializing containers");
|
|
170
178
|
throw e;
|
|
@@ -240,20 +248,16 @@ export const State = {
|
|
|
240
248
|
* Open a back-channel command pipe.
|
|
241
249
|
*/
|
|
242
250
|
async openPipe(name: string) {
|
|
243
|
-
if (Values.
|
|
244
|
-
|
|
251
|
+
if (Values.commandPipe === undefined) {
|
|
252
|
+
Values.commandPipe = new ContainerCommandPipe(State.container, this);
|
|
253
|
+
await Values.commandPipe.initialize();
|
|
254
|
+
State.onClose(async () => {
|
|
255
|
+
await Values.commandPipe?.close();
|
|
256
|
+
Values.commandPipe = undefined;
|
|
257
|
+
});
|
|
245
258
|
}
|
|
246
259
|
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
await pipe.initialize();
|
|
250
|
-
|
|
251
|
-
Values.activePipes.add(name);
|
|
252
|
-
|
|
253
|
-
State.onClose(async () => {
|
|
254
|
-
await pipe.close();
|
|
255
|
-
Values.activePipes.delete(name);
|
|
256
|
-
});
|
|
260
|
+
await Values.commandPipe.installForApp(name);
|
|
257
261
|
},
|
|
258
262
|
|
|
259
263
|
/**
|
|
@@ -402,6 +406,7 @@ export const State = {
|
|
|
402
406
|
*/
|
|
403
407
|
async function initialize() {
|
|
404
408
|
await configureContainer();
|
|
409
|
+
await configureScripts();
|
|
405
410
|
await configurePics();
|
|
406
411
|
await configureTests();
|
|
407
412
|
await configureNetwork();
|
|
@@ -465,19 +470,29 @@ async function configureContainer() {
|
|
|
465
470
|
}
|
|
466
471
|
|
|
467
472
|
/**
|
|
468
|
-
*
|
|
473
|
+
* Monkey patch test scripts to work around bugs.
|
|
469
474
|
*/
|
|
470
|
-
async function
|
|
471
|
-
|
|
472
|
-
|
|
475
|
+
async function configureScripts() {
|
|
476
|
+
// There is no ack on the command pipe. Writing to it is copied in a multitude of places (pending PR fixes some of
|
|
477
|
+
// this). Most places have a delay to try to compensate for lack of ack but in the "centralized" command writer the
|
|
478
|
+
// delay is only 1 ms. which is sometimes too short for us. Change to 20ms
|
|
479
|
+
await State.container.edit(
|
|
480
|
+
edit.sed("s/sleep(0.001)/sleep(.02)/"),
|
|
473
481
|
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
pics.patch(overrides);
|
|
482
|
+
// This is the one we actually use
|
|
483
|
+
"/usr/local/lib/python3.12/dist-packages/chip/testing/matter_testing.py",
|
|
477
484
|
|
|
478
|
-
|
|
485
|
+
// Patching here too just for completeness
|
|
486
|
+
"/src/python_testing/matter_testing_infrastructure/chip/testing/matter_testing.py",
|
|
487
|
+
);
|
|
488
|
+
}
|
|
479
489
|
|
|
480
|
-
|
|
490
|
+
/**
|
|
491
|
+
* Create a PICS file in the container appropriate for matter.js.
|
|
492
|
+
*/
|
|
493
|
+
async function configurePics() {
|
|
494
|
+
Values.defaultPics = await PicsSource.load(Constants.defaultPics);
|
|
495
|
+
Values.defaultPicsFilename = await PicsSource.install(Values.defaultPics);
|
|
481
496
|
}
|
|
482
497
|
|
|
483
498
|
/**
|
|
@@ -487,10 +502,18 @@ async function configureTests() {
|
|
|
487
502
|
const { container } = State;
|
|
488
503
|
|
|
489
504
|
// Load test descriptors
|
|
490
|
-
const descriptor = JSON.parse(await container.read(ContainerPaths.descriptorFile)) as
|
|
505
|
+
const descriptor = JSON.parse(await container.read(ContainerPaths.descriptorFile)) as RootTestDescriptor;
|
|
506
|
+
|
|
507
|
+
// Ensure this is a supported container version
|
|
508
|
+
if (descriptor.format !== TestDescriptor.CURRENT_FORMAT) {
|
|
509
|
+
throw new Error(`Invalid descriptor format "${descriptor.format}" (expected ${TestDescriptor.CURRENT_FORMAT})`);
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
// Ensure test descriptor isn't empty
|
|
491
513
|
if (!Array.isArray(descriptor.members)) {
|
|
492
514
|
throw new Error(`CHIP test descriptor has no members`);
|
|
493
515
|
}
|
|
516
|
+
|
|
494
517
|
Values.tests = TestDescriptor.Filesystem(descriptor);
|
|
495
518
|
}
|
|
496
519
|
|