rspec_parallel 0.1.1 → 0.1.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.
- data/lib/rspec_parallel.rb +405 -92
- metadata +3 -3
data/lib/rspec_parallel.rb
CHANGED
|
@@ -3,30 +3,57 @@ $LOAD_PATH << File.dirname(__FILE__)
|
|
|
3
3
|
require 'progressbar'
|
|
4
4
|
require 'color_helper'
|
|
5
5
|
require 'thread'
|
|
6
|
+
require 'rexml/document'
|
|
7
|
+
include REXML
|
|
6
8
|
include ColorHelpers
|
|
7
9
|
|
|
8
10
|
class Rspec_parallel
|
|
9
|
-
|
|
10
|
-
|
|
11
|
+
|
|
12
|
+
MAX_RERUN_TIMES = 10
|
|
13
|
+
|
|
14
|
+
def initialize(options = {})
|
|
15
|
+
@options = {:thread_number => 4, :case_folder => "./spec/", :report_folder => "./reports/",
|
|
16
|
+
:filter => {}, :env_list => [], :show_pending => false, :rerun => false,
|
|
17
|
+
:separate_rerun_report => true}.merge(options)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def run_tests()
|
|
21
|
+
start_time = Time.now # timer of rspec task
|
|
22
|
+
@queue = Queue.new # store all tests to run
|
|
23
|
+
@case_info_list = [] # store results of all tests
|
|
24
|
+
@lock = Mutex.new # use lock to avoid output mess up
|
|
25
|
+
|
|
26
|
+
thread_number = @options[:thread_number]
|
|
11
27
|
if thread_number < 1
|
|
12
28
|
puts red("threads_number can't be less than 1")
|
|
13
|
-
|
|
29
|
+
exit(1)
|
|
14
30
|
end
|
|
15
|
-
|
|
16
|
-
@filter_options = filter_options
|
|
17
|
-
@env_list = env_list
|
|
18
|
-
@show_pending = show_pending
|
|
19
|
-
@queue = Queue.new
|
|
20
|
-
end
|
|
31
|
+
puts yellow("threads number: #{thread_number}\n")
|
|
21
32
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
33
|
+
rerun = @options[:rerun]
|
|
34
|
+
separate_rerun_report = @options[:separate_rerun_report]
|
|
35
|
+
if rerun && separate_rerun_report
|
|
36
|
+
@report_folder = get_rerun_folder(true)
|
|
37
|
+
if @report_folder.include? "rerun#{MAX_RERUN_TIMES + 1}"
|
|
38
|
+
puts yellow("rerun task has been executed for #{MAX_RERUN_TIMES}" +
|
|
39
|
+
" times, maybe you should start a new run")
|
|
40
|
+
exit(1)
|
|
41
|
+
end
|
|
42
|
+
else
|
|
43
|
+
@report_folder = @options[:report_folder]
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
filter = @options[:filter]
|
|
47
|
+
if rerun
|
|
48
|
+
get_failed_cases
|
|
49
|
+
else
|
|
50
|
+
parse_case_list(filter)
|
|
51
|
+
end
|
|
27
52
|
|
|
28
|
-
|
|
29
|
-
|
|
53
|
+
if @queue.empty?
|
|
54
|
+
puts yellow("no cases to run, exit.")
|
|
55
|
+
return
|
|
56
|
+
end
|
|
30
57
|
|
|
31
58
|
pbar = ProgressBar.new("0/#{@queue.size}", @queue.size, $stdout)
|
|
32
59
|
pbar.format_arguments = [:title, :percentage, :bar, :stat]
|
|
@@ -39,34 +66,45 @@ class Rspec_parallel
|
|
|
39
66
|
Thread.abort_on_exception = false
|
|
40
67
|
threads = []
|
|
41
68
|
|
|
42
|
-
|
|
69
|
+
thread_number.times do |i|
|
|
43
70
|
threads << Thread.new do
|
|
44
71
|
until @queue.empty?
|
|
45
72
|
task = @queue.pop
|
|
46
73
|
env_extras = {}
|
|
47
|
-
|
|
48
|
-
|
|
74
|
+
env_list = @options[:env_list]
|
|
75
|
+
if env_list && env_list[i]
|
|
76
|
+
env_extras = env_list[i]
|
|
49
77
|
end
|
|
78
|
+
t1 = Time.now
|
|
50
79
|
task_output = run_task(task, env_extras)
|
|
80
|
+
t2 = Time.now
|
|
81
|
+
case_info = parse_case_log(task_output)
|
|
82
|
+
unless case_info
|
|
83
|
+
puts task_output
|
|
84
|
+
next
|
|
85
|
+
end
|
|
86
|
+
case_info['duration'] = t2 - t1
|
|
87
|
+
@case_info_list << case_info
|
|
51
88
|
|
|
52
|
-
if
|
|
89
|
+
if case_info['status'] == 'fail'
|
|
53
90
|
@lock.synchronize do
|
|
54
91
|
failure_number += 1
|
|
55
|
-
|
|
56
|
-
failure_list << [task, failure_log]
|
|
92
|
+
failure_list << case_info
|
|
57
93
|
|
|
58
94
|
# print failure immediately during the execution
|
|
59
95
|
$stdout.print "\e[K"
|
|
60
96
|
if failure_number == 1
|
|
61
|
-
|
|
97
|
+
$stdout.print "Failures:\n\n"
|
|
62
98
|
end
|
|
63
|
-
puts " #{failure_number}) #{
|
|
64
|
-
|
|
99
|
+
puts " #{failure_number}) #{case_info['test_name']}"
|
|
100
|
+
$stdout.print "#{red(case_info['error_message'])}"
|
|
101
|
+
$stdout.print "#{cyan(case_info['error_stack_trace'])}"
|
|
102
|
+
$stdout.print red(" (Failure time: #{Time.now})\n\n")
|
|
65
103
|
end
|
|
66
|
-
elsif
|
|
104
|
+
elsif case_info['status'] == 'pending'
|
|
67
105
|
@lock.synchronize do
|
|
68
106
|
pending_number += 1
|
|
69
|
-
pending_list <<
|
|
107
|
+
pending_list << case_info
|
|
70
108
|
end
|
|
71
109
|
end
|
|
72
110
|
case_number += 1
|
|
@@ -82,49 +120,44 @@ class Rspec_parallel
|
|
|
82
120
|
pbar.finish
|
|
83
121
|
|
|
84
122
|
# print pending cases if configured
|
|
85
|
-
|
|
86
|
-
if
|
|
123
|
+
show_pending = @options[:show_pending]
|
|
124
|
+
if show_pending && pending_number > 0
|
|
125
|
+
$stdout.print "\n"
|
|
87
126
|
puts "Pending:"
|
|
88
|
-
pending_list.each {|
|
|
89
|
-
puts " #{
|
|
127
|
+
pending_list.each {|case_info|
|
|
128
|
+
puts " #{yellow(case_info['test_name'])}\n"
|
|
129
|
+
$stdout.print cyan("#{case_info['pending_info']}")
|
|
90
130
|
}
|
|
91
131
|
end
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
132
|
+
|
|
133
|
+
# print total time and summary result
|
|
134
|
+
end_time = Time.now
|
|
135
|
+
puts "\nFinished in #{format_time(end_time-start_time)}\n"
|
|
95
136
|
if failure_number > 0
|
|
96
137
|
$stdout.print red("#{case_number} examples, #{failure_number} failures")
|
|
97
138
|
$stdout.print red(", #{pending_number} pending") if pending_number > 0
|
|
139
|
+
elsif pending_number > 0
|
|
140
|
+
$stdout.print yellow("#{case_number} examples, #{failure_number} failures, #{pending_number} pending")
|
|
98
141
|
else
|
|
99
|
-
$stdout.print
|
|
100
|
-
$stdout.print yellow(", #{pending_number} pending") if pending_number > 0
|
|
142
|
+
$stdout.print green("#{case_number} examples, 0 failures")
|
|
101
143
|
end
|
|
102
144
|
$stdout.print "\n"
|
|
103
145
|
|
|
104
|
-
#
|
|
146
|
+
# print rerun command of failed examples
|
|
105
147
|
unless failure_list.empty?
|
|
106
|
-
rerun_file = File.new('./rerun.sh', 'w', 0777)
|
|
107
148
|
$stdout.print "\nFailed examples:\n\n"
|
|
108
|
-
failure_list.
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
case_desc = line
|
|
112
|
-
break
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
rerun_cmd = 'rspec .' + log[0].match(/\/spec\/.*_spec\.rb:\d{1,4}/).to_s
|
|
116
|
-
rerun_file.puts "echo ----#{case_desc}"
|
|
117
|
-
rerun_file.puts rerun_cmd + " # #{case_desc}"
|
|
118
|
-
$stdout.print red(rerun_cmd)
|
|
119
|
-
$stdout.print cyan(" # #{case_desc}")
|
|
149
|
+
failure_list.each do |case_info|
|
|
150
|
+
$stdout.print red(case_info['rerun_cmd'].split(' # ')[0])
|
|
151
|
+
$stdout.print cyan(" # #{case_info['test_name']}\n")
|
|
120
152
|
end
|
|
121
|
-
rerun_file.close
|
|
122
|
-
$stdout.print "\n"
|
|
123
153
|
end
|
|
154
|
+
|
|
155
|
+
generate_reports(end_time - start_time, rerun && !separate_rerun_report)
|
|
124
156
|
end
|
|
125
157
|
|
|
126
158
|
def get_case_list
|
|
127
|
-
|
|
159
|
+
case_folder = @options[:case_folder]
|
|
160
|
+
file_list = `grep -rl '' #{case_folder}`
|
|
128
161
|
case_list = []
|
|
129
162
|
file_list.each_line { |filename|
|
|
130
163
|
unless filename.include? "_spec.rb"
|
|
@@ -209,14 +242,14 @@ class Rspec_parallel
|
|
|
209
242
|
case_list
|
|
210
243
|
end
|
|
211
244
|
|
|
212
|
-
def parse_case_list()
|
|
245
|
+
def parse_case_list(filter)
|
|
213
246
|
all_case_list = get_case_list
|
|
214
247
|
pattern_filter_list = []
|
|
215
248
|
tags_filter_list = []
|
|
216
249
|
|
|
217
|
-
if
|
|
250
|
+
if filter["pattern"]
|
|
218
251
|
all_case_list.each { |c|
|
|
219
|
-
if c["line"].match(
|
|
252
|
+
if c["line"].match(filter["pattern"])
|
|
220
253
|
pattern_filter_list << c
|
|
221
254
|
end
|
|
222
255
|
}
|
|
@@ -224,10 +257,10 @@ class Rspec_parallel
|
|
|
224
257
|
pattern_filter_list = all_case_list
|
|
225
258
|
end
|
|
226
259
|
|
|
227
|
-
if
|
|
260
|
+
if filter["tags"]
|
|
228
261
|
include_tags = []
|
|
229
262
|
exclude_tags = []
|
|
230
|
-
all_tags =
|
|
263
|
+
all_tags = filter["tags"].split(",")
|
|
231
264
|
all_tags.each { |tag|
|
|
232
265
|
if tag.start_with? "~"
|
|
233
266
|
exclude_tags << tag.gsub("~", "")
|
|
@@ -245,15 +278,65 @@ class Rspec_parallel
|
|
|
245
278
|
tags_filter_list = pattern_filter_list
|
|
246
279
|
end
|
|
247
280
|
|
|
281
|
+
tags_filter_list = reorder_tests(tags_filter_list)
|
|
282
|
+
|
|
248
283
|
tags_filter_list.each { |t|
|
|
249
284
|
@queue << t["line"]
|
|
250
285
|
}
|
|
251
286
|
end
|
|
252
287
|
|
|
288
|
+
def get_rerun_folder(get_next=false)
|
|
289
|
+
rerun_folder = @options[:report_folder]
|
|
290
|
+
i = MAX_RERUN_TIMES
|
|
291
|
+
while(i > 0)
|
|
292
|
+
if File.exists? File.join(rerun_folder, "rerun#{i}")
|
|
293
|
+
if get_next
|
|
294
|
+
rerun_folder = File.join(rerun_folder, "rerun#{i + 1}")
|
|
295
|
+
else
|
|
296
|
+
rerun_folder = File.join(rerun_folder, "rerun#{i}")
|
|
297
|
+
end
|
|
298
|
+
break
|
|
299
|
+
end
|
|
300
|
+
i -= 1
|
|
301
|
+
end
|
|
302
|
+
if get_next && (rerun_folder.include? "rerun") == false
|
|
303
|
+
rerun_folder = File.join(rerun_folder, 'rerun1')
|
|
304
|
+
end
|
|
305
|
+
rerun_folder
|
|
306
|
+
end
|
|
307
|
+
|
|
308
|
+
def get_failed_cases
|
|
309
|
+
if @options[:separate_rerun_report]
|
|
310
|
+
last_report_folder = get_rerun_folder
|
|
311
|
+
last_report_file_path = File.join(last_report_folder, "junitResult.xml")
|
|
312
|
+
else
|
|
313
|
+
last_report_file_path = File.join(@report_folder, "junitResult.xml")
|
|
314
|
+
end
|
|
315
|
+
unless File.exists? last_report_file_path
|
|
316
|
+
puts yellow("can't find result of last run")
|
|
317
|
+
exit(1)
|
|
318
|
+
end
|
|
319
|
+
report_file = File.new(last_report_file_path)
|
|
320
|
+
begin
|
|
321
|
+
@doc = REXML::Document.new report_file
|
|
322
|
+
rescue
|
|
323
|
+
puts red("invalid format of report xml")
|
|
324
|
+
exit(1)
|
|
325
|
+
end
|
|
326
|
+
|
|
327
|
+
@doc.elements.each("result/suites/suite/cases/case") do |c|
|
|
328
|
+
if c.get_elements("errorDetails")[0]
|
|
329
|
+
rerun_cmd = c.get_elements("rerunCommand")[0].text
|
|
330
|
+
line = rerun_cmd.split('#')[0].gsub('rspec ', '').strip
|
|
331
|
+
@queue << line
|
|
332
|
+
end
|
|
333
|
+
end
|
|
334
|
+
end
|
|
335
|
+
|
|
253
336
|
def run_task(task, env_extras)
|
|
254
337
|
cmd = [] # Preparing command for popen
|
|
255
338
|
cmd << ENV.to_hash.merge(env_extras)
|
|
256
|
-
cmd += ["bundle", "exec", "rspec", "--color", task]
|
|
339
|
+
cmd += ["bundle", "exec", "rspec", "-f", "d", "--color", task]
|
|
257
340
|
cmd
|
|
258
341
|
|
|
259
342
|
output = ""
|
|
@@ -264,6 +347,10 @@ class Rspec_parallel
|
|
|
264
347
|
output
|
|
265
348
|
end
|
|
266
349
|
|
|
350
|
+
def reorder_tests(case_list)
|
|
351
|
+
return case_list
|
|
352
|
+
end
|
|
353
|
+
|
|
267
354
|
def format_time(t)
|
|
268
355
|
time_str = ''
|
|
269
356
|
time_str += (t / 3600).to_i.to_s + " hours " if t > 3600
|
|
@@ -272,42 +359,268 @@ class Rspec_parallel
|
|
|
272
359
|
time_str
|
|
273
360
|
end
|
|
274
361
|
|
|
275
|
-
def
|
|
276
|
-
return
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
362
|
+
def parse_case_log(str)
|
|
363
|
+
return nil if str =~ /0 examples/
|
|
364
|
+
result = {}
|
|
365
|
+
logs = []
|
|
366
|
+
str.each_line {|l| logs << l}
|
|
367
|
+
return nil if logs == []
|
|
368
|
+
|
|
369
|
+
stderr = ''
|
|
370
|
+
unless logs[0].start_with? 'Run options:'
|
|
371
|
+
clear_logs = []
|
|
372
|
+
logs_start = false
|
|
373
|
+
for i in 0..logs.length-1
|
|
374
|
+
if logs[i].strip.start_with? 'Run options:'
|
|
375
|
+
logs_start = true
|
|
376
|
+
end
|
|
377
|
+
if logs_start
|
|
378
|
+
clear_logs << logs[i]
|
|
379
|
+
else
|
|
380
|
+
stderr += logs[i]
|
|
381
|
+
end
|
|
289
382
|
end
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
383
|
+
logs = clear_logs
|
|
384
|
+
end
|
|
385
|
+
result['stderr'] = stderr
|
|
386
|
+
|
|
387
|
+
stdout = ''
|
|
388
|
+
if logs[4].strip != ''
|
|
389
|
+
clear_logs = []
|
|
390
|
+
stdout_start = true
|
|
391
|
+
for i in 0..logs.length-1
|
|
392
|
+
if i < 3
|
|
393
|
+
clear_logs << logs[i]
|
|
394
|
+
elsif stdout_start && logs[i+1].strip == ''
|
|
395
|
+
clear_logs << logs[i]
|
|
396
|
+
stdout_start = false
|
|
397
|
+
elsif !stdout_start
|
|
398
|
+
clear_logs << logs[i]
|
|
399
|
+
else
|
|
400
|
+
stdout += logs[i]
|
|
401
|
+
end
|
|
402
|
+
end
|
|
403
|
+
logs = clear_logs
|
|
404
|
+
end
|
|
405
|
+
result['stdout'] = stdout
|
|
406
|
+
|
|
407
|
+
result['class_name'] = logs[2].strip
|
|
408
|
+
result['test_desc'] = logs[3].gsub(/\((FAILED|PENDING).+\)/, '').strip
|
|
409
|
+
result['test_name'] = result['class_name'] + ' ' + result['test_desc']
|
|
410
|
+
|
|
411
|
+
if logs[-1].include? '1 pending'
|
|
412
|
+
result['status'] = 'pending'
|
|
413
|
+
pending_info = ''
|
|
414
|
+
for i in 7..logs.length-4
|
|
415
|
+
next if logs[i].strip == ''
|
|
416
|
+
pending_info += logs[i]
|
|
417
|
+
end
|
|
418
|
+
result['pending_info'] = pending_info
|
|
419
|
+
elsif logs[-1].include? '0 failures'
|
|
420
|
+
result['status'] = 'pass'
|
|
421
|
+
elsif logs[-1].start_with? 'rspec '
|
|
422
|
+
result['status'] = 'fail'
|
|
423
|
+
result['rerun_cmd'] = logs[-1]
|
|
424
|
+
error_message = logs[8]
|
|
425
|
+
error_stack_trace = ''
|
|
426
|
+
for i in 9..logs.length-8
|
|
427
|
+
next if logs[i].strip == ''
|
|
428
|
+
if logs[i].strip.start_with? '# '
|
|
429
|
+
error_stack_trace += logs[i]
|
|
430
|
+
else
|
|
431
|
+
error_message += logs[i]
|
|
432
|
+
end
|
|
433
|
+
end
|
|
434
|
+
error_message.each_line do |l|
|
|
435
|
+
next if l.include? 'Error:'
|
|
436
|
+
result['error_details'] = l.strip
|
|
437
|
+
break
|
|
438
|
+
end
|
|
439
|
+
if error_message.index(result['error_details']) < error_message.length - result['error_details'].length - 10
|
|
440
|
+
result['error_details'] += "..."
|
|
441
|
+
end
|
|
442
|
+
result['error_message'] = error_message
|
|
443
|
+
result['error_stack_trace'] = error_stack_trace
|
|
444
|
+
else
|
|
445
|
+
result['status'] = 'unknown'
|
|
446
|
+
end
|
|
447
|
+
|
|
448
|
+
result
|
|
293
449
|
end
|
|
294
450
|
|
|
295
|
-
def
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
451
|
+
def generate_reports(time, update_report)
|
|
452
|
+
%x[mkdir #{@report_folder}] unless File.exists? @report_folder
|
|
453
|
+
@summary_report = ""
|
|
454
|
+
@summary_report += "<?xml version='1.0' encoding='UTF-8'?>\n"
|
|
455
|
+
@summary_report += "<result>\n"
|
|
456
|
+
@summary_report += "<suites>\n"
|
|
457
|
+
|
|
458
|
+
class_name_list = []
|
|
459
|
+
@case_info_list.each do |case_info|
|
|
460
|
+
class_name_list << case_info['class_name']
|
|
461
|
+
end
|
|
462
|
+
class_name_list.uniq!
|
|
463
|
+
class_name_list.sort!
|
|
464
|
+
class_name_list.each do |class_name|
|
|
465
|
+
temp_case_info_list = []
|
|
466
|
+
@case_info_list.each do |case_info|
|
|
467
|
+
if case_info['class_name'] == class_name
|
|
468
|
+
temp_case_info_list << case_info
|
|
469
|
+
end
|
|
308
470
|
end
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
471
|
+
generate_single_file_report(temp_case_info_list)
|
|
472
|
+
end
|
|
473
|
+
|
|
474
|
+
if update_report
|
|
475
|
+
update_ci_report
|
|
476
|
+
end
|
|
477
|
+
|
|
478
|
+
@summary_report += "</suites>\n"
|
|
479
|
+
@summary_report += "<duration>#{time}</duration>\n"
|
|
480
|
+
@summary_report += "<keepLongStdio>false</keepLongStdio>\n"
|
|
481
|
+
@summary_report += "</result>\n"
|
|
482
|
+
|
|
483
|
+
report_file_path = File.join(@report_folder, 'junitResult.xml')
|
|
484
|
+
fr = File.new(report_file_path, 'w')
|
|
485
|
+
if update_report
|
|
486
|
+
fr.puts @doc
|
|
487
|
+
else
|
|
488
|
+
fr.puts @summary_report
|
|
489
|
+
end
|
|
490
|
+
fr.close
|
|
491
|
+
end
|
|
492
|
+
|
|
493
|
+
def update_ci_report
|
|
494
|
+
@doc.elements.each("result/suites/suite/cases/case") do |c1|
|
|
495
|
+
if c1.get_elements("errorDetails")[0]
|
|
496
|
+
test_name = c1.get_elements("testName")[0].text
|
|
497
|
+
@case_info_list.each do |c2|
|
|
498
|
+
if test_name == c2['test_name'].encode({:xml => :attr})
|
|
499
|
+
c1.get_elements("duration")[0].text = c2['duration']
|
|
500
|
+
if c2['status'] == 'fail'
|
|
501
|
+
text = c2['error_message'].gsub('Failure/Error: ', '') + "\n"
|
|
502
|
+
text += c2['error_stack_trace'].gsub('# ', '')
|
|
503
|
+
c1.get_elements("errorStackTrace")[0].text = text
|
|
504
|
+
c1.get_elements("errorDetails")[0].text = c2['error_details']
|
|
505
|
+
c1.get_elements("rerunCommand")[0].text = c2['rerun_cmd']
|
|
506
|
+
else
|
|
507
|
+
c1.delete c1.get_elements("errorDetails")[0]
|
|
508
|
+
c1.delete c1.get_elements("errorStackTrace")[0]
|
|
509
|
+
c1.delete c1.get_elements("rerunCommand")[0]
|
|
510
|
+
end
|
|
511
|
+
break
|
|
512
|
+
end
|
|
513
|
+
end
|
|
514
|
+
end
|
|
515
|
+
end
|
|
516
|
+
end
|
|
517
|
+
|
|
518
|
+
def generate_single_file_report(case_info_list)
|
|
519
|
+
return if case_info_list == []
|
|
520
|
+
class_name = case_info_list[0]['class_name']
|
|
521
|
+
file_name = File.join(@report_folder, class_name.gsub(/:+/, '-') + '.xml')
|
|
522
|
+
name = class_name.gsub(':', '_')
|
|
523
|
+
|
|
524
|
+
suite_duration = 0.0
|
|
525
|
+
fail_num = 0
|
|
526
|
+
error_num = 0
|
|
527
|
+
pending_num = 0
|
|
528
|
+
stdout = ''
|
|
529
|
+
stderr = ''
|
|
530
|
+
stdout_list = []
|
|
531
|
+
stderr_list = []
|
|
532
|
+
case_desc_list = []
|
|
533
|
+
case_info_list.each do |case_info|
|
|
534
|
+
suite_duration += case_info['duration']
|
|
535
|
+
stdout_list << case_info['stdout']
|
|
536
|
+
stderr_list << case_info['stderr']
|
|
537
|
+
case_desc_list << case_info['test_desc']
|
|
538
|
+
if case_info['status'] == 'fail'
|
|
539
|
+
if case_info['error_message'].include? "expect"
|
|
540
|
+
fail_num += 1
|
|
541
|
+
else
|
|
542
|
+
error_num += 1
|
|
543
|
+
end
|
|
544
|
+
elsif case_info['status'] == 'pending'
|
|
545
|
+
pending_num += 1
|
|
546
|
+
end
|
|
547
|
+
end
|
|
548
|
+
stdout_list.uniq!
|
|
549
|
+
stderr_list.uniq!
|
|
550
|
+
case_desc_list.sort!
|
|
551
|
+
stdout_list.each {|s| stdout += s}
|
|
552
|
+
stderr_list.each {|s| stderr += s}
|
|
553
|
+
|
|
554
|
+
@summary_report += "<suite>\n"
|
|
555
|
+
@summary_report += "<file>#{file_name}</file>\n"
|
|
556
|
+
@summary_report += "<name>#{name}</name>\n"
|
|
557
|
+
@summary_report += "<stdout>\n"
|
|
558
|
+
@summary_report += stdout.encode({:xml => :text}) if stdout.length > 0
|
|
559
|
+
@summary_report += "</stdout>\n"
|
|
560
|
+
@summary_report += "<stderr>\n"
|
|
561
|
+
@summary_report += stderr.encode({:xml => :text}) if stderr.length > 0
|
|
562
|
+
@summary_report += "</stderr>\n"
|
|
563
|
+
@summary_report += "<duration>#{suite_duration}</duration>\n"
|
|
564
|
+
@summary_report += "<cases>\n"
|
|
565
|
+
|
|
566
|
+
ff = File.new(file_name, 'w')
|
|
567
|
+
ff.puts '<?xml version="1.0" encoding="UTF-8"?>'
|
|
568
|
+
ff.puts "<testsuite name=\"#{class_name}\" tests=\"#{case_info_list.size}\" time=\"#{suite_duration}\" failures=\"#{fail_num}\" errors=\"#{error_num}\" skipped=\"#{pending_num}\">"
|
|
569
|
+
|
|
570
|
+
case_desc_list.each do |case_desc|
|
|
571
|
+
i = case_info_list.index {|c| c['test_desc'] == case_desc}
|
|
572
|
+
case_info = case_info_list[i]
|
|
573
|
+
test_name = case_info['test_name']
|
|
574
|
+
test_name += " (PENDING)" if case_info['status'] == 'pending'
|
|
575
|
+
test_name = test_name.encode({:xml => :attr})
|
|
576
|
+
@summary_report += "<case>\n"
|
|
577
|
+
@summary_report += "<duration>#{case_info['duration']}</duration>\n"
|
|
578
|
+
@summary_report += "<className>#{case_info['class_name']}</className>\n"
|
|
579
|
+
@summary_report += "<testName>#{test_name}</testName>\n"
|
|
580
|
+
@summary_report += "<skipped>#{case_info['status'] == 'pending'}</skipped>\n"
|
|
581
|
+
|
|
582
|
+
ff.puts "<testcase name=#{test_name} time=\"#{case_info['duration']}\">"
|
|
583
|
+
ff.puts "<skipped/>" if case_info['status'] == 'pending'
|
|
584
|
+
|
|
585
|
+
if case_info['status'] == 'fail'
|
|
586
|
+
@summary_report += "<errorStackTrace>\n"
|
|
587
|
+
@summary_report += case_info['error_message'].encode({:xml => :text}).gsub('Failure/Error: ', '')
|
|
588
|
+
@summary_report += case_info['error_stack_trace'].encode({:xml => :text}).gsub('# ', '')
|
|
589
|
+
@summary_report += "</errorStackTrace>\n"
|
|
590
|
+
@summary_report += "<errorDetails>\n"
|
|
591
|
+
@summary_report += case_info['error_details'].encode({:xml => :text})
|
|
592
|
+
@summary_report += "</errorDetails>\n"
|
|
593
|
+
@summary_report += "<rerunCommand>#{case_info['rerun_cmd'].encode({:xml => :text})}</rerunCommand>\n"
|
|
594
|
+
|
|
595
|
+
if case_info['error_message'].include? "expected"
|
|
596
|
+
type = "RSpec::Expectations::ExpectationNotMetError"
|
|
597
|
+
elsif case_info['error_message'].include? "RuntimeError"
|
|
598
|
+
type = "RuntimeError"
|
|
599
|
+
else
|
|
600
|
+
type = "UnknownError"
|
|
601
|
+
end
|
|
602
|
+
ff.puts "<failure type=\"#{type}\" message=#{case_info['error_details'].encode({:xml => :attr})}>"
|
|
603
|
+
ff.puts case_info['error_message'].encode({:xml => :text}).gsub('Failure/Error: ', '')
|
|
604
|
+
ff.puts case_info['error_stack_trace'].encode({:xml => :text}).gsub('# ', '')
|
|
605
|
+
ff.puts "</failure>"
|
|
606
|
+
ff.puts "<rerunCommand>#{case_info['rerun_cmd'].encode({:xml => :text})}</rerunCommand>"
|
|
607
|
+
end
|
|
608
|
+
@summary_report += "<failedSince>0</failedSince>\n"
|
|
609
|
+
@summary_report += "</case>\n"
|
|
610
|
+
|
|
611
|
+
ff.puts "</testcase>"
|
|
612
|
+
end
|
|
613
|
+
|
|
614
|
+
@summary_report += "</cases>\n"
|
|
615
|
+
@summary_report += "</suite>"
|
|
616
|
+
|
|
617
|
+
ff.puts "<system-out>"
|
|
618
|
+
ff.puts stdout.encode({:xml => :text}) if stdout.length > 0
|
|
619
|
+
ff.puts "</system-out>"
|
|
620
|
+
ff.puts "<system-err>"
|
|
621
|
+
ff.puts stderr.encode({:xml => :text}) if stderr.length > 0
|
|
622
|
+
ff.puts "</system-err>"
|
|
623
|
+
ff.puts "</testsuite>"
|
|
624
|
+
ff.close
|
|
312
625
|
end
|
|
313
626
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: rspec_parallel
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.2
|
|
5
5
|
prerelease:
|
|
6
6
|
platform: ruby
|
|
7
7
|
authors:
|
|
@@ -9,7 +9,7 @@ authors:
|
|
|
9
9
|
autorequire:
|
|
10
10
|
bindir: bin
|
|
11
11
|
cert_chain: []
|
|
12
|
-
date: 2012-
|
|
12
|
+
date: 2012-11-27 00:00:00.000000000 Z
|
|
13
13
|
dependencies:
|
|
14
14
|
- !ruby/object:Gem::Dependency
|
|
15
15
|
name: progressbar
|
|
@@ -28,7 +28,7 @@ dependencies:
|
|
|
28
28
|
- !ruby/object:Gem::Version
|
|
29
29
|
version: 0.11.0
|
|
30
30
|
description: parallel all rspec examples
|
|
31
|
-
email:
|
|
31
|
+
email: zhangcheng@rbcon.com
|
|
32
32
|
executables: []
|
|
33
33
|
extensions: []
|
|
34
34
|
extra_rdoc_files: []
|