// Copyright 2014 The Chromium Authors // 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 #include "base/functional/bind.h" #include "base/functional/callback_helpers.h" #include "base/json/json_reader.h" #include "base/test/values_test_util.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 "testing/gtest/include/gtest/gtest.h" #include "third_party/abseil-cpp/absl/types/optional.h" using extensions::ExtensionFunctionDispatcher; namespace extensions { namespace api_test_utils { SendResponseHelper::SendResponseHelper(ExtensionFunction* function) { function->set_has_callback(true); function->set_response_callback( base::BindOnce(&SendResponseHelper::OnResponse, base::Unretained(this))); } SendResponseHelper::~SendResponseHelper() = default; bool SendResponseHelper::GetResponse() { EXPECT_TRUE(has_response()); return *response_; } void SendResponseHelper::OnResponse(ExtensionFunction::ResponseType response, base::Value::List results, const std::string& error, mojom::ExtraResponseDataPtr) { ASSERT_NE(ExtensionFunction::BAD_MESSAGE, response); response_ = std::make_unique(response == ExtensionFunction::SUCCEEDED); run_loop_.Quit(); } void SendResponseHelper::WaitForResponse() { run_loop_.Run(); } bool GetBoolean(const base::Value::Dict& dict, const std::string& key) { absl::optional value = dict.FindBool(key); if (!value.has_value()) { ADD_FAILURE() << key << " does not exist or is not a boolean."; return false; } return *value; } int GetInteger(const base::Value::Dict& dict, const std::string& key) { absl::optional value = dict.FindInt(key); if (!value.has_value()) { ADD_FAILURE() << key << " does not exist or is not an integer."; return 0; } return *value; } std::string GetString(const base::Value::Dict& dict, const std::string& key) { const std::string* value = dict.FindString(key); if (!value) { ADD_FAILURE() << key << " does not exist or is not a string."; return ""; } return *value; } base::Value::List GetList(const base::Value::Dict& dict, const std::string& key) { const base::Value::List* value = dict.FindList(key); if (!value) { ADD_FAILURE() << key << " does not exist or is not a list."; return base::Value::List(); } return value->Clone(); } base::Value::Dict GetDict(const base::Value::Dict& dict, const std::string& key) { const base::Value::Dict* value = dict.FindDict(key); if (!value) { ADD_FAILURE() << key << " does not exist or is not a dict."; return base::Value::Dict(); } return value->Clone(); } base::Value::Dict ToDict(absl::optional val) { if (!val) { ADD_FAILURE() << "val is nullopt"; return base::Value::Dict(); } base::Value result = val->ToValue(); if (!result.is_dict()) { ADD_FAILURE() << "val is not a dictionary"; return base::Value::Dict(); } return std::move(result).TakeDict(); } base::Value::List ToList(absl::optional val) { if (!val) { ADD_FAILURE() << "val is nullopt"; return base::Value::List(); } base::Value result = val->ToValue(); if (!result.is_list()) { ADD_FAILURE() << "val is not a dictionary"; return base::Value::List(); } return std::move(result).TakeList(); } absl::optional RunFunctionWithDelegateAndReturnSingleResult( scoped_refptr function, const std::string& args, std::unique_ptr dispatcher, FunctionMode mode) { base::Value::List parsed_args = base::test::ParseJsonList(args); return RunFunctionWithDelegateAndReturnSingleResult( function, std::move(parsed_args), std::move(dispatcher), mode); } absl::optional RunFunctionWithDelegateAndReturnSingleResult( scoped_refptr function, base::Value::List args, std::unique_ptr dispatcher, FunctionMode mode) { RunFunction(function.get(), std::move(args), std::move(dispatcher), mode); EXPECT_TRUE(function->GetError().empty()) << "Unexpected error: " << function->GetError(); const base::Value::List* results = function->GetResultListForTest(); if (!results || results->empty()) return absl::nullopt; return (*results)[0].Clone(); } absl::optional RunFunctionAndReturnSingleResult( scoped_refptr function, const std::string& args, content::BrowserContext* context) { return RunFunctionAndReturnSingleResult(std::move(function), args, context, FunctionMode::kNone); } absl::optional RunFunctionAndReturnSingleResult( scoped_refptr function, const std::string& args, content::BrowserContext* context, FunctionMode mode) { std::unique_ptr dispatcher( new ExtensionFunctionDispatcher(context)); return RunFunctionWithDelegateAndReturnSingleResult( std::move(function), args, std::move(dispatcher), mode); } std::string RunFunctionAndReturnError(scoped_refptr function, const std::string& args, content::BrowserContext* context) { return RunFunctionAndReturnError(std::move(function), args, context, FunctionMode::kNone); } std::string RunFunctionAndReturnError(scoped_refptr function, const std::string& args, content::BrowserContext* context, FunctionMode mode) { std::unique_ptr dispatcher( new ExtensionFunctionDispatcher(context)); // Without a callback the function will not generate a result. RunFunction(function, args, std::move(dispatcher), mode); // When sending a response, the function will set an empty list value if there // is no specified result. const base::Value::List* results = function->GetResultListForTest(); CHECK(results); EXPECT_TRUE(results->empty()) << "Did not expect a result"; CHECK(function->response_type()); EXPECT_EQ(ExtensionFunction::FAILED, *function->response_type()); return function->GetError(); } bool RunFunction(scoped_refptr function, const std::string& args, content::BrowserContext* context, FunctionMode mode) { std::unique_ptr dispatcher( new ExtensionFunctionDispatcher(context)); return RunFunction(std::move(function), args, std::move(dispatcher), mode); } bool RunFunction( scoped_refptr function, const std::string& args, std::unique_ptr dispatcher, FunctionMode mode) { base::Value::List parsed_args = base::test::ParseJsonList(args); return RunFunction(std::move(function), std::move(parsed_args), std::move(dispatcher), mode); } bool RunFunction(scoped_refptr function, base::Value::List args, std::unique_ptr dispatcher, FunctionMode mode) { SendResponseHelper response_helper(function.get()); function->SetArgs(std::move(args)); CHECK(dispatcher); function->SetDispatcher(dispatcher->AsWeakPtr()); function->set_include_incognito_information(mode == FunctionMode::kIncognito); function->preserve_results_for_testing(); function->RunWithValidation().Execute(); response_helper.WaitForResponse(); EXPECT_TRUE(response_helper.has_response()); return response_helper.GetResponse(); } } // namespace api_test_utils } // namespace extensions