specjour 0.2.0 → 0.2.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +2 -0
- data/History.markdown +9 -0
- data/README.markdown +17 -1
- data/VERSION +1 -1
- data/lib/specjour/cucumber/final_report.rb +0 -2
- data/lib/specjour/db_scrub.rb +1 -1
- data/lib/specjour/dispatcher.rb +6 -1
- data/lib/specjour/printer.rb +16 -5
- data/lib/specjour/rspec/final_report.rb +1 -2
- data/lib/specjour/rspec/marshalable_rspec_failure.rb +25 -23
- data/lib/specjour/rspec.rb +2 -3
- data/lib/specjour/rsync_daemon.rb +36 -14
- data/lib/specjour/worker.rb +11 -11
- data/lib/specjour.rb +2 -1
- data/spec/rsync_daemon_spec.rb +65 -0
- data/spec/spec.opts +1 -1
- metadata +5 -4
- data/lib/specjour/rspec/marshalable_failure_formatter.rb +0 -11
data/.gitignore
CHANGED
data/History.markdown
CHANGED
@@ -1,8 +1,17 @@
|
|
1
1
|
History
|
2
2
|
=======
|
3
3
|
|
4
|
+
0.2.1
|
5
|
+
-----
|
6
|
+
*2010-04-21*
|
7
|
+
|
8
|
+
* [added] The rsync daemon configuration file now lives in
|
9
|
+
project_path/.specjour/rsyncd.conf. Edit your rsync exclusions there.
|
10
|
+
* [fixed] Don't report connection errors when CTRL-C is sent.
|
11
|
+
|
4
12
|
0.2.0
|
5
13
|
-----
|
6
14
|
*2010-04-20*
|
7
15
|
|
8
16
|
* [added] Cucumber support. `rake specjour:cucumber`
|
17
|
+
* [added] CPU Core detection, use -w to override with less or more workers
|
data/README.markdown
CHANGED
@@ -17,7 +17,7 @@ _Distribute your spec suite amongst your LAN via Bonjour._
|
|
17
17
|
gem install specjour
|
18
18
|
|
19
19
|
## Start a manager
|
20
|
-
Running `specjour` on the command-line will start a manager which advertises that it's ready to run specs. By default, the manager will
|
20
|
+
Running `specjour` on the command-line will start a manager which advertises that it's ready to run specs. By default, the manager will use your system cores to determine the number of workers to use. Two cores equals two workers. If you only want to dedicate 1 core to running specs, use `$ specjour --workers 1`.
|
21
21
|
|
22
22
|
$ specjour
|
23
23
|
|
@@ -57,6 +57,22 @@ You could also listen to multiple projects:
|
|
57
57
|
|
58
58
|
$ specjour --projects bizconf,workbeast # only run specs for the bizconf and workbeast projects
|
59
59
|
|
60
|
+
## Customize what gets rsync'd
|
61
|
+
The standard rsync configuration file may be too broad for your
|
62
|
+
project. If you find you're rsyncing gigs of extraneous data from your public
|
63
|
+
directory, add an exclusion to your projects rsyncd.conf file.
|
64
|
+
|
65
|
+
$ vi workbeast/.specjour/rsyncd.conf
|
66
|
+
|
67
|
+
## Use one machine
|
68
|
+
Distributed testing doesn't have to happen over multiple machines, just multiple processes. Specjour is an excellent candidiate for running 4 tests at once on one machine with 4 cores. Just run `$ specjour` in one window and `$ rake specjour` in another.
|
69
|
+
|
70
|
+
## Thanks
|
71
|
+
|
72
|
+
* shayarnett - Cucumber support, pairing and other various patches
|
73
|
+
* voxdolo - Endless support, alpha testing, various patches
|
74
|
+
* leshill - Made rsync daemon configurable
|
75
|
+
|
60
76
|
## Note on Patches/Pull Requests
|
61
77
|
|
62
78
|
* Fork the project.
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.2.
|
1
|
+
0.2.1
|
@@ -29,14 +29,12 @@ module Specjour
|
|
29
29
|
end
|
30
30
|
|
31
31
|
def scenarios(status=nil)
|
32
|
-
require 'ostruct'
|
33
32
|
length = status ? @scenarios[status] : @scenarios.inject(0) {|h,(k,v)| h += v}
|
34
33
|
any = @scenarios[status] > 0 if status
|
35
34
|
OpenStruct.new(:length => length , :any? => any)
|
36
35
|
end
|
37
36
|
|
38
37
|
def steps(status=nil)
|
39
|
-
require 'ostruct'
|
40
38
|
length = status ? @steps[status] : @steps.inject(0) {|h,(k,v)| h += v}
|
41
39
|
any = @steps[status] > 0 if status
|
42
40
|
OpenStruct.new(:length => length , :any? => any)
|
data/lib/specjour/db_scrub.rb
CHANGED
@@ -6,7 +6,7 @@ module Specjour
|
|
6
6
|
def scrub
|
7
7
|
connect_to_database
|
8
8
|
if pending_migrations?
|
9
|
-
puts "Migrating schema for database #{ENV['TEST_ENV_NUMBER']
|
9
|
+
puts "Migrating schema for database #{ENV['TEST_ENV_NUMBER']}..."
|
10
10
|
Rake::Task['db:test:load'].invoke
|
11
11
|
else
|
12
12
|
purge_tables
|
data/lib/specjour/dispatcher.rb
CHANGED
@@ -4,6 +4,11 @@ module Specjour
|
|
4
4
|
Thread.abort_on_exception = true
|
5
5
|
include SocketHelpers
|
6
6
|
|
7
|
+
class << self
|
8
|
+
attr_accessor :interrupted
|
9
|
+
alias interrupted? interrupted
|
10
|
+
end
|
11
|
+
|
7
12
|
attr_reader :project_path, :managers, :manager_threads, :hosts
|
8
13
|
attr_accessor :worker_size
|
9
14
|
|
@@ -55,7 +60,7 @@ module Specjour
|
|
55
60
|
|
56
61
|
def gather_managers
|
57
62
|
puts "Waiting for managers"
|
58
|
-
Signal.trap('INT') { exit }
|
63
|
+
Signal.trap('INT') { self.class.interrupted = true; exit }
|
59
64
|
browser = DNSSD::Service.new
|
60
65
|
begin
|
61
66
|
Timeout.timeout(10) do
|
data/lib/specjour/printer.rb
CHANGED
@@ -1,4 +1,6 @@
|
|
1
1
|
module Specjour
|
2
|
+
require 'specjour/rspec'
|
3
|
+
|
2
4
|
class Printer < GServer
|
3
5
|
include Protocol
|
4
6
|
RANDOM_PORT = 0
|
@@ -48,7 +50,8 @@ module Specjour
|
|
48
50
|
def disconnecting(client_port)
|
49
51
|
self.disconnections += 1
|
50
52
|
if disconnections == worker_size
|
51
|
-
|
53
|
+
shutdown
|
54
|
+
stop unless stopped?
|
52
55
|
end
|
53
56
|
end
|
54
57
|
|
@@ -56,6 +59,10 @@ module Specjour
|
|
56
59
|
# noop
|
57
60
|
end
|
58
61
|
|
62
|
+
def error(exception)
|
63
|
+
Specjour.logger.debug exception.inspect
|
64
|
+
end
|
65
|
+
|
59
66
|
def process(message, client)
|
60
67
|
if message.is_a?(String)
|
61
68
|
$stdout.print message
|
@@ -71,11 +78,15 @@ module Specjour
|
|
71
78
|
|
72
79
|
def stopping
|
73
80
|
report.summarize
|
74
|
-
if disconnections != completed_workers
|
75
|
-
puts
|
76
|
-
puts "* ERROR: NOT ALL WORKERS COMPLETED PROPERLY, RE-RUN THE SUITE *"
|
77
|
-
puts "*" * 63
|
81
|
+
if disconnections != completed_workers && !Specjour::Dispatcher.interrupted?
|
82
|
+
puts abandoned_worker_message
|
78
83
|
end
|
79
84
|
end
|
85
|
+
|
86
|
+
def abandoned_worker_message
|
87
|
+
data = "* ERROR: NOT ALL WORKERS COMPLETED PROPERLY *"
|
88
|
+
filler = "*" * data.size
|
89
|
+
[filler, data, filler].join "\n"
|
90
|
+
end
|
80
91
|
end
|
81
92
|
end
|
@@ -1,7 +1,6 @@
|
|
1
1
|
module Specjour
|
2
2
|
module Rspec
|
3
3
|
class FinalReport
|
4
|
-
require 'specjour/rspec/marshalable_rspec_failure'
|
5
4
|
attr_reader :duration, :example_count, :failure_count, :pending_count, :pending_examples, :failing_examples
|
6
5
|
|
7
6
|
def initialize
|
@@ -38,7 +37,7 @@ module Specjour
|
|
38
37
|
|
39
38
|
def formatter
|
40
39
|
@formatter ||= begin
|
41
|
-
f =
|
40
|
+
f = Spec::Runner::Formatter::BaseTextFormatter.new(formatter_options, $stdout)
|
42
41
|
f.instance_variable_set(:@pending_examples, pending_examples)
|
43
42
|
f
|
44
43
|
end
|
@@ -1,33 +1,35 @@
|
|
1
|
-
module Specjour
|
2
|
-
|
3
|
-
|
1
|
+
module Specjour
|
2
|
+
module Rspec
|
3
|
+
class ::Spec::Runner::Reporter::Failure
|
4
4
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
5
|
+
def initialize(group_description, example_description, exception)
|
6
|
+
@example_name = "#{group_description} #{example_description}"
|
7
|
+
@exception = MarshalableException.new(exception)
|
8
|
+
@pending_fixed = exception.is_a?(Spec::Example::PendingExampleFixedError)
|
9
|
+
@exception_not_met = exception.is_a?(Spec::Expectations::ExpectationNotMetError)
|
10
|
+
end
|
11
|
+
|
12
|
+
def pending_fixed?
|
13
|
+
@pending_fixed
|
14
|
+
end
|
14
15
|
|
15
|
-
|
16
|
-
|
17
|
-
@header = "'#{@example_name}' FAILED"
|
18
|
-
elsif pending_fixed?
|
19
|
-
@header = "'#{@example_name}' FIXED"
|
20
|
-
else
|
21
|
-
@header = "#{exception_class_name} in '#{@example_name}'"
|
16
|
+
def expectation_not_met?
|
17
|
+
@exception_not_met
|
22
18
|
end
|
23
19
|
end
|
20
|
+
end
|
21
|
+
|
22
|
+
class MarshalableException
|
23
|
+
attr_accessor :message, :backtrace, :class_name
|
24
24
|
|
25
|
-
def
|
26
|
-
|
25
|
+
def initialize(exception)
|
26
|
+
self.class_name = exception.class.name
|
27
|
+
self.message = exception.message
|
28
|
+
self.backtrace = exception.backtrace
|
27
29
|
end
|
28
30
|
|
29
|
-
def
|
30
|
-
@
|
31
|
+
def class
|
32
|
+
@class ||= OpenStruct.new :name => class_name
|
31
33
|
end
|
32
34
|
end
|
33
35
|
end
|
data/lib/specjour/rspec.rb
CHANGED
@@ -3,8 +3,7 @@ module Specjour
|
|
3
3
|
require 'spec'
|
4
4
|
require 'spec/runner/formatter/base_text_formatter'
|
5
5
|
|
6
|
-
|
7
|
-
|
8
|
-
autoload :MarshalableFailureFormatter, 'specjour/rspec/marshalable_failure_formatter'
|
6
|
+
require 'specjour/rspec/distributed_formatter'
|
7
|
+
require 'specjour/rspec/final_report'
|
9
8
|
end
|
10
9
|
end
|
@@ -1,20 +1,36 @@
|
|
1
1
|
module Specjour
|
2
2
|
class RsyncDaemon
|
3
3
|
require 'fileutils'
|
4
|
+
include SocketHelpers
|
4
5
|
|
5
6
|
attr_reader :project_path, :project_name
|
7
|
+
|
6
8
|
def initialize(project_path, project_name)
|
7
9
|
@project_path = project_path
|
8
10
|
@project_name = project_name
|
9
11
|
end
|
10
12
|
|
13
|
+
def config_directory
|
14
|
+
@config_directory ||= File.join(project_path, ".specjour")
|
15
|
+
end
|
16
|
+
|
11
17
|
def config_file
|
12
|
-
File.join(
|
18
|
+
@config_file ||= File.join(config_directory, "rsyncd.conf")
|
19
|
+
end
|
20
|
+
|
21
|
+
def pid
|
22
|
+
if File.exists?(pid_file)
|
23
|
+
File.read(pid_file).strip.to_i
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def pid_file
|
28
|
+
File.join(config_directory, "rsync_daemon.pid")
|
13
29
|
end
|
14
30
|
|
15
31
|
def start
|
16
32
|
write_config
|
17
|
-
system
|
33
|
+
system *command
|
18
34
|
at_exit { stop }
|
19
35
|
end
|
20
36
|
|
@@ -28,32 +44,38 @@ module Specjour
|
|
28
44
|
protected
|
29
45
|
|
30
46
|
def write_config
|
31
|
-
File.
|
32
|
-
|
33
|
-
end
|
34
|
-
end
|
47
|
+
unless File.exists? config_file
|
48
|
+
FileUtils.mkdir_p config_directory
|
35
49
|
|
36
|
-
|
37
|
-
|
38
|
-
|
50
|
+
File.open(config_file, 'w') do |f|
|
51
|
+
f.write config
|
52
|
+
end
|
39
53
|
end
|
40
54
|
end
|
41
55
|
|
42
|
-
def
|
43
|
-
|
56
|
+
def command
|
57
|
+
["rsync", "--daemon", "--config=#{config_file}", "--port=8989"]
|
44
58
|
end
|
45
59
|
|
46
60
|
def config
|
47
61
|
<<-CONFIG
|
48
|
-
#
|
62
|
+
# #{Specjour::VERSION}
|
63
|
+
# Anonymous rsync daemon config for #{project_name}
|
64
|
+
#
|
65
|
+
# Serve this project with the following command:
|
66
|
+
# $ #{command.join(' ')}
|
67
|
+
#
|
68
|
+
# Rsync with the following command:
|
69
|
+
# $ rsync -a --port=8989 #{hostname}::#{project_name} ~/#{project_name}
|
70
|
+
#
|
49
71
|
use chroot = no
|
50
|
-
timeout =
|
72
|
+
timeout = 20
|
51
73
|
read only = yes
|
52
74
|
pid file = #{pid_file}
|
53
75
|
|
54
76
|
[#{project_name}]
|
55
77
|
path = #{project_path}
|
56
|
-
exclude = .git* doc tmp/* log script
|
78
|
+
exclude = .git* .specjour doc tmp/* log script
|
57
79
|
CONFIG
|
58
80
|
end
|
59
81
|
end
|
data/lib/specjour/worker.rb
CHANGED
@@ -22,28 +22,28 @@ module Specjour
|
|
22
22
|
end
|
23
23
|
|
24
24
|
def run
|
25
|
-
|
25
|
+
connection.send_message(:ready)
|
26
26
|
run_time = 0
|
27
27
|
Dir.chdir(project_path)
|
28
|
-
while !
|
28
|
+
while !connection.closed? && data = connection.gets(TERMINATOR)
|
29
29
|
test = load_object(data)
|
30
30
|
if test
|
31
31
|
run_time += Benchmark.realtime do
|
32
32
|
run_test test
|
33
33
|
end
|
34
|
-
|
34
|
+
connection.send_message(:ready)
|
35
35
|
else
|
36
|
-
|
37
|
-
|
38
|
-
|
36
|
+
connection.send_message(:worker_summary=, {:duration => sprintf("%6f", run_time)})
|
37
|
+
connection.send_message(:done)
|
38
|
+
connection.disconnect
|
39
39
|
end
|
40
40
|
end
|
41
41
|
end
|
42
42
|
|
43
43
|
protected
|
44
44
|
|
45
|
-
def
|
46
|
-
@
|
45
|
+
def connection
|
46
|
+
@connection ||= printer_connection
|
47
47
|
end
|
48
48
|
|
49
49
|
def printer_connection
|
@@ -51,7 +51,7 @@ module Specjour
|
|
51
51
|
end
|
52
52
|
|
53
53
|
def run_test(test)
|
54
|
-
puts "Running #{test}"
|
54
|
+
puts "[#{ENV['TEST_ENV_NUMBER']}] Running #{test}"
|
55
55
|
if test =~ /\.feature$/
|
56
56
|
run_feature test
|
57
57
|
else
|
@@ -61,7 +61,7 @@ module Specjour
|
|
61
61
|
|
62
62
|
def run_feature(feature)
|
63
63
|
set_up_cucumber
|
64
|
-
cli = ::Cucumber::Cli::Main.new(['--format', 'Specjour::Cucumber::DistributedFormatter', feature],
|
64
|
+
cli = ::Cucumber::Cli::Main.new(['--format', 'Specjour::Cucumber::DistributedFormatter', feature], connection)
|
65
65
|
cli.execute!(::Cucumber::Cli::Main.step_mother)
|
66
66
|
end
|
67
67
|
|
@@ -69,7 +69,7 @@ module Specjour
|
|
69
69
|
options = Spec::Runner::OptionParser.parse(
|
70
70
|
['--format=Specjour::Rspec::DistributedFormatter', spec],
|
71
71
|
$stderr,
|
72
|
-
|
72
|
+
connection
|
73
73
|
)
|
74
74
|
Spec::Runner.use options
|
75
75
|
options.run_examples
|
data/lib/specjour.rb
CHANGED
@@ -13,6 +13,7 @@ module Specjour
|
|
13
13
|
autoload :Connection, 'specjour/connection'
|
14
14
|
autoload :Dispatcher, 'specjour/dispatcher'
|
15
15
|
autoload :Manager, 'specjour/manager'
|
16
|
+
autoload :OpenStruct, 'ostruct'
|
16
17
|
autoload :Printer, 'specjour/printer'
|
17
18
|
autoload :Protocol, 'specjour/protocol'
|
18
19
|
autoload :RsyncDaemon, 'specjour/rsync_daemon'
|
@@ -22,7 +23,7 @@ module Specjour
|
|
22
23
|
autoload :Cucumber, 'specjour/cucumber'
|
23
24
|
autoload :Rspec, 'specjour/rspec'
|
24
25
|
|
25
|
-
VERSION = "0.2.
|
26
|
+
VERSION = "0.2.1".freeze
|
26
27
|
|
27
28
|
class Error < StandardError; end
|
28
29
|
|
@@ -0,0 +1,65 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Specjour::RsyncDaemon do
|
4
|
+
subject do
|
5
|
+
Specjour::RsyncDaemon.new('/tmp/seasonal', 'seasonal')
|
6
|
+
end
|
7
|
+
|
8
|
+
before do
|
9
|
+
stub(:system)
|
10
|
+
stub(:at_exit)
|
11
|
+
subject.stub(:write_config)
|
12
|
+
end
|
13
|
+
|
14
|
+
describe "#config_directory" do
|
15
|
+
specify { subject.config_directory.should == '/tmp/seasonal/.specjour' }
|
16
|
+
end
|
17
|
+
|
18
|
+
describe "#config_file" do
|
19
|
+
specify { subject.config_file.should == '/tmp/seasonal/.specjour/rsyncd.conf' }
|
20
|
+
end
|
21
|
+
|
22
|
+
describe "#start" do
|
23
|
+
it "writes the config" do
|
24
|
+
subject.should_receive(:write_config)
|
25
|
+
subject.start
|
26
|
+
end
|
27
|
+
|
28
|
+
it "executes the system command" do
|
29
|
+
subject.should_receive(:system).with(*subject.send(:command))
|
30
|
+
subject.start
|
31
|
+
end
|
32
|
+
|
33
|
+
it "stops at_exit" do
|
34
|
+
subject.should_receive(:at_exit)
|
35
|
+
subject.start
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
describe "#stop" do
|
40
|
+
context "with pid" do
|
41
|
+
before do
|
42
|
+
subject.stub(:pid => 100_000_000)
|
43
|
+
Process.stub(:kill)
|
44
|
+
FileUtils.stub(:rm)
|
45
|
+
end
|
46
|
+
|
47
|
+
it "kills the pid with TERM" do
|
48
|
+
Process.should_receive(:kill).with('TERM', subject.pid)
|
49
|
+
subject.stop
|
50
|
+
end
|
51
|
+
|
52
|
+
it "removes the pid file" do
|
53
|
+
FileUtils.should_receive(:rm).with(subject.pid_file)
|
54
|
+
subject.stop
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
context "without pid" do
|
59
|
+
it "does nothing" do
|
60
|
+
subject.stub(:pid => nil)
|
61
|
+
subject.stop.should be_nil
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
data/spec/spec.opts
CHANGED
@@ -1,2 +1,2 @@
|
|
1
1
|
--color
|
2
|
-
--
|
2
|
+
--format specdoc
|
metadata
CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
|
|
5
5
|
segments:
|
6
6
|
- 0
|
7
7
|
- 2
|
8
|
-
-
|
9
|
-
version: 0.2.
|
8
|
+
- 1
|
9
|
+
version: 0.2.1
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- Sandro Turriate
|
@@ -14,7 +14,7 @@ autorequire:
|
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
16
|
|
17
|
-
date: 2010-04-
|
17
|
+
date: 2010-04-21 00:00:00 -04:00
|
18
18
|
default_executable: specjour
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
@@ -104,7 +104,6 @@ files:
|
|
104
104
|
- lib/specjour/rspec.rb
|
105
105
|
- lib/specjour/rspec/distributed_formatter.rb
|
106
106
|
- lib/specjour/rspec/final_report.rb
|
107
|
-
- lib/specjour/rspec/marshalable_failure_formatter.rb
|
108
107
|
- lib/specjour/rspec/marshalable_rspec_failure.rb
|
109
108
|
- lib/specjour/rsync_daemon.rb
|
110
109
|
- lib/specjour/socket_helpers.rb
|
@@ -113,6 +112,7 @@ files:
|
|
113
112
|
- lib/specjour/worker.rb
|
114
113
|
- rails/init.rb
|
115
114
|
- spec/lib/specjour/worker_spec.rb
|
115
|
+
- spec/rsync_daemon_spec.rb
|
116
116
|
- spec/spec.opts
|
117
117
|
- spec/spec_helper.rb
|
118
118
|
- spec/specjour_spec.rb
|
@@ -148,5 +148,6 @@ specification_version: 3
|
|
148
148
|
summary: Distribute your spec suite amongst your LAN via Bonjour.
|
149
149
|
test_files:
|
150
150
|
- spec/lib/specjour/worker_spec.rb
|
151
|
+
- spec/rsync_daemon_spec.rb
|
151
152
|
- spec/spec_helper.rb
|
152
153
|
- spec/specjour_spec.rb
|
@@ -1,11 +0,0 @@
|
|
1
|
-
module Specjour::Rspec
|
2
|
-
class MarshalableFailureFormatter < Spec::Runner::Formatter::BaseTextFormatter
|
3
|
-
def dump_failure(counter, failure)
|
4
|
-
@output.puts
|
5
|
-
@output.puts "#{counter.to_s})"
|
6
|
-
@output.puts colorize_failure("#{failure.header}\n#{failure.message}", failure)
|
7
|
-
@output.puts format_backtrace(failure.backtrace)
|
8
|
-
@output.flush
|
9
|
-
end
|
10
|
-
end
|
11
|
-
end
|