Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

generator: support example solutions in .meta/solutions #605

Merged
merged 4 commits into from
May 3, 2017
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
24 changes: 17 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,19 @@ the language, so you're all set.

The files for an exercise live in `exercises/<exercise_name>` (where
`<exercise_name>` is the slug for the exercise, e.g. `clock` or
`atbash-cipher`). An exercise has a test suite
(`exercises/<exercise_name>/<exercise_name>_test.rb`) and an example solution
(`exercises/<exercise_name>/example.rb`).
`atbash-cipher`). All exercises have:

* a test suite, `<exercise_name>_test.rb`
* an example solution, `<exercise_name>.rb`, in `.meta/solutions`

Tests with a test generator will also have:

* a `.version`
* the test generator, `<exercise_name>_cases.rb`, in `.meta/generator`

A few tests have other custom files which are discussed below.

### Canonical Data

**Most exercises can be generated from shared inputs/outputs, called canonical
data (see [Generated Test Suites](#generated-test-suites) below).** To find out
Expand All @@ -25,9 +35,9 @@ the [x-common repo](https://github.com/exercism/x-common/tree/master/exercises).

## Running the Tests

Run the tests using `rake`, rather than `ruby path/to/the_test.rb`. `rake`
knows to look for `example.rb` and to disable skips. Just tell `rake` the
name of your problem and you are set:
Run the tests using `rake`, rather than `ruby path/to/the_test.rb`. `rake` knows
to look for the example solution and to disable skips. Just tell `rake` the name
of your problem and you are set:

```sh
rake test:clock
Expand Down Expand Up @@ -211,7 +221,7 @@ We have created a minimal set of guidelines for the testing files, which
you can take advantage of by installing the `rubocop` gem. It will use
the configuration file located in the root folder, `.rubocop.yml`. When
you edit your code, you can simply run `rubocop -D`. It will ignore
your `example.rb`, but will gently suggest style for your test code.
your example solution, but will gently suggest style for your test code.

The `-D` option that is suggested is provided to give you the ability to
easily ignore the Cops that you think should be ignored. This is easily
Expand Down
14 changes: 13 additions & 1 deletion lib/generator/files/track_files.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ def tests_version
end

def example_solution
ExampleSolutionFile.new(filename: File.join(exercise_path, 'example.rb'))
ExampleSolutionFile.new(filename: File.join(exercise_path, example_file))
end

def minitest_tests
Expand Down Expand Up @@ -48,6 +48,18 @@ def tests_template_filename
'test_template.erb'
end

def example_file
File.exist?(File.join(exercise_path, example_filename)) ? example_filename : legacy_example_filename
end

def example_filename
File.join('.meta', 'solutions', "#{exercise_name}.rb")
end

def legacy_example_filename
'example.rb'
end

def minitest_tests_filename
"#{exercise_name.gsub(/[ -]/, '_')}_test.rb"
end
Expand Down
10 changes: 9 additions & 1 deletion lib/tasks/exercise.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ def directory
end

def example_file
'example.rb'
File.exist?(example_filename) ? example_filename : legacy_example_filename
end

def testable_example_file
Expand All @@ -36,6 +36,14 @@ def test_file

private

def example_filename
File.join('.meta', 'solutions', "#{name}.rb")
end

def legacy_example_filename
'example.rb'
end

def base_file_name
@_base_file_name ||= name.tr('-', '_')
end
Expand Down
17 changes: 14 additions & 3 deletions test/generator/files/track_files_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ def initialize
@paths = FixturePaths
@exercise_name = 'alpha'
end
attr_reader :paths, :exercise_name
attr_accessor :paths, :exercise_name
Copy link
Contributor

@Insti Insti May 3, 2017

Choose a reason for hiding this comment

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

I'd rather exercise name modification being done via constructor argument rather than attr_accessor

but I can understand:

I'd rather live with the mess than refactor to get an elegant solution that would be around for a day or two.

include TrackFiles
end

Expand All @@ -24,7 +24,15 @@ def test_tests_version

def test_example_solution
subject = TestTrackFiles.new
assert_instance_of ExampleSolutionFile, subject.example_solution
expected_filename = FixturePaths.track + '/exercises/alpha/.meta/solutions/alpha.rb'
assert_equal expected_filename, subject.example_solution.filename
end

def test_legacy_example_solution
subject = TestTrackFiles.new
subject.exercise_name = 'beta'
expected_filename = FixturePaths.track + '/exercises/beta/example.rb'
assert_equal expected_filename, subject.example_solution.filename
end

def test_minitest_tests
Expand All @@ -38,6 +46,7 @@ def test_tests_template
assert_equal expected_filename, subject.tests_template.filename
end


class TestTrackFilesUseDefault
def initialize
@paths = FixturePaths
Expand Down Expand Up @@ -75,7 +84,9 @@ def save(content)
end

def test_update_version
subject = TestExampleSolutionFile.new(filename: 'test/fixtures/xruby/exercises/alpha/example.rb')
subject = TestExampleSolutionFile.new(
filename: 'test/fixtures/xruby/exercises/alpha/.meta/solutions/alpha.rb'
)
assert_match(/VERSION = 2/, subject.update_version(2))
end
end
Expand Down
6 changes: 6 additions & 0 deletions test/tasks/exercise_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,12 @@ def test_directory
end

def test_example_file
File.stub(:exist?, true) do
assert_equal '.meta/solutions/name.rb', Exercise.new('name').example_file
end
end

def test_legacy_example_file
assert_equal 'example.rb', Exercise.new('').example_file
end

Expand Down
64 changes: 64 additions & 0 deletions test/tasks/exercise_tests_runner_legacy_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
require_relative '../test_helper'

class FakeLegacyExercise
def name
'test'
end

alias :to_s :name

def directory
'test/.'
end

def example_file
'example.rb'
end

def testable_example_file
'test.rb'
end

def test_file
'test_test.rb'
end
end

class ExerciseTestsRunnerLegacyTest < Minitest::Test
def test_run
Dir.stub :mktmpdir, nil, 'dir' do
cp_mock = Minitest::Mock.new.expect(:call, nil, ['test/.', 'dir'])

mv_mock = Minitest::Mock.new.expect(
:call,
nil,
['dir/example.rb', 'dir/test.rb'],
)

ruby_mock = Minitest::Mock.new.expect(
:call,
nil,
['-I lib -r disable_skip.rb dir/test_test.rb -p'],
)

FileUtils.stub :cp_r, cp_mock do
FileUtils.stub :mv, mv_mock do
runner = ExerciseTestsRunner.new(
exercise: FakeLegacyExercise.new,
test_options: '-p',
)

runner.stub :ruby, ruby_mock do
assert_output "\n\n#{'-'*64}\nrunning tests for: test\n" do
runner.run
end
end

cp_mock.verify
mv_mock.verify
ruby_mock.verify
end
end
end
end
end
4 changes: 2 additions & 2 deletions test/tasks/exercise_tests_runner_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ def directory
end

def example_file
'example.rb'
'.meta/solutions/test.rb'
end

def testable_example_file
Expand All @@ -32,7 +32,7 @@ def test_run
mv_mock = Minitest::Mock.new.expect(
:call,
nil,
['dir/example.rb', 'dir/test.rb'],
['dir/.meta/solutions/test.rb', 'dir/test.rb'],
)

ruby_mock = Minitest::Mock.new.expect(
Expand Down