blob: 51a61bbb4faec62c5c9402696056890d83183b79 [file] [log] [blame]
#!/usr/bin/env python2
# Copyright 2017 The Chromium OS Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
# pylint: disable=module-missing-docstring,class-missing-docstring
from __future__ import print_function
import json
import jsonschema
import os
import unittest
import re
import tempfile
import cros_config_schema
BASIC_CONFIG = """
reef-9042-fw: &reef-9042-fw
bcs-overlay: 'overlay-reef-private'
ec-image: 'Reef_EC.9042.87.1.tbz2'
main-image: 'Reef.9042.87.1.tbz2'
main-rw-image: 'Reef.9042.110.0.tbz2'
build-targets:
coreboot: 'reef'
chromeos:
devices:
- $name: 'basking'
products:
- $key-id: 'OEM2'
$brand-code: 'ASUN'
skus:
- $sku-id: 0
config:
audio:
main:
$card: 'bxtda7219max'
cras-config-dir: '{{$name}}'
ucm-suffix: '{{$name}}'
files:
- source: "{{$dsp-ini}}"
destination: "/etc/cras/{{$dsp-ini}}"
$dsp-ini: "{{cras-config-dir}}/dsp.ini"
brand-code: '{{$brand-code}}'
identity:
platform-name: "Reef"
smbios-name-match: "Reef"
sku-id: "{{$sku-id}}"
name: '{{$name}}'
firmware: *reef-9042-fw
firmware-signing:
key-id: '{{$key-id}}'
signature-id: '{{$name}}'
powerd-prefs: 'reef'
test-label: 'reef'
"""
this_dir = os.path.dirname(__file__)
class GetNamedTupleTests(unittest.TestCase):
def testRecursiveDicts(self):
val = {'a': {'b': 1, 'c': 2}}
val_tuple = cros_config_schema.GetNamedTuple(val)
self.assertEqual(val['a']['b'], val_tuple.a.b)
self.assertEqual(val['a']['c'], val_tuple.a.c)
def testListInRecursiveDicts(self):
val = {'a': {'b': [{'c': 2}]}}
val_tuple = cros_config_schema.GetNamedTuple(val)
self.assertEqual(val['a']['b'][0]['c'], val_tuple.a.b[0].c)
def testDashesReplacedWithUnderscores(self):
val = {'a-b': 1}
val_tuple = cros_config_schema.GetNamedTuple(val)
self.assertEqual(val['a-b'], val_tuple.a_b)
class MergeDictionaries(unittest.TestCase):
def testBaseKeyMerge(self):
primary = {'a': {'b': 1, 'c': 2}}
overlay = {'a': {'c': 3}, 'b': 4}
cros_config_schema.MergeDictionaries(primary, overlay)
self.assertEqual({'a': {'b': 1, 'c': 3}, 'b': 4}, primary)
def testBaseListAppend(self):
primary = {'a': {'b': 1, 'c': [1, 2]}}
overlay = {'a': {'c': [3, 4]}}
cros_config_schema.MergeDictionaries(primary, overlay)
self.assertEqual({'a': {'b': 1, 'c': [1, 2, 3, 4]}}, primary)
class ParseArgsTests(unittest.TestCase):
def testParseArgs(self):
argv = ['-s', 'schema', '-c', 'config', '-o', 'output', '-f' 'True']
args = cros_config_schema.ParseArgs(argv)
self.assertEqual(args.schema, 'schema')
self.assertEqual(args.config, 'config')
self.assertEqual(args.output, 'output')
self.assertTrue(args.filter)
def testParseArgsForConfigs(self):
argv = ['-o', 'output', '-m' 'm1' 'm2' 'm3']
args = cros_config_schema.ParseArgs(argv)
self.assertEqual(args.output, 'output')
self.assertEqual(args.configs, ['m1' 'm2' 'm3'])
class TransformConfigTests(unittest.TestCase):
def testBasicTransform(self):
result = cros_config_schema.TransformConfig(BASIC_CONFIG)
json_dict = json.loads(result)
self.assertEqual(len(json_dict), 1)
json_obj = cros_config_schema.GetNamedTuple(json_dict)
self.assertEqual(1, len(json_obj.chromeos.configs))
model = json_obj.chromeos.configs[0]
self.assertEqual('basking', model.name)
self.assertEqual('basking', model.audio.main.cras_config_dir)
# Check multi-level template variable evaluation
self.assertEqual('/etc/cras/basking/dsp.ini',
model.audio.main.files[0].destination)
def testTemplateVariableScope(self):
scoped_config = """
audio_common: &audio_common
main:
$ucm: "default"
$cras: "default"
ucm-suffix: "{{$ucm}}"
cras-config-dir: "{{$cras}}"
chromeos:
devices:
- $name: "some"
$ucm: "overridden-by-device-scope"
products:
- $key-id: 'SOME-KEY'
$brand-code: 'SOME-BRAND'
$cras: "overridden-by-product-scope"
skus:
- $sku-id: 0
config:
audio: *audio_common
brand-code: '{{$brand-code}}'
identity:
platform-name: "Some"
smbios-name-match: "Some"
name: '{{$name}}'
firmware:
no-firmware: True
"""
result = cros_config_schema.TransformConfig(scoped_config)
json_dict = json.loads(result)
json_obj = cros_config_schema.GetNamedTuple(json_dict)
config = json_obj.chromeos.configs[0]
self.assertEqual(
'overridden-by-product-scope', config.audio.main.cras_config_dir)
self.assertEqual(
'overridden-by-device-scope', config.audio.main.ucm_suffix)
class ValidateConfigSchemaTests(unittest.TestCase):
def setUp(self):
with open(os.path.join(this_dir,
'cros_config_schema.yaml')) as schema_stream:
self._schema = schema_stream.read()
def testBasicSchemaValidation(self):
cros_config_schema.ValidateConfigSchema(
self._schema, cros_config_schema.TransformConfig(BASIC_CONFIG))
def testMissingRequiredElement(self):
config = re.sub(r' *cras-config-dir: .*', '', BASIC_CONFIG)
config = re.sub(r' *volume: .*', '', BASIC_CONFIG)
try:
cros_config_schema.ValidateConfigSchema(
self._schema, cros_config_schema.TransformConfig(config))
except jsonschema.ValidationError as err:
self.assertIn('required', err.__str__())
self.assertIn('cras-config-dir', err.__str__())
def testReferencedNonExistentTemplateVariable(self):
config = re.sub(r' *$card: .*', '', BASIC_CONFIG)
try:
cros_config_schema.ValidateConfigSchema(
self._schema, cros_config_schema.TransformConfig(config))
except cros_config_schema.ValidationError as err:
self.assertIn('Referenced template variable', err.__str__())
self.assertIn('cras-config-dir', err.__str__())
class ValidateConfigTests(unittest.TestCase):
def testBasicValidation(self):
cros_config_schema.ValidateConfig(
cros_config_schema.TransformConfig(BASIC_CONFIG))
def testIdentitiesNotUnique(self):
config = """
reef-9042-fw: &reef-9042-fw
bcs-overlay: 'overlay-reef-private'
ec-image: 'Reef_EC.9042.87.1.tbz2'
main-image: 'Reef.9042.87.1.tbz2'
main-rw-image: 'Reef.9042.110.0.tbz2'
build-targets:
coreboot: 'reef'
chromeos:
devices:
- $name: 'astronaut'
products:
- $key-id: 'OEM2'
skus:
- config:
identity:
sku-id: 0
audio:
main:
cras-config-dir: '{{$name}}'
ucm-suffix: '{{$name}}'
name: '{{$name}}'
firmware: *reef-9042-fw
firmware-signing:
key-id: '{{$key-id}}'
signature-id: '{{$name}}'
- $name: 'astronaut'
products:
- $key-id: 'OEM2'
skus:
- config:
identity:
sku-id: 0
audio:
main:
cras-config-dir: '{{$name}}'
ucm-suffix: '{{$name}}'
name: '{{$name}}'
firmware: *reef-9042-fw
firmware-signing:
key-id: '{{$key-id}}'
signature-id: '{{$name}}'
"""
try:
cros_config_schema.ValidateConfig(
cros_config_schema.TransformConfig(config))
except cros_config_schema.ValidationError as err:
self.assertIn('Identities are not unique', err.__str__())
def testWhitelabelWithOtherThanBrandChanges(self):
config = """
chromeos:
devices:
- $name: 'whitelabel'
products:
- $key-id: 'WL1'
$wallpaper: 'WL1_WALLPAPER'
$whitelabel-tag: 'WL1_TAG'
$brand-code: 'WL1_BRAND_CODE'
$powerd-prefs: 'WL1_POWERD_PREFS'
- $key-id: 'WL2'
$wallpaper: 'WL2_WALLPAPER'
$whitelabel-tag: 'WL2_TAG'
$brand-code: 'WL2_BRAND_CODE'
$powerd-prefs: 'WL2_POWERD_PREFS'
skus:
- config:
identity:
sku-id: 0
whitelabel-tag: '{{$whitelabel-tag}}'
name: '{{$name}}'
brand-code: '{{$brand-code}}'
wallpaper: '{{$wallpaper}}'
# THIS WILL CAUSE THE FAILURE
powerd-prefs: '{{$powerd-prefs}}'
"""
try:
cros_config_schema.ValidateConfig(
cros_config_schema.TransformConfig(config))
except cros_config_schema.ValidationError as err:
self.assertIn('Whitelabel configs can only', err.__str__())
class FilterBuildElements(unittest.TestCase):
def testBasicFilterBuildElements(self):
json_dict = json.loads(
cros_config_schema.FilterBuildElements(
cros_config_schema.TransformConfig(BASIC_CONFIG)))
self.assertNotIn('firmware', json_dict['chromeos']['configs'][0])
class MainTests(unittest.TestCase):
def testMainWithExampleWithBuildAndCBindings(self):
output = tempfile.mktemp()
c_output = tempfile.mktemp()
cros_config_schema.Main(
None,
os.path.join(this_dir, '../libcros_config/test.yaml'),
output,
gen_c_bindings_output=c_output)
regen_cmd = ('To regenerate the expected output, run:\n'
'\tpython -m cros_config_host.cros_config_schema '
'-c libcros_config/test.yaml '
'-o libcros_config/test_build.json '
'-g libcros_config/test.c')
with open(output, 'r') as output_stream:
with open(os.path.join(
this_dir, '../libcros_config/test_build.json')) as expected_stream:
self.assertEqual(expected_stream.read(), output_stream.read(),
regen_cmd)
with open(c_output, 'r') as output_stream:
with open(os.path.join(this_dir,
'../libcros_config/test.c')) as expected_stream:
self.assertEqual(expected_stream.read(), output_stream.read(),
regen_cmd)
def testMainWithExampleWithoutBuild(self):
output = tempfile.mktemp()
cros_config_schema.Main(
None,
os.path.join(this_dir, '../libcros_config/test.yaml'),
output,
filter_build_details=True)
with open(output, 'r') as output_stream:
with open(os.path.join(this_dir,
'../libcros_config/test.json')) as expected_stream:
self.assertEqual(expected_stream.read(), output_stream.read(),
('To regenerate the expected output, run:\n'
'\tpython -m cros_config_host.cros_config_schema '
'-f True '
'-c libcros_config/test.yaml '
'-o libcros_config/test.json'))
os.remove(output)
def testMainArmExample(self):
output = tempfile.mktemp()
c_output = tempfile.mktemp()
cros_config_schema.Main(
None,
os.path.join(this_dir, '../libcros_config/test_arm.yaml'),
output,
filter_build_details=True,
gen_c_bindings_output=c_output)
regen_cmd = ('To regenerate the expected output, run:\n'
'\tpython -m cros_config_host.cros_config_schema '
'-f True '
'-c libcros_config/test_arm.yaml '
'-o libcros_config/test_arm.json '
'-g libcros_config/test_arm.c')
with open(output, 'r') as output_stream:
with open(os.path.join(
this_dir,
'../libcros_config/test_arm.json')) as expected_stream:
self.assertEqual(
expected_stream.read(), output_stream.read(), regen_cmd)
with open(c_output, 'r') as output_stream:
expected_file = os.path.join(this_dir, '../libcros_config/test_arm.c')
with open(expected_file) as expected_stream:
self.assertEqual(expected_stream.read(), output_stream.read(),
regen_cmd)
os.remove(output)
def testMainImportExample(self):
output = tempfile.mktemp()
cros_config_schema.Main(
None,
os.path.join(this_dir, '../libcros_config/test_import.yaml'),
output)
regen_cmd = ('To regenerate the expected output, run:\n'
'\tpython -m cros_config_host.cros_config_schema '
'-c libcros_config/test_import.yaml '
'-o libcros_config/test_import.json')
with open(output, 'r') as output_stream:
with open(os.path.join(
this_dir,
'../libcros_config/test_import.json')) as expected_stream:
self.assertEqual(
expected_stream.read(), output_stream.read(), regen_cmd)
os.remove(output)
def testMainMergeExample(self):
output = tempfile.mktemp()
base_path = os.path.join(this_dir, '../libcros_config')
cros_config_schema.Main(
None,
None,
output,
configs=[os.path.join(base_path, 'test_merge_base.yaml'),
os.path.join(base_path, 'test_merge_overlay.yaml')])
regen_cmd = ('To regenerate the expected output, run:\n'
'\tpython -m cros_config_host.cros_config_schema '
'-o libcros_config/test_merge.json '
'-m libcros_config/test_merge_base.yaml '
'libcros_config/test_merge_overlay.yaml')
with open(output, 'r') as output_stream:
with open(os.path.join(
this_dir,
'../libcros_config/test_merge.json')) as expected_stream:
self.assertEqual(
expected_stream.read(), output_stream.read(), regen_cmd)
os.remove(output)
if __name__ == '__main__':
unittest.main()