zeus-edge 0.12.1

Sign up to get free protection for your applications and to get access to all the features.
data/lib/zeus/m.rb ADDED
@@ -0,0 +1,346 @@
1
+ # This is very largely based on @qrush's M, but there are many modifications.
2
+
3
+ # we need to load all dependencies up front, because bundler will
4
+ # remove us from the load path soon.
5
+ require "rubygems"
6
+ require "zeus/m/test_collection"
7
+ require "zeus/m/test_method"
8
+
9
+ # the Gemfile may specify a version of method_source, but we also want to require it here.
10
+ # To avoid possible "you've activated X; gemfile specifies Y" errors, we actually scan
11
+ # Gemfile.lock for a specific version, and require exactly that version if present.
12
+ gemfile_lock = ROOT_PATH + "/Gemfile.lock"
13
+ if File.exists?(gemfile_lock)
14
+ version = File.read(ROOT_PATH + "/Gemfile.lock").
15
+ scan(/\bmethod_source\s*\(([\d\.]+)\)/).flatten[0]
16
+
17
+ gem "method_source", version if version
18
+ end
19
+
20
+ require 'method_source'
21
+
22
+ module Zeus
23
+ #`m` stands for metal, which is a better test/unit test runner that can run
24
+ #tests by line number.
25
+ #
26
+ #[![m ci](https://secure.travis-ci.org/qrush/m.png)](http://travis-ci.org/qrush/m)
27
+ #
28
+ #![Rush is a heavy metal band. Look it up on Wikipedia.](https://raw.github.com/qrush/m/master/rush.jpg)
29
+ #
30
+ #<sub>[Rush at the Bristol Colston Hall May 1979](http://www.flickr.com/photos/8507625@N02/3468299995/)</sub>
31
+ ### Install
32
+ #
33
+ ### Usage
34
+ #
35
+ #Basically, I was sick of using the `-n` flag to grab one test to run. Instead, I
36
+ #prefer how RSpec's test runner allows tests to be run by line number.
37
+ #
38
+ #Given this file:
39
+ #
40
+ # $ cat -n test/example_test.rb
41
+ # 1 require 'test/unit'
42
+ # 2
43
+ # 3 class ExampleTest < Test::Unit::TestCase
44
+ # 4 def test_apple
45
+ # 5 assert_equal 1, 1
46
+ # 6 end
47
+ # 7
48
+ # 8 def test_banana
49
+ # 9 assert_equal 1, 1
50
+ # 10 end
51
+ # 11 end
52
+ #
53
+ #You can run a test by line number, using format `m TEST_FILE:LINE_NUMBER_OF_TEST`:
54
+ #
55
+ # $ m test/example_test.rb:4
56
+ # Run options: -n /test_apple/
57
+ #
58
+ # # Running tests:
59
+ #
60
+ # .
61
+ #
62
+ # Finished tests in 0.000525s, 1904.7619 tests/s, 1904.7619 assertions/s.
63
+ #
64
+ # 1 tests, 1 assertions, 0 failures, 0 errors, 0 skips
65
+ #
66
+ #Hit the wrong line number? No problem, `m` helps you out:
67
+ #
68
+ # $ m test/example_test.rb:2
69
+ # No tests found on line 2. Valid tests to run:
70
+ #
71
+ # test_apple: m test/examples/test_unit_example_test.rb:4
72
+ # test_banana: m test/examples/test_unit_example_test.rb:8
73
+ #
74
+ #Want to run the whole test? Just leave off the line number.
75
+ #
76
+ # $ m test/example_test.rb
77
+ # Run options:
78
+ #
79
+ # # Running tests:
80
+ #
81
+ # ..
82
+ #
83
+ # Finished tests in 0.001293s, 1546.7904 tests/s, 3093.5808 assertions/s.
84
+ #
85
+ # 1 tests, 2 assertions, 0 failures, 0 errors, 0 skips
86
+ #
87
+ #### Supports
88
+ #
89
+ #`m` works with a few Ruby test frameworks:
90
+ #
91
+ #* `Test::Unit`
92
+ #* `ActiveSupport::TestCase`
93
+ #* `MiniTest::Unit::TestCase`
94
+ #
95
+ ### License
96
+ #
97
+ #This gem is MIT licensed, please see `LICENSE` for more information.
98
+
99
+ ### M, your metal test runner
100
+ # Maybe this gem should have a longer name? Metal?
101
+ module M
102
+ VERSION = "1.2.1" unless defined?(VERSION)
103
+
104
+ # Accept arguments coming from bin/m and run tests.
105
+ def self.run(argv)
106
+ Runner.new(argv).run
107
+ end
108
+
109
+ ### Runner is in charge of running your tests.
110
+ # Instead of slamming all of this junk in an `M` class, it's here instead.
111
+ class Runner
112
+ def initialize(argv)
113
+ @argv = argv
114
+ end
115
+
116
+ # There's two steps to running our tests:
117
+ # 1. Parsing the given input for the tests we need to find (or groups of tests)
118
+ # 2. Run those tests we found that match what you wanted
119
+ def run
120
+ parse
121
+ execute
122
+ end
123
+
124
+ private
125
+
126
+ def parse
127
+ # With no arguments,
128
+ if @argv.empty?
129
+ # Just shell out to `rake test`.
130
+ require 'rake'
131
+ Rake::Task['test'].invoke
132
+ exit
133
+ else
134
+ parse_options! @argv
135
+
136
+ # Parse out ARGV, it should be coming in in a format like `test/test_file.rb:9`
137
+ _, line = @argv.first.split(':')
138
+ @line ||= line.nil? ? nil : line.to_i
139
+
140
+ @files = []
141
+ @argv.each do |arg|
142
+ add_file(arg)
143
+ end
144
+ end
145
+ end
146
+
147
+ def add_file(arg)
148
+ file = arg.split(':').first
149
+ if Dir.exist?(file)
150
+ files = Dir.glob("#{file}/**/*test*.rb")
151
+ @files.concat(files)
152
+ else
153
+ files = Dir.glob(file)
154
+ files == [] and abort "Couldn't find test file '#{file}'!"
155
+ @files.concat(files)
156
+ end
157
+ end
158
+
159
+ def parse_options!(argv)
160
+ require 'optparse'
161
+
162
+ OptionParser.new do |opts|
163
+ opts.banner = 'Options:'
164
+ opts.version = M::VERSION
165
+
166
+ opts.on '-h', '--help', 'Display this help.' do
167
+ puts "Usage: m [OPTIONS] [FILES]\n\n", opts
168
+ exit
169
+ end
170
+
171
+ opts.on '--version', 'Display the version.' do
172
+ puts "m #{M::VERSION}"
173
+ exit
174
+ end
175
+
176
+ opts.on '-l', '--line LINE', Integer, 'Line number for file.' do |line|
177
+ @line = line
178
+ end
179
+
180
+ opts.on '-n', '--name NAME', String, 'Name or pattern for test methods to run.' do |name|
181
+ if name[0] == "/" && name[-1] == "/"
182
+ @test_name = Regexp.new(name[1..-2])
183
+ else
184
+ @test_name = name
185
+ end
186
+ end
187
+
188
+ opts.parse! argv
189
+ end
190
+ end
191
+
192
+ def execute
193
+ generate_tests_to_run
194
+
195
+ test_arguments = build_test_arguments
196
+
197
+ # directly run the tests from here and exit with the status of the tests passing or failing
198
+ case framework
199
+ when :minitest
200
+ exit MiniTest::Unit.runner.run test_arguments
201
+ when :testunit1, :testunit2
202
+ exit Test::Unit::AutoRunner.run(false, nil, test_arguments)
203
+ else
204
+ not_supported
205
+ end
206
+ end
207
+
208
+ def generate_tests_to_run
209
+ # Locate tests to run that may be inside of this line. There could be more than one!
210
+ all_tests = tests
211
+ if @line
212
+ @tests_to_run = all_tests.within(@line)
213
+ end
214
+ end
215
+
216
+ def build_test_arguments
217
+ if @line
218
+ abort_with_no_test_found_by_line_number if @tests_to_run.empty?
219
+
220
+ # assemble the regexp to run these tests,
221
+ test_names = @tests_to_run.map(&:name).join('|')
222
+
223
+ # set up the args needed for the runner
224
+ ["-n", "/(#{test_names})/"]
225
+ elsif user_specified_name?
226
+ abort_with_no_test_found_by_name unless tests.contains?(@test_name)
227
+
228
+ test_names = test_name_to_s
229
+ ["-n", test_names]
230
+ else
231
+ []
232
+ end
233
+ end
234
+
235
+ def abort_with_no_test_found_by_line_number
236
+ abort_with_valid_tests_msg "No tests found on line #{@line}. "
237
+ end
238
+
239
+ def abort_with_no_test_found_by_name
240
+ abort_with_valid_tests_msg "No test name matches '#{test_name_to_s}'. "
241
+ end
242
+
243
+ def abort_with_valid_tests_msg message=""
244
+ message << "Valid tests to run:\n\n"
245
+ # For every test ordered by line number,
246
+ # spit out the test name and line number where it starts,
247
+ tests.by_line_number do |test|
248
+ message << "#{sprintf("%0#{tests.column_size}s", test.name)}: zeus test #{@files[0]}:#{test.start_line}\n"
249
+ end
250
+
251
+ # fail like a good unix process should.
252
+ abort message
253
+ end
254
+
255
+ def test_name_to_s
256
+ @test_name.is_a?(Regexp)? "/#{@test_name.source}/" : @test_name
257
+ end
258
+
259
+ def user_specified_name?
260
+ !@test_name.nil?
261
+ end
262
+
263
+ def framework
264
+ @framework ||= begin
265
+ if defined?(MiniTest)
266
+ :minitest
267
+ elsif defined?(Test)
268
+ if Test::Unit::TestCase.respond_to?(:test_suites)
269
+ :testunit2
270
+ else
271
+ :testunit1
272
+ end
273
+ end
274
+ end
275
+ end
276
+
277
+ # Finds all test suites in this test file, with test methods included.
278
+ def suites
279
+ # Since we're not using `ruby -Itest -Ilib` to run the tests, we need to add this directory to the `LOAD_PATH`
280
+ $:.unshift "./test", "./lib"
281
+
282
+ if framework == :testunit1
283
+ Test::Unit::TestCase.class_eval {
284
+ @@test_suites = {}
285
+ def self.inherited(klass)
286
+ @@test_suites[klass] = true
287
+ end
288
+ def self.test_suites
289
+ @@test_suites.keys
290
+ end
291
+ def self.test_methods
292
+ public_instance_methods(true).grep(/^test/).map(&:to_s)
293
+ end
294
+ }
295
+ end
296
+
297
+ begin
298
+ # Fire up the Ruby files. Let's hope they actually have tests.
299
+ @files.each { |f| load f }
300
+ rescue LoadError => e
301
+ # Fail with a happier error message instead of spitting out a backtrace from this gem
302
+ abort "Failed loading test file:\n#{e.message}"
303
+ end
304
+
305
+ # Figure out what test framework we're using
306
+ case framework
307
+ when :minitest
308
+ suites = MiniTest::Unit::TestCase.test_suites
309
+ when :testunit1, :testunit2
310
+ suites = Test::Unit::TestCase.test_suites
311
+ else
312
+ not_supported
313
+ end
314
+
315
+ # Use some janky internal APIs to group test methods by test suite.
316
+ suites.inject({}) do |suites, suite_class|
317
+ # End up with a hash of suite class name to an array of test methods, so we can later find them and ignore empty test suites
318
+ suites[suite_class] = suite_class.test_methods if suite_class.test_methods.size > 0
319
+ suites
320
+ end
321
+ end
322
+
323
+ # Shoves tests together in our custom container and collection classes.
324
+ # Memoize it since it's unnecessary to do this more than one for a given file.
325
+ def tests
326
+ @tests ||= begin
327
+ # With each suite and array of tests,
328
+ # and with each test method present in this test file,
329
+ # shove a new test method into this collection.
330
+ suites.inject(TestCollection.new) do |collection, (suite_class, test_methods)|
331
+ test_methods.each do |test_method|
332
+ find_locations = (@files.size == 1 && @line)
333
+ collection << TestMethod.create(suite_class, test_method, find_locations)
334
+ end
335
+ collection
336
+ end
337
+ end
338
+ end
339
+
340
+ # Fail loudly if this isn't supported
341
+ def not_supported
342
+ abort "This test framework is not supported! Please open up an issue at https://github.com/qrush/m !"
343
+ end
344
+ end
345
+ end
346
+ end
data/lib/zeus/plan.rb ADDED
File without changes
data/lib/zeus/rails.rb ADDED
@@ -0,0 +1,220 @@
1
+ ROOT_PATH = File.expand_path(Dir.pwd)
2
+ ENV_PATH = File.expand_path('config/environment', ROOT_PATH)
3
+ BOOT_PATH = File.expand_path('config/boot', ROOT_PATH)
4
+ APP_PATH = File.expand_path('config/application', ROOT_PATH)
5
+
6
+ require 'zeus'
7
+ require 'zeus/m'
8
+
9
+ module Zeus
10
+ class Rails < Plan
11
+ def deprecated
12
+ puts "Zeus 0.11.0 changed zeus.json. You'll have to rm zeus.json && zeus init."
13
+ end
14
+ alias_method :spec_helper, :deprecated
15
+ alias_method :testrb, :deprecated
16
+ alias_method :rspec, :deprecated
17
+
18
+
19
+ def after_fork
20
+ reconnect_activerecord
21
+ restart_girl_friday
22
+ reconnect_redis
23
+ end
24
+
25
+ def _monkeypatch_rake
26
+ require 'rake/testtask'
27
+ Rake::TestTask.class_eval {
28
+
29
+ # Create the tasks defined by this task lib.
30
+ def define
31
+ desc "Run tests" + (@name==:test ? "" : " for #{@name}")
32
+ task @name do
33
+ # ruby "#{ruby_opts_string} #{run_code} #{file_list_string} #{option_list}"
34
+ rails_env = ENV['RAILS_ENV']
35
+ rubyopt = ENV['RUBYOPT']
36
+ ENV['RAILS_ENV'] = nil
37
+ ENV['RUBYOPT'] = nil # bundler sets this to require bundler :|
38
+ puts "zeus test #{file_list_string}"
39
+ system "zeus test #{file_list_string}"
40
+ ENV['RAILS_ENV'] = rails_env
41
+ ENV['RUBYOPT'] = rubyopt
42
+ end
43
+ self
44
+ end
45
+
46
+ alias_method :_original_define, :define
47
+
48
+ def self.inherited(klass)
49
+ return unless klass.name == "TestTaskWithoutDescription"
50
+ klass.class_eval {
51
+ def self.method_added(sym)
52
+ class_eval do
53
+ if !@rails_hack_reversed
54
+ @rails_hack_reversed = true
55
+ alias_method :define, :_original_define
56
+ def desc(*)
57
+ end
58
+ end
59
+ end
60
+ end
61
+ }
62
+ end
63
+ }
64
+ end
65
+
66
+ def boot
67
+ _monkeypatch_rake
68
+
69
+ require BOOT_PATH
70
+ # config/application.rb normally requires 'rails/all'.
71
+ # Some 'alternative' ORMs such as Mongoid give instructions to switch this require
72
+ # out for a list of railties, not including ActiveRecord.
73
+ # We grep config/application.rb for all requires of rails/all or railties, and require them.
74
+ rails_components = File.read(APP_PATH + ".rb").
75
+ scan(/^\s*require\s*['"](.*railtie.*|rails\/all)['"]/).flatten
76
+
77
+ rails_components = ["rails/all"] if rails_components == []
78
+ rails_components.each do |component|
79
+ require component
80
+ end
81
+ end
82
+
83
+ def default_bundle
84
+ Bundler.require(:default)
85
+ end
86
+
87
+ def development_environment
88
+ Bundler.require(:development)
89
+ ::Rails.env = ENV['RAILS_ENV'] = "development"
90
+ require APP_PATH
91
+ ::Rails.application.require_environment!
92
+ end
93
+
94
+ def prerake
95
+ require 'rake'
96
+ end
97
+
98
+ def rake
99
+ Rake.application.run
100
+ end
101
+
102
+ def generate
103
+ load_rails_generators
104
+ require 'rails/commands/generate'
105
+ end
106
+
107
+ def destroy
108
+ load_rails_generators
109
+ require 'rails/commands/destroy'
110
+ end
111
+
112
+ def runner
113
+ require 'rails/commands/runner'
114
+ end
115
+
116
+ def console
117
+ require 'rails/commands/console'
118
+ ::Rails::Console.start(::Rails.application)
119
+ end
120
+
121
+ def dbconsole
122
+ require 'rails/commands/dbconsole'
123
+
124
+ meth = ::Rails::DBConsole.method(:start)
125
+
126
+ # `Rails::DBConsole.start` has been changed to load faster in
127
+ # https://github.com/rails/rails/commit/346bb018499cde6699fcce6c68dd7e9be45c75e1
128
+ #
129
+ # This will work with both versions.
130
+ if meth.arity.zero?
131
+ ::Rails::DBConsole.start
132
+ else
133
+ ::Rails::DBConsole.start(::Rails.application)
134
+ end
135
+ end
136
+
137
+ def server
138
+ require 'rails/commands/server'
139
+ server = ::Rails::Server.new
140
+ Dir.chdir(::Rails.application.root)
141
+ server.start
142
+ end
143
+
144
+ def test_environment
145
+ Bundler.require(:test)
146
+
147
+ ::Rails.env = ENV['RAILS_ENV'] = 'test'
148
+ require APP_PATH
149
+
150
+ $rails_rake_task = 'yup' # lie to skip eager loading
151
+ ::Rails.application.require_environment!
152
+ $rails_rake_task = nil
153
+
154
+ $LOAD_PATH.unshift ".", "./lib", "./test", "./spec"
155
+ end
156
+
157
+ def test_helper
158
+ if File.exists?(ROOT_PATH + "/spec/spec_helper.rb")
159
+ require 'spec_helper'
160
+ elsif File.exist?(ROOT_PATH + "/test/minitest_helper.rb")
161
+ require 'minitest_helper'
162
+ else
163
+ require 'test_helper'
164
+ end
165
+ end
166
+
167
+ def test
168
+ if defined?(RSpec)
169
+ exit RSpec::Core::Runner.run(ARGV)
170
+ else
171
+ Zeus::M.run(ARGV)
172
+ end
173
+ end
174
+
175
+ def cucumber_environment
176
+ require 'cucumber/rspec/disable_option_parser'
177
+ require 'cucumber/cli/main'
178
+ @cucumber_runtime = Cucumber::Runtime.new
179
+ end
180
+
181
+ def cucumber
182
+ cucumber_main = Cucumber::Cli::Main.new(ARGV.dup)
183
+ exit cucumber_main.execute!(@cucumber_runtime)
184
+ end
185
+
186
+
187
+ private
188
+
189
+ def restart_girl_friday
190
+ return unless defined?(GirlFriday::WorkQueue)
191
+ # The Actor is run in a thread, and threads don't persist post-fork.
192
+ # We just need to restart each one in the newly-forked process.
193
+ ObjectSpace.each_object(GirlFriday::WorkQueue) do |obj|
194
+ obj.send(:start)
195
+ end
196
+ end
197
+
198
+ def reconnect_activerecord
199
+ ActiveRecord::Base.clear_all_connections! rescue nil
200
+ ActiveRecord::Base.establish_connection rescue nil
201
+ end
202
+
203
+ def reconnect_redis
204
+ return unless defined?(Redis::Client)
205
+ ObjectSpace.each_object(Redis::Client) do |client|
206
+ client.connect
207
+ end
208
+ end
209
+
210
+ def load_rails_generators
211
+ require 'rails/generators'
212
+ ::Rails.application.load_generators
213
+ rescue LoadError # Rails 3.0 doesn't require this block to be run, but 3.2+ does
214
+ end
215
+
216
+ end
217
+ end
218
+
219
+ Zeus.plan ||= Zeus::Rails.new
220
+