class MesonJunit::Junit::XMLBuilder

Read parsed Meson test log and build JUnit-compatible XML.

Constants

BAD_CHARS

Invalid characters in JUnit class name.

Public Class Methods

build(log) click to toggle source
# File lib/meson-junit/junit/xml-builder.rb, line 5
def self.build(log)
  new.build(log)
end

Public Instance Methods

build(log) click to toggle source

Build JUnit-compatible XML from parsed Meson test log.

# File lib/meson-junit/junit/xml-builder.rb, line 12
def build(log)
  ::Nokogiri::XML::Builder.new do |xml|
    xml.testsuites(**testsuites_el_attrs(log)) do
      emit_testsuite_els(xml, log)
    end
  end.to_xml
end

Private Instance Methods

emit_properties(xml, test) click to toggle source

Emit Meson test attributes as JUnit `testsuite` properties.

# File lib/meson-junit/junit/xml-builder.rb, line 48
def emit_properties(xml, test)
  xml.properties do
    # convert all non-environment variable meson test attributes to
    # junit test suite properties
    test.data.each do |key, val|
      unless key == 'env'
        emit_property(xml, key, val)
      end
    end

    # convert meson test environment variables to junit test suite
    # properties
    (test.data['env'] || {}).each do |key, val|
      emit_property(xml, 'env.%s' % [key], val)
    end
  end
end
emit_property(xml, key, val) click to toggle source

Emit a Meson test attribute as a JUnit test suite property.

# File lib/meson-junit/junit/xml-builder.rb, line 94
def emit_property(xml, key, val)
  case val
  when Array, Hash
    # serialize arrays and hashes as JSON
    xml.property(name: key, value: ::JSON.unparse(val))
  else
    xml.property(name: key, value: val)
  end
end
emit_stdio(xml, test) click to toggle source

Emit standard output and standard error from a Meson test case as JUnit-compatible `system-out` and `system-err` elements.

# File lib/meson-junit/junit/xml-builder.rb, line 85
def emit_stdio(xml, test)
  # emit standard output and standard error
  xml.send('system-out', test.data['stdout'])
  xml.send('system-err', test.data['stderr'])
end
emit_testcase(xml, test) click to toggle source

Emit details of Meson test as a JUnit `testcase` element.

# File lib/meson-junit/junit/xml-builder.rb, line 68
def emit_testcase(xml, test)
  xml.testcase(**testcase_el_attrs(test)) do
    if test.failed?
      # emit a `failure` element with a body that contains the
      # standard error output from the Meson test.
      xml.failure(test.data['stderr'], message: 'Test failed.')
    end

    # emit standard io elements
    emit_stdio(xml, test)
  end
end
emit_testsuite_els(xml, log) click to toggle source

Convert Meson test log to a series of JUnit test suites.

We map Meson tests to a series of JUnit test suites with a single test case because that allows us to map the per-test attributes and environment variables to JUnit test suite properties.

# File lib/meson-junit/junit/xml-builder.rb, line 29
def emit_testsuite_els(xml, log)
  log.tests.each_with_index do |test, test_num|
    xml.testsuite(**testsuite_el_attrs(test, test_num)) do
      # emit testsuite properties
      emit_properties(xml, test)

      # emit testcase
      emit_testcase(xml, test)

      # emit standard output and standard error
      xml.send('system-out', test.data['stdout'])
      xml.send('system-err', test.data['stderr'])
    end
  end
end
safe_name(s) click to toggle source

Sanitize a Meson test name as a JUnit-friendly class name.

# File lib/meson-junit/junit/xml-builder.rb, line 170
def safe_name(s)
  s.gsub(BAD_CHARS, '_')
end
testcase_el_attrs(test) click to toggle source

Get hash of attributes for JUnit `testcase` element.

# File lib/meson-junit/junit/xml-builder.rb, line 149
def testcase_el_attrs(test)
  {
    # test case name
    name: 'main',

    # test case class name
    classname: safe_name(test.name),

    # time taken (in seconds) to execute test.
    time: test.duration,
  }
end
testsuite_el_attrs(test, test_num) click to toggle source

Get hash of attributes for JUnit `testsuite` element.

# File lib/meson-junit/junit/xml-builder.rb, line 127
def testsuite_el_attrs(test, test_num)
  {
    # test number, starting from zero
    id: test_num,

    # junit-friendly class name
    name: safe_name(test.name),

    # total number of tests (always 1)
    tests: 1,

    # total number of errors (always either 0 or 1)
    errors: test.failed? ? 1 : 0,

    # total amount of time for this test
    time: test.duration,
  }
end
testsuites_el_attrs(log) click to toggle source

Get hash of attributes for root JUnit `testsuites` element.

# File lib/meson-junit/junit/xml-builder.rb, line 107
def testsuites_el_attrs(log)
  {
    # total number of tests
    tests: log.tests.size,

    # total number of failed tests
    failures: log.tests.reduce(0) do |r, test|
      r + (test.failed? ? 1 : 0)
    end,

    # total amount of time across all tests
    time: log.tests.reduce(0) do |r, test|
      r + test.duration
    end,
  }
end