ngauthier-multitest 0.1.0 → 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +1 -0
- data/README.rdoc +49 -18
- data/VERSION +1 -1
- data/lib/multitest/multitest.rb +24 -21
- data/lib/multitest/safe_fork.rb +24 -0
- data/lib/multitest/tasks.rb +3 -3
- data/multitest.gemspec +4 -2
- data/test/multitest_test.rb +2 -2
- metadata +3 -2
data/.gitignore
CHANGED
data/README.rdoc
CHANGED
@@ -2,45 +2,76 @@
|
|
2
2
|
|
3
3
|
== Caveats
|
4
4
|
|
5
|
-
Multitest currently is not really tested and working with databases all that well. I have a project in Postgres with all of my data done transactionally during the test via factory_girl. It works there. But it didn't work on a quick test in a MySQL project.
|
6
|
-
|
7
5
|
Multitest is still very young, it is in active development and is no where near complete or robust.
|
8
6
|
|
7
|
+
Multitest currently only works in a rails environment. It's only because I use "constantize".
|
8
|
+
|
9
|
+
If you depend on fixture data in your database, you may have data inconsistency errors. I like to use an empty DB and then use factories with transactional tests, and this runs great.
|
10
|
+
|
9
11
|
== Installation
|
10
12
|
|
11
|
-
gem install ngauthier-multitest
|
13
|
+
gem install ngauthier-multitest
|
12
14
|
|
13
15
|
== Usage
|
14
16
|
|
15
17
|
in your Rakefile:
|
16
18
|
|
17
|
-
require 'multitest'
|
18
|
-
require 'multitest/tasks'
|
19
|
+
require 'multitest'
|
20
|
+
require 'multitest/tasks'
|
19
21
|
|
20
22
|
on the commandline:
|
21
23
|
|
22
|
-
rake multitest
|
23
|
-
rake multitest:units multitest:functionals multitest:integration
|
24
|
+
rake multitest
|
25
|
+
rake multitest:units multitest:functionals multitest:integration
|
24
26
|
|
25
27
|
== Configuration
|
26
28
|
|
27
29
|
in your Rakefile:
|
28
30
|
|
29
|
-
Multitest.cores = 8
|
31
|
+
Multitest.cores = 8
|
32
|
+
|
33
|
+
== Benchmarks
|
34
|
+
|
35
|
+
This is one of our largest apps.
|
36
|
+
|
37
|
+
=== Normal suite
|
38
|
+
|
39
|
+
time rake test
|
40
|
+
259.03user 27.50system 5:14.76elapsed 91%CPU
|
41
|
+
|
42
|
+
=== 2 Cores
|
43
|
+
|
44
|
+
time rake multitest
|
45
|
+
213.86user 23.38system 2:41.24elapsed 147%CPU
|
46
|
+
|
47
|
+
=== 4 Cores
|
48
|
+
|
49
|
+
time rake multitest
|
50
|
+
223.18user 24.53system 2:08.96elapsed 192%CPU
|
51
|
+
|
52
|
+
Note the wall-time differences of 5:14 vs 2:41. 195% speedup on two cores. On 4 cores there is only a speedup of 243%, but that's still quicker. If you send me impressive benchmarks, I'll be happy to put them up here (8 cores anyone?).
|
53
|
+
|
54
|
+
== Require Errors
|
55
|
+
|
56
|
+
If you get require errors like:
|
30
57
|
|
31
|
-
|
58
|
+
Running multitest:units
|
59
|
+
rake aborted!
|
60
|
+
no such file to load -- test_helper
|
32
61
|
|
33
|
-
|
62
|
+
Then you may want to use more absolute paths in your tests. For example, if you had:
|
34
63
|
|
35
|
-
|
36
|
-
|
37
|
-
|
64
|
+
# test/unit/my_class.rb
|
65
|
+
require 'test_helper'
|
66
|
+
class MyClass < ActiveSupport::TestCase
|
67
|
+
end
|
38
68
|
|
39
|
-
|
40
|
-
time rake multitest
|
41
|
-
8.35user 1.64system 0:08.51elapsed 117%CPU
|
69
|
+
You should change it to:
|
42
70
|
|
43
|
-
|
71
|
+
# test/unit/my_class.rb
|
72
|
+
require File.join(File.dirname(__FILE__), '..', 'test_helper')
|
73
|
+
class MyClass < ActiveSupport::TestCase
|
74
|
+
end
|
44
75
|
|
45
76
|
== Note on Patches/Pull Requests
|
46
77
|
|
@@ -50,7 +81,7 @@ Note the wall-time differences of 20.38 vs 8.51. 239% speedup on two cores.
|
|
50
81
|
future version unintentionally.
|
51
82
|
* Commit, do not mess with rakefile, version, or history.
|
52
83
|
(if you want to have your own version, that is fine but
|
53
|
-
|
84
|
+
bump version in a commit by itself I can ignore when I pull)
|
54
85
|
* Send me a pull request. Bonus points for topic branches.
|
55
86
|
|
56
87
|
== Copyright
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.1.
|
1
|
+
0.1.1
|
data/lib/multitest/multitest.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require File.join(File.dirname(__FILE__), 'pipe_dream')
|
2
|
+
require File.join(File.dirname(__FILE__), 'safe_fork')
|
2
3
|
require 'test/unit/testresult'
|
3
4
|
require 'test/unit'
|
4
5
|
|
@@ -32,39 +33,27 @@ class Multitest
|
|
32
33
|
$stderr.write @files.inspect+"\n"; $stderr.flush
|
33
34
|
@cores.times do |c|
|
34
35
|
@pipes << PipeDream.new
|
35
|
-
@children <<
|
36
|
+
@children << SafeFork.fork do
|
36
37
|
Signal.trap("TERM") { exit }
|
37
38
|
Signal.trap("HUP") { exit }
|
38
39
|
pipe = @pipes.last
|
39
40
|
pipe.identify_as_child
|
40
41
|
pipe.write("[Worker #{c}] Booted\n")
|
42
|
+
@result = Test::Unit::TestResult.new
|
43
|
+
@result.add_listener(Test::Unit::TestResult::FAULT) do |value|
|
44
|
+
$stderr.write value
|
45
|
+
$stderr.write "\n\n"
|
46
|
+
$stderr.flush
|
47
|
+
end
|
41
48
|
while !pipe.eof?
|
42
49
|
file = pipe.gets.chomp
|
43
50
|
begin
|
44
51
|
pipe.write "[Worker #{c} Starting: #{file}\n"
|
45
52
|
start = Time.now
|
46
53
|
|
47
|
-
@result = Test::Unit::TestResult.new
|
48
|
-
@result.add_listener(Test::Unit::TestResult::FAULT) do |value|
|
49
|
-
# $stderr.write "\n"
|
50
|
-
$stderr.write value
|
51
|
-
$stderr.write "\n\n"
|
52
|
-
$stderr.flush
|
53
|
-
end
|
54
54
|
klasses = Multitest.find_classes_in_file(file)
|
55
55
|
klasses.each{|k| k.suite.run(@result){|status, name| ;}}
|
56
56
|
|
57
|
-
#puts @result
|
58
|
-
|
59
|
-
#unless @result[:failures].empty?
|
60
|
-
# puts @result[:failures].inspect
|
61
|
-
#end
|
62
|
-
|
63
|
-
#unless @result.errors.empty?
|
64
|
-
# puts @result.errors.inspect
|
65
|
-
#end
|
66
|
-
|
67
|
-
|
68
57
|
finish = Time.now
|
69
58
|
pipe.write "[Worker #{c}] Completed: #{file} (#{finish-start})\n"
|
70
59
|
rescue => ex
|
@@ -119,8 +108,22 @@ class Multitest
|
|
119
108
|
def self.find_classes_in_file(f)
|
120
109
|
code = ""
|
121
110
|
File.open(f) {|buffer| code = buffer.read}
|
122
|
-
matches = code.scan
|
123
|
-
klasses = matches.collect
|
111
|
+
matches = code.scan(/class\s+([\S]+)/)
|
112
|
+
klasses = matches.collect do |c|
|
113
|
+
begin
|
114
|
+
if c.first.respond_to? :constantize
|
115
|
+
c.first.constantize
|
116
|
+
else
|
117
|
+
eval(c.first)
|
118
|
+
end
|
119
|
+
rescue NameError
|
120
|
+
# $stderr.write "Could not load [#{c.first}] from [#{f}]\n"
|
121
|
+
nil
|
122
|
+
rescue SyntaxError
|
123
|
+
# $stderr.write "Could not load [#{c.first}] from [#{f}]\n"
|
124
|
+
nil
|
125
|
+
end
|
126
|
+
end
|
124
127
|
return klasses.select{|k| k.respond_to? 'suite'}
|
125
128
|
end
|
126
129
|
|
@@ -0,0 +1,24 @@
|
|
1
|
+
class SafeFork
|
2
|
+
def self.fork
|
3
|
+
begin
|
4
|
+
# remove our connection so it doesn't get cloned
|
5
|
+
ActiveRecord::Base.remove_connection if defined?(ActiveRecord)
|
6
|
+
# fork a process
|
7
|
+
child = Process.fork do
|
8
|
+
begin
|
9
|
+
# create a new connection and perform the action
|
10
|
+
ActiveRecord::Base.establish_connection if defined?(ActiveRecord)
|
11
|
+
yield
|
12
|
+
ensure
|
13
|
+
# make sure we remove the connection before we're done
|
14
|
+
ActiveRecord::Base.remove_connection if defined?(ActiveRecord)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
ensure
|
18
|
+
# make sure we re-establish the connection before returning to the main instance
|
19
|
+
ActiveRecord::Base.establish_connection if defined?(ActiveRecord)
|
20
|
+
end
|
21
|
+
return child
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
data/lib/multitest/tasks.rb
CHANGED
@@ -4,7 +4,7 @@ module Multitest::Tasks
|
|
4
4
|
|
5
5
|
namespace :multitest do
|
6
6
|
desc "Multi-core test:units"
|
7
|
-
task :units do
|
7
|
+
task :units => [:environment] do
|
8
8
|
pattern = 'test/unit/**/*_test.rb'
|
9
9
|
files = Dir.glob(pattern)
|
10
10
|
$stderr.write "Running multitest:units\n"
|
@@ -12,7 +12,7 @@ module Multitest::Tasks
|
|
12
12
|
$stderr.write "Completed multitest:units\n\n"
|
13
13
|
end
|
14
14
|
desc "Multi-core test:functionals"
|
15
|
-
task :functionals do
|
15
|
+
task :functionals => [:environment] do
|
16
16
|
pattern = 'test/functional/**/*_test.rb'
|
17
17
|
files = Dir.glob(pattern)
|
18
18
|
$stderr.write "Running multitest:functionals\n"
|
@@ -20,7 +20,7 @@ module Multitest::Tasks
|
|
20
20
|
$stderr.write "Completed multitest:functionals\n\n"
|
21
21
|
end
|
22
22
|
desc "Multi-core test:integration"
|
23
|
-
task :integration do
|
23
|
+
task :integration => [:environment] do
|
24
24
|
pattern = 'test/integration/**/*_test.rb'
|
25
25
|
files = Dir.glob(pattern)
|
26
26
|
$stderr.write "Running multitest:integration\n"
|
data/multitest.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{multitest}
|
8
|
-
s.version = "0.1.
|
8
|
+
s.version = "0.1.1"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Nick Gauthier"]
|
12
|
-
s.date = %q{2009-09-
|
12
|
+
s.date = %q{2009-09-25}
|
13
13
|
s.description = %q{Run your tests across multiple cores automatically.}
|
14
14
|
s.email = %q{nick@smartlogicsolutions.com}
|
15
15
|
s.extra_rdoc_files = [
|
@@ -29,6 +29,8 @@ Gem::Specification.new do |s|
|
|
29
29
|
"lib/multitest/multitest.rb",
|
30
30
|
"lib/multitest/pipe_dream.rb",
|
31
31
|
"lib/multitest/pipe_dream.rb",
|
32
|
+
"lib/multitest/safe_fork.rb",
|
33
|
+
"lib/multitest/safe_fork.rb",
|
32
34
|
"lib/multitest/tasks.rb",
|
33
35
|
"lib/multitest/tasks.rb",
|
34
36
|
"multitest.gemspec",
|
data/test/multitest_test.rb
CHANGED
@@ -4,12 +4,12 @@ require 'multitest'
|
|
4
4
|
class MultitestTest < Test::Unit::TestCase
|
5
5
|
context "a Multitest" do
|
6
6
|
setup do
|
7
|
+
FileUtils.mkdir_p(File.join(File.dirname(__FILE__), '..', 'tmp'))
|
8
|
+
FileUtils.rm_rf(File.expand_path(File.join(File.dirname(__FILE__), '..', 'tmp', 'test.log')))
|
7
9
|
@mt = Multitest.new([
|
8
10
|
File.expand_path(File.join(File.dirname(__FILE__), 'tests', 'sample_test.rb')),
|
9
11
|
File.expand_path(File.join(File.dirname(__FILE__), 'tests', 'another_test.rb'))
|
10
12
|
])
|
11
|
-
FileUtils.mkdir_p(File.join(File.dirname(__FILE__), '..', 'tmp'))
|
12
|
-
FileUtils.rm_rf(File.expand_path(File.join(File.dirname(__FILE__), '..', 'tmp', 'test.log')))
|
13
13
|
end
|
14
14
|
|
15
15
|
should "run three instances" do
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ngauthier-multitest
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Nick Gauthier
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-09-
|
12
|
+
date: 2009-09-25 00:00:00 -07:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
@@ -41,6 +41,7 @@ files:
|
|
41
41
|
- lib/multitest.rb
|
42
42
|
- lib/multitest/multitest.rb
|
43
43
|
- lib/multitest/pipe_dream.rb
|
44
|
+
- lib/multitest/safe_fork.rb
|
44
45
|
- lib/multitest/tasks.rb
|
45
46
|
- multitest.gemspec
|
46
47
|
- test/multitest_test.rb
|