mutiny 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (100) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +6 -0
  3. data/.rspec +3 -0
  4. data/.rubocop.yml +18 -0
  5. data/.ruby-version +1 -0
  6. data/.travis.yml +6 -0
  7. data/Gemfile +5 -0
  8. data/Gemfile.lock +113 -0
  9. data/LICENSE.txt +22 -0
  10. data/README.md +16 -0
  11. data/RELEASES.md +8 -0
  12. data/Rakefile +23 -0
  13. data/TODO.md +55 -0
  14. data/bin/mutiny +35 -0
  15. data/examples/buggy_calculator/.rspec +2 -0
  16. data/examples/buggy_calculator/lib/calculator/max.rb +9 -0
  17. data/examples/buggy_calculator/lib/calculator.rb +4 -0
  18. data/examples/buggy_calculator/spec/calculator/max_spec.rb +17 -0
  19. data/examples/buggy_calculator/spec/spec_helper.rb +89 -0
  20. data/examples/calculator/.rspec +2 -0
  21. data/examples/calculator/lib/calculator/max.rb +9 -0
  22. data/examples/calculator/lib/calculator/min.rb +9 -0
  23. data/examples/calculator/lib/calculator.rb +5 -0
  24. data/examples/calculator/spec/calculator/max_spec.rb +17 -0
  25. data/examples/calculator/spec/calculator/min_spec.rb +17 -0
  26. data/examples/calculator/spec/spec_helper.rb +89 -0
  27. data/examples/untested_calculator/.rspec +2 -0
  28. data/examples/untested_calculator/lib/calculator/max.rb +9 -0
  29. data/examples/untested_calculator/lib/calculator.rb +4 -0
  30. data/examples/untested_calculator/spec/spec_helper.rb +89 -0
  31. data/lib/mutiny/configuration.rb +25 -0
  32. data/lib/mutiny/integration/rspec/context.rb +26 -0
  33. data/lib/mutiny/integration/rspec/parser.rb +36 -0
  34. data/lib/mutiny/integration/rspec/runner.rb +58 -0
  35. data/lib/mutiny/integration/rspec/test.rb +16 -0
  36. data/lib/mutiny/integration/rspec/test_set.rb +17 -0
  37. data/lib/mutiny/integration/rspec.rb +25 -0
  38. data/lib/mutiny/mode/check.rb +62 -0
  39. data/lib/mutiny/mode/mutate.rb +29 -0
  40. data/lib/mutiny/mode.rb +19 -0
  41. data/lib/mutiny/mutants/mutant.rb +32 -0
  42. data/lib/mutiny/mutants/mutant_set.rb +42 -0
  43. data/lib/mutiny/mutants/mutation/method/binary_arithmetic_operator_replacement.rb +15 -0
  44. data/lib/mutiny/mutants/mutation/method/conditional_operator_deletion.rb +19 -0
  45. data/lib/mutiny/mutants/mutation/method/conditional_operator_insertion.rb +25 -0
  46. data/lib/mutiny/mutants/mutation/method/conditional_operator_replacement.rb +19 -0
  47. data/lib/mutiny/mutants/mutation/method/helpers/infix_operator_replacement.rb +17 -0
  48. data/lib/mutiny/mutants/mutation/method/helpers/operator_replacement.rb +81 -0
  49. data/lib/mutiny/mutants/mutation/method/logical_operator_deletion.rb +19 -0
  50. data/lib/mutiny/mutants/mutation/method/logical_operator_insertion.rb +24 -0
  51. data/lib/mutiny/mutants/mutation/method/logical_operator_replacement.rb +15 -0
  52. data/lib/mutiny/mutants/mutation/method/relational_expression_replacement.rb +19 -0
  53. data/lib/mutiny/mutants/mutation/method/relational_operator_replacement.rb +15 -0
  54. data/lib/mutiny/mutants/mutation/method/shortcut_assignment_operator_replacement.rb +23 -0
  55. data/lib/mutiny/mutants/mutation/method/unary_arithmetic_operator_deletion.rb +29 -0
  56. data/lib/mutiny/mutants/mutation/method/unary_arithmetic_operator_insertion.rb +29 -0
  57. data/lib/mutiny/mutants/mutation.rb +10 -0
  58. data/lib/mutiny/mutants/mutation_set.rb +24 -0
  59. data/lib/mutiny/mutants/ruby.rb +42 -0
  60. data/lib/mutiny/pattern.rb +17 -0
  61. data/lib/mutiny/reporter/stdout.rb +9 -0
  62. data/lib/mutiny/subjects/environment/type.rb +54 -0
  63. data/lib/mutiny/subjects/environment.rb +25 -0
  64. data/lib/mutiny/subjects/subject.rb +32 -0
  65. data/lib/mutiny/subjects/subject_set.rb +17 -0
  66. data/lib/mutiny/subjects.rb +3 -0
  67. data/lib/mutiny/tests/test.rb +12 -0
  68. data/lib/mutiny/tests/test_run.rb +18 -0
  69. data/lib/mutiny/tests/test_set.rb +44 -0
  70. data/lib/mutiny/tests.rb +3 -0
  71. data/lib/mutiny/version.rb +3 -0
  72. data/lib/mutiny.rb +4 -0
  73. data/mutiny.gemspec +32 -0
  74. data/spec/integration/check_spec.rb +39 -0
  75. data/spec/integration/mutate_spec.rb +35 -0
  76. data/spec/spec_helper.rb +38 -0
  77. data/spec/support/aruba.rb +12 -0
  78. data/spec/support/in_example_project.rb +17 -0
  79. data/spec/support/shared_examples/shared_examples_for_an_operator_replacement_mutation.rb +26 -0
  80. data/spec/unit/integration/rspec/parser_spec.rb +23 -0
  81. data/spec/unit/integration/rspec/runner_spec.rb +47 -0
  82. data/spec/unit/mutants/mutant_set_spec.rb +57 -0
  83. data/spec/unit/mutants/mutations/method/binary_operator_replacement_spec.rb +11 -0
  84. data/spec/unit/mutants/mutations/method/conditional_operator_deletion_spec.rb +17 -0
  85. data/spec/unit/mutants/mutations/method/conditional_operator_insertion_spec.rb +17 -0
  86. data/spec/unit/mutants/mutations/method/conditional_operator_replacement_spec.rb +16 -0
  87. data/spec/unit/mutants/mutations/method/logical_operator_deletion_spec.rb +17 -0
  88. data/spec/unit/mutants/mutations/method/logical_operator_insertion_spec.rb +17 -0
  89. data/spec/unit/mutants/mutations/method/logical_operator_replacement_spec.rb +11 -0
  90. data/spec/unit/mutants/mutations/method/relational_expression_replacement_spec.rb +18 -0
  91. data/spec/unit/mutants/mutations/method/relational_operator_replacement_spec.rb +11 -0
  92. data/spec/unit/mutants/mutations/method/shortcut_assignment_operator_replacement_spec.rb +12 -0
  93. data/spec/unit/mutants/mutations/method/unary_arithmetic_operator_deletion_spec.rb +21 -0
  94. data/spec/unit/mutants/mutations/method/unary_arithmetic_operator_insertion_spec.rb +21 -0
  95. data/spec/unit/pattern_spec.rb +28 -0
  96. data/spec/unit/subjects/environment/type_spec.rb +54 -0
  97. data/spec/unit/subjects/environment_spec.rb +71 -0
  98. data/spec/unit/subjects/subject_spec.rb +25 -0
  99. data/spec/unit/subjects/test_set_spec.rb +75 -0
  100. 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,10 @@
1
+ require "metamorpher"
2
+
3
+ module Mutiny
4
+ module Mutants
5
+ class Mutation
6
+ include Metamorpher::Mutator
7
+ include Metamorpher::Builders::AST
8
+ end
9
+ end
10
+ 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,17 @@
1
+ module Mutiny
2
+ class Pattern
3
+ attr_reader :raw
4
+
5
+ def initialize(raw)
6
+ @raw = raw
7
+ end
8
+
9
+ def match?(identifier)
10
+ raw == "*" || identifier.start_with?(raw)
11
+ end
12
+
13
+ def to_s
14
+ raw
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,9 @@
1
+ module Mutiny
2
+ module Reporter
3
+ class Stdout
4
+ def report(message)
5
+ puts message
6
+ end
7
+ end
8
+ end
9
+ 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,17 @@
1
+ module Mutiny
2
+ module Subjects
3
+ class SubjectSet
4
+ def initialize(subjects)
5
+ @subjects = subjects
6
+ end
7
+
8
+ def names
9
+ @names ||= @subjects.map(&:name).sort
10
+ end
11
+
12
+ def each(&block)
13
+ @subjects.each(&block)
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,3 @@
1
+ require_relative "subjects/subject"
2
+ require_relative "subjects/subject_set"
3
+ require_relative "subjects/environment"
@@ -0,0 +1,12 @@
1
+ module Mutiny
2
+ module Tests
3
+ class Test
4
+ attr_reader :location, :expression
5
+
6
+ def initialize(location: nil, expression:)
7
+ @location = location
8
+ @expression = expression
9
+ end
10
+ end
11
+ end
12
+ 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
@@ -0,0 +1,3 @@
1
+ require_relative "tests/test"
2
+ require_relative "tests/test_set"
3
+ require_relative "tests/test_run"
@@ -0,0 +1,3 @@
1
+ module Mutiny
2
+ VERSION = "0.1.0"
3
+ end
data/lib/mutiny.rb ADDED
@@ -0,0 +1,4 @@
1
+ require_relative "mutiny/configuration"
2
+ require_relative "mutiny/subjects"
3
+ require_relative "mutiny/tests"
4
+ require_relative "mutiny/mode"
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
@@ -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
@@ -0,0 +1,12 @@
1
+ require 'aruba/api'
2
+ require 'aruba/reporting'
3
+
4
+ RSpec.configure do |config|
5
+ config.include Aruba::Api
6
+
7
+ config.before(:each) do
8
+ @dirs = [File.join(Dir.pwd, "examples")]
9
+ restore_env
10
+ set_env "GLI_DEBUG", "true"
11
+ end
12
+ end