Skip to content

Commit efc7fef

Browse files
author
Katrina Owen
committed
Update documentation about generated test cases
The various file names and file paths have changed. This breaks the documentation into three logical sections targeting the tasks that the reader will likely want to choose from: * regenerating the exercise * changing a test suite * implementing a new generator
1 parent 40e35ed commit efc7fef

File tree

1 file changed

+138
-23
lines changed

1 file changed

+138
-23
lines changed

README.md

Lines changed: 138 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -65,50 +65,163 @@ Note that flags which have an attached value, like above, must take the form
6565
### Generated Test Suites
6666

6767
If you find an `example.tt` file in a problem directory, then the test suite is
68-
generated from shared data. In this case changing the test file itself will
69-
not be enough.
68+
generated from shared data, which can be found in the exercise definition in the [x-common][]
69+
repository.
7070

71-
You will need to have cloned [the shared metadata](https://github.com/exercism/x-common)
72-
at the same level as the xruby repository. E.g.
71+
Typically you will want to do one of the following:
72+
73+
* [Regenerate the test suite](#regenerating-an-exercise) based on updated canonical data
74+
* [Make changes to a generated exercise](#changing-a-generated-exercise)
75+
* [Implement a new generator](#implementing-a-generator)
76+
77+
Generated exercises depend on the [the shared metadata][x-common], which must be
78+
cloned to the same directory that contains your clone of the xruby repository:
7379

7480
```
7581
tree -L 1 ~/code/exercism
7682
├── x-common
7783
└── xruby
7884
```
7985

80-
1. `xruby/$PROBLEM/example.tt` - the Erb template for the test file, `$PROBLEM_test.rb`.
81-
1. `x-common/$PROBLEM.json` - the shared inputs and outputs for the problem.
82-
1. `lib/$PROBLEM.rb` - the logic for turning the data into tests.
83-
1. `xruby/bin/generate $PROBLEM` - the command to actually generate the test suite.
84-
1. `.version` - used to keep track of the version of the test files as the data changes.
86+
#### Regenerating an Exercise
87+
88+
From within the xruby directory, run the following command, where $PROBLEM is the slug
89+
of the exercise, e.g. `clock` or `atbash-cipher`:
90+
91+
```
92+
bin/generate $PROBLEM
93+
```
94+
95+
#### Changing a Generated Exercise
96+
97+
The `$PROBLEM/$PROBLEM_test.rb` will never be edited directly.
98+
99+
There are two reasons why a test suite might change:
100+
101+
1. the tests are wrong (an incorrect expectation, a missing edge case, etc)
102+
1. there might be issues with the style or boilerplate
103+
104+
In the first case, the changes need to be made to the `canonical-data.json` file for
105+
the exercise, which lives in the x-common repository.
106+
107+
```
108+
../x-common/exercises/$PROBLEM/
109+
├── canonical-data.json
110+
├── description.md
111+
└── metadata.yml
112+
```
113+
114+
This change will need to be submitted as a pull request to the x-common repository. This pull
115+
request needs to be merged before you can regenerate the exercise.
85116

86-
Additionally, there is some common generator logic in `lib/generator.rb`.
117+
Changes that don't have to do directly with the test inputs and outputs, will either need to be
118+
made to `exercises/$PROBLEM/example.tt` or `lib/$PROBLEM_cases.rb`. Then you can regenerate the
119+
exercise with `bin/generate $PROBLEM`.
87120

88-
For example, take a look at the `hamming.json` file in the x-common repository, as well
89-
as the following files in the xruby repository:
121+
#### Implementing a Generator
90122

91-
1. `hamming/example.tt`
92-
1. `bin/generate hamming`
93-
1. `lib/hamming.rb`
94-
1. `lib/generator.rb`
123+
You will need to implement three files to create a generator:
95124

96-
The `hamming/hamming_test.rb` will never be edited directly. If there's a missing test case,
97-
then additional inputs/outputs should be submitted to the x-common repository.
125+
1. `exercises/$PROBLEM/example.tt` - the Erb template for the test file, `$PROBLEM_test.rb`.
126+
1. `exercises/$PROBLEM/.meta/.version` - used to keep track of the version of the test files as the data changes.
127+
1. `lib/$PROBLEM_cases.rb` - the logic for turning the data into tests.
98128

99-
Changes to the test suite (style, boilerplate, etc) will probably have to be made to
100-
`example.tt`.
129+
You will not need to touch the top-level script, `bin/generate`.
101130

102-
### Exercise Generators
131+
The `bin/generate` command relies on some common logic implemented in `lib/generator.rb`.
132+
You probably won't need to touch that, either.
103133

104-
If you wish to create a new generator, or edit an existing one, the generators currently live in the lib directory and are named `$PROBLEM_cases.rb`. For example, the hamming generator is `lib/hamming_cases.rb`.
134+
The `lib/$PROBLEM_cases.rb` file should contain a small class that wraps the JSON for a single test case:
105135

106-
All generators currently adhere to a common public interface, and must define the following three methods:
136+
```
137+
require 'exercise_cases'
138+
139+
class ProblemNameCase < OpenStruct
140+
def test_name
141+
'test_%s' % description.gsub(/[ -]/, '_')
142+
end
143+
144+
def workload
145+
# implement main logic of test here
146+
end
147+
148+
def skipped
149+
index.zero? ? '# skip' : 'skip'
150+
end
151+
end
152+
```
153+
154+
Instead of `ProblemName` use the name of the actual problem. This is important, since
155+
the generator script will infer the name of the class from the argument that is passed.
156+
157+
This class must implement the following methods:
107158

108159
- `test_name` - Returns the name of the test (i.e `test_one_equals_one`)
109160
- `workload` - Returns the main syntax for the test. This will vary depending on the test generator and its underlying implementation
110161
- `skipped` - Returns skip syntax (i.e. `skip` or `# skip`)
111162

163+
Beyond that, you can implement any helper methods that you need.
164+
165+
Below this class, implement a small loop that will generate all the test cases by reading the
166+
`canonical-data.json` file, and looping through the test cases.
167+
168+
You will need to adjust the logic to match the structure of the canonical data.
169+
170+
For example, if there is a single top-level key named "cases", then you can loop through
171+
them as follows:
172+
173+
```
174+
ProblemNameCases = proc do |data|
175+
JSON.parse(data)['cases'].map.with_index do |row, i|
176+
ProblemNameCase.new(row.merge('index' => i))
177+
end
178+
end
179+
```
180+
181+
If there are multiple sections, then you will need to loop through the sections, and then
182+
loop through each of the cases in an inner loop:
183+
184+
```
185+
ProblemNameCases = proc do |data|
186+
i = 0
187+
json = JSON.parse(data)
188+
cases = []
189+
%w(section1 section2 etc).each do |section|
190+
json[section]['cases'].each do |row|
191+
row = row.merge(row.merge('index' => i, 'section' => section))
192+
cases << ProblemNameCase.new(row)
193+
i += 1
194+
end
195+
end
196+
cases
197+
end
198+
```
199+
200+
Finally, you need to create a text template, `example.tt`, as the bases for the test suite.
201+
202+
Start with the following boilerplate, and adjust as necessary:
203+
204+
```
205+
#!/usr/bin/env ruby
206+
gem 'minitest', '>= 5.0.0'
207+
require 'minitest/autorun'
208+
require_relative '$PROBLEM'
209+
210+
# Common test data version: <%= abbreviated_commit_hash %>
211+
class ProblemNameTest < Minitest::Test<% test_cases.each do |test_case| %>
212+
def <%= test_case.name %>
213+
<%= test_case.skipped %>
214+
assert_equal <%= test_case.expected %>, <%= test_case.work_load %>
215+
end
216+
<% end %>
217+
<%= IO.read(XRUBY_LIB + '/bookkeeping.md') %>
218+
def test_bookkeeping
219+
skip
220+
assert_equal <%= version %>, BookKeeping::VERSION
221+
end
222+
end
223+
```
224+
112225
## Pull Requests
113226

114227
We welcome pull requests that provide fixes to existing test suites (missing
@@ -168,3 +281,5 @@ Copyright (c) 2014 Katrina Owen, [email protected]
168281

169282
## Ruby icon
170283
The Ruby icon is the Vienna.rb logo, and is used with permission. Thanks Floor Dress :)
284+
285+
[x-common]: https://github.com/exercism/x-common

0 commit comments

Comments
 (0)