mutant 0.1.1 → 0.2.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 (225) hide show
  1. data/.gitignore +5 -11
  2. data/.rspec +0 -1
  3. data/.travis.yml +14 -3
  4. data/Changelog.md +3 -0
  5. data/Gemfile +5 -1
  6. data/Gemfile.devtools +49 -0
  7. data/Guardfile +18 -0
  8. data/README.md +67 -0
  9. data/Rakefile +4 -1
  10. data/TODO +13 -0
  11. data/bin/mutant +14 -0
  12. data/bin/zombie +14 -0
  13. data/config/flay.yml +3 -0
  14. data/config/flog.yml +2 -0
  15. data/config/roodi.yml +26 -0
  16. data/config/site.reek +93 -0
  17. data/config/yardstick.yml +2 -0
  18. data/lib/inflector.rb +7 -0
  19. data/lib/inflector/defaults.rb +62 -0
  20. data/lib/inflector/inflections.rb +209 -0
  21. data/lib/inflector/methods.rb +149 -0
  22. data/lib/inflector/version.rb +3 -0
  23. data/lib/mutant.rb +96 -21
  24. data/lib/mutant/cli.rb +309 -0
  25. data/lib/mutant/color.rb +61 -0
  26. data/lib/mutant/context.rb +36 -0
  27. data/lib/mutant/context/scope.rb +138 -0
  28. data/lib/mutant/differ.rb +100 -0
  29. data/lib/mutant/helper.rb +38 -0
  30. data/lib/mutant/killer.rb +136 -0
  31. data/lib/mutant/killer/forking.rb +41 -0
  32. data/lib/mutant/killer/rspec.rb +49 -0
  33. data/lib/mutant/killer/static.rb +19 -0
  34. data/lib/mutant/loader.rb +129 -0
  35. data/lib/mutant/matcher.rb +55 -0
  36. data/lib/mutant/matcher/chain.rb +66 -0
  37. data/lib/mutant/matcher/method.rb +173 -0
  38. data/lib/mutant/matcher/method/classifier.rb +126 -0
  39. data/lib/mutant/matcher/method/instance.rb +67 -0
  40. data/lib/mutant/matcher/method/singleton.rb +141 -0
  41. data/lib/mutant/matcher/object_space.rb +114 -0
  42. data/lib/mutant/matcher/scope_methods.rb +127 -0
  43. data/lib/mutant/mutation.rb +101 -12
  44. data/lib/mutant/mutation/filter.rb +75 -0
  45. data/lib/mutant/mutation/filter/code.rb +68 -0
  46. data/lib/mutant/mutation/filter/regexp.rb +39 -0
  47. data/lib/mutant/mutation/filter/whitelist.rb +47 -0
  48. data/lib/mutant/mutator.rb +134 -30
  49. data/lib/mutant/mutator/node.rb +163 -0
  50. data/lib/mutant/mutator/node/arguments.rb +24 -0
  51. data/lib/mutant/mutator/node/block.rb +24 -0
  52. data/lib/mutant/mutator/node/define.rb +24 -0
  53. data/lib/mutant/mutator/node/if_statement.rb +93 -0
  54. data/lib/mutant/mutator/node/literal.rb +54 -0
  55. data/lib/mutant/mutator/node/literal/array.rb +28 -0
  56. data/lib/mutant/mutator/node/literal/boolean.rb +49 -0
  57. data/lib/mutant/mutator/node/literal/dynamic.rb +24 -0
  58. data/lib/mutant/mutator/node/literal/empty_array.rb +26 -0
  59. data/lib/mutant/mutator/node/literal/fixnum.rb +37 -0
  60. data/lib/mutant/mutator/node/literal/float.rb +48 -0
  61. data/lib/mutant/mutator/node/literal/hash.rb +89 -0
  62. data/lib/mutant/mutator/node/literal/nil.rb +25 -0
  63. data/lib/mutant/mutator/node/literal/range.rb +94 -0
  64. data/lib/mutant/mutator/node/literal/regex.rb +43 -0
  65. data/lib/mutant/mutator/node/literal/string.rb +26 -0
  66. data/lib/mutant/mutator/node/literal/symbol.rb +26 -0
  67. data/lib/mutant/mutator/node/noop.rb +55 -0
  68. data/lib/mutant/mutator/node/receiver_case.rb +140 -0
  69. data/lib/mutant/mutator/node/return.rb +31 -0
  70. data/lib/mutant/mutator/node/send.rb +112 -0
  71. data/lib/mutant/mutator/registry.rb +48 -0
  72. data/lib/mutant/mutator/util.rb +87 -0
  73. data/lib/mutant/random.rb +24 -27
  74. data/lib/mutant/reporter.rb +48 -30
  75. data/lib/mutant/reporter/cli.rb +221 -0
  76. data/lib/mutant/reporter/null.rb +42 -0
  77. data/lib/mutant/reporter/stats.rb +64 -0
  78. data/lib/mutant/runner.rb +112 -0
  79. data/lib/mutant/strategy.rb +42 -0
  80. data/lib/mutant/strategy/rspec.rb +59 -0
  81. data/lib/mutant/strategy/rspec/example_lookup.rb +122 -0
  82. data/lib/mutant/subject.rb +115 -0
  83. data/lib/mutant/support/method_object.rb +31 -0
  84. data/locator.rb +87 -0
  85. data/mutant.gemspec +21 -21
  86. data/spec/integration/mutant/differ_spec.rb +15 -0
  87. data/spec/integration/mutant/loader_spec.rb +21 -0
  88. data/spec/integration/mutant/method_matching_spec.rb +269 -0
  89. data/spec/integration/mutant/rspec_killer_spec.rb +24 -0
  90. data/spec/integration/mutant/runner_spec.rb +26 -0
  91. data/spec/integration/mutant/zombie_spec.rb +8 -0
  92. data/spec/rcov.opts +7 -0
  93. data/spec/shared/command_method_behavior.rb +7 -0
  94. data/spec/shared/each_method_behaviour.rb +15 -0
  95. data/spec/shared/hash_method_behavior.rb +17 -0
  96. data/spec/shared/idempotent_method_behavior.rb +7 -0
  97. data/spec/shared/invertible_method_behaviour.rb +9 -0
  98. data/spec/shared/method_filter_parse_behavior.rb +16 -0
  99. data/spec/shared/method_match_behavior.rb +39 -0
  100. data/spec/shared/mutator_behavior.rb +46 -0
  101. data/spec/spec_helper.rb +11 -14
  102. data/spec/support/compress_helper.rb +10 -0
  103. data/spec/support/rspec.rb +22 -0
  104. data/spec/support/test_app.rb +5 -0
  105. data/spec/support/zombie.rb +141 -0
  106. data/spec/unit/mutant/cli/class_methods/new_spec.rb +87 -0
  107. data/spec/unit/mutant/cli/class_methods/run_spec.rb +38 -0
  108. data/spec/unit/mutant/context/root_spec.rb +11 -0
  109. data/spec/unit/mutant/context/scope/class_methods/build_spec.rb +29 -0
  110. data/spec/unit/mutant/context/scope/root_spec.rb +22 -0
  111. data/spec/unit/mutant/context/scope/unqualified_name_spec.rb +27 -0
  112. data/spec/unit/mutant/killer/fail_ques_spec.rb +39 -0
  113. data/spec/unit/mutant/killer/rspec/class_methods/new_spec.rb +32 -0
  114. data/spec/unit/mutant/loader/eval/class_methods/run_spec.rb +33 -0
  115. data/spec/unit/mutant/loader/rubinius/class_methods/run_spec.rb +42 -0
  116. data/spec/unit/mutant/matcher/chain/each_spec.rb +37 -0
  117. data/spec/unit/mutant/matcher/chain/matchers_spec.rb +12 -0
  118. data/spec/unit/mutant/matcher/class_methods/from_string_spec.rb +49 -0
  119. data/spec/unit/mutant/matcher/class_methods/parse_spec.rb +12 -0
  120. data/spec/unit/mutant/matcher/each_spec.rb +14 -0
  121. data/spec/unit/mutant/matcher/method/class_methods/parse_spec.rb +21 -0
  122. data/spec/unit/mutant/matcher/method/classifier/class_methods/run_spec.rb +34 -0
  123. data/spec/unit/mutant/matcher/method/method_spec.rb +11 -0
  124. data/spec/unit/mutant/matcher/object_space/class_methods/parse_spec.rb +24 -0
  125. data/spec/unit/mutant/matcher/object_space/each_spec.rb +31 -0
  126. data/spec/unit/mutant/mutator/each_spec.rb +25 -0
  127. data/spec/unit/mutant/mutator/emit_new_spec.rb +51 -0
  128. data/spec/unit/mutant/mutator/emit_spec.rb +52 -0
  129. data/spec/unit/mutant/mutator/node/block/mutation_spec.rb +36 -0
  130. data/spec/unit/mutant/mutator/node/define/mutation_spec.rb +47 -0
  131. data/spec/unit/mutant/mutator/node/if_statement/mutation_spec.rb +30 -0
  132. data/spec/unit/mutant/mutator/node/literal/array_spec.rb +30 -0
  133. data/spec/unit/mutant/mutator/node/literal/boolean/mutation_spec.rb +23 -0
  134. data/spec/unit/mutant/mutator/node/literal/empty_array_spec.rb +17 -0
  135. data/spec/unit/mutant/mutator/node/literal/fixnum_spec.rb +17 -0
  136. data/spec/unit/mutant/mutator/node/literal/float_spec.rb +25 -0
  137. data/spec/unit/mutant/mutator/node/literal/hash_spec.rb +34 -0
  138. data/spec/unit/mutant/mutator/node/literal/nil_spec.rb +13 -0
  139. data/spec/unit/mutant/mutator/node/literal/range_spec.rb +35 -0
  140. data/spec/unit/mutant/mutator/node/literal/regex_spec.rb +23 -0
  141. data/spec/unit/mutant/mutator/node/literal/string_spec.rb +17 -0
  142. data/spec/unit/mutant/mutator/node/literal/symbol_spec.rb +17 -0
  143. data/spec/unit/mutant/mutator/node/receiver_case/mutation_spec.rb +27 -0
  144. data/spec/unit/mutant/mutator/node/return/mutation_spec.rb +21 -0
  145. data/spec/unit/mutant/mutator/node/send/mutation_spec.rb +78 -0
  146. data/spec/unit/mutant/mutator/self_spec.rb +7 -0
  147. data/spec/unit/mutant/subject/class_methods/new_spec.rb +13 -0
  148. data/spec/unit/mutant/subject/context_spec.rb +14 -0
  149. data/spec/unit/mutant/subject/each_spec.rb +35 -0
  150. data/spec/unit/mutant/subject/node_spec.rb +13 -0
  151. data/tasks/metrics/ci.rake +7 -0
  152. data/tasks/metrics/flay.rake +41 -0
  153. data/tasks/metrics/flog.rake +43 -0
  154. data/tasks/metrics/heckle.rake +216 -0
  155. data/tasks/metrics/metric_fu.rake +31 -0
  156. data/tasks/metrics/reek.rake +15 -0
  157. data/tasks/metrics/roodi.rake +15 -0
  158. data/tasks/metrics/yardstick.rake +23 -0
  159. data/tasks/spec.rake +45 -0
  160. data/tasks/yard.rake +9 -0
  161. data/test_app/.rspec +1 -0
  162. data/test_app/lib/test_app.rb +5 -0
  163. data/test_app/lib/test_app/literal.rb +32 -0
  164. data/test_app/spec/shared/command_method_behavior.rb +7 -0
  165. data/test_app/spec/shared/each_method_behaviour.rb +15 -0
  166. data/test_app/spec/shared/hash_method_behavior.rb +17 -0
  167. data/test_app/spec/shared/idempotent_method_behavior.rb +7 -0
  168. data/test_app/spec/shared/invertible_method_behaviour.rb +9 -0
  169. data/test_app/spec/shared/method_filter_parse_behavior.rb +16 -0
  170. data/test_app/spec/shared/method_match_behavior.rb +39 -0
  171. data/test_app/spec/shared/mutator_behavior.rb +44 -0
  172. data/test_app/spec/spec_helper.rb +7 -0
  173. data/test_app/spec/unit/test_app/literal/command_spec.rb +9 -0
  174. data/test_app/spec/unit/test_app/literal/string_spec.rb +9 -0
  175. metadata +346 -124
  176. data/.rvmrc +0 -1
  177. data/Readme.md +0 -13
  178. data/exe/mutate +0 -6
  179. data/lib/mutant/extensions.rb +0 -8
  180. data/lib/mutant/formatter.rb +0 -19
  181. data/lib/mutant/implementation.rb +0 -70
  182. data/lib/mutant/literal.rb +0 -147
  183. data/lib/mutant/method.rb +0 -31
  184. data/lib/mutant/mutatee.rb +0 -61
  185. data/lib/mutant/node.rb +0 -26
  186. data/lib/mutant/runners/rspec.rb +0 -34
  187. data/lib/mutant/version.rb +0 -3
  188. data/spec/functional/class_spec.rb +0 -46
  189. data/spec/functional/instance_method/array_spec.rb +0 -53
  190. data/spec/functional/instance_method/boolean_spec.rb +0 -101
  191. data/spec/functional/instance_method/call_spec.rb +0 -161
  192. data/spec/functional/instance_method/fixnum_spec.rb +0 -53
  193. data/spec/functional/instance_method/float_spec.rb +0 -53
  194. data/spec/functional/instance_method/hash_spec.rb +0 -53
  195. data/spec/functional/instance_method/if_spec.rb +0 -57
  196. data/spec/functional/instance_method/ivar_assign_spec.rb +0 -62
  197. data/spec/functional/instance_method/range_spec.rb +0 -53
  198. data/spec/functional/instance_method/regex_spec.rb +0 -55
  199. data/spec/functional/instance_method/string_spec.rb +0 -53
  200. data/spec/functional/instance_method/symbol_spec.rb +0 -53
  201. data/spec/functional/reporter/method_loaded_spec.rb +0 -62
  202. data/spec/functional/reporter/running_mutations_spec.rb +0 -60
  203. data/spec/functional/runners/rspec_spec.rb +0 -26
  204. data/spec/functional/singleton_method/array_spec.rb +0 -53
  205. data/spec/functional/singleton_method/boolean_spec.rb +0 -101
  206. data/spec/functional/singleton_method/call_spec.rb +0 -161
  207. data/spec/functional/singleton_method/fixnum_spec.rb +0 -53
  208. data/spec/functional/singleton_method/float_spec.rb +0 -53
  209. data/spec/functional/singleton_method/hash_spec.rb +0 -53
  210. data/spec/functional/singleton_method/if_spec.rb +0 -57
  211. data/spec/functional/singleton_method/ivar_assign_spec.rb +0 -60
  212. data/spec/functional/singleton_method/range_spec.rb +0 -53
  213. data/spec/functional/singleton_method/regex_spec.rb +0 -55
  214. data/spec/functional/singleton_method/string_spec.rb +0 -53
  215. data/spec/functional/singleton_method/symbol_spec.rb +0 -53
  216. data/spec/mutant/extensions_spec.rb +0 -13
  217. data/spec/mutant/implementation_spec.rb +0 -223
  218. data/spec/mutant/literal_spec.rb +0 -129
  219. data/spec/mutant/mutatee_spec.rb +0 -28
  220. data/spec/mutant/node_spec.rb +0 -41
  221. data/spec/mutant/random_spec.rb +0 -33
  222. data/spec/mutant/reporter_spec.rb +0 -17
  223. data/spec/mutant_spec.rb +0 -28
  224. data/spec/support/example_group_helpers.rb +0 -11
  225. data/spec/support/example_helpers.rb +0 -5
@@ -0,0 +1,24 @@
1
+ require 'spec_helper'
2
+
3
+ describe Mutant,'rspec integration' do
4
+
5
+ around do |example|
6
+ Dir.chdir(TestApp.root) do
7
+ example.run
8
+ end
9
+ end
10
+
11
+ let(:strategy) { Mutant::Strategy::Rspec::DM2 }
12
+
13
+ specify 'allows to kill mutations' do
14
+ Kernel.system("bundle exec mutant -I lib -r test_app --rspec-dm2 ::TestApp::Literal#string").should be(true)
15
+ end
16
+
17
+ specify 'fails to kill mutations when they are not covered' do
18
+ Kernel.system("bundle exec mutant -I lib -r test_app --rspec-dm2 ::TestApp::Literal#uncovered_string").should be(false)
19
+ end
20
+
21
+ specify 'fails when some mutations when are not covered' do
22
+ Kernel.system("bundle exec mutant -I lib -r test_app --rspec-dm2 ::TestApp::Literal").should be(false)
23
+ end
24
+ end
@@ -0,0 +1,26 @@
1
+ require 'spec_helper'
2
+
3
+ describe Mutant, 'runner' do
4
+ before do
5
+ pending
6
+ end
7
+
8
+ around do |example|
9
+ Dir.chdir(TestApp.root) do
10
+ example.run
11
+ end
12
+ end
13
+
14
+ it 'allows to run mutant over a project' do
15
+ output = StringIO.new
16
+ runner = Mutant::Runner.run(
17
+ :killer => Mutant::Killer::Rspec,
18
+ :matcher => Mutant::Matcher::ObjectSpace.new(/\ATestApp::/),
19
+ :reporter => Mutant::Reporter::CLI.new(output)
20
+ )
21
+ runner.fail?.should be(true)
22
+ runner.errors.size.should be(22)
23
+ output.rewind
24
+ output.lines.grep(/Mutant alive:/).size.should be(22)
25
+ end
26
+ end
@@ -0,0 +1,8 @@
1
+ require 'spec_helper'
2
+
3
+ describe Mutant, 'as a zombie' do
4
+ specify 'allows to create zombie from mutant' do
5
+ Zombie.setup
6
+ Zombie::Runner
7
+ end
8
+ end
@@ -0,0 +1,7 @@
1
+ --exclude-only "spec/,^/"
2
+ --sort coverage
3
+ --callsites
4
+ --xrefs
5
+ --profile
6
+ --text-summary
7
+ --failure-threshold 100
@@ -0,0 +1,7 @@
1
+ # encoding: utf-8
2
+
3
+ shared_examples_for 'a command method' do
4
+ it 'returns self' do
5
+ should equal(object)
6
+ end
7
+ end
@@ -0,0 +1,15 @@
1
+ # encoding: utf-8
2
+
3
+ shared_examples_for 'an #each method' do
4
+ it_should_behave_like 'a command method'
5
+
6
+ context 'with no block' do
7
+ subject { object.each }
8
+
9
+ it { should be_instance_of(to_enum.class) }
10
+
11
+ it 'yields the expected values' do
12
+ subject.to_a.should eql(object.to_a)
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,17 @@
1
+ # encoding: utf-8
2
+
3
+ shared_examples_for 'a hash method' do
4
+ it_should_behave_like 'an idempotent method'
5
+
6
+ specification = proc do
7
+ should be_instance_of(Fixnum)
8
+ end
9
+
10
+ it 'is a fixnum' do
11
+ instance_eval(&specification)
12
+ end
13
+
14
+ it 'memoizes the hash code' do
15
+ subject.should eql(object.memoized(:hash))
16
+ end
17
+ end
@@ -0,0 +1,7 @@
1
+ # encoding: utf-8
2
+
3
+ shared_examples_for 'an idempotent method' do
4
+ it 'is idempotent' do
5
+ should equal(instance_eval(&self.class.subject))
6
+ end
7
+ end
@@ -0,0 +1,9 @@
1
+ # encoding: utf-8
2
+
3
+ shared_examples_for 'an invertible method' do
4
+ it_should_behave_like 'an idempotent method'
5
+
6
+ it 'is invertible' do
7
+ subject.inverse.should equal(object)
8
+ end
9
+ end
@@ -0,0 +1,16 @@
1
+ shared_examples_for 'a method filter parse result' do
2
+ before do
3
+ expected_class.stub(:new => response)
4
+ end
5
+
6
+ let(:response) { mock('Response') }
7
+
8
+ it { should be(response) }
9
+
10
+ it 'should initialize method filter with correct arguments' do
11
+ expected_class.should_receive(:new).with(TestApp::Literal, :string).and_return(response)
12
+ subject
13
+ end
14
+ end
15
+
16
+
@@ -0,0 +1,39 @@
1
+ shared_examples_for 'a method match' do
2
+ subject { Mutant::Matcher::Method.parse(pattern).to_a }
3
+
4
+ let(:values) { defaults.merge(expectation) }
5
+
6
+ let(:method_name) { values.fetch(:method_name) }
7
+ let(:method_line) { values.fetch(:method_line) }
8
+ let(:method_arity) { values.fetch(:method_arity) }
9
+ let(:scope) { values.fetch(:scope) }
10
+ let(:node_class) { values.fetch(:node_class) }
11
+
12
+ let(:node) { mutation_subject.node }
13
+ let(:context) { mutation_subject.context }
14
+ let(:mutation_subject) { subject.first }
15
+
16
+ it 'should return one subject' do
17
+ subject.size.should be(1)
18
+ end
19
+
20
+ it 'should have correct method name' do
21
+ name(node).should eql(method_name)
22
+ end
23
+
24
+ it 'should have correct line number' do
25
+ node.line.should eql(method_line)
26
+ end
27
+
28
+ it 'should have correct arity' do
29
+ arguments(node).required.length.should eql(method_arity)
30
+ end
31
+
32
+ it 'should have correct scope in context' do
33
+ context.send(:scope).should eql(scope)
34
+ end
35
+
36
+ it 'should have the correct node class' do
37
+ node.should be_a(node_class)
38
+ end
39
+ end
@@ -0,0 +1,46 @@
1
+ shared_examples_for 'a mutator' do
2
+ subject { object.each(node) { |item| yields << item } }
3
+
4
+ let(:yields) { [] }
5
+ let(:object) { described_class }
6
+
7
+ unless instance_methods.map(&:to_s).include?('node')
8
+ let(:node) { source.to_ast }
9
+ end
10
+
11
+ it_should_behave_like 'a command method'
12
+
13
+ context 'with no block' do
14
+ subject { object.each(node) }
15
+
16
+ it { should be_instance_of(to_enum.class) }
17
+
18
+ let(:expected_mutations) do
19
+ mutations.map do |mutation|
20
+ if mutation.respond_to?(:to_ast)
21
+ mutation.to_ast.to_sexp
22
+ else
23
+ mutation
24
+ end
25
+ end.to_set
26
+ end
27
+
28
+ it 'generates the expected mutations' do
29
+ generated = self.subject.map(&:to_sexp).to_set
30
+
31
+ missing = (expected_mutations - generated).to_a
32
+ unexpected = (generated - expected_mutations).to_a
33
+
34
+ unless generated == expected_mutations
35
+ message = "Missing mutations: %s\nUnexpected mutations: %s" % [missing, unexpected].map(&:inspect)
36
+ fail message
37
+ end
38
+ end
39
+ end
40
+ end
41
+
42
+ shared_examples_for 'a noop mutator' do
43
+ let(:mutations) { [] }
44
+
45
+ it_should_behave_like 'a mutator'
46
+ end
@@ -1,18 +1,15 @@
1
- require 'mutant'
2
- require 'aruba/api'
1
+ # encoding: utf-8
3
2
 
4
- Dir['./spec/support/*.rb'].map {|f| require f }
3
+ require 'rspec'
5
4
 
6
- RSpec.configure do |config|
7
- config.treat_symbols_as_metadata_keys_with_true_values = true
8
- config.filter_run :focus
9
- config.run_all_when_everything_filtered = true
10
- config.extend ExampleGroupHelpers
11
- config.include ExampleHelpers
5
+ # require spec support files and shared behavior
6
+ Dir[File.expand_path('../{support,shared}/**/*.rb', __FILE__)].each { |f| require(f) }
7
+
8
+ $: << File.join(TestApp.root,'lib')
12
9
 
13
- {:example_group => {:file_path => /spec\/functional/}}.tap do |options|
14
- config.include Aruba::Api, options
15
- config.before(:suite, options) { @aruba_timeout_seconds = 5 }
16
- config.after(:each, options) { FileUtils.remove_dir('tmp/aruba') }
17
- end
10
+ require 'test_app'
11
+ require 'mutant'
12
+
13
+ RSpec.configure do |config|
14
+ config.include(CompressHelper)
18
15
  end
@@ -0,0 +1,10 @@
1
+ module CompressHelper
2
+ def strip_indent(string)
3
+ lines = string.lines
4
+ match = /\A( *)/.match(lines.first)
5
+ whitespaces = match[1].to_s.length
6
+ stripped = lines.map do |line|
7
+ line[whitespaces..-1]
8
+ end.join
9
+ end
10
+ end
@@ -0,0 +1,22 @@
1
+ # Patch rspec to allow nested execution
2
+ module Rspec
3
+ # Run block in clean rspec environment
4
+ #
5
+ # @return [Object]
6
+ # returns the value of block
7
+ #
8
+ # @api private
9
+ #
10
+ def self.nest
11
+ original_world, original_configuration =
12
+ ::RSpec.instance_variable_get(:@world),
13
+ ::RSpec.instance_variable_get(:@configuration)
14
+
15
+ ::RSpec.reset
16
+
17
+ yield
18
+ ensure
19
+ ::RSpec.instance_variable_set(:@world, original_world)
20
+ ::RSpec.instance_variable_set(:@configuration, original_configuration)
21
+ end
22
+ end
@@ -0,0 +1,5 @@
1
+ module TestApp
2
+ def self.root
3
+ File.expand_path('../../../test_app', __FILE__)
4
+ end
5
+ end
@@ -0,0 +1,141 @@
1
+ module Zombie
2
+ # Setup zombie
3
+ #
4
+ # @return [self]
5
+ #
6
+ # @api private
7
+ #
8
+ def self.setup
9
+ files.each do |path|
10
+ path = "#{File.expand_path(path, root)}.rb"
11
+ ast = File.read(path).to_ast
12
+ zombify(ast, path)
13
+ end
14
+
15
+ self
16
+ end
17
+
18
+ # Return library root directory
19
+ #
20
+ # @return [String]
21
+ #
22
+ # @api private
23
+ #
24
+ def self.root
25
+ File.expand_path('../../../lib',__FILE__)
26
+ end
27
+ private_class_method :root
28
+
29
+
30
+ # Replace Mutant with Zombie namespace
31
+ #
32
+ # @param [Rubinius::AST::Node]
33
+ #
34
+ # @api private
35
+ #
36
+ # @return [undefined]
37
+ #
38
+ def self.zombify(root, path)
39
+ node = find_mutant(root)
40
+ unless node
41
+ raise "unable to find mutant in AST from: #{path.inspect}"
42
+ end
43
+
44
+ name = node.name
45
+
46
+ node.name = Rubinius::AST::ModuleName.new(name.line, :Zombie)
47
+
48
+ scope = node.body
49
+
50
+ unless scope.kind_of?(Rubinius::AST::EmptyBody)
51
+ node.body = Rubinius::AST::ModuleScope.new(scope.line, node.name, scope.body)
52
+ end
53
+
54
+ ::Mutant::Loader::Eval.run(root)
55
+ end
56
+ private_class_method :zombify
57
+
58
+ # Find mutant module in AST
59
+ #
60
+ # @param [Rubinius::AST::Node]
61
+ #
62
+ # @return [Rubinius::AST::Node]
63
+ #
64
+ def self.find_mutant(root)
65
+ if is_mutant?(root)
66
+ return root
67
+ end
68
+
69
+ unless root.kind_of?(Rubinius::AST::Block)
70
+ raise "Cannot find mutant in: #{root.class}"
71
+ end
72
+
73
+ root.array.each do |node|
74
+ return node if is_mutant?(node)
75
+ end
76
+
77
+ nil
78
+ end
79
+ private_class_method :find_mutant
80
+
81
+ # Test if node is mutant module
82
+ #
83
+ # @param [Rubinius::AST::Node]
84
+ #
85
+ # @return [true]
86
+ # returns true if node is the mutant module
87
+ #
88
+ # @return [false]
89
+ # returns false otherwise
90
+ #
91
+ # @api private
92
+ #
93
+ def self.is_mutant?(node)
94
+ node.kind_of?(Rubinius::AST::Module) && is_mutant_name?(node.name)
95
+ end
96
+ private_class_method :is_mutant?
97
+
98
+ # Test if node is mutant module name
99
+ #
100
+ # @param [Rubinius::AST::ModuleName]
101
+ #
102
+ # @return [true]
103
+ # returns true if node is the mutant module name
104
+ #
105
+ # @return [false]
106
+ # returns false otherwise
107
+ #
108
+ # @api private
109
+ #
110
+ def self.is_mutant_name?(node)
111
+ node.name == :Mutant
112
+ end
113
+ private_class_method :is_mutant_name?
114
+
115
+ # Return all library files the mutant is made of.
116
+ #
117
+ # @return [Array<String>]
118
+ #
119
+ # @api private
120
+ #
121
+ # FIXME:
122
+ # Yeah looks very ugly but im currently to exited to do a cleanup.
123
+ #
124
+ def self.files
125
+ block = File.read("lib/mutant.rb").to_ast
126
+ files = block.array.select do |node|
127
+ node.class == Rubinius::AST::SendWithArguments &&
128
+ node.receiver.class == Rubinius::AST::Self &&
129
+ node.name == :require
130
+ end.map do |node|
131
+ arguments = node.arguments.array
132
+ raise unless arguments.one?
133
+ argument = arguments.first
134
+ raise unless argument.class == Rubinius::AST::StringLiteral
135
+ argument.string
136
+ end.select do |file|
137
+ file =~ /\Amutant/
138
+ end
139
+ end
140
+ private_class_method :files
141
+ end