15
15
16
16
import re
17
17
from dataclasses import dataclass
18
- from typing import Dict , List , Optional
18
+ from typing import Any , Dict , List
19
19
20
20
import yaml
21
21
22
22
23
23
@dataclass
24
24
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 :
32
36
"""
33
37
Sets the value of the attributes from a dictionary.
34
38
35
39
Attributes:
36
40
37
41
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.
40
44
"""
41
-
42
45
if "app" in attr_dict :
43
46
self .app = attr_dict ["app" ]
44
47
45
48
if "run" in attr_dict :
46
49
self .run = attr_dict ["run" ]
47
50
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 " ]
50
53
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 " ]
53
56
54
57
if "py_script_path" in attr_dict :
55
58
self .py_script_path = attr_dict ["py_script_path" ]
56
59
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" ])
58
71
59
72
60
73
class MetadataReader :
61
74
"""
62
- A class to parse run arguments from the test scripts and
75
+ A class to parse run arguments from the test scripts and
63
76
resolve them to environment specific values.
64
77
"""
65
78
@@ -70,97 +83,30 @@ def __init__(self, env_yaml_file_path: str):
70
83
Parameters:
71
84
72
85
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.
74
87
"""
75
88
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 )
77
90
78
91
def __resolve_env_vals__ (self , metadata_dict : Dict [str , str ]) -> None :
79
92
"""
80
93
Resolves the argument defined in the test script to environment values.
81
94
For example, if a test script defines "all_clusters" as the value for app
82
95
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
84
97
to this raw value.
85
98
86
99
Parameter:
87
100
88
101
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.
128
104
"""
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
164
110
165
111
def parse_script (self , py_script_path : str ) -> List [Metadata ]:
166
112
"""
@@ -171,47 +117,51 @@ def parse_script(self, py_script_path: str) -> List[Metadata]:
171
117
Parameter:
172
118
173
119
py_script_path:
174
- path to the python test script
120
+ path to the python test script
175
121
176
122
Return:
177
123
178
124
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.
182
128
"""
183
129
184
130
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*(.*)$' )
186
132
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 ] = []
189
135
190
136
with open (py_script_path , 'r' , encoding = 'utf8' ) as py_script :
191
137
for line in py_script .readlines ():
192
-
193
138
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 ())
195
140
196
141
if runs_match :
197
142
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
199
146
200
147
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 )
215
165
runs_metadata .append (metadata )
216
166
217
167
return runs_metadata
0 commit comments