summaryrefslogtreecommitdiff
path: root/tests/sources/git.py
blob: a3649a863716cc1835b55d368c13ba419fabe73a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
import os
import pytest
import tempfile
import subprocess

from buildstream import SourceError
from buildstream import utils

from tests.testutils.site import HAVE_GIT

# import our common fixture
from .fixture import Setup

DATA_DIR = os.path.join(
    os.path.dirname(os.path.realpath(__file__)),
    'git',
)


###############################################################
#                         Utilities                           #
###############################################################
# Derived setup which creates a target.bst with a git source
# and url pointing to a directory indicated by setup.origin_dir
#
class GitSetup(Setup):

    def __init__(self, datafiles, tmpdir, ref=None, track=None, bstfile=None):

        if not bstfile:
            bstfile = 'target.bst'

        # Where we get the project from
        directory = os.path.join(datafiles.dirname, datafiles.basename)

        # This is where we'll put gits
        self.origin_dir = os.path.join(str(tmpdir), 'origin')
        if not os.path.exists(self.origin_dir):
            os.mkdir(self.origin_dir)

        # Generate a target file with a file:// url in the tmpdir
        self.url = 'file://' + os.path.join(self.origin_dir, 'repo')
        self.generate_target_bst(directory, self.url, ref=ref, track=track, bstfile=bstfile)

        super().__init__(datafiles, bstfile, tmpdir)

    def generate_target_bst(self, directory, url, ref=None, track=None, bstfile=None):
        template = "kind: pony\n" + \
                   "description: This is the pony\n" + \
                   "sources:\n" + \
                   "- kind: git\n" + \
                   "  url: {url}\n"

        if track:
            template += "  track: {track}\n"
        if url:
            template += "  ref: {ref}\n"

        final = template.format(url=url, ref=ref, track=track)

        filename = os.path.join(directory, bstfile)
        with open(filename, 'w') as f:
            f.write(final)


class GitSubmoduleSetup(GitSetup):

    def generate_target_bst(self, directory, url, ref=None, track=None, bstfile=None):

        self.subrepo_url = 'file://' + os.path.join(self.origin_dir, 'subrepo')

        template = "kind: pony\n" + \
                   "description: This is the pony\n" + \
                   "sources:\n" + \
                   "- kind: git\n" + \
                   "  url: {url}\n"

        if track:
            template += "  track: {track}\n"
        if url:
            template += "  ref: {ref}\n"

        template += "  submodules:\n" + \
                    "    subrepo:\n" + \
                    "      url: {subrepo}\n"

        final = template.format(url=url, ref=ref, track=track, subrepo=self.subrepo_url)

        filename = os.path.join(directory, bstfile)
        with open(filename, 'w') as f:
            f.write(final)


GIT_ENV = {
    'GIT_AUTHOR_DATE': '1320966000 +0200',
    'GIT_AUTHOR_NAME': 'tomjon',
    'GIT_AUTHOR_EMAIL': 'tom@jon.com',
    'GIT_COMMITTER_DATE': '1320966000 +0200',
    'GIT_COMMITTER_NAME': 'tomjon',
    'GIT_COMMITTER_EMAIL': 'tom@jon.com'
}


# Create a git repository at the setup.origin_dir
def git_create(setup, reponame):
    repodir = os.path.join(setup.origin_dir, reponame)
    os.mkdir(repodir)
    subprocess.call(['git', 'init', '.'], env=GIT_ENV, cwd=repodir)


# Add a file to the git
def git_add_file(setup, reponame, filename, content):
    repodir = os.path.join(setup.origin_dir, reponame)
    fullname = os.path.join(repodir, filename)
    with open(fullname, 'w') as f:
        f.write(content)

    # We rely on deterministic commit shas for testing, so set date and author
    subprocess.call(['git', 'add', filename], env=GIT_ENV, cwd=repodir)
    subprocess.call(['git', 'commit', '-m', 'Added the file'], env=GIT_ENV, cwd=repodir)


# Add a submodule to the git
def git_add_submodule(setup, reponame, url, path):
    repodir = os.path.join(setup.origin_dir, reponame)

    # We rely on deterministic commit shas for testing, so set date and author
    subprocess.call(['git', 'submodule', 'add', url, path], env=GIT_ENV, cwd=repodir)
    subprocess.call(['git', 'commit', '-m', 'Added the submodule'], env=GIT_ENV, cwd=repodir)


###############################################################
#                            Tests                            #
###############################################################
@pytest.mark.skipif(HAVE_GIT is False, reason="git is not available")
@pytest.mark.datafiles(os.path.join(DATA_DIR, 'basic'))
def test_create_source(tmpdir, datafiles):
    setup = Setup(datafiles, 'target.bst', tmpdir)
    assert(setup.source.get_kind() == 'git')


@pytest.mark.skipif(HAVE_GIT is False, reason="git is not available")
@pytest.mark.datafiles(os.path.join(DATA_DIR, 'template'))
def test_unique_key(tmpdir, datafiles):

    # Give it a ref of 12345
    setup = GitSetup(datafiles, tmpdir, '12345')
    assert(setup.source.get_kind() == 'git')

    # Check that the key has the ref we gave it, this isn't
    # much of a real test except it ensures that the fixture
    # we provided so far works
    unique_key = setup.source.get_unique_key()
    assert(unique_key[1] == '12345')


@pytest.mark.skipif(HAVE_GIT is False, reason="git is not available")
@pytest.mark.datafiles(os.path.join(DATA_DIR, 'template'))
def test_fetch(tmpdir, datafiles):

    # We know this is the right commit sha for the repo we create
    setup = GitSetup(datafiles, tmpdir, 'a3f9511fd3e4f043692f34234b4d2c7108de61fc')
    assert(setup.source.get_kind() == 'git')

    git_create(setup, 'repo')
    git_add_file(setup, 'repo', 'file.txt', 'pony')

    # Make sure we preflight first
    setup.source.preflight()

    # This should result in the mirror being created in the git sources dir
    setup.source.fetch()

    # Check that there is now a mirrored git repository at the expected directory
    directory_name = utils.url_directory_name(setup.url)
    fullpath = os.path.join(setup.context.sourcedir, 'git', directory_name)
    assert(os.path.isdir(fullpath))


@pytest.mark.skipif(HAVE_GIT is False, reason="git is not available")
@pytest.mark.datafiles(os.path.join(DATA_DIR, 'template'))
def test_fetch_bad_ref(tmpdir, datafiles):

    # This is a bad ref, 5 is not the tree sha for the repo we're creating !
    setup = GitSetup(datafiles, tmpdir, '5')
    assert(setup.source.get_kind() == 'git')

    git_create(setup, 'repo')
    git_add_file(setup, 'repo', 'file.txt', 'pony')

    # Make sure we preflight first
    setup.source.preflight()

    # This should result result in an error, impossible to fetch ref '5'
    # from the origin since it doesnt exist.
    with pytest.raises(SourceError):
        setup.source.fetch()


@pytest.mark.skipif(HAVE_GIT is False, reason="git is not available")
@pytest.mark.datafiles(os.path.join(DATA_DIR, 'template'))
def test_stage(tmpdir, datafiles):

    # We know this is the right tree sha for the repo we create
    setup = GitSetup(datafiles, tmpdir, 'a3f9511fd3e4f043692f34234b4d2c7108de61fc')
    assert(setup.source.get_kind() == 'git')

    git_create(setup, 'repo')
    git_add_file(setup, 'repo', 'file.txt', 'pony')

    # Make sure we preflight and fetch first, cant stage without fetching
    setup.source.preflight()
    setup.source.fetch()

    # Stage the file and just check that it's there
    stagedir = os.path.join(setup.context.builddir, 'repo')
    setup.source.stage(stagedir)
    assert(os.path.exists(os.path.join(stagedir, 'file.txt')))


@pytest.mark.skipif(HAVE_GIT is False, reason="git is not available")
@pytest.mark.datafiles(os.path.join(DATA_DIR, 'template'))
def test_fetch_new_ref_and_stage(tmpdir, datafiles):

    # This tests the functionality of already having a local mirror
    # but not yet having the ref which was asked for in the yaml
    #
    setup = GitSetup(datafiles, tmpdir, 'a3f9511fd3e4f043692f34234b4d2c7108de61fc')
    assert(setup.source.get_kind() == 'git')

    # This will get us the first ref
    git_create(setup, 'repo')
    git_add_file(setup, 'repo', 'file.txt', 'pony')

    # This will make a new ref available in the repo
    git_add_file(setup, 'repo', 'anotherfile.txt', 'pony')

    setup.source.preflight()
    setup.source.fetch()

    # Check that there is now a mirrored git repository at the expected directory
    directory_name = utils.url_directory_name(setup.url)
    fullpath = os.path.join(setup.context.sourcedir, 'git', directory_name)
    assert(os.path.isdir(fullpath))

    setup2 = GitSetup(datafiles, tmpdir, '3ac9cce94dd57e50a101e03dd6d43e0fc8a56b95', bstfile='another.bst')
    assert(setup2.source.get_kind() == 'git')
    setup2.source.preflight()
    setup2.source.fetch()

    # Stage the second source and just check that both files we created exist
    stagedir = os.path.join(setup.context.builddir, 'repo')
    setup2.source.stage(stagedir)
    assert(os.path.exists(os.path.join(stagedir, 'file.txt')))
    assert(os.path.exists(os.path.join(stagedir, 'anotherfile.txt')))


@pytest.mark.skipif(HAVE_GIT is False, reason="git is not available")
@pytest.mark.datafiles(os.path.join(DATA_DIR, 'template'))
def test_track(tmpdir, datafiles):

    # Setup with the initial ref pointing to the first commit, but tracking master
    setup = GitSetup(datafiles, tmpdir, 'a3f9511fd3e4f043692f34234b4d2c7108de61fc', track='master')
    assert(setup.source.get_kind() == 'git')

    # Create the repo with the initial commit a3f9511fd3e4f043692f34234b4d2c7108de61fc
    git_create(setup, 'repo')
    git_add_file(setup, 'repo', 'file.txt', 'pony')

    # Add a new commit 3ac9cce94dd57e50a101e03dd6d43e0fc8a56b95
    git_add_file(setup, 'repo', 'anotherfile.txt', 'pony')

    setup.source.preflight()

    # Test that the new ref is the latest on master after tracking
    assert(setup.source.mirror.ref == 'a3f9511fd3e4f043692f34234b4d2c7108de61fc')
    new_ref = setup.source.track()
    assert(new_ref == '3ac9cce94dd57e50a101e03dd6d43e0fc8a56b95')


@pytest.mark.skipif(HAVE_GIT is False, reason="git is not available")
@pytest.mark.datafiles(os.path.join(DATA_DIR, 'template'))
def test_submodule_fetch(tmpdir, datafiles):

    # We cannot guess the submodule commit shas really, because we
    # need to encode the submodule uri in the commit which adds the
    # submodule, so let's use track() in this case.
    setup = GitSubmoduleSetup(datafiles, tmpdir, track='master')
    assert(setup.source.get_kind() == 'git')

    git_create(setup, 'repo')
    git_add_file(setup, 'repo', 'file.txt', 'pony')
    git_create(setup, 'subrepo')
    git_add_file(setup, 'subrepo', 'ponyfile.txt', 'file')
    git_add_submodule(setup, 'repo', setup.subrepo_url, 'subrepo')

    # Preflight, track and fetch
    setup.source.preflight()
    ref = setup.source.track()
    setup.source.set_ref(ref, setup.source._Source__origin_node)
    setup.source.fetch()

    # Check that there is now a mirrored git repository at the expected directory
    directory_name = utils.url_directory_name(setup.url)
    fullpath = os.path.join(setup.context.sourcedir, 'git', directory_name)
    assert(os.path.isdir(fullpath))


@pytest.mark.skipif(HAVE_GIT is False, reason="git is not available")
@pytest.mark.datafiles(os.path.join(DATA_DIR, 'template'))
def test_submodule_stage(tmpdir, datafiles):

    # We cannot guess the submodule commit shas really, because we
    # need to encode the submodule uri in the commit which adds the
    # submodule, so let's use track() in this case.
    setup = GitSubmoduleSetup(datafiles, tmpdir, track='master')
    assert(setup.source.get_kind() == 'git')

    git_create(setup, 'repo')
    git_add_file(setup, 'repo', 'file.txt', 'pony')
    git_create(setup, 'subrepo')
    git_add_file(setup, 'subrepo', 'ponyfile.txt', 'file')
    git_add_submodule(setup, 'repo', setup.subrepo_url, 'subrepo')

    # Preflight, track and fetch
    setup.source.preflight()
    ref = setup.source.track()
    setup.source.set_ref(ref, setup.source._Source__origin_node)
    setup.source.fetch()

    # Stage the file and just check that it's there
    stagedir = os.path.join(setup.context.builddir, 'repo')
    setup.source.stage(stagedir)
    assert(os.path.exists(os.path.join(stagedir, 'file.txt')))

    # Assert the submodule file made it there
    assert(os.path.exists(os.path.join(stagedir, 'subrepo', 'ponyfile.txt')))


@pytest.mark.skipif(HAVE_GIT is False, reason="git is not available")
@pytest.mark.datafiles(os.path.join(DATA_DIR, 'template'))
def test_fetch_new_ref_with_submodule(tmpdir, datafiles):

    # We know this is the right commit sha for the repo before adding a submodule
    setup = GitSetup(datafiles, tmpdir, 'a3f9511fd3e4f043692f34234b4d2c7108de61fc')
    assert(setup.source.get_kind() == 'git')

    git_create(setup, 'repo')
    git_add_file(setup, 'repo', 'file.txt', 'pony')

    setup.source.preflight()
    setup.source.fetch()

    # Check that there is now a mirrored git repository at the expected directory
    directory_name = utils.url_directory_name(setup.url)
    fullpath = os.path.join(setup.context.sourcedir, 'git', directory_name)
    assert(os.path.isdir(fullpath))

    # Now add another repo and add a commit to the main repo making the
    # other repo a submodule
    git_create(setup, 'subrepo')
    git_add_file(setup, 'subrepo', 'ponyfile.txt', 'file')
    subrepo_url = 'file://' + os.path.join(setup.origin_dir, 'subrepo')
    git_add_submodule(setup, 'repo', subrepo_url, 'subrepo')

    # This time we need to track and use master, we can't predict this commit sha
    #
    setup2 = GitSubmoduleSetup(datafiles, tmpdir, track='master', bstfile='another.bst')
    assert(setup.source.get_kind() == 'git')

    # Preflight, track and fetch
    setup2.source.preflight()
    ref = setup2.source.track()
    setup2.source.set_ref(ref, setup2.source._Source__origin_node)
    setup2.source.fetch()

    # Stage the file and just check that it's there
    stagedir = os.path.join(setup.context.builddir, 'repo')
    setup2.source.stage(stagedir)
    assert(os.path.exists(os.path.join(stagedir, 'file.txt')))

    # Assert the submodule file made it there
    assert(os.path.exists(os.path.join(stagedir, 'subrepo', 'ponyfile.txt')))