From 84e6d3801ae51755015ab9ae4b9c168f3159b44d Mon Sep 17 00:00:00 2001 From: Favio Manriquez Leon Date: Wed, 26 Oct 2016 13:09:49 -0700 Subject: [PATCH 1/4] Add triangle test generator --- exercises/triangle/.version | 1 + exercises/triangle/example.rb | 38 ++++----- exercises/triangle/example.tt | 22 +++++ exercises/triangle/triangle_test.rb | 119 ++++++++++++++++++---------- lib/triangle_cases.rb | 41 ++++++++++ 5 files changed, 160 insertions(+), 61 deletions(-) create mode 100644 exercises/triangle/.version create mode 100644 exercises/triangle/example.tt create mode 100644 lib/triangle_cases.rb diff --git a/exercises/triangle/.version b/exercises/triangle/.version new file mode 100644 index 0000000000..56a6051ca2 --- /dev/null +++ b/exercises/triangle/.version @@ -0,0 +1 @@ +1 \ No newline at end of file diff --git a/exercises/triangle/example.rb b/exercises/triangle/example.rb index c14012360b..0c59141c13 100644 --- a/exercises/triangle/example.rb +++ b/exercises/triangle/example.rb @@ -1,44 +1,40 @@ +module BookKeeping + VERSION = 1 +end + class TriangleError < RuntimeError end class Triangle - attr_reader :a, :b, :c - def initialize(a, b, c) - @a = a - @b = b - @c = c - end + attr_reader :sides - def kind - fail TriangleError if illegal? - if equilateral? - :equilateral - elsif isosceles? - :isosceles - else - :scalene + def initialize(sides) + @sides = sides + if illegal? + @sides = [] end end - private - - def sides - @sides ||= [a, b, c] - end - def equilateral? sides.uniq.size == 1 end def isosceles? - sides.uniq.size == 2 + sides.uniq.size.between?(1, 2) + end + + def scalene? + sides.uniq.size == 3 end + private + def illegal? impossible_length_side? || violates_inequality? end def violates_inequality? + a, b, c = sides a + b <= c || a + c <= b || b + c <= a end diff --git a/exercises/triangle/example.tt b/exercises/triangle/example.tt new file mode 100644 index 0000000000..0b2d2ce82c --- /dev/null +++ b/exercises/triangle/example.tt @@ -0,0 +1,22 @@ +#!/usr/bin/env ruby +gem 'minitest', '>= 5.0.0' +require 'minitest/autorun' +require_relative 'triangle' + +# Test data version: +# <%= sha1 %> +class TriangleTest < Minitest::Test<% test_cases.each do |test_case| %> + def <%= test_case.name %><% if test_case.skipped? %> + skip<% end %> + triangle = Triangle.new(<%= test_case.sides %>)<% if test_case.expected%> + assert <%= test_case.do %>, "<%= test_case.failure_message%>"<% else %> + refute <%= test_case.do %>, "<%= test_case.failure_message%>"<% end%> + end +<% end %> +<%= IO.read(XRUBY_LIB + '/bookkeeping.md') %> + def test_bookkeeping + skip + assert_equal <%= version.next %>, BookKeeping::VERSION + end +end + diff --git a/exercises/triangle/triangle_test.rb b/exercises/triangle/triangle_test.rb index 41ce62ec24..e367eacf6c 100755 --- a/exercises/triangle/triangle_test.rb +++ b/exercises/triangle/triangle_test.rb @@ -3,91 +3,130 @@ require 'minitest/autorun' require_relative 'triangle' +# Test data version: +# b806246 class TriangleTest < Minitest::Test - def test_equilateral_triangles_have_equal_sides - assert_equal :equilateral, Triangle.new(2, 2, 2).kind + def test_triangle_is_equilateral_if_all_sides_are_equal + triangle = Triangle.new([2, 2, 2]) + assert triangle.equilateral?, "Expected 'true', triangle is equilateral." end - def test_larger_equilateral_triangles_also_have_equal_sides + def test_triangle_is_not_equilateral_if_any_side_is_unequal skip - assert_equal :equilateral, Triangle.new(10, 10, 10).kind + triangle = Triangle.new([2, 3, 2]) + refute triangle.equilateral?, "Expected 'false', triangle is not equilateral." end - def test_isosceles_triangles_have_last_two_sides_equal + def test_triangle_is_not_equilateral_if_no_sides_are_equal skip - assert_equal :isosceles, Triangle.new(3, 4, 4).kind + triangle = Triangle.new([5, 4, 6]) + refute triangle.equilateral?, "Expected 'false', triangle is not equilateral." end - def test_isosceles_triangles_have_first_and_last_sides_equal + def test_all_zero_sides_are_illegal__so_the_triangle_is_not_equilateral skip - assert_equal :isosceles, Triangle.new(4, 3, 4).kind + triangle = Triangle.new([0, 0, 0]) + refute triangle.equilateral?, "Expected 'false', triangle is not equilateral." end - def test_isosceles_triangles_have_two_first_sides_equal + def test_equilateral_triangle_sides_may_be_floats skip - assert_equal :isosceles, Triangle.new(4, 4, 3).kind + triangle = Triangle.new([0.5, 0.5, 0.5]) + assert triangle.equilateral?, "Expected 'true', triangle is equilateral." end - def test_isosceles_triangles_have_in_fact_exactly_two_sides_equal + def test_triangle_is_isosceles_if_last_two_sides_are_equal skip - assert_equal :isosceles, Triangle.new(10, 10, 2).kind + triangle = Triangle.new([3, 4, 4]) + assert triangle.isosceles?, "Expected 'true', triangle is isosceles." end - def test_isosceles_triangles_have_unequal_side_larger_than_equal_sides + def test_triangle_is_isosceles_if_first_two_sides_are_equal skip - assert_equal :isosceles, Triangle.new(4, 7, 4).kind + triangle = Triangle.new([4, 4, 3]) + assert triangle.isosceles?, "Expected 'true', triangle is isosceles." end - def test_scalene_triangles_have_no_equal_sides + def test_triangle_is_isosceles_if_first_and_last_sides_are_equal skip - assert_equal :scalene, Triangle.new(3, 4, 5).kind + triangle = Triangle.new([4, 3, 4]) + assert triangle.isosceles?, "Expected 'true', triangle is isosceles." end - def test_2a_equals_b_plus_c_looks_like_equilateral_but_is_not + def test_equilateral_triangles_are_also_isosceles skip - assert_equal :scalene, Triangle.new(5, 4, 6).kind + triangle = Triangle.new([4, 4, 4]) + assert triangle.isosceles?, "Expected 'true', triangle is isosceles." end - def test_scalene_triangles_have_no_equal_sides_at_a_larger_scale_too + def test_triangle_is_not_isosceles_if_no_sides_are_equal skip - assert_equal :scalene, Triangle.new(10, 11, 12).kind + triangle = Triangle.new([2, 3, 4]) + refute triangle.isosceles?, "Expected 'false', triangle is not isosceles." end - def test_scalene_triangles_have_no_equal_sides_in_descending_order_either + def test_sides_that_violate_triangle_inequality_are_not_isosceles__even_if_two_are_equal skip - assert_equal :scalene, Triangle.new(5, 4, 2).kind + triangle = Triangle.new([1, 1, 3]) + refute triangle.isosceles?, "Expected 'false', triangle is not isosceles." end - def test_very_small_triangles_are_legal + def test_isosceles_triangle_sides_may_be_floats skip - assert_equal :scalene, Triangle.new(0.4, 0.6, 0.3).kind + triangle = Triangle.new([0.5, 0.4, 0.5]) + assert triangle.isosceles?, "Expected 'true', triangle is isosceles." end - def test_triangles_with_no_size_are_illegal + def test_triangle_is_scalene_if_no_sides_are_equal skip - assert_raises(TriangleError) do - Triangle.new(0, 0, 0).kind - end + triangle = Triangle.new([5, 4, 6]) + assert triangle.scalene?, "Expected 'true', triangle is scalene." end - def test_triangles_violating_triangle_inequality_are_illegal + def test_triangle_is_not_scalene_if_all_sides_are_equal skip - assert_raises(TriangleError) do - Triangle.new(1, 1, 3).kind - end + triangle = Triangle.new([4, 4, 4]) + refute triangle.scalene?, "Expected 'false', triangle is not scalene." end - def test_triangles_violating_triangle_inequality_are_illegal_2 + def test_triangle_is_not_scalene_if_two_sides_are_equal skip - assert_raises(TriangleError) do - Triangle.new(7, 3, 2).kind - end + triangle = Triangle.new([4, 4, 3]) + refute triangle.scalene?, "Expected 'false', triangle is not scalene." end - def test_triangles_violating_triangle_inequality_are_illegal_3 + def test_sides_that_violate_triangle_inequality_are_not_scalene__even_if_they_are_all_different skip - assert_raises(TriangleError) do - Triangle.new(1, 3, 1).kind - end + triangle = Triangle.new([7, 3, 2]) + refute triangle.scalene?, "Expected 'false', triangle is not scalene." + end + + def test_scalene_triangle_sides_may_be_floats + skip + triangle = Triangle.new([0.5, 0.4, 0.6]) + assert triangle.scalene?, "Expected 'true', triangle is scalene." + 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 end end + diff --git a/lib/triangle_cases.rb b/lib/triangle_cases.rb new file mode 100644 index 0000000000..b0c8c7f459 --- /dev/null +++ b/lib/triangle_cases.rb @@ -0,0 +1,41 @@ +class TriangleCase < OpenStruct + def name + initial = description.downcase + replaced = initial.gsub(/(true|false)/, replacements) + if initial.eql?(replaced) && !initial.include?(triangle) + replaced = triangle + ' triangle ' + initial + end + 'test_%s' % replaced.gsub(/[ ,-]/, '_') + end + + def replacements + booleans = { 'true' => '', 'false' => ' not' } + booleans.each { |k, v| booleans[k] = "triangle is#{v} #{triangle}" } + end + + def do + "triangle.#{triangle}?" + end + + def skipped? + index > 0 + end + + def failure_message + "Expected '#{expected}', triangle is #{expected ? '' : 'not '}#{triangle}." + end +end + +TriangleCases = proc do |data| + i = 0 + cases = [] + data = JSON.parse(data).select { |key, value| key.to_s.match(/[^#]+/) } + data.keys.each do |triangle| + data[triangle]['cases'].each do |row| + row = row.merge(row.merge('index' => i, 'triangle' => triangle)) + cases << TriangleCase.new(row) + i += 1 + end + end + cases +end From fadf0ad00f70e7469ff7f88f93529b39db720caf Mon Sep 17 00:00:00 2001 From: Favio Manriquez Leon Date: Thu, 27 Oct 2016 01:00:51 -0700 Subject: [PATCH 2/4] Update triangle test generator * Conform to the test generator common public interface * Fix double underscores in method names * Remove unused TriangleError class --- exercises/triangle/example.rb | 3 --- exercises/triangle/example.tt | 8 +++----- exercises/triangle/triangle_test.rb | 7 ++++--- lib/triangle_cases.rb | 20 +++++++++++++------- 4 files changed, 20 insertions(+), 18 deletions(-) diff --git a/exercises/triangle/example.rb b/exercises/triangle/example.rb index 0c59141c13..65cf463f08 100644 --- a/exercises/triangle/example.rb +++ b/exercises/triangle/example.rb @@ -2,9 +2,6 @@ module BookKeeping VERSION = 1 end -class TriangleError < RuntimeError -end - class Triangle attr_reader :sides diff --git a/exercises/triangle/example.tt b/exercises/triangle/example.tt index 0b2d2ce82c..7a4cac18a9 100644 --- a/exercises/triangle/example.tt +++ b/exercises/triangle/example.tt @@ -6,11 +6,9 @@ require_relative 'triangle' # Test data version: # <%= sha1 %> class TriangleTest < Minitest::Test<% test_cases.each do |test_case| %> - def <%= test_case.name %><% if test_case.skipped? %> - skip<% end %> - triangle = Triangle.new(<%= test_case.sides %>)<% if test_case.expected%> - assert <%= test_case.do %>, "<%= test_case.failure_message%>"<% else %> - refute <%= test_case.do %>, "<%= test_case.failure_message%>"<% end%> + def <%= test_case.test_name %> + <%= test_case.skipped %> + <%= test_case.workload %> end <% end %> <%= IO.read(XRUBY_LIB + '/bookkeeping.md') %> diff --git a/exercises/triangle/triangle_test.rb b/exercises/triangle/triangle_test.rb index e367eacf6c..b5277fe085 100755 --- a/exercises/triangle/triangle_test.rb +++ b/exercises/triangle/triangle_test.rb @@ -7,6 +7,7 @@ # b806246 class TriangleTest < Minitest::Test def test_triangle_is_equilateral_if_all_sides_are_equal + # skip triangle = Triangle.new([2, 2, 2]) assert triangle.equilateral?, "Expected 'true', triangle is equilateral." end @@ -23,7 +24,7 @@ def test_triangle_is_not_equilateral_if_no_sides_are_equal refute triangle.equilateral?, "Expected 'false', triangle is not equilateral." end - def test_all_zero_sides_are_illegal__so_the_triangle_is_not_equilateral + def test_all_zero_sides_are_illegal_so_the_triangle_is_not_equilateral skip triangle = Triangle.new([0, 0, 0]) refute triangle.equilateral?, "Expected 'false', triangle is not equilateral." @@ -65,7 +66,7 @@ def test_triangle_is_not_isosceles_if_no_sides_are_equal refute triangle.isosceles?, "Expected 'false', triangle is not isosceles." end - def test_sides_that_violate_triangle_inequality_are_not_isosceles__even_if_two_are_equal + def test_sides_that_violate_triangle_inequality_are_not_isosceles_even_if_two_are_equal skip triangle = Triangle.new([1, 1, 3]) refute triangle.isosceles?, "Expected 'false', triangle is not isosceles." @@ -95,7 +96,7 @@ def test_triangle_is_not_scalene_if_two_sides_are_equal refute triangle.scalene?, "Expected 'false', triangle is not scalene." end - def test_sides_that_violate_triangle_inequality_are_not_scalene__even_if_they_are_all_different + def test_sides_that_violate_triangle_inequality_are_not_scalene_even_if_they_are_all_different skip triangle = Triangle.new([7, 3, 2]) refute triangle.scalene?, "Expected 'false', triangle is not scalene." diff --git a/lib/triangle_cases.rb b/lib/triangle_cases.rb index b0c8c7f459..6061c24cd5 100644 --- a/lib/triangle_cases.rb +++ b/lib/triangle_cases.rb @@ -1,11 +1,11 @@ class TriangleCase < OpenStruct - def name + def test_name initial = description.downcase replaced = initial.gsub(/(true|false)/, replacements) if initial.eql?(replaced) && !initial.include?(triangle) replaced = triangle + ' triangle ' + initial end - 'test_%s' % replaced.gsub(/[ ,-]/, '_') + 'test_%s' % replaced.gsub(/[, -]/, '_').squeeze('_') end def replacements @@ -13,16 +13,22 @@ def replacements booleans.each { |k, v| booleans[k] = "triangle is#{v} #{triangle}" } end - def do - "triangle.#{triangle}?" + def workload + "triangle = Triangle.new(#{sides}) + #{assert_or_refute} triangle.#{triangle}?, #{failure_message}" end - def skipped? - index > 0 + def assert_or_refute + expected ? 'assert' : 'refute' end def failure_message - "Expected '#{expected}', triangle is #{expected ? '' : 'not '}#{triangle}." + "\"Expected '#{expected}', triangle is #{expected ? '' : 'not '}" + + "#{triangle}.\"" + end + + def skipped + index.zero? ? '# skip' : 'skip' end end From 0ee3a937844cb6fef6901626c5de98821df85f97 Mon Sep 17 00:00:00 2001 From: Favio Manriquez Leon Date: Thu, 27 Oct 2016 12:22:21 -0700 Subject: [PATCH 3/4] Add code review suggestions to triangle test generator * Use indentation for workload method * Use tr_s instead of gsub * Use %Q() to simplify string --- lib/triangle_cases.rb | 29 ++++++++++++++++++++++------- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/lib/triangle_cases.rb b/lib/triangle_cases.rb index 6061c24cd5..b8b580ca17 100644 --- a/lib/triangle_cases.rb +++ b/lib/triangle_cases.rb @@ -5,17 +5,23 @@ def test_name if initial.eql?(replaced) && !initial.include?(triangle) replaced = triangle + ' triangle ' + initial end - 'test_%s' % replaced.gsub(/[, -]/, '_').squeeze('_') + 'test_%s' % replaced.tr_s(', -', '_') end def replacements - booleans = { 'true' => '', 'false' => ' not' } - booleans.each { |k, v| booleans[k] = "triangle is#{v} #{triangle}" } + booleans = { 'true' => '', 'false' => '' } + booleans.each { |k, v| booleans[k] = expected_type(k) } end def workload - "triangle = Triangle.new(#{sides}) - #{assert_or_refute} triangle.#{triangle}?, #{failure_message}" + [ + "triangle = Triangle.new(#{sides})", + indent("#{assert_or_refute} triangle.#{triangle}?, #{failure_message}") + ].join("\n") + end + + def indent(line) + ' ' * 4 + line end def assert_or_refute @@ -23,8 +29,17 @@ def assert_or_refute end def failure_message - "\"Expected '#{expected}', triangle is #{expected ? '' : 'not '}" + - "#{triangle}.\"" + %Q("Expected '#{expected}', #{expected_type}.") + end + + def expected_type(boolean = nil) + boolean ||= expected + "triangle is #{boolean_check(boolean) ? '' : 'not '}#{triangle}" + end + + def boolean_check(boolean) + return true if boolean == true || boolean == 'true' + false end def skipped From 5b000fa3b2b95ea2e18f71412d98b8af5dba824b Mon Sep 17 00:00:00 2001 From: Favio Manriquez Leon Date: Sat, 5 Nov 2016 16:20:05 -0700 Subject: [PATCH 4/4] Remove redundant logic --- lib/triangle_cases.rb | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-) diff --git a/lib/triangle_cases.rb b/lib/triangle_cases.rb index b8b580ca17..09afe6a307 100644 --- a/lib/triangle_cases.rb +++ b/lib/triangle_cases.rb @@ -1,18 +1,13 @@ class TriangleCase < OpenStruct def test_name initial = description.downcase - replaced = initial.gsub(/(true|false)/, replacements) + replaced = initial.gsub(/(true|false)/, expected_type) if initial.eql?(replaced) && !initial.include?(triangle) replaced = triangle + ' triangle ' + initial end 'test_%s' % replaced.tr_s(', -', '_') end - def replacements - booleans = { 'true' => '', 'false' => '' } - booleans.each { |k, v| booleans[k] = expected_type(k) } - end - def workload [ "triangle = Triangle.new(#{sides})", @@ -32,14 +27,8 @@ def failure_message %Q("Expected '#{expected}', #{expected_type}.") end - def expected_type(boolean = nil) - boolean ||= expected - "triangle is #{boolean_check(boolean) ? '' : 'not '}#{triangle}" - end - - def boolean_check(boolean) - return true if boolean == true || boolean == 'true' - false + def expected_type + "triangle is #{expected ? '' : 'not '}#{triangle}" end def skipped