nitra 0.9.4 → 0.9.5
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +88 -0
- data/bin/nitra +2 -1
- data/lib/nitra/channel.rb +37 -35
- data/lib/nitra/command_line.rb +58 -38
- data/lib/nitra/configuration.rb +39 -17
- data/lib/nitra/ext/cucumber.rb +32 -0
- data/lib/nitra/formatter.rb +59 -0
- data/lib/nitra/master.rb +69 -14
- data/lib/nitra/progress.rb +19 -1
- data/lib/nitra/rails_tooling.rb +22 -0
- data/lib/nitra/runner.rb +125 -68
- data/lib/nitra/slave.rb +18 -6
- data/lib/nitra/tasks.rb +55 -0
- data/lib/nitra/utils.rb +30 -26
- data/lib/nitra/worker.rb +175 -92
- data/lib/nitra/workers/cucumber.rb +62 -0
- data/lib/nitra/workers/rspec.rb +65 -0
- data/lib/nitra.rb +5 -1
- data/spec/nitra/channel_spec.rb +44 -0
- data/spec/nitra/command_line_spec.rb +133 -0
- data/spec/nitra/configuration_spec.rb +60 -0
- data/spec/nitra/formatter.rb +126 -0
- data/spec/nitra/tasks_spec.rb +16 -0
- data/spec/nitra/worker_spec.rb +13 -0
- metadata +78 -46
- data/README +0 -3
- data/lib/nitra/client.rb +0 -43
data/README.md
ADDED
@@ -0,0 +1,88 @@
|
|
1
|
+
# Nitra
|
2
|
+
Nitra is a multi-process, optionally multi-server rspec and cucumber runner that uses forking to reduce memory usage and IPC to distribute builds amongst available CPUs efficiently.
|
3
|
+
|
4
|
+
## Philosophy
|
5
|
+
* Nitra attempts to do the simplest thing possible
|
6
|
+
* Nitra (ab)uses unix primitives where possible
|
7
|
+
* Nitra doesn't do thing that unix already does better (eg. rsync)
|
8
|
+
* IPC is accomplished via pipes and select
|
9
|
+
* Forking is used heavily for several reasons
|
10
|
+
* Running nitra locally should be easy
|
11
|
+
* Running nitra on a cluster should be easy too (though verbose)
|
12
|
+
* Config files are a nuisance and should be stuffed into rake files (deals with the verbosity)
|
13
|
+
|
14
|
+
## Usage
|
15
|
+
nitra [options] [spec_filename [...]]
|
16
|
+
-c, --cpus NUMBER Specify the number of CPUs to use on the host, or if specified after a --slave, on the slave
|
17
|
+
--cucumber Add full cucumber run, causes any files you list manually to be ignored
|
18
|
+
--debug Print debug output
|
19
|
+
-p, --print-failures Print failures immediately when they occur
|
20
|
+
-q, --quiet Quiet; don't display progress bar
|
21
|
+
--rake-after-runner task:1,task:2,task:3
|
22
|
+
The list of rake tasks to run, once per runner, in the runner's environment, just before the runner exits
|
23
|
+
--rake-before-runner task:1,task:2,task:3
|
24
|
+
The list of rake tasks to run, once per runner, in the runner's environment, after the runner starts
|
25
|
+
--rake-before-worker task:1,task:2,task:3
|
26
|
+
The list of rake tasks to run, once per worker, in the worker's environment, before the worker starts
|
27
|
+
--reset r
|
28
|
+
Reset database, equivalent to --rake-before-worker db:reset
|
29
|
+
--slave-mode Run in slave mode; ignores all other command-line options
|
30
|
+
--slave CONNECTION_COMMAND Provide a command that executes "nitra --slave-mode" on another host
|
31
|
+
--rspec Add full rspec run, causes any files you list manually to be ignored
|
32
|
+
-e, --environment ENV Set the RAILS_ENV to load
|
33
|
+
-h, --help Show this message
|
34
|
+
|
35
|
+
### Getting started
|
36
|
+
First things first add nitra to your Gemfile:
|
37
|
+
|
38
|
+
gem 'nitra'
|
39
|
+
|
40
|
+
Then run your specs locally across your cpu's cores:
|
41
|
+
|
42
|
+
bundle exec nitra --rspec
|
43
|
+
|
44
|
+
This will just run all your specs using the default number of cpu's reported by your system. Hyperthreaded intels will report a high number which might not be a good fit, you can tune this with the -c option.
|
45
|
+
|
46
|
+
Clustered commands run slightly differently. Effectively nitra will fork and exec a command that it expects will be a nitra slave, this means we can use ssh as our tunnel of choice. It looks something like this:
|
47
|
+
|
48
|
+
bundle exec nitra --rspec --slave "ssh your.server.name 'cd your/project && bundle exec nitra --slave-mode'"
|
49
|
+
|
50
|
+
When nitra --slave command it forks, execs it, and assumes it's another process that's running "nitra --slave-mode".
|
51
|
+
|
52
|
+
### Running a build cluster
|
53
|
+
Nitra doesn't prescribe how you get your code onto the other machines in your cluster. For example you can run a git checkout on the boxes you want to build on if it's the fastest way for you. For our part - we've had the most success with rsync.
|
54
|
+
|
55
|
+
Our build is run via rake tasks so we end up with a bunch of generated code to rsync files back and forth - here's a basic version that might help get you up and running:
|
56
|
+
|
57
|
+
namespace :nitra do
|
58
|
+
task :config do
|
59
|
+
@servers = [
|
60
|
+
{:name => "server1", :port => "66666", :c => "4"},
|
61
|
+
{:name => "server2", :port => "99999", :c => "2"},
|
62
|
+
{:name => "server3", :port => "77777", :c => "8"},
|
63
|
+
{:name => "server4", :port => "88888", :c => "4"}
|
64
|
+
]
|
65
|
+
end
|
66
|
+
|
67
|
+
desc "Sync the local directory and install the gems onto the remote servers"
|
68
|
+
task :prep => :config do
|
69
|
+
@servers.each do |server|
|
70
|
+
fork do
|
71
|
+
system "ssh -p #{server[:port]} #{server[:name]} 'mkdir -p nitra/projects/your_project'"
|
72
|
+
exec %(rsync -aze 'ssh -q -p #{server[:port]}' --exclude .git --delete ./ #{server[:name]}:nitra/projects/your_project/ && ssh -q -t -p #{server[:port]} #{server[:name]} 'cd nitra/projects/your_project && bundle install --quiet --path ~/gems')
|
73
|
+
end
|
74
|
+
end
|
75
|
+
Process.waitall
|
76
|
+
end
|
77
|
+
|
78
|
+
task :all => :config do
|
79
|
+
cmd = %(bundle exec nitra -p -q --rspec --cucumber --rake-before-worker db:reset)
|
80
|
+
@servers.each do |server|
|
81
|
+
cmd << %( --slave "ssh -p #{server[:port]} #{server[:name]} 'cd nitra/projects/your_project && bundle exec nitra --slave-mode'" -c#{server[:c]})
|
82
|
+
end
|
83
|
+
system cmd
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
## Copyright
|
88
|
+
Copyright 2012-2013 Roger Nesbitt, Powershop Limited, YouDo Limited. MIT licence.
|
data/bin/nitra
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
|
+
$LOAD_PATH.unshift File.expand_path(File.dirname(__FILE__) + '/../lib')
|
2
3
|
|
3
4
|
require 'nitra'
|
4
5
|
|
@@ -7,5 +8,5 @@ Nitra::CommandLine.new(configuration, ARGV)
|
|
7
8
|
if configuration.slave_mode
|
8
9
|
Nitra::Slave::Server.new.run
|
9
10
|
else
|
10
|
-
exit Nitra::
|
11
|
+
exit Nitra::Master.new(configuration, ARGV).run ? 0 : 1
|
11
12
|
end
|
data/lib/nitra/channel.rb
CHANGED
@@ -1,48 +1,50 @@
|
|
1
1
|
require 'yaml'
|
2
2
|
|
3
|
-
|
4
|
-
|
3
|
+
module Nitra
|
4
|
+
class Channel
|
5
|
+
ProtocolInvalidError = Class.new(StandardError)
|
5
6
|
|
6
|
-
|
7
|
-
|
7
|
+
attr_reader :rd, :wr
|
8
|
+
attr_accessor :raise_epipe_on_write_error
|
8
9
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
10
|
+
def initialize(rd, wr)
|
11
|
+
@rd = rd
|
12
|
+
@wr = wr
|
13
|
+
end
|
13
14
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
15
|
+
def self.pipe
|
16
|
+
c_rd, s_wr = IO.pipe
|
17
|
+
s_rd, c_wr = IO.pipe
|
18
|
+
[new(c_rd, c_wr), new(s_rd, s_wr)]
|
19
|
+
end
|
19
20
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
21
|
+
def self.read_select(channels)
|
22
|
+
fds = IO.select(channels.collect(&:rd))
|
23
|
+
fds.first.collect do |fd|
|
24
|
+
channels.detect {|c| c.rd == fd}
|
25
|
+
end
|
24
26
|
end
|
25
|
-
end
|
26
27
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
28
|
+
def close
|
29
|
+
rd.close
|
30
|
+
wr.close
|
31
|
+
end
|
31
32
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
33
|
+
def read
|
34
|
+
return unless line = rd.gets
|
35
|
+
if result = line.strip.match(/\ANITRA,(\d+)\z/)
|
36
|
+
data = rd.read(result[1].to_i)
|
37
|
+
YAML.load(data)
|
38
|
+
else
|
39
|
+
raise ProtocolInvalidError, "Expected nitra length line, got #{line.inspect}"
|
40
|
+
end
|
39
41
|
end
|
40
|
-
end
|
41
42
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
43
|
+
def write(data)
|
44
|
+
encoded = YAML.dump(data)
|
45
|
+
wr.write("NITRA,#{encoded.bytesize}\n#{encoded}")
|
46
|
+
rescue Errno::EPIPE
|
47
|
+
raise if raise_epipe_on_write_error
|
48
|
+
end
|
47
49
|
end
|
48
50
|
end
|
data/lib/nitra/command_line.rb
CHANGED
@@ -1,54 +1,74 @@
|
|
1
1
|
require 'optparse'
|
2
2
|
|
3
|
-
|
4
|
-
|
3
|
+
module Nitra
|
4
|
+
class CommandLine
|
5
|
+
attr_reader :configuration
|
5
6
|
|
6
|
-
|
7
|
-
|
7
|
+
def initialize(configuration, argv)
|
8
|
+
@configuration = configuration
|
8
9
|
|
9
|
-
|
10
|
-
|
10
|
+
OptionParser.new do |opts|
|
11
|
+
opts.banner = "Usage: nitra [options] [spec_filename [...]]"
|
11
12
|
|
12
|
-
|
13
|
-
|
14
|
-
|
13
|
+
opts.on("-c", "--cpus NUMBER", Integer, "Specify the number of CPUs to use on the host, or if specified after a --slave, on the slave") do |n|
|
14
|
+
configuration.set_process_count n
|
15
|
+
end
|
15
16
|
|
16
|
-
|
17
|
-
|
18
|
-
|
17
|
+
opts.on("--cucumber", "Add full cucumber run, causes any files you list manually to be ignored") do
|
18
|
+
configuration.add_framework "cucumber"
|
19
|
+
end
|
19
20
|
|
20
|
-
|
21
|
-
|
22
|
-
|
21
|
+
opts.on("--debug", "Print debug output") do
|
22
|
+
configuration.debug = true
|
23
|
+
end
|
23
24
|
|
24
|
-
|
25
|
-
|
26
|
-
|
25
|
+
opts.on("-p", "--print-failures", "Print failures immediately when they occur") do
|
26
|
+
configuration.print_failures = true
|
27
|
+
end
|
27
28
|
|
28
|
-
|
29
|
-
|
30
|
-
|
29
|
+
opts.on("-q", "--quiet", "Quiet; don't display progress bar") do
|
30
|
+
configuration.quiet = true
|
31
|
+
end
|
31
32
|
|
32
|
-
|
33
|
-
|
34
|
-
|
33
|
+
opts.on("--rake-after-runner task:1,task:2,task:3", Array, "The list of rake tasks to run, once per runner, in the runner's environment, just before the runner exits") do |rake_tasks|
|
34
|
+
configuration.add_rake_task(:after_runner, rake_tasks)
|
35
|
+
end
|
35
36
|
|
36
|
-
|
37
|
-
|
38
|
-
|
37
|
+
opts.on("--rake-before-runner task:1,task:2,task:3", Array, "The list of rake tasks to run, once per runner, in the runner's environment, after the runner starts") do |rake_tasks|
|
38
|
+
configuration.add_rake_task(:before_runner, rake_tasks)
|
39
|
+
end
|
39
40
|
|
40
|
-
|
41
|
-
|
42
|
-
|
41
|
+
opts.on("--rake-before-worker task:1,task:2,task:3", Array, "The list of rake tasks to run, once per worker, in the worker's environment, before the worker starts") do |rake_tasks|
|
42
|
+
configuration.add_rake_task(:before_worker, rake_tasks)
|
43
|
+
end
|
43
44
|
|
44
|
-
|
45
|
-
|
46
|
-
|
45
|
+
opts.on("-r", "--reset", "Reset database, equivalent to --rake-before-worker db:reset") do
|
46
|
+
configuration.add_rake_task(:before_worker, "db:reset")
|
47
|
+
end
|
47
48
|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
49
|
+
opts.on("--slave-mode", "Run in slave mode; ignores all other command-line options") do
|
50
|
+
configuration.slave_mode = true
|
51
|
+
end
|
52
|
+
|
53
|
+
opts.on("--slave CONNECTION_COMMAND", String, "Provide a command that executes \"nitra --slave-mode\" on another host") do |connection_command|
|
54
|
+
configuration.add_slave connection_command
|
55
|
+
end
|
56
|
+
|
57
|
+
opts.on("--rspec", "Add full rspec run, causes any files you list manually to be ignored") do
|
58
|
+
configuration.add_framework "rspec"
|
59
|
+
end
|
60
|
+
|
61
|
+
opts.on("-e", "--environment ENV", String, "Set the RAILS_ENV to load") do |env|
|
62
|
+
configuration.environment = env
|
63
|
+
end
|
64
|
+
|
65
|
+
opts.on_tail("-h", "--help", "Show this message") do
|
66
|
+
puts opts
|
67
|
+
exit
|
68
|
+
end
|
69
|
+
end.parse!(argv)
|
70
|
+
|
71
|
+
configuration.set_default_framework
|
72
|
+
end
|
53
73
|
end
|
54
74
|
end
|
data/lib/nitra/configuration.rb
CHANGED
@@ -1,22 +1,44 @@
|
|
1
|
-
|
2
|
-
attr_accessor :load_schema, :migrate, :debug, :quiet, :print_failures, :fork_for_each_file
|
3
|
-
attr_accessor :process_count, :environment, :slaves, :slave_mode
|
4
|
-
|
5
|
-
def initialize
|
6
|
-
self.environment = "nitra"
|
7
|
-
self.fork_for_each_file = true
|
8
|
-
self.slaves = []
|
9
|
-
end
|
1
|
+
require 'nitra/utils'
|
10
2
|
|
11
|
-
|
12
|
-
|
13
|
-
|
3
|
+
module Nitra
|
4
|
+
class Configuration
|
5
|
+
attr_accessor :debug, :quiet, :print_failures, :rake_tasks
|
6
|
+
attr_accessor :process_count, :environment, :slaves, :slave_mode, :framework, :frameworks
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
self.environment = "nitra"
|
10
|
+
self.slaves = []
|
11
|
+
self.rake_tasks = {}
|
12
|
+
self.frameworks = []
|
13
|
+
calculate_default_process_count
|
14
|
+
end
|
15
|
+
|
16
|
+
def add_framework(framework)
|
17
|
+
frameworks << framework
|
18
|
+
end
|
19
|
+
|
20
|
+
def add_rake_task(name, list)
|
21
|
+
rake_tasks[name] = list
|
22
|
+
end
|
23
|
+
|
24
|
+
def add_slave(command)
|
25
|
+
slaves << {:command => command, :cpus => nil}
|
26
|
+
end
|
27
|
+
|
28
|
+
def set_default_framework
|
29
|
+
self.framework = frameworks.first if frameworks.any?
|
30
|
+
end
|
31
|
+
|
32
|
+
def calculate_default_process_count
|
33
|
+
self.process_count ||= Nitra::Utils.processor_count
|
34
|
+
end
|
14
35
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
36
|
+
def set_process_count(n)
|
37
|
+
if slaves.empty?
|
38
|
+
self.process_count = n
|
39
|
+
else
|
40
|
+
slaves.last[:cpus] = n
|
41
|
+
end
|
20
42
|
end
|
21
43
|
end
|
22
44
|
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'cucumber/rb_support/rb_language'
|
2
|
+
require 'cucumber/runtime'
|
3
|
+
module Cucumber
|
4
|
+
module RbSupport
|
5
|
+
class RbLanguage
|
6
|
+
# Reloading support files is bad for us. Idealy we'd subclass but since
|
7
|
+
# Cucumber's internals are a bit shit and insists on using the new keyword
|
8
|
+
# everywhere we have to monkeypatch it out or spend another 6 months
|
9
|
+
# rewriting it and getting patches accepted...
|
10
|
+
def load_code_file(code_file)
|
11
|
+
require File.expand_path(code_file)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
class ResetableRuntime < Runtime
|
16
|
+
# Cucumber lacks a reset hook like the one Rspec has so we need to patch one in...
|
17
|
+
# Call this after configure so that the correct configuration is used to create the result set.
|
18
|
+
def reset
|
19
|
+
@results = Results.new(nil)
|
20
|
+
end
|
21
|
+
|
22
|
+
# Cucumber > 1.1.0 memoizes @loader which means we can't load in new files.
|
23
|
+
# Patch it back to how it used to work.
|
24
|
+
def features
|
25
|
+
@loader = Runtime::FeaturesLoader.new(
|
26
|
+
@configuration.feature_files,
|
27
|
+
@configuration.filters,
|
28
|
+
@configuration.tag_expression)
|
29
|
+
@loader.features
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
##
|
2
|
+
# Nitra::Formatter print out data regarding the run.
|
3
|
+
#
|
4
|
+
module Nitra
|
5
|
+
class Formatter
|
6
|
+
attr_accessor :start_time, :progress, :configuration
|
7
|
+
|
8
|
+
def initialize(progress, configuration)
|
9
|
+
self.progress = progress
|
10
|
+
self.configuration = configuration
|
11
|
+
end
|
12
|
+
|
13
|
+
def start
|
14
|
+
self.start_time = Time.now
|
15
|
+
print_progress
|
16
|
+
end
|
17
|
+
|
18
|
+
def print_progress
|
19
|
+
print_failures
|
20
|
+
print_bar
|
21
|
+
end
|
22
|
+
|
23
|
+
def finish
|
24
|
+
puts progress.filtered_output
|
25
|
+
|
26
|
+
puts "\n#{overview}"
|
27
|
+
puts "#{$aborted ? "Aborted after" : "Finished in"} #{"%0.1f" % (Time.now-start_time)} seconds"
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
##
|
33
|
+
# Print the progress bar, doesn't do anything if we're in debug.
|
34
|
+
#
|
35
|
+
def print_bar
|
36
|
+
return if configuration.quiet || configuration.debug || progress.file_count == 0
|
37
|
+
total = 50
|
38
|
+
completed = (progress.files_completed / progress.file_count.to_f * total).to_i
|
39
|
+
print "\r[#{"X" * completed}#{"." * (total - completed)}] #{overview}\r"
|
40
|
+
$stdout.flush
|
41
|
+
end
|
42
|
+
|
43
|
+
##
|
44
|
+
# Prints the output in the progress object and resets it if we've got eager printing turned on.
|
45
|
+
#
|
46
|
+
def print_failures
|
47
|
+
return unless progress.output.length > 0 && configuration.print_failures
|
48
|
+
puts progress.filtered_output
|
49
|
+
progress.output = ""
|
50
|
+
end
|
51
|
+
|
52
|
+
##
|
53
|
+
# A simple overview of files processed so far and success/failure numbers.
|
54
|
+
#
|
55
|
+
def overview
|
56
|
+
"#{progress.files_completed}/#{progress.file_count} files | #{progress.example_count} examples, #{progress.failure_count} failures"
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
data/lib/nitra/master.rb
CHANGED
@@ -1,18 +1,24 @@
|
|
1
1
|
class Nitra::Master
|
2
|
-
attr_reader :configuration, :files
|
2
|
+
attr_reader :configuration, :files, :frameworks, :current_framework
|
3
3
|
|
4
4
|
def initialize(configuration, files = nil)
|
5
5
|
@configuration = configuration
|
6
|
-
@
|
6
|
+
@frameworks = configuration.frameworks
|
7
|
+
if @frameworks.any?
|
8
|
+
load_files_from_framework_list
|
9
|
+
else
|
10
|
+
map_files_to_frameworks(files)
|
11
|
+
end
|
12
|
+
@current_framework = @frameworks.shift
|
13
|
+
@configuration.framework = @current_framework
|
7
14
|
end
|
8
15
|
|
9
16
|
def run
|
10
|
-
|
11
|
-
return if files.empty?
|
17
|
+
return if files_remaining == 0
|
12
18
|
|
13
19
|
progress = Nitra::Progress.new
|
14
|
-
progress.file_count =
|
15
|
-
|
20
|
+
progress.file_count = files_remaining
|
21
|
+
formatter = Nitra::Formatter.new(progress, configuration)
|
16
22
|
|
17
23
|
runners = []
|
18
24
|
|
@@ -27,24 +33,40 @@ class Nitra::Master
|
|
27
33
|
end
|
28
34
|
|
29
35
|
slave = Nitra::Slave::Client.new(configuration)
|
30
|
-
runners += slave.connect
|
36
|
+
runners += slave.connect
|
37
|
+
|
38
|
+
formatter.start
|
31
39
|
|
32
40
|
while runners.length > 0
|
33
41
|
Nitra::Channel.read_select(runners).each do |channel|
|
34
42
|
if data = channel.read
|
35
43
|
case data["command"]
|
36
44
|
when "next"
|
37
|
-
|
45
|
+
if files_remaining == 0
|
46
|
+
channel.write "command" => "drain"
|
47
|
+
elsif data["framework"] == current_framework
|
48
|
+
channel.write "command" => "file", "filename" => next_file
|
49
|
+
else
|
50
|
+
channel.write "command" => "framework", "framework" => current_framework
|
51
|
+
end
|
52
|
+
|
38
53
|
when "result"
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
progress.
|
43
|
-
|
54
|
+
examples = data["example_count"] || 0
|
55
|
+
failures = data["failure_count"] || 0
|
56
|
+
failure = data["return_code"].to_i != 0
|
57
|
+
progress.file_progress(examples, failures, failure, data["text"])
|
58
|
+
formatter.print_progress
|
59
|
+
|
60
|
+
when "error"
|
61
|
+
progress.fail("ERROR " + data["process"] + " " + data["text"])
|
62
|
+
formatter.progress
|
63
|
+
runners.delete channel
|
64
|
+
|
44
65
|
when "debug"
|
45
66
|
if configuration.debug
|
46
67
|
puts "[DEBUG] #{data["text"]}"
|
47
68
|
end
|
69
|
+
|
48
70
|
when "stdout"
|
49
71
|
if configuration.debug
|
50
72
|
puts "STDOUT for #{data["process"]} #{data["filename"]}:\n#{data["text"]}" unless data["text"].empty?
|
@@ -58,11 +80,44 @@ class Nitra::Master
|
|
58
80
|
|
59
81
|
debug "waiting for all children to exit..."
|
60
82
|
Process.waitall
|
61
|
-
|
83
|
+
|
84
|
+
formatter.finish
|
85
|
+
|
86
|
+
!$aborted && progress.files_completed == progress.file_count && progress.failure_count.zero? && !progress.failure
|
62
87
|
end
|
63
88
|
|
64
89
|
protected
|
65
90
|
def debug(*text)
|
66
91
|
puts "master: #{text.join}" if configuration.debug
|
67
92
|
end
|
93
|
+
|
94
|
+
def map_files_to_frameworks(files)
|
95
|
+
@files = files.group_by do |filename|
|
96
|
+
framework_name, framework_class = Nitra::Workers::Worker.worker_classes.find {|framework_name, framework_class| framework_class.filename_match?(filename)}
|
97
|
+
framework_name
|
98
|
+
end
|
99
|
+
@frameworks = @files.keys
|
100
|
+
end
|
101
|
+
|
102
|
+
def load_files_from_framework_list
|
103
|
+
@files = frameworks.inject({}) do |result, framework_name|
|
104
|
+
result[framework_name] = Nitra::Workers::Worker.worker_classes[framework_name].files
|
105
|
+
result
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
def files_remaining
|
110
|
+
files.values.inject(0) {|sum, filenames| sum + filenames.length}
|
111
|
+
end
|
112
|
+
|
113
|
+
def current_framework_files
|
114
|
+
files[current_framework]
|
115
|
+
end
|
116
|
+
|
117
|
+
def next_file
|
118
|
+
raise if files_remaining == 0
|
119
|
+
file = current_framework_files.shift
|
120
|
+
@current_framework = frameworks.shift if current_framework_files.length == 0
|
121
|
+
file
|
122
|
+
end
|
68
123
|
end
|
data/lib/nitra/progress.rb
CHANGED
@@ -1,8 +1,26 @@
|
|
1
1
|
class Nitra::Progress
|
2
|
-
attr_accessor :file_count, :files_completed, :example_count, :failure_count, :output
|
2
|
+
attr_accessor :file_count, :files_completed, :example_count, :failure_count, :output, :failure
|
3
3
|
|
4
4
|
def initialize
|
5
5
|
@file_count = @files_completed = @example_count = @failure_count = 0
|
6
6
|
@output = ""
|
7
|
+
@failure = false
|
8
|
+
end
|
9
|
+
|
10
|
+
def file_progress(examples, failures, failure, text)
|
11
|
+
self.files_completed += 1
|
12
|
+
self.example_count += examples
|
13
|
+
self.failure_count += failures
|
14
|
+
self.failure ||= failure
|
15
|
+
self.output.concat text
|
16
|
+
end
|
17
|
+
|
18
|
+
def fail(message)
|
19
|
+
self.failure = true
|
20
|
+
self.output.concat message
|
21
|
+
end
|
22
|
+
|
23
|
+
def filtered_output
|
24
|
+
output.gsub(/\n\n\n+/, "\n\n")
|
7
25
|
end
|
8
26
|
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Nitra
|
2
|
+
class RailsTooling
|
3
|
+
##
|
4
|
+
# Find the database config for the current TEST_ENV_NUMBER and manually initialise a connection.
|
5
|
+
#
|
6
|
+
def self.connect_to_database
|
7
|
+
return unless defined?(Rails)
|
8
|
+
# Config files are read at load time. Since we load rails in one env then
|
9
|
+
# change some flags to get different environments through forking we need
|
10
|
+
# always reload our database config...
|
11
|
+
ActiveRecord::Base.configurations = YAML.load(ERB.new(IO.read("#{Rails.root}/config/database.yml")).result)
|
12
|
+
|
13
|
+
ActiveRecord::Base.clear_all_connections!
|
14
|
+
ActiveRecord::Base.establish_connection
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.reset_cache
|
18
|
+
return unless defined?(Rails)
|
19
|
+
Rails.cache.reset if Rails.cache.respond_to?(:reset)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|