blob: 83751d60b0064dc6d8ad06f0a0a97b718ec457c4 [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
11
Alex Kleinbd6edf82019-07-18 10:30:49 -060012from google.protobuf import json_format
13
Alex Klein69339cc2019-07-22 14:08:35 -060014from chromite.api import api_config
Alex Klein146d4772019-06-20 13:48:25 -060015from chromite.api import router
Alex Klein7107bdd2019-03-14 17:14:31 -060016from chromite.api.gen.chromite.api import build_api_test_pb2
Alex Kleinc7d647f2020-01-06 12:00:48 -070017from chromite.lib import chroot_lib
Alex Klein2bfacb22019-02-04 11:42:17 -070018from chromite.lib import cros_build_lib
Alex Kleinf4dc4f52018-12-05 13:55:12 -070019from chromite.lib import cros_test_lib
Alex Klein5bcb4d22019-03-21 13:51:54 -060020from chromite.lib import osutils
Alex Kleinf4dc4f52018-12-05 13:55:12 -070021
22
Alex Klein69339cc2019-07-22 14:08:35 -060023class RouterTest(cros_test_lib.RunCommandTempDirTestCase,
24 api_config.ApiConfigMixin):
Alex Kleinf4dc4f52018-12-05 13:55:12 -070025 """Test Router functionality."""
Alex Klein00aa8072019-04-15 16:36:00 -060026 _INPUT_JSON_TEMPLATE = '{"id":"Input ID", "chroot":{"path": "%s"}}'
Alex Kleinf4dc4f52018-12-05 13:55:12 -070027
28 def setUp(self):
Alex Klein146d4772019-06-20 13:48:25 -060029 self.router = router.Router()
Alex Kleinf4dc4f52018-12-05 13:55:12 -070030 self.router.Register(build_api_test_pb2)
31
Alex Klein5bcb4d22019-03-21 13:51:54 -060032 self.input_file = os.path.join(self.tempdir, 'input.json')
33 self.output_file = os.path.join(self.tempdir, 'output.json')
34
Alex Klein00aa8072019-04-15 16:36:00 -060035 self.chroot_dir = os.path.join(self.tempdir, 'chroot')
36 chroot_tmp = os.path.join(self.chroot_dir, 'tmp')
37 # Make the tmp dir for the re-exec inside chroot input/output files.
38 osutils.SafeMakedirs(chroot_tmp)
39
40 osutils.WriteFile(self.input_file,
41 self._INPUT_JSON_TEMPLATE % self.chroot_dir)
Alex Kleinbd6edf82019-07-18 10:30:49 -060042 osutils.WriteFile(self.output_file, '{}')
43
44 self.subprocess_tempdir = os.path.join(self.chroot_dir, 'tempdir')
45 osutils.SafeMakedirs(self.subprocess_tempdir)
Alex Klein5bcb4d22019-03-21 13:51:54 -060046
Alex Kleinf4dc4f52018-12-05 13:55:12 -070047 def testInputOutputMethod(self):
48 """Test input/output handling."""
Alex Klein69339cc2019-07-22 14:08:35 -060049 def impl(input_msg, output_msg, config):
Alex Kleinf4dc4f52018-12-05 13:55:12 -070050 self.assertIsInstance(input_msg, build_api_test_pb2.TestRequestMessage)
51 self.assertIsInstance(output_msg, build_api_test_pb2.TestResultMessage)
Alex Klein69339cc2019-07-22 14:08:35 -060052 self.assertIsInstance(config, api_config.ApiConfig)
Alex Kleinf4dc4f52018-12-05 13:55:12 -070053
54 self.PatchObject(self.router, '_GetMethod', return_value=impl)
55
56 self.router.Route('chromite.api.TestApiService', 'InputOutputMethod',
Alex Klein69339cc2019-07-22 14:08:35 -060057 self.input_file, self.output_file, self.api_config)
Alex Kleinf4dc4f52018-12-05 13:55:12 -070058
Alex Klein2bfacb22019-02-04 11:42:17 -070059 def testRenameMethod(self):
60 """Test implementation name config."""
61 def _GetMethod(_, method_name):
62 self.assertEqual('CorrectName', method_name)
Alex Klein69339cc2019-07-22 14:08:35 -060063 return lambda x, y, z: None
Alex Klein2bfacb22019-02-04 11:42:17 -070064
65 self.PatchObject(self.router, '_GetMethod', side_effect=_GetMethod)
66
67 self.router.Route('chromite.api.TestApiService', 'RenamedMethod',
Alex Klein69339cc2019-07-22 14:08:35 -060068 self.input_file, self.output_file, self.api_config)
Alex Klein2bfacb22019-02-04 11:42:17 -070069
Alex Klein00aa8072019-04-15 16:36:00 -060070 def _mock_callable(self, expect_called):
71 """Helper to create the implementation mock to test chroot assertions.
72
73 Args:
74 expect_called (bool): Whether the implementation should be called.
75 When False, an assertion will fail if it is called.
76
77 Returns:
78 callable - The implementation.
79 """
Alex Klein69339cc2019-07-22 14:08:35 -060080 def impl(_input_msg, _output_msg, _config):
Alex Klein00aa8072019-04-15 16:36:00 -060081 self.assertTrue(expect_called,
Alex Klein2bfacb22019-02-04 11:42:17 -070082 'The implementation should not have been called.')
Alex Klein2bfacb22019-02-04 11:42:17 -070083
Alex Klein00aa8072019-04-15 16:36:00 -060084 return impl
Alex Klein2bfacb22019-02-04 11:42:17 -070085
Alex Kleinbd6edf82019-07-18 10:30:49 -060086 def _writeChrootCallOutput(self, content='{}'):
87 def impl(*_args, **_kwargs):
88 """Side effect for inside-chroot calls to the API."""
89 osutils.WriteFile(
90 os.path.join(self.subprocess_tempdir,
91 router.Router.REEXEC_OUTPUT_FILE),
92 content)
93
94 return impl
95
Alex Klein00aa8072019-04-15 16:36:00 -060096 def testInsideServiceInsideMethodInsideChroot(self):
97 """Test inside/inside/inside works correctly."""
98 self.PatchObject(self.router, '_GetMethod',
99 return_value=self._mock_callable(expect_called=True))
100 self.PatchObject(cros_build_lib, 'IsInsideChroot', return_value=True)
Alex Klein2bfacb22019-02-04 11:42:17 -0700101 self.router.Route('chromite.api.InsideChrootApiService',
Alex Klein5bcb4d22019-03-21 13:51:54 -0600102 'InsideServiceInsideMethod', self.input_file,
Alex Klein69339cc2019-07-22 14:08:35 -0600103 self.output_file, self.api_config)
Alex Klein2bfacb22019-02-04 11:42:17 -0700104
Alex Klein00aa8072019-04-15 16:36:00 -0600105 def testInsideServiceOutsideMethodOutsideChroot(self):
106 """Test the outside method override works as expected."""
107 self.PatchObject(self.router, '_GetMethod',
108 return_value=self._mock_callable(expect_called=True))
109 self.PatchObject(cros_build_lib, 'IsInsideChroot', return_value=False)
110 self.router.Route('chromite.api.InsideChrootApiService',
111 'InsideServiceOutsideMethod', self.input_file,
Alex Klein69339cc2019-07-22 14:08:35 -0600112 self.output_file, self.api_config)
Alex Klein00aa8072019-04-15 16:36:00 -0600113
114 def testInsideServiceInsideMethodOutsideChroot(self):
115 """Test calling an inside method from outside the chroot."""
116 self.PatchObject(self.router, '_GetMethod',
117 return_value=self._mock_callable(expect_called=False))
118 self.PatchObject(cros_build_lib, 'IsInsideChroot', return_value=False)
Alex Kleinbd6edf82019-07-18 10:30:49 -0600119 self.rc.SetDefaultCmdResult(side_effect=self._writeChrootCallOutput())
Alex Klein00aa8072019-04-15 16:36:00 -0600120
121 service = 'chromite.api.InsideChrootApiService'
122 method = 'InsideServiceInsideMethod'
123 service_method = '%s/%s' % (service, method)
Alex Klein69339cc2019-07-22 14:08:35 -0600124 self.router.Route(service, method, self.input_file, self.output_file,
125 self.api_config)
Alex Klein00aa8072019-04-15 16:36:00 -0600126
Alex Kleinc05f3d12019-05-29 14:16:21 -0600127 self.assertCommandContains(['build_api', service_method], enter_chroot=True)
Alex Klein00aa8072019-04-15 16:36:00 -0600128
129 def testInsideServiceOutsideMethodInsideChroot(self):
130 """Test inside chroot for outside method raises an error."""
131 self.PatchObject(self.router, '_GetMethod',
132 return_value=self._mock_callable(expect_called=False))
133 self.PatchObject(cros_build_lib, 'IsInsideChroot', return_value=True)
Alex Klein2bfacb22019-02-04 11:42:17 -0700134 with self.assertRaises(cros_build_lib.DieSystemExit):
135 self.router.Route('chromite.api.InsideChrootApiService',
Alex Klein5bcb4d22019-03-21 13:51:54 -0600136 'InsideServiceOutsideMethod', self.input_file,
Alex Klein69339cc2019-07-22 14:08:35 -0600137 self.output_file, self.api_config)
Alex Klein2bfacb22019-02-04 11:42:17 -0700138
Alex Klein00aa8072019-04-15 16:36:00 -0600139 def testOutsideServiceOutsideMethodOutsideChroot(self):
140 """Test outside/outside/outside works correctly."""
141 self.PatchObject(self.router, '_GetMethod',
142 return_value=self._mock_callable(expect_called=True))
143 self.PatchObject(cros_build_lib, 'IsInsideChroot', return_value=False)
Alex Klein2bfacb22019-02-04 11:42:17 -0700144 self.router.Route('chromite.api.OutsideChrootApiService',
Alex Klein5bcb4d22019-03-21 13:51:54 -0600145 'OutsideServiceOutsideMethod', self.input_file,
Alex Klein69339cc2019-07-22 14:08:35 -0600146 self.output_file, self.api_config)
Alex Klein2bfacb22019-02-04 11:42:17 -0700147
Alex Klein00aa8072019-04-15 16:36:00 -0600148 def testOutsideServiceInsideMethodInsideChroot(self):
149 """Test the inside method assertion override works properly."""
150 self.PatchObject(self.router, '_GetMethod',
151 return_value=self._mock_callable(expect_called=True))
152 self.PatchObject(cros_build_lib, 'IsInsideChroot', return_value=True)
Alex Klein2bfacb22019-02-04 11:42:17 -0700153 self.router.Route('chromite.api.OutsideChrootApiService',
Alex Klein5bcb4d22019-03-21 13:51:54 -0600154 'OutsideServiceInsideMethod', self.input_file,
Alex Klein69339cc2019-07-22 14:08:35 -0600155 self.output_file, self.api_config)
Alex Klein00aa8072019-04-15 16:36:00 -0600156
157 def testOutsideServiceInsideMethodOutsideChroot(self):
158 """Test calling an inside override method from outside the chroot."""
159 self.PatchObject(self.router, '_GetMethod',
160 return_value=self._mock_callable(expect_called=False))
161 self.PatchObject(cros_build_lib, 'IsInsideChroot', return_value=False)
Alex Kleinbd6edf82019-07-18 10:30:49 -0600162 self.rc.SetDefaultCmdResult(side_effect=self._writeChrootCallOutput())
Alex Klein00aa8072019-04-15 16:36:00 -0600163
164 service = 'chromite.api.OutsideChrootApiService'
165 method = 'OutsideServiceInsideMethod'
166 service_method = '%s/%s' % (service, method)
Alex Klein69339cc2019-07-22 14:08:35 -0600167 self.router.Route(service, method, self.input_file, self.output_file,
168 self.api_config)
Alex Klein00aa8072019-04-15 16:36:00 -0600169
Alex Kleinc05f3d12019-05-29 14:16:21 -0600170 self.assertCommandContains(['build_api', service_method], enter_chroot=True)
Alex Kleinbd6edf82019-07-18 10:30:49 -0600171
172 def testReexecNonemptyOutput(self):
173 """Test calling an inside chroot method that produced output."""
174 osutils.WriteFile(self.output_file, '')
175 self.PatchObject(self.router, '_GetMethod',
176 return_value=self._mock_callable(expect_called=False))
177 self.PatchObject(cros_build_lib, 'IsInsideChroot', return_value=False)
Alex Kleinc7d647f2020-01-06 12:00:48 -0700178
179 # Patch the chroot tempdir method to return a tempdir with our subprocess
180 # tempdir so the output file will be in the expected location.
181 tempdir = osutils.TempDir()
182 original = tempdir.tempdir
183 tempdir.tempdir = self.subprocess_tempdir
184 self.PatchObject(chroot_lib.Chroot, 'tempdir', return_value=tempdir)
185
Alex Kleinbd6edf82019-07-18 10:30:49 -0600186 self.rc.SetDefaultCmdResult(
187 side_effect=self._writeChrootCallOutput(content='{"result": "foo"}'))
188
189 service = 'chromite.api.OutsideChrootApiService'
190 method = 'OutsideServiceInsideMethod'
191 service_method = '%s/%s' % (service, method)
Alex Klein69339cc2019-07-22 14:08:35 -0600192 self.router.Route(service, method, self.input_file, self.output_file,
193 self.api_config)
Alex Kleinbd6edf82019-07-18 10:30:49 -0600194
195 self.assertCommandContains(['build_api', service_method], enter_chroot=True)
196
197 # It should be writing the result out to our output file.
198 expected = build_api_test_pb2.TestResultMessage()
199 json_format.Parse('{"result": "foo"}', expected)
200 result = build_api_test_pb2.TestResultMessage()
201 json_format.Parse(osutils.ReadFile(self.output_file), result)
202 self.assertEqual(expected, result)
203
Alex Kleinc7d647f2020-01-06 12:00:48 -0700204 tempdir.tempdir = original
205 del tempdir
206
Alex Kleinbd6edf82019-07-18 10:30:49 -0600207 def testReexecEmptyOutput(self):
208 """Test calling an inside chroot method that produced no output."""
209 osutils.WriteFile(self.output_file, '')
210 self.PatchObject(self.router, '_GetMethod',
211 return_value=self._mock_callable(expect_called=False))
212 self.PatchObject(cros_build_lib, 'IsInsideChroot', return_value=False)
213 self.rc.SetDefaultCmdResult(returncode=1)
214
215 service = 'chromite.api.OutsideChrootApiService'
216 method = 'OutsideServiceInsideMethod'
217 service_method = '%s/%s' % (service, method)
Alex Klein69339cc2019-07-22 14:08:35 -0600218 self.router.Route(service, method, self.input_file, self.output_file,
219 self.api_config)
Alex Kleinbd6edf82019-07-18 10:30:49 -0600220
221 self.assertCommandContains(['build_api', service_method], enter_chroot=True)
222 # It should be writing the empty message out.
223 self.assertFileContents(self.output_file, '{}')
Alex Klein6431d3a2019-08-29 10:16:08 -0600224
225 def testInvalidService(self):
226 """Test invalid service call."""
227 service = 'chromite.api.DoesNotExist'
228 method = 'OutsideServiceInsideMethod'
229
230 with self.assertRaises(router.UnknownServiceError):
231 self.router.Route(service, method, self.input_file, self.output_file,
232 self.api_config)
233
234 def testInvalidMethod(self):
235 """Test invalid method call."""
236 service = 'chromite.api.OutsideChrootApiService'
237 method = 'DoesNotExist'
238
239 with self.assertRaises(router.UnknownMethodError):
240 self.router.Route(service, method, self.input_file, self.output_file,
241 self.api_config)