Merge ~jslarraz/review-tools:rework-snap-review into review-tools:master
- Git
- lp:~jslarraz/review-tools
- rework-snap-review
- Merge into master
Proposed by
Jorge Sancho Larraz
Status: | Merged |
---|---|
Merged at revision: | bac6469d99efc75598fd4cd78605c69ade4eb377 |
Proposed branch: | ~jslarraz/review-tools:rework-snap-review |
Merge into: | review-tools:master |
Diff against target: |
372 lines (+117/-139) 2 files modified
bin/snap-review (+117/-119) reviewtools/modules.py (+0/-20) |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Alex Murray | Approve | ||
Review via email: mp+466751@code.launchpad.net |
Commit message
Improve bin/snap-review in preparation for using a common report class shared across all reviews
Description of the change
To post a comment you must log in.
Revision history for this message
Jorge Sancho Larraz (jslarraz) wrote : | # |
Yes, it was hehe
Originally, we initialized review classes with `modules.
Thus, this update to snap-review requires of updating tests/test.
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | diff --git a/bin/snap-review b/bin/snap-review |
2 | index 37c9fea..2c26158 100755 |
3 | --- a/bin/snap-review |
4 | +++ b/bin/snap-review |
5 | @@ -1,6 +1,6 @@ |
6 | #!/usr/bin/python3 |
7 | |
8 | -from reviewtools import modules |
9 | +from reviewtools import modules as modules_mod |
10 | import argparse |
11 | import copy |
12 | import json |
13 | @@ -21,33 +21,53 @@ from reviewtools.common import ( |
14 | from reviewtools.sr_declaration import validate_json |
15 | |
16 | |
17 | -def print_findings(results, description): |
18 | - """ |
19 | - Print a summary of the issues found. |
20 | - """ |
21 | - |
22 | - if not description or not results: |
23 | - return "" |
24 | - print(description) |
25 | - print("".center(len(description), "-")) |
26 | - for key in sorted(results.keys()): |
27 | - print(" - %s" % key) |
28 | - print("\t%s" % results[key]["text"]) |
29 | - if "link" in results[key]: |
30 | - print("\t%s" % results[key]["link"]) |
31 | - |
32 | - |
33 | -class Results(object): |
34 | +class Report(object): |
35 | results = {} |
36 | errors = {} |
37 | warnings = {} |
38 | info = {} |
39 | - rc = 0 |
40 | |
41 | - def __init__(self, args): |
42 | - self.args = args |
43 | - self.pkg_fn = self.args.filename |
44 | - self.modules = modules.get_modules() |
45 | + def __init__(self, filename, verbose: bool = False): |
46 | + self.filename = filename |
47 | + self.verbose = verbose |
48 | + |
49 | + # review_report[<section>][<result_type>][<review_name>] = <result> |
50 | + # section: section of the report (one per review module) |
51 | + # result_type: info, warn, error |
52 | + # review_name: name of the check (prefixed with self.review_type) |
53 | + # result: contents of the review |
54 | + # link: url for more information |
55 | + # manual_review: force manual review |
56 | + # override_result_type: prefix results with [<result_type>] and set |
57 | + # result_type to override_result_type |
58 | + def add_result( |
59 | + self, |
60 | + section, |
61 | + result_type, |
62 | + review_name, |
63 | + result, |
64 | + link=None, |
65 | + manual_review=False, |
66 | + ): |
67 | + """Add result to report""" |
68 | + if section not in self.results: |
69 | + self.results[section] = { |
70 | + "info": {}, |
71 | + "warn": {}, |
72 | + "error": {}, |
73 | + } |
74 | + |
75 | + if result_type not in self.results[section]: |
76 | + raise Exception("Invalid result type '%s'" % result_type) |
77 | + |
78 | + if review_name not in self.results[section][result_type]: |
79 | + self.results[section][result_type][review_name] = dict() |
80 | + |
81 | + self.results[section][result_type][review_name].update( |
82 | + {"text": "%s" % result, "manual_review": manual_review} |
83 | + ) |
84 | + if link is not None: |
85 | + self.results[section][result_type][review_name]["link"] = link |
86 | |
87 | def _summarise_results(self): |
88 | for module in self.results: |
89 | @@ -55,94 +75,51 @@ class Results(object): |
90 | self.errors[key] = self.results[module]["error"][key] |
91 | for key in self.results[module]["warn"]: |
92 | self.warnings[key] = self.results[module]["warn"][key] |
93 | - if self.args.verbose: |
94 | + if self.verbose: |
95 | for key in self.results[module]["info"]: |
96 | self.info[key] = self.results[module]["info"][key] |
97 | |
98 | - def complete_report(self): |
99 | + def print_findings(self, results, description): |
100 | + """ |
101 | + Print a summary of the issues found. |
102 | + """ |
103 | + if not description or not results: |
104 | + return "" |
105 | + print(description) |
106 | + print("".center(len(description), "-")) |
107 | + for key in sorted(results.keys()): |
108 | + print(" - %s" % key) |
109 | + print("\t%s" % results[key]["text"]) |
110 | + if "link" in results[key]: |
111 | + print("\t%s" % results[key]["link"]) |
112 | + |
113 | + def show_results(self, report_type="console"): |
114 | self._summarise_results() |
115 | |
116 | - if self.args.json: |
117 | + if report_type == "json": |
118 | print( |
119 | json.dumps( |
120 | self.results, sort_keys=True, indent=2, separators=(",", ": ") |
121 | ) |
122 | ) |
123 | - elif self.args.sdk: |
124 | + elif report_type == "sdk": |
125 | for section in sorted(self.results.keys()): |
126 | output = self.results[section] |
127 | print("= %s =" % section) |
128 | print( |
129 | json.dumps(output, sort_keys=True, indent=2, separators=(",", ": ")) |
130 | ) |
131 | - if output["error"] or output["warn"]: |
132 | - self.rc = 1 |
133 | else: |
134 | - print_findings(self.errors, "Errors") |
135 | - print_findings(self.warnings, "Warnings") |
136 | - if self.args.verbose: |
137 | - print_findings(self.info, "Info") |
138 | - if self.rc == 1: |
139 | - print("%s: RUNTIME ERROR" % self.args.filename) |
140 | + self.print_findings(self.errors, "Errors") |
141 | + self.print_findings(self.warnings, "Warnings") |
142 | + if self.verbose: |
143 | + self.print_findings(self.info, "Info") |
144 | + if "runtime-errors" in self.results: |
145 | + print("%s: RUNTIME ERROR" % self.filename) |
146 | elif self.warnings or self.errors: |
147 | - print("%s: FAIL" % self.args.filename) |
148 | + print("%s: FAIL" % self.filename) |
149 | else: |
150 | - print("%s: pass" % self.args.filename) |
151 | - if self.rc == 1: |
152 | - # always exit(1) if there are errors |
153 | - pass |
154 | - elif self.errors: |
155 | - self.rc = 2 |
156 | - elif self.warnings: |
157 | - self.rc = 3 |
158 | - |
159 | - def _run_module_checks(self, module, overrides, report_type): |
160 | - # What we are doing here is basically what all the |
161 | - # ./bin/snap-check-* scripts do as well, so for |
162 | - # example something like: |
163 | - # |
164 | - # review = sr_lint.SnapLint(sys.argv[1]) |
165 | - # review.do_checks() |
166 | - # rc = review.do_report() |
167 | - # |
168 | - section = module.replace("sr_", "snap.v2_") |
169 | - try: |
170 | - review = modules.init_main_class( |
171 | - module, self.pkg_fn, overrides=overrides, report_type=report_type |
172 | - ) |
173 | - |
174 | - if review: |
175 | - review.do_checks() |
176 | - self.results[section] = review.review_report |
177 | - return section |
178 | - except Exception: |
179 | - print("Caught exception (setting rc=1 and continuing):") |
180 | - traceback.print_exc(file=sys.stdout) |
181 | - self.rc = 1 |
182 | - return None |
183 | - |
184 | - def run_all_checks(self, overrides): |
185 | - if self.args.json: |
186 | - report_type = "json" |
187 | - elif self.args.sdk: |
188 | - # this should probably be "json", but leave as None since it has |
189 | - # always been that way |
190 | - report_type = None |
191 | - else: |
192 | - report_type = "console" |
193 | - |
194 | - for module in self.modules: |
195 | - self._run_module_checks(module, overrides, report_type) |
196 | - |
197 | - def add_runtime_error(self, args, name, msg): |
198 | - section = "runtime-errors" |
199 | - if section not in self.results: |
200 | - self.results[section] = {} |
201 | - self.results[section] = { |
202 | - "error": {name: {"text": msg, "manual_review": False}}, |
203 | - "info": {}, |
204 | - "warn": {}, |
205 | - } |
206 | + print("%s: pass" % self.filename) |
207 | |
208 | |
209 | def main(): |
210 | @@ -201,18 +178,13 @@ def main(): |
211 | "file '%s' does not exist." % args.filename, output_type=error_output_type |
212 | ) |
213 | |
214 | - results = Results(args) |
215 | - if not results.modules: |
216 | + modules = modules_mod.get_modules() |
217 | + if not modules: |
218 | print("No 'reviewtools' modules found.") |
219 | sys.exit(1) |
220 | |
221 | - overrides = None |
222 | - if args.overrides: |
223 | - overrides = json.loads(args.overrides) |
224 | - |
225 | + overrides = json.loads(args.overrides) if args.overrides else {} |
226 | if args.plugs: |
227 | - if overrides is None: |
228 | - overrides = {} |
229 | try: |
230 | with open(args.plugs, "r") as plugs_file: |
231 | overrides["snap_decl_plugs"] = json.loads(plugs_file.read()) |
232 | @@ -220,8 +192,6 @@ def main(): |
233 | except Exception as e: |
234 | error("Failed to read plugs: %s" % e, output_type=error_output_type) |
235 | if args.slots: |
236 | - if overrides is None: |
237 | - overrides = {} |
238 | try: |
239 | with open(args.slots, "r") as slots_file: |
240 | overrides["snap_decl_slots"] = json.loads(slots_file.read()) |
241 | @@ -229,16 +199,10 @@ def main(): |
242 | except Exception as e: |
243 | error("Failed to read slots: %s" % e, output_type=error_output_type) |
244 | if args.allow_classic: |
245 | - if overrides is None: |
246 | - overrides = {} |
247 | overrides["snap_allow_classic"] = args.allow_classic |
248 | if args.on_store: |
249 | - if overrides is None: |
250 | - overrides = {} |
251 | overrides["snap_on_store"] = args.on_store |
252 | if args.on_brand: |
253 | - if overrides is None: |
254 | - overrides = {} |
255 | overrides["snap_on_brand"] = args.on_brand |
256 | |
257 | # --state-input alone is unsupported |
258 | @@ -249,9 +213,6 @@ def main(): |
259 | ) |
260 | |
261 | if args.state_output: |
262 | - if overrides is None: |
263 | - overrides = {} |
264 | - |
265 | if args.state_input: |
266 | # --state-input --state-output indicates we have previous state |
267 | if not os.path.exists(args.state_input): |
268 | @@ -280,7 +241,20 @@ def main(): |
269 | overrides["state_output"] = copy.deepcopy(overrides["state_input"]) |
270 | |
271 | # Run the tests |
272 | - results.run_all_checks(overrides) |
273 | + rc = 0 |
274 | + report = Report(args.filename, args.verbose) |
275 | + for module in modules: |
276 | + section = module.replace("sr_", "snap.v2_") |
277 | + try: |
278 | + review_class = modules_mod.find_main_class(module) |
279 | + review = review_class(args.filename, overrides=overrides) |
280 | + |
281 | + review.do_checks() |
282 | + report.results[section] = review.review_report |
283 | + except Exception: |
284 | + print("Caught exception (setting rc=1 and continuing):") |
285 | + traceback.print_exc(file=sys.stdout) |
286 | + rc = 1 |
287 | |
288 | # Write out our state |
289 | if args.state_output: |
290 | @@ -290,8 +264,11 @@ def main(): |
291 | try: |
292 | verify_override_state(overrides["state_output"]) |
293 | except Exception as e: |
294 | - results.add_runtime_error( |
295 | - args, "state-output-verify", "state-output failed to verify: %s" % e |
296 | + report.add_result( |
297 | + "runtime-errors", |
298 | + "error", |
299 | + "state-output-verify", |
300 | + "state-output failed to verify: %s" % e, |
301 | ) |
302 | overrides["state_output"] = copy.deepcopy(overrides["state_input"]) |
303 | |
304 | @@ -312,14 +289,35 @@ def main(): |
305 | out_f.flush() |
306 | shutil.copyfile(out_f.name, args.state_output) |
307 | except Exception as e: |
308 | - results.add_runtime_error( |
309 | - args, "state-output-write", "Could not write state output: %s" % e |
310 | + report.add_result( |
311 | + "runtime-errors", |
312 | + "error", |
313 | + "state-output-write", |
314 | + "Could not write state output: %s" % e, |
315 | ) |
316 | |
317 | # Show the results of the report |
318 | - results.complete_report() |
319 | - |
320 | - sys.exit(results.rc) |
321 | + if args.json: |
322 | + report_type = "json" |
323 | + elif args.sdk: |
324 | + report_type = "sdk" |
325 | + if report.errors or report.warnings: |
326 | + # TODO: harmonize return codes |
327 | + rc = 1 |
328 | + else: |
329 | + report_type = "console" |
330 | + |
331 | + # Show results updates of report.errors and report.warnings |
332 | + report.show_results(report_type) |
333 | + |
334 | + if rc == 1: |
335 | + # always exit(1) if there are errors |
336 | + pass |
337 | + elif report.errors: |
338 | + rc = 2 |
339 | + elif report.warnings: |
340 | + rc = 3 |
341 | + sys.exit(rc) |
342 | |
343 | |
344 | if __name__ == "__main__": |
345 | diff --git a/reviewtools/modules.py b/reviewtools/modules.py |
346 | index 9b93f32..e0d56d2 100644 |
347 | --- a/reviewtools/modules.py |
348 | +++ b/reviewtools/modules.py |
349 | @@ -83,23 +83,3 @@ def find_main_class(module_name): |
350 | return None |
351 | init_object = getattr(module, test_class[0][0]) |
352 | return init_object |
353 | - |
354 | - |
355 | -def init_main_class(module_name, pkg_file, overrides=None, report_type=None): |
356 | - """ |
357 | - This function will instantiate the main Snap*Review |
358 | - class of a given module and instantiate it with the |
359 | - location of the file we want to inspect. |
360 | - """ |
361 | - |
362 | - init_object = find_main_class(module_name) |
363 | - if not init_object: |
364 | - return None |
365 | - try: |
366 | - ob = init_object(pkg_file, overrides) |
367 | - # set the report_type separately since it is in the common class |
368 | - ob.set_report_type(report_type) |
369 | - except TypeError as e: |
370 | - print("Could not init %s: %s" % (init_object, str(e))) |
371 | - raise |
372 | - return ob |
LGTM (was it intentional to include the change to reviewtools/sr_lint etc around desktop files here?)