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