-
-
Notifications
You must be signed in to change notification settings - Fork 763
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implement dumper/parser for example statuses.
- Loading branch information
1 parent
bd774ef
commit 0529b6e
Showing
2 changed files
with
191 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
module RSpec | ||
module Core | ||
class ExampleStatusPersister | ||
end | ||
|
||
# Dumps a list of hashes in a pretty, human readable format | ||
# for later parsing. The hashes are expected to have symbol | ||
# keys and string values, and each hash should have the same | ||
# set of keys. | ||
# @private | ||
class ExampleStatusDumper | ||
def self.dump(examples) | ||
new(examples).dump | ||
end | ||
|
||
def initialize(examples) | ||
@examples = examples | ||
end | ||
|
||
def dump | ||
return nil if @examples.empty? | ||
(formatted_header_rows + formatted_value_rows).join("\n") << "\n" | ||
end | ||
|
||
private | ||
|
||
def formatted_header_rows | ||
@formatted_header_rows ||= begin | ||
dividers = column_widths.map { |w| "-" * w } | ||
[formatted_row_from(headers.map(&:to_s)), formatted_row_from(dividers)] | ||
end | ||
end | ||
|
||
def formatted_value_rows | ||
@foramtted_value_rows ||= rows.map do |row| | ||
formatted_row_from(row) | ||
end | ||
end | ||
|
||
def rows | ||
@rows ||= @examples.map { |ex| ex.values_at(*headers) } | ||
end | ||
|
||
def formatted_row_from(row_values) | ||
padded_values = row_values.each_with_index.map do |value, index| | ||
value.ljust(column_widths[index]) | ||
end | ||
|
||
padded_values.join(" | ") << " |" | ||
end | ||
|
||
def headers | ||
@headers ||= @examples.first.keys | ||
end | ||
|
||
def column_widths | ||
@column_widths ||= begin | ||
value_sets = rows.transpose | ||
|
||
headers.each_with_index.map do |header, index| | ||
values = value_sets[index] << header.to_s | ||
values.map(&:length).max | ||
end | ||
end | ||
end | ||
end | ||
|
||
# Parses a string that has been previously dumped by ExampleStatusDumper. | ||
# Note that this parser is a bit naive in that it does a simple split on | ||
# "\n" and " | ", with no concern for handling escaping. For now, that's | ||
# OK because the values we plan to persist (example id, status, and perhaps | ||
# example duration) are highly unlikely to contain "\n" or " | " -- after | ||
# all, who puts those in file names? | ||
# @private | ||
class ExampleStatusParser | ||
def self.parse(string) | ||
new(string).parse | ||
end | ||
|
||
def initialize(string) | ||
@header_line, _, *@row_lines = string.lines.to_a | ||
end | ||
|
||
def parse | ||
@row_lines.map { |line| parse_row(line) } | ||
end | ||
|
||
private | ||
|
||
def parse_row(line) | ||
Hash[headers.zip(split_line(line))] | ||
end | ||
|
||
def headers | ||
@headers ||= split_line(@header_line).map(&:to_sym) | ||
end | ||
|
||
def split_line(line) | ||
line.split(/\s+\|\s+/) | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
require 'rspec/core/example_status_persister' | ||
|
||
module RSpec::Core | ||
RSpec.describe "Example status serialization" do | ||
it 'serializes the provided example statuses in a human readable format' do | ||
examples = [ | ||
{ :example_id => "./spec/unit/foo_spec.rb[1:1]", :status => 'passed' }, | ||
{ :example_id => "./spec/unit/foo_spec.rb[1:2]", :status => 'pending' }, | ||
{ :example_id => "./spec/integration/foo_spec.rb[1:2]", :status => 'failed' } | ||
] | ||
|
||
produce_expected_output = eq(unindent(<<-EOS)) | ||
example_id | status | | ||
----------------------------------- | ------- | | ||
./spec/unit/foo_spec.rb[1:1] | passed | | ||
./spec/unit/foo_spec.rb[1:2] | pending | | ||
./spec/integration/foo_spec.rb[1:2] | failed | | ||
EOS | ||
|
||
if RUBY_VERSION == '1.8.7' # unordered hashes :(. | ||
produce_expected_output |= eq(unindent(<<-EOS)) | ||
status | example_id | | ||
------- | ----------------------------------- | | ||
passed | ./spec/unit/foo_spec.rb[1:1] | | ||
pending | ./spec/unit/foo_spec.rb[1:2] | | ||
failed | ./spec/integration/foo_spec.rb[1:2] | | ||
EOS | ||
end | ||
|
||
expect(dump(examples)).to produce_expected_output | ||
end | ||
|
||
it 'takes the column headers into account when sizing the columns' do | ||
examples = [ | ||
{ :long_key => '12', :a => '20' }, | ||
{ :long_key => '120', :a => '2' } | ||
] | ||
|
||
produce_expected_output = eq(unindent(<<-EOS)) | ||
long_key | a | | ||
-------- | -- | | ||
12 | 20 | | ||
120 | 2 | | ||
EOS | ||
|
||
if RUBY_VERSION == '1.8.7' # unordered hashes :(. | ||
produce_expected_output |= eq(unindent(<<-EOS)) | ||
a | long_key | | ||
-- | -------- | | ||
20 | 12 | | ||
2 | 120 | | ||
EOS | ||
end | ||
|
||
expect(dump(examples)).to produce_expected_output | ||
end | ||
|
||
it 'can round trip through the dumper and parser' do | ||
examples = [ | ||
{ :example_id => "./spec/unit/foo_spec.rb[1:1]", :status => 'passed' }, | ||
{ :example_id => "./spec/unit/foo_spec.rb[1:2]", :status => 'pending' }, | ||
{ :example_id => "./spec/integration/foo_spec.rb[1:2]", :status => 'failed' } | ||
] | ||
|
||
round_tripped = parse(dump(examples)) | ||
expect(round_tripped).to eq(examples) | ||
end | ||
|
||
it 'produces nothing when given nothing' do | ||
expect(dump([])).to eq(nil) | ||
end | ||
|
||
# Intended for use with indented heredocs. | ||
# taken from Ruby Tapas: | ||
# https://rubytapas.dpdcart.com/subscriber/post?id=616#files | ||
def unindent(s) | ||
s.gsub(/^#{s.scan(/^[ \t]+(?=\S)/).min}/, "") | ||
end | ||
|
||
def dump(examples) | ||
ExampleStatusDumper.dump(examples) | ||
end | ||
|
||
def parse(string) | ||
ExampleStatusParser.parse(string) | ||
end | ||
end | ||
end |