@@ -59,6 +59,58 @@ def test_tmpdir_not_empty_parent
5959 end
6060 end
6161
62+ def test_writable_fallback_to_stat
63+ omit "no meaning on this platform" if /mswin|mingw/ =~ RUBY_PLATFORM
64+ Dir . mktmpdir do |tmpdir |
65+ envs = %w[ TMPDIR TMP TEMP ]
66+ oldenv = envs . each_with_object ( { } ) { |v , h | h [ v ] = ENV . delete ( v ) }
67+ begin
68+ ENV [ envs [ 0 ] ] = tmpdir
69+
70+ # Stub File.writable? to return false for our tmpdir
71+ # This simulates container environments where access(2) returns false
72+ # even though the directory is actually writable
73+ original_writable = File . method ( :writable? )
74+ File . define_singleton_method ( :writable? ) do |path |
75+ if path == tmpdir
76+ false
77+ else
78+ original_writable . call ( path )
79+ end
80+ end
81+
82+ # Should fall back to stat.writable? and succeed with a warning
83+ assert_equal ( tmpdir , assert_warn ( /File\. writable\? reports not writable but file mode bits suggest writable/ ) { Dir . tmpdir } )
84+
85+ ensure
86+ # Restore original File.writable?
87+ File . define_singleton_method ( :writable? , original_writable ) if original_writable
88+ ENV . update ( oldenv )
89+ end
90+ end
91+ end
92+
93+ def test_writable_fallback_both_fail
94+ omit "no meaning on this platform" if /mswin|mingw/ =~ RUBY_PLATFORM
95+ Dir . mktmpdir do |tmpdir |
96+ envs = %w[ TMPDIR TMP TEMP ]
97+ oldenv = envs . each_with_object ( { } ) { |v , h | h [ v ] = ENV . delete ( v ) }
98+ begin
99+ ENV [ envs [ 0 ] ] = tmpdir
100+
101+ # Make directory not writable (both File.writable? and stat.writable? will return false)
102+ File . chmod ( 0555 , tmpdir )
103+
104+ # Should reject the directory with "not writable" warning
105+ assert_not_equal ( tmpdir , assert_warn ( /is not writable/ ) { Dir . tmpdir } )
106+
107+ ensure
108+ File . chmod ( 0755 , tmpdir )
109+ ENV . update ( oldenv )
110+ end
111+ end
112+ end
113+
62114 def test_no_homedir
63115 bug7547 = '[ruby-core:50793]'
64116 home , ENV [ "HOME" ] = ENV [ "HOME" ] , nil
0 commit comments