trackler 2.0.3.2 → 2.0.3.3

Sign up to get free protection for your applications and to get access to all the features.
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