blob: 1bdb58ee34388d1f29df4ef0a54f1830226a62f2 [file] [log] [blame]
// Copyright 2018 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:async';
import 'dart:convert';
import 'dart:typed_data';
import 'package:fidl/fidl.dart';
import 'package:fidl_fuchsia_io/fidl_async.dart';
import 'package:fuchsia_vfs/vfs.dart';
import 'package:test/test.dart';
import 'package:zircon/zircon.dart';
void main() {
group('pseudo dir: ', () {
test('inode number', () {
Vnode dir = PseudoDir();
expect(dir.inodeNumber(), inoUnknown);
});
test('type', () {
Vnode dir = PseudoDir();
expect(dir.type(), direntTypeDirectory);
});
test('basic', () {
PseudoDir dir = PseudoDir();
var key1 = 'key1';
var key2 = 'key2';
var node1 = _TestVnode();
expect(dir.addNode(key1, node1), ZX.OK);
expect(dir.lookup(key1), node1);
var node2 = _TestVnode();
expect(dir.addNode(key2, node2), ZX.OK);
expect(dir.lookup(key2), node2);
// make sure key1 is still there
expect(dir.lookup(key1), node1);
});
test('duplicate key', () {
PseudoDir dir = PseudoDir();
var key = 'key';
var node = _TestVnode();
var dupNode = _TestVnode();
expect(dir.addNode(key, node), ZX.OK);
expect(dir.addNode(key, dupNode), ZX.ERR_ALREADY_EXISTS);
// check that key was not replaced
expect(dir.lookup(key), node);
});
test('remove node', () {
PseudoDir dir = PseudoDir();
var key = 'key';
var node = _TestVnode();
expect(dir.addNode(key, node), ZX.OK);
expect(dir.lookup(key), node);
expect(dir.removeNode(key), ZX.OK);
expect(dir.lookup(key), null);
// add again and check
expect(dir.addNode(key, node), ZX.OK);
expect(dir.lookup(key), node);
});
test('remove when multiple keys', () {
PseudoDir dir = PseudoDir();
var key1 = 'key1';
var key2 = 'key2';
var node1 = _TestVnode();
var node2 = _TestVnode();
expect(dir.addNode(key1, node1), ZX.OK);
expect(dir.addNode(key2, node2), ZX.OK);
expect(dir.lookup(key1), node1);
expect(dir.lookup(key2), node2);
expect(dir.removeNode(key1), ZX.OK);
expect(dir.lookup(key1), null);
// check that key2 is still there
expect(dir.lookup(key2), node2);
// add again and check
expect(dir.addNode(key1, node1), ZX.OK);
expect(dir.lookup(key1), node1);
expect(dir.lookup(key2), node2);
});
test('key order is maintained', () {
PseudoDir dir = PseudoDir();
var key1 = 'key1';
var key2 = 'key2';
var key3 = 'key3';
var node1 = _TestVnode();
var node2 = _TestVnode();
var node3 = _TestVnode();
expect(dir.addNode(key1, node1), ZX.OK);
expect(dir.addNode(key2, node2), ZX.OK);
expect(dir.addNode(key3, node3), ZX.OK);
expect(dir.listNodeNames(), [key1, key2, key3]);
// order maintained after removing node
expect(dir.removeNode(key1), ZX.OK);
expect(dir.listNodeNames(), [key2, key3]);
// add again and check
expect(dir.addNode(key1, node1), ZX.OK);
expect(dir.listNodeNames(), [key2, key3, key1]);
});
test('remove and isEmpty', () {
PseudoDir dir = PseudoDir();
var key1 = 'key1';
var key2 = 'key2';
var key3 = 'key3';
var node1 = _TestVnode();
var node2 = _TestVnode();
var node3 = _TestVnode();
expect(dir.isEmpty(), true);
expect(dir.addNode(key1, node1), ZX.OK);
expect(dir.addNode(key2, node2), ZX.OK);
expect(dir.addNode(key3, node3), ZX.OK);
expect(dir.isEmpty(), false);
expect(dir.removeNode(key1), ZX.OK);
expect(dir.isEmpty(), false);
dir.removeAllNodes();
expect(dir.isEmpty(), true);
expect(dir.listNodeNames(), []);
// make sure that keys are really gone
expect(dir.lookup(key2), null);
expect(dir.lookup(key3), null);
// add again and check
expect(dir.addNode(key1, node1), ZX.OK);
expect(dir.isEmpty(), false);
expect(dir.lookup(key1), node1);
expect(dir.listNodeNames(), [key1]);
});
});
group('pseudo dir server: ', () {
group('open fails: ', () {
test('invalid flags', () async {
PseudoDir dir = PseudoDir();
var invalidFlags = [
openFlagAppend,
openFlagCreate,
openFlagCreateIfAbsent,
openFlagNoRemote,
openFlagTruncate,
openRightAdmin,
];
var i = 0;
for (var flag in invalidFlags) {
DirectoryProxy proxy = DirectoryProxy();
var status = dir.connect(flag | openFlagDescribe, 0,
InterfaceRequest(proxy.ctrl.request().passChannel()));
expect(status, isNot(ZX.OK), reason: 'flagIndex: $i');
i++;
await proxy.onOpen.first.then((response) {
expect(response.s, status);
expect(response.info, isNull);
}).catchError((err) async {
fail(err.toString());
});
}
});
test('invalid mode', () async {
PseudoDir dir = PseudoDir();
var invalidModes = [
modeTypeBlockDevice,
modeTypeFile,
modeTypeService,
modeTypeService,
modeTypeSocket
];
var i = 0;
for (var mode in invalidModes) {
DirectoryProxy proxy = DirectoryProxy();
var status = dir.connect(openFlagDescribe, mode,
InterfaceRequest(proxy.ctrl.request().passChannel()));
expect(status, ZX.ERR_INVALID_ARGS, reason: 'modeIndex: $i');
i++;
await proxy.onOpen.first.then((response) {
expect(response.s, status);
expect(response.info, isNull);
}).catchError((err) async {
fail(err.toString());
});
}
});
});
DirectoryProxy _getProxyForDir(PseudoDir dir, [int flags = 0]) {
DirectoryProxy proxy = DirectoryProxy();
var status = dir.connect(
flags, 0, InterfaceRequest(proxy.ctrl.request().passChannel()));
expect(status, ZX.OK);
return proxy;
}
test('open passes', () async {
PseudoDir dir = PseudoDir();
DirectoryProxy proxy = _getProxyForDir(dir, openFlagDescribe);
await proxy.onOpen.first.then((response) {
expect(response.s, ZX.OK);
expect(response.info, isNotNull);
}).catchError((err) async {
fail(err.toString());
});
});
test('open passes with valid mode', () async {
PseudoDir dir = PseudoDir();
var validModes = [
modeProtectionMask,
modeTypeDirectory,
];
var i = 0;
for (var mode in validModes) {
DirectoryProxy proxy = DirectoryProxy();
var status = dir.connect(openFlagDescribe, mode,
InterfaceRequest(proxy.ctrl.request().passChannel()));
expect(status, ZX.OK, reason: 'modeIndex: $i');
i++;
await proxy.onOpen.first.then((response) {
expect(response.s, ZX.OK);
expect(response.info, isNotNull);
}).catchError((err) async {
fail(err.toString());
});
}
});
test('open passes with valid flags', () async {
PseudoDir dir = PseudoDir();
var validFlags = [
openRightReadable,
openRightWritable,
openFlagDirectory,
openFlagNodeReference
];
for (var flag in validFlags) {
DirectoryProxy proxy = _getProxyForDir(dir, flag | openFlagDescribe);
await proxy.onOpen.first.then((response) {
expect(response.s, ZX.OK);
expect(response.info, isNotNull);
}).catchError((err) async {
fail(err.toString());
});
}
});
test('getattr', () async {
PseudoDir dir = PseudoDir();
DirectoryProxy proxy = _getProxyForDir(dir);
var attr = await proxy.getAttr();
expect(attr.attributes.linkCount, 1);
expect(attr.attributes.mode, modeProtectionMask | modeTypeDirectory);
});
_Dirent _createDirentForDot() {
return _Dirent(inoUnknown, 1, direntTypeDirectory, '.');
}
_Dirent _createDirent(Vnode vnode, String name) {
return _Dirent(vnode.inodeNumber(), name.length, vnode.type(), name);
}
int _expectedDirentSize(List<_Dirent> dirents) {
var sum = 0;
for (var d in dirents) {
sum += d.direntSizeInBytes;
}
return sum;
}
void _validateExpectedDirents(
List<_Dirent> dirents, Directory$ReadDirents$Response response) {
expect(response.s, ZX.OK);
expect(response.dirents.length, _expectedDirentSize(dirents));
var offset = 0;
for (var dirent in dirents) {
var data = ByteData.view(
response.dirents.buffer, response.dirents.offsetInBytes + offset);
var actualDirent = _Dirent.fromData(data);
expect(actualDirent, dirent);
offset += actualDirent.direntSizeInBytes;
}
}
group('read dir:', () {
test('simple call should work', () async {
PseudoDir dir = PseudoDir();
PseudoDir subDir = PseudoDir();
var file1 = PseudoFile.readOnlyStr(() => 'file1');
var file2 = PseudoFile.readOnlyStr(() => 'file2');
var file3 = PseudoFile.readOnlyStr(() => 'file3');
dir
..addNode('file1', file1)
..addNode('subDir', subDir)
..addNode('file3', file3);
subDir.addNode('file2', file2);
DirectoryProxy proxy = _getProxyForDir(dir);
var expectedDirents = [
_createDirentForDot(),
_createDirent(file1, 'file1'),
_createDirent(subDir, 'subDir'),
_createDirent(file3, 'file3'),
];
var response = await proxy.readDirents(1024);
_validateExpectedDirents(expectedDirents, response);
// test that next read call returns length zero buffer
response = await proxy.readDirents(1024);
expect(response.s, ZX.OK);
expect(response.dirents.length, 0);
// also test sub folder and make sure it was not affected by parent dir.
proxy = _getProxyForDir(subDir);
expectedDirents = [
_createDirentForDot(),
_createDirent(file2, 'file2'),
];
response = await proxy.readDirents(1024);
_validateExpectedDirents(expectedDirents, response);
});
test('serve function works', () async {
PseudoDir dir = PseudoDir();
var file1 = PseudoFile.readOnlyStr(() => 'file1');
dir.addNode('file1', file1);
DirectoryProxy proxy = DirectoryProxy();
var status =
dir.serve(InterfaceRequest(proxy.ctrl.request().passChannel()));
expect(status, ZX.OK);
var expectedDirents = [
_createDirentForDot(),
_createDirent(file1, 'file1'),
];
var response = await proxy.readDirents(1024);
_validateExpectedDirents(expectedDirents, response);
});
test('passed buffer size is exact', () async {
PseudoDir dir = PseudoDir();
PseudoDir subDir = PseudoDir();
var file1 = PseudoFile.readOnlyStr(() => 'file1');
var file3 = PseudoFile.readOnlyStr(() => 'file3');
dir
..addNode('file1', file1)
..addNode('subDir', subDir)
..addNode('file3', file3);
var proxy = _getProxyForDir(dir);
var expectedDirents = [
_createDirentForDot(),
_createDirent(file1, 'file1'),
_createDirent(subDir, 'subDir'),
_createDirent(file3, 'file3'),
];
var response =
await proxy.readDirents(_expectedDirentSize(expectedDirents));
_validateExpectedDirents(expectedDirents, response);
// test that next read call returns length zero buffer
response = await proxy.readDirents(1024);
expect(response.s, ZX.OK);
expect(response.dirents.length, 0);
});
test('passed buffer size is exact - 1', () async {
PseudoDir dir = PseudoDir();
PseudoDir subDir = PseudoDir();
var file1 = PseudoFile.readOnlyStr(() => 'file1');
var file3 = PseudoFile.readOnlyStr(() => 'file3');
dir
..addNode('file1', file1)
..addNode('subDir', subDir)
..addNode('file3', file3);
var proxy = _getProxyForDir(dir);
var expectedDirents = [
_createDirentForDot(),
_createDirent(file1, 'file1'),
_createDirent(subDir, 'subDir'),
_createDirent(file3, 'file3'),
];
var response =
await proxy.readDirents(_expectedDirentSize(expectedDirents) - 1);
var lastDirent = expectedDirents.removeLast();
_validateExpectedDirents(expectedDirents, response);
// test that next read call returns last dirent
response = await proxy.readDirents(1024);
_validateExpectedDirents([lastDirent], response);
// test that next read call returns length zero buffer
response = await proxy.readDirents(1024);
expect(response.s, ZX.OK);
expect(response.dirents.length, 0);
});
test('buffer too small', () async {
PseudoDir dir = PseudoDir();
PseudoDir subDir = PseudoDir();
var file1 = PseudoFile.readOnlyStr(() => 'file1');
dir..addNode('file1', file1)..addNode('subDir', subDir);
var proxy = _getProxyForDir(dir);
var size = _expectedDirentSize([_createDirentForDot()]) - 1;
for (int i = 0; i < size; i++) {
var response = await proxy.readDirents(i);
expect(response.s, ZX.ERR_BUFFER_TOO_SMALL);
expect(response.dirents.length, 0);
}
});
test(
'buffer too small after first dot read and subsequent reads with bigger buffer returns correct dirents',
() async {
PseudoDir dir = PseudoDir();
PseudoDir subDir = PseudoDir();
var file1 = PseudoFile.readOnlyStr(() => 'file1');
dir..addNode('file1', file1)..addNode('subDir', subDir);
var proxy = _getProxyForDir(dir);
var size = _expectedDirentSize([_createDirentForDot()]);
var response = await proxy.readDirents(size);
// make sure that '.' was read
_validateExpectedDirents([_createDirentForDot()], response);
// this should return error
response = await proxy.readDirents(size);
expect(response.s, ZX.ERR_BUFFER_TOO_SMALL);
expect(response.dirents.length, 0);
var expectedDirents = [
_createDirent(file1, 'file1'),
_createDirent(subDir, 'subDir'),
];
response =
await proxy.readDirents(_expectedDirentSize(expectedDirents));
_validateExpectedDirents(expectedDirents, response);
});
test('multiple reads with small buffer', () async {
PseudoDir dir = PseudoDir();
PseudoDir subDir = PseudoDir();
var file1 = PseudoFile.readOnlyStr(() => 'file1');
dir..addNode('file1', file1)..addNode('subDir', subDir);
var proxy = _getProxyForDir(dir);
var expectedDirents = [
_createDirentForDot(),
_createDirent(file1, 'file1'),
_createDirent(subDir, 'subDir'),
];
for (var dirent in expectedDirents) {
var dirents = [dirent];
var response = await proxy.readDirents(_expectedDirentSize(dirents));
_validateExpectedDirents(dirents, response);
}
// test that next read call returns length zero buffer
var response = await proxy.readDirents(1024);
expect(response.s, ZX.OK);
expect(response.dirents.length, 0);
});
test('read two dirents then one', () async {
PseudoDir dir = PseudoDir();
PseudoDir subDir = PseudoDir();
var file1 = PseudoFile.readOnlyStr(() => 'file1');
dir..addNode('file1', file1)..addNode('subDir', subDir);
var proxy = _getProxyForDir(dir);
var expectedDirents = [
_createDirentForDot(),
_createDirent(file1, 'file1'),
];
var response =
await proxy.readDirents(_expectedDirentSize(expectedDirents));
_validateExpectedDirents(expectedDirents, response);
expectedDirents = [
_createDirent(subDir, 'subDir'),
];
response = await proxy.readDirents(1024);
_validateExpectedDirents(expectedDirents, response);
});
test('buffer size more than first less than 2 dirents', () async {
PseudoDir dir = PseudoDir();
PseudoDir subDir = PseudoDir();
var file1 = PseudoFile.readOnlyStr(() => 'file1');
dir..addNode('file1', file1)..addNode('subDir', subDir);
var proxy = _getProxyForDir(dir);
var expectedDirents = [
_createDirentForDot(),
];
var response =
await proxy.readDirents(_expectedDirentSize(expectedDirents) + 10);
_validateExpectedDirents(expectedDirents, response);
// now test that we are able to get rest
expectedDirents = [
_createDirent(file1, 'file1'),
_createDirent(subDir, 'subDir'),
];
response = await proxy.readDirents(1024);
_validateExpectedDirents(expectedDirents, response);
});
test('rewind works', () async {
PseudoDir dir = PseudoDir();
PseudoDir subDir = PseudoDir();
var file1 = PseudoFile.readOnlyStr(() => 'file1');
dir..addNode('file1', file1)..addNode('subDir', subDir);
var proxy = _getProxyForDir(dir);
var expectedDirents = [
_createDirentForDot(),
];
var response =
await proxy.readDirents(_expectedDirentSize(expectedDirents) + 10);
_validateExpectedDirents(expectedDirents, response);
var rewindResponse = await proxy.rewind();
expect(rewindResponse, ZX.OK);
response =
await proxy.readDirents(_expectedDirentSize(expectedDirents) + 10);
_validateExpectedDirents(expectedDirents, response);
});
test('rewind works after we reach directory end', () async {
PseudoDir dir = PseudoDir();
PseudoDir subDir = PseudoDir();
var file1 = PseudoFile.readOnlyStr(() => 'file1');
dir..addNode('file1', file1)..addNode('subDir', subDir);
var proxy = _getProxyForDir(dir);
var expectedDirents = [
_createDirentForDot(),
_createDirent(file1, 'file1'),
_createDirent(subDir, 'subDir'),
];
var response =
await proxy.readDirents(_expectedDirentSize(expectedDirents) + 10);
_validateExpectedDirents(expectedDirents, response);
var rewindResponse = await proxy.rewind();
expect(rewindResponse, ZX.OK);
response =
await proxy.readDirents(_expectedDirentSize(expectedDirents) + 10);
_validateExpectedDirents(expectedDirents, response);
});
test('readdir works when node removed', () async {
PseudoDir dir = PseudoDir();
PseudoDir subDir = PseudoDir();
var file1 = PseudoFile.readOnlyStr(() => 'file1');
dir..addNode('file1', file1)..addNode('subDir', subDir);
var proxy = _getProxyForDir(dir);
var expectedDirents = [
_createDirentForDot(),
];
var response =
await proxy.readDirents(_expectedDirentSize(expectedDirents));
_validateExpectedDirents(expectedDirents, response);
// remove first node
dir.removeNode('file1');
expectedDirents = [_createDirent(subDir, 'subDir')];
response = await proxy.readDirents(1024);
_validateExpectedDirents(expectedDirents, response);
});
test('readdir works when already last node is removed', () async {
PseudoDir dir = PseudoDir();
PseudoDir subDir = PseudoDir();
var file1 = PseudoFile.readOnlyStr(() => 'file1');
dir..addNode('file1', file1)..addNode('subDir', subDir);
var proxy = _getProxyForDir(dir);
var expectedDirents = [
_createDirentForDot(),
_createDirent(file1, 'file1')
];
var response =
await proxy.readDirents(_expectedDirentSize(expectedDirents));
_validateExpectedDirents(expectedDirents, response);
// remove first node
dir.removeNode('file1');
expectedDirents = [_createDirent(subDir, 'subDir')];
response = await proxy.readDirents(1024);
_validateExpectedDirents(expectedDirents, response);
});
test('readdir works when new node is added', () async {
PseudoDir dir = PseudoDir();
PseudoDir subDir = PseudoDir();
var file1 = PseudoFile.readOnlyStr(() => 'file1');
dir..addNode('file1', file1)..addNode('subDir', subDir);
var proxy = _getProxyForDir(dir);
var expectedDirents = [
_createDirentForDot(),
_createDirent(file1, 'file1'),
_createDirent(subDir, 'subDir')
];
var response =
await proxy.readDirents(_expectedDirentSize(expectedDirents));
_validateExpectedDirents(expectedDirents, response);
dir.addNode('file2', file1);
expectedDirents = [_createDirent(file1, 'file2')];
response = await proxy.readDirents(1024);
_validateExpectedDirents(expectedDirents, response);
});
test('readdir works when new node is added and only first node was read',
() async {
PseudoDir dir = PseudoDir();
PseudoDir subDir = PseudoDir();
var file1 = PseudoFile.readOnlyStr(() => 'file1');
dir..addNode('file1', file1)..addNode('subDir', subDir);
var proxy = _getProxyForDir(dir);
var expectedDirents = [
_createDirentForDot(),
];
var response =
await proxy.readDirents(_expectedDirentSize(expectedDirents));
_validateExpectedDirents(expectedDirents, response);
dir.addNode('file2', file1);
expectedDirents = [
_createDirent(file1, 'file1'),
_createDirent(subDir, 'subDir'),
_createDirent(file1, 'file2')
];
response = await proxy.readDirents(1024);
_validateExpectedDirents(expectedDirents, response);
});
});
group('open/close file/dir in dir:', () {
Future<void> _openFileAndAssert(DirectoryProxy proxy, String filePath,
int bufferLen, String expectedContent) async {
FileProxy fileProxy = FileProxy();
await proxy.open(openRightReadable, 0, filePath,
InterfaceRequest(fileProxy.ctrl.request().passChannel()));
var readResonse = await fileProxy.read(bufferLen);
expect(readResonse.s, ZX.OK);
expect(String.fromCharCodes(readResonse.data), expectedContent);
}
PseudoDir _setUpDir() {
PseudoDir dir = PseudoDir();
PseudoDir subDir = PseudoDir();
var file1 = PseudoFile.readOnlyStr(() => 'file1');
var file2 = PseudoFile.readOnlyStr(() => 'file2');
var file3 = PseudoFile.readOnlyStr(() => 'file3');
var file4 = PseudoFile.readOnlyStr(() => 'file4');
dir
..addNode('file1', file1)
..addNode('subDir', subDir)
..addNode('file3', file3);
subDir..addNode('file2', file2)..addNode('file4', file4);
return dir;
}
test('open self', () async {
PseudoDir dir = _setUpDir();
var proxy = _getProxyForDir(dir);
var paths = ['.', '', './', './/', './//'];
for (var path in paths) {
DirectoryProxy newProxy = DirectoryProxy();
await proxy.open(0, 0, path,
InterfaceRequest(newProxy.ctrl.request().passChannel()));
// open file 1 in proxy and check contents to make sure correct dir was opened.
await _openFileAndAssert(newProxy, 'file1', 100, 'file1');
}
});
test('open file', () async {
PseudoDir dir = _setUpDir();
var proxy = _getProxyForDir(dir);
// open file 1 check contents.
var paths = ['file1', './file1', './/file1', './//file1'];
for (var path in paths) {
await _openFileAndAssert(proxy, path, 100, 'file1');
}
});
test('open file fails for path ending with "/"', () async {
PseudoDir dir = _setUpDir();
var proxy = _getProxyForDir(dir);
FileProxy fileProxy = FileProxy();
await proxy.open(openRightReadable | openFlagDescribe, 0, 'file1/',
InterfaceRequest(fileProxy.ctrl.request().passChannel()));
await fileProxy.onOpen.first.then((response) {
expect(response.s, ZX.ERR_NOT_DIR);
expect(response.info, isNull);
}).catchError((err) async {
fail(err.toString());
});
});
test('open file fails for invalid key', () async {
PseudoDir dir = _setUpDir();
var proxy = _getProxyForDir(dir);
FileProxy fileProxy = FileProxy();
await proxy.open(openRightReadable, 0, 'invalid',
InterfaceRequest(fileProxy.ctrl.request().passChannel()));
// channel should be closed
fileProxy.ctrl.whenClosed.asStream().listen(expectAsync1((_) {}));
});
test('open fails for trying to open file within a file', () async {
PseudoDir dir = _setUpDir();
var proxy = _getProxyForDir(dir);
FileProxy fileProxy = FileProxy();
await proxy.open(openRightReadable | openFlagDescribe, 0, 'file1/file2',
InterfaceRequest(fileProxy.ctrl.request().passChannel()));
await fileProxy.onOpen.first.then((response) {
expect(response.s, ZX.ERR_NOT_DIR);
expect(response.info, isNull);
}).catchError((err) async {
fail(err.toString());
});
});
test('close works', () async {
PseudoDir dir = PseudoDir();
PseudoDir subDir = PseudoDir();
var file1 = PseudoFile.readOnlyStr(() => 'file1');
dir..addNode('file1', file1)..addNode('subDir', subDir);
var proxy = _getProxyForDir(dir);
DirectoryProxy subDirProxy = DirectoryProxy();
await proxy.open(0, 0, 'subDir',
InterfaceRequest(subDirProxy.ctrl.request().passChannel()));
FileProxy fileProxy = FileProxy();
await proxy.open(0, 0, 'file1',
InterfaceRequest(fileProxy.ctrl.request().passChannel()));
dir.close();
proxy.ctrl.whenClosed.asStream().listen(expectAsync1((_) {}));
subDirProxy.ctrl.whenClosed.asStream().listen(expectAsync1((_) {}));
fileProxy.ctrl.whenClosed.asStream().listen(expectAsync1((_) {}));
});
test('open sub dir', () async {
PseudoDir dir = _setUpDir();
var proxy = _getProxyForDir(dir);
DirectoryProxy dirProxy = DirectoryProxy();
await proxy.open(0, 0, 'subDir',
InterfaceRequest(dirProxy.ctrl.request().passChannel()));
// open file 2 check contents to make sure correct dir was opened.
await _openFileAndAssert(dirProxy, 'file2', 100, 'file2');
});
test('open sub dir with "/" at end', () async {
PseudoDir dir = _setUpDir();
var proxy = _getProxyForDir(dir);
DirectoryProxy dirProxy = DirectoryProxy();
await proxy.open(0, 0, 'subDir/',
InterfaceRequest(dirProxy.ctrl.request().passChannel()));
// open file 2 check contents to make sure correct dir was opened.
await _openFileAndAssert(dirProxy, 'file2', 100, 'file2');
});
test('directly open file in sub dir', () async {
PseudoDir dir = _setUpDir();
var proxy = _getProxyForDir(dir);
// open file 2 in subDir.
await _openFileAndAssert(proxy, 'subDir/file2', 100, 'file2');
});
test('readdir fails for NodeReference', () async {
PseudoDir dir = _setUpDir();
var proxy = _getProxyForDir(dir, openFlagNodeReference);
var response = await proxy.readDirents(1024);
expect(response.s, ZX.ERR_BAD_HANDLE);
});
test('able to open a file for NodeReference', () async {
PseudoDir dir = _setUpDir();
var proxy = _getProxyForDir(dir, openFlagNodeReference);
// open file 2 in subDir.
await _openFileAndAssert(proxy, 'subDir/file2', 100, 'file2');
});
});
test('test clone', () async {
PseudoDir dir = PseudoDir();
var proxy = _getProxyForDir(dir, 0);
DirectoryProxy newProxy = DirectoryProxy();
await proxy.clone(openFlagDescribe,
InterfaceRequest(newProxy.ctrl.request().passChannel()));
await newProxy.onOpen.first.then((response) {
expect(response.s, ZX.OK);
expect(response.info, isNotNull);
}).catchError((err) async {
fail(err.toString());
});
});
test('test clone fails for invalid flags', () async {
PseudoDir dir = PseudoDir();
var proxy = _getProxyForDir(dir, 0);
DirectoryProxy newProxy = DirectoryProxy();
await proxy.clone(openFlagTruncate | openFlagDescribe,
InterfaceRequest(newProxy.ctrl.request().passChannel()));
await newProxy.onOpen.first.then((response) {
expect(response.s, isNot(ZX.OK));
expect(response.info, isNull);
}).catchError((err) async {
fail(err.toString());
});
});
});
}
class _Dirent {
static const int _fixedSize = 10;
int ino;
int size;
int type;
String str;
int direntSizeInBytes;
_Dirent(this.ino, this.size, this.type, this.str) {
direntSizeInBytes = _fixedSize + size;
}
_Dirent.fromData(ByteData data) {
ino = data.getUint64(0, Endian.little);
size = data.getUint8(8);
type = data.getUint8(9);
var offset = _fixedSize;
List<int> charBytes = [];
direntSizeInBytes = offset + size;
expect(data.lengthInBytes, greaterThanOrEqualTo(direntSizeInBytes));
for (int i = 0; i < size; i++) {
charBytes.add(data.getUint8(offset++));
}
str = utf8.decode(charBytes);
}
@override
int get hashCode =>
ino.hashCode + size.hashCode + type.hashCode + str.hashCode;
@override
bool operator ==(Object o) {
return o is _Dirent &&
o.ino == ino &&
o.size == size &&
o.type == type &&
o.str == str;
}
@override
String toString() {
return '[ino: $ino, size: $size, type: $type, str: $str]';
}
}
class _TestVnode extends Vnode {
String _val;
_TestVnode([this._val = '']);
@override
int connect(int flags, int mode, InterfaceRequest<Node> request,
[int parentFlags = -1]) {
throw UnimplementedError();
}
@override
int inodeNumber() {
return inoUnknown;
}
@override
int type() {
return direntTypeUnknown;
}
String value() => _val;
@override
void close() {}
}