3 # Copyright (c) 2011 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.
7 """Unit tests for cros_portage_upgrade.py."""
13 from chromite.lib import cros_test_lib
14 from chromite.lib import gdata_lib
15 from chromite.lib import osutils
16 from chromite.lib import table as tablelib
17 from chromite.scripts import merge_package_status as mps
18 from chromite.scripts import upload_package_status as ups
20 # pylint: disable=W0212,R0904,E1120,E1101
23 class SSEntry(object):
24 """Class to simulate one spreadsheet entry."""
25 def __init__(self, text):
30 """Class for simulating spreadsheet row."""
31 def __init__(self, row, cols=None):
35 # If columns not specified, then column order doesn't matter.
38 ss_col = gdata_lib.PrepColNameForSS(col)
40 ss_val = gdata_lib.PrepValForSS(val)
41 self.custom[ss_col] = SSEntry(ss_val)
45 """Class for simulating spreadsheet list feed."""
46 def __init__(self, rows, cols=None):
49 self.entry.append(SSRow(row, cols))
52 class UploaderTest(cros_test_lib.MoxOutputTestCase):
53 """Test the functionality of upload_package_status.Uploader class."""
57 COL_OVERLAY = 'Overlay'
59 COL_VER = 'Current Version'
60 COL_STABLE_UP = 'Stable Upstream Version'
61 COL_LATEST_UP = 'Latest Upstream Version'
62 COL_TARGET = 'Chrome OS Root Target'
64 SS_COL_PKG = gdata_lib.PrepColNameForSS(COL_PKG)
65 SS_COL_SLOT = gdata_lib.PrepColNameForSS(COL_SLOT)
66 SS_COL_OVERLAY = gdata_lib.PrepColNameForSS(COL_OVERLAY)
67 SS_COL_STATUS = gdata_lib.PrepColNameForSS(COL_STATUS)
68 SS_COL_VER = gdata_lib.PrepColNameForSS(COL_VER)
69 SS_COL_STABLE_UP = gdata_lib.PrepColNameForSS(COL_STABLE_UP)
70 SS_COL_LATEST_UP = gdata_lib.PrepColNameForSS(COL_LATEST_UP)
71 SS_COL_TARGET = gdata_lib.PrepColNameForSS(COL_TARGET)
83 ROW0 = {COL_PKG: 'lib/foo',
85 COL_OVERLAY: 'portage',
86 COL_STATUS: 'needs upgrade',
88 COL_STABLE_UP: '3.0.9',
89 COL_LATEST_UP: '3.0.11',
90 COL_TARGET: 'virtual/target-os',
92 ROW1 = {COL_PKG: 'sys-dev/bar',
94 COL_OVERLAY: 'chromiumos-overlay',
95 COL_STATUS: 'needs upgrade',
97 COL_STABLE_UP: '1.2.3-r2',
98 COL_LATEST_UP: '1.2.4',
99 COL_TARGET: 'virtual/target-os-dev',
101 ROW2 = {COL_PKG: 'sys-dev/raster',
103 COL_OVERLAY: 'chromiumos-overlay',
104 COL_STATUS: 'current',
106 COL_STABLE_UP: '1.2.3',
107 COL_LATEST_UP: '1.2.4',
108 COL_TARGET: 'chromeos-test',
111 SS_ROW0 = dict([(gdata_lib.PrepColNameForSS(c), v) for c, v in ROW0.items()])
112 SS_ROW1 = dict([(gdata_lib.PrepColNameForSS(c), v) for c, v in ROW1.items()])
113 SS_ROW2 = dict([(gdata_lib.PrepColNameForSS(c), v) for c, v in ROW2.items()])
115 EMAIL = 'knights@ni.com'
118 def _MockUploader(self, table=None):
119 """Set up a mocked Uploader object."""
120 uploader = self.mox.CreateMock(ups.Uploader)
124 table = self._CreateDefaultTable()
126 for slot in ups.Uploader.__slots__:
127 uploader.__setattr__(slot, None)
129 uploader._csv_table = table
130 uploader._scomm = self.mox.CreateMock(gdata_lib.SpreadsheetComm)
131 uploader._creds = cros_test_lib.EasyAttr(user=self.EMAIL,
132 password=self.PASSWORD)
133 uploader._ss_row_cache = self._CreateRowCache(table)
137 def _CreateRowCache(self, table):
138 """Recreate the expected row cache (by pkg) from |table|."""
143 for rowIx, row in enumerate(table):
144 pkg = row[self.COL_PKG]
146 # Translate column names now.
149 ss_row_dict[gdata_lib.PrepColNameForSS(col)] = row[col]
151 ss_row = gdata_lib.SpreadsheetRow('OrigRow%d' % (rowIx + 2),
152 rowIx + 2, ss_row_dict)
153 entry = row_cache.get(pkg)
155 row_cache[pkg] = ss_row
156 elif type(entry) == list:
157 row_cache[pkg] = entry + [ss_row]
159 row_cache[pkg] = [entry, ss_row]
162 def _CreateDefaultTable(self):
163 return self._CreateTableWithRows(self.COLS,
164 [self.ROW0, self.ROW1])
166 def _CreateTableWithRows(self, cols, rows):
167 mytable = tablelib.Table(list(cols))
170 mytable.AppendRow(dict(row))
173 def testLoadTable(self):
174 # Note that this test is not actually for method of Uploader class.
176 self.mox.StubOutWithMock(tablelib.Table, 'LoadFromCSV')
180 tablelib.Table.LoadFromCSV(csv).AndReturn('loaded_table')
183 # Verification steps.
184 with self.OutputCapturer():
185 loaded_table = ups.LoadTable(csv)
186 self.assertEquals(loaded_table, 'loaded_table')
188 def testGetSSRowForPackage(self):
189 mocked_uploader = self._MockUploader()
194 # Verification steps.
195 result = ups.Uploader._GetSSRowForPackage(mocked_uploader,
196 self.ROW0[self.COL_PKG])
197 self.assertEquals(result, self.SS_ROW0)
198 self.assertEquals(2, result.ss_row_num)
199 result = ups.Uploader._GetSSRowForPackage(mocked_uploader,
200 self.ROW1[self.COL_PKG])
201 self.assertEquals(result, self.SS_ROW1)
202 self.assertEquals(3, result.ss_row_num)
203 result = ups.Uploader._GetSSRowForPackage(mocked_uploader,
204 self.ROW2[self.COL_PKG])
205 self.assertEquals(result, None)
209 def testUploadFirstWorksheet(self):
210 mocked_uploader = self._MockUploader()
212 # Clear ._scomm attribute to simulate uploading first worksheet.
213 mocked_scomm = mocked_uploader._scomm
214 mocked_uploader._scomm = None
216 self.mox.StubOutWithMock(gdata_lib.SpreadsheetComm, '__new__')
218 ss_key = 'Some ss_key'
219 ws_name = 'Some ws_name'
222 gdata_lib.SpreadsheetComm.__new__(gdata_lib.SpreadsheetComm
223 ).AndReturn(mocked_scomm)
224 mocked_scomm.Connect(mocked_uploader._creds, ss_key, ws_name,
225 source='Upload Package Status')
226 mocked_scomm.GetRowCacheByCol(self.SS_COL_PKG).AndReturn('RowCache')
227 mocked_uploader._UploadChangedRows().AndReturn(tuple([0, 1, 2]))
228 mocked_uploader._DeleteOldRows().AndReturn(tuple([3, 4]))
232 with self.OutputCapturer():
233 ups.Uploader.Upload(mocked_uploader, ss_key, ws_name)
236 def testUploadSecondWorksheet(self):
237 mocked_uploader = self._MockUploader()
239 ss_key = 'Some ss_key'
240 ws_name = 'Some ws_name'
243 mocked_uploader._scomm.SetCurrentWorksheet(ws_name)
244 mocked_uploader._scomm.GetRowCacheByCol(self.SS_COL_PKG).AndReturn('RCache')
245 mocked_uploader._UploadChangedRows().AndReturn(tuple([0, 1, 2]))
246 mocked_uploader._DeleteOldRows().AndReturn(tuple([3, 4]))
250 with self.OutputCapturer():
251 ups.Uploader.Upload(mocked_uploader, ss_key, ws_name)
254 def testUploadChangedRows(self):
255 table = self._CreateTableWithRows(self.COLS,
256 [self.ROW0, self.ROW1, self.ROW2])
257 mocked_uploader = self._MockUploader(table=table)
259 def RowVerifier(row_delta, golden_col_set, golden_row):
260 if golden_col_set != set(row_delta.keys()):
263 for col in row_delta:
265 if val != golden_row[col]:
271 # Pretend first row does not exist already in online spreadsheet
272 # by returning (None, None) from _GetSSRowForPackage.
274 row0_pkg = self.ROW0[self.COL_PKG]
275 mocked_uploader._GetSSRowForPackage(row0_pkg).AndReturn(None)
276 mocked_uploader._scomm.InsertRow(mox.IgnoreArg())
279 # Pretend second row does already exist in online spreadsheet, and
280 # pretend that it has a different value that needs to be changed
282 row1_pkg = self.ROW1[self.COL_PKG]
283 row1_reverse_delta = { self.SS_COL_VER: '1.2.3' }
284 ss_row1 = dict(self.SS_ROW1)
285 for col in row1_reverse_delta:
286 ss_row1[col] = row1_reverse_delta[col]
287 ss_row1 = gdata_lib.SpreadsheetRow('OrigRow1', 3, ss_row1)
288 mocked_uploader._GetSSRowForPackage(row1_pkg).AndReturn(ss_row1)
289 # Prepare verfication for row.
290 g_col_set1 = set(row1_reverse_delta.keys())
291 g_row1 = gdata_lib.PrepRowForSS(self.SS_ROW1)
292 row1_verifier = lambda rdelta : RowVerifier(rdelta, g_col_set1, g_row1)
293 mocked_uploader._scomm.UpdateRowCellByCell(3, mox.Func(row1_verifier))
296 # Pretend third row does already exist in online spreadsheet, and
297 # pretend that several values need to be changed by an upload.
298 row2_pkg = self.ROW2[self.COL_PKG]
299 row2_reverse_delta = { self.SS_COL_STATUS: 'needs upgrade',
300 self.SS_COL_VER: '0.5',
301 self.SS_COL_TARGET: 'chromeos-foo',
303 ss_row2 = dict(self.SS_ROW2)
304 for col in row2_reverse_delta:
305 ss_row2[col] = row2_reverse_delta[col]
306 ss_row2 = gdata_lib.SpreadsheetRow('OrigRow2', 4, ss_row2)
307 mocked_uploader._GetSSRowForPackage(row2_pkg).AndReturn(ss_row2)
308 # Prepare verification for row.
309 g_col_set2 = set(row2_reverse_delta.keys())
310 g_row2 = gdata_lib.PrepRowForSS(self.SS_ROW2)
311 row2_verifier = lambda rdelta : RowVerifier(rdelta, g_col_set2, g_row2)
312 mocked_uploader._scomm.UpdateRowCellByCell(4, mox.Func(row2_verifier))
317 with self.OutputCapturer():
318 ups.Uploader._UploadChangedRows(mocked_uploader)
321 def testDeleteOldRows(self):
322 mocked_uploader = self._MockUploader()
324 # Pretend spreadsheet has 2 rows, one in table and one not.
325 ss_row1 = gdata_lib.SpreadsheetRow('OrigRow1', 2, self.SS_ROW1)
326 ss_row2 = gdata_lib.SpreadsheetRow('OrigRow2', 3, self.SS_ROW2)
327 ss_rows = (ss_row1, ss_row2)
329 mocked_uploader._scomm.GetRows().AndReturn(ss_rows)
330 # We expect ROW2 in spreadsheet to be deleted.
331 mocked_uploader._scomm.DeleteRow('OrigRow2')
335 with self.OutputCapturer():
336 ups.Uploader._DeleteOldRows(mocked_uploader)
340 class MainTest(cros_test_lib.MoxOutputTestCase):
341 """Test argument handling at the main method level."""
344 """Test that --help is functioning"""
345 with self.OutputCapturer() as output:
346 # Running with --help should exit with code==0
349 except exceptions.SystemExit as e:
350 self.assertEquals(e.args[0], 0)
352 # Verify that a message beginning with "Usage: " was printed
353 stdout = output.GetStdout()
354 self.assertTrue(stdout.startswith('Usage: '))
356 def testMissingCSV(self):
357 """Test that running without a csv file argument exits with an error."""
358 with self.OutputCapturer():
359 # Running without a package should exit with code!=0
362 except exceptions.SystemExit as e:
363 self.assertNotEquals(e.args[0], 0)
365 self.AssertOutputEndsInError(check_stdout=True)
367 def testPrepareCredsEmailPassword(self):
371 token_file = 'boguser'
373 mocked_creds = self.mox.CreateMock(gdata_lib.Creds)
374 self.mox.StubOutWithMock(gdata_lib.Creds, '__new__')
376 gdata_lib.Creds.__new__(gdata_lib.Creds).AndReturn(mocked_creds)
377 mocked_creds.SetCreds(email, password)
380 ups.PrepareCreds(creds_file, token_file, email, password)
383 def testMainEmailPassword(self):
384 """Verify that running main with email/password follows flow."""
389 mocked_creds = self.mox.CreateMock(gdata_lib.Creds)
390 creds_file = 'non-existing-file'
392 self.mox.StubOutWithMock(ups, 'PrepareCreds')
393 self.mox.StubOutWithMock(ups, 'LoadTable')
394 self.mox.StubOutWithMock(mps, 'FinalizeTable')
395 self.mox.StubOutWithMock(ups.Uploader, 'Upload')
397 ups.PrepareCreds(creds_file, None, email, password).AndReturn(mocked_creds)
398 ups.LoadTable(csv).AndReturn('csv_table')
399 mps.FinalizeTable('csv_table')
400 ups.Uploader.Upload(mox.IgnoreArg(), ws_name='Packages')
401 ups.Uploader.Upload(mox.IgnoreArg(), ws_name='Dependencies')
402 mocked_creds.StoreCredsIfNeeded(creds_file)
405 ups.main(['--email=%s' % email,
406 '--password=%s' % password,
407 '--cred-file=%s' % creds_file,
412 @osutils.TempFileDecorator
413 def testMainCredsFile(self):
414 """Verify that running main with creds file follows flow."""
416 creds_file = self.tempfile
417 token_file = 'non-existing-file'
419 mocked_creds = self.mox.CreateMock(gdata_lib.Creds)
420 mocked_creds.auth_token_loaded = False
422 self.mox.StubOutWithMock(ups, 'PrepareCreds')
423 self.mox.StubOutWithMock(ups, 'LoadTable')
424 self.mox.StubOutWithMock(mps, 'FinalizeTable')
425 self.mox.StubOutWithMock(ups.Uploader, 'Upload')
427 ups.PrepareCreds(creds_file, token_file, None, None).AndReturn(mocked_creds)
428 ups.LoadTable(csv).AndReturn('csv_table')
429 mps.FinalizeTable('csv_table')
430 ups.Uploader.Upload(mox.IgnoreArg(), ws_name=ups.PKGS_WS_NAME)
431 ups.Uploader.Upload(mox.IgnoreArg(), ws_name=ups.DEPS_WS_NAME)
432 mocked_creds.StoreCredsIfNeeded(creds_file)
433 mocked_creds.StoreAuthTokenIfNeeded(token_file)
436 ups.main(['--cred-file=%s' % creds_file,
437 '--auth-token-file=%s' % token_file,
443 @osutils.TempFileDecorator
444 def testMainTokenFile(self):
445 """Verify that running main with token file follows flow."""
447 token_file = self.tempfile
448 creds_file = 'non-existing-file'
450 mocked_creds = self.mox.CreateMock(gdata_lib.Creds)
451 mocked_creds.auth_token_loaded = True
453 self.mox.StubOutWithMock(ups, 'PrepareCreds')
454 self.mox.StubOutWithMock(ups, 'LoadTable')
455 self.mox.StubOutWithMock(mps, 'FinalizeTable')
456 self.mox.StubOutWithMock(ups.Uploader, 'Upload')
458 ups.PrepareCreds(creds_file, token_file, None, None).AndReturn(mocked_creds)
459 ups.LoadTable(csv).AndReturn('csv_table')
460 mps.FinalizeTable('csv_table')
461 ups.Uploader.Upload(mox.IgnoreArg(), ws_name=ups.PKGS_WS_NAME)
462 ups.Uploader.Upload(mox.IgnoreArg(), ws_name=ups.DEPS_WS_NAME)
463 mocked_creds.StoreCredsIfNeeded(creds_file)
464 mocked_creds.StoreAuthTokenIfNeeded(token_file)
467 ups.main(['--cred-file=%s' % creds_file,
468 '--auth-token-file=%s' % token_file,
474 if __name__ == '__main__':