|
14 | 14 | # limitations under the License.
|
15 | 15 |
|
16 | 16 | import re
|
| 17 | +import sys |
17 | 18 | from dataclasses import dataclass
|
18 | 19 | from typing import Any, Dict, List
|
19 | 20 |
|
20 | 21 | import yaml
|
21 | 22 |
|
22 | 23 |
|
| 24 | +def bool_from_str(value: str) -> bool: |
| 25 | + """Convert True/true/False/false strings to bool.""" |
| 26 | + return value.strip().lower() == "true" |
| 27 | + |
| 28 | + |
23 | 29 | @dataclass
|
24 | 30 | class Metadata:
|
25 | 31 | py_script_path: str
|
@@ -58,16 +64,59 @@ def copy_from_dict(self, attr_dict: Dict[str, Any]) -> None:
|
58 | 64 | self.py_script_path = attr_dict["py_script_path"]
|
59 | 65 |
|
60 | 66 | if "factoryreset" in attr_dict:
|
61 |
| - self.factoryreset = bool(attr_dict["factoryreset"]) |
| 67 | + self.factoryreset = bool_from_str(attr_dict["factoryreset"]) |
62 | 68 |
|
63 | 69 | if "factoryreset_app_only" in attr_dict:
|
64 |
| - self.factoryreset_app_only = bool(attr_dict["factoryreset_app_only"]) |
| 70 | + self.factoryreset_app_only = bool_from_str(attr_dict["factoryreset_app_only"]) |
65 | 71 |
|
66 | 72 | if "script_gdb" in attr_dict:
|
67 |
| - self.script_gdb = bool(attr_dict["script_gdb"]) |
| 73 | + self.script_gdb = bool_from_str(attr_dict["script_gdb"]) |
68 | 74 |
|
69 | 75 | if "quiet" in attr_dict:
|
70 |
| - self.quiet = bool(attr_dict["quiet"]) |
| 76 | + self.quiet = bool_from_str(attr_dict["quiet"]) |
| 77 | + |
| 78 | + |
| 79 | +def extract_runs_arg_lines(py_script_path: str) -> Dict[str, Dict[str, str]]: |
| 80 | + """Extract the run arguments from the CI test arguments blocks.""" |
| 81 | + |
| 82 | + found_ci_args_section = False |
| 83 | + done_ci_args_section = False |
| 84 | + |
| 85 | + runs_def_ptrn = re.compile(r'^\s*#\s*test-runner-runs:\s*(?P<run_id>.*)$') |
| 86 | + arg_def_ptrn = re.compile( |
| 87 | + r'^\s*#\s*test-runner-run/(?P<run_id>[a-zA-Z0-9_]+)/(?P<arg_name>[a-zA-Z0-9_\-]+):\s*(?P<arg_val>.*)$') |
| 88 | + |
| 89 | + runs_arg_lines: Dict[str, Dict[str, str]] = {} |
| 90 | + |
| 91 | + with open(py_script_path, 'r', encoding='utf8') as py_script: |
| 92 | + for line_idx, line in enumerate(py_script.readlines()): |
| 93 | + line = line.strip() |
| 94 | + line_num = line_idx + 1 |
| 95 | + |
| 96 | + # Detect the single CI args section, to skip the lines otherwise. |
| 97 | + if not done_ci_args_section and line.startswith("# === BEGIN CI TEST ARGUMENTS ==="): |
| 98 | + found_ci_args_section = True |
| 99 | + elif found_ci_args_section and line.startswith("# === END CI TEST ARGUMENTS ==="): |
| 100 | + done_ci_args_section = True |
| 101 | + found_ci_args_section = False |
| 102 | + |
| 103 | + runs_match = runs_def_ptrn.match(line) |
| 104 | + args_match = arg_def_ptrn.match(line) |
| 105 | + |
| 106 | + if not found_ci_args_section and (runs_match or args_match): |
| 107 | + print(f"WARNING: {py_script_path}:{line_num}: Found CI args outside of CI TEST ARGUMENTS block!", file=sys.stderr) |
| 108 | + continue |
| 109 | + |
| 110 | + if runs_match: |
| 111 | + for run in runs_match.group("run_id").strip().split(): |
| 112 | + runs_arg_lines[run] = {} |
| 113 | + runs_arg_lines[run]['run'] = run |
| 114 | + runs_arg_lines[run]['py_script_path'] = py_script_path |
| 115 | + |
| 116 | + elif args_match: |
| 117 | + runs_arg_lines[args_match.group("run_id")][args_match.group("arg_name")] = args_match.group("arg_val") |
| 118 | + |
| 119 | + return runs_arg_lines |
71 | 120 |
|
72 | 121 |
|
73 | 122 | class MetadataReader:
|
@@ -126,33 +175,15 @@ def parse_script(self, py_script_path: str) -> List[Metadata]:
|
126 | 175 | the run arguments associated with a particular run defined in
|
127 | 176 | the script file.
|
128 | 177 | """
|
129 |
| - |
130 |
| - runs_def_ptrn = re.compile(r'^\s*#\s*test-runner-runs:\s*(.*)$') |
131 |
| - arg_def_ptrn = re.compile(r'^\s*#\s*test-runner-run/([a-zA-Z0-9_]+)/([a-zA-Z0-9_\-]+):\s*(.*)$') |
132 |
| - |
133 |
| - runs_arg_lines: Dict[str, Dict[str, str]] = {} |
134 | 178 | runs_metadata: List[Metadata] = []
|
135 |
| - |
136 |
| - with open(py_script_path, 'r', encoding='utf8') as py_script: |
137 |
| - for line in py_script.readlines(): |
138 |
| - runs_match = runs_def_ptrn.match(line.strip()) |
139 |
| - args_match = arg_def_ptrn.match(line.strip()) |
140 |
| - |
141 |
| - if runs_match: |
142 |
| - for run in runs_match.group(1).strip().split(): |
143 |
| - runs_arg_lines[run] = {} |
144 |
| - runs_arg_lines[run]['run'] = run |
145 |
| - runs_arg_lines[run]['py_script_path'] = py_script_path |
146 |
| - |
147 |
| - elif args_match: |
148 |
| - runs_arg_lines[args_match.group(1)][args_match.group(2)] = args_match.group(3) |
| 179 | + runs_arg_lines = extract_runs_arg_lines(py_script_path) |
149 | 180 |
|
150 | 181 | for run, attr in runs_arg_lines.items():
|
151 | 182 | self.__resolve_env_vals__(attr)
|
152 | 183 |
|
153 | 184 | metadata = Metadata(
|
154 | 185 | py_script_path=attr.get("py_script_path", ""),
|
155 |
| - run=attr.get("run", ""), |
| 186 | + run=run, |
156 | 187 | app=attr.get("app", ""),
|
157 | 188 | app_args=attr.get("app_args", ""),
|
158 | 189 | script_args=attr.get("script_args", ""),
|
|
0 commit comments