trackler 2.0.0.8 → 2.0.0.9
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/common/exercises/bob/canonical-data.json +1 -20
- data/common/exercises/raindrops/canonical-data.json +18 -0
- data/common/exercises/sublist/canonical-data.json +5 -0
- data/lib/trackler/version.rb +1 -1
- data/tracks/clojure/config.json +5 -0
- data/tracks/clojure/exercises/pig-latin/project.clj +4 -0
- data/tracks/clojure/exercises/pig-latin/src/example.clj +49 -0
- data/tracks/clojure/exercises/pig-latin/test/pig_latin_test.clj +92 -0
- data/tracks/clojure/exercises/robot-name/src/example.clj +8 -11
- data/tracks/clojure/exercises/robot-name/test/robot_name_test.clj +23 -18
- data/tracks/crystal/config.json +6 -0
- data/tracks/crystal/exercises/acronym/spec/acronym_spec.cr +7 -7
- data/tracks/crystal/exercises/forth/spec/forth_spec.cr +198 -0
- data/tracks/crystal/exercises/forth/src/example.cr +187 -0
- data/tracks/crystal/exercises/hello-world/spec/hello_world_spec.cr +3 -3
- data/tracks/crystal/src/generator/exercises/acronym.cr +1 -1
- data/tracks/crystal/src/generator/exercises/exercise_generator.cr +3 -2
- data/tracks/crystal/src/generator/exercises/forth.cr +56 -0
- data/tracks/crystal/src/generator/exercises/templates/example.tt +2 -2
- data/tracks/crystal/src/generator/spec/exercise_generator_spec.cr +5 -3
- data/tracks/csharp/exercises/wordy/WordyTest.cs +2 -2
- data/tracks/elixir/config.json +83 -70
- data/tracks/fsharp/exercises/wordy/WordyTest.fs +1 -1
- data/tracks/go/config.json +28 -3
- data/tracks/go/exercises/all-your-base/all_your_base_test.go +185 -0
- data/tracks/go/exercises/all-your-base/example.go +60 -0
- data/tracks/go/exercises/isogram/example.go +15 -0
- data/tracks/go/exercises/isogram/isogram_test.go +50 -0
- data/tracks/go/exercises/ledger/ledger.go +2 -2
- data/tracks/go/exercises/pangram/example.go +32 -0
- data/tracks/go/exercises/pangram/pangram_test.go +43 -0
- data/tracks/go/exercises/protein-translation/example.go +59 -0
- data/tracks/go/exercises/protein-translation/protein_translation_test.go +69 -0
- data/tracks/go/exercises/tree-building/tree_building.go +1 -1
- data/tracks/go/exercises/twelve-days/example.go +57 -0
- data/tracks/go/exercises/twelve-days/twelve_days_test.go +79 -0
- data/tracks/haskell/.travis.yml +13 -8
- data/tracks/haskell/config.json +6 -0
- data/tracks/haskell/exercises/accumulate/stack.yaml +1 -1
- data/tracks/haskell/exercises/all-your-base/stack.yaml +1 -1
- data/tracks/haskell/exercises/allergies/stack.yaml +1 -1
- data/tracks/haskell/exercises/alphametics/stack.yaml +1 -1
- data/tracks/haskell/exercises/anagram/stack.yaml +1 -1
- data/tracks/haskell/exercises/atbash-cipher/stack.yaml +1 -1
- data/tracks/haskell/exercises/bank-account/stack.yaml +1 -1
- data/tracks/haskell/exercises/beer-song/stack.yaml +1 -1
- data/tracks/haskell/exercises/binary/stack.yaml +1 -1
- data/tracks/haskell/exercises/binary-search-tree/stack.yaml +1 -1
- data/tracks/haskell/exercises/bob/stack.yaml +1 -1
- data/tracks/haskell/exercises/bowling/HINTS.md +19 -0
- data/tracks/haskell/exercises/bowling/examples/success-standard/package.yaml +16 -0
- data/tracks/haskell/exercises/bowling/examples/success-standard/src/Bowling.hs +84 -0
- data/tracks/haskell/exercises/bowling/package.yaml +19 -0
- data/tracks/haskell/exercises/bowling/src/Bowling.hs +9 -0
- data/tracks/haskell/exercises/bowling/stack.yaml +1 -0
- data/tracks/haskell/exercises/bowling/test/Tests.hs +143 -0
- data/tracks/haskell/exercises/change/stack.yaml +1 -1
- data/tracks/haskell/exercises/clock/stack.yaml +1 -1
- data/tracks/haskell/exercises/connect/stack.yaml +1 -1
- data/tracks/haskell/exercises/crypto-square/stack.yaml +1 -1
- data/tracks/haskell/exercises/custom-set/stack.yaml +1 -1
- data/tracks/haskell/exercises/difference-of-squares/stack.yaml +1 -1
- data/tracks/haskell/exercises/dominoes/stack.yaml +1 -1
- data/tracks/haskell/exercises/etl/stack.yaml +1 -1
- data/tracks/haskell/exercises/food-chain/stack.yaml +1 -1
- data/tracks/haskell/exercises/forth/stack.yaml +1 -1
- data/tracks/haskell/exercises/gigasecond/stack.yaml +1 -1
- data/tracks/haskell/exercises/go-counting/stack.yaml +1 -1
- data/tracks/haskell/exercises/grade-school/stack.yaml +1 -1
- data/tracks/haskell/exercises/grains/stack.yaml +1 -1
- data/tracks/haskell/exercises/hamming/stack.yaml +1 -1
- data/tracks/haskell/exercises/hexadecimal/stack.yaml +1 -1
- data/tracks/haskell/exercises/house/stack.yaml +1 -1
- data/tracks/haskell/exercises/kindergarten-garden/stack.yaml +1 -1
- data/tracks/haskell/exercises/largest-series-product/stack.yaml +1 -1
- data/tracks/haskell/exercises/leap/stack.yaml +1 -1
- data/tracks/haskell/exercises/lens-person/stack.yaml +1 -1
- data/tracks/haskell/exercises/linked-list/stack.yaml +1 -1
- data/tracks/haskell/exercises/list-ops/stack.yaml +1 -1
- data/tracks/haskell/exercises/luhn/stack.yaml +1 -1
- data/tracks/haskell/exercises/matrix/stack.yaml +1 -1
- data/tracks/haskell/exercises/meetup/stack.yaml +1 -1
- data/tracks/haskell/exercises/minesweeper/stack.yaml +1 -1
- data/tracks/haskell/exercises/nth-prime/stack.yaml +1 -1
- data/tracks/haskell/exercises/nucleotide-count/stack.yaml +1 -1
- data/tracks/haskell/exercises/ocr-numbers/stack.yaml +1 -1
- data/tracks/haskell/exercises/octal/stack.yaml +1 -1
- data/tracks/haskell/exercises/palindrome-products/stack.yaml +1 -1
- data/tracks/haskell/exercises/parallel-letter-frequency/stack.yaml +1 -1
- data/tracks/haskell/exercises/pascals-triangle/stack.yaml +1 -1
- data/tracks/haskell/exercises/phone-number/stack.yaml +1 -1
- data/tracks/haskell/exercises/pig-latin/stack.yaml +1 -1
- data/tracks/haskell/exercises/pov/stack.yaml +1 -1
- data/tracks/haskell/exercises/prime-factors/stack.yaml +1 -1
- data/tracks/haskell/exercises/pythagorean-triplet/stack.yaml +1 -1
- data/tracks/haskell/exercises/queen-attack/stack.yaml +1 -1
- data/tracks/haskell/exercises/raindrops/stack.yaml +1 -1
- data/tracks/haskell/exercises/rna-transcription/stack.yaml +1 -1
- data/tracks/haskell/exercises/robot-name/stack.yaml +1 -1
- data/tracks/haskell/exercises/robot-simulator/stack.yaml +1 -1
- data/tracks/haskell/exercises/roman-numerals/stack.yaml +1 -1
- data/tracks/haskell/exercises/saddle-points/stack.yaml +1 -1
- data/tracks/haskell/exercises/say/stack.yaml +1 -1
- data/tracks/haskell/exercises/scrabble-score/stack.yaml +1 -1
- data/tracks/haskell/exercises/secret-handshake/stack.yaml +1 -1
- data/tracks/haskell/exercises/series/stack.yaml +1 -1
- data/tracks/haskell/exercises/sgf-parsing/stack.yaml +1 -1
- data/tracks/haskell/exercises/sieve/stack.yaml +1 -1
- data/tracks/haskell/exercises/simple-cipher/stack.yaml +1 -1
- data/tracks/haskell/exercises/simple-linked-list/stack.yaml +1 -1
- data/tracks/haskell/exercises/space-age/stack.yaml +1 -1
- data/tracks/haskell/exercises/strain/stack.yaml +1 -1
- data/tracks/haskell/exercises/sublist/stack.yaml +1 -1
- data/tracks/haskell/exercises/sum-of-multiples/stack.yaml +1 -1
- data/tracks/haskell/exercises/triangle/stack.yaml +1 -1
- data/tracks/haskell/exercises/trinary/stack.yaml +1 -1
- data/tracks/haskell/exercises/word-count/stack.yaml +1 -1
- data/tracks/haskell/exercises/wordy/stack.yaml +1 -1
- data/tracks/haskell/exercises/zebra-puzzle/stack.yaml +1 -1
- data/tracks/haskell/exercises/zipper/stack.yaml +1 -1
- data/tracks/java/config.json +13 -1
- data/tracks/java/exercises/etl/src/main/java/Etl.java +3 -3
- data/tracks/java/exercises/hello-world/GETTING_STARTED.md +1 -1
- data/tracks/java/exercises/hello-world/src/main/java/HelloWorld.java +3 -3
- data/tracks/java/exercises/minesweeper/build.gradle +17 -0
- data/tracks/java/exercises/minesweeper/src/example/java/MinesweeperBoard.java +111 -0
- data/tracks/java/exercises/minesweeper/src/main/java/MinesweeperBoard.java +5 -0
- data/tracks/java/exercises/minesweeper/src/test/java/MinesweeperBoardTest.java +295 -0
- data/tracks/java/exercises/nucleotide-count/src/test/java/NucleotideTest.java +67 -67
- data/tracks/java/exercises/series/build.gradle +18 -0
- data/tracks/java/exercises/series/src/example/java/Series.java +39 -0
- data/tracks/java/exercises/series/src/main/java/.keep +0 -0
- data/tracks/java/exercises/series/src/main/java/Series.java +3 -0
- data/tracks/java/exercises/series/src/test/java/.keep +0 -0
- data/tracks/java/exercises/series/src/test/java/SeriesTest.java +154 -0
- data/tracks/java/exercises/settings.gradle +2 -0
- data/tracks/javascript/exercises/custom-set/custom-set.spec.js +130 -84
- data/tracks/javascript/exercises/custom-set/example-gen.js +200 -0
- data/tracks/ocaml/Makefile +5 -1
- data/tracks/ocaml/config.json +2 -1
- data/tracks/ocaml/exercises/anagram/test.ml +35 -24
- data/tracks/ocaml/exercises/bob/example.ml +1 -1
- data/tracks/ocaml/exercises/bob/test.ml +53 -40
- data/tracks/ocaml/exercises/hamming/test.ml +41 -31
- data/tracks/ocaml/exercises/raindrops/test.ml +38 -39
- data/tracks/ocaml/tools/test-generator/.merlin +5 -0
- data/tracks/ocaml/tools/test-generator/Makefile +15 -0
- data/tracks/ocaml/tools/test-generator/_tags +0 -0
- data/tracks/ocaml/tools/test-generator/src/codegen.ml +17 -0
- data/tracks/ocaml/tools/test-generator/src/codegen.mli +7 -0
- data/tracks/ocaml/tools/test-generator/src/leap.json +39 -0
- data/tracks/ocaml/tools/test-generator/src/model.ml +31 -0
- data/tracks/ocaml/tools/test-generator/src/parser.ml +61 -0
- data/tracks/ocaml/tools/test-generator/src/parser.mli +9 -0
- data/tracks/ocaml/tools/test-generator/src/special_cases.ml +12 -0
- data/tracks/ocaml/tools/test-generator/src/special_cases.mli +7 -0
- data/tracks/ocaml/tools/test-generator/src/test_gen.ml +25 -0
- data/tracks/ocaml/tools/test-generator/src/test_generator.ml +62 -0
- data/tracks/ocaml/tools/test-generator/src/test_generator.mli +6 -0
- data/tracks/ocaml/tools/test-generator/src/utils.ml +32 -0
- data/tracks/ocaml/tools/test-generator/src/utils.mli +18 -0
- data/tracks/ocaml/tools/test-generator/templates/anagram/template.ml +17 -0
- data/tracks/ocaml/tools/test-generator/templates/bob/template.ml +15 -0
- data/tracks/ocaml/tools/test-generator/templates/hamming/template.ml +30 -0
- data/tracks/ocaml/tools/test-generator/templates/leap/template.ml +15 -0
- data/tracks/ocaml/tools/test-generator/templates/raindrops/template.ml +15 -0
- data/tracks/ocaml/tools/test-generator/templates/word-count/template.ml +19 -0
- data/tracks/ocaml/tools/test-generator/test/all_tests.ml +11 -0
- data/tracks/ocaml/tools/test-generator/test/codegen_test.ml +20 -0
- data/tracks/ocaml/tools/test-generator/test/model_test.ml +27 -0
- data/tracks/ocaml/tools/test-generator/test/parser_test.ml +73 -0
- data/tracks/ocaml/tools/test-generator/test/special_cases_test.ml +22 -0
- data/tracks/ocaml/tools/test-generator/test/test_generator_test.ml +11 -0
- data/tracks/ocaml/tools/test-generator/test/utils_test.ml +21 -0
- data/tracks/perl5/config.json +1 -1
- data/tracks/perl6/accumulate/accumulate.t +8 -2
- data/tracks/perl6/anagram/Example.pm +1 -1
- data/tracks/perl6/anagram/anagram.t +7 -8
- data/tracks/perl6/binary/Example.pm +1 -1
- data/tracks/perl6/binary/binary.t +7 -8
- data/tracks/perl6/bob/bob.t +1 -3
- data/tracks/perl6/config.json +1 -0
- data/tracks/perl6/grains/grains.t +7 -2
- data/tracks/perl6/leap/leap.t +3 -1
- data/tracks/perl6/raindrops/raindrops.t +8 -2
- data/tracks/perl6/rna-transcription/rna_transcription.t +8 -9
- data/tracks/perl6/robot-name/robot.t +8 -9
- data/tracks/perl6/scrabble-score/Example.pm +1 -1
- data/tracks/perl6/scrabble-score/scrabble_score.t +8 -2
- data/tracks/perl6/word-count/word_count.t +8 -2
- data/tracks/php/config.json +5 -0
- data/tracks/php/exercises/markdown/example.php +85 -0
- data/tracks/php/exercises/markdown/markdown.php +82 -0
- data/tracks/php/exercises/markdown/markdown_test.php +57 -0
- data/tracks/python/exercises/hamming/example.py +3 -0
- data/tracks/python/exercises/hamming/hamming_test.py +40 -14
- data/tracks/python/exercises/rna-transcription/example.py +6 -1
- data/tracks/python/exercises/rna-transcription/rna_transcription_test.py +9 -0
- data/tracks/ruby/config.json +6 -0
- data/tracks/ruby/exercises/dominoes/.version +1 -0
- data/tracks/ruby/exercises/dominoes/dominoes_test.rb +149 -0
- data/tracks/ruby/exercises/dominoes/example.rb +37 -0
- data/tracks/ruby/exercises/dominoes/example.tt +57 -0
- data/tracks/ruby/exercises/pangram/.version +1 -1
- data/tracks/ruby/exercises/pangram/example.rb +2 -2
- data/tracks/ruby/exercises/pangram/example.tt +5 -7
- data/tracks/ruby/exercises/pangram/pangram_test.rb +30 -25
- data/tracks/ruby/exercises/queen-attack/.version +1 -0
- data/tracks/ruby/exercises/queen-attack/example.rb +5 -30
- data/tracks/ruby/exercises/queen-attack/example.tt +22 -0
- data/tracks/ruby/exercises/queen-attack/queen_attack_test.rb +46 -95
- data/tracks/ruby/exercises/triangle/.version +1 -0
- data/tracks/ruby/exercises/triangle/example.rb +15 -22
- data/tracks/ruby/exercises/triangle/example.tt +20 -0
- data/tracks/ruby/exercises/triangle/triangle_test.rb +80 -40
- data/tracks/ruby/lib/dominoes_cases.rb +23 -0
- data/tracks/ruby/lib/pangram_cases.rb +19 -4
- data/tracks/ruby/lib/queen_attack_cases.rb +54 -0
- data/tracks/ruby/lib/triangle_cases.rb +51 -0
- data/tracks/scala/config.json +6 -0
- data/tracks/scala/exercises/dominoes/Example.scala +41 -0
- data/tracks/scala/exercises/dominoes/build.sbt +3 -0
- data/tracks/scala/exercises/dominoes/src/main/scala/Dominoes.scala +4 -0
- data/tracks/scala/exercises/dominoes/src/test/scala/DominoesSuite.scala +91 -0
- metadata +83 -2
@@ -4,45 +4,58 @@ open Bob
|
|
4
4
|
|
5
5
|
let ae exp got _test_ctxt = assert_equal ~printer:String.to_string exp got
|
6
6
|
|
7
|
-
let tests =
|
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
|
-
|
7
|
+
let tests = [
|
8
|
+
"stating something" >::
|
9
|
+
ae "Whatever." (response_for "Tom-ay-to, tom-aaaah-to.");
|
10
|
+
"shouting" >::
|
11
|
+
ae "Whoa, chill out!" (response_for "WATCH OUT!");
|
12
|
+
"shouting gibberish" >::
|
13
|
+
ae "Whoa, chill out!" (response_for "FCECDFCAAB");
|
14
|
+
"asking a question" >::
|
15
|
+
ae "Sure." (response_for "Does this cryogenic chamber make me look fat?");
|
16
|
+
"asking a numeric question" >::
|
17
|
+
ae "Sure." (response_for "You are, what, like 15?");
|
18
|
+
"asking gibberish" >::
|
19
|
+
ae "Sure." (response_for "fffbbcbeab?");
|
20
|
+
"talking forcefully" >::
|
21
|
+
ae "Whatever." (response_for "Let's go make out behind the gym!");
|
22
|
+
"using acronyms in regular speech" >::
|
23
|
+
ae "Whatever." (response_for "It's OK if you don't want to go to the DMV.");
|
24
|
+
"forceful question" >::
|
25
|
+
ae "Whoa, chill out!" (response_for "WHAT THE HELL WERE YOU THINKING?");
|
26
|
+
"shouting numbers" >::
|
27
|
+
ae "Whoa, chill out!" (response_for "1, 2, 3 GO!");
|
28
|
+
"only numbers" >::
|
29
|
+
ae "Whatever." (response_for "1, 2, 3");
|
30
|
+
"question with only numbers" >::
|
31
|
+
ae "Sure." (response_for "4?");
|
32
|
+
"shouting with special characters" >::
|
33
|
+
ae "Whoa, chill out!" (response_for "ZOMG THE %^*@#$(*^ ZOMBIES ARE COMING!!11!!1!");
|
34
|
+
"shouting with no exclamation mark" >::
|
35
|
+
ae "Whoa, chill out!" (response_for "I HATE YOU");
|
36
|
+
"statement containing question mark" >::
|
37
|
+
ae "Whatever." (response_for "Ending with ? means a question.");
|
38
|
+
"non-letters with question" >::
|
39
|
+
ae "Sure." (response_for ":) ?");
|
40
|
+
"prattling on" >::
|
41
|
+
ae "Sure." (response_for "Wait! Hang on. Are you going to be OK?");
|
42
|
+
"silence" >::
|
43
|
+
ae "Fine. Be that way!" (response_for "");
|
44
|
+
"prolonged silence" >::
|
45
|
+
ae "Fine. Be that way!" (response_for " ");
|
46
|
+
"alternate silence" >::
|
47
|
+
ae "Fine. Be that way!" (response_for "\t\t\t\t\t\t\t\t\t\t");
|
48
|
+
"multiple line question" >::
|
49
|
+
ae "Whatever." (response_for "\nDoes this cryogenic chamber make me look fat?\nno");
|
50
|
+
"starting with whitespace" >::
|
51
|
+
ae "Whatever." (response_for " hmmmmmmm...");
|
52
|
+
"ending with whitespace" >::
|
53
|
+
ae "Sure." (response_for "Okay if like my spacebar quite a bit? ");
|
54
|
+
"other whitespace" >::
|
55
|
+
ae "Fine. Be that way!" (response_for "\n\r \t");
|
56
|
+
"non-question ending with whitespace" >::
|
57
|
+
ae "Whatever." (response_for "This is a statement ending with whitespace ");
|
58
|
+
]
|
46
59
|
|
47
60
|
let () =
|
48
|
-
run_test_tt_main ("bob tests" >::: tests)
|
61
|
+
run_test_tt_main ("bob tests" >::: tests)
|
@@ -8,39 +8,49 @@ let printer n =
|
|
8
8
|
let ae exp got _test_ctxt = assert_equal ~printer exp got
|
9
9
|
|
10
10
|
let dna_of_string s =
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
11
|
+
let open Hamming in
|
12
|
+
let f = function
|
13
|
+
| 'A' -> A
|
14
|
+
| 'C' -> C
|
15
|
+
| 'G' -> G
|
16
|
+
| 'T' -> T
|
17
|
+
| _ -> failwith "Big news! New nucleotide discovered" in
|
18
|
+
String.to_list s |> List.map ~f
|
19
19
|
|
20
20
|
let hamdist a b = Hamming.hamming_distance (dna_of_string a) (dna_of_string b)
|
21
21
|
|
22
|
-
let tests =
|
23
|
-
|
24
|
-
|
25
|
-
"
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
"small
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
"
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
22
|
+
let tests = [
|
23
|
+
"identical strands" >::
|
24
|
+
ae (Some 0) (hamdist "A" "A");
|
25
|
+
"long identical strands" >::
|
26
|
+
ae (Some 0) (hamdist "GGACTGA" "GGACTGA");
|
27
|
+
"complete distance in single nucleotide strands" >::
|
28
|
+
ae (Some 1) (hamdist "A" "G");
|
29
|
+
"complete distance in small strands" >::
|
30
|
+
ae (Some 2) (hamdist "AG" "CT");
|
31
|
+
"small distance in small strands" >::
|
32
|
+
ae (Some 1) (hamdist "AT" "CT");
|
33
|
+
"small distance" >::
|
34
|
+
ae (Some 1) (hamdist "GGACG" "GGTCG");
|
35
|
+
"small distance in long strands" >::
|
36
|
+
ae (Some 2) (hamdist "ACCAGGG" "ACTATGG");
|
37
|
+
"non-unique character in first strand" >::
|
38
|
+
ae (Some 1) (hamdist "AGA" "AGG");
|
39
|
+
"non-unique character in second strand" >::
|
40
|
+
ae (Some 1) (hamdist "AGG" "AGA");
|
41
|
+
"same nucleotides in different positions" >::
|
42
|
+
ae (Some 2) (hamdist "TAG" "GAT");
|
43
|
+
"large distance" >::
|
44
|
+
ae (Some 4) (hamdist "GATACA" "GCATAA");
|
45
|
+
"large distance in off-by-one strand" >::
|
46
|
+
ae (Some 9) (hamdist "GGACGGATTCTG" "AGGACGGATTCT");
|
47
|
+
"empty strands" >::
|
48
|
+
ae (Some 0) (hamdist "" "");
|
49
|
+
"disallow first strand longer" >::
|
50
|
+
ae None (hamdist "AATG" "AAA");
|
51
|
+
"disallow second strand longer" >::
|
52
|
+
ae None (hamdist "ATA" "AGTG");
|
53
|
+
]
|
44
54
|
|
45
55
|
let () =
|
46
|
-
run_test_tt_main ("
|
56
|
+
run_test_tt_main ("hamming tests" >::: tests)
|
@@ -4,45 +4,44 @@ open Raindrops
|
|
4
4
|
|
5
5
|
let ae exp got _test_ctxt = assert_equal ~printer:Fn.id exp got
|
6
6
|
|
7
|
-
let tests =
|
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
|
-
ae "Plang" (raindrop 3125);
|
7
|
+
let tests = [
|
8
|
+
"the sound for 1 is 1" >::
|
9
|
+
ae "1" (raindrop 1);
|
10
|
+
"the sound for 3 is Pling" >::
|
11
|
+
ae "Pling" (raindrop 3);
|
12
|
+
"the sound for 5 is Plang" >::
|
13
|
+
ae "Plang" (raindrop 5);
|
14
|
+
"the sound for 7 is Plong" >::
|
15
|
+
ae "Plong" (raindrop 7);
|
16
|
+
"the sound for 6 is Pling as it has a factor 3" >::
|
17
|
+
ae "Pling" (raindrop 6);
|
18
|
+
"2 to the power 3 does not make a raindrop sound as 3 is the exponent not the base" >::
|
19
|
+
ae "8" (raindrop 8);
|
20
|
+
"the sound for 9 is Pling as it has a factor 3" >::
|
21
|
+
ae "Pling" (raindrop 9);
|
22
|
+
"the sound for 10 is Plang as it has a factor 5" >::
|
23
|
+
ae "Plang" (raindrop 10);
|
24
|
+
"the sound for 14 is Plong as it has a factor of 7" >::
|
25
|
+
ae "Plong" (raindrop 14);
|
26
|
+
"the sound for 15 is PlingPlang as it has factors 3 and 5" >::
|
27
|
+
ae "PlingPlang" (raindrop 15);
|
28
|
+
"the sound for 21 is PlingPlong as it has factors 3 and 7" >::
|
29
|
+
ae "PlingPlong" (raindrop 21);
|
30
|
+
"the sound for 25 is Plang as it has a factor 5" >::
|
31
|
+
ae "Plang" (raindrop 25);
|
32
|
+
"the sound for 27 is Pling as it has a factor 3" >::
|
33
|
+
ae "Pling" (raindrop 27);
|
34
|
+
"the sound for 35 is PlangPlong as it has factors 5 and 7" >::
|
35
|
+
ae "PlangPlong" (raindrop 35);
|
36
|
+
"the sound for 49 is Plong as it has a factor 7" >::
|
37
|
+
ae "Plong" (raindrop 49);
|
38
|
+
"the sound for 52 is 52" >::
|
39
|
+
ae "52" (raindrop 52);
|
40
|
+
"the sound for 105 is PlingPlangPlong as it has factors 3, 5 and 7" >::
|
41
|
+
ae "PlingPlangPlong" (raindrop 105);
|
42
|
+
"the sound for 3125 is Plang as it has a factor 5" >::
|
43
|
+
ae "Plang" (raindrop 3125);
|
45
44
|
]
|
46
45
|
|
47
46
|
let () =
|
48
|
-
run_test_tt_main ("raindrops tests" >::: tests)
|
47
|
+
run_test_tt_main ("raindrops tests" >::: tests)
|
@@ -0,0 +1,15 @@
|
|
1
|
+
test: test_gen.native
|
2
|
+
@./all_tests.native
|
3
|
+
|
4
|
+
test_gen.native: all_tests.native
|
5
|
+
@ocamlbuild -use-ocamlfind -tag thread -tag short_paths -cflags -strict-sequence -r -pkg core -pkg yojson -pkg ppx_deriving.eq -pkg ppx_deriving.show -Is "src" test_gen.native
|
6
|
+
|
7
|
+
all_tests.native: src/*.ml src/*.mli test/*.ml
|
8
|
+
@ocamlbuild -use-ocamlfind -tag thread -tag short_paths -cflags -strict-sequence -r -pkg core -pkg oUnit -pkg yojson -pkg ppx_deriving.eq -pkg ppx_deriving.show -Is "src,test" all_tests.native
|
9
|
+
|
10
|
+
clean:
|
11
|
+
rm -rf _build
|
12
|
+
rm -f test_gen.native
|
13
|
+
rm -f all_tests.native
|
14
|
+
|
15
|
+
.PHONY: clean
|
File without changes
|
@@ -0,0 +1,17 @@
|
|
1
|
+
open Core.Std
|
2
|
+
|
3
|
+
open Model
|
4
|
+
|
5
|
+
type fixup_function = key: string -> value: parameter -> string
|
6
|
+
|
7
|
+
let replace_key (key: string) (value: string) (target: string): string =
|
8
|
+
String.substr_replace_all target ~pattern:("$" ^ key) ~with_:value
|
9
|
+
|
10
|
+
let rec replace_keys (f: fixup_function) (s: string) (c: case): string =
|
11
|
+
let s = replace_key "name" c.name s in
|
12
|
+
let expected = f ~key:"expected" ~value:c.expected in
|
13
|
+
let s = replace_key "expected" expected s in
|
14
|
+
List.fold c.parameters ~init:s ~f:(fun s (k,v) -> replace_key k (parameter_to_string v) s)
|
15
|
+
|
16
|
+
let generate_code (f: fixup_function) template cases =
|
17
|
+
Ok (List.map cases ~f:(replace_keys f template))
|
@@ -0,0 +1,39 @@
|
|
1
|
+
{
|
2
|
+
"cases": [
|
3
|
+
{
|
4
|
+
"description": "leap year",
|
5
|
+
"input": 1996,
|
6
|
+
"expected": true
|
7
|
+
},
|
8
|
+
{
|
9
|
+
"description": "standard and odd year",
|
10
|
+
"input": 1997,
|
11
|
+
"expected": false
|
12
|
+
},
|
13
|
+
{
|
14
|
+
"description": "standard even year",
|
15
|
+
"input": 1998,
|
16
|
+
"expected": false
|
17
|
+
},
|
18
|
+
{
|
19
|
+
"description": "standard nineteenth century",
|
20
|
+
"input": 1900,
|
21
|
+
"expected": false
|
22
|
+
},
|
23
|
+
{
|
24
|
+
"description": "standard eighteenth century",
|
25
|
+
"input": 1800,
|
26
|
+
"expected": false
|
27
|
+
},
|
28
|
+
{
|
29
|
+
"description": "leap twenty fourth century",
|
30
|
+
"input": 2400,
|
31
|
+
"expected": true
|
32
|
+
},
|
33
|
+
{
|
34
|
+
"description": "leap y2k",
|
35
|
+
"input": 2000,
|
36
|
+
"expected": true
|
37
|
+
}
|
38
|
+
]
|
39
|
+
}
|
@@ -0,0 +1,31 @@
|
|
1
|
+
open Core.Std
|
2
|
+
|
3
|
+
open Utils
|
4
|
+
|
5
|
+
type parameter =
|
6
|
+
| String of string
|
7
|
+
| Float of float
|
8
|
+
| Int of int
|
9
|
+
| Bool of bool
|
10
|
+
| StringList of (string list)
|
11
|
+
| IntStringMap of ((string * int) list) [@@deriving eq, show]
|
12
|
+
|
13
|
+
type 'a elements = (string * 'a) list [@@deriving eq, show]
|
14
|
+
|
15
|
+
type case = {
|
16
|
+
name: string;
|
17
|
+
parameters: parameter elements;
|
18
|
+
expected: parameter;
|
19
|
+
} [@@deriving eq, show]
|
20
|
+
|
21
|
+
let surround (ch: char) (s: string): string =
|
22
|
+
Char.to_string ch ^ s ^ Char.to_string ch
|
23
|
+
|
24
|
+
let parameter_to_string = function
|
25
|
+
| String s -> String.escaped s
|
26
|
+
| Float f -> Float.to_string f
|
27
|
+
| Int n -> Int.to_string n
|
28
|
+
| Bool b -> Bool.to_string b
|
29
|
+
| StringList xs -> "[" ^ String.concat ~sep:"; " (List.map ~f:(surround '\"' >> String.escaped) xs) ^ "]"
|
30
|
+
| IntStringMap xs -> "[" ^ String.concat ~sep:"; "
|
31
|
+
(List.map xs ~f:(fun (k,v) -> "(\"" ^ String.escaped k ^ "\", " ^ Int.to_string v ^ ")")) ^ "]"
|
@@ -0,0 +1,61 @@
|
|
1
|
+
open Core.Std
|
2
|
+
open Utils
|
3
|
+
open Yojson.Safe
|
4
|
+
open Yojson.Safe.Util
|
5
|
+
open Model
|
6
|
+
|
7
|
+
type error =
|
8
|
+
TopLevelMustHaveKeyCalledCases | ExpectingListOfCases | ExpectingMapForCase |
|
9
|
+
BadDescription | BadExpected
|
10
|
+
[@@deriving eq]
|
11
|
+
|
12
|
+
let to_int_unsafe = function
|
13
|
+
| `Int x -> x
|
14
|
+
| _ -> failwith "need an int here"
|
15
|
+
|
16
|
+
let to_parameter (s: json) = match s with
|
17
|
+
| `String x -> Some (String x)
|
18
|
+
| `Float x -> Some (Float x)
|
19
|
+
| `Int x -> Some (Int x)
|
20
|
+
| `Bool x -> Some (Bool x)
|
21
|
+
| `List x -> Some (StringList (List.map x ~f:to_string))
|
22
|
+
| `Assoc x -> Some (IntStringMap (List.map x ~f:(fun (k,v) -> (k,to_int_unsafe v))))
|
23
|
+
| _ -> None
|
24
|
+
|
25
|
+
let parse_parameters (parameters: (string * json) list): parameter elements =
|
26
|
+
List.filter_map parameters ~f:(fun (k, v) -> Option.map ~f:(fun v -> (k, v)) (to_parameter v))
|
27
|
+
|
28
|
+
let parse_case_assoc (parameters: (string * json) list): (case, error) Result.t =
|
29
|
+
let find name e = List.Assoc.find parameters name |> Result.of_option ~error:e in
|
30
|
+
let test_parameters = List.Assoc.remove parameters "description" in
|
31
|
+
let test_parameters = List.Assoc.remove test_parameters "expected" in
|
32
|
+
let open Result.Monad_infix in
|
33
|
+
find "description" BadDescription >>=
|
34
|
+
to_string_note BadDescription >>= fun description ->
|
35
|
+
find "expected" BadExpected >>= fun expectedJson ->
|
36
|
+
to_parameter expectedJson |> Result.of_option ~error:BadExpected >>= fun expected ->
|
37
|
+
Ok {name = description; parameters = parse_parameters test_parameters; expected = expected}
|
38
|
+
|
39
|
+
let parse_case (s: json): (case, error) Result.t = match s with
|
40
|
+
| `Assoc assoc -> parse_case_assoc assoc
|
41
|
+
| _ -> Error ExpectingMapForCase
|
42
|
+
|
43
|
+
let parse_cases (text: string): (json, error) Result.t =
|
44
|
+
match from_string text |> member "cases" with
|
45
|
+
| `Null -> Error TopLevelMustHaveKeyCalledCases
|
46
|
+
| json -> Ok json
|
47
|
+
|
48
|
+
let parse_json_text (text: string): (case list, error) Result.t =
|
49
|
+
let open Result.Monad_infix in
|
50
|
+
parse_cases text >>=
|
51
|
+
to_list_note ExpectingListOfCases >>=
|
52
|
+
(sequence >> (List.map ~f:parse_case))
|
53
|
+
|
54
|
+
let show_error = function
|
55
|
+
| TopLevelMustHaveKeyCalledCases -> "Cannot parse this json - " ^
|
56
|
+
"expecting an object with a key: 'cases'"
|
57
|
+
| ExpectingMapForCase -> "Expected a json map for a test case"
|
58
|
+
| ExpectingListOfCases -> "Expected a top level map with key cases, " ^
|
59
|
+
"and a list of cases as its value."
|
60
|
+
| BadDescription -> "Case is missing a description or it is not a string."
|
61
|
+
| BadExpected -> "Case is missing an expected key or it is not a string."
|
@@ -0,0 +1,12 @@
|
|
1
|
+
open Core.Std
|
2
|
+
|
3
|
+
open Model
|
4
|
+
|
5
|
+
let optional_int ~(none: int) = function
|
6
|
+
| Int n when n = none -> "None"
|
7
|
+
| Int n -> "(Some " ^ Int.to_string n ^ ")"
|
8
|
+
| _ -> failwith "can't handle non-int parameter"
|
9
|
+
|
10
|
+
let fixup ~(stringify: parameter -> string) ~(slug: string) ~(key: string) ~(value: parameter) = match (slug, key) with
|
11
|
+
| ("hamming", "expected") -> optional_int (-1) value
|
12
|
+
| _ -> stringify value
|
@@ -0,0 +1,25 @@
|
|
1
|
+
open Core.Std
|
2
|
+
|
3
|
+
let is_directory =
|
4
|
+
Command.Spec.Arg_type.create
|
5
|
+
(fun n ->
|
6
|
+
match Sys.is_directory n with
|
7
|
+
| `Yes -> n
|
8
|
+
| `No | `Unknown ->
|
9
|
+
eprintf "'%s' is not a regular folder.\n%!" n;
|
10
|
+
exit 1
|
11
|
+
)
|
12
|
+
|
13
|
+
let command =
|
14
|
+
Command.basic
|
15
|
+
~summary:"Generates test code from canonical data."
|
16
|
+
Command.Spec.(
|
17
|
+
empty
|
18
|
+
+> flag "-t" (optional_with_default "./templates" is_directory) ~doc:"string Directory containing templates."
|
19
|
+
+> flag "-c" (optional_with_default "../../../x-common/exercises" is_directory) ~doc:"string Directory containing canonical data."
|
20
|
+
+> flag "-o" (optional_with_default "../../exercises" is_directory) ~doc:"string Directory to output generated tests."
|
21
|
+
)
|
22
|
+
(fun templates_folder canonical_data_folder output_folder () -> Test_generator.run templates_folder canonical_data_folder output_folder)
|
23
|
+
|
24
|
+
let () =
|
25
|
+
Command.run ~version:"0.1" command
|
@@ -0,0 +1,62 @@
|
|
1
|
+
open Core.Std
|
2
|
+
open Parser
|
3
|
+
open Model
|
4
|
+
open Utils
|
5
|
+
open Codegen
|
6
|
+
open Special_cases
|
7
|
+
|
8
|
+
let find_template ~(template_text: string): (int * int * string) option =
|
9
|
+
let open Option.Monad_infix in
|
10
|
+
let str_contains pattern s = String.substr_index s ~pattern |> Option.is_some in
|
11
|
+
let lines = String.split_lines template_text |> List.to_array in
|
12
|
+
let start_index = find_arrayi lines ~f:(str_contains "(* GENERATED-CODE") |> Option.map ~f:fst in
|
13
|
+
let finish_index = (start_index >>= (fun start -> find_arrayi ~start lines ~f:(str_contains "END GENERATED-CODE *"))) |> Option.map ~f:fst in
|
14
|
+
let template_lines = Option.map2 start_index finish_index (fun s -> Array.slice lines (s+1)) in
|
15
|
+
Option.map2 start_index template_lines ~f:(fun s l -> (s, s + 1 + Array.length l, String.concat_array l ~sep:"\n"))
|
16
|
+
|
17
|
+
let splice_in_filled_in_code (start: int) (finish: int) ~(template: string) (substs: string list): string =
|
18
|
+
let lines = String.split_lines template |> List.to_array in
|
19
|
+
let before = Array.slice lines 0 start in
|
20
|
+
let subst = Array.of_list substs in
|
21
|
+
let after = Array.slice lines (finish + 1) (Array.length lines) in
|
22
|
+
let join = String.concat_array ~sep:"\n" in
|
23
|
+
String.concat [join before; join subst; join after] ~sep:"\n"
|
24
|
+
|
25
|
+
type content = string
|
26
|
+
|
27
|
+
let find_nested_files (name: string) (base: string): (string * content) list =
|
28
|
+
Sys.ls_dir base
|
29
|
+
|> List.filter ~f:(fun slug -> Sys.is_directory_exn (base ^ "/" ^ slug))
|
30
|
+
|> List.filter ~f:(fun slug -> Sys.file_exists_exn (base ^ "/" ^ slug ^ "/" ^ name))
|
31
|
+
|> List.map ~f:(fun slug -> (slug, In_channel.read_all (base ^ "/" ^ slug ^ "/" ^ name)))
|
32
|
+
|
33
|
+
let find_templates = find_nested_files "template.ml"
|
34
|
+
|
35
|
+
let find_canonical_data_files = find_nested_files "canonical-data.json"
|
36
|
+
|
37
|
+
let combine_files (templates: (string * content) list) (canonical_data: (string * content) list): (string * content * content) list =
|
38
|
+
List.filter_map templates ~f:(fun (n,t) -> (List.Assoc.find canonical_data ~equal:String.equal n |> Option.map ~f:(fun c -> (n,t,c))))
|
39
|
+
|
40
|
+
let generate_code ~slug ~template_file ~canonical_data_file =
|
41
|
+
let template = find_template template_file in
|
42
|
+
let template = Result.of_option template "cannot find a template" in
|
43
|
+
let cases = parse_json_text canonical_data_file in
|
44
|
+
let cases = Result.map_error cases show_error in
|
45
|
+
let open Result.Monad_infix in
|
46
|
+
template >>= fun (s,e,template) ->
|
47
|
+
cases >>= fun cs ->
|
48
|
+
let Ok substs = generate_code (fixup ~stringify:parameter_to_string ~slug) template cs in
|
49
|
+
Result.return (splice_in_filled_in_code s e ~template:template_file substs)
|
50
|
+
|
51
|
+
let output_tests (files: (string * content * content) list) (output_folder: string): unit =
|
52
|
+
let output_filepath name = output_folder ^ "/" ^ name ^ "/test.ml" in
|
53
|
+
let output1 (slug,t,c) =
|
54
|
+
let Ok code = generate_code slug t c in
|
55
|
+
Out_channel.write_all (output_filepath slug) code in
|
56
|
+
List.iter files ~f:output1
|
57
|
+
|
58
|
+
let run ~(templates_folder: string) ~(canonical_data_folder: string) ~(output_folder: string) =
|
59
|
+
let templates = find_templates templates_folder in
|
60
|
+
let canonical_data_files = find_canonical_data_files canonical_data_folder in
|
61
|
+
let combined = combine_files templates canonical_data_files in
|
62
|
+
output_tests combined output_folder
|
@@ -0,0 +1,6 @@
|
|
1
|
+
open Core.Std
|
2
|
+
|
3
|
+
(* finds a template within a file, returning the line numbers of the comment starting and ending the template, and the body of the template *)
|
4
|
+
val find_template : template_text: string -> (int * int * string) option
|
5
|
+
|
6
|
+
val run : templates_folder: string -> canonical_data_folder: string -> output_folder: string -> unit
|
@@ -0,0 +1,32 @@
|
|
1
|
+
open Core.Std
|
2
|
+
open Yojson.Safe
|
3
|
+
open Yojson.Safe.Util
|
4
|
+
|
5
|
+
let map2 (f: 'a -> 'b -> 'c) (r1: ('a, 'e) Result.t) (r2: ('b, 'e) Result.t): ('c, 'e) Result.t = match (r1, r2) with
|
6
|
+
| (Error x, _) -> Error x
|
7
|
+
| (_, Error x) -> Error x
|
8
|
+
| (Ok a, Ok b) -> Ok (f a b)
|
9
|
+
|
10
|
+
let sequence (rs: (('a, 'e) Result.t) list): (('a list), 'e) Result.t =
|
11
|
+
List.fold_right rs ~init:(Ok []) ~f:(map2 (fun x xs -> x :: xs))
|
12
|
+
|
13
|
+
let to_list_option json =
|
14
|
+
try Some (to_list json) with Type_error _ -> None
|
15
|
+
|
16
|
+
let to_list_note error json =
|
17
|
+
try Ok (to_list json) with Type_error _ -> Error error
|
18
|
+
|
19
|
+
let to_string_note error json =
|
20
|
+
try Ok (to_string json) with Type_error _ -> Error error
|
21
|
+
|
22
|
+
let safe_to_int_option json =
|
23
|
+
try Some (to_int json) with Type_error _ -> None
|
24
|
+
|
25
|
+
let (>>) f g = Fn.compose f g
|
26
|
+
|
27
|
+
let find_arrayi ?start:(start = 0) xs ~f =
|
28
|
+
let rec go i =
|
29
|
+
if i >= Array.length xs then None
|
30
|
+
else if f xs.(i) then Some (i, xs.(i))
|
31
|
+
else go (i + 1) in
|
32
|
+
go start
|
@@ -0,0 +1,18 @@
|
|
1
|
+
open Core.Std
|
2
|
+
open Yojson.Safe
|
3
|
+
|
4
|
+
val map2 : ('a -> 'b -> 'c) -> ('a, 'e) Result.t -> ('b, 'e) Result.t -> ('c, 'e) Result.t
|
5
|
+
|
6
|
+
val sequence : (('a, 'e) Result.t) list -> (('a list), 'e) Result.t
|
7
|
+
|
8
|
+
val to_list_option : json -> json list option
|
9
|
+
|
10
|
+
val to_list_note : 'e -> json -> ((json list, 'e) Result.t)
|
11
|
+
|
12
|
+
val to_string_note : 'e -> json -> ((string, 'e) Result.t)
|
13
|
+
|
14
|
+
val safe_to_int_option : json -> int option
|
15
|
+
|
16
|
+
val find_arrayi : ?start:int -> 'a array -> f:('a -> bool) -> (int * 'a) option
|
17
|
+
|
18
|
+
val (>>) : ('a -> 'b) -> ('c -> 'a) -> 'c -> 'b
|