lib/minitest_log.rb in minitest_log-1.0.0 vs lib/minitest_log.rb in minitest_log-1.0.1

- old
+ new

@@ -2,13 +2,16 @@ require 'minitest/autorun' require 'minitest/assertions' require 'diff/lcs' require_relative 'verdict_assertion' +require_relative 'minitest_assertions_patch' class MinitestLog + include REXML + include Minitest::Assertions include VerdictAssertion attr_accessor \ :assertions, :counts, @@ -19,57 +22,28 @@ :verdict_ids, :xml_indentation, :error_verdict, :summary - include REXML - include Minitest::Assertions - class MinitestLogError < Exception; end class NoBlockError < MinitestLogError; end class DuplicateVerdictIdError < MinitestLogError; end class IllegalElementNameError < MinitestLogError; end class IllegalNewError < MinitestLogError; end def initialize(file_path, options=Hash.new) raise NoBlockError.new('No block given for MinitestLog#new.') unless (block_given?) - default_options = Hash[ - :root_name => 'log', - :xml_indentation => 2, - :error_verdict => false, - :summary => false - ] - options = default_options.merge(options) - self.assertions = 0 self.file_path = file_path - self.root_name = options[:root_name] - self.xml_indentation = options[:xml_indentation] - self.summary = options[:summary] - self.error_verdict = options[:error_verdict] || false - self.backtrace_filter = options[:backtrace_filter] || /minitest/ - self.file = File.open(self.file_path, 'w') - log_puts("REMARK\tThis text log is the precursor for an XML log.") - log_puts("REMARK\tIf the logged process completes, this text will be converted to XML.") - log_puts("BEGIN\t#{self.root_name}") - self.counts = Hash[ - :verdict => 0, - :failure => 0, - :error => 0, - ] - begin - yield self - rescue => x - put_element('uncaught_exception', :timestamp, :class => x.class) do - put_element('message', x.message) - put_element('backtrace') do - backtrace = filter_backtrace(x.backtrace) - put_pre(backtrace.join("\n")) - end + handle_options(options) + do_log do + begin + yield self + rescue => x + handle_exception(x) end end - dispose - nil + create_xml_log end def section(name, *args) put_element('section', {:name => name}, *args) do yield if block_given? @@ -88,82 +62,16 @@ end nil end def put_element(element_name = 'element', *args) - if false || - caller[0].match(/minitest_log.rb/) || - caller[0].match(/verdict_assertion.rb/) - # Make the element name special. - element_name += '_' - elsif element_name.end_with?('_') - # Don't accept user's special. - message = "Element name should not end with underscore: #{element_name}" - raise IllegalElementNameError.new(message) + conditioned_element_name = condition_element_name(element_name, caller[0]) + if block_given? + Element.new(self, conditioned_element_name, *args, &Proc.new) else - # Ok. + Element.new(self, conditioned_element_name, *args) end - attributes = {} - pcdata = '' - start_time = nil - duration_to_be_included = false - block_to_be_rescued = false - args.each do |arg| - case - when arg.kind_of?(Hash) - attributes.merge!(arg) - when arg.kind_of?(String) - pcdata += arg - when arg == :timestamp - attributes[:timestamp] = MinitestLog.timestamp - when arg == :duration - duration_to_be_included = true - when arg == :rescue - block_to_be_rescued = true - else - pcdata = pcdata + arg.inspect - end - end - log_puts("BEGIN\t#{element_name}") - put_attributes(attributes) - unless pcdata.empty? - # Guard against using a terminator that's a substring of pcdata. - s = 'EOT' - terminator = s - while pcdata.match(terminator) do - terminator += s - end - log_puts("PCDATA\t<<#{terminator}") - log_puts(pcdata) - log_puts(terminator) - end - start_time = Time.new if duration_to_be_included - if block_given? - if block_to_be_rescued - begin - yield - rescue Exception => x - put_element('rescued_exception', {:class => x.class, :message => x.message}) do - put_element('backtrace') do - backtrace = filter_backtrace(x.backtrace) - put_pre(backtrace.join("\n")) - end - end - self.counts[:error] += 1 - end - else - yield - end - end - if start_time - end_time = Time.now - duration_f = end_time.to_f - start_time.to_f - duration_s = format('%.3f', duration_f) - put_attributes({:duration_seconds => duration_s}) - end - log_puts("END\t#{element_name}") - nil end def put_each_with_index(name, obj) lines = [''] obj.each_with_index do |item, i| @@ -271,22 +179,38 @@ end end private - def dispose + def do_log + begin_log + yield + end_log + end - # Add a verdict for the error count, if needed. + def begin_log + self.counts = Hash[ + :verdict => 0, + :failure => 0, + :error => 0, + ] + self.assertions = 0 + self.file = File.open(self.file_path, 'w') + log_puts("REMARK\tThis text log is the precursor for an XML log.") + log_puts("REMARK\tIf the logged process completes, this text will be converted to XML.") + log_puts("BEGIN\t#{self.root_name}") + end + + def end_log if self.error_verdict verdict_assert_equal?('error_count', 0, self.counts[:error]) end - - # Close the text log. log_puts("END\t#{self.root_name}") self.file.close + end - # Create the xml log. + def create_xml_log document = REXML::Document.new File.open(self.file_path, 'r') do |file| element = document stack = Array.new data_a = Array.new @@ -372,14 +296,11 @@ args_hash.each_pair do |k, v| put_element(k.to_s, {:class => v.class, :value => v.inspect}) end if exception self.counts[:failure] += 1 - # If the encoding is not UTF-8, a string will have been added. - # Remove it, so that the message is the same on all platforms. - conditioned_message = exception.message.gsub("# encoding: UTF-8\n", '') - put_element('exception', {:class => exception.class, :message => conditioned_message}) do + put_element('exception', {:class => exception.class, :message => exception.message}) do put_element('backtrace') do backtrace = filter_backtrace(exception.backtrace) put_pre(backtrace.join("\n")) end end @@ -390,11 +311,11 @@ def put_attributes(attributes) attributes.each_pair do |name, value| value = case when value.is_a?(String) - value + value.gsub("\n", "\\n") when value.is_a?(Symbol) value.to_s else value.inspect end @@ -417,23 +338,31 @@ end self.verdict_ids.add(verdict_id) nil end - def put_cdata(text) + def put_cdata_or_pcdata(token, text) # Guard against using a terminator that's a substring of the cdata. s = 'EOT' terminator = s while text.match(terminator) do terminator += s end - log_puts("CDATA\t<<#{terminator}") + log_puts("#{token}\t<<#{terminator}") log_puts(text) log_puts(terminator) nil end + def put_cdata(text) + put_cdata_or_pcdata('CDATA', text) + end + + def put_pcdata(text) + put_cdata_or_pcdata('PCDATA', text) + end + def get_assertion_outcome(verdict_id, assertion_method, *assertion_args) validate_verdict_id(verdict_id) self.counts[:verdict] += 1 begin if block_given? @@ -469,12 +398,11 @@ ts = now.strftime('%Y-%m-%d-%a-%H.%M.%S') usec_s = (now.usec / 1000).to_s while usec_s.length < 3 do usec_s = '0' + usec_s end - # noinspection RubyUnusedLocalVariable - ts += ".#{usec_s}" + "#{ts}.#{usec_s}" end def assertion_method_for(verdict_method) # Our verdict method name is just an assertion method name # with prefixed 'verdict_' and suffixed '?'. @@ -491,8 +419,164 @@ document = nil File.open(file_path) do |file| document = REXML::Document.new(file) end document + end + + def condition_element_name(element_name, caller_0) + if caller_is_us?(caller_0) + conditioned_element_name = element_name + '_' + elsif element_name.end_with?('_') + message = "Element name should not end with underscore: #{element_name}" + raise IllegalElementNameError.new(message) + else + conditioned_element_name = element_name + end + conditioned_element_name + end + + def caller_is_us?(caller_0) + caller_0.match(/minitest_log.rb/) || caller_0.match(/verdict_assertion.rb/) + end + + def handle_options(options) + default_options = Hash[ + :root_name => 'log', + :xml_indentation => 2, + :error_verdict => false, + :summary => false + ] + options = default_options.merge(options) + self.root_name = options[:root_name] + self.xml_indentation = options[:xml_indentation] + self.summary = options[:summary] + self.error_verdict = options[:error_verdict] || false + self.backtrace_filter = options[:backtrace_filter] || /minitest/ + end + + def handle_exception(x) + put_element('uncaught_exception', :timestamp, :class => x.class) do + put_element('message', x.message) + put_element('backtrace') do + backtrace = filter_backtrace(x.backtrace) + put_pre(backtrace.join("\n")) + end + end + end + + class Element + + attr_accessor \ + :args, + :attributes, + :block_to_be_rescued, + :duration_to_be_included, + :element_name, + :log, + :pcdata, + :start_time + + def initialize(log, element_name, *args) + + self.log = log + self.element_name = element_name + self.args = args + + self.attributes = {} + self.block_to_be_rescued = false + self.duration_to_be_included = false + self.pcdata = '' + self.start_time = nil + + process_args + put_element do + put_attributes + put_pcdata + do_duration do + do_block(&Proc.new) if block_given? + end + end + + end + + def process_args + args.each do |arg| + case + when arg.kind_of?(Hash) + self.attributes.merge!(arg) + when arg.kind_of?(String) + self.pcdata += arg + when arg == :timestamp + self.attributes[:timestamp] = MinitestLog.timestamp + when arg == :duration + self.duration_to_be_included = true + when arg == :rescue + self.block_to_be_rescued = true + else + self.pcdata = self.pcdata + arg.inspect + end + end + end + + def put_element + log_puts("BEGIN\t#{element_name}") + yield + log_puts("END\t#{element_name}") + end + + def put_attributes + log_put_attributes(attributes) + end + + def put_pcdata + unless pcdata.empty? + log.send(:put_pcdata, pcdata) + end + end + + def do_duration + self.start_time = Time.new + yield + if duration_to_be_included + end_time = Time.now + duration_f = end_time.to_f - start_time.to_f + duration_s = format('%.3f', duration_f) + log_put_attributes({:duration_seconds => duration_s}) + end + end + + def do_block + if block_to_be_rescued + begin + yield + rescue Exception => x + log.put_element('rescued_exception', {:class => x.class, :message => x.message}) do + log.put_element('backtrace') do + backtrace = log_filter_backtrace(x.backtrace) + log.put_pre(backtrace.join("\n")) + end + end + log.counts[:error] += 1 + end + else + yield + end + end + + # The called methods are private. + + def log_puts(s) + log.send(:log_puts, s) + end + + def log_put_attributes(attributes) + log.send(:put_attributes, attributes) + end + + def log_filter_backtrace(backtrace) + log.send(:filter_backtrace, backtrace) + end + end end