aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/args.py4
-rw-r--r--src/hooks.py6
-rw-r--r--src/sandbox.py6
-rw-r--r--src/suite/decorator.py8
-rw-r--r--src/suite/suite.py23
-rw-r--r--src/test/captured.py9
-rw-r--r--src/test/result.py23
-rw-r--r--src/test/test.py23
8 files changed, 78 insertions, 24 deletions
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 <charles.cabergs@gmail.com> +#+ +:+ +#+ #
# +#+#+#+#+#+ +#+ #
# 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 <me@cacharle.xyz> +#+ +:+ +#+ #
# +#+#+#+#+#+ +#+ #
# 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 <me@cacharle.xyz> +#+ +:+ +#+ #
# +#+#+#+#+#+ +#+ #
# 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 <me@cacharle.xyz> +#+ +:+ +#+ #
# +#+#+#+#+#+ +#+ #
# 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 <charles.cabergs@gmail.com> +#+ +:+ +#+ #
# +#+#+#+#+#+ +#+ #
# 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 <me@cacharle.xyz> +#+ +:+ +#+ #
# +#+#+#+#+#+ +#+ #
# 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 <me@cacharle.xyz> +#+ +:+ +#+ #
# +#+#+#+#+#+ +#+ #
# 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 <charles.cabergs@gmail.com> +#+ +:+ +#+ #
# +#+#+#+#+#+ +#+ #
# 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)