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