trackler 2.0.8.38 → 2.0.8.39
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/trackler/version.rb +1 -1
- data/tracks/csharp/exercises/list-ops/HINTS.md +1 -1
- data/tracks/csharp/exercises/nucleotide-count/HINTS.md +3 -0
- data/tracks/go/config.json +8 -0
- data/tracks/go/exercises/bowling/bowling_test.go +77 -0
- data/tracks/go/exercises/bowling/cases_test.go +225 -0
- data/tracks/go/exercises/bowling/example.go +133 -0
- data/tracks/go/exercises/bowling/example_gen.go +117 -0
- data/tracks/go/exercises/leap/cases_test.go +2 -1
- data/tracks/go/exercises/leap/example_gen.go +1 -3
- data/tracks/go/gen/gen.go +39 -6
- data/tracks/perl6/bin/README.md +29 -0
- data/tracks/perl6/bin/exercise-gen.pl6 +36 -0
- data/tracks/perl6/config.json +1 -0
- data/tracks/perl6/exercises/hello-world/example.yaml +19 -0
- data/tracks/perl6/templates/test.mustache +53 -0
- data/tracks/python/.travis.yml +1 -1
- data/tracks/python/README.md +2 -6
- data/tracks/python/config.json +9 -0
- data/tracks/python/exercises/anagram/anagram_test.py +52 -48
- data/tracks/python/exercises/largest-series-product/largest_series_product_test.py +43 -42
- data/tracks/python/exercises/nth-prime/example.py +3 -0
- data/tracks/python/exercises/nth-prime/nth_prime_test.py +15 -5
- data/tracks/python/exercises/rotational-cipher/example.py +14 -0
- data/tracks/python/exercises/rotational-cipher/rotational_cipher.py +2 -0
- data/tracks/python/exercises/rotational-cipher/rotational_cipher_test.py +53 -0
- data/tracks/python/exercises/word-search/example.py +9 -8
- metadata +57 -2
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
// +build ignore
|
|
2
|
+
|
|
3
|
+
package main
|
|
4
|
+
|
|
5
|
+
import (
|
|
6
|
+
"log"
|
|
7
|
+
"text/template"
|
|
8
|
+
|
|
9
|
+
"../../gen"
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
func main() {
|
|
13
|
+
t, err := template.New("").Parse(tmpl)
|
|
14
|
+
if err != nil {
|
|
15
|
+
log.Fatal(err)
|
|
16
|
+
}
|
|
17
|
+
var j js
|
|
18
|
+
if err := gen.Gen("bowling", &j, t); err != nil {
|
|
19
|
+
log.Fatal(err)
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// The JSON structure we expect to be able to unmarshal into
|
|
24
|
+
type js struct {
|
|
25
|
+
Exercise string
|
|
26
|
+
Version string
|
|
27
|
+
Comments []string
|
|
28
|
+
Cases []OneCase
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// template applied to above data structure generates the Go test cases
|
|
32
|
+
|
|
33
|
+
type OneCase struct {
|
|
34
|
+
Description string
|
|
35
|
+
Property string
|
|
36
|
+
PreviousRolls []int `json:"previous_rolls"`
|
|
37
|
+
Roll int
|
|
38
|
+
Expected interface{}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// ScoreTest and RollTest help determine which type of test case
|
|
42
|
+
// to generate in the template.
|
|
43
|
+
func (c OneCase) ScoreTest() bool { return c.Property == "score" }
|
|
44
|
+
func (c OneCase) RollTest() bool { return c.Property == "roll" }
|
|
45
|
+
|
|
46
|
+
func (c OneCase) Valid() bool {
|
|
47
|
+
valid, _, _ := determineExpected(c.Expected)
|
|
48
|
+
return valid
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
func (c OneCase) Score() int {
|
|
52
|
+
_, score, _ := determineExpected(c.Expected)
|
|
53
|
+
return score
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
func (c OneCase) ExplainText() string {
|
|
57
|
+
_, _, explainText := determineExpected(c.Expected)
|
|
58
|
+
return explainText
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// determineExpected examines an .Expected interface{} object and determines
|
|
62
|
+
// whether a test case is valid(bool), has a score field, and/or has an expected error,
|
|
63
|
+
// returning valid, score, and error explanation text.
|
|
64
|
+
func determineExpected(expected interface{}) (bool, int, string) {
|
|
65
|
+
score, ok := expected.(float64)
|
|
66
|
+
if ok {
|
|
67
|
+
return ok, int(score), ""
|
|
68
|
+
}
|
|
69
|
+
m, ok := expected.(map[string]interface{})
|
|
70
|
+
if !ok {
|
|
71
|
+
return false, 0, ""
|
|
72
|
+
}
|
|
73
|
+
iError, ok := m["error"].(interface{})
|
|
74
|
+
if !ok {
|
|
75
|
+
return false, 0, ""
|
|
76
|
+
}
|
|
77
|
+
explainText, ok := iError.(string)
|
|
78
|
+
return false, 0, explainText
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Template to generate two sets of test cases, one for Score tests and one for Roll tests.
|
|
82
|
+
var tmpl = `package bowling
|
|
83
|
+
|
|
84
|
+
{{.Header}}
|
|
85
|
+
|
|
86
|
+
var scoreTestCases = []struct {
|
|
87
|
+
description string
|
|
88
|
+
previousRolls []int // bowling rolls to do before the Score() test
|
|
89
|
+
valid bool // true => no error, false => error expected
|
|
90
|
+
score int // when .valid == true, the expected score value
|
|
91
|
+
explainText string // when .valid == false, error explanation text
|
|
92
|
+
}{ {{range .J.Cases}}
|
|
93
|
+
{{if .ScoreTest}}{
|
|
94
|
+
{{printf "%q" .Description}},
|
|
95
|
+
{{printf "%#v" .PreviousRolls}},
|
|
96
|
+
{{printf "%v" .Valid}},
|
|
97
|
+
{{printf "%d" .Score}},
|
|
98
|
+
{{printf "%q" .ExplainText}},
|
|
99
|
+
},{{- end}}{{end}}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
var rollTestCases = []struct {
|
|
103
|
+
description string
|
|
104
|
+
previousRolls []int // bowling rolls to do before the Roll(roll) test
|
|
105
|
+
valid bool // true => no error, false => error expected
|
|
106
|
+
roll int // pin count for the test roll
|
|
107
|
+
explainText string // when .valid == false, error explanation text
|
|
108
|
+
}{ {{range .J.Cases}}
|
|
109
|
+
{{if .RollTest}}{
|
|
110
|
+
{{printf "%q" .Description}},
|
|
111
|
+
{{printf "%#v" .PreviousRolls}},
|
|
112
|
+
{{printf "%v" .Valid}},
|
|
113
|
+
{{printf "%d" .Roll}},
|
|
114
|
+
{{printf "%q" .ExplainText}},
|
|
115
|
+
},{{- end}}{{end}}
|
|
116
|
+
}
|
|
117
|
+
`
|
|
@@ -32,9 +32,7 @@ type js struct {
|
|
|
32
32
|
// template applied to above data structure generates the Go test cases
|
|
33
33
|
var tmpl = `package leap
|
|
34
34
|
|
|
35
|
-
|
|
36
|
-
{{if .Commit}}// Commit: {{.Commit}}
|
|
37
|
-
{{end}}
|
|
35
|
+
{{.Header}}
|
|
38
36
|
|
|
39
37
|
var testCases = []struct {
|
|
40
38
|
year int
|
data/tracks/go/gen/gen.go
CHANGED
|
@@ -32,6 +32,27 @@ var dirMetadata string
|
|
|
32
32
|
// Falls back to the present working directory.
|
|
33
33
|
var dirProblem string
|
|
34
34
|
|
|
35
|
+
// Header tells how the test data was generated, for display in the header of cases_test.go
|
|
36
|
+
type Header struct {
|
|
37
|
+
// Ori is a deprecated short name for Origin.
|
|
38
|
+
// TODO: Remove Ori once everything switches to Origin.
|
|
39
|
+
Ori string
|
|
40
|
+
Origin string
|
|
41
|
+
Commit string
|
|
42
|
+
Version string
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
func (h Header) String() string {
|
|
46
|
+
s := fmt.Sprintf("// Source: %s\n", h.Origin)
|
|
47
|
+
if h.Commit != "" {
|
|
48
|
+
s += fmt.Sprintf("// Commit: %s\n", h.Commit)
|
|
49
|
+
}
|
|
50
|
+
if h.Version != "" {
|
|
51
|
+
s += fmt.Sprintf("// x-common version: %s\n", h.Version)
|
|
52
|
+
}
|
|
53
|
+
return s
|
|
54
|
+
}
|
|
55
|
+
|
|
35
56
|
func init() {
|
|
36
57
|
if _, path, _, ok := runtime.Caller(0); ok {
|
|
37
58
|
dirMetadata = filepath.Join(path, "..", "..", "..", "x-common")
|
|
@@ -50,7 +71,7 @@ func Gen(exercise string, j interface{}, t *template.Template) error {
|
|
|
50
71
|
}
|
|
51
72
|
jFile := filepath.Join("exercises", exercise, "canonical-data.json")
|
|
52
73
|
// find and read the json source file
|
|
53
|
-
jPath,
|
|
74
|
+
jPath, jOrigin, jCommit := getPath(jFile)
|
|
54
75
|
jSrc, err := ioutil.ReadFile(filepath.Join(jPath, jFile))
|
|
55
76
|
if err != nil {
|
|
56
77
|
return err
|
|
@@ -65,12 +86,24 @@ func Gen(exercise string, j interface{}, t *template.Template) error {
|
|
|
65
86
|
return fmt.Errorf(`unexpected data structure: %v`, err)
|
|
66
87
|
}
|
|
67
88
|
|
|
89
|
+
// These fields are guaranteed to be in every problem
|
|
90
|
+
var commonMetadata struct {
|
|
91
|
+
Version string
|
|
92
|
+
}
|
|
93
|
+
if err := json.Unmarshal(jSrc, &commonMetadata); err != nil {
|
|
94
|
+
return fmt.Errorf(`Didn't contain version: %v`, err)
|
|
95
|
+
}
|
|
96
|
+
|
|
68
97
|
// package up a little meta data
|
|
69
98
|
d := struct {
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
99
|
+
Header
|
|
100
|
+
J interface{}
|
|
101
|
+
}{Header{
|
|
102
|
+
Ori: jOrigin,
|
|
103
|
+
Origin: jOrigin,
|
|
104
|
+
Commit: jCommit,
|
|
105
|
+
Version: commonMetadata.Version,
|
|
106
|
+
}, j}
|
|
74
107
|
|
|
75
108
|
// render the Go test cases
|
|
76
109
|
var b bytes.Buffer
|
|
@@ -86,7 +119,7 @@ func Gen(exercise string, j interface{}, t *template.Template) error {
|
|
|
86
119
|
return ioutil.WriteFile(filepath.Join(dirProblem, "cases_test.go"), src, 0666)
|
|
87
120
|
}
|
|
88
121
|
|
|
89
|
-
func getPath(jFile string) (jPath,
|
|
122
|
+
func getPath(jFile string) (jPath, jOrigin, jCommit string) {
|
|
90
123
|
// Ideally draw from a .json which is pulled from the official x-common
|
|
91
124
|
// repository. For development however, accept a file in current directory
|
|
92
125
|
// if there is no .json in source control. Also allow an override in any
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
The `exercise-gen.pl6` file can be used in the following ways:
|
|
2
|
+
* From within the directory of the exercise you wish to generate a test for.
|
|
3
|
+
* With arguments specifying which exercises you want to generate tests for.
|
|
4
|
+
e.g. `./exercise-gen.pl6 hello-world leap`
|
|
5
|
+
* With the argument `--all` to run the generator for all exercises.
|
|
6
|
+
i.e `./exercise-gen.pl6 --all`
|
|
7
|
+
|
|
8
|
+
The generator will retrieve data from an `example.yaml` file within
|
|
9
|
+
each exercise directory, and use the contained information to generate
|
|
10
|
+
test files using `templates/test.mustache`. If it finds a
|
|
11
|
+
`canonical-data.json` file in `x-common` for the exercise in
|
|
12
|
+
question it will be included.
|
|
13
|
+
|
|
14
|
+
Example of a yaml file:
|
|
15
|
+
```yaml
|
|
16
|
+
exercise: MyExercise
|
|
17
|
+
version: 1
|
|
18
|
+
plan: 10
|
|
19
|
+
modules:
|
|
20
|
+
- use: Data::Dump
|
|
21
|
+
- use: Foo::Bar
|
|
22
|
+
imports: 'MyClass &my-subroutine'
|
|
23
|
+
methods: 'foo bar'
|
|
24
|
+
tests: |
|
|
25
|
+
ok True, 'Perl 6 code here';
|
|
26
|
+
pass;
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
You must have `Template::Mustache` and `YAMLish` to run `exercise-gen.pl6`.
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
#!/usr/bin/env perl6
|
|
2
|
+
use v6;
|
|
3
|
+
use Template::Mustache;
|
|
4
|
+
use YAMLish;
|
|
5
|
+
|
|
6
|
+
my $base-dir = $?FILE.IO.resolve.parent.parent;
|
|
7
|
+
my @exercises;
|
|
8
|
+
|
|
9
|
+
if @*ARGS {
|
|
10
|
+
if @*ARGS[0] eq '--all' {
|
|
11
|
+
push @exercises, $_.basename for $base-dir.child('exercises').dir;
|
|
12
|
+
} else {
|
|
13
|
+
@exercises = @*ARGS;
|
|
14
|
+
}
|
|
15
|
+
} else {
|
|
16
|
+
say 'No args given; working in current directory.';
|
|
17
|
+
if 'example.yaml'.IO ~~ :f {
|
|
18
|
+
push @exercises, $*CWD.IO.basename;
|
|
19
|
+
} else {
|
|
20
|
+
say 'example.yaml not found; exiting.';
|
|
21
|
+
exit;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
for @exercises -> $exercise {
|
|
26
|
+
my $exercise-dir = $base-dir.child("exercises/$exercise");
|
|
27
|
+
next if (my $yaml = $exercise-dir.child('example.yaml')) !~~ :f;
|
|
28
|
+
my $cdata = $base-dir.child("x-common/exercises/$exercise/canonical-data.json");
|
|
29
|
+
my %data = load-yaml $yaml.slurp;
|
|
30
|
+
%data<cdata> = {:json($cdata.slurp)} if $cdata ~~ :f;
|
|
31
|
+
|
|
32
|
+
spurt (my $test = $exercise-dir.child("$exercise.t")),
|
|
33
|
+
Template::Mustache.render($base-dir.child('templates/test.mustache').slurp, %data);
|
|
34
|
+
$test.chmod(0o755);
|
|
35
|
+
say "$exercise generated.";
|
|
36
|
+
}
|
data/tracks/perl6/config.json
CHANGED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
exercise: HelloWorld
|
|
2
|
+
version: 2
|
|
3
|
+
plan: 3
|
|
4
|
+
imports: '&hello'
|
|
5
|
+
tests: |
|
|
6
|
+
#`[Go through the cases (hiding at the bottom of this file)
|
|
7
|
+
and check that &hello gives us the correct response.]
|
|
8
|
+
is &::('hello')(), |.<expected description> for @($c-data<cases>);
|
|
9
|
+
|
|
10
|
+
exercise_comment: The name of this exercise.
|
|
11
|
+
module_comment: "%*ENV<EXERCISM> is for tests not directly for the exercise, don't worry about these :)"
|
|
12
|
+
version_comment: The version we will be matching against the exercise.
|
|
13
|
+
plan_comment: This is how many tests we expect to run.
|
|
14
|
+
use_test_comment: Check that the module can be use-d.
|
|
15
|
+
version_test_comment: "If the exercise is updated, we want to make sure other people testing\nyour code don't think you've made a mistake if things have changed!"
|
|
16
|
+
imports_comment: Import '&hello' from 'HelloWorld'
|
|
17
|
+
cdata_test_comment: Ignore this for your exercise! Tells Exercism folks when exercise cases become out of date.
|
|
18
|
+
done_testing_comment: There are no more tests after this :)
|
|
19
|
+
INIT_comment: "'INIT' is a phaser, it makes sure that the test data is available before everything else\nstarts running (otherwise we'd have to shove the test data into the middle of the file!)"
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
#!/usr/bin/env perl6
|
|
2
|
+
use v6;{{=#`{{ }}=}}#`{{! Mustache tags double up as Perl 6 embedded comments}}
|
|
3
|
+
use Test;
|
|
4
|
+
use lib #`{{#cdata}}my $dir = #`{{/cdata}}$?FILE.IO.dirname;#`{{#lib_comment}} #`[#`{{&lib_comment}}]#`{{/lib_comment}}
|
|
5
|
+
#`{{#cdata}}use JSON::Tiny;
|
|
6
|
+
#`{{/cdata}}#`{{#modules}}use #`{{&use}};
|
|
7
|
+
#`{{/modules}}
|
|
8
|
+
|
|
9
|
+
my $exercise#`{{#exercise}} = '#`{{&exercise}}'#`{{/exercise}};#`{{#exercise_comment}} #`[#`{{&exercise_comment}}]#`{{/exercise_comment}}
|
|
10
|
+
my $version#`{{#version}} = v#`{{&version}}#`{{/version}};#`{{#version_comment}} #`[#`{{&version_comment}}]#`{{/version_comment}}
|
|
11
|
+
my $module = %*ENV<EXERCISM> ?? 'Example' !! $exercise;#`{{#module_comment}} #`[#`{{&module_comment}}]#`{{/module_comment}}#`{{#plan}}
|
|
12
|
+
plan #`{{&plan}};#`{{#plan_comment}} #`[#`{{&plan_comment}}]#`{{/plan_comment}}#`{{/plan}}
|
|
13
|
+
#`{{#use_test_comment}}
|
|
14
|
+
|
|
15
|
+
#`[#`{{&use_test_comment}}]#`{{/use_test_comment}}
|
|
16
|
+
use-ok $module or bail-out;
|
|
17
|
+
require ::($module);
|
|
18
|
+
#`{{#version_test_comment}}
|
|
19
|
+
|
|
20
|
+
#`[#`{{&version_test_comment}}]#`{{/version_test_comment}}
|
|
21
|
+
if ::($exercise).^ver !~~ $version {
|
|
22
|
+
warn "\nExercise version mismatch. Further tests may fail!"
|
|
23
|
+
~ "\n$exercise is $(::($exercise).^ver.gist). "
|
|
24
|
+
~ "Test is $($version.gist).\n";
|
|
25
|
+
bail-out 'Example version must match test version.' if %*ENV<EXERCISM>;
|
|
26
|
+
}
|
|
27
|
+
#`{{#imports}}#`{{#imports_comment}}
|
|
28
|
+
#`[#`{{&imports_comment}}]#`{{/imports_comment}}
|
|
29
|
+
require ::($module) <#`{{&imports}}>;
|
|
30
|
+
#`{{/imports}}#`{{#methods}}#`{{#methods_comment}}
|
|
31
|
+
#`[#`{{&methods_comment}}]#`{{/methods_comment}}
|
|
32
|
+
subtest 'Class methods', {
|
|
33
|
+
ok ::($exercise).can($_), $_ for <#`{{&methods}}>;
|
|
34
|
+
}
|
|
35
|
+
#`{{/methods}}#`{{#cdata}}
|
|
36
|
+
my $c-data;#`{{/cdata}}
|
|
37
|
+
#`{{&tests}}#`{{#cdata}}#`{{#cdata_test_comment}}
|
|
38
|
+
#`[#`{{&cdata_test_comment}}]#`{{/cdata_test_comment}}
|
|
39
|
+
if %*ENV<EXERCISM> && (my $c-data-file =
|
|
40
|
+
"$dir/../../x-common/exercises/{$dir.IO.resolve.basename}/canonical-data.json".IO.resolve) ~~ :f
|
|
41
|
+
{ is-deeply $c-data, from-json($c-data-file.slurp), 'canonical-data' } else { skip }
|
|
42
|
+
#`{{/cdata}}
|
|
43
|
+
|
|
44
|
+
done-testing;#`{{#done_testing_comment}} #`[#`{{&done_testing_comment}}]#`{{/done_testing_comment}}#`{{#cdata}}
|
|
45
|
+
#`{{#INIT_comment}}
|
|
46
|
+
|
|
47
|
+
#`[#`{{&INIT_comment}}]#`{{/INIT_comment}}
|
|
48
|
+
INIT {
|
|
49
|
+
$c-data := from-json q:to/END/;
|
|
50
|
+
|
|
51
|
+
#`{{&json}}
|
|
52
|
+
END
|
|
53
|
+
}#`{{/cdata}}
|
data/tracks/python/.travis.yml
CHANGED
data/tracks/python/README.md
CHANGED
|
@@ -34,13 +34,9 @@ python test/check-exercises.py
|
|
|
34
34
|
|
|
35
35
|
## Code Style
|
|
36
36
|
|
|
37
|
-
The Python code in this repo is meant to
|
|
37
|
+
The Python code in this repo is meant to follow the [PEP8 style guide](https://www.python.org/dev/peps/pep-0008/).
|
|
38
38
|
|
|
39
|
-
This repo uses [flake8](http://flake8.readthedocs.org/en/latest/) to enforce the coding standard. When you submit a PR, it needs to pass the flake8 tool with no warnings, or it won't be accepted.
|
|
40
|
-
|
|
41
|
-
```
|
|
42
|
-
flake8 [your-code-here.py] --max-line-length=99 --select=E,W
|
|
43
|
-
```
|
|
39
|
+
This repo uses [flake8](http://flake8.readthedocs.org/en/latest/) with default settings to enforce the coding standard. When you submit a PR, it needs to pass the flake8 tool with no warnings, or it won't be accepted.
|
|
44
40
|
|
|
45
41
|
## Pull Requests
|
|
46
42
|
|
data/tracks/python/config.json
CHANGED
|
@@ -3,69 +3,73 @@ import unittest
|
|
|
3
3
|
from anagram import detect_anagrams
|
|
4
4
|
|
|
5
5
|
|
|
6
|
+
# test cases adapted from `x-common//canonical-data.json` @ version: 1.0.1
|
|
7
|
+
|
|
6
8
|
class AnagramTests(unittest.TestCase):
|
|
7
9
|
def test_no_matches(self):
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
detect_anagrams('diaper', 'hello world zombies pants'.split())
|
|
11
|
-
)
|
|
10
|
+
candidates = ["hello", "world", "zombies", "pants"]
|
|
11
|
+
self.assertEqual(detect_anagrams("diaper", candidates), [])
|
|
12
12
|
|
|
13
|
-
def
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
detect_anagrams('ant', 'tan stand at'.split())
|
|
17
|
-
)
|
|
13
|
+
def test_detects_simple_anagram(self):
|
|
14
|
+
candidates = ["tan", "stand", "at"]
|
|
15
|
+
self.assertEqual(detect_anagrams("ant", candidates), ["tan"])
|
|
18
16
|
|
|
19
|
-
def
|
|
20
|
-
self.assertEqual(
|
|
21
|
-
['stream', 'maters'],
|
|
22
|
-
detect_anagrams('master', 'stream pigeon maters'.split())
|
|
23
|
-
)
|
|
17
|
+
def test_does_not_detect_false_positives(self):
|
|
18
|
+
self.assertEqual(detect_anagrams("galea", ["eagle"]), [])
|
|
24
19
|
|
|
25
|
-
def
|
|
20
|
+
def test_detects_two_anagrams(self):
|
|
21
|
+
candidates = ["stream", "pigeon", "maters"]
|
|
26
22
|
self.assertEqual(
|
|
27
|
-
[]
|
|
28
|
-
detect_anagrams('galea', ['eagle'])
|
|
29
|
-
)
|
|
23
|
+
detect_anagrams("master", candidates), ["stream", "maters"])
|
|
30
24
|
|
|
31
|
-
def
|
|
32
|
-
self.assertEqual(
|
|
33
|
-
[],
|
|
34
|
-
detect_anagrams('good', 'dog goody'.split())
|
|
35
|
-
)
|
|
25
|
+
def test_does_not_detect_anagram_subsets(self):
|
|
26
|
+
self.assertEqual(detect_anagrams("good", ["dog", "goody"]), [])
|
|
36
27
|
|
|
37
|
-
def
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
detect_anagrams('listen', 'enlists google inlets banana'.split())
|
|
41
|
-
)
|
|
28
|
+
def test_detects_anagram(self):
|
|
29
|
+
candidates = ["enlists", "google", "inlets", "banana"]
|
|
30
|
+
self.assertEqual(detect_anagrams("listen", candidates), ["inlets"])
|
|
42
31
|
|
|
43
|
-
def
|
|
32
|
+
def test_detects_three_anagrams(self):
|
|
33
|
+
candidates = [
|
|
34
|
+
"gallery", "ballerina", "regally", "clergy", "largely", "leading"
|
|
35
|
+
]
|
|
44
36
|
self.assertEqual(
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
)
|
|
51
|
-
|
|
52
|
-
def
|
|
37
|
+
detect_anagrams("allergy", candidates),
|
|
38
|
+
["gallery", "regally", "largely"])
|
|
39
|
+
|
|
40
|
+
def test_does_not_detect_identical_words(self):
|
|
41
|
+
candidates = ["corn", "dark", "Corn", "rank", "CORN", "cron", "park"]
|
|
42
|
+
self.assertEqual(detect_anagrams("corn", candidates), ["cron"])
|
|
43
|
+
|
|
44
|
+
def test_does_not_detect_non_anagrams_with_identical_checksum(self):
|
|
45
|
+
self.assertEqual(detect_anagrams("mass", ["last"]), [])
|
|
46
|
+
|
|
47
|
+
def test_detects_anagrams_case_insensitively(self):
|
|
48
|
+
candidates = ["cashregister", "Carthorse", "radishes"]
|
|
53
49
|
self.assertEqual(
|
|
54
|
-
[
|
|
55
|
-
detect_anagrams('Orchestra',
|
|
56
|
-
'cashregister Carthorse radishes'.split())
|
|
57
|
-
)
|
|
50
|
+
detect_anagrams("Orchestra", candidates), ["Carthorse"])
|
|
58
51
|
|
|
59
|
-
def
|
|
52
|
+
def test_detects_anagrams_using_case_insensitive_subjec(self):
|
|
53
|
+
candidates = ["cashregister", "carthorse", "radishes"]
|
|
60
54
|
self.assertEqual(
|
|
61
|
-
[]
|
|
62
|
-
detect_anagrams('banana', ['banana'])
|
|
63
|
-
)
|
|
55
|
+
detect_anagrams("Orchestra", candidates), ["carthorse"])
|
|
64
56
|
|
|
57
|
+
def test_detects_anagrams_using_case_insensitive_possible_matches(self):
|
|
58
|
+
candidates = ["cashregister", "Carthorse", "radishes"]
|
|
65
59
|
self.assertEqual(
|
|
66
|
-
[]
|
|
67
|
-
|
|
68
|
-
|
|
60
|
+
detect_anagrams("orchestra", candidates), ["Carthorse"])
|
|
61
|
+
|
|
62
|
+
def test_does_not_detect_a_word_as_its_own_anagram(self):
|
|
63
|
+
self.assertEqual(detect_anagrams("banana", ["Banana"]), [])
|
|
64
|
+
|
|
65
|
+
def test_does_not_detect_a_anagram_if_the_original_word_is_repeated(self):
|
|
66
|
+
self.assertEqual(detect_anagrams("go", ["go Go GO"]), [])
|
|
67
|
+
|
|
68
|
+
def test_anagrams_must_use_all_letters_exactly_once(self):
|
|
69
|
+
self.assertEqual(detect_anagrams("tapper", ["patter"]), [])
|
|
70
|
+
|
|
71
|
+
def test_capital_word_is_not_own_anagram(self):
|
|
72
|
+
self.assertEqual(detect_anagrams("BANANA", ["Banana"]), [])
|
|
69
73
|
|
|
70
74
|
|
|
71
75
|
if __name__ == '__main__':
|