blob: 6c4148418b427c5567611be2771a556107b7506b [file]
#!/usr/bin/env vpython3
# Copyright 2026 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Unit tests for code_generator_frontend.py.
The generator runs at import time, so the tests run it as a subprocess, the
way the build does.
"""
import json
import os
import subprocess
import sys
import tempfile
import unittest
GENERATOR = os.path.join(os.path.dirname(os.path.abspath(__file__)),
'code_generator_frontend.py')
PROTOCOL = {
'version': {
'major': '1',
'minor': '3'
},
'domains': [{
'domain':
'DOM',
'types': [{
'id': 'NodeId',
'type': 'integer'
}, {
'id': 'Node',
'type': 'object',
'properties': [{
'name': 'nodeId',
'$ref': 'NodeId'
}],
}],
'commands': [{
'name':
'getDocument',
'description':
'Returns the root DOM node.',
'parameters': [{
'name': 'depth',
'type': 'integer',
'optional': True,
'description': 'The maximum depth.',
}],
'returns': [{
'name': 'root',
'$ref': 'Node'
}],
}],
'events': [{
'name': 'documentUpdated',
'parameters': []
}],
}],
}
class CodeGeneratorFrontendTest(unittest.TestCase):
def setUp(self):
tmpdir = tempfile.TemporaryDirectory()
self.addCleanup(tmpdir.cleanup)
self.protocol_json = os.path.join(tmpdir.name, 'protocol.json')
self.output = os.path.join(tmpdir.name, 'InspectorBackendCommands.ts')
with open(self.protocol_json, 'w', encoding='utf-8') as f:
json.dump(PROTOCOL, f)
def run_generator(self, *args):
return subprocess.run([sys.executable, GENERATOR] + list(args),
capture_output=True,
text=True,
check=False)
def generate(self):
result = self.run_generator('--input', self.protocol_json, '--output',
self.output)
self.assertEqual(result.returncode, 0, result.stderr)
with open(self.output, 'rb') as f:
return f.read().decode('utf-8')
def test_registers_the_protocol(self):
contents = self.generate()
self.assertIn(
'export function registerCommands(inspectorBackend: '
'InspectorBackendAPI): void {', contents)
self.assertIn(
'inspectorBackend.registerCommand("DOM.getDocument", '
'[{"name": "depth", "type": "number", "optional": true, '
'"description": "The maximum depth.", "typeRef": null}], '
'["root"], "Returns the root DOM node.");', contents)
self.assertIn(
'inspectorBackend.registerEvent("DOM.documentUpdated", [])',
contents)
self.assertIn(
'inspectorBackend.registerType("DOM.Node", [{"name": "nodeId", '
'"type": "number", "optional": false, "description": "", '
'"typeRef": "DOM.NodeId"}]);', contents)
def test_header_does_not_depend_on_the_checkout(self):
contents = self.generate()
self.assertTrue(
contents.startswith(
'// Copyright 2020 The Chromium Authors\n'
'// Use of this source code is governed by a BSD-style license that can be\n'
'// found in the LICENSE file.\n'
'// File is generated by scripts/build/code_generator_frontend.py\n'
), contents[:300])
self.assertNotIn('\r', contents)
def test_always_rewrites_the_output(self):
# scripts/build/run_with_restat.py restores the mtime of outputs whose
# bytes did not change; the generator itself must always write.
first = self.generate()
mtime = os.stat(self.output).st_mtime_ns
os.utime(self.output,
ns=(mtime - 1_000_000_000, mtime - 1_000_000_000))
second = self.generate()
self.assertEqual(first, second)
self.assertNotEqual(
os.stat(self.output).st_mtime_ns, mtime - 1_000_000_000)
def test_requires_both_paths(self):
self.assertNotEqual(
self.run_generator('--input', self.protocol_json).returncode, 0)
self.assertNotEqual(
self.run_generator('--output', self.output).returncode, 0)
self.assertFalse(os.path.exists(self.output))
if __name__ == '__main__':
unittest.main()