From 6d26886323c3416d7fae142b15638ce5857896d3 Mon Sep 17 00:00:00 2001 From: meatball Date: Sun, 31 Aug 2025 17:39:44 +0200 Subject: [PATCH 1/6] Add templates for exercises batch 3 --- .../circular-buffer/.meta/test_template.erb | 30 ++++ .../circular-buffer/circular_buffer_test.rb | 138 ++++++++++-------- .../crypto-square/.meta/test_template.erb | 12 ++ .../practice/crypto-square/.meta/tests.toml | 5 + .../crypto-square/crypto_square_test.rb | 34 ++--- .../custom-set/.meta/test_template.erb | 43 ++++++ .../practice/custom-set/custom_set_test.rb | 25 +++- .../practice/darts/.meta/test_template.erb | 12 ++ exercises/practice/darts/darts_test.rb | 4 +- .../practice/diamond/.meta/test_template.erb | 15 ++ exercises/practice/diamond/diamond_test.rb | 107 +++++++++++--- .../.meta/test_template.erb | 13 ++ .../dnd-character/.meta/test_template.erb | 69 +++++++++ .../dnd-character/dnd_character_test.rb | 50 +++---- 14 files changed, 433 insertions(+), 124 deletions(-) create mode 100644 exercises/practice/circular-buffer/.meta/test_template.erb create mode 100644 exercises/practice/crypto-square/.meta/test_template.erb create mode 100644 exercises/practice/custom-set/.meta/test_template.erb create mode 100644 exercises/practice/darts/.meta/test_template.erb create mode 100644 exercises/practice/diamond/.meta/test_template.erb create mode 100644 exercises/practice/difference-of-squares/.meta/test_template.erb create mode 100644 exercises/practice/dnd-character/.meta/test_template.erb diff --git a/exercises/practice/circular-buffer/.meta/test_template.erb b/exercises/practice/circular-buffer/.meta/test_template.erb new file mode 100644 index 0000000000..3be26a5f29 --- /dev/null +++ b/exercises/practice/circular-buffer/.meta/test_template.erb @@ -0,0 +1,30 @@ +require 'minitest/autorun' +require_relative 'circular_buffer' + +class CircularBufferTest < Minitest::Test +<% json["cases"].each do |cases| %> + def test_<%= underscore(cases["description"]) %> + <%= skip? %> + buffer = CircularBuffer.new(<%= cases["input"]["capacity"] %>) + <%- cases["input"]["operations"].each do |operation| -%> + <%- if operation["operation"] == "read"-%> + <%- if operation["should_succeed"] -%> + assert_equal '<%= operation["expected"] %>', buffer.read + <%- else -%> + assert_raises(CircularBuffer::BufferEmptyException) { buffer.read } + <%- end -%> + <%- elsif operation["operation"] == "write" -%> + <%- if operation["should_succeed"] -%> + buffer.write('<%= operation["item"] %>') + <%- else -%> + assert_raises(CircularBuffer::BufferFullException) { buffer.write('<%= operation["item"] %>') } + <%- end -%> + <%- elsif operation["operation"] == "overwrite" -%> + buffer.write!('<%= operation["item"] %>') + <% elsif operation["operation"] == "clear" -%> + buffer.clear + <%- end -%> + <%- end -%> + end + <% end %> +end \ No newline at end of file diff --git a/exercises/practice/circular-buffer/circular_buffer_test.rb b/exercises/practice/circular-buffer/circular_buffer_test.rb index c239e444e4..eacd0f54d0 100644 --- a/exercises/practice/circular-buffer/circular_buffer_test.rb +++ b/exercises/practice/circular-buffer/circular_buffer_test.rb @@ -2,107 +2,131 @@ require_relative 'circular_buffer' class CircularBufferTest < Minitest::Test - def test_read_empty_buffer_throws_buffer_empty_exception + def test_reading_empty_buffer_should_fail + # skip buffer = CircularBuffer.new(1) assert_raises(CircularBuffer::BufferEmptyException) { buffer.read } end - def test_write_and_read_back_one_item + def test_can_read_an_item_just_written skip buffer = CircularBuffer.new(1) - buffer.write '1' + buffer.write('1') assert_equal '1', buffer.read - assert_raises(CircularBuffer::BufferEmptyException) { buffer.read } end - def test_write_and_read_back_multiple_items + def test_each_item_may_only_be_read_once skip - buffer = CircularBuffer.new(2) - buffer.write '1' - buffer.write '2' + buffer = CircularBuffer.new(1) + buffer.write('1') assert_equal '1', buffer.read - assert_equal '2', buffer.read assert_raises(CircularBuffer::BufferEmptyException) { buffer.read } end - def test_clearing_buffer + def test_items_are_read_in_the_order_they_are_written skip - buffer = CircularBuffer.new(3) - ('1'..'3').each { |i| buffer.write i } - buffer.clear - assert_raises(CircularBuffer::BufferEmptyException) { buffer.read } - buffer.write '1' - buffer.write '2' + buffer = CircularBuffer.new(2) + buffer.write('1') + buffer.write('2') assert_equal '1', buffer.read - buffer.write '3' assert_equal '2', buffer.read end - def test_alternate_write_and_read + def test_full_buffer_cant_be_written_to skip - buffer = CircularBuffer.new(2) - buffer.write '1' + buffer = CircularBuffer.new(1) + buffer.write('1') + assert_raises(CircularBuffer::BufferFullException) { buffer.write('2') } + end + + def test_a_read_frees_up_capacity_for_another_write + skip + buffer = CircularBuffer.new(1) + buffer.write('1') assert_equal '1', buffer.read - buffer.write '2' + buffer.write('2') assert_equal '2', buffer.read end - def test_reads_back_oldest_item + def test_read_position_is_maintained_even_across_multiple_writes skip buffer = CircularBuffer.new(3) - buffer.write '1' - buffer.write '2' - buffer.read - buffer.write '3' + buffer.write('1') + buffer.write('2') + assert_equal '1', buffer.read + buffer.write('3') assert_equal '2', buffer.read assert_equal '3', buffer.read end - def test_writing_to_a_full_buffer_throws_an_exception + def test_items_cleared_out_of_buffer_cant_be_read skip - buffer = CircularBuffer.new(2) - buffer.write '1' - buffer.write '2' - assert_raises(CircularBuffer::BufferFullException) { buffer.write 'A' } + buffer = CircularBuffer.new(1) + buffer.write('1') + buffer.clear + assert_raises(CircularBuffer::BufferEmptyException) { buffer.read } end - def test_overwriting_oldest_item_in_a_full_buffer + def test_clear_frees_up_capacity_for_another_write skip - buffer = CircularBuffer.new(2) - buffer.write '1' - buffer.write '2' - buffer.write! 'A' + buffer = CircularBuffer.new(1) + buffer.write('1') + buffer.clear + buffer.write('2') assert_equal '2', buffer.read - assert_equal 'A', buffer.read - assert_raises(CircularBuffer::BufferEmptyException) { buffer.read } end - def test_forced_writes_to_non_full_buffer_should_behave_like_writes + def test_clear_does_nothing_on_empty_buffer + skip + buffer = CircularBuffer.new(1) + buffer.clear + buffer.write('1') + assert_equal '1', buffer.read + end + + def test_overwrite_acts_like_write_on_non_full_buffer skip buffer = CircularBuffer.new(2) - buffer.write '1' - buffer.write! '2' + buffer.write('1') + buffer.write!('2') assert_equal '1', buffer.read assert_equal '2', buffer.read - assert_raises(CircularBuffer::BufferEmptyException) { buffer.read } end - def test_alternate_read_and_write_into_buffer_overflow + def test_overwrite_replaces_the_oldest_item_on_full_buffer + skip + buffer = CircularBuffer.new(2) + buffer.write('1') + buffer.write('2') + buffer.write!('3') + assert_equal '2', buffer.read + assert_equal '3', buffer.read + end + + def test_overwrite_replaces_the_oldest_item_remaining_in_buffer_following_a_read skip - buffer = CircularBuffer.new(5) - ('1'..'3').each { |i| buffer.write i } - buffer.read - buffer.read - buffer.write '4' - buffer.read - ('5'..'8').each { |i| buffer.write i } - buffer.write! 'A' - buffer.write! 'B' - ('6'..'8').each do |i| - assert_equal i, buffer.read - end - assert_equal 'A', buffer.read - assert_equal 'B', buffer.read + buffer = CircularBuffer.new(3) + buffer.write('1') + buffer.write('2') + buffer.write('3') + assert_equal '1', buffer.read + buffer.write('4') + buffer.write!('5') + assert_equal '3', buffer.read + assert_equal '4', buffer.read + assert_equal '5', buffer.read + end + + def test_initial_clear_does_not_affect_wrapping_around + skip + buffer = CircularBuffer.new(2) + buffer.clear + buffer.write('1') + buffer.write('2') + buffer.write!('3') + buffer.write!('4') + assert_equal '3', buffer.read + assert_equal '4', buffer.read assert_raises(CircularBuffer::BufferEmptyException) { buffer.read } end end diff --git a/exercises/practice/crypto-square/.meta/test_template.erb b/exercises/practice/crypto-square/.meta/test_template.erb new file mode 100644 index 0000000000..7ab2747dbd --- /dev/null +++ b/exercises/practice/crypto-square/.meta/test_template.erb @@ -0,0 +1,12 @@ +require 'minitest/autorun' +require_relative 'crypto_square' + +class CryptoSquareTest < Minitest::Test +<% json["cases"].each do |cases| %> + def test_<%= underscore(cases["description"]) %> + <%= skip? %> + plaintext = '<%= cases["input"]["plaintext"] %>' + assert_equal '<%= cases["expected"] %>', Crypto.new(plaintext).ciphertext + end + <% end %> +end \ No newline at end of file diff --git a/exercises/practice/crypto-square/.meta/tests.toml b/exercises/practice/crypto-square/.meta/tests.toml index 085d142ead..94ef0819fe 100644 --- a/exercises/practice/crypto-square/.meta/tests.toml +++ b/exercises/practice/crypto-square/.meta/tests.toml @@ -32,3 +32,8 @@ description = "8 character plaintext results in 3 chunks, the last one with a tr [fbcb0c6d-4c39-4a31-83f6-c473baa6af80] description = "54 character plaintext results in 7 chunks, the last two with trailing spaces" +include = false + +[33fd914e-fa44-445b-8f38-ff8fbc9fe6e6] +description = "54 character plaintext results in 8 chunks, the last two with trailing spaces" +reimplements = "fbcb0c6d-4c39-4a31-83f6-c473baa6af80" diff --git a/exercises/practice/crypto-square/crypto_square_test.rb b/exercises/practice/crypto-square/crypto_square_test.rb index 2802501ea8..d7b88e141e 100644 --- a/exercises/practice/crypto-square/crypto_square_test.rb +++ b/exercises/practice/crypto-square/crypto_square_test.rb @@ -4,49 +4,49 @@ class CryptoSquareTest < Minitest::Test def test_empty_plaintext_results_in_an_empty_ciphertext # skip - plaintext = "" - assert_equal "", Crypto.new(plaintext).ciphertext + plaintext = '' + assert_equal '', Crypto.new(plaintext).ciphertext end def test_normalization_results_in_empty_plaintext skip - plaintext = "... --- ..." - assert_equal "", Crypto.new(plaintext).ciphertext + plaintext = '... --- ...' + assert_equal '', Crypto.new(plaintext).ciphertext end def test_lowercase skip - plaintext = "A" - assert_equal "a", Crypto.new(plaintext).ciphertext + plaintext = 'A' + assert_equal 'a', Crypto.new(plaintext).ciphertext end def test_remove_spaces skip - plaintext = " b " - assert_equal "b", Crypto.new(plaintext).ciphertext + plaintext = ' b ' + assert_equal 'b', Crypto.new(plaintext).ciphertext end def test_remove_punctuation skip - plaintext = "@1,%!" - assert_equal "1", Crypto.new(plaintext).ciphertext + plaintext = '@1,%!' + assert_equal '1', Crypto.new(plaintext).ciphertext end def test_9_character_plaintext_results_in_3_chunks_of_3_characters skip - plaintext = "This is fun!" - assert_equal "tsf hiu isn", Crypto.new(plaintext).ciphertext + plaintext = 'This is fun!' + assert_equal 'tsf hiu isn', Crypto.new(plaintext).ciphertext end def test_8_character_plaintext_results_in_3_chunks_the_last_one_with_a_trailing_space skip - plaintext = "Chill out." - assert_equal "clu hlt io ", Crypto.new(plaintext).ciphertext + plaintext = 'Chill out.' + assert_equal 'clu hlt io ', Crypto.new(plaintext).ciphertext end - def test_54_character_plaintext_results_in_7_chunks_the_last_two_with_trailing_spaces + def test_54_character_plaintext_results_in_8_chunks_the_last_two_with_trailing_spaces skip - plaintext = "If man was meant to stay on the ground, god would have given us roots." - assert_equal "imtgdvs fearwer mayoogo anouuio ntnnlvt wttddes aohghn sseoau ", Crypto.new(plaintext).ciphertext + plaintext = 'If man was meant to stay on the ground, god would have given us roots.' + assert_equal 'imtgdvs fearwer mayoogo anouuio ntnnlvt wttddes aohghn sseoau ', Crypto.new(plaintext).ciphertext end end diff --git a/exercises/practice/custom-set/.meta/test_template.erb b/exercises/practice/custom-set/.meta/test_template.erb new file mode 100644 index 0000000000..872eb2f016 --- /dev/null +++ b/exercises/practice/custom-set/.meta/test_template.erb @@ -0,0 +1,43 @@ +require 'minitest/autorun' +require_relative 'custom_set' + +class CustomSetTest < Minitest::Test +<% json["cases"].each do |cases| %> + <% cases["cases"].each do |sub_case| %> + def test_<%= underscore(sub_case["description"]) %> + <%= skip? %> + <%- if sub_case["input"].key?("set") -%> + set = CustomSet.new <%= sub_case["input"]["set"] %> + <%- if sub_case["property"] == "empty" -%> + <%= sub_case["expected"] ? "assert" : "refute" %>_empty set + <%- elsif sub_case["property"] == "contains" -%> + element = <%= sub_case["input"]["element"] %> + <%= sub_case["expected"] ? "assert" : "refute" %> set.member? element + <%- elsif sub_case["property"] == "add" -%> + expected = CustomSet.new <%= sub_case["expected"] %> + assert_equal expected, set.add(<%= sub_case["input"]["element"] %>) + <%- end -%> + <%- else -%> + set1 = CustomSet.new <%= sub_case["input"]["set1"] %> + set2 = CustomSet.new <%= sub_case["input"]["set2"] %> + <%- if sub_case["property"] == "subset" -%> + <%= sub_case["expected"] ? "assert" : "refute" %> set1.subset? set2 + <%- elsif sub_case["property"] == "disjoint" -%> + <%= sub_case["expected"] ? "assert" : "refute" %> set1.disjoint? set2 + <%- elsif sub_case["property"] == "equal" -%> + <%= sub_case["expected"] ? "assert" : "refute" %>_equal set1, set2 + <%- elsif sub_case["property"] == "intersection" -%> + expected = CustomSet.new <%= sub_case["expected"] %> + assert_equal expected, set1.intersection(set2) + <%- elsif sub_case["property"] == "difference" -%> + expected = CustomSet.new <%= sub_case["expected"] %> + assert_equal expected, set1.difference(set2) + <%- elsif sub_case["property"] == "union" -%> + expected = CustomSet.new <%= sub_case["expected"] %> + assert_equal expected, set1.union(set2) + <%- end -%> + <% end %> + end + <% end %> +<% end %> +end \ No newline at end of file diff --git a/exercises/practice/custom-set/custom_set_test.rb b/exercises/practice/custom-set/custom_set_test.rb index 648fa8549c..4991e94a2e 100644 --- a/exercises/practice/custom-set/custom_set_test.rb +++ b/exercises/practice/custom-set/custom_set_test.rb @@ -154,6 +154,13 @@ def test_set_is_not_equal_to_larger_set_with_same_elements refute_equal set1, set2 end + def test_set_is_equal_to_a_set_constructed_from_an_array_with_duplicates + skip + set1 = CustomSet.new [1] + set2 = CustomSet.new [1, 1] + assert_equal set1, set2 + end + def test_add_to_empty_set skip set = CustomSet.new [] @@ -180,7 +187,7 @@ def test_intersection_of_two_empty_sets_is_an_empty_set set1 = CustomSet.new [] set2 = CustomSet.new [] expected = CustomSet.new [] - assert_equal expected, set2.intersection(set1) + assert_equal expected, set1.intersection(set2) end def test_intersection_of_an_empty_set_and_non_empty_set_is_an_empty_set @@ -188,7 +195,7 @@ def test_intersection_of_an_empty_set_and_non_empty_set_is_an_empty_set set1 = CustomSet.new [] set2 = CustomSet.new [3, 2, 5] expected = CustomSet.new [] - assert_equal expected, set2.intersection(set1) + assert_equal expected, set1.intersection(set2) end def test_intersection_of_a_non_empty_set_and_an_empty_set_is_an_empty_set @@ -196,7 +203,7 @@ def test_intersection_of_a_non_empty_set_and_an_empty_set_is_an_empty_set set1 = CustomSet.new [1, 2, 3, 4] set2 = CustomSet.new [] expected = CustomSet.new [] - assert_equal expected, set2.intersection(set1) + assert_equal expected, set1.intersection(set2) end def test_intersection_of_two_sets_with_no_shared_elements_is_an_empty_set @@ -204,7 +211,7 @@ def test_intersection_of_two_sets_with_no_shared_elements_is_an_empty_set set1 = CustomSet.new [1, 2, 3] set2 = CustomSet.new [4, 5, 6] expected = CustomSet.new [] - assert_equal expected, set2.intersection(set1) + assert_equal expected, set1.intersection(set2) end def test_intersection_of_two_sets_with_shared_elements_is_a_set_of_the_shared_elements @@ -212,7 +219,7 @@ def test_intersection_of_two_sets_with_shared_elements_is_a_set_of_the_shared_el set1 = CustomSet.new [1, 2, 3, 4] set2 = CustomSet.new [3, 2, 5] expected = CustomSet.new [2, 3] - assert_equal expected, set2.intersection(set1) + assert_equal expected, set1.intersection(set2) end def test_difference_of_two_empty_sets_is_an_empty_set @@ -247,6 +254,14 @@ def test_difference_of_two_non_empty_sets_is_a_set_of_elements_that_are_only_in_ assert_equal expected, set1.difference(set2) end + def test_difference_removes_all_duplicates_in_the_first_set + skip + set1 = CustomSet.new [1, 1] + set2 = CustomSet.new [1] + expected = CustomSet.new [] + assert_equal expected, set1.difference(set2) + end + def test_union_of_empty_sets_is_an_empty_set skip set1 = CustomSet.new [] diff --git a/exercises/practice/darts/.meta/test_template.erb b/exercises/practice/darts/.meta/test_template.erb new file mode 100644 index 0000000000..a5ad83d9fe --- /dev/null +++ b/exercises/practice/darts/.meta/test_template.erb @@ -0,0 +1,12 @@ +require 'minitest/autorun' +require_relative 'darts' + +class DartsTest < Minitest::Test +<% json["cases"].each do |cases| %> + def test_<%= underscore(cases["description"]) %> + <%= skip? %> + darts = Darts.new(<%= cases["input"]["x"] %>, <%= cases["input"]["y"] %>) + assert_equal(<%= cases["expected"] %>, darts.score) + end + <% end %> +end \ No newline at end of file diff --git a/exercises/practice/darts/darts_test.rb b/exercises/practice/darts/darts_test.rb index d155ff2bd0..7f70119f86 100644 --- a/exercises/practice/darts/darts_test.rb +++ b/exercises/practice/darts/darts_test.rb @@ -26,13 +26,13 @@ def test_on_the_inner_circle assert_equal(10, darts.score) end - def test_exactly_on_centre + def test_exactly_on_center skip darts = Darts.new(0, 0) assert_equal(10, darts.score) end - def test_near_the_centre + def test_near_the_center skip darts = Darts.new(-0.1, -0.1) assert_equal(10, darts.score) diff --git a/exercises/practice/diamond/.meta/test_template.erb b/exercises/practice/diamond/.meta/test_template.erb new file mode 100644 index 0000000000..ff29ea8d18 --- /dev/null +++ b/exercises/practice/diamond/.meta/test_template.erb @@ -0,0 +1,15 @@ +require 'minitest/autorun' +require_relative 'diamond' + +class DiamondTest < Minitest::Test +<% json["cases"].each do |cases| %> + def test_<%= underscore(cases["description"]) %> + <%= skip? %> + answer = Diamond.make_diamond('<%= cases["input"]["letter"] %>') + string =<% cases["expected"].each do |row| -%> + "<%= row %>\n"\ + <%- end %> + assert_equal string, answer + end + <% end %> +end \ No newline at end of file diff --git a/exercises/practice/diamond/diamond_test.rb b/exercises/practice/diamond/diamond_test.rb index 579be07d1b..41cc04a2b2 100644 --- a/exercises/practice/diamond/diamond_test.rb +++ b/exercises/practice/diamond/diamond_test.rb @@ -2,34 +2,105 @@ require_relative 'diamond' class DiamondTest < Minitest::Test - def test_letter_a + def test_degenerate_case_with_a_single_a_row + # skip answer = Diamond.make_diamond('A') - assert_equal "A\n", answer + string = "A\n"\ + + assert_equal string, answer + end + + def test_degenerate_case_with_no_row_containing_3_distinct_groups_of_spaces + skip + answer = Diamond.make_diamond('B') + string = " A \n"\ + "B B\n"\ + " A \n"\ + + assert_equal string, answer end - def test_letter_c + def test_smallest_non_degenerate_case_with_odd_diamond_side_length skip answer = Diamond.make_diamond('C') string = " A \n"\ - " B B \n"\ - "C C\n"\ - " B B \n"\ - " A \n" + " B B \n"\ + "C C\n"\ + " B B \n"\ + " A \n"\ + assert_equal string, answer end - def test_letter_e + def test_smallest_non_degenerate_case_with_even_diamond_side_length skip - answer = Diamond.make_diamond('E') - string = " A \n"\ - " B B \n"\ - " C C \n"\ - " D D \n"\ - "E E\n"\ - " D D \n"\ - " C C \n"\ - " B B \n"\ - " A \n" + answer = Diamond.make_diamond('D') + string = " A \n"\ + " B B \n"\ + " C C \n"\ + "D D\n"\ + " C C \n"\ + " B B \n"\ + " A \n"\ + + assert_equal string, answer + end + + def test_largest_possible_diamond + skip + answer = Diamond.make_diamond('Z') + string = " A \n"\ + " B B \n"\ + " C C \n"\ + " D D \n"\ + " E E \n"\ + " F F \n"\ + " G G \n"\ + " H H \n"\ + " I I \n"\ + " J J \n"\ + " K K \n"\ + " L L \n"\ + " M M \n"\ + " N N \n"\ + " O O \n"\ + " P P \n"\ + " Q Q \n"\ + " R R \n"\ + " S S \n"\ + " T T \n"\ + " U U \n"\ + " V V \n"\ + " W W \n"\ + " X X \n"\ + " Y Y \n"\ + "Z Z\n"\ + " Y Y \n"\ + " X X \n"\ + " W W \n"\ + " V V \n"\ + " U U \n"\ + " T T \n"\ + " S S \n"\ + " R R \n"\ + " Q Q \n"\ + " P P \n"\ + " O O \n"\ + " N N \n"\ + " M M \n"\ + " L L \n"\ + " K K \n"\ + " J J \n"\ + " I I \n"\ + " H H \n"\ + " G G \n"\ + " F F \n"\ + " E E \n"\ + " D D \n"\ + " C C \n"\ + " B B \n"\ + " A \n"\ + assert_equal string, answer end end diff --git a/exercises/practice/difference-of-squares/.meta/test_template.erb b/exercises/practice/difference-of-squares/.meta/test_template.erb new file mode 100644 index 0000000000..85e8865cfa --- /dev/null +++ b/exercises/practice/difference-of-squares/.meta/test_template.erb @@ -0,0 +1,13 @@ +require 'minitest/autorun' +require_relative 'difference_of_squares' + +class DifferenceOfSquaresTest < Minitest::Test +<% json["cases"].each do |cases| %> + <% cases["cases"].each do |sub_case| %> + def test_<%= underscore(sub_case["description"]) %> + <%= skip? %> + assert_equal <%= sub_case["expected"] %>, Squares.new(<%= sub_case["input"]["number"] %>).<%= sub_case["property"] == "differenceOfSquares" ? "difference" : sub_case["property"].gsub(/([A-Z])/, '_\1').downcase.sub(/^_/, '') %> + end + <% end %> +<% end %> +end \ No newline at end of file diff --git a/exercises/practice/dnd-character/.meta/test_template.erb b/exercises/practice/dnd-character/.meta/test_template.erb new file mode 100644 index 0000000000..e841b1d8de --- /dev/null +++ b/exercises/practice/dnd-character/.meta/test_template.erb @@ -0,0 +1,69 @@ +require 'minitest/autorun' +require_relative 'dnd_character' + +module Helpers + BASE_HITPOINTS = 10 + + def attributes + %i[strength dexterity constitution intelligence wisdom charisma] + end +end + +class DndCharacterTest < Minitest::Test + include Helpers +<% json["cases"].each do |cases| %> + <% if cases.key?("cases")%> + + <% cases["cases"].each do |sub_case| %> + def test_<%= underscore(sub_case["description"]) %> + <%= skip? %> + <%- if sub_case["property"] == "modifier" -%> + assert_equal(<%= sub_case["expected"] %>, DndCharacter.modifier(<%= sub_case["input"]["score"] %>)) + <%- end -%> + end + <% end %> + <% end %> +<% end %> + # rubocop:disable Style/FormatString, Style/RedundantPercentQ + def test_random_character_stats + skip + 100.times do + character = DndCharacter.new + allowed_range = (3..18) + expected_hitpoints = BASE_HITPOINTS + + DndCharacter.modifier(character.constitution) + informative_message = %q(The character's %s must be within %s) + + attributes.each do |attribute| + assert_includes allowed_range, character.send(attribute), + informative_message % [attribute, allowed_range] + end + + informative_message = %q(The character's %s must be %s) + + assert_equal expected_hitpoints, character.hitpoints, + informative_message % ['hitpoints', expected_hitpoints] + end + end + + def test_stats_calculated_once + skip + informative_message = <<~EXPLAIN + The character's %s must not change if called more than once. + It was %s, is now %s. + EXPLAIN + + 100.times do + character = DndCharacter.new + + (attributes << :hitpoints).each do |attribute| + first = character.send(attribute) + second = character.send(attribute) + + assert_equal first, second, + informative_message % { attribute:, first:, second: } + end + end + # rubocop:enable Style/FormatString, Style/RedundantPercentQ + end +end \ No newline at end of file diff --git a/exercises/practice/dnd-character/dnd_character_test.rb b/exercises/practice/dnd-character/dnd_character_test.rb index 1506f2bd6f..3667a12974 100644 --- a/exercises/practice/dnd-character/dnd_character_test.rb +++ b/exercises/practice/dnd-character/dnd_character_test.rb @@ -12,84 +12,84 @@ def attributes class DndCharacterTest < Minitest::Test include Helpers - def test_modifier_score_3 + def test_ability_modifier_for_score_3_is__4 # skip assert_equal(-4, DndCharacter.modifier(3)) end - def test_modifier_score_4 + def test_ability_modifier_for_score_4_is__3 skip assert_equal(-3, DndCharacter.modifier(4)) end - def test_modifier_score_5 + def test_ability_modifier_for_score_5_is__3 skip assert_equal(-3, DndCharacter.modifier(5)) end - def test_modifier_score_6 + def test_ability_modifier_for_score_6_is__2 skip assert_equal(-2, DndCharacter.modifier(6)) end - def test_modifier_score_7 + def test_ability_modifier_for_score_7_is__2 skip assert_equal(-2, DndCharacter.modifier(7)) end - def test_modifier_score_8 + def test_ability_modifier_for_score_8_is__1 skip assert_equal(-1, DndCharacter.modifier(8)) end - def test_modifier_score_9 + def test_ability_modifier_for_score_9_is__1 skip assert_equal(-1, DndCharacter.modifier(9)) end - def test_modifier_score_10 + def test_ability_modifier_for_score_10_is_0 skip - assert_equal 0, DndCharacter.modifier(10) + assert_equal(0, DndCharacter.modifier(10)) end - def test_modifier_score_11 + def test_ability_modifier_for_score_11_is_0 skip - assert_equal 0, DndCharacter.modifier(11) + assert_equal(0, DndCharacter.modifier(11)) end - def test_modifier_score_12 + def test_ability_modifier_for_score_12_is_1 skip - assert_equal 1, DndCharacter.modifier(12) + assert_equal(1, DndCharacter.modifier(12)) end - def test_modifier_score_13 + def test_ability_modifier_for_score_13_is_1 skip - assert_equal 1, DndCharacter.modifier(13) + assert_equal(1, DndCharacter.modifier(13)) end - def test_modifier_score_14 + def test_ability_modifier_for_score_14_is_2 skip - assert_equal 2, DndCharacter.modifier(14) + assert_equal(2, DndCharacter.modifier(14)) end - def test_modifier_score_15 + def test_ability_modifier_for_score_15_is_2 skip - assert_equal 2, DndCharacter.modifier(15) + assert_equal(2, DndCharacter.modifier(15)) end - def test_modifier_score_16 + def test_ability_modifier_for_score_16_is_3 skip - assert_equal 3, DndCharacter.modifier(16) + assert_equal(3, DndCharacter.modifier(16)) end - def test_modifier_score_17 + def test_ability_modifier_for_score_17_is_3 skip - assert_equal 3, DndCharacter.modifier(17) + assert_equal(3, DndCharacter.modifier(17)) end - def test_modifier_score_18 + def test_ability_modifier_for_score_18_is_4 skip - assert_equal 4, DndCharacter.modifier(18) + assert_equal(4, DndCharacter.modifier(18)) end # rubocop:disable Style/FormatString, Style/RedundantPercentQ From c2d4d9560435abe40bd4d2fb8a5b092b155be2a5 Mon Sep 17 00:00:00 2001 From: meatball <69751659+meatball133@users.noreply.github.com> Date: Sun, 31 Aug 2025 23:07:07 +0200 Subject: [PATCH 2/6] Fix exercise naming in generator (#1770) (#1777) * 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 169890685e3437d176c3baa7edbf56dcc0bf6f57 Mon Sep 17 00:00:00 2001 From: meatball Date: Tue, 2 Sep 2025 19:58:26 +0200 Subject: [PATCH 3/6] Change to singel qoutes, change conditional statement --- generatorv2/lib/utils.rb | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/generatorv2/lib/utils.rb b/generatorv2/lib/utils.rb index a728848e8c..c180b29164 100644 --- a/generatorv2/lib/utils.rb +++ b/generatorv2/lib/utils.rb @@ -10,7 +10,7 @@ def toml(path = "./exercises/practice/#{@exercise}/.meta/tests.toml") uuid = TomlRB.load_file(path) uuid = uuid.filter do |_k, v| - v.none? { |k, inner_value| k == "include" && !inner_value } + v.none? { |k, inner_value| k == 'include' && !inner_value } end uuid.keys end @@ -39,18 +39,18 @@ def additional_json(json) file_path = "./exercises/practice/#{@exercise}/.meta/additional_tests.json" return unless File.exist?(file_path) - JSON.parse(File.read(file_path))["cases"].each do |test| - json["cases"] << test + JSON.parse(File.read(file_path))['cases'].each do |test| + json['cases'] << test end end def remove_tests(uuid, json) - json["cases"].each_with_object([]) do |x, acc| - if x["cases"] - acc << { "cases" => remove_tests(uuid, x), "description" => x["description"] } - elsif uuid.include?(x["uuid"]) - acc << x - end + json['cases'].each_with_object([]) do |x, acc| + acc << if x['cases'] + { 'cases' => remove_tests(uuid, x), 'description' => x['description'] } + elsif uuid.include?(x['uuid']) + x + end end end end From 964c3083cc0b215b81522a0ced3ae9f7229011aa Mon Sep 17 00:00:00 2001 From: meatball Date: Tue, 2 Sep 2025 19:59:21 +0200 Subject: [PATCH 4/6] add eol --- exercises/practice/circular-buffer/.meta/test_template.erb | 2 +- exercises/practice/crypto-square/.meta/test_template.erb | 2 +- exercises/practice/custom-set/.meta/test_template.erb | 2 +- exercises/practice/darts/.meta/test_template.erb | 2 +- exercises/practice/diamond/.meta/test_template.erb | 2 +- .../practice/difference-of-squares/.meta/test_template.erb | 2 +- exercises/practice/dnd-character/.meta/test_template.erb | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/exercises/practice/circular-buffer/.meta/test_template.erb b/exercises/practice/circular-buffer/.meta/test_template.erb index 3be26a5f29..dcf255bc44 100644 --- a/exercises/practice/circular-buffer/.meta/test_template.erb +++ b/exercises/practice/circular-buffer/.meta/test_template.erb @@ -27,4 +27,4 @@ class CircularBufferTest < Minitest::Test <%- end -%> end <% end %> -end \ No newline at end of file +end diff --git a/exercises/practice/crypto-square/.meta/test_template.erb b/exercises/practice/crypto-square/.meta/test_template.erb index 7ab2747dbd..3070e04fdc 100644 --- a/exercises/practice/crypto-square/.meta/test_template.erb +++ b/exercises/practice/crypto-square/.meta/test_template.erb @@ -9,4 +9,4 @@ class CryptoSquareTest < Minitest::Test assert_equal '<%= cases["expected"] %>', Crypto.new(plaintext).ciphertext end <% end %> -end \ No newline at end of file +end diff --git a/exercises/practice/custom-set/.meta/test_template.erb b/exercises/practice/custom-set/.meta/test_template.erb index 872eb2f016..75c33d6ed1 100644 --- a/exercises/practice/custom-set/.meta/test_template.erb +++ b/exercises/practice/custom-set/.meta/test_template.erb @@ -40,4 +40,4 @@ class CustomSetTest < Minitest::Test end <% end %> <% end %> -end \ No newline at end of file +end diff --git a/exercises/practice/darts/.meta/test_template.erb b/exercises/practice/darts/.meta/test_template.erb index a5ad83d9fe..3f915148fa 100644 --- a/exercises/practice/darts/.meta/test_template.erb +++ b/exercises/practice/darts/.meta/test_template.erb @@ -9,4 +9,4 @@ class DartsTest < Minitest::Test assert_equal(<%= cases["expected"] %>, darts.score) end <% end %> -end \ No newline at end of file +end diff --git a/exercises/practice/diamond/.meta/test_template.erb b/exercises/practice/diamond/.meta/test_template.erb index ff29ea8d18..4d8458e773 100644 --- a/exercises/practice/diamond/.meta/test_template.erb +++ b/exercises/practice/diamond/.meta/test_template.erb @@ -12,4 +12,4 @@ class DiamondTest < Minitest::Test assert_equal string, answer end <% end %> -end \ No newline at end of file +end diff --git a/exercises/practice/difference-of-squares/.meta/test_template.erb b/exercises/practice/difference-of-squares/.meta/test_template.erb index 85e8865cfa..53880e8ce9 100644 --- a/exercises/practice/difference-of-squares/.meta/test_template.erb +++ b/exercises/practice/difference-of-squares/.meta/test_template.erb @@ -10,4 +10,4 @@ class DifferenceOfSquaresTest < Minitest::Test end <% end %> <% end %> -end \ No newline at end of file +end diff --git a/exercises/practice/dnd-character/.meta/test_template.erb b/exercises/practice/dnd-character/.meta/test_template.erb index e841b1d8de..2fc7b96355 100644 --- a/exercises/practice/dnd-character/.meta/test_template.erb +++ b/exercises/practice/dnd-character/.meta/test_template.erb @@ -66,4 +66,4 @@ class DndCharacterTest < Minitest::Test end # rubocop:enable Style/FormatString, Style/RedundantPercentQ end -end \ No newline at end of file +end From 1b71ff2d75a71cdad1dc0352f97431fa83eb18db Mon Sep 17 00:00:00 2001 From: meatball Date: Tue, 2 Sep 2025 20:26:13 +0200 Subject: [PATCH 5/6] Rollback changes due to errors --- generatorv2/lib/utils.rb | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/generatorv2/lib/utils.rb b/generatorv2/lib/utils.rb index c180b29164..86bd6a0ed3 100644 --- a/generatorv2/lib/utils.rb +++ b/generatorv2/lib/utils.rb @@ -46,11 +46,11 @@ def additional_json(json) def remove_tests(uuid, json) json['cases'].each_with_object([]) do |x, acc| - acc << if x['cases'] - { 'cases' => remove_tests(uuid, x), 'description' => x['description'] } - elsif uuid.include?(x['uuid']) - x - end + if x['cases'] + acc << { 'cases' => remove_tests(uuid, x), 'description' => x['description'] } + elsif uuid.include?(x['uuid']) + acc << x + end end end end From 6007953c8690b6cf517c0f5463bde461abb0222f Mon Sep 17 00:00:00 2001 From: meatball Date: Sun, 7 Sep 2025 12:48:48 +0200 Subject: [PATCH 6/6] Remove trailling spaces --- exercises/practice/circular-buffer/.meta/test_template.erb | 2 +- exercises/practice/crypto-square/.meta/test_template.erb | 2 +- exercises/practice/darts/.meta/test_template.erb | 2 +- exercises/practice/diamond/.meta/test_template.erb | 2 +- .../practice/difference-of-squares/.meta/test_template.erb | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/exercises/practice/circular-buffer/.meta/test_template.erb b/exercises/practice/circular-buffer/.meta/test_template.erb index dcf255bc44..dedd77d4b6 100644 --- a/exercises/practice/circular-buffer/.meta/test_template.erb +++ b/exercises/practice/circular-buffer/.meta/test_template.erb @@ -4,7 +4,7 @@ require_relative 'circular_buffer' class CircularBufferTest < Minitest::Test <% json["cases"].each do |cases| %> def test_<%= underscore(cases["description"]) %> - <%= skip? %> + <%= skip? %> buffer = CircularBuffer.new(<%= cases["input"]["capacity"] %>) <%- cases["input"]["operations"].each do |operation| -%> <%- if operation["operation"] == "read"-%> diff --git a/exercises/practice/crypto-square/.meta/test_template.erb b/exercises/practice/crypto-square/.meta/test_template.erb index 3070e04fdc..101b09556b 100644 --- a/exercises/practice/crypto-square/.meta/test_template.erb +++ b/exercises/practice/crypto-square/.meta/test_template.erb @@ -4,7 +4,7 @@ require_relative 'crypto_square' class CryptoSquareTest < Minitest::Test <% json["cases"].each do |cases| %> def test_<%= underscore(cases["description"]) %> - <%= skip? %> + <%= skip? %> plaintext = '<%= cases["input"]["plaintext"] %>' assert_equal '<%= cases["expected"] %>', Crypto.new(plaintext).ciphertext end diff --git a/exercises/practice/darts/.meta/test_template.erb b/exercises/practice/darts/.meta/test_template.erb index 3f915148fa..abb30703af 100644 --- a/exercises/practice/darts/.meta/test_template.erb +++ b/exercises/practice/darts/.meta/test_template.erb @@ -4,7 +4,7 @@ require_relative 'darts' class DartsTest < Minitest::Test <% json["cases"].each do |cases| %> def test_<%= underscore(cases["description"]) %> - <%= skip? %> + <%= skip? %> darts = Darts.new(<%= cases["input"]["x"] %>, <%= cases["input"]["y"] %>) assert_equal(<%= cases["expected"] %>, darts.score) end diff --git a/exercises/practice/diamond/.meta/test_template.erb b/exercises/practice/diamond/.meta/test_template.erb index 4d8458e773..c1db095d5d 100644 --- a/exercises/practice/diamond/.meta/test_template.erb +++ b/exercises/practice/diamond/.meta/test_template.erb @@ -4,7 +4,7 @@ require_relative 'diamond' class DiamondTest < Minitest::Test <% json["cases"].each do |cases| %> def test_<%= underscore(cases["description"]) %> - <%= skip? %> + <%= skip? %> answer = Diamond.make_diamond('<%= cases["input"]["letter"] %>') string =<% cases["expected"].each do |row| -%> "<%= row %>\n"\ diff --git a/exercises/practice/difference-of-squares/.meta/test_template.erb b/exercises/practice/difference-of-squares/.meta/test_template.erb index 53880e8ce9..38bf92ca72 100644 --- a/exercises/practice/difference-of-squares/.meta/test_template.erb +++ b/exercises/practice/difference-of-squares/.meta/test_template.erb @@ -5,7 +5,7 @@ class DifferenceOfSquaresTest < Minitest::Test <% json["cases"].each do |cases| %> <% cases["cases"].each do |sub_case| %> def test_<%= underscore(sub_case["description"]) %> - <%= skip? %> + <%= skip? %> assert_equal <%= sub_case["expected"] %>, Squares.new(<%= sub_case["input"]["number"] %>).<%= sub_case["property"] == "differenceOfSquares" ? "difference" : sub_case["property"].gsub(/([A-Z])/, '_\1').downcase.sub(/^_/, '') %> end <% end %>