From 958c410ba8b621a8a4d8caf04012028e7f151e0f Mon Sep 17 00:00:00 2001 From: Charles Cabergs Date: Fri, 11 Sep 2020 22:20:15 +0200 Subject: Added comments --- src/args.py | 4 +++- src/hooks.py | 6 +++++- src/sandbox.py | 6 +++++- src/suite/decorator.py | 8 +++++--- src/suite/suite.py | 23 +++++++++++++++-------- src/test/captured.py | 9 ++++++++- src/test/result.py | 23 +++++++++++++++++++---- src/test/test.py | 23 ++++++++++++++++++----- 8 files changed, 78 insertions(+), 24 deletions(-) (limited to 'src') diff --git a/src/args.py b/src/args.py index c9e637b..a79ce15 100644 --- a/src/args.py +++ b/src/args.py @@ -6,7 +6,7 @@ # By: charles +#+ +:+ +#+ # # +#+#+#+#+#+ +#+ # # Created: 2020/07/15 18:24:32 by charles #+# #+# # -# Updated: 2020/09/11 14:22:47 by charles ### ########.fr # +# Updated: 2020/09/11 22:09:04 by charles ### ########.fr # # # # ############################################################################ # @@ -14,6 +14,8 @@ import argparse def parse_args(): + """Parse command line arguments""" + parser = argparse.ArgumentParser( description="Minishell test", epilog="Make sure read README.md" diff --git a/src/hooks.py b/src/hooks.py index 7582c6e..770aca8 100644 --- a/src/hooks.py +++ b/src/hooks.py @@ -6,7 +6,7 @@ # By: charles +#+ +:+ +#+ # # +#+#+#+#+#+ +#+ # # Created: 2020/09/11 16:10:20 by charles #+# #+# # -# Updated: 2020/09/11 19:53:43 by charles ### ########.fr # +# Updated: 2020/09/11 22:11:21 by charles ### ########.fr # # # # ############################################################################ # @@ -16,10 +16,12 @@ 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""" lines = output.split('\n') if len(lines) != 3: return output @@ -30,10 +32,12 @@ def error_line0(output): 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""" return sort_lines( '\n'.join([line for line in output.split('\n') if regex.match("^declare -x .+=\".*\"$", line) is not None]) diff --git a/src/sandbox.py b/src/sandbox.py index 418243e..efad60c 100644 --- a/src/sandbox.py +++ b/src/sandbox.py @@ -6,7 +6,7 @@ # By: charles +#+ +:+ +#+ # # +#+#+#+#+#+ +#+ # # Created: 2020/09/11 13:48:07 by charles #+# #+# # -# Updated: 2020/09/11 19:53:13 by charles ### ########.fr # +# Updated: 2020/09/11 20:25:38 by charles ### ########.fr # # # # ############################################################################ # @@ -19,6 +19,7 @@ import config def create(): + """Create a new sandbox directory""" try: os.mkdir(config.SANDBOX_PATH) except OSError: @@ -26,6 +27,9 @@ def create(): 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: diff --git a/src/suite/decorator.py b/src/suite/decorator.py index 448e0a3..d4801c3 100644 --- a/src/suite/decorator.py +++ b/src/suite/decorator.py @@ -6,7 +6,7 @@ # By: charles +#+ +:+ +#+ # # +#+#+#+#+#+ +#+ # # Created: 2020/09/11 12:28:00 by charles #+# #+# # -# Updated: 2020/09/11 20:08:27 by charles ### ########.fr # +# Updated: 2020/09/11 22:08:36 by charles ### ########.fr # # # # ############################################################################ # @@ -16,8 +16,10 @@ import inspect def suite(groups: [str] = [], bonus: bool = False): + """Decorator generator for suites arguments""" + def suite_wrapper(origin): - """ decorator for a suite function (fmt: suite_[name]) """ + """Decorator for a suite function (fmt: suite_[name]) """ mod_name = inspect.getmodule(origin).__name__[len("suites."):] # print(mod_name) @@ -30,7 +32,7 @@ def suite(groups: [str] = [], bonus: bool = False): s.add(Test(*args, **kwargs)) origin(test) - s.add_generator(test_generator) + s.generator_func = test_generator Suite.available.append(s) return test_generator diff --git a/src/suite/suite.py b/src/suite/suite.py index a51da65..2ac64e4 100644 --- a/src/suite/suite.py +++ b/src/suite/suite.py @@ -6,7 +6,7 @@ # By: charles +#+ +:+ +#+ # # +#+#+#+#+#+ +#+ # # Created: 2020/07/15 18:24:29 by charles #+# #+# # -# Updated: 2020/09/11 20:08:39 by charles ### ########.fr # +# Updated: 2020/09/11 20:35:13 by charles ### ########.fr # # # # ############################################################################ # @@ -18,11 +18,13 @@ class Suite: @classmethod def run_all(cls): + """Run all available suites""" for s in cls.available: s.run() @classmethod def setup(cls, asked_names: [str]): + """Remove not asked suite from available suites""" if len(asked_names) == 0: asked_names = [s.name for s in cls.available] if not config.BONUS: @@ -32,13 +34,19 @@ class Suite: + [s for s in cls.available if any([g for g in s.groups if g in asked_names])] )) for s in cls.available: - s.generate() + s.generator_func() @classmethod def available_names(cls) -> [str]: + """List of available suites names""" return [s.name for s in cls.available] def __init__(self, name: str, groups: [str], bonus: bool = False): + """Suite class + name: suite id + groups: list of suite groups + bonus: is this suite bonus + """ self.name = name self.groups = groups self.bonus = bonus @@ -46,12 +54,11 @@ class Suite: self.tests = [] def add(self, test): + """Append a test to the suite""" self.tests.append(test) - def add_generator(self, generator): - self.generator_func = generator - def run(self): + """Run all test in the suite""" if config.VERBOSE_LEVEL == 0: print(self.name + ": ", end="") else: @@ -61,10 +68,8 @@ class Suite: if config.VERBOSE_LEVEL == 0: print() - def generate(self): - self.generator_func() - def total(self) -> (int, int): + """Returns the total of passed and failed tests""" passed_total = 0 for t in self.tests: if t.result is None: @@ -75,6 +80,7 @@ class Suite: @classmethod def summarize(cls): + """Print a summary of all runned suites""" pass_sum = 0 fail_sum = 0 print("\nSummary:") @@ -91,6 +97,7 @@ class Suite: @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: diff --git a/src/test/captured.py b/src/test/captured.py index b42352e..f855212 100644 --- a/src/test/captured.py +++ b/src/test/captured.py @@ -6,7 +6,7 @@ # By: charles +#+ +:+ +#+ # # +#+#+#+#+#+ +#+ # # Created: 2020/09/11 12:16:25 by charles #+# #+# # -# Updated: 2020/09/11 20:08:00 by charles ### ########.fr # +# Updated: 2020/09/11 20:42:05 by charles ### ########.fr # # # # ############################################################################ # @@ -15,6 +15,12 @@ import config class Captured: def __init__(self, output: str, status: int, files_content: [str], is_timeout: bool = False): + """Captured class + output: captured content + status: command status + 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: @@ -37,4 +43,5 @@ class Captured: @staticmethod def timeout(): + """Create a new captured timeout""" return Captured("", 0, [], is_timeout=True) diff --git a/src/test/result.py b/src/test/result.py index 4f46e52..c64f20a 100644 --- a/src/test/result.py +++ b/src/test/result.py @@ -6,7 +6,7 @@ # By: charles +#+ +:+ +#+ # # +#+#+#+#+#+ +#+ # # Created: 2020/09/11 12:17:34 by charles #+# #+# # -# Updated: 2020/09/11 20:06:31 by charles ### ########.fr # +# Updated: 2020/09/11 22:20:03 by charles ### ########.fr # # # # ############################################################################ # @@ -24,6 +24,12 @@ class Result: CLOSE_CHARS = "\033[0m" def __init__(self, cmd: str, file_names: [str], expected: Captured, actual: Captured): + """Result class + cmd: runned command + file_names: names of watched files + expected: expected capture + actual: actual capture + """ self.cmd = cmd self.file_names = file_names self.expected = expected @@ -31,10 +37,8 @@ class Result: self.colored = True self.set_colors() - def toggle_colors(self): - self.colored = not self.colored - def set_colors(self): + """Set colors strings on or off based on self.colored""" if self.colored: self.color_red = self.RED_CHARS self.color_green = self.GREEN_CHARS @@ -62,13 +66,16 @@ class Result: @property def passed(self): + """Check if the result passed""" return self.actual == self.expected @property def failed(self): + """Check if the result failed""" return not self.passed 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('!') elif config.VERBOSE_LEVEL == 1: @@ -83,6 +90,7 @@ class Result: raise RuntimeError def put(self): + """Print a summary of the result""" if config.VERBOSE_LEVEL == 2 and self.passed: return print(self, end="") @@ -92,6 +100,7 @@ class Result: print() def header(self, title: str) -> str: + """Create a one line header with a title""" return self.bold("|---------------------------------------{:-<40}".format(title)) @property @@ -106,6 +115,7 @@ class Result: return self.bold(self.blue(prefix + " " + title)) def file_diff(self, file_name: str, expected: str, actual: str) -> str: + """Difference between 2 files""" if expected == actual: return "" return ( @@ -117,6 +127,7 @@ class Result: ) def files_diff(self): + """Difference between watched files""" return '\n'.join([self.file_diff(n, e, a) for n, e, a in zip(self.file_names, self.expected.files_content, @@ -124,6 +135,7 @@ class Result: if e != a]) def output_diff(self) -> str: + """Difference in command output""" out = "" if self.actual.is_timeout: return "TIMEOUT\n" @@ -140,12 +152,14 @@ class Result: return out def full_diff(self) -> str: + """Concat all difference reports""" return (self.indicator("WITH {}".format(self.escaped_cmd), "|>") + '\n' + self.output_diff() + self.files_diff() + "=" * 80 + '\n') def cat_e(self, s: str) -> str: + """Pass a string through a cat -e like output""" s = s.replace("\n", "$\n") if len(s) < 2: return s @@ -155,6 +169,7 @@ class Result: @property def escaped_cmd(self): + """Escape common control characters""" return (self.cmd .replace("\t", "\\t") .replace("\n", "\\n") diff --git a/src/test/test.py b/src/test/test.py index 48f05a0..054086a 100644 --- a/src/test/test.py +++ b/src/test/test.py @@ -6,7 +6,7 @@ # By: charles +#+ +:+ +#+ # # +#+#+#+#+#+ +#+ # # Created: 2020/06/16 21:48:50 by charles #+# #+# # -# Updated: 2020/09/11 20:00:52 by charles ### ########.fr # +# Updated: 2020/09/11 20:39:52 by charles ### ########.fr # # # # ############################################################################ # @@ -30,6 +30,15 @@ class Test: timeout: float = config.TIMEOUT, signal=None, hook=None): + """Test class + cmd: command to execute + setup: command to execute before tested command + files: files to watch (check content after test) + exports: exported variables + timeout: maximum amount of time taken by the test + signal: signal to send to the test + hook: function to execute on the output of the test + """ self.cmd = cmd self.setup = setup self.files = files @@ -40,6 +49,7 @@ class Test: self.hook = hook def run(self): + """Run the test for minishell and the reference shell and print the result out""" expected = self._run_sandboxed(config.REFERENCE_PATH, "-c") actual = self._run_sandboxed(config.MINISHELL_PATH, "-c") s = self.cmd @@ -52,12 +62,12 @@ class Test: self.result.put() def _run_sandboxed(self, shell_path: str, shell_option: str) -> Captured: - """ run the command in a sandbox environment - - capture the output (stdout and stderr) - capture the content of the watched files after the command is run + """ Run the command in a sandbox environment + Capture the output (stdout and stderr) + Capture the content of the watched files after the command is run """ + # create and setup sandbox sandbox.create() if self.setup != "": try: @@ -76,6 +86,7 @@ class Test: "no stderr" if e.stdout is None else e.stdout.decode().strip())) sys.exit(1) + # run the command in the sandbox process = subprocess.Popen( [shell_path, shell_option, self.cmd], stderr=subprocess.STDOUT, @@ -96,6 +107,7 @@ class Test: except subprocess.TimeoutExpired: return Captured.timeout() + # get command output try: stdout, _ = process.communicate() output = stdout.decode() @@ -110,6 +122,7 @@ class Test: files_content.append(f.read().decode()) except FileNotFoundError: files_content.append(None) + sandbox.remove() if self.hook is not None: output = self.hook(output) -- cgit