Chromium Code Reviews| Index: chrome/browser/download/download_extension_test.cc |
| diff --git a/chrome/browser/download/download_extension_test.cc b/chrome/browser/download/download_extension_test.cc |
| index fb6ca46b8999e666e353538e30400c8ebeb6eec3..618e56bec5447a13c2544940d4c8b38aaf13824a 100644 |
| --- a/chrome/browser/download/download_extension_test.cc |
| +++ b/chrome/browser/download/download_extension_test.cc |
| @@ -5,28 +5,43 @@ |
| #include <algorithm> |
| #include "base/file_util.h" |
| +#include "base/json/json_reader.h" |
| +#include "base/json/json_writer.h" |
| +#include "base/message_loop.h" |
| #include "base/scoped_temp_dir.h" |
| +#include "base/stl_util.h" |
| #include "base/stringprintf.h" |
| #include "chrome/browser/download/download_extension_api.h" |
| #include "chrome/browser/download/download_file_icon_extractor.h" |
| #include "chrome/browser/download/download_service.h" |
| #include "chrome/browser/download/download_service_factory.h" |
| #include "chrome/browser/download/download_test_observer.h" |
| +#include "chrome/browser/extensions/extension_apitest.h" |
| +#include "chrome/browser/extensions/extension_event_names.h" |
| #include "chrome/browser/extensions/extension_function_test_utils.h" |
| #include "chrome/browser/net/url_request_mock_util.h" |
| #include "chrome/browser/prefs/pref_service.h" |
| #include "chrome/browser/profiles/profile.h" |
| #include "chrome/browser/ui/browser.h" |
| +#include "chrome/browser/ui/tab_contents/tab_contents.h" |
| +#include "chrome/common/chrome_notification_types.h" |
| #include "chrome/common/pref_names.h" |
| #include "chrome/test/base/in_process_browser_test.h" |
| #include "chrome/test/base/ui_test_utils.h" |
| +#include "content/public/browser/browser_context.h" |
| #include "content/public/browser/browser_thread.h" |
| +#include "content/public/browser/download_item.h" |
| #include "content/public/browser/download_manager.h" |
| #include "content/public/browser/download_persistent_store_info.h" |
| +#include "content/public/browser/notification_service.h" |
| +#include "content/public/browser/web_contents.h" |
| +#include "content/public/common/page_transition_types.h" |
| #include "content/test/net/url_request_slow_download_job.h" |
| #include "net/base/data_url.h" |
| #include "net/base/net_util.h" |
| #include "ui/gfx/codec/png_codec.h" |
| +#include "webkit/fileapi/file_system_context.h" |
| +#include "webkit/fileapi/file_system_operation_interface.h" |
| using content::BrowserContext; |
| using content::BrowserThread; |
| @@ -44,7 +59,202 @@ struct DownloadIdComparator { |
| } |
| }; |
| -class DownloadExtensionTest : public InProcessBrowserTest { |
| +class DownloadsEventsListener : public content::NotificationObserver { |
| + public: |
| + // If positive, WaitFor() gives up after this many milliseconds, figuring that |
| + // the event it wants will never happen so that a failing test doesn't take |
| + // too long to fail. If negative, WaitFor() never gives you up, never lets you |
| + // down... The problem is that on very busy machines, the event can take more |
| + // than a few seconds. On very very busy machines, it can take more than many |
| + // seconds. I'd set it at 44 seconds, but there are multiple WaitFor()s per |
| + // TEST_F. I could make it keep track of how long the current TEST_F has been |
| + // running, but if a machine is so busy that a normally instantaneous event is |
| + // taking half a minute, then this soft timeout mechanism probably won't work |
| + // anyway. So this mechanism is really only good for debugging on otherwise |
| + // unloaded machines. Maybe at some point I'll figure out a way to safely set |
| + // this to a sensible number that it won't cause too many timeouts on the |
| + // bots. |
|
Randy Smith (Not in Mondays)
2012/06/19 19:23:23
This comment is targeted at the WaitFor() routine.
benjhayden
2012/06/21 17:50:48
Done.
|
| + static const int kTimeOutMs = -1; |
| + |
| + DownloadsEventsListener() |
| + : waiting_(false) { |
| + registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_DOWNLOADS_EVENT, |
| + content::NotificationService::AllSources()); |
| + } |
| + virtual ~DownloadsEventsListener() { |
| + registrar_.Remove(this, chrome::NOTIFICATION_EXTENSION_DOWNLOADS_EVENT, |
| + content::NotificationService::AllSources()); |
| + STLDeleteElements(&events_); |
| + } |
| + |
| + class Event { |
| + public: |
| + Event(Profile* profile, |
| + const std::string& event_name, |
| + const std::string& json_args, |
| + base::Time caught) |
| + : profile_(profile), |
| + event_name_(event_name), |
| + json_args_(json_args), |
| + args_(base::JSONReader::Read(json_args)), |
| + caught_(caught) { |
| + } |
| + |
| + const base::Time& caught() { return caught_; } |
| + |
| + bool Equals(const Event& other) { |
| + if ((profile_ != other.profile_) || |
| + (event_name_ != other.event_name_)) |
| + return false; |
| + if ((event_name_ == extension_event_names::kOnDownloadCreated || |
| + event_name_ == extension_event_names::kOnDownloadChanged) && |
| + args_.get() && |
| + other.args_.get()) { |
| + base::ListValue* left_list = NULL; |
| + base::DictionaryValue* left_dict = NULL; |
| + base::ListValue* right_list = NULL; |
| + base::DictionaryValue* right_dict = NULL; |
| + if (!args_->GetAsList(&left_list) || |
| + !other.args_->GetAsList(&right_list) || |
| + !left_list->GetDictionary(0, &left_dict) || |
| + !right_list->GetDictionary(0, &right_dict)) |
| + return false; |
| + for (base::DictionaryValue::Iterator iter(*left_dict); |
| + iter.HasNext(); iter.Advance()) { |
| + base::Value* right_value = NULL; |
| + if (right_dict->HasKey(iter.key()) && |
| + right_dict->Get(iter.key(), &right_value) && |
| + !iter.value().Equals(right_value)) { |
| + return false; |
| + } |
| + } |
| + return true; |
| + } else if ((event_name_ == extension_event_names::kOnDownloadErased) && |
| + args_.get() && |
| + other.args_.get()) { |
| + int my_id = -1, other_id = -1; |
| + return (args_->GetAsInteger(&my_id) && |
| + other.args_->GetAsInteger(&other_id) && |
| + my_id == other_id); |
| + } |
| + return json_args_ == other.json_args_; |
| + } |
| + |
| + std::string Debug() { |
| + return base::StringPrintf("Event(%p, %s, %s, %f)", |
| + profile_, |
| + event_name_.c_str(), |
| + json_args_.c_str(), |
| + caught_.ToJsTime()); |
| + } |
| + |
| + private: |
| + Profile* profile_; |
| + std::string event_name_; |
| + std::string json_args_; |
| + scoped_ptr<base::Value> args_; |
| + base::Time caught_; |
| + |
| + DISALLOW_COPY_AND_ASSIGN(Event); |
| + }; |
| + |
| + typedef ExtensionDownloadsEventRouter::DownloadsNotificationSource |
| + DownloadsNotificationSource; |
| + |
| + void Observe(int type, const content::NotificationSource& source, |
| + const content::NotificationDetails& details) { |
| + switch (type) { |
| + case chrome::NOTIFICATION_EXTENSION_DOWNLOADS_EVENT: |
| + { |
| + DownloadsNotificationSource* dns = |
| + content::Source<DownloadsNotificationSource>(source).ptr(); |
| + Event* new_event = new Event( |
| + dns->profile, |
| + dns->event_name, |
| + *content::Details<std::string>(details).ptr(), base::Time::Now()); |
| + events_.push_back(new_event); |
| + if (waiting_ && |
| + waiting_for_.get() && |
| + waiting_for_->Equals(*new_event)) { |
| + waiting_ = false; |
| + MessageLoopForUI::current()->Quit(); |
| + } |
| + break; |
| + } |
| + default: |
| + NOTREACHED(); |
| + } |
| + } |
| + |
| + bool WaitFor(int timeout_ms, |
| + Profile* profile, |
| + const std::string& event_name, |
| + const std::string& json_args) { |
| + waiting_for_.reset(new Event(profile, event_name, json_args, base::Time())); |
| + for (std::deque<Event*>::const_iterator iter = events_.begin(); |
| + iter != events_.end(); ++iter) { |
| + if ((*iter)->Equals(*waiting_for_.get())) |
| + return true; |
| + } |
| + waiting_ = true; |
| + if (timeout_ms > 0) { |
| + MessageLoop::current()->PostDelayedTask( |
| + FROM_HERE, base::Bind( |
| + &DownloadsEventsListener::TimedOut, base::Unretained(this)), |
| + timeout_ms); |
| + } |
| + ui_test_utils::RunMessageLoop(); |
| + bool success = !waiting_; |
| + if (waiting_) { |
| + // Print the events that were caught since the last WaitFor() call to help |
| + // find the erroneous event. |
| + // TODO(benjhayden) Fuzzy-match and highlight the erroneous event. |
| + for (std::deque<Event*>::const_iterator iter = events_.begin(); |
| + iter != events_.end(); ++iter) { |
| + if ((*iter)->caught() > last_wait_) { |
| + LOG(INFO) << "Caught " << (*iter)->Debug(); |
| + } |
| + } |
| + if (waiting_for_.get()) { |
| + LOG(INFO) << "Timed out waiting for " << waiting_for_->Debug(); |
| + } |
| + waiting_ = false; |
| + } |
| + waiting_for_.reset(); |
| + last_wait_ = base::Time::Now(); |
| + return success; |
| + } |
| + |
| + bool WaitFor(Profile* profile, |
| + const std::string& event_name, |
| + const std::string& json_args) { |
| + return WaitFor(kTimeOutMs, profile, event_name, json_args); |
| + } |
| + |
| + private: |
| + void TimedOut() { |
| + if (!waiting_for_.get()) |
| + return; |
| + MessageLoopForUI::current()->Quit(); |
| + } |
| + |
| + bool waiting_; |
| + base::Time last_wait_; |
| + scoped_ptr<Event> waiting_for_; |
| + content::NotificationRegistrar registrar_; |
| + std::deque<Event*> events_; |
| + |
| + DISALLOW_COPY_AND_ASSIGN(DownloadsEventsListener); |
| +}; |
| + |
| +class DownloadExtensionTest : public ExtensionApiTest { |
| + public: |
| + DownloadExtensionTest() |
| + : extension_(NULL), |
| + incognito_browser_(NULL), |
| + current_browser_(NULL) { |
| + } |
| + |
| protected: |
| // Used with CreateHistoryDownloads |
| struct HistoryDownloadInfo { |
| @@ -61,7 +271,15 @@ class DownloadExtensionTest : public InProcessBrowserTest { |
| content::DownloadDangerType danger_type; |
| }; |
| - virtual Browser* current_browser() { return browser(); } |
| + void LoadExtension(const char* name) { |
| + // Store the created Extension object so that we can attach it to |
| + // ExtensionFunctions. Also load the extension in incognito profiles for |
| + // testing incognito. |
| + extension_ = LoadExtensionIncognito(test_data_dir_.AppendASCII(name)); |
| + CHECK(extension_); |
| + } |
| + |
| + Browser* current_browser() { return current_browser_; } |
| // InProcessBrowserTest |
| virtual void SetUpOnMainThread() OVERRIDE { |
| @@ -69,14 +287,91 @@ class DownloadExtensionTest : public InProcessBrowserTest { |
| BrowserThread::IO, FROM_HERE, |
| base::Bind(&chrome_browser_net::SetUrlRequestMocksEnabled, true)); |
| InProcessBrowserTest::SetUpOnMainThread(); |
| + GoOnTheRecord(); |
| CreateAndSetDownloadsDirectory(); |
| current_browser()->profile()->GetPrefs()->SetBoolean( |
| prefs::kPromptForDownload, false); |
| - GetDownloadManager()->RemoveAllDownloads(); |
| + GetOnRecordManager()->RemoveAllDownloads(); |
| + events_listener_.reset(new DownloadsEventsListener()); |
| } |
| - virtual DownloadManager* GetDownloadManager() { |
| - return BrowserContext::GetDownloadManager(current_browser()->profile()); |
| + void GoOnTheRecord() { current_browser_ = browser(); } |
| + |
| + void GoOffTheRecord() { |
| + if (!incognito_browser_) { |
| + incognito_browser_ = CreateIncognitoBrowser(); |
| + GetOffRecordManager()->RemoveAllDownloads(); |
| + } |
| + current_browser_ = incognito_browser_; |
| + } |
| + |
| + bool WaitFor(int timeout_ms, const std::string& event_name, |
| + const std::string& json_args) { |
| + return events_listener_->WaitFor( |
| + timeout_ms, current_browser()->profile(), event_name, json_args); |
| + } |
| + |
| + bool WaitFor(const std::string& event_name, const std::string& json_args) { |
| + return events_listener_->WaitFor( |
| + current_browser()->profile(), event_name, json_args); |
| + } |
| + |
| + bool WaitForInterruption(DownloadItem* item, int expected_error, |
| + const std::string& on_created_event) { |
| + if (!WaitFor(extension_event_names::kOnDownloadCreated, on_created_event)) |
| + return false; |
| + // The item may or may not be interrupted before the onCreated event fires. |
| + if (item->IsInterrupted()) { |
| + scoped_ptr<base::Value> args(base::JSONReader::Read(on_created_event)); |
| + base::ListValue* args_list = NULL; |
| + base::DictionaryValue* args_dict = NULL; |
| + if (!args->GetAsList(&args_list) || |
| + !args_list->GetDictionary(0, &args_dict)) |
| + return false; |
| + args_dict->SetString("state", "interrupted"); |
| + args_dict->SetInteger("error", expected_error); |
| + std::string created_error; |
| + base::JSONWriter::Write(args_list, &created_error); |
| + // This is not waiting for a different event, it's refining the |
| + // expectations on the onCreated event that was just caught. Specifically, |
| + // if a DownloadItem is already interrupted by the time the onCreated |
| + // event fires, then the onCreated event should already describe the |
| + // error. |
| + return WaitFor(extension_event_names::kOnDownloadCreated, created_error); |
| + } else { |
| + return WaitFor(extension_event_names::kOnDownloadChanged, |
| + base::StringPrintf("[{\"id\": %d," |
| + " \"error\": {\"new\": %d}," |
| + " \"state\": {" |
| + " \"old\": \"in_progress\"," |
| + " \"new\": \"interrupted\"}}]", |
| + item->GetId(), |
| + expected_error)); |
| + } |
| + } |
| + |
| + std::string GetURL(const char* path) { |
| + return test_server()->GetURL(path).spec(); |
|
Randy Smith (Not in Mondays)
2012/06/19 19:23:23
Personal preference would be to expand this inline
benjhayden
2012/06/21 17:50:48
Done.
|
| + } |
| + |
| + std::string GetExtensionURL() { |
| + return extension_->url().spec(); |
| + } |
| + |
| + std::string GetFilename(const char* path) { |
| + return downloads_directory_.path().AppendASCII(path).AsUTF8Unsafe(); |
| + } |
| + |
| + DownloadManager* GetOnRecordManager() { |
| + return BrowserContext::GetDownloadManager(browser()->profile()); |
| + } |
| + DownloadManager* GetOffRecordManager() { |
| + return BrowserContext::GetDownloadManager( |
| + browser()->profile()->GetOffTheRecordProfile()); |
| + } |
| + DownloadManager* GetCurrentManager() { |
| + return (current_browser_ == incognito_browser_) ? |
| + GetOffRecordManager() : GetOnRecordManager(); |
| } |
| // Creates a set of history downloads based on the provided |history_info| |
| @@ -101,8 +396,8 @@ class DownloadExtensionTest : public InProcessBrowserTest { |
| false); // opened |
| entries.push_back(entry); |
| } |
| - GetDownloadManager()->OnPersistentStoreQueryComplete(&entries); |
| - GetDownloadManager()->GetAllDownloads(FilePath(), items); |
| + GetOnRecordManager()->OnPersistentStoreQueryComplete(&entries); |
| + GetOnRecordManager()->GetAllDownloads(FilePath(), items); |
| EXPECT_EQ(count, items->size()); |
| if (count != items->size()) |
| return false; |
| @@ -136,7 +431,7 @@ class DownloadExtensionTest : public InProcessBrowserTest { |
| // We don't expect a select file dialog. |
| ASSERT_FALSE(observer->select_file_dialog_seen()); |
| } |
| - GetDownloadManager()->GetAllDownloads(FilePath(), items); |
| + GetCurrentManager()->GetAllDownloads(FilePath(), items); |
| ASSERT_EQ(count, items->size()); |
| } |
| @@ -144,7 +439,7 @@ class DownloadExtensionTest : public InProcessBrowserTest { |
| scoped_ptr<DownloadTestObserver> observer( |
| CreateInProgressDownloadObserver(1)); |
| GURL slow_download_url(URLRequestSlowDownloadJob::kUnknownSizeUrl); |
| - DownloadManager* manager = GetDownloadManager(); |
| + DownloadManager* manager = GetCurrentManager(); |
| EXPECT_EQ(0, manager->InProgressCount()); |
| if (manager->InProgressCount() != 0) |
| @@ -188,15 +483,22 @@ class DownloadExtensionTest : public InProcessBrowserTest { |
| DownloadTestObserver* CreateDownloadObserver(size_t download_count) { |
| return new DownloadTestObserverTerminal( |
| - GetDownloadManager(), download_count, true, |
| + GetCurrentManager(), download_count, true, |
| DownloadTestObserver::ON_DANGEROUS_DOWNLOAD_FAIL); |
| } |
| DownloadTestObserver* CreateInProgressDownloadObserver( |
| size_t download_count) { |
| - return new DownloadTestObserverInProgress(GetDownloadManager(), |
| - download_count, |
| - true); |
| + return new DownloadTestObserverInProgress( |
| + GetCurrentManager(), download_count, true); |
| + } |
| + |
| + bool RunFunction(UIThreadExtensionFunction* function, |
| + const std::string& args) { |
| + scoped_refptr<UIThreadExtensionFunction> delete_function(function); |
| + SetUpExtensionFunction(function); |
| + return extension_function_test_utils::RunFunction( |
| + function, args, browser(), GetFlags()); |
| } |
| extension_function_test_utils::RunFunctionFlags GetFlags() { |
| @@ -207,25 +509,18 @@ class DownloadExtensionTest : public InProcessBrowserTest { |
| // extension_function_test_utils::RunFunction*() only uses browser for its |
| // profile(), so pass it the on-record browser so that it always uses the |
| - // on-record profile. |
| - |
| - bool RunFunction(UIThreadExtensionFunction* function, |
| - const std::string& args) { |
| - // extension_function_test_utils::RunFunction() does not take |
| - // ownership of |function|. |
| - scoped_refptr<ExtensionFunction> function_owner(function); |
| - return extension_function_test_utils::RunFunction( |
| - function, args, browser(), GetFlags()); |
| - } |
| + // on-record profile to match real-life behavior. |
| base::Value* RunFunctionAndReturnResult(UIThreadExtensionFunction* function, |
| const std::string& args) { |
| + SetUpExtensionFunction(function); |
| return extension_function_test_utils::RunFunctionAndReturnResult( |
| function, args, browser(), GetFlags()); |
| } |
| std::string RunFunctionAndReturnError(UIThreadExtensionFunction* function, |
| const std::string& args) { |
| + SetUpExtensionFunction(function); |
| return extension_function_test_utils::RunFunctionAndReturnError( |
| function, args, browser(), GetFlags()); |
| } |
| @@ -233,6 +528,7 @@ class DownloadExtensionTest : public InProcessBrowserTest { |
| bool RunFunctionAndReturnString(UIThreadExtensionFunction* function, |
| const std::string& args, |
| std::string* result_string) { |
| + SetUpExtensionFunction(function); |
| scoped_ptr<base::Value> result(RunFunctionAndReturnResult(function, args)); |
| EXPECT_TRUE(result.get()); |
| return result.get() && result->GetAsString(result_string); |
| @@ -270,6 +566,17 @@ class DownloadExtensionTest : public InProcessBrowserTest { |
| } |
| private: |
| + void SetUpExtensionFunction(UIThreadExtensionFunction* function) { |
| + if (extension_) { |
| + // Recreate the tab each time for insulation. |
| + TabContents* tab = current_browser()->AddSelectedTabWithURL( |
| + extension_->GetResourceURL("empty.html"), |
| + content::PAGE_TRANSITION_LINK); |
| + function->set_extension(extension_); |
| + function->SetRenderViewHost(tab->web_contents()->GetRenderViewHost()); |
| + } |
| + } |
| + |
| void CreateAndSetDownloadsDirectory() { |
| ASSERT_TRUE(downloads_directory_.CreateUniqueTempDir()); |
| current_browser()->profile()->GetPrefs()->SetFilePath( |
| @@ -278,26 +585,12 @@ class DownloadExtensionTest : public InProcessBrowserTest { |
| } |
| ScopedTempDir downloads_directory_; |
| -}; |
| - |
| -class DownloadExtensionTestIncognito : public DownloadExtensionTest { |
| - public: |
| - virtual Browser* current_browser() OVERRIDE { return current_browser_; } |
| - |
| - virtual void SetUpOnMainThread() OVERRIDE { |
| - GoOnTheRecord(); |
| - DownloadExtensionTest::SetUpOnMainThread(); |
| - incognito_browser_ = CreateIncognitoBrowser(); |
| - GoOffTheRecord(); |
| - GetDownloadManager()->RemoveAllDownloads(); |
| - } |
| - |
| - void GoOnTheRecord() { current_browser_ = browser(); } |
| - void GoOffTheRecord() { current_browser_ = incognito_browser_; } |
| - |
| - private: |
| + const extensions::Extension* extension_; |
| Browser* incognito_browser_; |
| Browser* current_browser_; |
| + scoped_ptr<DownloadsEventsListener> events_listener_; |
| + |
| + DISALLOW_COPY_AND_ASSIGN(DownloadExtensionTest); |
| }; |
| class MockIconExtractorImpl : public DownloadFileIconExtractor { |
| @@ -376,7 +669,54 @@ class ScopedItemVectorCanceller { |
| DISALLOW_COPY_AND_ASSIGN(ScopedItemVectorCanceller); |
| }; |
| -} // namespace |
| + |
| +// Tests may create HTML5 FileSystem Files for testing. |
| +// fileapi::FileSystemContext::OpenFileSystem() calls OpenFileSystemCallback() |
| +// below, which runs CreateFileOnIOThread, which calls |
| +// fileapi::FileSystemOperationInterface::CreateFile(), which calls |
| +// FileCreated() below, which runs FileCreatedOnUIThread. |
| + |
| +static const char* kHTML5FileCreated = "html5_file_created"; |
| + |
| +void FileCreatedOnUIThread(Profile* profile, std::string filename) { |
| + DownloadsEventsListener::DownloadsNotificationSource notification_source; |
| + notification_source.event_name = kHTML5FileCreated; |
| + notification_source.profile = profile; |
| + content::NotificationService::current()->Notify( |
| + chrome::NOTIFICATION_EXTENSION_DOWNLOADS_EVENT, |
| + content::Source<DownloadsEventsListener::DownloadsNotificationSource>( |
| + ¬ification_source), |
| + content::Details<std::string>(&filename)); |
| +} |
| + |
| +void FileCreated(Profile* profile, const std::string& filename, |
| + base::PlatformFileError result) { |
| + CHECK(BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, base::Bind( |
| + &FileCreatedOnUIThread, profile, filename))); |
| +} |
| + |
| +void CreateFileOnIOThread(fileapi::FileSystemOperationInterface* operation, |
| + Profile* profile, |
| + const std::string& filename, |
| + const std::string& url) { |
| + operation->CreateFile(GURL(url + filename), |
| + true/*exclusive*/, |
| + base::Bind(&FileCreated, profile, filename)); |
| +} |
| + |
| +void OpenFileSystemCallback(fileapi::FileSystemContext* fs, |
| + const std::string& filename, |
| + Profile* profile, |
| + base::PlatformFileError result, |
| + const std::string& fs_name, |
| + const GURL& root) { |
| + fileapi::FileSystemOperationInterface* operation = |
| + fs->CreateFileSystemOperation(root); |
| + CHECK(BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, base::Bind( |
| + &CreateFileOnIOThread, operation, profile, filename, root.spec()))); |
| +} |
| + |
| +} // namespace |
| IN_PROC_BROWSER_TEST_F(DownloadExtensionTest, DownloadsApi_PauseResumeCancel) { |
| DownloadItem* download_item = CreateSlowTestDownload(); |
| @@ -430,8 +770,17 @@ IN_PROC_BROWSER_TEST_F(DownloadExtensionTest, DownloadsApi_PauseResumeCancel) { |
| EXPECT_STREQ(download_extension_errors::kInvalidOperationError, |
| error.c_str()); |
| - // Calling pause()/resume()/cancel() with invalid download Ids is |
| - // tested in the API test (DownloadsApiTest). |
| + // Calling paused on a non-existent download yields kInvalidOperationError. |
| + error = RunFunctionAndReturnError( |
| + new DownloadsPauseFunction(), "[-42]"); |
| + EXPECT_STREQ(download_extension_errors::kInvalidOperationError, |
| + error.c_str()); |
| + |
| + // Calling resume on a non-existent download yields kInvalidOperationError |
| + error = RunFunctionAndReturnError( |
| + new DownloadsResumeFunction(), "[-42]"); |
| + EXPECT_STREQ(download_extension_errors::kInvalidOperationError, |
| + error.c_str()); |
| } |
| // Test downloads.getFileIcon() on in-progress, finished, cancelled and deleted |
| @@ -526,9 +875,6 @@ IN_PROC_BROWSER_TEST_F(DownloadExtensionTest, DownloadsApi_FileIcon_Active) { |
| error = RunFunctionAndReturnError(new DownloadsGetFileIconFunction(), args); |
| EXPECT_STREQ(download_extension_errors::kInvalidOperationError, |
| error.c_str()); |
| - |
| - // Asking for icons of other (invalid) sizes is tested in the API test |
| - // (DownloadsApiTest). |
| } |
| // Test that we can acquire file icons for history downloads regardless of |
| @@ -583,6 +929,7 @@ IN_PROC_BROWSER_TEST_F(DownloadExtensionTest, DownloadsApi_FileIcon_History) { |
| // The temporary files should be cleaned up when the ScopedTempDir is removed. |
| } |
| +// Test passing the empty query to search(). |
| IN_PROC_BROWSER_TEST_F(DownloadExtensionTest, DownloadsApi_SearchEmptyQuery) { |
| ScopedCancellingItem item(CreateSlowTestDownload()); |
| ASSERT_TRUE(item.get()); |
| @@ -595,6 +942,7 @@ IN_PROC_BROWSER_TEST_F(DownloadExtensionTest, DownloadsApi_SearchEmptyQuery) { |
| ASSERT_EQ(1UL, result_list->GetSize()); |
| } |
| +// Test the |filenameRegex| parameter for search(). |
| IN_PROC_BROWSER_TEST_F(DownloadExtensionTest, |
| DownloadsApi_SearchFilenameRegex) { |
| const HistoryDownloadInfo kHistoryInfo[] = { |
| @@ -622,6 +970,7 @@ IN_PROC_BROWSER_TEST_F(DownloadExtensionTest, |
| ASSERT_EQ(0, item_id); |
| } |
| +// Test the |id| parameter for search(). |
| IN_PROC_BROWSER_TEST_F(DownloadExtensionTest, DownloadsApi_SearchId) { |
| DownloadManager::DownloadVector items; |
| CreateSlowTestDownloads(2, &items); |
| @@ -640,6 +989,7 @@ IN_PROC_BROWSER_TEST_F(DownloadExtensionTest, DownloadsApi_SearchId) { |
| ASSERT_EQ(0, item_id); |
| } |
| +// Test specifying both the |id| and |filename| parameters for search(). |
| IN_PROC_BROWSER_TEST_F(DownloadExtensionTest, |
| DownloadsApi_SearchIdAndFilename) { |
| DownloadManager::DownloadVector items; |
| @@ -647,13 +997,15 @@ IN_PROC_BROWSER_TEST_F(DownloadExtensionTest, |
| ScopedItemVectorCanceller delete_items(&items); |
| scoped_ptr<base::Value> result(RunFunctionAndReturnResult( |
| - new DownloadsSearchFunction(), "[{\"id\": 0,\"filename\": \"foobar\"}]")); |
| + new DownloadsSearchFunction(), |
| + "[{\"id\": 0, \"filename\": \"foobar\"}]")); |
| ASSERT_TRUE(result.get()); |
| base::ListValue* result_list = NULL; |
| ASSERT_TRUE(result->GetAsList(&result_list)); |
| ASSERT_EQ(0UL, result_list->GetSize()); |
| } |
| +// Test a single |orderBy| parameter for search(). |
| IN_PROC_BROWSER_TEST_F(DownloadExtensionTest, DownloadsApi_SearchOrderBy) { |
| const HistoryDownloadInfo kHistoryInfo[] = { |
| { FILE_PATH_LITERAL("zzz"), |
| @@ -684,6 +1036,7 @@ IN_PROC_BROWSER_TEST_F(DownloadExtensionTest, DownloadsApi_SearchOrderBy) { |
| ASSERT_LT(item0_name, item1_name); |
| } |
| +// Test specifying an empty |orderBy| parameter for search(). |
| IN_PROC_BROWSER_TEST_F(DownloadExtensionTest, DownloadsApi_SearchOrderByEmpty) { |
| const HistoryDownloadInfo kHistoryInfo[] = { |
| { FILE_PATH_LITERAL("zzz"), |
| @@ -714,6 +1067,7 @@ IN_PROC_BROWSER_TEST_F(DownloadExtensionTest, DownloadsApi_SearchOrderByEmpty) { |
| ASSERT_GT(item0_name, item1_name); |
| } |
| +// Test the |danger| option for search(). |
| IN_PROC_BROWSER_TEST_F(DownloadExtensionTest, DownloadsApi_SearchDanger) { |
| const HistoryDownloadInfo kHistoryInfo[] = { |
| { FILE_PATH_LITERAL("zzz"), |
| @@ -735,6 +1089,7 @@ IN_PROC_BROWSER_TEST_F(DownloadExtensionTest, DownloadsApi_SearchDanger) { |
| ASSERT_EQ(1UL, result_list->GetSize()); |
| } |
| +// Test the |state| option for search(). |
| IN_PROC_BROWSER_TEST_F(DownloadExtensionTest, DownloadsApi_SearchState) { |
| DownloadManager::DownloadVector items; |
| CreateSlowTestDownloads(2, &items); |
| @@ -750,6 +1105,7 @@ IN_PROC_BROWSER_TEST_F(DownloadExtensionTest, DownloadsApi_SearchState) { |
| ASSERT_EQ(1UL, result_list->GetSize()); |
| } |
| +// Test the |limit| option for search(). |
| IN_PROC_BROWSER_TEST_F(DownloadExtensionTest, DownloadsApi_SearchLimit) { |
| DownloadManager::DownloadVector items; |
| CreateSlowTestDownloads(2, &items); |
| @@ -763,6 +1119,7 @@ IN_PROC_BROWSER_TEST_F(DownloadExtensionTest, DownloadsApi_SearchLimit) { |
| ASSERT_EQ(1UL, result_list->GetSize()); |
| } |
| +// Test invalid search parameters. |
| IN_PROC_BROWSER_TEST_F(DownloadExtensionTest, DownloadsApi_SearchInvalid) { |
| std::string error = RunFunctionAndReturnError( |
| new DownloadsSearchFunction(), "[{\"filenameRegex\": \"(\"}]"); |
| @@ -786,6 +1143,7 @@ IN_PROC_BROWSER_TEST_F(DownloadExtensionTest, DownloadsApi_SearchInvalid) { |
| error.c_str()); |
| } |
| +// Test searching using multiple conditions through multiple downloads. |
| IN_PROC_BROWSER_TEST_F(DownloadExtensionTest, DownloadsApi_SearchPlural) { |
| const HistoryDownloadInfo kHistoryInfo[] = { |
| { FILE_PATH_LITERAL("aaa"), |
| @@ -819,8 +1177,12 @@ IN_PROC_BROWSER_TEST_F(DownloadExtensionTest, DownloadsApi_SearchPlural) { |
| ASSERT_EQ(items[2]->GetFullPath().value(), item_name); |
| } |
| -IN_PROC_BROWSER_TEST_F(DownloadExtensionTestIncognito, |
| - DownloadsApi_SearchIncognito) { |
| +// Test that incognito downloads are only visible in incognito contexts, and |
| +// test that on-record downloads are visible in both incognito and on-record |
| +// contexts, for DownloadsSearchFunction, DownloadsPauseFunction, |
| +// DownloadsResumeFunction, and DownloadsCancelFunction. |
| +IN_PROC_BROWSER_TEST_F(DownloadExtensionTest, |
| + DownloadExtensionTest_SearchIncognito) { |
| scoped_ptr<base::Value> result_value; |
| base::ListValue* result_list = NULL; |
| base::DictionaryValue* result_dict = NULL; |
| @@ -832,22 +1194,25 @@ IN_PROC_BROWSER_TEST_F(DownloadExtensionTestIncognito, |
| std::string result_string; |
| // Set up one on-record item and one off-record item. |
| + // Set up the off-record item first because otherwise there are mysteriously 3 |
| + // items total instead of 2. |
| + // TODO(benjhayden): Figure out where the third item comes from. |
| + GoOffTheRecord(); |
| + DownloadItem* off_item = CreateSlowTestDownload(); |
| + ASSERT_TRUE(off_item); |
| + ASSERT_TRUE(off_item->IsOtr()); |
| + off_item_arg = DownloadItemIdAsArgList(off_item); |
| GoOnTheRecord(); |
| DownloadItem* on_item = CreateSlowTestDownload(); |
| ASSERT_TRUE(on_item); |
| ASSERT_FALSE(on_item->IsOtr()); |
| on_item_arg = DownloadItemIdAsArgList(on_item); |
| - |
| - GoOffTheRecord(); |
| - DownloadItem* off_item = CreateSlowTestDownload(); |
| - ASSERT_TRUE(off_item); |
| - ASSERT_TRUE(off_item->IsOtr()); |
| ASSERT_TRUE(on_item->GetFullPath() != off_item->GetFullPath()); |
| - off_item_arg = DownloadItemIdAsArgList(off_item); |
| // Extensions running in the incognito window should have access to both |
| // items because the Test extension is in spanning mode. |
| + GoOffTheRecord(); |
| result_value.reset(RunFunctionAndReturnResult( |
| new DownloadsSearchFunction(), "[{}]")); |
| ASSERT_TRUE(result_value.get()); |
| @@ -893,9 +1258,6 @@ IN_PROC_BROWSER_TEST_F(DownloadExtensionTestIncognito, |
| EXPECT_STREQ(download_extension_errors::kInvalidOperationError, |
| error.c_str()); |
| - // TODO(benjhayden): Test incognito_split_mode() extension. |
| - // TODO(benjhayden): Test download(), onCreated, onChanged, onErased. |
| - |
| GoOffTheRecord(); |
| // Do the FileIcon test for both the on- and off-items while off the record. |
| @@ -953,3 +1315,714 @@ IN_PROC_BROWSER_TEST_F(DownloadExtensionTestIncognito, |
| EXPECT_STREQ(download_extension_errors::kInvalidOperationError, |
| error.c_str()); |
| } |
| + |
| +// Test that DownloadsEventsListener times out quickly enough for an event |
| +// that isn't going to happen. |
| +IN_PROC_BROWSER_TEST_F(DownloadExtensionTest, |
| + DownloadExtensionTest_DownloadsEventsListener) { |
| + ASSERT_FALSE(WaitFor(100, extension_event_names::kOnDownloadErased, "-1")); |
| +} |
| + |
| +// Test that we can start a download and that the correct sequence of events is |
| +// fired for it. |
| +IN_PROC_BROWSER_TEST_F(DownloadExtensionTest, |
| + DownloadExtensionTest_Download_Basic) { |
| + LoadExtension("downloads_split"); |
| + CHECK(StartTestServer()); |
| + std::string download_url = GetURL("slow?0"); |
| + GoOnTheRecord(); |
| + |
| + scoped_ptr<base::Value> result(RunFunctionAndReturnResult( |
| + new DownloadsDownloadFunction(), base::StringPrintf( |
| + "[{\"url\": \"%s\"}]", download_url.c_str()))); |
| + ASSERT_TRUE(result.get()); |
| + int result_id = -1; |
| + ASSERT_TRUE(result->GetAsInteger(&result_id)); |
| + DownloadItem* item = GetCurrentManager()->GetActiveDownloadItem(result_id); |
| + if (!item) item = GetCurrentManager()->GetDownloadItem(result_id); |
| + ASSERT_TRUE(item); |
| + ScopedCancellingItem canceller(item); |
| + ASSERT_EQ(download_url, item->GetURL().spec()); |
| + |
| + ASSERT_TRUE(WaitFor(extension_event_names::kOnDownloadCreated, |
| + base::StringPrintf("[{\"danger\": \"safe\"," |
| + " \"filename\": \"%s\"," |
| + " \"incognito\": false," |
| + " \"mime\": \"text/plain\"," |
| + " \"paused\": false," |
| + " \"url\": \"%s\"}]", |
| + GetFilename("slow.txt.crdownload").c_str(), |
| + download_url.c_str()))); |
| + ASSERT_TRUE(WaitFor(extension_event_names::kOnDownloadChanged, |
| + base::StringPrintf("[{\"id\": %d," |
| + " \"filename\": {" |
| + " \"old\": \"%s\"," |
| + " \"new\": \"%s\"}," |
| + " \"state\": {" |
| + " \"old\": \"in_progress\"," |
| + " \"new\": \"complete\"}}]", |
| + result_id, |
| + GetFilename("slow.txt.crdownload").c_str(), |
| + GetFilename("slow.txt").c_str()))); |
| +} |
| + |
| +// Test that we can start a download from an incognito context, and that the |
| +// download knows that it's incognito. |
| +IN_PROC_BROWSER_TEST_F(DownloadExtensionTest, |
| + DownloadExtensionTest_Download_Incognito) { |
| + LoadExtension("downloads_split"); |
| + CHECK(StartTestServer()); |
| + GoOffTheRecord(); |
| + std::string download_url = GetURL("slow?0"); |
| + |
| + scoped_ptr<base::Value> result(RunFunctionAndReturnResult( |
| + new DownloadsDownloadFunction(), base::StringPrintf( |
| + "[{\"url\": \"%s\"}]", download_url.c_str()))); |
| + ASSERT_TRUE(result.get()); |
| + int result_id = -1; |
| + ASSERT_TRUE(result->GetAsInteger(&result_id)); |
| + DownloadItem* item = GetCurrentManager()->GetActiveDownloadItem(result_id); |
| + if (!item) item = GetCurrentManager()->GetDownloadItem(result_id); |
| + ASSERT_TRUE(item); |
| + ScopedCancellingItem canceller(item); |
| + ASSERT_EQ(download_url, item->GetURL().spec()); |
| + |
| + ASSERT_TRUE(WaitFor(extension_event_names::kOnDownloadCreated, |
| + base::StringPrintf("[{\"danger\": \"safe\"," |
| + " \"filename\": \"%s\"," |
| + " \"incognito\": true," |
| + " \"mime\": \"text/plain\"," |
| + " \"paused\": false," |
| + " \"url\": \"%s\"}]", |
| + GetFilename("slow.txt.crdownload").c_str(), |
| + download_url.c_str()))); |
| + ASSERT_TRUE(WaitFor(extension_event_names::kOnDownloadChanged, |
| + base::StringPrintf("[{\"id\":%d," |
| + " \"filename\": {" |
| + " \"old\": \"%s\"," |
| + " \"new\": \"%s\"}," |
| + " \"state\": {" |
| + " \"new\": \"complete\"," |
| + " \"old\": \"in_progress\"}}]", |
| + result_id, |
| + GetFilename("slow.txt.crdownload").c_str(), |
| + GetFilename("slow.txt").c_str()))); |
| +} |
| + |
| +// Test that we disallow certain headers case-insensitively. |
| +IN_PROC_BROWSER_TEST_F(DownloadExtensionTest, |
| + DownloadExtensionTest_Download_UnsafeHeaders) { |
| + LoadExtension("downloads_split"); |
| + CHECK(StartTestServer()); |
| + GoOnTheRecord(); |
| + |
| + static const char* kUnsafeHeaders[] = { |
| + "Accept-chArsEt", |
| + "accept-eNcoding", |
| + "coNNection", |
| + "coNteNt-leNgth", |
| + "cooKIE", |
| + "cOOkie2", |
| + "coNteNt-traNsfer-eNcodiNg", |
| + "dAtE", |
| + "ExpEcT", |
| + "hOsT", |
| + "kEEp-aLivE", |
| + "rEfErEr", |
| + "tE", |
| + "trAilER", |
| + "trANsfer-eNcodiNg", |
| + "upGRAde", |
| + "usER-agENt", |
| + "viA", |
| + "pRoxY-", |
| + "sEc-", |
| + "pRoxY-probably-not-evil", |
| + "sEc-probably-not-evil", |
| + "oRiGiN", |
| + "Access-Control-Request-Headers", |
| + "Access-Control-Request-Method", |
| + }; |
| + |
| + for (size_t index = 0; index < arraysize(kUnsafeHeaders); ++index) { |
| + std::string download_url = GetURL("slow?0"); |
| + EXPECT_STREQ(download_extension_errors::kGenericError, |
| + RunFunctionAndReturnError(new DownloadsDownloadFunction(), |
| + base::StringPrintf( |
| + "[{\"url\": \"%s\"," |
| + " \"filename\": \"unsafe-header-%lu.txt\"," |
| + " \"headers\": [{" |
| + " \"name\": \"%s\"," |
| + " \"value\": \"unsafe\"}]}]", |
| + download_url.c_str(), index, kUnsafeHeaders[index])).c_str()); |
| + } |
| +} |
| + |
| +// Test that subdirectories (slashes) are disallowed in filenames. |
| +// TODO(benjhayden) Update this when subdirectories are supported. |
| +IN_PROC_BROWSER_TEST_F(DownloadExtensionTest, |
| + DownloadExtensionTest_Download_Subdirectory) { |
| + LoadExtension("downloads_split"); |
| + CHECK(StartTestServer()); |
| + std::string download_url = GetURL("slow?0"); |
| + GoOnTheRecord(); |
| + |
| + EXPECT_STREQ(download_extension_errors::kGenericError, |
| + RunFunctionAndReturnError(new DownloadsDownloadFunction(), |
| + base::StringPrintf( |
| + "[{\"url\": \"%s\"," |
| + " \"filename\": \"sub/dir/ect/ory.txt\"}]", |
| + download_url.c_str())).c_str()); |
| +} |
| + |
| +// Test that invalid filenames are disallowed. |
| +IN_PROC_BROWSER_TEST_F(DownloadExtensionTest, |
| + DownloadExtensionTest_Download_InvalidFilename) { |
| + LoadExtension("downloads_split"); |
| + CHECK(StartTestServer()); |
| + std::string download_url = GetURL("slow?0"); |
| + GoOnTheRecord(); |
| + |
| + EXPECT_STREQ(download_extension_errors::kGenericError, |
| + RunFunctionAndReturnError(new DownloadsDownloadFunction(), |
| + base::StringPrintf( |
| + "[{\"url\": \"%s\"," |
| + " \"filename\": \"../../../../../etc/passwd\"}]", |
| + download_url.c_str())).c_str()); |
| +} |
| + |
| +// Test that downloading invalid URLs immediately returns kInvalidURLError. |
| +IN_PROC_BROWSER_TEST_F(DownloadExtensionTest, |
| + DownloadExtensionTest_Download_InvalidURLs) { |
| + LoadExtension("downloads_split"); |
| + GoOnTheRecord(); |
| + |
| + static const char* kInvalidURLs[] = { |
| + "foo bar", |
| + "../hello", |
| + "/hello", |
| + "google.com/", |
| + "http://", |
| + "#frag", |
| + "foo/bar.html#frag", |
| + "javascript:document.write(\\\"hello\\\");", |
| + "javascript:return false;", |
| + "ftp://example.com/example.txt", |
| + }; |
| + |
| + for (size_t index = 0; index < arraysize(kInvalidURLs); ++index) { |
| + EXPECT_STREQ(download_extension_errors::kInvalidURLError, |
| + RunFunctionAndReturnError(new DownloadsDownloadFunction(), |
| + base::StringPrintf( |
| + "[{\"url\": \"%s\"}]", kInvalidURLs[index])).c_str()); |
| + } |
| +} |
| + |
| +// TODO(benjhayden): Set up a test ftp server, add ftp://localhost* to |
| +// permissions, test downloading from ftp. |
| + |
| +// Valid URLs plus fragments are still valid URLs. |
| +IN_PROC_BROWSER_TEST_F(DownloadExtensionTest, |
| + DownloadExtensionTest_Download_URLFragment) { |
| + LoadExtension("downloads_split"); |
| + CHECK(StartTestServer()); |
| + std::string download_url = GetURL("slow?0#fragment"); |
| + GoOnTheRecord(); |
| + |
| + scoped_ptr<base::Value> result(RunFunctionAndReturnResult( |
| + new DownloadsDownloadFunction(), base::StringPrintf( |
| + "[{\"url\": \"%s\"}]", download_url.c_str()))); |
| + ASSERT_TRUE(result.get()); |
| + int result_id = -1; |
| + ASSERT_TRUE(result->GetAsInteger(&result_id)); |
| + DownloadItem* item = GetCurrentManager()->GetActiveDownloadItem(result_id); |
| + if (!item) item = GetCurrentManager()->GetDownloadItem(result_id); |
| + ASSERT_TRUE(item); |
| + ScopedCancellingItem canceller(item); |
| + ASSERT_EQ(download_url, item->GetURL().spec()); |
| + |
| + ASSERT_TRUE(WaitFor(extension_event_names::kOnDownloadCreated, |
| + base::StringPrintf("[{\"danger\": \"safe\"," |
| + " \"filename\": \"%s\"," |
| + " \"incognito\": false," |
| + " \"mime\": \"text/plain\"," |
| + " \"paused\": false," |
| + " \"url\": \"%s\"}]", |
| + GetFilename("slow.txt.crdownload").c_str(), |
| + download_url.c_str()))); |
| + ASSERT_TRUE(WaitFor(extension_event_names::kOnDownloadChanged, |
| + base::StringPrintf("[{\"id\": %d," |
| + " \"filename\": {" |
| + " \"old\": \"%s\"," |
| + " \"new\": \"%s\"}," |
| + " \"state\": {" |
| + " \"old\": \"in_progress\"," |
| + " \"new\": \"complete\"}}]", |
| + result_id, |
| + GetFilename("slow.txt.crdownload").c_str(), |
| + GetFilename("slow.txt").c_str()))); |
| +} |
| + |
| +// Valid data URLs are valid URLs. |
| +IN_PROC_BROWSER_TEST_F(DownloadExtensionTest, |
| + DownloadExtensionTest_Download_DataURL) { |
| + LoadExtension("downloads_split"); |
| + CHECK(StartTestServer()); |
| + std::string download_url = "data:text/plain,hello"; |
| + GoOnTheRecord(); |
| + |
| + scoped_ptr<base::Value> result(RunFunctionAndReturnResult( |
| + new DownloadsDownloadFunction(), base::StringPrintf( |
| + "[{\"url\": \"%s\"," |
| + " \"filename\": \"data.txt\"}]", download_url.c_str()))); |
| + ASSERT_TRUE(result.get()); |
| + int result_id = -1; |
| + ASSERT_TRUE(result->GetAsInteger(&result_id)); |
| + DownloadItem* item = GetCurrentManager()->GetActiveDownloadItem(result_id); |
| + if (!item) item = GetCurrentManager()->GetDownloadItem(result_id); |
| + ASSERT_TRUE(item); |
| + ScopedCancellingItem canceller(item); |
| + ASSERT_EQ(download_url, item->GetURL().spec()); |
| + |
| + ASSERT_TRUE(WaitFor(extension_event_names::kOnDownloadCreated, |
| + base::StringPrintf("[{\"danger\": \"safe\"," |
| + " \"filename\": \"%s\"," |
| + " \"incognito\": false," |
| + " \"mime\": \"text/plain\"," |
| + " \"paused\": false," |
| + " \"url\": \"%s\"}]", |
| + GetFilename("data.txt.crdownload").c_str(), |
| + download_url.c_str()))); |
| + ASSERT_TRUE(WaitFor(extension_event_names::kOnDownloadChanged, |
| + base::StringPrintf("[{\"id\": %d," |
| + " \"filename\": {" |
| + " \"old\": \"%s\"," |
| + " \"new\": \"%s\"}," |
| + " \"state\": {" |
| + " \"old\": \"in_progress\"," |
| + " \"new\": \"complete\"}}]", |
| + result_id, |
| + GetFilename("data.txt.crdownload").c_str(), |
| + GetFilename("data.txt").c_str()))); |
| +} |
| + |
| +// Valid file URLs are valid URLs. |
| +IN_PROC_BROWSER_TEST_F(DownloadExtensionTest, |
| + DownloadExtensionTest_Download_File) { |
| + LoadExtension("downloads_split"); |
| + CHECK(StartTestServer()); |
| + std::string download_url = "file:///"; |
| + GoOnTheRecord(); |
| + |
| + scoped_ptr<base::Value> result(RunFunctionAndReturnResult( |
| + new DownloadsDownloadFunction(), base::StringPrintf( |
| + "[{\"url\": \"%s\"," |
| + " \"filename\": \"file.txt\"}]", download_url.c_str()))); |
| + ASSERT_TRUE(result.get()); |
| + int result_id = -1; |
| + ASSERT_TRUE(result->GetAsInteger(&result_id)); |
| + DownloadItem* item = GetCurrentManager()->GetActiveDownloadItem(result_id); |
| + if (!item) item = GetCurrentManager()->GetDownloadItem(result_id); |
| + ASSERT_TRUE(item); |
| + ScopedCancellingItem canceller(item); |
| +#if defined(OS_WIN) |
| + download_url += "/"; |
| +#endif |
| + ASSERT_EQ(download_url, item->GetURL().spec()); |
| + |
| + ASSERT_TRUE(WaitFor(extension_event_names::kOnDownloadCreated, |
| + base::StringPrintf("[{\"danger\": \"safe\"," |
| + " \"filename\": \"%s\"," |
| + " \"incognito\": false," |
| + " \"mime\": \"text/html\"," |
| + " \"paused\": false," |
| + " \"url\": \"%s\"}]", |
| + GetFilename("file.txt.crdownload").c_str(), |
| + download_url.c_str()))); |
| + ASSERT_TRUE(WaitFor(extension_event_names::kOnDownloadChanged, |
| + base::StringPrintf("[{\"id\": %d," |
| + " \"filename\": {" |
| + " \"old\": \"%s\"," |
| + " \"new\": \"%s\"}," |
| + " \"state\": {" |
| + " \"old\": \"in_progress\"," |
| + " \"new\": \"complete\"}}]", |
| + result_id, |
| + GetFilename("file.txt.crdownload").c_str(), |
| + GetFilename("file.txt").c_str()))); |
| +} |
| + |
| +// Test that auth-basic-succeed would fail if the resource requires the |
| +// Authorization header and chrome fails to propagate it back to the server. |
| +// This tests both that testserver.py does not succeed when it should fail as |
| +// well as how the downloads extension API exposes the failure to extensions. |
| +IN_PROC_BROWSER_TEST_F(DownloadExtensionTest, |
| + DownloadExtensionTest_Download_AuthBasic_Fail) { |
| + LoadExtension("downloads_split"); |
| + CHECK(StartTestServer()); |
| + std::string download_url = GetURL("auth-basic"); |
| + GoOnTheRecord(); |
| + |
| + scoped_ptr<base::Value> result(RunFunctionAndReturnResult( |
| + new DownloadsDownloadFunction(), base::StringPrintf( |
| + "[{\"url\": \"%s\"," |
| + " \"filename\": \"auth-basic-fail.txt\"}]", |
| + download_url.c_str()))); |
| + ASSERT_TRUE(result.get()); |
| + int result_id = -1; |
| + ASSERT_TRUE(result->GetAsInteger(&result_id)); |
| + DownloadItem* item = GetCurrentManager()->GetActiveDownloadItem(result_id); |
| + if (!item) item = GetCurrentManager()->GetDownloadItem(result_id); |
| + ASSERT_TRUE(item); |
| + ScopedCancellingItem canceller(item); |
| + ASSERT_EQ(download_url, item->GetURL().spec()); |
| + |
| + ASSERT_TRUE(WaitForInterruption(item, 30, base::StringPrintf( |
| + "[{\"danger\": \"safe\"," |
| + " \"incognito\": false," |
| + " \"mime\": \"text/html\"," |
| + " \"paused\": false," |
| + " \"url\": \"%s\"}]", |
| + download_url.c_str()))); |
| +} |
| + |
| +// Test that DownloadsDownloadFunction propagates |headers| to the URLRequest. |
| +IN_PROC_BROWSER_TEST_F(DownloadExtensionTest, |
| + DownloadExtensionTest_Download_Headers) { |
| + LoadExtension("downloads_split"); |
| + CHECK(StartTestServer()); |
| + std::string download_url = GetURL("files/downloads/a_zip_file.zip?" |
| + "expected_headers=Foo:bar&expected_headers=Qx:yo"); |
| + GoOnTheRecord(); |
| + |
| + scoped_ptr<base::Value> result(RunFunctionAndReturnResult( |
| + new DownloadsDownloadFunction(), base::StringPrintf( |
| + "[{\"url\": \"%s\"," |
| + " \"filename\": \"headers-succeed.txt\"," |
| + " \"headers\": [" |
| + " {\"name\": \"Foo\", \"value\": \"bar\"}," |
| + " {\"name\": \"Qx\", \"value\":\"yo\"}]}]", |
| + download_url.c_str()))); |
| + ASSERT_TRUE(result.get()); |
| + int result_id = -1; |
| + ASSERT_TRUE(result->GetAsInteger(&result_id)); |
| + DownloadItem* item = GetCurrentManager()->GetActiveDownloadItem(result_id); |
| + if (!item) item = GetCurrentManager()->GetDownloadItem(result_id); |
| + ASSERT_TRUE(item); |
| + ScopedCancellingItem canceller(item); |
| + ASSERT_EQ(download_url, item->GetURL().spec()); |
| + |
| + ASSERT_TRUE(WaitFor(extension_event_names::kOnDownloadCreated, |
| + base::StringPrintf("[{\"danger\": \"safe\"," |
| + " \"incognito\": false," |
| + " \"mime\": \"application/octet-stream\"," |
| + " \"paused\": false," |
| + " \"url\": \"%s\"}]", |
| + download_url.c_str()))); |
| + std::string incomplete_filename = GetFilename( |
| + "headers-succeed.txt.crdownload"); |
| + ASSERT_TRUE(WaitFor(extension_event_names::kOnDownloadChanged, |
| + base::StringPrintf("[{\"id\": %d," |
| + " \"filename\": {" |
| + " \"old\": \"%s\"," |
| + " \"new\": \"%s\"}," |
| + " \"state\": {" |
| + " \"old\": \"in_progress\"," |
| + " \"new\": \"complete\"}}]", |
| + result_id, |
| + incomplete_filename.c_str(), |
| + GetFilename("headers-succeed.txt").c_str()))); |
| +} |
| + |
| +// Test that headers-succeed would fail if the resource requires the headers and |
| +// chrome fails to propagate them back to the server. This tests both that |
| +// testserver.py does not succeed when it should fail as well as how the |
| +// downloads extension api exposes the failure to extensions. |
| +IN_PROC_BROWSER_TEST_F(DownloadExtensionTest, |
| + DownloadExtensionTest_Download_Headers_Fail) { |
| + LoadExtension("downloads_split"); |
| + CHECK(StartTestServer()); |
| + std::string download_url = GetURL("files/downloads/a_zip_file.zip?" |
| + "expected_headers=Foo:bar&expected_headers=Qx:yo"); |
| + GoOnTheRecord(); |
| + |
| + scoped_ptr<base::Value> result(RunFunctionAndReturnResult( |
| + new DownloadsDownloadFunction(), base::StringPrintf( |
| + "[{\"url\": \"%s\"," |
| + " \"filename\": \"headers-fail.txt\"}]", |
| + download_url.c_str()))); |
| + ASSERT_TRUE(result.get()); |
| + int result_id = -1; |
| + ASSERT_TRUE(result->GetAsInteger(&result_id)); |
| + DownloadItem* item = GetCurrentManager()->GetActiveDownloadItem(result_id); |
| + if (!item) item = GetCurrentManager()->GetDownloadItem(result_id); |
| + ASSERT_TRUE(item); |
| + ScopedCancellingItem canceller(item); |
| + ASSERT_EQ(download_url, item->GetURL().spec()); |
| + |
| + ASSERT_TRUE(WaitForInterruption(item, 33, base::StringPrintf( |
| + "[{\"danger\": \"safe\"," |
| + " \"incognito\": false," |
| + " \"bytesReceived\": 0," |
| + " \"mime\": \"\"," |
| + " \"paused\": false," |
| + " \"url\": \"%s\"}]", |
| + download_url.c_str()))); |
| +} |
| + |
| +// Test that DownloadsDownloadFunction propagates the Authorization header |
| +// correctly. |
| +IN_PROC_BROWSER_TEST_F(DownloadExtensionTest, |
| + DownloadExtensionTest_Download_AuthBasic) { |
| + LoadExtension("downloads_split"); |
| + CHECK(StartTestServer()); |
| + std::string download_url = GetURL("auth-basic"); |
| + // This is just base64 of 'username:secret'. |
| + static const char* kAuthorization = "dXNlcm5hbWU6c2VjcmV0"; |
| + GoOnTheRecord(); |
| + |
| + scoped_ptr<base::Value> result(RunFunctionAndReturnResult( |
| + new DownloadsDownloadFunction(), base::StringPrintf( |
| + "[{\"url\": \"%s\"," |
| + " \"filename\": \"auth-basic-succeed.txt\"," |
| + " \"headers\": [{" |
| + " \"name\": \"Authorization\"," |
| + " \"value\": \"Basic %s\"}]}]", |
| + download_url.c_str(), kAuthorization))); |
| + ASSERT_TRUE(result.get()); |
| + int result_id = -1; |
| + ASSERT_TRUE(result->GetAsInteger(&result_id)); |
| + DownloadItem* item = GetCurrentManager()->GetActiveDownloadItem(result_id); |
| + if (!item) item = GetCurrentManager()->GetDownloadItem(result_id); |
| + ASSERT_TRUE(item); |
| + ScopedCancellingItem canceller(item); |
| + ASSERT_EQ(download_url, item->GetURL().spec()); |
| + |
| + ASSERT_TRUE(WaitFor(extension_event_names::kOnDownloadCreated, |
| + base::StringPrintf("[{\"danger\": \"safe\"," |
| + " \"incognito\": false," |
| + " \"mime\": \"text/html\"," |
| + " \"paused\": false," |
| + " \"url\": \"%s\"}]", download_url.c_str()))); |
| + ASSERT_TRUE(WaitFor(extension_event_names::kOnDownloadChanged, |
| + base::StringPrintf("[{\"id\": %d," |
| + " \"state\": {" |
| + " \"old\": \"in_progress\"," |
| + " \"new\": \"complete\"}}]", result_id))); |
| +} |
| + |
| +// Test that DownloadsDownloadFunction propagates the |method| and |body| |
| +// parameters to the URLRequest. |
| +IN_PROC_BROWSER_TEST_F(DownloadExtensionTest, |
| + DownloadExtensionTest_Download_Post) { |
| + LoadExtension("downloads_split"); |
| + CHECK(StartTestServer()); |
| + std::string download_url = GetURL("files/post/downloads/a_zip_file.zip?" |
| + "expected_body=BODY"); |
| + GoOnTheRecord(); |
| + |
| + scoped_ptr<base::Value> result(RunFunctionAndReturnResult( |
| + new DownloadsDownloadFunction(), base::StringPrintf( |
| + "[{\"url\": \"%s\"," |
| + " \"filename\": \"post-succeed.txt\"," |
| + " \"method\": \"POST\"," |
| + " \"body\": \"BODY\"}]", |
| + download_url.c_str()))); |
| + ASSERT_TRUE(result.get()); |
| + int result_id = -1; |
| + ASSERT_TRUE(result->GetAsInteger(&result_id)); |
| + DownloadItem* item = GetCurrentManager()->GetActiveDownloadItem(result_id); |
| + if (!item) item = GetCurrentManager()->GetDownloadItem(result_id); |
| + ASSERT_TRUE(item); |
| + ScopedCancellingItem canceller(item); |
| + ASSERT_EQ(download_url, item->GetURL().spec()); |
| + |
| + ASSERT_TRUE(WaitFor(extension_event_names::kOnDownloadCreated, |
| + base::StringPrintf("[{\"danger\": \"safe\"," |
| + " \"incognito\": false," |
| + " \"mime\": \"application/octet-stream\"," |
| + " \"paused\": false," |
| + " \"bytesReceived\": 164," |
| + " \"url\": \"%s\"}]", download_url.c_str()))); |
| + ASSERT_TRUE(WaitFor(extension_event_names::kOnDownloadChanged, |
| + base::StringPrintf("[{\"id\": %d," |
| + " \"state\": {" |
| + " \"old\": \"in_progress\"," |
| + " \"new\": \"complete\"}}]", result_id))); |
| +} |
| + |
| +// Test that downloadPostSuccess would fail if the resource requires the POST |
| +// method, and chrome fails to propagate the |method| parameter back to the |
| +// server. This tests both that testserver.py does not succeed when it should |
| +// fail, and this tests how the downloads extension api exposes the failure to |
| +// extensions. |
| +IN_PROC_BROWSER_TEST_F(DownloadExtensionTest, |
| + DownloadExtensionTest_Download_Post_Get) { |
| + LoadExtension("downloads_split"); |
| + CHECK(StartTestServer()); |
| + std::string download_url = GetURL("files/post/downloads/a_zip_file.zip?" |
| + "expected_body=BODY"); |
| + GoOnTheRecord(); |
| + |
| + scoped_ptr<base::Value> result(RunFunctionAndReturnResult( |
| + new DownloadsDownloadFunction(), base::StringPrintf( |
| + "[{\"url\": \"%s\"," |
| + " \"body\": \"BODY\"," |
| + " \"filename\": \"post-get.txt\"}]", |
| + download_url.c_str()))); |
| + ASSERT_TRUE(result.get()); |
| + int result_id = -1; |
| + ASSERT_TRUE(result->GetAsInteger(&result_id)); |
| + DownloadItem* item = GetCurrentManager()->GetActiveDownloadItem(result_id); |
| + if (!item) item = GetCurrentManager()->GetDownloadItem(result_id); |
| + ASSERT_TRUE(item); |
| + ScopedCancellingItem canceller(item); |
| + ASSERT_EQ(download_url, item->GetURL().spec()); |
| + |
| + ASSERT_TRUE(WaitForInterruption(item, 33, base::StringPrintf( |
| + "[{\"danger\": \"safe\"," |
| + " \"incognito\": false," |
| + " \"mime\": \"\"," |
| + " \"paused\": false," |
| + " \"id\": %d," |
| + " \"url\": \"%s\"}]", |
| + result_id, |
| + download_url.c_str()))); |
| +} |
| + |
| +// Test that downloadPostSuccess would fail if the resource requires the POST |
| +// method, and chrome fails to propagate the |body| parameter back to the |
| +// server. This tests both that testserver.py does not succeed when it should |
| +// fail, and this tests how the downloads extension api exposes the failure to |
| +// extensions. |
| +IN_PROC_BROWSER_TEST_F(DownloadExtensionTest, |
| + DownloadExtensionTest_Download_Post_NoBody) { |
| + LoadExtension("downloads_split"); |
| + CHECK(StartTestServer()); |
| + std::string download_url = GetURL("files/post/downloads/a_zip_file.zip?" |
| + "expected_body=BODY"); |
| + GoOnTheRecord(); |
| + |
| + scoped_ptr<base::Value> result(RunFunctionAndReturnResult( |
| + new DownloadsDownloadFunction(), base::StringPrintf( |
| + "[{\"url\": \"%s\"," |
| + " \"method\": \"POST\"," |
| + " \"filename\": \"post-nobody.txt\"}]", |
| + download_url.c_str()))); |
| + ASSERT_TRUE(result.get()); |
| + int result_id = -1; |
| + ASSERT_TRUE(result->GetAsInteger(&result_id)); |
| + DownloadItem* item = GetCurrentManager()->GetActiveDownloadItem(result_id); |
| + if (!item) item = GetCurrentManager()->GetDownloadItem(result_id); |
| + ASSERT_TRUE(item); |
| + ScopedCancellingItem canceller(item); |
| + ASSERT_EQ(download_url, item->GetURL().spec()); |
| + |
| + ASSERT_TRUE(WaitForInterruption(item, 33, base::StringPrintf( |
| + "[{\"danger\": \"safe\"," |
| + " \"incognito\": false," |
| + " \"mime\": \"\"," |
| + " \"paused\": false," |
| + " \"id\": %d," |
| + " \"url\": \"%s\"}]", |
| + result_id, |
| + download_url.c_str()))); |
| +} |
| + |
| +// Test that cancel()ing an in-progress download causes its state to transition |
| +// to interrupted, and test that that state transition is detectable by an |
| +// onChanged event listener. TODO(benjhayden): Test other sources of |
| +// interruptions such as server death. |
| +IN_PROC_BROWSER_TEST_F(DownloadExtensionTest, |
| + DownloadExtensionTest_Download_Cancel) { |
| + LoadExtension("downloads_split"); |
| + CHECK(StartTestServer()); |
| + std::string download_url = GetURL("download-known-size"); |
| + GoOnTheRecord(); |
| + |
| + scoped_ptr<base::Value> result(RunFunctionAndReturnResult( |
| + new DownloadsDownloadFunction(), base::StringPrintf( |
| + "[{\"url\": \"%s\"}]", download_url.c_str()))); |
| + ASSERT_TRUE(result.get()); |
| + int result_id = -1; |
| + ASSERT_TRUE(result->GetAsInteger(&result_id)); |
| + DownloadItem* item = GetCurrentManager()->GetActiveDownloadItem(result_id); |
| + if (!item) item = GetCurrentManager()->GetDownloadItem(result_id); |
| + ASSERT_TRUE(item); |
| + ScopedCancellingItem canceller(item); |
| + ASSERT_EQ(download_url, item->GetURL().spec()); |
| + |
| + ASSERT_TRUE(WaitFor(extension_event_names::kOnDownloadCreated, |
| + base::StringPrintf("[{\"danger\": \"safe\"," |
| + " \"incognito\": false," |
| + " \"mime\": \"application/octet-stream\"," |
| + " \"paused\": false," |
| + " \"id\": %d," |
| + " \"url\": \"%s\"}]", |
| + result_id, |
| + download_url.c_str()))); |
| + item->Cancel(true); |
| + ASSERT_TRUE(WaitFor(extension_event_names::kOnDownloadChanged, |
| + base::StringPrintf("[{\"id\": %d," |
| + " \"error\": {\"new\": 40}," |
| + " \"state\": {" |
| + " \"old\": \"in_progress\"," |
| + " \"new\": \"interrupted\"}}]", |
| + result_id))); |
| +} |
| + |
| +// Test downloading filesystem: URLs. |
| +// NOTE: chrome disallows creating HTML5 FileSystem Files in incognito. |
| +IN_PROC_BROWSER_TEST_F(DownloadExtensionTest, |
| + DownloadExtensionTest_Download_FileSystemURL) { |
| + static const char* kHTML5FileCreated = "html5_file_created"; |
| + LoadExtension("downloads_split"); |
| + fileapi::FileSystemContext* fs = BrowserContext::GetFileSystemContext( |
| + browser()->profile()); |
| + fileapi::FileSystemContext::OpenFileSystemCallback fs_opened = base::Bind( |
| + &OpenFileSystemCallback, |
| + base::Unretained(fs), |
| + "on_record.txt", |
| + browser()->profile()); |
| + fs->OpenFileSystem(GURL(GetExtensionURL()), |
| + fileapi::kFileSystemTypeTemporary, |
| + true/*create*/, |
| + fs_opened); |
| + ASSERT_TRUE(WaitFor(kHTML5FileCreated, "on_record.txt")); |
| + |
| + std::string download_url = "filesystem:" + GetExtensionURL() + |
| + "temporary/on_record.txt"; |
| + scoped_ptr<base::Value> result(RunFunctionAndReturnResult( |
| + new DownloadsDownloadFunction(), base::StringPrintf( |
| + "[{\"url\": \"%s\"}]", download_url.c_str()))); |
| + ASSERT_TRUE(result.get()); |
| + int result_id = -1; |
| + ASSERT_TRUE(result->GetAsInteger(&result_id)); |
| + |
| + DownloadItem* item = GetCurrentManager()->GetActiveDownloadItem(result_id); |
| + if (!item) item = GetCurrentManager()->GetDownloadItem(result_id); |
| + ASSERT_TRUE(item); |
| + ScopedCancellingItem canceller(item); |
| + ASSERT_EQ(download_url, item->GetURL().spec()); |
| + |
| + ASSERT_TRUE(WaitFor(extension_event_names::kOnDownloadCreated, |
| + base::StringPrintf("[{\"danger\": \"safe\"," |
| + " \"filename\": \"%s\"," |
| + " \"incognito\": false," |
| + " \"mime\": \"text/plain\"," |
| + " \"paused\": false," |
| + " \"url\": \"%s\"}]", |
| + GetFilename("on_record.txt.crdownload").c_str(), |
| + download_url.c_str()))); |
| + ASSERT_TRUE(WaitFor(extension_event_names::kOnDownloadChanged, |
| + base::StringPrintf("[{\"id\": %d," |
| + " \"filename\": {" |
| + " \"old\": \"%s\"," |
| + " \"new\": \"%s\"}," |
| + " \"state\": {" |
| + " \"old\": \"in_progress\"," |
| + " \"new\": \"complete\"}}]", |
| + result_id, |
| + GetFilename("on_record.txt.crdownload").c_str(), |
| + GetFilename("on_record.txt").c_str()))); |
| +} |