// Copyright 2014 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "extensions/browser/api_test_utils.h" #include #include "base/json/json_reader.h" #include "base/memory/scoped_ptr.h" #include "base/values.h" #include "components/crx_file/id_util.h" #include "content/public/browser/browser_context.h" #include "content/public/test/test_utils.h" #include "extensions/browser/extension_function.h" #include "extensions/browser/extension_function_dispatcher.h" #include "extensions/common/extension_builder.h" #include "testing/gtest/include/gtest/gtest.h" using extensions::ExtensionFunctionDispatcher; namespace { scoped_ptr ParseJSON(const std::string& data) { return base::JSONReader::Read(data); } scoped_ptr ParseList(const std::string& data) { return base::ListValue::From(ParseJSON(data)); } // This helps us be able to wait until an UIThreadExtensionFunction calls // SendResponse. class SendResponseDelegate : public UIThreadExtensionFunction::DelegateForTests { public: SendResponseDelegate() : should_post_quit_(false) {} virtual ~SendResponseDelegate() {} void set_should_post_quit(bool should_quit) { should_post_quit_ = should_quit; } bool HasResponse() { return response_.get() != NULL; } bool GetResponse() { EXPECT_TRUE(HasResponse()); return *response_.get(); } void OnSendResponse(UIThreadExtensionFunction* function, bool success, bool bad_message) override { ASSERT_FALSE(bad_message); ASSERT_FALSE(HasResponse()); response_.reset(new bool); *response_ = success; if (should_post_quit_) { base::MessageLoopForUI::current()->QuitWhenIdle(); } } private: scoped_ptr response_; bool should_post_quit_; }; } // namespace namespace extensions { namespace api_test_utils { scoped_ptr ParseDictionary(const std::string& data) { return base::DictionaryValue::From(ParseJSON(data)); } bool GetBoolean(const base::DictionaryValue* val, const std::string& key) { bool result = false; if (!val->GetBoolean(key, &result)) ADD_FAILURE() << key << " does not exist or is not a boolean."; return result; } int GetInteger(const base::DictionaryValue* val, const std::string& key) { int result = 0; if (!val->GetInteger(key, &result)) ADD_FAILURE() << key << " does not exist or is not an integer."; return result; } std::string GetString(const base::DictionaryValue* val, const std::string& key) { std::string result; if (!val->GetString(key, &result)) ADD_FAILURE() << key << " does not exist or is not a string."; return result; } scoped_refptr CreateExtension( Manifest::Location location, base::DictionaryValue* test_extension_value, const std::string& id_input) { std::string error; const base::FilePath test_extension_path; std::string id; if (!id_input.empty()) id = crx_file::id_util::GenerateId(id_input); scoped_refptr extension( Extension::Create(test_extension_path, location, *test_extension_value, Extension::NO_FLAGS, id, &error)); EXPECT_TRUE(error.empty()) << "Could not parse test extension " << error; return extension; } scoped_refptr CreateExtension( base::DictionaryValue* test_extension_value) { return CreateExtension(Manifest::INTERNAL, test_extension_value, std::string()); } scoped_refptr CreateEmptyExtensionWithLocation( Manifest::Location location) { scoped_ptr test_extension_value = ParseDictionary("{\"name\": \"Test\", \"version\": \"1.0\"}"); return CreateExtension(location, test_extension_value.get(), std::string()); } base::Value* RunFunctionWithDelegateAndReturnSingleResult( UIThreadExtensionFunction* function, const std::string& args, content::BrowserContext* context, scoped_ptr dispatcher) { return RunFunctionWithDelegateAndReturnSingleResult( function, args, context, std::move(dispatcher), NONE); } base::Value* RunFunctionWithDelegateAndReturnSingleResult( UIThreadExtensionFunction* function, const std::string& args, content::BrowserContext* context, scoped_ptr dispatcher, RunFunctionFlags flags) { scoped_refptr function_owner(function); // Without a callback the function will not generate a result. function->set_has_callback(true); RunFunction(function, args, context, std::move(dispatcher), flags); EXPECT_TRUE(function->GetError().empty()) << "Unexpected error: " << function->GetError(); const base::Value* single_result = NULL; if (function->GetResultList() != NULL && function->GetResultList()->Get(0, &single_result)) { return single_result->DeepCopy(); } return NULL; } base::Value* RunFunctionAndReturnSingleResult( UIThreadExtensionFunction* function, const std::string& args, content::BrowserContext* context) { return RunFunctionAndReturnSingleResult(function, args, context, NONE); } base::Value* RunFunctionAndReturnSingleResult( UIThreadExtensionFunction* function, const std::string& args, content::BrowserContext* context, RunFunctionFlags flags) { scoped_ptr dispatcher( new ExtensionFunctionDispatcher(context)); return RunFunctionWithDelegateAndReturnSingleResult( function, args, context, std::move(dispatcher), flags); } std::string RunFunctionAndReturnError(UIThreadExtensionFunction* function, const std::string& args, content::BrowserContext* context) { return RunFunctionAndReturnError(function, args, context, NONE); } std::string RunFunctionAndReturnError(UIThreadExtensionFunction* function, const std::string& args, content::BrowserContext* context, RunFunctionFlags flags) { scoped_ptr dispatcher( new ExtensionFunctionDispatcher(context)); scoped_refptr function_owner(function); // Without a callback the function will not generate a result. function->set_has_callback(true); RunFunction(function, args, context, std::move(dispatcher), flags); EXPECT_FALSE(function->GetResultList()) << "Did not expect a result"; return function->GetError(); } bool RunFunction(UIThreadExtensionFunction* function, const std::string& args, content::BrowserContext* context) { scoped_ptr dispatcher( new ExtensionFunctionDispatcher(context)); return RunFunction(function, args, context, std::move(dispatcher), NONE); } bool RunFunction(UIThreadExtensionFunction* function, const std::string& args, content::BrowserContext* context, scoped_ptr dispatcher, RunFunctionFlags flags) { scoped_ptr parsed_args = ParseList(args); EXPECT_TRUE(parsed_args.get()) << "Could not parse extension function arguments: " << args; return RunFunction(function, std::move(parsed_args), context, std::move(dispatcher), flags); } bool RunFunction(UIThreadExtensionFunction* function, scoped_ptr args, content::BrowserContext* context, scoped_ptr dispatcher, RunFunctionFlags flags) { SendResponseDelegate response_delegate; function->set_test_delegate(&response_delegate); function->SetArgs(args.get()); CHECK(dispatcher); function->set_dispatcher(dispatcher->AsWeakPtr()); function->set_browser_context(context); function->set_include_incognito(flags & INCLUDE_INCOGNITO); function->Run()->Execute(); // If the RunAsync of |function| didn't already call SendResponse, run the // message loop until they do. if (!response_delegate.HasResponse()) { response_delegate.set_should_post_quit(true); content::RunMessageLoop(); } EXPECT_TRUE(response_delegate.HasResponse()); return response_delegate.GetResponse(); } } // namespace api_test_utils } // namespace extensions