blob: bd39abb73b690a033f82695c6150d38ca1b64ab0 [file] [log] [blame]
xixuan44b55452016-09-06 15:35:56 -07001#!/usr/bin/env python2
Luis Hector Chavezdca9dd72018-06-12 12:56:30 -07002# -*- coding: utf-8 -*-
joychen3cb228e2013-06-12 12:13:13 -07003# Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
4# Use of this source code is governed by a BSD-style license that can be
5# found in the LICENSE file.
6
7"""Unit tests for xbuddy.py."""
8
Gilad Arnold5f46d8e2015-02-19 12:17:55 -08009from __future__ import print_function
10
joychen3cb228e2013-06-12 12:13:13 -070011import os
12import shutil
joychen921e1fb2013-06-28 11:12:20 -070013import tempfile
joychen3cb228e2013-06-12 12:13:13 -070014import time
15import unittest
16
Achuith Bhandarkar46b0c6e2019-09-19 15:46:16 +020017import mock
18from six.moves import configparser
joychen3cb228e2013-06-12 12:13:13 -070019
20import xbuddy
21
xixuan44b55452016-09-06 15:35:56 -070022# Make sure that chromite is available to import.
Achuith Bhandarkar46b0c6e2019-09-19 15:46:16 +020023import setup_chromite # pylint: disable=unused-import
xixuan44b55452016-09-06 15:35:56 -070024
25try:
26 from chromite.lib import gs
27except ImportError as e:
28 gs = None
29
Achuith Bhandarkar46b0c6e2019-09-19 15:46:16 +020030# pylint: disable=protected-access
31# pylint: disable=no-value-for-parameter
Simran Basi99e63c02014-05-20 10:39:52 -070032
33GS_ALTERNATE_DIR = 'gs://chromeos-alternate-archive/'
34
Achuith Bhandarkar46b0c6e2019-09-19 15:46:16 +020035class xBuddyTest(unittest.TestCase):
joychen3cb228e2013-06-12 12:13:13 -070036 """Regression tests for xbuddy."""
37 def setUp(self):
joychen3cb228e2013-06-12 12:13:13 -070038
joychen921e1fb2013-06-28 11:12:20 -070039 self.static_image_dir = tempfile.mkdtemp('xbuddy_unittest_static')
joychen3cb228e2013-06-12 12:13:13 -070040
joychen921e1fb2013-06-28 11:12:20 -070041 self.mock_xb = xbuddy.XBuddy(
Gilad Arnold5f46d8e2015-02-19 12:17:55 -080042 True,
43 static_dir=self.static_image_dir
joychen921e1fb2013-06-28 11:12:20 -070044 )
45 self.images_dir = tempfile.mkdtemp('xbuddy_unittest_images')
46 self.mock_xb.images_dir = self.images_dir
joychen3cb228e2013-06-12 12:13:13 -070047
48 def tearDown(self):
49 """Removes testing files."""
50 shutil.rmtree(self.static_image_dir)
joychen921e1fb2013-06-28 11:12:20 -070051 shutil.rmtree(self.images_dir)
joychen3cb228e2013-06-12 12:13:13 -070052
53 def testParseBoolean(self):
54 """Check that some common True/False strings are handled."""
55 self.assertEqual(xbuddy.XBuddy.ParseBoolean(None), False)
56 self.assertEqual(xbuddy.XBuddy.ParseBoolean('false'), False)
57 self.assertEqual(xbuddy.XBuddy.ParseBoolean('bs'), False)
58 self.assertEqual(xbuddy.XBuddy.ParseBoolean('true'), True)
59 self.assertEqual(xbuddy.XBuddy.ParseBoolean('y'), True)
60
xixuan44b55452016-09-06 15:35:56 -070061 def testGetLatestVersionFromGsDir(self):
62 """Test that we can get the most recent version from gsutil calls."""
xixuan44b55452016-09-06 15:35:56 -070063 mock_data1 = """gs://chromeos-releases/stable-channel/parrot/3701.96.0/
64 gs://chromeos-releases/stable-channel/parrot/3701.98.0/
65 gs://chromeos-releases/stable-channel/parrot/3912.100.0/
66 gs://chromeos-releases/stable-channel/parrot/3912.101.0/
67 gs://chromeos-releases/stable-channel/parrot/3912.79.0/
68 gs://chromeos-releases/stable-channel/parrot/3912.79.1/"""
69
70 mock_data2 = """gs://chromeos-image-archive/parrot-release/R26-3912.101.0
71 gs://chromeos-image-archive/parrot-release/R27-3912.101.0
72 gs://chromeos-image-archive/parrot-release/R28-3912.101.0"""
73
Achuith Bhandarkar46b0c6e2019-09-19 15:46:16 +020074 with mock.patch.object(
75 self.mock_xb, '_LS',
76 side_effect=[mock_data1.splitlines(),
77 mock_data2.splitlines()]) as ls_mock:
78 self.assertEqual(
79 self.mock_xb._GetLatestVersionFromGsDir('url1', with_release=False),
80 '3912.101.0')
81 ls_mock.assert_called_with('url1', list_subdirectory=False)
82 self.assertEqual(
83 self.mock_xb._GetLatestVersionFromGsDir(
84 'url2', list_subdirectory=True, with_release=True),
85 'R28-3912.101.0')
86 ls_mock.assert_called_with('url2', list_subdirectory=True)
xixuan44b55452016-09-06 15:35:56 -070087
joychenf8f07e22013-07-12 17:45:51 -070088 def testLookupOfficial(self):
89 """Basic test of _LookupOfficial. Checks that a given suffix is handled."""
Achuith Bhandarkar46b0c6e2019-09-19 15:46:16 +020090 with mock.patch.object(gs.GSContext, 'Cat', return_value='v') as cat_mock:
91 self.assertEqual(self.mock_xb._LookupOfficial('b', suffix='-s'), 'b-s/v')
92 cat_mock.assert_called_with(
93 'gs://chromeos-image-archive/b-s/LATEST-master')
joychenf8f07e22013-07-12 17:45:51 -070094
Achuith Bhandarkar46b0c6e2019-09-19 15:46:16 +020095 @mock.patch('xbuddy.XBuddy._GetLatestVersionFromGsDir',
96 side_effect=['4100.68.0', 'R28-4100.68.0'])
97 def testLookupChannel(self, version_mock):
joychenf8f07e22013-07-12 17:45:51 -070098 """Basic test of _LookupChannel. Checks that a given suffix is handled."""
Gilad Arnold896c6d82015-03-13 16:20:29 -070099 self.assertEqual(self.mock_xb._LookupChannel('b', '-release'),
Achuith Bhandarkar46b0c6e2019-09-19 15:46:16 +0200100 'b-release/R28-4100.68.0')
101 version_mock.assert_called_with(
102 'gs://chromeos-image-archive/b-release/R*4100.68.0',
103 list_subdirectory=True)
joychenf8f07e22013-07-12 17:45:51 -0700104
Gilad Arnold896c6d82015-03-13 16:20:29 -0700105 def testLookupAliasPathRewrite(self):
Gilad Arnold38e828c2015-04-24 13:52:07 -0700106 """Tests LookupAlias of path rewrite, including keyword substitution."""
Gilad Arnoldd04fcab2015-02-19 12:00:45 -0800107 path = 'remote/BOARD/VERSION/test'
Achuith Bhandarkar46b0c6e2019-09-19 15:46:16 +0200108 with mock.patch.object(
109 self.mock_xb.config, 'get',
110 side_effect=[configparser.Error, path]) as get_mock:
111 self.assertEqual(('remote/parrot/1.2.3/test', '-release'),
112 self.mock_xb.LookupAlias('foobar', board='parrot',
113 version='1.2.3'))
114 get_mock.assert_called_with('PATH_REWRITES', 'foobar')
Gilad Arnold896c6d82015-03-13 16:20:29 -0700115
116 def testLookupAliasSuffix(self):
Gilad Arnold38e828c2015-04-24 13:52:07 -0700117 """Tests LookupAlias of location suffix."""
Achuith Bhandarkar46b0c6e2019-09-19 15:46:16 +0200118 with mock.patch.object(
119 self.mock_xb.config, 'get',
120 side_effect=['-random', configparser.Error]) as get_mock:
121 self.assertEqual(('foobar', '-random'),
122 self.mock_xb.LookupAlias('foobar', board='parrot',
123 version='1.2.3'))
124 get_mock.assert_called_with('PATH_REWRITES', 'foobar')
Gilad Arnold896c6d82015-03-13 16:20:29 -0700125
126 def testLookupAliasPathRewriteAndSuffix(self):
Gilad Arnold38e828c2015-04-24 13:52:07 -0700127 """Tests LookupAlias with both path rewrite and suffix."""
Gilad Arnold896c6d82015-03-13 16:20:29 -0700128 path = 'remote/BOARD/VERSION/test'
Achuith Bhandarkar46b0c6e2019-09-19 15:46:16 +0200129 with mock.patch.object(
130 self.mock_xb.config, 'get',
131 side_effect=['-random', path]) as get_mock:
132 self.assertEqual(('remote/parrot/1.2.3/test', '-random'),
133 self.mock_xb.LookupAlias('foobar', board='parrot',
134 version='1.2.3'))
135 get_mock.assert_called_with('PATH_REWRITES', 'foobar')
Gilad Arnoldd04fcab2015-02-19 12:00:45 -0800136
Achuith Bhandarkar46b0c6e2019-09-19 15:46:16 +0200137 @mock.patch('xbuddy.XBuddy._LookupOfficial')
138 def testResolveVersionToBuildIdAndChannel_Official(self, lookup_mock):
Luis Hector Chavezdca9dd72018-06-12 12:56:30 -0700139 """Check _ResolveVersionToBuildIdAndChannel support for official build."""
Achuith Bhandarkar46b0c6e2019-09-19 15:46:16 +0200140 board = 'chell'
Gilad Arnold896c6d82015-03-13 16:20:29 -0700141 suffix = '-s'
joychenf8f07e22013-07-12 17:45:51 -0700142
joychenf8f07e22013-07-12 17:45:51 -0700143 version = 'latest-official'
Luis Hector Chavezdca9dd72018-06-12 12:56:30 -0700144 self.mock_xb._ResolveVersionToBuildIdAndChannel(board, suffix, version)
Achuith Bhandarkar46b0c6e2019-09-19 15:46:16 +0200145 lookup_mock.assert_called_with('chell', '-s', image_dir=None)
146
Luis Hector Chavezdca9dd72018-06-12 12:56:30 -0700147 self.mock_xb._ResolveVersionToBuildIdAndChannel(board, suffix, version,
148 image_dir=GS_ALTERNATE_DIR)
Achuith Bhandarkar46b0c6e2019-09-19 15:46:16 +0200149 lookup_mock.assert_called_with('chell', '-s',
150 image_dir='gs://chromeos-alternate-archive/')
151
joychenf8f07e22013-07-12 17:45:51 -0700152 version = 'latest-official-paladin'
Luis Hector Chavezdca9dd72018-06-12 12:56:30 -0700153 self.mock_xb._ResolveVersionToBuildIdAndChannel(board, suffix, version)
Achuith Bhandarkar46b0c6e2019-09-19 15:46:16 +0200154 lookup_mock.assert_called_with('chell', 'paladin', image_dir=None)
155
Luis Hector Chavezdca9dd72018-06-12 12:56:30 -0700156 self.mock_xb._ResolveVersionToBuildIdAndChannel(board, suffix, version,
157 image_dir=GS_ALTERNATE_DIR)
Achuith Bhandarkar46b0c6e2019-09-19 15:46:16 +0200158 lookup_mock.assert_called_with('chell', 'paladin',
159 image_dir='gs://chromeos-alternate-archive/')
joychenf8f07e22013-07-12 17:45:51 -0700160
Achuith Bhandarkar46b0c6e2019-09-19 15:46:16 +0200161 @mock.patch('xbuddy.XBuddy._LookupChannel')
162 def testResolveVersionToBuildIdAndChannel_Channel(self, lookup_mock):
Luis Hector Chavezdca9dd72018-06-12 12:56:30 -0700163 """Check _ResolveVersionToBuildIdAndChannel support for channels."""
Achuith Bhandarkar46b0c6e2019-09-19 15:46:16 +0200164 board = 'chell'
Gilad Arnold896c6d82015-03-13 16:20:29 -0700165 suffix = '-s'
joychenf8f07e22013-07-12 17:45:51 -0700166
joychenf8f07e22013-07-12 17:45:51 -0700167 version = 'latest'
Luis Hector Chavezdca9dd72018-06-12 12:56:30 -0700168 self.mock_xb._ResolveVersionToBuildIdAndChannel(board, suffix, version)
Achuith Bhandarkar46b0c6e2019-09-19 15:46:16 +0200169 lookup_mock.assert_called_with('chell', '-s', image_dir=None)
170
Luis Hector Chavezdca9dd72018-06-12 12:56:30 -0700171 self.mock_xb._ResolveVersionToBuildIdAndChannel(board, suffix, version,
172 image_dir=GS_ALTERNATE_DIR)
Achuith Bhandarkar46b0c6e2019-09-19 15:46:16 +0200173 lookup_mock.assert_called_with('chell', '-s',
174 image_dir='gs://chromeos-alternate-archive/')
175
joychenf8f07e22013-07-12 17:45:51 -0700176 version = 'latest-dev'
Luis Hector Chavezdca9dd72018-06-12 12:56:30 -0700177 self.mock_xb._ResolveVersionToBuildIdAndChannel(board, suffix, version)
Achuith Bhandarkar46b0c6e2019-09-19 15:46:16 +0200178 lookup_mock.assert_called_with('chell', '-s', channel='dev', image_dir=None)
179
Luis Hector Chavezdca9dd72018-06-12 12:56:30 -0700180 self.mock_xb._ResolveVersionToBuildIdAndChannel(board, suffix, version,
181 image_dir=GS_ALTERNATE_DIR)
Achuith Bhandarkar46b0c6e2019-09-19 15:46:16 +0200182 lookup_mock.assert_called_with('chell', '-s', channel='dev',
183 image_dir='gs://chromeos-alternate-archive/')
joychen3cb228e2013-06-12 12:13:13 -0700184
Don Garrett80e24112016-06-21 16:22:49 -0700185 # TODO(dgarrett): Re-enable when crbug.com/585914 is fixed.
186 # def testResolveVersionToBuildId_BaseVersion(self):
187 # """Check _ResolveVersionToBuildId handles a base version."""
188 # board = 'b'
189 # suffix = '-s'
Gilad Arnold869e8ab2015-02-19 23:34:49 -0800190
Don Garrett80e24112016-06-21 16:22:49 -0700191 # self.mox.StubOutWithMock(self.mock_xb, '_ResolveBuildVersion')
192 # self.mock_xb._ResolveBuildVersion(board, suffix, '1.2.3').AndReturn(
193 # 'R12-1.2.3')
194 # self.mox.StubOutWithMock(self.mock_xb, '_RemoteBuildId')
195 # self.mock_xb._RemoteBuildId(board, suffix, 'R12-1.2.3')
196 # self.mox.ReplayAll()
Gilad Arnold869e8ab2015-02-19 23:34:49 -0800197
Don Garrett80e24112016-06-21 16:22:49 -0700198 # self.mock_xb._ResolveVersionToBuildId(board, suffix, '1.2.3')
199 # self.mox.VerifyAll()
Gilad Arnold869e8ab2015-02-19 23:34:49 -0800200
joychen3cb228e2013-06-12 12:13:13 -0700201 def testBasicInterpretPath(self):
202 """Basic checks for splitting a path"""
joychen121fc9b2013-08-02 14:30:30 -0700203 path = 'parrot/R27-2455.0.0/test'
joychen7df67f72013-07-18 14:21:12 -0700204 expected = ('test', 'parrot', 'R27-2455.0.0', True)
joychen121fc9b2013-08-02 14:30:30 -0700205 self.assertEqual(self.mock_xb._InterpretPath(path=path), expected)
joychen7df67f72013-07-18 14:21:12 -0700206
joychen121fc9b2013-08-02 14:30:30 -0700207 path = 'parrot/R27-2455.0.0/full_payload'
208 expected = ('full_payload', 'parrot', 'R27-2455.0.0', True)
209 self.assertEqual(self.mock_xb._InterpretPath(path=path), expected)
210
211 path = 'parrot/R27-2455.0.0'
Chris Sosa0eecf962014-02-03 14:14:39 -0800212 expected = ('ANY', 'parrot', 'R27-2455.0.0', True)
joychen121fc9b2013-08-02 14:30:30 -0700213 self.assertEqual(self.mock_xb._InterpretPath(path=path), expected)
214
215 path = 'remote/parrot/R27-2455.0.0'
216 expected = ('test', 'parrot', 'R27-2455.0.0', False)
217 self.assertEqual(self.mock_xb._InterpretPath(path=path), expected)
218
219 path = 'local/parrot/R27-2455.0.0'
Chris Sosa0eecf962014-02-03 14:14:39 -0800220 expected = ('ANY', 'parrot', 'R27-2455.0.0', True)
joychen121fc9b2013-08-02 14:30:30 -0700221 self.assertEqual(self.mock_xb._InterpretPath(path=path), expected)
222
223 path = ''
Chris Sosa0eecf962014-02-03 14:14:39 -0800224 expected = ('ANY', None, 'latest', True)
225 self.assertEqual(self.mock_xb._InterpretPath(path=path), expected)
joychen3cb228e2013-06-12 12:13:13 -0700226
joychen121fc9b2013-08-02 14:30:30 -0700227 path = 'local'
Chris Sosa0eecf962014-02-03 14:14:39 -0800228 expected = ('ANY', None, 'latest', True)
229 self.assertEqual(self.mock_xb._InterpretPath(path=path), expected)
joychen7df67f72013-07-18 14:21:12 -0700230
joychenc3944cb2013-08-19 10:42:07 -0700231 path = 'local/parrot/latest/ANY'
232 expected = ('ANY', 'parrot', 'latest', True)
233 self.assertEqual(self.mock_xb._InterpretPath(path=path), expected)
234
Gilad Arnoldd04fcab2015-02-19 12:00:45 -0800235 def testInterpretPathWithDefaults(self):
236 """Test path splitting with default board/version."""
237 path = ''
238 expected = ('ANY', 'parrot', 'latest', True)
239 self.assertEqual(expected, self.mock_xb._InterpretPath(
240 path=path, default_board='parrot'))
241
242 path = ''
243 expected = ('ANY', None, '1.2.3', True)
244 self.assertEqual(expected, self.mock_xb._InterpretPath(
245 path=path, default_version='1.2.3'))
246
247 path = ''
248 expected = ('ANY', 'parrot', '1.2.3', True)
249 self.assertEqual(expected, self.mock_xb._InterpretPath(
250 path=path, default_board='parrot', default_version='1.2.3'))
251
252 path = '1.2.3'
253 expected = ('ANY', None, '1.2.3', True)
254 self.assertEqual(expected, self.mock_xb._InterpretPath(
255 path=path, default_version='1.2.3'))
256
257 path = 'latest'
258 expected = ('ANY', None, 'latest', True)
259 self.assertEqual(expected, self.mock_xb._InterpretPath(
260 path=path, default_version='1.2.3'))
261
262 path = '1.2.3'
263 expected = ('ANY', 'parrot', '1.2.3', True)
264 self.assertEqual(expected, self.mock_xb._InterpretPath(
265 path=path, default_board='parrot', default_version='1.2.3'))
266
267 path = 'parrot'
268 expected = ('ANY', 'parrot', '1.2.3', True)
269 self.assertEqual(expected, self.mock_xb._InterpretPath(
270 path=path, default_version='1.2.3'))
joychen7df67f72013-07-18 14:21:12 -0700271
joychen3cb228e2013-06-12 12:13:13 -0700272 def testTimestampsAndList(self):
273 """Creation and listing of builds according to their timestamps."""
274 # make 3 different timestamp files
joychen921e1fb2013-06-28 11:12:20 -0700275 b_id11 = 'b1/v1'
276 b_id12 = 'b1/v2'
277 b_id23 = 'b2/v3'
278 xbuddy.Timestamp.UpdateTimestamp(self.mock_xb._timestamp_folder, b_id11)
279 time.sleep(0.05)
280 xbuddy.Timestamp.UpdateTimestamp(self.mock_xb._timestamp_folder, b_id12)
281 time.sleep(0.05)
282 xbuddy.Timestamp.UpdateTimestamp(self.mock_xb._timestamp_folder, b_id23)
joychen3cb228e2013-06-12 12:13:13 -0700283
284 # reference second one again
joychen921e1fb2013-06-28 11:12:20 -0700285 time.sleep(0.05)
286 xbuddy.Timestamp.UpdateTimestamp(self.mock_xb._timestamp_folder, b_id12)
joychen3cb228e2013-06-12 12:13:13 -0700287
288 # check that list returns the same 3 things, in last referenced order
joychen921e1fb2013-06-28 11:12:20 -0700289 result = self.mock_xb._ListBuildTimes()
290 self.assertEqual(result[0][0], b_id12)
291 self.assertEqual(result[1][0], b_id23)
292 self.assertEqual(result[2][0], b_id11)
293
294 def testSyncRegistry(self):
295 # check that there are no builds initially
296 result = self.mock_xb._ListBuildTimes()
297 self.assertEqual(len(result), 0)
298
299 # set up the dummy build/images directory with images
300 boards = ['a', 'b']
301 versions = ['v1', 'v2']
302 for b in boards:
303 os.makedirs(os.path.join(self.mock_xb.images_dir, b))
304 for v in versions:
305 os.makedirs(os.path.join(self.mock_xb.images_dir, b, v))
306
307 # Sync and check that they've been added to xBuddy's registry
308 self.mock_xb._SyncRegistryWithBuildImages()
309 result = self.mock_xb._ListBuildTimes()
310 self.assertEqual(len(result), 4)
joychen3cb228e2013-06-12 12:13:13 -0700311
joychen3cb228e2013-06-12 12:13:13 -0700312 def testXBuddyCaching(self):
313 """Caching & replacement of timestamp files."""
joychen3cb228e2013-06-12 12:13:13 -0700314
Achuith Bhandarkar46b0c6e2019-09-19 15:46:16 +0200315 def _ReleaseOnly(name):
316 # All non-release URLs are invalid so we can meet expectations.
317 if name.find('-release') == -1:
318 raise gs.GSContextException('bad URL')
319 return name
Chris Sosaea734d92013-10-11 11:28:58 -0700320
Achuith Bhandarkar46b0c6e2019-09-19 15:46:16 +0200321 with mock.patch.object(
322 gs.GSContext, 'LS', side_effect=_ReleaseOnly) as ls_mock:
323 with mock.patch.object(
324 self.mock_xb, '_Download') as download_mock:
325 version = '%s-release/R0'
joychen3cb228e2013-06-12 12:13:13 -0700326
Achuith Bhandarkar46b0c6e2019-09-19 15:46:16 +0200327 def _Download(image):
328 gs_image = 'gs://chromeos-image-archive/' + version
329 self.mock_xb.Get(('remote', image, 'R0', 'test'))
330 ls_mock.assert_called_with(gs_image % image)
331 download_mock.assert_called_with(gs_image % image, ['test_image'],
332 version % image)
333 time.sleep(0.05)
joychen3cb228e2013-06-12 12:13:13 -0700334
Achuith Bhandarkar46b0c6e2019-09-19 15:46:16 +0200335 # Requires default capacity.
336 self.assertEqual(self.mock_xb.Capacity(), '5')
joychen3cb228e2013-06-12 12:13:13 -0700337
Achuith Bhandarkar46b0c6e2019-09-19 15:46:16 +0200338 # Get 6 different images: a,b,c,d,e,f.
339 images = ['a', 'b', 'c', 'd', 'e', 'f']
340 for image in images:
341 _Download(image)
joychen921e1fb2013-06-28 11:12:20 -0700342
Achuith Bhandarkar46b0c6e2019-09-19 15:46:16 +0200343 # Check that b,c,d,e,f are still stored.
344 result = self.mock_xb._ListBuildTimes()
345 self.assertEqual(len(result), 5)
joychen3cb228e2013-06-12 12:13:13 -0700346
Achuith Bhandarkar46b0c6e2019-09-19 15:46:16 +0200347 # Flip the list to get reverse chronological order
348 images.reverse()
349 for i in range(5):
350 self.assertEqual(result[i][0], version % images[i])
joychen3cb228e2013-06-12 12:13:13 -0700351
Achuith Bhandarkar46b0c6e2019-09-19 15:46:16 +0200352 # Get b,a.
353 images = ['b', 'a']
354 for image in images:
355 _Download(image)
joychen3cb228e2013-06-12 12:13:13 -0700356
Achuith Bhandarkar46b0c6e2019-09-19 15:46:16 +0200357 # Check that d,e,f,b,a are still stored.
358 result = self.mock_xb._ListBuildTimes()
359 self.assertEqual(len(result), 5)
360 images_expected = ['a', 'b', 'f', 'e', 'd']
361 for i in range(5):
362 self.assertEqual(result[i][0], '%s-release/R0' % images_expected[i])
joychen3cb228e2013-06-12 12:13:13 -0700363
364
365if __name__ == '__main__':
366 unittest.main()