trackler 2.0.8.38 → 2.0.8.39
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/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__':
|