def verify_all(colored_output: str): """Validate all parameters from the specified (maybe) colorized output.""" # Remove trailing \n to make comparisons easier. colored_output = colored_output.strip() # Test coloring / style (do first to create un-colored version to test others). if color is not None: ansi_color, ansi_clear = colorize("!", color=color, style=style).split("!") assert colored_output.startswith(ansi_color) assert colored_output.endswith(ansi_clear) assert color in colored_output if style is not None: assert style in colored_output else: ansi_color, ansi_clear = "", "" assert Ansi.Escape not in colored_output assert Ansi.Clear not in colored_output # Create uncolored version to make later tests easier. stripped_out = colored_output.replace(ansi_color, "").replace(ansi_clear, "") # Need at least (fill_width - 1) / 2 available. if fill_width < 3: fill = None else: if fill_width % 2 == 0: fill = fill_char * (fill_width // 2) else: fill = fill_char * ((fill_width - 1) // 2) # Verify the expected fill was used. if fill is not None: assert colored_output.count(fill) == 2 else: assert fill_char not in colored_output # Verify the padding specified is used. if fill is not None: l_fill, r_fill = stripped_out.split(stage) assert l_pad == l_fill.split(fill)[-1] assert r_pad == r_fill.split(fill)[0] else: if l_pad != "": assert l_pad not in stripped_out if r_pad != "": assert r_pad not in stripped_out assert stripped_out == stage # Verify the width is as expected. expected_width = len(ansi_color) + len(stage) + len(ansi_clear) if fill is not None: expected_width += (len(fill) * 2) + len(l_pad) + len(r_pad) if fill_width % 2 != 0: expected_width += len(fill_char) assert len(colored_output) == expected_width if fill_width >= 3: assert len(stripped_out) == full_width else: assert stripped_out == stage
def test_colorize(color: str, style: str): """Test |colorize| colors as expected for each platform.""" message = "colors!" colored = colorize(message, color=color, style=style) if style == Styles.Regular: assert colored.startswith(f"{Ansi.Escape}{color}m") else: assert colored.startswith(f"{Ansi.Escape}{color};{style}m") assert colored.endswith(f"{Ansi.Clear}") assert message in colored
def run_and_print(exe: Executable, *args, **kwargs) -> str: """Run the executable and print to stdout / stderr, return expected logging.""" proc = exe(*args, **kwargs) assert proc.returncode == 0 print(proc.stderr.decode("utf-8"), file=sys.stderr, end="") print(proc.stdout.decode("utf-8"), end="") if exe.log_calls: popen_args = (exe.exe_path, *args) message = f"{exe.log_prefix}{' '.join(popen_args)}" if exe.log_color: message = colorize(message, color=exe.log_color, style=exe.log_style) return message else: return ""
def test_fail(capsys, why: str, exit_code: int, no_prefix: bool): """Validate |fail| exits as expected.""" with pytest.raises(SystemExit) as se_excinfo: # Make sure calling this raises SystemExit with appropriate code. fail(why, exit_code=exit_code, no_prefix=no_prefix) # Make sure we exited with the expected code. assert se_excinfo.value.code == exit_code # Check printout of sys.stderr and make sure expected message was printed. if no_prefix: prefix = "" else: prefix = colorize("[X] ", color=Colors.Red, style=Styles.Bold) expected_error_message = f"{prefix}{why}\n" captured = capsys.readouterr() assert captured.out == "" assert captured.err == expected_error_message
def test_which(capsys): """Validate that |which| finds or does not find executables.""" # Make sure ci_exec.core.which and shutil.which agree (how could then not? xD). git = which("git") git_path = shutil.which("git") assert git.exe_path == git_path # This command should not exist. Right? no_cmd = "ja" * 22 with pytest.raises(SystemExit) as se_excinfo: which(no_cmd) assert se_excinfo.value.code == 1 captured = capsys.readouterr() assert captured.out == "" prefix = colorize("[X] ", color=Colors.Red, style=Styles.Bold) expected_error_message = "{prefix}Could not find '{no_cmd}' in $PATH.\n".format( prefix=prefix, no_cmd=no_cmd) assert captured.err == expected_error_message # Test manual $PATH override / make sure same python is found. actual_python = Path(sys.executable) python_name = actual_python.name python_dir = str(actual_python.parent) python = which(python_name, path=python_dir, log_calls=False) assert python.exe_path == str(actual_python) # Throwing in the __str__ test here because it doesn't deserve its own test method. assert str(python) == "Executable('{py}')".format(py=str(actual_python)) proc = python("-c", "import sys; print(sys.version_info)", stdout=PIPE, stderr=PIPE) assert proc.returncode == 0 assert proc.stderr == b"" assert proc.stdout.decode("utf-8").strip() == "{v}".format( v=sys.version_info) # :) with pytest.raises(TypeError) as te_excinfo: which("git", log_callz=False) assert "unexpected keyword argument 'log_callz'" in str(te_excinfo.value)
def test_dump_predefined_color_styles(capsys): """Validate :func:`~ci_exec.colorize.dump_predefined_color_styles` dumps all.""" dump_predefined_color_styles() captured = capsys.readouterr() assert captured.err == "" # Collect the printed results. colors_seen = {} spec_regex = re.compile(r"color=([a-zA-Z]+), style=([a-zA-Z]+)") for match in spec_regex.finditer(captured.out): color, style = match.groups() if color in colors_seen: colors_seen[color].append(style) else: colors_seen[color] = [style] # Make sure every color in every style was presented. all_colors = Colors.all_colors() all_styles = set(Styles.all_styles()) assert len(colors_seen) == len(all_colors) for color_name, style_names in colors_seen.items(): # The style names are printed, get the values styles_seen = [getattr(Styles, style) for style in style_names] assert len(styles_seen) == len(style_names) assert set(styles_seen) == all_styles # Check the actual colors showed up. color = getattr(Colors, color_name) for style_name, style in zip(style_names, styles_seen): expected = colorize( "color={color_name}, style={style_name}".format( color_name=color_name, style_name=style_name ), color=color, style=style ) assert expected in captured.out
def test_filter_file(capsys): """Validate that |filter_file| patches / errors as expected.""" # Non-existent files cannot be patched. with pytest.raises(SystemExit): filter_file("i_dont_exist", "boom", "blam") red_x = colorize("[X] ", color=Colors.Red, style=Styles.Bold) captured = capsys.readouterr() assert captured.out == "" err = "{red_x}Cannot filter 'i_dont_exist', no such file!".format( red_x=red_x) assert captured.err.strip() == err # Backup extension must not be empty string. with pytest.raises(SystemExit): filter_file("tox.ini", "boom", "blam", backup_extension="") captured = capsys.readouterr() assert captured.out == "" err = "{red_x}filter_file: 'backup_extension' may not be the empty string.".format( red_x=red_x) assert captured.err.strip() == err def read_both(cml: Path, bku: Path) -> Tuple[str, str]: """Open and read both files, returning the results.""" with cml.open() as cml_f: cml_contents = cml_f.read() with bku.open() as bku_f: bku_contents = bku_f.read() return (cml_contents, bku_contents) for line_based in (True, False): # Filtering nothing should error. filter_town, cmake_lists_txt = _make_dummy() with pytest.raises(SystemExit): filter_file(cmake_lists_txt, "", "", line_based=line_based) captured = capsys.readouterr() assert captured.out == "" assert "filter_file: no changes made to '" in captured.err assert "CMakeLists.txt'" in captured.err # Invalid replacement should trigger failure. filter_town, cmake_lists_txt = _make_dummy() with pytest.raises(SystemExit): filter_file(cmake_lists_txt, "export", lambda x: 11, line_based=line_based) captured = capsys.readouterr() assert captured.out == "" assert captured.err.startswith( "{red_x}Unable to filter".format(red_x=red_x)) assert "expected str instance, int found" in captured.err # No filtering with demand_different=False should not error. filter_town, cmake_lists_txt = _make_dummy() backup = filter_file(cmake_lists_txt, "", "", demand_different=False, line_based=line_based) cml, bku = read_both(cmake_lists_txt, backup) assert cml == bku # Test an actual patch. filter_town, cmake_lists_txt = _make_dummy() backup = filter_file(cmake_lists_txt, "super_project", "SUPER_PROJECT", line_based=line_based) cml, bku = read_both(cmake_lists_txt, backup) assert cml != bku assert bku == _please_stop assert cml == _please_stop.replace("super_project", "SUPER_PROJECT") # Cleanup rm_rf(filter_town)
def startswith(out: str, prefix: str, color: str, style: str) -> bool: """External check for cross-validating run_and_print.""" colored_prefix = colorize(prefix, color=color, style=style) colored = colored_prefix.split(Ansi.Clear)[0] return out.startswith(colored)