Skip to content

Commit

Permalink
split spec files, for custom usage
Browse files Browse the repository at this point in the history
  • Loading branch information
kostya authored and asterite committed Dec 20, 2016
1 parent 306ef6d commit 50f77eb
Show file tree
Hide file tree
Showing 3 changed files with 208 additions and 206 deletions.
177 changes: 1 addition & 176 deletions src/spec.cr
@@ -1,6 +1,4 @@
require "colorize"
require "option_parser"
require "signal"
require "./spec/dsl"

# Crystal's builtin testing library.
#
Expand Down Expand Up @@ -65,179 +63,6 @@ require "signal"
# ```
# crystal spec spec/my/test/file_spec.cr:14
# ```
module Spec
private COLORS = {
success: :green,
fail: :red,
error: :red,
pending: :yellow,
}

private LETTERS = {
success: '.',
fail: 'F',
error: 'E',
pending: '*',
}

@@use_colors = true

# :nodoc:
def self.color(str, status)
if use_colors?
str.colorize(COLORS[status])
else
str
end
end

# :nodoc:
def self.use_colors?
@@use_colors
end

# :nodoc:
def self.use_colors=(@@use_colors)
end

# :nodoc:
class AssertionFailed < Exception
getter file : String
getter line : Int32

def initialize(message, @file, @line)
super(message)
end
end

@@aborted = false

# :nodoc:
def self.abort!
exit
end

# :nodoc:
def self.pattern=(pattern)
@@pattern = Regex.new(Regex.escape(pattern))
end

# :nodoc:
def self.line=(@@line : Int32)
end

# :nodoc:
def self.slowest=(@@slowest : Int32)
end

# :nodoc:
def self.slowest
@@slowest
end

# :nodoc:
def self.to_human(span : Time::Span)
total_milliseconds = span.total_milliseconds
if total_milliseconds < 1
return "#{(span.total_milliseconds * 1000).round.to_i} microseconds"
end

total_seconds = span.total_seconds
if total_seconds < 1
return "#{span.total_milliseconds.round(2)} milliseconds"
end

if total_seconds < 60
return "#{total_seconds.round(2)} seconds"
end

minutes = span.minutes
seconds = span.seconds
"#{minutes}:#{seconds < 10 ? "0" : ""}#{seconds} minutes"
end

# :nodoc:
def self.add_location(file, line)
locations = @@locations ||= {} of String => Array(Int32)
lines = locations[File.expand_path(file)] ||= [] of Int32
lines << line
end

# :nodoc:
def self.matches?(description, file, line, end_line = line)
spec_pattern = @@pattern
spec_line = @@line
locations = @@locations

# When a method invokes `it` and only forwards line information,
# not end_line information (this can happen in code before we
# introduced the end_line feature) then running a spec by giving
# a line won't work because end_line might be located before line.
# So, we also check `line == spec_line` to somehow preserve
# backwards compatibility.
if spec_line && (line == spec_line || line <= spec_line <= end_line)
return true
end

if locations
lines = locations[file]?
return true if lines && lines.any? { |l| line == l || line <= l <= end_line }
end

if spec_pattern || spec_line || locations
Spec::RootContext.matches?(description, spec_pattern, spec_line, locations)
else
true
end
end

@@fail_fast = false

# :nodoc:
def self.fail_fast=(@@fail_fast)
end

# :nodoc:
def self.fail_fast?
@@fail_fast
end

# Instructs the spec runner to execute the given block
# before each spec, regardless of where this method is invoked.
def self.before_each(&block)
before_each = @@before_each ||= [] of ->
before_each << block
end

# Instructs the spec runner to execute the given block
# after each spec, regardless of where this method is invoked.
def self.after_each(&block)
after_each = @@after_each ||= [] of ->
after_each << block
end

# :nodoc:
def self.run_before_each_hooks
@@before_each.try &.each &.call
end

# :nodoc:
def self.run_after_each_hooks
@@after_each.try &.each &.call
end

# :nodoc:
def self.run
start_time = Time.now
at_exit do
elapsed_time = Time.now - start_time
Spec::RootContext.print_results(elapsed_time)
exit 1 unless Spec::RootContext.succeeded
end
end
end

require "./spec/*"

OptionParser.parse! do |opts|
opts.banner = "crystal spec runner"
Expand Down
189 changes: 159 additions & 30 deletions src/spec/dsl.cr
@@ -1,48 +1,177 @@
module Spec::DSL
def describe(description, file = __FILE__, line = __LINE__, &block)
Spec::RootContext.describe(description.to_s, file, line, &block)
require "colorize"
require "option_parser"
require "signal"

module Spec
private COLORS = {
success: :green,
fail: :red,
error: :red,
pending: :yellow,
}

private LETTERS = {
success: '.',
fail: 'F',
error: 'E',
pending: '*',
}

@@use_colors = true

# :nodoc:
def self.color(str, status)
if use_colors?
str.colorize(COLORS[status])
else
str
end
end

def context(description, file = __FILE__, line = __LINE__, &block)
describe(description.to_s, file, line, &block)
# :nodoc:
def self.use_colors?
@@use_colors
end

def it(description, file = __FILE__, line = __LINE__, end_line = __END_LINE__, &block)
return unless Spec.matches?(description, file, line, end_line)
# :nodoc:
def self.use_colors=(@@use_colors)
end

Spec.formatters.each(&.before_example(description))
# :nodoc:
class AssertionFailed < Exception
getter file : String
getter line : Int32

start = Time.now
begin
Spec.run_before_each_hooks
block.call
Spec::RootContext.report(:success, description, file, line, Time.now - start)
rescue ex : Spec::AssertionFailed
Spec::RootContext.report(:fail, description, file, line, Time.now - start, ex)
Spec.abort! if Spec.fail_fast?
rescue ex
Spec::RootContext.report(:error, description, file, line, Time.now - start, ex)
Spec.abort! if Spec.fail_fast?
ensure
Spec.run_after_each_hooks
def initialize(message, @file, @line)
super(message)
end
end

def pending(description, file = __FILE__, line = __LINE__, end_line = __END_LINE__, &block)
return unless Spec.matches?(description, file, line, end_line)
@@aborted = false

# :nodoc:
def self.abort!
exit
end

# :nodoc:
def self.pattern=(pattern)
@@pattern = Regex.new(Regex.escape(pattern))
end

# :nodoc:
def self.line=(@@line : Int32)
end

Spec.formatters.each(&.before_example(description))
# :nodoc:
def self.slowest=(@@slowest : Int32)
end

Spec::RootContext.report(:pending, description, file, line)
# :nodoc:
def self.slowest
@@slowest
end

def assert(file = __FILE__, line = __LINE__, end_line = __END_LINE__, &block)
it("assert", file, line, end_line, &block)
# :nodoc:
def self.to_human(span : Time::Span)
total_milliseconds = span.total_milliseconds
if total_milliseconds < 1
return "#{(span.total_milliseconds * 1000).round.to_i} microseconds"
end

total_seconds = span.total_seconds
if total_seconds < 1
return "#{span.total_milliseconds.round(2)} milliseconds"
end

if total_seconds < 60
return "#{total_seconds.round(2)} seconds"
end

minutes = span.minutes
seconds = span.seconds
"#{minutes}:#{seconds < 10 ? "0" : ""}#{seconds} minutes"
end

def fail(msg, file = __FILE__, line = __LINE__)
raise Spec::AssertionFailed.new(msg, file, line)
# :nodoc:
def self.add_location(file, line)
locations = @@locations ||= {} of String => Array(Int32)
lines = locations[File.expand_path(file)] ||= [] of Int32
lines << line
end

# :nodoc:
def self.matches?(description, file, line, end_line = line)
spec_pattern = @@pattern
spec_line = @@line
locations = @@locations

# When a method invokes `it` and only forwards line information,
# not end_line information (this can happen in code before we
# introduced the end_line feature) then running a spec by giving
# a line won't work because end_line might be located before line.
# So, we also check `line == spec_line` to somehow preserve
# backwards compatibility.
if spec_line && (line == spec_line || line <= spec_line <= end_line)
return true
end

if locations
lines = locations[file]?
return true if lines && lines.any? { |l| line == l || line <= l <= end_line }
end

if spec_pattern || spec_line || locations
Spec::RootContext.matches?(description, spec_pattern, spec_line, locations)
else
true
end
end

@@fail_fast = false

# :nodoc:
def self.fail_fast=(@@fail_fast)
end

# :nodoc:
def self.fail_fast?
@@fail_fast
end

# Instructs the spec runner to execute the given block
# before each spec, regardless of where this method is invoked.
def self.before_each(&block)
before_each = @@before_each ||= [] of ->
before_each << block
end

# Instructs the spec runner to execute the given block
# after each spec, regardless of where this method is invoked.
def self.after_each(&block)
after_each = @@after_each ||= [] of ->
after_each << block
end

# :nodoc:
def self.run_before_each_hooks
@@before_each.try &.each &.call
end

# :nodoc:
def self.run_after_each_hooks
@@after_each.try &.each &.call
end

# :nodoc:
def self.run
start_time = Time.now
at_exit do
elapsed_time = Time.now - start_time
Spec::RootContext.print_results(elapsed_time)
exit 1 unless Spec::RootContext.succeeded
end
end
end

include Spec::DSL
require "./*"

0 comments on commit 50f77eb

Please sign in to comment.