-
-
Notifications
You must be signed in to change notification settings - Fork 102
Override RSpec::Support::StdErrSplitter#clone #598
Override RSpec::Support::StdErrSplitter#clone #598
Conversation
With the default definition, cloning a StdErrSplitter results in the clone having the same @orig_stderr instance as the original Splitter has, which means that reopening either Splitter affects both (since the underlying stream is not cloned). This is different from the behavior of an actual stream, in a way that causes a problem for the CaptureStreamToTempfile in rspec-matchers (used by the `output.to_stderr_from_any_process` matcher), because it clones the original stream to store it for restoration, and then `reopen`s it at a Tempfile temporarily. But then when it goes back to _restore_ the stream, it doesn't actually do so, since the clone's stream reference is the same instance as the original Splitter has - it just reopens it with the Tempfile again.
This is what the matcher is trying to actually do, so ensuring that it works makes a stronger test.
tempfile.close | ||
tempfile.unlink | ||
end | ||
expect($stderr.to_io).not_to be_a(File) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This test fails on this line without the change - $stderr
is actually still pointed at the Tempfile (despite that it's closed), and not the original process stream.
Ugh, I'll put this in draft until I can figure out how to get jruby installed on an m2 -.- |
If it helps the legacy ruby builds are all docker containers from https://github.com/rspec/docker-ci |
Yeah, I stalled out on installing jruby locally and decided I'd need to work out how to dev in a container. I will get back to this, I'm just slammed at work currently -.- |
I wonder if Tempfile.new("foo").is_a?(File) is even true in JRuby Is it in MRI? |
My read of that is that JRuby returns a file from those methods so should respond true |
Hm. I finally found time/energy to dig back into this and got jruby set up locally (I'm on an m3 now!), but.. the test passes on jruby-9.1.17.0 locally for me.
I definitely assumed this was a general jruby problem; any obvious special-ness about jruby-in-ci? I do get two other failures locally when I run the full suite:
I don't think it's related though - that seems to be a problem with my jruby setup (it's failing to run (Setting |
Well.. so no; MRI
JRuby
It's a bit misleading, because Tempfile delegates most of its methods (including its console representations) to the File it wraps. I found an interesting thread discussing this particular method. Apparently it's the only case where an object's class and singleton class can be dynamically changed. |
ceb2c96
to
da06053
Compare
I commented on the wrong PR with my findings. |
On my Linux x86-64 machine. JRuby 9.1.17.0:
but:
Same for OpenJDK 22.0.1.u0-1 and OpenJDK 8.412.u08-1. Same on JRuby 9.2.14. For some reason, after the update there are less JRuby versions (no 9.3, no 9.4) to experiment with in RVM.
At a glance, this seems to be a MacOS vs Linux thing. Unless you'll succeed with replacing if RSpec::Support::Ruby.jruby?
skip "Failing on JRuby on Linux"
end @headius just a heads-up. |
-.- of course, osx. So we might be looking at a jruby bug then? I wouldn't expect a behavioral difference here, but it does seem like an edge that might have easily been missed; I saw several other bug reports about jruby and IO#reopen.. I'm not really happy leaving that behavioral difference in place in jruby+Linux, though I guess it's a fairly niche feature. I will see if I can come up with any other way to detect the state first. |
JRuby has since 2009 implemented Tempfile natively as a subclass of File, because it made sense to us to do so and because at the time the tempfile.rb library was poorly designed. Generally you should see this reflected in
The tempfile.rb version has improved over the years, and as of JRuby 10 (next major, due later this year) we will switch to using the same library for JRuby's Tempfile. This version works as in CRuby, with Tempfile delegating to a wrapped File:
If you are on a JRuby version prior to 10, I believe it is possible to activate and use the tempfile.rb library from the gem, which then makes it behavior like CRuby. This may be the source of some confusion:
The alternative hierarchy is certainly a difference but I don't know if I'd classify it as a bug per se; both versions of Tempfile should behave the same from an API perspective. I'm happy to answer any other questions about this! |
JRuby historically used its own implementation of Tempfile. This spec relies on a check that fails on JRuby. Evidently, JRuby starting from version 10 is going to behave similarly to CRuby.
With the default definition, cloning a StdErrSplitter results in the clone having the same @orig_stderr instance as the original Splitter has, which means that reopening either Splitter affects both (since the underlying stream is not cloned).
This is different from the behavior of an actual stream, in a way that causes a problem for the CaptureStreamToTempfile in rspec-matchers (used by the
output.to_stderr_from_any_process
matcher), because it clones the original stream to store it for restoration, and thenreopen
s it at a Tempfile temporarily. But then when it goes back to restore the stream, it doesn't actually do so, since the clone's stream reference is the same instance as the original Splitter has - it just reopens it with the Tempfile again.This is a supporting PR for rspec/rspec-expectations#1460 - before I can make compound
to_stderr_from_any_process
matchers work, I need the "restore the stream after capturing" behavior to actually work.