mutiny 0.2.4 → 0.2.5
Sign up to get free protection for your applications and to get access to all the features.
- 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
|