trackler 2.2.1.24 → 2.2.1.25
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/trackler/version.rb +1 -1
- data/problem-specifications/TOPICS.txt +73 -73
- data/tracks/bash/config.json +82 -73
- data/tracks/bash/config/maintainers.json +9 -9
- data/tracks/bash/exercises/nucleotide-count/README.md +38 -0
- data/tracks/bash/exercises/nucleotide-count/example.sh +38 -0
- data/tracks/bash/exercises/nucleotide-count/nucleotide_count_test.sh +29 -0
- data/tracks/csharp/.travis.yml +9 -2
- data/tracks/csharp/build.cake +7 -34
- data/tracks/csharp/build.ps1 +2 -2
- data/tracks/csharp/build.sh +2 -2
- data/tracks/csharp/docs/GENERATORS.md +102 -0
- data/tracks/csharp/exercises/Exercises.sln +669 -0
- data/tracks/csharp/exercises/queen-attack/Example.cs +14 -1
- data/tracks/csharp/exercises/queen-attack/QueenAttack.cs +6 -1
- data/tracks/csharp/exercises/queen-attack/QueenAttackTest.cs +57 -19
- data/tracks/csharp/generators/Exercises/QueenAttack.cs +85 -0
- data/tracks/d/README.md +3 -2
- data/tracks/d/docs/SNIPPET.txt +7 -0
- data/tracks/d/exercises/crypto-square/.dub/dub.json +6 -0
- data/tracks/d/exercises/crypto-square/example/crypto_square.d +4 -2
- data/tracks/dart/exercises/bob/pubspec.lock +28 -16
- data/tracks/dart/exercises/bob/pubspec.yaml +1 -1
- data/tracks/dart/exercises/bob/test/bob_test.dart +2 -0
- data/tracks/dart/exercises/difference-of-squares/pubspec.lock +28 -16
- data/tracks/dart/exercises/difference-of-squares/pubspec.yaml +1 -1
- data/tracks/dart/exercises/gigasecond/pubspec.lock +28 -16
- data/tracks/dart/exercises/gigasecond/pubspec.yaml +1 -1
- data/tracks/dart/exercises/hamming/pubspec.lock +28 -16
- data/tracks/dart/exercises/hamming/pubspec.yaml +1 -1
- data/tracks/dart/exercises/hello-world/pubspec.lock +28 -16
- data/tracks/dart/exercises/hello-world/pubspec.yaml +1 -1
- data/tracks/dart/exercises/hello-world/test/hello_world_test.dart +13 -15
- data/tracks/dart/exercises/leap/pubspec.lock +28 -16
- data/tracks/dart/exercises/leap/pubspec.yaml +1 -1
- data/tracks/dart/exercises/rna-transcription/pubspec.lock +28 -16
- data/tracks/dart/exercises/rna-transcription/pubspec.yaml +1 -1
- data/tracks/elixir/exercises/pig-latin/example.exs +17 -18
- data/tracks/elixir/exercises/pig-latin/pig_latin_test.exs +16 -1
- data/tracks/fsharp/build.cake +0 -2
- data/tracks/go/exercises/error-handling/.meta/description.md +39 -0
- data/tracks/go/exercises/error-handling/.meta/hints.md +5 -0
- data/tracks/go/exercises/error-handling/.meta/metadata.yml +2 -0
- data/tracks/go/exercises/error-handling/README.md +43 -5
- data/tracks/go/exercises/error-handling/common.go +4 -3
- data/tracks/go/exercises/error-handling/error_handling_test.go +1 -38
- data/tracks/go/exercises/secret-handshake/secret_handshake_test.go +3 -1
- data/tracks/haxe/docs/LEARNING.md +8 -0
- data/tracks/java/.gitignore +2 -1
- data/tracks/java/config.json +337 -491
- data/tracks/java/config/maintainers.json +26 -26
- data/tracks/java/exercises/bob/src/test/java/BobTest.java +7 -1
- data/tracks/java/exercises/rna-transcription/src/example/java/RnaTranscription.java +4 -2
- data/tracks/java/exercises/rna-transcription/src/main/java/RnaTranscription.java +5 -3
- data/tracks/java/exercises/rna-transcription/src/test/java/RnaTranscriptionTest.java +21 -26
- data/tracks/javascript/config.json +662 -664
- data/tracks/javascript/config/maintainers.json +35 -35
- data/tracks/javascript/exercises/simple-cipher/simple-cipher.spec.js +7 -3
- data/tracks/plsql/config.json +52 -66
- data/tracks/plsql/docs/SNIPPET.txt +6 -0
- data/tracks/plsql/exercises/hello-world/example.plsql +20 -0
- data/tracks/plsql/exercises/hello-world/hello_world#.plsql +20 -0
- data/tracks/plsql/exercises/hello-world/ut_hello_world#.plsql +60 -0
- data/tracks/sml/Makefile +5 -0
- data/tracks/sml/bin/generate +3 -4
- data/tracks/sml/config.json +55 -5
- data/tracks/sml/exercises/collatz-conjecture/README.md +63 -0
- data/tracks/sml/exercises/collatz-conjecture/collatz-conjecture.sml +4 -0
- data/tracks/sml/exercises/collatz-conjecture/example.sml +17 -0
- data/tracks/sml/exercises/collatz-conjecture/test.sml +30 -0
- data/tracks/sml/exercises/collatz-conjecture/testlib.sml +159 -0
- data/tracks/sml/exercises/leap/README.md +63 -0
- data/tracks/sml/exercises/leap/example.sml +2 -0
- data/tracks/sml/exercises/leap/leap.sml +2 -0
- data/tracks/sml/exercises/leap/test.sml +24 -0
- data/tracks/sml/exercises/leap/testlib.sml +159 -0
- data/tracks/sml/exercises/rna-transcription/README.md +55 -0
- data/tracks/sml/exercises/rna-transcription/example.sml +20 -0
- data/tracks/sml/exercises/rna-transcription/rna-transcription.sml +2 -0
- data/tracks/sml/exercises/rna-transcription/test.sml +36 -0
- data/tracks/sml/exercises/rna-transcription/testlib.sml +159 -0
- data/tracks/sml/exercises/sum-of-multiples/README.md +48 -0
- data/tracks/sml/exercises/sum-of-multiples/example.sml +8 -0
- data/tracks/sml/exercises/sum-of-multiples/sum-of-multiples.sml +2 -0
- data/tracks/sml/exercises/sum-of-multiples/test.sml +48 -0
- data/tracks/sml/exercises/sum-of-multiples/testlib.sml +159 -0
- data/tracks/sml/exercises/two-fer/README.md +78 -0
- data/tracks/sml/exercises/two-fer/example.sml +1 -0
- data/tracks/sml/exercises/two-fer/test.sml +21 -0
- data/tracks/sml/exercises/two-fer/testlib.sml +159 -0
- data/tracks/sml/exercises/two-fer/two-fer.sml +2 -0
- metadata +43 -5
- data/tracks/csharp/exercises/Exercises.All.sln +0 -1545
- data/tracks/csharp/exercises/Exercises.Default.sln +0 -1503
- data/tracks/csharp/exercises/Exercises.Refactoring.sln +0 -61
@@ -0,0 +1,8 @@
|
|
1
|
+
fun sum (factors: int list, limit: int): int = let
|
2
|
+
val sum' = foldl (op +) 0
|
3
|
+
val numbers = List.tabulate(limit - 1, fn x => x + 1) (* generate list from [1, limit - 1] *)
|
4
|
+
fun any f [] = false
|
5
|
+
| any f (x::xs) = f x orelse any f xs
|
6
|
+
in
|
7
|
+
sum' (List.filter (fn x => (any (fn f => x mod f = 0) factors)) numbers)
|
8
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
(* version 1.0.0 *)
|
2
|
+
|
3
|
+
use "sum-of-multiples.sml";
|
4
|
+
use "testlib.sml";
|
5
|
+
|
6
|
+
infixr |>
|
7
|
+
fun x |> f = f x
|
8
|
+
|
9
|
+
val testsuite =
|
10
|
+
describe "sum-of-multiples" [
|
11
|
+
test "multiples of 3 or 5 up to 1"
|
12
|
+
(fn _ => sum ([3, 5], 1) |> Expect.equalTo 0),
|
13
|
+
|
14
|
+
test "multiples of 3 or 5 up to 4"
|
15
|
+
(fn _ => sum ([3, 5], 4) |> Expect.equalTo 3),
|
16
|
+
|
17
|
+
test "multiples of 3 or 5 up to 10"
|
18
|
+
(fn _ => sum ([3, 5], 10) |> Expect.equalTo 23),
|
19
|
+
|
20
|
+
test "multiples of 3 or 5 up to 100"
|
21
|
+
(fn _ => sum ([3, 5], 100) |> Expect.equalTo 2318),
|
22
|
+
|
23
|
+
test "multiples of 3 or 5 up to 1000"
|
24
|
+
(fn _ => sum ([3, 5], 1000) |> Expect.equalTo 233168),
|
25
|
+
|
26
|
+
test "multiples of 7, 13 or 17 up to 20"
|
27
|
+
(fn _ => sum ([7, 13, 17], 20) |> Expect.equalTo 51),
|
28
|
+
|
29
|
+
test "multiples of 4 or 6 up to 15"
|
30
|
+
(fn _ => sum ([4, 6], 15) |> Expect.equalTo 30),
|
31
|
+
|
32
|
+
test "multiples of 5, 6 or 8 up to 150"
|
33
|
+
(fn _ => sum ([5, 6, 8], 150) |> Expect.equalTo 4419),
|
34
|
+
|
35
|
+
test "multiples of 5 or 25 up to 51"
|
36
|
+
(fn _ => sum ([5, 25], 51) |> Expect.equalTo 275),
|
37
|
+
|
38
|
+
test "multiples of 43 or 47 up to 10000"
|
39
|
+
(fn _ => sum ([43, 47], 10000) |> Expect.equalTo 2203160),
|
40
|
+
|
41
|
+
test "multiples of 1 up to 100"
|
42
|
+
(fn _ => sum ([1], 100) |> Expect.equalTo 4950),
|
43
|
+
|
44
|
+
test "multiples of an empty list up to 10000"
|
45
|
+
(fn _ => sum ([], 10000) |> Expect.equalTo 0)
|
46
|
+
]
|
47
|
+
|
48
|
+
val _ = Test.run testsuite
|
@@ -0,0 +1,159 @@
|
|
1
|
+
structure Expect =
|
2
|
+
struct
|
3
|
+
datatype expectation = Pass | Fail of string * string
|
4
|
+
|
5
|
+
local
|
6
|
+
fun failEq b a =
|
7
|
+
Fail ("Expected: " ^ b, "Got: " ^ a)
|
8
|
+
|
9
|
+
fun failExn b a =
|
10
|
+
Fail ("Expected: " ^ b, "Raised: " ^ a)
|
11
|
+
|
12
|
+
fun exnName (e: exn): string = General.exnName e
|
13
|
+
in
|
14
|
+
fun truthy a =
|
15
|
+
if a
|
16
|
+
then Pass
|
17
|
+
else failEq "true" "false"
|
18
|
+
|
19
|
+
fun falsy a =
|
20
|
+
if a
|
21
|
+
then failEq "false" "true"
|
22
|
+
else Pass
|
23
|
+
|
24
|
+
fun equalTo b a =
|
25
|
+
if a = b
|
26
|
+
then Pass
|
27
|
+
else failEq (PolyML.makestring b) (PolyML.makestring a)
|
28
|
+
|
29
|
+
fun nearTo b a =
|
30
|
+
if Real.== (a, b)
|
31
|
+
then Pass
|
32
|
+
else failEq (Real.toString b) (Real.toString a)
|
33
|
+
|
34
|
+
fun anyError f =
|
35
|
+
(
|
36
|
+
f ();
|
37
|
+
failExn "an exception" "Nothing"
|
38
|
+
) handle _ => Pass
|
39
|
+
|
40
|
+
fun error e f =
|
41
|
+
(
|
42
|
+
f ();
|
43
|
+
failExn (exnName e) "Nothing"
|
44
|
+
) handle e' => if exnMessage e' = exnMessage e
|
45
|
+
then Pass
|
46
|
+
else failExn (exnMessage e) (exnMessage e')
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
structure TermColor =
|
51
|
+
struct
|
52
|
+
datatype color = Red | Green | Yellow | Normal
|
53
|
+
|
54
|
+
fun f Red = "\027[31m"
|
55
|
+
| f Green = "\027[32m"
|
56
|
+
| f Yellow = "\027[33m"
|
57
|
+
| f Normal = "\027[0m"
|
58
|
+
|
59
|
+
fun colorize color s = (f color) ^ s ^ (f Normal)
|
60
|
+
|
61
|
+
val redit = colorize Red
|
62
|
+
|
63
|
+
val greenit = colorize Green
|
64
|
+
|
65
|
+
val yellowit = colorize Yellow
|
66
|
+
end
|
67
|
+
|
68
|
+
structure Test =
|
69
|
+
struct
|
70
|
+
datatype testnode = TestGroup of string * testnode list
|
71
|
+
| Test of string * (unit -> Expect.expectation)
|
72
|
+
|
73
|
+
local
|
74
|
+
datatype evaluation = Success of string
|
75
|
+
| Failure of string * string * string
|
76
|
+
| Error of string * string
|
77
|
+
|
78
|
+
fun indent n s = (implode (List.tabulate (n, fn _ => #" "))) ^ s
|
79
|
+
|
80
|
+
fun fmt indentlvl ev =
|
81
|
+
let
|
82
|
+
val check = TermColor.greenit "\226\156\148 " (* ✔ *)
|
83
|
+
val cross = TermColor.redit "\226\156\150 " (* ✖ *)
|
84
|
+
val indentlvl = indentlvl * 2
|
85
|
+
in
|
86
|
+
case ev of
|
87
|
+
Success descr => indent indentlvl (check ^ descr)
|
88
|
+
| Failure (descr, exp, got) =>
|
89
|
+
String.concatWith "\n" [indent indentlvl (cross ^ descr),
|
90
|
+
indent (indentlvl + 2) exp,
|
91
|
+
indent (indentlvl + 2) got]
|
92
|
+
| Error (descr, reason) =>
|
93
|
+
String.concatWith "\n" [indent indentlvl (cross ^ descr),
|
94
|
+
indent (indentlvl + 2) (TermColor.redit reason)]
|
95
|
+
end
|
96
|
+
|
97
|
+
fun eval (TestGroup _) = raise Fail "Only a 'Test' can be evaluated"
|
98
|
+
| eval (Test (descr, thunk)) =
|
99
|
+
(
|
100
|
+
case thunk () of
|
101
|
+
Expect.Pass => ((1, 0, 0), Success descr)
|
102
|
+
| Expect.Fail (s, s') => ((0, 1, 0), Failure (descr, s, s'))
|
103
|
+
)
|
104
|
+
handle e => ((0, 0, 1), Error (descr, "Unexpected error: " ^ exnMessage e))
|
105
|
+
|
106
|
+
fun flatten depth testnode =
|
107
|
+
let
|
108
|
+
fun sum (x, y, z) (a, b, c) = (x + a, y + b, z + c)
|
109
|
+
|
110
|
+
fun aux (t, (counter, acc)) =
|
111
|
+
let
|
112
|
+
val (counter', texts) = flatten (depth + 1) t
|
113
|
+
in
|
114
|
+
(sum counter' counter, texts :: acc)
|
115
|
+
end
|
116
|
+
in
|
117
|
+
case testnode of
|
118
|
+
TestGroup (descr, ts) =>
|
119
|
+
let
|
120
|
+
val (counter, texts) = foldr aux ((0, 0, 0), []) ts
|
121
|
+
in
|
122
|
+
(counter, (indent (depth * 2) descr) :: List.concat texts)
|
123
|
+
end
|
124
|
+
| Test _ =>
|
125
|
+
let
|
126
|
+
val (counter, evaluation) = eval testnode
|
127
|
+
in
|
128
|
+
(counter, [fmt depth evaluation])
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
fun println s = print (s ^ "\n")
|
133
|
+
in
|
134
|
+
fun run suite =
|
135
|
+
let
|
136
|
+
val ((succeeded, failed, errored), texts) = flatten 0 suite
|
137
|
+
|
138
|
+
val summary = String.concatWith ", " [
|
139
|
+
TermColor.greenit ((Int.toString succeeded) ^ " passed"),
|
140
|
+
TermColor.redit ((Int.toString failed) ^ " failed"),
|
141
|
+
TermColor.redit ((Int.toString errored) ^ " errored"),
|
142
|
+
(Int.toString (succeeded + failed + errored)) ^ " total"
|
143
|
+
]
|
144
|
+
|
145
|
+
val status = if failed = 0 andalso errored = 0
|
146
|
+
then OS.Process.success
|
147
|
+
else OS.Process.failure
|
148
|
+
|
149
|
+
in
|
150
|
+
List.app println texts;
|
151
|
+
println "";
|
152
|
+
println ("Tests: " ^ summary);
|
153
|
+
OS.Process.exit status
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
fun describe description tests = Test.TestGroup (description, tests)
|
159
|
+
fun test description thunk = Test.Test (description, thunk)
|
@@ -0,0 +1,78 @@
|
|
1
|
+
# Two Fer
|
2
|
+
|
3
|
+
`Two-fer` or `2-fer` is short for two for one. One for you and one for me.
|
4
|
+
|
5
|
+
```
|
6
|
+
"One for X, one for me."
|
7
|
+
```
|
8
|
+
|
9
|
+
When X is a name or "you".
|
10
|
+
|
11
|
+
If the given name is "Alice", the result should be "One for Alice, one for me."
|
12
|
+
If no name is given, the result should be "One for you, one for me."
|
13
|
+
|
14
|
+
|
15
|
+
## Test-Driven Development
|
16
|
+
|
17
|
+
As programmers mature, they eventually want to test their code.
|
18
|
+
|
19
|
+
Here at Exercism we simulate [Test-Driven
|
20
|
+
Development](http://en.wikipedia.org/wiki/Test-driven_development) (TDD), where
|
21
|
+
you write your tests before writing any functionality. The simulation comes in
|
22
|
+
the form of a pre-written test suite, which will signal that you have solved
|
23
|
+
the problem.
|
24
|
+
|
25
|
+
It will also provide you with a safety net to explore other solutions without
|
26
|
+
breaking the functionality.
|
27
|
+
|
28
|
+
### A typical TDD workflow on Exercism:
|
29
|
+
|
30
|
+
1. Run the test file and pick one test that's failing.
|
31
|
+
2. Write some code to fix the test you picked.
|
32
|
+
3. Re-run the tests to confirm the test is now passing.
|
33
|
+
4. Repeat from step 1.
|
34
|
+
5. Submit your solution (`exercism submit /path/to/file`)
|
35
|
+
|
36
|
+
## Instructions
|
37
|
+
|
38
|
+
Submissions are encouraged to be general, within reason. Having said that, it's
|
39
|
+
also important not to over-engineer a solution.
|
40
|
+
|
41
|
+
It's important to remember that the goal is to make code as expressive and
|
42
|
+
readable as we can.
|
43
|
+
|
44
|
+
## Loading your exercise implementation in PolyML
|
45
|
+
|
46
|
+
```
|
47
|
+
$ poly --use {exercise}.sml
|
48
|
+
```
|
49
|
+
|
50
|
+
Or:
|
51
|
+
|
52
|
+
```
|
53
|
+
$ poly
|
54
|
+
> use "{exercise}.sml";
|
55
|
+
```
|
56
|
+
|
57
|
+
**Note:** You have to replace {exercise}.
|
58
|
+
|
59
|
+
## Running the tests
|
60
|
+
|
61
|
+
```
|
62
|
+
$ poly -q --use test.sml
|
63
|
+
```
|
64
|
+
|
65
|
+
## Feedback, Issues, Pull Requests
|
66
|
+
|
67
|
+
The [exercism/sml](https://github.com/exercism/sml) repository on
|
68
|
+
GitHub is the home for all of the Standard ML exercises.
|
69
|
+
|
70
|
+
If you have feedback about an exercise, or want to help implementing a new
|
71
|
+
one, head over there and create an issue. We'll do our best to help you!
|
72
|
+
|
73
|
+
## Source
|
74
|
+
|
75
|
+
This is an exercise to introduce users to basic programming constructs, just after hello World. [https://en.wikipedia.org/wiki/Two-fer](https://en.wikipedia.org/wiki/Two-fer)
|
76
|
+
|
77
|
+
## Submitting Incomplete Solutions
|
78
|
+
It's possible to submit an incomplete solution so you can see how others have completed the exercise.
|
@@ -0,0 +1 @@
|
|
1
|
+
fun name input = "One for " ^ getOpt (input, "you") ^ ", one for me."
|
@@ -0,0 +1,21 @@
|
|
1
|
+
(* version 1.1.0 *)
|
2
|
+
|
3
|
+
use "testlib.sml";
|
4
|
+
use "two-fer.sml";
|
5
|
+
|
6
|
+
infixr |>
|
7
|
+
fun x |> f = f x
|
8
|
+
|
9
|
+
val testsuite =
|
10
|
+
describe "two-fer" [
|
11
|
+
test "no name given"
|
12
|
+
(fn _ => name (NONE) |> Expect.equalTo "One for you, one for me."),
|
13
|
+
|
14
|
+
test "a name given"
|
15
|
+
(fn _ => name (SOME "Alice") |> Expect.equalTo "One for Alice, one for me."),
|
16
|
+
|
17
|
+
test "another name given"
|
18
|
+
(fn _ => name (SOME "Bob") |> Expect.equalTo "One for Bob, one for me.")
|
19
|
+
]
|
20
|
+
|
21
|
+
val _ = Test.run testsuite
|
@@ -0,0 +1,159 @@
|
|
1
|
+
structure Expect =
|
2
|
+
struct
|
3
|
+
datatype expectation = Pass | Fail of string * string
|
4
|
+
|
5
|
+
local
|
6
|
+
fun failEq b a =
|
7
|
+
Fail ("Expected: " ^ b, "Got: " ^ a)
|
8
|
+
|
9
|
+
fun failExn b a =
|
10
|
+
Fail ("Expected: " ^ b, "Raised: " ^ a)
|
11
|
+
|
12
|
+
fun exnName (e: exn): string = General.exnName e
|
13
|
+
in
|
14
|
+
fun truthy a =
|
15
|
+
if a
|
16
|
+
then Pass
|
17
|
+
else failEq "true" "false"
|
18
|
+
|
19
|
+
fun falsy a =
|
20
|
+
if a
|
21
|
+
then failEq "false" "true"
|
22
|
+
else Pass
|
23
|
+
|
24
|
+
fun equalTo b a =
|
25
|
+
if a = b
|
26
|
+
then Pass
|
27
|
+
else failEq (PolyML.makestring b) (PolyML.makestring a)
|
28
|
+
|
29
|
+
fun nearTo b a =
|
30
|
+
if Real.== (a, b)
|
31
|
+
then Pass
|
32
|
+
else failEq (Real.toString b) (Real.toString a)
|
33
|
+
|
34
|
+
fun anyError f =
|
35
|
+
(
|
36
|
+
f ();
|
37
|
+
failExn "an exception" "Nothing"
|
38
|
+
) handle _ => Pass
|
39
|
+
|
40
|
+
fun error e f =
|
41
|
+
(
|
42
|
+
f ();
|
43
|
+
failExn (exnName e) "Nothing"
|
44
|
+
) handle e' => if exnMessage e' = exnMessage e
|
45
|
+
then Pass
|
46
|
+
else failExn (exnMessage e) (exnMessage e')
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
structure TermColor =
|
51
|
+
struct
|
52
|
+
datatype color = Red | Green | Yellow | Normal
|
53
|
+
|
54
|
+
fun f Red = "\027[31m"
|
55
|
+
| f Green = "\027[32m"
|
56
|
+
| f Yellow = "\027[33m"
|
57
|
+
| f Normal = "\027[0m"
|
58
|
+
|
59
|
+
fun colorize color s = (f color) ^ s ^ (f Normal)
|
60
|
+
|
61
|
+
val redit = colorize Red
|
62
|
+
|
63
|
+
val greenit = colorize Green
|
64
|
+
|
65
|
+
val yellowit = colorize Yellow
|
66
|
+
end
|
67
|
+
|
68
|
+
structure Test =
|
69
|
+
struct
|
70
|
+
datatype testnode = TestGroup of string * testnode list
|
71
|
+
| Test of string * (unit -> Expect.expectation)
|
72
|
+
|
73
|
+
local
|
74
|
+
datatype evaluation = Success of string
|
75
|
+
| Failure of string * string * string
|
76
|
+
| Error of string * string
|
77
|
+
|
78
|
+
fun indent n s = (implode (List.tabulate (n, fn _ => #" "))) ^ s
|
79
|
+
|
80
|
+
fun fmt indentlvl ev =
|
81
|
+
let
|
82
|
+
val check = TermColor.greenit "\226\156\148 " (* ✔ *)
|
83
|
+
val cross = TermColor.redit "\226\156\150 " (* ✖ *)
|
84
|
+
val indentlvl = indentlvl * 2
|
85
|
+
in
|
86
|
+
case ev of
|
87
|
+
Success descr => indent indentlvl (check ^ descr)
|
88
|
+
| Failure (descr, exp, got) =>
|
89
|
+
String.concatWith "\n" [indent indentlvl (cross ^ descr),
|
90
|
+
indent (indentlvl + 2) exp,
|
91
|
+
indent (indentlvl + 2) got]
|
92
|
+
| Error (descr, reason) =>
|
93
|
+
String.concatWith "\n" [indent indentlvl (cross ^ descr),
|
94
|
+
indent (indentlvl + 2) (TermColor.redit reason)]
|
95
|
+
end
|
96
|
+
|
97
|
+
fun eval (TestGroup _) = raise Fail "Only a 'Test' can be evaluated"
|
98
|
+
| eval (Test (descr, thunk)) =
|
99
|
+
(
|
100
|
+
case thunk () of
|
101
|
+
Expect.Pass => ((1, 0, 0), Success descr)
|
102
|
+
| Expect.Fail (s, s') => ((0, 1, 0), Failure (descr, s, s'))
|
103
|
+
)
|
104
|
+
handle e => ((0, 0, 1), Error (descr, "Unexpected error: " ^ exnMessage e))
|
105
|
+
|
106
|
+
fun flatten depth testnode =
|
107
|
+
let
|
108
|
+
fun sum (x, y, z) (a, b, c) = (x + a, y + b, z + c)
|
109
|
+
|
110
|
+
fun aux (t, (counter, acc)) =
|
111
|
+
let
|
112
|
+
val (counter', texts) = flatten (depth + 1) t
|
113
|
+
in
|
114
|
+
(sum counter' counter, texts :: acc)
|
115
|
+
end
|
116
|
+
in
|
117
|
+
case testnode of
|
118
|
+
TestGroup (descr, ts) =>
|
119
|
+
let
|
120
|
+
val (counter, texts) = foldr aux ((0, 0, 0), []) ts
|
121
|
+
in
|
122
|
+
(counter, (indent (depth * 2) descr) :: List.concat texts)
|
123
|
+
end
|
124
|
+
| Test _ =>
|
125
|
+
let
|
126
|
+
val (counter, evaluation) = eval testnode
|
127
|
+
in
|
128
|
+
(counter, [fmt depth evaluation])
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
fun println s = print (s ^ "\n")
|
133
|
+
in
|
134
|
+
fun run suite =
|
135
|
+
let
|
136
|
+
val ((succeeded, failed, errored), texts) = flatten 0 suite
|
137
|
+
|
138
|
+
val summary = String.concatWith ", " [
|
139
|
+
TermColor.greenit ((Int.toString succeeded) ^ " passed"),
|
140
|
+
TermColor.redit ((Int.toString failed) ^ " failed"),
|
141
|
+
TermColor.redit ((Int.toString errored) ^ " errored"),
|
142
|
+
(Int.toString (succeeded + failed + errored)) ^ " total"
|
143
|
+
]
|
144
|
+
|
145
|
+
val status = if failed = 0 andalso errored = 0
|
146
|
+
then OS.Process.success
|
147
|
+
else OS.Process.failure
|
148
|
+
|
149
|
+
in
|
150
|
+
List.app println texts;
|
151
|
+
println "";
|
152
|
+
println ("Tests: " ^ summary);
|
153
|
+
OS.Process.exit status
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
fun describe description tests = Test.TestGroup (description, tests)
|
159
|
+
fun test description thunk = Test.Test (description, thunk)
|