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.
6 """Test the cbuildbot_run module."""
8 from __future__ import print_function
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
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'
34 # Access to protected member.
35 # pylint: disable=W0212
37 DEFAULT_OPTIONS = cros_test_lib.EasyAttr(
38 archive_base=DEFAULT_ARCHIVE_BASE,
39 buildroot=DEFAULT_BUILDROOT,
40 buildnumber=DEFAULT_BUILDNUMBER,
42 branch=DEFAULT_BRANCH,
47 DEFAULT_CONFIG = cbuildbot_config._config(
48 name=DEFAULT_BOT_NAME,
50 boards=[DEFAULT_BOARD],
52 child_configs=[cbuildbot_config._config(name='foo', postsync_patch=False,
54 cbuildbot_config._config(name='bar', postsync_patch=False,
59 DEFAULT_VERSION = '6543.2.1'
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)
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)
76 class ExceptionsTest(cros_test_lib.TestCase):
77 """Test that the exceptions in the module are sane."""
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))
83 self.assertTrue(str(err).startswith(expected_startswith))
84 self.assertEqual(str(err), str(err2))
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')
91 err2 = cbuildbot_run.ParallelAttributeError('SomeAttr', 'SomeBoard',
93 self._TestException(err2, 'No such board-specific parallel run attribute')
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')
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')
106 # TODO(mtennant): Turn this into a PartialMock.
107 class _BuilderRunTestCase(cros_test_lib.MockTestCase):
108 """Provide methods for creating BuilderRun or ChildBuilderRun."""
111 self._manager = parallel.Manager()
113 # Mimic entering a 'with' statement.
114 self._manager.__enter__()
117 # Mimic exiting a 'with' statement.
118 self._manager.__exit__(None, None, None)
120 def _NewRunAttributes(self):
121 return cbuildbot_run.RunAttributes(self._manager)
123 def _NewBuilderRun(self, options=None, config=None):
124 """Create a BuilderRun objection from options and config values.
127 options: Specify options or default to DEFAULT_OPTIONS.
128 config: Specify build config or default to DEFAULT_CONFIG.
133 options = options or DEFAULT_OPTIONS
134 config = config or DEFAULT_CONFIG
136 return cbuildbot_run.BuilderRun(options, config, self._manager)
138 def _NewChildBuilderRun(self, child_index, options=None, config=None):
139 """Create a ChildBuilderRun objection from options and config values.
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.
147 ChildBuilderRun object.
149 run = self._NewBuilderRun(options, config)
150 return cbuildbot_run.ChildBuilderRun(run, child_index)
153 class BuilderRunPickleTest(_BuilderRunTestCase):
154 """Make sure BuilderRun objects can be pickled."""
157 self.real_config = cbuildbot_config.config['x86-alex-release-group']
158 self.PatchObject(cbuildbot_run._BuilderRunBase, 'GetVersion',
159 return_value=DEFAULT_VERSION)
161 def _TestPickle(self, run1):
162 self.assertEquals(DEFAULT_VERSION, run1.GetVersion())
163 run1.attrs.release_tag = 'TheReleaseTag'
165 # Accessing a method on BuilderRun has special behavior, so access and
166 # use one before pickling.
167 patch_after_sync = run1.ShouldPatchAfterSync()
169 # Access the archive object before pickling, too.
170 upload_url = run1.GetArchive().upload_url
172 # Pickle and unpickle run1 into run2.
173 run2 = cPickle.loads(cPickle.dumps(run1, cPickle.HIGHEST_PROTOCOL))
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)
184 # The attrs objects should be identical.
185 self.assertTrue(run1.attrs is run2.attrs)
187 # And the run objects themselves are different.
188 self.assertFalse(run1 is run2)
190 def testPickleBuilderRun(self):
191 self._TestPickle(self._NewBuilderRun(config=self.real_config))
193 def testPickleChildBuilderRun(self):
194 self._TestPickle(self._NewChildBuilderRun(0, config=self.real_config))
197 class BuilderRunTest(_BuilderRunTestCase):
198 """Test the BuilderRun class."""
201 with mock.patch.object(cbuildbot_run._BuilderRunBase, 'GetVersion') as m:
202 m.return_value = DEFAULT_VERSION
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))
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__)
219 # We actually do not support identity and equality checks right now.
220 self.assertNotEqual(meth1, meth2)
221 self.assertFalse(meth1 is meth2)
223 def testOptions(self):
224 options = _ExtendDefaultOptions(foo=True, bar=10)
225 run = self._NewBuilderRun(options=options)
227 self.assertEquals(True, run.options.foo)
228 self.assertEquals(10, run.options.__getattr__('bar'))
229 self.assertRaises(AttributeError, run.options.__getattr__, 'baz')
231 def testConfig(self):
232 config = _ExtendDefaultConfig(foo=True, bar=10)
233 run = self._NewBuilderRun(config=config)
235 self.assertEquals(True, run.config.foo)
236 self.assertEquals(10, run.config.__getattr__('bar'))
237 self.assertRaises(AttributeError, run.config.__getattr__, 'baz')
240 run = self._NewBuilderRun()
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__,
246 run.attrs.manifest_manager = 'foo'
247 self.assertEquals('foo', run.attrs.manifest_manager)
248 self.assertEquals('foo', run.attrs.__getattribute__('manifest_manager'))
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')
255 def testArchive(self):
256 run = self._NewBuilderRun()
258 with mock.patch.object(cbuildbot_run._BuilderRunBase, 'GetVersion') as m:
259 m.return_value = DEFAULT_VERSION
261 archive = run.GetArchive()
263 # Check archive.archive_path.
264 expected = ('%s/%s/%s/%s' %
266 cbuildbot_run.archive_lib.Archive._BUILDBOT_ARCHIVE,
267 DEFAULT_BOT_NAME, DEFAULT_VERSION))
268 self.assertEqual(expected, archive.archive_path)
270 # Check archive.upload_url.
271 expected = '%s/%s/%s' % (DEFAULT_ARCHIVE_BASE, DEFAULT_BOT_NAME,
273 self.assertEqual(expected, archive.upload_url)
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)
281 def _RunAccessor(self, method_name, options_dict, config_dict):
282 """Run the given accessor method of the BuilderRun class.
284 Create a BuilderRun object with the options and config provided and
285 then return the result of calling the given method on it.
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.
293 Result of calling the given method.
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)
302 def testDualEnableSetting(self):
304 'prebuilts': 'ShouldUploadPrebuilts',
305 'postsync_patch': 'ShouldPatchAfterSync',
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)])
313 for inputs in truth_table:
314 option_val, config_val = inputs
315 for key, accessor in settings.iteritems():
317 self._RunAccessor(accessor, {key: option_val}, {key: config_val}),
318 truth_table.GetOutput(inputs))
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.
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)])
328 for inputs in truth_table:
329 option_val, config_val, same_root = inputs
332 build_root = os.path.dirname(os.path.dirname(__file__))
334 build_root = DEFAULT_BUILDROOT
336 result = self._RunAccessor(
337 'ShouldReexecAfterSync',
338 {'postsync_reexec': option_val, 'buildroot': build_root},
339 {'postsync_reexec': config_val})
341 self.assertEquals(result, truth_table.GetOutput(inputs))
344 class GetVersionTest(_BuilderRunTestCase):
345 """Test the GetVersion and GetVersionInfo methods of BuilderRun class."""
346 # Access to protected member.
347 # pylint: disable=W0212
349 def testGetVersionInfo(self):
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)
357 m.assert_called_once_with(DEFAULT_BUILDROOT)
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
367 # Prepare a real BuilderRun object with a release tag.
368 run = self._NewBuilderRun()
369 run.attrs.release_tag = release_tag
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()
379 def testGetVersionReleaseTag(self):
380 result = self._TestGetVersionReleaseTag('RT')
381 self.assertEquals('R%s-%s' % (DEFAULT_CHROME_BRANCH, 'RT'), result)
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)
390 class ChildBuilderRunTest(_BuilderRunTestCase):
391 """Test the ChildBuilderRun class"""
394 with mock.patch.object(cbuildbot_run._BuilderRunBase, 'GetVersion') as m:
395 m.return_value = DEFAULT_VERSION
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))
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__)
413 # We actually do not support identity and equality checks right now.
414 self.assertNotEqual(meth1, meth2)
415 self.assertFalse(meth1 is meth2)
418 class RunAttributesTest(_BuilderRunTestCase):
419 """Test the RunAttributes class."""
422 TARGET = 'SomeConfigName'
423 VALUE = 'AnyValueWillDo'
425 # Any valid board-specific attribute will work here.
426 BATTR = 'breakpad_symbols_generated'
428 def testRegisterBoardTarget(self):
429 """Test behavior of attributes before and after registering board target."""
430 ra = self._NewRunAttributes()
432 with self.assertRaises(AssertionError):
433 ra.HasBoardParallel(self.BATTR, self.BOARD, self.TARGET)
435 ra.RegisterBoardAttrs(self.BOARD, self.TARGET)
437 self.assertFalse(ra.HasBoardParallel(self.BATTR, self.BOARD, self.TARGET))
439 ra.SetBoardParallel(self.BATTR, 'TheValue', self.BOARD, self.TARGET)
441 self.assertTrue(ra.HasBoardParallel(self.BATTR, self.BOARD, self.TARGET))
443 def testSetGet(self):
444 """Test simple set/get of regular and parallel run attributes."""
445 ra = self._NewRunAttributes()
448 # Set/Get a regular run attribute using direct access.
449 ra.release_tag = value
450 self.assertEqual(value, ra.release_tag)
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')
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'))
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))
467 def testSetDefault(self):
468 """Test setting default value of parallel run attributes."""
469 ra = self._NewRunAttributes()
472 # Attribute starts off not set.
473 self.assertFalse(ra.HasParallel('unittest_value'))
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'))
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'))
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))
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))
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))
503 def testAttributeError(self):
504 """Test accessing run attributes that do not exist."""
505 ra = self._NewRunAttributes()
508 # Set/Get on made up attribute name.
509 self.assertRaises(AttributeError, setattr, ra, 'foo', value)
510 self.assertRaises(AttributeError, getattr, ra, 'foo')
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)
520 class BoardRunAttributesTest(_BuilderRunTestCase):
521 """Test the BoardRunAttributes class."""
524 TARGET = 'SomeConfigName'
525 VALUE = 'AnyValueWillDo'
527 # Any valid board-specific attribute will work here.
528 BATTR = 'breakpad_symbols_generated'
530 class _SetAttr(object):
531 """Stage-like class to set attr on a BoardRunAttributes obj."""
532 def __init__(self, bra, attr, value, delay=1):
540 time.sleep(self.delay)
541 self.bra.SetParallel(self.attr, self.value)
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):
548 self.expected_value = expected_value
549 self.timeout = timeout
551 def GetParallel(self):
552 return self.bra.GetParallel(self.attr, timeout=self.timeout)
554 class _CheckWaitForAttr(_WaitForAttr):
555 """Stage-like class to wait for then check attr on BoardRunAttributes."""
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))
562 class _TimeoutWaitForAttr(_WaitForAttr):
563 """Stage-like class to time-out waiting for attr on BoardRunAttributes."""
567 assert False, 'Expected AttrTimeoutError'
568 except cbuildbot_run.AttrTimeoutError:
572 self.ra = self._NewRunAttributes()
573 self.bra = self.ra.RegisterBoardAttrs(self.BOARD, self.TARGET)
575 def _TestParallelSetGet(self, stage_args):
576 """Helper to run "stages" in parallel, according to |stage_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.
583 stages = [a[0](self.bra, *a[1:]) for a in stage_args]
584 steps = [stage.Run for stage in stages]
586 parallel.RunParallelSteps(steps)
588 def testParallelSetGetFast(self):
589 """Pass the parallel run attribute around with no delay."""
591 (self._CheckWaitForAttr, self.BATTR, self.VALUE),
592 (self._SetAttr, self.BATTR, self.VALUE),
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))
599 def testParallelSetGetSlow(self):
600 """Pass the parallel run attribute around with a delay."""
602 (self._SetAttr, self.BATTR, self.VALUE, 10),
603 (self._TimeoutWaitForAttr, self.BATTR, self.VALUE, 2),
605 self._TestParallelSetGet(stage_args)
606 self.assertEqual(self.VALUE, self.bra.GetParallel(self.BATTR))
608 def testParallelSetGetManyGets(self):
609 """Set the parallel run attribute in one stage, access in many stages."""
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),
617 self._TestParallelSetGet(stage_args)
618 self.assertEqual(self.VALUE, self.bra.GetParallel(self.BATTR))
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.
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),
629 self._TestParallelSetGet(stage_args)
630 self.assertEqual(self.VALUE + '2', self.bra.GetParallel(self.BATTR))
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')
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')
645 if __name__ == '__main__':
646 cros_test_lib.main(level=logging.DEBUG)