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