mutant 0.8.0 → 0.8.1

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 (187) hide show
  1. checksums.yaml +4 -4
  2. data/Changelog.md +7 -0
  3. data/config/flay.yml +1 -1
  4. data/config/reek.yml +1 -0
  5. data/lib/mutant.rb +10 -3
  6. data/lib/mutant/actor.rb +2 -5
  7. data/lib/mutant/actor/env.rb +1 -3
  8. data/lib/mutant/actor/mailbox.rb +2 -4
  9. data/lib/mutant/actor/receiver.rb +0 -2
  10. data/lib/mutant/actor/sender.rb +0 -1
  11. data/lib/mutant/ast.rb +22 -28
  12. data/lib/mutant/ast/meta.rb +8 -88
  13. data/lib/mutant/ast/named_children.rb +1 -8
  14. data/lib/mutant/ast/sexp.rb +0 -2
  15. data/lib/mutant/cache.rb +1 -3
  16. data/lib/mutant/cli.rb +9 -19
  17. data/lib/mutant/color.rb +0 -3
  18. data/lib/mutant/config.rb +6 -2
  19. data/lib/mutant/context.rb +2 -4
  20. data/lib/mutant/context/scope.rb +10 -16
  21. data/lib/mutant/delegator.rb +0 -3
  22. data/lib/mutant/diff.rb +8 -17
  23. data/lib/mutant/env.rb +3 -5
  24. data/lib/mutant/env/bootstrap.rb +32 -39
  25. data/lib/mutant/expression.rb +14 -132
  26. data/lib/mutant/expression/method.rb +25 -42
  27. data/lib/mutant/expression/methods.rb +17 -29
  28. data/lib/mutant/expression/namespace.rb +33 -28
  29. data/lib/mutant/expression/parser.rb +71 -0
  30. data/lib/mutant/integration.rb +17 -16
  31. data/lib/mutant/isolation.rb +14 -14
  32. data/lib/mutant/loader.rb +2 -4
  33. data/lib/mutant/matcher.rb +1 -11
  34. data/lib/mutant/matcher/chain.rb +0 -1
  35. data/lib/mutant/matcher/compiler.rb +19 -52
  36. data/lib/mutant/matcher/config.rb +65 -5
  37. data/lib/mutant/matcher/filter.rb +11 -1
  38. data/lib/mutant/matcher/method.rb +11 -21
  39. data/lib/mutant/matcher/method/instance.rb +2 -16
  40. data/lib/mutant/matcher/method/singleton.rb +3 -20
  41. data/lib/mutant/matcher/methods.rb +11 -21
  42. data/lib/mutant/matcher/namespace.rb +0 -4
  43. data/lib/mutant/matcher/null.rb +0 -1
  44. data/lib/mutant/matcher/scope.rb +0 -12
  45. data/lib/mutant/meta.rb +0 -1
  46. data/lib/mutant/meta/example.rb +12 -26
  47. data/lib/mutant/meta/example/dsl.rb +1 -8
  48. data/lib/mutant/mutation.rb +6 -14
  49. data/lib/mutant/mutator.rb +2 -14
  50. data/lib/mutant/mutator/node.rb +6 -33
  51. data/lib/mutant/mutator/node/and_asgn.rb +0 -1
  52. data/lib/mutant/mutator/node/argument.rb +0 -5
  53. data/lib/mutant/mutator/node/arguments.rb +1 -7
  54. data/lib/mutant/mutator/node/begin.rb +0 -2
  55. data/lib/mutant/mutator/node/binary.rb +0 -3
  56. data/lib/mutant/mutator/node/block.rb +0 -2
  57. data/lib/mutant/mutator/node/break.rb +0 -1
  58. data/lib/mutant/mutator/node/case.rb +0 -3
  59. data/lib/mutant/mutator/node/conditional_loop.rb +0 -1
  60. data/lib/mutant/mutator/node/const.rb +0 -1
  61. data/lib/mutant/mutator/node/define.rb +0 -1
  62. data/lib/mutant/mutator/node/defined.rb +0 -1
  63. data/lib/mutant/mutator/node/dstr.rb +0 -1
  64. data/lib/mutant/mutator/node/dsym.rb +0 -1
  65. data/lib/mutant/mutator/node/generic.rb +0 -1
  66. data/lib/mutant/mutator/node/if.rb +0 -4
  67. data/lib/mutant/mutator/node/kwbegin.rb +0 -1
  68. data/lib/mutant/mutator/node/literal/array.rb +0 -2
  69. data/lib/mutant/mutator/node/literal/boolean.rb +0 -1
  70. data/lib/mutant/mutator/node/literal/fixnum.rb +2 -5
  71. data/lib/mutant/mutator/node/literal/float.rb +1 -4
  72. data/lib/mutant/mutator/node/literal/hash.rb +0 -3
  73. data/lib/mutant/mutator/node/literal/nil.rb +0 -1
  74. data/lib/mutant/mutator/node/literal/range.rb +1 -5
  75. data/lib/mutant/mutator/node/literal/regex.rb +1 -3
  76. data/lib/mutant/mutator/node/literal/string.rb +0 -1
  77. data/lib/mutant/mutator/node/literal/symbol.rb +0 -1
  78. data/lib/mutant/mutator/node/masgn.rb +0 -1
  79. data/lib/mutant/mutator/node/match_current_line.rb +0 -1
  80. data/lib/mutant/mutator/node/mlhs.rb +0 -1
  81. data/lib/mutant/mutator/node/named_value/access.rb +0 -1
  82. data/lib/mutant/mutator/node/named_value/constant_assignment.rb +0 -2
  83. data/lib/mutant/mutator/node/named_value/variable_assignment.rb +0 -2
  84. data/lib/mutant/mutator/node/next.rb +0 -1
  85. data/lib/mutant/mutator/node/noop.rb +0 -1
  86. data/lib/mutant/mutator/node/nthref.rb +0 -1
  87. data/lib/mutant/mutator/node/op_asgn.rb +0 -1
  88. data/lib/mutant/mutator/node/or_asgn.rb +1 -2
  89. data/lib/mutant/mutator/node/resbody.rb +0 -2
  90. data/lib/mutant/mutator/node/rescue.rb +1 -6
  91. data/lib/mutant/mutator/node/return.rb +0 -1
  92. data/lib/mutant/mutator/node/send.rb +16 -17
  93. data/lib/mutant/mutator/node/send/attribute_assignment.rb +0 -3
  94. data/lib/mutant/mutator/node/send/binary.rb +0 -1
  95. data/lib/mutant/mutator/node/send/index.rb +0 -3
  96. data/lib/mutant/mutator/node/splat.rb +0 -1
  97. data/lib/mutant/mutator/node/super.rb +0 -1
  98. data/lib/mutant/mutator/node/when.rb +2 -7
  99. data/lib/mutant/mutator/node/yield.rb +0 -1
  100. data/lib/mutant/mutator/node/zsuper.rb +0 -1
  101. data/lib/mutant/mutator/registry.rb +1 -4
  102. data/lib/mutant/mutator/util.rb +0 -2
  103. data/lib/mutant/mutator/util/array.rb +0 -3
  104. data/lib/mutant/mutator/util/symbol.rb +0 -1
  105. data/lib/mutant/parallel.rb +3 -9
  106. data/lib/mutant/parallel/master.rb +7 -17
  107. data/lib/mutant/parallel/source.rb +2 -7
  108. data/lib/mutant/parallel/worker.rb +1 -5
  109. data/lib/mutant/reporter.rb +0 -4
  110. data/lib/mutant/reporter/cli.rb +1 -8
  111. data/lib/mutant/reporter/cli/format.rb +7 -18
  112. data/lib/mutant/reporter/cli/printer.rb +2 -12
  113. data/lib/mutant/reporter/cli/printer/config.rb +1 -4
  114. data/lib/mutant/reporter/cli/printer/env_progress.rb +3 -7
  115. data/lib/mutant/reporter/cli/printer/env_result.rb +0 -1
  116. data/lib/mutant/reporter/cli/printer/mutation_progress_result.rb +0 -2
  117. data/lib/mutant/reporter/cli/printer/mutation_result.rb +3 -11
  118. data/lib/mutant/reporter/cli/printer/status.rb +1 -4
  119. data/lib/mutant/reporter/cli/printer/status_progressive.rb +1 -3
  120. data/lib/mutant/reporter/cli/printer/subject_progress.rb +0 -5
  121. data/lib/mutant/reporter/cli/printer/subject_result.rb +0 -1
  122. data/lib/mutant/reporter/cli/printer/test_result.rb +0 -1
  123. data/lib/mutant/reporter/cli/tput.rb +4 -1
  124. data/lib/mutant/reporter/trace.rb +2 -4
  125. data/lib/mutant/repository.rb +88 -0
  126. data/lib/mutant/require_highjack.rb +0 -1
  127. data/lib/mutant/result.rb +25 -48
  128. data/lib/mutant/runner.rb +7 -16
  129. data/lib/mutant/runner/sink.rb +3 -11
  130. data/lib/mutant/selector.rb +1 -2
  131. data/lib/mutant/selector/expression.rb +1 -2
  132. data/lib/mutant/subject.rb +10 -21
  133. data/lib/mutant/subject/method.rb +11 -12
  134. data/lib/mutant/subject/method/instance.rb +1 -5
  135. data/lib/mutant/subject/method/singleton.rb +1 -3
  136. data/lib/mutant/test.rb +1 -2
  137. data/lib/mutant/version.rb +2 -2
  138. data/lib/mutant/warning_filter.rb +2 -7
  139. data/lib/mutant/zombifier.rb +6 -10
  140. data/meta/send.rb +8 -0
  141. data/spec/spec_helper.rb +5 -1
  142. data/spec/support/corpus.rb +5 -9
  143. data/spec/support/rb_bug.rb +0 -1
  144. data/spec/support/rspec.rb +0 -1
  145. data/spec/support/ruby_vm.rb +0 -2
  146. data/spec/unit/mutant/ast/meta/send_spec.rb +42 -0
  147. data/spec/unit/mutant/ast/named_children_spec.rb +51 -0
  148. data/spec/unit/mutant/ast/sexp_spec.rb +36 -0
  149. data/spec/unit/mutant/ast_spec.rb +8 -0
  150. data/spec/unit/mutant/cli_spec.rb +34 -19
  151. data/spec/unit/mutant/context/scope_spec.rb +11 -0
  152. data/spec/unit/mutant/env/boostrap_spec.rb +5 -2
  153. data/spec/unit/mutant/env_spec.rb +14 -15
  154. data/spec/unit/mutant/expression/method_spec.rb +10 -14
  155. data/spec/unit/mutant/expression/methods_spec.rb +24 -11
  156. data/spec/unit/mutant/expression/namespace/flat_spec.rb +5 -6
  157. data/spec/unit/mutant/expression/namespace/recursive_spec.rb +16 -10
  158. data/spec/unit/mutant/expression/parser_spec.rb +67 -0
  159. data/spec/unit/mutant/expression_spec.rb +24 -57
  160. data/spec/unit/mutant/integration/rspec_spec.rb +7 -7
  161. data/spec/unit/mutant/integration_spec.rb +2 -2
  162. data/spec/unit/mutant/isolation_spec.rb +31 -29
  163. data/spec/unit/mutant/matcher/compiler/subject_prefix_spec.rb +2 -2
  164. data/spec/unit/mutant/matcher/compiler_spec.rb +27 -58
  165. data/spec/unit/mutant/matcher/config_spec.rb +45 -0
  166. data/spec/unit/mutant/matcher/filter_spec.rb +12 -5
  167. data/spec/unit/mutant/matcher/method/instance_spec.rb +0 -1
  168. data/spec/unit/mutant/matcher/method/singleton_spec.rb +0 -1
  169. data/spec/unit/mutant/matcher/namespace_spec.rb +4 -4
  170. data/spec/unit/mutant/parallel/worker_spec.rb +1 -1
  171. data/spec/unit/mutant/reporter/cli/printer/config_spec.rb +4 -4
  172. data/spec/unit/mutant/reporter/cli/printer/env_progress_spec.rb +7 -7
  173. data/spec/unit/mutant/reporter/cli/printer/env_result_spec.rb +2 -2
  174. data/spec/unit/mutant/reporter/cli/printer/status_progressive_spec.rb +2 -2
  175. data/spec/unit/mutant/reporter/cli/printer/status_spec.rb +12 -12
  176. data/spec/unit/mutant/reporter/cli/printer/subject_progress_spec.rb +1 -1
  177. data/spec/unit/mutant/reporter/cli_spec.rb +9 -10
  178. data/spec/unit/mutant/repository/diff_spec.rb +80 -0
  179. data/spec/unit/mutant/repository/subject_filter_spec.rb +28 -0
  180. data/spec/unit/mutant/result/env_spec.rb +1 -1
  181. data/spec/unit/mutant/runner_spec.rb +0 -1
  182. data/spec/unit/mutant/selector/expression_spec.rb +14 -14
  183. data/spec/unit/mutant/subject/method/instance_spec.rb +2 -2
  184. data/spec/unit/mutant/subject/method/singleton_spec.rb +2 -2
  185. data/spec/unit/mutant/subject_spec.rb +5 -2
  186. metadata +20 -3
  187. data/spec/support/mutation_verifier.rb +0 -96
@@ -0,0 +1,11 @@
1
+ RSpec.describe Mutant::Context::Scope do
2
+ let(:object) { described_class.new(scope, source_path) }
3
+ let(:scope) { double('scope', name: double('name')) }
4
+ let(:source_path) { double('source path') }
5
+
6
+ describe '#identification' do
7
+ subject { object.identification }
8
+
9
+ it { should be(scope.name) }
10
+ end
11
+ end
@@ -9,6 +9,8 @@ RSpec.describe Mutant::Env::Bootstrap do
9
9
  )
10
10
  end
11
11
 
12
+ let(:integration) { Mutant::Integration::Null.new(config) }
13
+
12
14
  let(:expected_env) do
13
15
  Mutant::Env.new(
14
16
  cache: Mutant::Cache.new,
@@ -16,8 +18,9 @@ RSpec.describe Mutant::Env::Bootstrap do
16
18
  matchable_scopes: [],
17
19
  mutations: [],
18
20
  config: config,
19
- selector: Mutant::Selector::Expression.new(config.integration),
20
- actor_env: Mutant::Actor::Env.new(Thread)
21
+ selector: Mutant::Selector::Expression.new(integration),
22
+ actor_env: Mutant::Actor::Env.new(Thread),
23
+ integration: integration
21
24
  )
22
25
  end
23
26
 
@@ -8,26 +8,26 @@ RSpec.describe Mutant::Env do
8
8
  selector: selector,
9
9
  subjects: [],
10
10
  mutations: [],
11
- matchable_scopes: []
11
+ matchable_scopes: [],
12
+ integration: integration
12
13
  )
13
14
  end
14
15
 
16
+ let(:integration) { integration_class.new(config) }
17
+
15
18
  let(:config) do
16
- Mutant::Config::DEFAULT.update(
17
- isolation: isolation,
18
- integration: integration
19
- )
19
+ Mutant::Config::DEFAULT.update(isolation: isolation, integration: integration_class)
20
20
  end
21
21
 
22
- let(:integration) { double('Integration') }
23
- let(:isolation) { double('Isolation') }
24
- let(:mutation) { Mutant::Mutation::Evil.new(mutation_subject, Mutant::AST::Nodes::N_NIL) }
25
- let(:wrapped_node) { double('Wrapped Node') }
26
- let(:context) { double('Context') }
27
- let(:test_a) { double('Test A') }
28
- let(:test_b) { double('Test B') }
29
- let(:tests) { [test_a, test_b] }
30
- let(:selector) { double('Selector') }
22
+ let(:isolation) { double('Isolation') }
23
+ let(:mutation) { Mutant::Mutation::Evil.new(mutation_subject, Mutant::AST::Nodes::N_NIL) }
24
+ let(:wrapped_node) { double('Wrapped Node') }
25
+ let(:context) { double('Context') }
26
+ let(:test_a) { double('Test A') }
27
+ let(:test_b) { double('Test B') }
28
+ let(:tests) { [test_a, test_b] }
29
+ let(:selector) { double('Selector') }
30
+ let(:integration_class) { Mutant::Integration::Null }
31
31
 
32
32
  let(:mutation_subject) do
33
33
  double(
@@ -59,7 +59,6 @@ RSpec.describe Mutant::Env do
59
59
  expect(mutation_subject).to receive(:prepare).and_return(mutation_subject).ordered
60
60
  expect(context).to receive(:root).with(s(:nil)).and_return(wrapped_node).ordered
61
61
  expect(Mutant::Loader::Eval).to receive(:call).with(wrapped_node, mutation_subject).and_return(nil).ordered
62
- expect(integration).to receive(:call).with(tests).and_return(test_result).ordered
63
62
  end
64
63
 
65
64
  include_examples 'mutation kill'
@@ -1,9 +1,8 @@
1
1
  RSpec.describe Mutant::Expression::Method do
2
-
3
- let(:object) { described_class.parse(input) }
4
- let(:env) { Fixtures::TEST_ENV }
5
- let(:instance_method) { 'TestApp::Literal#string' }
6
- let(:singleton_method) { 'TestApp::Literal.string' }
2
+ let(:object) { parse_expression(input) }
3
+ let(:env) { Fixtures::TEST_ENV }
4
+ let(:instance_method) { 'TestApp::Literal#string' }
5
+ let(:singleton_method) { 'TestApp::Literal.string' }
7
6
 
8
7
  describe '#match_length' do
9
8
  let(:input) { instance_method }
@@ -11,13 +10,13 @@ RSpec.describe Mutant::Expression::Method do
11
10
  subject { object.match_length(other) }
12
11
 
13
12
  context 'when other is an equivalent expression' do
14
- let(:other) { described_class.parse(object.syntax) }
13
+ let(:other) { parse_expression(object.syntax) }
15
14
 
16
15
  it { should be(object.syntax.length) }
17
16
  end
18
17
 
19
18
  context 'when other is an unequivalent expression' do
20
- let(:other) { described_class.parse('Foo*') }
19
+ let(:other) { parse_expression('Foo*') }
21
20
 
22
21
  it { should be(0) }
23
22
  end
@@ -30,19 +29,16 @@ RSpec.describe Mutant::Expression::Method do
30
29
  let(:input) { instance_method }
31
30
 
32
31
  it 'returns correct matcher' do
33
- should eql(
34
- Mutant::Matcher::Method::Instance.new(
35
- env,
36
- TestApp::Literal, TestApp::Literal.instance_method(:string)
37
- )
38
- )
32
+ expect(subject.map(&:expression)).to eql([object])
39
33
  end
40
34
  end
41
35
 
42
36
  context 'with a singleton method' do
43
37
  let(:input) { singleton_method }
44
38
 
45
- it { should eql(Mutant::Matcher::Method::Singleton.new(env, TestApp::Literal, TestApp::Literal.method(:string))) }
39
+ it 'returns correct matcher' do
40
+ expect(subject.map(&:expression)).to eql([object])
41
+ end
46
42
  end
47
43
  end
48
44
  end
@@ -1,45 +1,58 @@
1
1
  RSpec.describe Mutant::Expression::Methods do
2
-
3
- let(:object) { described_class.parse(input) }
4
- let(:env) { Fixtures::TEST_ENV }
5
- let(:instance_methods) { 'TestApp::Literal#' }
6
- let(:singleton_methods) { 'TestApp::Literal.' }
2
+ let(:env) { Fixtures::TEST_ENV }
3
+ let(:object) { described_class.new(attributes) }
7
4
 
8
5
  describe '#match_length' do
9
- let(:input) { instance_methods }
6
+ let(:attributes) { { scope_name: 'TestApp::Literal', scope_symbol: '#' } }
10
7
 
11
8
  subject { object.match_length(other) }
12
9
 
13
10
  context 'when other is an equivalent expression' do
14
- let(:other) { described_class.parse(object.syntax) }
11
+ let(:other) { parse_expression(object.syntax) }
15
12
 
16
13
  it { should be(object.syntax.length) }
17
14
  end
18
15
 
19
16
  context 'when other is matched' do
20
- let(:other) { described_class.parse('TestApp::Literal#foo') }
17
+ let(:other) { parse_expression('TestApp::Literal#foo') }
21
18
 
22
19
  it { should be(object.syntax.length) }
23
20
  end
24
21
 
25
22
  context 'when other is an not matched expression' do
26
- let(:other) { described_class.parse('Foo*') }
23
+ let(:other) { parse_expression('Foo*') }
27
24
 
28
25
  it { should be(0) }
29
26
  end
30
27
  end
31
28
 
29
+ describe '#syntax' do
30
+ subject { object.syntax }
31
+
32
+ context 'with an instance method' do
33
+ let(:attributes) { { scope_name: 'TestApp::Literal', scope_symbol: '#' } }
34
+
35
+ it { should eql('TestApp::Literal#') }
36
+ end
37
+
38
+ context 'with a singleton method' do
39
+ let(:attributes) { { scope_name: 'TestApp::Literal', scope_symbol: '.' } }
40
+
41
+ it { should eql('TestApp::Literal.') }
42
+ end
43
+ end
44
+
32
45
  describe '#matcher' do
33
46
  subject { object.matcher(env) }
34
47
 
35
48
  context 'with an instance method' do
36
- let(:input) { instance_methods }
49
+ let(:attributes) { { scope_name: 'TestApp::Literal', scope_symbol: '#' } }
37
50
 
38
51
  it { should eql(Mutant::Matcher::Methods::Instance.new(env, TestApp::Literal)) }
39
52
  end
40
53
 
41
54
  context 'with a singleton method' do
42
- let(:input) { singleton_methods }
55
+ let(:attributes) { { scope_name: 'TestApp::Literal', scope_symbol: '.' } }
43
56
 
44
57
  it { should eql(Mutant::Matcher::Methods::Singleton.new(env, TestApp::Literal)) }
45
58
  end
@@ -1,8 +1,7 @@
1
1
  RSpec.describe Mutant::Expression::Namespace::Exact do
2
-
3
- let(:object) { described_class.parse(input) }
4
- let(:env) { Fixtures::TEST_ENV }
5
- let(:input) { 'TestApp::Literal' }
2
+ let(:object) { parse_expression(input) }
3
+ let(:env) { Fixtures::TEST_ENV }
4
+ let(:input) { 'TestApp::Literal' }
6
5
 
7
6
  describe '#matcher' do
8
7
  subject { object.matcher(env) }
@@ -14,13 +13,13 @@ RSpec.describe Mutant::Expression::Namespace::Exact do
14
13
  subject { object.match_length(other) }
15
14
 
16
15
  context 'when other is an equivalent expression' do
17
- let(:other) { described_class.parse(object.syntax) }
16
+ let(:other) { parse_expression(object.syntax) }
18
17
 
19
18
  it { should be(object.syntax.length) }
20
19
  end
21
20
 
22
21
  context 'when other is an unequivalent expression' do
23
- let(:other) { described_class.parse('Foo*') }
22
+ let(:other) { parse_expression('Foo*') }
24
23
 
25
24
  it { should be(0) }
26
25
  end
@@ -1,8 +1,8 @@
1
1
  RSpec.describe Mutant::Expression::Namespace::Recursive do
2
2
 
3
- let(:object) { described_class.parse(input) }
4
- let(:input) { 'TestApp::Literal*' }
5
- let(:env) { Fixtures::TEST_ENV }
3
+ let(:object) { parse_expression(input) }
4
+ let(:input) { 'TestApp::Literal*' }
5
+ let(:env) { Fixtures::TEST_ENV }
6
6
 
7
7
  describe '#matcher' do
8
8
  subject { object.matcher(env) }
@@ -10,48 +10,54 @@ RSpec.describe Mutant::Expression::Namespace::Recursive do
10
10
  it { should eql(Mutant::Matcher::Namespace.new(env, object)) }
11
11
  end
12
12
 
13
+ describe '#syntax' do
14
+ subject { object.syntax }
15
+
16
+ it { should eql(input) }
17
+ end
18
+
13
19
  describe '#match_length' do
14
20
  subject { object.match_length(other) }
15
21
 
16
22
  context 'when other is an equivalent expression' do
17
- let(:other) { described_class.parse(object.syntax) }
23
+ let(:other) { parse_expression(object.syntax) }
18
24
 
19
25
  it { should be(0) }
20
26
  end
21
27
 
22
28
  context 'when other expression describes a shorter prefix' do
23
- let(:other) { described_class.parse('TestApp') }
29
+ let(:other) { parse_expression('TestApp') }
24
30
 
25
31
  it { should be(0) }
26
32
  end
27
33
 
28
34
  context 'when other expression describes adjacent namespace' do
29
- let(:other) { described_class.parse('TestApp::LiteralFoo') }
35
+ let(:other) { parse_expression('TestApp::LiteralFoo') }
30
36
 
31
37
  it { should be(0) }
32
38
  end
33
39
 
34
40
  context 'when other expression describes root namespace' do
35
- let(:other) { described_class.parse('TestApp::Literal') }
41
+ let(:other) { parse_expression('TestApp::Literal') }
36
42
 
37
43
  it { should be(16) }
38
44
  end
39
45
 
40
46
  context 'when other expression describes a longer prefix' do
41
47
  context 'on constants' do
42
- let(:other) { described_class.parse('TestApp::Literal::Deep') }
48
+ let(:other) { parse_expression('TestApp::Literal::Deep') }
43
49
 
44
50
  it { should be(input[0..-2].length) }
45
51
  end
46
52
 
47
53
  context 'on singleton method' do
48
- let(:other) { described_class.parse('TestApp::Literal.foo') }
54
+ let(:other) { parse_expression('TestApp::Literal.foo') }
49
55
 
50
56
  it { should be(input[0..-2].length) }
51
57
  end
52
58
 
53
59
  context 'on instance method' do
54
- let(:other) { described_class.parse('TestApp::Literal#foo') }
60
+ let(:other) { parse_expression('TestApp::Literal#foo') }
55
61
 
56
62
  it { should be(input[0..-2].length) }
57
63
  end
@@ -0,0 +1,67 @@
1
+ RSpec.describe Mutant::Expression::Parser do
2
+ let(:object) { Mutant::Config::DEFAULT.expression_parser }
3
+
4
+ describe '#call' do
5
+ subject { object.call(input) }
6
+
7
+ context 'on nonsense' do
8
+ let(:input) { 'foo bar' }
9
+
10
+ it 'raises an exception' do
11
+ expect { subject }.to raise_error(
12
+ Mutant::Expression::Parser::InvalidExpressionError,
13
+ 'Expression: "foo bar" is not valid'
14
+ )
15
+ end
16
+ end
17
+
18
+ context 'on a valid expression' do
19
+ let(:input) { 'Foo' }
20
+
21
+ it { should eql(Mutant::Expression::Namespace::Exact.new(scope_name: 'Foo')) }
22
+ end
23
+ end
24
+
25
+ describe '.try_parse' do
26
+ subject { object.try_parse(input) }
27
+
28
+ context 'on nonsense' do
29
+ let(:input) { 'foo bar' }
30
+
31
+ it { should be(nil) }
32
+ end
33
+
34
+ context 'on a valid expression' do
35
+ let(:input) { 'Foo' }
36
+
37
+ it { should eql(Mutant::Expression::Namespace::Exact.new(scope_name: 'Foo')) }
38
+ end
39
+
40
+ context 'on ambiguous expression' do
41
+ let(:object) { described_class.new([test_a, test_b]) }
42
+
43
+ let(:test_a) do
44
+ Class.new(Mutant::Expression) do
45
+ include Anima.new
46
+ const_set(:REGEXP, /\Atest-syntax\z/.freeze)
47
+ end
48
+ end
49
+
50
+ let(:test_b) do
51
+ Class.new(Mutant::Expression) do
52
+ include Anima.new
53
+ const_set(:REGEXP, /^test-syntax$/.freeze)
54
+ end
55
+ end
56
+
57
+ let(:input) { 'test-syntax' }
58
+
59
+ it 'raises expected exception' do
60
+ expect { subject }.to raise_error(
61
+ Mutant::Expression::Parser::AmbiguousExpressionError,
62
+ 'Ambiguous expression: "test-syntax"'
63
+ )
64
+ end
65
+ end
66
+ end
67
+ end
@@ -1,80 +1,47 @@
1
1
  RSpec.describe Mutant::Expression do
2
2
  let(:object) { described_class }
3
3
 
4
- describe '.try_parse' do
5
- subject { object.try_parse(input) }
4
+ let(:parser) { Mutant::Config::DEFAULT.expression_parser }
6
5
 
7
- context 'on nonsense' do
8
- let(:input) { 'foo bar' }
6
+ describe '#prefix?' do
7
+ let(:object) { parser.call('Foo*') }
9
8
 
10
- it { should be(nil) }
11
- end
9
+ subject { object.prefix?(other) }
12
10
 
13
- context 'on a valid expression' do
14
- let(:input) { 'Foo' }
11
+ context 'when object is a prefix of other' do
12
+ let(:other) { parser.call('Foo::Bar') }
15
13
 
16
- it { should eql(Mutant::Expression::Namespace::Exact.new('Foo')) }
14
+ it { should be(true) }
17
15
  end
18
16
 
19
- context 'on ambiguous expression' do
20
- class ExpressionA < Mutant::Expression
21
- register(/\Atest-syntax\z/)
22
- end
17
+ context 'when other is not a prefix of other' do
18
+ let(:other) { parser.call('Bar') }
23
19
 
24
- class ExpressionB < Mutant::Expression
25
- register(/^test-syntax$/)
26
- end
27
-
28
- let(:input) { 'test-syntax' }
29
-
30
- it 'raises an exception' do
31
- expect { subject }.to raise_error(
32
- Mutant::Expression::AmbiguousExpressionError,
33
- 'Ambiguous expression: "test-syntax"'
34
- )
35
- end
20
+ it { should be(false) }
36
21
  end
37
22
  end
38
23
 
39
- describe '#inspect' do
40
- let(:object) { described_class.parse('Foo') }
41
- subject { object.inspect }
42
-
43
- it { should eql('<Mutant::Expression: Foo>') }
44
- it_should_behave_like 'an idempotent method'
45
- end
46
-
47
- describe '#_dump' do
48
- let(:object) { described_class.parse('Foo') }
49
- subject { object._dump(double('Level')) }
50
-
51
- it { should eql('Foo') }
52
- end
53
-
54
- describe '.parse' do
55
- subject { object.parse(input) }
56
-
57
- context 'on nonsense' do
58
- let(:input) { 'foo bar' }
24
+ describe '.try_parse' do
25
+ let(:object) do
26
+ Class.new(described_class) do
27
+ include Anima.new(:foo)
59
28
 
60
- it 'raises an exception' do
61
- expect { subject }.to raise_error(
62
- Mutant::Expression::InvalidExpressionError,
63
- 'Expression: "foo bar" is not valid'
64
- )
29
+ const_set(:REGEXP, /(?<foo>foo)/)
65
30
  end
66
31
  end
67
32
 
68
- context 'on a valid expression' do
69
- let(:input) { 'Foo' }
33
+ subject { object.try_parse(input) }
34
+
35
+ context 'on successful parse' do
36
+ let(:input) { 'foo' }
70
37
 
71
- it { should eql(Mutant::Expression::Namespace::Exact.new('Foo')) }
38
+ it { should eql(object.new(foo: 'foo')) }
72
39
  end
73
- end
74
40
 
75
- describe '._load' do
76
- subject { described_class._load('Foo') }
41
+ context 'on unsuccessful parse' do
42
+ let(:input) { 'bar' }
77
43
 
78
- it { should eql(described_class.parse('Foo')) }
44
+ it { should be(nil) }
45
+ end
79
46
  end
80
47
  end