Large diffs are not rendered by default.

@@ -15,7 +15,7 @@ def build_extension(name, arch)
args << "-I#{$rhoroot}/platform/shared"

if ENV['RHO_PLATFORM'] == 'android'
args << "-I#{$rhoroot}/platform/shared/ruby/linux"
args << "-I#{$rhoroot}/platform/shared/ruby/android"
args << "-I#{$rhoroot}/platform/shared/ruby/generated"
cc_compile f, $tempdir, args or exit 1

@@ -15,7 +15,7 @@ def build_extension(name, arch)
args << "-I../../../../platform/shared"

if $android
args << "-I../../../../platform/shared/ruby/linux"
args << "-I../../../../platform/shared/ruby/android"
args << "-I../../../../platform/shared/ruby/generated"
cc_compile f, $tempdir, args or exit 1

@@ -14,7 +14,7 @@ def build_extension(name, arch)
args << "-I#{$rootdir}/platform/shared"

if ENV['RHO_PLATFORM'] == 'android'
args << "-I#{$rootdir}/platform/shared/ruby/linux"
args << "-I#{$rootdir}/platform/shared/ruby/android"
args << "-I#{$rootdir}/platform/shared/ruby/generated"
cc_compile f, $tempdir, args or exit 1

@@ -13,7 +13,7 @@ def build_extension(name, arch)
args << "-I../../../../platform/shared"

if $android
args << "-I../../../../platform/shared/ruby/linux"
args << "-I../../../../platform/shared/ruby/android"
args << "-I../../../../platform/shared/ruby/generated"
cc_compile f, $tempdir, args or exit 1

@@ -1,17 +1,17 @@
class ExpectationNotMetError < StandardError; end
class ExpectationNotFoundError < StandardError
class SpecExpectationNotMetError < StandardError; end
class SpecExpectationNotFoundError < StandardError
def message
"No behavior expectation was found in the example"
end
end

class Expectation
class SpecExpectation
def self.fail_with(expected, actual)
if expected.to_s.size + actual.to_s.size > 80
message = expected.to_s.chomp + "\n" + actual.to_s
else
message = expected.to_s + " " + actual.to_s
end
Kernel.raise ExpectationNotMetError, message
Kernel.raise SpecExpectationNotMetError, message
end
end
@@ -1,25 +1,26 @@
class Object
def should(matcher=nil)
NO_MATCHER_GIVEN = Object.new
def should(matcher=NO_MATCHER_GIVEN)
MSpec.expectation
MSpec.actions :expectation, MSpec.current.state
if matcher
unless matcher.equal?(NO_MATCHER_GIVEN)
unless matcher.matches?(self)
Expectation.fail_with(*matcher.failure_message)
SpecExpectation.fail_with(*matcher.failure_message)
end
else
PositiveOperatorMatcher.new(self)
SpecPositiveOperatorMatcher.new(self)
end
end

def should_not(matcher=nil)
def should_not(matcher=NO_MATCHER_GIVEN)
MSpec.expectation
MSpec.actions :expectation, MSpec.current.state
if matcher
unless matcher.equal?(NO_MATCHER_GIVEN)
if matcher.matches?(self)
Expectation.fail_with(*matcher.negative_failure_message)
SpecExpectation.fail_with(*matcher.negative_failure_message)
end
else
NegativeOperatorMatcher.new(self)
SpecNegativeOperatorMatcher.new(self)
end
end
end

This file was deleted.

@@ -5,12 +5,15 @@
require 'mspec/guards/conflict'
require 'mspec/guards/endian'
require 'mspec/guards/extensions'
require 'mspec/guards/feature'
require 'mspec/guards/guard'
require 'mspec/guards/noncompliance'
require 'mspec/guards/platform'
require 'mspec/guards/quarantine'
require 'mspec/guards/runner'
require 'mspec/guards/specified'
require 'mspec/guards/support'
require 'mspec/guards/superuser'
require 'mspec/guards/tty'
require 'mspec/guards/user'
require 'mspec/guards/version'
@@ -2,12 +2,17 @@

class ConflictsGuard < SpecGuard
def match?
constants = Object.constants
@args.any? { |mod| constants.include? mod.to_s }
# Always convert constants to symbols regardless of version.
constants = Object.constants.map { |x| x.to_sym }
@args.any? { |mod| constants.include? mod }
end
end

class Object
# In some cases, libraries will modify another Ruby method's
# behavior. The specs for the method's behavior will then fail
# if that library is loaded. This guard will not run if any of
# the specified constants exist in Object.constants.
def conflicts_with(*modules)
g = ConflictsGuard.new(*modules)
g.name = :conflicts_with
@@ -3,25 +3,22 @@
# Despite that these are inverses, the two classes are
# used to simplify MSpec guard reporting modes

class BigEndianGuard < SpecGuard
class EndianGuard < SpecGuard
def pattern
[1].pack('L')
@pattern ||= [1].pack('L')
end
private :pattern
end

class BigEndianGuard < EndianGuard
def match?
pattern[-1] == ?\1
pattern[-1] == ?\001
end
end

class LittleEndianGuard < SpecGuard
def pattern
[1].pack('L')
end
private :pattern

class LittleEndianGuard < EndianGuard
def match?
pattern[-1] == ?\0
pattern[-1] == ?\000
end
end

@@ -0,0 +1,47 @@
require 'mspec/guards/guard'

class FeatureGuard < SpecGuard
def self.enabled?(*features)
new(*features).match?
end

def match?
@parameters.all? { |f| MSpec.feature_enabled? f }
end
end

class Object
# Provides better documentation in the specs by
# naming sets of features that work together as
# a whole. Examples include :encoding, :fiber,
# :continuation, :fork.
#
# Usage example:
#
# with_feature :encoding do
# # specs for a method that provides aspects
# # of the encoding feature
# end
#
# Multiple features must all be enabled for the
# guard to run:
#
# with_feature :one, :two do
# # these specs will run if features :one AND
# # :two are enabled.
# end
#
# The implementation must explicitly enable a feature
# by adding code like the following to the .mspec
# configuration file:
#
# MSpec.enable_feature :encoding
#
def with_feature(*features)
g = FeatureGuard.new(*features)
g.name = :with_feature
yield if g.yield?
ensure
g.unregister
end
end
@@ -1,5 +1,6 @@
require 'mspec/runner/mspec'
require 'mspec/runner/actions/tally'
require 'rbconfig'

class SpecGuard
def self.report
@@ -30,6 +31,16 @@ def self.clear_guards
@guards = []
end

@@ruby_version_override = nil

def self.ruby_version_override=(version)
@@ruby_version_override = version
end

def self.ruby_version_override
@@ruby_version_override
end

# Returns a partial Ruby version string based on +which+. For example,
# if RUBY_VERSION = 8.2.3 and RUBY_PATCHLEVEL = 71:
#
@@ -52,26 +63,18 @@ def self.ruby_version(which = :minor)

patch = RUBY_PATCHLEVEL.to_i
patch = 0 if patch < 0
version = "#{RUBY_VERSION}.#{patch}"
version = "#{ruby_version_override || RUBY_VERSION}.#{patch}"
version.split('.')[0,n].join('.')
end

def self.windows?(key = RUBY_PLATFORM)
!!key.match(/(mswin|mingw)/)
end

def self.android?(key = RUBY_PLATFORM)
!!key.match(/android/)
end

attr_accessor :name, :parameters

def initialize(*args)
self.parameters = @args = args
end

def yield?(invert=false)
#return true if MSpec.mode? :unguarded
return true if MSpec.mode? :unguarded

allow = match? ^ invert

@@ -127,6 +130,8 @@ def implementation?(*args)
RUBY_NAME =~ /^ironruby/
when :macruby
RUBY_NAME =~ /^macruby/
when :maglev
RUBY_NAME =~ /^maglev/
else
false
end
@@ -138,7 +143,7 @@ def standard?
end

def windows?(sym, key)
sym == :windows && SpecGuard.windows?(key)
sym == :windows && !key.match(/(mswin|mingw)/).nil?
end

def platform?(*args)
@@ -156,9 +161,9 @@ def wordsize?(size)
end

def os?(*oses)
# require 'rbconfig'
oses.any? do |os|
host_os = RUBY_PLATFORM.downcase
host_os = ::RbConfig::CONFIG['host_os'] || RUBY_PLATFORM
host_os.downcase!
host_os.match(os.to_s) || windows?(os, host_os)
end
end
@@ -1,6 +1,10 @@
require 'mspec/guards/guard'

class PlatformGuard < SpecGuard
def self.windows?
PlatformGuard.new(:os => :windows).match?
end

def initialize(*args)
if args.last.is_a?(Hash)
@options, @platforms = args.last, args[0..-2]
@@ -0,0 +1,66 @@
require 'mspec/guards/guard'

class UnspecifiedGuard < SpecGuard
def match?
not standard?
end
end

class SpecifiedOnGuard < SpecGuard
def match?
if @args.include? :ruby
raise Exception, "improper use of specified_on guard"
end
not standard? and implementation?(*@args)
end
end

class Object
# This guard wraps one or more #specified_on guards to group them and
# document the specs. The purpose of the guard is for situations where MRI
# either does not specify Ruby behavior or where MRI's behavior is all but
# impossible to spec, for example due to relying on platform-specific
# behavior that is not easily testable from Ruby code. In such cases, it
# may be desirable for implementations to explore a specified set of
# behaviors that are explicitly documented in the specs.
#
# unspecified do
# specified_on :rubinius, :ironruby do
# it "returns true when passed :foo" do
# # ...
# end
#
# it "returns false when passed :bar" do
# # ...
# end
# end
#
# specified_on :jruby do
# it "returns true when passed :bar" do
# # ...
# end
# end
# end
#
# Note that these guards do not change the policy of the #compliant_on,
# #not_compliant_on, #deviates_on, #extended_on, and #not_supported_on
# guards.
#
def unspecified
g = UnspecifiedGuard.new
g.name = :unspecified
yield if g.yield?
ensure
g.unregister
end

# This guard wraps specs for one or more particular implementations. See the
# #unspecified guard for further documentation.
def specified_on(*args)
g = SpecifiedOnGuard.new(*args)
g.name = :specified_on
yield if g.yield?
ensure
g.unregister
end
end
@@ -0,0 +1,17 @@
require 'mspec/guards/guard'

class UserGuard < SpecGuard
def match?
Process.euid != 0
end
end

class Object
def as_user
g = UserGuard.new
g.name = :as_user
yield if g.yield?
ensure
g.unregister
end
end
@@ -1,19 +1,21 @@
require 'mspec/helpers/argv'
require 'mspec/helpers/bignum'
require 'mspec/helpers/const_lookup'
require 'mspec/helpers/datetime'
require 'mspec/helpers/ducktype'
require 'mspec/helpers/encode'
require 'mspec/helpers/enumerator_class'
require 'mspec/helpers/environment'
require 'mspec/helpers/fixture'
require 'mspec/helpers/flunk'
require 'mspec/helpers/fmode'
require 'mspec/helpers/fs'
require 'mspec/helpers/hash'
require 'mspec/helpers/infinity'
require 'mspec/helpers/io'
require 'mspec/helpers/language_version'
require 'mspec/helpers/metaclass'
require 'mspec/helpers/mock_to_path'
require 'mspec/helpers/numeric'
require 'mspec/helpers/ruby_exe'
require 'mspec/helpers/nan'
require 'mspec/helpers/scratch'
require 'mspec/helpers/stasy'
require 'mspec/helpers/singleton_class'
require 'mspec/helpers/tmp'

This file was deleted.

@@ -3,7 +3,8 @@ def const_lookup(c)
names = c.split '::'
names.shift if names.first.empty?
names.inject(Object) do |m, n|
m.const_defined?(n) ? m.const_get(n) : m.const_missing(n)
defined = RUBY_VERSION =~ /^1.9/ ? m.const_defined?(n, false) : m.const_defined?(n)
defined ? m.const_get(n) : m.const_missing(n)
end
end
end
@@ -0,0 +1,30 @@
require 'date'

class Object
# The new_datetime helper makes writing DateTime specs more simple by
# providing default constructor values and accepting a Hash of only the
# constructor values needed for the particular spec. For example:
#
# new_datetime :hour => 1, :minute => 20
#
# Possible keys are:
# :year, :month, :day, :hour, :minute, :second, :offset and :sg.

MSPEC_DATETIME_OPTIONS = {
:year => -4712,
:month => 1,
:day => 1,
:hour => 0,
:minute => 0,
:second => 0,
:offset => 0,
:sg => Date::ITALY
}

def new_datetime(opts={})
value = MSPEC_DATETIME_OPTIONS.dup.merge opts

DateTime.new value[:year], value[:month], value[:day], value[:hour],
value[:minute], value[:second], value[:offset], value[:sg]
end
end
@@ -1,30 +1,30 @@
class Object
def responds_to(sym)
metaclass.class_eval <<-END
singleton_class.class_eval <<-END
def respond_to?(sym, include_private=false)
sym.to_sym == #{sym.to_sym.inspect} ? true : super
end
END
end

def does_not_respond_to(sym)
metaclass.class_eval <<-END
singleton_class.class_eval <<-END
def respond_to?(sym, include_private=false)
sym.to_sym == #{sym.to_sym.inspect} ? false : super
end
END
end

def undefine(sym)
metaclass.class_eval <<-END
singleton_class.class_eval <<-END
undef_method #{sym.to_sym.inspect}
END
end

def fake!(sym, value=nil)
responds_to sym

metaclass.class_eval <<-END
singleton_class.class_eval <<-END
def method_missing(sym, *args)
return #{value.inspect} if sym.to_sym == #{sym.to_sym.inspect}
end
@@ -0,0 +1,21 @@
require 'mspec/guards/feature'

class Object
# Helper to handle String encodings. The +str+ and +encoding+ parameters
# must be Strings and an ArgumentError will be raised if not. This ensures
# that the encode() helper can be used regardless of whether Encoding exits.
# The helper is a no-op (i.e. passes through +str+ unmodified) if the
# :encoding feature is not enabled (see with_feature guard). If the
# :encoding feature is enabled, +str+.force_encoding(+encoding+) is called.
def encode(str, encoding)
unless str.is_a? String and encoding.is_a? String
raise ArgumentError, "encoding name must be a String"
end

if FeatureGuard.enabled? :encoding
str.force_encoding encoding
end

str
end
end
@@ -3,10 +3,8 @@
class Object
def env
env = ""
if SpecGuard.windows?
if PlatformGuard.windows?
env = Hash[*`cmd.exe /C set`.split("\n").map { |e| e.split("=", 2) }.flatten]
elsif SpecGuard.android?
env = {}
else
env = Hash[*`env`.split("\n").map { |e| e.split("=", 2) }.flatten]
end
@@ -15,22 +13,28 @@ def env

def windows_env_echo(var)
`cmd.exe /C ECHO %#{var}%`.strip
end
end

def username
user = ""
if SpecGuard.windows?
if PlatformGuard.windows?
user = windows_env_echo('USERNAME')
elsif SpecGuard.android?
user = ENV['USER']
else
user = `whoami`.strip
end
user
end

def home_directory
return ENV['HOME'] unless SpecGuard.windows?
return ENV['HOME'] unless PlatformGuard.windows?
windows_env_echo('HOMEDRIVE') + windows_env_echo('HOMEPATH')
end
end

def dev_null
if PlatformGuard.windows?
"NUL"
else
"/dev/null"
end
end
end
@@ -15,6 +15,7 @@ class Object
def fixture(dir, *args)
path = File.dirname(dir)
path = path[0..-7] if path[-7..-1] == "/shared"
File.expand_path(File.join(path, "fixtures", args))
dir = path[-9..-1] == "/fixtures" ? "" : "fixtures"
File.expand_path(File.join(path, dir, args))
end
end
@@ -1,5 +1,5 @@
class Object
def flunk(msg="This example is a failure")
Expectation.fail_with "Failed:", msg
SpecExpectation.fail_with "Failed:", msg
end
end
@@ -0,0 +1,15 @@
require 'mspec/guards/feature'

class Object
# This helper simplifies passing file access modes regardless of
# whether the :encoding feature is enabled. Only the access specifier
# itself will be returned if :encoding is not enabled. Otherwise,
# the full mode string will be returned (i.e. the helper is a no-op).
def fmode(mode)
if FeatureGuard.enabled? :encoding
mode
else
mode.split(':').first
end
end
end

This file was deleted.

@@ -1,17 +1,36 @@
class IOStub < String
def write(*str)
self << str.to_s
self << str.join
end

def print(*str)
write(str.to_s + $\.to_s)
write(str.join + $\.to_s)
end

def puts(*str)
write(str.collect { |s| s.to_s.chomp }.concat([nil]).join("\n"))
end

def printf(format, *args)
self << sprintf(format, *args)
end

def flush
self
end
end

class Object
# Creates a "bare" file descriptor (i.e. one that is not associated
# with any Ruby object). The file descriptor can safely be passed
# to IO.new without creating a Ruby object alias to the fd.
def new_fd(name, mode="w:utf-8")
IO.sysopen name, fmode(mode)
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, fmode(mode)), fmode(mode)
end
end
@@ -10,11 +10,21 @@ class Object
#
# Then add a file "language/versions/method_1.8.rb" for the specs that are
# syntax-compatible with Ruby 1.8.x.
#
# The most version-specific file will be loaded. If the version is 1.8.6,
# "method_1.8.6.rb" will be loaded if it exists, otherwise "method_1.8.rb"
# will be loaded if it exists.
def language_version(dir, name)
path = File.dirname(File.expand_path(dir))
name = "#{name}_#{SpecGuard.ruby_version}.rb"
file = File.join path, "versions", name

require file if File.exists? file
[SpecGuard.ruby_version(:tiny), SpecGuard.ruby_version].each do |version|
file = File.join path, "versions", "#{name}_#{version}.rb"
if File.exists? file
require file
break
end
end

nil
end
end

This file was deleted.

This file was deleted.

@@ -0,0 +1,89 @@
class Object
def nan_value
0/0.0
end

def infinity_value
1/0.0
end

def bignum_value(plus=0)
0x8000_0000_0000_0000 + plus
end

# This is a bit hairy, but we need to be able to write specs that cover the
# boundary between Fixnum and Bignum for operations like Fixnum#<<. Since
# this boundary is implementation-dependent, we use these helpers to write
# specs based on the relationship between values rather than specific
# values.
guard = SpecGuard.new

if guard.standard?
if guard.wordsize? 32
def fixnum_max()
(2**30) - 1
end

def fixnum_min()
-(2**30)
end
elsif guard.wordsize? 64
def fixnum_max()
(2**62) - 1
end

def fixnum_min()
-(2**62)
end
end
elsif guard.implementation? :rubinius
def fixnum_max()
Fixnum::MAX
end

def fixnum_min()
Fixnum::MIN
end
elsif guard.implementation? :jruby
# Values from jruby/test/testFixnumBignumAutoconversion.rb
def fixnum_max()
9223372036854775807
end

def fixnum_min()
-9223372036854775808
end
elsif guard.implementation? :ironruby
def fixnum_max()
raise "fixnum_max() helper not implemented"
end

def fixnum_min()
raise "fixnum_min() helper not implemented"
end
elsif guard.implementation? :maglev
def fixnum_max()
raise "fixnum_max() helper not implemented"
end

def fixnum_min()
raise "fixnum_min() helper not implemented"
end
elsif guard.implementation? :macruby
def fixnum_max()
raise "fixnum_max() helper not implemented"
end

def fixnum_min()
raise "fixnum_min() helper not implemented"
end
else
def fixnum_max()
raise "unknown implementation for fixnum_max() helper"
end

def fixnum_min()
raise "unknown implementation for fixnum_min() helper"
end
end
end
@@ -1,123 +1,138 @@
# require 'mspec/utils/ruby_name'
# require 'mspec/guards/guard'
#
# # The ruby_exe helper provides a wrapper for invoking the
# # same Ruby interpreter as the one running the specs and
# # getting the output from running the code. If +code+ is a
# # file that exists, it will be run. Otherwise, +code+ should
# # be Ruby code that will be run with the -e command line
# # option. For example:
# #
# # ruby_exe('path/to/some/file.rb')
# #
# # will be executed as
# #
# # `#{RUBY_EXE} #{'path/to/some/file.rb'}`
# #
# # while
# #
# # ruby_exe('puts "hello, world."')
# #
# # will be executed as
# #
# # `#{RUBY_EXE} -e #{'puts "hello, world."'}`
# #
# # The ruby_exe helper also accepts an options hash with two
# # keys: :options and :args. For example:
# #
# # ruby_exe('file.rb', :options => "-w", :args => "> file.txt")
# #
# # will be executed as
# #
# # `#{RUBY_EXE} -w #{'file.rb'} > file.txt`
# #
# # If +nil+ is passed for the first argument, the command line
# # will be built only from the options hash.
# #
# # The RUBY_EXE constant can be set explicitly since the value
# # is used each time ruby_exe is invoked. The mspec runner script
# # will set ENV['RUBY_EXE'] to the name of the executable used
# # to invoke the mspec-run script. The value of RUBY_EXE will be
# # constructed as follows:
# #
# # 1. the value of ENV['RUBY_EXE']
# # 2. an explicit value based on RUBY_NAME
# # 3. cwd/(RUBY_NAME + $(EXEEXT) || $(exeext) || '')
# # 4. $(bindir)/$(RUBY_INSTALL_NAME)
# #
# # The value will only be used if the file exists and is executable.
# #
# # These 4 ways correspond to the following scenarios:
# #
# # 1. Using the MSpec runner scripts, the name of the
# # executable is explicitly passed by ENV['RUBY_EXE']
# # so there is no ambiguity.
# #
# # Otherwise, if using RSpec (or something else)
# #
# # 2. Running the specs while developing an alternative
# # Ruby implementation. This explicitly names the
# # executable in the development directory based on
# # the value of RUBY_NAME, which is probably initialized
# # from the value of RUBY_ENGINE.
# # 3. Running the specs within the source directory for
# # some implementation. (E.g. a local build directory.)
# # 4. Running the specs against some installed Ruby
# # implementation.
#
# class Object
# def ruby_exe_options(option)
# case option
# when :env
# ENV['RUBY_EXE']
# when :engine
# case RUBY_NAME
# when 'rbx'
# "bin/rbx"
# when 'jruby'
# "bin/jruby"
# when 'ironruby'
# "ir"
# end
# when :name
# bin = RUBY_NAME + (Config::CONFIG['EXEEXT'] || Config::CONFIG['exeext'] || '')
# File.join(".", bin)
# when :install_name
# bin = Config::CONFIG["RUBY_INSTALL_NAME"] || Config::CONFIG["ruby_install_name"]
# bin << (Config::CONFIG['EXEEXT'] || Config::CONFIG['exeext'] || '')
# File.join(Config::CONFIG['bindir'], bin)
# end
# end
#
# def resolve_ruby_exe
# [:env, :engine, :name, :install_name].each do |option|
# next unless cmd = ruby_exe_options(option)
# exe = cmd.split.first
#
# # It has been reported that File.executable is not reliable
# # on Windows platforms (see commit 56bc555c). So, we check the
# # platform.
# if File.exists?(exe) and (SpecGuard.windows? or File.executable?(exe))
# return cmd
# end
# end
# nil
# end
#
# def ruby_exe(code, opts = {})
# body = code
# working_dir = opts[:dir] || "."
# Dir.chdir(working_dir) do
# body = "-e #{code.inspect}" if code and not File.exists?(code)
# cmd = [RUBY_EXE, ENV['RUBY_FLAGS'], opts[:options], body, opts[:args]]
# `#{cmd.compact.join(' ')}`
# end
# end
#
# unless Object.const_defined?(:RUBY_EXE) and RUBY_EXE
# require 'rbconfig'
#
# RUBY_EXE = resolve_ruby_exe or
# raise Exception, "Unable to find a suitable ruby executable."
# end
# end
require 'mspec/utils/ruby_name'
require 'mspec/guards/platform'

# The ruby_exe helper provides a wrapper for invoking the
# same Ruby interpreter as the one running the specs and
# getting the output from running the code. If +code+ is a
# file that exists, it will be run. Otherwise, +code+ should
# be Ruby code that will be run with the -e command line
# option. For example:
#
# ruby_exe('path/to/some/file.rb')
#
# will be executed as
#
# `#{RUBY_EXE} #{'path/to/some/file.rb'}`
#
# while
#
# ruby_exe('puts "hello, world."')
#
# will be executed as
#
# `#{RUBY_EXE} -e #{'puts "hello, world."'}`
#
# The ruby_exe helper also accepts an options hash with two
# keys: :options and :args. For example:
#
# ruby_exe('file.rb', :options => "-w", :args => "> file.txt")
#
# will be executed as
#
# `#{RUBY_EXE} -w #{'file.rb'} > file.txt`
#
# If +nil+ is passed for the first argument, the command line
# will be built only from the options hash.
#
# The RUBY_EXE constant can be set explicitly since the value
# is used each time ruby_exe is invoked. The mspec runner script
# will set ENV['RUBY_EXE'] to the name of the executable used
# to invoke the mspec-run script. The value of RUBY_EXE will be
# constructed as follows:
#
# 1. the value of ENV['RUBY_EXE']
# 2. an explicit value based on RUBY_NAME
# 3. cwd/(RUBY_NAME + $(EXEEXT) || $(exeext) || '')
# 4. $(bindir)/$(RUBY_INSTALL_NAME)
#
# The value will only be used if the file exists and is executable.
#
# These 4 ways correspond to the following scenarios:
#
# 1. Using the MSpec runner scripts, the name of the
# executable is explicitly passed by ENV['RUBY_EXE']
# so there is no ambiguity.
#
# Otherwise, if using RSpec (or something else)
#
# 2. Running the specs while developing an alternative
# Ruby implementation. This explicitly names the
# executable in the development directory based on
# the value of RUBY_NAME, which is probably initialized
# from the value of RUBY_ENGINE.
# 3. Running the specs within the source directory for
# some implementation. (E.g. a local build directory.)
# 4. Running the specs against some installed Ruby
# implementation.

class Object
def ruby_exe_options(option)
case option
when :env
ENV['RUBY_EXE']
when :engine
case RUBY_NAME
when 'rbx'
if SpecGuard.ruby_version < "1.9"
"bin/rbx"
else
"bin/rbx -X19"
end
when 'jruby'
"bin/jruby"
when 'maglev'
"maglev-ruby"
when 'ironruby'
"ir"
end
when :name
bin = RUBY_NAME + (RbConfig::CONFIG['EXEEXT'] || RbConfig::CONFIG['exeext'] || '')
File.join(".", bin)
when :install_name
bin = RbConfig::CONFIG["RUBY_INSTALL_NAME"] || RbConfig::CONFIG["ruby_install_name"]
bin << (RbConfig::CONFIG['EXEEXT'] || RbConfig::CONFIG['exeext'] || '')
File.join(RbConfig::CONFIG['bindir'], bin)
end
end

def resolve_ruby_exe
[:env, :engine, :name, :install_name].each do |option|
next unless cmd = ruby_exe_options(option)
exe = cmd.split.first

# It has been reported that File.executable is not reliable
# on Windows platforms (see commit 56bc555c). So, we check the
# platform.
if File.exists?(exe) and (PlatformGuard.windows? or File.executable?(exe))
return cmd
end
end
nil
end

def ruby_exe(code, opts = {})
body = code
working_dir = opts[:dir] || "."
Dir.chdir(working_dir) do
if code and not File.exists?(code)
if opts[:escape]
code = "'#{code}'"
else
code = code.inspect
end
body = "-e #{code}"
end
cmd = [RUBY_EXE, ENV['RUBY_FLAGS'], opts[:options], body, opts[:args]]
`#{cmd.compact.join(' ')}`
end
end

#RHO
# unless Object.const_defined?(:RUBY_EXE) and RUBY_EXE
# require 'rbconfig'
#
# RUBY_EXE = resolve_ruby_exe or
# raise Exception, "Unable to find a suitable ruby executable."
# end
#RHO
end
@@ -0,0 +1,7 @@
class Object
unless method_defined? :singleton_class
def singleton_class
class << self; self; end
end
end
end
@@ -0,0 +1,33 @@
require 'mspec/utils/version'
require 'mspec/guards/guard'

class Object
# Accepts either a single argument or an Array of arguments. If RUBY_VERSION
# is less than 1.9, converts the argument(s) to Strings; otherwise, converts
# the argument(s) to Symbols.
#
# If one argument is passed, the converted argument is returned. If an Array
# is passed, an Array is returned.
#
# For example, if RUBY_VERSION == 1.8.7
#
# stasy(:some) => "some"
# stasy("nom") => "nom"
#
# while if RUBY_VERSION == 1.9.0
#
# stasy(:some) => :some
# stasy("nom") => :nom

def stasy(one, *rest)
era = SpecVersion.new(SpecGuard.ruby_version) < "1.9"
convert = era ? :to_s : :to_sym

one = one.send convert
if rest.empty?
one
else
[one].concat rest.map { |x| x.send convert }
end
end
end
@@ -1,32 +1,40 @@
# The #tmp method provides a similar functionality
# to that of Dir.tmpdir. This helper can be overridden
# by different implementations to provide a more useful
# behavior if needed.
#
# Usage in a spec:
#
# File.open(tmp("tags.txt"), "w") { |f| f.puts "" }
#
# The order of directories below with "/private/tmp"
# preceding "/tmp" is significant. On OS X, the directory
# "/tmp" is a symlink to "private/tmp" with no leading
# "/". Rather than futzing with what constitutes an
# absolute path, we just look for "/private/tmp" first.
# Creates a temporary directory in the current working directory
# for temporary files created while running the specs. All specs
# should clean up any temporary files created so that the temp
# directory is empty when the process exits.

SPEC_TEMP_DIR = "#{File.expand_path(Dir.pwd)}/rubyspec_temp"

SPEC_TEMP_UNIQUIFIER = "0"

at_exit do
begin
Dir.delete SPEC_TEMP_DIR if File.directory? SPEC_TEMP_DIR
rescue SystemCallError
STDERR.puts <<-EOM
-----------------------------------------------------
The rubyspec temp directory is not empty. Ensure that
all specs are cleaning up temporary files.
-----------------------------------------------------
EOM
rescue Object => e
STDERR.puts "failed to remove spec temp directory"
STDERR.puts e.message
end
end

class Object
def tmp(name)
unless @spec_temp_directory
[ "/private/tmp", "/tmp", "/var/tmp", ENV["TMPDIR"], ENV["TMP"],
ENV["TEMP"], ENV["USERPROFILE"] ].each do |dir|
if dir and File.directory?(dir) and File.writable?(dir)
temp = File.expand_path dir
temp = File.readlink temp if File.symlink? temp
@spec_temp_directory = temp
break
end
end
def tmp(name, uniquify=true)
Dir.mkdir SPEC_TEMP_DIR unless File.exists? SPEC_TEMP_DIR

if uniquify and !name.empty?
slash = name.rindex "/"
index = slash ? slash + 1 : 0
name.insert index, "#{SPEC_TEMP_UNIQUIFIER.succ!}-"
end

File.join @spec_temp_directory, name
File.join SPEC_TEMP_DIR, name
end
end
@@ -2,21 +2,30 @@
require 'mspec/matchers/be_an_instance_of'
require 'mspec/matchers/be_ancestor_of'
require 'mspec/matchers/be_close'
require 'mspec/matchers/be_computed_by'
require 'mspec/matchers/be_computed_by_function'
require 'mspec/matchers/be_empty'
require 'mspec/matchers/be_false'
require 'mspec/matchers/be_kind_of'
require 'mspec/matchers/be_nil'
require 'mspec/matchers/be_true'
require 'mspec/matchers/be_valid_dns_name'
require 'mspec/matchers/complain'
require 'mspec/matchers/eql'
require 'mspec/matchers/equal'
require 'mspec/matchers/equal_element'
require 'mspec/matchers/equal_utf16'
require 'mspec/matchers/have_constant'
require 'mspec/matchers/have_class_variable'
require 'mspec/matchers/have_data'
require 'mspec/matchers/have_instance_method'
require 'mspec/matchers/have_instance_variable'
require 'mspec/matchers/have_method'
require 'mspec/matchers/have_private_instance_method'
require 'mspec/matchers/have_private_method'
require 'mspec/matchers/have_protected_instance_method'
require 'mspec/matchers/have_public_instance_method'
require 'mspec/matchers/have_singleton_method'
require 'mspec/matchers/include'
require 'mspec/matchers/match_yaml'
require 'mspec/matchers/raise_error'
@@ -1,94 +1,94 @@
class PositiveOperatorMatcher
class SpecPositiveOperatorMatcher
def initialize(actual)
@actual = actual
end

def ==(expected)
unless @actual == expected
Expectation.fail_with("Expected #{@actual.pretty_inspect}",
SpecExpectation.fail_with("Expected #{@actual.pretty_inspect}",
"to equal #{expected.pretty_inspect}")
end
end

def <(expected)
unless @actual < expected
Expectation.fail_with("Expected #{@actual.pretty_inspect}",
SpecExpectation.fail_with("Expected #{@actual.pretty_inspect}",
"to be less than #{expected.pretty_inspect}")
end
end

def <=(expected)
unless @actual <= expected
Expectation.fail_with("Expected #{@actual.pretty_inspect}",
SpecExpectation.fail_with("Expected #{@actual.pretty_inspect}",
"to be less than or equal to #{expected.pretty_inspect}")
end
end

def >(expected)
unless @actual > expected
Expectation.fail_with("Expected #{@actual.pretty_inspect}",
SpecExpectation.fail_with("Expected #{@actual.pretty_inspect}",
"to be greater than #{expected.pretty_inspect}")
end
end

def >=(expected)
unless @actual >= expected
Expectation.fail_with("Expected #{@actual.pretty_inspect}",
SpecExpectation.fail_with("Expected #{@actual.pretty_inspect}",
"to be greater than or equal to #{expected.pretty_inspect}")
end
end

def =~(expected)
unless @actual =~ expected
Expectation.fail_with("Expected #{@actual.pretty_inspect}",
SpecExpectation.fail_with("Expected #{@actual.pretty_inspect}",
"to match #{expected.pretty_inspect}")
end
end
end

class NegativeOperatorMatcher
class SpecNegativeOperatorMatcher
def initialize(actual)
@actual = actual
end

def ==(expected)
if @actual == expected
Expectation.fail_with("Expected #{@actual.pretty_inspect}",
SpecExpectation.fail_with("Expected #{@actual.pretty_inspect}",
"not to equal #{expected.pretty_inspect}")
end
end

def <(expected)
if @actual < expected
Expectation.fail_with("Expected #{@actual.pretty_inspect}",
SpecExpectation.fail_with("Expected #{@actual.pretty_inspect}",
"not to be less than #{expected.pretty_inspect}")
end
end

def <=(expected)
if @actual <= expected
Expectation.fail_with("Expected #{@actual.pretty_inspect}",
SpecExpectation.fail_with("Expected #{@actual.pretty_inspect}",
"not to be less than or equal to #{expected.pretty_inspect}")
end
end

def >(expected)
if @actual > expected
Expectation.fail_with("Expected #{@actual.pretty_inspect}",
SpecExpectation.fail_with("Expected #{@actual.pretty_inspect}",
"not to be greater than #{expected.pretty_inspect}")
end
end

def >=(expected)
if @actual >= expected
Expectation.fail_with("Expected #{@actual.pretty_inspect}",
SpecExpectation.fail_with("Expected #{@actual.pretty_inspect}",
"not to be greater than or equal to #{expected.pretty_inspect}")
end
end

def =~(expected)
if @actual =~ expected
Expectation.fail_with("Expected #{@actual.pretty_inspect}",
SpecExpectation.fail_with("Expected #{@actual.pretty_inspect}",
"not to match #{expected.pretty_inspect}")
end
end
@@ -0,0 +1,36 @@
class BeComputedByMatcher
def initialize(sym, *args)
@method = sym
@args = args
end

def matches?(array)
array.each do |line|
@receiver = line.shift
@value = line.pop
@arguments = line
@arguments += @args
return false unless @receiver.send(@method, *@arguments) == @value
end

return true
end

def method_call
method_call = "#{@receiver.inspect}.#{@method}"
unless @arguments.empty?
method_call << " from #{@arguments.map { |x| x.inspect }.join(", ")}"
end
method_call
end

def failure_message
["Expected #{@value.inspect}", "to be computed by #{method_call}"]
end
end

class Object
def be_computed_by(sym, *args)
BeComputedByMatcher.new(sym, *args)
end
end
@@ -0,0 +1,35 @@
class BeComputedByFunctionMatcher
def initialize(sym, *args)
@function = sym
@args = args
end

def matches?(array)
array.each do |line|
@value = line.pop
@arguments = line
@arguments += @args
return false unless send(@function, *@arguments) == @value
end

return true
end

def function_call
function_call = "#{@function}"
unless @arguments.empty?
function_call << "(#{@arguments.map { |x| x.inspect }.join(", ")})"
end
function_call
end

def failure_message
["Expected #{@value.inspect}", "to be computed by #{function_call}"]
end
end

class Object
def be_computed_by_function(sym, *args)
BeComputedByFunctionMatcher.new(sym, *args)
end
end
@@ -0,0 +1,24 @@
class BeValidDNSName
# http://stackoverflow.com/questions/106179/regular-expression-to-match-hostname-or-ip-address
# ftp://ftp.rfc-editor.org/in-notes/rfc3696.txt
VALID_DNS = /^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z]|[A-Za-z][A-Za-z0-9\-]*[A-Za-z0-9])\.?$/

def matches?(actual)
@actual = actual
(VALID_DNS =~ @actual) == 0
end

def failure_message
["Expected '#{@actual}'", "to be a valid DNS name"]
end

def negative_failure_message
["Expected '#{@actual}'", "not to be a valid DNS name"]
end
end

class Object
def be_valid_DNS_name
BeValidDNSName.new
end
end
@@ -1,19 +1,17 @@
require 'mspec/helpers/encode'

class EqualUtf16Matcher
def initialize(expected)
@expected = expected
@expected = Array(expected).map { |x| encode x, "binary" }
end

def matches?(actual)
@actual = actual
@actual = Array(actual).map { |x| encode x, "binary" }
@actual == @expected || @actual == expected_swapped
end

def expected_swapped
if @expected.respond_to?(:to_str)
@expected_swapped ||= @expected.to_str.gsub(/(.)(.)/, '\2\1')
else
@expected_swapped ||= @expected.collect { |s| s.to_str.gsub(/(.)(.)/, '\2\1') }
end
@expected_swapped ||= @expected.map { |x| x.to_str.gsub(/(.)(.)/, '\2\1') }
end

def failure_message
@@ -0,0 +1,12 @@
require 'mspec/matchers/variable'

class HaveClassVariableMatcher < VariableMatcher
self.variables_method = :class_variables
self.description = 'class variable'
end

class Object
def have_class_variable(variable)
HaveClassVariableMatcher.new(variable)
end
end
@@ -1,30 +1,12 @@
require 'mspec/matchers/stringsymboladapter'
require 'mspec/matchers/variable'

class HaveConstantMatcher
include StringSymbolAdapter

def initialize(name)
@name = convert_name name
end

def matches?(mod)
@mod = mod
@mod.constants.include? @name
end

def failure_message
["Expected #{@mod} to have constant '#{@name.to_s}'",
"but it does not"]
end

def negative_failure_message
["Expected #{@mod} NOT to have constant '#{@name.to_s}'",
"but it does"]
end
class HaveConstantMatcher < VariableMatcher
self.variables_method = :constants
self.description = 'constant'
end

class Object
def have_constant(name)
HaveConstantMatcher.new name
def have_constant(variable)
HaveConstantMatcher.new(variable)
end
end
@@ -0,0 +1,49 @@
require 'mspec/guards/feature'
require 'mspec/helpers/fmode'

class HaveDataMatcher
def initialize(data, mode="rb:binary")
@data = data
@mode = mode
end

def matches?(name)
@name = name

if FeatureGuard.enabled? :encoding
size = @data.bytesize
else
size = @data.size
end

File.open @name, fmode(@mode) do |f|
return f.read(size) == @data
end
end

def failure_message
["Expected #{@name}",
"to have data #{@data.pretty_inspect}"]
end

def negative_failure_message
["Expected #{@name}",
"not to have data #{@data.pretty_inspect}"]
end
end

class Object
# Opens a file specified by the string the matcher is called on
# and compares the +data+ passed to the matcher with the contents
# of the file. Expects to match the first N bytes of the file
# with +data+. For example, suppose @name is the name of a file:
#
# @name.should have_data("123")
#
# passes if the file @name has "123" as the first 3 bytes. The
# file can contain more bytes than +data+. The extra bytes do not
# affect the result.
def have_data(data, mode="rb:binary")
HaveDataMatcher.new(data, mode)
end
end
@@ -3,7 +3,7 @@
class HaveInstanceMethodMatcher < MethodMatcher
def matches?(mod)
@mod = mod
mod.instance_methods(@include_super).include? @method.to_sym
mod.instance_methods(@include_super).include? @method
end

def failure_message
@@ -0,0 +1,12 @@
require 'mspec/matchers/variable'

class HaveInstanceVariableMatcher < VariableMatcher
self.variables_method = :instance_variables
self.description = 'instance variable'
end

class Object
def have_instance_variable(variable)
HaveInstanceVariableMatcher.new(variable)
end
end
@@ -3,7 +3,7 @@
class HaveMethodMatcher < MethodMatcher
def matches?(mod)
@mod = mod
@mod.methods(@include_super).include? @method.to_sym
@mod.methods(@include_super).include? @method
end

def failure_message
@@ -3,7 +3,7 @@
class HavePrivateInstanceMethodMatcher < MethodMatcher
def matches?(mod)
@mod = mod
mod.private_instance_methods(@include_super).include? @method.to_sym
mod.private_instance_methods(@include_super).include? @method
end

def failure_message
@@ -0,0 +1,24 @@
require 'mspec/matchers/method'

class HavePrivateMethodMatcher < MethodMatcher
def matches?(mod)
@mod = mod
mod.private_methods(@include_super).include? @method
end

def failure_message
["Expected #{@mod} to have private method '#{@method.to_s}'",
"but it does not"]
end

def negative_failure_message
["Expected #{@mod} NOT to have private method '#{@method.to_s}'",
"but it does"]
end
end

class Object
def have_private_method(method, include_super=true)
HavePrivateMethodMatcher.new method, include_super
end
end
@@ -0,0 +1,24 @@
require 'mspec/matchers/method'

class HaveProtectedInstanceMethodMatcher < MethodMatcher
def matches?(mod)
@mod = mod
mod.protected_instance_methods(@include_super).include? @method
end

def failure_message
["Expected #{@mod} to have protected instance method '#{@method.to_s}'",
"but it does not"]
end

def negative_failure_message
["Expected #{@mod} NOT to have protected instance method '#{@method.to_s}'",
"but it does"]
end
end

class Object
def have_protected_instance_method(method, include_super=true)
HaveProtectedInstanceMethodMatcher.new method, include_super
end
end
@@ -3,7 +3,7 @@
class HavePublicInstanceMethodMatcher < MethodMatcher
def matches?(mod)
@mod = mod
mod.public_instance_methods(@include_super).include? @method.to_sym
mod.public_instance_methods(@include_super).include? @method
end

def failure_message
@@ -0,0 +1,24 @@
require 'mspec/matchers/method'

class HaveSingletonMethodMatcher < MethodMatcher
def matches?(obj)
@obj = obj
obj.singleton_methods(@include_super).include? @method
end

def failure_message
["Expected #{@obj} to have singleton method '#{@method.to_s}'",
"but it does not"]
end

def negative_failure_message
["Expected #{@obj} NOT to have singleton method '#{@method.to_s}'",
"but it does"]
end
end

class Object
def have_singleton_method(method, include_super=true)
HaveSingletonMethodMatcher.new method, include_super
end
end
@@ -1,71 +1,71 @@
require 'mspec/helpers/tmp'
require 'fileutils'
# Lower-level output speccing mechanism for a single
# output stream. Unlike OutputMatcher which provides
# methods to capture the output, we actually replace
# the FD itself so that there is no reliance on a
# certain method being used.
class OutputToFDMatcher
def initialize(expected, to)
@to, @expected = to, expected
case @to
when STDOUT
@to_name = "STDOUT"
when STDERR
@to_name = "STDERR"
when IO
@to_name = @to.object_id.to_s
else
raise ArgumentError, "#{@to.inspect} is not a supported output target"
end
end
def matches?(block)
old_to = @to.dup
out = File.open(tmp("mspec_output_to_#{$$}_#{Time.now.to_i}"), 'w+')
# Replacing with a file handle so that Readline etc. work
@to.reopen out
block.call
ensure
begin
@to.reopen old_to
out.rewind
@actual = out.read
case @expected
when Regexp
return !(@actual =~ @expected).nil?
else
return @actual == @expected
end
# Clean up
ensure
out.close unless out.closed?
FileUtils.rm out.path
end
return true
end
def failure_message()
["Expected (#{@to_name}): #{@expected.inspect}\n",
"#{'but got'.rjust(@to_name.length + 10)}: #{@actual.inspect}\nBacktrace"]
end
def negative_failure_message()
["Expected output (#{@to_name}) to NOT be:\n", @actual.inspect]
end
end
class Object
def output_to_fd(what, where = STDOUT)
OutputToFDMatcher.new what, where
end
end
require 'mspec/helpers/tmp'
require 'fileutils'

# Lower-level output speccing mechanism for a single
# output stream. Unlike OutputMatcher which provides
# methods to capture the output, we actually replace
# the FD itself so that there is no reliance on a
# certain method being used.
class OutputToFDMatcher
def initialize(expected, to)
@to, @expected = to, expected

case @to
when STDOUT
@to_name = "STDOUT"
when STDERR
@to_name = "STDERR"
when IO
@to_name = @to.object_id.to_s
else
raise ArgumentError, "#{@to.inspect} is not a supported output target"
end
end

def matches?(block)
old_to = @to.dup
out = File.open(tmp("mspec_output_to_#{$$}_#{Time.now.to_i}"), 'w+')

# Replacing with a file handle so that Readline etc. work
@to.reopen out

block.call

ensure
begin
@to.reopen old_to

out.rewind
@actual = out.read

case @expected
when Regexp
return !(@actual =~ @expected).nil?
else
return @actual == @expected
end

# Clean up
ensure
out.close unless out.closed?
FileUtils.rm out.path
end

return true
end

def failure_message()
["Expected (#{@to_name}): #{@expected.inspect}\n",
"#{'but got'.rjust(@to_name.length + 10)}: #{@actual.inspect}\nBacktrace"]
end

def negative_failure_message()
["Expected output (#{@to_name}) to NOT be:\n", @actual.inspect]
end
end

class Object
def output_to_fd(what, where = STDOUT)
OutputToFDMatcher.new what, where
end
end
@@ -8,8 +8,7 @@ def initialize(exception, message, &block)
def matches?(proc)
proc.call
return false
rescue Exception => err
@actual = err
rescue Exception => @actual
return false unless @exception === @actual
if @message then
case @message
@@ -38,7 +37,9 @@ def failure_message
end

def negative_failure_message
["Expected to not get #{@exception}#{%[ (#{@message})] if @message}", ""]
message = ["Expected to not get #{@exception}#{%[ (#{@message})] if @message}", ""]
message[1] = "but got #{@actual.class}#{%[ (#{@actual.message})] if @actual.message}" unless @actual.class == @exception
message
end
end

@@ -3,6 +3,6 @@
module StringSymbolAdapter
def convert_name(name)
version = SpecVersion.new(RUBY_VERSION) <=> "1.9"
version < 0 ? name.to_s : name
version < 0 ? name.to_s : name.to_sym
end
end
@@ -0,0 +1,28 @@
require 'mspec/matchers/stringsymboladapter'

class VariableMatcher
include StringSymbolAdapter

class << self
attr_accessor :variables_method, :description
end

def initialize(variable)
@variable = convert_name(variable)
end

def matches?(object)
@object = object
@object.send(self.class.variables_method).include? @variable
end

def failure_message
["Expected #{@object} to have #{self.class.description} '#{@variable}'",
"but it does not"]
end

def negative_failure_message
["Expected #{@object} NOT to have #{self.class.description} '#{@variable}'",
"but it does"]
end
end
@@ -1,8 +1,17 @@
require 'mspec/expectations/expectations'
require 'mspec/helpers/singleton_class'

class Object
alias_method :__mspec_object_id__, :object_id
end

module Mock
def self.reset
@mocks = @stubs = nil
@mocks = @stubs = @objects = nil
end

def self.objects
@objects ||= {}
end

def self.mocks
@@ -14,35 +23,51 @@ def self.stubs
end

def self.replaced_name(obj, sym)
:"__ms_#{obj.__id__}_#{sym}__"
:"__mspec_#{obj.__mspec_object_id__}_#{sym}__"
end

def self.replaced_key(obj, sym)
[replaced_name(obj, sym), obj, sym]
[replaced_name(obj, sym), sym]
end

def self.has_key?(keys, sym)
!!keys.find { |k| k.first == sym }
end

def self.replaced?(sym)
has_key?(mocks.keys, sym) or has_key?(stubs.keys, sym)
end

def self.replaced?(key)
!!(mocks.keys + stubs.keys).find { |k| k.first == key.first }
def self.clear_replaced(key)
mocks.delete key
stubs.delete key
end

def self.mock_respond_to?(obj, sym)
name = replaced_name(obj, :respond_to?)
if replaced? name
obj.__send__ name, sym
else
obj.respond_to? sym
end
end

def self.install_method(obj, sym, type=nil)
meta = class << obj; self; end
meta = obj.singleton_class

key = replaced_key obj, sym
if (sym.to_sym == :respond_to? or obj.respond_to?(sym)) and !replaced?(key)
meta.__send__ :alias_method, key.first, sym.to_sym
end

# LB: We don't support String eval, using define_method approach instead
# meta.class_eval <<-END
# def #{sym}(*args, &block)
# Mock.verify_call self, :#{sym}, *args, &block
# end
# END
meta.class_eval do
define_method(sym.to_sym) { |*args, &block| Mock.verify_call self, sym.to_sym, *args, &block }
sym = sym.to_sym

if (sym == :respond_to? or mock_respond_to?(obj, sym)) and !replaced?(key.first)
meta.__send__ :alias_method, key.first, sym
end


meta.class_eval <<-END
def #{sym}(*args, &block)
Mock.verify_call self, :#{sym}, *args, &block
end
END

proxy = MockProxy.new type

if proxy.mock?
@@ -55,6 +80,7 @@ def self.install_method(obj, sym, type=nil)
else
mocks[key] << proxy
end
objects[key] = obj

proxy
end
@@ -65,7 +91,7 @@ def self.name_or_inspect(obj)

def self.verify_count
mocks.each do |key, proxies|
replaced, obj, sym = *key
obj = objects[key]
proxies.each do |proxy|
qualifier, count = proxy.count
pass = case qualifier
@@ -81,8 +107,8 @@ def self.verify_count
false
end
unless pass
Expectation.fail_with(
"Mock '#{name_or_inspect obj}' expected to receive '#{sym}' " \
SpecExpectation.fail_with(
"Mock '#{name_or_inspect obj}' expected to receive '#{key.last}' " \
"#{qualifier.to_s.sub('_', ' ')} #{count} times",
"but received it #{proxy.calls} times")
end
@@ -92,68 +118,84 @@ def self.verify_count

def self.verify_call(obj, sym, *args, &block)
compare = *args
if RUBY_VERSION >= '1.9'
behaves_like_ruby_1_9 = *[]
if (behaves_like_ruby_1_9)
compare = compare.first if compare.length <= 1
end

key = replaced_key obj, sym
proxies = mocks[key] + stubs[key]
proxies.each do |proxy|
pass = case proxy.arguments
when :any_args
true
when :no_args
compare.nil?
else
proxy.arguments == compare
end
[mocks, stubs].each do |proxies|
proxies[key].each do |proxy|
pass = case proxy.arguments
when :any_args
true
when :no_args
compare.nil?
else
proxy.arguments == compare
end

if proxy.yielding?
if block
proxy.yielding.each do |args_to_yield|
if block.arity == -1 || block.arity == args_to_yield.size
block.call(*args_to_yield)
else
Expectation.fail_with(
"Mock '#{name_or_inspect obj}' asked to yield " \
"|#{proxy.yielding.join(', ')}| on #{sym}\n",
"but a block with arity #{block.arity} was passed")
if proxy.yielding?
if block
proxy.yielding.each do |args_to_yield|
if block.arity == -1 || block.arity == args_to_yield.size
block.call(*args_to_yield)
else
SpecExpectation.fail_with(
"Mock '#{name_or_inspect obj}' asked to yield " \
"|#{proxy.yielding.join(', ')}| on #{sym}\n",
"but a block with arity #{block.arity} was passed")
end
end
else
SpecExpectation.fail_with(
"Mock '#{name_or_inspect obj}' asked to yield " \
"|[#{proxy.yielding.join('], [')}]| on #{sym}\n",
"but no block was passed")
end
else
Expectation.fail_with(
"Mock '#{name_or_inspect obj}' asked to yield " \
"|[#{proxy.yielding.join('], [')}]| on #{sym}\n",
"but no block was passed")
end
end

if pass
proxy.called
return proxy.returning
if pass
proxy.called

if proxy.raising?
raise proxy.raising
else
return proxy.returning
end
end
end
end

if sym.to_sym == :respond_to?
return obj.__send__(replaced_name(obj, sym), compare)
mock_respond_to? obj, compare
else
Expectation.fail_with("Mock '#{name_or_inspect obj}': method #{sym}\n",
SpecExpectation.fail_with("Mock '#{name_or_inspect obj}': method #{sym}\n",
"called with unexpected arguments (#{Array(compare).join(' ')})")
end
end

def self.cleanup
symbols = mocks.keys + stubs.keys
symbols.uniq.each do |replaced, obj, sym|
meta = class << obj; self; end
objects.each do |key, obj|
if obj.kind_of? MockIntObject
clear_replaced key
next
end

replaced = key.first
sym = key.last
meta = obj.singleton_class

if meta.instance_methods.include?(replaced.to_s)
meta.__send__ :alias_method, sym.to_sym, replaced
if mock_respond_to? obj, replaced
meta.__send__ :alias_method, sym, replaced
meta.__send__ :remove_method, replaced
else
meta.__send__ :remove_method, sym.to_sym
meta.__send__ :remove_method, sym
end

clear_replaced key
end
ensure
reset
end
end
@@ -17,8 +17,12 @@ def should_not_receive(sym)
def mock(name, options={})
MockObject.new name, options
end

def mock_int(val)
MockIntObject.new(val)
end

def mock_numeric(name, options={})
NumericMockObject.new name, options
end

end
@@ -7,6 +7,7 @@ def initialize(name, options={})
def method_missing(sym, *args, &block)
@null ? self : super
end
private :method_missing
end

class NumericMockObject < Numeric
@@ -23,11 +24,36 @@ def singleton_method_added(val)
end
end

class MockIntObject
def initialize(val)
@value = val
@calls = 0

key = [self, :to_int]

Mock.objects[key] = self
Mock.mocks[key] << self
end

attr_reader :calls

def to_int
@calls += 1
@value
end

def count
[:at_least, 1]
end
end

class MockProxy
attr_reader :raising, :yielding

def initialize(type=nil)
@multiple_returns = nil
@returning = nil
@raising = nil
@yielding = []
@arguments = :any_args
@type = type || :mock
@@ -103,7 +129,8 @@ def any_number_of_times
def with(*args)
raise ArgumentError, "you must specify the expected arguments" if args.empty?
@arguments = *args
if RUBY_VERSION >= '1.9'
behaves_like_ruby_1_9 = *[]
if (behaves_like_ruby_1_9)
@arguments = @arguments.first if @arguments.length <= 1
end
self
@@ -123,15 +150,23 @@ def and_return(*args)
self
end

def and_raise(exception)
if exception.kind_of? String
@raising = RuntimeError.new exception
else
@raising = exception
end
end

def raising?
@raising != nil
end

def and_yield(*args)
@yielding << args
self
end

def yielding
@yielding
end


def yielding?
!@yielding.empty?
end
@@ -2,6 +2,7 @@
require 'mspec/runner/mspec'
require 'mspec/runner/context'
require 'mspec/runner/example'
require 'mspec/runner/exception'
require 'mspec/runner/object'
require 'mspec/runner/formatters'
require 'mspec/runner/actions'
@@ -2,6 +2,8 @@

class DebugAction < ActionFilter
def before(state)
require 'rubygems'
require 'ruby-debug'
Kernel.debugger if self === state.description
end

@@ -32,9 +32,15 @@ def initialize(mod, options=nil)
@parents = [self]
@children = []

@mock_verify = lambda { Mock.verify_count }
@mock_cleanup = lambda { Mock.cleanup }
@expectation_missing = lambda { raise ExpectationNotFoundError }
@mock_verify = Proc.new { Mock.verify_count }
@mock_cleanup = Proc.new { Mock.cleanup }
@expectation_missing = Proc.new { raise SpecExpectationNotFoundError }
end

# Remove caching when a ContextState is dup'd for shared specs.
def initialize_copy(other)
@pre = {}
@post = {}
end

# Returns true if this is a shared +ContextState+. Essentially, when
@@ -47,13 +53,19 @@ def shared?
# the +parents+ list.
def parent=(parent)
@description = nil
@parent = parent
parent.child self if parent and not shared?

state = parent
while state
parents.unshift state
state = state.parent
if shared?
@parent = nil
else
@parent = parent
parent.child self if parent

@parents = [self]
state = parent
while state
@parents.unshift state
state = state.parent
end
end
end

@@ -63,6 +75,29 @@ def child(child)
@children << child
end

# Adds a nested ContextState in a shared ContextState to a containing
# ContextState.
#
# Normal adoption is from the parent's perspective. But adopt is a good
# verb and it's reasonable for the child to adopt the parent as well. In
# this case, manipulating state from inside the child avoids needlessly
# exposing the state to manipulate it externally in the dup. (See
# #it_should_behave_like)
def adopt(parent)
self.parent = parent

@examples = @examples.map do |example|
example = example.dup
example.context = self
example
end

children = @children
@children = []

children.each { |child| child.dup.adopt self }
end

# Returns a list of all before(+what+) blocks from self and any parents.
def pre(what)
@pre[what] ||= parents.inject([]) { |l, s| l.push(*s.before(what)) }
@@ -92,7 +127,9 @@ def after(what, &block)
# Creates an ExampleState instance for the block and stores it
# in a list of examples to evaluate unless the example is filtered.
def it(desc, &block)
#RHO
puts "- it \"#{desc}\""
#RHO
example = ExampleState.new(self, desc, block)
MSpec.actions :add, example
return if MSpec.guarded?
@@ -101,15 +138,14 @@ def it(desc, &block)

# Evaluates the block and resets the toplevel +ContextState+ to #parent.
def describe(&block)
puts "describe \"#{self.to_s}\""
@parsed = protect @to_s, block, false
MSpec.register_current parent
MSpec.register_shared self if shared?
end

# Returns a description string generated from self and all parents
def description
@description ||= parents.map { |p| p.to_s }.join(" ")
@description ||= parents.map { |p| p.to_s }.compact.join(" ")
end

# Injects the before/after blocks and examples from the shared
@@ -121,11 +157,20 @@ def it_should_behave_like(desc)
raise Exception, "Unable to find shared 'describe' for #{desc}"
end

state.examples.each { |ex| ex.context = self; @examples << ex }
state.before(:all).each { |b| before :all, &b }
state.before(:each).each { |b| before :each, &b }
state.after(:each).each { |b| after :each, &b }
state.after(:all).each { |b| after :all, &b }

state.examples.each do |example|
example = example.dup
example.context = self
@examples << example
end

state.children.each do |child|
child.dup.adopt self
end
end

# Evaluates each block in +blocks+ using the +MSpec.protect+ method
@@ -164,7 +209,7 @@ def process
if example
passed = protect nil, example
MSpec.actions :example, state, example
protect nil, @expectation_missing unless MSpec.expectation? or not passed
#protect nil, @expectation_missing unless MSpec.expectation? or not passed
end
protect "after :each", post(:each)
protect "Mock.verify_count", @mock_verify
@@ -1,8 +1,6 @@
class ExceptionState
attr_reader :description, :describe, :it, :exception

PATH = /#{File.expand_path(File.dirname(__FILE__) + '/../../..')}/

def initialize(state, location, exception)
@exception = exception

@@ -18,26 +16,29 @@ def initialize(state, location, exception)
end

def failure?
[ExpectationNotMetError, ExpectationNotFoundError].any? { |e| @exception.is_a? e }
[SpecExpectationNotMetError, SpecExpectationNotFoundError].any? { |e| @exception.is_a? e }
end

def message
if @exception.message.empty?
"<No message>"
elsif @exception.class == ExpectationNotMetError ||
@exception.class == ExpectationNotFoundError
elsif @exception.class == SpecExpectationNotMetError ||
@exception.class == SpecExpectationNotFoundError
@exception.message
else
"#{@exception.class}: #{@exception.message}"
end
end

def backtrace
@backtrace_filter ||= MSpecScript.config[:backtrace_filter]

begin
bt = @exception.awesome_backtrace.show.split "\n"
rescue Exception
bt = @exception.backtrace || []
end
bt.reject { |line| PATH =~ line }.join("\n")

bt.select { |line| $MSPEC_DEBUG or @backtrace_filter !~ line }.join("\n")
end
end
@@ -8,3 +8,4 @@
require 'mspec/runner/formatters/spinner'
require 'mspec/runner/formatters/method'
require 'mspec/runner/formatters/yaml'
require 'mspec/runner/formatters/profile'
@@ -14,6 +14,8 @@ def initialize(out=nil)
else
@out = File.open out, "w"
end

@current_state = nil
end

# Creates the +TimerAction+ and +TallyAction+ instances and
@@ -28,6 +30,13 @@ def register
MSpec.register :before, self
MSpec.register :after, self
MSpec.register :finish, self
MSpec.register :abort, self
end

def abort
if @current_state
puts " aborting example: #{@current_state.description}"
end
end

# Returns true if any exception is raised while running
@@ -47,7 +56,8 @@ def failure?

# Callback for the MSpec :before event. Resets the
# +#exception?+ and +#failure+ flags.
def before(state = nil)
def before(state=nil)
@current_state = state
@failure = @exception = false
end

@@ -65,9 +75,11 @@ def exception(exception)
# Callback for the MSpec :after event. Prints an indicator
# for the result of evaluating this example as follows:
# . = No failure or error
# F = An ExpectationNotMetError was raised
# E = Any exception other than ExpectationNotMetError
# F = An SpecExpectationNotMetError was raised
# E = Any exception other than SpecExpectationNotMetError
def after(state = nil)
@current_state = nil

unless exception?
print "."
else
@@ -0,0 +1,70 @@
require 'mspec/expectations/expectations'
require 'mspec/runner/formatters/dotted'

class ProfileFormatter < DottedFormatter
def initialize(out=nil)
super

@describe_name = nil
@describe_time = nil
@describes = []
@its = []
end

def register
super
MSpec.register :enter, self
end

# Callback for the MSpec :enter event. Prints the
# +describe+ block string.
def enter(describe)
if @describe_time
@describes << [@describe_name, Time.now.to_f - @describe_time]
end

@describe_name = describe
@describe_time = Time.now.to_f
end

# Callback for the MSpec :before event. Prints the
# +it+ block string.
def before(state)
super

@it_name = state.it
@it_time = Time.now.to_f
end

# Callback for the MSpec :after event. Prints a
# newline to finish the description string output.
def after(state)
@its << [@describe_name, @it_name, Time.now.to_f - @it_time]
super
end

def finish
puts "\nProfiling info:"

desc = @describes.sort { |a,b| b.last <=> a.last }
desc.delete_if { |a| a.last <= 0.001 }
show = desc[0, 100]

puts "Top #{show.size} describes:"

show.each do |des, time|
printf "%3.3f - %s\n", time, des
end

its = @its.sort { |a,b| b.last <=> a.last }
its.delete_if { |a| a.last <= 0.001 }
show = its[0, 100]

puts "\nTop #{show.size} its:"
show.each do |des, it, time|
printf "%3.3f - %s %s\n", time, des, it
end

super
end
end
@@ -1,8 +1,7 @@
require 'mspec/runner/context'
require 'mspec/runner/exception'
require 'mspec/runner/tag'
require 'mspec/fileutils'
require 'mspec/pp'
require 'fileutils'

module MSpec
@count = 0
@@ -24,25 +23,26 @@ module MSpec
@modes = []
@shared = {}
@guarded = []
@features = {}
@exception = nil
@randomize = nil
@expectation = nil
@expectations = false
@backtrace = false
@backtrace = false
@file_count = 0

def self.exc_count
@exc_count
end

def self.count
@count
end

def self.file_count
@file_count
end

def self.backtrace=(backtrace)
@backtrace = backtrace
end
@@ -62,7 +62,7 @@ def self.process
files
actions :finish
end

def self.files
return unless files = retrieve(:files)

@@ -90,8 +90,11 @@ def self.protect(location, &block)
rescue SystemExit
raise
rescue Exception => exc
#RHO
puts "FAIL: #{current} - #{exc.message}\n" + (@backtrace ? exc.backtrace.join("\n") : "")
@exc_count+=1
#RHO

register_exit 1
actions :exception, ExceptionState.new(current && current.state, location, exc)
return false
@@ -186,6 +189,18 @@ def self.mode?(mode)
retrieve(:modes).include? mode
end

def self.enable_feature(feature)
retrieve(:features)[feature] = true
end

def self.disable_feature(feature)
retrieve(:features)[feature] = false
end

def self.feature_enabled?(feature)
retrieve(:features)[feature] || false
end

def self.retrieve(symbol)
instance_variable_get :"@#{symbol}"
end
@@ -289,14 +304,12 @@ def self.read_tags(keys)
tags = []
file = tags_file
if File.exist? file
File.open(file, "r") do |f|
unless File.directory?(f)
f.each_line do |line|
line.chomp!
next if line.empty?
tag = SpecTag.new line.chomp
tags << tag if keys.include? tag.tag
end
File.open(file, "rb") do |f|
f.each_line do |line|
line.chomp!
next if line.empty?
tag = SpecTag.new line.chomp
tags << tag if keys.include? tag.tag
end
end
end
@@ -309,7 +322,7 @@ def self.write_tags(tags)
file = tags_file
path = File.dirname file
FileUtils.mkdir_p path unless File.exist? path
File.open(file, "w") do |f|
File.open(file, "wb") do |f|
tags.each { |t| f.puts t }
end
end
@@ -322,11 +335,11 @@ def self.write_tag(tag)
path = File.dirname file
FileUtils.mkdir_p path unless File.exist? path
if File.exist? file
File.open(file, "r") do |f|
File.open(file, "rb") do |f|
f.each_line { |line| return false if line.chomp == string }
end
end
File.open(file, "a") { |f| f.puts string }
File.open(file, "ab") { |f| f.puts string }
return true
end

@@ -339,7 +352,7 @@ def self.delete_tag(tag)
file = tags_file
if File.exist? file
lines = IO.readlines(file)
File.open(file, "w") do |f|
File.open(file, "wb") do |f|
lines.each do |line|
unless pattern =~ line.chomp
f.puts line unless line.empty?
@@ -19,6 +19,10 @@ def it_should_behave_like(desc)
MSpec.current.it_should_behave_like desc
end

# For ReadRuby compatiability
def doc(*a)
end

alias_method :context, :describe
alias_method :specify, :it
end
@@ -4,7 +4,7 @@ class Object
def it_behaves_like(desc, meth, obj=nil)
send :before, :all do
@method = meth
@object = obj if obj
@object = obj
end

send :it_should_behave_like, desc.to_s
@@ -60,8 +60,14 @@ class NameMap
MSpecScript
MkSpec
DTracer
Etc
FileUtils
MSpecOption
MSpecOptions
NameMap
OptionParser
RbConfig
SpecVersion
YAML
]

@@ -96,11 +102,12 @@ def map(hash, constants, mod=nil)
next unless m and not @seen[m]
@seen[m] = true

ms = m.methods false
ms = m.methods(false).map { |x| x.to_s }
hash["#{name}."] = ms.sort unless ms.empty?

ms = m.public_instance_methods(false) +
m.protected_instance_methods(false)
ms.map! { |x| x.to_s }
hash["#{name}#"] = ms.sort unless ms.empty?

map hash, m.constants, name
@@ -102,8 +102,10 @@ def process(argv, entry, opt, arg)
if option.arg?
arg = argv.shift if arg.nil?
raise ParseError, "No argument provided for #{opt}" unless arg
option.block[arg] if option.block
else
option.block[] if option.block
end
option.block[arg] if option.block
end
option
end
@@ -205,32 +207,38 @@ def name

def targets
on("-t", "--target", "TARGET",
"Implementation to run the specs, where:") do |t|
"Implementation to run the specs, where TARGET is:") do |t|
case t
when 'r', 'ruby'
config[:target] = 'ruby'
when 'r19', 'ruby19'
config[:target] = 'ruby1.9'
when 'x', 'rubinius'
config[:target] = './bin/rbx'
when 'x19', 'rubinius19'
config[:target] = './bin/rbx -X19'
when 'X', 'rbx'
config[:target] = 'rbx'
when 'j', 'jruby'
config[:target] = 'jruby'
when 'i','ironruby'
config[:target] = 'ir'
when 'm','maglev'
config[:target] = 'maglev-ruby'
else
config[:target] = t
end
end

doc ""
doc " r or ruby invokes ruby in PATH"
doc " r19, ruby19 or ruby1.9 invokes ruby1.9 in PATH"
doc " x or rubinius invokes ./bin/rbx"
doc " X or rbx invokes rbx in PATH"
doc " j or jruby invokes jruby in PATH"
doc " i or ironruby invokes ir in PATH\n"
doc " r or ruby invokes ruby in PATH"
doc " r19, ruby19 invokes ruby1.9 in PATH"
doc " x or rubinius invokes ./bin/rbx"
doc " X or rbx invokes rbx in PATH"
doc " j or jruby invokes jruby in PATH"
doc " i or ironruby invokes ir in PATH"
doc " m or maglev invokes maglev-ruby in PATH"
doc " full path to EXE invokes EXE directly\n"

on("-T", "--target-opt", "OPT",
"Pass OPT as a flag to the target implementation") do |t|
@@ -270,6 +278,8 @@ def formatters
config[:formatter] = MethodFormatter
when 'y', 'yaml'
config[:formatter] = YamlFormatter
when 'p', 'profile'
config[:formatter] = ProfileFormatter
else
puts "Unknown format: #{o}"
puts @parser
@@ -438,4 +448,11 @@ def actions
config[:gdb] = true
end
end

def debug
on("-d", "--debug",
"Set MSpec debugging flag for more verbose output") do
$MSPEC_DEBUG = true
end
end
end
@@ -3,6 +3,6 @@
RUBY_NAME = RUBY_ENGINE
else
require 'rbconfig'
RUBY_NAME = Config::CONFIG["RUBY_INSTALL_NAME"] || Config::CONFIG["ruby_install_name"]
RUBY_NAME = ::RbConfig::CONFIG["RUBY_INSTALL_NAME"] || RbConfig::CONFIG["ruby_install_name"]
end
end
@@ -141,6 +141,7 @@ def custom_register
def signals
if config[:abort]
Signal.trap "INT" do
MSpec.actions :abort
puts "\nProcess aborted!"
exit! 1
end
@@ -161,20 +162,19 @@ def signals
#
# If unable to resolve +partial+, returns <tt>Dir[partial]</tt>.
def entries(partial)
file = partial + "_spec.iseq"
file = partial + "_spec.rb"
patterns = [partial]
patterns << file
puts "file: #{file}"
if config[:prefix]
patterns << File.join(config[:prefix], partial)
patterns << File.join(config[:prefix], file)
end

patterns.each do |pattern|
expanded = File.expand_path(pattern)
return [pattern] if File.file?(expanded)
return [expanded] if File.file?(expanded)

specs = File.join(pattern, "/**/*_spec.iseq")
specs = File.join(pattern, "/**/*_spec.rb")
specs = File.expand_path(specs) rescue specs
return Dir[specs].sort if File.directory?(expanded)
end
@@ -1,5 +1,5 @@
require 'mspec/utils/version'

module MSpec
VERSION = SpecVersion.new "1.5.10"
VERSION = SpecVersion.new "1.5.17"
end
@@ -28,7 +28,7 @@ def build_extension(name, arch, src_files)
args << "-D__NEW__" if use_own_stlport
args << "-I#{$stlport_includes}" if use_own_stlport

args << "-I#{$rootdir}/platform/shared/ruby/linux"
args << "-I#{$rootdir}/platform/shared/ruby/android"
args << "-I#{$rootdir}/platform/shared/ruby/generated"

cc_compile f, $tempdir, args or exit 1