From e9d07a752a51dd5d05a06954d6675147e7e60eab Mon Sep 17 00:00:00 2001 From: Tommy Schaefer Date: Mon, 19 Dec 2016 21:41:43 -0600 Subject: [PATCH 1/3] Generate tests for "all your base" --- exercises/all-your-base/.meta/.version | 1 + exercises/all-your-base/all_your_base_test.rb | 110 ++++++++---------- exercises/all-your-base/example.rb | 11 +- exercises/all-your-base/example.tt | 17 +++ lib/all_your_base_cases.rb | 108 +++++++++++++++++ 5 files changed, 184 insertions(+), 63 deletions(-) create mode 100644 exercises/all-your-base/.meta/.version create mode 100644 exercises/all-your-base/example.tt create mode 100644 lib/all_your_base_cases.rb diff --git a/exercises/all-your-base/.meta/.version b/exercises/all-your-base/.meta/.version new file mode 100644 index 0000000000..d8263ee986 --- /dev/null +++ b/exercises/all-your-base/.meta/.version @@ -0,0 +1 @@ +2 \ No newline at end of file diff --git a/exercises/all-your-base/all_your_base_test.rb b/exercises/all-your-base/all_your_base_test.rb index 9f9b78ed2c..f88bc77bee 100755 --- a/exercises/all-your-base/all_your_base_test.rb +++ b/exercises/all-your-base/all_your_base_test.rb @@ -1,9 +1,10 @@ -gem 'minitest', '>= 5.0.0' require 'minitest/autorun' require_relative 'all_your_base' +# Test data version: aa12f2e class AllYourBaseTest < Minitest::Test - def test_single_bit_to_one_decimal + def test_single_bit_one_to_decimal + # skip digits = [1] input_base = 2 output_base = 10 @@ -131,7 +132,7 @@ def test_empty_list def test_single_zero skip digits = [0] - input_base = 10 + input_base = 10 output_base = 2 expected = [0] @@ -142,10 +143,10 @@ def test_single_zero "Expected #{expected} but got #{converted}." end - def test_multiple_zeroes + def test_multiple_zeros skip digits = [0, 0, 0] - input_base = 10 + input_base = 10 output_base = 2 expected = [0] @@ -175,13 +176,10 @@ def test_negative_digit digits = [1, -1, 1, 0, 1, 0] input_base = 2 output_base = 10 - expected = nil - - converted = BaseConverter.convert(input_base, digits, output_base) - assert_equal expected, converted, - "Input base: #{input_base}, output base #{output_base}. " \ - "Expected #{expected} but got #{converted}." + assert_raises ArgumentError do + BaseConverter.convert(input_base, digits, output_base) + end end def test_invalid_positive_digit @@ -189,13 +187,10 @@ def test_invalid_positive_digit digits = [1, 2, 1, 0, 1, 0] input_base = 2 output_base = 10 - expected = nil - - converted = BaseConverter.convert(input_base, digits, output_base) - assert_equal expected, converted, - "Input base: #{input_base}, output base #{output_base}. " \ - "Expected #{expected} but got #{converted}." + assert_raises ArgumentError do + BaseConverter.convert(input_base, digits, output_base) + end end def test_first_base_is_one @@ -203,13 +198,10 @@ def test_first_base_is_one digits = [] input_base = 1 output_base = 10 - expected = nil - converted = BaseConverter.convert(input_base, digits, output_base) - - assert_equal expected, converted, - "Input base: #{input_base}, output base #{output_base}. " \ - "Expected #{expected} but got #{converted}." + assert_raises ArgumentError do + BaseConverter.convert(input_base, digits, output_base) + end end def test_second_base_is_one @@ -217,13 +209,10 @@ def test_second_base_is_one digits = [1, 0, 1, 0, 1, 0] input_base = 2 output_base = 1 - expected = nil - - converted = BaseConverter.convert(input_base, digits, output_base) - assert_equal expected, converted, - "Input base: #{input_base}, output base #{output_base}. " \ - "Expected #{expected} but got #{converted}." + assert_raises ArgumentError do + BaseConverter.convert(input_base, digits, output_base) + end end def test_first_base_is_zero @@ -231,13 +220,10 @@ def test_first_base_is_zero digits = [] input_base = 0 output_base = 10 - expected = nil - - converted = BaseConverter.convert(input_base, digits, output_base) - assert_equal expected, converted, - "Input base: #{input_base}, output base #{output_base}. " \ - "Expected #{expected} but got #{converted}." + assert_raises ArgumentError do + BaseConverter.convert(input_base, digits, output_base) + end end def test_second_base_is_zero @@ -245,13 +231,10 @@ def test_second_base_is_zero digits = [7] input_base = 10 output_base = 0 - expected = nil - converted = BaseConverter.convert(input_base, digits, output_base) - - assert_equal expected, converted, - "Input base: #{input_base}, output base #{output_base}. " \ - "Expected #{expected} but got #{converted}." + assert_raises ArgumentError do + BaseConverter.convert(input_base, digits, output_base) + end end def test_first_base_is_negative @@ -259,13 +242,10 @@ def test_first_base_is_negative digits = [1] input_base = -2 output_base = 10 - expected = nil - - converted = BaseConverter.convert(input_base, digits, output_base) - assert_equal expected, converted, - "Input base: #{input_base}, output base #{output_base}. " \ - "Expected #{expected} but got #{converted}." + assert_raises ArgumentError do + BaseConverter.convert(input_base, digits, output_base) + end end def test_second_base_is_negative @@ -273,13 +253,10 @@ def test_second_base_is_negative digits = [1] input_base = 2 output_base = -7 - expected = nil - - converted = BaseConverter.convert(input_base, digits, output_base) - assert_equal expected, converted, - "Input base: #{input_base}, output base #{output_base}. " \ - "Expected #{expected} but got #{converted}." + assert_raises ArgumentError do + BaseConverter.convert(input_base, digits, output_base) + end end def test_both_bases_are_negative @@ -287,17 +264,30 @@ def test_both_bases_are_negative digits = [1] input_base = -2 output_base = -7 - expected = nil - converted = BaseConverter.convert(input_base, digits, output_base) - - assert_equal expected, converted, - "Input base: #{input_base}, output base #{output_base}. " \ - "Expected #{expected} but got #{converted}." + assert_raises ArgumentError do + BaseConverter.convert(input_base, digits, output_base) + end end + # Problems in exercism evolve over time, as we find better ways to ask + # questions. + # The version number refers to the version of the problem you solved, + # not your solution. + # + # Define a constant named VERSION inside of the top level BookKeeping + # module, which may be placed near the end of your file. + # + # In your file, it will look like this: + # + # module BookKeeping + # VERSION = 1 # Where the version number matches the one in the test. + # end + # + # If you are curious, read more about constants on RubyDoc: + # http://ruby-doc.org/docs/ruby-doc-bundle/UsersGuide/rg/constants.html def test_bookkeeping skip - assert_equal 1, BookKeeping::VERSION + assert_equal 2, BookKeeping::VERSION end end diff --git a/exercises/all-your-base/example.rb b/exercises/all-your-base/example.rb index ccfd660e4e..56fc99c341 100644 --- a/exercises/all-your-base/example.rb +++ b/exercises/all-your-base/example.rb @@ -1,17 +1,22 @@ module BookKeeping - VERSION = 1 + VERSION = 2 end class BaseConverter def self.convert(base_from, number_array, base_to) - return if number_array.any?{|number| number < 0 || number >= base_from} - return if base_from <= 1 || base_to <= 1 + fail ArgumentError if invalid_inputs?(base_from, number_array, base_to) return [] unless number_array.any? number_in_canonical_base = convert_to_canonical_base(number_array, base_from) convert_from_canonical_base(number_in_canonical_base, base_to) end private + + def self.invalid_inputs?(base_from, number_array, base_to) + number_array.any?{ |number| number < 0 || number >= base_from } || + base_from <= 1 || base_to <= 1 + end + def self.convert_to_canonical_base(number_array, base) total = 0 number_array.reverse.each_with_index do |number, index| diff --git a/exercises/all-your-base/example.tt b/exercises/all-your-base/example.tt new file mode 100644 index 0000000000..064b38a372 --- /dev/null +++ b/exercises/all-your-base/example.tt @@ -0,0 +1,17 @@ +require 'minitest/autorun' +require_relative 'all_your_base' + +# Test data version: <%= sha1 %> +class AllYourBaseTest < Minitest::Test<% test_cases.each do |test_case| %> + def <%= test_case.test_name %> + <%= test_case.skipped %> +<%= test_case.workload %> + end +<% end %> + +<%= IO.read(XRUBY_LIB + '/bookkeeping.md') %> + def test_bookkeeping + skip + assert_equal <%= version.next %>, BookKeeping::VERSION + end +end diff --git a/lib/all_your_base_cases.rb b/lib/all_your_base_cases.rb new file mode 100644 index 0000000000..8640fca1e4 --- /dev/null +++ b/lib/all_your_base_cases.rb @@ -0,0 +1,108 @@ +class AllYourBaseCase < OpenStruct + def test_name + 'test_%s' % description.downcase.tr(' -', '_') + end + + def workload + indent(4, (assignments + assertion).join("\n")) + "\n" + end + + def skipped + index.zero? ? '# skip' : 'skip' + end + + private + + def indent(size, text) + text.lines.each_with_object('') do |line, obj| + obj << (line == "\n" ? line : ' ' * size + line) + end + end + + def assignments + [ + "digits = #{input_digits}", + "input_base = #{input_base}", + "output_base = #{output_base}", + ] + end + + def assertion + return error_assertion unless expected + + [ + "expected = #{expected}", + "", + "converted = BaseConverter.convert(input_base, digits, output_base)", + "", + "assert_equal expected, converted,", + indent(13, error_message), + ] + end + + def error_assertion + [ + "", + "assert_raises ArgumentError do", + " BaseConverter.convert(input_base, digits, output_base)", + "end", + ] + end + + def error_message + %q("Input base: #{input_base}, output base #{output_base}. " \\) \ + "\n" + %q("Expected #{expected} but got #{converted}.") + end +end + +class AllYourBaseCase::PreProcessor + class << self + def call(row) + @row = row + + row.merge('expected' => expected_value) + end + + private + + attr_reader :row + + def expected_value + return row['expected'] if row['expected'] + + if invalid_input_digits? || invalid_bases? + nil + elsif row['input_digits'].empty? + [] + elsif input_of_zero? + [0] + else + handle_special_cases + end + end + + def invalid_input_digits? + row['input_digits'].any? { |x| x < 0 || x >= row['input_base'] } + end + + def invalid_bases? + row['input_base'] <= 1 || row['output_base'] <= 1 + end + + def input_of_zero? + row['input_digits'].all? { |x| x == 0 } + end + + def handle_special_cases + [4,2] if row['input_digits'] == [0, 6, 0] + end + end +end + +AllYourBaseCases = proc do |data| + JSON.parse(data)['cases'].map.with_index do |row, i| + AllYourBaseCase.new( + AllYourBaseCase::PreProcessor.call(row).merge(index: i), + ) + end +end From a23949e9019ded2de85d04eedabb0a356e11f62e Mon Sep 17 00:00:00 2001 From: Tommy Schaefer Date: Fri, 30 Dec 2016 13:50:00 -0600 Subject: [PATCH 2/3] Move private row attribute to avoid ruby warning This change is to remove a warning present in ruby versions less than 2.3.0. A thread discussing this warning can be found at https://bugs.ruby-lang.org/issues/10967. It is removed in ruby/ruby@32a5a098e39e377350e4696d2d9f2d8930f1ec41. --- lib/all_your_base_cases.rb | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/all_your_base_cases.rb b/lib/all_your_base_cases.rb index 8640fca1e4..1d564510e9 100644 --- a/lib/all_your_base_cases.rb +++ b/lib/all_your_base_cases.rb @@ -57,16 +57,17 @@ def error_message class AllYourBaseCase::PreProcessor class << self + attr_reader :row + def call(row) @row = row row.merge('expected' => expected_value) end + private :row private - attr_reader :row - def expected_value return row['expected'] if row['expected'] From 0e96040852b3c68d3d51d63f2e34256077a6c71d Mon Sep 17 00:00:00 2001 From: kotp Date: Mon, 2 Jan 2017 04:46:59 -0500 Subject: [PATCH 3/3] A bit more idiomatic Ruby Also, the private call was ineffective. --- exercises/all-your-base/example.rb | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/exercises/all-your-base/example.rb b/exercises/all-your-base/example.rb index 56fc99c341..2a035e0c1b 100644 --- a/exercises/all-your-base/example.rb +++ b/exercises/all-your-base/example.rb @@ -10,17 +10,17 @@ def self.convert(base_from, number_array, base_to) convert_from_canonical_base(number_in_canonical_base, base_to) end - private + private_class_method def self.invalid_inputs?(base_from, number_array, base_to) - number_array.any?{ |number| number < 0 || number >= base_from } || + number_array.any? { |number| number < 0 || number >= base_from } || base_from <= 1 || base_to <= 1 end def self.convert_to_canonical_base(number_array, base) total = 0 number_array.reverse.each_with_index do |number, index| - total += number * base ** index + total += number * base**index end total end @@ -28,9 +28,9 @@ def self.convert_to_canonical_base(number_array, base) def self.convert_from_canonical_base(number, base_to) result = [] current_number = number - while current_number >= base_to do - result << current_number % base_to - current_number = current_number / base_to + while current_number >= base_to + result << current_number % base_to + current_number /= base_to end result << current_number % base_to result.reverse