file_manager_browsertest.cc revision 5d1f7b1de12d16ceb2c938c56701a3e8bfa558f7
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 "apps/app_window.h"
15#include "apps/app_window_registry.h"
16#include "ash/session_state_delegate.h"
17#include "ash/shell.h"
18#include "base/bind.h"
19#include "base/callback.h"
20#include "base/file_util.h"
21#include "base/files/file_path.h"
22#include "base/json/json_reader.h"
23#include "base/json/json_value_converter.h"
24#include "base/json/json_writer.h"
25#include "base/prefs/pref_service.h"
26#include "base/strings/string_piece.h"
27#include "base/strings/stringprintf.h"
28#include "base/strings/utf_string_conversions.h"
29#include "base/time/time.h"
30#include "chrome/browser/browser_process.h"
31#include "chrome/browser/chrome_notification_types.h"
32#include "chrome/browser/chromeos/drive/drive_integration_service.h"
33#include "chrome/browser/chromeos/drive/file_system_interface.h"
34#include "chrome/browser/chromeos/drive/test_util.h"
35#include "chrome/browser/chromeos/file_manager/app_id.h"
36#include "chrome/browser/chromeos/file_manager/drive_test_util.h"
37#include "chrome/browser/chromeos/file_manager/path_util.h"
38#include "chrome/browser/chromeos/file_manager/volume_manager.h"
39#include "chrome/browser/chromeos/login/user_manager.h"
40#include "chrome/browser/chromeos/profiles/profile_helper.h"
41#include "chrome/browser/drive/fake_drive_service.h"
42#include "chrome/browser/extensions/api/test/test_api.h"
43#include "chrome/browser/extensions/component_loader.h"
44#include "chrome/browser/extensions/extension_apitest.h"
45#include "chrome/browser/extensions/extension_test_message_listener.h"
46#include "chrome/browser/profiles/profile.h"
47#include "chrome/browser/profiles/profile_manager.h"
48#include "chrome/browser/ui/ash/multi_user/multi_user_util.h"
49#include "chrome/browser/ui/ash/multi_user/multi_user_window_manager.h"
50#include "chrome/common/chrome_switches.h"
51#include "chrome/common/pref_names.h"
52#include "chromeos/chromeos_switches.h"
53#include "content/public/browser/browser_context.h"
54#include "content/public/browser/notification_service.h"
55#include "content/public/test/test_utils.h"
56#include "extensions/common/extension.h"
57#include "google_apis/drive/gdata_wapi_parser.h"
58#include "google_apis/drive/test_util.h"
59#include "net/test/embedded_test_server/embedded_test_server.h"
60#include "webkit/browser/fileapi/external_mount_points.h"
61
62using drive::DriveIntegrationServiceFactory;
63
64namespace file_manager {
65namespace {
66
67enum EntryType {
68  FILE,
69  DIRECTORY,
70};
71
72enum TargetVolume {
73  LOCAL_VOLUME,
74  DRIVE_VOLUME,
75};
76
77enum SharedOption {
78  NONE,
79  SHARED,
80};
81
82enum GuestMode {
83  NOT_IN_GUEST_MODE,
84  IN_GUEST_MODE,
85};
86
87// This global operator is used from Google Test to format error messages.
88std::ostream& operator<<(std::ostream& os, const GuestMode& guest_mode) {
89  return os << (guest_mode == IN_GUEST_MODE ?
90                "IN_GUEST_MODE" : "NOT_IN_GUEST_MODE");
91}
92
93// Maps the given string to EntryType. Returns true on success.
94bool MapStringToEntryType(const base::StringPiece& value, EntryType* output) {
95  if (value == "file")
96    *output = FILE;
97  else if (value == "directory")
98    *output = DIRECTORY;
99  else
100    return false;
101  return true;
102}
103
104// Maps the given string to SharedOption. Returns true on success.
105bool MapStringToSharedOption(const base::StringPiece& value,
106                             SharedOption* output) {
107  if (value == "shared")
108    *output = SHARED;
109  else if (value == "none")
110    *output = NONE;
111  else
112    return false;
113  return true;
114}
115
116// Maps the given string to TargetVolume. Returns true on success.
117bool MapStringToTargetVolume(const base::StringPiece& value,
118                             TargetVolume* output) {
119  if (value == "drive")
120    *output = DRIVE_VOLUME;
121  else if (value == "local")
122    *output = LOCAL_VOLUME;
123  else
124    return false;
125  return true;
126}
127
128// Maps the given string to base::Time. Returns true on success.
129bool MapStringToTime(const base::StringPiece& value, base::Time* time) {
130  return base::Time::FromString(value.as_string().c_str(), time);
131}
132
133// Test data of file or directory.
134struct TestEntryInfo {
135  TestEntryInfo() : type(FILE), shared_option(NONE) {}
136
137  TestEntryInfo(EntryType type,
138                const std::string& source_file_name,
139                const std::string& target_path,
140                const std::string& mime_type,
141                SharedOption shared_option,
142                const base::Time& last_modified_time) :
143      type(type),
144      source_file_name(source_file_name),
145      target_path(target_path),
146      mime_type(mime_type),
147      shared_option(shared_option),
148      last_modified_time(last_modified_time) {
149  }
150
151  EntryType type;
152  std::string source_file_name;  // Source file name to be used as a prototype.
153  std::string target_path;  // Target file or directory path.
154  std::string mime_type;
155  SharedOption shared_option;
156  base::Time last_modified_time;
157
158  // Registers the member information to the given converter.
159  static void RegisterJSONConverter(
160      base::JSONValueConverter<TestEntryInfo>* converter);
161};
162
163// static
164void TestEntryInfo::RegisterJSONConverter(
165    base::JSONValueConverter<TestEntryInfo>* converter) {
166  converter->RegisterCustomField("type",
167                                 &TestEntryInfo::type,
168                                 &MapStringToEntryType);
169  converter->RegisterStringField("sourceFileName",
170                                 &TestEntryInfo::source_file_name);
171  converter->RegisterStringField("targetPath", &TestEntryInfo::target_path);
172  converter->RegisterStringField("mimeType", &TestEntryInfo::mime_type);
173  converter->RegisterCustomField("sharedOption",
174                                 &TestEntryInfo::shared_option,
175                                 &MapStringToSharedOption);
176  converter->RegisterCustomField("lastModifiedTime",
177                                 &TestEntryInfo::last_modified_time,
178                                 &MapStringToTime);
179}
180
181// Message from JavaScript to add entries.
182struct AddEntriesMessage {
183  // Target volume to be added the |entries|.
184  TargetVolume volume;
185
186  // Entries to be added.
187  ScopedVector<TestEntryInfo> entries;
188
189  // Registers the member information to the given converter.
190  static void RegisterJSONConverter(
191      base::JSONValueConverter<AddEntriesMessage>* converter);
192};
193
194
195// static
196void AddEntriesMessage::RegisterJSONConverter(
197    base::JSONValueConverter<AddEntriesMessage>* converter) {
198  converter->RegisterCustomField("volume",
199                                 &AddEntriesMessage::volume,
200                                 &MapStringToTargetVolume);
201  converter->RegisterRepeatedMessage<TestEntryInfo>(
202      "entries",
203      &AddEntriesMessage::entries);
204}
205
206// The local volume class for test.
207// This class provides the operations for a test volume that simulates local
208// drive.
209class LocalTestVolume {
210 public:
211  // Adds this volume to the file system as a local volume. Returns true on
212  // success.
213  bool Mount(Profile* profile) {
214    if (local_path_.empty()) {
215      if (!tmp_dir_.CreateUniqueTempDir())
216        return false;
217      local_path_ = tmp_dir_.path().Append("Downloads");
218    }
219
220    return VolumeManager::Get(profile)->RegisterDownloadsDirectoryForTesting(
221        local_path_) && base::CreateDirectory(local_path_);
222  }
223
224  void CreateEntry(const TestEntryInfo& entry) {
225    const base::FilePath target_path =
226        local_path_.AppendASCII(entry.target_path);
227
228    entries_.insert(std::make_pair(target_path, entry));
229    switch (entry.type) {
230      case FILE: {
231        const base::FilePath source_path =
232            google_apis::test_util::GetTestFilePath("chromeos/file_manager").
233            AppendASCII(entry.source_file_name);
234        ASSERT_TRUE(base::CopyFile(source_path, target_path))
235            << "Copy from " << source_path.value()
236            << " to " << target_path.value() << " failed.";
237        break;
238      }
239      case DIRECTORY:
240        ASSERT_TRUE(base::CreateDirectory(target_path)) <<
241            "Failed to create a directory: " << target_path.value();
242        break;
243    }
244    ASSERT_TRUE(UpdateModifiedTime(entry));
245  }
246
247 private:
248  // Updates ModifiedTime of the entry and its parents by referring
249  // TestEntryInfo. Returns true on success.
250  bool UpdateModifiedTime(const TestEntryInfo& entry) {
251    const base::FilePath path = local_path_.AppendASCII(entry.target_path);
252    if (!base::TouchFile(path, entry.last_modified_time,
253                         entry.last_modified_time))
254      return false;
255
256    // Update the modified time of parent directories because it may be also
257    // affected by the update of child items.
258    if (path.DirName() != local_path_) {
259      const std::map<base::FilePath, const TestEntryInfo>::iterator it =
260          entries_.find(path.DirName());
261      if (it == entries_.end())
262        return false;
263      return UpdateModifiedTime(it->second);
264    }
265    return true;
266  }
267
268  base::FilePath local_path_;
269  base::ScopedTempDir tmp_dir_;
270  std::map<base::FilePath, const TestEntryInfo> entries_;
271};
272
273// The drive volume class for test.
274// This class provides the operations for a test volume that simulates Google
275// drive.
276class DriveTestVolume {
277 public:
278  DriveTestVolume() : integration_service_(NULL) {
279  }
280
281  // Sends request to add this volume to the file system as Google drive.
282  // This method must be called at SetUp method of FileManagerBrowserTestBase.
283  // Returns true on success.
284  bool SetUp() {
285    if (!test_cache_root_.CreateUniqueTempDir())
286      return false;
287    create_drive_integration_service_ =
288        base::Bind(&DriveTestVolume::CreateDriveIntegrationService,
289                   base::Unretained(this));
290    service_factory_for_test_.reset(
291        new DriveIntegrationServiceFactory::ScopedFactoryForTest(
292            &create_drive_integration_service_));
293    return true;
294  }
295
296  void CreateEntry(Profile* profile, const TestEntryInfo& entry) {
297    const base::FilePath path =
298        base::FilePath::FromUTF8Unsafe(entry.target_path);
299    const std::string target_name = path.BaseName().AsUTF8Unsafe();
300
301    // Obtain the parent entry.
302    drive::FileError error = drive::FILE_ERROR_OK;
303    scoped_ptr<drive::ResourceEntry> parent_entry(new drive::ResourceEntry);
304    integration_service_->file_system()->GetResourceEntry(
305        drive::util::GetDriveMyDriveRootPath().Append(path).DirName(),
306        google_apis::test_util::CreateCopyResultCallback(
307            &error, &parent_entry));
308    drive::test_util::RunBlockingPoolTask();
309    ASSERT_EQ(drive::FILE_ERROR_OK, error);
310    ASSERT_TRUE(parent_entry);
311
312    switch (entry.type) {
313      case FILE:
314        CreateFile(profile,
315                   entry.source_file_name,
316                   parent_entry->resource_id(),
317                   target_name,
318                   entry.mime_type,
319                   entry.shared_option == SHARED,
320                   entry.last_modified_time);
321        break;
322      case DIRECTORY:
323        CreateDirectory(profile,
324                        parent_entry->resource_id(),
325                        target_name,
326                        entry.last_modified_time);
327        break;
328    }
329  }
330
331  // Creates an empty directory with the given |name| and |modification_time|.
332  void CreateDirectory(Profile* profile,
333                       const std::string& parent_id,
334                       const std::string& target_name,
335                       const base::Time& modification_time) {
336    DCHECK(fake_drive_service_.count(profile));
337
338    google_apis::GDataErrorCode error = google_apis::GDATA_OTHER_ERROR;
339    scoped_ptr<google_apis::ResourceEntry> resource_entry;
340    fake_drive_service_[profile]->AddNewDirectory(
341        parent_id,
342        target_name,
343        drive::DriveServiceInterface::AddNewDirectoryOptions(),
344        google_apis::test_util::CreateCopyResultCallback(&error,
345                                                         &resource_entry));
346    base::MessageLoop::current()->RunUntilIdle();
347    ASSERT_EQ(google_apis::HTTP_CREATED, error);
348    ASSERT_TRUE(resource_entry);
349
350    fake_drive_service_[profile]->SetLastModifiedTime(
351        resource_entry->resource_id(),
352        modification_time,
353        google_apis::test_util::CreateCopyResultCallback(&error,
354                                                         &resource_entry));
355    base::MessageLoop::current()->RunUntilIdle();
356    ASSERT_TRUE(error == google_apis::HTTP_SUCCESS);
357    ASSERT_TRUE(resource_entry);
358    CheckForUpdates();
359  }
360
361  // Creates a test file with the given spec.
362  // Serves |test_file_name| file. Pass an empty string for an empty file.
363  void CreateFile(Profile* profile,
364                  const std::string& source_file_name,
365                  const std::string& parent_id,
366                  const std::string& target_name,
367                  const std::string& mime_type,
368                  bool shared_with_me,
369                  const base::Time& modification_time) {
370    DCHECK(fake_drive_service_.count(profile));
371
372    google_apis::GDataErrorCode error = google_apis::GDATA_OTHER_ERROR;
373
374    std::string content_data;
375    if (!source_file_name.empty()) {
376      base::FilePath source_file_path =
377          google_apis::test_util::GetTestFilePath("chromeos/file_manager").
378              AppendASCII(source_file_name);
379      ASSERT_TRUE(base::ReadFileToString(source_file_path, &content_data));
380    }
381
382    scoped_ptr<google_apis::ResourceEntry> resource_entry;
383    fake_drive_service_[profile]->AddNewFile(
384        mime_type,
385        content_data,
386        parent_id,
387        target_name,
388        shared_with_me,
389        google_apis::test_util::CreateCopyResultCallback(&error,
390                                                         &resource_entry));
391    base::MessageLoop::current()->RunUntilIdle();
392    ASSERT_EQ(google_apis::HTTP_CREATED, error);
393    ASSERT_TRUE(resource_entry);
394
395    fake_drive_service_[profile]->SetLastModifiedTime(
396        resource_entry->resource_id(),
397        modification_time,
398        google_apis::test_util::CreateCopyResultCallback(&error,
399                                                         &resource_entry));
400    base::MessageLoop::current()->RunUntilIdle();
401    ASSERT_EQ(google_apis::HTTP_SUCCESS, error);
402    ASSERT_TRUE(resource_entry);
403
404    CheckForUpdates();
405  }
406
407  // Notifies FileSystem that the contents in FakeDriveService are
408  // changed, hence the new contents should be fetched.
409  void CheckForUpdates() {
410    if (integration_service_ && integration_service_->file_system()) {
411      integration_service_->file_system()->CheckForUpdates();
412    }
413  }
414
415  // Sets the url base for the test server to be used to generate share urls
416  // on the files and directories.
417  void ConfigureShareUrlBase(Profile* profile, const GURL& share_url_base) {
418    DCHECK(fake_drive_service_.count(profile));
419    fake_drive_service_[profile]->set_share_url_base(share_url_base);
420  }
421
422  drive::DriveIntegrationService* CreateDriveIntegrationService(
423      Profile* profile) {
424    base::FilePath subdir;
425    if (!base::CreateTemporaryDirInDir(test_cache_root_.path(),
426                                       base::FilePath::StringType(),
427                                       &subdir)) {
428      return NULL;
429    }
430
431    drive::FakeDriveService* const fake_drive_service =
432        new drive::FakeDriveService;
433    fake_drive_service->LoadResourceListForWapi(
434        "gdata/empty_feed.json");
435    fake_drive_service->LoadAccountMetadataForWapi(
436        "gdata/account_metadata.json");
437    fake_drive_service->LoadAppListForDriveApi("drive/applist.json");
438
439    integration_service_ = new drive::DriveIntegrationService(
440        profile, NULL, fake_drive_service, std::string(), subdir, NULL);
441    fake_drive_service_[profile] = fake_drive_service;
442    return integration_service_;
443  }
444
445 private:
446  base::ScopedTempDir test_cache_root_;
447  std::map<Profile*, drive::FakeDriveService*> fake_drive_service_;
448  drive::DriveIntegrationService* integration_service_;
449  DriveIntegrationServiceFactory::FactoryCallback
450      create_drive_integration_service_;
451  scoped_ptr<DriveIntegrationServiceFactory::ScopedFactoryForTest>
452      service_factory_for_test_;
453};
454
455// Listener to obtain the test relative messages synchronously.
456class FileManagerTestListener : public content::NotificationObserver {
457 public:
458  struct Message {
459    int type;
460    std::string message;
461    scoped_refptr<extensions::TestSendMessageFunction> function;
462  };
463
464  FileManagerTestListener() {
465    registrar_.Add(this,
466                   chrome::NOTIFICATION_EXTENSION_TEST_PASSED,
467                   content::NotificationService::AllSources());
468    registrar_.Add(this,
469                   chrome::NOTIFICATION_EXTENSION_TEST_FAILED,
470                   content::NotificationService::AllSources());
471    registrar_.Add(this,
472                   chrome::NOTIFICATION_EXTENSION_TEST_MESSAGE,
473                   content::NotificationService::AllSources());
474  }
475
476  Message GetNextMessage() {
477    if (messages_.empty())
478      content::RunMessageLoop();
479    const Message entry = messages_.front();
480    messages_.pop_front();
481    return entry;
482  }
483
484  virtual void Observe(int type,
485                       const content::NotificationSource& source,
486                       const content::NotificationDetails& details) OVERRIDE {
487    Message entry;
488    entry.type = type;
489    entry.message = type != chrome::NOTIFICATION_EXTENSION_TEST_PASSED ?
490        *content::Details<std::string>(details).ptr() :
491        std::string();
492    entry.function = type == chrome::NOTIFICATION_EXTENSION_TEST_MESSAGE ?
493        content::Source<extensions::TestSendMessageFunction>(source).ptr() :
494        NULL;
495    messages_.push_back(entry);
496    base::MessageLoopForUI::current()->Quit();
497  }
498
499 private:
500  std::deque<Message> messages_;
501  content::NotificationRegistrar registrar_;
502};
503
504// The base test class.
505class FileManagerBrowserTestBase : public ExtensionApiTest {
506 protected:
507  virtual void SetUpInProcessBrowserTestFixture() OVERRIDE;
508
509  virtual void SetUpOnMainThread() OVERRIDE;
510
511  // Adds an incognito and guest-mode flags for tests in the guest mode.
512  virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE;
513
514  // Loads our testing extension and sends it a string identifying the current
515  // test.
516  void StartTest();
517
518  // Overriding point for test configurations.
519  virtual GuestMode GetGuestModeParam() const = 0;
520  virtual const char* GetTestCaseNameParam() const = 0;
521  virtual std::string OnMessage(const std::string& name,
522                                const base::Value* value);
523
524  scoped_ptr<LocalTestVolume> local_volume_;
525  scoped_ptr<DriveTestVolume> drive_volume_;
526};
527
528void FileManagerBrowserTestBase::SetUpInProcessBrowserTestFixture() {
529  ExtensionApiTest::SetUpInProcessBrowserTestFixture();
530  extensions::ComponentLoader::EnableBackgroundExtensionsForTesting();
531
532  local_volume_.reset(new LocalTestVolume);
533  if (GetGuestModeParam() != IN_GUEST_MODE) {
534    drive_volume_.reset(new DriveTestVolume);
535    ASSERT_TRUE(drive_volume_->SetUp());
536  }
537}
538
539void FileManagerBrowserTestBase::SetUpOnMainThread() {
540  ExtensionApiTest::SetUpOnMainThread();
541  ASSERT_TRUE(local_volume_->Mount(profile()));
542
543  if (drive_volume_) {
544    // Install the web server to serve the mocked share dialog.
545    ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
546    const GURL share_url_base(embedded_test_server()->GetURL(
547        "/chromeos/file_manager/share_dialog_mock/index.html"));
548    drive_volume_->ConfigureShareUrlBase(profile(), share_url_base);
549    test_util::WaitUntilDriveMountPointIsAdded(profile());
550  }
551}
552
553void FileManagerBrowserTestBase::SetUpCommandLine(CommandLine* command_line) {
554  if (GetGuestModeParam() == IN_GUEST_MODE) {
555    command_line->AppendSwitch(chromeos::switches::kGuestSession);
556    command_line->AppendSwitchNative(chromeos::switches::kLoginUser, "");
557    command_line->AppendSwitch(switches::kIncognito);
558  }
559  ExtensionApiTest::SetUpCommandLine(command_line);
560}
561
562void FileManagerBrowserTestBase::StartTest() {
563  // Launch the extension.
564  base::FilePath path = test_data_dir_.AppendASCII("file_manager_browsertest");
565  const extensions::Extension* extension = LoadExtensionAsComponent(path);
566  ASSERT_TRUE(extension);
567
568  // Handle the messages from JavaScript.
569  // The while loop is break when the test is passed or failed.
570  FileManagerTestListener listener;
571  while (true) {
572    FileManagerTestListener::Message entry = listener.GetNextMessage();
573    if (entry.type == chrome::NOTIFICATION_EXTENSION_TEST_PASSED) {
574      // Test succeed.
575      break;
576    } else if (entry.type == chrome::NOTIFICATION_EXTENSION_TEST_FAILED) {
577      // Test failed.
578      ADD_FAILURE() << entry.message;
579      break;
580    }
581
582    // Parse the message value as JSON.
583    const scoped_ptr<const base::Value> value(
584        base::JSONReader::Read(entry.message));
585
586    // If the message is not the expected format, just ignore it.
587    const base::DictionaryValue* message_dictionary = NULL;
588    std::string name;
589    if (!value || !value->GetAsDictionary(&message_dictionary) ||
590        !message_dictionary->GetString("name", &name))
591      continue;
592
593    entry.function->Reply(OnMessage(name, value.get()));
594  }
595}
596
597std::string FileManagerBrowserTestBase::OnMessage(const std::string& name,
598                                                  const base::Value* value) {
599  if (name == "getTestName") {
600    // Pass the test case name.
601    return GetTestCaseNameParam();
602  } else if (name == "getRootPaths") {
603    // Pass the root paths.
604    const scoped_ptr<base::DictionaryValue> res(new base::DictionaryValue());
605    res->SetString("downloads",
606        "/" + util::GetDownloadsMountPointName(profile()));
607    res->SetString("drive",
608        "/" + drive::util::GetDriveMountPointPath(profile()
609            ).BaseName().AsUTF8Unsafe() + "/root");
610    std::string jsonString;
611    base::JSONWriter::Write(res.get(), &jsonString);
612    return jsonString;
613  } else if (name == "isInGuestMode") {
614    // Obtain whether the test is in guest mode or not.
615    return GetGuestModeParam() ? "true" : "false";
616  } else if (name == "getCwsWidgetContainerMockUrl") {
617    // Obtain whether the test is in guest mode or not.
618    const GURL url = embedded_test_server()->GetURL(
619          "/chromeos/file_manager/cws_container_mock/index.html");
620    std::string origin = url.GetOrigin().spec();
621
622    // Removes trailing a slash.
623    if (*origin.rbegin() == '/')
624      origin.resize(origin.length() - 1);
625
626    const scoped_ptr<base::DictionaryValue> res(new base::DictionaryValue());
627    res->SetString("url", url.spec());
628    res->SetString("origin", origin);
629    std::string jsonString;
630    base::JSONWriter::Write(res.get(), &jsonString);
631    return jsonString;
632  } else if (name == "addEntries") {
633    // Add entries to the specified volume.
634    base::JSONValueConverter<AddEntriesMessage> add_entries_message_converter;
635    AddEntriesMessage message;
636    if (!add_entries_message_converter.Convert(*value, &message))
637      return "onError";
638    for (size_t i = 0; i < message.entries.size(); ++i) {
639      switch (message.volume) {
640        case LOCAL_VOLUME:
641          local_volume_->CreateEntry(*message.entries[i]);
642          break;
643        case DRIVE_VOLUME:
644          if (drive_volume_)
645            drive_volume_->CreateEntry(profile(), *message.entries[i]);
646          break;
647        default:
648          NOTREACHED();
649          break;
650      }
651    }
652    return "onEntryAdded";
653  }
654  return "unknownMessage";
655}
656
657// Parameter of FileManagerBrowserTest.
658// The second value is the case name of JavaScript.
659typedef std::tr1::tuple<GuestMode, const char*> TestParameter;
660
661// Test fixture class for normal (not multi-profile related) tests.
662class FileManagerBrowserTest :
663      public FileManagerBrowserTestBase,
664      public ::testing::WithParamInterface<TestParameter> {
665  virtual GuestMode GetGuestModeParam() const OVERRIDE {
666    return std::tr1::get<0>(GetParam());
667  }
668  virtual const char* GetTestCaseNameParam() const OVERRIDE {
669    return std::tr1::get<1>(GetParam());
670  }
671};
672
673IN_PROC_BROWSER_TEST_P(FileManagerBrowserTest, Test) {
674  StartTest();
675}
676
677INSTANTIATE_TEST_CASE_P(
678    FileDisplay,
679    FileManagerBrowserTest,
680    ::testing::Values(TestParameter(NOT_IN_GUEST_MODE, "fileDisplayDownloads"),
681                      TestParameter(IN_GUEST_MODE, "fileDisplayDownloads"),
682                      TestParameter(NOT_IN_GUEST_MODE, "fileDisplayDrive")));
683
684INSTANTIATE_TEST_CASE_P(
685    OpenSpecialTypes,
686    FileManagerBrowserTest,
687    ::testing::Values(TestParameter(IN_GUEST_MODE, "videoOpenDownloads"),
688                      TestParameter(NOT_IN_GUEST_MODE, "videoOpenDownloads"),
689                      TestParameter(NOT_IN_GUEST_MODE, "videoOpenDrive"),
690// Audio player tests are temporary disabled.
691// TODO(yoshiki): Re-enable them: crbug.com/340955.
692//                      TestParameter(IN_GUEST_MODE, "audioOpenDownloads"),
693//                      TestParameter(NOT_IN_GUEST_MODE, "audioOpenDownloads"),
694//                      TestParameter(NOT_IN_GUEST_MODE, "audioOpenDrive"),
695                      TestParameter(IN_GUEST_MODE, "galleryOpenDownloads"),
696                      TestParameter(NOT_IN_GUEST_MODE,
697                                    "galleryOpenDownloads"),
698                      TestParameter(NOT_IN_GUEST_MODE, "galleryOpenDrive")));
699
700INSTANTIATE_TEST_CASE_P(
701    KeyboardOperations,
702    FileManagerBrowserTest,
703    ::testing::Values(TestParameter(IN_GUEST_MODE, "keyboardDeleteDownloads"),
704                      TestParameter(NOT_IN_GUEST_MODE,
705                                    "keyboardDeleteDownloads"),
706                      TestParameter(NOT_IN_GUEST_MODE, "keyboardDeleteDrive"),
707                      TestParameter(IN_GUEST_MODE, "keyboardCopyDownloads"),
708                      TestParameter(NOT_IN_GUEST_MODE, "keyboardCopyDownloads"),
709                      TestParameter(NOT_IN_GUEST_MODE, "keyboardCopyDrive")));
710
711INSTANTIATE_TEST_CASE_P(
712    DriveSpecific,
713    FileManagerBrowserTest,
714    ::testing::Values(TestParameter(NOT_IN_GUEST_MODE, "openSidebarRecent"),
715                      TestParameter(NOT_IN_GUEST_MODE, "openSidebarOffline"),
716                      TestParameter(NOT_IN_GUEST_MODE,
717                                    "openSidebarSharedWithMe"),
718                      TestParameter(NOT_IN_GUEST_MODE, "autocomplete")));
719
720INSTANTIATE_TEST_CASE_P(
721    Transfer,
722    FileManagerBrowserTest,
723    ::testing::Values(TestParameter(NOT_IN_GUEST_MODE,
724                                    "transferFromDriveToDownloads"),
725                      TestParameter(NOT_IN_GUEST_MODE,
726                                    "transferFromDownloadsToDrive"),
727                      TestParameter(NOT_IN_GUEST_MODE,
728                                    "transferFromSharedToDownloads"),
729                      TestParameter(NOT_IN_GUEST_MODE,
730                                    "transferFromSharedToDrive"),
731                      TestParameter(NOT_IN_GUEST_MODE,
732                                    "transferFromRecentToDownloads"),
733                      TestParameter(NOT_IN_GUEST_MODE,
734                                    "transferFromRecentToDrive"),
735                      TestParameter(NOT_IN_GUEST_MODE,
736                                    "transferFromOfflineToDownloads"),
737                      TestParameter(NOT_IN_GUEST_MODE,
738                                    "transferFromOfflineToDrive")));
739
740INSTANTIATE_TEST_CASE_P(
741     HideSearchBox,
742     FileManagerBrowserTest,
743     ::testing::Values(TestParameter(IN_GUEST_MODE, "hideSearchBox"),
744                       TestParameter(NOT_IN_GUEST_MODE, "hideSearchBox")));
745
746INSTANTIATE_TEST_CASE_P(
747    RestorePrefs,
748    FileManagerBrowserTest,
749    ::testing::Values(TestParameter(IN_GUEST_MODE, "restoreSortColumn"),
750                      TestParameter(NOT_IN_GUEST_MODE, "restoreSortColumn"),
751                      TestParameter(IN_GUEST_MODE, "restoreCurrentView"),
752                      TestParameter(NOT_IN_GUEST_MODE, "restoreCurrentView")));
753
754INSTANTIATE_TEST_CASE_P(
755    ShareDialog,
756    FileManagerBrowserTest,
757    ::testing::Values(TestParameter(NOT_IN_GUEST_MODE, "shareFile"),
758                      TestParameter(NOT_IN_GUEST_MODE, "shareDirectory")));
759
760INSTANTIATE_TEST_CASE_P(
761    RestoreGeometry,
762    FileManagerBrowserTest,
763    ::testing::Values(TestParameter(NOT_IN_GUEST_MODE, "restoreGeometry"),
764                      TestParameter(IN_GUEST_MODE, "restoreGeometry")));
765
766INSTANTIATE_TEST_CASE_P(
767    Traverse,
768    FileManagerBrowserTest,
769    ::testing::Values(TestParameter(IN_GUEST_MODE, "traverseDownloads"),
770                      TestParameter(NOT_IN_GUEST_MODE, "traverseDownloads"),
771                      TestParameter(NOT_IN_GUEST_MODE, "traverseDrive")));
772
773INSTANTIATE_TEST_CASE_P(
774    SuggestAppDialog,
775    FileManagerBrowserTest,
776    ::testing::Values(TestParameter(NOT_IN_GUEST_MODE, "suggestAppDialog")));
777
778INSTANTIATE_TEST_CASE_P(
779    ExecuteDefaultTaskOnDownloads,
780    FileManagerBrowserTest,
781    ::testing::Values(
782        TestParameter(NOT_IN_GUEST_MODE, "executeDefaultTaskOnDownloads"),
783        TestParameter(IN_GUEST_MODE, "executeDefaultTaskOnDownloads")));
784
785INSTANTIATE_TEST_CASE_P(
786    ExecuteDefaultTaskOnDrive,
787    FileManagerBrowserTest,
788    ::testing::Values(
789        TestParameter(NOT_IN_GUEST_MODE, "executeDefaultTaskOnDrive")));
790
791INSTANTIATE_TEST_CASE_P(
792    NavigationList,
793    FileManagerBrowserTest,
794    ::testing::Values(TestParameter(NOT_IN_GUEST_MODE,
795                                    "traverseNavigationList")));
796
797INSTANTIATE_TEST_CASE_P(
798    TabIndex,
799    FileManagerBrowserTest,
800    ::testing::Values(TestParameter(NOT_IN_GUEST_MODE, "searchBoxFocus")));
801
802INSTANTIATE_TEST_CASE_P(
803    Thumbnails,
804    FileManagerBrowserTest,
805    ::testing::Values(TestParameter(NOT_IN_GUEST_MODE, "thumbnailsDownloads"),
806                      TestParameter(IN_GUEST_MODE, "thumbnailsDownloads")));
807
808// Structure to describe an account info.
809struct TestAccountInfo {
810  const char* const email;
811  const char* const hash;
812  const char* const display_name;
813};
814
815enum {
816  DUMMY_ACCOUNT_INDEX = 0,
817  PRIMARY_ACCOUNT_INDEX = 1,
818  SECONDARY_ACCOUNT_INDEX_START = 2,
819};
820
821static const TestAccountInfo kTestAccounts[] = {
822  {"__dummy__@invalid.domain", "hashdummy", "Dummy Account"},
823  {"alice@invalid.domain", "hashalice", "Alice"},
824  {"bob@invalid.domain", "hashbob", "Bob"},
825  {"charlie@invalid.domain", "hashcharlie", "Charlie"},
826};
827
828// Test fixture class for testing multi-profile features.
829class MultiProfileFileManagerBrowserTest : public FileManagerBrowserTestBase {
830 protected:
831  // Enables multi-profiles.
832  virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
833    FileManagerBrowserTestBase::SetUpCommandLine(command_line);
834    command_line->AppendSwitch(switches::kMultiProfiles);
835    // Logs in to a dummy profile (For making MultiProfileWindowManager happy;
836    // browser test creates a default window and the manager tries to assign a
837    // user for it, and we need a profile connected to a user.)
838    command_line->AppendSwitchASCII(chromeos::switches::kLoginUser,
839                                    kTestAccounts[DUMMY_ACCOUNT_INDEX].email);
840    command_line->AppendSwitchASCII(chromeos::switches::kLoginProfile,
841                                    kTestAccounts[DUMMY_ACCOUNT_INDEX].hash);
842  }
843
844  // Logs in to the primary profile of this test.
845  virtual void SetUpOnMainThread() OVERRIDE {
846    const TestAccountInfo& info = kTestAccounts[PRIMARY_ACCOUNT_INDEX];
847
848    AddUser(info, true);
849    chromeos::UserManager* const user_manager = chromeos::UserManager::Get();
850    if (user_manager->GetActiveUser() != user_manager->FindUser(info.email))
851      chromeos::UserManager::Get()->SwitchActiveUser(info.email);
852    FileManagerBrowserTestBase::SetUpOnMainThread();
853  }
854
855  // Loads all users to the current session and sets up necessary fields.
856  // This is used for preparing all accounts in PRE_ test setup, and for testing
857  // actual login behavior.
858  void AddAllUsers() {
859    for (size_t i = 0; i < arraysize(kTestAccounts); ++i)
860      AddUser(kTestAccounts[i], i >= SECONDARY_ACCOUNT_INDEX_START);
861  }
862
863  // Add as many as users
864  void AddExtraUsersForStressTesting() {
865    ash::Shell* const shell = ash::Shell::GetInstance();
866    const size_t maxLogin =
867        shell->session_state_delegate()->GetMaximumNumberOfLoggedInUsers();
868
869    for (int i = 0; i + arraysize(kTestAccounts) < maxLogin; ++i) {
870      const std::string email = base::StringPrintf("user%d@invalid.domain", i);
871      const std::string hash = base::StringPrintf("hashuser%d", i);
872      const std::string name = base::StringPrintf("Additional User %d", i);
873      const TestAccountInfo info = {email.c_str(), hash.c_str(), name.c_str()};
874      AddUser(info, true);
875    }
876  }
877
878  // Returns primary profile (if it is already created.)
879  virtual Profile* profile() OVERRIDE {
880    Profile* const profile = chromeos::ProfileHelper::GetProfileByUserIdHash(
881        kTestAccounts[PRIMARY_ACCOUNT_INDEX].hash);
882    return profile ? profile : FileManagerBrowserTestBase::profile();
883  }
884
885  // Sets the test case name (used as a function name in test_cases.js to call.)
886  void set_test_case_name(const std::string& name) { test_case_name_ = name; }
887
888  // Adds a new user for testing to the current session.
889  void AddUser(const TestAccountInfo& info, bool log_in) {
890    chromeos::UserManager* const user_manager = chromeos::UserManager::Get();
891    if (log_in)
892      user_manager->UserLoggedIn(info.email, info.hash, false);
893    user_manager->SaveUserDisplayName(info.email,
894                                      base::UTF8ToUTF16(info.display_name));
895    chromeos::ProfileHelper::GetProfileByUserIdHash(info.hash)->GetPrefs()->
896        SetString(prefs::kGoogleServicesUsername, info.email);
897  }
898
899 private:
900  virtual GuestMode GetGuestModeParam() const OVERRIDE {
901    return NOT_IN_GUEST_MODE;
902  }
903
904  virtual const char* GetTestCaseNameParam() const OVERRIDE {
905    return test_case_name_.c_str();
906  }
907
908  virtual std::string OnMessage(const std::string& name,
909                                const base::Value* value) OVERRIDE {
910    if (name == "addAllUsers") {
911      AddAllUsers();
912      return "true";
913    } else if (name == "getWindowOwnerId") {
914      chrome::MultiUserWindowManager* const window_manager =
915          chrome::MultiUserWindowManager::GetInstance();
916      apps::AppWindowRegistry* const app_window_registry =
917          apps::AppWindowRegistry::Get(profile());
918      DCHECK(window_manager);
919      DCHECK(app_window_registry);
920
921      const apps::AppWindowRegistry::AppWindowList& list =
922          app_window_registry->GetAppWindowsForApp(
923              file_manager::kFileManagerAppId);
924      return list.size() == 1u ?
925          window_manager->GetUserPresentingWindow(
926              list.front()->GetNativeWindow()) : "";
927    }
928    return FileManagerBrowserTestBase::OnMessage(name, value);
929  }
930
931  std::string test_case_name_;
932};
933
934IN_PROC_BROWSER_TEST_F(MultiProfileFileManagerBrowserTest, PRE_BasicDownloads) {
935  AddAllUsers();
936}
937
938IN_PROC_BROWSER_TEST_F(MultiProfileFileManagerBrowserTest, BasicDownloads) {
939  AddAllUsers();
940
941  // Sanity check that normal operations work in multi-profile setting as well.
942  set_test_case_name("keyboardCopyDownloads");
943  StartTest();
944}
945
946IN_PROC_BROWSER_TEST_F(MultiProfileFileManagerBrowserTest, PRE_BasicDrive) {
947  AddAllUsers();
948}
949
950IN_PROC_BROWSER_TEST_F(MultiProfileFileManagerBrowserTest, BasicDrive) {
951  AddAllUsers();
952
953  // Sanity check that normal operations work in multi-profile setting as well.
954  set_test_case_name("keyboardCopyDrive");
955  StartTest();
956}
957
958IN_PROC_BROWSER_TEST_F(MultiProfileFileManagerBrowserTest, PRE_Badge) {
959  AddAllUsers();
960}
961
962IN_PROC_BROWSER_TEST_F(MultiProfileFileManagerBrowserTest, Badge) {
963  // Test the profile badge to be correctly shown and hidden.
964  set_test_case_name("multiProfileBadge");
965  StartTest();
966}
967
968IN_PROC_BROWSER_TEST_F(MultiProfileFileManagerBrowserTest,
969                       PRE_VisitDesktopMenu) {
970  AddAllUsers();
971}
972
973IN_PROC_BROWSER_TEST_F(MultiProfileFileManagerBrowserTest, VisitDesktopMenu) {
974  // Test for the menu item for visiting other profile's desktop.
975  set_test_case_name("multiProfileVisitDesktopMenu");
976  StartTest();
977}
978
979IN_PROC_BROWSER_TEST_F(MultiProfileFileManagerBrowserTest, PRE_MaxUser) {
980  AddAllUsers();
981  AddExtraUsersForStressTesting();
982}
983
984IN_PROC_BROWSER_TEST_F(MultiProfileFileManagerBrowserTest, MaxUser) {
985  // Run the same test as VisitDesktopMenu with maximum number of users logged
986  // in and checks that nothing goes wrong. Here, the primary user (alice) logs
987  // in first, then the "extra" users follow, and then lastly the other users
988  // (bob and charlie) are added in the test. Thus the existing test verifies
989  // that the feature is effectively working with lastly logged in users.
990  AddExtraUsersForStressTesting();
991
992  set_test_case_name("multiProfileVisitDesktopMenu");
993  StartTest();
994}
995
996}  // namespace
997}  // namespace file_manager
998