Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
9e9977a
lib/generator.rb: Ignore blank template lines. (#479)
Insti Nov 5, 2016
2098525
Regenerated based on updated xcommon data.
Insti Nov 5, 2016
ea9dc16
Don't increment the version here.
Insti Nov 5, 2016
a140290
Generate whole test body in generator.
Insti Nov 5, 2016
3e0d0e5
Add spaces around bookkeeping comment.
Insti Nov 5, 2016
f9d2ba0
Generate the whole test in the generator
Insti Nov 5, 2016
3d616a9
Remove redundant code.
Insti Nov 5, 2016
c78a049
Move bookkeeping comment into generator.
Insti Nov 5, 2016
a38141c
Move Bookeeping gerneration into generator.
Insti Nov 5, 2016
22697ca
Rename assertion to workload
Insti Nov 5, 2016
dbd5210
add canonical_data alias
Insti Nov 5, 2016
671643d
Add to_s to TestCase that returns the full test
Insti Nov 5, 2016
5ea23bf
Rename data to canonical data
Insti Nov 5, 2016
cc9af61
Move the index out of the canonical data
Insti Nov 5, 2016
c127328
Make IsogramCases a class rather than a proc
Insti Nov 5, 2016
44bad1c
Added generic TestCases class
Insti Nov 5, 2016
a3587f5
Add test failure message.
Insti Nov 5, 2016
d2acb90
Rename method to `isogram?`
Insti Nov 5, 2016
091bf9f
Created tests
Insti Nov 5, 2016
a7f013d
Get coverage info on everything in lib
Insti Nov 5, 2016
5d77f2e
Tidy up ExerciseTestCase a bit.
Insti Nov 6, 2016
68a5dec
Group test cases in coverage report.
Insti Nov 6, 2016
b23e0b6
Add rake and simplecov for beter testing.
Insti Nov 6, 2016
3f25835
Update method name.
Insti Nov 6, 2016
fe35f2d
Only test /exercises/* tests for executability.
Insti Nov 6, 2016
1e54c30
Add 'Guard' for automatically running tests.
Insti Nov 6, 2016
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
9 changes: 9 additions & 0 deletions Gemfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,13 @@
source 'https://rubygems.org'

ruby '2.2.5'

gem 'minitest'
gem 'rubocop', '0.36.0'
gem 'simplecov'
gem 'rake'

# These are not strictly necessary, but I use them to automatically run the
# tests whenever I edit a file.
gem 'guard'
gem 'guard-shell'
39 changes: 39 additions & 0 deletions Guardfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# A sample Guardfile
# More info at https://github.com/guard/guard#readme

## Uncomment and set this to only include directories you want to watch
# directories %w(app lib config test spec features) \
# .select{|d| Dir.exists?(d) ? d : UI.warning("Directory #{d} does not exist")}

## Note: if you are using the `directories` clause above and you are not
## watching the project directory ('.'), then you will want to move
## the Guardfile to a watched dir and symlink it back, e.g.
#
# $ mkdir config
# $ mv Guardfile config/
# $ ln -s config/Guardfile .
#
# and, you'll have to watch "config/Guardfile" instead of "Guardfile"

interactor :off

# Add files and commands to this file, like the example:
# watch(%r{file/path}) { `command(s)` }
#
guard :shell do
watch(/(lib|tests\/)(.*)\.rb$/) do |m|
file = m[0]
puts "File changed: #{file}"
test_file = file[/_test\.rb/] ? file : file.sub(/\.rb/, '_test.rb')
commands =
"bundle exec rake test",
"bundle exec rubocop -D #{file}"
generator =
"bin/generate isogram",
"git --no-pager diff exercises/isogram/isogram_test.rb"
command = commands.join(' && ')
p command
system(command)
system(generator.join(' && '))
end
end
6 changes: 6 additions & 0 deletions Rakefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
require 'rake'
require 'rake/testtask'

Rake::TestTask.new do |task|
task.pattern = 'tests/*_test.rb'
end
2 changes: 1 addition & 1 deletion bin/executable-tests-check
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ require 'minitest/autorun'

# Assume that this file lives in #{base}/bin
base = File.join(__dir__,'..')
files = Dir.glob("#{base}/**/*test.rb") + Dir.glob("#{base}/bin/*")
files = Dir.glob("#{base}/exercises/**/*test.rb") + Dir.glob("#{base}/bin/*")

files.each do |file|
describe file do
Expand Down
2 changes: 1 addition & 1 deletion exercises/isogram/.version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
1
2
4 changes: 2 additions & 2 deletions exercises/isogram/example.rb
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
module BookKeeping
VERSION = 1
VERSION = 2
end

class Isogram
def self.is_isogram?(str)
def self.isogram?(str)
letters = str.downcase.gsub(/[[:punct:]]| /, '').chars
letters == letters.uniq
end
Expand Down
16 changes: 5 additions & 11 deletions exercises/isogram/example.tt
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,10 @@ require 'minitest/autorun'
require_relative 'isogram'

# Common test data version: <%= sha1 %>
class IsogramTest < Minitest::Test<% test_cases.each do |test_case| %>
def <%= test_case.name %>
<%= test_case.skip %>
string = '<%= test_case.input %>'
<%= test_case.assertion %> Isogram.is_isogram?(string)
end
class IsogramTest < Minitest::Test
<% test_cases.each_with_index do |test_case,index| %>
<%= test_case.render(index) %>

<% end %>
<%= IO.read(XRUBY_LIB + '/bookkeeping.md') %>
def test_bookkeeping
skip
assert_equal <%= version.next %>, BookKeeping::VERSION
end
<%= test_version_bookkeeping.render %>
end
56 changes: 22 additions & 34 deletions exercises/isogram/isogram_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,66 +4,54 @@
require 'minitest/autorun'
require_relative 'isogram'

# Common test data version: c1cb73f
# Common test data version: 2adfe21
class IsogramTest < Minitest::Test
def test_duplicates
def test_empty_string_has_no_duplicates
# skip
string = ''
assert Isogram.isogram?(string), '"" is an isogram'
end

def test_isogram_with_only_lower_case_characters
skip
string = 'duplicates'
assert Isogram.is_isogram?(string)
assert Isogram.isogram?(string), '"duplicates" is an isogram'
end

def test_eleven
def test_word_with_one_duplicated_character
skip
string = 'eleven'
refute Isogram.is_isogram?(string)
refute Isogram.isogram?(string), '"eleven" is NOT an isogram'
end

def test_subdermatoglyphic
def test_longest_reported_english_isogram
skip
string = 'subdermatoglyphic'
assert Isogram.is_isogram?(string)
assert Isogram.isogram?(string), '"subdermatoglyphic" is an isogram'
end

def test_alphabet
def test_word_with_duplicated_character_in_mixed_case
skip
string = 'Alphabet'
refute Isogram.is_isogram?(string)
refute Isogram.isogram?(string), '"Alphabet" is NOT an isogram'
end

def test_thumbscrew_japingly
def test_hypothetical_isogrammic_word_with_hyphen
skip
string = 'thumbscrew-japingly'
assert Isogram.is_isogram?(string)
assert Isogram.isogram?(string), '"thumbscrew-japingly" is an isogram'
end

def test_hjelmqvist_gryb_zock_pfund_wax
def test_isogram_with_duplicated_non_letter_character
skip
string = 'Hjelmqvist-Gryb-Zock-Pfund-Wax'
assert Isogram.is_isogram?(string)
end

def test_heizölrückstoßabdämpfung
skip
string = 'Heizölrückstoßabdämpfung'
assert Isogram.is_isogram?(string)
assert Isogram.isogram?(string), '"Hjelmqvist-Gryb-Zock-Pfund-Wax" is an isogram'
end

def test_the_quick_brown_fox
skip
string = 'the quick brown fox'
refute Isogram.is_isogram?(string)
end

def test_emily_jung_schwartzkopf
def test_made_up_name_that_is_an_isogram
skip
string = 'Emily Jung Schwartzkopf'
assert Isogram.is_isogram?(string)
end

def test_éléphant
skip
string = 'éléphant'
refute Isogram.is_isogram?(string)
assert Isogram.isogram?(string), '"Emily Jung Schwartzkopf" is an isogram'
end

# Problems in exercism evolve over time, as we find better ways to ask
Expand All @@ -85,6 +73,6 @@ def test_éléphant

def test_bookkeeping
skip
assert_equal 1, BookKeeping::VERSION
assert_equal 2, BookKeeping::VERSION
end
end
60 changes: 60 additions & 0 deletions lib/exercise_testcase.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
class ExerciseTestCase
attr_reader :canonical_data

def initialize(canonical_data)
@canonical_data = OpenStruct.new(canonical_data)
end

def render(index = -1)
@index = index
[comment, *full_method].compact.join("\n") + "\n"
end

def full_method
indent( [
method_definition,
*method_body,
method_end
], 2)
end

def comment
nil
end

def description
canonical_data.description || ''
end

def method_definition
"def #{test_name}"
end

def method_body
indent( [skip, *workload], 2)
end

def method_end
"end"
end

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

def test_name
"test_#{description_as_test_name}"
end

def description_as_test_name
description.downcase.tr_s(' -','_')
end

def workload
"assert #{@canonical_data.expected.inspect}, Subject.method #{@canonical_data.input.inspect}"
end

def indent(lines, count)
lines.compact.map { |line| ' ' * count + line }
end
end
20 changes: 20 additions & 0 deletions lib/exercise_testcases.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
class ExerciseTestCases
attr_reader :cases_key
def initialize(json_data)
@data = json_data
@cases_key = 'cases'
end

def case_classname
classname = self.class.to_s.sub(/Cases$/,'Case')
Object.const_get(classname)
end

def parsed_json_cases
JSON.parse(@data)[cases_key]
end

def to_a
parsed_json_cases.map { |test_case| case_classname.new(test_case) }
end
end
30 changes: 24 additions & 6 deletions lib/generator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
require 'json'
require 'ostruct'

require_relative 'exercise_testcases'
require_relative 'exercise_testcase'

class Generator
METADATA_REPOSITORY = 'x-common'.freeze

Expand All @@ -14,7 +17,6 @@ def initialize(name, cases)
end

def metadata_dir
# rubocop:disable Metrics/LineLength
File.expand_path(File.join('..', '..', '..', METADATA_REPOSITORY, 'exercises', name), __FILE__)
end

Expand All @@ -35,7 +37,22 @@ def sha1
end

def test_cases
cases.call(data)
cases.new(data).to_a
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Could we insert the bookkeeping test case into the cases array to always generate it at the end of the view? This would allows us to render it just like all the other test cases, since it is a ExerciseTestCase

end

class BookKeeping < ExerciseTestCase
def comment
IO.read(XRUBY_LIB + '/bookkeeping.md')
end

def workload
'assert_equal 2, BookKeeping::VERSION'
end
end

def test_version_bookkeeping
BookKeeping.new('description' => 'bookkeeping')
# [comment, "\n", bla.full_method].join
end

def metadata_repository_missing_message
Expand All @@ -51,20 +68,21 @@ def metadata_repository_missing_message
def generate
check_metadata_repository_exists
generate_test_file
increment_version
increment_version_in_example
# increment_version
# increment_version_in_example
end

def check_metadata_repository_exists
unless File.directory?(metadata_dir)
STDERR.puts metadata_repository_missing_message
fail Errno::ENOENT.new(metadata_dir)
raise Errno::ENOENT, metadata_dir
end
end

def generate_test_file
File.open(path_to("#{name.gsub(/[ -]/, '_')}_test.rb"), 'w') do |f|
f.write ERB.new(File.read(path_to('example.tt'))).result binding
template = File.read(path_to('example.tt'))
f.write ERB.new(template, nil, '<>').result binding
end
end

Expand Down
30 changes: 16 additions & 14 deletions lib/isogram_cases.rb
Original file line number Diff line number Diff line change
@@ -1,24 +1,26 @@
class IsogramCase < OpenStruct
require_relative 'exercise_testcases'
require_relative 'exercise_testcase'

def name
format('test_%s', description)
end
class IsogramCases < ExerciseTestCases
end

def description
input.downcase.gsub(/[ -]/,'_')
class IsogramCase < ExerciseTestCase
def workload
[
"string = '#{canonical_data.input}'",
"#{assertion} Isogram.isogram?(string), '#{failure_message}'"
]
end

def assertion
expected ? 'assert' : 'refute'
def failure_message
"#{canonical_data.input.inspect} #{is_or_isnt} an isogram"
end

def skip
index.zero? ? '# skip' : 'skip'
def is_or_isnt
canonical_data.expected ? 'is' : 'is NOT'
end
end

IsogramCases = proc do |data|
JSON.parse(data)['cases'].map.with_index do |row, i|
IsogramCase.new(row.merge('index' => i))
def assertion
canonical_data.expected ? 'assert' : 'refute'
end
end
Loading