From 877831a98b2bbd555adb39a1ad310bd816a508cf Mon Sep 17 00:00:00 2001 From: Michael Miller Date: Sun, 30 May 2021 10:17:49 -0600 Subject: [PATCH] Add docs --- src/spectator/formatting/components/block.cr | 12 ++++++++ .../formatting/components/comment.cr | 5 ++++ .../components/error_result_block.cr | 18 ++++++++--- .../formatting/components/example_command.cr | 4 +++ .../components/fail_result_block.cr | 5 ++++ .../components/failure_command_list.cr | 5 ++++ .../components/pending_result_block.cr | 6 ++++ .../formatting/components/profile.cr | 4 +++ .../formatting/components/result_block.cr | 30 +++++++++++++++++++ src/spectator/formatting/components/stats.cr | 6 ++++ .../formatting/components/tap_profile.cr | 5 ++++ src/spectator/formatting/components/totals.cr | 8 +++++ 12 files changed, 104 insertions(+), 4 deletions(-) diff --git a/src/spectator/formatting/components/block.cr b/src/spectator/formatting/components/block.cr index 19286c8..40cd5a8 100644 --- a/src/spectator/formatting/components/block.cr +++ b/src/spectator/formatting/components/block.cr @@ -1,16 +1,28 @@ module Spectator::Formatting::Components + # Base type for handling indented output. + # Indents are tracked and automatically printed. + # Use `#indent` to increase the indent for the duration of a block. + # Use `#line` to produce a line with an indentation prefixing it. abstract struct Block + # Default indent amount. private INDENT = 2 + # Creates the block. + # A default *indent* size can be specified. def initialize(*, @indent : Int32 = INDENT) end + # Increases the indent by the a specific *amount* for the duration of the block. private def indent(amount = INDENT) @indent += amount yield @indent -= amount end + # Produces a line of output with an indent before it. + # The contents of the line should be generated by a block provided to this method. + # Ensure that _only_ one line is produced by the block, + # otherwise the indent will be lost. private def line(io) @indent.times { io << ' ' } yield diff --git a/src/spectator/formatting/components/comment.cr b/src/spectator/formatting/components/comment.cr index 7405c6e..3ae5341 100644 --- a/src/spectator/formatting/components/comment.cr +++ b/src/spectator/formatting/components/comment.cr @@ -1,16 +1,21 @@ require "colorize" module Spectator::Formatting::Components + # Object that can be stringified pre-pended with a comment mark (#). struct Comment(T) + # Default color for a comment. private COLOR = :cyan + # Creates a comment with the specified content. def initialize(@content : T) end + # Creates a colored comment. def self.colorize(content) new(content).colorize(COLOR) end + # Writes the comment to the output. def to_s(io) io << '#' io << ' ' diff --git a/src/spectator/formatting/components/error_result_block.cr b/src/spectator/formatting/components/error_result_block.cr index 6ebbab5..bbd348c 100644 --- a/src/spectator/formatting/components/error_result_block.cr +++ b/src/spectator/formatting/components/error_result_block.cr @@ -4,41 +4,51 @@ require "../../error_result" require "./result_block" module Spectator::Formatting::Components + # Displays information about an error result. struct ErrorResultBlock < ResultBlock + # Creates the component. def initialize(index : Int32, example : Example, @result : ErrorResult) super(index, example) end + # Content displayed on the second line of the block after the label. private def subtitle @result.error.message.try(&.each_line.first) end + # Prefix for the second line of the block. private def subtitle_label "Error: ".colorize(:red) end + # Display error information. private def content(io) + # Fetch the error and message. + # If there's no message error = @result.error lines = error.message.try(&.lines) || {"".colorize(:purple)} + # Display the error type and first line of the message. line(io) do io << "#{error.class}: ".colorize(:red) io << lines.first end + # Display additional lines after the first if there's any. lines.skip(1).each do |entry| - line(io) do - io << entry - end + line(io) { io << entry } end - error.backtrace?.try do |backtrace| + # Display the backtrace if it's available. + if backtrace = error.backtrace? indent { write_backtrace(io, backtrace) } end end + # Writes the backtrace entries to the output. private def write_backtrace(io, backtrace) backtrace.each do |entry| + # Dim entries that are outside the shard. entry = entry.colorize.dim unless entry.starts_with?(/(src|spec)\//) line(io) { io << entry } end diff --git a/src/spectator/formatting/components/example_command.cr b/src/spectator/formatting/components/example_command.cr index 648d8ea..5dd4c05 100644 --- a/src/spectator/formatting/components/example_command.cr +++ b/src/spectator/formatting/components/example_command.cr @@ -1,10 +1,14 @@ +require "../../example" require "./comment" module Spectator::Formatting::Components + # Provides syntax for running a specific example from the command-line. struct ExampleCommand + # Creates the component with the specified example. def initialize(@example : Example) end + # Produces output for running the previously specified example. def to_s(io) io << "crystal spec " io << @example.location diff --git a/src/spectator/formatting/components/fail_result_block.cr b/src/spectator/formatting/components/fail_result_block.cr index 1b22c6a..dc5780d 100644 --- a/src/spectator/formatting/components/fail_result_block.cr +++ b/src/spectator/formatting/components/fail_result_block.cr @@ -4,19 +4,24 @@ require "../../fail_result" require "./result_block" module Spectator::Formatting::Components + # Displays information about a fail result. struct FailResultBlock < ResultBlock + # Creates the component. def initialize(index : Int32, example : Example, @result : FailResult) super(index, example) end + # Content displayed on the second line of the block after the label. private def subtitle @result.error.message.try(&.each_line.first) end + # Prefix for the second line of the block. private def subtitle_label "Failure: ".colorize(:red) end + # Display expectation match data. private def content(io) # TODO: Display match data. end diff --git a/src/spectator/formatting/components/failure_command_list.cr b/src/spectator/formatting/components/failure_command_list.cr index c6f247b..7da5ed2 100644 --- a/src/spectator/formatting/components/failure_command_list.cr +++ b/src/spectator/formatting/components/failure_command_list.cr @@ -1,10 +1,15 @@ +require "../../example" require "./example_command" module Spectator::Formatting::Components + # Produces a list of commands to run failed examples. struct FailureCommandList + # Creates the component. + # Requires a set of *failures* to display commands for. def initialize(@failures : Enumerable(Example)) end + # Produces the list of commands to run failed examples. def to_s(io) io.puts "Failed examples:" io.puts diff --git a/src/spectator/formatting/components/pending_result_block.cr b/src/spectator/formatting/components/pending_result_block.cr index 389eda3..12ee2a1 100644 --- a/src/spectator/formatting/components/pending_result_block.cr +++ b/src/spectator/formatting/components/pending_result_block.cr @@ -1,22 +1,28 @@ +require "colorize" require "../../example" require "../../pending_result" require "./result_block" module Spectator::Formatting::Components + # Displays information about a pending result. struct PendingResultBlock < ResultBlock + # Creates the component. def initialize(index : Int32, example : Example, @result : PendingResult) super(index, example) end + # Content displayed on the second line of the block after the label. private def subtitle "No reason given" # TODO: Get reason from result. end + # Prefix for the second line of the block. private def subtitle_label # TODO: Could be pending or skipped. "Pending: ".colorize(:yellow) end + # No content for this type of block. private def content(io) end end diff --git a/src/spectator/formatting/components/profile.cr b/src/spectator/formatting/components/profile.cr index d38210c..7e35b30 100644 --- a/src/spectator/formatting/components/profile.cr +++ b/src/spectator/formatting/components/profile.cr @@ -2,10 +2,13 @@ require "../../profile" require "./runtime" module Spectator::Formatting::Components + # Displays profiling information for slow examples. struct Profile + # Creates the component with the specified *profile*. def initialize(@profile : Spectator::Profile) end + # Produces the output containing the profiling information. def to_s(io) io << "Top " io << @profile.size @@ -20,6 +23,7 @@ module Spectator::Formatting::Components end end + # Writes a single example's timing to the output. private def example_profile(io, example) io << " " io.puts example diff --git a/src/spectator/formatting/components/result_block.cr b/src/spectator/formatting/components/result_block.cr index 2025441..fd5eb5f 100644 --- a/src/spectator/formatting/components/result_block.cr +++ b/src/spectator/formatting/components/result_block.cr @@ -3,21 +3,47 @@ require "./block" require "./comment" module Spectator::Formatting::Components + # Base class that displayed indexed results in block form. + # These typically take the form: + # ```text + # 1) Title + # Label: Subtitle + # + # Content + # # Source + # ``` abstract struct ResultBlock < Block + # Creates the block with the specified *index* and for the given *example*. def initialize(@index : Int32, @example : Example) super() end + # Content displayed on the first line of the block. + # Will be stringified. + # By default, uses the example name. + # Can be overridden to use a different value. private def title @example end + # Content displayed on the second line of the block after the label. + # Will be stringified. private abstract def subtitle + # Prefix for the second line of the block. + # Will be stringified. + # This is typically something like "Error:" or "Failure:" private abstract def subtitle_label + # Produces the main content of the block. + # *io* is the stream to write to. + # `#line` and `#indent` (from `Block`) should be used to maintain spacing. + private abstract def content(io) + + # Writes the component's output to the specified stream. def to_s(io) title_line(io) + # Ident over to align with the spacing used by the index. indent(index_digit_count + 2) do subtitle_line(io) io.puts @@ -26,6 +52,7 @@ module Spectator::Formatting::Components end end + # Produces the title line. private def title_line(io) line(io) do io << @index @@ -35,6 +62,7 @@ module Spectator::Formatting::Components end end + # Produces the subtitle line. private def subtitle_line(io) line(io) do io << subtitle_label @@ -42,6 +70,7 @@ module Spectator::Formatting::Components end end + # Produces the (example) source line. private def source_line(io) source = if (result = @example.result).responds_to?(:source) result.source @@ -51,6 +80,7 @@ module Spectator::Formatting::Components line(io) { io << Comment.colorize(source) } end + # Computes the number of spaces the index takes private def index_digit_count (Math.log(@index.to_f + 1) / Math::LOG10).ceil.to_i end diff --git a/src/spectator/formatting/components/stats.cr b/src/spectator/formatting/components/stats.cr index cd5baea..ab76c7c 100644 --- a/src/spectator/formatting/components/stats.cr +++ b/src/spectator/formatting/components/stats.cr @@ -1,13 +1,16 @@ require "colorize" +require "../../report" require "./runtime" require "./totals" module Spectator::Formatting::Components # Statistics information displayed at the end of a run. struct Stats + # Creates the component with stats from *report*. def initialize(@report : Report) end + # Displays the stats. def to_s(io) runtime(io) totals(io) @@ -16,15 +19,18 @@ module Spectator::Formatting::Components end end + # Displays the time it took to run the suite. private def runtime(io) io << "Finished in " io.puts Runtime.new(@report.runtime) end + # Displays the counts for each type of result. private def totals(io) io.puts Totals.colorize(@report.counts) end + # Displays the random seed. private def random(io, seed) io.puts "Randomized with seed: #{seed}".colorize(:cyan) end diff --git a/src/spectator/formatting/components/tap_profile.cr b/src/spectator/formatting/components/tap_profile.cr index 6e3a558..8ae022f 100644 --- a/src/spectator/formatting/components/tap_profile.cr +++ b/src/spectator/formatting/components/tap_profile.cr @@ -2,10 +2,14 @@ require "../../profile" require "./runtime" module Spectator::Formatting::Components + # Displays profiling information for slow examples in a TAP format. + # Produces output similar to `Profile`, but formatted for TAP. struct TAPProfile + # Creates the component with the specified *profile*. def initialize(@profile : Spectator::Profile) end + # Produces the output containing the profiling information. def to_s(io) io << "# Top " io << @profile.size @@ -20,6 +24,7 @@ module Spectator::Formatting::Components end end + # Writes a single example's timing to the output. private def example_profile(io, example) io << "# " io.puts example diff --git a/src/spectator/formatting/components/totals.cr b/src/spectator/formatting/components/totals.cr index 06483e5..db4edab 100644 --- a/src/spectator/formatting/components/totals.cr +++ b/src/spectator/formatting/components/totals.cr @@ -1,10 +1,13 @@ require "colorize" module Spectator::Formatting::Components + # Displays counts for each type of example result (pass, fail, error, pending). struct Totals + # Creates the component with the specified counts. def initialize(@examples : Int32, @failures : Int32, @errors : Int32, @pending : Int32) end + # Creates the component by pulling numbers from *counts*. def initialize(counts) @examples = counts.run @failures = counts.fail @@ -12,6 +15,10 @@ module Spectator::Formatting::Components @pending = counts.pending end + # Creates the component, but colors it whether there were pending or failed results. + # The component will be red if there were failures (or errors), + # yellow if there were pending/skipped tests, + # and green if everything passed. def self.colorize(counts) totals = new(counts) if counts.fail > 0 @@ -23,6 +30,7 @@ module Spectator::Formatting::Components end end + # Writes the counts to the output. def to_s(io) io << @examples io << " examples, "