mutiny 0.2.8 → 0.3.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: ffb1518ee7685be979e2c8126897e4adc1c1968a
4
- data.tar.gz: 8e7c06989301a0b963b975e66a377ed7292fa15e
3
+ metadata.gz: c8022ed2607c22424b82450df4d6677b0cca31f7
4
+ data.tar.gz: 5ae8a0cbc564142fd95b3dbea091c9de2743fe66
5
5
  SHA512:
6
- metadata.gz: 317e070449d82430923f27b231360bc3e881189e17dd6ebef48dcea0e5e4a69dbfc3a5921ead07f4de0492ff8899a374b9e119df9a41a082a80932ec72d4efc3
7
- data.tar.gz: 32d348132b09838e11cbde3e189b605cc4bf1fb45b8e956f4856411fdc5ef62a9d1342808eac796d75341df7cdb182fe1b08eb0c72385faecdd337c9ed79348f
6
+ metadata.gz: 260df403d7bd1d04c1516f08098792f31920ef4b15a6f98eafb1e97d79f7fb71090f84d766b003bd52798555a5de86aaef9519e769d7da7976da24ae6daeb7ad
7
+ data.tar.gz: f4e6fbfe0cc8a16a4d1055234babbae6f0f381dc4a4a1d332c1127d589a2d19cd4732c9c6dba48752fafc2cacc872468c07896bbd2889a5447aa906117c8c0ac
@@ -1,9 +1,9 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- mutiny (0.2.8)
4
+ mutiny (0.3.0)
5
5
  gli (~> 2.13.0)
6
- metamorpher (~> 0.2.5)
6
+ metamorpher (~> 0.2.6)
7
7
  parser (~> 2.2.2)
8
8
  unparser (~> 0.2.4)
9
9
 
@@ -51,7 +51,7 @@ GEM
51
51
  json (1.8.3)
52
52
  memoizable (0.4.2)
53
53
  thread_safe (~> 0.3, >= 0.3.1)
54
- metamorpher (0.2.5)
54
+ metamorpher (0.2.6)
55
55
  attributable (~> 0.1.0)
56
56
  parser (~> 2.2.2)
57
57
  unparser (~> 0.2.4)
@@ -1,5 +1,11 @@
1
1
  # Release History
2
2
 
3
+ ## v0.3.0 (18 March 2016)
4
+ * Implement more sophisticated test filtering, which works well with targeted tests (e.g., "List#empty should have size 0") and with generic tests (e.g., "Users should be able to log out").
5
+ * Capture more forms of stillborn mutant, including those arising from instances of `ScriptError`.
6
+ * Fix bug in which mutation would be repeated if there was more than one subject in a single source file.
7
+ * Fix bugs in mutations of hash literals and array literals.
8
+
3
9
  ## v0.2.8 (22 February 2016)
4
10
  * Prevent insertion operators from incorrectly mutating private and protected keywords.
5
11
  * Fix bugs in mutating overlapping ASTs (by upgrading to metamorpher v0.2.5).
@@ -4,8 +4,12 @@ module Mutiny
4
4
  module Analysis
5
5
  class Analyser
6
6
  class Default < self
7
+ def before_all(mutant_set)
8
+ @subject_set = Subjects::SubjectSet.new(mutant_set.subjects)
9
+ end
10
+
7
11
  def select_tests(mutant)
8
- integration.tests.for(mutant.subject) # TODO: is this correctly minimal?
12
+ integration.tests.filterable(@subject_set).for(mutant.subject)
9
13
  end
10
14
  end
11
15
  end
@@ -7,8 +7,6 @@ module Mutiny
7
7
  # This code originally based on Markus Schirp's implementation of Mutant::Integration::Rspec
8
8
  # https://github.com/mbj/mutant/blob/master/lib/mutant/integration/rspec.rb
9
9
  class Parser
10
- EXPRESSION_DELIMITER = " "
11
-
12
10
  def initialize(context = Context.new)
13
11
  @world = context.world
14
12
  end
@@ -26,9 +24,9 @@ module Mutiny
26
24
  def parse_example(example)
27
25
  metadata = example.metadata
28
26
  location = metadata.fetch(:location)
29
- expression = metadata.fetch(:full_description).split(EXPRESSION_DELIMITER, 2).first
27
+ name = metadata.fetch(:full_description)
30
28
 
31
- Test.new(location: location, expression: expression, example: example)
29
+ Test.new(location: location, name: name, example: example)
32
30
  end
33
31
  end
34
32
  end
@@ -6,7 +6,7 @@ module Mutiny
6
6
  def run
7
7
  report "Checking..."
8
8
 
9
- if test_set.empty?
9
+ if relevant_test_set.empty?
10
10
  report_invalid
11
11
 
12
12
  elsif test_run.passed?
@@ -25,7 +25,7 @@ module Mutiny
25
25
  end
26
26
 
27
27
  def report_warning
28
- report " At least one relevant test found (#{test_set.size} in total)"
28
+ report " At least one relevant test found (#{relevant_test_set.size} in total)"
29
29
  report " Not all relevant tests passed. The failing tests are:\n"
30
30
 
31
31
  failed_test_locations.each do |location|
@@ -37,7 +37,7 @@ module Mutiny
37
37
  end
38
38
 
39
39
  def report_valid
40
- report " At least one relevant test found (#{test_set.size} in total)"
40
+ report " At least one relevant test found (#{relevant_test_set.size} in total)"
41
41
  report " All relevant tests passed"
42
42
  report "Looks good!"
43
43
  end
@@ -50,12 +50,16 @@ module Mutiny
50
50
  test_run.failed_tests.locations
51
51
  end
52
52
 
53
- def test_set
54
- @test_set ||= configuration.integration.tests.for_all(environment.subjects)
53
+ def relevant_test_set
54
+ @test_set ||= complete_test_set.for_all(environment.subjects)
55
+ end
56
+
57
+ def complete_test_set
58
+ @complete_test_set ||= configuration.integration.tests.filterable(environment.subjects)
55
59
  end
56
60
 
57
61
  def test_run
58
- @test_run ||= configuration.integration.run(test_set)
62
+ @test_run ||= configuration.integration.run(relevant_test_set)
59
63
  end
60
64
  end
61
65
  end
@@ -27,7 +27,7 @@ module Mutiny
27
27
  # rubocop:disable Eval
28
28
  eval(code, TOPLEVEL_BINDING)
29
29
  # rubocop:enable Eval
30
- rescue
30
+ rescue StandardError, ScriptError
31
31
  @stillborn = true
32
32
  end
33
33
 
@@ -12,7 +12,7 @@ module Mutiny
12
12
  end
13
13
 
14
14
  def subjects
15
- SubjectSet.new(modules.select(&:relevant?).map(&:to_subject))
15
+ SubjectSet.new(modules.select(&:relevant?).map(&:to_subject)).per_file
16
16
  end
17
17
 
18
18
  private
@@ -3,7 +3,8 @@ module Mutiny
3
3
  class SubjectSet
4
4
  include Enumerable
5
5
  extend Forwardable
6
- def_delegators :@subjects, :each, :product
6
+ attr_reader :subjects
7
+ def_delegators :@subjects, :each, :product, :hash
7
8
 
8
9
  def initialize(subjects)
9
10
  @subjects = subjects
@@ -12,6 +13,23 @@ module Mutiny
12
13
  def names
13
14
  @names ||= map(&:name).sort
14
15
  end
16
+
17
+ def [](index)
18
+ subjects.detect { |s| s.name == index }
19
+ end
20
+
21
+ # Returns a new SubjectSet which contains only one subject per source file
22
+ # For source files that contain more than one subject (i.e., Ruby module),
23
+ # the subjects are ordered by name alphabetically and only the first is used
24
+ def per_file
25
+ self.class.new(group_by(&:path).values.map { |subjects| subjects.sort_by(&:name).first })
26
+ end
27
+
28
+ def eql?(other)
29
+ is_a?(other.class) && other.subjects == subjects
30
+ end
31
+
32
+ alias_method "==", "eql?"
15
33
  end
16
34
  end
17
35
  end
@@ -1,11 +1,21 @@
1
1
  module Mutiny
2
2
  module Tests
3
3
  class Test
4
- attr_reader :location, :expression
4
+ attr_reader :location, :name
5
5
 
6
- def initialize(location: nil, expression:)
6
+ def initialize(location: nil, name:)
7
7
  @location = location
8
- @expression = expression
8
+ @name = name
9
+ end
10
+
11
+ def eql?(other)
12
+ other.location == location && other.name == name
13
+ end
14
+
15
+ alias_method "==", "eql?"
16
+
17
+ def hash
18
+ [location, name].hash
9
19
  end
10
20
  end
11
21
  end
@@ -1,4 +1,6 @@
1
1
  require "forwardable"
2
+ require_relative "test_set/filterable"
3
+ require_relative "test_set/filter/default"
2
4
 
3
5
  module Mutiny
4
6
  module Tests
@@ -18,31 +20,38 @@ module Mutiny
18
20
  tests.map(&:location)
19
21
  end
20
22
 
21
- def for_all(subject_set)
22
- subset { |test| subject_set.names.include?(test.expression) }
23
- end
24
-
25
- def for(subject)
26
- subset { |test| subject.name == test.expression }
27
- end
28
-
29
23
  def subset(&block)
30
- self.class.new(tests.select(&block))
24
+ derive(tests.select(&block))
31
25
  end
32
26
 
33
27
  def take(n)
34
- self.class.new(tests.take(n))
28
+ derive(tests.take(n))
35
29
  end
36
30
 
37
31
  def eql?(other)
38
32
  is_a?(other.class) && other.tests == tests
39
33
  end
40
34
 
35
+ def filterable(subjects, filtering_strategy: Filter::Default)
36
+ extend(Filterable)
37
+ self.filter = filtering_strategy.new(subject_names: subjects.names)
38
+ self
39
+ end
40
+
41
41
  alias_method "==", "eql?"
42
42
 
43
43
  protected
44
44
 
45
45
  attr_reader :tests
46
+
47
+ def derive(tests)
48
+ self.class.new(tests).tap do |derived|
49
+ if respond_to?(:filter)
50
+ derived.extend(Filterable)
51
+ derived.filter = filter
52
+ end
53
+ end
54
+ end
46
55
  end
47
56
  end
48
57
  end
@@ -0,0 +1,19 @@
1
+ module Mutiny
2
+ module Tests
3
+ class TestSet
4
+ class Filter
5
+ def initialize(subject_names:)
6
+ @subject_names = subject_names
7
+ end
8
+
9
+ def related?(subject_name:, test_name:)
10
+ fail "Subclasses must implement Filter#related? for #{subject_name}, #{test_name}"
11
+ end
12
+
13
+ protected
14
+
15
+ attr_reader :subject_names
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,29 @@
1
+ require_relative "../filter"
2
+
3
+ module Mutiny
4
+ module Tests
5
+ class TestSet
6
+ class Filter
7
+ class Default < self
8
+ def related?(subject_name:, test_name:)
9
+ general?(test_name) || specific_to?(test_name, subject_name)
10
+ end
11
+
12
+ private
13
+
14
+ # Returns true if the test is applicable to no specific subject
15
+ # (e.g., is an end-to-end test)
16
+ def general?(test_name)
17
+ subject_names.none? { |subject_name| test_name.start_with?(subject_name) }
18
+ end
19
+
20
+ # Returns true if the test is specific to the given subject
21
+ # (e.g., is a unit test for the specified subject)
22
+ def specific_to?(test_name, subject_name)
23
+ test_name.start_with?(subject_name)
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,26 @@
1
+ require "forwardable"
2
+ require_relative "filter/default"
3
+
4
+ module Mutiny
5
+ module Tests
6
+ class TestSet
7
+ module Filterable
8
+ attr_accessor :filter
9
+
10
+ def for_all(subject_set)
11
+ subset { |test| subject_set.any? { |subject| related?(subject, test) } }
12
+ end
13
+
14
+ def for(subject)
15
+ subset { |test| related?(subject, test) }
16
+ end
17
+
18
+ private
19
+
20
+ def related?(subject, test)
21
+ filter.related?(subject_name: subject.name, test_name: test.name)
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -1,3 +1,3 @@
1
1
  module Mutiny
2
- VERSION = "0.2.8"
2
+ VERSION = "0.3.0"
3
3
  end
@@ -21,7 +21,7 @@ Gem::Specification.new do |spec|
21
21
  spec.add_runtime_dependency "parser", "~> 2.2.2"
22
22
  spec.add_runtime_dependency "unparser", "~> 0.2.4"
23
23
  spec.add_runtime_dependency "gli", "~> 2.13.0"
24
- spec.add_runtime_dependency "metamorpher", "~> 0.2.5"
24
+ spec.add_runtime_dependency "metamorpher", "~> 0.2.6"
25
25
 
26
26
  spec.add_development_dependency "bundler", "~> 1.11"
27
27
  spec.add_development_dependency "rake", "~> 10.4.2"
@@ -0,0 +1,27 @@
1
+ module Mutiny
2
+ module Subjects
3
+ describe SubjectSet do
4
+ context "per_file" do
5
+ it "should contain only the alphabetically first subject when more than one in a file" do
6
+ first = Subject.new(name: "Bar", path: "main.rb")
7
+ second = Subject.new(name: "Foo", path: "main.rb")
8
+ nested = Subject.new(name: "Foo::Bar", path: "main.rb")
9
+
10
+ all_subjects = SubjectSet.new([first, second, nested])
11
+
12
+ expect(all_subjects.per_file).to eq(SubjectSet.new([first]))
13
+ end
14
+
15
+ it "should contain all subjects when there are one-per-file" do
16
+ first = Subject.new(name: "Bar", path: "bar.rb")
17
+ second = Subject.new(name: "Foo", path: "foo.rb")
18
+ nested = Subject.new(name: "Foo::Bar", path: "foo/bar.rb")
19
+
20
+ all_subjects = SubjectSet.new([first, second, nested])
21
+
22
+ expect(all_subjects.per_file).to eq(all_subjects)
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,32 @@
1
+ module Mutiny
2
+ module Tests
3
+ class TestSet
4
+ class Filter
5
+ describe Default do
6
+ subject { Default.new(subject_names: %w(Max Min)) }
7
+
8
+ it "should return true when the test and subject have the same name" do
9
+ expect(subject.related?(subject_name: "Max", test_name: "Max")).to be_truthy
10
+ end
11
+
12
+ it "should return true when the test is for a specific method in the subject" do
13
+ expect(subject.related?(subject_name: "Max", test_name: "Max.run")).to be_truthy
14
+ end
15
+
16
+ it "should return true when the test is for a specific class method in the subject" do
17
+ expect(subject.related?(subject_name: "Max", test_name: "Max#run")).to be_truthy
18
+ end
19
+
20
+ it "should return false when the test and subject have different names" do
21
+ expect(subject.related?(subject_name: "Min", test_name: "Max")).to be_falsey
22
+ end
23
+
24
+ it "should return true when the test does not begin with a known subject" do
25
+ expect(subject.related?(subject_name: "Max", test_name: "system test")).to be_truthy
26
+ expect(subject.related?(subject_name: "Min", test_name: "system test")).to be_truthy
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,61 @@
1
+ module Mutiny
2
+ module Tests
3
+ class TestSet
4
+ describe Filterable do
5
+ context "for" do
6
+ let(:subjects) { subject_set_for("Max", "Min") }
7
+ let(:test_set) { test_set_for("Min", "Min#run", subjects: subjects) }
8
+
9
+ it "should return only those tests that are relevant to the subject" do
10
+ expected = test_set_for("Min", "Min#run", subjects: subjects)
11
+ expect(test_set.for(subjects["Min"])).to eq(expected)
12
+ end
13
+
14
+ it "should return no tests if none are relevant to the subject" do
15
+ expect(test_set.for(subjects["Max"])).to eq(TestSet.empty)
16
+ end
17
+ end
18
+
19
+ context "for all" do
20
+ let(:subjects) { subject_set_for("Max", "Min") }
21
+ let(:test_set) { test_set_for("Min", "Min#run", subjects: subjects) }
22
+
23
+ it "should remove irrelevant tests" do
24
+ expected = test_set.subset { |t| t.name != "Subtract" }
25
+
26
+ expect(test_set.for_all(subjects)).to eq(expected)
27
+ end
28
+
29
+ it "should return all relevant tests" do
30
+ expected = test_set.subset { |t| t.name.start_with?("Min") }
31
+
32
+ expect(test_set.for_all(subjects)).to eq(expected)
33
+ end
34
+
35
+ it "should return no tests when there are no tests" do
36
+ expect(TestSet.empty.filterable(subjects).for_all(subjects)).to eq(TestSet.empty)
37
+ end
38
+ end
39
+
40
+ def subject_set_for(*names)
41
+ Subjects::SubjectSet.new(names.map { |n| Subjects::Subject.new(name: n) })
42
+ end
43
+
44
+ def test_set_for(*expressions, subjects:)
45
+ TestSet.new(tests_for(*expressions))
46
+ .filterable(subjects, filtering_strategy: DummyStrategy)
47
+ end
48
+
49
+ def tests_for(*names)
50
+ names.map { |name| Test.new(name: name) }
51
+ end
52
+
53
+ class DummyStrategy < Filter
54
+ def related?(subject_name:, test_name:)
55
+ test_name.start_with?(subject_name)
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,37 @@
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
+ end
36
+ end
37
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mutiny
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.8
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Louis Rose
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-03-12 00:00:00.000000000 Z
11
+ date: 2016-03-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: parser
@@ -58,14 +58,14 @@ dependencies:
58
58
  requirements:
59
59
  - - "~>"
60
60
  - !ruby/object:Gem::Version
61
- version: 0.2.5
61
+ version: 0.2.6
62
62
  type: :runtime
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
66
  - - "~>"
67
67
  - !ruby/object:Gem::Version
68
- version: 0.2.5
68
+ version: 0.2.6
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: bundler
71
71
  requirement: !ruby/object:Gem::Requirement
@@ -248,6 +248,9 @@ files:
248
248
  - lib/mutiny/tests/test.rb
249
249
  - lib/mutiny/tests/test_run.rb
250
250
  - lib/mutiny/tests/test_set.rb
251
+ - lib/mutiny/tests/test_set/filter.rb
252
+ - lib/mutiny/tests/test_set/filter/default.rb
253
+ - lib/mutiny/tests/test_set/filterable.rb
251
254
  - lib/mutiny/version.rb
252
255
  - mutiny.gemspec
253
256
  - spec/integration/check_spec.rb
@@ -284,8 +287,11 @@ files:
284
287
  - spec/unit/pattern_spec.rb
285
288
  - spec/unit/subjects/environment/type_spec.rb
286
289
  - spec/unit/subjects/environment_spec.rb
290
+ - spec/unit/subjects/subject_set_spec.rb
287
291
  - spec/unit/subjects/subject_spec.rb
288
- - spec/unit/subjects/test_set_spec.rb
292
+ - spec/unit/tests/test_set/filter/default_spec.rb
293
+ - spec/unit/tests/test_set/filterable_spec.rb
294
+ - spec/unit/tests/test_set_spec.rb
289
295
  homepage: https://github.com/mutiny/mutiny
290
296
  licenses:
291
297
  - MIT
@@ -345,5 +351,8 @@ test_files:
345
351
  - spec/unit/pattern_spec.rb
346
352
  - spec/unit/subjects/environment/type_spec.rb
347
353
  - spec/unit/subjects/environment_spec.rb
354
+ - spec/unit/subjects/subject_set_spec.rb
348
355
  - spec/unit/subjects/subject_spec.rb
349
- - spec/unit/subjects/test_set_spec.rb
356
+ - spec/unit/tests/test_set/filter/default_spec.rb
357
+ - spec/unit/tests/test_set/filterable_spec.rb
358
+ - spec/unit/tests/test_set_spec.rb
@@ -1,75 +0,0 @@
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
- subjects = subject_set_for("Max", "Min")
39
- test_set = test_set_for("Subtract", "Min", "Add")
40
-
41
- expect(test_set.for_all(subjects)).to eq(test_set.subset { |t| t.expression == "Min" })
42
- end
43
-
44
- it "should return multiple tests for a single subject" do
45
- subjects = subject_set_for("Min")
46
- test_set = test_set_for("Min", "Max", "Min", "Max", "Min")
47
-
48
- expect(test_set.for_all(subjects)).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
- subjects = subject_set_for("Max", "Min")
53
- test_set = TestSet.empty
54
-
55
- expect(test_set.for_all(subjects)).to eq(TestSet.empty)
56
- end
57
-
58
- it "should return no tests when there are no relevant subjects" do
59
- subjects = subject_set_for("Max", "Min")
60
- test_set = test_set_for("Subtract", "Add")
61
-
62
- expect(test_set.for_all(subjects)).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