1# Copyright 2018 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"""Unit tests for the `stable_version` module and CLI."""
6
7import mock
8import unittest
9
10import common
11from autotest_lib.server import frontend
12from autotest_lib.site_utils.stable_images import stable_version
13
14
15class ParseArgsTestCase(unittest.TestCase):
16    """Unit tests for `_parse_args()`."""
17
18    def test_default_options(self):
19        """Test for an empty command line."""
20        arguments = stable_version._parse_args(['command'])
21        self.assertFalse(arguments.dry_run)
22        self.assertIsNone(arguments.type)
23        self.assertIsNone(arguments.web)
24        self.assertFalse(arguments.delete)
25        self.assertIsNone(arguments.key)
26        self.assertIsNone(arguments.version)
27
28    def test_web_option(self):
29        """Test for the `--web` option."""
30        for option in ['-w', '--web']:
31            argv = ['command', option, 'server']
32            arguments = stable_version._parse_args(argv)
33            self.assertEqual(arguments.web, argv[2])
34
35    def test_dry_run_option(self):
36        """Test for the `--dry-run` option."""
37        for option in ['-n', '--dry-run']:
38            argv = ['command', option]
39            arguments = stable_version._parse_args(argv)
40            self.assertTrue(arguments.dry_run)
41
42    def test_image_type_option(self):
43        """Test for the `--type` option."""
44        for image_type in stable_version._ALL_IMAGE_TYPES:
45            for option in ['-t', '--type']:
46                argv = ['command', option, image_type]
47                arguments = stable_version._parse_args(argv)
48
49    def test_delete_option(self):
50        """Test for the `--delete` option."""
51        for option in ['-d', '--delete']:
52            argv = ['command', option]
53            arguments = stable_version._parse_args(argv)
54            self.assertTrue(arguments.delete)
55
56    def test_key_argument(self):
57        """Test for the BOARD_OR_MODEL argument."""
58        argv = ['command', 'key']
59        arguments = stable_version._parse_args(argv)
60        self.assertEqual(arguments.key, argv[1])
61
62    def test_version_argument(self):
63        """Test for the VERSION argument."""
64        argv = ['command', 'key', 'version']
65        arguments = stable_version._parse_args(argv)
66        self.assertEqual(arguments.key, argv[1])
67        self.assertEqual(arguments.version, argv[2])
68
69
70class ProcessCommandTestCase(unittest.TestCase):
71    """Unit tests for `_dispatch_command()`."""
72
73    def _dispatch_command_success(self, afe, argv, name_called):
74        arguments = stable_version._parse_args(argv)
75        patches = mock.patch.multiple(stable_version,
76                                      list_all_mappings=mock.DEFAULT,
77                                      list_mapping_by_key=mock.DEFAULT,
78                                      set_mapping=mock.DEFAULT,
79                                      delete_mapping=mock.DEFAULT)
80        with patches as mocks:
81            stable_version._dispatch_command(afe, arguments)
82        for not_called in set(mocks) - set([name_called]):
83            mocks[not_called].assert_not_called()
84        return mocks[name_called]
85
86    def _assert_command_error(self, argv):
87        afe = object()
88        arguments = stable_version._parse_args(argv)
89        with self.assertRaises(stable_version._CommandError):
90            stable_version._dispatch_command(afe, arguments)
91
92    def test_list_all(self):
93        """Test that `list_all_mappings` is called when required."""
94        argv = ['command']
95        afe = object()
96        called_mock = self._dispatch_command_success(
97                afe, argv, 'list_all_mappings')
98        called_mock.assert_called_once_with(afe, None)
99
100    def test_list_all_with_type(self):
101        """Test that `list_all_mappings` is called with a supplied type."""
102        argv = ['command', '-t', frontend.AFE.CROS_IMAGE_TYPE]
103        afe = object()
104        called_mock = self._dispatch_command_success(
105                afe, argv, 'list_all_mappings')
106        called_mock.assert_called_once_with(afe, argv[2])
107
108    def test_list_mapping_by_key(self):
109        """Test that `list_mapping_by_key` is called when required."""
110        argv = ['command', 'board']
111        afe = object()
112        called_mock = self._dispatch_command_success(
113                afe, argv, 'list_mapping_by_key')
114        called_mock.assert_called_once_with(afe, None, argv[1])
115
116    def test_list_mapping_by_key_with_type(self):
117        """Test that `list_mapping_by_key` is called with a supplied type."""
118        argv = ['command', '-t', frontend.AFE.CROS_IMAGE_TYPE, 'board']
119        afe = object()
120        called_mock = self._dispatch_command_success(
121                afe, argv, 'list_mapping_by_key')
122        called_mock.assert_called_once_with(afe, argv[2], argv[3])
123
124    def test_set_mapping(self):
125        """Test that `set_mapping` is called when required."""
126        argv = ['command', '-t', frontend.AFE.CROS_IMAGE_TYPE, 'board', 'V0.0']
127        afe = object()
128        called_mock = self._dispatch_command_success(
129                afe, argv, 'set_mapping')
130        called_mock.assert_called_once_with(afe, argv[2], argv[3],
131                                            argv[4], False)
132
133    def test_set_mapping_firmware(self):
134        """Test error when `set_mapping` is called for firmware."""
135        argv = ['command', '-t', frontend.AFE.FIRMWARE_IMAGE_TYPE,
136                'board', 'V0.0']
137        self._assert_command_error(argv)
138
139    def test_set_mapping_no_type(self):
140        """Test error when `set_mapping` is called without a type."""
141        argv = ['command', 'board', 'V0.0']
142        self._assert_command_error(argv)
143
144    def test_delete_mapping(self):
145        """Test that `delete_mapping` is called when required."""
146        argv = ['command', '-t', frontend.AFE.CROS_IMAGE_TYPE, '-d', 'board']
147        afe = object()
148        called_mock = self._dispatch_command_success(
149                afe, argv, 'delete_mapping')
150        called_mock.assert_called_once_with(afe, argv[2], argv[4], False)
151
152    def test_delete_mapping_no_key(self):
153        """Test error when `delete_mapping` is called without a key."""
154        argv = ['command', '-t', frontend.AFE.CROS_IMAGE_TYPE, '-d']
155        self._assert_command_error(argv)
156
157    def test_delete_mapping_no_type(self):
158        """Test error when `delete_mapping` is called without a type."""
159        argv = ['command', '-d', 'board']
160        self._assert_command_error(argv)
161
162    def test_delete_mapping_with_version(self):
163        """Test error when `delete_mapping` is called with extra arguments."""
164        argv = ['command', '-t', frontend.AFE.CROS_IMAGE_TYPE, '-d',
165                'board', 'V0.0']
166        self._assert_command_error(argv)
167
168
169if __name__ == '__main__':
170    unittest.main()
171