Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / third_party / chromite / cbuildbot / cbuildbot_run_unittest.py
1 #!/usr/bin/python
2 # Copyright (c) 2013 The Chromium OS 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 """Test the cbuildbot_run module."""
7
8 from __future__ import print_function
9
10 import logging
11 import os
12 import cPickle
13 import sys
14 import time
15
16 sys.path.insert(0, os.path.abspath('%s/../..' % os.path.dirname(__file__)))
17 from chromite.cbuildbot import cbuildbot_config
18 from chromite.cbuildbot import cbuildbot_run
19 from chromite.lib import cros_test_lib
20 from chromite.lib import parallel
21
22 import mock
23
24 DEFAULT_ARCHIVE_GS_PATH = 'bogus_bucket/TheArchiveBase'
25 DEFAULT_ARCHIVE_BASE = 'gs://%s' % DEFAULT_ARCHIVE_GS_PATH
26 DEFAULT_BUILDROOT = '/tmp/foo/bar/buildroot'
27 DEFAULT_BUILDNUMBER = 12345
28 DEFAULT_BRANCH = 'TheBranch'
29 DEFAULT_CHROME_BRANCH = 'TheChromeBranch'
30 DEFAULT_VERSION_STRING = 'TheVersionString'
31 DEFAULT_BOARD = 'TheBoard'
32 DEFAULT_BOT_NAME = 'TheCoolBot'
33
34 # Access to protected member.
35 # pylint: disable=W0212
36
37 DEFAULT_OPTIONS = cros_test_lib.EasyAttr(
38     archive_base=DEFAULT_ARCHIVE_BASE,
39     buildroot=DEFAULT_BUILDROOT,
40     buildnumber=DEFAULT_BUILDNUMBER,
41     buildbot=True,
42     branch=DEFAULT_BRANCH,
43     remote_trybot=False,
44     debug=False,
45     postsync_patch=True,
46 )
47 DEFAULT_CONFIG = cbuildbot_config._config(
48     name=DEFAULT_BOT_NAME,
49     master=True,
50     boards=[DEFAULT_BOARD],
51     postsync_patch=True,
52     child_configs=[cbuildbot_config._config(name='foo', postsync_patch=False,
53                                             boards=[]),
54                    cbuildbot_config._config(name='bar', postsync_patch=False,
55                                             boards=[]),
56                   ],
57 )
58
59 DEFAULT_VERSION = '6543.2.1'
60
61
62 def _ExtendDefaultOptions(**kwargs):
63   """Extend DEFAULT_OPTIONS with keys/values in kwargs."""
64   options_kwargs = DEFAULT_OPTIONS.copy()
65   options_kwargs.update(kwargs)
66   return cros_test_lib.EasyAttr(**options_kwargs)
67
68
69 def _ExtendDefaultConfig(**kwargs):
70   """Extend DEFAULT_CONFIG with keys/values in kwargs."""
71   config_kwargs = DEFAULT_CONFIG.copy()
72   config_kwargs.update(kwargs)
73   return cbuildbot_config._config(**config_kwargs)
74
75
76 class ExceptionsTest(cros_test_lib.TestCase):
77   """Test that the exceptions in the module are sane."""
78
79   def _TestException(self, err, expected_startswith):
80     """Test that str and pickle behavior of |err| are as expected."""
81     err2 = cPickle.loads(cPickle.dumps(err, cPickle.HIGHEST_PROTOCOL))
82
83     self.assertTrue(str(err).startswith(expected_startswith))
84     self.assertEqual(str(err), str(err2))
85
86   def testParallelAttributeError(self):
87     """Test ParallelAttributeError message and pickle behavior."""
88     err1 = cbuildbot_run.ParallelAttributeError('SomeAttr')
89     self._TestException(err1, 'No such parallel run attribute')
90
91     err2 = cbuildbot_run.ParallelAttributeError('SomeAttr', 'SomeBoard',
92                                                 'SomeTarget')
93     self._TestException(err2, 'No such board-specific parallel run attribute')
94
95   def testAttrSepCountError(self):
96     """Test AttrSepCountError message and pickle behavior."""
97     err1 = cbuildbot_run.AttrSepCountError('SomeAttr')
98     self._TestException(err1, 'Attribute name has an unexpected number')
99
100   def testAttrNotPickleableError(self):
101     """Test AttrNotPickleableError message and pickle behavior."""
102     err1 = cbuildbot_run.AttrNotPickleableError('SomeAttr', 'SomeValue')
103     self._TestException(err1, 'Run attribute "SomeAttr" value cannot')
104
105
106 # TODO(mtennant): Turn this into a PartialMock.
107 class _BuilderRunTestCase(cros_test_lib.MockTestCase):
108   """Provide methods for creating BuilderRun or ChildBuilderRun."""
109
110   def setUp(self):
111     self._manager = parallel.Manager()
112
113     # Mimic entering a 'with' statement.
114     self._manager.__enter__()
115
116   def tearDown(self):
117     # Mimic exiting a 'with' statement.
118     self._manager.__exit__(None, None, None)
119
120   def _NewRunAttributes(self):
121     return cbuildbot_run.RunAttributes(self._manager)
122
123   def _NewBuilderRun(self, options=None, config=None):
124     """Create a BuilderRun objection from options and config values.
125
126     Args:
127       options: Specify options or default to DEFAULT_OPTIONS.
128       config: Specify build config or default to DEFAULT_CONFIG.
129
130     Returns:
131       BuilderRun object.
132     """
133     options = options or DEFAULT_OPTIONS
134     config = config or DEFAULT_CONFIG
135
136     return cbuildbot_run.BuilderRun(options, config, self._manager)
137
138   def _NewChildBuilderRun(self, child_index, options=None, config=None):
139     """Create a ChildBuilderRun objection from options and config values.
140
141     Args:
142       child_index: Index of child config to use within config.
143       options: Specify options or default to DEFAULT_OPTIONS.
144       config: Specify build config or default to DEFAULT_CONFIG.
145
146     Returns:
147       ChildBuilderRun object.
148     """
149     run = self._NewBuilderRun(options, config)
150     return cbuildbot_run.ChildBuilderRun(run, child_index)
151
152
153 class BuilderRunPickleTest(_BuilderRunTestCase):
154   """Make sure BuilderRun objects can be pickled."""
155
156   def setUp(self):
157     self.real_config = cbuildbot_config.config['x86-alex-release-group']
158     self.PatchObject(cbuildbot_run._BuilderRunBase, 'GetVersion',
159                      return_value=DEFAULT_VERSION)
160
161   def _TestPickle(self, run1):
162     self.assertEquals(DEFAULT_VERSION, run1.GetVersion())
163     run1.attrs.release_tag = 'TheReleaseTag'
164
165     # Accessing a method on BuilderRun has special behavior, so access and
166     # use one before pickling.
167     patch_after_sync = run1.ShouldPatchAfterSync()
168
169     # Access the archive object before pickling, too.
170     upload_url = run1.GetArchive().upload_url
171
172     # Pickle and unpickle run1 into run2.
173     run2 = cPickle.loads(cPickle.dumps(run1, cPickle.HIGHEST_PROTOCOL))
174
175     self.assertEquals(run1.buildnumber, run2.buildnumber)
176     self.assertEquals(run1.config.boards, run2.config.boards)
177     self.assertEquals(run1.options.branch, run2.options.branch)
178     self.assertEquals(run1.attrs.release_tag, run2.attrs.release_tag)
179     self.assertRaises(AttributeError, getattr, run1.attrs, 'manifest_manager')
180     self.assertRaises(AttributeError, getattr, run2.attrs, 'manifest_manager')
181     self.assertEquals(patch_after_sync, run2.ShouldPatchAfterSync())
182     self.assertEquals(upload_url, run2.GetArchive().upload_url)
183
184     # The attrs objects should be identical.
185     self.assertTrue(run1.attrs is run2.attrs)
186
187     # And the run objects themselves are different.
188     self.assertFalse(run1 is run2)
189
190   def testPickleBuilderRun(self):
191     self._TestPickle(self._NewBuilderRun(config=self.real_config))
192
193   def testPickleChildBuilderRun(self):
194     self._TestPickle(self._NewChildBuilderRun(0, config=self.real_config))
195
196
197 class BuilderRunTest(_BuilderRunTestCase):
198   """Test the BuilderRun class."""
199
200   def testInit(self):
201     with mock.patch.object(cbuildbot_run._BuilderRunBase, 'GetVersion') as m:
202       m.return_value = DEFAULT_VERSION
203
204       run = self._NewBuilderRun()
205       self.assertEquals(DEFAULT_BUILDROOT, run.buildroot)
206       self.assertEquals(DEFAULT_BUILDNUMBER, run.buildnumber)
207       self.assertEquals(DEFAULT_BRANCH, run.manifest_branch)
208       self.assertEquals(DEFAULT_OPTIONS, run.options)
209       self.assertEquals(DEFAULT_CONFIG, run.config)
210       self.assertTrue(isinstance(run.attrs, cbuildbot_run.RunAttributes))
211       self.assertTrue(isinstance(run.GetArchive(),
212                                  cbuildbot_run.archive_lib.Archive))
213
214       # Make sure methods behave normally, since BuilderRun messes with them.
215       meth1 = run.GetVersionInfo
216       meth2 = run.GetVersionInfo
217       self.assertEqual(meth1.__name__, meth2.__name__)
218
219       # We actually do not support identity and equality checks right now.
220       self.assertNotEqual(meth1, meth2)
221       self.assertFalse(meth1 is meth2)
222
223   def testOptions(self):
224     options = _ExtendDefaultOptions(foo=True, bar=10)
225     run = self._NewBuilderRun(options=options)
226
227     self.assertEquals(True, run.options.foo)
228     self.assertEquals(10, run.options.__getattr__('bar'))
229     self.assertRaises(AttributeError, run.options.__getattr__, 'baz')
230
231   def testConfig(self):
232     config = _ExtendDefaultConfig(foo=True, bar=10)
233     run = self._NewBuilderRun(config=config)
234
235     self.assertEquals(True, run.config.foo)
236     self.assertEquals(10, run.config.__getattr__('bar'))
237     self.assertRaises(AttributeError, run.config.__getattr__, 'baz')
238
239   def testAttrs(self):
240     run = self._NewBuilderRun()
241
242     # manifest_manager is a valid run attribute.  It gives Attribute error
243     # if accessed before being set, but thereafter works fine.
244     self.assertRaises(AttributeError, run.attrs.__getattribute__,
245                       'manifest_manager')
246     run.attrs.manifest_manager = 'foo'
247     self.assertEquals('foo', run.attrs.manifest_manager)
248     self.assertEquals('foo', run.attrs.__getattribute__('manifest_manager'))
249
250     # foobar is not a valid run attribute.  It gives AttributeError when
251     # accessed or changed.
252     self.assertRaises(AttributeError, run.attrs.__getattribute__, 'foobar')
253     self.assertRaises(AttributeError, run.attrs.__setattr__, 'foobar', 'foo')
254
255   def testArchive(self):
256     run = self._NewBuilderRun()
257
258     with mock.patch.object(cbuildbot_run._BuilderRunBase, 'GetVersion') as m:
259       m.return_value = DEFAULT_VERSION
260
261       archive = run.GetArchive()
262
263       # Check archive.archive_path.
264       expected = ('%s/%s/%s/%s' %
265                   (DEFAULT_BUILDROOT,
266                    cbuildbot_run.archive_lib.Archive._BUILDBOT_ARCHIVE,
267                    DEFAULT_BOT_NAME, DEFAULT_VERSION))
268       self.assertEqual(expected, archive.archive_path)
269
270       # Check archive.upload_url.
271       expected = '%s/%s/%s' % (DEFAULT_ARCHIVE_BASE, DEFAULT_BOT_NAME,
272                                DEFAULT_VERSION)
273       self.assertEqual(expected, archive.upload_url)
274
275       # Check archive.download_url.
276       expected = ('%s%s/%s/%s' %
277                   (cbuildbot_run.archive_lib.gs.PRIVATE_BASE_HTTPS_URL,
278                    DEFAULT_ARCHIVE_GS_PATH, DEFAULT_BOT_NAME, DEFAULT_VERSION))
279       self.assertEqual(expected, archive.download_url)
280
281   def _RunAccessor(self, method_name, options_dict, config_dict):
282     """Run the given accessor method of the BuilderRun class.
283
284     Create a BuilderRun object with the options and config provided and
285     then return the result of calling the given method on it.
286
287     Args:
288       method_name: A BuilderRun method to call, specified by name.
289       options_dict: Extend default options with this.
290       config_dict: Extend default config with this.
291
292     Returns:
293       Result of calling the given method.
294     """
295     options = _ExtendDefaultOptions(**options_dict)
296     config = _ExtendDefaultConfig(**config_dict)
297     run = self._NewBuilderRun(options=options, config=config)
298     method = getattr(run, method_name)
299     self.assertEqual(method.__name__, method_name)
300     return method()
301
302   def testDualEnableSetting(self):
303     settings = {
304         'prebuilts': 'ShouldUploadPrebuilts',
305         'postsync_patch': 'ShouldPatchAfterSync',
306     }
307
308     # Both option and config enabled should result in True.
309     # Create truth table with three variables in this order:
310     # <key> option value, <key> config value (e.g. <key> == 'prebuilts').
311     truth_table = cros_test_lib.TruthTable(inputs=[(True, True)])
312
313     for inputs in truth_table:
314       option_val, config_val = inputs
315       for key, accessor in settings.iteritems():
316         self.assertEquals(
317             self._RunAccessor(accessor, {key: option_val}, {key: config_val}),
318             truth_table.GetOutput(inputs))
319
320   def testShouldReexecAfterSync(self):
321     # If option and config have postsync_reexec enabled, and this file is not
322     # in the build root, then we expect ShouldReexecAfterSync to return True.
323
324     # Construct a truth table across three variables in this order:
325     # postsync_reexec option value, postsync_reexec config value, same_root.
326     truth_table = cros_test_lib.TruthTable(inputs=[(True, True, False)])
327
328     for inputs in truth_table:
329       option_val, config_val, same_root = inputs
330
331       if same_root:
332         build_root = os.path.dirname(os.path.dirname(__file__))
333       else:
334         build_root = DEFAULT_BUILDROOT
335
336       result = self._RunAccessor(
337           'ShouldReexecAfterSync',
338           {'postsync_reexec': option_val, 'buildroot': build_root},
339           {'postsync_reexec': config_val})
340
341       self.assertEquals(result, truth_table.GetOutput(inputs))
342
343
344 class GetVersionTest(_BuilderRunTestCase):
345   """Test the GetVersion and GetVersionInfo methods of BuilderRun class."""
346   # Access to protected member.
347   # pylint: disable=W0212
348
349   def testGetVersionInfo(self):
350     verinfo = object()
351
352     with mock.patch('cbuildbot_run.manifest_version.VersionInfo.from_repo',
353                     return_value=verinfo) as m:
354       result = cbuildbot_run._BuilderRunBase.GetVersionInfo(DEFAULT_BUILDROOT)
355       self.assertEquals(result, verinfo)
356
357       m.assert_called_once_with(DEFAULT_BUILDROOT)
358
359   def _TestGetVersionReleaseTag(self, release_tag):
360     with mock.patch.object(cbuildbot_run._BuilderRunBase,
361                            'GetVersionInfo') as m:
362       verinfo_mock = mock.Mock()
363       verinfo_mock.chrome_branch = DEFAULT_CHROME_BRANCH
364       verinfo_mock.VersionString = mock.Mock(return_value='VS')
365       m.return_value = verinfo_mock
366
367       # Prepare a real BuilderRun object with a release tag.
368       run = self._NewBuilderRun()
369       run.attrs.release_tag = release_tag
370
371       # Run the test return the result.
372       result = run.GetVersion()
373       m.assert_called_once_with(DEFAULT_BUILDROOT)
374       if release_tag is None:
375         verinfo_mock.VersionString.assert_called_once()
376
377       return result
378
379   def testGetVersionReleaseTag(self):
380     result = self._TestGetVersionReleaseTag('RT')
381     self.assertEquals('R%s-%s' % (DEFAULT_CHROME_BRANCH, 'RT'), result)
382
383   def testGetVersionNoReleaseTag(self):
384     result = self._TestGetVersionReleaseTag(None)
385     expected_result = ('R%s-%s-b%s' %
386                        (DEFAULT_CHROME_BRANCH, 'VS', DEFAULT_BUILDNUMBER))
387     self.assertEquals(result, expected_result)
388
389
390 class ChildBuilderRunTest(_BuilderRunTestCase):
391   """Test the ChildBuilderRun class"""
392
393   def testInit(self):
394     with mock.patch.object(cbuildbot_run._BuilderRunBase, 'GetVersion') as m:
395       m.return_value = DEFAULT_VERSION
396
397       crun = self._NewChildBuilderRun(0)
398       self.assertEquals(DEFAULT_BUILDROOT, crun.buildroot)
399       self.assertEquals(DEFAULT_BUILDNUMBER, crun.buildnumber)
400       self.assertEquals(DEFAULT_BRANCH, crun.manifest_branch)
401       self.assertEquals(DEFAULT_OPTIONS, crun.options)
402       self.assertEquals(DEFAULT_CONFIG.child_configs[0], crun.config)
403       self.assertEquals('foo', crun.config.name)
404       self.assertTrue(isinstance(crun.attrs, cbuildbot_run.RunAttributes))
405       self.assertTrue(isinstance(crun.GetArchive(),
406                                  cbuildbot_run.archive_lib.Archive))
407
408       # Make sure methods behave normally, since BuilderRun messes with them.
409       meth1 = crun.GetVersionInfo
410       meth2 = crun.GetVersionInfo
411       self.assertEqual(meth1.__name__, meth2.__name__)
412
413       # We actually do not support identity and equality checks right now.
414       self.assertNotEqual(meth1, meth2)
415       self.assertFalse(meth1 is meth2)
416
417
418 class RunAttributesTest(_BuilderRunTestCase):
419   """Test the RunAttributes class."""
420
421   BOARD = 'SomeBoard'
422   TARGET = 'SomeConfigName'
423   VALUE = 'AnyValueWillDo'
424
425   # Any valid board-specific attribute will work here.
426   BATTR = 'breakpad_symbols_generated'
427
428   def testRegisterBoardTarget(self):
429     """Test behavior of attributes before and after registering board target."""
430     ra = self._NewRunAttributes()
431
432     with self.assertRaises(AssertionError):
433       ra.HasBoardParallel(self.BATTR, self.BOARD, self.TARGET)
434
435     ra.RegisterBoardAttrs(self.BOARD, self.TARGET)
436
437     self.assertFalse(ra.HasBoardParallel(self.BATTR, self.BOARD, self.TARGET))
438
439     ra.SetBoardParallel(self.BATTR, 'TheValue', self.BOARD, self.TARGET)
440
441     self.assertTrue(ra.HasBoardParallel(self.BATTR, self.BOARD, self.TARGET))
442
443   def testSetGet(self):
444     """Test simple set/get of regular and parallel run attributes."""
445     ra = self._NewRunAttributes()
446     value = 'foobar'
447
448     # Set/Get a regular run attribute using direct access.
449     ra.release_tag = value
450     self.assertEqual(value, ra.release_tag)
451
452     # Set/Get of a parallel run attribute using direct access fails.
453     self.assertRaises(AttributeError, setattr, ra, 'unittest_value', value)
454     self.assertRaises(AttributeError, getattr, ra, 'unittest_value')
455
456     # Set/Get of a parallel run attribute with supported interface.
457     ra.SetParallel('unittest_value', value)
458     self.assertEqual(value, ra.GetParallel('unittest_value'))
459
460     # Set/Get a board parallel run attribute, testing both the encouraged
461     # interface and the underlying interface.
462     ra.RegisterBoardAttrs(self.BOARD, self.TARGET)
463     ra.SetBoardParallel(self.BATTR, value, self.BOARD, self.TARGET)
464     self.assertEqual(value,
465                      ra.GetBoardParallel(self.BATTR, self.BOARD, self.TARGET))
466
467   def testSetDefault(self):
468     """Test setting default value of parallel run attributes."""
469     ra = self._NewRunAttributes()
470     value = 'foobar'
471
472     # Attribute starts off not set.
473     self.assertFalse(ra.HasParallel('unittest_value'))
474
475     # Use SetParallelDefault to set it.
476     ra.SetParallelDefault('unittest_value', value)
477     self.assertTrue(ra.HasParallel('unittest_value'))
478     self.assertEqual(value, ra.GetParallel('unittest_value'))
479
480     # Calling SetParallelDefault again has no effect.
481     ra.SetParallelDefault('unittest_value', 'junk')
482     self.assertTrue(ra.HasParallel('unittest_value'))
483     self.assertEqual(value, ra.GetParallel('unittest_value'))
484
485     # Run through same sequence for a board-specific attribute.
486     with self.assertRaises(AssertionError):
487       ra.HasBoardParallel(self.BATTR, self.BOARD, self.TARGET)
488     ra.RegisterBoardAttrs(self.BOARD, self.TARGET)
489     self.assertFalse(ra.HasBoardParallel(self.BATTR, self.BOARD, self.TARGET))
490
491     # Use SetBoardParallelDefault to set it.
492     ra.SetBoardParallelDefault(self.BATTR, value, self.BOARD, self.TARGET)
493     self.assertTrue(ra.HasBoardParallel(self.BATTR, self.BOARD, self.TARGET))
494     self.assertEqual(value,
495                      ra.GetBoardParallel(self.BATTR, self.BOARD, self.TARGET))
496
497     # Calling SetBoardParallelDefault again has no effect.
498     ra.SetBoardParallelDefault(self.BATTR, 'junk', self.BOARD, self.TARGET)
499     self.assertTrue(ra.HasBoardParallel(self.BATTR, self.BOARD, self.TARGET))
500     self.assertEqual(value,
501                      ra.GetBoardParallel(self.BATTR, self.BOARD, self.TARGET))
502
503   def testAttributeError(self):
504     """Test accessing run attributes that do not exist."""
505     ra = self._NewRunAttributes()
506     value = 'foobar'
507
508     # Set/Get on made up attribute name.
509     self.assertRaises(AttributeError, setattr, ra, 'foo', value)
510     self.assertRaises(AttributeError, getattr, ra, 'foo')
511
512     # A board/target value is valid, but only if it is registered first.
513     self.assertRaises(AssertionError, ra.GetBoardParallel,
514                       self.BATTR, self.BOARD, self.TARGET)
515     ra.RegisterBoardAttrs(self.BOARD, self.TARGET)
516     self.assertRaises(AttributeError, ra.GetBoardParallel,
517                       self.BATTR, self.BOARD, self.TARGET)
518
519
520 class BoardRunAttributesTest(_BuilderRunTestCase):
521   """Test the BoardRunAttributes class."""
522
523   BOARD = 'SomeBoard'
524   TARGET = 'SomeConfigName'
525   VALUE = 'AnyValueWillDo'
526
527   # Any valid board-specific attribute will work here.
528   BATTR = 'breakpad_symbols_generated'
529
530   class _SetAttr(object):
531     """Stage-like class to set attr on a BoardRunAttributes obj."""
532     def __init__(self, bra, attr, value, delay=1):
533       self.bra = bra
534       self.attr = attr
535       self.value = value
536       self.delay = delay
537
538     def Run(self):
539       if self.delay:
540         time.sleep(self.delay)
541       self.bra.SetParallel(self.attr, self.value)
542
543   class _WaitForAttr(object):
544     """Stage-like class to wait for attr on BoardRunAttributes obj."""
545     def __init__(self, bra, attr, expected_value, timeout=10):
546       self.bra = bra
547       self.attr = attr
548       self.expected_value = expected_value
549       self.timeout = timeout
550
551     def GetParallel(self):
552       return self.bra.GetParallel(self.attr, timeout=self.timeout)
553
554   class _CheckWaitForAttr(_WaitForAttr):
555     """Stage-like class to wait for then check attr on BoardRunAttributes."""
556     def Run(self):
557       value = self.GetParallel()
558       assert value == self.expected_value, \
559           ('For run attribute %s expected value %r but got %r.' %
560            (self.attr, self.expected_value, value))
561
562   class _TimeoutWaitForAttr(_WaitForAttr):
563     """Stage-like class to time-out waiting for attr on BoardRunAttributes."""
564     def Run(self):
565       try:
566         self.GetParallel()
567         assert False, 'Expected AttrTimeoutError'
568       except cbuildbot_run.AttrTimeoutError:
569         pass
570
571   def setUp(self):
572     self.ra = self._NewRunAttributes()
573     self.bra = self.ra.RegisterBoardAttrs(self.BOARD, self.TARGET)
574
575   def _TestParallelSetGet(self, stage_args):
576     """Helper to run "stages" in parallel, according to |stage_args|.
577
578     Args:
579       stage_args: List of tuples of the form (stage_object, extra_args, ...)
580         where stage_object has a Run method which takes a BoardRunAttributes
581         object as the first argument and extra_args for the remaining arguments.
582     """
583     stages = [a[0](self.bra, *a[1:]) for a in stage_args]
584     steps = [stage.Run for stage in stages]
585
586     parallel.RunParallelSteps(steps)
587
588   def testParallelSetGetFast(self):
589     """Pass the parallel run attribute around with no delay."""
590     stage_args = [
591         (self._CheckWaitForAttr, self.BATTR, self.VALUE),
592         (self._SetAttr, self.BATTR, self.VALUE),
593     ]
594     self._TestParallelSetGet(stage_args)
595     self.assertRaises(AttributeError,
596                       getattr, self.bra, self.BATTR)
597     self.assertEqual(self.VALUE, self.bra.GetParallel(self.BATTR))
598
599   def testParallelSetGetSlow(self):
600     """Pass the parallel run attribute around with a delay."""
601     stage_args = [
602         (self._SetAttr, self.BATTR, self.VALUE, 10),
603         (self._TimeoutWaitForAttr, self.BATTR, self.VALUE, 2),
604     ]
605     self._TestParallelSetGet(stage_args)
606     self.assertEqual(self.VALUE, self.bra.GetParallel(self.BATTR))
607
608   def testParallelSetGetManyGets(self):
609     """Set the parallel run attribute in one stage, access in many stages."""
610     stage_args = [
611         (self._SetAttr, self.BATTR, self.VALUE, 8),
612         (self._CheckWaitForAttr, self.BATTR, self.VALUE, 16),
613         (self._CheckWaitForAttr, self.BATTR, self.VALUE, 16),
614         (self._CheckWaitForAttr, self.BATTR, self.VALUE, 16),
615         (self._TimeoutWaitForAttr, self.BATTR, self.VALUE, 1),
616     ]
617     self._TestParallelSetGet(stage_args)
618     self.assertEqual(self.VALUE, self.bra.GetParallel(self.BATTR))
619
620   def testParallelSetGetManySets(self):
621     """Set the parallel run attribute in many stages, access in one stage."""
622     # Three "stages" set the value, with increasing delays.  The stage that
623     # checks the value should get the first value set.
624     stage_args = [
625         (self._SetAttr, self.BATTR, self.VALUE + '1', 1),
626         (self._SetAttr, self.BATTR, self.VALUE + '2', 11),
627         (self._CheckWaitForAttr, self.BATTR, self.VALUE + '1', 12),
628     ]
629     self._TestParallelSetGet(stage_args)
630     self.assertEqual(self.VALUE + '2', self.bra.GetParallel(self.BATTR))
631
632   def testSetGet(self):
633     """Test that board-specific attrs do not work with set/get directly."""
634     self.assertRaises(AttributeError, setattr,
635                       self.bra, 'breakpad_symbols_generated', self.VALUE)
636     self.assertRaises(AttributeError, getattr,
637                       self.bra, 'breakpad_symbols_generated')
638
639   def testAccessRegularRunAttr(self):
640     """Test that regular attributes are not known to BoardRunAttributes."""
641     self.assertRaises(AttributeError, getattr, self.bra, 'release_tag')
642     self.assertRaises(AttributeError, setattr, self.bra, 'release_tag', 'foo')
643
644
645 if __name__ == '__main__':
646   cros_test_lib.main(level=logging.DEBUG)