blob: a0264326377b2a7b8291d3c590b1bbe7f6b92ffd [file] [log] [blame]
@TestOn('vm')
import 'dart:async';
import 'dart:convert';
import 'dart:typed_data';
import 'package:tar/tar.dart' as tar;
import 'package:test/test.dart';
import 'system_tar.dart';
const oneMbSize = 1024 * 1024;
const tenGbSize = oneMbSize * 1024 * 10;
void main() {
group('writes long file names', () {
for (final style in tar.OutputFormat.values) {
test(style.toString(), () async {
final name = '${'very' * 30} long name.txt';
final withLongName = tar.TarEntry.data(
tar.TarHeader(name: name, mode: 0, size: 0),
Uint8List(0),
);
final proc = await writeToTar(['--list'], Stream.value(withLongName),
format: style);
expect(proc.lines, emits(contains(name)));
});
}
}, testOn: '!windows');
test('writes headers', () async {
final date = DateTime.parse('2020-12-30 12:34');
final entry = tar.TarEntry.data(
tar.TarHeader(
name: 'hello_dart.txt',
mode: int.parse('744', radix: 8),
size: 0,
userId: 3,
groupId: 4,
userName: 'my_user',
groupName: 'long group that exceeds 32 characters',
modified: date,
),
Uint8List(0),
);
final proc = await writeToTar(['--list', '--verbose'], Stream.value(entry));
expect(
proc.lines,
emits(
allOf(
contains('-rwxr--r--'),
contains('my_user'),
contains('long group that exceeds 32 characters'),
// The date format is different across GNU and BSD tar
anyOf(contains('12:34'), contains('Dec 30')),
),
),
);
}, testOn: '!windows');
test('writes entries synchronously', () async {
final date = DateTime.parse('2020-12-30 12:34');
final builder = BytesBuilder(copy: false);
final sink = tar.tarConverter
.startChunkedConversion(ByteConversionSink.withCallback(builder.add));
sink.add(tar.TarEntry.data(
tar.TarHeader(
name: 'first.txt',
mode: int.parse('644', radix: 8),
size: 0,
userId: 3,
groupId: 4,
userName: 'my_user',
modified: date,
),
Uint8List(10),
));
sink.add(tar.TarEntry.data(
tar.TarHeader(
name: 'second.txt',
mode: int.parse('644', radix: 8),
size: 0,
userId: 3,
groupId: 4,
userName: 'my_user',
modified: date,
),
Uint8List(512),
));
sink.close();
final process = await startTar(['--list', '--verbose']);
process.stdin.add(builder.takeBytes());
expect(
process.lines,
emitsInOrder(
<Matcher>[
allOf(
contains('-rw-r--r--'),
contains('my_user'),
contains('10'),
// The date format is different across GNU and BSD tar
anyOf(contains('12:34'), contains('Dec 30')),
contains('first.txt'),
),
allOf(
contains('-rw-r--r--'),
contains('my_user'),
contains('512'),
// The date format is different across GNU and BSD tar
anyOf(contains('12:34'), contains('Dec 30')),
contains('second.txt'),
),
],
),
);
await process.stdin.close();
}, testOn: '!windows');
test('writes huge files', () async {
final oneMb = Uint8List(oneMbSize);
const count = tenGbSize ~/ oneMbSize;
final entry = tar.TarEntry(
tar.TarHeader(
name: 'file.blob',
mode: 0,
size: tenGbSize,
),
Stream<List<int>>.fromIterable(Iterable.generate(count, (i) => oneMb)),
);
final proc = await writeToTar(['--list', '--verbose'], Stream.value(entry));
expect(proc.lines, emits(contains(tenGbSize.toString())));
}, testOn: '!windows && !node');
group('refuses to write files with OutputFormat.gnu', () {
void shouldThrow(tar.TarEntry entry) {
final output = tar.tarWritingSink(_NullStreamSink(),
format: tar.OutputFormat.gnuLongName);
expect(Stream.value(entry).pipe(output), throwsA(isUnsupportedError));
}
test('when they are too large', () {
final oneMb = Uint8List(oneMbSize);
const count = tenGbSize ~/ oneMbSize;
final entry = tar.TarEntry(
tar.TarHeader(
name: 'file.blob',
mode: 0,
size: tenGbSize,
),
Stream<List<int>>.fromIterable(Iterable.generate(count, (i) => oneMb)),
);
shouldThrow(entry);
});
test('when they use long user names', () {
shouldThrow(
tar.TarEntry.data(
tar.TarHeader(
name: 'file.txt',
userName: 'this name is longer than 32 chars, which is not allowed',
),
[],
),
);
});
});
}
class _NullStreamSink<T> extends StreamSink<T> {
@override
void add(T event) {}
@override
void addError(Object error, [StackTrace? stackTrace]) {
// ignore: only_throw_errors
throw error;
}
@override
Future<void> addStream(Stream<T> stream) {
return stream.forEach(add);
}
@override
Future<void> close() async {}
@override
Future<void> get done => close();
}