Chromium Code Reviews| Index: extensions/renderer/api_bindings_system_unittest.cc |
| diff --git a/extensions/renderer/api_bindings_system_unittest.cc b/extensions/renderer/api_bindings_system_unittest.cc |
| index 3e43733ffc869cf75438b59ea3640be03c7193f8..c5046ea5ea0261d2c29a13b5953130d8c4b72268 100644 |
| --- a/extensions/renderer/api_bindings_system_unittest.cc |
| +++ b/extensions/renderer/api_bindings_system_unittest.cc |
| @@ -35,6 +35,10 @@ const char kAlphaAPISpec[] = |
| " 'prop1': {'type': 'string'}," |
| " 'prop2': {'type': 'integer', 'optional': true}" |
| " }" |
| + " }, {" |
| + " 'id': 'alpha.enumRef'," |
| + " 'type': 'string'," |
| + " 'enum': ['cat', 'dog']" |
| " }]," |
| " 'functions': [{" |
| " 'name': 'functionWithCallback'," |
| @@ -54,6 +58,9 @@ const char kAlphaAPISpec[] = |
| " 'name': 'callback'," |
| " 'type': 'function'" |
| " }]" |
| + " }, {" |
| + " 'name': 'functionWithEnum'," |
| + " 'parameters': [{ 'name': 'e', '$ref': 'alpha.enumRef'}]" |
|
lazyboy
2017/03/20 19:24:19
nit: rm space after {
Devlin
2017/03/20 22:00:34
Done.
|
| " }]," |
| " 'events': [{" |
| " 'name': 'alphaEvent'" |
| @@ -87,12 +94,14 @@ bool AllowAllAPIs(const std::string& name) { |
| // The base class to test the APIBindingsSystem. This allows subclasses to |
| // retrieve API schemas differently. |
| -class APIBindingsSystemTestBase : public APIBindingTest { |
| +class APIBindingsSystemTest : public APIBindingTest { |
| public: |
| // Returns the DictionaryValue representing the schema with the given API |
| // name. |
| - virtual const base::DictionaryValue& GetAPISchema( |
| - const std::string& api_name) = 0; |
| + const base::DictionaryValue& GetAPISchema(const std::string& api_name) { |
| + EXPECT_TRUE(base::ContainsKey(api_schemas_, api_name)); |
| + return *api_schemas_[api_name]; |
| + } |
| // Stores the request in |last_request_|. |
| void OnAPIRequest(std::unique_ptr<APIRequestHandler::Request> request, |
| @@ -106,17 +115,36 @@ class APIBindingsSystemTestBase : public APIBindingTest { |
| v8::Local<v8::Context> context) {} |
| protected: |
| - APIBindingsSystemTestBase() {} |
| + APIBindingsSystemTest() {} |
| void SetUp() override { |
| APIBindingTest::SetUp(); |
| + |
| + // Create the fake API schemas. |
| + { |
| + struct APIData { |
| + const char* name; |
| + const char* spec; |
| + } api_data[] = { |
| + {kAlphaAPIName, kAlphaAPISpec}, |
| + {kBetaAPIName, kBetaAPISpec}, |
| + {kGammaAPIName, kGammaAPISpec}, |
| + }; |
| + for (const auto& api : api_data) { |
| + std::unique_ptr<base::DictionaryValue> api_schema = |
| + DictionaryValueFromString(api.spec); |
| + ASSERT_TRUE(api_schema); |
| + api_schemas_[api.name] = std::move(api_schema); |
| + } |
| + } |
| + |
| bindings_system_ = base::MakeUnique<APIBindingsSystem>( |
| base::Bind(&RunFunctionOnGlobalAndIgnoreResult), |
| base::Bind(&RunFunctionOnGlobalAndReturnHandle), |
| - base::Bind(&APIBindingsSystemTestBase::GetAPISchema, |
| + base::Bind(&APIBindingsSystemTest::GetAPISchema, |
| base::Unretained(this)), |
| - base::Bind(&APIBindingsSystemTestBase::OnAPIRequest, |
| + base::Bind(&APIBindingsSystemTest::OnAPIRequest, |
| base::Unretained(this)), |
| - base::Bind(&APIBindingsSystemTestBase::OnEventListenersChanged, |
| + base::Bind(&APIBindingsSystemTest::OnEventListenersChanged, |
| base::Unretained(this)), |
| APILastError(APILastError::GetParent())); |
| } |
| @@ -135,7 +163,29 @@ class APIBindingsSystemTestBase : public APIBindingTest { |
| // Checks that |last_request_| exists and was provided with the |
| // |expected_name| and |expected_arguments|. |
| void ValidateLastRequest(const std::string& expected_name, |
| - const std::string& expected_arguments); |
| + const std::string& expected_arguments) { |
| + ASSERT_TRUE(last_request()); |
| + // Note that even if no arguments are provided by the API call, we should |
| + // have an empty list. |
| + ASSERT_TRUE(last_request()->arguments); |
| + EXPECT_EQ(expected_name, last_request()->method_name); |
| + EXPECT_EQ(ReplaceSingleQuotes(expected_arguments), |
| + ValueToString(*last_request()->arguments)); |
| + } |
| + |
| + void CallFunctionOnObject(v8::Local<v8::Context> context, |
| + v8::Local<v8::Object> object, |
| + const std::string& script_source) { |
| + std::string wrapped_script_source = |
| + base::StringPrintf("(function(obj) { %s })", script_source.c_str()); |
| + |
| + v8::Local<v8::Function> func = |
| + FunctionFromString(context, wrapped_script_source); |
| + ASSERT_FALSE(func.IsEmpty()); |
| + |
| + v8::Local<v8::Value> argv[] = {object}; |
| + RunFunction(func, context, 1, argv); |
| + } |
| const APIRequestHandler::Request* last_request() const { |
| return last_request_.get(); |
| @@ -144,6 +194,9 @@ class APIBindingsSystemTestBase : public APIBindingTest { |
| APIBindingsSystem* bindings_system() { return bindings_system_.get(); } |
| private: |
| + // The API schemas for the fake APIs. |
| + std::map<std::string, std::unique_ptr<base::DictionaryValue>> api_schemas_; |
| + |
| // The APIBindingsSystem associated with the test. Safe to use across multiple |
| // contexts. |
| std::unique_ptr<APIBindingsSystem> bindings_system_; |
| @@ -152,82 +205,9 @@ class APIBindingsSystemTestBase : public APIBindingTest { |
| // there is none. |
| std::unique_ptr<APIRequestHandler::Request> last_request_; |
| - DISALLOW_COPY_AND_ASSIGN(APIBindingsSystemTestBase); |
| -}; |
| - |
| -void APIBindingsSystemTestBase::ValidateLastRequest( |
| - const std::string& expected_name, |
| - const std::string& expected_arguments) { |
| - ASSERT_TRUE(last_request()); |
| - // Note that even if no arguments are provided by the API call, we should have |
| - // an empty list. |
| - ASSERT_TRUE(last_request()->arguments); |
| - EXPECT_EQ(expected_name, last_request()->method_name); |
| - EXPECT_EQ(ReplaceSingleQuotes(expected_arguments), |
| - ValueToString(*last_request()->arguments)); |
| -} |
| - |
| -// An implementation that works with fake/supplied APIs, for easy testability. |
| -class APIBindingsSystemTest : public APIBindingsSystemTestBase { |
| - protected: |
| - APIBindingsSystemTest() {} |
| - |
| - // Calls a function constructed from |script_source| on the given |object|. |
| - void CallFunctionOnObject(v8::Local<v8::Context> context, |
| - v8::Local<v8::Object> object, |
| - const std::string& script_source); |
| - |
| - private: |
| - const base::DictionaryValue& GetAPISchema( |
| - const std::string& api_name) override { |
| - EXPECT_TRUE(base::ContainsKey(api_schemas_, api_name)); |
| - return *api_schemas_[api_name]; |
| - } |
| - void SetUp() override; |
| - |
| - // The API schemas for the fake APIs. |
| - std::map<std::string, std::unique_ptr<base::DictionaryValue>> api_schemas_; |
| - |
| DISALLOW_COPY_AND_ASSIGN(APIBindingsSystemTest); |
| }; |
| -void APIBindingsSystemTest::CallFunctionOnObject( |
| - v8::Local<v8::Context> context, |
| - v8::Local<v8::Object> object, |
| - const std::string& script_source) { |
| - std::string wrapped_script_source = |
| - base::StringPrintf("(function(obj) { %s })", script_source.c_str()); |
| - |
| - v8::Local<v8::Function> func = |
| - FunctionFromString(context, wrapped_script_source); |
| - ASSERT_FALSE(func.IsEmpty()); |
| - |
| - v8::Local<v8::Value> argv[] = {object}; |
| - RunFunction(func, context, 1, argv); |
| -} |
| - |
| -void APIBindingsSystemTest::SetUp() { |
| - // Create the fake API schemas. |
| - { |
| - struct APIData { |
| - const char* name; |
| - const char* spec; |
| - } api_data[] = { |
| - {kAlphaAPIName, kAlphaAPISpec}, |
| - {kBetaAPIName, kBetaAPISpec}, |
| - {kGammaAPIName, kGammaAPISpec}, |
| - }; |
| - for (const auto& api : api_data) { |
| - std::unique_ptr<base::DictionaryValue> api_schema = |
| - DictionaryValueFromString(api.spec); |
| - ASSERT_TRUE(api_schema); |
| - api_schemas_[api.name] = std::move(api_schema); |
| - } |
| - } |
| - |
| - APIBindingsSystemTestBase::SetUp(); |
| -} |
| - |
| // Tests API object initialization, calling a method on the supplied APIs, and |
| // triggering the callback for the request. |
| TEST_F(APIBindingsSystemTest, TestInitializationAndCallbacks) { |
| @@ -285,6 +265,18 @@ TEST_F(APIBindingsSystemTest, TestInitializationAndCallbacks) { |
| } |
| { |
| + // Test an invalid invocation -> throwing error. |
| + const char kTestCall[] = |
| + "(function(obj) { obj.functionWithEnum('mouse') })"; |
| + v8::Local<v8::Function> function = FunctionFromString(context, kTestCall); |
| + v8::Local<v8::Value> args[] = {alpha_api}; |
| + RunFunctionAndExpectError(function, context, arraysize(args), args, |
| + "Uncaught TypeError: Invalid invocation"); |
| + EXPECT_FALSE(last_request()); |
| + reset_last_request(); // Just to not pollute future results. |
| + } |
| + |
| + { |
| // Test an event registration -> event occurrence. |
| const char kTestCall[] = |
| "obj.alphaEvent.addListener(function() {\n" |
| @@ -445,153 +437,4 @@ TEST_F(APIBindingsSystemTest, CrossAPIReferences) { |
| } |
| } |
| -// An implementation using real API schemas. |
| -class APIBindingsSystemTestWithRealAPI : public APIBindingsSystemTestBase { |
| - protected: |
| - APIBindingsSystemTestWithRealAPI() {} |
| - |
| - // Executes the given |script_source| in |context|, expecting no exceptions. |
| - void ExecuteScript(v8::Local<v8::Context> context, |
| - const std::string& script_source); |
| - |
| - // Executes the given |script_source| in |context| and compares a caught |
| - // error to |expected_error|. |
| - void ExecuteScriptAndExpectError(v8::Local<v8::Context> context, |
| - const std::string& script_source, |
| - const std::string& expected_error); |
| - |
| - private: |
| - const base::DictionaryValue& GetAPISchema( |
| - const std::string& api_name) override { |
| - const base::DictionaryValue* schema = |
| - ExtensionAPI::GetSharedInstance()->GetSchema(api_name); |
| - EXPECT_TRUE(schema); |
| - return *schema; |
| - } |
| - |
| - DISALLOW_COPY_AND_ASSIGN(APIBindingsSystemTestWithRealAPI); |
| -}; |
| - |
| -void APIBindingsSystemTestWithRealAPI::ExecuteScript( |
| - v8::Local<v8::Context> context, |
| - const std::string& script_source) { |
| - v8::TryCatch try_catch(isolate()); |
| - // V8ValueFromScriptSource runs the source and returns the result; here, we |
| - // only care about running the source. |
| - V8ValueFromScriptSource(context, script_source); |
| - EXPECT_FALSE(try_catch.HasCaught()) |
| - << gin::V8ToString(try_catch.Message()->Get()); |
| -} |
| - |
| -void APIBindingsSystemTestWithRealAPI::ExecuteScriptAndExpectError( |
| - v8::Local<v8::Context> context, |
| - const std::string& script_source, |
| - const std::string& expected_error) { |
| - v8::TryCatch try_catch(isolate()); |
| - V8ValueFromScriptSource(context, script_source); |
| - ASSERT_TRUE(try_catch.HasCaught()) << script_source; |
| - EXPECT_EQ(expected_error, gin::V8ToString(try_catch.Message()->Get())); |
| -} |
| - |
| -// The following test demonstrates how APIBindingsSystem can be used with "real" |
| -// Extension APIs; that is, using the raw Extension API schemas, rather than a |
| -// substituted test schema. This is useful to both show how the system is |
| -// intended to be used in the future as well as to make sure that it works with |
| -// actual APIs. |
| -TEST_F(APIBindingsSystemTestWithRealAPI, RealAPIs) { |
| - v8::HandleScope handle_scope(isolate()); |
| - v8::Local<v8::Context> context = MainContext(); |
| - |
| - v8::Local<v8::Object> chrome = v8::Object::New(isolate()); |
| - { |
| - v8::Maybe<bool> res = context->Global()->Set( |
| - context, gin::StringToV8(isolate(), "chrome"), chrome); |
| - ASSERT_TRUE(res.IsJust()); |
| - ASSERT_TRUE(res.FromJust()); |
| - } |
| - |
| - auto add_api_to_chrome = [this, &chrome, |
| - &context](const std::string& api_name) { |
| - v8::Local<v8::Object> api = bindings_system()->CreateAPIInstance( |
| - api_name, context, context->GetIsolate(), base::Bind(&AllowAllAPIs), |
| - nullptr); |
| - ASSERT_FALSE(api.IsEmpty()) << api_name; |
| - v8::Maybe<bool> res = chrome->Set( |
| - context, gin::StringToV8(context->GetIsolate(), api_name), api); |
| - ASSERT_TRUE(res.IsJust()); |
| - ASSERT_TRUE(res.FromJust()); |
| - }; |
| - |
| - // Pick two relatively small APIs that don't have any custom bindings (which |
| - // aren't supported yet). |
| - add_api_to_chrome("idle"); |
| - add_api_to_chrome("power"); |
| - |
| - // Test passing methods. |
| - { |
| - const char kTestCall[] = "chrome.power.requestKeepAwake('display');"; |
| - ExecuteScript(context, kTestCall); |
| - ValidateLastRequest("power.requestKeepAwake", "['display']"); |
| - EXPECT_EQ(-1, last_request()->request_id); |
| - reset_last_request(); |
| - } |
| - |
| - { |
| - const char kTestCall[] = "chrome.power.releaseKeepAwake()"; |
| - ExecuteScript(context, kTestCall); |
| - ValidateLastRequest("power.releaseKeepAwake", "[]"); |
| - EXPECT_EQ(-1, last_request()->request_id); |
| - reset_last_request(); |
| - } |
| - |
| - { |
| - const char kTestCall[] = "chrome.idle.queryState(30, function() {})"; |
| - ExecuteScript(context, kTestCall); |
| - ValidateLastRequest("idle.queryState", "[30]"); |
| - EXPECT_NE(-1, last_request()->request_id); |
| - reset_last_request(); |
| - } |
| - |
| - { |
| - const char kTestCall[] = "chrome.idle.setDetectionInterval(30);"; |
| - ExecuteScript(context, kTestCall); |
| - ValidateLastRequest("idle.setDetectionInterval", "[30]"); |
| - EXPECT_EQ(-1, last_request()->request_id); |
| - reset_last_request(); |
| - } |
| - |
| - // Check catching errors. |
| - const char kError[] = "Uncaught TypeError: Invalid invocation"; |
| - { |
| - // "disp" is not an allowed enum value. |
| - const char kTestCall[] = "chrome.power.requestKeepAwake('disp');"; |
| - ExecuteScriptAndExpectError(context, kTestCall, kError); |
| - EXPECT_FALSE(last_request()); |
| - reset_last_request(); // Just to not pollute future results. |
| - } |
| - |
| - { |
| - // The queryState() param has a minimum of 15. |
| - const char kTestCall[] = "chrome.idle.queryState(10, function() {});"; |
| - ExecuteScriptAndExpectError(context, kTestCall, kError); |
| - EXPECT_FALSE(last_request()); |
| - reset_last_request(); // Just to not pollute future results. |
| - } |
| - |
| - { |
| - const char kTestCall[] = |
| - "chrome.idle.onStateChanged.addListener(state => {\n" |
| - " this.idleState = state;\n" |
| - "});\n"; |
| - ExecuteScript(context, kTestCall); |
| - EXPECT_EQ("undefined", GetStringPropertyFromObject(context->Global(), |
| - context, "idleState")); |
| - bindings_system()->FireEventInContext("idle.onStateChanged", context, |
| - *ListValueFromString("['active']")); |
| - |
| - EXPECT_EQ("\"active\"", GetStringPropertyFromObject(context->Global(), |
| - context, "idleState")); |
| - } |
| -} |
| - |
| } // namespace extensions |