Module: Oats::Driver

Defined in:
lib/oats/driver.rb

Class Method Summary collapse

Class Method Details

.agentObject



12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# File 'lib/oats/driver.rb', line 12

def Driver.agent
  ENV['KILL_AGENT' ] = 'KILL_AGENT' if $oats_execution['options']["_:kill_agent"]
  ENV['OATS_USER' ] = $oats_execution['options']["_:oats_user"] if $oats_execution['options']["_:oats_user"]
  ENV['OATS_TESTS_REPOSITORY_VERSION' ] = $oats_execution['options']["_:repository_version"] if $oats_execution['options']["_:repository_version"]
  nick = $oats_execution['options']["execution:occ:agent_nickname"]
  ENV['OATS_AGENT_NICKNAME' ] = nick if nick
  port = $oats_execution['options']["execution:occ:agent_port"].to_s
  ENV['OATS_AGENT_PORT'] = port if port
  dir = ENV['HOME'] + "/results_archive/#{nick}/agent_logs"
  FileUtils.mkdir_p(dir) unless File.exists?(dir)
  ENV['OATS_AGENT_CONFIG_FILE'] = "#{dir}/config-agent.txt"
  dat = `date +'%m%d%H%M%S'`.chomp
  log_file = "#{dir}/agent_#{dat}.log"
  ENV['OATS_AGENT_LOGFILE'] = log_file
  agent_log_file = "#{dir}/agent.log"
  cmd = ENV['OATS_HOME'] + '/bin/agent'
  pid = `#{cmd}`.chomp
  puts "Running PID: " + pid + ', Log: ' + log_file
  #      echo "$NICKNAME $PORT $PID $DISPLAY_NUM"  >| $config_agent_file
  10.times do
    if File.exist? log_file
      FileUtils.rm_f agent_log_file
      FileUtils.ln log_file, agent_log_file
      break
    end
    sleep 1
  end
end

.parse_xl(path, id) ⇒ Object

Return all the included worksheet lists in XL and place all their test arrays in $oats_global



448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
# File 'lib/oats/driver.rb', line 448

def Driver.parse_xl(path,id)
  require 'spreadsheet' unless defined?(Spreadsheet)
  book = Spreadsheet.open path, 'rb'
  sheet = book.worksheet 'Main'
  Oats.assert sheet, "Could not locate worksheet 'Main' in: " + path
  list = Driver.xl_sheet_tests(sheet, id, 'Main', 'Test_Scenarios')
  Oats.assert !list.empty?, "No executable worksheets are listed in Main worksheet in: " + path
  $log.info "Processing 'Main' worksheet in XL file: " + path
  $log.info "Worksheets to be included: #{list.inspect}"
  tests = $oats_global['xl'] = {}
  test_files = []
  list.each do |ws|
    sheet = book.worksheet ws
    Oats.assert sheet, "XL file does not contain worksheet: " + ws
    suite_ws = id + '/' + ws
    execute_index = test_index = nil
    tests[suite_ws] = Driver.xl_sheet_tests(sheet, id, ws, 'Test_Cases')
    Oats.assert !tests[suite_ws].empty?, "No executable tests are listed in worksheet: " + suite_ws
    test_files.push suite_ws + '.xlw'
  end
  test_files
end

.process_oats_data(oats_data) ⇒ Object

Process each test_file in oats_data once



313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
# File 'lib/oats/driver.rb', line 313

def Driver.process_oats_data(oats_data)
  stop_file = oats_data['execution']['stop_file']
  $oats = oats_data  # Oats Data becomes global only this point down to allow recursion.
  begin
    ApplicationLogs.tail_errors # If the user just wants to tail, this never returns
  rescue OatsBadInput
    $log.fatal $!.to_s
    exit
  end
  # The environment file for tailing is included only in the user's very first variation.
  test_files = oats_data['execution']['test_files']
  if ! test_files or test_files.empty?
    $log.fatal( "Must provide at least one test.")
    $log.fatal 'Effective config file sequence ' + OatsData.history[1..-1].inspect
    return
  end
  BuildId.generate  # Specific to the AUT, supplied in the test_dir/lib
  # Dump oats_data and start each test with a fresh copy each time to avoid contamination
  oats_data_dump = Marshal.dump(oats_data)
  while test_file = test_files.shift do
    skip_test = false
    if stop_file and File.exist?(stop_file)
      $oats_info['stop_oats'] = Time.new.to_i
      FileUtils.mv(stop_file, stop_file + '_' + $oats_info['start_time'].to_s[2..-1] )
    end
    break if $oats_info['stop_oats']
    $oats = Marshal.load(oats_data_dump)
    begin
      if test_file.instance_of? Array
        path = test_file[0]
        id = path.sub( Regexp.new( '^' + $oats['execution']['dir_tests'] + '/', Regexp::IGNORECASE ) , '')
        id = id.sub(/\.rb/,'.'+test_file[1])
        tst = TestCase.new(id,test_file)
        tst.run
      else
        id, extension, path, handler = TestCase.parse_test_files(test_file)
        restrict_tests = $oats['execution']['restrict_tests']
        if restrict_tests and restrict_tests.include?(test_file)
          $oats['execution']['no_run'] = 'restrict_tests'
          #            skip_test = true
          #            next
        end
        if (extension == 'yml' and handler.nil?) or extension == 'xlw'
          unless path
            $log.error "Could not locate test list '#{id}'"
            TestList.current.variations.last.tests.pop
            return
          end
          begin
            Driver.process_test_yaml( $oats, id, path)
          ensure
            $oats = Marshal.load(oats_data_dump)
            post = $oats['execution']['handler_post_test_list']
            if post
              post_tst = TestCase.new(post)
              post_tst.type = 0
              post_tst.run
              TestList.current.post_test = post_tst
              TestList.current.variations.last.tests.pop
              $log.error post_tst.errors.first[1] unless post_tst.errors.empty?
            end
          end
        else
          case extension
          when 'xls'
            # Use it to include for suite.worksheet entries into test_files.
            # Later process these similar to list.yml files
            unless path
              $log.error "Could not locate XL file '#{id}'"
              TestList.current.variations.last.tests.pop
              return
            end
            test_files.concat Driver.parse_xl(path, id)

          when 'txt'
            list = TestList.txt_tests(path)
            $log.info "Including test list [#{path}]: #{list.inspect}"
            test_files = list + test_files
            skip_test = true

          else
            tst = TestCase.new(test_file, id, extension, path, handler)
            tst.run
          end
        end
      end
      #      rescue OatsError  # OatsTestError # Explicit Test Failure Assertion
      #        $log.debug "OatsError exception caught by test driver"
      #        tst = TestCase.new(test_file,path) unless path # TestData.Locate has failed
      #        $log.error $!.to_s.chomp
      #        TestData.error($!)
    rescue Exception => e
      $log.debug "General Exception caught by test driver."
      case e
      when OatsVerifyError # Selenium::CommandError, Timeout::Error then $log.error backtrace($!)
        $log.error e.to_s.chomp
      when OatsError
        $log.error TestCase.backtrace(e)
      else
        $log.error e
      end
      tst = TestCase.new(test_file) unless tst # just in case something happened above before test creation
      TestData.error(e)
    ensure
      if $oats_global['test_files'].instance_of?(Array)
        test_files += $oats_global['test_files']
        $oats_global.delete 'test_files'
      end
      next if skip_test or tst.nil? or tst.instance_of?(TestList) # coming from next above
      tst.end_time = Time.new.to_i
      case tst.status
      when 0 then $log.info "PASSED: #{tst.id}"
      when 1 then $log.warn "FAILED: #{tst.id} [#{tst.errors.last[1].chomp}]"
      when 2 then $log.warn "SKIPPED: #{tst.id}"
      else
        if tst.status.nil? and $oats_execution['agent']
          $log.error "Removing results of last test due to empty test.status, possibly due to agent shutdown."
          TestData.tests.pop
        else
          $log.error "Unrecognized test.status: [#{tst.status}] for [#{tst.name}] . Please inform OATS administrator."
        end
      end
      test_outputter = Log4r::Outputter['test_log']
      if test_outputter and ! test_outputter.closed?
        test_outputter.close
        $log.remove('test_log')
      end
      Report.oats_info_store
    end
  end

end

.process_test_yaml(oats_data, id = nil, test_yaml = nil) ⇒ Object

Expand additional test_files given as test_yaml.yml plus variations



178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
# File 'lib/oats/driver.rb', line 178

def Driver.process_test_yaml(oats_data, id = nil, test_yaml = nil)
  return if $oats_info['stop_oats']
  $oats = oats_data # TestData.locate needs test_dirs location.
  if test_yaml
    oats_data['execution']['test_files'] = nil # Ensure test files exist and taken from the input oats_file
    case test_yaml
    when /\.yml$/
      yaml_file = TestData.locate(test_yaml)
      unless yaml_file
        Oats.error "Can not locate file: #{test_yaml}"
        return
      end
      oats_data = OatsData.load( yaml_file, oats_data)
      #      oats_data['_']['load_history'].last.omit = true
    when /\.xls$/
      suite = id
      require 'spreadsheet' unless defined?(Spreadsheet)
      book = Spreadsheet.open test_yaml, 'rb'
      tests = $oats_global['xl']
      unless tests and tests[id]
        xl_id = File.dirname(id)
        list_id = File.basename(id)
        path = test_yaml.sub(/#{list_id}\.xls$/, File.basename(xl_id)+'.xls')
        Driver.parse_xl(path, xl_id)
        tests = $oats_global['xl']
      end
      header = nil
      book.worksheet('Business Flow').each do |row|
        unless header
          header = row.dup
          next
        end
        test_name = row.shift
        next unless tests[id].include?(test_name)
        test_id = suite + '/' + test_name
        tests[test_id] = { 'keywords' => row.collect{|i|i}} # Need to convert to Array
      end

      header = nil
      book.worksheet('Test Data').each do |row|
        unless header
          header = row.dup
          header.shift
          next
        end
        test_name = row.shift
        next unless tests[id].include?(test_name)
        test_id = suite + '/' + test_name
        Oats.assert tests[test_id],
          "No corresponding TC_ID was defined in Business Flow worksheet for Test Data worksheet TC_ID: " + File.basename(test_id)
        tests[test_id]['data'] = {}
        row.each_with_index do |cell,idx|
          next unless header[idx] and cell
          tests[test_id]['data'][header[idx]] = cell
        end
      end
      list = tests[id].collect{|t| suite + '/' + t + '.xltest'}
      $log.info "Processing worksheet [#{suite}] tests: #{tests[id].inspect}"
      oats_data['execution']['test_files'] = list
    end
  end
  pre = $oats['execution']['handler_pre_test_list']
  if test_yaml and pre
    pre_tst = TestCase.new(pre)
    pre_tst.type = 4
    pre_tst.run
    TestList.current.pre_test = pre_tst
    TestList.current.variations.last.tests.pop
    $log.error pre_tst.errors.first[1] unless pre_tst.errors.empty?
  end
  variations = oats_data['execution']['environments']
  Oats.assert variations, "Missing entry for Oats.data execution.environments"
  # Don't let environment variations propogate down OatsData.history.inspect variations.inspect
  variations = nil if OatsData.history.find{|var|  var =~ /\/environments\/#{variations.first}/ }
  # Should also eliminate propogations of other variations if want to support other variations.
  cur_list = TestList.current
  cur_list.variations.last.end_time = Time.now.to_i if cur_list and cur_list.variations.last.end_time.nil?
  new_list = TestList.new(id,test_yaml)
  if variations.nil? or variations.empty?

    Driver.process_oats_data(oats_data)
    new_list.variations.last.end_time = Time.now.to_i
  else
    # Don't let variations files modify test file
    test_files = oats_data['execution']['test_files']
    #      # Don't let current variations propagate down
    variations.each do |variation|
      begin
        new_list.add_variation(variation)
        break if $oats_info['stop_oats']
        variation = variation.sub(/\.yml$/,'') # Get rid of extension, if provided.
        # Look for variation in environments
        environment_variation = Util.expand_path( variation+'.yml',
          File.join(oats_data['execution']['dir_tests'], 'environments') )
        raise(OatsError, "Can not locate variation [#{variation}]: #{environment_variation}" ) \
          unless File.exist?(environment_variation)
        new_oats_data = OatsData.load( environment_variation, oats_data)
        new_oats_data['env']['name'] = variation
        new_oats_data['_']['load_history'].last.in_result_dir = false if variations.length == 1
        new_oats_data['_']['environments'] << variation
        # If the same variation is found in user's directories, merge it
        user_var_dir = oats_data['execution']['dir_environments']
        if user_var_dir and File.directory?(user_var_dir)
          users_variation = Util.expand_path(variation+'.yml',user_var_dir)
          if File.exist?(users_variation) and # in case input was absolute
            not File.identical?(users_variation, environment_variation)
            new_oats_data = OatsData.load( users_variation, new_oats_data)
            new_oats_data['_']['load_history'].last.in_result_dir = false if variations.length == 1
            new_oats_data['_']['environments'] << users_variation
            # Keep only one name, the one in the user's variation in history
            #              new_oats_data['_']['load_history'][-2].omit = true
          end
        end
        new_oats_data['execution']['test_files'] = test_files
        Roptions.overlay($oats['_']['options']) if $oats['_']['options']
        Driver.process_oats_data(new_oats_data)
      rescue OatsError
        $log.error $!.to_s
        $log.error "Test variation is being skipped: #{variation} "
        next
      rescue
        $log.error TestCase.backtrace($!)
        $log.error "Test variation is being skipped: #{variation} "
        next
      ensure
        new_list.variations.last.end_time = Time.now.to_i
      end
    end
  end
  new_list.end_time = Time.now.to_i
  new_list.variations.last.end_time = new_list.end_time unless new_list.variations.last.end_time
  TestList.current = cur_list if cur_list
end

.run(args) ⇒ Object

Main method that starts oats execution in either agent or standalone mode. Parameters are command-line arguments Returns oats_info object containing execution results



44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
# File 'lib/oats/driver.rb', line 44

def Driver.run(args)
  unless ENV['HOSTNAME']
    if RUBY_PLATFORM =~ /(mswin|mingw)/
      ENV['HOSTNAME'] = ENV['COMPUTERNAME']
    else
      ENV['HOSTNAME'] = `hostname`.chomp
    end
  end

  Log4r::Logger.root.level = Log4r::DEBUG
  Log4r::StdoutOutputter.new('console', :level=>1,
    :formatter=>Log4r::PatternFormatter.new(:depth=>50,
      :pattern => "%-5l %d %M", :date_pattern=>"%y-%m-%d %H:%M:%S"))
  $log = Log4r::Logger.new('R')
  $log.add('console')
  options = CommandlineOptions.options(args)
  $oats_info = {}
  $oats_global = {}
  @@quiet = options['_:quiet'] # save quiet option from initial commandline options
  $log.remove('console') if @@quiet
  ENV['HOME'] = Util.expand_path(ENV['HOME']) if ENV['HOME'] # Normalize for cygwin
  if $oats_execution['agent']
    require 'oats/ragent'
    # $oats_execution['agent'] (used by framework) == Oats.global['agent'] (by YAMLs) === options
    $oats = OatsData.load(options['_:ini_file'])
    $oats['_']['options'] = options
    Roptions.override(options)
    # $oats would be reset and and regenerated by agents across jobs.

    options['execution:occ:agent_host'] = $oats['execution']['occ']['agent_host']
    options['execution:occ:agent_port'] = $oats['execution']['occ']['agent_port']
    options['execution:occ:agent_nickname'] = ($oats['execution']['occ']['agent_nickname'] ||  options['execution:occ:agent_host'].sub(/\..*/,''))
    $oats['execution']['occ']['agent_nickname'] = options['execution:occ:agent_nickname']

    if options['_:command']
      require 'oats/rclient'
      options['_:id'] = Time.now.to_i.to_s
      request = { :command => options['_:command'], :args => options['_:args'] }
      request[:id] = options['_:id'] if options['_:id']
      EventMachine::run { EventMachine::connect options['execution:occ:agent_host'],
        options['execution:occ:agent_port'].to_i, Rclient, options['execution:occ:agent_host'], request
      }
    else
      Ragent.start $oats['execution']['occ']
    end
  else
    Driver.start(nil,options)
  end
  $oats_info
end

.start(jid, options) ⇒ Object

Executes OATS Returns oats_info object containing execution results



97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
# File 'lib/oats/driver.rb', line 97

def Driver.start(jid,options)
  begin
    $oats_info = {}  # Holds Oats.context, to be transmitted to OCC
    $oats_global = {}  # Holds Oats.global for inter-test data, within an execution sequence
    Oats.context['start_time'] = Time.new.to_i
    if jid
      Oats.global['agent'] = $oats_execution['agent']
      Oats.context['jobid'] = jid
    else
      Oats.context['jobid'] = Oats.context['start_time'].to_s[2..-1]
    end
    ! options['execution:tail_logs_ip'] and ! OatsLock.set and  return false
    $log.remove('console') if options['_:quiet']
    $oats_execution['testlist_init'] and $oats_execution['testlist_init'].each_pair do |klas, args|
      klas.init
    end
    TestList.current = nil  # Initialize
    $oats = nil  # Holds Oats.data, the resolved oats.yml contents
    oats_data = OatsData.load(options['_:ini_file'])
    $oats = oats_data
    $oats['_']['options'] = options
    Roptions.override(options)
    Oats.result_archive_dir # Adjust results_ dir variables if running on agent mode
    oats_data = $oats
    Oselenium.reset if defined?(Oats::Oselenium) # Initialize class variables and kill running browsers, in case running in server host mode
    #      oats_data['execution']['test_files'] = test_files if test_files and ! test_files.empty?
    dir_res = oats_data['execution']['dir_results']
    stop_file = dir_res + '/stop_oats'
    oats_data['execution']['stop_file'] = stop_file
    if stop_file and File.exist?(stop_file)
      $oats_info['stop_oats'] = Time.new.to_i
      FileUtils.mv(stop_file, dir_res + '/stop_file_' + Oats.context['jobid'] )
    end
    Report.archive_results if not oats_data['execution']['tail_logs_ip']
    FileUtils.mkdir_p(dir_res)
    oats_data['execution']['log'] = oats_data['execution']['dir_results'] + '/oats.log'
    oats_log = oats_data['execution']['log']
    unless oats_data['execution']['tail_logs_ip']
      if oats_log
        dir_oats_log = File.dirname(oats_log)
        raise(OatsBadInput,"Can not locate directory of execution:log #{dir_oats_log}") unless File.directory?(dir_oats_log)
        oats_log = Util.expand_path(oats_log)
        # Ensure log_level valid
        level = Log4r::Log4rConfig::LogLevels.index($oats['execution']['log_level'])
        raise(OatsBadInput, "Unrecognized execution:log_level [#{$oats['execution']['log_level']}]") unless level
        Log4r::FileOutputter.new('logfile',
          :filename=>oats_log, :trunc=>false, :level=>level,
          :formatter=>Log4r::PatternFormatter.new(:depth=>50,
            :pattern => "%-5l %d %M", :date_pattern=>"%y-%m-%d %H:%M:%S"))
        $log.info "Redirecting output to logfile: " + oats_log
        $log.add('logfile')
      end
      $log.info "Started OATS execution [#{Oats.context['jobid']}] at #{Time.now}"
    end
    begin
      Oats.info "OATS_TESTS Directory: " + ENV['OATS_TESTS']
      oats_data['_']['environments'] = [] # Keep track of variations stack
      Driver.process_test_yaml(oats_data)
      Report.results($oats_info['test_files'])
      $oats_info['end_time'] = Time.now.to_i
      Report.oats_info_store
      $log.warn "*** Stopping per stop_oats request [#{$oats_info['stop_oats']}]" if $oats_info['stop_oats']
      $log.info "Finished OATS execution [#{Oats.context['jobid']}] at #{Time.at($oats_info['end_time'])} [#{$oats_info['end_time']}]" +
        (oats_log ? ": " + oats_log : '')
      Report.archive_results(true)
    ensure
      Oselenium.reset if defined?(Oats::Oselenium)
      unless oats_data['execution']['tail_logs_ip'] or oats_log.nil?
        Log4r::Outputter['logfile'].close
        $log.remove('logfile')
      end
      OatsLock.reset
      $log.add('console') if options['_:quiet'] and ! @@quiet
    end
  rescue Exception => e
    $log.debug "Top level Exception caught by test driver."
    $log.error $!
  end
end

.xl_sheet_tests(sheet, xl, ws, test_header) ⇒ Object



471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
# File 'lib/oats/driver.rb', line 471

def Driver.xl_sheet_tests(sheet,xl, ws, test_header)
  execute_index = test_index = nil
  msg = " in worksheet '#{ws}' of: #{xl}"
  sheet.collect do |row|
    if test_index
      #          Oats.assert row[test_index], "Missing value in column '#{test_header}'" + msg
      row[test_index] if row[execute_index] and (row[execute_index] == true or row[execute_index].downcase == 'true')
    else
      row.each_with_index do |col,idx|
        case col
        when test_header then test_index = idx
        when 'Execute' then execute_index = idx
        end
      end
      Oats.assert test_index, "Missing column '#{test_header}' "+ msg
      Oats.assert test_index, "Missing column 'Execute' "+ msg
    end
  end.compact
end