mutiny 0.2.3 → 0.2.4
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/.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
|