| // 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() {} |
| } |