sdktools_test.py revision c2e0dbddbe15c98d52c4786dac06cb8952a8ae6d
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. 5 6import os 7import re 8import subprocess 9import sys 10import tarfile 11import tempfile 12import test_server 13import unittest 14import zipfile 15 16SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__)) 17BUILD_TOOLS_DIR = os.path.dirname(SCRIPT_DIR) 18TOOLS_DIR = os.path.join(os.path.dirname(BUILD_TOOLS_DIR), 'tools') 19 20sys.path.extend([BUILD_TOOLS_DIR, TOOLS_DIR]) 21import build_utils 22import getos 23import manifest_util 24import oshelpers 25 26 27MANIFEST_BASENAME = 'naclsdk_manifest2.json' 28 29# Attribute '' defined outside __init__ 30# pylint: disable=W0201 31 32class SdkToolsTestCase(unittest.TestCase): 33 def tearDown(self): 34 if self.server: 35 self.server.Shutdown() 36 oshelpers.Remove(['-rf', self.basedir]) 37 38 def SetupDefault(self): 39 self.SetupWithBaseDirPrefix('sdktools') 40 41 def SetupWithBaseDirPrefix(self, basedir_prefix, tmpdir=None): 42 self.basedir = tempfile.mkdtemp(prefix=basedir_prefix, dir=tmpdir) 43 self.cache_dir = os.path.join(self.basedir, 'nacl_sdk', 'sdk_cache') 44 # We have to make sure that we build our updaters with a version that is at 45 # least as large as the version in the sdk_tools bundle. If not, update 46 # tests may fail because the "current" version (according to the sdk_cache) 47 # is greater than the version we are attempting to update to. 48 self.current_revision = self._GetSdkToolsBundleRevision() 49 self._BuildUpdater(self.basedir, self.current_revision) 50 self._LoadCacheManifest() 51 self.server = test_server.LocalHTTPServer(self.basedir) 52 53 def _GetSdkToolsBundleRevision(self): 54 """Get the sdk_tools bundle revision. 55 We get this from the checked-in path; this is the same file that 56 build_updater uses to specify the current revision of sdk_tools.""" 57 58 manifest_filename = os.path.join(BUILD_TOOLS_DIR, 'json', 59 'naclsdk_manifest0.json') 60 manifest = manifest_util.SDKManifest() 61 manifest.LoadDataFromString(open(manifest_filename, 'r').read()) 62 return manifest.GetBundle('sdk_tools').revision 63 64 def _LoadCacheManifest(self): 65 """Read the manifest from nacl_sdk/sdk_cache. 66 67 This manifest should only contain the sdk_tools bundle. 68 """ 69 manifest_filename = os.path.join(self.cache_dir, MANIFEST_BASENAME) 70 self.manifest = manifest_util.SDKManifest() 71 self.manifest.LoadDataFromString(open(manifest_filename).read()) 72 self.sdk_tools_bundle = self.manifest.GetBundle('sdk_tools') 73 74 def _WriteConfig(self, config_data): 75 config_filename = os.path.join(self.cache_dir, 'naclsdk_config.json') 76 with open(config_filename, 'w') as stream: 77 stream.write(config_data) 78 79 def _WriteCacheManifest(self, manifest): 80 """Write the manifest at nacl_sdk/sdk_cache. 81 82 This is useful for faking having installed a bundle. 83 """ 84 manifest_filename = os.path.join(self.cache_dir, MANIFEST_BASENAME) 85 with open(manifest_filename, 'w') as stream: 86 stream.write(manifest.GetDataAsString()) 87 88 def _WriteManifest(self): 89 with open(os.path.join(self.basedir, MANIFEST_BASENAME), 'w') as stream: 90 stream.write(self.manifest.GetDataAsString()) 91 92 def _BuildUpdater(self, out_dir, revision=None): 93 build_updater_py = os.path.join(BUILD_TOOLS_DIR, 'build_updater.py') 94 cmd = [sys.executable, build_updater_py, '-o', out_dir] 95 if revision: 96 cmd.extend(['-r', str(revision)]) 97 98 process = subprocess.Popen(cmd, stdout=subprocess.PIPE) 99 _, _ = process.communicate() 100 self.assertEqual(process.returncode, 0) 101 102 def _BuildUpdaterArchive(self, rel_path, revision): 103 """Build a new sdk_tools bundle. 104 105 Args: 106 rel_path: The relative path to build the updater. 107 revision: The revision number to give to this bundle. 108 Returns: 109 A manifest_util.Archive() that points to this new bundle on the local 110 server. 111 """ 112 self._BuildUpdater(os.path.join(self.basedir, rel_path), revision) 113 114 new_sdk_tools_tgz = os.path.join(self.basedir, rel_path, 'sdk_tools.tgz') 115 with open(new_sdk_tools_tgz, 'rb') as sdk_tools_stream: 116 archive_sha1, archive_size = manifest_util.DownloadAndComputeHash( 117 sdk_tools_stream) 118 119 archive = manifest_util.Archive('all') 120 archive.url = self.server.GetURL('%s/sdk_tools.tgz' % (rel_path,)) 121 archive.checksum = archive_sha1 122 archive.size = archive_size 123 return archive 124 125 def _Run(self, args): 126 naclsdk_shell_script = os.path.join(self.basedir, 'nacl_sdk', 'naclsdk') 127 if getos.GetPlatform() == 'win': 128 naclsdk_shell_script += '.bat' 129 cmd = [naclsdk_shell_script] 130 cmd.extend(args) 131 cmd.extend(['-U', self.server.GetURL(MANIFEST_BASENAME)]) 132 process = subprocess.Popen(cmd, stdout=subprocess.PIPE) 133 stdout, _ = process.communicate() 134 try: 135 self.assertEqual(process.returncode, 0) 136 except Exception: 137 print stdout 138 raise 139 return stdout 140 141 def _RunAndExtractRevision(self): 142 stdout = self._Run(['version']) 143 match = re.search('version r(\d+)', stdout) 144 self.assertTrue(match is not None) 145 return int(match.group(1)) 146 147 148class TestSdkTools(SdkToolsTestCase): 149 def testPathHasSpaces(self): 150 """Test that running naclsdk from a path with spaces works.""" 151 self.SetupWithBaseDirPrefix('sdk tools') 152 self._WriteManifest() 153 self._RunAndExtractRevision() 154 155 156class TestBuildUpdater(SdkToolsTestCase): 157 def setUp(self): 158 self.SetupDefault() 159 160 def testUpdaterPathsAreSane(self): 161 """Test that the paths to files in nacl_sdk.zip and sdktools.tgz are 162 relative to the output directory.""" 163 nacl_sdk_zip_path = os.path.join(self.basedir, 'nacl_sdk.zip') 164 zip_stream = zipfile.ZipFile(nacl_sdk_zip_path, 'r') 165 try: 166 self.assertTrue(all(name.startswith('nacl_sdk') 167 for name in zip_stream.namelist())) 168 finally: 169 zip_stream.close() 170 171 # sdktools.tgz has no built-in directories to look for. Instead, just look 172 # for some files that must be there. 173 sdktools_tgz_path = os.path.join(self.basedir, 'sdk_tools.tgz') 174 tar_stream = tarfile.open(sdktools_tgz_path, 'r:gz') 175 try: 176 names = [m.name for m in tar_stream.getmembers()] 177 self.assertTrue('LICENSE' in names) 178 self.assertTrue('sdk_update.py' in names) 179 finally: 180 tar_stream.close() 181 182 183class TestAutoUpdateSdkTools(SdkToolsTestCase): 184 def setUp(self): 185 self.SetupDefault() 186 187 def testNoUpdate(self): 188 """Test that running naclsdk with current revision does nothing.""" 189 self._WriteManifest() 190 revision = self._RunAndExtractRevision() 191 self.assertEqual(revision, self.current_revision) 192 193 def testUpdate(self): 194 """Test that running naclsdk with a new revision will auto-update.""" 195 new_revision = self.current_revision + 1 196 archive = self._BuildUpdaterArchive('new', new_revision) 197 self.sdk_tools_bundle.RemoveAllArchivesForHostOS(archive.host_os) 198 self.sdk_tools_bundle.AddArchive(archive) 199 self.sdk_tools_bundle.revision = new_revision 200 self._WriteManifest() 201 202 revision = self._RunAndExtractRevision() 203 self.assertEqual(revision, new_revision) 204 205 def testManualUpdateIsIgnored(self): 206 """Test that attempting to manually update sdk_tools is ignored. 207 208 If the sdk_tools bundle was updated normally (i.e. the old way), it would 209 leave a sdk_tools_update folder that would then be copied over on a 210 subsequent run. This test ensures that there is no folder made. 211 """ 212 new_revision = self.current_revision + 1 213 archive = self._BuildUpdaterArchive('new', new_revision) 214 self.sdk_tools_bundle.RemoveAllArchivesForHostOS(archive.host_os) 215 self.sdk_tools_bundle.AddArchive(archive) 216 self.sdk_tools_bundle.revision = new_revision 217 self._WriteManifest() 218 219 sdk_tools_update_dir = os.path.join(self.basedir, 'nacl_sdk', 220 'sdk_tools_update') 221 self.assertFalse(os.path.exists(sdk_tools_update_dir)) 222 stdout = self._Run(['update', 'sdk_tools']) 223 self.assertTrue(stdout.find('Ignoring manual update request.') != -1) 224 self.assertFalse(os.path.exists(sdk_tools_update_dir)) 225 226 def testHelpCommand(self): 227 """Running naclsdk with -h should work. 228 229 This is a regression test for a bug where the auto-updater would remove the 230 sdk_tools directory when running "naclsdk -h". 231 """ 232 self._WriteManifest() 233 self._Run(['-h']) 234 235 236class TestAutoUpdateSdkToolsDifferentFilesystem(TestAutoUpdateSdkTools): 237 def setUp(self): 238 # On Linux (on my machine at least), /tmp is a different filesystem than 239 # the current directory. os.rename fails when the source and destination 240 # are on different filesystems. Test that case here. 241 self.SetupWithBaseDirPrefix('sdktools', tmpdir='.') 242 243 244if __name__ == '__main__': 245 sys.exit(unittest.main()) 246