Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / native_client_sdk / src / build_tools / tests / sdktools_commands_test.py
blob7dd9e6182cad719e859d7199fd30acb2e850779d
1 #!/usr/bin/env python
2 # Copyright (c) 2012 The Chromium 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.
6 import os
7 import sys
8 import re
9 import tarfile
10 import tempfile
11 import unittest
12 from sdktools_test import SdkToolsTestCase
14 SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
15 BUILD_TOOLS_DIR = os.path.dirname(SCRIPT_DIR)
16 TOOLS_DIR = os.path.join(os.path.dirname(BUILD_TOOLS_DIR), 'tools')
18 sys.path.extend([BUILD_TOOLS_DIR, TOOLS_DIR])
19 import manifest_util
20 import oshelpers
23 class TestCommands(SdkToolsTestCase):
24 def setUp(self):
25 self.SetupDefault()
27 def _AddDummyBundle(self, manifest, bundle_name):
28 bundle = manifest_util.Bundle(bundle_name)
29 bundle.revision = 1337
30 bundle.version = 23
31 bundle.description = bundle_name
32 bundle.stability = 'beta'
33 bundle.recommended = 'no'
34 bundle.repath = bundle_name
35 archive = self._MakeDummyArchive(bundle_name)
36 bundle.AddArchive(archive)
37 manifest.SetBundle(bundle)
39 # Need to get the bundle from the manifest -- it doesn't use the one we
40 # gave it.
41 return manifest.GetBundle(bundle_name)
43 def _MakeDummyArchive(self, bundle_name, tarname=None, filename='dummy.txt'):
44 tarname = (tarname or bundle_name) + '.tar.bz2'
45 temp_dir = tempfile.mkdtemp(prefix='archive')
46 try:
47 dummy_path = os.path.join(temp_dir, filename)
48 with open(dummy_path, 'w') as stream:
49 stream.write('Dummy stuff for %s' % bundle_name)
51 # Build the tarfile directly into the server's directory.
52 tar_path = os.path.join(self.basedir, tarname)
53 tarstream = tarfile.open(tar_path, 'w:bz2')
54 try:
55 tarstream.add(dummy_path, os.path.join(bundle_name, filename))
56 finally:
57 tarstream.close()
59 with open(tar_path, 'rb') as archive_stream:
60 sha1, size = manifest_util.DownloadAndComputeHash(archive_stream)
62 archive = manifest_util.Archive(manifest_util.GetHostOS())
63 archive.url = self.server.GetURL(os.path.basename(tar_path))
64 archive.size = size
65 archive.checksum = sha1
66 return archive
67 finally:
68 oshelpers.Remove(['-rf', temp_dir])
70 def testInfoBasic(self):
71 """The info command should display information about the given bundle."""
72 self._WriteManifest()
73 output = self._Run(['info', 'sdk_tools'])
74 # Make sure basic information is there
75 bundle = self.manifest.GetBundle('sdk_tools')
76 archive = bundle.GetHostOSArchive();
77 self.assertTrue(bundle.name in output)
78 self.assertTrue(bundle.description in output)
79 self.assertTrue(str(bundle.revision) in output)
80 self.assertTrue(str(archive.size) in output)
81 self.assertTrue(archive.checksum in output)
82 self.assertTrue(bundle.stability in output)
84 def testInfoUnknownBundle(self):
85 """The info command should notify the user of unknown bundles."""
86 self._WriteManifest()
87 bogus_bundle = 'foobar'
88 output = self._Run(['info', bogus_bundle])
89 self.assertTrue(re.search(r'[uU]nknown', output))
90 self.assertTrue(bogus_bundle in output)
92 def testInfoMultipleBundles(self):
93 """The info command should support listing multiple bundles."""
94 self._AddDummyBundle(self.manifest, 'pepper_23')
95 self._AddDummyBundle(self.manifest, 'pepper_24')
96 self._WriteManifest()
97 output = self._Run(['info', 'pepper_23', 'pepper_24'])
98 self.assertTrue('pepper_23' in output)
99 self.assertTrue('pepper_24' in output)
100 self.assertFalse(re.search(r'[uU]nknown', output))
102 def testInfoMultipleArchives(self):
103 """The info command should display multiple archives."""
104 bundle = self._AddDummyBundle(self.manifest, 'pepper_26')
105 archive2 = self._MakeDummyArchive('pepper_26', tarname='pepper_26_more',
106 filename='dummy2.txt')
107 archive2.host_os = 'all'
108 bundle.AddArchive(archive2)
109 self._WriteManifest()
110 output = self._Run(['info', 'pepper_26'])
111 self.assertTrue('pepper_26' in output)
112 self.assertTrue('pepper_26_more' in output)
114 def testListBasic(self):
115 """The list command should display basic information about remote
116 bundles."""
117 self._WriteManifest()
118 output = self._Run(['list'])
119 self.assertTrue(re.search('I.*?sdk_tools.*?stable', output, re.MULTILINE))
120 # This line is important (it's used by the updater to determine if the
121 # sdk_tools bundle needs to be updated), so let's be explicit.
122 self.assertTrue('All installed bundles are up-to-date.')
124 def testListMultiple(self):
125 """The list command should display multiple bundles."""
126 self._AddDummyBundle(self.manifest, 'pepper_23')
127 self._WriteManifest()
128 output = self._Run(['list'])
129 # Added pepper_23 to the remote manifest not the local manifest, so it
130 # shouldn't be installed.
131 self.assertTrue(re.search('^[^I]*pepper_23', output, re.MULTILINE))
132 self.assertTrue('sdk_tools' in output)
134 def testListWithRevision(self):
135 """The list command should display the revision, if desired."""
136 self._AddDummyBundle(self.manifest, 'pepper_23')
137 self._WriteManifest()
138 output = self._Run(['list', '-r'])
139 self.assertTrue(re.search('pepper_23.*?r1337', output))
141 def testListWithUpdatedRevision(self):
142 """The list command should display when there is an update available."""
143 p23bundle = self._AddDummyBundle(self.manifest, 'pepper_23')
144 self._WriteCacheManifest(self.manifest)
145 # Modify the remote manifest to have a newer revision.
146 p23bundle.revision += 1
147 self._WriteManifest()
148 output = self._Run(['list', '-r'])
149 # We should see a display like this: I* pepper_23 (r1337 -> r1338)
150 # The star indicates the bundle has an update.
151 self.assertTrue(re.search('I\*\s+pepper_23.*?r1337.*?r1338', output))
153 def testListLocalVersionNotOnRemote(self):
154 """The list command should tell the user if they have a bundle installed
155 that doesn't exist in the remote manifest."""
156 self._WriteManifest()
157 p23bundle = self._AddDummyBundle(self.manifest, 'pepper_23')
158 self._WriteCacheManifest(self.manifest)
159 output = self._Run(['list', '-r'])
160 message = 'Bundles installed locally that are not available remotely:'
161 message_loc = output.find(message)
162 self.assertNotEqual(message_loc, -1)
163 # Make sure pepper_23 is listed after the message above.
164 self.assertTrue('pepper_23' in output[message_loc:])
166 def testSources(self):
167 """The sources command should allow adding/listing/removing of sources.
168 When a source is added, it will provide an additional set of bundles."""
169 other_manifest = manifest_util.SDKManifest()
170 self._AddDummyBundle(other_manifest, 'naclmono_23')
171 with open(os.path.join(self.basedir, 'source.json'), 'w') as stream:
172 stream.write(other_manifest.GetDataAsString())
174 source_json_url = self.server.GetURL('source.json')
175 self._WriteManifest()
176 output = self._Run(['sources', '--list'])
177 self.assertTrue('No external sources installed.' in output)
178 output = self._Run(['sources', '--add', source_json_url])
179 output = self._Run(['sources', '--list'])
180 self.assertTrue(source_json_url in output)
182 # Should be able to get info about that bundle.
183 output = self._Run(['info', 'naclmono_23'])
184 self.assertTrue('Unknown bundle' not in output)
186 self._Run(['sources', '--remove', source_json_url])
187 output = self._Run(['sources', '--list'])
188 self.assertTrue('No external sources installed.' in output)
190 def testUpdateBasic(self):
191 """The update command should install the contents of a bundle to the SDK."""
192 self._AddDummyBundle(self.manifest, 'pepper_23')
193 self._WriteManifest()
194 self._Run(['update', 'pepper_23'])
195 self.assertTrue(os.path.exists(
196 os.path.join(self.basedir, 'nacl_sdk', 'pepper_23', 'dummy.txt')))
198 def testUpdateInCacheButDirectoryRemoved(self):
199 """The update command should update if the bundle directory does not exist,
200 even if the bundle is already in the cache manifest."""
201 self._AddDummyBundle(self.manifest, 'pepper_23')
202 self._WriteCacheManifest(self.manifest)
203 self._WriteManifest()
204 self._Run(['update', 'pepper_23'])
205 self.assertTrue(os.path.exists(
206 os.path.join(self.basedir, 'nacl_sdk', 'pepper_23', 'dummy.txt')))
208 def testUpdateNoNewVersion(self):
209 """The update command should do nothing if the bundle is already up-to-date.
211 self._AddDummyBundle(self.manifest, 'pepper_23')
212 self._WriteManifest()
213 self._Run(['update', 'pepper_23'])
214 output = self._Run(['update', 'pepper_23'])
215 self.assertTrue('is already up-to-date.' in output)
217 def testUpdateWithNewVersion(self):
218 """The update command should update to a new version if it exists."""
219 bundle = self._AddDummyBundle(self.manifest, 'pepper_23')
220 self._WriteManifest()
221 self._Run(['update', 'pepper_23'])
223 bundle.revision += 1
224 self._WriteManifest()
225 output = self._Run(['update', 'pepper_23'])
226 self.assertTrue('already exists, but has an update available' in output)
228 # Now update using --force.
229 output = self._Run(['update', 'pepper_23', '--force'])
230 self.assertTrue('Updating bundle' in output)
232 cache_manifest = self._ReadCacheManifest()
233 num_archives = len(cache_manifest.GetBundle('pepper_23').GetArchives())
234 self.assertEqual(num_archives, 1)
236 def testUpdateUnknownBundles(self):
237 """The update command should ignore unknown bundles and notify the user."""
238 self._WriteManifest()
239 output = self._Run(['update', 'foobar'])
240 self.assertTrue('unknown bundle' in output)
242 def testUpdateRecommended(self):
243 """The update command should update only recommended bundles when run
244 without args.
246 bundle_25 = self._AddDummyBundle(self.manifest, 'pepper_25')
247 bundle_25.recommended = 'no'
248 bundle_26 = self._AddDummyBundle(self.manifest, 'pepper_26')
249 bundle_26.recommended = 'yes'
251 self._WriteManifest()
252 output = self._Run(['update'])
254 # Should not try to update sdk_tools (even though it is recommended)
255 self.assertTrue('Ignoring manual update request.' not in output)
256 self.assertFalse(os.path.exists(
257 os.path.join(self.basedir, 'nacl_sdk', 'pepper_25')))
258 self.assertTrue(os.path.exists(
259 os.path.join(self.basedir, 'nacl_sdk', 'pepper_26', 'dummy.txt')))
261 def testUpdateCanary(self):
262 """The update command should create the correct directory name for repath'd
263 bundles.
265 bundle = self._AddDummyBundle(self.manifest, 'pepper_26')
266 bundle.name = 'pepper_canary'
267 self._WriteManifest()
268 output = self._Run(['update', 'pepper_canary'])
269 self.assertTrue(os.path.exists(
270 os.path.join(self.basedir, 'nacl_sdk', 'pepper_canary', 'dummy.txt')))
272 def testUpdateMultiArchive(self):
273 """The update command should include download/untar multiple archives
274 specified in the bundle.
276 bundle = self._AddDummyBundle(self.manifest, 'pepper_26')
277 archive2 = self._MakeDummyArchive('pepper_26', tarname='pepper_26_more',
278 filename='dummy2.txt')
279 archive2.host_os = 'all'
280 bundle.AddArchive(archive2)
281 self._WriteManifest()
282 output = self._Run(['update', 'pepper_26'])
283 self.assertTrue(os.path.exists(
284 os.path.join(self.basedir, 'nacl_sdk', 'pepper_26', 'dummy.txt')))
285 self.assertTrue(os.path.exists(
286 os.path.join(self.basedir, 'nacl_sdk', 'pepper_26', 'dummy2.txt')))
288 def testUpdateBadSize(self):
289 """If an archive has a bad size, print an error.
291 bundle = self._AddDummyBundle(self.manifest, 'pepper_26')
292 archive = bundle.GetHostOSArchive();
293 archive.size = -1
294 self._WriteManifest()
295 stdout = self._Run(['update', 'pepper_26'], expect_error=True)
296 self.assertTrue('Size mismatch' in stdout)
298 def testUpdateBadSHA(self):
299 """If an archive has a bad SHA, print an error.
301 bundle = self._AddDummyBundle(self.manifest, 'pepper_26')
302 archive = bundle.GetHostOSArchive();
303 archive.checksum = 0
304 self._WriteManifest()
305 stdout = self._Run(['update', 'pepper_26'], expect_error=True)
306 self.assertTrue('SHA1 checksum mismatch' in stdout)
308 def testUninstall(self):
309 """The uninstall command should remove the installed bundle, if it
310 exists.
312 # First install the bundle.
313 self._AddDummyBundle(self.manifest, 'pepper_23')
314 self._WriteManifest()
315 output = self._Run(['update', 'pepper_23'])
316 self.assertTrue(os.path.exists(
317 os.path.join(self.basedir, 'nacl_sdk', 'pepper_23', 'dummy.txt')))
319 # Now remove it.
320 self._Run(['uninstall', 'pepper_23'])
321 self.assertFalse(os.path.exists(
322 os.path.join(self.basedir, 'nacl_sdk', 'pepper_23')))
324 # The bundle should not be marked as installed.
325 output = self._Run(['list'])
326 self.assertTrue(re.search('^[^I]*pepper_23', output, re.MULTILINE))
328 def testReinstall(self):
329 """The reinstall command should remove, then install, the specified
330 bundles.
332 # First install the bundle.
333 self._AddDummyBundle(self.manifest, 'pepper_23')
334 self._WriteManifest()
335 output = self._Run(['update', 'pepper_23'])
336 dummy_txt = os.path.join(self.basedir, 'nacl_sdk', 'pepper_23', 'dummy.txt')
337 self.assertTrue(os.path.exists(dummy_txt))
338 with open(dummy_txt) as f:
339 self.assertEqual(f.read(), 'Dummy stuff for pepper_23')
341 # Change some files.
342 foo_txt = os.path.join(self.basedir, 'nacl_sdk', 'pepper_23', 'foo.txt')
343 with open(foo_txt, 'w') as f:
344 f.write('Another dummy file. This one is not part of the bundle.')
345 with open(dummy_txt, 'w') as f:
346 f.write('changed dummy.txt')
348 # Reinstall the bundle.
349 self._Run(['reinstall', 'pepper_23'])
351 self.assertFalse(os.path.exists(foo_txt))
352 self.assertTrue(os.path.exists(dummy_txt))
353 with open(dummy_txt) as f:
354 self.assertEqual(f.read(), 'Dummy stuff for pepper_23')
356 cache_manifest = self._ReadCacheManifest()
357 num_archives = len(cache_manifest.GetBundle('pepper_23').GetArchives())
358 self.assertEqual(num_archives, 1)
360 def testReinstallWithDuplicatedArchives(self):
361 """The reinstall command should only use the most recent archive if there
362 are duplicated archives.
364 NOTE: There was a bug where the sdk_cache/naclsdk_manifest2.json file was
365 duplicating archives from different revisions. Make sure that reinstall
366 ignores old archives in the bundle.
368 # First install the bundle.
369 self._AddDummyBundle(self.manifest, 'pepper_23')
370 self._WriteManifest()
371 self._Run(['update', 'pepper_23'])
373 manifest = self._ReadCacheManifest()
374 bundle = manifest.GetBundle('pepper_23')
375 self.assertEqual(len(bundle.GetArchives()), 1)
377 # Now add a bogus duplicate archive
378 archive2 = self._MakeDummyArchive('pepper_23', tarname='pepper_23',
379 filename='dummy2.txt')
380 bundle.AddArchive(archive2)
381 self._WriteCacheManifest(manifest)
383 output = self._Run(['reinstall', 'pepper_23'])
384 # When updating just one file, there is no (file 1/2 - "...") output.
385 self.assertFalse('file 1/' in output)
386 # Should be using the last archive.
387 self.assertFalse(os.path.exists(
388 os.path.join(self.basedir, 'nacl_sdk', 'pepper_23', 'dummy.txt')))
389 self.assertTrue(os.path.exists(
390 os.path.join(self.basedir, 'nacl_sdk', 'pepper_23', 'dummy2.txt')))
392 def testReinstallDoesntUpdate(self):
393 """The reinstall command should not update a bundle that has an update."""
394 # First install the bundle.
395 bundle = self._AddDummyBundle(self.manifest, 'pepper_23')
396 self._WriteManifest()
397 self._Run(['update', 'pepper_23'])
398 dummy_txt = os.path.join(self.basedir, 'nacl_sdk', 'pepper_23', 'dummy.txt')
399 self.assertTrue(os.path.exists(dummy_txt))
400 with open(dummy_txt) as f:
401 self.assertEqual(f.read(), 'Dummy stuff for pepper_23')
403 # Update the revision.
404 bundle.revision += 1
405 self._WriteManifest()
407 # Change the file.
408 foo_txt = os.path.join(self.basedir, 'nacl_sdk', 'pepper_23', 'foo.txt')
409 with open(dummy_txt, 'w') as f:
410 f.write('changed dummy.txt')
412 # Reinstall.
413 self._Run(['reinstall', 'pepper_23'])
415 # The data has been reinstalled.
416 self.assertTrue(os.path.exists(dummy_txt))
417 with open(dummy_txt) as f:
418 self.assertEqual(f.read(), 'Dummy stuff for pepper_23')
420 # ... but the version hasn't been updated.
421 output = self._Run(['list', '-r'])
422 self.assertTrue(re.search('I\*\s+pepper_23.*?r1337.*?r1338', output))
424 def testArchiveCacheBasic(self):
425 """Downloaded archives should be stored in the cache by default."""
426 self._AddDummyBundle(self.manifest, 'pepper_23')
427 self._WriteManifest()
428 self._Run(['update', 'pepper_23'])
429 archive_cache = os.path.join(self.cache_dir, 'archives')
430 cache_contents = os.listdir(archive_cache)
431 self.assertEqual(cache_contents, ['pepper_23'])
432 cache_contents = os.listdir(os.path.join(archive_cache, 'pepper_23'))
433 self.assertEqual(cache_contents, ['pepper_23.tar.bz2'])
435 def testArchiveCacheEviction(self):
436 archive_cache = os.path.join(self.cache_dir, 'archives')
437 self._AddDummyBundle(self.manifest, 'pepper_23')
438 self._AddDummyBundle(self.manifest, 'pepper_22')
439 self._WriteManifest()
441 # First install pepper_23
442 self._Run(['update', 'pepper_23'])
443 archive = os.path.join(archive_cache, 'pepper_23', 'pepper_23.tar.bz2')
444 archive_size = os.path.getsize(archive)
446 # Set the mtime on the pepper_23 bundle to be a few seconds in the past.
447 # This is needed so that the two bundles don't end up with the same
448 # timestamp which can happen on systems that don't report sub-second
449 # timestamps.
450 atime = os.path.getatime(archive)
451 mtime = os.path.getmtime(archive)
452 os.utime(archive, (atime, mtime-10))
454 # Set cache limit to size of pepper archive * 1.5
455 self._WriteConfig('{ "cache_max": %d }' % int(archive_size * 1.5))
457 # Now install pepper_22, which should cause pepper_23 to be evicted
458 self._Run(['update', 'pepper_22'])
459 cache_contents = os.listdir(archive_cache)
460 self.assertEqual(cache_contents, ['pepper_22'])
462 def testArchiveCacheZero(self):
463 """Archives should not be cached when cache_max is zero."""
464 self._AddDummyBundle(self.manifest, 'pepper_23')
465 self._WriteConfig('{ "cache_max": 0 }')
466 self._AddDummyBundle(self.manifest, 'pepper_23')
467 self._WriteManifest()
468 self._Run(['update', 'pepper_23'])
469 archive_cache = os.path.join(self.cache_dir, 'archives')
470 # Archive folder should be completely remove by cache cleanup
471 self.assertFalse(os.path.exists(archive_cache))
473 if __name__ == '__main__':
474 unittest.main()