blob: 742ed69a567f8a616f7d1076e7edf8356c0d54e9 [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 Klein2bfacb22019-02-04 11:42:17 -070017from chromite.lib import cros_build_lib
Alex Kleinf4dc4f52018-12-05 13:55:12 -070018from chromite.lib import cros_test_lib
Alex Klein5bcb4d22019-03-21 13:51:54 -060019from chromite.lib import osutils
Alex Kleinf4dc4f52018-12-05 13:55:12 -070020
21
Alex Klein69339cc2019-07-22 14:08:35 -060022class RouterTest(cros_test_lib.RunCommandTempDirTestCase,
23 api_config.ApiConfigMixin):
Alex Kleinf4dc4f52018-12-05 13:55:12 -070024 """Test Router functionality."""
Alex Klein00aa8072019-04-15 16:36:00 -060025 _INPUT_JSON_TEMPLATE = '{"id":"Input ID", "chroot":{"path": "%s"}}'
Alex Kleinf4dc4f52018-12-05 13:55:12 -070026
27 def setUp(self):
Alex Klein146d4772019-06-20 13:48:25 -060028 self.router = router.Router()
Alex Kleinf4dc4f52018-12-05 13:55:12 -070029 self.router.Register(build_api_test_pb2)
30
Alex Klein5bcb4d22019-03-21 13:51:54 -060031 self.input_file = os.path.join(self.tempdir, 'input.json')
32 self.output_file = os.path.join(self.tempdir, 'output.json')
33
Alex Klein00aa8072019-04-15 16:36:00 -060034 self.chroot_dir = os.path.join(self.tempdir, 'chroot')
35 chroot_tmp = os.path.join(self.chroot_dir, 'tmp')
36 # Make the tmp dir for the re-exec inside chroot input/output files.
37 osutils.SafeMakedirs(chroot_tmp)
38
39 osutils.WriteFile(self.input_file,
40 self._INPUT_JSON_TEMPLATE % self.chroot_dir)
Alex Kleinbd6edf82019-07-18 10:30:49 -060041 osutils.WriteFile(self.output_file, '{}')
42
43 self.subprocess_tempdir = os.path.join(self.chroot_dir, 'tempdir')
44 osutils.SafeMakedirs(self.subprocess_tempdir)
45 self.PatchObject(osutils.TempDir, '__enter__',
46 return_value=self.subprocess_tempdir)
Alex Klein5bcb4d22019-03-21 13:51:54 -060047
Alex Kleinf4dc4f52018-12-05 13:55:12 -070048 def testInputOutputMethod(self):
49 """Test input/output handling."""
Alex Klein69339cc2019-07-22 14:08:35 -060050 def impl(input_msg, output_msg, config):
Alex Kleinf4dc4f52018-12-05 13:55:12 -070051 self.assertIsInstance(input_msg, build_api_test_pb2.TestRequestMessage)
52 self.assertIsInstance(output_msg, build_api_test_pb2.TestResultMessage)
Alex Klein69339cc2019-07-22 14:08:35 -060053 self.assertIsInstance(config, api_config.ApiConfig)
Alex Kleinf4dc4f52018-12-05 13:55:12 -070054
55 self.PatchObject(self.router, '_GetMethod', return_value=impl)
56
57 self.router.Route('chromite.api.TestApiService', 'InputOutputMethod',
Alex Klein69339cc2019-07-22 14:08:35 -060058 self.input_file, self.output_file, self.api_config)
Alex Kleinf4dc4f52018-12-05 13:55:12 -070059
Alex Klein2bfacb22019-02-04 11:42:17 -070060 def testRenameMethod(self):
61 """Test implementation name config."""
62 def _GetMethod(_, method_name):
63 self.assertEqual('CorrectName', method_name)
Alex Klein69339cc2019-07-22 14:08:35 -060064 return lambda x, y, z: None
Alex Klein2bfacb22019-02-04 11:42:17 -070065
66 self.PatchObject(self.router, '_GetMethod', side_effect=_GetMethod)
67
68 self.router.Route('chromite.api.TestApiService', 'RenamedMethod',
Alex Klein69339cc2019-07-22 14:08:35 -060069 self.input_file, self.output_file, self.api_config)
Alex Klein2bfacb22019-02-04 11:42:17 -070070
Alex Klein00aa8072019-04-15 16:36:00 -060071 def _mock_callable(self, expect_called):
72 """Helper to create the implementation mock to test chroot assertions.
73
74 Args:
75 expect_called (bool): Whether the implementation should be called.
76 When False, an assertion will fail if it is called.
77
78 Returns:
79 callable - The implementation.
80 """
Alex Klein69339cc2019-07-22 14:08:35 -060081 def impl(_input_msg, _output_msg, _config):
Alex Klein00aa8072019-04-15 16:36:00 -060082 self.assertTrue(expect_called,
Alex Klein2bfacb22019-02-04 11:42:17 -070083 'The implementation should not have been called.')
Alex Klein2bfacb22019-02-04 11:42:17 -070084
Alex Klein00aa8072019-04-15 16:36:00 -060085 return impl
Alex Klein2bfacb22019-02-04 11:42:17 -070086
Alex Kleinbd6edf82019-07-18 10:30:49 -060087 def _writeChrootCallOutput(self, content='{}'):
88 def impl(*_args, **_kwargs):
89 """Side effect for inside-chroot calls to the API."""
90 osutils.WriteFile(
91 os.path.join(self.subprocess_tempdir,
92 router.Router.REEXEC_OUTPUT_FILE),
93 content)
94
95 return impl
96
Alex Klein00aa8072019-04-15 16:36:00 -060097 def testInsideServiceInsideMethodInsideChroot(self):
98 """Test inside/inside/inside works correctly."""
99 self.PatchObject(self.router, '_GetMethod',
100 return_value=self._mock_callable(expect_called=True))
101 self.PatchObject(cros_build_lib, 'IsInsideChroot', return_value=True)
Alex Klein2bfacb22019-02-04 11:42:17 -0700102 self.router.Route('chromite.api.InsideChrootApiService',
Alex Klein5bcb4d22019-03-21 13:51:54 -0600103 'InsideServiceInsideMethod', self.input_file,
Alex Klein69339cc2019-07-22 14:08:35 -0600104 self.output_file, self.api_config)
Alex Klein2bfacb22019-02-04 11:42:17 -0700105
Alex Klein00aa8072019-04-15 16:36:00 -0600106 def testInsideServiceOutsideMethodOutsideChroot(self):
107 """Test the outside method override works as expected."""
108 self.PatchObject(self.router, '_GetMethod',
109 return_value=self._mock_callable(expect_called=True))
110 self.PatchObject(cros_build_lib, 'IsInsideChroot', return_value=False)
111 self.router.Route('chromite.api.InsideChrootApiService',
112 'InsideServiceOutsideMethod', self.input_file,
Alex Klein69339cc2019-07-22 14:08:35 -0600113 self.output_file, self.api_config)
Alex Klein00aa8072019-04-15 16:36:00 -0600114
115 def testInsideServiceInsideMethodOutsideChroot(self):
116 """Test calling an inside method from outside the chroot."""
117 self.PatchObject(self.router, '_GetMethod',
118 return_value=self._mock_callable(expect_called=False))
119 self.PatchObject(cros_build_lib, 'IsInsideChroot', return_value=False)
Alex Kleinbd6edf82019-07-18 10:30:49 -0600120 self.rc.SetDefaultCmdResult(side_effect=self._writeChrootCallOutput())
Alex Klein00aa8072019-04-15 16:36:00 -0600121
122 service = 'chromite.api.InsideChrootApiService'
123 method = 'InsideServiceInsideMethod'
124 service_method = '%s/%s' % (service, method)
Alex Klein69339cc2019-07-22 14:08:35 -0600125 self.router.Route(service, method, self.input_file, self.output_file,
126 self.api_config)
Alex Klein00aa8072019-04-15 16:36:00 -0600127
Alex Kleinc05f3d12019-05-29 14:16:21 -0600128 self.assertCommandContains(['build_api', service_method], enter_chroot=True)
Alex Klein00aa8072019-04-15 16:36:00 -0600129
130 def testInsideServiceOutsideMethodInsideChroot(self):
131 """Test inside chroot for outside method raises an error."""
132 self.PatchObject(self.router, '_GetMethod',
133 return_value=self._mock_callable(expect_called=False))
134 self.PatchObject(cros_build_lib, 'IsInsideChroot', return_value=True)
Alex Klein2bfacb22019-02-04 11:42:17 -0700135 with self.assertRaises(cros_build_lib.DieSystemExit):
136 self.router.Route('chromite.api.InsideChrootApiService',
Alex Klein5bcb4d22019-03-21 13:51:54 -0600137 'InsideServiceOutsideMethod', self.input_file,
Alex Klein69339cc2019-07-22 14:08:35 -0600138 self.output_file, self.api_config)
Alex Klein2bfacb22019-02-04 11:42:17 -0700139
Alex Klein00aa8072019-04-15 16:36:00 -0600140 def testOutsideServiceOutsideMethodOutsideChroot(self):
141 """Test outside/outside/outside works correctly."""
142 self.PatchObject(self.router, '_GetMethod',
143 return_value=self._mock_callable(expect_called=True))
144 self.PatchObject(cros_build_lib, 'IsInsideChroot', return_value=False)
Alex Klein2bfacb22019-02-04 11:42:17 -0700145 self.router.Route('chromite.api.OutsideChrootApiService',
Alex Klein5bcb4d22019-03-21 13:51:54 -0600146 'OutsideServiceOutsideMethod', self.input_file,
Alex Klein69339cc2019-07-22 14:08:35 -0600147 self.output_file, self.api_config)
Alex Klein2bfacb22019-02-04 11:42:17 -0700148
Alex Klein00aa8072019-04-15 16:36:00 -0600149 def testOutsideServiceInsideMethodInsideChroot(self):
150 """Test the inside method assertion override works properly."""
151 self.PatchObject(self.router, '_GetMethod',
152 return_value=self._mock_callable(expect_called=True))
153 self.PatchObject(cros_build_lib, 'IsInsideChroot', return_value=True)
Alex Klein2bfacb22019-02-04 11:42:17 -0700154 self.router.Route('chromite.api.OutsideChrootApiService',
Alex Klein5bcb4d22019-03-21 13:51:54 -0600155 'OutsideServiceInsideMethod', self.input_file,
Alex Klein69339cc2019-07-22 14:08:35 -0600156 self.output_file, self.api_config)
Alex Klein00aa8072019-04-15 16:36:00 -0600157
158 def testOutsideServiceInsideMethodOutsideChroot(self):
159 """Test calling an inside override method from outside the chroot."""
160 self.PatchObject(self.router, '_GetMethod',
161 return_value=self._mock_callable(expect_called=False))
162 self.PatchObject(cros_build_lib, 'IsInsideChroot', return_value=False)
Alex Kleinbd6edf82019-07-18 10:30:49 -0600163 self.rc.SetDefaultCmdResult(side_effect=self._writeChrootCallOutput())
Alex Klein00aa8072019-04-15 16:36:00 -0600164
165 service = 'chromite.api.OutsideChrootApiService'
166 method = 'OutsideServiceInsideMethod'
167 service_method = '%s/%s' % (service, method)
Alex Klein69339cc2019-07-22 14:08:35 -0600168 self.router.Route(service, method, self.input_file, self.output_file,
169 self.api_config)
Alex Klein00aa8072019-04-15 16:36:00 -0600170
Alex Kleinc05f3d12019-05-29 14:16:21 -0600171 self.assertCommandContains(['build_api', service_method], enter_chroot=True)
Alex Kleinbd6edf82019-07-18 10:30:49 -0600172
173 def testReexecNonemptyOutput(self):
174 """Test calling an inside chroot method that produced output."""
175 osutils.WriteFile(self.output_file, '')
176 self.PatchObject(self.router, '_GetMethod',
177 return_value=self._mock_callable(expect_called=False))
178 self.PatchObject(cros_build_lib, 'IsInsideChroot', return_value=False)
179 self.rc.SetDefaultCmdResult(
180 side_effect=self._writeChrootCallOutput(content='{"result": "foo"}'))
181
182 service = 'chromite.api.OutsideChrootApiService'
183 method = 'OutsideServiceInsideMethod'
184 service_method = '%s/%s' % (service, method)
Alex Klein69339cc2019-07-22 14:08:35 -0600185 self.router.Route(service, method, self.input_file, self.output_file,
186 self.api_config)
Alex Kleinbd6edf82019-07-18 10:30:49 -0600187
188 self.assertCommandContains(['build_api', service_method], enter_chroot=True)
189
190 # It should be writing the result out to our output file.
191 expected = build_api_test_pb2.TestResultMessage()
192 json_format.Parse('{"result": "foo"}', expected)
193 result = build_api_test_pb2.TestResultMessage()
194 json_format.Parse(osutils.ReadFile(self.output_file), result)
195 self.assertEqual(expected, result)
196
197 def testReexecEmptyOutput(self):
198 """Test calling an inside chroot method that produced no output."""
199 osutils.WriteFile(self.output_file, '')
200 self.PatchObject(self.router, '_GetMethod',
201 return_value=self._mock_callable(expect_called=False))
202 self.PatchObject(cros_build_lib, 'IsInsideChroot', return_value=False)
203 self.rc.SetDefaultCmdResult(returncode=1)
204
205 service = 'chromite.api.OutsideChrootApiService'
206 method = 'OutsideServiceInsideMethod'
207 service_method = '%s/%s' % (service, method)
Alex Klein69339cc2019-07-22 14:08:35 -0600208 self.router.Route(service, method, self.input_file, self.output_file,
209 self.api_config)
Alex Kleinbd6edf82019-07-18 10:30:49 -0600210
211 self.assertCommandContains(['build_api', service_method], enter_chroot=True)
212 # It should be writing the empty message out.
213 self.assertFileContents(self.output_file, '{}')
Alex Klein6431d3a2019-08-29 10:16:08 -0600214
215 def testInvalidService(self):
216 """Test invalid service call."""
217 service = 'chromite.api.DoesNotExist'
218 method = 'OutsideServiceInsideMethod'
219
220 with self.assertRaises(router.UnknownServiceError):
221 self.router.Route(service, method, self.input_file, self.output_file,
222 self.api_config)
223
224 def testInvalidMethod(self):
225 """Test invalid method call."""
226 service = 'chromite.api.OutsideChrootApiService'
227 method = 'DoesNotExist'
228
229 with self.assertRaises(router.UnknownMethodError):
230 self.router.Route(service, method, self.input_file, self.output_file,
231 self.api_config)