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/internal_api/public/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
);
103 // OAuth2TokenService::TokenServiceProvider implementation.
104 virtual scoped_refptr
<base::SingleThreadTaskRunner
>
105 GetTokenServiceTaskRunner() OVERRIDE
;
106 virtual OAuth2TokenService
* GetTokenService() OVERRIDE
;
109 virtual ~TokenServiceProvider();
111 scoped_refptr
<base::SingleThreadTaskRunner
> task_runner_
;
112 OAuth2TokenService
* token_service_
;
115 TokenServiceProvider::TokenServiceProvider(OAuth2TokenService
* token_service
)
116 : task_runner_(base::ThreadTaskRunnerHandle::Get()),
117 token_service_(token_service
) {
118 DCHECK(token_service_
);
121 TokenServiceProvider::~TokenServiceProvider() {
124 scoped_refptr
<base::SingleThreadTaskRunner
>
125 TokenServiceProvider::GetTokenServiceTaskRunner() {
129 OAuth2TokenService
* TokenServiceProvider::GetTokenService() {
130 DCHECK(task_runner_
->BelongsToCurrentThread());
131 return token_service_
;
136 class AttachmentDownloaderImplTest
: public testing::Test
{
138 typedef std::map
<AttachmentId
, AttachmentDownloader::DownloadResult
>
141 AttachmentDownloaderImplTest() : num_completed_downloads_(0) {}
143 virtual void SetUp() OVERRIDE
;
144 virtual void TearDown() OVERRIDE
;
146 AttachmentDownloader
* downloader() { return attachment_downloader_
.get(); }
148 MockOAuth2TokenService
* token_service() { return token_service_
.get(); }
150 int num_completed_downloads() { return num_completed_downloads_
; }
152 AttachmentDownloader::DownloadCallback
download_callback(
153 const AttachmentId
& id
) {
154 return base::Bind(&AttachmentDownloaderImplTest::DownloadDone
,
155 base::Unretained(this),
159 void CompleteDownload(int response_code
);
161 void DownloadDone(const AttachmentId
& attachment_id
,
162 const AttachmentDownloader::DownloadResult
& result
,
163 scoped_ptr
<Attachment
> attachment
);
165 void VerifyDownloadResult(const AttachmentId
& attachment_id
,
166 const AttachmentDownloader::DownloadResult
& result
);
168 void RunMessageLoop();
171 base::MessageLoopForIO message_loop_
;
172 scoped_refptr
<net::URLRequestContextGetter
> url_request_context_getter_
;
173 net::TestURLFetcherFactory url_fetcher_factory_
;
174 scoped_ptr
<MockOAuth2TokenService
> token_service_
;
175 scoped_ptr
<AttachmentDownloader
> attachment_downloader_
;
176 ResultsMap download_results_
;
177 int num_completed_downloads_
;
180 void AttachmentDownloaderImplTest::SetUp() {
181 url_request_context_getter_
=
182 new net::TestURLRequestContextGetter(message_loop_
.message_loop_proxy());
183 url_fetcher_factory_
.set_remove_fetcher_on_delete(true);
184 token_service_
.reset(new MockOAuth2TokenService());
185 token_service_
->AddAccount(kAccountId
);
186 scoped_refptr
<OAuth2TokenServiceRequest::TokenServiceProvider
>
187 token_service_provider(new TokenServiceProvider(token_service_
.get()));
189 OAuth2TokenService::ScopeSet scopes
;
190 scopes
.insert(GaiaConstants::kChromeSyncOAuth2Scope
);
191 attachment_downloader_
=
192 AttachmentDownloader::Create(GURL(kAttachmentServerUrl
),
193 url_request_context_getter_
,
196 token_service_provider
);
199 void AttachmentDownloaderImplTest::TearDown() {
203 void AttachmentDownloaderImplTest::CompleteDownload(int response_code
) {
204 // TestURLFetcherFactory remembers last active URLFetcher.
205 net::TestURLFetcher
* fetcher
= url_fetcher_factory_
.GetFetcherByID(0);
206 // There should be outstanding url fetch request.
207 EXPECT_TRUE(fetcher
!= NULL
);
208 fetcher
->set_status(net::URLRequestStatus());
209 fetcher
->set_response_code(response_code
);
210 if (response_code
== net::HTTP_OK
) {
211 fetcher
->SetResponseString(kAttachmentContent
);
213 // Call URLFetcherDelegate.
214 net::URLFetcherDelegate
* delegate
= fetcher
->delegate();
215 delegate
->OnURLFetchComplete(fetcher
);
217 // Once result is processed URLFetcher should be deleted.
218 fetcher
= url_fetcher_factory_
.GetFetcherByID(0);
219 EXPECT_TRUE(fetcher
== NULL
);
222 void AttachmentDownloaderImplTest::DownloadDone(
223 const AttachmentId
& attachment_id
,
224 const AttachmentDownloader::DownloadResult
& result
,
225 scoped_ptr
<Attachment
> attachment
) {
226 download_results_
.insert(std::make_pair(attachment_id
, result
));
227 if (result
== AttachmentDownloader::DOWNLOAD_SUCCESS
) {
228 // Successful download should be accompanied by valid attachment with
229 // matching id and valid data.
230 EXPECT_TRUE(attachment
!= NULL
);
231 EXPECT_EQ(attachment_id
, attachment
->GetId());
233 scoped_refptr
<base::RefCountedMemory
> data
= attachment
->GetData();
234 std::string
data_as_string(data
->front_as
<char>(), data
->size());
235 EXPECT_EQ(data_as_string
, kAttachmentContent
);
237 EXPECT_TRUE(attachment
== NULL
);
239 ++num_completed_downloads_
;
242 void AttachmentDownloaderImplTest::VerifyDownloadResult(
243 const AttachmentId
& attachment_id
,
244 const AttachmentDownloader::DownloadResult
& result
) {
245 ResultsMap::const_iterator iter
= download_results_
.find(attachment_id
);
246 EXPECT_TRUE(iter
!= download_results_
.end());
247 EXPECT_EQ(iter
->second
, result
);
250 void AttachmentDownloaderImplTest::RunMessageLoop() {
251 base::RunLoop run_loop
;
252 run_loop
.RunUntilIdle();
255 TEST_F(AttachmentDownloaderImplTest
, HappyCase
) {
256 AttachmentId id1
= AttachmentId::Create();
257 // DownloadAttachment should trigger RequestAccessToken.
258 downloader()->DownloadAttachment(id1
, download_callback(id1
));
260 // Return valid access token.
261 token_service()->RespondToAccessTokenRequest(
262 GoogleServiceAuthError::AuthErrorNone());
264 // Check that there is outstanding URLFetcher request and complete it.
265 CompleteDownload(net::HTTP_OK
);
266 // Verify that callback was called for the right id with the right result.
267 VerifyDownloadResult(id1
, AttachmentDownloader::DOWNLOAD_SUCCESS
);
270 TEST_F(AttachmentDownloaderImplTest
, SameIdMultipleDownloads
) {
271 AttachmentId id1
= AttachmentId::Create();
272 // Call DownloadAttachment two times for the same id.
273 downloader()->DownloadAttachment(id1
, download_callback(id1
));
274 downloader()->DownloadAttachment(id1
, download_callback(id1
));
276 // Return valid access token.
277 token_service()->RespondToAccessTokenRequest(
278 GoogleServiceAuthError::AuthErrorNone());
280 // Start one more download after access token is received.
281 downloader()->DownloadAttachment(id1
, download_callback(id1
));
282 // Complete URLFetcher request.
283 CompleteDownload(net::HTTP_OK
);
284 // Verify that all download requests completed.
285 VerifyDownloadResult(id1
, AttachmentDownloader::DOWNLOAD_SUCCESS
);
286 EXPECT_EQ(3, num_completed_downloads());
288 // Let's download the same attachment again.
289 downloader()->DownloadAttachment(id1
, download_callback(id1
));
291 // Verify that it didn't finish prematurely.
292 EXPECT_EQ(3, num_completed_downloads());
293 // Return valid access token.
294 token_service()->RespondToAccessTokenRequest(
295 GoogleServiceAuthError::AuthErrorNone());
297 // Complete URLFetcher request.
298 CompleteDownload(net::HTTP_OK
);
299 // Verify that all download requests completed.
300 VerifyDownloadResult(id1
, AttachmentDownloader::DOWNLOAD_SUCCESS
);
301 EXPECT_EQ(4, num_completed_downloads());
304 TEST_F(AttachmentDownloaderImplTest
, RequestAccessTokenFails
) {
305 AttachmentId id1
= AttachmentId::Create();
306 AttachmentId id2
= AttachmentId::Create();
307 // Trigger first RequestAccessToken.
308 downloader()->DownloadAttachment(id1
, download_callback(id1
));
310 // Return valid access token.
311 token_service()->RespondToAccessTokenRequest(
312 GoogleServiceAuthError::AuthErrorNone());
314 // Trigger second RequestAccessToken.
315 downloader()->DownloadAttachment(id2
, download_callback(id2
));
317 // Fail RequestAccessToken.
318 token_service()->RespondToAccessTokenRequest(
319 GoogleServiceAuthError(GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS
));
321 // Only id2 should fail.
322 VerifyDownloadResult(id2
, AttachmentDownloader::DOWNLOAD_UNSPECIFIED_ERROR
);
323 // Complete request for id1.
324 CompleteDownload(net::HTTP_OK
);
325 VerifyDownloadResult(id1
, AttachmentDownloader::DOWNLOAD_SUCCESS
);
328 TEST_F(AttachmentDownloaderImplTest
, URLFetcher_BadToken
) {
329 AttachmentId id1
= AttachmentId::Create();
330 downloader()->DownloadAttachment(id1
, download_callback(id1
));
332 // Return valid access token.
333 token_service()->RespondToAccessTokenRequest(
334 GoogleServiceAuthError::AuthErrorNone());
336 // Fail URLFetcher. This should trigger download failure and access token
338 CompleteDownload(net::HTTP_UNAUTHORIZED
);
339 EXPECT_EQ(1, token_service()->num_invalidate_token());
340 VerifyDownloadResult(id1
, AttachmentDownloader::DOWNLOAD_UNSPECIFIED_ERROR
);
343 TEST_F(AttachmentDownloaderImplTest
, URLFetcher_ServiceUnavailable
) {
344 AttachmentId id1
= AttachmentId::Create();
345 downloader()->DownloadAttachment(id1
, download_callback(id1
));
347 // Return valid access token.
348 token_service()->RespondToAccessTokenRequest(
349 GoogleServiceAuthError::AuthErrorNone());
351 // Fail URLFetcher. This should trigger download failure. Access token
352 // shouldn't be invalidated.
353 CompleteDownload(net::HTTP_SERVICE_UNAVAILABLE
);
354 EXPECT_EQ(0, token_service()->num_invalidate_token());
355 VerifyDownloadResult(id1
, AttachmentDownloader::DOWNLOAD_UNSPECIFIED_ERROR
);
358 } // namespace syncer