diff options
author | binji@chromium.org <binji@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-11-20 23:30:44 +0000 |
---|---|---|
committer | binji@chromium.org <binji@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-11-20 23:30:44 +0000 |
commit | 8c587afa858ba02a00693af360085bb79dd5849a (patch) | |
tree | 90fef351b20d3b0761466bc6e81027fba3fae002 /native_client_sdk/src | |
parent | 7f74766f9d3ad8a7d1bc2485bceb6dcc1867f614 (diff) | |
download | chromium_src-8c587afa858ba02a00693af360085bb79dd5849a.zip chromium_src-8c587afa858ba02a00693af360085bb79dd5849a.tar.gz chromium_src-8c587afa858ba02a00693af360085bb79dd5849a.tar.bz2 |
[NaCl SDK] Add URLLoader fake pepper interface.
BUG=244313,245431
R=sbc@chromium.org
Review URL: https://codereview.chromium.org/77423002
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@236343 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'native_client_sdk/src')
10 files changed, 933 insertions, 548 deletions
diff --git a/native_client_sdk/src/libraries/nacl_io/mount_http.h b/native_client_sdk/src/libraries/nacl_io/mount_http.h index 21d688e..4946d9f 100644 --- a/native_client_sdk/src/libraries/nacl_io/mount_http.h +++ b/native_client_sdk/src/libraries/nacl_io/mount_http.h @@ -11,12 +11,8 @@ #include "nacl_io/pepper_interface.h" #include "nacl_io/typed_mount_factory.h" -class MountHttpMock; - namespace nacl_io { -class MountNode; - std::string NormalizeHeaderKey(const std::string& s); class MountHttp : public Mount { @@ -44,6 +40,8 @@ class MountHttp : public Mount { Error LoadManifest(const std::string& path, char** out_manifest); Error ParseManifest(const char *text); + NodeMap_t* GetNodeCacheForTesting() { return &node_cache_; } + private: // Gets the URL to fetch for |path|. // |path| is relative to the mount point for the HTTP filesystem. @@ -59,7 +57,6 @@ class MountHttp : public Mount { friend class TypedMountFactory<MountHttp>; friend class MountNodeHttp; - friend class ::MountHttpMock; }; } // namespace nacl_io diff --git a/native_client_sdk/src/libraries/nacl_io/mount_node_http.cc b/native_client_sdk/src/libraries/nacl_io/mount_node_http.cc index 5a65d41..25135e4 100644 --- a/native_client_sdk/src/libraries/nacl_io/mount_node_http.cc +++ b/native_client_sdk/src/libraries/nacl_io/mount_node_http.cc @@ -151,14 +151,14 @@ void MountNodeHttp::SetCachedSize(off_t size) { stat_.st_size = size; } -Error MountNodeHttp::FSync() { return ENOSYS; } +Error MountNodeHttp::FSync() { return EACCES; } Error MountNodeHttp::GetDents(size_t offs, struct dirent* pdir, size_t count, int* out_bytes) { *out_bytes = 0; - return ENOSYS; + return EACCES; } Error MountNodeHttp::GetStat(struct stat* stat) { @@ -193,10 +193,8 @@ Error MountNodeHttp::GetStat(struct stat* stat) { SetCachedSize(static_cast<off_t>(entity_length)); } else if (cache_content_ && !has_cached_size_) { error = DownloadToCache(); - // TODO(binji): this error should not be dropped, but it requires a bit - // of a refactor of the tests. See crbug.com/245431 - // if (error) - // return error; + if (error) + return error; } else { // Don't use SetCachedSize here -- it is actually unknown. stat_.st_size = 0; @@ -230,13 +228,13 @@ Error MountNodeHttp::Read(const HandleAttr& attr, return error; } - return ReadPartialFromCache(attr.offs, buf, count, out_bytes); + return ReadPartialFromCache(attr, buf, count, out_bytes); } - return DownloadPartial(attr.offs, buf, count, out_bytes); + return DownloadPartial(attr, buf, count, out_bytes); } -Error MountNodeHttp::FTruncate(off_t size) { return ENOSYS; } +Error MountNodeHttp::FTruncate(off_t size) { return EACCES; } Error MountNodeHttp::Write(const HandleAttr& attr, const void* buf, @@ -244,7 +242,7 @@ Error MountNodeHttp::Write(const HandleAttr& attr, int* out_bytes) { // TODO(binji): support POST? *out_bytes = 0; - return ENOSYS; + return EACCES; } Error MountNodeHttp::GetSize(size_t* out_size) { @@ -342,6 +340,8 @@ Error MountNodeHttp::OpenUrl(const char* method, *out_response_headers = ParseHeaders(response_headers_str, response_headers_length); + var_interface->Release(response_headers_var); + return 0; } @@ -404,23 +404,25 @@ Error MountNodeHttp::DownloadToCache() { } } -Error MountNodeHttp::ReadPartialFromCache(size_t offs, +Error MountNodeHttp::ReadPartialFromCache(const HandleAttr& attr, void* buf, int count, int* out_bytes) { *out_bytes = 0; + size_t size = cached_data_.size(); - if (offs > cached_data_.size()) - return EINVAL; + if (attr.offs + count > size) + count = size - attr.offs; - count = std::min(count, static_cast<int>(cached_data_.size() - offs)); - memcpy(buf, &cached_data_.data()[offs], count); + if (count <= 0) + return 0; + memcpy(buf, &cached_data_.data()[attr.offs], count); *out_bytes = count; return 0; } -Error MountNodeHttp::DownloadPartial(size_t offs, +Error MountNodeHttp::DownloadPartial(const HandleAttr& attr, void* buf, size_t count, int* out_bytes) { @@ -433,8 +435,8 @@ Error MountNodeHttp::DownloadPartial(size_t offs, snprintf(&buffer[0], sizeof(buffer), "bytes=%" PRIuS "-%" PRIuS, - offs, - offs + count - 1); + attr.offs, + attr.offs + count - 1); headers["Range"] = buffer; PP_Resource loader; @@ -462,12 +464,12 @@ Error MountNodeHttp::DownloadPartial(size_t offs, // No partial result, read everything starting from the part we care about. size_t content_length; if (ParseContentLength(response_headers, &content_length)) { - if (offs >= content_length) + if (attr.offs >= content_length) return EINVAL; // Clamp count, if trying to read past the end of the file. - if (offs + count > content_length) { - count = content_length - offs; + if (attr.offs + count > content_length) { + count = content_length - attr.offs; } } } else if (statuscode == STATUSCODE_PARTIAL_CONTENT) { @@ -476,7 +478,7 @@ Error MountNodeHttp::DownloadPartial(size_t offs, size_t entity_length; if (ParseContentRange( response_headers, &read_start, &read_end, &entity_length)) { - if (read_start > offs || read_start > read_end) { + if (read_start > attr.offs || read_start > read_end) { // If this error occurs, the server is returning bogus values. return EINVAL; } @@ -488,14 +490,14 @@ Error MountNodeHttp::DownloadPartial(size_t offs, // exactly what we asked for. This can happen even when the server // returns 200 -- the cache may return 206 in this case, but not modify // the headers. - read_start = offs; + read_start = attr.offs; } } - if (read_start < offs) { + if (read_start < attr.offs) { // We aren't yet at the location where we want to start reading. Read into // our dummy buffer until then. - size_t bytes_to_read = offs - read_start; + size_t bytes_to_read = attr.offs - read_start; if (buffer_.size() < bytes_to_read) buffer_.resize(std::min(bytes_to_read, MAX_READ_BUFFER_SIZE)); diff --git a/native_client_sdk/src/libraries/nacl_io/mount_node_http.h b/native_client_sdk/src/libraries/nacl_io/mount_node_http.h index 0e083b4..9ea5f14 100644 --- a/native_client_sdk/src/libraries/nacl_io/mount_node_http.h +++ b/native_client_sdk/src/libraries/nacl_io/mount_node_http.h @@ -52,11 +52,14 @@ class MountNodeHttp : public MountNode { StringMap_t* out_response_headers); Error DownloadToCache(); - Error ReadPartialFromCache(size_t offs, + Error ReadPartialFromCache(const HandleAttr& attr, void* buf, int count, int* out_bytes); - Error DownloadPartial(size_t offs, void* buf, size_t count, int* out_bytes); + Error DownloadPartial(const HandleAttr& attr, + void* buf, + size_t count, + int* out_bytes); Error DownloadToBuffer(PP_Resource loader, void* buf, int count, diff --git a/native_client_sdk/src/tests/nacl_io_test/example.dsc b/native_client_sdk/src/tests/nacl_io_test/example.dsc index 059c389..908ef17 100644 --- a/native_client_sdk/src/tests/nacl_io_test/example.dsc +++ b/native_client_sdk/src/tests/nacl_io_test/example.dsc @@ -12,6 +12,8 @@ 'fake_core_interface.h', 'fake_pepper_interface_html5fs.cc', 'fake_pepper_interface_html5fs.h', + 'fake_pepper_interface_url_loader.cc', + 'fake_pepper_interface_url_loader.h', 'fake_resource_manager.cc', 'fake_resource_manager.h', 'fake_var_interface.cc', diff --git a/native_client_sdk/src/tests/nacl_io_test/fake_pepper_interface_html5fs.h b/native_client_sdk/src/tests/nacl_io_test/fake_pepper_interface_html5fs.h index 6e10844..9903c30 100644 --- a/native_client_sdk/src/tests/nacl_io_test/fake_pepper_interface_html5fs.h +++ b/native_client_sdk/src/tests/nacl_io_test/fake_pepper_interface_html5fs.h @@ -98,29 +98,29 @@ class FakeFileIoInterface : public nacl_io::FileIoInterface { public: explicit FakeFileIoInterface(FakeCoreInterface* core_interface); - PP_Resource Create(PP_Resource instance); - int32_t Open(PP_Resource file_io, - PP_Resource file_ref, - int32_t open_flags, - PP_CompletionCallback callback); - int32_t Query(PP_Resource file_io, - PP_FileInfo* info, - PP_CompletionCallback callback); - int32_t Read(PP_Resource file_io, - int64_t offset, - char* buffer, - int32_t bytes_to_read, - PP_CompletionCallback callback); - int32_t Write(PP_Resource file_io, - int64_t offset, - const char* buffer, - int32_t bytes_to_write, - PP_CompletionCallback callback); - int32_t SetLength(PP_Resource file_io, - int64_t length, - PP_CompletionCallback callback); - int32_t Flush(PP_Resource file_io, PP_CompletionCallback callback); - void Close(PP_Resource file_io); + virtual PP_Resource Create(PP_Resource instance); + virtual int32_t Open(PP_Resource file_io, + PP_Resource file_ref, + int32_t open_flags, + PP_CompletionCallback callback); + virtual int32_t Query(PP_Resource file_io, + PP_FileInfo* info, + PP_CompletionCallback callback); + virtual int32_t Read(PP_Resource file_io, + int64_t offset, + char* buffer, + int32_t bytes_to_read, + PP_CompletionCallback callback); + virtual int32_t Write(PP_Resource file_io, + int64_t offset, + const char* buffer, + int32_t bytes_to_write, + PP_CompletionCallback callback); + virtual int32_t SetLength(PP_Resource file_io, + int64_t length, + PP_CompletionCallback callback); + virtual int32_t Flush(PP_Resource file_io, PP_CompletionCallback callback); + virtual void Close(PP_Resource file_io); private: FakeCoreInterface* core_interface_; // Weak reference. @@ -133,18 +133,18 @@ class FakeFileRefInterface : public nacl_io::FileRefInterface { FakeFileRefInterface(FakeCoreInterface* core_interface, FakeVarInterface* var_interface); - PP_Resource Create(PP_Resource file_system, const char* path); - PP_Var GetName(PP_Resource file_ref); - int32_t MakeDirectory(PP_Resource directory_ref, - PP_Bool make_ancestors, + virtual PP_Resource Create(PP_Resource file_system, const char* path); + virtual PP_Var GetName(PP_Resource file_ref); + virtual int32_t MakeDirectory(PP_Resource directory_ref, + PP_Bool make_ancestors, + PP_CompletionCallback callback); + virtual int32_t Delete(PP_Resource file_ref, PP_CompletionCallback callback); + virtual int32_t Query(PP_Resource file_ref, + PP_FileInfo* info, PP_CompletionCallback callback); - int32_t Delete(PP_Resource file_ref, PP_CompletionCallback callback); - int32_t Query(PP_Resource file_ref, - PP_FileInfo* info, - PP_CompletionCallback callback); - int32_t ReadDirectoryEntries(PP_Resource file_ref, - const PP_ArrayOutput& output, - PP_CompletionCallback callback); + virtual int32_t ReadDirectoryEntries(PP_Resource file_ref, + const PP_ArrayOutput& output, + PP_CompletionCallback callback); private: FakeCoreInterface* core_interface_; // Weak reference. @@ -157,10 +157,10 @@ class FakeFileSystemInterface : public nacl_io::FileSystemInterface { public: FakeFileSystemInterface(FakeCoreInterface* core_interface); - PP_Resource Create(PP_Instance instance, PP_FileSystemType type); - int32_t Open(PP_Resource file_system, - int64_t expected_size, - PP_CompletionCallback callback); + virtual PP_Resource Create(PP_Instance instance, PP_FileSystemType type); + virtual int32_t Open(PP_Resource file_system, + int64_t expected_size, + PP_CompletionCallback callback); private: FakeCoreInterface* core_interface_; // Weak reference. diff --git a/native_client_sdk/src/tests/nacl_io_test/fake_pepper_interface_url_loader.cc b/native_client_sdk/src/tests/nacl_io_test/fake_pepper_interface_url_loader.cc new file mode 100644 index 0000000..1b6b92c --- /dev/null +++ b/native_client_sdk/src/tests/nacl_io_test/fake_pepper_interface_url_loader.cc @@ -0,0 +1,502 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "fake_pepper_interface_url_loader.h" + +#include <string.h> +#include <strings.h> + +#include <algorithm> +#include <sstream> + +#include "gtest/gtest.h" + +namespace { + +bool GetHeaderValue(const std::string& headers, const std::string& key, + std::string* out_value) { + out_value->clear(); + + size_t offset = 0; + while (offset != std::string::npos) { + // Find the next colon; this separates the key from the value. + size_t colon = headers.find(':', offset); + if (colon == std::string::npos) + return false; + + // Find the newline; this separates the value from the next header. + size_t newline = headers.find('\n', offset); + if (strncasecmp(key.c_str(), &headers.data()[offset], key.size()) != 0) { + // Key doesn't match, skip to next header. + offset = newline; + continue; + } + + // Key matches, extract value. First, skip leading spaces. + size_t nonspace = headers.find_first_not_of(' ', colon + 1); + if (nonspace == std::string::npos) + return false; + + out_value->assign(headers, nonspace, newline - nonspace); + return true; + } + + return false; +} + +class FakeInstanceResource : public FakeResource { + public: + FakeInstanceResource() : server_template(NULL) {} + static const char* classname() { return "FakeInstanceResource"; } + + FakeURLLoaderServer* server_template; // Weak reference. +}; + +class FakeURLLoaderResource : public FakeResource { + public: + FakeURLLoaderResource() + : manager(NULL), + server(NULL), + entity(NULL), + response(0), + read_offset(0) {} + + virtual void Destroy() { + EXPECT_TRUE(manager != NULL); + if (response != 0) + manager->Release(response); + } + + static const char* classname() { return "FakeURLLoaderResource"; } + + FakeResourceManager* manager; // Weak reference. + FakeURLLoaderServer* server; // Weak reference. + FakeURLLoaderEntity* entity; // Weak reference. + PP_Resource response; + size_t read_offset; + size_t read_end; +}; + +class FakeURLRequestInfoResource : public FakeResource { + public: + FakeURLRequestInfoResource() {} + static const char* classname() { return "FakeURLRequestInfoResource"; } + + std::string url; + std::string method; + std::string headers; +}; + +class FakeURLResponseInfoResource : public FakeResource { + public: + FakeURLResponseInfoResource() : status_code(0) {} + static const char* classname() { return "FakeURLResponseInfoResource"; } + + int status_code; + std::string url; + std::string headers; +}; + +// Helper function to call the completion callback if it is defined (an +// asynchronous call), or return the result directly if it isn't (a synchronous +// call). +// +// Use like this: +// if (<some error condition>) +// return RunCompletionCallback(callback, PP_ERROR_FUBAR); +// +// /* Everything worked OK */ +// return RunCompletionCallback(callback, PP_OK); +int32_t RunCompletionCallback(PP_CompletionCallback* callback, int32_t result) { + if (callback->func) { + PP_RunCompletionCallback(callback, result); + return PP_OK_COMPLETIONPENDING; + } + return result; +} + +} // namespace + +FakeURLLoaderEntity::FakeURLLoaderEntity(const std::string& body) + : body_(body) {} + +FakeURLLoaderServer::FakeURLLoaderServer() + : max_read_size_(0), send_content_length_(false), allow_partial_(false) {} + +void FakeURLLoaderServer::Clear() { + entity_map_.clear(); +} + +bool FakeURLLoaderServer::AddEntity(const std::string& url, + const std::string& body, + FakeURLLoaderEntity** out_entity) { + EntityMap::iterator iter = entity_map_.find(url); + if (iter != entity_map_.end()) { + if (out_entity) + *out_entity = NULL; + return false; + } + + FakeURLLoaderEntity entity(body); + std::pair<EntityMap::iterator, bool> result = + entity_map_.insert(EntityMap::value_type(url, entity)); + + EXPECT_EQ(true, result.second); + if (out_entity) + *out_entity = &result.first->second; + return true; +} + +bool FakeURLLoaderServer::AddError(const std::string& url, + int http_status_code) { + ErrorMap::iterator iter = error_map_.find(url); + if (iter != error_map_.end()) + return false; + + error_map_[url] = http_status_code; + return true; +} + +FakeURLLoaderEntity* FakeURLLoaderServer::GetEntity(const std::string& url) { + EntityMap::iterator iter = entity_map_.find(url); + if (iter == entity_map_.end()) + return NULL; + return &iter->second; +} + +int FakeURLLoaderServer::GetError(const std::string& url) { + ErrorMap::iterator iter = error_map_.find(url); + if (iter != error_map_.end()) + return 0; + return iter->second; +} + +FakeURLLoaderInterface::FakeURLLoaderInterface( + FakeCoreInterface* core_interface) + : core_interface_(core_interface) {} + +PP_Resource FakeURLLoaderInterface::Create(PP_Instance instance) { + FakeInstanceResource* instance_resource = + core_interface_->resource_manager()->Get<FakeInstanceResource>(instance); + if (instance_resource == NULL) + return PP_ERROR_BADRESOURCE; + + FakeURLLoaderResource* loader_resource = new FakeURLLoaderResource; + loader_resource->manager = core_interface_->resource_manager(); + loader_resource->server = + new FakeURLLoaderServer(*instance_resource->server_template); + + return CREATE_RESOURCE(core_interface_->resource_manager(), + FakeURLLoaderResource, + loader_resource); +} + +int32_t FakeURLLoaderInterface::Open(PP_Resource loader, + PP_Resource request_info, + PP_CompletionCallback callback) { + FakeURLLoaderResource* loader_resource = + core_interface_->resource_manager()->Get<FakeURLLoaderResource>(loader); + if (loader_resource == NULL) + return PP_ERROR_BADRESOURCE; + + FakeURLRequestInfoResource* request_info_resource = + core_interface_->resource_manager()->Get<FakeURLRequestInfoResource>( + request_info); + if (request_info_resource == NULL) + return PP_ERROR_BADRESOURCE; + + // Create a response resource. + FakeURLResponseInfoResource* response = new FakeURLResponseInfoResource; + loader_resource->response = + CREATE_RESOURCE(core_interface_->resource_manager(), + FakeURLResponseInfoResource, + response); + + loader_resource->entity = NULL; + loader_resource->read_offset = 0; + loader_resource->read_end = 0; + + // Get the URL from the request info. + std::string url = request_info_resource->url; + std::string method = request_info_resource->method; + + response->url = url; + // TODO(binji): allow this to be set? + response->headers.clear(); + + // Check the error map first, to see if this URL should produce an error. + EXPECT_TRUE(NULL != loader_resource->server); + int http_status_code = loader_resource->server->GetError(url); + if (http_status_code != 0) { + // Got an error, return that in the response. + response->status_code = http_status_code; + } else { + // Look up the URL in the loader resource entity map. + FakeURLLoaderEntity* entity = loader_resource->server->GetEntity(url); + response->status_code = entity ? 200 : 404; + + if (method == "GET") { + loader_resource->entity = entity; + } else if (method == "HEAD") { + // Do nothing, we only set the status code. + } else { + response->status_code = 405; // Method not allowed. + } + + if (entity != NULL) { + size_t content_length = entity->body().size(); + loader_resource->read_end = content_length; + + if (loader_resource->server->send_content_length()) { + std::ostringstream ss; + ss << "Content-Length: " << content_length << "\n"; + response->headers += ss.str(); + } + + if (loader_resource->server->allow_partial()) { + std::string headers = request_info_resource->headers; + std::string range; + if (GetHeaderValue(headers, "Range", &range)) { + // We don't support all range requests, just bytes=<num>-<num> + int lo; + int hi; + if (sscanf(range.c_str(), "bytes=%d-%d", &lo, &hi) == 2) { + // The range is a closed interval; e.g. 0-10 is 11 bytes. We'll + // store it as a half-open interval instead--it's more natural in + // C that way. + loader_resource->read_offset = lo; + loader_resource->read_end = hi + 1; + + // Also add a "Content-Range" response header. + std::ostringstream ss; + ss << "Content-Range: " + << lo << "-" << hi << "/" << content_length << "\n"; + response->headers += ss.str(); + + response->status_code = 206; // Partial content + } else { + // Couldn't parse the range. + response->status_code = 416; // Request range not satisfiable. + } + } + } + } + } + + // Call the callback. + return RunCompletionCallback(&callback, PP_OK); +} + +PP_Resource FakeURLLoaderInterface::GetResponseInfo(PP_Resource loader) { + FakeURLLoaderResource* loader_resource = + core_interface_->resource_manager()->Get<FakeURLLoaderResource>(loader); + if (loader_resource == NULL) + return 0; + + // Returned resources have an implicit AddRef. + core_interface_->resource_manager()->AddRef(loader_resource->response); + return loader_resource->response; +} + +int32_t FakeURLLoaderInterface::ReadResponseBody( + PP_Resource loader, + void* buffer, + int32_t bytes_to_read, + PP_CompletionCallback callback) { + FakeURLLoaderResource* loader_resource = + core_interface_->resource_manager()->Get<FakeURLLoaderResource>(loader); + if (loader_resource == NULL) + return PP_ERROR_BADRESOURCE; + + if (loader_resource->entity == NULL) + // TODO(binji): figure out the correct error here. + return PP_ERROR_FAILED; + + const std::string& body = loader_resource->entity->body(); + size_t offset = loader_resource->read_offset; + // Never read more than is available. + size_t max_readable = std::max<size_t>(0, body.length() - offset); + size_t server_max_read_size = loader_resource->server->max_read_size(); + // Allow the test to specify how much the "server" should send in each call + // to ReadResponseBody. A max_read_size of 0 means read as much as the + // buffer will allow. + if (server_max_read_size != 0) + max_readable = std::min(max_readable, server_max_read_size); + + bytes_to_read = std::min(static_cast<size_t>(bytes_to_read), max_readable); + memcpy(buffer, &body.data()[offset], bytes_to_read); + loader_resource->read_offset += bytes_to_read; + + return RunCompletionCallback(&callback, bytes_to_read); +} + +void FakeURLLoaderInterface::Close(PP_Resource loader) { + FakeURLLoaderResource* loader_resource = + core_interface_->resource_manager()->Get<FakeURLLoaderResource>(loader); + if (loader_resource == NULL) + return; + + core_interface_->resource_manager()->Release(loader_resource->response); + + loader_resource->server = NULL; + loader_resource->entity = NULL; + loader_resource->response = 0; + loader_resource->read_offset = 0; +} + +FakeURLRequestInfoInterface::FakeURLRequestInfoInterface( + FakeCoreInterface* core_interface, + FakeVarInterface* var_interface) + : core_interface_(core_interface), var_interface_(var_interface) {} + +PP_Resource FakeURLRequestInfoInterface::Create(PP_Instance instance) { + FakeInstanceResource* instance_resource = + core_interface_->resource_manager()->Get<FakeInstanceResource>(instance); + if (instance_resource == NULL) + return PP_ERROR_BADRESOURCE; + + return CREATE_RESOURCE(core_interface_->resource_manager(), + FakeURLRequestInfoResource, + new FakeURLRequestInfoResource); +} + +PP_Bool FakeURLRequestInfoInterface::SetProperty(PP_Resource request, + PP_URLRequestProperty property, + PP_Var value) { + FakeURLRequestInfoResource* request_resource = + core_interface_->resource_manager()->Get<FakeURLRequestInfoResource>( + request); + if (request_resource == NULL) + return PP_FALSE; + + switch (property) { + case PP_URLREQUESTPROPERTY_URL: { + if (value.type != PP_VARTYPE_STRING) + return PP_FALSE; + + uint32_t len; + const char* url = var_interface_->VarToUtf8(value, &len); + if (url == NULL) + return PP_FALSE; + + request_resource->url = url; + var_interface_->Release(value); + return PP_TRUE; + } + case PP_URLREQUESTPROPERTY_METHOD: { + if (value.type != PP_VARTYPE_STRING) + return PP_FALSE; + + uint32_t len; + const char* url = var_interface_->VarToUtf8(value, &len); + if (url == NULL) + return PP_FALSE; + + request_resource->method = url; + var_interface_->Release(value); + return PP_TRUE; + } + case PP_URLREQUESTPROPERTY_HEADERS: { + if (value.type != PP_VARTYPE_STRING) + return PP_FALSE; + + uint32_t len; + const char* url = var_interface_->VarToUtf8(value, &len); + if (url == NULL) + return PP_FALSE; + + request_resource->headers = url; + var_interface_->Release(value); + return PP_TRUE; + } + case PP_URLREQUESTPROPERTY_ALLOWCROSSORIGINREQUESTS: { + if (value.type != PP_VARTYPE_BOOL) + return PP_FALSE; + // Throw the value away for now. TODO(binji): add tests for this. + return PP_TRUE; + } + case PP_URLREQUESTPROPERTY_ALLOWCREDENTIALS: { + if (value.type != PP_VARTYPE_BOOL) + return PP_FALSE; + // Throw the value away for now. TODO(binji): add tests for this. + return PP_TRUE; + } + default: + EXPECT_TRUE(false) << "Unimplemented property " << property + << " in " + "FakeURLRequestInfoInterface::SetProperty"; + return PP_FALSE; + } +} + +FakeURLResponseInfoInterface::FakeURLResponseInfoInterface( + FakeCoreInterface* core_interface, + FakeVarInterface* var_interface) + : core_interface_(core_interface), var_interface_(var_interface) {} + +PP_Var FakeURLResponseInfoInterface::GetProperty( + PP_Resource response, + PP_URLResponseProperty property) { + FakeURLResponseInfoResource* response_resource = + core_interface_->resource_manager()->Get<FakeURLResponseInfoResource>( + response); + if (response_resource == NULL) + return PP_Var(); + + switch (property) { + case PP_URLRESPONSEPROPERTY_URL: + return var_interface_->VarFromUtf8(response_resource->url.data(), + response_resource->url.size()); + + case PP_URLRESPONSEPROPERTY_STATUSCODE: + return PP_MakeInt32(response_resource->status_code); + + case PP_URLRESPONSEPROPERTY_HEADERS: + return var_interface_->VarFromUtf8(response_resource->headers.data(), + response_resource->headers.size()); + default: + EXPECT_TRUE(false) << "Unimplemented property " << property + << " in " + "FakeURLResponseInfoInterface::GetProperty"; + return PP_Var(); + } +} + +FakePepperInterfaceURLLoader::FakePepperInterfaceURLLoader() + : url_loader_interface_(&core_interface_), + url_request_info_interface_(&core_interface_, &var_interface_), + url_response_info_interface_(&core_interface_, &var_interface_) { + FakeInstanceResource* instance_resource = new FakeInstanceResource; + instance_resource->server_template = &server_template_; + instance_ = CREATE_RESOURCE(core_interface_.resource_manager(), + FakeInstanceResource, + instance_resource); +} + +FakePepperInterfaceURLLoader::~FakePepperInterfaceURLLoader() { + core_interface_.ReleaseResource(instance_); +} + +nacl_io::CoreInterface* FakePepperInterfaceURLLoader::GetCoreInterface() { + return &core_interface_; +} + +nacl_io::VarInterface* FakePepperInterfaceURLLoader::GetVarInterface() { + return &var_interface_; +} + +nacl_io::URLLoaderInterface* +FakePepperInterfaceURLLoader::GetURLLoaderInterface() { + return &url_loader_interface_; +} + +nacl_io::URLRequestInfoInterface* +FakePepperInterfaceURLLoader::GetURLRequestInfoInterface() { + return &url_request_info_interface_; +} + +nacl_io::URLResponseInfoInterface* +FakePepperInterfaceURLLoader::GetURLResponseInfoInterface() { + return &url_response_info_interface_; +} diff --git a/native_client_sdk/src/tests/nacl_io_test/fake_pepper_interface_url_loader.h b/native_client_sdk/src/tests/nacl_io_test/fake_pepper_interface_url_loader.h new file mode 100644 index 0000000..5945d13 --- /dev/null +++ b/native_client_sdk/src/tests/nacl_io_test/fake_pepper_interface_url_loader.h @@ -0,0 +1,151 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef LIBRARIES_NACL_IO_TEST_FAKE_PEPPER_INTERFACE_URL_LOADER_H_ +#define LIBRARIES_NACL_IO_TEST_FAKE_PEPPER_INTERFACE_URL_LOADER_H_ + +#include <map> +#include <string> +#include <vector> + +#include "fake_core_interface.h" +#include "fake_var_interface.h" +#include "nacl_io/pepper_interface_dummy.h" +#include "sdk_util/macros.h" + +class FakeURLLoaderEntity { + public: + explicit FakeURLLoaderEntity(const std::string& body); + + const std::string& body() const { return body_; } + + private: + std::string body_; +}; + +class FakeURLLoaderServer { + public: + FakeURLLoaderServer(); + + void Clear(); + bool AddEntity(const std::string& url, + const std::string& body, + FakeURLLoaderEntity** out_entity); + bool AddError(const std::string& url, + int http_status_code); + FakeURLLoaderEntity* GetEntity(const std::string& url); + // Returns 0 if the url is not in the error map. + int GetError(const std::string& url); + + // The maximum number of bytes that ReadResponseBody will send. If 0, then + // send as many as are requested. + void set_max_read_size(size_t max_read_size) { + max_read_size_ = max_read_size; + } + + // Whether to add the "Content-Length" header. + void set_send_content_length(bool send_content_length) { + send_content_length_ = send_content_length; + } + + // Whether to allow partial reads (via the "Range" request header). + void set_allow_partial(bool allow_partial) { allow_partial_ = allow_partial; } + + size_t max_read_size() const { return max_read_size_; } + bool send_content_length() const { return send_content_length_; } + bool allow_partial() const { return allow_partial_; } + + private: + typedef std::map<std::string, FakeURLLoaderEntity> EntityMap; + typedef std::map<std::string, int> ErrorMap; + EntityMap entity_map_; + ErrorMap error_map_; + size_t max_read_size_; + bool send_content_length_; + bool allow_partial_; +}; + +class FakeURLLoaderInterface : public nacl_io::URLLoaderInterface { + public: + explicit FakeURLLoaderInterface(FakeCoreInterface* core_interface); + + virtual PP_Resource Create(PP_Instance instance); + virtual int32_t Open(PP_Resource loader, + PP_Resource request_info, + PP_CompletionCallback callback); + virtual PP_Resource GetResponseInfo(PP_Resource loader); + virtual int32_t ReadResponseBody(PP_Resource loader, + void* buffer, + int32_t bytes_to_read, + PP_CompletionCallback callback); + virtual void Close(PP_Resource loader); + + private: + FakeCoreInterface* core_interface_; // Weak reference. + + DISALLOW_COPY_AND_ASSIGN(FakeURLLoaderInterface); +}; + +class FakeURLRequestInfoInterface : public nacl_io::URLRequestInfoInterface { + public: + FakeURLRequestInfoInterface(FakeCoreInterface* core_interface, + FakeVarInterface* var_interface); + + virtual PP_Resource Create(PP_Instance instance); + virtual PP_Bool SetProperty(PP_Resource request, + PP_URLRequestProperty property, + PP_Var value); + + private: + FakeCoreInterface* core_interface_; // Weak reference. + FakeVarInterface* var_interface_; // Weak reference. + + DISALLOW_COPY_AND_ASSIGN(FakeURLRequestInfoInterface); +}; + +class FakeURLResponseInfoInterface : public nacl_io::URLResponseInfoInterface { + public: + FakeURLResponseInfoInterface(FakeCoreInterface* core_interface, + FakeVarInterface* var_interface); + + virtual PP_Var GetProperty(PP_Resource response, + PP_URLResponseProperty property); + + private: + FakeCoreInterface* core_interface_; // Weak reference. + FakeVarInterface* var_interface_; // Weak reference. + + DISALLOW_COPY_AND_ASSIGN(FakeURLResponseInfoInterface); +}; + +class FakePepperInterfaceURLLoader : public nacl_io::PepperInterfaceDummy { + public: + FakePepperInterfaceURLLoader(); + FakePepperInterfaceURLLoader(const FakeURLLoaderServer& server); + ~FakePepperInterfaceURLLoader(); + + virtual PP_Instance GetInstance() { return instance_; } + virtual nacl_io::CoreInterface* GetCoreInterface(); + virtual nacl_io::VarInterface* GetVarInterface(); + virtual nacl_io::URLLoaderInterface* GetURLLoaderInterface(); + virtual nacl_io::URLRequestInfoInterface* GetURLRequestInfoInterface(); + virtual nacl_io::URLResponseInfoInterface* GetURLResponseInfoInterface(); + + FakeURLLoaderServer* server_template() { return &server_template_; } + + private: + void Init(); + + FakeCoreInterface core_interface_; + FakeVarInterface var_interface_; + FakeURLLoaderServer server_template_; + FakeURLLoaderInterface url_loader_interface_; + FakeURLRequestInfoInterface url_request_info_interface_; + FakeURLResponseInfoInterface url_response_info_interface_; + PP_Instance instance_; + + DISALLOW_COPY_AND_ASSIGN(FakePepperInterfaceURLLoader); +}; + +#endif // LIBRARIES_NACL_IO_TEST_FAKE_PEPPER_INTERFACE_URL_LOADER_H_ diff --git a/native_client_sdk/src/tests/nacl_io_test/fake_resource_manager.cc b/native_client_sdk/src/tests/nacl_io_test/fake_resource_manager.cc index b87248f..ccb2993 100644 --- a/native_client_sdk/src/tests/nacl_io_test/fake_resource_manager.cc +++ b/native_client_sdk/src/tests/nacl_io_test/fake_resource_manager.cc @@ -57,7 +57,7 @@ void FakeResourceManager::AddRef(PP_Resource handle) { } void FakeResourceManager::Release(PP_Resource handle) { - AUTO_LOCK(lock_); + sdk_util::AutoLock lock(lock_); ResourceMap::iterator iter = resource_map_.find(handle); ASSERT_NE(resource_map_.end(), iter) << "Releasing unknown resource " << handle; @@ -70,6 +70,18 @@ void FakeResourceManager::Release(PP_Resource handle) { << resource_tracker->file() << ":" << resource_tracker->line(); resource_tracker->Release(); + // It's OK to access the tracker when its refcount is zero; it doesn't + // actually destroy the object until the manager is destroyed. + if (resource_tracker->ref_count() == 0) { + // Remove the resource from this tracker. + FakeResource* resource = resource_tracker->Pass(); + // Release the lock before we call Destroy; resources can call + // FakeResourceManager::Release(), which will deadlock if we are already + // holding the lock. + lock.Unlock(); + + resource->Destroy(); + } } FakeResourceTracker* FakeResourceManager::Get(PP_Resource handle) { diff --git a/native_client_sdk/src/tests/nacl_io_test/fake_resource_manager.h b/native_client_sdk/src/tests/nacl_io_test/fake_resource_manager.h index f545e91..33c4444 100644 --- a/native_client_sdk/src/tests/nacl_io_test/fake_resource_manager.h +++ b/native_client_sdk/src/tests/nacl_io_test/fake_resource_manager.h @@ -55,6 +55,14 @@ class FakeResourceTracker { void Release() { sdk_util::AtomicAddFetch(&ref_count_, -1); } int32_t ref_count() const { return ref_count_; } + // Give up ownership of this resource. It is the responsibility of the caller + // to delete this FakeResource. + FakeResource* Pass() { + FakeResource* result = resource_; + resource_ = NULL; + return result; + } + template <typename T> T* resource() { if (!CheckType(T::classname())) @@ -63,6 +71,8 @@ class FakeResourceTracker { return static_cast<T*>(resource_); } + FakeResource* resource() { return resource_; } + const char* classname() const { return classname_; } const char* file() const { return file_; } int line() const { return line_; } @@ -82,7 +92,11 @@ class FakeResourceTracker { class FakeResource { public: FakeResource() {} + // Called when the resource is destroyed. For debugging purposes, this does + // not happen until the resource manager is destroyed. virtual ~FakeResource() {} + // Called when the last reference to the resource is released. + virtual void Destroy() {} private: DISALLOW_COPY_AND_ASSIGN(FakeResource); @@ -94,6 +108,6 @@ inline T* FakeResourceManager::Get(PP_Resource handle) { } #define CREATE_RESOURCE(MANAGER, TYPE, RESOURCE) \ - (MANAGER)->Create(RESOURCE, #TYPE, __FILE__, __LINE__) + (MANAGER)->Create((RESOURCE), #TYPE, __FILE__, __LINE__) #endif // LIBRARIES_NACL_IO_TEST_FAKE_RESOURCE_MANAGER_H_ diff --git a/native_client_sdk/src/tests/nacl_io_test/mount_http_test.cc b/native_client_sdk/src/tests/nacl_io_test/mount_http_test.cc index 8ec27db..79a2fcb 100644 --- a/native_client_sdk/src/tests/nacl_io_test/mount_http_test.cc +++ b/native_client_sdk/src/tests/nacl_io_test/mount_http_test.cc @@ -10,36 +10,26 @@ #include <sys/stat.h> #include <sys/types.h> -#include "mock_util.h" +#include "fake_pepper_interface_url_loader.h" + #include "nacl_io/kernel_handle.h" #include "nacl_io/kernel_intercept.h" #include "nacl_io/mount_http.h" #include "nacl_io/mount_node_dir.h" #include "nacl_io/osdirent.h" #include "nacl_io/osunistd.h" -#include "pepper_interface_mock.h" using namespace nacl_io; -using ::testing::_; -using ::testing::DoAll; -using ::testing::Mock; -using ::testing::Return; -using ::testing::SetArgPointee; -using ::testing::StrEq; +namespace { -class MountHttpMock : public MountHttp { +class MountHttpForTesting : public MountHttp { public: - MountHttpMock(StringMap_t map, PepperInterfaceMock* ppapi) { + MountHttpForTesting(StringMap_t map, PepperInterface* ppapi) { EXPECT_EQ(0, Init(1, map, ppapi)); } - ~MountHttpMock() { - Destroy(); - } - - NodeMap_t& GetMap() { return node_cache_; } - + using MountHttp::GetNodeCacheForTesting; using MountHttp::ParseManifest; using MountHttp::FindOrCreateDir; }; @@ -47,103 +37,186 @@ class MountHttpMock : public MountHttp { class MountHttpTest : public ::testing::Test { public: MountHttpTest(); - ~MountHttpTest(); protected: - PepperInterfaceMock ppapi_; - MountHttpMock* mnt_; + FakePepperInterfaceURLLoader ppapi_; + MountHttpForTesting mnt_; +}; + +MountHttpTest::MountHttpTest() : mnt_(StringMap_t(), &ppapi_) {} + +StringMap_t StringMap_NoCache() { + StringMap_t smap; + smap["cache_content"] = "false"; + return smap; +} + +class MountHttpNoCacheTest : public ::testing::Test { + public: + MountHttpNoCacheTest(); - static const PP_Instance instance_ = 123; + protected: + FakePepperInterfaceURLLoader ppapi_; + MountHttpForTesting mnt_; }; -MountHttpTest::MountHttpTest() - : ppapi_(instance_), - mnt_(NULL) { +MountHttpNoCacheTest::MountHttpNoCacheTest() : + mnt_(StringMap_NoCache(), &ppapi_) {} + +} // namespace + + +TEST_F(MountHttpTest, Access) { + ASSERT_TRUE(ppapi_.server_template()->AddEntity("foo", "", NULL)); + + ASSERT_EQ(0, mnt_.Access(Path("/foo"), R_OK)); + ASSERT_EQ(EACCES, mnt_.Access(Path("/foo"), W_OK)); + ASSERT_EQ(EACCES, mnt_.Access(Path("/foo"), X_OK)); + ASSERT_EQ(ENOENT, mnt_.Access(Path("/bar"), F_OK)); +} + +TEST_F(MountHttpTest, Read) { + const char contents[] = "contents"; + ASSERT_TRUE(ppapi_.server_template()->AddEntity("file", contents, NULL)); + + ScopedMountNode node; + ASSERT_EQ(0, mnt_.Open(Path("/file"), O_RDONLY, &node)); + + char buffer[10] = {0}; + int bytes_read = 0; + HandleAttr attr; + EXPECT_EQ(0, node->Read(attr, &buffer[0], sizeof(buffer), &bytes_read)); + EXPECT_EQ(strlen(contents), bytes_read); + EXPECT_STREQ(contents, buffer); + + // Read nothing past the end of the file. + attr.offs = 100; + EXPECT_EQ(0, node->Read(attr, &buffer[0], sizeof(buffer), &bytes_read)); + EXPECT_EQ(0, bytes_read); + + // Read part of the data. + attr.offs = 4; + EXPECT_EQ(0, node->Read(attr, &buffer[0], sizeof(buffer), &bytes_read)); + ASSERT_EQ(strlen(contents) - 4, bytes_read); + buffer[bytes_read] = 0; + EXPECT_STREQ("ents", buffer); +} + +TEST_F(MountHttpTest, Write) { + const char contents[] = "contents"; + ASSERT_TRUE(ppapi_.server_template()->AddEntity("file", contents, NULL)); + + ScopedMountNode node; + ASSERT_EQ(0, mnt_.Open(Path("/file"), O_WRONLY, &node)); + + // Writing always fails. + HandleAttr attr; + attr.offs = 3; + int bytes_written = 1; // Set to a non-zero value. + EXPECT_EQ(EACCES, node->Write(attr, "struct", 6, &bytes_written)); + EXPECT_EQ(0, bytes_written); } -MountHttpTest::~MountHttpTest() { - delete mnt_; +TEST_F(MountHttpTest, GetStat) { + const char contents[] = "contents"; + ASSERT_TRUE(ppapi_.server_template()->AddEntity("file", contents, NULL)); + + ScopedMountNode node; + ASSERT_EQ(0, mnt_.Open(Path("/file"), O_RDONLY, &node)); + + struct stat statbuf; + EXPECT_EQ(0, node->GetStat(&statbuf)); + EXPECT_EQ(S_IFREG | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH, + statbuf.st_mode); + EXPECT_EQ(strlen(contents), statbuf.st_size); + // These are not currently set. + EXPECT_EQ(0, statbuf.st_atime); + EXPECT_EQ(0, statbuf.st_ctime); + EXPECT_EQ(0, statbuf.st_mtime); } +TEST_F(MountHttpTest, FTruncate) { + const char contents[] = "contents"; + ASSERT_TRUE(ppapi_.server_template()->AddEntity("file", contents, NULL)); -TEST_F(MountHttpTest, MountEmpty) { - StringMap_t args; - mnt_ = new MountHttpMock(args, &ppapi_); + ScopedMountNode node; + ASSERT_EQ(0, mnt_.Open(Path("/file"), O_RDWR, &node)); + EXPECT_EQ(EACCES, node->FTruncate(4)); } -TEST_F(MountHttpTest, Mkdir) { +TEST(MountHttpDirTest, Mkdir) { StringMap_t args; - mnt_ = new MountHttpMock(args, &ppapi_); + MountHttpForTesting mnt(args, NULL); char manifest[] = "-r-- 123 /mydir/foo\n-rw- 234 /thatdir/bar\n"; - EXPECT_EQ(0, mnt_->ParseManifest(manifest)); + ASSERT_EQ(0, mnt.ParseManifest(manifest)); // mkdir of existing directories should give "File exists". - EXPECT_EQ(EEXIST, mnt_->Mkdir(Path("/"), 0)); - EXPECT_EQ(EEXIST, mnt_->Mkdir(Path("/mydir"), 0)); + EXPECT_EQ(EEXIST, mnt.Mkdir(Path("/"), 0)); + EXPECT_EQ(EEXIST, mnt.Mkdir(Path("/mydir"), 0)); // mkdir of non-existent directories should give "Permission denied". - EXPECT_EQ(EACCES, mnt_->Mkdir(Path("/non_existent"), 0)); + EXPECT_EQ(EACCES, mnt.Mkdir(Path("/non_existent"), 0)); } -TEST_F(MountHttpTest, Rmdir) { +TEST(MountHttpDirTest, Rmdir) { StringMap_t args; - mnt_ = new MountHttpMock(args, &ppapi_); + MountHttpForTesting mnt(args, NULL); char manifest[] = "-r-- 123 /mydir/foo\n-rw- 234 /thatdir/bar\n"; - EXPECT_EQ(0, mnt_->ParseManifest(manifest)); + ASSERT_EQ(0, mnt.ParseManifest(manifest)); // Rmdir on existing dirs should give "Permission Denied" - EXPECT_EQ(EACCES, mnt_->Rmdir(Path("/"))); - EXPECT_EQ(EACCES, mnt_->Rmdir(Path("/mydir"))); + EXPECT_EQ(EACCES, mnt.Rmdir(Path("/"))); + EXPECT_EQ(EACCES, mnt.Rmdir(Path("/mydir"))); // Rmdir on existing files should give "Not a direcotory" - EXPECT_EQ(ENOTDIR, mnt_->Rmdir(Path("/mydir/foo"))); + EXPECT_EQ(ENOTDIR, mnt.Rmdir(Path("/mydir/foo"))); // Rmdir on non-existent files should give "No such file or directory" - EXPECT_EQ(ENOENT, mnt_->Rmdir(Path("/non_existent"))); + EXPECT_EQ(ENOENT, mnt.Rmdir(Path("/non_existent"))); } -TEST_F(MountHttpTest, Unlink) { +TEST(MountHttpDirTest, Unlink) { StringMap_t args; - mnt_ = new MountHttpMock(args, &ppapi_); + MountHttpForTesting mnt(args, NULL); char manifest[] = "-r-- 123 /mydir/foo\n-rw- 234 /thatdir/bar\n"; - EXPECT_EQ(0, mnt_->ParseManifest(manifest)); + ASSERT_EQ(0, mnt.ParseManifest(manifest)); // Unlink of existing files should give "Permission Denied" - EXPECT_EQ(EACCES, mnt_->Unlink(Path("/mydir/foo"))); + EXPECT_EQ(EACCES, mnt.Unlink(Path("/mydir/foo"))); // Unlink of existing directory should give "Is a directory" - EXPECT_EQ(EISDIR, mnt_->Unlink(Path("/mydir"))); + EXPECT_EQ(EISDIR, mnt.Unlink(Path("/mydir"))); // Unlink of non-existent files should give "No such file or directory" - EXPECT_EQ(ENOENT, mnt_->Unlink(Path("/non_existent"))); + EXPECT_EQ(ENOENT, mnt.Unlink(Path("/non_existent"))); } -TEST_F(MountHttpTest, Remove) { +TEST(MountHttpDirTest, Remove) { StringMap_t args; - mnt_ = new MountHttpMock(args, &ppapi_); + MountHttpForTesting mnt(args, NULL); char manifest[] = "-r-- 123 /mydir/foo\n-rw- 234 /thatdir/bar\n"; - EXPECT_EQ(0, mnt_->ParseManifest(manifest)); + ASSERT_EQ(0, mnt.ParseManifest(manifest)); // Remove of existing files should give "Permission Denied" - EXPECT_EQ(EACCES, mnt_->Remove(Path("/mydir/foo"))); + EXPECT_EQ(EACCES, mnt.Remove(Path("/mydir/foo"))); // Remove of existing directory should give "Permission Denied" - EXPECT_EQ(EACCES, mnt_->Remove(Path("/mydir"))); + EXPECT_EQ(EACCES, mnt.Remove(Path("/mydir"))); // Unlink of non-existent files should give "No such file or directory" - EXPECT_EQ(ENOENT, mnt_->Remove(Path("/non_existent"))); + EXPECT_EQ(ENOENT, mnt.Remove(Path("/non_existent"))); } -TEST_F(MountHttpTest, ParseManifest) { +TEST(MountHttpDirTest, ParseManifest) { StringMap_t args; size_t result_size = 0; - mnt_ = new MountHttpMock(args, &ppapi_); + MountHttpForTesting mnt(args, NULL); // Multiple consecutive newlines or spaces should be ignored. char manifest[] = "-r-- 123 /mydir/foo\n\n-rw- 234 /thatdir/bar\n"; - EXPECT_EQ(0, mnt_->ParseManifest(manifest)); + ASSERT_EQ(0, mnt.ParseManifest(manifest)); ScopedMountNode root; - EXPECT_EQ(0, mnt_->FindOrCreateDir(Path("/"), &root)); + EXPECT_EQ(0, mnt.FindOrCreateDir(Path("/"), &root)); ASSERT_NE((MountNode*)NULL, root.get()); EXPECT_EQ(2, root->ChildCount()); ScopedMountNode dir; - EXPECT_EQ(0, mnt_->FindOrCreateDir(Path("/mydir"), &dir)); + EXPECT_EQ(0, mnt.FindOrCreateDir(Path("/mydir"), &dir)); ASSERT_NE((MountNode*)NULL, dir.get()); EXPECT_EQ(1, dir->ChildCount()); - MountNode* node = mnt_->GetMap()["/mydir/foo"].get(); + MountNode* node = (*mnt.GetNodeCacheForTesting())["/mydir/foo"].get(); EXPECT_NE((MountNode*)NULL, node); EXPECT_EQ(0, node->GetSize(&result_size)); EXPECT_EQ(123, result_size); @@ -151,10 +224,10 @@ TEST_F(MountHttpTest, ParseManifest) { // Since these files are cached thanks to the manifest, we can open them // without accessing the PPAPI URL API. ScopedMountNode foo; - EXPECT_EQ(0, mnt_->Open(Path("/mydir/foo"), O_RDONLY, &foo)); + ASSERT_EQ(0, mnt.Open(Path("/mydir/foo"), O_RDONLY, &foo)); ScopedMountNode bar; - EXPECT_EQ(0, mnt_->Open(Path("/thatdir/bar"), O_RDWR, &bar)); + ASSERT_EQ(0, mnt.Open(Path("/thatdir/bar"), O_RDWR, &bar)); struct stat sfoo; struct stat sbar; @@ -169,459 +242,88 @@ TEST_F(MountHttpTest, ParseManifest) { EXPECT_EQ(S_IFREG | S_IRALL | S_IWALL, sbar.st_mode); } +TEST_F(MountHttpNoCacheTest, OpenAndClose) { + const char contents[] = "contents"; + ASSERT_TRUE(ppapi_.server_template()->AddEntity("file", contents, NULL)); -class MountHttpNodeTest : public MountHttpTest { - public: - MountHttpNodeTest(); - virtual void TearDown(); - - void SetMountArgs(const StringMap_t& args); - void ExpectOpen(const char* method); - void ExpectHeaders(const char* headers); - void OpenNode(); - void SetResponse(int status_code, const char* headers); - // Set a response code, but expect the request to fail. Certain function calls - // expected by SetResponse are not expected here. - void SetResponseExpectFail(int status_code, const char* headers); - void SetResponseBody(const char* body); - void ResetMocks(); - - protected: - MountHttpMock* mnt_; - ScopedMountNode node_; - - CoreInterfaceMock* core_; - VarInterfaceMock* var_; - URLLoaderInterfaceMock* loader_; - URLRequestInfoInterfaceMock* request_; - URLResponseInfoInterfaceMock* response_; - size_t response_body_offset_; - - static const char path_[]; - static const char rel_path_[]; - static const PP_Resource loader_resource_ = 235; - static const PP_Resource request_resource_ = 236; - static const PP_Resource response_resource_ = 237; -}; - -// static -const char MountHttpNodeTest::path_[] = "/foo"; -// static -const char MountHttpNodeTest::rel_path_[] = "foo"; - -MountHttpNodeTest::MountHttpNodeTest() - : mnt_(NULL), - node_(NULL) { -} - -static PP_Var MakeString(PP_Resource resource) { - PP_Var result = { PP_VARTYPE_STRING, 0, {PP_FALSE} }; - result.value.as_id = resource; - return result; -} - -void MountHttpNodeTest::SetMountArgs(const StringMap_t& args) { - mnt_ = new MountHttpMock(args, &ppapi_); -} - -void MountHttpNodeTest::ExpectOpen(const char* method) { - core_ = ppapi_.GetCoreInterface(); - loader_ = ppapi_.GetURLLoaderInterface(); - request_ = ppapi_.GetURLRequestInfoInterface(); - response_ = ppapi_.GetURLResponseInfoInterface(); - var_ = ppapi_.GetVarInterface(); - - ON_CALL(*request_, SetProperty(request_resource_, _, _)) - .WillByDefault(Return(PP_TRUE)); - ON_CALL(*var_, VarFromUtf8(_, _)).WillByDefault(Return(PP_MakeUndefined())); - - EXPECT_CALL(*loader_, Create(instance_)).WillOnce(Return(loader_resource_)); - EXPECT_CALL(*request_, Create(instance_)).WillOnce(Return(request_resource_)); - - PP_Var var_head = MakeString(345); - PP_Var var_url = MakeString(346); - EXPECT_CALL(*var_, VarFromUtf8(StrEq(method), _)).WillOnce(Return(var_head)); - EXPECT_CALL(*var_, VarFromUtf8(StrEq(rel_path_), _)) - .WillOnce(Return(var_url)); - -#define EXPECT_SET_PROPERTY(NAME, VAR) \ - EXPECT_CALL(*request_, SetProperty(request_resource_, NAME, VAR)) - - EXPECT_SET_PROPERTY(PP_URLREQUESTPROPERTY_URL, IsEqualToVar(var_url)); - EXPECT_SET_PROPERTY(PP_URLREQUESTPROPERTY_METHOD, IsEqualToVar(var_head)); - EXPECT_SET_PROPERTY(PP_URLREQUESTPROPERTY_ALLOWCROSSORIGINREQUESTS, _); - EXPECT_SET_PROPERTY(PP_URLREQUESTPROPERTY_ALLOWCREDENTIALS, _); - -#undef EXPECT_SET_PROPERTY - - EXPECT_CALL(*loader_, Open(loader_resource_, request_resource_, _)) - .WillOnce(DoAll(CallCallback<2>(int32_t(PP_OK)), - Return(int32_t(PP_OK_COMPLETIONPENDING)))); - EXPECT_CALL(*loader_, GetResponseInfo(loader_resource_)) - .WillOnce(Return(response_resource_)); - - EXPECT_CALL(*core_, ReleaseResource(loader_resource_)); - EXPECT_CALL(*core_, ReleaseResource(request_resource_)); - EXPECT_CALL(*core_, ReleaseResource(response_resource_)); -} - -void MountHttpNodeTest::ExpectHeaders(const char* headers) { - PP_Var var_headers = MakeString(347); - var_ = ppapi_.GetVarInterface(); - EXPECT_CALL(*var_, VarFromUtf8(StrEq(headers), _)) - .WillOnce(Return(var_headers)); - - EXPECT_CALL(*request_, SetProperty(request_resource_, - PP_URLREQUESTPROPERTY_HEADERS, - IsEqualToVar(var_headers))).Times(1); + ScopedMountNode node; + ASSERT_EQ(0, mnt_.Open(Path("/file"), O_RDONLY, &node)); } -void MountHttpNodeTest::SetResponse(int status_code, const char* headers) { - ON_CALL(*response_, GetProperty(response_resource_, _)) - .WillByDefault(Return(PP_MakeUndefined())); - - PP_Var var_headers = MakeString(348); - EXPECT_CALL(*response_, - GetProperty(response_resource_, - PP_URLRESPONSEPROPERTY_STATUSCODE)) - .WillOnce(Return(PP_MakeInt32(status_code))); - EXPECT_CALL(*response_, - GetProperty(response_resource_, PP_URLRESPONSEPROPERTY_HEADERS)) - .WillOnce(Return(var_headers)); - EXPECT_CALL(*var_, VarToUtf8(IsEqualToVar(var_headers), _)) - .WillOnce(DoAll(SetArgPointee<1>(strlen(headers)), - Return(headers))); -} - -void MountHttpNodeTest::SetResponseExpectFail(int status_code, - const char* headers) { - ON_CALL(*response_, GetProperty(response_resource_, _)) - .WillByDefault(Return(PP_MakeUndefined())); - - EXPECT_CALL(*response_, - GetProperty(response_resource_, - PP_URLRESPONSEPROPERTY_STATUSCODE)) - .WillOnce(Return(PP_MakeInt32(status_code))); +TEST_F(MountHttpNoCacheTest, OpenAndCloseNotFound) { + ScopedMountNode node; + ASSERT_EQ(ENOENT, mnt_.Open(Path("/file"), O_RDONLY, &node)); } -ACTION_P3(ReadResponseBodyAction, offset, body, body_length) { - char* buf = static_cast<char*>(arg1); - size_t read_length = arg2; - PP_CompletionCallback callback = arg3; - if (*offset >= body_length) - return 0; - - read_length = std::min(read_length, body_length - *offset); - memcpy(buf, body + *offset, read_length); - *offset += read_length; - - // Also call the callback. - if (callback.func) - (*callback.func)(callback.user_data, PP_OK); - - return read_length; -} +TEST_F(MountHttpNoCacheTest, OpenAndCloseServerError) { + ASSERT_TRUE(ppapi_.server_template()->AddError("file", 500)); -void MountHttpNodeTest::SetResponseBody(const char* body) { - response_body_offset_ = 0; - EXPECT_CALL(*loader_, ReadResponseBody(loader_resource_, _, _, _)) - .WillRepeatedly(ReadResponseBodyAction( - &response_body_offset_, body, strlen(body))); -} - -void MountHttpNodeTest::OpenNode() { - ASSERT_EQ(0, mnt_->Open(Path(path_), O_RDONLY, &node_)); - ASSERT_NE((MountNode*)NULL, node_.get()); -} - -void MountHttpNodeTest::ResetMocks() { - Mock::VerifyAndClearExpectations(&ppapi_); - Mock::VerifyAndClearExpectations(loader_); - Mock::VerifyAndClearExpectations(request_); - Mock::VerifyAndClearExpectations(response_); - Mock::VerifyAndClearExpectations(var_); -} - -void MountHttpNodeTest::TearDown() { - node_.reset(); - delete mnt_; -} - -// TODO(binji): These tests are all broken now. In another CL, I'll reimplement -// these tests using an HTTP fake. -TEST_F(MountHttpNodeTest, DISABLED_OpenAndCloseNoCache) { - StringMap_t smap; - smap["cache_content"] = "false"; - SetMountArgs(StringMap_t()); - ExpectOpen("HEAD"); - ExpectHeaders(""); - SetResponse(200, ""); - OpenNode(); -} - -TEST_F(MountHttpNodeTest, DISABLED_OpenAndCloseNotFound) { - StringMap_t smap; - smap["cache_content"] = "false"; - SetMountArgs(StringMap_t()); - ExpectOpen("HEAD"); - ExpectHeaders(""); - SetResponseExpectFail(404, ""); - ASSERT_EQ(ENOENT, mnt_->Open(Path(path_), O_RDONLY, &node_)); -} - -TEST_F(MountHttpNodeTest, DISABLED_OpenAndCloseServerError) { - StringMap_t smap; - smap["cache_content"] = "false"; - SetMountArgs(StringMap_t()); - ExpectOpen("HEAD"); - ExpectHeaders(""); - SetResponseExpectFail(500, ""); - ASSERT_EQ(EIO, mnt_->Open(Path(path_), O_RDONLY, &node_)); + ScopedMountNode node; + ASSERT_EQ(ENOENT, mnt_.Open(Path("/file"), O_RDONLY, &node)); } -TEST_F(MountHttpNodeTest, DISABLED_GetStat) { - StringMap_t smap; - smap["cache_content"] = "false"; - SetMountArgs(StringMap_t()); - ExpectOpen("HEAD"); - ExpectHeaders(""); - SetResponse(200, "Content-Length: 42\n"); - OpenNode(); - - struct stat stat; - EXPECT_EQ(0, node_->GetStat(&stat)); - EXPECT_EQ(42, stat.st_size); -} +TEST_F(MountHttpNoCacheTest, GetSize) { + const char contents[] = "contents"; + ASSERT_TRUE(ppapi_.server_template()->AddEntity("file", contents, NULL)); + ppapi_.server_template()->set_send_content_length(true); -TEST_F(MountHttpNodeTest, DISABLED_Access) { - StringMap_t smap; - smap["cache_content"] = "false"; - SetMountArgs(StringMap_t()); - ExpectOpen("HEAD"); - ExpectHeaders(""); - SetResponse(200, ""); - ASSERT_EQ(0, mnt_->Access(Path(path_), R_OK)); + ScopedMountNode node; + struct stat statbuf; + ASSERT_EQ(0, mnt_.Open(Path("/file"), O_RDONLY, &node)); + EXPECT_EQ(0, node->GetStat(&statbuf)); + EXPECT_EQ(strlen(contents), statbuf.st_size); } -TEST_F(MountHttpNodeTest, DISABLED_AccessWrite) { - StringMap_t smap; - smap["cache_content"] = "false"; - SetMountArgs(StringMap_t()); - ExpectOpen("HEAD"); - ExpectHeaders(""); - SetResponse(200, ""); - ASSERT_EQ(EACCES, mnt_->Access(Path(path_), W_OK)); -} +TEST_F(MountHttpNoCacheTest, Access) { + ASSERT_TRUE(ppapi_.server_template()->AddEntity("file", "", NULL)); -TEST_F(MountHttpNodeTest, DISABLED_AccessNotFound) { - StringMap_t smap; - smap["cache_content"] = "false"; - SetMountArgs(StringMap_t()); - ExpectOpen("HEAD"); - ExpectHeaders(""); - SetResponseExpectFail(404, ""); - ASSERT_EQ(ENOENT, mnt_->Access(Path(path_), R_OK)); + ASSERT_EQ(0, mnt_.Access(Path("/file"), R_OK)); + ASSERT_EQ(EACCES, mnt_.Access(Path("/file"), W_OK)); + ASSERT_EQ(ENOENT, mnt_.Access(Path("/bar"), R_OK)); } -TEST_F(MountHttpNodeTest, DISABLED_ReadCached) { - size_t result_size = 0; - int result_bytes = 0; - - SetMountArgs(StringMap_t()); - ExpectOpen("HEAD"); - ExpectHeaders(""); - SetResponse(200, "Content-Length: 42\n"); - OpenNode(); - ResetMocks(); - - EXPECT_EQ(0, node_->GetSize(&result_size)); - EXPECT_EQ(42, result_size); - - char buf[10]; - memset(&buf[0], 0, sizeof(buf)); - - ExpectOpen("GET"); - ExpectHeaders(""); - SetResponse(200, "Content-Length: 42\n"); - SetResponseBody("Here is some response text. And some more."); - HandleAttr attr; - EXPECT_EQ(0, node_->Read(attr, buf, sizeof(buf) - 1, &result_bytes)); - EXPECT_STREQ("Here is s", &buf[0]); - ResetMocks(); +TEST_F(MountHttpNoCacheTest, ReadPartial) { + const char contents[] = "0123456789abcdefghi"; + ASSERT_TRUE(ppapi_.server_template()->AddEntity("file", contents, NULL)); + ppapi_.server_template()->set_allow_partial(true); - // Further reads should be cached. - EXPECT_EQ(0, node_->Read(attr, buf, sizeof(buf) - 1, &result_bytes)); - EXPECT_STREQ("Here is s", &buf[0]); - attr.offs = 10; - EXPECT_EQ(0, node_->Read(attr, buf, sizeof(buf) - 1, &result_bytes)); - EXPECT_STREQ("me respon", &buf[0]); - - EXPECT_EQ(0, node_->GetSize(&result_size)); - EXPECT_EQ(42, result_size); -} - -TEST_F(MountHttpNodeTest, DISABLED_ReadCachedNoContentLength) { - size_t result_size = 0; int result_bytes = 0; - SetMountArgs(StringMap_t()); - ExpectOpen("HEAD"); - ExpectHeaders(""); - SetResponse(200, ""); - OpenNode(); - ResetMocks(); - - ExpectOpen("GET"); - ExpectHeaders(""); - SetResponse(200, ""); // No Content-Length response here. - SetResponseBody("Here is some response text. And some more."); - - // GetSize will Read() because it didn't get the content length from the HEAD - // request. - EXPECT_EQ(0, node_->GetSize(&result_size)); - EXPECT_EQ(42, result_size); - char buf[10]; memset(&buf[0], 0, sizeof(buf)); + ScopedMountNode node; + ASSERT_EQ(0, mnt_.Open(Path("/file"), O_RDONLY, &node)); HandleAttr attr; - EXPECT_EQ(0, node_->Read(attr, buf, sizeof(buf) - 1, &result_bytes)); - EXPECT_STREQ("Here is s", &buf[0]); - ResetMocks(); + EXPECT_EQ(0, node->Read(attr, buf, sizeof(buf) - 1, &result_bytes)); + EXPECT_EQ(sizeof(buf) - 1, result_bytes); + EXPECT_STREQ("012345678", &buf[0]); - // Further reads should be cached. - EXPECT_EQ(0, node_->Read(attr, buf, sizeof(buf) - 1, &result_bytes)); - EXPECT_STREQ("Here is s", &buf[0]); attr.offs = 10; - EXPECT_EQ(0, node_->Read(attr, buf, sizeof(buf) - 1, &result_bytes)); - EXPECT_STREQ("me respon", &buf[0]); - - EXPECT_EQ(0, node_->GetSize(&result_size)); - EXPECT_EQ(42, result_size); -} - -TEST_F(MountHttpNodeTest, DISABLED_ReadCachedUnderrun) { - size_t result_size = 0; - int result_bytes = 0; - - SetMountArgs(StringMap_t()); - ExpectOpen("HEAD"); - ExpectHeaders(""); - SetResponse(200, "Content-Length: 100\n"); - OpenNode(); - ResetMocks(); - - EXPECT_EQ(0, node_->GetSize(&result_size)); - EXPECT_EQ(100, result_size); - - char buf[10]; - memset(&buf[0], 0, sizeof(buf)); - - ExpectOpen("GET"); - ExpectHeaders(""); - SetResponse(200, "Content-Length: 100\n"); - SetResponseBody("abcdefghijklmnopqrstuvwxyz"); - HandleAttr attr; - EXPECT_EQ(0, node_->Read(attr, buf, sizeof(buf) - 1, &result_bytes)); + EXPECT_EQ(0, node->Read(attr, buf, sizeof(buf) - 1, &result_bytes)); EXPECT_EQ(sizeof(buf) - 1, result_bytes); EXPECT_STREQ("abcdefghi", &buf[0]); - ResetMocks(); - - EXPECT_EQ(0, node_->GetSize(&result_size)); - EXPECT_EQ(26, result_size); } -TEST_F(MountHttpNodeTest, DISABLED_ReadCachedOverrun) { - size_t result_size = 0; - int result_bytes = 0; - - SetMountArgs(StringMap_t()); - ExpectOpen("HEAD"); - ExpectHeaders(""); - SetResponse(200, "Content-Length: 15\n"); - OpenNode(); - ResetMocks(); - - EXPECT_EQ(0, node_->GetSize(&result_size)); - EXPECT_EQ(15, result_size); - - char buf[10]; - memset(&buf[0], 0, sizeof(buf)); +TEST_F(MountHttpNoCacheTest, ReadPartialNoServerSupport) { + const char contents[] = "0123456789abcdefghi"; + ASSERT_TRUE(ppapi_.server_template()->AddEntity("file", contents, NULL)); + ppapi_.server_template()->set_allow_partial(false); - ExpectOpen("GET"); - ExpectHeaders(""); - SetResponse(200, "Content-Length: 15\n"); - SetResponseBody("01234567890123456789"); - HandleAttr attr; - attr.offs = 10; - EXPECT_EQ(0, node_->Read(attr, buf, sizeof(buf) - 1, &result_bytes)); - EXPECT_EQ(5, result_bytes); - EXPECT_STREQ("01234", &buf[0]); - ResetMocks(); - - EXPECT_EQ(0, node_->GetSize(&result_size)); - EXPECT_EQ(15, result_size); -} - -TEST_F(MountHttpNodeTest, DISABLED_ReadPartial) { int result_bytes = 0; - StringMap_t args; - args["cache_content"] = "false"; - SetMountArgs(args); - ExpectOpen("HEAD"); - ExpectHeaders(""); - SetResponse(200, ""); - OpenNode(); - ResetMocks(); - char buf[10]; memset(&buf[0], 0, sizeof(buf)); - ExpectOpen("GET"); - ExpectHeaders("Range: bytes=0-8\n"); - SetResponse(206, "Content-Length: 9\nContent-Range: bytes=0-8\n"); - SetResponseBody("012345678"); + ScopedMountNode node; + ASSERT_EQ(0, mnt_.Open(Path("/file"), O_RDONLY, &node)); HandleAttr attr; - EXPECT_EQ(0, node_->Read(attr, buf, sizeof(buf) - 1, &result_bytes)); + EXPECT_EQ(0, node->Read(attr, buf, sizeof(buf) - 1, &result_bytes)); EXPECT_EQ(sizeof(buf) - 1, result_bytes); EXPECT_STREQ("012345678", &buf[0]); - ResetMocks(); - // Another read is another request. - ExpectOpen("GET"); - ExpectHeaders("Range: bytes=10-18\n"); - SetResponse(206, "Content-Length: 9\nContent-Range: bytes=10-18\n"); - SetResponseBody("abcdefghi"); attr.offs = 10; - EXPECT_EQ(0, node_->Read(attr, buf, sizeof(buf) - 1, &result_bytes)); + EXPECT_EQ(0, node->Read(attr, buf, sizeof(buf) - 1, &result_bytes)); EXPECT_EQ(sizeof(buf) - 1, result_bytes); EXPECT_STREQ("abcdefghi", &buf[0]); } - -TEST_F(MountHttpNodeTest, DISABLED_ReadPartialNoServerSupport) { - int result_bytes = 0; - - StringMap_t args; - args["cache_content"] = "false"; - SetMountArgs(args); - ExpectOpen("HEAD"); - ExpectHeaders(""); - SetResponse(200, ""); - OpenNode(); - ResetMocks(); - - char buf[10]; - memset(&buf[0], 0, sizeof(buf)); - - ExpectOpen("GET"); - ExpectHeaders("Range: bytes=10-18\n"); - SetResponse(200, "Content-Length: 20\n"); - SetResponseBody("0123456789abcdefghij"); - HandleAttr attr; - attr.offs = 10; - EXPECT_EQ(0, node_->Read(attr, buf, sizeof(buf) - 1, &result_bytes)); - EXPECT_EQ(sizeof(buf) - 1, result_bytes); - EXPECT_STREQ("abcdefghi", &buf[0]); -} - |