drunit 0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/README.markdown +32 -0
- data/Rakefile +52 -0
- data/bin/drunit_remote +13 -0
- data/lib/drunit/remote_app.rb +59 -0
- data/lib/drunit/remote_test.rb +54 -0
- data/lib/drunit.rb +47 -0
- data/test/fake_app/fake_app.rb +13 -0
- data/test/test_helper.rb +5 -0
- data/test/unit/exception_handling_test.rb +13 -0
- data/test/unit/main_test.rb +41 -0
- metadata +66 -0
data/README.markdown
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
# Distributed Ruby Unit (Testing)
|
2
|
+
|
3
|
+
A library for running tests across multiple applications from a single test case.
|
4
|
+
|
5
|
+
|
6
|
+
## Basic usage:
|
7
|
+
|
8
|
+
class MainTest < Test::Unit::TestCase
|
9
|
+
include Drunit
|
10
|
+
RemoteApp(:fake_app, FAKE_APP_PATH + "/fake_app.rb")
|
11
|
+
RemoteApp(:rails_app, RAILS_APP_PATH + "/script/runner")
|
12
|
+
|
13
|
+
def test_should_use_the_same_db
|
14
|
+
id = in_app(:fake_app){
|
15
|
+
User.create!(:name => "Some Name")
|
16
|
+
}
|
17
|
+
|
18
|
+
in_app(:rails_app, id) do |id|
|
19
|
+
assert_nothing_raised() { User.find(id) }
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
## Other "features"
|
25
|
+
* Automatically "Brings Up" and "Takes Down" your apps as needed. (Uses GC to tell when it's no longer needed.)
|
26
|
+
* Tracking of assertion counts.
|
27
|
+
* Beautified (a little) backtraces.
|
28
|
+
* Line number preserving between apps.
|
29
|
+
* Packed full of bugs (probably, I'm not sure).
|
30
|
+
* Packed full of 1.9 incompatibilities (I'm assuming).
|
31
|
+
* Packed full of space ninjas.
|
32
|
+
* Over 14% more awesome than a bag of chips.
|
data/Rakefile
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
require 'rake/testtask'
|
4
|
+
require 'rake/rdoctask'
|
5
|
+
require 'rake/gempackagetask'
|
6
|
+
require 'date'
|
7
|
+
|
8
|
+
desc 'Default: run unit tests.'
|
9
|
+
task :default => :test
|
10
|
+
|
11
|
+
desc 'run tests.'
|
12
|
+
Rake::TestTask.new(:test) do |t|
|
13
|
+
t.libs << 'lib'
|
14
|
+
t.pattern = 'test/**/*_test.rb'
|
15
|
+
t.verbose = true
|
16
|
+
end
|
17
|
+
|
18
|
+
spec = Gem::Specification.new do |s|
|
19
|
+
s.name = %q{drunit}
|
20
|
+
s.version = "0.2"
|
21
|
+
s.summary = %q{A library for running tests across multiple applications from a single test case.}
|
22
|
+
s.description = %q{A library for running tests across multiple applications from a single test case.}
|
23
|
+
|
24
|
+
s.files = FileList['[A-Z]*', 'lib/**/*.rb', 'test/**/*.rb', 'rails/*']
|
25
|
+
s.executables = "drunit_remote"
|
26
|
+
s.require_path = 'lib'
|
27
|
+
s.test_files = Dir[*['test/**/*_test.rb']]
|
28
|
+
|
29
|
+
s.has_rdoc = true
|
30
|
+
s.extra_rdoc_files = ["README.markdown"]
|
31
|
+
s.rdoc_options = ['--line-numbers', '--inline-source', "--main", "README.markdown"]
|
32
|
+
|
33
|
+
s.authors = ["Tom Lea"]
|
34
|
+
s.email = %q{commits@tomlea.co.uk}
|
35
|
+
|
36
|
+
s.platform = Gem::Platform::RUBY
|
37
|
+
end
|
38
|
+
|
39
|
+
Rake::GemPackageTask.new spec do |pkg|
|
40
|
+
pkg.need_tar = true
|
41
|
+
pkg.need_zip = true
|
42
|
+
end
|
43
|
+
|
44
|
+
desc "Clean files generated by rake tasks"
|
45
|
+
task :clobber => [:clobber_rdoc, :clobber_package]
|
46
|
+
|
47
|
+
desc "Generate a gemspec file"
|
48
|
+
task :gemspec do
|
49
|
+
File.open("#{spec.name}.gemspec", 'w') do |f|
|
50
|
+
f.write spec.to_ruby
|
51
|
+
end
|
52
|
+
end
|
data/bin/drunit_remote
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require File.join(File.dirname(__FILE__), *%w[.. lib drunit remote_test])
|
4
|
+
|
5
|
+
include Drunit
|
6
|
+
require ARGV.first if ARGV.first
|
7
|
+
|
8
|
+
DRb.start_service nil, RemoteTest.new
|
9
|
+
|
10
|
+
STDOUT.puts "DRUNIT:URI #{DRb.uri}"
|
11
|
+
STDOUT.reopen(STDERR)
|
12
|
+
|
13
|
+
DRb.thread.join
|
@@ -0,0 +1,59 @@
|
|
1
|
+
require 'drb'
|
2
|
+
require 'ruby2ruby'
|
3
|
+
|
4
|
+
module Drunit
|
5
|
+
class RemoteApp
|
6
|
+
def initialize(name, boot = nil)
|
7
|
+
@name = name
|
8
|
+
@boot = boot
|
9
|
+
@boot ||= "#{name}/test/drunit_test_helper.rb" if File.exist? "#{name}/test/drunit_test_helper.rb"
|
10
|
+
@boot ||= "#{name}/test/test_helper.rb" if File.exist? "#{name}/test/test_helper.rb"
|
11
|
+
@remote_object = nil
|
12
|
+
end
|
13
|
+
|
14
|
+
def run(method_name, file, line, *args, &block)
|
15
|
+
raise_or_return(app.eval(block_to_source(method_name, &block), file, line, method_name, *args), method_name)
|
16
|
+
end
|
17
|
+
|
18
|
+
def last_assertion_count
|
19
|
+
app.last_assertion_count
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
def raise_or_return(e, method_name)
|
24
|
+
return e unless e.is_a? Exception
|
25
|
+
if first_remote_line = e.backtrace.grep(Regexp.new(method_name)).last
|
26
|
+
index = e.backtrace.index(first_remote_line)
|
27
|
+
raise e, e.message, e.backtrace[0..index] + ["RemoteApp<#{@name}>"] + caller(0)
|
28
|
+
end
|
29
|
+
raise e
|
30
|
+
end
|
31
|
+
|
32
|
+
def get_url(pipe)
|
33
|
+
pipe.each_line do |line|
|
34
|
+
return $1 if line =~ /^DRUNIT:URI (.*)$/
|
35
|
+
STDERR.puts "From drunit_remote>> #{line}"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def start_app!
|
40
|
+
drb_server = File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "bin", "drunit_remote"))
|
41
|
+
pipe = IO.popen("#{drb_server} #{@boot}")
|
42
|
+
pid = pipe.pid
|
43
|
+
url = get_url(pipe) or raise "Could not establish connection to the remote drunit instance."
|
44
|
+
remote_object = DRbObject.new(nil, url)
|
45
|
+
ObjectSpace.define_finalizer(remote_object, proc{|id| Process.kill("KILL", pid) && Process.wait})
|
46
|
+
return remote_object
|
47
|
+
end
|
48
|
+
|
49
|
+
def app
|
50
|
+
@remote_object ||= start_app!
|
51
|
+
end
|
52
|
+
|
53
|
+
def block_to_source(method_name, &block)
|
54
|
+
m = Module.new
|
55
|
+
m.send(:define_method, method_name, &block)
|
56
|
+
Ruby2Ruby.translate(m, method_name)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'drb'
|
2
|
+
require 'test/unit'
|
3
|
+
|
4
|
+
module Drunit
|
5
|
+
class RemoteError < RuntimeError
|
6
|
+
def initialize(real_exception)
|
7
|
+
@real_exception = real_exception
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
class RemoteTest
|
12
|
+
attr_reader :last_assertion_count
|
13
|
+
|
14
|
+
class TestCase
|
15
|
+
include Test::Unit::Assertions
|
16
|
+
attr_reader :assertion_count
|
17
|
+
|
18
|
+
def initialize(code, source_file, source_line, method_name)
|
19
|
+
@assertion_count = 0
|
20
|
+
@method_name = method_name
|
21
|
+
instance_eval(code, source_file, source_line)
|
22
|
+
end
|
23
|
+
|
24
|
+
def add_assertion
|
25
|
+
@assertion_count += 1
|
26
|
+
end
|
27
|
+
|
28
|
+
def run(*args)
|
29
|
+
send(@method_name, *args)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def eval(code, source_file, source_line, method_name, *args)
|
34
|
+
test_case = TestCase.new(code, source_file, source_line, method_name)
|
35
|
+
return rewrite_exceptions{ test_case.run(*args) }
|
36
|
+
rescue Exception => e
|
37
|
+
return e
|
38
|
+
ensure
|
39
|
+
@last_assertion_count = defined?(test_case.assertion_count) ? test_case.assertion_count : 0
|
40
|
+
end
|
41
|
+
private
|
42
|
+
# We need to strib down and generalise the exceptions to prevent the integration project from having to know anything about anything.
|
43
|
+
def rewrite_exceptions
|
44
|
+
return yield
|
45
|
+
rescue Test::Unit::AssertionFailedError
|
46
|
+
raise
|
47
|
+
rescue ArgumentError => e
|
48
|
+
raise e unless e.message =~ /Anonymous modules /
|
49
|
+
raise NameError, "Const Missing", e.backtrace[3..-1]
|
50
|
+
rescue Exception => e
|
51
|
+
raise Drunit::RemoteError.new(e.class.name), e.message, e.backtrace
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
data/lib/drunit.rb
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
module Drunit
|
2
|
+
class RemoteError < RuntimeError
|
3
|
+
def self.name
|
4
|
+
@@name
|
5
|
+
end
|
6
|
+
|
7
|
+
def class
|
8
|
+
if type = look_up_exception
|
9
|
+
return type
|
10
|
+
else
|
11
|
+
@@name = @real_exception.to_s
|
12
|
+
super
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def look_up_exception
|
17
|
+
@real_exception.split("::").inject(Object){|node, part|
|
18
|
+
node && node.const_defined?(part) && node.const_get(part)
|
19
|
+
}
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
module ClassMethods
|
24
|
+
def RemoteApp(name, *args)
|
25
|
+
const_set "RemoteAppFor_#{name}", RemoteApp.new(name, *args)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def in_app(name, *args, &block)
|
30
|
+
file, line, method = caller(2).first.split(":")
|
31
|
+
method ||= "unknown_method"
|
32
|
+
remote_app_for(name).run(method.gsub(/^in /, "").gsub(/[^a-zA-Z0-9_?!]/, ""), file, line.to_i, *args, &block)
|
33
|
+
ensure
|
34
|
+
remote_app_for(name).last_assertion_count.times{ add_assertion } rescue nil
|
35
|
+
end
|
36
|
+
|
37
|
+
def remote_app_for(name)
|
38
|
+
self.class.const_get("RemoteAppFor_#{name}")
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.included(other)
|
42
|
+
other.send(:extend, ClassMethods)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
require File.join(File.dirname(__FILE__), *%w[drunit remote_app])
|
47
|
+
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), *%w[.. test_helper])
|
2
|
+
|
3
|
+
class ExceptionHandlingTest < Test::Unit::TestCase
|
4
|
+
include Drunit
|
5
|
+
RemoteApp(:fake_app, FAKE_APP_PATH + "/fake_app.rb")
|
6
|
+
def InApp(*args, &block)
|
7
|
+
in_app(:fake_app, *args, &block)
|
8
|
+
end
|
9
|
+
|
10
|
+
def test_should_raise_a_generic_exception
|
11
|
+
assert_raise(Drunit::RemoteError) { InApp{ raise MyModule::SomeOtherException} }
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), *%w[.. test_helper])
|
2
|
+
|
3
|
+
class MainTest < Test::Unit::TestCase
|
4
|
+
include Drunit
|
5
|
+
RemoteApp(:fake_app, FAKE_APP_PATH + "/fake_app.rb")
|
6
|
+
def InApp(*args, &block)
|
7
|
+
in_app(:fake_app, *args, &block)
|
8
|
+
end
|
9
|
+
|
10
|
+
def test_should_not_raise_in_a_basic_case
|
11
|
+
InApp do
|
12
|
+
assert true
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def test_should_raise_an_exception
|
17
|
+
assert_raise(RuntimeError) { InApp{ raise "Fail" } }
|
18
|
+
end
|
19
|
+
|
20
|
+
def test_should_raise_an_exception_faking_its_class_name_if_we_have_never_heard_of_it
|
21
|
+
InApp{ raise SomeException, "Fooo" }
|
22
|
+
flunk "Should have raised."
|
23
|
+
rescue => e
|
24
|
+
assert_equal "SomeException", e.class.name
|
25
|
+
end
|
26
|
+
|
27
|
+
def test_should_inject_the_fact_we_are_in_a_remote_app_into_the_backtrace
|
28
|
+
e = assert_raise(RuntimeError) { InApp{ raise "Fail" } }
|
29
|
+
assert_equal 1, e.backtrace.grep("RemoteApp<fake_app>").size
|
30
|
+
end
|
31
|
+
|
32
|
+
def test_should_raise_the_assertion_count_when_we_assert
|
33
|
+
original_count = @_result.assertion_count
|
34
|
+
InApp{ assert true}
|
35
|
+
assert_equal original_count + 1, @_result.assertion_count
|
36
|
+
end
|
37
|
+
|
38
|
+
def test_should_be_able_to_pass_in_simple_params
|
39
|
+
assert_equal 12, InApp(2,6){|a,b| SomeFoo.new.multiply(a, b)}
|
40
|
+
end
|
41
|
+
end
|
metadata
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: drunit
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: "0.2"
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Tom Lea
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-04-07 00:00:00 +01:00
|
13
|
+
default_executable: drunit_remote
|
14
|
+
dependencies: []
|
15
|
+
|
16
|
+
description: A library for running tests across multiple applications from a single test case.
|
17
|
+
email: commits@tomlea.co.uk
|
18
|
+
executables:
|
19
|
+
- drunit_remote
|
20
|
+
extensions: []
|
21
|
+
|
22
|
+
extra_rdoc_files:
|
23
|
+
- README.markdown
|
24
|
+
files:
|
25
|
+
- Rakefile
|
26
|
+
- README.markdown
|
27
|
+
- lib/drunit/remote_app.rb
|
28
|
+
- lib/drunit/remote_test.rb
|
29
|
+
- lib/drunit.rb
|
30
|
+
- test/fake_app/fake_app.rb
|
31
|
+
- test/test_helper.rb
|
32
|
+
- test/unit/exception_handling_test.rb
|
33
|
+
- test/unit/main_test.rb
|
34
|
+
- bin/drunit_remote
|
35
|
+
has_rdoc: true
|
36
|
+
homepage:
|
37
|
+
post_install_message:
|
38
|
+
rdoc_options:
|
39
|
+
- --line-numbers
|
40
|
+
- --inline-source
|
41
|
+
- --main
|
42
|
+
- README.markdown
|
43
|
+
require_paths:
|
44
|
+
- lib
|
45
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
46
|
+
requirements:
|
47
|
+
- - ">="
|
48
|
+
- !ruby/object:Gem::Version
|
49
|
+
version: "0"
|
50
|
+
version:
|
51
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - ">="
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: "0"
|
56
|
+
version:
|
57
|
+
requirements: []
|
58
|
+
|
59
|
+
rubyforge_project:
|
60
|
+
rubygems_version: 1.3.1
|
61
|
+
signing_key:
|
62
|
+
specification_version: 2
|
63
|
+
summary: A library for running tests across multiple applications from a single test case.
|
64
|
+
test_files:
|
65
|
+
- test/unit/exception_handling_test.rb
|
66
|
+
- test/unit/main_test.rb
|