trackler 2.0.8.17 → 2.0.8.18
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/common/exercises/forth/canonical-data.json +307 -321
- data/common/exercises/largest-series-product/canonical-data.json +139 -122
- data/common/exercises/list-ops/canonical-data.json +162 -141
- data/common/exercises/markdown/canonical-data.json +15 -14
- data/common/exercises/pov/canonical-data.json +264 -116
- data/common/exercises/prime-factors/canonical-data.json +51 -40
- data/common/exercises/rail-fence-cipher/canonical-data.json +56 -44
- data/common/exercises/react/canonical-data.json +18 -4
- data/common/exercises/rectangles/canonical-data.json +16 -1
- data/common/exercises/rotational-cipher/canonical-data.json +81 -67
- data/common/exercises/run-length-encoding/canonical-data.json +89 -71
- data/common/exercises/space-age/canonical-data.json +61 -45
- data/common/exercises/sublist/canonical-data.json +136 -99
- data/common/exercises/transpose/canonical-data.json +207 -194
- data/common/exercises/variable-length-quantity/canonical-data.json +171 -141
- data/lib/trackler/version.rb +1 -1
- data/tracks/csharp/exercises/diamond/HINTS.md +9 -0
- data/tracks/go/README.md +3 -0
- data/tracks/go/exercises/queen-attack/queen_attack_test.go +1 -1
- data/tracks/haskell/exercises/hamming/src/Hamming.hs +2 -1
- data/tracks/java/exercises/flatten-array/src/test/java/FlattenerTest.java +5 -0
- data/tracks/ocaml/exercises/anagram/test.ml +2 -2
- data/tracks/ocaml/tools/test-generator/src/controller.ml +7 -2
- data/tracks/ocaml/tools/test-generator/src/parser.ml +31 -18
- data/tracks/ocaml/tools/test-generator/src/template.ml +10 -8
- data/tracks/ocaml/tools/test-generator/src/utils.ml +11 -0
- data/tracks/ocaml/tools/test-generator/templates/phone-number/template.ml +4 -5
- data/tracks/ocaml/tools/test-generator/test/beer-song.json +77 -0
- data/tracks/ocaml/tools/test-generator/test/difference_of_squares.json +76 -62
- data/tracks/ocaml/tools/test-generator/test/parser_test.ml +11 -9
- data/tracks/ocaml/tools/test-generator/test/template_test.ml +2 -2
- metadata +4 -2
@@ -1,147 +1,177 @@
|
|
1
1
|
{
|
2
|
-
"
|
2
|
+
"exercise": "variable-length-quantity",
|
3
|
+
"version": "1.0.0",
|
4
|
+
"comments": [
|
3
5
|
"JSON doesn't allow hexadecimal literals.",
|
4
6
|
"All numbers are given as decimal literals instead.",
|
5
7
|
"It is highly recommended that your track's test generator display all numbers as hexadecimal literals."
|
6
8
|
],
|
7
|
-
"
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
9
|
+
"cases": [
|
10
|
+
{
|
11
|
+
"description": "Encode a series of integers, producing a series of bytes.",
|
12
|
+
"cases": [
|
13
|
+
{
|
14
|
+
"description": "zero",
|
15
|
+
"property": "encode",
|
16
|
+
"input": [0],
|
17
|
+
"expected": [0]
|
18
|
+
},
|
19
|
+
{
|
20
|
+
"description": "arbitrary single byte",
|
21
|
+
"property": "encode",
|
22
|
+
"input": [64],
|
23
|
+
"expected": [64]
|
24
|
+
},
|
25
|
+
{
|
26
|
+
"description": "largest single byte",
|
27
|
+
"property": "encode",
|
28
|
+
"input": [127],
|
29
|
+
"expected": [127]
|
30
|
+
},
|
31
|
+
{
|
32
|
+
"description": "smallest double byte",
|
33
|
+
"property": "encode",
|
34
|
+
"input": [128],
|
35
|
+
"expected": [129,0]
|
36
|
+
},
|
37
|
+
{
|
38
|
+
"description": "arbitrary double byte",
|
39
|
+
"property": "encode",
|
40
|
+
"input": [8192],
|
41
|
+
"expected": [192, 0]
|
42
|
+
},
|
43
|
+
{
|
44
|
+
"description": "largest double byte",
|
45
|
+
"property": "encode",
|
46
|
+
"input": [16383],
|
47
|
+
"expected": [255, 127]
|
48
|
+
},
|
49
|
+
{
|
50
|
+
"description": "smallest triple byte",
|
51
|
+
"property": "encode",
|
52
|
+
"input": [16384],
|
53
|
+
"expected": [129, 128, 0]
|
54
|
+
},
|
55
|
+
{
|
56
|
+
"description": "arbitrary triple byte",
|
57
|
+
"property": "encode",
|
58
|
+
"input": [1048576],
|
59
|
+
"expected": [192, 128, 0]
|
60
|
+
},
|
61
|
+
{
|
62
|
+
"description": "largest triple byte",
|
63
|
+
"property": "encode",
|
64
|
+
"input": [2097151],
|
65
|
+
"expected": [255, 255, 127]
|
66
|
+
},
|
67
|
+
{
|
68
|
+
"description": "smallest quadruple byte",
|
69
|
+
"property": "encode",
|
70
|
+
"input": [2097152],
|
71
|
+
"expected": [129, 128, 128, 0]
|
72
|
+
},
|
73
|
+
{
|
74
|
+
"description": "arbitrary quadruple byte",
|
75
|
+
"property": "encode",
|
76
|
+
"input": [134217728],
|
77
|
+
"expected": [192, 128, 128, 0]
|
78
|
+
},
|
79
|
+
{
|
80
|
+
"description": "largest quadruple byte",
|
81
|
+
"property": "encode",
|
82
|
+
"input": [268435455],
|
83
|
+
"expected": [255, 255, 255, 127]
|
84
|
+
},
|
85
|
+
{
|
86
|
+
"description": "smallest quintuple byte",
|
87
|
+
"property": "encode",
|
88
|
+
"input": [268435456],
|
89
|
+
"expected": [129, 128, 128, 128, 0]
|
90
|
+
},
|
91
|
+
{
|
92
|
+
"description": "arbitrary quintuple byte",
|
93
|
+
"property": "encode",
|
94
|
+
"input": [4278190080],
|
95
|
+
"expected": [143, 248, 128, 128, 0]
|
96
|
+
},
|
97
|
+
{
|
98
|
+
"description": "maximum 32-bit integer input",
|
99
|
+
"property": "encode",
|
100
|
+
"input": [4294967295],
|
101
|
+
"expected": [143, 255, 255, 255, 127]
|
102
|
+
},
|
103
|
+
{
|
104
|
+
"description": "two single-byte values",
|
105
|
+
"property": "encode",
|
106
|
+
"input": [64, 127],
|
107
|
+
"expected": [64, 127]
|
108
|
+
},
|
109
|
+
{
|
110
|
+
"description": "two multi-byte values",
|
111
|
+
"property": "encode",
|
112
|
+
"input": [16384, 1193046],
|
113
|
+
"expected": [129, 128, 0, 200, 232, 86]
|
114
|
+
},
|
115
|
+
{
|
116
|
+
"description": "many multi-byte values",
|
117
|
+
"property": "encode",
|
118
|
+
"input": [8192, 1193046, 268435455, 0, 16383, 16384],
|
119
|
+
"expected": [192, 0, 200, 232, 86, 255, 255, 255, 127, 0, 255, 127, 129, 128, 0]
|
120
|
+
}
|
121
|
+
]
|
122
|
+
},
|
123
|
+
{
|
124
|
+
"description": "Decode a series of bytes, producing a series of integers.",
|
125
|
+
"cases": [
|
126
|
+
{
|
127
|
+
"description": "one byte",
|
128
|
+
"property": "decode",
|
129
|
+
"input": [127],
|
130
|
+
"expected": [127]
|
131
|
+
},
|
132
|
+
{
|
133
|
+
"description": "two bytes",
|
134
|
+
"property": "decode",
|
135
|
+
"input": [192, 0],
|
136
|
+
"expected": [8192]
|
137
|
+
},
|
138
|
+
{
|
139
|
+
"description": "three bytes",
|
140
|
+
"property": "decode",
|
141
|
+
"input": [255, 255, 127],
|
142
|
+
"expected": [2097151]
|
143
|
+
},
|
144
|
+
{
|
145
|
+
"description": "four bytes",
|
146
|
+
"property": "decode",
|
147
|
+
"input": [129, 128, 128, 0],
|
148
|
+
"expected": [2097152]
|
149
|
+
},
|
150
|
+
{
|
151
|
+
"description": "maximum 32-bit integer",
|
152
|
+
"property": "decode",
|
153
|
+
"input": [143, 255, 255, 255, 127],
|
154
|
+
"expected": [4294967295]
|
155
|
+
},
|
156
|
+
{
|
157
|
+
"description": "incomplete sequence causes error",
|
158
|
+
"property": "decode",
|
159
|
+
"input": [255],
|
160
|
+
"expected": null
|
161
|
+
},
|
162
|
+
{
|
163
|
+
"description": "incomplete sequence causes error, even if value is zero",
|
164
|
+
"property": "decode",
|
165
|
+
"input": [128],
|
166
|
+
"expected": null
|
167
|
+
},
|
168
|
+
{
|
169
|
+
"description": "multiple values",
|
170
|
+
"property": "decode",
|
171
|
+
"input": [192, 0, 200, 232, 86, 255, 255, 255, 127, 0, 255, 127, 129, 128, 0],
|
172
|
+
"expected": [8192, 1193046, 268435455, 0, 16383, 16384]
|
173
|
+
}
|
174
|
+
]
|
175
|
+
}
|
176
|
+
]
|
147
177
|
}
|
data/lib/trackler/version.rb
CHANGED
@@ -0,0 +1,9 @@
|
|
1
|
+
## Hints
|
2
|
+
The tests in this exercise are different from your usual tests. Normally, a test checks if for a given input, the output matches the expected value. This is called *value-based testing*. However, this exercise uses *property-based testing*, where the tests check if for a range of inputs, the output has a specific property. The two key differences that differentiate property-based testing from value-based testing are:
|
3
|
+
|
4
|
+
1. A property-based test works not with a single input value, but with many.
|
5
|
+
1. A property-based test verifies properties, not concrete values.
|
6
|
+
|
7
|
+
For this exercise, the tests all verify a property of the diamond shape your code should be producing. Furthermore, all tests check if the property they test holds for all valid input letters ('A' to 'Z').
|
8
|
+
|
9
|
+
For more information on property-based testing, see [this article](http://www.erikschierboom.com/2016/02/22/property-based-testing/).
|
data/tracks/go/README.md
CHANGED
@@ -26,6 +26,9 @@ Test your clone by cding to the xgo directory and typing
|
|
26
26
|
Note that unlike most other Go code, it is not necessary to clone this to your GOPATH.
|
27
27
|
This is because this repo only imports from the standard library and isn't expected to be imported by other packages.
|
28
28
|
|
29
|
+
There is a [misspelling tool](https://github.com/client9/misspell). You can install and occasionally run it to
|
30
|
+
find low hanging typo problems. [#570](https://github.com/exercism/xgo/pull/570) It's not added into CI since it could give false positives.
|
31
|
+
|
29
32
|
## Contributing Guide
|
30
33
|
|
31
34
|
Please be familiar with the [contributing guide](https://github.com/exercism/x-common/blob/master/CONTRIBUTING.md)
|
@@ -33,7 +33,7 @@ var tests = []struct {
|
|
33
33
|
|
34
34
|
func TestTestVersion(t *testing.T) {
|
35
35
|
if testVersion != targetTestVersion {
|
36
|
-
t.
|
36
|
+
t.Fatalf("Found testVersion = %v, want %v.", testVersion, targetTestVersion)
|
37
37
|
}
|
38
38
|
}
|
39
39
|
|
@@ -1,5 +1,6 @@
|
|
1
1
|
import org.junit.Before;
|
2
2
|
import org.junit.Test;
|
3
|
+
import org.junit.Ignore;
|
3
4
|
|
4
5
|
import static java.util.Arrays.asList;
|
5
6
|
import static java.util.Collections.emptyList;
|
@@ -32,6 +33,7 @@ public final class FlattenerTest {
|
|
32
33
|
8)));
|
33
34
|
}
|
34
35
|
|
36
|
+
@Ignore
|
35
37
|
@Test
|
36
38
|
public void testFiveLevelsOfNestingWithNoNulls() {
|
37
39
|
assertEquals(
|
@@ -54,6 +56,7 @@ public final class FlattenerTest {
|
|
54
56
|
"-2")));
|
55
57
|
}
|
56
58
|
|
59
|
+
@Ignore
|
57
60
|
@Test
|
58
61
|
public void testSixLevelsOfNestingWithNoNulls() {
|
59
62
|
assertEquals(
|
@@ -76,6 +79,7 @@ public final class FlattenerTest {
|
|
76
79
|
"8")));
|
77
80
|
}
|
78
81
|
|
82
|
+
@Ignore
|
79
83
|
@Test
|
80
84
|
public void testSixLevelsOfNestingWithNulls() {
|
81
85
|
assertEquals(
|
@@ -99,6 +103,7 @@ public final class FlattenerTest {
|
|
99
103
|
"negative two")));
|
100
104
|
}
|
101
105
|
|
106
|
+
@Ignore
|
102
107
|
@Test
|
103
108
|
public void testNestedListsFullOfNullsOnly() {
|
104
109
|
assertEquals(
|
@@ -13,13 +13,13 @@ let tests = [
|
|
13
13
|
ae ["tan"] (anagrams "ant" ["tan"; "stand"; "at"]);
|
14
14
|
"does not detect false positives" >::
|
15
15
|
ae [] (anagrams "galea" ["eagle"]);
|
16
|
-
"detects
|
16
|
+
"detects two anagrams" >::
|
17
17
|
ae ["stream"; "maters"] (anagrams "master" ["stream"; "pigeon"; "maters"]);
|
18
18
|
"does not detect anagram subsets" >::
|
19
19
|
ae [] (anagrams "good" ["dog"; "goody"]);
|
20
20
|
"detects anagram" >::
|
21
21
|
ae ["inlets"] (anagrams "listen" ["enlists"; "google"; "inlets"; "banana"]);
|
22
|
-
"detects
|
22
|
+
"detects three anagrams" >::
|
23
23
|
ae ["gallery"; "regally"; "largely"] (anagrams "allergy" ["gallery"; "ballerina"; "regally"; "clergy"; "largely"; "leading"]);
|
24
24
|
"does not detect identical words" >::
|
25
25
|
ae ["cron"] (anagrams "corn" ["corn"; "dark"; "Corn"; "rank"; "CORN"; "cron"; "park"]);
|
@@ -22,6 +22,12 @@ let find_canonical_data_files = find_nested_files "canonical-data.json"
|
|
22
22
|
let combine_files (template_files: (string * content) list) (canonical_data_files: (string * content) list): (string * content * content) list =
|
23
23
|
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))))
|
24
24
|
|
25
|
+
(* pangram in the canonical data is a suite but it does not really need to be as there's only one group. Convert a Suite to
|
26
|
+
a Single test in this case, to simplify the template. *)
|
27
|
+
let simplify_single_test_suite tests = match tests with
|
28
|
+
| Suite [{name = name; cases = cases}] -> Single cases
|
29
|
+
| x -> x
|
30
|
+
|
25
31
|
let generate_code ~(slug: string) ~(template_file: content) ~(canonical_data_file: content): (content, content) Result.t =
|
26
32
|
let template = find_template template_file in
|
27
33
|
let edit_expected = edit_expected ~stringify:json_to_string ~slug in
|
@@ -30,7 +36,7 @@ let generate_code ~(slug: string) ~(template_file: content) ~(canonical_data_fil
|
|
30
36
|
let open Result.Monad_infix in
|
31
37
|
Result.of_option template ("cannot recognize file for " ^ slug ^ " as a template") >>= fun template ->
|
32
38
|
parse_json_text canonical_data_file (expected_key_name slug) (cases_name slug)
|
33
|
-
|> Result.map_error ~f:show_error >>= (function
|
39
|
+
|> Result.map_error ~f:show_error >>| simplify_single_test_suite >>= (function
|
34
40
|
| Single cases ->
|
35
41
|
fill_in_template template.template slug cases
|
36
42
|
|> fill_tests template
|
@@ -38,7 +44,6 @@ let generate_code ~(slug: string) ~(template_file: content) ~(canonical_data_fil
|
|
38
44
|
| Suite tests ->
|
39
45
|
List.map tests ~f:(fun {name;cases} -> (name, fill_in_template template.template name cases))
|
40
46
|
|> fill_suite template
|
41
|
-
|> Result.return
|
42
47
|
)
|
43
48
|
|
44
49
|
let output_tests (files: (string * content * content) list) (output_folder: string) ~(generated_folder: string): unit =
|
@@ -24,7 +24,7 @@ let parse_case (expected_key: string) (s: json): (case, error) Result.t = match
|
|
24
24
|
|
25
25
|
let parse_cases (text: string) (cases_key: string): (json, error) Result.t =
|
26
26
|
match from_string text |> member cases_key with
|
27
|
-
| `Null -> Error (TestMustHaveKeyCalledCases
|
27
|
+
| `Null -> Error (TestMustHaveKeyCalledCases cases_key)
|
28
28
|
| json -> Ok json
|
29
29
|
|
30
30
|
let parse_single (text: string) (expected_key: string) (cases_key: string): (tests, error) Result.t =
|
@@ -34,15 +34,34 @@ let parse_single (text: string) (expected_key: string) (cases_key: string): (tes
|
|
34
34
|
(sequence >> (List.map ~f:(parse_case expected_key))) >>= fun ts ->
|
35
35
|
Result.return (Single ts)
|
36
36
|
|
37
|
-
let
|
38
|
-
let
|
39
|
-
|
40
|
-
let
|
41
|
-
|
37
|
+
let rec to_cases case: (case list, error) Result.t =
|
38
|
+
let open Result.Monad_infix in
|
39
|
+
find_note case "description" NoDescription >>= to_string_note BadDescription >>= fun desc ->
|
40
|
+
let cases = List.Assoc.find case "cases" in
|
41
|
+
match cases with
|
42
|
+
| Some cases -> to_list_note UnrecognizedJson cases >>= fun cases ->
|
43
|
+
List.map cases ~f:(to_assoc_note UnrecognizedJson) |> sequence >>= fun x ->
|
44
|
+
List.map x ~f:to_cases |> sequence |> Result.map ~f:List.concat
|
45
|
+
| None ->
|
46
|
+
find_note case "expected" (NoExpected "expected") >>= fun expected ->
|
47
|
+
Result.return [{description = desc; parameters = case; expected = expected}]
|
48
|
+
|
49
|
+
let convert_cases_description_to_name desc =
|
50
|
+
String.lowercase desc |> String.substr_replace_all ~pattern:" " ~with_:"_"
|
42
51
|
|
43
|
-
let
|
44
|
-
|
45
|
-
|
52
|
+
let suite_case json: (test, error) Result.t =
|
53
|
+
let open Result.Monad_infix in
|
54
|
+
to_assoc_note UnrecognizedJson json >>= fun case ->
|
55
|
+
find_note case "description" NoDescription >>= to_string_note BadDescription >>= fun desc ->
|
56
|
+
find_note case "cases" ExpectingListOfCases >>= to_list_note ExpectingListOfCases >>= fun case_assocs ->
|
57
|
+
List.map ~f:(to_assoc_note ExpectingMapForCase) case_assocs |> sequence >>= fun cases ->
|
58
|
+
List.map cases ~f:to_cases |> sequence >>= fun cases ->
|
59
|
+
Result.return {name = convert_cases_description_to_name desc; cases = List.concat cases}
|
60
|
+
|
61
|
+
let suite_cases (json: json) (cases_key: string): (test list, error) Result.t =
|
62
|
+
let open Result.Monad_infix in
|
63
|
+
(member cases_key json |> to_list_note ExpectingListOfCases) >>= fun assoc_cases ->
|
64
|
+
List.map ~f:suite_case assoc_cases |> sequence
|
46
65
|
|
47
66
|
let parse_cases_from_suite name suite expected_key cases_key =
|
48
67
|
let open Result.Monad_infix in
|
@@ -53,15 +72,9 @@ let parse_cases_from_suite name suite expected_key cases_key =
|
|
53
72
|
let parse_json_text (text: string) (expected_key: string) (cases_key: string): (tests, error) Result.t =
|
54
73
|
let open Result.Monad_infix in
|
55
74
|
let json = from_string text in
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
let tests = List.filter tests ~f:(fun (n, _) -> n <> "#") in
|
60
|
-
let tests = List.map tests ~f:(fun (name, suite) -> merge_result (name, parse_cases_from_suite name suite expected_key cases_key)) in
|
61
|
-
sequence tests >>= fun tests ->
|
62
|
-
Ok (Suite tests)
|
63
|
-
else
|
64
|
-
parse_single text expected_key cases_key
|
75
|
+
match suite_cases json cases_key with
|
76
|
+
| Ok suite_cases -> Ok (Suite suite_cases)
|
77
|
+
| Error _ -> parse_single text expected_key cases_key
|
65
78
|
|
66
79
|
let show_error = function
|
67
80
|
| TestMustHaveKeyCalledCases name -> "Test named '" ^ name ^ "' is expected to have an object with a key: 'cases'"
|
@@ -40,21 +40,23 @@ let fill_tests (template: t) (substs: subst list): string =
|
|
40
40
|
let join = String.concat_array ~sep:"\n" in
|
41
41
|
String.concat [join before; join subst; join after] ~sep:"\n"
|
42
42
|
|
43
|
-
let fill_single_suite (template: t) (suite_substs: string * subst list): string =
|
43
|
+
let fill_single_suite (template: t) (suite_substs: string * subst list): (string, string) Result.t =
|
44
|
+
let open Result.Monad_infix in
|
44
45
|
let (suite_name, substs) = suite_substs in
|
45
|
-
|
46
|
-
|
46
|
+
Result.of_option template.suite_name_line ~error:"no suite name" >>= fun suite_name_line ->
|
47
|
+
Result.of_option template.suite_end ~error:"no suite end" >>= fun suite_end_line ->
|
47
48
|
let lines = String.split_lines template.file_text |> Fn.flip List.drop suite_name_line |> List.to_array in
|
48
49
|
Array.replace lines 0 ~f:(String.substr_replace_all ~pattern:"(* SUITE *)$(suite_name)_tests" ~with_:(suite_name ^ "_tests"));
|
49
50
|
let before = Array.slice lines 0 (template.start - suite_name_line) in
|
50
51
|
let subst = Array.of_list (List.map ~f:subst_to_string substs) in
|
51
52
|
let after = Array.slice lines (template.finish - suite_name_line + 1) (suite_end_line - suite_name_line) in
|
52
53
|
let join = String.concat_array ~sep:"\n" in
|
53
|
-
String.concat [join before; join subst; join after] ~sep:"\n"
|
54
|
+
Result.return @@ (String.concat [join before; join subst; join after] ~sep:"\n") ^ "\n"
|
54
55
|
|
55
|
-
let fill_suite (template: t) (suite_substs: (string * subst list) list): string =
|
56
|
-
let
|
57
|
-
|
56
|
+
let fill_suite (template: t) (suite_substs: (string * subst list) list): (string, string) Result.t =
|
57
|
+
let open Result.Monad_infix in
|
58
|
+
List.map suite_substs ~f:(fun x -> (fill_single_suite template x)) |> sequence >>= fun fills ->
|
59
|
+
Result.of_option template.suite_name_line "no suite name line" >>= fun suite_name_line ->
|
58
60
|
let lines = String.split_lines template.file_text |> List.to_array in
|
59
61
|
let before = Array.slice lines 0 suite_name_line in
|
60
62
|
let subst = Array.of_list fills in
|
@@ -62,4 +64,4 @@ let fill_suite (template: t) (suite_substs: (string * subst list) list): string
|
|
62
64
|
let join = String.concat_array ~sep:"\n" in
|
63
65
|
let generated = String.concat [join before; join subst; join after] ~sep:"\n" in
|
64
66
|
let all_suite_names = String.concat ~sep:"; " @@ List.map ~f:(fun (s,_) -> s ^ "_tests") suite_substs in
|
65
|
-
String.substr_replace_all generated ~pattern:"(* suite-all-names *)" ~with_:all_suite_names
|
67
|
+
Result.return @@ String.substr_replace_all generated ~pattern:"(* suite-all-names *)" ~with_:all_suite_names
|
@@ -27,15 +27,26 @@ let to_list_note error json =
|
|
27
27
|
let to_assoc_note error json =
|
28
28
|
try Ok (to_assoc json) with Type_error _ -> Error error
|
29
29
|
|
30
|
+
let to_assoc_option json =
|
31
|
+
try Some (to_assoc json) with Type_error _ -> None
|
32
|
+
|
30
33
|
let to_string_note error json =
|
31
34
|
try Ok (to_string json) with Type_error _ -> Error error
|
32
35
|
|
36
|
+
let to_string_option json =
|
37
|
+
try Some (to_string json) with _ -> None
|
38
|
+
|
33
39
|
let safe_to_int_option json =
|
34
40
|
try Some (to_int json) with Type_error _ -> None
|
35
41
|
|
36
42
|
let member_note error m json =
|
37
43
|
try Ok (member m json) with Type_error _ -> Error error
|
38
44
|
|
45
|
+
let find_note (xs: ('a, 'b) List.Assoc.t) (key: 'a) (error: 'e): ('b, 'e) Result.t =
|
46
|
+
match List.Assoc.find xs key with
|
47
|
+
| Some v -> Ok v
|
48
|
+
| None -> Error error
|
49
|
+
|
39
50
|
let (>>) f g = Fn.compose f g
|
40
51
|
|
41
52
|
let find_arrayi ?start:(start = 0) xs ~f =
|
@@ -9,13 +9,12 @@ let option_to_string f = function
|
|
9
9
|
let ae exp got _test_ctxt =
|
10
10
|
assert_equal ~printer:(option_to_string String.to_string) exp got
|
11
11
|
|
12
|
-
let
|
12
|
+
let tests = [
|
13
13
|
(* TEST
|
14
|
-
|
15
|
-
|
16
|
-
|
14
|
+
"$description" >::
|
15
|
+
ae $expected (number $phrase);
|
16
|
+
END TEST *)
|
17
17
|
]
|
18
|
-
(* END SUITE *)
|
19
18
|
|
20
19
|
let () =
|
21
20
|
run_test_tt_main ("phone-number tests" >::: number_tests)
|