Skip to content

Commit acf823f

Browse files
Automated implementation of initially 1 python test to use Metadata script as an intermediate argument holder (#33888)
* Automated implementation of 1 python test to use Metadata script as an intermediate argument holder * Added modified tests.yaml * Restyled by autopep8 * Restyled by isort * Fixed the unite test for metadata * Fixed the Syntax error Unterminated quoted string * Restyled by autopep8 * Restyled by isort * Fixed the quoted string error * did ruff clean * rename scripts/tests/yaml to scripts/tests/chipyaml and replace the imports for yaml.paths_finder to chipyaml.paths_finder * Adding the rename changes I had in the new PR to make it clean and independent * Updates and Fixes to the Script * Fixed latest tests.yaml and test_metadata.py changes * Restyled by autopep8 * Restyled by isort * Removed extra import typing.list as per code linter * Restyled by autopep8 --------- Co-authored-by: Restyled.io <commits@restyled.io>
1 parent e1d746a commit acf823f

File tree

5 files changed

+162
-144
lines changed

5 files changed

+162
-144
lines changed

.github/workflows/tests.yaml

+11-1
Original file line numberDiff line numberDiff line change
@@ -475,6 +475,16 @@ jobs:
475475
build \
476476
--copy-artifacts-to objdir-clone \
477477
"
478+
- name: Generate an argument environment file
479+
run: |
480+
echo -n "" >/tmp/test_env.yaml
481+
echo "ALL_CLUSTERS_APP: out/linux-x64-all-clusters-ipv6only-no-ble-no-wifi-tsan-clang-test/chip-all-clusters-app" >> /tmp/test_env.yaml
482+
echo "CHIP_LOCK_APP: out/linux-x64-lock-ipv6only-no-ble-no-wifi-tsan-clang-test/chip-lock-app" >> /tmp/test_env.yaml
483+
echo "ENERGY_MANAGEMENT_APP: out/linux-x64-energy-management-ipv6only-no-ble-no-wifi-tsan-clang-test/chip-energy-management-app" >> /tmp/test_env.yaml
484+
echo "TRACE_APP: out/trace_data/app-{SCRIPT_BASE_NAME}" >> /tmp/test_env.yaml
485+
echo "TRACE_TEST_JSON: out/trace_data/test-{SCRIPT_BASE_NAME}" >> /tmp/test_env.yaml
486+
echo "TRACE_TEST_PERFETTO: out/trace_data/test-{SCRIPT_BASE_NAME}" >> /tmp/test_env.yaml
487+
478488
- name: Run Tests
479489
run: |
480490
mkdir -p out/trace_data
@@ -516,7 +526,7 @@ jobs:
516526
scripts/run_in_python_env.sh out/venv './scripts/tests/run_python_test.py --app out/linux-x64-all-clusters-ipv6only-no-ble-no-wifi-tsan-clang-test/chip-all-clusters-app --factoryreset --quiet --app-args "--discriminator 1234 --KVS kvs1 --trace-to json:out/trace_data/app-{SCRIPT_BASE_NAME}.json --enable-key 000102030405060708090a0b0c0d0e0f" --script "src/python_testing/TC_IDM_1_4.py" --script-args "--hex-arg PIXIT.DGGEN.TEST_EVENT_TRIGGER_KEY:000102030405060708090a0b0c0d0e0f --storage-path admin_storage.json --commissioning-method on-network --discriminator 1234 --passcode 20202021 --trace-to json:out/trace_data/test-{SCRIPT_BASE_NAME}.json --trace-to perfetto:out/trace_data/test-{SCRIPT_BASE_NAME}.perfetto"'
517527
scripts/run_in_python_env.sh out/venv './scripts/tests/run_python_test.py --app out/linux-x64-all-clusters-ipv6only-no-ble-no-wifi-tsan-clang-test/chip-all-clusters-app --factoryreset --quiet --app-args "--discriminator 1234 --KVS kvs1 --trace-to json:out/trace_data/app-{SCRIPT_BASE_NAME}.json" --script "src/python_testing/TC_PWRTL_2_1.py" --script-args "--storage-path admin_storage.json --commissioning-method on-network --discriminator 1234 --passcode 20202021 --trace-to json:out/trace_data/test-{SCRIPT_BASE_NAME}.json --trace-to perfetto:out/trace_data/test-{SCRIPT_BASE_NAME}.perfetto"'
518528
scripts/run_in_python_env.sh out/venv './scripts/tests/run_python_test.py --app out/linux-x64-all-clusters-ipv6only-no-ble-no-wifi-tsan-clang-test/chip-all-clusters-app --factoryreset --quiet --app-args "--discriminator 1234 --KVS kvs1 --trace-to json:out/trace_data/app-{SCRIPT_BASE_NAME}.json" --script "src/python_testing/TC_RR_1_1.py" --script-args "--storage-path admin_storage.json --commissioning-method on-network --discriminator 1234 --passcode 20202021 --trace-to json:out/trace_data/test-{SCRIPT_BASE_NAME}.json --trace-to perfetto:out/trace_data/test-{SCRIPT_BASE_NAME}.perfetto"'
519-
scripts/run_in_python_env.sh out/venv './scripts/tests/run_python_test.py --app out/linux-x64-all-clusters-ipv6only-no-ble-no-wifi-tsan-clang-test/chip-all-clusters-app --factoryreset --quiet --app-args "--discriminator 1234 --KVS kvs1 --trace-to json:out/trace_data/app-{SCRIPT_BASE_NAME}.json" --script "src/python_testing/TC_SC_3_6.py" --script-args "--storage-path admin_storage.json --commissioning-method on-network --discriminator 1234 --passcode 20202021 --trace-to json:out/trace_data/test-{SCRIPT_BASE_NAME}.json --trace-to perfetto:out/trace_data/test-{SCRIPT_BASE_NAME}.perfetto"'
529+
scripts/run_in_python_env.sh out/venv './scripts/tests/run_python_test.py --load-from-env /tmp/test_env.yaml --script src/python_testing/TC_SC_3_6.py'
520530
scripts/run_in_python_env.sh out/venv './scripts/tests/run_python_test.py --app out/linux-x64-all-clusters-ipv6only-no-ble-no-wifi-tsan-clang-test/chip-all-clusters-app --factoryreset --quiet --app-args "--discriminator 1234 --KVS kvs1 --trace-to json:out/trace_data/app-{SCRIPT_BASE_NAME}.json" --script "src/python_testing/TC_TIMESYNC_2_1.py" --script-args "--storage-path admin_storage.json --commissioning-method on-network --discriminator 1234 --passcode 20202021 --PICS src/app/tests/suites/certification/ci-pics-values --trace-to json:out/trace_data/test-{SCRIPT_BASE_NAME}.json --trace-to perfetto:out/trace_data/test-{SCRIPT_BASE_NAME}.perfetto"'
521531
scripts/run_in_python_env.sh out/venv './scripts/tests/run_python_test.py --app out/linux-x64-all-clusters-ipv6only-no-ble-no-wifi-tsan-clang-test/chip-all-clusters-app --factoryreset --quiet --app-args "--discriminator 1234 --KVS kvs1 --trace-to json:out/trace_data/app-{SCRIPT_BASE_NAME}.json" --script "src/python_testing/TC_TIMESYNC_2_10.py" --script-args "--storage-path admin_storage.json --commissioning-method on-network --discriminator 1234 --passcode 20202021 --PICS src/app/tests/suites/certification/ci-pics-values --trace-to json:out/trace_data/test-{SCRIPT_BASE_NAME}.json --trace-to perfetto:out/trace_data/test-{SCRIPT_BASE_NAME}.perfetto"'
522532
scripts/run_in_python_env.sh out/venv './scripts/tests/run_python_test.py --app out/linux-x64-all-clusters-ipv6only-no-ble-no-wifi-tsan-clang-test/chip-all-clusters-app --factoryreset --quiet --app-args "--discriminator 1234 --KVS kvs1 --trace-to json:out/trace_data/app-{SCRIPT_BASE_NAME}.json" --script "src/python_testing/TC_TIMESYNC_2_11.py" --script-args "--storage-path admin_storage.json --commissioning-method on-network --discriminator 1234 --passcode 20202021 --PICS src/app/tests/suites/certification/ci-pics-values --trace-to json:out/trace_data/test-{SCRIPT_BASE_NAME}.json --trace-to perfetto:out/trace_data/test-{SCRIPT_BASE_NAME}.perfetto"'

scripts/tests/py/metadata.py

+68-118
Original file line numberDiff line numberDiff line change
@@ -15,51 +15,64 @@
1515

1616
import re
1717
from dataclasses import dataclass
18-
from typing import Dict, List, Optional
18+
from typing import Any, Dict, List
1919

2020
import yaml
2121

2222

2323
@dataclass
2424
class Metadata:
25-
py_script_path: Optional[str] = None
26-
run: Optional[str] = None
27-
app: Optional[str] = None
28-
discriminator: Optional[str] = None
29-
passcode: Optional[str] = None
30-
31-
def copy_from_dict(self, attr_dict: Dict[str, str]) -> None:
25+
py_script_path: str
26+
run: str
27+
app: str
28+
app_args: str
29+
script_args: str
30+
factoryreset: bool = False
31+
factoryreset_app_only: bool = False
32+
script_gdb: bool = False
33+
quiet: bool = True
34+
35+
def copy_from_dict(self, attr_dict: Dict[str, Any]) -> None:
3236
"""
3337
Sets the value of the attributes from a dictionary.
3438
3539
Attributes:
3640
3741
attr_dict:
38-
Dictionary that stores attributes value that should
39-
be transferred to this class.
42+
Dictionary that stores attributes value that should
43+
be transferred to this class.
4044
"""
41-
4245
if "app" in attr_dict:
4346
self.app = attr_dict["app"]
4447

4548
if "run" in attr_dict:
4649
self.run = attr_dict["run"]
4750

48-
if "discriminator" in attr_dict:
49-
self.discriminator = attr_dict["discriminator"]
51+
if "app-args" in attr_dict:
52+
self.app_args = attr_dict["app-args"]
5053

51-
if "passcode" in attr_dict:
52-
self.passcode = attr_dict["passcode"]
54+
if "script-args" in attr_dict:
55+
self.script_args = attr_dict["script-args"]
5356

5457
if "py_script_path" in attr_dict:
5558
self.py_script_path = attr_dict["py_script_path"]
5659

57-
# TODO - set other attributes as well
60+
if "factoryreset" in attr_dict:
61+
self.factoryreset = bool(attr_dict["factoryreset"])
62+
63+
if "factoryreset_app_only" in attr_dict:
64+
self.factoryreset_app_only = bool(attr_dict["factoryreset_app_only"])
65+
66+
if "script_gdb" in attr_dict:
67+
self.script_gdb = bool(attr_dict["script_gdb"])
68+
69+
if "quiet" in attr_dict:
70+
self.quiet = bool(attr_dict["quiet"])
5871

5972

6073
class MetadataReader:
6174
"""
62-
A class to parse run arguments from the test scripts and
75+
A class to parse run arguments from the test scripts and
6376
resolve them to environment specific values.
6477
"""
6578

@@ -70,97 +83,30 @@ def __init__(self, env_yaml_file_path: str):
7083
Parameters:
7184
7285
env_yaml_file_path:
73-
Path to the environment file that contains the YAML configuration.
86+
Path to the environment file that contains the YAML configuration.
7487
"""
7588
with open(env_yaml_file_path) as stream:
76-
self.env = yaml.safe_load(stream)
89+
self.env: Dict[str, str] = yaml.safe_load(stream)
7790

7891
def __resolve_env_vals__(self, metadata_dict: Dict[str, str]) -> None:
7992
"""
8093
Resolves the argument defined in the test script to environment values.
8194
For example, if a test script defines "all_clusters" as the value for app
8295
name, we will check the environment configuration to see what raw value is
83-
assocaited with the "all_cluster" variable and set the value for "app" option
96+
associated with the "all_cluster" variable and set the value for "app" option
8497
to this raw value.
8598
8699
Parameter:
87100
88101
metadata_dict:
89-
Dictionary where each key represent a particular argument and its value represent
90-
the value for that argument defined in the test script.
91-
"""
92-
93-
for run_arg, run_arg_val in metadata_dict.items():
94-
95-
if not type(run_arg_val) == str or run_arg == "run":
96-
metadata_dict[run_arg] = run_arg_val
97-
continue
98-
99-
if run_arg_val is None:
100-
continue
101-
102-
sub_args = run_arg_val.split('/')
103-
104-
if len(sub_args) not in [1, 2]:
105-
err = """The argument is not in the correct format.
106-
The argument must follow the format of arg1 or arg1/arg2.
107-
For example, arg1 represents the argument type and optionally arg2
108-
represents a specific variable defined in the environment file whose
109-
value should be used as the argument value. If arg2 is not specified,
110-
we will just use the first value associated with arg1 in the environment file."""
111-
raise Exception(err)
112-
113-
if len(sub_args) == 1:
114-
run_arg_val = self.env.get(sub_args[0])
115-
116-
elif len(sub_args) == 2:
117-
run_arg_val = self.env.get(sub_args[0]).get(sub_args[1])
118-
119-
# if a argument has been specified in the comment header
120-
# but can't be found in the env file, consider it to be
121-
# boolean value.
122-
if run_arg_val is None:
123-
run_arg_val = True
124-
125-
metadata_dict[run_arg] = run_arg_val
126-
127-
def __read_args__(self, run_args_lines: List[str]) -> Dict[str, str]:
102+
Dictionary where each key represent a particular argument and its value represent
103+
the value for that argument defined in the test script.
128104
"""
129-
Parses a list of lines and extracts argument
130-
values from it.
131-
132-
Parameters:
133-
134-
run_args_lines:
135-
Line in test script header that contains run argument definition.
136-
Each line will contain a list of run arguments separated by a space.
137-
Line below is one example of what the run argument line will look like:
138-
"app/all-clusters discriminator KVS storage-path"
139-
140-
In this case the line defines that app, discriminator, KVS, and storage-path
141-
are the arguments that should be used with this run.
142-
143-
An argument can be defined multiple times in the same line or in different lines.
144-
The last definition will override any previous definition. For example,
145-
"KVS/kvs1 KVS/kvs2 KVS/kvs3" line will lead to KVS value of kvs3.
146-
"""
147-
metadata_dict = {}
148-
149-
for run_line in run_args_lines:
150-
for run_arg_word in run_line.strip().split():
151-
'''
152-
We expect the run arg to be defined in one of the
153-
following two formats:
154-
1. run_arg
155-
2. run_arg/run_arg_val
156-
157-
Examples: "discriminator" and "app/all_clusters"
158-
159-
'''
160-
run_arg = run_arg_word.split('/', 1)[0]
161-
metadata_dict[run_arg] = run_arg_word
162-
163-
return metadata_dict
105+
for arg, arg_val in metadata_dict.items():
106+
# We do not expect to recurse (like ${FOO_${BAR}}) so just expand once
107+
for name, value in self.env.items():
108+
arg_val = arg_val.replace(f'${{{name}}}', value)
109+
metadata_dict[arg] = arg_val
164110

165111
def parse_script(self, py_script_path: str) -> List[Metadata]:
166112
"""
@@ -171,47 +117,51 @@ def parse_script(self, py_script_path: str) -> List[Metadata]:
171117
Parameter:
172118
173119
py_script_path:
174-
path to the python test script
120+
path to the python test script
175121
176122
Return:
177123
178124
List[Metadata]
179-
List of Metadata object where each Metadata element represents
180-
the run arguments associated with a particular run defined in
181-
the script file.
125+
List of Metadata object where each Metadata element represents
126+
the run arguments associated with a particular run defined in
127+
the script file.
182128
"""
183129

184130
runs_def_ptrn = re.compile(r'^\s*#\s*test-runner-runs:\s*(.*)$')
185-
args_def_ptrn = re.compile(r'^\s*#\s*test-runner-run/([a-zA-Z0-9_]+):\s*(.*)$')
131+
arg_def_ptrn = re.compile(r'^\s*#\s*test-runner-run/([a-zA-Z0-9_]+)/([a-zA-Z0-9_\-]+):\s*(.*)$')
186132

187-
runs_arg_lines: Dict[str, List[str]] = {}
188-
runs_metadata = []
133+
runs_arg_lines: Dict[str, Dict[str, str]] = {}
134+
runs_metadata: List[Metadata] = []
189135

190136
with open(py_script_path, 'r', encoding='utf8') as py_script:
191137
for line in py_script.readlines():
192-
193138
runs_match = runs_def_ptrn.match(line.strip())
194-
args_match = args_def_ptrn.match(line.strip())
139+
args_match = arg_def_ptrn.match(line.strip())
195140

196141
if runs_match:
197142
for run in runs_match.group(1).strip().split():
198-
runs_arg_lines[run] = []
143+
runs_arg_lines[run] = {}
144+
runs_arg_lines[run]['run'] = run
145+
runs_arg_lines[run]['py_script_path'] = py_script_path
199146

200147
elif args_match:
201-
runs_arg_lines[args_match.group(1)].append(args_match.group(2))
202-
203-
for run, lines in runs_arg_lines.items():
204-
metadata_dict = self.__read_args__(lines)
205-
self.__resolve_env_vals__(metadata_dict)
206-
207-
# store the run value and script location in the
208-
# metadata object
209-
metadata_dict['py_script_path'] = py_script_path
210-
metadata_dict['run'] = run
211-
212-
metadata = Metadata()
213-
214-
metadata.copy_from_dict(metadata_dict)
148+
runs_arg_lines[args_match.group(1)][args_match.group(2)] = args_match.group(3)
149+
150+
for run, attr in runs_arg_lines.items():
151+
self.__resolve_env_vals__(attr)
152+
153+
metadata = Metadata(
154+
py_script_path=attr.get("py_script_path", ""),
155+
run=attr.get("run", ""),
156+
app=attr.get("app", ""),
157+
app_args=attr.get("app_args", ""),
158+
script_args=attr.get("script_args", ""),
159+
factoryreset=bool(attr.get("factoryreset", False)),
160+
factoryreset_app_only=bool(attr.get("factoryreset_app_only", False)),
161+
script_gdb=bool(attr.get("script_gdb", False)),
162+
quiet=bool(attr.get("quiet", True))
163+
)
164+
metadata.copy_from_dict(attr)
215165
runs_metadata.append(metadata)
216166

217167
return runs_metadata

scripts/tests/py/test_metadata.py

+44-23
Original file line numberDiff line numberDiff line change
@@ -12,39 +12,60 @@
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
1414

15+
import os
1516
import tempfile
1617
import unittest
17-
from os import path
18-
from typing import List
1918

2019
from metadata import Metadata, MetadataReader
2120

2221

2322
class TestMetadataReader(unittest.TestCase):
2423

25-
def setUp(self):
26-
# build the reader object
27-
self.reader = MetadataReader(path.join(path.dirname(__file__), "env_test.yaml"))
24+
test_file_content = '''
25+
# test-runner-runs: run1
26+
# test-runner-run/run1/app: ${ALL_CLUSTERS_APP}
27+
# test-runner-run/run1/app-args: --discriminator 1234 --trace-to json:${TRACE_APP}.json
28+
# test-runner-run/run1/script-args: --commissioning-method on-network --trace-to json:${TRACE_TEST_JSON}.json --trace-to perfetto:${TRACE_TEST_PERFETTO}.perfetto
29+
# test-runner-run/run1/factoryreset: True
30+
# test-runner-run/run1/quiet: True
31+
'''
2832

29-
def assertMetadataParse(self, file_content: str, expected: List[Metadata]):
30-
with tempfile.NamedTemporaryFile(mode='w', delete=False) as fp:
33+
env_file_content = '''
34+
ALL_CLUSTERS_APP: out/linux-x64-all-clusters-ipv6only-no-ble-no-wifi-tsan-clang-test/chip-all-clusters-app
35+
CHIP_LOCK_APP: out/linux-x64-lock-ipv6only-no-ble-no-wifi-tsan-clang-test/chip-lock-app
36+
ENERGY_MANAGEMENT_APP: out/linux-x64-energy-management-ipv6only-no-ble-no-wifi-tsan-clang-test/chip-energy-management-app
37+
TRACE_APP: out/trace_data/app-{SCRIPT_BASE_NAME}
38+
TRACE_TEST_JSON: out/trace_data/test-{SCRIPT_BASE_NAME}
39+
TRACE_TEST_PERFETTO: out/trace_data/test-{SCRIPT_BASE_NAME}
40+
'''
41+
42+
expected_metadata = Metadata(
43+
script_args="--commissioning-method on-network --trace-to json:out/trace_data/test-{SCRIPT_BASE_NAME}.json --trace-to perfetto:out/trace_data/test-{SCRIPT_BASE_NAME}.perfetto",
44+
py_script_path="",
45+
app_args="--discriminator 1234 --trace-to json:out/trace_data/app-{SCRIPT_BASE_NAME}.json",
46+
run="run1",
47+
app="out/linux-x64-all-clusters-ipv6only-no-ble-no-wifi-tsan-clang-test/chip-all-clusters-app",
48+
factoryreset=True,
49+
quiet=True
50+
)
51+
52+
def generate_temp_file(self, directory: str, file_content: str) -> str:
53+
fd, temp_file_path = tempfile.mkstemp(dir=directory)
54+
with os.fdopen(fd, 'w') as fp:
3155
fp.write(file_content)
32-
fp.close()
33-
for e in expected:
34-
e.py_script_path = fp.name
35-
actual = self.reader.parse_script(fp.name)
36-
self.assertEqual(actual, expected)
37-
38-
def test_parse_single_run(self):
39-
self.assertMetadataParse('''
40-
# test-runner-runs: run1
41-
# test-runner-run/run1: app/all-clusters discriminator passcode
42-
''',
43-
[
44-
Metadata(app="out/linux-x64-all-clusters-ipv6only-no-ble-no-wifi-tsan-clang-test/chip-all-clusters-app",
45-
discriminator=1234, run="run1", passcode=20202021)
46-
]
47-
)
56+
return temp_file_path
57+
58+
def test_run_arg_generation(self):
59+
with tempfile.TemporaryDirectory() as temp_dir:
60+
temp_file = self.generate_temp_file(temp_dir, self.test_file_content)
61+
env_file = self.generate_temp_file(temp_dir, self.env_file_content)
62+
63+
reader = MetadataReader(env_file)
64+
self.maxDiff = None
65+
66+
self.expected_metadata.py_script_path = temp_file
67+
actual = reader.parse_script(temp_file)[0]
68+
self.assertEqual(self.expected_metadata, actual)
4869

4970

5071
if __name__ == "__main__":

0 commit comments

Comments
 (0)