Class: Browsery::Parallel

Inherits:
Object
  • Object
show all
Defined in:
lib/browsery/parallel.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(simultaneous_jobs, all_tests) ⇒ Parallel

Returns a new instance of Parallel.



6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# File 'lib/browsery/parallel.rb', line 6

def initialize(simultaneous_jobs, all_tests)
  @start_time = Time.now

  @result_dir = 'logs/tap_results'

  connector = Browsery.settings.connector
  @on_sauce = true if connector.include? 'saucelabs'
  @platform = connector.split(':')[2] || ''

  @simultaneous_jobs = simultaneous_jobs
  @simultaneous_jobs = 10 if run_on_mac? # saucelabs account limit for parallel is 10 for mac
  @all_tests = all_tests

  @pids = []
  @static_run_command = "browsery -c #{Browsery.settings.connector} -e #{Browsery.settings.env}"
  if Browsery.settings.rerun_failure
    @static_run_command += " -R #{Browsery.settings.rerun_failure}"
  end
  tap_reporter_path = Browsery.gem_root.join('lib/tapout/custom_reporters/fancy_tap_reporter.rb')
  @pipe_tap = "--tapy | tapout --no-color -r #{tap_reporter_path.to_s} fancytap"
end

Instance Attribute Details

#all_testsObject (readonly)

Returns the value of attribute all_tests.



4
5
6
# File 'lib/browsery/parallel.rb', line 4

def all_tests
  @all_tests
end

#simultaneous_jobsObject (readonly)

Returns the value of attribute simultaneous_jobs.



4
5
6
# File 'lib/browsery/parallel.rb', line 4

def simultaneous_jobs
  @simultaneous_jobs
end

Instance Method Details

#aggregate_tap_resultsObject

Aggregate all individual test_*.t files replace them with one file - test_aggregated_result.tap so they will be considered as one test plan by tap result parser



72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
# File 'lib/browsery/parallel.rb', line 72

def aggregate_tap_results
  results_count = Dir.glob("#{@result_dir}/*.t").size
  File.open("#{@result_dir}/test_aggregated_result.tap", 'a+') do |result_file|
    result_stats = {
        'pass' => 0,
        'fail' => 0,
        'errs' => 0,
        'todo' => 0,
        'omit' => 0
    }
    result_stats_line_start = '  # 1 tests:'
    result_file.puts "1..#{results_count}"
    file_count = 0
    Dir.glob("#{@result_dir}/*.t") do |filename|
      file_count += 1
      File.open(filename, 'r') do |file|
        breakpoint_line = 0
        file.each_with_index do |line, index|
          next if index == 0 || (breakpoint_line > 0 && index > breakpoint_line)
          if line.start_with?(result_stats_line_start)
            pass, fail, errs, todo, omit = line.match(/(\d+) pass, (\d+) fail, (\d+) errs, (\d+) todo, (\d+) omit/).captures
            one_test_result = {
                'pass' => pass.to_i,
                'fail' => fail.to_i,
                'errs' => errs.to_i,
                'todo' => todo.to_i,
                'omit' => omit.to_i
            }
            result_stats = result_stats.merge(one_test_result) { |k, total, one| total + one }
            breakpoint_line = index
          elsif line.strip == '#'
            next
          else
            if line.start_with?('ok 1') || line.start_with?('not ok 1')
              line_begin, line_end = line.split('1 -')
              result_file.puts [line_begin, line_end].join("#{file_count} -")
            else
              result_file.puts line
            end
          end
        end
      end
      File.delete(filename)
    end
    result_file.puts '  #'
    result_file.puts "  # #{results_count} tests: #{result_stats['pass']} pass, #{result_stats['fail']} fail, #{result_stats['errs']} errs, #{result_stats['todo']} todo, #{result_stats['omit']} omit"
    result_file.puts "  # [00:00:00.00 0.00t/s 00.0000s/t] Finished at: #{Time.now}"
  end
end

#clean_result!Object

remove all results files under @result_dir if there’s any

Raises:

  • (Exception)


35
36
37
38
39
40
41
# File 'lib/browsery/parallel.rb', line 35

def clean_result!
  raise Exception, '@result_dir is not set' if @result_dir.nil?
  unless Dir.glob("#{@result_dir}/*").empty?
    FileUtils.rm_rf(Dir.glob("#{@result_dir}/*"))
  end
  puts "Cleaning result files.\n"
end

#count_browsery_processObject



122
123
124
125
# File 'lib/browsery/parallel.rb', line 122

def count_browsery_process
  counting_process_output = IO.popen "ps -ef | grep 'bin/#{@static_run_command}' -c"
  counting_process_output.readlines[0].to_i - 1 # minus grep process
end

#keep_running_full(all_to_run) ⇒ Object

recursively keep running ##simultaneous_jobs number of tests in parallel exit when no test left to run



159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
# File 'lib/browsery/parallel.rb', line 159

def keep_running_full(all_to_run)
  running_subprocess_count = count_browsery_process - 1 # minus parent process
  puts "WARNING: running_subprocess_count = #{running_subprocess_count}
        is more than what it is supposed to run(#{simultaneous_jobs}),
        notify browsery maintainers" if running_subprocess_count > simultaneous_jobs + 1
  while running_subprocess_count >= simultaneous_jobs
    sleep 5
    running_subprocess_count = count_browsery_process - 1
  end
  to_run_count = simultaneous_jobs - running_subprocess_count
  tests_to_run = all_to_run.slice!(0, to_run_count)

  run_test_set(tests_to_run)

  keep_running_full(all_to_run) if all_to_run.size > 0
end

#remove_redundant_tapObject



43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
# File 'lib/browsery/parallel.rb', line 43

def remove_redundant_tap
  ever_failed_tests_file = "#{@result_dir}/ever_failed_tests.json"
  if File.file? ever_failed_tests_file
    data_hash = JSON.parse(File.read(ever_failed_tests_file))
    data_hash.keys.each do |test|
      if test.start_with? 'test_'
        tap_result_file = "#{@result_dir}/#{test}.t"
        result_lines = IO.readlines(tap_result_file)
        last_tap_start_index = 0
        last_tap_end_index = result_lines.size - 1
        result_lines.each_with_index do |l, index|
          last_tap_start_index = index if l.delete!("\n") == '1..1'
        end
        File.open(tap_result_file, 'w') do |f|
          f.puts result_lines[last_tap_start_index..last_tap_end_index]
        end
        puts "Processed #{tap_result_file}"
      else
        next
      end
    end
  else
    puts "==> File #{ever_failed_tests_file} doesn't exist - all tests passed!"
  end
end

#run_in_parallel!Object

run multiple commands with logging to start multiple tests in parallel n = number of tests will be running in parallel

Parameters:

  • (Integer, Array)


130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
# File 'lib/browsery/parallel.rb', line 130

def run_in_parallel!
  size = all_tests.size
  if size <= simultaneous_jobs
    run_test_set(all_tests)
    puts "CAUTION! All #{size} tests are starting at the same time!"
    puts "will not really run it since computer will die" if size > 30
    sleep 20
  else
    first_test_set = all_tests[0, simultaneous_jobs]
    all_to_run = all_tests[simultaneous_jobs..(all_tests.size - 1)]
    run_test_set(first_test_set)
    keep_running_full(all_to_run)
  end

  Process.waitall
  puts "\nAll Complete! Started at #{@start_time} and finished at #{Time.now}\n"
end

#run_on_mac?boolean

return true only if specified to run on mac in connector

Returns:

  • (boolean)


30
31
32
# File 'lib/browsery/parallel.rb', line 30

def run_on_mac?
  @platform.include?('osx')
end

#run_test_set(test_set) ⇒ Object

runs each test from a test set in a separate child process



149
150
151
152
153
154
155
# File 'lib/browsery/parallel.rb', line 149

def run_test_set(test_set)
  test_set.each do |test|
    run_command = "#{@static_run_command} -n #{test} #{@pipe_tap} > #{@result_dir}/#{test}.t"
    pipe = IO.popen(run_command)
    puts "Running #{test}  #{pipe.pid}"
  end
end

#wait_all_done_saucelabsObject

Deprecated.

Too time consuming and fragile, should use more native wait/check of Process



192
193
194
195
196
197
198
199
200
# File 'lib/browsery/parallel.rb', line 192

def wait_all_done_saucelabs
  size = all_tests.size
  job_statuses = saucelabs_last_n_statuses(size)
  while job_statuses.include?('in progress')
    puts "There are tests still running, waiting..."
    sleep 20
    job_statuses = saucelabs_last_n_statuses(size)
  end
end

#wait_for_pids(pids) ⇒ Object

Deprecated.

Use more native wait/check of Process



177
178
179
180
181
182
183
184
185
186
187
188
189
# File 'lib/browsery/parallel.rb', line 177

def wait_for_pids(pids)
  running_pids = pids # assume all pids are running at this moment
  while running_pids.size > 1
    sleep 5
    puts "running_pids = #{running_pids}"
    running_pids.each do |pid|
      unless process_running?(pid)
        puts "#{pid} is not running, removing it from pool"
        running_pids.delete(pid)
      end
    end
  end
end