mutiny 0.1.0
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 +7 -0
- data/.gitignore +6 -0
- data/.rspec +3 -0
- data/.rubocop.yml +18 -0
- data/.ruby-version +1 -0
- data/.travis.yml +6 -0
- data/Gemfile +5 -0
- data/Gemfile.lock +113 -0
- data/LICENSE.txt +22 -0
- data/README.md +16 -0
- data/RELEASES.md +8 -0
- data/Rakefile +23 -0
- data/TODO.md +55 -0
- data/bin/mutiny +35 -0
- data/examples/buggy_calculator/.rspec +2 -0
- data/examples/buggy_calculator/lib/calculator/max.rb +9 -0
- data/examples/buggy_calculator/lib/calculator.rb +4 -0
- data/examples/buggy_calculator/spec/calculator/max_spec.rb +17 -0
- data/examples/buggy_calculator/spec/spec_helper.rb +89 -0
- data/examples/calculator/.rspec +2 -0
- data/examples/calculator/lib/calculator/max.rb +9 -0
- data/examples/calculator/lib/calculator/min.rb +9 -0
- data/examples/calculator/lib/calculator.rb +5 -0
- data/examples/calculator/spec/calculator/max_spec.rb +17 -0
- data/examples/calculator/spec/calculator/min_spec.rb +17 -0
- data/examples/calculator/spec/spec_helper.rb +89 -0
- data/examples/untested_calculator/.rspec +2 -0
- data/examples/untested_calculator/lib/calculator/max.rb +9 -0
- data/examples/untested_calculator/lib/calculator.rb +4 -0
- data/examples/untested_calculator/spec/spec_helper.rb +89 -0
- data/lib/mutiny/configuration.rb +25 -0
- data/lib/mutiny/integration/rspec/context.rb +26 -0
- data/lib/mutiny/integration/rspec/parser.rb +36 -0
- data/lib/mutiny/integration/rspec/runner.rb +58 -0
- data/lib/mutiny/integration/rspec/test.rb +16 -0
- data/lib/mutiny/integration/rspec/test_set.rb +17 -0
- data/lib/mutiny/integration/rspec.rb +25 -0
- data/lib/mutiny/mode/check.rb +62 -0
- data/lib/mutiny/mode/mutate.rb +29 -0
- data/lib/mutiny/mode.rb +19 -0
- data/lib/mutiny/mutants/mutant.rb +32 -0
- data/lib/mutiny/mutants/mutant_set.rb +42 -0
- data/lib/mutiny/mutants/mutation/method/binary_arithmetic_operator_replacement.rb +15 -0
- data/lib/mutiny/mutants/mutation/method/conditional_operator_deletion.rb +19 -0
- data/lib/mutiny/mutants/mutation/method/conditional_operator_insertion.rb +25 -0
- data/lib/mutiny/mutants/mutation/method/conditional_operator_replacement.rb +19 -0
- data/lib/mutiny/mutants/mutation/method/helpers/infix_operator_replacement.rb +17 -0
- data/lib/mutiny/mutants/mutation/method/helpers/operator_replacement.rb +81 -0
- data/lib/mutiny/mutants/mutation/method/logical_operator_deletion.rb +19 -0
- data/lib/mutiny/mutants/mutation/method/logical_operator_insertion.rb +24 -0
- data/lib/mutiny/mutants/mutation/method/logical_operator_replacement.rb +15 -0
- data/lib/mutiny/mutants/mutation/method/relational_expression_replacement.rb +19 -0
- data/lib/mutiny/mutants/mutation/method/relational_operator_replacement.rb +15 -0
- data/lib/mutiny/mutants/mutation/method/shortcut_assignment_operator_replacement.rb +23 -0
- data/lib/mutiny/mutants/mutation/method/unary_arithmetic_operator_deletion.rb +29 -0
- data/lib/mutiny/mutants/mutation/method/unary_arithmetic_operator_insertion.rb +29 -0
- data/lib/mutiny/mutants/mutation.rb +10 -0
- data/lib/mutiny/mutants/mutation_set.rb +24 -0
- data/lib/mutiny/mutants/ruby.rb +42 -0
- data/lib/mutiny/pattern.rb +17 -0
- data/lib/mutiny/reporter/stdout.rb +9 -0
- data/lib/mutiny/subjects/environment/type.rb +54 -0
- data/lib/mutiny/subjects/environment.rb +25 -0
- data/lib/mutiny/subjects/subject.rb +32 -0
- data/lib/mutiny/subjects/subject_set.rb +17 -0
- data/lib/mutiny/subjects.rb +3 -0
- data/lib/mutiny/tests/test.rb +12 -0
- data/lib/mutiny/tests/test_run.rb +18 -0
- data/lib/mutiny/tests/test_set.rb +44 -0
- data/lib/mutiny/tests.rb +3 -0
- data/lib/mutiny/version.rb +3 -0
- data/lib/mutiny.rb +4 -0
- data/mutiny.gemspec +32 -0
- data/spec/integration/check_spec.rb +39 -0
- data/spec/integration/mutate_spec.rb +35 -0
- data/spec/spec_helper.rb +38 -0
- data/spec/support/aruba.rb +12 -0
- data/spec/support/in_example_project.rb +17 -0
- data/spec/support/shared_examples/shared_examples_for_an_operator_replacement_mutation.rb +26 -0
- data/spec/unit/integration/rspec/parser_spec.rb +23 -0
- data/spec/unit/integration/rspec/runner_spec.rb +47 -0
- data/spec/unit/mutants/mutant_set_spec.rb +57 -0
- data/spec/unit/mutants/mutations/method/binary_operator_replacement_spec.rb +11 -0
- data/spec/unit/mutants/mutations/method/conditional_operator_deletion_spec.rb +17 -0
- data/spec/unit/mutants/mutations/method/conditional_operator_insertion_spec.rb +17 -0
- data/spec/unit/mutants/mutations/method/conditional_operator_replacement_spec.rb +16 -0
- data/spec/unit/mutants/mutations/method/logical_operator_deletion_spec.rb +17 -0
- data/spec/unit/mutants/mutations/method/logical_operator_insertion_spec.rb +17 -0
- data/spec/unit/mutants/mutations/method/logical_operator_replacement_spec.rb +11 -0
- data/spec/unit/mutants/mutations/method/relational_expression_replacement_spec.rb +18 -0
- data/spec/unit/mutants/mutations/method/relational_operator_replacement_spec.rb +11 -0
- data/spec/unit/mutants/mutations/method/shortcut_assignment_operator_replacement_spec.rb +12 -0
- data/spec/unit/mutants/mutations/method/unary_arithmetic_operator_deletion_spec.rb +21 -0
- data/spec/unit/mutants/mutations/method/unary_arithmetic_operator_insertion_spec.rb +21 -0
- data/spec/unit/pattern_spec.rb +28 -0
- data/spec/unit/subjects/environment/type_spec.rb +54 -0
- data/spec/unit/subjects/environment_spec.rb +71 -0
- data/spec/unit/subjects/subject_spec.rb +25 -0
- data/spec/unit/subjects/test_set_spec.rb +75 -0
- metadata +310 -0
@@ -0,0 +1,24 @@
|
|
1
|
+
require "mutiny/mutants/mutation"
|
2
|
+
|
3
|
+
module Mutiny
|
4
|
+
module Mutants
|
5
|
+
class Mutation
|
6
|
+
module Method
|
7
|
+
class LogicalOperatorInsertion < Mutation
|
8
|
+
def pattern
|
9
|
+
builder.either!(
|
10
|
+
builder.literal!(:int, builder.VAL),
|
11
|
+
builder.literal!(:send, nil, builder.VAL)
|
12
|
+
)
|
13
|
+
end
|
14
|
+
|
15
|
+
def replacement
|
16
|
+
builder.derivation! :& do |root|
|
17
|
+
builder.literal!(:send, root, :~)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require_relative "helpers/infix_operator_replacement"
|
2
|
+
|
3
|
+
module Mutiny
|
4
|
+
module Mutants
|
5
|
+
class Mutation
|
6
|
+
module Method
|
7
|
+
class LogicalOperatorReplacement < Helpers::InfixOperatorReplacement
|
8
|
+
def operator_names
|
9
|
+
%i(& | ^)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require_relative "helpers/infix_operator_replacement"
|
2
|
+
|
3
|
+
module Mutiny
|
4
|
+
module Mutants
|
5
|
+
class Mutation
|
6
|
+
module Method
|
7
|
+
class RelationalExpressionReplacement < Helpers::InfixOperatorReplacement
|
8
|
+
def operator_names
|
9
|
+
%i(< <= == != >= >)
|
10
|
+
end
|
11
|
+
|
12
|
+
def replacement
|
13
|
+
builder.either!(builder.true, builder.false)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require_relative "helpers/infix_operator_replacement"
|
2
|
+
|
3
|
+
module Mutiny
|
4
|
+
module Mutants
|
5
|
+
class Mutation
|
6
|
+
module Method
|
7
|
+
class RelationalOperatorReplacement < Helpers::InfixOperatorReplacement
|
8
|
+
def operator_names
|
9
|
+
%i(< <= == != >= >)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require_relative "helpers/operator_replacement"
|
2
|
+
|
3
|
+
module Mutiny
|
4
|
+
module Mutants
|
5
|
+
class Mutation
|
6
|
+
module Method
|
7
|
+
class ShortcutAssignmentOperatorReplacement < Helpers::OperatorReplacement
|
8
|
+
def infix_operator_root
|
9
|
+
:op_asgn
|
10
|
+
end
|
11
|
+
|
12
|
+
def infix_operator_names
|
13
|
+
%i(+ - * / % ** & | ^ << >>)
|
14
|
+
end
|
15
|
+
|
16
|
+
def prefix_operator_names
|
17
|
+
%i(and_asgn or_asgn)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require "mutiny/mutants/mutation"
|
2
|
+
|
3
|
+
module Mutiny
|
4
|
+
module Mutants
|
5
|
+
class Mutation
|
6
|
+
module Method
|
7
|
+
class UnaryArithmeticOperatorDeletion < Mutation
|
8
|
+
def pattern # rubocop:disable Metrics/AbcSize
|
9
|
+
builder.either!(
|
10
|
+
builder.literal!(:int, builder.VAL { |val| val.name < 0 }),
|
11
|
+
builder.literal!(:float, builder.VAL { |val| val.name < 0 }),
|
12
|
+
builder.literal!(:send, builder.VAL, :-@)
|
13
|
+
)
|
14
|
+
end
|
15
|
+
|
16
|
+
def replacement
|
17
|
+
builder.derivation! :val, :& do |val, root|
|
18
|
+
if val.name.is_a?(Numeric)
|
19
|
+
builder.literal!(root.name, -val.name)
|
20
|
+
else
|
21
|
+
val
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require "mutiny/mutants/mutation"
|
2
|
+
|
3
|
+
module Mutiny
|
4
|
+
module Mutants
|
5
|
+
class Mutation
|
6
|
+
module Method
|
7
|
+
class UnaryArithmeticOperatorInsertion < Mutation
|
8
|
+
def pattern # rubocop:disable Metrics/AbcSize
|
9
|
+
builder.either!(
|
10
|
+
builder.literal!(:int, builder.VAL { |val| val.name > 0 }),
|
11
|
+
builder.literal!(:float, builder.VAL { |val| val.name > 0 }),
|
12
|
+
builder.literal!(:send, nil, builder.VAL)
|
13
|
+
)
|
14
|
+
end
|
15
|
+
|
16
|
+
def replacement
|
17
|
+
builder.derivation! :val, :& do |val, root|
|
18
|
+
if val.name.is_a?(Numeric)
|
19
|
+
builder.literal!(root.name, -val.name)
|
20
|
+
else
|
21
|
+
builder.literal!(:send, root, :-@)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require_relative "mutant_set"
|
2
|
+
|
3
|
+
module Mutiny
|
4
|
+
module Mutants
|
5
|
+
class MutationSet
|
6
|
+
def initialize(*mutations)
|
7
|
+
@mutations = mutations
|
8
|
+
end
|
9
|
+
|
10
|
+
# TODO : would performance improve by iterating over subjects than over operators?
|
11
|
+
# Probably could improve (more) if metamorpher also supported composite transformers so that
|
12
|
+
# several mutation operators could be matched simulatenously during a single AST traversal
|
13
|
+
def mutate(subjects)
|
14
|
+
MutantSet.new.tap do |mutants|
|
15
|
+
@mutations.each do |mutation|
|
16
|
+
subjects.each do |subject|
|
17
|
+
mutants.add(subject, mutation.mutate_file(subject.path))
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require_relative "mutation_set"
|
2
|
+
require_relative "mutation/method/binary_arithmetic_operator_replacement"
|
3
|
+
require_relative "mutation/method/conditional_operator_deletion"
|
4
|
+
require_relative "mutation/method/conditional_operator_insertion"
|
5
|
+
require_relative "mutation/method/conditional_operator_replacement"
|
6
|
+
require_relative "mutation/method/relational_expression_replacement"
|
7
|
+
require_relative "mutation/method/relational_operator_replacement"
|
8
|
+
require_relative "mutation/method/logical_operator_deletion"
|
9
|
+
require_relative "mutation/method/logical_operator_insertion"
|
10
|
+
require_relative "mutation/method/logical_operator_replacement"
|
11
|
+
require_relative "mutation/method/shortcut_assignment_operator_replacement"
|
12
|
+
require_relative "mutation/method/unary_arithmetic_operator_deletion"
|
13
|
+
require_relative "mutation/method/unary_arithmetic_operator_insertion"
|
14
|
+
|
15
|
+
module Mutiny
|
16
|
+
module Mutants
|
17
|
+
class Ruby
|
18
|
+
def mutants_for(subjects)
|
19
|
+
mutations.mutate(subjects)
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def mutations # rubocop:disable Metrics/MethodLength
|
25
|
+
@operators ||= MutationSet.new(
|
26
|
+
Mutation::Method::BinaryArithmeticOperatorReplacement.new,
|
27
|
+
Mutation::Method::ConditionalOperatorDeletion.new,
|
28
|
+
Mutation::Method::ConditionalOperatorInsertion.new,
|
29
|
+
Mutation::Method::ConditionalOperatorReplacement.new,
|
30
|
+
Mutation::Method::RelationalExpressionReplacement.new,
|
31
|
+
Mutation::Method::RelationalOperatorReplacement.new,
|
32
|
+
Mutation::Method::LogicalOperatorDeletion.new,
|
33
|
+
Mutation::Method::LogicalOperatorInsertion.new,
|
34
|
+
Mutation::Method::LogicalOperatorReplacement.new,
|
35
|
+
Mutation::Method::ShortcutAssignmentOperatorReplacement.new,
|
36
|
+
Mutation::Method::UnaryArithmeticOperatorDeletion.new,
|
37
|
+
Mutation::Method::UnaryArithmeticOperatorInsertion.new
|
38
|
+
)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
module Mutiny
|
2
|
+
module Subjects
|
3
|
+
class Environment
|
4
|
+
class Type
|
5
|
+
attr_reader :mod, :configuration
|
6
|
+
|
7
|
+
def initialize(mod, configuration)
|
8
|
+
@mod = mod
|
9
|
+
@configuration = configuration
|
10
|
+
end
|
11
|
+
|
12
|
+
def relevant?
|
13
|
+
!name.nil? && in_scope? && loadable?
|
14
|
+
end
|
15
|
+
|
16
|
+
def to_subject
|
17
|
+
Subject.new(name: name, path: absolute_path, root: load_path)
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def name
|
23
|
+
mod.name
|
24
|
+
end
|
25
|
+
|
26
|
+
def in_scope?
|
27
|
+
configuration.patterns.any? { |pattern| pattern.match?(name) }
|
28
|
+
end
|
29
|
+
|
30
|
+
def loadable?
|
31
|
+
!load_path.nil?
|
32
|
+
end
|
33
|
+
|
34
|
+
def load_path
|
35
|
+
configuration.load_paths.detect do |load_path|
|
36
|
+
source_locations.any? { |locs| locs.start_with?(load_path) }
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def source_locations
|
41
|
+
mod.instance_methods(false)
|
42
|
+
.map { |method_name| mod.instance_method(method_name).source_location }
|
43
|
+
.reject(&:nil?)
|
44
|
+
.map(&:first)
|
45
|
+
.uniq
|
46
|
+
end
|
47
|
+
|
48
|
+
def absolute_path
|
49
|
+
source_locations.first
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require_relative "environment/type"
|
2
|
+
|
3
|
+
module Mutiny
|
4
|
+
module Subjects
|
5
|
+
class Environment
|
6
|
+
attr_reader :configuration
|
7
|
+
|
8
|
+
def initialize(configuration)
|
9
|
+
@configuration = configuration
|
10
|
+
configuration.loads.each { |l| $LOAD_PATH << l }
|
11
|
+
configuration.requires.each { |r| require r }
|
12
|
+
end
|
13
|
+
|
14
|
+
def subjects
|
15
|
+
SubjectSet.new(modules.select(&:relevant?).map(&:to_subject))
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def modules
|
21
|
+
ObjectSpace.each_object(Module).map { |mod| Type.new(mod, configuration) }
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'pathname'
|
2
|
+
|
3
|
+
module Mutiny
|
4
|
+
module Subjects
|
5
|
+
class Subject
|
6
|
+
attr_reader :name, :path, :root
|
7
|
+
|
8
|
+
def initialize(name:, path: nil, root: nil)
|
9
|
+
@name = name
|
10
|
+
@path = path
|
11
|
+
@root = root
|
12
|
+
end
|
13
|
+
|
14
|
+
def relative_path
|
15
|
+
absolute_path = Pathname.new(path)
|
16
|
+
root_path = Pathname.new(root)
|
17
|
+
|
18
|
+
absolute_path.relative_path_from(root_path).to_s
|
19
|
+
end
|
20
|
+
|
21
|
+
def eql?(other)
|
22
|
+
is_a?(other.class) && other.name == name && other.path == path && other.root == root
|
23
|
+
end
|
24
|
+
|
25
|
+
alias_method "==", "eql?"
|
26
|
+
|
27
|
+
def hash
|
28
|
+
[name, path, root].hash
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Mutiny
|
2
|
+
module Tests
|
3
|
+
class TestRun
|
4
|
+
attr_reader :tests, :failed_tests, :output, :runtime
|
5
|
+
|
6
|
+
def initialize(tests:, failed_tests:, output:, runtime:)
|
7
|
+
@tests = tests
|
8
|
+
@failed_tests = failed_tests
|
9
|
+
@output = output
|
10
|
+
@runtime = runtime
|
11
|
+
end
|
12
|
+
|
13
|
+
def passed?
|
14
|
+
failed_tests.empty?
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require "forwardable"
|
2
|
+
|
3
|
+
module Mutiny
|
4
|
+
module Tests
|
5
|
+
class TestSet
|
6
|
+
extend Forwardable
|
7
|
+
def_delegators :tests, :size, :empty?, :hash
|
8
|
+
|
9
|
+
def self.empty
|
10
|
+
new([])
|
11
|
+
end
|
12
|
+
|
13
|
+
def initialize(tests)
|
14
|
+
@tests = tests
|
15
|
+
end
|
16
|
+
|
17
|
+
def locations
|
18
|
+
tests.map(&:location)
|
19
|
+
end
|
20
|
+
|
21
|
+
def for(subject_set)
|
22
|
+
subset { |test| subject_set.names.include?(test.expression) }
|
23
|
+
end
|
24
|
+
|
25
|
+
def subset(&block)
|
26
|
+
self.class.new(tests.select(&block))
|
27
|
+
end
|
28
|
+
|
29
|
+
def take(n)
|
30
|
+
self.class.new(tests.take(n))
|
31
|
+
end
|
32
|
+
|
33
|
+
def eql?(other)
|
34
|
+
is_a?(other.class) && other.tests == tests
|
35
|
+
end
|
36
|
+
|
37
|
+
alias_method "==", "eql?"
|
38
|
+
|
39
|
+
protected
|
40
|
+
|
41
|
+
attr_reader :tests
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
data/lib/mutiny/tests.rb
ADDED
data/lib/mutiny.rb
ADDED
data/mutiny.gemspec
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'mutiny/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "mutiny"
|
8
|
+
spec.version = Mutiny::VERSION
|
9
|
+
spec.authors = ["Louis Rose"]
|
10
|
+
spec.email = ["louis.rose@york.ac.uk"]
|
11
|
+
spec.description = %q{A tiny and experimental mutation testing framework for exploring research ideas.}
|
12
|
+
spec.summary = %q{A tiny mutation testing framework for Ruby}
|
13
|
+
spec.homepage = "https://github.com/mutiny/mutiny"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files`.split($/)
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_runtime_dependency "parser", "~> 2.2.2"
|
22
|
+
spec.add_runtime_dependency "unparser", "~> 0.2.4"
|
23
|
+
spec.add_runtime_dependency "gli", "~> 2.13.0"
|
24
|
+
spec.add_runtime_dependency "metamorpher", "~> 0.2.2"
|
25
|
+
|
26
|
+
spec.add_development_dependency "bundler", "~> 1.10.2"
|
27
|
+
spec.add_development_dependency "rake", "~> 10.4.2"
|
28
|
+
spec.add_development_dependency "rspec", "~> 3.2.0"
|
29
|
+
spec.add_development_dependency "aruba", "~> 0.6.0"
|
30
|
+
spec.add_development_dependency "codeclimate-test-reporter", "~> 0.4.6"
|
31
|
+
spec.add_development_dependency "rubocop", "~> 0.31.0"
|
32
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
describe "Using Mutiny to check whether mutation testing can be carried out" do
|
2
|
+
it "should report success for a valid setup" do
|
3
|
+
cd "calculator"
|
4
|
+
run "bundle exec mutiny check"
|
5
|
+
|
6
|
+
expected_output = "Checking...\n" \
|
7
|
+
" At least one relevant test found (6 in total)\n" \
|
8
|
+
" All relevant tests passed\n" \
|
9
|
+
"Looks good!\n"
|
10
|
+
|
11
|
+
expect(all_output).to eq(expected_output)
|
12
|
+
end
|
13
|
+
|
14
|
+
it "should report failure when there are no relevant tests" do
|
15
|
+
cd "untested_calculator"
|
16
|
+
run "bundle exec mutiny -r calculator check"
|
17
|
+
|
18
|
+
expected_output = "Checking...\n" \
|
19
|
+
" No relevant tests found (for modules matching '*')\n" \
|
20
|
+
"Either your mutiny configuration is wrong, or you're missing some tests!\n"
|
21
|
+
|
22
|
+
expect(all_output).to eq(expected_output)
|
23
|
+
end
|
24
|
+
|
25
|
+
it "should report warning when there are failing tests" do
|
26
|
+
cd "buggy_calculator"
|
27
|
+
run "bundle exec mutiny -r calculator check"
|
28
|
+
|
29
|
+
expected_output = "Checking...\n" \
|
30
|
+
" At least one relevant test found (3 in total)\n" \
|
31
|
+
" Not all relevant tests passed. The failing tests are:\n" \
|
32
|
+
" ./spec/calculator/max_spec.rb:9\n" \
|
33
|
+
" ./spec/calculator/max_spec.rb:13\n" \
|
34
|
+
"\n" \
|
35
|
+
"Looks ok, but note that mutiny is most effective when all tests pass.\n"
|
36
|
+
|
37
|
+
expect(all_output).to eq(expected_output)
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
describe "Using Mutiny to generate mutants" do
|
2
|
+
it "should report statistics of generated mutants" do
|
3
|
+
cd "calculator"
|
4
|
+
run "bundle exec mutiny mutate"
|
5
|
+
|
6
|
+
expected_output = "Mutating...\n" \
|
7
|
+
"Generated 14 mutants:\n" \
|
8
|
+
" * calculator/max.rb - 7 mutants\n" \
|
9
|
+
" * calculator/min.rb - 7 mutants\n" \
|
10
|
+
"Check the '.mutants' directory to browse the generated mutants.\n"
|
11
|
+
|
12
|
+
expect(all_output).to eq(expected_output)
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should write mutants to the .mutants directory" do
|
16
|
+
cd "calculator"
|
17
|
+
run "bundle exec mutiny mutate"
|
18
|
+
|
19
|
+
check_file_content(".mutants/calculator/min.0.rb", /if true/)
|
20
|
+
check_file_content(".mutants/calculator/min.1.rb", /if false/)
|
21
|
+
check_file_content(".mutants/calculator/min.2.rb", /if right <= left/)
|
22
|
+
check_file_content(".mutants/calculator/min.3.rb", /if right == left/)
|
23
|
+
check_file_content(".mutants/calculator/min.4.rb", /if right != left/)
|
24
|
+
check_file_content(".mutants/calculator/min.5.rb", /if right >= left/)
|
25
|
+
check_file_content(".mutants/calculator/min.6.rb", /if right > left/)
|
26
|
+
|
27
|
+
check_file_content(".mutants/calculator/max.0.rb", /if true/)
|
28
|
+
check_file_content(".mutants/calculator/max.1.rb", /if false/)
|
29
|
+
check_file_content(".mutants/calculator/max.2.rb", /if right < left/)
|
30
|
+
check_file_content(".mutants/calculator/max.3.rb", /if right <= left/)
|
31
|
+
check_file_content(".mutants/calculator/max.4.rb", /if right == left/)
|
32
|
+
check_file_content(".mutants/calculator/max.5.rb", /if right != left/)
|
33
|
+
check_file_content(".mutants/calculator/max.6.rb", /if right >= left/)
|
34
|
+
end
|
35
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'rspec/core/sandbox'
|
2
|
+
require 'rspec/support/spec/stderr_splitter'
|
3
|
+
require 'rspec/support/spec/in_sub_process'
|
4
|
+
|
5
|
+
require "codeclimate-test-reporter"
|
6
|
+
CodeClimate::TestReporter.start
|
7
|
+
|
8
|
+
Dir["./spec/support/**/*.rb"].each { |f| require f }
|
9
|
+
|
10
|
+
require "mutiny"
|
11
|
+
|
12
|
+
# This file was generated by the `rspec --init` command. Conventionally, all
|
13
|
+
# specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
|
14
|
+
# Require this file using `require "spec_helper"` to ensure that it is only
|
15
|
+
# loaded once.
|
16
|
+
#
|
17
|
+
# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
|
18
|
+
RSpec.configure do |config|
|
19
|
+
config.run_all_when_everything_filtered = true
|
20
|
+
config.filter_run :focus
|
21
|
+
|
22
|
+
# Run specs in random order to surface order dependencies. If you find an
|
23
|
+
# order dependency and want to debug it, you can fix the order by providing
|
24
|
+
# the seed, which is printed after each run.
|
25
|
+
# --seed 1234
|
26
|
+
config.order = 'random'
|
27
|
+
|
28
|
+
# Isolate mutiny-under-test from its own test suite
|
29
|
+
config.around(:example) do |example|
|
30
|
+
RSpec::Core::Sandbox.sandboxed do
|
31
|
+
example.run
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
# Make in_sub_process method available for any tests that need
|
36
|
+
# to pollute the Ruby environment (e.g., change the ObjectSpace)
|
37
|
+
config.include RSpec::Support::InSubProcess
|
38
|
+
end
|