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