Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1427)

Unified Diff: content/browser/loader/async_revalidation_manager_unittest.cc

Issue 1041993004: content::ResourceDispatcherHostImpl changes for stale-while-revalidate (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@s-w-r-yhirano-patch
Patch Set: Remove unnecessary deferral logic from AsyncRevalidationDriver Created 5 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: content/browser/loader/async_revalidation_manager_unittest.cc
diff --git a/content/browser/loader/async_revalidation_manager_unittest.cc b/content/browser/loader/async_revalidation_manager_unittest.cc
new file mode 100644
index 0000000000000000000000000000000000000000..a4626e49ce094a0eb74d1a97dbdbdd6e867deab8
--- /dev/null
+++ b/content/browser/loader/async_revalidation_manager_unittest.cc
@@ -0,0 +1,585 @@
+// Copyright 2015 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 "content/browser/loader/async_revalidation_manager.h"
+
+#include <queue>
+#include <set>
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/memory/shared_memory_handle.h"
+#include "base/pickle.h"
+#include "base/run_loop.h"
+#include "content/browser/child_process_security_policy_impl.h"
+#include "content/browser/loader/resource_dispatcher_host_impl.h"
+#include "content/browser/loader/resource_message_filter.h"
+#include "content/common/child_process_host_impl.h"
+#include "content/common/resource_messages.h"
+#include "content/public/browser/resource_context.h"
+#include "content/public/common/appcache_info.h"
+#include "content/public/common/process_type.h"
+#include "content/public/common/resource_type.h"
+#include "content/public/test/test_browser_context.h"
+#include "content/public/test/test_browser_thread_bundle.h"
+#include "ipc/ipc_param_traits.h"
+#include "net/base/load_flags.h"
+#include "net/base/network_delegate.h"
+#include "net/http/http_util.h"
+#include "net/url_request/url_request.h"
+#include "net/url_request/url_request_job.h"
+#include "net/url_request/url_request_job_factory.h"
+#include "net/url_request/url_request_test_job.h"
+#include "net/url_request/url_request_test_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/base/page_transition_types.h"
+#include "url/gurl.h"
+
+namespace content {
+
+namespace {
+
+class TestURLRequestJobFactory : public net::URLRequestJobFactory {
+ public:
+ TestURLRequestJobFactory() = default;
+
+ void HandleScheme(const std::string& scheme) {
+ supported_schemes_.insert(scheme);
+ }
+
+ // Sets the contents of the response. |headers| should have "\n" as line
+ // breaks and end in "\n\n".
+ void SetResponse(const std::string& headers, const std::string& data) {
+ response_headers_ =
+ net::HttpUtil::AssembleRawHeaders(headers.data(), headers.size());
+ response_data_ = data;
+ }
+
+ typedef base::Callback<net::URLRequestJob*(net::URLRequest*,
Bence 2015/11/17 13:12:23 Use "using" instead of "typedef", see https://chro
Adam Rice 2015/11/17 17:45:53 Done.
+ net::NetworkDelegate*)>
+ URLRequestJobCreateCallback;
+
+ void SetCustomURLRequestJobCreateCallback(
+ const URLRequestJobCreateCallback& callback) {
+ custom_url_request_job_create_callback_ = callback;
+ }
+
+ net::URLRequestJob* MaybeCreateJobWithProtocolHandler(
Bence 2015/11/17 13:12:23 Optional: feel free to inline function definition
Adam Rice 2015/11/17 17:45:53 Done.
+ const std::string& scheme,
+ net::URLRequest* request,
+ net::NetworkDelegate* network_delegate) const override;
+
+ net::URLRequestJob* MaybeInterceptRedirect(
+ net::URLRequest* request,
+ net::NetworkDelegate* network_delegate,
+ const GURL& location) const override {
+ return nullptr;
+ }
+
+ net::URLRequestJob* MaybeInterceptResponse(
+ net::URLRequest* request,
+ net::NetworkDelegate* network_delegate) const override {
+ return nullptr;
+ }
+
+ bool IsHandledProtocol(const std::string& scheme) const override {
+ return supported_schemes_.count(scheme) > 0;
+ }
+
+ bool IsHandledURL(const GURL& url) const override {
+ return supported_schemes_.count(url.scheme()) > 0;
+ }
+
+ bool IsSafeRedirectTarget(const GURL& location) const override {
+ return false;
+ }
+
+ private:
+ std::string response_headers_;
+ std::string response_data_;
+ std::set<std::string> supported_schemes_;
+ URLRequestJobCreateCallback custom_url_request_job_create_callback_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestURLRequestJobFactory);
+};
+
+// This class is a variation on URLRequestTestJob that
+// returns ERR_IO_PENDING before every read, not just the first one.
+class URLRequestTestDelayedCompletionJob : public net::URLRequestTestJob {
+ public:
+ URLRequestTestDelayedCompletionJob(net::URLRequest* request,
+ net::NetworkDelegate* network_delegate,
+ const std::string& response_headers,
+ const std::string& response_data)
+ : net::URLRequestTestJob(request,
+ network_delegate,
+ response_headers,
+ response_data,
+ false) {}
+
+ private:
+ ~URLRequestTestDelayedCompletionJob() override {}
+
+ bool NextReadAsync() override { return true; }
+
+ DISALLOW_COPY_AND_ASSIGN(URLRequestTestDelayedCompletionJob);
+};
+
+net::URLRequestJob* TestURLRequestJobFactory::MaybeCreateJobWithProtocolHandler(
+ const std::string& scheme,
+ net::URLRequest* request,
+ net::NetworkDelegate* network_delegate) const {
+ if (!custom_url_request_job_create_callback_.is_null()) {
+ return custom_url_request_job_create_callback_.Run(request,
+ network_delegate);
+ }
+
+ return new URLRequestTestDelayedCompletionJob(
+ request, network_delegate, response_headers_, response_data_);
+}
+
+// On Windows, ResourceMsg_SetDataBuffer supplies a HANDLE which is not
+// automatically released.
+//
+// See ResourceDispatcher::ReleaseResourcesInDataMessage.
+//
+// TODO(ricea): Maybe share this implementation with
+// resource_dispatcher_host_unittest.cc
+void ReleaseHandlesInMessage(const IPC::Message& message) {
+ if (message.type() == ResourceMsg_SetDataBuffer::ID) {
+ base::PickleIterator iter(message);
+ int request_id;
+ CHECK(iter.ReadInt(&request_id));
+ base::SharedMemoryHandle shm_handle;
+ if (IPC::ParamTraits<base::SharedMemoryHandle>::Read(&message, &iter,
+ &shm_handle)) {
+ if (base::SharedMemory::IsHandleValid(shm_handle))
+ base::SharedMemory::CloseHandle(shm_handle);
+ }
+ }
+}
+
+// This filter just deletes any messages that are sent through it.
+class BlackholeFilter : public ResourceMessageFilter {
+ public:
+ explicit BlackholeFilter(ResourceContext* resource_context)
+ : ResourceMessageFilter(
+ ChildProcessHostImpl::GenerateChildProcessUniqueId(),
+ PROCESS_TYPE_RENDERER,
+ NULL,
Bence 2015/11/17 13:12:23 Use nullptr instead of NULL.
Adam Rice 2015/11/17 17:45:53 Done.
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ base::Bind(&BlackholeFilter::GetContexts, base::Unretained(this))),
+ resource_context_(resource_context) {
+ ChildProcessSecurityPolicyImpl::GetInstance()->Add(child_id());
+ }
+
+ bool Send(IPC::Message* msg) override {
+ ReleaseHandlesInMessage(*msg);
+ delete msg;
+ return true;
+ }
+
+ private:
+ ~BlackholeFilter() override {
+ ChildProcessSecurityPolicyImpl::GetInstance()->Remove(child_id());
+ }
+
+ void GetContexts(const ResourceHostMsg_Request& request,
+ ResourceContext** resource_context,
+ net::URLRequestContext** request_context) {
+ *resource_context = resource_context_;
+ *request_context = resource_context_->GetRequestContext();
+ }
+
+ ResourceContext* resource_context_;
+
+ DISALLOW_COPY_AND_ASSIGN(BlackholeFilter);
+};
+
+ResourceHostMsg_Request CreateResourceRequest(const char* method,
+ ResourceType type,
+ const GURL& url) {
+ ResourceHostMsg_Request request;
+ request.method = std::string(method);
+ request.url = url;
+ request.first_party_for_cookies = url; // bypass third-party cookie blocking
+ request.referrer_policy = blink::WebReferrerPolicyDefault;
+ request.load_flags = 0;
+ request.origin_pid = 0;
+ request.resource_type = type;
+ request.request_context = 0;
+ request.appcache_host_id = kAppCacheNoHostId;
+ request.download_to_file = false;
+ request.should_reset_appcache = false;
+ request.is_main_frame = true;
+ request.parent_is_main_frame = false;
+ request.parent_render_frame_id = -1;
+ request.transition_type = ui::PAGE_TRANSITION_LINK;
+ request.allow_download = true;
+ return request;
+}
+
+} // namespace
+
+class AsyncRevalidationManagerTest : public ::testing::Test {
+ protected:
+ AsyncRevalidationManagerTest(
+ scoped_ptr<net::TestNetworkDelegate> network_delegate)
+ : thread_bundle_(content::TestBrowserThreadBundle::IO_MAINLOOP),
+ network_delegate_(network_delegate.Pass()) {
+ browser_context_.reset(new TestBrowserContext());
+ BrowserContext::EnsureResourceContextInitialized(browser_context_.get());
+ base::RunLoop().RunUntilIdle();
+ ResourceContext* resource_context = browser_context_->GetResourceContext();
+ filter_ = new BlackholeFilter(resource_context);
+ net::URLRequestContext* request_context =
+ resource_context->GetRequestContext();
+ job_factory_.reset(new TestURLRequestJobFactory);
+ request_context->set_job_factory(job_factory_.get());
+ request_context->set_network_delegate(network_delegate_.get());
+ host_.EnableStaleWhileRevalidateForTesting();
+ }
+
+ AsyncRevalidationManagerTest()
+ : AsyncRevalidationManagerTest(
+ make_scoped_ptr(new net::TestNetworkDelegate)) {}
+
+ void TearDown() override {
+ host_.CancelRequestsForProcess(filter_->child_id());
+ host_.Shutdown();
+ host_.CancelRequestsForContext(browser_context_->GetResourceContext());
+ browser_context_.reset();
+ base::RunLoop().RunUntilIdle();
+ }
+
+ void HandleScheme(const std::string& scheme) {
+ job_factory_->HandleScheme(scheme);
+ EnsureSchemeIsAllowed(scheme);
+ }
+
+ void SetResponse(const std::string& headers, const std::string& data) {
+ job_factory_->SetResponse(headers, data);
+ }
+
+ void SetCustomURLRequestJobCreateCallback(
+ const TestURLRequestJobFactory::URLRequestJobCreateCallback& callback) {
+ job_factory_->SetCustomURLRequestJobCreateCallback(callback);
+ }
+
+ // Creates a request using the current test object as the filter and
+ // SubResource as the resource type.
+ void MakeTestRequest(int render_view_id, int request_id, const GURL& url);
Bence 2015/11/17 13:12:23 Optional: feel free to inline if you prefer, it sh
Adam Rice 2015/11/17 17:45:53 Done.
+
+ void EnsureSchemeIsAllowed(const std::string& scheme) {
+ ChildProcessSecurityPolicyImpl* policy =
+ ChildProcessSecurityPolicyImpl::GetInstance();
+ if (!policy->IsWebSafeScheme(scheme))
+ policy->RegisterWebSafeScheme(scheme);
+ }
+
+ net::TestNetworkDelegate* network_delegate() {
+ return network_delegate_.get();
+ }
+
+ content::TestBrowserThreadBundle thread_bundle_;
+ scoped_ptr<TestBrowserContext> browser_context_;
+ scoped_ptr<TestURLRequestJobFactory> job_factory_;
+ scoped_refptr<BlackholeFilter> filter_;
+ scoped_ptr<net::TestNetworkDelegate> network_delegate_;
+ ResourceDispatcherHostImpl host_;
+};
+
+void AsyncRevalidationManagerTest::MakeTestRequest(int render_view_id,
+ int request_id,
+ const GURL& url) {
+ ResourceHostMsg_Request request =
+ CreateResourceRequest("GET", RESOURCE_TYPE_SUB_RESOURCE, url);
+ ResourceHostMsg_RequestResource msg(render_view_id, request_id, request);
+ host_.OnMessageReceived(msg, filter_.get());
+ base::RunLoop().RunUntilIdle();
+}
+
+TEST_F(AsyncRevalidationManagerTest, SupportsAsyncRevalidation) {
+ // Scheme has to be HTTP or HTTPS to support async revalidation.
+ HandleScheme("http");
+ SetResponse(net::URLRequestTestJob::test_headers(), "delay complete");
+ MakeTestRequest(0, 1, GURL("http://example.com/baz"));
+
+ net::URLRequest* url_request(
+ host_.GetURLRequest(GlobalRequestID(filter_->child_id(), 1)));
+ ASSERT_TRUE(url_request);
+
+ EXPECT_TRUE(url_request->load_flags() & net::LOAD_SUPPORT_ASYNC_REVALIDATION);
+}
+
+TEST_F(AsyncRevalidationManagerTest, AsyncRevalidationNotSupportedForPOST) {
+ // Scheme has to be HTTP or HTTPS to support async revalidation.
+ HandleScheme("http");
+ SetResponse(net::URLRequestTestJob::test_headers(), "delay complete");
+ // Create POST request.
+ ResourceHostMsg_Request request = CreateResourceRequest(
+ "POST", RESOURCE_TYPE_SUB_RESOURCE, GURL("http://example.com/baz.php"));
+ ResourceHostMsg_RequestResource msg(0, 1, request);
+ host_.OnMessageReceived(msg, filter_.get());
+ base::RunLoop().RunUntilIdle();
+
+ net::URLRequest* url_request(
+ host_.GetURLRequest(GlobalRequestID(filter_->child_id(), 1)));
+ ASSERT_TRUE(url_request);
+
+ EXPECT_FALSE(url_request->load_flags() &
+ net::LOAD_SUPPORT_ASYNC_REVALIDATION);
+}
+
+TEST_F(AsyncRevalidationManagerTest, AsyncRevalidationNotSupportedForRedirect) {
+ static const char kRedirectHeaders[] =
+ "HTTP/1.1 302 MOVED\n"
+ "Location: http://example.com/var\n"
+ "\n";
+ // Scheme has to be HTTP or HTTPS to support async revalidation.
+ HandleScheme("http");
+ SetResponse(kRedirectHeaders, "");
+
+ MakeTestRequest(0, 1, GURL("http://example.com/baz"));
+
+ net::URLRequest* url_request(
+ host_.GetURLRequest(GlobalRequestID(filter_->child_id(), 1)));
+ ASSERT_TRUE(url_request);
+
+ EXPECT_FALSE(url_request->load_flags() &
+ net::LOAD_SUPPORT_ASYNC_REVALIDATION);
+}
+
+// A URLRequestJob implementation which sets the |async_revalidation_required|
+// flag on the HttpResponseInfo object to true if the request has the
+// LOAD_SUPPORT_ASYNC_REVALIDATION flag.
+class AsyncRevalidationRequiredURLRequestTestJob
+ : public net::URLRequestTestJob {
+ public:
+ // The Create() method is useful for wrapping the construction of the object
+ // in a Callback.
+ static net::URLRequestJob* Create(net::URLRequest* request,
+ net::NetworkDelegate* network_delegate) {
+ return new AsyncRevalidationRequiredURLRequestTestJob(request,
+ network_delegate);
+ }
+
+ void GetResponseInfo(net::HttpResponseInfo* info) override {
+ if (request()->load_flags() & net::LOAD_SUPPORT_ASYNC_REVALIDATION)
+ info->async_revalidation_required = true;
+ }
+
+ private:
+ AsyncRevalidationRequiredURLRequestTestJob(
+ net::URLRequest* request,
+ net::NetworkDelegate* network_delegate)
+ : URLRequestTestJob(request,
+ network_delegate,
+ net::URLRequestTestJob::test_headers(),
+ std::string(),
+ false) {}
+
+ ~AsyncRevalidationRequiredURLRequestTestJob() override {}
+
+ DISALLOW_COPY_AND_ASSIGN(AsyncRevalidationRequiredURLRequestTestJob);
+};
+
+// A URLRequestJob implementation which serves a redirect and sets the
+// |async_revalidation_required| flag on the HttpResponseInfo object to true if
+// the request has the LOAD_SUPPORT_ASYNC_REVALIDATION flag.
+class RedirectAndRevalidateURLRequestTestJob : public net::URLRequestTestJob {
+ public:
+ // The Create() method is useful for wrapping the construction of the object
+ // in a Callback.
+ static net::URLRequestJob* Create(net::URLRequest* request,
+ net::NetworkDelegate* network_delegate) {
+ return new RedirectAndRevalidateURLRequestTestJob(request,
+ network_delegate);
+ }
+
+ void GetResponseInfo(net::HttpResponseInfo* info) override {
+ URLRequestTestJob::GetResponseInfo(info);
+ if (request()->load_flags() & net::LOAD_SUPPORT_ASYNC_REVALIDATION)
+ info->async_revalidation_required = true;
+ }
+
+ private:
+ RedirectAndRevalidateURLRequestTestJob(net::URLRequest* request,
+ net::NetworkDelegate* network_delegate)
+ : URLRequestTestJob(request,
+ network_delegate,
+ std::string(CreateRedirectHeaders()),
+ std::string(),
+ false) {}
+
+ ~RedirectAndRevalidateURLRequestTestJob() override {}
+
+ static std::string CreateRedirectHeaders() {
+ static const char kRedirectHeaders[] =
+ "HTTP/1.1 302 MOVED\0"
+ "Location: http://example.com/var\0"
+ "\0";
+ return std::string(kRedirectHeaders, arraysize(kRedirectHeaders));
+ }
+
+ DISALLOW_COPY_AND_ASSIGN(RedirectAndRevalidateURLRequestTestJob);
+};
+
+// A NetworkDelegate that records the URLRequests as they are created.
+class URLRequestRecordingNetworkDelegate : public net::TestNetworkDelegate {
+ public:
+ URLRequestRecordingNetworkDelegate() : requests_() {}
+
+ net::URLRequest* NextRequest() {
+ if (requests_.empty())
+ return nullptr;
+ net::URLRequest* request = requests_.front();
+ requests_.pop();
+ return request;
+ }
+
+ bool IsEmpty() { return requests_.empty(); }
+
+ int OnBeforeURLRequest(net::URLRequest* request,
+ const net::CompletionCallback& callback,
+ GURL* new_url) override {
+ requests_.push(request);
+ return TestNetworkDelegate::OnBeforeURLRequest(request, callback, new_url);
+ }
+
+ private:
+ std::queue<net::URLRequest*> requests_;
+
+ DISALLOW_COPY_AND_ASSIGN(URLRequestRecordingNetworkDelegate);
+};
+
+class AsyncRevalidationManagerRecordingTest
+ : public AsyncRevalidationManagerTest {
+ public:
+ AsyncRevalidationManagerRecordingTest()
+ : AsyncRevalidationManagerTest(
+ make_scoped_ptr(new URLRequestRecordingNetworkDelegate)) {
+ // Scheme has to be HTTP or HTTPS to support async revalidation.
+ HandleScheme("http");
+ // Use the AsyncRevalidationRequiredURLRequestTestJob.
+ SetCustomURLRequestJobCreateCallback(
+ base::Bind(&AsyncRevalidationRequiredURLRequestTestJob::Create));
+ }
+
+ URLRequestRecordingNetworkDelegate* recording_network_delegate() {
+ return static_cast<URLRequestRecordingNetworkDelegate*>(network_delegate());
+ }
+
+ net::URLRequest* NextRequest() {
+ return recording_network_delegate()->NextRequest();
+ }
+
+ bool IsEmpty() { return recording_network_delegate()->IsEmpty(); }
+};
+
+// Verify that an async revalidation is actually created when needed.
+TEST_F(AsyncRevalidationManagerRecordingTest, Issued) {
+ // Create the original request.
+ MakeTestRequest(0, 1, GURL("http://example.com/baz"));
+
+ net::URLRequest* initial_request = NextRequest();
+ ASSERT_TRUE(initial_request);
+ EXPECT_TRUE(initial_request->load_flags() &
+ net::LOAD_SUPPORT_ASYNC_REVALIDATION);
+
+ net::URLRequest* async_request = NextRequest();
+ ASSERT_TRUE(async_request);
+}
+
+// Verify the the URL of the async revalidation matches the original request.
+TEST_F(AsyncRevalidationManagerRecordingTest, URLMatches) {
+ // Create the original request.
+ MakeTestRequest(0, 1, GURL("http://example.com/special-baz"));
+
+ // Discard the original request.
+ NextRequest();
+
+ net::URLRequest* async_request = NextRequest();
+ ASSERT_TRUE(async_request);
+ EXPECT_EQ(GURL("http://example.com/special-baz"), async_request->url());
+}
+
+TEST_F(AsyncRevalidationManagerRecordingTest,
+ AsyncRevalidationsDoNotSupportAsyncRevalidation) {
+ // Create the original request.
+ MakeTestRequest(0, 1, GURL("http://example.com/baz"));
+
+ // Discard the original request.
+ NextRequest();
+
+ // Get the async revalidation request.
+ net::URLRequest* async_request = NextRequest();
+ ASSERT_TRUE(async_request);
+ EXPECT_FALSE(async_request->load_flags() &
+ net::LOAD_SUPPORT_ASYNC_REVALIDATION);
+}
+
+TEST_F(AsyncRevalidationManagerRecordingTest, AsyncRevalidationsNotDuplicated) {
+ // Create the original request.
+ MakeTestRequest(0, 1, GURL("http://example.com/baz"));
+
+ // Discard the original request.
+ NextRequest();
+
+ // Get the async revalidation request.
+ net::URLRequest* async_request = NextRequest();
+ EXPECT_TRUE(async_request);
+
+ // Start a second request to the same URL.
+ MakeTestRequest(0, 2, GURL("http://example.com/baz"));
+
+ // Discard the second request.
+ NextRequest();
+
+ // There should not be another async revalidation request.
+ EXPECT_TRUE(IsEmpty());
+}
+
+// Async revalidation to different URLs should not be treated as duplicates.
+TEST_F(AsyncRevalidationManagerRecordingTest,
+ AsyncRevalidationsToSeparateURLsAreSeparate) {
+ // Create two requests to two URLs.
+ MakeTestRequest(0, 1, GURL("http://example.com/baz"));
+ MakeTestRequest(0, 2, GURL("http://example.com/far"));
+
+ net::URLRequest* initial_request = NextRequest();
+ ASSERT_TRUE(initial_request);
+ net::URLRequest* initial_async_revalidation = NextRequest();
+ ASSERT_TRUE(initial_async_revalidation);
+ net::URLRequest* second_request = NextRequest();
+ ASSERT_TRUE(second_request);
+ net::URLRequest* second_async_revalidation = NextRequest();
+ ASSERT_TRUE(second_async_revalidation);
+
+ EXPECT_EQ("http://example.com/baz", initial_request->url().spec());
+ EXPECT_EQ("http://example.com/baz", initial_async_revalidation->url().spec());
+ EXPECT_EQ("http://example.com/far", second_request->url().spec());
+ EXPECT_EQ("http://example.com/far", second_async_revalidation->url().spec());
+}
+
+// A stale-while-revalidate applicable redirect response should not result in an
+// async revalidation.
+TEST_F(AsyncRevalidationManagerRecordingTest, RedirectNotApplicable) {
+ // Use the appropriate URLRequestJob for the test.
+ SetCustomURLRequestJobCreateCallback(
+ base::Bind(&RedirectAndRevalidateURLRequestTestJob::Create));
+ MakeTestRequest(0, 1, GURL("http://example.com/redirect"));
+
+ net::URLRequest* initial_request = NextRequest();
+ EXPECT_TRUE(initial_request);
+
+ // There should not be an async revalidation request.
+ EXPECT_TRUE(IsEmpty());
+}
+
+} // namespace content

Powered by Google App Engine
This is Rietveld 408576698