DTR 0.0.1 → 0.0.2

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.
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