aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--minishell_test/suite/suite.py20
-rw-r--r--minishell_test/test/result.py109
-rw-r--r--tests/helpers.py27
-rw-r--r--tests/test/test_result.py467
-rw-r--r--tests/test_hooks.py31
5 files changed, 555 insertions, 99 deletions
diff --git a/minishell_test/suite/suite.py b/minishell_test/suite/suite.py
index b058569..2a87fbc 100644
--- a/minishell_test/suite/suite.py
+++ b/minishell_test/suite/suite.py
@@ -6,7 +6,7 @@
# By: charles <charles.cabergs@gmail.com> +#+ +:+ +#+ #
# +#+#+#+#+#+ +#+ #
# Created: 2020/07/15 18:24:29 by charles #+# #+# #
-# Updated: 2021/03/02 11:12:35 by cacharle ### ########.fr #
+# Updated: 2021/03/02 13:17:31 by cacharle ### ########.fr #
# #
# ############################################################################ #
@@ -129,6 +129,7 @@ class Suite:
self.bonus = bonus
self.generator_func: Optional[Callable] = None
self.tests: List[Test] = []
+ self.results: List[Result] = []
def add(self, test):
"""Append a test to the suite"""
@@ -149,6 +150,7 @@ class Suite:
self.tests = self.test[Config.range[0] : Config.range[1] + 1]
for i, test in enumerate(self.tests):
result = test.run()
+ self.results.append(result)
print(result.to_string(i))
if Config.exit_first and result is not None and result.failed:
return False
@@ -157,10 +159,10 @@ class Suite:
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:
+ for result in self.results:
+ if result is None:
return (-1, -1)
- if t.result.passed:
+ if result.passed:
passed_total += 1
return passed_total, len(self.tests) - passed_total
@@ -184,10 +186,8 @@ class Suite:
@classmethod
def save_log(cls):
"""Save the result of all suites to a file"""
+ colors.disable()
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')
+ for result in self.results:
+ if result is not None and result.failed:
+ log_file.write(result.full_diff() + '\n')
diff --git a/minishell_test/test/result.py b/minishell_test/test/result.py
index 566520a..7936fb3 100644
--- a/minishell_test/test/result.py
+++ b/minishell_test/test/result.py
@@ -6,15 +6,15 @@
# By: charles <me@cacharle.xyz> +#+ +:+ +#+ #
# +#+#+#+#+#+ +#+ #
# Created: 2020/09/11 12:17:34 by charles #+# #+# #
-# Updated: 2021/03/02 14:17:01 by cacharle ### ########.fr #
+# Updated: 2021/03/02 17:44:02 by cacharle ### ########.fr #
# #
# ############################################################################ #
import re
-from typing import Match, List, Optional, Union
+from typing import Match, List
from minishell_test.config import Config
-from minishell_test.colors import green, red, blue, bold
+from minishell_test.colors import green, red
from minishell_test.test.captured import CapturedCommand, CapturedTimeout, CapturedType
@@ -76,7 +76,6 @@ class BaseResult:
return f"|> WITH {self._cmd}\n"
-
class Result(BaseResult):
def __init__(
self,
@@ -96,6 +95,8 @@ class Result(BaseResult):
:param actual:
actual capture
"""
+ if isinstance(expected, CapturedTimeout):
+ raise RuntimeError
super().__init__(cmd)
self.file_names = file_names
self.expected = expected
@@ -110,8 +111,7 @@ class Result(BaseResult):
return (
self._cmd_header +
self._cmd_diff() +
- self._files_diff() +
- "=" * 80 + '\n'
+ self._files_diff()
)
def _cmd_diff(self) -> str:
@@ -125,7 +125,7 @@ class Result(BaseResult):
out += self._content_diff(self.expected.output, self.actual.output)
return out
- _FILE_NOT_CREATED_MESSAGE = "FROM TEST: File not created\n"
+ _FILE_NOT_CREATED_MESSAGE = "FROM TEST: File not created"
def _files_diff(self):
"""Difference between watched files"""
@@ -133,62 +133,57 @@ class Result(BaseResult):
if isinstance(self.actual, CapturedTimeout):
return ""
- def diff(name, expected, actual):
- expected = expected or self._FILE_NOT_CREATED_MESSAGE
- actual = actual or self._FILE_NOT_CREATED_MESSAGE
+ def diff(file_name, expected, actual):
+ if expected is None:
+ expected = self._FILE_NOT_CREATED_MESSAGE
+ if actual is None:
+ actual = self._FILE_NOT_CREATED_MESSAGE
return f"|# FILE {file_name}\n" + self._content_diff(expected, actual)
- return '\n'.join([
+ return ''.join([
diff(name, expected, actual)
for name, expected, actual in
- zip(
- self.file_names,
- self.expected.files_content,
- self.actual.files_content
- )
+ zip(
+ self.file_names,
+ self.expected.files_content,
+ self.actual.files_content
+ )
if expected != actual
])
def _content_diff(self, expected: str, actual: str) -> str:
- return (
- self._expected_header +
- self._show_newlines(expected) +
- self._actual_header +
- self._show_newlines(actual)
- )
-
- def _header(self, title: str) -> str:
- """Create a 80 characters wide header in the format ``-- title --``"""
- return f"|{'-' * 40}{title:-<40}\n"
-
- @property
- def _expected_header(self) -> str:
- return self._header("EXPECTED")
-
- @property
- def _actual_header(self) -> str:
- return self._header("ACTUAL")
-
- def _show_newlines(self, s: str) -> str:
"""Add a ``$`` at the end of each newline
If the string doesn't end with a newline add one but doesn't add a
``$`` to represent it.
"""
- s = s.replace("\n", "$\n")
- if len(s) < 2:
+
+ def header(title):
+ return f"|{'-' * 40}{title:-<39}\n"
+
+ def show_newlines(s):
+ s = s.replace("\n", "$\n")
+ if len(s) < 2:
+ return s
+ if s[-1] != '\n':
+ s += '\n'
return s
- if s[-1] != '\n':
- s += '\n'
- return s
+
+ return (
+ header("EXPECTED") +
+ show_newlines(expected) +
+ header("ACTUAL") +
+ show_newlines(actual)
+ )
class LeakResultException(Exception):
- def __init__(self, result: 'LeakResult'):
- self._result = result
+ def __init__(self, cmd: str, captured: CapturedCommand):
+ self._cmd = cmd
+ self._captured = captured
def __str__(self) -> str:
- return f"valgrind output parsing failed for `{self._result._cmd}`:\n{self._result._captured.output}"
+ return f"valgrind output parsing failed for `{self._cmd}`:\n{self._captured.output}"
class LeakResult(BaseResult):
@@ -197,33 +192,31 @@ class LeakResult(BaseResult):
super().__init__(cmd)
def __repr__(self) -> str:
- return self._cmd_header + self.captured.output
-
- @property
- def passed(self) -> str:
if isinstance(self._captured, CapturedTimeout):
- return False
- return self._lost_bytes == 0
+ return self._cmd_header + "TIMEOUT\n"
+ return self._cmd_header + self._captured.output
_VALGRIND_OK_MESSAGE = "All heap blocks were freed -- no leaks are possible"
@property
- def _lost_bytes(self):
+ def passed(self) -> bool:
+ if isinstance(self._captured, CapturedTimeout):
+ return False
# Some versions of valgrind don't output `definitely` and `indirectly`
# when no leaks are found.
if self._captured.output.find(self._VALGRIND_OK_MESSAGE) != -1:
- return 0
- definite_match = self._search_leak_kind("definitely")
- indirect_match = self._search_leak_kind("indirectly")
+ return True
+ definite_match = self._search_leak_kind("definitely", self._captured)
+ indirect_match = self._search_leak_kind("indirectly", self._captured)
definite_bytes = int(definite_match.group("bytes").replace(",", ""))
indirect_bytes = int(indirect_match.group("bytes").replace(",", ""))
- return definite_bytes + indirect_bytes
+ return (definite_bytes + indirect_bytes) == 0
- def _search_leak_kind(self, kind: str) -> Match:
+ def _search_leak_kind(self, kind: str, captured: CapturedCommand) -> Match:
match = re.search(
r"==\d+==\s+" + kind + r" lost: (?P<bytes>[0-9,]+) bytes in [0-9,]+ blocks",
- self._captured.output
+ captured.output
)
if match is None:
- raise LeakResultException(self)
+ raise LeakResultException(self._cmd, captured)
return match
diff --git a/tests/helpers.py b/tests/helpers.py
new file mode 100644
index 0000000..f64be06
--- /dev/null
+++ b/tests/helpers.py
@@ -0,0 +1,27 @@
+# ############################################################################ #
+# #
+# ::: :::::::: #
+# helpers.py :+: :+: :+: #
+# +:+ +:+ +:+ #
+# By: cacharle <me@cacharle.xyz> +#+ +:+ +#+ #
+# +#+#+#+#+#+ +#+ #
+# Created: 2021/03/02 14:37:38 by cacharle #+# #+# #
+# Updated: 2021/03/02 17:45:28 by cacharle ### ########.fr #
+# #
+# ############################################################################ #
+
+import contextlib
+
+from minishell_test.config import Config
+
+
+@contextlib.contextmanager
+def config_context(**kwargs):
+ prevs = {attr: getattr(Config, attr) for attr in kwargs.keys()}
+ for attr, value in kwargs.items():
+ setattr(Config, attr, value)
+ try:
+ yield
+ finally:
+ for attr, value in prevs.items():
+ setattr(Config, attr, value)
diff --git a/tests/test/test_result.py b/tests/test/test_result.py
index 35c6213..7b29f66 100644
--- a/tests/test/test_result.py
+++ b/tests/test/test_result.py
@@ -6,7 +6,7 @@
# By: cacharle <me@cacharle.xyz> +#+ +:+ +#+ #
# +#+#+#+#+#+ +#+ #
# Created: 2021/03/01 16:26:34 by cacharle #+# #+# #
-# Updated: 2021/03/02 14:21:14 by cacharle ### ########.fr #
+# Updated: 2021/03/02 17:47:59 by cacharle ### ########.fr #
# #
# ############################################################################ #
@@ -15,12 +15,15 @@ import pytest
from minishell_test.config import Config
from minishell_test import colors
+from minishell_test.test.result import BaseResult, Result, LeakResult, LeakResultException
+from minishell_test.test.captured import CapturedCommand, CapturedTimeout
+
+from tests.helpers import config_context
+
+
colors.disable()
Config.init([])
-from minishell_test.test.result import BaseResult, Result, LeakResult
-from minishell_test.test.captured import CapturedCommand
-
class TestBaseResult:
@pytest.fixture
@@ -39,6 +42,10 @@ class TestBaseResult:
with pytest.raises(NotImplementedError):
base_result.__repr__()
+ def test_summarize(self, base_result):
+ with pytest.raises(NotImplementedError):
+ base_result.summarize(0)
+
def test_cmd(self, base_result):
assert "echo bonjour" == base_result._cmd
assert "foo\\nbar" == BaseResult("foo\nbar")._cmd
@@ -47,11 +54,6 @@ class TestBaseResult:
assert "foo\\rbar" == BaseResult("foo\rbar")._cmd
assert "foo\\fbar" == BaseResult("foo\fbar")._cmd
- def test_summarize(self, base_result):
- pass
-
-
-
class TestResult:
@pytest.fixture
@@ -72,11 +74,454 @@ class TestResult:
CapturedCommand("aurevoir", 0, []),
)
- def test_passed(self, result_pass, result_fail):
+ @pytest.fixture
+ def result_fail_status(self):
+ return Result(
+ "echo bonjour",
+ [],
+ CapturedCommand("bonjour", 0, []),
+ CapturedCommand("bonjour", 1, []),
+ )
+
+ @pytest.fixture
+ def result_fail_file(self):
+ return Result(
+ "echo bonjour > foo",
+ ["foo"],
+ CapturedCommand("bonjour", 0, ["bonjour"]),
+ CapturedCommand("bonjour", 0, ["aurevoir"]),
+ )
+
+ @pytest.fixture
+ def result_fail_file_not_exist(self):
+ return Result(
+ "echo bonjour > foo",
+ ["foo"],
+ CapturedCommand("bonjour", 0, ["bonjour"]),
+ CapturedCommand("bonjour", 0, [None]),
+ )
+
+ @pytest.fixture
+ def result_fail_file_multiple(self):
+ return Result(
+ "echo bonjour > foo > bar",
+ ["foo", "bar"],
+ CapturedCommand("bonjour", 0, ["", "bonjour"]),
+ CapturedCommand("bonjour", 0, ["bonjour", None]),
+ )
+
+ @pytest.fixture
+ def result_fail_timeout(self):
+ return Result("echo bonjour", [], CapturedCommand("bonjour", 0, []), CapturedTimeout())
+
+ def test_passed(self, result_pass, result_fail, result_fail_status,
+ result_fail_file, result_fail_file_not_exist, result_fail_file_multiple, result_fail_timeout):
assert result_pass.passed
assert not result_fail.passed
+ assert not result_fail_status.passed
+ assert not result_fail_file.passed
+ assert not result_fail_file_not_exist.passed
+ assert not result_fail_file_multiple.passed
+ assert not result_fail_timeout.passed
- def test_failed(self, result_pass, result_fail):
+ def test_failed(self, result_pass, result_fail, result_fail_status,
+ result_fail_file, result_fail_file_not_exist, result_fail_file_multiple, result_fail_timeout):
assert not result_pass.failed
assert result_fail.failed
+ assert result_fail_status.failed
+ assert result_fail_file.failed
+ assert result_fail_file_not_exist.failed
+ assert result_fail_file_multiple.failed
+ assert result_fail_timeout.failed
+
+ @pytest.mark.parametrize("term_cols", range(40, 300, 40))
+ def test_summarize(self, result_pass, result_fail, result_fail_status,
+ result_fail_file, result_fail_file_not_exist, result_fail_timeout, term_cols):
+ with config_context(show_range=False, term_cols=term_cols):
+ assert f"{'echo bonjour':{term_cols - 7}} [PASS]" == result_pass.summarize(-1)
+ assert f"{'echo bonjour':{term_cols - 7}} [FAIL]" == result_fail.summarize(-1)
+ assert f"{'echo bonjour':{term_cols - 7}} [FAIL]" == result_fail_status.summarize(-1)
+ assert f"{'echo bonjour > foo':{term_cols - 7}} [FAIL]" == result_fail_file.summarize(-1)
+ assert f"{'echo bonjour > foo':{term_cols - 7}} [FAIL]" == result_fail_file_not_exist.summarize(-1)
+ assert f"{'echo bonjour':{term_cols - 7}} [FAIL]" == result_fail_timeout.summarize(-1)
+ with config_context(show_range=True, term_cols=term_cols):
+ assert f" 1: {'echo bonjour':{term_cols - 11}} [PASS]" == result_pass.summarize(1)
+ assert f" 1: {'echo bonjour':{term_cols - 11}} [FAIL]" == result_fail.summarize(1)
+ assert f" 1: {'echo bonjour':{term_cols - 11}} [FAIL]" == result_fail_status.summarize(1)
+ assert f" 1: {'echo bonjour > foo':{term_cols - 11}} [FAIL]" == result_fail_file.summarize(1)
+ assert f" 1: {'echo bonjour > foo':{term_cols - 11}} [FAIL]" == result_fail_file_not_exist.summarize(1)
+ assert f" 1: {'echo bonjour':{term_cols - 11}} [FAIL]" == result_fail_timeout.summarize(1)
+ assert f"99: {'echo bonjour':{term_cols - 11}} [PASS]" == result_pass.summarize(99)
+ assert f"99: {'echo bonjour':{term_cols - 11}} [FAIL]" == result_fail.summarize(99)
+ assert f"99: {'echo bonjour':{term_cols - 11}} [FAIL]" == result_fail_status.summarize(99)
+ assert f"99: {'echo bonjour > foo':{term_cols - 11}} [FAIL]" == result_fail_file.summarize(99)
+ assert f"99: {'echo bonjour > foo':{term_cols - 11}} [FAIL]" == result_fail_file_not_exist.summarize(99)
+ assert f"99: {'echo bonjour':{term_cols - 11}} [FAIL]" == result_fail_timeout.summarize(99)
+ assert f"100: {'echo bonjour':{term_cols - 12}} [PASS]" == result_pass.summarize(100)
+ assert f"100: {'echo bonjour':{term_cols - 12}} [FAIL]" == result_fail.summarize(100)
+ assert f"100: {'echo bonjour':{term_cols - 12}} [FAIL]" == result_fail_status.summarize(100)
+ assert f"100: {'echo bonjour > foo':{term_cols - 12}} [FAIL]" == result_fail_file.summarize(100)
+ assert f"100: {'echo bonjour > foo':{term_cols - 12}} [FAIL]" == result_fail_file_not_exist.summarize(100)
+ assert f"100: {'echo bonjour':{term_cols - 12}} [FAIL]" == result_fail_timeout.summarize(100)
+
+ def test_repr(self, result_fail, result_fail_status, result_fail_file,
+ result_fail_file_not_exist, result_fail_file_multiple, result_fail_timeout):
+ assert """\
+|> WITH echo bonjour
+|----------------------------------------EXPECTED-------------------------------
+bonjour
+|----------------------------------------ACTUAL---------------------------------
+aurevoir
+""" == result_fail.__repr__()
+ assert """\
+|> WITH echo bonjour
+| STATUS: expected 0 actual 1
+""" == result_fail_status.__repr__()
+ assert """\
+|> WITH echo bonjour > foo
+|# FILE foo
+|----------------------------------------EXPECTED-------------------------------
+bonjour
+|----------------------------------------ACTUAL---------------------------------
+aurevoir
+""" == result_fail_file.__repr__()
+ assert """\
+|> WITH echo bonjour > foo
+|# FILE foo
+|----------------------------------------EXPECTED-------------------------------
+bonjour
+|----------------------------------------ACTUAL---------------------------------
+FROM TEST: File not created
+""" == result_fail_file_not_exist.__repr__()
+ assert """\
+|> WITH echo bonjour > foo > bar
+|# FILE foo
+|----------------------------------------EXPECTED-------------------------------
+|----------------------------------------ACTUAL---------------------------------
+bonjour
+|# FILE bar
+|----------------------------------------EXPECTED-------------------------------
+bonjour
+|----------------------------------------ACTUAL---------------------------------
+FROM TEST: File not created
+""" == result_fail_file_multiple.__repr__()
+ assert """\
+|> WITH echo bonjour
+TIMEOUT
+""" == result_fail_timeout.__repr__()
+
+ def test_expected_is_timeout(self):
+ with pytest.raises(RuntimeError):
+ Result("echo bonjour", [], CapturedTimeout(), CapturedCommand("bonjour", 0, []))
+
+
+class TestLeakResult:
+ @pytest.fixture
+ def result_leak_pass(self):
+ valgrind_output = r"""
+==33584== Memcheck, a memory error detector
+==33584== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
+==33584== Using Valgrind-3.16.1 and LibVEX; rerun with -h for copyright info
+==33584== Command: ./minishell -c echo\ bonjour
+==33584==
+bonjour
+==33584==
+==33584== HEAP SUMMARY:
+==33584== in use at exit: 0 bytes in 0 blocks
+==33584== total heap usage: 88 allocs, 88 frees, 102,949 bytes allocated
+==33584==
+==33584== LEAK SUMMARY:
+==33584== definitely lost: 0 bytes in 0 blocks
+==33584== indirectly lost: 0 bytes in 0 blocks
+==33584== possibly lost: 0 bytes in 0 blocks
+==33584== still reachable: 0 bytes in 0 blocks
+==33584== suppressed: 0 bytes in 0 blocks
+==33584==
+==33584== For lists of detected and suppressed errors, rerun with: -s
+==33584== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
+""".lstrip("\n")
+ return LeakResult("echo bonjour", CapturedCommand(valgrind_output, 0, []))
+
+ @pytest.fixture
+ def result_leak_fail_definitive(self):
+ valgrind_output = r"""
+==32896== Memcheck, a memory error detector
+==32896== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
+==32896== Using Valgrind-3.16.1 and LibVEX; rerun with -h for copyright info
+==32896== Command: ./minishell -c echo\ bonjour
+==32896==
+bonjour
+==32896==
+==32896== HEAP SUMMARY:
+==32896== in use at exit: 3,787 bytes in 75 blocks
+==32896== total heap usage: 88 allocs, 13 frees, 102,949 bytes allocated
+==32896==
+==32896== 3,787 (24 direct, 3,763 indirect) bytes in 1 blocks are definitely lost in loss record 5 of 5
+==32896== at 0x483E77F: malloc (vg_replace_malloc.c:307)
+==32896== by 0x10D317: ft_vecnew (ft_vecnew.c:26)
+==32896== by 0x109382: env_from_array (env.c:33)
+==32896== by 0x1091C1: main (main.c:111)
+==32896==
+==32896== LEAK SUMMARY:
+==32896== definitely lost: 24 bytes in 1 blocks
+==32896== indirectly lost: 0 bytes in 0 blocks
+==32896== possibly lost: 0 bytes in 0 blocks
+==32896== still reachable: 0 bytes in 0 blocks
+==32896== suppressed: 0 bytes in 0 blocks
+==32896==
+==32896== For lists of detected and suppressed errors, rerun with: -s
+==32896== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
+""".lstrip("\n")
+ return LeakResult("echo bonjour", CapturedCommand(valgrind_output, 0, []))
+
+ @pytest.fixture
+ def result_leak_fail_indirect(self):
+ valgrind_output = r"""
+==32896== Memcheck, a memory error detector
+==32896== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
+==32896== Using Valgrind-3.16.1 and LibVEX; rerun with -h for copyright info
+==32896== Command: ./minishell -c echo\ bonjour
+==32896==
+bonjour
+==32896==
+==32896== HEAP SUMMARY:
+==32896== in use at exit: 3,787 bytes in 75 blocks
+==32896== total heap usage: 88 allocs, 13 frees, 102,949 bytes allocated
+==32896==
+==32896== 3,787 (24 direct, 3,763 indirect) bytes in 1 blocks are definitely lost in loss record 5 of 5
+==32896== at 0x483E77F: malloc (vg_replace_malloc.c:307)
+==32896== by 0x10D317: ft_vecnew (ft_vecnew.c:26)
+==32896== by 0x109382: env_from_array (env.c:33)
+==32896== by 0x1091C1: main (main.c:111)
+==32896==
+==32896== LEAK SUMMARY:
+==32896== definitely lost: 0 bytes in 0 blocks
+==32896== indirectly lost: 3,763 bytes in 74 blocks
+==32896== possibly lost: 0 bytes in 0 blocks
+==32896== still reachable: 0 bytes in 0 blocks
+==32896== suppressed: 0 bytes in 0 blocks
+==32896==
+==32896== For lists of detected and suppressed errors, rerun with: -s
+==32896== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
+""".lstrip("\n")
+ return LeakResult("echo bonjour", CapturedCommand(valgrind_output, 0, []))
+
+ @pytest.fixture
+ def result_leak_fail_both(self):
+ valgrind_output = r"""
+==32896== Memcheck, a memory error detector
+==32896== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
+==32896== Using Valgrind-3.16.1 and LibVEX; rerun with -h for copyright info
+==32896== Command: ./minishell -c echo\ bonjour
+==32896==
+bonjour
+==32896==
+==32896== HEAP SUMMARY:
+==32896== in use at exit: 3,787 bytes in 75 blocks
+==32896== total heap usage: 88 allocs, 13 frees, 102,949 bytes allocated
+==32896==
+==32896== 3,787 (24 direct, 3,763 indirect) bytes in 1 blocks are definitely lost in loss record 5 of 5
+==32896== at 0x483E77F: malloc (vg_replace_malloc.c:307)
+==32896== by 0x10D317: ft_vecnew (ft_vecnew.c:26)
+==32896== by 0x109382: env_from_array (env.c:33)
+==32896== by 0x1091C1: main (main.c:111)
+==32896==
+==32896== LEAK SUMMARY:
+==32896== definitely lost: 24 bytes in 1 blocks
+==32896== indirectly lost: 3,763 bytes in 74 blocks
+==32896== possibly lost: 0 bytes in 0 blocks
+==32896== still reachable: 0 bytes in 0 blocks
+==32896== suppressed: 0 bytes in 0 blocks
+==32896==
+==32896== For lists of detected and suppressed errors, rerun with: -s
+==32896== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
+""".lstrip("\n")
+ return LeakResult("echo bonjour", CapturedCommand(valgrind_output, 0, []))
+
+ @pytest.fixture
+ def result_leak_pass_no_count(self):
+ valgrind_output = r"""
+==33584== Memcheck, a memory error detector
+==33584== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
+==33584== Using Valgrind-3.16.1 and LibVEX; rerun with -h for copyright info
+==33584== Command: ./minishell -c echo\ bonjour
+==33584==
+bonjour
+==33584==
+==33584== HEAP SUMMARY:
+==33584== in use at exit: 0 bytes in 0 blocks
+==33584== total heap usage: 88 allocs, 88 frees, 102,949 bytes allocated
+==33584==
+==33584== All heap blocks were freed -- no leaks are possible
+==33584==
+==33584== For lists of detected and suppressed errors, rerun with: -s
+==33584== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
+""".lstrip("\n")
+ return LeakResult("echo bonjour", CapturedCommand(valgrind_output, 0, []))
+
+ @pytest.fixture
+ def result_leak_fail_timeout(self):
+ return LeakResult("echo bonjour", CapturedTimeout())
+
+ def test_passed(self, result_leak_pass, result_leak_fail_indirect, result_leak_fail_definitive,
+ result_leak_fail_both, result_leak_pass_no_count, result_leak_fail_timeout):
+ assert result_leak_pass.passed
+ assert result_leak_pass_no_count.passed
+ assert not result_leak_fail_indirect.passed
+ assert not result_leak_fail_definitive.passed
+ assert not result_leak_fail_both.passed
+ assert not result_leak_fail_timeout.passed
+
+ def test_failed(self, result_leak_pass, result_leak_fail_indirect, result_leak_fail_definitive,
+ result_leak_fail_both, result_leak_pass_no_count, result_leak_fail_timeout):
+ assert not result_leak_pass.failed
+ assert not result_leak_pass_no_count.failed
+ assert result_leak_fail_indirect.failed
+ assert result_leak_fail_definitive.failed
+ assert result_leak_fail_both.failed
+ assert result_leak_fail_timeout.failed
+
+ @pytest.mark.parametrize("term_cols", range(40, 300, 40))
+ def test_summarize(self, result_leak_pass, result_leak_fail_indirect, result_leak_fail_definitive,
+ result_leak_fail_both, result_leak_pass_no_count, result_leak_fail_timeout, term_cols):
+ with config_context(show_range=False, term_cols=term_cols):
+ assert f"{'echo bonjour':{term_cols - 7}} [PASS]" == result_leak_pass.summarize(-1)
+ assert f"{'echo bonjour':{term_cols - 7}} [PASS]" == result_leak_pass_no_count.summarize(-1)
+ assert f"{'echo bonjour':{term_cols - 7}} [FAIL]" == result_leak_fail_definitive.summarize(-1)
+ assert f"{'echo bonjour':{term_cols - 7}} [FAIL]" == result_leak_fail_indirect.summarize(-1)
+ assert f"{'echo bonjour':{term_cols - 7}} [FAIL]" == result_leak_fail_both.summarize(-1)
+ assert f"{'echo bonjour':{term_cols - 7}} [FAIL]" == result_leak_fail_timeout.summarize(-1)
+ with config_context(show_range=True, term_cols=term_cols):
+ assert f" 1: {'echo bonjour':{term_cols - 11}} [PASS]" == result_leak_pass.summarize(1)
+ assert f" 1: {'echo bonjour':{term_cols - 11}} [PASS]" == result_leak_pass_no_count.summarize(1)
+ assert f" 1: {'echo bonjour':{term_cols - 11}} [FAIL]" == result_leak_fail_definitive.summarize(1)
+ assert f" 1: {'echo bonjour':{term_cols - 11}} [FAIL]" == result_leak_fail_indirect.summarize(1)
+ assert f" 1: {'echo bonjour':{term_cols - 11}} [FAIL]" == result_leak_fail_both.summarize(1)
+ assert f" 1: {'echo bonjour':{term_cols - 11}} [FAIL]" == result_leak_fail_timeout.summarize(1)
+ assert f"99: {'echo bonjour':{term_cols - 11}} [PASS]" == result_leak_pass.summarize(99)
+ assert f"99: {'echo bonjour':{term_cols - 11}} [PASS]" == result_leak_pass_no_count.summarize(99)
+ assert f"99: {'echo bonjour':{term_cols - 11}} [FAIL]" == result_leak_fail_definitive.summarize(99)
+ assert f"99: {'echo bonjour':{term_cols - 11}} [FAIL]" == result_leak_fail_indirect.summarize(99)
+ assert f"99: {'echo bonjour':{term_cols - 11}} [FAIL]" == result_leak_fail_both.summarize(99)
+ assert f"99: {'echo bonjour':{term_cols - 11}} [FAIL]" == result_leak_fail_timeout.summarize(99)
+ assert f"100: {'echo bonjour':{term_cols - 12}} [PASS]" == result_leak_pass.summarize(100)
+ assert f"100: {'echo bonjour':{term_cols - 12}} [PASS]" == result_leak_pass_no_count.summarize(100)
+ assert f"100: {'echo bonjour':{term_cols - 12}} [FAIL]" == result_leak_fail_definitive.summarize(100)
+ assert f"100: {'echo bonjour':{term_cols - 12}} [FAIL]" == result_leak_fail_indirect.summarize(100)
+ assert f"100: {'echo bonjour':{term_cols - 12}} [FAIL]" == result_leak_fail_both.summarize(100)
+ assert f"100: {'echo bonjour':{term_cols - 12}} [FAIL]" == result_leak_fail_timeout.summarize(100)
+
+ def test_repr(self, result_leak_fail_indirect, result_leak_fail_definitive, result_leak_fail_both, result_leak_fail_timeout):
+ assert r"""
+|> WITH echo bonjour
+==32896== Memcheck, a memory error detector
+==32896== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
+==32896== Using Valgrind-3.16.1 and LibVEX; rerun with -h for copyright info
+==32896== Command: ./minishell -c echo\ bonjour
+==32896==
+bonjour
+==32896==
+==32896== HEAP SUMMARY:
+==32896== in use at exit: 3,787 bytes in 75 blocks
+==32896== total heap usage: 88 allocs, 13 frees, 102,949 bytes allocated
+==32896==
+==32896== 3,787 (24 direct, 3,763 indirect) bytes in 1 blocks are definitely lost in loss record 5 of 5
+==32896== at 0x483E77F: malloc (vg_replace_malloc.c:307)
+==32896== by 0x10D317: ft_vecnew (ft_vecnew.c:26)
+==32896== by 0x109382: env_from_array (env.c:33)
+==32896== by 0x1091C1: main (main.c:111)
+==32896==
+==32896== LEAK SUMMARY:
+==32896== definitely lost: 24 bytes in 1 blocks
+==32896== indirectly lost: 0 bytes in 0 blocks
+==32896== possibly lost: 0 bytes in 0 blocks
+==32896== still reachable: 0 bytes in 0 blocks
+==32896== suppressed: 0 bytes in 0 blocks
+==32896==
+==32896== For lists of detected and suppressed errors, rerun with: -s
+==32896== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
+""".lstrip("\n") == result_leak_fail_definitive.__repr__()
+ assert r"""
+|> WITH echo bonjour
+==32896== Memcheck, a memory error detector
+==32896== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
+==32896== Using Valgrind-3.16.1 and LibVEX; rerun with -h for copyright info
+==32896== Command: ./minishell -c echo\ bonjour
+==32896==
+bonjour
+==32896==
+==32896== HEAP SUMMARY:
+==32896== in use at exit: 3,787 bytes in 75 blocks
+==32896== total heap usage: 88 allocs, 13 frees, 102,949 bytes allocated
+==32896==
+==32896== 3,787 (24 direct, 3,763 indirect) bytes in 1 blocks are definitely lost in loss record 5 of 5
+==32896== at 0x483E77F: malloc (vg_replace_malloc.c:307)
+==32896== by 0x10D317: ft_vecnew (ft_vecnew.c:26)
+==32896== by 0x109382: env_from_array (env.c:33)
+==32896== by 0x1091C1: main (main.c:111)
+==32896==
+==32896== LEAK SUMMARY:
+==32896== definitely lost: 0 bytes in 0 blocks
+==32896== indirectly lost: 3,763 bytes in 74 blocks
+==32896== possibly lost: 0 bytes in 0 blocks
+==32896== still reachable: 0 bytes in 0 blocks
+==32896== suppressed: 0 bytes in 0 blocks
+==32896==
+==32896== For lists of detected and suppressed errors, rerun with: -s
+==32896== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
+""".lstrip("\n") == result_leak_fail_indirect.__repr__()
+ assert r"""
+|> WITH echo bonjour
+==32896== Memcheck, a memory error detector
+==32896== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
+==32896== Using Valgrind-3.16.1 and LibVEX; rerun with -h for copyright info
+==32896== Command: ./minishell -c echo\ bonjour
+==32896==
+bonjour
+==32896==
+==32896== HEAP SUMMARY:
+==32896== in use at exit: 3,787 bytes in 75 blocks
+==32896== total heap usage: 88 allocs, 13 frees, 102,949 bytes allocated
+==32896==
+==32896== 3,787 (24 direct, 3,763 indirect) bytes in 1 blocks are definitely lost in loss record 5 of 5
+==32896== at 0x483E77F: malloc (vg_replace_malloc.c:307)
+==32896== by 0x10D317: ft_vecnew (ft_vecnew.c:26)
+==32896== by 0x109382: env_from_array (env.c:33)
+==32896== by 0x1091C1: main (main.c:111)
+==32896==
+==32896== LEAK SUMMARY:
+==32896== definitely lost: 24 bytes in 1 blocks
+==32896== indirectly lost: 3,763 bytes in 74 blocks
+==32896== possibly lost: 0 bytes in 0 blocks
+==32896== still reachable: 0 bytes in 0 blocks
+==32896== suppressed: 0 bytes in 0 blocks
+==32896==
+==32896== For lists of detected and suppressed errors, rerun with: -s
+==32896== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
+""".lstrip("\n") == result_leak_fail_both.__repr__()
+ assert r"""
+|> WITH echo bonjour
+TIMEOUT
+""".lstrip("\n") == result_leak_fail_timeout.__repr__()
+ @pytest.mark.parametrize(
+ "output",
+ [
+ "",
+ "foo",
+ "asdlfjas;dkljfaslkdjflas",
+ "==32896== definitely lost: bytes in 1 blocks"
+ "==32896== definitely lost: a bytes in 1 blocks"
+ "==32896== definitely lost: _ bytes in 1 blocks"
+ "==32896== indirectly lost: bytes in 1 blocks"
+ "==32896== indirectly lost: a bytes in 1 blocks"
+ "==32896== indirectly lost: _ bytes in 1 blocks"
+ "==32896== bonjour lost: 1 bytes in 1 blocks"
+ ]
+ )
+ def test_parsing_error(self, output):
+ with pytest.raises(LeakResultException) as e:
+ LeakResult("echo bonjour", CapturedCommand(output, 0, [])).passed
+ assert "echo bonjour" == e.value._cmd
+ assert output == e.value._captured.output
+ assert f"valgrind output parsing failed for `echo bonjour`:\n{output}" == e.value.__str__()
diff --git a/tests/test_hooks.py b/tests/test_hooks.py
index a811e55..2593b6e 100644
--- a/tests/test_hooks.py
+++ b/tests/test_hooks.py
@@ -6,12 +6,10 @@
# By: cacharle <me@cacharle.xyz> +#+ +:+ +#+ #
# +#+#+#+#+#+ +#+ #
# Created: 2021/02/27 20:03:52 by cacharle #+# #+# #
-# Updated: 2021/02/28 12:05:58 by cacharle ### ########.fr #
+# Updated: 2021/03/02 17:45:15 by cacharle ### ########.fr #
# #
# ############################################################################ #
-import contextlib
-
from minishell_test.config import Config
from minishell_test.hooks import (
@@ -28,17 +26,10 @@ from minishell_test.hooks import (
DISCARDED_TEXT,
)
-Config.init([])
+from tests.helpers import config_context
-@contextlib.contextmanager
-def config_context(attr, value):
- prev = getattr(Config, attr)
- setattr(Config, attr, value)
- try:
- yield
- finally:
- setattr(Config, attr, prev)
+Config.init([])
def test_sort_lines():
@@ -116,7 +107,7 @@ declare -x YSU_VERSION="1.7.3"
declare -x ZDOTDIR="/home/cacharle/.config/zsh"\
""")
- with config_context("shell_reference_args", ["--posix"]):
+ with config_context(shell_reference_args=["--posix"]):
assert "" == export_singleton("export IGOTNUMBERS42")
assert "" == export_singleton("export IGOTUNDERSCORE__")
assert "" == export_singleton("export I")
@@ -165,31 +156,31 @@ def test_should_not_be():
def test_platform_status():
- with config_context('platform', 'darwin'):
+ with config_context(platform='darwin'):
assert 0 == platform_status(0, 1)(0)
assert 1 == platform_status(42, 42)(1)
- with config_context('platform', 'linux'):
+ with config_context(platform='linux'):
assert 0 == platform_status(0, 1)(1)
assert 42 == platform_status(0, 1)(42)
- with config_context('platform', 'foo'):
+ with config_context(platform='foo'):
assert 0 == platform_status(42, 42)(0)
def test_linux_replace():
- with config_context('platform', 'darwin'):
+ with config_context(platform='darwin'):
assert "Is a directory" == linux_replace("Is a directory", "is a directory")("Is a directory")
assert "SHLVL=0" == linux_replace("SHLVL=0", "SHLVL=1")("SHLVL=0")
assert "\\" == linux_replace("\\", "")("\\")
- with config_context('platform', 'linux'):
+ with config_context(platform='linux'):
assert "is a directory" == linux_replace("Is a directory", "is a directory")("Is a directory")
assert "SHLVL=1" == linux_replace("SHLVL=0", "SHLVL=1")("SHLVL=0")
assert "" == linux_replace("\\", "")("\\")
def test_linux_discard():
- with config_context('platform', 'darwin'):
+ with config_context(platform='darwin'):
assert "" == linux_discard("")
assert "foo" == linux_discard("foo")
- with config_context('platform', 'linux'):
+ with config_context(platform='linux'):
assert DISCARDED_TEXT == linux_discard("")
assert DISCARDED_TEXT == linux_discard("foo")