def TransformConfig(config, device_filter=None): """Transforms the source config (YAML) to the target system format (JSON) Applies consistent transforms to covert a source YAML configuration into JSON output that will be used by the tast test program. Args: config: Config that will be transformed. device_filter: Only returns configs that match the filter. Returns: Resulting JSON output from the transform. """ config_yaml = yaml.load(config) json_from_yaml = json.dumps(config_yaml, sort_keys=True, indent=2) json_config = json.loads(json_from_yaml) configs = [] if DEVICES in json_config[CHROMEOS]: for device in json_config[CHROMEOS][DEVICES]: configs.append(device) if device_filter: configs = [ config for config in configs if device_filter == config['device-name'] ] # Drop everything except for devices since they were just used as shared # config in the source yaml. json_config = { CHROMEOS: { DEVICES: configs, }, } return libcros_schema.FormatJson(json_config)
def Start(config, filter_name, output, schema): """Transforms and validates a cros config test file for use on the system Applies consistent transforms to covert a source YAML configuration into a JSON file that will be used on the system by cros_config tast tests. Verifies that the file complies with the schema verification rules and performs additional verification checks for config consistency. Args: config: Source config file that will be transformed/verified. filter_name: Device name to filter on. output: Output file that will be generated by the transform. schema: Schema file used to verify the config. """ json_transform = MergeConfig(config, filter_name) if schema is None: schema = default_test_schema with open(schema, 'r') as schema_stream: libcros_schema.ValidateConfigSchema( schema_stream.read(), libcros_schema.FormatJson(json_transform)) if output: with open(output, 'w') as output_stream: # Using print function adds proper trailing newline. print(json_transform, file=output_stream) else: print(json_transform)
def TransformConfig(config, model_filter_regex=None): """Transforms the source config (YAML) to the target system format (JSON) Applies consistent transforms to covert a source YAML configuration into JSON output that will be used on the system by cros_config. Args: config: Config that will be transformed. model_filter_regex: Only returns configs that match the filter Returns: Resulting JSON output from the transform. """ config_yaml = yaml.load(config) json_from_yaml = json.dumps(config_yaml, sort_keys=True, indent=2) json_config = json.loads(json_from_yaml) configs = [] if DEVICES in json_config[CHROMEOS]: for device in json_config[CHROMEOS][DEVICES]: template_vars = {} for product in device.get(PRODUCTS, [{}]): for sku in device[SKUS]: # Template variables scope is config, then device, then product # This allows shared configs to define defaults using anchors, which # can then be easily overridden by the product/device scope. _SetTemplateVars(sku, template_vars) _SetTemplateVars(device, template_vars) _SetTemplateVars(product, template_vars) while _HasTemplateVariables(template_vars): _ApplyTemplateVars(template_vars, template_vars) sku_clone = copy.deepcopy(sku) _ApplyTemplateVars(sku_clone, template_vars) config = sku_clone[CONFIG] _DeleteTemplateOnlyVars(config) configs.append(config) else: configs = json_config[CHROMEOS][CONFIGS] if model_filter_regex: matcher = re.compile(model_filter_regex) configs = [ config for config in configs if matcher.match(config['name']) ] # Drop everything except for configs since they were just used as shared # config in the source yaml. json_config = { CHROMEOS: { CONFIGS: configs, }, } return libcros_schema.FormatJson(json_config)
def FilterBuildElements(config): """Removes build only elements from the schema. Removes build only elements from the schema in preparation for the platform. Args: config: Config (transformed) that will be filtered """ json_config = json.loads(config) for config in json_config[CHROMEOS][CONFIGS]: _FilterBuildElements(config, '') return libcros_schema.FormatJson(json_config)
def MergeConfigs(configs): """Evaluates and merges all config files into a single configuration. Args: configs: List of source config files that will be transformed/merged. Returns: Final merged JSON result. """ json_files = [] for yaml_file in configs: yaml_with_imports = libcros_schema.ApplyImports(yaml_file) json_transformed_file = TransformConfig(yaml_with_imports) json_files.append(json.loads(json_transformed_file)) result_json = json_files[0] for overlay_json in json_files[1:]: for to_merge_config in overlay_json['chromeos']['configs']: to_merge_identity = to_merge_config.get('identity', {}) to_merge_name = to_merge_config.get('name', '') matched = False # Find all existing configs where there is a full/partial identity # match or name match and merge that config into the source. # If there are no matches, then append the config. for source_config in result_json['chromeos']['configs']: identity_match = False if to_merge_identity: source_identity = source_config['identity'] # If we are missing anything from the source identity, copy # it into to_merge_identity before doing the comparison, as # missing attributes in the to_merge_identity should be # treated as matched. to_merge_identity_extended = to_merge_identity.copy() for key, value in source_identity.items(): if key not in to_merge_identity_extended: to_merge_identity_extended[key] = value identity_match = _IdentityEq(source_identity, to_merge_identity_extended) elif to_merge_name: identity_match = to_merge_name == source_config.get('name', '') if identity_match: MergeDictionaries(source_config, to_merge_config) matched = True if not matched: result_json['chromeos']['configs'].append(to_merge_config) return libcros_schema.FormatJson(result_json)
def MergeConfigs(configs): """Evaluates and merges all config files into a single configuration. Args: configs: List of source config files that will be transformed/merged. Returns: Final merged JSON result. """ json_files = [] for yaml_file in configs: yaml_with_imports = libcros_schema.ApplyImports(yaml_file) json_transformed_file = TransformConfig(yaml_with_imports) json_files.append(json.loads(json_transformed_file)) result_json = json_files[0] for overlay_json in json_files[1:]: for to_merge_config in overlay_json['chromeos']['configs']: to_merge_identity = to_merge_config.get('identity', {}) to_merge_name = to_merge_config.get('name', '') matched = False # Find all existing configs where there is a full/partial identity # match or name match and merge that config into the source. # If there are no matches, then append the config. for source_config in result_json['chromeos']['configs']: identity_match = False if to_merge_identity: source_identity = source_config['identity'] identity_match = True for identity_key, identity_value in to_merge_identity.iteritems( ): if (identity_key not in source_identity or source_identity[identity_key] != identity_value): identity_match = False break elif to_merge_name: identity_match = to_merge_name == source_config.get( 'name', '') if identity_match: MergeDictionaries(source_config, to_merge_config) matched = True if not matched: result_json['chromeos']['configs'].append(to_merge_config) return libcros_schema.FormatJson(result_json)
def FilterBuildElements(config, build_only_elements): """Removes build only elements from the schema. Removes build only elements from the schema in preparation for the platform, and generates any runtime-only inferred elements. Args: config: Config (transformed) that will be filtered build_only_elements: List of strings of paths of fields to be filtered """ json_config = json.loads(config) json_config = _GenerateInferredElements(json_config) for device_config in json_config[CHROMEOS][CONFIGS]: _FilterBuildElements(device_config, '', build_only_elements) return libcros_schema.FormatJson(json_config)
def _GetElementToIntMap(schema_yaml, hwprop): """Returns a mapping of an enum's elements to a distinct integer. Used in the c_template to assign an integer to the stylus category type. Args: schema_yaml: Cros_config_schema in yaml format. hwprop: String representing the hardware property of the enum (ex. stylus-category) """ schema_json_from_yaml = libcros_schema.FormatJson(schema_yaml) schema_json = json.loads(schema_json_from_yaml) if hwprop not in schema_json['typeDefs']: raise ValidationError('Hardware property not found: %s' % str(hwprop)) if 'enum' not in schema_json['typeDefs'][hwprop]: raise ValidationError('Hardware property is not an enum: %s' % str(hwprop)) return dict((element, i) for (i, element) in enumerate( schema_json['typeDefs'][hwprop]['enum']))
def testROVersion(self): config = { 'chromeos': { 'configs': [ { 'identity': { 'platform-name': 'foo', 'sku-id': 1 }, 'name': 'foo', 'fingerprint': { 'board': 'dartmonkey', 'ro-version': '123' } }, ], }, } libcros_schema.ValidateConfigSchema(self._schema, libcros_schema.FormatJson(config))
def testDevices(self): config = { 'chromeos': { 'configs': [ { 'identity': { 'platform-name': 'foo', 'sku-id': 1 }, 'name': 'foo', 'camera': { 'count': 2, 'devices': [ { 'interface': 'usb', 'facing': 'front', 'orientation': 180, 'flags': { 'support-1080p': False, 'support-autofocus': False, }, 'ids': ['0123:abcd', '4567:efef'], }, { 'interface': 'mipi', 'facing': 'back', 'orientation': 0, 'flags': { 'support-1080p': True, 'support-autofocus': True, }, }, ], } }, ], }, } libcros_schema.ValidateConfigSchema(self._schema, libcros_schema.FormatJson(config))
def testInvalidUsbId(self): if version.parse(jsonschema.__version__) < version.Version('3.0.0'): self.skipTest('jsonschema needs upgrade to support conditionals') for invalid_usb_id in ('0123-abcd', '0123:Abcd', '123:abcd'): config = { 'chromeos': { 'configs': [ { 'identity': { 'platform-name': 'foo', 'sku-id': 1 }, 'name': 'foo', 'camera': { 'count': 1, 'devices': [ { 'interface': 'usb', 'facing': 'front', 'orientation': 0, 'flags': { 'support-1080p': False, 'support-autofocus': True, }, 'ids': [invalid_usb_id], }, ], } }, ], }, } with self.assertRaises(jsonschema.ValidationError) as ctx: libcros_schema.ValidateConfigSchema( self._schema, libcros_schema.FormatJson(config)) self.assertIn('%r does not match' % invalid_usb_id, str(ctx.exception))
def testROVersionMissingBoardName(self): config = { 'chromeos': { 'configs': [ { 'identity': { 'platform-name': 'foo', 'sku-id': 1 }, 'name': 'foo', 'fingerprint': { # "ro-version" only allowed if "board" is also specified. 'ro-version': '123' } }, ], }, } with self.assertRaises(jsonschema.exceptions.ValidationError) as ctx: libcros_schema.ValidateConfigSchema( self._schema, libcros_schema.FormatJson(config)) self.assertEqual(ctx.exception.message, "'board' is a dependency of 'ro-version'")