1#!/usr/bin/env python
2#
3# Copyright (C) 2018 The Android Open Source Project
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9#      http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16#
17
18import os
19import unittest
20
21try:
22    from unittest import mock
23except ImportError:
24    import mock
25
26from host_controller import common
27from host_controller.command_processor import command_upload
28
29
30def side_effect(value):
31    return value
32
33
34class CommandUploadTest(unittest.TestCase):
35    """Tests for upload command processor"""
36
37    @mock.patch("host_controller.console.Console")
38    @mock.patch("host_controller.command_processor.command_upload.open")
39    @mock.patch("host_controller.command_processor.command_upload.cmd_utils")
40    @mock.patch("host_controller.command_processor.command_upload.SuiteResMsg")
41    @mock.patch("host_controller.command_processor.command_upload.SchedCfgMsg")
42    def testUploadReportBootupErr(self, mock_sched_config_msg,
43                                  mock_suite_res_msg, mock_cmd_util, mock_open,
44                                  mock_console):
45        mock_open.__enter__ = mock.Mock(return_value=mock_open)
46        mock_open.__exit__ = mock.Mock(return_value=None)
47        mock_cmd_util.ExecuteOneShellCommand = mock.Mock(
48            return_value=("", "", 0))
49        mock_console.vti_endpoint_client.CheckBootUpStatus.return_value = False
50        mock_console.FormatString.side_effect = side_effect
51        mock_console.fetch_info = {
52            "branch": "git_device_branch",
53            "target": "device-userdebug",
54            "build_id": "1234567",
55            "account_id": common._DEFAULT_ACCOUNT_ID,
56        }
57        mock_console.detailed_fetch_info = {
58            "device": {
59                "branch": "git_device_branch",
60                "target": "device-userdebug",
61                "build_id": "1234567",
62                "account_id": common._DEFAULT_ACCOUNT_ID,
63                "fetch_signed_build": False,
64            },
65            "gsi": {
66                "branch": "git_aosp_gsi_branch",
67                "target": "gsi-userdebug",
68                "build_id": "2345678",
69                "account_id": common._DEFAULT_ACCOUNT_ID_INTERNAL,
70            }
71        }
72        mock_console.tmp_logdir = "tmp/log"
73        mock_console.device_image_info = {
74            "bootloader.img": "path/to/bootloader.img"
75        }
76        mock_pb2 = mock.Mock()
77        mock_pb2.repacked_image_path = []
78        mock_pb2.schedule_config.build_target = []
79        mock_build_sched_config_pb2 = mock.Mock()
80        mock_build_sched_config_pb2.test_schedule = []
81        mock_test_sched_config_pb2 = mock.Mock()
82        mock_suite_res_msg.TestSuiteResultMessage.return_value = mock_pb2
83        mock_sched_config_msg.BuildScheduleConfigMessage.return_value = (
84            mock_build_sched_config_pb2)
85        mock_sched_config_msg.TestScheduleConfigMessage.return_value = (
86            mock_test_sched_config_pb2)
87        command = command_upload.CommandUpload()
88        command._SetUp(mock_console)
89        command.UploadReport("/path/to/bin/gsutil", "gs://report-bucket/",
90                             "tmp/console.log", "tmp/result.log", "vts",
91                             "some_plan")
92        self.assertEqual(mock_pb2.build_id, "1234567")
93        self.assertEqual(mock_pb2.suite_name, "vts")
94        self.assertEqual(mock_pb2.suite_plan, "some_plan")
95        self.assertEqual(mock_pb2.suite_build_number, mock_pb2.build_id)
96        self.assertEqual(mock_pb2.build_system_fingerprint,
97                         "git_aosp_gsi_branch/gsi-userdebug/2345678")
98        self.assertEqual(mock_pb2.build_vendor_fingerprint,
99                         "git_device_branch/device-userdebug/1234567")
100        self.assertEqual(mock_pb2.repacked_image_path, ["{repack_path}"])
101        self.assertEqual(mock_pb2.schedule_config.manifest_branch,
102                         "git_device_branch")
103        self.assertEqual(mock_pb2.schedule_config.pab_account_id,
104                         common._DEFAULT_ACCOUNT_ID)
105
106        self.assertTrue(mock_build_sched_config_pb2.has_bootloader_img)
107        self.assertFalse(mock_build_sched_config_pb2.has_radio_img)
108
109        self.assertEqual(mock_test_sched_config_pb2.gsi_branch,
110                         "git_aosp_gsi_branch")
111        self.assertEqual(mock_test_sched_config_pb2.gsi_build_target,
112                         "gsi-userdebug")
113        self.assertEqual(mock_test_sched_config_pb2.gsi_pab_account_id,
114                         common._DEFAULT_ACCOUNT_ID_INTERNAL)
115        mock_cmd_util.ExecuteOneShellCommand.assert_called_with(
116            "/path/to/bin/gsutil cp tmp/log/{timestamp_time}.bin "
117            "gs://report-bucket/{timestamp_time}.bin")
118
119    @mock.patch("host_controller.console.Console")
120    @mock.patch("host_controller.command_processor.command_upload.open")
121    @mock.patch("host_controller.command_processor.command_upload.cmd_utils")
122    @mock.patch("host_controller.command_processor.command_upload.SuiteResMsg")
123    @mock.patch("host_controller.command_processor.command_upload.os")
124    @mock.patch("host_controller.command_processor.command_upload.xml_utils")
125    @mock.patch("host_controller.command_processor.command_upload.SchedCfgMsg")
126    def testUploadReportBootupOk(self, mock_sched_config_msg, mock_xml_util,
127                                 mock_os, mock_suite_res_msg, mock_cmd_util,
128                                 mock_open, mock_console):
129        mock_open.__enter__ = mock.Mock(return_value=mock_open)
130        mock_open.__exit__ = mock.Mock(return_value=None)
131        mock_cmd_util.ExecuteOneShellCommand = mock.Mock(
132            return_value=("", "", 0))
133        mock_console.vti_endpoint_client.CheckBootUpStatus.return_value = True
134        mock_console.FormatString.side_effect = side_effect
135        mock_console.tmp_logdir = "tmp/log"
136        mock_pb2 = mock.Mock()
137        mock_pb2.repacked_image_path = []
138        mock_pb2.schedule_config.build_target = []
139        mock_build_sched_config_pb2 = mock.Mock()
140        mock_build_sched_config_pb2.test_schedule = []
141        mock_test_sched_config_pb2 = mock.Mock()
142        mock_suite_res_msg.TestSuiteResultMessage.return_value = mock_pb2
143        mock_sched_config_msg.BuildScheduleConfigMessage.return_value = (
144            mock_build_sched_config_pb2)
145        mock_sched_config_msg.TestScheduleConfigMessage.return_value = (
146            mock_test_sched_config_pb2)
147        mock_os.listdir.return_value = ["1", "2", "3"]
148        mock_os.path.isdir.return_value = True
149        mock_os.path.islink.return_value = False
150        mock_os.path.join = os.path.join
151        mock_os.path.basename = os.path.basename
152        mock_xml_util.GetAttributes.return_value = {
153            common._SUITE_NAME_ATTR_KEY:
154            "vts",
155            common._SUITE_PLAN_ATTR_KEY:
156            "some_plan",
157            common._SUITE_VERSION_ATTR_KEY:
158            "8.0_R1",
159            common._SUITE_BUILD_NUM_ATTR_KEY:
160            "1234567",
161            common._START_TIME_ATTR_KEY:
162            "0",
163            common._END_TIME_ATTR_KEY:
164            "1",
165            common._HOST_NAME_ATTR_KEY:
166            "this-host",
167            common._FINGERPRINT_ATTR_KEY:
168            "git_device_branch/device-userdebug/1234567",
169            common._SYSTEM_FINGERPRINT_ATTR_KEY:
170            "git_aosp_gsi_branch/gsi-userdebug/2345678",
171            common._VENDOR_FINGERPRINT_ATTR_KEY:
172            "git_device_branch/device-userdebug/1234567",
173            common._PASSED_ATTR_KEY:
174            "1265",
175            common._FAILED_ATTR_KEY:
176            "43",
177            common._MODULES_TOTAL_ATTR_KEY:
178            "100",
179            common._MODULES_DONE_ATTR_KEY:
180            "98",
181        }
182        command = command_upload.CommandUpload()
183        command._SetUp(mock_console)
184        command.UploadReport("/path/to/bin/gsutil", "gs://report-bucket/",
185                             "tmp/console.log", "tmp/vts/results", "vts",
186                             "some_plan")
187        self.assertEqual(mock_pb2.build_id, "1234567")
188        self.assertEqual(mock_pb2.suite_name, "vts")
189        self.assertEqual(mock_pb2.suite_plan, "some_plan")
190        self.assertEqual(mock_pb2.suite_version, "8.0_R1")
191        self.assertEqual(mock_pb2.suite_build_number, mock_pb2.build_id)
192        self.assertEqual(mock_pb2.start_time, 0)
193        self.assertEqual(mock_pb2.end_time, 1)
194        self.assertEqual(mock_pb2.host_name, "this-host")
195        self.assertEqual(mock_pb2.build_system_fingerprint,
196                         "git_aosp_gsi_branch/gsi-userdebug/2345678")
197        self.assertEqual(mock_pb2.build_vendor_fingerprint,
198                         "git_device_branch/device-userdebug/1234567")
199        self.assertEqual(mock_pb2.passed_test_case_count, 1265)
200        self.assertEqual(mock_pb2.failed_test_case_count, 43)
201        self.assertEqual(mock_pb2.modules_total, 100)
202        self.assertEqual(mock_pb2.modules_done, 98)
203        self.assertEqual(mock_pb2.repacked_image_path, ["{repack_path}"])
204        mock_cmd_util.ExecuteOneShellCommand.assert_called_with(
205            "/path/to/bin/gsutil cp tmp/log/{timestamp_time}.bin "
206            "gs://report-bucket/{timestamp_time}.bin")
207
208    @mock.patch("host_controller.console.Console")
209    @mock.patch("host_controller.command_processor.command_upload.os")
210    @mock.patch("host_controller.command_processor.command_upload.logging")
211    def testUploadReportResultDirAbsent(self, mock_logger, mock_os,
212                                        mock_console):
213        mock_console.vti_endpoint_client.CheckBootUpStatus.return_value = True
214        mock_console.FormatString.side_effect = side_effect
215        mock_console.tmp_logdir = "tmp/log"
216        mock_os.listdir.return_value = []
217        command = command_upload.CommandUpload()
218        command._SetUp(mock_console)
219        ret = command.UploadReport("/path/to/bin/gsutil",
220                                   "gs://report-bucket/", "tmp/console.log",
221                                   "tmp/result.log", "vts", "some_plan")
222        self.assertFalse(ret)
223        mock_logger.error.assert_called_with("No test result found.")
224
225    @mock.patch("host_controller.console.Console")
226    @mock.patch("host_controller.command_processor.command_upload.gcs_utils")
227    @mock.patch("host_controller.command_processor.command_upload.logging")
228    def testCommandUploadGsutilAbsent(self, mock_logger, mock_gcs_util,
229                                      mock_console):
230        mock_gcs_util.GetGsutilPath.return_value = ""
231        command = command_upload.CommandUpload()
232        command.UploadReport = mock.Mock()
233        command._SetUp(mock_console)
234        ret = command._Run("--src=tmp/result.log --dest=gs://report-bucket/")
235        self.assertFalse(ret)
236        mock_logger.error.assert_called_with(
237            "Please check gsutil is installed and on your PATH")
238
239    @mock.patch("host_controller.console.Console")
240    @mock.patch("host_controller.command_processor.command_upload.gcs_utils")
241    @mock.patch("host_controller.command_processor.command_upload.logging")
242    def testCommandUploadLatestSrc(self, mock_logger, mock_gcs_util,
243                                   mock_console):
244        mock_gcs_util.GetGsutilPath.return_value = "/path/to/bin/gsutil"
245        command = command_upload.CommandUpload()
246        command.UploadReport = mock.Mock()
247        command._SetUp(mock_console)
248        ret = command._Run(
249            "--src=latest-something.img --dest=gs://report-bucket/")
250        self.assertFalse(ret)
251        mock_logger.error.assert_called_with(
252            "Unable to find something.img in device_image_info")
253
254    @mock.patch("host_controller.console.Console")
255    @mock.patch("host_controller.command_processor.command_upload.gcs_utils")
256    @mock.patch("host_controller.command_processor.command_upload.os")
257    def testCommandUploadLatestLegitSrc(self, mock_os, mock_gcs_util,
258                                        mock_console):
259        mock_os.path.isfile.return_value = True
260        mock_console.device_image_info = {"system.img": "path/to/system.img"}
261        mock_console.FormatString.side_effect = side_effect
262        mock_gcs_util.GetGsutilPath.return_value = "/path/to/bin/gsutil"
263        command = command_upload.CommandUpload()
264        command.UploadReport = mock.Mock()
265        command._SetUp(mock_console)
266        ret = command._Run(
267            "--src=latest-system.img --dest=gs://report-bucket/dir "
268            "--clear_dest")
269        self.assertIsNone(ret)
270        mock_gcs_util.Remove.assert_called_with(
271            "/path/to/bin/gsutil", "gs://report-bucket/dir", recursive=True)
272        mock_gcs_util.Copy.assert_called_with('/path/to/bin/gsutil',
273                                              'path/to/system.img',
274                                              'gs://report-bucket/dir')
275
276    @mock.patch("host_controller.console.Console")
277    @mock.patch("host_controller.command_processor.command_upload.gcs_utils")
278    @mock.patch("host_controller.command_processor.command_upload.os")
279    @mock.patch("host_controller.command_processor.command_upload.logging")
280    def testCommandUploadFalseDest(self, mock_logger, mock_os, mock_gcs_util,
281                                   mock_console):
282        mock_os.path.isfile.return_value = True
283        mock_console.device_image_info = {"system.img": "path/to/system.img"}
284        mock_console.FormatString.side_effect = side_effect
285        mock_gcs_util.GetGsutilPath.return_value = "/path/to/bin/gsutil"
286        command = command_upload.CommandUpload()
287        command.UploadReport = mock.Mock()
288        command._SetUp(mock_console)
289        ret = command._Run("--src=latest-system.img --dest=/report-bucket/")
290        self.assertFalse(ret)
291        mock_logger.error.assert_called_with(
292            "/report-bucket/ is not correct GCS url.")
293
294    @mock.patch("host_controller.console.Console")
295    @mock.patch("host_controller.command_processor.command_upload.gcs_utils")
296    @mock.patch("host_controller.command_processor.command_upload.os")
297    def testCommandUploadMultipleFiles(self, mock_os, mock_gcs_util,
298                                       mock_console):
299        mock_os.path.isfile.return_value = True
300        mock_console.FormatString.side_effect = side_effect
301        mock_gcs_util.GetGsutilPath.return_value = "/path/to/bin/gsutil"
302        command = command_upload.CommandUpload()
303        command.UploadReport = mock.Mock()
304        command._SetUp(mock_console)
305        ret = command._Run("--src=result.zip --dest=gs://report-bucket/")
306        self.assertIsNone(ret)
307        mock_gcs_util.Copy.assert_called_with(
308            '/path/to/bin/gsutil', 'result.zip', 'gs://report-bucket/')
309
310
311if __name__ == "__main__":
312    unittest.main()
313