| # Copyright 2016 The Chromium OS Authors. All rights reserved. |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| """ |
| Unit tests for functions in `assign_stable_images`. |
| """ |
| |
| |
| import mock |
| import unittest |
| |
| import common |
| from autotest_lib.site_utils.stable_images import assign_stable_images |
| from autotest_lib.site_utils.stable_images import build_data |
| |
| |
| _DEFAULT_BOARD = assign_stable_images._DEFAULT_BOARD |
| |
| |
| class GetFirmwareUpgradesTests(unittest.TestCase): |
| """Tests for _get_firmware_upgrades.""" |
| |
| @mock.patch.object(build_data, 'get_firmware_versions') |
| def test_get_firmware_upgrades(self, mock_get_firmware_versions): |
| """Test _get_firmware_upgrades.""" |
| mock_get_firmware_versions.side_effect = [ |
| {'auron_paine': 'fw_version'}, |
| {'blue': 'fw_version', |
| 'robo360': 'fw_version', |
| 'porbeagle': 'fw_version'} |
| ] |
| cros_versions = { |
| 'coral': 'R64-10176.65.0', |
| 'auron_paine': 'R64-10176.65.0' |
| } |
| boards = ['auron_paine', 'coral'] |
| |
| firmware_upgrades = assign_stable_images._get_firmware_upgrades( |
| cros_versions) |
| expected_firmware_upgrades = { |
| 'auron_paine': 'fw_version', |
| 'blue': 'fw_version', |
| 'robo360': 'fw_version', |
| 'porbeagle': 'fw_version' |
| } |
| self.assertEqual(firmware_upgrades, expected_firmware_upgrades) |
| |
| |
| class GetUpgradeTests(unittest.TestCase): |
| """Tests for the `_get_upgrade_versions()` function.""" |
| |
| # _VERSIONS - a list of sample version strings such as may be used |
| # for Chrome OS, sorted from oldest to newest. These are used to |
| # construct test data in multiple test cases, below. |
| _VERSIONS = ['R1-1.0.0', 'R1-1.1.0', 'R2-4.0.0'] |
| |
| def test_board_conversions(self): |
| """ |
| Test proper mapping of names from the AFE to Omaha. |
| |
| Board names in Omaha don't have '_' characters; when an AFE |
| board contains '_' characters, they must be converted to '-'. |
| |
| Assert that for various forms of name in the AFE mapping, the |
| converted name is the one looked up in the Omaha mapping. |
| """ |
| board_equivalents = [ |
| ('a-b', 'a-b'), ('c_d', 'c-d'), |
| ('e_f-g', 'e-f-g'), ('hi', 'hi')] |
| afe_versions = { |
| _DEFAULT_BOARD: self._VERSIONS[0] |
| } |
| omaha_versions = {} |
| expected = {} |
| boards = set() |
| for afe_board, omaha_board in board_equivalents: |
| boards.add(afe_board) |
| afe_versions[afe_board] = self._VERSIONS[1] |
| omaha_versions[omaha_board] = self._VERSIONS[2] |
| expected[afe_board] = self._VERSIONS[2] |
| upgrades, _ = assign_stable_images._get_upgrade_versions( |
| afe_versions, omaha_versions, boards) |
| self.assertEqual(upgrades, expected) |
| |
| def test_afe_default(self): |
| """ |
| Test that the AFE default board mapping is honored. |
| |
| If a board isn't present in the AFE dictionary, the mapping |
| for `_DEFAULT_BOARD` should be used. |
| |
| Primary assertions: |
| * When a board is present in the AFE mapping, its version |
| mapping is used. |
| * When a board is not present in the AFE mapping, the default |
| version mapping is used. |
| |
| Secondarily, assert that when a mapping is absent from Omaha, |
| the AFE mapping is left unchanged. |
| """ |
| afe_versions = { |
| _DEFAULT_BOARD: self._VERSIONS[0], |
| 'a': self._VERSIONS[1] |
| } |
| boards = set(['a', 'b']) |
| expected = { |
| 'a': self._VERSIONS[1], |
| 'b': self._VERSIONS[0] |
| } |
| upgrades, _ = assign_stable_images._get_upgrade_versions( |
| afe_versions, {}, boards) |
| self.assertEqual(upgrades, expected) |
| |
| def test_omaha_upgrade(self): |
| """ |
| Test that upgrades from Omaha are detected. |
| |
| Primary assertion: |
| * If a board is found in Omaha, and the version in Omaha is |
| newer than the AFE version, the Omaha version is the one |
| used. |
| |
| Secondarily, asserts that version comparisons between various |
| specific version strings are all correct. |
| """ |
| boards = set(['a']) |
| for i in range(0, len(self._VERSIONS) - 1): |
| afe_versions = {_DEFAULT_BOARD: self._VERSIONS[i]} |
| for j in range(i+1, len(self._VERSIONS)): |
| omaha_versions = {b: self._VERSIONS[j] for b in boards} |
| upgrades, _ = assign_stable_images._get_upgrade_versions( |
| afe_versions, omaha_versions, boards) |
| self.assertEqual(upgrades, omaha_versions) |
| |
| def test_no_upgrade(self): |
| """ |
| Test that if Omaha is behind the AFE, it is ignored. |
| |
| Primary assertion: |
| * If a board is found in Omaha, and the version in Omaha is |
| older than the AFE version, the AFE version is the one used. |
| |
| Secondarily, asserts that version comparisons between various |
| specific version strings are all correct. |
| """ |
| boards = set(['a']) |
| for i in range(1, len(self._VERSIONS)): |
| afe_versions = {_DEFAULT_BOARD: self._VERSIONS[i]} |
| expected = {b: self._VERSIONS[i] for b in boards} |
| for j in range(0, i): |
| omaha_versions = {b: self._VERSIONS[j] for b in boards} |
| upgrades, _ = assign_stable_images._get_upgrade_versions( |
| afe_versions, omaha_versions, boards) |
| self.assertEqual(upgrades, expected) |
| |
| def test_ignore_unused_boards(self): |
| """ |
| Test that unlisted boards are ignored. |
| |
| Assert that boards present in the AFE or Omaha mappings aren't |
| included in the return mappings when they aren't in the passed |
| in set of boards. |
| """ |
| unused_boards = set(['a', 'b']) |
| used_boards = set(['c', 'd']) |
| afe_versions = {b: self._VERSIONS[0] for b in unused_boards} |
| afe_versions[_DEFAULT_BOARD] = self._VERSIONS[1] |
| expected = {b: self._VERSIONS[1] for b in used_boards} |
| omaha_versions = expected.copy() |
| omaha_versions.update( |
| {b: self._VERSIONS[0] for b in unused_boards}) |
| upgrades, _ = assign_stable_images._get_upgrade_versions( |
| afe_versions, omaha_versions, used_boards) |
| self.assertEqual(upgrades, expected) |
| |
| def test_default_unchanged(self): |
| """ |
| Test correct handling when the default build is unchanged. |
| |
| Assert that if in Omaha, one board in a set of three upgrades |
| from the AFE default, that the returned default board mapping is |
| the original default in the AFE. |
| """ |
| boards = set(['a', 'b', 'c']) |
| afe_versions = {_DEFAULT_BOARD: self._VERSIONS[0]} |
| omaha_versions = {b: self._VERSIONS[0] for b in boards} |
| omaha_versions['c'] = self._VERSIONS[1] |
| _, new_default = assign_stable_images._get_upgrade_versions( |
| afe_versions, omaha_versions, boards) |
| self.assertEqual(new_default, self._VERSIONS[0]) |
| |
| def test_default_upgrade(self): |
| """ |
| Test correct handling when the default build must change. |
| |
| Assert that if in Omaha, two boards in a set of three upgrade |
| from the AFE default, that the returned default board mapping is |
| the new build in Omaha. |
| """ |
| boards = set(['a', 'b', 'c']) |
| afe_versions = {_DEFAULT_BOARD: self._VERSIONS[0]} |
| omaha_versions = {b: self._VERSIONS[1] for b in boards} |
| omaha_versions['c'] = self._VERSIONS[0] |
| _, new_default = assign_stable_images._get_upgrade_versions( |
| afe_versions, omaha_versions, boards) |
| self.assertEqual(new_default, self._VERSIONS[1]) |
| |
| |
| # Sample version string values to be used when testing |
| # `_apply_upgrades()`. |
| # |
| # _OLD_DEFAULT - Test value representing the default version mapping |
| # in the `old_versions` dictionary in a call to `_apply_upgrades()`. |
| # _NEW_DEFAULT - Test value representing the default version mapping |
| # in the `new_versions` dictionary when a version update is being |
| # tested. |
| # _OLD_VERSION - Test value representing an arbitrary version for a |
| # board that is mapped in the `old_versions` dictionary in a call to |
| # `_apply_upgrades()`. |
| # _NEW_VERSION - Test value representing an arbitrary version for a |
| # board that is mapped in the `new_versions` dictionary in a call to |
| # `_apply_upgrades()`. |
| # |
| _OLD_DEFAULT = 'old-default-version' |
| _NEW_DEFAULT = 'new-default-version' |
| _OLD_VERSION = 'old-board-version' |
| _NEW_VERSION = 'new-board-version' |
| |
| |
| class _StubAFE(object): |
| """Stubbed out version of `server.frontend.AFE`.""" |
| |
| CROS_IMAGE_TYPE = 'cros-image-type' |
| FIRMWARE_IMAGE_TYPE = 'firmware-image-type' |
| |
| def get_stable_version_map(self, image_type): |
| return image_type |
| |
| |
| class _TestUpdater(assign_stable_images._VersionUpdater): |
| """ |
| Subclass of `_VersionUpdater` for testing. |
| |
| This class extends `_VersionUpdater` to provide support for testing |
| various assertions about the behavior of the base class and its |
| interactions with `_apply_cros_upgrades()` and |
| `_apply_firmware_upgrades()`. |
| |
| The class tests assertions along the following lines: |
| * When applied to the original mappings, the calls to |
| `_do_set_mapping()` and `_do_delete_mapping()` create the |
| expected final mapping state. |
| * Calls to report state changes are made with the expected |
| values. |
| * There's a one-to-one match between reported and actually |
| executed changes. |
| |
| """ |
| |
| def __init__(self, testcase): |
| super(_TestUpdater, self).__init__(_StubAFE()) |
| self._testcase = testcase |
| self._default_changed = None |
| self._reported_mappings = None |
| self._updated_mappings = None |
| self._reported_deletions = None |
| self._actual_deletions = None |
| self._original_mappings = None |
| self._mappings = None |
| self._expected_mappings = None |
| self._unchanged_boards = None |
| |
| def pretest_init(self, initial_versions, expected_versions): |
| """ |
| Initialize for testing. |
| |
| @param initial_versions Mappings to be used as the starting |
| point for testing. |
| @param expected_versions The expected final value of the |
| mappings after the test. |
| """ |
| self._default_changed = False |
| self._reported_mappings = {} |
| self._updated_mappings = {} |
| self._reported_deletions = set() |
| self._actual_deletions = set() |
| self._original_mappings = initial_versions.copy() |
| self._mappings = initial_versions.copy() |
| self._expected_mappings = expected_versions |
| self._unchanged_boards = set() |
| |
| def check_results(self, change_default): |
| """ |
| Assert that observed changes match expectations. |
| |
| Asserts the following: |
| * The `report_default_changed()` method was called (or not) |
| based on whether `change_default` is true (or not). |
| * The changes reported via `_report_board_changed()` match |
| the changes actually applied. |
| * The final mappings after applying requested changes match |
| the actually expected mappings. |
| |
| @param old_versions Parameter to be passed to |
| `_apply_cros_upgrades()`. |
| @param new_versions Parameter to be passed to |
| `_apply_cros_upgrades()`. |
| @param change_default Whether the test should include a change |
| to the default version mapping. |
| """ |
| self._testcase.assertEqual(change_default, |
| self._default_changed) |
| self._testcase.assertEqual(self._reported_mappings, |
| self._updated_mappings) |
| self._testcase.assertEqual(self._reported_deletions, |
| self._actual_deletions) |
| self._testcase.assertEqual(self._mappings, |
| self._expected_mappings) |
| |
| def report(self, message): |
| """Report message.""" |
| pass |
| |
| def report_default_changed(self, old_default, new_default): |
| """ |
| Override of our parent class' method for test purposes. |
| |
| Saves a record of the report for testing the final result in |
| `apply_upgrades()`, above. |
| |
| Assert the following: |
| * The old and new default values match the values that |
| were passed in the original call's arguments. |
| * This function is not being called for a second time. |
| |
| @param old_default The original default version. |
| @param new_default The new default version to be applied. |
| """ |
| self._testcase.assertNotEqual(old_default, new_default) |
| self._testcase.assertEqual(old_default, |
| self._original_mappings[_DEFAULT_BOARD]) |
| self._testcase.assertEqual(new_default, |
| self._expected_mappings[_DEFAULT_BOARD]) |
| self._testcase.assertFalse(self._default_changed) |
| self._default_changed = True |
| self._reported_mappings[_DEFAULT_BOARD] = new_default |
| |
| def _report_board_changed(self, board, old_version, new_version): |
| """ |
| Override of our parent class' method for test purposes. |
| |
| Saves a record of the report for testing the final result in |
| `apply_upgrades()`, above. |
| |
| Assert the following: |
| * The change being reported actually reports two different |
| versions. |
| * If the board isn't mapped to the default version, then the |
| reported old version is the actually mapped old version. |
| * If the board isn't changing to the default version, then the |
| reported new version is the expected new version. |
| * This is not a second report for this board. |
| |
| The implementation implicitly requires that the specified board |
| have a valid mapping. |
| |
| @param board The board with the changing version. |
| @param old_version The original version mapped to the board. |
| @param new_version The new version to be applied to the board. |
| """ |
| self._testcase.assertNotEqual(old_version, new_version) |
| if board in self._original_mappings: |
| self._testcase.assertEqual(old_version, |
| self._original_mappings[board]) |
| if board in self._expected_mappings: |
| self._testcase.assertEqual(new_version, |
| self._expected_mappings[board]) |
| self._testcase.assertNotIn(board, self._reported_mappings) |
| self._reported_mappings[board] = new_version |
| else: |
| self._testcase.assertNotIn(board, self._reported_deletions) |
| self._reported_deletions.add(board) |
| |
| def report_board_unchanged(self, board, old_version): |
| """ |
| Override of our parent class' method for test purposes. |
| |
| Assert the following: |
| * The version being reported as unchanged is actually mapped. |
| * The reported old version matches the expected value. |
| * This is not a second report for this board. |
| |
| @param board The board that is not changing. |
| @param old_version The board's version mapping. |
| """ |
| self._testcase.assertIn(board, self._original_mappings) |
| self._testcase.assertEqual(old_version, |
| self._original_mappings[board]) |
| self._testcase.assertNotIn(board, self._unchanged_boards) |
| self._unchanged_boards.add(board) |
| |
| def _do_set_mapping(self, board, new_version): |
| """ |
| Override of our parent class' method for test purposes. |
| |
| Saves a record of the change for testing the final result in |
| `apply_upgrades()`, above. |
| |
| Assert the following: |
| * This is not a second change for this board. |
| * If we're changing the default mapping, then every board |
| that will be changing to a non-default mapping has been |
| updated. |
| |
| @param board The board with the changing version. |
| @param new_version The new version to be applied to the board. |
| """ |
| self._mappings[board] = new_version |
| self._testcase.assertNotIn(board, self._updated_mappings) |
| self._updated_mappings[board] = new_version |
| if board == _DEFAULT_BOARD: |
| for board in self._expected_mappings: |
| self._testcase.assertIn(board, self._mappings) |
| |
| def _do_delete_mapping(self, board): |
| """ |
| Override of our parent class' method for test purposes. |
| |
| Saves a record of the change for testing the final result in |
| `apply_upgrades()`, above. |
| |
| Assert that the board has a mapping prior to deletion. |
| |
| @param board The board with the version to be deleted. |
| """ |
| self._testcase.assertNotEqual(board, _DEFAULT_BOARD) |
| self._testcase.assertIn(board, self._mappings) |
| del self._mappings[board] |
| self._actual_deletions.add(board) |
| |
| |
| class ApplyCrOSUpgradesTests(unittest.TestCase): |
| """Tests for the `_apply_cros_upgrades()` function.""" |
| |
| def _apply_upgrades(self, old_versions, new_versions, change_default): |
| """ |
| Test a single call to `_apply_cros_upgrades()`. |
| |
| All assertions are handled by an instance of `_TestUpdater`. |
| |
| @param old_versions Parameter to be passed to |
| `_apply_cros_upgrades()`. |
| @param new_versions Parameter to be passed to |
| `_apply_cros_upgrades()`. |
| @param change_default Whether the test should include a change |
| to the default version mapping. |
| """ |
| old_versions[_DEFAULT_BOARD] = _OLD_DEFAULT |
| if change_default: |
| new_default = _NEW_DEFAULT |
| else: |
| new_default = _OLD_DEFAULT |
| expected_versions = { |
| b: v for b, v in new_versions.items() if v != new_default |
| } |
| expected_versions[_DEFAULT_BOARD] = new_default |
| updater = _TestUpdater(self) |
| updater.pretest_init(old_versions, expected_versions) |
| assign_stable_images._apply_cros_upgrades( |
| updater, old_versions, new_versions, new_default) |
| updater.check_results(change_default) |
| |
| def test_no_changes(self): |
| """ |
| Test an empty upgrade that does nothing. |
| |
| Test the boundary case of an upgrade where there are no boards, |
| and the default does not change. |
| """ |
| self._apply_upgrades({}, {}, False) |
| |
| def test_change_default(self): |
| """ |
| Test an empty upgrade that merely changes the default. |
| |
| Test the boundary case of an upgrade where there are no boards, |
| but the default is upgraded. |
| """ |
| self._apply_upgrades({}, {}, True) |
| |
| def test_board_default_no_changes(self): |
| """ |
| Test that a board at default stays with an unchanged default. |
| |
| Test the case of a board that is mapped to the default, where |
| neither the board nor the default change. |
| """ |
| self._apply_upgrades({}, {'board': _OLD_DEFAULT}, False) |
| |
| def test_board_left_behind(self): |
| """ |
| Test a board left at the old default after a default upgrade. |
| |
| Test the case of a board that stays mapped to the old default as |
| the default board is upgraded. |
| """ |
| self._apply_upgrades({}, {'board': _OLD_DEFAULT}, True) |
| |
| def test_board_upgrade_from_default(self): |
| """ |
| Test upgrading a board from a default that doesn't change. |
| |
| Test the case of upgrading a board from default to non-default, |
| where the default doesn't change. |
| """ |
| self._apply_upgrades({}, {'board': _NEW_VERSION}, False) |
| |
| def test_board_and_default_diverge(self): |
| """ |
| Test upgrading a board that diverges from the default. |
| |
| Test the case of upgrading a board and default together from the |
| same to different versions. |
| """ |
| self._apply_upgrades({}, {'board': _NEW_VERSION}, True) |
| |
| def test_board_tracks_default(self): |
| """ |
| Test upgrading a board to track a default upgrade. |
| |
| Test the case of upgrading a board and the default together. |
| """ |
| self._apply_upgrades({}, {'board': _NEW_DEFAULT}, True) |
| |
| def test_board_non_default_no_changes(self): |
| """ |
| Test an upgrade with no changes to a board or the default. |
| |
| Test the case of an upgrade with a board in it, where neither |
| the board nor the default change. |
| """ |
| self._apply_upgrades({'board': _NEW_VERSION}, |
| {'board': _NEW_VERSION}, |
| False) |
| |
| def test_board_upgrade_and_keep_default(self): |
| """ |
| Test a board upgrade with an unchanged default. |
| |
| Test the case of upgrading a board while the default stays the |
| same. |
| """ |
| self._apply_upgrades({'board': _OLD_VERSION}, |
| {'board': _NEW_VERSION}, |
| False) |
| |
| def test_board_upgrade_and_change_default(self): |
| """ |
| Test upgrading a board and the default separately. |
| |
| Test the case of upgrading both a board and the default, each |
| from and to different versions. |
| """ |
| self._apply_upgrades({'board': _OLD_VERSION}, |
| {'board': _NEW_VERSION}, |
| True) |
| |
| def test_board_leads_default(self): |
| """ |
| Test a board that upgrades ahead of the new default. |
| |
| Test the case of upgrading both a board and the default, where |
| the board's old version is the new default version. |
| """ |
| self._apply_upgrades({'board': _NEW_DEFAULT}, |
| {'board': _NEW_VERSION}, |
| True) |
| |
| def test_board_lags_to_old_default(self): |
| """ |
| Test a board that upgrades behind the old default. |
| |
| Test the case of upgrading both a board and the default, where |
| the board's new version is the old default version. |
| """ |
| self._apply_upgrades({'board': _OLD_VERSION}, |
| {'board': _OLD_DEFAULT}, |
| True) |
| |
| def test_board_joins_old_default(self): |
| """ |
| Test upgrading a board to a default that doesn't change. |
| |
| Test the case of upgrading board to the default, where the |
| default mapping stays unchanged. |
| """ |
| self._apply_upgrades({'board': _OLD_VERSION}, |
| {'board': _OLD_DEFAULT}, |
| False) |
| |
| def test_board_joins_new_default(self): |
| """ |
| Test upgrading a board to match the new default. |
| |
| Test the case of upgrading board and the default to the same |
| version. |
| """ |
| self._apply_upgrades({'board': _OLD_VERSION}, |
| {'board': _NEW_DEFAULT}, |
| True) |
| |
| def test_board_becomes_default(self): |
| """ |
| Test a board that becomes default after a default upgrade. |
| |
| Test the case of upgrading the default to a version already |
| mapped for an existing board. |
| """ |
| self._apply_upgrades({'board': _NEW_DEFAULT}, |
| {'board': _NEW_DEFAULT}, |
| True) |
| |
| |
| class ApplyFirmwareUpgradesTests(unittest.TestCase): |
| """Tests for the `_apply_firmware_upgrades()` function.""" |
| |
| def _apply_upgrades(self, old_versions, new_versions): |
| """ |
| Test a single call to `_apply_firmware_upgrades()`. |
| |
| All assertions are handled by an instance of `_TestUpdater`. |
| |
| @param old_versions Parameter to be passed to |
| `_apply_firmware_upgrades()`. |
| @param new_versions Parameter to be passed to |
| `_apply_firmware_upgrades()`. |
| """ |
| updater = _TestUpdater(self) |
| updater.pretest_init(old_versions, new_versions) |
| assign_stable_images._apply_firmware_upgrades( |
| updater, old_versions, new_versions) |
| updater.check_results(False) |
| |
| def test_no_changes(self): |
| """ |
| Test an empty upgrade that does nothing. |
| |
| Test the boundary case of an upgrade where there are no boards. |
| """ |
| self._apply_upgrades({}, {}) |
| |
| def test_board_added(self): |
| """ |
| Test an upgrade that adds a new board. |
| |
| Test the case of an upgrade where a board that was previously |
| unmapped is added. |
| """ |
| self._apply_upgrades({}, {'board': _NEW_VERSION}) |
| |
| def test_board_unchanged(self): |
| """ |
| Test an upgrade with no changes to a board. |
| |
| Test the case of an upgrade with a board that stays the same. |
| """ |
| self._apply_upgrades({'board': _NEW_VERSION}, |
| {'board': _NEW_VERSION}) |
| |
| def test_board_upgrade_and_change_default(self): |
| """ |
| Test upgrading a board. |
| |
| Test the case of upgrading a board to a new version. |
| """ |
| self._apply_upgrades({'board': _OLD_VERSION}, |
| {'board': _NEW_VERSION}) |
| |
| |
| if __name__ == '__main__': |
| unittest.main() |