diff options
| -rw-r--r-- | README.md | 12 | ||||
| -rw-r--r-- | src/config.py | 5 | ||||
| -rw-r--r-- | src/helper.py | 17 | ||||
| -rwxr-xr-x | src/main.py | 21 | ||||
| -rw-r--r-- | src/test/error.py | 4 | ||||
| -rw-r--r-- | src/test/philo.py | 87 | ||||
| -rw-r--r-- | src/test/test.py | 34 |
7 files changed, 130 insertions, 50 deletions
@@ -13,5 +13,15 @@ Clone this repository next to your project directory. (or change the default pat ``` ``` -./run --help +$ ./run --help +usage: run [-h] -p {1,2,3} [-b] [-g] + +Philosophers test + +optional arguments: + -h, --help show this help message and exit + -p {1,2,3}, --philo {1,2,3} + Id of the philosophers to test + -b, --build Build and exit + -g, --pager Open result.log in a pager after the test ``` diff --git a/src/config.py b/src/config.py index b8811f3..f50b3da 100644 --- a/src/config.py +++ b/src/config.py @@ -6,7 +6,7 @@ # By: charles <me@cacharle.xyz> +#+ +:+ +#+ # # +#+#+#+#+#+ +#+ # # Created: 2020/09/27 11:05:38 by charles #+# #+# # -# Updated: 2020/09/29 10:55:23 by cacharle ### ########.fr # +# Updated: 2020/09/29 14:33:35 by cacharle ### ########.fr # # # # ############################################################################ # @@ -32,6 +32,9 @@ TIMEOUT_ERROR = 0.2 # Timeout for infinite test INFINITE_WAIT_TIME = 0.2 +# Maximum number of lines for infinite test +INFINITE_MAX_LINE = 1000 + # Destination of the full summary of failed tests RESULT_FILE = "result.log" diff --git a/src/helper.py b/src/helper.py new file mode 100644 index 0000000..56014d6 --- /dev/null +++ b/src/helper.py @@ -0,0 +1,17 @@ +# ############################################################################ # +# # +# ::: :::::::: # +# helper.py :+: :+: :+: # +# +:+ +:+ +:+ # +# By: cacharle <me@cacharle.xyz> +#+ +:+ +#+ # +# +#+#+#+#+#+ +#+ # +# Created: 2020/09/29 11:19:32 by cacharle #+# #+# # +# Updated: 2020/09/29 11:19:50 by cacharle ### ########.fr # +# # +# ############################################################################ # + +import time + + +def current_ms(): + return int(time.time() * 1000) diff --git a/src/main.py b/src/main.py index 17d8b52..6aee6c9 100755 --- a/src/main.py +++ b/src/main.py @@ -17,7 +17,7 @@ # [x] timestamp not in order # [ ] crash # [ ] should be infinity -# [ ] argument error +# [x] argument error # [x] print lines after died # [x] bad output format # [x] should be dead @@ -89,11 +89,24 @@ def main(): Test.new_error(["10", "10", "10", str(-config.UINT_MAX)]) Test.new_error(["10", "10", "10", "10", str(-config.UINT_MAX)]) - Test.new_error(["0", "100", "100", "100"]) - Test.new_error(["1", "100", "100", "100"]) + Test(0, 100, 10, 10) + Test(1, 100, 10, 10) + + Test(2, 100, 50, 50) + Test(3, 100, 50, 50) + Test(4, 100, 50, 50) + Test(5, 100, 50, 50) + Test(6, 100, 50, 50) + Test(7, 100, 50, 50) + + Test(100, 100, 50, 50) Test(10, 100, 100, 10) - Test(10, 200, 10, 10, infinite=True) + + # Test(2, 50, 10, 10, infinite=True) + # Test(10, 50, 10, 10, infinite=True) + # Test(10, 100, 10, 10, infinite=True) + # Test(10, 200, 10, 10, infinite=True) try: Test.run_all(config.PHILO_EXEC_PATHS[0]) diff --git a/src/test/error.py b/src/test/error.py index 4bfa0e3..51c3f7b 100644 --- a/src/test/error.py +++ b/src/test/error.py @@ -6,7 +6,7 @@ # By: cacharle <me@cacharle.xyz> +#+ +:+ +#+ # # +#+#+#+#+#+ +#+ # # Created: 2020/09/29 09:09:31 by cacharle #+# #+# # -# Updated: 2020/09/29 10:08:24 by cacharle ### ########.fr # +# Updated: 2020/09/29 11:14:08 by cacharle ### ########.fr # # # # ############################################################################ # @@ -42,7 +42,7 @@ class Format(Philo): @property def summary(self): - return "format: {}".format(self._msg) + return "format: {} {}".format(self._line, self._msg) class Log(Philo): diff --git a/src/test/philo.py b/src/test/philo.py index 9039ac0..022a278 100644 --- a/src/test/philo.py +++ b/src/test/philo.py @@ -6,19 +6,16 @@ # By: charles <me@cacharle.xyz> +#+ +:+ +#+ # # +#+#+#+#+#+ +#+ # # Created: 2020/09/27 17:49:41 by charles #+# #+# # -# Updated: 2020/09/29 10:54:03 by cacharle ### ########.fr # +# Updated: 2020/09/29 15:16:16 by cacharle ### ########.fr # # # # ############################################################################ # import re -import time import enum import itertools import test.error as error - -def current_ms(): - return int(time.time() * 1000) +from helper import current_ms class Event(enum.Enum): EATING = 1 @@ -39,7 +36,7 @@ class Event(enum.Enum): class Log: - def __init__(self, line, philo_num): + def __init__(self, line, philo_num, start_time): match = re.match( r"^(?P<timestamp>\d+) " r"(?P<id>\d+) " @@ -49,9 +46,10 @@ class Log: if match is None: raise error.Format(line, "wrong format") - curr = current_ms() - self.timestamp = Log._parse_ranged_int(match.group("timestamp"), curr - 100, curr + 100) - self.id = Log._parse_ranged_int(match.group("id"), 1, philo_num) + self._line = line + self.id = self._parse_ranged_int(match.group("id"), 1, philo_num) + self.timestamp = self._parse_ranged_int( + match.group("timestamp"), start_time, current_ms()) self.event = { "is thinking": Event.THINKING, @@ -60,14 +58,14 @@ class Log: "died": Event.DIED, }[match.group('event')] - @staticmethod - def _parse_ranged_int(s, lo, hi): + def _parse_ranged_int(self, s, lo, hi): try: value = int(s) if not (lo <= value <= hi): - raise error.Format(s, "should be between {} - {}".format(lo, hi)) + raise error.Format(self._line, + "{} should be between {} - {}".format(s, lo, hi)) except ValueError: - raise error.Format(s, "sould be an integer".format(s)) + raise error.Format(self._line, "{} sould be an integer".format(s)) return value def __repr__(self): @@ -75,12 +73,20 @@ class Log: class Philo: - def __init__(self, id_: int, timeout_eat: int, meal_num: int = 1): + def __init__( + self, + id_: int, + timeout_die: int, + timeout_eat: int, + timeout_sleep: int, + meal_num: int + ): self._logs = [] self.id = id_ - self.meal_num = meal_num + self._timeout_die = timeout_die self._timeout_eat = timeout_eat - # self._start_time = current_ms() + self._timeout_sleep = timeout_sleep + self._meal_num = meal_num def add_log(self, log): self._logs.append(log) @@ -91,23 +97,28 @@ class Philo: grouped = [(e, list(g)) for e, g in itertools.groupby(self._logs, (lambda x: x.event))] for e, g in grouped: if e is Event.EATING: - if len(g) != self.meal_num: - raise error.Log(self._logs, "should eat {} times".format(self.meal_num)) + if len(g) != self._meal_num: + raise error.Log(self._logs, "should eat {} times".format(self._meal_num)) else: if len(g) != 1: raise error.Log(self._logs, "should {} 1 time".format(Event.to_verb(e))) - events = [e for e, _ in grouped] - for e1, e2 in zip(events, events[1:]): - if e2 is Event.DIED: + # events = [e for e, _ in grouped] + for l1, l2 in zip(self._logs, self._logs[1:]): + if l2.event is Event.DIED: break - second = { - Event.THINKING: Event.EATING, - Event.EATING: Event.SLEEPING, - Event.SLEEPING: Event.THINKING - }[e1] - if e2 is not second: - raise error.Log(self._logs, "invalid switch {} -> {}".format(e1, e2)) + if l1.event is Event.EATING and l2.event is Event.EATING: + if l2.timestamp - l1.timestamp > self._timeout_eat: + raise ValueError + second, timeout = { + Event.THINKING: (Event.EATING, None), + Event.EATING: (Event.SLEEPING, self._timeout_eat), + Event.SLEEPING: (Event.THINKING, self._timeout_sleep) + }[l1.event] + if l2.event is not second: + raise error.Log(self._logs, "invalid switch {} -> {}".format(l1.event, l2.event)) + if timeout is not None and l2.timestamp - l1.timestamp > timeout: + raise ValueError last_eat = None for log in reversed(self._logs): @@ -116,8 +127,10 @@ class Philo: break last = self._logs[-1] if last_eat is not None and last_eat is not last: - if last.timestamp - last_eat.timestamp > self._timeout_eat + 20: - raise error.Log(self._logs, "{} should be dead {}".format(self.id, last.timestamp)) + if last.timestamp - last_eat.timestamp > self._timeout_die + 10: + raise error.Log(self._logs, "{} should be dead {} - {} > {}".format( + self.id, last.timestamp, last_eat.timestamp, self._timeout_die + 10)) + @property def last_event(self): @@ -127,8 +140,16 @@ class Philo: class Table: - def __init__(self, timeout_eat, philo_num): - self._philos = [Philo(id_, timeout_eat) for id_ in range(1, philo_num + 1)] + def __init__( + self, + philo_num: int, + timeout_die: int, + timeout_eat: int, + timeout_sleep: int, + meal_num: int + ): + self._philos = [Philo(id_, timeout_die, timeout_eat, timeout_sleep, meal_num) + for id_ in range(1, philo_num + 1)] self._logs = [] self._philo_num = philo_num self.dead = False @@ -150,6 +171,6 @@ class Table: raise error.Log(self._logs, "using nonexistant forks") for l1, l2 in zip(self._logs, self._logs[1:]): if l1.timestamp > l2.timestamp: - raise error.Log("timestamp not in ordered") + raise error.Log(self._logs, "timestamp not in ordered") for p in self._philos: p.check() diff --git a/src/test/test.py b/src/test/test.py index 506bfff..8a63bd7 100644 --- a/src/test/test.py +++ b/src/test/test.py @@ -6,16 +6,18 @@ # By: charles <me@cacharle.xyz> +#+ +:+ +#+ # # +#+#+#+#+#+ +#+ # # Created: 2020/09/27 11:36:32 by charles #+# #+# # -# Updated: 2020/09/29 10:53:14 by cacharle ### ########.fr # +# Updated: 2020/09/29 15:15:46 by cacharle ### ########.fr # # # # ############################################################################ # import os +import time import subprocess import config import test.philo as philo import test.error as error +from helper import current_ms class Test: @@ -75,21 +77,35 @@ class Test: if self._error_cmd is not None: self._check_error(process) elif self._infinite: - self._check_output(process.stdout, died=False) - time.sleep(config.INFINITE_WAIT_TIME) - process.kill() + try: + self._check_output(process.stdout, died=False) + process.wait(timeout=config.INFINITE_WAIT_TIME) + except subprocess.TimeoutExpired: + pass + else: + raise ShouldBeInfinite else: self._check_output(process.stdout) process.wait(timeout=config.TIMEOUT) def _check_output(self, stream, died: bool = True): - table = philo.Table(self._philo_num, self._timeout_eat) - for line in stream: + start_time = current_ms() + table = philo.Table( + self._philo_num, self._timeout_die, self._timeout_eat, self._timeout_sleep, + 1 if self._meal_num is None else self._meal_num) + for i, line in enumerate(stream): line = line.decode()[:-1] - table.add_log(philo.Log(line, self._philo_num)) + table.add_log(philo.Log(line, self._philo_num, start_time)) table.check() - if died and not table.dead: - raise philo.error.Log("one philosopher should have died") + if i > 1000: + break + if died: + if not table.dead and self._philo_num != 0: + raise philo.error.Log(table._logs, "one philosopher should have died") + else: + if table.dead: + raise philo.error.Log(table._logs, "infinite shouldn't die") + def _check_error(self, process): try: |
