• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#============================================================
2#  Author:   John Theofanopoulos
3#  A simple parser.   Takes the output files generated during the build process and
4# extracts information relating to the tests.
5#
6#  Notes:
7#    To capture an output file under VS builds use the following:
8#      devenv [build instructions]  > Output.txt & type Output.txt
9#
10#    To capture an output file under GCC/Linux builds use the following:
11#      make | tee Output.txt
12#
13#    To use this parser use the following command
14#    ruby parseOutput.rb [options] [file]
15#        options:  -xml  : produce a JUnit compatible XML file
16#        file      :  file to scan for results
17#============================================================
18
19class ParseOutput
20  def initialize
21    @test_flag = false
22    @xml_out = false
23    @array_list = false
24    @total_tests = false
25    @class_index = false
26  end
27
28  #   Set the flag to indicate if there will be an XML output file or not
29  def set_xml_output
30    @xml_out = true
31  end
32
33  #  if write our output to XML
34  def write_xml_output
35    output = File.open('report.xml', 'w')
36    output << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
37    @array_list.each do |item|
38      output << item << "\n"
39    end
40    output << "</testsuite>\n"
41  end
42
43  #  This function will try and determine when the suite is changed.   This is
44  # is the name that gets added to the classname parameter.
45  def test_suite_verify(test_suite_name)
46    return if @test_flag
47
48    @test_flag = true
49    # Split the path name
50    test_name = test_suite_name.split('/')
51    # Remove the extension
52    base_name = test_name[test_name.size - 1].split('.')
53    @test_suite = 'test.' + base_name[0]
54    printf "New Test: %s\n", @test_suite
55  end
56
57  # Test was flagged as having passed so format the output
58  def test_passed(array)
59    last_item = array.length - 1
60    test_name = array[last_item - 1]
61    test_suite_verify(array[@class_name])
62    printf "%-40s PASS\n", test_name
63
64    return unless @xml_out
65
66    @array_list.push '     <testcase classname="' + @test_suite + '" name="' + test_name + '"/>'
67  end
68
69  # Test was flagged as having passed so format the output.
70  # This is using the Unity fixture output and not the original Unity output.
71  def test_passed_unity_fixture(array)
72    test_suite = array[0].sub('TEST(', '')
73    test_suite = test_suite.sub(',', '')
74    test_name = array[1].sub(')', '')
75
76    return unless @xml_out
77
78    @array_list.push '     <testcase classname="' + test_suite + '" name="' + test_name + '"/>'
79  end
80
81  # Test was flagged as being ingored so format the output
82  def test_ignored(array)
83    last_item = array.length - 1
84    test_name = array[last_item - 2]
85    reason = array[last_item].chomp
86    test_suite_verify(array[@class_name])
87    printf "%-40s IGNORED\n", test_name
88
89    if test_name.start_with? 'TEST('
90      array2 = test_name.split(' ')
91      @test_suite = array2[0].sub('TEST(', '')
92      @test_suite = @test_suite.sub(',', '')
93      test_name = array2[1].sub(')', '')
94    end
95
96    return unless @xml_out
97
98    @array_list.push '     <testcase classname="' + @test_suite + '" name="' + test_name + '">'
99    @array_list.push '            <skipped type="TEST IGNORED"> ' + reason + ' </skipped>'
100    @array_list.push '     </testcase>'
101  end
102
103  # Test was flagged as having failed  so format the line
104  def test_failed(array)
105    last_item = array.length - 1
106    test_name = array[last_item - 2]
107    reason = array[last_item].chomp + ' at line: ' + array[last_item - 3]
108    test_suite_verify(array[@class_name])
109    printf "%-40s FAILED\n", test_name
110
111    if test_name.start_with? 'TEST('
112      array2 = test_name.split(' ')
113      @test_suite = array2[0].sub('TEST(', '')
114      @test_suite = @test_suite.sub(',', '')
115      test_name = array2[1].sub(')', '')
116    end
117
118    return unless @xml_out
119
120    @array_list.push '     <testcase classname="' + @test_suite + '" name="' + test_name + '">'
121    @array_list.push '            <failure type="ASSERT FAILED"> ' + reason + ' </failure>'
122    @array_list.push '     </testcase>'
123  end
124
125  # Figure out what OS we are running on.   For now we are assuming if it's not Windows it must
126  # be Unix based.
127  def detect_os
128    os = RUBY_PLATFORM.split('-')
129    @class_name = if os.size == 2
130                    if os[1] == 'mingw32'
131                      1
132                    else
133                      0
134                    end
135                  else
136                    0
137                  end
138  end
139
140  # Main function used to parse the file that was captured.
141  def process(name)
142    @test_flag = false
143    @array_list = []
144
145    detect_os
146
147    puts 'Parsing file: ' + name
148
149    test_pass = 0
150    test_fail = 0
151    test_ignore = 0
152    puts ''
153    puts '=================== RESULTS ====================='
154    puts ''
155    File.open(name).each do |line|
156      # Typical test lines look like this:
157      # <path>/<test_file>.c:36:test_tc1000_opsys:FAIL: Expected 1 Was 0
158      # <path>/<test_file>.c:112:test_tc5004_initCanChannel:IGNORE: Not Yet Implemented
159      # <path>/<test_file>.c:115:test_tc5100_initCanVoidPtrs:PASS
160      #
161      # where path is different on Unix vs Windows devices (Windows leads with a drive letter)
162      line_array = line.split(':')
163
164      # If we were able to split the line then we can look to see if any of our target words
165      # were found.  Case is important.
166      if (line_array.size >= 4) || (line.start_with? 'TEST(')
167        # Determine if this test passed
168        if line.include? ':PASS'
169          test_passed(line_array)
170          test_pass += 1
171        elsif line.include? ':FAIL:'
172          test_failed(line_array)
173          test_fail += 1
174        elsif line.include? ':IGNORE:'
175          test_ignored(line_array)
176          test_ignore += 1
177        elsif line.start_with? 'TEST('
178          if line.include? ' PASS'
179            line_array = line.split(' ')
180            test_passed_unity_fixture(line_array)
181            test_pass += 1
182          end
183        # If none of the keywords are found there are no more tests for this suite so clear
184        # the test flag
185        else
186          @test_flag = false
187        end
188      else
189        @test_flag = false
190      end
191    end
192    puts ''
193    puts '=================== SUMMARY ====================='
194    puts ''
195    puts 'Tests Passed  : ' + test_pass.to_s
196    puts 'Tests Failed  : ' + test_fail.to_s
197    puts 'Tests Ignored : ' + test_ignore.to_s
198    @total_tests = test_pass + test_fail + test_ignore
199
200    return unless @xml_out
201
202    heading = '<testsuite tests="' + @total_tests.to_s + '" failures="' + test_fail.to_s + '"' + ' skips="' + test_ignore.to_s + '">'
203    @array_list.insert(0, heading)
204    write_xml_output
205  end
206end
207
208# If the command line has no values in, used a default value of Output.txt
209parse_my_file = ParseOutput.new
210
211if ARGV.size >= 1
212  ARGV.each do |a|
213    if a == '-xml'
214      parse_my_file.set_xml_output
215    else
216      parse_my_file.process(a)
217      break
218    end
219  end
220end
221