trackler 2.0.8.15 → 2.0.8.16
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/common/exercises/clock/canonical-data.json +473 -425
- data/common/exercises/crypto-square/canonical-data.json +105 -95
- data/common/exercises/difference-of-squares/canonical-data.json +76 -62
- data/common/exercises/etl/canonical-data.json +67 -59
- data/common/exercises/meetup/canonical-data.json +859 -762
- data/common/exercises/meetup/description.md +13 -7
- data/common/exercises/minesweeper/canonical-data.json +21 -5
- data/common/exercises/nth-prime/canonical-data.json +23 -16
- data/common/exercises/nucleotide-count/canonical-data.json +49 -41
- data/common/exercises/ocr-numbers/canonical-data.json +204 -183
- data/common/exercises/pascals-triangle/canonical-data.json +38 -27
- data/common/exercises/queen-attack/canonical-data.json +125 -109
- data/common/exercises/rotational-cipher/canonical-data.json +1 -1
- data/common/exercises/triangle/canonical-data.json +103 -74
- data/lib/trackler/version.rb +1 -1
- data/tracks/csharp/docs/TESTS.md +7 -1
- data/tracks/csharp/exercises/{exercises.sln → Exercises.All.sln} +0 -0
- data/tracks/csharp/exercises/Exercises.Default.sln +1433 -0
- data/tracks/csharp/exercises/Exercises.Refactoring.sln +61 -0
- data/tracks/csharp/exercises/acronym/AcronymTest.cs +35 -11
- data/tracks/csharp/exercises/parallel-letter-frequency/ParallelLetterFrequencyTest.cs +2 -2
- data/tracks/delphi/docs/TESTS.md +2 -2
- data/tracks/elixir/exercises/bowling/bowling.exs +1 -1
- data/tracks/fsharp/docs/LEARNING.md +2 -1
- data/tracks/go/config.json +7 -1
- data/tracks/go/exercises/prime-factors/{primefactors_test.go → prime_factors_test.go} +4 -1
- data/tracks/go/exercises/protein-translation/protein_translation_test.go +6 -6
- data/tracks/go/exercises/pythagorean-triplet/example.go +2 -0
- data/tracks/go/exercises/pythagorean-triplet/pythagorean_triplet_test.go +8 -0
- data/tracks/julia/README.md +2 -0
- data/tracks/julia/config.json +9 -0
- data/tracks/julia/exercises/rotational-cipher/HINTS.md +21 -0
- data/tracks/julia/exercises/rotational-cipher/example.jl +16 -0
- data/tracks/julia/exercises/rotational-cipher/rotational-cipher.jl +0 -0
- data/tracks/julia/exercises/rotational-cipher/runtests.jl +51 -0
- data/tracks/ocaml/config.json +5 -0
- data/tracks/ocaml/exercises/connect/.merlin +3 -0
- data/tracks/ocaml/exercises/connect/Makefile +15 -0
- data/tracks/ocaml/exercises/connect/connect.mli +4 -0
- data/tracks/ocaml/exercises/connect/example.ml +80 -0
- data/tracks/ocaml/exercises/connect/test.ml +121 -0
- data/tracks/ocaml/tools/test-generator/templates/connect/template.ml +23 -0
- data/tracks/python/exercises/all-your-base/all_your_base_test.py +2 -0
- data/tracks/python/exercises/luhn/example.py +5 -8
- data/tracks/python/exercises/luhn/luhn_test.py +34 -24
- data/tracks/ruby/README.md +138 -23
- metadata +16 -4
@@ -0,0 +1,121 @@
|
|
1
|
+
open Core.Std
|
2
|
+
open OUnit2
|
3
|
+
open Connect
|
4
|
+
|
5
|
+
let show_player = function
|
6
|
+
| Some X -> "X"
|
7
|
+
| Some O -> "O"
|
8
|
+
| None -> "None"
|
9
|
+
|
10
|
+
let ae exp got = assert_equal ~printer:show_player exp got
|
11
|
+
|
12
|
+
let tests = [
|
13
|
+
"an empty board has no winner" >::(fun _ctxt ->
|
14
|
+
let board = [
|
15
|
+
". . . . .";
|
16
|
+
" . . . . .";
|
17
|
+
" . . . . .";
|
18
|
+
" . . . . .";
|
19
|
+
" . . . . ."
|
20
|
+
]
|
21
|
+
in
|
22
|
+
ae None (connect board)
|
23
|
+
);
|
24
|
+
"X can win on a 1x1 board" >::(fun _ctxt ->
|
25
|
+
let board = [
|
26
|
+
"X"
|
27
|
+
]
|
28
|
+
in
|
29
|
+
ae (Some X) (connect board)
|
30
|
+
);
|
31
|
+
"O can win on a 1x1 board" >::(fun _ctxt ->
|
32
|
+
let board = [
|
33
|
+
"O"
|
34
|
+
]
|
35
|
+
in
|
36
|
+
ae (Some O) (connect board)
|
37
|
+
);
|
38
|
+
"only edges does not make a winner" >::(fun _ctxt ->
|
39
|
+
let board = [
|
40
|
+
"O O O X";
|
41
|
+
" X . . X";
|
42
|
+
" X . . X";
|
43
|
+
" X O O O"
|
44
|
+
]
|
45
|
+
in
|
46
|
+
ae None (connect board)
|
47
|
+
);
|
48
|
+
"illegal diagonal does not make a winner" >::(fun _ctxt ->
|
49
|
+
let board = [
|
50
|
+
"X O . .";
|
51
|
+
" O X X X";
|
52
|
+
" O X O .";
|
53
|
+
" . O X .";
|
54
|
+
" X X O O"
|
55
|
+
]
|
56
|
+
in
|
57
|
+
ae None (connect board)
|
58
|
+
);
|
59
|
+
"nobody wins crossing adjacent angles" >::(fun _ctxt ->
|
60
|
+
let board = [
|
61
|
+
"X . . .";
|
62
|
+
" . X O .";
|
63
|
+
" O . X O";
|
64
|
+
" . O . X";
|
65
|
+
" . . O ."
|
66
|
+
]
|
67
|
+
in
|
68
|
+
ae None (connect board)
|
69
|
+
);
|
70
|
+
"X wins crossing from left to right" >::(fun _ctxt ->
|
71
|
+
let board = [
|
72
|
+
". O . .";
|
73
|
+
" O X X X";
|
74
|
+
" O X O .";
|
75
|
+
" X X O X";
|
76
|
+
" . O X ."
|
77
|
+
]
|
78
|
+
in
|
79
|
+
ae (Some X) (connect board)
|
80
|
+
);
|
81
|
+
"O wins crossing from top to bottom" >::(fun _ctxt ->
|
82
|
+
let board = [
|
83
|
+
". O . .";
|
84
|
+
" O X X X";
|
85
|
+
" O O O .";
|
86
|
+
" X X O X";
|
87
|
+
" . O X ."
|
88
|
+
]
|
89
|
+
in
|
90
|
+
ae (Some O) (connect board)
|
91
|
+
);
|
92
|
+
"X wins using a convoluted path" >::(fun _ctxt ->
|
93
|
+
let board = [
|
94
|
+
". X X . .";
|
95
|
+
" X . X . X";
|
96
|
+
" . X . X .";
|
97
|
+
" . X X . .";
|
98
|
+
" O O O O O"
|
99
|
+
]
|
100
|
+
in
|
101
|
+
ae (Some X) (connect board)
|
102
|
+
);
|
103
|
+
"X wins using a spiral path" >::(fun _ctxt ->
|
104
|
+
let board = [
|
105
|
+
"O X X X X X X X X";
|
106
|
+
" O X O O O O O O O";
|
107
|
+
" O X O X X X X X O";
|
108
|
+
" O X O X O O O X O";
|
109
|
+
" O X O X X X O X O";
|
110
|
+
" O X O O O X O X O";
|
111
|
+
" O X X X X X O X O";
|
112
|
+
" O O O O O O O X O";
|
113
|
+
" X X X X X X X X O"
|
114
|
+
]
|
115
|
+
in
|
116
|
+
ae (Some X) (connect board)
|
117
|
+
);
|
118
|
+
]
|
119
|
+
|
120
|
+
let () =
|
121
|
+
run_test_tt_main ("connect tests" >::: tests)
|
@@ -0,0 +1,23 @@
|
|
1
|
+
open Core.Std
|
2
|
+
open OUnit2
|
3
|
+
open Connect
|
4
|
+
|
5
|
+
let show_player = function
|
6
|
+
| Some X -> "X"
|
7
|
+
| Some O -> "O"
|
8
|
+
| None -> "None"
|
9
|
+
|
10
|
+
let ae exp got = assert_equal ~printer:show_player exp got
|
11
|
+
|
12
|
+
let tests = [
|
13
|
+
(* TEST
|
14
|
+
"$description" >::(fun _ctxt ->
|
15
|
+
let board = $board
|
16
|
+
in
|
17
|
+
ae $expected (connect board)
|
18
|
+
);
|
19
|
+
END TEST *)
|
20
|
+
]
|
21
|
+
|
22
|
+
let () =
|
23
|
+
run_test_tt_main ("connect tests" >::: tests)
|
@@ -1,11 +1,11 @@
|
|
1
1
|
class Luhn(object):
|
2
|
-
def __init__(self,
|
3
|
-
self.
|
2
|
+
def __init__(self, string):
|
3
|
+
self.string = string.replace(" ", "")
|
4
4
|
|
5
5
|
def addends(self):
|
6
6
|
def luhn_transform(n):
|
7
7
|
return (2 * n - 9) if (n > 4) else (2 * n)
|
8
|
-
old_digits = [int(d) for d in str(self.
|
8
|
+
old_digits = [int(d) for d in str(self.string)]
|
9
9
|
return [(luhn_transform(n) if (i % 2 == 0) else n)
|
10
10
|
for i, n in enumerate(old_digits, start=len(old_digits) % 2)]
|
11
11
|
|
@@ -13,9 +13,6 @@ class Luhn(object):
|
|
13
13
|
return sum(self.addends())
|
14
14
|
|
15
15
|
def is_valid(self):
|
16
|
+
if len(self.string) <= 1 or not self.string.isdigit():
|
17
|
+
return False
|
16
18
|
return self.checksum() % 10 == 0
|
17
|
-
|
18
|
-
@staticmethod
|
19
|
-
def create(n):
|
20
|
-
diff = (10 - Luhn(n * 10).checksum()) % 10
|
21
|
-
return 10 * n + diff
|
@@ -1,46 +1,56 @@
|
|
1
|
-
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
|
2
3
|
import unittest
|
3
4
|
|
4
5
|
from luhn import Luhn
|
5
6
|
|
6
7
|
|
7
8
|
class LuhnTests(unittest.TestCase):
|
8
|
-
def
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
def test_single_digit_strings_can_not_be_valid(self):
|
10
|
+
self.assertFalse(Luhn("1").is_valid())
|
11
|
+
|
12
|
+
def test_a_single_zero_is_invalid(self):
|
13
|
+
self.assertFalse(Luhn("0").is_valid())
|
14
|
+
|
15
|
+
def test_a_simple_valid_SIN_that_remains_valid_if_reversed(self):
|
16
|
+
self.assertTrue(Luhn("059").is_valid())
|
17
|
+
|
18
|
+
def test_a_simple_valid_SIN_that_becomes_invalid_if_reversed(self):
|
19
|
+
self.assertTrue(Luhn("59").is_valid())
|
20
|
+
|
21
|
+
def test_a_valid_Canadian_SIN(self):
|
22
|
+
self.assertTrue(Luhn("055 444 285").is_valid())
|
12
23
|
|
13
|
-
def
|
14
|
-
|
15
|
-
self.assertEqual(Counter([7, 6, 6, 1]),
|
16
|
-
Counter(Luhn(8631).addends()))
|
24
|
+
def test_invalid_Canadian_SIN(self):
|
25
|
+
self.assertFalse(Luhn("055 444 286").is_valid())
|
17
26
|
|
18
|
-
def
|
19
|
-
self.
|
27
|
+
def test_invalid_credit_card(self):
|
28
|
+
self.assertFalse(Luhn("8273 1232 7352 0569").is_valid())
|
20
29
|
|
21
|
-
def
|
22
|
-
self.
|
30
|
+
def test_valid_strings_with_a_non_digit_included_become_invalid(self):
|
31
|
+
self.assertFalse(Luhn("055a 444 285").is_valid())
|
23
32
|
|
24
|
-
def
|
25
|
-
self.assertFalse(Luhn(
|
33
|
+
def test_valid_strings_with_punctuation_included_become_invalid(self):
|
34
|
+
self.assertFalse(Luhn("055-444-285").is_valid())
|
26
35
|
|
27
|
-
def
|
28
|
-
self.
|
36
|
+
def test_valid_strings_with_symbols_included_become_invalid(self):
|
37
|
+
self.assertFalse(Luhn("055£ 444$ 285").is_valid())
|
29
38
|
|
30
|
-
def
|
31
|
-
self.
|
39
|
+
def test_single_zero_with_space_is_invalid(self):
|
40
|
+
self.assertFalse(Luhn("0").is_valid())
|
32
41
|
|
33
|
-
def
|
34
|
-
self.
|
42
|
+
def test_more_than_a_single_zero_is_valid(self):
|
43
|
+
self.assertTrue(Luhn("0000 0").is_valid())
|
35
44
|
|
36
|
-
def
|
37
|
-
self.
|
45
|
+
def test_input_digit_9_is_correctly_converted_to_output_digit_9(self):
|
46
|
+
self.assertTrue(Luhn("091").is_valid())
|
38
47
|
|
39
48
|
def test_is_valid_can_be_called_repeatedly(self):
|
49
|
+
# Additional track specific test case
|
40
50
|
# This test was added, because we saw many implementations
|
41
51
|
# in which the first call to is_valid() worked, but the
|
42
52
|
# second call failed().
|
43
|
-
number = Luhn(
|
53
|
+
number = Luhn("055 444 285")
|
44
54
|
self.assertTrue(number.is_valid())
|
45
55
|
self.assertTrue(number.is_valid())
|
46
56
|
|
data/tracks/ruby/README.md
CHANGED
@@ -65,11 +65,17 @@ Note that flags which have an attached value, like above, must take the form
|
|
65
65
|
### Generated Test Suites
|
66
66
|
|
67
67
|
If you find an `example.tt` file in a problem directory, then the test suite is
|
68
|
-
generated from shared data
|
69
|
-
|
68
|
+
generated from shared data, which can be found in the exercise definition in the [x-common][]
|
69
|
+
repository.
|
70
70
|
|
71
|
-
|
72
|
-
|
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:
|
73
79
|
|
74
80
|
```
|
75
81
|
tree -L 1 ~/code/exercism
|
@@ -77,38 +83,145 @@ tree -L 1 ~/code/exercism
|
|
77
83
|
└── xruby
|
78
84
|
```
|
79
85
|
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
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.
|
85
116
|
|
86
|
-
|
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`.
|
87
120
|
|
88
|
-
|
89
|
-
as the following files in the xruby repository:
|
121
|
+
#### Implementing a Generator
|
90
122
|
|
91
|
-
|
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:
|
95
124
|
|
96
|
-
|
97
|
-
|
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.
|
98
128
|
|
99
|
-
|
100
|
-
`example.tt`.
|
129
|
+
You will not need to touch the top-level script, `bin/generate`.
|
101
130
|
|
102
|
-
|
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.
|
103
133
|
|
104
|
-
|
134
|
+
The `lib/$PROBLEM_cases.rb` file should contain a small class that wraps the JSON for a single test case:
|
105
135
|
|
106
|
-
|
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:
|
107
158
|
|
108
159
|
- `test_name` - Returns the name of the test (i.e `test_one_equals_one`)
|
109
160
|
- `workload` - Returns the main syntax for the test. This will vary depending on the test generator and its underlying implementation
|
110
161
|
- `skipped` - Returns skip syntax (i.e. `skip` or `# skip`)
|
111
162
|
|
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
|
+
|
112
225
|
## Pull Requests
|
113
226
|
|
114
227
|
We welcome pull requests that provide fixes to existing test suites (missing
|
@@ -168,3 +281,5 @@ Copyright (c) 2014 Katrina Owen, _@kytrinyx.com
|
|
168
281
|
|
169
282
|
## Ruby icon
|
170
283
|
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
|