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..83caeb2806ae3921c2edc9448276bda3d4975b82 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'}]" |
" }]," |
" '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 |