kernel_object_test.cc revision 1320f92c476a1ad9d19dba2a48c72b75566198e9
1// Copyright (c) 2012 The Chromium 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 <errno.h>
6#include <fcntl.h>
7#include <pthread.h>
8#include <sys/stat.h>
9
10#include <map>
11#include <string>
12
13#include "gtest/gtest.h"
14
15#include "nacl_io/filesystem.h"
16#include "nacl_io/kernel_handle.h"
17#include "nacl_io/kernel_object.h"
18#include "nacl_io/path.h"
19
20using namespace nacl_io;
21
22namespace {
23
24class NodeForTesting : public Node {
25 public:
26  explicit NodeForTesting(Filesystem* fs) : Node(fs) {}
27};
28
29class FilesystemForTesting : public Filesystem {
30 public:
31  FilesystemForTesting() {}
32
33 public:
34  Error Access(const Path& path, int a_mode) { return ENOSYS; }
35  Error OpenWithMode(const Path& path, int open_flags,
36                     mode_t mode, ScopedNode* out_node) {
37    out_node->reset(NULL);
38    return ENOSYS;
39  }
40  Error Unlink(const Path& path) { return 0; }
41  Error Mkdir(const Path& path, int permissions) { return 0; }
42  Error Rmdir(const Path& path) { return 0; }
43  Error Remove(const Path& path) { return 0; }
44  Error Rename(const Path& path, const Path& newpath) { return 0; }
45};
46
47class KernelHandleForTesting : public KernelHandle {
48 public:
49  KernelHandleForTesting(const ScopedFilesystem& fs, const ScopedNode& node)
50      : KernelHandle(fs, node) {}
51};
52
53class KernelObjectTest : public ::testing::Test {
54 public:
55  void SetUp() {
56    fs.reset(new FilesystemForTesting());
57    node.reset(new NodeForTesting(fs.get()));
58  }
59
60  void TearDown() {
61    // fs is ref-counted, it doesn't need to be explicitly deleted.
62    node.reset(NULL);
63    fs.reset(NULL);
64  }
65
66  KernelObject proxy;
67  ScopedFilesystem fs;
68  ScopedNode node;
69};
70
71}  // namespace
72
73TEST_F(KernelObjectTest, Referencing) {
74  // The filesystem and node should have 1 ref count at this point
75  EXPECT_EQ(1, fs->RefCount());
76  EXPECT_EQ(1, node->RefCount());
77
78  // Pass the filesystem and node into a KernelHandle
79  KernelHandle* raw_handle = new KernelHandleForTesting(fs, node);
80  ScopedKernelHandle handle_a(raw_handle);
81
82  // The filesystem and node should have 1 ref count at this point
83  EXPECT_EQ(1, handle_a->RefCount());
84  EXPECT_EQ(2, fs->RefCount());
85  EXPECT_EQ(2, node->RefCount());
86
87  ScopedKernelHandle handle_b = handle_a;
88
89  // There should be two references to the KernelHandle, the filesystem and node
90  // should be unchanged.
91  EXPECT_EQ(2, handle_a->RefCount());
92  EXPECT_EQ(2, handle_b->RefCount());
93  EXPECT_EQ(handle_a.get(), handle_b.get());
94  EXPECT_EQ(2, fs->RefCount());
95  EXPECT_EQ(2, node->RefCount());
96
97  // Allocating an FD should cause the KernelProxy to ref the handle and
98  // the node and filesystem should be unchanged.
99  int fd1 = proxy.AllocateFD(handle_a, "/example");
100  EXPECT_EQ(3, handle_a->RefCount());
101  EXPECT_EQ(2, fs->RefCount());
102  EXPECT_EQ(2, node->RefCount());
103
104  // If we "dup" the handle, we should bump the ref count on the handle
105  int fd2 = proxy.AllocateFD(handle_b, "");
106  EXPECT_EQ(4, handle_a->RefCount());
107  EXPECT_EQ(2, fs->RefCount());
108  EXPECT_EQ(2, node->RefCount());
109
110  // Handles are expected to come out in order
111  EXPECT_EQ(0, fd1);
112  EXPECT_EQ(1, fd2);
113
114  // Now we "free" the handles, since the proxy should hold them.
115  handle_a.reset(NULL);
116  handle_b.reset(NULL);
117  EXPECT_EQ(2, fs->RefCount());
118  EXPECT_EQ(2, node->RefCount());
119
120  // We should find the handle by either fd
121  EXPECT_EQ(0, proxy.AcquireHandle(fd1, &handle_a));
122  EXPECT_EQ(0, proxy.AcquireHandle(fd2, &handle_b));
123  EXPECT_EQ(raw_handle, handle_a.get());
124  EXPECT_EQ(raw_handle, handle_b.get());
125
126  EXPECT_EQ(4, handle_a->RefCount());
127  EXPECT_EQ(2, fs->RefCount());
128  EXPECT_EQ(2, node->RefCount());
129
130  // A non existent fd should fail, and handleA should decrement as handleB
131  // is released by the call.
132  EXPECT_EQ(EBADF, proxy.AcquireHandle(-1, &handle_b));
133  EXPECT_EQ(NULL, handle_b.get());
134  EXPECT_EQ(3, handle_a->RefCount());
135
136  EXPECT_EQ(EBADF, proxy.AcquireHandle(100, &handle_b));
137  EXPECT_EQ(NULL, handle_b.get());
138
139  // Now only the KernelProxy should reference the KernelHandle in the
140  // FD to KernelHandle Map.
141  handle_a.reset();
142  handle_b.reset();
143
144  EXPECT_EQ(2, raw_handle->RefCount());
145  EXPECT_EQ(2, fs->RefCount());
146  EXPECT_EQ(2, node->RefCount());
147  proxy.FreeFD(fd2);
148  EXPECT_EQ(1, raw_handle->RefCount());
149  EXPECT_EQ(2, fs->RefCount());
150  EXPECT_EQ(2, node->RefCount());
151
152  proxy.FreeFD(fd1);
153  EXPECT_EQ(1, fs->RefCount());
154  EXPECT_EQ(1, node->RefCount());
155}
156
157TEST_F(KernelObjectTest, FreeAndReassignFD) {
158  // The filesystem and node should have 1 ref count at this point
159  EXPECT_EQ(1, fs->RefCount());
160  EXPECT_EQ(1, node->RefCount());
161
162  KernelHandle* raw_handle = new KernelHandleForTesting(fs, node);
163  ScopedKernelHandle handle(raw_handle);
164
165  EXPECT_EQ(2, fs->RefCount());
166  EXPECT_EQ(2, node->RefCount());
167  EXPECT_EQ(1, raw_handle->RefCount());
168
169  proxy.AllocateFD(handle, "/example");
170  EXPECT_EQ(2, fs->RefCount());
171  EXPECT_EQ(2, node->RefCount());
172  EXPECT_EQ(2, raw_handle->RefCount());
173
174  proxy.FreeAndReassignFD(5, handle, "/example");
175  EXPECT_EQ(2, fs->RefCount());
176  EXPECT_EQ(2, node->RefCount());
177  EXPECT_EQ(3, raw_handle->RefCount());
178
179
180  handle.reset();
181  EXPECT_EQ(2, raw_handle->RefCount());
182
183  proxy.AcquireHandle(5, &handle);
184  EXPECT_EQ(3, raw_handle->RefCount());
185  EXPECT_EQ(raw_handle, handle.get());
186}
187