blob: 1414bd83c83239cf37cc72647cd7aa0274cad69f [file] [log] [blame]
// Copyright (c) 2024, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
// Verify that file system APIs handle (long) UNC paths correctly on Windows
// and that two phase `File::Copy` implementation (which first creates a
// temporary file and then replaces the destination) uses correct location
// for temporary file.
import 'dart:io';
import 'package:expect/expect.dart';
import 'package:path/path.dart' as p;
// Run a number of tests using the given [filePath]: create the file,
// copy it to [anotherFilePath], rename it to [anotherFilePath] and then
// rename it back to [filePath].
void testFilePath(String filePath, String anotherFilePath) {
void checkContent(File f) {
Expect.isTrue(f.existsSync());
Expect.equals(filePath, f.readAsStringSync());
}
final f = File(filePath);
f.writeAsStringSync(filePath);
checkContent(f);
f.copySync(anotherFilePath);
checkContent(File(anotherFilePath));
File(anotherFilePath).deleteSync();
Expect.isFalse(File(anotherFilePath).existsSync());
f.renameSync(anotherFilePath);
checkContent(File(anotherFilePath));
File(anotherFilePath).renameSync(f.path);
checkContent(f);
f.deleteSync();
Expect.isFalse(f.existsSync());
}
// Verify a sequence of events induced by [testFilePath] in the temporary
// directory [subdirPath], which servers as parent folder for `anotherFilePath`.
void verifyEvents(List<FileSystemEvent> events, String anotherFilePath) {
// We expect `f.copySync(anotherFilePath)` to create a temporary file in
// [subdirPath] and then overwrite `anotherFilePath` with it.
Expect.isTrue(events[0] is FileSystemCreateEvent);
Expect.isTrue(
events[1] is FileSystemModifyEvent && events[1].path == events[0].path);
Expect.isTrue(
events[2] is FileSystemModifyEvent && events[2].path == events[0].path);
Expect.isTrue(events[3] is FileSystemMoveEvent &&
events[3].path == events[0].path &&
(events[3] as FileSystemMoveEvent).destination == anotherFilePath);
// File(anotherFilePath).deleteSync();
Expect.isTrue(
events[4] is FileSystemDeleteEvent && events[4].path == anotherFilePath);
// f.renameSync(anotherFilePath);
Expect.isTrue(
events[5] is FileSystemCreateEvent && events[5].path == anotherFilePath);
// File(anotherFilePath).deleteSync();
Expect.isTrue(
events[6] is FileSystemDeleteEvent && events[6].path == anotherFilePath);
}
// Convert `C:\x\y\z` to `\\localhost\C$\x\y\z`.
String toUnc(String path) =>
r'\\localhost\' + path[0] + r'$' + path.substring(2);
void main() async {
// This is Windows only test.
if (!Platform.isWindows) {
return;
}
final temp = Directory.systemTemp.createTempSync().absolute;
try {
print(temp.path);
print(temp.path.length);
final subdir1Path = p.join(temp.path, 'x' * (251 - temp.path.length));
Directory(subdir1Path).createSync();
Expect.isTrue(Directory(subdir1Path).existsSync());
final subdir2Path = p.join(temp.path, 'y' * (251 - temp.path.length));
Directory(subdir2Path).createSync();
Expect.isTrue(Directory(subdir2Path).existsSync());
final subdir1Events = Directory(subdir1Path)
.watch(recursive: true)
.takeWhile((event) => !event.path.contains('TEST_DONE'))
.toList();
final subdir2Events = Directory(subdir2Path)
.watch(recursive: true)
.takeWhile((event) => !event.path.contains('TEST_DONE'))
.toList();
// Make UNC path by replacing X: with \\localhost\X$\
final tempPathUnc = toUnc(temp.path);
print(tempPathUnc);
Expect.isTrue(Directory(tempPathUnc).existsSync());
testFilePath(
p.join(temp.path, 'a' * (250 - temp.path.length)),
p.join(subdir1Path, 'f'),
);
// Note: WinAPI seems to refuse to move file from UNC path into non-UNC
// path event though they actually point to the same physical drive.
testFilePath(
p.join(tempPathUnc, 'a' * (250 - temp.path.length)),
toUnc(p.join(subdir2Path, 'f')),
);
// Signal test completion to file system event watchers.
File(p.join(subdir1Path, 'TEST_DONE')).writeAsStringSync('TEST_DONE');
File(p.join(subdir2Path, 'TEST_DONE')).writeAsStringSync('TEST_DONE');
// Verify collected events.
verifyEvents(await subdir1Events, p.join(subdir1Path, 'f'));
verifyEvents(await subdir2Events, p.join(subdir2Path, 'f'));
} finally {
temp.deleteSync(recursive: true);
}
}