specjour 0.7.0 → 2.0.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. checksums.yaml +7 -0
  2. data/History.markdown +12 -0
  3. data/README.markdown +24 -1
  4. data/Rakefile +12 -12
  5. data/bin/specjour +3 -1
  6. data/lib/specjour/cli.rb +86 -110
  7. data/lib/specjour/colors.rb +23 -0
  8. data/lib/specjour/configuration.rb +47 -91
  9. data/lib/specjour/connection.rb +69 -20
  10. data/lib/specjour/cpu.rb +4 -0
  11. data/lib/specjour/fork.rb +1 -1
  12. data/lib/specjour/formatter.rb +153 -0
  13. data/lib/specjour/listener.rb +181 -0
  14. data/lib/specjour/loader.rb +55 -119
  15. data/lib/specjour/logger.rb +34 -0
  16. data/lib/specjour/plugin/base.rb +61 -0
  17. data/lib/specjour/plugin/manager.rb +28 -0
  18. data/lib/specjour/plugin/rails.rb +47 -0
  19. data/lib/specjour/plugin/rails_v3.rb +23 -0
  20. data/lib/specjour/plugin/rails_v4.rb +25 -0
  21. data/lib/specjour/plugin/rspec.rb +160 -0
  22. data/lib/specjour/plugin/rspec_v2.rb +53 -0
  23. data/lib/specjour/plugin/rspec_v3.rb +59 -0
  24. data/lib/specjour/plugin/ssh.rb +24 -0
  25. data/lib/specjour/plugin.rb +4 -0
  26. data/lib/specjour/printer.rb +235 -67
  27. data/lib/specjour/protocol.rb +13 -6
  28. data/lib/specjour/rspec_formatter.rb +17 -0
  29. data/lib/specjour/rsync_daemon.rb +6 -3
  30. data/lib/specjour/socket_helper.rb +26 -10
  31. data/lib/specjour/worker.rb +36 -62
  32. data/lib/specjour.rb +50 -24
  33. data/lib/specjour_plugin.rb +5 -0
  34. metadata +52 -84
  35. data/lib/specjour/cucumber/distributed_formatter.rb +0 -82
  36. data/lib/specjour/cucumber/final_report.rb +0 -83
  37. data/lib/specjour/cucumber/preloader.rb +0 -22
  38. data/lib/specjour/cucumber/runner.rb +0 -15
  39. data/lib/specjour/cucumber.rb +0 -16
  40. data/lib/specjour/db_scrub.rb +0 -56
  41. data/lib/specjour/dispatcher.rb +0 -170
  42. data/lib/specjour/manager.rb +0 -174
  43. data/lib/specjour/rspec/distributed_formatter.rb +0 -50
  44. data/lib/specjour/rspec/final_report.rb +0 -73
  45. data/lib/specjour/rspec/marshalable_exception.rb +0 -19
  46. data/lib/specjour/rspec/preloader.rb +0 -15
  47. data/lib/specjour/rspec/runner.rb +0 -14
  48. data/lib/specjour/rspec/shared_example_group_ext.rb +0 -9
  49. data/lib/specjour/rspec.rb +0 -17
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 2b2e786f14b5e926503fdadd4a4504fc188ded2c
4
+ data.tar.gz: e2de43dfdccc720d0befdad67fd51ffc239cc63a
5
+ SHA512:
6
+ metadata.gz: 0228128342542bf82575bcbd36a1a00e42852dec83735dbdb1b46186647eec849b11cb7fae53294b6087250af505ac28a60b7406f38926133e4eb1b6ffe8f195
7
+ data.tar.gz: 1db0922eac8c9552bade26f9669bc9149393e780741ffb4d359d70a598d83c322ba250ad2fb61de783926a24c628436ff76e6f241d39e6f18561f2d59027b2bc
data/History.markdown CHANGED
@@ -1,6 +1,18 @@
1
1
  History
2
2
  =======
3
3
 
4
+ 0.7.1 / (master)
5
+ ---------------------------
6
+ * [fixed] printer exit\_status returns false if there are no reporters
7
+ * [fixed] regression when running a subdirectory. Specjour was loading all
8
+ specs, even those outside of the default spec directory, i.e. a fast\_specs/
9
+ directory.
10
+ * [fixed] "file has vanished" bug. The specjour listener can now transition
11
+ between networks without restarts.
12
+ * [fixed] before(:all). Specjour now distributes before(:all) as a group.
13
+ Previously, each example would be distributed alone, effectively turning
14
+ before(:all) into before(:each).
15
+
4
16
  0.7.0 / 2012-11-21
5
17
  ---------------------------
6
18
  * [added] Cucumber now distributes individual scenarios instead of files
data/README.markdown CHANGED
@@ -1,6 +1,23 @@
1
1
  # Specjour
2
2
 
3
- ## FUCK SETI. Run specs with your spare CPU cycles.
3
+ ## Notes
4
+ Listener daemonized or foregrounded
5
+ forks a loader
6
+ loader should load, setsid, then exit
7
+ all workers then have the same sid and gid with no parent pid
8
+ worker pgid = 100 sid = 100
9
+ webkit_server
10
+ worker pgid = 100 sid = 100
11
+ webkit_server
12
+
13
+ performance.txt
14
+ printing informational messages
15
+ single specjour command starts daemon
16
+ interrupts
17
+ handle no careful_test database
18
+ bonjour announce project names
19
+ printer#project_name aliases
20
+
4
21
 
5
22
  ## Instructions
6
23
 
@@ -20,6 +37,12 @@ by [parallel\_tests](http://github.com/grosser/parallel_tests)):
20
37
  database: project_name_test<%=ENV['TEST_ENV_NUMBER']%>
21
38
 
22
39
  ## Give it a try
40
+
41
+ source .dev
42
+ bin/specjour listen -f -l
43
+ bin/specjour spec/
44
+
45
+
23
46
  Run `specjour` to start a dispatcher, manager, and multiple workers in the same
24
47
  terminal window.
25
48
 
data/Rakefile CHANGED
@@ -1,21 +1,21 @@
1
1
  require 'bundler/gem_tasks'
2
2
  require "bundler/setup"
3
3
 
4
- require 'rspec/core/rake_task'
5
- RSpec::Core::RakeTask.new(:spec)
4
+ # require 'rspec/core/rake_task'
5
+ # RSpec::Core::RakeTask.new(:spec)
6
6
 
7
- RSpec::Core::RakeTask.new(:rcov)
7
+ # RSpec::Core::RakeTask.new(:rcov)
8
8
 
9
- task :default => :spec
9
+ # task :default => :spec
10
10
 
11
- begin
12
- require 'yard'
13
- YARD::Rake::YardocTask.new
14
- rescue LoadError
15
- task :yardoc do
16
- abort "YARD is not available. In order to run yardoc, you must: sudo gem install yard"
17
- end
18
- end
11
+ # begin
12
+ # require 'yard'
13
+ # YARD::Rake::YardocTask.new
14
+ # rescue LoadError
15
+ # task :yardoc do
16
+ # abort "YARD is not available. In order to run yardoc, you must: sudo gem install yard"
17
+ # end
18
+ # end
19
19
 
20
20
  desc "tag, push gem, push to github"
21
21
  task :prerelease do
data/bin/specjour CHANGED
@@ -2,4 +2,6 @@
2
2
  require 'specjour'
3
3
 
4
4
  $PROGRAM_NAME = File.basename(__FILE__)
5
- Specjour::CLI.start
5
+ Specjour.load_plugins
6
+ Specjour.load_custom_hooks
7
+ Specjour::CLI.new.start
data/lib/specjour/cli.rb CHANGED
@@ -1,135 +1,111 @@
1
1
  module Specjour
2
- require 'thor'
3
- class CLI < Thor
2
+ require 'optparse'
3
+ class CLI
4
+ include Logger
4
5
 
5
- def self.worker_option
6
- method_option :workers, :aliases => "-w", :type => :numeric, :desc => "Number of concurent processes to run. Defaults to your system's available cores."
7
- end
6
+ COMMANDS = %w(listen ls stop)
8
7
 
9
- def self.dispatcher_option
10
- method_option :alias, :aliases => "-a", :desc => "Project name advertised to listeners"
11
- end
8
+ attr_accessor :options
12
9
 
13
- def self.rsync_port_option
14
- method_option :rsync_port, :type => :numeric, :default => 23456, :desc => "Port to use for rsync daemon"
10
+ def initialize
11
+ self.options = {}
15
12
  end
16
13
 
17
- # allow specjour to be called with path arguments
18
- def self.start(original_args=ARGV, config={})
19
- Specjour.trap_interrupt
20
- real_tasks = all_tasks.keys | @map.keys
21
- unless real_tasks.include? original_args.first
22
- original_args.unshift default_task
14
+ def stop_running_listener
15
+ listener = Listener.new
16
+ if listener.started?
17
+ listener.stop
23
18
  end
24
- super(original_args)
19
+ listener
25
20
  end
26
21
 
27
- default_task :dispatch
28
-
29
- class_option :log, :aliases => "-l", :type => :boolean, :desc => "Print debug messages to $stderr"
30
-
31
- desc "listen", "Listen for incoming tests to run"
32
- long_desc <<-DESC
33
- Advertise availability to run tests for the current directory.
34
- DESC
35
- worker_option
36
- rsync_port_option
37
- method_option :projects, :aliases => "-p", :type => :array, :desc => "Projects supported by this listener"
38
- def listen
39
- handle_logging
40
- handle_workers
41
- params[:registered_projects] = params.delete(:projects) || [File.basename(Dir.pwd)]
42
- append_to_program_name "listen"
43
- Specjour::Manager.new(params).start
22
+ def start
23
+ Specjour.trap_interrupt_with_exit
24
+ parser.parse!
25
+ append_to_program_name(ARGV[0])
26
+ ensure_alias
27
+ case ARGV[0]
28
+ when "listen"
29
+ listener = stop_running_listener
30
+ listener.daemonize unless options[:foreground]
31
+ listener.start
32
+ when "ls"
33
+ puts "Plugins:"
34
+ puts Specjour.plugin_manager.plugins
35
+ when "stop"
36
+ listener = Listener.new
37
+ if listener.started?
38
+ puts "Stopping listener with pid #{listener.pid}"
39
+ listener.stop
40
+ else
41
+ abort("No listener found")
42
+ end
43
+ when "help"
44
+ abort("Commands are: #{COMMANDS.join(" ")}")
45
+ else
46
+ test_paths = ARGV[0..-1]
47
+ if options[:workers]
48
+ stop_running_listener
49
+ if options[:workers] > 0
50
+ Listener.ensure_started
51
+ end
52
+ else
53
+ Listener.ensure_started
54
+ end
55
+ printer = Printer.new test_paths: Array(test_paths)
56
+ printer.announce
57
+ printer.start_rsync
58
+ printer.start
59
+ end
44
60
  end
45
61
 
46
- desc "load", "load the app, then fork workers", :hide => true
47
- worker_option
48
- method_option :printer_uri, :required => true
49
- method_option :project_path, :required => true
50
- method_option :task, :required => true
51
- method_option :test_paths, :type => :array, :default => []
52
- method_option :quiet, :type => :boolean, :default => false
53
- def load
54
- handle_logging
55
- handle_workers
56
- append_to_program_name "load"
57
- Specjour::Loader.new(params).start
62
+ def ensure_alias
63
+ if Specjour.configuration.project_aliases.empty?
64
+ Specjour.configuration.project_aliases = [File.basename(Dir.pwd)]
65
+ end
58
66
  end
59
67
 
60
- desc "dispatch [test_paths]", "Send tests to a listener"
61
- worker_option
62
- dispatcher_option
63
- rsync_port_option
64
- long_desc <<-DESC
65
- This is run when you simply type `specjour`.
66
- By default, it will run the specs and features found in the current directory.
67
- If you like, you can run a subset of tests by specifying the folder containing the tests.\n
68
- Examples\n
69
- `specjour dispatch spec`\n
70
- `specjour dispatch features`\n
71
- `specjour dispatch spec/models features/sign_up.feature`\n
72
- DESC
73
- def dispatch(*paths)
74
- handle_logging
75
- handle_workers
76
- handle_dispatcher(paths)
77
- append_to_program_name "dispatch"
78
- Specjour::Dispatcher.new(params).start
79
- end
68
+ def parser
69
+ @parser ||= OptionParser.new do |parser|
70
+ parser.banner = "Usage: specjour [command] [options] [files or directories]\n\nCommands are #{COMMANDS.join(",")}\n\n"
80
71
 
81
- desc "prepare [PROJECT_PATH]", "Run the prepare task on all listening workers"
82
- long_desc <<-DESC
83
- Run the Specjour::Configuration.prepare block on all listening workers.
84
- Defaults to dropping the database, then loading the schema.
85
- DESC
86
- worker_option
87
- dispatcher_option
88
- rsync_port_option
89
- def prepare(path = Dir.pwd)
90
- handle_logging
91
- handle_workers
92
- params[:project_path] = File.expand_path(path)
93
- params[:project_alias] = params.delete(:alias)
94
- params[:test_paths] = []
95
- params[:worker_task] = 'prepare'
96
- append_to_program_name "prepare"
97
- Specjour::Dispatcher.new(params).start
98
- end
72
+ parser.on('-b', '--backtrace', 'Include specjour in the backtrace (do not scrub backtrace)') do |o|
73
+ options[:full_backtrace] = true
74
+ Specjour.configuration.full_backtrace = true
75
+ end
99
76
 
100
- map %w(-v --version) => :version
101
- desc "version", "Show the current version"
102
- def version
103
- puts Specjour::VERSION
104
- end
77
+ parser.on("-l", "--log", "Enable informational logging") do
78
+ Specjour.new_logger ::Logger::INFO
79
+ end
105
80
 
106
- protected
81
+ parser.on("-d", "--debug", "Enable debug logging") do
82
+ Specjour.new_logger ::Logger::DEBUG
83
+ end
107
84
 
108
- def append_to_program_name(command)
109
- $PROGRAM_NAME = "#{$PROGRAM_NAME} #{command}"
110
- end
85
+ parser.on("-f", "--foreground", "Foreground the listener") do |option|
86
+ options[:foreground] = option
87
+ end
111
88
 
112
- def params
113
- @params ||= options.dup
114
- end
89
+ parser.on("-w", "--workers NUM", Numeric, "Number of workers") do |option|
90
+ options[:workers] = option.to_i
91
+ Specjour.configuration.worker_size = options[:workers]
92
+ end
115
93
 
116
- def handle_logging
117
- Specjour.new_logger(Logger::DEBUG) if options['log']
118
- end
94
+ parser.on("-a", "--alias NAME", Array, "Project name alias") do |option|
95
+ Specjour.configuration.project_aliases = option
96
+ end
119
97
 
120
- def handle_workers
121
- params[:worker_size] = options["workers"] || CPU.cores
98
+ parser.on("-v", "--version", "Version number") do |option|
99
+ puts Specjour::VERSION
100
+ exit
101
+ end
102
+ end
122
103
  end
123
104
 
124
- def handle_dispatcher(paths)
125
- if paths.empty?
126
- params[:project_path] = Dir.pwd
127
- else
128
- params[:project_path] = File.expand_path(paths.first.sub(/(spec|features).*$/, ''))
129
- end
130
- params[:test_paths] = paths
131
- params[:project_alias] = params.delete(:alias)
132
- raise ArgumentError, "Cannot dispatch line numbers" if paths.any? {|p| p =~ /:\d+/}
105
+ private
106
+
107
+ def append_to_program_name(command)
108
+ $PROGRAM_NAME = "#{$PROGRAM_NAME} #{command}"
133
109
  end
134
110
  end
135
111
  end
@@ -0,0 +1,23 @@
1
+ module Specjour
2
+ module Colors
3
+
4
+ VT100_COLORS = {
5
+ :black => 30,
6
+ :red => 31,
7
+ :green => 32,
8
+ :yellow => 33,
9
+ :blue => 34,
10
+ :magenta => 35,
11
+ :cyan => 36,
12
+ :white => 37
13
+ }
14
+
15
+ def colorize(text, color)
16
+ if output.tty?
17
+ "\e[#{VT100_COLORS[color]}m#{text}\e[0m"
18
+ else
19
+ text
20
+ end
21
+ end
22
+ end
23
+ end
@@ -1,105 +1,61 @@
1
1
  module Specjour
2
- module Configuration
3
- extend self
4
-
5
- attr_writer :before_fork, :after_fork, :after_load, :prepare, :rspec_formatter, :rsync_options
6
-
7
- # This block is run by each worker before they begin running tests.
8
- # The default action is to migrate the database, and clear it of any old
9
- # data.
10
- def after_fork
11
- @after_fork ||= default_after_fork
12
- end
13
-
14
- # This block is run after the manager loads the app into memory, but before
15
- # forking new worker processes. The default action is to disconnect from
16
- # the ActiveRecord database.
17
- def after_load
18
- @after_load ||= default_after_load
19
- end
20
-
21
- # This block is run by the manager before forking workers. The default
22
- # action is to run bundle install.
23
- def before_fork
24
- @before_fork ||= default_before_fork
25
- end
26
-
27
- # This block is run on all workers when invoking `specjour prepare`
28
- # Defaults to dropping the worker's database and recreating it. This
29
- # is especially useful when two teams are sharing workers and writing
30
- # migrations at around the same time causing databases to get out of sync.
31
- def prepare
32
- @prepare ||= default_prepare
33
- end
34
-
35
- def reset
36
- @before_fork = nil
37
- @after_fork = nil
38
- @after_load = nil
39
- @prepare = nil
40
- @rsync_options = nil
41
- @rspec_formatter = nil
42
- end
43
-
44
- def rspec_formatter
45
- @rspec_formatter ||= default_rspec_formatter
46
- end
47
-
48
- def rsync_options
49
- @rsync_options ||= default_rsync_options
50
- end
51
-
52
- def bundle_install
53
- if system('which bundle')
54
- system('bundle check') || system('bundle install')
55
- end
56
- end
57
-
58
- def default_before_fork
59
- lambda do
60
- bundle_install
61
- end
62
- end
63
-
64
- def default_after_fork
65
- lambda do
66
- DbScrub.scrub if rails_with_ar?
67
- end
68
- end
69
-
70
- def default_after_load
71
- lambda do
72
- ActiveRecord::Base.remove_connection if rails_with_ar?
73
- end
74
- end
75
-
76
- def default_prepare
77
- lambda do
78
- if rails_with_ar?
79
- DbScrub.drop
80
- DbScrub.scrub
2
+ class Configuration
3
+ attr_accessor :options
4
+
5
+ DEFAULT_BACKTRACE_EXCLUSION = Regexp.union([
6
+ "/lib/specjour/",
7
+ /lib\/rspec\/(core|expectations|matchers|mocks)/,
8
+ "/gems/",
9
+ "spec/spec_helper.rb",
10
+ "spec/rails_helper.rb",
11
+ "bin/"
12
+ ]).freeze
13
+
14
+ DEFAULT_OPTIONS = {
15
+ backtrace_exclusion_pattern: DEFAULT_BACKTRACE_EXCLUSION,
16
+ formatter: Formatter.new,
17
+ full_backtrace: false,
18
+ printer_port: nil,
19
+ printer_uri: nil,
20
+ project_aliases: [],
21
+ project_name: nil,
22
+ project_path: nil,
23
+ remote_job: nil,
24
+ rsync_options: "-aL --delete --ignore-errors",
25
+ rsync_port: 23456,
26
+ test_paths: nil,
27
+ tmp_path: "/tmp",
28
+ worker_size: lambda { Specjour.configuration.remote_job ? CPU.half_cores : CPU.cores },
29
+ worker_number: 0
30
+ }.freeze
31
+
32
+ def self.make_option(name)
33
+ define_method(name) do
34
+ option = @options[name]
35
+ if option.respond_to?(:call)
36
+ option.call()
37
+ else
38
+ option
81
39
  end
82
40
  end
83
- end
84
41
 
85
- def default_rspec_formatter
86
- lambda do
87
- ::RSpec::Core::Formatters::ProgressFormatter
42
+ define_method("#{name}=") do |value|
43
+ @options[name] = value
88
44
  end
89
45
  end
90
46
 
91
- def default_rsync_options
92
- "-aL --delete --ignore-errors"
47
+ DEFAULT_OPTIONS.each do |k,v|
48
+ make_option(k)
93
49
  end
94
50
 
95
- protected
96
-
97
- def rails_with_ar?
98
- defined?(Rails) && defined?(ActiveRecord::Base)
51
+ def initialize(options={})
52
+ @original_options = options
53
+ set_options
99
54
  end
100
55
 
101
- def system(cmd)
102
- Kernel.system("#{cmd} > /dev/null")
56
+ def set_options
57
+ @options = DEFAULT_OPTIONS.merge @original_options
103
58
  end
59
+
104
60
  end
105
61
  end
@@ -1,12 +1,13 @@
1
1
  module Specjour
2
2
  class Connection
3
+ include Logger
3
4
  include Protocol
4
5
  extend Forwardable
5
6
 
6
7
  attr_reader :uri, :retries
7
8
  attr_writer :socket
8
9
 
9
- def_delegators :socket, :flush, :close, :closed?, :gets, :each
10
+ def_delegators :socket, :flush, :close, :closed?, :gets, :puts, :each, :eof?, :tty?
10
11
 
11
12
  def self.wrap(established_connection)
12
13
  host, port = established_connection.peeraddr.values_at(3,1)
@@ -23,58 +24,106 @@ module Specjour
23
24
  alias to_str to_s
24
25
 
25
26
  def connect
27
+ debug "connecting to socket #{host}:#{port}"
26
28
  timeout { connect_socket }
27
29
  end
28
30
 
29
31
  def disconnect
30
- socket.close if socket && !socket.closed?
32
+ if socket && !socket.closed?
33
+ debug "closing socket"
34
+ socket.close
35
+ end
36
+ end
37
+
38
+ def host
39
+ uri.host
40
+ end
41
+
42
+ def port
43
+ uri.port
31
44
  end
32
45
 
33
46
  def socket
34
47
  @socket ||= connect
35
48
  end
36
49
 
50
+ def add_to_profiler(test, time, host)
51
+ send_command("add_to_profiler", test, time, host)
52
+ end
53
+
54
+ def done
55
+ send_command("done")
56
+ end
57
+
58
+ def error(exception)
59
+ prefix = if n = ENV["TEST_ENV_NUMBER"]
60
+ "[#{n}]"
61
+ else
62
+ ""
63
+ end
64
+ send_command("error", "#{prefix}#{exception.inspect}\n#{exception.backtrace.join("\n")}")
65
+ rescue => error
66
+ $stderr.puts "Error sending error to server: #{error.inspect}"
67
+ $stderr.puts error.backtrace
68
+ end
69
+
70
+ def report_test(test)
71
+ send_command("report_test", test)
72
+ end
73
+
37
74
  def next_test
38
- will_reconnect do
39
- send_message(:ready)
40
- load_object socket.gets(TERMINATOR)
41
- end
75
+ send_recv_command("next_test")
42
76
  end
43
77
 
44
- def print(arg)
78
+ def ready(info)
79
+ send_recv_command("ready", info)
80
+ end
81
+
82
+ def reconnect
83
+ socket.close unless socket.closed?
84
+ connect
85
+ end
86
+
87
+ def register_tests(tests)
88
+ send_command("register_tests", tests)
89
+ end
90
+
91
+ def send_server_done(signal)
92
+ send_command("server_done", signal)
93
+ end
94
+
95
+ def get_server_done
45
96
  will_reconnect do
46
- socket.print dump_object(arg)
97
+ data = recv_data
98
+ if data[:command] == "server_done"
99
+ data[:args].first
100
+ end
47
101
  end
48
102
  end
49
103
 
50
- def puts(arg='')
104
+ def send_command(method_name, *args)
51
105
  will_reconnect do
52
- print(arg << "\n")
106
+ send_data command: method_name, args: args
53
107
  end
54
108
  end
55
109
 
56
- def send_message(method_name, *args)
110
+ def send_recv_command(method_name, *args)
111
+ send_command(method_name, *args)
57
112
  will_reconnect do
58
- print([method_name, *args])
59
- flush
113
+ recv_data
60
114
  end
61
115
  end
62
116
 
63
117
  protected
64
118
 
65
119
  def connect_socket
66
- @socket = TCPSocket.open(uri.host, uri.port)
120
+ @socket = TCPSocket.open(host, port)
67
121
  rescue Errno::ECONNREFUSED => error
68
122
  retry
69
123
  end
70
124
 
71
- def reconnect
72
- socket.close unless socket.closed?
73
- connect
74
- end
75
-
76
125
  def timeout(&block)
77
- Timeout.timeout(1.0, &block)
126
+ Timeout.timeout(0.2, &block)
78
127
  rescue Timeout::Error
79
128
  end
80
129
 
data/lib/specjour/cpu.rb CHANGED
@@ -10,6 +10,10 @@ module Specjour
10
10
  end
11
11
  end
12
12
 
13
+ def self.half_cores
14
+ cores / 2
15
+ end
16
+
13
17
  protected
14
18
 
15
19
  def self.command(cmd)
data/lib/specjour/fork.rb CHANGED
@@ -10,7 +10,7 @@ module Specjour::Fork
10
10
  at_exit { exit! }
11
11
  begin
12
12
  yield
13
- rescue StandardError => e
13
+ rescue Exception => e
14
14
  $stderr.puts "#{e.class} #{e.message}", e.backtrace
15
15
  end
16
16
  end