22# frozen_string_literal: true
33
44require "test_helper"
5+ require "timeout"
56
67class IntegrationTest < Minitest ::Test
78 def setup
@@ -374,24 +375,17 @@ def test_launch_mode_update_does_not_modify_main_lockfile
374375 end
375376
376377 def test_launch_mode_retries_if_setup_failed_after_successful_install
377- # RubyGems asks for confirmation when uninstalling a gem, so we need to be able to write the `y` to stdin
378- uninstall_rails = -> ( ) {
379- stdin , _stdout , _stderr , wait_thread = Open3 . popen3 ( "gem" , "uninstall" , "rails" )
380- stdin . write ( "y\n " )
381- wait_thread . join
382- }
383-
384378 in_temp_dir do |dir |
385379 File . write ( File . join ( dir , "Gemfile" ) , <<~RUBY )
386380 source "https://rubygems.org"
387- gem "rails"
381+ gem "rails", "8.1.1"
388382 RUBY
389383
390384 Bundler . with_unbundled_env do
391385 # Generate a lockfile first
392386 capture_subprocess_io { system ( "bundle" , "install" ) }
393387 # Uninstall the gem so that composing the bundle has to install it
394- uninstall_rails . call
388+ system ( "gem" , "uninstall" , "rails" , "-v" , "8.1.1" , "--executables" , "--silent" )
395389
396390 # Preemptively create the bundle_env file and acquire an exclusive lock on it, so that composing the bundle will
397391 # have to pause immediately after bundle installing, but before invoking Bundler.setup
@@ -400,14 +394,19 @@ def test_launch_mode_retries_if_setup_failed_after_successful_install
400394 FileUtils . touch ( bundle_env_path )
401395
402396 thread = Thread . new do
403- File . open ( bundle_env_path ) do | f |
404- f . flock ( File :: LOCK_EX )
405-
397+ file = File . open ( bundle_env_path )
398+ begin
399+ file . flock ( File :: LOCK_EX )
406400 # Give the bundle compose enough time to finish and get stuck on the lock
407401 sleep ( 2 )
408402 # Uninstall Rails after successfully bundle installing and before invoking Bundler.setup, which will cause
409403 # it to fail with `Bundler::GemNotFound`. This triggers our retry mechanism
410- uninstall_rails . call
404+ system ( "gem" , "uninstall" , "rails" , "-v" , "8.1.1" , "--executables" , "--silent" )
405+ rescue => e
406+ flunk ( e . full_message )
407+ ensure
408+ file . flock ( File ::LOCK_UN )
409+ file . close
411410 end
412411 end
413412
@@ -437,49 +436,59 @@ def test_launching_an_older_server_version
437436 private
438437
439438 def launch ( workspace_path , exec = "ruby-lsp-launcher" , extra_env = { } )
440- stdin , stdout , stderr , wait_thr = Open3 #: as untyped
441- . popen3 (
442- extra_env ,
443- Gem . ruby ,
444- File . join ( __dir__ , ".." , "exe" , exec ) ,
445- )
446- stdin . sync = true
447- stdin . binmode
448- stdout . sync = true
449- stdout . binmode
450- stderr . sync = true
451- stderr . binmode
452-
453- send_message ( stdin , {
454- id : 1 ,
455- method : "initialize" ,
456- params : {
457- initializationOptions : { } ,
458- capabilities : { general : { positionEncodings : [ "utf-8" ] } } ,
459- workspaceFolders : [ { uri : URI ::Generic . from_path ( path : workspace_path ) . to_s } ] ,
460- } ,
461- } )
462-
463- # First message is the log of initializing Ruby LSP
464- read_message ( stdout , stderr )
465- # Verify that initialization didn't fail
466- initialize_response = read_message ( stdout , stderr )
467- refute ( initialize_response [ :error ] , initialize_response . dig ( :error , :message ) )
468-
469- send_message ( stdin , { id : 2 , method : "shutdown" } )
470- send_message ( stdin , { method : "exit" } )
471-
472- # Wait until the process exits
473- wait_thr . join
474-
475- # If the child process failed, it is really difficult to diagnose what's happening unless we read what was printed
476- # to stderr
477- unless wait_thr . value . success?
478- require "timeout"
479-
480- Timeout . timeout ( 5 ) do
481- flunk ( "Process failed\n #{ stderr . read } " )
439+ stdin = nil #: IO?
440+ stdout = nil #: IO?
441+ stderr = nil #: IO?
442+
443+ begin
444+ Timeout . timeout ( 180 ) do
445+ stdin , stdout , stderr , wait_thr = Open3 #: as untyped
446+ . popen3 (
447+ extra_env ,
448+ Gem . ruby ,
449+ File . join ( __dir__ , ".." , "exe" , exec ) ,
450+ )
451+ stdin . sync = true
452+ stdin . binmode
453+ stdout . sync = true
454+ stdout . binmode
455+ stderr . sync = true
456+ stderr . binmode
457+
458+ send_message ( stdin , {
459+ id : 1 ,
460+ method : "initialize" ,
461+ params : {
462+ initializationOptions : { } ,
463+ capabilities : { general : { positionEncodings : [ "utf-8" ] } } ,
464+ workspaceFolders : [ { uri : URI ::Generic . from_path ( path : workspace_path ) . to_s } ] ,
465+ } ,
466+ } )
467+
468+ # First message is the log of initializing Ruby LSP
469+ read_message ( stdout , stderr )
470+ # Verify that initialization didn't fail
471+ initialize_response = read_message ( stdout , stderr )
472+ refute ( initialize_response [ :error ] , initialize_response . dig ( :error , :message ) )
473+
474+ send_message ( stdin , { id : 2 , method : "shutdown" } )
475+ send_message ( stdin , { method : "exit" } )
476+
477+ # Wait until the process exits
478+ wait_thr . join
479+ end
480+ rescue Timeout ::Error
481+ if stderr
482+ Timeout . timeout ( 5 ) { flunk ( "Launching the server timed out\n #{ stderr . read } " ) }
483+ end
484+
485+ if stdout
486+ Timeout . timeout ( 5 ) { flunk ( "Launching the server timed out\n #{ stdout . read } " ) }
482487 end
488+ ensure
489+ stdin &.close
490+ stdout &.close
491+ stderr &.close
483492 end
484493
485494 assert_path_exists ( File . join ( workspace_path , ".ruby-lsp" , "Gemfile" ) )
0 commit comments