blob: d0aad9d7ecfaea5717b7e114771f5e8c486af201 [file] [log] [blame]
Frank Farzan37761d12011-12-01 14:29:08 -08001#!/usr/bin/python
2#
3# Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
4# Use of this source code is governed by a BSD-style license that can be
5# found in the LICENSE file.
6
7"""Unit tests for devserver_util module."""
8
Chris Sosaea148d92012-03-06 16:22:04 -08009import mox
Frank Farzan37761d12011-12-01 14:29:08 -080010import os
11import shutil
Chris Sosaea148d92012-03-06 16:22:04 -080012import subprocess
Frank Farzan37761d12011-12-01 14:29:08 -080013import tempfile
14import unittest
15
16import devserver_util
17
18
19# Fake Dev Server Layout:
20TEST_LAYOUT = {
21 'test-board-1': ['R17-1413.0.0-a1-b1346', 'R17-18.0.0-a1-b1346'],
22 'test-board-2': ['R16-2241.0.0-a0-b2', 'R17-2.0.0-a1-b1346']
23}
24
25
Chris Sosaea148d92012-03-06 16:22:04 -080026class DevServerUtilTest(mox.MoxTestBase):
Frank Farzan37761d12011-12-01 14:29:08 -080027
28 def setUp(self):
Chris Sosaea148d92012-03-06 16:22:04 -080029 mox.MoxTestBase.setUp(self)
Frank Farzan37761d12011-12-01 14:29:08 -080030 self._static_dir = tempfile.mkdtemp()
31 self._outside_sandbox_dir = tempfile.mkdtemp()
32
33 for board, builds in TEST_LAYOUT.iteritems():
34 board_path = os.path.join(self._static_dir, board)
35 os.mkdir(board_path)
36 for build in builds:
37 build_path = os.path.join(board_path, build)
38 os.mkdir(build_path)
39 with open(os.path.join(build_path,
40 devserver_util.TEST_IMAGE), 'w') as f:
41 f.write('TEST_IMAGE')
42 with open(os.path.join(build_path,
43 devserver_util.STATEFUL_UPDATE), 'w') as f:
44 f.write('STATEFUL_UPDATE')
45 with open(os.path.join(build_path,
46 devserver_util.ROOT_UPDATE), 'w') as f:
47 f.write('ROOT_UPDATE')
48 # AU payloads.
49 au_dir = os.path.join(build_path, devserver_util.AU_BASE)
50 nton_dir = os.path.join(au_dir, build + devserver_util.NTON_DIR_SUFFIX)
51 os.makedirs(nton_dir)
52 with open(os.path.join(nton_dir,
53 devserver_util.ROOT_UPDATE), 'w') as f:
54 f.write('ROOT_UPDATE')
55 mton_dir = os.path.join(au_dir, build + devserver_util.MTON_DIR_SUFFIX)
56 os.makedirs(mton_dir)
57 with open(os.path.join(mton_dir,
58 devserver_util.ROOT_UPDATE), 'w') as f:
59 f.write('ROOT_UPDATE')
60
Chris Sosaea148d92012-03-06 16:22:04 -080061 self._good_mock_process = self.mox.CreateMock(subprocess.Popen)
62 self._good_mock_process.returncode = 0
63 self._bad_mock_process = self.mox.CreateMock(subprocess.Popen)
64 self._bad_mock_process.returncode = 1
65
Frank Farzan37761d12011-12-01 14:29:08 -080066 def tearDown(self):
67 shutil.rmtree(self._static_dir)
68 shutil.rmtree(self._outside_sandbox_dir)
69
70 def testParsePayloadList(self):
71 archive_url_prefix = ('gs://chromeos-image-archive/x86-mario-release/'
72 'R17-1413.0.0-a1-b1346/')
73 mton_url = (archive_url_prefix + 'chromeos_R17-1412.0.0-a1-b1345_'
74 'R17-1413.0.0-a1_x86-mario_delta_dev.bin')
75 nton_url = (archive_url_prefix + 'chromeos_R17-1413.0.0-a1_'
76 'R17-1413.0.0-a1_x86-mario_delta_dev.bin')
77 full_url = (archive_url_prefix + 'chromeos_R17-1413.0.0-a1_'
78 'x86-mario_full_dev.bin')
79 full_url_out, nton_url_out, mton_url_out = (
80 devserver_util.ParsePayloadList([full_url, nton_url, mton_url]))
81 self.assertEqual([full_url, nton_url, mton_url],
82 [full_url_out, nton_url_out, mton_url_out])
83
84 archive_url_prefix = ('gs://chromeos-image-archive/x86-alex_he-release/'
85 'R18-1420.0.0-a1-b541')
86 mton_url = (archive_url_prefix + 'chromeos_R18-1418.0.0-a1-b540_'
87 'R18-1420.0.0-a1_x86-alex_he_delta_dev.bin')
88 nton_url = (archive_url_prefix + 'chromeos_R18-1420.0.0-a1_'
89 'R18-1420.0.0-a1_x86-alex_he_delta_dev.bin')
90 full_url = (archive_url_prefix + 'chromeos_R18-1420.0.0-a1_'
91 'x86-alex_he_full_dev.bin')
92 full_url_out, nton_url_out, mton_url_out = (
93 devserver_util.ParsePayloadList([full_url, nton_url, mton_url]))
94 self.assertEqual([full_url, nton_url, mton_url],
95 [full_url_out, nton_url_out, mton_url_out])
96
Chris Sosaea148d92012-03-06 16:22:04 -080097 def _CallRunGS(self, output, str_should_contain, attempts=1):
98 """Helper that wraps a RunGS for tests."""
99 for attempt in range(attempts):
100 if attempt == devserver_util.GSUTIL_ATTEMPTS:
101 # We can't mock more than we can attempt.
102 return
103
104 # Return 1's for all but last attempt.
105 if attempt != attempts - 1:
106 mock_process = self._bad_mock_process
107 else:
108 mock_process = self._good_mock_process
109
110 subprocess.Popen(mox.StrContains(str_should_contain), shell=True,
111 stdout=subprocess.PIPE).AndReturn(mock_process)
112 mock_process.communicate().AndReturn((output, None))
113
Frank Farzan37761d12011-12-01 14:29:08 -0800114 def testDownloadBuildFromGS(self):
Chris Sosaea148d92012-03-06 16:22:04 -0800115 """Tests that we can run download build from gs with one error."""
116 build = 'R17-1413.0.0-a1-b1346'
117 archive_url_prefix = ('gs://chromeos-image-archive/x86-mario-release/' +
118 build)
119 mock_data = 'mock data\nmock_data\nmock_data'
120 payloads = ['p1', 'p2', 'p3']
121 self.mox.StubOutWithMock(subprocess, 'Popen', use_mock_anything=True)
122 self.mox.StubOutWithMock(devserver_util, 'ParsePayloadList')
123
124 # Make sure we our retry works.
125 self._CallRunGS(mock_data, archive_url_prefix, attempts=2)
126 devserver_util.ParsePayloadList(mock_data.splitlines()).AndReturn(
127 payloads)
128
129 for payload in payloads + [devserver_util.STATEFUL_UPDATE,
130 devserver_util.AUTOTEST_PACKAGE]:
131 self._CallRunGS(mock_data, payload,)
132
133 self.mox.ReplayAll()
134 devserver_util.DownloadBuildFromGS(self._static_dir, archive_url_prefix,
135 build)
136 self.mox.VerifyAll()
137
138 def testDownloadBuildFromGSButGSDown(self):
139 """Tests that we fail correctly if we can't reach GS."""
140 build = 'R17-1413.0.0-a1-b1346'
141 archive_url_prefix = ('gs://chromeos-image-archive/x86-mario-release/' +
142 build)
143 mock_data = 'mock data\nmock_data\nmock_data'
144 self.mox.StubOutWithMock(subprocess, 'Popen', use_mock_anything=True)
145
146 self._CallRunGS(mock_data, archive_url_prefix,
147 attempts=devserver_util.GSUTIL_ATTEMPTS + 1)
148
149 self.mox.ReplayAll()
150 self.assertRaises(
151 devserver_util.DevServerUtilError,
152 devserver_util.DownloadBuildFromGS,
153 self._static_dir, archive_url_prefix, build)
154 self.mox.VerifyAll()
Frank Farzan37761d12011-12-01 14:29:08 -0800155
156 def testInstallBuild(self):
Simon Glassf5019de2012-03-20 12:14:41 -0700157 # TODO(frankf): Implement this test
158 # self.fail('Not implemented.')
159 pass
Frank Farzan37761d12011-12-01 14:29:08 -0800160
161 def testPrepareAutotestPkgs(self):
Simon Glassf5019de2012-03-20 12:14:41 -0700162 # TODO(frankf): Implement this test
163 # self.fail('Not implemented.')
164 pass
Frank Farzan37761d12011-12-01 14:29:08 -0800165
166 def testSafeSandboxAccess(self):
167 # Path is in sandbox.
168 self.assertTrue(
169 devserver_util.SafeSandboxAccess(
170 self._static_dir, os.path.join(self._static_dir, 'some-board')))
171
172 # Path is sandbox.
173 self.assertFalse(
174 devserver_util.SafeSandboxAccess(self._static_dir, self._static_dir))
175
176 # Path is outside the sandbox.
177 self.assertFalse(
178 devserver_util.SafeSandboxAccess(self._static_dir,
179 self._outside_sandbox_dir))
180
181 # Path contains '..'.
182 self.assertFalse(
183 devserver_util.SafeSandboxAccess(
184 self._static_dir, os.path.join(self._static_dir, os.pardir)))
185
186 # Path contains symbolic link references.
187 os.chdir(self._static_dir)
188 os.symlink(os.pardir, 'parent')
189 self.assertFalse(
190 devserver_util.SafeSandboxAccess(
191 self._static_dir, os.path.join(self._static_dir, os.pardir)))
192
193 def testAcquireReleaseLocks(self):
194 # Successful lock and unlock.
195 lock_file = devserver_util.AcquireLock(self._static_dir, 'test-lock')
196 self.assertTrue(os.path.exists(lock_file))
197 devserver_util.ReleaseLock(self._static_dir, 'test-lock')
198 self.assertFalse(os.path.exists(lock_file))
199
200 # Attempt to lock an existing directory.
201 devserver_util.AcquireLock(self._static_dir, 'test-lock')
202 self.assertRaises(devserver_util.DevServerUtilError,
203 devserver_util.AcquireLock, self._static_dir, 'test-lock')
204
205 def testFindMatchingBoards(self):
206 for key in TEST_LAYOUT:
207 # Partial match with multiple boards.
208 self.assertEqual(
209 set(devserver_util.FindMatchingBoards(self._static_dir, key[:-5])),
210 set(TEST_LAYOUT.keys()))
211
212 # Absolute match.
213 self.assertEqual(
214 devserver_util.FindMatchingBoards(self._static_dir, key), [key])
215
216 # Invalid partial match.
217 self.assertEqual(
218 devserver_util.FindMatchingBoards(self._static_dir, 'asdfsadf'), [])
219
220 def testFindMatchingBuilds(self):
221 # Try a partial board and build match with single match.
222 self.assertEqual(
223 devserver_util.FindMatchingBuilds(self._static_dir, 'test-board',
224 'R17-1413'),
225 [('test-board-1', 'R17-1413.0.0-a1-b1346')])
226
227 # Try a partial board and build match with multiple match.
228 actual = set(devserver_util.FindMatchingBuilds(
229 self._static_dir, 'test-board', 'R17'))
230 expected = set([('test-board-1', 'R17-1413.0.0-a1-b1346'),
231 ('test-board-1', 'R17-18.0.0-a1-b1346'),
232 ('test-board-2', 'R17-2.0.0-a1-b1346')])
233 self.assertEqual(actual, expected)
234
235 def testGetLatestBuildVersion(self):
236 self.assertEqual(
237 devserver_util.GetLatestBuildVersion(self._static_dir, 'test-board-1'),
238 'R17-1413.0.0-a1-b1346')
239
240 def testFindBuild(self):
241 # Ensure no matching boards raises exception for latest.
242 self.assertRaises(
243 devserver_util.DevServerUtilError, devserver_util.FindBuild,
244 self._static_dir, 'aasdf', 'latest')
245
246 # Ensure no matching builds or boards raises an exception.
247 self.assertRaises(
248 devserver_util.DevServerUtilError, devserver_util.FindBuild,
249 self._static_dir, 'aasdf', 'asdgsadg')
250
251 # Ensure latest returns the proper build.
252 self.assertEqual(
253 devserver_util.FindBuild(self._static_dir, 'test-board-1', 'latest'),
254 ('test-board-1', 'R17-1413.0.0-a1-b1346'))
255
256 # Ensure specific board, build is returned.
257 self.assertEqual(
258 devserver_util.FindBuild(self._static_dir, 'test-board-1',
259 'R17-18.0.0-a1-b1346'),
260 ('test-board-1', 'R17-18.0.0-a1-b1346'))
261
262 # Ensure too many matches raises an exception.
263 self.assertRaises(
264 devserver_util.DevServerUtilError, devserver_util.FindBuild,
265 self._static_dir, 'test-board', 'R17')
266
267 def testCloneBuild(self):
268 test_prefix = 'abc'
269 test_tag = test_prefix + '/123'
270 abc_path = os.path.join(self._static_dir, devserver_util.DEV_BUILD_PREFIX,
271 test_tag)
272
273 os.mkdir(os.path.join(self._static_dir, test_prefix))
274
275 # Verify leaf path is created and proper values returned.
276 board, builds = TEST_LAYOUT.items()[0]
277 dev_build = devserver_util.CloneBuild(self._static_dir, board, builds[0],
278 test_tag)
279 self.assertEquals(dev_build, abc_path)
280 self.assertTrue(os.path.exists(abc_path))
281 self.assertTrue(os.path.isfile(os.path.join(
282 abc_path, devserver_util.TEST_IMAGE)))
283 self.assertTrue(os.path.isfile(os.path.join(
284 abc_path, devserver_util.ROOT_UPDATE)))
285 self.assertTrue(os.path.isfile(os.path.join(
286 abc_path, devserver_util.STATEFUL_UPDATE)))
287
288 # Verify force properly removes the old directory.
289 junk_path = os.path.join(dev_build, 'junk')
290 with open(junk_path, 'w') as f:
291 f.write('hello!')
292 remote_dir = devserver_util.CloneBuild(
293 self._static_dir, board, builds[0], test_tag, force=True)
294 self.assertEquals(remote_dir, abc_path)
295 self.assertTrue(os.path.exists(abc_path))
296 self.assertTrue(os.path.isfile(os.path.join(
297 abc_path, devserver_util.TEST_IMAGE)))
298 self.assertTrue(os.path.isfile(os.path.join(
299 abc_path, devserver_util.ROOT_UPDATE)))
300 self.assertTrue(os.path.isfile(os.path.join(
301 abc_path, devserver_util.STATEFUL_UPDATE)))
302 self.assertFalse(os.path.exists(junk_path))
303
304 def testGetControlFile(self):
305 control_file_dir = os.path.join(
Chris Sosaea148d92012-03-06 16:22:04 -0800306 self._static_dir, 'test-board-1', 'R17-1413.0.0-a1-b1346', 'autotest',
307 'server', 'site_tests', 'network_VPN')
Frank Farzan37761d12011-12-01 14:29:08 -0800308 os.makedirs(control_file_dir)
309 with open(os.path.join(control_file_dir, 'control'), 'w') as f:
310 f.write('hello!')
311
312 control_content = devserver_util.GetControlFile(
Chris Sosaea148d92012-03-06 16:22:04 -0800313 self._static_dir, 'test-board-1/R17-1413.0.0-a1-b1346',
Frank Farzan37761d12011-12-01 14:29:08 -0800314 os.path.join('server', 'site_tests', 'network_VPN', 'control'))
315 self.assertEqual(control_content, 'hello!')
316
317 def testListAutoupdateTargets(self):
318 for board, builds in TEST_LAYOUT.iteritems():
319 for build in builds:
320 au_targets = devserver_util.ListAutoupdateTargets(self._static_dir,
321 board, build)
322 self.assertEqual(set(au_targets),
323 set([build + devserver_util.NTON_DIR_SUFFIX,
324 build + devserver_util.MTON_DIR_SUFFIX]))
325
326
327if __name__ == '__main__':
328 unittest.main()