blob: eea0e0bf4da72046d5e8caee932f52d15905f1a4 [file] [log] [blame]
# Copyright 2016 The ChromiumOS Authors
# 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 patch_series module."""
import contextlib
import os
from unittest import mock
from chromite.cbuildbot import patch_series
from chromite.lib import config_lib
from chromite.lib import cros_test_lib
from chromite.lib import gerrit
from chromite.lib import parallel_unittest
from chromite.lib import partial_mock
from chromite.lib import patch as cros_patch
from chromite.lib import patch_unittest
class MockManifest:
"""Helper class for Mocking Manifest objects."""
def __init__(self, path, **kwargs) -> None:
self.root = path
for key, attr in kwargs.items():
setattr(self, key, attr)
def FakeFetchChangesForRepo(fetched_changes, by_repo, repo) -> None:
"""Fake version of the "PatchSeries._FetchChangesForRepo" method.
Thes does nothing to the changes and simply copies them into the output
dict.
"""
for c in by_repo[repo]:
fetched_changes[c.id] = c
class FakePatch(partial_mock.PartialMock):
"""Mocks out dependency and fetch methods of GitRepoPatch.
Examples:
set FakePatch.parents and .build_roots per patch, and set
FakePatch.assertEqual to your TestCase's assertEqual method. The
behavior of GerritDependencies, and Fetch` depends on the patch id.
"""
TARGET = "chromite.lib.patch.GitRepoPatch"
ATTRS = ("GerritDependencies", "Fetch")
parents = {}
build_root = None
assertEqual = None
def PreStart(self) -> None:
FakePatch.parents = {}
def PreStop(self) -> None:
FakePatch.build_root = None
FakePatch.assertEqual = None
def GerritDependencies(self, patch):
return [cros_patch.ParsePatchDep(x) for x in self.parents[patch.id]]
def Fetch(self, patch, path):
self._assertPath(patch, path)
return patch.sha1
def _assertPath(self, patch, path) -> None:
# pylint: disable=not-callable
self.assertEqual(path, os.path.join(self.build_root, patch.project))
class FakeGerritPatch(FakePatch):
"""Mocks out the "GerritDependencies" method of GerritPatch.
This is necessary because GerritPatch overrides the GerritDependencies
method.
"""
TARGET = "chromite.lib.patch.GerritPatch"
ATTRS = ("GerritDependencies",)
# pylint: disable=protected-access
# pylint: disable=too-many-ancestors
class PatchSeriesTestCase(
patch_unittest.UploadedLocalPatchTestCase, cros_test_lib.MockTestCase
):
"""Base class for tests that need to test PatchSeries."""
@contextlib.contextmanager
def _ValidateTransactionCall(self, _changes):
yield
def setUp(self) -> None:
self.StartPatcher(parallel_unittest.ParallelMock())
self._patch_factory = patch_unittest.MockPatchFactory()
self.build_root = "fakebuildroot"
self.GetPatches = self._patch_factory.GetPatches
self.MockPatch = self._patch_factory.MockPatch
def MakeHelper(self, cros_internal=None, cros=None):
# pylint: disable=attribute-defined-outside-init
site_params = config_lib.GetSiteParams()
if cros_internal:
cros_internal = mock.create_autospec(gerrit.GerritHelper)
cros_internal.version = "2.2"
cros_internal.remote = site_params.INTERNAL_REMOTE
if cros:
cros = mock.create_autospec(gerrit.GerritHelper)
cros.remote = site_params.EXTERNAL_REMOTE
cros.version = "2.2"
return patch_series.HelperPool(cros_internal=cros_internal, cros=cros)
def GetPatchSeries(self, helper_pool=None):
if helper_pool is None:
helper_pool = self.MakeHelper(cros_internal=True, cros=True)
series = patch_series.PatchSeries(self.build_root, helper_pool)
# Suppress transactions.
series._Transaction = self._ValidateTransactionCall
series.GetGitRepoForChange = lambda change, **kwargs: os.path.join(
self.build_root, change.project
)
series.GetGitReposForChange = lambda change, **kwargs: [
os.path.join(self.build_root, change.project)
]
return series
def CheckPatchApply(self, apply_mocks) -> None:
for apply_mock in apply_mocks:
apply_mock.assert_called_once_with(mock.ANY, trivial=False)
value = apply_mock.call_args[0][0]
self.assertIsInstance(value, MockManifest)
self.assertEqual(value.root, self.build_root)
def SetPatchApply(self, patch):
return self.PatchObject(patch, "ApplyAgainstManifest")
def assertResults(
self,
series,
changes,
applied=(),
failed_tot=(),
failed_inflight=(),
frozen=True,
):
manifest = MockManifest(self.build_root)
result = series.Apply(changes, 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 = [
_GetFailedIds(x) for x in result[1:]
]
applied = _GetIds(applied)
failed_tot = _GetIds(failed_tot)
failed_inflight = _GetIds(failed_inflight)
self.assertEqual(applied, applied_result)
self.assertCountEqual(failed_inflight, failed_inflight_result)
self.assertCountEqual(failed_tot, failed_tot_result)
return result
class TestUploadedLocalPatch(PatchSeriesTestCase):
"""Test interaction between uploaded local git patches and PatchSeries."""
def testFetchChanges(self) -> None:
"""Test fetching uploaded local patches."""
git1, git2, patch1 = self._CommonGitSetup()
patch2 = self.CommitFile(git1, "monkeys2", "foon2")
patch3 = self._MkPatch(git1, None, original_sha1=patch1.sha1)
patch4 = self._MkPatch(git1, None, original_sha1=patch2.sha1)
self.assertEqual(patch3.id, patch1.id)
self.assertEqual(patch4.id, patch2.id)
self.assertNotEqual(patch3.id, patch4.id)
series = self.GetPatchSeries()
series.GetGitRepoForChange = lambda change, **kwargs: git2
series.GetGitReposForChange = lambda change, **kwargs: [git2]
patches, _ = series.FetchChanges([patch3, patch4])
self.assertEqual(len(patches), 2)
self.assertEqual(patches[0].id, patch3.id)
self.assertEqual(patches[1].id, patch4.id)
def testFetchChangesWithChangeNotInManifest(self) -> None:
"""test FetchChanges with ChangeNotInManifest."""
# pylint: disable=unused-argument
def raiseException(change, **kwargs) -> None:
raise cros_patch.ChangeNotInManifest(change)
patch_1, patch_2 = patches = self.GetPatches(2)
series = self.GetPatchSeries()
series.GetGitRepoForChange = raiseException
series.GetGitReposForChange = raiseException
changes, not_in_manifest = series.FetchChanges(patches)
self.assertEqual(len(changes), 0)
self.assertEqual(len(not_in_manifest), 2)
self.assertEqual(not_in_manifest[0].patch, patch_1)
self.assertEqual(not_in_manifest[1].patch, patch_2)