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