| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include <fcntl.h> | 5 #include <fcntl.h> |
| 6 #include <gmock/gmock.h> | 6 #include <gmock/gmock.h> |
| 7 #include <ppapi/c/ppb_file_io.h> | 7 #include <ppapi/c/ppb_file_io.h> |
| 8 #include <ppapi/c/pp_errors.h> | 8 #include <ppapi/c/pp_errors.h> |
| 9 #include <ppapi/c/pp_instance.h> | 9 #include <ppapi/c/pp_instance.h> |
| 10 #include <sys/stat.h> | 10 #include <sys/stat.h> |
| 11 #include <sys/types.h> | 11 #include <sys/types.h> |
| 12 | 12 |
| 13 #include "mock_util.h" | 13 #include "fake_pepper_interface_url_loader.h" |
| 14 |
| 14 #include "nacl_io/kernel_handle.h" | 15 #include "nacl_io/kernel_handle.h" |
| 15 #include "nacl_io/kernel_intercept.h" | 16 #include "nacl_io/kernel_intercept.h" |
| 16 #include "nacl_io/mount_http.h" | 17 #include "nacl_io/mount_http.h" |
| 17 #include "nacl_io/mount_node_dir.h" | 18 #include "nacl_io/mount_node_dir.h" |
| 18 #include "nacl_io/osdirent.h" | 19 #include "nacl_io/osdirent.h" |
| 19 #include "nacl_io/osunistd.h" | 20 #include "nacl_io/osunistd.h" |
| 20 #include "pepper_interface_mock.h" | |
| 21 | 21 |
| 22 using namespace nacl_io; | 22 using namespace nacl_io; |
| 23 | 23 |
| 24 using ::testing::_; | 24 namespace { |
| 25 using ::testing::DoAll; | |
| 26 using ::testing::Mock; | |
| 27 using ::testing::Return; | |
| 28 using ::testing::SetArgPointee; | |
| 29 using ::testing::StrEq; | |
| 30 | 25 |
| 31 class MountHttpMock : public MountHttp { | 26 class MountHttpForTesting : public MountHttp { |
| 32 public: | 27 public: |
| 33 MountHttpMock(StringMap_t map, PepperInterfaceMock* ppapi) { | 28 MountHttpForTesting(StringMap_t map, PepperInterface* ppapi) { |
| 34 EXPECT_EQ(0, Init(1, map, ppapi)); | 29 EXPECT_EQ(0, Init(1, map, ppapi)); |
| 35 } | 30 } |
| 36 | 31 |
| 37 ~MountHttpMock() { | 32 using MountHttp::GetNodeCacheForTesting; |
| 38 Destroy(); | |
| 39 } | |
| 40 | |
| 41 NodeMap_t& GetMap() { return node_cache_; } | |
| 42 | |
| 43 using MountHttp::ParseManifest; | 33 using MountHttp::ParseManifest; |
| 44 using MountHttp::FindOrCreateDir; | 34 using MountHttp::FindOrCreateDir; |
| 45 }; | 35 }; |
| 46 | 36 |
| 47 class MountHttpTest : public ::testing::Test { | 37 class MountHttpTest : public ::testing::Test { |
| 48 public: | 38 public: |
| 49 MountHttpTest(); | 39 MountHttpTest(); |
| 50 ~MountHttpTest(); | |
| 51 | 40 |
| 52 protected: | 41 protected: |
| 53 PepperInterfaceMock ppapi_; | 42 FakePepperInterfaceURLLoader ppapi_; |
| 54 MountHttpMock* mnt_; | 43 MountHttpForTesting mnt_; |
| 55 | |
| 56 static const PP_Instance instance_ = 123; | |
| 57 }; | 44 }; |
| 58 | 45 |
| 59 MountHttpTest::MountHttpTest() | 46 MountHttpTest::MountHttpTest() : mnt_(StringMap_t(), &ppapi_) {} |
| 60 : ppapi_(instance_), | 47 |
| 61 mnt_(NULL) { | 48 StringMap_t StringMap_NoCache() { |
| 49 StringMap_t smap; |
| 50 smap["cache_content"] = "false"; |
| 51 return smap; |
| 62 } | 52 } |
| 63 | 53 |
| 64 MountHttpTest::~MountHttpTest() { | 54 class MountHttpNoCacheTest : public ::testing::Test { |
| 65 delete mnt_; | 55 public: |
| 56 MountHttpNoCacheTest(); |
| 57 |
| 58 protected: |
| 59 FakePepperInterfaceURLLoader ppapi_; |
| 60 MountHttpForTesting mnt_; |
| 61 }; |
| 62 |
| 63 MountHttpNoCacheTest::MountHttpNoCacheTest() : |
| 64 mnt_(StringMap_NoCache(), &ppapi_) {} |
| 65 |
| 66 } // namespace |
| 67 |
| 68 |
| 69 TEST_F(MountHttpTest, Access) { |
| 70 ASSERT_TRUE(ppapi_.server_template()->AddEntity("foo", "", NULL)); |
| 71 |
| 72 ASSERT_EQ(0, mnt_.Access(Path("/foo"), R_OK)); |
| 73 ASSERT_EQ(EACCES, mnt_.Access(Path("/foo"), W_OK)); |
| 74 ASSERT_EQ(EACCES, mnt_.Access(Path("/foo"), X_OK)); |
| 75 ASSERT_EQ(ENOENT, mnt_.Access(Path("/bar"), F_OK)); |
| 66 } | 76 } |
| 67 | 77 |
| 78 TEST_F(MountHttpTest, Read) { |
| 79 const char contents[] = "contents"; |
| 80 ASSERT_TRUE(ppapi_.server_template()->AddEntity("file", contents, NULL)); |
| 68 | 81 |
| 69 TEST_F(MountHttpTest, MountEmpty) { | 82 ScopedMountNode node; |
| 70 StringMap_t args; | 83 ASSERT_EQ(0, mnt_.Open(Path("/file"), O_RDONLY, &node)); |
| 71 mnt_ = new MountHttpMock(args, &ppapi_); | 84 |
| 85 char buffer[10] = {0}; |
| 86 int bytes_read = 0; |
| 87 HandleAttr attr; |
| 88 EXPECT_EQ(0, node->Read(attr, &buffer[0], sizeof(buffer), &bytes_read)); |
| 89 EXPECT_EQ(strlen(contents), bytes_read); |
| 90 EXPECT_STREQ(contents, buffer); |
| 91 |
| 92 // Read nothing past the end of the file. |
| 93 attr.offs = 100; |
| 94 EXPECT_EQ(0, node->Read(attr, &buffer[0], sizeof(buffer), &bytes_read)); |
| 95 EXPECT_EQ(0, bytes_read); |
| 96 |
| 97 // Read part of the data. |
| 98 attr.offs = 4; |
| 99 EXPECT_EQ(0, node->Read(attr, &buffer[0], sizeof(buffer), &bytes_read)); |
| 100 ASSERT_EQ(strlen(contents) - 4, bytes_read); |
| 101 buffer[bytes_read] = 0; |
| 102 EXPECT_STREQ("ents", buffer); |
| 72 } | 103 } |
| 73 | 104 |
| 74 TEST_F(MountHttpTest, Mkdir) { | 105 TEST_F(MountHttpTest, Write) { |
| 75 StringMap_t args; | 106 const char contents[] = "contents"; |
| 76 mnt_ = new MountHttpMock(args, &ppapi_); | 107 ASSERT_TRUE(ppapi_.server_template()->AddEntity("file", contents, NULL)); |
| 77 char manifest[] = "-r-- 123 /mydir/foo\n-rw- 234 /thatdir/bar\n"; | 108 |
| 78 EXPECT_EQ(0, mnt_->ParseManifest(manifest)); | 109 ScopedMountNode node; |
| 79 // mkdir of existing directories should give "File exists". | 110 ASSERT_EQ(0, mnt_.Open(Path("/file"), O_WRONLY, &node)); |
| 80 EXPECT_EQ(EEXIST, mnt_->Mkdir(Path("/"), 0)); | 111 |
| 81 EXPECT_EQ(EEXIST, mnt_->Mkdir(Path("/mydir"), 0)); | 112 // Writing always fails. |
| 82 // mkdir of non-existent directories should give "Permission denied". | 113 HandleAttr attr; |
| 83 EXPECT_EQ(EACCES, mnt_->Mkdir(Path("/non_existent"), 0)); | 114 attr.offs = 3; |
| 115 int bytes_written = 1; // Set to a non-zero value. |
| 116 EXPECT_EQ(EACCES, node->Write(attr, "struct", 6, &bytes_written)); |
| 117 EXPECT_EQ(0, bytes_written); |
| 84 } | 118 } |
| 85 | 119 |
| 86 TEST_F(MountHttpTest, Rmdir) { | 120 TEST_F(MountHttpTest, GetStat) { |
| 87 StringMap_t args; | 121 const char contents[] = "contents"; |
| 88 mnt_ = new MountHttpMock(args, &ppapi_); | 122 ASSERT_TRUE(ppapi_.server_template()->AddEntity("file", contents, NULL)); |
| 89 char manifest[] = "-r-- 123 /mydir/foo\n-rw- 234 /thatdir/bar\n"; | 123 |
| 90 EXPECT_EQ(0, mnt_->ParseManifest(manifest)); | 124 ScopedMountNode node; |
| 91 // Rmdir on existing dirs should give "Permission Denied" | 125 ASSERT_EQ(0, mnt_.Open(Path("/file"), O_RDONLY, &node)); |
| 92 EXPECT_EQ(EACCES, mnt_->Rmdir(Path("/"))); | 126 |
| 93 EXPECT_EQ(EACCES, mnt_->Rmdir(Path("/mydir"))); | 127 struct stat statbuf; |
| 94 // Rmdir on existing files should give "Not a direcotory" | 128 EXPECT_EQ(0, node->GetStat(&statbuf)); |
| 95 EXPECT_EQ(ENOTDIR, mnt_->Rmdir(Path("/mydir/foo"))); | 129 EXPECT_EQ(S_IFREG | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH, |
| 96 // Rmdir on non-existent files should give "No such file or directory" | 130 statbuf.st_mode); |
| 97 EXPECT_EQ(ENOENT, mnt_->Rmdir(Path("/non_existent"))); | 131 EXPECT_EQ(strlen(contents), statbuf.st_size); |
| 132 // These are not currently set. |
| 133 EXPECT_EQ(0, statbuf.st_atime); |
| 134 EXPECT_EQ(0, statbuf.st_ctime); |
| 135 EXPECT_EQ(0, statbuf.st_mtime); |
| 98 } | 136 } |
| 99 | 137 |
| 100 TEST_F(MountHttpTest, Unlink) { | 138 TEST_F(MountHttpTest, FTruncate) { |
| 101 StringMap_t args; | 139 const char contents[] = "contents"; |
| 102 mnt_ = new MountHttpMock(args, &ppapi_); | 140 ASSERT_TRUE(ppapi_.server_template()->AddEntity("file", contents, NULL)); |
| 103 char manifest[] = "-r-- 123 /mydir/foo\n-rw- 234 /thatdir/bar\n"; | 141 |
| 104 EXPECT_EQ(0, mnt_->ParseManifest(manifest)); | 142 ScopedMountNode node; |
| 105 // Unlink of existing files should give "Permission Denied" | 143 ASSERT_EQ(0, mnt_.Open(Path("/file"), O_RDWR, &node)); |
| 106 EXPECT_EQ(EACCES, mnt_->Unlink(Path("/mydir/foo"))); | 144 EXPECT_EQ(EACCES, node->FTruncate(4)); |
| 107 // Unlink of existing directory should give "Is a directory" | |
| 108 EXPECT_EQ(EISDIR, mnt_->Unlink(Path("/mydir"))); | |
| 109 // Unlink of non-existent files should give "No such file or directory" | |
| 110 EXPECT_EQ(ENOENT, mnt_->Unlink(Path("/non_existent"))); | |
| 111 } | 145 } |
| 112 | 146 |
| 113 TEST_F(MountHttpTest, Remove) { | 147 TEST(MountHttpDirTest, Mkdir) { |
| 114 StringMap_t args; | 148 StringMap_t args; |
| 115 mnt_ = new MountHttpMock(args, &ppapi_); | 149 MountHttpForTesting mnt(args, NULL); |
| 116 char manifest[] = "-r-- 123 /mydir/foo\n-rw- 234 /thatdir/bar\n"; | 150 char manifest[] = "-r-- 123 /mydir/foo\n-rw- 234 /thatdir/bar\n"; |
| 117 EXPECT_EQ(0, mnt_->ParseManifest(manifest)); | 151 ASSERT_EQ(0, mnt.ParseManifest(manifest)); |
| 118 // Remove of existing files should give "Permission Denied" | 152 // mkdir of existing directories should give "File exists". |
| 119 EXPECT_EQ(EACCES, mnt_->Remove(Path("/mydir/foo"))); | 153 EXPECT_EQ(EEXIST, mnt.Mkdir(Path("/"), 0)); |
| 120 // Remove of existing directory should give "Permission Denied" | 154 EXPECT_EQ(EEXIST, mnt.Mkdir(Path("/mydir"), 0)); |
| 121 EXPECT_EQ(EACCES, mnt_->Remove(Path("/mydir"))); | 155 // mkdir of non-existent directories should give "Permission denied". |
| 122 // Unlink of non-existent files should give "No such file or directory" | 156 EXPECT_EQ(EACCES, mnt.Mkdir(Path("/non_existent"), 0)); |
| 123 EXPECT_EQ(ENOENT, mnt_->Remove(Path("/non_existent"))); | |
| 124 } | 157 } |
| 125 | 158 |
| 126 TEST_F(MountHttpTest, ParseManifest) { | 159 TEST(MountHttpDirTest, Rmdir) { |
| 160 StringMap_t args; |
| 161 MountHttpForTesting mnt(args, NULL); |
| 162 char manifest[] = "-r-- 123 /mydir/foo\n-rw- 234 /thatdir/bar\n"; |
| 163 ASSERT_EQ(0, mnt.ParseManifest(manifest)); |
| 164 // Rmdir on existing dirs should give "Permission Denied" |
| 165 EXPECT_EQ(EACCES, mnt.Rmdir(Path("/"))); |
| 166 EXPECT_EQ(EACCES, mnt.Rmdir(Path("/mydir"))); |
| 167 // Rmdir on existing files should give "Not a direcotory" |
| 168 EXPECT_EQ(ENOTDIR, mnt.Rmdir(Path("/mydir/foo"))); |
| 169 // Rmdir on non-existent files should give "No such file or directory" |
| 170 EXPECT_EQ(ENOENT, mnt.Rmdir(Path("/non_existent"))); |
| 171 } |
| 172 |
| 173 TEST(MountHttpDirTest, Unlink) { |
| 174 StringMap_t args; |
| 175 MountHttpForTesting mnt(args, NULL); |
| 176 char manifest[] = "-r-- 123 /mydir/foo\n-rw- 234 /thatdir/bar\n"; |
| 177 ASSERT_EQ(0, mnt.ParseManifest(manifest)); |
| 178 // Unlink of existing files should give "Permission Denied" |
| 179 EXPECT_EQ(EACCES, mnt.Unlink(Path("/mydir/foo"))); |
| 180 // Unlink of existing directory should give "Is a directory" |
| 181 EXPECT_EQ(EISDIR, mnt.Unlink(Path("/mydir"))); |
| 182 // Unlink of non-existent files should give "No such file or directory" |
| 183 EXPECT_EQ(ENOENT, mnt.Unlink(Path("/non_existent"))); |
| 184 } |
| 185 |
| 186 TEST(MountHttpDirTest, Remove) { |
| 187 StringMap_t args; |
| 188 MountHttpForTesting mnt(args, NULL); |
| 189 char manifest[] = "-r-- 123 /mydir/foo\n-rw- 234 /thatdir/bar\n"; |
| 190 ASSERT_EQ(0, mnt.ParseManifest(manifest)); |
| 191 // Remove of existing files should give "Permission Denied" |
| 192 EXPECT_EQ(EACCES, mnt.Remove(Path("/mydir/foo"))); |
| 193 // Remove of existing directory should give "Permission Denied" |
| 194 EXPECT_EQ(EACCES, mnt.Remove(Path("/mydir"))); |
| 195 // Unlink of non-existent files should give "No such file or directory" |
| 196 EXPECT_EQ(ENOENT, mnt.Remove(Path("/non_existent"))); |
| 197 } |
| 198 |
| 199 TEST(MountHttpDirTest, ParseManifest) { |
| 127 StringMap_t args; | 200 StringMap_t args; |
| 128 size_t result_size = 0; | 201 size_t result_size = 0; |
| 129 | 202 |
| 130 mnt_ = new MountHttpMock(args, &ppapi_); | 203 MountHttpForTesting mnt(args, NULL); |
| 131 | 204 |
| 132 // Multiple consecutive newlines or spaces should be ignored. | 205 // Multiple consecutive newlines or spaces should be ignored. |
| 133 char manifest[] = "-r-- 123 /mydir/foo\n\n-rw- 234 /thatdir/bar\n"; | 206 char manifest[] = "-r-- 123 /mydir/foo\n\n-rw- 234 /thatdir/bar\n"; |
| 134 EXPECT_EQ(0, mnt_->ParseManifest(manifest)); | 207 ASSERT_EQ(0, mnt.ParseManifest(manifest)); |
| 135 | 208 |
| 136 ScopedMountNode root; | 209 ScopedMountNode root; |
| 137 EXPECT_EQ(0, mnt_->FindOrCreateDir(Path("/"), &root)); | 210 EXPECT_EQ(0, mnt.FindOrCreateDir(Path("/"), &root)); |
| 138 ASSERT_NE((MountNode*)NULL, root.get()); | 211 ASSERT_NE((MountNode*)NULL, root.get()); |
| 139 EXPECT_EQ(2, root->ChildCount()); | 212 EXPECT_EQ(2, root->ChildCount()); |
| 140 | 213 |
| 141 ScopedMountNode dir; | 214 ScopedMountNode dir; |
| 142 EXPECT_EQ(0, mnt_->FindOrCreateDir(Path("/mydir"), &dir)); | 215 EXPECT_EQ(0, mnt.FindOrCreateDir(Path("/mydir"), &dir)); |
| 143 ASSERT_NE((MountNode*)NULL, dir.get()); | 216 ASSERT_NE((MountNode*)NULL, dir.get()); |
| 144 EXPECT_EQ(1, dir->ChildCount()); | 217 EXPECT_EQ(1, dir->ChildCount()); |
| 145 | 218 |
| 146 MountNode* node = mnt_->GetMap()["/mydir/foo"].get(); | 219 MountNode* node = (*mnt.GetNodeCacheForTesting())["/mydir/foo"].get(); |
| 147 EXPECT_NE((MountNode*)NULL, node); | 220 EXPECT_NE((MountNode*)NULL, node); |
| 148 EXPECT_EQ(0, node->GetSize(&result_size)); | 221 EXPECT_EQ(0, node->GetSize(&result_size)); |
| 149 EXPECT_EQ(123, result_size); | 222 EXPECT_EQ(123, result_size); |
| 150 | 223 |
| 151 // Since these files are cached thanks to the manifest, we can open them | 224 // Since these files are cached thanks to the manifest, we can open them |
| 152 // without accessing the PPAPI URL API. | 225 // without accessing the PPAPI URL API. |
| 153 ScopedMountNode foo; | 226 ScopedMountNode foo; |
| 154 EXPECT_EQ(0, mnt_->Open(Path("/mydir/foo"), O_RDONLY, &foo)); | 227 ASSERT_EQ(0, mnt.Open(Path("/mydir/foo"), O_RDONLY, &foo)); |
| 155 | 228 |
| 156 ScopedMountNode bar; | 229 ScopedMountNode bar; |
| 157 EXPECT_EQ(0, mnt_->Open(Path("/thatdir/bar"), O_RDWR, &bar)); | 230 ASSERT_EQ(0, mnt.Open(Path("/thatdir/bar"), O_RDWR, &bar)); |
| 158 | 231 |
| 159 struct stat sfoo; | 232 struct stat sfoo; |
| 160 struct stat sbar; | 233 struct stat sbar; |
| 161 | 234 |
| 162 EXPECT_FALSE(foo->GetStat(&sfoo)); | 235 EXPECT_FALSE(foo->GetStat(&sfoo)); |
| 163 EXPECT_FALSE(bar->GetStat(&sbar)); | 236 EXPECT_FALSE(bar->GetStat(&sbar)); |
| 164 | 237 |
| 165 EXPECT_EQ(123, sfoo.st_size); | 238 EXPECT_EQ(123, sfoo.st_size); |
| 166 EXPECT_EQ(S_IFREG | S_IRALL, sfoo.st_mode); | 239 EXPECT_EQ(S_IFREG | S_IRALL, sfoo.st_mode); |
| 167 | 240 |
| 168 EXPECT_EQ(234, sbar.st_size); | 241 EXPECT_EQ(234, sbar.st_size); |
| 169 EXPECT_EQ(S_IFREG | S_IRALL | S_IWALL, sbar.st_mode); | 242 EXPECT_EQ(S_IFREG | S_IRALL | S_IWALL, sbar.st_mode); |
| 170 } | 243 } |
| 171 | 244 |
| 245 TEST_F(MountHttpNoCacheTest, OpenAndClose) { |
| 246 const char contents[] = "contents"; |
| 247 ASSERT_TRUE(ppapi_.server_template()->AddEntity("file", contents, NULL)); |
| 172 | 248 |
| 173 class MountHttpNodeTest : public MountHttpTest { | 249 ScopedMountNode node; |
| 174 public: | 250 ASSERT_EQ(0, mnt_.Open(Path("/file"), O_RDONLY, &node)); |
| 175 MountHttpNodeTest(); | |
| 176 virtual void TearDown(); | |
| 177 | |
| 178 void SetMountArgs(const StringMap_t& args); | |
| 179 void ExpectOpen(const char* method); | |
| 180 void ExpectHeaders(const char* headers); | |
| 181 void OpenNode(); | |
| 182 void SetResponse(int status_code, const char* headers); | |
| 183 // Set a response code, but expect the request to fail. Certain function calls | |
| 184 // expected by SetResponse are not expected here. | |
| 185 void SetResponseExpectFail(int status_code, const char* headers); | |
| 186 void SetResponseBody(const char* body); | |
| 187 void ResetMocks(); | |
| 188 | |
| 189 protected: | |
| 190 MountHttpMock* mnt_; | |
| 191 ScopedMountNode node_; | |
| 192 | |
| 193 CoreInterfaceMock* core_; | |
| 194 VarInterfaceMock* var_; | |
| 195 URLLoaderInterfaceMock* loader_; | |
| 196 URLRequestInfoInterfaceMock* request_; | |
| 197 URLResponseInfoInterfaceMock* response_; | |
| 198 size_t response_body_offset_; | |
| 199 | |
| 200 static const char path_[]; | |
| 201 static const char rel_path_[]; | |
| 202 static const PP_Resource loader_resource_ = 235; | |
| 203 static const PP_Resource request_resource_ = 236; | |
| 204 static const PP_Resource response_resource_ = 237; | |
| 205 }; | |
| 206 | |
| 207 // static | |
| 208 const char MountHttpNodeTest::path_[] = "/foo"; | |
| 209 // static | |
| 210 const char MountHttpNodeTest::rel_path_[] = "foo"; | |
| 211 | |
| 212 MountHttpNodeTest::MountHttpNodeTest() | |
| 213 : mnt_(NULL), | |
| 214 node_(NULL) { | |
| 215 } | 251 } |
| 216 | 252 |
| 217 static PP_Var MakeString(PP_Resource resource) { | 253 TEST_F(MountHttpNoCacheTest, OpenAndCloseNotFound) { |
| 218 PP_Var result = { PP_VARTYPE_STRING, 0, {PP_FALSE} }; | 254 ScopedMountNode node; |
| 219 result.value.as_id = resource; | 255 ASSERT_EQ(ENOENT, mnt_.Open(Path("/file"), O_RDONLY, &node)); |
| 220 return result; | |
| 221 } | 256 } |
| 222 | 257 |
| 223 void MountHttpNodeTest::SetMountArgs(const StringMap_t& args) { | 258 TEST_F(MountHttpNoCacheTest, OpenAndCloseServerError) { |
| 224 mnt_ = new MountHttpMock(args, &ppapi_); | 259 ASSERT_TRUE(ppapi_.server_template()->AddError("file", 500)); |
| 260 |
| 261 ScopedMountNode node; |
| 262 ASSERT_EQ(ENOENT, mnt_.Open(Path("/file"), O_RDONLY, &node)); |
| 225 } | 263 } |
| 226 | 264 |
| 227 void MountHttpNodeTest::ExpectOpen(const char* method) { | 265 TEST_F(MountHttpNoCacheTest, GetSize) { |
| 228 core_ = ppapi_.GetCoreInterface(); | 266 const char contents[] = "contents"; |
| 229 loader_ = ppapi_.GetURLLoaderInterface(); | 267 ASSERT_TRUE(ppapi_.server_template()->AddEntity("file", contents, NULL)); |
| 230 request_ = ppapi_.GetURLRequestInfoInterface(); | 268 ppapi_.server_template()->set_send_content_length(true); |
| 231 response_ = ppapi_.GetURLResponseInfoInterface(); | |
| 232 var_ = ppapi_.GetVarInterface(); | |
| 233 | 269 |
| 234 ON_CALL(*request_, SetProperty(request_resource_, _, _)) | 270 ScopedMountNode node; |
| 235 .WillByDefault(Return(PP_TRUE)); | 271 struct stat statbuf; |
| 236 ON_CALL(*var_, VarFromUtf8(_, _)).WillByDefault(Return(PP_MakeUndefined())); | 272 ASSERT_EQ(0, mnt_.Open(Path("/file"), O_RDONLY, &node)); |
| 237 | 273 EXPECT_EQ(0, node->GetStat(&statbuf)); |
| 238 EXPECT_CALL(*loader_, Create(instance_)).WillOnce(Return(loader_resource_)); | 274 EXPECT_EQ(strlen(contents), statbuf.st_size); |
| 239 EXPECT_CALL(*request_, Create(instance_)).WillOnce(Return(request_resource_)); | |
| 240 | |
| 241 PP_Var var_head = MakeString(345); | |
| 242 PP_Var var_url = MakeString(346); | |
| 243 EXPECT_CALL(*var_, VarFromUtf8(StrEq(method), _)).WillOnce(Return(var_head)); | |
| 244 EXPECT_CALL(*var_, VarFromUtf8(StrEq(rel_path_), _)) | |
| 245 .WillOnce(Return(var_url)); | |
| 246 | |
| 247 #define EXPECT_SET_PROPERTY(NAME, VAR) \ | |
| 248 EXPECT_CALL(*request_, SetProperty(request_resource_, NAME, VAR)) | |
| 249 | |
| 250 EXPECT_SET_PROPERTY(PP_URLREQUESTPROPERTY_URL, IsEqualToVar(var_url)); | |
| 251 EXPECT_SET_PROPERTY(PP_URLREQUESTPROPERTY_METHOD, IsEqualToVar(var_head)); | |
| 252 EXPECT_SET_PROPERTY(PP_URLREQUESTPROPERTY_ALLOWCROSSORIGINREQUESTS, _); | |
| 253 EXPECT_SET_PROPERTY(PP_URLREQUESTPROPERTY_ALLOWCREDENTIALS, _); | |
| 254 | |
| 255 #undef EXPECT_SET_PROPERTY | |
| 256 | |
| 257 EXPECT_CALL(*loader_, Open(loader_resource_, request_resource_, _)) | |
| 258 .WillOnce(DoAll(CallCallback<2>(int32_t(PP_OK)), | |
| 259 Return(int32_t(PP_OK_COMPLETIONPENDING)))); | |
| 260 EXPECT_CALL(*loader_, GetResponseInfo(loader_resource_)) | |
| 261 .WillOnce(Return(response_resource_)); | |
| 262 | |
| 263 EXPECT_CALL(*core_, ReleaseResource(loader_resource_)); | |
| 264 EXPECT_CALL(*core_, ReleaseResource(request_resource_)); | |
| 265 EXPECT_CALL(*core_, ReleaseResource(response_resource_)); | |
| 266 } | 275 } |
| 267 | 276 |
| 268 void MountHttpNodeTest::ExpectHeaders(const char* headers) { | 277 TEST_F(MountHttpNoCacheTest, Access) { |
| 269 PP_Var var_headers = MakeString(347); | 278 ASSERT_TRUE(ppapi_.server_template()->AddEntity("file", "", NULL)); |
| 270 var_ = ppapi_.GetVarInterface(); | |
| 271 EXPECT_CALL(*var_, VarFromUtf8(StrEq(headers), _)) | |
| 272 .WillOnce(Return(var_headers)); | |
| 273 | 279 |
| 274 EXPECT_CALL(*request_, SetProperty(request_resource_, | 280 ASSERT_EQ(0, mnt_.Access(Path("/file"), R_OK)); |
| 275 PP_URLREQUESTPROPERTY_HEADERS, | 281 ASSERT_EQ(EACCES, mnt_.Access(Path("/file"), W_OK)); |
| 276 IsEqualToVar(var_headers))).Times(1); | 282 ASSERT_EQ(ENOENT, mnt_.Access(Path("/bar"), R_OK)); |
| 277 } | 283 } |
| 278 | 284 |
| 279 void MountHttpNodeTest::SetResponse(int status_code, const char* headers) { | 285 TEST_F(MountHttpNoCacheTest, ReadPartial) { |
| 280 ON_CALL(*response_, GetProperty(response_resource_, _)) | 286 const char contents[] = "0123456789abcdefghi"; |
| 281 .WillByDefault(Return(PP_MakeUndefined())); | 287 ASSERT_TRUE(ppapi_.server_template()->AddEntity("file", contents, NULL)); |
| 288 ppapi_.server_template()->set_allow_partial(true); |
| 282 | 289 |
| 283 PP_Var var_headers = MakeString(348); | |
| 284 EXPECT_CALL(*response_, | |
| 285 GetProperty(response_resource_, | |
| 286 PP_URLRESPONSEPROPERTY_STATUSCODE)) | |
| 287 .WillOnce(Return(PP_MakeInt32(status_code))); | |
| 288 EXPECT_CALL(*response_, | |
| 289 GetProperty(response_resource_, PP_URLRESPONSEPROPERTY_HEADERS)) | |
| 290 .WillOnce(Return(var_headers)); | |
| 291 EXPECT_CALL(*var_, VarToUtf8(IsEqualToVar(var_headers), _)) | |
| 292 .WillOnce(DoAll(SetArgPointee<1>(strlen(headers)), | |
| 293 Return(headers))); | |
| 294 } | |
| 295 | |
| 296 void MountHttpNodeTest::SetResponseExpectFail(int status_code, | |
| 297 const char* headers) { | |
| 298 ON_CALL(*response_, GetProperty(response_resource_, _)) | |
| 299 .WillByDefault(Return(PP_MakeUndefined())); | |
| 300 | |
| 301 EXPECT_CALL(*response_, | |
| 302 GetProperty(response_resource_, | |
| 303 PP_URLRESPONSEPROPERTY_STATUSCODE)) | |
| 304 .WillOnce(Return(PP_MakeInt32(status_code))); | |
| 305 } | |
| 306 | |
| 307 ACTION_P3(ReadResponseBodyAction, offset, body, body_length) { | |
| 308 char* buf = static_cast<char*>(arg1); | |
| 309 size_t read_length = arg2; | |
| 310 PP_CompletionCallback callback = arg3; | |
| 311 if (*offset >= body_length) | |
| 312 return 0; | |
| 313 | |
| 314 read_length = std::min(read_length, body_length - *offset); | |
| 315 memcpy(buf, body + *offset, read_length); | |
| 316 *offset += read_length; | |
| 317 | |
| 318 // Also call the callback. | |
| 319 if (callback.func) | |
| 320 (*callback.func)(callback.user_data, PP_OK); | |
| 321 | |
| 322 return read_length; | |
| 323 } | |
| 324 | |
| 325 void MountHttpNodeTest::SetResponseBody(const char* body) { | |
| 326 response_body_offset_ = 0; | |
| 327 EXPECT_CALL(*loader_, ReadResponseBody(loader_resource_, _, _, _)) | |
| 328 .WillRepeatedly(ReadResponseBodyAction( | |
| 329 &response_body_offset_, body, strlen(body))); | |
| 330 } | |
| 331 | |
| 332 void MountHttpNodeTest::OpenNode() { | |
| 333 ASSERT_EQ(0, mnt_->Open(Path(path_), O_RDONLY, &node_)); | |
| 334 ASSERT_NE((MountNode*)NULL, node_.get()); | |
| 335 } | |
| 336 | |
| 337 void MountHttpNodeTest::ResetMocks() { | |
| 338 Mock::VerifyAndClearExpectations(&ppapi_); | |
| 339 Mock::VerifyAndClearExpectations(loader_); | |
| 340 Mock::VerifyAndClearExpectations(request_); | |
| 341 Mock::VerifyAndClearExpectations(response_); | |
| 342 Mock::VerifyAndClearExpectations(var_); | |
| 343 } | |
| 344 | |
| 345 void MountHttpNodeTest::TearDown() { | |
| 346 node_.reset(); | |
| 347 delete mnt_; | |
| 348 } | |
| 349 | |
| 350 // TODO(binji): These tests are all broken now. In another CL, I'll reimplement | |
| 351 // these tests using an HTTP fake. | |
| 352 TEST_F(MountHttpNodeTest, DISABLED_OpenAndCloseNoCache) { | |
| 353 StringMap_t smap; | |
| 354 smap["cache_content"] = "false"; | |
| 355 SetMountArgs(StringMap_t()); | |
| 356 ExpectOpen("HEAD"); | |
| 357 ExpectHeaders(""); | |
| 358 SetResponse(200, ""); | |
| 359 OpenNode(); | |
| 360 } | |
| 361 | |
| 362 TEST_F(MountHttpNodeTest, DISABLED_OpenAndCloseNotFound) { | |
| 363 StringMap_t smap; | |
| 364 smap["cache_content"] = "false"; | |
| 365 SetMountArgs(StringMap_t()); | |
| 366 ExpectOpen("HEAD"); | |
| 367 ExpectHeaders(""); | |
| 368 SetResponseExpectFail(404, ""); | |
| 369 ASSERT_EQ(ENOENT, mnt_->Open(Path(path_), O_RDONLY, &node_)); | |
| 370 } | |
| 371 | |
| 372 TEST_F(MountHttpNodeTest, DISABLED_OpenAndCloseServerError) { | |
| 373 StringMap_t smap; | |
| 374 smap["cache_content"] = "false"; | |
| 375 SetMountArgs(StringMap_t()); | |
| 376 ExpectOpen("HEAD"); | |
| 377 ExpectHeaders(""); | |
| 378 SetResponseExpectFail(500, ""); | |
| 379 ASSERT_EQ(EIO, mnt_->Open(Path(path_), O_RDONLY, &node_)); | |
| 380 } | |
| 381 | |
| 382 TEST_F(MountHttpNodeTest, DISABLED_GetStat) { | |
| 383 StringMap_t smap; | |
| 384 smap["cache_content"] = "false"; | |
| 385 SetMountArgs(StringMap_t()); | |
| 386 ExpectOpen("HEAD"); | |
| 387 ExpectHeaders(""); | |
| 388 SetResponse(200, "Content-Length: 42\n"); | |
| 389 OpenNode(); | |
| 390 | |
| 391 struct stat stat; | |
| 392 EXPECT_EQ(0, node_->GetStat(&stat)); | |
| 393 EXPECT_EQ(42, stat.st_size); | |
| 394 } | |
| 395 | |
| 396 TEST_F(MountHttpNodeTest, DISABLED_Access) { | |
| 397 StringMap_t smap; | |
| 398 smap["cache_content"] = "false"; | |
| 399 SetMountArgs(StringMap_t()); | |
| 400 ExpectOpen("HEAD"); | |
| 401 ExpectHeaders(""); | |
| 402 SetResponse(200, ""); | |
| 403 ASSERT_EQ(0, mnt_->Access(Path(path_), R_OK)); | |
| 404 } | |
| 405 | |
| 406 TEST_F(MountHttpNodeTest, DISABLED_AccessWrite) { | |
| 407 StringMap_t smap; | |
| 408 smap["cache_content"] = "false"; | |
| 409 SetMountArgs(StringMap_t()); | |
| 410 ExpectOpen("HEAD"); | |
| 411 ExpectHeaders(""); | |
| 412 SetResponse(200, ""); | |
| 413 ASSERT_EQ(EACCES, mnt_->Access(Path(path_), W_OK)); | |
| 414 } | |
| 415 | |
| 416 TEST_F(MountHttpNodeTest, DISABLED_AccessNotFound) { | |
| 417 StringMap_t smap; | |
| 418 smap["cache_content"] = "false"; | |
| 419 SetMountArgs(StringMap_t()); | |
| 420 ExpectOpen("HEAD"); | |
| 421 ExpectHeaders(""); | |
| 422 SetResponseExpectFail(404, ""); | |
| 423 ASSERT_EQ(ENOENT, mnt_->Access(Path(path_), R_OK)); | |
| 424 } | |
| 425 | |
| 426 TEST_F(MountHttpNodeTest, DISABLED_ReadCached) { | |
| 427 size_t result_size = 0; | |
| 428 int result_bytes = 0; | 290 int result_bytes = 0; |
| 429 | 291 |
| 430 SetMountArgs(StringMap_t()); | |
| 431 ExpectOpen("HEAD"); | |
| 432 ExpectHeaders(""); | |
| 433 SetResponse(200, "Content-Length: 42\n"); | |
| 434 OpenNode(); | |
| 435 ResetMocks(); | |
| 436 | |
| 437 EXPECT_EQ(0, node_->GetSize(&result_size)); | |
| 438 EXPECT_EQ(42, result_size); | |
| 439 | |
| 440 char buf[10]; | 292 char buf[10]; |
| 441 memset(&buf[0], 0, sizeof(buf)); | 293 memset(&buf[0], 0, sizeof(buf)); |
| 442 | 294 |
| 443 ExpectOpen("GET"); | 295 ScopedMountNode node; |
| 444 ExpectHeaders(""); | 296 ASSERT_EQ(0, mnt_.Open(Path("/file"), O_RDONLY, &node)); |
| 445 SetResponse(200, "Content-Length: 42\n"); | |
| 446 SetResponseBody("Here is some response text. And some more."); | |
| 447 HandleAttr attr; | 297 HandleAttr attr; |
| 448 EXPECT_EQ(0, node_->Read(attr, buf, sizeof(buf) - 1, &result_bytes)); | 298 EXPECT_EQ(0, node->Read(attr, buf, sizeof(buf) - 1, &result_bytes)); |
| 449 EXPECT_STREQ("Here is s", &buf[0]); | 299 EXPECT_EQ(sizeof(buf) - 1, result_bytes); |
| 450 ResetMocks(); | 300 EXPECT_STREQ("012345678", &buf[0]); |
| 451 | 301 |
| 452 // Further reads should be cached. | |
| 453 EXPECT_EQ(0, node_->Read(attr, buf, sizeof(buf) - 1, &result_bytes)); | |
| 454 EXPECT_STREQ("Here is s", &buf[0]); | |
| 455 attr.offs = 10; | 302 attr.offs = 10; |
| 456 EXPECT_EQ(0, node_->Read(attr, buf, sizeof(buf) - 1, &result_bytes)); | 303 EXPECT_EQ(0, node->Read(attr, buf, sizeof(buf) - 1, &result_bytes)); |
| 457 EXPECT_STREQ("me respon", &buf[0]); | 304 EXPECT_EQ(sizeof(buf) - 1, result_bytes); |
| 458 | 305 EXPECT_STREQ("abcdefghi", &buf[0]); |
| 459 EXPECT_EQ(0, node_->GetSize(&result_size)); | |
| 460 EXPECT_EQ(42, result_size); | |
| 461 } | 306 } |
| 462 | 307 |
| 463 TEST_F(MountHttpNodeTest, DISABLED_ReadCachedNoContentLength) { | 308 TEST_F(MountHttpNoCacheTest, ReadPartialNoServerSupport) { |
| 464 size_t result_size = 0; | 309 const char contents[] = "0123456789abcdefghi"; |
| 310 ASSERT_TRUE(ppapi_.server_template()->AddEntity("file", contents, NULL)); |
| 311 ppapi_.server_template()->set_allow_partial(false); |
| 312 |
| 465 int result_bytes = 0; | 313 int result_bytes = 0; |
| 466 | 314 |
| 467 SetMountArgs(StringMap_t()); | |
| 468 ExpectOpen("HEAD"); | |
| 469 ExpectHeaders(""); | |
| 470 SetResponse(200, ""); | |
| 471 OpenNode(); | |
| 472 ResetMocks(); | |
| 473 | |
| 474 ExpectOpen("GET"); | |
| 475 ExpectHeaders(""); | |
| 476 SetResponse(200, ""); // No Content-Length response here. | |
| 477 SetResponseBody("Here is some response text. And some more."); | |
| 478 | |
| 479 // GetSize will Read() because it didn't get the content length from the HEAD | |
| 480 // request. | |
| 481 EXPECT_EQ(0, node_->GetSize(&result_size)); | |
| 482 EXPECT_EQ(42, result_size); | |
| 483 | |
| 484 char buf[10]; | 315 char buf[10]; |
| 485 memset(&buf[0], 0, sizeof(buf)); | 316 memset(&buf[0], 0, sizeof(buf)); |
| 486 | 317 |
| 318 ScopedMountNode node; |
| 319 ASSERT_EQ(0, mnt_.Open(Path("/file"), O_RDONLY, &node)); |
| 487 HandleAttr attr; | 320 HandleAttr attr; |
| 488 EXPECT_EQ(0, node_->Read(attr, buf, sizeof(buf) - 1, &result_bytes)); | 321 EXPECT_EQ(0, node->Read(attr, buf, sizeof(buf) - 1, &result_bytes)); |
| 489 EXPECT_STREQ("Here is s", &buf[0]); | |
| 490 ResetMocks(); | |
| 491 | |
| 492 // Further reads should be cached. | |
| 493 EXPECT_EQ(0, node_->Read(attr, buf, sizeof(buf) - 1, &result_bytes)); | |
| 494 EXPECT_STREQ("Here is s", &buf[0]); | |
| 495 attr.offs = 10; | |
| 496 EXPECT_EQ(0, node_->Read(attr, buf, sizeof(buf) - 1, &result_bytes)); | |
| 497 EXPECT_STREQ("me respon", &buf[0]); | |
| 498 | |
| 499 EXPECT_EQ(0, node_->GetSize(&result_size)); | |
| 500 EXPECT_EQ(42, result_size); | |
| 501 } | |
| 502 | |
| 503 TEST_F(MountHttpNodeTest, DISABLED_ReadCachedUnderrun) { | |
| 504 size_t result_size = 0; | |
| 505 int result_bytes = 0; | |
| 506 | |
| 507 SetMountArgs(StringMap_t()); | |
| 508 ExpectOpen("HEAD"); | |
| 509 ExpectHeaders(""); | |
| 510 SetResponse(200, "Content-Length: 100\n"); | |
| 511 OpenNode(); | |
| 512 ResetMocks(); | |
| 513 | |
| 514 EXPECT_EQ(0, node_->GetSize(&result_size)); | |
| 515 EXPECT_EQ(100, result_size); | |
| 516 | |
| 517 char buf[10]; | |
| 518 memset(&buf[0], 0, sizeof(buf)); | |
| 519 | |
| 520 ExpectOpen("GET"); | |
| 521 ExpectHeaders(""); | |
| 522 SetResponse(200, "Content-Length: 100\n"); | |
| 523 SetResponseBody("abcdefghijklmnopqrstuvwxyz"); | |
| 524 HandleAttr attr; | |
| 525 EXPECT_EQ(0, node_->Read(attr, buf, sizeof(buf) - 1, &result_bytes)); | |
| 526 EXPECT_EQ(sizeof(buf) - 1, result_bytes); | |
| 527 EXPECT_STREQ("abcdefghi", &buf[0]); | |
| 528 ResetMocks(); | |
| 529 | |
| 530 EXPECT_EQ(0, node_->GetSize(&result_size)); | |
| 531 EXPECT_EQ(26, result_size); | |
| 532 } | |
| 533 | |
| 534 TEST_F(MountHttpNodeTest, DISABLED_ReadCachedOverrun) { | |
| 535 size_t result_size = 0; | |
| 536 int result_bytes = 0; | |
| 537 | |
| 538 SetMountArgs(StringMap_t()); | |
| 539 ExpectOpen("HEAD"); | |
| 540 ExpectHeaders(""); | |
| 541 SetResponse(200, "Content-Length: 15\n"); | |
| 542 OpenNode(); | |
| 543 ResetMocks(); | |
| 544 | |
| 545 EXPECT_EQ(0, node_->GetSize(&result_size)); | |
| 546 EXPECT_EQ(15, result_size); | |
| 547 | |
| 548 char buf[10]; | |
| 549 memset(&buf[0], 0, sizeof(buf)); | |
| 550 | |
| 551 ExpectOpen("GET"); | |
| 552 ExpectHeaders(""); | |
| 553 SetResponse(200, "Content-Length: 15\n"); | |
| 554 SetResponseBody("01234567890123456789"); | |
| 555 HandleAttr attr; | |
| 556 attr.offs = 10; | |
| 557 EXPECT_EQ(0, node_->Read(attr, buf, sizeof(buf) - 1, &result_bytes)); | |
| 558 EXPECT_EQ(5, result_bytes); | |
| 559 EXPECT_STREQ("01234", &buf[0]); | |
| 560 ResetMocks(); | |
| 561 | |
| 562 EXPECT_EQ(0, node_->GetSize(&result_size)); | |
| 563 EXPECT_EQ(15, result_size); | |
| 564 } | |
| 565 | |
| 566 TEST_F(MountHttpNodeTest, DISABLED_ReadPartial) { | |
| 567 int result_bytes = 0; | |
| 568 | |
| 569 StringMap_t args; | |
| 570 args["cache_content"] = "false"; | |
| 571 SetMountArgs(args); | |
| 572 ExpectOpen("HEAD"); | |
| 573 ExpectHeaders(""); | |
| 574 SetResponse(200, ""); | |
| 575 OpenNode(); | |
| 576 ResetMocks(); | |
| 577 | |
| 578 char buf[10]; | |
| 579 memset(&buf[0], 0, sizeof(buf)); | |
| 580 | |
| 581 ExpectOpen("GET"); | |
| 582 ExpectHeaders("Range: bytes=0-8\n"); | |
| 583 SetResponse(206, "Content-Length: 9\nContent-Range: bytes=0-8\n"); | |
| 584 SetResponseBody("012345678"); | |
| 585 HandleAttr attr; | |
| 586 EXPECT_EQ(0, node_->Read(attr, buf, sizeof(buf) - 1, &result_bytes)); | |
| 587 EXPECT_EQ(sizeof(buf) - 1, result_bytes); | 322 EXPECT_EQ(sizeof(buf) - 1, result_bytes); |
| 588 EXPECT_STREQ("012345678", &buf[0]); | 323 EXPECT_STREQ("012345678", &buf[0]); |
| 589 ResetMocks(); | |
| 590 | 324 |
| 591 // Another read is another request. | |
| 592 ExpectOpen("GET"); | |
| 593 ExpectHeaders("Range: bytes=10-18\n"); | |
| 594 SetResponse(206, "Content-Length: 9\nContent-Range: bytes=10-18\n"); | |
| 595 SetResponseBody("abcdefghi"); | |
| 596 attr.offs = 10; | 325 attr.offs = 10; |
| 597 EXPECT_EQ(0, node_->Read(attr, buf, sizeof(buf) - 1, &result_bytes)); | 326 EXPECT_EQ(0, node->Read(attr, buf, sizeof(buf) - 1, &result_bytes)); |
| 598 EXPECT_EQ(sizeof(buf) - 1, result_bytes); | 327 EXPECT_EQ(sizeof(buf) - 1, result_bytes); |
| 599 EXPECT_STREQ("abcdefghi", &buf[0]); | 328 EXPECT_STREQ("abcdefghi", &buf[0]); |
| 600 } | 329 } |
| 601 | |
| 602 TEST_F(MountHttpNodeTest, DISABLED_ReadPartialNoServerSupport) { | |
| 603 int result_bytes = 0; | |
| 604 | |
| 605 StringMap_t args; | |
| 606 args["cache_content"] = "false"; | |
| 607 SetMountArgs(args); | |
| 608 ExpectOpen("HEAD"); | |
| 609 ExpectHeaders(""); | |
| 610 SetResponse(200, ""); | |
| 611 OpenNode(); | |
| 612 ResetMocks(); | |
| 613 | |
| 614 char buf[10]; | |
| 615 memset(&buf[0], 0, sizeof(buf)); | |
| 616 | |
| 617 ExpectOpen("GET"); | |
| 618 ExpectHeaders("Range: bytes=10-18\n"); | |
| 619 SetResponse(200, "Content-Length: 20\n"); | |
| 620 SetResponseBody("0123456789abcdefghij"); | |
| 621 HandleAttr attr; | |
| 622 attr.offs = 10; | |
| 623 EXPECT_EQ(0, node_->Read(attr, buf, sizeof(buf) - 1, &result_bytes)); | |
| 624 EXPECT_EQ(sizeof(buf) - 1, result_bytes); | |
| 625 EXPECT_STREQ("abcdefghi", &buf[0]); | |
| 626 } | |
| 627 | |
| OLD | NEW |