trackler 2.0.3.2 → 2.0.3.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/trackler/version.rb +1 -1
- data/tracks/c/config.json +10 -0
- data/tracks/c/exercises/robot-simulator/makefile +16 -0
- data/tracks/c/exercises/robot-simulator/src/example.c +74 -0
- data/tracks/c/exercises/robot-simulator/src/robot_simulator.h +41 -0
- data/tracks/c/exercises/robot-simulator/test/test_robot_simulator.c +165 -0
- data/tracks/c/exercises/robot-simulator/test/vendor/unity.c +1300 -0
- data/tracks/c/exercises/robot-simulator/test/vendor/unity.h +274 -0
- data/tracks/c/exercises/robot-simulator/test/vendor/unity_internals.h +701 -0
- data/tracks/ecmascript/exercises/rna-transcription/example.js +9 -2
- data/tracks/ecmascript/exercises/rna-transcription/rna-transcription.spec.js +18 -0
- data/tracks/go/exercises/hello-world/example.go +4 -2
- data/tracks/go/exercises/hello-world/hello_test.go +4 -4
- data/tracks/go/exercises/hello-world/hello_world.go +6 -6
- data/tracks/groovy/config.json +5 -0
- data/tracks/groovy/exercises/phone-number/Example.groovy +20 -0
- data/tracks/groovy/exercises/phone-number/PhoneNumberSpec.groovy +75 -0
- data/tracks/haskell/exercises/difference-of-squares/test/Tests.hs +2 -0
- data/tracks/java/config.json +13 -1
- data/tracks/java/exercises/all-your-base/build.gradle +17 -0
- data/tracks/java/exercises/all-your-base/src/example/java/BaseConverter.java +80 -0
- data/tracks/java/exercises/all-your-base/src/main/java/BaseConverter.java +5 -0
- data/tracks/java/exercises/all-your-base/src/test/java/BaseConverterTest.java +256 -0
- data/tracks/java/exercises/binary-search/build.gradle +17 -0
- data/tracks/java/exercises/binary-search/src/example/java/BinarySearch.java +40 -0
- data/tracks/java/exercises/binary-search/src/main/java/.keep +0 -0
- data/tracks/java/exercises/binary-search/src/main/java/BinarySearch.java +4 -0
- data/tracks/java/exercises/binary-search/src/test/java/.keep +0 -0
- data/tracks/java/exercises/binary-search/src/test/java/BinarySearchTest.java +136 -0
- data/tracks/java/exercises/settings.gradle +1 -0
- data/tracks/lua/config.json +16 -0
- data/tracks/lua/exercises/say/example.lua +92 -0
- data/tracks/lua/exercises/say/say_spec.lua +105 -0
- data/tracks/lua/exercises/secret-handshake/example.lua +17 -0
- data/tracks/lua/exercises/secret-handshake/secret-handshake_spec.lua +38 -0
- data/tracks/ocaml/config.json +10 -1
- data/tracks/ocaml/exercises/bracket-push/.merlin +3 -0
- data/tracks/ocaml/exercises/bracket-push/Makefile +11 -0
- data/tracks/ocaml/exercises/bracket-push/bracket_push.mli +3 -0
- data/tracks/ocaml/exercises/bracket-push/example.ml +40 -0
- data/tracks/ocaml/exercises/bracket-push/test.ml +38 -0
- data/tracks/ocaml/exercises/leap/test.ml +7 -7
- data/tracks/ocaml/tools/test-generator/Makefile +2 -2
- data/tracks/ocaml/tools/test-generator/interfaces/codegen.mli +2 -2
- data/tracks/ocaml/tools/test-generator/interfaces/{test_generator.mli → controller.mli} +0 -0
- data/tracks/ocaml/tools/test-generator/interfaces/parser.mli +7 -1
- data/tracks/ocaml/tools/test-generator/interfaces/special_cases.mli +2 -2
- data/tracks/ocaml/tools/test-generator/interfaces/utils.mli +4 -0
- data/tracks/ocaml/tools/test-generator/src/codegen.ml +9 -5
- data/tracks/ocaml/tools/test-generator/src/{test_generator.ml → controller.ml} +24 -15
- data/tracks/ocaml/tools/test-generator/src/model.ml +6 -0
- data/tracks/ocaml/tools/test-generator/src/parser.ml +34 -7
- data/tracks/ocaml/tools/test-generator/src/special_cases.ml +11 -4
- data/tracks/ocaml/tools/test-generator/src/template.ml +49 -6
- data/tracks/ocaml/tools/test-generator/src/test_gen.ml +1 -1
- data/tracks/ocaml/tools/test-generator/src/utils.ml +6 -0
- data/tracks/ocaml/tools/test-generator/templates/anagram/template.ml +2 -2
- data/tracks/ocaml/tools/test-generator/templates/bob/template.ml +2 -2
- data/tracks/ocaml/tools/test-generator/templates/bracket-push/template.ml +16 -0
- data/tracks/ocaml/tools/test-generator/templates/difference-of-squares/template.ml +19 -0
- data/tracks/ocaml/tools/test-generator/templates/hamming/template.ml +2 -2
- data/tracks/ocaml/tools/test-generator/templates/hello-world/template.ml +2 -2
- data/tracks/ocaml/tools/test-generator/templates/leap/template.ml +2 -2
- data/tracks/ocaml/tools/test-generator/templates/raindrops/template.ml +2 -2
- data/tracks/ocaml/tools/test-generator/templates/say/template.ml +3 -3
- data/tracks/ocaml/tools/test-generator/templates/word-count/template.ml +2 -2
- data/tracks/ocaml/tools/test-generator/test/all_tests.ml +3 -1
- data/tracks/ocaml/tools/test-generator/test/codegen_test.ml +7 -7
- data/tracks/ocaml/tools/test-generator/test/difference_of_squares.json +67 -0
- data/tracks/ocaml/tools/test-generator/test/hello_world.json +23 -0
- data/tracks/ocaml/tools/test-generator/test/parser_test.ml +30 -12
- data/tracks/ocaml/tools/test-generator/test/sample-suite-template.txt +16 -0
- data/tracks/ocaml/tools/test-generator/test/sample_template.txt +2 -2
- data/tracks/ocaml/tools/test-generator/test/special_cases_test.ml +1 -1
- data/tracks/ocaml/tools/test-generator/test/template_test.ml +40 -6
- data/tracks/perl6/.travis.yml +3 -0
- data/tracks/perl6/docs/RESOURCES.md +1 -1
- data/tracks/perl6/exercises/accumulate/accumulate.t +3 -7
- data/tracks/perl6/exercises/anagram/anagram.t +3 -7
- data/tracks/perl6/exercises/binary/binary.t +3 -7
- data/tracks/perl6/exercises/bob/bob.t +4 -8
- data/tracks/perl6/exercises/grains/grains.t +3 -7
- data/tracks/perl6/exercises/leap/leap.t +3 -7
- data/tracks/perl6/exercises/rna-transcription/rna_transcription.t +4 -8
- data/tracks/perl6/exercises/robot-name/robot.t +4 -8
- data/tracks/perl6/exercises/scrabble-score/scrabble_score.t +3 -7
- data/tracks/perl6/exercises/word-count/word_count.t +3 -8
- data/tracks/r/config.json +10 -0
- data/tracks/r/docs/ABOUT.md +14 -0
- data/tracks/r/exercises/difference-of-squares/difference-of-squares.R +5 -0
- data/tracks/r/exercises/difference-of-squares/example.R +6 -0
- data/tracks/r/exercises/difference-of-squares/test_difference-of-squares.R +23 -0
- data/tracks/r/exercises/hamming/example.R +8 -0
- data/tracks/r/exercises/hamming/hamming.R +4 -0
- data/tracks/r/exercises/hamming/test_hamming.R +87 -0
- data/tracks/ruby/exercises/binary/.version +1 -1
- data/tracks/ruby/exercises/binary/binary_test.rb +17 -19
- data/tracks/ruby/exercises/binary/example.rb +13 -13
- data/tracks/ruby/exercises/isogram/.version +1 -1
- data/tracks/ruby/exercises/isogram/example.rb +1 -1
- data/tracks/ruby/exercises/isogram/isogram_test.rb +16 -30
- data/tracks/ruby/lib/binary_cases.rb +2 -3
- data/tracks/ruby/lib/isogram_cases.rb +1 -5
- data/tracks/scala/exercises/phone-number/HINTS.md +9 -0
- metadata +45 -4
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
local secret_handshake = require 'secret-handshake'
|
|
2
|
+
|
|
3
|
+
describe('secret-handshake', function()
|
|
4
|
+
it('should allow empty handshakes', function()
|
|
5
|
+
assert.are.same({}, secret_handshake(0))
|
|
6
|
+
end)
|
|
7
|
+
|
|
8
|
+
it('should interpret 0b1 as wink', function()
|
|
9
|
+
assert.are.same({ 'wink' }, secret_handshake(tonumber('1', 2)))
|
|
10
|
+
end)
|
|
11
|
+
|
|
12
|
+
it('should interpret 0b10 as double blink', function()
|
|
13
|
+
assert.are.same({ 'double blink' }, secret_handshake(tonumber('10', 2)))
|
|
14
|
+
end)
|
|
15
|
+
|
|
16
|
+
it('should interpret 0b100 as close your eyes', function()
|
|
17
|
+
assert.are.same({ 'close your eyes' }, secret_handshake(tonumber('100', 2)))
|
|
18
|
+
end)
|
|
19
|
+
|
|
20
|
+
it('should interpret 0b1000 as jump', function()
|
|
21
|
+
assert.are.same({ 'jump' }, secret_handshake(tonumber('1000', 2)))
|
|
22
|
+
end)
|
|
23
|
+
|
|
24
|
+
it('should allow multiple handshake primitives to be used together', function()
|
|
25
|
+
assert.are.same({ 'wink', 'double blink' }, secret_handshake(tonumber('11', 2)))
|
|
26
|
+
end)
|
|
27
|
+
|
|
28
|
+
it('should reverse the order of the primitives when 0b10000 is used', function()
|
|
29
|
+
assert.are.same({ 'double blink', 'wink' }, secret_handshake(tonumber('10011', 2)))
|
|
30
|
+
end)
|
|
31
|
+
|
|
32
|
+
it('should allow all primitives to be used together', function()
|
|
33
|
+
assert.are.same(
|
|
34
|
+
{ 'jump', 'close your eyes', 'double blink', 'wink' },
|
|
35
|
+
secret_handshake(tonumber('11111', 2))
|
|
36
|
+
)
|
|
37
|
+
end)
|
|
38
|
+
end)
|
data/tracks/ocaml/config.json
CHANGED
|
@@ -7,7 +7,11 @@
|
|
|
7
7
|
{
|
|
8
8
|
"slug": "hello-world",
|
|
9
9
|
"difficulty": 1,
|
|
10
|
-
"topics": [
|
|
10
|
+
"topics": [
|
|
11
|
+
"Strings",
|
|
12
|
+
"Optional values",
|
|
13
|
+
"Pattern matching"
|
|
14
|
+
]
|
|
11
15
|
},
|
|
12
16
|
{
|
|
13
17
|
"slug": "leap",
|
|
@@ -69,6 +73,11 @@
|
|
|
69
73
|
"difficulty": 3,
|
|
70
74
|
"topics": []
|
|
71
75
|
},
|
|
76
|
+
{
|
|
77
|
+
"slug": "bracket-push",
|
|
78
|
+
"difficulty": 4,
|
|
79
|
+
"topics": ["Stacks"]
|
|
80
|
+
},
|
|
72
81
|
{
|
|
73
82
|
"slug": "bowling",
|
|
74
83
|
"difficulty": 4,
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
open Core.Std
|
|
2
|
+
|
|
3
|
+
type 'a stack = 'a list
|
|
4
|
+
|
|
5
|
+
let push (s: 'a stack) (a: 'a): 'a stack = a :: s
|
|
6
|
+
let pop (s: 'a stack): ('a * 'a stack) option = match s with
|
|
7
|
+
| [] -> None
|
|
8
|
+
| x :: xs -> Some (x, xs)
|
|
9
|
+
|
|
10
|
+
(* this receives a character from the input string s.
|
|
11
|
+
if the character is an opening bracket, then push it on to the stack, and
|
|
12
|
+
return the stack (inside an Option)
|
|
13
|
+
if the character is a closing bracket, then pop the top of the stack, and
|
|
14
|
+
check the popped character matches. If it does, then return the new stack,
|
|
15
|
+
otherwise return None (to indicate matching failure). *)
|
|
16
|
+
let update (s: (char stack) option) (ch: char): (char stack) option =
|
|
17
|
+
match s with
|
|
18
|
+
| None -> None
|
|
19
|
+
| Some s ->
|
|
20
|
+
let pop_matching m = Option.filter (pop s) ~f:(fun (top, _) -> top = m)
|
|
21
|
+
|> Option.map ~f:snd in
|
|
22
|
+
match ch with
|
|
23
|
+
| '(' | '{' | '[' -> Some (push s ch)
|
|
24
|
+
| ')' -> pop_matching '('
|
|
25
|
+
| '}' -> pop_matching '{'
|
|
26
|
+
| ']' -> pop_matching '['
|
|
27
|
+
| _ -> Some s
|
|
28
|
+
|
|
29
|
+
(* The fold loops over the characters of s, repeatedly calling update on a stack
|
|
30
|
+
and each character of s.
|
|
31
|
+
If update ever encounters a non-matching bracket, it returns None, and the
|
|
32
|
+
fold will as well.
|
|
33
|
+
Otherwise, the fold will return a stack after going through all of the string.
|
|
34
|
+
If the stack is non-empty, then some non-matching brackets must remain, so the string
|
|
35
|
+
is not balanced.
|
|
36
|
+
If the stack is empty, everything matches, and the string balances.
|
|
37
|
+
*)
|
|
38
|
+
let are_balanced s =
|
|
39
|
+
List.fold_left (String.to_list s) ~init:(Some []) ~f:update
|
|
40
|
+
|> Option.exists ~f:(List.is_empty)
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
open Core.Std
|
|
2
|
+
open OUnit2
|
|
3
|
+
open Bracket_push
|
|
4
|
+
|
|
5
|
+
let ae exp got _test_ctxt =
|
|
6
|
+
assert_equal exp got ~printer:Bool.to_string
|
|
7
|
+
|
|
8
|
+
let tests = [
|
|
9
|
+
"paired square brackets" >::
|
|
10
|
+
ae true (are_balanced "[]");
|
|
11
|
+
"empty string" >::
|
|
12
|
+
ae true (are_balanced "");
|
|
13
|
+
"unpaired brackets" >::
|
|
14
|
+
ae false (are_balanced "[[");
|
|
15
|
+
"wrong ordered brackets" >::
|
|
16
|
+
ae false (are_balanced "}{");
|
|
17
|
+
"paired with whitespace" >::
|
|
18
|
+
ae true (are_balanced "{ }");
|
|
19
|
+
"simple nested brackets" >::
|
|
20
|
+
ae true (are_balanced "{[]}");
|
|
21
|
+
"several paired brackets" >::
|
|
22
|
+
ae true (are_balanced "{}[]");
|
|
23
|
+
"paired and nested brackets" >::
|
|
24
|
+
ae true (are_balanced "([{}({}[])])");
|
|
25
|
+
"unopened closing brackets" >::
|
|
26
|
+
ae false (are_balanced "{[)][]}");
|
|
27
|
+
"unpaired and nested brackets" >::
|
|
28
|
+
ae false (are_balanced "([{])");
|
|
29
|
+
"paired and wrong nested brackets" >::
|
|
30
|
+
ae false (are_balanced "[({]})");
|
|
31
|
+
"math expression" >::
|
|
32
|
+
ae true (are_balanced "(((185 + 223.85) * 15) - 543)/2");
|
|
33
|
+
"complex latex expression" >::
|
|
34
|
+
ae true (are_balanced "\\left(\\begin{array}{cc} \\frac{1}{3} & x\\\\ \\mathrm{e}^{x} &... x^2 \\end{array}\\right)");
|
|
35
|
+
]
|
|
36
|
+
|
|
37
|
+
let () =
|
|
38
|
+
run_test_tt_main ("bracket-push tests" >::: tests)
|
|
@@ -5,19 +5,19 @@ open Leap
|
|
|
5
5
|
let ae exp got _test_ctxt = assert_equal exp got
|
|
6
6
|
|
|
7
7
|
let tests = [
|
|
8
|
-
"leap year" >::
|
|
8
|
+
"leap year in twentieth century" >::
|
|
9
9
|
ae true (leap_year 1996);
|
|
10
|
-
"standard
|
|
10
|
+
"odd standard year in twentieth century" >::
|
|
11
11
|
ae false (leap_year 1997);
|
|
12
|
-
"standard
|
|
12
|
+
"even standard year in twentieth century" >::
|
|
13
13
|
ae false (leap_year 1998);
|
|
14
|
-
"standard nineteenth century" >::
|
|
14
|
+
"standard year in nineteenth century" >::
|
|
15
15
|
ae false (leap_year 1900);
|
|
16
|
-
"standard eighteenth century" >::
|
|
16
|
+
"standard year in eighteenth century" >::
|
|
17
17
|
ae false (leap_year 1800);
|
|
18
|
-
"leap twenty
|
|
18
|
+
"leap year twenty four hundred" >::
|
|
19
19
|
ae true (leap_year 2400);
|
|
20
|
-
"leap
|
|
20
|
+
"leap year two thousand" >::
|
|
21
21
|
ae true (leap_year 2000);
|
|
22
22
|
]
|
|
23
23
|
|
|
@@ -2,10 +2,10 @@ test: test_gen.native
|
|
|
2
2
|
@./all_tests.native
|
|
3
3
|
|
|
4
4
|
test_gen.native: all_tests.native src/*.ml interfaces/*.mli test/*.ml
|
|
5
|
-
@ocamlbuild -use-ocamlfind -tag thread -tag short_paths -cflags -strict-sequence -r -pkg core -pkg yojson -pkg ppx_deriving -pkg ppx_deriving.eq -pkg ppx_deriving.show -Is
|
|
5
|
+
@ocamlbuild -use-ocamlfind -tag thread -tag short_paths -cflags -strict-sequence -r -pkg core -pkg yojson -pkg ppx_deriving -pkg ppx_deriving.eq -pkg ppx_deriving.show -Is src,interfaces test_gen.native
|
|
6
6
|
|
|
7
7
|
all_tests.native: src/*.ml test/*.ml
|
|
8
|
-
@ocamlbuild -use-ocamlfind -tag thread -tag short_paths -cflags -strict-sequence -r -pkg core -pkg oUnit -pkg yojson -pkg ppx_deriving -pkg ppx_deriving.eq -pkg ppx_deriving.show -Is
|
|
8
|
+
@ocamlbuild -use-ocamlfind -tag thread -tag short_paths -cflags -strict-sequence -r -pkg core -pkg oUnit -pkg yojson -pkg ppx_deriving -pkg ppx_deriving.eq -pkg ppx_deriving.show -Is src,test all_tests.native
|
|
9
9
|
|
|
10
10
|
clean:
|
|
11
11
|
rm -rf _build
|
|
@@ -2,7 +2,7 @@ open Core.Std
|
|
|
2
2
|
|
|
3
3
|
open Model
|
|
4
4
|
|
|
5
|
-
type
|
|
5
|
+
type edit_expected_function = value: parameter -> string
|
|
6
6
|
|
|
7
7
|
type edit_parameters_function = (string * string) list -> (string * string) list
|
|
8
8
|
|
|
@@ -10,4 +10,4 @@ type subst = Subst of string
|
|
|
10
10
|
|
|
11
11
|
val subst_to_string : subst -> string
|
|
12
12
|
|
|
13
|
-
val
|
|
13
|
+
val fill_in_template : edit_expected_function -> edit_parameters_function -> string -> case list -> (subst list, string) Result.t
|
|
File without changes
|
|
@@ -4,6 +4,12 @@ open Model
|
|
|
4
4
|
type error = TopLevelMustHaveKeyCalledCases | ExpectingListOfCases |
|
|
5
5
|
ExpectingMapForCase | BadDescription | BadExpected
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
type test = {name: string; cases: case list} [@@deriving eq, show]
|
|
8
|
+
|
|
9
|
+
type tests =
|
|
10
|
+
| Single of case list
|
|
11
|
+
| Suite of test list [@@deriving eq, show]
|
|
12
|
+
|
|
13
|
+
val parse_json_text : string -> (tests, error) Result.t
|
|
8
14
|
|
|
9
15
|
val show_error : error -> string
|
|
@@ -2,6 +2,6 @@ open Core.Std
|
|
|
2
2
|
|
|
3
3
|
open Model
|
|
4
4
|
|
|
5
|
-
val
|
|
5
|
+
val edit_expected : stringify:(parameter -> string) -> slug:string -> key:string -> value:parameter -> string
|
|
6
6
|
|
|
7
|
-
val
|
|
7
|
+
val edit_parameters : slug: string -> (string * string) list -> (string * string) list
|
|
@@ -9,8 +9,12 @@ val to_list_option : json -> json list option
|
|
|
9
9
|
|
|
10
10
|
val to_list_note : 'e -> json -> ((json list, 'e) Result.t)
|
|
11
11
|
|
|
12
|
+
val to_assoc_note : 'e -> json -> ((json list, 'e) Result.t)
|
|
13
|
+
|
|
12
14
|
val to_string_note : 'e -> json -> ((string, 'e) Result.t)
|
|
13
15
|
|
|
16
|
+
val member_note : 'e -> string -> json -> ((json, 'e) Result.t)
|
|
17
|
+
|
|
14
18
|
val safe_to_int_option : json -> int option
|
|
15
19
|
|
|
16
20
|
val find_arrayi : ?start:int -> 'a array -> f:('a -> bool) -> (int * 'a) option
|
|
@@ -2,7 +2,7 @@ open Core.Std
|
|
|
2
2
|
|
|
3
3
|
open Model
|
|
4
4
|
|
|
5
|
-
type
|
|
5
|
+
type edit_expected_function = value: parameter -> string
|
|
6
6
|
|
|
7
7
|
type edit_parameters_function = (string * string) list -> (string * string) list
|
|
8
8
|
|
|
@@ -10,16 +10,20 @@ type subst = Subst of string [@@deriving eq, show]
|
|
|
10
10
|
|
|
11
11
|
let subst_to_string (Subst s) = s
|
|
12
12
|
|
|
13
|
+
let map_subst (s: subst) ~(f: string -> string): subst =
|
|
14
|
+
Subst (subst_to_string s |> f)
|
|
15
|
+
|
|
13
16
|
let replace_key (key: string) (value: string) (target: string): string =
|
|
14
17
|
let replace = String.substr_replace_all ~with_:value in
|
|
15
18
|
replace ~pattern:("$" ^ key) target |> replace ~pattern:("$(" ^ key ^ ")")
|
|
16
19
|
|
|
17
|
-
let rec replace_keys (f:
|
|
20
|
+
let rec replace_keys (f: edit_expected_function) (ed: edit_parameters_function) (s: string) (suite_name: string) (c: case): subst =
|
|
18
21
|
let s = replace_key "description" c.description s in
|
|
19
|
-
let expected = f ~
|
|
22
|
+
let expected = f ~value:c.expected in
|
|
20
23
|
let s = replace_key "expected" expected s in
|
|
24
|
+
let s = replace_key "suite-name" suite_name s in
|
|
21
25
|
let parameter_strings = ed @@ List.map ~f:(fun (k,p) -> (k,parameter_to_string p)) c.parameters in
|
|
22
26
|
List.fold parameter_strings ~init:(Subst s) ~f:(fun (Subst s) (k,v) -> Subst (replace_key k v s))
|
|
23
27
|
|
|
24
|
-
let
|
|
25
|
-
|
|
28
|
+
let fill_in_template (f: edit_expected_function) (ed: edit_parameters_function) test_template suite_name cases =
|
|
29
|
+
List.map cases ~f:(replace_keys f ed test_template suite_name)
|
|
@@ -21,27 +21,36 @@ let find_canonical_data_files = find_nested_files "canonical-data.json"
|
|
|
21
21
|
let combine_files (template_files: (string * content) list) (canonical_data_files: (string * content) list): (string * content * content) list =
|
|
22
22
|
List.filter_map template_files ~f:(fun (n,t) -> (List.Assoc.find canonical_data_files ~equal:String.equal n |> Option.map ~f:(fun c -> (n,t,c))))
|
|
23
23
|
|
|
24
|
-
let generate_code ~slug ~template_file ~canonical_data_file =
|
|
24
|
+
let generate_code ~(slug: string) ~(template_file: content) ~(canonical_data_file: content): (content, content) Result.t =
|
|
25
25
|
let template = find_template template_file in
|
|
26
|
-
let template = Result.of_option template ("cannot recognize file for " ^ slug ^ " as a template") in
|
|
27
|
-
let cases = parse_json_text canonical_data_file in
|
|
28
|
-
let cases = Result.map_error cases show_error in
|
|
29
26
|
let open Result.Monad_infix in
|
|
30
|
-
template >>= fun template ->
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
(
|
|
36
|
-
|
|
37
|
-
|
|
27
|
+
Result.of_option template ("cannot recognize file for " ^ slug ^ " as a template") >>= fun template ->
|
|
28
|
+
parse_json_text canonical_data_file |> Result.map_error ~f:show_error >>= (function
|
|
29
|
+
| Single cases ->
|
|
30
|
+
Result.return (fill_in_template
|
|
31
|
+
(edit_expected ~stringify:parameter_to_string ~slug)
|
|
32
|
+
(edit_parameters ~slug)
|
|
33
|
+
template.template
|
|
34
|
+
slug
|
|
35
|
+
cases |> fill_tests template);
|
|
36
|
+
| Suite tests ->
|
|
37
|
+
let x = List.map tests ~f:(fun {name;cases} ->
|
|
38
|
+
(name, fill_in_template
|
|
39
|
+
(edit_expected ~stringify:parameter_to_string ~slug)
|
|
40
|
+
(edit_parameters ~slug)
|
|
41
|
+
template.template
|
|
42
|
+
name
|
|
43
|
+
cases)
|
|
44
|
+
) in
|
|
45
|
+
Result.return (fill_suite template x)
|
|
46
|
+
)
|
|
38
47
|
|
|
39
48
|
let output_tests (files: (string * content * content) list) (output_folder: string): unit =
|
|
40
49
|
let output_filepath name = output_folder ^ "/" ^ name ^ "/test.ml" in
|
|
41
50
|
let output1 (slug,t,c) =
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
51
|
+
match generate_code slug t c with
|
|
52
|
+
| Ok code -> Out_channel.write_all (output_filepath slug) code
|
|
53
|
+
| Error e -> print_endline ("Failed when generating " ^ slug ^ ", error: " ^ e) in
|
|
45
54
|
List.iter files ~f:output1
|
|
46
55
|
|
|
47
56
|
let run ~(templates_folder: string) ~(canonical_data_folder: string) ~(output_folder: string) =
|
|
@@ -18,6 +18,12 @@ type case = {
|
|
|
18
18
|
expected: parameter;
|
|
19
19
|
} [@@deriving eq, show]
|
|
20
20
|
|
|
21
|
+
type test = {name: string; cases: case list} [@@deriving eq, show]
|
|
22
|
+
|
|
23
|
+
type tests =
|
|
24
|
+
| Single of case list
|
|
25
|
+
| Suite of test list [@@deriving eq, show]
|
|
26
|
+
|
|
21
27
|
let surround (ch: char) (s: string): string =
|
|
22
28
|
Char.to_string ch ^ s ^ Char.to_string ch
|
|
23
29
|
|
|
@@ -5,9 +5,8 @@ open Yojson.Safe.Util
|
|
|
5
5
|
open Model
|
|
6
6
|
|
|
7
7
|
type error =
|
|
8
|
-
|
|
9
|
-
BadDescription | BadExpected
|
|
10
|
-
[@@deriving eq]
|
|
8
|
+
TestMustHaveKeyCalledCases | ExpectingListOfCases | ExpectingMapForCase |
|
|
9
|
+
BadDescription | BadExpected | UnrecognizedJson [@@deriving eq, show]
|
|
11
10
|
|
|
12
11
|
let to_int_unsafe = function
|
|
13
12
|
| `Int x -> x
|
|
@@ -42,20 +41,48 @@ let parse_case (s: json): (case, error) Result.t = match s with
|
|
|
42
41
|
|
|
43
42
|
let parse_cases (text: string): (json, error) Result.t =
|
|
44
43
|
match from_string text |> member "cases" with
|
|
45
|
-
| `Null -> Error
|
|
44
|
+
| `Null -> Error TestMustHaveKeyCalledCases
|
|
46
45
|
| json -> Ok json
|
|
47
46
|
|
|
48
|
-
let
|
|
47
|
+
let parse_single (text: string): (tests, error) Result.t =
|
|
49
48
|
let open Result.Monad_infix in
|
|
50
49
|
parse_cases text >>=
|
|
51
50
|
to_list_note ExpectingListOfCases >>=
|
|
52
|
-
(sequence >> (List.map ~f:parse_case))
|
|
51
|
+
(sequence >> (List.map ~f:parse_case)) >>= fun ts ->
|
|
52
|
+
Result.return (Single ts)
|
|
53
|
+
|
|
54
|
+
let is_suite (json: json) =
|
|
55
|
+
let keys = List.sort (keys json) ~cmp:String.compare in
|
|
56
|
+
not (List.is_empty keys || keys = ["cases"] || keys = ["#"; "cases"])
|
|
57
|
+
|
|
58
|
+
let merge_result = function
|
|
59
|
+
| (_, Error x) -> Error x
|
|
60
|
+
| (n, Ok c) -> Ok {name = n; cases = c}
|
|
61
|
+
|
|
62
|
+
let parse_cases_from_suite suite =
|
|
63
|
+
let open Result.Monad_infix in
|
|
64
|
+
member_note UnrecognizedJson "cases" suite >>=
|
|
65
|
+
to_list_note UnrecognizedJson >>= fun tests ->
|
|
66
|
+
List.map tests ~f:parse_case |> sequence
|
|
67
|
+
|
|
68
|
+
let parse_json_text (text: string): (tests, error) Result.t =
|
|
69
|
+
let open Result.Monad_infix in
|
|
70
|
+
let json = from_string text in
|
|
71
|
+
if is_suite json
|
|
72
|
+
then
|
|
73
|
+
to_assoc_note UnrecognizedJson json >>= fun tests ->
|
|
74
|
+
Result.return (List.map tests ~f:(fun (name, suite) -> merge_result (name, parse_cases_from_suite suite))) >>= fun tests ->
|
|
75
|
+
sequence tests >>= fun tests ->
|
|
76
|
+
Ok (Suite tests)
|
|
77
|
+
else
|
|
78
|
+
parse_single text
|
|
53
79
|
|
|
54
80
|
let show_error = function
|
|
55
|
-
|
|
|
81
|
+
| TestMustHaveKeyCalledCases -> "Cannot parse this json - " ^
|
|
56
82
|
"expecting an object with a key: 'cases'"
|
|
57
83
|
| ExpectingMapForCase -> "Expected a json map for a test case"
|
|
58
84
|
| ExpectingListOfCases -> "Expected a top level map with key cases, " ^
|
|
59
85
|
"and a list of cases as its value."
|
|
60
86
|
| BadDescription -> "Case is missing a description or it is not a string."
|
|
61
87
|
| BadExpected -> "Case is missing an expected key or it is not a string."
|
|
88
|
+
| UnrecognizedJson -> "Cannot understand this json."
|
|
@@ -25,13 +25,20 @@ let optional_strings ~(f: string -> bool) (parameters: (string * string) list):
|
|
|
25
25
|
else parameter in
|
|
26
26
|
List.map ~f:replace parameters
|
|
27
27
|
|
|
28
|
-
let
|
|
29
|
-
|
|
|
30
|
-
|
|
|
28
|
+
let edit_expected ~(stringify: parameter -> string) ~(slug: string) ~(value: parameter) = match slug with
|
|
29
|
+
| "hamming" -> optional_int ~none:(-1) value
|
|
30
|
+
| "say" -> optional_int_or_string ~none:(-1) value
|
|
31
31
|
| _ -> stringify value
|
|
32
32
|
|
|
33
|
-
let
|
|
33
|
+
let edit_say (ps: (string * string) list) =
|
|
34
|
+
let edit = function
|
|
35
|
+
| ("input", v) -> ("input", if Int.of_string v < 0 then "(" ^ v ^ "L)" else v ^ "L")
|
|
36
|
+
| x -> x in
|
|
37
|
+
List.map ps ~f:edit
|
|
38
|
+
|
|
39
|
+
let edit_parameters ~(slug: string) (parameters: (string * string) list) = match (slug, parameters) with
|
|
34
40
|
| ("hello-world", ps) -> default_value ~key:"name" ~value:"None"
|
|
35
41
|
@@ optional_strings ~f:(fun _x -> true)
|
|
36
42
|
@@ parameters
|
|
43
|
+
| ("say", ps) -> edit_say ps
|
|
37
44
|
| (_, ps) -> ps
|