3 # 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.
7 """Test the cbuildbot_run module."""
15 sys.path.insert(0, os.path.abspath('%s/../..' % os.path.dirname(__file__)))
16 from chromite.buildbot import cbuildbot_config
17 from chromite.buildbot import cbuildbot_run
18 from chromite.lib import cros_test_lib
19 from chromite.lib import parallel
23 DEFAULT_ARCHIVE_GS_PATH = 'bogus_bucket/TheArchiveBase'
24 DEFAULT_ARCHIVE_BASE = 'gs://%s' % DEFAULT_ARCHIVE_GS_PATH
25 DEFAULT_BUILDROOT = '/tmp/foo/bar/buildroot'
26 DEFAULT_BUILDNUMBER = 12345
27 DEFAULT_BRANCH = 'TheBranch'
28 DEFAULT_CHROME_BRANCH = 'TheChromeBranch'
29 DEFAULT_VERSION_STRING = 'TheVersionString'
30 DEFAULT_BOARD = 'TheBoard'
31 DEFAULT_BOT_NAME = 'TheCoolBot'
33 # Access to protected member.
34 # pylint: disable=W0212
36 DEFAULT_OPTIONS = cros_test_lib.EasyAttr(
37 archive_base=DEFAULT_ARCHIVE_BASE,
38 buildroot=DEFAULT_BUILDROOT,
39 buildnumber=DEFAULT_BUILDNUMBER,
41 branch=DEFAULT_BRANCH,
46 DEFAULT_CONFIG = cbuildbot_config._config(
47 name=DEFAULT_BOT_NAME,
49 boards=[DEFAULT_BOARD],
51 child_configs=[cbuildbot_config._config(name='foo', postsync_patch=False,
53 cbuildbot_config._config(name='bar', postsync_patch=False,
58 DEFAULT_VERSION = '6543.2.1'
61 def _ExtendDefaultOptions(**kwargs):
62 """Extend DEFAULT_OPTIONS with keys/values in kwargs."""
63 options_kwargs = DEFAULT_OPTIONS.copy()
64 options_kwargs.update(kwargs)
65 return cros_test_lib.EasyAttr(**options_kwargs)
68 def _ExtendDefaultConfig(**kwargs):
69 """Extend DEFAULT_CONFIG with keys/values in kwargs."""
70 config_kwargs = DEFAULT_CONFIG.copy()
71 config_kwargs.update(kwargs)
72 return cbuildbot_config._config(**config_kwargs)
75 class ExceptionsTest(cros_test_lib.TestCase):
76 """Test that the exceptions in the module are sane."""
78 def _TestException(self, err, expected_startswith):
79 """Test that str and pickle behavior of |err| are as expected."""
80 err2 = cPickle.loads(cPickle.dumps(err, cPickle.HIGHEST_PROTOCOL))
82 self.assertTrue(str(err).startswith(expected_startswith))
83 self.assertEqual(str(err), str(err2))
85 def testParallelAttributeError(self):
86 """Test ParallelAttributeError message and pickle behavior."""
87 err1 = cbuildbot_run.ParallelAttributeError('SomeAttr')
88 self._TestException(err1, 'No such parallel run attribute')
90 err2 = cbuildbot_run.ParallelAttributeError('SomeAttr', 'SomeBoard',
92 self._TestException(err2, 'No such board-specific parallel run attribute')
94 def testAttrSepCountError(self):
95 """Test AttrSepCountError message and pickle behavior."""
96 err1 = cbuildbot_run.AttrSepCountError('SomeAttr')
97 self._TestException(err1, 'Attribute name has an unexpected number')
99 def testAttrNotPickleableError(self):
100 """Test AttrNotPickleableError message and pickle behavior."""
101 err1 = cbuildbot_run.AttrNotPickleableError('SomeAttr', 'SomeValue')
102 self._TestException(err1, 'Run attribute "SomeAttr" value cannot')
105 # TODO(mtennant): Turn this into a PartialMock.
106 class _BuilderRunTestCase(cros_test_lib.MockTestCase):
107 """Provide methods for creating BuilderRun or ChildBuilderRun."""
110 self._manager = parallel.Manager()
112 # Mimic entering a 'with' statement.
113 self._manager.__enter__()
116 # Mimic exiting a 'with' statement.
117 self._manager.__exit__(None, None, None)
119 def _NewRunAttributes(self):
120 return cbuildbot_run.RunAttributes(self._manager)
122 def _NewBuilderRun(self, options=None, config=None):
123 """Create a BuilderRun objection from options and config values.
126 options: Specify options or default to DEFAULT_OPTIONS.
127 config: Specify build config or default to DEFAULT_CONFIG.
132 options = options or DEFAULT_OPTIONS
133 config = config or DEFAULT_CONFIG
135 return cbuildbot_run.BuilderRun(options, config, self._manager)
137 def _NewChildBuilderRun(self, child_index, options=None, config=None):
138 """Create a ChildBuilderRun objection from options and config values.
141 child_index: Index of child config to use within config.
142 options: Specify options or default to DEFAULT_OPTIONS.
143 config: Specify build config or default to DEFAULT_CONFIG.
146 ChildBuilderRun object.
148 run = self._NewBuilderRun(options, config)
149 return cbuildbot_run.ChildBuilderRun(run, child_index)
152 class BuilderRunPickleTest(_BuilderRunTestCase):
153 """Make sure BuilderRun objects can be pickled."""
156 self.real_config = cbuildbot_config.config['x86-alex-release-group']
157 self.PatchObject(cbuildbot_run._BuilderRunBase, 'GetVersion',
158 return_value=DEFAULT_VERSION)
160 def _TestPickle(self, run1):
161 self.assertEquals(DEFAULT_VERSION, run1.GetVersion())
162 run1.attrs.release_tag = 'TheReleaseTag'
164 # Accessing a method on BuilderRun has special behavior, so access and
165 # use one before pickling.
166 patch_after_sync = run1.ShouldPatchAfterSync()
168 # Access the archive object before pickling, too.
169 upload_url = run1.GetArchive().upload_url
171 # Pickle and unpickle run1 into run2.
172 run2 = cPickle.loads(cPickle.dumps(run1, cPickle.HIGHEST_PROTOCOL))
174 self.assertEquals(run1.buildnumber, run2.buildnumber)
175 self.assertEquals(run1.config.boards, run2.config.boards)
176 self.assertEquals(run1.options.branch, run2.options.branch)
177 self.assertEquals(run1.attrs.release_tag, run2.attrs.release_tag)
178 self.assertRaises(AttributeError, getattr, run1.attrs, 'manifest_manager')
179 self.assertRaises(AttributeError, getattr, run2.attrs, 'manifest_manager')
180 self.assertEquals(patch_after_sync, run2.ShouldPatchAfterSync())
181 self.assertEquals(upload_url, run2.GetArchive().upload_url)
183 # The attrs objects should be identical.
184 self.assertTrue(run1.attrs is run2.attrs)
186 # And the run objects themselves are different.
187 self.assertFalse(run1 is run2)
189 def testPickleBuilderRun(self):
190 self._TestPickle(self._NewBuilderRun(config=self.real_config))
192 def testPickleChildBuilderRun(self):
193 self._TestPickle(self._NewChildBuilderRun(0, config=self.real_config))
196 class BuilderRunTest(_BuilderRunTestCase):
197 """Test the BuilderRun class."""
200 with mock.patch.object(cbuildbot_run._BuilderRunBase, 'GetVersion') as m:
201 m.return_value = DEFAULT_VERSION
203 run = self._NewBuilderRun()
204 self.assertEquals(DEFAULT_BUILDROOT, run.buildroot)
205 self.assertEquals(DEFAULT_BUILDNUMBER, run.buildnumber)
206 self.assertEquals(DEFAULT_BRANCH, run.manifest_branch)
207 self.assertEquals(DEFAULT_OPTIONS, run.options)
208 self.assertEquals(DEFAULT_CONFIG, run.config)
209 self.assertTrue(isinstance(run.attrs, cbuildbot_run.RunAttributes))
210 self.assertTrue(isinstance(run.GetArchive(),
211 cbuildbot_run.cbuildbot_archive.Archive))
213 # Make sure methods behave normally, since BuilderRun messes with them.
214 meth1 = run.GetVersionInfo
215 meth2 = run.GetVersionInfo
216 self.assertEqual(meth1.__name__, meth2.__name__)
218 # We actually do not support identity and equality checks right now.
219 self.assertNotEqual(meth1, meth2)
220 self.assertFalse(meth1 is meth2)
222 def testOptions(self):
223 options = _ExtendDefaultOptions(foo=True, bar=10)
224 run = self._NewBuilderRun(options=options)
226 self.assertEquals(True, run.options.foo)
227 self.assertEquals(10, run.options.__getattr__('bar'))
228 self.assertRaises(AttributeError, run.options.__getattr__, 'baz')
230 def testConfig(self):
231 config = _ExtendDefaultConfig(foo=True, bar=10)
232 run = self._NewBuilderRun(config=config)
234 self.assertEquals(True, run.config.foo)
235 self.assertEquals(10, run.config.__getattr__('bar'))
236 self.assertRaises(AttributeError, run.config.__getattr__, 'baz')
239 run = self._NewBuilderRun()
241 # manifest_manager is a valid run attribute. It gives Attribute error
242 # if accessed before being set, but thereafter works fine.
243 self.assertRaises(AttributeError, run.attrs.__getattribute__,
245 run.attrs.manifest_manager = 'foo'
246 self.assertEquals('foo', run.attrs.manifest_manager)
247 self.assertEquals('foo', run.attrs.__getattribute__('manifest_manager'))
249 # foobar is not a valid run attribute. It gives AttributeError when
250 # accessed or changed.
251 self.assertRaises(AttributeError, run.attrs.__getattribute__, 'foobar')
252 self.assertRaises(AttributeError, run.attrs.__setattr__, 'foobar', 'foo')
254 def testArchive(self):
255 run = self._NewBuilderRun()
257 with mock.patch.object(cbuildbot_run._BuilderRunBase, 'GetVersion') as m:
258 m.return_value = DEFAULT_VERSION
260 archive = run.GetArchive()
262 # Check archive.archive_path.
263 expected = ('%s/%s/%s/%s' %
265 cbuildbot_run.cbuildbot_archive.Archive._BUILDBOT_ARCHIVE,
266 DEFAULT_BOT_NAME, DEFAULT_VERSION))
267 self.assertEqual(expected, archive.archive_path)
269 # Check archive.upload_url.
270 expected = '%s/%s/%s' % (DEFAULT_ARCHIVE_BASE, DEFAULT_BOT_NAME,
272 self.assertEqual(expected, archive.upload_url)
274 # Check archive.download_url.
275 expected = ('%s%s/%s/%s' %
276 (cbuildbot_run.cbuildbot_archive.gs.PRIVATE_BASE_HTTPS_URL,
277 DEFAULT_ARCHIVE_GS_PATH, DEFAULT_BOT_NAME, DEFAULT_VERSION))
278 self.assertEqual(expected, archive.download_url)
280 def _RunAccessor(self, method_name, options_dict, config_dict):
281 """Run the given accessor method of the BuilderRun class.
283 Create a BuilderRun object with the options and config provided and
284 then return the result of calling the given method on it.
287 method_name: A BuilderRun method to call, specified by name.
288 options_dict: Extend default options with this.
289 config_dict: Extend default config with this.
292 Result of calling the given method.
294 options = _ExtendDefaultOptions(**options_dict)
295 config = _ExtendDefaultConfig(**config_dict)
296 run = self._NewBuilderRun(options=options, config=config)
297 method = getattr(run, method_name)
298 self.assertEqual(method.__name__, method_name)
301 def testDualEnableSetting(self):
303 'prebuilts': 'ShouldUploadPrebuilts',
304 'postsync_patch': 'ShouldPatchAfterSync',
307 # Both option and config enabled should result in True.
308 # Create truth table with three variables in this order:
309 # <key> option value, <key> config value (e.g. <key> == 'prebuilts').
310 truth_table = cros_test_lib.TruthTable(inputs=[(True, True)])
312 for inputs in truth_table:
313 option_val, config_val = inputs
314 for key, accessor in settings.iteritems():
316 self._RunAccessor(accessor, {key: option_val}, {key: config_val}),
317 truth_table.GetOutput(inputs))
319 def testShouldReexecAfterSync(self):
320 # If option and config have postsync_reexec enabled, and this file is not
321 # in the build root, then we expect ShouldReexecAfterSync to return True.
323 # Construct a truth table across three variables in this order:
324 # postsync_reexec option value, postsync_reexec config value, same_root.
325 truth_table = cros_test_lib.TruthTable(inputs=[(True, True, False)])
327 for inputs in truth_table:
328 option_val, config_val, same_root = inputs
331 build_root = os.path.dirname(os.path.dirname(__file__))
333 build_root = DEFAULT_BUILDROOT
335 result = self._RunAccessor(
336 'ShouldReexecAfterSync',
337 {'postsync_reexec': option_val, 'buildroot': build_root},
338 {'postsync_reexec': config_val})
340 self.assertEquals(result, truth_table.GetOutput(inputs))
343 class GetVersionTest(_BuilderRunTestCase):
344 """Test the GetVersion and GetVersionInfo methods of BuilderRun class."""
345 # Access to protected member.
346 # pylint: disable=W0212
348 def testGetVersionInfo(self):
351 with mock.patch('cbuildbot_run.manifest_version.VersionInfo.from_repo',
352 return_value=verinfo) as m:
353 result = cbuildbot_run._BuilderRunBase.GetVersionInfo(DEFAULT_BUILDROOT)
354 self.assertEquals(result, verinfo)
356 m.assert_called_once_with(DEFAULT_BUILDROOT)
358 def _TestGetVersionReleaseTag(self, release_tag):
359 with mock.patch.object(cbuildbot_run._BuilderRunBase,
360 'GetVersionInfo') as m:
361 verinfo_mock = mock.Mock()
362 verinfo_mock.chrome_branch = DEFAULT_CHROME_BRANCH
363 verinfo_mock.VersionString = mock.Mock(return_value='VS')
364 m.return_value = verinfo_mock
366 # Prepare a real BuilderRun object with a release tag.
367 run = self._NewBuilderRun()
368 run.attrs.release_tag = release_tag
370 # Run the test return the result.
371 result = run.GetVersion()
372 m.assert_called_once_with(DEFAULT_BUILDROOT)
373 if release_tag is None:
374 verinfo_mock.VersionString.assert_called_once()
378 def testGetVersionReleaseTag(self):
379 result = self._TestGetVersionReleaseTag('RT')
380 self.assertEquals('R%s-%s' % (DEFAULT_CHROME_BRANCH, 'RT'), result)
382 def testGetVersionNoReleaseTag(self):
383 result = self._TestGetVersionReleaseTag(None)
384 expected_result = ('R%s-%s-b%s' %
385 (DEFAULT_CHROME_BRANCH, 'VS', DEFAULT_BUILDNUMBER))
386 self.assertEquals(result, expected_result)
389 class ChildBuilderRunTest(_BuilderRunTestCase):
390 """Test the ChildBuilderRun class"""
393 with mock.patch.object(cbuildbot_run._BuilderRunBase, 'GetVersion') as m:
394 m.return_value = DEFAULT_VERSION
396 crun = self._NewChildBuilderRun(0)
397 self.assertEquals(DEFAULT_BUILDROOT, crun.buildroot)
398 self.assertEquals(DEFAULT_BUILDNUMBER, crun.buildnumber)
399 self.assertEquals(DEFAULT_BRANCH, crun.manifest_branch)
400 self.assertEquals(DEFAULT_OPTIONS, crun.options)
401 self.assertEquals(DEFAULT_CONFIG.child_configs[0], crun.config)
402 self.assertEquals('foo', crun.config.name)
403 self.assertTrue(isinstance(crun.attrs, cbuildbot_run.RunAttributes))
404 self.assertTrue(isinstance(crun.GetArchive(),
405 cbuildbot_run.cbuildbot_archive.Archive))
407 # Make sure methods behave normally, since BuilderRun messes with them.
408 meth1 = crun.GetVersionInfo
409 meth2 = crun.GetVersionInfo
410 self.assertEqual(meth1.__name__, meth2.__name__)
412 # We actually do not support identity and equality checks right now.
413 self.assertNotEqual(meth1, meth2)
414 self.assertFalse(meth1 is meth2)
417 class RunAttributesTest(_BuilderRunTestCase):
418 """Test the RunAttributes class."""
421 TARGET = 'SomeConfigName'
422 VALUE = 'AnyValueWillDo'
424 # Any valid board-specific attribute will work here.
425 BATTR = 'breakpad_symbols_generated'
426 UNIQUIFIED_BATTR = cbuildbot_run.RunAttributes._GetBoardAttrName(
427 BATTR, BOARD, TARGET)
429 def testRegisterBoardTarget(self):
430 """Test behavior of attributes before and after registering board target."""
431 ra = self._NewRunAttributes()
433 self.assertFalse(ra.HasBoardParallel(self.BATTR, self.BOARD, self.TARGET))
434 self.assertFalse(ra.HasParallel(self.UNIQUIFIED_BATTR))
436 ra.RegisterBoardAttrs(self.BOARD, self.TARGET)
438 self.assertFalse(ra.HasBoardParallel(self.BATTR, self.BOARD, self.TARGET))
439 self.assertFalse(ra.HasParallel(self.UNIQUIFIED_BATTR))
441 ra.SetBoardParallel(self.BATTR, 'TheValue', self.BOARD, self.TARGET)
443 self.assertTrue(ra.HasBoardParallel(self.BATTR, self.BOARD, self.TARGET))
444 self.assertTrue(ra.HasParallel(self.UNIQUIFIED_BATTR))
446 def testSetGet(self):
447 """Test simple set/get of regular and parallel run attributes."""
448 ra = self._NewRunAttributes()
451 # Set/Get a regular run attribute using direct access.
452 ra.release_tag = value
453 self.assertEqual(value, ra.release_tag)
455 # Set/Get of a parallel run attribute using direct access fails.
456 self.assertRaises(AttributeError, setattr, ra, 'unittest_value', value)
457 self.assertRaises(AttributeError, getattr, ra, 'unittest_value')
459 # Set/Get of a parallel run attribute with supported interface.
460 ra.SetParallel('unittest_value', value)
461 self.assertEqual(value, ra.GetParallel('unittest_value'))
463 # Set/Get a board parallel run attribute, testing both the encouraged
464 # interface and the underlying interface.
465 ra.RegisterBoardAttrs(self.BOARD, self.TARGET)
466 ra.SetBoardParallel(self.BATTR, value, self.BOARD, self.TARGET)
467 self.assertEqual(value,
468 ra.GetBoardParallel(self.BATTR, self.BOARD, self.TARGET))
469 self.assertEqual(value,
470 ra.GetParallel(self.UNIQUIFIED_BATTR))
472 def testSetDefault(self):
473 """Test setting default value of parallel run attributes."""
474 ra = self._NewRunAttributes()
477 # Attribute starts off not set.
478 self.assertFalse(ra.HasParallel('unittest_value'))
480 # Use SetParallelDefault to set it.
481 ra.SetParallelDefault('unittest_value', value)
482 self.assertTrue(ra.HasParallel('unittest_value'))
483 self.assertEqual(value, ra.GetParallel('unittest_value'))
485 # Calling SetParallelDefault again has no effect.
486 ra.SetParallelDefault('unittest_value', 'junk')
487 self.assertTrue(ra.HasParallel('unittest_value'))
488 self.assertEqual(value, ra.GetParallel('unittest_value'))
490 # Run through same sequence for a board-specific attribute.
492 # Attribute starts off not set.
493 self.assertFalse(ra.HasBoardParallel(self.BATTR, self.BOARD, self.TARGET))
494 ra.RegisterBoardAttrs(self.BOARD, self.TARGET)
495 self.assertFalse(ra.HasBoardParallel(self.BATTR, self.BOARD, self.TARGET))
497 # Use SetBoardParallelDefault to set it.
498 ra.SetBoardParallelDefault(self.BATTR, value, 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))
503 # Calling SetBoardParallelDefault again has no effect.
504 ra.SetBoardParallelDefault(self.BATTR, 'junk', self.BOARD, self.TARGET)
505 self.assertTrue(ra.HasBoardParallel(self.BATTR, self.BOARD, self.TARGET))
506 self.assertEqual(value,
507 ra.GetBoardParallel(self.BATTR, self.BOARD, self.TARGET))
509 def testAttributeError(self):
510 """Test accessing run attributes that do not exist."""
511 ra = self._NewRunAttributes()
514 # Set/Get on made up attribute name.
515 self.assertRaises(AttributeError, setattr, ra, 'foo', value)
516 self.assertRaises(AttributeError, getattr, ra, 'foo')
518 # self.UNIQUIFIED_BATTR is valid, but only if board/target registered first.
519 self.assertRaises(AttributeError, ra.GetBoardParallel,
520 self.BATTR, self.BOARD, self.TARGET)
521 self.assertRaises(AttributeError, ra.SetParallel,
522 self.UNIQUIFIED_BATTR, value)
523 self.assertRaises(AttributeError, ra.GetParallel, self.UNIQUIFIED_BATTR)
526 class BoardRunAttributesTest(_BuilderRunTestCase):
527 """Test the BoardRunAttributes class."""
530 TARGET = 'SomeConfigName'
531 VALUE = 'AnyValueWillDo'
533 # Any valid board-specific attribute will work here.
534 BATTR = 'breakpad_symbols_generated'
536 class _SetAttr(object):
537 """Stage-like class to set attr on a BoardRunAttributes obj."""
538 def __init__(self, bra, attr, value, delay=1):
546 time.sleep(self.delay)
547 self.bra.SetParallel(self.attr, self.value)
549 class _WaitForAttr(object):
550 """Stage-like class to wait for attr on BoardRunAttributes obj."""
551 def __init__(self, bra, attr, expected_value, timeout=10):
554 self.expected_value = expected_value
555 self.timeout = timeout
557 def GetParallel(self):
558 return self.bra.GetParallel(self.attr, timeout=self.timeout)
560 class _CheckWaitForAttr(_WaitForAttr):
561 """Stage-like class to wait for then check attr on BoardRunAttributes."""
563 value = self.GetParallel()
564 assert value == self.expected_value, \
565 ('For run attribute %s expected value %r but got %r.' %
566 (self.attr, self.expected_value, value))
568 class _TimeoutWaitForAttr(_WaitForAttr):
569 """Stage-like class to time-out waiting for attr on BoardRunAttributes."""
573 assert False, 'Expected AttrTimeoutError'
574 except cbuildbot_run.AttrTimeoutError:
578 self.ra = self._NewRunAttributes()
579 self.bra = self.ra.RegisterBoardAttrs(self.BOARD, self.TARGET)
581 def _TestParallelSetGet(self, stage_args):
582 """Helper to run "stages" in parallel, according to |stage_args|.
585 stage_args: List of tuples of the form (stage_object, extra_args, ...)
586 where stage_object has a Run method which takes a BoardRunAttributes
587 object as the first argument and extra_args for the remaining arguments.
589 stages = [a[0](self.bra, *a[1:]) for a in stage_args]
590 steps = [stage.Run for stage in stages]
592 parallel.RunParallelSteps(steps)
594 def testParallelSetGetFast(self):
595 """Pass the parallel run attribute around with no delay."""
597 (self._CheckWaitForAttr, self.BATTR, self.VALUE),
598 (self._SetAttr, self.BATTR, self.VALUE),
600 self._TestParallelSetGet(stage_args)
601 self.assertRaises(AttributeError,
602 getattr, self.bra, self.BATTR)
603 self.assertEqual(self.VALUE, self.bra.GetParallel(self.BATTR))
605 def testParallelSetGetSlow(self):
606 """Pass the parallel run attribute around with a delay."""
608 (self._SetAttr, self.BATTR, self.VALUE, 10),
609 (self._TimeoutWaitForAttr, self.BATTR, self.VALUE, 2),
611 self._TestParallelSetGet(stage_args)
612 self.assertEqual(self.VALUE, self.bra.GetParallel(self.BATTR))
614 def testParallelSetGetManyGets(self):
615 """Set the parallel run attribute in one stage, access in many stages."""
617 (self._SetAttr, self.BATTR, self.VALUE, 8),
618 (self._CheckWaitForAttr, self.BATTR, self.VALUE, 16),
619 (self._CheckWaitForAttr, self.BATTR, self.VALUE, 16),
620 (self._CheckWaitForAttr, self.BATTR, self.VALUE, 16),
621 (self._TimeoutWaitForAttr, self.BATTR, self.VALUE, 1),
623 self._TestParallelSetGet(stage_args)
624 self.assertEqual(self.VALUE, self.bra.GetParallel(self.BATTR))
626 def testParallelSetGetManySets(self):
627 """Set the parallel run attribute in many stages, access in one stage."""
628 # Three "stages" set the value, with increasing delays. The stage that
629 # checks the value should get the first value set.
631 (self._SetAttr, self.BATTR, self.VALUE + '1', 1),
632 (self._SetAttr, self.BATTR, self.VALUE + '2', 11),
633 (self._CheckWaitForAttr, self.BATTR, self.VALUE + '1', 12),
635 self._TestParallelSetGet(stage_args)
636 self.assertEqual(self.VALUE + '2', self.bra.GetParallel(self.BATTR))
638 def testSetGet(self):
639 """Test that board-specific attrs do not work with set/get directly."""
640 self.assertRaises(AttributeError, setattr,
641 self.bra, 'breakpad_symbols_generated', self.VALUE)
642 self.assertRaises(AttributeError, getattr,
643 self.bra, 'breakpad_symbols_generated')
645 def testAccessRegularRunAttr(self):
646 """Test that regular attributes are not known to BoardRunAttributes."""
647 self.assertRaises(AttributeError, getattr, self.bra, 'release_tag')
648 self.assertRaises(AttributeError, setattr, self.bra, 'release_tag', 'foo')
651 if __name__ == '__main__':
652 cros_test_lib.main(level=logging.DEBUG)