diff --git a/swh/loader/svn/svn.py b/swh/loader/svn/svn.py --- a/swh/loader/svn/svn.py +++ b/swh/loader/svn/svn.py @@ -84,8 +84,7 @@ # compute root directory path from the remote repository URL, required to # properly load the sub-tree of a repository mounted from a dump file - info = self.client.info(origin_url.rstrip("/")) - repos_root_url = next(iter(info.values())).repos_root_url + repos_root_url = self.info(origin_url).repos_root_url self.root_directory = origin_url.replace(repos_root_url, "", 1) def __str__(self): @@ -214,6 +213,13 @@ enabling to retry the operation if a network error occurs.""" return RemoteAccess(self.remote_url, auth=auth) + @svn_retry() + def info(self, origin_url: str): + """Simple wrapper around subvertpy.client.Client.info enabling to retry + the command if a network error occurs.""" + info = self.client.info(origin_url.rstrip("/")) + return next(iter(info.values())) + @svn_retry() def export( self, diff --git a/swh/loader/svn/tests/conftest.py b/swh/loader/svn/tests/conftest.py --- a/swh/loader/svn/tests/conftest.py +++ b/swh/loader/svn/tests/conftest.py @@ -55,3 +55,4 @@ mocker.patch.object(SvnRepo.checkout.retry, "sleep") mocker.patch.object(SvnRepo.propget.retry, "sleep") mocker.patch.object(SvnRepo.remote_access.retry, "sleep") + mocker.patch.object(SvnRepo.info.retry, "sleep") diff --git a/swh/loader/svn/tests/test_svn_retry.py b/swh/loader/svn/tests/test_svn_retry.py --- a/swh/loader/svn/tests/test_svn_retry.py +++ b/swh/loader/svn/tests/test_svn_retry.py @@ -58,6 +58,9 @@ def propget(self, *args, **kwargs): return self._wrapped_svn_cmd(self.client.propget, *args, **kwargs) + def info(self, *args, **kwargs): + return self._wrapped_svn_cmd(self.client.info, *args, **kwargs) + def assert_sleep_calls(mock_sleep, mocker, nb_failures): mock_sleep.assert_has_calls( @@ -290,3 +293,41 @@ ) assert_sleep_calls(mock_sleep, mocker, nb_failed_calls - 1) + + +@pytest.mark.parametrize("exception_to_retry", RETRYABLE_EXCEPTIONS) +def test_svn_info_retry_success(mocker, tmp_path, sample_repo_url, exception_to_retry): + svnrepo = SvnRepo( + sample_repo_url, sample_repo_url, tmp_path, max_content_length=100000 + ) + + mock_sleep = mocker.patch.object(svnrepo.info.retry, "sleep") + + nb_failed_calls = 2 + svnrepo.client = SVNClientWrapper( + svnrepo.client, exception_to_retry, nb_failed_calls + ) + + info = svnrepo.info(sample_repo_url) + assert info + + assert_sleep_calls(mock_sleep, mocker, nb_failed_calls) + + +@pytest.mark.parametrize("exception_to_retry", RETRYABLE_EXCEPTIONS) +def test_svn_info_retry_failure(mocker, tmp_path, sample_repo_url, exception_to_retry): + svnrepo = SvnRepo( + sample_repo_url, sample_repo_url, tmp_path, max_content_length=100000 + ) + + mock_sleep = mocker.patch.object(svnrepo.info.retry, "sleep") + + nb_failed_calls = SVN_RETRY_MAX_ATTEMPTS + svnrepo.client = SVNClientWrapper( + svnrepo.client, exception_to_retry, nb_failed_calls + ) + + with pytest.raises(type(exception_to_retry)): + svnrepo.info(sample_repo_url) + + assert_sleep_calls(mock_sleep, mocker, nb_failed_calls - 1)