mutiny 0.2.3 → 0.2.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +1 -1
- data/Gemfile.lock +1 -1
- data/RELEASES.md +4 -0
- data/bin/mutiny +5 -2
- data/lib/mutiny/analysis/analyser.rb +8 -14
- data/lib/mutiny/configuration.rb +23 -3
- data/lib/mutiny/integration/hook.rb +11 -0
- data/lib/mutiny/integration/rspec/hook.rb +32 -0
- data/lib/mutiny/integration/rspec/runner.rb +19 -9
- data/lib/mutiny/integration/rspec.rb +4 -2
- data/lib/mutiny/integration.rb +10 -3
- data/lib/mutiny/mode/check.rb +2 -2
- data/lib/mutiny/mode/mutate.rb +6 -2
- data/lib/mutiny/mode/score.rb +13 -2
- data/lib/mutiny/mode.rb +3 -2
- data/lib/mutiny/mutants/mutant/location.rb +27 -0
- data/lib/mutiny/mutants/mutant.rb +10 -3
- data/lib/mutiny/mutants/mutant_set.rb +2 -26
- data/lib/mutiny/mutants/mutation_set.rb +21 -7
- data/lib/mutiny/mutants/storage/file_store.rb +27 -0
- data/lib/mutiny/mutants/storage/mutant_file.rb +55 -0
- data/lib/mutiny/mutants/storage/mutant_file_contents.rb +33 -0
- data/lib/mutiny/mutants/storage/mutant_file_name.rb +32 -0
- data/lib/mutiny/mutants/storage/path.rb +31 -0
- data/lib/mutiny/mutants/storage.rb +27 -0
- data/lib/mutiny/tests/selection/default.rb +11 -0
- data/lib/mutiny/tests/test_set.rb +4 -4
- data/lib/mutiny/version.rb +1 -1
- data/spec/integration/mutate_spec.rb +21 -0
- data/spec/integration/score_cached_spec.rb +34 -0
- data/spec/integration/score_spec.rb +1 -1
- data/spec/unit/integration/rspec_spec.rb +45 -0
- data/spec/unit/mutants/mutant/location_spec.rb +23 -0
- data/spec/unit/mutants/storage/mutant_file_contents_spec.rb +49 -0
- data/spec/unit/mutants/storage/mutant_file_name_spec.rb +34 -0
- data/spec/unit/mutants/storage_spec.rb +46 -0
- data/spec/unit/{subjects → tests}/test_set_spec.rb +49 -7
- metadata +26 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b3b90916fcddcbfd2b6d6b373927154110c8d1a0
|
4
|
+
data.tar.gz: f8d277e9dc277bdde93a449e8a10018be5c96859
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 74a0c7772807505bc0d79f386fcd958d53ff1466cef6bb6053fe6ba2d978bf07b9fbfe17bb6db1c8243bf30a1750bcc9d8f8273fe902ab5360f62f305d45f7d8
|
7
|
+
data.tar.gz: 3180029d832460442147ff1623b4f535318a1226b1312d41232a736546475fce1f793827a0f16a4ae12e354a7f77d0ad3099f487f0fd21700b048c9a0f60e218
|
data/.travis.yml
CHANGED
data/Gemfile.lock
CHANGED
data/RELEASES.md
CHANGED
@@ -1,5 +1,9 @@
|
|
1
1
|
# Release History
|
2
2
|
|
3
|
+
## v0.2.4 (10 February 2016)
|
4
|
+
* Add --cached switch to the score command, which loads mutants from disk rather than generating them anew.
|
5
|
+
* Various changes to improve extensibility (i.e., mutant storage, test selection) and capabilities (i.e., mutant location and test hooks).
|
6
|
+
|
3
7
|
## v0.2.3 (26 January 2016)
|
4
8
|
* Add mutation name to each mutant written to disk
|
5
9
|
* Update to Ruby 2.2.3
|
data/bin/mutiny
CHANGED
@@ -39,8 +39,11 @@ end
|
|
39
39
|
desc 'Calculates a mutation score for your project'
|
40
40
|
long_desc 'Calculates a mutation score for your project and displays a list of surviving mutants'
|
41
41
|
command :score do |c|
|
42
|
-
|
43
|
-
|
42
|
+
cached_desc = 'Use the mutants in "./.mutants" rather than generating mutants before scoring'
|
43
|
+
c.switch [:c, :cached], desc: cached_desc, negatable: false
|
44
|
+
c.action do |_, options, _|
|
45
|
+
cached = options.fetch(:cached, false)
|
46
|
+
Mutiny::Mode::Score.new(@configuration, cached: cached).run
|
44
47
|
end
|
45
48
|
end
|
46
49
|
|
@@ -11,26 +11,20 @@ module Mutiny
|
|
11
11
|
end
|
12
12
|
|
13
13
|
def call
|
14
|
-
|
15
|
-
results
|
16
|
-
end
|
17
|
-
|
18
|
-
private
|
14
|
+
results = Results.new
|
19
15
|
|
20
|
-
def analyse_all
|
21
16
|
mutant_set.mutants.each do |mutant|
|
22
|
-
mutant
|
23
|
-
test_run = integration.test(mutant.subject) unless mutant.stillborn?
|
24
|
-
results.add(mutant, test_run)
|
17
|
+
results.add(mutant, analyse(mutant))
|
25
18
|
end
|
26
|
-
end
|
27
19
|
|
28
|
-
|
29
|
-
@results ||= Results.new
|
20
|
+
results
|
30
21
|
end
|
31
22
|
|
32
|
-
|
33
|
-
|
23
|
+
private
|
24
|
+
|
25
|
+
def analyse(mutant)
|
26
|
+
mutant.apply
|
27
|
+
mutant.stillborn? ? nil : integration.test(mutant)
|
34
28
|
end
|
35
29
|
end
|
36
30
|
end
|
data/lib/mutiny/configuration.rb
CHANGED
@@ -1,11 +1,13 @@
|
|
1
1
|
require_relative 'pattern'
|
2
2
|
require_relative 'reporter/stdout'
|
3
|
+
require_relative 'tests/selection/default'
|
3
4
|
require_relative 'integration/rspec'
|
4
5
|
require_relative 'mutants/ruby'
|
6
|
+
require_relative 'mutants/storage'
|
5
7
|
|
6
8
|
module Mutiny
|
7
9
|
class Configuration
|
8
|
-
attr_reader :loads, :requires, :patterns, :reporter, :
|
10
|
+
attr_reader :loads, :requires, :patterns, :reporter, :mutants, :tests
|
9
11
|
|
10
12
|
def initialize(loads: [], requires: [], patterns: [])
|
11
13
|
@loads = loads
|
@@ -14,8 +16,8 @@ module Mutiny
|
|
14
16
|
@patterns.map!(&Pattern.method(:new))
|
15
17
|
|
16
18
|
@reporter = Reporter::Stdout.new
|
17
|
-
@
|
18
|
-
@
|
19
|
+
@mutants = Mutants.new
|
20
|
+
@tests = Tests.new
|
19
21
|
end
|
20
22
|
|
21
23
|
def load_paths
|
@@ -25,5 +27,23 @@ module Mutiny
|
|
25
27
|
def can_load?(source_path)
|
26
28
|
load_paths.any? { |load_path| source_path.start_with?(load_path) }
|
27
29
|
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
|
28
48
|
end
|
29
49
|
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module Mutiny
|
2
|
+
class Integration
|
3
|
+
class RSpec < self
|
4
|
+
class Hook
|
5
|
+
attr_reader :hook
|
6
|
+
|
7
|
+
def initialize(hook)
|
8
|
+
@hook = hook
|
9
|
+
end
|
10
|
+
|
11
|
+
def install(configuration)
|
12
|
+
configuration.reporter.register_listener(self, :example_started)
|
13
|
+
configuration.reporter.register_listener(self, :example_failed)
|
14
|
+
configuration.reporter.register_listener(self, :example_passed)
|
15
|
+
end
|
16
|
+
|
17
|
+
def example_started(notification)
|
18
|
+
example = notification.example
|
19
|
+
hook.before(example) unless example.pending? || example.skipped?
|
20
|
+
end
|
21
|
+
|
22
|
+
def example_failed(notification)
|
23
|
+
hook.after(notification.example)
|
24
|
+
end
|
25
|
+
|
26
|
+
def example_passed(notification)
|
27
|
+
hook.after(notification.example)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -9,9 +9,10 @@ module Mutiny
|
|
9
9
|
extend Forwardable
|
10
10
|
def_delegators :@context, :world, :runner, :configuration, :output
|
11
11
|
|
12
|
-
def initialize(test_set, context = Context.new)
|
12
|
+
def initialize(test_set, context = Context.new, hooks = [])
|
13
13
|
@test_set = test_set
|
14
14
|
@context = context
|
15
|
+
@hooks = hooks
|
15
16
|
end
|
16
17
|
|
17
18
|
def call
|
@@ -28,9 +29,9 @@ module Mutiny
|
|
28
29
|
end
|
29
30
|
|
30
31
|
def prepare
|
32
|
+
install_hooks
|
31
33
|
filter_examples
|
32
|
-
|
33
|
-
configuration.reporter.register_listener(self, :example_failed)
|
34
|
+
listen_for_example_results
|
34
35
|
end
|
35
36
|
|
36
37
|
def run
|
@@ -51,12 +52,8 @@ module Mutiny
|
|
51
52
|
)
|
52
53
|
end
|
53
54
|
|
54
|
-
def
|
55
|
-
@
|
56
|
-
end
|
57
|
-
|
58
|
-
def example_failed(notification)
|
59
|
-
@failed_examples << notification.example
|
55
|
+
def install_hooks
|
56
|
+
@hooks.each { |hook| hook.install(configuration) }
|
60
57
|
end
|
61
58
|
|
62
59
|
def filter_examples
|
@@ -64,6 +61,19 @@ module Mutiny
|
|
64
61
|
example.keep_if(&@test_set.examples.method(:include?))
|
65
62
|
end
|
66
63
|
end
|
64
|
+
|
65
|
+
def listen_for_example_results
|
66
|
+
configuration.reporter.register_listener(self, :example_passed)
|
67
|
+
configuration.reporter.register_listener(self, :example_failed)
|
68
|
+
end
|
69
|
+
|
70
|
+
def example_passed(notification)
|
71
|
+
@passed_examples << notification.example
|
72
|
+
end
|
73
|
+
|
74
|
+
def example_failed(notification)
|
75
|
+
@failed_examples << notification.example
|
76
|
+
end
|
67
77
|
end
|
68
78
|
end
|
69
79
|
end
|
@@ -1,6 +1,7 @@
|
|
1
1
|
require_relative "rspec/context"
|
2
2
|
require_relative "rspec/parser"
|
3
3
|
require_relative "rspec/runner"
|
4
|
+
require_relative "rspec/hook"
|
4
5
|
|
5
6
|
module Mutiny
|
6
7
|
class Integration
|
@@ -11,8 +12,9 @@ module Mutiny
|
|
11
12
|
Parser.new(context(options)).call
|
12
13
|
end
|
13
14
|
|
14
|
-
def run(test_set,
|
15
|
-
|
15
|
+
def run(test_set, hooks: [], **options)
|
16
|
+
rspec_hooks = hooks.map { |hook| RSpec::Hook.new(hook) }
|
17
|
+
Runner.new(test_set, context(options), rspec_hooks).call
|
16
18
|
end
|
17
19
|
|
18
20
|
private
|
data/lib/mutiny/integration.rb
CHANGED
@@ -1,11 +1,18 @@
|
|
1
|
+
require_relative "tests/selection/default"
|
1
2
|
require_relative "isolation"
|
2
3
|
|
3
4
|
module Mutiny
|
4
5
|
class Integration
|
5
|
-
|
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)
|
6
13
|
Isolation.call do
|
7
|
-
|
8
|
-
run(
|
14
|
+
selected_tests = test_selection.for(mutant, from: tests)
|
15
|
+
run(selected_tests, fail_fast: true)
|
9
16
|
end
|
10
17
|
end
|
11
18
|
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.integration.tests.
|
54
|
+
@test_set ||= configuration.tests.integration.tests.for_subjects(environment.subjects)
|
55
55
|
end
|
56
56
|
|
57
57
|
def test_run
|
58
|
-
@test_run ||= configuration.integration.run(test_set)
|
58
|
+
@test_run ||= configuration.tests.integration.run(test_set)
|
59
59
|
end
|
60
60
|
end
|
61
61
|
end
|
data/lib/mutiny/mode/mutate.rb
CHANGED
@@ -17,12 +17,16 @@ module Mutiny
|
|
17
17
|
end
|
18
18
|
|
19
19
|
def store_mutants
|
20
|
-
mutant_set
|
20
|
+
mutant_storage.save(mutant_set)
|
21
21
|
report "Check the '.mutants' directory to browse the generated mutants."
|
22
22
|
end
|
23
23
|
|
24
24
|
def mutant_set
|
25
|
-
@mutant_set ||= configuration.mutator.mutants_for(environment.subjects)
|
25
|
+
@mutant_set ||= configuration.mutants.mutator.mutants_for(environment.subjects)
|
26
|
+
end
|
27
|
+
|
28
|
+
def mutant_storage
|
29
|
+
@store ||= configuration.mutants.storage
|
26
30
|
end
|
27
31
|
end
|
28
32
|
end
|
data/lib/mutiny/mode/score.rb
CHANGED
@@ -58,11 +58,22 @@ module Mutiny
|
|
58
58
|
end
|
59
59
|
|
60
60
|
def analyser
|
61
|
-
Analysis::Analyser.new(
|
61
|
+
Analysis::Analyser.new(
|
62
|
+
mutant_set: mutant_set,
|
63
|
+
integration: configuration.tests.integration
|
64
|
+
)
|
62
65
|
end
|
63
66
|
|
64
67
|
def mutant_set
|
65
|
-
@mutant_set ||=
|
68
|
+
@mutant_set ||= initialize_mutant_set
|
69
|
+
end
|
70
|
+
|
71
|
+
def initialize_mutant_set
|
72
|
+
if options[:cached]
|
73
|
+
configuration.mutants.storage.load
|
74
|
+
else
|
75
|
+
configuration.mutants.mutator.mutants_for(environment.subjects)
|
76
|
+
end
|
66
77
|
end
|
67
78
|
end
|
68
79
|
end
|
data/lib/mutiny/mode.rb
CHANGED
@@ -4,11 +4,12 @@ require_relative "mode/score"
|
|
4
4
|
|
5
5
|
module Mutiny
|
6
6
|
class Mode
|
7
|
-
attr_reader :configuration, :environment
|
7
|
+
attr_reader :configuration, :environment, :options
|
8
8
|
|
9
|
-
def initialize(configuration)
|
9
|
+
def initialize(configuration, **options)
|
10
10
|
@configuration = configuration
|
11
11
|
@environment = Subjects::Environment.new(configuration)
|
12
|
+
@options = options
|
12
13
|
end
|
13
14
|
|
14
15
|
private
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Mutiny
|
2
|
+
module Mutants
|
3
|
+
class Mutant
|
4
|
+
class Location
|
5
|
+
attr_reader :position, :content
|
6
|
+
|
7
|
+
def initialize(position:, content:)
|
8
|
+
@position = position.freeze
|
9
|
+
@content = content
|
10
|
+
end
|
11
|
+
|
12
|
+
def lines
|
13
|
+
Range.new(
|
14
|
+
line_number_of_offset(position.begin),
|
15
|
+
line_number_of_offset(position.end)
|
16
|
+
)
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def line_number_of_offset(offset)
|
22
|
+
content[0..offset].lines.size
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -1,16 +1,23 @@
|
|
1
|
-
|
1
|
+
require_relative "mutant/location"
|
2
2
|
|
3
3
|
module Mutiny
|
4
4
|
module Mutants
|
5
5
|
class Mutant
|
6
|
-
attr_reader :subject, :code, :mutation_name, :stillborn
|
6
|
+
attr_reader :subject, :code, :mutation_name, :stillborn, :location
|
7
|
+
attr_accessor :index
|
7
8
|
alias_method :stillborn?, :stillborn
|
8
9
|
|
9
|
-
def initialize(subject: nil, code:, mutation_name: nil)
|
10
|
+
def initialize(subject: nil, code:, mutation_name: nil, index: nil, position: nil)
|
10
11
|
@subject = subject
|
11
12
|
@code = code
|
12
13
|
@mutation_name = mutation_name
|
14
|
+
@index = index
|
13
15
|
@stillborn = false
|
16
|
+
@location = Location.new(position: position, content: code)
|
17
|
+
end
|
18
|
+
|
19
|
+
def identifier
|
20
|
+
subject.relative_path.sub(/\.rb$/, ".#{index}.rb")
|
14
21
|
end
|
15
22
|
|
16
23
|
def apply
|
@@ -20,15 +20,12 @@ module Mutiny
|
|
20
20
|
def ordered
|
21
21
|
group_by_subject.flat_map do |_, mutants|
|
22
22
|
mutants.map.with_index do |mutant, index|
|
23
|
-
|
23
|
+
mutant.index ||= index
|
24
|
+
mutant
|
24
25
|
end
|
25
26
|
end
|
26
27
|
end
|
27
28
|
|
28
|
-
def store(mutant_directory = ".mutants")
|
29
|
-
ordered.each { |m| m.store(mutant_directory) }
|
30
|
-
end
|
31
|
-
|
32
29
|
def eql?(other)
|
33
30
|
other.mutants == mutants
|
34
31
|
end
|
@@ -38,27 +35,6 @@ module Mutiny
|
|
38
35
|
def hash
|
39
36
|
mutants.hash
|
40
37
|
end
|
41
|
-
|
42
|
-
class OrderedMutant < SimpleDelegator
|
43
|
-
def initialize(mutant, number)
|
44
|
-
super(mutant)
|
45
|
-
@number = number
|
46
|
-
end
|
47
|
-
|
48
|
-
def identifier
|
49
|
-
subject.relative_path.sub(/\.rb$/, ".#{@number}.rb")
|
50
|
-
end
|
51
|
-
|
52
|
-
def store(directory)
|
53
|
-
path = File.join(directory, identifier)
|
54
|
-
FileUtils.mkdir_p(File.dirname(path))
|
55
|
-
File.open(path, 'w') { |f| f.write(serialised) }
|
56
|
-
end
|
57
|
-
|
58
|
-
def serialised
|
59
|
-
"# " + mutation_name + "\n" + code
|
60
|
-
end
|
61
|
-
end
|
62
38
|
end
|
63
39
|
end
|
64
40
|
end
|
@@ -14,23 +14,37 @@ module Mutiny
|
|
14
14
|
# Probably could improve (more) if metamorpher also supported composite transformers so that
|
15
15
|
# several mutation operators could be matched simulatenously during a single AST traversal
|
16
16
|
def mutate(subjects)
|
17
|
-
MutantSet.new
|
18
|
-
|
19
|
-
|
20
|
-
end
|
17
|
+
mutants = MutantSet.new
|
18
|
+
subjects.product(mutations).each do |subject, mutation|
|
19
|
+
mutants.concat(mutate_one(subject, mutation))
|
21
20
|
end
|
21
|
+
mutants
|
22
22
|
end
|
23
23
|
|
24
24
|
private
|
25
25
|
|
26
26
|
def mutate_one(subject, mutation)
|
27
|
-
safely_mutate_file(subject.path, mutation).map do |code|
|
28
|
-
Mutant.new(
|
27
|
+
safely_mutate_file(subject.path, mutation).map do |code, position|
|
28
|
+
Mutant.new(
|
29
|
+
subject: subject,
|
30
|
+
mutation_name: mutation.short_name,
|
31
|
+
code: code,
|
32
|
+
position: position
|
33
|
+
)
|
29
34
|
end
|
30
35
|
end
|
31
36
|
|
32
37
|
def safely_mutate_file(path, mutation)
|
33
|
-
|
38
|
+
positions = []
|
39
|
+
|
40
|
+
code = mutation.mutate_file(path) do |change|
|
41
|
+
starting_position = change.original_position.begin
|
42
|
+
ending_position = change.original_position.begin + change.transformed_code.size - 1
|
43
|
+
|
44
|
+
positions << (starting_position..ending_position)
|
45
|
+
end
|
46
|
+
|
47
|
+
code.zip(positions)
|
34
48
|
rescue
|
35
49
|
msg = "Error encountered whilst mutating file at '#{path}' with #{mutation.name}"
|
36
50
|
raise Mutation::Error, msg
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require_relative "mutant_file"
|
2
|
+
|
3
|
+
module Mutiny
|
4
|
+
module Mutants
|
5
|
+
class Storage
|
6
|
+
class FileStore
|
7
|
+
attr_reader :mutant_directory, :strategy
|
8
|
+
|
9
|
+
def initialize(mutant_directory: ".mutants")
|
10
|
+
@mutant_directory = mutant_directory
|
11
|
+
@strategy = MutantFile.new(mutant_directory)
|
12
|
+
end
|
13
|
+
|
14
|
+
def save_all(mutants)
|
15
|
+
mutants.ordered.each do |mutant|
|
16
|
+
strategy.store(mutant)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def load_all
|
21
|
+
absolute_paths = Dir.glob(File.join(mutant_directory, "**", "*.rb"))
|
22
|
+
absolute_paths.map { |path| strategy.load(path) }
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require "fileutils"
|
2
|
+
require_relative "path"
|
3
|
+
require_relative "mutant_file_name"
|
4
|
+
require_relative "mutant_file_contents"
|
5
|
+
|
6
|
+
module Mutiny
|
7
|
+
module Mutants
|
8
|
+
class Storage
|
9
|
+
class MutantFile
|
10
|
+
attr_reader :mutant_directory
|
11
|
+
|
12
|
+
def initialize(mutant_directory)
|
13
|
+
@mutant_directory = mutant_directory
|
14
|
+
end
|
15
|
+
|
16
|
+
def load(absolute_path)
|
17
|
+
path = Path.from_absolute(path: absolute_path, root: mutant_directory)
|
18
|
+
deserialise
|
19
|
+
.merge(deserialised_contents(path)) { |_, left, right| left.merge(right) }
|
20
|
+
.merge(deserialised_filename(path)) { |_, left, right| left.merge(right) }
|
21
|
+
end
|
22
|
+
|
23
|
+
def store(mutant)
|
24
|
+
path = Path.from_relative(root: mutant_directory, path: filename.serialise(mutant))
|
25
|
+
FileUtils.mkdir_p(File.dirname(path.absolute))
|
26
|
+
File.open(path.absolute, 'w') { |f| f.write(contents.serialise(mutant)) }
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def deserialise
|
32
|
+
{
|
33
|
+
subject: { root: mutant_directory }
|
34
|
+
}
|
35
|
+
end
|
36
|
+
|
37
|
+
def deserialised_filename(path)
|
38
|
+
filename.deserialise(path.relative)
|
39
|
+
end
|
40
|
+
|
41
|
+
def deserialised_contents(path)
|
42
|
+
contents.deserialise(File.read(path.absolute))
|
43
|
+
end
|
44
|
+
|
45
|
+
def filename
|
46
|
+
@filename ||= MutantFileName.new
|
47
|
+
end
|
48
|
+
|
49
|
+
def contents
|
50
|
+
@contents ||= MutantFileContents.new
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Mutiny
|
2
|
+
module Mutants
|
3
|
+
class Storage
|
4
|
+
class MutantFileContents
|
5
|
+
def serialise(mutant)
|
6
|
+
"# " + mutant.subject.name + "\n" \
|
7
|
+
"# " + mutant.mutation_name + "\n" \
|
8
|
+
"# " + mutant.location.position.to_s + "\n" +
|
9
|
+
mutant.code
|
10
|
+
end
|
11
|
+
|
12
|
+
def deserialise(contents)
|
13
|
+
{
|
14
|
+
subject: { name: extract_contents_of_comment(contents.lines[0]) },
|
15
|
+
mutation_name: extract_contents_of_comment(contents.lines[1]),
|
16
|
+
position: convert_to_range(extract_contents_of_comment(contents.lines[2])),
|
17
|
+
code: contents.lines.drop(3).join
|
18
|
+
}
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def extract_contents_of_comment(line)
|
24
|
+
line[2..-1].strip
|
25
|
+
end
|
26
|
+
|
27
|
+
def convert_to_range(string)
|
28
|
+
Range.new(*string.split("..").map(&:to_i))
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module Mutiny
|
2
|
+
module Mutants
|
3
|
+
class Storage
|
4
|
+
class MutantFileName
|
5
|
+
def serialise(mutant)
|
6
|
+
path_with_index(mutant.subject.relative_path, mutant.index)
|
7
|
+
end
|
8
|
+
|
9
|
+
def deserialise(path)
|
10
|
+
{
|
11
|
+
subject: { path: path_without_index(path) },
|
12
|
+
index: index_of(path)
|
13
|
+
}
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def path_with_index(path, index)
|
19
|
+
path.sub(/\.rb$/, ".#{index}.rb")
|
20
|
+
end
|
21
|
+
|
22
|
+
def path_without_index(path)
|
23
|
+
path.sub(/\.\d+\.rb$/, ".rb")
|
24
|
+
end
|
25
|
+
|
26
|
+
def index_of(path)
|
27
|
+
path.match(/.*\.(\d+)\.rb/)[1].to_i
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require_relative "mutant_file"
|
2
|
+
|
3
|
+
module Mutiny
|
4
|
+
module Mutants
|
5
|
+
class Storage
|
6
|
+
class Path
|
7
|
+
def self.from_absolute(path:, root:)
|
8
|
+
relative_path = Pathname.new(path).relative_path_from(Pathname.new(root)).to_s
|
9
|
+
new(relative_path, root)
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.from_relative(path:, root:)
|
13
|
+
new(path, root)
|
14
|
+
end
|
15
|
+
|
16
|
+
attr_reader :relative, :root
|
17
|
+
|
18
|
+
def absolute
|
19
|
+
File.join(root, relative)
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def initialize(relative, root)
|
25
|
+
@relative = relative
|
26
|
+
@root = root
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require_relative "storage/file_store"
|
2
|
+
|
3
|
+
module Mutiny
|
4
|
+
module Mutants
|
5
|
+
class Storage
|
6
|
+
attr_accessor :store
|
7
|
+
|
8
|
+
def initialize(mutant_directory: ".mutants", store: nil)
|
9
|
+
@store = store || FileStore.new(mutant_directory: mutant_directory)
|
10
|
+
end
|
11
|
+
|
12
|
+
def save(mutants)
|
13
|
+
store.save_all(mutants)
|
14
|
+
end
|
15
|
+
|
16
|
+
def load
|
17
|
+
mutants = store.load_all.map do |mutant_specification|
|
18
|
+
subject = Subjects::Subject.new(**mutant_specification[:subject])
|
19
|
+
mutant_specification[:subject] = subject
|
20
|
+
Mutant.new(**mutant_specification)
|
21
|
+
end
|
22
|
+
|
23
|
+
MutantSet.new(*mutants)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
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_subject(subject)
|
22
|
+
subset { |test| subject.name == test.expression }
|
23
23
|
end
|
24
24
|
|
25
|
-
def
|
26
|
-
subset { |test|
|
25
|
+
def for_subjects(subjects)
|
26
|
+
subset { |test| subjects.names.include?(test.expression) }
|
27
27
|
end
|
28
28
|
|
29
29
|
def subset(&block)
|
data/lib/mutiny/version.rb
CHANGED
@@ -53,4 +53,25 @@ describe "Using Mutiny to generate mutants" do
|
|
53
53
|
check_file_content(".mutants/calculator/max.5.rb", /# RelationalOperatorReplacement/)
|
54
54
|
check_file_content(".mutants/calculator/max.6.rb", /# RelationalOperatorReplacement/)
|
55
55
|
end
|
56
|
+
|
57
|
+
it "should write position to each mutant" do
|
58
|
+
cd "calculator"
|
59
|
+
run "bundle exec mutiny mutate"
|
60
|
+
|
61
|
+
check_file_content(".mutants/calculator/min.0.rb", /# 93..96/)
|
62
|
+
check_file_content(".mutants/calculator/min.1.rb", /# 93..97/)
|
63
|
+
check_file_content(".mutants/calculator/min.2.rb", /# 93..105/)
|
64
|
+
check_file_content(".mutants/calculator/min.3.rb", /# 93..105/)
|
65
|
+
check_file_content(".mutants/calculator/min.4.rb", /# 93..105/)
|
66
|
+
check_file_content(".mutants/calculator/min.5.rb", /# 93..105/)
|
67
|
+
check_file_content(".mutants/calculator/min.6.rb", /# 93..104/)
|
68
|
+
|
69
|
+
check_file_content(".mutants/calculator/max.0.rb", /# 93..96/)
|
70
|
+
check_file_content(".mutants/calculator/max.1.rb", /# 93..97/)
|
71
|
+
check_file_content(".mutants/calculator/max.2.rb", /# 93..104/)
|
72
|
+
check_file_content(".mutants/calculator/max.3.rb", /# 93..105/)
|
73
|
+
check_file_content(".mutants/calculator/max.4.rb", /# 93..105/)
|
74
|
+
check_file_content(".mutants/calculator/max.5.rb", /# 93..105/)
|
75
|
+
check_file_content(".mutants/calculator/max.6.rb", /# 93..105/)
|
76
|
+
end
|
56
77
|
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
describe "Using Mutiny to score existing mutants" do
|
2
|
+
before(:each) do
|
3
|
+
cd "calculator"
|
4
|
+
run "bundle exec mutiny mutate"
|
5
|
+
|
6
|
+
run "rm -rf .mutants/calculator/max.0.rb"
|
7
|
+
run "rm -rf .mutants/calculator/max.1.rb"
|
8
|
+
run "rm -rf .mutants/calculator/max.2.rb"
|
9
|
+
run "rm -rf .mutants/calculator/max.3.rb"
|
10
|
+
run "rm -rf .mutants/calculator/max.4.rb"
|
11
|
+
run "rm -rf .mutants/calculator/max.5.rb"
|
12
|
+
run "rm -rf .mutants/calculator/max.6.rb"
|
13
|
+
|
14
|
+
run "rm -rf .mutants/calculator/min.0.rb"
|
15
|
+
run "rm -rf .mutants/calculator/min.1.rb"
|
16
|
+
|
17
|
+
run "bundle exec mutiny score --cached"
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should report a mutation score" do
|
21
|
+
expected_output = "Scoring...\n" \
|
22
|
+
"5 mutants, 4 killed\n"
|
23
|
+
|
24
|
+
expect(all_output).to include(expected_output)
|
25
|
+
end
|
26
|
+
|
27
|
+
it "should report status of mutants" do
|
28
|
+
expect(all_output).to include("calculator/min.2.rb | survived")
|
29
|
+
expect(all_output).to include("calculator/min.3.rb | killed")
|
30
|
+
expect(all_output).to include("calculator/min.4.rb | killed")
|
31
|
+
expect(all_output).to include("calculator/min.5.rb | killed")
|
32
|
+
expect(all_output).to include("calculator/min.6.rb | killed")
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require "mutiny/integration/hook"
|
2
|
+
|
3
|
+
module Mutiny
|
4
|
+
class Integration
|
5
|
+
describe RSpec do
|
6
|
+
let(:test_set) { subject.tests }
|
7
|
+
|
8
|
+
it "should call hooks before each spec" do
|
9
|
+
in_example_project("calculator") do
|
10
|
+
hook = TestHook.new
|
11
|
+
subject.run(test_set, hooks: [hook])
|
12
|
+
|
13
|
+
expect(hook.examples_started.size).to eq(test_set.size)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
it "should call hooks after each spec" do
|
18
|
+
in_example_project("calculator") do
|
19
|
+
hook = TestHook.new
|
20
|
+
subject.run(test_set, hooks: [hook])
|
21
|
+
|
22
|
+
expect(hook.examples_finished.size).to eq(test_set.size)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
class TestHook < Hook
|
27
|
+
def before(example)
|
28
|
+
examples_started << example
|
29
|
+
end
|
30
|
+
|
31
|
+
def after(example)
|
32
|
+
examples_finished << example
|
33
|
+
end
|
34
|
+
|
35
|
+
def examples_started
|
36
|
+
@examples_started ||= []
|
37
|
+
end
|
38
|
+
|
39
|
+
def examples_finished
|
40
|
+
@examples_finished ||= []
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Mutiny
|
2
|
+
module Mutants
|
3
|
+
class Mutant
|
4
|
+
describe Location do
|
5
|
+
context "calculates lines" do
|
6
|
+
it "correctly for a multi-line location" do
|
7
|
+
# 0 1 2
|
8
|
+
# 01234567 89012345678901 234
|
9
|
+
location = Location.new(position: 3..21, content: "if BOMB\n raise 'boom'\nend")
|
10
|
+
|
11
|
+
expect(location.lines).to eq(1..2)
|
12
|
+
end
|
13
|
+
|
14
|
+
it "correctly for a single-line location" do
|
15
|
+
location = Location.new(position: 2..4, content: "a <= b\nb <= c")
|
16
|
+
|
17
|
+
expect(location.lines).to eq(1..1)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module Mutiny
|
2
|
+
module Mutants
|
3
|
+
class Storage
|
4
|
+
describe MutantFileContents do
|
5
|
+
it "serialises" do
|
6
|
+
expect(subject.serialise(mutant)).to eq(serialised_mutant)
|
7
|
+
end
|
8
|
+
|
9
|
+
it "deserialises" do
|
10
|
+
expect(subject.deserialise(serialised_mutant)).to eq(deserialised_mutant)
|
11
|
+
end
|
12
|
+
|
13
|
+
def mutant
|
14
|
+
Mutant.new(
|
15
|
+
subject: subject_of_mutation,
|
16
|
+
code: "2 - 2",
|
17
|
+
index: 0,
|
18
|
+
mutation_name: "BAOR",
|
19
|
+
position: 2..3
|
20
|
+
)
|
21
|
+
end
|
22
|
+
|
23
|
+
def subject_of_mutation
|
24
|
+
Subjects::Subject.new(
|
25
|
+
name: "Two",
|
26
|
+
path: "~/Code/sums/two.rb",
|
27
|
+
root: "~/Code/sums"
|
28
|
+
)
|
29
|
+
end
|
30
|
+
|
31
|
+
def serialised_mutant
|
32
|
+
"# Two\n" \
|
33
|
+
"# BAOR\n" \
|
34
|
+
"# 2..3\n" \
|
35
|
+
"2 - 2"
|
36
|
+
end
|
37
|
+
|
38
|
+
def deserialised_mutant
|
39
|
+
{
|
40
|
+
subject: { name: "Two" },
|
41
|
+
mutation_name: "BAOR",
|
42
|
+
code: "2 - 2",
|
43
|
+
position: 2..3
|
44
|
+
}
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Mutiny
|
2
|
+
module Mutants
|
3
|
+
class Storage
|
4
|
+
describe MutantFileName do
|
5
|
+
it "serialises" do
|
6
|
+
expect(subject.serialise(mutant)).to eq(serialised_mutant)
|
7
|
+
end
|
8
|
+
|
9
|
+
it "deserialises" do
|
10
|
+
expect(subject.deserialise(serialised_mutant)).to eq(deserialised_mutant)
|
11
|
+
end
|
12
|
+
|
13
|
+
def mutant
|
14
|
+
Mutant.new(subject: subject_of_mutation, code: "2 - 2", index: 10, mutation_name: "BAOR")
|
15
|
+
end
|
16
|
+
|
17
|
+
def subject_of_mutation
|
18
|
+
Subjects::Subject.new(name: "Two", path: "~/Code/sums/two.rb", root: "~/Code/sums")
|
19
|
+
end
|
20
|
+
|
21
|
+
def serialised_mutant
|
22
|
+
"two.10.rb"
|
23
|
+
end
|
24
|
+
|
25
|
+
def deserialised_mutant
|
26
|
+
{
|
27
|
+
subject: { path: "two.rb" },
|
28
|
+
index: 10
|
29
|
+
}
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module Mutiny
|
2
|
+
module Mutants
|
3
|
+
describe Storage do
|
4
|
+
it "transforms hashes to mutants and subjects" do
|
5
|
+
store = instance_double(Storage::FileStore)
|
6
|
+
allow(store).to receive(:load_all).and_return(loaded_data)
|
7
|
+
|
8
|
+
storage = Storage.new(store: store)
|
9
|
+
expect(storage.load).to eq(expected_mutant_set)
|
10
|
+
end
|
11
|
+
|
12
|
+
# rubocop:disable MethodLength
|
13
|
+
def loaded_data
|
14
|
+
[
|
15
|
+
{
|
16
|
+
subject: { name: "Two", path: "two.rb", root: "~/Code/sums" },
|
17
|
+
code: "2 - 2",
|
18
|
+
index: 0
|
19
|
+
},
|
20
|
+
{
|
21
|
+
subject: { name: "Two", path: "two.rb", root: "~/Code/sums" },
|
22
|
+
code: "2 * 2",
|
23
|
+
index: 1
|
24
|
+
},
|
25
|
+
{
|
26
|
+
subject: { name: "Four", path: "four.rb", root: "~/Code/sums" },
|
27
|
+
code: "4 - 4",
|
28
|
+
index: 0
|
29
|
+
}
|
30
|
+
]
|
31
|
+
end
|
32
|
+
# 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
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -33,42 +33,84 @@ module Mutiny
|
|
33
33
|
end
|
34
34
|
end
|
35
35
|
|
36
|
-
context "for" do
|
36
|
+
context "for all" 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)
|
40
41
|
|
41
|
-
expect(
|
42
|
+
expect(selected).to eq(test_set.subset { |t| t.expression == "Min" })
|
42
43
|
end
|
43
44
|
|
44
45
|
it "should return multiple tests for a single subject" do
|
45
46
|
subjects = subject_set_for("Min")
|
46
47
|
test_set = test_set_for("Min", "Max", "Min", "Max", "Min")
|
48
|
+
selected = test_set.for_subjects(subjects)
|
47
49
|
|
48
|
-
expect(
|
50
|
+
expect(selected).to eq(test_set.subset { |t| t.expression == "Min" })
|
49
51
|
end
|
50
52
|
|
51
53
|
it "should return no tests when there are no tests" do
|
52
54
|
subjects = subject_set_for("Max", "Min")
|
53
55
|
test_set = TestSet.empty
|
56
|
+
selected = test_set.for_subjects(subjects)
|
54
57
|
|
55
|
-
expect(
|
58
|
+
expect(selected).to eq(TestSet.empty)
|
56
59
|
end
|
57
60
|
|
58
61
|
it "should return no tests when there are no relevant subjects" do
|
59
62
|
subjects = subject_set_for("Max", "Min")
|
60
63
|
test_set = test_set_for("Subtract", "Add")
|
64
|
+
selected = test_set.for_subjects(subjects)
|
61
65
|
|
62
|
-
expect(
|
66
|
+
expect(selected).to eq(TestSet.empty)
|
63
67
|
end
|
64
68
|
|
65
69
|
def subject_set_for(*names)
|
66
70
|
Subjects::SubjectSet.new(names.map { |n| Subjects::Subject.new(name: n) })
|
67
71
|
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
|
68
90
|
|
69
|
-
|
70
|
-
|
91
|
+
it "should return no tests when there are no tests" do
|
92
|
+
subject = subject_for("Min")
|
93
|
+
test_set = TestSet.empty
|
94
|
+
selected = test_set.for_subject(subject)
|
95
|
+
|
96
|
+
expect(selected).to eq(TestSet.empty)
|
71
97
|
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) })
|
72
114
|
end
|
73
115
|
end
|
74
116
|
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.4
|
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-
|
11
|
+
date: 2016-02-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: parser
|
@@ -194,8 +194,10 @@ files:
|
|
194
194
|
- lib/mutiny/analysis/results.rb
|
195
195
|
- lib/mutiny/configuration.rb
|
196
196
|
- lib/mutiny/integration.rb
|
197
|
+
- lib/mutiny/integration/hook.rb
|
197
198
|
- lib/mutiny/integration/rspec.rb
|
198
199
|
- lib/mutiny/integration/rspec/context.rb
|
200
|
+
- lib/mutiny/integration/rspec/hook.rb
|
199
201
|
- lib/mutiny/integration/rspec/parser.rb
|
200
202
|
- lib/mutiny/integration/rspec/runner.rb
|
201
203
|
- lib/mutiny/integration/rspec/test.rb
|
@@ -208,6 +210,7 @@ files:
|
|
208
210
|
- lib/mutiny/mode/mutate.rb
|
209
211
|
- lib/mutiny/mode/score.rb
|
210
212
|
- lib/mutiny/mutants/mutant.rb
|
213
|
+
- lib/mutiny/mutants/mutant/location.rb
|
211
214
|
- lib/mutiny/mutants/mutant_set.rb
|
212
215
|
- lib/mutiny/mutants/mutation.rb
|
213
216
|
- lib/mutiny/mutants/mutation/error.rb
|
@@ -227,6 +230,12 @@ files:
|
|
227
230
|
- lib/mutiny/mutants/mutation/method/unary_arithmetic_operator_insertion.rb
|
228
231
|
- lib/mutiny/mutants/mutation_set.rb
|
229
232
|
- lib/mutiny/mutants/ruby.rb
|
233
|
+
- lib/mutiny/mutants/storage.rb
|
234
|
+
- lib/mutiny/mutants/storage/file_store.rb
|
235
|
+
- lib/mutiny/mutants/storage/mutant_file.rb
|
236
|
+
- lib/mutiny/mutants/storage/mutant_file_contents.rb
|
237
|
+
- lib/mutiny/mutants/storage/mutant_file_name.rb
|
238
|
+
- lib/mutiny/mutants/storage/path.rb
|
230
239
|
- lib/mutiny/output/table.rb
|
231
240
|
- lib/mutiny/pattern.rb
|
232
241
|
- lib/mutiny/reporter/stdout.rb
|
@@ -236,6 +245,7 @@ files:
|
|
236
245
|
- lib/mutiny/subjects/subject.rb
|
237
246
|
- lib/mutiny/subjects/subject_set.rb
|
238
247
|
- lib/mutiny/tests.rb
|
248
|
+
- lib/mutiny/tests/selection/default.rb
|
239
249
|
- lib/mutiny/tests/test.rb
|
240
250
|
- lib/mutiny/tests/test_run.rb
|
241
251
|
- lib/mutiny/tests/test_set.rb
|
@@ -243,6 +253,7 @@ files:
|
|
243
253
|
- mutiny.gemspec
|
244
254
|
- spec/integration/check_spec.rb
|
245
255
|
- spec/integration/mutate_spec.rb
|
256
|
+
- spec/integration/score_cached_spec.rb
|
246
257
|
- spec/integration/score_spec.rb
|
247
258
|
- spec/spec_helper.rb
|
248
259
|
- spec/support/aruba.rb
|
@@ -250,7 +261,9 @@ files:
|
|
250
261
|
- spec/support/shared_examples/shared_examples_for_an_operator_replacement_mutation.rb
|
251
262
|
- spec/unit/integration/rspec/parser_spec.rb
|
252
263
|
- spec/unit/integration/rspec/runner_spec.rb
|
264
|
+
- spec/unit/integration/rspec_spec.rb
|
253
265
|
- spec/unit/isolation_spec.rb
|
266
|
+
- spec/unit/mutants/mutant/location_spec.rb
|
254
267
|
- spec/unit/mutants/mutant_set_spec.rb
|
255
268
|
- spec/unit/mutants/mutant_spec.rb
|
256
269
|
- spec/unit/mutants/mutation_set_spec.rb
|
@@ -266,11 +279,14 @@ files:
|
|
266
279
|
- spec/unit/mutants/mutations/method/shortcut_assignment_operator_replacement_spec.rb
|
267
280
|
- spec/unit/mutants/mutations/method/unary_arithmetic_operator_deletion_spec.rb
|
268
281
|
- spec/unit/mutants/mutations/method/unary_arithmetic_operator_insertion_spec.rb
|
282
|
+
- spec/unit/mutants/storage/mutant_file_contents_spec.rb
|
283
|
+
- spec/unit/mutants/storage/mutant_file_name_spec.rb
|
284
|
+
- spec/unit/mutants/storage_spec.rb
|
269
285
|
- spec/unit/pattern_spec.rb
|
270
286
|
- spec/unit/subjects/environment/type_spec.rb
|
271
287
|
- spec/unit/subjects/environment_spec.rb
|
272
288
|
- spec/unit/subjects/subject_spec.rb
|
273
|
-
- spec/unit/
|
289
|
+
- spec/unit/tests/test_set_spec.rb
|
274
290
|
homepage: https://github.com/mutiny/mutiny
|
275
291
|
licenses:
|
276
292
|
- MIT
|
@@ -298,6 +314,7 @@ summary: A tiny mutation testing framework for Ruby
|
|
298
314
|
test_files:
|
299
315
|
- spec/integration/check_spec.rb
|
300
316
|
- spec/integration/mutate_spec.rb
|
317
|
+
- spec/integration/score_cached_spec.rb
|
301
318
|
- spec/integration/score_spec.rb
|
302
319
|
- spec/spec_helper.rb
|
303
320
|
- spec/support/aruba.rb
|
@@ -305,7 +322,9 @@ test_files:
|
|
305
322
|
- spec/support/shared_examples/shared_examples_for_an_operator_replacement_mutation.rb
|
306
323
|
- spec/unit/integration/rspec/parser_spec.rb
|
307
324
|
- spec/unit/integration/rspec/runner_spec.rb
|
325
|
+
- spec/unit/integration/rspec_spec.rb
|
308
326
|
- spec/unit/isolation_spec.rb
|
327
|
+
- spec/unit/mutants/mutant/location_spec.rb
|
309
328
|
- spec/unit/mutants/mutant_set_spec.rb
|
310
329
|
- spec/unit/mutants/mutant_spec.rb
|
311
330
|
- spec/unit/mutants/mutation_set_spec.rb
|
@@ -321,8 +340,11 @@ test_files:
|
|
321
340
|
- spec/unit/mutants/mutations/method/shortcut_assignment_operator_replacement_spec.rb
|
322
341
|
- spec/unit/mutants/mutations/method/unary_arithmetic_operator_deletion_spec.rb
|
323
342
|
- spec/unit/mutants/mutations/method/unary_arithmetic_operator_insertion_spec.rb
|
343
|
+
- spec/unit/mutants/storage/mutant_file_contents_spec.rb
|
344
|
+
- spec/unit/mutants/storage/mutant_file_name_spec.rb
|
345
|
+
- spec/unit/mutants/storage_spec.rb
|
324
346
|
- spec/unit/pattern_spec.rb
|
325
347
|
- spec/unit/subjects/environment/type_spec.rb
|
326
348
|
- spec/unit/subjects/environment_spec.rb
|
327
349
|
- spec/unit/subjects/subject_spec.rb
|
328
|
-
- spec/unit/
|
350
|
+
- spec/unit/tests/test_set_spec.rb
|