Home | History | Annotate | Download | only in stable_images
      1 # Copyright 2016 The Chromium OS Authors. All rights reserved.
      2 # Use of this source code is governed by a BSD-style license that can be
      3 # found in the LICENSE file.
      4 
      5 """
      6 Unit tests for functions in `assign_stable_images`.
      7 """
      8 
      9 
     10 import json
     11 import mock
     12 import os
     13 import sys
     14 import unittest
     15 
     16 import common
     17 from autotest_lib.server import frontend
     18 from autotest_lib.site_utils.stable_images import assign_stable_images
     19 
     20 
     21 # _OMAHA_TEST_DATA - File with JSON data to be used as test input to
     22 #   `_make_omaha_versions()`.  In the file, the various items in the
     23 #   `omaha_data` list are selected to capture various specific test
     24 #   cases:
     25 #     + Board with no "beta" channel.
     26 #     + Board with "beta" and another channel.
     27 #     + Board with only a "beta" channel.
     28 #     + Board with no "chrome_version" entry.
     29 #     + Obsolete board with "is_active" set to false.
     30 # The JSON content of the file is a subset of an actual
     31 # `omaha_status.json` file copied when the unit test was last
     32 # updated.
     33 #
     34 # _EXPECTED_OMAHA_VERSIONS - The expected output produced by
     35 #   _STUB_OMAHA_DATA.
     36 #
     37 _OMAHA_TEST_DATA = 'test_omaha_status.json'
     38 
     39 _EXPECTED_OMAHA_VERSIONS = {'auron-paine': 'R55-8872.54.0',
     40                             'gale': 'R55-8872.40.9',
     41                             'kevin': 'R55-8872.64.0',
     42                             'zako-freon': 'R41-6680.52.0'}
     43 
     44 _DEFAULT_BOARD = assign_stable_images._DEFAULT_BOARD
     45 
     46 
     47 class OmahaDataTests(unittest.TestCase):
     48     """Tests for the `_make_omaha_versions()` function."""
     49 
     50     def test_make_omaha_versions(self):
     51         """
     52         Test `_make_omaha_versions()` against one simple input.
     53 
     54         This is a trivial sanity test that confirms that a single
     55         hard-coded input returns a correct hard-coded output.
     56         """
     57         module_dir = os.path.dirname(sys.modules[__name__].__file__)
     58         data_file_path = os.path.join(module_dir, _OMAHA_TEST_DATA)
     59         omaha_versions = assign_stable_images._make_omaha_versions(
     60                 json.load(open(data_file_path, 'r')))
     61         self.assertEqual(omaha_versions, _EXPECTED_OMAHA_VERSIONS)
     62 
     63 
     64 class KeyPathTests(unittest.TestCase):
     65     """Tests for the `_get_by_key_path()` function."""
     66 
     67     DICTDICT = {'level0': 'OK', 'level1_a': {'level1_b': 'OK'}}
     68 
     69     def _get_by_key_path(self, keypath):
     70         get_by_key_path = assign_stable_images._get_by_key_path
     71         return get_by_key_path(self.DICTDICT, keypath)
     72 
     73     def _check_path_valid(self, keypath):
     74         self.assertEqual(self._get_by_key_path(keypath), 'OK')
     75 
     76     def _check_path_invalid(self, keypath):
     77         self.assertIsNone(self._get_by_key_path(keypath))
     78 
     79     def test_one_element(self):
     80         """Test a single-element key path with a valid key."""
     81         self._check_path_valid(['level0'])
     82 
     83     def test_two_element(self):
     84         """Test a two-element key path with a valid key."""
     85         self._check_path_valid(['level1_a', 'level1_b'])
     86 
     87     def test_one_element_invalid(self):
     88         """Test a single-element key path with an invalid key."""
     89         self._check_path_invalid(['absent'])
     90 
     91     def test_two_element_invalid(self):
     92         """Test a two-element key path with an invalid key."""
     93         self._check_path_invalid(['level1_a', 'absent'])
     94 
     95 
     96 class GetFirmwareUpgradesTests(unittest.TestCase):
     97     """Tests for _get_firmware_upgrades."""
     98 
     99 
    100     def setUp(self):
    101         self.version_map = frontend._CrosVersionMap(mock.Mock())
    102 
    103 
    104     @mock.patch.object(assign_stable_images, 'get_firmware_versions')
    105     def test_get_firmware_upgrades(self, mock_get_firmware_versions):
    106         """Test _get_firmware_upgrades."""
    107         mock_get_firmware_versions.side_effect = [
    108                 {'auron_paine': 'fw_version'},
    109                 {'blue': 'fw_version',
    110                  'robo360': 'fw_version',
    111                  'porbeagle': 'fw_version'}
    112         ]
    113         cros_versions = {
    114                 'coral': 'R64-10176.65.0',
    115                 'auron_paine': 'R64-10176.65.0'
    116         }
    117         boards = ['auron_paine', 'coral']
    118 
    119         firmware_upgrades = assign_stable_images._get_firmware_upgrades(
    120             self.version_map, cros_versions)
    121         expected_firmware_upgrades = {
    122                 'auron_paine': 'fw_version',
    123                 'blue': 'fw_version',
    124                 'robo360': 'fw_version',
    125                 'porbeagle': 'fw_version'
    126         }
    127         self.assertEqual(firmware_upgrades, expected_firmware_upgrades)
    128 
    129 
    130 class GetFirmwareVersionsTests(unittest.TestCase):
    131     """Tests for get_firmware_versions."""
    132 
    133 
    134     def setUp(self):
    135         self.version_map = frontend._CrosVersionMap(mock.Mock())
    136         self.cros_version = 'R64-10176.65.0'
    137 
    138 
    139     @mock.patch.object(assign_stable_images, '_read_gs_json_data')
    140     def test_get_firmware_versions_on_normal_build(self, mock_read_gs):
    141         """Test get_firmware_versions on normal build."""
    142         metadata_json = """
    143 {
    144     "unibuild": false,
    145     "board-metadata":{
    146         "auron_paine":{
    147              "main-firmware-version":"Google_Auron_paine.6301.58.98"
    148         }
    149    }
    150 }
    151         """
    152         mock_read_gs.return_value = json.loads(metadata_json)
    153         board = 'auron_paine'
    154 
    155         fw_version = assign_stable_images.get_firmware_versions(
    156                 self.version_map, board, self.cros_version)
    157         expected_version = {board: "Google_Auron_paine.6301.58.98"}
    158         self.assertEqual(fw_version, expected_version)
    159 
    160 
    161     @mock.patch.object(assign_stable_images, '_read_gs_json_data',
    162                        side_effect = Exception('GS ERROR'))
    163     def test_get_firmware_versions_with_exceptions(self, mock_read_gs):
    164         """Test get_firmware_versions on normal build with exceptions."""
    165         afe_mock = mock.Mock()
    166         version_map = frontend._CrosVersionMap(afe_mock)
    167 
    168         fw_version = assign_stable_images.get_firmware_versions(
    169                 self.version_map, 'auron_paine', self.cros_version)
    170         self.assertEqual(fw_version, {'auron_paine': None})
    171 
    172 
    173     @mock.patch.object(assign_stable_images, '_read_gs_json_data')
    174     def test_get_firmware_versions_on_unibuild(self, mock_read_gs):
    175         """Test get_firmware_version on uni-build."""
    176         metadata_json = """
    177 {
    178     "unibuild": true,
    179     "board-metadata":{
    180         "coral":{
    181             "kernel-version":"4.4.114-r1354",
    182             "models":{
    183                 "blue":{
    184                     "main-readonly-firmware-version":"Google_Coral.10068.37.0",
    185                     "main-readwrite-firmware-version":"Google_Coral.10068.39.0"
    186                 },
    187                 "robo360":{
    188                     "main-readonly-firmware-version":"Google_Coral.10068.34.0",
    189                     "main-readwrite-firmware-version":null
    190                 },
    191                 "porbeagle":{
    192                     "main-readonly-firmware-version":null,
    193                     "main-readwrite-firmware-version":null
    194                 }
    195             }
    196         }
    197     }
    198 }
    199 """
    200         mock_read_gs.return_value = json.loads(metadata_json)
    201 
    202         fw_version = assign_stable_images.get_firmware_versions(
    203                 self.version_map, 'coral', self.cros_version)
    204         expected_version = {
    205                 'blue': 'Google_Coral.10068.39.0',
    206                 'robo360': 'Google_Coral.10068.34.0',
    207                 'porbeagle': None
    208         }
    209         self.assertEqual(fw_version, expected_version)
    210 
    211 
    212     @mock.patch.object(assign_stable_images, '_read_gs_json_data')
    213     def test_get_firmware_versions_on_unibuild_no_models(self, mock_read_gs):
    214         """Test get_firmware_versions on uni-build without models dict."""
    215         metadata_json = """
    216 {
    217     "unibuild": true,
    218     "board-metadata":{
    219         "coral":{
    220             "kernel-version":"4.4.114-r1354"
    221         }
    222     }
    223 }
    224 """
    225         mock_read_gs.return_value = json.loads(metadata_json)
    226 
    227         fw_version = assign_stable_images.get_firmware_versions(
    228                 self.version_map, 'coral', self.cros_version)
    229         self.assertEqual(fw_version, {'coral': None})
    230 
    231 
    232 class GetUpgradeTests(unittest.TestCase):
    233     """Tests for the `_get_upgrade_versions()` function."""
    234 
    235     # _VERSIONS - a list of sample version strings such as may be used
    236     #   for Chrome OS, sorted from oldest to newest.  These are used to
    237     #   construct test data in multiple test cases, below.
    238     _VERSIONS = ['R1-1.0.0', 'R1-1.1.0', 'R2-4.0.0']
    239 
    240     def test_board_conversions(self):
    241         """
    242         Test proper mapping of names from the AFE to Omaha.
    243 
    244         Board names in Omaha don't have '_' characters; when an AFE
    245         board contains '_' characters, they must be converted to '-'.
    246 
    247         Assert that for various forms of name in the AFE mapping, the
    248         converted name is the one looked up in the Omaha mapping.
    249         """
    250         board_equivalents = [
    251             ('a-b', 'a-b'), ('c_d', 'c-d'),
    252             ('e_f-g', 'e-f-g'), ('hi', 'hi')]
    253         afe_versions = {
    254             _DEFAULT_BOARD: self._VERSIONS[0]
    255         }
    256         omaha_versions = {}
    257         expected = {}
    258         boards = set()
    259         for afe_board, omaha_board in board_equivalents:
    260             boards.add(afe_board)
    261             afe_versions[afe_board] = self._VERSIONS[1]
    262             omaha_versions[omaha_board] = self._VERSIONS[2]
    263             expected[afe_board] = self._VERSIONS[2]
    264         upgrades, _ = assign_stable_images._get_upgrade_versions(
    265                 afe_versions, omaha_versions, boards)
    266         self.assertEqual(upgrades, expected)
    267 
    268     def test_afe_default(self):
    269         """
    270         Test that the AFE default board mapping is honored.
    271 
    272         If a board isn't present in the AFE dictionary, the mapping
    273         for `_DEFAULT_BOARD` should be used.
    274 
    275         Primary assertions:
    276           * When a board is present in the AFE mapping, its version
    277             mapping is used.
    278           * When a board is not present in the AFE mapping, the default
    279             version mapping is used.
    280 
    281         Secondarily, assert that when a mapping is absent from Omaha,
    282         the AFE mapping is left unchanged.
    283         """
    284         afe_versions = {
    285             _DEFAULT_BOARD: self._VERSIONS[0],
    286             'a': self._VERSIONS[1]
    287         }
    288         boards = set(['a', 'b'])
    289         expected = {
    290             'a': self._VERSIONS[1],
    291             'b': self._VERSIONS[0]
    292         }
    293         upgrades, _ = assign_stable_images._get_upgrade_versions(
    294                 afe_versions, {}, boards)
    295         self.assertEqual(upgrades, expected)
    296 
    297     def test_omaha_upgrade(self):
    298         """
    299         Test that upgrades from Omaha are detected.
    300 
    301         Primary assertion:
    302           * If a board is found in Omaha, and the version in Omaha is
    303             newer than the AFE version, the Omaha version is the one
    304             used.
    305 
    306         Secondarily, asserts that version comparisons between various
    307         specific version strings are all correct.
    308         """
    309         boards = set(['a'])
    310         for i in range(0, len(self._VERSIONS) - 1):
    311             afe_versions = {_DEFAULT_BOARD: self._VERSIONS[i]}
    312             for j in range(i+1, len(self._VERSIONS)):
    313                 omaha_versions = {b: self._VERSIONS[j] for b in boards}
    314                 upgrades, _ = assign_stable_images._get_upgrade_versions(
    315                         afe_versions, omaha_versions, boards)
    316                 self.assertEqual(upgrades, omaha_versions)
    317 
    318     def test_no_upgrade(self):
    319         """
    320         Test that if Omaha is behind the AFE, it is ignored.
    321 
    322         Primary assertion:
    323           * If a board is found in Omaha, and the version in Omaha is
    324             older than the AFE version, the AFE version is the one used.
    325 
    326         Secondarily, asserts that version comparisons between various
    327         specific version strings are all correct.
    328         """
    329         boards = set(['a'])
    330         for i in range(1, len(self._VERSIONS)):
    331             afe_versions = {_DEFAULT_BOARD: self._VERSIONS[i]}
    332             expected = {b: self._VERSIONS[i] for b in boards}
    333             for j in range(0, i):
    334                 omaha_versions = {b: self._VERSIONS[j] for b in boards}
    335                 upgrades, _ = assign_stable_images._get_upgrade_versions(
    336                         afe_versions, omaha_versions, boards)
    337                 self.assertEqual(upgrades, expected)
    338 
    339     def test_ignore_unused_boards(self):
    340         """
    341         Test that unlisted boards are ignored.
    342 
    343         Assert that boards present in the AFE or Omaha mappings aren't
    344         included in the return mappings when they aren't in the passed
    345         in set of boards.
    346         """
    347         unused_boards = set(['a', 'b'])
    348         used_boards = set(['c', 'd'])
    349         afe_versions = {b: self._VERSIONS[0] for b in unused_boards}
    350         afe_versions[_DEFAULT_BOARD] = self._VERSIONS[1]
    351         expected = {b: self._VERSIONS[1] for b in used_boards}
    352         omaha_versions = expected.copy()
    353         omaha_versions.update(
    354                 {b: self._VERSIONS[0] for b in unused_boards})
    355         upgrades, _ = assign_stable_images._get_upgrade_versions(
    356                 afe_versions, omaha_versions, used_boards)
    357         self.assertEqual(upgrades, expected)
    358 
    359     def test_default_unchanged(self):
    360         """
    361         Test correct handling when the default build is unchanged.
    362 
    363         Assert that if in Omaha, one board in a set of three upgrades
    364         from the AFE default, that the returned default board mapping is
    365         the original default in the AFE.
    366         """
    367         boards = set(['a', 'b', 'c'])
    368         afe_versions = {_DEFAULT_BOARD: self._VERSIONS[0]}
    369         omaha_versions = {b: self._VERSIONS[0] for b in boards}
    370         omaha_versions['c'] = self._VERSIONS[1]
    371         _, new_default = assign_stable_images._get_upgrade_versions(
    372                 afe_versions, omaha_versions, boards)
    373         self.assertEqual(new_default, self._VERSIONS[0])
    374 
    375     def test_default_upgrade(self):
    376         """
    377         Test correct handling when the default build must change.
    378 
    379         Assert that if in Omaha, two boards in a set of three upgrade
    380         from the AFE default, that the returned default board mapping is
    381         the new build in Omaha.
    382         """
    383         boards = set(['a', 'b', 'c'])
    384         afe_versions = {_DEFAULT_BOARD: self._VERSIONS[0]}
    385         omaha_versions = {b: self._VERSIONS[1] for b in boards}
    386         omaha_versions['c'] = self._VERSIONS[0]
    387         _, new_default = assign_stable_images._get_upgrade_versions(
    388                 afe_versions, omaha_versions, boards)
    389         self.assertEqual(new_default, self._VERSIONS[1])
    390 
    391 
    392 # Sample version string values to be used when testing
    393 # `_apply_upgrades()`.
    394 #
    395 # _OLD_DEFAULT - Test value representing the default version mapping
    396 #   in the `old_versions` dictionary in a call to `_apply_upgrades()`.
    397 # _NEW_DEFAULT - Test value representing the default version mapping
    398 #   in the `new_versions` dictionary when a version update is being
    399 #   tested.
    400 # _OLD_VERSION - Test value representing an arbitrary version for a
    401 #   board that is mapped in the `old_versions` dictionary in a call to
    402 #   `_apply_upgrades()`.
    403 # _NEW_VERSION - Test value representing an arbitrary version for a
    404 #   board that is mapped in the `new_versions` dictionary in a call to
    405 #   `_apply_upgrades()`.
    406 #
    407 _OLD_DEFAULT = 'old-default-version'
    408 _NEW_DEFAULT = 'new-default-version'
    409 _OLD_VERSION = 'old-board-version'
    410 _NEW_VERSION = 'new-board-version'
    411 
    412 
    413 class _StubAFE(object):
    414     """Stubbed out version of `server.frontend.AFE`."""
    415 
    416     CROS_IMAGE_TYPE = 'cros-image-type'
    417     FIRMWARE_IMAGE_TYPE = 'firmware-image-type'
    418 
    419     def get_stable_version_map(self, image_type):
    420         return image_type
    421 
    422 
    423 class _TestUpdater(assign_stable_images._VersionUpdater):
    424     """
    425     Subclass of `_VersionUpdater` for testing.
    426 
    427     This class extends `_VersionUpdater` to provide support for testing
    428     various assertions about the behavior of the base class and its
    429     interactions with `_apply_cros_upgrades()` and
    430     `_apply_firmware_upgrades()`.
    431 
    432     The class tests assertions along the following lines:
    433       * When applied to the original mappings, the calls to
    434         `_do_set_mapping()` and `_do_delete_mapping()` create the
    435         expected final mapping state.
    436       * Calls to report state changes are made with the expected
    437         values.
    438       * There's a one-to-one match between reported and actually
    439         executed changes.
    440 
    441     """
    442 
    443     def __init__(self, testcase):
    444         super(_TestUpdater, self).__init__(_StubAFE())
    445         self._testcase = testcase
    446         self._default_changed = None
    447         self._reported_mappings = None
    448         self._updated_mappings = None
    449         self._reported_deletions = None
    450         self._actual_deletions = None
    451         self._original_mappings = None
    452         self._mappings = None
    453         self._expected_mappings = None
    454         self._unchanged_boards = None
    455 
    456     def pretest_init(self, initial_versions, expected_versions):
    457         """
    458         Initialize for testing.
    459 
    460         @param initial_versions   Mappings to be used as the starting
    461                                   point for testing.
    462         @param expected_versions  The expected final value of the
    463                                   mappings after the test.
    464         """
    465         self._default_changed = False
    466         self._reported_mappings = {}
    467         self._updated_mappings = {}
    468         self._reported_deletions = set()
    469         self._actual_deletions = set()
    470         self._original_mappings = initial_versions.copy()
    471         self._mappings = initial_versions.copy()
    472         self._expected_mappings = expected_versions
    473         self._unchanged_boards = set()
    474 
    475     def check_results(self, change_default):
    476         """
    477         Assert that observed changes match expectations.
    478 
    479         Asserts the following:
    480           * The `report_default_changed()` method was called (or not)
    481             based on whether `change_default` is true (or not).
    482           * The changes reported via `_report_board_changed()` match
    483             the changes actually applied.
    484           * The final mappings after applying requested changes match
    485             the actually expected mappings.
    486 
    487         @param old_versions   Parameter to be passed to
    488                               `_apply_cros_upgrades()`.
    489         @param new_versions   Parameter to be passed to
    490                               `_apply_cros_upgrades()`.
    491         @param change_default   Whether the test should include a change
    492                                 to the default version mapping.
    493         """
    494         self._testcase.assertEqual(change_default,
    495                                    self._default_changed)
    496         self._testcase.assertEqual(self._reported_mappings,
    497                                    self._updated_mappings)
    498         self._testcase.assertEqual(self._reported_deletions,
    499                                    self._actual_deletions)
    500         self._testcase.assertEqual(self._mappings,
    501                                    self._expected_mappings)
    502 
    503     def report(self, message):
    504         """Report message."""
    505         pass
    506 
    507     def report_default_changed(self, old_default, new_default):
    508         """
    509         Override of our parent class' method for test purposes.
    510 
    511         Saves a record of the report for testing the final result in
    512         `apply_upgrades()`, above.
    513 
    514         Assert the following:
    515           * The old and new default values match the values that
    516             were passed in the original call's arguments.
    517           * This function is not being called for a second time.
    518 
    519         @param old_default  The original default version.
    520         @param new_default  The new default version to be applied.
    521         """
    522         self._testcase.assertNotEqual(old_default, new_default)
    523         self._testcase.assertEqual(old_default,
    524                                    self._original_mappings[_DEFAULT_BOARD])
    525         self._testcase.assertEqual(new_default,
    526                                    self._expected_mappings[_DEFAULT_BOARD])
    527         self._testcase.assertFalse(self._default_changed)
    528         self._default_changed = True
    529         self._reported_mappings[_DEFAULT_BOARD] = new_default
    530 
    531     def _report_board_changed(self, board, old_version, new_version):
    532         """
    533         Override of our parent class' method for test purposes.
    534 
    535         Saves a record of the report for testing the final result in
    536         `apply_upgrades()`, above.
    537 
    538         Assert the following:
    539           * The change being reported actually reports two different
    540             versions.
    541           * If the board isn't mapped to the default version, then the
    542             reported old version is the actually mapped old version.
    543           * If the board isn't changing to the default version, then the
    544             reported new version is the expected new version.
    545           * This is not a second report for this board.
    546 
    547         The implementation implicitly requires that the specified board
    548         have a valid mapping.
    549 
    550         @param board        The board with the changing version.
    551         @param old_version  The original version mapped to the board.
    552         @param new_version  The new version to be applied to the board.
    553         """
    554         self._testcase.assertNotEqual(old_version, new_version)
    555         if board in self._original_mappings:
    556             self._testcase.assertEqual(old_version,
    557                                        self._original_mappings[board])
    558         if board in self._expected_mappings:
    559             self._testcase.assertEqual(new_version,
    560                                        self._expected_mappings[board])
    561             self._testcase.assertNotIn(board, self._reported_mappings)
    562             self._reported_mappings[board] = new_version
    563         else:
    564             self._testcase.assertNotIn(board, self._reported_deletions)
    565             self._reported_deletions.add(board)
    566 
    567     def report_board_unchanged(self, board, old_version):
    568         """
    569         Override of our parent class' method for test purposes.
    570 
    571         Assert the following:
    572           * The version being reported as unchanged is actually mapped.
    573           * The reported old version matches the expected value.
    574           * This is not a second report for this board.
    575 
    576         @param board        The board that is not changing.
    577         @param old_version  The board's version mapping.
    578         """
    579         self._testcase.assertIn(board, self._original_mappings)
    580         self._testcase.assertEqual(old_version,
    581                                    self._original_mappings[board])
    582         self._testcase.assertNotIn(board, self._unchanged_boards)
    583         self._unchanged_boards.add(board)
    584 
    585     def _do_set_mapping(self, board, new_version):
    586         """
    587         Override of our parent class' method for test purposes.
    588 
    589         Saves a record of the change for testing the final result in
    590         `apply_upgrades()`, above.
    591 
    592         Assert the following:
    593           * This is not a second change for this board.
    594           * If we're changing the default mapping, then every board
    595             that will be changing to a non-default mapping has been
    596             updated.
    597 
    598         @param board        The board with the changing version.
    599         @param new_version  The new version to be applied to the board.
    600         """
    601         self._mappings[board] = new_version
    602         self._testcase.assertNotIn(board, self._updated_mappings)
    603         self._updated_mappings[board] = new_version
    604         if board == _DEFAULT_BOARD:
    605             for board in self._expected_mappings:
    606                 self._testcase.assertIn(board, self._mappings)
    607 
    608     def _do_delete_mapping(self, board):
    609         """
    610         Override of our parent class' method for test purposes.
    611 
    612         Saves a record of the change for testing the final result in
    613         `apply_upgrades()`, above.
    614 
    615         Assert that the board has a mapping prior to deletion.
    616 
    617         @param board        The board with the version to be deleted.
    618         """
    619         self._testcase.assertNotEqual(board, _DEFAULT_BOARD)
    620         self._testcase.assertIn(board, self._mappings)
    621         del self._mappings[board]
    622         self._actual_deletions.add(board)
    623 
    624 
    625 class ApplyCrOSUpgradesTests(unittest.TestCase):
    626     """Tests for the `_apply_cros_upgrades()` function."""
    627 
    628     def _apply_upgrades(self, old_versions, new_versions, change_default):
    629         """
    630         Test a single call to `_apply_cros_upgrades()`.
    631 
    632         All assertions are handled by an instance of `_TestUpdater`.
    633 
    634         @param old_versions   Parameter to be passed to
    635                               `_apply_cros_upgrades()`.
    636         @param new_versions   Parameter to be passed to
    637                               `_apply_cros_upgrades()`.
    638         @param change_default   Whether the test should include a change
    639                                 to the default version mapping.
    640         """
    641         old_versions[_DEFAULT_BOARD] = _OLD_DEFAULT
    642         if change_default:
    643             new_default = _NEW_DEFAULT
    644         else:
    645             new_default = _OLD_DEFAULT
    646         expected_versions = {
    647             b: v for b, v in new_versions.items() if v != new_default
    648         }
    649         expected_versions[_DEFAULT_BOARD] = new_default
    650         updater = _TestUpdater(self)
    651         updater.pretest_init(old_versions, expected_versions)
    652         assign_stable_images._apply_cros_upgrades(
    653             updater, old_versions, new_versions, new_default)
    654         updater.check_results(change_default)
    655 
    656     def test_no_changes(self):
    657         """
    658         Test an empty upgrade that does nothing.
    659 
    660         Test the boundary case of an upgrade where there are no boards,
    661         and the default does not change.
    662         """
    663         self._apply_upgrades({}, {}, False)
    664 
    665     def test_change_default(self):
    666         """
    667         Test an empty upgrade that merely changes the default.
    668 
    669         Test the boundary case of an upgrade where there are no boards,
    670         but the default is upgraded.
    671         """
    672         self._apply_upgrades({}, {}, True)
    673 
    674     def test_board_default_no_changes(self):
    675         """
    676         Test that a board at default stays with an unchanged default.
    677 
    678         Test the case of a board that is mapped to the default, where
    679         neither the board nor the default change.
    680         """
    681         self._apply_upgrades({}, {'board': _OLD_DEFAULT}, False)
    682 
    683     def test_board_left_behind(self):
    684         """
    685         Test a board left at the old default after a default upgrade.
    686 
    687         Test the case of a board that stays mapped to the old default as
    688         the default board is upgraded.
    689         """
    690         self._apply_upgrades({}, {'board': _OLD_DEFAULT}, True)
    691 
    692     def test_board_upgrade_from_default(self):
    693         """
    694         Test upgrading a board from a default that doesn't change.
    695 
    696         Test the case of upgrading a board from default to non-default,
    697         where the default doesn't change.
    698         """
    699         self._apply_upgrades({}, {'board': _NEW_VERSION}, False)
    700 
    701     def test_board_and_default_diverge(self):
    702         """
    703         Test upgrading a board that diverges from the default.
    704 
    705         Test the case of upgrading a board and default together from the
    706         same to different versions.
    707         """
    708         self._apply_upgrades({}, {'board': _NEW_VERSION}, True)
    709 
    710     def test_board_tracks_default(self):
    711         """
    712         Test upgrading a board to track a default upgrade.
    713 
    714         Test the case of upgrading a board and the default together.
    715         """
    716         self._apply_upgrades({}, {'board': _NEW_DEFAULT}, True)
    717 
    718     def test_board_non_default_no_changes(self):
    719         """
    720         Test an upgrade with no changes to a board or the default.
    721 
    722         Test the case of an upgrade with a board in it, where neither
    723         the board nor the default change.
    724         """
    725         self._apply_upgrades({'board': _NEW_VERSION},
    726                              {'board': _NEW_VERSION},
    727                              False)
    728 
    729     def test_board_upgrade_and_keep_default(self):
    730         """
    731         Test a board upgrade with an unchanged default.
    732 
    733         Test the case of upgrading a board while the default stays the
    734         same.
    735         """
    736         self._apply_upgrades({'board': _OLD_VERSION},
    737                              {'board': _NEW_VERSION},
    738                              False)
    739 
    740     def test_board_upgrade_and_change_default(self):
    741         """
    742         Test upgrading a board and the default separately.
    743 
    744         Test the case of upgrading both a board and the default, each
    745         from and to different versions.
    746         """
    747         self._apply_upgrades({'board': _OLD_VERSION},
    748                              {'board': _NEW_VERSION},
    749                              True)
    750 
    751     def test_board_leads_default(self):
    752         """
    753         Test a board that upgrades ahead of the new default.
    754 
    755         Test the case of upgrading both a board and the default, where
    756         the board's old version is the new default version.
    757         """
    758         self._apply_upgrades({'board': _NEW_DEFAULT},
    759                              {'board': _NEW_VERSION},
    760                              True)
    761 
    762     def test_board_lags_to_old_default(self):
    763         """
    764         Test a board that upgrades behind the old default.
    765 
    766         Test the case of upgrading both a board and the default, where
    767         the board's new version is the old default version.
    768         """
    769         self._apply_upgrades({'board': _OLD_VERSION},
    770                              {'board': _OLD_DEFAULT},
    771                              True)
    772 
    773     def test_board_joins_old_default(self):
    774         """
    775         Test upgrading a board to a default that doesn't change.
    776 
    777         Test the case of upgrading board to the default, where the
    778         default mapping stays unchanged.
    779         """
    780         self._apply_upgrades({'board': _OLD_VERSION},
    781                              {'board': _OLD_DEFAULT},
    782                              False)
    783 
    784     def test_board_joins_new_default(self):
    785         """
    786         Test upgrading a board to match the new default.
    787 
    788         Test the case of upgrading board and the default to the same
    789         version.
    790         """
    791         self._apply_upgrades({'board': _OLD_VERSION},
    792                              {'board': _NEW_DEFAULT},
    793                              True)
    794 
    795     def test_board_becomes_default(self):
    796         """
    797         Test a board that becomes default after a default upgrade.
    798 
    799         Test the case of upgrading the default to a version already
    800         mapped for an existing board.
    801         """
    802         self._apply_upgrades({'board': _NEW_DEFAULT},
    803                              {'board': _NEW_DEFAULT},
    804                              True)
    805 
    806 
    807 class ApplyFirmwareUpgradesTests(unittest.TestCase):
    808     """Tests for the `_apply_firmware_upgrades()` function."""
    809 
    810     def _apply_upgrades(self, old_versions, new_versions):
    811         """
    812         Test a single call to `_apply_firmware_upgrades()`.
    813 
    814         All assertions are handled by an instance of `_TestUpdater`.
    815 
    816         @param old_versions   Parameter to be passed to
    817                               `_apply_firmware_upgrades()`.
    818         @param new_versions   Parameter to be passed to
    819                               `_apply_firmware_upgrades()`.
    820         """
    821         updater = _TestUpdater(self)
    822         updater.pretest_init(old_versions, new_versions)
    823         assign_stable_images._apply_firmware_upgrades(
    824             updater, old_versions, new_versions)
    825         updater.check_results(False)
    826 
    827     def test_no_changes(self):
    828         """
    829         Test an empty upgrade that does nothing.
    830 
    831         Test the boundary case of an upgrade where there are no boards.
    832         """
    833         self._apply_upgrades({}, {})
    834 
    835     def test_board_added(self):
    836         """
    837         Test an upgrade that adds a new board.
    838 
    839         Test the case of an upgrade where a board that was previously
    840         unmapped is added.
    841         """
    842         self._apply_upgrades({}, {'board': _NEW_VERSION})
    843 
    844     def test_board_unchanged(self):
    845         """
    846         Test an upgrade with no changes to a board.
    847 
    848         Test the case of an upgrade with a board that stays the same.
    849         """
    850         self._apply_upgrades({'board': _NEW_VERSION},
    851                              {'board': _NEW_VERSION})
    852 
    853     def test_board_upgrade_and_change_default(self):
    854         """
    855         Test upgrading a board.
    856 
    857         Test the case of upgrading a board to a new version.
    858         """
    859         self._apply_upgrades({'board': _OLD_VERSION},
    860                              {'board': _NEW_VERSION})
    861 
    862 
    863 if __name__ == '__main__':
    864     unittest.main()
    865