| ... | @@ -71,19 +71,7 @@ pub fn main() anyerror!void { | ... | @@ -71,19 +71,7 @@ pub fn main() anyerror!void { |
| 71 | try testExec(allocator, "heLLo", "hello from exe\n"); | 71 | try testExec(allocator, "heLLo", "hello from exe\n"); |
| 72 | | 72 | |
| 73 | // now rename the exe to not have an extension | 73 | // now rename the exe to not have an extension |
| 74 | { | 74 | try renameExe(tmp.dir, "hello.exe", "hello"); |
| 75 | var attempt: u5 = 0; | | |
| 76 | while (true) break tmp.dir.rename("hello.exe", "hello") catch |err| switch (err) { | | |
| 77 | error.AccessDenied => { | | |
| 78 | if (attempt == 13) return error.AccessDenied; | | |
| 79 | // give the kernel a chance to finish closing the executable handle | | |
| 80 | std.os.windows.kernel32.Sleep(@as(u32, 1) << attempt >> 1); | | |
| 81 | attempt += 1; | | |
| 82 | continue; | | |
| 83 | }, | | |
| 84 | else => |e| return e, | | |
| 85 | }; | | |
| 86 | } | | |
| 87 | | 75 | |
| 88 | // with extension should now fail | 76 | // with extension should now fail |
| 89 | try testExecError(error.FileNotFound, allocator, "hello.exe"); | 77 | try testExecError(error.FileNotFound, allocator, "hello.exe"); |
| ... | @@ -91,7 +79,7 @@ pub fn main() anyerror!void { | ... | @@ -91,7 +79,7 @@ pub fn main() anyerror!void { |
| 91 | try testExec(allocator, "heLLo", "hello from exe\n"); | 79 | try testExec(allocator, "heLLo", "hello from exe\n"); |
| 92 | | 80 | |
| 93 | try tmp.dir.makeDir("something"); | 81 | try tmp.dir.makeDir("something"); |
| 94 | try tmp.dir.rename("hello", "something/hello.exe"); | 82 | try renameExe(tmp.dir, "hello", "something/hello.exe"); |
| 95 | | 83 | |
| 96 | const relative_path_no_ext = try std.fs.path.join(allocator, &.{ tmp_relative_path, "something/hello" }); | 84 | const relative_path_no_ext = try std.fs.path.join(allocator, &.{ tmp_relative_path, "something/hello" }); |
| 97 | defer allocator.free(relative_path_no_ext); | 85 | defer allocator.free(relative_path_no_ext); |
| ... | @@ -118,14 +106,14 @@ pub fn main() anyerror!void { | ... | @@ -118,14 +106,14 @@ pub fn main() anyerror!void { |
| 118 | try testExecError(error.InvalidExe, allocator, "hello"); | 106 | try testExecError(error.InvalidExe, allocator, "hello"); |
| 119 | | 107 | |
| 120 | // If we now rename hello.exe to have no extension, it will behave differently | 108 | // If we now rename hello.exe to have no extension, it will behave differently |
| 121 | try tmp.dir.rename("hello.exe", "hello"); | 109 | try renameExe(tmp.dir, "hello.exe", "hello"); |
| 122 | | 110 | |
| 123 | // Now, trying to execute it without an extension should treat InvalidExe as recoverable | 111 | // Now, trying to execute it without an extension should treat InvalidExe as recoverable |
| 124 | // and skip over it and find hello.bat and execute that | 112 | // and skip over it and find hello.bat and execute that |
| 125 | try testExec(allocator, "hello", "hello from bat\r\n"); | 113 | try testExec(allocator, "hello", "hello from bat\r\n"); |
| 126 | | 114 | |
| 127 | // If we rename the invalid exe to something else | 115 | // If we rename the invalid exe to something else |
| 128 | try tmp.dir.rename("hello", "goodbye"); | 116 | try renameExe(tmp.dir, "hello", "goodbye"); |
| 129 | // Then we should now get FileNotFound when trying to execute 'goodbye', | 117 | // Then we should now get FileNotFound when trying to execute 'goodbye', |
| 130 | // since that is what the original error will be after searching for 'goodbye' | 118 | // since that is what the original error will be after searching for 'goodbye' |
| 131 | // in the cwd. It will try to execute 'goodbye' from the PATH but the InvalidExe error | 119 | // in the cwd. It will try to execute 'goodbye' from the PATH but the InvalidExe error |
| ... | @@ -151,7 +139,7 @@ pub fn main() anyerror!void { | ... | @@ -151,7 +139,7 @@ pub fn main() anyerror!void { |
| 151 | try testExec(allocator, "hello", "hello from bat\r\n"); | 139 | try testExec(allocator, "hello", "hello from bat\r\n"); |
| 152 | | 140 | |
| 153 | // If we rename something/hello.exe to something/goodbye.exe | 141 | // If we rename something/hello.exe to something/goodbye.exe |
| 154 | try tmp.dir.rename("something/hello.exe", "something/goodbye.exe"); | 142 | try renameExe(tmp.dir, "something/hello.exe", "something/goodbye.exe"); |
| 155 | // And try to execute goodbye, then the one in something should be found | 143 | // And try to execute goodbye, then the one in something should be found |
| 156 | // since the one in cwd is an invalid executable | 144 | // since the one in cwd is an invalid executable |
| 157 | try testExec(allocator, "goodbye", "hello from exe\n"); | 145 | try testExec(allocator, "goodbye", "hello from exe\n"); |
| ... | @@ -196,7 +184,7 @@ pub fn main() anyerror!void { | ... | @@ -196,7 +184,7 @@ pub fn main() anyerror!void { |
| 196 | var subdir_cwd = try tmp.dir.openDir(denormed_something_subdir_wtf8, .{}); | 184 | var subdir_cwd = try tmp.dir.openDir(denormed_something_subdir_wtf8, .{}); |
| 197 | defer subdir_cwd.close(); | 185 | defer subdir_cwd.close(); |
| 198 | | 186 | |
| 199 | try tmp.dir.rename("something/goodbye.exe", "hello.exe"); | 187 | try renameExe(tmp.dir, "something/goodbye.exe", "hello.exe"); |
| 200 | try subdir_cwd.setAsCwd(); | 188 | try subdir_cwd.setAsCwd(); |
| 201 | | 189 | |
| 202 | // clear the PATH again | 190 | // clear the PATH again |
| ... | @@ -229,3 +217,17 @@ fn testExecWithCwd(allocator: std.mem.Allocator, command: []const u8, cwd: ?[]co | ... | @@ -229,3 +217,17 @@ fn testExecWithCwd(allocator: std.mem.Allocator, command: []const u8, cwd: ?[]co |
| 229 | try std.testing.expectEqualStrings("", result.stderr); | 217 | try std.testing.expectEqualStrings("", result.stderr); |
| 230 | try std.testing.expectEqualStrings(expected_stdout, result.stdout); | 218 | try std.testing.expectEqualStrings(expected_stdout, result.stdout); |
| 231 | } | 219 | } |
| | 220 | |
| | 221 | fn renameExe(dir: std.fs.Dir, old_sub_path: []const u8, new_sub_path: []const u8) !void { |
| | 222 | var attempt: u5 = 0; |
| | 223 | while (true) break dir.rename(old_sub_path, new_sub_path) catch |err| switch (err) { |
| | 224 | error.AccessDenied => { |
| | 225 | if (attempt == 13) return error.AccessDenied; |
| | 226 | // give the kernel a chance to finish closing the executable handle |
| | 227 | std.os.windows.kernel32.Sleep(@as(u32, 1) << attempt >> 1); |
| | 228 | attempt += 1; |
| | 229 | continue; |
| | 230 | }, |
| | 231 | else => |e| return e, |
| | 232 | }; |
| | 233 | } |