diff options
| -rw-r--r-- | .gitignore | 6 | ||||
| -rw-r--r-- | .travis.yml | 29 | ||||
| -rw-r--r-- | README.md | 37 | ||||
| -rw-r--r-- | minishell_test/__init__.py | 0 | ||||
| -rwxr-xr-x | minishell_test/__main__.py | 26 | ||||
| -rw-r--r-- | minishell_test/args.py | 23 | ||||
| -rw-r--r-- | minishell_test/config.py | 4 | ||||
| -rw-r--r-- | minishell_test/hooks.py | 4 | ||||
| -rw-r--r-- | minishell_test/sandbox.py | 4 | ||||
| -rw-r--r-- | minishell_test/suite/__init__.py | 4 | ||||
| -rw-r--r-- | minishell_test/suite/decorator.py | 8 | ||||
| -rw-r--r-- | minishell_test/suite/suite.py | 53 | ||||
| -rw-r--r-- | minishell_test/suites/__init__.py | 2 | ||||
| -rw-r--r-- | minishell_test/suites/builtin.py | 10 | ||||
| -rw-r--r-- | minishell_test/suites/cmd.py | 8 | ||||
| -rw-r--r-- | minishell_test/suites/flow.py | 14 | ||||
| -rw-r--r-- | minishell_test/suites/misc.py | 4 | ||||
| -rw-r--r-- | minishell_test/suites/path.py | 10 | ||||
| -rw-r--r-- | minishell_test/suites/preprocess.py | 8 | ||||
| -rw-r--r-- | minishell_test/test/__init__.py | 4 | ||||
| -rw-r--r-- | minishell_test/test/captured.py | 10 | ||||
| -rw-r--r-- | minishell_test/test/result.py | 24 | ||||
| -rw-r--r-- | minishell_test/test/test.py | 34 | ||||
| -rw-r--r-- | requirements.txt | 3 | ||||
| -rw-r--r-- | setup.cfg | 16 | ||||
| -rw-r--r-- | setup.py | 10 | ||||
| -rw-r--r-- | tox.ini | 28 |
27 files changed, 242 insertions, 141 deletions
@@ -1,6 +1,8 @@ *__pycache__* *.log -bin +bin/ tags -dist +dist/ *.egg-info +build/ +.tox/ diff --git a/.travis.yml b/.travis.yml index 1c3d705..0fb4c0e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,19 +1,32 @@ language: python + +os: + - linux + - osx + python: - "3.6" - "3.7" - "3.8" - "3.9" +addons: + apt: + packages: + - valgrind + +before_install: + - if [ "$TRAVIS_OS_NAME" = "osx" ]; then brew update ; fi + - if [ "$TRAVIS_OS_NAME" = "osx" ]; then brew install valgrind; fi + install: + - pip install -e . - pip install -r requirements.txt - - git clone --recurse-submodule https://github.com/ouaisbrefbams/minishell ../minishell - - sed -i 's/-Werror//' ../minishell/Makefile ../minishell/libft/Makefile - - sudo apt update - - sudo apt install valgrind + - git clone --recurse-submodule https://github.com/ouaisbrefbams/minishell /tmp/minishell + - sed -i 's/-Werror//' /tmp/minishell/Makefile /tmp/minishell/libft/Makefile script: - - python -m flake8 - - python -m mypy src - - ./run - - ./run -k pwd + - python -m flake8 minishell_test + - python -m mypy minishell_test + - python -m minishell_test -p /tmp/minishell + - python -m minishell_test -p /tmp/minishell -k pwd @@ -4,6 +4,22 @@ Test for the minishell project of school 42.  +## Installation + +### pip + +``` +$ pip3 install minishell-test +``` + +### Manual + +``` +$ git clone https://github.com/cacharle/minishell_test +$ cd minishell_test +$ pip3 install -e minishell_test +``` + ## Usage The default path to your project is `../minishell` but you can change it the the [configuration](src/config.py). @@ -12,16 +28,9 @@ The default path to your project is `../minishell` but you can change it the the $ ./run # run all tests $ ./run --help -usage: run [-h] [-k] [-x] [-r BEGIN END] [--show-range] [-v] [-b] [-n] [-l] - [-m] [-p] - [suite [suite ...]] +usage: run [-h] [-k] [-r BEGIN END] [--show-range] [-x] [-v] [-b] [-n] [-l] [-m] [-g] [suite ...] -___ ____ _ _ _ _ _ _ -| \/ (_) (_) | | | | | | | | | -| . . |_ _ __ _ ___| |__ ___| | | | |_ ___ ___| |_ -| |\/| | | '_ \| / __| '_ \ / _ \ | | | __/ _ \/ __| __| -| | | | | | | | \__ \ | | | __/ | | | || __/\__ \ |_ -\_| |_/_|_| |_|_|___/_| |_|\___|_|_| \__\___||___/\__| +Test for the minishell project of school 42. positional arguments: suite Test suites/group to run. @@ -31,20 +40,20 @@ positional arguments: optional arguments: -h, --help show this help message and exit -k, --check-leaks Run valgrind on tests (disable usual comparison with bash) - -x, --exit-first Exit on first fail -r BEGIN END, --range BEGIN END Range of test index to run (imply --show-index) --show-range Show test index (useful with --range) + -x, --exit-first Exit on first fail -v, --verbose Increase verbosity level (e.g -vv == 2) -b, --bonus Enable bonus tests -n, --no-bonus Disable bonus tests -l, --list Print available test suites -m, --make Make minishell and exit - -p, --pager After running the test, display the result in a pager of your choice + -g, --pager After running the test, display the result in a pager of your choice -Signal handling is not tested -There is a commented glob suite in src/suites/preprocess.py. -Good luck handling `*'.*'`. +Made by cacharle +https://github.com/cacharle +https://cacharle.xyz ``` ## Test compatibility diff --git a/minishell_test/__init__.py b/minishell_test/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/minishell_test/__init__.py diff --git a/minishell_test/__main__.py b/minishell_test/__main__.py index da7f173..7d7b619 100755 --- a/minishell_test/__main__.py +++ b/minishell_test/__main__.py @@ -18,19 +18,25 @@ import shutil import distutils.spawn import subprocess -import config -import sandbox -from args import parse_args -from suite import Suite -from suites import * # noqa: F403,F401 +import minishell_test.config as config +import minishell_test.sandbox as sandbox +from minishell_test.args import parse_args +from minishell_test.suite.suite import Suite, SuiteException +from minishell_test.suites import * # noqa: F403,F401 -def main(): +def main(argv=None): args = parse_args() if args.list: Suite.list() sys.exit(0) + config.MINISHELL_DIR = args.path + config.MINISHELL_PATH = os.path.abspath( + os.path.join(config.MINISHELL_DIR, config.MINISHELL_EXEC) + ) + config.VALGRIND_CMD[-1] = config.MINISHELL_PATH + if config.MINISHELL_MAKE or args.make: try: print("{:=^{width}}".format("MAKE", width=config.TERM_COLS)) @@ -72,10 +78,16 @@ def main(): if config.RANGE is not None or config.CHECK_LEAKS: config.SHOW_RANGE = True - Suite.setup(args.suites) + try: + Suite.setup(args.suites) + except SuiteException as e: + print(e) + sys.exit(1) try: Suite.run_all() except KeyboardInterrupt: + pass + finally: sandbox.remove() Suite.summarize() diff --git a/minishell_test/args.py b/minishell_test/args.py index b7fcca6..3dd124f 100644 --- a/minishell_test/args.py +++ b/minishell_test/args.py @@ -6,31 +6,24 @@ # By: charles <charles.cabergs@gmail.com> +#+ +:+ +#+ # # +#+#+#+#+#+ +#+ # # Created: 2020/07/15 18:24:32 by charles #+# #+# # -# Updated: 2021/01/11 22:20:16 by charles ### ########.fr # +# Updated: 2021/02/05 20:38:28 by charles ### ########.fr # # # # ############################################################################ # import argparse import textwrap +import minishell_test.config as config + def parse_args(): """Parse command line arguments""" parser = argparse.ArgumentParser( - description=textwrap.dedent(r""" - ___ ____ _ _ _ _ _ _ - | \/ (_) (_) | | | | | | | | | - | . . |_ _ __ _ ___| |__ ___| | | | |_ ___ ___| |_ - | |\/| | | '_ \| / __| '_ \ / _ \ | | | __/ _ \/ __| __| - | | | | | | | | \__ \ | | | __/ | | | || __/\__ \ |_ - \_| |_/_|_| |_|_|___/_| |_|\___|_|_| \__\___||___/\__| - """), + description="Test for the minishell project of school 42.", formatter_class=argparse.RawTextHelpFormatter, - epilog=textwrap.dedent("""\ - Signal handling is not tested - There is a commented glob suite in src/suites/preprocess.py. - Good luck handling `*'.*'`. + epilog=textwrap.dedent(""" + Made by cacharle - https://cacharle.xyz """) ) parser.add_argument( @@ -74,6 +67,10 @@ def parse_args(): help="After running the test, display the result in a pager of your choice" ) parser.add_argument( + "-p", "--path", default=config.MINISHELL_DIR, + help="Path to minishell directory" + ) + parser.add_argument( "suites", nargs='*', metavar="suite", help=textwrap.dedent("""\ Test suites/group to run. diff --git a/minishell_test/config.py b/minishell_test/config.py index 54e3c28..ac5b3ca 100644 --- a/minishell_test/config.py +++ b/minishell_test/config.py @@ -6,7 +6,7 @@ # By: charles <charles.cabergs@gmail.com> +#+ +:+ +#+ # # +#+#+#+#+#+ +#+ # # Created: 2020/07/15 18:24:19 by charles #+# #+# # -# Updated: 2021/02/11 17:56:37 by charles ### ########.fr # +# Updated: 2021/02/24 07:50:26 by cacharle ### ########.fr # # # # ############################################################################ # @@ -24,7 +24,7 @@ from typing import List BONUS = False # minishell dir path -MINISHELL_DIR = "../minishell" +MINISHELL_DIR = "." # minishell executable MINISHELL_EXEC = "minishell" diff --git a/minishell_test/hooks.py b/minishell_test/hooks.py index e37f2aa..9881354 100644 --- a/minishell_test/hooks.py +++ b/minishell_test/hooks.py @@ -6,7 +6,7 @@ # By: charles <me@cacharle.xyz> +#+ +:+ +#+ # # +#+#+#+#+#+ +#+ # # Created: 2020/09/11 16:10:20 by charles #+# #+# # -# Updated: 2020/11/25 21:36:18 by charles ### ########.fr # +# Updated: 2021/02/05 15:13:30 by charles ### ########.fr # # # # ############################################################################ # @@ -14,7 +14,7 @@ import re import sys import os -import config +import minishell_test.config as config def sort_lines(output): diff --git a/minishell_test/sandbox.py b/minishell_test/sandbox.py index bd49d1e..f10eacf 100644 --- a/minishell_test/sandbox.py +++ b/minishell_test/sandbox.py @@ -6,7 +6,7 @@ # By: charles <me@cacharle.xyz> +#+ +:+ +#+ # # +#+#+#+#+#+ +#+ # # Created: 2020/09/11 13:48:07 by charles #+# #+# # -# Updated: 2021/01/31 03:59:30 by charles ### ########.fr # +# Updated: 2021/02/05 14:54:37 by charles ### ########.fr # # # # ############################################################################ # @@ -16,7 +16,7 @@ import shutil import subprocess from contextlib import contextmanager -import config +import minishell_test.config as config def create(): diff --git a/minishell_test/suite/__init__.py b/minishell_test/suite/__init__.py index 6f7f321..e7677b6 100644 --- a/minishell_test/suite/__init__.py +++ b/minishell_test/suite/__init__.py @@ -1,2 +1,2 @@ -from suite.suite import Suite # noqa: F401 -from suite.decorator import suite # noqa: F401 +from minishell_test.suite.suite import Suite # noqa: F401 +# from minishell_test.suite.decorator import suite # noqa: F401 diff --git a/minishell_test/suite/decorator.py b/minishell_test/suite/decorator.py index fdc7fb6..45f96b6 100644 --- a/minishell_test/suite/decorator.py +++ b/minishell_test/suite/decorator.py @@ -6,15 +6,15 @@ # By: charles <me@cacharle.xyz> +#+ +:+ +#+ # # +#+#+#+#+#+ +#+ # # Created: 2020/09/11 12:28:00 by charles #+# #+# # -# Updated: 2021/02/04 16:18:11 by charles ### ########.fr # +# Updated: 2021/02/05 17:44:25 by charles ### ########.fr # # # # ############################################################################ # import inspect from typing import List -from suite import Suite -from test import Test +from minishell_test.suite import Suite +from minishell_test.test import Test def suite(groups: List[str] = [], bonus: bool = False): # type: ignore @@ -26,7 +26,7 @@ def suite(groups: List[str] = [], bonus: bool = False): # type: ignore mod = inspect.getmodule(origin) if mod is None: raise NotImplementedError - mod_name = mod.__name__[len("suites."):] + mod_name = mod.__name__[len("minishell_test.suites."):] name = "{}/{}".format(mod_name, origin.__name__[len("suite_"):]) description = origin.__doc__ if description is None: diff --git a/minishell_test/suite/suite.py b/minishell_test/suite/suite.py index 836cac0..8c57633 100644 --- a/minishell_test/suite/suite.py +++ b/minishell_test/suite/suite.py @@ -6,15 +6,40 @@ # By: charles <charles.cabergs@gmail.com> +#+ +:+ +#+ # # +#+#+#+#+#+ +#+ # # Created: 2020/07/15 18:24:29 by charles #+# #+# # -# Updated: 2021/02/04 16:13:08 by charles ### ########.fr # +# Updated: 2021/02/05 18:15:26 by charles ### ########.fr # # # # ############################################################################ # -import sys from typing import List, Tuple, Optional, Callable -import config -from test import Test +import minishell_test.config as config +from minishell_test.test import Test + + +class SuiteException(Exception): + """ Base exception for suite """ + pass + + +class AmbiguousNameException(SuiteException): + def __init__(self, name: str, matches: List[str]): + self.name = name + self.matches = matches + + def __str__(self) -> str: + return (("Ambiguous name `{}` match the following suites\n\t{}\n" + "Try to run with -l to see the available suites") + .format(self.name, ', '.join(self.matches))) + + +class NoMatchException(SuiteException): + def __init__(self, name: str): + self.name = name + + def __str__(self) -> str: + return (("Name `{}` doesn't match any suite/group name\n\t" + "Try to run with -l to see the available suites") + .format(self.name)) class Suite: @@ -44,27 +69,21 @@ class Suite: names.append(name) continue matches = [n for n in suite_names - if n.find("/") != -1 - and n[n.find("/") + 1:].startswith(name) - or n.startswith(name)] + if n.find("/") != -1 and + n[n.find("/") + 1:].startswith(name) or + n.startswith(name)] if len(matches) == 1: names.append(matches[0]) elif len(matches) != 0 and all(n.startswith(name) for n in matches): names.extend(matches) elif len(matches) > 2: - print(("Ambiguous name `{}` match the following suites\n\t{}\n" - "Try to run with -l to see the available suites") - .format(name, ', '.join(matches))) - sys.exit(1) + raise AmbiguousNameException(name, matches) elif len(matches) == 0: - print(("Name `{}` doesn't match any suite/group name\n\t" - "Try to run with -l to see the available suites") - .format(name)) - sys.exit(1) + raise NoMatchException(name) cls.available = list(set( - [s for s in cls.available if s.name in names] - + [s for s in cls.available if any(g for g in s.groups if g in names)] + [s for s in cls.available if s.name in names] + + [s for s in cls.available if any(g for g in s.groups if g in names)] )) cls.available.sort(key=lambda s: s.name) for s in cls.available: diff --git a/minishell_test/suites/__init__.py b/minishell_test/suites/__init__.py index b6b3b68..4c5687d 100644 --- a/minishell_test/suites/__init__.py +++ b/minishell_test/suites/__init__.py @@ -6,7 +6,7 @@ # By: charles <charles.cabergs@gmail.com> +#+ +:+ +#+ # # +#+#+#+#+#+ +#+ # # Created: 2020/07/15 18:24:48 by charles #+# #+# # -# Updated: 2020/09/11 13:25:26 by charles ### ########.fr # +# Updated: 2021/02/05 15:16:25 by charles ### ########.fr # # # # ############################################################################ # diff --git a/minishell_test/suites/builtin.py b/minishell_test/suites/builtin.py index 9ab2af8..768850a 100644 --- a/minishell_test/suites/builtin.py +++ b/minishell_test/suites/builtin.py @@ -6,17 +6,17 @@ # By: juligonz <juligonz@student.42.fr> +#+ +:+ +#+ # # +#+#+#+#+#+ +#+ # # Created: 2020/07/15 18:24:43 by charles #+# #+# # -# Updated: 2020/11/28 06:17:19 by charles ### ########.fr # +# Updated: 2021/02/05 14:48:47 by charles ### ########.fr # # Updated: 2020/09/11 18:01:27 by juligonz ### ########.fr # # # # **************************************************************************** # import os -import config -import hooks -from suite import suite -from hooks import linux_discard +import minishell_test.config as config +import minishell_test.hooks as hooks +from minishell_test.suite.decorator import suite +from minishell_test.hooks import linux_discard @suite() diff --git a/minishell_test/suites/cmd.py b/minishell_test/suites/cmd.py index 53b1f97..da3b14a 100644 --- a/minishell_test/suites/cmd.py +++ b/minishell_test/suites/cmd.py @@ -6,15 +6,15 @@ # By: charles <charles.cabergs@gmail.com> +#+ +:+ +#+ # # +#+#+#+#+#+ +#+ # # Created: 2020/07/15 15:11:46 by charles #+# #+# # -# Updated: 2021/02/04 16:14:54 by charles ### ########.fr # +# Updated: 2021/02/05 16:15:42 by charles ### ########.fr # # # # ############################################################################ # import distutils -import hooks -import config -from suite import suite +import minishell_test.hooks as hooks +import minishell_test.config as config +from minishell_test.suite.decorator import suite @suite() diff --git a/minishell_test/suites/flow.py b/minishell_test/suites/flow.py index 2c00b2f..ed5fd03 100644 --- a/minishell_test/suites/flow.py +++ b/minishell_test/suites/flow.py @@ -6,13 +6,19 @@ # By: charles <charles.cabergs@gmail.com> +#+ +:+ +#+ # # +#+#+#+#+#+ +#+ # # Created: 2020/07/15 18:24:52 by charles #+# #+# # -# Updated: 2020/11/10 13:16:28 by cacharle ### ########.fr # +# Updated: 2021/02/05 17:40:00 by charles ### ########.fr # # # # ############################################################################ # -import config -from suite import suite -from hooks import error_line0, platform_status, discard, replace_double_semi_colon, error_eof_to_expected_token +import minishell_test.config as config +from minishell_test.suite.decorator import suite +from minishell_test.hooks import ( + error_line0, + platform_status, + discard, + replace_double_semi_colon, + error_eof_to_expected_token +) @suite() diff --git a/minishell_test/suites/misc.py b/minishell_test/suites/misc.py index a6b9bf2..6a351cc 100644 --- a/minishell_test/suites/misc.py +++ b/minishell_test/suites/misc.py @@ -6,11 +6,11 @@ # By: cacharle <me@cacharle.xyz> +#+ +:+ +#+ # # +#+#+#+#+#+ +#+ # # Created: 2020/10/10 13:06:19 by cacharle #+# #+# # -# Updated: 2020/11/28 06:10:12 by charles ### ########.fr # +# Updated: 2021/02/05 14:49:39 by charles ### ########.fr # # # # ############################################################################ # -from suite import suite +from minishell_test.suite.decorator import suite @suite() diff --git a/minishell_test/suites/path.py b/minishell_test/suites/path.py index 93d4232..fa67d9a 100644 --- a/minishell_test/suites/path.py +++ b/minishell_test/suites/path.py @@ -6,13 +6,13 @@ # By: charles <me@cacharle.xyz> +#+ +:+ +#+ # # +#+#+#+#+#+ +#+ # # Created: 2020/09/09 15:12:58 by charles #+# #+# # -# Updated: 2021/02/04 16:14:20 by charles ### ########.fr # +# Updated: 2021/02/05 17:47:32 by charles ### ########.fr # # # # ############################################################################ # import distutils.spawn -from suite import suite +from minishell_test.suite.decorator import suite @suite() @@ -22,9 +22,9 @@ def suite_path(test): if whoami_path is None: print("Couldn't find `whoami` in your PATH: Skipping suite") return - mode_fmt = ("mkdir path && cp " - + whoami_path - + " ./path/a && chmod {} ./path/a") + mode_fmt = ("mkdir path && cp " + + whoami_path + + " ./path/a && chmod {} ./path/a") test("a", setup=mode_fmt.format("000"), exports={"PATH": "path"}) test("a", setup=mode_fmt.format("001"), exports={"PATH": "path"}) test("a", setup=mode_fmt.format("002"), exports={"PATH": "path"}) diff --git a/minishell_test/suites/preprocess.py b/minishell_test/suites/preprocess.py index 0176399..d7d6bbc 100644 --- a/minishell_test/suites/preprocess.py +++ b/minishell_test/suites/preprocess.py @@ -6,13 +6,13 @@ # By: juligonz <juligonz@student.42.fr> +#+ +:+ +#+ # # +#+#+#+#+#+ +#+ # # Created: 2020/07/15 18:25:00 by charles #+# #+# # -# Updated: 2021/01/21 13:06:55 by charles ### ########.fr # +# Updated: 2021/02/05 14:43:27 by charles ### ########.fr # # # # **************************************************************************** # -import config -import hooks -from suite import suite +import minishell_test.config as config +import minishell_test.hooks as hooks +from minishell_test.suite.decorator import suite @suite() diff --git a/minishell_test/test/__init__.py b/minishell_test/test/__init__.py index cf9949f..534da32 100644 --- a/minishell_test/test/__init__.py +++ b/minishell_test/test/__init__.py @@ -6,8 +6,8 @@ # By: charles <me@cacharle.xyz> +#+ +:+ +#+ # # +#+#+#+#+#+ +#+ # # Created: 2020/09/11 12:18:14 by charles #+# #+# # -# Updated: 2020/09/11 20:18:10 by charles ### ########.fr # +# Updated: 2021/02/05 14:42:47 by charles ### ########.fr # # # # ############################################################################ # -from test.test import Test # noqa: F401 +from minishell_test.test.test import Test # noqa: F401 diff --git a/minishell_test/test/captured.py b/minishell_test/test/captured.py index f7dae3e..7db9739 100644 --- a/minishell_test/test/captured.py +++ b/minishell_test/test/captured.py @@ -6,13 +6,13 @@ # By: charles <me@cacharle.xyz> +#+ +:+ +#+ # # +#+#+#+#+#+ +#+ # # Created: 2020/09/11 12:16:25 by charles #+# #+# # -# Updated: 2021/02/04 15:52:19 by charles ### ########.fr # +# Updated: 2021/02/05 17:47:10 by charles ### ########.fr # # # # ############################################################################ # from typing import List, Optional -import config +import minishell_test.config as config class Captured: @@ -46,9 +46,9 @@ class Captured: raise NotImplementedError if self.is_timeout: return self.is_timeout == other.is_timeout - return (self.output == other.output - and self.status == other.status - and all(x == y for x, y in zip(self.files_content, other.files_content))) + return (self.output == other.output and + self.status == other.status and + all(x == y for x, y in zip(self.files_content, other.files_content))) @staticmethod def timeout(): diff --git a/minishell_test/test/result.py b/minishell_test/test/result.py index eff7b8b..9769527 100644 --- a/minishell_test/test/result.py +++ b/minishell_test/test/result.py @@ -6,7 +6,7 @@ # By: charles <me@cacharle.xyz> +#+ +:+ +#+ # # +#+#+#+#+#+ +#+ # # Created: 2020/09/11 12:17:34 by charles #+# #+# # -# Updated: 2021/02/05 01:36:44 by charles ### ########.fr # +# Updated: 2021/02/05 17:46:30 by charles ### ########.fr # # # # ############################################################################ # @@ -14,8 +14,8 @@ import sys import re from typing import Match, List, Optional -import config -from test.captured import Captured +import minishell_test.config as config +from minishell_test.test.captured import Captured class BaseResult: @@ -168,11 +168,11 @@ class Result(BaseResult): return "" file_header = self.indicator("FILE {}".format(file_name), "|#") + '\n' return ( - file_header - + self.expected_header - + self.cat_e(expected) - + self.actual_header - + self.cat_e(actual) + file_header + + self.expected_header + + self.cat_e(expected) + + self.actual_header + + self.cat_e(actual) ) def files_diff(self): @@ -194,10 +194,10 @@ class Result(BaseResult): .format(self.expected.status, self.actual.status), "| " ) + '\n' if self.expected.output != self.actual.output: - out += (self.expected_header - + self.cat_e(self.expected.output) - + self.actual_header - + self.cat_e(self.actual.output)) + out += (self.expected_header + + self.cat_e(self.expected.output) + + self.actual_header + + self.cat_e(self.actual.output)) return out def full_diff(self) -> str: |
