Skip to content

Commit f56c429

Browse files
authored
Merge pull request #568 from hilary/auto_extract_cases
Provides a common implementation of the code that extracts the individual test cases from a problem's `canonical_data.json` file, and removes support for the 'proc' based procedure previously used in the 'lib/<problem>_cases.rb' files.
2 parents e51ac9c + 557997c commit f56c429

File tree

8 files changed

+172
-15
lines changed

8 files changed

+172
-15
lines changed

lib/generator/case_values.rb

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
module Generator
2+
module CaseValues
3+
class Extractor
4+
def initialize(case_class:)
5+
@case_class = case_class
6+
end
7+
8+
def cases(exercise_data)
9+
extract_test_cases(
10+
data: JSON.parse(exercise_data)['cases']
11+
).map.with_index do |test, index|
12+
@case_class.new(test.merge('index' => index))
13+
end
14+
end
15+
16+
private
17+
18+
def extract_test_cases(data:)
19+
data.flat_map do |entry|
20+
entry.key?('cases') ? extract_test_cases(data: entry['cases']) : entry
21+
end
22+
end
23+
end
24+
end
25+
end

lib/generator/files/track_files.rb

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,18 @@ def filename(exercise_name)
1414
"#{exercise_name.tr('-', '_')}_cases"
1515
end
1616

17-
def proc_name(exercise_name)
18-
filename(exercise_name).split('_').map(&:capitalize).join
17+
def class_name(exercise_name)
18+
filename(exercise_name)[0..-2].split('_').map(&:capitalize).join
1919
end
2020

2121
def exercise_name(filename)
2222
%r{([^/]*)_cases\.rb$}.match(filename).captures[0].tr('_', '-')
2323
end
24+
25+
def load_filename(track_path, exercise_name)
26+
path = File.join(track_path, 'lib')
27+
"%s/%s.rb" % [ path, filename(exercise_name) ]
28+
end
2429
end
2530

2631
module TrackFiles

lib/generator/template_values.rb

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,23 +16,28 @@ def get_binding
1616

1717
module TemplateValuesFactory
1818
def template_values
19-
require cases_require_name
20-
2119
TemplateValues.new(
2220
abbreviated_commit_hash: canonical_data.abbreviated_commit_hash,
2321
version: version,
24-
test_cases: test_cases_proc.call(canonical_data.to_s)
22+
test_cases: extract
2523
)
2624
end
2725

2826
private
2927

30-
def cases_require_name
31-
Files::GeneratorCases.filename(exercise_name)
28+
def extract
29+
load cases_load_name
30+
extractor.cases(canonical_data.to_s)
31+
end
32+
33+
def extractor
34+
CaseValues::Extractor.new(
35+
case_class: Object.const_get(Files::GeneratorCases.class_name(exercise_name))
36+
)
3237
end
3338

34-
def test_cases_proc
35-
Object.const_get(Files::GeneratorCases.proc_name(exercise_name))
39+
def cases_load_name
40+
Files::GeneratorCases.load_filename(paths.track, exercise_name)
3641
end
3742
end
3843
end
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
{
2+
"exercise": "beer-song",
3+
"version": "1.0.0",
4+
"cases": [
5+
{
6+
"description": "verse",
7+
"cases": [
8+
{
9+
"description": "single verse",
10+
"cases": [
11+
{
12+
"description": "first generic verse",
13+
"property": "verse",
14+
"number": 99,
15+
"expected": "99 bottles of beer on the wall, YAAAR"
16+
},
17+
{
18+
"description": "last generic verse",
19+
"property": "verse",
20+
"number": 3,
21+
"expected": "3 bottles of beer on the wall, YAAAR"
22+
}
23+
]
24+
}
25+
]
26+
},
27+
{
28+
"description": "lyrics",
29+
"cases": [
30+
{
31+
"description": "multiple verses",
32+
"cases": [
33+
{
34+
"description": "first two verses",
35+
"property": "verses",
36+
"beginning": 99,
37+
"end": 98,
38+
"expected": "99 bottles of beer on the wall, YAR, PIRATES CAN'T COUNT"
39+
}
40+
]
41+
}
42+
]
43+
}
44+
]
45+
}

test/fixtures/xruby/lib/beta_cases.rb

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
require 'exercise_cases'
2+
3+
class BetaCase < ExerciseCase
4+
def workload
5+
assert_equal { "Beta.call('#{input}')" }
6+
end
7+
end

test/generator/case_values_test.rb

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
require_relative '../test_helper'
2+
3+
class ComplexCase < ExerciseCase
4+
def workload
5+
assert { Complex.foo(bar) }
6+
end
7+
end
8+
9+
module Generator
10+
module CaseValues
11+
class ExtractorTest < Minitest::Test
12+
def test_multi_level_auto_extraction
13+
canonical_data = File.read('test/fixtures/metadata/exercises/complex/canonical-data.json')
14+
cases = Extractor.new(
15+
case_class: ComplexCase,
16+
).cases(canonical_data)
17+
18+
expected = [
19+
ComplexCase.new(description: 'first generic verse', property: 'verse', number: 99,
20+
expected: '99 bottles of beer on the wall, YAAAR', index: 0),
21+
ComplexCase.new(description: 'last generic verse', property: 'verse', number: 3,
22+
expected: '3 bottles of beer on the wall, YAAAR', index: 1),
23+
ComplexCase.new(description: 'first two verses', property: 'verses', beginning: 99, end: 98,
24+
expected: "99 bottles of beer on the wall, YAR, PIRATES CAN'T COUNT", index: 2)
25+
]
26+
assert_equal expected, cases
27+
end
28+
end
29+
end
30+
end

test/generator/files/track_files_test.rb

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,8 @@ def test_filename
2525
assert_equal 'two_parter_cases', GeneratorCases.filename(exercise_name)
2626
end
2727

28-
def test_proc_name
29-
exercise_name = 'two-parter'
30-
assert_equal 'TwoParterCases', GeneratorCases.proc_name(exercise_name)
28+
def test_class_name
29+
assert_equal 'TwoParterCase', GeneratorCases.class_name('two-parter')
3130
end
3231
end
3332

test/generator/template_values_test.rb

Lines changed: 44 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,17 +43,58 @@ def canonical_data
4343
mock_canonical_data
4444
end
4545

46+
def paths
47+
mock_paths = Minitest::Mock.new
48+
mock_paths.expect :track, 'test/fixtures/xruby'
49+
mock_paths
50+
end
51+
4652
include TemplateValuesFactory
4753
end
4854

49-
def test_template_values
50-
$LOAD_PATH.unshift 'test/fixtures/xruby/lib'
55+
class ClassBasedTestTemplateValuesFactory
56+
def exercise_name
57+
'beta'
58+
end
59+
60+
def version
61+
2
62+
end
63+
64+
def canonical_data
65+
mock_canonical_data = Minitest::Mock.new
66+
mock_canonical_data.expect :abbreviated_commit_hash, nil
67+
mock_canonical_data.expect :to_s, '{"cases":[]}'
68+
mock_canonical_data
69+
end
70+
71+
def paths
72+
mock_paths = Minitest::Mock.new
73+
mock_paths.expect :track, 'test/fixtures/xruby'
74+
mock_paths
75+
end
76+
77+
include TemplateValuesFactory
78+
end
79+
80+
def test_template_values_from_class
81+
subject = ClassBasedTestTemplateValuesFactory.new
82+
assert_instance_of TemplateValues, subject.template_values
83+
end
84+
85+
def test_template_values_loads_problem_case_classes
5186
subject = TestTemplateValuesFactory.new
5287
assert_instance_of TemplateValues, subject.template_values
88+
assert Object.const_defined?(:AlphaCase)
89+
assert Object.const_defined?(:AlphaCases)
5390
end
5491

5592
def teardown
56-
$LOAD_PATH.delete 'test/fixtures/xruby/lib'
93+
[:AlphaCase, :AlphaCases].each do |classname|
94+
if Object.const_defined?(classname)
95+
Object.send(:remove_const, classname)
96+
end
97+
end
5798
end
5899
end
59100
end

0 commit comments

Comments
 (0)