blob: 5cb721da9171e506e4aaba8094c8f4d0558a5107 [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 generate_aria.py."""
import contextlib
import io
import json
import os
import tempfile
import textwrap
import unittest
import generate_aria
# Exercises what JSON5 allows on top of JSON: comments, unquoted keys,
# single quotes and trailing commas.
ARIA_PROPERTIES_JSON5 = """\
{
// Metadata about the generated file.
metadata: {
namespace: 'ARIA',
export: 'CORE_EXPORT',
},
attributes: [
{
name: 'aria-checked',
type: 'token',
enum: ['true', 'false', 'mixed', 'undefined'],
default: 'undefined',
supportedOnRoles: ['checkbox', 'menuitemcheckbox'],
},
{
name: 'aria-label',
type: 'string',
isGlobal: true,
},
],
roles: [
{
name: 'checkbox',
superclasses: ['input'],
nameFrom: ['contents', 'author'],
nameRequired: true,
implicitValues: {'aria-checked': false},
},
],
}
"""
class GenerateAriaTest(unittest.TestCase):
def setUp(self):
tmpdir = tempfile.TemporaryDirectory()
self.addCleanup(tmpdir.cleanup)
self.input = os.path.join(tmpdir.name, 'aria_properties.json5')
self.output = os.path.join(tmpdir.name, 'ARIAProperties.ts')
with open(self.input, 'w', encoding='utf-8') as f:
f.write(ARIA_PROPERTIES_JSON5)
def generate(self):
code = generate_aria.main(
['--input', self.input, '--output', self.output])
self.assertEqual(code, 0)
with open(self.output, 'rb') as f:
return f.read().decode('utf-8')
def test_writes_the_config_as_a_typed_constant(self):
contents = self.generate()
self.assertTrue(
contents.startswith('// Copyright 2018 The Chromium Authors\n'))
self.assertIn('export interface AriaConfig {\n', contents)
prefix = 'export const config: AriaConfig = '
self.assertIn(prefix, contents)
self.assertTrue(contents.endswith(';\n'))
# The constant is the JSON5 input as JSON, with sorted keys.
config = contents[contents.index(prefix) + len(prefix):-len(';\n')]
self.assertEqual(
json.loads(config), {
'metadata': {
'namespace': 'ARIA',
'export': 'CORE_EXPORT',
},
'attributes': [
{
'name': 'aria-checked',
'type': 'token',
'enum': ['true', 'false', 'mixed', 'undefined'],
'default': 'undefined',
'supportedOnRoles': ['checkbox', 'menuitemcheckbox'],
},
{
'name': 'aria-label',
'type': 'string',
'isGlobal': True,
},
],
'roles': [{
'name': 'checkbox',
'superclasses': ['input'],
'nameFrom': ['contents', 'author'],
'nameRequired': True,
'implicitValues': {
'aria-checked': False
},
}],
})
self.assertEqual(
config.splitlines()[:3],
['{', ' "attributes": [', ' {'],
)
def test_output_is_deterministic_and_uses_lf_newlines(self):
first = self.generate()
second = self.generate()
self.assertEqual(first, second)
self.assertNotIn('\r', first)
def test_always_rewrites_the_output(self):
# Keeping the mtime of an unchanged output is run_with_restat.py's job,
# so the generator must not skip the write itself.
with open(self.output, 'w', encoding='utf-8') as f:
f.write('stale')
contents = self.generate()
self.assertNotEqual(contents, 'stale')
def test_requires_input_and_output(self):
with contextlib.redirect_stderr(io.StringIO()) as stderr:
with self.assertRaises(SystemExit) as cm:
generate_aria.main(['--input', self.input])
self.assertEqual(cm.exception.code, 2)
self.assertIn('--output', stderr.getvalue())
def test_rejects_invalid_json5(self):
with open(self.input, 'w', encoding='utf-8') as f:
f.write(
textwrap.dedent('''\
{
attributes: [
'''))
with self.assertRaises(ValueError):
generate_aria.main(
['--input', self.input, '--output', self.output])
self.assertFalse(os.path.exists(self.output))
if __name__ == '__main__':
unittest.main()