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.
5 #include "sync/api/attachments/attachment_downloader.h"
8 #include "base/memory/weak_ptr.h"
9 #include "base/message_loop/message_loop.h"
10 #include "base/run_loop.h"
11 #include "base/thread_task_runner_handle.h"
12 #include "google_apis/gaia/fake_oauth2_token_service.h"
13 #include "google_apis/gaia/gaia_constants.h"
14 #include "net/url_request/test_url_fetcher_factory.h"
15 #include "net/url_request/url_request_test_util.h"
16 #include "sync/api/attachments/attachment.h"
17 #include "testing/gtest/include/gtest/gtest.h"
23 const char kAccountId
[] = "attachments@gmail.com";
24 const char kAccessToken
[] = "access.token";
25 const char kAttachmentServerUrl
[] = "http://attachments.com/";
26 const char kAttachmentContent
[] = "attachment.content";
28 // MockOAuth2TokenService remembers last request for access token and verifies
29 // that only one request is active at a time.
30 // Call RespondToAccessTokenRequest to respond to it.
31 class MockOAuth2TokenService
: public FakeOAuth2TokenService
{
33 MockOAuth2TokenService() : num_invalidate_token_(0) {}
35 virtual ~MockOAuth2TokenService() {}
37 void RespondToAccessTokenRequest(GoogleServiceAuthError error
);
39 int num_invalidate_token() const { return num_invalidate_token_
; }
42 virtual void FetchOAuth2Token(RequestImpl
* request
,
43 const std::string
& account_id
,
44 net::URLRequestContextGetter
* getter
,
45 const std::string
& client_id
,
46 const std::string
& client_secret
,
47 const ScopeSet
& scopes
) OVERRIDE
;
49 virtual void InvalidateOAuth2Token(const std::string
& account_id
,
50 const std::string
& client_id
,
51 const ScopeSet
& scopes
,
52 const std::string
& access_token
) OVERRIDE
;
55 base::WeakPtr
<RequestImpl
> last_request_
;
56 int num_invalidate_token_
;
59 void MockOAuth2TokenService::RespondToAccessTokenRequest(
60 GoogleServiceAuthError error
) {
61 EXPECT_TRUE(last_request_
!= NULL
);
62 std::string access_token
;
63 base::Time expiration_time
;
64 if (error
== GoogleServiceAuthError::AuthErrorNone()) {
65 access_token
= kAccessToken
;
66 expiration_time
= base::Time::Max();
68 base::MessageLoop::current()->PostTask(
70 base::Bind(&OAuth2TokenService::RequestImpl::InformConsumer
,
77 void MockOAuth2TokenService::FetchOAuth2Token(
79 const std::string
& account_id
,
80 net::URLRequestContextGetter
* getter
,
81 const std::string
& client_id
,
82 const std::string
& client_secret
,
83 const ScopeSet
& scopes
) {
84 // Only one request at a time is allowed.
85 EXPECT_TRUE(last_request_
== NULL
);
86 last_request_
= request
->AsWeakPtr();
89 void MockOAuth2TokenService::InvalidateOAuth2Token(
90 const std::string
& account_id
,
91 const std::string
& client_id
,
92 const ScopeSet
& scopes
,
93 const std::string
& access_token
) {
94 ++num_invalidate_token_
;
97 class TokenServiceProvider
98 : public OAuth2TokenServiceRequest::TokenServiceProvider
,
101 TokenServiceProvider(OAuth2TokenService
* token_service
);
102 virtual ~TokenServiceProvider();
104 // OAuth2TokenService::TokenServiceProvider implementation.
105 virtual scoped_refptr
<base::SingleThreadTaskRunner
>
106 GetTokenServiceTaskRunner() OVERRIDE
;
107 virtual OAuth2TokenService
* GetTokenService() OVERRIDE
;
110 scoped_refptr
<base::SingleThreadTaskRunner
> task_runner_
;
111 OAuth2TokenService
* token_service_
;
114 TokenServiceProvider::TokenServiceProvider(OAuth2TokenService
* token_service
)
115 : task_runner_(base::ThreadTaskRunnerHandle::Get()),
116 token_service_(token_service
) {
117 DCHECK(token_service_
);
120 TokenServiceProvider::~TokenServiceProvider() {
123 scoped_refptr
<base::SingleThreadTaskRunner
>
124 TokenServiceProvider::GetTokenServiceTaskRunner() {
128 OAuth2TokenService
* TokenServiceProvider::GetTokenService() {
129 DCHECK(task_runner_
->BelongsToCurrentThread());
130 return token_service_
;
135 class AttachmentDownloaderImplTest
: public testing::Test
{
137 typedef std::map
<AttachmentId
, AttachmentDownloader::DownloadResult
>
140 AttachmentDownloaderImplTest() : num_completed_downloads_(0) {}
142 virtual void SetUp() OVERRIDE
;
143 virtual void TearDown() OVERRIDE
;
145 AttachmentDownloader
* downloader() { return attachment_downloader_
.get(); }
147 MockOAuth2TokenService
* token_service() { return token_service_
.get(); }
149 int num_completed_downloads() { return num_completed_downloads_
; }
151 AttachmentDownloader::DownloadCallback
download_callback(
152 const AttachmentId
& id
) {
153 return base::Bind(&AttachmentDownloaderImplTest::DownloadDone
,
154 base::Unretained(this),
158 void CompleteDownload(int response_code
);
160 void DownloadDone(const AttachmentId
& attachment_id
,
161 const AttachmentDownloader::DownloadResult
& result
,
162 scoped_ptr
<Attachment
> attachment
);
164 void VerifyDownloadResult(const AttachmentId
& attachment_id
,
165 const AttachmentDownloader::DownloadResult
& result
);
167 void RunMessageLoop();
170 base::MessageLoopForIO message_loop_
;
171 scoped_refptr
<net::URLRequestContextGetter
> url_request_context_getter_
;
172 net::TestURLFetcherFactory url_fetcher_factory_
;
173 scoped_ptr
<MockOAuth2TokenService
> token_service_
;
174 scoped_ptr
<AttachmentDownloader
> attachment_downloader_
;
175 ResultsMap download_results_
;
176 int num_completed_downloads_
;
179 void AttachmentDownloaderImplTest::SetUp() {
180 url_request_context_getter_
=
181 new net::TestURLRequestContextGetter(message_loop_
.message_loop_proxy());
182 url_fetcher_factory_
.set_remove_fetcher_on_delete(true);
183 token_service_
.reset(new MockOAuth2TokenService());
184 token_service_
->AddAccount(kAccountId
);
185 scoped_ptr
<OAuth2TokenServiceRequest::TokenServiceProvider
>
186 token_service_provider(new TokenServiceProvider(token_service_
.get()));
188 OAuth2TokenService::ScopeSet scopes
;
189 scopes
.insert(GaiaConstants::kChromeSyncOAuth2Scope
);
190 attachment_downloader_
=
191 AttachmentDownloader::Create(GURL(kAttachmentServerUrl
),
192 url_request_context_getter_
,
195 token_service_provider
.Pass());
198 void AttachmentDownloaderImplTest::TearDown() {
202 void AttachmentDownloaderImplTest::CompleteDownload(int response_code
) {
203 // TestURLFetcherFactory remembers last active URLFetcher.
204 net::TestURLFetcher
* fetcher
= url_fetcher_factory_
.GetFetcherByID(0);
205 // There should be outstanding url fetch request.
206 EXPECT_TRUE(fetcher
!= NULL
);
207 fetcher
->set_status(net::URLRequestStatus());
208 fetcher
->set_response_code(response_code
);
209 if (response_code
== net::HTTP_OK
) {
210 fetcher
->SetResponseString(kAttachmentContent
);
212 // Call URLFetcherDelegate.
213 net::URLFetcherDelegate
* delegate
= fetcher
->delegate();
214 delegate
->OnURLFetchComplete(fetcher
);
216 // Once result is processed URLFetcher should be deleted.
217 fetcher
= url_fetcher_factory_
.GetFetcherByID(0);
218 EXPECT_TRUE(fetcher
== NULL
);
221 void AttachmentDownloaderImplTest::DownloadDone(
222 const AttachmentId
& attachment_id
,
223 const AttachmentDownloader::DownloadResult
& result
,
224 scoped_ptr
<Attachment
> attachment
) {
225 download_results_
.insert(std::make_pair(attachment_id
, result
));
226 if (result
== AttachmentDownloader::DOWNLOAD_SUCCESS
) {
227 // Successful download should be accompanied by valid attachment with
228 // matching id and valid data.
229 EXPECT_TRUE(attachment
!= NULL
);
230 EXPECT_EQ(attachment_id
, attachment
->GetId());
232 scoped_refptr
<base::RefCountedMemory
> data
= attachment
->GetData();
233 std::string
data_as_string(data
->front_as
<char>(), data
->size());
234 EXPECT_EQ(data_as_string
, kAttachmentContent
);
236 EXPECT_TRUE(attachment
== NULL
);
238 ++num_completed_downloads_
;
241 void AttachmentDownloaderImplTest::VerifyDownloadResult(
242 const AttachmentId
& attachment_id
,
243 const AttachmentDownloader::DownloadResult
& result
) {
244 ResultsMap::const_iterator iter
= download_results_
.find(attachment_id
);
245 EXPECT_TRUE(iter
!= download_results_
.end());
246 EXPECT_EQ(iter
->second
, result
);
249 void AttachmentDownloaderImplTest::RunMessageLoop() {
250 base::RunLoop run_loop
;
251 run_loop
.RunUntilIdle();
254 TEST_F(AttachmentDownloaderImplTest
, HappyCase
) {
255 AttachmentId id1
= AttachmentId::Create();
256 // DownloadAttachment should trigger RequestAccessToken.
257 downloader()->DownloadAttachment(id1
, download_callback(id1
));
259 // Return valid access token.
260 token_service()->RespondToAccessTokenRequest(
261 GoogleServiceAuthError::AuthErrorNone());
263 // Check that there is outstanding URLFetcher request and complete it.
264 CompleteDownload(net::HTTP_OK
);
265 // Verify that callback was called for the right id with the right result.
266 VerifyDownloadResult(id1
, AttachmentDownloader::DOWNLOAD_SUCCESS
);
269 TEST_F(AttachmentDownloaderImplTest
, SameIdMultipleDownloads
) {
270 AttachmentId id1
= AttachmentId::Create();
271 // Call DownloadAttachment two times for the same id.
272 downloader()->DownloadAttachment(id1
, download_callback(id1
));
273 downloader()->DownloadAttachment(id1
, download_callback(id1
));
275 // Return valid access token.
276 token_service()->RespondToAccessTokenRequest(
277 GoogleServiceAuthError::AuthErrorNone());
279 // Start one more download after access token is received.
280 downloader()->DownloadAttachment(id1
, download_callback(id1
));
281 // Complete URLFetcher request.
282 CompleteDownload(net::HTTP_OK
);
283 // Verify that all download requests completed.
284 VerifyDownloadResult(id1
, AttachmentDownloader::DOWNLOAD_SUCCESS
);
285 EXPECT_EQ(3, num_completed_downloads());
287 // Let's download the same attachment again.
288 downloader()->DownloadAttachment(id1
, download_callback(id1
));
290 // Verify that it didn't finish prematurely.
291 EXPECT_EQ(3, num_completed_downloads());
292 // Return valid access token.
293 token_service()->RespondToAccessTokenRequest(
294 GoogleServiceAuthError::AuthErrorNone());
296 // Complete URLFetcher request.
297 CompleteDownload(net::HTTP_OK
);
298 // Verify that all download requests completed.
299 VerifyDownloadResult(id1
, AttachmentDownloader::DOWNLOAD_SUCCESS
);
300 EXPECT_EQ(4, num_completed_downloads());
303 TEST_F(AttachmentDownloaderImplTest
, RequestAccessTokenFails
) {
304 AttachmentId id1
= AttachmentId::Create();
305 AttachmentId id2
= AttachmentId::Create();
306 // Trigger first RequestAccessToken.
307 downloader()->DownloadAttachment(id1
, download_callback(id1
));
309 // Return valid access token.
310 token_service()->RespondToAccessTokenRequest(
311 GoogleServiceAuthError::AuthErrorNone());
313 // Trigger second RequestAccessToken.
314 downloader()->DownloadAttachment(id2
, download_callback(id2
));
316 // Fail RequestAccessToken.
317 token_service()->RespondToAccessTokenRequest(
318 GoogleServiceAuthError(GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS
));
320 // Only id2 should fail.
321 VerifyDownloadResult(id2
, AttachmentDownloader::DOWNLOAD_UNSPECIFIED_ERROR
);
322 // Complete request for id1.
323 CompleteDownload(net::HTTP_OK
);
324 VerifyDownloadResult(id1
, AttachmentDownloader::DOWNLOAD_SUCCESS
);
327 TEST_F(AttachmentDownloaderImplTest
, URLFetcher_BadToken
) {
328 AttachmentId id1
= AttachmentId::Create();
329 downloader()->DownloadAttachment(id1
, download_callback(id1
));
331 // Return valid access token.
332 token_service()->RespondToAccessTokenRequest(
333 GoogleServiceAuthError::AuthErrorNone());
335 // Fail URLFetcher. This should trigger download failure and access token
337 CompleteDownload(net::HTTP_UNAUTHORIZED
);
338 EXPECT_EQ(1, token_service()->num_invalidate_token());
339 VerifyDownloadResult(id1
, AttachmentDownloader::DOWNLOAD_UNSPECIFIED_ERROR
);
342 TEST_F(AttachmentDownloaderImplTest
, URLFetcher_ServiceUnavailable
) {
343 AttachmentId id1
= AttachmentId::Create();
344 downloader()->DownloadAttachment(id1
, download_callback(id1
));
346 // Return valid access token.
347 token_service()->RespondToAccessTokenRequest(
348 GoogleServiceAuthError::AuthErrorNone());
350 // Fail URLFetcher. This should trigger download failure. Access token
351 // shouldn't be invalidated.
352 CompleteDownload(net::HTTP_SERVICE_UNAVAILABLE
);
353 EXPECT_EQ(0, token_service()->num_invalidate_token());
354 VerifyDownloadResult(id1
, AttachmentDownloader::DOWNLOAD_UNSPECIFIED_ERROR
);
357 } // namespace syncer