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 copy
      7 import datetime
      8 import hashlib
      9 import logging
     10 import os
     11 import posixpath
     12 import subprocess
     13 import sys
     14 import tempfile
     15 import unittest
     16 import urlparse
     17 
     18 SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
     19 BUILD_TOOLS_DIR = os.path.dirname(SCRIPT_DIR)
     20 
     21 sys.path.append(BUILD_TOOLS_DIR)
     22 import manifest_util
     23 import update_nacl_manifest
     24 from update_nacl_manifest import CANARY_BUNDLE_NAME, BIONIC_CANARY_BUNDLE_NAME
     25 
     26 
     27 HTTPS_BASE_URL = 'https://storage.googleapis.com' \
     28     '/nativeclient_mirror/nacl/nacl_sdk/'
     29 
     30 OS_CR = ('cros',)
     31 OS_L = ('linux',)
     32 OS_M = ('mac',)
     33 OS_ML = ('mac', 'linux')
     34 OS_MW = ('mac', 'win')
     35 OS_LW = ('linux', 'win')
     36 OS_MLW = ('mac', 'linux', 'win')
     37 OS_ALL = ('all',)
     38 POST_STABLE = 'post_stable'
     39 STABLE = 'stable'
     40 BETA = 'beta'
     41 DEV = 'dev'
     42 CANARY = 'canary'
     43 
     44 
     45 def GetArchiveURL(basename, version):
     46   return urlparse.urljoin(HTTPS_BASE_URL, posixpath.join(version, basename))
     47 
     48 
     49 def GetPlatformArchiveUrl(host_os, version):
     50   basename = 'naclsdk_%s.tar.bz2' % (host_os,)
     51   return GetArchiveURL(basename, version)
     52 
     53 
     54 def GetBionicArchiveUrl(version):
     55   basename = 'naclsdk_bionic.tar.bz2'
     56   return GetArchiveURL(basename, version)
     57 
     58 
     59 def MakeGsUrl(rel_path):
     60   return update_nacl_manifest.GS_BUCKET_PATH + rel_path
     61 
     62 
     63 def GetPathFromGsUrl(url):
     64   assert url.startswith(update_nacl_manifest.GS_BUCKET_PATH)
     65   return url[len(update_nacl_manifest.GS_BUCKET_PATH):]
     66 
     67 
     68 def GetPathFromHttpsUrl(url):
     69   assert url.startswith(HTTPS_BASE_URL)
     70   return url[len(HTTPS_BASE_URL):]
     71 
     72 
     73 def MakeArchive(url, host_os):
     74   archive = manifest_util.Archive(host_os)
     75   archive.url = url
     76   # dummy values that won't succeed if we ever use them, but will pass
     77   # validation. :)
     78   archive.checksum = {'sha1': 'foobar'}
     79   archive.size = 1
     80   return archive
     81 
     82 
     83 def MakePlatformArchive(host_os, version):
     84   return MakeArchive(GetPlatformArchiveUrl(host_os, version), host_os)
     85 
     86 
     87 def MakeBionicArchive(host_os, version):
     88   return MakeArchive(GetBionicArchiveUrl(version), host_os)
     89 
     90 
     91 def MakeNonPlatformArchive(basename, version):
     92   return MakeArchive(GetArchiveURL(basename, version), 'all')
     93 
     94 
     95 def MakeNonPepperBundle(name, with_archives=False):
     96   bundle = manifest_util.Bundle(name)
     97   bundle.version = 1
     98   bundle.revision = 1
     99   bundle.description = 'Dummy bundle'
    100   bundle.recommended = 'yes'
    101   bundle.stability = 'stable'
    102 
    103   if with_archives:
    104     for host_os in OS_MLW:
    105       archive = manifest_util.Archive(host_os)
    106       archive.url = 'http://example.com'
    107       archive.checksum = {'sha1': 'blah'}
    108       archive.size = 2
    109       bundle.AddArchive(archive)
    110   return bundle
    111 
    112 
    113 def MakePepperBundle(major_version, revision=0, version=None, stability='dev',
    114                      bundle_name=None):
    115   assert (version is None or
    116           version.split('.')[0] == 'trunk' or
    117           version.split('.')[0] == str(major_version))
    118   if not bundle_name:
    119     bundle_name = 'pepper_' + str(major_version)
    120 
    121   bundle = manifest_util.Bundle(bundle_name)
    122   bundle.version = major_version
    123   bundle.revision = revision
    124   bundle.description = 'Chrome %s bundle, revision %s' % (major_version,
    125                                                           revision)
    126   bundle.repath = 'pepper_' + str(major_version)
    127   bundle.recommended = 'no'
    128   bundle.stability = stability
    129 
    130   return bundle
    131 
    132 
    133 def MakePlatformBundle(major_version, revision=0, version=None, host_oses=None,
    134                        stability='dev'):
    135   bundle = MakePepperBundle(major_version, revision, version, stability)
    136 
    137   if host_oses:
    138     for host_os in host_oses:
    139       bundle.AddArchive(MakePlatformArchive(host_os, version))
    140 
    141   return bundle
    142 
    143 
    144 def MakeBionicBundle(major_version, revision=0, version=None, host_oses=None):
    145   bundle = MakePepperBundle(major_version, revision, version, 'dev')
    146 
    147   if host_oses:
    148     for host_os in host_oses:
    149       bundle.AddArchive(MakeBionicArchive(host_os, version))
    150 
    151   return bundle
    152 
    153 
    154 class MakeManifest(manifest_util.SDKManifest):
    155   def __init__(self, *args):
    156     manifest_util.SDKManifest.__init__(self)
    157 
    158     for bundle in args:
    159       self.AddBundle(bundle)
    160 
    161   def AddBundle(self, bundle):
    162     self.MergeBundle(bundle, allow_existing=False)
    163 
    164 
    165 class MakeHistory(object):
    166   def __init__(self):
    167     # used for a dummy timestamp
    168     self.datetime = datetime.datetime.utcnow()
    169     self.history = []
    170 
    171   def Add(self, host_oses, channel, version):
    172     for host_os in host_oses:
    173       timestamp = self.datetime.strftime('%Y-%m-%d %H:%M:%S.%f')
    174       self.history.append((host_os, channel, version, timestamp))
    175       self.datetime += datetime.timedelta(0, -3600) # one hour earlier
    176     self.datetime += datetime.timedelta(-1) # one day earlier
    177 
    178 
    179 class MakeFiles(dict):
    180   def AddOnlineManifest(self, manifest_string):
    181     self['naclsdk_manifest2.json'] = manifest_string
    182 
    183   def Add(self, bundle, add_archive_for_os=OS_MLW, add_json_for_os=OS_MLW):
    184     for archive in bundle.GetArchives():
    185       if not archive.host_os in add_archive_for_os:
    186         continue
    187 
    188       self.AddArchive(bundle, archive, archive.host_os in add_json_for_os)
    189 
    190   def AddArchive(self, bundle, archive, add_json=True):
    191     path = GetPathFromHttpsUrl(archive.url)
    192     self[path] = 'My Dummy archive'
    193 
    194     if add_json:
    195       # add .json manifest snippet, it should look like a normal Bundle, but
    196       # only has one archive.
    197       new_bundle = manifest_util.Bundle('')
    198       new_bundle.CopyFrom(bundle)
    199       del new_bundle.archives[:]
    200       new_bundle.AddArchive(archive)
    201       self[path + '.json'] = new_bundle.GetDataAsString()
    202 
    203 
    204 class TestDelegate(update_nacl_manifest.Delegate):
    205   def __init__(self, manifest, history, files):
    206     self.manifest = manifest
    207     self.history = history
    208     self.files = files
    209     self.dryrun = 0
    210     self.called_gsutil_cp = False
    211     self.called_sendmail = False
    212 
    213   def GetRepoManifest(self):
    214     return self.manifest
    215 
    216   def GetHistory(self):
    217     return self.history
    218 
    219   def GsUtil_ls(self, url):
    220     path = GetPathFromGsUrl(url)
    221     result = []
    222     for filename in self.files.iterkeys():
    223       if not filename.startswith(path):
    224         continue
    225 
    226       # Find the first slash after the prefix (path).
    227       # +1, because if the slash is directly after path, then we want to find
    228       # the following slash anyway.
    229       slash = filename.find('/', len(path) + 1)
    230 
    231       if slash != -1:
    232         filename = filename[:slash]
    233 
    234       result.append(MakeGsUrl(filename))
    235 
    236     # Remove dupes.
    237     return list(set(result))
    238 
    239   def GsUtil_cat(self, url):
    240     path = GetPathFromGsUrl(url)
    241     if path not in self.files:
    242       raise subprocess.CalledProcessError(1, 'gsutil cat %s' % (url,))
    243     return self.files[path]
    244 
    245   def GsUtil_cp(self, src, dest, stdin=None):
    246     self.called_gsutil_cp = True
    247     dest_path = GetPathFromGsUrl(dest)
    248     if src == '-':
    249       self.files[dest_path] = stdin
    250     else:
    251       src_path = GetPathFromGsUrl(src)
    252       if src_path not in self.files:
    253         raise subprocess.CalledProcessError(1, 'gsutil cp %s %s' % (src, dest))
    254       self.files[dest_path] = self.files[src_path]
    255 
    256   def SendMail(self, subject, text):
    257     self.called_sendmail = True
    258 
    259 
    260 # Shorthand for premade bundles/versions
    261 V18_0_1025_163 = '18.0.1025.163'
    262 V18_0_1025_175 = '18.0.1025.175'
    263 V18_0_1025_184 = '18.0.1025.184'
    264 V19_0_1084_41 = '19.0.1084.41'
    265 V19_0_1084_67 = '19.0.1084.67'
    266 V21_0_1145_0 = '21.0.1145.0'
    267 V21_0_1166_0 = '21.0.1166.0'
    268 V26_0_1386_0 = '26.0.1386.0'
    269 V26_0_1386_1 = '26.0.1386.1'
    270 V37_0_2054_0 = '37.0.2054.0'
    271 VTRUNK_140819 = 'trunk.140819'
    272 VTRUNK_277776 = 'trunk.277776'
    273 B18_0_1025_163_MLW = MakePlatformBundle(18, 132135, V18_0_1025_163, OS_MLW)
    274 B18_0_1025_184_MLW = MakePlatformBundle(18, 134900, V18_0_1025_184, OS_MLW)
    275 B18_NONE = MakePlatformBundle(18)
    276 B19_0_1084_41_MLW = MakePlatformBundle(19, 134854, V19_0_1084_41, OS_MLW)
    277 B19_0_1084_67_MLW = MakePlatformBundle(19, 142000, V19_0_1084_67, OS_MLW)
    278 B19_NONE = MakePlatformBundle(19)
    279 BCANARY_NONE = MakePepperBundle(0, stability=CANARY,
    280                                 bundle_name=CANARY_BUNDLE_NAME)
    281 B21_0_1145_0_MLW = MakePlatformBundle(21, 138079, V21_0_1145_0, OS_MLW)
    282 B21_0_1166_0_MW = MakePlatformBundle(21, 140819, V21_0_1166_0, OS_MW)
    283 B26_NONE = MakePlatformBundle(26)
    284 B26_0_1386_0_MLW = MakePlatformBundle(26, 177362, V26_0_1386_0, OS_MLW)
    285 B26_0_1386_1_MLW = MakePlatformBundle(26, 177439, V26_0_1386_1, OS_MLW)
    286 BTRUNK_140819_MLW = MakePlatformBundle(21, 140819, VTRUNK_140819, OS_MLW)
    287 BBIONIC_NONE = MakePepperBundle(0, stability=CANARY,
    288                                 bundle_name=BIONIC_CANARY_BUNDLE_NAME)
    289 BBIONIC_TRUNK_277776 = MakeBionicBundle(37, 277776, VTRUNK_277776, OS_L)
    290 NON_PEPPER_BUNDLE_NOARCHIVES = MakeNonPepperBundle('foo')
    291 NON_PEPPER_BUNDLE_ARCHIVES = MakeNonPepperBundle('bar', with_archives=True)
    292 
    293 
    294 class TestUpdateManifest(unittest.TestCase):
    295   def setUp(self):
    296     self.history = MakeHistory()
    297     self.files = MakeFiles()
    298     self.version_mapping = {}
    299     self.delegate = None
    300     self.uploaded_manifest = None
    301     self.manifest = None
    302 
    303   def _MakeDelegate(self):
    304     self.delegate = TestDelegate(self.manifest, self.history.history,
    305         self.files)
    306 
    307   def _Run(self, host_oses, extra_archives=None, fixed_bundle_versions=None):
    308     update_nacl_manifest.Run(self.delegate, host_oses, extra_archives,
    309                              fixed_bundle_versions)
    310 
    311   def _HasUploadedManifest(self):
    312     return 'naclsdk_manifest2.json' in self.files
    313 
    314   def _ReadUploadedManifest(self):
    315     self.uploaded_manifest = manifest_util.SDKManifest()
    316     self.uploaded_manifest.LoadDataFromString(
    317         self.files['naclsdk_manifest2.json'])
    318 
    319   def _AssertUploadedManifestHasBundle(self, bundle, stability,
    320                                        bundle_name=None):
    321     if not bundle_name:
    322       bundle_name = bundle.name
    323 
    324     uploaded_manifest_bundle = self.uploaded_manifest.GetBundle(bundle_name)
    325     # Bundles that we create in the test (and in the manifest snippets) have
    326     # their stability set to "dev". update_nacl_manifest correctly updates it.
    327     # So we have to force the stability of |bundle| so they compare equal.
    328     test_bundle = copy.copy(bundle)
    329     test_bundle.stability = stability
    330     if bundle_name:
    331       test_bundle.name = bundle_name
    332     self.assertEqual(uploaded_manifest_bundle, test_bundle)
    333 
    334   def _AddCsvHistory(self, history):
    335     import csv
    336     import cStringIO
    337     history_stream = cStringIO.StringIO(history)
    338     self.history.history = [(platform, channel, version, date)
    339         for platform, channel, version, date in csv.reader(history_stream)]
    340 
    341   def testNoUpdateNeeded(self):
    342     self.manifest = MakeManifest(B18_0_1025_163_MLW)
    343     self._MakeDelegate()
    344     self._Run(OS_MLW)
    345     self.assertFalse(self._HasUploadedManifest())
    346 
    347     # Add another bundle, make sure it still doesn't update
    348     self.manifest.AddBundle(B19_0_1084_41_MLW)
    349     self._Run(OS_MLW)
    350     self.assertFalse(self._HasUploadedManifest())
    351 
    352   def testSimpleUpdate(self):
    353     self.manifest = MakeManifest(B18_NONE)
    354     self.history.Add(OS_MLW, BETA, V18_0_1025_163)
    355     self.files.Add(B18_0_1025_163_MLW)
    356     self._MakeDelegate()
    357     self._Run(OS_MLW)
    358     self._ReadUploadedManifest()
    359     self._AssertUploadedManifestHasBundle(B18_0_1025_163_MLW, BETA)
    360     self.assertEqual(len(self.uploaded_manifest.GetBundles()), 1)
    361 
    362   def testOnePlatformHasNewerRelease(self):
    363     self.manifest = MakeManifest(B18_NONE)
    364     self.history.Add(OS_M, BETA, V18_0_1025_175)  # Mac has newer version
    365     self.history.Add(OS_MLW, BETA, V18_0_1025_163)
    366     self.files.Add(B18_0_1025_163_MLW)
    367     self._MakeDelegate()
    368     self._Run(OS_MLW)
    369     self._ReadUploadedManifest()
    370     self._AssertUploadedManifestHasBundle(B18_0_1025_163_MLW, BETA)
    371     self.assertEqual(len(self.uploaded_manifest.GetBundles()), 1)
    372 
    373   def testMultipleMissingPlatformsInHistory(self):
    374     self.manifest = MakeManifest(B18_NONE)
    375     self.history.Add(OS_ML, BETA, V18_0_1025_184)
    376     self.history.Add(OS_M, BETA, V18_0_1025_175)
    377     self.history.Add(OS_MLW, BETA, V18_0_1025_163)
    378     self.files.Add(B18_0_1025_163_MLW)
    379     self._MakeDelegate()
    380     self._Run(OS_MLW)
    381     self._ReadUploadedManifest()
    382     self._AssertUploadedManifestHasBundle(B18_0_1025_163_MLW, BETA)
    383     self.assertEqual(len(self.uploaded_manifest.GetBundles()), 1)
    384 
    385   def testUpdateOnlyOneBundle(self):
    386     self.manifest = MakeManifest(B18_NONE, B19_0_1084_41_MLW)
    387     self.history.Add(OS_MLW, BETA, V18_0_1025_163)
    388     self.files.Add(B18_0_1025_163_MLW)
    389     self._MakeDelegate()
    390     self._Run(OS_MLW)
    391     self._ReadUploadedManifest()
    392     self._AssertUploadedManifestHasBundle(B18_0_1025_163_MLW, BETA)
    393     self._AssertUploadedManifestHasBundle(B19_0_1084_41_MLW, DEV)
    394     self.assertEqual(len(self.uploaded_manifest.GetBundles()), 2)
    395 
    396   def testUpdateTwoBundles(self):
    397     self.manifest = MakeManifest(B18_NONE, B19_NONE)
    398     self.history.Add(OS_MLW, DEV, V19_0_1084_41)
    399     self.history.Add(OS_MLW, BETA, V18_0_1025_163)
    400     self.files.Add(B18_0_1025_163_MLW)
    401     self.files.Add(B19_0_1084_41_MLW)
    402     self._MakeDelegate()
    403     self._Run(OS_MLW)
    404     self._ReadUploadedManifest()
    405     self._AssertUploadedManifestHasBundle(B18_0_1025_163_MLW, BETA)
    406     self._AssertUploadedManifestHasBundle(B19_0_1084_41_MLW, DEV)
    407     self.assertEqual(len(self.uploaded_manifest.GetBundles()), 2)
    408 
    409   def testUpdateWithMissingPlatformsInArchives(self):
    410     self.manifest = MakeManifest(B18_NONE)
    411     self.history.Add(OS_MLW, BETA, V18_0_1025_184)
    412     self.history.Add(OS_MLW, BETA, V18_0_1025_163)
    413     self.files.Add(B18_0_1025_184_MLW, add_archive_for_os=OS_M)
    414     self.files.Add(B18_0_1025_163_MLW)
    415     self._MakeDelegate()
    416     self._Run(OS_MLW)
    417     self._ReadUploadedManifest()
    418     self._AssertUploadedManifestHasBundle(B18_0_1025_163_MLW, BETA)
    419     self.assertEqual(len(self.uploaded_manifest.GetBundles()), 1)
    420 
    421   def testUpdateWithMissingManifestSnippets(self):
    422     self.manifest = MakeManifest(B18_NONE)
    423     self.history.Add(OS_MLW, BETA, V18_0_1025_184)
    424     self.history.Add(OS_MLW, BETA, V18_0_1025_163)
    425     self.files.Add(B18_0_1025_184_MLW, add_json_for_os=OS_ML)
    426     self.files.Add(B18_0_1025_163_MLW)
    427     self._MakeDelegate()
    428     self._Run(OS_MLW)
    429     self._ReadUploadedManifest()
    430     self._AssertUploadedManifestHasBundle(B18_0_1025_163_MLW, BETA)
    431     self.assertEqual(len(self.uploaded_manifest.GetBundles()), 1)
    432 
    433   def testRecommendedIsStable(self):
    434     for channel in STABLE, BETA, DEV, CANARY:
    435       self.setUp()
    436       bundle = copy.deepcopy(B18_NONE)
    437       self.manifest = MakeManifest(bundle)
    438       self.history.Add(OS_MLW, channel, V18_0_1025_163)
    439       self.files.Add(B18_0_1025_163_MLW)
    440       self._MakeDelegate()
    441       self._Run(OS_MLW)
    442       self._ReadUploadedManifest()
    443       self.assertEqual(len(self.uploaded_manifest.GetBundles()), 1)
    444       uploaded_bundle = self.uploaded_manifest.GetBundle('pepper_18')
    445       if channel == STABLE:
    446         self.assertEqual(uploaded_bundle.recommended, 'yes')
    447       else:
    448         self.assertEqual(uploaded_bundle.recommended, 'no')
    449 
    450   def testNoUpdateWithNonPepperBundle(self):
    451     self.manifest = MakeManifest(NON_PEPPER_BUNDLE_NOARCHIVES,
    452         B18_0_1025_163_MLW)
    453     self._MakeDelegate()
    454     self._Run(OS_MLW)
    455     self.assertFalse(self._HasUploadedManifest())
    456 
    457   def testUpdateWithHistoryWithExtraneousPlatforms(self):
    458     self.manifest = MakeManifest(B18_NONE)
    459     self.history.Add(OS_ML, BETA, V18_0_1025_184)
    460     self.history.Add(OS_CR, BETA, V18_0_1025_184)
    461     self.history.Add(OS_CR, BETA, V18_0_1025_175)
    462     self.history.Add(OS_MLW, BETA, V18_0_1025_163)
    463     self.files.Add(B18_0_1025_163_MLW)
    464     self._MakeDelegate()
    465     self._Run(OS_MLW)
    466     self._ReadUploadedManifest()
    467     self._AssertUploadedManifestHasBundle(B18_0_1025_163_MLW, BETA)
    468     self.assertEqual(len(self.uploaded_manifest.GetBundles()), 1)
    469 
    470   def testSnippetWithStringRevisionAndVersion(self):
    471     # This test exists because some manifest snippets were uploaded with
    472     # strings for their revisions and versions. I want to make sure the
    473     # resulting manifest is still consistent with the old format.
    474     self.manifest = MakeManifest(B18_NONE)
    475     self.history.Add(OS_MLW, BETA, V18_0_1025_163)
    476     bundle_string_revision = MakePlatformBundle('18', '1234', V18_0_1025_163,
    477                                                 OS_MLW)
    478     self.files.Add(bundle_string_revision)
    479     self._MakeDelegate()
    480     self._Run(OS_MLW)
    481     self._ReadUploadedManifest()
    482     uploaded_bundle = self.uploaded_manifest.GetBundle(
    483         bundle_string_revision.name)
    484     self.assertEqual(uploaded_bundle.revision, 1234)
    485     self.assertEqual(uploaded_bundle.version, 18)
    486 
    487   def testUpdateCanary(self):
    488     self.manifest = MakeManifest(copy.deepcopy(BCANARY_NONE))
    489     self.files.Add(BTRUNK_140819_MLW)
    490     self._MakeDelegate()
    491     self._Run(OS_MLW)
    492     self._ReadUploadedManifest()
    493     self._AssertUploadedManifestHasBundle(BTRUNK_140819_MLW, CANARY,
    494                                           bundle_name=CANARY_BUNDLE_NAME)
    495 
    496   def testCanaryShouldOnlyUseCanaryVersions(self):
    497     canary_bundle = copy.deepcopy(BCANARY_NONE)
    498     self.manifest = MakeManifest(canary_bundle)
    499     self.history.Add(OS_MW, CANARY, V21_0_1166_0)
    500     self.history.Add(OS_MW, BETA, V19_0_1084_41)
    501     self.files.Add(B19_0_1084_41_MLW)
    502     self.version_mapping[V21_0_1166_0] = VTRUNK_140819
    503     self._MakeDelegate()
    504     self.assertRaises(Exception, self._Run, OS_MLW)
    505 
    506   def testExtensionWorksAsBz2(self):
    507     # Allow old bundles with just .bz2 extension to work
    508     self.manifest = MakeManifest(B18_NONE)
    509     self.history.Add(OS_MLW, BETA, V18_0_1025_163)
    510     bundle = copy.deepcopy(B18_0_1025_163_MLW)
    511     archive_url = bundle.GetArchive('mac').url
    512     bundle.GetArchive('mac').url = archive_url.replace('.tar', '')
    513     self.files.Add(bundle)
    514     self._MakeDelegate()
    515     self._Run(OS_MLW)
    516     self._ReadUploadedManifest()
    517     self._AssertUploadedManifestHasBundle(bundle, BETA)
    518     self.assertEqual(len(self.uploaded_manifest.GetBundles()), 1)
    519 
    520   def testOnlyOneStableBundle(self):
    521     # Make sure that any bundle that has an older version than STABLE is marked
    522     # as POST_STABLE, even if the last version we found was BETA, DEV, etc.
    523     for channel in STABLE, BETA, DEV, CANARY:
    524       self.setUp()
    525       self.manifest = MakeManifest(B18_NONE, B19_NONE)
    526       self.history.Add(OS_MLW, channel, V18_0_1025_163)
    527       self.history.Add(OS_MLW, STABLE, V19_0_1084_41)
    528       self.files.Add(B18_0_1025_163_MLW)
    529       self.files.Add(B19_0_1084_41_MLW)
    530       self._MakeDelegate()
    531       self._Run(OS_MLW)
    532       self._ReadUploadedManifest()
    533       p18_bundle = self.uploaded_manifest.GetBundle(B18_NONE.name)
    534       self.assertEqual(p18_bundle.stability, POST_STABLE)
    535       self.assertEqual(p18_bundle.recommended, 'no')
    536       p19_bundle = self.uploaded_manifest.GetBundle(B19_NONE.name)
    537       self.assertEqual(p19_bundle.stability, STABLE)
    538       self.assertEqual(p19_bundle.recommended, 'yes')
    539 
    540   def testDontPushIfNoChange(self):
    541     # Make an online manifest that already has this bundle.
    542     online_manifest = MakeManifest(B18_0_1025_163_MLW)
    543     self.files.AddOnlineManifest(online_manifest.GetDataAsString())
    544 
    545     self.manifest = MakeManifest(B18_NONE)
    546     self.history.Add(OS_MLW, DEV, V18_0_1025_163)
    547     self.files.Add(B18_0_1025_163_MLW)
    548 
    549     self._MakeDelegate()
    550     self._Run(OS_MLW)
    551     self.assertFalse(self.delegate.called_gsutil_cp)
    552 
    553   def testDontPushIfRollback(self):
    554     # Make an online manifest that has a newer bundle
    555     online_manifest = MakeManifest(B18_0_1025_184_MLW)
    556     self.files.AddOnlineManifest(online_manifest.GetDataAsString())
    557 
    558     self.manifest = MakeManifest(B18_NONE)
    559     self.history.Add(OS_MLW, DEV, V18_0_1025_163)
    560     self.files.Add(B18_0_1025_163_MLW)
    561 
    562     self._MakeDelegate()
    563     self._Run(OS_MLW)
    564     self.assertFalse(self.delegate.called_gsutil_cp)
    565 
    566   def testRunWithFixedBundleVersions(self):
    567     self.manifest = MakeManifest(B18_NONE)
    568     self.history.Add(OS_MLW, BETA, V18_0_1025_163)
    569     self.files.Add(B18_0_1025_163_MLW)
    570     self.files.Add(B18_0_1025_184_MLW)
    571 
    572     self._MakeDelegate()
    573     self._Run(OS_MLW, None, [('pepper_18', '18.0.1025.184')])
    574     self._ReadUploadedManifest()
    575     self._AssertUploadedManifestHasBundle(B18_0_1025_184_MLW, BETA)
    576     self.assertEqual(len(self.uploaded_manifest.GetBundles()), 1)
    577 
    578   def testRunWithMissingFixedBundleVersions(self):
    579     self.manifest = MakeManifest(B18_NONE)
    580     self.history.Add(OS_MLW, BETA, V18_0_1025_163)
    581     self.files.Add(B18_0_1025_163_MLW)
    582 
    583     self._MakeDelegate()
    584     self._Run(OS_MLW, None, [('pepper_18', '18.0.1025.184')])
    585     # Nothing should be uploaded if the user gives a missing fixed version.
    586     self.assertFalse(self.delegate.called_gsutil_cp)
    587 
    588   def testDontIncludeRandomBundles(self):
    589     self.manifest = MakeManifest(B26_NONE)
    590     self.history.Add(OS_MLW, BETA, V26_0_1386_0)
    591     self.files.Add(B26_0_1386_0_MLW)
    592 
    593     some_other_bundle = MakePepperBundle(26, 1, V26_0_1386_0, BETA)
    594     some_other_archive = MakeNonPlatformArchive('some_other.tar.bz2',
    595                                                 V26_0_1386_0)
    596     some_other_bundle.AddArchive(some_other_archive)
    597     self.files.AddArchive(some_other_bundle, some_other_archive)
    598 
    599     self._MakeDelegate()
    600     self._Run(OS_MLW)
    601     self._ReadUploadedManifest()
    602     uploaded_bundle = self.uploaded_manifest.GetBundle('pepper_26')
    603     self.assertEqual(1, len(uploaded_bundle.GetHostOSArchives()))
    604 
    605   def testNaclportsBundle(self):
    606     self.manifest = MakeManifest(B26_NONE)
    607     self.history.Add(OS_MLW, BETA, V26_0_1386_0)
    608     self.files.Add(B26_0_1386_0_MLW)
    609 
    610     # NaclPorts "bundle".
    611     naclports_bundle = MakePepperBundle(26, 1, V26_0_1386_0, BETA)
    612     naclports_archive = MakeNonPlatformArchive('naclports.tar.bz2',
    613                                                V26_0_1386_0)
    614     naclports_bundle.AddArchive(naclports_archive)
    615     self.files.AddArchive(naclports_bundle, naclports_archive)
    616 
    617     self._MakeDelegate()
    618     self._Run(OS_MLW, [('naclports.tar.bz2', '26.0.1386.0')])
    619     self._ReadUploadedManifest()
    620 
    621     uploaded_bundle = self.uploaded_manifest.GetBundle('pepper_26')
    622     self.assertEqual(2, len(uploaded_bundle.GetHostOSArchives()))
    623 
    624   def testKeepBundleOrder(self):
    625     # This is a regression test: when a bundle is skipped (because it isn't
    626     # newer than the online bundle), it was added to the end of the list.
    627 
    628     # Make an online manifest that already has B18.
    629     online_manifest = MakeManifest(B18_0_1025_163_MLW)
    630     self.files.AddOnlineManifest(online_manifest.GetDataAsString())
    631 
    632     self.manifest = MakeManifest(B18_NONE, B19_NONE)
    633     self.history.Add(OS_MLW, STABLE, V18_0_1025_163)
    634     self.history.Add(OS_MLW, STABLE, V19_0_1084_41)
    635     self.files.Add(B18_0_1025_163_MLW)
    636     self.files.Add(B19_0_1084_41_MLW)
    637 
    638     self._MakeDelegate()
    639     self._Run(OS_MLW)
    640     self._ReadUploadedManifest()
    641 
    642     # Bundle 18 should be before bundle 19.
    643     bundles = self.uploaded_manifest.GetBundles()
    644     self.assertEqual(2, len(bundles))
    645     self.assertEqual('pepper_18', bundles[0].name)
    646     self.assertEqual('pepper_19', bundles[1].name)
    647 
    648   def testBundleWithoutHistoryUsesOnline(self):
    649     online_manifest = MakeManifest(B18_0_1025_163_MLW)
    650     self.files.AddOnlineManifest(online_manifest.GetDataAsString())
    651 
    652     self.manifest = MakeManifest(B18_NONE)
    653 
    654     self._MakeDelegate()
    655     # This should not raise.
    656     self._Run(OS_MLW)
    657     self._ReadUploadedManifest()
    658 
    659     # But it should have sent an email nagging the users to lock this bundle
    660     # manually.
    661     self.assertTrue(self.delegate.called_sendmail)
    662 
    663     uploaded_bundle = self.uploaded_manifest.GetBundle('pepper_18')
    664     self.assertEqual(uploaded_bundle, B18_0_1025_163_MLW)
    665 
    666   def testBundleWithoutHistoryOrOnlineRaises(self):
    667     self.manifest = MakeManifest(B18_NONE)
    668     self._MakeDelegate()
    669     self.assertRaises(update_nacl_manifest.UnknownLockedBundleException,
    670                       self._Run, OS_MLW)
    671 
    672   def testUpdateBionic(self):
    673     bionic_bundle = copy.deepcopy(BBIONIC_NONE)
    674     self.manifest = MakeManifest(bionic_bundle)
    675     self.history.Add(OS_MW, CANARY, V37_0_2054_0)
    676     self.files.Add(BBIONIC_TRUNK_277776)
    677     self.version_mapping[V37_0_2054_0] = VTRUNK_277776
    678     self._MakeDelegate()
    679     self._Run(OS_MLW)
    680     self._ReadUploadedManifest()
    681     self._AssertUploadedManifestHasBundle(BBIONIC_TRUNK_277776, CANARY,
    682                                           bundle_name=BIONIC_CANARY_BUNDLE_NAME)
    683 
    684 
    685 class TestUpdateVitals(unittest.TestCase):
    686   def setUp(self):
    687     f = tempfile.NamedTemporaryFile('w', prefix="test_update_nacl_manifest")
    688     self.test_file = f.name
    689     f.close()
    690     test_data = "Some test data"
    691     self.sha1 = hashlib.sha1(test_data).hexdigest()
    692     self.data_len = len(test_data)
    693     with open(self.test_file, 'w') as f:
    694       f.write(test_data)
    695 
    696   def tearDown(self):
    697     os.remove(self.test_file)
    698 
    699   def testUpdateVitals(self):
    700     archive = manifest_util.Archive(manifest_util.GetHostOS())
    701     path = os.path.abspath(self.test_file)
    702     if sys.platform == 'win32':
    703       # On Windows, the path must start with three slashes, i.e.
    704       # (file:///C:\whatever)
    705       path = '/' + path
    706     archive.url = 'file://' + path
    707 
    708     bundle = MakePlatformBundle(18)
    709     bundle.AddArchive(archive)
    710     manifest = MakeManifest(bundle)
    711     archive = manifest.GetBundles()[0]['archives'][0]
    712 
    713     self.assertTrue('size' not in archive)
    714     self.assertTrue('checksum' not in archive)
    715     self.assertRaises(manifest_util.Error, manifest.Validate)
    716 
    717     manifest.Validate(add_missing_info=True)
    718 
    719     self.assertEqual(archive['size'], self.data_len)
    720     self.assertEqual(archive['checksum']['sha1'], self.sha1)
    721 
    722 
    723 if __name__ == '__main__':
    724   logging.basicConfig(level=logging.CRITICAL)
    725   # Uncomment the following line to enable more debugging info.
    726   # logging.getLogger('update_nacl_manifest').setLevel(logging.INFO)
    727 
    728   sys.exit(unittest.main())
    729