Merge ~racb/git-ubuntu:importer-add-tests into git-ubuntu:master
- Git
- lp:~racb/git-ubuntu
- importer-add-tests
- Merge into master
Status: | Merged |
---|---|
Merged at revision: | 4585dc8073d0c5cedbbfddda58ae1d746d0b9a7c |
Proposed branch: | ~racb/git-ubuntu:importer-add-tests |
Merge into: | git-ubuntu:master |
Diff against target: |
3009 lines (+1951/-647) 8 files modified
gitubuntu/git_repository_test.py (+32/-115) gitubuntu/importer_tag_test.py (+744/-0) gitubuntu/importer_test.py (+783/-277) gitubuntu/repo_builder.py (+77/-245) gitubuntu/repo_builder_test.py (+284/-0) gitubuntu/source_builder.py (+11/-5) gitubuntu/source_builder_test.py (+18/-5) pytest.ini (+2/-0) |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Bryce Harrington | Approve | ||
Server Team CI bot | continuous-integration | Approve | |
Canonical Server | Pending | ||
Andreas Hasenack | Pending | ||
Robie Basak | Pending | ||
Review via email: mp+375096@code.launchpad.net |
This proposal supersedes a proposal from 2018-04-16.
Commit message
Make jenkins happy
Description of the change
Add tests, some of which are xfail pending future changes.
Server Team CI bot (server-team-bot) wrote : Posted in a previous version of this proposal | # |
Server Team CI bot (server-team-bot) wrote : Posted in a previous version of this proposal | # |
PASSED: Continuous integration, rev:6fc24f6fe4f
https:/
Executed test runs:
SUCCESS: VM Setup
SUCCESS: Build
SUCCESS: Unit Tests
SUCCESS: Integration Tests
IN_PROGRESS: Declarative: Post Actions
Click here to trigger a rebuild:
https:/
Andreas Hasenack (ahasenack) wrote : Posted in a previous version of this proposal | # |
I'll have to continue tomorrow, these tests are complex to grasp.
Server Team CI bot (server-team-bot) wrote : Posted in a previous version of this proposal | # |
PASSED: Continuous integration, rev:d99073ccf1c
https:/
Executed test runs:
SUCCESS: VM Setup
SUCCESS: Build
SUCCESS: Unit Tests
SUCCESS: Integration Tests
IN_PROGRESS: Declarative: Post Actions
Click here to trigger a rebuild:
https:/
Robie Basak (racb) : Posted in a previous version of this proposal | # |
Nish Aravamudan (nacc) wrote : Posted in a previous version of this proposal | # |
On Tue, May 22, 2018 at 6:48 AM, Robie Basak <email address hidden>
wrote:
>
>
> Diff comments:
>
> > diff --git a/doc/SPECIFICA
> > new file mode 100644
> > index 0000000..8c7f80c
> > --- /dev/null
> > +++ b/doc/SPECIFICA
> > @@ -0,0 +1,72 @@
> > +When importing a publishing record, there are two potential logical
> > +consequences:
> > +
> > + 1) Some new commit is created, with an associated tag; and/or
> > + 2) Some branch is adjusted to point at some commit
> > +
> > +In the following, existing tags may be 'import', 'upload' or 'applied'.
> > +Based upon SPECIFICATION.tags, 'import' and 'applied' tags are lists
> > +tags, which might be a single import tag or a list of reimport tags for
>
Should this be "lists of tags"?
>
Yep, typo. Want me to just push on top?
> > +any given version.
> > +
> > +If an existing tag with the same Git tree is found as a new publish
> > +event, we should reuse the tag, and adjust the corresponding branch to
> > +the same tagged commit.
> > +
> > +If no existing tag with same Git tree is found, a new import/applied tag
> > +should be created, as appropriate, and the corresponding branch should
> > +be adjusted to the the new tagged commit.
> > +
> > +If a new commit is created, appropriate commit parents are found in the
> > +existing Git commit graph. A changelog parent is the nearest (reverse
> > +chronologically) tagged version from debian/changelog of the publishing
> > +record.
> > +
> > +For patches-unapplied imports, parents are:
> > + 1) import-tagged changelog parents.
> > +
> > +For patches-applied imports, parents are:
> > + 1) applied-tagged changelog parents
> > + 2) import-tagged patches-unapplied commit of the same version as
> > + this publishing record.
> > +
> > +Logically, therefore, it is assumed for any given version that the
> > +patches-unapplied import occurs before the patches-applied import.
> > +
> > +Specific patches-unapplied cases, where in the following a tag exists
> > +for a specific version:
> > +
> > +1) An existing import tag (or reimport tag) with the same Git tree
> > + - Reuse import tag
> > +
> > +2) An existing import tag with a different Git tree and an existing
> > + upload tag with the same Git tree
> > + - Reuse upload tag, create reimport tag
> > +
> > +3) An existing import tag with a different Git tree and an existing
> > + upload tag with a different Git tree
> > + - Create reimport tag
> > +
> > +4) An existing import tag with a different Git tree and no upload tag
> > + - Create reimport tag
> > +
> > +5) No import tag and an existing upload tag with the same Git tree
> > + - Reuse upload tag, create import tag
> > +
> > +6) No import tag and an existing upload tag with a different Git tree
> > + - Create import tag
> > +
> > +7) No import tags or upload tags
> > + - Create import tag
> > +
> > +Specific patches-applied cases, where in the following a tag exists
> > +for a specific version:
> > +
> > +1) An existing applied tag (or reimport tag) with the same Git tree
> > + - Reuse applied tag
> > +
> > +2) An existing applied tag with a different Git tree
> > ...
Robie Basak (racb) wrote : Posted in a previous version of this proposal | # |
After a long absence I'm looking at making progress on the specification, tests and importer changes again. Nish, what's the level of involvement you'd like right now? I will happily take over if you're unavailable.
For now, I noticed something, comment inline, in part as a note to myself. I'm not intending to send you work without talking to you first and I'd be happy to fix up these review comments myself given how long it's been.
If you want less involvement Nish, I'm not sure yet if I intend to adjust this branch, or cherry-pick into a new one to land this work in more pieces. If you'd like more involvement, let's coordinate to avoid any wasted effort. Thanks!
Server Team CI bot (server-team-bot) wrote : | # |
PASSED: Continuous integration, rev:ece36b58b61
https:/
Executed test runs:
SUCCESS: VM Setup
SUCCESS: Build
SUCCESS: Unit Tests
IN_PROGRESS: Declarative: Post Actions
Click here to trigger a rebuild:
https:/
Bryce Harrington (bryce) wrote : | # |
Since this changeset is entirely just test code, there should be no risk to production code.
The xfail_strict=true is noteworthy in that it potentially could break CI if there were inconsistently marked xfail tests, but CI has passed so this is not a concern. It enforces a conventional understanding of xfail, so it's a good change to have in the testsuite. I've doublechecked each of the xfail cases has an LP# associated with it, and reviewed each bug report and test case.
Additional comments below. Main one is suggesting addition of a helper routine mk_commit() to refactor some repetitive setup code. Mostly copyedits and verbage suggestions beyond that.
I'm still working on wrapping my head around the test cases themselves, so will have more feedback to come.
Bryce Harrington (bryce) wrote : | # |
A bit more feedback, and I think I finally follow the mechanics of the test cases. A few more refactoring suggestions, some spelling copyedits, and I spotted an incorrect documentation for one of the test cases.
One thing I've not verified is if this covers all the necessary cases for the importer's logic. But I figure it's not like we can't keep adding to this, and expect there will be more cases to add as you delve into the importer logic. The main thing is that this gives a strong structure to add onto.
Also, while I've run the tests for this branch in the past, I haven't done so recently. I did check that jenkins appears to be running them:
#394 gitubuntu/
#397 gitubuntu/
The first corresponds to current trunk, the second this branch; the increase in tests and the xfails in the second case confirm to me the tests were run and passed.
Server Team CI bot (server-team-bot) wrote : | # |
PASSED: Continuous integration, rev:5e9c1e9fcb5
https:/
Executed test runs:
SUCCESS: VM Setup
SUCCESS: Build
SUCCESS: Unit Tests
IN_PROGRESS: Declarative: Post Actions
Click here to trigger a rebuild:
https:/
Robie Basak (racb) wrote : | # |
Thank you for the review!
Some of your suggested refactorings also apply to tests already present,
so for consistency, particular as some of your requests were for the
purposes of consistency, I have also made matching consistency changes
to tests that were already there. To make this practical I have had to
do some heavy rebasing. Most of the consistency-type refactorings are
squashed into the main "Add importer tests" commit now. Changes to the
previously existing tests appear before "Add importer tests" in the
patchset and are individually committed and documented. Other review
changes I've left separate for your review convenience, ready to squash
with --autosquash before merging.
On Tue, Nov 05, 2019 at 04:15:47AM -0000, Bryce Harrington wrote:
> > +# When importing a publishing record, there are two potential logical
> > +# consequences:
> > +#
> > +# 1) Some new commit is created, with an associated tag; and/or
> > +# 2) Some branch is adjusted to point at some commit
> > +#
> > +# If an existing tag with the same Git tree is found as a new publish
> > +# event, we should reuse the tag, and adjust the corresponding branch to
> > +# the same tagged commit.
> > +#
> > +# If no existing tag with same Git tree is found, a new tree and a new
>
> s/b "with the same"
Fixed in commit 196ec89.
> > +# import/applied tag that points to it should be created, as appropriate,
> > +# and the corresponding branch should be adjusted to the the new tagged
> > +# commit.
> > +#
> > +# If a new commit is created, appropriate commit parents are found in the
> > +# existing Git commit graph. A changelog parent is the nearest (reverse
> > +# chronologically) tagged version from debian/changelog of the publishing
> > +# record.
>
> s/b "from the debian/changelog of"
Fixed in commit 196ec89.
> > +@pytest.
> > + 'name, input_repo, expected_changes, compare_refs, exist_refs, reuse', [
> > + # 1) An existing import tag (or reimport tag) with the same Git tree
> > + # - Reuse import tag
> > + pytest.param(
> > + 'Case 1',
> > + repo_builder.Repo(
> > + commit_list=[
> > + repo_builder.
> > + tree=repo_
> > + source_
> > + source_
> > + version='1-1',
> > + native=False,
> > + )
> > + )
> > + ),
> > + name='import'
> > + ),
>
> This stanza of code to create a commit is repeated throughout this
> test suite with slight alterations. I think the tests would read
> easier if this was generated by a helper routine. I'm imagining
> something like:
>
> def mk_commit(name, with_file=False):
> file_contents = None
> if with_file:
> file_contents = {
> 'debian/random': 'The {} tag contents'.format( name ),
> }
>
> return repo_builder.
> tree=repo_
> ...
Server Team CI bot (server-team-bot) wrote : | # |
PASSED: Continuous integration, rev:68d61822e69
https:/
Executed test runs:
SUCCESS: VM Setup
SUCCESS: Build
SUCCESS: Unit Tests
IN_PROGRESS: Declarative: Post Actions
Click here to trigger a rebuild:
https:/
Bryce Harrington (bryce) wrote : | # |
This looks good and is nearly ready to land.
This is a quite healthy refactoring, that makes the test cases significantly more readable. The count of lines being deleted is a good sign.
I found one typo and one whitespace issue (below) but those are super minor niggles, feel free to ignore them or just fix as you land if you want.
I've verified the main suggests from the previous review are addressed, and I like how this has turned out. I've done a full read-thru of the patch and didn't spot any other issues to flag, codewise. If any further code errors exist they can get addressed as they surface.
I've run tests against the branch in lxc:
- [√] python3 ./setup.py check
- [√] python3 ./setup.py build
- [x] pytest-3 .
There are a just couple test failures on the importer and tag tests, that aren't present on the master branch:
git-ubuntu-
=======
platform linux -- Python 3.7.5rc1, pytest-3.10.1, py-1.8.0, pluggy-0.12.0
rootdir: /home/bryce/
collected 218 items
gitubuntu/
gitubuntu/
gitubuntu/
gitubuntu/
gitubuntu/
gitubuntu/
gitubuntu/
gitubuntu/
gitubuntu/
gitubuntu/
Full output of the test log is here: https:/
I've been poking at it myself but am not sure what the failure is. Guessing at least one failure is maybe due to date formatting, and might merely be a locale issue? Since jenkins isn't seeing failures I can't rule out that it's not something with my container setup.
Robie Basak (racb) wrote : | # |
The verbose pytest output is quite tricky to read, so I spent a while going through it. The two failures are separated at line 852. Both of the test failures appear to have the same cause: a RuntimeError raised because a generator raised a StopIteration. This is caused by quilt returning an exit code of 2 at importer.py:942.
It looks like the behaviour changed according to https:/
There is an underlying bug here: it's incorrect for a generator to raise a StopIteration; it should just return when done (and this was the case with older versions of Python too). But given that this definitely doesn't affect production (as the snap's version is fixed), CI would catch it if this changes, and these are your only identified test failures, I think it's OK to land this branch as-is. We can fix the failures in your test environment by replaces the StopIteration raises with returns in the generators in a separate branch.
I've fixed the typos, so I think this is ready to land. Thank you for the reviews!
Robie Basak (racb) wrote : | # |
Final branch candidate to land rebased and force pushed.
Bryce, could you confirm you're happy with my assessment in the previous comment and this is OK to land, please?
Server Team CI bot (server-team-bot) wrote : | # |
PASSED: Continuous integration, rev:b5ac84c74cb
https:/
Executed test runs:
SUCCESS: VM Setup
SUCCESS: Build
SUCCESS: Unit Tests
IN_PROGRESS: Declarative: Post Actions
Click here to trigger a rebuild:
https:/
Bryce Harrington (bryce) wrote : | # |
Yep, thanks for investigating, I did suspect it was something specific to my environment (the lxc container is eoan-based, I do need to redo it.) In fact I'm certain I've looked into the StopIteration issue once before (Nish chimed in on my comments about it on one of the bugs), and I seem to recall having similar ideas on a fix for it. I agree it can be treated as a separate problem. Land away!
Bryce Harrington (bryce) wrote : | # |
For reference, the StopIteration bug I mentioned looking at previously is https:/
Preview Diff
1 | diff --git a/gitubuntu/git_repository_test.py b/gitubuntu/git_repository_test.py |
2 | index 64c6876..01d3525 100644 |
3 | --- a/gitubuntu/git_repository_test.py |
4 | +++ b/gitubuntu/git_repository_test.py |
5 | @@ -558,44 +558,21 @@ def test_repo_derive_env_change(repo): |
6 | ( |
7 | 'Common case', |
8 | Repo( |
9 | - commit_list=[ |
10 | - Commit( |
11 | - tree=SourceTree( |
12 | - Source( |
13 | - SourceSpec( |
14 | - version='1-1', |
15 | - native=False, |
16 | - ) |
17 | - ), |
18 | - ), |
19 | - name='old/debian', |
20 | + commits=[ |
21 | + Commit.from_spec( |
22 | + name='old/debian' |
23 | ), |
24 | - Commit( |
25 | - tree=SourceTree( |
26 | - Source( |
27 | - SourceSpec( |
28 | - changelog_versions=['1-1ubuntu1', '1-1',], |
29 | - native=False, |
30 | - ) |
31 | - ), |
32 | - ), |
33 | - parents=[Placeholder('old/debian'),], |
34 | + Commit.from_spec( |
35 | + parents=[Placeholder('old/debian')], |
36 | name='old/ubuntu', |
37 | + changelog_versions=['1-1ubuntu1', '1-1'], |
38 | ), |
39 | - Commit( |
40 | - tree=SourceTree( |
41 | - Source( |
42 | - SourceSpec( |
43 | - changelog_versions=['2-1', '1-1',], |
44 | - native=False, |
45 | - ) |
46 | - ), |
47 | - ), |
48 | - parents=[Placeholder('old/debian'),], |
49 | + Commit.from_spec( |
50 | + parents=[Placeholder('old/debian')], |
51 | name='new/debian', |
52 | + changelog_versions=['2-1', '1-1'], |
53 | ), |
54 | ], |
55 | - branches={}, |
56 | tags={ |
57 | 'pkg/import/1-1': Placeholder('old/debian'), |
58 | 'pkg/import/1-1ubuntu1': Placeholder('old/ubuntu'), |
59 | @@ -609,56 +586,26 @@ def test_repo_derive_env_change(repo): |
60 | ( |
61 | 'Ubuntu delta based on a NMU', |
62 | Repo( |
63 | - commit_list=[ |
64 | - Commit( |
65 | - tree=SourceTree( |
66 | - Source( |
67 | - SourceSpec( |
68 | - version='1-1', |
69 | - native=False, |
70 | - ) |
71 | - ), |
72 | - ), |
73 | - name='fork_point', |
74 | + commits=[ |
75 | + Commit.from_spec( |
76 | + name='fork_point' |
77 | ), |
78 | - Commit( |
79 | - tree=SourceTree( |
80 | - Source( |
81 | - SourceSpec( |
82 | - changelog_versions=['1-1.1', '1-1',], |
83 | - native=False, |
84 | - ) |
85 | - ), |
86 | - ), |
87 | + Commit.from_spec( |
88 | parents=[Placeholder('fork_point')], |
89 | name='old/debian', |
90 | + changelog_versions=['1-1.1', '1-1'], |
91 | ), |
92 | - Commit( |
93 | - tree=SourceTree( |
94 | - Source( |
95 | - SourceSpec( |
96 | - changelog_versions=['1-1.1ubuntu1', '1-1.1', '1-1',], |
97 | - native=False, |
98 | - ) |
99 | - ), |
100 | - ), |
101 | - parents=[Placeholder('old/debian'),], |
102 | + Commit.from_spec( |
103 | + parents=[Placeholder('old/debian')], |
104 | name='old/ubuntu', |
105 | + changelog_versions=['1-1.1ubuntu1', '1-1.1', '1-1'], |
106 | ), |
107 | - Commit( |
108 | - tree=SourceTree( |
109 | - Source( |
110 | - SourceSpec( |
111 | - changelog_versions=['2-1', '1-1',], |
112 | - native=False, |
113 | - ) |
114 | - ), |
115 | - ), |
116 | - parents=[Placeholder('fork_point'),], |
117 | + Commit.from_spec( |
118 | + parents=[Placeholder('fork_point')], |
119 | name='new/debian', |
120 | + changelog_versions=['2-1', '1-1'], |
121 | ), |
122 | ], |
123 | - branches={}, |
124 | tags={ |
125 | 'pkg/import/1-1': Placeholder('fork_point'), |
126 | 'pkg/import/1-1.1': Placeholder('old/debian'), |
127 | @@ -673,56 +620,26 @@ def test_repo_derive_env_change(repo): |
128 | ( |
129 | 'Ubuntu upstream version head of Debian', |
130 | Repo( |
131 | - commit_list=[ |
132 | - Commit( |
133 | - tree=SourceTree( |
134 | - Source( |
135 | - SourceSpec( |
136 | - version='1-1', |
137 | - native=False, |
138 | - ) |
139 | - ), |
140 | - ), |
141 | - name='old/debian', |
142 | + commits=[ |
143 | + Commit.from_spec( |
144 | + name='old/debian' |
145 | ), |
146 | - Commit( |
147 | - tree=SourceTree( |
148 | - Source( |
149 | - SourceSpec( |
150 | - changelog_versions=['1-1ubuntu1', '1-1',], |
151 | - native=False, |
152 | - ) |
153 | - ), |
154 | - ), |
155 | - parents=[Placeholder('old/debian'),], |
156 | + Commit.from_spec( |
157 | + parents=[Placeholder('old/debian')], |
158 | name='mid_ubuntu', |
159 | + changelog_versions=['1-1ubuntu1', '1-1'], |
160 | ), |
161 | - Commit( |
162 | - tree=SourceTree( |
163 | - Source( |
164 | - SourceSpec( |
165 | - changelog_versions=['2-0ubuntu1', '1-1ubuntu1', '1-1',], |
166 | - native=False, |
167 | - ) |
168 | - ), |
169 | - ), |
170 | - parents=[Placeholder('mid_ubuntu'),], |
171 | + Commit.from_spec( |
172 | + parents=[Placeholder('mid_ubuntu')], |
173 | name='old/ubuntu', |
174 | + changelog_versions=['2-0ubuntu1', '1-1ubuntu1', '1-1'], |
175 | ), |
176 | - Commit( |
177 | - tree=SourceTree( |
178 | - Source( |
179 | - SourceSpec( |
180 | - changelog_versions=['3-1', '1-1',], |
181 | - native=False, |
182 | - ) |
183 | - ), |
184 | - ), |
185 | - parents=[Placeholder('old/debian'),], |
186 | + Commit.from_spec( |
187 | + parents=[Placeholder('old/debian')], |
188 | name='new/debian', |
189 | + changelog_versions=['3-1', '1-1'], |
190 | ), |
191 | ], |
192 | - branches={}, |
193 | tags={ |
194 | 'pkg/import/1-1': Placeholder('old/debian'), |
195 | 'pkg/import/1-1ubuntu1': Placeholder('mid_ubuntu'), |
196 | diff --git a/gitubuntu/importer_tag_test.py b/gitubuntu/importer_tag_test.py |
197 | new file mode 100644 |
198 | index 0000000..901ab10 |
199 | --- /dev/null |
200 | +++ b/gitubuntu/importer_tag_test.py |
201 | @@ -0,0 +1,744 @@ |
202 | +# This file contains tests additional to importer_test.py grouped here because |
203 | +# they are considerable on their own. |
204 | + |
205 | +from unittest.mock import ( |
206 | + patch, |
207 | +) |
208 | + |
209 | +import pygit2 |
210 | +import pytest |
211 | + |
212 | +import gitubuntu.repo_builder as repo_builder |
213 | +from gitubuntu.repo_builder import Commit, Placeholder |
214 | +import gitubuntu.repo_comparator as repo_comparator |
215 | +import gitubuntu.source_builder as source_builder |
216 | + |
217 | +import gitubuntu.importer as target |
218 | + |
219 | +from gitubuntu.test_fixtures import repo |
220 | +from gitubuntu.importer_test import MockSPI, patch_get_commit_environment |
221 | + |
222 | + |
223 | +# When importing a publishing record, there are two potential logical |
224 | +# consequences: |
225 | +# |
226 | +# 1) Some new commit is created, with an associated tag; and/or |
227 | +# 2) Some branch is adjusted to point at some commit |
228 | +# |
229 | +# If an existing tag with the same Git tree is found as a new publish |
230 | +# event, we should reuse the tag, and adjust the corresponding branch to |
231 | +# the same tagged commit. |
232 | +# |
233 | +# If no existing tag with the same Git tree is found, a new tree and a new |
234 | +# import/applied tag that points to it should be created, as appropriate, and |
235 | +# the corresponding branch should be adjusted to the the new tagged commit. |
236 | +# |
237 | +# If a new commit is created, appropriate commit parents are found in the |
238 | +# existing Git commit graph. A changelog parent is the nearest (reverse |
239 | +# chronologically) tagged version from the debian/changelog of the publishing |
240 | +# record. |
241 | + |
242 | +@pytest.mark.parametrize( |
243 | + [ |
244 | + 'input_repo', |
245 | + 'expected_output_refs', |
246 | + 'validation_repo_delta', |
247 | + 'validation_repo_expected_identical_refs', |
248 | + 'reuse', |
249 | + ], |
250 | + [ |
251 | + # 1) An existing import tag (or reimport tag) with the same Git tree |
252 | + # - Reuse import tag |
253 | + pytest.param( |
254 | + # input_repo: |
255 | + repo_builder.Repo( |
256 | + commits=[Commit.from_spec(name='import')], |
257 | + branches={ |
258 | + 'importer/ubuntu/trusty-proposed': Placeholder('import'), |
259 | + }, |
260 | + tags={'importer/import/1-1': Placeholder('import')}, |
261 | + ), |
262 | + # expected_output_refs: |
263 | + [ |
264 | + 'refs/heads/do-not-push', |
265 | + 'refs/tags/importer/upstream/ubuntu/1.gz', |
266 | + 'refs/heads/importer/importer/ubuntu/dsc', |
267 | + 'refs/heads/importer/importer/ubuntu/pristine-tar', |
268 | + ], |
269 | + # validation_repo_delta: |
270 | + { |
271 | + 'update_branches': { |
272 | + 'importer/ubuntu/trusty': Placeholder('import'), |
273 | + }, |
274 | + }, |
275 | + # validation_repo_expected_identical_refs: |
276 | + [ |
277 | + 'refs/heads/importer/ubuntu/trusty-proposed', |
278 | + 'refs/heads/importer/ubuntu/trusty', |
279 | + 'refs/tags/importer/import/1-1', |
280 | + ], |
281 | + # reuse: |
282 | + True, |
283 | + ), |
284 | + pytest.param( |
285 | + # input_repo: |
286 | + repo_builder.Repo( |
287 | + commits=[ |
288 | + Commit.from_spec( |
289 | + name='import', |
290 | + mutate='import tag contents', |
291 | + ), |
292 | + Commit.from_spec(name='reimport'), |
293 | + ], |
294 | + branches={ |
295 | + 'importer/ubuntu/trusty-proposed': Placeholder('import'), |
296 | + }, |
297 | + tags={ |
298 | + 'importer/import/1-1': Placeholder('import'), |
299 | + 'importer/reimport/import/1-1/0': Placeholder('import'), |
300 | + 'importer/reimport/import/1-1/1': Placeholder('reimport'), |
301 | + }, |
302 | + ), |
303 | + # expected_output_refs: |
304 | + [ |
305 | + 'refs/heads/do-not-push', |
306 | + 'refs/tags/importer/upstream/ubuntu/1.gz', |
307 | + 'refs/heads/importer/importer/ubuntu/dsc', |
308 | + 'refs/heads/importer/importer/ubuntu/pristine-tar', |
309 | + ], |
310 | + # validation_repo_delta: |
311 | + { |
312 | + 'update_branches': { |
313 | + 'importer/ubuntu/trusty': Placeholder('reimport'), |
314 | + }, |
315 | + }, |
316 | + # validation_repo_expected_identical_refs: |
317 | + [ |
318 | + 'refs/heads/importer/ubuntu/trusty-proposed', |
319 | + 'refs/heads/importer/ubuntu/trusty', |
320 | + 'refs/tags/importer/import/1-1', |
321 | + 'refs/tags/importer/reimport/import/1-1/0', |
322 | + 'refs/tags/importer/reimport/import/1-1/1', |
323 | + ], |
324 | + # reuse: |
325 | + True, |
326 | + |
327 | + marks=pytest.mark.xfail(reason='LP: #1761331'), |
328 | + ), |
329 | + |
330 | + # 2) An existing import tag with a different Git tree and an existing |
331 | + # upload tag with the same Git tree |
332 | + # - Reuse upload tag, create reimport tag |
333 | + pytest.param( |
334 | + # input_repo: |
335 | + repo_builder.Repo( |
336 | + commits=[ |
337 | + Commit.from_spec( |
338 | + name='import', |
339 | + mutate='The import tag contents', |
340 | + ), |
341 | + Commit.from_spec(name='upload'), |
342 | + ], |
343 | + branches={ |
344 | + 'importer/ubuntu/trusty-proposed': Placeholder('import'), |
345 | + }, |
346 | + tags={ |
347 | + 'importer/import/1-1': Placeholder('import'), |
348 | + 'importer/upload/1-1': Placeholder('upload'), |
349 | + }, |
350 | + ), |
351 | + # expected_output_refs: |
352 | + [ |
353 | + 'refs/heads/do-not-push', |
354 | + 'refs/tags/importer/upstream/ubuntu/1.gz', |
355 | + 'refs/heads/importer/importer/ubuntu/dsc', |
356 | + 'refs/heads/importer/importer/ubuntu/pristine-tar', |
357 | + ], |
358 | + # validation_repo_delta: |
359 | + { |
360 | + 'update_tags': { |
361 | + 'importer/reimport/import/1-1/0': Placeholder('import'), |
362 | + 'importer/reimport/import/1-1/1': Placeholder('upload'), |
363 | + }, |
364 | + 'update_branches': { |
365 | + 'importer/ubuntu/trusty': Placeholder('upload'), |
366 | + }, |
367 | + }, |
368 | + # validation_repo_expected_identical_refs: |
369 | + [ |
370 | + 'refs/heads/importer/ubuntu/trusty-proposed', |
371 | + 'refs/heads/importer/ubuntu/trusty', |
372 | + 'refs/tags/importer/import/1-1', |
373 | + 'refs/tags/importer/upload/1-1', |
374 | + 'refs/tags/importer/reimport/import/1-1/0', |
375 | + 'refs/tags/importer/reimport/import/1-1/1', |
376 | + ], |
377 | + # reuse: |
378 | + True, |
379 | + |
380 | + marks=pytest.mark.xfail(reason='LP: #1754194'), |
381 | + ), |
382 | + |
383 | + # 3) An existing import tag with a different Git tree and an existing |
384 | + # upload tag with a different Git tree |
385 | + # - Create reimport tag |
386 | + pytest.param( |
387 | + # input_repo: |
388 | + repo_builder.Repo( |
389 | + commits=[ |
390 | + Commit.from_spec( |
391 | + name='import', |
392 | + mutate='The import tag contents', |
393 | + ), |
394 | + Commit.from_spec( |
395 | + name='import', |
396 | + mutate='The upload tag contents', |
397 | + ), |
398 | + ], |
399 | + branches={ |
400 | + 'importer/ubuntu/trusty-proposed': Placeholder('import'), |
401 | + }, |
402 | + tags={ |
403 | + 'importer/import/1-1': Placeholder('import'), |
404 | + 'importer/upload/1-1': Placeholder('upload'), |
405 | + }, |
406 | + ), |
407 | + # expected_output_refs: |
408 | + [ |
409 | + 'refs/heads/do-not-push', |
410 | + 'refs/tags/importer/upstream/ubuntu/1.gz', |
411 | + 'refs/heads/importer/importer/ubuntu/dsc', |
412 | + 'refs/heads/importer/importer/ubuntu/pristine-tar', |
413 | + ], |
414 | + # validation_repo_delta: |
415 | + { |
416 | + 'add_commits': [Commit.from_spec(name='reimport')], |
417 | + 'update_tags': { |
418 | + 'importer/reimport/import/1-1/0': Placeholder('import'), |
419 | + 'importer/reimport/import/1-1/1': Placeholder('reimport'), |
420 | + }, |
421 | + 'update_branches': { |
422 | + 'importer/ubuntu/trusty': Placeholder('reimport'), |
423 | + }, |
424 | + }, |
425 | + # validation_repo_expected_identical_refs: |
426 | + [ |
427 | + 'refs/heads/importer/ubuntu/trusty-proposed', |
428 | + 'refs/heads/importer/ubuntu/trusty', |
429 | + 'refs/tags/importer/import/1-1', |
430 | + 'refs/tags/importer/upload/1-1', |
431 | + 'refs/tags/importer/reimport/import/1-1/0', |
432 | + 'refs/tags/importer/reimport/import/1-1/1', |
433 | + ], |
434 | + # reuse: |
435 | + False, |
436 | + |
437 | + marks=pytest.mark.xfail(reason='LP: #1754507'), |
438 | + ), |
439 | + |
440 | + # 4) An existing import tag with a different Git tree and no upload tag |
441 | + # - Create reimport tag |
442 | + pytest.param( |
443 | + # input_repo: |
444 | + repo_builder.Repo( |
445 | + commits=[ |
446 | + Commit.from_spec( |
447 | + name='import', |
448 | + mutate='The import tag contents', |
449 | + ), |
450 | + ], |
451 | + branches={ |
452 | + 'importer/ubuntu/trusty-proposed': Placeholder('import'), |
453 | + }, |
454 | + tags={'importer/import/1-1': Placeholder('import')}, |
455 | + ), |
456 | + # expected_output_refs: |
457 | + [ |
458 | + 'refs/heads/do-not-push', |
459 | + 'refs/tags/importer/upstream/ubuntu/1.gz', |
460 | + 'refs/heads/importer/importer/ubuntu/dsc', |
461 | + 'refs/heads/importer/importer/ubuntu/pristine-tar', |
462 | + ], |
463 | + # validation_repo_delta: |
464 | + { |
465 | + 'add_commits': [Commit.from_spec(name='reimport')], |
466 | + 'update_tags': { |
467 | + 'importer/reimport/import/1-1/0': Placeholder('import'), |
468 | + 'importer/reimport/import/1-1/1': Placeholder('reimport'), |
469 | + }, |
470 | + 'update_branches': { |
471 | + 'importer/ubuntu/trusty': Placeholder('reimport'), |
472 | + }, |
473 | + }, |
474 | + # validation_repo_expected_identical_refs: |
475 | + [ |
476 | + 'refs/heads/importer/ubuntu/trusty-proposed', |
477 | + 'refs/heads/importer/ubuntu/trusty', |
478 | + 'refs/tags/importer/import/1-1', |
479 | + 'refs/tags/importer/reimport/import/1-1/0', |
480 | + 'refs/tags/importer/reimport/import/1-1/1', |
481 | + ], |
482 | + # reuse: |
483 | + False, |
484 | + |
485 | + marks=pytest.mark.xfail(reason='LP: #1754706'), |
486 | + ), |
487 | + |
488 | + # 5) No import tag and an existing upload tag with the same Git tree |
489 | + # - Reuse upload tag, create import tag |
490 | + pytest.param( |
491 | + # input_repo: |
492 | + repo_builder.Repo( |
493 | + commits=[Commit.from_spec(name='upload')], |
494 | + tags={'importer/upload/1-1': Placeholder('upload')}, |
495 | + ), |
496 | + # expected_output_refs: |
497 | + [ |
498 | + 'refs/heads/do-not-push', |
499 | + 'refs/tags/importer/upstream/ubuntu/1.gz', |
500 | + 'refs/heads/importer/importer/ubuntu/dsc', |
501 | + 'refs/heads/importer/importer/ubuntu/pristine-tar', |
502 | + ], |
503 | + # validation_repo_delta: |
504 | + { |
505 | + 'update_tags': { |
506 | + 'importer/import/1-1': Placeholder('upload'), |
507 | + }, |
508 | + 'update_branches': { |
509 | + 'importer/ubuntu/trusty': Placeholder('upload'), |
510 | + }, |
511 | + }, |
512 | + # validation_repo_expected_identical_refs: |
513 | + [ |
514 | + 'refs/heads/importer/ubuntu/trusty', |
515 | + 'refs/tags/importer/import/1-1', |
516 | + 'refs/tags/importer/upload/1-1', |
517 | + ], |
518 | + # reuse: |
519 | + True, |
520 | + |
521 | + marks=pytest.mark.xfail(reason='LP: #1734883'), |
522 | + ), |
523 | + |
524 | + # 6) No import tag and an existing upload tag with a different Git tree |
525 | + # - Create import tag |
526 | + pytest.param( |
527 | + # input_repo: |
528 | + repo_builder.Repo( |
529 | + commits=[ |
530 | + Commit.from_spec( |
531 | + name='upload', |
532 | + mutate='The upload tag contents', |
533 | + ), |
534 | + ], |
535 | + tags={'importer/upload/1-1': Placeholder('upload')}, |
536 | + ), |
537 | + # expected_output_refs: |
538 | + [ |
539 | + 'refs/heads/do-not-push', |
540 | + 'refs/tags/importer/upstream/ubuntu/1.gz', |
541 | + 'refs/heads/importer/importer/ubuntu/dsc', |
542 | + 'refs/heads/importer/importer/ubuntu/pristine-tar', |
543 | + ], |
544 | + # validation_repo_delta: |
545 | + { |
546 | + 'add_commits': [ |
547 | + Commit.from_spec( |
548 | + name='publish', |
549 | + message='Test commit (new)', |
550 | + ), |
551 | + ], |
552 | + 'update_tags': { |
553 | + 'importer/import/1-1': Placeholder('publish'), |
554 | + }, |
555 | + 'update_branches': { |
556 | + 'importer/ubuntu/trusty': Placeholder('publish'), |
557 | + }, |
558 | + }, |
559 | + # validation_repo_expected_identical_refs: |
560 | + [ |
561 | + 'refs/heads/importer/ubuntu/trusty', |
562 | + 'refs/tags/importer/import/1-1', |
563 | + 'refs/tags/importer/upload/1-1', |
564 | + ], |
565 | + # reuse: |
566 | + False, |
567 | + ), |
568 | + |
569 | + # 7) No import tags or upload tags |
570 | + # - Create import tag |
571 | + pytest.param( |
572 | + # input_repo: |
573 | + repo_builder.Repo(), |
574 | + # expected_output_refs: |
575 | + [ |
576 | + 'refs/heads/do-not-push', |
577 | + 'refs/tags/importer/upstream/ubuntu/1.gz', |
578 | + 'refs/heads/importer/importer/ubuntu/dsc', |
579 | + 'refs/heads/importer/importer/ubuntu/pristine-tar', |
580 | + ], |
581 | + # validation_repo_delta: |
582 | + { |
583 | + 'add_commits': [ |
584 | + Commit.from_spec( |
585 | + name='publish', |
586 | + message='Test commit (new)', |
587 | + ), |
588 | + ], |
589 | + 'update_tags': { |
590 | + 'importer/import/1-1': Placeholder('publish'), |
591 | + }, |
592 | + 'update_branches': { |
593 | + 'importer/ubuntu/trusty': Placeholder('publish'), |
594 | + }, |
595 | + }, |
596 | + # validation_repo_expected_identical_refs: |
597 | + [ |
598 | + 'refs/heads/importer/ubuntu/trusty', |
599 | + 'refs/tags/importer/import/1-1' |
600 | + ], |
601 | + # reuse: |
602 | + False, |
603 | + ), |
604 | + ] |
605 | +) |
606 | +@patch('gitubuntu.importer.get_import_tag_msg') |
607 | +@patch('gitubuntu.importer.get_import_commit_msg') |
608 | +def test_import_unapplied_spi_tags( |
609 | + get_import_commit_msg_mock, |
610 | + get_import_tag_msg_mock, |
611 | + repo, |
612 | + input_repo, |
613 | + expected_output_refs, |
614 | + validation_repo_delta, |
615 | + validation_repo_expected_identical_refs, |
616 | + reuse, |
617 | +): |
618 | + """Test that unapplied tags are correctly created, adjusted and/or reused |
619 | + |
620 | + :param unittest.mock.Mock get_import_commit_msg_mock: mock of the function |
621 | + that determines the commit message to use for a given import |
622 | + :param unittest.mock.Mock get_import_tag_msg_mock: mock of the function |
623 | + that determines the tag message to use for a given import |
624 | + :param repo gitubuntu.git_repository.GitUbuntuRepository: fixture to hold a |
625 | + temporary output repository |
626 | + :param repo_builder.Repo input_repo: input repository data |
627 | + :param list(str) expected_output_refs: refs that must exist in the output |
628 | + repository |
629 | + :param dict validation_repo_delta: how to transform the input |
630 | + repository into a "validation repository", expressed as a dict to |
631 | + provide as **kwargs to gitubuntu.repo_builder.Repo.copy() against the |
632 | + input repository. The validation repository is then used for the |
633 | + purposes of comparison against the output repository. |
634 | + :param list(str) validation_repo_expected_identical_refs: refs that must be |
635 | + identical between the validation repository and the output repository |
636 | + :param bool reuse: if set, the output ref importer/ubuntu/trusty must be |
637 | + one supplied by the input repository (assumed to have a commit message |
638 | + of "Test commit") and not one created by the importer in this run |
639 | + (arranged by this test to have a different commit message) |
640 | + |
641 | + The input repository data is written into the output repository and then a |
642 | + fake non-native source package publication of version 1-1 in the Trusty |
643 | + release pocket is imported into it by calling import_unapplied_spi() |
644 | + directly. expected_output_refs, validation_repo_expected_identical_refs and |
645 | + reuse are then asserted. It is further asserted that no other refs exist in |
646 | + the output repository except for those listed in expected_output_refs and |
647 | + validation_repo_expected_identical_refs. |
648 | + """ |
649 | + # Match the repo_builder objects |
650 | + get_import_tag_msg_mock.return_value = 'Test tag' |
651 | + # Importantly, the following commit message must not be the same as the |
652 | + # commit messages used by the test input repository commits, so that we can |
653 | + # later detect the difference between commits that were already there and |
654 | + # new commits created by the importer for the purposes of asserting the |
655 | + # reuse parameter correctly. |
656 | + get_import_commit_msg_mock.return_value = b'Test commit (new)' |
657 | + |
658 | + input_repo.write(repo.raw_repo) |
659 | + |
660 | + publish_spec = source_builder.SourceSpec( |
661 | + version='1-1', |
662 | + native=False, |
663 | + ) |
664 | + |
665 | + with patch_get_commit_environment(repo): |
666 | + with source_builder.Source(publish_spec) as dsc_path: |
667 | + target.import_unapplied_spi( |
668 | + repo=repo, |
669 | + spi=MockSPI(dsc_path, publish_spec.version), |
670 | + namespace='importer', |
671 | + skip_orig=False, |
672 | + parent_overrides={}, |
673 | + ) |
674 | + |
675 | + if reuse: |
676 | + # Test that the previous commit was reused. In this case, the |
677 | + # commit message should be one created by the repo_builder.Commit() |
678 | + # default, and not one created by the importer. |
679 | + tip_ref = repo.raw_repo.lookup_reference( |
680 | + 'refs/heads/importer/ubuntu/trusty' |
681 | + ) |
682 | + message = tip_ref.peel(pygit2.Commit).message |
683 | + assert message == 'Test commit' |
684 | + |
685 | + validation_repo = input_repo.copy(**validation_repo_delta) |
686 | + |
687 | + # Verify validation_repo_expected_identical_refs |
688 | + assert repo_comparator.equals( |
689 | + repoA=repo.raw_repo, |
690 | + repoB=validation_repo, |
691 | + test_refs=validation_repo_expected_identical_refs, |
692 | + ) |
693 | + |
694 | + # Verify expected_output_refs |
695 | + for ref in expected_output_refs: |
696 | + assert repo.raw_repo.lookup_reference(ref) |
697 | + |
698 | + # Verify that no other refs exist |
699 | + all_expected_output_refs = ( |
700 | + frozenset(validation_repo_expected_identical_refs) | |
701 | + frozenset(expected_output_refs) |
702 | + ) |
703 | + all_actual_output_refs = frozenset(repo.raw_repo.listall_references()) |
704 | + assert all_expected_output_refs == all_actual_output_refs |
705 | + |
706 | + |
707 | +@pytest.mark.parametrize( |
708 | + [ |
709 | + 'input_repo', |
710 | + 'validation_repo_delta', |
711 | + 'validation_repo_expected_treewise_refs', |
712 | + 'reuse', |
713 | + ], |
714 | + [ |
715 | + # 1) An existing applied tag (or reimport tag) with the same Git tree |
716 | + # - Reuse applied tag |
717 | + pytest.param( |
718 | + # input_repo: |
719 | + repo_builder.Repo( |
720 | + commits=[ |
721 | + Commit.from_spec(name='unapplied', has_patches=True), |
722 | + Commit.from_spec(name='applied', patches_applied=True), |
723 | + ], |
724 | + branches={ |
725 | + 'importer/ubuntu/trusty': Placeholder('unapplied'), |
726 | + 'importer/applied/ubuntu/trusty': Placeholder('applied'), |
727 | + }, |
728 | + tags={ |
729 | + 'importer/import/1-1': Placeholder('unapplied'), |
730 | + 'importer/applied/1-1': Placeholder('applied'), |
731 | + }, |
732 | + ), |
733 | + # validation_repo_delta: |
734 | + { |
735 | + # no output repository delta |
736 | + }, |
737 | + # validation_repo_expected_treewise_refs: |
738 | + [ |
739 | + 'refs/heads/importer/ubuntu/trusty', |
740 | + 'refs/heads/importer/applied/ubuntu/trusty', |
741 | + 'refs/tags/importer/import/1-1', |
742 | + 'refs/tags/importer/applied/1-1', |
743 | + ], |
744 | + # reuse: |
745 | + True, |
746 | + ), |
747 | + |
748 | + # 2) An existing applied tag with a different Git tree |
749 | + # - Create reimport tag |
750 | + pytest.param( |
751 | + # input_repo: |
752 | + repo_builder.Repo( |
753 | + commits=[ |
754 | + Commit.from_spec( |
755 | + name='unapplied', |
756 | + has_patches=True, |
757 | + mutate='import tag contents', |
758 | + ), |
759 | + Commit.from_spec( |
760 | + name='unapplied_reimport', |
761 | + has_patches=True, |
762 | + ), |
763 | + Commit.from_spec( |
764 | + name='applied', |
765 | + patches_applied=True, |
766 | + mutate='import tag contents', |
767 | + ) |
768 | + ], |
769 | + branches={'importer/ubuntu/trusty': Placeholder('unapplied')}, |
770 | + tags={ |
771 | + 'importer/import/1-1': |
772 | + Placeholder('unapplied'), |
773 | + 'importer/reimport/import/1-1/0': |
774 | + Placeholder('unapplied'), |
775 | + 'importer/reimport/import/1-1/1': |
776 | + Placeholder('unapplied_reimport'), |
777 | + 'importer/applied/1-1': |
778 | + Placeholder('applied'), |
779 | + }, |
780 | + ), |
781 | + # validation_repo_delta: |
782 | + { |
783 | + 'add_commits': [ |
784 | + Commit.from_spec( |
785 | + name='applied_reimport', |
786 | + patches_applied=True, |
787 | + parents=[Placeholder('unapplied_reimport')], |
788 | + ), |
789 | + ], |
790 | + 'update_tags': { |
791 | + 'importer/reimport/applied/1-1/0': |
792 | + Placeholder('applied'), |
793 | + 'importer/reimport/applied/1-1/1': |
794 | + Placeholder('applied_reimport'), |
795 | + }, |
796 | + 'update_branches': { |
797 | + 'importer/applied/ubuntu/trusty': |
798 | + Placeholder('applied_reimport'), |
799 | + }, |
800 | + }, |
801 | + # validation_repo_expected_treewise_refs: |
802 | + [ |
803 | + 'refs/heads/importer/ubuntu/trusty', |
804 | + 'refs/heads/importer/applied/ubuntu/trusty', |
805 | + 'refs/tags/importer/import/1-1', |
806 | + 'refs/tags/importer/reimport/import/1-1/0', |
807 | + 'refs/tags/importer/reimport/import/1-1/1', |
808 | + 'refs/tags/importer/applied/1-1', |
809 | + 'refs/tags/importer/reimport/applied/1-1/0', |
810 | + 'refs/tags/importer/reimport/applied/1-1/1', |
811 | + ], |
812 | + # reuse: |
813 | + False, |
814 | + |
815 | + marks=pytest.mark.xfail(reason='LP: #1755247'), |
816 | + ), |
817 | + |
818 | + # 3) No applied tags |
819 | + # - Create applied tag |
820 | + pytest.param( |
821 | + # input_repo: |
822 | + repo_builder.Repo( |
823 | + commits=[Commit.from_spec(name='unapplied', has_patches=True)], |
824 | + branches={'importer/ubuntu/trusty': Placeholder('unapplied')}, |
825 | + tags={'importer/import/1-1': Placeholder('unapplied')}, |
826 | + ), |
827 | + # validation_repo_delta: |
828 | + { |
829 | + 'add_commits': [ |
830 | + Commit.from_spec( |
831 | + name='applied', |
832 | + patches_applied=True, |
833 | + parents=[Placeholder('unapplied')], |
834 | + ), |
835 | + ], |
836 | + 'update_tags': { |
837 | + 'importer/applied/1-1': Placeholder('applied') |
838 | + }, |
839 | + 'update_branches': { |
840 | + 'importer/applied/ubuntu/trusty': Placeholder('applied'), |
841 | + }, |
842 | + }, |
843 | + # validation_repo_expected_treewise_refs: |
844 | + [ |
845 | + 'refs/heads/importer/ubuntu/trusty', |
846 | + 'refs/heads/importer/applied/ubuntu/trusty', |
847 | + 'refs/tags/importer/import/1-1', |
848 | + 'refs/tags/importer/applied/1-1' |
849 | + ], |
850 | + # reuse: |
851 | + False, |
852 | + ), |
853 | + ] |
854 | +) |
855 | +@patch('gitubuntu.importer.get_import_tag_msg') |
856 | +@patch('gitubuntu.importer.get_import_commit_msg') |
857 | +def test_import_applied_spi_tags( |
858 | + get_import_commit_msg_mock, |
859 | + get_import_tag_msg_mock, |
860 | + repo, |
861 | + input_repo, |
862 | + validation_repo_delta, |
863 | + validation_repo_expected_treewise_refs, |
864 | + reuse, |
865 | +): |
866 | + """Test that applied tags are correctly created, adjusted and/or reused |
867 | + |
868 | + :param unittest.mock.Mock get_import_commit_msg_mock: mock of the function |
869 | + that determines the commit message to use for a given import |
870 | + :param unittest.mock.Mock get_import_tag_msg_mock: mock of the function |
871 | + that determines the tag message to use for a given import |
872 | + :param repo gitubuntu.git_repository.GitUbuntuRepository: fixture to hold a |
873 | + temporary output repository |
874 | + :param repo_builder.Repo input_repo: input repository data |
875 | + :param dict validation_repo_delta: how to transform the input repository |
876 | + into a "validation repository", expressed as a dict to |
877 | + provide as **kwargs to gitubuntu.repo_builder.Repo.copy() against the |
878 | + input repository. The validation repository is then used for the |
879 | + purposes of comparison against the output repository. |
880 | + :param list(str) validation_repo_expected_treewise_refs: refs whose trees |
881 | + must be identical between the validation repository and the output |
882 | + repository |
883 | + :param bool reuse: if set, the output ref importer/ubuntu/trusty must be |
884 | + one supplied by the input repository (assumed to have a commit message |
885 | + of "Test commit") and not one created by the importer in this run |
886 | + (arranged by this test to have a different commit message) |
887 | + |
888 | + The input repository data is written into the output repository and then a |
889 | + fake non-native source package publication of version 1-1 in the Trusty |
890 | + release pocket is imported into it by calling import_applied_spi() |
891 | + directly. reuse and validation_repo_expected_treewise_refs are then |
892 | + asserted. |
893 | + |
894 | + This is similar to test_unapplied_spi_tags except that it calls |
895 | + import_applied_spi() instead of import_unapplied_spi() and only treewise |
896 | + ref comparisons are made. |
897 | + """ |
898 | + # Match the repo_builder objects |
899 | + get_import_tag_msg_mock.return_value = 'Test tag' |
900 | + # Importantly, the following commit message must not be the same as the |
901 | + # commit messages used by the test input repository commits, so that we can |
902 | + # later detect the difference between commits that were already there and |
903 | + # new commits created by the importer for the purposes of asserting the |
904 | + # reuse parameter correctly. |
905 | + get_import_commit_msg_mock.return_value = b'Test commit (new)' |
906 | + |
907 | + input_repo.write(repo.raw_repo) |
908 | + |
909 | + publish_spec = source_builder.SourceSpec( |
910 | + version='1-1', |
911 | + native=False, |
912 | + has_patches=True, |
913 | + ) |
914 | + |
915 | + with patch_get_commit_environment(repo): |
916 | + with source_builder.Source(publish_spec) as dsc_path: |
917 | + target.import_applied_spi( |
918 | + repo=repo, |
919 | + spi=MockSPI(dsc_path, publish_spec.version), |
920 | + namespace='importer', |
921 | + allow_applied_failures=False, |
922 | + parent_overrides={}, |
923 | + ) |
924 | + |
925 | + if reuse: |
926 | + # Test that the previous commit was reused. In this case, |
927 | + # the commit message should be one created by the |
928 | + # repo_builder.Commit() default, and not one created by the |
929 | + # importer. |
930 | + tip_ref = repo.raw_repo.lookup_reference( |
931 | + 'refs/heads/importer/ubuntu/trusty' |
932 | + ) |
933 | + message = tip_ref.peel(pygit2.Commit).message |
934 | + assert message == 'Test commit' |
935 | + |
936 | + validation_repo = input_repo.copy(**validation_repo_delta) |
937 | + assert repo_comparator.equals( |
938 | + repoA=repo.raw_repo, |
939 | + repoB=validation_repo, |
940 | + test_refs=validation_repo_expected_treewise_refs, |
941 | + # this should be Commit, but we do not have a method yet |
942 | + # to obtain the stage-wise `quilt push` commits for a |
943 | + # patches-applied import |
944 | + comparison_type=repo_comparator.RepoComparisonType.Tree, |
945 | + ) |
946 | diff --git a/gitubuntu/importer_test.py b/gitubuntu/importer_test.py |
947 | index f9537c4..0ce014a 100644 |
948 | --- a/gitubuntu/importer_test.py |
949 | +++ b/gitubuntu/importer_test.py |
950 | @@ -12,6 +12,7 @@ import tempfile |
951 | import pygit2 |
952 | |
953 | import gitubuntu.repo_builder as repo_builder |
954 | +from gitubuntu.repo_builder import Placeholder |
955 | import gitubuntu.repo_comparator as repo_comparator |
956 | import gitubuntu.source_builder as source_builder |
957 | |
958 | @@ -120,10 +121,7 @@ def test_get_import_commit_msg( |
959 | target.get_changelog_for_commit = Mock() |
960 | target.get_changelog_for_commit.return_value = b'' |
961 | |
962 | - publish_spec = source_builder.SourceSpec( |
963 | - version='1-1', |
964 | - native=False, |
965 | - ) |
966 | + publish_spec = source_builder.SourceSpec() |
967 | publish_source = source_builder.Source(publish_spec) |
968 | |
969 | publish_oid = repo_builder.SourceTree(publish_source).write(repo.raw_repo) |
970 | @@ -144,42 +142,27 @@ def test_get_import_commit_msg( |
971 | |
972 | |
973 | @pytest.mark.parametrize( |
974 | - 'input_data, expected', [ |
975 | + 'input_repo, expected', [ |
976 | ( |
977 | - repo_builder.Repo( |
978 | - commit_list=[], |
979 | - branches={}, |
980 | - tags={}, |
981 | - ), |
982 | + repo_builder.Repo(), |
983 | [], |
984 | ), |
985 | ( |
986 | repo_builder.Repo( |
987 | - commit_list=[], |
988 | - branches={}, |
989 | - tags={ |
990 | - 'importer/import/1-1': repo_builder.Commit(repo_builder.Tree({})), |
991 | - }, |
992 | + tags={'importer/import/1-1': repo_builder.Commit()}, |
993 | ), |
994 | ['refs/tags/importer/import/1-1'], |
995 | ), |
996 | ( |
997 | repo_builder.Repo( |
998 | - commit_list=[ |
999 | - repo_builder.Commit( |
1000 | - tree=repo_builder.Tree({}), |
1001 | - name='import' |
1002 | - ), |
1003 | - repo_builder.Commit( |
1004 | - tree=repo_builder.Tree({}), |
1005 | - name='reimport1' |
1006 | - ), |
1007 | + commits=[ |
1008 | + repo_builder.Commit(name='import'), |
1009 | + repo_builder.Commit(name='reimport1'), |
1010 | ], |
1011 | - branches={}, |
1012 | tags={ |
1013 | - 'importer/import/1-1': repo_builder.Commit(repo_builder.Tree({})), |
1014 | - 'importer/reimport/import/1-1/0': repo_builder.Commit(repo_builder.Tree({})), |
1015 | - 'importer/reimport/import/1-1/1': repo_builder.Commit(repo_builder.Tree({})), |
1016 | + 'importer/import/1-1': repo_builder.Commit(), |
1017 | + 'importer/reimport/import/1-1/0': repo_builder.Commit(), |
1018 | + 'importer/reimport/import/1-1/1': repo_builder.Commit(), |
1019 | }, |
1020 | ), |
1021 | [ |
1022 | @@ -189,8 +172,8 @@ def test_get_import_commit_msg( |
1023 | ), |
1024 | ], |
1025 | ) |
1026 | -def test_get_existing_import_tags(repo, input_data, expected): |
1027 | - input_data.write(repo.raw_repo) |
1028 | +def test_get_existing_import_tags(repo, input_repo, expected): |
1029 | + input_repo.write(repo.raw_repo) |
1030 | |
1031 | assert [ |
1032 | ref.name for ref in |
1033 | @@ -199,42 +182,27 @@ def test_get_existing_import_tags(repo, input_data, expected): |
1034 | |
1035 | |
1036 | @pytest.mark.parametrize( |
1037 | - 'input_data, expected', [ |
1038 | + 'input_repo, expected', [ |
1039 | ( |
1040 | - repo_builder.Repo( |
1041 | - commit_list=[], |
1042 | - branches={}, |
1043 | - tags={}, |
1044 | - ), |
1045 | + repo_builder.Repo(), |
1046 | [], |
1047 | ), |
1048 | ( |
1049 | repo_builder.Repo( |
1050 | - commit_list=[], |
1051 | - branches={}, |
1052 | - tags={ |
1053 | - 'importer/applied/1-1': repo_builder.Commit(repo_builder.Tree({})), |
1054 | - }, |
1055 | + tags={'importer/applied/1-1': repo_builder.Commit()}, |
1056 | ), |
1057 | ['refs/tags/importer/applied/1-1'], |
1058 | ), |
1059 | ( |
1060 | repo_builder.Repo( |
1061 | - commit_list=[ |
1062 | - repo_builder.Commit( |
1063 | - tree=repo_builder.Tree({}), |
1064 | - name='applied' |
1065 | - ), |
1066 | - repo_builder.Commit( |
1067 | - tree=repo_builder.Tree({}), |
1068 | - name='reimport1' |
1069 | - ), |
1070 | + commits=[ |
1071 | + repo_builder.Commit(name='applied'), |
1072 | + repo_builder.Commit(name='reimport1'), |
1073 | ], |
1074 | - branches={}, |
1075 | tags={ |
1076 | - 'importer/applied/1-1': repo_builder.Commit(repo_builder.Tree({})), |
1077 | - 'importer/reimport/applied/1-1/0': repo_builder.Commit(repo_builder.Tree({})), |
1078 | - 'importer/reimport/applied/1-1/1': repo_builder.Commit(repo_builder.Tree({})), |
1079 | + 'importer/applied/1-1': repo_builder.Commit(), |
1080 | + 'importer/reimport/applied/1-1/0': repo_builder.Commit(), |
1081 | + 'importer/reimport/applied/1-1/1': repo_builder.Commit(), |
1082 | }, |
1083 | ), |
1084 | [ |
1085 | @@ -244,8 +212,8 @@ def test_get_existing_import_tags(repo, input_data, expected): |
1086 | ), |
1087 | ], |
1088 | ) |
1089 | -def test_get_existing_applied_tags(repo, input_data, expected): |
1090 | - input_data.write(repo.raw_repo) |
1091 | +def test_get_existing_applied_tags(repo, input_repo, expected): |
1092 | + input_repo.write(repo.raw_repo) |
1093 | |
1094 | assert [ |
1095 | ref.name for ref in |
1096 | @@ -254,24 +222,20 @@ def test_get_existing_applied_tags(repo, input_data, expected): |
1097 | |
1098 | |
1099 | @pytest.mark.parametrize( |
1100 | - 'input_data, expected_changes, test_refs', [ |
1101 | + [ |
1102 | + 'input_repo', |
1103 | + 'validation_repo_delta', |
1104 | + 'validation_repo_expected_identical_refs', |
1105 | + ], |
1106 | + [ |
1107 | ( |
1108 | - repo_builder.Repo( |
1109 | - commit_list=[], |
1110 | - branches={}, |
1111 | - tags={}, |
1112 | - ), |
1113 | + repo_builder.Repo(), |
1114 | { |
1115 | - 'commit_list_to_add': [ |
1116 | - repo_builder.Commit( |
1117 | - tree=repo_builder.Tree({}), |
1118 | - name='import' |
1119 | - ), |
1120 | + 'add_commits': [ |
1121 | + repo_builder.Commit(name='import'), |
1122 | ], |
1123 | - 'tags_to_add': { |
1124 | - 'importer/import/1-1': repo_builder.Placeholder('import'), |
1125 | - }, |
1126 | - 'branches_to_update': { |
1127 | + 'update_tags': { |
1128 | + 'importer/import/1-1': Placeholder('import'), |
1129 | }, |
1130 | }, |
1131 | [ |
1132 | @@ -280,29 +244,16 @@ def test_get_existing_applied_tags(repo, input_data, expected): |
1133 | ), |
1134 | ( |
1135 | repo_builder.Repo( |
1136 | - commit_list=[ |
1137 | - repo_builder.Commit( |
1138 | - tree=repo_builder.Tree({}), |
1139 | - name='import' |
1140 | - ), |
1141 | - ], |
1142 | - branches={}, |
1143 | - tags={ |
1144 | - 'importer/import/1-1': repo_builder.Placeholder('import'), |
1145 | - }, |
1146 | + commits=[repo_builder.Commit(name='import')], |
1147 | + tags={'importer/import/1-1': Placeholder('import')}, |
1148 | ), |
1149 | { |
1150 | - 'commit_list_to_add': [ |
1151 | - repo_builder.Commit( |
1152 | - tree=repo_builder.Tree({}), |
1153 | - name='reimport' |
1154 | - ), |
1155 | + 'add_commits': [ |
1156 | + repo_builder.Commit(name='reimport'), |
1157 | ], |
1158 | - 'tags_to_add': { |
1159 | - 'importer/reimport/import/1-1/0': repo_builder.Placeholder('import'), |
1160 | - 'importer/reimport/import/1-1/1': repo_builder.Placeholder('reimport'), |
1161 | - }, |
1162 | - 'branches_to_update': { |
1163 | + 'update_tags': { |
1164 | + 'importer/reimport/import/1-1/0': Placeholder('import'), |
1165 | + 'importer/reimport/import/1-1/1': Placeholder('reimport'), |
1166 | }, |
1167 | }, |
1168 | [ |
1169 | @@ -313,34 +264,22 @@ def test_get_existing_applied_tags(repo, input_data, expected): |
1170 | ), |
1171 | ( |
1172 | repo_builder.Repo( |
1173 | - commit_list=[ |
1174 | - repo_builder.Commit( |
1175 | - tree=repo_builder.Tree({}), |
1176 | - name='import' |
1177 | - ), |
1178 | - repo_builder.Commit( |
1179 | - tree=repo_builder.Tree({}), |
1180 | - name='reimport1' |
1181 | - ), |
1182 | + commits=[ |
1183 | + repo_builder.Commit(name='import'), |
1184 | + repo_builder.Commit(name='reimport1'), |
1185 | ], |
1186 | - branches={}, |
1187 | tags={ |
1188 | - 'importer/import/1-1': repo_builder.Placeholder('import'), |
1189 | - 'importer/reimport/import/1-1/0': repo_builder.Placeholder('import'), |
1190 | - 'importer/reimport/import/1-1/1': repo_builder.Placeholder('reimport1'), |
1191 | + 'importer/import/1-1': Placeholder('import'), |
1192 | + 'importer/reimport/import/1-1/0': Placeholder('import'), |
1193 | + 'importer/reimport/import/1-1/1': Placeholder('reimport1'), |
1194 | }, |
1195 | ), |
1196 | { |
1197 | - 'commit_list_to_add': [ |
1198 | - repo_builder.Commit( |
1199 | - tree=repo_builder.Tree({}), |
1200 | - name='reimport2' |
1201 | - ), |
1202 | + 'add_commits': [ |
1203 | + repo_builder.Commit(name='reimport2'), |
1204 | ], |
1205 | - 'tags_to_add': { |
1206 | - 'importer/reimport/import/1-1/2': repo_builder.Placeholder('reimport2'), |
1207 | - }, |
1208 | - 'branches_to_update': { |
1209 | + 'update_tags': { |
1210 | + 'importer/reimport/import/1-1/2': Placeholder('reimport2'), |
1211 | }, |
1212 | }, |
1213 | [ |
1214 | @@ -352,45 +291,55 @@ def test_get_existing_applied_tags(repo, input_data, expected): |
1215 | ), |
1216 | ], |
1217 | ) |
1218 | -def test_create_import_tag(repo, input_data, expected_changes, test_refs): |
1219 | - publish_commit_str = str(repo.raw_repo.get(repo_builder.Commit( |
1220 | - repo_builder.Tree({}) |
1221 | - ).write(repo.raw_repo)).peel(pygit2.Commit).id) |
1222 | - |
1223 | - # copy before write, so that placeholders are still present |
1224 | - expected_result = input_data.copy(**expected_changes) |
1225 | +def test_create_import_tag( |
1226 | + repo, |
1227 | + input_repo, |
1228 | + validation_repo_delta, |
1229 | + validation_repo_expected_identical_refs, |
1230 | +): |
1231 | + """ |
1232 | + Unit test that create_import_tag creates the correct import tag |
1233 | |
1234 | - input_data.write(repo.raw_repo) |
1235 | + :param repo gitubuntu.git_repository.GitUbuntuRepository: fixture to hold a |
1236 | + temporary output repository |
1237 | + :param repo_builder.Repo input_repo: input repository data |
1238 | + :param dict validation_repo_delta: how to transform the input repository |
1239 | + into a "validation repository", expressed as a dict to |
1240 | + provide as **kwargs to gitubuntu.repo_builder.Repo.copy() against the |
1241 | + input repository. The validation repository is then used for the |
1242 | + purposes of comparison against the output repository. |
1243 | + :param list(str) validation_repo_expected_identical_refs: refs that must be |
1244 | + identical between the validation repository and the output repository |
1245 | + """ |
1246 | + publish_commit_str = str( |
1247 | + repo.raw_repo.get( |
1248 | + repo_builder.Commit().write(repo.raw_repo) |
1249 | + ).peel(pygit2.Commit).id |
1250 | + ) |
1251 | + input_repo.write(repo.raw_repo) |
1252 | |
1253 | target.create_import_tag(repo, publish_commit_str, '1-1', 'importer') |
1254 | |
1255 | + validation_repo = input_repo.copy(**validation_repo_delta) |
1256 | assert repo_comparator.equals( |
1257 | repoA=repo.raw_repo, |
1258 | - repoB=expected_result, |
1259 | - test_refs=test_refs, |
1260 | + repoB=validation_repo, |
1261 | + test_refs=validation_repo_expected_identical_refs, |
1262 | ) |
1263 | |
1264 | |
1265 | @pytest.mark.parametrize( |
1266 | - 'input_data, expected_changes, test_refs', [ |
1267 | + [ |
1268 | + 'input_repo', |
1269 | + 'validation_repo_delta', |
1270 | + 'validation_repo_expected_identical_refs', |
1271 | + ], |
1272 | + [ |
1273 | ( |
1274 | - repo_builder.Repo( |
1275 | - commit_list=[], |
1276 | - branches={}, |
1277 | - tags={}, |
1278 | - ), |
1279 | + repo_builder.Repo(), |
1280 | { |
1281 | - 'commit_list_to_add': [ |
1282 | - repo_builder.Commit( |
1283 | - tree=repo_builder.Tree({}), |
1284 | - name='import' |
1285 | - ), |
1286 | - ], |
1287 | - 'tags_to_add': { |
1288 | - 'importer/applied/1-1': repo_builder.Placeholder('import'), |
1289 | - }, |
1290 | - 'branches_to_update': { |
1291 | - }, |
1292 | + 'add_commits': [repo_builder.Commit(name='import')], |
1293 | + 'update_tags': {'importer/applied/1-1': Placeholder('import')}, |
1294 | }, |
1295 | [ |
1296 | 'refs/tags/importer/applied/1-1', |
1297 | @@ -398,29 +347,14 @@ def test_create_import_tag(repo, input_data, expected_changes, test_refs): |
1298 | ), |
1299 | ( |
1300 | repo_builder.Repo( |
1301 | - commit_list=[ |
1302 | - repo_builder.Commit( |
1303 | - tree=repo_builder.Tree({}), |
1304 | - name='import' |
1305 | - ), |
1306 | - ], |
1307 | - branches={}, |
1308 | - tags={ |
1309 | - 'importer/applied/1-1': repo_builder.Placeholder('import'), |
1310 | - }, |
1311 | + commits=[repo_builder.Commit(name='import')], |
1312 | + tags={'importer/applied/1-1': Placeholder('import')}, |
1313 | ), |
1314 | { |
1315 | - 'commit_list_to_add': [ |
1316 | - repo_builder.Commit( |
1317 | - tree=repo_builder.Tree({}), |
1318 | - name='reimport' |
1319 | - ), |
1320 | - ], |
1321 | - 'tags_to_add': { |
1322 | - 'importer/reimport/applied/1-1/0': repo_builder.Placeholder('import'), |
1323 | - 'importer/reimport/applied/1-1/1': repo_builder.Placeholder('reimport'), |
1324 | - }, |
1325 | - 'branches_to_update': { |
1326 | + 'add_commits': [repo_builder.Commit(name='reimport')], |
1327 | + 'update_tags': { |
1328 | + 'importer/reimport/applied/1-1/0': Placeholder('import'), |
1329 | + 'importer/reimport/applied/1-1/1': Placeholder('reimport'), |
1330 | }, |
1331 | }, |
1332 | [ |
1333 | @@ -431,34 +365,22 @@ def test_create_import_tag(repo, input_data, expected_changes, test_refs): |
1334 | ), |
1335 | ( |
1336 | repo_builder.Repo( |
1337 | - commit_list=[ |
1338 | - repo_builder.Commit( |
1339 | - tree=repo_builder.Tree({}), |
1340 | - name='import' |
1341 | - ), |
1342 | - repo_builder.Commit( |
1343 | - tree=repo_builder.Tree({}), |
1344 | - name='reimport1' |
1345 | - ), |
1346 | + commits=[ |
1347 | + repo_builder.Commit(name='import'), |
1348 | + repo_builder.Commit(name='reimport1'), |
1349 | ], |
1350 | - branches={}, |
1351 | tags={ |
1352 | - 'importer/applied/1-1': repo_builder.Placeholder('import'), |
1353 | - 'importer/reimport/applied/1-1/0': repo_builder.Placeholder('import'), |
1354 | - 'importer/reimport/applied/1-1/1': repo_builder.Placeholder('reimport1'), |
1355 | + 'importer/applied/1-1': Placeholder('import'), |
1356 | + 'importer/reimport/applied/1-1/0': Placeholder('import'), |
1357 | + 'importer/reimport/applied/1-1/1': Placeholder('reimport1'), |
1358 | }, |
1359 | ), |
1360 | { |
1361 | - 'commit_list_to_add': [ |
1362 | - repo_builder.Commit( |
1363 | - tree=repo_builder.Tree({}), |
1364 | - name='reimport2' |
1365 | - ), |
1366 | + 'add_commits': [ |
1367 | + repo_builder.Commit(name='reimport2') |
1368 | ], |
1369 | - 'tags_to_add': { |
1370 | - 'importer/reimport/applied/1-1/2': repo_builder.Placeholder('reimport2'), |
1371 | - }, |
1372 | - 'branches_to_update': { |
1373 | + 'update_tags': { |
1374 | + 'importer/reimport/applied/1-1/2': Placeholder('reimport2'), |
1375 | }, |
1376 | }, |
1377 | [ |
1378 | @@ -470,33 +392,55 @@ def test_create_import_tag(repo, input_data, expected_changes, test_refs): |
1379 | ), |
1380 | ], |
1381 | ) |
1382 | -def test_create_applied_tag(repo, input_data, expected_changes, test_refs): |
1383 | - publish_commit_str = str(repo.raw_repo.get(repo_builder.Commit( |
1384 | - repo_builder.Tree({}) |
1385 | - ).write(repo.raw_repo)).peel(pygit2.Commit).id) |
1386 | +def test_create_applied_tag( |
1387 | + repo, |
1388 | + input_repo, |
1389 | + validation_repo_delta, |
1390 | + validation_repo_expected_identical_refs, |
1391 | +): |
1392 | + """ |
1393 | + Unit test that create_applied_tag creates the correct import tag |
1394 | |
1395 | - # copy before write, so that placeholders are still present |
1396 | - expected_result = input_data.copy(**expected_changes) |
1397 | + :param repo gitubuntu.git_repository.GitUbuntuRepository: fixture to hold a |
1398 | + temporary output repository |
1399 | + :param repo_builder.Repo input_repo: input repository data |
1400 | + :param dict validation_repo_delta: how to transform the input repository |
1401 | + into a "validation repository", expressed as a dict to |
1402 | + provide as **kwargs to gitubuntu.repo_builder.Repo.copy() against the |
1403 | + input repository. The validation repository is then used for the |
1404 | + purposes of comparison against the output repository. |
1405 | + :param list(str) validation_repo_expected_identical_refs: refs that must be |
1406 | + identical between the validation repository and the output repository |
1407 | + """ |
1408 | + publish_commit_str = str( |
1409 | + repo.raw_repo.get( |
1410 | + repo_builder.Commit().write(repo.raw_repo) |
1411 | + ).peel(pygit2.Commit).id |
1412 | + ) |
1413 | |
1414 | - input_data.write(repo.raw_repo) |
1415 | + input_repo.write(repo.raw_repo) |
1416 | |
1417 | target.create_applied_tag(repo, publish_commit_str, '1-1', 'importer') |
1418 | |
1419 | + validation_repo = input_repo.copy(**validation_repo_delta) |
1420 | assert repo_comparator.equals( |
1421 | repoA=repo.raw_repo, |
1422 | - repoB=expected_result, |
1423 | - test_refs=test_refs, |
1424 | + repoB=validation_repo, |
1425 | + test_refs=validation_repo_expected_identical_refs, |
1426 | ) |
1427 | |
1428 | |
1429 | -@pytest.mark.parametrize('input_data, parent_overrides, changelog_versions, patches_applied, expected_ref', |
1430 | +@pytest.mark.parametrize( |
1431 | + [ |
1432 | + 'input_repo', |
1433 | + 'parent_overrides', |
1434 | + 'changelog_versions', |
1435 | + 'patches_applied', |
1436 | + 'expected_ref', |
1437 | + ], |
1438 | [ |
1439 | ( |
1440 | - repo_builder.Repo( |
1441 | - commit_list=[], |
1442 | - branches={}, |
1443 | - tags={}, |
1444 | - ), |
1445 | + repo_builder.Repo(), |
1446 | {}, |
1447 | ['1-2', '1-1',], |
1448 | False, |
1449 | @@ -504,24 +448,8 @@ def test_create_applied_tag(repo, input_data, expected_changes, test_refs): |
1450 | ), |
1451 | ( |
1452 | repo_builder.Repo( |
1453 | - commit_list=[ |
1454 | - repo_builder.Commit( |
1455 | - tree=repo_builder.SourceTree( |
1456 | - source_builder.Source( |
1457 | - source_builder.SourceSpec( |
1458 | - version='1-1', |
1459 | - native=False, |
1460 | - ) |
1461 | - ) |
1462 | - ), |
1463 | - name='import', |
1464 | - ), |
1465 | - ], |
1466 | - branches={ |
1467 | - }, |
1468 | - tags={ |
1469 | - 'importer/import/1-1': repo_builder.Placeholder('import'), |
1470 | - }, |
1471 | + commits=[repo_builder.Commit.from_spec(name='import')], |
1472 | + tags={'importer/import/1-1': Placeholder('import')}, |
1473 | ), |
1474 | {}, |
1475 | ['1-2', '1-1'], |
1476 | @@ -530,24 +458,8 @@ def test_create_applied_tag(repo, input_data, expected_changes, test_refs): |
1477 | ), |
1478 | ( |
1479 | repo_builder.Repo( |
1480 | - commit_list=[ |
1481 | - repo_builder.Commit( |
1482 | - tree=repo_builder.SourceTree( |
1483 | - source_builder.Source( |
1484 | - source_builder.SourceSpec( |
1485 | - version='1-1', |
1486 | - native=False, |
1487 | - ) |
1488 | - ) |
1489 | - ), |
1490 | - name='import', |
1491 | - ), |
1492 | - ], |
1493 | - branches={ |
1494 | - }, |
1495 | - tags={ |
1496 | - 'importer/import/1-1': repo_builder.Placeholder('import'), |
1497 | - }, |
1498 | + commits=[repo_builder.Commit.from_spec(name='import')], |
1499 | + tags={'importer/import/1-1': Placeholder('import')}, |
1500 | ), |
1501 | {}, |
1502 | ['1-3', '1-2', '1-1'], |
1503 | @@ -555,11 +467,7 @@ def test_create_applied_tag(repo, input_data, expected_changes, test_refs): |
1504 | 'refs/tags/importer/import/1-1', |
1505 | ), |
1506 | ( |
1507 | - repo_builder.Repo( |
1508 | - commit_list=[], |
1509 | - branches={}, |
1510 | - tags={}, |
1511 | - ), |
1512 | + repo_builder.Repo(), |
1513 | {}, |
1514 | ['1-2', '1-1',], |
1515 | True, |
1516 | @@ -567,24 +475,8 @@ def test_create_applied_tag(repo, input_data, expected_changes, test_refs): |
1517 | ), |
1518 | ( |
1519 | repo_builder.Repo( |
1520 | - commit_list=[ |
1521 | - repo_builder.Commit( |
1522 | - tree=repo_builder.SourceTree( |
1523 | - source_builder.Source( |
1524 | - source_builder.SourceSpec( |
1525 | - version='1-1', |
1526 | - native=False, |
1527 | - ) |
1528 | - ) |
1529 | - ), |
1530 | - name='applied', |
1531 | - ), |
1532 | - ], |
1533 | - branches={ |
1534 | - }, |
1535 | - tags={ |
1536 | - 'importer/applied/1-1': repo_builder.Placeholder('applied'), |
1537 | - }, |
1538 | + commits=[repo_builder.Commit.from_spec(name='applied')], |
1539 | + tags={'importer/applied/1-1': Placeholder('applied')}, |
1540 | ), |
1541 | {}, |
1542 | ['1-2', '1-1'], |
1543 | @@ -593,24 +485,8 @@ def test_create_applied_tag(repo, input_data, expected_changes, test_refs): |
1544 | ), |
1545 | ( |
1546 | repo_builder.Repo( |
1547 | - commit_list=[ |
1548 | - repo_builder.Commit( |
1549 | - tree=repo_builder.SourceTree( |
1550 | - source_builder.Source( |
1551 | - source_builder.SourceSpec( |
1552 | - version='1-1', |
1553 | - native=False, |
1554 | - ) |
1555 | - ) |
1556 | - ), |
1557 | - name='applied', |
1558 | - ), |
1559 | - ], |
1560 | - branches={ |
1561 | - }, |
1562 | - tags={ |
1563 | - 'importer/applied/1-1': repo_builder.Placeholder('applied'), |
1564 | - }, |
1565 | + commits=[repo_builder.Commit.from_spec(name='applied')], |
1566 | + tags={'importer/applied/1-1': Placeholder('applied')}, |
1567 | ), |
1568 | {}, |
1569 | ['1-3', '1-2', '1-1'], |
1570 | @@ -621,13 +497,13 @@ def test_create_applied_tag(repo, input_data, expected_changes, test_refs): |
1571 | ) |
1572 | def test_get_changelog_parent_commit( |
1573 | repo, |
1574 | - input_data, |
1575 | + input_repo, |
1576 | parent_overrides, |
1577 | changelog_versions, |
1578 | patches_applied, |
1579 | expected_ref, |
1580 | ): |
1581 | - input_data.write(repo.raw_repo) |
1582 | + input_repo.write(repo.raw_repo) |
1583 | assert target.get_changelog_parent_commit( |
1584 | repo, |
1585 | 'importer', |
1586 | @@ -697,3 +573,633 @@ def test_importer_close_repository_on_exception(main_with_repo_mock): |
1587 | with pytest.raises(MockError): |
1588 | target.main(pkgname='dummy', owner='dummy', repo=repo) |
1589 | repo.close.assert_called() |
1590 | + |
1591 | + |
1592 | +def patch_get_commit_environment(repo): |
1593 | + """Patch a repository to use constant metadata |
1594 | + |
1595 | + :param GitUbuntuRepository repo: the repository whose behaviour will be |
1596 | + patched. |
1597 | + :rtype: contextmanager |
1598 | + :returns: a context manager that, when entered, will cause the repository |
1599 | + object to use constant metadata for created commits when metadata is |
1600 | + looked up against a treeish. |
1601 | + """ |
1602 | + return patch.object( |
1603 | + repo, |
1604 | + 'get_commit_environment', |
1605 | + return_value={ |
1606 | + 'GIT_AUTHOR_NAME':'Test Builder', |
1607 | + 'GIT_AUTHOR_EMAIL':'test@example.com', |
1608 | + 'GIT_AUTHOR_DATE':'1970-01-01T00:00:00Z', |
1609 | + 'GIT_COMMITTER_NAME':'Test Builder', |
1610 | + 'GIT_COMMITTER_EMAIL':'test@example.com', |
1611 | + 'GIT_COMMITTER_DATE':'1970-01-01T00:00:00Z', |
1612 | + }, |
1613 | + ) |
1614 | + |
1615 | + |
1616 | +def MockSPI(dsc_path, version): |
1617 | + """Construct a Mock SourcePackageInformation object |
1618 | + |
1619 | + In the following test cases we often need a Mock object with sufficient |
1620 | + property information to enable an import. This function constructs such an |
1621 | + object. |
1622 | + |
1623 | + :param str dsc_path: the path to a dsc file. This will be returned by the |
1624 | + dsc_pathname attribute. |
1625 | + :param str version: the package version string. This will be returned by |
1626 | + the version attribute. |
1627 | + :rtype: Mock |
1628 | + :returns: a Mock object ready to use |
1629 | + """ |
1630 | + spi = Mock() |
1631 | + spi.dsc_pathname = dsc_path |
1632 | + spi.distribution_name = 'Ubuntu' |
1633 | + spi.version = version |
1634 | + spi.date_published = '1970-01-01T00:00:00Z' |
1635 | + head_name = Mock(name='head_name') |
1636 | + head_name.return_value = 'importer/ubuntu/trusty' |
1637 | + spi.head_name = head_name |
1638 | + applied_head_name = Mock(name='applied_head_name') |
1639 | + applied_head_name.return_value = 'importer/applied/ubuntu/trusty' |
1640 | + spi.applied_head_name = applied_head_name |
1641 | + return spi |
1642 | + |
1643 | + |
1644 | +@patch('gitubuntu.importer.get_import_tag_msg') |
1645 | +@patch('gitubuntu.importer.get_import_commit_msg') |
1646 | +def test_import_unapplied_spi_quilt_patches( |
1647 | + get_import_commit_msg_mock, |
1648 | + get_import_tag_msg_mock, |
1649 | + repo, |
1650 | +): |
1651 | + """Test that a package with quilt patches is imported with correct |
1652 | + unapplied refs |
1653 | + |
1654 | + :param unittest.mock.Mock get_import_commit_msg_mock: mock of the function |
1655 | + that determines the commit message to use for a given import |
1656 | + :param unittest.mock.Mock get_import_tag_msg_mock: mock of the function |
1657 | + that determines the tag message to use for a given import |
1658 | + :param repo gitubuntu.git_repository.GitUbuntuRepository: fixture to hold a |
1659 | + temporary output repository |
1660 | + """ |
1661 | + # Match the repo_builder objects |
1662 | + get_import_tag_msg_mock.return_value = 'Test tag' |
1663 | + get_import_commit_msg_mock.return_value = b'Test commit' |
1664 | + |
1665 | + publish_spec = source_builder.SourceSpec(has_patches=True) |
1666 | + |
1667 | + input_repo = repo_builder.Repo() |
1668 | + input_repo.write(repo.raw_repo) |
1669 | + expected_result = repo_builder.Repo( |
1670 | + commits=[ |
1671 | + repo_builder.Commit( |
1672 | + tree=repo_builder.SourceTree( |
1673 | + source_builder.Source(publish_spec) |
1674 | + ), |
1675 | + name='publish' |
1676 | + ), |
1677 | + ], |
1678 | + tags={'importer/import/1-1': Placeholder('publish')}, |
1679 | + branches={'importer/ubuntu/trusty': Placeholder('publish')}, |
1680 | + ) |
1681 | + |
1682 | + with patch_get_commit_environment(repo): |
1683 | + with source_builder.Source(publish_spec) as dsc_path: |
1684 | + # import_unapplied_spi currently assumes it is called from the |
1685 | + # repository directory (pristine-tar and other commands rely on |
1686 | + # this) |
1687 | + target.import_unapplied_spi( |
1688 | + repo=repo, |
1689 | + spi=MockSPI(dsc_path, publish_spec.version), |
1690 | + namespace='importer', |
1691 | + skip_orig=False, |
1692 | + parent_overrides={}, |
1693 | + ) |
1694 | + |
1695 | + assert repo_comparator.equals( |
1696 | + repoA=repo.raw_repo, |
1697 | + repoB=expected_result, |
1698 | + test_refs=[ |
1699 | + 'refs/heads/importer/ubuntu/trusty', |
1700 | + 'refs/tags/importer/import/1-1', |
1701 | + ], |
1702 | + ) |
1703 | + |
1704 | + |
1705 | +@pytest.mark.parametrize( |
1706 | + [ |
1707 | + 'input_repo', |
1708 | + 'changelog_versions', |
1709 | + 'validation_repo_delta', |
1710 | + 'validation_repo_expected_identical_refs', |
1711 | + ], |
1712 | + [ |
1713 | + pytest.param( |
1714 | + repo_builder.Repo( |
1715 | + commits=[repo_builder.Commit.from_spec(name='import')], |
1716 | + tags={'importer/import/1-1': Placeholder('import')}, |
1717 | + ), |
1718 | + ['1-2', '1-1'], |
1719 | + { |
1720 | + 'add_commits': [ |
1721 | + repo_builder.Commit.from_spec( |
1722 | + name='publish', |
1723 | + parents=[Placeholder('import')], |
1724 | + changelog_versions=['1-2', '1-1'], |
1725 | + ), |
1726 | + ], |
1727 | + 'update_tags': {'importer/import/1-2': Placeholder('publish')}, |
1728 | + }, |
1729 | + [ |
1730 | + 'refs/tags/importer/import/1-1', |
1731 | + 'refs/tags/importer/import/1-2', |
1732 | + ], |
1733 | + ), |
1734 | + pytest.param( |
1735 | + repo_builder.Repo( |
1736 | + commits=[repo_builder.Commit.from_spec(name='import')], |
1737 | + tags={'importer/import/1-1': Placeholder('import')}, |
1738 | + ), |
1739 | + ['1-3', '1-2', '1-1'], |
1740 | + { |
1741 | + 'add_commits': [ |
1742 | + repo_builder.Commit.from_spec( |
1743 | + parents=[Placeholder('import')], |
1744 | + name='publish', |
1745 | + changelog_versions=['1-3', '1-2', '1-1'], |
1746 | + ), |
1747 | + ], |
1748 | + 'update_tags': {'importer/import/1-3': Placeholder('publish')}, |
1749 | + }, |
1750 | + [ |
1751 | + 'refs/tags/importer/import/1-1', |
1752 | + 'refs/tags/importer/import/1-3', |
1753 | + ], |
1754 | + ), |
1755 | + pytest.param( |
1756 | + repo_builder.Repo( |
1757 | + commits=[ |
1758 | + repo_builder.Commit.from_spec(name='import'), |
1759 | + repo_builder.Commit.from_spec( |
1760 | + name='reimport', |
1761 | + mutate='Reimport tag contents', |
1762 | + ), |
1763 | + ], |
1764 | + tags={ |
1765 | + 'importer/import/1-1': Placeholder('import'), |
1766 | + 'importer/reimport/import/1-1/0': Placeholder('import'), |
1767 | + 'importer/reimport/import/1-1/1': Placeholder('reimport'), |
1768 | + }, |
1769 | + ), |
1770 | + ['1-2', '1-1'], |
1771 | + { |
1772 | + 'add_commits': [ |
1773 | + repo_builder.Commit.from_spec( |
1774 | + parents=[ |
1775 | + Placeholder('import'), |
1776 | + Placeholder('reimport'), |
1777 | + ], |
1778 | + name='publish', |
1779 | + changelog_versions=['1-2', '1-1'], |
1780 | + ), |
1781 | + ], |
1782 | + 'update_tags': {'importer/import/1-2': Placeholder('publish')}, |
1783 | + }, |
1784 | + [ |
1785 | + 'refs/tags/importer/import/1-1', |
1786 | + 'refs/tags/importer/reimport/import/1-1/0', |
1787 | + 'refs/tags/importer/reimport/import/1-1/1', |
1788 | + 'refs/tags/importer/import/1-2', |
1789 | + ], |
1790 | + marks=pytest.mark.xfail(reason='LP: #1761332'), |
1791 | + ), |
1792 | + ] |
1793 | +) |
1794 | +@patch('gitubuntu.importer.get_import_tag_msg') |
1795 | +@patch('gitubuntu.importer.get_import_commit_msg') |
1796 | +def test_import_unapplied_spi_parenting( |
1797 | + get_import_commit_msg_mock, |
1798 | + get_import_tag_msg_mock, |
1799 | + repo, |
1800 | + input_repo, |
1801 | + changelog_versions, |
1802 | + validation_repo_delta, |
1803 | + validation_repo_expected_identical_refs, |
1804 | +): |
1805 | + """Test that unapplied import commits have the correct parents |
1806 | + |
1807 | + :param unittest.mock.Mock get_import_commit_msg_mock: mock of the function |
1808 | + that determines the commit message to use for a given import |
1809 | + :param unittest.mock.Mock get_import_tag_msg_mock: mock of the function |
1810 | + that determines the tag message to use for a given import |
1811 | + :param repo gitubuntu.git_repository.GitUbuntuRepository: fixture to hold a |
1812 | + temporary output repository |
1813 | + :param repo_builder.Repo input_repo: input repository data |
1814 | + :param list(str) changelog_versions: the versions in the changelog of a |
1815 | + fake package to test import |
1816 | + :param dict validation_repo_delta: how to transform the input |
1817 | + repository into a "validation repository", expressed as a dict to |
1818 | + provide as **kwargs to gitubuntu.repo_builder.Repo.copy() against the |
1819 | + input repository. The validation repository is then used for the |
1820 | + purposes of comparison against the output repository. |
1821 | + :param list(str) validation_repo_expected_identical_refs: refs that must be |
1822 | + identical between the validation repository and the output repository |
1823 | + |
1824 | + Verify that if an import of a package is made into input_repo where the |
1825 | + package being imported has the given changelog_versions, then the output |
1826 | + repository has commits with the parents we expect. This is tested by |
1827 | + comparing specific output references against the validation repository. |
1828 | + """ |
1829 | + |
1830 | + # Match the repo_builder objects |
1831 | + get_import_tag_msg_mock.return_value = 'Test tag' |
1832 | + get_import_commit_msg_mock.return_value = b'Test commit' |
1833 | + |
1834 | + input_repo.write(repo.raw_repo) |
1835 | + |
1836 | + publish_spec = source_builder.SourceSpec( |
1837 | + changelog_versions=changelog_versions, |
1838 | + ) |
1839 | + |
1840 | + with patch_get_commit_environment(repo): |
1841 | + with source_builder.Source(publish_spec) as dsc_path: |
1842 | + # import_unapplied_spi currently assumes it is called from the |
1843 | + # repository directory (pristine-tar and other commands rely on |
1844 | + # this) |
1845 | + target.import_unapplied_spi( |
1846 | + repo=repo, |
1847 | + spi=MockSPI(dsc_path, publish_spec.version), |
1848 | + namespace='importer', |
1849 | + skip_orig=False, |
1850 | + parent_overrides={}, |
1851 | + ) |
1852 | + |
1853 | + # we would like to check the commit hashes, but we cannot |
1854 | + # currently, as the commit messages are specific to the importer |
1855 | + # and not codified |
1856 | + validation_repo = input_repo.copy(**validation_repo_delta) |
1857 | + assert repo_comparator.equals( |
1858 | + repoA=repo.raw_repo, |
1859 | + repoB=validation_repo, |
1860 | + test_refs=validation_repo_expected_identical_refs, |
1861 | + ) |
1862 | + |
1863 | + |
1864 | +@patch('gitubuntu.importer.get_import_tag_msg') |
1865 | +@patch('gitubuntu.importer.get_import_commit_msg') |
1866 | +def test_import_unapplied_spi_parent_override( |
1867 | + get_import_commit_msg_mock, |
1868 | + get_import_tag_msg_mock, |
1869 | + repo, |
1870 | +): |
1871 | + """Test import_unapplied_spi() parent_override functionality |
1872 | + |
1873 | + Test that if parent_overrides is used in the import_unapplied_spi call then |
1874 | + the resulting commit correctly uses the overridden parents specified. |
1875 | + |
1876 | + :param unittest.mock.Mock get_import_commit_msg_mock: mock of the function |
1877 | + that determines the commit message to use for a given import |
1878 | + :param unittest.mock.Mock get_import_tag_msg_mock: mock of the function |
1879 | + that determines the tag message to use for a given import |
1880 | + :param repo gitubuntu.git_repository.GitUbuntuRepository: fixture to hold a |
1881 | + temporary output repository |
1882 | + :param repo_builder.Repo input_repo: input repository data |
1883 | + """ |
1884 | + # Match the repo_builder objects |
1885 | + get_import_tag_msg_mock.return_value = 'Test tag' |
1886 | + get_import_commit_msg_mock.return_value = b'Test commit' |
1887 | + |
1888 | + input_repo = repo_builder.Repo( |
1889 | + commits=[ |
1890 | + repo_builder.Commit.from_spec(name='import1-1', version='1-1'), |
1891 | + repo_builder.Commit.from_spec(name='import1-2', version='1-2'), |
1892 | + ], |
1893 | + tags={ |
1894 | + 'importer/import/1-1': Placeholder('import1-1'), |
1895 | + 'importer/import/1-2': Placeholder('import1-2'), |
1896 | + }, |
1897 | + ) |
1898 | + input_repo.write(repo.raw_repo) |
1899 | + |
1900 | + publish_spec = source_builder.SourceSpec( |
1901 | + changelog_versions=['2-1', '1-1'], |
1902 | + ) |
1903 | + |
1904 | + with patch_get_commit_environment(repo): |
1905 | + with source_builder.Source(publish_spec) as dsc_path: |
1906 | + # import_unapplied_spi currently assumes it is called from the |
1907 | + # repository directory (pristine-tar and other commands rely on |
1908 | + # this) |
1909 | + target.import_unapplied_spi( |
1910 | + repo=repo, |
1911 | + spi=MockSPI(dsc_path, publish_spec.version), |
1912 | + namespace='importer', |
1913 | + skip_orig=False, |
1914 | + parent_overrides={'2-1': {'changelog_parent': '1-2'}}, |
1915 | + ) |
1916 | + |
1917 | + validation_repo = input_repo.copy( |
1918 | + add_commits=[ |
1919 | + repo_builder.Commit.from_spec( |
1920 | + parents=[Placeholder('import1-2')], |
1921 | + name='publish', |
1922 | + changelog_versions=['2-1', '1-1'], |
1923 | + ), |
1924 | + ], |
1925 | + update_tags={ |
1926 | + 'importer/import/2-1': Placeholder('publish'), |
1927 | + }, |
1928 | + ) |
1929 | + validation_repo_expected_identical_refs = [ |
1930 | + 'refs/tags/importer/import/1-1', |
1931 | + 'refs/tags/importer/import/1-2', |
1932 | + 'refs/tags/importer/import/2-1', |
1933 | + ] |
1934 | + assert repo_comparator.equals( |
1935 | + repoA=repo.raw_repo, |
1936 | + repoB=validation_repo, |
1937 | + test_refs=validation_repo_expected_identical_refs, |
1938 | + ) |
1939 | + |
1940 | + |
1941 | +def test_import_unapplied_spi_parent_override_failure(repo): |
1942 | + """ |
1943 | + Test override_parents ParentOverrideError raise |
1944 | + |
1945 | + When a parent override is specified but the specified version doesn't have |
1946 | + an import tag, an exception should be raised. |
1947 | + |
1948 | + :param repo gitubuntu.git_repository.GitUbuntuRepository: fixture to hold a |
1949 | + temporary output repository |
1950 | + """ |
1951 | + repo_builder.Repo( |
1952 | + commits=[repo_builder.Commit.from_spec(name='import1-1')], |
1953 | + tags={'importer/import/1-1': Placeholder('import1-1')}, |
1954 | + ).write(repo.raw_repo) |
1955 | + |
1956 | + with pytest.raises(target.ParentOverrideError): |
1957 | + target.override_parents( |
1958 | + parent_overrides={'2-1': {'changelog_parent': '1-2'}}, |
1959 | + repo=repo, |
1960 | + version='2-1', |
1961 | + namespace='importer', |
1962 | + ) |
1963 | + |
1964 | + |
1965 | +@pytest.mark.parametrize( |
1966 | + 'input_repo, expected_ancestor_commits, expected_parent_commits', [ |
1967 | + # In general, these tests do not set applied commit parents in the |
1968 | + # input repository since we have no mechanism to do that correctly, but |
1969 | + # this doesn't matter for the purposes of these tests. |
1970 | + |
1971 | + # if only one import tag exists, then it is the parent |
1972 | + pytest.param( |
1973 | + repo_builder.Repo( |
1974 | + commits=[ |
1975 | + repo_builder.Commit.from_spec( |
1976 | + name='unapplied1', |
1977 | + has_patches=True, |
1978 | + ), |
1979 | + repo_builder.Commit.from_spec( |
1980 | + parents=[Placeholder('unapplied1')], |
1981 | + name='unapplied2', |
1982 | + changelog_versions=['1-2', '1-1'], |
1983 | + has_patches=True, |
1984 | + ), |
1985 | + repo_builder.Commit.from_spec( |
1986 | + name='applied1', |
1987 | + patches_applied=True, |
1988 | + ), |
1989 | + ], |
1990 | + # no branches: technically not possible but branches are not |
1991 | + # relevant to the test |
1992 | + branches={}, |
1993 | + tags={ |
1994 | + 'importer/import/1-1': Placeholder('unapplied1'), |
1995 | + 'importer/import/1-2': Placeholder('unapplied2'), |
1996 | + 'importer/applied/1-1': Placeholder('applied1'), |
1997 | + }, |
1998 | + ), |
1999 | + ['refs/tags/importer/import/1-2'], |
2000 | + ['refs/tags/importer/applied/1-1'], |
2001 | + ), |
2002 | + |
2003 | + # if multiple import tags exist, then do they all end up as parents? |
2004 | + pytest.param( |
2005 | + repo_builder.Repo( |
2006 | + commits=[ |
2007 | + repo_builder.Commit.from_spec( |
2008 | + name='unapplied1', |
2009 | + has_patches=True, |
2010 | + ), |
2011 | + repo_builder.Commit.from_spec( |
2012 | + parents=[Placeholder('unapplied1')], |
2013 | + name='unapplied2', |
2014 | + changelog_versions=['1-2', '1-1'], |
2015 | + has_patches=True, |
2016 | + ), |
2017 | + repo_builder.Commit.from_spec( |
2018 | + parents=[Placeholder('unapplied1')], |
2019 | + name='unapplied2reimport', |
2020 | + changelog_versions=['1-2', '1-1'], |
2021 | + has_patches=True, |
2022 | + mutate='reimport tag', |
2023 | + ), |
2024 | + repo_builder.Commit.from_spec( |
2025 | + name='applied1', |
2026 | + patches_applied=True, |
2027 | + ), |
2028 | + ], |
2029 | + # no branches: technically not possible but branches are not |
2030 | + # relevant to the test |
2031 | + branches={}, |
2032 | + tags={ |
2033 | + 'importer/import/1-1': |
2034 | + Placeholder('unapplied1'), |
2035 | + 'importer/import/1-2': |
2036 | + Placeholder('unapplied2'), |
2037 | + 'importer/reimport/import/1-2/0': |
2038 | + Placeholder('unapplied2'), |
2039 | + 'importer/reimport/import/1-2/1': |
2040 | + Placeholder('unapplied2reimport'), |
2041 | + 'importer/applied/1-1': |
2042 | + Placeholder('applied1'), |
2043 | + }, |
2044 | + ), |
2045 | + [ |
2046 | + 'refs/tags/importer/reimport/import/1-2/0', |
2047 | + 'refs/tags/importer/reimport/import/1-2/1', |
2048 | + ], |
2049 | + [ |
2050 | + 'refs/tags/importer/applied/1-1', |
2051 | + ], |
2052 | + marks=pytest.mark.xfail(reason='LP: #1755247'), |
2053 | + ), |
2054 | + |
2055 | + # do we correctly create a reimport tag because a different import |
2056 | + # already exists? |
2057 | + pytest.param( |
2058 | + repo_builder.Repo( |
2059 | + commits=[ |
2060 | + repo_builder.Commit.from_spec( |
2061 | + name='unapplied1', |
2062 | + has_patches=True, |
2063 | + ), |
2064 | + repo_builder.Commit.from_spec( |
2065 | + name='unapplied1_reimport', |
2066 | + has_patches=True, |
2067 | + mutate='reimport contents', |
2068 | + ), |
2069 | + repo_builder.Commit.from_spec( |
2070 | + parents=[Placeholder('unapplied1')], |
2071 | + name='unapplied2', |
2072 | + changelog_versions=['1-2', '1-1'], |
2073 | + has_patches=True, |
2074 | + ), |
2075 | + repo_builder.Commit.from_spec( |
2076 | + name='applied1', |
2077 | + patches_applied=True, |
2078 | + ), |
2079 | + repo_builder.Commit.from_spec( |
2080 | + name='applied1_reimport', |
2081 | + patches_applied=True, |
2082 | + mutate='reimport contents', |
2083 | + ), |
2084 | + ], |
2085 | + # no branches: technically not possible but branches are not |
2086 | + # relevant to the test |
2087 | + branches={}, |
2088 | + tags={ |
2089 | + 'importer/import/1-1': |
2090 | + Placeholder('unapplied1'), |
2091 | + 'importer/reimport/import/1-1/0': |
2092 | + Placeholder('unapplied1'), |
2093 | + 'importer/reimport/import/1-1/1': |
2094 | + Placeholder('unapplied1_reimport'), |
2095 | + 'importer/import/1-2': |
2096 | + Placeholder('unapplied2'), |
2097 | + 'importer/applied/1-1': |
2098 | + Placeholder('applied1'), |
2099 | + 'importer/reimport/applied/1-1/0': |
2100 | + Placeholder('applied1'), |
2101 | + 'importer/reimport/applied/1-1/1': |
2102 | + Placeholder('applied1_reimport'), |
2103 | + }, |
2104 | + ), |
2105 | + [ |
2106 | + 'refs/tags/importer/reimport/import/1-2/0', |
2107 | + 'refs/tags/importer/reimport/import/1-2/1', |
2108 | + ], |
2109 | + [ |
2110 | + 'refs/tags/importer/reimport/applied/1-1/0', |
2111 | + 'refs/tags/importer/reimport/applied/1-1/1', |
2112 | + ], |
2113 | + marks=pytest.mark.xfail(reason='LP: #1755247'), |
2114 | + ), |
2115 | + ], |
2116 | +) |
2117 | +@patch('gitubuntu.importer.get_import_tag_msg') |
2118 | +@patch('gitubuntu.importer.get_import_commit_msg') |
2119 | +def test_import_applied_spi_parenting( |
2120 | + get_import_commit_msg_mock, |
2121 | + get_import_tag_msg_mock, |
2122 | + repo, |
2123 | + input_repo, |
2124 | + expected_ancestor_commits, |
2125 | + expected_parent_commits, |
2126 | +): |
2127 | + """Test that applied import commits have the right parents |
2128 | + |
2129 | + :param unittest.mock.Mock get_import_commit_msg_mock: mock of the function |
2130 | + that determines the commit message to use for a given import |
2131 | + :param unittest.mock.Mock get_import_tag_msg_mock: mock of the function |
2132 | + that determines the tag message to use for a given import |
2133 | + :param repo gitubuntu.git_repository.GitUbuntuRepository: fixture to hold a |
2134 | + temporary output repository |
2135 | + :param repo_builder.Repo input_repo: input repository data |
2136 | + :param list(str) expected_ancestor_commits: list of commit-ish strings that |
2137 | + must be ancestors of the 'applied/1-2' tag following the applied import |
2138 | + :param list(str) expected_parent_commits: list of commit-ish strings that |
2139 | + must be parents of the 'applied/1-2' tag following the applied import. |
2140 | + |
2141 | + A fake package with version '1-2' that has a changelog parent of '1-1' is |
2142 | + imported on top of the provided input_repo. The test fails if any |
2143 | + of the expected_ancestor_commits or expected_parent_commits are not |
2144 | + present. |
2145 | + |
2146 | + This test is ugly because we do not yet have a programmatic way |
2147 | + to get the interstitial commits of the patch applications. |
2148 | + """ |
2149 | + # Match the repo_builder objects |
2150 | + get_import_tag_msg_mock.return_value = 'Test tag' |
2151 | + get_import_commit_msg_mock.return_value = b'Test commit' |
2152 | + |
2153 | + input_repo.write(repo.raw_repo) |
2154 | + |
2155 | + publish_spec = source_builder.SourceSpec( |
2156 | + changelog_versions=['1-2', '1-1'], |
2157 | + has_patches=True, |
2158 | + ) |
2159 | + |
2160 | + with patch_get_commit_environment(repo): |
2161 | + with source_builder.Source(publish_spec) as dsc_path: |
2162 | + target.import_applied_spi( |
2163 | + repo=repo, |
2164 | + spi=MockSPI(dsc_path, publish_spec.version), |
2165 | + namespace='importer', |
2166 | + allow_applied_failures=False, |
2167 | + parent_overrides={}, |
2168 | + ) |
2169 | + |
2170 | + applied_tag = repo.raw_repo.lookup_reference( |
2171 | + 'refs/tags/importer/applied/1-2', |
2172 | + ) |
2173 | + applied_commit = applied_tag.peel(pygit2.Commit) |
2174 | + applied_commit_parents = applied_commit.parents |
2175 | + |
2176 | + # convert refs to commits |
2177 | + expected_ancestor_commit_hashes = [ |
2178 | + str( |
2179 | + repo.raw_repo.lookup_reference( |
2180 | + expected_ancestor |
2181 | + ).peel(pygit2.Commit).id |
2182 | + ) |
2183 | + for expected_ancestor in expected_ancestor_commits |
2184 | + ] |
2185 | + expected_parent_commit_hashes = [ |
2186 | + str( |
2187 | + repo.raw_repo.lookup_reference( |
2188 | + expected_parent |
2189 | + ).peel(pygit2.Commit).id |
2190 | + ) |
2191 | + for expected_parent in expected_parent_commits |
2192 | + ] |
2193 | + |
2194 | + missing_ancestor_commit_hashes = [] |
2195 | + for ancestor_commit_hash in expected_ancestor_commit_hashes: |
2196 | + # We use merge_base as an ancestry test. If a merge base against an |
2197 | + # expected ancestor isn't the expected ancestor itself, then it isn't |
2198 | + # an ancestor. |
2199 | + merge_base = repo.raw_repo.merge_base( |
2200 | + repo.raw_repo.get(ancestor_commit_hash).id, |
2201 | + applied_commit.id, |
2202 | + ) |
2203 | + if str( |
2204 | + repo.raw_repo.get(merge_base).peel(pygit2.Commit).id |
2205 | + ) != ancestor_commit_hash: |
2206 | + missing_ancestor_commit_hashes.append(ancestor_commit_hash) |
2207 | + |
2208 | + missing_parent_commit_hashes = [] |
2209 | + for parent_commit_hash in expected_parent_commit_hashes: |
2210 | + for parent in applied_commit_parents: |
2211 | + if str(parent.id) == parent_commit_hash: |
2212 | + break |
2213 | + else: |
2214 | + missing_parent_commit_hashes.append(parent_commit_hash) |
2215 | + |
2216 | + assert ( |
2217 | + not missing_ancestor_commit_hashes and |
2218 | + not missing_parent_commit_hashes |
2219 | + ) |
2220 | diff --git a/gitubuntu/repo_builder.py b/gitubuntu/repo_builder.py |
2221 | index 94bc62a..c779c71 100644 |
2222 | --- a/gitubuntu/repo_builder.py |
2223 | +++ b/gitubuntu/repo_builder.py |
2224 | @@ -1,21 +1,10 @@ |
2225 | import binascii |
2226 | import copy |
2227 | -import functools |
2228 | -import tempfile |
2229 | -import unittest |
2230 | |
2231 | import pygit2 |
2232 | -import pytest |
2233 | |
2234 | import gitubuntu.importer |
2235 | -from gitubuntu.test_fixtures import ( |
2236 | - repo, |
2237 | - pygit2_repo, |
2238 | -) |
2239 | - |
2240 | -# Only for tests; can move to repo_builder_test when the move is done |
2241 | -import gitubuntu.git_repository |
2242 | -from gitubuntu.source_builder import Source |
2243 | +from gitubuntu.source_builder import Source, SourceSpec |
2244 | |
2245 | |
2246 | """Build test git repositories as data structures |
2247 | @@ -206,12 +195,62 @@ class SourceTree(NamedMixin, WriteMixin): |
2248 | |
2249 | |
2250 | class Commit(NamedMixin, WriteMixin): |
2251 | - def __init__(self, tree, parents=None, message=None, **kwargs): |
2252 | + def __init__(self, tree=None, parents=None, message=None, **kwargs): |
2253 | + """Construct a Commit object |
2254 | + |
2255 | + :param Tree tree: the tree contained by the commit. If None, an empty |
2256 | + tree is used. |
2257 | + :param list(Commit) parents: the commit objects that are the parents of |
2258 | + this commit. The list may be empty if the commit is to have no |
2259 | + parent. |
2260 | + :param str message: the commit message. |
2261 | + :param **kwargs: other parameters supplied to superclasses. |
2262 | + """ |
2263 | super().__init__(**kwargs) |
2264 | - self.tree = tree |
2265 | + self.tree = tree or Tree({}) |
2266 | self.parents = parents or [] |
2267 | self.message = 'Test commit' if message is None else message |
2268 | |
2269 | + @classmethod |
2270 | + def from_spec( |
2271 | + cls, |
2272 | + parents=None, |
2273 | + message=None, |
2274 | + name=None, |
2275 | + patches_applied=False, |
2276 | + **kwargs, |
2277 | + ): |
2278 | + """Construct a Commit object containing a test source package |
2279 | + |
2280 | + :param list(Commit) parents: the commit objects that are the parents of |
2281 | + this commit. The list may be empty if the commit is to have no |
2282 | + parent. |
2283 | + :param str message: the commit message. |
2284 | + :param str name: the value for the name attribute of the constructed |
2285 | + object, used for matching with Placeholder objects after |
2286 | + construction. |
2287 | + :param bool patches_applied: whether the tree inside the commit should |
2288 | + have patches applied. Passed to the SourceTree constructor. If |
2289 | + True, then the test source package is generated with patches using |
2290 | + the has_patches argument to the SourceSpec constructor, unless |
2291 | + overridden by kwargs. |
2292 | + :param **kwargs: additional parameters are passed to the SourceSpec |
2293 | + constructor to customise the test source package used for the tree |
2294 | + of the commit. |
2295 | + """ |
2296 | + spec_args = {'has_patches': patches_applied} |
2297 | + spec_args.update(kwargs) |
2298 | + |
2299 | + return cls( |
2300 | + tree=SourceTree( |
2301 | + Source(SourceSpec(**spec_args)), |
2302 | + patches_applied=patches_applied, |
2303 | + ), |
2304 | + parents=parents, |
2305 | + message=message, |
2306 | + name=name, |
2307 | + ) |
2308 | + |
2309 | def replace(self, old, new): |
2310 | for i, parent in enumerate(self.parents): |
2311 | if parent is old: |
2312 | @@ -248,10 +287,10 @@ class Repo: |
2313 | This is affectively a commit container that exists for the |
2314 | convenience of writing them to a git repository at once. |
2315 | """ |
2316 | - def __init__(self, commit_list, branches=None, tags=None): |
2317 | + def __init__(self, commits=None, branches=None, tags=None): |
2318 | """Construct a Repo instance |
2319 | |
2320 | - :param list(Commit) commit_list: the commits this Repo should |
2321 | + :param list(Commit) commits: the commits this Repo should |
2322 | contain. These may be related to each other with parenting |
2323 | relationships, but do not have to be. |
2324 | :param dict(str: Commit or Placeholder) branches: the branches |
2325 | @@ -262,7 +301,7 @@ class Repo: |
2326 | should contain. A tag has a name and a target to point to. |
2327 | The target can be a Commit object or Placeholder object. |
2328 | """ |
2329 | - self.commit_list = commit_list |
2330 | + self.commit_list = commits or list() |
2331 | self.branches = branches or dict() |
2332 | self.tags = tags or dict() |
2333 | |
2334 | @@ -308,11 +347,28 @@ class Repo: |
2335 | for _, tag in self.tags.items(): |
2336 | yield self, tag |
2337 | |
2338 | - def copy(self, commit_list_to_add, branches_to_update, tags_to_add): |
2339 | + def copy(self, add_commits=None, update_branches=None, update_tags=None): |
2340 | + """Clone the Repo, optionally with some changes |
2341 | + |
2342 | + :param list(Commit) add_commits: commits to add to the cloned Repo |
2343 | + :param dict(str: Commit or Placeholder) branches: branches to update in |
2344 | + the cloned Repo. |
2345 | + :param dict(str: Commit or Placeholder) tags: tags to update in the |
2346 | + cloned Repo. |
2347 | + |
2348 | + "update" is in the sense of a dict object's update method: this allows |
2349 | + for both the definition of new branches and tags, and updates to |
2350 | + existing branches and tags. |
2351 | + |
2352 | + There is currently no facility to delete branches or tags. |
2353 | + """ |
2354 | new_repo = copy.deepcopy(self) |
2355 | - new_repo.commit_list.extend(commit_list_to_add) |
2356 | - new_repo.branches.update(branches_to_update) |
2357 | - new_repo.tags.update(tags_to_add) |
2358 | + if add_commits: |
2359 | + new_repo.commit_list.extend(add_commits) |
2360 | + if update_branches: |
2361 | + new_repo.branches.update(update_branches) |
2362 | + if update_tags: |
2363 | + new_repo.tags.update(update_tags) |
2364 | return new_repo |
2365 | |
2366 | |
2367 | @@ -336,227 +392,3 @@ def replace_placeholders(top): |
2368 | for parent, obj in top.walk(): |
2369 | if isinstance(obj, Placeholder): |
2370 | parent.replace(obj, find_node(top, obj.target_name)) |
2371 | - |
2372 | - |
2373 | -class TestObjectCreation(unittest.TestCase): |
2374 | - def setUp(self): |
2375 | - self.git_dir = tempfile.TemporaryDirectory() |
2376 | - self.repo = pygit2.init_repository(self.git_dir.name) |
2377 | - |
2378 | - def tearDown(self): |
2379 | - self.repo = None |
2380 | - self.git_dir.cleanup() |
2381 | - |
2382 | - def testBlobCreation(self): |
2383 | - ref = Blob(b'foo').write(self.repo) |
2384 | - assert self.repo.get(ref).data == b'foo' |
2385 | - |
2386 | - def testExecutableBlobTreeCreation(self): |
2387 | - tree_ref = Tree({'foo': ExecutableBlob(b'bar')}).write(self.repo) |
2388 | - tree_entry = self.repo.get(tree_ref)['foo'] |
2389 | - assert self.repo.get(tree_entry.id).data == b'bar' |
2390 | - assert tree_entry.filemode == pygit2.GIT_FILEMODE_BLOB_EXECUTABLE |
2391 | - |
2392 | - def testSymlinkTreeCreation(self): |
2393 | - tree_ref = Tree({'foo': Symlink(b'bar')}).write(self.repo) |
2394 | - tree_entry = self.repo.get(tree_ref)['foo'] |
2395 | - assert tree_entry.filemode == pygit2.GIT_FILEMODE_LINK |
2396 | - |
2397 | - def testCommitCreation(self): |
2398 | - tree = Tree({'foo': Blob(b'bar')}) |
2399 | - tree_ref = tree.write(self.repo) |
2400 | - commit_ref = Commit(tree).write(self.repo) |
2401 | - commit = self.repo.get(commit_ref) |
2402 | - assert commit.tree_id == tree_ref |
2403 | - |
2404 | - def testCommitMessage(self): |
2405 | - ref = Commit(Tree({}), message='foo').write(self.repo) |
2406 | - commit = self.repo.get(ref) |
2407 | - assert commit.message == 'foo' |
2408 | - |
2409 | - def testCommitParents(self): |
2410 | - tree = Tree({}) |
2411 | - top = Commit(tree, message='top') |
2412 | - top_ref = top.write(self.repo) |
2413 | - child_a = Commit(tree, parents=[top], message='child_a') |
2414 | - child_a_ref = child_a.write(self.repo) |
2415 | - child_b = Commit(tree, parents=[top], message='child_b') |
2416 | - child_b_ref = child_b.write(self.repo) |
2417 | - merge = Commit(tree, parents=[child_a, child_b], message='merge') |
2418 | - merge_ref = merge.write(self.repo) |
2419 | - |
2420 | - merge_commit = self.repo.get(merge_ref) |
2421 | - assert merge_commit.parent_ids == [child_a_ref, child_b_ref] |
2422 | - |
2423 | - child_a_commit = self.repo.get(child_a_ref) |
2424 | - assert child_a_commit.parent_ids == [top_ref] |
2425 | - |
2426 | - top_commit = self.repo.get(top_ref) |
2427 | - assert top_commit.parent_ids == [] |
2428 | - |
2429 | - def testNameSearch(self): |
2430 | - inner_tree = Tree({'foo': Blob(b'bar', name='blob')}, name='inner') |
2431 | - outer_tree = Tree({'baz': inner_tree}, name='outer') |
2432 | - commit = Commit(outer_tree, name='commit') |
2433 | - assert find_node(commit, 'commit') is commit |
2434 | - assert find_node(commit, 'inner') is inner_tree |
2435 | - assert find_node(commit, 'outer') is outer_tree |
2436 | - assert isinstance(find_node(commit, 'blob'), Blob) |
2437 | - with pytest.raises(KeyError): |
2438 | - find_node(commit, 'absent') |
2439 | - |
2440 | - def testRepo(self): |
2441 | - graph = Repo([ |
2442 | - Commit(Tree({}), parents=[Placeholder('parent')]), |
2443 | - Commit(Tree({}), name='parent'), |
2444 | - ]) |
2445 | - child_ref = graph.write(self.repo) |
2446 | - child = self.repo.get(child_ref) |
2447 | - assert child.parent_ids == [graph.commit_list[1].write(self.repo)] |
2448 | - |
2449 | - def testRepoBranchesTags(self): |
2450 | - graph = Repo( |
2451 | - commit_list=[ |
2452 | - Commit( |
2453 | - Tree({}), |
2454 | - parents=[Placeholder('parent')], |
2455 | - name='child', |
2456 | - ), |
2457 | - Commit(Tree({}), name='parent'), |
2458 | - ], |
2459 | - branches={ |
2460 | - 'branch1': Placeholder('parent'), |
2461 | - 'branch2': Commit(Tree({'foo': Blob(b'qux')})), |
2462 | - }, |
2463 | - tags={ |
2464 | - 'tag1': Placeholder('child'), |
2465 | - 'tag2': Commit(Tree({'foo': Blob(b'quz')})), |
2466 | - }, |
2467 | - ) |
2468 | - child_ref = graph.write(self.repo) |
2469 | - child = self.repo.get(child_ref) |
2470 | - assert self.repo.lookup_reference( |
2471 | - 'refs/heads/branch1' |
2472 | - ).peel(pygit2.Commit).id == graph.commit_list[1].write(self.repo) |
2473 | - assert self.repo.lookup_reference('refs/heads/branch2') |
2474 | - assert self.repo.lookup_reference( |
2475 | - 'refs/tags/tag1' |
2476 | - ).peel(pygit2.Commit).id == graph.commit_list[0].write(self.repo) |
2477 | - assert self.repo.lookup_reference('refs/tags/tag2') |
2478 | - assert child.parent_ids == [graph.commit_list[1].write(self.repo)] |
2479 | - |
2480 | - |
2481 | -def test_source_tree(pygit2_repo): |
2482 | - commit_str = Commit(SourceTree(Source())).write(pygit2_repo) |
2483 | - commit = pygit2_repo.get(commit_str) |
2484 | - assert gitubuntu.git_repository.follow_symlinks_to_blob( |
2485 | - repo=pygit2_repo, |
2486 | - treeish_object=commit, |
2487 | - path='debian/changelog', |
2488 | - ) |
2489 | - |
2490 | - |
2491 | -@pytest.mark.parametrize('cls', [ |
2492 | - functools.partial(Blob, b'qux'), |
2493 | - functools.partial(ExecutableBlob, b'qux'), |
2494 | - functools.partial(Symlink, 'target'), |
2495 | - functools.partial(Tree, {}), |
2496 | - functools.partial(SourceTree, {}), |
2497 | -]) |
2498 | -def test_replace_placeholders(cls): |
2499 | - common_blob = cls(name='name') |
2500 | - top = Tree({'foo': common_blob, 'bar': Placeholder('name')}) |
2501 | - assert top.entries['foo'] is not top.entries['bar'] |
2502 | - replace_placeholders(top) |
2503 | - assert top.entries['foo'] is top.entries['bar'] |
2504 | - assert isinstance(top.entries['bar'], type(common_blob)) |
2505 | - |
2506 | - |
2507 | -# The following test uses replace directly, because |
2508 | -# replace_placeholders' use of find_node will raise its own KeyError. |
2509 | -@pytest.mark.parametrize('testobj', [ |
2510 | - Tree({}), |
2511 | - Commit(Tree({})), |
2512 | - Repo(commit_list=[]), |
2513 | -]) |
2514 | -def test_replace_missing_placeholder(testobj): |
2515 | - commit = Commit(Tree({})) |
2516 | - with pytest.raises(KeyError): |
2517 | - testobj.replace(Placeholder('name'), commit) |
2518 | - |
2519 | - |
2520 | -def test_repo_placeholder(): |
2521 | - empty_tree = Tree({}) |
2522 | - graph = Repo( |
2523 | - commit_list=[ |
2524 | - Commit(empty_tree, message='top', name='top'), |
2525 | - Commit( |
2526 | - empty_tree, |
2527 | - message='child', |
2528 | - parents=[Placeholder('top')], |
2529 | - name='child', |
2530 | - ), |
2531 | - ], |
2532 | - branches={ |
2533 | - 'branch1': Placeholder('top'), |
2534 | - 'branch2': Placeholder('child'), |
2535 | - }, |
2536 | - tags={ |
2537 | - 'tag1': Placeholder('child'), |
2538 | - 'tag2': Placeholder('top'), |
2539 | - }, |
2540 | - ) |
2541 | - replace_placeholders(graph) |
2542 | - assert graph.commit_list[1].parents[0] is graph.commit_list[0] |
2543 | - assert graph.branches['branch1'] is graph.commit_list[0] |
2544 | - assert graph.branches['branch2'] is graph.commit_list[1] |
2545 | - assert graph.tags['tag2'] is graph.commit_list[0] |
2546 | - assert graph.tags['tag1'] is graph.commit_list[1] |
2547 | - |
2548 | - |
2549 | -def test_copy(): |
2550 | - graph = Repo( |
2551 | - commit_list=[ |
2552 | - Commit(Tree({}), message='top', name='top'), |
2553 | - Commit( |
2554 | - Tree({}), |
2555 | - message='child', |
2556 | - parents=[Placeholder('top')], |
2557 | - name='child', |
2558 | - ), |
2559 | - ], |
2560 | - branches={ |
2561 | - 'branch1': Placeholder('top'), |
2562 | - 'branch2': Placeholder('child'), |
2563 | - }, |
2564 | - tags={ |
2565 | - 'tag1': Placeholder('child'), |
2566 | - }, |
2567 | - ) |
2568 | - copy_graph = graph.copy( |
2569 | - commit_list_to_add=[ |
2570 | - Commit(Tree({}), message='new', name='new'), |
2571 | - ], |
2572 | - branches_to_update={ |
2573 | - 'branch2': Placeholder('new'), |
2574 | - 'branch3': Placeholder('new'), |
2575 | - }, |
2576 | - tags_to_add={ |
2577 | - 'tag2': Placeholder('new'), |
2578 | - 'tag3': Placeholder('top'), |
2579 | - } |
2580 | - ) |
2581 | - replace_placeholders(graph) |
2582 | - replace_placeholders(copy_graph) |
2583 | - |
2584 | - assert graph.commit_list[1].parents[0] is graph.commit_list[0] |
2585 | - assert graph.branches['branch1'] is graph.commit_list[0] |
2586 | - assert graph.tags['tag1'] is graph.commit_list[1] |
2587 | - |
2588 | - assert copy_graph.commit_list[2].message == 'new' |
2589 | - assert copy_graph.branches['branch1'] is copy_graph.commit_list[0] |
2590 | - assert copy_graph.branches['branch2'] is copy_graph.commit_list[2] |
2591 | - assert copy_graph.branches['branch3'] is copy_graph.commit_list[2] |
2592 | - assert copy_graph.tags['tag1'] is copy_graph.commit_list[1] |
2593 | - assert copy_graph.tags['tag2'] is copy_graph.commit_list[2] |
2594 | - assert copy_graph.tags['tag3'] is copy_graph.commit_list[0] |
2595 | diff --git a/gitubuntu/repo_builder_test.py b/gitubuntu/repo_builder_test.py |
2596 | new file mode 100644 |
2597 | index 0000000..bfe053f |
2598 | --- /dev/null |
2599 | +++ b/gitubuntu/repo_builder_test.py |
2600 | @@ -0,0 +1,284 @@ |
2601 | +import functools |
2602 | +import tempfile |
2603 | +import unittest |
2604 | +from unittest.mock import sentinel |
2605 | + |
2606 | +import pygit2 |
2607 | +import pytest |
2608 | + |
2609 | +import gitubuntu.git_repository |
2610 | +from gitubuntu.repo_builder import ( |
2611 | + Blob, |
2612 | + Commit, |
2613 | + ExecutableBlob, |
2614 | + Placeholder, |
2615 | + Repo, |
2616 | + Source, |
2617 | + SourceTree, |
2618 | + Symlink, |
2619 | + Tree, |
2620 | + find_node, |
2621 | + replace_placeholders, |
2622 | +) |
2623 | +from gitubuntu.test_fixtures import ( |
2624 | + repo, |
2625 | + pygit2_repo, |
2626 | +) |
2627 | + |
2628 | + |
2629 | +class TestObjectCreation(unittest.TestCase): |
2630 | + def setUp(self): |
2631 | + self.git_dir = tempfile.TemporaryDirectory() |
2632 | + self.repo = pygit2.init_repository(self.git_dir.name) |
2633 | + |
2634 | + def tearDown(self): |
2635 | + self.repo = None |
2636 | + self.git_dir.cleanup() |
2637 | + |
2638 | + def testBlobCreation(self): |
2639 | + ref = Blob(b'foo').write(self.repo) |
2640 | + assert self.repo.get(ref).data == b'foo' |
2641 | + |
2642 | + def testExecutableBlobTreeCreation(self): |
2643 | + tree_ref = Tree({'foo': ExecutableBlob(b'bar')}).write(self.repo) |
2644 | + tree_entry = self.repo.get(tree_ref)['foo'] |
2645 | + assert self.repo.get(tree_entry.id).data == b'bar' |
2646 | + assert tree_entry.filemode == pygit2.GIT_FILEMODE_BLOB_EXECUTABLE |
2647 | + |
2648 | + def testSymlinkTreeCreation(self): |
2649 | + tree_ref = Tree({'foo': Symlink(b'bar')}).write(self.repo) |
2650 | + tree_entry = self.repo.get(tree_ref)['foo'] |
2651 | + assert tree_entry.filemode == pygit2.GIT_FILEMODE_LINK |
2652 | + |
2653 | + def testCommitCreation(self): |
2654 | + tree = Tree({'foo': Blob(b'bar')}) |
2655 | + tree_ref = tree.write(self.repo) |
2656 | + commit_ref = Commit(tree).write(self.repo) |
2657 | + commit = self.repo.get(commit_ref) |
2658 | + assert commit.tree_id == tree_ref |
2659 | + |
2660 | + def testCommitMessage(self): |
2661 | + ref = Commit(Tree({}), message='foo').write(self.repo) |
2662 | + commit = self.repo.get(ref) |
2663 | + assert commit.message == 'foo' |
2664 | + |
2665 | + def testCommitParents(self): |
2666 | + tree = Tree({}) |
2667 | + top = Commit(tree, message='top') |
2668 | + top_ref = top.write(self.repo) |
2669 | + child_a = Commit(tree, parents=[top], message='child_a') |
2670 | + child_a_ref = child_a.write(self.repo) |
2671 | + child_b = Commit(tree, parents=[top], message='child_b') |
2672 | + child_b_ref = child_b.write(self.repo) |
2673 | + merge = Commit(tree, parents=[child_a, child_b], message='merge') |
2674 | + merge_ref = merge.write(self.repo) |
2675 | + |
2676 | + merge_commit = self.repo.get(merge_ref) |
2677 | + assert merge_commit.parent_ids == [child_a_ref, child_b_ref] |
2678 | + |
2679 | + child_a_commit = self.repo.get(child_a_ref) |
2680 | + assert child_a_commit.parent_ids == [top_ref] |
2681 | + |
2682 | + top_commit = self.repo.get(top_ref) |
2683 | + assert top_commit.parent_ids == [] |
2684 | + |
2685 | + def testNameSearch(self): |
2686 | + inner_tree = Tree({'foo': Blob(b'bar', name='blob')}, name='inner') |
2687 | + outer_tree = Tree({'baz': inner_tree}, name='outer') |
2688 | + commit = Commit(outer_tree, name='commit') |
2689 | + assert find_node(commit, 'commit') is commit |
2690 | + assert find_node(commit, 'inner') is inner_tree |
2691 | + assert find_node(commit, 'outer') is outer_tree |
2692 | + assert isinstance(find_node(commit, 'blob'), Blob) |
2693 | + with pytest.raises(KeyError): |
2694 | + find_node(commit, 'absent') |
2695 | + |
2696 | + def testRepo(self): |
2697 | + graph = Repo([ |
2698 | + Commit(Tree({}), parents=[Placeholder('parent')]), |
2699 | + Commit(Tree({}), name='parent'), |
2700 | + ]) |
2701 | + child_ref = graph.write(self.repo) |
2702 | + child = self.repo.get(child_ref) |
2703 | + assert child.parent_ids == [graph.commit_list[1].write(self.repo)] |
2704 | + |
2705 | + def testRepoBranchesTags(self): |
2706 | + graph = Repo( |
2707 | + commits=[ |
2708 | + Commit( |
2709 | + Tree({}), |
2710 | + parents=[Placeholder('parent')], |
2711 | + name='child', |
2712 | + ), |
2713 | + Commit(Tree({}), name='parent'), |
2714 | + ], |
2715 | + branches={ |
2716 | + 'branch1': Placeholder('parent'), |
2717 | + 'branch2': Commit(Tree({'foo': Blob(b'qux')})), |
2718 | + }, |
2719 | + tags={ |
2720 | + 'tag1': Placeholder('child'), |
2721 | + 'tag2': Commit(Tree({'foo': Blob(b'quz')})), |
2722 | + }, |
2723 | + ) |
2724 | + child_ref = graph.write(self.repo) |
2725 | + child = self.repo.get(child_ref) |
2726 | + assert self.repo.lookup_reference( |
2727 | + 'refs/heads/branch1' |
2728 | + ).peel(pygit2.Commit).id == graph.commit_list[1].write(self.repo) |
2729 | + assert self.repo.lookup_reference('refs/heads/branch2') |
2730 | + assert self.repo.lookup_reference( |
2731 | + 'refs/tags/tag1' |
2732 | + ).peel(pygit2.Commit).id == graph.commit_list[0].write(self.repo) |
2733 | + assert self.repo.lookup_reference('refs/tags/tag2') |
2734 | + assert child.parent_ids == [graph.commit_list[1].write(self.repo)] |
2735 | + |
2736 | + |
2737 | +def test_source_tree(pygit2_repo): |
2738 | + commit_str = Commit(SourceTree(Source())).write(pygit2_repo) |
2739 | + commit = pygit2_repo.get(commit_str) |
2740 | + assert gitubuntu.git_repository.follow_symlinks_to_blob( |
2741 | + repo=pygit2_repo, |
2742 | + treeish_object=commit, |
2743 | + path='debian/changelog', |
2744 | + ) |
2745 | + |
2746 | + |
2747 | +@pytest.mark.parametrize('cls', [ |
2748 | + functools.partial(Blob, b'qux'), |
2749 | + functools.partial(ExecutableBlob, b'qux'), |
2750 | + functools.partial(Symlink, 'target'), |
2751 | + functools.partial(Tree, {}), |
2752 | + functools.partial(SourceTree, {}), |
2753 | +]) |
2754 | +def test_replace_placeholders(cls): |
2755 | + common_blob = cls(name='name') |
2756 | + top = Tree({'foo': common_blob, 'bar': Placeholder('name')}) |
2757 | + assert top.entries['foo'] is not top.entries['bar'] |
2758 | + replace_placeholders(top) |
2759 | + assert top.entries['foo'] is top.entries['bar'] |
2760 | + assert isinstance(top.entries['bar'], type(common_blob)) |
2761 | + |
2762 | + |
2763 | +# The following test uses replace directly, because |
2764 | +# replace_placeholders' use of find_node will raise its own KeyError. |
2765 | +@pytest.mark.parametrize('testobj', [ |
2766 | + Tree({}), |
2767 | + Commit(Tree({})), |
2768 | + Repo(), |
2769 | +]) |
2770 | +def test_replace_missing_placeholder(testobj): |
2771 | + commit = Commit(Tree({})) |
2772 | + with pytest.raises(KeyError): |
2773 | + testobj.replace(Placeholder('name'), commit) |
2774 | + |
2775 | + |
2776 | +def test_repo_placeholder(): |
2777 | + empty_tree = Tree({}) |
2778 | + graph = Repo( |
2779 | + commits=[ |
2780 | + Commit(empty_tree, message='top', name='top'), |
2781 | + Commit( |
2782 | + empty_tree, |
2783 | + message='child', |
2784 | + parents=[Placeholder('top')], |
2785 | + name='child', |
2786 | + ), |
2787 | + ], |
2788 | + branches={ |
2789 | + 'branch1': Placeholder('top'), |
2790 | + 'branch2': Placeholder('child'), |
2791 | + }, |
2792 | + tags={ |
2793 | + 'tag1': Placeholder('child'), |
2794 | + 'tag2': Placeholder('top'), |
2795 | + }, |
2796 | + ) |
2797 | + replace_placeholders(graph) |
2798 | + assert graph.commit_list[1].parents[0] is graph.commit_list[0] |
2799 | + assert graph.branches['branch1'] is graph.commit_list[0] |
2800 | + assert graph.branches['branch2'] is graph.commit_list[1] |
2801 | + assert graph.tags['tag2'] is graph.commit_list[0] |
2802 | + assert graph.tags['tag1'] is graph.commit_list[1] |
2803 | + |
2804 | + |
2805 | +def test_copy(): |
2806 | + graph = Repo( |
2807 | + commits=[ |
2808 | + Commit(Tree({}), message='top', name='top'), |
2809 | + Commit( |
2810 | + Tree({}), |
2811 | + message='child', |
2812 | + parents=[Placeholder('top')], |
2813 | + name='child', |
2814 | + ), |
2815 | + ], |
2816 | + branches={ |
2817 | + 'branch1': Placeholder('top'), |
2818 | + 'branch2': Placeholder('child'), |
2819 | + }, |
2820 | + tags={ |
2821 | + 'tag1': Placeholder('child'), |
2822 | + }, |
2823 | + ) |
2824 | + copy_graph = graph.copy( |
2825 | + add_commits=[ |
2826 | + Commit(Tree({}), message='new', name='new'), |
2827 | + ], |
2828 | + update_branches={ |
2829 | + 'branch2': Placeholder('new'), |
2830 | + 'branch3': Placeholder('new'), |
2831 | + }, |
2832 | + update_tags={ |
2833 | + 'tag2': Placeholder('new'), |
2834 | + 'tag3': Placeholder('top'), |
2835 | + } |
2836 | + ) |
2837 | + replace_placeholders(graph) |
2838 | + replace_placeholders(copy_graph) |
2839 | + |
2840 | + assert graph.commit_list[1].parents[0] is graph.commit_list[0] |
2841 | + assert graph.branches['branch1'] is graph.commit_list[0] |
2842 | + assert graph.tags['tag1'] is graph.commit_list[1] |
2843 | + |
2844 | + assert copy_graph.commit_list[2].message == 'new' |
2845 | + assert copy_graph.branches['branch1'] is copy_graph.commit_list[0] |
2846 | + assert copy_graph.branches['branch2'] is copy_graph.commit_list[2] |
2847 | + assert copy_graph.branches['branch3'] is copy_graph.commit_list[2] |
2848 | + assert copy_graph.tags['tag1'] is copy_graph.commit_list[1] |
2849 | + assert copy_graph.tags['tag2'] is copy_graph.commit_list[2] |
2850 | + assert copy_graph.tags['tag3'] is copy_graph.commit_list[0] |
2851 | + |
2852 | + |
2853 | +def test_commit_from_spec_parents(): |
2854 | + """Argument parents should end up in Commit object parents attribute""" |
2855 | + commit = Commit.from_spec(parents=sentinel.parents).parents |
2856 | + assert commit == sentinel.parents |
2857 | + |
2858 | + |
2859 | +def test_commit_from_spec_message(): |
2860 | + """Argument message should end up in Commit object message attribute""" |
2861 | + commit = Commit.from_spec(message=sentinel.message).message |
2862 | + assert commit == sentinel.message |
2863 | + |
2864 | + |
2865 | +def test_commit_from_spec_name(): |
2866 | + """Argument name should end up in Commit object name attribute""" |
2867 | + assert Commit.from_spec(name=sentinel.name).name == sentinel.name |
2868 | + |
2869 | + |
2870 | +@pytest.mark.parametrize('patches_applied', [True, False]) |
2871 | +def test_commit_from_spec_patches_applied(patches_applied): |
2872 | + """Check behaviour of patches_applied argument""" |
2873 | + commit = Commit.from_spec(patches_applied=patches_applied) |
2874 | + # Underlying SourceTree object should now have a matching patches_applied |
2875 | + # attribute |
2876 | + assert commit.tree.patches_applied == patches_applied |
2877 | + # Underlying SourceSpec object should now have a matching has_patches |
2878 | + # attribute |
2879 | + assert commit.tree.source.spec.has_patches == patches_applied |
2880 | + |
2881 | + |
2882 | +def test_commit_from_spec_kwargs(): |
2883 | + """Arbitrary arguments should result in adjusted behaviour in SourceSpec""" |
2884 | + assert Commit.from_spec(native=True).tree.source.spec.native |
2885 | diff --git a/gitubuntu/source_builder.py b/gitubuntu/source_builder.py |
2886 | index 024b569..dac9670 100644 |
2887 | --- a/gitubuntu/source_builder.py |
2888 | +++ b/gitubuntu/source_builder.py |
2889 | @@ -62,11 +62,12 @@ NEW_FILE_PATCH_TEMPLATE = """ |
2890 | |
2891 | class SourceSpec: |
2892 | """A high level abstraction of the attributes of a test source package""" |
2893 | - version = '1' |
2894 | - native = True |
2895 | + version = '1-1' |
2896 | + native = False |
2897 | has_patches = False |
2898 | changelog_versions = None |
2899 | file_contents = None |
2900 | + mutate = False |
2901 | reserved_files = [ |
2902 | 'debian/changelog', |
2903 | 'debian/control', |
2904 | @@ -88,6 +89,8 @@ class SourceSpec: |
2905 | package should contain the specified file names with the |
2906 | specified contents. This is used to control the tree hashes |
2907 | of generated source packages. |
2908 | + :param mutate: if bool(mutate) is True, then this will add a file |
2909 | + called debian/mutate containing the data str(mutate). |
2910 | |
2911 | Keyword arguments to the constructor map directly to class instances |
2912 | properties. Properties may be manipulated after construction. |
2913 | @@ -112,8 +115,8 @@ class SourceSpec: |
2914 | |
2915 | # If the version was not explicitly set, toggle the default for |
2916 | # non-native packages |
2917 | - if 'version' not in kwargs and not self.native: |
2918 | - self.version = '1-1' |
2919 | + if 'version' not in kwargs and self.native: |
2920 | + self.version = '1' |
2921 | |
2922 | if self.native and '-' in self.version: |
2923 | raise ValueError("Version must not have a '-' in a native package") |
2924 | @@ -198,7 +201,10 @@ class SourceFiles: |
2925 | :returns: a mapping of filename to content |
2926 | :rtype: dict(str, str) |
2927 | """ |
2928 | - return self.spec.file_contents |
2929 | + result = dict(self.spec.file_contents) |
2930 | + if self.spec.mutate: |
2931 | + result['debian/mutate'] = str(self.spec.mutate) |
2932 | + return result |
2933 | |
2934 | @property |
2935 | def source_format(self): |
2936 | diff --git a/gitubuntu/source_builder_test.py b/gitubuntu/source_builder_test.py |
2937 | index e18e05f..9131a98 100644 |
2938 | --- a/gitubuntu/source_builder_test.py |
2939 | +++ b/gitubuntu/source_builder_test.py |
2940 | @@ -53,12 +53,12 @@ def test_source_is_created(): |
2941 | |
2942 | |
2943 | def test_source_create_with_version(repo): |
2944 | - version = get_spec_changelog_version(repo, version='3') |
2945 | + version = get_spec_changelog_version(repo, version='3', native=True) |
2946 | assert version == '3' |
2947 | |
2948 | |
2949 | def test_source_create_with_versions(repo): |
2950 | - source_spec = target.SourceSpec(changelog_versions=['3', '4']) |
2951 | + source_spec = target.SourceSpec(changelog_versions=['3', '4'], native=True) |
2952 | with target.Source(source_spec) as f: |
2953 | tree_hash = importer.dsc_to_tree_hash(repo.raw_repo, f) |
2954 | changelog = repo.get_changelog_from_treeish(tree_hash) |
2955 | @@ -110,6 +110,19 @@ def test_source_create_fails_with_absolute_path(repo): |
2956 | pass |
2957 | |
2958 | |
2959 | +def test_source_mutate(): |
2960 | + """mutate kwarg results in a debian/mutate file |
2961 | + |
2962 | + If mutate is used, then a file called 'debian/mutate' should be generated |
2963 | + with the same contents. |
2964 | + """ |
2965 | + source = target.Source(target.SourceSpec(mutate='foo')) |
2966 | + |
2967 | + # It is sufficient to just verify spec_files contains what we want here as |
2968 | + # the spec_files functionality is checked in a separate test already. |
2969 | + assert source.files.spec_files['debian/mutate'] == 'foo' |
2970 | + |
2971 | + |
2972 | @pytest.mark.parametrize('native,expected', [ |
2973 | (True, b"3.0 (native)\n"), |
2974 | (False, b"3.0 (quilt)\n"), |
2975 | @@ -124,7 +137,7 @@ def test_source_native_source_format(repo, native, expected): |
2976 | assert blob.data == expected |
2977 | |
2978 | def test_source_quilt_no_patches(repo): |
2979 | - with target.Source(target.SourceSpec(native=False)) as dsc_path: |
2980 | + with target.Source(target.SourceSpec()) as dsc_path: |
2981 | top = dsc_path_to_tree(repo, dsc_path) |
2982 | debian_entry = top['debian'] |
2983 | assert debian_entry.type == 'tree' |
2984 | @@ -134,7 +147,7 @@ def test_source_quilt_no_patches(repo): |
2985 | |
2986 | |
2987 | def test_source_quilt_with_patches(repo): |
2988 | - spec = target.SourceSpec(native=False, has_patches=True) |
2989 | + spec = target.SourceSpec(has_patches=True) |
2990 | with target.Source(spec) as dsc_path: |
2991 | top = dsc_path_to_tree(repo, dsc_path) |
2992 | expected_files = ['series', 'a', 'b'] |
2993 | @@ -155,7 +168,7 @@ def test_source_quilt_with_patches(repo): |
2994 | |
2995 | |
2996 | def test_source_quilt_with_patches_applied(repo): |
2997 | - spec = target.SourceSpec(native=False, has_patches=True) |
2998 | + spec = target.SourceSpec(has_patches=True) |
2999 | with target.Source(spec) as dsc_path: |
3000 | top = dsc_path_to_tree(repo, dsc_path, patches_applied=True) |
3001 | expected_files = [ |
3002 | diff --git a/pytest.ini b/pytest.ini |
3003 | new file mode 100644 |
3004 | index 0000000..d61d029 |
3005 | --- /dev/null |
3006 | +++ b/pytest.ini |
3007 | @@ -0,0 +1,2 @@ |
3008 | +[pytest] |
3009 | +xfail_strict=true |
FAILED: Continuous integration, rev:a1c88d7eca4 aaf6d235332efa7 cd26b8238b35bb /jenkins. ubuntu. com/server/ job/git- ubuntu- ci/409/
https:/
Executed test runs:
SUCCESS: VM Setup
SUCCESS: Build
FAILED: Unit Tests
Click here to trigger a rebuild: /jenkins. ubuntu. com/server/ job/git- ubuntu- ci/409/ rebuild
https:/