summaryrefslogtreecommitdiff
path: root/FreeRTOS-Plus/Test/CMock/vendor/unity/auto/stylize_as_junit.rb
blob: e01f7912a357f7c735829a67a3fc9dee92f5bcfa (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
#!/usr/bin/ruby
#
# unity_to_junit.rb
#
require 'fileutils'
require 'optparse'
require 'ostruct'
require 'set'

require 'pp'

VERSION = 1.0

class ArgvParser
  #
  # Return a structure describing the options.
  #
  def self.parse(args)
    # The options specified on the command line will be collected in *options*.
    # We set default values here.
    options = OpenStruct.new
    options.results_dir = '.'
    options.root_path = '.'
    options.out_file = 'results.xml'

    opts = OptionParser.new do |o|
      o.banner = 'Usage: unity_to_junit.rb [options]'

      o.separator ''
      o.separator 'Specific options:'

      o.on('-r', '--results <dir>', 'Look for Unity Results files here.') do |results|
        # puts "results #{results}"
        options.results_dir = results
      end

      o.on('-p', '--root_path <path>', 'Prepend this path to files in results.') do |root_path|
        options.root_path = root_path
      end

      o.on('-o', '--output <filename>', 'XML file to generate.') do |out_file|
        # puts "out_file: #{out_file}"
        options.out_file = out_file
      end

      o.separator ''
      o.separator 'Common options:'

      # No argument, shows at tail.  This will print an options summary.
      o.on_tail('-h', '--help', 'Show this message') do
        puts o
        exit
      end

      # Another typical switch to print the version.
      o.on_tail('--version', 'Show version') do
        puts "unity_to_junit.rb version #{VERSION}"
        exit
      end
    end

    opts.parse!(args)
    options
  end
end

class UnityToJUnit
  include FileUtils::Verbose
  attr_reader :report, :total_tests, :failures, :ignored
  attr_writer :targets, :root, :out_file

  def initialize
    @report = ''
    @unit_name = ''
  end

  def run
    # Clean up result file names
    results = @targets.map { |target| target.tr('\\', '/') }
    # puts "Output File: #{@out_file}"
    f = File.new(@out_file, 'w')
    write_xml_header(f)
    write_suites_header(f)
    results.each do |result_file|
      lines = File.readlines(result_file).map(&:chomp)

      raise "Empty test result file: #{result_file}" if lines.empty?

      result_output = get_details(result_file, lines)
      tests, failures, ignored = parse_test_summary(lines)
      result_output[:counts][:total] = tests
      result_output[:counts][:failed] = failures
      result_output[:counts][:ignored] = ignored
      result_output[:counts][:passed] = (result_output[:counts][:total] - result_output[:counts][:failed] - result_output[:counts][:ignored])

      # use line[0] from the test output to get the test_file path and name
      test_file_str = lines[0].tr('\\', '/')
      test_file_str = test_file_str.split(':')
      test_file = if test_file_str.length < 2
                    result_file
                  else
                    test_file_str[0] + ':' + test_file_str[1]
                  end
      result_output[:source][:path] = File.dirname(test_file)
      result_output[:source][:file] = File.basename(test_file)

      # save result_output
      @unit_name = File.basename(test_file, '.*')

      write_suite_header(result_output[:counts], f)
      write_failures(result_output, f)
      write_tests(result_output, f)
      write_ignored(result_output, f)
      write_suite_footer(f)
    end
    write_suites_footer(f)
    f.close
  end

  def usage(err_msg = nil)
    puts "\nERROR: "
    puts err_msg if err_msg
    puts 'Usage: unity_to_junit.rb [options]'
    puts ''
    puts 'Specific options:'
    puts '    -r, --results <dir>              Look for Unity Results files here.'
    puts '    -p, --root_path <path>           Prepend this path to files in results.'
    puts '    -o, --output <filename>          XML file to generate.'
    puts ''
    puts 'Common options:'
    puts '    -h, --help                       Show this message'
    puts '        --version                    Show version'

    exit 1
  end

  protected

  def get_details(_result_file, lines)
    results = results_structure
    lines.each do |line|
      line = line.tr('\\', '/')
      _src_file, src_line, test_name, status, msg = line.split(/:/)
      case status
      when 'IGNORE' then results[:ignores] << { test: test_name, line: src_line, message: msg }
      when 'FAIL'   then results[:failures] << { test: test_name, line: src_line, message: msg }
      when 'PASS'   then results[:successes] << { test: test_name, line: src_line, message: msg }
      end
    end
    results
  end

  def parse_test_summary(summary)
    raise "Couldn't parse test results: #{summary}" unless summary.find { |v| v =~ /(\d+) Tests (\d+) Failures (\d+) Ignored/ }

    [Regexp.last_match(1).to_i, Regexp.last_match(2).to_i, Regexp.last_match(3).to_i]
  end

  private

  def results_structure
    {
      source: { path: '', file: '' },
      successes: [],
      failures: [],
      ignores: [],
      counts: { total: 0, passed: 0, failed: 0, ignored: 0 },
      stdout: []
    }
  end

  def write_xml_header(stream)
    stream.puts "<?xml version='1.0' encoding='utf-8' ?>"
  end

  def write_suites_header(stream)
    stream.puts '<testsuites>'
  end

  def write_suite_header(counts, stream)
    stream.puts "\t<testsuite errors=\"0\" skipped=\"#{counts[:ignored]}\" failures=\"#{counts[:failed]}\" tests=\"#{counts[:total]}\" name=\"unity\">"
  end

  def write_failures(results, stream)
    result = results[:failures]
    result.each do |item|
      filename = File.join(results[:source][:path], File.basename(results[:source][:file], '.*'))
      stream.puts "\t\t<testcase classname=\"#{@unit_name}\" name=\"#{item[:test]}\" time=\"0\">"
      stream.puts "\t\t\t<failure message=\"#{item[:message]}\" type=\"Assertion\"/>"
      stream.puts "\t\t\t<system-err>&#xD;[File] #{filename}&#xD;[Line] #{item[:line]}&#xD;</system-err>"
      stream.puts "\t\t</testcase>"
    end
  end

  def write_tests(results, stream)
    result = results[:successes]
    result.each do |item|
      stream.puts "\t\t<testcase classname=\"#{@unit_name}\" name=\"#{item[:test]}\" time=\"0\" />"
    end
  end

  def write_ignored(results, stream)
    result = results[:ignores]
    result.each do |item|
      filename = File.join(results[:source][:path], File.basename(results[:source][:file], '.*'))
      puts "Writing ignored tests for test harness: #{filename}"
      stream.puts "\t\t<testcase classname=\"#{@unit_name}\" name=\"#{item[:test]}\" time=\"0\">"
      stream.puts "\t\t\t<skipped message=\"#{item[:message]}\" type=\"Assertion\"/>"
      stream.puts "\t\t\t<system-err>&#xD;[File] #{filename}&#xD;[Line] #{item[:line]}&#xD;</system-err>"
      stream.puts "\t\t</testcase>"
    end
  end

  def write_suite_footer(stream)
    stream.puts "\t</testsuite>"
  end

  def write_suites_footer(stream)
    stream.puts '</testsuites>'
  end
end

if $0 == __FILE__
  # parse out the command options
  options = ArgvParser.parse(ARGV)

  # create an instance to work with
  utj = UnityToJUnit.new
  begin
    # look in the specified or current directory for result files
    targets = "#{options.results_dir.tr('\\', '/')}**/*.test*"

    results = Dir[targets]

    raise "No *.testpass, *.testfail, or *.testresults files found in '#{targets}'" if results.empty?

    utj.targets = results

    # set the root path
    utj.root = options.root_path

    # set the output XML file name
    # puts "Output File from options: #{options.out_file}"
    utj.out_file = options.out_file

    # run the summarizer
    puts utj.run
  rescue StandardError => e
    utj.usage e.message
  end
end