hwid: Revises the structure of the RuleContext class.
This CL flattens the `hwid` property in a rule context object into
`database`, `bom` and `mode`. The final goal is to deprecate the
class `common.HWID`.
TEST=make test
BUG=chromium:689944
Change-Id: Ia1a2dacfbe5c9640e44cd01d935add86a7f7dd12
Reviewed-on: https://chromium-review.googlesource.com/845478
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/device/info.py b/py/device/info.py
index 5d8fa03..315434e 100755
--- a/py/device/info.py
+++ b/py/device/info.py
@@ -266,7 +266,7 @@
def hwid_database_version(self):
"""Uses checksum of hwid file as hwid database version."""
hwid_file_path = self._device.path.join(
- hwid_utils.DEFAULT_HWID_DATA_PATH, hwid_utils.ProbeProject().upper())
+ hwid_utils.GetDefaultDataPath(), hwid_utils.ProbeProject().upper())
# TODO(hungte) Support remote DUT.
return hwid_utils.ComputeDatabaseChecksum(hwid_file_path)
diff --git a/py/gooftool/commands.py b/py/gooftool/commands.py
index 97d5359..4704f35 100755
--- a/py/gooftool/commands.py
+++ b/py/gooftool/commands.py
@@ -125,7 +125,7 @@
_hwdb_path_cmd_arg = CmdArg(
'--hwdb_path', metavar='PATH',
- default=hwid_utils.DEFAULT_HWID_DATA_PATH,
+ default=hwid_utils.GetDefaultDataPath(),
help='Path to the HWID database.')
_hwid_status_list_cmd_arg = CmdArg(
diff --git a/py/gooftool/core.py b/py/gooftool/core.py
index 5076df6..42ec20c 100644
--- a/py/gooftool/core.py
+++ b/py/gooftool/core.py
@@ -65,8 +65,8 @@
load. If not specified, the project name will be detected with
cros.factory.hwid.ProbeProject(). Used for HWID v3 only.
hwdb_path: The path to load the project-specific component database from.
- If not specified, cros.factory.hwid.DEFAULT_HWID_DATA_PATH will be used.
- Used for HWID v3 only.
+ If not specified, cros.factory.hwid.hwid_utils.GetDefaultDataPath() will
+ be used. Used for HWID v3 only.
"""
self._hwid_version = hwid_version
if hwid_version == 2:
@@ -76,7 +76,7 @@
self._db_creator = lambda: component_db or self._hardware_db.comp_db
elif hwid_version == 3:
self._project = project or hwid_utils.ProbeProject()
- self._hwdb_path = hwdb_path or hwid_utils.DEFAULT_HWID_DATA_PATH
+ self._hwdb_path = hwdb_path or hwid_utils.GetDefaultDataPath()
self._db_creator = lambda: Database.LoadFile(
os.path.join(self._hwdb_path, self._project.upper()))
else:
diff --git a/py/hwid/v3/bom.py b/py/hwid/v3/bom.py
index 2e76561..19ff203 100644
--- a/py/hwid/v3/bom.py
+++ b/py/hwid/v3/bom.py
@@ -4,6 +4,7 @@
"""BOM class for HWID v3 framework."""
+import collections
import copy
import factory_common # pylint: disable=W0611
@@ -11,6 +12,11 @@
from cros.factory.utils import schema
+# A named tuple to store the probed component name and the error if any.
+ProbedComponentResult = collections.namedtuple(
+ 'ProbedComponentResult', ['component_name', 'probed_values', 'error'])
+
+
class BOM(object):
"""A class that holds all the information regarding a BOM.
diff --git a/py/hwid/v3/common.py b/py/hwid/v3/common.py
index 3820fbb..b2cd7a7 100644
--- a/py/hwid/v3/common.py
+++ b/py/hwid/v3/common.py
@@ -6,7 +6,6 @@
"""Common classes for HWID v3 operation."""
-import collections
import json
import re
@@ -28,10 +27,6 @@
return (MP_KEY_NAME_PATTERN.search(name) and
not PRE_MP_KEY_NAME_PATTERN.search(name))
-# A named tuple to store the probed component name and the error if any.
-ProbedComponentResult = collections.namedtuple(
- 'ProbedComponentResult', ['component_name', 'probed_values', 'error'])
-
UNPROBEABLE_COMPONENT_ERROR = lambda comp_cls: (
'Component class %r is unprobeable' % comp_cls)
MISSING_COMPONENT_ERROR = lambda comp_cls: 'Missing %r component' % comp_cls
@@ -149,7 +144,7 @@
and the self._binary_string is matched, then return it.
"""
# pylint: disable=W0404
- from cros.factory.hwid.v3.encoder import BOMToBinaryString
+ from cros.factory.hwid.v3.transformer import BOMToBinaryString
binary_string = BOMToBinaryString(self.database, self.bom)
if (self._identity and
HWID.IsEquivalentBinaryString(self._identity.binary_string,
@@ -165,7 +160,7 @@
and 45 is the checksum. Compare to binary_string, it is human-trackable.
"""
# pylint: disable=W0404
- from cros.factory.hwid.v3.encoder import BinaryStringToEncodedString
+ from cros.factory.hwid.v3.transformer import BinaryStringToEncodedString
return BinaryStringToEncodedString(self.database, self.binary_string)
def VerifySelf(self):
@@ -179,8 +174,11 @@
Raises:
HWIDException on verification error.
"""
- self.database.VerifyBOM(self.bom)
- self.database.VerifyEncodedString(self.encoded_string)
+ # pylint: disable=W0404
+ from cros.factory.hwid.v3.transformer import VerifyBOM
+ from cros.factory.hwid.v3.transformer import VerifyEncodedString
+ VerifyBOM(self.database, self.bom)
+ VerifyEncodedString(self.database, self.encoded_string)
def VerifyComponentStatus(self, current_phase=None):
"""Verifies the status of all components.
@@ -317,3 +315,22 @@
if errors:
raise HWIDException('MP keys are required in %s, but %s' % (
current_phase, ' and '.join(errors)))
+
+
+HWID_FORMAT = {
+ HWID.ENCODING_SCHEME.base32: re.compile(
+ r'^([A-Z0-9]+)' # group(0): Project
+ r' (' # group(1): Entire BOM.
+ r'(?:[A-Z2-7]{4}-)*' # Zero or more 4-character groups with
+ # dash.
+ r'[A-Z2-7]{1,4}' # Last group with 1 to 4 characters.
+ r')$' # End group(1)
+ ),
+ HWID.ENCODING_SCHEME.base8192: re.compile(
+ r'^([A-Z0-9]+)' # group(0): Project
+ r' (' # group(1): Entire BOM
+ r'(?:[A-Z2-7][2-9][A-Z2-7]-)*' # Zero or more 3-character groups with
+ # dash.
+ r'[A-Z2-7][2-9][A-Z2-7]' # Last group with 3 characters.
+ r')$' # End group(1)
+ )}
diff --git a/py/hwid/v3/common_unittest.py b/py/hwid/v3/common_unittest.py
index e76f43d..37fe8b2 100755
--- a/py/hwid/v3/common_unittest.py
+++ b/py/hwid/v3/common_unittest.py
@@ -15,9 +15,9 @@
from cros.factory.hwid.v3.common import HWIDException
from cros.factory.hwid.v3.common import IsMPKeyName
from cros.factory.hwid.v3.database import Database
-from cros.factory.hwid.v3.encoder import Encode
from cros.factory.hwid.v3 import hwid_utils
from cros.factory.hwid.v3.identity import Identity
+from cros.factory.hwid.v3.transformer import Encode
from cros.factory.utils import json_utils
_TEST_DATA_PATH = os.path.join(os.path.dirname(__file__), 'testdata')
@@ -52,7 +52,7 @@
def testInvalidInitialize(self):
bom = hwid_utils.GenerateBOMFromProbedResults(self.database,
self.results[0])
- bom = self.database.UpdateComponentsOfBOM(bom, {
+ self.database.UpdateComponentsOfBOM(bom, {
'keyboard': 'keyboard_us', 'dram': 'dram_0',
'display_panel': 'display_panel_0'})
bom.image_id = 2
@@ -76,7 +76,7 @@
def testVerifySelf(self):
bom = hwid_utils.GenerateBOMFromProbedResults(self.database,
self.results[0])
- bom = self.database.UpdateComponentsOfBOM(bom, {
+ self.database.UpdateComponentsOfBOM(bom, {
'keyboard': 'keyboard_us', 'dram': 'dram_0',
'display_panel': 'display_panel_0'})
bom.image_id = 2
@@ -99,7 +99,7 @@
def testVerifyProbeResult(self):
result = self.results[0]
bom = hwid_utils.GenerateBOMFromProbedResults(self.database, result)
- bom = self.database.UpdateComponentsOfBOM(bom, {
+ self.database.UpdateComponentsOfBOM(bom, {
'keyboard': 'keyboard_us', 'dram': 'dram_0',
'display_panel': 'display_panel_0'})
bom.image_id = 2
diff --git a/py/hwid/v3/database.py b/py/hwid/v3/database.py
index d5b3881..a5ebea9 100644
--- a/py/hwid/v3/database.py
+++ b/py/hwid/v3/database.py
@@ -10,17 +10,16 @@
import copy
import hashlib
import math
-import pprint
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.
from cros.factory.hwid.v3 import yaml_tags # pylint: disable=W0611
from cros.factory.hwid.v3 import yaml_wrapper as yaml
from cros.factory.hwid.v3.base32 import Base32
-from cros.factory.hwid.v3.base8192 import Base8192
from cros.factory.utils import file_utils
from cros.factory.utils import schema
from cros.factory.utils import type_utils
@@ -82,23 +81,6 @@
rules: A Rules object.
checksum: The value of the checksum field.
"""
- _HWID_FORMAT = {
- common.HWID.ENCODING_SCHEME.base32: re.compile(
- r'^([A-Z0-9]+)' # group(0): Project
- r' (' # group(1): Entire BOM.
- r'(?:[A-Z2-7]{4}-)*' # Zero or more 4-character groups with
- # dash.
- r'[A-Z2-7]{1,4}' # Last group with 1 to 4 characters.
- r')$' # End group(1)
- ),
- common.HWID.ENCODING_SCHEME.base8192: re.compile(
- r'^([A-Z0-9]+)' # group(0): Project
- r' (' # group(1): Entire BOM
- r'(?:[A-Z2-7][2-9][A-Z2-7]-)*' # Zero or more 3-character groups with
- # dash.
- r'[A-Z2-7][2-9][A-Z2-7]' # Last group with 3 characters.
- r')$' # End group(1)
- )}
def __init__(self, project, encoding_patterns, image_id, pattern,
encoded_fields, components, rules, checksum):
@@ -267,32 +249,26 @@
bom: A BOM object to update.
updated_components: A dict of component classes to component names
indicating the set of components to update.
-
- Returns:
- A BOM object with updated components and encoded fields.
"""
- result = bom.Duplicate()
for comp_cls, comp_name in updated_components.iteritems():
new_probed_result = []
if comp_name is None:
- new_probed_result.append(common.ProbedComponentResult(
+ 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(common.ProbedComponentResult(
+ new_probed_result.append(ProbedComponentResult(
name, comp_attrs['values'], None))
# Update components data of the duplicated BOM.
- result.components[comp_cls] = new_probed_result
+ bom.components[comp_cls] = new_probed_result
# Re-calculate all the encoded index of each encoded field.
- result.encoded_fields = {}
+ bom.encoded_fields = {}
for field in self.encoded_fields:
- result.encoded_fields[field] = self.GetFieldIndexFromProbedComponents(
- field, result.components)
-
- return result
+ 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
@@ -382,170 +358,6 @@
result[comp_cls].append(new_attr)
return result
- def VerifyBinaryString(self, binary_string):
- """Verifies the binary string.
-
- Args:
- binary_string: The binary string to verify.
-
- Raises:
- HWIDException if verification fails.
- """
- if set(binary_string) - set('01'):
- raise common.HWIDException('Invalid binary string: %r' % binary_string)
-
- if '1' not in binary_string:
- raise common.HWIDException('Binary string %r does not have stop bit set',
- binary_string)
- # Truncate trailing 0s.
- string_without_paddings = binary_string[:binary_string.rfind('1') + 1]
-
- image_id = self.pattern.GetImageIdFromBinaryString(binary_string)
- if len(string_without_paddings) > self.pattern.GetTotalBitLength(image_id):
- raise common.HWIDException('Invalid bit string length of %r. Expected '
- 'length <= %d, got length %d' %
- (binary_string,
- self.pattern.GetTotalBitLength(image_id),
- len(string_without_paddings)))
-
- def VerifyEncodedStringFormat(self, encoded_string):
- """Verifies that the format of the given encoded string.
-
- Checks that the string matches either base32 or base8192 format.
-
- Args:
- encoded_string: The encoded string to verify.
-
- Raises:
- HWIDException if verification fails.
- """
- if not any(hwid_format.match(encoded_string) for hwid_format in
- self._HWID_FORMAT.itervalues()):
- raise common.HWIDException(
- 'HWID string %r is neither base32 nor base8192 encoded' %
- encoded_string)
-
- def VerifyEncodedString(self, encoded_string):
- """Verifies the given encoded string.
-
- Args:
- encoded_string: The encoded string to verify.
-
- Raises:
- HWIDException if verification fails.
- """
- try:
- image_id = self.pattern.GetImageIdFromEncodedString(encoded_string)
- encoding_scheme = self.pattern.GetPatternByImageId(
- image_id)['encoding_scheme']
- project, bom_checksum = Database._HWID_FORMAT[encoding_scheme].findall(
- encoded_string)[0]
- except IndexError:
- raise common.HWIDException(
- 'Invalid HWID string format: %r' % encoded_string)
- if len(bom_checksum) < 2:
- raise common.HWIDException(
- 'Length of encoded string %r is less than 2 characters' %
- bom_checksum)
- if project != self.project.upper():
- raise common.HWIDException('Invalid project name: %r' % project)
- # Verify the checksum
- stripped = encoded_string.replace('-', '')
- hwid = stripped[:-2]
- checksum = stripped[-2:]
- if encoding_scheme == common.HWID.ENCODING_SCHEME.base32:
- expected_checksum = Base32.Checksum(hwid)
- elif encoding_scheme == common.HWID.ENCODING_SCHEME.base8192:
- expected_checksum = Base8192.Checksum(hwid)
- if checksum != expected_checksum:
- raise common.HWIDException('Checksum of %r mismatch (expected %r)' % (
- encoded_string, expected_checksum))
-
- def VerifyBOM(self, 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 != self.project:
- raise common.HWIDException('Invalid project name. Expected %r, got %r' %
- (self.project, bom.project))
-
- if bom.encoding_pattern_index not in self.encoding_patterns:
- raise common.HWIDException('Invalid encoding pattern: %r' %
- bom.encoding_pattern_index)
- if bom.image_id not in self.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 = self.pattern.GetFieldNames(bom.image_id)
- for comp_cls in self.GetActiveComponents(bom.image_id):
- if (comp_cls not in bom.components and
- (comp_cls in self.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 self.components.probeable:
- continue
- for element in probed_values:
- probed_values = element.probed_values
- if probed_values is None:
- continue
- found_comps = self.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 self.components.probeable
- for comp_cls in self.encoded_fields[field_name][0].keys()]):
- continue
- index = bom.encoded_fields[field_name]
- if index is None or index not in self.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 VerifyComponents(self, bom, comp_list=None):
"""Given a list of component classes, verify that the probed components of
all the component classes in the list are valid components in the database.
diff --git a/py/hwid/v3/database_unittest.py b/py/hwid/v3/database_unittest.py
index d5d80a8..d2e82e9 100755
--- a/py/hwid/v3/database_unittest.py
+++ b/py/hwid/v3/database_unittest.py
@@ -13,8 +13,9 @@
import factory_common # pylint: disable=W0611
from cros.factory.hwid.v3 import common
-from cros.factory.hwid.v3.common import HWIDException, ProbedComponentResult
-from cros.factory.hwid.v3.database import Database, Components
+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
@@ -97,9 +98,6 @@
probed_value))])
self.assertEquals(bom.encoded_fields['cellular_field'], None)
- def testVerifyBOMWithDefault(self):
- self.assertEquals(None, self.database.VerifyBOM(self.boms[6], True))
-
class DatabaseTest(unittest.TestCase):
@@ -197,28 +195,29 @@
def testUpdateComponentsOfBOM(self):
bom = self.boms[0]
- new_bom = self.database.UpdateComponentsOfBOM(
- bom, {'keyboard': 'keyboard_gb'})
+ 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 = self.database.UpdateComponentsOfBOM(
- bom, {'audio_codec': ['codec_0', 'hdmi_0']})
+ 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 = self.database.UpdateComponentsOfBOM(
- bom, {'cellular': 'cellular_0'})
+ 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 = self.database.UpdateComponentsOfBOM(
- bom, {'cellular': None})
+ 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'])
@@ -286,81 +285,6 @@
self.assertEquals({'cellular': None},
self.database._GetAttributesByIndex('cellular', 0))
- def testVerifyBinaryString(self):
- self.assertEquals(
- None, self.database.VerifyBinaryString('0000000000111010000011000'))
- self.assertRaisesRegexp(
- HWIDException, r'Invalid binary string: .*',
- self.database.VerifyBinaryString, '020001010011011011000')
- self.assertRaisesRegexp(
- HWIDException, r'Binary string .* does not have stop bit set',
- self.database.VerifyBinaryString, '00000')
- self.assertRaisesRegexp(
- HWIDException, r'Invalid bit string length',
- self.database.VerifyBinaryString, '000000000010100110110111000')
-
- def testVerifyEncodedString(self):
- self.assertEquals(
- None, self.database.VerifyEncodedString('CHROMEBOOK AW3L-M7I7-V'))
- self.assertRaisesRegexp(
- HWIDException, r'Invalid HWID string format',
- self.database.VerifyEncodedString, 'AW3L-M7I5-4')
- self.assertRaisesRegexp(
- HWIDException, r'Length of encoded string .* is less than 2 characters',
- self.database.VerifyEncodedString, 'FOO A')
- self.assertRaisesRegexp(
- HWIDException, r'Invalid project name',
- self.database.VerifyEncodedString, 'FOO AW3L-M7IK-W')
- self.assertRaisesRegexp(
- HWIDException, r'Checksum of .* mismatch',
- self.database.VerifyEncodedString, 'CHROMEBOOK AW3L-M7IA-B')
-
- def testVerifyBOM(self):
- # Before evaluating rule to update the unprobeable component, we only verify
- # the probeable components.
- bom = self.boms[0]
- self.assertEquals(None, self.database.VerifyBOM(bom, True))
-
- original_value = bom.project
- bom.project = 'FOO'
- with self.assertRaisesRegexp(HWIDException,
- r'Invalid project name. Expected .*, got .*'):
- self.database.VerifyBOM(bom, True)
- bom.project = original_value
-
- original_value = bom.encoding_pattern_index
- bom.encoding_pattern_index = 2
- with self.assertRaisesRegexp(HWIDException, r'Invalid encoding pattern'):
- self.database.VerifyBOM(bom, True)
- bom.encoding_pattern_index = original_value
-
- original_value = bom.image_id
- bom.image_id = 6
- with self.assertRaisesRegexp(HWIDException, r'Invalid image id: .*'):
- self.database.VerifyBOM(bom, True)
- bom.image_id = original_value
-
- original_value = bom.encoded_fields['cpu']
- bom.encoded_fields['cpu'] = 8
- with self.assertRaisesRegexp(HWIDException,
- r'Encoded fields .* have unknown indices'):
- self.database.VerifyBOM(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:.*'):
- self.database.VerifyBOM(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: .*'):
- self.database.VerifyBOM(bom, True)
- bom.encoded_fields['cpu'] = original_value
-
def testVerifyComponents(self):
self.maxDiff = None
bom = self.boms[0]
diff --git a/py/hwid/v3/decoder.py b/py/hwid/v3/decoder.py
deleted file mode 100644
index b485f3e..0000000
--- a/py/hwid/v3/decoder.py
+++ /dev/null
@@ -1,118 +0,0 @@
-# Copyright 2013 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 decoder."""
-
-import collections
-import factory_common # pylint: disable=W0611
-
-from cros.factory.hwid.v3.bom import BOM
-from cros.factory.hwid.v3 import common
-from cros.factory.hwid.v3.base32 import Base32
-from cros.factory.hwid.v3.base8192 import Base8192
-from cros.factory.hwid.v3.identity import Identity
-
-_Decoder = {
- common.HWID.ENCODING_SCHEME.base32: Base32,
- common.HWID.ENCODING_SCHEME.base8192: Base8192
-}
-
-
-def BinaryStringToBOM(database, binary_string):
- """Decodes the given binary string to a BOM object.
-
- Args:
- database: A Database object that is used to provide device-specific
- information for decoding.
- binary_string: A binary string.
-
- Returns:
- A BOM object
- """
- database.VerifyBinaryString(binary_string)
- stripped_binary_string = binary_string[:binary_string.rfind('1')]
-
- project = database.project
- encoding_pattern = int(stripped_binary_string[0], 2)
- image_id = database.pattern.GetImageIdFromBinaryString(binary_string)
-
- # Construct the encoded fields dict.
- encoded_fields = collections.defaultdict(int)
- for field_name in database.pattern.GetFieldNames(image_id):
- encoded_fields[field_name] = 0
- bit_mapping = database.pattern.GetBitMapping(
- image_id=image_id, binary_string_length=len(stripped_binary_string))
- for i, (field, bit_offset) in bit_mapping.iteritems():
- if i >= len(stripped_binary_string):
- break
- encoded_fields[field] += int(stripped_binary_string[i], 2) << bit_offset
-
- # Check that all the encoded field indices are valid.
- expected_encoded_fields = database.pattern.GetFieldNames(image_id)
- missing_fields = set(expected_encoded_fields) - set(encoded_fields.keys())
- if missing_fields:
- raise common.HWIDException('Index of the fields are missing: %r' %
- list(missing_fields))
- for field in encoded_fields:
- if encoded_fields[field] not in database.encoded_fields[field]:
- raise common.HWIDException('Invalid encoded field index: {%r: %r}' %
- (field, encoded_fields[field]))
-
- # Construct the components dict.
- components = collections.defaultdict(list)
- 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(common.ProbedComponentResult(
- None, None, common.MISSING_COMPONENT_ERROR(comp_cls)))
- else:
- for attrs in attr_list:
- components[comp_cls].append(common.ProbedComponentResult(
- attrs['name'], attrs['values'], None))
-
- return BOM(project, encoding_pattern, image_id, components, encoded_fields)
-
-
-def EncodedStringToBinaryString(database, encoded_string):
- """Decodes the given encoded HWID string to a binary string.
-
- Args:
- database: A Database object that is used to provide device-specific
- information for decoding.
- encoded_string: An encoded string (with or without dashed).
-
- Returns:
- A binary string.
- """
- database.VerifyEncodedStringFormat(encoded_string)
- image_id = database.pattern.GetImageIdFromEncodedString(encoded_string)
- encoding_scheme = database.pattern.GetPatternByImageId(
- image_id)['encoding_scheme']
- database.VerifyEncodedString(encoded_string)
- _, hwid_string = encoded_string.split(' ')
- hwid_string = hwid_string.replace('-', '')
- return _Decoder[encoding_scheme].Decode(
- hwid_string)[:-_Decoder[encoding_scheme].CHECKSUM_SIZE].rstrip('0')
-
-
-def Decode(database, encoded_string, mode=common.HWID.OPERATION_MODE.normal):
- """Decodes the given encoded string to a HWID object.
-
- Args:
- database: A Database object that is used to provide device-specific
- information for decoding.
- encoded_string: An encoded string.
- mode: The operation mode of the generated HWID object. Valid values are:
- ('normal', 'rma')
-
- Returns:
- A HWID object which contains the BOM, the binary string, and the encoded
- string derived from the given encoded string.
- """
- binary_string = EncodedStringToBinaryString(database, encoded_string)
- identity = Identity(database.project, binary_string, encoded_string)
- bom = BinaryStringToBOM(database, binary_string)
- return common.HWID(database, bom=bom, identity=identity, mode=mode)
diff --git a/py/hwid/v3/decoder_unittest.py b/py/hwid/v3/decoder_unittest.py
deleted file mode 100755
index 3e515ae..0000000
--- a/py/hwid/v3/decoder_unittest.py
+++ /dev/null
@@ -1,202 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-#
-# Copyright 2013 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 os
-import unittest
-import factory_common # pylint: disable=W0611
-
-from cros.factory.hwid.v3.common import HWIDException
-from cros.factory.hwid.v3.database import Database
-from cros.factory.hwid.v3.decoder import EncodedStringToBinaryString
-from cros.factory.hwid.v3.decoder import BinaryStringToBOM, Decode
-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')
-
-
-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])
-
- def testEncodedStringToBinaryString(self):
- self.assertEquals('0000000000111010000011',
- EncodedStringToBinaryString(
- self.database, 'CHROMEBOOK AA5A-Y6L'))
- self.assertEquals('0001000000111010000011',
- EncodedStringToBinaryString(
- self.database, 'CHROMEBOOK C2H-I3Q-A6Q'))
- self.assertEquals('1000000000111010000011',
- EncodedStringToBinaryString(
- self.database, 'CHROMEBOOK QA5A-YCJ'))
-
- def testBinaryStringToBOM(self):
- reference_bom = hwid_utils.GenerateBOMFromProbedResults(self.database,
- self.results[0])
- reference_bom = self.database.UpdateComponentsOfBOM(reference_bom, {
- 'keyboard': 'keyboard_us',
- 'display_panel': 'display_panel_0'})
- bom = BinaryStringToBOM(self.database, '0000000000111010000011')
- self._CheckBOM(reference_bom, bom)
-
- bom = BinaryStringToBOM(self.database, '0000000001111010000011')
- self.assertEquals(1, bom.encoded_fields['firmware'])
- self.assertEquals(2, BinaryStringToBOM(
- self.database, '0001000000111010000011').image_id)
- self.assertEquals(1, BinaryStringToBOM(
- self.database, '1000000000111010000011').encoding_pattern_index)
- self.assertRaisesRegexp(
- HWIDException, r"Invalid encoded field index: {'cpu': 6}",
- 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 = 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 = 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])
- reference_bom = self.database.UpdateComponentsOfBOM(reference_bom, {
- 'keyboard': 'keyboard_us', 'dram': 'dram_0',
- 'display_panel': 'display_panel_0'})
- hwid = Decode(self.database, 'CHROMEBOOK AA5A-Y6L')
- self.assertEquals('0000000000111010000011', hwid.binary_string)
- self.assertEquals('CHROMEBOOK AA5A-Y6L', hwid.encoded_string)
- self._CheckBOM(reference_bom, hwid.bom)
-
- hwid = Decode(self.database, 'CHROMEBOOK C2H-I3Q-A6Q')
- self.assertEquals('0001000000111010000011', hwid.binary_string)
- self.assertEquals('CHROMEBOOK C2H-I3Q-A6Q', hwid.encoded_string)
- reference_bom.image_id = 2
- self._CheckBOM(reference_bom, hwid.bom)
-
- def testPreviousVersionOfEncodedString(self):
- bom = BinaryStringToBOM(self.database, '000000000011101000001')
- self.assertEquals(1, bom.encoded_fields['cpu'])
- hwid = Decode(self.database, 'CHROMEBOOK AA5A-Q7Z')
- self.assertEquals('000000000011101000001', hwid.binary_string)
- self.assertEquals('CHROMEBOOK AA5A-Q7Z', hwid.encoded_string)
- self.assertEquals(1, hwid.bom.encoded_fields['cpu'])
-
- def testDecodeRegion(self):
- db = Database.LoadFile(
- os.path.join(_TEST_DATA_PATH, 'test_db_regions.yaml'))
- hwid = Decode(db, 'CHROMEBOOK A25-Q22')
- # The BOM should load 'us' region from the probe result (numeric_id=29).)
- self.assertEquals(29, hwid.bom.encoded_fields['region_field'])
- self.assertEquals(
- [('us', {'region_code': Value('us')}, None)],
- hwid.bom.components['region'])
-
-
-if __name__ == '__main__':
- unittest.main()
diff --git a/py/hwid/v3/encoder.py b/py/hwid/v3/encoder.py
deleted file mode 100644
index 5385bed..0000000
--- a/py/hwid/v3/encoder.py
+++ /dev/null
@@ -1,91 +0,0 @@
-# Copyright 2013 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."""
-
-import factory_common # pylint: disable=W0611
-
-from cros.factory.hwid.v3 import common
-from cros.factory.hwid.v3.base32 import Base32
-from cros.factory.hwid.v3.base8192 import Base8192
-
-_Encoder = {
- common.HWID.ENCODING_SCHEME.base32: Base32,
- common.HWID.ENCODING_SCHEME.base8192: Base8192
-}
-
-
-def BOMToBinaryString(database, bom):
- """Encodes the given BOM object to a binary string.
-
- Args:
- database: A Database object that is used to provide device-specific
- information for encoding.
-
- Returns:
- A binary string.
- """
- database.VerifyBOM(bom)
- bit_length = database.pattern.GetTotalBitLength(bom.image_id)
- binary_list = bit_length * [0]
-
- # Fill in header.
- binary_list[0] = bom.encoding_pattern_index
- for i in xrange(1, 5):
- binary_list[i] = (bom.image_id >> (4 - i)) & 1
- # 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
- # Set stop bit.
- binary_list[bit_length - 1] = 1
- return ''.join(['%d' % bit for bit in binary_list])
-
-
-def BinaryStringToEncodedString(database, binary_string):
- """Encodes the given binary string to a encoded string.
-
- Args:
- database: A Database object that is used to provide device-specific
- information for encoding.
- binary_string: A string of '0's and '1's.
-
- Returns:
- An encoded string with project name, base32-encoded HWID, and checksum.
- """
- database.VerifyBinaryString(binary_string)
- image_id = database.pattern.GetImageIdFromBinaryString(binary_string)
- encoding_scheme = database.pattern.GetPatternByImageId(
- image_id)['encoding_scheme']
- encoder = _Encoder[encoding_scheme]
- encoded_string = encoder.Encode(binary_string)
- # Make project name part of the checksum.
- encoded_string += encoder.Checksum(
- database.project.upper() + ' ' + encoded_string)
- # Insert dashes to increase readibility.
- encoded_string = ('-'.join(
- [encoded_string[i:i + encoder.DASH_INSERTION_WIDTH]
- for i in xrange(0, len(encoded_string), encoder.DASH_INSERTION_WIDTH)]))
- return database.project.upper() + ' ' + encoded_string
-
-
-def Encode(database, bom, mode=common.HWID.OPERATION_MODE.normal,
- skip_check=False):
- """Encodes all the given BOM object.
-
- Args:
- database: A Database object that is used to provide device-specific
- information for encoding.
- bom: A BOM object.
- mode: The operation mode of the generated HWID object. Valid values are:
- ('normal', 'rma')
- skip_check: Whether to skip HWID verification checks. Set to True when
- generating HWID skeleton objects for further processing.
-
- Returns:
- A HWID object which contains the BOM, the binary string, and the encoded
- string derived from the given BOM object.
- """
- hwid = common.HWID(database, bom=bom, mode=mode, skip_check=skip_check)
- return hwid
diff --git a/py/hwid/v3/encoder_unittest.py b/py/hwid/v3/encoder_unittest.py
deleted file mode 100755
index c6235af..0000000
--- a/py/hwid/v3/encoder_unittest.py
+++ /dev/null
@@ -1,113 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-#
-# Copyright 2013 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.common import HWIDException
-from cros.factory.hwid.v3.database import Database
-from cros.factory.hwid.v3.encoder import BinaryStringToEncodedString
-from cros.factory.hwid.v3.encoder import BOMToBinaryString
-from cros.factory.hwid.v3.encoder import Encode
-from cros.factory.hwid.v3 import hwid_utils
-from cros.factory.utils import json_utils
-
-_TEST_DATA_PATH = os.path.join(os.path.dirname(__file__), 'testdata')
-
-
-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.
- bom = self.database.UpdateComponentsOfBOM(bom, {
- 'keyboard': 'keyboard_us', 'display_panel': 'display_panel_0'})
- self.assertEquals(
- '0000000000111010000011', BOMToBinaryString(self.database, bom))
- # Change firmware's encoded index to 1.
- mocked_bom = self.database.UpdateComponentsOfBOM(
- bom, {'ro_main_firmware': 'ro_main_firmware_1'})
- self.assertEquals(
- '0000000001111010000011', BOMToBinaryString(self.database, mocked_bom))
- # Change image id to 2.
- mocked_bom.image_id = 2
- self.assertEquals(
- '0001000001111010000011', BOMToBinaryString(self.database, mocked_bom))
- # Change encoding pattern index to 1.
- mocked_bom.encoding_pattern_index = 1
- self.assertEquals(
- '1001000001111010000011', BOMToBinaryString(self.database, mocked_bom))
-
- def testBinaryStringToEncodedString(self):
- self.assertEquals('CHROMEBOOK A5AU-LU',
- BinaryStringToEncodedString(
- self.database, '000001110100000101'))
- self.assertEquals('CHROMEBOOK C9I-F4N',
- BinaryStringToEncodedString(
- self.database, '000101110100000101'))
-
- def testEncode(self):
- bom = hwid_utils.GenerateBOMFromProbedResults(self.database,
- self.results[0])
- # Manually set unprobeable components.
- bom = self.database.UpdateComponentsOfBOM(bom, {
- 'keyboard': 'keyboard_us', 'display_panel': 'display_panel_0'})
- bom.image_id = 0
- hwid = Encode(self.database, bom)
- self.assertEquals('0000000000111010000011', hwid.binary_string)
- self.assertEquals('CHROMEBOOK AA5A-Y6L', hwid.encoded_string)
-
- bom.image_id = 2
- hwid = Encode(self.database, bom)
- self.assertEquals('0001000000111010000011', hwid.binary_string)
- self.assertEquals('CHROMEBOOK C2H-I3Q-A6Q', hwid.encoded_string)
-
- 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)
- bom = self.database.UpdateComponentsOfBOM(bom, {
- 'keyboard': 'keyboard_us', 'display_panel': 'display_panel_0'})
- self.assertRaisesRegexp(
- HWIDException, "Encoded fields 'dram' have unknown indices",
- Encode, self.database, bom)
-
- # 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)
- bom = self.database.UpdateComponentsOfBOM(bom, {
- 'keyboard': 'keyboard_us', 'display_panel': 'display_panel_0'})
- self.assertRaisesRegexp(
- HWIDException, "Unknown component values: "
- "\"dram:{'size': '4G', 'vendor': 'FOO'}\"",
- Encode, self.database, 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'])
- hwid = Encode(db, bom)
- # The encoder should encode field index 29 into the region field.
- self.assertEquals('00000000111011', hwid.binary_string)
- self.assertEquals('CHROMEBOOK A25-Q22', hwid.encoded_string)
-
-
-if __name__ == '__main__':
- unittest.main()
diff --git a/py/hwid/v3/hwid_cmdline.py b/py/hwid/v3/hwid_cmdline.py
index 8548dd4..0c86188 100755
--- a/py/hwid/v3/hwid_cmdline.py
+++ b/py/hwid/v3/hwid_cmdline.py
@@ -13,7 +13,7 @@
import sys
import factory_common # pylint: disable=W0611
-from cros.factory.hwid.v3 import common
+from cros.factory.hwid.v3.bom import ProbedComponentResult
from cros.factory.hwid.v3 import database
from cros.factory.hwid.v3 import hwid_utils
from cros.factory.hwid.v3 import rule
@@ -276,7 +276,7 @@
options.components)
if options.json_output:
def _ConvertToDict(obj):
- if isinstance(obj, (common.ProbedComponentResult, rule.Value)):
+ if isinstance(obj, (ProbedComponentResult, rule.Value)):
return _ConvertToDict(obj.__dict__)
if isinstance(obj, list):
return [_ConvertToDict(item) for item in obj]
@@ -371,7 +371,7 @@
def InitializeDefaultOptions(options):
if not options.hwid_db_path:
- options.hwid_db_path = hwid_utils.DEFAULT_HWID_DATA_PATH
+ options.hwid_db_path = hwid_utils.GetDefaultDataPath()
if options.project is None:
if sys_utils.InChroot():
print 'Argument -j/--project is required'
diff --git a/py/hwid/v3/hwid_rule_functions.py b/py/hwid/v3/hwid_rule_functions.py
index ac5b26f..1adcf4c 100644
--- a/py/hwid/v3/hwid_rule_functions.py
+++ b/py/hwid/v3/hwid_rule_functions.py
@@ -18,7 +18,7 @@
from cros.factory.utils.type_utils import MakeList
-def GetClassAttributesOnBOM(hwid, comp_cls):
+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
@@ -36,7 +36,8 @@
}
Args:
- hwid: The HWID context to extract attributes from.
+ 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:
@@ -52,17 +53,17 @@
if c.component_name:
results.append(c.component_name)
continue
- matched_component = hwid.database.components.MatchComponentsFromValues(
+ 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 hwid.database.components.GetRequiredComponents():
+ 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(hwid.bom, comp_cls)
+ results = PackProbedValues(bom, comp_cls)
# If the set is empty, add a None element indicating that the component
# class is missing.
if not results:
@@ -80,7 +81,7 @@
all.
"""
context = GetContext()
- attrs = GetClassAttributesOnBOM(context.hwid, comp_cls)
+ attrs = GetClassAttributesOnBOM(context.database, context.bom, comp_cls)
if attrs is None:
return False
values = [
@@ -89,7 +90,7 @@
[any([v.Matches(attr) for attr in attrs]) for v in values])
-@RuleFunction(['hwid'])
+@RuleFunction(['bom', 'database'])
def ComponentEq(comp_cls, values):
"""Test if the component equals to the values set.
@@ -105,7 +106,7 @@
return _ComponentCompare(comp_cls, values, all)
-@RuleFunction(['hwid'])
+@RuleFunction(['bom', 'database'])
def ComponentIn(comp_cls, values):
"""Test if the component is in the values set.
@@ -121,7 +122,7 @@
return _ComponentCompare(comp_cls, values, any)
-@RuleFunction(['hwid'])
+@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.
@@ -131,11 +132,10 @@
name: The component name to set to the given component class.
"""
context = GetContext()
- context.hwid.bom = context.hwid.database.UpdateComponentsOfBOM(
- context.hwid.bom, {comp_cls: name})
+ context.database.UpdateComponentsOfBOM(context.bom, {comp_cls: name})
-@RuleFunction(['hwid'])
+@RuleFunction(['bom', 'database'])
def SetImageId(image_id):
"""A function to set the image id of the given HWID context.
@@ -146,34 +146,34 @@
if isinstance(image_id, str):
# Convert image_id string to its corresponding encoded value.
reversed_image_id_dict = dict((value, key) for key, value in
- context.hwid.database.image_id.iteritems())
+ context.database.image_id.iteritems())
if image_id not in reversed_image_id_dict:
raise HWIDException('Invalid image id: %r' % image_id)
image_id = reversed_image_id_dict[image_id]
- if image_id not in context.hwid.database.image_id:
+ if image_id not in context.database.image_id:
raise HWIDException('Invalid image id: %r' % image_id)
- context.hwid.bom.image_id = image_id
+ context.bom.image_id = image_id
-@RuleFunction(['hwid'])
+@RuleFunction(['bom'])
def GetImageId():
"""A function to get the image id from the given HWID context.
Returns:
The image id of the HWID context.
"""
- return GetContext().hwid.bom.image_id
+ return GetContext().bom.image_id
-@RuleFunction(['hwid'])
+@RuleFunction(['mode'])
def GetOperationMode():
"""A function to get the set of operation modes of the HWID context.
Returns:
The set of operations modes currently enabled on the given HWID context.
"""
- return GetContext().hwid.mode
+ return GetContext().mode
@RuleFunction(['device_info'])
@@ -237,7 +237,7 @@
# pylint: disable=W0622
-@RuleFunction(['hwid'])
+@RuleFunction(['database'])
def CheckRegistrationCode(code, type=None, device=None):
"""A wrapper method to verify registration code.
@@ -267,7 +267,7 @@
# (e.g., "spring", not "daisy_spring"). For Zerg devices this may be chassis
# or project ID.
if device is None:
- device = GetContext().hwid.database.project.lower()
+ device = GetContext().database.project.lower()
registration_codes.CheckRegistrationCode(code, type=type, device=device)
diff --git a/py/hwid/v3/hwid_rule_functions_unittest.py b/py/hwid/v3/hwid_rule_functions_unittest.py
index 21eb38a..48b723a 100755
--- a/py/hwid/v3/hwid_rule_functions_unittest.py
+++ b/py/hwid/v3/hwid_rule_functions_unittest.py
@@ -15,7 +15,7 @@
import cros.factory.hwid.v3.common_rule_functions # pylint: disable=W0611
from cros.factory.hwid.v3.common import HWIDException
from cros.factory.hwid.v3.database import Database
-from cros.factory.hwid.v3.encoder import Encode
+from cros.factory.hwid.v3.transformer import Encode
from cros.factory.hwid.v3 import hwid_utils
from cros.factory.hwid.v3.hwid_rule_functions import CheckRegistrationCode
from cros.factory.hwid.v3.hwid_rule_functions import ComponentEq
@@ -49,7 +49,7 @@
os.path.join(_TEST_DATA_PATH, 'test_probe_result.json'))
bom = hwid_utils.GenerateBOMFromProbedResults(self.database,
self.results[0])
- bom = self.database.UpdateComponentsOfBOM(bom, {
+ self.database.UpdateComponentsOfBOM(bom, {
'keyboard': 'keyboard_us', 'dram': 'dram_0',
'display_panel': 'display_panel_0'})
self.hwid = Encode(self.database, bom)
@@ -66,8 +66,9 @@
'registration_code': 'buz'
}
}
- self.context = Context(hwid=self.hwid, device_info=self.device_info,
- vpd=self.vpd)
+ self.context = Context(
+ database=self.hwid.database, bom=self.hwid.bom, mode=self.hwid.mode,
+ device_info=self.device_info, vpd=self.vpd)
SetContext(self.context)
def testRule(self):
@@ -171,10 +172,12 @@
rule.Evaluate, self.context)
def testGetClassAttributesOnBOM(self):
- cpu_attrs = GetClassAttributesOnBOM(self.hwid, 'cpu')
+ cpu_attrs = GetClassAttributesOnBOM(
+ self.hwid.database, self.hwid.bom, 'cpu')
self.assertEquals(['cpu_5'], cpu_attrs)
- self.assertEquals(None, GetClassAttributesOnBOM(self.hwid, 'foo'))
+ self.assertEquals(
+ None, GetClassAttributesOnBOM(self.hwid.database, self.hwid.bom, 'foo'))
self.assertEquals("ERROR: Invalid component class: 'foo'",
GetLogger().error[0].message)
@@ -191,17 +194,17 @@
def testSetComponent(self):
SetComponent('cpu', 'cpu_3')
self.assertEquals(
- 'cpu_3', self.context.hwid.bom.components['cpu'][0].component_name)
+ 'cpu_3', self.context.bom.components['cpu'][0].component_name)
self.assertEquals(
- 3, self.context.hwid.bom.encoded_fields['cpu'])
+ 3, self.context.bom.encoded_fields['cpu'])
self.assertEquals('0000000000111010010001', self.hwid.binary_string)
self.assertEquals('CHROMEBOOK AA5E-IVL', self.hwid.encoded_string)
SetComponent('cellular', 'cellular_0')
self.assertEquals(
'cellular_0',
- self.context.hwid.bom.components['cellular'][0].component_name)
+ self.context.bom.components['cellular'][0].component_name)
self.assertEquals(
- 1, self.context.hwid.bom.encoded_fields['cellular'])
+ 1, self.context.bom.encoded_fields['cellular'])
self.assertEquals('0000000000111110010001', self.hwid.binary_string)
self.assertEquals('CHROMEBOOK AA7E-IWF', self.hwid.encoded_string)
diff --git a/py/hwid/v3/hwid_utils.py b/py/hwid/v3/hwid_utils.py
index cddaec0..fc875ca 100644
--- a/py/hwid/v3/hwid_utils.py
+++ b/py/hwid/v3/hwid_utils.py
@@ -7,21 +7,17 @@
import collections
import logging
import os
-import subprocess
import factory_common # pylint: disable=W0611
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
-from cros.factory.hwid.v3 import decoder
-from cros.factory.hwid.v3 import encoder
from cros.factory.hwid.v3 import rule
+from cros.factory.hwid.v3 import transformer
from cros.factory.hwid.v3 import yaml_wrapper as yaml
-from cros.factory.utils import cros_board_utils
-from cros.factory.utils import process_utils
from cros.factory.utils import json_utils
-from cros.factory.utils import sys_utils
from cros.factory.utils import type_utils
@@ -77,10 +73,11 @@
"""
hwid_mode = _HWIDMode(rma_mode)
# Construct a base BOM from probe_results.
- hwid = encoder.Encode(db, bom, mode=hwid_mode, skip_check=True)
+ hwid = transformer.Encode(db, bom, mode=hwid_mode, skip_check=True)
# Update unprobeable components with rules defined in db before verification.
- context_args = dict(hwid=hwid, device_info=device_info)
+ context_args = dict(database=hwid.database, bom=hwid.bom, mode=hwid.mode,
+ device_info=device_info)
if vpd is not None:
context_args['vpd'] = vpd
context = rule.Context(**context_args)
@@ -99,7 +96,7 @@
Returns:
The decoded HWIDv3 context object.
"""
- return decoder.Decode(db, encoded_string)
+ return transformer.Decode(db, encoded_string)
def ParseDecodedHWID(hwid):
@@ -164,11 +161,11 @@
HWIDException if verification fails.
"""
hwid_mode = _HWIDMode(rma_mode)
- hwid = decoder.Decode(db, encoded_string, mode=hwid_mode)
+ hwid = transformer.Decode(db, encoded_string, mode=hwid_mode)
hwid.VerifyBOM(bom)
hwid.VerifyComponentStatus(current_phase=current_phase)
hwid.VerifyPhase(current_phase)
- context_args = dict(hwid=hwid)
+ context_args = dict(database=hwid.database, bom=hwid.bom, mode=hwid.mode)
if vpd is not None:
context_args['vpd'] = vpd
context = rule.Context(**context_args)
@@ -258,7 +255,7 @@
for comp_cls, attr_list in attr_dict.iteritems():
if attr_list is None:
comp_items.append('None')
- components[comp_cls].append(common.ProbedComponentResult(
+ components[comp_cls].append(ProbedComponentResult(
None, None, common.MISSING_COMPONENT_ERROR(comp_cls)))
else:
for attrs in attr_list:
@@ -278,14 +275,15 @@
attrs['status'])
break
comp_items.append(attrs['name'])
- components[comp_cls].append(common.ProbedComponentResult(
+ 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 = encoder.BOMToBinaryString(db, bom)
- encoded_string = encoder.BinaryStringToEncodedString(db, binary_string)
+ binary_string = transformer.BOMToBinaryString(db, bom)
+ encoded_string = transformer.BinaryStringToEncodedString(
+ db, binary_string)
hwid_dict[encoded_string] = ','.join(component_list)
def _RecursivelyGenerate(index=None, encoded_fields=None):
@@ -371,6 +369,8 @@
elif raw_data:
return json_utils.LoadStr(raw_data)
else:
+ from cros.factory.utils import process_utils
+ from cros.factory.utils import sys_utils
if sys_utils.InChroot():
raise ValueError('Cannot probe components in chroot. Please specify '
'probed results with an input file. If you are running '
@@ -424,7 +424,7 @@
comp_status = database.components.GetComponentStatus(comp_cls, comp_name)
if comp_status != common.HWID.COMPONENT_STATUS.unsupported:
probed_components[comp_cls].append(
- common.ProbedComponentResult(comp_name, None, None))
+ ProbedComponentResult(comp_name, None, None))
return True
return False
@@ -439,7 +439,7 @@
# Probeable comp_cls but no component is found in probe results.
if comp_cls in database.components.probeable:
probed_components[comp_cls].append(
- common.ProbedComponentResult(
+ ProbedComponentResult(
None, None, common.MISSING_COMPONENT_ERROR(comp_cls)))
else:
# Unprobeable comp_cls and only has 1 component, treat as found.
@@ -450,16 +450,15 @@
comp_cls, comp_name)
if comp_status == common.HWID.COMPONENT_STATUS.supported:
probed_components[comp_cls].append(
- common.ProbedComponentResult(comp_name, None, None))
+ ProbedComponentResult(comp_name, None, None))
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(
- common.ProbedComponentResult(
- None, probed_value,
- common.UNPROBEABLE_COMPONENT_ERROR(comp_cls)))
+ probed_components[comp_cls].append(ProbedComponentResult(
+ None, probed_value,
+ common.UNPROBEABLE_COMPONENT_ERROR(comp_cls)))
continue
matched_comps = database.components.MatchComponentsFromValues(
@@ -467,7 +466,7 @@
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(common.ProbedComponentResult(
+ probed_components[comp_cls].append(ProbedComponentResult(
None, probed_value,
common.INVALID_COMPONENT_ERROR(comp_cls, probed_value)))
elif len(matched_comps) == 1:
@@ -476,16 +475,14 @@
comp_cls, comp_name)
if comp_status == common.HWID.COMPONENT_STATUS.supported:
probed_components[comp_cls].append(
- common.ProbedComponentResult(
- comp_name, comp_data['values'], None))
+ ProbedComponentResult(comp_name, comp_data['values'], None))
else:
- probed_components[comp_cls].append(
- common.ProbedComponentResult(
- comp_name, comp_data['values'],
- common.UNSUPPORTED_COMPONENT_ERROR(comp_cls, comp_name,
- comp_status)))
+ probed_components[comp_cls].append(ProbedComponentResult(
+ comp_name, comp_data['values'],
+ common.UNSUPPORTED_COMPONENT_ERROR(comp_cls, comp_name,
+ comp_status)))
elif len(matched_comps) > 1:
- probed_components[comp_cls].append(common.ProbedComponentResult(
+ probed_components[comp_cls].append(ProbedComponentResult(
None, probed_value,
common.AMBIGUOUS_COMPONENT_ERROR(
comp_cls, probed_value, matched_comps)))
@@ -531,6 +528,7 @@
"""
assert not (run_vpd and vpd_data_file)
if run_vpd:
+ from cros.factory.utils import sys_utils
vpd_tool = sys_utils.VPDTool()
return {
'ro': vpd_tool.GetAllData(partition=vpd_tool.RO_PARTITION),
@@ -550,7 +548,7 @@
def ProbeProject():
"""Probes the project name.
- This function will try to run the command `mosys platform chassis` to get the
+ This function will try to run the command `mosys platform model` to get the
project name. If failed, this function will return the board name as legacy
chromebook projects used to assume that the board name is equal to the
project name.
@@ -558,6 +556,11 @@
Returns:
The probed project name as a string.
"""
+ import subprocess
+
+ from cros.factory.utils import process_utils
+ from cros.factory.utils import cros_board_utils
+
try:
project = process_utils.CheckOutput(
['mosys', 'platform', 'model']).strip().lower()
@@ -570,13 +573,23 @@
return cros_board_utils.BuildBoard().short_name
-# The expected location of HWID data within a factory image or the
-# chroot.
-DEFAULT_HWID_DATA_PATH = (
- os.path.join(os.environ['CROS_WORKON_SRCROOT'],
- 'src', 'platform', 'chromeos-hwid', 'v3')
- if sys_utils.InChroot()
- else '/usr/local/factory/hwid')
+_DEFAULT_DATA_PATH = None
+
+def GetDefaultDataPath():
+ """Returns the expected location of HWID data within a factory image or the
+ chroot.
+ """
+ from cros.factory.utils import sys_utils
+
+ global _DEFAULT_DATA_PATH # pylint: disable=global-statement
+ if _DEFAULT_DATA_PATH is None:
+ if sys_utils.InChroot():
+ _DEFAULT_DATA_PATH = os.path.join(
+ os.environ['CROS_WORKON_SRCROOT'],
+ 'src', 'platform', 'chromeos-hwid', 'v3')
+ else:
+ _DEFAULT_DATA_PATH = '/usr/local/factory/hwid'
+ return _DEFAULT_DATA_PATH
def GetHWIDBundleName(project=None):
diff --git a/py/hwid/v3/transformer.py b/py/hwid/v3/transformer.py
new file mode 100644
index 0000000..b998969
--- /dev/null
+++ b/py/hwid/v3/transformer.py
@@ -0,0 +1,369 @@
+# Copyright 2013 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 factory_common # pylint: disable=W0611
+
+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.base32 import Base32
+from cros.factory.hwid.v3.base8192 import Base8192
+from cros.factory.hwid.v3.identity import Identity
+from cros.factory.utils import type_utils
+
+
+_Encoder = {
+ common.HWID.ENCODING_SCHEME.base32: Base32,
+ common.HWID.ENCODING_SCHEME.base8192: Base8192
+}
+
+_Decoder = {
+ common.HWID.ENCODING_SCHEME.base32: Base32,
+ common.HWID.ENCODING_SCHEME.base8192: Base8192
+}
+
+
+def VerifyBinaryString(database, binary_string):
+ """Verifies the binary string.
+
+ Args:
+ binary_string: The binary string to verify.
+
+ Raises:
+ HWIDException if verification fails.
+ """
+ if set(binary_string) - set('01'):
+ raise common.HWIDException('Invalid binary string: %r' % binary_string)
+
+ if '1' not in binary_string:
+ raise common.HWIDException('Binary string %r does not have stop bit set',
+ binary_string)
+ # Truncate trailing 0s.
+ string_without_paddings = binary_string[:binary_string.rfind('1') + 1]
+
+ image_id = database.pattern.GetImageIdFromBinaryString(binary_string)
+ total_bit_length = database.pattern.GetTotalBitLength(image_id)
+ if len(string_without_paddings) > total_bit_length:
+ raise common.HWIDException(
+ 'Invalid bit string length of %r. Expected length <= %d, got length %d'
+ % (binary_string, total_bit_length, len(string_without_paddings)))
+
+
+def VerifyEncodedStringFormat(encoded_string):
+ """Verifies that the format of the given encoded string.
+
+ Checks that the string matches either base32 or base8192 format.
+
+ Args:
+ encoded_string: The encoded string to verify.
+
+ Raises:
+ HWIDException if verification fails.
+ """
+ if not any(hwid_format.match(encoded_string) for hwid_format in
+ common.HWID_FORMAT.itervalues()):
+ raise common.HWIDException(
+ 'HWID string %r is neither base32 nor base8192 encoded' %
+ encoded_string)
+
+
+def VerifyEncodedString(database, encoded_string):
+ """Verifies the given encoded string.
+
+ Args:
+ encoded_string: The encoded string to verify.
+
+ Raises:
+ HWIDException if verification fails.
+ """
+ try:
+ image_id = database.pattern.GetImageIdFromEncodedString(encoded_string)
+ encoding_scheme = database.pattern.GetPatternByImageId(
+ image_id)['encoding_scheme']
+ project, bom_checksum = common.HWID_FORMAT[encoding_scheme].findall(
+ encoded_string)[0]
+ except IndexError:
+ raise common.HWIDException(
+ 'Invalid HWID string format: %r' % encoded_string)
+ if len(bom_checksum) < 2:
+ raise common.HWIDException(
+ 'Length of encoded string %r is less than 2 characters' %
+ bom_checksum)
+ if project != database.project.upper():
+ raise common.HWIDException('Invalid project name: %r' % project)
+ # Verify the checksum
+ stripped = encoded_string.replace('-', '')
+ hwid = stripped[:-2]
+ checksum = stripped[-2:]
+ if encoding_scheme == common.HWID.ENCODING_SCHEME.base32:
+ expected_checksum = Base32.Checksum(hwid)
+ elif encoding_scheme == common.HWID.ENCODING_SCHEME.base8192:
+ expected_checksum = Base8192.Checksum(hwid)
+ if checksum != expected_checksum:
+ raise common.HWIDException('Checksum of %r mismatch (expected %r)' % (
+ encoded_string, expected_checksum))
+
+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 BOMToBinaryString(database, bom):
+ """Encodes the given BOM object to a binary string.
+
+ Args:
+ database: A Database object that is used to provide device-specific
+ information for encoding.
+
+ Returns:
+ A binary string.
+ """
+ VerifyBOM(database, bom)
+ bit_length = database.pattern.GetTotalBitLength(bom.image_id)
+ binary_list = bit_length * [0]
+
+ # Fill in header.
+ binary_list[0] = bom.encoding_pattern_index
+ for i in xrange(1, 5):
+ binary_list[i] = (bom.image_id >> (4 - i)) & 1
+ # 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
+ # Set stop bit.
+ binary_list[bit_length - 1] = 1
+ return ''.join(['%d' % bit for bit in binary_list])
+
+
+def BinaryStringToEncodedString(database, binary_string):
+ """Encodes the given binary string to a encoded string.
+
+ Args:
+ database: A Database object that is used to provide device-specific
+ information for encoding.
+ binary_string: A string of '0's and '1's.
+
+ Returns:
+ An encoded string with project name, base32-encoded HWID, and checksum.
+ """
+ VerifyBinaryString(database, binary_string)
+ image_id = database.pattern.GetImageIdFromBinaryString(binary_string)
+ encoding_scheme = database.pattern.GetPatternByImageId(
+ image_id)['encoding_scheme']
+ encoder = _Encoder[encoding_scheme]
+ encoded_string = encoder.Encode(binary_string)
+ # Make project name part of the checksum.
+ encoded_string += encoder.Checksum(
+ database.project.upper() + ' ' + encoded_string)
+ # Insert dashes to increase readibility.
+ encoded_string = ('-'.join(
+ [encoded_string[i:i + encoder.DASH_INSERTION_WIDTH]
+ for i in xrange(0, len(encoded_string), encoder.DASH_INSERTION_WIDTH)]))
+ return database.project.upper() + ' ' + encoded_string
+
+
+def Encode(database, bom, mode=common.HWID.OPERATION_MODE.normal,
+ skip_check=False):
+ """Encodes all the given BOM object.
+
+ Args:
+ database: A Database object that is used to provide device-specific
+ information for encoding.
+ bom: A BOM object.
+ mode: The operation mode of the generated HWID object. Valid values are:
+ ('normal', 'rma')
+ skip_check: Whether to skip HWID verification checks. Set to True when
+ generating HWID skeleton objects for further processing.
+
+ Returns:
+ A HWID object which contains the BOM, the binary string, and the encoded
+ string derived from the given BOM object.
+ """
+ hwid = common.HWID(database, bom=bom, mode=mode, skip_check=skip_check)
+ return hwid
+
+
+def BinaryStringToBOM(database, binary_string):
+ """Decodes the given binary string to a BOM object.
+
+ Args:
+ database: A Database object that is used to provide device-specific
+ information for decoding.
+ binary_string: A binary string.
+
+ Returns:
+ A BOM object
+ """
+ VerifyBinaryString(database, binary_string)
+ stripped_binary_string = binary_string[:binary_string.rfind('1')]
+
+ project = database.project
+ encoding_pattern = int(stripped_binary_string[0], 2)
+ image_id = database.pattern.GetImageIdFromBinaryString(binary_string)
+
+ # Construct the encoded fields dict.
+ encoded_fields = collections.defaultdict(int)
+ for field_name in database.pattern.GetFieldNames(image_id):
+ encoded_fields[field_name] = 0
+ bit_mapping = database.pattern.GetBitMapping(
+ image_id=image_id, binary_string_length=len(stripped_binary_string))
+ for i, (field, bit_offset) in bit_mapping.iteritems():
+ if i >= len(stripped_binary_string):
+ break
+ encoded_fields[field] += int(stripped_binary_string[i], 2) << bit_offset
+
+ # Check that all the encoded field indices are valid.
+ expected_encoded_fields = database.pattern.GetFieldNames(image_id)
+ missing_fields = set(expected_encoded_fields) - set(encoded_fields.keys())
+ if missing_fields:
+ raise common.HWIDException('Index of the fields are missing: %r' %
+ list(missing_fields))
+ for field in encoded_fields:
+ if encoded_fields[field] not in database.encoded_fields[field]:
+ raise common.HWIDException('Invalid encoded field index: {%r: %r}' %
+ (field, encoded_fields[field]))
+
+ # Construct the components dict.
+ components = collections.defaultdict(list)
+ 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))
+
+ return BOM(project, encoding_pattern, image_id, components, encoded_fields)
+
+
+def EncodedStringToBinaryString(database, encoded_string):
+ """Decodes the given encoded HWID string to a binary string.
+
+ Args:
+ database: A Database object that is used to provide device-specific
+ information for decoding.
+ encoded_string: An encoded string (with or without dashed).
+
+ Returns:
+ A binary string.
+ """
+ VerifyEncodedStringFormat(encoded_string)
+ image_id = database.pattern.GetImageIdFromEncodedString(encoded_string)
+ encoding_scheme = database.pattern.GetPatternByImageId(
+ image_id)['encoding_scheme']
+ VerifyEncodedString(database, encoded_string)
+ _, hwid_string = encoded_string.split(' ')
+ hwid_string = hwid_string.replace('-', '')
+ return _Decoder[encoding_scheme].Decode(
+ hwid_string)[:-_Decoder[encoding_scheme].CHECKSUM_SIZE].rstrip('0')
+
+
+def Decode(database, encoded_string, mode=common.HWID.OPERATION_MODE.normal):
+ """Decodes the given encoded string to a HWID object.
+
+ Args:
+ database: A Database object that is used to provide device-specific
+ information for decoding.
+ encoded_string: An encoded string.
+ mode: The operation mode of the generated HWID object. Valid values are:
+ ('normal', 'rma')
+
+ Returns:
+ A HWID object which contains the BOM, the binary string, and the encoded
+ string derived from the given encoded string.
+ """
+ binary_string = EncodedStringToBinaryString(database, encoded_string)
+ identity = Identity(database.project, binary_string, encoded_string)
+ bom = BinaryStringToBOM(database, binary_string)
+ return common.HWID(database, bom=bom, identity=identity, mode=mode)
diff --git a/py/hwid/v3/transformer_unittest.py b/py/hwid/v3/transformer_unittest.py
new file mode 100755
index 0000000..fe1b5e4
--- /dev/null
+++ b/py/hwid/v3/transformer_unittest.py
@@ -0,0 +1,419 @@
+#!/usr/bin/env python
+# 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.
+
+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.common import HWIDException
+from cros.factory.hwid.v3.database import Database
+from cros.factory.hwid.v3.transformer import BinaryStringToBOM
+from cros.factory.hwid.v3.transformer import BinaryStringToEncodedString
+from cros.factory.hwid.v3.transformer import BOMToBinaryString
+from cros.factory.hwid.v3.transformer import Decode
+from cros.factory.hwid.v3.transformer import Encode
+from cros.factory.hwid.v3.transformer import EncodedStringToBinaryString
+from cros.factory.hwid.v3.transformer import VerifyBinaryString
+from cros.factory.hwid.v3.transformer import VerifyEncodedString
+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')
+
+
+class VerifyBinaryStringTest(unittest.TestCase):
+ def setUp(self):
+ self.database = Database.LoadFile(os.path.join(_TEST_DATA_PATH,
+ 'test_db.yaml'))
+
+ def testVerifyBinaryString(self):
+ func = lambda s: VerifyBinaryString(self.database, s)
+
+ self.assertEquals(None, func('0000000000111010000011000'))
+ self.assertRaisesRegexp(
+ HWIDException, r'Invalid binary string: .*',
+ func, '020001010011011011000')
+ self.assertRaisesRegexp(
+ HWIDException, r'Binary string .* does not have stop bit set',
+ func, '00000')
+ self.assertRaisesRegexp(
+ HWIDException, r'Invalid bit string length',
+ func, '000000000010100110110111000')
+
+
+class VerifyEncodedStringTest(unittest.TestCase):
+ def setUp(self):
+ self.database = Database.LoadFile(os.path.join(_TEST_DATA_PATH,
+ 'test_db.yaml'))
+
+ def testVerifyEncodedString(self):
+ func = lambda s: VerifyEncodedString(self.database, s)
+
+ self.assertEquals(None, func('CHROMEBOOK AW3L-M7I7-V'))
+ self.assertRaisesRegexp(
+ HWIDException, r'Invalid HWID string format', func, 'AW3L-M7I5-4')
+ self.assertRaisesRegexp(
+ HWIDException, r'Length of encoded string .* is less than 2 characters',
+ func, 'FOO A')
+ self.assertRaisesRegexp(
+ HWIDException, r'Invalid project name', func, 'FOO AW3L-M7IK-W')
+ self.assertRaisesRegexp(
+ HWIDException, r'Checksum of .* mismatch',
+ func, 'CHROMEBOOK AW3L-M7IA-B')
+
+
+class VerifyBOMTest(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 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))
+
+ 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
+
+ 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
+
+ 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
+
+ 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
+
+
+class NewVerifyBOMTest(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 testVerifyBOMWithDefault(self):
+ self.assertEquals(None, VerifyBOM(self.database, self.boms[6], True))
+
+
+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])
+
+ def testEncodedStringToBinaryString(self):
+ self.assertEquals('0000000000111010000011',
+ EncodedStringToBinaryString(
+ self.database, 'CHROMEBOOK AA5A-Y6L'))
+ self.assertEquals('0001000000111010000011',
+ EncodedStringToBinaryString(
+ self.database, 'CHROMEBOOK C2H-I3Q-A6Q'))
+ self.assertEquals('1000000000111010000011',
+ EncodedStringToBinaryString(
+ self.database, 'CHROMEBOOK QA5A-YCJ'))
+
+ 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 = BinaryStringToBOM(self.database, '0000000000111010000011')
+ self._CheckBOM(reference_bom, bom)
+
+ bom = BinaryStringToBOM(self.database, '0000000001111010000011')
+ self.assertEquals(1, bom.encoded_fields['firmware'])
+ self.assertEquals(2, BinaryStringToBOM(
+ self.database, '0001000000111010000011').image_id)
+ self.assertEquals(1, BinaryStringToBOM(
+ self.database, '1000000000111010000011').encoding_pattern_index)
+ self.assertRaisesRegexp(
+ HWIDException, r"Invalid encoded field index: {'cpu': 6}",
+ 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 = 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 = 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'})
+ hwid = Decode(self.database, 'CHROMEBOOK AA5A-Y6L')
+ self.assertEquals('0000000000111010000011', hwid.binary_string)
+ self.assertEquals('CHROMEBOOK AA5A-Y6L', hwid.encoded_string)
+ self._CheckBOM(reference_bom, hwid.bom)
+
+ hwid = Decode(self.database, 'CHROMEBOOK C2H-I3Q-A6Q')
+ self.assertEquals('0001000000111010000011', hwid.binary_string)
+ self.assertEquals('CHROMEBOOK C2H-I3Q-A6Q', hwid.encoded_string)
+ reference_bom.image_id = 2
+ self._CheckBOM(reference_bom, hwid.bom)
+
+ def testPreviousVersionOfEncodedString(self):
+ bom = BinaryStringToBOM(self.database, '000000000011101000001')
+ self.assertEquals(1, bom.encoded_fields['cpu'])
+ hwid = Decode(self.database, 'CHROMEBOOK AA5A-Q7Z')
+ self.assertEquals('000000000011101000001', hwid.binary_string)
+ self.assertEquals('CHROMEBOOK AA5A-Q7Z', hwid.encoded_string)
+ self.assertEquals(1, hwid.bom.encoded_fields['cpu'])
+
+ def testDecodeRegion(self):
+ db = Database.LoadFile(
+ os.path.join(_TEST_DATA_PATH, 'test_db_regions.yaml'))
+ hwid = Decode(db, 'CHROMEBOOK A25-Q22')
+ # The BOM should load 'us' region from the probe result (numeric_id=29).)
+ self.assertEquals(29, hwid.bom.encoded_fields['region_field'])
+ self.assertEquals(
+ [('us', {'region_code': Value('us')}, None)],
+ hwid.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', BOMToBinaryString(self.database, bom))
+ # 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', BOMToBinaryString(self.database, mocked_bom))
+ # Change image id to 2.
+ mocked_bom.image_id = 2
+ self.assertEquals(
+ '0001000001111010000011', BOMToBinaryString(self.database, mocked_bom))
+ # Change encoding pattern index to 1.
+ mocked_bom.encoding_pattern_index = 1
+ self.assertEquals(
+ '1001000001111010000011', BOMToBinaryString(self.database, mocked_bom))
+
+ def testBinaryStringToEncodedString(self):
+ self.assertEquals('CHROMEBOOK A5AU-LU',
+ BinaryStringToEncodedString(
+ self.database, '000001110100000101'))
+ self.assertEquals('CHROMEBOOK C9I-F4N',
+ BinaryStringToEncodedString(
+ self.database, '000101110100000101'))
+
+ 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
+ hwid = Encode(self.database, bom)
+ self.assertEquals('0000000000111010000011', hwid.binary_string)
+ self.assertEquals('CHROMEBOOK AA5A-Y6L', hwid.encoded_string)
+
+ bom.image_id = 2
+ hwid = Encode(self.database, bom)
+ self.assertEquals('0001000000111010000011', hwid.binary_string)
+ self.assertEquals('CHROMEBOOK C2H-I3Q-A6Q', hwid.encoded_string)
+
+ 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",
+ Encode, self.database, bom)
+
+ # 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'}\"",
+ Encode, self.database, 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'])
+ hwid = Encode(db, bom)
+ # The encoder should encode field index 29 into the region field.
+ self.assertEquals('00000000111011', hwid.binary_string)
+ self.assertEquals('CHROMEBOOK A25-Q22', hwid.encoded_string)
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/py/hwid/v3/valid_hwid_db_unittest.py b/py/hwid/v3/valid_hwid_db_unittest.py
index 7dac596..ba854c3 100755
--- a/py/hwid/v3/valid_hwid_db_unittest.py
+++ b/py/hwid/v3/valid_hwid_db_unittest.py
@@ -299,8 +299,9 @@
hwid = hwid_utils.GenerateHWID(db, bom, device_info,
vpd=vpd, rma_mode=rma_mode)
# Test all rules.
- db.rules.EvaluateRules(Context(hwid=hwid, vpd=vpd,
- device_info=device_info))
+ db.rules.EvaluateRules(Context(
+ database=hwid.database, bom=hwid.bom, mode=hwid.mode, vpd=vpd,
+ device_info=device_info))
return hwid
if error:
diff --git a/py/test/event_log_unittest.py b/py/test/event_log_unittest.py
index 8b98046..9719aa6 100755
--- a/py/test/event_log_unittest.py
+++ b/py/test/event_log_unittest.py
@@ -23,7 +23,6 @@
import yaml
import factory_common # pylint: disable=unused-import
-from cros.factory.hwid.v3.common import ProbedComponentResult
from cros.factory.test import event_log
from cros.factory.test import session
from cros.factory.utils import file_utils
@@ -34,6 +33,9 @@
'[a-f0-9]{4}-[a-f0-9]{12}$')
+_TestNamedTuple = collections.namedtuple('_TestNamedTuple', ['a', 'b', 'c'])
+
+
def Reset():
# Deletes state files and resets global variables.
event_log.device_id = event_log.reimage_id = None
@@ -92,7 +94,7 @@
dump(collections.OrderedDict([('bar', 1), ('foo', 3)])))
# A subclass of a list
self.assertEqual('\n'.join(['- comp_foo', '- value_foo', '- null']),
- dump(ProbedComponentResult('comp_foo', 'value_foo', None)))
+ dump(_TestNamedTuple('comp_foo', 'value_foo', None)))
# Tuple type
self.assertEqual('\n'.join(['- v1', '- v2', '- v3']),
dump(('v1', 'v2', 'v3')))
diff --git a/py/test/pytests/hwid.py b/py/test/pytests/hwid.py
index 282693a..508bd22 100644
--- a/py/test/pytests/hwid.py
+++ b/py/test/pytests/hwid.py
@@ -82,12 +82,12 @@
# If present, these files will override the project and probe results
# (for testing).
OVERRIDE_PROJECT_PATH = os.path.join(
- hwid_utils.DEFAULT_HWID_DATA_PATH, 'OVERRIDE_PROJECT')
+ hwid_utils.GetDefaultDataPath(), 'OVERRIDE_PROJECT')
# OVERRIDE_PROBED_RESULTS should be generated with:
# `gooftool probe`
# to include all the VPD in it.
OVERRIDE_PROBED_RESULTS_PATH = os.path.join(
- hwid_utils.DEFAULT_HWID_DATA_PATH, 'OVERRIDE_PROBED_RESULTS')
+ hwid_utils.GetDefaultDataPath(), 'OVERRIDE_PROBED_RESULTS')
class HWIDV3Test(test_ui.TestCaseWithUI):