conftest.py
259 lines
| 7.6 KiB
| text/x-python
|
PythonLexer
r5608 | # Copyright (C) 2010-2024 RhodeCode GmbH | |||
r1 | # | |||
# This program is free software: you can redistribute it and/or modify | ||||
# it under the terms of the GNU Affero General Public License, version 3 | ||||
# (only), as published by the Free Software Foundation. | ||||
# | ||||
# This program is distributed in the hope that it will be useful, | ||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||||
# GNU General Public License for more details. | ||||
# | ||||
# You should have received a copy of the GNU Affero General Public License | ||||
# along with this program. If not, see <http://www.gnu.org/licenses/>. | ||||
# | ||||
# This program is dual-licensed. If you wish to learn more about the | ||||
# RhodeCode Enterprise Edition, including its added features, Support services, | ||||
# and proprietary license terms, please see https://rhodecode.com/licenses/ | ||||
r2453 | import time | |||
r1 | import shutil | |||
import datetime | ||||
import pytest | ||||
r5087 | from rhodecode.lib.str_utils import safe_bytes | |||
r1 | from rhodecode.lib.vcs.backends import get_backend | |||
from rhodecode.lib.vcs.backends.base import Config | ||||
from rhodecode.lib.vcs.nodes import FileNode | ||||
from rhodecode.tests import get_new_dir | ||||
from rhodecode.tests.utils import check_skip_backends, check_xfail_backends | ||||
@pytest.fixture() | ||||
r5607 | def vcs_repository_support(request, backend_alias, baseapp, _vcs_repo_container): | |||
r1 | """ | |||
Provide a test repository for the test run. | ||||
Depending on the value of `recreate_repo_per_test` a new repo for each | ||||
test will be created. | ||||
The parameter `--backends` can be used to limit this fixture to specific | ||||
backend implementations. | ||||
""" | ||||
cls = request.cls | ||||
check_skip_backends(request.node, backend_alias) | ||||
check_xfail_backends(request.node, backend_alias) | ||||
if _should_create_repo_per_test(cls): | ||||
_vcs_repo_container = _create_vcs_repo_container(request) | ||||
repo = _vcs_repo_container.get_repo(cls, backend_alias=backend_alias) | ||||
# TODO: johbo: Supporting old test class api, think about removing this | ||||
cls.repo = repo | ||||
cls.repo_path = repo.path | ||||
cls.default_branch = repo.DEFAULT_BRANCH_NAME | ||||
cls.Backend = cls.backend_class = repo.__class__ | ||||
cls.imc = repo.in_memory_commit | ||||
r2453 | return backend_alias, repo | |||
r1 | ||||
r5607 | @pytest.fixture(scope="class") | |||
r1 | def _vcs_repo_container(request): | |||
""" | ||||
Internal fixture intended to help support class based scoping on demand. | ||||
""" | ||||
return _create_vcs_repo_container(request) | ||||
def _create_vcs_repo_container(request): | ||||
repo_container = VcsRepoContainer() | ||||
r5607 | if not request.config.getoption("--keep-tmp-path"): | |||
r1 | request.addfinalizer(repo_container.cleanup) | |||
return repo_container | ||||
class VcsRepoContainer(object): | ||||
def __init__(self): | ||||
self._cleanup_paths = [] | ||||
self._repos = {} | ||||
def get_repo(self, test_class, backend_alias): | ||||
if backend_alias not in self._repos: | ||||
repo = _create_empty_repository(test_class, backend_alias) | ||||
r2453 | ||||
r1 | self._cleanup_paths.append(repo.path) | |||
self._repos[backend_alias] = repo | ||||
return self._repos[backend_alias] | ||||
def cleanup(self): | ||||
for repo_path in reversed(self._cleanup_paths): | ||||
shutil.rmtree(repo_path) | ||||
def _should_create_repo_per_test(cls): | ||||
r5607 | return getattr(cls, "recreate_repo_per_test", False) | |||
r1 | ||||
def _create_empty_repository(cls, backend_alias=None): | ||||
Backend = get_backend(backend_alias or cls.backend_alias) | ||||
repo_path = get_new_dir(str(time.time())) | ||||
repo = Backend(repo_path, create=True) | ||||
r5607 | if hasattr(cls, "_get_commits"): | |||
r2453 | commits = cls._get_commits() | |||
cls.tip = _add_commits_to_repo(repo, commits) | ||||
r1 | ||||
return repo | ||||
r3946 | @pytest.fixture() | |||
r1 | def config(): | |||
""" | ||||
Instance of a repository config. | ||||
The instance contains only one value: | ||||
- Section: "section-a" | ||||
- Key: "a-1" | ||||
- Value: "value-a-1" | ||||
The intended usage is for cases where a config instance is needed but no | ||||
specific content is required. | ||||
""" | ||||
config = Config() | ||||
r5607 | config.set("section-a", "a-1", "value-a-1") | |||
r1 | return config | |||
def _add_commits_to_repo(repo, commits): | ||||
imc = repo.in_memory_commit | ||||
r2453 | tip = None | |||
r1 | ||||
for commit in commits: | ||||
r5607 | for node in commit.get("added", []): | |||
r5087 | if not isinstance(node, FileNode): | |||
node = FileNode(safe_bytes(node.path), content=node.content) | ||||
imc.add(node) | ||||
r5607 | for node in commit.get("changed", []): | |||
r5087 | if not isinstance(node, FileNode): | |||
node = FileNode(safe_bytes(node.path), content=node.content) | ||||
imc.change(node) | ||||
r5607 | for node in commit.get("removed", []): | |||
r5087 | imc.remove(FileNode(safe_bytes(node.path))) | |||
r1 | ||||
r2453 | tip = imc.commit( | |||
r5607 | message=str(commit["message"]), | |||
author=str(commit["author"]), | ||||
date=commit["date"], | ||||
branch=commit.get("branch"), | ||||
r5087 | ) | |||
r2453 | return tip | |||
r1 | ||||
r3946 | @pytest.fixture() | |||
r1 | def vcs_repo(request, backend_alias): | |||
Backend = get_backend(backend_alias) | ||||
repo_path = get_new_dir(str(time.time())) | ||||
repo = Backend(repo_path, create=True) | ||||
@request.addfinalizer | ||||
def cleanup(): | ||||
shutil.rmtree(repo_path) | ||||
return repo | ||||
r3946 | @pytest.fixture() | |||
r1 | def generate_repo_with_commits(vcs_repo): | |||
""" | ||||
r5087 | Creates a fabric to generate N commits with some file nodes on a randomly | |||
r1 | generated repository | |||
""" | ||||
def commit_generator(num): | ||||
start_date = datetime.datetime(2010, 1, 1, 20) | ||||
r4906 | for x in range(num): | |||
r1 | yield { | |||
r5607 | "message": "Commit %d" % x, | |||
"author": "Joe Doe <joe.doe@example.com>", | ||||
"date": start_date + datetime.timedelta(hours=12 * x), | ||||
"added": [ | ||||
FileNode(b"file_%d.txt" % x, content=b"Foobar %d" % x), | ||||
r1 | ], | |||
r5607 | "modified": [ | |||
FileNode(b"file_%d.txt" % x, content=b"Foobar %d modified" % (x - 1)), | ||||
], | ||||
r1 | } | |||
def commit_maker(num=5): | ||||
_add_commits_to_repo(vcs_repo, commit_generator(num)) | ||||
return vcs_repo | ||||
return commit_maker | ||||
r3946 | @pytest.fixture() | |||
r1 | def hg_repo(request, vcs_repo): | |||
repo = vcs_repo | ||||
r2453 | commits = repo._get_commits() | |||
r1 | _add_commits_to_repo(repo, commits) | |||
return repo | ||||
r3946 | @pytest.fixture() | |||
r1 | def hg_commit(hg_repo): | |||
return hg_repo.get_commit() | ||||
r2453 | ||||
class BackendTestMixin(object): | ||||
""" | ||||
This is a backend independent test case class which should be created | ||||
with ``type`` method. | ||||
It is required to set following attributes at subclass: | ||||
- ``backend_alias``: alias of used backend (see ``vcs.BACKENDS``) | ||||
- ``repo_path``: path to the repository which would be created for set of | ||||
tests | ||||
- ``recreate_repo_per_test``: If set to ``False``, repo would NOT be | ||||
created | ||||
before every single test. Defaults to ``True``. | ||||
""" | ||||
r5607 | ||||
r2453 | recreate_repo_per_test = True | |||
@classmethod | ||||
def _get_commits(cls): | ||||
commits = [ | ||||
{ | ||||
r5607 | "message": "Initial commit", | |||
"author": "Joe Doe <joe.doe@example.com>", | ||||
"date": datetime.datetime(2010, 1, 1, 20), | ||||
"added": [ | ||||
FileNode(b"foobar", content=b"Foobar"), | ||||
FileNode(b"foobar2", content=b"Foobar II"), | ||||
FileNode(b"foo/bar/baz", content=b"baz here!"), | ||||
r2453 | ], | |||
}, | ||||
{ | ||||
r5607 | "message": "Changes...", | |||
"author": "Jane Doe <jane.doe@example.com>", | ||||
"date": datetime.datetime(2010, 1, 1, 21), | ||||
"added": [ | ||||
FileNode(b"some/new.txt", content=b"news..."), | ||||
r2453 | ], | |||
r5607 | "changed": [ | |||
FileNode(b"foobar", b"Foobar I"), | ||||
r2453 | ], | |||
r5607 | "removed": [], | |||
r2453 | }, | |||
] | ||||
return commits | ||||