module Actir
  module ParallelTests
    module Test
      class Result

        class << self
          
          #
          # 通过结果判断测试套的详细信息
          #
          # 将测试套和测试用例的详细信息写入全局变量$testsuites中
          #
          def get_testsuite_detail(test_result, mode = :runner)
            $testsuites = [] unless $testsuites
            if mode == :runner
              get_run_test_info(test_result)
              #如果有用例失败,则记录详细信息,否则不需要
              if any_test_failed?(test_result)
                record_detail(test_result)
              end
            else
              record_detail(test_result)
            end
          end

          def record_detail(test_result)
            failure_detail_hash = get_testfailed_info(test_result)
              $testsuites.each do |testsuite|
                testcases = testsuite[:testcases] 
                testcases.each do |testcase|
                  #标识用例是否执行失败
                  fail_flag = 0
                  failure_detail_hash.each do |testcase_failure, detail|
                    if testcase_failure == testcase[:testcase_name]
                      testcase[:success] = false
                      testcase[:detail] = detail 
                      fail_flag = 1
                      #从hash表中移除 
                      failure_detail_hash.delete(testcase_failure)
                    end
                  end
                  if fail_flag == 0
                    testcase[:success] = true
                    testcase[:detail] = nil
                  end
                end
              end
          end

          def get_run_test_info(test_result)
            output = test_result[:stdout]
            # output.scan(/^(\[suite start\])([^\.]*)(\[suite end\])$/).each do |suite|
            output.scan(file_suite_case_reg).each do |suite|
              filename = suite[0]
              testsuite = suite[1].scan(/^(suitname:\s*)([\d\w]*)/)[0][1]
              testsuite_name = get_unique_testname(filename, testsuite)
              cases = suite[1].scan(/^(testcase:\s*)([\d\w]*)/).inject([]) do |cases,testcase|
                testcase_name = get_unique_testname(filename, testcase[1])
                cases << {:testcase_name => testcase_name, :success => true, :detail => nil}
              end
              # 如果testsuites中已存在此用例的信息,说明这个用例执行了rerun,就不再次添加了
              is_case_exist = $testsuites.inject(false) do |is_case_exist, testsuite|
                if testsuite.has_value?(testsuite_name)
                  is_case_exist = true
                  break
                end
                is_case_exist
              end
              if(is_case_exist == false)
                testsuite = {:testsuite_name => testsuite_name, :testcases =>cases}
                $testsuites << testsuite
              end
            end
          end

          #
          # 通过结果判断失败用例,获取失败用例的详细信息
          #
          # 将测试套和测试用例的详细信息写入全局变量$testsuites中
          #
          def get_testfailed_info(test_result)
            result_array = test_result[:stdout].split("\n")
            failure_detail_hash = {}
            testfile = ""
            testcase = "" 
            detail = ""
            testcase_name = ""
            record_detail_switch = 0

            result_array.each do |result|
              record_detail_switch = 0 if result =~ failure_or_error_switch_off
              #遇到错误信息,开启记录错误信息开关
              record_detail_switch = 1 if result =~failure_or_error_switch_on

              # 记录报错信息
              if record_detail_switch == 1 
                detail += result + "\n"
              end

              # 记录报错用例名称
              if (result =~ failure_tests_name_reg) || (result =~ error_tests_name_reg) || (result =~ fail_tests_name_reg_extra)
                testcase = $1                
              end

              # 记录报错用例文件名称
              if (result =~ failure_tests_file_reg) || (result =~ failure_tests_file_reg_extra)
                #范例:"testcode/test_tt/test_hehe.rb:8:in `xxxx'"
                testfile = $1
              end

              # 合并用例名称和文件名称
              testcase_name = get_unique_testname(testfile, testcase)

              if testcase_name != "" && detail != "" && record_detail_switch == 0
                failure_detail_hash[testcase_name] = detail
                testcase = ""
                testfile = ""
                detail = ""
                testcase_name = ""
              end
            end

            failure_detail_hash
          end

          # 从执行结果中获取文件/测试套/测试用例的名称
          def file_suite_case_reg
            /^Loaded\ssuite\s(.*)\nStarted\n\[suite start\]([^\.]*)\[suite end\]$/
          end

          #判断是否有用例失败
          def any_test_failed?(result)
            result[:exit_status] != 0
          end

          #获取错误用例名的正则
          def error_tests_name_reg
            /^Error:\s(test.+)\(.+\):/
          end

          #获取失败用例名的正则
          def failure_tests_name_reg
            /^Failure:\s(test.+)\(.+\)/
          end

          # 获取失败用例名的正则[补充场景]
          def fail_tests_name_reg_extra
            /^(test.+)\(.+\)/
          end

          # 获取失败用例文件名的正则
          def failure_tests_file_reg
            /(.+\/test.+rb):\d+:in\s`.+'/
          end

          # 获取失败用例文件名的正则[补充场景]
          def failure_tests_file_reg_extra
            /^(test.+rb):\d+:in\s`.+'/
          end

          # 测试套信息开头正则
          def test_info_swtich_on
            /^\[suite start\]/
          end

          # 测试套信息截止正则
          def test_info_swtich_off
            /^\[suite end\]/
          end

          # 测试用例执行报错信息开头正则
          def failure_or_error_switch_on
            /^Failure:|^Error:/
          end

          # 测试用例执行报错信息截止正则
          def failure_or_error_switch_off
            /^===============================================================================$/
          end

          # 因为测试用例名称/测试套名称有可能重复,所以采用 测试文件名称:测试用例名称 的方式作为测试用例名称的唯一标识
          # 也可用于测试套名称组合
          def get_unique_testname(testfile, test)
            if (test != "" || test != nil) && (testfile != "" || testfile != nil)
              # 判断测试文件名称是否包含.rb后缀,如果没有则加上
              unless testfile =~ /.*\.rb$/
                testfile += ".rb"
              end
              return testfile + ":" + test 
            else
              return ""
            end
          end

          def get_testfile_from_unique(unique_testname)
            unique_testname =~ /(.*)\:(.*)/
            $1       
          end

          def get_testcase_from_unique(unique_testname)
            unique_testname =~ /(.*)\:(.*)/
            $2        
          end

        end

      end
    end
  end
end