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.
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,17 @@
1
+ require 'aruba/api'
2
+ require 'aruba/reporting'
3
+
4
+ module InExampleProject
5
+ def in_example_project(example_name, should_require = false)
6
+ in_sub_process do
7
+ $LOAD_PATH << File.join(Dir.pwd, "examples", example_name, "lib")
8
+ Dir.chdir(File.join("examples", example_name))
9
+ require example_name if should_require
10
+ yield
11
+ end
12
+ end
13
+ end
14
+
15
+ RSpec.configure do |config|
16
+ config.include InExampleProject
17
+ end
@@ -0,0 +1,26 @@
1
+ shared_examples "an operator replacement mutation" do |operators|
2
+ operators.each do |original_operator|
3
+ it "mutates #{original_operator} to every other operator" do
4
+ program = "foo #{original_operator} bar"
5
+ mutants = subject.mutate(program)
6
+ expected_mutants = operators
7
+ .reject { |op| op == original_operator }
8
+ .map { |op| program.gsub(original_operator.to_s, op.to_s) }
9
+
10
+ expect(mutants).to eq(expected_mutants)
11
+ end
12
+ end
13
+ end
14
+
15
+ shared_examples "an operator replacement mutation with extra replacements" do |replacements|
16
+ replacements.each do |original_operator, replacement_operators|
17
+ it "mutates #{original_operator} to every other operator" do
18
+ program = "foo #{original_operator} bar"
19
+ mutants = subject.mutate(program)
20
+ expected_mutants = replacement_operators
21
+ .map { |op| program.gsub(original_operator.to_s, op.to_s) }
22
+
23
+ expect(mutants).to eq(expected_mutants)
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,23 @@
1
+ module Mutiny
2
+ class Integration
3
+ class RSpec
4
+ describe Parser do
5
+ let(:test_set) { subject.call }
6
+
7
+ it "should be able to count tests" do
8
+ in_example_project("calculator") do
9
+ expect(test_set.size).to eq(6)
10
+ expect(test_set.empty?).to be_falsey
11
+ end
12
+ end
13
+
14
+ it "should be able to report that there are no tests" do
15
+ in_example_project("untested_calculator") do
16
+ expect(test_set.size).to eq(0)
17
+ expect(test_set.empty?).to be_truthy
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,47 @@
1
+ module Mutiny
2
+ class Integration
3
+ class RSpec
4
+ describe Runner do
5
+ let(:test_set) { Parser.new.call }
6
+ let(:test_run) { subject.call }
7
+ subject { Runner.new(test_set) }
8
+
9
+ it "should be able to run a passing test set" do
10
+ in_example_project("calculator") do
11
+ expect(test_run.passed?).to be_truthy
12
+ expect(test_run.failed_tests.empty?).to be_truthy
13
+ end
14
+ end
15
+
16
+ it "should be able to run a failing test set" do
17
+ in_example_project("buggy_calculator") do
18
+ expect(test_run.passed?).to be_falsey
19
+ expect(test_run.failed_tests.locations).to eq([
20
+ "./examples/buggy_calculator/spec/calculator/max_spec.rb:9",
21
+ "./examples/buggy_calculator/spec/calculator/max_spec.rb:13"
22
+ ])
23
+ end
24
+ end
25
+
26
+ it "should be able to run a partial test set" do
27
+ in_example_project("buggy_calculator") do
28
+ partial_test_set = test_set.take(1)
29
+ test_run = Runner.new(partial_test_set).call
30
+
31
+ # Note: This would be false if the whole test set was run
32
+ expect(test_run.passed?).to be_truthy
33
+ end
34
+ end
35
+
36
+ it "should be able to run an empty test set" do
37
+ in_example_project("calculator") do
38
+ test_run = Runner.new(TestSet.empty).call
39
+
40
+ expect(test_run.passed?).to be_truthy
41
+ expect(test_run.failed_tests.empty?).to be_truthy
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,57 @@
1
+ module Mutiny
2
+ module Mutants
3
+ class ObservableMutantSet < MutantSet
4
+ def create_mutant(subject, code)
5
+ MutantSpy.new(subject: subject, code: code)
6
+ end
7
+ end
8
+
9
+ class MutantSpy < Mutant
10
+ attr_reader :directory, :index
11
+
12
+ def store(directory, index)
13
+ @directory = directory
14
+ @index = index
15
+ end
16
+ end
17
+
18
+ describe MutantSet do
19
+ subject(:mutant_set) { ObservableMutantSet.new }
20
+
21
+ before(:each) do
22
+ mutant_set.add(:min, [:min_mutant_1, :min_mutant_2])
23
+ mutant_set.add(:max, [:max_mutant_1])
24
+ mutant_set.add(:min, [:min_mutant_3])
25
+ end
26
+
27
+ it "groups mutants by subject" do
28
+ groups = mutant_set.group_by_subject.to_a
29
+ first = groups.first
30
+ second = groups.last
31
+
32
+ expect(first).to eq(mutants_for(:min, :min_mutant_1, :min_mutant_2, :min_mutant_3))
33
+ expect(second).to eq(mutants_for(:max, :max_mutant_1))
34
+ end
35
+
36
+ it "counts mutants" do
37
+ expect(mutant_set.size).to eq(4)
38
+ end
39
+
40
+ it "stores by delegating to mutants" do
41
+ mutant_set.store(:mutant_dir)
42
+
43
+ mutant_set.group_by_subject.each do |_, mutants|
44
+ mutants.each_with_index do |mutant, index|
45
+ expect(mutant.directory).to eq(:mutant_dir)
46
+ expect(mutant.index).to eq(index)
47
+ end
48
+ end
49
+ end
50
+
51
+ def mutants_for(subject, *code)
52
+ mutants = code.map { |c| Mutant.new(subject: subject, code: c) }
53
+ [subject, mutants]
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,11 @@
1
+ module Mutiny
2
+ module Mutants
3
+ class Mutation
4
+ module Method
5
+ describe BinaryArithmeticOperatorReplacement do
6
+ it_behaves_like "an operator replacement mutation", %i(+ - * / %)
7
+ end
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,17 @@
1
+ module Mutiny
2
+ module Mutants
3
+ class Mutation
4
+ module Method
5
+ describe ConditionalOperatorDeletion do
6
+ it "mutates negated literal to literal" do
7
+ expect(subject.mutate("a = !true")).to eq(["a = true"])
8
+ end
9
+
10
+ it "mutates negated variable to variable" do
11
+ expect(subject.mutate("a = !b")).to eq(["a = b"])
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,17 @@
1
+ module Mutiny
2
+ module Mutants
3
+ class Mutation
4
+ module Method
5
+ describe ConditionalOperatorInsertion do
6
+ it "mutates literal to negated literal" do
7
+ expect(subject.mutate("return true")).to eq(["return !true"])
8
+ end
9
+
10
+ it "mutates variable to negated variable" do
11
+ expect(subject.mutate("return a")).to eq(["return !a"])
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,16 @@
1
+ module Mutiny
2
+ module Mutants
3
+ class Mutation
4
+ module Method
5
+ describe ConditionalOperatorReplacement do
6
+ it_behaves_like "an operator replacement mutation with extra replacements",
7
+ '^': %i(&& ||),
8
+ '&&': %i(^ ||),
9
+ '||': %i(^ &&),
10
+ and: %i(^ ||),
11
+ or: %i(^ &&)
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,17 @@
1
+ module Mutiny
2
+ module Mutants
3
+ class Mutation
4
+ module Method
5
+ describe LogicalOperatorDeletion do
6
+ it "mutates inverted integer to integer" do
7
+ expect(subject.mutate("a = ~42")).to eq(["a = 42"])
8
+ end
9
+
10
+ it "mutates inverted variable to variable" do
11
+ expect(subject.mutate("a = ~b")).to eq(["a = b"])
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,17 @@
1
+ module Mutiny
2
+ module Mutants
3
+ class Mutation
4
+ module Method
5
+ describe LogicalOperatorInsertion do
6
+ it "mutates integer to inverted integer" do
7
+ expect(subject.mutate("return 42")).to eq(["return ~42"])
8
+ end
9
+
10
+ it "mutates variable to inverted variable" do
11
+ expect(subject.mutate("return a")).to eq(["return ~a"])
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,11 @@
1
+ module Mutiny
2
+ module Mutants
3
+ class Mutation
4
+ module Method
5
+ describe LogicalOperatorReplacement do
6
+ it_behaves_like "an operator replacement mutation", %i(& | ^)
7
+ end
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,18 @@
1
+ module Mutiny
2
+ module Mutants
3
+ class Mutation
4
+ module Method
5
+ describe RelationalExpressionReplacement do
6
+ %i(< <= == != >= >).each do |operator|
7
+ it "mutates #{operator} to true and false" do
8
+ program = "foo #{operator} bar"
9
+ mutants = subject.mutate(program)
10
+
11
+ expect(mutants).to eq(%w(true false))
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,11 @@
1
+ module Mutiny
2
+ module Mutants
3
+ class Mutation
4
+ module Method
5
+ describe RelationalOperatorReplacement do
6
+ it_behaves_like "an operator replacement mutation", %i(< <= == != >= >)
7
+ end
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,12 @@
1
+ module Mutiny
2
+ module Mutants
3
+ class Mutation
4
+ module Method
5
+ describe ShortcutAssignmentOperatorReplacement do
6
+ it_behaves_like "an operator replacement mutation",
7
+ %i(+= -= *= /= %= **= &= |= ^= <<= >>= &&= ||=)
8
+ end
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,21 @@
1
+ module Mutiny
2
+ module Mutants
3
+ class Mutation
4
+ module Method
5
+ describe UnaryArithmeticOperatorDeletion do
6
+ it "mutates negative integer to positive integer" do
7
+ expect(subject.mutate("a = -42")).to eq(["a = 42"])
8
+ end
9
+
10
+ it "mutates negative float to positive float" do
11
+ expect(subject.mutate("a = -4.2")).to eq(["a = 4.2"])
12
+ end
13
+
14
+ it "mutates negative variable to positive variable" do
15
+ expect(subject.mutate("a = -b")).to eq(["a = b"])
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,21 @@
1
+ module Mutiny
2
+ module Mutants
3
+ class Mutation
4
+ module Method
5
+ describe UnaryArithmeticOperatorInsertion do
6
+ it "mutates positive integer to negative integer" do
7
+ expect(subject.mutate("a = 42")).to eq(["a = -42"])
8
+ end
9
+
10
+ it "mutates positive float to negative float" do
11
+ expect(subject.mutate("a = 4.2")).to eq(["a = -4.2"])
12
+ end
13
+
14
+ it "mutates positive variable to negative variable" do
15
+ expect(subject.mutate("a = b")).to eq(["a = -b"])
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,28 @@
1
+ module Mutiny
2
+ describe Pattern do
3
+ context "wildcard" do
4
+ subject { Pattern.new("*") }
5
+
6
+ it "should match all" do
7
+ expect(subject.match?("Calculator")).to be_truthy
8
+ expect(subject.match?("Calculator::Max")).to be_truthy
9
+ end
10
+ end
11
+
12
+ context "fully qualified name" do
13
+ subject { Pattern.new("Calculator::Max") }
14
+
15
+ it "should match same name" do
16
+ expect(subject.match?("Calculator::Max")).to be_truthy
17
+ end
18
+
19
+ it "should match a more specific name" do
20
+ expect(subject.match?("Calculator::Max::Float")).to be_truthy
21
+ end
22
+
23
+ it "should not match a less specific name" do
24
+ expect(subject.match?("Calculator")).to be_falsey
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,54 @@
1
+ module Mutiny
2
+ module Subjects
3
+ class Environment
4
+ describe Type do
5
+ context "relevance" do
6
+ it "should include named class that are in scope and that are on the load path" do
7
+ configuration = Configuration.new(patterns: ["*"], loads: ["spec"])
8
+ type = Type.new(Person, configuration)
9
+
10
+ expect(type.relevant?).to be_truthy
11
+ end
12
+
13
+ it "should exclude anonymous classes" do
14
+ type = Type.new(Class.new, Configuration.new(patterns: ["*"], loads: ["spec"]))
15
+
16
+ expect(type.relevant?).to be_falsey
17
+ end
18
+
19
+ it "should exclude classes that are not in scope" do
20
+ configuration = Configuration.new(patterns: ["Department"], loads: ["spec"])
21
+ type = Type.new(Person, configuration)
22
+
23
+ expect(type.relevant?).to be_falsey
24
+ end
25
+
26
+ it "should exclude classes that are not on the load path" do
27
+ configuration = Configuration.new(patterns: ["*"], loads: ["lib"])
28
+ type = Type.new(Person, configuration)
29
+
30
+ expect(type.relevant?).to be_falsey
31
+ end
32
+ end
33
+
34
+ context "to_subject" do
35
+ it "should build a valid subject" do
36
+ configuration = Configuration.new(patterns: ["*"], loads: ["spec"])
37
+ type = Type.new(Person, configuration)
38
+ expected_subject = Subject.new(
39
+ name: "Mutiny::Subjects::Environment::Person",
40
+ path: __FILE__,
41
+ root: __FILE__.match(%r{.*/spec})[0]
42
+ )
43
+
44
+ expect(type.to_subject).to eq(expected_subject)
45
+ end
46
+ end
47
+
48
+ class Person
49
+ attr_accessor :name
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,71 @@
1
+ module Mutiny
2
+ module Subjects
3
+ describe Environment do
4
+ let(:module_names) { %w(Calculator Calculator::Max Calculator::Min) }
5
+
6
+ context "initialization" do
7
+ it "should add the required class and all dependences to available modules" do
8
+ in_sub_process do
9
+ configuration = Configuration.new(
10
+ loads: ["examples/calculator/lib"],
11
+ requires: ["calculator"]
12
+ )
13
+
14
+ expect { Environment.new(configuration) }
15
+ .to change { ObjectSpace.each_object(Module).map(&:name).reject(&:nil?).sort }
16
+ .by(module_names)
17
+ end
18
+ end
19
+
20
+ it "should not add any classes without a require" do
21
+ in_sub_process do
22
+ configuration = Configuration.new(
23
+ loads: ["examples/calculator/lib/calculator"]
24
+ )
25
+
26
+ expect { Environment.new(configuration) }
27
+ .not_to change { ObjectSpace.each_object(Module).map(&:name) }
28
+ end
29
+ end
30
+
31
+ it "should raise when require not found" do
32
+ in_sub_process do
33
+ configuration = Configuration.new(
34
+ requires: ["calculator"]
35
+ )
36
+
37
+ expect { Environment.new(configuration) }
38
+ .to raise_error(LoadError)
39
+ end
40
+ end
41
+ end
42
+
43
+ context "subjects" do
44
+ let(:mutatable_module_names) { %w(Calculator::Max Calculator::Min) }
45
+
46
+ let(:configuration) do
47
+ Configuration.new(
48
+ loads: ["examples/calculator/lib"],
49
+ requires: ["calculator"],
50
+ patterns: ["Calculator"]
51
+ )
52
+ end
53
+
54
+ let(:environment) { Environment.new(configuration) }
55
+
56
+ it "should return all subjects matching pattern" do
57
+ in_sub_process do
58
+ expect(environment.subjects.names).to eq(mutatable_module_names)
59
+ end
60
+ end
61
+
62
+ it "should function in the presence of anonymous modules" do
63
+ in_sub_process do
64
+ Class.new # create anonymous class
65
+ expect(environment.subjects.names).to eq(mutatable_module_names)
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,25 @@
1
+ module Mutiny
2
+ module Subjects
3
+ describe Subject do
4
+ context "relative_path" do
5
+ it "should return path relative from root" do
6
+ subject = Subject.new(name: "Foo::Bar::Baz", path: "/foo/bar/baz.rb", root: "/foo")
7
+
8
+ expect(subject.relative_path).to eq("bar/baz.rb")
9
+ end
10
+
11
+ it "should return path relative from nested root" do
12
+ subject = Subject.new(name: "Foo::Bar::Baz", path: "/foo/bar/baz.rb", root: "/foo/bar")
13
+
14
+ expect(subject.relative_path).to eq("baz.rb")
15
+ end
16
+
17
+ it "should return path relative from root with trailing slash" do
18
+ subject = Subject.new(name: "Foo::Bar::Baz", path: "/foo/bar/baz.rb", root: "/foo/")
19
+
20
+ expect(subject.relative_path).to eq("bar/baz.rb")
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,75 @@
1
+ module Mutiny
2
+ module Tests
3
+ describe TestSet do
4
+ let(:tests) { %w(test1 test2 test3) }
5
+ subject { TestSet.new(tests) }
6
+
7
+ context "take" do
8
+ it "(n) should return TestSet containing first n tests" do
9
+ expect(subject.take(2)).to eq(TestSet.new(tests.take(2)))
10
+ end
11
+
12
+ it "(0) should return an empty TestSet" do
13
+ expect(subject.take(0)).to eq(TestSet.empty)
14
+ end
15
+
16
+ it "(n) of an empty TestSet should return an empty TestSet" do
17
+ expect(TestSet.empty.take(2)).to eq(TestSet.empty)
18
+ end
19
+ end
20
+
21
+ context "subset" do
22
+ it "should return TestSet containing tests that match condition" do
23
+ subset = subject.subset { |t| t.end_with? "2" }
24
+ expected = TestSet.new(tests.select { |t| t.end_with? "2" })
25
+
26
+ expect(subset).to eq(expected)
27
+ end
28
+
29
+ it "should return empty TestSet when no tests match condition" do
30
+ subset = subject.subset { |_| false }
31
+
32
+ expect(subset).to eq(TestSet.empty)
33
+ end
34
+ end
35
+
36
+ context "for" do
37
+ it "should return only those tests (whose expression) matches a subject" do
38
+ subject_set = subject_set_for("Max", "Min")
39
+ test_set = test_set_for("Subtract", "Min", "Add")
40
+
41
+ expect(test_set.for(subject_set)).to eq(test_set.subset { |t| t.expression == "Min" })
42
+ end
43
+
44
+ it "should return multiple tests for a single subject" do
45
+ subject_set = subject_set_for("Min")
46
+ test_set = test_set_for("Min", "Max", "Min", "Max", "Min")
47
+
48
+ expect(test_set.for(subject_set)).to eq(test_set.subset { |t| t.expression == "Min" })
49
+ end
50
+
51
+ it "should return no tests when there are no tests" do
52
+ subject_set = subject_set_for("Max", "Min")
53
+ test_set = TestSet.empty
54
+
55
+ expect(test_set.for(subject_set)).to eq(TestSet.empty)
56
+ end
57
+
58
+ it "should return no tests when there are no relevant subjects" do
59
+ subject_set = subject_set_for("Max", "Min")
60
+ test_set = test_set_for("Subtract", "Add")
61
+
62
+ expect(test_set.for(subject_set)).to eq(TestSet.empty)
63
+ end
64
+
65
+ def subject_set_for(*names)
66
+ Subjects::SubjectSet.new(names.map { |n| Subjects::Subject.new(name: n) })
67
+ end
68
+
69
+ def test_set_for(*expressions)
70
+ TestSet.new(expressions.map { |e| Test.new(expression: e) })
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end