blob: 42e8f86e10a4468dadad7a97aa3c0ad5170827ee [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'],
Scott Zawalski16954532012-03-20 15:31:36 -040022 'test-board-2': ['R16-2241.0.0-a0-b2', 'R17-2.0.0-a1-b1346'],
23 'test-board-3': []
Frank Farzan37761d12011-12-01 14:29:08 -080024}
25
26
Chris Sosaea148d92012-03-06 16:22:04 -080027class DevServerUtilTest(mox.MoxTestBase):
Frank Farzan37761d12011-12-01 14:29:08 -080028
29 def setUp(self):
Chris Sosaea148d92012-03-06 16:22:04 -080030 mox.MoxTestBase.setUp(self)
Frank Farzan37761d12011-12-01 14:29:08 -080031 self._static_dir = tempfile.mkdtemp()
32 self._outside_sandbox_dir = tempfile.mkdtemp()
33
34 for board, builds in TEST_LAYOUT.iteritems():
35 board_path = os.path.join(self._static_dir, board)
36 os.mkdir(board_path)
37 for build in builds:
38 build_path = os.path.join(board_path, build)
39 os.mkdir(build_path)
40 with open(os.path.join(build_path,
41 devserver_util.TEST_IMAGE), 'w') as f:
42 f.write('TEST_IMAGE')
43 with open(os.path.join(build_path,
44 devserver_util.STATEFUL_UPDATE), 'w') as f:
45 f.write('STATEFUL_UPDATE')
46 with open(os.path.join(build_path,
47 devserver_util.ROOT_UPDATE), 'w') as f:
48 f.write('ROOT_UPDATE')
49 # AU payloads.
50 au_dir = os.path.join(build_path, devserver_util.AU_BASE)
51 nton_dir = os.path.join(au_dir, build + devserver_util.NTON_DIR_SUFFIX)
52 os.makedirs(nton_dir)
53 with open(os.path.join(nton_dir,
54 devserver_util.ROOT_UPDATE), 'w') as f:
55 f.write('ROOT_UPDATE')
56 mton_dir = os.path.join(au_dir, build + devserver_util.MTON_DIR_SUFFIX)
57 os.makedirs(mton_dir)
58 with open(os.path.join(mton_dir,
59 devserver_util.ROOT_UPDATE), 'w') as f:
60 f.write('ROOT_UPDATE')
61
Chris Sosaea148d92012-03-06 16:22:04 -080062 self._good_mock_process = self.mox.CreateMock(subprocess.Popen)
63 self._good_mock_process.returncode = 0
64 self._bad_mock_process = self.mox.CreateMock(subprocess.Popen)
65 self._bad_mock_process.returncode = 1
66
Frank Farzan37761d12011-12-01 14:29:08 -080067 def tearDown(self):
68 shutil.rmtree(self._static_dir)
69 shutil.rmtree(self._outside_sandbox_dir)
70
71 def testParsePayloadList(self):
72 archive_url_prefix = ('gs://chromeos-image-archive/x86-mario-release/'
73 'R17-1413.0.0-a1-b1346/')
74 mton_url = (archive_url_prefix + 'chromeos_R17-1412.0.0-a1-b1345_'
75 'R17-1413.0.0-a1_x86-mario_delta_dev.bin')
76 nton_url = (archive_url_prefix + 'chromeos_R17-1413.0.0-a1_'
77 'R17-1413.0.0-a1_x86-mario_delta_dev.bin')
78 full_url = (archive_url_prefix + 'chromeos_R17-1413.0.0-a1_'
79 'x86-mario_full_dev.bin')
80 full_url_out, nton_url_out, mton_url_out = (
81 devserver_util.ParsePayloadList([full_url, nton_url, mton_url]))
82 self.assertEqual([full_url, nton_url, mton_url],
83 [full_url_out, nton_url_out, mton_url_out])
84
85 archive_url_prefix = ('gs://chromeos-image-archive/x86-alex_he-release/'
86 'R18-1420.0.0-a1-b541')
87 mton_url = (archive_url_prefix + 'chromeos_R18-1418.0.0-a1-b540_'
88 'R18-1420.0.0-a1_x86-alex_he_delta_dev.bin')
89 nton_url = (archive_url_prefix + 'chromeos_R18-1420.0.0-a1_'
90 'R18-1420.0.0-a1_x86-alex_he_delta_dev.bin')
91 full_url = (archive_url_prefix + 'chromeos_R18-1420.0.0-a1_'
92 'x86-alex_he_full_dev.bin')
93 full_url_out, nton_url_out, mton_url_out = (
94 devserver_util.ParsePayloadList([full_url, nton_url, mton_url]))
95 self.assertEqual([full_url, nton_url, mton_url],
96 [full_url_out, nton_url_out, mton_url_out])
97
Chris Sosaea148d92012-03-06 16:22:04 -080098 def _CallRunGS(self, output, str_should_contain, attempts=1):
99 """Helper that wraps a RunGS for tests."""
100 for attempt in range(attempts):
101 if attempt == devserver_util.GSUTIL_ATTEMPTS:
102 # We can't mock more than we can attempt.
103 return
104
105 # Return 1's for all but last attempt.
106 if attempt != attempts - 1:
107 mock_process = self._bad_mock_process
108 else:
109 mock_process = self._good_mock_process
110
111 subprocess.Popen(mox.StrContains(str_should_contain), shell=True,
112 stdout=subprocess.PIPE).AndReturn(mock_process)
113 mock_process.communicate().AndReturn((output, None))
114
Frank Farzan37761d12011-12-01 14:29:08 -0800115 def testDownloadBuildFromGS(self):
Chris Sosaea148d92012-03-06 16:22:04 -0800116 """Tests that we can run download build from gs with one error."""
117 build = 'R17-1413.0.0-a1-b1346'
118 archive_url_prefix = ('gs://chromeos-image-archive/x86-mario-release/' +
119 build)
120 mock_data = 'mock data\nmock_data\nmock_data'
121 payloads = ['p1', 'p2', 'p3']
122 self.mox.StubOutWithMock(subprocess, 'Popen', use_mock_anything=True)
123 self.mox.StubOutWithMock(devserver_util, 'ParsePayloadList')
124
125 # Make sure we our retry works.
126 self._CallRunGS(mock_data, archive_url_prefix, attempts=2)
127 devserver_util.ParsePayloadList(mock_data.splitlines()).AndReturn(
128 payloads)
129
130 for payload in payloads + [devserver_util.STATEFUL_UPDATE,
131 devserver_util.AUTOTEST_PACKAGE]:
132 self._CallRunGS(mock_data, payload,)
133
134 self.mox.ReplayAll()
135 devserver_util.DownloadBuildFromGS(self._static_dir, archive_url_prefix,
136 build)
137 self.mox.VerifyAll()
138
139 def testDownloadBuildFromGSButGSDown(self):
140 """Tests that we fail correctly if we can't reach GS."""
141 build = 'R17-1413.0.0-a1-b1346'
142 archive_url_prefix = ('gs://chromeos-image-archive/x86-mario-release/' +
143 build)
144 mock_data = 'mock data\nmock_data\nmock_data'
145 self.mox.StubOutWithMock(subprocess, 'Popen', use_mock_anything=True)
146
147 self._CallRunGS(mock_data, archive_url_prefix,
148 attempts=devserver_util.GSUTIL_ATTEMPTS + 1)
149
150 self.mox.ReplayAll()
151 self.assertRaises(
152 devserver_util.DevServerUtilError,
153 devserver_util.DownloadBuildFromGS,
154 self._static_dir, archive_url_prefix, build)
155 self.mox.VerifyAll()
Frank Farzan37761d12011-12-01 14:29:08 -0800156
157 def testInstallBuild(self):
Simon Glassf5019de2012-03-20 12:14:41 -0700158 # TODO(frankf): Implement this test
159 # self.fail('Not implemented.')
160 pass
Frank Farzan37761d12011-12-01 14:29:08 -0800161
162 def testPrepareAutotestPkgs(self):
Simon Glassf5019de2012-03-20 12:14:41 -0700163 # TODO(frankf): Implement this test
164 # self.fail('Not implemented.')
Scott Zawalski16954532012-03-20 15:31:36 -0400165 # TODO: implement
Simon Glassf5019de2012-03-20 12:14:41 -0700166 pass
Frank Farzan37761d12011-12-01 14:29:08 -0800167
168 def testSafeSandboxAccess(self):
169 # Path is in sandbox.
170 self.assertTrue(
171 devserver_util.SafeSandboxAccess(
172 self._static_dir, os.path.join(self._static_dir, 'some-board')))
173
174 # Path is sandbox.
175 self.assertFalse(
176 devserver_util.SafeSandboxAccess(self._static_dir, self._static_dir))
177
178 # Path is outside the sandbox.
179 self.assertFalse(
180 devserver_util.SafeSandboxAccess(self._static_dir,
181 self._outside_sandbox_dir))
182
183 # Path contains '..'.
184 self.assertFalse(
185 devserver_util.SafeSandboxAccess(
186 self._static_dir, os.path.join(self._static_dir, os.pardir)))
187
188 # Path contains symbolic link references.
189 os.chdir(self._static_dir)
190 os.symlink(os.pardir, 'parent')
191 self.assertFalse(
192 devserver_util.SafeSandboxAccess(
193 self._static_dir, os.path.join(self._static_dir, os.pardir)))
194
195 def testAcquireReleaseLocks(self):
196 # Successful lock and unlock.
197 lock_file = devserver_util.AcquireLock(self._static_dir, 'test-lock')
198 self.assertTrue(os.path.exists(lock_file))
199 devserver_util.ReleaseLock(self._static_dir, 'test-lock')
200 self.assertFalse(os.path.exists(lock_file))
201
202 # Attempt to lock an existing directory.
203 devserver_util.AcquireLock(self._static_dir, 'test-lock')
204 self.assertRaises(devserver_util.DevServerUtilError,
205 devserver_util.AcquireLock, self._static_dir, 'test-lock')
206
207 def testFindMatchingBoards(self):
208 for key in TEST_LAYOUT:
209 # Partial match with multiple boards.
210 self.assertEqual(
211 set(devserver_util.FindMatchingBoards(self._static_dir, key[:-5])),
212 set(TEST_LAYOUT.keys()))
213
214 # Absolute match.
215 self.assertEqual(
216 devserver_util.FindMatchingBoards(self._static_dir, key), [key])
217
218 # Invalid partial match.
219 self.assertEqual(
220 devserver_util.FindMatchingBoards(self._static_dir, 'asdfsadf'), [])
221
222 def testFindMatchingBuilds(self):
223 # Try a partial board and build match with single match.
224 self.assertEqual(
225 devserver_util.FindMatchingBuilds(self._static_dir, 'test-board',
226 'R17-1413'),
227 [('test-board-1', 'R17-1413.0.0-a1-b1346')])
228
229 # Try a partial board and build match with multiple match.
230 actual = set(devserver_util.FindMatchingBuilds(
231 self._static_dir, 'test-board', 'R17'))
232 expected = set([('test-board-1', 'R17-1413.0.0-a1-b1346'),
233 ('test-board-1', 'R17-18.0.0-a1-b1346'),
234 ('test-board-2', 'R17-2.0.0-a1-b1346')])
235 self.assertEqual(actual, expected)
236
237 def testGetLatestBuildVersion(self):
238 self.assertEqual(
239 devserver_util.GetLatestBuildVersion(self._static_dir, 'test-board-1'),
240 'R17-1413.0.0-a1-b1346')
241
Scott Zawalski16954532012-03-20 15:31:36 -0400242 def testGetLatestBuildVersionLatest(self):
243 """Test that we raise DevServerUtilError when a build dir is empty."""
244 self.assertRaises(devserver_util.DevServerUtilError,
245 devserver_util.GetLatestBuildVersion,
246 self._static_dir, 'test-board-3')
Frank Farzan37761d12011-12-01 14:29:08 -0800247
Scott Zawalski16954532012-03-20 15:31:36 -0400248 def testGetLatestBuildVersionUnknownBuild(self):
249 """Test that we raise DevServerUtilError when a build dir does not exist."""
250 self.assertRaises(devserver_util.DevServerUtilError,
251 devserver_util.GetLatestBuildVersion,
252 self._static_dir, 'bad-dir')
Frank Farzan37761d12011-12-01 14:29:08 -0800253
Scott Zawalski16954532012-03-20 15:31:36 -0400254 def testGetLatestBuildVersionMilestone(self):
255 """Test that we can get builds based on milestone."""
256 expected_build_str = 'R16-2241.0.0-a0-b2'
257 milestone = 'R16'
258 build_str = devserver_util.GetLatestBuildVersion(
259 self._static_dir, 'test-board-2', milestone)
260 self.assertEqual(expected_build_str, build_str)
Frank Farzan37761d12011-12-01 14:29:08 -0800261
262 def testCloneBuild(self):
263 test_prefix = 'abc'
264 test_tag = test_prefix + '/123'
265 abc_path = os.path.join(self._static_dir, devserver_util.DEV_BUILD_PREFIX,
266 test_tag)
267
268 os.mkdir(os.path.join(self._static_dir, test_prefix))
269
270 # Verify leaf path is created and proper values returned.
271 board, builds = TEST_LAYOUT.items()[0]
272 dev_build = devserver_util.CloneBuild(self._static_dir, board, builds[0],
273 test_tag)
274 self.assertEquals(dev_build, abc_path)
275 self.assertTrue(os.path.exists(abc_path))
276 self.assertTrue(os.path.isfile(os.path.join(
277 abc_path, devserver_util.TEST_IMAGE)))
278 self.assertTrue(os.path.isfile(os.path.join(
279 abc_path, devserver_util.ROOT_UPDATE)))
280 self.assertTrue(os.path.isfile(os.path.join(
281 abc_path, devserver_util.STATEFUL_UPDATE)))
282
283 # Verify force properly removes the old directory.
284 junk_path = os.path.join(dev_build, 'junk')
285 with open(junk_path, 'w') as f:
286 f.write('hello!')
287 remote_dir = devserver_util.CloneBuild(
288 self._static_dir, board, builds[0], test_tag, force=True)
289 self.assertEquals(remote_dir, abc_path)
290 self.assertTrue(os.path.exists(abc_path))
291 self.assertTrue(os.path.isfile(os.path.join(
292 abc_path, devserver_util.TEST_IMAGE)))
293 self.assertTrue(os.path.isfile(os.path.join(
294 abc_path, devserver_util.ROOT_UPDATE)))
295 self.assertTrue(os.path.isfile(os.path.join(
296 abc_path, devserver_util.STATEFUL_UPDATE)))
297 self.assertFalse(os.path.exists(junk_path))
298
299 def testGetControlFile(self):
300 control_file_dir = os.path.join(
Chris Sosaea148d92012-03-06 16:22:04 -0800301 self._static_dir, 'test-board-1', 'R17-1413.0.0-a1-b1346', 'autotest',
302 'server', 'site_tests', 'network_VPN')
Frank Farzan37761d12011-12-01 14:29:08 -0800303 os.makedirs(control_file_dir)
304 with open(os.path.join(control_file_dir, 'control'), 'w') as f:
305 f.write('hello!')
306
307 control_content = devserver_util.GetControlFile(
Chris Sosaea148d92012-03-06 16:22:04 -0800308 self._static_dir, 'test-board-1/R17-1413.0.0-a1-b1346',
Frank Farzan37761d12011-12-01 14:29:08 -0800309 os.path.join('server', 'site_tests', 'network_VPN', 'control'))
310 self.assertEqual(control_content, 'hello!')
311
312 def testListAutoupdateTargets(self):
313 for board, builds in TEST_LAYOUT.iteritems():
314 for build in builds:
315 au_targets = devserver_util.ListAutoupdateTargets(self._static_dir,
316 board, build)
317 self.assertEqual(set(au_targets),
318 set([build + devserver_util.NTON_DIR_SUFFIX,
319 build + devserver_util.MTON_DIR_SUFFIX]))
320
321
322if __name__ == '__main__':
323 unittest.main()