blob: a045b7e8cf893be2250a1e236559a6d00a4c4c18 [file] [log] [blame]
Alex Kleinf4dc4f52018-12-05 13:55:12 -07001# -*- coding: utf-8 -*-
2# Copyright 2018 The Chromium OS Authors. All rights reserved.
3# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
5
Alex Klein2bfacb22019-02-04 11:42:17 -07006"""Tests for the build_api script covering the base Build API functionality."""
7
Alex Kleinf4dc4f52018-12-05 13:55:12 -07008from __future__ import print_function
9
Alex Klein5bcb4d22019-03-21 13:51:54 -060010import os
Mike Frysingeref94e4c2020-02-10 23:59:54 -050011import sys
Alex Klein5bcb4d22019-03-21 13:51:54 -060012
Alex Kleinbd6edf82019-07-18 10:30:49 -060013from google.protobuf import json_format
14
Alex Klein69339cc2019-07-22 14:08:35 -060015from chromite.api import api_config
Alex Klein146d4772019-06-20 13:48:25 -060016from chromite.api import router
Alex Klein7107bdd2019-03-14 17:14:31 -060017from chromite.api.gen.chromite.api import build_api_test_pb2
Alex Kleinc7d647f2020-01-06 12:00:48 -070018from chromite.lib import chroot_lib
Alex Klein2bfacb22019-02-04 11:42:17 -070019from chromite.lib import cros_build_lib
Alex Kleinf4dc4f52018-12-05 13:55:12 -070020from chromite.lib import cros_test_lib
Alex Klein5bcb4d22019-03-21 13:51:54 -060021from chromite.lib import osutils
Alex Kleinf4dc4f52018-12-05 13:55:12 -070022
23
Mike Frysingeref94e4c2020-02-10 23:59:54 -050024assert sys.version_info >= (3, 6), 'This module requires Python 3.6+'
25
26
Alex Klein69339cc2019-07-22 14:08:35 -060027class RouterTest(cros_test_lib.RunCommandTempDirTestCase,
28 api_config.ApiConfigMixin):
Alex Kleinf4dc4f52018-12-05 13:55:12 -070029 """Test Router functionality."""
Alex Klein00aa8072019-04-15 16:36:00 -060030 _INPUT_JSON_TEMPLATE = '{"id":"Input ID", "chroot":{"path": "%s"}}'
Alex Kleinf4dc4f52018-12-05 13:55:12 -070031
32 def setUp(self):
Alex Klein146d4772019-06-20 13:48:25 -060033 self.router = router.Router()
Alex Kleinf4dc4f52018-12-05 13:55:12 -070034 self.router.Register(build_api_test_pb2)
35
Alex Klein5bcb4d22019-03-21 13:51:54 -060036 self.input_file = os.path.join(self.tempdir, 'input.json')
37 self.output_file = os.path.join(self.tempdir, 'output.json')
38
Alex Klein00aa8072019-04-15 16:36:00 -060039 self.chroot_dir = os.path.join(self.tempdir, 'chroot')
40 chroot_tmp = os.path.join(self.chroot_dir, 'tmp')
41 # Make the tmp dir for the re-exec inside chroot input/output files.
42 osutils.SafeMakedirs(chroot_tmp)
43
44 osutils.WriteFile(self.input_file,
45 self._INPUT_JSON_TEMPLATE % self.chroot_dir)
Alex Kleinbd6edf82019-07-18 10:30:49 -060046 osutils.WriteFile(self.output_file, '{}')
47
48 self.subprocess_tempdir = os.path.join(self.chroot_dir, 'tempdir')
49 osutils.SafeMakedirs(self.subprocess_tempdir)
Alex Klein5bcb4d22019-03-21 13:51:54 -060050
Alex Kleinf4dc4f52018-12-05 13:55:12 -070051 def testInputOutputMethod(self):
52 """Test input/output handling."""
Alex Klein69339cc2019-07-22 14:08:35 -060053 def impl(input_msg, output_msg, config):
Alex Kleinf4dc4f52018-12-05 13:55:12 -070054 self.assertIsInstance(input_msg, build_api_test_pb2.TestRequestMessage)
55 self.assertIsInstance(output_msg, build_api_test_pb2.TestResultMessage)
Alex Klein69339cc2019-07-22 14:08:35 -060056 self.assertIsInstance(config, api_config.ApiConfig)
Alex Kleinf4dc4f52018-12-05 13:55:12 -070057
58 self.PatchObject(self.router, '_GetMethod', return_value=impl)
59
60 self.router.Route('chromite.api.TestApiService', 'InputOutputMethod',
Alex Klein69339cc2019-07-22 14:08:35 -060061 self.input_file, self.output_file, self.api_config)
Alex Kleinf4dc4f52018-12-05 13:55:12 -070062
Alex Klein2bfacb22019-02-04 11:42:17 -070063 def testRenameMethod(self):
64 """Test implementation name config."""
65 def _GetMethod(_, method_name):
66 self.assertEqual('CorrectName', method_name)
Alex Klein69339cc2019-07-22 14:08:35 -060067 return lambda x, y, z: None
Alex Klein2bfacb22019-02-04 11:42:17 -070068
69 self.PatchObject(self.router, '_GetMethod', side_effect=_GetMethod)
70
71 self.router.Route('chromite.api.TestApiService', 'RenamedMethod',
Alex Klein69339cc2019-07-22 14:08:35 -060072 self.input_file, self.output_file, self.api_config)
Alex Klein2bfacb22019-02-04 11:42:17 -070073
Alex Klein00aa8072019-04-15 16:36:00 -060074 def _mock_callable(self, expect_called):
75 """Helper to create the implementation mock to test chroot assertions.
76
77 Args:
78 expect_called (bool): Whether the implementation should be called.
79 When False, an assertion will fail if it is called.
80
81 Returns:
82 callable - The implementation.
83 """
Alex Klein69339cc2019-07-22 14:08:35 -060084 def impl(_input_msg, _output_msg, _config):
Alex Klein00aa8072019-04-15 16:36:00 -060085 self.assertTrue(expect_called,
Alex Klein2bfacb22019-02-04 11:42:17 -070086 'The implementation should not have been called.')
Alex Klein2bfacb22019-02-04 11:42:17 -070087
Alex Klein00aa8072019-04-15 16:36:00 -060088 return impl
Alex Klein2bfacb22019-02-04 11:42:17 -070089
Alex Kleinbd6edf82019-07-18 10:30:49 -060090 def _writeChrootCallOutput(self, content='{}'):
91 def impl(*_args, **_kwargs):
92 """Side effect for inside-chroot calls to the API."""
93 osutils.WriteFile(
94 os.path.join(self.subprocess_tempdir,
95 router.Router.REEXEC_OUTPUT_FILE),
96 content)
97
98 return impl
99
Alex Klein00aa8072019-04-15 16:36:00 -0600100 def testInsideServiceInsideMethodInsideChroot(self):
101 """Test inside/inside/inside works correctly."""
102 self.PatchObject(self.router, '_GetMethod',
103 return_value=self._mock_callable(expect_called=True))
104 self.PatchObject(cros_build_lib, 'IsInsideChroot', return_value=True)
Alex Klein2bfacb22019-02-04 11:42:17 -0700105 self.router.Route('chromite.api.InsideChrootApiService',
Alex Klein5bcb4d22019-03-21 13:51:54 -0600106 'InsideServiceInsideMethod', self.input_file,
Alex Klein69339cc2019-07-22 14:08:35 -0600107 self.output_file, self.api_config)
Alex Klein2bfacb22019-02-04 11:42:17 -0700108
Alex Klein00aa8072019-04-15 16:36:00 -0600109 def testInsideServiceOutsideMethodOutsideChroot(self):
110 """Test the outside method override works as expected."""
111 self.PatchObject(self.router, '_GetMethod',
112 return_value=self._mock_callable(expect_called=True))
113 self.PatchObject(cros_build_lib, 'IsInsideChroot', return_value=False)
114 self.router.Route('chromite.api.InsideChrootApiService',
115 'InsideServiceOutsideMethod', self.input_file,
Alex Klein69339cc2019-07-22 14:08:35 -0600116 self.output_file, self.api_config)
Alex Klein00aa8072019-04-15 16:36:00 -0600117
118 def testInsideServiceInsideMethodOutsideChroot(self):
119 """Test calling an inside method from outside the chroot."""
120 self.PatchObject(self.router, '_GetMethod',
121 return_value=self._mock_callable(expect_called=False))
122 self.PatchObject(cros_build_lib, 'IsInsideChroot', return_value=False)
Alex Kleinbd6edf82019-07-18 10:30:49 -0600123 self.rc.SetDefaultCmdResult(side_effect=self._writeChrootCallOutput())
Alex Klein00aa8072019-04-15 16:36:00 -0600124
125 service = 'chromite.api.InsideChrootApiService'
126 method = 'InsideServiceInsideMethod'
127 service_method = '%s/%s' % (service, method)
Alex Klein69339cc2019-07-22 14:08:35 -0600128 self.router.Route(service, method, self.input_file, self.output_file,
129 self.api_config)
Alex Klein00aa8072019-04-15 16:36:00 -0600130
Alex Kleinc05f3d12019-05-29 14:16:21 -0600131 self.assertCommandContains(['build_api', service_method], enter_chroot=True)
Alex Klein00aa8072019-04-15 16:36:00 -0600132
133 def testInsideServiceOutsideMethodInsideChroot(self):
134 """Test inside chroot for outside method raises an error."""
135 self.PatchObject(self.router, '_GetMethod',
136 return_value=self._mock_callable(expect_called=False))
137 self.PatchObject(cros_build_lib, 'IsInsideChroot', return_value=True)
Alex Klein2bfacb22019-02-04 11:42:17 -0700138 with self.assertRaises(cros_build_lib.DieSystemExit):
139 self.router.Route('chromite.api.InsideChrootApiService',
Alex Klein5bcb4d22019-03-21 13:51:54 -0600140 'InsideServiceOutsideMethod', self.input_file,
Alex Klein69339cc2019-07-22 14:08:35 -0600141 self.output_file, self.api_config)
Alex Klein2bfacb22019-02-04 11:42:17 -0700142
Alex Klein00aa8072019-04-15 16:36:00 -0600143 def testOutsideServiceOutsideMethodOutsideChroot(self):
144 """Test outside/outside/outside works correctly."""
145 self.PatchObject(self.router, '_GetMethod',
146 return_value=self._mock_callable(expect_called=True))
147 self.PatchObject(cros_build_lib, 'IsInsideChroot', return_value=False)
Alex Klein2bfacb22019-02-04 11:42:17 -0700148 self.router.Route('chromite.api.OutsideChrootApiService',
Alex Klein5bcb4d22019-03-21 13:51:54 -0600149 'OutsideServiceOutsideMethod', self.input_file,
Alex Klein69339cc2019-07-22 14:08:35 -0600150 self.output_file, self.api_config)
Alex Klein2bfacb22019-02-04 11:42:17 -0700151
Alex Klein00aa8072019-04-15 16:36:00 -0600152 def testOutsideServiceInsideMethodInsideChroot(self):
153 """Test the inside method assertion override works properly."""
154 self.PatchObject(self.router, '_GetMethod',
155 return_value=self._mock_callable(expect_called=True))
156 self.PatchObject(cros_build_lib, 'IsInsideChroot', return_value=True)
Alex Klein2bfacb22019-02-04 11:42:17 -0700157 self.router.Route('chromite.api.OutsideChrootApiService',
Alex Klein5bcb4d22019-03-21 13:51:54 -0600158 'OutsideServiceInsideMethod', self.input_file,
Alex Klein69339cc2019-07-22 14:08:35 -0600159 self.output_file, self.api_config)
Alex Klein00aa8072019-04-15 16:36:00 -0600160
161 def testOutsideServiceInsideMethodOutsideChroot(self):
162 """Test calling an inside override method from outside the chroot."""
163 self.PatchObject(self.router, '_GetMethod',
164 return_value=self._mock_callable(expect_called=False))
165 self.PatchObject(cros_build_lib, 'IsInsideChroot', return_value=False)
Alex Kleinbd6edf82019-07-18 10:30:49 -0600166 self.rc.SetDefaultCmdResult(side_effect=self._writeChrootCallOutput())
Alex Klein00aa8072019-04-15 16:36:00 -0600167
168 service = 'chromite.api.OutsideChrootApiService'
169 method = 'OutsideServiceInsideMethod'
170 service_method = '%s/%s' % (service, method)
Alex Klein69339cc2019-07-22 14:08:35 -0600171 self.router.Route(service, method, self.input_file, self.output_file,
172 self.api_config)
Alex Klein00aa8072019-04-15 16:36:00 -0600173
Alex Kleinc05f3d12019-05-29 14:16:21 -0600174 self.assertCommandContains(['build_api', service_method], enter_chroot=True)
Alex Kleinbd6edf82019-07-18 10:30:49 -0600175
176 def testReexecNonemptyOutput(self):
177 """Test calling an inside chroot method that produced output."""
178 osutils.WriteFile(self.output_file, '')
179 self.PatchObject(self.router, '_GetMethod',
180 return_value=self._mock_callable(expect_called=False))
181 self.PatchObject(cros_build_lib, 'IsInsideChroot', return_value=False)
Alex Kleinc7d647f2020-01-06 12:00:48 -0700182
183 # Patch the chroot tempdir method to return a tempdir with our subprocess
184 # tempdir so the output file will be in the expected location.
185 tempdir = osutils.TempDir()
186 original = tempdir.tempdir
187 tempdir.tempdir = self.subprocess_tempdir
188 self.PatchObject(chroot_lib.Chroot, 'tempdir', return_value=tempdir)
189
Alex Kleinbd6edf82019-07-18 10:30:49 -0600190 self.rc.SetDefaultCmdResult(
191 side_effect=self._writeChrootCallOutput(content='{"result": "foo"}'))
192
193 service = 'chromite.api.OutsideChrootApiService'
194 method = 'OutsideServiceInsideMethod'
195 service_method = '%s/%s' % (service, method)
Alex Klein69339cc2019-07-22 14:08:35 -0600196 self.router.Route(service, method, self.input_file, self.output_file,
197 self.api_config)
Alex Kleinbd6edf82019-07-18 10:30:49 -0600198
199 self.assertCommandContains(['build_api', service_method], enter_chroot=True)
200
201 # It should be writing the result out to our output file.
202 expected = build_api_test_pb2.TestResultMessage()
203 json_format.Parse('{"result": "foo"}', expected)
204 result = build_api_test_pb2.TestResultMessage()
205 json_format.Parse(osutils.ReadFile(self.output_file), result)
206 self.assertEqual(expected, result)
207
Alex Kleinc7d647f2020-01-06 12:00:48 -0700208 tempdir.tempdir = original
209 del tempdir
210
Alex Kleinbd6edf82019-07-18 10:30:49 -0600211 def testReexecEmptyOutput(self):
212 """Test calling an inside chroot method that produced no output."""
213 osutils.WriteFile(self.output_file, '')
214 self.PatchObject(self.router, '_GetMethod',
215 return_value=self._mock_callable(expect_called=False))
216 self.PatchObject(cros_build_lib, 'IsInsideChroot', return_value=False)
217 self.rc.SetDefaultCmdResult(returncode=1)
218
219 service = 'chromite.api.OutsideChrootApiService'
220 method = 'OutsideServiceInsideMethod'
221 service_method = '%s/%s' % (service, method)
Alex Klein69339cc2019-07-22 14:08:35 -0600222 self.router.Route(service, method, self.input_file, self.output_file,
223 self.api_config)
Alex Kleinbd6edf82019-07-18 10:30:49 -0600224
225 self.assertCommandContains(['build_api', service_method], enter_chroot=True)
226 # It should be writing the empty message out.
227 self.assertFileContents(self.output_file, '{}')
Alex Klein6431d3a2019-08-29 10:16:08 -0600228
229 def testInvalidService(self):
230 """Test invalid service call."""
231 service = 'chromite.api.DoesNotExist'
232 method = 'OutsideServiceInsideMethod'
233
234 with self.assertRaises(router.UnknownServiceError):
235 self.router.Route(service, method, self.input_file, self.output_file,
236 self.api_config)
237
238 def testInvalidMethod(self):
239 """Test invalid method call."""
240 service = 'chromite.api.OutsideChrootApiService'
241 method = 'DoesNotExist'
242
243 with self.assertRaises(router.UnknownMethodError):
244 self.router.Route(service, method, self.input_file, self.output_file,
245 self.api_config)