Merge branch 'master' into specs

This commit is contained in:
Michael Miller 2020-08-16 09:54:59 -06:00
commit ca03e75b99
No known key found for this signature in database
GPG key ID: FB9F12F7C646A4AD
13 changed files with 92 additions and 29 deletions

View file

@ -10,23 +10,25 @@ cache:
before_script: before_script:
- crystal -v # Print out Crystal version for debugging - crystal -v # Print out Crystal version for debugging
- shards
spec: spec:
script: script:
- crystal spec --error-on-warnings - shards
- crystal spec --error-on-warnings
style: style:
script: script:
- bin/ameba - shards
- crystal tool format --check - bin/ameba
- crystal tool format --check
nightly: nightly:
image: "crystallang/crystal:nightly" image: "crystallang/crystal:nightly"
allow_failure: true allow_failure: true
script: script:
- crystal spec --error-on-warnings - shards --ignore-crystal-version
- crystal tool format --check - crystal spec --error-on-warnings
- crystal tool format --check
pages: pages:
stage: deploy stage: deploy

View file

@ -1,5 +1,5 @@
name: spectator name: spectator
version: 0.9.20 version: 0.9.22
description: | description: |
A feature-rich spec testing framework for Crystal with similarities to RSpec. A feature-rich spec testing framework for Crystal with similarities to RSpec.
@ -13,4 +13,4 @@ license: MIT
development_dependencies: development_dependencies:
ameba: ameba:
github: crystal-ameba/ameba github: crystal-ameba/ameba
version: ~> 0.12.0 version: ~> 0.13.1

View file

@ -6,7 +6,7 @@ module Spectator
extend self extend self
# Current version of the Spectator library. # Current version of the Spectator library.
VERSION = "0.9.20" VERSION = "0.9.22"
# Top-level describe method. # Top-level describe method.
# All specs in a file must be wrapped in this call. # All specs in a file must be wrapped in this call.
@ -45,7 +45,7 @@ module Spectator
end end
end end
# ditto # :ditto:
macro context(description, &block) macro context(description, &block)
describe({{description}}) {{block}} describe({{description}}) {{block}}
end end

View file

@ -61,7 +61,7 @@ module Spectator
private def seed_option(parser, builder) private def seed_option(parser, builder)
parser.on("--seed INTEGER", "Set the seed for the random number generator (implies -r)") do |seed| parser.on("--seed INTEGER", "Set the seed for the random number generator (implies -r)") do |seed|
builder.randomize builder.randomize
builder.seed = seed.to_i builder.seed = seed.to_u64
end end
end end
@ -74,7 +74,7 @@ module Spectator
when /^rand/ when /^rand/
builder.randomize builder.randomize
parts = method.split(':', 2) parts = method.split(':', 2)
builder.seed = parts[1].to_i if parts.size > 1 builder.seed = parts[1].to_u64 if parts.size > 1
else else
nil nil
end end

View file

@ -19,6 +19,9 @@ module Spectator
# Indicates whether tests are run in a random order. # Indicates whether tests are run in a random order.
getter? randomize : Bool getter? randomize : Bool
# Random seed used for number generation.
getter! random_seed : UInt64?
# Indicates whether profiling information should be displayed. # Indicates whether profiling information should be displayed.
getter? profile : Bool getter? profile : Bool
@ -33,6 +36,7 @@ module Spectator
@dry_run = builder.dry_run? @dry_run = builder.dry_run?
@random = builder.random @random = builder.random
@randomize = builder.randomize? @randomize = builder.randomize?
@random_seed = builder.seed?
@profile = builder.profile? @profile = builder.profile?
@example_filter = builder.example_filter @example_filter = builder.example_filter
end end

View file

@ -11,6 +11,11 @@ module Spectator
# Random number generator to use. # Random number generator to use.
protected getter random = Random::DEFAULT protected getter random = Random::DEFAULT
def initialize
@seed = seed = @random.rand(UInt16).to_u64
@random.new_seed(seed)
end
@primary_formatter : Formatting::Formatter? @primary_formatter : Formatting::Formatter?
@additional_formatters = [] of Formatting::Formatter @additional_formatters = [] of Formatting::Formatter
@fail_fast = false @fail_fast = false
@ -90,8 +95,12 @@ module Spectator
@dry_run @dry_run
end end
# Seed used for random number generation.
getter! seed : UInt64?
# Sets the seed for the random number generator. # Sets the seed for the random number generator.
def seed=(seed) def seed=(seed)
@seed = seed
@random = Random.new(seed) @random = Random.new(seed)
end end

View file

@ -207,7 +207,7 @@ module Spectator
raise ExampleFailed.new(reason) raise ExampleFailed.new(reason)
end end
# ditto # :ditto:
@[AlwaysInline] @[AlwaysInline]
def fail def fail
fail("Example failed") fail("Example failed")

View file

@ -450,8 +450,13 @@ module Spectator
# expect(%i[a b c]).to contain(:a, :b) # expect(%i[a b c]).to contain(:a, :b)
# ``` # ```
macro contain(*expected) macro contain(*expected)
%test_value = ::Spectator::TestValue.new({{expected}}, {{expected.splat.stringify}}) {% if expected.id.starts_with?("{*") %}
::Spectator::Matchers::ContainMatcher.new(%test_value) %test_value = ::Spectator::TestValue.new({{expected.id[2...-1]}}, {{expected.splat.stringify}})
::Spectator::Matchers::ContainMatcher.new(%test_value)
{% else %}
%test_value = ::Spectator::TestValue.new({{expected}}, {{expected.splat.stringify}})
::Spectator::Matchers::ContainMatcher.new(%test_value)
{% end %}
end end
# Indicates that some range (or collection) should contain another value. # Indicates that some range (or collection) should contain another value.
@ -471,8 +476,13 @@ module Spectator
# expect(..100).to contain(0, 50) # expect(..100).to contain(0, 50)
# ``` # ```
macro cover(*expected) macro cover(*expected)
%test_value = ::Spectator::TestValue.new({{expected}}, {{expected.splat.stringify}}) {% if expected.id.starts_with?("{*") %}
::Spectator::Matchers::ContainMatcher.new(%test_value) %test_value = ::Spectator::TestValue.new({{expected.id[2...-1]}}, {{expected.splat.stringify}})
::Spectator::Matchers::ContainMatcher.new(%test_value)
{% else %}
%test_value = ::Spectator::TestValue.new({{expected}}, {{expected.splat.stringify}})
::Spectator::Matchers::ContainMatcher.new(%test_value)
{% end %}
end end
# Indicates that some value or set should contain another value. # Indicates that some value or set should contain another value.
@ -501,8 +511,13 @@ module Spectator
# expect(%w[FOO BAR BAZ]).to have(/foo/i, String) # expect(%w[FOO BAR BAZ]).to have(/foo/i, String)
# ``` # ```
macro have(*expected) macro have(*expected)
%test_value = ::Spectator::TestValue.new({{expected}}, {{expected.splat.stringify}}) {% if expected.id.starts_with?("{*") %}
::Spectator::Matchers::HaveMatcher.new(%test_value) %test_value = ::Spectator::TestValue.new({{expected.id[2...-1]}}, {{expected.splat.stringify}})
::Spectator::Matchers::HaveMatcher.new(%test_value)
{% else %}
%test_value = ::Spectator::TestValue.new({{expected}}, {{expected.splat.stringify}})
::Spectator::Matchers::HaveMatcher.new(%test_value)
{% end %}
end end
# Indicates that some set, such as a `Hash`, has a given key. # Indicates that some set, such as a `Hash`, has a given key.
@ -518,7 +533,7 @@ module Spectator
::Spectator::Matchers::HaveKeyMatcher.new(%test_value) ::Spectator::Matchers::HaveKeyMatcher.new(%test_value)
end end
# ditto # :ditto:
macro has_key(expected) macro has_key(expected)
have_key({{expected}}) have_key({{expected}})
end end
@ -536,7 +551,7 @@ module Spectator
::Spectator::Matchers::HaveValueMatcher.new(%test_value) ::Spectator::Matchers::HaveValueMatcher.new(%test_value)
end end
# ditto # :ditto:
macro has_value(expected) macro has_value(expected)
have_value({{expected}}) have_value({{expected}})
end end
@ -548,8 +563,13 @@ module Spectator
# expect([1, 2, 3]).to contain_exactly(3, 2, 1) # expect([1, 2, 3]).to contain_exactly(3, 2, 1)
# ``` # ```
macro contain_exactly(*expected) macro contain_exactly(*expected)
%test_value = ::Spectator::TestValue.new(({{expected}}).to_a, {{expected.stringify}}) {% if expected.id.starts_with?("{*") %}
::Spectator::Matchers::ArrayMatcher.new(%test_value) %test_value = ::Spectator::TestValue.new(({{expected.id[2...-1]}}).to_a, {{expected.stringify}})
::Spectator::Matchers::ArrayMatcher.new(%test_value)
{% else %}
%test_value = ::Spectator::TestValue.new(({{expected}}).to_a, {{expected.stringify}})
::Spectator::Matchers::ArrayMatcher.new(%test_value)
{% end %}
end end
# Indicates that some set should contain the same values in any order as another set. # Indicates that some set should contain the same values in any order as another set.
@ -597,8 +617,13 @@ module Spectator
# expect(%i[a b c]).to have_attributes(size: 1..5, first: Symbol) # expect(%i[a b c]).to have_attributes(size: 1..5, first: Symbol)
# ``` # ```
macro have_attributes(**expected) macro have_attributes(**expected)
%test_value = ::Spectator::TestValue.new({{expected}}, {{expected.double_splat.stringify}}) {% if expected.id.starts_with?("{**") %}
::Spectator::Matchers::AttributesMatcher.new(%test_value) %test_value = ::Spectator::TestValue.new({{expected.id[3...-1]}}, {{expected.double_splat.stringify}})
::Spectator::Matchers::AttributesMatcher.new(%test_value)
{% else %}
%test_value = ::Spectator::TestValue.new({{expected}}, {{expected.double_splat.stringify}})
::Spectator::Matchers::AttributesMatcher.new(%test_value)
{% end %}
end end
# Verifies that all elements of a collection satisfy some matcher. # Verifies that all elements of a collection satisfy some matcher.

View file

@ -52,7 +52,7 @@ module Spectator::Expectations
stubs.each { |stub| to_not(stub) } stubs.each { |stub| to_not(stub) }
end end
# ditto # :ditto:
@[AlwaysInline] @[AlwaysInline]
def not_to(matcher) : Nil def not_to(matcher) : Nil
to_not(matcher) to_not(matcher)
@ -86,7 +86,7 @@ module Spectator::Expectations
to_not(stub) to_not(stub)
end end
# ditto # :ditto:
@[AlwaysInline] @[AlwaysInline]
def never_to(matcher) : Nil def never_to(matcher) : Nil
to_never(matcher) to_never(matcher)

View file

@ -0,0 +1,14 @@
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

View file

@ -49,6 +49,10 @@ module Spectator::Formatting
private def stats(report) private def stats(report)
@io.puts Runtime.new(report.runtime) @io.puts Runtime.new(report.runtime)
@io.puts StatsCounter.new(report).color @io.puts StatsCounter.new(report).color
if (seed = report.random_seed?)
@io.puts
@io.puts RandomSeedText.new(seed)
end
end end
# Produces the skipped tests text if fail-fast is enabled and tests were omitted. # Produces the skipped tests text if fail-fast is enabled and tests were omitted.

View file

@ -25,12 +25,16 @@ module Spectator
# This will be greater than zero only in fail-fast mode. # This will be greater than zero only in fail-fast mode.
getter remaining_count getter remaining_count
# Random seed used to determine test ordering.
getter! random_seed : UInt64?
# Creates the report. # Creates the report.
# The *results* are from running the examples in the test suite. # The *results* are from running the examples in the test suite.
# The *runtime* is the total time it took to execute the suite. # The *runtime* is the total time it took to execute the suite.
# The *remaining_count* is the number of tests skipped due to fail-fast. # The *remaining_count* is the number of tests skipped due to fail-fast.
# The *fail_blank* flag indicates whether it is a failure if there were no tests run. # The *fail_blank* flag indicates whether it is a failure if there were no tests run.
def initialize(@results : Array(Result), @runtime, @remaining_count = 0, @fail_blank = false) # The *random_seed* is the seed used for random number generation.
def initialize(@results : Array(Result), @runtime, @remaining_count = 0, @fail_blank = false, @random_seed = nil)
@results.each do |result| @results.each do |result|
case result case result
when SuccessfulResult when SuccessfulResult

View file

@ -25,7 +25,8 @@ module Spectator
# Generate a report and pass it along to the formatter. # Generate a report and pass it along to the formatter.
remaining = @suite.size - results.size remaining = @suite.size - results.size
report = Report.new(results, elapsed, remaining, @config.fail_blank?) seed = (@config.random_seed? if @config.randomize?)
report = Report.new(results, elapsed, remaining, @config.fail_blank?, seed)
@config.each_formatter(&.end_suite(report, profile(report))) @config.each_formatter(&.end_suite(report, profile(report)))
!report.failed? !report.failed?