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

Unified Diff: chrome/browser/net/sdch_browsertest.cc

Issue 380003002: Improve testing for SDCH. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Fixed braino: Added SDHC enable to the tests that need it. Created 6 years, 5 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: chrome/browser/net/sdch_browsertest.cc
diff --git a/chrome/browser/net/sdch_browsertest.cc b/chrome/browser/net/sdch_browsertest.cc
new file mode 100644
index 0000000000000000000000000000000000000000..b39420f0daf96607678a198e7989ebcfb38d4500
--- /dev/null
+++ b/chrome/browser/net/sdch_browsertest.cc
@@ -0,0 +1,639 @@
+// Copyright 2014 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.
+
+// End-to-end SDCH tests. Uses the embedded test server to return SDCH
+// results
+
+#include "base/base64.h"
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/command_line.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/memory/weak_ptr.h"
+#include "base/path_service.h"
+#include "base/run_loop.h"
+#include "base/strings/string_util.h"
+#include "base/strings/stringprintf.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/browsing_data/browsing_data_helper.h"
+#include "chrome/browser/browsing_data/browsing_data_remover.h"
+#include "chrome/browser/browsing_data/browsing_data_remover_test_util.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/profiles/profile_manager.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/browser_tabstrip.h"
+#include "chrome/browser/ui/browser_window.h"
+#include "chrome/browser/ui/tabs/tab_strip_model.h"
+#include "chrome/common/chrome_paths.h"
+#include "chrome/test/base/in_process_browser_test.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/common/content_switches.h"
+#include "content/public/test/browser_test_utils.h"
+#include "content/public/test/test_utils.h"
+#include "crypto/sha2.h"
+#include "net/base/sdch_manager.h"
+#include "net/http/http_response_headers.h"
+#include "net/test/embedded_test_server/embedded_test_server.h"
+#include "net/test/embedded_test_server/http_request.h"
+#include "net/test/embedded_test_server/http_response.h"
+#include "net/url_request/url_fetcher.h"
+#include "net/url_request/url_fetcher_delegate.h"
+#include "net/url_request/url_request_context.h"
+#include "net/url_request/url_request_context_getter.h"
+#include "sdch/open-vcdiff/src/google/vcencoder.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+typedef std::vector<net::test_server::HttpRequest> RequestVector;
+typedef std::map<std::string, std::string> HttpRequestHeaderMap;
+
+// Credit Alfred, Lord Tennyson
+static const char kSampleData[] = "<html><body><pre>"
+ "There lies the port; the vessel puffs her sail:\n"
+ "There gloom the dark, broad seas. My mariners,\n"
+ "Souls that have toil'd, and wrought, and thought with me—\n"
+ "That ever with a frolic welcome took\n"
+ "The thunder and the sunshine, and opposed\n"
+ "Free hearts, free foreheads—you and I are old;\n"
+ "Old age hath yet his honour and his toil;\n"
+ "Death closes all: but something ere the end,\n"
+ "Some work of noble note, may yet be done,\n"
+ "Not unbecoming men that strove with Gods.\n"
+ "The lights begin to twinkle from the rocks:\n"
+ "The long day wanes: the slow moon climbs: the deep\n"
+ "Moans round with many voices. Come, my friends,\n"
+ "'T is not too late to seek a newer world.\n"
+ "Push off, and sitting well in order smite\n"
+ "The sounding furrows; for my purpose holds\n"
+ "To sail beyond the sunset, and the baths\n"
+ "Of all the western stars, until I die.\n"
+ "It may be that the gulfs will wash us down:\n"
+ "It may be we shall touch the Happy Isles,\n"
+ "And see the great Achilles, whom we knew.\n"
+ "Tho' much is taken, much abides; and tho'\n"
+ "We are not now that strength which in old days\n"
+ "Moved earth and heaven, that which we are, we are;\n"
+ "One equal temper of heroic hearts,\n"
+ "Made weak by time and fate, but strong in will\n"
+ "To strive, to seek, to find, and not to yield.\n"
+ "</pre></body></html>";
+
+// Random selection of lines from above, to allow some encoding, but
+// not a trivial encoding.
+static const char kDictionaryContents[] =
+ "The thunder and the sunshine, and opposed\n"
+ "To sail beyond the sunset, and the baths\n"
+ "Of all the western stars, until I die.\n"
+ "Made weak by time and fate, but strong in will\n"
+ "Moans round with many voices. Come, my friends,\n"
+ "The lights begin to twinkle from the rocks:";
+
+// Scanns in a case-insensitive way for |header| in |map|,
mef 2014/07/23 18:34:28 nit: Scanns
Randy Smith (Not in Mondays) 2014/07/29 23:44:21 Done.
+// returning true if found and setting |*value| to the value
+// of that header. Does not handle multiple instances of the same
+// header.
+bool GetRequestHeader(const HttpRequestHeaderMap& map,
+ const char* header,
mef 2014/07/23 18:34:29 nit: alignment
Randy Smith (Not in Mondays) 2014/07/29 23:44:21 Done.
+ std::string* value) {
+ for (HttpRequestHeaderMap::const_iterator it = map.begin();
+ it != map.end(); ++it) {
+
jar (doing other things) 2014/07/31 03:24:34 nit: minimize vertical whitespace... and I doubt
Randy Smith (Not in Mondays) 2014/08/11 20:33:24 Yeah, typo. Done.
+ if (!base::strcasecmp(it->first.c_str(), header)) {
+ *value = it->second;
+ return true;
+ }
+ }
+ return false;
+}
+
+// Do a URL-safe base64 encoding. See the SDCH spec "Dictionary Identifier"
+// section, and RFC 3548 section 4.
+void SafeBase64Encode(const std::string& input_value, std::string* output) {
+ DCHECK(output);
+ base::Base64Encode(input_value, output);
+ for (size_t i = 0; i < output->size(); ++i) {
mef 2014/07/23 18:34:28 It seems that common practice is to use std::repla
Randy Smith (Not in Mondays) 2014/07/29 23:44:21 Done, both here and in source copy location. Neat
+ if ((*output)[i] == '+')
+ (*output)[i] = '-';
+ if ((*output)[i] == '/')
+ (*output)[i] = '_';
+ }
+}
+
+bool ResponseContainsHeaderValue(const net::HttpResponseHeaders& headers,
mef 2014/07/23 18:34:28 Seems the same as HttpResponseHeaders::HasHeaderVa
Randy Smith (Not in Mondays) 2014/07/29 23:44:20 Thanks for the catch. Done.
+ const char* header,
+ const char* value) {
+ std::string iterated_value;
+ void *iter = NULL;
+ while (headers.EnumerateHeader(&iter, header, &iterated_value)) {
+ if (!base::strcasecmp(iterated_value.c_str(), value))
+ return true;
+ }
+ return false;
+}
+
+// Class that bundles responses for an EmbeddedTestServer().
+// Dictionary is at <domain>/dict, data at <domain>/data.
+// The data is sent SDCH encoded if that's allowed by protoocol.
+class SdchResponseHandler {
+ public:
+ // Do initial preparation so that SDCH requests can be handled.
+ explicit SdchResponseHandler(std::string domain)
+ : cache_sdch_response_(false),
+ weak_ptr_factory_(this) {
+ // Dictionary
+ sdch_dictionary_contents_ = "Domain: ";
+ sdch_dictionary_contents_ += domain;
+ sdch_dictionary_contents_ += "\n\n";
+ sdch_dictionary_contents_ += kDictionaryContents;
+
+ // Dictionary hash for client and server.
+ char binary_hash[32];
+ crypto::SHA256HashString(sdch_dictionary_contents_, binary_hash,
+ sizeof(binary_hash));
+ SafeBase64Encode(std::string(&binary_hash[0], 6), &dictionary_client_hash_);
+ SafeBase64Encode(std::string(&binary_hash[6], 6), &dictionary_server_hash_);
+
+ // Encoded response.
+ open_vcdiff::HashedDictionary vcdiff_dictionary(
+ kDictionaryContents, strlen(kDictionaryContents));
+ bool result = vcdiff_dictionary.Init();
+ DCHECK(result);
+ open_vcdiff::VCDiffStreamingEncoder encoder(&vcdiff_dictionary, 0, false);
+ encoded_data_ = dictionary_server_hash_;
+ encoded_data_ += '\0';
+ result = encoder.StartEncoding(&encoded_data_);
+ DCHECK(result);
+ result = encoder.EncodeChunk(
+ kSampleData, strlen(kSampleData), &encoded_data_);
+ DCHECK(result);
+ result = encoder.FinishEncoding(&encoded_data_);
+ DCHECK(result);
+ }
+
+ static bool ClientIsAdvertisingSdchEncoding(const HttpRequestHeaderMap& map) {
+ std::string value;
+ if (!GetRequestHeader(map, "accept-encoding", &value))
+ return false;
+ std::string::iterator word_start = value.begin();
+ // Scan comma separated list of encodings for case insensitive match
mef 2014/07/23 18:34:29 Suggest: Use base/strings/string_tokenizer.h
Randy Smith (Not in Mondays) 2014/07/29 23:44:21 Major advantage of code reviews; learning new tech
mef 2014/07/30 17:14:58 It is mutual, I've learned about it doing the revi
+ // for "sdch".
+ do {
+ std::string::iterator it(word_start);
+ while (it != value.end() && IsAsciiAlpha(*it))
+ ++it;
+ if (it != word_start) {
+ if (!base::strcasecmp(std::string(word_start,it).c_str(), "sdch"))
+ return true;
+ }
+ while (it != value.end() && !IsAsciiAlpha(*it))
+ ++it;
+ word_start = it;
+ } while (word_start != value.end());
+ return false;
+ }
+
+ bool ShouldRespondWithSdchEncoding(const HttpRequestHeaderMap& map) {
+ std::string value;
+ if (!GetRequestHeader(map, "avail-dictionary", &value))
+ return false;
+ return value == dictionary_client_hash_;
+ }
+
+ scoped_ptr<net::test_server::HttpResponse> HandleRequest(
+ const net::test_server::HttpRequest& request) {
+ request_vector_.push_back(request);
+
+ scoped_ptr<net::test_server::BasicHttpResponse> response(
+ new net::test_server::BasicHttpResponse);
+ if (request.relative_url == "/data") {
+ if (ShouldRespondWithSdchEncoding(request.headers)) {
+ // Note that chrome doesn't advertise accepting SDCH encoding
+ // for POSTs (because the meta-refresh hack would break a POST),
+ // but that's not for the server to enforce.
+ DCHECK_NE(encoded_data_, "");
mef 2014/07/23 18:34:29 Given that this is a unit test, should DCHECKs be
Randy Smith (Not in Mondays) 2014/07/29 23:44:21 My take was that if I was making statements about
mef 2014/07/30 17:14:58 Acknowledged.
+ response->set_content_type("text/html");
+ response->set_content(encoded_data_);
+ response->AddCustomHeader("Content-Encoding", "sdch");
+ // We allow tests to set caching on the sdch response,
+ // so that we can force an encoded response with no
+ // dictionary.
+ if (cache_sdch_response_)
+ response->AddCustomHeader("Cache-Control", "max-age=3600");
+ else
+ response->AddCustomHeader("Cache-Control", "no-store");
+ } else {
+ response->set_content_type("text/plain");
+ response->set_content(kSampleData);
+ if (ClientIsAdvertisingSdchEncoding(request.headers))
+ response->AddCustomHeader("Get-Dictionary", "/dict");
+ // We never cache the plain data response, to make it
+ // easy to refresh after we get the dictionary.
+ response->AddCustomHeader("Cache-Control", "no-store");
+ }
+ } else {
+ DCHECK_EQ(request.relative_url, "/dict");
mef 2014/07/23 18:34:28 thought: Should "/dict" and such be defined consta
Randy Smith (Not in Mondays) 2014/07/29 23:44:21 Probably worth doing, if only to reduce the chance
+ DCHECK_NE(sdch_dictionary_contents_, "");
+ response->set_content_type("application/x-sdch-dictionary");
+ response->set_content(sdch_dictionary_contents_);
+ }
+ std::vector<base::Closure> callbacks;
+ callbacks.swap(callback_vector_);
+ for (std::vector<base::Closure>::iterator it = callbacks.begin();
+ it != callbacks.end(); ++it) {
+ it->Run();
+ }
+ return response.PassAs<net::test_server::HttpResponse>();
+ }
+
+ void WaitAndGetRequestVector(int num_requests,
+ base::Closure callback,
+ RequestVector* v) {
+ DCHECK_LT(0, num_requests);
+ if (static_cast<size_t>(num_requests) > request_vector_.size()) {
+ callback_vector_.push_back(
+ base::Bind(&SdchResponseHandler::WaitAndGetRequestVector,
mef 2014/07/23 18:34:29 I'm a bit unhappy about polling loop here, but I'm
Randy Smith (Not in Mondays) 2014/07/29 23:44:20 I'm confused by your comment; I don't see code her
mef 2014/07/30 17:14:58 Never mind. I've called it a polling loop because
+ weak_ptr_factory_.GetWeakPtr(), num_requests,
+ callback, v));
+ return;
+ }
+ *v = request_vector_;
+ content::BrowserThread::PostTask(
+ content::BrowserThread::UI, FROM_HERE, callback);
+ }
+
+ void set_cache_sdch_response(bool cache_sdch_response) {
+ cache_sdch_response_ = cache_sdch_response;
+ }
+
+ private:
+ bool cache_sdch_response_;
+ std::string encoded_data_;
+ std::string sdch_dictionary_contents_;
+ std::string dictionary_client_hash_;
+ std::string dictionary_server_hash_;
+ RequestVector request_vector_;
+ std::vector<base::Closure> callback_vector_;
+ base::WeakPtrFactory<SdchResponseHandler> weak_ptr_factory_;
+};
+
+class SdchBrowserTest : public InProcessBrowserTest, net::URLFetcherDelegate {
+ public:
+ static const char kTestHost[];
+
+ SdchBrowserTest()
+ : response_handler_(kTestHost),
+ url_request_context_getter_(NULL),
+ fetcher_response_code_(0),
+ url_fetch_complete_(false),
+ waiting_(false) {}
+
+ // ** Helper functions for fetching data.
mef 2014/07/23 18:34:29 nit: Do we need **?
Randy Smith (Not in Mondays) 2014/07/29 23:44:21 So I put the ** in to indicate that they were comm
+
+ void FetchUrlDetailed(GURL url, net::URLRequestContextGetter* getter,
+ bool use_post) {
mef 2014/07/23 18:34:29 nit: use_post may fit on previous line.
Randy Smith (Not in Mondays) 2014/07/29 23:44:20 Not in my checkout :-{.
jar (doing other things) 2014/07/31 03:24:33 nit: When args in definition or declaration don't
Randy Smith (Not in Mondays) 2014/08/11 20:33:24 Moot; use_post removed.
+ url_fetch_complete_ = false;
+ fetcher_status_ = net::URLRequestStatus();
+ fetcher_response_code_ = 0;
+ fetcher_response_headers_ = NULL;
+ fetcher_response_contents_ = "";
+ fetcher_.reset(net::URLFetcher::Create(
+ url, use_post ? net::URLFetcher::POST : net::URLFetcher::GET, this));
+ if (use_post)
+ fetcher_->SetUploadData("text/plain", "Simple content");
+ fetcher_->SetRequestContext(getter);
+ fetcher_->Start();
+ if (!url_fetch_complete_) {
+ waiting_ = true;
+ content::RunMessageLoop();
+ waiting_ = false;
+ }
+ CHECK(url_fetch_complete_);
+ }
+ void FetchUrl(GURL url) {
mef 2014/07/23 18:34:29 nit: I think we separate methods by blank lines.
Randy Smith (Not in Mondays) 2014/07/29 23:44:21 The coding style guide says "Minimize use of verti
mef 2014/07/30 17:14:58 SGTM, I don't feel strongly, and I'm still learnin
jar (doing other things) 2014/07/31 03:24:33 FWIW: I think vertical whitespace between function
Randy Smith (Not in Mondays) 2014/08/11 20:33:25 url_request_test_util.h, TestDelegate as an exampl
+ FetchUrlDetailed(url, url_request_context_getter_, false);
+ }
+ int fetcher_response_code() { return fetcher_response_code_; }
mef 2014/07/23 18:34:29 nit: const (also below).
Randy Smith (Not in Mondays) 2014/07/29 23:44:21 Done.
+ const net::URLRequestStatus& fetcher_status() { return fetcher_status_; }
+ const net::HttpResponseHeaders* fetcher_response_headers() {
+ return fetcher_response_headers_;
+ }
+ std::string fetcher_response_contents() { return fetcher_response_contents_; }
+
+ GURL data_url() {
mef 2014/07/23 18:34:28 nit: it's not a simple accessor, so should be GetD
Randy Smith (Not in Mondays) 2014/07/29 23:44:20 I only used it in one place, so I inlined it.
jar (doing other things) 2014/07/31 03:24:33 +1 on the comment: avoid hacker style except for s
Randy Smith (Not in Mondays) 2014/08/11 20:33:25 I don't understand this comment--I think of gettin
+ return GURL(base::StringPrintf(
+ "http://%s:%d/data", kTestHost, test_server_port()));
+ }
+
+ // Get the data from the server. Return value is success/failure of the
+ // data operation, |*sdch_encoding_used| indicates whether or not the
+ // data was retrieved with sdch encoding.
+ // This is done through FetchUrl(), so the various helper functions
+ // will have valid status if it returns successfully.
+ bool GetDataDetailed(net::URLRequestContextGetter* getter,
+ bool use_post,
mef 2014/07/23 18:34:29 FWIW it seems that |use_post| is never true any mo
Randy Smith (Not in Mondays) 2014/07/29 23:44:21 Good point. Gone.
+ bool* sdch_encoding_used) {
+ FetchUrlDetailed(data_url(), getter, use_post);
+ EXPECT_EQ(net::URLRequestStatus::SUCCESS, fetcher_status().status())
+ << "Error code is " << fetcher_status().error();
+ EXPECT_EQ(200, fetcher_response_code());
+ EXPECT_EQ(kSampleData, fetcher_response_contents());
+
+ if (net::URLRequestStatus::SUCCESS != fetcher_status().status() ||
+ 200 != fetcher_response_code()) {
+ *sdch_encoding_used = false;
+ return false;
+ }
+
+ *sdch_encoding_used = ResponseContainsHeaderValue(
+ *fetcher_response_headers(), "Content-Encoding", "sdch");
+
+ if (fetcher_response_contents() != kSampleData)
+ return false;
+
+ return true;
+ }
+ bool GetData(bool* sdch_encoding_used) {
+ return GetDataDetailed(
+ url_request_context_getter_, false, sdch_encoding_used);
+ }
+
+ // ** Client information and control.
jar (doing other things) 2014/07/31 03:24:33 nit: I saw mention of your convention that "**" me
Randy Smith (Not in Mondays) 2014/08/11 20:33:25 I believe this comment is moot.
+
+ int GetNumberOfDictionaryFetches(Profile* profile) {
+ int fetches = -1;
+ base::RunLoop run_loop;
+ content::BrowserThread::PostTaskAndReply(
+ content::BrowserThread::IO, FROM_HERE,
+ base::Bind(&SdchBrowserTest::GetNumberOfDictionaryFetchesOnIOThread,
+ base::Unretained(profile->GetRequestContext()),
+ &fetches),
+ run_loop.QuitClosure());
+ run_loop.Run();
jar (doing other things) 2014/07/31 03:24:33 This might be the (only?) way to run these tests..
Randy Smith (Not in Mondays) 2014/08/11 20:33:25 I believe that this last point is exactly what the
jar (doing other things) 2014/08/13 01:28:58 <sigh> I don't think I see a way to make the asser
Randy Smith (Not in Mondays) 2014/08/13 02:05:25 We're reading the header file differently. I take
jar (doing other things) 2014/08/14 22:33:25 Independent of the performance... I'm OK with this
+ DCHECK_NE(-1, fetches);
+ return fetches;
+ }
+
+ void BrowsingDataRemoveAndWait(int remove_mask) {
+ BrowsingDataRemover* remover = BrowsingDataRemover::CreateForPeriod(
+ browser()->profile(), BrowsingDataRemover::LAST_HOUR);
+ BrowsingDataRemoverCompletionObserver completion_observer(remover);
+ remover->Remove(remove_mask, BrowsingDataHelper::UNPROTECTED_WEB);
+ completion_observer.BlockUntilCompletion();
+ }
+
+ // Something of a cheat; nuke the dictionaries off the SdchManager without
+ // touching the cache (which browsing data remover would do).
+ void NukeSdchDictionaries() {
+ base::RunLoop run_loop;
+ content::BrowserThread::PostTaskAndReply(
+ content::BrowserThread::IO, FROM_HERE,
+ base::Bind(&SdchBrowserTest::NukeSdchDictionariesOnIOThread,
+ url_request_context_getter_),
+ run_loop.QuitClosure());
+ run_loop.Run();
+ }
+
+ // Create a second profile to work within multi-profile.
+ Profile* CreateSecondProfile() {
+ base::FilePath user_data_dir;
+ PathService::Get(chrome::DIR_USER_DATA, &user_data_dir);
+
+ if (!second_profile_data_dir_.CreateUniqueTempDirUnderPath(user_data_dir))
+ return NULL;
+
+ Profile* second_profile =
+ g_browser_process->profile_manager()->GetProfile(
+ second_profile_data_dir_.path());
+
+ return second_profile;
jar (doing other things) 2014/07/31 03:24:34 nit: probably wasn't worth defining a variable he
Randy Smith (Not in Mondays) 2014/08/11 20:33:24 Semi-moot, as function has been inlined (sic?) int
+ }
+
+ Browser* CreateBrowserOnProfile(Profile* profile) {
+ DCHECK(profile);
+ Browser* new_browser =
+ new Browser(Browser::CreateParams(
+ profile, browser()->host_desktop_type()));
+ chrome::AddSelectedTabWithURL(new_browser,
+ GURL(url::kAboutBlankURL),
+ content::PAGE_TRANSITION_AUTO_TOPLEVEL);
+ content::WaitForLoadStop(
+ new_browser->tab_strip_model()->GetActiveWebContents());
+ new_browser->window()->Show();
+ return new_browser;
+ }
+
+ // ** Server information and control.
jar (doing other things) 2014/07/31 03:24:33 nit: again, kill "**", and use words.
Randy Smith (Not in Mondays) 2014/08/11 20:33:25 Moot.
+
+ void WaitAndGetTestVector(int num_requests, RequestVector* result) {
+ base::RunLoop run_loop;
+ content::BrowserThread::PostTask(
+ content::BrowserThread::IO, FROM_HERE,
+ base::Bind(&SdchResponseHandler::WaitAndGetRequestVector,
+ base::Unretained(&response_handler_),
+ num_requests,
+ run_loop.QuitClosure(),
+ result));
+ run_loop.Run();
+ }
+
+ int test_server_port() { return test_server_.port(); }
+
+ void SetSdchCacheability(bool cache_sdch_response) {
+ base::RunLoop run_loop;
+ content::BrowserThread::PostTaskAndReply(
+ content::BrowserThread::IO, FROM_HERE,
+ base::Bind(&SdchResponseHandler::set_cache_sdch_response,
+ base::Unretained(&response_handler_),
+ cache_sdch_response),
+ run_loop.QuitClosure());
+ run_loop.Run();
+ }
+
+ // Helper function for common test pattern.
+ //
+ // This function gets the data, confirms that the initial sending of the
+ // data included a dictionary advertisement, that that advertisement
+ // resulted in queueing a dictionary fetch, forces that fetch to
+ // go through, and confirms that a follow-on data load uses SDCH
+ // encoding. Returns true if the entire sequence of events occurred.
+ bool ForceSdchDictionaryLoad(Browser* browser) {
+ bool sdch_encoding_used = true;
+ bool data_gotten = GetDataDetailed(
+ browser->profile()->GetRequestContext(), false,
+ &sdch_encoding_used);
+ EXPECT_TRUE(data_gotten);
+ if (!data_gotten) return false;
+ EXPECT_FALSE(sdch_encoding_used);
+
+ // Confirm that we were told to get the dictionary
+ const net::HttpResponseHeaders* headers = fetcher_response_headers();
+ std::string value;
+ bool have_dict_header =
+ headers->EnumerateHeader(NULL, "Get-Dictionary", &value);
+ EXPECT_TRUE(have_dict_header);
+ if (!have_dict_header) return false;
mef 2014/07/23 18:34:29 nit: Should this be ASSERT instead as we exit anyw
Randy Smith (Not in Mondays) 2014/07/29 23:44:20 So this is an annoying test pattern that I'd be ha
mef 2014/07/30 17:14:58 Acknowledged.
+
+ // If the above didn't result in a dictionary fetch being queued, the
+ // rest of the test will time out. Avoid that.
+ int num_fetches = GetNumberOfDictionaryFetches(browser->profile());
+ EXPECT_EQ(1, num_fetches);
+ if (1 != num_fetches) return false;
+
+ // Wait until the dictionary fetch actually happens.
+ RequestVector request_vector;
+ WaitAndGetTestVector(2, &request_vector);
+ EXPECT_EQ(request_vector[1].relative_url, "/dict");
+ if (request_vector[1].relative_url != "/dict") return false;
mef 2014/07/23 18:34:28 Maybe making "/dict" and "/data" explicit constant
Randy Smith (Not in Mondays) 2014/07/29 23:44:21 Done.
+
+ // Do a round trip to the server ignoring the encoding, presuming
+ // that if we've gotten data to this thread, the dictionary's made
+ // it into the SdchManager.
+ data_gotten = GetDataDetailed(
+ browser->profile()->GetRequestContext(), false, &sdch_encoding_used);
+ EXPECT_TRUE(data_gotten);
+ if (!data_gotten) return false;
+
+ // Now data fetches should be SDCH encoded.
+ sdch_encoding_used = false;
+ data_gotten = GetDataDetailed(
+ browser->profile()->GetRequestContext(), false, &sdch_encoding_used);
+ EXPECT_TRUE(data_gotten);
+ EXPECT_TRUE(sdch_encoding_used);
+
+ if (!data_gotten || !sdch_encoding_used) return false;
+
+ // Confirm the request vector looks at this point as expected.
+ WaitAndGetTestVector(4, &request_vector);
+ EXPECT_EQ(4u, request_vector.size());
+ EXPECT_EQ(request_vector[2].relative_url, "/data");
+ EXPECT_EQ(request_vector[3].relative_url, "/data");
+ return (4u == request_vector.size() &&
+ request_vector[2].relative_url == "/data" &&
+ request_vector[3].relative_url == "/data");
+ }
+
+ private:
+ // InProcessBrowserTest
+ virtual void SetUpCommandLine(base::CommandLine* command_line) OVERRIDE {
+ command_line->AppendSwitchASCII(
+ switches::kHostResolverRules,
+ "MAP " + std::string(kTestHost) + " 127.0.0.1");
+ }
+
+ virtual void SetUpOnMainThread() OVERRIDE {
+ test_server_.RegisterRequestHandler(
+ base::Bind(&SdchResponseHandler::HandleRequest,
+ base::Unretained(&response_handler_)));
+ CHECK(test_server_.InitializeAndWaitUntilReady());
+ url_request_context_getter_ = browser()->profile()->GetRequestContext();
+ }
+
+ virtual void TearDownOnMainThread() OVERRIDE {
+ CHECK(test_server_.ShutdownAndWaitUntilComplete());
+ }
+
+ static void NukeSdchDictionariesOnIOThread(
mef 2014/07/23 18:34:29 I presume these are not overrides from InProcessBr
Randy Smith (Not in Mondays) 2014/07/29 23:44:21 Done.
+ net::URLRequestContextGetter* context_getter) {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
+ net::SdchManager* sdch_manager =
+ context_getter->GetURLRequestContext()->sdch_manager();
+ DCHECK(sdch_manager);
+ sdch_manager->ClearData();
+ }
+
+ static void GetNumberOfDictionaryFetchesOnIOThread(
+ net::URLRequestContextGetter* url_request_context_getter,
+ int* result) {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
+ net::SdchManager* sdch_manager =
+ url_request_context_getter->GetURLRequestContext()->sdch_manager();
+ DCHECK(sdch_manager);
+ *result = sdch_manager->GetFetchesRequestedForTesting();
+ }
+
+ // URLFetcherDelegate
+ virtual void OnURLFetchComplete(const net::URLFetcher* source) OVERRIDE {
+ fetcher_status_ = source->GetStatus();
+ if (fetcher_status_.status() == net::URLRequestStatus::SUCCESS) {
+ fetcher_response_code_ = source->GetResponseCode();
mef 2014/07/23 18:34:29 Do we really need to copy them into this class?
Randy Smith (Not in Mondays) 2014/07/29 23:44:20 Probably not, given that we seem to always check t
+ fetcher_response_headers_ = source->GetResponseHeaders();
+ CHECK(source->GetResponseAsString(&fetcher_response_contents_));
+ }
+ url_fetch_complete_ = true;
+ if (waiting_)
+ base::MessageLoopForUI::current()->Quit();
+ }
+
+ SdchResponseHandler response_handler_;
+ net::test_server::EmbeddedTestServer test_server_;
+ scoped_refptr<net::URLRequestContextGetter> url_request_context_getter_;
+ scoped_ptr<net::URLFetcher> fetcher_;
+ net::URLRequestStatus fetcher_status_;
+ int fetcher_response_code_;
+ net::HttpResponseHeaders* fetcher_response_headers_;
+ std::string fetcher_response_contents_;
+ bool url_fetch_complete_;
+ bool waiting_;
+ base::ScopedTempDir second_profile_data_dir_;
mef 2014/07/23 18:34:28 Would it get deleted if profile is still open?
Randy Smith (Not in Mondays) 2014/07/29 23:44:20 So this was a tricky question to track down, but:
mef 2014/07/30 17:14:58 Acknowledged.
+};
+
+const char SdchBrowserTest::kTestHost[] = "our.test.host.com";;
mef 2014/07/23 18:34:29 nit: ;;
Randy Smith (Not in Mondays) 2014/07/29 23:44:20 Done.
+
+// Confirm that after getting a dictionary, calling the browsing
+// data remover renders it unusable. Also (in calling
+// ForceSdchDictionaryLoad()) servers as a smoke test for SDCH.
+IN_PROC_BROWSER_TEST_F(SdchBrowserTest, BrowsingDataRemover) {
+ ASSERT_TRUE(ForceSdchDictionaryLoad(browser()));
+
+ // Confirm browsing data remover without removing the cache leaves
+ // SDCH alone.
+ BrowsingDataRemoveAndWait(BrowsingDataRemover::REMOVE_ALL &
+ ~BrowsingDataRemover::REMOVE_CACHE);
+ bool sdch_encoding_used = false;
+ ASSERT_TRUE(GetData(&sdch_encoding_used));
+ EXPECT_TRUE(sdch_encoding_used);
+
+ // Confirm browsing data remover removing the cache clears SDCH state.
+ BrowsingDataRemoveAndWait(BrowsingDataRemover::REMOVE_CACHE);
+ sdch_encoding_used = false;
+ ASSERT_TRUE(GetData(&sdch_encoding_used));
+ EXPECT_FALSE(sdch_encoding_used);
+}
+
+// Confirm dictionaries not visible in other profiles.
+IN_PROC_BROWSER_TEST_F(SdchBrowserTest, Isolation) {
+ ASSERT_TRUE(ForceSdchDictionaryLoad(browser()));
+
+ // Data fetches from incognito or separate profiles should not be SDCH
+ // encoded.
+ bool sdch_encoding_used = true;
+ Browser* incognito_browser = CreateIncognitoBrowser();
+ EXPECT_TRUE(GetDataDetailed(
mef 2014/07/23 18:34:29 nit: Could use GetData() instead. Also below.
Randy Smith (Not in Mondays) 2014/07/29 23:44:20 Why? I'm not seeing it--I want to test the fetch
mef 2014/07/30 17:14:58 nm, I've missed that GetData() doesn't take the co
+ incognito_browser->profile()->GetRequestContext(),
+ false,
+ &sdch_encoding_used));
+ EXPECT_FALSE(sdch_encoding_used);
+
+ Browser* new_browser = CreateBrowserOnProfile(CreateSecondProfile());
+ sdch_encoding_used = true;
+ EXPECT_TRUE(GetDataDetailed(
+ new_browser->profile()->GetRequestContext(),
+ false,
+ &sdch_encoding_used));
+ EXPECT_FALSE(sdch_encoding_used);
+}
+
+// Confirm a dictionary loaded in incognito isn't visible in the main profile.
+IN_PROC_BROWSER_TEST_F(SdchBrowserTest, ReverseIsolation) {
+ Browser* incognito_browser = CreateIncognitoBrowser();
+ ASSERT_TRUE(ForceSdchDictionaryLoad(incognito_browser));
+
+ // Data fetches on main browser should not be SDCH encoded.
+ bool sdch_encoding_used = true;
+ ASSERT_TRUE(GetData(&sdch_encoding_used));
+ EXPECT_FALSE(sdch_encoding_used);
mef 2014/07/23 18:34:28 Could it happen that SDCH dictionary is left over
Randy Smith (Not in Mondays) 2014/07/29 23:44:21 I believe we create and destroy a new profile dire
mef 2014/07/30 17:14:58 Acknowledged.
+}
+
+} // namespace

Powered by Google App Engine
This is Rietveld 408576698