diff options
| -rw-r--r-- | minishell_test/suite/suite.py | 20 | ||||
| -rw-r--r-- | minishell_test/test/result.py | 109 | ||||
| -rw-r--r-- | tests/helpers.py | 27 | ||||
| -rw-r--r-- | tests/test/test_result.py | 467 | ||||
| -rw-r--r-- | tests/test_hooks.py | 31 |
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") |
