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.
@@ -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
+ `
@@ -1,7 +1,8 @@
1
1
  package leap
2
2
 
3
3
  // Source: exercism/x-common
4
- // Commit: be6fa53 leap: Rewrite the test cases and their descriptions
4
+ // Commit: cc65ebe leap: Fix canonical-data.json formatting
5
+ // x-common version: 1.0.0
5
6
 
6
7
  var testCases = []struct {
7
8
  year int
@@ -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
- // Source: {{.Ori}}
36
- {{if .Commit}}// Commit: {{.Commit}}
37
- {{end}}
35
+ {{.Header}}
38
36
 
39
37
  var testCases = []struct {
40
38
  year int
@@ -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, jOri, jCommit := getPath(jFile)
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
- Ori string
71
- Commit string
72
- J interface{}
73
- }{jOri, jCommit, j}
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, jOri, jCommit string) {
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
+ }
@@ -11,6 +11,7 @@
11
11
  ],
12
12
  "ignored": [
13
13
  "docs",
14
+ "templates",
14
15
  "x-common",
15
16
  "img"
16
17
  ],
@@ -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}}
@@ -18,7 +18,7 @@ install:
18
18
  - pip install -r requirements-travis.txt
19
19
 
20
20
  before_script:
21
- - flake8 ./exercises/ --max-line-length=99 --select=E,W
21
+ - flake8
22
22
 
23
23
  script:
24
24
  - ./test/check-exercises.py
@@ -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 largely obey the [PEP8 style guide](https://www.python.org/dev/peps/pep-0008/).
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. Here are the settings used by the build system:
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
 
@@ -101,6 +101,15 @@
101
101
  "topics": [
102
102
  ]
103
103
  },
104
+ {
105
+ "slug": "rotational-cipher",
106
+ "difficulty": 1,
107
+ "topics": [
108
+ "strings",
109
+ "logic",
110
+ "control-flow (loops)"
111
+ ]
112
+ },
104
113
  {
105
114
  "slug": "difference-of-squares",
106
115
  "difficulty": 1,
@@ -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
- self.assertEqual(
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 test_detect_simple_anagram(self):
14
- self.assertEqual(
15
- ['tan'],
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 test_detect_multiple_anagrams(self):
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 test_does_not_confuse_different_duplicates(self):
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 test_eliminate_anagram_subsets(self):
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 test_detect_anagram(self):
38
- self.assertEqual(
39
- ['inlets'],
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 test_multiple_anagrams(self):
32
+ def test_detects_three_anagrams(self):
33
+ candidates = [
34
+ "gallery", "ballerina", "regally", "clergy", "largely", "leading"
35
+ ]
44
36
  self.assertEqual(
45
- 'gallery regally largely'.split(),
46
- detect_anagrams(
47
- 'allergy',
48
- 'gallery ballerina regally clergy largely leading'.split()
49
- )
50
- )
51
-
52
- def test_anagrams_are_case_insensitive(self):
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
- ['Carthorse'],
55
- detect_anagrams('Orchestra',
56
- 'cashregister Carthorse radishes'.split())
57
- )
50
+ detect_anagrams("Orchestra", candidates), ["Carthorse"])
58
51
 
59
- def test_same_word_isnt_anagram(self):
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
- detect_anagrams('go', 'go Go GO'.split())
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__':