Commit 6b9268d9 authored by skyostil's avatar skyostil Committed by Commit bot

headless: Ensure all commands have parameters and return types

When generating C++ bindings, ensure all commands have a method which
takes a full parameters object and returns a return value object. This
ensures the API for experimental commands is retained even if that
command is made stable.

BUG=640919

Review-Url: https://codereview.chromium.org/2278123002
Cr-Commit-Position: refs/heads/master@{#414431}
parent 1708d758
...@@ -373,16 +373,21 @@ def PatchExperimentalCommandsAndEvents(json_api): ...@@ -373,16 +373,21 @@ def PatchExperimentalCommandsAndEvents(json_api):
command['experimental'] = True command['experimental'] = True
for event in domain.get('events', []): for event in domain.get('events', []):
event['experimental'] = True event['experimental'] = True
def EnsureCommandsHaveParametersAndReturnTypes(json_api):
"""
Make sure all commands have at least empty parameters and return values. This
guarantees API compatibility if a previously experimental command is made
stable.
"""
for domain in json_api['domains']:
for command in domain.get('commands', []): for command in domain.get('commands', []):
if not command.get('experimental', False):
continue
if not 'parameters' in command: if not 'parameters' in command:
command['parameters'] = [] command['parameters'] = []
if not 'returns' in command: if not 'returns' in command:
command['returns'] = [] command['returns'] = []
for event in domain.get('events', []): for event in domain.get('events', []):
if not event.get('experimental', False):
continue
if not 'parameters' in event: if not 'parameters' in event:
event['parameters'] = [] event['parameters'] = []
...@@ -422,6 +427,7 @@ if __name__ == '__main__': ...@@ -422,6 +427,7 @@ if __name__ == '__main__':
json_api, output_dirname = ParseArguments(sys.argv[1:]) json_api, output_dirname = ParseArguments(sys.argv[1:])
jinja_env = InitializeJinjaEnv(output_dirname) jinja_env = InitializeJinjaEnv(output_dirname)
PatchExperimentalCommandsAndEvents(json_api) PatchExperimentalCommandsAndEvents(json_api)
EnsureCommandsHaveParametersAndReturnTypes(json_api)
SynthesizeCommandTypes(json_api) SynthesizeCommandTypes(json_api)
SynthesizeEventTypes(json_api) SynthesizeEventTypes(json_api)
PatchFullQualifiedRefs(json_api) PatchFullQualifiedRefs(json_api)
......
...@@ -370,37 +370,13 @@ class ClientApiGeneratorTest(unittest.TestCase): ...@@ -370,37 +370,13 @@ class ClientApiGeneratorTest(unittest.TestCase):
} }
] ]
} }
expected_types = [
{
'type': 'object',
'id': 'FooCommandParams',
'description': 'Parameters for the FooCommand command.',
'properties': [],
},
{
'type': 'object',
'id': 'FooCommandResult',
'description': 'Result for the FooCommand command.',
'properties': [],
},
{
'type': 'object',
'id': 'BarEventParams',
'description': 'Parameters for the BarEvent event.',
'properties': [],
}
]
client_api_generator.PatchExperimentalCommandsAndEvents(json_api) client_api_generator.PatchExperimentalCommandsAndEvents(json_api)
client_api_generator.SynthesizeCommandTypes(json_api)
client_api_generator.SynthesizeEventTypes(json_api)
for command in json_api['domains'][0]['commands']: for command in json_api['domains'][0]['commands']:
self.assertTrue(command['experimental']) self.assertTrue(command['experimental'])
for event in json_api['domains'][0]['events']: for event in json_api['domains'][0]['events']:
self.assertTrue(command['experimental']) self.assertTrue(command['experimental'])
types = json_api['domains'][0]['types']
self.assertListEqual(types, expected_types)
def test_PatchExperimentalCommandsAndEvents(self): def test_EnsureCommandsHaveParametersAndReturnTypes(self):
json_api = { json_api = {
'domains': [ 'domains': [
{ {
...@@ -408,13 +384,11 @@ class ClientApiGeneratorTest(unittest.TestCase): ...@@ -408,13 +384,11 @@ class ClientApiGeneratorTest(unittest.TestCase):
'commands': [ 'commands': [
{ {
'name': 'FooCommand', 'name': 'FooCommand',
'experimental': True,
} }
], ],
'events': [ 'events': [
{ {
'name': 'BarEvent', 'name': 'BarEvent',
'experimental': True,
} }
] ]
} }
...@@ -440,13 +414,9 @@ class ClientApiGeneratorTest(unittest.TestCase): ...@@ -440,13 +414,9 @@ class ClientApiGeneratorTest(unittest.TestCase):
'properties': [], 'properties': [],
} }
] ]
client_api_generator.PatchExperimentalCommandsAndEvents(json_api) client_api_generator.EnsureCommandsHaveParametersAndReturnTypes(json_api)
client_api_generator.SynthesizeCommandTypes(json_api) client_api_generator.SynthesizeCommandTypes(json_api)
client_api_generator.SynthesizeEventTypes(json_api) client_api_generator.SynthesizeEventTypes(json_api)
for command in json_api['domains'][0]['commands']:
self.assertTrue(command['experimental'])
for event in json_api['domains'][0]['events']:
self.assertTrue(command['experimental'])
types = json_api['domains'][0]['types'] types = json_api['domains'][0]['types']
self.assertListEqual(types, expected_types) self.assertListEqual(types, expected_types)
......
...@@ -32,19 +32,8 @@ void Domain::RemoveObserver(Observer* observer) { ...@@ -32,19 +32,8 @@ void Domain::RemoveObserver(Observer* observer) {
{% set class_name = 'ExperimentalDomain' if command.experimental else 'Domain' %} {% set class_name = 'ExperimentalDomain' if command.experimental else 'Domain' %}
{% set method_name = command.name | to_title_case %} {% set method_name = command.name | to_title_case %}
{% if "parameters" in command and "returns" in command %}
void {{class_name}}::{{method_name}}(std::unique_ptr<{{method_name}}Params> params, base::Callback<void(std::unique_ptr<{{method_name}}Result>)> callback) { void {{class_name}}::{{method_name}}(std::unique_ptr<{{method_name}}Params> params, base::Callback<void(std::unique_ptr<{{method_name}}Result>)> callback) {
dispatcher_->SendMessage("{{domain.domain}}.{{command.name}}", params->Serialize(), base::Bind(&Domain::Handle{{method_name}}Response, callback)); dispatcher_->SendMessage("{{domain.domain}}.{{command.name}}", params->Serialize(), base::Bind(&Domain::Handle{{method_name}}Response, callback));
{% elif "parameters" in command %}
void {{class_name}}::{{method_name}}(std::unique_ptr<{{method_name}}Params> params, base::Callback<void()> callback) {
dispatcher_->SendMessage("{{domain.domain}}.{{command.name}}", params->Serialize(), std::move(callback));
{% elif "returns" in command %}
void {{class_name}}::{{method_name}}(base::Callback<void(std::unique_ptr<{{method_name}}Result>)> callback) {
dispatcher_->SendMessage("{{domain.domain}}.{{command.name}}", base::Bind(&Domain::Handle{{method_name}}Response, std::move(callback)));
{% else %}
void {{class_name}}::{{method_name}}(base::Callback<void()> callback) {
dispatcher_->SendMessage("{{domain.domain}}.{{command.name}}", std::move(callback));
{% endif %}
} }
{# Generate convenience methods that take the required parameters directly. #} {# Generate convenience methods that take the required parameters directly. #}
{% if not "parameters" in command %}{% continue %}{% endif %} {% if not "parameters" in command %}{% continue %}{% endif %}
...@@ -59,8 +48,8 @@ void {{class_name}}::{{method_name}}({##} ...@@ -59,8 +48,8 @@ void {{class_name}}::{{method_name}}({##}
{% if not loop.first %}, {% endif %} {% if not loop.first %}, {% endif %}
{{resolve_type(parameter).pass_type}} {{parameter.name | camelcase_to_hacker_style -}} {{resolve_type(parameter).pass_type}} {{parameter.name | camelcase_to_hacker_style -}}
{% endfor %} {% endfor %}
{% if "parameters" in command and not command.parameters[0].get("optional", False) %}, {% endif %}{# -#} {% if command.get("parameters", []) and not command.parameters[0].get("optional", False) %}, {% endif %}{# -#}
{% if "returns" in command -%} {% if command.get("returns", []) -%}
base::Callback<void(std::unique_ptr<{{method_name}}Result>)> callback{##} base::Callback<void(std::unique_ptr<{{method_name}}Result>)> callback{##}
{% else -%} {% else -%}
base::Callback<void()> callback{##} base::Callback<void()> callback{##}
...@@ -75,8 +64,19 @@ void {{class_name}}::{{method_name}}({##} ...@@ -75,8 +64,19 @@ void {{class_name}}::{{method_name}}({##}
{% endfor %} {% endfor %}
.Build(); .Build();
{# Send the message. #} {# Send the message. #}
{{method_name}}(std::move(params), std::move(callback)); {% if command.get("returns", []) -%}
dispatcher_->SendMessage("{{domain.domain}}.{{command.name}}", params->Serialize(), base::Bind(&Domain::Handle{{method_name}}Response, callback));
{% else %}
dispatcher_->SendMessage("{{domain.domain}}.{{command.name}}", params->Serialize(), std::move(callback));
{% endif %}
}
{# If the command has no return value, generate a convenience method that #}
{# accepts a base::Callback<void()> together with the parameters object. #}
{% if not command.get("returns", []) %}
void {{class_name}}::{{method_name}}(std::unique_ptr<{{method_name}}Params> params, base::Callback<void()> callback) {
dispatcher_->SendMessage("{{domain.domain}}.{{command.name}}", params->Serialize(), std::move(callback));
} }
{% endif %}
{% endfor %} {% endfor %}
{# Generate response handlers for commands that need them. #} {# Generate response handlers for commands that need them. #}
......
...@@ -20,15 +20,7 @@ ...@@ -20,15 +20,7 @@
{% if command.description %} {% if command.description %}
// {{ command.description }} // {{ command.description }}
{% endif %} {% endif %}
{% if "parameters" in command and "returns" in command %}
void {{method_name}}(std::unique_ptr<{{method_name}}Params> params, base::Callback<void(std::unique_ptr<{{method_name}}Result>)> callback = base::Callback<void(std::unique_ptr<{{method_name}}Result>)>()); void {{method_name}}(std::unique_ptr<{{method_name}}Params> params, base::Callback<void(std::unique_ptr<{{method_name}}Result>)> callback = base::Callback<void(std::unique_ptr<{{method_name}}Result>)>());
{% elif "parameters" in command %}
void {{method_name}}(std::unique_ptr<{{method_name}}Params> params, base::Callback<void()> callback = base::Callback<void()>());
{% elif "returns" in command %}
void {{method_name}}(base::Callback<void(std::unique_ptr<{{method_name}}Result>)> callback = base::Callback<void(std::unique_ptr<{{method_name}}Result>)>());
{% else %}
void {{method_name}}(base::Callback<void()> callback = base::Callback<void()>());
{% endif %}
{# Generate convenience methods that take the required parameters directly. #} {# Generate convenience methods that take the required parameters directly. #}
{# Don't generate these for experimental commands. #} {# Don't generate these for experimental commands. #}
{% if "parameters" in command and not command.experimental %} {% if "parameters" in command and not command.experimental %}
...@@ -40,12 +32,17 @@ ...@@ -40,12 +32,17 @@
{% if not loop.first %}, {% endif %} {% if not loop.first %}, {% endif %}
{{resolve_type(parameter).pass_type}} {{parameter.name | camelcase_to_hacker_style -}} {{resolve_type(parameter).pass_type}} {{parameter.name | camelcase_to_hacker_style -}}
{% endfor %} {% endfor %}
{% if "parameters" in command and not command.parameters[0].get("optional", False) %}, {% endif %}{# -#} {% if command.get("parameters", []) and not command.parameters[0].get("optional", False) %}, {% endif %}{# -#}
{% if "returns" in command -%} {% if command.get("returns", []) -%}
base::Callback<void(std::unique_ptr<{{method_name}}Result>)> callback = base::Callback<void(std::unique_ptr<{{method_name}}Result>)>(){##} base::Callback<void(std::unique_ptr<{{method_name}}Result>)> callback = base::Callback<void(std::unique_ptr<{{method_name}}Result>)>(){##}
{% else -%} {% else -%}
base::Callback<void()> callback = base::Callback<void()>(){##} base::Callback<void()> callback = base::Callback<void()>(){##}
{% endif %}); {% endif %});
{# If the command has no return value, generate a convenience method that #}
{# accepts a base::Callback<void()> together with the parameters object. #}
{% if not command.get("returns", []) %}
void {{method_name}}(std::unique_ptr<{{method_name}}Params> params, base::Callback<void()> callback);
{% endif %}
{% endif %} {% endif %}
{% endmacro %} {% endmacro %}
......
...@@ -192,6 +192,11 @@ class HeadlessDevToolsClientExperimentalTest ...@@ -192,6 +192,11 @@ class HeadlessDevToolsClientExperimentalTest
.SetEnabled(false) .SetEnabled(false)
.Build()); .Build());
// Check that a previously experimental command which takes no parameters
// still works by giving it a parameter object.
devtools_client_->GetRuntime()->GetExperimental()->RunIfWaitingForDebugger(
runtime::RunIfWaitingForDebuggerParams::Builder().Build());
devtools_client_->GetPage()->GetExperimental()->AddObserver(this); devtools_client_->GetPage()->GetExperimental()->AddObserver(this);
devtools_client_->GetPage()->Enable(); devtools_client_->GetPage()->Enable();
devtools_client_->GetPage()->Navigate( devtools_client_->GetPage()->Navigate(
...@@ -200,8 +205,15 @@ class HeadlessDevToolsClientExperimentalTest ...@@ -200,8 +205,15 @@ class HeadlessDevToolsClientExperimentalTest
void OnFrameStoppedLoading( void OnFrameStoppedLoading(
const page::FrameStoppedLoadingParams& params) override { const page::FrameStoppedLoadingParams& params) override {
FinishAsynchronousTest(); // Check that a non-experimental command which has no return value can be
// called with a void() callback.
devtools_client_->GetPage()->Reload(
page::ReloadParams::Builder().Build(),
base::Bind(&HeadlessDevToolsClientExperimentalTest::OnReloadStarted,
base::Unretained(this)));
} }
void OnReloadStarted() { FinishAsynchronousTest(); }
}; };
HEADLESS_ASYNC_DEVTOOLED_TEST_F(HeadlessDevToolsClientExperimentalTest); HEADLESS_ASYNC_DEVTOOLED_TEST_F(HeadlessDevToolsClientExperimentalTest);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment