parse_output.rb 7.2 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 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 40 41 42 43 44 45 46 47 48 49
#============================================================
#  Author:   John Theofanopoulos
#  A simple parser.   Takes the output files generated during the build process and
# extracts information relating to the tests.
#
#  Notes:
#    To capture an output file under VS builds use the following:
#      devenv [build instructions]  > Output.txt & type Output.txt
#
#    To capture an output file under GCC/Linux builds use the following:
#      make | tee Output.txt
#
#    To use this parser use the following command
#    ruby parseOutput.rb [options] [file]
#        options:  -xml  : produce a JUnit compatible XML file
#        file      :  file to scan for results
#============================================================

class ParseOutput
  def initialize
    @test_flag = false
    @xml_out = false
    @array_list = false
    @total_tests = false
    @class_index = false
  end

  #   Set the flag to indicate if there will be an XML output file or not
  def set_xml_output
    @xml_out = true
  end

  #  if write our output to XML
  def write_xml_output
    output = File.open('report.xml', 'w')
    output << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
    @array_list.each do |item|
      output << item << "\n"
    end
    output << "</testsuite>\n"
  end

  #  This function will try and determine when the suite is changed.   This is
  # is the name that gets added to the classname parameter.
  def test_suite_verify(test_suite_name)
    return if @test_flag

    @test_flag = true
    # Split the path name
F
Fabian Zahn 已提交
50 51 52 53 54 55
    test_name = if @class_name == 1
                  test_suite_name.split('\\') # Windows
                else
                  test_suite_name.split('/')  # Unix based
                end

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
    # Remove the extension
    base_name = test_name[test_name.size - 1].split('.')
    @test_suite = 'test.' + base_name[0]
    printf "New Test: %s\n", @test_suite
  end

  # Test was flagged as having passed so format the output
  def test_passed(array)
    last_item = array.length - 1
    test_name = array[last_item - 1]
    test_suite_verify(array[@class_name])
    printf "%-40s PASS\n", test_name

    return unless @xml_out

    @array_list.push '     <testcase classname="' + @test_suite + '" name="' + test_name + '"/>'
  end

  # Test was flagged as having passed so format the output.
  # This is using the Unity fixture output and not the original Unity output.
  def test_passed_unity_fixture(array)
    test_suite = array[0].sub('TEST(', '')
    test_suite = test_suite.sub(',', '')
    test_name = array[1].sub(')', '')

    return unless @xml_out

    @array_list.push '     <testcase classname="' + test_suite + '" name="' + test_name + '"/>'
  end

F
Fabian Zahn 已提交
86
  # Test was flagged as being ignored so format the output
87 88 89
  def test_ignored(array)
    last_item = array.length - 1
    test_name = array[last_item - 2]
F
Fabian Zahn 已提交
90
    reason = array[last_item].chomp.lstrip
91 92 93 94 95 96 97 98 99 100 101 102 103
    test_suite_verify(array[@class_name])
    printf "%-40s IGNORED\n", test_name

    if test_name.start_with? 'TEST('
      array2 = test_name.split(' ')
      @test_suite = array2[0].sub('TEST(', '')
      @test_suite = @test_suite.sub(',', '')
      test_name = array2[1].sub(')', '')
    end

    return unless @xml_out

    @array_list.push '     <testcase classname="' + @test_suite + '" name="' + test_name + '">'
F
Fabian Zahn 已提交
104
    @array_list.push '            <skipped type="TEST IGNORED">' + reason + '</skipped>'
105 106 107 108 109 110 111
    @array_list.push '     </testcase>'
  end

  # Test was flagged as having failed  so format the line
  def test_failed(array)
    last_item = array.length - 1
    test_name = array[last_item - 2]
F
Fabian Zahn 已提交
112
    reason = array[last_item].chomp.lstrip + ' at line: ' + array[last_item - 3]
113 114 115 116 117 118 119 120 121 122 123 124 125
    test_suite_verify(array[@class_name])
    printf "%-40s FAILED\n", test_name

    if test_name.start_with? 'TEST('
      array2 = test_name.split(' ')
      @test_suite = array2[0].sub('TEST(', '')
      @test_suite = @test_suite.sub(',', '')
      test_name = array2[1].sub(')', '')
    end

    return unless @xml_out

    @array_list.push '     <testcase classname="' + @test_suite + '" name="' + test_name + '">'
F
Fabian Zahn 已提交
126
    @array_list.push '            <failure type="ASSERT FAILED">' + reason + '</failure>'
127 128 129
    @array_list.push '     </testcase>'
  end

F
Fabian Zahn 已提交
130
  # Figure out what OS we are running on. For now we are assuming if it's not Windows it must
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
  # be Unix based.
  def detect_os
    os = RUBY_PLATFORM.split('-')
    @class_name = if os.size == 2
                    if os[1] == 'mingw32'
                      1
                    else
                      0
                    end
                  else
                    0
                  end
  end

  # Main function used to parse the file that was captured.
  def process(name)
    @test_flag = false
    @array_list = []

    detect_os

    puts 'Parsing file: ' + name

    test_pass = 0
    test_fail = 0
    test_ignore = 0
    puts ''
    puts '=================== RESULTS ====================='
    puts ''
    File.open(name).each do |line|
      # Typical test lines look like this:
      # <path>/<test_file>.c:36:test_tc1000_opsys:FAIL: Expected 1 Was 0
      # <path>/<test_file>.c:112:test_tc5004_initCanChannel:IGNORE: Not Yet Implemented
      # <path>/<test_file>.c:115:test_tc5100_initCanVoidPtrs:PASS
      #
      # where path is different on Unix vs Windows devices (Windows leads with a drive letter)
      line_array = line.split(':')

      # If we were able to split the line then we can look to see if any of our target words
      # were found.  Case is important.
      if (line_array.size >= 4) || (line.start_with? 'TEST(')
        # Determine if this test passed
        if line.include? ':PASS'
          test_passed(line_array)
          test_pass += 1
F
Fabian Zahn 已提交
176
        elsif line.include? ':FAIL'
177 178 179 180 181
          test_failed(line_array)
          test_fail += 1
        elsif line.include? ':IGNORE:'
          test_ignored(line_array)
          test_ignore += 1
F
Fabian Zahn 已提交
182 183 184 185
        elsif line.include? ':IGNORE'
          line_array.push('No reason given')
          test_ignored(line_array)
          test_ignore += 1
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
        elsif line.start_with? 'TEST('
          if line.include? ' PASS'
            line_array = line.split(' ')
            test_passed_unity_fixture(line_array)
            test_pass += 1
          end
        # If none of the keywords are found there are no more tests for this suite so clear
        # the test flag
        else
          @test_flag = false
        end
      else
        @test_flag = false
      end
    end
    puts ''
    puts '=================== SUMMARY ====================='
    puts ''
    puts 'Tests Passed  : ' + test_pass.to_s
    puts 'Tests Failed  : ' + test_fail.to_s
    puts 'Tests Ignored : ' + test_ignore.to_s
    @total_tests = test_pass + test_fail + test_ignore

    return unless @xml_out

211
    heading = '<testsuite name="' + @test_suite.to_s + '" tests="' + @total_tests.to_s + '" failures="' + test_fail.to_s + '"' + ' skips="' + test_ignore.to_s + '">'
212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229
    @array_list.insert(0, heading)
    write_xml_output
  end
end

# If the command line has no values in, used a default value of Output.txt
parse_my_file = ParseOutput.new

if ARGV.size >= 1
  ARGV.each do |a|
    if a == '-xml'
      parse_my_file.set_xml_output
    else
      parse_my_file.process(a)
      break
    end
  end
end