1// Copyright 2017 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include <lib/async-loop/cpp/loop.h>
6#include <lib/fdio/util.h>
7#include <fs/synchronous-vfs.h>
8#include <fs/pseudo-dir.h>
9#include <fs/service.h>
10
11#include <unittest/unittest.h>
12
13namespace {
14
15bool test_service() {
16    BEGIN_TEST;
17
18    // set up a service which can only be bound once (to make it easy to
19    // simulate an error to test error reporting behavior from the connector)
20    zx::channel bound_channel;
21    auto svc = fbl::AdoptRef<fs::Service>(new fs::Service(
22        [&bound_channel](zx::channel channel) {
23            if (bound_channel)
24                return ZX_ERR_IO;
25            bound_channel = fbl::move(channel);
26            return ZX_OK;
27        }));
28
29    // open
30    fbl::RefPtr<fs::Vnode> redirect;
31    EXPECT_EQ(ZX_OK, svc->ValidateFlags(ZX_FS_RIGHT_READABLE));
32    EXPECT_EQ(ZX_OK, svc->Open(ZX_FS_RIGHT_READABLE, &redirect));
33    EXPECT_NULL(redirect);
34    EXPECT_EQ(ZX_ERR_NOT_DIR, svc->ValidateFlags(ZX_FS_FLAG_DIRECTORY));
35
36    // get attr
37    vnattr_t attr;
38    EXPECT_EQ(ZX_OK, svc->Getattr(&attr));
39    EXPECT_EQ(V_TYPE_FILE, attr.mode);
40    EXPECT_EQ(1, attr.nlink);
41
42    // make some channels we can use for testing
43    zx::channel c1, c2;
44    EXPECT_EQ(ZX_OK, zx::channel::create(0u, &c1, &c2));
45    zx_handle_t hc1 = c1.get();
46
47    // serve, the connector will return success the first time
48    fs::SynchronousVfs vfs;
49    EXPECT_EQ(ZX_OK, svc->Serve(&vfs, fbl::move(c1), ZX_FS_RIGHT_READABLE));
50    EXPECT_EQ(hc1, bound_channel.get());
51
52    // the connector will return failure because bound_channel is still valid
53    // we test that the error is propagated back up through Serve
54    EXPECT_EQ(ZX_ERR_IO, svc->Serve(&vfs, fbl::move(c2), ZX_FS_RIGHT_READABLE));
55    EXPECT_EQ(hc1, bound_channel.get());
56
57    END_TEST;
58}
59
60bool test_serve_directory() {
61    BEGIN_TEST;
62
63    zx::channel client, server;
64    EXPECT_EQ(ZX_OK, zx::channel::create(0u, &client, &server));
65
66    // open client
67    zx::channel c1, c2;
68    EXPECT_EQ(ZX_OK, zx::channel::create(0u, &c1, &c2));
69    EXPECT_EQ(ZX_OK,
70              fdio_service_connect_at(client.get(), "abc", c2.release()));
71
72    // close client
73    // We test the semantic that a pending open is processed even if the client
74    // has been closed.
75    client.reset();
76
77    // serve
78     async::Loop loop(&kAsyncLoopConfigNoAttachToThread);
79    fs::SynchronousVfs vfs(loop.dispatcher());
80
81    auto directory = fbl::AdoptRef<fs::PseudoDir>(new fs::PseudoDir());
82    auto vnode = fbl::AdoptRef<fs::Service>(new fs::Service(
83        [&loop](zx::channel channel) {
84            loop.Shutdown();
85            return ZX_OK;
86        }));
87    directory->AddEntry("abc", vnode);
88
89    EXPECT_EQ(ZX_OK, vfs.ServeDirectory(directory, fbl::move(server)));
90    EXPECT_EQ(ZX_ERR_BAD_STATE, loop.RunUntilIdle());
91
92    END_TEST;
93}
94
95} // namespace
96
97BEGIN_TEST_CASE(service_tests)
98RUN_TEST(test_service)
99RUN_TEST(test_serve_directory)
100END_TEST_CASE(service_tests)
101