tracks/perl5/exercises/clock/clock.t in trackler-2.2.1.110 vs tracks/perl5/exercises/clock/clock.t in trackler-2.2.1.111

- old
+ new

@@ -1,107 +1,643 @@ #!/usr/bin/env perl use strict; use warnings; - -use Test::More; use FindBin; -my $dir; -BEGIN { $dir = $FindBin::Bin . '/' }; -use lib $dir; +use lib $FindBin::Bin; +use JSON::PP; -my @cases = ( - { - clock => [8], - expected => "08:00" - }, - { - clock => [9], - expected => "09:00" - }, - { - clock => [11, 9], - expected => "11:09" - }, - { - clock => [10], - add => 3, - expected => "10:03" - }, - { - clock => [10], - add => 61, - expected => "11:01" - }, - { - clock => [23, 30], - add => 60, - expected => "00:30" - }, - { - clock => [10], - subtract => 90, - expected => "08:30" - }, - { - clock => [0, 30], - subtract => 60, - expected => "23:30" - }, - { - clock => [15, 37], - equal => [15, 36], - expected => 0 - }, - { - clock => [15, 37], - equal => [15, 37], - expected => 1 - }, - { - clock => [15, 37], - equal => [14, 37], - expected => 0 - } -); +my $exercise = 'Clock'; +my $test_version = 1; +use Test::More tests => 57; -my $module = 'Clock'; +use_ok $exercise or BAIL_OUT; -plan tests => 6 + @cases; +my $exercise_version = $exercise->VERSION // 0; +if ($exercise_version != $test_version) { + warn "\nExercise version mismatch. Further tests may fail!" + . "\n$exercise is v$exercise_version. " + . "Test is v$test_version.\n"; + BAIL_OUT if $ENV{EXERCISM}; +} -ok -e "${dir}${module}.pm", "Missing $module.pm" - or BAIL_OUT "You need to create file: $module.pm"; +my %subs; +foreach ( qw(new time add_minutes subtract_minutes) ) { + can_ok $exercise, $_; + $subs{$_} = $exercise->can($_); +} -eval "use $module"; +my $C_DATA = do { local $/; decode_json(<DATA>); }; +foreach (@{$C_DATA->{cases}}) { + foreach (@{$_->{cases}}) { + if ($_->{property} eq 'create') { + is Clock->new($_->{input})->time, $_->{expected}, $_->{description}; + } -ok !$@, "Cannot load $module" - or BAIL_OUT "Cannot load $module. Does it compile? Does it end with 1;?"; + elsif ($_->{property} eq 'add' || $_->{property} eq 'subtract') { + my $clock = Clock->new({ + hour => $_->{input}{hour}, + minute => $_->{input}{minute}, + }); + if ($_->{property} eq 'subtract') { $clock->subtract_minutes($_->{input}{value}) } + elsif ($_->{property} eq 'add' ) { $clock->add_minutes( $_->{input}{value}) } + is $clock->time, $_->{expected}, $_->{description}; + } -ok exists $INC{'overload.pm'}, "$module loads overload module; use overload" - or BAIL_OUT "You need to overload these operators + - eq"; + elsif ($_->{property} eq 'equal') { + ok $_->{expected} == + (Clock->new($_->{input}{clock1})->time eq Clock->new($_->{input}{clock2})->time), $_->{description}; + } + } +} -use overload; +is Clock->new({hour => 0, minute => 0})->add_minutes(65)->time, '01:05', 'add_minutes method can be chained'; +is Clock->new({hour => 0, minute => 0})->subtract_minutes(65)->time, '22:55', 'subtract_minutes method can be chained'; -is ref overload::Method($module, '+'), 'CODE', "$module overloads '+' operator" - or BAIL_OUT "Missing package $module; or missing 'use overload '+' => sub { ... }"; -is ref overload::Method($module, '-'), 'CODE', "$module overloads '-' operator" - or BAIL_OUT "Missing package $module; or missing 'use overload '-' => sub { ... }"; -is ref overload::Method($module, 'eq'), 'CODE', "$module overloads 'eq' operator" - or BAIL_OUT "Missing package $module; or missing 'use overload 'eq' => sub { ... }"; - - -foreach my $c (@cases) { - my $clock = $module->new($c->{clock}); - - unless ($c->{add} or $c->{subtract} or $c->{equal}) { - is $clock, $c->{expected}, "clock(@{$c->{clock}}) is $c->{expected}"; +__DATA__ +{ + "exercise": "clock", + "version": "2.2.1", + "comments": [ + "Most languages require constructing a clock with initial values,", + "adding or subtracting some number of minutes, and testing equality", + "in some language-native way. Negative and out of range values are", + "generally expected to wrap around rather than represent errors." + ], + "cases": [ + { + "description": "Create a new clock with an initial time", + "cases": [ + { + "description": "on the hour", + "property": "create", + "input": { + "hour": 8, + "minute": 0 + }, + "expected": "08:00" + }, + { + "description": "past the hour", + "property": "create", + "input": { + "hour": 11, + "minute": 9 + }, + "expected": "11:09" + }, + { + "description": "midnight is zero hours", + "property": "create", + "input": { + "hour": 24, + "minute": 0 + }, + "expected": "00:00" + }, + { + "description": "hour rolls over", + "property": "create", + "input": { + "hour": 25, + "minute": 0 + }, + "expected": "01:00" + }, + { + "description": "hour rolls over continuously", + "property": "create", + "input": { + "hour": 100, + "minute": 0 + }, + "expected": "04:00" + }, + { + "description": "sixty minutes is next hour", + "property": "create", + "input": { + "hour": 1, + "minute": 60 + }, + "expected": "02:00" + }, + { + "description": "minutes roll over", + "property": "create", + "input": { + "hour": 0, + "minute": 160 + }, + "expected": "02:40" + }, + { + "description": "minutes roll over continuously", + "property": "create", + "input": { + "hour": 0, + "minute": 1723 + }, + "expected": "04:43" + }, + { + "description": "hour and minutes roll over", + "property": "create", + "input": { + "hour": 25, + "minute": 160 + }, + "expected": "03:40" + }, + { + "description": "hour and minutes roll over continuously", + "property": "create", + "input": { + "hour": 201, + "minute": 3001 + }, + "expected": "11:01" + }, + { + "description": "hour and minutes roll over to exactly midnight", + "property": "create", + "input": { + "hour": 72, + "minute": 8640 + }, + "expected": "00:00" + }, + { + "description": "negative hour", + "property": "create", + "input": { + "hour": -1, + "minute": 15 + }, + "expected": "23:15" + }, + { + "description": "negative hour rolls over", + "property": "create", + "input": { + "hour": -25, + "minute": 0 + }, + "expected": "23:00" + }, + { + "description": "negative hour rolls over continuously", + "property": "create", + "input": { + "hour": -91, + "minute": 0 + }, + "expected": "05:00" + }, + { + "description": "negative minutes", + "property": "create", + "input": { + "hour": 1, + "minute": -40 + }, + "expected": "00:20" + }, + { + "description": "negative minutes roll over", + "property": "create", + "input": { + "hour": 1, + "minute": -160 + }, + "expected": "22:20" + }, + { + "description": "negative minutes roll over continuously", + "property": "create", + "input": { + "hour": 1, + "minute": -4820 + }, + "expected": "16:40" + }, + { + "description": "negative hour and minutes both roll over", + "property": "create", + "input": { + "hour": -25, + "minute": -160 + }, + "expected": "20:20" + }, + { + "description": "negative hour and minutes both roll over continuously", + "property": "create", + "input": { + "hour": -121, + "minute": -5810 + }, + "expected": "22:10" + } + ] + }, + { + "description": "Add minutes", + "cases": [ + { + "description": "add minutes", + "property": "add", + "input": { + "hour": 10, + "minute": 0, + "value": 3 + }, + "expected": "10:03" + }, + { + "description": "add no minutes", + "property": "add", + "input": { + "hour": 6, + "minute": 41, + "value": 0 + }, + "expected": "06:41" + }, + { + "description": "add to next hour", + "property": "add", + "input": { + "hour": 0, + "minute": 45, + "value": 40 + }, + "expected": "01:25" + }, + { + "description": "add more than one hour", + "property": "add", + "input": { + "hour": 10, + "minute": 0, + "value": 61 + }, + "expected": "11:01" + }, + { + "description": "add more than two hours with carry", + "property": "add", + "input": { + "hour": 0, + "minute": 45, + "value": 160 + }, + "expected": "03:25" + }, + { + "description": "add across midnight", + "property": "add", + "input": { + "hour": 23, + "minute": 59, + "value": 2 + }, + "expected": "00:01" + }, + { + "description": "add more than one day (1500 min = 25 hrs)", + "property": "add", + "input": { + "hour": 5, + "minute": 32, + "value": 1500 + }, + "expected": "06:32" + }, + { + "description": "add more than two days", + "property": "add", + "input": { + "hour": 1, + "minute": 1, + "value": 3500 + }, + "expected": "11:21" + } + ] + }, + { + "description": "Subtract minutes", + "cases": [ + { + "description": "subtract minutes", + "property": "subtract", + "input": { + "hour": 10, + "minute": 3, + "value": 3 + }, + "expected": "10:00" + }, + { + "description": "subtract to previous hour", + "property": "subtract", + "input": { + "hour": 10, + "minute": 3, + "value": 30 + }, + "expected": "09:33" + }, + { + "description": "subtract more than an hour", + "property": "subtract", + "input": { + "hour": 10, + "minute": 3, + "value": 70 + }, + "expected": "08:53" + }, + { + "description": "subtract across midnight", + "property": "subtract", + "input": { + "hour": 0, + "minute": 3, + "value": 4 + }, + "expected": "23:59" + }, + { + "description": "subtract more than two hours", + "property": "subtract", + "input": { + "hour": 0, + "minute": 0, + "value": 160 + }, + "expected": "21:20" + }, + { + "description": "subtract more than two hours with borrow", + "property": "subtract", + "input": { + "hour": 6, + "minute": 15, + "value": 160 + }, + "expected": "03:35" + }, + { + "description": "subtract more than one day (1500 min = 25 hrs)", + "property": "subtract", + "input": { + "hour": 5, + "minute": 32, + "value": 1500 + }, + "expected": "04:32" + }, + { + "description": "subtract more than two days", + "property": "subtract", + "input": { + "hour": 2, + "minute": 20, + "value": 3000 + }, + "expected": "00:20" + } + ] + }, + { + "description": "Compare two clocks for equality", + "cases": [ + { + "description": "clocks with same time", + "property": "equal", + "input": { + "clock1": { + "hour": 15, + "minute": 37 + }, + "clock2": { + "hour": 15, + "minute": 37 + } + }, + "expected": true + }, + { + "description": "clocks a minute apart", + "property": "equal", + "input": { + "clock1": { + "hour": 15, + "minute": 36 + }, + "clock2": { + "hour": 15, + "minute": 37 + } + }, + "expected": false + }, + { + "description": "clocks an hour apart", + "property": "equal", + "input": { + "clock1": { + "hour": 14, + "minute": 37 + }, + "clock2": { + "hour": 15, + "minute": 37 + } + }, + "expected": false + }, + { + "description": "clocks with hour overflow", + "property": "equal", + "input": { + "clock1": { + "hour": 10, + "minute": 37 + }, + "clock2": { + "hour": 34, + "minute": 37 + } + }, + "expected": true + }, + { + "description": "clocks with hour overflow by several days", + "property": "equal", + "input": { + "clock1": { + "hour": 3, + "minute": 11 + }, + "clock2": { + "hour": 99, + "minute": 11 + } + }, + "expected": true + }, + { + "description": "clocks with negative hour", + "property": "equal", + "input": { + "clock1": { + "hour": 22, + "minute": 40 + }, + "clock2": { + "hour": -2, + "minute": 40 + } + }, + "expected": true + }, + { + "description": "clocks with negative hour that wraps", + "property": "equal", + "input": { + "clock1": { + "hour": 17, + "minute": 3 + }, + "clock2": { + "hour": -31, + "minute": 3 + } + }, + "expected": true + }, + { + "description": "clocks with negative hour that wraps multiple times", + "property": "equal", + "input": { + "clock1": { + "hour": 13, + "minute": 49 + }, + "clock2": { + "hour": -83, + "minute": 49 + } + }, + "expected": true + }, + { + "description": "clocks with minute overflow", + "property": "equal", + "input": { + "clock1": { + "hour": 0, + "minute": 1 + }, + "clock2": { + "hour": 0, + "minute": 1441 + } + }, + "expected": true + }, + { + "description": "clocks with minute overflow by several days", + "property": "equal", + "input": { + "clock1": { + "hour": 2, + "minute": 2 + }, + "clock2": { + "hour": 2, + "minute": 4322 + } + }, + "expected": true + }, + { + "description": "clocks with negative minute", + "property": "equal", + "input": { + "clock1": { + "hour": 2, + "minute": 40 + }, + "clock2": { + "hour": 3, + "minute": -20 + } + }, + "expected": true + }, + { + "description": "clocks with negative minute that wraps", + "property": "equal", + "input": { + "clock1": { + "hour": 4, + "minute": 10 + }, + "clock2": { + "hour": 5, + "minute": -1490 + } + }, + "expected": true + }, + { + "description": "clocks with negative minute that wraps multiple times", + "property": "equal", + "input": { + "clock1": { + "hour": 6, + "minute": 15 + }, + "clock2": { + "hour": 6, + "minute": -4305 + } + }, + "expected": true + }, + { + "description": "clocks with negative hours and minutes", + "property": "equal", + "input": { + "clock1": { + "hour": 7, + "minute": 32 + }, + "clock2": { + "hour": -12, + "minute": -268 + } + }, + "expected": true + }, + { + "description": "clocks with negative hours and minutes that wrap", + "property": "equal", + "input": { + "clock1": { + "hour": 18, + "minute": 7 + }, + "clock2": { + "hour": -54, + "minute": -11513 + } + }, + "expected": true + } + ] } - if ($c->{add}) { - is $clock + $c->{add}, $c->{expected}, "add $c->{add} minutes to clock(@{$c->{clock}})"; - } - if ($c->{subtract}) { - is $clock - $c->{subtract}, $c->{expected}, "subtract $c->{subtract} minutes to clock(@{$c->{clock}})"; - } - if ($c->{equal}) { - is int($clock eq $module->new($c->{equal})), $c->{expected}, - "clock(@{$c->{clock}}) eq clock(@{$c->{equal}}) ($c->{expected})"; - } + ] }