• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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"""
6Unit tests for functions in `assign_stable_images`.
7"""
8
9
10import mock
11import unittest
12
13import common
14from autotest_lib.site_utils.stable_images import assign_stable_images
15from autotest_lib.site_utils.stable_images import build_data
16
17
18_DEFAULT_BOARD = assign_stable_images._DEFAULT_BOARD
19
20
21class GetFirmwareUpgradesTests(unittest.TestCase):
22    """Tests for _get_firmware_upgrades."""
23
24    @mock.patch.object(build_data, 'get_firmware_versions')
25    def test_get_firmware_upgrades(self, mock_get_firmware_versions):
26        """Test _get_firmware_upgrades."""
27        mock_get_firmware_versions.side_effect = [
28            {'auron_paine': 'fw_version'},
29            {'blue': 'fw_version',
30             'robo360': 'fw_version',
31             'porbeagle': 'fw_version'}
32        ]
33        cros_versions = {
34            'coral': 'R64-10176.65.0',
35            'auron_paine': 'R64-10176.65.0'
36        }
37        boards = ['auron_paine', 'coral']
38
39        firmware_upgrades = assign_stable_images._get_firmware_upgrades(
40            cros_versions)
41        expected_firmware_upgrades = {
42            'auron_paine': 'fw_version',
43            'blue': 'fw_version',
44            'robo360': 'fw_version',
45            'porbeagle': 'fw_version'
46        }
47        self.assertEqual(firmware_upgrades, expected_firmware_upgrades)
48
49
50class GetUpgradeTests(unittest.TestCase):
51    """Tests for the `_get_upgrade_versions()` function."""
52
53    # _VERSIONS - a list of sample version strings such as may be used
54    #   for Chrome OS, sorted from oldest to newest.  These are used to
55    #   construct test data in multiple test cases, below.
56    _VERSIONS = ['R1-1.0.0', 'R1-1.1.0', 'R2-4.0.0']
57
58    def test_board_conversions(self):
59        """
60        Test proper mapping of names from the AFE to Omaha.
61
62        Board names in Omaha don't have '_' characters; when an AFE
63        board contains '_' characters, they must be converted to '-'.
64
65        Assert that for various forms of name in the AFE mapping, the
66        converted name is the one looked up in the Omaha mapping.
67        """
68        board_equivalents = [
69            ('a-b', 'a-b'), ('c_d', 'c-d'),
70            ('e_f-g', 'e-f-g'), ('hi', 'hi')]
71        afe_versions = {
72            _DEFAULT_BOARD: self._VERSIONS[0]
73        }
74        omaha_versions = {}
75        expected = {}
76        boards = set()
77        for afe_board, omaha_board in board_equivalents:
78            boards.add(afe_board)
79            afe_versions[afe_board] = self._VERSIONS[1]
80            omaha_versions[omaha_board] = self._VERSIONS[2]
81            expected[afe_board] = self._VERSIONS[2]
82        upgrades, _ = assign_stable_images._get_upgrade_versions(
83                afe_versions, omaha_versions, boards)
84        self.assertEqual(upgrades, expected)
85
86    def test_afe_default(self):
87        """
88        Test that the AFE default board mapping is honored.
89
90        If a board isn't present in the AFE dictionary, the mapping
91        for `_DEFAULT_BOARD` should be used.
92
93        Primary assertions:
94          * When a board is present in the AFE mapping, its version
95            mapping is used.
96          * When a board is not present in the AFE mapping, the default
97            version mapping is used.
98
99        Secondarily, assert that when a mapping is absent from Omaha,
100        the AFE mapping is left unchanged.
101        """
102        afe_versions = {
103            _DEFAULT_BOARD: self._VERSIONS[0],
104            'a': self._VERSIONS[1]
105        }
106        boards = set(['a', 'b'])
107        expected = {
108            'a': self._VERSIONS[1],
109            'b': self._VERSIONS[0]
110        }
111        upgrades, _ = assign_stable_images._get_upgrade_versions(
112                afe_versions, {}, boards)
113        self.assertEqual(upgrades, expected)
114
115    def test_omaha_upgrade(self):
116        """
117        Test that upgrades from Omaha are detected.
118
119        Primary assertion:
120          * If a board is found in Omaha, and the version in Omaha is
121            newer than the AFE version, the Omaha version is the one
122            used.
123
124        Secondarily, asserts that version comparisons between various
125        specific version strings are all correct.
126        """
127        boards = set(['a'])
128        for i in range(0, len(self._VERSIONS) - 1):
129            afe_versions = {_DEFAULT_BOARD: self._VERSIONS[i]}
130            for j in range(i+1, len(self._VERSIONS)):
131                omaha_versions = {b: self._VERSIONS[j] for b in boards}
132                upgrades, _ = assign_stable_images._get_upgrade_versions(
133                        afe_versions, omaha_versions, boards)
134                self.assertEqual(upgrades, omaha_versions)
135
136    def test_no_upgrade(self):
137        """
138        Test that if Omaha is behind the AFE, it is ignored.
139
140        Primary assertion:
141          * If a board is found in Omaha, and the version in Omaha is
142            older than the AFE version, the AFE version is the one used.
143
144        Secondarily, asserts that version comparisons between various
145        specific version strings are all correct.
146        """
147        boards = set(['a'])
148        for i in range(1, len(self._VERSIONS)):
149            afe_versions = {_DEFAULT_BOARD: self._VERSIONS[i]}
150            expected = {b: self._VERSIONS[i] for b in boards}
151            for j in range(0, i):
152                omaha_versions = {b: self._VERSIONS[j] for b in boards}
153                upgrades, _ = assign_stable_images._get_upgrade_versions(
154                        afe_versions, omaha_versions, boards)
155                self.assertEqual(upgrades, expected)
156
157    def test_ignore_unused_boards(self):
158        """
159        Test that unlisted boards are ignored.
160
161        Assert that boards present in the AFE or Omaha mappings aren't
162        included in the return mappings when they aren't in the passed
163        in set of boards.
164        """
165        unused_boards = set(['a', 'b'])
166        used_boards = set(['c', 'd'])
167        afe_versions = {b: self._VERSIONS[0] for b in unused_boards}
168        afe_versions[_DEFAULT_BOARD] = self._VERSIONS[1]
169        expected = {b: self._VERSIONS[1] for b in used_boards}
170        omaha_versions = expected.copy()
171        omaha_versions.update(
172                {b: self._VERSIONS[0] for b in unused_boards})
173        upgrades, _ = assign_stable_images._get_upgrade_versions(
174                afe_versions, omaha_versions, used_boards)
175        self.assertEqual(upgrades, expected)
176
177    def test_default_unchanged(self):
178        """
179        Test correct handling when the default build is unchanged.
180
181        Assert that if in Omaha, one board in a set of three upgrades
182        from the AFE default, that the returned default board mapping is
183        the original default in the AFE.
184        """
185        boards = set(['a', 'b', 'c'])
186        afe_versions = {_DEFAULT_BOARD: self._VERSIONS[0]}
187        omaha_versions = {b: self._VERSIONS[0] for b in boards}
188        omaha_versions['c'] = self._VERSIONS[1]
189        _, new_default = assign_stable_images._get_upgrade_versions(
190                afe_versions, omaha_versions, boards)
191        self.assertEqual(new_default, self._VERSIONS[0])
192
193    def test_default_upgrade(self):
194        """
195        Test correct handling when the default build must change.
196
197        Assert that if in Omaha, two boards in a set of three upgrade
198        from the AFE default, that the returned default board mapping is
199        the new build in Omaha.
200        """
201        boards = set(['a', 'b', 'c'])
202        afe_versions = {_DEFAULT_BOARD: self._VERSIONS[0]}
203        omaha_versions = {b: self._VERSIONS[1] for b in boards}
204        omaha_versions['c'] = self._VERSIONS[0]
205        _, new_default = assign_stable_images._get_upgrade_versions(
206                afe_versions, omaha_versions, boards)
207        self.assertEqual(new_default, self._VERSIONS[1])
208
209
210# Sample version string values to be used when testing
211# `_apply_upgrades()`.
212#
213# _OLD_DEFAULT - Test value representing the default version mapping
214#   in the `old_versions` dictionary in a call to `_apply_upgrades()`.
215# _NEW_DEFAULT - Test value representing the default version mapping
216#   in the `new_versions` dictionary when a version update is being
217#   tested.
218# _OLD_VERSION - Test value representing an arbitrary version for a
219#   board that is mapped in the `old_versions` dictionary in a call to
220#   `_apply_upgrades()`.
221# _NEW_VERSION - Test value representing an arbitrary version for a
222#   board that is mapped in the `new_versions` dictionary in a call to
223#   `_apply_upgrades()`.
224#
225_OLD_DEFAULT = 'old-default-version'
226_NEW_DEFAULT = 'new-default-version'
227_OLD_VERSION = 'old-board-version'
228_NEW_VERSION = 'new-board-version'
229
230
231class _StubAFE(object):
232    """Stubbed out version of `server.frontend.AFE`."""
233
234    CROS_IMAGE_TYPE = 'cros-image-type'
235    FIRMWARE_IMAGE_TYPE = 'firmware-image-type'
236
237    def get_stable_version_map(self, image_type):
238        return image_type
239
240
241class _TestUpdater(assign_stable_images._VersionUpdater):
242    """
243    Subclass of `_VersionUpdater` for testing.
244
245    This class extends `_VersionUpdater` to provide support for testing
246    various assertions about the behavior of the base class and its
247    interactions with `_apply_cros_upgrades()` and
248    `_apply_firmware_upgrades()`.
249
250    The class tests assertions along the following lines:
251      * When applied to the original mappings, the calls to
252        `_do_set_mapping()` and `_do_delete_mapping()` create the
253        expected final mapping state.
254      * Calls to report state changes are made with the expected
255        values.
256      * There's a one-to-one match between reported and actually
257        executed changes.
258
259    """
260
261    def __init__(self, testcase):
262        super(_TestUpdater, self).__init__(_StubAFE(), dry_run=True)
263        self._testcase = testcase
264        self._default_changed = None
265        self._reported_mappings = None
266        self._updated_mappings = None
267        self._reported_deletions = None
268        self._actual_deletions = None
269        self._original_mappings = None
270        self._mappings = None
271        self._expected_mappings = None
272        self._unchanged_boards = None
273
274    def pretest_init(self, initial_versions, expected_versions):
275        """
276        Initialize for testing.
277
278        @param initial_versions   Mappings to be used as the starting
279                                  point for testing.
280        @param expected_versions  The expected final value of the
281                                  mappings after the test.
282        """
283        self._default_changed = False
284        self._reported_mappings = {}
285        self._updated_mappings = {}
286        self._reported_deletions = set()
287        self._actual_deletions = set()
288        self._original_mappings = initial_versions.copy()
289        self._mappings = initial_versions.copy()
290        self._expected_mappings = expected_versions
291        self._unchanged_boards = set()
292
293    def check_results(self, change_default):
294        """
295        Assert that observed changes match expectations.
296
297        Asserts the following:
298          * The `report_default_changed()` method was called (or not)
299            based on whether `change_default` is true (or not).
300          * The changes reported via `_report_board_changed()` match
301            the changes actually applied.
302          * The final mappings after applying requested changes match
303            the actually expected mappings.
304
305        @param old_versions   Parameter to be passed to
306                              `_apply_cros_upgrades()`.
307        @param new_versions   Parameter to be passed to
308                              `_apply_cros_upgrades()`.
309        @param change_default   Whether the test should include a change
310                                to the default version mapping.
311        """
312        self._testcase.assertEqual(change_default,
313                                   self._default_changed)
314        self._testcase.assertEqual(self._reported_mappings,
315                                   self._updated_mappings)
316        self._testcase.assertEqual(self._reported_deletions,
317                                   self._actual_deletions)
318        self._testcase.assertEqual(self._mappings,
319                                   self._expected_mappings)
320
321    def report_default_changed(self, old_default, new_default):
322        """
323        Override of our parent class' method for test purposes.
324
325        Saves a record of the report for testing the final result in
326        `apply_upgrades()`, above.
327
328        Assert the following:
329          * The old and new default values match the values that
330            were passed in the original call's arguments.
331          * This function is not being called for a second time.
332
333        @param old_default  The original default version.
334        @param new_default  The new default version to be applied.
335        """
336        self._testcase.assertNotEqual(old_default, new_default)
337        self._testcase.assertEqual(old_default,
338                                   self._original_mappings[_DEFAULT_BOARD])
339        self._testcase.assertEqual(new_default,
340                                   self._expected_mappings[_DEFAULT_BOARD])
341        self._testcase.assertFalse(self._default_changed)
342        self._default_changed = True
343        self._reported_mappings[_DEFAULT_BOARD] = new_default
344
345    def _report_board_changed(self, board, old_version, new_version):
346        """
347        Override of our parent class' method for test purposes.
348
349        Saves a record of the report for testing the final result in
350        `apply_upgrades()`, above.
351
352        Assert the following:
353          * The change being reported actually reports two different
354            versions.
355          * If the board isn't mapped to the default version, then the
356            reported old version is the actually mapped old version.
357          * If the board isn't changing to the default version, then the
358            reported new version is the expected new version.
359          * This is not a second report for this board.
360
361        The implementation implicitly requires that the specified board
362        have a valid mapping.
363
364        @param board        The board with the changing version.
365        @param old_version  The original version mapped to the board.
366        @param new_version  The new version to be applied to the board.
367        """
368        self._testcase.assertNotEqual(old_version, new_version)
369        if board in self._original_mappings:
370            self._testcase.assertEqual(old_version,
371                                       self._original_mappings[board])
372        if board in self._expected_mappings:
373            self._testcase.assertEqual(new_version,
374                                       self._expected_mappings[board])
375            self._testcase.assertNotIn(board, self._reported_mappings)
376            self._reported_mappings[board] = new_version
377        else:
378            self._testcase.assertNotIn(board, self._reported_deletions)
379            self._reported_deletions.add(board)
380
381    def report_board_unchanged(self, board, old_version):
382        """
383        Override of our parent class' method for test purposes.
384
385        Assert the following:
386          * The version being reported as unchanged is actually mapped.
387          * The reported old version matches the expected value.
388          * This is not a second report for this board.
389
390        @param board        The board that is not changing.
391        @param old_version  The board's version mapping.
392        """
393        self._testcase.assertIn(board, self._original_mappings)
394        self._testcase.assertEqual(old_version,
395                                   self._original_mappings[board])
396        self._testcase.assertNotIn(board, self._unchanged_boards)
397        self._unchanged_boards.add(board)
398
399    def _do_set_mapping(self, board, new_version):
400        """
401        Override of our parent class' method for test purposes.
402
403        Saves a record of the change for testing the final result in
404        `apply_upgrades()`, above.
405
406        Assert the following:
407          * This is not a second change for this board.
408          * If we're changing the default mapping, then every board
409            that will be changing to a non-default mapping has been
410            updated.
411
412        @param board        The board with the changing version.
413        @param new_version  The new version to be applied to the board.
414        """
415        self._mappings[board] = new_version
416        self._testcase.assertNotIn(board, self._updated_mappings)
417        self._updated_mappings[board] = new_version
418        if board == _DEFAULT_BOARD:
419            for board in self._expected_mappings:
420                self._testcase.assertIn(board, self._mappings)
421
422    def _do_delete_mapping(self, board):
423        """
424        Override of our parent class' method for test purposes.
425
426        Saves a record of the change for testing the final result in
427        `apply_upgrades()`, above.
428
429        Assert that the board has a mapping prior to deletion.
430
431        @param board        The board with the version to be deleted.
432        """
433        self._testcase.assertNotEqual(board, _DEFAULT_BOARD)
434        self._testcase.assertIn(board, self._mappings)
435        del self._mappings[board]
436        self._actual_deletions.add(board)
437
438
439class ApplyCrOSUpgradesTests(unittest.TestCase):
440    """Tests for the `_apply_cros_upgrades()` function."""
441
442    def _apply_upgrades(self, old_versions, new_versions, change_default):
443        """
444        Test a single call to `_apply_cros_upgrades()`.
445
446        All assertions are handled by an instance of `_TestUpdater`.
447
448        @param old_versions   Parameter to be passed to
449                              `_apply_cros_upgrades()`.
450        @param new_versions   Parameter to be passed to
451                              `_apply_cros_upgrades()`.
452        @param change_default   Whether the test should include a change
453                                to the default version mapping.
454        """
455        old_versions[_DEFAULT_BOARD] = _OLD_DEFAULT
456        if change_default:
457            new_default = _NEW_DEFAULT
458        else:
459            new_default = _OLD_DEFAULT
460        expected_versions = {
461            b: v for b, v in new_versions.items() if v != new_default
462        }
463        expected_versions[_DEFAULT_BOARD] = new_default
464        updater = _TestUpdater(self)
465        updater.pretest_init(old_versions, expected_versions)
466        assign_stable_images._apply_cros_upgrades(
467            updater, old_versions, new_versions, new_default)
468        updater.check_results(change_default)
469
470    def test_no_changes(self):
471        """
472        Test an empty upgrade that does nothing.
473
474        Test the boundary case of an upgrade where there are no boards,
475        and the default does not change.
476        """
477        self._apply_upgrades({}, {}, False)
478
479    def test_change_default(self):
480        """
481        Test an empty upgrade that merely changes the default.
482
483        Test the boundary case of an upgrade where there are no boards,
484        but the default is upgraded.
485        """
486        self._apply_upgrades({}, {}, True)
487
488    def test_board_default_no_changes(self):
489        """
490        Test that a board at default stays with an unchanged default.
491
492        Test the case of a board that is mapped to the default, where
493        neither the board nor the default change.
494        """
495        self._apply_upgrades({}, {'board': _OLD_DEFAULT}, False)
496
497    def test_board_left_behind(self):
498        """
499        Test a board left at the old default after a default upgrade.
500
501        Test the case of a board that stays mapped to the old default as
502        the default board is upgraded.
503        """
504        self._apply_upgrades({}, {'board': _OLD_DEFAULT}, True)
505
506    def test_board_upgrade_from_default(self):
507        """
508        Test upgrading a board from a default that doesn't change.
509
510        Test the case of upgrading a board from default to non-default,
511        where the default doesn't change.
512        """
513        self._apply_upgrades({}, {'board': _NEW_VERSION}, False)
514
515    def test_board_and_default_diverge(self):
516        """
517        Test upgrading a board that diverges from the default.
518
519        Test the case of upgrading a board and default together from the
520        same to different versions.
521        """
522        self._apply_upgrades({}, {'board': _NEW_VERSION}, True)
523
524    def test_board_tracks_default(self):
525        """
526        Test upgrading a board to track a default upgrade.
527
528        Test the case of upgrading a board and the default together.
529        """
530        self._apply_upgrades({}, {'board': _NEW_DEFAULT}, True)
531
532    def test_board_non_default_no_changes(self):
533        """
534        Test an upgrade with no changes to a board or the default.
535
536        Test the case of an upgrade with a board in it, where neither
537        the board nor the default change.
538        """
539        self._apply_upgrades({'board': _NEW_VERSION},
540                             {'board': _NEW_VERSION},
541                             False)
542
543    def test_board_upgrade_and_keep_default(self):
544        """
545        Test a board upgrade with an unchanged default.
546
547        Test the case of upgrading a board while the default stays the
548        same.
549        """
550        self._apply_upgrades({'board': _OLD_VERSION},
551                             {'board': _NEW_VERSION},
552                             False)
553
554    def test_board_upgrade_and_change_default(self):
555        """
556        Test upgrading a board and the default separately.
557
558        Test the case of upgrading both a board and the default, each
559        from and to different versions.
560        """
561        self._apply_upgrades({'board': _OLD_VERSION},
562                             {'board': _NEW_VERSION},
563                             True)
564
565    def test_board_leads_default(self):
566        """
567        Test a board that upgrades ahead of the new default.
568
569        Test the case of upgrading both a board and the default, where
570        the board's old version is the new default version.
571        """
572        self._apply_upgrades({'board': _NEW_DEFAULT},
573                             {'board': _NEW_VERSION},
574                             True)
575
576    def test_board_lags_to_old_default(self):
577        """
578        Test a board that upgrades behind the old default.
579
580        Test the case of upgrading both a board and the default, where
581        the board's new version is the old default version.
582        """
583        self._apply_upgrades({'board': _OLD_VERSION},
584                             {'board': _OLD_DEFAULT},
585                             True)
586
587    def test_board_joins_old_default(self):
588        """
589        Test upgrading a board to a default that doesn't change.
590
591        Test the case of upgrading board to the default, where the
592        default mapping stays unchanged.
593        """
594        self._apply_upgrades({'board': _OLD_VERSION},
595                             {'board': _OLD_DEFAULT},
596                             False)
597
598    def test_board_joins_new_default(self):
599        """
600        Test upgrading a board to match the new default.
601
602        Test the case of upgrading board and the default to the same
603        version.
604        """
605        self._apply_upgrades({'board': _OLD_VERSION},
606                             {'board': _NEW_DEFAULT},
607                             True)
608
609    def test_board_becomes_default(self):
610        """
611        Test a board that becomes default after a default upgrade.
612
613        Test the case of upgrading the default to a version already
614        mapped for an existing board.
615        """
616        self._apply_upgrades({'board': _NEW_DEFAULT},
617                             {'board': _NEW_DEFAULT},
618                             True)
619
620
621class ApplyFirmwareUpgradesTests(unittest.TestCase):
622    """Tests for the `_apply_firmware_upgrades()` function."""
623
624    def _apply_upgrades(self, old_versions, new_versions):
625        """
626        Test a single call to `_apply_firmware_upgrades()`.
627
628        All assertions are handled by an instance of `_TestUpdater`.
629
630        @param old_versions   Parameter to be passed to
631                              `_apply_firmware_upgrades()`.
632        @param new_versions   Parameter to be passed to
633                              `_apply_firmware_upgrades()`.
634        """
635        updater = _TestUpdater(self)
636        updater.pretest_init(old_versions, new_versions)
637        assign_stable_images._apply_firmware_upgrades(
638            updater, old_versions, new_versions)
639        updater.check_results(False)
640
641    def test_no_changes(self):
642        """
643        Test an empty upgrade that does nothing.
644
645        Test the boundary case of an upgrade where there are no boards.
646        """
647        self._apply_upgrades({}, {})
648
649    def test_board_added(self):
650        """
651        Test an upgrade that adds a new board.
652
653        Test the case of an upgrade where a board that was previously
654        unmapped is added.
655        """
656        self._apply_upgrades({}, {'board': _NEW_VERSION})
657
658    def test_board_unchanged(self):
659        """
660        Test an upgrade with no changes to a board.
661
662        Test the case of an upgrade with a board that stays the same.
663        """
664        self._apply_upgrades({'board': _NEW_VERSION},
665                             {'board': _NEW_VERSION})
666
667    def test_board_upgrade_and_change_default(self):
668        """
669        Test upgrading a board.
670
671        Test the case of upgrading a board to a new version.
672        """
673        self._apply_upgrades({'board': _OLD_VERSION},
674                             {'board': _NEW_VERSION})
675
676
677if __name__ == '__main__':
678    unittest.main()
679