module RSpecFlake
<testsuite errors=“0” failures=“0” skipped=“0” tests=“4” time=“0.001283” timestamp=“2015-01-15T10:25:40-05:00”>
<testsuite location="./spec/a_spec.rb:1" name="a" tests="2" errors="0" failures="0" skipped="0"> <testcase name="a a 1" time="0.000177" location="./spec/a_spec.rb:6">
# sax parser # nokogiri.org/Nokogiri/XML/SAX.html
Constants
- DATE
- VERSION
Public Class Methods
cdata(content)
click to toggle source
# File lib/rspec_flake/convert.rb, line 30 def cdata content "\n<![CDATA[#{content}]]>\n" end
file_time(opts={})
click to toggle source
Read in JUnit xml files and output an xml report that lists the time spent per file
@example puts RSpecFlake.file_time
files: Dir.glob(“*.xml”)
# File lib/rspec_flake/file_time.rb, line 8 def file_time opts={} files = opts[:files] files = [files] unless files.is_a?(Array) file_times = Hash.new 0 xml = Nokogiri::XML(merge_individual_xml files: files) xml.xpath('//testcase').each do |test| file_name = test.attr('location').split(':').first file_times[file_name] += test.attr('time').to_i end file_times = file_times.sort_by { |file, time| time }.reverse # Paste into Google Sheets with Ctrl/Command + Shift + V output = '' file_times.each do |file, time| time = ChronicDuration.output(time) || '0' output += "#{time.ljust(12)}\t#{file}\n" end output end
hash_to_xml(source_hash)
click to toggle source
# File lib/rspec_flake/convert.rb, line 3 def hash_to_xml source_hash # top level attribues from root node converted = source_hash[:testsuites][:attrs] || {} converted[:testsuite] = [] source_hash[:testsuites][:testsuite].each do |suite_location, suite_obj| suite_with_tests = suite_obj[:attrs] || {} suite_with_tests[:testcase] = [] suite_obj[:testcase].each do |testcase_location, testcase_obj| testcase = testcase_obj[:attrs] if testcase_obj[:failure] fail_content = testcase_obj[:failure][:content] fail_content = cdata fail_content # content key must be a string for xml simple testcase.merge!(failure: testcase_obj[:failure][:attrs].merge('content' => fail_content)) end suite_with_tests[:testcase] << testcase end converted[:testsuite] << suite_with_tests end # ap converted, index: false, indent: 2 xml_out converted end
merge_individual_xml(opts={})
click to toggle source
Merges individual xml reports (from parallel_rspec) into a single combined xml file
@example:
merge_individual_xml files: Dir.glob('tmp/*.xml')
# File lib/rspec_flake/merge.rb, line 9 def merge_individual_xml opts={} files = opts[:files] files = [files] unless files.is_a?(Array) files_hash = {} files.each { |file| files_hash.merge!(RSpecFlake.parse_xml(file)[:testsuites][:testsuite]) } RSpecFlake.hash_to_xml({ testsuites: { testsuite: files_hash } }) end
merge_xml(opts={})
click to toggle source
merge input xml files into a hash
input - single path or array of paths to input xml files @return merged hash
# File lib/rspec_flake/merge.rb, line 23 def merge_xml opts={} input = opts[:input] raise 'input path(s) not provided' unless input input = [input] unless input.kind_of?(Array) merged = { testsuite: { testcase: {} } } # annotate each testcase node with two attributes # # failures - amount of times this test failed # runs - amount of times this test was executed # # name/time - saved from the first run of the testcase # location - stored for each run input.each do |xml_path| parsed = parse_xml(xml_path) parsed[:testsuites][:testsuite].each do |suite_location, suite_obj| suite_obj[:testcase].each do |testcase_location, testcase_obj| attrs = testcase_obj[:attrs] test = merged[:testsuite][:testcase][testcase_location] ||= { failures: 0, runs: 0, name: attrs['name'], location: attrs['location'], time: [] } if testcase_obj[:failure] test[:failures] += 1 test[:failure] ||= [] test[:failure] << { 'content' => cdata(testcase_obj[:failure][:content]) } end test[:runs] += 1 test[:time] << attrs['time'] end end end # transform merge hash keyed on location into simple testcase array # for xml conversion converted = { testsuite: { testcase: [] } } merged[:testsuite][:testcase].each do |testcase_location, testcase_obj| converted[:testsuite][:testcase] << testcase_obj end xml_out converted end
parse_xml(xml_path)
click to toggle source
Parse JUnit xml into a Hash keyed on location
# File lib/rspec_flake/parse.rb, line 73 def parse_xml xml_path xml = read_as_utf8 xml_path parser = @parser ||= Nokogiri::XML::SAX::Parser.new(ReportParser.new) parser.document.reset parser.parse xml parser.document.result end
read_as_utf8(path)
click to toggle source
# File lib/rspec_flake/parse.rb, line 68 def read_as_utf8 path File.read(path).encode!('UTF-8', invalid: :replace, undef: :replace, replace: '') end
stats_from_merge_xml(xml_string)
click to toggle source
# File lib/rspec_flake/stats.rb, line 3 def stats_from_merge_xml xml_string output = '' xml = Nokogiri::XML(xml_string) xml.xpath('//testcase').each do |testcase| # <testcase failures="0" runs="3" name="a a 1" location="./spec/a_spec.rb:6"> failures = testcase[:failures] runs = testcase[:runs] name = testcase[:name] location = testcase[:location] times = [] testcase.xpath('time').each do |time| times << time.content.to_f end average_time = (times.inject(:+).to_f / times.size).round 2 output += "#{name} - runs: #{runs} - failures: #{failures} - avg time: #{average_time} - #{location}\n" end output end
xml_out(hash)
click to toggle source
# File lib/rspec_flake/convert.rb, line 34 def xml_out hash xml_header = %Q(<?xml version="1.0" encoding="UTF-8"?>\n) xml_body = XmlSimple.xml_out(hash, RootName: 'testsuites') # xmlsimple will escape the cdata by default xml_body = EscapeUtils.unescape_html xml_body xml_header + xml_body end