From 399d24094877adcb3e6f899446b9a508d940341f Mon Sep 17 00:00:00 2001 From: Dmitry Kokorin Date: Fri, 11 Jun 2021 16:54:11 +0300 Subject: [PATCH] Python: clock --- python/clock/.exercism/metadata.json | 1 + python/clock/README.md | 54 +++++++++ python/clock/clock.py | 24 ++++ python/clock/clock_test.py | 171 +++++++++++++++++++++++++++ 4 files changed, 250 insertions(+) create mode 100644 python/clock/.exercism/metadata.json create mode 100644 python/clock/README.md create mode 100644 python/clock/clock.py create mode 100644 python/clock/clock_test.py diff --git a/python/clock/.exercism/metadata.json b/python/clock/.exercism/metadata.json new file mode 100644 index 0000000..7ab3231 --- /dev/null +++ b/python/clock/.exercism/metadata.json @@ -0,0 +1 @@ +{"track":"python","exercise":"clock","id":"ba856df2676641fd8ad5587c6a71ad92","url":"https://exercism.io/my/solutions/ba856df2676641fd8ad5587c6a71ad92","handle":"DmitryKokorin","is_requester":true,"auto_approve":false} \ No newline at end of file diff --git a/python/clock/README.md b/python/clock/README.md new file mode 100644 index 0000000..bbf0ebb --- /dev/null +++ b/python/clock/README.md @@ -0,0 +1,54 @@ +# Clock + +Implement a clock that handles times without dates. + +You should be able to add and subtract minutes to it. + +Two clocks that represent the same time should be equal to each other. + + +## Exception messages + +Sometimes it is necessary to raise an exception. When you do this, you should include a meaningful error message to +indicate what the source of the error is. This makes your code more readable and helps significantly with debugging. Not +every exercise will require you to raise an exception, but for those that do, the tests will only pass if you include +a message. + +To raise a message with an exception, just write it as an argument to the exception type. For example, instead of +`raise Exception`, you should write: + +```python +raise Exception("Meaningful message indicating the source of the error") +``` + +## Running the tests + +To run the tests, run `pytest clock_test.py` + +Alternatively, you can tell Python to run the pytest module: +`python -m pytest clock_test.py` + +### Common `pytest` options + +- `-v` : enable verbose output +- `-x` : stop running tests on first failure +- `--ff` : run failures from previous test before running other test cases + +For other options, see `python -m pytest -h` + +## Submitting Exercises + +Note that, when trying to submit an exercise, make sure the solution is in the `$EXERCISM_WORKSPACE/python/clock` directory. + +You can find your Exercism workspace by running `exercism debug` and looking for the line that starts with `Workspace`. + +For more detailed information about running tests, code style and linting, +please see [Running the Tests](http://exercism.io/tracks/python/tests). + +## Source + +Pairing session with Erin Drummond [https://twitter.com/ebdrummond](https://twitter.com/ebdrummond) + +## Submitting Incomplete Solutions + +It's possible to submit an incomplete solution so you can see how others have completed the exercise. diff --git a/python/clock/clock.py b/python/clock/clock.py new file mode 100644 index 0000000..c1f200c --- /dev/null +++ b/python/clock/clock.py @@ -0,0 +1,24 @@ +HOURS_IN_DAY = 24 +MINUTES_IN_HOUR = 60 +MINUTES_IN_DAY = HOURS_IN_DAY*MINUTES_IN_HOUR + + +class Clock: + def __init__(self, hour=0, minute=0): + self.minutes = (MINUTES_IN_HOUR*hour + minute) % MINUTES_IN_DAY + + def __repr__(self): + hours = self.minutes // MINUTES_IN_HOUR + minutes = self.minutes % MINUTES_IN_HOUR + return f'{hours:02}:{minutes:02}' + + def __eq__(self, other): + return self.minutes == other.minutes + + def __add__(self, minutes): + clock = Clock() + clock.minutes = (self.minutes + minutes) % MINUTES_IN_DAY + return clock + + def __sub__(self, minutes): + return self.__add__(-minutes) diff --git a/python/clock/clock_test.py b/python/clock/clock_test.py new file mode 100644 index 0000000..0c3c3dd --- /dev/null +++ b/python/clock/clock_test.py @@ -0,0 +1,171 @@ +import unittest + +from clock import Clock + +# Tests adapted from `problem-specifications//canonical-data.json` + + +class ClockTest(unittest.TestCase): + # Create A New Clock With An Initial Time + def test_on_the_hour(self): + self.assertEqual(str(Clock(8, 0)), "08:00") + + def test_past_the_hour(self): + self.assertEqual(str(Clock(11, 9)), "11:09") + + def test_midnight_is_zero_hours(self): + self.assertEqual(str(Clock(24, 0)), "00:00") + + def test_hour_rolls_over(self): + self.assertEqual(str(Clock(25, 0)), "01:00") + + def test_hour_rolls_over_continuously(self): + self.assertEqual(str(Clock(100, 0)), "04:00") + + def test_sixty_minutes_is_next_hour(self): + self.assertEqual(str(Clock(1, 60)), "02:00") + + def test_minutes_roll_over(self): + self.assertEqual(str(Clock(0, 160)), "02:40") + + def test_minutes_roll_over_continuously(self): + self.assertEqual(str(Clock(0, 1723)), "04:43") + + def test_hour_and_minutes_roll_over(self): + self.assertEqual(str(Clock(25, 160)), "03:40") + + def test_hour_and_minutes_roll_over_continuously(self): + self.assertEqual(str(Clock(201, 3001)), "11:01") + + def test_hour_and_minutes_roll_over_to_exactly_midnight(self): + self.assertEqual(str(Clock(72, 8640)), "00:00") + + def test_negative_hour(self): + self.assertEqual(str(Clock(-1, 15)), "23:15") + + def test_negative_hour_rolls_over(self): + self.assertEqual(str(Clock(-25, 0)), "23:00") + + def test_negative_hour_rolls_over_continuously(self): + self.assertEqual(str(Clock(-91, 0)), "05:00") + + def test_negative_minutes(self): + self.assertEqual(str(Clock(1, -40)), "00:20") + + def test_negative_minutes_roll_over(self): + self.assertEqual(str(Clock(1, -160)), "22:20") + + def test_negative_minutes_roll_over_continuously(self): + self.assertEqual(str(Clock(1, -4820)), "16:40") + + def test_negative_sixty_minutes_is_previous_hour(self): + self.assertEqual(str(Clock(2, -60)), "01:00") + + def test_negative_hour_and_minutes_both_roll_over(self): + self.assertEqual(str(Clock(-25, -160)), "20:20") + + def test_negative_hour_and_minutes_both_roll_over_continuously(self): + self.assertEqual(str(Clock(-121, -5810)), "22:10") + + # Add Minutes + def test_add_minutes(self): + self.assertEqual(str(Clock(10, 0) + 3), "10:03") + + def test_add_no_minutes(self): + self.assertEqual(str(Clock(6, 41) + 0), "06:41") + + def test_add_to_next_hour(self): + self.assertEqual(str(Clock(0, 45) + 40), "01:25") + + def test_add_more_than_one_hour(self): + self.assertEqual(str(Clock(10, 0) + 61), "11:01") + + def test_add_more_than_two_hours_with_carry(self): + self.assertEqual(str(Clock(0, 45) + 160), "03:25") + + def test_add_across_midnight(self): + self.assertEqual(str(Clock(23, 59) + 2), "00:01") + + def test_add_more_than_one_day_1500_min_25_hrs(self): + self.assertEqual(str(Clock(5, 32) + 1500), "06:32") + + def test_add_more_than_two_days(self): + self.assertEqual(str(Clock(1, 1) + 3500), "11:21") + + # Subtract Minutes + def test_subtract_minutes(self): + self.assertEqual(str(Clock(10, 3) - 3), "10:00") + + def test_subtract_to_previous_hour(self): + self.assertEqual(str(Clock(10, 3) - 30), "09:33") + + def test_subtract_more_than_an_hour(self): + self.assertEqual(str(Clock(10, 3) - 70), "08:53") + + def test_subtract_across_midnight(self): + self.assertEqual(str(Clock(0, 3) - 4), "23:59") + + def test_subtract_more_than_two_hours(self): + self.assertEqual(str(Clock(0, 0) - 160), "21:20") + + def test_subtract_more_than_two_hours_with_borrow(self): + self.assertEqual(str(Clock(6, 15) - 160), "03:35") + + def test_subtract_more_than_one_day_1500_min_25_hrs(self): + self.assertEqual(str(Clock(5, 32) - 1500), "04:32") + + def test_subtract_more_than_two_days(self): + self.assertEqual(str(Clock(2, 20) - 3000), "00:20") + + # Compare Two Clocks For Equality + def test_clocks_with_same_time(self): + self.assertEqual(Clock(15, 37), Clock(15, 37)) + + def test_clocks_a_minute_apart(self): + self.assertNotEqual(Clock(15, 36), Clock(15, 37)) + + def test_clocks_an_hour_apart(self): + self.assertNotEqual(Clock(14, 37), Clock(15, 37)) + + def test_clocks_with_hour_overflow(self): + self.assertEqual(Clock(10, 37), Clock(34, 37)) + + def test_clocks_with_hour_overflow_by_several_days(self): + self.assertEqual(Clock(3, 11), Clock(99, 11)) + + def test_clocks_with_negative_hour(self): + self.assertEqual(Clock(22, 40), Clock(-2, 40)) + + def test_clocks_with_negative_hour_that_wraps(self): + self.assertEqual(Clock(17, 3), Clock(-31, 3)) + + def test_clocks_with_negative_hour_that_wraps_multiple_times(self): + self.assertEqual(Clock(13, 49), Clock(-83, 49)) + + def test_clocks_with_minute_overflow(self): + self.assertEqual(Clock(0, 1), Clock(0, 1441)) + + def test_clocks_with_minute_overflow_by_several_days(self): + self.assertEqual(Clock(2, 2), Clock(2, 4322)) + + def test_clocks_with_negative_minute(self): + self.assertEqual(Clock(2, 40), Clock(3, -20)) + + def test_clocks_with_negative_minute_that_wraps(self): + self.assertEqual(Clock(4, 10), Clock(5, -1490)) + + def test_clocks_with_negative_minute_that_wraps_multiple_times(self): + self.assertEqual(Clock(6, 15), Clock(6, -4305)) + + def test_clocks_with_negative_hours_and_minutes(self): + self.assertEqual(Clock(7, 32), Clock(-12, -268)) + + def test_clocks_with_negative_hours_and_minutes_that_wrap(self): + self.assertEqual(Clock(18, 7), Clock(-54, -11513)) + + def test_full_clock_and_zeroed_clock(self): + self.assertEqual(Clock(24, 0), Clock(0, 0)) + + +if __name__ == "__main__": + unittest.main()