hwid: Refactors the HWID v3 framework.
This CL does:
1. Simplify the BOM class to record only `encoding_pattern_index`,
`image_id` and `components` because only these 3 items are enough
for encoding.
2. Revise the `transformer` module to match the change of `BOM`.
3. Re-write the unittest for `transformer` module so that it only test
whether that module works or not.
4. Revise the `verifier` module to match the change of `BOM`.
5. Re-write the unittest for `verifier` module so that it only test
whether that module works or not.
6. Remove unused method in `database` module.
7. Re-write the test code for `hwid_utils` module by a clear way
as now `BOM` class is much more simplier.
8. Adds unittest for `hwid_cmdline`.
BUG=chromium:689944
TEST=make test
Change-Id: I3034cb131b36df9996539b401bc7e5b394c077da
Reviewed-on: https://chromium-review.googlesource.com/861685
Commit-Ready: Yong Hong <yhong@google.com>
Tested-by: Yong Hong <yhong@google.com>
Reviewed-by: Yong Hong <yhong@google.com>
diff --git a/py/gooftool/commands.py b/py/gooftool/commands.py
index 904a34b..25de5d1 100755
--- a/py/gooftool/commands.py
+++ b/py/gooftool/commands.py
@@ -744,22 +744,19 @@
This is mainly for Gooftool to verify v3 HWID during finalize. For testing
and development purposes, please use `hwid` command.
"""
- db = GetGooftool(options).db
- encoded_string = options.hwid or GetGooftool(options).ReadHWID()
- if options.probe_results:
- probed_results = hwid_utils.GetProbedResults(infile=options.probe_results)
- else:
- probed_results = GetGooftool(options).Probe()
+ database = GetGooftool(options).db
- vpd = hwid_utils.GetVPDData(options.hwid_run_vpd, options.hwid_vpd_data_file)
+ encoded_string = options.hwid or GetGooftool(options).ReadHWID()
+
+ probed_results = hwid_utils.GetProbedResults(infile=options.probe_results)
+ vpd = hwid_utils.GetVPDData(run_vpd=options.hwid_run_vpd,
+ infile=options.hwid_vpd_data_file)
event_log.Log('probed_results', probed_results=FilterDict(probed_results))
event_log.Log('vpd', vpd=FilterDict(vpd) if vpd is None else None)
- bom = hwid_utils.GenerateBOMFromProbedResults(db, probed_results)
-
- hwid_utils.VerifyHWID(db, encoded_string, bom, vpd=vpd,
- rma_mode=options.rma_mode)
+ hwid_utils.VerifyHWID(database, encoded_string, probed_results=probed_results,
+ vpd=vpd, rma_mode=options.rma_mode)
event_log.Log('verified_hwid', hwid=encoded_string)
diff --git a/py/hwid/v3/bom.py b/py/hwid/v3/bom.py
index 19ff203..b86cee4 100644
--- a/py/hwid/v3/bom.py
+++ b/py/hwid/v3/bom.py
@@ -1,68 +1,56 @@
-# Copyright 2017 The Chromium OS Authors. All rights reserved.
+# Copyright 2018 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.
-"""BOM class for HWID v3 framework."""
+"""BOM class for the HWID v3 framework.
-import collections
-import copy
+A BOM object mainly records what exact components are installed on the
+Chromebook, while a HWID database records what components are allowed to be
+installed on the Chromebook and their corresponding index.
-import factory_common # pylint: disable=W0611
-from cros.factory.hwid.v3 import rule
-from cros.factory.utils import schema
+There are two use cases of the BOM class:
+ 1. Generate a BOM object by probing the device, and then we can encode the BOM
+ object to a HWID identity according to the HWID database.
+ 2. Decode a given HWID identity into a BOM object according to the HWID
+ database.
+The above two use cases can be written to a more simple form (in LaTeX syntax):
+ 1. identity = Encode_{database}(bom)
+ 2. bom = Decode_{database}(identity)
-# A named tuple to store the probed component name and the error if any.
-ProbedComponentResult = collections.namedtuple(
- 'ProbedComponentResult', ['component_name', 'probed_values', 'error'])
+Above two formulas are implemented in `transformer.py`.
+"""
+
+import factory_common # pylint: disable=unused-import
+from cros.factory.utils import type_utils
class BOM(object):
"""A class that holds all the information regarding a BOM.
- Attributes:
- project: A string of project name.
- encoding_pattern_index: An int indicating the encoding pattern. Currently,
- only 0 is used.
+ This class is for HWID v3 framework internal use only. It does not verify
+ anything because its properties are not given from external resources.
+
+ Properties:
+ encoding_pattern_index: An int indicating the encoding pattern.
image_id: An int indicating the image id.
- components: A dict that maps component classes to a list of
- ProbedComponentResult.
- encoded_fields: A dict that maps each encoded field to its index.
-
- Raises:
- SchemaException if invalid argument format is found.
+ components: A dict that maps component classes to a set of string of
+ component name.
"""
- _COMPONENTS_SCHEMA = schema.Dict(
- 'bom',
- key_type=schema.Scalar('component class', str),
- value_type=schema.List(
- 'list of ProbedComponentResult',
- schema.Tuple('ProbedComponentResult',
- [schema.Optional(schema.Scalar('component name', str)),
- schema.Optional(schema.Dict(
- 'probed_values',
- key_type=schema.Scalar('key', str),
- value_type=schema.AnyOf([
- schema.Scalar('value', str),
- schema.Scalar('value', rule.Value)]))),
- schema.Optional(schema.Scalar('error', str))])))
- def __init__(self, project, encoding_pattern_index, image_id,
- components, encoded_fields):
- self.project = project
+ def __init__(self, encoding_pattern_index, image_id, components):
self.encoding_pattern_index = encoding_pattern_index
self.image_id = image_id
- self.components = components
- self.encoded_fields = encoded_fields
- BOM._COMPONENTS_SCHEMA.Validate(self.components)
+ self.components = {}
- def Duplicate(self):
- """Duplicates this BOM object.
+ for comp_cls, comp_names in components.iteritems():
+ self.SetComponent(comp_cls, comp_names)
- Returns:
- A deepcopy of the original BOM object.
- """
- return copy.deepcopy(self)
+ def SetComponent(self, comp_cls, comp_names):
+ self.components[comp_cls] = sorted(type_utils.MakeList(comp_names))
+
+ def RemoveComponent(self, comp_cls):
+ del self.components[comp_cls]
def __eq__(self, op2):
if not isinstance(op2, BOM):
@@ -71,3 +59,7 @@
def __ne__(self, op2):
return not self.__eq__(op2)
+
+ def __repr__(self):
+ return 'BOM(encoding_pattern_index=%r, image_id=%r, components=%r)' % (
+ self.encoding_pattern_index, self.image_id, self.components)
diff --git a/py/hwid/v3/database.py b/py/hwid/v3/database.py
index 9a29f39..41d50f3 100644
--- a/py/hwid/v3/database.py
+++ b/py/hwid/v3/database.py
@@ -13,7 +13,6 @@
import re
import factory_common # pylint: disable=W0611
-from cros.factory.hwid.v3.bom import ProbedComponentResult
from cros.factory.hwid.v3 import common
from cros.factory.hwid.v3 import rule
# Import yaml_tags to decode special YAML tags specific to HWID module.
@@ -239,82 +238,6 @@
Rules(db_yaml['rules']),
db_yaml.get('checksum'))
- def UpdateComponentsOfBOM(self, bom, updated_components):
- """Updates the components data of the given BOM.
-
- The components field of the given BOM is updated with the given component
- class and component name, and the encoded_fields field is re-calculated.
-
- Args:
- bom: A BOM object to update.
- updated_components: A dict of component classes to component names
- indicating the set of components to update.
- """
- for comp_cls, comp_name in updated_components.iteritems():
- new_probed_result = []
- if comp_name is None:
- new_probed_result.append(ProbedComponentResult(
- None, None, common.MISSING_COMPONENT_ERROR(comp_cls)))
- else:
- comp_name = type_utils.MakeList(comp_name)
- for name in comp_name:
- comp_attrs = self.components.GetComponentAttributes(comp_cls, name)
- new_probed_result.append(ProbedComponentResult(
- name, comp_attrs['values'], None))
- # Update components data of the duplicated BOM.
- bom.components[comp_cls] = new_probed_result
-
- # Re-calculate all the encoded index of each encoded field.
- bom.encoded_fields = {}
- for field in self.encoded_fields:
- bom.encoded_fields[field] = self.GetFieldIndexFromProbedComponents(
- field, bom.components)
-
- def GetFieldIndexFromProbedComponents(self, encoded_field, probed_components):
- """Gets the encoded index of the specified encoded field by matching
- the given probed components against the definitions in the database.
-
- Args:
- encoded_field: A string indicating the encoding field of interest.
- probed_components: A dict that maps a set of component classes to their
- list of ProbedComponentResult.
-
- Returns:
- An int indicating the encoded index, or None if no matching encoded
- index is found.
- """
- if encoded_field not in self.encoded_fields:
- return None
-
- for index, db_comp_cls_names in (
- self.encoded_fields[encoded_field].iteritems()):
- # Iterate through all indices in the encoded_fields of the database.
- found = True
- for db_comp_cls, db_comp_names in db_comp_cls_names.iteritems():
- # Check if every component class and value the index consists of
- # matches.
- if db_comp_names is None:
- # Special handling for NULL component.
- if (probed_components[db_comp_cls] and
- probed_components[db_comp_cls][0].probed_values is not None):
- found = False
- break
- else:
- # Create a sorted list of component names of db_comp_cls from the
- # probed_components argument.
- bom_component_names_of_the_class = sorted([
- x.component_name for x in probed_components[db_comp_cls]])
- # Create a sorted list of component names of db_comp_cls from the
- # database.
- db_component_names_of_the_class = sorted(db_comp_names)
- if (bom_component_names_of_the_class !=
- db_component_names_of_the_class):
- found = False
- break
- if found:
- return index
- return None
-
def _GetAllIndices(self, encoded_field):
"""Gets a list of all the encoded indices of the given encoded_field in the
database.
@@ -345,12 +268,13 @@
return None
if index not in self.encoded_fields[encoded_field]:
return None
- result = collections.defaultdict(list)
+ result = {}
for comp_cls, comp_names in (
self.encoded_fields[encoded_field][index].iteritems()):
if comp_names is None:
result[comp_cls] = None
else:
+ result[comp_cls] = []
for name in comp_names:
# Add an additional index 'name' to record component name
new_attr = self.components.GetComponentAttributes(comp_cls, name)
@@ -443,12 +367,14 @@
schema.Scalar('component name', str))]))))
self.schema.Validate(encoded_fields_dict)
super(EncodedFields, self).__init__(encoded_fields_dict)
- # Convert string to list of string.
+ # Convert string to list of string and None to empty list.
for field in self:
for index in self[field]:
for comp_cls in self[field][index]:
comp_value = self[field][index][comp_cls]
- if isinstance(comp_value, str):
+ if comp_value is None:
+ self[field][index][comp_cls] = []
+ elif isinstance(comp_value, str):
self[field][index][comp_cls] = type_utils.MakeList(comp_value)
@@ -460,27 +386,14 @@
components_dict: A dict of components of the form:
{
'component_class_1': { # Probeable component class.
- 'probeable': True,
'items': {
'component_name_1': {
'values': { probed values dict },
- 'labels': [ labels ],
'status': status
},
...
}
- },
- 'component_class_2': { # Unprobeable component class.
- 'probeable': False,
- 'items': {
- 'component_name_2': {
- 'values': None,
- 'labels': [ labels ],
- 'status': status
- },
- ...
- }
- },
+ }
...
}
@@ -532,13 +445,6 @@
'Component %s has more than one default items: %r' %
(comp_cls, default_items))
- # Classify components based on their attributes.
- self.probeable = set()
- for comp_cls, comp_cls_properties in components_dict.iteritems():
- if comp_cls_properties.get('probeable', True):
- # Default 'probeable' to True.
- self.probeable.add(comp_cls)
-
for comp_cls_data in components_dict.itervalues():
for comp_cls_item_attrs in comp_cls_data['items'].itervalues():
# Sanity check for component status.
@@ -660,9 +566,7 @@
if match:
results[comp_name] = copy.deepcopy(comp_attrs)
- if results:
- return results
- return None
+ return results
def CheckComponent(self, comp_cls, comp_name):
"""Checks if the given component class and component name are valid.
diff --git a/py/hwid/v3/database_unittest.py b/py/hwid/v3/database_unittest.py
index 8a1f7e6..b55c97c 100755
--- a/py/hwid/v3/database_unittest.py
+++ b/py/hwid/v3/database_unittest.py
@@ -12,103 +12,21 @@
import unittest
import factory_common # pylint: disable=W0611
-from cros.factory.hwid.v3 import common
from cros.factory.hwid.v3.common import HWIDException
from cros.factory.hwid.v3.database import Components
from cros.factory.hwid.v3.database import Database
-from cros.factory.hwid.v3 import hwid_utils
from cros.factory.hwid.v3.rule import Value
from cros.factory.hwid.v3 import yaml_wrapper as yaml
-from cros.factory.utils import json_utils
+
_TEST_DATA_PATH = os.path.join(os.path.dirname(__file__), 'testdata')
-class NewDatabaseTest(unittest.TestCase):
- """Test the new style of HWID database.
-
- - Split key_root and key_recovery to a new component: firmware_keys
- - Separate firmware field to let each field only has one component
- - Add default argument in audio_codec and cellular
- """
-
- def setUp(self):
- self.database = Database.LoadFile(os.path.join(_TEST_DATA_PATH,
- 'test_new_db.yaml'))
- self.results = json_utils.LoadFile(
- os.path.join(_TEST_DATA_PATH, 'test_probe_result.json'))
- self.boms = [hwid_utils.GenerateBOMFromProbedResults(self.database,
- probed_results)
- for probed_results in self.results]
-
- def testProbeResultToBOM(self):
- bom = self.boms[0]
- self.assertEquals('CHROMEBOOK', bom.project)
- self.assertEquals(0, bom.encoding_pattern_index)
- self.assertEquals(0, bom.image_id)
- self.assertEquals(bom.components['firmware_keys'],
- [('firmware_keys_0',
- {'key_recovery': Value('kv3#key_recovery_0'),
- 'key_root': Value('kv3#key_root_0')},
- None)])
- self.assertEquals(bom.components['ro_ec_firmware'],
- [('ro_ec_firmware_0',
- {'compact_str': Value('ev2#ro_ec_firmware_0')},
- None)])
- self.assertEquals(bom.components['ro_main_firmware'],
- [('ro_main_firmware_0',
- {'compact_str': Value('mv2#ro_main_firmware_0')},
- None)])
- self.assertEquals(bom.encoded_fields['ro_main_firmware_field'], 0)
- self.assertEquals(bom.encoded_fields['firmware_keys_field'], 0)
- self.assertEquals(bom.encoded_fields['ro_ec_firmware_field'], 0)
-
- def testProbeResultToBOMWithDefault(self):
- """Test the behaviour of the default argument.
-
- In this database, audio_codec has only one default item, and cellular has a
- deprecated default item.
- """
- # No audio_codec and no cellular
- bom = self.boms[6]
- self.assertEquals(bom.components['audio_codec'],
- [('audio_codec_default', None, None)])
- self.assertEquals(bom.components['cellular'],
- [(None, None,
- common.MISSING_COMPONENT_ERROR('cellular'))])
- self.assertEquals(bom.encoded_fields['audio_codec_field'], 0)
- self.assertEquals(bom.encoded_fields['cellular_field'], 1)
-
- # There is audio_codec and cellular_a
- bom = self.boms[7]
- self.assertEquals(bom.components['audio_codec'],
- [('audio_codec_default', None, None)])
- self.assertEquals(bom.components['cellular'],
- [('cellular_a',
- {'compact_str': Value('cellular_a')},
- None)])
- self.assertEquals(bom.encoded_fields['audio_codec_field'], 0)
- self.assertEquals(bom.encoded_fields['cellular_field'], 2)
-
- # Invalid cellular probed result
- bom = self.boms[8]
- probed_value = {'compact_str': 'invalid_value'}
- self.assertEquals(bom.components['cellular'],
- [(None, probed_value,
- common.INVALID_COMPONENT_ERROR('cellular',
- probed_value))])
- self.assertEquals(bom.encoded_fields['cellular_field'], None)
-
class DatabaseTest(unittest.TestCase):
def setUp(self):
self.database = Database.LoadFile(os.path.join(_TEST_DATA_PATH,
'test_db.yaml'))
- self.results = json_utils.LoadFile(
- os.path.join(_TEST_DATA_PATH, 'test_probe_result.json'))
- self.boms = [hwid_utils.GenerateBOMFromProbedResults(self.database,
- probed_results)
- for probed_results in self.results]
def testLoadFile(self):
self.assertIsInstance(Database.LoadFile(os.path.join(
@@ -193,56 +111,6 @@
r'its bit length is 1 in the pattern',
mock_db._SanityChecks)
- def testUpdateComponentsOfBOM(self):
- bom = self.boms[0]
- new_bom = bom.Duplicate()
- self.database.UpdateComponentsOfBOM(new_bom, {'keyboard': 'keyboard_gb'})
- self.assertEquals([('keyboard_gb', None, None)],
- new_bom.components['keyboard'])
- self.assertEquals(1, new_bom.encoded_fields['keyboard'])
- new_bom = bom.Duplicate()
- self.database.UpdateComponentsOfBOM(
- new_bom, {'audio_codec': ['codec_0', 'hdmi_0']})
- self.assertEquals(
- [('codec_0', {'compact_str': Value('Codec 0')}, None),
- ('hdmi_0', {'compact_str': Value('HDMI 0')}, None)],
- new_bom.components['audio_codec'])
- self.assertEquals(0, new_bom.encoded_fields['audio_codec'])
- new_bom = bom.Duplicate()
- self.database.UpdateComponentsOfBOM(new_bom, {'cellular': 'cellular_0'})
- self.assertEquals([('cellular_0',
- {'idVendor': Value('89ab'), 'idProduct': Value('abcd'),
- 'name': Value('Cellular Card')},
- None)],
- new_bom.components['cellular'])
- self.assertEquals(1, new_bom.encoded_fields['cellular'])
- new_bom = bom.Duplicate()
- self.database.UpdateComponentsOfBOM(new_bom, {'cellular': None})
- self.assertEquals([(None, None, "Missing 'cellular' component")],
- new_bom.components['cellular'])
- self.assertEquals(0, new_bom.encoded_fields['cellular'])
- self.assertRaisesRegexp(
- HWIDException,
- r"Invalid component class 'foo'",
- self.database.UpdateComponentsOfBOM, bom, {'foo': 'bar'})
- self.assertRaisesRegexp(
- HWIDException,
- r"Invalid component name 'bar' of class 'cpu'",
- self.database.UpdateComponentsOfBOM, bom, {'cpu': 'bar'})
-
- def testGetFieldIndexFromComponents(self):
- bom = self.boms[0]
- self.assertEquals(5, self.database.GetFieldIndexFromProbedComponents(
- 'cpu', bom.components))
- self.assertEquals(1, self.database.GetFieldIndexFromProbedComponents(
- 'audio_codec', bom.components))
- self.assertEquals(3, self.database.GetFieldIndexFromProbedComponents(
- 'battery', bom.components))
- self.assertEquals(0, self.database.GetFieldIndexFromProbedComponents(
- 'storage', bom.components))
- self.assertEquals(0, self.database.GetFieldIndexFromProbedComponents(
- 'cellular', bom.components))
-
def testGetAllIndices(self):
self.assertEquals([0, 1, 2, 3, 4, 5], self.database._GetAllIndices('cpu'))
self.assertEquals([0, 1], self.database._GetAllIndices('dram'))
@@ -282,14 +150,14 @@
{'name': 'codec_0', 'values': {'compact_str': Value('Codec 0')}},
{'name': 'hdmi_0', 'values': {'compact_str': Value('HDMI 0')}}]},
self.database._GetAttributesByIndex('audio_codec', 0))
- self.assertEquals({'cellular': None},
+ self.assertEquals({'cellular': []},
self.database._GetAttributesByIndex('cellular', 0))
def testLoadDatabaseWithRegionInfo(self):
db = Database.LoadFile(
os.path.join(_TEST_DATA_PATH, 'test_db_regions.yaml'))
# Make sure that region fields are generated correctly.
- self.assertEquals({'region': None}, db.encoded_fields['region_field'][0])
+ self.assertEquals({'region': []}, db.encoded_fields['region_field'][0])
self.assertEquals({'region': ['us']}, db.encoded_fields['region_field'][29])
# Make sure that region components are generated correctly.
self.assertEquals(
@@ -463,7 +331,7 @@
{'comp_2': {'values': None}},
self.components.MatchComponentsFromValues('comp_cls_2', None))
self.assertEquals(
- None,
+ {},
self.components.MatchComponentsFromValues('comp_cls_1',
{'field1': 'foo'}))
diff --git a/py/hwid/v3/hwid_cmdline.py b/py/hwid/v3/hwid_cmdline.py
index a61e2b8..4d3ede5 100755
--- a/py/hwid/v3/hwid_cmdline.py
+++ b/py/hwid/v3/hwid_cmdline.py
@@ -6,26 +6,22 @@
"""Command-line interface for HWID v3 utilities."""
-import json
import logging
import os
import shutil
import sys
-import factory_common # pylint: disable=W0611
+import factory_common # pylint: disable=unused-import
from cros.factory.hwid.v3 import database
from cros.factory.hwid.v3 import hwid_utils
from cros.factory.hwid.v3 import yaml_wrapper as yaml
-try:
- from cros.factory.test import device_data
- _HAS_DEVICE_DATA = True
-except ImportError:
- _HAS_DEVICE_DATA = False
from cros.factory.test.rules import phase
from cros.factory.utils.argparse_utils import CmdArg
from cros.factory.utils.argparse_utils import Command
from cros.factory.utils.argparse_utils import ParseCmdline
+from cros.factory.utils import json_utils
from cros.factory.utils import sys_utils
+from cros.factory.utils import type_utils
from cros.factory.utils import yaml_utils
from cros.factory.utils import process_utils
@@ -45,6 +41,11 @@
'as returned by the "factory phase" command)')),
]
+_OUTPUT_FORMAT_COMMON_ARGS = [
+ CmdArg('--json-output', default=False, action='store_true',
+ help='Whether to dump result in JSON format.'),
+]
+
_DATABASE_BUILDER_COMMON_ARGS = [
CmdArg('--add-default-component', default=None,
nargs='+', metavar='COMP', dest='add_default_comp',
@@ -61,7 +62,17 @@
help='Supported chassis identifiers')
]
-_VPD_COMMON_ARGS = [
+_DEVICE_DATA_COMMON_ARGS = [
+ CmdArg('--probed-results-file', default=None,
+ help=('A file with probed results.\n'
+ '(Required if not running on a DUT.)')),
+ CmdArg('--device-info-file', default=None,
+ help=('A file with device info.\n'
+ '(Required if not running on a DUT.)\n'
+ 'example content of this file:\n'
+ ' component.antenna: ACN\n'
+ ' component.has_cellular: True\n'
+ ' component.keyboard: US_API\n')),
CmdArg('--run-vpd', action='store_true',
help=('Obtain the vpd data from the device before generating '
'the HWID string. Also see --vpd-data-file for more '
@@ -74,6 +85,11 @@
'specified.')),
]
+_RMA_COMMON_ARGS = [
+ CmdArg('--rma-mode', default=False, action='store_true',
+ help='Whether to enable RMA mode.'),
+]
+
class Arg(object):
"""A simple class to store arguments passed to the add_argument method of
@@ -85,10 +101,21 @@
self.kwargs = kwargs
-def _GetHWIDString():
+def GetHWIDString():
return process_utils.CheckOutput(['gooftool', 'read_hwid']).strip()
+def Output(msg):
+ print msg
+
+
+def OutputObject(options, obj):
+ if options.json_output:
+ Output(json_utils.DumpStr(obj, pretty=True))
+ else:
+ Output(yaml.safe_dump(obj, default_flow_style=False))
+
+
@Command(
'build-database',
CmdArg('--probed-results-file', default=None, required=True,
@@ -153,99 +180,102 @@
logging.info('Output the updated database to %s.', database_path)
-@Command(
- 'generate',
- CmdArg('--probed-results-file', default=None,
- help=('A file with probed results.\n'
- '(Required if not running on a DUT)')),
- CmdArg('--device-info-file', default=None,
- help=('A file with device info.\n'
- '(Required if not running on a DUT.)\n'
- 'example content of this file:\n'
- ' component.antenna: ACN\n'
- ' component.has_cellular: True\n'
- ' component.keyboard: US_API\n')),
- CmdArg('--rma-mode', default=False, action='store_true',
- help='Whether to enable RMA mode.'),
- CmdArg('--json-output', default=False, action='store_true',
- help='Whether to dump result in JSON format.'),
- *_VPD_COMMON_ARGS)
-def GenerateHWIDWrapper(options):
- """Generates HWID."""
+def ObtainAllDeviceData(options):
+ """Gets all device data needed by the HWID framework according to options.
+
+ Args:
+ options: The given options.
+
+ Returns:
+ An instance of `type_utils.Obj` with attributes:
+ probed_results: An object records the probed results.
+ device_info: An object records the device info.
+ vpd: An object records the vpd data.
+
+ Raises:
+ ValueError if the given options is invalid.
+ """
+ if sys_utils.InChroot():
+ if not options.device_info_file:
+ raise ValueError('Please specify device info with an input file when '
+ 'running in chroot.')
+
+ if not options.probed_results_file:
+ raise ValueError('Please specify probed results with an input file when '
+ 'running in chroot.')
+
+ if options.run_vpd:
+ raise ValueError('Cannot run `vpd` tool in chroot.')
+
if options.run_vpd and options.vpd_data_file:
raise ValueError('The arguments --run-vpd and --vpd-data-file cannot be '
'set at the same time')
- bom = hwid_utils.GenerateBOMFromProbedResults(
- options.database, hwid_utils.GetProbedResults(
- infile=options.probed_results_file))
+ device_data = type_utils.Obj(
+ probed_results=hwid_utils.GetProbedResults(
+ infile=options.probed_results_file),
+ device_info=hwid_utils.GetDeviceInfo(infile=options.device_info_file),
+ vpd=hwid_utils.GetVPDData(run_vpd=options.run_vpd,
+ infile=options.vpd_data_file))
- # Select right device info (from file or shopfloor).
- if options.device_info_file:
- device_info = hwid_utils.GetDeviceInfo(options.device_info_file)
- elif sys_utils.InChroot():
- raise ValueError('Cannot get device info from shopfloor in chroot. '
- 'Please specify device info with an input file. If you '
- 'are running with command-line, use --device-info-file')
- elif not _HAS_DEVICE_DATA:
- raise ValueError('The device_data module is not available.')
- else:
- device_info = device_data.GetAllDeviceData()
+ logging.debug(yaml.dump(device_data.__dict__, default_flow_style=False))
- vpd = hwid_utils.GetVPDData(options.run_vpd, options.vpd_data_file)
+ return device_data
- verbose_output = {
- 'device_info': device_info,
- 'bom': bom,
- 'vpd': vpd
- }
- logging.debug(yaml.dump(verbose_output, default_flow_style=False))
- identity = hwid_utils.GenerateHWID(options.database, bom, device_info,
- rma_mode=options.rma_mode, vpd=vpd)
- if options.json_output:
- print json.dumps({
- 'encoded_string': identity.encoded_string,
- 'binary_string': identity.binary_string,
- 'hwdb_checksum': options.database.checksum})
- else:
- print 'Encoded HWID string: %s' % identity.encoded_string
- print 'Binary HWID string: %s' % identity.binary_string
+
+@Command(
+ 'generate',
+ *(_OUTPUT_FORMAT_COMMON_ARGS + _DEVICE_DATA_COMMON_ARGS + _RMA_COMMON_ARGS))
+def GenerateHWIDWrapper(options):
+ """Generates HWID."""
+ device_data = ObtainAllDeviceData(options)
+
+ identity = hwid_utils.GenerateHWID(
+ options.database, probed_results=device_data.probed_results,
+ device_info=device_data.device_info, vpd=device_data.vpd,
+ rma_mode=options.rma_mode)
+
+ OutputObject(options, {'encoded_string': identity.encoded_string,
+ 'binary_string': identity.binary_string,
+ 'database_checksum': options.database.checksum})
@Command(
'decode',
CmdArg('hwid', nargs='?', default=None,
- help='the HWID to decode.\n(required if not running on a DUT)'))
+ help='the HWID to decode.\n(required if not running on a DUT)'),
+ *_OUTPUT_FORMAT_COMMON_ARGS)
def DecodeHWIDWrapper(options):
"""Decodes HWID."""
- encoded_string = options.hwid if options.hwid else _GetHWIDString()
+ encoded_string = options.hwid if options.hwid else GetHWIDString()
identity, bom = hwid_utils.DecodeHWID(options.database, encoded_string)
- print yaml.dump(hwid_utils.ParseDecodedHWID(options.database, bom, identity),
- default_flow_style=False)
+
+ OutputObject(options,
+ {'project': identity.project,
+ 'binary_string': identity.binary_string,
+ 'image_id': bom.image_id,
+ 'components': bom.components})
@Command(
'verify',
- CmdArg('hwid', nargs='?', default=None,
+ CmdArg('hwid', default=None,
help='the HWID to verify.\n(required if not running on a DUT)'),
- CmdArg('--probed-results-file', default=None,
- help=('a file with probed results.\n'
- '(required if not running on a DUT)')),
- CmdArg('--rma-mode', default=False, action='store_true',
- help='whether to enable RMA mode.'),
- *_VPD_COMMON_ARGS)
+ *(_DEVICE_DATA_COMMON_ARGS + _RMA_COMMON_ARGS))
def VerifyHWIDWrapper(options):
"""Verifies HWID."""
- encoded_string = options.hwid if options.hwid else _GetHWIDString()
- probed_bom = hwid_utils.GenerateBOMFromProbedResults(
- options.database, hwid_utils.GetProbedResults(
- infile=options.probed_results_file))
- vpd = hwid_utils.GetVPDData(options.run_vpd, options.vpd_data_file)
- hwid_utils.VerifyHWID(options.database, encoded_string, probed_bom,
- vpd=vpd, rma_mode=options.rma_mode,
- current_phase=options.phase)
+ encoded_string = options.hwid if options.hwid else GetHWIDString()
+
+ device_data = ObtainAllDeviceData(options)
+
+ hwid_utils.VerifyHWID(
+ options.database, encoded_string,
+ probed_results=device_data.probed_results,
+ device_info=device_data.device_info, vpd=device_data.vpd,
+ rma_mode=options.rma_mode, current_phase=options.phase)
+
# No exception raised. Verification was successful.
- print 'Verification passed.'
+ Output('Verification passed.')
@Command(
@@ -257,7 +287,7 @@
raise ValueError('Cannot write HWID to GBB in chroot.')
process_utils.CheckOutput(['gooftool', 'write_hwid', options.hwid])
- print 'HWID %r written to firmware GBB.' % options.hwid
+ Output('HWID %r written to firmware GBB.' % options.hwid)
@Command('read')
@@ -266,18 +296,19 @@
if sys_utils.InChroot():
raise ValueError('Cannot read HWID from GBB in chroot.')
- print _GetHWIDString()
+ Output(GetHWIDString())
@Command(
'list-components',
CmdArg('comp_class', nargs='*', default=None,
- help='the component classes to look up'))
+ help='the component classes to look up'),
+ *_OUTPUT_FORMAT_COMMON_ARGS)
def ListComponentsWrapper(options):
"""Lists components of the given class."""
components_list = hwid_utils.ListComponents(options.database,
options.comp_class)
- print yaml.safe_dump(components_list, default_flow_style=False)
+ OutputObject(options, components_list)
@Command(
@@ -286,16 +317,36 @@
help='the image ID to enumerate.'),
CmdArg('-s', '--status', default='supported',
choices=['supported', 'released', 'all'],
- help='the status of components to enumerate'))
+ help='the status of components to enumerate'),
+ CmdArg('--comp', nargs='*', default=None,
+ help=('Specify some of the component to limit the output. '
+ 'The format of COMP is '
+ '"<comp_cls>=<comp_name>[,<comp_name>[,<comp_name>...]]"')),
+ CmdArg('--no-bom', action='store_true',
+ help='Print the encoded string only.'))
def EnumerateHWIDWrapper(options):
"""Enumerates possible HWIDs."""
+ comps = {}
+ if options.comp:
+ for comp in options.comp:
+ if '=' not in comp:
+ raise ValueError('The format of the --comp argument is incorrect.')
+ comp_cls, _, comp_names = comp.partition('=')
+ comps[comp_cls] = comp_names.split(',')
+
# Enumerating may take a very long time so we want to verbosely make logs.
logging.debug('Enumerating all HWIDs...')
- hwids = hwid_utils.EnumerateHWID(options.database, options.image_id,
- options.status)
+ hwids = hwid_utils.EnumerateHWID(
+ options.database, image_id=options.image_id, status=options.status,
+ comps=comps)
+
logging.debug('Printing %d sorted HWIDs...', len(hwids))
- for k, v in sorted(hwids.iteritems()):
- print '%s: %s' % (k, v)
+ if options.no_bom:
+ for k in sorted(hwids.iterkeys()):
+ Output(k)
+ else:
+ for k, v in sorted(hwids.iteritems()):
+ Output('%s: %s' % (k, v))
@Command('verify-database')
@@ -303,7 +354,7 @@
"""Verifies the given HWID database."""
# Do nothing here since all the verifications are done when loading the
# database with HWID library.
- print 'Database %s verified' % options.project
+ Output('Database %s verified' % options.project)
def ParseOptions(args=None):
@@ -317,7 +368,7 @@
options.hwid_db_path = hwid_utils.GetDefaultDataPath()
if options.project is None:
if sys_utils.InChroot():
- print 'Argument -j/--project is required'
+ Output('Argument -j/--project is required')
sys.exit(1)
options.project = hwid_utils.ProbeProject()
diff --git a/py/hwid/v3/hwid_cmdline_unittest.py b/py/hwid/v3/hwid_cmdline_unittest.py
new file mode 100755
index 0000000..e9ca1e1
--- /dev/null
+++ b/py/hwid/v3/hwid_cmdline_unittest.py
@@ -0,0 +1,195 @@
+#!/usr/bin/env python
+# Copyright 2018 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.
+
+import json
+import mock
+import unittest
+
+import yaml
+
+import factory_common # pylint: disable=unused-import
+from cros.factory.hwid.v3.common import HWIDException
+from cros.factory.hwid.v3 import hwid_cmdline
+from cros.factory.utils import type_utils
+
+
+class FakeOutput(object):
+ def __init__(self):
+ self.data = ''
+
+ def __call__(self, msg):
+ self.data += msg + '\n'
+
+
+class TestCaseBaseWithFakeOutput(unittest.TestCase):
+ def setUp(self):
+ self.orig_output = hwid_cmdline.Output
+ hwid_cmdline.Output = FakeOutput()
+
+ def tearDown(self):
+ hwid_cmdline.Output = self.orig_output
+
+
+class OutputObjectTest(TestCaseBaseWithFakeOutput):
+ def testYamlFormat(self):
+ hwid_cmdline.OutputObject(mock.MagicMock(json_output=False),
+ {'aaa': ['bbb', 'ccc'], 'xxx': 3})
+ self.assertEquals(yaml.load(hwid_cmdline.Output.data),
+ {'aaa': ['bbb', 'ccc'], 'xxx': 3})
+
+ def testJsonFormat(self):
+ hwid_cmdline.OutputObject(mock.MagicMock(json_output=True),
+ {'aaa': ['bbb', 'ccc'], 'xxx': 3})
+ self.assertEquals(json.loads(hwid_cmdline.Output.data),
+ {'aaa': ['bbb', 'ccc'], 'xxx': 3})
+
+
+class TestCaseBaseWithMockedOutputObject(unittest.TestCase):
+ def setUp(self):
+ self.orig_output_object = hwid_cmdline.OutputObject
+ hwid_cmdline.OutputObject = mock.MagicMock()
+
+ def tearDown(self):
+ hwid_cmdline.OutputObject = self.orig_output_object
+
+
+class BuildDatabaseWrapperTest(unittest.TestCase):
+ @mock.patch('cros.factory.hwid.v3.hwid_utils.BuildDatabase')
+ def testNormal(self, build_database_mock):
+ pass
+
+
+class UpdateDatabaseWrapperTest(unittest.TestCase):
+ @mock.patch('cros.factory.hwid.v3.hwid_utils.BuildDatabase')
+ def testNormal(self, build_database_mock):
+ pass
+
+
+class ObtainAllDeviceDataTest(unittest.TestCase):
+ @mock.patch('cros.factory.hwid.v3.yaml_wrapper.dump')
+ @mock.patch('cros.factory.hwid.v3.hwid_utils.GetVPDData')
+ @mock.patch('cros.factory.hwid.v3.hwid_utils.GetDeviceInfo')
+ @mock.patch('cros.factory.hwid.v3.hwid_utils.GetProbedResults')
+ def testNormal(self, get_probed_results_mock, get_device_info_mock,
+ get_vpd_data_mock, unused_dump_mock):
+ options = mock.MagicMock(run_vpd=False)
+ ret = hwid_cmdline.ObtainAllDeviceData(options)
+
+ # The function should call below functions to get the proper device data.
+ get_probed_results_mock.assert_called_once_with(
+ infile=options.probed_results_file)
+ get_vpd_data_mock.assert_called_once_with(
+ run_vpd=options.run_vpd, infile=options.vpd_data_file)
+ get_device_info_mock.assert_called_once_with(
+ infile=options.device_info_file)
+
+ self.assertEquals(type_utils.Obj(probed_results=ret.probed_results,
+ vpd=ret.vpd,
+ device_info=ret.device_info),
+ ret)
+
+
+class GenerateHWIDWrapperTest(TestCaseBaseWithMockedOutputObject):
+ @mock.patch('cros.factory.hwid.v3.hwid_utils.GenerateHWID')
+ @mock.patch('cros.factory.hwid.v3.hwid_cmdline.ObtainAllDeviceData')
+ def testNormal(self, obtain_all_device_data_mock, generate_hwid_mock):
+ options = mock.MagicMock()
+ hwid_cmdline.GenerateHWIDWrapper(options)
+
+ device_data = obtain_all_device_data_mock.return_value
+ generate_hwid_mock.assert_called_once_with(
+ options.database, probed_results=device_data.probed_results,
+ device_info=device_data.device_info, vpd=device_data.vpd,
+ rma_mode=options.rma_mode)
+
+ identity = generate_hwid_mock.return_value
+ hwid_cmdline.OutputObject.assert_called_once_with(
+ options,
+ {'encoded_string': identity.encoded_string,
+ 'binary_string': identity.binary_string,
+ 'database_checksum': options.database.checksum})
+
+
+class DecodeHWIDWrapperTest(TestCaseBaseWithMockedOutputObject):
+ @mock.patch('cros.factory.hwid.v3.hwid_utils.DecodeHWID',
+ return_value=(mock.MagicMock(), mock.MagicMock()))
+ def testNormal(self, decode_hwid_mock):
+ options = mock.MagicMock()
+ hwid_cmdline.DecodeHWIDWrapper(options)
+
+ decode_hwid_mock.assert_called_once_with(options.database, options.hwid)
+ identity, bom = decode_hwid_mock.return_value
+
+ hwid_cmdline.OutputObject.assert_called_once_with(
+ options,
+ {'project': identity.project,
+ 'binary_string': identity.binary_string,
+ 'image_id': bom.image_id,
+ 'components': bom.components})
+
+
+class VerifyHWIDWrapperTest(TestCaseBaseWithFakeOutput):
+ @mock.patch('cros.factory.hwid.v3.hwid_utils.VerifyHWID')
+ @mock.patch('cros.factory.hwid.v3.hwid_cmdline.ObtainAllDeviceData')
+ def testNormal(self, obtain_all_device_data_mock, verify_hwid_mock):
+ options = mock.MagicMock()
+ hwid_cmdline.VerifyHWIDWrapper(options)
+
+ device_data = obtain_all_device_data_mock.return_value
+
+ verify_hwid_mock.assert_called_once_with(
+ options.database, options.hwid,
+ probed_results=device_data.probed_results,
+ device_info=device_data.device_info, vpd=device_data.vpd,
+ rma_mode=options.rma_mode, current_phase=options.phase)
+
+ @mock.patch('cros.factory.hwid.v3.hwid_utils.VerifyHWID',
+ side_effect=HWIDException('verify fail'))
+ @mock.patch('cros.factory.hwid.v3.hwid_cmdline.ObtainAllDeviceData')
+ def testVerifyFailed(
+ self, unused_obtain_all_device_data_mock, unused_verify_hwid_mock):
+ self.assertRaises(HWIDException, hwid_cmdline.VerifyHWIDWrapper,
+ mock.MagicMock())
+
+
+class ListComponentsWrapperTest(TestCaseBaseWithMockedOutputObject):
+ @mock.patch('cros.factory.hwid.v3.hwid_utils.ListComponents')
+ def testNormal(self, list_components_mock):
+ options = mock.MagicMock()
+ hwid_cmdline.ListComponentsWrapper(options)
+
+ list_components_mock.assert_called_once_with(options.database,
+ options.comp_class)
+ hwid_cmdline.OutputObject.assert_called_once_with(
+ options, list_components_mock.return_value)
+
+
+class EnumerateHWIDWrapperTest(TestCaseBaseWithFakeOutput):
+ @mock.patch('cros.factory.hwid.v3.hwid_utils.EnumerateHWID',
+ return_value={'HWID1': 'bbb', 'HWID2': 'aaa'})
+ def testDefault(self, unused_enumerate_hwid_mock):
+ hwid_cmdline.EnumerateHWIDWrapper(mock.MagicMock(comp=None, no_bom=False))
+
+ self.assertEquals(hwid_cmdline.Output.data, 'HWID1: bbb\nHWID2: aaa\n')
+
+ @mock.patch('cros.factory.hwid.v3.hwid_utils.EnumerateHWID', return_value={})
+ def testComp(self, enumerate_hwid_mock):
+ options = mock.MagicMock(comp=['aaa=bbb', 'ccc=ddd,eee'])
+ hwid_cmdline.EnumerateHWIDWrapper(options)
+
+ enumerate_hwid_mock.assert_called_once_with(
+ options.database, image_id=options.image_id, status=options.status,
+ comps={'aaa': ['bbb'], 'ccc': ['ddd', 'eee']})
+
+ @mock.patch('cros.factory.hwid.v3.hwid_utils.EnumerateHWID',
+ return_value={'HWID1': 'bbb', 'HWID2': 'aaa'})
+ def testOutputWithoutBOM(self, unused_enumerate_hwid_mock):
+ hwid_cmdline.EnumerateHWIDWrapper(mock.MagicMock(no_bom=True))
+
+ self.assertEquals(hwid_cmdline.Output.data, 'HWID1\nHWID2\n')
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/py/hwid/v3/hwid_rule_functions.py b/py/hwid/v3/hwid_rule_functions.py
index 7ac0dba..6646567 100644
--- a/py/hwid/v3/hwid_rule_functions.py
+++ b/py/hwid/v3/hwid_rule_functions.py
@@ -8,64 +8,10 @@
from cros.factory.hwid.v3.common import HWIDException
from cros.factory.hwid.v3.rule import GetContext
-from cros.factory.hwid.v3.rule import GetLogger
from cros.factory.hwid.v3.rule import RuleFunction
from cros.factory.hwid.v3.rule import Value
from cros.factory.test.rules import phase
-from cros.factory.utils.type_utils import MakeList
-
-
-def GetClassAttributesOnBOM(database, bom, comp_cls):
- """Creates a set of valid rule values to be evaluated with.
-
- This method accepts a HWID context and a component class, and generates a dict
- of attributes under the HWID context. First it checks there is a valid
- component of the given class in the BOM by matching the probed values in the
- BOM object to the values defined in the database. Then it extracts from the
- database all feasible values that can be used in rule evaluation (e.g.
- component name, component values, labels, ... etc.), and return the values as
- a dict. A dict with name maps to None is used to represent missing components.
- For example, the valid attributes for 'storage' class may look like:
-
- valid_attributes = {
- 'name': 'sandisk_16g',
- 'value': 'Sandisk 33456'
- }
-
- Args:
- database: The Database to extract attributes from.
- bom: The BOM object to extract attributes from.
- comp_cls: The component class to retrieve values for.
-
- Returns:
- A dict of attributes extracted from database that can be used to represent
- or describe the given component class. None if comp_cls is invalid.
- """
- def PackProbedValues(bom, comp_cls):
- results = []
- for c in bom.components[comp_cls]:
- if c.probed_values is None:
- # For non-probeable components, report its component name directly if
- # there is one.
- if c.component_name:
- results.append(c.component_name)
- continue
- matched_component = database.components.MatchComponentsFromValues(
- comp_cls, c.probed_values)
- if matched_component:
- results.extend(matched_component.keys())
- return results
-
- if comp_cls not in database.components.GetRequiredComponents():
- GetLogger().Error('Invalid component class: %r' % comp_cls)
- return None
- # Construct a set of known values from hwid.database and hwid.bom.
- results = PackProbedValues(bom, comp_cls)
- # If the set is empty, add a None element indicating that the component
- # class is missing.
- if not results:
- results.append(None)
- return results
+from cros.factory.utils import type_utils
def _ComponentCompare(comp_cls, values, op_for_values):
@@ -77,14 +23,20 @@
op_for_values: The operation used to generate final result. Must be any or
all.
"""
+ def _IsMatch(value):
+ return any(
+ [value.Matches(name) for name in context.bom.components[comp_cls]])
+
context = GetContext()
- attrs = GetClassAttributesOnBOM(context.database, context.bom, comp_cls)
- if attrs is None:
+
+ # Always treat as comparing failed if the specified component class is not
+ # recorded in the BOM object.
+ if comp_cls not in context.bom.components:
return False
- values = [
- Value(v) if not isinstance(v, Value) else v for v in MakeList(values)]
- return op_for_values(
- [any([v.Matches(attr) for attr in attrs]) for v in values])
+
+ values = [Value(v) if not isinstance(v, Value) else v
+ for v in type_utils.MakeList(values)]
+ return op_for_values([_IsMatch(v) for v in values])
@RuleFunction(['bom', 'database'])
@@ -120,16 +72,26 @@
@RuleFunction(['bom', 'database'])
-def SetComponent(comp_cls, name):
- """A wrapper method to update {comp_cls: name} pair of BOM and re-generate
- 'binary_string' and 'encoded_string' of the HWID context.
+def SetComponent(comp_cls, names):
+ """Set the component of the given component class recorded in the BOM object.
Args:
comp_cls: The component class to set.
- name: The component name to set to the given component class.
+ names: The component name to set to the given component class.
"""
context = GetContext()
- context.database.UpdateComponentsOfBOM(context.bom, {comp_cls: name})
+
+ if not isinstance(comp_cls, str):
+ raise HWIDException(
+ 'Component class should be in string type, but got %r.' % comp_cls)
+
+ names = [] if names is None else type_utils.MakeList(names)
+ for name in names:
+ if not isinstance(name, str):
+ raise HWIDException(
+ 'Component name should be in string type, but got %r.' % name)
+
+ context.bom.SetComponent(comp_cls, names)
@RuleFunction(['bom', 'database'])
diff --git a/py/hwid/v3/hwid_rule_functions_unittest.py b/py/hwid/v3/hwid_rule_functions_unittest.py
index 50713bf..ac5cf41 100755
--- a/py/hwid/v3/hwid_rule_functions_unittest.py
+++ b/py/hwid/v3/hwid_rule_functions_unittest.py
@@ -10,7 +10,6 @@
import factory_common # pylint: disable=unused-import
from cros.factory.hwid.v3.bom import BOM
-from cros.factory.hwid.v3.bom import ProbedComponentResult
from cros.factory.hwid.v3 import common
from cros.factory.hwid.v3.database import Database
from cros.factory.hwid.v3.hwid_rule_functions import ComponentEq
@@ -37,18 +36,8 @@
self.database = Database.LoadFile(_TEST_DATABASE_PATH,
verify_checksum=False)
self.bom = BOM(
- project='CHROMEBOOK',
- encoding_pattern_index=0,
- image_id=0,
- components={
- 'cpu': [ProbedComponentResult('cpu_0', {'unused_value': 'value_0'},
- None)]
- },
- encoded_fields={'cpu': 0})
- self.device_info = {
- 'SKU': 1,
- 'has_cellular': False
- }
+ encoding_pattern_index=0, image_id=0, components={'cpu': ['cpu_0']})
+ self.device_info = {'SKU': 1, 'has_cellular': False}
self.vpd = {
'ro': {
'serial_number': 'foo',
@@ -75,12 +64,10 @@
def testSetComponent(self):
SetComponent('cpu', 'cpu_3')
- self.assertEquals(1, len(self.bom.components['cpu']))
- self.assertEquals('cpu_3', self.bom.components['cpu'][0].component_name)
+ self.assertEquals(['cpu_3'], self.bom.components['cpu'])
SetComponent('cpu', None)
- self.assertEquals(1, len(self.bom.components['cpu']))
- self.assertEquals(None, self.bom.components['cpu'][0].component_name)
+ self.assertEquals(0, len(self.bom.components['cpu']))
def testGetSetImageId(self):
self.assertEquals(0, GetImageId())
diff --git a/py/hwid/v3/hwid_utils.py b/py/hwid/v3/hwid_utils.py
index d635d7c..c19a593 100644
--- a/py/hwid/v3/hwid_utils.py
+++ b/py/hwid/v3/hwid_utils.py
@@ -8,9 +8,8 @@
import logging
import os
-import factory_common # pylint: disable=W0611
+import factory_common # pylint: disable=unused-import
from cros.factory.hwid.v3.bom import BOM
-from cros.factory.hwid.v3.bom import ProbedComponentResult
from cros.factory.hwid.v3 import builder
from cros.factory.hwid.v3 import common
from cros.factory.hwid.v3.database import Database
@@ -52,7 +51,8 @@
db_builder.Render(database_path)
-def GenerateHWID(db, bom, device_info, vpd=None, rma_mode=False):
+def GenerateHWID(database, probed_results=None, device_info=None, vpd=None,
+ rma_mode=False):
"""Generates a HWID v3 from the given data.
The HWID is generated based on the given device info and a BOM object. If
@@ -60,12 +60,12 @@
bom, priority is given to device_info.
Args:
- db: A Database object to be used.
- bom: A BOM object contains a list of components installed on the DUT.
- device_info: A dict of component infomation keys to their corresponding
- values. The format is device-specific and the meanings of each key and
- value vary from device to device. The valid keys and values should be
- specified in project-specific component database.
+ database: A Database object to be used.
+ probed_results: None or the probed results of the DUT.
+ device_info: None or a dict of component infomation keys to their
+ corresponding values. The format is device-specific and the meanings of
+ each key and value vary from device to device. The valid keys and values
+ should be specified in project-specific component database.
vpd: None or a dict of RO and RW VPD values. This argument should be set
if some rules in the HWID database rely on the VPD values.
rma_mode: Whether to verify components status in RMA mode. Defaults to
@@ -76,74 +76,41 @@
"""
hwid_mode = _HWIDMode(rma_mode)
- # Update unprobeable components with rules defined in db before verification.
- context_args = dict(database=db, bom=bom, mode=hwid_mode,
- device_info=device_info)
- if vpd is not None:
- context_args['vpd'] = vpd
- context = rule.Context(**context_args)
- db.rules.EvaluateRules(context, namespace='device_info.*')
+ if probed_results is None:
+ probed_results = GetProbedResults()
- identity = transformer.BOMToIdentity(db, bom)
+ if device_info is None:
+ device_info = GetDeviceInfo()
- verifier.VerifyComponentStatus(db, bom, hwid_mode)
+ bom = _GenerateBOMFromProbedResults(
+ database, probed_results, device_info, vpd, hwid_mode)
+ verifier.VerifyComponentStatus(database, bom, hwid_mode)
+
+ identity = transformer.BOMToIdentity(database, bom)
+
return identity
-def GetIdentityFromEncodedString(database, encoded_string):
- image_id = identity_utils.GetImageIdFromEncodedString(encoded_string)
- encoding_scheme = database.pattern.GetEncodingScheme(image_id)
- return Identity.GenerateFromEncodedString(encoding_scheme, encoded_string)
-
-
def DecodeHWID(database, encoded_string):
- """Decodes the given HWID v3 encoded string and returns the decoded info.
+ """Decodes the given HWID v3 encoded string into a BOM object.
Args:
- db: A Database object to be used.
+ database: A Database object to be used.
encoded_string: An encoded HWID string to test.
Returns:
- The decoded HWIDv3 context object.
+ The decoded BOM object.
"""
- identity = GetIdentityFromEncodedString(database, encoded_string)
+ image_id = identity_utils.GetImageIdFromEncodedString(encoded_string)
+ encoding_scheme = database.pattern.GetEncodingScheme(image_id)
+ identity = Identity.GenerateFromEncodedString(encoding_scheme, encoded_string)
bom = transformer.IdentityToBOM(database, identity)
return identity, bom
-def ParseDecodedHWID(database, bom, identity):
- """Parses the HWID object into a more compact dict.
-
- This function returns the project name and binary string from the HWID object,
- along with a generated dict of components to their probed values decoded in
- the HWID object.
-
- Args:
- hwid: A decoded HWID object.
-
- Returns:
- A dict containing the project name, the binary string, and the list of
- components.
- """
- output_components = collections.defaultdict(list)
- components = bom.components
- db_components = database.components
- for comp_cls in sorted(components):
- for (comp_name, probed_values, _) in sorted(components[comp_cls]):
- if not probed_values:
- probed_values = db_components.GetComponentAttributes(
- comp_cls, comp_name).get('values')
- output_components[comp_cls].append(
- {comp_name: probed_values if probed_values else None})
- return {'project': database.project,
- 'binary_string': identity.binary_string,
- 'image_id': database.image_id[bom.image_id],
- 'components': dict(output_components)}
-
-
-def VerifyHWID(db, encoded_string, probed_bom, vpd=None, rma_mode=False,
- current_phase=None):
- """Verifies the given encoded HWID v3 string against the component db.
+def VerifyHWID(database, encoded_string, probed_results=None,
+ device_info=None, vpd=None, rma_mode=False, current_phase=None):
+ """Verifies the given encoded HWID v3 string against the probed BOM object.
A HWID context is built with the encoded HWID string and the project-specific
component database. The HWID context is used to verify that the probed
@@ -158,9 +125,9 @@
A set of mandatory rules for VPD are also forced here.
Args:
- db: A Database object to be used.
+ database: A Database object to be used.
encoded_string: An encoded HWID string to test.
- probed_bom: A BOM object contains a list of components installed on the DUT.
+ probed_results: None or the probed results of the DUT.
vpd: None or a dict of RO and RW VPD values. This argument should be set
if some rules in the HWID database rely on the VPD values.
rma_mode: True for RMA mode to allow deprecated components. Defaults to
@@ -173,17 +140,22 @@
HWIDException if verification fails.
"""
hwid_mode = _HWIDMode(rma_mode)
- identity = GetIdentityFromEncodedString(db, encoded_string)
- decoded_bom = transformer.IdentityToBOM(db, identity)
- verifier.VerifyBOM(db, decoded_bom, probed_bom)
+
+ if probed_results is None:
+ probed_results = GetProbedResults()
+ probed_bom = _GenerateBOMFromProbedResults(
+ database, probed_results, device_info, vpd, hwid_mode)
+
+ decoded_bom = DecodeHWID(database, encoded_string)[1]
+
+ verifier.VerifyBOM(database, decoded_bom, probed_bom)
verifier.VerifyComponentStatus(
- db, decoded_bom, hwid_mode, current_phase=current_phase)
- verifier.VerifyPhase(db, decoded_bom, current_phase)
- context_args = dict(database=db, bom=decoded_bom, mode=hwid_mode)
- if vpd is not None:
- context_args['vpd'] = vpd
- context = rule.Context(**context_args)
- db.rules.EvaluateRules(context, namespace='verify.*')
+ database, decoded_bom, hwid_mode, current_phase=current_phase)
+ verifier.VerifyPhase(database, decoded_bom, current_phase)
+
+ context = rule.Context(
+ database=database, bom=decoded_bom, mode=hwid_mode, vpd=vpd)
+ database.rules.EvaluateRules(context, namespace='verify.*')
def ListComponents(db, comp_class=None):
@@ -214,131 +186,87 @@
return dict(output_components)
-def EnumerateHWID(db, image_id=None, status='supported'):
+def EnumerateHWID(database, image_id=None, status='supported', comps=None):
"""Enumerates all the possible HWIDs.
Args:
- db: A Database object to be used.
+ database: A Database object to be used.
image_id: The image ID to use. Defaults to the latest image ID.
status: By default only 'supported' components are enumerated. Set this to
'released' will include 'supported' and 'deprecated'. Set this to
'all' if you want to include 'deprecated', 'unsupported' and
'unqualified' components.
+ comps: None or a dict of list of string as the limit to specified
+ components.
Returns:
A dict of all enumetated HWIDs to their list of components.
"""
- def _GenerateEncodedString(encoded_fields):
- """Generates encoded string by encoded_fields
+ limited_comps = comps or {}
- Args:
- encoded_fields: This parameter records indices of encoded fields
- """
- encoding_pattern = 0
- pass_check = True
- components = collections.defaultdict(list)
- component_list = []
- logging.debug('EnumerateHWID: Iterate encoded_fields %s',
- ','.join(map(str, encoded_fields.values())))
- for field, index in encoded_fields.iteritems():
- # pylint: disable=W0212
- attr_dict = db._GetAttributesByIndex(field, index)
- comp_items = []
- for comp_cls, attr_list in attr_dict.iteritems():
- if attr_list is None:
- comp_items.append('None')
- components[comp_cls].append(ProbedComponentResult(
- None, None, common.MISSING_COMPONENT_ERROR(comp_cls)))
- else:
- for attrs in attr_list:
- if status == 'supported' and attrs.get('status') in (
- common.COMPONENT_STATUS.unsupported,
- common.COMPONENT_STATUS.deprecated,
- common.COMPONENT_STATUS.unqualified):
- pass_check = False
- logging.debug('Ignore %s.%s: %r', comp_cls, attrs['name'],
- attrs['status'])
- break
- if status == 'released' and attrs.get('status') in (
- common.COMPONENT_STATUS.unsupported,
- common.COMPONENT_STATUS.unqualified):
- pass_check = False
- logging.debug('Ignore %s.%s: %r', comp_cls, attrs['name'],
- attrs['status'])
- break
- comp_items.append(attrs['name'])
- components[comp_cls].append(ProbedComponentResult(
- attrs['name'], attrs['values'], None))
- component_list.append(' '.join(comp_items))
- if pass_check:
- bom = BOM(db.project, encoding_pattern, image_id, components,
- encoded_fields)
- binary_string = transformer.BOMToBinaryString(db, bom)
- encoded_string = transformer.BinaryStringToEncodedString(
- db, binary_string)
- hwid_dict[encoded_string] = ','.join(component_list)
+ if image_id is None:
+ image_id = max(database.image_id.keys())
- def _RecursivelyGenerate(index=None, encoded_fields=None):
- """Recursive function to generate all combinations.
+ if status == 'supported':
+ acceptable_status = set(['supported'])
+ elif status == 'released':
+ acceptable_status = set(['supported', 'deprecated'])
+ elif status == 'all':
+ acceptable_status = set(common.COMPONENT_STATUS)
+ else:
+ raise ValueError('The argument `status` must be one of "supported", '
+ '"released", "all", but got %r.' % status)
- Args:
- index: This parameter means the index of pattern fields
- encoded_fields: This parameter records index of components
- """
- if index >= len(fields_list):
- _GenerateEncodedString(encoded_fields)
+ def _IsComponentsSetValid(comps):
+ for comp_cls, comp_names in comps.iteritems():
+ comp_names = type_utils.MakeList(comp_names)
+ if (comp_cls in limited_comps and
+ sorted(list(limited_comps[comp_cls])) != sorted(comp_names)):
+ return False
+ for comp_name in comp_names:
+ status = database.components.GetComponentStatus(comp_cls, comp_name)
+ if status not in acceptable_status:
+ return False
+ return True
+
+ results = {}
+ def _RecordResult(components):
+ bom = BOM(0, image_id, components)
+ identity = transformer.BOMToIdentity(database, bom)
+ results[identity.encoded_string] = bom
+
+ def _RecursivelyEnumerateCombinations(combinations, i,
+ selected_combinations):
+ if i >= len(combinations):
+ components = {}
+ for selected_combination in selected_combinations:
+ components.update(selected_combination)
+
+ _RecordResult(components)
return
- field = fields_list[index]
- if field not in fields_bits.keys():
- encoded_fields[field] = 0
- _RecursivelyGenerate(index + 1, encoded_fields)
- else:
- for i in xrange(0, len(db.encoded_fields[field])):
- if i >= 2 ** fields_bits[field]:
- break
- encoded_fields[field] = i
- _RecursivelyGenerate(index + 1, encoded_fields)
+ for combination in combinations[i]:
+ selected_combinations[i] = combination
+ _RecursivelyEnumerateCombinations(
+ combinations, i + 1, selected_combinations)
- def _ConvertImageID(image_id=None):
- """Gets image ID.
+ combinations = []
+ for field_name, bit_length in database.pattern.GetFieldsBitLength(
+ image_id).iteritems():
+ max_index = (1 << bit_length) - 1
+ last_combinations = []
+ for index, comps_set in database.encoded_fields[field_name].iteritems():
+ if index <= max_index and _IsComponentsSetValid(comps_set):
+ last_combinations.append(
+ {comp_cls: type_utils.MakeList(comp_names)
+ for comp_cls, comp_names in comps_set.iteritems()})
- Args:
- image_id: The image ID. It can be a number, a string, or None:
- 1. If it's a number then return the number.
- 2. If it's a string then look up the image ID in the database with it.
- 3. If it's None, return the latest image ID.
+ if not last_combinations:
+ return {}
+ combinations.append(last_combinations)
- Returns:
- An integer of the image ID as defined in the database.
- """
- max_image_id = max(db.image_id.keys())
- if not isinstance(image_id, int):
- if image_id is None:
- image_id = max_image_id
- elif image_id.isdigit():
- image_id = int(image_id)
- else:
- for k, v in db.image_id.iteritems():
- if image_id == v:
- image_id = k
- break
- assert image_id in range(0, max_image_id + 1), 'Invalid Image ID'
- return image_id
-
- hwid_dict = {}
- encoded_fields = collections.defaultdict(int)
- image_id = _ConvertImageID(image_id)
-
- fields_bits = collections.defaultdict(int)
- for field in db.pattern.GetPatternByImageId(image_id)['fields']:
- comp, bit_width = field.items()[0]
- fields_bits[comp] += bit_width
- fields_list = db.encoded_fields.keys()
-
- # Recursively generate all combinations of HWID.
- _RecursivelyGenerate(0, encoded_fields)
- return hwid_dict
+ _RecursivelyEnumerateCombinations(combinations, 0, [None] * len(combinations))
+ return results
def GetProbedResults(infile=None, raw_data=None):
@@ -373,14 +301,18 @@
return json_utils.LoadStr(process_utils.CheckOutput(cmd, shell=True))
-def GenerateBOMFromProbedResults(database,
- probed_results, loose_matching=False):
+def _GenerateBOMFromProbedResults(
+ database, probed_results,
+ device_info=None, vpd=None, mode=None, loose_matching=False):
"""Generates a BOM object according to the given probed results.
Args:
database: An instance of a HWID database.
probed_results: A JSON-serializable dict of the probe result, which is
usually the output of the probe command.
+ device_info: None or a dict of device info.
+ vpd: None or a dict of vpd data.
+ mode: None or "rma" or "normal".
loose_matching: If set to True, partial match of probed results will be
accepted. For example, if the probed results only contain the
firmware version of RO main firmware but not its hash, and we want to
@@ -391,105 +323,57 @@
Returns:
A instance of BOM class.
"""
- # TODO(yhong): The transform process of probed results to BOM should not
- # relate to the HWID database.
-
- # encoding_pattern_index and image_id are unprobeable and should be set
- # explictly. Defaults them to 0.
- encoding_pattern_index = 0
- image_id = 0
-
- def LookupProbedValue(comp_cls):
- if comp_cls in probed_results:
- # We don't need the component name here.
- return sum(probed_results[comp_cls].values(), [])
- return None
-
- def TryAddDefaultItem(probed_components, comp_cls):
- """Try to add the default component item.
-
- If the default component exists and its status is not 'unsupported', then
- add it into the probed_components and return True.
- """
- if comp_cls in database.components.default:
- comp_name = database.components.default[comp_cls]
- comp_status = database.components.GetComponentStatus(comp_cls, comp_name)
- if comp_status != common.COMPONENT_STATUS.unsupported:
- probed_components[comp_cls].append(
- ProbedComponentResult(comp_name, None, None))
- return True
- return False
-
- # Construct a dict of component classes to list of ProbedComponentResult.
- probed_components = collections.defaultdict(list)
- for comp_cls in database.components.GetRequiredComponents():
- probed_comp_values = LookupProbedValue(comp_cls)
- if probed_comp_values is None:
- # The component class has the default item.
- if TryAddDefaultItem(probed_components, comp_cls):
- continue
- # Probeable comp_cls but no component is found in probe results.
- if comp_cls in database.components.probeable:
- probed_components[comp_cls].append(
- ProbedComponentResult(
- None, None, common.MISSING_COMPONENT_ERROR(comp_cls)))
- else:
- # Unprobeable comp_cls and only has 1 component, treat as found.
- comp_dict = database.components.components_dict[comp_cls]
- if len(comp_dict['items']) == 1:
- comp_name = comp_dict['items'].keys()[0]
- comp_status = database.components.GetComponentStatus(
- comp_cls, comp_name)
- if comp_status == common.COMPONENT_STATUS.supported:
- probed_components[comp_cls].append(
- ProbedComponentResult(comp_name, None, None))
+ # Construct a dict of component classes to list of component names.
+ components = {}
+ for comp_cls in probed_results:
+ if comp_cls not in database.components.components_dict:
+ logging.warning('The component class %r is not listed in '
+ 'the HWID database.', comp_cls)
continue
- for probed_value in probed_comp_values:
- # Unprobeable comp_cls but component is found in probe results.
- if comp_cls not in database.components.probeable:
- probed_components[comp_cls].append(ProbedComponentResult(
- None, probed_value,
- common.UNPROBEABLE_COMPONENT_ERROR(comp_cls)))
- continue
+ components[comp_cls] = []
+ # We don't need the component name here.
+ probed_values = sum(probed_results[comp_cls].values(), [])
+
+ if not probed_values:
+ # TODO(yhong): This logic should be workarounded by rules.
+ if comp_cls in database.components.default:
+ comp_name = database.components.default[comp_cls]
+ components[comp_cls].append(comp_name)
+
+ continue
+
+ for probed_value in probed_values:
matched_comps = database.components.MatchComponentsFromValues(
comp_cls, probed_value, loose_matching, include_default=False)
- if matched_comps is None:
- # If there is no default item, add invalid error.
- if not TryAddDefaultItem(probed_components, comp_cls):
- probed_components[comp_cls].append(ProbedComponentResult(
- None, probed_value,
- common.INVALID_COMPONENT_ERROR(comp_cls, probed_value)))
- elif len(matched_comps) == 1:
- comp_name, comp_data = matched_comps.items()[0]
- comp_status = database.components.GetComponentStatus(
- comp_cls, comp_name)
- if comp_status == common.COMPONENT_STATUS.supported:
- probed_components[comp_cls].append(
- ProbedComponentResult(comp_name, comp_data['values'], None))
- else:
- probed_components[comp_cls].append(ProbedComponentResult(
- comp_name, comp_data['values'],
- common.UNSUPPORTED_COMPONENT_ERROR(comp_cls, comp_name,
- comp_status)))
+
+ if not matched_comps:
+ logging.warning('The probed values %r of %r component does not match '
+ 'any components recorded in the database.',
+ probed_value, comp_cls)
+
elif len(matched_comps) > 1:
- probed_components[comp_cls].append(ProbedComponentResult(
- None, probed_value,
- common.AMBIGUOUS_COMPONENT_ERROR(
- comp_cls, probed_value, matched_comps)))
+ raise common.HWIDException(
+ 'Ambiguous probes values %s of %r component. '
+ 'Possible components are: %r.' %
+ (probed_value, comp_cls, matched_comps))
- # Encode the components to a dict of encoded fields to encoded indices.
- encoded_fields = {}
- for field in database.encoded_fields:
- encoded_fields[field] = database.GetFieldIndexFromProbedComponents(
- field, probed_components)
+ else:
+ comp_name, _ = matched_comps.items()[0]
+ components[comp_cls].append(comp_name)
- return BOM(database.project, encoding_pattern_index, image_id,
- probed_components, encoded_fields)
+ bom = BOM(encoding_pattern_index=0, image_id=0, components=components)
+
+ # Evaluate device_info rules to fill unprobeable data in the BOM object.
+ context = rule.Context(database=database, bom=bom,
+ device_info=device_info, vpd=vpd, mode=mode)
+ database.rules.EvaluateRules(context, namespace='device_info.*')
+
+ return bom
-def GetDeviceInfo(infile):
+def GetDeviceInfo(infile=None):
"""Get device info from the given file.
Args:
@@ -502,23 +386,30 @@
Returns:
A dict of device info.
"""
- with open(infile, 'r') as f:
- device_info = yaml.load(f.read())
- return device_info
+ if infile:
+ with open(infile, 'r') as f:
+ return yaml.load(f.read())
+
+ try:
+ from cros.factory.test import device_data
+ return device_data.GetAllDeviceData()
+
+ except ImportError:
+ return {}
-def GetVPDData(run_vpd=False, vpd_data_file=None):
+def GetVPDData(run_vpd=False, infile=None):
"""Get the vpd data for the context instance.
Args:
run_vpd: Whether to run `vpd` command-line tool to obtain the vpd data.
- vpd_data_file: Obtain the vpd data by reading the specified file if set.
+ infile: Obtain the vpd data by reading the specified file if set.
Returns:
- A dict of vpd data. Empty if neither `run_vpd` nor `vpd_data_file` are
+ A dict of vpd data. Empty if neither `run_vpd` nor `infile` are
specified.
"""
- assert not (run_vpd and vpd_data_file)
+ assert not (run_vpd and infile)
if run_vpd:
from cros.factory.utils import sys_utils
vpd_tool = sys_utils.VPDTool()
@@ -526,8 +417,8 @@
'ro': vpd_tool.GetAllData(partition=vpd_tool.RO_PARTITION),
'rw': vpd_tool.GetAllData(partition=vpd_tool.RW_PARTITION)
}
- elif vpd_data_file:
- return json_utils.LoadFile(vpd_data_file)
+ elif infile:
+ return json_utils.LoadFile(infile)
else:
return {'ro': {}, 'rw': {}}
diff --git a/py/hwid/v3/hwid_utils_unittest.py b/py/hwid/v3/hwid_utils_unittest.py
index 36e7cda..7a2adc5 100755
--- a/py/hwid/v3/hwid_utils_unittest.py
+++ b/py/hwid/v3/hwid_utils_unittest.py
@@ -1,10 +1,10 @@
#!/usr/bin/env python
#
-# Copyright 2013 The Chromium OS Authors. All rights reserved.
+# 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.
-"""Unit tests for HWID v3 utility functions."""
+"""Integration tests for the HWID v3 framework."""
import copy
import logging
@@ -13,322 +13,332 @@
import tempfile
import unittest
-import factory_common # pylint: disable=W0611
+import factory_common # pylint: disable=unused-import
+from cros.factory.hwid.v3.bom import BOM
from cros.factory.hwid.v3 import common
-from cros.factory.hwid.v3 import database
+from cros.factory.hwid.v3.database import Database
from cros.factory.hwid.v3 import hwid_utils
from cros.factory.hwid.v3 import yaml_wrapper as yaml
-from cros.factory.hwid.v3.rule import Value
-from cros.factory.test.rules import phase
+from cros.factory.hwid.v3.rule import RuleException
from cros.factory.utils import json_utils
from cros.factory.utils import sys_utils
from cros.factory.utils import yaml_utils
-TEST_DATA_PATH = os.path.join(os.path.dirname(__file__), 'testdata')
+_TEST_DATA_PATH = os.path.join(os.path.dirname(__file__), 'testdata')
-class HWIDv3UtilsTestWithNewDatabase(unittest.TestCase):
- """Test cases for HWID v3 utilities with the new database.
+_TEST_DATABASE_PATH = os.path.join(_TEST_DATA_PATH, 'TEST_PROJECT')
- The new database adds a new image_id and a pattern, that removes display_panel
- and cellular field, and add firmware_keys field. It also adds SKU that has no
- audio_codec component.
- """
+_TEST_PROBED_RESULTS_PATH = os.path.join(
+ _TEST_DATA_PATH, 'TEST_PROJECT_probed_results')
+_TEST_INVALID_PROBED_RESULTS_PATH = os.path.join(
+ _TEST_DATA_PATH, 'TEST_PROJECT_invalid_probed_results')
+
+
+class TestData(object):
+ def __init__(self, encoded_string, image_id, update_components=None,
+ device_info=None, rma_mode=None):
+ components = {
+ 'audio_codec': ['codec_0', 'hdmi_0'],
+ 'battery': ['battery_unsupported'],
+ 'bluetooth': ['bluetooth_0'],
+ 'cellular': [],
+ 'cpu': ['cpu_0'],
+ 'display_panel': ['display_panel_0'],
+ 'dram': ['dram_0'],
+ 'embedded_controller': ['embedded_controller_0'],
+ 'firmware_keys': ['firmware_keys_premp'],
+ 'flash_chip': ['flash_chip_0'],
+ 'keyboard': ['keyboard_us'],
+ 'region': [],
+ 'ro_ec_firmware': ['ro_ec_firmware_0'],
+ 'ro_main_firmware': ['ro_main_firmware_0'],
+ 'storage': ['storage_0'],
+ 'video': ['camera_0'],
+ }
+ if image_id in [0, 1]:
+ del components['flash_chip']
+ if update_components is not None:
+ components.update(update_components)
+
+ self.encoded_string = encoded_string
+ self.bom = BOM(0, image_id, components)
+ self.device_info = device_info or {}
+ self.rma_mode = rma_mode
+
+
+_TEST_DATA_CAN_GENERATE = [
+ TestData('CHROMEBOOK D9L-S3Q-A9G', 3,
+ dict(audio_codec=['codec_1', 'hdmi_1'],
+ battery=['battery_supported'],
+ cpu=['cpu_5'],
+ dram=['dram_1', 'dram_1'],
+ firmware_keys=['firmware_keys_mp'],
+ region=['tw'],
+ ro_main_firmware=['ro_main_firmware_1']),
+ {'component.has_cellular': False},
+ False),
+ TestData('CHROMEBOOK D5Q-Q3Q-A3F', 3,
+ dict(battery=['battery_supported'],
+ cellular=['cellular_0'],
+ cpu=['cpu_4'],
+ firmware_keys=['firmware_keys_mp']),
+ {'component.has_cellular': True},
+ True),
+]
+
+_TEST_DATA_RELEASED = [
+ TestData('CHROMEBOOK AIEB-ED', 0,
+ dict(battery=['battery_unqualified'],
+ ro_main_firmware=['ro_main_firmware_1'])),
+ TestData('CHROMEBOOK ANUB-CX', 0,
+ dict(battery=['battery_supported'],
+ cpu=['cpu_1'],
+ dram=['dram_2', 'dram_2', 'dram_2'],
+ ro_main_firmware=['ro_main_firmware_1'])),
+ TestData('CHROMEBOOK AEAB-HD', 0, dict(battery=['battery_deprecated'])),
+ TestData('CHROMEBOOK BMAB-VV', 1, dict(battery=['battery_supported'])),
+ TestData('CHROMEBOOK D3B-A2Q-A8C', 3,
+ dict(battery=['battery_deprecated'],
+ ro_main_firmware=['ro_main_firmware_1'])),
+]
+
+_TEST_DATA_BREAK_VERIFY_RULES = TestData(
+ 'CHROMEBOOK D9D-S2Q-A2Q', 3,
+ dict(audio_codec=['codec_1', 'hdmi_1'],
+ battery=['battery_supported'],
+ dram=['dram_1', 'dram_1'],
+ firmware_keys=['firmware_keys_mp'],
+ region=['tw'],
+ ro_main_firmware=['ro_main_firmware_1']))
+
+_TEST_DATA_BAD_COMPONENT_STATUS = TestData(
+ 'CHROMEBOOK D6L-S3Q-A3T', 3,
+ dict(audio_codec=['codec_1', 'hdmi_1'],
+ cpu=['cpu_5'],
+ dram=['dram_1', 'dram_1'],
+ firmware_keys=['firmware_keys_mp'],
+ region=['tw'],
+ ro_main_firmware=['ro_main_firmware_1']))
+
+_TEST_DATA_INVALID_ENCODED_STRING = [
+ 'CHROMEBOOK ANUB-XX',
+ 'CHROMEBOOK ANUB-2233-44WN',
+ 'CHROME?OOK ANUB-CX',
+ 'CHROMEBOOK#ANUB-CX',
+ 'CHROMEBOOK AIEZ-GV',
+]
+
+
+def _LoadMultiProbedResults(path):
+ with open(path, 'r') as f:
+ return [hwid_utils.GetProbedResults(raw_data=raw_data)
+ for raw_data in f.read().split('##### SPLITLINE #####')]
+
+
+class _CustomAssertions(object):
+ def assertBOMEquals(self, bom1, bom2):
+ self.assertEquals(bom1.encoding_pattern_index, bom2.encoding_pattern_index)
+ self.assertEquals(bom1.image_id, bom2.image_id)
+ self.assertEquals(set(bom1.components.keys()), set(bom2.components.keys()))
+ for comp_cls, bom1_comp_names in bom1.components.iteritems():
+ self.assertEquals(bom1_comp_names, bom2.components[comp_cls])
+
+
+class GenerateHWIDTest(unittest.TestCase, _CustomAssertions):
def setUp(self):
- self.db = database.Database.LoadFile(
- os.path.join(TEST_DATA_PATH, 'NEW_TEST_PROJECT'))
- self.probed_results = json_utils.LoadFile(os.path.join(
- TEST_DATA_PATH, 'new_test_probe_result_hwid_utils.json'))
- self.vpd = {
- 'ro': {
- 'region': 'us',
- 'serial_number': 'foo'
- },
- 'rw': {
- 'gbind_attribute': '333333333333333333333333333333333333'
- '33333333333333333333333333332dbecc73',
- 'ubind_attribute': '323232323232323232323232323232323232'
- '323232323232323232323232323256850612'
- }
- }
+ self.database = Database.LoadFile(_TEST_DATABASE_PATH)
+ self.probed_results = _LoadMultiProbedResults(_TEST_PROBED_RESULTS_PATH)
+ self.invalid_probed_results = _LoadMultiProbedResults(
+ _TEST_INVALID_PROBED_RESULTS_PATH)
- def testGenerateHWID(self):
- device_info = {
- 'component.keyboard': 'us',
- }
- # Test new database with audio codec
- bom = hwid_utils.GenerateBOMFromProbedResults(self.db,
- self.probed_results[0])
- self.assertEquals(
- 'CHROMEBOOK E35-A2Y-A7B',
- hwid_utils.GenerateHWID(
- self.db, bom, device_info, self.vpd, False).encoded_string)
- # Test new database without audio codec
- bom = hwid_utils.GenerateBOMFromProbedResults(self.db,
- self.probed_results[1])
- self.assertEquals(
- 'CHROMEBOOK E45-A2Y-A2Z',
- hwid_utils.GenerateHWID(
- self.db, bom, device_info, self.vpd, False).encoded_string)
+ def testSucc(self):
+ for i, test_data in enumerate(_TEST_DATA_CAN_GENERATE):
+ generated_encoded_string = hwid_utils.GenerateHWID(
+ self.database, self.probed_results[i],
+ test_data.device_info, {}, rma_mode=test_data.rma_mode).encoded_string
+ self.assertEquals(generated_encoded_string, test_data.encoded_string)
- def testDecodeHWID(self):
- """Tests HWID decoding."""
- # Decode old HWID string
- identity, bom = hwid_utils.DecodeHWID(self.db, 'CHROMEBOOK D9I-F9U')
- parsed_result = hwid_utils.ParseDecodedHWID(self.db, bom, identity)
- self.assertNotIn('firmware_keys', parsed_result)
- self.assertEquals(parsed_result['components']['cellular'], [{None: None}])
- self.assertEquals(parsed_result['components']['audio_codec'],
- [{'codec_1': {'compact_str': Value('Codec 1')}},
- {'hdmi_1': {'compact_str': Value('HDMI 1')}}])
- self.assertEquals(parsed_result['components']['display_panel'],
- [{'display_panel_0': None}])
+ def testBadComponentStatus(self):
+ for i, test_data in enumerate(_TEST_DATA_CAN_GENERATE):
+ if not test_data.rma_mode:
+ continue
+ self.assertRaises(common.HWIDException, hwid_utils.GenerateHWID,
+ self.database, self.probed_results[i],
+ test_data.device_info, {}, rma_mode=False)
- # Decode new HWID string with audio_codec
- identity, bom = hwid_utils.DecodeHWID(self.db, 'CHROMEBOOK E35-A2Y-A7B')
- parsed_result = hwid_utils.ParseDecodedHWID(self.db, bom, identity)
- self.assertNotIn('display_panel', parsed_result)
- self.assertNotIn('cellular', parsed_result)
- self.assertEquals(parsed_result['components']['firmware_keys'],
- [{'firmware_keys_mp': {
- 'key_recovery': Value('kv3#key_recovery_mp'),
- 'key_root': Value('kv3#key_root_mp')}}])
- self.assertEquals(parsed_result['components']['audio_codec'],
- [{'codec_1': {'compact_str': Value('Codec 1')}},
- {'hdmi_1': {'compact_str': Value('HDMI 1')}}])
-
- # Decode new HWID string without audio_codec
- identity, bom = hwid_utils.DecodeHWID(self.db, 'CHROMEBOOK E45-A2Y-A2Z')
- parsed_result = hwid_utils.ParseDecodedHWID(self.db, bom, identity)
- self.assertNotIn('display_panel', parsed_result)
- self.assertNotIn('cellular', parsed_result)
- self.assertEquals(parsed_result['components']['firmware_keys'],
- [{'firmware_keys_mp': {
- 'key_recovery': Value('kv3#key_recovery_mp'),
- 'key_root': Value('kv3#key_root_mp')}}])
- self.assertEquals(parsed_result['components']['audio_codec'],
- [{None: None}])
+ def testBadProbedResults(self):
+ for probed_results in self.invalid_probed_results:
+ self.assertRaises(common.HWIDException, hwid_utils.GenerateHWID,
+ self.database, probed_results,
+ {'component.has_cellular': True}, {}, rma_mode=False)
-class HWIDv3UtilsTest(unittest.TestCase):
- """Test cases for HWID v3 utilities."""
-
+class DecodeHWIDTest(unittest.TestCase, _CustomAssertions):
def setUp(self):
- self.db = database.Database.LoadFile(
- os.path.join(TEST_DATA_PATH, 'TEST_PROJECT'))
- self.probed_results = json_utils.LoadFile(os.path.join(
- TEST_DATA_PATH, 'test_probe_result_hwid_utils.json'))
- self.vpd = {
- 'ro': {
- 'region': 'us',
- 'serial_number': 'foo'
- },
- 'rw': {
- 'gbind_attribute': '333333333333333333333333333333333333'
- '33333333333333333333333333332dbecc73',
- 'ubind_attribute': '323232323232323232323232323232323232'
- '323232323232323232323232323256850612'
- }
- }
+ self.database = Database.LoadFile(_TEST_DATABASE_PATH)
- def testGenerateHWID(self):
- """Tests HWID generation."""
- device_info = {
- 'component.has_cellular': False,
- 'component.keyboard': 'us',
- 'component.dram': 'foo',
- 'component.audio_codec': 'set_1'
- }
- bom = hwid_utils.GenerateBOMFromProbedResults(self.db, self.probed_results)
+ def testSucc(self):
+ for test_data in (_TEST_DATA_CAN_GENERATE +
+ _TEST_DATA_RELEASED +
+ [_TEST_DATA_BREAK_VERIFY_RULES] +
+ [_TEST_DATA_BAD_COMPONENT_STATUS]):
+ _, decoded_bom = hwid_utils.DecodeHWID(
+ self.database, test_data.encoded_string)
+
+ self.assertBOMEquals(decoded_bom, test_data.bom)
+
+ def testInvalidEncodedString(self):
+ for encoded_string in _TEST_DATA_INVALID_ENCODED_STRING:
+ self.assertRaises(common.HWIDException, hwid_utils.DecodeHWID,
+ self.database, encoded_string)
+
+
+class VerifyHWIDTest(unittest.TestCase):
+ def setUp(self):
+ self.database = Database.LoadFile(_TEST_DATABASE_PATH)
+ self.probed_results = _LoadMultiProbedResults(_TEST_PROBED_RESULTS_PATH)
+ self.default_device_info = _TEST_DATA_CAN_GENERATE[0].device_info
+
+ def testSucc(self):
+ for i, test_data in enumerate(_TEST_DATA_CAN_GENERATE):
+ hwid_utils.VerifyHWID(self.database, test_data.encoded_string,
+ self.probed_results[i], test_data.device_info, {},
+ rma_mode=test_data.rma_mode)
+
+ def testBOMMisMatch(self):
+ self.assertRaises(common.HWIDException, hwid_utils.VerifyHWID,
+ self.database, _TEST_DATA_CAN_GENERATE[0].encoded_string,
+ self.probed_results[1],
+ _TEST_DATA_CAN_GENERATE[1].device_info, {}, rma_mode=True)
+
+ def testBadComponentStatus(self):
+ test_data = _TEST_DATA_BAD_COMPONENT_STATUS
+ self.probed_results[0]['battery'] = {
+ 'battery_unsupported': [{'tech': 'Battery Li-ion', 'size': '2500000'}]}
+ self.assertRaises(common.HWIDException, hwid_utils.VerifyHWID,
+ self.database, test_data.encoded_string,
+ self.probed_results[0], self.default_device_info)
+
+ def testBreakVerifyRules(self):
+ test_data = _TEST_DATA_BREAK_VERIFY_RULES
+ self.probed_results[0]['cpu'] = {
+ 'cpu_0': [{'name': 'CPU @ 1.80GHz', 'cores': '4'}]}
+ self.assertRaises(RuleException, hwid_utils.VerifyHWID,
+ self.database, test_data.encoded_string,
+ self.probed_results[0], self.default_device_info)
+
+ def testInvalidEncodedString(self):
+ for encoded_string in _TEST_DATA_INVALID_ENCODED_STRING:
+ self.assertRaises(common.HWIDException, hwid_utils.VerifyHWID,
+ self.database, encoded_string,
+ self.probed_results[0], self.default_device_info)
+
+
+class ListComponentsTest(unittest.TestCase):
+ def setUp(self):
+ self.database = Database.LoadFile(_TEST_DATABASE_PATH)
+
+ def _TestListComponents(self, comp_cls, expected_results):
+ def _ConvertToSets(orig_dict):
+ return {key: set(value) for key, value in orig_dict.iteritems()}
+
+ results = hwid_utils.ListComponents(self.database, comp_cls)
+ self.assertEquals(_ConvertToSets(results), _ConvertToSets(expected_results))
+
+ def testSingleComponentClass(self):
+ self._TestListComponents(
+ 'cpu', {'cpu': ['cpu_0', 'cpu_1', 'cpu_2', 'cpu_3', 'cpu_4', 'cpu_5']})
+
+ def testMultipleComponentClass(self):
+ self._TestListComponents(
+ ['bluetooth', 'display_panel'],
+ {'bluetooth': ['bluetooth_0'], 'display_panel': ['display_panel_0']})
+
+ def testAllComponentClass(self):
+ # Too many entries, just do some simple test.
+ results = hwid_utils.ListComponents(self.database)
+ self.assertEquals(len(results), 16)
+ self.assertIn('keyboard', results)
+ self.assertIn('cpu', results)
+ self.assertIn('battery', results)
+ self.assertIn('dram', results)
self.assertEquals(
- 'CHROMEBOOK D9I-E4A-A2B',
- hwid_utils.GenerateHWID(
- self.db, bom, device_info, self.vpd, False).encoded_string)
+ set(results['dram']), set(['dram_0', 'dram_1', 'dram_2']))
- device_info = {
- 'component.has_cellular': True,
- 'component.keyboard': 'gb',
- 'component.dram': 'foo',
- 'component.audio_codec': 'set_1'
- }
+
+class EnumerateHWIDTest(unittest.TestCase, _CustomAssertions):
+ def setUp(self):
+ self.database = Database.LoadFile(_TEST_DATABASE_PATH)
+ self.default_combinations = {
+ field_name: len(field_data)
+ for field_name, field_data in self.database.encoded_fields.iteritems()}
+
+ def _CalculateNumCombinations(self, **kwargs):
+ result = 1
+ for key, value in self.default_combinations.iteritems():
+ value = kwargs.get(key, value)
+ if value is None:
+ continue
+ result *= value
+ return result
+
+ def testSupported(self):
+ for kwargs in [{}, {'status': 'supported'}]:
+ kwargs['comps'] = {'storage': ['storage_0']}
+ results = hwid_utils.EnumerateHWID(self.database, **kwargs)
+ self.assertEquals(len(results), self._CalculateNumCombinations(
+ battery_field=1, # only battery_supported is available
+ firmware_field=1, # ro_main_firmware_1 is deprecated
+ storage_field=1))
+
+ for test_data in _TEST_DATA_CAN_GENERATE:
+ if test_data.rma_mode:
+ continue
+ encoded_string = test_data.encoded_string
+ self.assertIn(encoded_string, results,
+ 'Encoded string %r is not found.' % encoded_string)
+ self.assertBOMEquals(results[encoded_string], test_data.bom)
+
+ def testReleased(self):
+ results = hwid_utils.EnumerateHWID(self.database, status='released',
+ comps={'cpu': ['cpu_0'],
+ 'storage': ['storage_0']})
+ # both battery_supported, battery_deprecated is available
self.assertEquals(
- 'CHROMEBOOK D92-E4A-A87',
- hwid_utils.GenerateHWID(
- self.db, bom, device_info, self.vpd, False).encoded_string)
+ len(results), self._CalculateNumCombinations(battery_field=2,
+ cpu_field=1,
+ storage_field=1))
- device_info = {
- 'component.has_cellular': True,
- 'component.keyboard': 'gb',
- 'component.dram': 'foo',
- 'component.audio_codec': 'set_0'
- }
+ def testAll(self):
+ results = hwid_utils.EnumerateHWID(self.database, status='all',
+ comps={'cpu': ['cpu_0'],
+ 'region': ['us'],
+ 'storage': ['storage_0']})
self.assertEquals(
- 'CHROMEBOOK D52-E4A-A7E',
- hwid_utils.GenerateHWID(
- self.db, bom, device_info, self.vpd, False).encoded_string)
+ len(results), self._CalculateNumCombinations(cpu_field=1,
+ region_field=1,
+ storage_field=1))
- def testVerifyHWID(self):
- """Tests HWID verification."""
- bom = hwid_utils.GenerateBOMFromProbedResults(self.db, self.probed_results)
- self.assertEquals(None, hwid_utils.VerifyHWID(
- self.db, 'CHROMEBOOK A5AU-LU', bom, self.vpd, False,
- phase.EVT))
- for current_phase in (phase.PVT, phase.PVT_DOGFOOD):
- self.assertEquals(None, hwid_utils.VerifyHWID(
- self.db, 'CHROMEBOOK D9I-F9U', bom, self.vpd, False,
- current_phase))
+ def testPrevImageId(self):
+ results = hwid_utils.EnumerateHWID(self.database, image_id=0, status='all',
+ comps={'region': ['us'],
+ 'storage': ['storage_0']})
+ self.assertEquals(len(results), self._CalculateNumCombinations(
+ flash_chip_field=None, cpu_field=2, region_field=1, storage_field=1))
- # Check for mismatched phase.
- self.assertRaisesRegexp(
- common.HWIDException,
- r"In DVT phase, expected an image name beginning with 'DVT' "
- r"\(but .* 'PVT2'\)",
- hwid_utils.VerifyHWID,
- self.db, 'CHROMEBOOK D9I-F9U', bom, self.vpd, False,
- phase.DVT)
-
- probed_results = copy.deepcopy(self.probed_results)
- probed_results['audio_codec']['generic'][1] = {'compact_str': 'HDMI 0'}
- bom = hwid_utils.GenerateBOMFromProbedResults(self.db, probed_results)
- self.assertRaises(
- common.HWIDException,
- hwid_utils.VerifyHWID, self.db, 'CHROMEBOOK D9I-F9U', bom,
- self.vpd, False, phase.PVT)
-
- # Test pre-MP recovery/root keys.
- probed_results = copy.deepcopy(self.probed_results)
- probed_results['key_root']['generic'][0].update(
- {'compact_str': 'kv3#key_root_premp'})
- probed_results['key_recovery']['generic'][0].update(
- {'compact_str': 'kv3#key_recovery_premp'})
- bom = hwid_utils.GenerateBOMFromProbedResults(self.db, probed_results)
- # Pre-MP recovery/root keys are fine in DVT...
- self.assertEquals(None, hwid_utils.VerifyHWID(
- self.db, 'CHROMEBOOK B5AW-5W', bom, self.vpd, False,
- phase.DVT))
- # ...but not in PVT
- self.assertRaisesRegexp(
- common.HWIDException,
- 'MP keys are required in PVT, but key_recovery component name is '
- "'key_recovery_premp' and key_root component name is 'key_root_premp'",
- hwid_utils.VerifyHWID, self.db, 'CHROMEBOOK D9I-F6A-A6B',
- bom, self.vpd, False, phase.PVT)
-
- # Test deprecated component.
- probed_results = copy.deepcopy(self.probed_results)
- probed_results['ro_main_firmware']['generic'][0].update(
- {'compact_str': 'mv2#ro_main_firmware_1'})
- bom = hwid_utils.GenerateBOMFromProbedResults(self.db, probed_results)
- self.assertRaisesRegexp(
- common.HWIDException, r'Not in RMA mode. Found deprecated component of '
- r"'ro_main_firmware': 'ro_main_firmware_1'",
- hwid_utils.VerifyHWID, self.db, 'CHROMEBOOK D9I-H9T', bom,
- self.vpd, False, phase.PVT)
-
- # Test deprecated component is allowed in rma mode.
- self.assertEquals(None, hwid_utils.VerifyHWID(
- self.db, 'CHROMEBOOK D9I-H9T', bom, self.vpd, True,
- phase.PVT))
-
- # Test unqualified component.
- probed_results = copy.deepcopy(self.probed_results)
- probed_results['dram']['generic'][0].update(
- {'vendor': 'DRAM 2', 'size': '8G'})
- bom = hwid_utils.GenerateBOMFromProbedResults(self.db, probed_results)
- self.assertRaisesRegexp(
- common.HWIDException, r'Found unqualified component of '
- r"'dram': 'dram_2' in Phase\(PVT\)",
- hwid_utils.VerifyHWID, self.db,
- 'CHROMEBOOK D9I-E8A-A5F', bom,
- self.vpd, False, phase.PVT)
-
- # Test unqualified component is allowed in early builds: PROTO/EVT/DVT.
- self.assertEquals(None, hwid_utils.VerifyHWID(
- self.db, 'CHROMEBOOK A5AT-PC', bom, self.vpd, False,
- phase.EVT))
-
- def testDecodeHWID(self):
- """Tests HWID decoding."""
- identity, bom = hwid_utils.DecodeHWID(self.db, 'CHROMEBOOK D9I-F9U')
- data = {
- 'audio_codec_field': 1,
- 'battery_field': 3,
- 'firmware_field': 0,
- 'storage_field': 0,
- 'bluetooth_field': 0,
- 'video_field': 0,
- 'display_panel_field': 0,
- 'cellular_field': 0,
- 'keyboard_field': 0,
- 'dram_field': 0,
- 'cpu_field': 5}
- self.assertEquals(data, bom.encoded_fields)
-
- parsed_result = hwid_utils.ParseDecodedHWID(
- self.db, bom, identity)
- self.assertEquals(parsed_result['project'], 'CHROMEBOOK')
- self.assertEquals(parsed_result['binary_string'], '000111110100000101')
- self.assertEquals(parsed_result['image_id'], 'PVT2')
- self.assertEquals(parsed_result['components'], {
- 'key_recovery': [{
- 'key_recovery_mp': {
- 'compact_str': Value('kv3#key_recovery_mp', is_re=False)}}],
- 'cellular': [{None: None}],
- 'ro_main_firmware': [{
- 'ro_main_firmware_0': {
- 'compact_str': Value('mv2#ro_main_firmware_0', is_re=False)}}],
- 'battery': [{
- 'battery_huge': {
- 'tech': Value('Battery Li-ion', is_re=False),
- 'size': Value('10000000', is_re=False)}}],
- 'hash_gbb': [{
- 'hash_gbb_0': {
- 'compact_str': Value('gv2#hash_gbb_0', is_re=False)}}],
- 'bluetooth': [{
- 'bluetooth_0': {
- 'bcd': Value('0001', is_re=False),
- 'idVendor': Value('0123', is_re=False),
- 'idProduct': Value('abcd', is_re=False)}}],
- 'key_root': [{
- 'key_root_mp': {
- 'compact_str': Value('kv3#key_root_mp', is_re=False)}}],
- 'video': [{
- 'camera_0': {
- 'idVendor': Value('4567', is_re=False),
- 'type': Value('webcam', is_re=False),
- 'idProduct': Value('abcd', is_re=False)}}],
- 'audio_codec': [
- {'codec_1': {'compact_str': Value('Codec 1', is_re=False)}},
- {'hdmi_1': {'compact_str': Value('HDMI 1', is_re=False)}}],
- 'keyboard': [
- {'keyboard_us': {'compact_str': Value('xkb:us::eng',
- is_re=False)}}],
- 'dram': [{
- 'dram_0': {
- 'vendor': Value('DRAM 0', is_re=False),
- 'size': Value('4G', is_re=False)}}],
- 'storage': [{
- 'storage_0': {
- 'serial': Value('#123456', is_re=False),
- 'type': Value('SSD', is_re=False),
- 'size': Value('16G', is_re=False)}}],
- 'display_panel': [{'display_panel_0': None}],
- 'ro_ec_firmware':[{
- 'ro_ec_firmware_0': {
- 'compact_str': Value('ev2#ro_ec_firmware_0', is_re=False)}}],
- 'cpu': [{
- 'cpu_5': {
- 'cores': Value('4', is_re=False),
- 'name': Value('CPU @ 2.80GHz', is_re=False)}}]})
+ def testNoResult(self):
+ results = hwid_utils.EnumerateHWID(self.database, image_id=0, status='all',
+ comps={'storage': ['storage_999']})
+ self.assertEquals(len(results), 0)
class DatabaseBuilderTest(unittest.TestCase):
-
def setUp(self):
yaml_utils.ParseMappingAsOrderedDict(loader=yaml.Loader, dumper=yaml.Dumper)
self.probed_results = json_utils.LoadFile(
- os.path.join(TEST_DATA_PATH, 'test_builder_probe_results.json'))
+ os.path.join(_TEST_DATA_PATH, 'test_builder_probe_results.json'))
self.output_path = tempfile.mktemp()
def tearDown(self):
@@ -345,7 +355,7 @@
region=['tw', 'jp'], chassis=['FOO', 'BAR'])
# If not in Chroot, the checksum is not updated.
verify_checksum = sys_utils.InChroot()
- database.Database.LoadFile(self.output_path, verify_checksum)
+ Database.LoadFile(self.output_path, verify_checksum)
# Check the value.
with open(self.output_path, 'r') as f:
db = yaml.load(f.read())
@@ -395,10 +405,10 @@
with mock.patch('__builtin__.raw_input', return_value='y'):
hwid_utils.UpdateDatabase(self.output_path, None, db,
add_null_comp=['touchpad', 'chassis'])
- new_db = database.Database.LoadFile(self.output_path, verify_checksum)
- self.assertIn({'touchpad': None},
+ new_db = Database.LoadFile(self.output_path, verify_checksum)
+ self.assertIn({'touchpad': []},
new_db.encoded_fields['touchpad_field'].values())
- self.assertIn({'chassis': None},
+ self.assertIn({'chassis': []},
new_db.encoded_fields['chassis_field'].values())
# Add a component without a new image_id.
@@ -410,7 +420,7 @@
with mock.patch('__builtin__.raw_input', return_value='y'):
hwid_utils.UpdateDatabase(self.output_path, probed_result, db)
- new_db = database.Database.LoadFile(self.output_path, verify_checksum)
+ new_db = Database.LoadFile(self.output_path, verify_checksum)
self.assertIn({'touchpad_field': 0}, new_db.pattern.pattern[0]['fields'])
# Delete bluetooth, and add region and chassis.
@@ -418,7 +428,7 @@
self.output_path, None, db, 'DVT',
add_default_comp=None, del_comp=['bluetooth'],
region=['us'], chassis=['NEW'])
- new_db = database.Database.LoadFile(self.output_path, verify_checksum)
+ new_db = Database.LoadFile(self.output_path, verify_checksum)
# Check the value.
self.assertEquals(new_db.project, 'CHROMEBOOK')
self.assertEquals(new_db.image_id, {0: 'EVT', 1: 'DVT'})
@@ -494,7 +504,7 @@
'status': 'unqualified',
'values': None})
hwid_utils.UpdateDatabase(self.output_path, self.probed_results[0], db)
- new_db = database.Database.LoadFile(self.output_path, False)
+ new_db = Database.LoadFile(self.output_path, False)
comp_dict = new_db.components.components_dict
self.assertEquals(
comp_dict['mainboard']['items']['mainboard_default'],
@@ -503,101 +513,6 @@
'values': None})
-class GenerateBOMFromProbedResultsTest(unittest.TestCase):
- def setUp(self):
- self.database = database.Database.LoadFile(os.path.join(TEST_DATA_PATH,
- 'test_db.yaml'))
- self.results = json_utils.LoadFile(
- os.path.join(TEST_DATA_PATH, 'test_probe_result.json'))
- self.boms = [hwid_utils.GenerateBOMFromProbedResults(self.database,
- probed_results)
- for probed_results in self.results]
-
- def testProbeResultToBOM(self):
- bom = self.boms[0]
- self.assertEquals('CHROMEBOOK', bom.project)
- self.assertEquals(0, bom.encoding_pattern_index)
- self.assertEquals(0, bom.image_id)
- self.assertEquals({
- 'audio_codec': [('codec_1', {'compact_str': Value('Codec 1')}, None),
- ('hdmi_1', {'compact_str': Value('HDMI 1')}, None)],
- 'battery': [('battery_huge',
- {'tech': Value('Battery Li-ion'),
- 'size': Value('10000000')},
- None)],
- 'bluetooth': [('bluetooth_0',
- {'idVendor': Value('0123'), 'idProduct': Value('abcd'),
- 'bcd': Value('0001')},
- None)],
- 'cellular': [(None, None, "Missing 'cellular' component")],
- 'cpu': [('cpu_5',
- {'name': Value('CPU @ 2.80GHz'), 'cores': Value('4')},
- None)],
- 'display_panel': [('display_panel_0', None, None)],
- 'dram': [('dram_0',
- {'vendor': Value('DRAM 0'), 'size': Value('4G')},
- None)],
- 'ec_flash_chip': [('ec_flash_chip_0',
- {'compact_str': Value('EC Flash Chip')},
- None)],
- 'embedded_controller': [('embedded_controller_0',
- {'compact_str': Value('Embedded Controller')},
- None)],
- 'flash_chip': [('flash_chip_0',
- {'compact_str': Value('Flash Chip')},
- None)],
- 'hash_gbb': [('hash_gbb_0',
- {'compact_str': Value('gv2#hash_gbb_0')},
- None)],
- 'key_recovery': [('key_recovery_0',
- {'compact_str': Value('kv3#key_recovery_0')},
- None)],
- 'key_root': [('key_root_0',
- {'compact_str': Value('kv3#key_root_0')},
- None)],
- 'keyboard': [(None,
- {'compact_str': 'xkb:us::eng'},
- "Component class 'keyboard' is unprobeable")],
- 'ro_ec_firmware': [('ro_ec_firmware_0',
- {'compact_str': Value('ev2#ro_ec_firmware_0')},
- None)],
- 'ro_main_firmware': [('ro_main_firmware_0',
- {'compact_str': Value('mv2#ro_main_firmware_0')},
- None)],
- 'storage': [('storage_0',
- {'type': Value('SSD'), 'size': Value('16G'),
- 'serial': Value(r'^#123\d+$', is_re=True)},
- None)],
- 'video': [('camera_0',
- {'idVendor': Value('4567'), 'idProduct': Value('abcd'),
- 'type': Value('webcam')},
- None)]},
- bom.components)
- self.assertEquals({
- 'audio_codec': 1,
- 'battery': 3,
- 'bluetooth': 0,
- 'cellular': 0,
- 'cpu': 5,
- 'display_panel': 0,
- 'dram': 0,
- 'ec_flash_chip': 0,
- 'embedded_controller': 0,
- 'firmware': 0,
- 'flash_chip': 0,
- 'keyboard': None,
- 'storage': 0,
- 'video': 0}, bom.encoded_fields)
-
-
-class GetHWIDBundleNameTest(unittest.TestCase):
- def testWithProjectName(self):
- self.assertEqual(hwid_utils.GetHWIDBundleName('abc'),
- 'hwid_v3_bundle_ABC.sh')
- self.assertEqual(hwid_utils.GetHWIDBundleName('ABC'),
- 'hwid_v3_bundle_ABC.sh')
-
-
if __name__ == '__main__':
logging.basicConfig(level=logging.INFO)
unittest.main()
diff --git a/py/hwid/v3/identity.py b/py/hwid/v3/identity.py
index 9478066..a854f2d 100644
--- a/py/hwid/v3/identity.py
+++ b/py/hwid/v3/identity.py
@@ -20,7 +20,7 @@
Given the encoding scheme, the `<encoded_body>` can be decoded into a binary
string which length is greater than 5. The binary string contains 3 parts:
- 1. The 1st (left most) digit is the `encode_pattern_index`,
+ 1. The 1st (left most) digit is the `encoding_pattern_index`,
which value can be either 0 or 1.
2. The 2ed to the 5th digits is a 4-bit big-endian integer of the `image_id`.
3. The reset of the digits is called `components_bitset`. A
@@ -30,7 +30,7 @@
beyond `Identiy`'s business.
For example, if the binary string is `0 0010 0111010101011`, then we have:
- 1. `encode_pattern_index` = 0
+ 1. `encoding_pattern_index` = 0
2. `image_id` = 2
3. `components_bitset` = '0111010101011'
@@ -110,11 +110,11 @@
Properties:
project: A string of the name of the Chromebook project.
encoded_string: A string of the HWID encoded string.
- encode_pattern_index: A integer of the encode pattern index.
- encode_pattern_index: A integer of the image id.
+ encoding_pattern_index: A integer of the encode pattern index.
+ image_id: A integer of the image id.
components_bitset: A binary string ends with '1'.
"""
- def __init__(self, project, encoded_string, encode_pattern_index, image_id,
+ def __init__(self, project, encoded_string, encoding_pattern_index, image_id,
components_bitset):
"""Constructor.
@@ -124,13 +124,13 @@
"""
self.project = project
self.encoded_string = encoded_string
- self.encode_pattern_index = encode_pattern_index
+ self.encoding_pattern_index = encoding_pattern_index
self.image_id = image_id
self.components_bitset = components_bitset
# TODO(yhong): Remove this property since other package shouldn't care
# about the whole binary string.
- self.binary_string = ('{0:01b}'.format(encode_pattern_index) +
+ self.binary_string = ('{0:01b}'.format(encoding_pattern_index) +
'{0:04b}'.format(image_id) +
components_bitset)
@@ -145,7 +145,7 @@
@staticmethod
def GenerateFromBinaryString(encoding_scheme, project,
- encode_pattern_index, image_id,
+ encoding_pattern_index, image_id,
components_bitset):
"""Generates an instance of Identity from the given 3 parts of the binary
string.
@@ -156,7 +156,7 @@
Args:
encoding_scheme: The encoding scheme used when this HWID was generated.
project: A string of the Chromebook project name.
- encode_pattern_index: An integer of the encode pattern index.
+ encoding_pattern_index: An integer of the encode pattern index.
image_id: An integer of the image id.
compoents_bitset: A binary string ends with '1'.
@@ -168,13 +168,13 @@
_VerifyProjectPart(project)
_VerifyPart(lambda val: val in [0, 1],
- 'encode_pattern_index', encode_pattern_index)
+ 'encoding_pattern_index', encoding_pattern_index)
_VerifyPart(lambda val: val in range(1 << 4), 'image_id', image_id)
_VerifyPart(lambda val: val and not set(val) - set('01') and val[-1] == '1',
'components_bitset', components_bitset)
# Generate the binary string with paddings.
- binary_string = ('{0:01b}'.format(encode_pattern_index) +
+ binary_string = ('{0:01b}'.format(encoding_pattern_index) +
'{0:04b}'.format(image_id) +
components_bitset)
binary_string += '0' * converter.GetPaddingLength(len(binary_string))
@@ -187,7 +187,7 @@
for idx in xrange(0, len(encoded_body_with_checksum),
converter.DASH_INSERTION_WIDTH)])
- return Identity(project, encoded_string, encode_pattern_index, image_id,
+ return Identity(project, encoded_string, encoding_pattern_index, image_id,
components_bitset)
@staticmethod
@@ -224,9 +224,9 @@
_VerifyPart(lambda val: len(val) > 5, 'binary_string', binary_string)
- encode_pattern_index = int(binary_string[0], 2)
+ encoding_pattern_index = int(binary_string[0], 2)
image_id = int(binary_string[1:5], 2)
components_bitset = binary_string[5:]
- return Identity(project, encoded_string, encode_pattern_index, image_id,
+ return Identity(project, encoded_string, encoding_pattern_index, image_id,
components_bitset)
diff --git a/py/hwid/v3/identity_unittest.py b/py/hwid/v3/identity_unittest.py
index dc0786d..6e2c800 100755
--- a/py/hwid/v3/identity_unittest.py
+++ b/py/hwid/v3/identity_unittest.py
@@ -44,7 +44,7 @@
class _Sample(dict):
- _ARGS = ['encoded_string', 'project', 'encode_pattern_index', 'image_id',
+ _ARGS = ['encoded_string', 'project', 'encoding_pattern_index', 'image_id',
'components_bitset', 'encoding_scheme']
def __init__(self, *args):
@@ -86,7 +86,7 @@
class IdentityGenerateFromBinaryStringTest(unittest.TestCase,
_IdentityGeneratorTestBase):
- NEEDED_ARGS = ['encoding_scheme', 'project', 'encode_pattern_index',
+ NEEDED_ARGS = ['encoding_scheme', 'project', 'encoding_pattern_index',
'image_id', 'components_bitset']
def GenerateIdentity(self, **kwargs):
@@ -102,10 +102,10 @@
self.doTestInvalid(_SAMPLES[0], project='proj')
def testBadEncodePatternIndex(self):
- # The encode_pattern_index is either 0 or 1.
- self.doTestInvalid(_SAMPLES[0], encode_pattern_index=-1)
- self.doTestInvalid(_SAMPLES[0], encode_pattern_index=2)
- self.doTestInvalid(_SAMPLES[0], encode_pattern_index=3)
+ # The encoding_pattern_index is either 0 or 1.
+ self.doTestInvalid(_SAMPLES[0], encoding_pattern_index=-1)
+ self.doTestInvalid(_SAMPLES[0], encoding_pattern_index=2)
+ self.doTestInvalid(_SAMPLES[0], encoding_pattern_index=3)
def testBadImageId(self):
# The range of the image_id is 0~15.
diff --git a/py/hwid/v3/testdata/NEW_TEST_PROJECT b/py/hwid/v3/testdata/NEW_TEST_PROJECT
deleted file mode 100644
index ab9631b..0000000
--- a/py/hwid/v3/testdata/NEW_TEST_PROJECT
+++ /dev/null
@@ -1,381 +0,0 @@
-checksum: 3a8951e004c7bc3193b83840fe1d24519553ea25
-project: CHROMEBOOK
-
-encoding_patterns:
- 0: default
- 1: new pattern
-
-image_id:
- 0: EVT
- 1: DVT
- 2: PVT
- 3: PVT2
- 4: PVT3
-
-pattern:
- - image_ids: [0, 1]
- encoding_scheme: base32
- fields:
- - audio_codec: 1
- - battery: 2
- - bluetooth: 0
- - cellular: 1
- - cpu: 1 # 5 bits
- - display_panel: 0
- - dram: 1
- - keyboard: 1
- - video: 0
- - cpu: 1
- - storage: 2 # 10 bits
- - cpu: 1
- - firmware: 1
- - firmware: 1
- - dram: 1 # 14 bits
-
- - image_ids: [2, 3]
- encoding_scheme: base8192
- fields:
- - audio_codec: 1
- - battery: 2
- - bluetooth: 0
- - cellular: 1
- - cpu: 1 # 5 bits
- - display_panel: 0
- - dram: 1
- - keyboard: 1
- - video: 0
- - cpu: 1
- - storage: 2 # 10 bits
- - cpu: 1
- - firmware: 1
- - firmware: 1
- - dram: 1 # 14 bits
-
- # Remove display_panel and cellular, and add firmware_keys field
- # Add SKU that has no audio_codec
- - image_ids: [4]
- encoding_scheme: base8192
- fields:
- - audio_codec: 3
- - battery: 2
- - bluetooth: 0
- - cpu: 3
- - dram: 2
- - keyboard: 1
- - video: 0
- - storage: 2
- - firmware: 2
- - firmware_keys: 2
-
-encoded_fields:
- audio_codec:
- 0:
- audio_codec: [codec_0, hdmi_0]
- 1:
- audio_codec: [codec_1, hdmi_1]
- 2:
- audio_codec: NULL
- battery:
- 0:
- battery: battery_small
- 1:
- battery: battery_medium
- 2:
- battery: battery_large
- 3:
- battery: battery_huge
- bluetooth:
- 0:
- bluetooth: bluetooth_0
- cellular:
- 0:
- cellular: NULL
- 1:
- cellular: cellular_0
- cpu:
- 0:
- cpu: cpu_0
- 1:
- cpu: cpu_1
- 2:
- cpu: cpu_2
- 3:
- cpu: cpu_3
- 4:
- cpu: cpu_4
- 5:
- cpu: cpu_5
- display_panel:
- 0:
- display_panel: display_panel_0
- dram:
- 0:
- dram: dram_0
- 1:
- dram: dram_1
- 2:
- dram: dram_2
- ec_flash_chip:
- 0:
- ec_flash_chip: ec_flash_chip_0
- embedded_controller:
- 0:
- embedded_controller: embedded_controller_0
- flash_chip:
- 0:
- flash_chip: flash_chip_0
- keyboard:
- 0:
- keyboard: keyboard_us
- 1:
- keyboard: keyboard_gb
- storage:
- 0:
- storage: storage_0
- 1:
- storage: storage_1
- 2:
- storage: storage_2
- video:
- 0:
- video: camera_0
- firmware:
- 0:
- hash_gbb: hash_gbb_0
- key_recovery: key_recovery_mp
- key_root: key_root_mp
- ro_ec_firmware: ro_ec_firmware_0
- ro_main_firmware: ro_main_firmware_0
- 1:
- hash_gbb: hash_gbb_0
- key_recovery: key_recovery_mp
- key_root: key_root_mp
- ro_ec_firmware: ro_ec_firmware_0
- ro_main_firmware: ro_main_firmware_1
- 2:
- hash_gbb: hash_gbb_0
- key_recovery: key_recovery_premp
- key_root: key_root_premp
- ro_ec_firmware: ro_ec_firmware_0
- ro_main_firmware: ro_main_firmware_0
- firmware_keys:
- 0:
- firmware_keys: firmware_keys_premp
- 1:
- firmware_keys: firmware_keys_mp
-
-components:
- audio_codec:
- items:
- codec_0:
- values: { compact_str: Codec 0 }
-
- codec_1:
- values: { compact_str: Codec 1 }
-
- hdmi_0:
- values: { compact_str: HDMI 0 }
-
- hdmi_1:
- values: { compact_str: HDMI 1 }
-
- battery:
- items:
- battery_small:
- values:
- tech: Battery Li-ion
- size: '2500000'
-
- battery_medium:
- values:
- tech: Battery Li-ion
- size: '5000000'
-
- battery_large:
- values:
- tech: Battery Li-ion
- size: '7500000'
-
- battery_huge:
- values:
- tech: Battery Li-ion
- size: '10000000'
-
- bluetooth:
- items:
- bluetooth_0:
- values:
- idVendor: '0123'
- idProduct: abcd
- bcd: '0001'
-
- cellular:
- items:
- cellular_0:
- values:
- idVendor: 89ab
- idProduct: abcd
- name: Cellular Card
-
- cpu:
- items:
- cpu_0:
- values:
- name: CPU @ 1.80GHz
- cores: '4'
-
- cpu_1:
- values:
- name: CPU @ 2.00GHz
- cores: '4'
-
- cpu_2:
- values:
- name: CPU @ 2.20GHz
- cores: '4'
-
- cpu_3:
- values:
- name: CPU @ 2.40GHz
- cores: '4'
-
- cpu_4:
- values:
- name: CPU @ 2.60GHz
- cores: '4'
-
- cpu_5:
- values:
- name: CPU @ 2.80GHz
- cores: '4'
-
- display_panel:
- probeable: False
- items:
- display_panel_0:
- values: NULL
-
- dram:
- items:
- dram_0:
- values:
- vendor: DRAM 0
- size: 4G
-
- dram_1:
- values:
- vendor: DRAM 1
- size: 4G
-
- dram_2:
- status: unqualified
- values:
- vendor: DRAM 2
- size: 8G
-
- ec_flash_chip:
- items:
- ec_flash_chip_0:
- values: { compact_str: EC Flash Chip }
-
- embedded_controller:
- items:
- embedded_controller_0:
- values: { compact_str: Embedded Controller }
-
- flash_chip:
- items:
- flash_chip_0:
- values: { compact_str: Flash Chip }
-
- keyboard:
- probeable: False
- items:
- keyboard_gb:
- values: NULL
-
- keyboard_us:
- values: NULL
-
- storage:
- items:
- storage_0:
- values:
- type: SSD
- size: 16G
- serial: '#123456'
-
- storage_1:
- values:
- type: SSD
- size: 32G
- serial: '#123456'
-
- storage_2:
- values:
- type: HDD
- size: 500G
- serial: '#123456'
-
- video:
- items:
- camera_0:
- values:
- idVendor: '4567'
- idProduct: abcd
- type: webcam
-
- hash_gbb:
- items:
- hash_gbb_0:
- values: { compact_str: gv2#hash_gbb_0 }
-
- key_recovery:
- items:
- key_recovery_mp:
- values: { compact_str : kv3#key_recovery_mp }
- key_recovery_premp:
- values: { compact_str : kv3#key_recovery_premp }
-
- key_root:
- items:
- key_root_mp:
- values: { compact_str: kv3#key_root_mp }
- key_root_premp:
- values: { compact_str: kv3#key_root_premp }
-
- firmware_keys:
- items:
- firmware_keys_premp:
- values:
- key_recovery: kv3#key_recovery_premp
- key_root: kv3#key_root_premp
- firmware_keys_mp:
- values:
- key_recovery: kv3#key_recovery_mp
- key_root: kv3#key_root_mp
-
- ro_ec_firmware:
- items:
- ro_ec_firmware_0:
- values: { compact_str: ev2#ro_ec_firmware_0 }
-
- ro_main_firmware:
- items:
- ro_main_firmware_0:
- values: { compact_str: mv2#ro_main_firmware_0 }
- ro_main_firmware_1:
- status: deprecated
- values: { compact_str: mv2#ro_main_firmware_1 }
-
-rules:
-- name: device_info.set_image_id
- evaluate: SetImageId('PVT3')
-
-- name: device_info.component.keyboard
- evaluate: >
- SetComponent(
- 'keyboard', LookupMap(GetDeviceInfo('component.keyboard'), {
- 'us': 'keyboard_us',
- 'gb': 'keyboard_gb'
- }))
diff --git a/py/hwid/v3/testdata/TEST_PROJECT b/py/hwid/v3/testdata/TEST_PROJECT
index 0d28c98..6b38dd7 100644
--- a/py/hwid/v3/testdata/TEST_PROJECT
+++ b/py/hwid/v3/testdata/TEST_PROJECT
@@ -1,9 +1,9 @@
-checksum: 477a3f89bc4e8e2e2531ee7f4ce953bd6368e6ae
+checksum: xxx
+
project: CHROMEBOOK
encoding_patterns:
0: default
- 1: new pattern
image_id:
0: EVT
@@ -14,41 +14,43 @@
pattern:
- image_ids: [0, 1]
encoding_scheme: base32
- fields:
+ fields: # only 1 bit for cpu_field, no flash_chip_field
- audio_codec_field: 1
- battery_field: 2
- bluetooth_field: 0
- cellular_field: 1
- cpu_field: 1 # 5 bits
- display_panel_field: 0
- - dram_field: 1
- - keyboard_field: 1
- - video_field: 0
- - cpu_field: 1
- - storage_field: 2 # 10 bits
- - cpu_field: 1
+ - dram_field: 2
+ - embedded_controller_field: 0
- firmware_field: 1
- - firmware_field: 1
- - dram_field: 1 # 14 bits
+ - firmware_keys_field: 1
+ - keyboard_field: 1 # 10 bits
+ - region_field: 2
+ - storage_field: 2
+ - video_field: 0 # 14 bits
- image_ids: [2, 3]
encoding_scheme: base8192
fields:
- audio_codec_field: 1
- - battery_field: 2
+ - battery_field: 2 # 3 bits
- bluetooth_field: 0
- cellular_field: 1
- - cpu_field: 1 # 5 bits
+ - cpu_field: 1
- display_panel_field: 0
- - dram_field: 1
+ - dram_field: 2
+ - embedded_controller_field: 0
+ - firmware_field: 1 # 8 bits
+ - firmware_keys_field: 1
- keyboard_field: 1
+ - cpu_field: 1
+ - region_field: 2 # 13 bits
+ - storage_field: 2
- video_field: 0
- - cpu_field: 1
- - storage_field: 2 # 10 bits
- - cpu_field: 1
- - firmware_field: 1
- - firmware_field: 1
- - dram_field: 1 # 14 bits
+
+ - flash_chip_field: 0
+ - cpu_field: 1 # 16 bits
encoded_fields:
audio_codec_field:
@@ -58,19 +60,19 @@
audio_codec: [codec_1, hdmi_1]
battery_field:
0:
- battery: battery_small
+ battery: battery_unsupported
1:
- battery: battery_medium
+ battery: battery_deprecated
2:
- battery: battery_large
+ battery: battery_unqualified
3:
- battery: battery_huge
+ battery: battery_supported
bluetooth_field:
0:
bluetooth: bluetooth_0
cellular_field:
0:
- cellular: NULL
+ cellular: []
1:
cellular: cellular_0
cpu_field:
@@ -93,15 +95,29 @@
0:
dram: dram_0
1:
- dram: dram_1
+ dram:
+ - dram_1
+ - dram_1
2:
- dram: dram_2
- ec_flash_chip_field:
- 0:
- ec_flash_chip: ec_flash_chip_0
+ dram:
+ - dram_2
+ - dram_2
+ - dram_2
embedded_controller_field:
0:
embedded_controller: embedded_controller_0
+ firmware_field:
+ 0:
+ ro_ec_firmware: ro_ec_firmware_0
+ ro_main_firmware: ro_main_firmware_0
+ 1:
+ ro_ec_firmware: ro_ec_firmware_0
+ ro_main_firmware: ro_main_firmware_1
+ firmware_keys_field:
+ 0:
+ firmware_keys: firmware_keys_premp
+ 1:
+ firmware_keys: firmware_keys_mp
flash_chip_field:
0:
flash_chip: flash_chip_0
@@ -110,6 +126,7 @@
keyboard: keyboard_us
1:
keyboard: keyboard_gb
+ region_field: !region_field [us, tw]
storage_field:
0:
storage: storage_0
@@ -120,25 +137,6 @@
video_field:
0:
video: camera_0
- firmware_field:
- 0:
- hash_gbb: hash_gbb_0
- key_recovery: key_recovery_mp
- key_root: key_root_mp
- ro_ec_firmware: ro_ec_firmware_0
- ro_main_firmware: ro_main_firmware_0
- 1:
- hash_gbb: hash_gbb_0
- key_recovery: key_recovery_mp
- key_root: key_root_mp
- ro_ec_firmware: ro_ec_firmware_0
- ro_main_firmware: ro_main_firmware_1
- 2:
- hash_gbb: hash_gbb_0
- key_recovery: key_recovery_premp
- key_root: key_root_premp
- ro_ec_firmware: ro_ec_firmware_0
- ro_main_firmware: ro_main_firmware_0
components:
audio_codec:
@@ -157,22 +155,26 @@
battery:
items:
- battery_small:
+ battery_unsupported:
+ status: unsupported
values:
tech: Battery Li-ion
size: '2500000'
- battery_medium:
+ battery_deprecated:
+ status: deprecated
values:
tech: Battery Li-ion
size: '5000000'
- battery_large:
+ battery_unqualified:
+ status: unqualified
values:
tech: Battery Li-ion
size: '7500000'
- battery_huge:
+ battery_supported:
+ status: supported
values:
tech: Battery Li-ion
size: '10000000'
@@ -226,10 +228,10 @@
cores: '4'
display_panel:
- probeable: False
items:
display_panel_0:
- values: NULL
+ values:
+ fake_flag: flag_value
dram:
items:
@@ -244,21 +246,26 @@
size: 4G
dram_2:
- status: unqualified
values:
vendor: DRAM 2
size: 8G
- ec_flash_chip:
- items:
- ec_flash_chip_0:
- values: { compact_str: EC Flash Chip }
-
embedded_controller:
items:
embedded_controller_0:
values: { compact_str: Embedded Controller }
+ firmware_keys:
+ items:
+ firmware_keys_premp:
+ values:
+ key_recovery: kv3#key_recovery_premp
+ key_root: kv3#key_root_premp
+ firmware_keys_mp:
+ values:
+ key_recovery: kv3#key_recovery_mp
+ key_root: kv3#key_root_mp
+
flash_chip:
items:
flash_chip_0:
@@ -274,6 +281,21 @@
values:
compact_str: xkb:us::eng
+ region: !region_component
+
+ ro_ec_firmware:
+ items:
+ ro_ec_firmware_0:
+ values: { compact_str: ev2#ro_ec_firmware_0 }
+
+ ro_main_firmware:
+ items:
+ ro_main_firmware_0:
+ status: deprecated
+ values: { compact_str: mv2#ro_main_firmware_0 }
+ ro_main_firmware_1:
+ values: { compact_str: mv2#ro_main_firmware_1 }
+
storage:
items:
storage_0:
@@ -302,38 +324,6 @@
idProduct: abcd
type: webcam
- hash_gbb:
- items:
- hash_gbb_0:
- values: { compact_str: gv2#hash_gbb_0 }
-
- key_recovery:
- items:
- key_recovery_mp:
- values: { compact_str : kv3#key_recovery_mp }
- key_recovery_premp:
- values: { compact_str : kv3#key_recovery_premp }
-
- key_root:
- items:
- key_root_mp:
- values: { compact_str: kv3#key_root_mp }
- key_root_premp:
- values: { compact_str: kv3#key_root_premp }
-
- ro_ec_firmware:
- items:
- ro_ec_firmware_0:
- values: { compact_str: ev2#ro_ec_firmware_0 }
-
- ro_main_firmware:
- items:
- ro_main_firmware_0:
- values: { compact_str: mv2#ro_main_firmware_0 }
- ro_main_firmware_1:
- status: deprecated
- values: { compact_str: mv2#ro_main_firmware_1 }
-
rules:
- name: device_info.set_image_id
evaluate: SetImageId('PVT2')
@@ -343,67 +333,8 @@
evaluate: SetComponent('cellular', 'cellular_0')
otherwise: SetComponent('cellular', None)
-- name: device_info.component.keyboard
- evaluate: >
- SetComponent(
- 'keyboard', LookupMap(GetDeviceInfo('component.keyboard'), {
- 'us': 'keyboard_us',
- 'gb': 'keyboard_gb'
- }))
-
-- name: device_info.component.dram
- evaluate: >
- SetComponent(
- 'dram', LookupMap(GetDeviceInfo('component.dram'), {
- 'foo': 'dram_0',
- 'bar': 'dram_1',
- 'unqualified': 'dram_2'
- }))
-
-- name: device_info.component.audio_codec
- evaluate: >
- SetComponent(
- 'audio_codec', LookupMap(GetDeviceInfo('component.audio_codec'), {
- 'set_0': ['codec_0', 'hdmi_0'],
- 'set_1': ['codec_1', 'hdmi_1']
- }))
-
- name: verify.components.rule_1
- when: ComponentEq('audio_codec', 'Codec 1') and
- ComponentEq('audio_codec', 'HDMI 1')
+ when: ComponentEq('audio_codec', 'codec_1') and
+ ComponentEq('audio_codec', 'hdmi_1')
evaluate:
- - Assert(ComponentEq('battery', 'battery_huge'))
- - Assert(ComponentEq('hash_gbb', 'hash_gbb_0'))
- - Assert(ComponentEq('key_recovery', 'key_recovery_mp'))
- - Assert(ComponentEq('key_root', 'key_root_mp'))
- - Assert(ComponentEq('ro_ec_firmware', 'ro_ec_firmware_0'))
- - Assert(ComponentEq('ro_main_firmware', 'ro_main_firmware_0'))
-
-- name: verify.components.rule_3
- when: ComponentEq('dram', 'dram_0') and ComponentEq('cellular', 'cellular_0')
- evaluate:
- - Assert(ComponentEq('battery', 'huge_battery'))
- - Assert(ComponentIn('cpu', ['cpu_1', 'cpu_2']))
-
-- name: verify.components.rule_4
- when: not ComponentEq('cpu', None)
- evaluate:
- - Assert((not ComponentEq('battery', 'battery_small')) or
- (ComponentIn('cpu', ['cpu_1', 'cpu_2', 'cpu_3'])))
-
-
-- name: verify.sku.us
- evaluate: >
- ComponentEq('audio_codec', ['hdmi_1', 'codec_1']) and
- ComponentEq('cpu', 'cpu_5') and
- ComponentEq('battery', 'battery_huge') and
- ComponentEq('keyboard', 'US') and
- ComponentEq('storage', ['SSD', '16G'])
-
-- name: verify.sku.gb
- evaluate: >
- ComponentEq('audio_codec', ['hdmi_1', 'codec_1']) and
- ComponentEq('cpu', 'cpu_4') and
- ComponentEq('battery', 'battery_medium') and
- ComponentEq('keyboard', 'GB') and
- ComponentEq('storage', ['HDD', '500G'])
+ - Assert(ComponentIn('cpu', ['cpu_3', 'cpu_4', 'cpu_5']))
diff --git a/py/hwid/v3/testdata/TEST_PROJECT_invalid_probed_results b/py/hwid/v3/testdata/TEST_PROJECT_invalid_probed_results
new file mode 100644
index 0000000..5a2ab0c
--- /dev/null
+++ b/py/hwid/v3/testdata/TEST_PROJECT_invalid_probed_results
@@ -0,0 +1,350 @@
+{
+ "audio_codec": {
+ "codec_0": [
+ ],
+ "codec_1": [
+ {
+ "compact_str": "Codec XXXXXXXXXXXXXX"
+ }
+ ],
+ "hdmi_0": [
+ ],
+ "hdmi_1": [
+ {
+ "compact_str": "HDMI 1"
+ }
+ ]
+ },
+ "battery": {
+ "battery_unsupported": [
+ ],
+ "battery_deprecated": [
+ ],
+ "battery_unqualified": [
+ ],
+ "battery_supported": [
+ {
+ "tech": "Battery Li-ion",
+ "size": "10000000"
+ }
+ ]
+ },
+ "bluetooth": {
+ "bluetooth_0": [
+ {
+ "idVendor": "0123",
+ "idProduct": "abcd",
+ "bcd": "0001"
+ }
+ ]
+ },
+ "cellular": {
+ "cellular_0": [
+ ]
+ },
+ "cpu": {
+ "cpu_0": [
+ ],
+ "cpu_1": [
+ ],
+ "cpu_2": [
+ ],
+ "cpu_3": [
+ ],
+ "cpu_4": [
+ {
+ "name": "CPU @ 2.60GHz",
+ "cores": "4"
+ }
+ ],
+ "cpu_5": [
+ ]
+ },
+ "display_panel": {
+ "display_panel_0": [
+ {
+ "fake_flag": "flag_value"
+ }
+ ]
+ },
+ "dram": {
+ "dram_0": [
+ ],
+ "dram_1": [
+ {
+ "vendor": "DRAM 1",
+ "size": "4G"
+ },
+ {
+ "vendor": "DRAM 1",
+ "size": "4G"
+ }
+ ],
+ "dram_2": [
+ ]
+ },
+ "embedded_controller": {
+ "embedded_controller_0": [
+ {
+ "compact_str": "Embedded Controller"
+ }
+ ]
+ },
+ "firmware_keys": {
+ "firmware_keys_premp": [
+ {
+ "key_recovery": "kv3#key_recovery_premp",
+ "key_root": "kv3#key_root_premp"
+ }
+ ],
+ "firmware_keys_mp": [
+ ]
+ },
+ "flash_chip": {
+ "flash_chip_0": [
+ {
+ "compact_str": "Flash Chip"
+ }
+ ]
+ },
+ "keyboard": {
+ "keyboard_us": [
+ {
+ "compact_str": "xkb:gb::eng"
+ }
+ ],
+ "keyboard_gb": [
+ ]
+ },
+ "region": {
+ "tw": [
+ {
+ "region_code": "tw"
+ }
+ ],
+ "us": [
+ ]
+ },
+ "ro_ec_firmware": {
+ "ro_ec_firmware_0": [
+ {
+ "compact_str": "ev2#ro_ec_firmware_0"
+ }
+ ]
+ },
+ "ro_main_firmware": {
+ "ro_main_firmware_0": [
+ ],
+ "ro_main_firmware_1": [
+ {
+ "compact_str": "mv2#ro_main_firmware_1"
+ }
+ ]
+ },
+ "storage": {
+ "storage_0": [
+ {
+ "type": "SSD",
+ "size": "16G",
+ "serial": "#123456"
+ }
+ ],
+ "storage_1": [
+ ],
+ "storage_2": [
+ ]
+ },
+ "video": {
+ "camera_0": [
+ {
+ "idVendor": "4567",
+ "idProduct": "abcd",
+ "type": "webcam"
+ }
+ ]
+ }
+}
+##### SPLITLINE #####
+{
+ "audio_codec": {
+ "codec_0": [
+ ],
+ "codec_1": [
+ {
+ "compact_str": "Codec 1"
+ }
+ ],
+ "hdmi_0": [
+ ],
+ "hdmi_1": [
+ {
+ "compact_str": "HDMI 1"
+ }
+ ]
+ },
+ "battery": {
+ "battery_unsupported": [
+ ],
+ "battery_deprecated": [
+ ],
+ "battery_unqualified": [
+ ],
+ "battery_supported": [
+ {
+ "tech": "Battery Li-ion",
+ "size": "10000000"
+ }
+ ]
+ },
+ "bluetooth": {
+ "bluetooth_0": [
+ {
+ "idVendor": "0123",
+ "idProduct": "abcd",
+ "bcd": "0001"
+ }
+ ]
+ },
+ "cellular": {
+ "cellular_0": [
+ ]
+ },
+ "cpu": {
+ "cpu_0": [
+ ],
+ "cpu_1": [
+ ],
+ "cpu_2": [
+ ],
+ "cpu_3": [
+ ],
+ "cpu_4": [
+ {
+ "name": "CPU @ 2.60GHz",
+ "cores": "4"
+ }
+ ],
+ "cpu_5": [
+ ]
+ },
+ "display_panel": {
+ "display_panel_0": [
+ {
+ "fake_flag": "flag_value"
+ }
+ ]
+ },
+ "dram": {
+ "dram_0": [
+ ],
+ "dram_1": [
+ {
+ "vendor": "DRAM 1",
+ "size": "4G"
+ },
+ {
+ "vendor": "DRAM 1",
+ "size": "4G"
+ }
+ ],
+ "dram_2": [
+ ]
+ },
+ "embedded_controller": {
+ "embedded_controller_0": [
+ {
+ "compact_str": "Embedded Controller"
+ }
+ ]
+ },
+ "firmware_keys": {
+ "firmware_keys_premp": [
+ {
+ "key_recovery": "kv3#key_recovery_premp",
+ "key_root": "kv3#key_root_premp"
+ }
+ ],
+ "firmware_keys_mp": [
+ ]
+ },
+ "flash_chip": {
+ "flash_chip_0": [
+ {
+ "compact_str": "Flash Chip"
+ }
+ ]
+ },
+ "keyboard": {
+ "keyboard_us": [
+ {
+ "compact_str": "xkb:gb::eng"
+ }
+ ],
+ "keyboard_gb": [
+ ]
+ },
+ "region": {
+ "tw": [
+ {
+ "region_code": "tw"
+ }
+ ],
+ "us": [
+ ]
+ },
+ "ro_ec_firmware": {
+ "ro_ec_firmware_0": [
+ {
+ "compact_str": "ev2#ro_ec_firmware_0"
+ }
+ ]
+ },
+ "ro_main_firmware": {
+ "ro_main_firmware_0": [
+ ],
+ "ro_main_firmware_1": [
+ {
+ "compact_str": "mv2#ro_main_firmware_1"
+ }
+ ]
+ },
+ "storage": {
+ "storage_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX_too_general": [
+ {
+ "type": "SSD"
+ }
+ ],
+ "storage_1": [
+ ],
+ "storage_2": [
+ ]
+ },
+ "video": {
+ "camera_0": [
+ {
+ "idVendor": "4567",
+ "idProduct": "abcd",
+ "type": "webcam"
+ }
+ ]
+ }
+}
+##### SPLITLINE #####
+{
+ "audio_codec": {
+ "codec_0": [
+ ],
+ "codec_1": [
+ {
+ "compact_str": "Codec 1"
+ }
+ ],
+ "hdmi_0": [
+ ],
+ "hdmi_1": [
+ {
+ "compact_str": "HDMI 1"
+ }
+ ]
+ }
+}
diff --git a/py/hwid/v3/testdata/TEST_PROJECT_probed_results b/py/hwid/v3/testdata/TEST_PROJECT_probed_results
new file mode 100644
index 0000000..ff9987e
--- /dev/null
+++ b/py/hwid/v3/testdata/TEST_PROJECT_probed_results
@@ -0,0 +1,318 @@
+{
+ "audio_codec": {
+ "codec_0": [
+ ],
+ "codec_1": [
+ {
+ "compact_str": "Codec 1"
+ }
+ ],
+ "hdmi_0": [
+ ],
+ "hdmi_1": [
+ {
+ "compact_str": "HDMI 1"
+ }
+ ]
+ },
+ "battery": {
+ "battery_unsupported": [
+ ],
+ "battery_deprecated": [
+ ],
+ "battery_unqualified": [
+ ],
+ "battery_supported": [
+ {
+ "tech": "Battery Li-ion",
+ "size": "10000000"
+ }
+ ]
+ },
+ "bluetooth": {
+ "bluetooth_0": [
+ {
+ "idVendor": "0123",
+ "idProduct": "abcd",
+ "bcd": "0001"
+ }
+ ]
+ },
+ "cellular": {
+ "cellular_0": [
+ ]
+ },
+ "cpu": {
+ "cpu_0": [
+ ],
+ "cpu_1": [
+ ],
+ "cpu_2": [
+ ],
+ "cpu_3": [
+ ],
+ "cpu_4": [
+ ],
+ "cpu_5": [
+ {
+ "name": "CPU @ 2.80GHz",
+ "cores": "4"
+ }
+ ]
+ },
+ "display_panel": {
+ "display_panel_0": [
+ {
+ "fake_flag": "flag_value"
+ }
+ ]
+ },
+ "dram": {
+ "dram_0": [
+ ],
+ "dram_1": [
+ {
+ "vendor": "DRAM 1",
+ "size": "4G"
+ },
+ {
+ "vendor": "DRAM 1",
+ "size": "4G"
+ }
+ ],
+ "dram_2": [
+ ]
+ },
+ "embedded_controller": {
+ "embedded_controller_0": [
+ {
+ "compact_str": "Embedded Controller"
+ }
+ ]
+ },
+ "firmware_keys": {
+ "firmware_keys_premp": [
+ ],
+ "firmware_keys_mp": [
+ {
+ "key_recovery": "kv3#key_recovery_mp",
+ "key_root": "kv3#key_root_mp"
+ }
+ ]
+ },
+ "flash_chip": {
+ "flash_chip_0": [
+ {
+ "compact_str": "Flash Chip"
+ }
+ ]
+ },
+ "keyboard": {
+ "keyboard_us": [
+ {
+ "compact_str": "xkb:us::eng"
+ }
+ ],
+ "keyboard_gb": [
+ ]
+ },
+ "region": {
+ "tw": [
+ {
+ "region_code": "tw"
+ }
+ ],
+ "us": [
+ ]
+ },
+ "ro_ec_firmware": {
+ "ro_ec_firmware_0": [
+ {
+ "compact_str": "ev2#ro_ec_firmware_0"
+ }
+ ]
+ },
+ "ro_main_firmware": {
+ "ro_main_firmware_0": [
+ ],
+ "ro_main_firmware_1": [
+ {
+ "compact_str": "mv2#ro_main_firmware_1"
+ }
+ ]
+ },
+ "storage": {
+ "storage_0": [
+ {
+ "type": "SSD",
+ "size": "16G",
+ "serial": "#123456"
+ }
+ ],
+ "storage_1": [
+ ],
+ "storage_2": [
+ ]
+ },
+ "video": {
+ "camera_0": [
+ {
+ "idVendor": "4567",
+ "idProduct": "abcd",
+ "type": "webcam"
+ }
+ ]
+ }
+}
+##### SPLITLINE #####
+{
+ "audio_codec": {
+ "codec_0": [
+ {
+ "compact_str": "Codec 0"
+ }
+ ],
+ "codec_1": [
+ ],
+ "hdmi_0": [
+ {
+ "compact_str": "HDMI 0"
+ }
+ ],
+ "hdmi_1": [
+ ]
+ },
+ "battery": {
+ "battery_unsupported": [
+ ],
+ "battery_deprecated": [
+ ],
+ "battery_unqualified": [
+ ],
+ "battery_supported": [
+ {
+ "tech": "Battery Li-ion",
+ "size": "10000000"
+ }
+ ]
+ },
+ "bluetooth": {
+ "bluetooth_0": [
+ {
+ "idVendor": "0123",
+ "idProduct": "abcd",
+ "bcd": "0001"
+ }
+ ]
+ },
+ "cpu": {
+ "cpu_0": [
+ ],
+ "cpu_1": [
+ ],
+ "cpu_2": [
+ ],
+ "cpu_3": [
+ ],
+ "cpu_4": [
+ {
+ "name": "CPU @ 2.60GHz",
+ "cores": "4"
+ }
+ ],
+ "cpu_5": [
+ ]
+ },
+ "display_panel": {
+ "display_panel_0": [
+ {
+ "fake_flag": "flag_value"
+ }
+ ]
+ },
+ "dram": {
+ "dram_0": [
+ {
+ "vendor": "DRAM 0",
+ "size": "4G"
+ }
+ ],
+ "dram_1": [
+ ],
+ "dram_2": [
+ ]
+ },
+ "embedded_controller": {
+ "embedded_controller_0": [
+ {
+ "compact_str": "Embedded Controller"
+ }
+ ]
+ },
+ "firmware_keys": {
+ "firmware_keys_premp": [
+ ],
+ "firmware_keys_mp": [
+ {
+ "key_recovery": "kv3#key_recovery_mp",
+ "key_root": "kv3#key_root_mp"
+ }
+ ]
+ },
+ "flash_chip": {
+ "flash_chip_0": [
+ {
+ "compact_str": "Flash Chip"
+ }
+ ]
+ },
+ "keyboard": {
+ "keyboard_us": [
+ {
+ "compact_str": "xkb:us::eng"
+ }
+ ],
+ "keyboard_gb": [
+ ]
+ },
+ "region": {
+ },
+ "ro_ec_firmware": {
+ "ro_ec_firmware_0": [
+ {
+ "compact_str": "ev2#ro_ec_firmware_0"
+ }
+ ]
+ },
+ "ro_main_firmware": {
+ "ro_main_firmware_0": [
+ {
+ "compact_str": "mv2#ro_main_firmware_0"
+ }
+ ],
+ "ro_main_firmware_1": [
+ ]
+ },
+ "storage": {
+ "storage_0": [
+ {
+ "type": "SSD",
+ "size": "16G",
+ "serial": "#123456"
+ }
+ ],
+ "storage_1": [
+ ],
+ "storage_2": [
+ ]
+ },
+ "video": {
+ "camera_0": [
+ {
+ "idVendor": "4567",
+ "idProduct": "abcd",
+ "type": "webcam"
+ }
+ ]
+ }
+}
diff --git a/py/hwid/v3/testdata/new_test_probe_result_hwid_utils.json b/py/hwid/v3/testdata/new_test_probe_result_hwid_utils.json
deleted file mode 100644
index f772b14..0000000
--- a/py/hwid/v3/testdata/new_test_probe_result_hwid_utils.json
+++ /dev/null
@@ -1,549 +0,0 @@
-[
- {
- "audio_codec": {
- "generic": [
- {
- "compact_str": "Codec 1"
- },
- {
- "compact_str": "HDMI 1"
- }
- ]
- },
- "battery": {
- "generic": [
- {
- "size": "10000000",
- "tech": "Battery Li-ion"
- }
- ]
- },
- "bluetooth": {
- "generic": [
- {
- "bcd": "0001",
- "idProduct": "abcd",
- "idVendor": "0123"
- }
- ]
- },
- "cpu": {
- "generic": [
- {
- "cores": "4",
- "name": "CPU @ 2.80GHz"
- }
- ]
- },
- "dram": {
- "generic": [
- {
- "size": "4G",
- "vendor": "DRAM 0"
- }
- ]
- },
- "ec_flash_chip": {
- "generic": [
- {
- "compact_str": "EC Flash Chip"
- }
- ]
- },
- "embedded_controller": {
- "generic": [
- {
- "compact_str": "Embedded Controller"
- }
- ]
- },
- "firmware_keys": {
- "generic": [
- {
- "key_recovery": "kv3#key_recovery_mp",
- "key_root": "kv3#key_root_mp"
- }
- ]
- },
- "flash_chip": {
- "generic": [
- {
- "compact_str": "Flash Chip"
- }
- ]
- },
- "hash_gbb": {
- "generic": [
- {
- "compact_str": "gv2#hash_gbb_0"
- }
- ]
- },
- "key_recovery": {
- "generic": [
- {
- "compact_str": "kv3#key_recovery_mp"
- }
- ]
- },
- "key_root": {
- "generic": [
- {
- "compact_str": "kv3#key_root_mp"
- }
- ]
- },
- "keyboard": {
- "generic": [
- {
- "compact_str": "xkb:us::eng"
- }
- ]
- },
- "region": {
- "vpd.ro.region": [
- {
- "region_code": "us"
- }
- ]
- },
- "ro_ec_firmware": {
- "generic": [
- {
- "compact_str": "ev2#ro_ec_firmware_0"
- }
- ]
- },
- "ro_main_firmware": {
- "generic": [
- {
- "compact_str": "mv2#ro_main_firmware_0"
- }
- ]
- },
- "storage": {
- "generic": [
- {
- "serial": "#123456",
- "size": "16G",
- "type": "SSD"
- }
- ]
- },
- "video": {
- "generic": [
- {
- "idProduct": "abcd",
- "idVendor": "4567",
- "type": "webcam"
- }
- ]
- }
- },
- {
- "battery": {
- "generic": [
- {
- "size": "10000000",
- "tech": "Battery Li-ion"
- }
- ]
- },
- "bluetooth": {
- "generic": [
- {
- "bcd": "0001",
- "idProduct": "abcd",
- "idVendor": "0123"
- }
- ]
- },
- "cpu": {
- "generic": [
- {
- "cores": "4",
- "name": "CPU @ 2.80GHz"
- }
- ]
- },
- "dram": {
- "generic": [
- {
- "size": "4G",
- "vendor": "DRAM 0"
- }
- ]
- },
- "ec_flash_chip": {
- "generic": [
- {
- "compact_str": "EC Flash Chip"
- }
- ]
- },
- "embedded_controller": {
- "generic": [
- {
- "compact_str": "Embedded Controller"
- }
- ]
- },
- "firmware_keys": {
- "generic": [
- {
- "key_recovery": "kv3#key_recovery_mp",
- "key_root": "kv3#key_root_mp"
- }
- ]
- },
- "flash_chip": {
- "generic": [
- {
- "compact_str": "Flash Chip"
- }
- ]
- },
- "hash_gbb": {
- "generic": [
- {
- "compact_str": "gv2#hash_gbb_0"
- }
- ]
- },
- "key_recovery": {
- "generic": [
- {
- "compact_str": "kv3#key_recovery_mp"
- }
- ]
- },
- "key_root": {
- "generic": [
- {
- "compact_str": "kv3#key_root_mp"
- }
- ]
- },
- "keyboard": {
- "generic": [
- {
- "compact_str": "xkb:us::eng"
- }
- ]
- },
- "region": {
- "vpd.ro.region": [
- {
- "region_code": "us"
- }
- ]
- },
- "ro_ec_firmware": {
- "generic": [
- {
- "compact_str": "ev2#ro_ec_firmware_0"
- }
- ]
- },
- "ro_main_firmware": {
- "generic": [
- {
- "compact_str": "mv2#ro_main_firmware_0"
- }
- ]
- },
- "storage": {
- "generic": [
- {
- "serial": "#123456",
- "size": "16G",
- "type": "SSD"
- }
- ]
- },
- "video": {
- "generic": [
- {
- "idProduct": "abcd",
- "idVendor": "4567",
- "type": "webcam"
- }
- ]
- }
- },
- [
- {
- "audio_codec": {
- "generic": [
- {
- "compact_str": "Codec 1"
- },
- {
- "compact_str": "HDMI 1"
- }
- ]
- },
- "battery": {
- "generic": [
- {
- "size": "10000000",
- "tech": "Battery Li-ion"
- }
- ]
- },
- "bluetooth": {
- "generic": [
- {
- "bcd": "0001",
- "idProduct": "abcd",
- "idVendor": "0123"
- }
- ]
- },
- "cpu": {
- "generic": [
- {
- "cores": "4",
- "name": "CPU @ 2.80GHz"
- }
- ]
- },
- "dram": {
- "generic": [
- {
- "size": "4G",
- "vendor": "DRAM 0"
- }
- ]
- },
- "ec_flash_chip": {
- "generic": [
- {
- "compact_str": "EC Flash Chip"
- }
- ]
- },
- "embedded_controller": {
- "generic": [
- {
- "compact_str": "Embedded Controller"
- }
- ]
- },
- "flash_chip": {
- "generic": [
- {
- "compact_str": "Flash Chip"
- }
- ]
- },
- "keyboard": {
- "generic": [
- {
- "compact_str": "xkb:us::eng"
- }
- ]
- },
- "region": {
- "vpd.ro.region": [
- {
- "region_code": "us"
- }
- ]
- },
- "storage": {
- "generic": [
- {
- "serial": "#123456",
- "size": "16G",
- "type": "SSD"
- }
- ]
- },
- "video": {
- "generic": [
- {
- "idProduct": "abcd",
- "idVendor": "4567",
- "type": "webcam"
- }
- ]
- },
- "firmware_keys": {
- "generic": [
- {
- "key_recovery": "kv3#key_recovery_mp",
- "key_root": "kv3#key_root_mp"
- }
- ]
- },
- "hash_gbb": {
- "generic": [
- {
- "compact_str": "gv2#hash_gbb_0"
- }
- ]
- },
- "key_recovery": {
- "generic": [
- {
- "compact_str": "kv3#key_recovery_mp"
- }
- ]
- },
- "key_root": {
- "generic": [
- {
- "compact_str": "kv3#key_root_mp"
- }
- ]
- },
- "ro_ec_firmware": {
- "generic": [
- {
- "compact_str": "ev2#ro_ec_firmware_0"
- }
- ]
- },
- "ro_main_firmware": {
- "generic": [
- {
- "compact_str": "mv2#ro_main_firmware_0"
- }
- ]
- }
- },
- {
- "battery": {
- "generic": [
- {
- "size": "10000000",
- "tech": "Battery Li-ion"
- }
- ]
- },
- "bluetooth": {
- "generic": [
- {
- "bcd": "0001",
- "idProduct": "abcd",
- "idVendor": "0123"
- }
- ]
- },
- "cpu": {
- "generic": [
- {
- "cores": "4",
- "name": "CPU @ 2.80GHz"
- }
- ]
- },
- "dram": {
- "generic": [
- {
- "size": "4G",
- "vendor": "DRAM 0"
- }
- ]
- },
- "ec_flash_chip": {
- "generic": [
- {
- "compact_str": "EC Flash Chip"
- }
- ]
- },
- "embedded_controller": {
- "generic": [
- {
- "compact_str": "Embedded Controller"
- }
- ]
- },
- "flash_chip": {
- "generic": [
- {
- "compact_str": "Flash Chip"
- }
- ]
- },
- "keyboard": {
- "generic": [
- {
- "compact_str": "xkb:us::eng"
- }
- ]
- },
- "region": {
- "vpd.ro.region": [
- {
- "region_code": "us"
- }
- ]
- },
- "storage": {
- "generic": [
- {
- "serial": "#123456",
- "size": "16G",
- "type": "SSD"
- }
- ]
- },
- "video": {
- "generic": [
- {
- "idProduct": "abcd",
- "idVendor": "4567",
- "type": "webcam"
- }
- ]
- },
- "firmware_keys": {
- "generic": [
- {
- "key_recovery": "kv3#key_recovery_mp",
- "key_root": "kv3#key_root_mp"
- }
- ]
- },
- "hash_gbb": {
- "generic": [
- {
- "compact_str": "gv2#hash_gbb_0"
- }
- ]
- },
- "key_recovery": {
- "generic": [
- {
- "compact_str": "kv3#key_recovery_mp"
- }
- ]
- },
- "key_root": {
- "generic": [
- {
- "compact_str": "kv3#key_root_mp"
- }
- ]
- },
- "ro_ec_firmware": {
- "generic": [
- {
- "compact_str": "ev2#ro_ec_firmware_0"
- }
- ]
- },
- "ro_main_firmware": {
- "generic": [
- {
- "compact_str": "mv2#ro_main_firmware_0"
- }
- ]
- }
- },
- null
- ]
-]
diff --git a/py/hwid/v3/testdata/test_db.yaml b/py/hwid/v3/testdata/test_db.yaml
index 490e45e..8ba07d0 100644
--- a/py/hwid/v3/testdata/test_db.yaml
+++ b/py/hwid/v3/testdata/test_db.yaml
@@ -1,5 +1,5 @@
# Dummy line before checksum
-checksum: 3ba13057b33d9df01da5c88e7a92beeb3ed9c3a5
+checksum: 564e6326dc7f9f963fdd9ad987da2ec39d6b92a9
# Dummy line after checksum
project: CHROMEBOOK
@@ -230,7 +230,7 @@
probeable: False
items:
display_panel_0:
- values: NULL
+ values: {fake_flag: flag_value}
dram:
items:
@@ -263,10 +263,10 @@
probeable: False
items:
keyboard_gb:
- values: NULL
+ values: { compact_str: 'meow' }
keyboard_us:
- values: NULL
+ values: { compact_str: 'xkb:us::eng' }
storage:
items:
diff --git a/py/hwid/v3/testdata/test_db_no_checksum.yaml b/py/hwid/v3/testdata/test_db_no_checksum.yaml
index 2823140..f4884fc 100644
--- a/py/hwid/v3/testdata/test_db_no_checksum.yaml
+++ b/py/hwid/v3/testdata/test_db_no_checksum.yaml
@@ -229,7 +229,7 @@
probeable: False
items:
display_panel_0:
- values: NULL
+ values: {fake_flag: flag_value}
dram:
items:
@@ -262,10 +262,10 @@
probeable: False
items:
keyboard_gb:
- values: NULL
+ values: { compact_str: 'meow' }
keyboard_us:
- values: NULL
+ values: { compact_str: 'xkb:us::eng' }
storage:
items:
diff --git a/py/hwid/v3/testdata/test_new_db.yaml b/py/hwid/v3/testdata/test_new_db.yaml
deleted file mode 100644
index 0a7413f..0000000
--- a/py/hwid/v3/testdata/test_new_db.yaml
+++ /dev/null
@@ -1,83 +0,0 @@
-# Dummy line before checksum
-checksum: a685a6882dea5cfb53830887bb76c08439a6f8ac
-# Dummy line after checksum
-project: CHROMEBOOK
-
-encoding_patterns:
- 0: default
-
-image_id:
- 0: EVT
- 1: DVT
-
-pattern:
- - image_ids: [0, 1]
- encoding_scheme: base32
- fields:
- - ro_main_firmware_field: 3
- - firmware_keys_field: 2
- - ro_ec_firmware_field: 3
- - audio_codec_field: 1
- - cellular_field: 2
-
-
-encoded_fields:
- firmware_keys_field:
- 0: {firmware_keys: firmware_keys_0}
- 1: {firmware_keys: firmware_keys_1}
- ro_main_firmware_field:
- 0: {ro_main_firmware: ro_main_firmware_0}
- 1: {ro_main_firmware: ro_main_firmware_1}
- ro_ec_firmware_field:
- 0: {ro_ec_firmware: ro_ec_firmware_0}
- 1: {ro_ec_firmware: ro_ec_firmware_1}
- audio_codec_field:
- 0: {audio_codec: audio_codec_default}
- cellular_field:
- 0: {cellular: cellular_default}
- 1: {cellular: NULL}
- 2: {cellular: cellular_a}
-
-components:
- firmware_keys:
- items:
- firmware_keys_0:
- values:
- key_root: kv3#key_root_0
- key_recovery: kv3#key_recovery_0
- firmware_keys_1:
- values:
- key_root: kv3#key_root_1
- key_recovery: kv3#key_recovery_1
- ro_main_firmware:
- items:
- ro_main_firmware_0:
- values: { compact_str: mv2#ro_main_firmware_0 }
- ro_main_firmware_1:
- status: deprecated
- values: { compact_str: mv2#ro_main_firmware_1 }
- ro_ec_firmware:
- items:
- ro_ec_firmware_0:
- values: { compact_str: ev2#ro_ec_firmware_0 }
- ro_ec_firmware_1:
- status: unsupported
- values: { compact_str: ev2#ro_ec_firmware_1 }
- audio_codec:
- items:
- audio_codec_default:
- default: True
- values: NULL
- cellular:
- items:
- cellular_default:
- default: True
- status: unsupported
- values: NULL
- cellular_a:
- values:
- compact_str: cellular_a
-
-rules:
-- name: device_info.set_image_id
- evaluate: SetImageId('EVT')
diff --git a/py/hwid/v3/testdata/test_probe_result.json b/py/hwid/v3/testdata/test_probe_result.json
deleted file mode 100644
index 4dfbbda..0000000
--- a/py/hwid/v3/testdata/test_probe_result.json
+++ /dev/null
@@ -1,250 +0,0 @@
-[
- {
- "audio_codec": {
- "generic": [
- {
- "compact_str": "Codec 1"
- },
- {
- "compact_str": "HDMI 1"
- }
- ]
- },
- "battery": {
- "generic": [
- {
- "size": "10000000",
- "tech": "Battery Li-ion"
- }
- ]
- },
- "bluetooth": {
- "generic": [
- {
- "bcd": "0001",
- "idProduct": "abcd",
- "idVendor": "0123"
- }
- ]
- },
- "cpu": {
- "generic": [
- {
- "cores": "4",
- "name": "CPU @ 2.80GHz"
- }
- ]
- },
- "dram": {
- "generic": [
- {
- "size": "4G",
- "vendor": "DRAM 0"
- }
- ]
- },
- "ec_flash_chip": {
- "generic": [
- {
- "compact_str": "EC Flash Chip"
- }
- ]
- },
- "embedded_controller": {
- "generic": [
- {
- "compact_str": "Embedded Controller"
- }
- ]
- },
- "firmware_keys": {
- "generic": [
- {
- "key_recovery": "kv3#key_recovery_0",
- "key_root": "kv3#key_root_0"
- }
- ]
- },
- "flash_chip": {
- "generic": [
- {
- "compact_str": "Flash Chip"
- }
- ]
- },
- "hash_gbb": {
- "generic": [
- {
- "compact_str": "gv2#hash_gbb_0"
- }
- ]
- },
- "key_recovery": {
- "generic": [
- {
- "compact_str": "kv3#key_recovery_0"
- }
- ]
- },
- "key_root": {
- "generic": [
- {
- "compact_str": "kv3#key_root_0"
- }
- ]
- },
- "keyboard": {
- "generic": [
- {
- "compact_str": "xkb:us::eng"
- }
- ]
- },
- "ro_ec_firmware": {
- "generic": [
- {
- "compact_str": "ev2#ro_ec_firmware_0"
- }
- ]
- },
- "ro_main_firmware": {
- "generic": [
- {
- "compact_str": "mv2#ro_main_firmware_0"
- }
- ]
- },
- "storage": {
- "generic": [
- {
- "serial": "#123456",
- "size": "16G",
- "type": "SSD"
- }
- ]
- },
- "video": {
- "generic": [
- {
- "idProduct": "abcd",
- "idVendor": "4567",
- "type": "webcam"
- }
- ]
- }
- },
- {
- "audio_codec": {
- "generic": [
- {
- "compact_str": "Codec 1"
- },
- {
- "compact_str": "HDMI 3"
- }
- ]
- }
- },
- {
- "storage": {
- "generic": [
- {
- "serial": "#1234aa",
- "size": "16G",
- "type": "SSD"
- }
- ]
- }
- },
- {
- "storage": {
- "generic": [
- {
- "size": "500G"
- }
- ]
- }
- },
- {
- "storage": {
- "generic": [
- {
- "foo": "bar"
- }
- ]
- }
- },
- {
- "region": {
- "vpd.ro.region": [
- {
- "region_code": "us"
- }
- ]
- }
- },
- {
- "firmware_keys": {
- "generic": [
- {
- "key_recovery": "kv3#key_recovery_0",
- "key_root": "kv3#key_root_0"
- }
- ]
- },
- "foo": null,
- "key_recovery": {
- "generic": [
- {
- "compact_str": "kv3#key_recovery_0"
- }
- ]
- },
- "key_root": {
- "generic": [
- {
- "compact_str": "kv3#key_root_0"
- }
- ]
- },
- "ro_ec_firmware": {
- "generic": [
- {
- "compact_str": "ev2#ro_ec_firmware_0"
- }
- ]
- },
- "ro_main_firmware": {
- "generic": [
- {
- "compact_str": "mv2#ro_main_firmware_0"
- }
- ]
- }
- },
- {
- "audio_codec": {
- "generic": [
- {
- "compact_str": "fake"
- }
- ]
- },
- "cellular": {
- "generic": [
- {
- "compact_str": "cellular_a"
- }
- ]
- }
- },
- {
- "cellular": {
- "generic": [
- {
- "compact_str": "invalid_value"
- }
- ]
- }
- }
-]
diff --git a/py/hwid/v3/testdata/test_probe_result_hwid_utils.json b/py/hwid/v3/testdata/test_probe_result_hwid_utils.json
deleted file mode 100644
index 370bed0..0000000
--- a/py/hwid/v3/testdata/test_probe_result_hwid_utils.json
+++ /dev/null
@@ -1,141 +0,0 @@
-{
- "audio_codec": {
- "generic": [
- {
- "compact_str": "Codec 1"
- },
- {
- "compact_str": "HDMI 1"
- }
- ]
- },
- "battery": {
- "generic": [
- {
- "size": "10000000",
- "tech": "Battery Li-ion"
- }
- ]
- },
- "bluetooth": {
- "generic": [
- {
- "bcd": "0001",
- "idProduct": "abcd",
- "idVendor": "0123"
- }
- ]
- },
- "cpu": {
- "generic": [
- {
- "cores": "4",
- "name": "CPU @ 2.80GHz"
- }
- ]
- },
- "dram": {
- "generic": [
- {
- "size": "4G",
- "vendor": "DRAM 0"
- }
- ]
- },
- "ec_flash_chip": {
- "generic": [
- {
- "compact_str": "EC Flash Chip"
- }
- ]
- },
- "embedded_controller": {
- "generic": [
- {
- "compact_str": "Embedded Controller"
- }
- ]
- },
- "firmware_keys": {
- "generic": [
- {
- "key_recovery": "kv3#key_recovery_mp",
- "key_root": "kv3#key_root_mp"
- }
- ]
- },
- "flash_chip": {
- "generic": [
- {
- "compact_str": "Flash Chip"
- }
- ]
- },
- "hash_gbb": {
- "generic": [
- {
- "compact_str": "gv2#hash_gbb_0"
- }
- ]
- },
- "key_recovery": {
- "generic": [
- {
- "compact_str": "kv3#key_recovery_mp"
- }
- ]
- },
- "key_root": {
- "generic": [
- {
- "compact_str": "kv3#key_root_mp"
- }
- ]
- },
- "keyboard": {
- "generic": [
- {
- "compact_str": "xkb:us::eng"
- }
- ]
- },
- "region": {
- "vpd.ro.region": [
- {
- "region_code": "us"
- }
- ]
- },
- "ro_ec_firmware": {
- "generic": [
- {
- "compact_str": "ev2#ro_ec_firmware_0"
- }
- ]
- },
- "ro_main_firmware": {
- "generic": [
- {
- "compact_str": "mv2#ro_main_firmware_0"
- }
- ]
- },
- "storage": {
- "generic": [
- {
- "serial": "#123456",
- "size": "16G",
- "type": "SSD"
- }
- ]
- },
- "video": {
- "generic": [
- {
- "idProduct": "abcd",
- "idVendor": "4567",
- "type": "webcam"
- }
- ]
- }
-}
diff --git a/py/hwid/v3/testdata/test_transformer_db.yaml b/py/hwid/v3/testdata/test_transformer_db.yaml
new file mode 100644
index 0000000..f398389
--- /dev/null
+++ b/py/hwid/v3/testdata/test_transformer_db.yaml
@@ -0,0 +1,93 @@
+# Dummy line before checksum
+checksum: not used here
+# Dummy line after checksum
+project: CHROMEBOOK
+
+encoding_patterns:
+ 0: default
+
+image_id:
+ 0: IMAGE_NAME_0
+ 1: IMAGE_NAME_1
+ 2: IMAGE_NAME_2
+ 3: IMAGE_NAME_3
+
+pattern:
+ - image_ids: [0, 1]
+ encoding_scheme: base32
+ fields: # This pattern doesn't contain `battery_field` and allowcates only
+ # only 2 bits for `audio_and_video_field`.
+ - cpu_field: 0
+ - audio_and_video_field: 2
+
+ - image_ids: [2]
+ encoding_scheme: base8192
+ fields: # This pattern only allowcates 2 bits for `audio_and_video_field`
+ # and 1 bit for `battery_field`.
+ - cpu_field: 0
+ - audio_and_video_field: 2
+ - battery_field: 1
+
+ - image_ids: [3]
+ encoding_scheme: base8192
+ fields:
+ - cpu_field: 5
+ - audio_and_video_field: 3
+ - battery_field: 2
+
+
+encoded_fields:
+ cpu_field:
+ 0: {cpu: cpu_0}
+
+ battery_field:
+ 0: {battery: battery_0}
+ 3: {battery: [battery_3, battery_3, battery_3]}
+
+ audio_and_video_field:
+ 0:
+ audio: []
+ video: []
+ 1:
+ audio: [audio_0]
+ video: []
+ 2:
+ audio: [audio_0, audio_1]
+ video: []
+ 3:
+ audio: []
+ video: [video_0]
+ 4:
+ audio: [audio_0]
+ video: [video_0]
+ 5:
+ audio: [audio_0, audio_1]
+ video: [video_0]
+ 6:
+ audio: []
+ video: [video_0, video_1]
+ 7:
+ audio: [audio_0]
+ video: [video_0, video_1]
+
+components:
+ cpu:
+ items:
+ cpu_0: {values: {unused_value: cpu_0}}
+
+ audio:
+ items:
+ audio_0: {values: {unused_value: audio_0}}
+ audio_1: {values: {unused_value: audio_1}}
+
+ video:
+ items:
+ video_0: {values: {unused_value: video_0}}
+ video_1: {values: {unused_value: video_1}}
+
+ battery:
+ items:
+ battery_0: {values: {unused_value: battery_0}}
+ battery_3: {values: {unused_value: battery_3}}
+
+rules: []
diff --git a/py/hwid/v3/transformer.py b/py/hwid/v3/transformer.py
index 9c7cc64..a3ac6e4 100644
--- a/py/hwid/v3/transformer.py
+++ b/py/hwid/v3/transformer.py
@@ -1,104 +1,16 @@
-# Copyright 2013 The Chromium OS Authors. All rights reserved.
+# Copyright 2018 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.
"""Implementation of HWID v3 encoder and decoder."""
import collections
-import pprint
+import logging
-import factory_common # pylint: disable=W0611
+import factory_common # pylint: disable=unused-import
from cros.factory.hwid.v3.bom import BOM
-from cros.factory.hwid.v3.bom import ProbedComponentResult
from cros.factory.hwid.v3 import common
from cros.factory.hwid.v3.identity import Identity
-from cros.factory.utils import type_utils
-
-
-def VerifyBOM(database, bom, probeable_only=False):
- """Verifies the data contained in the given BOM object matches the settings
- and definitions in the database.
-
- Because the components for each image ID might be different, for example a
- component might be removed in later build. We only verify the components in
- the target image ID, not all components listed in the database.
-
- When the BOM is decoded by HWID string, it would contain the information of
- every component recorded in the pattern. But if the BOM object is created by
- the probed result, it does not contain the unprobeable component before
- evaluating the rule. We should verify the probeable components only.
-
- Args:
- bom: The BOM object to verify.
- probeable_only: True to verify the probeable component only.
-
- Raises:
- HWIDException if verification fails.
- """
- if bom.project != database.project:
- raise common.HWIDException('Invalid project name. Expected %r, got %r' %
- (database.project, bom.project))
-
- if bom.encoding_pattern_index not in database.encoding_patterns:
- raise common.HWIDException('Invalid encoding pattern: %r' %
- bom.encoding_pattern_index)
- if bom.image_id not in database.image_id:
- raise common.HWIDException('Invalid image id: %r' % bom.image_id)
-
- # All the classes encoded in the pattern should exist in BOM.
- # Ignore unprobeable components if probeable_only is True.
- missing_comp = []
- expected_encoded_fields = database.pattern.GetFieldNames(bom.image_id)
- for comp_cls in database.GetActiveComponents(bom.image_id):
- if (comp_cls not in bom.components and
- (comp_cls in database.components.probeable or not probeable_only)):
- missing_comp.append(comp_cls)
- if missing_comp:
- raise common.HWIDException('Missing component classes: %r',
- ', '.join(sorted(missing_comp)))
-
- bom_encoded_fields = type_utils.MakeSet(bom.encoded_fields.keys())
- db_encoded_fields = type_utils.MakeSet(expected_encoded_fields)
- # Every encoded field defined in the database must present in BOM.
- if db_encoded_fields - bom_encoded_fields:
- raise common.HWIDException('Missing encoded fields in BOM: %r',
- ', '.join(sorted(db_encoded_fields -
- bom_encoded_fields)))
-
- # All the probeable component values in the BOM should exist in the
- # database.
- unknown_values = []
- for comp_cls, probed_values in bom.components.iteritems():
- if comp_cls not in database.components.probeable:
- continue
- for element in probed_values:
- probed_values = element.probed_values
- if probed_values is None:
- continue
- found_comps = database.components.MatchComponentsFromValues(
- comp_cls, probed_values, include_default=True)
- if not found_comps:
- unknown_values.append('%s:%s' % (comp_cls, pprint.pformat(
- probed_values, indent=0, width=1024)))
- if unknown_values:
- raise common.HWIDException('Unknown component values: %r' %
- ', '.join(sorted(unknown_values)))
-
- # All the encoded index should exist in the database.
- invalid_fields = []
- for field_name in expected_encoded_fields:
- # Ignore the field containing unprobeable component.
- if probeable_only and not all(
- [comp_cls in database.components.probeable
- for comp_cls in database.encoded_fields[field_name][0].keys()]):
- continue
- index = bom.encoded_fields[field_name]
- if index is None or index not in database.encoded_fields[field_name]:
- invalid_fields.append(field_name)
-
- if invalid_fields:
- raise common.HWIDException('Encoded fields %r have unknown indices' %
- ', '.join(sorted(invalid_fields)))
def BOMToIdentity(database, bom):
@@ -107,11 +19,53 @@
Args:
database: A Database object that is used to provide device-specific
information for encoding.
+ bom: A BOM object to be decoded.
Returns:
- A binary string.
+ An Identity object.
"""
- VerifyBOM(database, bom)
+ if bom.encoding_pattern_index not in database.encoding_patterns:
+ raise common.HWIDException(
+ 'Invalid encoding pattern: %r' % bom.encoding_pattern_index)
+
+ if bom.image_id not in database.image_id:
+ raise common.HWIDException('Invalid image id: %r' % bom.image_id)
+
+ extra_component_cls = (set(bom.components.keys())
+ - set(database.components.components_dict.keys()))
+ if extra_component_cls:
+ # It's ok for the BOM to contain more information than what we need.
+ logging.warning(
+ '%r will not be encoded into the HWID identity.', extra_component_cls)
+
+ # Try to encode every field and fail if some fields are missing or some fields
+ # are not listed in the encoding pattern.
+ encoded_fields = {}
+ for field_name, field_data in database.encoded_fields.iteritems():
+ for index, components in field_data.iteritems():
+ for comp_cls, comp_names in components.iteritems():
+ if (comp_cls not in bom.components or
+ sorted(comp_names) != bom.components[comp_cls]):
+ break
+ else:
+ encoded_fields[field_name] = index
+ break
+
+ expected_field_names = database.pattern.GetFieldNames(bom.image_id)
+ extra_field_names = set(encoded_fields.keys()) - expected_field_names
+ if extra_field_names:
+ raise common.HWIDException(
+ 'Extra fields for the pattern: %s' % ', '.join(extra_field_names))
+
+ missing_field_names = expected_field_names - set(encoded_fields.keys())
+ if missing_field_names:
+ raise common.HWIDException('Encoded fields %s has unknown indices' %
+ ', '.join(sorted(missing_field_names)))
+
+ for field_name, bit_length in database.pattern.GetFieldsBitLength(
+ bom.image_id).iteritems():
+ if encoded_fields[field_name] >= (2 ** bit_length):
+ raise common.HWIDException('Index overflow in field %r' % field_name)
encoding_scheme = database.pattern.GetEncodingScheme(bom.image_id)
@@ -122,7 +76,7 @@
# Fill in each bit.
bit_mapping = database.pattern.GetBitMapping(bom.image_id)
for index, (field, bit_offset) in bit_mapping.iteritems():
- binary_list[index] = (bom.encoded_fields[field] >> bit_offset) & 1
+ binary_list[index] = (encoded_fields[field] >> bit_offset) & 1
# Set stop bit.
binary_list[bit_length - 1] = 1
@@ -151,10 +105,24 @@
# TODO(yhong): Use identity.components_bitset directly.
stripped_binary_string = '00000' + identity.components_bitset[:-1]
- project = database.project
- encode_pattern_index = identity.encode_pattern_index
+ if identity.project != database.project:
+ raise common.HWIDException('Invalid project: %r' % identity.project)
+
+ if identity.encoding_pattern_index not in database.encoding_patterns:
+ raise common.HWIDException(
+ 'Invalid encoding pattern index: %r' % identity.encoding_pattern_index)
+
image_id = identity.image_id
+ # Re-generate the identity by the encoding scheme specified in the HWID
+ # database to verify whether the given identity is generated with the correct
+ # encoding scheme.
+ identity2 = Identity.GenerateFromEncodedString(
+ database.pattern.GetEncodingScheme(image_id), identity.encoded_string)
+ if identity != identity2:
+ raise common.HWIDException(
+ 'The hwid %r was generated with wrong encoding scheme.' % identity)
+
total_bit_length = database.pattern.GetTotalBitLength(image_id)
if len(stripped_binary_string) > total_bit_length:
raise common.HWIDException(
@@ -184,18 +152,13 @@
(field, encoded_fields[field]))
# Construct the components dict.
- components = collections.defaultdict(list)
+ components = {}
for field, index in encoded_fields.iteritems():
# pylint: disable=W0212
attr_dict = database._GetAttributesByIndex(field, index)
for comp_cls, attr_list in attr_dict.iteritems():
- if attr_list is None:
- components[comp_cls].append(ProbedComponentResult(
- None, None, common.MISSING_COMPONENT_ERROR(comp_cls)))
- else:
- for attrs in attr_list:
- components[comp_cls].append(ProbedComponentResult(
- attrs['name'], attrs['values'], None))
+ components[comp_cls] = []
+ for attrs in attr_list if attr_list is not None else []:
+ components[comp_cls].append(attrs['name'])
- return BOM(project, encode_pattern_index, image_id, components,
- encoded_fields)
+ return BOM(identity.encoding_pattern_index, image_id, components)
diff --git a/py/hwid/v3/transformer_unittest.py b/py/hwid/v3/transformer_unittest.py
index f7b2e5b..affe4f5 100755
--- a/py/hwid/v3/transformer_unittest.py
+++ b/py/hwid/v3/transformer_unittest.py
@@ -1,361 +1,181 @@
#!/usr/bin/env python
-# Copyright 2017 The Chromium OS Authors. All rights reserved.
+# Copyright 2018 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.
-import copy
import os
import unittest
import factory_common # pylint: disable=W0611
-from cros.factory.hwid.v3.bom import ProbedComponentResult
+from cros.factory.hwid.v3.bom import BOM
from cros.factory.hwid.v3.common import HWIDException
from cros.factory.hwid.v3.database import Database
-from cros.factory.hwid.v3 import identity as identity_utils
from cros.factory.hwid.v3.identity import Identity
from cros.factory.hwid.v3 import transformer
-from cros.factory.hwid.v3.transformer import BOMToIdentity
-from cros.factory.hwid.v3.transformer import IdentityToBOM
-from cros.factory.hwid.v3.transformer import VerifyBOM
-from cros.factory.hwid.v3 import hwid_utils
-from cros.factory.hwid.v3.rule import Value
-from cros.factory.utils import json_utils
-_TEST_DATA_PATH = os.path.join(os.path.dirname(__file__), 'testdata')
+_TEST_DATABASE_FILENAME = os.path.join(
+ os.path.dirname(__file__), 'testdata', 'test_transformer_db.yaml')
-def _EncodedStringToBOM(database, encoded_string):
- identity = hwid_utils.GetIdentityFromEncodedString(database, encoded_string)
- return transformer.IdentityToBOM(database, identity)
-
-
-class VerifyBOMTest(unittest.TestCase):
+class _TransformerTestBase(unittest.TestCase):
def setUp(self):
- self.database = Database.LoadFile(os.path.join(_TEST_DATA_PATH,
- 'test_db.yaml'))
- self.results = json_utils.LoadFile(
- os.path.join(_TEST_DATA_PATH, 'test_probe_result.json'))
- self.boms = [hwid_utils.GenerateBOMFromProbedResults(self.database,
- probed_results)
- for probed_results in self.results]
+ self.database = Database.LoadFile(_TEST_DATABASE_FILENAME,
+ verify_checksum=False)
+ self.test_data = [
+ # Simplest case.
+ ('001', BOM(0, 0, dict(cpu=['cpu_0'], audio=[], video=[]))),
- def testVerifyBOM(self):
- # Before evaluating rule to update the unprobeable component, we only verify
- # the probeable components.
- bom = self.boms[0]
- self.assertEquals(None, VerifyBOM(self.database, bom, True))
+ # battery_field is 0.
+ ('0001', BOM(0, 2, dict(cpu=['cpu_0'], audio=[], video=[],
+ battery=['battery_0']))),
- original_value = bom.project
- bom.project = 'FOO'
- with self.assertRaisesRegexp(HWIDException,
- r'Invalid project name. Expected .*, got .*'):
- VerifyBOM(self.database, bom, True)
- bom.project = original_value
+ # battery_field is 3.
+ ('00000000111', BOM(0, 3, dict(cpu=['cpu_0'], audio=[], video=[],
+ battery=['battery_3'] * 3))),
- original_value = bom.encoding_pattern_index
- bom.encoding_pattern_index = 2
- with self.assertRaisesRegexp(HWIDException, r'Invalid encoding pattern'):
- VerifyBOM(self.database, bom, True)
- bom.encoding_pattern_index = original_value
+ # audio_and_video_field is 2
+ ('1001', BOM(0, 2, dict(cpu=['cpu_0'], audio=['audio_1', 'audio_0'],
+ video=[], battery=['battery_0']))),
- original_value = bom.image_id
- bom.image_id = 6
- with self.assertRaisesRegexp(HWIDException, r'Invalid image id: .*'):
- VerifyBOM(self.database, bom, True)
- bom.image_id = original_value
+ # audio_and_video_field is 4
+ ('00000100001', BOM(0, 3, dict(cpu=['cpu_0'], audio=['audio_0'],
+ video=['video_0'],
+ battery=['battery_0']))),
- original_value = bom.encoded_fields['cpu']
- bom.encoded_fields['cpu'] = 8
- with self.assertRaisesRegexp(HWIDException,
- r'Encoded fields .* have unknown indices'):
- VerifyBOM(self.database, bom, True)
- bom.encoded_fields['cpu'] = original_value
-
- original_value = bom.components['cpu']
- bom.components['cpu'] = [ProbedComponentResult(
- 'cpu', {'name': Value('foo'), 'cores': Value('4')}, None)]
- with self.assertRaisesRegexp(HWIDException, r'Unknown component values:.*'):
- VerifyBOM(self.database, bom, True)
- bom.components['cpu'] = original_value
-
- original_value = bom.encoded_fields['cpu']
- bom.encoded_fields.pop('cpu')
- with self.assertRaisesRegexp(HWIDException,
- r'Missing encoded fields in BOM: .*'):
- VerifyBOM(self.database, bom, True)
- bom.encoded_fields['cpu'] = original_value
+ # audio_and_video_field is 7, battery_field is 3
+ ('00000111111', BOM(0, 3, dict(cpu=['cpu_0'],
+ audio=['audio_0'],
+ video=['video_0', 'video_1'],
+ battery=['battery_3'] * 3)))]
-class NewVerifyBOMTest(unittest.TestCase):
- """Test the new style of HWID database.
+class BOMToIdentityTest(_TransformerTestBase):
+ def testInvalidEncodingPatternIndex(self):
+ for invalid_encoding_pattern_index in [1, 2, 3, -1]:
+ bom = self.test_data[0][1]
+ bom.encoding_pattern_index = invalid_encoding_pattern_index
+ self.assertRaises(HWIDException,
+ transformer.BOMToIdentity, self.database, bom)
- - Split key_root and key_recovery to a new component: firmware_keys
- - Separate firmware field to let each field only has one component
- - Add default argument in audio_codec and cellular
- """
+ def testInvalidImageId(self):
+ for invalid_image_id in [4, 5, 6, -1]:
+ bom = self.test_data[0][1]
+ bom.image_id = invalid_image_id
+ self.assertRaises(HWIDException,
+ transformer.BOMToIdentity, self.database, bom)
- def setUp(self):
- self.database = Database.LoadFile(os.path.join(_TEST_DATA_PATH,
- 'test_new_db.yaml'))
- self.results = json_utils.LoadFile(
- os.path.join(_TEST_DATA_PATH, 'test_probe_result.json'))
- self.boms = [hwid_utils.GenerateBOMFromProbedResults(self.database,
- probed_results)
- for probed_results in self.results]
+ def testMissingEncodedField(self):
+ bom = self.test_data[1][1]
+ bom.RemoveComponent('battery')
+ self.assertRaises(HWIDException,
+ transformer.BOMToIdentity, self.database, bom)
- def testVerifyBOMWithDefault(self):
- self.assertEquals(None, VerifyBOM(self.database, self.boms[6], True))
+ # `audio_and_video_field` needs both audio and video components.
+ bom = self.test_data[2][1]
+ bom.RemoveComponent('audio')
+ self.assertRaises(HWIDException,
+ transformer.BOMToIdentity, self.database, bom)
+ def testTooManyEncodedField(self):
+ bom = self.test_data[0][1]
+ bom.SetComponent('battery', self.test_data[1][1].components['battery'])
+ self.assertRaises(HWIDException,
+ transformer.BOMToIdentity, self.database, bom)
-class DecoderTest(unittest.TestCase):
-
- def setUp(self):
- self.database = Database.LoadFile(os.path.join(_TEST_DATA_PATH,
- 'test_db.yaml'))
- self.results = json_utils.LoadFile(
- os.path.join(_TEST_DATA_PATH, 'test_probe_result.json'))
- self.expected_components_from_db = {
- 'audio_codec': [('codec_1', {'compact_str': Value('Codec 1')}, None),
- ('hdmi_1', {'compact_str': Value('HDMI 1')}, None)],
- 'battery': [('battery_huge',
- {'tech': Value('Battery Li-ion'),
- 'size': Value('10000000')},
- None)],
- 'bluetooth': [('bluetooth_0',
- {'idVendor': Value('0123'), 'idProduct': Value('abcd'),
- 'bcd': Value('0001')},
- None)],
- 'cellular': [(None, None, "Missing 'cellular' component")],
- 'cpu': [('cpu_5',
- {'name': Value('CPU @ 2.80GHz'), 'cores': Value('4')},
- None)],
- 'display_panel': [('display_panel_0', None, None)],
- 'dram': [('dram_0',
- {'vendor': Value('DRAM 0'), 'size': Value('4G')},
- None)],
- 'ec_flash_chip': [('ec_flash_chip_0',
- {'compact_str': Value('EC Flash Chip')},
- None)],
- 'embedded_controller': [('embedded_controller_0',
- {'compact_str': Value('Embedded Controller')},
- None)],
- 'flash_chip': [('flash_chip_0',
- {'compact_str': Value('Flash Chip')},
- None)],
- 'hash_gbb': [('hash_gbb_0',
- {'compact_str': Value('gv2#hash_gbb_0')},
- None)],
- 'key_recovery': [('key_recovery_0',
- {'compact_str': Value('kv3#key_recovery_0')},
- None)],
- 'key_root': [('key_root_0',
- {'compact_str': Value('kv3#key_root_0')},
- None)],
- 'keyboard': [('keyboard_us', None, None)],
- 'ro_ec_firmware': [('ro_ec_firmware_0',
- {'compact_str': Value('ev2#ro_ec_firmware_0')},
- None)],
- 'ro_main_firmware': [('ro_main_firmware_0',
- {'compact_str': Value('mv2#ro_main_firmware_0')},
- None)],
- 'storage': [('storage_0',
- {'type': Value('SSD'), 'size': Value('16G'),
- 'serial': Value(r'^#123\d+$', is_re=True)},
- None)],
- 'video': [('camera_0',
- {'idVendor': Value('4567'), 'idProduct': Value('abcd'),
- 'type': Value('webcam')},
- None)]}
-
- def _CheckBOM(self, reference_bom, bom):
- """Check the BOM decoded from HWID is equivalent to the reference BOM.
-
- reference_bom (generated from probed result) has all information of
- encoded_fields and component, but bom (generated from binary string) only
- has the information of the pattern. Therefore we check bom is the subset
- of the reference_bom.
- """
- self.assertEquals(reference_bom.project, bom.project)
- self.assertEquals(reference_bom.encoding_pattern_index,
- bom.encoding_pattern_index)
- self.assertEquals(reference_bom.image_id, bom.image_id)
- for comp_cls in bom.encoded_fields:
- self.assertEquals(reference_bom.encoded_fields[comp_cls],
- bom.encoded_fields[comp_cls])
- for comp_cls in bom.components:
- self.assertEquals(self.expected_components_from_db[comp_cls],
- bom.components[comp_cls])
-
- @staticmethod
- def _BinaryStringToBOM(database, binary_string):
- image_id = identity_utils.GetImageIdFromBinaryString(binary_string)
- encoding_scheme = database.pattern.GetEncodingScheme(image_id)
- encode_pattern_index = int(binary_string[0], 2)
- identity = Identity.GenerateFromBinaryString(
- encoding_scheme, database.project, encode_pattern_index, image_id,
- binary_string[5:])
- return IdentityToBOM(database, identity)
-
- def testBinaryStringToBOM(self):
- reference_bom = hwid_utils.GenerateBOMFromProbedResults(self.database,
- self.results[0])
- self.database.UpdateComponentsOfBOM(reference_bom, {
- 'keyboard': 'keyboard_us',
- 'display_panel': 'display_panel_0'})
- bom = self._BinaryStringToBOM(self.database, '0000000000111010000011')
- self._CheckBOM(reference_bom, bom)
-
- bom = self._BinaryStringToBOM(self.database, '0000000001111010000011')
- self.assertEquals(1, bom.encoded_fields['firmware'])
- self.assertEquals(2, self._BinaryStringToBOM(
- self.database, '0001000000111010000011').image_id)
- self.assertEquals(1, self._BinaryStringToBOM(
- self.database, '1000000000111010000011').encoding_pattern_index)
- self.assertRaisesRegexp(
- HWIDException, r"Invalid encoded field index: {'cpu': 6}",
- self._BinaryStringToBOM, self.database, '0000000000111000010011')
-
- def testIncompleteBinaryStringToBOM(self):
- # The latest pattern in the test database has 16 bits (plus the image ID and
- # the stop bit), with the last three bits being one two-bit storage_field,
- # and one one-bit cpu_field.
-
- # Test with 21 bits here. This should be regarded as a valid binary string
- # that was generated before we extended cpu_field.
- bom = self._BinaryStringToBOM(
- self.database,
- '00000' # image ID
- '0000111101000' # 13 bits, up through second cpu_field
- '00' # storage_field
- '1') # stop bit
- # Bit 15 is 1, which is the first cpu_field. The cpu_field should be decoded
- # as b01 = 1.
- self.assertEquals(1, bom.encoded_fields['cpu'])
- self.assertEquals(0, bom.encoded_fields['storage'])
-
- # Test with 20 bits here. This should be regarded as an incomplete bit chunk
- # for storage_field, i.e. storage_field was previously one bit (and some
- # HWIDs were generated) but it has since been extended to two bits.
- bom = self._BinaryStringToBOM(
- self.database,
- '00000' # image ID
- '0000111101000' # 12 bits, up through second cpu_field
- '1' # the incomplete storage_field chunk
- '1') # stop bit
- # The cpu_field should still be b01 = 1.
- self.assertEquals(1, bom.encoded_fields['cpu'])
- # The 1 in the incomplete two-bit storage field should be decoded as b1 = 1
- # instead of b10 = 2.
- self.assertEquals(1, bom.encoded_fields['storage'])
-
- def testDecode(self):
- reference_bom = hwid_utils.GenerateBOMFromProbedResults(self.database,
- self.results[0])
- self.database.UpdateComponentsOfBOM(reference_bom, {
- 'keyboard': 'keyboard_us', 'dram': 'dram_0',
- 'display_panel': 'display_panel_0'})
- bom = _EncodedStringToBOM(self.database, 'CHROMEBOOK AA5A-Y6L')
- self._CheckBOM(reference_bom, bom)
-
- bom = _EncodedStringToBOM(self.database, 'CHROMEBOOK C2H-I3Q-A6Q')
- reference_bom.image_id = 2
- self._CheckBOM(reference_bom, bom)
-
- def testPreviousVersionOfEncodedString(self):
- bom = self._BinaryStringToBOM(self.database, '000000000011101000001')
- self.assertEquals(1, bom.encoded_fields['cpu'])
- bom = _EncodedStringToBOM(self.database, 'CHROMEBOOK AA5A-Q7Z')
- self.assertEquals(1, bom.encoded_fields['cpu'])
-
- def testDecodeRegion(self):
- db = Database.LoadFile(
- os.path.join(_TEST_DATA_PATH, 'test_db_regions.yaml'))
- bom = _EncodedStringToBOM(db, 'CHROMEBOOK A25-Q22')
- # The BOM should load 'us' region from the probe result (numeric_id=29).)
- self.assertEquals(29, bom.encoded_fields['region_field'])
- self.assertEquals(
- [('us', {'region_code': Value('us')}, None)],
- bom.components['region'])
-
-
-class EncoderTest(unittest.TestCase):
-
- def setUp(self):
- self.database = Database.LoadFile(os.path.join(_TEST_DATA_PATH,
- 'test_db.yaml'))
- self.results = json_utils.LoadFile(
- os.path.join(_TEST_DATA_PATH, 'test_probe_result.json'))
-
- def testBOMToBinaryString(self):
- bom = hwid_utils.GenerateBOMFromProbedResults(self.database,
- self.results[0])
- # Manually set unprobeable components.
- self.database.UpdateComponentsOfBOM(bom, {
- 'keyboard': 'keyboard_us', 'display_panel': 'display_panel_0'})
- self.assertEquals('0000000000111010000011',
- BOMToIdentity(self.database, bom).binary_string)
- # Change firmware's encoded index to 1.
- mocked_bom = bom.Duplicate()
- self.database.UpdateComponentsOfBOM(
- mocked_bom, {'ro_main_firmware': 'ro_main_firmware_1'})
- self.assertEquals('0000000001111010000011',
- BOMToIdentity(self.database, mocked_bom).binary_string)
- # Change image id to 2.
- mocked_bom.image_id = 2
- self.assertEquals('0001000001111010000011',
- BOMToIdentity(self.database, mocked_bom).binary_string)
- # Change encoding pattern index to 1.
- mocked_bom.encoding_pattern_index = 1
- self.assertEquals('1001000001111010000011',
- BOMToIdentity(self.database, mocked_bom).binary_string)
-
- def testEncode(self):
- bom = hwid_utils.GenerateBOMFromProbedResults(self.database,
- self.results[0])
- # Manually set unprobeable components.
- self.database.UpdateComponentsOfBOM(bom, {
- 'keyboard': 'keyboard_us', 'display_panel': 'display_panel_0'})
- bom.image_id = 0
- identity = transformer.BOMToIdentity(self.database, bom)
- self.assertEquals('CHROMEBOOK AA5A-Y6L', identity.encoded_string)
-
+ def testEncodedNumberOutOfRange(self):
+ bom = self.test_data[5][1]
bom.image_id = 2
- identity = transformer.BOMToIdentity(self.database, bom)
- self.assertEquals('CHROMEBOOK C2H-I3Q-A6Q', identity.encoded_string)
+ self.assertRaises(HWIDException,
+ transformer.BOMToIdentity, self.database, bom)
- def testEncodeError(self):
- # Missing required component 'dram'.
- mock_results = copy.deepcopy(self.results[0])
- mock_results.pop('dram')
- bom = hwid_utils.GenerateBOMFromProbedResults(self.database, mock_results)
- self.database.UpdateComponentsOfBOM(bom, {
- 'keyboard': 'keyboard_us', 'display_panel': 'display_panel_0'})
- self.assertRaisesRegexp(
- HWIDException, "Encoded fields 'dram' have unknown indices",
- transformer.BOMToIdentity, self.database, bom)
+ def testNormal(self):
+ def _VerifyTransformer(components_bitset, bom):
+ identity = transformer.BOMToIdentity(self.database, bom)
+ self.assertEqual(identity.project, 'CHROMEBOOK')
+ self.assertEqual(identity.encoding_pattern_index, 0)
+ self.assertEqual(identity.image_id, bom.image_id)
+ self.assertEqual(identity.components_bitset, components_bitset)
- # Unsupported probe values of component 'dram'.
- mock_results = copy.deepcopy(self.results[0])
- mock_results['dram'] = {'generic': [{'vendor': 'FOO', 'size': '4G'}]}
- bom = hwid_utils.GenerateBOMFromProbedResults(self.database, mock_results)
- self.database.UpdateComponentsOfBOM(bom, {
- 'keyboard': 'keyboard_us', 'display_panel': 'display_panel_0'})
- self.assertRaisesRegexp(
- HWIDException, "Unknown component values: "
- "\"dram:{'size': '4G', 'vendor': 'FOO'}\"",
- transformer.BOMToIdentity, self.database, bom)
+ for components_bitset, bom in self.test_data:
+ _VerifyTransformer(components_bitset, bom)
- def testEncodeRegion(self):
- db = Database.LoadFile(
- os.path.join(_TEST_DATA_PATH, 'test_db_regions.yaml'))
- bom = hwid_utils.GenerateBOMFromProbedResults(db, self.results[5])
- # The BOM should load 'us' region from the probe result (numeric_id=29).)
- self.assertEquals(29, bom.encoded_fields['region_field'])
- identity = transformer.BOMToIdentity(db, bom)
- # The encoder should encode field index 29 into the region field.
- self.assertEquals('CHROMEBOOK A25-Q22', identity.encoded_string)
+ # Extra components shouldn't fail the transformer.
+ bom.SetComponent('cool', 'meow')
+ _VerifyTransformer(components_bitset, bom)
+
+
+class IdentityToBOMTest(_TransformerTestBase):
+ def _GenerateIdentityFromTestData(self, test_data_idx,
+ encoding_scheme=None, project=None,
+ encoding_pattern_index=None,
+ image_id=None, components_bitset=None):
+ test_data = self.test_data[test_data_idx]
+
+ project = project or self.database.project
+ encoding_pattern_index = (
+ encoding_pattern_index or test_data[1].encoding_pattern_index)
+ image_id = image_id or test_data[1].image_id
+ components_bitset = components_bitset or test_data[0]
+
+ encoding_scheme = (
+ encoding_scheme or self.database.pattern.GetEncodingScheme(image_id))
+
+ return Identity.GenerateFromBinaryString(
+ encoding_scheme, project, encoding_pattern_index, image_id,
+ components_bitset)
+
+ def testInvalidEncodingScheme(self):
+ identity = self._GenerateIdentityFromTestData(0, encoding_scheme='base8192')
+ self.assertRaises(HWIDException,
+ transformer.IdentityToBOM, self.database, identity)
+
+ def testInvalidProject(self):
+ identity = self._GenerateIdentityFromTestData(0, project='CHROMEBOOK123')
+ self.assertRaises(HWIDException,
+ transformer.IdentityToBOM, self.database, identity)
+
+ def testInvalidEncodingPattern(self):
+ identity = self._GenerateIdentityFromTestData(0, encoding_pattern_index=1)
+ self.assertRaises(HWIDException,
+ transformer.IdentityToBOM, self.database, identity)
+
+ def testInvalidImageId(self):
+ for invalid_image_id in [4, 5, 6]:
+ identity = self._GenerateIdentityFromTestData(
+ 0, image_id=invalid_image_id, encoding_scheme='base8192')
+ self.assertRaises(HWIDException,
+ transformer.IdentityToBOM, self.database, identity)
+
+ def testComponentsBitsetTooLong(self):
+ # Adds some useless bits.
+ identity = self._GenerateIdentityFromTestData(
+ 0, components_bitset=self.test_data[0][0] + '010101')
+ self.assertRaises(HWIDException,
+ transformer.IdentityToBOM, self.database, identity)
+
+ def testEncodedFieldIndexOutOfRange(self):
+ # Modify the bits for cpu field to 31.
+ identity = self._GenerateIdentityFromTestData(
+ 5, components_bitset='11111' + self.test_data[5][0][5:])
+ self.assertRaises(HWIDException,
+ transformer.IdentityToBOM, self.database, identity)
+
+ def testComponentsBitsetTooShort(self):
+ # In real case, this case is confusing. But in theory this is acceptable.
+
+ # Removes the bits for the battery field.
+ identity = self._GenerateIdentityFromTestData(
+ 4, components_bitset=self.test_data[4][0][:-3] + '1')
+ self.assertEquals(self.test_data[4][1],
+ transformer.IdentityToBOM(self.database, identity))
+
+ def testNormal(self):
+ for idx, test_data in enumerate(self.test_data):
+ identity = self._GenerateIdentityFromTestData(idx)
+ self.assertEquals(test_data[1],
+ transformer.IdentityToBOM(self.database, identity))
if __name__ == '__main__':
diff --git a/py/hwid/v3/verifier.py b/py/hwid/v3/verifier.py
index 5db24f8..34a7485 100644
--- a/py/hwid/v3/verifier.py
+++ b/py/hwid/v3/verifier.py
@@ -13,6 +13,7 @@
3. The rootkey is mp for PVT and later build.
"""
+import collections
import re
import factory_common # pylint: disable=unused-import
@@ -30,19 +31,18 @@
PVT_DOGFOOD/PVT.
Args:
+ database: The Database object which records the status of each components.
+ bom: The BOM object to be verified.
+ mode: Either "normal" or "rma".
current_phase: The current phase, for phase checks. If None is
specified, then phase.GetPhase() is used (this defaults to PVT
if none is available).
Raises:
- HWIDException is verification fails.
+ HWIDException if verification fails.
"""
- for comp_cls, comps in bom.components.iteritems():
- for comp in comps:
- comp_name = comp.component_name
- if not comp_name:
- continue
-
+ for comp_cls, comp_names in bom.components.iteritems():
+ for comp_name in comp_names:
status = database.components.GetComponentStatus(comp_cls, comp_name)
if status == common.COMPONENT_STATUS.supported:
continue
@@ -89,9 +89,14 @@
the image ID must begin with 'PVT').
Args:
+ database: The Database object which records image names.
+ bom: The BOM object to be verified.
current_phase: The current phase, for phase checks. If None is
specified, then phase.GetPhase() is used (this defaults to PVT
if none is available).
+
+ Raises:
+ HWIDException if verification fails.
"""
# Coerce current_phase to a Phase object, and use default phase
# if unspecified.
@@ -110,22 +115,13 @@
# MP-key checking applies only in PVT and above
if current_phase >= phase.PVT:
- if 'firmware_keys' in bom.components:
- key_types = ['firmware_keys']
- else:
- key_types = ['key_recovery', 'key_root']
+ if 'firmware_keys' not in bom.components:
+ raise HWIDException('firmware_keys is required but not found.')
- errors = []
- for key_type in key_types:
- if key_type not in bom.components:
- raise HWIDException(
- 'Component %r is required but not found.' % (key_type,))
- name = bom.components[key_type][0].component_name
- if not _IsMPKeyName(name):
- errors.append('%s component name is %r' % (key_type, name))
- if errors:
- raise HWIDException('MP keys are required in %s, but %s' % (
- current_phase, ' and '.join(errors)))
+ name = next(iter(bom.components['firmware_keys']))
+ if not _IsMPKeyName(name):
+ raise HWIDException(
+ 'MP keys are required in %r, but got %r' % (current_phase, name))
def VerifyBOM(database, decoded_bom, probed_bom):
@@ -136,34 +132,45 @@
a fake BOM object.
Args:
+ database: The Database object which records what components to check.
decoded_bom: The BOM object decoded from the the HWID identity.
probed_bom: The BOM object generated from the probed results.
Raises:
HWIDException if the BOM objects mismatch.
"""
+ def _GetExtraComponents(comps1, comps2):
+ num_comps = collections.defaultdict(int)
+ for comp in comps1:
+ num_comps[comp] += 1
+
+ for comp in comps2:
+ if comp in num_comps:
+ num_comps[comp] -= 1
+
+ results = [[comp] * num_comp
+ for comp, num_comp in num_comps.iteritems() if num_comp > 0]
+ return sorted(sum(results, []))
+
# We only verify the components listed in the pattern.
for comp_cls in database.GetActiveComponents(decoded_bom.image_id):
if comp_cls not in probed_bom.components:
raise HWIDException(
'Component class %r is not found in probed BOM.' % comp_cls)
- decoded_components = set(
- [comp.component_name for comp in decoded_bom.components[comp_cls]])
- probed_components = set(
- [comp.component_name for comp in probed_bom.components[comp_cls]])
-
err_msgs = []
- extra_components = decoded_components - probed_components
+ extra_components = _GetExtraComponents(decoded_bom.components[comp_cls],
+ probed_bom.components[comp_cls])
if extra_components:
- err_msgs.append('has extra components: %r' % sorted(extra_components))
+ err_msgs.append('has extra components: %r' % extra_components)
- missing_components = probed_components - decoded_components
+ missing_components = _GetExtraComponents(probed_bom.components[comp_cls],
+ decoded_bom.components[comp_cls])
if missing_components:
- err_msgs.append('is missing components: %r' % sorted(missing_components))
+ err_msgs.append('is missing components: %r' % missing_components)
if err_msgs:
raise HWIDException(
'Component class %r ' % comp_cls + ' and '.join(err_msgs) +
- '. Expected components are: %r' % sorted(probed_components))
+ '. Expected components are: %r' % probed_bom.components[comp_cls])
diff --git a/py/hwid/v3/verifier_unittest.py b/py/hwid/v3/verifier_unittest.py
index ae6ae20..5a427af 100755
--- a/py/hwid/v3/verifier_unittest.py
+++ b/py/hwid/v3/verifier_unittest.py
@@ -8,7 +8,6 @@
import factory_common # pylint: disable=unused-import
from cros.factory.hwid.v3.bom import BOM
-from cros.factory.hwid.v3.bom import ProbedComponentResult
from cros.factory.hwid.v3 import common
from cros.factory.hwid.v3.database import Database
from cros.factory.hwid.v3 import verifier
@@ -25,13 +24,10 @@
verify_checksum=False)
self.boms = {}
for status in common.COMPONENT_STATUS:
- bom = BOM(project='CHROMEBOOK',
- encoding_pattern_index=0,
+ bom = BOM(encoding_pattern_index=0,
image_id=0,
- components={
- 'cpu': [ProbedComponentResult('cpu_%s' % status, {}, None)],
- 'ram': [ProbedComponentResult('ram_supported', {}, None)]},
- encoded_fields=None)
+ components={'cpu': ['cpu_%s' % status],
+ 'ram': ['ram_supported']})
self.boms[status] = bom
def testSupported(self):
@@ -56,7 +52,7 @@
self.boms[common.COMPONENT_STATUS.unqualified],
mode=mode, current_phase=ph)
- def testDeprecatedInNormalMode(self):
+ def testDeprecated(self):
# Should pass the verification only in rma mode.
for ph in phase.PHASE_NAMES:
verifier.VerifyComponentStatus(
@@ -89,10 +85,8 @@
def _CreateBOM(image_id, firmware_key_name=None):
components = {}
if firmware_key_name:
- components['firmware_keys'] = [
- ProbedComponentResult(firmware_key_name, {}, None)]
- return BOM(project='CHROMEBOOK', encoding_pattern_index=0,
- image_id=image_id, components=components, encoded_fields=None)
+ components['firmware_keys'] = [firmware_key_name]
+ return BOM(0, image_id, components)
def testNoKeyComponent(self):
for image_id in [0, 1, 2]:
@@ -136,79 +130,53 @@
self.database = Database.LoadFile(_TEST_DATABASE_PATH,
verify_checksum=False)
self.decoded_bom = BOM(
- project='CHROMEBOOK', encoding_pattern_index=0, image_id=0,
- components={
- 'cpu': [ProbedComponentResult('cpu_supported', {}, None),
- ProbedComponentResult('cpu_unqualified', {}, None)],
+ encoding_pattern_index=0, image_id=0, components={
+ 'cpu': ['cpu_supported', 'cpu_unqualified'],
'ram': [],
- 'firmware_keys': [
- ProbedComponentResult('firmware_keys_dev', {}, None)]},
- encoded_fields=None)
+ 'firmware_keys': ['firmware_keys_dev']})
def testPass(self):
probed_bom = BOM(
- project='CHROMEBOOK', encoding_pattern_index=0, image_id=0,
- components={
- 'cpu': [ProbedComponentResult('cpu_unqualified', {}, None),
- ProbedComponentResult('cpu_supported', {}, None)],
+ encoding_pattern_index=0, image_id=0, components={
+ 'cpu': ['cpu_unqualified', 'cpu_supported'],
'ram': [],
- 'firmware_keys': [
- ProbedComponentResult('firmware_keys_dev', {}, None)]},
- encoded_fields=None)
+ 'firmware_keys': ['firmware_keys_dev']})
verifier.VerifyBOM(self.database, self.decoded_bom, probed_bom)
def testHasExtraComponents(self):
probed_bom = BOM(
- project='CHROMEBOOK', encoding_pattern_index=0, image_id=0,
- components={
- 'cpu': [ProbedComponentResult('cpu_unqualified', {}, None),
- ProbedComponentResult('cpu_supported', {}, None)],
- 'ram': [ProbedComponentResult('ram_supported', {}, None)],
- 'firmware_keys': [
- ProbedComponentResult('firmware_keys_dev', {}, None)]},
- encoded_fields=None)
+ encoding_pattern_index=0, image_id=0, components={
+ 'cpu': ['cpu_unqualified', 'cpu_supported'],
+ 'ram': ['ram_supported'],
+ 'firmware_keys': ['firmware_keys_dev']})
self.assertRaises(common.HWIDException, verifier.VerifyBOM,
self.database, self.decoded_bom, probed_bom)
probed_bom = BOM(
- project='CHROMEBOOK', encoding_pattern_index=0, image_id=0,
- components={
- 'cpu': [ProbedComponentResult('cpu_unqualified', {}, None),
- ProbedComponentResult('cpu_supported', {}, None),
- ProbedComponentResult('cpu_deprecated', {}, None)],
+ encoding_pattern_index=0, image_id=0, components={
+ 'cpu': ['cpu_unqualified', 'cpu_supported', 'cpu_deprecated'],
'ram': [],
- 'firmware_keys': [
- ProbedComponentResult('firmware_keys_dev', {}, None)]},
- encoded_fields=None)
+ 'firmware_keys': ['firmware_keys_dev']})
self.assertRaises(common.HWIDException, verifier.VerifyBOM,
self.database, self.decoded_bom, probed_bom)
def testIsMissingComponents(self):
- probed_bom = BOM(
- project='CHROMEBOOK', encoding_pattern_index=0, image_id=0,
- components={
- 'cpu': [ProbedComponentResult('cpu_unqualified', {}, None)],
- 'ram': [],
- 'firmware_keys': [
- ProbedComponentResult('firmware_keys_dev', {}, None)]},
- encoded_fields=None)
+ probed_bom = BOM(encoding_pattern_index=0, image_id=0, components={
+ 'cpu': ['cpu_unqualified'],
+ 'ram': [],
+ 'firmware_keys': ['firmware_keys_dev']})
self.assertRaises(common.HWIDException, verifier.VerifyBOM,
self.database, self.decoded_bom, probed_bom)
def testMisMatch(self):
- probed_bom = BOM(
- project='CHROMEBOOK', encoding_pattern_index=0, image_id=0,
- components={
- 'cpu': [ProbedComponentResult('cpu_unqualified', {}, None),
- ProbedComponentResult('cpu_deprecated', {}, None)],
- 'ram': [],
- 'firmware_keys': [
- ProbedComponentResult('firmware_keys_dev', {}, None)]},
- encoded_fields=None)
+ probed_bom = BOM(encoding_pattern_index=0, image_id=0, components={
+ 'cpu': ['cpu_unqualified', 'cpu_deprecated'],
+ 'ram': [],
+ 'firmware_keys': ['firmware_keys_dev']})
self.assertRaises(common.HWIDException, verifier.VerifyBOM,
self.database, self.decoded_bom, probed_bom)
diff --git a/py/hwid/v3/yaml_tags.py b/py/hwid/v3/yaml_tags.py
index 033ed14..1e2b791 100644
--- a/py/hwid/v3/yaml_tags.py
+++ b/py/hwid/v3/yaml_tags.py
@@ -132,7 +132,6 @@
def __init__(self):
components_dict = {
- 'probeable': True,
'items': {}
}
for code, region in regions.REGIONS.iteritems():
diff --git a/py/test/pytests/hwid.py b/py/test/pytests/hwid.py
index 508bd22..d46b761 100644
--- a/py/test/pytests/hwid.py
+++ b/py/test/pytests/hwid.py
@@ -195,7 +195,7 @@
decoded_hwid = self.factory_tools.CallOutput(decode_cmd)
self.assertIsNotNone(decoded_hwid, 'HWID decode failed.')
- logging.info('HWDB checksum: %s', hwid['hwdb_checksum'])
+ logging.info('HWID Database checksum: %s', hwid['database_checksum'])
testlog.LogParam(name='generated_hwid', value=encoded_string)
testlog.LogParam(name='hwdb_checksum', value=hwid['hwdb_checksum'])
diff --git a/py/tools/deps.conf b/py/tools/deps.conf
index 499298e..dafa849 100644
--- a/py/tools/deps.conf
+++ b/py/tools/deps.conf
@@ -40,6 +40,7 @@
- cros.factory.testlog.*
- =cros.factory.test.event_log
test_utils:
+ - =cros.factory.test.device_data
- cros.factory.test.utils # Allow 3rd party / external dependency.
- =cros.factory.test.state
test_cros:
diff --git a/py/utils/type_utils.py b/py/utils/type_utils.py
index b29ad3f..a2bc2f8 100644
--- a/py/utils/type_utils.py
+++ b/py/utils/type_utils.py
@@ -49,6 +49,12 @@
def __repr__(self):
return repr(self.__dict__)
+ def __eq__(self, rhs):
+ return self.__dict__ == rhs.__dict__
+
+ def __ne__(self, rhs):
+ return self.__dict__ != rhs.__dict__
+
class Enum(frozenset):
"""An enumeration type.