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