From 75e1e234b8c9127e2e3602d701aac4e2db2d1896 Mon Sep 17 00:00:00 2001 From: meatball Date: Sun, 31 Aug 2025 10:01:18 +0200 Subject: [PATCH 1/5] Update bob and change and book store --- .../practice/bob/.meta/test_template.erb | 12 ++++ exercises/practice/bob/bob_test.rb | 60 +++++++++---------- .../book-store/.meta/test_template.erb | 12 ++++ .../practice/change/.meta/test_template.erb | 26 ++++++++ exercises/practice/change/change_test.rb | 27 ++++++--- 5 files changed, 100 insertions(+), 37 deletions(-) create mode 100644 exercises/practice/bob/.meta/test_template.erb create mode 100644 exercises/practice/book-store/.meta/test_template.erb create mode 100644 exercises/practice/change/.meta/test_template.erb diff --git a/exercises/practice/bob/.meta/test_template.erb b/exercises/practice/bob/.meta/test_template.erb new file mode 100644 index 0000000000..1472ccfe46 --- /dev/null +++ b/exercises/practice/bob/.meta/test_template.erb @@ -0,0 +1,12 @@ +require 'minitest/autorun' +require_relative 'bob' + +class BobTest < Minitest::Test +<% json["cases"].each do |cases| %> + def test_<%= underscore(cases["description"]) %> + <%= skip? %> + remark = <%= cases["input"]["heyBob"].dump() %> + assert_equal "<%= cases["expected"] %>", Bob.hey(remark), <%= "Bob hears #{ cases["input"]["heyBob"].dump}, and..".dump %> + end +<% end %> +end \ No newline at end of file diff --git a/exercises/practice/bob/bob_test.rb b/exercises/practice/bob/bob_test.rb index bd04ca3a46..87e0b8c817 100644 --- a/exercises/practice/bob/bob_test.rb +++ b/exercises/practice/bob/bob_test.rb @@ -5,150 +5,150 @@ class BobTest < Minitest::Test def test_stating_something # skip remark = "Tom-ay-to, tom-aaaah-to." - assert_equal "Whatever.", Bob.hey(remark), 'Bob hears "Tom-ay-to, tom-aaaah-to.", and..' + assert_equal "Whatever.", Bob.hey(remark), "Bob hears \"Tom-ay-to, tom-aaaah-to.\", and.." end def test_shouting skip remark = "WATCH OUT!" - assert_equal "Whoa, chill out!", Bob.hey(remark), 'Bob hears "WATCH OUT!", and..' + assert_equal "Whoa, chill out!", Bob.hey(remark), "Bob hears \"WATCH OUT!\", and.." end def test_shouting_gibberish skip remark = "FCECDFCAAB" - assert_equal "Whoa, chill out!", Bob.hey(remark), 'Bob hears "FCECDFCAAB", and..' + assert_equal "Whoa, chill out!", Bob.hey(remark), "Bob hears \"FCECDFCAAB\", and.." end def test_asking_a_question skip remark = "Does this cryogenic chamber make me look fat?" - assert_equal "Sure.", Bob.hey(remark), 'Bob hears "Does this cryogenic chamber make me look fat?", and..' + assert_equal "Sure.", Bob.hey(remark), "Bob hears \"Does this cryogenic chamber make me look fat?\", and.." end def test_asking_a_numeric_question skip remark = "You are, what, like 15?" - assert_equal "Sure.", Bob.hey(remark), 'Bob hears "You are, what, like 15?", and..' + assert_equal "Sure.", Bob.hey(remark), "Bob hears \"You are, what, like 15?\", and.." end def test_asking_gibberish skip remark = "fffbbcbeab?" - assert_equal "Sure.", Bob.hey(remark), 'Bob hears "fffbbcbeab?", and..' + assert_equal "Sure.", Bob.hey(remark), "Bob hears \"fffbbcbeab?\", and.." end def test_talking_forcefully skip remark = "Hi there!" - assert_equal "Whatever.", Bob.hey(remark), 'Bob hears "Hi there!", and..' + assert_equal "Whatever.", Bob.hey(remark), "Bob hears \"Hi there!\", and.." end def test_using_acronyms_in_regular_speech skip remark = "It's OK if you don't want to go work for NASA." - assert_equal "Whatever.", Bob.hey(remark), %q(Bob hears "It's OK if you don't want to go work for NASA.", and..) + assert_equal "Whatever.", Bob.hey(remark), "Bob hears \"It's OK if you don't want to go work for NASA.\", and.." end def test_forceful_question skip remark = "WHAT'S GOING ON?" - assert_equal "Calm down, I know what I'm doing!", Bob.hey(remark), %q(Bob hears "WHAT'S GOING ON?", and..) + assert_equal "Calm down, I know what I'm doing!", Bob.hey(remark), "Bob hears \"WHAT'S GOING ON?\", and.." end def test_shouting_numbers skip remark = "1, 2, 3 GO!" - assert_equal "Whoa, chill out!", Bob.hey(remark), 'Bob hears "1, 2, 3 GO!", and..' + assert_equal "Whoa, chill out!", Bob.hey(remark), "Bob hears \"1, 2, 3 GO!\", and.." end def test_no_letters skip remark = "1, 2, 3" - assert_equal "Whatever.", Bob.hey(remark), 'Bob hears "1, 2, 3", and..' + assert_equal "Whatever.", Bob.hey(remark), "Bob hears \"1, 2, 3\", and.." end def test_question_with_no_letters skip remark = "4?" - assert_equal "Sure.", Bob.hey(remark), 'Bob hears "4?", and..' + assert_equal "Sure.", Bob.hey(remark), "Bob hears \"4?\", and.." end def test_shouting_with_special_characters skip remark = "ZOMG THE %^*@\#$(*^ ZOMBIES ARE COMING!!11!!1!" - assert_equal "Whoa, chill out!", Bob.hey(remark), %q{Bob hears "ZOMG THE %^*@\#$(*^ ZOMBIES ARE COMING!!11!!1!", and..} + assert_equal "Whoa, chill out!", Bob.hey(remark), "Bob hears \"ZOMG THE %^*@\\\#$(*^ ZOMBIES ARE COMING!!11!!1!\", and.." end def test_shouting_with_no_exclamation_mark skip remark = "I HATE THE DENTIST" - assert_equal "Whoa, chill out!", Bob.hey(remark), 'Bob hears "I HATE THE DENTIST", and..' + assert_equal "Whoa, chill out!", Bob.hey(remark), "Bob hears \"I HATE THE DENTIST\", and.." end def test_statement_containing_question_mark skip remark = "Ending with ? means a question." - assert_equal "Whatever.", Bob.hey(remark), 'Bob hears "Ending with ? means a question.", and..' + assert_equal "Whatever.", Bob.hey(remark), "Bob hears \"Ending with ? means a question.\", and.." end def test_non_letters_with_question skip remark = ":) ?" - assert_equal "Sure.", Bob.hey(remark), 'Bob hears ":) ?", and..' + assert_equal "Sure.", Bob.hey(remark), "Bob hears \":) ?\", and.." end def test_prattling_on skip remark = "Wait! Hang on. Are you going to be OK?" - assert_equal "Sure.", Bob.hey(remark), 'Bob hears "Wait! Hang on. Are you going to be OK?", and..' + assert_equal "Sure.", Bob.hey(remark), "Bob hears \"Wait! Hang on. Are you going to be OK?\", and.." end def test_silence skip remark = "" - assert_equal "Fine. Be that way!", Bob.hey(remark), 'Bob hears "", and..' + assert_equal "Fine. Be that way!", Bob.hey(remark), "Bob hears \"\", and.." end def test_prolonged_silence skip remark = " " - assert_equal "Fine. Be that way!", Bob.hey(remark), 'Bob hears " ", and..' + assert_equal "Fine. Be that way!", Bob.hey(remark), "Bob hears \" \", and.." end def test_alternate_silence skip remark = "\t\t\t\t\t\t\t\t\t\t" - assert_equal "Fine. Be that way!", Bob.hey(remark), %q(Bob hears "\t\t\t\t\t\t\t\t\t\t", and..) - end - - def test_multiple_line_question - skip - remark = "\nDoes this cryogenic chamber make me look fat?\nNo." - assert_equal "Whatever.", Bob.hey(remark), %q(Bob hears "\nDoes this cryogenic chamber make me look fat?\nNo.", and..) + assert_equal "Fine. Be that way!", Bob.hey(remark), "Bob hears \"\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\", and.." end def test_starting_with_whitespace skip remark = " hmmmmmmm..." - assert_equal "Whatever.", Bob.hey(remark), 'Bob hears " hmmmmmmm...", and..' + assert_equal "Whatever.", Bob.hey(remark), "Bob hears \" hmmmmmmm...\", and.." end def test_ending_with_whitespace skip remark = "Okay if like my spacebar quite a bit? " - assert_equal "Sure.", Bob.hey(remark), 'Bob hears "Okay if like my spacebar quite a bit? ", and..' + assert_equal "Sure.", Bob.hey(remark), "Bob hears \"Okay if like my spacebar quite a bit? \", and.." end def test_other_whitespace skip remark = "\n\r \t" - assert_equal "Fine. Be that way!", Bob.hey(remark), %q(Bob hears "\n\r \t", and..) + assert_equal "Fine. Be that way!", Bob.hey(remark), "Bob hears \"\\n\\r \\t\", and.." end def test_non_question_ending_with_whitespace skip remark = "This is a statement ending with whitespace " - assert_equal "Whatever.", Bob.hey(remark), 'Bob hears "This is a statement ending with whitespace ", and..' + assert_equal "Whatever.", Bob.hey(remark), "Bob hears \"This is a statement ending with whitespace \", and.." + end + + def test_multiple_line_question + skip + remark = "\nDoes this cryogenic chamber make\n me look fat?" + assert_equal "Sure.", Bob.hey(remark), "Bob hears \"\\nDoes this cryogenic chamber make\\n me look fat?\", and.." end end diff --git a/exercises/practice/book-store/.meta/test_template.erb b/exercises/practice/book-store/.meta/test_template.erb new file mode 100644 index 0000000000..16714b66d6 --- /dev/null +++ b/exercises/practice/book-store/.meta/test_template.erb @@ -0,0 +1,12 @@ +require 'minitest/autorun' +require_relative 'book_store' + +class BookStoreTest < Minitest::Test +<% json["cases"].each do |cases| %> + def test_<%= underscore(cases["description"]) %> + <%= skip? %> + basket = <%= cases["input"]["basket"] %> + assert_in_delta <%= cases["expected"] / 100.0 %>0, BookStore.calculate_price(basket), 0.001 + end +<% end %> +end \ No newline at end of file diff --git a/exercises/practice/change/.meta/test_template.erb b/exercises/practice/change/.meta/test_template.erb new file mode 100644 index 0000000000..3537b0ad3a --- /dev/null +++ b/exercises/practice/change/.meta/test_template.erb @@ -0,0 +1,26 @@ +require 'minitest/autorun' +require_relative 'change' + +class ChangeTest < Minitest::Test +<% json["cases"].each do |cases| %> + def test_<%= underscore(cases["description"]) %> + <%= skip? %> + <%- if cases["expected"].is_a?(Hash) && cases["expected"].key?("error") -%> + <%- if cases["expected"]["error"] == "can't make target with given coins" -%> + assert_raises(Change::ImpossibleCombinationError) do + <%- elsif cases["expected"]["error"] == "target can't be negative" -%> + assert_raises(Change::NegativeTargetError) do + <%- end -%> + Change.generate(<%= cases["input"]["coins"] %>, <%= cases["input"]["target"] %>) + end + <%- else -%> + <%- if cases["expected"].empty? -%> + assert_empty Change.generate(<%= cases["input"]["coins"] %>, <%= cases["input"]["target"] %>) + <%- else -%> + expected = <%= cases["expected"] %> + assert_equal expected, Change.generate(<%= cases["input"]["coins"] %>, <%= cases["input"]["target"] %>) + <%- end -%> + <%- end -%> + end +<% end %> +end \ No newline at end of file diff --git a/exercises/practice/change/change_test.rb b/exercises/practice/change/change_test.rb index 416a82a7e4..ad6f9ac762 100644 --- a/exercises/practice/change/change_test.rb +++ b/exercises/practice/change/change_test.rb @@ -4,27 +4,32 @@ class ChangeTest < Minitest::Test def test_change_for_1_cent # skip - assert_equal [1], Change.generate([1, 5, 10, 25], 1) + expected = [1] + assert_equal expected, Change.generate([1, 5, 10, 25], 1) end def test_single_coin_change skip - assert_equal [25], Change.generate([1, 5, 10, 25, 100], 25) + expected = [25] + assert_equal expected, Change.generate([1, 5, 10, 25, 100], 25) end def test_multiple_coin_change skip - assert_equal [5, 10], Change.generate([1, 5, 10, 25, 100], 15) + expected = [5, 10] + assert_equal expected, Change.generate([1, 5, 10, 25, 100], 15) end def test_change_with_lilliputian_coins skip - assert_equal [4, 4, 15], Change.generate([1, 4, 15, 20, 50], 23) + expected = [4, 4, 15] + assert_equal expected, Change.generate([1, 4, 15, 20, 50], 23) end def test_change_with_lower_elbonia_coins skip - assert_equal [21, 21, 21], Change.generate([1, 5, 10, 21, 25], 63) + expected = [21, 21, 21] + assert_equal expected, Change.generate([1, 5, 10, 21, 25], 63) end def test_large_target_values @@ -35,12 +40,20 @@ def test_large_target_values def test_possible_change_without_unit_coins_available skip - assert_equal [2, 2, 2, 5, 10], Change.generate([2, 5, 10, 20, 50], 21) + expected = [2, 2, 2, 5, 10] + assert_equal expected, Change.generate([2, 5, 10, 20, 50], 21) end def test_another_possible_change_without_unit_coins_available skip - assert_equal [4, 4, 4, 5, 5, 5], Change.generate([4, 5], 27) + expected = [4, 4, 4, 5, 5, 5] + assert_equal expected, Change.generate([4, 5], 27) + end + + def test_a_greedy_approach_is_not_optimal + skip + expected = [10, 10] + assert_equal expected, Change.generate([1, 10, 11], 20) end def test_no_coins_make_0_change From 6fc655a2eaabe8422ff3601d67d4f2eba127f70f Mon Sep 17 00:00:00 2001 From: meatball Date: Sun, 31 Aug 2025 13:15:25 +0200 Subject: [PATCH 2/5] Add templates for bottlesong, bowling,clock and collatz conjecture --- .../bottle-song/.meta/test_template.erb | 18 ++++ .../practice/bottle-song/bottle_song_test.rb | 2 +- .../practice/bowling/.meta/test_template.erb | 26 ++++++ .../practice/clock/.meta/test_template.erb | 25 ++++++ exercises/practice/clock/clock_test.rb | 90 +++++++++---------- .../.meta/test_template.erb | 17 ++++ 6 files changed, 132 insertions(+), 46 deletions(-) create mode 100644 exercises/practice/bottle-song/.meta/test_template.erb create mode 100644 exercises/practice/bowling/.meta/test_template.erb create mode 100644 exercises/practice/clock/.meta/test_template.erb create mode 100644 exercises/practice/collatz-conjecture/.meta/test_template.erb diff --git a/exercises/practice/bottle-song/.meta/test_template.erb b/exercises/practice/bottle-song/.meta/test_template.erb new file mode 100644 index 0000000000..a0e752c9c7 --- /dev/null +++ b/exercises/practice/bottle-song/.meta/test_template.erb @@ -0,0 +1,18 @@ +require 'minitest/autorun' +require_relative 'bottle_song' + +class BottleSongTest < Minitest::Test +<% json["cases"].each do |cases| %> + <% cases["cases"].each do |sub_case| %> + <% sub_case["cases"].each do |sub_sub_case| %> + def test_<%= underscore(sub_sub_case["description"]) %> + <%= skip? %> + expected = <<~TEXT +<%= sub_sub_case["expected"].join("\n") %> + TEXT + assert_equal expected, BottleSong.recite(<%= sub_sub_case["input"]["startBottles"] %>, <%= sub_sub_case["input"]["takeDown"] %>) + end + <% end %> + <% end %> +<% end %> +end diff --git a/exercises/practice/bottle-song/bottle_song_test.rb b/exercises/practice/bottle-song/bottle_song_test.rb index 8eb9bef5e3..e7b23e97e9 100644 --- a/exercises/practice/bottle-song/bottle_song_test.rb +++ b/exercises/practice/bottle-song/bottle_song_test.rb @@ -2,7 +2,7 @@ require_relative 'bottle_song' class BottleSongTest < Minitest::Test - def test_single_verse + def test_first_generic_verse # skip expected = <<~TEXT Ten green bottles hanging on the wall, diff --git a/exercises/practice/bowling/.meta/test_template.erb b/exercises/practice/bowling/.meta/test_template.erb new file mode 100644 index 0000000000..1f60bac68d --- /dev/null +++ b/exercises/practice/bowling/.meta/test_template.erb @@ -0,0 +1,26 @@ +require 'minitest/autorun' +require_relative 'bowling' + +class BowlingTest < Minitest::Test +<% json["cases"].each do |cases| %> + def test_<%= underscore(cases["description"]) %> + <%= skip? %> + game = Game.new + rolls = <%= cases["input"]["previousRolls"] %> + rolls.each { |pins| game.roll(pins) } + <%- if cases["property"] == "score" -%> + <%- if cases["expected"].is_a?(Hash) && cases["expected"].key?("error") -%> + assert_raises Game::BowlingError do + game.score + end + <%- else -%> + assert_equal <%= cases["expected"] %>, game.score + <%- end -%> + <%- elsif cases["property"] == "roll" -%> + assert_raises Game::BowlingError do + game.roll(<%= cases["input"]["roll"] %>) + end + <%- end -%> + end +<% end %> +end diff --git a/exercises/practice/clock/.meta/test_template.erb b/exercises/practice/clock/.meta/test_template.erb new file mode 100644 index 0000000000..072048f6a3 --- /dev/null +++ b/exercises/practice/clock/.meta/test_template.erb @@ -0,0 +1,25 @@ +require 'minitest/autorun' +require_relative 'clock' + +class ClockTest < Minitest::Test +<% json["cases"].each do |cases| %> + <% cases["cases"].each do |sub_case| %> + def test_<%= underscore(sub_case["description"]) %> + <%= skip? %> + <%- if sub_case["property"] == "create" -%> + assert_equal '<%= sub_case["expected"] %>', Clock.new(<%= sub_case["input"]["hour"] != 0 ? "hour: #{sub_case["input"]["hour"]}" : "" %><%= sub_case["input"]["minute"] != 0 && sub_case["input"]["hour"] != 0 ? ", " : "" %><%= sub_case["input"]["minute"] != 0 ? "minute: #{sub_case["input"]["minute"]}" : "" %>).to_s + <%- elsif sub_case["property"] == "add" -%> + clock = Clock.new(<%= sub_case["input"]["hour"] != 0 ? "hour: #{sub_case["input"]["hour"]}" : "" %><%= sub_case["input"]["minute"] != 0 && sub_case["input"]["hour"] != 0 ? ", " : "" %><%= sub_case["input"]["minute"] != 0 ? "minute: #{sub_case["input"]["minute"]}" : "" %>) + assert_equal '<%= sub_case["expected"] %>', (clock + Clock.new(minute: <%= sub_case["input"]["value"]%>)).to_s + <%- elsif sub_case["property"] == "subtract" -%> + clock = Clock.new(<%= sub_case["input"]["hour"] != 0 ? "hour: #{sub_case["input"]["hour"]}" : "" %><%= sub_case["input"]["minute"] != 0 && sub_case["input"]["hour"] != 0 ? ", " : "" %><%= sub_case["input"]["minute"] != 0 ? "minute: #{sub_case["input"]["minute"]}" : "" %>) + assert_equal '<%= sub_case["expected"] %>', (clock - Clock.new(minute: <%= sub_case["input"]["value"]%>)).to_s + <%- elsif sub_case["property"] == "equal" -%> + clock1 = Clock.new(hour: <%= sub_case["input"]["clock1"]["hour"] %>, minute: <%= sub_case["input"]["clock1"]["minute"] %>) + clock2 = Clock.new(hour: <%= sub_case["input"]["clock2"]["hour"] %>, minute: <%= sub_case["input"]["clock2"]["minute"] %>) + <%= sub_case["expected"] ? "assert_equal" : "refute_equal"%> clock1, clock2 + <%- end -%> + end + <% end %> +<% end %> +end \ No newline at end of file diff --git a/exercises/practice/clock/clock_test.rb b/exercises/practice/clock/clock_test.rb index 92bbff02d5..95ac1585a3 100644 --- a/exercises/practice/clock/clock_test.rb +++ b/exercises/practice/clock/clock_test.rb @@ -4,198 +4,198 @@ class ClockTest < Minitest::Test def test_on_the_hour # skip - assert_equal "08:00", Clock.new(hour: 8).to_s + assert_equal '08:00', Clock.new(hour: 8).to_s end def test_past_the_hour skip - assert_equal "11:09", Clock.new(hour: 11, minute: 9).to_s + assert_equal '11:09', Clock.new(hour: 11, minute: 9).to_s end def test_midnight_is_zero_hours skip - assert_equal "00:00", Clock.new(hour: 24).to_s + assert_equal '00:00', Clock.new(hour: 24).to_s end def test_hour_rolls_over skip - assert_equal "01:00", Clock.new(hour: 25).to_s + assert_equal '01:00', Clock.new(hour: 25).to_s end def test_hour_rolls_over_continuously skip - assert_equal "04:00", Clock.new(hour: 100).to_s + assert_equal '04:00', Clock.new(hour: 100).to_s end def test_sixty_minutes_is_next_hour skip - assert_equal "02:00", Clock.new(hour: 1, minute: 60).to_s + assert_equal '02:00', Clock.new(hour: 1, minute: 60).to_s end def test_minutes_roll_over skip - assert_equal "02:40", Clock.new(minute: 160).to_s + assert_equal '02:40', Clock.new(minute: 160).to_s end def test_minutes_roll_over_continuously skip - assert_equal "04:43", Clock.new(minute: 1723).to_s + assert_equal '04:43', Clock.new(minute: 1723).to_s end def test_hour_and_minutes_roll_over skip - assert_equal "03:40", Clock.new(hour: 25, minute: 160).to_s + assert_equal '03:40', Clock.new(hour: 25, minute: 160).to_s end def test_hour_and_minutes_roll_over_continuously skip - assert_equal "11:01", Clock.new(hour: 201, minute: 3001).to_s + assert_equal '11:01', Clock.new(hour: 201, minute: 3001).to_s end def test_hour_and_minutes_roll_over_to_exactly_midnight skip - assert_equal "00:00", Clock.new(hour: 72, minute: 8640).to_s + assert_equal '00:00', Clock.new(hour: 72, minute: 8640).to_s end def test_negative_hour skip - assert_equal "23:15", Clock.new(hour: -1, minute: 15).to_s + assert_equal '23:15', Clock.new(hour: -1, minute: 15).to_s end def test_negative_hour_rolls_over skip - assert_equal "23:00", Clock.new(hour: -25).to_s + assert_equal '23:00', Clock.new(hour: -25).to_s end def test_negative_hour_rolls_over_continuously skip - assert_equal "05:00", Clock.new(hour: -91).to_s + assert_equal '05:00', Clock.new(hour: -91).to_s end def test_negative_minutes skip - assert_equal "00:20", Clock.new(hour: 1, minute: -40).to_s + assert_equal '00:20', Clock.new(hour: 1, minute: -40).to_s end def test_negative_minutes_roll_over skip - assert_equal "22:20", Clock.new(hour: 1, minute: -160).to_s + assert_equal '22:20', Clock.new(hour: 1, minute: -160).to_s end def test_negative_minutes_roll_over_continuously skip - assert_equal "16:40", Clock.new(hour: 1, minute: -4820).to_s + assert_equal '16:40', Clock.new(hour: 1, minute: -4820).to_s end def test_negative_sixty_minutes_is_previous_hour skip - assert_equal "01:00", Clock.new(hour: 2, minute: -60).to_s + assert_equal '01:00', Clock.new(hour: 2, minute: -60).to_s end def test_negative_hour_and_minutes_both_roll_over skip - assert_equal "20:20", Clock.new(hour: -25, minute: -160).to_s + assert_equal '20:20', Clock.new(hour: -25, minute: -160).to_s end def test_negative_hour_and_minutes_both_roll_over_continuously skip - assert_equal "22:10", Clock.new(hour: -121, minute: -5810).to_s + assert_equal '22:10', Clock.new(hour: -121, minute: -5810).to_s end def test_add_minutes skip - clock = Clock.new(hour: 10, minute: 0) - assert_equal "10:03", (clock + Clock.new(minute: 3)).to_s + clock = Clock.new(hour: 10) + assert_equal '10:03', (clock + Clock.new(minute: 3)).to_s end def test_add_no_minutes skip clock = Clock.new(hour: 6, minute: 41) - assert_equal "06:41", (clock + Clock.new(minute: 0)).to_s + assert_equal '06:41', (clock + Clock.new(minute: 0)).to_s end def test_add_to_next_hour skip - clock = Clock.new(hour: 0, minute: 45) - assert_equal "01:25", (clock + Clock.new(minute: 40)).to_s + clock = Clock.new(minute: 45) + assert_equal '01:25', (clock + Clock.new(minute: 40)).to_s end def test_add_more_than_one_hour skip - clock = Clock.new(hour: 10, minute: 0) - assert_equal "11:01", (clock + Clock.new(minute: 61)).to_s + clock = Clock.new(hour: 10) + assert_equal '11:01', (clock + Clock.new(minute: 61)).to_s end def test_add_more_than_two_hours_with_carry skip - clock = Clock.new(hour: 0, minute: 45) - assert_equal "03:25", (clock + Clock.new(minute: 160)).to_s + clock = Clock.new(minute: 45) + assert_equal '03:25', (clock + Clock.new(minute: 160)).to_s end def test_add_across_midnight skip clock = Clock.new(hour: 23, minute: 59) - assert_equal "00:01", (clock + Clock.new(minute: 2)).to_s + assert_equal '00:01', (clock + Clock.new(minute: 2)).to_s end - def test_add_more_than_one_day + def test_add_more_than_one_day_1500_min__25_hrs skip clock = Clock.new(hour: 5, minute: 32) - assert_equal "06:32", (clock + Clock.new(minute: 1500)).to_s + assert_equal '06:32', (clock + Clock.new(minute: 1500)).to_s end def test_add_more_than_two_days skip clock = Clock.new(hour: 1, minute: 1) - assert_equal "11:21", (clock + Clock.new(minute: 3500)).to_s + assert_equal '11:21', (clock + Clock.new(minute: 3500)).to_s end def test_subtract_minutes skip clock = Clock.new(hour: 10, minute: 3) - assert_equal "10:00", (clock - Clock.new(minute: 3)).to_s + assert_equal '10:00', (clock - Clock.new(minute: 3)).to_s end def test_subtract_to_previous_hour skip clock = Clock.new(hour: 10, minute: 3) - assert_equal "09:33", (clock - Clock.new(minute: 30)).to_s + assert_equal '09:33', (clock - Clock.new(minute: 30)).to_s end def test_subtract_more_than_an_hour skip clock = Clock.new(hour: 10, minute: 3) - assert_equal "08:53", (clock - Clock.new(minute: 70)).to_s + assert_equal '08:53', (clock - Clock.new(minute: 70)).to_s end def test_subtract_across_midnight skip - clock = Clock.new(hour: 0, minute: 3) - assert_equal "23:59", (clock - Clock.new(minute: 4)).to_s + clock = Clock.new(minute: 3) + assert_equal '23:59', (clock - Clock.new(minute: 4)).to_s end def test_subtract_more_than_two_hours skip - clock = Clock.new(hour: 0, minute: 0) - assert_equal "21:20", (clock - Clock.new(minute: 160)).to_s + clock = Clock.new + assert_equal '21:20', (clock - Clock.new(minute: 160)).to_s end def test_subtract_more_than_two_hours_with_borrow skip clock = Clock.new(hour: 6, minute: 15) - assert_equal "03:35", (clock - Clock.new(minute: 160)).to_s + assert_equal '03:35', (clock - Clock.new(minute: 160)).to_s end - def test_subtract_more_than_one_day + def test_subtract_more_than_one_day_1500_min__25_hrs skip clock = Clock.new(hour: 5, minute: 32) - assert_equal "04:32", (clock - Clock.new(minute: 1500)).to_s + assert_equal '04:32', (clock - Clock.new(minute: 1500)).to_s end def test_subtract_more_than_two_days skip clock = Clock.new(hour: 2, minute: 20) - assert_equal "00:20", (clock - Clock.new(minute: 3000)).to_s + assert_equal '00:20', (clock - Clock.new(minute: 3000)).to_s end def test_clocks_with_same_time @@ -303,7 +303,7 @@ def test_clocks_with_negative_hours_and_minutes_that_wrap assert_equal clock1, clock2 end - def test_full_clockand_zeroed_clock + def test_full_clock_and_zeroed_clock skip clock1 = Clock.new(hour: 24, minute: 0) clock2 = Clock.new(hour: 0, minute: 0) diff --git a/exercises/practice/collatz-conjecture/.meta/test_template.erb b/exercises/practice/collatz-conjecture/.meta/test_template.erb new file mode 100644 index 0000000000..6b2641d24b --- /dev/null +++ b/exercises/practice/collatz-conjecture/.meta/test_template.erb @@ -0,0 +1,17 @@ +require 'minitest/autorun' +require_relative 'collatz_conjecture' + +class CollatzConjectureTest < Minitest::Test +<% json["cases"].each do |cases| %> + def test_<%= underscore(cases["description"]) %> + <%= skip? %> + <%- if cases["expected"].is_a?(Hash) && cases["expected"].key?("error") -%> + assert_raises(ArgumentError) do + CollatzConjecture.steps(<%= cases["input"]["number"] %>) + end + <%- else -%> + assert_equal <%= cases["expected"] %>, CollatzConjecture.steps(<%= cases["input"]["number"] %>) + <%- end -%> + end +<% end %> +end \ No newline at end of file From 6a1c3cb4b508fe2576728a0a9029434f1111792d Mon Sep 17 00:00:00 2001 From: meatball <69751659+meatball133@users.noreply.github.com> Date: Sun, 31 Aug 2025 23:06:52 +0200 Subject: [PATCH 3/5] Fix exercise naming in generator (#1770) (#1776) * Enhance underscore method to filter invalid characters and update test file naming convention * Add test case which test underscore for special characters * Use character classes in regex. Also expose description for nested test cases. * Underscore must not create multiple underscores around special characters --- bin/generate | 6 +++++- generatorv2/lib/generator.rb | 6 +++--- generatorv2/lib/utils.rb | 2 +- generatorv2/test/utils_test.rb | 10 ++++++++++ 4 files changed, 19 insertions(+), 5 deletions(-) diff --git a/bin/generate b/bin/generate index fd6f32f277..4966be4299 100755 --- a/bin/generate +++ b/bin/generate @@ -9,6 +9,10 @@ def exercises .select { |file| File.directory? File.join('./exercises/practice', file) } end +def underscore(str) + str.gsub(/[^\w\s-]/, '').gsub(/[-\s]/, '_').downcase +end + class VerificationError < StandardError MESSAGE = 'The result generated for %s, does not match the current file' @@ -39,7 +43,7 @@ end parser.on('--verify', 'Verify all exercises') do exercises.each do |exercise| if File.exist?("./exercises/practice/#{exercise}/.meta/test_template.erb") - current_code = File.read("./exercises/practice/#{exercise}/#{exercise}_test.rb") + current_code = File.read("./exercises/practice/#{exercise}/#{underscore(exercise)}_test.rb") f = File.new("./exercises/practice/#{exercise}/temp_test.rb", 'w+') Generator.new(exercise).generate(f.path) generated_code = f.read diff --git a/generatorv2/lib/generator.rb b/generatorv2/lib/generator.rb index a0d669e929..5f15b0b43c 100644 --- a/generatorv2/lib/generator.rb +++ b/generatorv2/lib/generator.rb @@ -21,17 +21,17 @@ def generate(result_path = "./exercises/practice/#{@exercise}/#{underscore(@exer additional_json(json) json["cases"] = remove_tests(uuid, json) status = proc { status } - template = ERB.new File.read("./exercises/practice/#{@exercise}/.meta/test_template.erb") + template = ERB.new(File.read("./exercises/practice/#{@exercise}/.meta/test_template.erb"), trim_mode: '-') result = template.result(binding) File.write(result_path, result) RuboCop::CLI.new. - run(['-x', '-c', '.rubocop.yml', '-o', NullDevice.path, result_path]) + run(['-a', '-c', '.rubocop.yml', '-o', NullDevice.path, result_path]) end def underscore(str) - str.gsub(/[-\s]/, '_').downcase + str.gsub(/[^\w\s-]/, '').gsub(' ', ' ').gsub(/[-\s]/, '_').downcase end def camel_case(str) diff --git a/generatorv2/lib/utils.rb b/generatorv2/lib/utils.rb index d6b80971e6..a728848e8c 100644 --- a/generatorv2/lib/utils.rb +++ b/generatorv2/lib/utils.rb @@ -47,7 +47,7 @@ def additional_json(json) def remove_tests(uuid, json) json["cases"].each_with_object([]) do |x, acc| if x["cases"] - acc << { "cases" => remove_tests(uuid, x) } + acc << { "cases" => remove_tests(uuid, x), "description" => x["description"] } elsif uuid.include?(x["uuid"]) acc << x end diff --git a/generatorv2/test/utils_test.rb b/generatorv2/test/utils_test.rb index 235021882c..944e0b66b8 100644 --- a/generatorv2/test/utils_test.rb +++ b/generatorv2/test/utils_test.rb @@ -22,6 +22,16 @@ def test_underscore_with_two_words Generator.new("two-fer").underscore("two-fer") end + def test_underscore_with_special_characters + assert_equal "two_fer", + Generator.new("two-fer").underscore("two,!@#$%^&*()-fer") + end + + def test_underscore_with_special_characters_should_not_create_multiple_spaces + assert_equal "two_fer", + Generator.new("two-fer").underscore("two = fer") + end + def test_first_time_includes_hastag assert_equal "# skip", Generator.new("acronym").skip? From fa46239d33d0470be8c87ab942058091e3a318b4 Mon Sep 17 00:00:00 2001 From: meatball Date: Sun, 31 Aug 2025 23:13:02 +0200 Subject: [PATCH 4/5] Fix formatting --- exercises/practice/clock/clock_test.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/exercises/practice/clock/clock_test.rb b/exercises/practice/clock/clock_test.rb index 95ac1585a3..7a876c7f65 100644 --- a/exercises/practice/clock/clock_test.rb +++ b/exercises/practice/clock/clock_test.rb @@ -138,7 +138,7 @@ def test_add_across_midnight assert_equal '00:01', (clock + Clock.new(minute: 2)).to_s end - def test_add_more_than_one_day_1500_min__25_hrs + def test_add_more_than_one_day_1500_min_25_hrs skip clock = Clock.new(hour: 5, minute: 32) assert_equal '06:32', (clock + Clock.new(minute: 1500)).to_s @@ -186,7 +186,7 @@ def test_subtract_more_than_two_hours_with_borrow assert_equal '03:35', (clock - Clock.new(minute: 160)).to_s end - def test_subtract_more_than_one_day_1500_min__25_hrs + def test_subtract_more_than_one_day_1500_min_25_hrs skip clock = Clock.new(hour: 5, minute: 32) assert_equal '04:32', (clock - Clock.new(minute: 1500)).to_s From 5aeee02927c87d68f4b0349eb10063167edbfa4f Mon Sep 17 00:00:00 2001 From: meatball Date: Tue, 2 Sep 2025 19:54:16 +0200 Subject: [PATCH 5/5] Add EOL --- exercises/practice/bob/.meta/test_template.erb | 2 +- exercises/practice/book-store/.meta/test_template.erb | 2 +- exercises/practice/change/.meta/test_template.erb | 2 +- exercises/practice/clock/.meta/test_template.erb | 2 +- exercises/practice/collatz-conjecture/.meta/test_template.erb | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/exercises/practice/bob/.meta/test_template.erb b/exercises/practice/bob/.meta/test_template.erb index 1472ccfe46..521fa04e84 100644 --- a/exercises/practice/bob/.meta/test_template.erb +++ b/exercises/practice/bob/.meta/test_template.erb @@ -9,4 +9,4 @@ class BobTest < Minitest::Test assert_equal "<%= cases["expected"] %>", Bob.hey(remark), <%= "Bob hears #{ cases["input"]["heyBob"].dump}, and..".dump %> end <% end %> -end \ No newline at end of file +end diff --git a/exercises/practice/book-store/.meta/test_template.erb b/exercises/practice/book-store/.meta/test_template.erb index 16714b66d6..3466cd8163 100644 --- a/exercises/practice/book-store/.meta/test_template.erb +++ b/exercises/practice/book-store/.meta/test_template.erb @@ -9,4 +9,4 @@ class BookStoreTest < Minitest::Test assert_in_delta <%= cases["expected"] / 100.0 %>0, BookStore.calculate_price(basket), 0.001 end <% end %> -end \ No newline at end of file +end diff --git a/exercises/practice/change/.meta/test_template.erb b/exercises/practice/change/.meta/test_template.erb index 3537b0ad3a..d78da98584 100644 --- a/exercises/practice/change/.meta/test_template.erb +++ b/exercises/practice/change/.meta/test_template.erb @@ -23,4 +23,4 @@ class ChangeTest < Minitest::Test <%- end -%> end <% end %> -end \ No newline at end of file +end diff --git a/exercises/practice/clock/.meta/test_template.erb b/exercises/practice/clock/.meta/test_template.erb index 072048f6a3..73a9a4f615 100644 --- a/exercises/practice/clock/.meta/test_template.erb +++ b/exercises/practice/clock/.meta/test_template.erb @@ -22,4 +22,4 @@ class ClockTest < Minitest::Test end <% end %> <% end %> -end \ No newline at end of file +end diff --git a/exercises/practice/collatz-conjecture/.meta/test_template.erb b/exercises/practice/collatz-conjecture/.meta/test_template.erb index 6b2641d24b..3ddb2ed329 100644 --- a/exercises/practice/collatz-conjecture/.meta/test_template.erb +++ b/exercises/practice/collatz-conjecture/.meta/test_template.erb @@ -14,4 +14,4 @@ class CollatzConjectureTest < Minitest::Test <%- end -%> end <% end %> -end \ No newline at end of file +end