diff options
Diffstat (limited to 'minishell_test')
| -rwxr-xr-x | minishell_test/__main__.py | 93 | ||||
| -rw-r--r-- | minishell_test/args.py | 87 | ||||
| -rw-r--r-- | minishell_test/config.py | 120 | ||||
| -rw-r--r-- | minishell_test/hooks.py | 123 | ||||
| -rw-r--r-- | minishell_test/sandbox.py | 48 | ||||
| -rw-r--r-- | minishell_test/suite/__init__.py | 2 | ||||
| -rw-r--r-- | minishell_test/suite/decorator.py | 47 | ||||
| -rw-r--r-- | minishell_test/suite/suite.py | 179 | ||||
| -rw-r--r-- | minishell_test/suites/__init__.py | 17 | ||||
| -rw-r--r-- | minishell_test/suites/builtin.py | 403 | ||||
| -rw-r--r-- | minishell_test/suites/cmd.py | 331 | ||||
| -rw-r--r-- | minishell_test/suites/flow.py | 290 | ||||
| -rw-r--r-- | minishell_test/suites/misc.py | 100 | ||||
| -rw-r--r-- | minishell_test/suites/path.py | 137 | ||||
| -rw-r--r-- | minishell_test/suites/preprocess.py | 463 | ||||
| -rw-r--r-- | minishell_test/test/__init__.py | 13 | ||||
| -rw-r--r-- | minishell_test/test/captured.py | 56 | ||||
| -rw-r--r-- | minishell_test/test/result.py | 246 | ||||
| -rw-r--r-- | minishell_test/test/test.py | 155 |
19 files changed, 2910 insertions, 0 deletions
diff --git a/minishell_test/__main__.py b/minishell_test/__main__.py new file mode 100755 index 0000000..fe48b5e --- /dev/null +++ b/minishell_test/__main__.py @@ -0,0 +1,93 @@ +#!/usr/bin/env python3 + +# ############################################################################ # +# # +# ::: :::::::: # +# main.py :+: :+: :+: # +# +:+ +:+ +:+ # +# By: charles <charles.cabergs@gmail.com> +#+ +:+ +#+ # +# +#+#+#+#+#+ +#+ # +# Created: 2020/07/15 15:11:52 by charles #+# #+# # +# Updated: 2020/07/15 15:11:52 by charles ### ########.fr # +# # +# ############################################################################ # + +import os +import sys +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 + + +def main(): + args = parse_args() + if args.list: + Suite.list() + sys.exit(0) + + if config.MINISHELL_MAKE or args.make: + try: + print("{:=^{width}}".format("MAKE", width=config.TERM_COLS)) + subprocess.run(["make", "--no-print-directory", "-C", config.MINISHELL_DIR], + check=True, + env={"MINISHELL_TEST_FLAGS": "-DMINISHELL_TEST", **os.environ}) + print("=" * config.TERM_COLS) + except subprocess.CalledProcessError: + sys.exit(1) + if args.make: + sys.exit(0) + if os.path.exists(config.EXECUTABLES_PATH): + shutil.rmtree(config.EXECUTABLES_PATH) + os.mkdir(config.EXECUTABLES_PATH) + for cmd in config.AVAILABLE_COMMANDS: + cmd_path = distutils.spawn.find_executable(cmd) + if cmd_path is None: + raise RuntimeError + shutil.copy(cmd_path, + os.path.join(config.EXECUTABLES_PATH, cmd)) + + reference_args = os.environ.get("MINISHELL_TEST_ARGS") + if reference_args is not None: + config.REFERENCE_ARGS.extend(reference_args.split(',')) + + pager = os.environ.get("MINISHELL_TEST_PAGER") + if pager is not None: + config.PAGER = pager + + config.VERBOSE_LEVEL = args.verbose + if args.bonus or os.environ.get("MINISHELL_TEST_BONUS") == "yes": + config.BONUS = True + if args.no_bonus: + config.BONUS = False + config.EXIT_FIRST = args.exit_first + config.CHECK_LEAKS = args.check_leaks + config.RANGE = args.range + config.SHOW_RANGE = args.show_range + if config.RANGE is not None or config.CHECK_LEAKS: + config.SHOW_RANGE = True + + Suite.setup(args.suites) + try: + Suite.run_all() + except KeyboardInterrupt: + sandbox.remove() + + Suite.summarize() + Suite.save_log() + print("See", config.LOG_PATH, "for more information") + if config.CHECK_LEAKS: + print("HELP: Valgrind is really slow the -x and --range options could be useful" + " (./run -h for more details)") + + if args.pager: + subprocess.run([config.PAGER, config.LOG_PATH]) + + +if __name__ == "__main__": + main() diff --git a/minishell_test/args.py b/minishell_test/args.py new file mode 100644 index 0000000..b7fcca6 --- /dev/null +++ b/minishell_test/args.py @@ -0,0 +1,87 @@ +# ############################################################################ # +# # +# ::: :::::::: # +# args.py :+: :+: :+: # +# +:+ +:+ +:+ # +# 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 # +# # +# ############################################################################ # + +import argparse +import textwrap + + +def parse_args(): + """Parse command line arguments""" + + parser = argparse.ArgumentParser( + description=textwrap.dedent(r""" + ___ ____ _ _ _ _ _ _ + | \/ (_) (_) | | | | | | | | | + | . . |_ _ __ _ ___| |__ ___| | | | |_ ___ ___| |_ + | |\/| | | '_ \| / __| '_ \ / _ \ | | | __/ _ \/ __| __| + | | | | | | | | \__ \ | | | __/ | | | || __/\__ \ |_ + \_| |_/_|_| |_|_|___/_| |_|\___|_|_| \__\___||___/\__| + """), + 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 `*'.*'`. + """) + ) + parser.add_argument( + "-k", "--check-leaks", action="store_true", + help="Run valgrind on tests (disable usual comparison with bash)" + ) + parser.add_argument( + "-r", "--range", nargs=2, type=int, metavar=("BEGIN", "END"), + help="Range of test index to run (imply --show-index)" + ) + parser.add_argument( + "--show-range", action="store_true", + help="Show test index (useful with --range)" + ) + parser.add_argument( + "-x", "--exit-first", action="store_true", + help="Exit on first fail" + ) + parser.add_argument( + "-v", "--verbose", action="count", + help="Increase verbosity level (e.g -vv == 2)" + ) + parser.add_argument( + "-b", "--bonus", action="store_true", + help="Enable bonus tests" + ) + parser.add_argument( + "-n", "--no-bonus", action="store_true", + help="Disable bonus tests" + ) + parser.add_argument( + "-l", "--list", action="store_true", + help="Print available test suites" + ) + parser.add_argument( + "-m", "--make", action="store_true", + help="Make minishell and exit" + ) + parser.add_argument( + "-g", "--pager", action="store_true", + help="After running the test, display the result in a pager of your choice" + ) + parser.add_argument( + "suites", nargs='*', metavar="suite", + help=textwrap.dedent("""\ + Test suites/group to run. + It tries to be smart and autocomplete the suite name + (e.g ./run int -> ./run preprocess/interpolation) + """) + ) + tmp = parser.parse_args() + if tmp.verbose is None: + tmp.verbose = 1 + return tmp diff --git a/minishell_test/config.py b/minishell_test/config.py new file mode 100644 index 0000000..493652c --- /dev/null +++ b/minishell_test/config.py @@ -0,0 +1,120 @@ +# ############################################################################ # +# # +# ::: :::::::: # +# config.py :+: :+: :+: # +# +:+ +:+ +:+ # +# By: charles <charles.cabergs@gmail.com> +#+ +:+ +#+ # +# +#+#+#+#+#+ +#+ # +# Created: 2020/07/15 18:24:19 by charles #+# #+# # +# Updated: 2021/01/31 04:41:29 by charles ### ########.fr # +# # +# ############################################################################ # + +################################################################################ +# Minishell configuration file # +################################################################################ + +import os +import shutil +import distutils.spawn +from typing import List + +# run the bonus tests +# can be changed with `export MINISHELL_TEST_BONUS=yes` in your shell rc file. +BONUS = False + +# minishell dir path +MINISHELL_DIR = "../minishell" + +# minishell executable +MINISHELL_EXEC = "minishell" + +# make minishell before executing the test if set to True +MINISHELL_MAKE = True + +# path to reference shell (shell which will be compared minishell) +# has to support the -c option (sh, bash and zsh support it) +REFERENCE_PATH = "/bin/bash" +# can be changed with `export MINISHELL_TEST_ARGS=--poxix,--otherarg` +REFERENCE_ARGS: List[str] = [] # ["--posix"] + +# pager to use with --pager option +# can be changed with `export MINISHELL_TEST_PAGER=yourpager` +PAGER = "less" + +# log file path +LOG_PATH = "result.log" + +# path to the sandbox directory +# WARNING: will be rm -rf so be careful +SANDBOX_PATH = "sandbox" + +# where the availables commands are stored +EXECUTABLES_PATH = "./bin" + +# commands available in test" +AVAILABLE_COMMANDS = ["rmdir", "env", "cat", "touch", "ls", "grep", "sh", "head"] + +# $PATH environment variable passed to the shell +PATH_VARIABLE = os.path.abspath(EXECUTABLES_PATH) + +# test timeout +TIMEOUT = 0.5 + +# check leaks test timeout +CHECK_LEAKS_TIMEOUT = 10 + +LOREM = """ +Mollitia asperiores assumenda excepturi et ipsa. Nihil corporis facere aut a rem consequatur. +Quas molestiae corporis et quibusdam maiores. Molestiae sed unde aut at sed. +Deserunt quidem quidem aspernatur pariatur vel illum voluptatum. Culpa unde dolor aspernatur sit. +Mollitia tenetur sed eaque autem placeat a aut in. Ipsam ea consequuntur omnis. +Non et qui vel corrupti similique eum aut voluptatibus. Iste consequatur voluptatum et omnis debitis. +Sit quia neque nihil consequatur sint. Velit libero ut aut et et rerum. +Placeat cumque incidunt non repellat sunt perspiciatis ullam. +Repellendus repudiandae nostrum quia quis corrupti. +Rerum veniam earum cumque pariatur accusantium voluptatum omnis. +Alias ut et et adipisci. Tempore omnis numquam ullam et animi et eveniet. +Dolor itaque distinctio in. Magnam rerum quia est laboriosam repellat perspiciatis eos. +Consequuntur quae corrupti atque. Numquam enim ut ut. +Perspiciatis ut maxime et libero quo voluptas consequatur illum. Pariatur porro dolor cumque molestiae harum. +""" +LOREM = ' '.join(LOREM.split('\n')) + +############################################################################### +# You probably shouldn't edit after # +############################################################################### + +MINISHELL_PATH = os.path.abspath( + os.path.join(MINISHELL_DIR, MINISHELL_EXEC) +) + +VALGRIND_CMD: List[str] = [ + distutils.spawn.find_executable("valgrind") or "couldn't find valgrind", + # "valgrind", + "--trace-children=no", + "--leak-check=yes", + "--child-silent-after-fork=yes", + "--show-leak-kinds=definite", + MINISHELL_PATH, +] + +# 0, 1, 2 +VERBOSE_LEVEL = 1 + +MINISHELL_ERROR_BEGIN = os.path.basename(MINISHELL_PATH) + ": " +REFERENCE_ERROR_BEGIN = REFERENCE_PATH + ": line 0: " + +TERM_COLS = shutil.get_terminal_size().columns +if TERM_COLS < 40: + raise RuntimeError("You're terminal isn't wide enough") + +PLATFORM = os.uname().sysname + +EXIT_FIRST = False + +CHECK_LEAKS = False + +SHOW_RANGE = False + +RANGE = None diff --git a/minishell_test/hooks.py b/minishell_test/hooks.py new file mode 100644 index 0000000..e37f2aa --- /dev/null +++ b/minishell_test/hooks.py @@ -0,0 +1,123 @@ +# ############################################################################ # +# # +# ::: :::::::: # +# hooks.py :+: :+: :+: # +# +:+ +:+ +:+ # +# By: charles <me@cacharle.xyz> +#+ +:+ +#+ # +# +#+#+#+#+#+ +#+ # +# Created: 2020/09/11 16:10:20 by charles #+# #+# # +# Updated: 2020/11/25 21:36:18 by charles ### ########.fr # +# # +# ############################################################################ # + +import re +import sys +import os + +import config + + +def sort_lines(output): + """Sort lines of output""" + return '\n'.join(sorted(output.split('\n'))) + + +def error_line0(output): + """Replace "/bin/bash: -c: line 0:" by "minishell:" and delete the second line""" + error_message = os.environ.get("MINISHELL_TEST_DONT_CHECK_ERROR_MESSAGE") + if error_message is not None and error_message == "yes": + return "DISCARDED BY TEST" + + lines = output.split('\n') + if len(lines) != 3: + return output + prefix = "{}: -c: line 0: ".format(config.REFERENCE_PATH) + if lines[0].find(prefix) != 0: + return output + return lines[0].replace(prefix, "minishell: ") + "\n" + + +def discard(output): + """Discard the output""" + return "DISCARDED BY TEST" + + +def export_singleton(output): + """Remove variable that are not set to anything in a call to export without arguments""" + prefix = "export " if ("--posix" in config.REFERENCE_ARGS) else "declare -x " + return sort_lines( + '\n'.join([line for line in output.split('\n') + if re.match("^{}[a-zA-Z]+$".format(prefix), line) is None]) + ) + + +def replace_double_slash(output): + """Replace occurence of double slash by one""" + return output.replace("//", "/") + + +def replace_double_semi_colon(output): + """Replace occurence of double semi-colon by one""" + return output.replace(";;", ";") + + +def platform_status(darwin_status, linux_status, windows_status=None): + def hook(status): + if config.PLATFORM == "Darwin": + return status + elif config.PLATFORM == "Linux": + return (darwin_status if status == linux_status else status) + else: + raise RuntimeError("This platform exit codes are not supported yet," + "feel free to contact me to add it.") + sys.exit(2) + return status + return hook + + +def is_directory(output): + if config.PLATFORM == "Linux": + return output.replace("Is a directory", "is a directory") + else: + return output + + +# def no_cd_too_many_arguments(output): +# for i, line in output.split("\n"): +# if line.find("too many arguments") + + +def shlvl_0_to_1(output): + if config.PLATFORM == "Linux": + return output.replace("SHLVL=0", "SHLVL=1") + else: + return output + + +def delete_escape(output): + if config.PLATFORM == "Linux": + return output.replace("\\", "") + else: + return output + + +def error_eof_to_expected_token(output): + return output.replace( + "-c: line 1: syntax error: unexpected end of file", + "syntax error expected token" + ) + + +def linux_discard(output): + if config.PLATFORM == "Linux": + return "DISCARDED BY MINISHELL TEST" + else: + return output + + +def should_not_be(not_expected): + def hook(output): + if output == not_expected: + return "OUTPUT SHOULD NOT BE " + output + return "DISCARDED BY TEST" + return hook diff --git a/minishell_test/sandbox.py b/minishell_test/sandbox.py new file mode 100644 index 0000000..bd49d1e --- /dev/null +++ b/minishell_test/sandbox.py @@ -0,0 +1,48 @@ +# ############################################################################ # +# # +# ::: :::::::: # +# sandbox.py :+: :+: :+: # +# +:+ +:+ +:+ # +# By: charles <me@cacharle.xyz> +#+ +:+ +#+ # +# +#+#+#+#+#+ +#+ # +# Created: 2020/09/11 13:48:07 by charles #+# #+# # +# Updated: 2021/01/31 03:59:30 by charles ### ########.fr # +# # +# ############################################################################ # + +import os +import glob +import shutil +import subprocess +from contextlib import contextmanager + +import config + + +def create(): + """Create a new sandbox directory""" + try: + os.mkdir(config.SANDBOX_PATH) + except OSError: + pass + + +def remove(): + """Remove the sandbox directory + Brute force rm -rf if clean removal doesn't work due to permissions. + """ + try: + shutil.rmtree(config.SANDBOX_PATH) + except PermissionError: + subprocess.run(["chmod", "777", *glob.glob(config.SANDBOX_PATH + "/*")], check=True) + subprocess.run(["rm", "-rf", config.SANDBOX_PATH], check=True) + except FileNotFoundError: + pass + + +@contextmanager +def context(): + """Sandbox context manager""" + create() + yield + remove() diff --git a/minishell_test/suite/__init__.py b/minishell_test/suite/__init__.py new file mode 100644 index 0000000..6f7f321 --- /dev/null +++ b/minishell_test/suite/__init__.py @@ -0,0 +1,2 @@ +from suite.suite import Suite # noqa: F401 +from suite.decorator import suite # noqa: F401 diff --git a/minishell_test/suite/decorator.py b/minishell_test/suite/decorator.py new file mode 100644 index 0000000..fdc7fb6 --- /dev/null +++ b/minishell_test/suite/decorator.py @@ -0,0 +1,47 @@ +# ############################################################################ # +# # +# ::: :::::::: # +# decorator.py :+: :+: :+: # +# +:+ +:+ +:+ # +# By: charles <me@cacharle.xyz> +#+ +:+ +#+ # +# +#+#+#+#+#+ +#+ # +# Created: 2020/09/11 12:28:00 by charles #+# #+# # +# Updated: 2021/02/04 16:18:11 by charles ### ########.fr # +# # +# ############################################################################ # + +import inspect +from typing import List + +from suite import Suite +from test import Test + + +def suite(groups: List[str] = [], bonus: bool = False): # type: ignore + """Decorator generator for suites arguments""" + + def suite_wrapper(origin): + """Decorator for a suite function (fmt: suite_[name]) """ + + mod = inspect.getmodule(origin) + if mod is None: + raise NotImplementedError + mod_name = mod.__name__[len("suites."):] + name = "{}/{}".format(mod_name, origin.__name__[len("suite_"):]) + description = origin.__doc__ + if description is None: + print("You should had a doc string to the {} suite".format(name)) + description = "no description" + description = description.split("\n")[0].strip() + s = Suite(name, groups + [mod_name], bonus, description) + + def test_generator(): + def test(*args, **kwargs): + s.add(Test(*args, **kwargs)) + origin(test) + + s.generator_func = test_generator + Suite.available.append(s) + return test_generator + + return suite_wrapper diff --git a/minishell_test/suite/suite.py b/minishell_test/suite/suite.py new file mode 100644 index 0000000..836cac0 --- /dev/null +++ b/minishell_test/suite/suite.py @@ -0,0 +1,179 @@ +# ############################################################################ # +# # +# ::: :::::::: # +# suite.py :+: :+: :+: # +# +:+ +:+ +:+ # +# 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 # +# # +# ############################################################################ # + +import sys +from typing import List, Tuple, Optional, Callable + +import config +from test import Test + + +class Suite: + available: List['Suite'] = [] + + @classmethod + def run_all(cls): + """Run all available suites""" + for s in cls.available: + if not s.run() and config.EXIT_FIRST: + break + + @classmethod + def setup(cls, asked_names: List[str]) -> None: + """ Remove not asked suite from available suites + Tries to autocomplete the asked names + """ + if not config.BONUS: + cls.available = [s for s in cls.available if not s.bonus] + if len(asked_names) == 0: + asked_names = [s.name for s in cls.available] + + suite_names = [s.name for s in cls.available] + names = [] + for i, name in enumerate(asked_names): + if name in suite_names: + 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 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) + 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) + + 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)] + )) + cls.available.sort(key=lambda s: s.name) + for s in cls.available: + if s.generator_func is not None: + s.generator_func() + + @classmethod + def available_names(cls) -> List[str]: + """List of available suites names""" + return [s.name for s in cls.available] + + @classmethod + def list(cls): + print("Groups:") + print("\n".join({" - " + ', '.join(s.groups) for s in Suite.available})) + print("The available suites are:") + max_name_width = max(len(s.name) for s in Suite.available) + 5 + lines = [ + " - {:.<{max_name_width}} {}".format( + s.name + " ", + s.description, + max_name_width=max_name_width + ) + for s in Suite.available + ] + print("\n".join(lines)) + + def __init__( + self, + name: str, + groups: List[str], + bonus: bool = False, + description: str = "no description", + ): + """Suite class + name: suite id + groups: list of suite groups + bonus: is this suite bonus + """ + self.name = name + self.groups = groups + self.description = description + self.bonus = bonus + self.generator_func: Optional[Callable] = None + self.tests: List[Test] = [] + + def add(self, test): + """Append a test to the suite""" + self.tests.append(test) + + BLUE_CHARS = "\033[34m" + CLOSE_CHARS = "\033[0m" + + def run(self) -> bool: + """Run all test in the suite""" + if config.VERBOSE_LEVEL == 0: + print(self.name + ": ", end="") + else: + print("{}{:#^{width}}{}".format( + self.BLUE_CHARS, + " " + self.name + " ", + self.CLOSE_CHARS, + width=config.TERM_COLS + )) + for i, t in enumerate(self.tests): + if config.RANGE is not None: + if not (config.RANGE[0] <= i <= config.RANGE[1]): + continue + t.run(i) + if config.EXIT_FIRST and t.result is not None and t.result.failed: + return False + if config.VERBOSE_LEVEL == 0: + print() + return True + + def total(self) -> Tuple[int, int]: + """Returns the total of passed and failed tests""" + passed_total = 0 + for t in self.tests: + if t.result is None: + return (-1, -1) + if t.result.passed: + passed_total += 1 + return passed_total, len(self.tests) - passed_total + + @classmethod + def summarize(cls): + """Print a summary of all runned suites""" + pass_sum = 0 + fail_sum = 0 + print("\nSummary:") + for s in cls.available: + (pass_total, fail_total) = s.total() + if pass_total == -1: + continue + pass_sum += pass_total + fail_sum += fail_total + print("{:.<{width}} \033[32m{:4} [PASS]\033[0m \033[31m{:4} [FAIL]\033[0m" + .format(s.name + " ", pass_total, fail_total, width=config.TERM_COLS - 24)) + print("{:.<{width}} \033[32m{:4} [PASS]\033[0m \033[31m{:4} [FAIL]\033[0m" + .format("TOTAL ", pass_sum, fail_sum, width=config.TERM_COLS - 24)) + + @classmethod + def save_log(cls): + """Save the result of all suites to a file""" + with open(config.LOG_PATH, "w") as log_file: + for s in cls.available: + for t in s.tests: + if t.result is not None and t.result.failed: + t.result.colored = False + t.result.set_colors() + log_file.write(t.result.full_diff() + '\n') diff --git a/minishell_test/suites/__init__.py b/minishell_test/suites/__init__.py new file mode 100644 index 0000000..b6b3b68 --- /dev/null +++ b/minishell_test/suites/__init__.py @@ -0,0 +1,17 @@ +# ############################################################################ # +# # +# ::: :::::::: # +# __init__.py :+: :+: :+: # +# +:+ +:+ +:+ # +# 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 # +# # +# ############################################################################ # + +import os +import glob + +modules = glob.glob(os.path.join(os.path.dirname(__file__), "*.py")) +__all__ = [os.path.basename(f)[:-3] for f in modules if os.path.isfile(f) and not f.endswith("__init__.py")] diff --git a/minishell_test/suites/builtin.py b/minishell_test/suites/builtin.py new file mode 100644 index 0000000..9ab2af8 --- /dev/null +++ b/minishell_test/suites/builtin.py @@ -0,0 +1,403 @@ +# **************************************************************************** # +# # +# ::: :::::::: # +# builtin.py :+: :+: :+: # +# +:+ +:+ +:+ # +# 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: 2020/09/11 18:01:27 by juligonz ### ########.fr # +# # +# **************************************************************************** # + +import os + +import config +import hooks +from suite import suite +from hooks import linux_discard + + +@suite() +def suite_echo(test): + """ echo builtin tests """ + test("echo") + test("echo bonjour") + test("echo lalalala lalalalal alalalalal alalalala") + test("echo lalalala lalalalal alalalalal alalalala") + test("echo " + config.LOREM) + test("echo -n") + test("echo -n bonjour") + test("echo -n lalalala lalalalal alalalalal alalalala") + test("echo -n lalalala lalalalal alalalalal alalalala") + test("echo -n " + config.LOREM) + test("echo bonjour -n") + test("echo -n bonjour -n") + test(" echo bonjour je") + test(" echo -n bonjour je") + test("echo a '' b '' c '' d") + test('echo a "" b "" c "" d') + test("echo -n a '' b '' c '' d") + test('echo -n a "" b "" c "" d') + test("echo '' '' ''") + test("Echo bonjour") + test("eCho bonjour") + test("ecHo bonjour") + test("echO bonjour") + test("EchO bonjour") + test("eCHo bonjour") + test("EcHo bonjour") + test("eChO bonjour") + test("Echo bonjour", exports={"PATH": "/bin:/usr/bin"}) + test("eCho bonjour", exports={"PATH": "/bin:/usr/bin"}) + test("ecHo bonjour", exports={"PATH": "/bin:/usr/bin"}) + test("echO bonjour", exports={"PATH": "/bin:/usr/bin"}) + test("EchO bonjour", exports={"PATH": "/bin:/usr/bin"}) + test("eCHo bonjour", exports={"PATH": "/bin:/usr/bin"}) + test("EcHo bonjour", exports={"PATH": "/bin:/usr/bin"}) + test("eChO bonjour", exports={"PATH": "/bin:/usr/bin"}) + test("eChO -e 'bonjo\\nur'", exports={"PATH": "/bin:/usr/bin"}) + test("echo -n -n -n -n bonjour") + test("echo -nnnnnnnnnnnnnnnnnnnnn bonjour") + test("echo -nnnnnnnnnnnnnnnnnnnnn -n -n -n bonjour -n -n") + + +@suite() +def suite_export(test): + """ export builtin tests """ + test("export", hook=hooks.export_singleton) + test("export", exports={"A": ""}, hook=hooks.export_singleton) + test("export", exports={"A": "\""}, hook=hooks.export_singleton) + test("export", exports={"A": "\\"}, hook=hooks.export_singleton) + test("export", exports={"A": "$"}, hook=hooks.export_singleton) + test("export", exports={"A": "\t"}, hook=hooks.export_singleton) + test("export", exports={"A": "'"}, hook=hooks.export_singleton) + test("export", exports={"A": "a"}, hook=hooks.export_singleton) + test("export A=a; echo $A") + test("export A=a B=b C=c; echo $A$B$C") + test("export A=a B=b C=c D=d E=e F=f G=g H=h I=i J=j K=k L=l" + "M=m N=n O=o P=p Q=q R=r S=s T=t U=u V=v W=w |
