# Copyright (C) 2006-2012, 2016 Canonical Ltd
# Authors: Robert Collins <robert.collins@canonical.com>
#          and others
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# 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 General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA


"""Repository implementation tests for bzr.

These test the conformance of all the repository variations to the expected API.
Specific tests for individual formats are in the tests/test_repository.py file
rather than in tests/per_branch/*.py.
"""

from breezy import repository
from breezy.bzr.remote import RemoteRepositoryFormat
from breezy.tests import default_transport, multiply_tests, test_server
from breezy.tests.per_controldir.test_controldir import TestCaseWithControlDir
from breezy.transport import memory


def formats_to_scenarios(
    formats, transport_server, transport_readonly_server, vfs_transport_factory=None
):
    """Transform the input formats to a list of scenarios.

    :param formats: A list of (scenario_name_suffix, repo_format)
        where the scenario_name_suffix is to be appended to the format
        name, and the repo_format is a RepositoryFormat subclass
        instance.
    :returns: Scenarios of [(scenario_name, {parameter_name: value})]
    """
    result = []
    for scenario_name_suffix, repository_format in formats:
        scenario_name = repository_format.__class__.__name__
        scenario_name += scenario_name_suffix
        scenario = (
            scenario_name,
            {
                "transport_server": transport_server,
                "transport_readonly_server": transport_readonly_server,
                "bzrdir_format": repository_format._matchingcontroldir,
                "repository_format": repository_format,
            },
        )
        # Only override the test's vfs_transport_factory if one was
        # specified, otherwise just leave the default in place.
        if vfs_transport_factory:
            scenario[1]["vfs_transport_factory"] = vfs_transport_factory
        result.append(scenario)
    return result


def all_repository_format_scenarios():
    """Return a list of test scenarios for parameterising repository tests."""
    all_formats = repository.format_registry._get_all()
    # format_scenarios is all the implementations of Repository; i.e. all disk
    # formats plus RemoteRepository.
    format_scenarios = formats_to_scenarios(
        [("", format) for format in all_formats],
        default_transport,
        # None here will cause a readonly decorator to be created
        # by the TestCaseWithTransport.get_readonly_transport method.
        None,
    )
    format_scenarios.extend(
        formats_to_scenarios(
            [("-default", RemoteRepositoryFormat())],
            test_server.SmartTCPServer_for_testing,
            test_server.ReadonlySmartTCPServer_for_testing,
            memory.MemoryServer,
        )
    )
    format_scenarios.extend(
        formats_to_scenarios(
            [("-v2", RemoteRepositoryFormat())],
            test_server.SmartTCPServer_for_testing_v2_only,
            test_server.ReadonlySmartTCPServer_for_testing_v2_only,
            memory.MemoryServer,
        )
    )
    return format_scenarios


class TestCaseWithRepository(TestCaseWithControlDir):
    def get_default_format(self):
        format = self.repository_format._matchingcontroldir
        self.assertEqual(format.repository_format, self.repository_format)
        return format

    def make_repository(self, relpath, shared=None, format=None):
        format = self.resolve_format(format)
        repo = super().make_repository(relpath, shared=shared, format=format)
        if format is None or format.repository_format is self.repository_format:
            # Create a repository of the type we are trying to test.
            if getattr(self, "repository_to_test_repository", None):
                repo = self.repository_to_test_repository(repo)
        return repo


def load_tests(loader, standard_tests, pattern):
    prefix = "breezy.tests.per_repository."
    test_repository_modules = [
        "test_add_fallback_repository",
        "test_break_lock",
        "test_check",
        "test_commit_builder",
        "test_fetch",
        "test_file_graph",
        "test_get_parent_map",
        "test_get_rev_id_for_revno",
        "test_has_same_location",
        "test_has_revisions",
        "test_locking",
        "test_pack",
        "test_reconcile",
        "test_refresh_data",
        "test_repository",
        "test_revision",
        "test_signatures",
        "test_statistics",
        "test_write_group",
    ]
    # Parameterize per_repository test modules by format.
    submod_tests = loader.loadTestsFromModuleNames(
        [prefix + module_name for module_name in test_repository_modules]
    )
    format_scenarios = all_repository_format_scenarios()
    return multiply_tests(submod_tests, format_scenarios, standard_tests)
