class Actir::ParallelTests::CLI

Public Instance Methods

run(argv) click to toggle source
# File lib/actir/parallel_tests/cli.rb, line 10
def run(argv)
  $env = "qatest"
  options = parse_options!(argv)
  num_processes = Actir::ParallelTests.determine_number_of_processes(options[:count])
  $mode = Actir::ParallelTests.determine_run_mode(options[:mode])
  if options[:execute]
    execute_shell_command_in_parallel(options[:execute], num_processes, options)
  else
    run_tests_in_parallel(num_processes, options)
  end
end

Private Instance Methods

any_test_failed?(test_results) click to toggle source

exit with correct status code so rake parallel:test && echo 123 works

# File lib/actir/parallel_tests/cli.rb, line 165
def any_test_failed?(test_results)
  test_results.any? { |result| result[:exit_status] != 0 }
end
detail_report() click to toggle source

生成详细报告

# File lib/actir/parallel_tests/cli.rb, line 300
def detail_report
  @report_path = File.join($project_path, 'report')
  Dir::mkdir(@report_path) if not File.directory?(@report_path)
  time = Time.now.strftime('%Y%m%d_%H%M%S')
  # file_path = File.join(@report_path, "REPORT_#{time}.html")
  file_path = File.join(@report_path, "index.html")
  file = File.new(file_path,"w")
  report = HtmlReport.new(file)
end
detailed_duration(seconds) click to toggle source
# File lib/actir/parallel_tests/cli.rb, line 272
def detailed_duration(seconds)
  parts = [ seconds / 3600, seconds % 3600 / 60, seconds % 60 ].drop_while(&:zero?)
  return if parts.size < 2
  parts = parts.map { |i| "%02d" % i }.join(':').sub(/^0/, '')
  " (#{parts})"
end
division_str() click to toggle source

add by Hub division for Actir report

# File lib/actir/parallel_tests/cli.rb, line 295
def division_str
  "---------------------------------------------------------------------------------------------\n"
end
execute_in_parallel(items, num_processes, options) { |item| ... } click to toggle source
# File lib/actir/parallel_tests/cli.rb, line 24
def execute_in_parallel(items, num_processes, options)
  Tempfile.open 'parallel_tests-lock' do |lock|
    return Parallel.map(items, :in_threads => num_processes) do |item|
      result = yield(item)
      report_output(result, lock) if options[:serialize_stdout]
      result
    end
  end
end
execute_shell_command_in_parallel(command, num_processes, options) click to toggle source
# File lib/actir/parallel_tests/cli.rb, line 251
def execute_shell_command_in_parallel(command, num_processes, options)
  runs = (0...num_processes).to_a
  results = if options[:non_parallel]
    runs.map do |i|
      @runner.execute_command(command, i, num_processes, options)
    end
  else
    execute_in_parallel(runs, num_processes, options) do |i|
      @runner.execute_command(command, i, num_processes, options)
    end
  end.flatten

  abort if results.any? { |r| r[:exit_status] != 0 }
end
failed_testcase_str() click to toggle source
# File lib/actir/parallel_tests/cli.rb, line 310
def failed_testcase_str
  failed_case_array = Actir::ParallelTests::Test::Result.get_failed_testcase
  failed_case_str = " Failed testcase:\n"
  failed_case_array.each_with_index do |failed_case, index|
    index_num = (index + 1).to_s
    failed_case_str += " " + index_num + " : " + failed_case + "\n" 
  end
  failed_case_str
end
final_fail_message() click to toggle source
# File lib/actir/parallel_tests/cli.rb, line 279
def final_fail_message
  fail_message = "#{@runner.name}s Failed\n\n"
  fail_message = "\e[31m#{fail_message}\e[0m" if use_colors?
  "\n" + pre_str + fail_message + failed_testcase_str + "\n"
end
get_project_path(argv) click to toggle source

根据传入的测试文件/文件夹的路径,获取测试工程的路径

# File lib/actir/parallel_tests/cli.rb, line 220
def get_project_path(argv)
  testcode_path = ""
  (argv || []).map do |file_or_folder|
    # 如果传入的路径是绝对路径,那么直接将传入的路径作为test_path
    if file_or_folder =~ /^\//
      test_path = file_or_folder
    # 如果传入的路径是相对路径,那么将当前路径与传入的相对路径做拼接生成test_path
    else
      test_path = File.join(Dir.pwd , file_or_folder)
    end
    #获取testcode文件夹的path
    if test_path =~ /(\/.*\/testcode)/
      testcode_path = $1
      break
    end
  end
  #根据testcode path 拿到project的path
  #要求testcode必须是project的下一级/testcode必须是config的平级
  $project_path = File.join(testcode_path, "../")
end
load_runner(type) click to toggle source
# File lib/actir/parallel_tests/cli.rb, line 241
def load_runner(type)
  require "actir/parallel_tests/#{type}/runner"
  require "actir/parallel_tests/#{type}/rerunner"
  require "actir/parallel_tests/#{type}/logger"
  require "actir/parallel_tests/#{type}/result"
  runner_classname = type.split("_").map(&:capitalize).join.sub("Rspec", "RSpec")
  klass_name = "Actir::ParallelTests::#{runner_classname}::Runner"
  klass_name.split('::').inject(Object) { |x, y| x.const_get(y) }
end
parse_options!(argv) click to toggle source
# File lib/actir/parallel_tests/cli.rb, line 169
      def parse_options!(argv)
        options = {}
        @runner = load_runner("test")
        OptionParser.new do |opts|
          opts.banner = <<-BANNER.gsub(/^          /, '')
            Run all testcase in parallel
            Usage: actir [switches] [--] [files & folders] [-] [testcase_name]
            Options are:
          BANNER
          opts.on("-n [TESTCASE]", String, "Run this testcase") { |casename| options[:testcase] = casename }
          opts.on("-p [PROCESSES]", Integer, "How many processes to use, default: 1") { |p| options[:count] = p }
          # opts.on("--group-by [TYPE]", <<-TEXT.gsub(/^          /, '')
          # group tests by:
          #             found - order of finding files
          #             filesize - by size of the file
          #             default - filesize
          #   TEXT
          #   ) { |type| options[:group_by] = type.to_sym }
          opts.on("-e [online][qatest]", String, "set environment to run testcase, default: online") { |env| env = "online" if ((env != "qatest" && env != "online")||(env == nil)); $env = env}
          opts.on("-r [TIMES]", "--rerun [TIMES]", Integer, "rerun times for failure&error testcase, default: 0") { |n| options[:rerun] = n }
          opts.on("-u", "--update", "Update Baifubao's cookies") { options[:update] = true }
          opts.on("--verbose", "Print more output") { options[:verbose] = true }
          opts.on("--log", "record exec result to logfile") { options[:log] = true}
          opts.on("--report", "make a report to show the test result") { options[:report] = true}
          # opts.on("--remote", "run testcase in remote environment") { options[:mode] = :remote }
          # opts.on("--local", "run testcase in local environment") { options[:mode] = :local }
                    # opts.on("-e", "--exec [COMMAND]", "execute this code parallel") { |path| options[:execute] = path }
          # opts.on("--serialize-stdout", "Serialize stdout output, nothing will be written until everything is done") { options[:serialize_stdout] = true }
          # opts.on("--combine-stderr", "Combine stderr into stdout, useful in conjunction with --serialize-stdout") { options[:combine_stderr] = true }
          # opts.on("--non-parallel", "execute same commands but do not in parallel, needs --exec") { options[:non_parallel] = true }
          # opts.on("--nice", "execute test commands with low priority") { options[:nice] = true }
          opts.on("-h", "--help", "Show this.") { puts opts; exit }
          opts.on("-v", "--version", "Show version.") { puts Actir::VERSION; exit }
        end.parse!(argv)

        if options[:count] == 0
          options.delete(:count)
          options[:non_parallel] = true
        end

        abort "Pass files or folders to run" if argv.empty? && !options[:execute]

        #如果argv为空,则默认执行所有测试文件
        #遍历testcode下所有文件,组成字符串
        options[:files] = argv
        get_project_path(argv)

        options
      end
pre_str() click to toggle source
# File lib/actir/parallel_tests/cli.rb, line 289
def pre_str
  " Actir : "
end
re_run_tests(result, process_number, num_processes, options, address, re_run_times) click to toggle source
# File lib/actir/parallel_tests/cli.rb, line 104
def re_run_tests(result, process_number, num_processes, options, address, re_run_times)
  Actir::ParallelTests::Test::Rerunner.re_run_tests(result, process_number, num_processes, options, address, re_run_times)
end
report_address_of_env(address) click to toggle source

add by Hub show test env address

# File lib/actir/parallel_tests/cli.rb, line 138
def report_address_of_env(address)
  if $mode == :remote
    node_name = Actir::Config.get("config.test_mode.docker.name")
    address.each_with_index do |ip, i|
      puts " " + $env + node_name + (i+1).to_s + " : " + ip
    end
  else
    puts " " + "local"
  end
  puts division_str
end
report_number_of_tests(groups) click to toggle source
# File lib/actir/parallel_tests/cli.rb, line 127
def report_number_of_tests(groups)
  name = @runner.test_file_name
  num_processes = groups.size
  num_tests = groups.map(&:size).inject(:+)
  puts division_str
  puts pre_str + "#{num_processes} processes for #{num_tests} #{name}s, ~ #{num_tests / groups.size} #{name}s per process"
  #puts division_str
end
report_output(result, lock) click to toggle source
# File lib/actir/parallel_tests/cli.rb, line 108
def report_output(result, lock)
  lock.flock File::LOCK_EX
  $stdout.puts result[:stdout]
  $stdout.flush
ensure
  lock.flock File::LOCK_UN
end
report_results(test_results, options) click to toggle source
# File lib/actir/parallel_tests/cli.rb, line 116
def report_results(test_results, options)
  results = @runner.find_results(test_results.map { |result| result[:stdout] }*"")
  puts division_str
  puts pre_str + @runner.summarize_results(results)

  #add by shanmao
  #生成详细报告
  detail_report if (options[:report] == true)
  #puts pre_str + any_test_failed?(test_results).to_s
end
report_time_taken() { || ... } click to toggle source
# File lib/actir/parallel_tests/cli.rb, line 266
def report_time_taken
  seconds = Actir::ParallelTests.delta { yield }.to_i
  puts "\n" + pre_str + "Cost #{seconds} seconds#{detailed_duration(seconds)}\n"
  puts division_str
end
run_tests(group, process_number, num_processes, options, address) click to toggle source
# File lib/actir/parallel_tests/cli.rb, line 95
def run_tests(group, process_number, num_processes, options, address)
  if group.empty?
    {:stdout => '', :exit_status => 0}
  else
    #puts pre_str + "ready to exec #{group}"
    @runner.run_tests(group, process_number, num_processes, options, address)
  end
end
run_tests_in_parallel(num_processes, options) click to toggle source
# File lib/actir/parallel_tests/cli.rb, line 34
def run_tests_in_parallel(num_processes, options)
  test_results = nil
  
  report_time_taken do
    groups = @runner.tests_in_groups(options[:files], num_processes, options)

    # @modify by Hub
    # @Date : 2015.3.9
    # 远程执行模式下获取服务器IP和端口号
    address = []
    # if $mode == :remote
    #   address = Actir::Remote.get_remote_address(num_processes)
    #   num_processes = address.size
    # end

    #更新百度支付-百付宝的cookies
    #-u为强制更新,若没有加强制更新命令,则每天自动更新一次
    #qatest模式则不需要更新baiducookies,qatest环境的百度支付已mock
    if $env == "online"
      Actir::CookiesBaidu.init
      if options[:update] || !(Actir::Config.is_same_day?("cookies", Actir::CookiesBaidu.directory))
        begin
          Actir::CookiesBaidu.update_cookies("card1")
        rescue Exception => e
          #若更新baidu_cookies失败,则关闭浏览器,打印错误信息,并中断测试执行
          Actir::CookiesBaidu.clear_after_failure
          puts "Exception: #{e.message} in"
          puts e.backtrace
          abort "update baidu cookies failed!!!"
        end
      end
    end

    #计算重跑次数
    re_run_times = Actir::ParallelTests.determine_times_of_rerun(options[:rerun])
    #报用例数
    report_number_of_tests(groups)
    #报执行环境
    # report_address_of_env(address)
    #并发执行不同group中的测试用例
    test_results = execute_in_parallel(groups, groups.size, options) do |group|
      p_num = groups.index(group)
      #执行用例脚本
      result = run_tests(group, p_num, num_processes, options, address[p_num])
      #从结果中取出失败用例重跑
      if ( result[:exit_status] != 0 ) && ( re_run_times > 0 )
        result = re_run_tests(result, p_num, num_processes, options, address[p_num], re_run_times)
      end
      result
    end

    #顺序输出并发执行过程
    show_process_serialize(num_processes, options)

    #输出最终的执行结果
    report_results(test_results, options)
  end

  abort final_fail_message if any_test_failed?(test_results)
end
show_process_serialize(num_processes, options) click to toggle source

add by Hub show result of every process exec testcases this func will last for a while due to the big logfile

# File lib/actir/parallel_tests/cli.rb, line 153
def show_process_serialize(num_processes, options)
  if options[:log]
    puts "\n" + division_str + pre_str + "SHOW_PROCESS_LOG--START\n" + division_str
    for i in 0..(num_processes-1)
      Actir::ParallelTests::Test::Logger.show_log(i)
      puts division_str
    end
    puts division_str + pre_str + "SHOW_PROCESS_LOG--END\n" + division_str
  end
end
use_colors?() click to toggle source
# File lib/actir/parallel_tests/cli.rb, line 285
def use_colors?
  $stdout.tty?
end