DTR 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
metadata CHANGED
@@ -3,8 +3,8 @@ rubygems_version: 0.9.4
3
3
  specification_version: 1
4
4
  name: DTR
5
5
  version: !ruby/object:Gem::Version
6
- version: 0.0.1
7
- date: 2007-09-19 00:00:00 +08:00
6
+ version: 0.0.2
7
+ date: 2007-10-08 00:00:00 +08:00
8
8
  summary: DTR is a distributed test runner to run tests on distributed computers for decreasing build time.
9
9
  require_paths:
10
10
  - lib
@@ -30,32 +30,25 @@ authors:
30
30
  - Li Xiao
31
31
  files:
32
32
  - install.rb
33
- - bin
34
33
  - CHANGES
35
- - doc
36
- - html
37
- - lib
38
34
  - LICENSE.TXT
39
35
  - Rakefile
40
36
  - README
41
- - show_process_exit_code.bat
42
- - test
43
- - testdata
44
- - tmp
45
37
  - TODO
46
38
  - bin/dtr
47
- - lib/dtr/base.rb
48
- - lib/dtr/command_line.rb
49
- - lib/dtr/drb_dtr.rb
39
+ - lib/dtr/env_store.rb
40
+ - lib/dtr/inject.rb
41
+ - lib/dtr/inject_without_updating_codebase.rb
42
+ - lib/dtr/inject_with_svn.rb
43
+ - lib/dtr/raketasks.rb
50
44
  - lib/dtr/ruby_ext.rb
45
+ - lib/dtr/runner.rb
46
+ - lib/dtr/service_provider.rb
47
+ - lib/dtr/svn.rb
51
48
  - lib/dtr.rb
52
- - test/average_packer_test.rb
53
- - test/base_test.rb
54
- - test/original_test_reports_test.rb
55
- - test/ruby_ext_test.rb
56
- - test/ruby_runner_test.rb
57
- - test/scenario_setup_and_run_tests_simply.rb
58
- - test/server_test.rb
49
+ - test/inject_test.rb
50
+ - test/scenario_tests.rb
51
+ - test/svn_test.rb
59
52
  - test/test_helper.rb
60
53
  - doc/jamis.rb
61
54
  test_files: []
data/lib/dtr/base.rb DELETED
@@ -1,281 +0,0 @@
1
- require 'yaml'
2
- require 'dtr/command_line'
3
-
4
- module DTR
5
- class AveragePacker
6
- def pack(test_files, num_packets)
7
- raise 'No test files given' if test_files.nil? || test_files.empty?
8
- @packets = Array.new(num_packets) {[]}
9
- test_files.each_with_index {|tf,i| @packets[i%num_packets] << tf }
10
-
11
- @packets
12
- end
13
- end
14
-
15
- class RubyRunner
16
- def run(server, client, test_files, signature)
17
- test_files.each do |test_file|
18
- logger.info { "\tRun #{test_file}..." }
19
- unless File.exist?(test_file.untaint)
20
- logger.info {"\tSkip test file not exists: #{test_file}"}
21
- next
22
- end
23
-
24
- server_reports = server.reports
25
- break unless server_reports # if can't get reports, server maybe shutdown
26
-
27
- if server_reports[test_file]
28
- logger.info {"\tSkip test file has report on server: #{test_file}"}
29
- next
30
- end
31
- result = client.execute("ruby #{test_file}")
32
- tests, assertions, failures, errors = parse(result[:stdout])
33
- report = TestReport.new(test_file, result, tests, assertions, failures, errors)
34
-
35
- logger.info { report.to_yaml } unless report.succeeded?
36
-
37
- break unless server.update signature, report
38
- end
39
- end
40
-
41
- SUMMARY_REGEX = /^\s*(\d+) tests, (\d+) assertions, (\d+) failures, (\d+) errors$/
42
- def parse(stdout)
43
- summary = stdout.strip.split("\n").reverse.find do |line|
44
- SUMMARY_REGEX =~ line.strip
45
- end
46
- if SUMMARY_REGEX =~ summary
47
- [$1, $2, $3, $4].collect{|str| str.to_i}
48
- else
49
- logger.info {"\tStdout has no tests summary line: #{stdout}"}
50
- [0, 0, 0, 0]
51
- end
52
- end
53
- end
54
-
55
- class Server
56
-
57
- class NoAliveClientsError < StandardError
58
- end
59
-
60
- attr_accessor :server_signature, :test_files, :packer, :runner_name, :latest_build_time
61
-
62
- def initialize(test_files=[])
63
- @test_files = test_files
64
- @packer = AveragePacker.new
65
- @runner_name = RubyRunner.name
66
- @server_signature = Object.new.to_s
67
- @report_signature = nil
68
- @raise_on_no_alive_client = DTROPTIONS[:raise_on_no_alive_client]
69
- end
70
-
71
- def latest_build_summary
72
- raise 'Server never ran!' unless latest_build_time
73
- %{Finished in #{format('%.1f', latest_build_time)} seconds.
74
-
75
- #{collect_report(:tests)}, #{collect_report(:assertions)}, #{collect_report(:failures)}, #{collect_report(:errors)}}
76
- end
77
-
78
- def clients
79
- clients_map.values
80
- end
81
-
82
- def available_clients
83
- alive_clients = self.clients.select{|client| client.server_signature == @server_signature}
84
- raise NoAliveClientsError.new if @raise_on_no_alive_client && alive_clients.empty?
85
- alive_clients.select{|client| client.idle?}
86
- end
87
-
88
- def succeeded?
89
- complete? && !reports.empty? && reports.values.all?{|report| report.succeeded?}
90
- end
91
-
92
- def complete?
93
- @complete ||= false
94
- end
95
-
96
- def run(opt={})
97
- opt = {:wait_for_reports => true, :setup => false}.merge opt
98
- run_start(opt)
99
- @run_once = false
100
- loop do
101
- break if complete?
102
- run_once unless @run_once
103
- @run_once = DTROPTIONS[:packing_once]
104
- break if !opt[:wait_for_reports] || complete?
105
- sleep(DTROPTIONS[:wait_a_moment]) if DTROPTIONS[:wait_a_moment]
106
- break if complete?
107
- end
108
- ensure
109
- @report_signature = nil if opt[:wait_for_reports]
110
- self.latest_build_time = Time.now - @build_start_time
111
- end
112
-
113
- def reports
114
- @reports ||= {}
115
- end
116
-
117
- def client_setup_logs
118
- @client_setup_logs ||= {}
119
- end
120
-
121
- def add_client(client)
122
- clients_map[client.name] = client
123
- client_setup_logs[client.name] = nil
124
- end
125
-
126
- def update_setup_log(signature, report)
127
- with_valid_signature(signature) do
128
- client_setup_logs[report[:client_name]] = report
129
- if report[:exit_code] != 0
130
- clients_map.delete(report[:client_name])
131
- end
132
- end
133
- end
134
-
135
- def update(signature, report)
136
- with_valid_signature(signature) do
137
- self.reports[report.test] = report
138
- @report_signature = nil if @complete = self.reports.keys.size == @test_files.size
139
- end
140
- end
141
-
142
- private
143
- def run_start(opt)
144
- @build_start_time = Time.now
145
- if test_files.nil? || test_files.empty?
146
- @complete = true
147
- return
148
- end
149
- @report_signature = @build_start_time.to_s
150
- @setup_clients = opt[:setup] ? [] : nil
151
- end
152
-
153
- def run_once
154
- clients = available_clients
155
-
156
- logger.debug { "run tests on #{clients.collect{|c|c.name}.join(', ')}"} unless clients.empty?
157
- no_report_tests = test_files - reports.keys
158
- return if clients.empty? || no_report_tests.empty?
159
-
160
- packets = packer.pack no_report_tests, clients.size
161
- clients.each do |client|
162
- if @setup_clients && !@setup_clients.include?(client)
163
- @setup_clients << client
164
- client.setup(@report_signature)
165
- end
166
- client.run self.runner_name, packets.pop, @report_signature
167
- end
168
- end
169
-
170
- def with_valid_signature(signature)
171
- if !@report_signature.nil? && signature == @report_signature
172
- yield
173
- true
174
- end
175
- end
176
-
177
- def collect_report(type)
178
- "#{reports.values.collect{|r| r.send(type)}.inject(0){|r, i| r += i}} #{type}"
179
- end
180
-
181
- def clients_map
182
- @clients_map ||= {}
183
- end
184
- end
185
-
186
- class Client
187
-
188
- RUNNERS = {RubyRunner.name => RubyRunner.new}
189
-
190
- attr_reader :name
191
-
192
- def initialize(server, name, setup_cmd=DTROPTIONS[:setup])
193
- @server = server
194
- @name = name
195
- @executing_cmd = nil
196
- @tmp_dir = DTROPTIONS[:tmp_dir]
197
- @setup_cmd = setup_cmd
198
- end
199
-
200
- def idle?
201
- @executing_cmd.nil?
202
- end
203
-
204
- def run(runner_name, test_files, signature)
205
- RUNNERS[runner_name].run(@server, self, test_files, signature)
206
- end
207
-
208
- def setup(signature)
209
- result = execute(@setup_cmd)
210
- @server.update_setup_log(signature, result)
211
- result
212
- end
213
-
214
- def server_signature
215
- @server.server_signature
216
- end
217
-
218
- def completed_cmds
219
- @completed_cmds ||= []
220
- end
221
-
222
- def execute(cmd)
223
- @executing_cmd = cmd
224
-
225
- outputs = {:stdout => "#{@tmp_dir}/execution_stdout.log", :stderr => "#{@tmp_dir}/execution_stderr.log"}
226
- outputs.values.each do |log_file|
227
- File.delete log_file if File.exist? log_file
228
- end
229
-
230
- begin
231
- CommandLine.execute cmd, outputs
232
- exit_code = 0
233
- rescue CommandLine::ExecutionError => e
234
- exit_code = e.exitstatus
235
- end
236
-
237
- outputs.each_key do |key|
238
- outputs[key] = File.open(outputs[key], 'r') {|f| f.read}
239
- end
240
-
241
- self.completed_cmds << cmd
242
- @executing_cmd = nil
243
- {:client_name => @name, :stdout => outputs[:stdout], :stderr => outputs[:stderr], :exit_code => exit_code}
244
- end
245
- end
246
-
247
- class TestReport
248
- attr_reader :test, :stdout, :stderr, :exit_code, :client_name, :tests, :assertions, :failures, :errors
249
- def initialize(test, execution_result, tests, assertions, failures, errors)
250
- @test = test
251
- @client_name = execution_result[:client_name]
252
- @stdout = execution_result[:stdout]
253
- @stderr = execution_result[:stderr]
254
- @exit_code = execution_result[:exit_code]
255
- @tests = tests
256
- @assertions = assertions
257
- @failures = failures
258
- @errors = errors
259
- end
260
-
261
- def succeeded?
262
- @failures == 0 && @errors == 0
263
- end
264
-
265
- def failed?
266
- @failures > 0
267
- end
268
-
269
- def error?
270
- @errors > 0
271
- end
272
-
273
- def ==(another)
274
- test == another.test
275
- end
276
-
277
- def successes
278
- tests - failures - errors
279
- end
280
- end
281
- end
@@ -1,173 +0,0 @@
1
- # borrowed (with modifications) from the RSCM project
2
- require 'rbconfig'
3
- module Platform
4
- def family
5
- target_os = Config::CONFIG["target_os"] or raise 'Cannot determine operating system'
6
- case target_os
7
- when /linux/, /Linux/ then 'linux'
8
- when /32/ then 'mswin32'
9
- when /darwin/ then 'powerpc-darwin'
10
- when /cyg/ then 'cygwin'
11
- when /solaris/ then 'solaris'
12
- when /freebsd/, /netbsd/ then 'bsd'
13
- else raise "Unknown OS: #{target_os}"
14
- end
15
- end
16
- module_function :family
17
-
18
- def user
19
- family == "mswin32" ? ENV['USERNAME'] : ENV['USER']
20
- end
21
- module_function :user
22
-
23
- def prompt(dir=Dir.pwd)
24
- prompt = "#{dir.gsub(/\//, File::SEPARATOR)} #{user}$"
25
- end
26
- module_function :prompt
27
-
28
- end
29
-
30
- module CommandLine
31
- QUOTE_REPLACEMENT = (Platform.family == "mswin32") ? "\"" : "\\\""
32
- LESS_THAN_REPLACEMENT = (Platform.family == "mswin32") ? "<" : "\\<"
33
- class OptionError < StandardError; end
34
- class ExecutionError < StandardError
35
- attr_reader :cmd, :dir, :exitstatus, :stderr
36
- def initialize(cmd, full_cmd, dir, exitstatus, stderr)
37
- @cmd, @full_cmd, @dir, @exitstatus, @stderr = cmd, full_cmd, dir, exitstatus, stderr
38
- end
39
- def to_s
40
- "\ndir : #{@dir}\n" +
41
- "command : #{@cmd}\n" +
42
- "executed command : #{@full_cmd}\n" +
43
- "exitstatus: #{@exitstatus}\n" +
44
- "STDERR TAIL START\n#{@stderr}\nSTDERR TAIL END\n"
45
- end
46
- end
47
-
48
- # Executes +cmd+.
49
- # If the +:stdout+ and +:stderr+ options are specified, a line consisting
50
- # of a prompt (including +cmd+) will be appended to the respective output streams will be appended
51
- # to those files, followed by the output itself. Example:
52
- #
53
- # CommandLine.execute("echo hello world", {:stdout => "stdout.log", :stderr => "stderr.log"})
54
- #
55
- # will result in the following being written to stdout.log:
56
- #
57
- # /Users/aslakhellesoy/scm/buildpatterns/repos/damagecontrol/trunk aslakhellesoy$ echo hello world
58
- # hello world
59
- #
60
- # -and to stderr.log:
61
- # /Users/aslakhellesoy/scm/buildpatterns/repos/damagecontrol/trunk aslakhellesoy$ echo hello world
62
- #
63
- # If a block is passed, the stdout io will be yielded to it (as with IO.popen). In this case the output
64
- # will not be written to the stdout file (even if it's specified):
65
- #
66
- # /Users/aslakhellesoy/scm/buildpatterns/repos/damagecontrol/trunk aslakhellesoy$ echo hello world
67
- # [output captured and therefore not logged]
68
- #
69
- # If the exitstatus of the command is different from the value specified by the +:exitstatus+ option
70
- # (which defaults to 0) then an ExecutionError is raised, its message containing the last 400 bytes of stderr
71
- # (provided +:stderr+ was specified)
72
- #
73
- # You can also specify the +:dir+ option, which will cause the command to be executed in that directory
74
- # (default is current directory).
75
- #
76
- # You can also specify a hash of environment variables in +:env+, which will add additional environment variables
77
- # to the default environment.
78
- #
79
- # Finally, you can specify several commands within one by separating them with '&&' (as you would in a shell).
80
- # This will result in several lines to be appended to the log (as if you had executed the commands separately).
81
- #
82
- # See the unit test for more examples.
83
- def execute(cmd, options={}, &proc)
84
- raise "Can't have newline in cmd" if cmd =~ /\n/
85
- options = {
86
- :dir => Dir.pwd.untaint,
87
- :env => {},
88
- :mode => 'r',
89
- :exitstatus => 0
90
- }.merge(options)
91
-
92
- options[:stdout] = %{"#{File.expand_path(options[:stdout])}"}.untaint if options[:stdout]
93
- options[:stderr] = %{"#{File.expand_path(options[:stderr])}"}.untaint if options[:stderr]
94
-
95
-
96
- if options[:dir].nil?
97
- e(cmd, options, &proc)
98
- else
99
- Dir.chdir(options[:dir]) do
100
- e(cmd, options, &proc)
101
- end
102
- end
103
- end
104
- module_function :execute
105
-
106
- private
107
-
108
- def full_cmd(cmd, options, &proc)
109
- commands = cmd.split("&&").collect{|c| c.strip}
110
- stdout_opt = options[:stdout] ? ">> #{options[:stdout]}" : ""
111
- stderr_opt = options[:stderr] ? "2>> #{options[:stderr]}" : ""
112
- capture_info_command = (block_given? && options[:stdout])? "echo [output captured and therefore not logged] >> #{options[:stdout]} && " : ""
113
-
114
- full_cmd = commands.collect do |c|
115
- escaped_command = c.gsub(/"/, QUOTE_REPLACEMENT).gsub(/</, LESS_THAN_REPLACEMENT)
116
- stdout_prompt_command = options[:stdout] ? "echo #{Platform.prompt} #{escaped_command} >> #{options[:stdout]} && " : ""
117
- stderr_prompt_command = options[:stderr] ? "echo #{Platform.prompt} #{escaped_command} >> #{options[:stderr]} && " : ""
118
- redirected_command = block_given? ? "#{c} #{stderr_opt}" : "#{c} #{stdout_opt} #{stderr_opt}"
119
-
120
- stdout_prompt_command + capture_info_command + stderr_prompt_command + redirected_command
121
- end.join(" && ")
122
- full_cmd.untaint
123
- end
124
- module_function :full_cmd
125
-
126
- def verify_exit_code(cmd, full_cmd, options)
127
- if($?.exitstatus != options[:exitstatus])
128
- error_message = "#{options[:stderr]} doesn't exist"
129
- if options[:stderr] && File.exist?(options[:stderr])
130
- File.open(options[:stderr]) do |errio|
131
- begin
132
- errio.seek(-1200, IO::SEEK_END)
133
- rescue Errno::EINVAL
134
- # ignore - it just means we didn't have 400 bytes.
135
- end
136
- error_message = errio.read
137
- end
138
- end
139
- raise ExecutionError.new(cmd, full_cmd, options[:dir] || Dir.pwd, $?.exitstatus, error_message)
140
- end
141
- end
142
- module_function :verify_exit_code
143
-
144
- def e(cmd, options, &proc)
145
- full_cmd = full_cmd(cmd, options, &proc)
146
-
147
- options[:env].each{|k,v| ENV[k]=v}
148
- begin
149
- STDOUT.puts "#{Platform.prompt} #{cmd}" if options[:stdout].nil?
150
- IO.popen(full_cmd, options[:mode]) do |io|
151
- if(block_given?)
152
- proc.call(io)
153
- else
154
- io.each_line do |line|
155
- STDOUT.puts line if options[:stdout].nil?
156
- end
157
- end
158
- end
159
- rescue Errno::ENOENT => e
160
- unless options[:stderr].nil?
161
- File.open(options[:stderr], "a") {|io| io.write(e.message)}
162
- else
163
- STDERR.puts e.message
164
- STDERR.puts e.backtrace.join("\n")
165
- end
166
- raise ExecutionError.new(cmd, full_cmd, options[:dir] || Dir.pwd.untaint, nil, e.message)
167
- ensure
168
- verify_exit_code(cmd, full_cmd, options)
169
- end
170
- end
171
- module_function :e
172
-
173
- end