1 // Copyright 2014 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "net/base/sdch_dictionary_fetcher.h" 6 7 #include <string> 8 9 #include "base/run_loop.h" 10 #include "base/strings/stringprintf.h" 11 #include "base/thread_task_runner_handle.h" 12 #include "net/base/sdch_manager.h" 13 #include "net/url_request/url_request_data_job.h" 14 #include "net/url_request/url_request_filter.h" 15 #include "net/url_request/url_request_interceptor.h" 16 #include "net/url_request/url_request_test_util.h" 17 #include "testing/gtest/include/gtest/gtest.h" 18 #include "url/gurl.h" 19 20 namespace net { 21 22 static const char* kSampleBufferContext = "This is a sample buffer."; 23 static const char* kTestDomain = "top.domain.test"; 24 25 class URLRequestSpecifiedResponseJob : public URLRequestSimpleJob { 26 public: 27 URLRequestSpecifiedResponseJob(URLRequest* request, 28 NetworkDelegate* network_delegate) 29 : URLRequestSimpleJob(request, network_delegate) {} 30 31 static void AddUrlHandler() { 32 net::URLRequestFilter* filter = net::URLRequestFilter::GetInstance(); 33 jobs_requested_ = 0; 34 filter->AddHostnameHandler("http", kTestDomain, 35 &URLRequestSpecifiedResponseJob::Factory); 36 } 37 38 static void RemoveUrlHandler() { 39 net::URLRequestFilter* filter = net::URLRequestFilter::GetInstance(); 40 filter->RemoveHostnameHandler("http", kTestDomain); 41 jobs_requested_ = 0; 42 } 43 44 static URLRequestJob* Factory( 45 URLRequest* request, 46 net::NetworkDelegate* network_delegate, 47 const std::string& scheme) { 48 ++jobs_requested_; 49 return new URLRequestSpecifiedResponseJob(request, network_delegate); 50 } 51 52 static std::string ExpectedResponseForURL(const GURL& url) { 53 return base::StringPrintf("Response for %s\n%s\nEnd Response for %s\n", 54 url.spec().c_str(), kSampleBufferContext, 55 url.spec().c_str()); 56 } 57 58 static int jobs_requested() { return jobs_requested_; } 59 60 private: 61 virtual ~URLRequestSpecifiedResponseJob() {}; 62 virtual int GetData(std::string* mime_type, 63 std::string* charset, 64 std::string* data, 65 const CompletionCallback& callback) const OVERRIDE { 66 GURL url(request_->url()); 67 *data = ExpectedResponseForURL(url); 68 return OK; 69 } 70 71 static int jobs_requested_; 72 }; 73 74 int URLRequestSpecifiedResponseJob::jobs_requested_(0); 75 76 class SdchTestDelegate : public SdchFetcher::Delegate { 77 public: 78 struct DictionaryAdditions { 79 DictionaryAdditions(const std::string& dictionary_text, 80 const GURL& dictionary_url) 81 : dictionary_text(dictionary_text), 82 dictionary_url(dictionary_url) {} 83 84 85 std::string dictionary_text; 86 GURL dictionary_url; 87 }; 88 89 virtual void AddSdchDictionary(const std::string& dictionary_text, 90 const GURL& dictionary_url) OVERRIDE { 91 dictionary_additions.push_back( 92 DictionaryAdditions(dictionary_text, dictionary_url)); 93 } 94 95 void GetDictionaryAdditions(std::vector<DictionaryAdditions>* out) { 96 out->swap(dictionary_additions); 97 dictionary_additions.clear(); 98 } 99 100 private: 101 std::vector<DictionaryAdditions> dictionary_additions; 102 }; 103 104 class SdchDictionaryFetcherTest : public ::testing::Test { 105 public: 106 SdchDictionaryFetcherTest() {} 107 108 virtual void SetUp() OVERRIDE { 109 DCHECK(!fetcher_.get()); 110 111 URLRequestSpecifiedResponseJob::AddUrlHandler(); 112 fetcher_delegate_.reset(new SdchTestDelegate); 113 context_.reset(new TestURLRequestContext); 114 fetcher_.reset(new SdchDictionaryFetcher( 115 fetcher_delegate_.get(), context_.get())); 116 } 117 118 virtual void TearDown() OVERRIDE { 119 URLRequestSpecifiedResponseJob::RemoveUrlHandler(); 120 fetcher_.reset(); 121 context_.reset(); 122 fetcher_delegate_.reset(); 123 } 124 125 SdchDictionaryFetcher* fetcher() { return fetcher_.get(); } 126 SdchTestDelegate* manager() { return fetcher_delegate_.get(); } 127 128 // May not be called outside the SetUp()/TearDown() interval. 129 int JobsRequested() { 130 return URLRequestSpecifiedResponseJob::jobs_requested(); 131 } 132 133 GURL PathToGurl(const char* path) { 134 std::string gurl_string("http://"); 135 gurl_string += kTestDomain; 136 gurl_string += "/"; 137 gurl_string += path; 138 return GURL(gurl_string); 139 } 140 141 private: 142 scoped_ptr<SdchTestDelegate> fetcher_delegate_; 143 scoped_ptr<TestURLRequestContext> context_; 144 scoped_ptr<SdchDictionaryFetcher> fetcher_; 145 }; 146 147 // Schedule a fetch and make sure it happens. 148 TEST_F(SdchDictionaryFetcherTest, Basic) { 149 GURL dictionary_url(PathToGurl("dictionary")); 150 fetcher()->Schedule(dictionary_url); 151 152 base::RunLoop().RunUntilIdle(); 153 EXPECT_EQ(1, JobsRequested()); 154 std::vector<SdchTestDelegate::DictionaryAdditions> additions; 155 manager()->GetDictionaryAdditions(&additions); 156 ASSERT_EQ(1u, additions.size()); 157 EXPECT_EQ(URLRequestSpecifiedResponseJob::ExpectedResponseForURL( 158 dictionary_url), additions[0].dictionary_text); 159 } 160 161 // Multiple fetches of the same URL should result in only one request. 162 TEST_F(SdchDictionaryFetcherTest, Multiple) { 163 GURL dictionary_url(PathToGurl("dictionary")); 164 fetcher()->Schedule(dictionary_url); 165 fetcher()->Schedule(dictionary_url); 166 fetcher()->Schedule(dictionary_url); 167 base::RunLoop().RunUntilIdle(); 168 169 EXPECT_EQ(1, JobsRequested()); 170 std::vector<SdchTestDelegate::DictionaryAdditions> additions; 171 manager()->GetDictionaryAdditions(&additions); 172 ASSERT_EQ(1u, additions.size()); 173 EXPECT_EQ(URLRequestSpecifiedResponseJob::ExpectedResponseForURL( 174 dictionary_url), additions[0].dictionary_text); 175 } 176 177 // A cancel should result in no actual requests being generated. 178 TEST_F(SdchDictionaryFetcherTest, Cancel) { 179 GURL dictionary_url_1(PathToGurl("dictionary_1")); 180 GURL dictionary_url_2(PathToGurl("dictionary_2")); 181 GURL dictionary_url_3(PathToGurl("dictionary_3")); 182 183 fetcher()->Schedule(dictionary_url_1); 184 fetcher()->Schedule(dictionary_url_2); 185 fetcher()->Schedule(dictionary_url_3); 186 fetcher()->Cancel(); 187 base::RunLoop().RunUntilIdle(); 188 189 // Synchronous execution may have resulted in a single job being scheduled. 190 EXPECT_GE(1, JobsRequested()); 191 } 192 193 } 194