debug icon indicating copy to clipboard operation
debug copied to clipboard

Chrome hang with Capybara and RSpec

Open ybiquitous opened this issue 4 years ago • 13 comments

Your environment

  • ruby -v: ruby 3.0.2p107 (2021-07-07 revision 0db68f0233) [arm64-darwin20]
  • rdbg -v: rdbg 1.3.1 (f5ff7f7)
  • OS: macOS 11.6 20G165

Describe the bug

When running Capybara and RSpec with Chrome under a certain condition, Chrome hangs. This never occurs with the child / parent fork mode. It occurs only with the both fork mode (default).

To Reproduce

I prepare a repository for reproduction. Please follow the instruction on the README: https://github.com/ybiquitous/ruby-debug-with-capybara

https://user-images.githubusercontent.com/473530/138542439-59d77839-886b-4f6b-bf9b-37f05cd538f9.mov

Note: Here is a simplified version on Gist (thanks to @nishio-dens): https://gist.github.com/nishio-dens/c64245eb143e9c3b22b9d7b9f93f25d2

Expected behavior

I think Chrome should not hang.

Additional context

None.

ybiquitous avatar Oct 23 '21 04:10 ybiquitous

OMG: "Failed to find Chrome binary." my WSL2 environment it's difficult...

without chrome (with REPL), do you have same trouble?

ko1 avatar Oct 25 '21 07:10 ko1

without chrome (with REPL), do you have same trouble?

No, this problem occurs with Capybara and Chrome (also headless mode).

OMG: "Failed to find Chrome binary." my WSL2 environment it's difficult...

Oh, I can reproduce it with macOS. The webdrivers gem's instruction might help you, but it seems limited... https://github.com/titusfortner/webdrivers#wslv2-support

ybiquitous avatar Oct 25 '21 07:10 ybiquitous

Thank you for the information. @ono-max san can you try it?

ko1 avatar Oct 25 '21 07:10 ko1

Temporarily, is there a way to stop the debug server only when starting Capybara?

ybiquitous avatar Nov 04 '21 01:11 ybiquitous

Unfortunately, there is no way to disable debugger now. I'll consider.

This never occurs with the child / parent fork mode. It occurs only with the both fork mode (default).

Instead of disabling the debugger, you can choose child or parent debug mode (maybe parent mode is fine for you).

On the debugger REPL:

> config set fork_mode parent

On the Ruby code:

DEBUGGERR__::CONFIG[:fork_mode] = :parent

ko1 avatar Nov 05 '21 05:11 ko1

@ko1 Thank you for the helpful advice! The workaround works. 👍🏼

FYI. The following code via ENV also works:

ENV["RUBY_DEBUG_FORK_MODE"] ||= "parent"

ybiquitous avatar Nov 05 '21 09:11 ybiquitous

Spent hours debugging why starting multiple browsers (Chrome or Firefox) with Capybara failed to close them all (it only ever closed the last one created). Turned out it was the #at_exit call here:

https://github.com/ruby/debug/blob/3b3ce8ff51d85b39422d7a0e96c9e2ab3c2973d0/lib/debug/local.rb#L93-L105

Everytime Capybara forked off a new browser it would register a new #at_exit to clean itself up here:

https://github.com/teamcapybara/capybara/blob/a12915a6ae6faf1f79445ccd9e2b29d4c3850d7a/lib/capybara/selenium/driver.rb#L509-L514

So when these were called in reverse order of creation, it would clean up the latest browser then hit the #at_exit created from #after_fork_parent and block forever. It would never make it to the next browser to clean it up.

Finally found this issue and after setting the environment variable RUBY_DEBUG_FORK_MODE=parent it all works.

nickjer avatar Nov 14 '21 19:11 nickjer

Adding the debug gem broke my Capybara setup (rspec would never exit), but as some other people mentioned running my specs with RUBY_DEBUG_FORK_MODE=parent bin/rspec solved it.

Not sure whether this is an issue with debug or Capybara, but would be great to get it resolved so everything works out-of-the-box without requiring ENV vars.

marckohlbrugge avatar Jun 30 '22 16:06 marckohlbrugge

Thank you for heading up. Can you confirm that the process is hung around https://github.com/ruby/debug/blob/master/lib/debug/local.rb#L101 ?

ko1 avatar Jul 05 '22 18:07 ko1

Hi @ko1 👋 Long time no see! I found myself looking at a very similar situation when running rspec tests with Capybara today. The rspec process never completes. The main thread is hung up on the local.rb line that you referenced above:

/Users/.../ruby/3.1.1/lib/ruby/gems/3.1.0/gems/debug-1.6.1/lib/debug/local.rb:101:in `waitpid'
/Users/.../ruby/3.1.1/lib/ruby/gems/3.1.0/gems/debug-1.6.1/lib/debug/local.rb:101:in `block in after_fork_parent'

The spec forked twice (Once for a capybara server and once to run an external pdflatex command.):

DEBUGGER: Attaching after process 5803 fork to child process 6425
DEBUGGER[/Users/.../ruby/3.1.1/bin/rspec#6460]: Attaching after process 5803 fork to child process 6460

Things work well if the spec only forks once.

I'm happy to provide additional details if they help to narrow down the problem.

senny avatar Sep 14 '22 12:09 senny

Thank you, could you make a small repro-code?

ko1 avatar Sep 16 '22 21:09 ko1

My first attempts in making a reproduction were not successful. It's very possible that I'm missing a detail that triggers the condition in our main codebase. There are a lot of moving parts and layers involved that could make a difference.

I'll try a bit more but I'm not sure I'll be able to isolate the behavior.

senny avatar Sep 19 '22 06:09 senny

I am seeing the same behavior with Capybara + RSpec + selenium at the moment. In my case I am registering a custom chrome driver:

Capybara.register_driver :selenium_chrome_headless_with_downloads do |app|
  browser_options = Selenium::WebDriver::Chrome::Options.new

  [
    "--no-sandbox",
    "--headless",
    "--disable-site-isolation-trials",
    "--disable-dev-shm-usage"
  ].each { |arg| browser_options.add_argument(arg) }

  browser_options.add_preference(:download, prompt_for_download: false, default_directory: DownloadHelpers::PATH.to_s)
  browser_options.add_preference(:browser, set_download_behavior: {behavior: "allow"})
  Capybara::Selenium::Driver.new(app, browser: :chrome, capabilities: [browser_options])
end

If any tests driven by this driver run then rspec fails to exit. Interestingly tests driven by the standard selenium_chrome_headless driver don't have this problem.

I currently have DEBUGGER__::CONFIG[:fork_mode] = :parent at the top of my config file and that seems to have solved the problem.

rbclark avatar Sep 22 '22 13:09 rbclark