mutiny 0.2.4 → 0.2.5
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/.rubocop.yml +1 -1
- data/Gemfile.lock +1 -1
- data/RELEASES.md +5 -0
- data/lib/mutiny/analysis/analyser/default.rb +13 -0
- data/lib/mutiny/analysis/analyser.rb +22 -5
- data/lib/mutiny/configuration.rb +7 -22
- data/lib/mutiny/mode/check.rb +2 -2
- data/lib/mutiny/mode/mutate.rb +2 -2
- data/lib/mutiny/mode/score.rb +3 -11
- data/lib/mutiny/mutants/mutant_set.rb +4 -0
- data/lib/mutiny/mutants/storage/file_store.rb +1 -1
- data/lib/mutiny/mutants/storage/mutant_file.rb +3 -7
- data/lib/mutiny/mutants/storage.rb +10 -5
- data/lib/mutiny/tests/test_set.rb +4 -4
- data/lib/mutiny/version.rb +1 -1
- data/lib/mutiny.rb +0 -1
- data/spec/unit/mutants/storage_spec.rb +14 -15
- data/spec/unit/{tests → subjects}/test_set_spec.rb +7 -49
- metadata +5 -6
- data/lib/mutiny/integration.rb +0 -19
- data/lib/mutiny/tests/selection/default.rb +0 -11
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ca31e6c61eac704333513e20a7ba33416eacb370
|
4
|
+
data.tar.gz: 2c08a4a4337d1d6c9a842f995baa993c9f7ea460
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: deb63bad6550131c72454459b7eafd301f7ad5b4641498e42b262a17dacf7f77636a790daa15addb55bf749f410845eea82843f5cacc5e8324606505d400988a
|
7
|
+
data.tar.gz: 649fdc273f4e3ab181e7cb0f806b78996754d08f9beb2e98627a7dbca59aae5f9cafde975e6351c9ecc2d97e1c94d8c5b968ec90287b043e631ba746c4aa61d1
|
data/.rubocop.yml
CHANGED
data/Gemfile.lock
CHANGED
data/RELEASES.md
CHANGED
@@ -1,5 +1,10 @@
|
|
1
1
|
# Release History
|
2
2
|
|
3
|
+
## v0.2.5 (16 February 2016)
|
4
|
+
* Fix bug in the --cached switch of the score command, which was preventing mutants being correctly loaded from disk on Linux.
|
5
|
+
* Fix bug in the --cached switch of the score command, which was causing mutiny to report the incorrect path to subject files.
|
6
|
+
* Improve extensibility by supporting different implementations of the analyser. This has removed the need to have test selection as an extension point (for now).
|
7
|
+
|
3
8
|
## v0.2.4 (10 February 2016)
|
4
9
|
* Add --cached switch to the score command, which loads mutants from disk rather than generating them anew.
|
5
10
|
* Various changes to improve extensibility (i.e., mutant storage, test selection) and capabilities (i.e., mutant location and test hooks).
|
@@ -1,18 +1,20 @@
|
|
1
|
+
require_relative "../isolation"
|
1
2
|
require_relative "results"
|
2
3
|
|
3
4
|
module Mutiny
|
4
5
|
module Analysis
|
5
6
|
class Analyser
|
6
|
-
attr_reader :
|
7
|
+
attr_reader :integration
|
7
8
|
|
8
|
-
def initialize(
|
9
|
-
@mutant_set = mutant_set
|
9
|
+
def initialize(integration:)
|
10
10
|
@integration = integration
|
11
11
|
end
|
12
12
|
|
13
|
-
def call
|
13
|
+
def call(mutant_set)
|
14
14
|
results = Results.new
|
15
15
|
|
16
|
+
before_all(mutant_set)
|
17
|
+
|
16
18
|
mutant_set.mutants.each do |mutant|
|
17
19
|
results.add(mutant, analyse(mutant))
|
18
20
|
end
|
@@ -20,11 +22,26 @@ module Mutiny
|
|
20
22
|
results
|
21
23
|
end
|
22
24
|
|
25
|
+
protected
|
26
|
+
|
27
|
+
def before_all(_mutant_set)
|
28
|
+
end
|
29
|
+
|
30
|
+
def select_tests(_mutant)
|
31
|
+
fail "No implementation has been provided for select_tests"
|
32
|
+
end
|
33
|
+
|
23
34
|
private
|
24
35
|
|
25
36
|
def analyse(mutant)
|
26
37
|
mutant.apply
|
27
|
-
mutant.stillborn? ? nil :
|
38
|
+
mutant.stillborn? ? nil : run_tests(select_tests(mutant))
|
39
|
+
end
|
40
|
+
|
41
|
+
def run_tests(test_set)
|
42
|
+
Isolation.call do
|
43
|
+
integration.run(test_set, fail_fast: true)
|
44
|
+
end
|
28
45
|
end
|
29
46
|
end
|
30
47
|
end
|
data/lib/mutiny/configuration.rb
CHANGED
@@ -1,13 +1,14 @@
|
|
1
1
|
require_relative 'pattern'
|
2
2
|
require_relative 'reporter/stdout'
|
3
|
-
require_relative 'tests/selection/default'
|
4
3
|
require_relative 'integration/rspec'
|
5
4
|
require_relative 'mutants/ruby'
|
6
5
|
require_relative 'mutants/storage'
|
6
|
+
require_relative 'analysis/analyser/default'
|
7
7
|
|
8
8
|
module Mutiny
|
9
9
|
class Configuration
|
10
|
-
attr_reader :loads, :requires, :patterns
|
10
|
+
attr_reader :loads, :requires, :patterns
|
11
|
+
attr_reader :reporter, :integration, :mutator, :mutant_storage, :analyser
|
11
12
|
|
12
13
|
def initialize(loads: [], requires: [], patterns: [])
|
13
14
|
@loads = loads
|
@@ -16,8 +17,10 @@ module Mutiny
|
|
16
17
|
@patterns.map!(&Pattern.method(:new))
|
17
18
|
|
18
19
|
@reporter = Reporter::Stdout.new
|
19
|
-
@
|
20
|
-
@
|
20
|
+
@integration = Integration::RSpec.new
|
21
|
+
@mutator = Mutants::Ruby.new
|
22
|
+
@mutant_storage = Mutants::Storage.new
|
23
|
+
@analyser = Analysis::Analyser::Default.new(integration: @integration)
|
21
24
|
end
|
22
25
|
|
23
26
|
def load_paths
|
@@ -27,23 +30,5 @@ module Mutiny
|
|
27
30
|
def can_load?(source_path)
|
28
31
|
load_paths.any? { |load_path| source_path.start_with?(load_path) }
|
29
32
|
end
|
30
|
-
|
31
|
-
class Mutants
|
32
|
-
attr_reader :mutator, :storage
|
33
|
-
|
34
|
-
def initialize
|
35
|
-
@mutator = Mutiny::Mutants::Ruby.new
|
36
|
-
@storage = Mutiny::Mutants::Storage.new
|
37
|
-
end
|
38
|
-
end
|
39
|
-
|
40
|
-
class Tests
|
41
|
-
attr_reader :selection, :integration
|
42
|
-
|
43
|
-
def initialize
|
44
|
-
@selection = Mutiny::Tests::Selection::Default.new
|
45
|
-
@integration = Mutiny::Integration::RSpec.new(@selection)
|
46
|
-
end
|
47
|
-
end
|
48
33
|
end
|
49
34
|
end
|
data/lib/mutiny/mode/check.rb
CHANGED
@@ -51,11 +51,11 @@ module Mutiny
|
|
51
51
|
end
|
52
52
|
|
53
53
|
def test_set
|
54
|
-
@test_set ||= configuration.
|
54
|
+
@test_set ||= configuration.integration.tests.for_all(environment.subjects)
|
55
55
|
end
|
56
56
|
|
57
57
|
def test_run
|
58
|
-
@test_run ||= configuration.
|
58
|
+
@test_run ||= configuration.integration.run(test_set)
|
59
59
|
end
|
60
60
|
end
|
61
61
|
end
|
data/lib/mutiny/mode/mutate.rb
CHANGED
@@ -22,11 +22,11 @@ module Mutiny
|
|
22
22
|
end
|
23
23
|
|
24
24
|
def mutant_set
|
25
|
-
@mutant_set ||= configuration.
|
25
|
+
@mutant_set ||= configuration.mutator.mutants_for(environment.subjects)
|
26
26
|
end
|
27
27
|
|
28
28
|
def mutant_storage
|
29
|
-
@store ||= configuration.
|
29
|
+
@store ||= configuration.mutant_storage
|
30
30
|
end
|
31
31
|
end
|
32
32
|
end
|
data/lib/mutiny/mode/score.rb
CHANGED
@@ -1,4 +1,3 @@
|
|
1
|
-
require_relative "../analysis/analyser"
|
2
1
|
require_relative "../output/table"
|
3
2
|
|
4
3
|
module Mutiny
|
@@ -54,14 +53,7 @@ module Mutiny
|
|
54
53
|
end
|
55
54
|
|
56
55
|
def results
|
57
|
-
@results ||= analyser.call
|
58
|
-
end
|
59
|
-
|
60
|
-
def analyser
|
61
|
-
Analysis::Analyser.new(
|
62
|
-
mutant_set: mutant_set,
|
63
|
-
integration: configuration.tests.integration
|
64
|
-
)
|
56
|
+
@results ||= configuration.analyser.call(mutant_set)
|
65
57
|
end
|
66
58
|
|
67
59
|
def mutant_set
|
@@ -70,9 +62,9 @@ module Mutiny
|
|
70
62
|
|
71
63
|
def initialize_mutant_set
|
72
64
|
if options[:cached]
|
73
|
-
configuration.
|
65
|
+
configuration.mutant_storage.load_for(environment.subjects)
|
74
66
|
else
|
75
|
-
configuration.
|
67
|
+
configuration.mutator.mutants_for(environment.subjects)
|
76
68
|
end
|
77
69
|
end
|
78
70
|
end
|
@@ -15,9 +15,7 @@ module Mutiny
|
|
15
15
|
|
16
16
|
def load(absolute_path)
|
17
17
|
path = Path.from_absolute(path: absolute_path, root: mutant_directory)
|
18
|
-
|
19
|
-
.merge(deserialised_contents(path)) { |_, left, right| left.merge(right) }
|
20
|
-
.merge(deserialised_filename(path)) { |_, left, right| left.merge(right) }
|
18
|
+
deep_merge(deserialised_contents(path), deserialised_filename(path))
|
21
19
|
end
|
22
20
|
|
23
21
|
def store(mutant)
|
@@ -28,10 +26,8 @@ module Mutiny
|
|
28
26
|
|
29
27
|
private
|
30
28
|
|
31
|
-
def
|
32
|
-
{
|
33
|
-
subject: { root: mutant_directory }
|
34
|
-
}
|
29
|
+
def deep_merge(hash1, hash2)
|
30
|
+
hash1.merge(hash2) { |_, h1_member, h2_member| h1_member.merge(h2_member) }
|
35
31
|
end
|
36
32
|
|
37
33
|
def deserialised_filename(path)
|
@@ -13,15 +13,20 @@ module Mutiny
|
|
13
13
|
store.save_all(mutants)
|
14
14
|
end
|
15
15
|
|
16
|
-
def
|
17
|
-
mutants = store.load_all.map do |
|
18
|
-
subject =
|
19
|
-
|
20
|
-
Mutant.new(**mutant_specification)
|
16
|
+
def load_for(subjects)
|
17
|
+
mutants = store.load_all.map do |mutant_spec|
|
18
|
+
mutant_spec[:subject] = resolve_subject(subjects, **mutant_spec[:subject])
|
19
|
+
Mutant.new(**mutant_spec)
|
21
20
|
end
|
22
21
|
|
23
22
|
MutantSet.new(*mutants)
|
24
23
|
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def resolve_subject(subjects, name:, path:)
|
28
|
+
subjects.find { |subject| subject.name == name && subject.relative_path == path }
|
29
|
+
end
|
25
30
|
end
|
26
31
|
end
|
27
32
|
end
|
@@ -18,12 +18,12 @@ module Mutiny
|
|
18
18
|
tests.map(&:location)
|
19
19
|
end
|
20
20
|
|
21
|
-
def
|
22
|
-
subset { |test|
|
21
|
+
def for_all(subject_set)
|
22
|
+
subset { |test| subject_set.names.include?(test.expression) }
|
23
23
|
end
|
24
24
|
|
25
|
-
def
|
26
|
-
subset { |test|
|
25
|
+
def for(subject)
|
26
|
+
subset { |test| subject.name == test.expression }
|
27
27
|
end
|
28
28
|
|
29
29
|
def subset(&block)
|
data/lib/mutiny/version.rb
CHANGED
data/lib/mutiny.rb
CHANGED
@@ -5,42 +5,41 @@ module Mutiny
|
|
5
5
|
store = instance_double(Storage::FileStore)
|
6
6
|
allow(store).to receive(:load_all).and_return(loaded_data)
|
7
7
|
|
8
|
+
first_subject = Subjects::Subject.new(name: "Two", path: "two.rb", root: ".")
|
9
|
+
second_subject = Subjects::Subject.new(name: "Four", path: "four.rb", root: ".")
|
10
|
+
subjects = Subjects::SubjectSet.new([first_subject, second_subject])
|
11
|
+
|
12
|
+
expected_mutant_set = MutantSet.new(
|
13
|
+
Mutant.new(subject: first_subject, code: "2 - 2", index: 0),
|
14
|
+
Mutant.new(subject: first_subject, code: "2 * 2", index: 1),
|
15
|
+
Mutant.new(subject: second_subject, code: "4 - 4", index: 2)
|
16
|
+
)
|
17
|
+
|
8
18
|
storage = Storage.new(store: store)
|
9
|
-
expect(storage.
|
19
|
+
expect(storage.load_for(subjects)).to eq(expected_mutant_set)
|
10
20
|
end
|
11
21
|
|
12
22
|
# rubocop:disable MethodLength
|
13
23
|
def loaded_data
|
14
24
|
[
|
15
25
|
{
|
16
|
-
subject: { name: "Two", path: "two.rb"
|
26
|
+
subject: { name: "Two", path: "two.rb" },
|
17
27
|
code: "2 - 2",
|
18
28
|
index: 0
|
19
29
|
},
|
20
30
|
{
|
21
|
-
subject: { name: "Two", path: "two.rb"
|
31
|
+
subject: { name: "Two", path: "two.rb" },
|
22
32
|
code: "2 * 2",
|
23
33
|
index: 1
|
24
34
|
},
|
25
35
|
{
|
26
|
-
subject: { name: "Four", path: "four.rb"
|
36
|
+
subject: { name: "Four", path: "four.rb" },
|
27
37
|
code: "4 - 4",
|
28
38
|
index: 0
|
29
39
|
}
|
30
40
|
]
|
31
41
|
end
|
32
42
|
# rubocop:enable MethodLength
|
33
|
-
|
34
|
-
def expected_mutant_set
|
35
|
-
first_subject = Subjects::Subject.new(name: "Two", path: "two.rb", root: "~/Code/sums")
|
36
|
-
second_subject = Subjects::Subject.new(name: "Four", path: "four.rb", root: "~/Code/sums")
|
37
|
-
|
38
|
-
MutantSet.new(
|
39
|
-
Mutant.new(subject: first_subject, code: "2 - 2", index: 0),
|
40
|
-
Mutant.new(subject: first_subject, code: "2 * 2", index: 1),
|
41
|
-
Mutant.new(subject: second_subject, code: "4 - 4", index: 2)
|
42
|
-
)
|
43
|
-
end
|
44
43
|
end
|
45
44
|
end
|
46
45
|
end
|
@@ -33,84 +33,42 @@ module Mutiny
|
|
33
33
|
end
|
34
34
|
end
|
35
35
|
|
36
|
-
context "for
|
36
|
+
context "for" do
|
37
37
|
it "should return only those tests (whose expression) matches a subject" do
|
38
38
|
subjects = subject_set_for("Max", "Min")
|
39
39
|
test_set = test_set_for("Subtract", "Min", "Add")
|
40
|
-
selected = test_set.for_subjects(subjects)
|
41
40
|
|
42
|
-
expect(
|
41
|
+
expect(test_set.for_all(subjects)).to eq(test_set.subset { |t| t.expression == "Min" })
|
43
42
|
end
|
44
43
|
|
45
44
|
it "should return multiple tests for a single subject" do
|
46
45
|
subjects = subject_set_for("Min")
|
47
46
|
test_set = test_set_for("Min", "Max", "Min", "Max", "Min")
|
48
|
-
selected = test_set.for_subjects(subjects)
|
49
47
|
|
50
|
-
expect(
|
48
|
+
expect(test_set.for_all(subjects)).to eq(test_set.subset { |t| t.expression == "Min" })
|
51
49
|
end
|
52
50
|
|
53
51
|
it "should return no tests when there are no tests" do
|
54
52
|
subjects = subject_set_for("Max", "Min")
|
55
53
|
test_set = TestSet.empty
|
56
|
-
selected = test_set.for_subjects(subjects)
|
57
54
|
|
58
|
-
expect(
|
55
|
+
expect(test_set.for_all(subjects)).to eq(TestSet.empty)
|
59
56
|
end
|
60
57
|
|
61
58
|
it "should return no tests when there are no relevant subjects" do
|
62
59
|
subjects = subject_set_for("Max", "Min")
|
63
60
|
test_set = test_set_for("Subtract", "Add")
|
64
|
-
selected = test_set.for_subjects(subjects)
|
65
61
|
|
66
|
-
expect(
|
62
|
+
expect(test_set.for_all(subjects)).to eq(TestSet.empty)
|
67
63
|
end
|
68
64
|
|
69
65
|
def subject_set_for(*names)
|
70
66
|
Subjects::SubjectSet.new(names.map { |n| Subjects::Subject.new(name: n) })
|
71
67
|
end
|
72
|
-
end
|
73
|
-
|
74
|
-
context "for" do
|
75
|
-
it "should return only those tests (whose expression) matches the mutant's subject" do
|
76
|
-
subject = subject_for("Min")
|
77
|
-
test_set = test_set_for("Subtract", "Min", "Add")
|
78
|
-
selected = test_set.for_subject(subject)
|
79
|
-
|
80
|
-
expect(selected).to eq(test_set.subset { |t| t.expression == "Min" })
|
81
|
-
end
|
82
|
-
|
83
|
-
it "should return multiple tests for a single mutant" do
|
84
|
-
subject = subject_for("Min")
|
85
|
-
test_set = test_set_for("Min", "Max", "Min", "Max", "Min")
|
86
|
-
selected = test_set.for_subject(subject)
|
87
|
-
|
88
|
-
expect(selected).to eq(test_set.subset { |t| t.expression == "Min" })
|
89
|
-
end
|
90
68
|
|
91
|
-
|
92
|
-
|
93
|
-
test_set = TestSet.empty
|
94
|
-
selected = test_set.for_subject(subject)
|
95
|
-
|
96
|
-
expect(selected).to eq(TestSet.empty)
|
69
|
+
def test_set_for(*expressions)
|
70
|
+
TestSet.new(expressions.map { |e| Test.new(expression: e) })
|
97
71
|
end
|
98
|
-
|
99
|
-
it "should return no tests when mutant is irrelevant" do
|
100
|
-
subject = subject_for("Min")
|
101
|
-
test_set = test_set_for("Subtract", "Add")
|
102
|
-
selected = test_set.for_subject(subject)
|
103
|
-
|
104
|
-
expect(selected).to eq(TestSet.empty)
|
105
|
-
end
|
106
|
-
|
107
|
-
def subject_for(name)
|
108
|
-
Subjects::Subject.new(name: name)
|
109
|
-
end
|
110
|
-
end
|
111
|
-
|
112
|
-
def test_set_for(*expressions)
|
113
|
-
TestSet.new(expressions.map { |e| Test.new(expression: e) })
|
114
72
|
end
|
115
73
|
end
|
116
74
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mutiny
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Louis Rose
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-02-
|
11
|
+
date: 2016-02-16 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: parser
|
@@ -191,9 +191,9 @@ files:
|
|
191
191
|
- examples/untested_calculator/spec/spec_helper.rb
|
192
192
|
- lib/mutiny.rb
|
193
193
|
- lib/mutiny/analysis/analyser.rb
|
194
|
+
- lib/mutiny/analysis/analyser/default.rb
|
194
195
|
- lib/mutiny/analysis/results.rb
|
195
196
|
- lib/mutiny/configuration.rb
|
196
|
-
- lib/mutiny/integration.rb
|
197
197
|
- lib/mutiny/integration/hook.rb
|
198
198
|
- lib/mutiny/integration/rspec.rb
|
199
199
|
- lib/mutiny/integration/rspec/context.rb
|
@@ -245,7 +245,6 @@ files:
|
|
245
245
|
- lib/mutiny/subjects/subject.rb
|
246
246
|
- lib/mutiny/subjects/subject_set.rb
|
247
247
|
- lib/mutiny/tests.rb
|
248
|
-
- lib/mutiny/tests/selection/default.rb
|
249
248
|
- lib/mutiny/tests/test.rb
|
250
249
|
- lib/mutiny/tests/test_run.rb
|
251
250
|
- lib/mutiny/tests/test_set.rb
|
@@ -286,7 +285,7 @@ files:
|
|
286
285
|
- spec/unit/subjects/environment/type_spec.rb
|
287
286
|
- spec/unit/subjects/environment_spec.rb
|
288
287
|
- spec/unit/subjects/subject_spec.rb
|
289
|
-
- spec/unit/
|
288
|
+
- spec/unit/subjects/test_set_spec.rb
|
290
289
|
homepage: https://github.com/mutiny/mutiny
|
291
290
|
licenses:
|
292
291
|
- MIT
|
@@ -347,4 +346,4 @@ test_files:
|
|
347
346
|
- spec/unit/subjects/environment/type_spec.rb
|
348
347
|
- spec/unit/subjects/environment_spec.rb
|
349
348
|
- spec/unit/subjects/subject_spec.rb
|
350
|
-
- spec/unit/
|
349
|
+
- spec/unit/subjects/test_set_spec.rb
|
data/lib/mutiny/integration.rb
DELETED
@@ -1,19 +0,0 @@
|
|
1
|
-
require_relative "tests/selection/default"
|
2
|
-
require_relative "isolation"
|
3
|
-
|
4
|
-
module Mutiny
|
5
|
-
class Integration
|
6
|
-
attr_reader :test_selection
|
7
|
-
|
8
|
-
def initialize(test_selection = Tests::Selection::Default.new)
|
9
|
-
@test_selection = test_selection
|
10
|
-
end
|
11
|
-
|
12
|
-
def test(mutant)
|
13
|
-
Isolation.call do
|
14
|
-
selected_tests = test_selection.for(mutant, from: tests)
|
15
|
-
run(selected_tests, fail_fast: true)
|
16
|
-
end
|
17
|
-
end
|
18
|
-
end
|
19
|
-
end
|