From 22d391dc637dd756fdcdfc0ece4a71753b5b2ddb Mon Sep 17 00:00:00 2001 From: Samuel Williams Date: Sat, 10 Dec 2022 13:54:53 +1300 Subject: [PATCH] Add spec for fiber storage. (#6896) --- cont.c | 7 ++ spec/ruby/core/fiber/storage_spec.rb | 111 +++++++++++++++++++++++++++ 2 files changed, 118 insertions(+) create mode 100644 spec/ruby/core/fiber/storage_spec.rb diff --git a/cont.c b/cont.c index e050deefdc..49629efd90 100644 --- a/cont.c +++ b/cont.c @@ -2090,10 +2090,17 @@ fiber_storage_validate_each(VALUE key, VALUE value, VALUE _argument) static void fiber_storage_validate(VALUE value) { + // nil is an allowed value and will be lazily initialized. + if (value == Qnil) return; + if (!RB_TYPE_P(value, T_HASH)) { rb_raise(rb_eTypeError, "storage must be a hash"); } + if (RB_OBJ_FROZEN(value)) { + rb_raise(rb_eFrozenError, "storage must not be frozen"); + } + rb_hash_foreach(value, fiber_storage_validate_each, Qundef); } diff --git a/spec/ruby/core/fiber/storage_spec.rb b/spec/ruby/core/fiber/storage_spec.rb new file mode 100644 index 0000000000..4adfd30d2b --- /dev/null +++ b/spec/ruby/core/fiber/storage_spec.rb @@ -0,0 +1,111 @@ +require_relative '../../spec_helper' + +require 'fiber' + +describe "Fiber.new(storage:)" do + ruby_version_is "3.2" do + it "creates a Fiber with the given storage" do + storage = {life: 42} + fiber = Fiber.new(storage: storage) { Fiber.current.storage } + fiber.resume.should == storage + end + + it "creates a fiber with lazily initialized storage" do + fiber = Fiber.new(storage: nil) {} + fiber.storage.should == {} + end + + it "creates a fiber by inheriting the storage of the parent fiber" do + fiber = Fiber.new(storage: {life: 42}) do + Fiber.new { Fiber.current.storage }.resume + end + fiber.resume.should == {life: 42} + end + + it "creates a fiber with a reference to the storage of the parent fiber" do + fiber = Fiber.new(storage: {life: 42}) do + Fiber.new(storage: false) { Fiber[:life] = 43 }.resume + Fiber.current.storage + end + fiber.resume.should == {life: 43} + end + + it "cannot create a fiber with non-hash storage" do + -> { Fiber.new(storage: 42) {} }.should raise_error(TypeError) + end + end +end + +describe "Fiber#storage" do + ruby_version_is "3.2" do + it "can clear the storage of the fiber" do + fiber = Fiber.new(storage: {life: 42}) { Fiber.current.storage } + fiber.storage = nil + fiber.resume.should == {} + end + + it "can set the storage of the fiber" do + fiber = Fiber.new(storage: {life: 42}) { Fiber.current.storage } + fiber.storage = {life: 43} + fiber.resume.should == {life: 43} + end + + it "can't set the storage of the fiber to non-hash" do + fiber = Fiber.new(storage: {life: 42}) { Fiber.current.storage } + -> { fiber.storage = 42 }.should raise_error(TypeError) + end + + it "can't set the storage of the fiber to a frozen hash" do + fiber = Fiber.new(storage: {life: 42}) { Fiber.current.storage } + -> { fiber.storage = {life: 43}.freeze }.should raise_error(FrozenError) + end + + it "can't set the storage of the fiber to a hash with non-symbol keys" do + fiber = Fiber.new(storage: {life: 42}) { Fiber.current.storage } + -> { fiber.storage = {life: 43, Object.new => 44} }.should raise_error(TypeError) + end + end +end + +describe "Fiber.[]" do + ruby_version_is "3.2" do + it "returns the value of the given key in the storage of the current fiber" do + Fiber.new(storage: {life: 42}) { Fiber[:life] }.resume.should == 42 + end + + it "returns nil if the key is not present in the storage of the current fiber" do + Fiber.new(storage: {life: 42}) { Fiber[:death] }.resume.should be_nil + end + + it "returns nil if the current fiber has no storage" do + Fiber.new { Fiber[:life] }.resume.should be_nil + end + end +end + +describe "Fiber.[]=" do + ruby_version_is "3.2" do + it "sets the value of the given key in the storage of the current fiber" do + Fiber.new(storage: {life: 42}) { Fiber[:life] = 43; Fiber[:life] }.resume.should == 43 + end + + it "sets the value of the given key in the storage of the current fiber" do + Fiber.new(storage: {life: 42}) { Fiber[:death] = 43; Fiber[:death] }.resume.should == 43 + end + + it "sets the value of the given key in the storage of the current fiber" do + Fiber.new { Fiber[:life] = 43; Fiber[:life] }.resume.should == 43 + end + end +end + +describe "Thread.new" do + ruby_version_is "3.2" do + it "creates a thread with the storage of the current fiber" do + fiber = Fiber.new(storage: {life: 42}) do + Thread.new { Fiber.current.storage }.value + end + fiber.resume.should == {life: 42} + end + end +end