mirror of
https://gitea.invidious.io/iv-org/shard-spectator.git
synced 2024-08-15 00:53:35 +00:00
Nuke formatting types to prep for new types
This commit is contained in:
parent
f3afd74dc5
commit
31d819e4c9
32 changed files with 0 additions and 1271 deletions
|
@ -1,42 +0,0 @@
|
||||||
require "colorize"
|
|
||||||
|
|
||||||
module Spectator::Formatting
|
|
||||||
# Method for colorizing output.
|
|
||||||
module Color
|
|
||||||
extend self
|
|
||||||
|
|
||||||
# Symbols in `Colorize` representing result types and formatting types.
|
|
||||||
private COLORS = {
|
|
||||||
success: :green,
|
|
||||||
failure: :red,
|
|
||||||
error: :magenta,
|
|
||||||
pending: :yellow,
|
|
||||||
comment: :cyan,
|
|
||||||
}
|
|
||||||
|
|
||||||
# Colorizes some text with the success color.
|
|
||||||
def pass(text)
|
|
||||||
text.colorize(COLORS[:success])
|
|
||||||
end
|
|
||||||
|
|
||||||
# Colorizes some text with the failure color.
|
|
||||||
def failure(text)
|
|
||||||
text.colorize(COLORS[:failure])
|
|
||||||
end
|
|
||||||
|
|
||||||
# Colorizes some text with the error color.
|
|
||||||
def error(text)
|
|
||||||
text.colorize(COLORS[:error])
|
|
||||||
end
|
|
||||||
|
|
||||||
# Colorizes some text with the pending/skipped color.
|
|
||||||
def pending(text)
|
|
||||||
text.colorize(COLORS[:pending])
|
|
||||||
end
|
|
||||||
|
|
||||||
# Colorizes some text with the comment color.
|
|
||||||
def comment(text)
|
|
||||||
text.colorize(COLORS[:comment])
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,20 +0,0 @@
|
||||||
module Spectator::Formatting
|
|
||||||
# Produces a stringified comment for output.
|
|
||||||
private struct Comment(T)
|
|
||||||
# Creates the comment.
|
|
||||||
def initialize(@text : T)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Appends the comment to the output.
|
|
||||||
def to_s(io)
|
|
||||||
io << '#'
|
|
||||||
io << ' '
|
|
||||||
io << @text
|
|
||||||
end
|
|
||||||
|
|
||||||
# Creates a colorized version of the comment.
|
|
||||||
def self.color(text)
|
|
||||||
Color.comment(new(text))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,65 +0,0 @@
|
||||||
require "../example_group"
|
|
||||||
require "./formatter"
|
|
||||||
require "./suite_summary"
|
|
||||||
|
|
||||||
module Spectator::Formatting
|
|
||||||
# Produces an indented document-style output.
|
|
||||||
# Each nested group of examples increases the indent.
|
|
||||||
# Example names are output in a color based on their result.
|
|
||||||
class DocumentFormatter < Formatter
|
|
||||||
include SuiteSummary
|
|
||||||
|
|
||||||
private INDENT = " "
|
|
||||||
|
|
||||||
@previous_hierarchy = [] of ExampleGroup
|
|
||||||
|
|
||||||
# Creates the formatter.
|
|
||||||
# By default, output is sent to STDOUT.
|
|
||||||
def initialize(@io : IO = STDOUT)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Does nothing when an example is started.
|
|
||||||
def start_example(example)
|
|
||||||
hierarchy = group_hierarchy(example)
|
|
||||||
tuple = hierarchy_diff(@previous_hierarchy, hierarchy)
|
|
||||||
print_sub_hierarchy(*tuple)
|
|
||||||
@previous_hierarchy = hierarchy
|
|
||||||
end
|
|
||||||
|
|
||||||
# Produces a single character output based on a result.
|
|
||||||
def end_example(example)
|
|
||||||
@previous_hierarchy.size.times { @io.print INDENT }
|
|
||||||
@io.puts example.result.accept(Color) { example }
|
|
||||||
end
|
|
||||||
|
|
||||||
# Produces a list of groups making up the hierarchy for an example.
|
|
||||||
private def group_hierarchy(example)
|
|
||||||
hierarchy = [] of ExampleGroup
|
|
||||||
group = example.group
|
|
||||||
while group.is_a?(ExampleGroup)
|
|
||||||
hierarchy << group if group.name?
|
|
||||||
group = group.group?
|
|
||||||
end
|
|
||||||
hierarchy.reverse
|
|
||||||
end
|
|
||||||
|
|
||||||
# Generates a difference between two hierarchies.
|
|
||||||
private def hierarchy_diff(first, second)
|
|
||||||
index = -1
|
|
||||||
diff = second.skip_while do |group|
|
|
||||||
index += 1
|
|
||||||
first.size > index && first[index] == group
|
|
||||||
end
|
|
||||||
{index, diff}
|
|
||||||
end
|
|
||||||
|
|
||||||
# Displays an indented hierarchy starting partially into the whole hierarchy.
|
|
||||||
private def print_sub_hierarchy(index, sub_hierarchy)
|
|
||||||
sub_hierarchy.each do |group|
|
|
||||||
index.times { @io.print INDENT }
|
|
||||||
@io.puts group.name
|
|
||||||
index += 1
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,60 +0,0 @@
|
||||||
require "./formatter"
|
|
||||||
require "./suite_summary"
|
|
||||||
|
|
||||||
module Spectator::Formatting
|
|
||||||
# Produces a single character for each example.
|
|
||||||
# A dot is output for each successful example (hence the name).
|
|
||||||
# Other characters are output for non-successful results.
|
|
||||||
# At the end of the test suite, a summary of failures and results is displayed.
|
|
||||||
class DotsFormatter < Formatter
|
|
||||||
include SuiteSummary
|
|
||||||
|
|
||||||
# Creates the formatter.
|
|
||||||
# By default, output is sent to STDOUT.
|
|
||||||
def initialize(@io : IO = STDOUT)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Does nothing when an example is started.
|
|
||||||
def start_example(example)
|
|
||||||
# ...
|
|
||||||
end
|
|
||||||
|
|
||||||
# Produces a single character output based on a result.
|
|
||||||
def end_example(example)
|
|
||||||
@io.print example.result.accept(Character)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Interface for `Result` to pick a character for output.
|
|
||||||
private module Character
|
|
||||||
extend self
|
|
||||||
|
|
||||||
# Characters for each of the result types.
|
|
||||||
private CHARACTERS = {
|
|
||||||
success: '.',
|
|
||||||
failure: 'F',
|
|
||||||
error: 'E',
|
|
||||||
pending: '*',
|
|
||||||
}
|
|
||||||
|
|
||||||
# Character output for a successful example.
|
|
||||||
def pass
|
|
||||||
Color.pass(CHARACTERS[:success])
|
|
||||||
end
|
|
||||||
|
|
||||||
# Character output for a failed example.
|
|
||||||
def failure
|
|
||||||
Color.failure(CHARACTERS[:failure])
|
|
||||||
end
|
|
||||||
|
|
||||||
# Character output for an errored example.
|
|
||||||
def error
|
|
||||||
Color.error(CHARACTERS[:error])
|
|
||||||
end
|
|
||||||
|
|
||||||
# Character output for a pending or skipped example.
|
|
||||||
def pending
|
|
||||||
Color.pending(CHARACTERS[:pending])
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,21 +0,0 @@
|
||||||
require "./failure_junit_test_case"
|
|
||||||
|
|
||||||
module Spectator::Formatting
|
|
||||||
# JUnit test case for a errored result.
|
|
||||||
private class ErrorJUnitTestCase < FailureJUnitTestCase
|
|
||||||
# Result for this test case.
|
|
||||||
private getter result
|
|
||||||
|
|
||||||
# Creates the JUnit test case.
|
|
||||||
def initialize(example : Example)
|
|
||||||
super
|
|
||||||
@result = example.result.as(ErrorResult)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Adds the exception to the XML block.
|
|
||||||
private def content(xml)
|
|
||||||
xml.element("error", message: @result.error.message, type: @result.error.class)
|
|
||||||
super
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,126 +0,0 @@
|
||||||
module Spectator::Formatting
|
|
||||||
# Constructs a block of text containing information about a failed example.
|
|
||||||
#
|
|
||||||
# A failure block takes the form:
|
|
||||||
#
|
|
||||||
# ```text
|
|
||||||
# 1) Example name
|
|
||||||
# Failure: Reason or message
|
|
||||||
#
|
|
||||||
# Expected: value
|
|
||||||
# got: value
|
|
||||||
#
|
|
||||||
# # spec/source_spec.cr:42
|
|
||||||
# ```
|
|
||||||
private struct FailureBlock
|
|
||||||
# Creates the failure block.
|
|
||||||
# The *index* uniquely identifies the failure in the output.
|
|
||||||
# The *example* is the failed example.
|
|
||||||
def initialize(@index : Int32, @example : Example)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Retrieves the failed result.
|
|
||||||
private def result
|
|
||||||
@example.result.as(FailResult)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Creates the block of text describing the failure.
|
|
||||||
def to_s(io)
|
|
||||||
indent = Indent.new(io)
|
|
||||||
inner_indent = integer_length(@index) + 2 # +2 for ) and space after number.
|
|
||||||
|
|
||||||
indent.increase do
|
|
||||||
title(indent)
|
|
||||||
indent.increase(inner_indent) do
|
|
||||||
content(indent)
|
|
||||||
location(indent)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Produces the title of the failure block.
|
|
||||||
# The line takes the form:
|
|
||||||
# ```text
|
|
||||||
# 1) Example name
|
|
||||||
# ```
|
|
||||||
private def title(indent)
|
|
||||||
indent.line(NumberedItem.new(@index, @example))
|
|
||||||
end
|
|
||||||
|
|
||||||
# Produces the main content of the failure block.
|
|
||||||
# Any failed expectations are displayed,
|
|
||||||
# then an error stacktrace if an error occurred.
|
|
||||||
private def content(indent)
|
|
||||||
unsatisfied_expectations(indent)
|
|
||||||
error_stacktrace(indent) if result.is_a?(ErrorResult)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Produces a list of unsatisfied expectations and their values.
|
|
||||||
private def unsatisfied_expectations(indent)
|
|
||||||
result.expectations.reject(&.satisfied?).each do |expectation|
|
|
||||||
indent.line(Color.failure(LabeledText.new("Failure", expectation.failure_message)))
|
|
||||||
indent.line
|
|
||||||
indent.increase do
|
|
||||||
matcher_values(indent, expectation)
|
|
||||||
end
|
|
||||||
indent.line
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Produces the values list for an expectation
|
|
||||||
private def matcher_values(indent, expectation)
|
|
||||||
MatchDataValues.new(expectation.values).each do |pair|
|
|
||||||
colored_pair = if expectation.satisfied?
|
|
||||||
Color.pass(pair)
|
|
||||||
else
|
|
||||||
Color.failure(pair)
|
|
||||||
end
|
|
||||||
indent.line(colored_pair)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Produces the stack trace for an errored result.
|
|
||||||
private def error_stacktrace(indent)
|
|
||||||
error = result.error
|
|
||||||
first_line = error.message.try(&.lines).try(&.first)
|
|
||||||
indent.line(Color.error(LabeledText.new("Error", first_line)))
|
|
||||||
indent.line
|
|
||||||
indent.increase do
|
|
||||||
loop do
|
|
||||||
display_error(indent, error)
|
|
||||||
if (next_error = error.cause)
|
|
||||||
error = next_error
|
|
||||||
else
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
indent.line
|
|
||||||
end
|
|
||||||
|
|
||||||
# Display a single error and its stacktrace.
|
|
||||||
private def display_error(indent, error) : Nil
|
|
||||||
indent.line(Color.error(LabeledText.new(error.class.to_s, error)))
|
|
||||||
indent.increase do
|
|
||||||
error.backtrace.each do |frame|
|
|
||||||
indent.line(Color.error(frame))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Produces the location line of the failure block.
|
|
||||||
private def location(indent)
|
|
||||||
indent.line(Comment.color(@example.location))
|
|
||||||
end
|
|
||||||
|
|
||||||
# Gets the number of characters a positive integer spans in base 10.
|
|
||||||
private def integer_length(index)
|
|
||||||
count = 1
|
|
||||||
while index >= 10
|
|
||||||
index /= 10
|
|
||||||
count += 1
|
|
||||||
end
|
|
||||||
count
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,19 +0,0 @@
|
||||||
module Spectator::Formatting
|
|
||||||
# Produces a stringified command to run a failed test.
|
|
||||||
private struct FailureCommand
|
|
||||||
# Creates the failure command.
|
|
||||||
def initialize(@example : Example)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Appends the command to the output.
|
|
||||||
def to_s(io)
|
|
||||||
io << "crystal spec "
|
|
||||||
io << @example.location
|
|
||||||
end
|
|
||||||
|
|
||||||
# Colorizes the command instance based on the result.
|
|
||||||
def self.color(example)
|
|
||||||
example.result.accept(Color) { new(example) }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,39 +0,0 @@
|
||||||
require "./finished_junit_test_case"
|
|
||||||
|
|
||||||
module Spectator::Formatting
|
|
||||||
# JUnit test case for a failed result.
|
|
||||||
private class FailureJUnitTestCase < FinishedJUnitTestCase
|
|
||||||
# Result for this test case.
|
|
||||||
private getter result
|
|
||||||
|
|
||||||
# Creates the JUnit test case.
|
|
||||||
def initialize(example : Example)
|
|
||||||
super
|
|
||||||
@result = example.result.as(FailResult)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Status string specific to the result type.
|
|
||||||
private def status : String
|
|
||||||
"FAIL"
|
|
||||||
end
|
|
||||||
|
|
||||||
# Adds the failed expectations to the XML block.
|
|
||||||
private def content(xml)
|
|
||||||
super
|
|
||||||
@result.expectations.reject(&.satisfied?).each do |expectation|
|
|
||||||
xml.element("failure", message: expectation.failure_message) do
|
|
||||||
expectation_values(expectation.values, xml)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Adds the expectation values to the failure block.
|
|
||||||
private def expectation_values(labeled_values, xml)
|
|
||||||
labeled_values.each do |entry|
|
|
||||||
label = entry.first
|
|
||||||
value = entry.last
|
|
||||||
xml.text("#{label}: #{value}\n")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,21 +0,0 @@
|
||||||
require "./junit_test_case"
|
|
||||||
|
|
||||||
module Spectator::Formatting
|
|
||||||
# Commonalities of all test cases that ran (success or failure).
|
|
||||||
private abstract class FinishedJUnitTestCase < JUnitTestCase
|
|
||||||
# Produces the test case XML element.
|
|
||||||
def to_xml(xml : ::XML::Builder)
|
|
||||||
xml.element("testcase", **full_attributes) do
|
|
||||||
content(xml)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Attributes that go in the "testcase" XML element.
|
|
||||||
private def full_attributes
|
|
||||||
attributes.merge(
|
|
||||||
time: result.elapsed.total_seconds,
|
|
||||||
assertions: result.expectations.size
|
|
||||||
)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,41 +0,0 @@
|
||||||
module Spectator::Formatting
|
|
||||||
# Provides a more human-friendly formatting for a time span.
|
|
||||||
# This produces a string with the minimum of
|
|
||||||
# microseconds, milliseconds, seconds, minutes, hours, or days.
|
|
||||||
private struct HumanTime
|
|
||||||
@string : String
|
|
||||||
|
|
||||||
# Creates the wrapper
|
|
||||||
def initialize(span)
|
|
||||||
@string = simplify(span)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Produces the human-friendly string for a time span.
|
|
||||||
def to_s(io)
|
|
||||||
io << @string
|
|
||||||
end
|
|
||||||
|
|
||||||
# Does the actual work of converting a time span to string.
|
|
||||||
private def simplify(span)
|
|
||||||
millis = span.total_milliseconds
|
|
||||||
return "#{(millis * 1000).round.to_i} microseconds" if millis < 1
|
|
||||||
|
|
||||||
seconds = span.total_seconds
|
|
||||||
return "#{millis.round(2)} milliseconds" if seconds < 1
|
|
||||||
return "#{seconds.round(2)} seconds" if seconds < 60
|
|
||||||
|
|
||||||
int_seconds = seconds.to_i
|
|
||||||
minutes = int_seconds // 60
|
|
||||||
int_seconds %= 60
|
|
||||||
return sprintf("%i:%02i", minutes, int_seconds) if minutes < 60
|
|
||||||
|
|
||||||
hours = minutes // 60
|
|
||||||
minutes %= 60
|
|
||||||
return sprintf("%i:%02i:%02i", hours, minutes, int_seconds) if hours < 24
|
|
||||||
|
|
||||||
days = hours // 24
|
|
||||||
hours %= 24
|
|
||||||
sprintf("%i days %i:%02i:%02i", days, hours, minutes, int_seconds)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,50 +0,0 @@
|
||||||
module Spectator::Formatting
|
|
||||||
# Tracks indentation for text output.
|
|
||||||
# To use, create an instance and call `#increase` when a block should be indented.
|
|
||||||
# The `#increase` method yields, so additional `#increase` and `#line` methods can be called.
|
|
||||||
# Then call `#line` to produce a line of text at the current indent.
|
|
||||||
# ```
|
|
||||||
# indent = Indent.new(io)
|
|
||||||
# indent.increase do
|
|
||||||
# indent.line("Text")
|
|
||||||
# indent.increase do
|
|
||||||
# indent.line("More text")
|
|
||||||
# end
|
|
||||||
# end
|
|
||||||
# ```
|
|
||||||
private struct Indent
|
|
||||||
# Default number of spaces to indent by.
|
|
||||||
INDENT_SIZE = 2
|
|
||||||
|
|
||||||
# Creates the identation tracker.
|
|
||||||
# The *io* is the stream to output to.
|
|
||||||
# The *indent_size* is how much (number of spaces) to indent at each level.
|
|
||||||
# The *initial_indent* is what the ident should be set to.
|
|
||||||
def initialize(@io : IO, @indent_size = INDENT_SIZE, inital_indent @indent = 0)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Indents the text and yields.
|
|
||||||
def increase(&block)
|
|
||||||
increase(@indent_size, &block)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Indents the text by a specified amount and yields.
|
|
||||||
def increase(amount) : Nil
|
|
||||||
@indent += amount
|
|
||||||
yield
|
|
||||||
ensure
|
|
||||||
@indent -= amount
|
|
||||||
end
|
|
||||||
|
|
||||||
# Produces an empty line.
|
|
||||||
def line
|
|
||||||
@io.puts
|
|
||||||
end
|
|
||||||
|
|
||||||
# Produces a line of indented text.
|
|
||||||
def line(text)
|
|
||||||
@indent.times { @io << ' ' }
|
|
||||||
@io.puts text
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,95 +0,0 @@
|
||||||
require "json"
|
|
||||||
require "./formatter"
|
|
||||||
|
|
||||||
module Spectator::Formatting
|
|
||||||
# Produces a JSON document containing the test results.
|
|
||||||
class JsonFormatter < Formatter
|
|
||||||
# Creates the formatter.
|
|
||||||
# By default, output is sent to STDOUT.
|
|
||||||
def initialize(io : IO = STDOUT)
|
|
||||||
@json = ::JSON::Builder.new(io)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Called when a test suite is starting to execute.
|
|
||||||
def start_suite(suite : TestSuite)
|
|
||||||
@json.start_document
|
|
||||||
@json.start_object
|
|
||||||
@json.string("examples")
|
|
||||||
@json.start_array
|
|
||||||
end
|
|
||||||
|
|
||||||
# Called when a test suite finishes.
|
|
||||||
# The results from the entire suite are provided.
|
|
||||||
# The *profile* value is not nil when profiling results should be displayed.
|
|
||||||
def end_suite(report : Report, profile : Profile?)
|
|
||||||
@json.end_array # examples
|
|
||||||
totals(report)
|
|
||||||
timing(report)
|
|
||||||
profile(profile) if profile
|
|
||||||
@json.field("result", report.failed? ? "fail" : "success")
|
|
||||||
@json.end_object
|
|
||||||
end
|
|
||||||
|
|
||||||
# Called before a test starts.
|
|
||||||
def start_example(example : Example)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Called when a test finishes.
|
|
||||||
# The result of the test is provided.
|
|
||||||
def end_example(example : Example)
|
|
||||||
example.result.to_json(@json, example)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Adds the totals section of the document.
|
|
||||||
private def totals(report)
|
|
||||||
@json.field("totals") do
|
|
||||||
@json.object do
|
|
||||||
@json.field("examples", report.example_count)
|
|
||||||
@json.field("success", report.successful_count)
|
|
||||||
@json.field("fail", report.failed_count)
|
|
||||||
@json.field("error", report.error_count)
|
|
||||||
@json.field("pending", report.pending_count)
|
|
||||||
@json.field("remaining", report.remaining_count)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Adds the timings section of the document.
|
|
||||||
private def timing(report)
|
|
||||||
@json.field("timing") do
|
|
||||||
@json.object do
|
|
||||||
@json.field("runtime", report.runtime.total_seconds)
|
|
||||||
@json.field("examples", report.example_runtime.total_seconds)
|
|
||||||
@json.field("overhead", report.overhead_time.total_seconds)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Adds the profile information to the document.
|
|
||||||
private def profile(profile)
|
|
||||||
@json.field("profile") do
|
|
||||||
@json.object do
|
|
||||||
@json.field("count", profile.size)
|
|
||||||
@json.field("time", profile.total_time.total_seconds)
|
|
||||||
@json.field("percentage", profile.percentage)
|
|
||||||
@json.field("results") do
|
|
||||||
@json.array do
|
|
||||||
profile.each do |result|
|
|
||||||
profile_entry(result)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Adds a profile entry to the document.
|
|
||||||
private def profile_entry(example)
|
|
||||||
@json.object do
|
|
||||||
@json.field("example", example)
|
|
||||||
@json.field("time", example.result.elapsed.total_seconds)
|
|
||||||
@json.field("location", example.location)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,63 +0,0 @@
|
||||||
require "xml"
|
|
||||||
|
|
||||||
module Spectator::Formatting
|
|
||||||
# Formatter for producing a JUnit XML report.
|
|
||||||
class JUnitFormatter < Formatter
|
|
||||||
# Name of the JUnit output file.
|
|
||||||
private JUNIT_XML_FILE = "output.xml"
|
|
||||||
|
|
||||||
# Name of the top-level test suites block.
|
|
||||||
private NAME = "Spec"
|
|
||||||
|
|
||||||
# Creates the formatter.
|
|
||||||
# By default, output is sent to STDOUT.
|
|
||||||
def initialize(output_dir : String)
|
|
||||||
path = File.join(output_dir, JUNIT_XML_FILE)
|
|
||||||
@io = File.open(path, "w")
|
|
||||||
@xml = XML::Builder.new(@io)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Called when a test suite is starting to execute.
|
|
||||||
def start_suite(suite : TestSuite)
|
|
||||||
@xml.start_document(encoding: "UTF-8")
|
|
||||||
end
|
|
||||||
|
|
||||||
# Called when a test suite finishes.
|
|
||||||
# The results from the entire suite are provided.
|
|
||||||
# The *profile* value does nothing for this formatter.
|
|
||||||
def end_suite(report : Report, profile : Profile?)
|
|
||||||
test_suites_block(report)
|
|
||||||
@xml.end_document
|
|
||||||
@xml.flush
|
|
||||||
@io.close
|
|
||||||
end
|
|
||||||
|
|
||||||
# Called before a test starts.
|
|
||||||
def start_example(example : Example)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Called when a test finishes.
|
|
||||||
# The result of the test is provided by *example*.
|
|
||||||
def end_example(example : Example)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Creates the "testsuites" block in the XML.
|
|
||||||
private def test_suites_block(report)
|
|
||||||
@xml.element("testsuites",
|
|
||||||
tests: report.example_count,
|
|
||||||
failures: report.failed_count,
|
|
||||||
errors: report.error_count,
|
|
||||||
time: report.runtime.total_seconds,
|
|
||||||
name: NAME) do
|
|
||||||
add_test_suites(report)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Adds all of the individual test suite blocks.
|
|
||||||
private def add_test_suites(report)
|
|
||||||
report.group_by(&.location.path).each do |path, examples|
|
|
||||||
JUnitTestSuite.new(path, examples).to_xml(@xml)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,49 +0,0 @@
|
||||||
module Spectator::Formatting
|
|
||||||
# Base type for all JUnit test case results.
|
|
||||||
private abstract class JUnitTestCase
|
|
||||||
# Creates the JUnit test case.
|
|
||||||
def initialize(@example : Example)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Produces the test case XML element.
|
|
||||||
def to_xml(xml : ::XML::Builder)
|
|
||||||
xml.element("testcase", **attributes) do
|
|
||||||
content(xml)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Attributes that go in the "testcase" XML element.
|
|
||||||
private def attributes
|
|
||||||
{
|
|
||||||
name: example,
|
|
||||||
status: status,
|
|
||||||
classname: classname,
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
# Result to pull values from.
|
|
||||||
private abstract def result
|
|
||||||
|
|
||||||
# Status string specific to the result type.
|
|
||||||
private abstract def status : String
|
|
||||||
|
|
||||||
# Example for this test case.
|
|
||||||
private getter example : Example
|
|
||||||
|
|
||||||
# Adds additional content to the "testcase" XML block.
|
|
||||||
# Override this to add more content.
|
|
||||||
private def content(xml)
|
|
||||||
# ...
|
|
||||||
end
|
|
||||||
|
|
||||||
# Java-ified class name created from the spec.
|
|
||||||
private def classname
|
|
||||||
path = example.location.path
|
|
||||||
file = File.basename(path)
|
|
||||||
ext = File.extname(file)
|
|
||||||
name = file[0...-(ext.size)]
|
|
||||||
dir = path[0...-(file.size + 1)]
|
|
||||||
{dir.gsub('/', '.').underscore, name.camelcase}.join('.')
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,73 +0,0 @@
|
||||||
module Spectator::Formatting
|
|
||||||
# Mapping of a single spec file into a JUnit test suite.
|
|
||||||
private struct JUnitTestSuite
|
|
||||||
# Creates the JUnit test suite.
|
|
||||||
# The *path* should be the file that all results are from.
|
|
||||||
# The *examples* is a subset of all examples that share the path.
|
|
||||||
def initialize(@path : String, examples : Array(Example))
|
|
||||||
@report = Report.new(examples)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Generates the XML for the test suite (and all nested test cases).
|
|
||||||
def to_xml(xml : ::XML::Builder)
|
|
||||||
xml.element("testsuite",
|
|
||||||
tests: @report.example_count,
|
|
||||||
failures: @report.failed_count,
|
|
||||||
errors: @report.error_count,
|
|
||||||
skipped: @report.pending_count,
|
|
||||||
time: @report.runtime.total_seconds,
|
|
||||||
name: name,
|
|
||||||
package: package) do
|
|
||||||
add_test_cases(xml)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Adds the test case elements to the XML.
|
|
||||||
private def add_test_cases(xml)
|
|
||||||
@report.each do |example|
|
|
||||||
test_case = example.result.accept(JUnitTestCaseSelector) { example }
|
|
||||||
test_case.to_xml(xml)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Java-ified name of the test suite.
|
|
||||||
private def name
|
|
||||||
file = File.basename(@path)
|
|
||||||
ext = File.extname(file)
|
|
||||||
name = file[0...-(ext.size)]
|
|
||||||
name.camelcase
|
|
||||||
end
|
|
||||||
|
|
||||||
# Java-ified package (path) of the test suite.
|
|
||||||
private def package
|
|
||||||
file = File.basename(@path)
|
|
||||||
dir = @path[0...-(file.size + 1)]
|
|
||||||
dir.gsub('/', '.').underscore
|
|
||||||
end
|
|
||||||
|
|
||||||
# Selector for creating a JUnit test case based on a result.
|
|
||||||
private module JUnitTestCaseSelector
|
|
||||||
extend self
|
|
||||||
|
|
||||||
# Creates a successful JUnit test case.
|
|
||||||
def pass(example)
|
|
||||||
SuccessfulJUnitTestCase.new(example)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Creates a failure JUnit test case.
|
|
||||||
def failure(example)
|
|
||||||
FailureJUnitTestCase.new(example)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Creates an error JUnit test case.
|
|
||||||
def error(example)
|
|
||||||
ErrorJUnitTestCase.new(example)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Creates a skipped JUnit test case.
|
|
||||||
def pending(example)
|
|
||||||
SkippedJUnitTestCase.new(example)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,15 +0,0 @@
|
||||||
module Spectator::Formatting
|
|
||||||
# Produces a stringified message with a prefix.
|
|
||||||
private struct LabeledText(T)
|
|
||||||
# Creates the labeled text.
|
|
||||||
def initialize(@label : String, @text : T)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Appends the message to the output.
|
|
||||||
def to_s(io)
|
|
||||||
io << @label
|
|
||||||
io << ": "
|
|
||||||
io << @text
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,16 +0,0 @@
|
||||||
module Spectator::Formatting
|
|
||||||
# Produces the timing line in a profile block.
|
|
||||||
# This contains the length of time, and the example's location.
|
|
||||||
private struct LocationTiming
|
|
||||||
# Creates the location timing line.
|
|
||||||
def initialize(@span : Time::Span, @location : Location)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Appends the location timing information to the output.
|
|
||||||
def to_s(io)
|
|
||||||
io << HumanTime.new(@span).colorize.bold
|
|
||||||
io << ' '
|
|
||||||
io << @location
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,16 +0,0 @@
|
||||||
module Spectator::Formatting
|
|
||||||
# A single labeled value from the `Spectator::Matchers::MatchData#value` method.
|
|
||||||
private struct MatchDataValuePair
|
|
||||||
# Creates the pair formatter.
|
|
||||||
def initialize(@key : Symbol, @value : String, @padding : Int32)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Appends the pair to the output.
|
|
||||||
def to_s(io)
|
|
||||||
@padding.times { io << ' ' }
|
|
||||||
io << @key
|
|
||||||
io << ": "
|
|
||||||
io << @value
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,24 +0,0 @@
|
||||||
module Spectator::Formatting
|
|
||||||
# Produces a `MatchDataValuePair` for each key-value pair
|
|
||||||
# from `Spectator::Matchers::MatchData#values`.
|
|
||||||
private struct MatchDataValues
|
|
||||||
include Enumerable(Tuple(Symbol, String))
|
|
||||||
|
|
||||||
@max_key_length : Int32
|
|
||||||
|
|
||||||
# Creates the values mapper.
|
|
||||||
def initialize(@values : Array(Tuple(Symbol, String)))
|
|
||||||
@max_key_length = @values.map(&.first.to_s.size).max
|
|
||||||
end
|
|
||||||
|
|
||||||
# Yields pairs that can be printed to output.
|
|
||||||
def each
|
|
||||||
@values.each do |labeled_value|
|
|
||||||
key = labeled_value.first
|
|
||||||
key_length = key.to_s.size
|
|
||||||
padding = @max_key_length - key_length
|
|
||||||
yield MatchDataValuePair.new(key, labeled_value.last, padding)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,16 +0,0 @@
|
||||||
module Spectator::Formatting
|
|
||||||
# Produces a stringified value with a numerical prefix.
|
|
||||||
private struct NumberedItem(T)
|
|
||||||
# Creates the numbered item.
|
|
||||||
def initialize(@number : Int32, @text : T)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Appends the numbered item to the output.
|
|
||||||
def to_s(io)
|
|
||||||
io << @number
|
|
||||||
io << ')'
|
|
||||||
io << ' '
|
|
||||||
io << @text
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,28 +0,0 @@
|
||||||
module Spectator::Formatting
|
|
||||||
# Contents of a profile block.
|
|
||||||
private struct ProfileBlock
|
|
||||||
# Creates the block.
|
|
||||||
def initialize(@profile : Profile)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Appends the block to the output.
|
|
||||||
def to_s(io)
|
|
||||||
io.puts(ProfileSummary.new(@profile))
|
|
||||||
|
|
||||||
indent = Indent.new(io)
|
|
||||||
indent.increase do
|
|
||||||
@profile.each do |example|
|
|
||||||
entry(indent, example)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Adds a result entry to the output.
|
|
||||||
private def entry(indent, example)
|
|
||||||
indent.line(example)
|
|
||||||
indent.increase do
|
|
||||||
indent.line(LocationTiming.new(example.result.elapsed, example.location))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,29 +0,0 @@
|
||||||
module Spectator::Formatting
|
|
||||||
# Top line of a profile block which gives a summary.
|
|
||||||
private struct ProfileSummary
|
|
||||||
# Creates the summary line.
|
|
||||||
def initialize(@profile : Profile)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Appends the summary to the output.
|
|
||||||
def to_s(io)
|
|
||||||
io << "Top "
|
|
||||||
io << @profile.size
|
|
||||||
io << " slowest examples ("
|
|
||||||
io << human_time
|
|
||||||
io << ", "
|
|
||||||
io.printf("%.2f", percentage)
|
|
||||||
io << "% of total time):"
|
|
||||||
end
|
|
||||||
|
|
||||||
# Creates a human-friendly string for the total time.
|
|
||||||
private def human_time
|
|
||||||
HumanTime.new(@profile.total_time)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Percentage (0 to 100) of total time.
|
|
||||||
private def percentage
|
|
||||||
@profile.percentage * 100
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,14 +0,0 @@
|
||||||
module Spectator::Formatting
|
|
||||||
# Text displayed when using a random seed.
|
|
||||||
private struct RandomSeedText
|
|
||||||
# Creates the text object.
|
|
||||||
def initialize(@seed : UInt64)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Appends the command to the output.
|
|
||||||
def to_s(io)
|
|
||||||
io << "Randomized with seed "
|
|
||||||
io << @seed
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,15 +0,0 @@
|
||||||
module Spectator::Formatting
|
|
||||||
# Text displayed when fail-fast is enabled and tests were skipped.
|
|
||||||
private struct RemainingText
|
|
||||||
# Creates the text object.
|
|
||||||
def initialize(@count : Int32)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Appends the command to the output.
|
|
||||||
def to_s(io)
|
|
||||||
io << "Text execution aborted (fail-fast) - "
|
|
||||||
io << @count
|
|
||||||
io << " examples were omitted."
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,24 +0,0 @@
|
||||||
module Spectator::Formatting
|
|
||||||
# Produces a stringified time span for the runtime.
|
|
||||||
private struct Runtime
|
|
||||||
# Creates the runtime instance.
|
|
||||||
def initialize(@runtime : Time::Span)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Appends the runtime to the output.
|
|
||||||
# The text will be formatted as follows,
|
|
||||||
# depending on the length of time:
|
|
||||||
# ```text
|
|
||||||
# Finished in ## microseconds
|
|
||||||
# Finished in ## milliseconds
|
|
||||||
# Finished in ## seconds
|
|
||||||
# Finished in #:##
|
|
||||||
# Finished in #:##:##
|
|
||||||
# Finished in # days #:##:##
|
|
||||||
# ```
|
|
||||||
def to_s(io)
|
|
||||||
io << "Finished in "
|
|
||||||
io << HumanTime.new(@runtime)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,27 +0,0 @@
|
||||||
module Spectator::Formatting
|
|
||||||
# Formatter that outputs nothing.
|
|
||||||
# Useful for testing and larger automated processes.
|
|
||||||
class SilentFormatter < Formatter
|
|
||||||
# Called when a test suite is starting to execute.
|
|
||||||
def start_suite(suite : TestSuite)
|
|
||||||
# ... crickets ...
|
|
||||||
end
|
|
||||||
|
|
||||||
# Called when a test suite finishes.
|
|
||||||
# The results from the entire suite are provided.
|
|
||||||
def end_suite(report : Report, profile : Profile?)
|
|
||||||
# ... crickets ...
|
|
||||||
end
|
|
||||||
|
|
||||||
# Called before a test starts.
|
|
||||||
def start_example(example : Example)
|
|
||||||
# ... crickets ...
|
|
||||||
end
|
|
||||||
|
|
||||||
# Called when a test finishes.
|
|
||||||
# The result of the test is provided by *example*.
|
|
||||||
def end_example(example : Example)
|
|
||||||
# ... crickets ...
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,26 +0,0 @@
|
||||||
require "./junit_test_case"
|
|
||||||
|
|
||||||
module Spectator::Formatting
|
|
||||||
# JUnit test case for a pending result.
|
|
||||||
private class SkippedJUnitTestCase < JUnitTestCase
|
|
||||||
# Result for this test case.
|
|
||||||
private getter result
|
|
||||||
|
|
||||||
# Creates the JUnit test case.
|
|
||||||
def initialize(example : Example)
|
|
||||||
super
|
|
||||||
@result = example.result.as(PendingResult)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Status string specific to the result type.
|
|
||||||
private def status : String
|
|
||||||
"TODO"
|
|
||||||
end
|
|
||||||
|
|
||||||
# Adds the skipped tag to the XML block.
|
|
||||||
private def content(xml)
|
|
||||||
super
|
|
||||||
xml.element("skipped")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,50 +0,0 @@
|
||||||
module Spectator::Formatting
|
|
||||||
# Produces a stringified stats counter from result totals.
|
|
||||||
private struct StatsCounter
|
|
||||||
# Creates the instance with each of the counters.
|
|
||||||
private def initialize(@examples : Int32, @failures : Int32, @errors : Int32, @pending : Int32, @failed : Bool)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Creates the instance from the counters in a report.
|
|
||||||
def initialize(report)
|
|
||||||
initialize(report.example_count, report.failed_count, report.error_count, report.pending_count, report.failed?)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Produces a colorized formatting for the stats,
|
|
||||||
# depending on the number of each type of result.
|
|
||||||
def color
|
|
||||||
if @errors > 0
|
|
||||||
Color.error(self)
|
|
||||||
elsif @failed || @failures > 0
|
|
||||||
Color.failure(self)
|
|
||||||
elsif @pending > 0
|
|
||||||
Color.pending(self)
|
|
||||||
else
|
|
||||||
Color.pass(self)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Appends the counters to the output.
|
|
||||||
# The format will be:
|
|
||||||
# ```text
|
|
||||||
# # examples, # failures, # errors, # pending
|
|
||||||
# ```
|
|
||||||
def to_s(io)
|
|
||||||
stats.each_with_index do |stat, value, index|
|
|
||||||
io << ", " if index > 0
|
|
||||||
io << value
|
|
||||||
io << ' '
|
|
||||||
io << stat
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
private def stats
|
|
||||||
{
|
|
||||||
examples: @examples,
|
|
||||||
failures: @failures,
|
|
||||||
errors: @errors,
|
|
||||||
pending: @pending,
|
|
||||||
}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,18 +0,0 @@
|
||||||
module Spectator::Formatting
|
|
||||||
# JUnit test case for a successful result.
|
|
||||||
private class SuccessfulJUnitTestCase < FinishedJUnitTestCase
|
|
||||||
# Result for this test case.
|
|
||||||
private getter result
|
|
||||||
|
|
||||||
# Creates the JUnit test case.
|
|
||||||
def initialize(example : Example)
|
|
||||||
super
|
|
||||||
@result = example.result.as(PassResult)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Status string specific to the result type.
|
|
||||||
private def status : String
|
|
||||||
"PASS"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,78 +0,0 @@
|
||||||
module Spectator::Formatting
|
|
||||||
# Mix-in for producing a human-readable summary of a test suite.
|
|
||||||
module SuiteSummary
|
|
||||||
# Does nothing when starting a test suite.
|
|
||||||
def start_suite(suite)
|
|
||||||
# ...
|
|
||||||
end
|
|
||||||
|
|
||||||
# Produces the summary of test suite from a report.
|
|
||||||
# A block describing each failure is displayed.
|
|
||||||
# At the end, the totals and runtime are printed.
|
|
||||||
# The *profile* value is not nil when profiling results should be displayed.
|
|
||||||
def end_suite(report, profile : Profile?)
|
|
||||||
if report.example_count > 0
|
|
||||||
@io.puts if is_a?(DotsFormatter)
|
|
||||||
@io.puts
|
|
||||||
end
|
|
||||||
failures(report.failures) if report.failed_count > 0
|
|
||||||
profile(profile) if profile
|
|
||||||
stats(report)
|
|
||||||
remaining(report) if report.remaining?
|
|
||||||
if report.failed?
|
|
||||||
if report.examples_ran > 0
|
|
||||||
failure_commands(report.failures)
|
|
||||||
else
|
|
||||||
@io.puts Color.failure("Failing because no tests were run (fail-blank)")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Produces the failure section of the summary.
|
|
||||||
# This has a "Failures" title followed by a block for each failure.
|
|
||||||
private def failures(failures)
|
|
||||||
@io.puts "Failures:"
|
|
||||||
@io.puts
|
|
||||||
failures.each_with_index do |example, index|
|
|
||||||
@io.puts FailureBlock.new(index + 1, example)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Produces the profiling section of the summary.
|
|
||||||
private def profile(profile)
|
|
||||||
@io.puts ProfileBlock.new(profile)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Produces the statistical section of the summary.
|
|
||||||
# This contains how long the suite took to run
|
|
||||||
# and the counts for the results (total, failures, errors, and pending).
|
|
||||||
private def stats(report)
|
|
||||||
@io.puts Runtime.new(report.runtime)
|
|
||||||
@io.puts StatsCounter.new(report).color
|
|
||||||
if (seed = report.random_seed?)
|
|
||||||
@io.puts
|
|
||||||
@io.puts RandomSeedText.new(seed)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Produces the skipped tests text if fail-fast is enabled and tests were omitted.
|
|
||||||
private def remaining(report)
|
|
||||||
text = RemainingText.new(report.remaining_count)
|
|
||||||
@io.puts Color.failure(text)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Produces the failure commands section of the summary.
|
|
||||||
# This provides a set of commands the user can run
|
|
||||||
# to test just the examples that failed.
|
|
||||||
private def failure_commands(failures)
|
|
||||||
@io.puts
|
|
||||||
@io.puts "Failed examples:"
|
|
||||||
@io.puts
|
|
||||||
failures.each do |example|
|
|
||||||
@io << FailureCommand.color(example)
|
|
||||||
@io << ' '
|
|
||||||
@io.puts Comment.color(example)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,58 +0,0 @@
|
||||||
module Spectator::Formatting
|
|
||||||
# Formatter for the "Test Anything Protocol".
|
|
||||||
# For details, see: https://testanything.org/
|
|
||||||
class TAPFormatter < Formatter
|
|
||||||
# Creates the formatter.
|
|
||||||
# By default, output is sent to STDOUT.
|
|
||||||
def initialize(@io : IO = STDOUT)
|
|
||||||
@index = 1
|
|
||||||
end
|
|
||||||
|
|
||||||
# Called when a test suite is starting to execute.
|
|
||||||
def start_suite(suite : TestSuite)
|
|
||||||
@io << "1.."
|
|
||||||
@io.puts suite.size
|
|
||||||
end
|
|
||||||
|
|
||||||
# Called when a test suite finishes.
|
|
||||||
# The results from the entire suite are provided.
|
|
||||||
# The *profile* value is not nil when profiling results should be displayed.
|
|
||||||
def end_suite(report : Report, profile : Profile?)
|
|
||||||
@io.puts "Bail out!" if report.remaining?
|
|
||||||
profile(profile) if profile
|
|
||||||
end
|
|
||||||
|
|
||||||
# Called before a test starts.
|
|
||||||
def start_example(example : Example)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Called when a test finishes.
|
|
||||||
# The result of the test is provided by *example*.
|
|
||||||
def end_example(example : Example)
|
|
||||||
@io.puts TAPTestLine.new(@index, example)
|
|
||||||
@index += 1
|
|
||||||
end
|
|
||||||
|
|
||||||
# Displays profiling information.
|
|
||||||
private def profile(profile)
|
|
||||||
@io.puts(Comment.new(ProfileSummary.new(profile)))
|
|
||||||
|
|
||||||
indent = Indent.new(@io)
|
|
||||||
indent.increase do
|
|
||||||
profile.each do |example|
|
|
||||||
profile_entry(indent, example)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Adds a profile result entry to the output.
|
|
||||||
private def profile_entry(indent, example)
|
|
||||||
@io << "# "
|
|
||||||
indent.line(example)
|
|
||||||
indent.increase do
|
|
||||||
@io << "# "
|
|
||||||
indent.line(LocationTiming.new(example.result.elapsed, example.location))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,33 +0,0 @@
|
||||||
module Spectator::Formatting
|
|
||||||
# Produces a formatted TAP test line.
|
|
||||||
private struct TAPTestLine
|
|
||||||
# Creates the test line.
|
|
||||||
def initialize(@index : Int32, @example : Example)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Appends the line to the output.
|
|
||||||
def to_s(io)
|
|
||||||
io << status
|
|
||||||
io << ' '
|
|
||||||
io << @index
|
|
||||||
io << " - "
|
|
||||||
io << @example
|
|
||||||
io << " # skip" if pending?
|
|
||||||
end
|
|
||||||
|
|
||||||
# The text "ok" or "not ok" depending on the result.
|
|
||||||
private def status
|
|
||||||
result.is_a?(FailResult) ? "not ok" : "ok"
|
|
||||||
end
|
|
||||||
|
|
||||||
# The result of running the example.
|
|
||||||
private def result
|
|
||||||
@example.result
|
|
||||||
end
|
|
||||||
|
|
||||||
# Indicates whether this test was skipped.
|
|
||||||
private def pending?
|
|
||||||
result.is_a?(PendingResult)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
Loading…
Add table
Add a link
Reference in a new issue