summaryrefslogtreecommitdiff
path: root/spec/mspec/lib
diff options
context:
space:
mode:
authorBenoit Daloze <[email protected]>2019-09-29 16:03:47 +0200
committerBenoit Daloze <[email protected]>2019-09-29 16:03:47 +0200
commit31bb66a19df26409c9d47afcf37919c9a065516a (patch)
tree21d3d4171acbf2780b92c23833fe3727900de7c4 /spec/mspec/lib
parenta27dc83113afa9831dcdf919138666b09a73ccf6 (diff)
Update to ruby/mspec@8106083
Diffstat (limited to 'spec/mspec/lib')
-rw-r--r--spec/mspec/lib/mspec.rb14
-rw-r--r--spec/mspec/lib/mspec/commands/mspec-ci.rb1
-rw-r--r--spec/mspec/lib/mspec/commands/mspec-run.rb1
-rw-r--r--spec/mspec/lib/mspec/commands/mspec-tag.rb1
-rw-r--r--spec/mspec/lib/mspec/expectations/expectations.rb15
-rw-r--r--spec/mspec/lib/mspec/expectations/should.rb12
-rw-r--r--spec/mspec/lib/mspec/helpers/io.rb8
-rw-r--r--spec/mspec/lib/mspec/helpers/warning.rb10
-rw-r--r--spec/mspec/lib/mspec/matchers/base.rb86
-rw-r--r--spec/mspec/lib/mspec/matchers/complain.rb8
-rw-r--r--spec/mspec/lib/mspec/matchers/eql.rb8
-rw-r--r--spec/mspec/lib/mspec/matchers/equal.rb8
-rw-r--r--spec/mspec/lib/mspec/matchers/equal_element.rb4
-rw-r--r--spec/mspec/lib/mspec/matchers/raise_error.rb8
-rw-r--r--spec/mspec/lib/mspec/runner/actions/timeout.rb60
-rw-r--r--spec/mspec/lib/mspec/utils/format.rb20
-rw-r--r--spec/mspec/lib/mspec/utils/options.rb8
17 files changed, 163 insertions, 109 deletions
diff --git a/spec/mspec/lib/mspec.rb b/spec/mspec/lib/mspec.rb
index 42d590c99a..d24abd96f1 100644
--- a/spec/mspec/lib/mspec.rb
+++ b/spec/mspec/lib/mspec.rb
@@ -1,3 +1,4 @@
+require 'mspec/utils/format'
require 'mspec/matchers'
require 'mspec/expectations'
require 'mspec/mocks'
@@ -5,16 +6,3 @@ require 'mspec/runner'
require 'mspec/guards'
require 'mspec/helpers'
require 'mspec/version'
-
-# If the implementation on which the specs are run cannot
-# load pp from the standard library, add a pp.rb file that
-# defines the #pretty_inspect method on Object or Kernel.
-begin
- require 'pp'
-rescue LoadError
- module Kernel
- def pretty_inspect
- inspect
- end
- end
-end
diff --git a/spec/mspec/lib/mspec/commands/mspec-ci.rb b/spec/mspec/lib/mspec/commands/mspec-ci.rb
index cb0193f42d..2882613350 100644
--- a/spec/mspec/lib/mspec/commands/mspec-ci.rb
+++ b/spec/mspec/lib/mspec/commands/mspec-ci.rb
@@ -24,6 +24,7 @@ class MSpecCI < MSpecScript
options.configure { |f| load f }
options.pretend
options.interrupt
+ options.timeout
options.doc "\n How to modify the guard behavior"
options.unguarded
diff --git a/spec/mspec/lib/mspec/commands/mspec-run.rb b/spec/mspec/lib/mspec/commands/mspec-run.rb
index 249f9f5771..1e62bf2a13 100644
--- a/spec/mspec/lib/mspec/commands/mspec-run.rb
+++ b/spec/mspec/lib/mspec/commands/mspec-run.rb
@@ -36,6 +36,7 @@ class MSpecRun < MSpecScript
options.repeat
options.pretend
options.interrupt
+ options.timeout
options.doc "\n How to modify the guard behavior"
options.unguarded
diff --git a/spec/mspec/lib/mspec/commands/mspec-tag.rb b/spec/mspec/lib/mspec/commands/mspec-tag.rb
index 8bc3382e91..f5d85026ae 100644
--- a/spec/mspec/lib/mspec/commands/mspec-tag.rb
+++ b/spec/mspec/lib/mspec/commands/mspec-tag.rb
@@ -33,6 +33,7 @@ class MSpecTag < MSpecScript
options.pretend
options.unguarded
options.interrupt
+ options.timeout
options.doc "\n How to display their output"
options.formatters
diff --git a/spec/mspec/lib/mspec/expectations/expectations.rb b/spec/mspec/lib/mspec/expectations/expectations.rb
index cfdc2b63a3..b42dfebb7f 100644
--- a/spec/mspec/lib/mspec/expectations/expectations.rb
+++ b/spec/mspec/lib/mspec/expectations/expectations.rb
@@ -12,10 +12,21 @@ class SpecExpectation
expected_to_s = expected.to_s
actual_to_s = actual.to_s
if expected_to_s.size + actual_to_s.size > 80
- message = "#{expected_to_s.chomp}\n#{actual_to_s}"
+ message = "#{expected_to_s}\n#{actual_to_s}"
else
message = "#{expected_to_s} #{actual_to_s}"
end
- Kernel.raise SpecExpectationNotMetError, message
+ raise SpecExpectationNotMetError, message
+ end
+
+ def self.fail_predicate(receiver, predicate, args, block, result, expectation)
+ receiver_to_s = MSpec.format(receiver)
+ before_method = predicate.to_s =~ /^[a-z]/ ? "." : " "
+ predicate_to_s = "#{before_method}#{predicate}"
+ predicate_to_s += " " unless args.empty?
+ args_to_s = args.map { |arg| MSpec.format(arg) }.join(', ')
+ args_to_s += " { ... }" if block
+ result_to_s = MSpec.format(result)
+ raise SpecExpectationNotMetError, "Expected #{receiver_to_s}#{predicate_to_s}#{args_to_s}\n#{expectation} but was #{result_to_s}"
end
end
diff --git a/spec/mspec/lib/mspec/expectations/should.rb b/spec/mspec/lib/mspec/expectations/should.rb
index f6d83053f5..231ad15c21 100644
--- a/spec/mspec/lib/mspec/expectations/should.rb
+++ b/spec/mspec/lib/mspec/expectations/should.rb
@@ -4,26 +4,26 @@ class Object
def should(matcher = NO_MATCHER_GIVEN)
MSpec.expectation
MSpec.actions :expectation, MSpec.current.state
- unless matcher.equal? NO_MATCHER_GIVEN
+ if NO_MATCHER_GIVEN.equal?(matcher)
+ SpecPositiveOperatorMatcher.new(self)
+ else
unless matcher.matches? self
expected, actual = matcher.failure_message
SpecExpectation.fail_with(expected, actual)
end
- else
- SpecPositiveOperatorMatcher.new(self)
end
end
def should_not(matcher = NO_MATCHER_GIVEN)
MSpec.expectation
MSpec.actions :expectation, MSpec.current.state
- unless matcher.equal? NO_MATCHER_GIVEN
+ if NO_MATCHER_GIVEN.equal?(matcher)
+ SpecNegativeOperatorMatcher.new(self)
+ else
if matcher.matches? self
expected, actual = matcher.negative_failure_message
SpecExpectation.fail_with(expected, actual)
end
- else
- SpecNegativeOperatorMatcher.new(self)
end
end
end
diff --git a/spec/mspec/lib/mspec/helpers/io.rb b/spec/mspec/lib/mspec/helpers/io.rb
index f2c799dcab..d12d4b6ec3 100644
--- a/spec/mspec/lib/mspec/helpers/io.rb
+++ b/spec/mspec/lib/mspec/helpers/io.rb
@@ -78,8 +78,12 @@ end
# Creates an IO instance for a temporary file name. The file
# must be deleted.
-def new_io(name, mode="w:utf-8")
- IO.new new_fd(name, mode), mode
+def new_io(name, mode = "w:utf-8")
+ if Hash === mode # Avoid kwargs warnings on Ruby 2.7+
+ File.new(name, **mode)
+ else
+ File.new(name, mode)
+ end
end
def find_unused_fd
diff --git a/spec/mspec/lib/mspec/helpers/warning.rb b/spec/mspec/lib/mspec/helpers/warning.rb
index 9e093074e5..f54e4abf4d 100644
--- a/spec/mspec/lib/mspec/helpers/warning.rb
+++ b/spec/mspec/lib/mspec/helpers/warning.rb
@@ -5,3 +5,13 @@ def suppress_warning
ensure
$VERBOSE = verbose
end
+
+if ruby_version_is("2.7")
+ def suppress_keyword_warning(&block)
+ suppress_warning(&block)
+ end
+else
+ def suppress_keyword_warning
+ yield
+ end
+end
diff --git a/spec/mspec/lib/mspec/matchers/base.rb b/spec/mspec/lib/mspec/matchers/base.rb
index fc2d36c84a..94d3b71e55 100644
--- a/spec/mspec/lib/mspec/matchers/base.rb
+++ b/spec/mspec/lib/mspec/matchers/base.rb
@@ -10,98 +10,52 @@ class Module
include MSpecMatchers
end
-class SpecPositiveOperatorMatcher
+class SpecPositiveOperatorMatcher < BasicObject
def initialize(actual)
@actual = actual
end
def ==(expected)
- unless @actual == expected
- SpecExpectation.fail_with("Expected #{@actual.pretty_inspect}",
- "to equal #{expected.pretty_inspect}")
- end
+ method_missing(:==, expected)
end
- def <(expected)
- unless @actual < expected
- SpecExpectation.fail_with("Expected #{@actual.pretty_inspect}",
- "to be less than #{expected.pretty_inspect}")
- end
- end
-
- def <=(expected)
- unless @actual <= expected
- SpecExpectation.fail_with("Expected #{@actual.pretty_inspect}",
- "to be less than or equal to #{expected.pretty_inspect}")
- end
+ def !=(expected)
+ method_missing(:!=, expected)
end
- def >(expected)
- unless @actual > expected
- SpecExpectation.fail_with("Expected #{@actual.pretty_inspect}",
- "to be greater than #{expected.pretty_inspect}")
- end
- end
-
- def >=(expected)
- unless @actual >= expected
- SpecExpectation.fail_with("Expected #{@actual.pretty_inspect}",
- "to be greater than or equal to #{expected.pretty_inspect}")
- end
+ def equal?(expected)
+ method_missing(:equal?, expected)
end
- def =~(expected)
- unless @actual =~ expected
- SpecExpectation.fail_with("Expected #{@actual.pretty_inspect}",
- "to match #{expected.pretty_inspect}")
+ def method_missing(name, *args, &block)
+ result = @actual.__send__(name, *args, &block)
+ unless result
+ ::SpecExpectation.fail_predicate(@actual, name, args, block, result, "to be truthy")
end
end
end
-class SpecNegativeOperatorMatcher
+class SpecNegativeOperatorMatcher < BasicObject
def initialize(actual)
@actual = actual
end
def ==(expected)
- if @actual == expected
- SpecExpectation.fail_with("Expected #{@actual.pretty_inspect}",
- "not to equal #{expected.pretty_inspect}")
- end
+ method_missing(:==, expected)
end
- def <(expected)
- if @actual < expected
- SpecExpectation.fail_with("Expected #{@actual.pretty_inspect}",
- "not to be less than #{expected.pretty_inspect}")
- end
- end
-
- def <=(expected)
- if @actual <= expected
- SpecExpectation.fail_with("Expected #{@actual.pretty_inspect}",
- "not to be less than or equal to #{expected.pretty_inspect}")
- end
+ def !=(expected)
+ method_missing(:!=, expected)
end
- def >(expected)
- if @actual > expected
- SpecExpectation.fail_with("Expected #{@actual.pretty_inspect}",
- "not to be greater than #{expected.pretty_inspect}")
- end
- end
-
- def >=(expected)
- if @actual >= expected
- SpecExpectation.fail_with("Expected #{@actual.pretty_inspect}",
- "not to be greater than or equal to #{expected.pretty_inspect}")
- end
+ def equal?(expected)
+ method_missing(:equal?, expected)
end
- def =~(expected)
- if @actual =~ expected
- SpecExpectation.fail_with("Expected #{@actual.pretty_inspect}",
- "not to match #{expected.pretty_inspect}")
+ def method_missing(name, *args, &block)
+ result = @actual.__send__(name, *args, &block)
+ if result
+ ::SpecExpectation.fail_predicate(@actual, name, args, block, result, "to be falsy")
end
end
end
diff --git a/spec/mspec/lib/mspec/matchers/complain.rb b/spec/mspec/lib/mspec/matchers/complain.rb
index 22b8be17e1..887e72b4b0 100644
--- a/spec/mspec/lib/mspec/matchers/complain.rb
+++ b/spec/mspec/lib/mspec/matchers/complain.rb
@@ -17,10 +17,12 @@ class ComplainMatcher
def matches?(proc)
@saved_err = $stderr
@verbose = $VERBOSE
+ err = IOStub.new
+
+ Thread.current[:in_mspec_complain_matcher] = true
+ $stderr = err
+ $VERBOSE = @options.key?(:verbose) ? @options[:verbose] : false
begin
- err = $stderr = IOStub.new
- $VERBOSE = @options.key?(:verbose) ? @options[:verbose] : false
- Thread.current[:in_mspec_complain_matcher] = true
proc.call
ensure
$VERBOSE = @verbose
diff --git a/spec/mspec/lib/mspec/matchers/eql.rb b/spec/mspec/lib/mspec/matchers/eql.rb
index a855789550..bcab88ebee 100644
--- a/spec/mspec/lib/mspec/matchers/eql.rb
+++ b/spec/mspec/lib/mspec/matchers/eql.rb
@@ -9,13 +9,13 @@ class EqlMatcher
end
def failure_message
- ["Expected #{@actual.pretty_inspect}",
- "to have same value and type as #{@expected.pretty_inspect}"]
+ ["Expected #{MSpec.format(@actual)}",
+ "to have same value and type as #{MSpec.format(@expected)}"]
end
def negative_failure_message
- ["Expected #{@actual.pretty_inspect}",
- "not to have same value or type as #{@expected.pretty_inspect}"]
+ ["Expected #{MSpec.format(@actual)}",
+ "not to have same value or type as #{MSpec.format(@expected)}"]
end
end
diff --git a/spec/mspec/lib/mspec/matchers/equal.rb b/spec/mspec/lib/mspec/matchers/equal.rb
index 5dc77d27ea..5ba4856d82 100644
--- a/spec/mspec/lib/mspec/matchers/equal.rb
+++ b/spec/mspec/lib/mspec/matchers/equal.rb
@@ -9,13 +9,13 @@ class EqualMatcher
end
def failure_message
- ["Expected #{@actual.pretty_inspect}",
- "to be identical to #{@expected.pretty_inspect}"]
+ ["Expected #{MSpec.format(@actual)}",
+ "to be identical to #{MSpec.format(@expected)}"]
end
def negative_failure_message
- ["Expected #{@actual.pretty_inspect}",
- "not to be identical to #{@expected.pretty_inspect}"]
+ ["Expected #{MSpec.format(@actual)}",
+ "not to be identical to #{MSpec.format(@expected)}"]
end
end
diff --git a/spec/mspec/lib/mspec/matchers/equal_element.rb b/spec/mspec/lib/mspec/matchers/equal_element.rb
index 1e9dfbcca1..8da2567fcf 100644
--- a/spec/mspec/lib/mspec/matchers/equal_element.rb
+++ b/spec/mspec/lib/mspec/matchers/equal_element.rb
@@ -37,12 +37,12 @@ class EqualElementMatcher
end
def failure_message
- ["Expected #{@actual.pretty_inspect}",
+ ["Expected #{MSpec.format(@actual)}",
"to be a '#{@element}' element with #{attributes_for_failure_message} and #{content_for_failure_message}"]
end
def negative_failure_message
- ["Expected #{@actual.pretty_inspect}",
+ ["Expected #{MSpec.format(@actual)}",
"not to be a '#{@element}' element with #{attributes_for_failure_message} and #{content_for_failure_message}"]
end
diff --git a/spec/mspec/lib/mspec/matchers/raise_error.rb b/spec/mspec/lib/mspec/matchers/raise_error.rb
index 8350e25794..a720460091 100644
--- a/spec/mspec/lib/mspec/matchers/raise_error.rb
+++ b/spec/mspec/lib/mspec/matchers/raise_error.rb
@@ -52,19 +52,13 @@ class RaiseErrorMatcher
exception_class_and_message(exception.class, exception.message)
end
- def format_result(result)
- result.pretty_inspect.chomp
- rescue => e
- "#pretty_inspect raised #{e.class}; A #<#{result.class}>"
- end
-
def failure_message
message = ["Expected #{format_expected_exception}"]
if @actual
message << "but got #{format_exception(@actual)}"
else
- message << "but no exception was raised (#{format_result(@result)} was returned)"
+ message << "but no exception was raised (#{MSpec.format(@result)} was returned)"
end
message
diff --git a/spec/mspec/lib/mspec/runner/actions/timeout.rb b/spec/mspec/lib/mspec/runner/actions/timeout.rb
new file mode 100644
index 0000000000..9090efbe44
--- /dev/null
+++ b/spec/mspec/lib/mspec/runner/actions/timeout.rb
@@ -0,0 +1,60 @@
+class TimeoutAction
+ def initialize(timeout)
+ @timeout = timeout
+ @queue = Queue.new
+ @started = now
+ end
+
+ def register
+ MSpec.register :start, self
+ MSpec.register :before, self
+ MSpec.register :finish, self
+ end
+
+ private def now
+ Process.clock_gettime(Process::CLOCK_MONOTONIC)
+ end
+
+ private def fetch_item
+ @queue.pop(true)
+ rescue ThreadError
+ nil
+ end
+
+ def start
+ @thread = Thread.new do
+ loop do
+ if action = fetch_item
+ action.call
+ else
+ wakeup_at = @started + @timeout
+ left = wakeup_at - now
+ sleep left if left > 0
+ Thread.pass # Let the main thread run
+
+ if @queue.empty?
+ elapsed = now - @started
+ if elapsed > @timeout
+ STDERR.puts "\n#{@current_state.description}"
+ STDERR.flush
+ abort "Example took #{now - @started}s, which is longer than the timeout of #{@timeout}s"
+ end
+ end
+ end
+ end
+ end
+ end
+
+ def before(state=nil)
+ time = now
+ @queue << -> do
+ @current_state = state
+ @started = time
+ end
+ end
+
+ def finish
+ @thread.kill
+ @thread.join
+ end
+end
diff --git a/spec/mspec/lib/mspec/utils/format.rb b/spec/mspec/lib/mspec/utils/format.rb
new file mode 100644
index 0000000000..bb75e131de
--- /dev/null
+++ b/spec/mspec/lib/mspec/utils/format.rb
@@ -0,0 +1,20 @@
+# If the implementation on which the specs are run cannot
+# load pp from the standard library, add a pp.rb file that
+# defines the #pretty_inspect method on Object or Kernel.
+begin
+ require 'pp'
+rescue LoadError
+ module Kernel
+ def pretty_inspect
+ inspect
+ end
+ end
+end
+
+module MSpec
+ def self.format(obj)
+ obj.pretty_inspect.chomp
+ rescue => e
+ "#<#{obj.class}>(#pretty_inspect raised #{e.inspect})"
+ end
+end
diff --git a/spec/mspec/lib/mspec/utils/options.rb b/spec/mspec/lib/mspec/utils/options.rb
index bbe64238c5..560826a723 100644
--- a/spec/mspec/lib/mspec/utils/options.rb
+++ b/spec/mspec/lib/mspec/utils/options.rb
@@ -419,6 +419,14 @@ class MSpecOptions
end
end
+ def timeout
+ on("--timeout", "TIMEOUT", "Abort if a spec takes longer than TIMEOUT seconds") do |timeout|
+ require 'mspec/runner/actions/timeout'
+ timeout = Float(timeout)
+ TimeoutAction.new(timeout).register
+ end
+ end
+
def verify
on("--report-on", "GUARD", "Report specs guarded by GUARD") do |g|
MSpec.register_mode :report_on