blob: 5894185a97acb2c401fd17553327f511fba6f4fd [file] [log] [blame]
#!/usr/bin/python
# Copyright (c) 2011-2012 The Chromium OS Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Module that contains unittests for validation_pool module."""
import contextlib
import copy
import functools
import itertools
import mox
import os
import pickle
import sys
import time
import unittest
import urllib
import constants
sys.path.insert(0, constants.SOURCE_ROOT)
from chromite.buildbot import gerrit_helper
from chromite.buildbot import patch as cros_patch
from chromite.buildbot import patch_unittest
from chromite.buildbot import repository
from chromite.buildbot import validation_pool
from chromite.lib import cros_build_lib
from chromite.lib import cros_test_lib
_GetNumber = iter(itertools.count()).next
class MockPatch(mox.MockObject):
def __eq__(self, other):
return self.id == getattr(other, 'id')
def GetTestJson(change_id=None):
"""Get usable fake Gerrit patch json data
Args:
change_id: If given, force this ChangeId
"""
data = copy.deepcopy(patch_unittest.FAKE_PATCH_JSON)
if change_id is not None:
data['id'] = str(change_id)
return data
# pylint: disable=W0212,R0904
class TestValidationPool(mox.MoxTestBase):
"""Tests methods in validation_pool.ValidationPool."""
class base_mixin(object):
def setUp(self):
mox.MoxTestBase.setUp(self)
self.mox.StubOutWithMock(validation_pool, '_RunCommand')
self.mox.StubOutWithMock(time, 'sleep')
self.mox.StubOutWithMock(validation_pool.ValidationPool, '_IsTreeOpen')
# Supress all gerrit access; having this occur is generally a sign
# the code is either misbehaving, or the tests are bad.
self.mox.StubOutWithMock(gerrit_helper.GerritHelper, 'Query')
self._patch_counter = (itertools.count(1)).next
self.build_root = 'fakebuildroot'
def MockPatch(self, change_id=None, patch_number=None, is_merged=False,
project='chromiumos/chromite'):
# pylint: disable=W0201
# We have to use a custom mock class to fix some brain behaviour of
# pymox where multiple separate mocks can easily equal each other
# (or not; the behaviour varies depending on stubs used).
patch = MockPatch(cros_patch.GerritPatch)
self.mox._mock_objects.append(patch)
patch.internal = False
if change_id is None:
change_id = self._patch_counter()
patch.change_id = patch.id = 'ChangeId%i' % (change_id,)
patch.gerrit_number = change_id
patch.patch_number = (patch_number if patch_number is not None else
_GetNumber())
patch.url = 'fake_url/%i' % (change_id,)
patch.apply_error_message = None
patch.project = project
patch.sha1 = 'sha1-%s' % (patch.change_id,)
patch.IsAlreadyMerged = lambda:is_merged
return patch
def GetPatches(self, how_many=1):
l = [self.MockPatch() for _ in xrange(how_many)]
if how_many == 1:
return l[0]
return l
def MakeHelper(self, internal=None, external=None):
if internal:
internal = self.mox.CreateMock(gerrit_helper.GerritHelper)
internal.version = '2.1'
internal.internal = True
if external:
external = self.mox.CreateMock(gerrit_helper.GerritHelper)
external.internal = False
external.version = '2.1'
return validation_pool.HelperPool(internal=internal, external=external)
# pylint: disable=W0212,R0904
class TestPatchSeries(base_mixin, mox.MoxTestBase):
"""Tests the core resolution and applying logic of
validation_pool.ValidationPool."""
def setUp(self):
base_mixin.setUp(self)
# All tests should set their content merging projects via
# SetContentMergingProjects since FindContentMergingProjects
# requires admin rights in gerrit.
self.mox.StubOutWithMock(gerrit_helper.GerritHelper,
'FindContentMergingProjects')
@staticmethod
def SetContentMergingProjects(series, projects=(), internal=False):
helper = series._helper_pool.GetHelper(internal)
series._content_merging[helper] = frozenset(projects)
@contextlib.contextmanager
def _ValidateTransactionCall(self, build_root, _changes):
self.assertEqual(build_root, self.build_root)
yield
def GetPatchSeries(self, helper_pool=None, force_content_merging=False):
if helper_pool is None:
helper_pool = self.MakeHelper(internal=True, external=True)
series = validation_pool.PatchSeries(helper_pool, force_content_merging)
# Suppress transactions.
series._Transaction = self._ValidateTransactionCall
return series
def assertPath(self, _patch, return_value, path):
self.assertEqual(path, self.build_root)
if isinstance(return_value, Exception):
raise return_value
return return_value
def SetPatchDeps(self, patch, parents=(), cq=()):
patch.GerritDependencies = functools.partial(
self.assertPath, patch, parents)
patch.PaladinDependencies = functools.partial(
self.assertPath, patch, cq)
patch.Fetch = functools.partial(
self.assertPath, patch, patch.sha1)
def assertResults(self, series, changes, applied=(), failed_tot=(),
failed_inflight=(), frozen=True, dryrun=False):
# Convenience; set the content pool as necessary.
for internal in set(x.internal for x in changes):
helper = series._helper_pool.GetHelper(internal)
series._content_merging.setdefault(helper, frozenset())
manifest = self.mox.CreateMock(cros_build_lib.ManifestCheckout)
manifest.root = self.build_root
result = series.Apply(manifest.root, changes, dryrun=dryrun,
frozen=frozen, manifest=manifest)
_GetIds = lambda seq:[x.id for x in seq]
_GetFailedIds = lambda seq:_GetIds(x.patch for x in seq)
applied_result = _GetIds(result[0])
failed_tot_result, failed_inflight_result = map(_GetFailedIds, result[1:])
applied = _GetIds(applied)
failed_tot = _GetIds(failed_tot)
failed_inflight = _GetIds(failed_inflight)
self.assertEqual(
[applied, failed_tot, failed_inflight],
[applied_result, failed_tot_result, failed_inflight_result])
def testApplyWithDeps(self):
"""Test that we can apply changes correctly and respect deps.
This tests a simple out-of-order change where change1 depends on change2
but tries to get applied before change2. What should happen is that
we should notice change2 is a dep of change1 and apply it first.
"""
series = self.GetPatchSeries()
patch1, patch2 = patches = self.GetPatches(2)
self.SetPatchDeps(patch2)
self.SetPatchDeps(patch1, [patch2.id])
patch2.Apply(self.build_root, trivial=True)
patch1.Apply(self.build_root, trivial=True)
self.mox.ReplayAll()
self.assertResults(series, patches, [patch2, patch1])
self.mox.VerifyAll()
@staticmethod
def _SetQuery(series, change):
helper = series._helper_pool.GetHelper(change.internal)
return helper.QuerySingleRecord(change.id, must_match=True)
def testApplyMissingDep(self):
"""Test that we don't try to apply a change without met dependencies.
Patch2 is in the validation pool that depends on Patch1 (which is not)
Nothing should get applied.
"""
series = self.GetPatchSeries()
patch1, patch2 = self.GetPatches(2)
self.SetPatchDeps(patch2, [patch1.id])
self._SetQuery(series, patch1).AndReturn(patch1)
self.mox.ReplayAll()
self.assertResults(series, [patch2],
[], [patch2])
self.mox.VerifyAll()
def testApplyWithCommittedDeps(self):
"""Test that we apply a change with dependency already committed."""
series = self.GetPatchSeries()
patch1 = self.MockPatch(1, is_merged=True)
patch2 = self.MockPatch(2)
self.SetPatchDeps(patch2, [patch1.id])
self._SetQuery(series, patch1).AndReturn(patch1)
patch2.Apply(self.build_root, trivial=True)
self.mox.ReplayAll()
self.assertResults(series, [patch2], [patch2])
self.mox.VerifyAll()
def testApplyPartialFailures(self):
"""Test that can apply changes correctly when one change fails to apply.
This tests a simple change order where 1 depends on 2 and 1 fails to apply.
Only 1 should get tried as 2 will abort once it sees that 1 can't be
applied. 3 with no dependencies should go through fine.
Since patch1 fails to apply, we should also get a call to handle the
failure.
"""
series = self.GetPatchSeries()
patch1, patch2, patch3, patch4 = patches = self.GetPatches(4)
self.SetPatchDeps(patch1)
self.SetPatchDeps(patch2, [patch1.id])
self.SetPatchDeps(patch3)
self.SetPatchDeps(patch4)
patch1.Apply(self.build_root, trivial=True).AndRaise(
cros_patch.ApplyPatchException(patch1))
patch3.Apply(self.build_root, trivial=True)
patch4.Apply(self.build_root, trivial=True).AndRaise(
cros_patch.ApplyPatchException(patch1, inflight=True))
self.mox.ReplayAll()
self.assertResults(series, patches,
[patch3], [patch2, patch1], [patch4])
self.mox.VerifyAll()
def testApplyMissingChangeId(self):
"""Test that applies changes correctly with a dep with missing changeid."""
series = self.GetPatchSeries()
patch1, patch2 = patches = self.GetPatches(2)
patch1.GerritDependencies(self.build_root).AndRaise(
cros_patch.BrokenChangeID(patch1, 'Could not find changeid'))
self.SetPatchDeps(patch2)
patch2.Apply(self.build_root, trivial=True)
self.mox.ReplayAll()
self.assertResults(series, patches, [patch2], [patch1], [])
self.mox.VerifyAll()
def testComplexApply(self):
"""More complex deps test.
This tests a total of 2 change chains where the first change we see
only has a partial chain with the 3rd change having the whole chain i.e.
1->2, 3->1->2, 4->nothing. Since we get these in the order 1,2,3,4 the
order we should apply is 2,1,3,4.
This test also checks the patch order to verify that Apply re-orders
correctly based on the chain.
"""
series = self.GetPatchSeries()
patch1, patch2, patch3, patch4, patch5 = patches = self.GetPatches(5)
self.SetPatchDeps(patch1, [patch2.id])
self.SetPatchDeps(patch2)
self.SetPatchDeps(patch3, [patch1.id, patch2.id])
self.SetPatchDeps(patch4, cq=[patch5.id])
self.SetPatchDeps(patch5)
for patch in (patch2, patch1, patch3, patch4, patch5):
patch.Apply(self.build_root, trivial=True)
self.mox.ReplayAll()
self.assertResults(
series, patches, [patch2, patch1, patch3, patch4, patch5])
self.mox.VerifyAll()
def testApplyStandalonePatches(self):
"""Simple apply of two changes with no dependent CL's."""
series = self.GetPatchSeries()
patches = self.GetPatches(3)
for patch in patches:
self.SetPatchDeps(patch)
for patch in patches:
patch.Apply(self.build_root, trivial=True)
self.mox.ReplayAll()
self.assertResults(series, patches, patches)
self.mox.VerifyAll()
# pylint: disable=W0212,R0904
class TestCoreLogic(base_mixin, mox.MoxTestBase):
"""Tests the core resolution and applying logic of
validation_pool.ValidationPool."""
def setUp(self):
base_mixin.setUp(self)
self.mox.StubOutWithMock(gerrit_helper.GerritHelper, '_SqlQuery')
self.mox.StubOutWithMock(gerrit_helper.GerritHelper,
'FindContentMergingProjects')
def MakePool(self, overlays=constants.PUBLIC_OVERLAYS, build_number=1,
builder_name='foon', is_master=True, dryrun=True, **kwds):
handlers = kwds.pop('handlers', False)
kwds.setdefault('helper_pool', validation_pool.HelperPool.SimpleCreate())
kwds.setdefault('changes', [])
pool = validation_pool.ValidationPool(
overlays, build_number, builder_name, is_master, dryrun, **kwds)
self.mox.StubOutWithMock(pool, '_SendNotification')
if handlers:
self.mox.StubOutWithMock(pool, '_HandleApplySuccess')
self.mox.StubOutWithMock(pool, '_HandleApplyFailure')
self.mox.StubOutWithMock(pool, '_HandleCouldNotApply')
self.mox.StubOutWithMock(pool, '_patch_series')
return pool
def MakeFailure(self, patch, inflight=True):
return cros_patch.ApplyPatchException(patch, inflight=inflight)
def GetPool(self, changes, applied=(), tot=(),
inflight=(), dryrun=True, **kwds):
pool = self.MakePool(changes=changes, **kwds)
applied = list(applied)
tot = [self.MakeFailure(x, inflight=False) for x in tot]
inflight = [self.MakeFailure(x, inflight=True) for x in inflight]
pool._patch_series.Apply(
self.build_root, changes, dryrun, manifest=mox.IgnoreArg()
).AndReturn((applied, tot, inflight))
for patch in applied:
pool._HandleApplySuccess(patch).AndReturn(None)
if tot:
pool._HandleApplyFailure(tot).AndReturn(None)
# We stash this on the pool object so we can reuse it during validation.
# We could stash this in the test instances, but that would break
# for any tests that do multiple pool instances.
pool._test_data = (changes, applied, tot, inflight)
return pool
def runApply(self, pool, result):
self.assertEqual(result, pool.ApplyPoolIntoRepo(self.build_root))
self.assertEqual(pool.changes, pool._test_data[1])
failed_inflight = pool.changes_that_failed_to_apply_earlier
expected_inflight = set(pool._test_data[3])
# Intersect the results, since it's possible there were results failed
# results that weren't related to the ApplyPoolIntoRepo call.
self.assertEqual(set(failed_inflight).intersection(expected_inflight),
expected_inflight)
# Ensure that no old code/pathways are setting apply_error_message.
for patch in pool._test_data[0]:
if getattr(patch, 'apply_error_message', None) is not None:
raise AssertionError(
"patch %s has an apply_error_message that is not None: %s"
% (patch, patch.apply_error_message))
self.assertEqual(pool.changes, pool._test_data[1])
def testPatchSeriesInteraction(self):
"""Verify the interaction between PatchSeries and ValidationPool.
Effectively, this validates data going into PatchSeries, and coming back
out; verifies the hand off to _Handle* functions, but no deeper.
"""
patches = self.GetPatches(3)
apply_pool = self.GetPool(patches, applied=patches, handlers=True)
all_inflight = self.GetPool(patches, inflight=patches, handlers=True)
all_tot = self.GetPool(patches, tot=patches, handlers=True)
mixed = self.GetPool(patches, tot=patches[0:1], inflight=patches[1:2],
applied=patches[2:3], handlers=True)
self.mox.ReplayAll()
self.runApply(apply_pool, True)
self.runApply(all_inflight, False)
self.runApply(all_tot, False)
self.runApply(mixed, True)
self.mox.VerifyAll()
def testHandleApplySuccess(self):
"""Validate steps taken for successfull application."""
patch = self.GetPatches(1)
pool = self.MakePool()
pool._SendNotification(patch, mox.StrContains('has picked up your change'))
self.mox.ReplayAll()
pool._HandleApplySuccess(patch)
self.mox.VerifyAll()
def testHandleApplyFailure(self):
failures = [cros_patch.ApplyPatchException(x) for x in self.GetPatches(4)]
notified_patches = failures[:2]
unnotified_patches = failures[2:]
master_pool = self.MakePool(dryrun=False)
slave_pool = self.MakePool(is_master=False)
self.mox.StubOutWithMock(gerrit_helper.GerritHelper, 'RemoveCommitReady')
for failure in notified_patches:
master_pool._SendNotification(
failure.patch,
mox.StrContains('failed to apply your change'),
failure=mox.IgnoreArg())
# This pylint suppressin shouldn't be necessary, but pylint is invalidly
# thinking that the first arg isn't passed in; we suppress it to suppress
# the pylnt bug.
# pylint: disable=E1120
gerrit_helper.GerritHelper.RemoveCommitReady(failure.patch, dryrun=False)
self.mox.ReplayAll()
master_pool._HandleApplyFailure(notified_patches)
slave_pool._HandleApplyFailure(unnotified_patches)
self.mox.VerifyAll()
def testSubmitPoolFailures(self):
pool = self.MakePool(dryrun=False)
patch1, patch2, patch3 = patches = self.GetPatches(3)
failed = self.GetPatches(3)
pool.changes = patches[:]
# While we don't do anything w/ these patches, that's
# intentional; we're verifying that it isn't submitted
# if there is a failure.
pool.changes_that_failed_to_apply_earlier = failed[:]
self.mox.StubOutWithMock(pool, '_SubmitChange')
self.mox.StubOutWithMock(pool, '_HandleCouldNotSubmit')
self.mox.StubOutWithMock(gerrit_helper.GerritHelper, 'IsChangeCommitted')
pool._SubmitChange(patch1).AndReturn(None)
gerrit_helper.GerritHelper.IsChangeCommitted(
str(patch1.gerrit_number), False).AndReturn(True)
pool._SubmitChange(patch2).AndReturn(None)
gerrit_helper.GerritHelper.IsChangeCommitted(
str(patch2.gerrit_number), False).InAnyOrder().AndReturn(False)
pool._HandleCouldNotSubmit(patch2).InAnyOrder()
pool._SubmitChange(patch3).AndRaise(
cros_build_lib.RunCommandError('blah', None))
pool._HandleCouldNotSubmit(patch3).InAnyOrder().AndReturn(None)
pool._IsTreeOpen().AndReturn(True)
self.mox.ReplayAll()
self.assertRaises(validation_pool.FailedToSubmitAllChangesException,
pool.SubmitPool)
self.mox.VerifyAll()
def testSubmitPool(self):
pool = self.MakePool(dryrun=False)
passed = self.GetPatches(3)
failed = self.GetPatches(3)
pool.changes = passed
pool.changes_that_failed_to_apply_earlier = failed[:]
self.mox.StubOutWithMock(pool, '_SubmitChange')
self.mox.StubOutWithMock(pool, '_HandleCouldNotSubmit')
self.mox.StubOutWithMock(pool, '_HandleApplyFailure')
self.mox.StubOutWithMock(gerrit_helper.GerritHelper, 'IsChangeCommitted')
for patch in passed:
pool._SubmitChange(patch).AndReturn(None)
gerrit_helper.GerritHelper.IsChangeCommitted(
str(patch.gerrit_number), False).AndReturn(True)
pool._HandleApplyFailure(failed)
pool._IsTreeOpen().AndReturn(True)
self.mox.ReplayAll()
pool.SubmitPool()
self.mox.VerifyAll()
def testSubmitNonManifestChanges(self):
"""Simple test to make sure we can submit non-manifest changes."""
pool = self.MakePool(dryrun=False)
patch1, patch2 = passed = self.GetPatches(2)
pool.non_manifest_changes = passed[:]
self.mox.StubOutWithMock(pool, '_SubmitChange')
self.mox.StubOutWithMock(pool, '_HandleCouldNotSubmit')
self.mox.StubOutWithMock(gerrit_helper.GerritHelper, 'IsChangeCommitted')
pool._SubmitChange(patch1).AndReturn(None)
gerrit_helper.GerritHelper.IsChangeCommitted(
str(patch1.gerrit_number), False).AndReturn(True)
pool._SubmitChange(patch2).AndReturn(None)
gerrit_helper.GerritHelper.IsChangeCommitted(
str(patch2.gerrit_number), False).AndReturn(True)
pool._IsTreeOpen().AndReturn(True)
self.mox.ReplayAll()
pool.SubmitNonManifestChanges()
self.mox.VerifyAll()
def testGerritSubmit(self):
"""Tests submission review string looks correct."""
pool = self.MakePool(dryrun=False)
patch = self.GetPatches(1)
cmd = ('ssh -p 29418 gerrit.chromium.org gerrit review '
'--submit %i,%i' % (patch.gerrit_number, patch.patch_number))
validation_pool._RunCommand(cmd.split(), False).AndReturn(None)
self.mox.ReplayAll()
pool._SubmitChange(patch)
self.mox.VerifyAll()
def testUnhandledExceptions(self):
"""Test that CQ doesn't loop due to unhandled Exceptions."""
pool = self.MakePool(dryrun=False)
patches = self.GetPatches(2)
pool.changes = patches[:]
class MyException(Exception):
pass
self.mox.StubOutWithMock(pool._patch_series, 'Apply')
# Suppressed because pylint can't tell that we just replaced Apply via mox.
# pylint: disable=E1101
pool._patch_series.Apply(
self.build_root, patches, False, manifest=mox.IgnoreArg()).AndRaise(
MyException)
def _ValidateExceptioN(changes):
for patch in changes:
self.assertTrue(isinstance(patch, validation_pool.InternalCQError),
msg="Expected %s to be type InternalCQError, got %r" %
(patch, type(patch)))
self.assertEqual(set(patches),
set(x.patch for x in changes))
self.mox.ReplayAll()
self.assertRaises(MyException, pool.ApplyPoolIntoRepo, self.build_root)
self.mox.VerifyAll()
# pylint: disable=W0212,R0904
class TestTreeStatus(mox.MoxTestBase):
"""Tests methods in validation_pool.ValidationPool."""
def setUp(self):
mox.MoxTestBase.setUp(self)
self.mox.StubOutWithMock(validation_pool, '_RunCommand')
self.mox.StubOutWithMock(time, 'sleep')
def _TreeStatusFile(self, message, general_state):
"""Returns a file-like object with the status message writtin in it."""
my_response = self.mox.CreateMockAnything()
my_response.json = '{"message": "%s", "general_state": "%s"}' % (
message, general_state)
return my_response
@cros_build_lib.TimeoutDecorator(3)
def _TreeStatusTestHelper(self, tree_status, general_state, expected_return,
retries_500=0, max_timeout=0):
"""Tests whether we return the correct value based on tree_status."""
return_status = self._TreeStatusFile(tree_status, general_state)
self.mox.StubOutWithMock(urllib, 'urlopen')
status_url = 'https://chromiumos-status.appspot.com/current?format=json'
backoff = 1
for _attempt in range(retries_500):
urllib.urlopen(status_url).AndReturn(return_status)
return_status.getcode().AndReturn(500)
time.sleep(backoff)
backoff *= 2
urllib.urlopen(status_url).MultipleTimes().AndReturn(return_status)
if expected_return == False:
self.mox.StubOutWithMock(time, 'time')
time.time().AndReturn(1)
time.time().AndReturn(1)
sleep_timeout = min(max(max_timeout / 5, 1), 30)
x = 0
while x < max_timeout:
time.time().AndReturn(x + 1)
x += sleep_timeout
time.time().AndReturn(max_timeout + 1)
time.sleep(mox.IgnoreArg()).MultipleTimes()
return_status.getcode().MultipleTimes().AndReturn(200)
return_status.read().MultipleTimes().AndReturn(return_status.json)
self.mox.ReplayAll()
self.assertEqual(validation_pool.ValidationPool._IsTreeOpen(max_timeout),
expected_return)
self.mox.VerifyAll()
def testTreeIsOpen(self):
"""Tests that we return True is the tree is open."""
self._TreeStatusTestHelper('Tree is open (flaky bug on flaky builder)',
'open', True)
def testTreeIsOpenAlwaysOnBranches(self):
"""Tests that we return True is the tree is open."""
self.mox.StubOutWithMock(cros_build_lib, 'GetChromiteTrackingBranch')
cros_build_lib.GetChromiteTrackingBranch().AndReturn('release-ooga-booga')
self.mox.ReplayAll()
self.assertTrue(validation_pool.ValidationPool._IsTreeOpen(max_timeout=10))
def testTreeIsClosed(self):
"""Tests that we return false is the tree is closed."""
self._TreeStatusTestHelper('Tree is closed (working on a patch)', 'closed',
False, max_timeout=5)
def testTreeIsOpenWithTimeout(self):
"""Tests that we return True even if we get some failures."""
self._TreeStatusTestHelper('Tree is open (flaky test)', 'open',
True, retries_500=2)
def testTreeIsThrottled(self):
"""Tests that we return false is the tree is throttled."""
self._TreeStatusTestHelper('Tree is throttled (waiting to cycle)',
'throttled', True)
def testTreeStatusWithNetworkFailures(self):
"""Checks for non-500 errors.."""
self._TreeStatusTestHelper('Tree is open (flaky bug on flaky builder)',
'open', True, retries_500=2)
class TestPickling(cros_test_lib.TempDirMixin, unittest.TestCase):
"""Tests to validate pickling of ValidationPool, covering CQ's needs"""
def testSelfCompatibility(self):
"""Verify compatibility of current git HEAD against itself."""
self._CheckTestData(self._GetTestData())
def testToTCompatibility(self):
"""Validate that ToT can use our pickles, and that we can use ToT's data."""
repo = os.path.join(self.tempdir, 'chromite')
reference = os.path.abspath(__file__)
reference = os.path.normpath(os.path.join(reference, '../../'))
repository.CloneGitRepo(repo,
'%s/chromiumos/chromite' % constants.GIT_HTTP_URL,
reference=reference)
code = """
import sys
from chromite.buildbot import validation_pool_unittest
if not hasattr(validation_pool_unittest, 'TestPickling'):
sys.exit(0)
sys.stdout.write(validation_pool_unittest.TestPickling.%s)
"""
# Verify ToT can take our pickle.
cros_build_lib.RunCommandCaptureOutput(
['python', '-c', code % '_CheckTestData(sys.stdin.read())'],
cwd=self.tempdir, print_cmd=False,
input=self._GetTestData())
# Verify we can handle ToT's pickle.
ret = cros_build_lib.RunCommandCaptureOutput(
['python', '-c', code % '_GetTestData()'],
cwd=self.tempdir, print_cmd=False)
self._CheckTestData(ret.output)
@staticmethod
def _GetTestData():
ids = [cros_patch.MakeChangeId() for _ in xrange(3)]
changes = [cros_patch.GerritPatch(GetTestJson(ids[0]), True)]
non_os = [cros_patch.GerritPatch(GetTestJson(ids[1]), False)]
conflicting = [cros_patch.GerritPatch(GetTestJson(ids[2]), True)]
conflicting = [cros_patch.PatchException(x) for x in conflicting]
pool = validation_pool.ValidationPool(
constants.PUBLIC_OVERLAYS, 1, 'testing', True, True,
changes=changes, non_os_changes=non_os,
conflicting_changes=conflicting)
return pickle.dumps([pool, changes, non_os, conflicting])
@staticmethod
def _CheckTestData(data):
results = pickle.loads(data)
pool, changes, non_os, conflicting = results
def _f(source, value, getter=lambda x:x):
assert len(source) == len(value)
for s_item, v_item in zip(source, value):
assert getter(s_item).id == getter(v_item).id
assert getter(s_item).internal == getter(v_item).internal
_f(pool.changes, changes)
_f(pool.non_manifest_changes, non_os)
_f(pool.changes_that_failed_to_apply_earlier, conflicting,
getter=lambda s:getattr(s, 'patch', s))
return ''
if __name__ == '__main__':
cros_build_lib.SetupBasicLogging()
unittest.main()