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.
Files changed (2) hide show
  1. data/lib/rspec_parallel.rb +405 -92
  2. metadata +3 -3
@@ -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
- def initialize(thread_number, case_folder, filter_options = {}, env_list = [], show_pending = false)
10
- @thread_number = thread_number
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
- return
29
+ exit(1)
14
30
  end
15
- @case_folder = case_folder
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
- def run_tests()
23
- # use lock to avoid output mess up
24
- @lock = Mutex.new
25
- # timer of rspec task
26
- start_time = Time.now
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
- puts yellow("threads number: #{@thread_number}\n")
29
- parse_case_list
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
- @thread_number.times do |i|
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
- if @env_list && @env_list[i]
48
- env_extras = @env_list[i]
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 task_output =~ /Failures/ || task_output =~ /0 examples/
89
+ if case_info['status'] == 'fail'
53
90
  @lock.synchronize do
54
91
  failure_number += 1
55
- failure_log = parse_failure_log(task_output)
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
- puts "Failures:"
97
+ $stdout.print "Failures:\n\n"
62
98
  end
63
- puts " #{failure_number}) #{failure_log}\n"
64
- puts red(" (Failure time: #{Time.now})\n\n")
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 task_output =~ /Pending/
104
+ elsif case_info['status'] == 'pending'
67
105
  @lock.synchronize do
68
106
  pending_number += 1
69
- pending_list << [task, parse_pending_log(task_output)]
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
- $stdout.print "\n"
86
- if @show_pending && pending_number > 0
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 {|p|
89
- puts " #{p[1]}\n"
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
- $stdout.print "\n"
93
- t2 = Time.now
94
- puts green("Finished in #{format_time(t2-start_time)}\n")
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 yellow("#{case_number} examples, #{failure_number} failures")
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
- # record failed rspec examples to rerun.sh
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.each_with_index do |log, i|
109
- case_desc = ''
110
- log[1].each_line {|line|
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
- file_list = `grep -rl '' #{@case_folder}`
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 @filter_options["pattern"]
250
+ if filter["pattern"]
218
251
  all_case_list.each { |c|
219
- if c["line"].match(@filter_options["pattern"])
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 @filter_options["tags"]
260
+ if filter["tags"]
228
261
  include_tags = []
229
262
  exclude_tags = []
230
- all_tags = @filter_options["tags"].split(",")
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 parse_failure_log(str)
276
- return str if str =~ /0 examples/
277
- index1 = str.index('1) ')
278
- index2 = str.index('Finished in')
279
- output = ""
280
- temp = str.slice(index1+3..index2-1).strip
281
- first_line = true
282
- temp.each_line { |line|
283
- if first_line
284
- output += line
285
- elsif line.strip.start_with? "# "
286
- output += cyan(line)
287
- else
288
- output += red(line)
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
- first_line = false
291
- }
292
- output
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 parse_pending_log(str)
296
- index1 = str.index('Pending:')
297
- index2 = str.index('Finished in')
298
- output = ""
299
- temp = str.slice(index1+8..index2-1).strip
300
- first_line = true
301
- temp.each_line { |line|
302
- if first_line
303
- output += yellow(line)
304
- elsif line.strip.start_with? "# "
305
- output += cyan(line)
306
- else
307
- output += line
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
- first_line = false
310
- }
311
- output
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.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-10-15 00:00:00.000000000 Z
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: zhchshy@hotmail.com
31
+ email: zhangcheng@rbcon.com
32
32
  executables: []
33
33
  extensions: []
34
34
  extra_rdoc_files: []