-
Ensure the bug was not already reported by searching the forum.
-
If you're unable to find an open conversation addressing the problem, open a new one. Be sure to include a title and clear description, as much relevant information as possible, and (when possible) a code sample.
-
Ensure that the bug is reported (see above). Only start fixing the bug when there is agreement on whether (and how) it should be fixed.
-
Fix the bug and submit a Pull Request to this repository.
-
Ensure the PR description clearly describes the problem and solution. Include a link to the bug's corresponding forum conversation.
-
Before submitting, please read the Contributors Pull Request Guide and Pull Request Guide.
-
Ensure that the feature or change is discussed on the forum. Only start adding the feature or change when there is agreement on whether (and how) it should be added or changed.
-
Add the feature or change and submit a Pull Request to this repository.
-
Ensure the PR description clearly describes the problem and solution. Include a link to the bug's corresponding forum conversation.
-
Before submitting, please read the Contributors Pull Request Guide and Pull Request Guide.
-
Ensure that someone else isn't already adding it
- start with the Practice exercises to implement issue.
- also search the forum and the repository's issues and pull requests.
-
If nobody is yet adding the exercise, open a conversation and indicate you'd like to add the exercise.
-
Run
bin/add-practice-exercise ${slug_name}This creates the scaffolding for the new exercise:
- The test, stub and example files are empty.
- The canonical data from problem-specifications gets added into your local
canonical-datadirectory. - A stub test generator is created:
exercises/practice/${slug_name}/.meta/spec_generator.moon
-
Review the canonical data and decide if there are any tests cases to exclude.
If there are, add
include = falseproperties in the exercise's.meta/tests.tomlfile.If there are whole test groups to exclude, add an "exclusions" property to the spec_generator module (see below).
-
Considering the canonical data, decide if this exercise makes sense to use a test generator.
-
If no:
- delete the stubbed .meta/spec_generator and create the test suite manually.
- Use the file
canonical-data/${slug_name}.jsonto create the tests. - Remember, this track uses TDD, so the first test uses
itand all the rest usepending.
-
If yes:
-
Edit the spec_generator -- see below for more details.
-
Run the generator script and review the new test suite.
bin/generate-spec ${slug_name}Loop back to the previous step as needed.
-
-
-
Create the example solution:
.meta/example.moon- follow the style guide.
-
Test it with
bin/verify-exercises ${slug_name} -
When you're satisfied with the solution, create the stub file
${slug_name}.moon. Provide a stub for every function being tested. For classes, provide stubs for the constructor and each method. The stubbed function should emit an error. See what it looks like in other exercises.complex-numbersis a good one. -
Revisit the exercise difficulty in config.json if the implementation was harder/easier than expected.
-
Run
bin/configlet lintto ensure that the new exercise conforms to Exercism standards.
The spec_generator.moon file is a MoonScript module containing the functions necessary to generate the test suite for practice exercises.
It is located in exercises/practice/${slug_name}/.meta/ directory.
It's a module that returns a table.
- (required) one of the following
module_name: (string) this is the left-hand side of${module_name} = require '${slug_name}'in the test filemodule_imports: (list of strings) this is the list of names that appear inimport ${names} from require '${slug_name}'in the test file
- (required)
generate_test: this is a function that creates the body of each test case. It takes 2 parameters:caseis the Lua object for one test case taken from the canonical data.level, default value 2, is the indentation level of the body. MoonScript is a whitespace-sensitive language, so we need to be careful about indenting the test functions properly.
- (optional)
test_helpers: (string) a block of code that gets added at the top of the top-leveldescribeblock. This might include required modules used in the tests, or helper functions, or (more interstingly) custom assertions, or even just helpful comments for students. - (optional)
exclusions: (list of tables) identifies things from the canonical data to exclude. Normally, settinginclude = falsein the tests.toml file is the preferred way to exclude individual tests, but sometimes you want to exclude a whole block of tests.- Examples:
simple-linked-list,gigasecond
- Examples:
- (optional)
bonus: (string) bonus tests to add to the spec file as extra challenge for the students. These tests are not executed by the test runner.- Examples:
robot-name,custom-set
- Examples:
dnd-character--assert.between value, min, maxspace-age--assert.approx_equal #{case.expected}, resultword-count--assert.has.same_kv result, expected
These functions are exported from bin/generate-spec for use in spec_generator modules
indent(text, level)-- provide leading whitespace to the appropriate level.quote(str)-- add quotation marks to the string, single or double as appropriate.is_empty(tbl)-- predicate: is the table emptyis_json_null(value)-- predicate: is the valuejson.nullfrom dkjsoncontains(tbl, value)-- predicate: does the table contain the value (uses==)
We have a library of helper functions, useful for generating pretty tables mostly.
For example, to nicely format a list of words, or a list of strings over multiple lines, or to recursively format nested tables.
Look in lib/test_helpers.moon.
Example:
import int_list, string_list from require 'test_helpers'
...
{
generate_test: (case, level) ->
lines = {
"input = #{int_list case.input.numbers}"
"expected = #{string_list case.expected, level}"
...The assert.are.same t1, t2 assertion is used to compare tables deeply.
However when they don't match, by default busted does not show the whole object, which makes it hard for the student to see the difference.
Consider this busted output where something is different in the ...more sections.
Failure → ./rest_api_spec.moon @ 251
rest-api iou lender owes borrower less than new loan
...uarocks/lib/luarocks/rocks-5.4/busted/2.3.0-1/bin/busted:7: Expected objects to be the same.
Passed in:
(table: 0x641e0548a540) {
*[users] = {
[1] = {
[balance] = 1.0
[name] = 'Adam'
[owed_by] = { ... more }
[owes] = { } }
*[2] = {
[balance] = -1.0
[name] = 'Bob'
[owed_by] = { }
*[owes] = { ... more } } } }
Expected:
(table: 0x641e0548a620) {
*[users] = {
[1] = {
[balance] = 1.0
[name] = 'Adam'
[owed_by] = { ... more }
[owes] = { } }
*[2] = {
[balance] = -1.0
[name] = 'Bob'
[owed_by] = { }
*[owes] = { ... more } } } }
The solution here is to configure the assert object,.
In the spec_generator.moon module, add this:
-- we have deep tables to compare, display it all when not the same
test_helpers: [[
assert\set_parameter "TableFormatLevel", 4
]]
Here, the value 4 was chosen to reflect the max depth of the expected value:
...1...2...3...4
{
users: {
{
owed_by: {
Bob: 3.0
}
balance: 3.0
owes: {}
name: "Adam"
},
...