| OLD | NEW |
| (Empty) |
| 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 <algorithm> | |
| 12 #include <string> | |
| 13 | |
| 14 #include "base/callback.h" | |
| 15 #include "base/file_util.h" | |
| 16 #include "base/files/file_path.h" | |
| 17 #include "base/files/file_path_watcher.h" | |
| 18 #include "base/platform_file.h" | |
| 19 #include "base/threading/platform_thread.h" | |
| 20 #include "base/time.h" | |
| 21 #include "base/utf_string_conversions.h" | |
| 22 #include "chrome/browser/extensions/component_loader.h" | |
| 23 #include "chrome/browser/extensions/extension_apitest.h" | |
| 24 #include "chrome/browser/extensions/extension_service.h" | |
| 25 #include "chrome/browser/extensions/extension_test_message_listener.h" | |
| 26 #include "chrome/browser/profiles/profile.h" | |
| 27 #include "chrome/browser/ui/browser_window.h" | |
| 28 #include "chrome/browser/ui/tabs/tab_strip_model.h" | |
| 29 #include "chrome/common/chrome_switches.h" | |
| 30 #include "chrome/common/extensions/extension.h" | |
| 31 #include "chrome/test/base/ui_test_utils.h" | |
| 32 #include "chromeos/chromeos_switches.h" | |
| 33 #include "content/public/browser/browser_context.h" | |
| 34 #include "content/public/browser/render_view_host.h" | |
| 35 #include "net/base/escape.h" | |
| 36 #include "webkit/fileapi/external_mount_points.h" | |
| 37 | |
| 38 namespace { | |
| 39 | |
| 40 const char kFileManagerExtensionId[] = "hhaomjibdihmijegdhdafkllkbggdgoj"; | |
| 41 | |
| 42 const char kKeyboardTestFileName[] = "world.mpeg"; | |
| 43 const int64 kKeyboardTestFileSize = 1000; | |
| 44 const char kKeyboardTestFileCopyName[] = "world (1).mpeg"; | |
| 45 | |
| 46 // The base test class. Used by FileManagerBrowserLocalTest and | |
| 47 // FileManagerBrowserDriveTest. | |
| 48 // TODO(satorux): Add the latter: crbug.com/224534. | |
| 49 class FileManagerBrowserTestBase : public ExtensionApiTest { | |
| 50 protected: | |
| 51 // Loads the file manager extension, navigating it to |directory_path| for | |
| 52 // testing, and waits for it to finish initializing. This is invoked at the | |
| 53 // start of each test (it crashes if run in SetUp). | |
| 54 void StartFileManager(const std::string& directory_path); | |
| 55 | |
| 56 // Loads our testing extension and sends it a string identifying the current | |
| 57 // test. | |
| 58 void StartTest(const std::string& test_name); | |
| 59 }; | |
| 60 | |
| 61 void FileManagerBrowserTestBase::StartFileManager( | |
| 62 const std::string& directory_path) { | |
| 63 std::string file_manager_url = | |
| 64 (std::string("chrome-extension://") + | |
| 65 kFileManagerExtensionId + | |
| 66 "/main.html#" + | |
| 67 net::EscapeQueryParamValue(directory_path, false /* use_plus */)); | |
| 68 | |
| 69 ui_test_utils::NavigateToURL(browser(), GURL(file_manager_url)); | |
| 70 | |
| 71 // This is sent by the file manager when it's finished initializing. | |
| 72 ExtensionTestMessageListener listener("worker-initialized", false); | |
| 73 ASSERT_TRUE(listener.WaitUntilSatisfied()); | |
| 74 } | |
| 75 | |
| 76 void FileManagerBrowserTestBase::StartTest(const std::string& test_name) { | |
| 77 base::FilePath path = test_data_dir_.AppendASCII("file_manager_browsertest"); | |
| 78 const extensions::Extension* extension = LoadExtensionAsComponent(path); | |
| 79 ASSERT_TRUE(extension); | |
| 80 | |
| 81 ExtensionTestMessageListener listener("which test", true); | |
| 82 ASSERT_TRUE(listener.WaitUntilSatisfied()); | |
| 83 listener.Reply(test_name); | |
| 84 } | |
| 85 | |
| 86 // The boolean parameter, retrieved by GetParam(), is true if testing in the | |
| 87 // guest mode. See SetUpCommandLine() below for details. | |
| 88 class FileManagerBrowserLocalTest : public FileManagerBrowserTestBase, | |
| 89 public ::testing::WithParamInterface<bool> { | |
| 90 public: | |
| 91 virtual void SetUp() OVERRIDE { | |
| 92 extensions::ComponentLoader::EnableBackgroundExtensionsForTesting(); | |
| 93 | |
| 94 ASSERT_TRUE(tmp_dir_.CreateUniqueTempDir()); | |
| 95 downloads_path_ = tmp_dir_.path().Append("Downloads"); | |
| 96 ASSERT_TRUE(file_util::CreateDirectory(downloads_path_)); | |
| 97 | |
| 98 CreateTestFile("hello.txt", 123, "4 Sep 1998 12:34:56"); | |
| 99 CreateTestFile("My Desktop Background.png", 1024, "18 Jan 2038 01:02:03"); | |
| 100 CreateTestFile(kKeyboardTestFileName, kKeyboardTestFileSize, | |
| 101 "4 July 2012 10:35:00"); | |
| 102 CreateTestDirectory("photos", "1 Jan 1980 23:59:59"); | |
| 103 // Files starting with . are filtered out in | |
| 104 // file_manager/js/directory_contents.js, so this should not be shown. | |
| 105 CreateTestDirectory(".warez", "26 Oct 1985 13:39"); | |
| 106 | |
| 107 ExtensionApiTest::SetUp(); | |
| 108 } | |
| 109 | |
| 110 virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE { | |
| 111 bool in_guest_mode = GetParam(); | |
| 112 if (in_guest_mode) { | |
| 113 command_line->AppendSwitch(chromeos::switches::kGuestSession); | |
| 114 command_line->AppendSwitch(switches::kIncognito); | |
| 115 } | |
| 116 ExtensionApiTest::SetUpCommandLine(command_line); | |
| 117 } | |
| 118 | |
| 119 protected: | |
| 120 // Creates a file with the given |name|, |length|, and |modification_time|. | |
| 121 void CreateTestFile(const std::string& name, | |
| 122 int length, | |
| 123 const std::string& modification_time); | |
| 124 | |
| 125 // Creates an empty directory with the given |name| and |modification_time|. | |
| 126 void CreateTestDirectory(const std::string& name, | |
| 127 const std::string& modification_time); | |
| 128 | |
| 129 // Add a mount point to the fake Downloads directory. Should be called | |
| 130 // before StartFileManager(). | |
| 131 void AddMountPointToFakeDownloads(); | |
| 132 | |
| 133 // Path to the fake Downloads directory used in the test. | |
| 134 base::FilePath downloads_path_; | |
| 135 | |
| 136 private: | |
| 137 base::ScopedTempDir tmp_dir_; | |
| 138 }; | |
| 139 | |
| 140 INSTANTIATE_TEST_CASE_P(InGuestMode, | |
| 141 FileManagerBrowserLocalTest, | |
| 142 ::testing::Values(true)); | |
| 143 | |
| 144 INSTANTIATE_TEST_CASE_P(InNonGuestMode, | |
| 145 FileManagerBrowserLocalTest, | |
| 146 ::testing::Values(false)); | |
| 147 | |
| 148 void FileManagerBrowserLocalTest::CreateTestFile( | |
| 149 const std::string& name, | |
| 150 int length, | |
| 151 const std::string& modification_time) { | |
| 152 ASSERT_GE(length, 0); | |
| 153 base::FilePath path = downloads_path_.AppendASCII(name); | |
| 154 int flags = base::PLATFORM_FILE_CREATE | base::PLATFORM_FILE_WRITE; | |
| 155 bool created = false; | |
| 156 base::PlatformFileError error = base::PLATFORM_FILE_ERROR_FAILED; | |
| 157 base::PlatformFile file = base::CreatePlatformFile(path, flags, | |
| 158 &created, &error); | |
| 159 ASSERT_TRUE(created); | |
| 160 ASSERT_FALSE(error) << error; | |
| 161 ASSERT_TRUE(base::TruncatePlatformFile(file, length)); | |
| 162 ASSERT_TRUE(base::ClosePlatformFile(file)); | |
| 163 base::Time time; | |
| 164 ASSERT_TRUE(base::Time::FromString(modification_time.c_str(), &time)); | |
| 165 ASSERT_TRUE(file_util::SetLastModifiedTime(path, time)); | |
| 166 } | |
| 167 | |
| 168 void FileManagerBrowserLocalTest::CreateTestDirectory( | |
| 169 const std::string& name, | |
| 170 const std::string& modification_time) { | |
| 171 base::FilePath path = downloads_path_.AppendASCII(name); | |
| 172 ASSERT_TRUE(file_util::CreateDirectory(path)); | |
| 173 base::Time time; | |
| 174 ASSERT_TRUE(base::Time::FromString(modification_time.c_str(), &time)); | |
| 175 ASSERT_TRUE(file_util::SetLastModifiedTime(path, time)); | |
| 176 } | |
| 177 | |
| 178 void FileManagerBrowserLocalTest::AddMountPointToFakeDownloads() { | |
| 179 // Install our fake Downloads mount point first. | |
| 180 fileapi::ExternalMountPoints* mount_points = | |
| 181 content::BrowserContext::GetMountPoints(profile()); | |
| 182 ASSERT_TRUE(mount_points->RevokeFileSystem("Downloads")); | |
| 183 ASSERT_TRUE(mount_points->RegisterFileSystem( | |
| 184 "Downloads", fileapi::kFileSystemTypeNativeLocal, downloads_path_)); | |
| 185 } | |
| 186 | |
| 187 // Monitors changes to a single file until the supplied condition callback | |
| 188 // returns true. Usage: | |
| 189 // TestFilePathWatcher watcher(path_to_file, MyConditionCallback); | |
| 190 // watcher.StartAndWaitUntilReady(); | |
| 191 // ... trigger filesystem modification ... | |
| 192 // watcher.RunMessageLoopUntilConditionSatisfied(); | |
| 193 class TestFilePathWatcher { | |
| 194 public: | |
| 195 typedef base::Callback<bool(const base::FilePath& file_path)> | |
| 196 ConditionCallback; | |
| 197 | |
| 198 // Stores the supplied |path| and |condition| for later use (no side effects). | |
| 199 TestFilePathWatcher(const base::FilePath& path, | |
| 200 const ConditionCallback& condition); | |
| 201 | |
| 202 // Waits (running a message pump) until the callback returns true or | |
| 203 // FilePathWatcher reports an error. Return true on success. | |
| 204 bool RunMessageLoopUntilConditionSatisfied(); | |
| 205 | |
| 206 private: | |
| 207 // Starts the FilePathWatcher to watch the target file. Also check if the | |
| 208 // condition is already met. | |
| 209 void StartWatching(); | |
| 210 | |
| 211 // FilePathWatcher callback (on the FILE thread). Posts Done() to the UI | |
| 212 // thread when the condition is satisfied or there is an error. | |
| 213 void FilePathWatcherCallback(const base::FilePath& path, bool error); | |
| 214 | |
| 215 const base::FilePath path_; | |
| 216 ConditionCallback condition_; | |
| 217 scoped_ptr<base::FilePathWatcher> watcher_; | |
| 218 base::RunLoop run_loop_; | |
| 219 base::Closure quit_closure_; | |
| 220 bool failed_; | |
| 221 }; | |
| 222 | |
| 223 TestFilePathWatcher::TestFilePathWatcher(const base::FilePath& path, | |
| 224 const ConditionCallback& condition) | |
| 225 : path_(path), | |
| 226 condition_(condition), | |
| 227 quit_closure_(run_loop_.QuitClosure()), | |
| 228 failed_(false) { | |
| 229 } | |
| 230 | |
| 231 void TestFilePathWatcher::StartWatching() { | |
| 232 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE)); | |
| 233 | |
| 234 watcher_.reset(new base::FilePathWatcher); | |
| 235 bool ok = watcher_->Watch( | |
| 236 path_, false /*recursive*/, | |
| 237 base::Bind(&TestFilePathWatcher::FilePathWatcherCallback, | |
| 238 base::Unretained(this))); | |
| 239 DCHECK(ok); | |
| 240 | |
| 241 // If the condition was already met before FilePathWatcher was launched, | |
| 242 // FilePathWatcher won't be able to detect a change, so check the condition | |
| 243 // here. | |
| 244 if (condition_.Run(path_)) { | |
| 245 watcher_.reset(); | |
| 246 content::BrowserThread::PostTask(content::BrowserThread::UI, | |
| 247 FROM_HERE, | |
| 248 quit_closure_); | |
| 249 return; | |
| 250 } | |
| 251 } | |
| 252 | |
| 253 void TestFilePathWatcher::FilePathWatcherCallback(const base::FilePath& path, | |
| 254 bool failed) { | |
| 255 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE)); | |
| 256 DCHECK_EQ(path_.value(), path.value()); | |
| 257 | |
| 258 if (failed || condition_.Run(path)) { | |
| 259 failed_ = failed; | |
| 260 watcher_.reset(); | |
| 261 content::BrowserThread::PostTask(content::BrowserThread::UI, | |
| 262 FROM_HERE, | |
| 263 quit_closure_); | |
| 264 } | |
| 265 } | |
| 266 | |
| 267 bool TestFilePathWatcher::RunMessageLoopUntilConditionSatisfied() { | |
| 268 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | |
| 269 | |
| 270 content::BrowserThread::PostTask( | |
| 271 content::BrowserThread::FILE, | |
| 272 FROM_HERE, | |
| 273 base::Bind(&TestFilePathWatcher::StartWatching, | |
| 274 base::Unretained(this))); | |
| 275 | |
| 276 // Wait until the condition is met. | |
| 277 run_loop_.Run(); | |
| 278 return !failed_; | |
| 279 } | |
| 280 | |
| 281 // Returns true if a file with the given size is present at |path|. | |
| 282 bool FilePresentWithSize(const int64 file_size, | |
| 283 const base::FilePath& path) { | |
| 284 int64 copy_size = 0; | |
| 285 // If the file doesn't exist yet this will fail and we'll keep waiting. | |
| 286 if (!file_util::GetFileSize(path, ©_size)) | |
| 287 return false; | |
| 288 return (copy_size == file_size); | |
| 289 } | |
| 290 | |
| 291 // Returns true if a file is not present at |path|. | |
| 292 bool FileNotPresent(const base::FilePath& path) { | |
| 293 return !file_util::PathExists(path); | |
| 294 }; | |
| 295 | |
| 296 IN_PROC_BROWSER_TEST_P(FileManagerBrowserLocalTest, TestFileDisplay) { | |
| 297 AddMountPointToFakeDownloads(); | |
| 298 StartFileManager("/Downloads"); | |
| 299 | |
| 300 ResultCatcher catcher; | |
| 301 | |
| 302 StartTest("file display"); | |
| 303 | |
| 304 ExtensionTestMessageListener listener("initial check done", true); | |
| 305 ASSERT_TRUE(listener.WaitUntilSatisfied()); | |
| 306 CreateTestFile("newly added file.mp3", 2000, "4 Sep 1998 00:00:00"); | |
| 307 listener.Reply("file added"); | |
| 308 | |
| 309 ASSERT_TRUE(catcher.GetNextResult()) << catcher.message(); | |
| 310 } | |
| 311 | |
| 312 IN_PROC_BROWSER_TEST_P(FileManagerBrowserLocalTest, TestKeyboardCopy) { | |
| 313 AddMountPointToFakeDownloads(); | |
| 314 StartFileManager("/Downloads"); | |
| 315 | |
| 316 base::FilePath copy_path = | |
| 317 downloads_path_.AppendASCII(kKeyboardTestFileCopyName); | |
| 318 ASSERT_FALSE(file_util::PathExists(copy_path)); | |
| 319 | |
| 320 ResultCatcher catcher; | |
| 321 StartTest("keyboard copy"); | |
| 322 | |
| 323 ASSERT_TRUE(catcher.GetNextResult()) << catcher.message(); | |
| 324 | |
| 325 TestFilePathWatcher watcher( | |
| 326 copy_path, | |
| 327 base::Bind(FilePresentWithSize, kKeyboardTestFileSize)); | |
| 328 ASSERT_TRUE(watcher.RunMessageLoopUntilConditionSatisfied()); | |
| 329 | |
| 330 // Check that it was a copy, not a move. | |
| 331 base::FilePath source_path = | |
| 332 downloads_path_.AppendASCII(kKeyboardTestFileName); | |
| 333 ASSERT_TRUE(file_util::PathExists(source_path)); | |
| 334 } | |
| 335 | |
| 336 IN_PROC_BROWSER_TEST_P(FileManagerBrowserLocalTest, TestKeyboardDelete) { | |
| 337 AddMountPointToFakeDownloads(); | |
| 338 StartFileManager("/Downloads"); | |
| 339 | |
| 340 base::FilePath delete_path = | |
| 341 downloads_path_.AppendASCII(kKeyboardTestFileName); | |
| 342 ASSERT_TRUE(file_util::PathExists(delete_path)); | |
| 343 | |
| 344 ResultCatcher catcher; | |
| 345 StartTest("keyboard delete"); | |
| 346 ASSERT_TRUE(catcher.GetNextResult()) << catcher.message(); | |
| 347 | |
| 348 TestFilePathWatcher watcher(delete_path, | |
| 349 base::Bind(FileNotPresent)); | |
| 350 ASSERT_TRUE(watcher.RunMessageLoopUntilConditionSatisfied()); | |
| 351 } | |
| 352 | |
| 353 } // namespace | |
| OLD | NEW |