blob: 06716119519ed94896ccd519d542c22f8ca139bf [file] [log] [blame]
/**
* Copyright 2010 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// Author: abliss@google.com (Adam Bliss)
// Unit-test the resource manager
#include "base/scoped_ptr.h"
#include "net/instaweb/rewriter/public/output_resource.h"
#include "net/instaweb/rewriter/public/resource_namer.h"
#include "net/instaweb/rewriter/public/resource_manager.h"
#include "net/instaweb/rewriter/public/resource_manager_test_base.h"
#include "net/instaweb/rewriter/public/rewrite_filter.h"
#include "net/instaweb/rewriter/resource_manager_testing_peer.h"
#include "net/instaweb/util/public/content_type.h"
#include "net/instaweb/util/public/gtest.h"
#include "net/instaweb/util/public/meta_data.h"
#include "net/instaweb/util/public/simple_meta_data.h"
#include "net/instaweb/util/public/string_util.h"
#include "net/instaweb/util/public/string_writer.h"
namespace net_instaweb {
class VerifyContentsCallback : public Resource::AsyncCallback {
public:
explicit VerifyContentsCallback(const std::string& contents) :
contents_(contents), called_(false) {
}
virtual void Done(bool success, Resource* resource) {
EXPECT_EQ(0, contents_.compare(resource->contents().as_string()));
called_ = true;
}
void AssertCalled() {
EXPECT_TRUE(called_);
}
std::string contents_;
bool called_;
};
class ResourceManagerTest : public ResourceManagerTestBase {
protected:
ResourceManagerTest() { }
// Calls FetchOutputResource with different values of writer and
// response_headers, to test all branches. Expects the fetch to succeed all
// times, and finally returns the contents.
std::string FetchOutputResource(OutputResource* resource) {
EXPECT_TRUE(resource_manager_->FetchOutputResource(
resource, NULL, NULL, &message_handler_));
SimpleMetaData empty;
EXPECT_TRUE(resource_manager_->FetchOutputResource(
resource, NULL, &empty, &message_handler_));
std::string contents;
StringWriter writer(&contents);
EXPECT_TRUE(resource_manager_->FetchOutputResource(
resource, &writer, resource->metadata(),
&message_handler_));
return contents;
}
// Asserts that the given url starts with an appropriate prefix;
// then cuts off that prefix.
virtual void RemoveUrlPrefix(std::string* url) {
EXPECT_EQ(0, url->compare(0, url_prefix_.length(), url_prefix_));
url->erase(0, url_prefix_.length());
}
// Tests for the lifecycle and various flows of a named output resource.
void TestNamed() {
const char* filter_prefix = "fp";
const char* name = "name";
const char* contents = "contents";
const int64 origin_expire_time_ms = 1000;
const ContentType* content_type = &kContentTypeText;
// Create a NamedOutputResource and write it.
scoped_ptr<OutputResource> nor(
resource_manager_->CreateOutputResourceWithPath(
url_prefix_, filter_prefix, name, content_type, &message_handler_));
ASSERT_TRUE(nor.get() != NULL);
EXPECT_FALSE(nor->IsWritten());
EXPECT_FALSE(ResourceManagerTestingPeer::HasHash(nor.get()));
EXPECT_FALSE(ResourceManagerTestingPeer::Generated(nor.get()));
EXPECT_TRUE(resource_manager_->Write(HttpStatus::kOK, contents, nor.get(),
origin_expire_time_ms,
&message_handler_));
EXPECT_TRUE(nor->IsWritten());
// Retrieve the same NOR from the cache.
scoped_ptr<OutputResource> nor2(
resource_manager_->CreateOutputResourceWithPath(
url_prefix_, filter_prefix, name, &kContentTypeText,
&message_handler_));
ASSERT_TRUE(nor2.get() != NULL);
EXPECT_TRUE(ResourceManagerTestingPeer::HasHash(nor.get()));
EXPECT_FALSE(ResourceManagerTestingPeer::Generated(nor.get()));
EXPECT_TRUE(nor->IsWritten());
// Fetch its contents and make sure they match
std::string newContents;
EXPECT_EQ(contents, FetchOutputResource(nor2.get()));
// Try asynchronously too
VerifyContentsCallback callback(contents);
resource_manager_->ReadAsync(nor2.get(), &callback, &message_handler_);
callback.AssertCalled();
// Grab the URL for later
EXPECT_TRUE(nor2->HasValidUrl());
std::string url = nor2->url();
EXPECT_LT(0, url.length());
// Now expire it from the HTTP cache. Since we don't know its hash, we
// cannot fetch it (even though the contents are still in the filesystem).
mock_timer_.advance_ms(2 * origin_expire_time_ms);
scoped_ptr<OutputResource> nor3(
resource_manager_->CreateOutputResourceWithPath(
url_prefix_, filter_prefix, name, &kContentTypeText,
&message_handler_));
EXPECT_FALSE(resource_manager_->FetchOutputResource(
nor3.get(), NULL, NULL, &message_handler_));
// But with the URL (which contains the hash), we can retrieve it
// from the http_cache.
// first cut off the "http://mysite{,.0,.1}/" from the front.
RemoveUrlPrefix(&url);
ResourceNamer full_name;
ASSERT_TRUE(full_name.Decode(url));
EXPECT_EQ(content_type, NameExtensionToContentType(url));
EXPECT_EQ(filter_prefix, full_name.id());
EXPECT_EQ(name, full_name.name());
scoped_ptr<OutputResource> nor4(
resource_manager_->CreateOutputResourceForFetch(
nor->url(), &message_handler_));
EXPECT_EQ(nor->url(), nor4->url());
EXPECT_EQ(contents, FetchOutputResource(nor4.get()));
// If it's evicted from the http_cache, we can also retrieve it from the
// filesystem.
lru_cache_->Clear();
nor4.reset(
resource_manager_->CreateOutputResourceForFetch(
nor->url(), &message_handler_));
EXPECT_EQ(nor->url(), nor4->url());
EXPECT_EQ(contents, FetchOutputResource(nor4.get()));
// This also works asynchronously.
lru_cache_->Clear();
VerifyContentsCallback callback2(contents);
resource_manager_->ReadAsync(nor4.get(), &callback2, &message_handler_);
callback2.AssertCalled();
}
};
class ResourceManagerShardedTest : public ResourceManagerTest {
protected:
ResourceManagerShardedTest() {
url_prefix_ ="http://mysite.%d/";
num_shards_ = 2;
}
virtual void RemoveUrlPrefix(std::string* url) {
// One of these two should match.
StringPiece prefix(url->data(), 16);
EXPECT_TRUE((prefix == "http://mysite.0/") ||
(prefix == "http://mysite.1/")) << *url;
// "%d" -> "0" (or "1") loses 1 char.
url->erase(0, url_prefix_.length() - 1);
}
};
TEST_F(ResourceManagerTest, TestNamed) {
TestNamed();
}
// TODO(jmaessen): re-enable after sharding works again.
// TEST_F(ResourceManagerShardedTest, TestNamed) {
// TestNamed();
// }
} // namespace net_instaweb