aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rwxr-xr-xminishell_test/__main__.py71
-rw-r--r--minishell_test/args.py57
-rw-r--r--minishell_test/config.py219
-rw-r--r--minishell_test/data/__init__.py0
-rw-r--r--minishell_test/data/default.cfg29
-rw-r--r--minishell_test/data/lorem13
-rw-r--r--minishell_test/hooks.py35
-rw-r--r--minishell_test/sandbox.py20
-rw-r--r--minishell_test/suite/suite.py35
-rw-r--r--minishell_test/suites/builtin.py6
-rw-r--r--minishell_test/suites/cmd.py6
-rw-r--r--minishell_test/suites/flow.py4
-rw-r--r--minishell_test/suites/preprocess.py6
-rw-r--r--minishell_test/test/captured.py15
-rw-r--r--minishell_test/test/result.py34
-rw-r--r--minishell_test/test/test.py30
-rw-r--r--setup.cfg3
18 files changed, 301 insertions, 283 deletions
diff --git a/.gitignore b/.gitignore
index 691b3c8..b71245a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -7,3 +7,4 @@ dist/
.tox/
build/
docs/_build/
+bash3.2.57
diff --git a/minishell_test/__main__.py b/minishell_test/__main__.py
index 3ae1648..b5761d0 100755
--- a/minishell_test/__main__.py
+++ b/minishell_test/__main__.py
@@ -18,8 +18,8 @@ import shutil
import distutils.spawn
import subprocess
-import minishell_test.config as config
-import minishell_test.sandbox as sandbox
+from minishell_test import config
+from minishell_test import sandbox
from minishell_test.args import parse_args
from minishell_test.suite.suite import Suite, SuiteException
from minishell_test.suites import * # noqa: F403,F401
@@ -28,67 +28,49 @@ from minishell_test.test import Test
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:
+ # running ``make`` in minishell directory
+ if config.MAKE or args.make:
+ print("{:=^{width}}".format("MAKE", width=config.TERM_COLS))
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)
+ subprocess.run(
+ ["make", *config.MAKE_ARGS, "--no-print-directory", "-C", config.MINISHELL_DIR],
+ check=True,
+ env=os.environ,
+ )
except subprocess.CalledProcessError:
sys.exit(1)
+ print("=" * config.TERM_COLS)
if args.make:
sys.exit(0)
- if not os.path.exists(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))
+ # setup available commands
+ if not config.SHELL_AVAILABLE_COMMANDS_DIR.exists():
+ config.SHELL_AVAILABLE_COMMANDS_DIR.mkdir(parents=True, exist_ok=True)
+ for cmd in config.SHELL_AVAILABLE_COMMANDS:
+ copied_path = config.SHELL_AVAILABLE_COMMANDS_DIR / cmd
+ if copied_path.exists():
+ continue
+ cmd_path = distutils.spawn.find_executable(cmd)
+ if cmd_path is None:
+ raise RuntimeError(f"Command not found {cmd}")
+ shutil.copy(cmd_path, copied_path)
if args.try_cmd is not None:
print("Output")
print(Test.try_run(args.try_cmd))
sys.exit(0)
- 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
-
try:
Suite.setup(args.suites)
except SuiteException as e:
print(e)
sys.exit(1)
+
try:
Suite.run_all()
except KeyboardInterrupt:
@@ -103,8 +85,9 @@ def main(argv=None):
print("HELP: Valgrind is really slow the -x and --range options could be useful"
" ({} -h for more details)".format(sys.argv[0]))
- if args.pager:
- subprocess.run([config.PAGER, config.LOG_PATH])
+ if config.PAGER:
+ # TODO {} replaced by filename in pager config var
+ subprocess.run([config.PAGER_PROG, config.LOG_PATH])
if __name__ == "__main__":
diff --git a/minishell_test/args.py b/minishell_test/args.py
index ef47081..3f56b8c 100644
--- a/minishell_test/args.py
+++ b/minishell_test/args.py
@@ -6,16 +6,16 @@
# By: charles <charles.cabergs@gmail.com> +#+ +:+ +#+ #
# +#+#+#+#+#+ +#+ #
# Created: 2020/07/15 18:24:32 by charles #+# #+# #
-# Updated: 2021/02/24 08:48:15 by cacharle ### ########.fr #
+# Updated: 2021/02/27 12:08:55 by cacharle ### ########.fr #
# #
# ############################################################################ #
import argparse
import textwrap
-
-import minishell_test.config as config
+import functools
+@functools.lru_cache(maxsize=1)
def parse_args():
"""Parse command line arguments"""
@@ -25,65 +25,64 @@ def parse_args():
epilog="Made by cacharle - https://cacharle.xyz"
)
parser.add_argument(
- "-p", "--path", default=config.MINISHELL_DIR,
+ "-p", "--path",
+ default=".",
help="Path to minishell directory"
)
parser.add_argument(
- "-l", "--list", action="store_true",
+ "-l", "--list",
+ action="store_true",
help="Print available test suites"
)
parser.add_argument(
- "-t", "--try-cmd", metavar="COMMAND",
+ "-t", "--try",
+ metavar="COMMAND",
+ dest='try_cmd',
help=textwrap.dedent("""\
Run a custom command like this test would
(the only environment variable passed to your executable are TERM and PATH)
""")
)
parser.add_argument(
- "-k", "--check-leaks", action="store_true",
+ "-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"),
+ "-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",
+ "--show-range",
+ action="store_true",
help="Show test index (useful with --range)"
)
parser.add_argument(
- "-x", "--exit-first", action="store_true",
+ "-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(
- "-m", "--make", action="store_true",
+ "-m", "--make",
+ action="store_true",
help="Make minishell and exit"
)
parser.add_argument(
- "-g", "--pager", action="store_true",
+ "-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",
+ "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
+ return parser.parse_args()
diff --git a/minishell_test/config.py b/minishell_test/config.py
index 1d471c9..2cc96b7 100644
--- a/minishell_test/config.py
+++ b/minishell_test/config.py
@@ -1,117 +1,134 @@
# ############################################################################ #
# #
# ::: :::::::: #
-# defaults.py :+: :+: :+: #
+# config.py :+: :+: :+: #
# +:+ +:+ +:+ #
-# By: charles <charles.cabergs@gmail.com> +#+ +:+ +#+ #
+# By: cacharle <me@cacharle.xyz> +#+ +:+ +#+ #
# +#+#+#+#+#+ +#+ #
-# Created: 2020/07/15 18:24:19 by charles #+# #+# #
-# Updated: 2021/02/26 09:40:07 by cacharle ### ########.fr #
+# Created: 2021/02/26 09:40:36 by cacharle #+# #+# #
+# Updated: 2021/02/27 16:16:07 by cacharle ### ########.fr #
# #
# ############################################################################ #
-
import os
+import sys
+import configparser
+import inspect
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 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 = "minishell_test_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 = "/tmp/minishell_test_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: "
+import distutils
+from pathlib import Path
-TERM_COLS = shutil.get_terminal_size().columns
-if TERM_COLS < 40:
- raise RuntimeError("You're terminal isn't wide enough")
+import minishell_test.data
+from minishell_test.args import parse_args
+
+
+DATA_DIR = Path(inspect.getfile(minishell_test.data)).parent
+
+
+class ConfigParser(configparser.ConfigParser):
+ BOOLEAN_STATES = {'true': True, 'false': False}
+
+ def __init__(self):
+ super().__init__()
+
+ def getpath(self, section, options):
+ return Path(self.get(section, options)).resolve()
+
+ def getargs(self, section, options):
+ value = self.get(section, options)
+ return value.strip().split(' ') if len(value) != 0 else []
+
+ def getmultiline(self, section, options):
+ return self.get(section, options).strip().split('\n')
+
+
+args = parse_args()
+MINISHELL_DIR = Path(args.path).resolve()
+
+CONFIG_FILENAME = Path('minishell_test.cfg')
+
+config = ConfigParser()
+config.read(DATA_DIR / 'default.cfg')
+user_config = ConfigParser()
+user_config.read(MINISHELL_DIR / CONFIG_FILENAME)
-PLATFORM = os.uname().sysname
+for section in user_config:
+ if section not in config:
+ raise RuntimeError(f"Unknown section name: {section}")
+ for key in user_config[section]:
+ if key not in config[section]:
+ raise RuntimeError(f"Unknown key name: {key}")
-EXIT_FIRST = False
+config.read_dict({**config, **user_config})
-CHECK_LEAKS = False
+BONUS = config.getboolean('minishell_test', 'bonus')
+EXEC_NAME = config.get('minishell_test', 'exec_name')
+MAKE = config.getboolean('minishell_test', 'make')
+MAKE_ARGS = config.getargs('minishell_test', 'make_args')
+PAGER = config.getboolean('minishell_test', 'pager')
+PAGER_PROG = config.get('minishell_test', 'pager_prog')
+LOG_PATH = config.getpath('minishell_test', 'log_path')
+CHECK_ERROR_MESSAGES = config.getboolean('minishell_test', 'check_error_messages')
-SHOW_RANGE = False
+SHELL_AVAILABLE_COMMANDS = config.getmultiline('shell', 'available_commands')
+SHELL_PATH_VARIABLE = config.get('shell', 'path_variable')
+
+
+SHELL_REFERENCE_PATH = config.getpath('shell:reference', 'path')
+SHELL_REFERENCE_ARGS = config.getargs('shell:reference', 'args')
+
+TIMEOUT_TEST = config.getfloat('timeout', 'test')
+TIMEOUT_LEAKS = config.getfloat('timeout', 'leaks')
+
+xdg_cache_home = os.environ.get('XDG_CACHE_HOME')
+home = os.environ.get('HOME')
+if xdg_cache_home is not None:
+ CACHE_DIR = Path(xdg_cache_home) / 'minishell_test'
+elif home is not None:
+ CACHE_DIR = Path(home) / '.cache' / 'minishell_test'
+else:
+ CACHE_DIR = Path('.cache', 'minishell_test')
+
+SANDBOX_DIR = CACHE_DIR / 'sandbox'
+SHELL_AVAILABLE_COMMANDS_DIR = CACHE_DIR / 'bin'
+
+SHELL_PATH_VARIABLE = SHELL_PATH_VARIABLE.format(shell_available_commands_dir=SHELL_AVAILABLE_COMMANDS_DIR)
+
+with open(DATA_DIR / 'lorem') as f:
+ LOREM = ' '.join(f.read().split('\n'))
+
+MINISHELL_EXEC_PATH = MINISHELL_DIR / EXEC_NAME
+
+MINISHELL_PREFIX = EXEC_NAME + ": "
+SHELL_REFERENCE_PREFIX = str(SHELL_REFERENCE_PATH) + ": "
+
+EXIT_FIRST = args.exit_first
+RANGE = args.range
+CHECK_LEAKS = args.check_leaks
+
+if RANGE is not None or CHECK_LEAKS:
+ SHOW_RANGE = True
+else:
+ SHOW_RANGE = args.show_range
+
+if CHECK_LEAKS:
+ valgrind_path = distutils.spawn.find_executable("valgrind")
+ if valgrind_path is None:
+ raise RuntimeError("Could not find valgrind command on your system")
+ VALGRIND_CMD = [
+ str(valgrind_path),
+ "--trace-children=no",
+ "--leak-check=yes",
+ "--child-silent-after-fork=yes",
+ "--show-leak-kinds=definite",
+ str(MINISHELL_EXEC_PATH),
+ ]
+
+TERM_COLS = shutil.get_terminal_size().columns
+if TERM_COLS < 40:
+ raise RuntimeError("You're terminal isn't wide enough 40 cols minimum required")
-RANGE = None
+PLATFORM = sys.platform
+supported = ['linux', 'darwin']
+if PLATFORM not in supported:
+ raise RuntimeError("Your platform ({PLATFORM}) is not supported, supported platforms are: {', '.join(supported)}")
diff --git a/minishell_test/data/__init__.py b/minishell_test/data/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/minishell_test/data/__init__.py
diff --git a/minishell_test/data/default.cfg b/minishell_test/data/default.cfg
new file mode 100644
index 0000000..158c7af
--- /dev/null
+++ b/minishell_test/data/default.cfg
@@ -0,0 +1,29 @@
+[minishell_test]
+bonus = false
+exec_name = minishell
+make = true
+make_args = MINISHELL_TEST_FLAGS=-DMINISHELL_TEST
+pager = false
+pager_prog = less
+log_path = minishell_test.log
+check_error_messages = true
+
+[shell]
+available_commands =
+ rmdir
+ env
+ cat
+ touch
+ ls
+ grep
+ sh
+ head
+path_variable = {shell_available_commands_dir}
+
+[shell:reference]
+path = /bin/bash
+args =
+
+[timeout]
+test = 0.5
+leaks = 10
diff --git a/minishell_test/data/lorem b/minishell_test/data/lorem
new file mode 100644
index 0000000..388f482
--- /dev/null
+++ b/minishell_test/data/lorem
@@ -0,0 +1,13 @@
+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.
diff --git a/minishell_test/hooks.py b/minishell_test/hooks.py
index 9881354..6356080 100644
--- a/minishell_test/hooks.py
+++ b/minishell_test/hooks.py
@@ -6,15 +6,13 @@
# By: charles <me@cacharle.xyz> +#+ +:+ +#+ #
# +#+#+#+#+#+ +#+ #
# Created: 2020/09/11 16:10:20 by charles #+# #+# #
-# Updated: 2021/02/05 15:13:30 by charles ### ########.fr #
+# Updated: 2021/02/27 15:40:25 by cacharle ### ########.fr #
# #
# ############################################################################ #
import re
-import sys
-import os
-import minishell_test.config as config
+from minishell_test import config
def sort_lines(output):
@@ -23,18 +21,17 @@ def sort_lines(output):
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":
+ """Replace "/bin/bash: -c: line n:" by "minishell:" and delete the second line"""
+ if not config.CHECK_ERROR_MESSAGES:
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:
+ prefix = config.SHELL_REFERENCE_PREFIX + "-c: "
+ if not lines[0].startswith(prefix):
return output
- return lines[0].replace(prefix, "minishell: ") + "\n"
+ return lines[0].replace(prefix, config.MINISHELL_PREFIX, 1) + "\n"
def discard(output):
@@ -44,7 +41,7 @@ def discard(output):
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 "
+ prefix = "export " if ("--posix" in config.SHELL_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])
@@ -63,20 +60,16 @@ def replace_double_semi_colon(output):
def platform_status(darwin_status, linux_status, windows_status=None):
def hook(status):
- if config.PLATFORM == "Darwin":
+ if config.PLATFORM == "darwin":
return status
- elif config.PLATFORM == "Linux":
+ 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":
+ if config.PLATFORM == "linux":
return output.replace("Is a directory", "is a directory")
else:
return output
@@ -88,14 +81,14 @@ def is_directory(output):
def shlvl_0_to_1(output):
- if config.PLATFORM == "Linux":
+ if config.PLATFORM == "linux":
return output.replace("SHLVL=0", "SHLVL=1")
else:
return output
def delete_escape(output):
- if config.PLATFORM == "Linux":
+ if config.PLATFORM == "linux":
return output.replace("\\", "")
else:
return output
@@ -109,7 +102,7 @@ def error_eof_to_expected_token(output):
def linux_discard(output):
- if config.PLATFORM == "Linux":
+ if config.PLATFORM == "linux":
return "DISCARDED BY MINISHELL TEST"
else:
return output
diff --git a/minishell_test/sandbox.py b/minishell_test/sandbox.py
index f10eacf..980cfe7 100644
--- a/minishell_test/sandbox.py
+++ b/minishell_test/sandbox.py
@@ -6,23 +6,21 @@
# By: charles <me@cacharle.xyz> +#+ +:+ +#+ #
# +#+#+#+#+#+ +#+ #
# Created: 2020/09/11 13:48:07 by charles #+# #+# #
-# Updated: 2021/02/05 14:54:37 by charles ### ########.fr #
+# Updated: 2021/02/27 12:32:17 by cacharle ### ########.fr #
# #
# ############################################################################ #
-import os
-import glob
import shutil
import subprocess
from contextlib import contextmanager
-import minishell_test.config as config
+from minishell_test import config
def create():
"""Create a new sandbox directory"""
try:
- os.mkdir(config.SANDBOX_PATH)
+ config.SANDBOX_DIR.mkdir(parents=True, exist_ok=True)
except OSError:
pass
@@ -32,10 +30,10 @@ def remove():
Brute force rm -rf if clean removal doesn't work due to permissions.
"""
try:
- shutil.rmtree(config.SANDBOX_PATH)
+ shutil.rmtree(config.SANDBOX_DIR)
except PermissionError:
- subprocess.run(["chmod", "777", *glob.glob(config.SANDBOX_PATH + "/*")], check=True)
- subprocess.run(["rm", "-rf", config.SANDBOX_PATH], check=True)
+ subprocess.run(["chmod", "777", *config.SANDBOX_DIR.glob("*")], check=True)
+ shutil.rmtree(config.SANDBOX_DIR)
except FileNotFoundError:
pass
@@ -44,5 +42,7 @@ def remove():
def context():
"""Sandbox context manager"""
create()
- yield
- remove()
+ try:
+ yield
+ finally:
+ remove()
diff --git a/minishell_test/suite/suite.py b/minishell_test/suite/suite.py
index 8c57633..5d36600 100644
--- a/minishell_test/suite/suite.py
+++ b/minishell_test/suite/suite.py
@@ -6,13 +6,13 @@
# By: charles <charles.cabergs@gmail.com> +#+ +:+ +#+ #
# +#+#+#+#+#+ +#+ #
# Created: 2020/07/15 18:24:29 by charles #+# #+# #
-# Updated: 2021/02/05 18:15:26 by charles ### ########.fr #
+# Updated: 2021/02/27 12:07:59 by cacharle ### ########.fr #
# #
# ############################################################################ #
from typing import List, Tuple, Optional, Callable
-import minishell_test.config as config
+from minishell_test import config
from minishell_test.test import Test
@@ -139,24 +139,19 @@ class Suite:
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()
+ 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
return True
def total(self) -> Tuple[int, int]:
diff --git a/minishell_test/suites/builtin.py b/minishell_test/suites/builtin.py
index 768850a..c373ce6 100644
--- a/minishell_test/suites/builtin.py
+++ b/minishell_test/suites/builtin.py
@@ -6,15 +6,15 @@
# By: juligonz <juligonz@student.42.fr> +#+ +:+ +#+ #
# +#+#+#+#+#+ +#+ #
# Created: 2020/07/15 18:24:43 by charles #+# #+# #
-# Updated: 2021/02/05 14:48:47 by charles ### ########.fr #
+# Updated: 2021/02/27 12:07:46 by cacharle ### ########.fr #
# Updated: 2020/09/11 18:01:27 by juligonz ### ########.fr #
# #
# **************************************************************************** #
import os
-import minishell_test.config as config
-import minishell_test.hooks as hooks
+from minishell_test import config
+from minishell_test import hooks
from minishell_test.suite.decorator import suite
from minishell_test.hooks import linux_discard
diff --git a/minishell_test/suites/cmd.py b/minishell_test/suites/cmd.py
index da3b14a..8d2bc09 100644
--- a/minishell_test/suites/cmd.py
+++ b/minishell_test/suites/cmd.py
@@ -6,14 +6,14 @@
# By: charles <charles.cabergs@gmail.com> +#+ +:+ +#+ #
# +#+#+#+#+#+ +#+ #
# Created: 2020/07/15 15:11:46 by charles #+# #+# #
-# Updated: 2021/02/05 16:15:42 by charles ### ########.fr #
+# Updated: 2021/02/27 12:07:29 by cacharle ### ########.fr #
# #
# ############################################################################ #
import distutils
-import minishell_test.hooks as hooks
-import minishell_test.config as config
+from minishell_test import hooks
+from minishell_test import config
from minishell_test.suite.decorator import suite
diff --git a/minishell_test/suites/flow.py b/minishell_test/suites/flow.py
index ed5fd03..2adbb4b 100644
--- a/minishell_test/suites/flow.py
+++ b/minishell_test/suites/flow.py
@@ -6,11 +6,11 @@
# By: charles <charles.cabergs@gmail.com> +#+ +:+ +#+ #
# +#+#+#+#+#+ +#+ #
# Created: 2020/07/15 18:24:52 by charles #+# #+# #
-# Updated: 2021/02/05 17:40:00 by charles ### ########.fr #
+# Updated: 2021/02/27 12:06:58 by cacharle ### ########.fr #
# #
# ############################################################################ #
-import minishell_test.config as config
+from minishell_test import config
from minishell_test.suite.decorator import suite
from minishell_test.hooks import (
error_line0,
diff --git a/minishell_test/suites/preprocess.py b/minishell_test/suites/preprocess.py
index d7d6bbc..c296dcb 100644
--- a/minishell_test/suites/preprocess.py
+++ b/minishell_test/suites/preprocess.py
@@ -6,12 +6,12 @@
# By: juligonz <juligonz@student.42.fr> +#+ +:+ +#+ #
# +#+#+#+#+#+ +#+ #
# Created: 2020/07/15 18:25:00 by charles #+# #+# #
-# Updated: 2021/02/05 14:43:27 by charles ### ########.fr #
+# Updated: 2021/02/27 12:07:11 by cacharle ### ########.fr #
# #
# **************************************************************************** #
-import minishell_test.config as config
-import minishell_test.hooks as hooks
+from minishell_test import config
+from minishell_test import hooks
from minishell_test.suite.decorator import suite
diff --git a/minishell_test/test/captured.py b/minishell_test/test/captured.py
index 7db9739..6ba861b 100644
--- a/minishell_test/test/captured.py
+++ b/minishell_test/test/captured.py
@@ -6,13 +6,14 @@
# By: charles <me@cacharle.xyz> +#+ +:+ +#+ #
# +#+#+#+#+#+ +#+ #
# Created: 2020/09/11 12:16:25 by charles #+# #+# #
-# Updated: 2021/02/05 17:47:10 by charles ### ########.fr #
+# Updated: 2021/02/27 16:15:13 by cacharle ### ########.fr #
# #
# ############################################################################ #
+import re
from typing import List, Optional
-import minishell_test.config as config
+from minishell_test import config
class Captured:
@@ -29,12 +30,12 @@ class Captured:
files_content: content of the files altered by the command
is_timeout: the command has timed out
"""
+
lines = output.split('\n')
- for i, l in enumerate(lines):
- if l.find(config.REFERENCE_ERROR_BEGIN) == 0:
- lines[i] = l.replace(config.REFERENCE_ERROR_BEGIN, config.MINISHELL_ERROR_BEGIN, 1)
- elif l.find(config.REFERENCE_PATH + ": ") == 0:
- lines[i] = l.replace(config.REFERENCE_PATH + ": ", config.MINISHELL_ERROR_BEGIN, 1)
+ for i, line in enumerate(lines):
+ lines[i] = line = re.sub("line [01]: ", "", lines[i], 1)
+ if line.startswith(config.SHELL_REFERENCE_PREFIX):
+ lines[i] = config.MINISHELL_PREFIX + line[len(config.SHELL_REFERENCE_PREFIX):]
self.output = '\n'.join(lines)
self.status = status
diff --git a/minishell_test/test/result.py b/minishell_test/test/result.py
index fe465e5..87eeb5d 100644
--- a/minishell_test/test/result.py
+++ b/minishell_test/test/result.py
@@ -6,15 +6,14 @@
# By: charles <me@cacharle.xyz> +#+ +:+ +#+ #
# +#+#+#+#+#+ +#+ #
# Created: 2020/09/11 12:17:34 by charles #+# #+# #
-# Updated: 2021/02/24 08:56:03 by cacharle ### ########.fr #
+# Updated: 2021/02/27 12:28:20 by cacharle ### ########.fr #
# #
# ############################################################################ #
-import sys
import re
from typing import Match, List, Optional
-import minishell_test.config as config
+from minishell_test import config
from minishell_test.test.captured import Captured
@@ -42,31 +41,18 @@ class BaseResult:
def __repr__(self):
"""Returns a representation of the result based on the verbosity"""
- if config.VERBOSE_LEVEL == 0:
- return self.green('.') if self.passed else self.red('!')
- if config.VERBOSE_LEVEL == 1:
- printed = self._escaped_cmd[:]
- if config.SHOW_RANGE:
- printed = "{:2}: ".format(self.index) + printed
- if len(printed) > config.TERM_COLS - 7:
- printed = printed[:config.TERM_COLS - 10] + "..."
- fmt = self.green("{:{width}} [PASS]") if self.passed else self.red("{:{width}} [FAIL]")
- return fmt.format(printed, width=config.TERM_COLS - 7)
- elif config.VERBOSE_LEVEL == 2:
- return self.full_diff()
- else:
- raise RuntimeError("Invalid verbose level")
+ printed = self._escaped_cmd[:]
+ if config.SHOW_RANGE:
+ printed = "{:2}: ".format(self.index) + printed
+ if len(printed) > config.TERM_COLS - 7:
+ printed = printed[:config.TERM_COLS - 10] + "..."
+ fmt = self.green("{:{width}} [PASS]") if self.passed else self.red("{:{width}} [FAIL]")
+ return fmt.format(printed, width=config.TERM_COLS - 7)
def put(self, index: int) -> None:
"""Print a summary of the result"""
- if config.VERBOSE_LEVEL == 2 and self.passed:
- return
self.index = index
- print(self, end="")
- if config.VERBOSE_LEVEL == 0:
- sys.stdout.flush()
- else:
- print()
+ print(self)
def indicator(self, title: str, prefix: str) -> str:
return self.bold(self.blue(prefix + " " + title))
diff --git a/minishell_test/test/test.py b/minishell_test/test/test.py
index ff60522..c0e402f 100644
--- a/minishell_test/test/test.py
+++ b/minishell_test/test/test.py
@@ -6,19 +6,20 @@
# By: charles <charles.cabergs@gmail.com> +#+ +:+ +#+ #
# +#+#+#+#+#+ +#+ #
# Created: 2020/06/16 21:48:50 by charles #+# #+# #
-# Updated: 2021/02/24 09:09:39 by cacharle ### ########.fr #
+# Updated: 2021/02/27 12:19:15 by cacharle ### ########.fr #
# #
# ############################################################################ #
import os
import sys
import subprocess
+from pathlib import Path
from typing import Optional, List, Dict, Union, Callable
-import minishell_test.config as config
+from minishell_test import config
from minishell_test.test.captured import Captured
from minishell_test.test.result import Result, LeakResult
-import minishell_test.sandbox as sandbox
+from minishell_test import sandbox
HookType = Union[Callable[[str], str], List[Callable[[str], str]]]
HookStatusType = Union[Callable[[int], int], List[Callable[[int], int]]]
@@ -31,7 +32,7 @@ class Test:
setup: str = "",
files: List[str] = [],
exports: Dict[str, str] = {},
- timeout: float = config.TIMEOUT,
+ timeout: float = config.TIMEOUT_TEST,
hook: HookType = [],
hook_status: HookStatusType = [],
):
@@ -64,18 +65,16 @@ class Test:
self.hook = []
self.hook_status = []
captured = self._run_sandboxed([*config.VALGRIND_CMD, "-c"])
- if config.VERBOSE_LEVEL == 2:
- print(captured.output)
self.result = LeakResult(self.full_cmd, captured)
self.result.put(index)
return
- expected = self._run_sandboxed([config.REFERENCE_PATH, *config.REFERENCE_ARGS, "-c"])
- actual = self._run_sandboxed([config.MINISHELL_PATH, "-c"])
+ expected = self._run_sandboxed([config.SHELL_REFERENCE_PATH, *config.SHELL_REFERENCE_ARGS, "-c"])
+ actual = self._run_sandboxed([config.MINISHELL_EXEC_PATH, "-c"])
self.result = Result(self.full_cmd, self.files, expected, actual)
self.result.put(index)
- def _run_sandboxed(self, shell_cmd: List[str]) -> Captured:
+ def _run_sandboxed(self, shell_cmd: List[Union[str, Path]]) -> Captured:
""" Run the command in a sandbox environment """
with sandbox.context():
if self.setup != "":
@@ -83,7 +82,7 @@ class Test:
subprocess.run(
self.setup,
shell=True,
- cwd=config.SANDBOX_PATH,
+ cwd=config.SANDBOX_DIR,
stderr=subprocess.STDOUT,
stdout=subprocess.PIPE,
check=True
@@ -97,7 +96,7 @@ class Test:
sys.exit(1)
return self._run_capture(shell_cmd)
- def _run_capture(self, shell_cmd: List[str]) -> Captured:
+ def _run_capture(self, shell_cmd: List[Union[str, Path]]) -> Captured:
""" Capture the output (stdout and stderr)
Capture the content of the watched files after the command is run
"""
@@ -106,9 +105,9 @@ class Test:
[*shell_cmd, self.cmd],
stderr=subprocess.STDOUT,
stdout=subprocess.PIPE,
- cwd=config.SANDBOX_PATH,
+ cwd=config.SANDBOX_DIR,
env={
- 'PATH': config.PATH_VARIABLE,
+ 'PATH': config.SHELL_PATH_VARIABLE,
'TERM': 'xterm-256color',
**self.exports,
},
@@ -117,7 +116,7 @@ class Test:
# https://docs.python.org/3/library/subprocess.html#subprocess.Popen.communicate
try:
stdout, _ = process.communicate(
- timeout=(self.timeout if not config.CHECK_LEAKS else config.CHECK_LEAKS_TIMEOUT))
+ timeout=(self.timeout if not config.CHECK_LEAKS else config.TIMEOUT_LEAKS))
except subprocess.TimeoutExpired:
process.kill()
# _, _ = process.communicate(timeout=2)
@@ -131,7 +130,7 @@ class Test:
files_content: List[Optional[str]] = []
for file_name in self.files:
try:
- with open(os.path.join(config.SANDBOX_PATH, file_name), "rb") as f:
+ with open(os.path.join(config.SANDBOX_DIR, file_name), "rb") as f:
files_content.append(f.read().decode())
except FileNotFoundError:
files_content.append(None)
@@ -156,7 +155,6 @@ class Test:
@classmethod
def try_run(cls, cmd: str) -> str:
- config.VERBOSE_LEVEL = 2
test = Test(cmd)
test.run(0)
if isinstance(test.result, LeakResult):
diff --git a/setup.cfg b/setup.cfg
index 9eea9b7..b31ad20 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -28,6 +28,9 @@ classifiers =
packages = find:
python_requires = >=3.6
+[options.package_data]
+c_formatter_42/data = *
+
[options.entry_points]
console_scripts =
minishell_test = minishell_test.__main__:main