1// Copyright (c) 2012 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 "chrome/browser/chromeos/drive/job_scheduler.h"
6
7#include <set>
8
9#include "base/bind.h"
10#include "base/files/file_util.h"
11#include "base/files/scoped_temp_dir.h"
12#include "base/prefs/testing_pref_service.h"
13#include "base/run_loop.h"
14#include "base/stl_util.h"
15#include "base/strings/stringprintf.h"
16#include "chrome/browser/chromeos/drive/test_util.h"
17#include "chrome/browser/drive/event_logger.h"
18#include "chrome/browser/drive/fake_drive_service.h"
19#include "chrome/browser/drive/test_util.h"
20#include "chrome/common/pref_names.h"
21#include "content/public/test/test_browser_thread_bundle.h"
22#include "google_apis/drive/drive_api_parser.h"
23#include "google_apis/drive/test_util.h"
24#include "testing/gtest/include/gtest/gtest.h"
25
26namespace drive {
27
28namespace {
29
30// Dummy value passed for the |expected_file_size| parameter of DownloadFile().
31const int64 kDummyDownloadFileSize = 0;
32
33void CopyTitleFromFileResourceCallback(
34    std::vector<std::string>* title_list_out,
35    google_apis::GDataErrorCode error_in,
36    scoped_ptr<google_apis::FileResource> entry_in) {
37  title_list_out->push_back(entry_in->title());
38}
39
40class JobListLogger : public JobListObserver {
41 public:
42  enum EventType {
43    ADDED,
44    UPDATED,
45    DONE,
46  };
47
48  struct EventLog {
49    EventType type;
50    JobInfo info;
51
52    EventLog(EventType type, const JobInfo& info) : type(type), info(info) {
53    }
54  };
55
56  // Checks whether the specified type of event has occurred.
57  bool Has(EventType type, JobType job_type) {
58    for (size_t i = 0; i < events.size(); ++i) {
59      if (events[i].type == type && events[i].info.job_type == job_type)
60        return true;
61    }
62    return false;
63  }
64
65  // Gets the progress event information of the specified type.
66  void GetProgressInfo(JobType job_type, std::vector<int64>* progress) {
67    for (size_t i = 0; i < events.size(); ++i) {
68      if (events[i].type == UPDATED && events[i].info.job_type == job_type)
69        progress->push_back(events[i].info.num_completed_bytes);
70    }
71  }
72
73  // JobListObserver overrides.
74  virtual void OnJobAdded(const JobInfo& info) OVERRIDE {
75    events.push_back(EventLog(ADDED, info));
76  }
77
78  virtual void OnJobUpdated(const JobInfo& info) OVERRIDE {
79    events.push_back(EventLog(UPDATED, info));
80  }
81
82  virtual void OnJobDone(const JobInfo& info, FileError error) OVERRIDE {
83    events.push_back(EventLog(DONE, info));
84  }
85
86 private:
87  std::vector<EventLog> events;
88};
89
90// Fake drive service extended for testing cancellation.
91// When upload_new_file_cancelable is set, this Drive service starts
92// returning a closure to cancel from InitiateUploadNewFile(). The task will
93// finish only when the cancel closure is called.
94class CancelTestableFakeDriveService : public FakeDriveService {
95 public:
96  CancelTestableFakeDriveService()
97      : upload_new_file_cancelable_(false) {
98  }
99
100  void set_upload_new_file_cancelable(bool cancelable) {
101    upload_new_file_cancelable_ = cancelable;
102  }
103
104  virtual google_apis::CancelCallback InitiateUploadNewFile(
105      const std::string& content_type,
106      int64 content_length,
107      const std::string& parent_resource_id,
108      const std::string& title,
109      const InitiateUploadNewFileOptions& options,
110      const google_apis::InitiateUploadCallback& callback) OVERRIDE {
111    if (upload_new_file_cancelable_)
112      return base::Bind(callback, google_apis::GDATA_CANCELLED, GURL());
113
114    return FakeDriveService::InitiateUploadNewFile(content_type,
115                                                   content_length,
116                                                   parent_resource_id,
117                                                   title,
118                                                   options,
119                                                   callback);
120  }
121
122 private:
123  bool upload_new_file_cancelable_;
124};
125
126}  // namespace
127
128class JobSchedulerTest : public testing::Test {
129 public:
130  JobSchedulerTest()
131      : pref_service_(new TestingPrefServiceSimple) {
132    test_util::RegisterDrivePrefs(pref_service_->registry());
133  }
134
135  virtual void SetUp() OVERRIDE {
136    fake_network_change_notifier_.reset(
137        new test_util::FakeNetworkChangeNotifier);
138
139    logger_.reset(new EventLogger);
140
141    fake_drive_service_.reset(new CancelTestableFakeDriveService);
142    test_util::SetUpTestEntries(fake_drive_service_.get());
143    fake_drive_service_->LoadAppListForDriveApi("drive/applist.json");
144
145    scheduler_.reset(new JobScheduler(pref_service_.get(),
146                                      logger_.get(),
147                                      fake_drive_service_.get(),
148                                      base::MessageLoopProxy::current().get()));
149    scheduler_->SetDisableThrottling(true);
150  }
151
152 protected:
153  // Sets up FakeNetworkChangeNotifier as if it's connected to a network with
154  // the specified connection type.
155  void ChangeConnectionType(net::NetworkChangeNotifier::ConnectionType type) {
156    fake_network_change_notifier_->SetConnectionType(type);
157  }
158
159  // Sets up FakeNetworkChangeNotifier as if it's connected to wifi network.
160  void ConnectToWifi() {
161    ChangeConnectionType(net::NetworkChangeNotifier::CONNECTION_WIFI);
162  }
163
164  // Sets up FakeNetworkChangeNotifier as if it's connected to cellular network.
165  void ConnectToCellular() {
166    ChangeConnectionType(net::NetworkChangeNotifier::CONNECTION_2G);
167  }
168
169  // Sets up FakeNetworkChangeNotifier as if it's connected to wimax network.
170  void ConnectToWimax() {
171    ChangeConnectionType(net::NetworkChangeNotifier::CONNECTION_4G);
172  }
173
174  // Sets up FakeNetworkChangeNotifier as if it's disconnected.
175  void ConnectToNone() {
176    ChangeConnectionType(net::NetworkChangeNotifier::CONNECTION_NONE);
177  }
178
179  static int GetMetadataQueueMaxJobCount() {
180    return JobScheduler::kMaxJobCount[JobScheduler::METADATA_QUEUE];
181  }
182
183  content::TestBrowserThreadBundle thread_bundle_;
184  scoped_ptr<TestingPrefServiceSimple> pref_service_;
185  scoped_ptr<test_util::FakeNetworkChangeNotifier>
186      fake_network_change_notifier_;
187  scoped_ptr<EventLogger> logger_;
188  scoped_ptr<CancelTestableFakeDriveService> fake_drive_service_;
189  scoped_ptr<JobScheduler> scheduler_;
190};
191
192TEST_F(JobSchedulerTest, GetAboutResource) {
193  ConnectToWifi();
194
195  google_apis::GDataErrorCode error = google_apis::GDATA_OTHER_ERROR;
196  scoped_ptr<google_apis::AboutResource> about_resource;
197  scheduler_->GetAboutResource(
198      google_apis::test_util::CreateCopyResultCallback(
199          &error, &about_resource));
200  base::RunLoop().RunUntilIdle();
201  ASSERT_EQ(google_apis::HTTP_SUCCESS, error);
202  ASSERT_TRUE(about_resource);
203}
204
205TEST_F(JobSchedulerTest, GetAppList) {
206  ConnectToWifi();
207
208  google_apis::GDataErrorCode error = google_apis::GDATA_OTHER_ERROR;
209  scoped_ptr<google_apis::AppList> app_list;
210
211  scheduler_->GetAppList(
212      google_apis::test_util::CreateCopyResultCallback(&error, &app_list));
213  base::RunLoop().RunUntilIdle();
214
215  ASSERT_EQ(google_apis::HTTP_SUCCESS, error);
216  ASSERT_TRUE(app_list);
217}
218
219TEST_F(JobSchedulerTest, GetAllFileList) {
220  ConnectToWifi();
221
222  google_apis::GDataErrorCode error = google_apis::GDATA_OTHER_ERROR;
223  scoped_ptr<google_apis::FileList> file_list;
224
225  scheduler_->GetAllFileList(
226      google_apis::test_util::CreateCopyResultCallback(&error, &file_list));
227  base::RunLoop().RunUntilIdle();
228
229  ASSERT_EQ(google_apis::HTTP_SUCCESS, error);
230  ASSERT_TRUE(file_list);
231}
232
233TEST_F(JobSchedulerTest, GetFileListInDirectory) {
234  ConnectToWifi();
235
236  google_apis::GDataErrorCode error = google_apis::GDATA_OTHER_ERROR;
237  scoped_ptr<google_apis::FileList> file_list;
238
239  scheduler_->GetFileListInDirectory(
240      fake_drive_service_->GetRootResourceId(),
241      google_apis::test_util::CreateCopyResultCallback(&error, &file_list));
242  base::RunLoop().RunUntilIdle();
243
244  ASSERT_EQ(google_apis::HTTP_SUCCESS, error);
245  ASSERT_TRUE(file_list);
246}
247
248TEST_F(JobSchedulerTest, Search) {
249  ConnectToWifi();
250
251  google_apis::GDataErrorCode error = google_apis::GDATA_OTHER_ERROR;
252  scoped_ptr<google_apis::FileList> file_list;
253
254  scheduler_->Search(
255      "File",  // search query
256      google_apis::test_util::CreateCopyResultCallback(&error, &file_list));
257  base::RunLoop().RunUntilIdle();
258
259  ASSERT_EQ(google_apis::HTTP_SUCCESS, error);
260  ASSERT_TRUE(file_list);
261}
262
263TEST_F(JobSchedulerTest, GetChangeList) {
264  ConnectToWifi();
265
266  int64 old_largest_change_id =
267      fake_drive_service_->about_resource().largest_change_id();
268
269  google_apis::GDataErrorCode error = google_apis::GDATA_OTHER_ERROR;
270
271  // Create a new directory.
272  {
273    scoped_ptr<google_apis::FileResource> entry;
274    fake_drive_service_->AddNewDirectory(
275        fake_drive_service_->GetRootResourceId(),
276        "new directory",
277        DriveServiceInterface::AddNewDirectoryOptions(),
278        google_apis::test_util::CreateCopyResultCallback(
279            &error, &entry));
280    base::RunLoop().RunUntilIdle();
281    ASSERT_EQ(google_apis::HTTP_CREATED, error);
282  }
283
284  error = google_apis::GDATA_OTHER_ERROR;
285  scoped_ptr<google_apis::ChangeList> change_list;
286  scheduler_->GetChangeList(
287      old_largest_change_id + 1,
288      google_apis::test_util::CreateCopyResultCallback(&error, &change_list));
289  base::RunLoop().RunUntilIdle();
290
291  ASSERT_EQ(google_apis::HTTP_SUCCESS, error);
292  ASSERT_TRUE(change_list);
293}
294
295TEST_F(JobSchedulerTest, GetRemainingChangeList) {
296  ConnectToWifi();
297  fake_drive_service_->set_default_max_results(2);
298
299  google_apis::GDataErrorCode error = google_apis::GDATA_OTHER_ERROR;
300  scoped_ptr<google_apis::ChangeList> change_list;
301
302  scheduler_->GetChangeList(
303      0,
304      google_apis::test_util::CreateCopyResultCallback(&error, &change_list));
305  base::RunLoop().RunUntilIdle();
306
307  ASSERT_EQ(google_apis::HTTP_SUCCESS, error);
308  ASSERT_TRUE(change_list);
309
310  // Keep the next url before releasing the |change_list|.
311  GURL next_url(change_list->next_link());
312
313  error = google_apis::GDATA_OTHER_ERROR;
314  change_list.reset();
315
316  scheduler_->GetRemainingChangeList(
317      next_url,
318      google_apis::test_util::CreateCopyResultCallback(&error, &change_list));
319  base::RunLoop().RunUntilIdle();
320
321  ASSERT_EQ(google_apis::HTTP_SUCCESS, error);
322  ASSERT_TRUE(change_list);
323}
324
325TEST_F(JobSchedulerTest, GetRemainingFileList) {
326  ConnectToWifi();
327  fake_drive_service_->set_default_max_results(2);
328
329  google_apis::GDataErrorCode error = google_apis::GDATA_OTHER_ERROR;
330  scoped_ptr<google_apis::FileList> file_list;
331
332  scheduler_->GetFileListInDirectory(
333      fake_drive_service_->GetRootResourceId(),
334      google_apis::test_util::CreateCopyResultCallback(&error, &file_list));
335  base::RunLoop().RunUntilIdle();
336
337  ASSERT_EQ(google_apis::HTTP_SUCCESS, error);
338  ASSERT_TRUE(file_list);
339
340  // Keep the next url before releasing the |file_list|.
341  GURL next_url(file_list->next_link());
342
343  error = google_apis::GDATA_OTHER_ERROR;
344  file_list.reset();
345
346  scheduler_->GetRemainingFileList(
347      next_url,
348      google_apis::test_util::CreateCopyResultCallback(&error, &file_list));
349  base::RunLoop().RunUntilIdle();
350
351  ASSERT_EQ(google_apis::HTTP_SUCCESS, error);
352  ASSERT_TRUE(file_list);
353}
354
355TEST_F(JobSchedulerTest, GetFileResource) {
356  ConnectToWifi();
357
358  google_apis::GDataErrorCode error = google_apis::GDATA_OTHER_ERROR;
359  scoped_ptr<google_apis::FileResource> entry;
360
361  scheduler_->GetFileResource(
362      "2_file_resource_id",  // resource ID
363      ClientContext(USER_INITIATED),
364      google_apis::test_util::CreateCopyResultCallback(&error, &entry));
365  base::RunLoop().RunUntilIdle();
366
367  ASSERT_EQ(google_apis::HTTP_SUCCESS, error);
368  ASSERT_TRUE(entry);
369}
370
371TEST_F(JobSchedulerTest, GetShareUrl) {
372  ConnectToWifi();
373
374  google_apis::GDataErrorCode error = google_apis::GDATA_OTHER_ERROR;
375  GURL share_url;
376
377  scheduler_->GetShareUrl(
378      "2_file_resource_id",  // resource ID
379      GURL("chrome-extension://test-id/"), // embed origin
380      ClientContext(USER_INITIATED),
381      google_apis::test_util::CreateCopyResultCallback(&error, &share_url));
382  base::RunLoop().RunUntilIdle();
383
384  ASSERT_EQ(google_apis::HTTP_SUCCESS, error);
385  ASSERT_FALSE(share_url.is_empty());
386}
387
388TEST_F(JobSchedulerTest, TrashResource) {
389  ConnectToWifi();
390
391  google_apis::GDataErrorCode error = google_apis::GDATA_OTHER_ERROR;
392
393  scheduler_->TrashResource(
394      "2_file_resource_id",
395      ClientContext(USER_INITIATED),
396      google_apis::test_util::CreateCopyResultCallback(&error));
397  base::RunLoop().RunUntilIdle();
398
399  ASSERT_EQ(google_apis::HTTP_SUCCESS, error);
400}
401
402TEST_F(JobSchedulerTest, CopyResource) {
403  ConnectToWifi();
404
405  google_apis::GDataErrorCode error = google_apis::GDATA_OTHER_ERROR;
406  scoped_ptr<google_apis::FileResource> entry;
407
408  scheduler_->CopyResource(
409      "2_file_resource_id",  // resource ID
410      "1_folder_resource_id",  // parent resource ID
411      "New Document",  // new title
412      base::Time(),
413      google_apis::test_util::CreateCopyResultCallback(&error, &entry));
414  base::RunLoop().RunUntilIdle();
415
416  ASSERT_EQ(google_apis::HTTP_SUCCESS, error);
417  ASSERT_TRUE(entry);
418}
419
420TEST_F(JobSchedulerTest, UpdateResource) {
421  ConnectToWifi();
422
423  google_apis::GDataErrorCode error = google_apis::GDATA_OTHER_ERROR;
424  scoped_ptr<google_apis::FileResource> entry;
425
426  scheduler_->UpdateResource(
427      "2_file_resource_id",  // resource ID
428      "1_folder_resource_id",  // parent resource ID
429      "New Document",  // new title
430      base::Time(),
431      base::Time(),
432      ClientContext(USER_INITIATED),
433      google_apis::test_util::CreateCopyResultCallback(&error, &entry));
434  base::RunLoop().RunUntilIdle();
435
436  ASSERT_EQ(google_apis::HTTP_SUCCESS, error);
437  ASSERT_TRUE(entry);
438}
439
440TEST_F(JobSchedulerTest, AddResourceToDirectory) {
441  ConnectToWifi();
442
443  google_apis::GDataErrorCode error = google_apis::GDATA_OTHER_ERROR;
444
445  scheduler_->AddResourceToDirectory(
446      "1_folder_resource_id",
447      "2_file_resource_id",
448      google_apis::test_util::CreateCopyResultCallback(&error));
449  base::RunLoop().RunUntilIdle();
450
451  ASSERT_EQ(google_apis::HTTP_SUCCESS, error);
452}
453
454TEST_F(JobSchedulerTest, RemoveResourceFromDirectory) {
455  ConnectToWifi();
456
457  google_apis::GDataErrorCode error = google_apis::GDATA_OTHER_ERROR;
458
459  scheduler_->RemoveResourceFromDirectory(
460      "1_folder_resource_id",
461      "subdirectory_file_1_id",  // resource ID
462      ClientContext(USER_INITIATED),
463      google_apis::test_util::CreateCopyResultCallback(&error));
464  base::RunLoop().RunUntilIdle();
465
466  ASSERT_EQ(google_apis::HTTP_NO_CONTENT, error);
467}
468
469TEST_F(JobSchedulerTest, AddNewDirectory) {
470  ConnectToWifi();
471
472  google_apis::GDataErrorCode error = google_apis::GDATA_OTHER_ERROR;
473  scoped_ptr<google_apis::FileResource> entry;
474
475  scheduler_->AddNewDirectory(
476      fake_drive_service_->GetRootResourceId(),  // Root directory.
477      "New Directory",
478      DriveServiceInterface::AddNewDirectoryOptions(),
479      ClientContext(USER_INITIATED),
480      google_apis::test_util::CreateCopyResultCallback(&error, &entry));
481  base::RunLoop().RunUntilIdle();
482
483  ASSERT_EQ(google_apis::HTTP_CREATED, error);
484  ASSERT_TRUE(entry);
485}
486
487TEST_F(JobSchedulerTest, PriorityHandling) {
488  // Saturate the metadata job queue with uninteresting jobs to prevent
489  // following jobs from starting.
490  google_apis::GDataErrorCode error_dontcare = google_apis::GDATA_OTHER_ERROR;
491  scoped_ptr<google_apis::FileResource> entry_dontcare;
492  for (int i = 0; i < GetMetadataQueueMaxJobCount(); ++i) {
493    std::string resource_id("2_file_resource_id");
494    scheduler_->GetFileResource(
495        resource_id,
496        ClientContext(USER_INITIATED),
497        google_apis::test_util::CreateCopyResultCallback(&error_dontcare,
498                                                         &entry_dontcare));
499  }
500
501  // Start jobs with different priorities.
502  std::string title_1("new file 1");
503  std::string title_2("new file 2");
504  std::string title_3("new file 3");
505  std::string title_4("new file 4");
506  std::vector<std::string> titles;
507
508  scheduler_->AddNewDirectory(
509      fake_drive_service_->GetRootResourceId(),
510      title_1,
511      DriveServiceInterface::AddNewDirectoryOptions(),
512      ClientContext(USER_INITIATED),
513      base::Bind(&CopyTitleFromFileResourceCallback, &titles));
514  scheduler_->AddNewDirectory(
515      fake_drive_service_->GetRootResourceId(),
516      title_2,
517      DriveServiceInterface::AddNewDirectoryOptions(),
518      ClientContext(BACKGROUND),
519      base::Bind(&CopyTitleFromFileResourceCallback, &titles));
520  scheduler_->AddNewDirectory(
521      fake_drive_service_->GetRootResourceId(),
522      title_3,
523      DriveServiceInterface::AddNewDirectoryOptions(),
524      ClientContext(BACKGROUND),
525      base::Bind(&CopyTitleFromFileResourceCallback, &titles));
526  scheduler_->AddNewDirectory(
527      fake_drive_service_->GetRootResourceId(),
528      title_4,
529      DriveServiceInterface::AddNewDirectoryOptions(),
530      ClientContext(USER_INITIATED),
531      base::Bind(&CopyTitleFromFileResourceCallback, &titles));
532
533  base::RunLoop().RunUntilIdle();
534
535  ASSERT_EQ(4ul, titles.size());
536  EXPECT_EQ(title_1, titles[0]);
537  EXPECT_EQ(title_4, titles[1]);
538  EXPECT_EQ(title_2, titles[2]);
539  EXPECT_EQ(title_3, titles[3]);
540}
541
542TEST_F(JobSchedulerTest, NoConnectionUserInitiated) {
543  ConnectToNone();
544
545  std::string resource_id("2_file_resource_id");
546
547  google_apis::GDataErrorCode error = google_apis::GDATA_OTHER_ERROR;
548  scoped_ptr<google_apis::FileResource> entry;
549  scheduler_->GetFileResource(
550      resource_id,
551      ClientContext(USER_INITIATED),
552      google_apis::test_util::CreateCopyResultCallback(&error, &entry));
553  base::RunLoop().RunUntilIdle();
554
555  EXPECT_EQ(google_apis::GDATA_NO_CONNECTION, error);
556}
557
558TEST_F(JobSchedulerTest, NoConnectionBackground) {
559  ConnectToNone();
560
561  std::string resource_id("2_file_resource_id");
562
563  google_apis::GDataErrorCode error = google_apis::GDATA_OTHER_ERROR;
564  scoped_ptr<google_apis::FileResource> entry;
565  scheduler_->GetFileResource(
566      resource_id,
567      ClientContext(BACKGROUND),
568      google_apis::test_util::CreateCopyResultCallback(&error, &entry));
569  base::RunLoop().RunUntilIdle();
570
571  EXPECT_FALSE(entry);
572
573  // Reconnect to the net.
574  ConnectToWifi();
575
576  base::RunLoop().RunUntilIdle();
577
578  EXPECT_EQ(google_apis::HTTP_SUCCESS, error);
579  ASSERT_TRUE(entry);
580}
581
582TEST_F(JobSchedulerTest, DownloadFileCellularDisabled) {
583  ConnectToCellular();
584
585  // Disable fetching over cellular network.
586  pref_service_->SetBoolean(prefs::kDisableDriveOverCellular, true);
587
588  // Try to get a file in the background
589  base::ScopedTempDir temp_dir;
590  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
591
592  const base::FilePath kOutputFilePath =
593      temp_dir.path().AppendASCII("whatever.txt");
594  google_apis::GDataErrorCode download_error = google_apis::GDATA_OTHER_ERROR;
595  base::FilePath output_file_path;
596  scheduler_->DownloadFile(
597      base::FilePath::FromUTF8Unsafe("drive/whatever.txt"),  // virtual path
598      kDummyDownloadFileSize,
599      kOutputFilePath,
600      "2_file_resource_id",
601      ClientContext(BACKGROUND),
602      google_apis::test_util::CreateCopyResultCallback(
603          &download_error, &output_file_path),
604      google_apis::GetContentCallback());
605  // Metadata should still work
606  google_apis::GDataErrorCode metadata_error = google_apis::GDATA_OTHER_ERROR;
607  scoped_ptr<google_apis::AboutResource> about_resource;
608
609  // Try to get the metadata
610  scheduler_->GetAboutResource(
611      google_apis::test_util::CreateCopyResultCallback(
612          &metadata_error, &about_resource));
613  base::RunLoop().RunUntilIdle();
614
615  // Check the metadata
616  ASSERT_EQ(google_apis::HTTP_SUCCESS, metadata_error);
617  ASSERT_TRUE(about_resource);
618
619  // Check the download
620  EXPECT_EQ(google_apis::GDATA_OTHER_ERROR, download_error);
621
622  // Switch to a Wifi connection
623  ConnectToWifi();
624
625  base::RunLoop().RunUntilIdle();
626
627  // Check the download again
628  EXPECT_EQ(google_apis::HTTP_SUCCESS, download_error);
629  std::string content;
630  EXPECT_EQ(output_file_path, kOutputFilePath);
631  ASSERT_TRUE(base::ReadFileToString(output_file_path, &content));
632  EXPECT_EQ("This is some test content.", content);
633}
634
635TEST_F(JobSchedulerTest, DownloadFileWimaxDisabled) {
636  ConnectToWimax();
637
638  // Disable fetching over cellular network.
639  pref_service_->SetBoolean(prefs::kDisableDriveOverCellular, true);
640
641  // Try to get a file in the background
642  base::ScopedTempDir temp_dir;
643  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
644
645  const base::FilePath kOutputFilePath =
646      temp_dir.path().AppendASCII("whatever.txt");
647  google_apis::GDataErrorCode download_error = google_apis::GDATA_OTHER_ERROR;
648  base::FilePath output_file_path;
649  scheduler_->DownloadFile(
650      base::FilePath::FromUTF8Unsafe("drive/whatever.txt"),  // virtual path
651      kDummyDownloadFileSize,
652      kOutputFilePath,
653      "2_file_resource_id",
654      ClientContext(BACKGROUND),
655      google_apis::test_util::CreateCopyResultCallback(
656          &download_error, &output_file_path),
657      google_apis::GetContentCallback());
658  // Metadata should still work
659  google_apis::GDataErrorCode metadata_error = google_apis::GDATA_OTHER_ERROR;
660  scoped_ptr<google_apis::AboutResource> about_resource;
661
662  // Try to get the metadata
663  scheduler_->GetAboutResource(
664      google_apis::test_util::CreateCopyResultCallback(
665          &metadata_error, &about_resource));
666  base::RunLoop().RunUntilIdle();
667
668  // Check the metadata
669  ASSERT_EQ(google_apis::HTTP_SUCCESS, metadata_error);
670  ASSERT_TRUE(about_resource);
671
672  // Check the download
673  EXPECT_EQ(google_apis::GDATA_OTHER_ERROR, download_error);
674
675  // Switch to a Wifi connection
676  ConnectToWifi();
677
678  base::RunLoop().RunUntilIdle();
679
680  // Check the download again
681  EXPECT_EQ(google_apis::HTTP_SUCCESS, download_error);
682  std::string content;
683  EXPECT_EQ(output_file_path, kOutputFilePath);
684  ASSERT_TRUE(base::ReadFileToString(output_file_path, &content));
685  EXPECT_EQ("This is some test content.", content);
686}
687
688TEST_F(JobSchedulerTest, DownloadFileCellularEnabled) {
689  ConnectToCellular();
690
691  // Enable fetching over cellular network.
692  pref_service_->SetBoolean(prefs::kDisableDriveOverCellular, false);
693
694  // Try to get a file in the background
695  base::ScopedTempDir temp_dir;
696  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
697
698  const base::FilePath kOutputFilePath =
699      temp_dir.path().AppendASCII("whatever.txt");
700  google_apis::GDataErrorCode download_error = google_apis::GDATA_OTHER_ERROR;
701  base::FilePath output_file_path;
702  scheduler_->DownloadFile(
703      base::FilePath::FromUTF8Unsafe("drive/whatever.txt"),  // virtual path
704      kDummyDownloadFileSize,
705      kOutputFilePath,
706      "2_file_resource_id",
707      ClientContext(BACKGROUND),
708      google_apis::test_util::CreateCopyResultCallback(
709          &download_error, &output_file_path),
710      google_apis::GetContentCallback());
711  // Metadata should still work
712  google_apis::GDataErrorCode metadata_error = google_apis::GDATA_OTHER_ERROR;
713  scoped_ptr<google_apis::AboutResource> about_resource;
714
715  // Try to get the metadata
716  scheduler_->GetAboutResource(
717      google_apis::test_util::CreateCopyResultCallback(
718          &metadata_error, &about_resource));
719  base::RunLoop().RunUntilIdle();
720
721  // Check the metadata
722  ASSERT_EQ(google_apis::HTTP_SUCCESS, metadata_error);
723  ASSERT_TRUE(about_resource);
724
725  // Check the download
726  EXPECT_EQ(google_apis::HTTP_SUCCESS, download_error);
727  std::string content;
728  EXPECT_EQ(output_file_path, kOutputFilePath);
729  ASSERT_TRUE(base::ReadFileToString(output_file_path, &content));
730  EXPECT_EQ("This is some test content.", content);
731}
732
733TEST_F(JobSchedulerTest, DownloadFileWimaxEnabled) {
734  ConnectToWimax();
735
736  // Enable fetching over cellular network.
737  pref_service_->SetBoolean(prefs::kDisableDriveOverCellular, false);
738
739  // Try to get a file in the background
740  base::ScopedTempDir temp_dir;
741  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
742
743  const base::FilePath kOutputFilePath =
744      temp_dir.path().AppendASCII("whatever.txt");
745  google_apis::GDataErrorCode download_error = google_apis::GDATA_OTHER_ERROR;
746  base::FilePath output_file_path;
747  scheduler_->DownloadFile(
748      base::FilePath::FromUTF8Unsafe("drive/whatever.txt"),  // virtual path
749      kDummyDownloadFileSize,
750      kOutputFilePath,
751      "2_file_resource_id",
752      ClientContext(BACKGROUND),
753      google_apis::test_util::CreateCopyResultCallback(
754          &download_error, &output_file_path),
755      google_apis::GetContentCallback());
756  // Metadata should still work
757  google_apis::GDataErrorCode metadata_error = google_apis::GDATA_OTHER_ERROR;
758  scoped_ptr<google_apis::AboutResource> about_resource;
759
760  // Try to get the metadata
761  scheduler_->GetAboutResource(
762      google_apis::test_util::CreateCopyResultCallback(
763          &metadata_error, &about_resource));
764  base::RunLoop().RunUntilIdle();
765
766  // Check the metadata
767  ASSERT_EQ(google_apis::HTTP_SUCCESS, metadata_error);
768  ASSERT_TRUE(about_resource);
769
770  // Check the download
771  EXPECT_EQ(google_apis::HTTP_SUCCESS, download_error);
772  std::string content;
773  EXPECT_EQ(output_file_path, kOutputFilePath);
774  ASSERT_TRUE(base::ReadFileToString(output_file_path, &content));
775  EXPECT_EQ("This is some test content.", content);
776}
777
778TEST_F(JobSchedulerTest, JobInfo) {
779  JobListLogger logger;
780  scheduler_->AddObserver(&logger);
781
782  // Disable background upload/download.
783  ConnectToWimax();
784  pref_service_->SetBoolean(prefs::kDisableDriveOverCellular, true);
785
786  base::ScopedTempDir temp_dir;
787  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
788
789  google_apis::GDataErrorCode error = google_apis::GDATA_OTHER_ERROR;
790  scoped_ptr<google_apis::FileResource> entry;
791  scoped_ptr<google_apis::AboutResource> about_resource;
792  base::FilePath path;
793
794  std::set<JobType> expected_types;
795
796  // Add many jobs.
797  expected_types.insert(TYPE_ADD_NEW_DIRECTORY);
798  scheduler_->AddNewDirectory(
799      fake_drive_service_->GetRootResourceId(),
800      "New Directory",
801      DriveServiceInterface::AddNewDirectoryOptions(),
802      ClientContext(USER_INITIATED),
803      google_apis::test_util::CreateCopyResultCallback(&error, &entry));
804  expected_types.insert(TYPE_GET_ABOUT_RESOURCE);
805  scheduler_->GetAboutResource(
806      google_apis::test_util::CreateCopyResultCallback(
807          &error, &about_resource));
808  expected_types.insert(TYPE_UPDATE_RESOURCE);
809  scheduler_->UpdateResource(
810      "2_file_resource_id",
811      std::string(),
812      "New Title",
813      base::Time(),
814      base::Time(),
815      ClientContext(USER_INITIATED),
816      google_apis::test_util::CreateCopyResultCallback(&error, &entry));
817  expected_types.insert(TYPE_DOWNLOAD_FILE);
818  scheduler_->DownloadFile(
819      base::FilePath::FromUTF8Unsafe("drive/whatever.txt"),  // virtual path
820      kDummyDownloadFileSize,
821      temp_dir.path().AppendASCII("whatever.txt"),
822      "2_file_resource_id",
823      ClientContext(BACKGROUND),
824      google_apis::test_util::CreateCopyResultCallback(&error, &path),
825      google_apis::GetContentCallback());
826
827  // The number of jobs queued so far.
828  EXPECT_EQ(4U, scheduler_->GetJobInfoList().size());
829  EXPECT_TRUE(logger.Has(JobListLogger::ADDED, TYPE_ADD_NEW_DIRECTORY));
830  EXPECT_TRUE(logger.Has(JobListLogger::ADDED, TYPE_GET_ABOUT_RESOURCE));
831  EXPECT_TRUE(logger.Has(JobListLogger::ADDED, TYPE_UPDATE_RESOURCE));
832  EXPECT_TRUE(logger.Has(JobListLogger::ADDED, TYPE_DOWNLOAD_FILE));
833  EXPECT_FALSE(logger.Has(JobListLogger::DONE, TYPE_ADD_NEW_DIRECTORY));
834  EXPECT_FALSE(logger.Has(JobListLogger::DONE, TYPE_GET_ABOUT_RESOURCE));
835  EXPECT_FALSE(logger.Has(JobListLogger::DONE, TYPE_UPDATE_RESOURCE));
836  EXPECT_FALSE(logger.Has(JobListLogger::DONE, TYPE_DOWNLOAD_FILE));
837
838  // Add more jobs.
839  expected_types.insert(TYPE_ADD_RESOURCE_TO_DIRECTORY);
840  scheduler_->AddResourceToDirectory(
841      "1_folder_resource_id",
842      "2_file_resource_id",
843      google_apis::test_util::CreateCopyResultCallback(&error));
844  expected_types.insert(TYPE_COPY_RESOURCE);
845  scheduler_->CopyResource(
846      "5_document_resource_id",
847      fake_drive_service_->GetRootResourceId(),
848      "New Document",
849      base::Time(),  // last_modified
850      google_apis::test_util::CreateCopyResultCallback(&error, &entry));
851
852  // 6 jobs in total were queued.
853  std::vector<JobInfo> jobs = scheduler_->GetJobInfoList();
854  EXPECT_EQ(6U, jobs.size());
855  std::set<JobType> actual_types;
856  std::set<JobID> job_ids;
857  for (size_t i = 0; i < jobs.size(); ++i) {
858    actual_types.insert(jobs[i].job_type);
859    job_ids.insert(jobs[i].job_id);
860  }
861  EXPECT_EQ(expected_types, actual_types);
862  EXPECT_EQ(6U, job_ids.size()) << "All job IDs must be unique";
863  EXPECT_TRUE(logger.Has(JobListLogger::ADDED, TYPE_ADD_RESOURCE_TO_DIRECTORY));
864  EXPECT_TRUE(logger.Has(JobListLogger::ADDED, TYPE_COPY_RESOURCE));
865  EXPECT_FALSE(logger.Has(JobListLogger::DONE, TYPE_ADD_RESOURCE_TO_DIRECTORY));
866  EXPECT_FALSE(logger.Has(JobListLogger::DONE, TYPE_COPY_RESOURCE));
867
868  // Run the jobs.
869  base::RunLoop().RunUntilIdle();
870
871  // All jobs except the BACKGROUND job should have started running (UPDATED)
872  // and then finished (DONE).
873  jobs = scheduler_->GetJobInfoList();
874  ASSERT_EQ(1U, jobs.size());
875  EXPECT_EQ(TYPE_DOWNLOAD_FILE, jobs[0].job_type);
876
877  EXPECT_TRUE(logger.Has(JobListLogger::UPDATED, TYPE_ADD_NEW_DIRECTORY));
878  EXPECT_TRUE(logger.Has(JobListLogger::UPDATED, TYPE_GET_ABOUT_RESOURCE));
879  EXPECT_TRUE(logger.Has(JobListLogger::UPDATED, TYPE_UPDATE_RESOURCE));
880  EXPECT_TRUE(logger.Has(JobListLogger::UPDATED,
881                         TYPE_ADD_RESOURCE_TO_DIRECTORY));
882  EXPECT_TRUE(logger.Has(JobListLogger::UPDATED, TYPE_COPY_RESOURCE));
883  EXPECT_FALSE(logger.Has(JobListLogger::UPDATED, TYPE_DOWNLOAD_FILE));
884
885  EXPECT_TRUE(logger.Has(JobListLogger::DONE, TYPE_ADD_NEW_DIRECTORY));
886  EXPECT_TRUE(logger.Has(JobListLogger::DONE, TYPE_GET_ABOUT_RESOURCE));
887  EXPECT_TRUE(logger.Has(JobListLogger::DONE, TYPE_UPDATE_RESOURCE));
888  EXPECT_TRUE(logger.Has(JobListLogger::DONE, TYPE_ADD_RESOURCE_TO_DIRECTORY));
889  EXPECT_TRUE(logger.Has(JobListLogger::DONE, TYPE_COPY_RESOURCE));
890  EXPECT_FALSE(logger.Has(JobListLogger::DONE, TYPE_DOWNLOAD_FILE));
891
892  // Run the background downloading job as well.
893  ConnectToWifi();
894  base::RunLoop().RunUntilIdle();
895
896  // All jobs should have finished.
897  EXPECT_EQ(0U, scheduler_->GetJobInfoList().size());
898  EXPECT_TRUE(logger.Has(JobListLogger::UPDATED, TYPE_DOWNLOAD_FILE));
899  EXPECT_TRUE(logger.Has(JobListLogger::DONE, TYPE_DOWNLOAD_FILE));
900}
901
902TEST_F(JobSchedulerTest, JobInfoProgress) {
903  JobListLogger logger;
904  scheduler_->AddObserver(&logger);
905
906  ConnectToWifi();
907
908  base::ScopedTempDir temp_dir;
909  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
910
911  google_apis::GDataErrorCode error = google_apis::GDATA_OTHER_ERROR;
912  base::FilePath path;
913
914  // Download job.
915  scheduler_->DownloadFile(
916      base::FilePath::FromUTF8Unsafe("drive/whatever.txt"),  // virtual path
917      kDummyDownloadFileSize,
918      temp_dir.path().AppendASCII("whatever.txt"),
919      "2_file_resource_id",
920      ClientContext(BACKGROUND),
921      google_apis::test_util::CreateCopyResultCallback(&error, &path),
922      google_apis::GetContentCallback());
923  base::RunLoop().RunUntilIdle();
924
925  std::vector<int64> download_progress;
926  logger.GetProgressInfo(TYPE_DOWNLOAD_FILE, &download_progress);
927  ASSERT_TRUE(!download_progress.empty());
928  EXPECT_TRUE(base::STLIsSorted(download_progress));
929  EXPECT_GE(download_progress.front(), 0);
930  EXPECT_LE(download_progress.back(), 26);
931
932  // Upload job.
933  path = temp_dir.path().AppendASCII("new_file.txt");
934  ASSERT_TRUE(google_apis::test_util::WriteStringToFile(path, "Hello"));
935  google_apis::GDataErrorCode upload_error =
936      google_apis::GDATA_OTHER_ERROR;
937  scoped_ptr<google_apis::FileResource> entry;
938
939  scheduler_->UploadNewFile(
940      fake_drive_service_->GetRootResourceId(),
941      base::FilePath::FromUTF8Unsafe("drive/new_file.txt"),
942      path,
943      "dummy title",
944      "plain/plain",
945      DriveUploader::UploadNewFileOptions(),
946      ClientContext(BACKGROUND),
947      google_apis::test_util::CreateCopyResultCallback(&upload_error, &entry));
948  base::RunLoop().RunUntilIdle();
949
950  std::vector<int64> upload_progress;
951  logger.GetProgressInfo(TYPE_UPLOAD_NEW_FILE, &upload_progress);
952  ASSERT_TRUE(!upload_progress.empty());
953  EXPECT_TRUE(base::STLIsSorted(upload_progress));
954  EXPECT_GE(upload_progress.front(), 0);
955  EXPECT_LE(upload_progress.back(), 13);
956}
957
958TEST_F(JobSchedulerTest, CancelPendingJob) {
959  base::ScopedTempDir temp_dir;
960  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
961  base::FilePath upload_path = temp_dir.path().AppendASCII("new_file.txt");
962  ASSERT_TRUE(google_apis::test_util::WriteStringToFile(upload_path, "Hello"));
963
964  // To create a pending job for testing, set the mode to cellular connection
965  // and issue BACKGROUND jobs.
966  ConnectToCellular();
967  pref_service_->SetBoolean(prefs::kDisableDriveOverCellular, true);
968
969  // Start the first job and record its job ID.
970  google_apis::GDataErrorCode error1 = google_apis::GDATA_OTHER_ERROR;
971  scoped_ptr<google_apis::FileResource> entry;
972  scheduler_->UploadNewFile(
973      fake_drive_service_->GetRootResourceId(),
974      base::FilePath::FromUTF8Unsafe("dummy/path"),
975      upload_path,
976      "dummy title 1",
977      "text/plain",
978      DriveUploader::UploadNewFileOptions(),
979      ClientContext(BACKGROUND),
980      google_apis::test_util::CreateCopyResultCallback(&error1, &entry));
981
982  const std::vector<JobInfo>& jobs = scheduler_->GetJobInfoList();
983  ASSERT_EQ(1u, jobs.size());
984  ASSERT_EQ(STATE_NONE, jobs[0].state);  // Not started yet.
985  JobID first_job_id = jobs[0].job_id;
986
987  // Start the second job.
988  google_apis::GDataErrorCode error2 = google_apis::GDATA_OTHER_ERROR;
989  scheduler_->UploadNewFile(
990      fake_drive_service_->GetRootResourceId(),
991      base::FilePath::FromUTF8Unsafe("dummy/path"),
992      upload_path,
993      "dummy title 2",
994      "text/plain",
995      DriveUploader::UploadNewFileOptions(),
996      ClientContext(BACKGROUND),
997      google_apis::test_util::CreateCopyResultCallback(&error2, &entry));
998
999  // Cancel the first one.
1000  scheduler_->CancelJob(first_job_id);
1001
1002  // Only the first job should be cancelled.
1003  ConnectToWifi();
1004  base::RunLoop().RunUntilIdle();
1005  EXPECT_EQ(google_apis::GDATA_CANCELLED, error1);
1006  EXPECT_EQ(google_apis::HTTP_SUCCESS, error2);
1007  EXPECT_TRUE(scheduler_->GetJobInfoList().empty());
1008}
1009
1010TEST_F(JobSchedulerTest, CancelRunningJob) {
1011  ConnectToWifi();
1012
1013  base::ScopedTempDir temp_dir;
1014  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
1015  base::FilePath upload_path = temp_dir.path().AppendASCII("new_file.txt");
1016  ASSERT_TRUE(google_apis::test_util::WriteStringToFile(upload_path, "Hello"));
1017
1018  // Run as a cancelable task.
1019  fake_drive_service_->set_upload_new_file_cancelable(true);
1020  google_apis::GDataErrorCode error1 = google_apis::GDATA_OTHER_ERROR;
1021  scoped_ptr<google_apis::FileResource> entry;
1022  scheduler_->UploadNewFile(
1023      fake_drive_service_->GetRootResourceId(),
1024      base::FilePath::FromUTF8Unsafe("dummy/path"),
1025      upload_path,
1026      "dummy title 1",
1027      "text/plain",
1028      DriveUploader::UploadNewFileOptions(),
1029      ClientContext(USER_INITIATED),
1030      google_apis::test_util::CreateCopyResultCallback(&error1, &entry));
1031
1032  const std::vector<JobInfo>& jobs = scheduler_->GetJobInfoList();
1033  ASSERT_EQ(1u, jobs.size());
1034  ASSERT_EQ(STATE_RUNNING, jobs[0].state);  // It's running.
1035  JobID first_job_id = jobs[0].job_id;
1036
1037  // Start the second job normally.
1038  fake_drive_service_->set_upload_new_file_cancelable(false);
1039  google_apis::GDataErrorCode error2 = google_apis::GDATA_OTHER_ERROR;
1040  scheduler_->UploadNewFile(
1041      fake_drive_service_->GetRootResourceId(),
1042      base::FilePath::FromUTF8Unsafe("dummy/path"),
1043      upload_path,
1044      "dummy title 2",
1045      "text/plain",
1046      DriveUploader::UploadNewFileOptions(),
1047      ClientContext(USER_INITIATED),
1048      google_apis::test_util::CreateCopyResultCallback(&error2, &entry));
1049
1050  // Cancel the first one.
1051  scheduler_->CancelJob(first_job_id);
1052
1053  // Only the first job should be cancelled.
1054  base::RunLoop().RunUntilIdle();
1055  EXPECT_EQ(google_apis::GDATA_CANCELLED, error1);
1056  EXPECT_EQ(google_apis::HTTP_SUCCESS, error2);
1057  EXPECT_TRUE(scheduler_->GetJobInfoList().empty());
1058}
1059
1060}  // namespace drive
1061