specjour 0.7.0 → 2.0.0.rc1
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.
- checksums.yaml +7 -0
- data/History.markdown +12 -0
- data/README.markdown +24 -1
- data/Rakefile +12 -12
- data/bin/specjour +3 -1
- data/lib/specjour/cli.rb +86 -110
- data/lib/specjour/colors.rb +23 -0
- data/lib/specjour/configuration.rb +47 -91
- data/lib/specjour/connection.rb +69 -20
- data/lib/specjour/cpu.rb +4 -0
- data/lib/specjour/fork.rb +1 -1
- data/lib/specjour/formatter.rb +153 -0
- data/lib/specjour/listener.rb +181 -0
- data/lib/specjour/loader.rb +55 -119
- data/lib/specjour/logger.rb +34 -0
- data/lib/specjour/plugin/base.rb +61 -0
- data/lib/specjour/plugin/manager.rb +28 -0
- data/lib/specjour/plugin/rails.rb +47 -0
- data/lib/specjour/plugin/rails_v3.rb +23 -0
- data/lib/specjour/plugin/rails_v4.rb +25 -0
- data/lib/specjour/plugin/rspec.rb +160 -0
- data/lib/specjour/plugin/rspec_v2.rb +53 -0
- data/lib/specjour/plugin/rspec_v3.rb +59 -0
- data/lib/specjour/plugin/ssh.rb +24 -0
- data/lib/specjour/plugin.rb +4 -0
- data/lib/specjour/printer.rb +235 -67
- data/lib/specjour/protocol.rb +13 -6
- data/lib/specjour/rspec_formatter.rb +17 -0
- data/lib/specjour/rsync_daemon.rb +6 -3
- data/lib/specjour/socket_helper.rb +26 -10
- data/lib/specjour/worker.rb +36 -62
- data/lib/specjour.rb +50 -24
- data/lib/specjour_plugin.rb +5 -0
- metadata +52 -84
- data/lib/specjour/cucumber/distributed_formatter.rb +0 -82
- data/lib/specjour/cucumber/final_report.rb +0 -83
- data/lib/specjour/cucumber/preloader.rb +0 -22
- data/lib/specjour/cucumber/runner.rb +0 -15
- data/lib/specjour/cucumber.rb +0 -16
- data/lib/specjour/db_scrub.rb +0 -56
- data/lib/specjour/dispatcher.rb +0 -170
- data/lib/specjour/manager.rb +0 -174
- data/lib/specjour/rspec/distributed_formatter.rb +0 -50
- data/lib/specjour/rspec/final_report.rb +0 -73
- data/lib/specjour/rspec/marshalable_exception.rb +0 -19
- data/lib/specjour/rspec/preloader.rb +0 -15
- data/lib/specjour/rspec/runner.rb +0 -14
- data/lib/specjour/rspec/shared_example_group_ext.rb +0 -9
- data/lib/specjour/rspec.rb +0 -17
@@ -0,0 +1,153 @@
|
|
1
|
+
module Specjour
|
2
|
+
class Formatter
|
3
|
+
require 'json'
|
4
|
+
include Colors
|
5
|
+
# description, status [pending,failed,passed] file_path, line_number, exception => [class, message, backtrace]
|
6
|
+
|
7
|
+
STATUSES = Hash.new({char: "?", color: :white}).merge!(
|
8
|
+
"passed" => {char: ".", color: :green},
|
9
|
+
"failed" => {char: "F", color: :red},
|
10
|
+
"error" => {char: "E", color: :magenta},
|
11
|
+
"pending" => {char: "P", color: :yellow},
|
12
|
+
"other" => {char: "O", color: :white}
|
13
|
+
)
|
14
|
+
|
15
|
+
attr_accessor \
|
16
|
+
:error_count,
|
17
|
+
:fail_count,
|
18
|
+
:failures,
|
19
|
+
:output,
|
20
|
+
:pass_count,
|
21
|
+
:pending_count,
|
22
|
+
:start_time,
|
23
|
+
:end_time,
|
24
|
+
:tests
|
25
|
+
|
26
|
+
def initialize(output=$stdout)
|
27
|
+
@output = output
|
28
|
+
@tests = []
|
29
|
+
@failures = []
|
30
|
+
@pass_count, @pending_count, @fail_count, @error_count = 0, 0, 0, 0
|
31
|
+
@start_time = Time.now
|
32
|
+
end
|
33
|
+
|
34
|
+
def print(test)
|
35
|
+
if test[:status] == "failed"
|
36
|
+
@output.puts
|
37
|
+
print_failure(test, fail_count)
|
38
|
+
else
|
39
|
+
status_format = STATUSES[test[:status]]
|
40
|
+
@output.print colorize(status_format[:char], status_format[:color])
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def print_failures
|
45
|
+
@output.puts "Failures:\n\n"
|
46
|
+
failures.each_with_index do |test, index|
|
47
|
+
print_failure(test, index)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def print_failure(test, index)
|
52
|
+
exception = test[:exception]
|
53
|
+
num = index + 1
|
54
|
+
indent = num.to_s.size + 2
|
55
|
+
worker = "#{test[:hostname]}[#{test[:worker_number]}]"
|
56
|
+
@output.puts colorize("#{num}. #{worker}", :red)
|
57
|
+
description = colorize("#{test[:description]}", :red)
|
58
|
+
@output.print " " * indent
|
59
|
+
@output.puts description
|
60
|
+
message = colorize("#{exception[:class]}: #{exception[:message]}", :red)
|
61
|
+
@output.print " " * indent
|
62
|
+
@output.puts message
|
63
|
+
@output.puts format_backtrace(exception[:backtrace])
|
64
|
+
@output.puts
|
65
|
+
end
|
66
|
+
|
67
|
+
def format_backtrace(backtrace)
|
68
|
+
backtrace = Array(backtrace)
|
69
|
+
if Specjour.configuration.full_backtrace
|
70
|
+
backtrace
|
71
|
+
else
|
72
|
+
backtrace.reject {|l| Specjour.configuration.backtrace_exclusion_pattern =~ l}
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def failing_test_paths
|
77
|
+
failures.map do |f|
|
78
|
+
"#{f[:file_path]}:#{f[:line_number]}"
|
79
|
+
end.uniq
|
80
|
+
end
|
81
|
+
|
82
|
+
|
83
|
+
def print_counts
|
84
|
+
@output.puts colorize("Pending: #{pending_count}", :yellow)
|
85
|
+
@output.puts colorize("Errors: #{error_count}", :magenta)
|
86
|
+
@output.puts colorize("Passed: #{pass_count}", :green)
|
87
|
+
@output.puts colorize("Failed: #{fail_count}", :red)
|
88
|
+
end
|
89
|
+
|
90
|
+
def execution_time
|
91
|
+
Time.new(2000,1,1,0,0,0,0) + (end_time - start_time)
|
92
|
+
end
|
93
|
+
|
94
|
+
def print_overview
|
95
|
+
overall_color = fail_count == 0 ? :green : :red
|
96
|
+
@output.puts colorize("\nRan: #{tests.size} tests in #{execution_time.strftime("%Mm:%Ss:%Lms")}", overall_color)
|
97
|
+
end
|
98
|
+
|
99
|
+
def print_summary
|
100
|
+
@output.puts "\n\n"
|
101
|
+
print_failures if failures.any?
|
102
|
+
print_counts
|
103
|
+
print_overview
|
104
|
+
@output.puts "\n"
|
105
|
+
end
|
106
|
+
|
107
|
+
def report_test(test)
|
108
|
+
print(test)
|
109
|
+
tests << test
|
110
|
+
case test[:status]
|
111
|
+
when "passed"
|
112
|
+
@pass_count += 1
|
113
|
+
when "failed"
|
114
|
+
@fail_count += 1
|
115
|
+
failures << test
|
116
|
+
when "error"
|
117
|
+
@error_count += 1
|
118
|
+
when "pending"
|
119
|
+
@pending_count += 1
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
def set_end_time!
|
124
|
+
@end_time = Time.now
|
125
|
+
end
|
126
|
+
|
127
|
+
def exit_status
|
128
|
+
failures.any? ? 1 : 0
|
129
|
+
end
|
130
|
+
|
131
|
+
end
|
132
|
+
end
|
133
|
+
__END__
|
134
|
+
Pending:
|
135
|
+
Specjour pends as an example
|
136
|
+
# No reason given
|
137
|
+
# ./spec/specjour_spec.rb:25
|
138
|
+
|
139
|
+
Failures:
|
140
|
+
|
141
|
+
1) Specjour fails as an example
|
142
|
+
Failure/Error: raise CustomException, 'fails'
|
143
|
+
CustomException:
|
144
|
+
fails
|
145
|
+
# ./spec/specjour_spec.rb:7:in `boo'
|
146
|
+
# ./spec/specjour_spec.rb:30:in `block (2 levels) in <top (required)>'
|
147
|
+
|
148
|
+
Finished in 0.00297 seconds
|
149
|
+
14 examples, 1 failure, 1 pending
|
150
|
+
|
151
|
+
Failed examples:
|
152
|
+
|
153
|
+
rspec ./spec/specjour_spec.rb:29 # Specjour fails as an example
|
@@ -0,0 +1,181 @@
|
|
1
|
+
module Specjour
|
2
|
+
class Listener
|
3
|
+
require 'dnssd'
|
4
|
+
Thread.abort_on_exception = true
|
5
|
+
|
6
|
+
LOCK_FILE = "listener.lock"
|
7
|
+
|
8
|
+
include Logger
|
9
|
+
include SocketHelper
|
10
|
+
|
11
|
+
attr_accessor :options, :printer
|
12
|
+
|
13
|
+
def self.ensure_started
|
14
|
+
listener = new
|
15
|
+
unless listener.started?
|
16
|
+
listener_pid = fork do
|
17
|
+
Specjour.plugin_manager.send_task(:remove_connection)
|
18
|
+
listener.daemonize
|
19
|
+
listener.start
|
20
|
+
end
|
21
|
+
Process.detach(listener_pid)
|
22
|
+
end
|
23
|
+
listener
|
24
|
+
end
|
25
|
+
|
26
|
+
def initialize(options={})
|
27
|
+
self.options = options
|
28
|
+
end
|
29
|
+
|
30
|
+
def available_for?(project_name)
|
31
|
+
if Specjour.configuration.project_aliases.any? || !project_name.empty?
|
32
|
+
Specjour.configuration.project_aliases.include? project_name
|
33
|
+
else
|
34
|
+
true
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def config_directory
|
39
|
+
return @config_directory if @config_directory
|
40
|
+
@config_directory = File.join(Dir.tmpdir, "specjour")
|
41
|
+
FileUtils.mkdir_p @config_directory
|
42
|
+
@config_directory
|
43
|
+
end
|
44
|
+
|
45
|
+
def daemonize
|
46
|
+
Process.daemon
|
47
|
+
end
|
48
|
+
|
49
|
+
def add_printer(params)
|
50
|
+
log "Listener adding printer #{params}"
|
51
|
+
self.printer = params
|
52
|
+
Specjour.configuration.printer_uri = params[:uri]
|
53
|
+
Specjour.configuration.remote_job = remote_ip?(params[:ip])
|
54
|
+
end
|
55
|
+
|
56
|
+
def remove_printer
|
57
|
+
self.printer = nil
|
58
|
+
Specjour.configuration.printer_uri = nil
|
59
|
+
Specjour.configuration.remote_job = nil
|
60
|
+
end
|
61
|
+
|
62
|
+
def fork_loader
|
63
|
+
Specjour.plugin_manager.send_task(:before_loader_fork)
|
64
|
+
fork do
|
65
|
+
Specjour.plugin_manager.send_task(:remove_connection)
|
66
|
+
loader = Loader.new({task: "run_tests"})
|
67
|
+
Specjour.plugin_manager.send_task(:after_loader_fork)
|
68
|
+
loader.start
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def gather
|
73
|
+
@dnssd_service = DNSSD.browse!('_specjour._tcp') do |reply|
|
74
|
+
log ['reply', reply.name, reply.service_name, reply.domain,reply.flags, reply.interface]
|
75
|
+
if reply.flags.add?
|
76
|
+
DNSSD.resolve!(reply.name, reply.type, reply.domain, flags=0, reply.interface) do |resolved|
|
77
|
+
log "Bonjour discovered #{resolved.target} #{resolved.text_record.inspect}"
|
78
|
+
if resolved.text_record && resolved.text_record['version'] == Specjour::VERSION
|
79
|
+
if available_for?(resolved.text_record['project_alias'].to_s)
|
80
|
+
resolved_ip = ip_from_hostname(resolved.target)
|
81
|
+
uri = URI::Generic.build :host => resolved_ip, :port => resolved.port
|
82
|
+
add_printer(name: resolved.name, uri: uri, ip: resolved_ip)
|
83
|
+
else
|
84
|
+
$stderr.puts "Found #{resolved.target} but not listening to project alias: #{resolved.text_record['project_alias']}. Skipping..."
|
85
|
+
end
|
86
|
+
else
|
87
|
+
$stderr.puts "Found #{resolved.target} but its version doesn't match v#{Specjour::VERSION}. Skipping..."
|
88
|
+
end
|
89
|
+
break
|
90
|
+
end
|
91
|
+
break if printer
|
92
|
+
else
|
93
|
+
log "REMOVING #{reply.name} #{reply}"
|
94
|
+
remove_printer
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def pid
|
100
|
+
if File.exist?(pid_file) && !File.directory?(pid_file)
|
101
|
+
File.read(pid_file).strip.to_i
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def pid_file
|
106
|
+
@pid_file ||= File.join(config_directory, "#{Specjour.configuration.project_aliases.join("-")}.pid")
|
107
|
+
end
|
108
|
+
|
109
|
+
def lock_file
|
110
|
+
@lock_file ||= File.join(config_directory, LOCK_FILE)
|
111
|
+
end
|
112
|
+
|
113
|
+
# only allow one listener to execute at a time
|
114
|
+
def listener_lock(&block)
|
115
|
+
exception = nil
|
116
|
+
File.open(lock_file, "w") do |file|
|
117
|
+
begin
|
118
|
+
debug "Acquiring listener lock"
|
119
|
+
file.flock File::LOCK_EX
|
120
|
+
debug "Listener lock acquired"
|
121
|
+
block.call
|
122
|
+
rescue => exception
|
123
|
+
file.flock File::LOCK_UN
|
124
|
+
end
|
125
|
+
end
|
126
|
+
raise exception if exception
|
127
|
+
end
|
128
|
+
|
129
|
+
def program_name
|
130
|
+
name = "specjour listen"
|
131
|
+
if Specjour.configuration.project_aliases.any?
|
132
|
+
name += " -a #{Specjour.configuration.project_aliases.join(",")}"
|
133
|
+
end
|
134
|
+
name
|
135
|
+
end
|
136
|
+
|
137
|
+
def start
|
138
|
+
$PROGRAM_NAME = program_name
|
139
|
+
log "Listener starting"
|
140
|
+
write_pid
|
141
|
+
loop do
|
142
|
+
log "listening..."
|
143
|
+
gather
|
144
|
+
listener_lock do
|
145
|
+
@loader_pid = fork_loader
|
146
|
+
Process.waitall
|
147
|
+
remove_printer
|
148
|
+
sleep 3 # let bonjour services stop
|
149
|
+
end
|
150
|
+
end
|
151
|
+
rescue StandardError, ScriptError => e
|
152
|
+
$stderr.puts "RESCUED #{e.message}"
|
153
|
+
$stderr.puts e.backtrace
|
154
|
+
connection.error(e)
|
155
|
+
ensure
|
156
|
+
remove_pid
|
157
|
+
remove_connection
|
158
|
+
log "Shutting down listener"
|
159
|
+
end
|
160
|
+
|
161
|
+
def started?
|
162
|
+
!pid.nil?
|
163
|
+
end
|
164
|
+
|
165
|
+
def stop
|
166
|
+
Process.kill("TERM", pid) rescue TypeError
|
167
|
+
ensure
|
168
|
+
remove_pid
|
169
|
+
end
|
170
|
+
|
171
|
+
def write_pid
|
172
|
+
File.open(pid_file, 'w') do |f|
|
173
|
+
f.write Process.pid
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
def remove_pid
|
178
|
+
File.unlink(pid_file) if File.exist?(pid_file) && pid == Process.pid
|
179
|
+
end
|
180
|
+
end
|
181
|
+
end
|
data/lib/specjour/loader.rb
CHANGED
@@ -1,150 +1,86 @@
|
|
1
1
|
module Specjour
|
2
|
+
require 'specjour/worker'
|
2
3
|
class Loader
|
3
|
-
include
|
4
|
-
include
|
4
|
+
include Logger
|
5
|
+
include SocketHelper
|
5
6
|
|
6
|
-
attr_reader
|
7
|
+
attr_reader \
|
8
|
+
:options,
|
9
|
+
:quiet,
|
10
|
+
:task,
|
11
|
+
:worker_pids
|
7
12
|
|
8
13
|
def initialize(options = {})
|
9
14
|
@options = options
|
10
|
-
@printer_uri = options[:printer_uri]
|
11
|
-
@test_paths = options[:test_paths]
|
12
|
-
@worker_size = options[:worker_size]
|
13
15
|
@task = options[:task]
|
14
16
|
@quiet = options[:quiet]
|
15
|
-
@project_path = options[:project_path]
|
16
17
|
@worker_pids = []
|
17
|
-
Dir.chdir project_path
|
18
|
-
Specjour.load_custom_hooks
|
19
18
|
end
|
20
19
|
|
21
20
|
def start
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
21
|
+
Process.setsid
|
22
|
+
$PROGRAM_NAME = "specjour loader"
|
23
|
+
set_up
|
24
|
+
sync
|
25
|
+
Specjour.plugin_manager.send_task(:load_application)
|
26
|
+
Specjour.plugin_manager.send_task(:register_tests_with_printer)
|
27
|
+
fork_workers
|
28
|
+
wait_srv
|
29
|
+
rescue StandardError, ScriptError => e
|
30
|
+
$stderr.puts "RESCUED #{e.class} '#{e.message}'"
|
31
|
+
$stderr.puts e.backtrace
|
32
|
+
$stderr.puts "\n\n"
|
33
|
+
connection.error(e)
|
34
34
|
ensure
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
def spec_files
|
39
|
-
@spec_files ||= file_collector(spec_paths) do |path|
|
40
|
-
if path == project_path
|
41
|
-
Dir["spec/**/*_spec.rb"]
|
42
|
-
else
|
43
|
-
Dir["**/*_spec.rb"]
|
44
|
-
end
|
45
|
-
end
|
35
|
+
remove_connection
|
36
|
+
log "Loader killing group #{Process.getsid}"
|
46
37
|
end
|
47
38
|
|
48
|
-
def
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
39
|
+
def fork_workers
|
40
|
+
Specjour.plugin_manager.send_task(:before_worker_fork)
|
41
|
+
(1..Specjour.configuration.worker_size).each do |index|
|
42
|
+
worker_pids << fork do
|
43
|
+
remove_connection
|
44
|
+
Specjour.plugin_manager.send_task(:remove_connection)
|
45
|
+
$PROGRAM_NAME = "specjour worker"
|
46
|
+
worker = Worker.new(
|
47
|
+
:number => index,
|
48
|
+
:quiet => quiet
|
49
|
+
)
|
50
|
+
Specjour.plugin_manager.send_task(:after_worker_fork)
|
51
|
+
worker.send(task)
|
54
52
|
end
|
55
53
|
end
|
56
54
|
end
|
57
55
|
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
end
|
67
|
-
|
68
|
-
def file_collector(paths, &globber)
|
69
|
-
if spec_paths.empty? && feature_paths.empty?
|
70
|
-
globber[project_path]
|
71
|
-
else
|
72
|
-
paths.map do |path|
|
73
|
-
path = File.expand_path(path, project_path)
|
74
|
-
if File.directory?(path)
|
75
|
-
globber[path]
|
76
|
-
else
|
77
|
-
path
|
78
|
-
end
|
79
|
-
end.flatten.uniq
|
80
|
-
end
|
81
|
-
end
|
82
|
-
|
83
|
-
def load_app
|
84
|
-
RSpec::Preloader.load spec_files if spec_files.any?
|
85
|
-
Cucumber::Preloader.load(feature_files, connection) if feature_files.any?
|
86
|
-
register_tests_with_printer
|
87
|
-
end
|
88
|
-
|
89
|
-
def register_tests_with_printer
|
90
|
-
tests = rspec_examples | cucumber_scenarios
|
91
|
-
connection.send_message :tests=, tests
|
92
|
-
end
|
93
|
-
|
94
|
-
def rspec_examples
|
95
|
-
if spec_files.any?
|
96
|
-
filtered_examples
|
97
|
-
else
|
98
|
-
[]
|
99
|
-
end
|
100
|
-
end
|
101
|
-
|
102
|
-
def filtered_examples
|
103
|
-
examples = ::RSpec.world.example_groups.map do |g|
|
104
|
-
g.descendant_filtered_examples
|
105
|
-
end.flatten
|
106
|
-
locations = examples.map do |e|
|
107
|
-
meta = e.metadata
|
108
|
-
groups = e.example_group.parent_groups + [e.example_group]
|
109
|
-
shared_group = groups.detect do |group|
|
110
|
-
group.metadata[:shared_group_name]
|
111
|
-
end
|
112
|
-
if shared_group
|
113
|
-
meta = shared_group.metadata[:example_group]
|
56
|
+
def wait_srv
|
57
|
+
select [connection.socket]
|
58
|
+
if !connection.socket.eof?
|
59
|
+
signal = connection.get_server_done
|
60
|
+
case signal
|
61
|
+
when "INT"
|
62
|
+
debug "Sending INT to -#{Process.getsid}"
|
63
|
+
Process.kill("INT", -Process.getsid)
|
114
64
|
end
|
115
|
-
meta[:location]
|
116
|
-
end
|
117
|
-
ensure
|
118
|
-
::RSpec.reset
|
119
|
-
end
|
120
|
-
|
121
|
-
def cucumber_scenarios
|
122
|
-
if feature_files.any?
|
123
|
-
scenarios
|
124
|
-
else
|
125
|
-
[]
|
126
65
|
end
|
127
66
|
end
|
128
67
|
|
129
|
-
def
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
end.flatten
|
68
|
+
def set_up
|
69
|
+
data = connection.ready({hostname: hostname, worker_size: Specjour.configuration.worker_size})
|
70
|
+
Specjour.configuration.project_name = data[:project_name]
|
71
|
+
Specjour.configuration.test_paths = data[:test_paths]
|
72
|
+
Specjour.configuration.project_path = File.expand_path(Specjour.configuration.project_name, Specjour.configuration.tmp_path)
|
135
73
|
end
|
136
74
|
|
137
|
-
def
|
138
|
-
|
139
|
-
|
75
|
+
def sync
|
76
|
+
cmd "rsync #{Specjour.configuration.rsync_options} --port=#{Specjour.configuration.rsync_port} #{connection.host}::#{Specjour.configuration.project_name} #{Specjour.configuration.project_path}"
|
77
|
+
Dir.chdir Specjour.configuration.project_path
|
140
78
|
end
|
141
79
|
|
142
|
-
def
|
143
|
-
|
144
|
-
|
145
|
-
Connection.new URI.parse(printer_uri)
|
80
|
+
def cmd(command)
|
81
|
+
Specjour.benchmark(command) do
|
82
|
+
system *command.split
|
146
83
|
end
|
147
84
|
end
|
148
|
-
|
149
85
|
end
|
150
86
|
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Specjour
|
2
|
+
module Logger
|
3
|
+
|
4
|
+
def log(msg)
|
5
|
+
Specjour.logger.info(self.class.name) { format(msg) }
|
6
|
+
end
|
7
|
+
|
8
|
+
def debug(msg)
|
9
|
+
Specjour.logger.debug(self.class.name) { format("#{msg}\n\t#{called_from}") }
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
def format(msg)
|
15
|
+
prefix = Specjour.configuration.worker_number > 0 ? "[#{Specjour.configuration.worker_number}] " : ""
|
16
|
+
"#{prefix}#{msg}"
|
17
|
+
end
|
18
|
+
|
19
|
+
def called_from
|
20
|
+
caller.detect {|s| s =~ /specjour\/lib\/specjour\/(?!logger\.rb)/}
|
21
|
+
end
|
22
|
+
|
23
|
+
# def self.extended(base)
|
24
|
+
# base.instance_methods.each do |instance_method|
|
25
|
+
# define_method instance_method do |*args|
|
26
|
+
# log __method__
|
27
|
+
# val = super *args
|
28
|
+
# log val
|
29
|
+
# val
|
30
|
+
# end
|
31
|
+
# end
|
32
|
+
# end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
module Specjour
|
2
|
+
module Plugin
|
3
|
+
class Base
|
4
|
+
include SocketHelper
|
5
|
+
include Specjour::Logger
|
6
|
+
|
7
|
+
attr_reader :listener, :loader, :worker
|
8
|
+
|
9
|
+
def after_register
|
10
|
+
end
|
11
|
+
|
12
|
+
def before_suite
|
13
|
+
end
|
14
|
+
|
15
|
+
def after_suite
|
16
|
+
end
|
17
|
+
|
18
|
+
def before_loader_fork
|
19
|
+
end
|
20
|
+
|
21
|
+
def load_application
|
22
|
+
end
|
23
|
+
|
24
|
+
def load_test_suite
|
25
|
+
end
|
26
|
+
|
27
|
+
def after_loader_fork
|
28
|
+
end
|
29
|
+
|
30
|
+
def before_worker_fork
|
31
|
+
end
|
32
|
+
|
33
|
+
def after_worker_fork
|
34
|
+
end
|
35
|
+
|
36
|
+
def interrupted!
|
37
|
+
end
|
38
|
+
|
39
|
+
def register_tests_with_printer
|
40
|
+
end
|
41
|
+
|
42
|
+
def run_test(test)
|
43
|
+
false
|
44
|
+
end
|
45
|
+
|
46
|
+
def tests_to_register
|
47
|
+
[]
|
48
|
+
end
|
49
|
+
|
50
|
+
def before_print_summary(formatter)
|
51
|
+
end
|
52
|
+
|
53
|
+
def after_print_summary(formatter)
|
54
|
+
end
|
55
|
+
|
56
|
+
def exit_status(formatter)
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Specjour::Plugin
|
2
|
+
class Manager
|
3
|
+
include Specjour::Logger
|
4
|
+
attr_reader :plugins
|
5
|
+
|
6
|
+
def initialize
|
7
|
+
@plugins = []
|
8
|
+
end
|
9
|
+
|
10
|
+
def register_plugin(plugin, position=-1)
|
11
|
+
if !plugins.include?(plugin)
|
12
|
+
plugins.insert(position, plugin)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def clear_plugins
|
17
|
+
plugins.clear
|
18
|
+
end
|
19
|
+
|
20
|
+
def send_task(task, *args)
|
21
|
+
plugins.map do |plugin|
|
22
|
+
log "sending task to plugin: #{task}, #{plugin}"
|
23
|
+
plugin.__send__(task, *args)
|
24
|
+
# break if plugin.__send__(task, *args) == false
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|