Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions exercises/grains/.meta/.version
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
1
7 changes: 6 additions & 1 deletion exercises/grains/example.rb
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
module BookKeeping
VERSION = 1
end

module Grains
def self.square(number)
fail ArgumentError if number <= 0 || number > 64
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had to make an adjustment to the implementation because the shared test data includes some error cases that weren't in the original ruby implementation.

How does this work with book keeping when it wasn't there originally? Normally, I'm assuming we would bump the BookKeeping::VERSION, but this exercise wasn't using book keeping yet.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The adjustment looks reasonable.

BookKeeping is related to the tests, so you might need to add it in so that the example.rb still passes the tests.

2**(number - 1)
end

def self.total
square(65) - 1
2**64 - 1
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because of the change to the square method, calling square(65) would raise an ArgumentError, so I just made the smallest change possible to make the tests pass. Is this okay?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is fine.

end
end
17 changes: 17 additions & 0 deletions exercises/grains/example.tt
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
require 'minitest/autorun'
require_relative 'grains'

# Test data version: <%= sha1 %>
class GrainsTest < 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
56 changes: 46 additions & 10 deletions exercises/grains/grains_test.rb
Original file line number Diff line number Diff line change
@@ -1,45 +1,81 @@
#!/usr/bin/env ruby
gem 'minitest', '>= 5.0.0'
require 'minitest/autorun'
require_relative 'grains'

# Test data version: aa12f2e
class GrainsTest < Minitest::Test
def test_square_1
def test_1
# skip
assert_equal 1, Grains.square(1)
end

def test_square_2
def test_2
skip
assert_equal 2, Grains.square(2)
end

def test_square_3
def test_3
skip
assert_equal 4, Grains.square(3)
end

def test_square_4
def test_4
skip
assert_equal 8, Grains.square(4)
end

def test_square_16
def test_16
skip
assert_equal 32_768, Grains.square(16)
end

def test_square_32
def test_32
skip
assert_equal 2_147_483_648, Grains.square(32)
end

def test_square_64
def test_64
skip
assert_equal 9_223_372_036_854_775_808, Grains.square(64)
end

def test_total_grains
def test_square_0_raises_an_exception
skip
assert_raises(ArgumentError) { Grains.square(0) }
end

def test_negative_square_raises_an_exception
skip
assert_raises(ArgumentError) { Grains.square(-1) }
end

def test_square_greater_than_64_raises_an_exception
skip
assert_raises(ArgumentError) { Grains.square(65) }
end

def test_returns_the_total_number_of_grains_on_the_board
skip
assert_equal 18_446_744_073_709_551_615, Grains.total
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
51 changes: 51 additions & 0 deletions lib/grains_cases.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
class GrainsCase < OpenStruct
def test_name
'test_%s' % description.downcase.tr_s(' ', '_')
end

def workload
fail NotImplementedError
end

def skipped
index.zero? ? '# skip' : 'skip'
end

private

def underscore_format(number)
number.to_s.reverse.gsub(/...(?=.)/,'\&_').reverse
end
end

class GrainsCase::SquareMethod < GrainsCase
def workload
return error_assertion if expected < 0

"assert_equal #{underscore_format(expected)}, Grains.square(#{input})"
end

private

def error_assertion
"assert_raises(ArgumentError) { Grains.square(#{input}) }"
end
end

class GrainsCase::TotalMethod < GrainsCase
def workload
"assert_equal #{underscore_format(expected)}, Grains.total"
end
end

GrainsCases = proc do |data|
data = JSON.parse(data)

cases = data['square']['cases'].map.with_index do |row, i|
GrainsCase::SquareMethod.new(row.merge('index' => i))
end

cases << GrainsCase::TotalMethod.new(
data['total'].merge('index' => cases.size)
)
end
102 changes: 102 additions & 0 deletions test/grains_cases_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
require_relative 'test_helper'

class GrainsTest < Minitest::Test
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I really like that you're unit testing the Case classes. 👍

Hopefully doing this will help you realise which methods should be part of some kind of BaseCase class that could be inherited from rather than always starting from OpenStruct

def test_test_name
test_case = GrainsCase.new(description: 'description')

assert_equal 'test_description', test_case.test_name
end

def test_test_name_when_description_has_spaces
test_case = GrainsCase.new(description: 'description with spaces')

assert_equal 'test_description_with_spaces', test_case.test_name
end

def test_workload_is_not_implemented
assert_raises(NotImplementedError) { GrainsCase.new.workload }
end

def test_skipped_with_zero_index
assert_equal '# skip', GrainsCase.new(index: 0).skipped
end

def test_skipped_with_non_zero_index
assert_equal 'skip', GrainsCase.new(index: 1).skipped
end
end

class SquareMethodTest < Minitest::Test
def test_workload_when_expected_is_negative
test_case = GrainsCase::SquareMethod.new(expected: -1, input: 10)

assert_equal 'assert_raises(ArgumentError) { Grains.square(10) }',
test_case.workload
end

def test_workload_when_expected_is_positive
test_case = GrainsCase::SquareMethod.new(expected: 1, input: 10)

assert_equal 'assert_equal 1, Grains.square(10)', test_case.workload
end

def test_workload_with_large_expected_number
test_case = GrainsCase::SquareMethod.new(expected: 10000, input: 10)

assert_equal 'assert_equal 10_000, Grains.square(10)', test_case.workload
end
end

class TotalMethodTest < Minitest::Test
def test_workload
test_case = GrainsCase::TotalMethod.new(expected: 1)

assert_equal 'assert_equal 1, Grains.total', test_case.workload
end

def test_workload_with_large_expected_number
test_case = GrainsCase::TotalMethod.new(expected: 10000)

assert_equal 'assert_equal 10_000, Grains.total', test_case.workload
end
end

class GrainsCasesTest < Minitest::Test
def test_handling_of_json
json = {
square: {
cases: [
{},
{},
],
},
total: {
}
}.to_json

cases = GrainsCases.call(json)

assert_instance_of GrainsCase::SquareMethod, cases[0]
assert_instance_of GrainsCase::SquareMethod, cases[1]
assert_instance_of GrainsCase::TotalMethod, cases[2]
end

def test_continuous_index
json = {
square: {
cases: [
{},
{},
],
},
total: {
}
}.to_json

cases = GrainsCases.call(json)

assert_equal 0, cases[0].index
assert_equal 1, cases[1].index
assert_equal 2, cases[2].index
end
end