| # Copyright 2025 The Fuchsia Authors. All rights reserved. | |
| # Use of this source code is governed by a BSD-style license that can be | |
| # found in the LICENSE file. | |
| """ A basic read-eval-print loop to enable or disable test cases and annotate | |
| them with bug info. Can print a list or filtered list; each test case is | |
| labeled with a number NN.MM which is used for the bug and enable/disable | |
| commands. """ | |
| import sys | |
| from typing import Sequence, Tuple | |
| import data_structure | |
| import util | |
| class CLI: | |
| """A command line interface satisfying the main.UI protocol.""" | |
| def __init__(self) -> None: | |
| self.unsaved = False | |
| def inform(self, out: util.Outputs) -> None: | |
| """Show the user the given outputs.""" | |
| for u in out.updates: | |
| print(f" {u[:75]}") | |
| for p in out.prints: | |
| print(p) | |
| for e in out.errors: | |
| if e.startswith("ERROR: "): | |
| print(e) | |
| else: | |
| print(f"ERROR: {e}") | |
| def bail(self, failure_message: str) -> None: | |
| """Show a failure message and exit the program.""" | |
| print(failure_message) | |
| sys.exit(1) | |
| def test_list(self, tests: data_structure.Tests) -> None: | |
| """Let the user interact with the test list.""" | |
| self.tests = tests | |
| self._build_ui_structs() | |
| while True: | |
| self._read_eval_print() | |
| def _print_filtered_list(self, filter: str) -> None: | |
| print_whole_suite = False # Set for every TestSuite | |
| for key, item in self.print_list: | |
| if isinstance(item, data_structure.TestSuite): | |
| print_whole_suite = filter in item.name | |
| if print_whole_suite: | |
| print(f"{key}: {item.name}") | |
| else: # it's a TestCase | |
| if print_whole_suite or filter in item.name: | |
| if item.disabled: | |
| dstr = "D" | |
| else: | |
| dstr = " " | |
| print(f"{key}: {dstr} {item.name} {item.bug}") | |
| def _build_ui_structs(self) -> None: | |
| self.test_case_dict = {} | |
| self.print_list: Sequence[ | |
| Tuple[str, data_structure.TestSuite] | |
| | Tuple[str, data_structure.TestCase] | |
| ] = [] | |
| for i, suite in enumerate(self.tests.tests): | |
| self.print_list.append((str(i), suite)) | |
| for j, case in enumerate(suite.cases): | |
| key = f"{i}.{j}" | |
| self.test_case_dict[key] = case | |
| self.print_list.append((key, case)) | |
| def _set_disabled(self, args: str, value: bool) -> None: | |
| if args in self.test_case_dict: | |
| self.test_case_dict[args].disabled = value | |
| else: | |
| print(f"'{args}' is not a valid test case label.") | |
| def _set_bug(self, args: str) -> None: | |
| lb = args.split(maxsplit=1) | |
| label = lb[0] | |
| bug = lb[1:] | |
| if label in self.test_case_dict: | |
| if bug: | |
| bug_text = bug[0] | |
| else: | |
| bug_text = "" | |
| self.test_case_dict[label].bug = bug_text | |
| else: | |
| print(f"'{args}' is not a valid test case label") | |
| def _read_eval_print(self) -> None: | |
| """Reads a single input and responds appropriately.""" | |
| i = input( | |
| "\n".join( | |
| [ | |
| "b(ug) <label> [<str>] | d(isable)/e(nable) <label> |", | |
| "f(ilter) <str> | l(ist) |", | |
| "q(uit) | q! (q unsaved) | s(ave) | sq(SaveQuit): ", | |
| ] | |
| ) | |
| ) | |
| words = i.split(maxsplit=1) | |
| cmd = words[0] | |
| args = words[1:] # Empty list or one string containing all args. | |
| if cmd not in "b d e f l q q! s sq".split(): | |
| print("I couldn't understand that") | |
| return | |
| if cmd in "b d e".split(): | |
| self.unsaved = True | |
| if cmd == "q": | |
| if self.unsaved: | |
| print("State not saved. Use q! to quit without saving.") | |
| else: | |
| sys.exit(0) | |
| if cmd == "q!": | |
| sys.exit() | |
| if cmd == "s": | |
| self.tests.save() | |
| self.unsaved = False | |
| print("Use 'sq' to save and quit") | |
| if cmd == "sq": | |
| self.tests.save() | |
| sys.exit() | |
| if cmd == "l": | |
| self._print_filtered_list("") | |
| # Above this line, we should have handled all commands without args. | |
| # Commands with arg | |
| if cmd not in "b d e f": | |
| return | |
| elif not args: | |
| print("Command needs arguments") | |
| return | |
| else: | |
| arg_string = args[0] | |
| if cmd == "d": | |
| self._set_disabled(arg_string, True) | |
| if cmd == "e": | |
| self._set_disabled(arg_string, False) | |
| if cmd == "b": | |
| self._set_bug(arg_string) | |
| if cmd == "f": | |
| self._print_filtered_list(arg_string) |