blob: 6df62f3ecc820e55385bdac86b5b7d5a5297b524 [file]
// Copyright (c) 2025, 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.
@TestOn('vm')
library;
import 'dart:io' as io;
import 'dart:typed_data';
import 'package:io_file/io_file.dart';
import 'package:path/path.dart' as p;
import 'package:test/test.dart';
import 'package:win32/win32.dart' as win32;
import 'errors.dart' as errors;
import 'test_utils.dart';
void main() {
group('writeAsBytes', () {
late String tmp;
late String cwd;
setUp(() {
tmp = createTemp('writeAsBytes');
cwd = fileSystem.currentDirectory;
fileSystem.currentDirectory = tmp;
});
tearDown(() {
fileSystem.currentDirectory = cwd;
deleteTemp(tmp);
});
test('directory', () {
expect(
() => fileSystem.writeAsBytes(
tmp,
Uint8List(5),
WriteMode.truncateExisting,
),
throwsA(
isA<IOFileException>()
.having(
(e) => e.errorCode,
'errorCode',
io.Platform.isWindows
? win32.ERROR_ACCESS_DENIED
: errors.eisdir,
)
.having((e) => e.path1, 'path1', tmp),
),
);
});
test('symlink', () {
final filePath = '$tmp/file1';
final symlinkPath = '$tmp/file2';
final data = randomUint8List(20);
io.File(filePath).writeAsBytesSync(Uint8List(0));
io.Link(symlinkPath).createSync(filePath);
fileSystem.writeAsBytes(symlinkPath, data, WriteMode.truncateExisting);
expect(fileSystem.readAsBytes(symlinkPath), data);
});
group('broken symlink', () {
test('failExisting', () {
final filePath = '$tmp/file1';
final symlinkPath = '$tmp/file2';
final data = randomUint8List(20);
io.File(filePath).writeAsBytesSync(Uint8List(0));
io.Link(symlinkPath).createSync(filePath);
io.File(filePath).deleteSync();
if (io.Platform.isWindows) {
// Windows considers a broken symlink to not be an existing file.
fileSystem.writeAsBytes(
symlinkPath,
Uint8List.fromList(data),
WriteMode.failExisting,
);
// Should write at the symlink target, which should also mean that the
// symlink is no longer broken.
expect(fileSystem.readAsBytes(symlinkPath), data);
expect(fileSystem.readAsBytes(filePath), data);
} else {
expect(
() => fileSystem.writeAsBytes(
symlinkPath,
Uint8List.fromList(data),
WriteMode.failExisting,
),
throwsA(
isA<PathExistsException>()
.having(
(e) => e.errorCode,
'errorCode',
io.Platform.isWindows
? win32.ERROR_FILE_EXISTS
: errors.eexist,
)
.having((e) => e.path1, 'path1', symlinkPath),
),
);
}
});
test('truncateExisting', () {
final filePath = '$tmp/file1';
final symlinkPath = '$tmp/file2';
final data = randomUint8List(20);
io.File(filePath).writeAsBytesSync(Uint8List(0));
io.Link(symlinkPath).createSync(filePath);
io.File(filePath).deleteSync();
fileSystem.writeAsBytes(symlinkPath, data, WriteMode.truncateExisting);
// Should write at the symlink target, which should also mean that the
// symlink is no longer broken.
expect(fileSystem.readAsBytes(symlinkPath), data);
expect(fileSystem.readAsBytes(filePath), data);
});
});
group('new file', () {
test('appendExisting', () {
final data = randomUint8List(20);
final path = '$tmp/file';
fileSystem.writeAsBytes(
path,
Uint8List.fromList(data),
WriteMode.appendExisting,
);
expect(io.File(path).readAsBytesSync(), data);
});
test('failExisting', () {
final data = randomUint8List(20);
final path = '$tmp/file';
fileSystem.writeAsBytes(
path,
Uint8List.fromList(data),
WriteMode.failExisting,
);
expect(io.File(path).readAsBytesSync(), data);
});
test('truncateExisting', () {
final data = randomUint8List(20);
final path = '$tmp/file';
fileSystem.writeAsBytes(
path,
Uint8List.fromList(data),
WriteMode.truncateExisting,
);
expect(io.File(path).readAsBytesSync(), data);
});
});
group('existing file', () {
test('appendExisting', () {
final data = randomUint8List(20);
final path = '$tmp/file';
io.File(path).writeAsBytesSync([1, 2, 3]);
fileSystem.writeAsBytes(
path,
Uint8List.fromList(data),
WriteMode.appendExisting,
);
expect(io.File(path).readAsBytesSync(), [1, 2, 3] + data);
});
test('null file', () {
final data = randomUint8List(20);
fileSystem.writeAsBytes(
io.Platform.isWindows ? r'\\.\NUL' : '/dev/null',
Uint8List.fromList(data),
WriteMode.appendExisting,
);
});
test('failExisting', () {
final data = randomUint8List(20);
final path = '$tmp/file';
io.File(path).writeAsBytesSync([1, 2, 3]);
expect(
() => fileSystem.writeAsBytes(path, data, WriteMode.failExisting),
throwsA(
isA<PathExistsException>()
.having(
(e) => e.errorCode,
'errorCode',
io.Platform.isWindows
? win32.ERROR_FILE_EXISTS
: errors.eexist,
)
.having((e) => e.path1, 'path1', path),
),
);
});
test('truncateExisting', () {
final data = randomUint8List(20);
final path = '$tmp/file';
io.File(path).writeAsBytesSync([1, 2, 3]);
fileSystem.writeAsBytes(path, data, WriteMode.truncateExisting);
expect(io.File(path).readAsBytesSync(), data);
});
});
test('absolute path, long file name', () {
final data = randomUint8List(20);
final path = p.join(tmp, 'f' * 255);
fileSystem.writeAsBytes(path, data);
expect(fileSystem.readAsBytes(path), data);
});
test('relative path, long file name', () {
final data = randomUint8List(20);
final path = 'f' * 255;
fileSystem.writeAsBytes(path, data);
expect(fileSystem.readAsBytes(path), data);
});
group('regular files', () {
for (var i = 0; i <= 1024; ++i) {
test('Write small file: $i bytes', () {
final data = randomUint8List(i);
final path = '$tmp/file';
fileSystem.writeAsBytes(path, data);
expect(fileSystem.readAsBytes(path), data);
});
}
for (var i = 1 << 12; i <= 1 << 30; i <<= 4) {
test('Write large file: $i bytes', () {
final data = randomUint8List(i);
final path = '$tmp/file';
fileSystem.writeAsBytes(path, data);
expect(fileSystem.readAsBytes(path), data);
});
}
test('Write very large file', () {
// FreeBSD/Windows cannot write more than INT_MAX at once.
final data = randomUint8List(1 << 31 + 1);
final path = '$tmp/file';
fileSystem.writeAsBytes(path, data);
expect(fileSystem.readAsBytes(path), data);
}, skip: 'very slow');
});
});
}