Home | History | Annotate | Download | only in file_manager
      1 // Copyright (c) 2013 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 // Browser test for basic Chrome OS file manager functionality:
      6 //  - The file list is updated when a file is added externally to the Downloads
      7 //    folder.
      8 //  - Selecting a file and copy-pasting it with the keyboard copies the file.
      9 //  - Selecting a file and pressing delete deletes it.
     10 
     11 #include <deque>
     12 #include <string>
     13 
     14 #include "base/bind.h"
     15 #include "base/callback.h"
     16 #include "base/file_util.h"
     17 #include "base/files/file_path.h"
     18 #include "base/time/time.h"
     19 #include "chrome/browser/chrome_notification_types.h"
     20 #include "chrome/browser/chromeos/drive/drive_integration_service.h"
     21 #include "chrome/browser/chromeos/drive/file_system_interface.h"
     22 #include "chrome/browser/chromeos/extensions/file_manager/drive_test_util.h"
     23 #include "chrome/browser/drive/fake_drive_service.h"
     24 #include "chrome/browser/extensions/api/test/test_api.h"
     25 #include "chrome/browser/extensions/component_loader.h"
     26 #include "chrome/browser/extensions/extension_apitest.h"
     27 #include "chrome/browser/extensions/extension_test_message_listener.h"
     28 #include "chrome/browser/google_apis/gdata_wapi_parser.h"
     29 #include "chrome/browser/google_apis/test_util.h"
     30 #include "chrome/browser/profiles/profile.h"
     31 #include "chrome/common/chrome_switches.h"
     32 #include "chrome/common/extensions/extension.h"
     33 #include "chromeos/chromeos_switches.h"
     34 #include "content/public/browser/browser_context.h"
     35 #include "content/public/browser/notification_service.h"
     36 #include "content/public/test/test_utils.h"
     37 #include "net/test/embedded_test_server/embedded_test_server.h"
     38 #include "webkit/browser/fileapi/external_mount_points.h"
     39 
     40 namespace file_manager {
     41 namespace {
     42 
     43 enum EntryType {
     44   FILE,
     45   DIRECTORY,
     46 };
     47 
     48 enum SharedOption {
     49   NONE,
     50   SHARED,
     51 };
     52 
     53 enum GuestMode {
     54   NOT_IN_GUEST_MODE,
     55   IN_GUEST_MODE
     56 };
     57 
     58 // This global operator is used from Google Test to format error messages.
     59 std::ostream& operator<<(std::ostream& os, const GuestMode& guest_mode) {
     60   return os << (guest_mode == IN_GUEST_MODE ?
     61                 "IN_GUEST_MODE" : "NOT_IN_GUEST_MODE");
     62 }
     63 
     64 struct TestEntryInfo {
     65   EntryType type;
     66   const char* source_file_name;  // Source file name to be used as a prototype.
     67   const char* target_name;  // Target file or directory name.
     68   const char* mime_type;
     69   SharedOption shared_option;
     70   const char* last_modified_time_as_string;
     71 };
     72 
     73 TestEntryInfo kTestEntrySetCommon[] = {
     74   { FILE, "text.txt", "hello.txt", "text/plain", NONE, "4 Sep 1998 12:34:56" },
     75   { FILE, "image.png", "My Desktop Background.png", "text/plain", NONE,
     76     "18 Jan 2038 01:02:03" },
     77   { FILE, "music.ogg", "Beautiful Song.ogg", "text/plain", NONE,
     78     "12 Nov 2086 12:00:00" },
     79   { FILE, "video.ogv", "world.ogv", "text/plain", NONE,
     80     "4 July 2012 10:35:00" },
     81   { DIRECTORY, "", "photos", NULL, NONE, "1 Jan 1980 23:59:59" },
     82   { DIRECTORY, "", ".warez", NULL, NONE, "26 Oct 1985 13:39" }
     83 };
     84 
     85 TestEntryInfo kTestEntrySetDriveOnly[] = {
     86   { FILE, "", "Test Document", "application/vnd.google-apps.document", NONE,
     87     "10 Apr 2013 16:20:00" },
     88   { FILE, "", "Test Shared Document", "application/vnd.google-apps.document",
     89     SHARED, "20 Mar 2013 22:40:00" }
     90 };
     91 
     92 // The local volume class for test.
     93 // This class provides the operations for a test volume that simulates local
     94 // drive.
     95 class LocalTestVolume {
     96  public:
     97   // Adds this volume to the file system as a local volume. Returns true on
     98   // success.
     99   bool Mount(Profile* profile) {
    100     const std::string kDownloads = "Downloads";
    101 
    102     if (local_path_.empty()) {
    103       if (!tmp_dir_.CreateUniqueTempDir())
    104         return false;
    105       local_path_ = tmp_dir_.path().Append(kDownloads);
    106     }
    107     fileapi::ExternalMountPoints* const mount_points =
    108         content::BrowserContext::GetMountPoints(profile);
    109     mount_points->RevokeFileSystem(kDownloads);
    110 
    111     return mount_points->RegisterFileSystem(
    112         kDownloads, fileapi::kFileSystemTypeNativeLocal, local_path_) &&
    113         file_util::CreateDirectory(local_path_);
    114   }
    115 
    116   void CreateEntry(const TestEntryInfo& entry) {
    117     base::FilePath target_path = local_path_.AppendASCII(entry.target_name);
    118     switch (entry.type) {
    119       case FILE: {
    120         base::FilePath source_path =
    121             google_apis::test_util::GetTestFilePath("chromeos/file_manager").
    122             AppendASCII(entry.source_file_name);
    123         ASSERT_TRUE(base::CopyFile(source_path, target_path))
    124             << "Copy from " << source_path.value()
    125             << " to " << target_path.value() << " failed.";
    126         break;
    127       }
    128       case DIRECTORY:
    129         ASSERT_TRUE(file_util::CreateDirectory(target_path)) <<
    130             "Failed to create a directory: " << target_path.value();
    131         break;
    132     }
    133     base::Time time;
    134     ASSERT_TRUE(base::Time::FromString(entry.last_modified_time_as_string,
    135                                        &time));
    136     ASSERT_TRUE(file_util::SetLastModifiedTime(target_path, time));
    137   }
    138 
    139  private:
    140   base::FilePath local_path_;
    141   base::ScopedTempDir tmp_dir_;
    142 };
    143 
    144 // The drive volume class for test.
    145 // This class provides the operations for a test volume that simulates Google
    146 // drive.
    147 class DriveTestVolume {
    148  public:
    149   DriveTestVolume() : fake_drive_service_(NULL),
    150                       integration_service_(NULL) {
    151   }
    152 
    153   // Send request to add this volume to the file system as Google drive.
    154   // This method must be calld at SetUp method of FileManagerBrowserTestBase.
    155   // Returns true on success.
    156   bool SetUp() {
    157     if (!test_cache_root_.CreateUniqueTempDir())
    158       return false;
    159     drive::DriveIntegrationServiceFactory::SetFactoryForTest(
    160         base::Bind(&DriveTestVolume::CreateDriveIntegrationService,
    161                    base::Unretained(this)));
    162     return true;
    163   }
    164 
    165   void CreateEntry(const TestEntryInfo& entry) {
    166     switch (entry.type) {
    167       case FILE:
    168         CreateFile(entry.source_file_name,
    169                    entry.target_name,
    170                    entry.mime_type,
    171                    entry.shared_option == SHARED,
    172                    entry.last_modified_time_as_string);
    173         break;
    174       case DIRECTORY:
    175         CreateDirectory(entry.target_name, entry.last_modified_time_as_string);
    176         break;
    177     }
    178   }
    179 
    180   // Creates an empty directory with the given |name| and |modification_time|.
    181   void CreateDirectory(const std::string& name,
    182                        const std::string& modification_time) {
    183     google_apis::GDataErrorCode error = google_apis::GDATA_OTHER_ERROR;
    184     scoped_ptr<google_apis::ResourceEntry> resource_entry;
    185     fake_drive_service_->AddNewDirectory(
    186         fake_drive_service_->GetRootResourceId(),
    187         name,
    188         google_apis::test_util::CreateCopyResultCallback(&error,
    189                                                          &resource_entry));
    190     base::MessageLoop::current()->RunUntilIdle();
    191     ASSERT_TRUE(error == google_apis::HTTP_CREATED);
    192     ASSERT_TRUE(resource_entry);
    193 
    194     base::Time time;
    195     ASSERT_TRUE(base::Time::FromString(modification_time.c_str(), &time));
    196     fake_drive_service_->SetLastModifiedTime(
    197         resource_entry->resource_id(),
    198         time,
    199         google_apis::test_util::CreateCopyResultCallback(&error,
    200                                                          &resource_entry));
    201     base::MessageLoop::current()->RunUntilIdle();
    202     ASSERT_TRUE(error == google_apis::HTTP_SUCCESS);
    203     ASSERT_TRUE(resource_entry);
    204     CheckForUpdates();
    205   }
    206 
    207   // Creates a test file with the given spec.
    208   // Serves |test_file_name| file. Pass an empty string for an empty file.
    209   void CreateFile(const std::string& source_file_name,
    210                   const std::string& target_file_name,
    211                   const std::string& mime_type,
    212                   bool shared_with_me,
    213                   const std::string& modification_time) {
    214     google_apis::GDataErrorCode error = google_apis::GDATA_OTHER_ERROR;
    215 
    216     std::string content_data;
    217     if (!source_file_name.empty()) {
    218       base::FilePath source_file_path =
    219           google_apis::test_util::GetTestFilePath("chromeos/file_manager").
    220               AppendASCII(source_file_name);
    221       ASSERT_TRUE(file_util::ReadFileToString(source_file_path, &content_data));
    222     }
    223 
    224     scoped_ptr<google_apis::ResourceEntry> resource_entry;
    225     fake_drive_service_->AddNewFile(
    226         mime_type,
    227         content_data,
    228         fake_drive_service_->GetRootResourceId(),
    229         target_file_name,
    230         shared_with_me,
    231         google_apis::test_util::CreateCopyResultCallback(&error,
    232                                                          &resource_entry));
    233     base::MessageLoop::current()->RunUntilIdle();
    234     ASSERT_EQ(google_apis::HTTP_CREATED, error);
    235     ASSERT_TRUE(resource_entry);
    236 
    237     base::Time time;
    238     ASSERT_TRUE(base::Time::FromString(modification_time.c_str(), &time));
    239     fake_drive_service_->SetLastModifiedTime(
    240         resource_entry->resource_id(),
    241         time,
    242         google_apis::test_util::CreateCopyResultCallback(&error,
    243                                                          &resource_entry));
    244     base::MessageLoop::current()->RunUntilIdle();
    245     ASSERT_EQ(google_apis::HTTP_SUCCESS, error);
    246     ASSERT_TRUE(resource_entry);
    247 
    248     CheckForUpdates();
    249   }
    250 
    251   // Notifies FileSystem that the contents in FakeDriveService are
    252   // changed, hence the new contents should be fetched.
    253   void CheckForUpdates() {
    254     if (integration_service_ && integration_service_->file_system()) {
    255       integration_service_->file_system()->CheckForUpdates();
    256     }
    257   }
    258 
    259   // Sets the url base for the test server to be used to generate share urls
    260   // on the files and directories.
    261   void ConfigureShareUrlBase(const GURL& share_url_base) {
    262     fake_drive_service_->set_share_url_base(share_url_base);
    263   }
    264 
    265   drive::DriveIntegrationService* CreateDriveIntegrationService(
    266       Profile* profile) {
    267     fake_drive_service_ = new drive::FakeDriveService;
    268     fake_drive_service_->LoadResourceListForWapi(
    269         "gdata/empty_feed.json");
    270     fake_drive_service_->LoadAccountMetadataForWapi(
    271         "gdata/account_metadata.json");
    272     fake_drive_service_->LoadAppListForDriveApi("drive/applist.json");
    273     integration_service_ = new drive::DriveIntegrationService(
    274         profile,
    275         fake_drive_service_,
    276         test_cache_root_.path(),
    277         NULL);
    278     return integration_service_;
    279   }
    280 
    281  private:
    282   base::ScopedTempDir test_cache_root_;
    283   drive::FakeDriveService* fake_drive_service_;
    284   drive::DriveIntegrationService* integration_service_;
    285 };
    286 
    287 // Listener to obtain the test relative messages synchronously.
    288 class FileManagerTestListener : public content::NotificationObserver {
    289  public:
    290   struct Message {
    291     int type;
    292     std::string message;
    293     extensions::TestSendMessageFunction* function;
    294   };
    295 
    296   FileManagerTestListener() {
    297     registrar_.Add(this,
    298                    chrome::NOTIFICATION_EXTENSION_TEST_PASSED,
    299                    content::NotificationService::AllSources());
    300     registrar_.Add(this,
    301                    chrome::NOTIFICATION_EXTENSION_TEST_FAILED,
    302                    content::NotificationService::AllSources());
    303     registrar_.Add(this,
    304                    chrome::NOTIFICATION_EXTENSION_TEST_MESSAGE,
    305                    content::NotificationService::AllSources());
    306   }
    307 
    308   Message GetNextMessage() {
    309     if (messages_.empty())
    310       content::RunMessageLoop();
    311     const Message entry = messages_.front();
    312     messages_.pop_front();
    313     return entry;
    314   }
    315 
    316   virtual void Observe(int type,
    317                        const content::NotificationSource& source,
    318                        const content::NotificationDetails& details) OVERRIDE {
    319     Message entry;
    320     entry.type = type;
    321     entry.message = type != chrome::NOTIFICATION_EXTENSION_TEST_PASSED ?
    322         *content::Details<std::string>(details).ptr() :
    323         std::string();
    324     entry.function = type == chrome::NOTIFICATION_EXTENSION_TEST_MESSAGE ?
    325         content::Source<extensions::TestSendMessageFunction>(source).ptr() :
    326         NULL;
    327     messages_.push_back(entry);
    328     base::MessageLoopForUI::current()->Quit();
    329   }
    330 
    331  private:
    332   std::deque<Message> messages_;
    333   content::NotificationRegistrar registrar_;
    334 };
    335 
    336 // Parameter of FileManagerBrowserTest.
    337 // The second value is the case name of JavaScript.
    338 typedef std::tr1::tuple<GuestMode, const char*> TestParameter;
    339 
    340 // The base test class.
    341 class FileManagerBrowserTest :
    342       public ExtensionApiTest,
    343       public ::testing::WithParamInterface<TestParameter> {
    344  protected:
    345   FileManagerBrowserTest() :
    346       local_volume_(new LocalTestVolume),
    347       drive_volume_(std::tr1::get<0>(GetParam()) != IN_GUEST_MODE ?
    348                     new DriveTestVolume() : NULL) {}
    349 
    350   virtual void SetUp() OVERRIDE {
    351     // TODO(danakj): The GPU Video Decoder needs real GL bindings.
    352     // crbug.com/269087
    353     UseRealGLBindings();
    354 
    355     ExtensionApiTest::SetUp();
    356   }
    357 
    358   virtual void SetUpInProcessBrowserTestFixture() OVERRIDE;
    359 
    360   virtual void SetUpOnMainThread() OVERRIDE;
    361 
    362   // Adds an incognito and guest-mode flags for tests in the guest mode.
    363   virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE;
    364 
    365   // Loads our testing extension and sends it a string identifying the current
    366   // test.
    367   void StartTest();
    368 
    369   const scoped_ptr<LocalTestVolume> local_volume_;
    370   const scoped_ptr<DriveTestVolume> drive_volume_;
    371 };
    372 
    373 void FileManagerBrowserTest::SetUpInProcessBrowserTestFixture() {
    374   ExtensionApiTest::SetUpInProcessBrowserTestFixture();
    375   extensions::ComponentLoader::EnableBackgroundExtensionsForTesting();
    376   if (drive_volume_)
    377     ASSERT_TRUE(drive_volume_->SetUp());
    378 }
    379 
    380 void FileManagerBrowserTest::SetUpOnMainThread() {
    381   ExtensionApiTest::SetUpOnMainThread();
    382   ASSERT_TRUE(local_volume_->Mount(browser()->profile()));
    383 
    384   for (size_t i = 0; i < arraysize(kTestEntrySetCommon); ++i)
    385     local_volume_->CreateEntry(kTestEntrySetCommon[i]);
    386 
    387   if (drive_volume_) {
    388     // Install the web server to serve the mocked share dialog.
    389     ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
    390     const GURL share_url_base(embedded_test_server()->GetURL(
    391         "/chromeos/file_manager/share_dialog_mock/index.html"));
    392     drive_volume_->ConfigureShareUrlBase(share_url_base);
    393 
    394     for (size_t i = 0; i < arraysize(kTestEntrySetCommon); ++i)
    395       drive_volume_->CreateEntry(kTestEntrySetCommon[i]);
    396 
    397     // For testing Drive, create more entries with Drive specific attributes.
    398     // TODO(haruki): Add a case for an entry cached by DriveCache.
    399     for (size_t i = 0; i < arraysize(kTestEntrySetDriveOnly); ++i)
    400       drive_volume_->CreateEntry(kTestEntrySetDriveOnly[i]);
    401 
    402     test_util::WaitUntilDriveMountPointIsAdded(browser()->profile());
    403   }
    404 }
    405 
    406 void FileManagerBrowserTest::SetUpCommandLine(CommandLine* command_line) {
    407   if (std::tr1::get<0>(GetParam()) == IN_GUEST_MODE) {
    408     command_line->AppendSwitch(chromeos::switches::kGuestSession);
    409     command_line->AppendSwitchNative(chromeos::switches::kLoginUser, "");
    410     command_line->AppendSwitch(switches::kIncognito);
    411   }
    412   ExtensionApiTest::SetUpCommandLine(command_line);
    413 }
    414 
    415 IN_PROC_BROWSER_TEST_P(FileManagerBrowserTest, Test) {
    416   // Launch the extension.
    417   base::FilePath path = test_data_dir_.AppendASCII("file_manager_browsertest");
    418   const extensions::Extension* extension = LoadExtensionAsComponent(path);
    419   ASSERT_TRUE(extension);
    420 
    421   // Handle the messages from JavaScript.
    422   // The while loop is break when the test is passed or failed.
    423   FileManagerTestListener listener;
    424   while (true) {
    425     FileManagerTestListener::Message entry = listener.GetNextMessage();
    426     if (entry.type == chrome::NOTIFICATION_EXTENSION_TEST_PASSED) {
    427       // Test succeed.
    428       break;
    429     } else if (entry.type == chrome::NOTIFICATION_EXTENSION_TEST_FAILED) {
    430       // Test failed.
    431       ADD_FAILURE() << entry.message;
    432       break;
    433     } else if (entry.message == "getTestName") {
    434       // Pass the test case name.
    435       entry.function->Reply(std::tr1::get<1>(GetParam()));
    436     } else if (entry.message == "isInGuestMode") {
    437       // Obtains whther the test is in guest mode or not.
    438       entry.function->Reply(std::tr1::get<0>(GetParam()) ? "true" : "false");
    439     } else if (entry.message == "addEntry") {
    440       // Add the extra entry.
    441       const TestEntryInfo file = {
    442         FILE,
    443         "music.ogg",  // Prototype file name.
    444         "newly added file.ogg",  // Target file name.
    445         "audio/ogg",
    446         NONE,
    447         "4 Sep 1998 00:00:00"
    448       };
    449       if (drive_volume_)
    450         drive_volume_->CreateEntry(file);
    451       local_volume_->CreateEntry(file);
    452       entry.function->Reply("onEntryAdded");
    453     }
    454   }
    455 }
    456 
    457 INSTANTIATE_TEST_CASE_P(
    458     FileDisplay,
    459     FileManagerBrowserTest,
    460     ::testing::Values(TestParameter(NOT_IN_GUEST_MODE, "fileDisplayDownloads"),
    461                       TestParameter(IN_GUEST_MODE, "fileDisplayDownloads"),
    462                       TestParameter(NOT_IN_GUEST_MODE, "fileDisplayDrive")));
    463 
    464 // TODO(mtomasz): Fix this test. crbug.com/252561
    465 INSTANTIATE_TEST_CASE_P(
    466     OpenSpecialTypes,
    467     FileManagerBrowserTest,
    468     ::testing::Values(TestParameter(IN_GUEST_MODE, "videoOpenDownloads"),
    469                       TestParameter(NOT_IN_GUEST_MODE, "videoOpenDownloads"),
    470                       TestParameter(NOT_IN_GUEST_MODE, "videoOpenDrive"),
    471                       TestParameter(IN_GUEST_MODE, "audioOpenDownloads"),
    472                       TestParameter(NOT_IN_GUEST_MODE, "audioOpenDownloads"),
    473                       TestParameter(NOT_IN_GUEST_MODE, "audioOpenDrive"),
    474                       TestParameter(IN_GUEST_MODE, "galleryOpenDownloads"),
    475                       TestParameter(NOT_IN_GUEST_MODE,
    476                                     "galleryOpenDownloads"),
    477                       TestParameter(NOT_IN_GUEST_MODE, "galleryOpenDrive")));
    478 
    479 INSTANTIATE_TEST_CASE_P(
    480     KeyboardOperations,
    481     FileManagerBrowserTest,
    482     ::testing::Values(TestParameter(IN_GUEST_MODE, "keyboardDeleteDownloads"),
    483                       TestParameter(NOT_IN_GUEST_MODE,
    484                                     "keyboardDeleteDownloads"),
    485                       TestParameter(NOT_IN_GUEST_MODE, "keyboardDeleteDrive"),
    486                       TestParameter(IN_GUEST_MODE, "keyboardCopyDownloads"),
    487                       TestParameter(NOT_IN_GUEST_MODE, "keyboardCopyDownloads"),
    488                       TestParameter(NOT_IN_GUEST_MODE, "keyboardCopyDrive")));
    489 
    490 INSTANTIATE_TEST_CASE_P(
    491     DriveSpecific,
    492     FileManagerBrowserTest,
    493     ::testing::Values(TestParameter(NOT_IN_GUEST_MODE, "openSidebarRecent"),
    494                       TestParameter(NOT_IN_GUEST_MODE, "openSidebarOffline"),
    495                       TestParameter(NOT_IN_GUEST_MODE,
    496                                     "openSidebarSharedWithMe"),
    497                       TestParameter(NOT_IN_GUEST_MODE, "autocomplete")));
    498 
    499 INSTANTIATE_TEST_CASE_P(
    500     Transfer,
    501     FileManagerBrowserTest,
    502     ::testing::Values(TestParameter(NOT_IN_GUEST_MODE,
    503                                     "transferFromDriveToDownloads"),
    504                       TestParameter(NOT_IN_GUEST_MODE,
    505                                     "transferFromDownloadsToDrive"),
    506                       TestParameter(NOT_IN_GUEST_MODE,
    507                                     "transferFromSharedToDownloads"),
    508                       TestParameter(NOT_IN_GUEST_MODE,
    509                                     "transferFromSharedToDrive"),
    510                       TestParameter(NOT_IN_GUEST_MODE,
    511                                     "transferFromRecentToDownloads"),
    512                       TestParameter(NOT_IN_GUEST_MODE,
    513                                     "transferFromRecentToDrive"),
    514                       TestParameter(NOT_IN_GUEST_MODE,
    515                                     "transferFromOfflineToDownloads"),
    516                       TestParameter(NOT_IN_GUEST_MODE,
    517                                     "transferFromOfflineToDrive")));
    518 
    519 INSTANTIATE_TEST_CASE_P(
    520      HideSearchBox,
    521      FileManagerBrowserTest,
    522      ::testing::Values(TestParameter(IN_GUEST_MODE, "hideSearchBox"),
    523                        TestParameter(NOT_IN_GUEST_MODE, "hideSearchBox")));
    524 
    525 INSTANTIATE_TEST_CASE_P(
    526     RestorePrefs,
    527     FileManagerBrowserTest,
    528     ::testing::Values(TestParameter(IN_GUEST_MODE, "restoreSortColumn"),
    529                       TestParameter(NOT_IN_GUEST_MODE, "restoreSortColumn"),
    530                       TestParameter(IN_GUEST_MODE, "restoreCurrentView"),
    531                       TestParameter(NOT_IN_GUEST_MODE, "restoreCurrentView")));
    532 
    533 INSTANTIATE_TEST_CASE_P(
    534     ShareDialog,
    535     FileManagerBrowserTest,
    536     ::testing::Values(TestParameter(NOT_IN_GUEST_MODE, "shareFile"),
    537                       TestParameter(NOT_IN_GUEST_MODE, "shareDirectory")));
    538 
    539 }  // namespace
    540 }  // namespace file_manager
    541