Skip to content

Commit

Permalink
Showing 68 changed files with 1,386 additions and 332 deletions.
40 changes: 40 additions & 0 deletions configure
Original file line number Diff line number Diff line change
@@ -1239,6 +1239,46 @@ int main() { return tgetnum(""); }
@defines << "HAVE_GETTID"
end

if has_struct_member("stat", "st_atim", ["sys/stat.h"])
@defines << "HAVE_STRUCT_STAT_ST_ATIM"
end

if has_struct_member("stat", "st_atimespec", ["sys/stat.h"])
@defines << "HAVE_STRUCT_STAT_ST_ATIMESPEC"
end

if has_struct_member("stat", "st_atimensec", ["sys/stat.h"])
@defines << "HAVE_STRUCT_STAT_ST_ATIMENSEC"
end

if has_struct_member("stat", "st_mtim", ["sys/stat.h"])
@defines << "HAVE_STRUCT_STAT_ST_MTIM"
end

if has_struct_member("stat", "st_mtimespec", ["sys/stat.h"])
@defines << "HAVE_STRUCT_STAT_ST_MTIMESPEC"
end

if has_struct_member("stat", "st_mtimensec", ["sys/stat.h"])
@defines << "HAVE_STRUCT_STAT_ST_MTIMENSEC"
end

if has_struct_member("stat", "st_ctim", ["sys/stat.h"])
@defines << "HAVE_STRUCT_STAT_ST_CTIM"
end

if has_struct_member("stat", "st_ctimespec", ["sys/stat.h"])
@defines << "HAVE_STRUCT_STAT_ST_CTIMESPEC"
end

if has_struct_member("stat", "st_ctimensec", ["sys/stat.h"])
@defines << "HAVE_STRUCT_STAT_ST_CTIMENSEC"
end

if has_struct_member("stat", "st_birthtimespec", ["sys/stat.h"])
@defines << "HAVE_ST_BIRTHTIME"
end

# glibc has useless lchmod() so we don't try to use lchmod() on linux
if !@linux and has_function("lchmod", ["sys/stat.h", "unistd.h"])
@have_lchmod = true
5 changes: 5 additions & 0 deletions kernel/bootstrap/stat.rb
Original file line number Diff line number Diff line change
@@ -87,6 +87,11 @@ def ctime
raise PrimitiveFailure, "Rubinius::Stat#ctime primitive failed"
end

def birthtime
Rubinius.primitive :stat_birthtime
raise NotImplementedError, "birthtime() function is unimplemented on this machine"
end

def inspect
"#<#{self.class.name} dev=0x#{self.dev.to_s(16)}, ino=#{self.ino}, " \
"mode=#{sprintf("%07d", self.mode.to_s(8).to_i)}, nlink=#{self.nlink}, " \
8 changes: 6 additions & 2 deletions kernel/common/binding.rb
Original file line number Diff line number Diff line change
@@ -3,8 +3,8 @@ class Binding
attr_accessor :compiled_code
attr_accessor :constant_scope
attr_accessor :proc_environment
attr_accessor :self
attr_accessor :location
attr_accessor :receiver

def from_proc?
@proc_environment
@@ -29,7 +29,7 @@ def self.self_context(recv, variables)
def self.setup(variables, code, constant_scope, recv=nil, location=nil)
bind = allocate()

bind.self = self_context(recv, variables)
bind.receiver = self_context(recv, variables)
bind.variables = variables
bind.compiled_code = code
bind.constant_scope = constant_scope
@@ -54,4 +54,8 @@ def eval(expr, filename=nil, lineno=nil)

Kernel.eval(expr, self, filename, lineno)
end

def local_variables
variables.local_variables
end
end
5 changes: 5 additions & 0 deletions kernel/common/dir.rb
Original file line number Diff line number Diff line change
@@ -174,6 +174,11 @@ def each
self
end

def fileno
Rubinius.primitive :dir_fileno
raise PrimitiveFailure, "Dir#fileno primitive failed"
end

attr_reader :path

alias_method :to_path, :path
360 changes: 218 additions & 142 deletions kernel/common/enumerable.rb

Large diffs are not rendered by default.

24 changes: 2 additions & 22 deletions kernel/common/eval.rb
Original file line number Diff line number Diff line change
@@ -110,28 +110,8 @@ module Kernel
# Names of local variables at point of call (including evaled)
#
def local_variables
locals = []

scope = Rubinius::VariableScope.of_sender

# Ascend up through all applicable blocks to get all vars.
while scope
if scope.method.local_names
scope.method.local_names.each do |name|
locals << name
end
end

if dyn = scope.dynamic_locals
dyn.keys.each do |name|
locals << name unless locals.include?(name)
end
end

scope = scope.parent
end

locals
scope.local_variables
end
module_function :local_variables

@@ -176,7 +156,7 @@ def eval(string, binding=nil, filename=nil, lineno=nil)
c = Rubinius::ToolSets::Runtime::Compiler
be = c.construct_block string, binding, filename, lineno

result = be.call_on_instance(binding.self)
result = be.call_on_instance(binding.receiver)
binding.constant_scope = existing_scope
result
end
3 changes: 3 additions & 0 deletions kernel/common/exception.rb
Original file line number Diff line number Diff line change
@@ -213,6 +213,9 @@ def to_s
end
end

class UncaughtThrowError < ArgumentError
end

class IndexError < StandardError
end

134 changes: 83 additions & 51 deletions kernel/common/file.rb
Original file line number Diff line number Diff line change
@@ -103,6 +103,69 @@ def self.atime(path)
Stat.new(path).atime
end

##
# Returns the change time for the named file (the
# time at which directory information about the
# file was changed, not the file itself).
#
# File.ctime("testfile") #=> Wed Apr 09 08:53:13 CDT 2003
def self.ctime(path)
Stat.new(path).ctime
end

##
# Returns the birthtime for the named file
#
# Note this is not supported on all platforms.
# See stat(2) for more information.
def self.birthtime(path)
Stat.new(path).birthtime
end

##
# Returns the modification time for the named file as a Time object.
#
# File.mtime("testfile") #=> Tue Apr 08 12:58:04 CDT 2003
def self.mtime(path)
Stat.new(path).mtime
end

##
# Sets the access and modification times of each named
# file to the first two arguments. Returns the number
# of file names in the argument list.
# #=> Integer
def self.utime(a_in, m_in, *paths)
a_in ||= Time.now
a_in_usec = if a_in.is_a?(Time) || a_in.is_a?(Float) || a_in.is_a?(Rational)
Time.at(a_in).usec
else
0
end
m_in ||= Time.now
m_in_usec = if m_in.is_a?(Time) || m_in.is_a?(Float) || m_in.is_a?(Rational)
Time.at(m_in).usec
else
0
end

FFI::MemoryPointer.new(POSIX::TimeVal, 2) do |ptr|
atime = POSIX::TimeVal.new ptr
mtime = POSIX::TimeVal.new ptr[1]
atime[:tv_sec] = a_in.to_i
atime[:tv_usec] = a_in_usec

mtime[:tv_sec] = m_in.to_i
mtime[:tv_usec] = m_in_usec

paths.each do |path|

n = POSIX.utimes(Rubinius::Type.coerce_to_path(path), ptr)
Errno.handle unless n == 0
end
end
end

##
# Returns the last component of the filename given
# in file_name, which must be formed using forward
@@ -304,16 +367,6 @@ def self.lchown(owner, group, *paths)
paths.size
end

##
# Returns the change time for the named file (the
# time at which directory information about the
# file was changed, not the file itself).
#
# File.ctime("testfile") #=> Wed Apr 09 08:53:13 CDT 2003
def self.ctime(path)
Stat.new(path).ctime
end

##
# Returns true if the named file is a directory, false otherwise.
#
@@ -816,14 +869,6 @@ def self.lstat(path)
Stat.lstat path
end

##
# Returns the modification time for the named file as a Time object.
#
# File.mtime("testfile") #=> Tue Apr 08 12:58:04 CDT 2003
def self.mtime(path)
Stat.new(path).mtime
end

def self.path(obj)
return obj.to_path if obj.respond_to? :to_path

@@ -1072,31 +1117,6 @@ def self.unlink(*paths)
paths.size
end

##
# Sets the access and modification times of each named
# file to the first two arguments. Returns the number
# of file names in the argument list.
# #=> Integer
def self.utime(a_in, m_in, *paths)
a_in ||= Time.now
m_in ||= Time.now
FFI::MemoryPointer.new(POSIX::TimeVal, 2) do |ptr|
atime = POSIX::TimeVal.new ptr
mtime = POSIX::TimeVal.new ptr[1]
atime[:tv_sec] = a_in.to_i
atime[:tv_usec] = 0

mtime[:tv_sec] = m_in.to_i
mtime[:tv_usec] = 0

paths.each do |path|

n = POSIX.utimes(Rubinius::Type.coerce_to_path(path), ptr)
Errno.handle unless n == 0
end
end
end

def self.world_readable?(path)
path = Rubinius::Type.coerce_to_path path
return nil unless exist? path
@@ -1222,10 +1242,30 @@ def initialize(path_or_fd, mode=undefined, perm=undefined, options=undefined)

private :initialize

##
# see File.atime
def atime
Stat.new(@path).atime
end

##
# see File.ctime
def ctime
Stat.new(@path).ctime
end

##
# see File.birthtime
def birthtime
Stat.new(@path).birthtime
end

##
# see File.mtime
def mtime
Stat.new(@path).mtime
end

def reopen(other, mode = 'r+')
rewind unless closed?
unless other.kind_of? IO
@@ -1234,10 +1274,6 @@ def reopen(other, mode = 'r+')
super(other, mode)
end

def ctime
Stat.new(@path).ctime
end

def flock(const)
const = Rubinius::Type.coerce_to const, Integer, :to_int

@@ -1251,10 +1287,6 @@ def lstat
Stat.lstat @path
end

def mtime
Stat.new(@path).mtime
end

def stat
Stat.fstat @descriptor
end
28 changes: 28 additions & 0 deletions kernel/common/float.rb
Original file line number Diff line number Diff line change
@@ -285,4 +285,32 @@ def floor
return int if self > 0 or self == int
return int - 1
end

def next_float
return NAN if self.nan?
return -MAX if self == -INFINITY
return INFINITY if self == MAX
return Math.ldexp(0.5, MIN_EXP - MANT_DIG + 1) if self.zero?

frac, exp = Math.frexp self

if frac == -0.5
frac *= 2
exp -= 1
end

smallest_frac = EPSILON / 2
smallest_frac = Math.ldexp(smallest_frac, MIN_EXP - exp) if exp < MIN_EXP

result_frac = frac + smallest_frac

return -0.0 if result_frac.zero? && frac < 0
return 0.0 if result_frac.zero? && frac > 0

return Math.ldexp result_frac, exp
end

def prev_float
return -(-self).next_float
end
end
34 changes: 34 additions & 0 deletions kernel/common/method.rb
Original file line number Diff line number Diff line change
@@ -141,6 +141,12 @@ def to_proc
Proc.from_method self
end

##
# Calls curry on the method in proc representation
def curry(n = nil)
to_proc.curry(n)
end

##
# Detach this Method from the receiver object it is bound to and create an
# UnboundMethod object. Populates the UnboundMethod with the method data as
@@ -152,6 +158,20 @@ def unbind
UnboundMethod.new(@defined_in, @executable, @receiver.class, @name)
end

def super_method
superclass = @defined_in.direct_superclass

if superclass
mod, entry = superclass.lookup_method(@name)

if entry && entry.visibility != :undef
return Method.new(@receiver, superclass, entry.method, @name)
end
end

return nil
end

end

##
@@ -282,4 +302,18 @@ def owner
@defined_in
end
end

def super_method
superclass = @defined_in.direct_superclass

if superclass
mod, entry = superclass.lookup_method(@name)

if entry && entry.visibility != :undef
return UnboundMethod.new(superclass, entry.method, @defined_in, @name)
end
end

return nil
end
end
3 changes: 1 addition & 2 deletions kernel/common/process_mirror.rb
Original file line number Diff line number Diff line change
@@ -185,8 +185,7 @@ def convert_to_fd(obj, target)
[obj[0], File::RDONLY, 0644]
when 2
if obj[0] == :child
fd = convert_to_fd obj[1], target
fd.kind_of?(::Fixnum) ? -(fd + 1) : fd
convert_to_fd obj[1], target
else
[obj[0], convert_file_mode(obj[1]), 0644]
end
2 changes: 1 addition & 1 deletion kernel/common/throw_catch.rb
Original file line number Diff line number Diff line change
@@ -34,7 +34,7 @@ def catch(obj = Object.new, &block)

def throw(obj, value=nil)
unless Rubinius::ThrownValue.available? obj
raise ArgumentError, "uncaught throw #{obj.inspect}"
raise UncaughtThrowError, "uncaught throw #{obj.inspect}"
end

Rubinius.throw obj, value
26 changes: 26 additions & 0 deletions kernel/common/variable_scope.rb
Original file line number Diff line number Diff line change
@@ -101,6 +101,32 @@ def local_layout
out
end

# Returns the names of local variables available in this scope
#
def local_variables
locals = []
scope = self

# Ascend up through all applicable blocks to get all vars.
while scope
if scope.method.local_names
scope.method.local_names.each do |name|
locals << name
end
end

if dyn = scope.dynamic_locals
dyn.keys.each do |name|
locals << name unless locals.include?(name)
end
end

scope = scope.parent
end

locals
end

def exitted?
@exitted
end
29 changes: 29 additions & 0 deletions spec/ruby/core/binding/local_variables_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
require File.expand_path('../../../spec_helper', __FILE__)

describe "Binding#local_variables" do
it "returns an Array" do
binding.local_variables.should be_kind_of(Array)
end

it "includes local variables in the current scope" do
a = 1
b = nil
binding.local_variables.should == [:a, :b]
end

it "includes local variables defined after calling binding.local_variables" do
binding.local_variables.should == [:a, :b]
a = 1
b = 2
end

it "includes local variables of inherited scopes and eval'ed context" do
p = proc { |a| b = 1; eval("c = 2; binding.local_variables") }
p.call.should == [:c, :a, :b, :p]
end

it "includes shadowed local variables only once" do
a = 1
proc { a = 2; binding.local_variables }.call.should == [:a]
end
end
11 changes: 11 additions & 0 deletions spec/ruby/core/binding/receiver_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
require File.expand_path('../../../spec_helper', __FILE__)
require File.expand_path('../fixtures/classes', __FILE__)

describe "Binding#receiver" do
it "returns the object to which binding is bound" do
obj = BindingSpecs::Demo.new(1)
obj.get_binding.receiver.should == obj

binding.receiver.should == self
end
end
35 changes: 16 additions & 19 deletions spec/ruby/core/dir/fileno_spec.rb
Original file line number Diff line number Diff line change
@@ -2,29 +2,26 @@
require File.expand_path('../fixtures/common', __FILE__)

describe "Dir#fileno" do
platform_is_not :windows do
before :each do
@name = tmp("fileno")
mkdir_p @name
end

ruby_version_is "2.2" do
platform_is_not :windows do
before :each do
@name = tmp("fileno")
mkdir_p @name
end

after :each do
rm_r @name
end
after :each do
rm_r @name
end

it "returns the file descriptor of the dir" do
dir = Dir.new(@name)
dir.fileno.should.be_kind_of(Fixnum)
end
it "returns the file descriptor of the dir" do
dir = Dir.new(@name)
dir.fileno.should.be_kind_of(Fixnum)
end
end

platform_is :windows do
it "raises an error" do
dir = Dir.new('.')
lambda { dir.fileno }.to raise_error(NotImplementedError)
end
platform_is :windows do
it "raises an error" do
dir = Dir.new('.')
lambda { dir.fileno }.to raise_error(NotImplementedError)
end
end
end
2 changes: 1 addition & 1 deletion spec/ruby/core/enumerable/each_cons_spec.rb
Original file line number Diff line number Diff line change
@@ -14,7 +14,7 @@
acc.should == @in_threes
end

it "raises an Argument Error if there is not a single parameter > 0" do
it "raises an ArgumentError if there is not a single parameter > 0" do
lambda{ @enum.each_cons(0){} }.should raise_error(ArgumentError)
lambda{ @enum.each_cons(-2){} }.should raise_error(ArgumentError)
lambda{ @enum.each_cons{} }.should raise_error(ArgumentError)
2 changes: 1 addition & 1 deletion spec/ruby/core/enumerable/each_entry_spec.rb
Original file line number Diff line number Diff line change
@@ -26,7 +26,7 @@
ScratchPad.recorded.should == [[:a, 0], [:b, 1]]
end

it "raises an Argument error when extra arguments" do
it "raises an ArgumentError when extra arguments" do
lambda { @enum.each_entry("one").to_a }.should raise_error(ArgumentError)
lambda { @enum.each_entry("one"){}.to_a }.should raise_error(ArgumentError)
end
2 changes: 1 addition & 1 deletion spec/ruby/core/enumerable/each_slice_spec.rb
Original file line number Diff line number Diff line change
@@ -14,7 +14,7 @@
acc.should == @sliced
end

it "raises an Argument Error if there is not a single parameter > 0" do
it "raises an ArgumentError if there is not a single parameter > 0" do
lambda{ @enum.each_slice(0){} }.should raise_error(ArgumentError)
lambda{ @enum.each_slice(-2){} }.should raise_error(ArgumentError)
lambda{ @enum.each_slice{} }.should raise_error(ArgumentError)
38 changes: 38 additions & 0 deletions spec/ruby/core/enumerable/max_by_spec.rb
Original file line number Diff line number Diff line change
@@ -40,4 +40,42 @@
end

it_behaves_like :enumerable_enumeratorized_with_origin_size, :max_by

context "when called with an argument n" do
before :each do
@enum = EnumerableSpecs::Numerous.new(101, 55, 1, 20, 33, 500, 60)
end

context "without a block" do
it "returns an enumerator" do
@enum.max_by(2).should be_an_instance_of(enumerator_class)
end
end

context "with a block" do
it "returns an array containing the maximum n elements based on the block's value" do
result = @enum.max_by(3) { |i| i.to_s }
result.should == [60, 55, 500]
end

context "on a enumerable of length x where x < n" do
it "returns an array containing the maximum n elements of length n" do
result = @enum.max_by(500) { |i| i.to_s }
result.length.should == 7
end
end

context "when n is negative" do
it "raises an ArgumentError" do
lambda { @enum.max_by(-1) { |i| i.to_s } }.should raise_error(ArgumentError)
end
end

context "when n is nil" do
it "returns the maximum element" do
@enum.max_by(nil) { |i| i.to_s }.should == 60
end
end
end
end
end
63 changes: 48 additions & 15 deletions spec/ruby/core/enumerable/max_spec.rb
Original file line number Diff line number Diff line change
@@ -3,13 +3,11 @@

describe "Enumerable#max" do
before :each do
@a = EnumerableSpecs::EachDefiner.new( 2, 4, 6, 8, 10 )

@e_strs = EnumerableSpecs::EachDefiner.new("333", "22", "666666", "1", "55555", "1010101010")
@e_ints = EnumerableSpecs::EachDefiner.new( 333, 22, 666666, 55555, 1010101010)
end

it "max should return the maximum element" do
it "returns the maximum element" do
EnumerableSpecs::Numerous.new.max.should == 6
end

@@ -52,24 +50,25 @@
end.should raise_error(ArgumentError)
end

it "returns the maximum element (with block" do
# with a block
EnumerableSpecs::EachDefiner.new("2","33","4","11").max {|a,b| a <=> b }.should == "4"
EnumerableSpecs::EachDefiner.new( 2 , 33 , 4 , 11 ).max {|a,b| a <=> b }.should == 33
context "when passed a block" do
it "returns the maximum element" do
EnumerableSpecs::EachDefiner.new("2","33","4","11").max {|a,b| a <=> b }.should == "4"
EnumerableSpecs::EachDefiner.new( 2 , 33 , 4 , 11 ).max {|a,b| a <=> b }.should == 33

EnumerableSpecs::EachDefiner.new("2","33","4","11").max {|a,b| b <=> a }.should == "11"
EnumerableSpecs::EachDefiner.new( 2 , 33 , 4 , 11 ).max {|a,b| b <=> a }.should == 2
EnumerableSpecs::EachDefiner.new("2","33","4","11").max {|a,b| b <=> a }.should == "11"
EnumerableSpecs::EachDefiner.new( 2 , 33 , 4 , 11 ).max {|a,b| b <=> a }.should == 2

@e_strs.max {|a,b| a.length <=> b.length }.should == "1010101010"
@e_strs.max {|a,b| a.length <=> b.length }.should == "1010101010"

@e_strs.max {|a,b| a <=> b }.should == "666666"
@e_strs.max {|a,b| a.to_i <=> b.to_i }.should == "1010101010"
@e_strs.max {|a,b| a <=> b }.should == "666666"
@e_strs.max {|a,b| a.to_i <=> b.to_i }.should == "1010101010"

@e_ints.max {|a,b| a <=> b }.should == 1010101010
@e_ints.max {|a,b| a.to_s <=> b.to_s }.should == 666666
@e_ints.max {|a,b| a <=> b }.should == 1010101010
@e_ints.max {|a,b| a.to_s <=> b.to_s }.should == 666666
end
end

it "returns the minimum for enumerables that contain nils" do
it "returns the maximum for enumerables that contain nils" do
arr = EnumerableSpecs::Numerous.new(nil, nil, true)
arr.max { |a, b|
x = a.nil? ? 1 : a ? 0 : -1
@@ -83,4 +82,38 @@
multi.max.should == [6, 7, 8, 9]
end

context "when called with an argument n" do
context "without a block" do
it "returns an array containing the maximum n elements" do
result = @e_ints.max(2)
result.should == [1010101010, 666666]
end
end

context "with a block" do
it "returns an array containing the maximum n elements" do
result = @e_ints.max(2) { |a, b| a * 2 <=> b * 2 }
result.should == [1010101010, 666666]
end
end

context "on a enumerable of length x where x < n" do
it "returns an array containing the maximum n elements of length x" do
result = @e_ints.max(500)
result.length.should == 5
end
end

context "that is negative" do
it "raises an ArgumentError" do
lambda { @e_ints.max(-1) }.should raise_error(ArgumentError)
end
end

context "that is nil" do
it "returns the maximum element" do
@e_ints.max(nil).should == 1010101010
end
end
end
end
43 changes: 40 additions & 3 deletions spec/ruby/core/enumerable/min_by_spec.rb
Original file line number Diff line number Diff line change
@@ -11,8 +11,7 @@
EnumerableSpecs::Empty.new.min_by {|o| o.nonesuch }.should == nil
end


it "returns the object for whom the value returned by block is the largest" do
it "returns the object for whom the value returned by block is the smallest" do
EnumerableSpecs::Numerous.new(*%w[3 2 1]).min_by {|obj| obj.to_i }.should == '1'
EnumerableSpecs::Numerous.new(*%w[five three]).min_by {|obj| obj.length }.should == 'five'
end
@@ -29,7 +28,7 @@
EnumerableSpecs::Numerous.new(a, b, c).min_by {|obj| obj }.should == c
end

it "is able to return the maximum for enums that contain nils" do
it "is able to return the minimum for enums that contain nils" do
enum = EnumerableSpecs::Numerous.new(nil, nil, true)
enum.min_by {|o| o.nil? ? 0 : 1 }.should == nil
enum.min_by {|o| o.nil? ? 1 : 0 }.should == true
@@ -41,4 +40,42 @@
end

it_behaves_like :enumerable_enumeratorized_with_origin_size, :min_by

context "when called with an argument n" do
before :each do
@enum = EnumerableSpecs::Numerous.new(101, 55, 1, 20, 33, 500, 60)
end

context "without a block" do
it "returns an enumerator" do
@enum.min_by(2).should be_an_instance_of(enumerator_class)
end
end

context "with a block" do
it "returns an array containing the minimum n elements based on the block's value" do
result = @enum.min_by(3) { |i| i.to_s }
result.should == [1, 101, 20]
end

context "on a enumerable of length x where x < n" do
it "returns an array containing the minimum n elements of length n" do
result = @enum.min_by(500) { |i| i.to_s }
result.length.should == 7
end
end

context "when n is negative" do
it "raises an ArgumentError" do
lambda { @enum.min_by(-1) { |i| i.to_s } }.should raise_error(ArgumentError)
end
end

context "when n is nil" do
it "returns the minimum element" do
@enum.min_by(nil) { |i| i.to_s }.should == 1
end
end
end
end
end
36 changes: 34 additions & 2 deletions spec/ruby/core/enumerable/min_spec.rb
Original file line number Diff line number Diff line change
@@ -3,8 +3,6 @@

describe "Enumerable#min" do
before :each do
@a = EnumerableSpecs::EachDefiner.new( 2, 4, 6, 8, 10 )

@e_strs = EnumerableSpecs::EachDefiner.new("333", "22", "666666", "1", "55555", "1010101010")
@e_ints = EnumerableSpecs::EachDefiner.new( 333, 22, 666666, 55555, 1010101010)
end
@@ -88,4 +86,38 @@
multi.min.should == [1, 2]
end

context "when called with an argument n" do
context "without a block" do
it "returns an array containing the minimum n elements" do
result = @e_ints.min(2)
result.should == [22, 333]
end
end

context "with a block" do
it "returns an array containing the minimum n elements" do
result = @e_ints.min(2) { |a, b| a * 2 <=> b * 2 }
result.should == [22, 333]
end
end

context "on a enumerable of length x where x < n" do
it "returns an array containing the minimum n elements of length x" do
result = @e_ints.min(500)
result.length.should == 5
end
end

context "that is negative" do
it "raises an ArgumentError" do
lambda { @e_ints.min(-1) }.should raise_error(ArgumentError)
end
end

context "that is nil" do
it "returns the minimum element" do
@e_ints.min(nil).should == 22
end
end
end
end
53 changes: 53 additions & 0 deletions spec/ruby/core/enumerable/slice_after_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
require File.expand_path('../../../spec_helper', __FILE__)
require File.expand_path('../fixtures/classes', __FILE__)

describe "Enumerable#slice_after" do
before :each do
@enum = EnumerableSpecs::Numerous.new(7, 6, 5, 4, 3, 2, 1)
end

describe "when given an argument and no block" do
it "calls === on the argument to determine when to yield" do
arg = mock "filter"
arg.should_receive(:===).and_return(false, true, false, false, false, true, false)
e = @enum.slice_after(arg)
e.should be_an_instance_of(enumerator_class)
e.to_a.should == [[7, 6], [5, 4, 3, 2], [1]]
end

it "doesn't yield an empty array if the filter matches the first entry or the last entry" do
arg = mock "filter"
arg.should_receive(:===).and_return(true).exactly(7)
e = @enum.slice_after(arg)
e.to_a.should == [[7], [6], [5], [4], [3], [2], [1]]
end

it "uses standard boolean as a test" do
arg = mock "filter"
arg.should_receive(:===).and_return(false, :foo, nil, false, false, 42, false)
e = @enum.slice_after(arg)
e.to_a.should == [[7, 6], [5, 4, 3, 2], [1]]
end
end

describe "when given a block" do
describe "and no argument" do
it "calls the block to determine when to yield" do
e = @enum.slice_after{ |i| i == 6 || i == 2 }
e.should be_an_instance_of(enumerator_class)
e.to_a.should == [[7, 6], [5, 4, 3, 2], [1]]
end
end

describe "and an argument" do
it "raises an ArgumentError" do
lambda { @enum.slice_after(42) { |i| i == 6 } }.should raise_error(ArgumentError)
end
end
end

it "raises an ArgumentError when given an incorrect number of arguments" do
lambda { @enum.slice_after("one", "two") }.should raise_error(ArgumentError)
lambda { @enum.slice_after }.should raise_error(ArgumentError)
end
end
2 changes: 1 addition & 1 deletion spec/ruby/core/enumerable/slice_before_spec.rb
Original file line number Diff line number Diff line change
@@ -71,7 +71,7 @@
end
end

it "raises an Argument error when given an incorrect number of arguments" do
it "raises an ArgumentError when given an incorrect number of arguments" do
lambda { @enum.slice_before("one", "two") }.should raise_error(ArgumentError)
lambda { @enum.slice_before }.should raise_error(ArgumentError)
end
36 changes: 36 additions & 0 deletions spec/ruby/core/enumerable/slice_when_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
require File.expand_path('../../../spec_helper', __FILE__)
require File.expand_path('../fixtures/classes', __FILE__)

describe "Enumerable#slice_when" do
before :each do
ary = [10, 9, 7, 6, 4, 3, 2, 1]
@enum = EnumerableSpecs::Numerous.new *ary
@result = @enum.slice_when { |i, j| i - 1 != j }
@enum_length = ary.length
end

context "when given a block" do
it "returns an enumerator" do
@result.should be_an_instance_of(enumerator_class)
end

it "splits chunks between adjacent elements i and j where the block returns true" do
@result.to_a.should == [[10, 9], [7, 6], [4, 3, 2, 1]]
end

it "calls the block for length of the receiver enumerable minus one times" do
times_called = 0
@enum.slice_when do |i, j|
times_called += 1
i - 1 != j
end.to_a
times_called.should == (@enum_length - 1)
end
end

context "when not given a block" do
it "raises an ArgumentError" do
lambda { @enum.slice_when }.should raise_error(ArgumentError)
end
end
end
56 changes: 56 additions & 0 deletions spec/ruby/core/file/birthtime_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
require File.expand_path('../../../spec_helper', __FILE__)

describe "File.birthtime" do
before :each do
@file = __FILE__
end

after :each do
@file = nil
end

platform_is :darwin do
it "returns the birth time for the named file as a Time object" do
File.birthtime(@file)
File.birthtime(@file).should be_kind_of(Time)
end

it "accepts an object that has a #to_path method" do
File.birthtime(mock_to_path(@file))
end

it "raises an Errno::ENOENT exception if the file is not found" do
lambda { File.birthtime('bogus') }.should raise_error(Errno::ENOENT)
end
end

platform_is :windows, :linux, :openbsd, :freebsd, :netbsd do
it "raises an NotImplementedError" do
lambda { File.birthtime(@file) }.should raise_error(NotImplementedError)
end
end
end

describe "File#birthtime" do
before :each do
@file = File.open(__FILE__)
end

after :each do
@file.close
@file = nil
end

platform_is :darwin do
it "returns the birth time for self" do
@file.birthtime
@file.birthtime.should be_kind_of(Time)
end
end

platform_is :windows, :linux, :openbsd, :freebsd, :netbsd do
it "raises an NotImplementedError" do
lambda { @file.birthtime }.should raise_error(NotImplementedError)
end
end
end
2 changes: 1 addition & 1 deletion spec/ruby/core/file/ctime_spec.rb
Original file line number Diff line number Diff line change
@@ -28,7 +28,7 @@
@file = File.open(__FILE__)
end

after:each do
after :each do
@file.close
@file = nil
end
18 changes: 18 additions & 0 deletions spec/ruby/core/file/stat/birthtime_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
require File.expand_path('../../../../spec_helper', __FILE__)

describe "File::Stat#birthtime" do
before :each do
@file = tmp('i_exist')
touch(@file) { |f| f.write "rubinius" }
end

after :each do
rm_r @file
end

it "returns the birthtime of a File::Stat object" do
st = File.stat(@file)
st.birthtime.should be_kind_of(Time)
st.birthtime.should <= Time.now
end
end
30 changes: 28 additions & 2 deletions spec/ruby/core/file/utime_spec.rb
Original file line number Diff line number Diff line change
@@ -2,8 +2,8 @@

describe "File.utime" do
before :each do
@atime = Time.now
@mtime = Time.now
@atime = Time.new(2000, 1, 1, 1, 1, 1.00001)
@mtime = Time.new(2000, 1, 1, 1, 1, 1.00001)
@file1 = tmp("specs_file_utime1")
@file2 = tmp("specs_file_utime2")
touch @file1
@@ -22,6 +22,32 @@
File.mtime(@file2).to_i.should be_close(@mtime.to_i, 2)
end

platform_is_not :darwin do
it "sets and gets microseconds from Time arguments" do
File.utime(@atime, @mtime, @file1, @file2)
File.atime(@file1).usec.should equal(10)
File.mtime(@file1).usec.should equal(10)
File.atime(@file2).usec.should equal(10)
File.mtime(@file2).usec.should equal(10)
end

it "sets and gets microseconds from Float arguments" do
File.utime(0.0001, 0.0001, @file1, @file2)
File.atime(@file1).usec.should equal(100)
File.mtime(@file1).usec.should equal(100)
File.atime(@file2).usec.should equal(100)
File.mtime(@file2).usec.should equal(100)
end

it "sets and gets microseconds from Rational arguments" do
File.utime(Rational(1, 1000), Rational(1, 1000), @file1, @file2)
File.atime(@file1).usec.should equal(1000)
File.mtime(@file1).usec.should equal(1000)
File.atime(@file2).usec.should equal(1000)
File.mtime(@file2).usec.should equal(1000)
end
end

it "uses the current times if two nil values are passed" do
File.utime(nil, nil, @file1, @file2)
File.atime(@file1).to_i.should be_close(Time.now.to_i, 2)
44 changes: 44 additions & 0 deletions spec/ruby/core/float/next_float_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
require File.expand_path('../../../spec_helper', __FILE__)

describe "Float" do
it "returns a float the smallest possible step greater than the receiver" do
barely_positive = 0.0.next_float
barely_positive.should eql 0.0.next_float

barely_positive.should > 0.0
barely_positive.should < barely_positive.next_float

midpoint = barely_positive / 2
[0.0, barely_positive].should include midpoint
end

it "steps directly between MAX and INFINITY" do
(-Float::INFINITY).next_float.should eql -Float::MAX
Float::MAX.next_float.should eql Float::INFINITY
end

it "steps directly between 1.0 and EPSILON + 1.0" do
1.0.next_float.should eql Float::EPSILON + 1.0
end

it "steps directly between -1.0 and EPSILON/2 - 1.0" do
(-1.0).next_float.should eql Float::EPSILON/2 - 1.0
end

it "reverses the effect of prev_float" do
num = rand
num.prev_float.next_float.should eql num
end

it "returns negative zero when stepping upward from just below zero" do
x = 0.0.prev_float.next_float
(1/x).should eql -Float::INFINITY
x = (-0.0).prev_float.next_float
(1/x).should eql -Float::INFINITY
x.next_float.should > 0
end

it "returns NAN if NAN was the receiver" do
Float::NAN.next_float.nan?.should eql true
end
end
44 changes: 44 additions & 0 deletions spec/ruby/core/float/prev_float_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
require File.expand_path('../../../spec_helper', __FILE__)

describe "Float" do
it "returns a float the smallest possible step greater than the receiver" do
barely_negative = 0.0.prev_float
barely_negative.should eql 0.0.prev_float

barely_negative.should < 0.0
barely_negative.should > barely_negative.prev_float

midpoint = barely_negative / 2
[0.0, barely_negative].should include midpoint
end

it "steps directly between MAX and INFINITY" do
Float::INFINITY.prev_float.should eql Float::MAX
(-Float::MAX).prev_float.should eql -Float::INFINITY
end

it "steps directly between 1.0 and -EPSILON/2 + 1.0" do
1.0.prev_float.should eql -Float::EPSILON/2 + 1.0
end

it "steps directly between -1.0 and -EPSILON - 1.0" do
(-1.0).prev_float.should eql -Float::EPSILON - 1.0
end

it "reverses the effect of next_float" do
num = rand
num.next_float.prev_float.should eql num
end

it "returns positive zero when stepping downward from just above zero" do
x = 0.0.next_float.prev_float
(1/x).should eql Float::INFINITY
x = (-0.0).next_float.prev_float
(1/x).should eql Float::INFINITY
x.prev_float.should < 0
end

it "returns NAN if NAN was the receiver" do
Float::NAN.prev_float.nan?.should eql true
end
end
8 changes: 4 additions & 4 deletions spec/ruby/core/kernel/catch_spec.rb
Original file line number Diff line number Diff line change
@@ -30,12 +30,12 @@
ScratchPad.recorded.should == :thrown_key
end

it "raises an ArgumentError if a Symbol is thrown for a String catch value" do
lambda { catch("exit") { throw :exit } }.should raise_error(ArgumentError)
it "raises an UncaughtThrowError if a Symbol is thrown for a String catch value" do
lambda { catch("exit") { throw :exit } }.should raise_error(UncaughtThrowError)
end

it "raises an ArgumentError if a String with different identity is thrown" do
lambda { catch("exit") { throw "exit" } }.should raise_error(ArgumentError)
it "raises an UncaughtThrowError if a String with different identity is thrown" do
lambda { catch("exit") { throw "exit" } }.should raise_error(UncaughtThrowError)
end

it "catches a Symbol when thrown a matching Symbol" do
18 changes: 18 additions & 0 deletions spec/ruby/core/kernel/frozen_spec.rb
Original file line number Diff line number Diff line change
@@ -41,4 +41,22 @@
@symbol.frozen?.should be_true
end
end

describe "on nil" do
it "returns true" do
nil.frozen?.should be_true
end
end

describe "on true" do
it "returns true" do
true.frozen?.should be_true
end
end

describe "on false" do
it "returns true" do
false.frozen?.should be_true
end
end
end
10 changes: 4 additions & 6 deletions spec/ruby/core/kernel/itself_spec.rb
Original file line number Diff line number Diff line change
@@ -2,11 +2,9 @@
require File.expand_path('../fixtures/classes', __FILE__)

describe "Kernel#itself" do
ruby_version_is "2.2" do
it "returns the receiver itself" do
foo = Object.new
foo.itself.should equal foo
foo.itself.object_id.should equal foo.object_id
end
it "returns the receiver itself" do
foo = Object.new
foo.itself.should equal foo
foo.itself.object_id.should == foo.object_id
end
end
4 changes: 2 additions & 2 deletions spec/ruby/core/kernel/throw_spec.rb
Original file line number Diff line number Diff line change
@@ -41,8 +41,8 @@
res.should == :return_value
end

it "raises an ArgumentError if there is no catch block for the symbol" do
lambda { throw :blah }.should raise_error(ArgumentError)
it "raises an UncaughtThrowError if there is no catch block for the symbol" do
lambda { throw :blah }.should raise_error(UncaughtThrowError)
end

it "raises ArgumentError if 3 or more arguments provided" do
38 changes: 38 additions & 0 deletions spec/ruby/core/method/curry_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
require File.expand_path('../../../spec_helper', __FILE__)
require File.expand_path('../fixtures/classes', __FILE__)

describe "Method#curry" do

it "returns a curried proc" do
x = Object.new
def x.foo(a,b,c); [a,b,c]; end

c = x.method(:foo).curry
c.should be_kind_of(Proc)
c.(1).(2, 3).should == [1,2,3]
end

describe "with optional arity argument" do
before(:each) do
@obj = MethodSpecs::Methods.new
end

it "returns a curried proc when given correct arity" do
@obj.method(:one_req).curry(1).should be_kind_of(Proc)
@obj.method(:zero_with_splat).curry(100).should be_kind_of(Proc)
@obj.method(:two_req_with_splat).curry(2).should be_kind_of(Proc)
end

it "raises ArgumentError when the method requires less arguments than the given arity" do
lambda { @obj.method(:zero).curry(1) }.should raise_error(ArgumentError)
lambda { @obj.method(:one_req_one_opt).curry(3) }.should raise_error(ArgumentError)
lambda { @obj.method(:two_req_one_opt_with_block).curry(4) }.should raise_error(ArgumentError)
end

it "raises ArgumentError when the method requires more arguments than the given arity" do
lambda { @obj.method(:two_req_with_splat).curry(1) }.should raise_error(ArgumentError)
lambda { @obj.method(:one_req).curry(0) }.should raise_error(ArgumentError)
end
end

end
12 changes: 12 additions & 0 deletions spec/ruby/core/method/fixtures/classes.rb
Original file line number Diff line number Diff line change
@@ -99,12 +99,24 @@ class A
def baz(a, b)
self.class
end
def overridden; end
end

class B < A
def overridden; end
end

module BetweenBAndC
def overridden; end
end

class C < B
include BetweenBAndC
def overridden; end
end

module OverrideAgain
def overridden; end
end

class D
39 changes: 39 additions & 0 deletions spec/ruby/core/method/super_method_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
require File.expand_path('../../../spec_helper', __FILE__)
require File.expand_path('../fixtures/classes', __FILE__)

describe "Method#super_method" do
it "returns the method that would be called by super in the method" do
obj = MethodSpecs::C.new
obj.extend MethodSpecs::OverrideAgain
meth = obj.method(:overridden)

s_meth = meth.super_method
s_meth.owner.should == MethodSpecs::C
s_meth.receiver.should == obj
s_meth.name.should == :overridden

ss_meth = meth.super_method.super_method
ss_meth.owner.should == MethodSpecs::BetweenBAndC
ss_meth.receiver.should == obj
ss_meth.name.should == :overridden

sss_meth = meth.super_method.super_method.super_method
sss_meth.owner.should == MethodSpecs::B
sss_meth.receiver.should == obj
sss_meth.name.should == :overridden
end

it "returns nil when there's no super method in the parent" do
method = Object.new.method(:method)
method.super_method.should == nil
end

it "returns nil when the parent's method is removed" do
object = MethodSpecs::B.new
method = object.method(:overridden)

MethodSpecs::A.class_eval { undef :overridden }

method.super_method.should == nil
end
end
1 change: 1 addition & 0 deletions spec/ruby/core/proc/curry_spec.rb
Original file line number Diff line number Diff line change
@@ -124,6 +124,7 @@
it "raises an ArgumentError if called on a lambda that requires fewer than _arity_ arguments" do
lambda { @lambda_add.curry(4) }.should raise_error(ArgumentError)
lambda { lambda { true }.curry(1) }.should raise_error(ArgumentError)
lambda { lambda {|a, b=nil|}.curry(5) }.should raise_error(ArgumentError)
end

it "calls the curried proc with the arguments if _arity_ arguments have been given" do
103 changes: 103 additions & 0 deletions spec/ruby/core/string/unicode_normalize_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
require File.expand_path('../../../spec_helper', __FILE__)

# Examples taken from http://www.unicode.org/reports/tr15/#Norm_Forms

describe "String#unicode_normalize" do
before do
@accented_f = "\u1e9b\u0323"
@angstrom = "\u212b"
@ohm = "\u2126"
end

it "normalizes code points in the string according to the form that is specified" do
@accented_f.unicode_normalize(:nfc).should == "\u1e9b\u0323"
@accented_f.unicode_normalize(:nfd).should == "\u017f\u0323\u0307"
@accented_f.unicode_normalize(:nfkc).should == "\u1e69"
@accented_f.unicode_normalize(:nfkd).should == "\u0073\u0323\u0307"
end

it "defaults to the nfc normalization form if no forms are specified" do
@accented_f.unicode_normalize.should == "\u1e9b\u0323"
@angstrom.unicode_normalize.should == "\u00c5"
@ohm.unicode_normalize.should == "\u03a9"
end

it "raises an Encoding::CompatibilityError if the string is not in an unicode encoding" do
lambda do
@angstrom.force_encoding("ISO-8859-1").unicode_normalize
end.should raise_error(Encoding::CompatibilityError)
end

it "raises an ArgumentError if the specified form is invalid" do
lambda { @angstrom.unicode_normalize(:invalid_form) }.should raise_error(ArgumentError)
end
end

describe "String#unicode_normalize!" do
before do
@ohm = "\u2126"
end

it "normalizes code points and modifies the receiving string" do
angstrom = "\u212b"
angstrom.unicode_normalize!
angstrom.should == "\u00c5"
angstrom.should_not == "\u212b"
end

it "raises an Encoding::CompatibilityError if the string is not in an unicode encoding" do
lambda do
@ohm.force_encoding("ISO-8859-1").unicode_normalize!
end.should raise_error(Encoding::CompatibilityError)
end

it "raises an ArgumentError if the specified form is invalid" do
lambda { @ohm.unicode_normalize!(:invalid_form) }.should raise_error(ArgumentError)
end
end

describe "String#unicode_normalized?" do
before do
@nfc_normalized_str = "\u1e9b\u0323"
@nfd_normalized_str = "\u017f\u0323\u0307"
@nfkc_normalized_str = "\u1e69"
@nfkd_normalized_str = "\u0073\u0323\u0307"
end

it "returns true if string is in the specified normalization form" do
@nfc_normalized_str.unicode_normalized?(:nfc).should == true
@nfd_normalized_str.unicode_normalized?(:nfd).should == true
@nfkc_normalized_str.unicode_normalized?(:nfkc).should == true
@nfkd_normalized_str.unicode_normalized?(:nfkd).should == true
end

it "returns false if string is not in the supplied normalization form" do
@nfd_normalized_str.unicode_normalized?(:nfc).should == false
@nfc_normalized_str.unicode_normalized?(:nfd).should == false
@nfc_normalized_str.unicode_normalized?(:nfkc).should == false
@nfc_normalized_str.unicode_normalized?(:nfkd).should == false
end

it "defaults to the nfc normalization form if no forms are specified" do
@nfc_normalized_str.unicode_normalized?.should == true
@nfd_normalized_str.unicode_normalized?.should == false
end

it "returns true if string is empty" do
"".unicode_normalized?.should == true
end

it "returns true if string does not contain any unicode codepoints" do
"abc".unicode_normalized?.should == true
end

it "raises an Encoding::CompatibilityError if the string is not in an unicode encoding" do
lambda do
@nfc_normalized_str.force_encoding("ISO-8859-1").unicode_normalized?
end.should raise_error(Encoding::CompatibilityError)
end

it "raises an ArgumentError if the specified form is invalid" do
lambda { @nfc_normalized_str.unicode_normalized?(:invalid_form) }.should raise_error(ArgumentError)
end
end
3 changes: 3 additions & 0 deletions spec/ruby/core/unboundmethod/fixtures/classes.rb
Original file line number Diff line number Diff line change
@@ -73,11 +73,14 @@ class A
def baz(a, b)
return [__FILE__, self.class]
end
def overridden; end
end

class B < A
def overridden; end
end

class C < B
def overridden; end
end
end
26 changes: 26 additions & 0 deletions spec/ruby/core/unboundmethod/super_method_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
require File.expand_path('../../../spec_helper', __FILE__)
require File.expand_path('../fixtures/classes', __FILE__)

describe "UnboundMethod#super_method" do
it "returns the method that would be called by super in the method" do
meth = UnboundMethodSpecs::C.instance_method(:overridden)
meth = meth.super_method
meth.should == UnboundMethodSpecs::B.instance_method(:overridden)
meth = meth.super_method
meth.should == UnboundMethodSpecs::A.instance_method(:overridden)
end

it "returns nil when there's no super method in the parent" do
method = Object.instance_method(:method)
method.super_method.should == nil
end

it "returns nil when the parent's method is removed" do
object = UnboundMethodSpecs::B
method = object.instance_method(:overridden)

UnboundMethodSpecs::A.class_eval { undef :overridden }

method.super_method.should == nil
end
end
17 changes: 17 additions & 0 deletions spec/ruby/language/block_spec.rb
Original file line number Diff line number Diff line change
@@ -789,6 +789,23 @@ def m(a) yield a end
[a, b, c, d]
end.call(2, 3).should == [2, 6, [], 3]
end

describe "with a circular argument reference" do
it "shadows an existing local with the same name as the argument" do
a = 1
proc { |a=a| a }.call.should == nil
end

it "shadows an existing method with the same name as the argument" do
def a; 1; end
proc { |a=a| a }.call.should == nil
end

it "calls an existing method with the same name as the argument if explicitly using ()" do
def a; 1; end
proc { |a=a()| a }.call.should == 1
end
end
end

describe "with pattern matching" do
13 changes: 12 additions & 1 deletion spec/ruby/language/def_spec.rb
Original file line number Diff line number Diff line change
@@ -154,13 +154,24 @@ def foo(a, b = 2, *args)
foo(2,3,3).should == [2,3,[3]]
end

it "calls a method with the same name as the local" do
it "shadows an existing method with the same name as the local" do
def bar
1
end
def foo(bar = bar)
bar
end
foo.should == nil
foo(2).should == 2
end

it "calls a method with the same name as the local when explicitly using ()" do
def bar
1
end
def foo(bar = bar())
bar
end
foo.should == 1
foo(2).should == 2
end
14 changes: 7 additions & 7 deletions spec/ruby/language/hash_spec.rb
Original file line number Diff line number Diff line change
@@ -73,9 +73,9 @@
{rbx: :cool, specs: 'fail_sometimes',}.should == h
end

it "accepts mixed 'key: value' and 'key => value' syntax" do
h = {:a => 1, :b => 2, "c" => 3}
{a: 1, :b => 2, "c" => 3}.should == h
it "accepts mixed 'key: value', 'key => value' and '\"key\"': value' syntax" do
h = {:a => 1, :b => 2, "c" => 3, :d => 4}
eval('{a: 1, :b => 2, "c" => 3, "d": 4}').should == h
end

it "expands an '**{}' element into the containing Hash literal initialization" do
@@ -93,7 +93,7 @@
obj = mock("hash splat")
obj.should_receive(:to_hash).and_return({a: 2, b: 3})

{a: 1, **obj, c: 3}.should == {a:1, b: 3, c: 3}
{a: 1, **obj, c: 3}.should == {a: 2, b: 3, c: 3}
end

it "raises a TypeError if #to_hash does not return a Hash" do
@@ -103,12 +103,12 @@
lambda { {**obj} }.should raise_error(TypeError)
end

it "merges the containing Hash into the **obj before importing obj's items" do
{a: 1, **{a: 2, b: 3, c: 4}, c: 3}.should == {a: 1, b: 3, c: 3}
it "expands an '**obj' element into the containing Hash and keeps the latter keys if there are duplicates" do
{a: 1, **{a: 2, b: 3, c: 4}, c: 3}.should == {a: 2, b: 3, c: 3}
end

it "merges multiple nested '**obj' in Hash literals" do
h = {a: 1, **{a: 2, **{b: 3, **{c: 4}}, **{d: 5}, }, **{d: 6}}
h.should == {a: 1, b: 3, c: 4, d: 5}
h.should == {a: 2, b: 3, c: 4, d: 6}
end
end
17 changes: 17 additions & 0 deletions spec/ruby/language/lambda_spec.rb
Original file line number Diff line number Diff line change
@@ -268,6 +268,23 @@ def m
result = @a.(1, 2, e: 3, g: 4, h: 5, i: 6, &(l = ->{}))
result.should == [1, 1, [], 2, 3, 2, 4, { h: 5, i: 6 }, l]
end

describe "with circular optional argument reference" do
it "shadows an existing local with the same name as the argument" do
a = 1
-> (a=a) { a }.call.should == nil
end

it "shadows an existing method with the same name as the argument" do
def a; 1; end
-> (a=a) { a }.call.should == nil
end

it "calls an existing method with the same name as the argument if explicitly using ()" do
def a; 1; end
-> (a=a()) { a }.call.should == 1
end
end
end
end

1 change: 0 additions & 1 deletion spec/ruby/language/regexp/character_classes_spec.rb
Original file line number Diff line number Diff line change
@@ -142,7 +142,6 @@
end

it "matches Unicode space characters with [[:blank:]]" do
"\u{180E}".match(/[[:blank:]]/).to_a.should == ["\u{180E}"]
"\u{1680}".match(/[[:blank:]]/).to_a.should == ["\u{1680}"]
end

12 changes: 6 additions & 6 deletions spec/ruby/language/throw_spec.rb
Original file line number Diff line number Diff line change
@@ -44,7 +44,7 @@
end

it "does not convert strings to a symbol" do
lambda { catch(:exit) { throw "exit" } }.should raise_error(ArgumentError)
lambda { catch(:exit) { throw "exit" } }.should raise_error(UncaughtThrowError)
end

it "unwinds stack from within a method" do
@@ -62,18 +62,18 @@ def throw_method(handler,val)
catch(:foo) { c.call }.should == :msg
end

it "raises an ArgumentError if outside of scope of a matching catch" do
lambda { throw :test,5 }.should raise_error(ArgumentError)
lambda { catch(:different) { throw :test,5 } }.should raise_error(ArgumentError)
it "raises an UncaughtThrowError if outside of scope of a matching catch" do
lambda { throw :test,5 }.should raise_error(UncaughtThrowError)
lambda { catch(:different) { throw :test,5 } }.should raise_error(UncaughtThrowError)
end

it "raises an ArgumentError if used to exit a thread" do
it "raises an UncaughtThrowError if used to exit a thread" do
lambda {
catch(:what) do
Thread.new do
throw :what
end.join
end
}.should raise_error(ArgumentError)
}.should raise_error(UncaughtThrowError)
end
end
16 changes: 8 additions & 8 deletions spec/ruby/optional/capi/kernel_spec.rb
Original file line number Diff line number Diff line change
@@ -87,8 +87,8 @@
ScratchPad.recorded.should == [:before_throw]
end

it "raises an ArgumentError if there is no catch block for the symbol" do
lambda { @s.rb_throw(nil) }.should raise_error(ArgumentError)
it "raises an UncaughtThrowError if there is no catch block for the symbol" do
lambda { @s.rb_throw(nil) }.should raise_error(UncaughtThrowError)
end
end

@@ -113,8 +113,8 @@
ScratchPad.recorded.should == [:before_throw]
end

it "raises an ArgumentError if there is no catch block for the symbol" do
lambda { @s.rb_throw(nil) }.should raise_error(ArgumentError)
it "raises an UncaughtThrowError if there is no catch block for the symbol" do
lambda { @s.rb_throw(nil) }.should raise_error(UncaughtThrowError)
end
end

@@ -296,8 +296,8 @@
ScratchPad.recorded.should == [:before_throw]
end

it "raises an ArgumentError if the throw symbol isn't caught" do
lambda { @s.rb_catch("foo", lambda { throw :bar }) }.should raise_error(ArgumentError)
it "raises an UncaughtThrowError if the throw symbol isn't caught" do
lambda { @s.rb_catch("foo", lambda { throw :bar }) }.should raise_error(UncaughtThrowError)
end
end

@@ -322,8 +322,8 @@
ScratchPad.recorded.should == [:before_throw]
end

it "raises an ArgumentError if the throw symbol isn't caught" do
lambda { @s.rb_catch("foo", lambda { throw :bar }) }.should raise_error(ArgumentError)
it "raises an UncaughtThrowError if the throw symbol isn't caught" do
lambda { @s.rb_catch("foo", lambda { throw :bar }) }.should raise_error(UncaughtThrowError)
end
end

34 changes: 21 additions & 13 deletions spec/ruby/shared/process/spawn.rb
Original file line number Diff line number Diff line change
@@ -229,19 +229,19 @@
end.should output_to_fd(Process.getpgid(Process.pid).to_s)
end

it "joins the current process if :pgroup => false" do
it "joins the current process if :pgroup is false" do
lambda do
Process.wait @object.spawn(ruby_cmd("print Process.getpgid(Process.pid)"), :pgroup => false)
end.should output_to_fd(Process.getpgid(Process.pid).to_s)
end

it "joins the current process if :pgroup => nil" do
it "joins the current process if :pgroup is nil" do
lambda do
Process.wait @object.spawn(ruby_cmd("print Process.getpgid(Process.pid)"), :pgroup => nil)
end.should output_to_fd(Process.getpgid(Process.pid).to_s)
end

it "joins a new process group if :pgroup => true" do
it "joins a new process group if :pgroup is true" do
process = lambda do
Process.wait @object.spawn(ruby_cmd("print Process.getpgid(Process.pid)"), :pgroup => true)
end
@@ -250,7 +250,7 @@
process.should output_to_fd(/\d+/)
end

it "joins a new process group if :pgroup => 0" do
it "joins a new process group if :pgroup is 0" do
process = lambda do
Process.wait @object.spawn(ruby_cmd("print Process.getpgid(Process.pid)"), :pgroup => 0)
end
@@ -259,7 +259,7 @@
process.should output_to_fd(/\d+/)
end

it "joins the specified process group if :pgroup => pgid" do
it "joins the specified process group if :pgroup is pgid" do
lambda do
Process.wait @object.spawn(ruby_cmd("print Process.getpgid(Process.pid)"), :pgroup => 123)
end.should_not output_to_fd("123")
@@ -334,48 +334,56 @@

# redirection

it "redirects STDOUT to the given file descriptior if :out => Fixnum" do
it "redirects STDOUT to the given file descriptior if :out is Fixnum" do
File.open(@name, 'w') do |file|
lambda do
Process.wait @object.spawn(ruby_cmd("print :glark"), :out => file.fileno)
end.should output_to_fd("glark", file)
end
end

it "redirects STDOUT to the given file if :out => IO" do
it "redirects STDOUT to the given file if :out is IO" do
File.open(@name, 'w') do |file|
lambda do
Process.wait @object.spawn(ruby_cmd("print :glark"), :out => file)
end.should output_to_fd("glark", file)
end
end

it "redirects STDOUT to the given file if :out => String" do
it "redirects STDOUT to the given file if :out is String" do
Process.wait @object.spawn(ruby_cmd("print :glark"), :out => @name)
@name.should have_data("glark")
end

it "redirects STDERR to the given file descriptior if :err => Fixnum" do
it "redirects STDERR to the given file descriptior if :err is Fixnum" do
File.open(@name, 'w') do |file|
lambda do
Process.wait @object.spawn(ruby_cmd("STDERR.print :glark"), :err => file.fileno)
end.should output_to_fd("glark", file)
end
end

it "redirects STDERR to the given file descriptor if :err => IO" do
it "redirects STDERR to the given file descriptor if :err is IO" do
File.open(@name, 'w') do |file|
lambda do
Process.wait @object.spawn(ruby_cmd("STDERR.print :glark"), :err => file)
end.should output_to_fd("glark", file)
end
end

it "redirects STDERR to the given file if :err => String" do
it "redirects STDERR to the given file if :err is String" do
Process.wait @object.spawn(ruby_cmd("STDERR.print :glark"), :err => @name)
@name.should have_data("glark")
end

it "redirects STDERR to child STDOUT if :err is [:child, :out]" do
File.open(@name, 'w') do |file|
lambda do
Process.wait @object.spawn(ruby_cmd("STDERR.print :glark"), :out => file, :err => [:child, :out])
end.should output_to_fd("glark", file)
end
end

it "redirects both STDERR and STDOUT to the given file descriptior" do
File.open(@name, 'w') do |file|
lambda do
@@ -402,7 +410,7 @@
@name.should have_data("")
end

context "when passed :close_others => true" do
context "when passed :close_others is true" do
before :each do
@output = tmp("spawn_close_others_true")
@options = { :close_others => true }
@@ -445,7 +453,7 @@
end
end

context "when passed :close_others => false" do
context "when passed :close_others is false" do
before :each do
@output = tmp("spawn_close_others_false")
@options = { :close_others => false }
1 change: 1 addition & 0 deletions spec/tags/ruby/core/file/stat/birthtime_tags.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
fails:File::Stat#birthtime returns the birthtime of a File::Stat object
10 changes: 0 additions & 10 deletions spec/tags/ruby/core/io/popen_tags.txt

This file was deleted.

3 changes: 3 additions & 0 deletions spec/tags/ruby/core/kernel/frozen_tags.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
fails:Kernel#frozen? on nil returns true
fails:Kernel#frozen? on true returns true
fails:Kernel#frozen? on false returns true
1 change: 1 addition & 0 deletions spec/tags/ruby/core/method/curry_tags.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
fails:Method#curry with optional arity argument raises ArgumentError when the method requires less arguments than the given arity
1 change: 1 addition & 0 deletions spec/tags/ruby/core/proc/curry_tags.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
fails:raises an ArgumentError if called on a lambda that requires fewer than _arity_ arguments
14 changes: 14 additions & 0 deletions spec/tags/ruby/core/string/unicode_normalize_tags.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
fails:String#unicode_normalize normalizes code points in the string according to the form that is specified
fails:String#unicode_normalize defaults to the nfc normalization form if no forms are specified
fails:String#unicode_normalize raises an Encoding::CompatibilityError if the string is not in an unicode encoding
fails:String#unicode_normalize raises an ArgumentError if the specified form is invalid
fails:String#unicode_normalize! normalizes code points and modifies the receiving string
fails:String#unicode_normalize! raises an Encoding::CompatibilityError if the string is not in an unicode encoding
fails:String#unicode_normalize! raises an ArgumentError if the specified form is invalid
fails:String#unicode_normalized? returns true if string is in the specified normalization form
fails:String#unicode_normalized? returns false if string is not in the supplied normalization form
fails:String#unicode_normalized? defaults to the nfc normalization form if no forms are specified
fails:String#unicode_normalized? returns true if string is empty
fails:String#unicode_normalized? returns true if string does not contain any unicode codepoints
fails:String#unicode_normalized? raises an Encoding::CompatibilityError if the string is not in an unicode encoding
fails:String#unicode_normalized? raises an ArgumentError if the specified form is invalid
2 changes: 2 additions & 0 deletions spec/tags/ruby/language/block_tags.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
fails:Post-args with optional args with a circular argument reference shadows an existing local with the same name as the argument
fails:Post-args with optional args with a circular argument reference shadows an existing method with the same name as the argument
1 change: 1 addition & 0 deletions spec/tags/ruby/language/def_tags.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
fails:An instance method with a default argument shadows an existing method with the same name as the local
fails:A singleton method definition raises RuntimeError if frozen
fails:A method definition inside a metaclass scope raises RuntimeError if frozen
4 changes: 4 additions & 0 deletions spec/tags/ruby/language/hash_tags.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
fails:Hash literal accepts mixed 'key: value', 'key => value' and '"key"': value' syntax
fails:Hash literal calls #to_hash to convert an '**obj' element
fails:Hash literal expands an '**obj' element into the containing Hash and keeps the latter keys if there are duplicates
fails:Hash literal merges multiple nested '**obj' in Hash literals
2 changes: 2 additions & 0 deletions spec/tags/ruby/language/lambda_tags.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
fails:A lambda literal -> () { } assigns variables from parameters with circular optional argument reference shadows an existing local with the same name as the argument
fails:A lambda literal -> () { } assigns variables from parameters with circular optional argument reference shadows an existing method with the same name as the argument
4 changes: 2 additions & 2 deletions vm/builtin/access_variable.cpp
Original file line number Diff line number Diff line change
@@ -49,7 +49,7 @@ namespace rubinius {

Object* recv = args.recv();

if(CBOOL(recv->frozen_p(state))) {
if(CBOOL(recv->frozen_p(state)) && CBOOL(recv->frozen_mod_disallowed(state))) {
Exception::frozen_error(state, call_frame, recv);
return 0;
}
@@ -66,7 +66,7 @@ namespace rubinius {

/* The writer case. */
if(access->write()->true_p()) {
if(CBOOL(self->frozen_p(state))) {
if(CBOOL(self->frozen_p(state)) && CBOOL(self->frozen_mod_disallowed(state))) {
Exception::frozen_error(state, call_frame, self);
return 0;
}
6 changes: 6 additions & 0 deletions vm/builtin/dir.cpp
Original file line number Diff line number Diff line change
@@ -71,6 +71,12 @@ namespace rubinius {
return cTrue;
}

Object* Dir::fileno(STATE) {
int fd = dirfd(os_);

return Fixnum::from(fd);
}

Object* Dir::close(STATE) {
guard(state);

3 changes: 3 additions & 0 deletions vm/builtin/dir.hpp
Original file line number Diff line number Diff line change
@@ -36,6 +36,9 @@ namespace rubinius {
// Rubinius.primitive :dir_open
Object* open(STATE, String *path, Object* enc);

// Rubinius.primitive :dir_fileno
Object* fileno(STATE);

// Rubinius.primitive :dir_close
Object* close(STATE);

13 changes: 10 additions & 3 deletions vm/builtin/object.cpp
Original file line number Diff line number Diff line change
@@ -171,9 +171,9 @@ namespace rubinius {
Object* Object::frozen_p(STATE) {
if(reference_p()) {
return RBOOL(is_frozen_p());
} else if(try_as<Symbol>(this)) {
} else if(try_as<Symbol>(this) || try_as<Fixnum>(this)) {
return cTrue;
} else if(try_as<Fixnum>(this)) {
} else if(this->nil_p() || this->true_p() || this->false_p()) {
return cTrue;
} else {
LookupTable* tbl = try_as<LookupTable>(G(external_ivars)->fetch(state, this));
@@ -183,10 +183,17 @@ namespace rubinius {
}

void Object::check_frozen(STATE) {
if(CBOOL(frozen_p(state))) {
if(CBOOL(frozen_p(state)) && CBOOL(frozen_mod_disallowed(state))) {
Exception::frozen_error(state, this);
}
}

Object* Object::frozen_mod_disallowed(STATE) {
if(this->nil_p() || this->true_p() || this->false_p()) {
return cFalse;
}
return cTrue;
}

Object* Object::get_field(STATE, size_t index) {
return type_info(state)->get_field(state, this, index);
7 changes: 7 additions & 0 deletions vm/builtin/object.hpp
Original file line number Diff line number Diff line change
@@ -390,6 +390,13 @@ namespace rubinius {
* Similar to CRuby rb_check_frozen
*/
void check_frozen(STATE);

/**
* Returns true unless one of the objects is nil, true, or
* false. Those objects are allowed to be modified when
* frozen.
*/
Object* frozen_mod_disallowed(STATE);

public: /* accessors */

39 changes: 36 additions & 3 deletions vm/builtin/stat.cpp
Original file line number Diff line number Diff line change
@@ -66,15 +66,48 @@ namespace rubinius {
}

Time* Stat::stat_atime(STATE) {
return Time::at(state, st_.st_atime);
#ifdef HAVE_STRUCT_STAT_ST_ATIM
return Time::at(state, st_.st_atim.tv_sec, st_.st_atim.tv_nsec);
#elif HAVE_STRUCT_STAT_ST_ATIMESPEC
return Time::at(state, st_.st_atimespec.tv_sec, st_.st_atimespec.tv_nsec);
#elif HAVE_STRUCT_STAT_ST_ATIMENSEC
return Time::at(state, st_.st_atime, static_cast<long>(st_.st_atimensec));
#else
return Time::at(state, st_.st_atime);
#endif
}

Time* Stat::stat_mtime(STATE) {
return Time::at(state, st_.st_mtime);
#ifdef HAVE_STRUCT_STAT_ST_MTIM
return Time::at(state, st_.st_mtim.tv_sec, st_.st_mtim.tv_nsec);
#elif HAVE_STRUCT_STAT_ST_MTIMESPEC
return Time::at(state, st_.st_mtimespec.tv_sec, st_.st_mtimespec.tv_nsec);
#elif HAVE_STRUCT_STAT_ST_MTIMENSEC
return Time::at(state, st_.st_mtime, static_cast<long>(st_.st_mtimensec));
#else
return Time::at(state, st_.st_mtime);
#endif
}

Time* Stat::stat_ctime(STATE) {
return Time::at(state, st_.st_ctime);
#ifdef HAVE_STRUCT_STAT_ST_CTIM
return Time::at(state, st_.st_ctim.tv_sec, st_.st_ctim.tv_nsec);
#elif HAVE_STRUCT_STAT_ST_CTIMESPEC
return Time::at(state, st_.st_ctimespec.tv_sec, st_.st_ctimespec.tv_nsec);
#elif HAVE_STRUCT_STAT_ST_CTIMENSEC
return Time::at(state, st_.st_ctime, static_cast<long>(st_.st_ctimensec));
#else
return Time::at(state, st_.st_ctime);
#endif
}

Object* Stat::stat_birthtime(STATE) {
#ifdef HAVE_ST_BIRTHTIME
struct timespec ts = st_.st_birthtimespec;
return Time::at(state, ts.tv_sec, ts.tv_nsec);
#else
return Primitives::failure();
#endif
}

}
3 changes: 3 additions & 0 deletions vm/builtin/stat.hpp
Original file line number Diff line number Diff line change
@@ -73,6 +73,9 @@ namespace rubinius {
// Rubinius.primitive+ :stat_ctime
Time* stat_ctime(STATE);

// Rubinius.primitive+ :stat_birthtime
Object* stat_birthtime(STATE);

class Info : public TypeInfo {
public:
BASIC_TYPEINFO(TypeInfo)

0 comments on commit d8e5c64

Please sign in to comment.