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.
Files changed (106) hide show
  1. checksums.yaml +4 -4
  2. data/lib/trackler/version.rb +1 -1
  3. data/tracks/c/config.json +10 -0
  4. data/tracks/c/exercises/robot-simulator/makefile +16 -0
  5. data/tracks/c/exercises/robot-simulator/src/example.c +74 -0
  6. data/tracks/c/exercises/robot-simulator/src/robot_simulator.h +41 -0
  7. data/tracks/c/exercises/robot-simulator/test/test_robot_simulator.c +165 -0
  8. data/tracks/c/exercises/robot-simulator/test/vendor/unity.c +1300 -0
  9. data/tracks/c/exercises/robot-simulator/test/vendor/unity.h +274 -0
  10. data/tracks/c/exercises/robot-simulator/test/vendor/unity_internals.h +701 -0
  11. data/tracks/ecmascript/exercises/rna-transcription/example.js +9 -2
  12. data/tracks/ecmascript/exercises/rna-transcription/rna-transcription.spec.js +18 -0
  13. data/tracks/go/exercises/hello-world/example.go +4 -2
  14. data/tracks/go/exercises/hello-world/hello_test.go +4 -4
  15. data/tracks/go/exercises/hello-world/hello_world.go +6 -6
  16. data/tracks/groovy/config.json +5 -0
  17. data/tracks/groovy/exercises/phone-number/Example.groovy +20 -0
  18. data/tracks/groovy/exercises/phone-number/PhoneNumberSpec.groovy +75 -0
  19. data/tracks/haskell/exercises/difference-of-squares/test/Tests.hs +2 -0
  20. data/tracks/java/config.json +13 -1
  21. data/tracks/java/exercises/all-your-base/build.gradle +17 -0
  22. data/tracks/java/exercises/all-your-base/src/example/java/BaseConverter.java +80 -0
  23. data/tracks/java/exercises/all-your-base/src/main/java/BaseConverter.java +5 -0
  24. data/tracks/java/exercises/all-your-base/src/test/java/BaseConverterTest.java +256 -0
  25. data/tracks/java/exercises/binary-search/build.gradle +17 -0
  26. data/tracks/java/exercises/binary-search/src/example/java/BinarySearch.java +40 -0
  27. data/tracks/java/exercises/binary-search/src/main/java/.keep +0 -0
  28. data/tracks/java/exercises/binary-search/src/main/java/BinarySearch.java +4 -0
  29. data/tracks/java/exercises/binary-search/src/test/java/.keep +0 -0
  30. data/tracks/java/exercises/binary-search/src/test/java/BinarySearchTest.java +136 -0
  31. data/tracks/java/exercises/settings.gradle +1 -0
  32. data/tracks/lua/config.json +16 -0
  33. data/tracks/lua/exercises/say/example.lua +92 -0
  34. data/tracks/lua/exercises/say/say_spec.lua +105 -0
  35. data/tracks/lua/exercises/secret-handshake/example.lua +17 -0
  36. data/tracks/lua/exercises/secret-handshake/secret-handshake_spec.lua +38 -0
  37. data/tracks/ocaml/config.json +10 -1
  38. data/tracks/ocaml/exercises/bracket-push/.merlin +3 -0
  39. data/tracks/ocaml/exercises/bracket-push/Makefile +11 -0
  40. data/tracks/ocaml/exercises/bracket-push/bracket_push.mli +3 -0
  41. data/tracks/ocaml/exercises/bracket-push/example.ml +40 -0
  42. data/tracks/ocaml/exercises/bracket-push/test.ml +38 -0
  43. data/tracks/ocaml/exercises/leap/test.ml +7 -7
  44. data/tracks/ocaml/tools/test-generator/Makefile +2 -2
  45. data/tracks/ocaml/tools/test-generator/interfaces/codegen.mli +2 -2
  46. data/tracks/ocaml/tools/test-generator/interfaces/{test_generator.mli → controller.mli} +0 -0
  47. data/tracks/ocaml/tools/test-generator/interfaces/parser.mli +7 -1
  48. data/tracks/ocaml/tools/test-generator/interfaces/special_cases.mli +2 -2
  49. data/tracks/ocaml/tools/test-generator/interfaces/utils.mli +4 -0
  50. data/tracks/ocaml/tools/test-generator/src/codegen.ml +9 -5
  51. data/tracks/ocaml/tools/test-generator/src/{test_generator.ml → controller.ml} +24 -15
  52. data/tracks/ocaml/tools/test-generator/src/model.ml +6 -0
  53. data/tracks/ocaml/tools/test-generator/src/parser.ml +34 -7
  54. data/tracks/ocaml/tools/test-generator/src/special_cases.ml +11 -4
  55. data/tracks/ocaml/tools/test-generator/src/template.ml +49 -6
  56. data/tracks/ocaml/tools/test-generator/src/test_gen.ml +1 -1
  57. data/tracks/ocaml/tools/test-generator/src/utils.ml +6 -0
  58. data/tracks/ocaml/tools/test-generator/templates/anagram/template.ml +2 -2
  59. data/tracks/ocaml/tools/test-generator/templates/bob/template.ml +2 -2
  60. data/tracks/ocaml/tools/test-generator/templates/bracket-push/template.ml +16 -0
  61. data/tracks/ocaml/tools/test-generator/templates/difference-of-squares/template.ml +19 -0
  62. data/tracks/ocaml/tools/test-generator/templates/hamming/template.ml +2 -2
  63. data/tracks/ocaml/tools/test-generator/templates/hello-world/template.ml +2 -2
  64. data/tracks/ocaml/tools/test-generator/templates/leap/template.ml +2 -2
  65. data/tracks/ocaml/tools/test-generator/templates/raindrops/template.ml +2 -2
  66. data/tracks/ocaml/tools/test-generator/templates/say/template.ml +3 -3
  67. data/tracks/ocaml/tools/test-generator/templates/word-count/template.ml +2 -2
  68. data/tracks/ocaml/tools/test-generator/test/all_tests.ml +3 -1
  69. data/tracks/ocaml/tools/test-generator/test/codegen_test.ml +7 -7
  70. data/tracks/ocaml/tools/test-generator/test/difference_of_squares.json +67 -0
  71. data/tracks/ocaml/tools/test-generator/test/hello_world.json +23 -0
  72. data/tracks/ocaml/tools/test-generator/test/parser_test.ml +30 -12
  73. data/tracks/ocaml/tools/test-generator/test/sample-suite-template.txt +16 -0
  74. data/tracks/ocaml/tools/test-generator/test/sample_template.txt +2 -2
  75. data/tracks/ocaml/tools/test-generator/test/special_cases_test.ml +1 -1
  76. data/tracks/ocaml/tools/test-generator/test/template_test.ml +40 -6
  77. data/tracks/perl6/.travis.yml +3 -0
  78. data/tracks/perl6/docs/RESOURCES.md +1 -1
  79. data/tracks/perl6/exercises/accumulate/accumulate.t +3 -7
  80. data/tracks/perl6/exercises/anagram/anagram.t +3 -7
  81. data/tracks/perl6/exercises/binary/binary.t +3 -7
  82. data/tracks/perl6/exercises/bob/bob.t +4 -8
  83. data/tracks/perl6/exercises/grains/grains.t +3 -7
  84. data/tracks/perl6/exercises/leap/leap.t +3 -7
  85. data/tracks/perl6/exercises/rna-transcription/rna_transcription.t +4 -8
  86. data/tracks/perl6/exercises/robot-name/robot.t +4 -8
  87. data/tracks/perl6/exercises/scrabble-score/scrabble_score.t +3 -7
  88. data/tracks/perl6/exercises/word-count/word_count.t +3 -8
  89. data/tracks/r/config.json +10 -0
  90. data/tracks/r/docs/ABOUT.md +14 -0
  91. data/tracks/r/exercises/difference-of-squares/difference-of-squares.R +5 -0
  92. data/tracks/r/exercises/difference-of-squares/example.R +6 -0
  93. data/tracks/r/exercises/difference-of-squares/test_difference-of-squares.R +23 -0
  94. data/tracks/r/exercises/hamming/example.R +8 -0
  95. data/tracks/r/exercises/hamming/hamming.R +4 -0
  96. data/tracks/r/exercises/hamming/test_hamming.R +87 -0
  97. data/tracks/ruby/exercises/binary/.version +1 -1
  98. data/tracks/ruby/exercises/binary/binary_test.rb +17 -19
  99. data/tracks/ruby/exercises/binary/example.rb +13 -13
  100. data/tracks/ruby/exercises/isogram/.version +1 -1
  101. data/tracks/ruby/exercises/isogram/example.rb +1 -1
  102. data/tracks/ruby/exercises/isogram/isogram_test.rb +16 -30
  103. data/tracks/ruby/lib/binary_cases.rb +2 -3
  104. data/tracks/ruby/lib/isogram_cases.rb +1 -5
  105. data/tracks/scala/exercises/phone-number/HINTS.md +9 -0
  106. 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)
@@ -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,3 @@
1
+ PKG core oUnit
2
+ S *
3
+ B build/*
@@ -0,0 +1,11 @@
1
+ test: test.native
2
+ @./test.native
3
+
4
+ test.native: *.ml *.mli
5
+ @corebuild -r -quiet -pkg oUnit test.native
6
+
7
+ clean:
8
+ rm -rf _build
9
+ rm -f test.native
10
+
11
+ .PHONY: clean
@@ -0,0 +1,3 @@
1
+ open Core.Std
2
+
3
+ val are_balanced : string -> bool
@@ -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 and odd year" >::
10
+ "odd standard year in twentieth century" >::
11
11
  ae false (leap_year 1997);
12
- "standard even year" >::
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 fourth century" >::
18
+ "leap year twenty four hundred" >::
19
19
  ae true (leap_year 2400);
20
- "leap y2k" >::
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 "src,interfaces" test_gen.native
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 "src,test" all_tests.native
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 fixup_parameter_function = key: string -> value: parameter -> string
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 generate_code : fixup_parameter_function -> edit_parameters_function -> string -> case list -> (subst list, string) Result.t
13
+ val fill_in_template : edit_expected_function -> edit_parameters_function -> string -> case list -> (subst list, string) Result.t
@@ -4,6 +4,12 @@ open Model
4
4
  type error = TopLevelMustHaveKeyCalledCases | ExpectingListOfCases |
5
5
  ExpectingMapForCase | BadDescription | BadExpected
6
6
 
7
- val parse_json_text : string -> (case list, error) Result.t
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 fixup : stringify:(parameter -> string) -> slug:string -> key:string -> value:parameter -> string
5
+ val edit_expected : stringify:(parameter -> string) -> slug:string -> key:string -> value:parameter -> string
6
6
 
7
- val edit : slug: string -> (string * string) list -> (string * string) list
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 fixup_parameter_function = key: string -> value: parameter -> string
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: fixup_parameter_function) (ed: edit_parameters_function) (s: string) (c: case): subst =
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 ~key:"expected" ~value:c.expected in
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 generate_code (f: fixup_parameter_function) (ed: edit_parameters_function) template cases =
25
- Ok (List.map cases ~f:(replace_keys f ed template))
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
- cases >>= fun cs ->
32
- let substs = Result.ok_or_failwith @@ generate_code (fixup ~stringify:parameter_to_string ~slug) (edit ~slug) template.template cs in
33
- Result.return (fill template substs)
34
-
35
- (* Hack - right now it's generating -1L, but Ocaml needs (-1L) *)
36
- let final_fixup (s: string): string =
37
- String.substr_replace_all s ~pattern:"-1L" ~with_:"(-1L)"
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
- let code = Result.ok_or_failwith @@ generate_code slug t c in
43
- let code = final_fixup code in
44
- Out_channel.write_all (output_filepath slug) code in
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
- TopLevelMustHaveKeyCalledCases | ExpectingListOfCases | ExpectingMapForCase |
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 TopLevelMustHaveKeyCalledCases
44
+ | `Null -> Error TestMustHaveKeyCalledCases
46
45
  | json -> Ok json
47
46
 
48
- let parse_json_text (text: string): (case list, error) Result.t =
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
- | TopLevelMustHaveKeyCalledCases -> "Cannot parse this json - " ^
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 fixup ~(stringify: parameter -> string) ~(slug: string) ~(key: string) ~(value: parameter) = match (slug, key) with
29
- | ("hamming", "expected") -> optional_int ~none:(-1) value
30
- | ("say", "expected") -> optional_int_or_string ~none:(-1) value
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 edit ~(slug: string) (parameters: (string * string) list) = match (slug, parameters) with
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