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
#============================================================
#  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
    @xml_out = false
    @array_list = false
23 24
    @class_name = 0
    @test_suite = nil
25 26 27 28 29 30 31 32
    @total_tests = 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

33
  #  If write our output to XML
34 35 36 37 38 39 40 41 42
  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

43
  # This function will try and determine when the suite is changed. This is
44 45 46 47
  # is the name that gets added to the classname parameter.
  def test_suite_verify(test_suite_name)

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

54 55 56 57 58 59 60 61 62
    # Remove the extension and extract the base_name
    base_name = test_name[test_name.size - 1].split('.')[0]

    # Is this a new test suite?
    if base_name.to_s != @test_suite.to_s
      @test_suite = base_name
      printf "New Test: %s\n", @test_suite
    end

63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82
  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(')', '')
83 84
    test_suite_verify(array[@class_name])
    printf "%-40s PASS\n", test_name
85 86 87 88 89
    return unless @xml_out

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

F
Fabian Zahn 已提交
90
  # Test was flagged as being ignored so format the output
91 92 93
  def test_ignored(array)
    last_item = array.length - 1
    test_name = array[last_item - 2]
F
Fabian Zahn 已提交
94
    reason = array[last_item].chomp.lstrip
95
    class_name = array[@class_name]
96 97 98

    if test_name.start_with? 'TEST('
      array2 = test_name.split(' ')
99 100 101 102 103

      test_suite = array2[0].sub('TEST(', '')
      test_suite = test_suite.sub(',', '')
      class_name = test_suite

104 105 106
      test_name = array2[1].sub(')', '')
    end

107 108 109
    test_suite_verify(class_name)
    printf "%-40s IGNORED\n", test_name

110 111 112
    return unless @xml_out

    @array_list.push '     <testcase classname="' + @test_suite + '" name="' + test_name + '">'
F
Fabian Zahn 已提交
113
    @array_list.push '            <skipped type="TEST IGNORED">' + reason + '</skipped>'
114 115 116 117 118 119 120
    @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 已提交
121
    reason = array[last_item].chomp.lstrip + ' at line: ' + array[last_item - 3]
122
    class_name = array[@class_name]
123 124 125

    if test_name.start_with? 'TEST('
      array2 = test_name.split(' ')
126 127 128 129 130

      test_suite = array2[0].sub('TEST(', '')
      test_suite = test_suite.sub(',', '')
      class_name = test_suite

131 132 133
      test_name = array2[1].sub(')', '')
    end

134 135 136
    test_suite_verify(class_name)
    printf "%-40s FAILED\n", test_name

137 138 139
    return unless @xml_out

    @array_list.push '     <testcase classname="' + @test_suite + '" name="' + test_name + '">'
F
Fabian Zahn 已提交
140
    @array_list.push '            <failure type="ASSERT FAILED">' + reason + '</failure>'
141 142 143
    @array_list.push '     </testcase>'
  end

F
Fabian Zahn 已提交
144
  # Figure out what OS we are running on. For now we are assuming if it's not Windows it must
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 176 177 178 179 180 181 182 183 184 185 186 187 188
  # 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)
    @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 已提交
189
        elsif line.include? ':FAIL'
190 191 192 193 194
          test_failed(line_array)
          test_fail += 1
        elsif line.include? ':IGNORE:'
          test_ignored(line_array)
          test_ignore += 1
F
Fabian Zahn 已提交
195 196 197 198
        elsif line.include? ':IGNORE'
          line_array.push('No reason given')
          test_ignored(line_array)
          test_ignore += 1
199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217
        elsif line.start_with? 'TEST('
          if line.include? ' PASS'
            line_array = line.split(' ')
            test_passed_unity_fixture(line_array)
            test_pass += 1
          end
        end
      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

218
    heading = '<testsuite name="Unity" tests="' + @total_tests.to_s + '" failures="' + test_fail.to_s + '"' + ' skips="' + test_ignore.to_s + '">'
219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236
    @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