summaryrefslogtreecommitdiff
path: root/FreeRTOS-Plus/Test/CMock/vendor/unity/auto/parse_output.rb
blob: d72c6e8b2ee1218a866102d697e948c2b907a746 (plain)
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
50
51
52
53
54
55
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
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
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
176
177
178
179
180
181
182
183
184
185
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
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
#============================================================
#  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 Linux builds use the following:
#      make | tee Output.txt
#
#    This script can handle the following output formats:
#    - normal output (raw unity)
#    - fixture output (unity_fixture.h/.c)
#    - fixture output with verbose flag set ("-v")
#
#    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
#============================================================

# Parser class for handling the input file
class ParseOutput
  def initialize
    # internal data
    @class_name_idx = 0
    @path_delim = nil

    # xml output related
    @xml_out = false
    @array_list = false

    # current suite name and statistics
    @test_suite = nil
    @total_tests = 0
    @test_passed = 0
    @test_failed = 0
    @test_ignored = 0
  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
  end

  # Pushes the suite info as xml to the array list, which will be written later
  def push_xml_output_suite_info
    # Insert opening tag at front
    heading = '<testsuite name="Unity" tests="' + @total_tests.to_s + '" failures="' + @test_failed.to_s + '"' + ' skips="' + @test_ignored.to_s + '">'
    @array_list.insert(0, heading)
    # Push back the closing tag
    @array_list.push '</testsuite>'
  end

  # Pushes xml output data to the array list, which will be written later
  def push_xml_output_passed(test_name)
    @array_list.push '    <testcase classname="' + @test_suite + '" name="' + test_name + '"/>'
  end

  # Pushes xml output data to the array list, which will be written later
  def push_xml_output_failed(test_name, reason)
    @array_list.push '    <testcase classname="' + @test_suite + '" name="' + test_name + '">'
    @array_list.push '        <failure type="ASSERT FAILED">' + reason + '</failure>'
    @array_list.push '    </testcase>'
  end

  # Pushes xml output data to the array list, which will be written later
  def push_xml_output_ignored(test_name, reason)
    @array_list.push '    <testcase classname="' + @test_suite + '" name="' + test_name + '">'
    @array_list.push '        <skipped type="TEST IGNORED">' + reason + '</skipped>'
    @array_list.push '    </testcase>'
  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)
    # Split the path name
    test_name = test_suite_name.split(@path_delim)

    # Remove the extension and extract the base_name
    base_name = test_name[test_name.size - 1].split('.')[0]

    # Return if the test suite hasn't changed
    return unless base_name.to_s != @test_suite.to_s

    @test_suite = base_name
    printf "New Test: %s\n", @test_suite
  end

  # Prepares the line for verbose fixture output ("-v")
  def prepare_fixture_line(line)
    line = line.sub('IGNORE_TEST(', '')
    line = line.sub('TEST(', '')
    line = line.sub(')', ',')
    line = line.chomp
    array = line.split(',')
    array.map { |x| x.to_s.lstrip.chomp }
  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)
    class_name = array[0]
    test_name  = array[1]
    test_suite_verify(class_name)
    printf "%-40s PASS\n", test_name

    push_xml_output_passed(test_name) if @xml_out
  end

  # Test was flagged as having failed so format the output.
  # This is using the Unity fixture output and not the original Unity output.
  def test_failed_unity_fixture(array)
    class_name = array[0]
    test_name  = array[1]
    test_suite_verify(class_name)
    reason_array = array[2].split(':')
    reason = reason_array[-1].lstrip.chomp + ' at line: ' + reason_array[-4]

    printf "%-40s FAILED\n", test_name

    push_xml_output_failed(test_name, reason) if @xml_out
  end

  # Test was flagged as being ignored so format the output.
  # This is using the Unity fixture output and not the original Unity output.
  def test_ignored_unity_fixture(array)
    class_name = array[0]
    test_name  = array[1]
    reason = 'No reason given'
    if array.size > 2
      reason_array = array[2].split(':')
      tmp_reason = reason_array[-1].lstrip.chomp
      reason = tmp_reason == 'IGNORE' ? 'No reason given' : tmp_reason
    end
    test_suite_verify(class_name)
    printf "%-40s IGNORED\n", test_name

    push_xml_output_ignored(test_name, reason) if @xml_out
  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_idx])
    printf "%-40s PASS\n", test_name

    return unless @xml_out

    push_xml_output_passed(test_name) if @xml_out
  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]
    reason = array[last_item].chomp.lstrip + ' at line: ' + array[last_item - 3]
    class_name = array[@class_name_idx]

    if test_name.start_with? 'TEST('
      array2 = test_name.split(' ')

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

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

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

    push_xml_output_failed(test_name, reason) if @xml_out
  end

  # Test was flagged as being ignored so format the output
  def test_ignored(array)
    last_item = array.length - 1
    test_name = array[last_item - 2]
    reason = array[last_item].chomp.lstrip
    class_name = array[@class_name_idx]

    if test_name.start_with? 'TEST('
      array2 = test_name.split(' ')

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

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

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

    push_xml_output_ignored(test_name, reason) if @xml_out
  end

  # Adjusts the os specific members according to the current path style
  # (Windows or Unix based)
  def detect_os_specifics(line)
    if line.include? '\\'
      # Windows X:\Y\Z
      @class_name_idx = 1
      @path_delim = '\\'
    else
      # Unix Based /X/Y/Z
      @class_name_idx = 0
      @path_delim = '/'
    end
  end

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

    puts 'Parsing file: ' + file_name

    @test_passed = 0
    @test_failed = 0
    @test_ignored = 0
    puts ''
    puts '=================== RESULTS ====================='
    puts ''
    File.open(file_name).each do |line|
      # Typical test lines look like these:
      # ----------------------------------------------------
      # 1. normal output:
      # <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
      #
      # 2. fixture output
      # <path>/<test_file>.c:63:TEST(<test_group>, <test_function>):FAIL: Expected 0x00001234 Was 0x00005A5A
      # <path>/<test_file>.c:36:TEST(<test_group>, <test_function>):IGNORE
      # Note: "PASS" information won't be generated in this mode
      #
      # 3. fixture output with verbose information ("-v")
      # TEST(<test_group, <test_file>)<path>/<test_file>:168::FAIL: Expected 0x8D Was 0x8C
      # TEST(<test_group>, <test_file>)<path>/<test_file>:22::IGNORE: This Test Was Ignored On Purpose
      # IGNORE_TEST(<test_group, <test_file>)
      # TEST(<test_group, <test_file>) PASS
      #
      # Note: Where path is different on Unix vs Windows devices (Windows leads with a drive letter)!
      detect_os_specifics(line)
      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.
      next unless (line_array.size >= 4) || (line.start_with? 'TEST(') || (line.start_with? 'IGNORE_TEST(')

      # check if the output is fixture output (with verbose flag "-v")
      if (line.start_with? 'TEST(') || (line.start_with? 'IGNORE_TEST(')
        line_array = prepare_fixture_line(line)
        if line.include? ' PASS'
          test_passed_unity_fixture(line_array)
          @test_passed += 1
        elsif line.include? 'FAIL'
          test_failed_unity_fixture(line_array)
          @test_failed += 1
        elsif line.include? 'IGNORE'
          test_ignored_unity_fixture(line_array)
          @test_ignored += 1
        end
      # normal output / fixture output (without verbose "-v")
      elsif line.include? ':PASS'
        test_passed(line_array)
        @test_passed += 1
      elsif line.include? ':FAIL'
        test_failed(line_array)
        @test_failed += 1
      elsif line.include? ':IGNORE:'
        test_ignored(line_array)
        @test_ignored += 1
      elsif line.include? ':IGNORE'
        line_array.push('No reason given')
        test_ignored(line_array)
        @test_ignored += 1
      end
      @total_tests = @test_passed + @test_failed + @test_ignored
    end
    puts ''
    puts '=================== SUMMARY ====================='
    puts ''
    puts 'Tests Passed  : ' + @test_passed.to_s
    puts 'Tests Failed  : ' + @test_failed.to_s
    puts 'Tests Ignored : ' + @test_ignored.to_s

    return unless @xml_out

    # push information about the suite
    push_xml_output_suite_info
    # write xml output file
    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 |arg|
    if arg == '-xml'
      parse_my_file.set_xml_output
    else
      parse_my_file.process(arg)
      break
    end
  end
end