blob: 15b41ff52d9364a1fb740730fd0bebecb0208696 [file] [log] [blame]
Frank Farzan37761d12011-12-01 14:29:08 -08001#!/usr/bin/python
2#
Chris Sosa781ba6d2012-04-11 12:44:43 -07003# Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
Frank Farzan37761d12011-12-01 14:29:08 -08004# 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
Chris Sosa47a7d4e2012-03-28 11:26:55 -070017import downloadable_artifact
18import gsutil_util
Frank Farzan37761d12011-12-01 14:29:08 -080019
20
21# Fake Dev Server Layout:
22TEST_LAYOUT = {
23 'test-board-1': ['R17-1413.0.0-a1-b1346', 'R17-18.0.0-a1-b1346'],
Scott Zawalski16954532012-03-20 15:31:36 -040024 'test-board-2': ['R16-2241.0.0-a0-b2', 'R17-2.0.0-a1-b1346'],
25 'test-board-3': []
Frank Farzan37761d12011-12-01 14:29:08 -080026}
27
28
Chris Sosaea148d92012-03-06 16:22:04 -080029class DevServerUtilTest(mox.MoxTestBase):
Frank Farzan37761d12011-12-01 14:29:08 -080030
31 def setUp(self):
Chris Sosaea148d92012-03-06 16:22:04 -080032 mox.MoxTestBase.setUp(self)
Chris Sosa47a7d4e2012-03-28 11:26:55 -070033 self._static_dir = tempfile.mkdtemp('devserver_util_unittest')
34 self._outside_sandbox_dir = tempfile.mkdtemp('devserver_util_unittest')
35 self._install_dir = tempfile.mkdtemp('devserver_util_unittest')
Frank Farzan37761d12011-12-01 14:29:08 -080036
37 for board, builds in TEST_LAYOUT.iteritems():
38 board_path = os.path.join(self._static_dir, board)
39 os.mkdir(board_path)
40 for build in builds:
41 build_path = os.path.join(board_path, build)
42 os.mkdir(build_path)
43 with open(os.path.join(build_path,
Chris Sosa47a7d4e2012-03-28 11:26:55 -070044 downloadable_artifact.TEST_IMAGE), 'w') as f:
Frank Farzan37761d12011-12-01 14:29:08 -080045 f.write('TEST_IMAGE')
Chris Sosa47a7d4e2012-03-28 11:26:55 -070046 with open(os.path.join(
47 build_path, downloadable_artifact.STATEFUL_UPDATE), 'w') as f:
Frank Farzan37761d12011-12-01 14:29:08 -080048 f.write('STATEFUL_UPDATE')
49 with open(os.path.join(build_path,
Chris Sosa47a7d4e2012-03-28 11:26:55 -070050 downloadable_artifact.ROOT_UPDATE), 'w') as f:
Frank Farzan37761d12011-12-01 14:29:08 -080051 f.write('ROOT_UPDATE')
52 # AU payloads.
53 au_dir = os.path.join(build_path, devserver_util.AU_BASE)
54 nton_dir = os.path.join(au_dir, build + devserver_util.NTON_DIR_SUFFIX)
55 os.makedirs(nton_dir)
56 with open(os.path.join(nton_dir,
Chris Sosa47a7d4e2012-03-28 11:26:55 -070057 downloadable_artifact.ROOT_UPDATE), 'w') as f:
Frank Farzan37761d12011-12-01 14:29:08 -080058 f.write('ROOT_UPDATE')
59 mton_dir = os.path.join(au_dir, build + devserver_util.MTON_DIR_SUFFIX)
60 os.makedirs(mton_dir)
61 with open(os.path.join(mton_dir,
Chris Sosa47a7d4e2012-03-28 11:26:55 -070062 downloadable_artifact.ROOT_UPDATE), 'w') as f:
Frank Farzan37761d12011-12-01 14:29:08 -080063 f.write('ROOT_UPDATE')
64
Chris Sosaea148d92012-03-06 16:22:04 -080065 self._good_mock_process = self.mox.CreateMock(subprocess.Popen)
66 self._good_mock_process.returncode = 0
67 self._bad_mock_process = self.mox.CreateMock(subprocess.Popen)
68 self._bad_mock_process.returncode = 1
69
Frank Farzan37761d12011-12-01 14:29:08 -080070 def tearDown(self):
71 shutil.rmtree(self._static_dir)
72 shutil.rmtree(self._outside_sandbox_dir)
Chris Sosa47a7d4e2012-03-28 11:26:55 -070073 shutil.rmtree(self._install_dir)
Frank Farzan37761d12011-12-01 14:29:08 -080074
75 def testParsePayloadList(self):
76 archive_url_prefix = ('gs://chromeos-image-archive/x86-mario-release/'
77 'R17-1413.0.0-a1-b1346/')
78 mton_url = (archive_url_prefix + 'chromeos_R17-1412.0.0-a1-b1345_'
79 'R17-1413.0.0-a1_x86-mario_delta_dev.bin')
80 nton_url = (archive_url_prefix + 'chromeos_R17-1413.0.0-a1_'
81 'R17-1413.0.0-a1_x86-mario_delta_dev.bin')
82 full_url = (archive_url_prefix + 'chromeos_R17-1413.0.0-a1_'
83 'x86-mario_full_dev.bin')
84 full_url_out, nton_url_out, mton_url_out = (
85 devserver_util.ParsePayloadList([full_url, nton_url, mton_url]))
86 self.assertEqual([full_url, nton_url, mton_url],
87 [full_url_out, nton_url_out, mton_url_out])
88
89 archive_url_prefix = ('gs://chromeos-image-archive/x86-alex_he-release/'
90 'R18-1420.0.0-a1-b541')
91 mton_url = (archive_url_prefix + 'chromeos_R18-1418.0.0-a1-b540_'
92 'R18-1420.0.0-a1_x86-alex_he_delta_dev.bin')
93 nton_url = (archive_url_prefix + 'chromeos_R18-1420.0.0-a1_'
94 'R18-1420.0.0-a1_x86-alex_he_delta_dev.bin')
95 full_url = (archive_url_prefix + 'chromeos_R18-1420.0.0-a1_'
96 'x86-alex_he_full_dev.bin')
97 full_url_out, nton_url_out, mton_url_out = (
98 devserver_util.ParsePayloadList([full_url, nton_url, mton_url]))
99 self.assertEqual([full_url, nton_url, mton_url],
100 [full_url_out, nton_url_out, mton_url_out])
101
Chris Sosa781ba6d2012-04-11 12:44:43 -0700102 def testParsePartialPayloadList(self):
103 """Tests that we can parse a payload list with missing optional payload."""
104 archive_url_prefix = ('gs://chromeos-image-archive/x86-mario-release/'
105 'R17-1413.0.0-a1-b1346/')
106 nton_url = (archive_url_prefix + 'chromeos_R17-1413.0.0-a1_'
107 'R17-1413.0.0-a1_x86-mario_delta_dev.bin')
108 full_url = (archive_url_prefix + 'chromeos_R17-1413.0.0-a1_'
109 'x86-mario_full_dev.bin')
110 full_url_out, nton_url_out, mton_url_out = (
111 devserver_util.ParsePayloadList([full_url, nton_url]))
112 self.assertEqual([full_url, nton_url, None],
113 [full_url_out, nton_url_out, mton_url_out])
114
Frank Farzan37761d12011-12-01 14:29:08 -0800115 def testInstallBuild(self):
Simon Glassf5019de2012-03-20 12:14:41 -0700116 # TODO(frankf): Implement this test
117 # self.fail('Not implemented.')
118 pass
Frank Farzan37761d12011-12-01 14:29:08 -0800119
120 def testPrepareAutotestPkgs(self):
Simon Glassf5019de2012-03-20 12:14:41 -0700121 # TODO(frankf): Implement this test
122 # self.fail('Not implemented.')
Scott Zawalski16954532012-03-20 15:31:36 -0400123 # TODO: implement
Simon Glassf5019de2012-03-20 12:14:41 -0700124 pass
Frank Farzan37761d12011-12-01 14:29:08 -0800125
126 def testSafeSandboxAccess(self):
127 # Path is in sandbox.
128 self.assertTrue(
129 devserver_util.SafeSandboxAccess(
130 self._static_dir, os.path.join(self._static_dir, 'some-board')))
131
132 # Path is sandbox.
133 self.assertFalse(
134 devserver_util.SafeSandboxAccess(self._static_dir, self._static_dir))
135
136 # Path is outside the sandbox.
137 self.assertFalse(
138 devserver_util.SafeSandboxAccess(self._static_dir,
139 self._outside_sandbox_dir))
140
141 # Path contains '..'.
142 self.assertFalse(
143 devserver_util.SafeSandboxAccess(
144 self._static_dir, os.path.join(self._static_dir, os.pardir)))
145
146 # Path contains symbolic link references.
147 os.chdir(self._static_dir)
148 os.symlink(os.pardir, 'parent')
149 self.assertFalse(
150 devserver_util.SafeSandboxAccess(
151 self._static_dir, os.path.join(self._static_dir, os.pardir)))
152
153 def testAcquireReleaseLocks(self):
154 # Successful lock and unlock.
155 lock_file = devserver_util.AcquireLock(self._static_dir, 'test-lock')
156 self.assertTrue(os.path.exists(lock_file))
157 devserver_util.ReleaseLock(self._static_dir, 'test-lock')
158 self.assertFalse(os.path.exists(lock_file))
159
160 # Attempt to lock an existing directory.
161 devserver_util.AcquireLock(self._static_dir, 'test-lock')
162 self.assertRaises(devserver_util.DevServerUtilError,
163 devserver_util.AcquireLock, self._static_dir, 'test-lock')
164
165 def testFindMatchingBoards(self):
166 for key in TEST_LAYOUT:
167 # Partial match with multiple boards.
168 self.assertEqual(
169 set(devserver_util.FindMatchingBoards(self._static_dir, key[:-5])),
170 set(TEST_LAYOUT.keys()))
171
172 # Absolute match.
173 self.assertEqual(
174 devserver_util.FindMatchingBoards(self._static_dir, key), [key])
175
176 # Invalid partial match.
177 self.assertEqual(
178 devserver_util.FindMatchingBoards(self._static_dir, 'asdfsadf'), [])
179
180 def testFindMatchingBuilds(self):
181 # Try a partial board and build match with single match.
182 self.assertEqual(
183 devserver_util.FindMatchingBuilds(self._static_dir, 'test-board',
184 'R17-1413'),
185 [('test-board-1', 'R17-1413.0.0-a1-b1346')])
186
187 # Try a partial board and build match with multiple match.
188 actual = set(devserver_util.FindMatchingBuilds(
189 self._static_dir, 'test-board', 'R17'))
190 expected = set([('test-board-1', 'R17-1413.0.0-a1-b1346'),
191 ('test-board-1', 'R17-18.0.0-a1-b1346'),
192 ('test-board-2', 'R17-2.0.0-a1-b1346')])
193 self.assertEqual(actual, expected)
194
195 def testGetLatestBuildVersion(self):
196 self.assertEqual(
197 devserver_util.GetLatestBuildVersion(self._static_dir, 'test-board-1'),
198 'R17-1413.0.0-a1-b1346')
199
Scott Zawalski16954532012-03-20 15:31:36 -0400200 def testGetLatestBuildVersionLatest(self):
201 """Test that we raise DevServerUtilError when a build dir is empty."""
202 self.assertRaises(devserver_util.DevServerUtilError,
203 devserver_util.GetLatestBuildVersion,
204 self._static_dir, 'test-board-3')
Frank Farzan37761d12011-12-01 14:29:08 -0800205
Scott Zawalski16954532012-03-20 15:31:36 -0400206 def testGetLatestBuildVersionUnknownBuild(self):
207 """Test that we raise DevServerUtilError when a build dir does not exist."""
208 self.assertRaises(devserver_util.DevServerUtilError,
209 devserver_util.GetLatestBuildVersion,
210 self._static_dir, 'bad-dir')
Frank Farzan37761d12011-12-01 14:29:08 -0800211
Scott Zawalski16954532012-03-20 15:31:36 -0400212 def testGetLatestBuildVersionMilestone(self):
213 """Test that we can get builds based on milestone."""
214 expected_build_str = 'R16-2241.0.0-a0-b2'
215 milestone = 'R16'
216 build_str = devserver_util.GetLatestBuildVersion(
217 self._static_dir, 'test-board-2', milestone)
218 self.assertEqual(expected_build_str, build_str)
Frank Farzan37761d12011-12-01 14:29:08 -0800219
220 def testCloneBuild(self):
221 test_prefix = 'abc'
222 test_tag = test_prefix + '/123'
223 abc_path = os.path.join(self._static_dir, devserver_util.DEV_BUILD_PREFIX,
224 test_tag)
225
226 os.mkdir(os.path.join(self._static_dir, test_prefix))
227
228 # Verify leaf path is created and proper values returned.
229 board, builds = TEST_LAYOUT.items()[0]
230 dev_build = devserver_util.CloneBuild(self._static_dir, board, builds[0],
231 test_tag)
232 self.assertEquals(dev_build, abc_path)
233 self.assertTrue(os.path.exists(abc_path))
234 self.assertTrue(os.path.isfile(os.path.join(
Chris Sosa47a7d4e2012-03-28 11:26:55 -0700235 abc_path, downloadable_artifact.TEST_IMAGE)))
Frank Farzan37761d12011-12-01 14:29:08 -0800236 self.assertTrue(os.path.isfile(os.path.join(
Chris Sosa47a7d4e2012-03-28 11:26:55 -0700237 abc_path, downloadable_artifact.ROOT_UPDATE)))
Frank Farzan37761d12011-12-01 14:29:08 -0800238 self.assertTrue(os.path.isfile(os.path.join(
Chris Sosa47a7d4e2012-03-28 11:26:55 -0700239 abc_path, downloadable_artifact.STATEFUL_UPDATE)))
Frank Farzan37761d12011-12-01 14:29:08 -0800240
241 # Verify force properly removes the old directory.
242 junk_path = os.path.join(dev_build, 'junk')
243 with open(junk_path, 'w') as f:
244 f.write('hello!')
245 remote_dir = devserver_util.CloneBuild(
246 self._static_dir, board, builds[0], test_tag, force=True)
247 self.assertEquals(remote_dir, abc_path)
248 self.assertTrue(os.path.exists(abc_path))
249 self.assertTrue(os.path.isfile(os.path.join(
Chris Sosa47a7d4e2012-03-28 11:26:55 -0700250 abc_path, downloadable_artifact.TEST_IMAGE)))
Frank Farzan37761d12011-12-01 14:29:08 -0800251 self.assertTrue(os.path.isfile(os.path.join(
Chris Sosa47a7d4e2012-03-28 11:26:55 -0700252 abc_path, downloadable_artifact.ROOT_UPDATE)))
Frank Farzan37761d12011-12-01 14:29:08 -0800253 self.assertTrue(os.path.isfile(os.path.join(
Chris Sosa47a7d4e2012-03-28 11:26:55 -0700254 abc_path, downloadable_artifact.STATEFUL_UPDATE)))
Frank Farzan37761d12011-12-01 14:29:08 -0800255 self.assertFalse(os.path.exists(junk_path))
256
257 def testGetControlFile(self):
258 control_file_dir = os.path.join(
Chris Sosaea148d92012-03-06 16:22:04 -0800259 self._static_dir, 'test-board-1', 'R17-1413.0.0-a1-b1346', 'autotest',
260 'server', 'site_tests', 'network_VPN')
Frank Farzan37761d12011-12-01 14:29:08 -0800261 os.makedirs(control_file_dir)
262 with open(os.path.join(control_file_dir, 'control'), 'w') as f:
263 f.write('hello!')
264
265 control_content = devserver_util.GetControlFile(
Chris Sosaea148d92012-03-06 16:22:04 -0800266 self._static_dir, 'test-board-1/R17-1413.0.0-a1-b1346',
Frank Farzan37761d12011-12-01 14:29:08 -0800267 os.path.join('server', 'site_tests', 'network_VPN', 'control'))
268 self.assertEqual(control_content, 'hello!')
269
270 def testListAutoupdateTargets(self):
271 for board, builds in TEST_LAYOUT.iteritems():
272 for build in builds:
273 au_targets = devserver_util.ListAutoupdateTargets(self._static_dir,
274 board, build)
275 self.assertEqual(set(au_targets),
276 set([build + devserver_util.NTON_DIR_SUFFIX,
277 build + devserver_util.MTON_DIR_SUFFIX]))
278
Chris Sosa47a7d4e2012-03-28 11:26:55 -0700279 def testGatherArtifactDownloads(self):
280 """Tests that we can gather the correct download requirements."""
281 build = 'R17-1413.0.0-a1-b1346'
282 archive_url_prefix = ('gs://chromeos-image-archive/x86-mario-release/' +
283 build)
284 mock_data = 'mock data\nmock_data\nmock_data'
285 payloads = map(lambda x: '/'.join([archive_url_prefix, x]),
286 ['p1', 'p2', 'p3'])
287 expected_payloads = payloads + map(
288 lambda x: '/'.join([archive_url_prefix, x]),
Chris Masone3fe44db2012-05-02 10:50:21 -0700289 [downloadable_artifact.STATEFUL_UPDATE,
Chris Sosa47a7d4e2012-03-28 11:26:55 -0700290 downloadable_artifact.AUTOTEST_PACKAGE,
291 downloadable_artifact.TEST_SUITES_PACKAGE])
292 self.mox.StubOutWithMock(gsutil_util, 'GSUtilRun')
293 self.mox.StubOutWithMock(devserver_util, 'ParsePayloadList')
294
295 # GSUtil ls.
296 gsutil_util.GSUtilRun(mox.StrContains(archive_url_prefix),
297 mox.IgnoreArg()).AndReturn(mock_data)
298 devserver_util.ParsePayloadList(mock_data.splitlines()).AndReturn(payloads)
299
300 self.mox.ReplayAll()
301 artifacts = devserver_util.GatherArtifactDownloads(
Chris Masone3fe44db2012-05-02 10:50:21 -0700302 self._static_dir, archive_url_prefix, build, self._install_dir)
Chris Sosa47a7d4e2012-03-28 11:26:55 -0700303 for index, artifact in enumerate(artifacts):
304 self.assertEqual(artifact._gs_path, expected_payloads[index])
305 self.assertTrue(artifact._tmp_staging_dir.startswith(self._static_dir))
306 print 'Will Download Artifact: %s' % artifact
307
308 self.mox.VerifyAll()
309
Chris Sosa781ba6d2012-04-11 12:44:43 -0700310 def testGatherArtifactDownloadsWithoutMton(self):
311 """Gather the correct download requirements without mton delta."""
312 build = 'R17-1413.0.0-a1-b1346'
313 archive_url_prefix = ('gs://chromeos-image-archive/x86-mario-release/' +
314 build)
315 mock_data = 'mock data\nmock_data'
316 payloads = map(lambda x: '/'.join([archive_url_prefix, x]),
317 ['p1', 'p2'])
318 expected_payloads = payloads + map(
319 lambda x: '/'.join([archive_url_prefix, x]),
Chris Masone3fe44db2012-05-02 10:50:21 -0700320 [downloadable_artifact.STATEFUL_UPDATE,
Chris Sosa781ba6d2012-04-11 12:44:43 -0700321 downloadable_artifact.AUTOTEST_PACKAGE,
322 downloadable_artifact.TEST_SUITES_PACKAGE])
323 self.mox.StubOutWithMock(gsutil_util, 'GSUtilRun')
324 self.mox.StubOutWithMock(devserver_util, 'ParsePayloadList')
325
326 # GSUtil ls.
327 gsutil_util.GSUtilRun(mox.StrContains(archive_url_prefix),
328 mox.IgnoreArg()).AndReturn(mock_data)
329 devserver_util.ParsePayloadList(mock_data.splitlines()).AndReturn(
330 payloads + [None])
331
332 self.mox.ReplayAll()
333 artifacts = devserver_util.GatherArtifactDownloads(
Chris Masone3fe44db2012-05-02 10:50:21 -0700334 self._static_dir, archive_url_prefix, build, self._install_dir)
Chris Sosa781ba6d2012-04-11 12:44:43 -0700335 for index, artifact in enumerate(artifacts):
336 self.assertEqual(artifact._gs_path, expected_payloads[index])
337 self.assertTrue(artifact._tmp_staging_dir.startswith(self._static_dir))
338 print 'Will Download Artifact: %s' % artifact
339
340 self.mox.VerifyAll()
341
Frank Farzan37761d12011-12-01 14:29:08 -0800342
343if __name__ == '__main__':
344 unittest.main()