From 4b734dcf093778f363764915d83f9cc73fc85913 Mon Sep 17 00:00:00 2001 From: Jens Johansen Date: Wed, 7 May 2025 08:53:51 +0200 Subject: [PATCH 1/2] [path] Fix test race condition caused by changing Directory.current --- pkgs/path/test/io_test.dart | 115 ++++++++++++++++++------------------ 1 file changed, 56 insertions(+), 59 deletions(-) diff --git a/pkgs/path/test/io_test.dart b/pkgs/path/test/io_test.dart index e428b89a..a26c4873 100644 --- a/pkgs/path/test/io_test.dart +++ b/pkgs/path/test/io_test.dart @@ -43,81 +43,78 @@ void main() { test( 'uses the previous working directory if deleted', - () { - final dir = io.Directory.current.path; - try { - final temp = io.Directory.systemTemp.createTempSync('path_test'); - final tempPath = temp.resolveSymbolicLinksSync(); - io.Directory.current = temp; - - // Call "current" once so that it can be cached. - expect(path.normalize(path.absolute(path.current)), equals(tempPath)); - - temp.deleteSync(); - - // Even though the directory no longer exists, no exception is thrown. - expect(path.normalize(path.absolute(path.current)), equals(tempPath)); - } finally { - io.Directory.current = dir; - } - }, + currentDirHelper(() { + final temp = io.Directory.systemTemp.createTempSync('path_test'); + final tempPath = temp.resolveSymbolicLinksSync(); + io.Directory.current = temp; + + // Call "current" once so that it can be cached. + expect(path.normalize(path.absolute(path.current)), equals(tempPath)); + + temp.deleteSync(); + + // Even though the directory no longer exists, no exception is thrown. + expect(path.normalize(path.absolute(path.current)), equals(tempPath)); + }), //TODO: Figure out why this is failing on windows and fix! skip: io.Platform.isWindows ? 'Untriaged failure on Windows' : false, ); }); - test('registers changes to the working directory', () { + test('registers changes to the working directory', currentDirHelper(() { final dir = io.Directory.current.path; - try { - expect(path.absolute('foo/bar'), equals(path.join(dir, 'foo/bar'))); + expect(path.absolute('foo/bar'), equals(path.join(dir, 'foo/bar'))); + expect( + path.absolute('foo/bar'), + equals(path.context.join(dir, 'foo/bar')), + ); + + io.Directory.current = path.dirname(dir); + expect( + path.normalize(path.absolute('foo/bar')), + equals(path.normalize(path.join(dir, '../foo/bar'))), + ); + expect( + path.normalize(path.absolute('foo/bar')), + equals(path.normalize(path.context.join(dir, '../foo/bar'))), + ); + })); + + // Regression test for #35. This tests against the *actual* working directory + // rather than just a custom context because we do some processing in + // [path.current] that has clobbered the root in the past. + test( + 'absolute works on root working directory', + currentDirHelper(() { + io.sleep(const Duration(seconds: 2)); + io.Directory.current = path.rootPrefix(path.current); + io.sleep(const Duration(seconds: 2)); + expect( - path.absolute('foo/bar'), - equals(path.context.join(dir, 'foo/bar')), + path.relative(path.absolute('foo/bar'), from: path.current), + path.relative(path.absolute('foo/bar')), ); - io.Directory.current = path.dirname(dir); expect( path.normalize(path.absolute('foo/bar')), - equals(path.normalize(path.join(dir, '../foo/bar'))), + equals(path.normalize(path.join(path.current, '../foo/bar'))), ); + expect( path.normalize(path.absolute('foo/bar')), - equals(path.normalize(path.context.join(dir, '../foo/bar'))), + equals(path.normalize(path.join(path.current, '../foo/bar'))), ); - } finally { - io.Directory.current = dir; - } - }); - - // Regression test for #35. This tests against the *actual* working directory - // rather than just a custom context because we do some processing in - // [path.current] that has clobbered the root in the past. - test( - 'absolute works on root working directory', - () { - final dir = path.current; - try { - io.Directory.current = path.rootPrefix(path.current); - - expect( - path.relative(path.absolute('foo/bar'), from: path.current), - path.relative(path.absolute('foo/bar')), - ); - - expect( - path.normalize(path.absolute('foo/bar')), - equals(path.normalize(path.join(path.current, '../foo/bar'))), - ); - - expect( - path.normalize(path.absolute('foo/bar')), - equals(path.normalize(path.join(path.current, '../foo/bar'))), - ); - } finally { - io.Directory.current = dir; - } - }, + }), //TODO(kevmoo): figure out why this is failing on windows and fix! skip: io.Platform.isWindows ? 'Untriaged failure on Windows' : null, ); } + +dynamic Function() currentDirHelper(dynamic Function() body) { + var savedCurrentDirectory = io.Directory.current; + return () => io.IOOverrides.runZoned(body, + getCurrentDirectory: () => savedCurrentDirectory, + setCurrentDirectory: (dir) { + savedCurrentDirectory = io.Directory(dir); + }); +} From d3eca1bd8a240d705767954d4f9d97813919108a Mon Sep 17 00:00:00 2001 From: Jens Johansen Date: Wed, 7 May 2025 13:41:25 +0200 Subject: [PATCH 2/2] feedback and removed debug sleep left in by accident --- pkgs/path/test/io_test.dart | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/pkgs/path/test/io_test.dart b/pkgs/path/test/io_test.dart index a26c4873..fa33ab92 100644 --- a/pkgs/path/test/io_test.dart +++ b/pkgs/path/test/io_test.dart @@ -43,7 +43,7 @@ void main() { test( 'uses the previous working directory if deleted', - currentDirHelper(() { + withLocalCurrentDirectory(() { final temp = io.Directory.systemTemp.createTempSync('path_test'); final tempPath = temp.resolveSymbolicLinksSync(); io.Directory.current = temp; @@ -61,7 +61,8 @@ void main() { ); }); - test('registers changes to the working directory', currentDirHelper(() { + test('registers changes to the working directory', + withLocalCurrentDirectory(() { final dir = io.Directory.current.path; expect(path.absolute('foo/bar'), equals(path.join(dir, 'foo/bar'))); expect( @@ -85,10 +86,8 @@ void main() { // [path.current] that has clobbered the root in the past. test( 'absolute works on root working directory', - currentDirHelper(() { - io.sleep(const Duration(seconds: 2)); + withLocalCurrentDirectory(() { io.Directory.current = path.rootPrefix(path.current); - io.sleep(const Duration(seconds: 2)); expect( path.relative(path.absolute('foo/bar'), from: path.current), @@ -110,7 +109,11 @@ void main() { ); } -dynamic Function() currentDirHelper(dynamic Function() body) { +/// Runs [body] in a zone with a local current working directory. +/// +/// Avoids clobbering the current working directory of the entire process +/// when writing to it and reading it back through `dart:io` functions. +R Function() withLocalCurrentDirectory(R Function() body) { var savedCurrentDirectory = io.Directory.current; return () => io.IOOverrides.runZoned(body, getCurrentDirectory: () => savedCurrentDirectory,