Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Try to reproduce RSpec example binding in Ractor (but cannot as of now) #9

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 53 additions & 1 deletion lib/pbt.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,62 @@ class PropertyFailure < StandardError; end
extend Check::RunnerMethods
extend Check::ConfigurationMethods

require "prism"
class BlockInspector < Prism::Visitor
attr_reader :proc_str
def initialize(line)
@line = line
@proc_str = nil
super()
end

def visit_call_node(node)
if node.name == :property && node.block.opening_loc.start_line == @line
# Extract the block content
# e.g.
# "do |n|\n raise unless n.is_a?(Integer)\n end"
@proc_str = "(Proc.new " + node.block.slice + ")"
end

super
end
end

# @param args [Array<Pbt::Arbitrary>]
# @param kwargs [Hash<Symbol->Pbt::Arbitrary>]
# @return [Property]
def self.property(*args, **kwargs, &predicate)
def self.property(ex, *args, **kwargs, &predicate)
# This code reproduces predicate block's content as String
file, line = predicate.source_location
res = Prism.parse_file(file)
foo = BlockInspector.new(line)
res.value.statements.accept(foo)
ps = foo.proc_str

# This works
# Get the object_id of the example instance and get the instance by the id
id = ex.object_id
pr1 = ObjectSpace._id2ref(id).instance_eval ps
pr1.call(1)

# This doesn't work. We need to create an example instance in Ractor block.
r = Ractor.new(id, ps) do |id, ps|
# If we can run the proc as if it's in a RSpec example instance
# we can use `expect`, `be`, `raise_error`, etc.
pr2 = ObjectSpace._id2ref(id).instance_eval ps
pr2.call(1)
end

begin
r.take
rescue => e
# --- Caused by: ---
# RangeError:
# "3400" is id of the unshareable object on multi-ractor
# ./lib/pbt.rb:49:in `_id2ref'
raise e.cause
end

arb = to_arbitrary(args, kwargs)
Check::Property.new(arb, &predicate)
end
Expand Down
32 changes: 32 additions & 0 deletions spec/e2e/prism_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# frozen_string_literal: true

RSpec.describe Pbt do
describe "arguments" do
it "passes a value that the given single arbitrary generates" do
RSpec::Matchers::BuiltIn.constants.each { |c| RSpec::Matchers::BuiltIn.const_get(c) }

r = Ractor.new do
loop do
v, msg = Ractor.receive
v.instance_eval msg
end
end

r.send([self, "expect(1).to eq 1"]) # move: true doesn't work by TypeError: can't create instance of singleton class

begin
r.take
rescue => e
raise e.cause
end

# Pbt.assert do
# Pbt.property(self, Pbt.integer) do |n|
# puts "called!!!!!!!!!!!!!"
# puts n
# raise unless n.is_a?(Integer)
# end
# end
end
end
end
Loading