nitra 0.9.4 → 0.9.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|