Statistics
| Revision:

gvsig-scripting / org.gvsig.scripting / trunk / org.gvsig.scripting / org.gvsig.scripting.app / org.gvsig.scripting.app.mainplugin / src / main / resources-plugin / scripting / lib / dulwich / tests / compat / server_utils.py @ 959

History | View | Annotate | Download (12.3 KB)

1
# server_utils.py -- Git server compatibility utilities
2
# Copyright (C) 2010 Google, Inc.
3
#
4
# Dulwich is dual-licensed under the Apache License, Version 2.0 and the GNU
5
# General Public License as public by the Free Software Foundation; version 2.0
6
# or (at your option) any later version. You can redistribute it and/or
7
# modify it under the terms of either of these two licenses.
8
#
9
# Unless required by applicable law or agreed to in writing, software
10
# distributed under the License is distributed on an "AS IS" BASIS,
11
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
# See the License for the specific language governing permissions and
13
# limitations under the License.
14
#
15
# You should have received a copy of the licenses; if not, see
16
# <http://www.gnu.org/licenses/> for a copy of the GNU General Public License
17
# and <http://www.apache.org/licenses/LICENSE-2.0> for a copy of the Apache
18
# License, Version 2.0.
19
#
20

    
21
"""Utilities for testing git server compatibility."""
22

    
23
import errno
24
import os
25
import shutil
26
import socket
27
import tempfile
28

    
29
from dulwich.repo import Repo
30
from dulwich.objects import hex_to_sha
31
from dulwich.server import (
32
    ReceivePackHandler,
33
    )
34
from dulwich.tests.utils import (
35
    tear_down_repo,
36
    )
37
from dulwich.tests.compat.utils import (
38
    run_git_or_fail,
39
    )
40
from dulwich.tests.compat.utils import require_git_version
41

    
42

    
43
class _StubRepo(object):
44
    """A stub repo that just contains a path to tear down."""
45

    
46
    def __init__(self, name):
47
        temp_dir = tempfile.mkdtemp()
48
        self.path = os.path.join(temp_dir, name)
49
        os.mkdir(self.path)
50

    
51
    def close(self):
52
        pass
53

    
54

    
55
def _get_shallow(repo):
56
    shallow_file = repo.get_named_file('shallow')
57
    if not shallow_file:
58
        return []
59
    shallows = []
60
    with shallow_file:
61
        for line in shallow_file:
62
            sha = line.strip()
63
            if not sha:
64
                continue
65
            hex_to_sha(sha)
66
            shallows.append(sha)
67
    return shallows
68

    
69

    
70
class ServerTests(object):
71
    """Base tests for testing servers.
72

73
    Does not inherit from TestCase so tests are not automatically run.
74
    """
75

    
76
    min_single_branch_version = (1, 7, 10,)
77

    
78
    def import_repos(self):
79
        self._old_repo = self.import_repo('server_old.export')
80
        self._new_repo = self.import_repo('server_new.export')
81

    
82
    def url(self, port):
83
        return '%s://localhost:%s/' % (self.protocol, port)
84

    
85
    def branch_args(self, branches=None):
86
        if branches is None:
87
            branches = ['master', 'branch']
88
        return ['%s:%s' % (b, b) for b in branches]
89

    
90
    def test_push_to_dulwich(self):
91
        self.import_repos()
92
        self.assertReposNotEqual(self._old_repo, self._new_repo)
93
        port = self._start_server(self._old_repo)
94

    
95
        run_git_or_fail(['push', self.url(port)] + self.branch_args(),
96
                        cwd=self._new_repo.path)
97
        self.assertReposEqual(self._old_repo, self._new_repo)
98

    
99
    def test_push_to_dulwich_no_op(self):
100
        self._old_repo = self.import_repo('server_old.export')
101
        self._new_repo = self.import_repo('server_old.export')
102
        self.assertReposEqual(self._old_repo, self._new_repo)
103
        port = self._start_server(self._old_repo)
104

    
105
        run_git_or_fail(['push', self.url(port)] + self.branch_args(),
106
                        cwd=self._new_repo.path)
107
        self.assertReposEqual(self._old_repo, self._new_repo)
108

    
109
    def test_push_to_dulwich_remove_branch(self):
110
        self._old_repo = self.import_repo('server_old.export')
111
        self._new_repo = self.import_repo('server_old.export')
112
        self.assertReposEqual(self._old_repo, self._new_repo)
113
        port = self._start_server(self._old_repo)
114

    
115
        run_git_or_fail(['push', self.url(port), ":master"],
116
                        cwd=self._new_repo.path)
117

    
118
        self.assertEqual(
119
            list(self._old_repo.get_refs().keys()), [b"refs/heads/branch"])
120

    
121
    def test_fetch_from_dulwich(self):
122
        self.import_repos()
123
        self.assertReposNotEqual(self._old_repo, self._new_repo)
124
        port = self._start_server(self._new_repo)
125

    
126
        run_git_or_fail(['fetch', self.url(port)] + self.branch_args(),
127
                        cwd=self._old_repo.path)
128
        # flush the pack cache so any new packs are picked up
129
        self._old_repo.object_store._pack_cache_time = 0
130
        self.assertReposEqual(self._old_repo, self._new_repo)
131

    
132
    def test_fetch_from_dulwich_no_op(self):
133
        self._old_repo = self.import_repo('server_old.export')
134
        self._new_repo = self.import_repo('server_old.export')
135
        self.assertReposEqual(self._old_repo, self._new_repo)
136
        port = self._start_server(self._new_repo)
137

    
138
        run_git_or_fail(['fetch', self.url(port)] + self.branch_args(),
139
                        cwd=self._old_repo.path)
140
        # flush the pack cache so any new packs are picked up
141
        self._old_repo.object_store._pack_cache_time = 0
142
        self.assertReposEqual(self._old_repo, self._new_repo)
143

    
144
    def test_clone_from_dulwich_empty(self):
145
        old_repo_dir = tempfile.mkdtemp()
146
        self.addCleanup(shutil.rmtree, old_repo_dir)
147
        self._old_repo = Repo.init_bare(old_repo_dir)
148
        port = self._start_server(self._old_repo)
149

    
150
        new_repo_base_dir = tempfile.mkdtemp()
151
        self.addCleanup(shutil.rmtree, new_repo_base_dir)
152
        new_repo_dir = os.path.join(new_repo_base_dir, 'empty_new')
153
        run_git_or_fail(['clone', self.url(port), new_repo_dir],
154
                        cwd=new_repo_base_dir)
155
        new_repo = Repo(new_repo_dir)
156
        self.assertReposEqual(self._old_repo, new_repo)
157

    
158
    def test_lsremote_from_dulwich(self):
159
        self._repo = self.import_repo('server_old.export')
160
        port = self._start_server(self._repo)
161
        o = run_git_or_fail(['ls-remote', self.url(port)])
162
        self.assertEqual(len(o.split(b'\n')), 4)
163

    
164
    def test_new_shallow_clone_from_dulwich(self):
165
        require_git_version(self.min_single_branch_version)
166
        self._source_repo = self.import_repo('server_new.export')
167
        self._stub_repo = _StubRepo('shallow')
168
        self.addCleanup(tear_down_repo, self._stub_repo)
169
        port = self._start_server(self._source_repo)
170

    
171
        # Fetch at depth 1
172
        run_git_or_fail(['clone', '--mirror', '--depth=1', '--no-single-branch',
173
                        self.url(port), self._stub_repo.path])
174
        clone = self._stub_repo = Repo(self._stub_repo.path)
175
        expected_shallow = [b'35e0b59e187dd72a0af294aedffc213eaa4d03ff',
176
                            b'514dc6d3fbfe77361bcaef320c4d21b72bc10be9']
177
        self.assertEqual(expected_shallow, _get_shallow(clone))
178
        self.assertReposNotEqual(clone, self._source_repo)
179

    
180
    def test_shallow_clone_from_git_is_identical(self):
181
        require_git_version(self.min_single_branch_version)
182
        self._source_repo = self.import_repo('server_new.export')
183
        self._stub_repo_git = _StubRepo('shallow-git')
184
        self.addCleanup(tear_down_repo, self._stub_repo_git)
185
        self._stub_repo_dw = _StubRepo('shallow-dw')
186
        self.addCleanup(tear_down_repo, self._stub_repo_dw)
187

    
188
        # shallow clone using stock git, then using dulwich
189
        run_git_or_fail(['clone', '--mirror', '--depth=1', '--no-single-branch',
190
                         'file://' + self._source_repo.path,
191
                         self._stub_repo_git.path])
192

    
193
        port = self._start_server(self._source_repo)
194
        run_git_or_fail(['clone', '--mirror', '--depth=1', '--no-single-branch',
195
                        self.url(port), self._stub_repo_dw.path])
196

    
197
        # compare the two clones; they should be equal
198
        self.assertReposEqual(Repo(self._stub_repo_git.path),
199
                              Repo(self._stub_repo_dw.path))
200

    
201
    def test_fetch_same_depth_into_shallow_clone_from_dulwich(self):
202
        require_git_version(self.min_single_branch_version)
203
        self._source_repo = self.import_repo('server_new.export')
204
        self._stub_repo = _StubRepo('shallow')
205
        self.addCleanup(tear_down_repo, self._stub_repo)
206
        port = self._start_server(self._source_repo)
207

    
208
        # Fetch at depth 2
209
        run_git_or_fail(['clone', '--mirror', '--depth=2', '--no-single-branch',
210
                        self.url(port), self._stub_repo.path])
211
        clone = self._stub_repo = Repo(self._stub_repo.path)
212

    
213
        # Fetching at the same depth is a no-op.
214
        run_git_or_fail(
215
          ['fetch', '--depth=2', self.url(port)] + self.branch_args(),
216
          cwd=self._stub_repo.path)
217
        expected_shallow = [b'94de09a530df27ac3bb613aaecdd539e0a0655e1',
218
                            b'da5cd81e1883c62a25bb37c4d1f8ad965b29bf8d']
219
        self.assertEqual(expected_shallow, _get_shallow(clone))
220
        self.assertReposNotEqual(clone, self._source_repo)
221

    
222
    def test_fetch_full_depth_into_shallow_clone_from_dulwich(self):
223
        require_git_version(self.min_single_branch_version)
224
        self._source_repo = self.import_repo('server_new.export')
225
        self._stub_repo = _StubRepo('shallow')
226
        self.addCleanup(tear_down_repo, self._stub_repo)
227
        port = self._start_server(self._source_repo)
228

    
229
        # Fetch at depth 2
230
        run_git_or_fail(['clone', '--mirror', '--depth=2', '--no-single-branch',
231
                        self.url(port), self._stub_repo.path])
232
        clone = self._stub_repo = Repo(self._stub_repo.path)
233

    
234
        # Fetching at the same depth is a no-op.
235
        run_git_or_fail(
236
          ['fetch', '--depth=2', self.url(port)] + self.branch_args(),
237
          cwd=self._stub_repo.path)
238

    
239
        # The whole repo only has depth 4, so it should equal server_new.
240
        run_git_or_fail(
241
          ['fetch', '--depth=4', self.url(port)] + self.branch_args(),
242
          cwd=self._stub_repo.path)
243
        self.assertEqual([], _get_shallow(clone))
244
        self.assertReposEqual(clone, self._source_repo)
245

    
246
    def test_fetch_from_dulwich_issue_88_standard(self):
247
        # Basically an integration test to see that the ACK/NAK
248
        # generation works on repos with common head.
249
        self._source_repo = self.import_repo('issue88_expect_ack_nak_server.export')
250
        self._client_repo = self.import_repo('issue88_expect_ack_nak_client.export')
251
        port = self._start_server(self._source_repo)
252

    
253
        run_git_or_fail(['fetch', self.url(port), 'master',],
254
                        cwd=self._client_repo.path)
255
        self.assertObjectStoreEqual(
256
            self._source_repo.object_store,
257
            self._client_repo.object_store)
258

    
259
    def test_fetch_from_dulwich_issue_88_alternative(self):
260
        # likewise, but the case where the two repos have no common parent
261
        self._source_repo = self.import_repo('issue88_expect_ack_nak_other.export')
262
        self._client_repo = self.import_repo('issue88_expect_ack_nak_client.export')
263
        port = self._start_server(self._source_repo)
264

    
265
        self.assertRaises(KeyError, self._client_repo.get_object,
266
            b'02a14da1fc1fc13389bbf32f0af7d8899f2b2323')
267
        run_git_or_fail(['fetch', self.url(port), 'master',],
268
                        cwd=self._client_repo.path)
269
        self.assertEqual(b'commit', self._client_repo.get_object(
270
            b'02a14da1fc1fc13389bbf32f0af7d8899f2b2323').type_name)
271

    
272
    def test_push_to_dulwich_issue_88_standard(self):
273
        # Same thing, but we reverse the role of the server/client
274
        # and do a push instead.
275
        self._source_repo = self.import_repo('issue88_expect_ack_nak_client.export')
276
        self._client_repo = self.import_repo('issue88_expect_ack_nak_server.export')
277
        port = self._start_server(self._source_repo)
278

    
279
        run_git_or_fail(['push', self.url(port), 'master',],
280
                        cwd=self._client_repo.path)
281
        self.assertReposEqual(self._source_repo, self._client_repo)
282

    
283

    
284
# TODO(dborowitz): Come up with a better way of testing various permutations of
285
# capabilities. The only reason it is the way it is now is that side-band-64k
286
# was only recently introduced into git-receive-pack.
287
class NoSideBand64kReceivePackHandler(ReceivePackHandler):
288
    """ReceivePackHandler that does not support side-band-64k."""
289

    
290
    @classmethod
291
    def capabilities(cls):
292
        return tuple(c for c in ReceivePackHandler.capabilities()
293
                     if c != b'side-band-64k')
294

    
295

    
296
def ignore_error(error):
297
    """Check whether this error is safe to ignore."""
298
    (e_type, e_value, e_tb) = error
299
    return (issubclass(e_type, socket.error) and
300
            e_value[0] in (errno.ECONNRESET, errno.EPIPE))
301