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.
7 #include "base/callback.h"
8 #include "base/json/json_writer.h"
9 #include "base/memory/scoped_ptr.h"
10 #include "base/message_loop/message_loop.h"
11 #include "base/thread_task_runner_handle.h"
12 #include "base/values.h"
13 #include "chrome/browser/supervised_user/experimental/supervised_user_async_url_checker.h"
14 #include "net/base/net_errors.h"
15 #include "net/url_request/test_url_fetcher_factory.h"
16 #include "net/url_request/url_request_test_util.h"
17 #include "testing/gmock/include/gmock/gmock.h"
18 #include "testing/gtest/include/gtest/gtest.h"
25 const size_t kCacheSize
= 2;
27 const int kSupervisedUserAsyncURLCheckerSafeURLFetcherID
= 0;
28 const int kSupervisedUserAsyncURLCheckerUnsafeURLFetcherID
= 1;
30 const char* kURLs
[] = {
31 "http://www.randomsite1.com",
32 "http://www.randomsite2.com",
33 "http://www.randomsite3.com",
34 "http://www.randomsite4.com",
35 "http://www.randomsite5.com",
36 "http://www.randomsite6.com",
37 "http://www.randomsite7.com",
38 "http://www.randomsite8.com",
39 "http://www.randomsite9.com",
42 std::string
BuildResponse(const GURL
& url
) {
43 base::DictionaryValue dict
;
44 base::DictionaryValue
* search_info_dict
= new base::DictionaryValue
;
45 std::string result_count
= url
.is_valid() ? "1" : "0";
46 search_info_dict
->SetStringWithoutPathExpansion("totalResults",
48 dict
.SetWithoutPathExpansion("searchInformation", search_info_dict
);
49 if (result_count
!= "0") {
50 base::ListValue
* results_list
= new base::ListValue
;
51 base::DictionaryValue
* result_dict
= new base::DictionaryValue
;
52 result_dict
->SetStringWithoutPathExpansion("link", url
.spec());
53 results_list
->Append(result_dict
);
54 dict
.SetWithoutPathExpansion("items", results_list
);
57 base::JSONWriter::Write(dict
, &result
);
63 class SupervisedUserAsyncURLCheckerTest
: public testing::Test
{
65 SupervisedUserAsyncURLCheckerTest()
67 request_context_(new net::TestURLRequestContextGetter(
68 base::ThreadTaskRunnerHandle::Get())),
69 checker_(request_context_
.get(), kCacheSize
) {}
71 MOCK_METHOD3(OnCheckDone
,
73 SupervisedUserURLFilter::FilteringBehavior behavior
,
78 CHECK(next_url_
< arraysize(kURLs
));
79 return GURL(kURLs
[next_url_
++]);
82 // Returns true if the result was returned synchronously (cache hit).
83 bool CheckURL(const GURL
& url
) {
84 return checker_
.CheckURL(
86 base::Bind(&SupervisedUserAsyncURLCheckerTest::OnCheckDone
,
87 base::Unretained(this)));
90 net::TestURLFetcher
* GetURLFetcher(bool safe
) {
91 int id
= safe
? kSupervisedUserAsyncURLCheckerSafeURLFetcherID
92 : kSupervisedUserAsyncURLCheckerUnsafeURLFetcherID
;
93 net::TestURLFetcher
* url_fetcher
= url_fetcher_factory_
.GetFetcherByID(id
);
94 EXPECT_TRUE(url_fetcher
);
98 void SendResponse(bool safe
, net::Error error
, const std::string
& response
) {
99 net::TestURLFetcher
* url_fetcher
= GetURLFetcher(safe
);
100 url_fetcher
->set_status(net::URLRequestStatus::FromError(error
));
101 url_fetcher
->set_response_code(net::HTTP_OK
);
102 url_fetcher
->SetResponseString(response
);
103 url_fetcher
->delegate()->OnURLFetchComplete(url_fetcher
);
106 void SendValidResponse(bool safe
, const GURL
& url
) {
107 SendResponse(safe
, net::OK
, BuildResponse(url
));
110 void SendFailedResponse(bool safe
) {
111 SendResponse(safe
, net::ERR_ABORTED
, std::string());
115 base::MessageLoop message_loop_
;
116 scoped_refptr
<net::TestURLRequestContextGetter
> request_context_
;
117 net::TestURLFetcherFactory url_fetcher_factory_
;
118 SupervisedUserAsyncURLChecker checker_
;
121 TEST_F(SupervisedUserAsyncURLCheckerTest
, Simple
) {
123 GURL
url(GetNewURL());
124 EXPECT_FALSE(CheckURL(url
));
125 // "URL found" response from safe fetcher should immediately give a
126 // "not blocked" result.
127 EXPECT_CALL(*this, OnCheckDone(url
, SupervisedUserURLFilter::ALLOW
, false));
128 SendValidResponse(true, url
);
131 GURL
url(GetNewURL());
132 EXPECT_FALSE(CheckURL(url
));
133 // "URL not found" response from safe fetcher should not immediately give a
135 EXPECT_CALL(*this, OnCheckDone(_
, _
, _
)).Times(0);
136 SendValidResponse(true, GURL());
137 // "URL found" response from unsafe fetcher should give a "blocked" result.
138 EXPECT_CALL(*this, OnCheckDone(url
, SupervisedUserURLFilter::BLOCK
, false));
139 SendValidResponse(false, url
);
142 GURL
url(GetNewURL());
143 EXPECT_FALSE(CheckURL(url
));
144 // "URL found" response from unsafe fetcher should not immediately give a
146 EXPECT_CALL(*this, OnCheckDone(_
, _
, _
)).Times(0);
147 SendValidResponse(false, url
);
148 // "URL not found" response from safe fetcher should give a "blocked"
150 EXPECT_CALL(*this, OnCheckDone(url
, SupervisedUserURLFilter::BLOCK
, false));
151 SendValidResponse(true, GURL());
154 GURL
url(GetNewURL());
155 EXPECT_FALSE(CheckURL(url
));
156 // "URL not found" response from unsafe fetcher should immediately give a
157 // "not blocked (but uncertain)" result.
158 EXPECT_CALL(*this, OnCheckDone(url
, SupervisedUserURLFilter::ALLOW
, true));
159 SendValidResponse(false, GURL());
163 TEST_F(SupervisedUserAsyncURLCheckerTest
, Equivalence
) {
164 // Leading "www." in the response should be ignored.
166 GURL
url("http://example.com");
167 GURL
url_response("http://www.example.com");
168 EXPECT_FALSE(CheckURL(url
));
169 EXPECT_CALL(*this, OnCheckDone(url
, SupervisedUserURLFilter::ALLOW
, false));
170 SendValidResponse(true, url_response
);
172 // Scheme should be ignored.
174 GURL
url("http://www.example2.com");
175 GURL
url_response("https://www.example2.com");
176 EXPECT_FALSE(CheckURL(url
));
177 EXPECT_CALL(*this, OnCheckDone(url
, SupervisedUserURLFilter::ALLOW
, false));
178 SendValidResponse(true, url_response
);
180 // Both at the same time should work as well.
182 GURL
url("http://example3.com");
183 GURL
url_response("https://www.example3.com");
184 EXPECT_FALSE(CheckURL(url
));
185 EXPECT_CALL(*this, OnCheckDone(url
, SupervisedUserURLFilter::ALLOW
, false));
186 SendValidResponse(true, url_response
);
190 TEST_F(SupervisedUserAsyncURLCheckerTest
, Cache
) {
191 // One more URL than fit in the cache.
192 ASSERT_EQ(2u, kCacheSize
);
193 GURL
url1(GetNewURL());
194 GURL
url2(GetNewURL());
195 GURL
url3(GetNewURL());
197 // Populate the cache.
198 EXPECT_FALSE(CheckURL(url1
));
199 EXPECT_CALL(*this, OnCheckDone(url1
, SupervisedUserURLFilter::ALLOW
, false));
200 SendValidResponse(true, url1
);
201 EXPECT_FALSE(CheckURL(url2
));
202 EXPECT_CALL(*this, OnCheckDone(url2
, SupervisedUserURLFilter::ALLOW
, false));
203 SendValidResponse(true, url2
);
205 // Now we should get results synchronously.
206 EXPECT_CALL(*this, OnCheckDone(url2
, SupervisedUserURLFilter::ALLOW
, false));
207 EXPECT_TRUE(CheckURL(url2
));
208 EXPECT_CALL(*this, OnCheckDone(url1
, SupervisedUserURLFilter::ALLOW
, false));
209 EXPECT_TRUE(CheckURL(url1
));
211 // Now |url2| is the LRU and should be evicted on the next check.
212 EXPECT_FALSE(CheckURL(url3
));
213 EXPECT_CALL(*this, OnCheckDone(url3
, SupervisedUserURLFilter::ALLOW
, false));
214 SendValidResponse(true, url3
);
216 EXPECT_FALSE(CheckURL(url2
));
217 EXPECT_CALL(*this, OnCheckDone(url2
, SupervisedUserURLFilter::ALLOW
, false));
218 SendValidResponse(true, url2
);
221 TEST_F(SupervisedUserAsyncURLCheckerTest
, CoalesceRequestsToSameURL
) {
222 GURL
url(GetNewURL());
223 // Start two checks for the same URL.
224 EXPECT_FALSE(CheckURL(url
));
225 EXPECT_FALSE(CheckURL(url
));
226 // A single response should answer both checks.
227 EXPECT_CALL(*this, OnCheckDone(url
, SupervisedUserURLFilter::ALLOW
, false))
229 SendValidResponse(true, url
);