Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(165)

Side by Side Diff: recipe_engine/unittests/fetch_test.py

Issue 2720853002: Fix GitBackend.checkout (Closed)
Patch Set: tests Created 3 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 #!/usr/bin/env python 1 #!/usr/bin/env python
2 # Copyright 2016 The LUCI Authors. All rights reserved. 2 # Copyright 2016 The LUCI Authors. All rights reserved.
3 # Use of this source code is governed under the Apache License, Version 2.0 3 # Use of this source code is governed under the Apache License, Version 2.0
4 # that can be found in the LICENSE file. 4 # that can be found in the LICENSE file.
5 5
6 import base64 6 import base64
7 import json 7 import json
8 import unittest 8 import unittest
9 9
10 import test_env 10 import test_env
11 11
12 import mock 12 import mock
13 import subprocess42 13 import subprocess42
14 14
15 from recipe_engine import fetch 15 from recipe_engine import fetch
16 from recipe_engine import requests_ssl 16 from recipe_engine import requests_ssl
17 17
18 def mock_git_dir(*args):
19 return mock.call('GIT', '-C', 'dir', *args)
20
18 21
19 class TestGit(unittest.TestCase): 22 class TestGit(unittest.TestCase):
20 23
21 def setUp(self): 24 def setUp(self):
22 self._patchers = [ 25 self._patchers = [
23 mock.patch('logging.warning'), 26 mock.patch('logging.warning'),
24 mock.patch('logging.exception'), 27 mock.patch('logging.exception'),
25 mock.patch('recipe_engine.fetch.GitBackend.Git._resolve_git', 28 mock.patch('recipe_engine.fetch.GitBackend.Git._resolve_git',
26 return_value='GIT'), 29 return_value='GIT'),
30 mock.patch('os.path.isdir', return_value=False),
31 mock.patch('os.makedirs'),
27 ] 32 ]
28 for p in self._patchers: 33 for p in self._patchers:
29 p.start() 34 p.start()
30 35
31 def tearDown(self): 36 def tearDown(self):
32 for p in reversed(self._patchers): 37 for p in reversed(self._patchers):
33 p.stop() 38 p.stop()
34 39
35 @mock.patch('recipe_engine.fetch.GitBackend.Git._execute') 40 @mock.patch('recipe_engine.fetch.GitBackend.Git._execute')
36 def test_fresh_clone(self, git): 41 def test_fresh_fetch_branch(self, git):
37 fetch.GitBackend().checkout( 42 fetch.GitBackend().checkout(
38 'repo', 'revision', 'dir', allow_fetch=True) 43 'repo', 'refs/heads/master', 'dir', allow_fetch=True)
39 git.assert_has_calls([ 44 git.assert_has_calls([
40 mock.call('GIT', 'clone', '-q', 'repo', 'dir'), 45 mock_git_dir('init'),
41 mock.call('GIT', '-C', 'dir', 'config', 'remote.origin.url', 'repo'), 46 mock_git_dir('fetch', 'repo', 'refs/heads/master'),
42 mock.call('GIT', '-C', 'dir', 'rev-parse', '-q', '--verify', 47 mock_git_dir('checkout', '-q', '-f', 'FETCH_HEAD'),
43 'revision^{commit}'), 48 ])
44 mock.call('GIT', '-C', 'dir', 'reset', '-q', '--hard', 'revision'), 49
50 @mock.patch('recipe_engine.fetch.GitBackend.Git._execute')
51 def test_fresh_fetch_commit(self, git):
52 fetch.GitBackend().checkout(
53 'repo', 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa', 'dir',
54 allow_fetch=True)
55 git.assert_has_calls([
56 mock_git_dir('init'),
57 mock_git_dir('fetch', 'repo', 'refs/heads/master'),
58 mock_git_dir(
59 'checkout', '-q', '-f', 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'),
45 ]) 60 ])
46 61
47 @mock.patch('time.sleep') 62 @mock.patch('time.sleep')
48 @mock.patch('os.path.isdir')
49 @mock.patch('recipe_engine.fetch.GitBackend.Git._execute') 63 @mock.patch('recipe_engine.fetch.GitBackend.Git._execute')
50 def test_fresh_clone_retries(self, git, isdir, sleep): 64 def test_fresh_fetch_retries(self, git, sleep):
51 isdir.return_value = False
52 65
53 clone_fails = [] 66 fetch_fails = []
54 def fail_four_clones(*args): 67 def fail_four_fetches(*args):
55 if 'clone' in args and len(clone_fails) < 4: 68 if 'fetch' in args and len(fetch_fails) < 4:
56 clone_fails.append(True) 69 fetch_fails.append(True)
57 raise subprocess42.CalledProcessError(1, args) 70 raise subprocess42.CalledProcessError(1, args)
58 return None 71 return None
59 git.side_effect = fail_four_clones 72 git.side_effect = fail_four_fetches
60 73
61 fetch.GitBackend().checkout( 74 fetch.GitBackend().checkout(
62 'repo', 'revision', 'dir', allow_fetch=True) 75 'repo', 'refs/heads/master', 'dir', allow_fetch=True)
63 git.assert_has_calls([ 76 git.assert_has_calls([
64 mock.call('GIT', 'clone', '-q', 'repo', 'dir')] * 5 + [ 77 mock_git_dir('init'),
65 mock.call('GIT', '-C', 'dir', 'config', 'remote.origin.url', 'repo'), 78 mock_git_dir('fetch', 'repo', 'refs/heads/master'),
66 mock.call('GIT', '-C', 'dir', 'rev-parse', '-q', '--verify', 79 mock_git_dir('checkout', '-q', '-f', 'FETCH_HEAD'),
67 'revision^{commit}'),
68 mock.call('GIT', '-C', 'dir', 'reset', '-q', '--hard', 'revision'),
69 ]) 80 ])
81 self.assertEqual(len(fetch_fails), 4)
70 82
71 @mock.patch('os.path.isdir') 83 @mock.patch('os.path.isdir')
72 @mock.patch('recipe_engine.fetch.GitBackend.Git._execute') 84 @mock.patch('recipe_engine.fetch.GitBackend.Git._execute')
73 def test_existing_checkout(self, git, isdir): 85 def test_existing_checkout_commit_present(self, git, isdir):
74 isdir.return_value = True 86 isdir.return_value = True
75 fetch.GitBackend().checkout( 87 fetch.GitBackend().checkout(
76 'repo', 'revision', 'dir', allow_fetch=True) 88 'repo', 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa', 'dir',
89 allow_fetch=True)
77 isdir.assert_has_calls([ 90 isdir.assert_has_calls([
78 mock.call('dir'), 91 mock.call('dir'),
79 mock.call('dir/.git'), 92 mock.call('dir/.git'),
80 ]) 93 ])
81 git.assert_has_calls([ 94 git.assert_has_calls([
82 mock.call('GIT', '-C', 'dir', 'config', 'remote.origin.url', 'repo'), 95 mock_git_dir(
83 mock.call('GIT', '-C', 'dir', 'rev-parse', '-q', '--verify', 96 'rev-parse', '-q', '--verify',
84 'revision^{commit}'), 97 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa^{commit}'),
85 mock.call('GIT', '-C', 'dir', 'reset', '-q', '--hard', 'revision'), 98 mock_git_dir('checkout', '-q', '-f',
99 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'),
100 ])
101
102 @mock.patch('os.path.isdir')
103 @mock.patch('recipe_engine.fetch.GitBackend.Git._execute')
104 def test_existing_checkout_commit_present_no_fetch(self, git, isdir):
105 isdir.return_value = True
106 fetch.GitBackend().checkout(
107 'repo', 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa', 'dir',
108 allow_fetch=False)
109 isdir.assert_has_calls([
110 mock.call('dir'),
111 mock.call('dir/.git'),
112 ])
113 git.assert_has_calls([
114 mock_git_dir(
115 'rev-parse', '-q', '--verify',
116 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa^{commit}'),
117 mock_git_dir('checkout', '-q', '-f',
118 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'),
119 ])
120
121 @mock.patch('os.path.isdir')
122 @mock.patch('recipe_engine.fetch.GitBackend.Git._execute')
123 def test_existing_checkout_commit_not_present(self, git, isdir):
124 isdir.return_value = True
125 git.side_effect = [
126 subprocess42.CalledProcessError(1, ['git-rev-parse']),
127 None,
128 None,
129 None,
130 ]
131 isdir.return_value = True
132 fetch.GitBackend().checkout(
133 'repo', 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa', 'dir',
134 allow_fetch=True)
135 isdir.assert_has_calls([
136 mock.call('dir'),
137 mock.call('dir/.git'),
138 ])
139 git.assert_has_calls([
140 mock_git_dir(
141 'rev-parse', '-q', '--verify',
142 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa^{commit}'),
143 mock_git_dir('init'),
144 mock_git_dir('fetch', 'repo', 'refs/heads/master'),
145 mock_git_dir('checkout', '-q', '-f',
146 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'),
147 ])
148
149 @mock.patch('os.path.isdir')
150 @mock.patch('recipe_engine.fetch.GitBackend.Git._execute')
151 def test_existing_checkout_commit_not_present_no_fetch(self, git, isdir):
152 isdir.return_value = True
153 git.side_effect = [
154 subprocess42.CalledProcessError(1, ['git-rev-parse']),
155 ]
156 isdir.return_value = True
157 with self.assertRaises(fetch.FetchNotAllowedError):
158 fetch.GitBackend().checkout(
159 'repo', 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa', 'dir',
160 allow_fetch=False)
161 isdir.assert_has_calls([
162 mock.call('dir'),
163 mock.call('dir/.git'),
164 ])
165 git.assert_has_calls([
166 mock_git_dir(
167 'rev-parse', '-q', '--verify',
168 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa^{commit}'),
169 ])
170
171
172 @mock.patch('os.path.isdir')
173 @mock.patch('recipe_engine.fetch.GitBackend.Git._execute')
174 def test_existing_checkout_branch(self, git, isdir):
175 isdir.return_value = True
176 fetch.GitBackend().checkout('repo', 'refs/heads/master', 'dir',
177 allow_fetch=True)
178 isdir.assert_has_calls([
179 mock.call('dir'),
180 mock.call('dir/.git'),
181 ])
182 git.assert_has_calls([
183 mock_git_dir('init'),
184 mock_git_dir('fetch', 'repo', 'refs/heads/master'),
185 mock_git_dir('checkout', '-q', '-f', 'FETCH_HEAD'),
86 ]) 186 ])
87 187
88 @mock.patch('recipe_engine.fetch.GitBackend.Git._execute') 188 @mock.patch('recipe_engine.fetch.GitBackend.Git._execute')
89 def test_clone_not_allowed(self, git): 189 def test_fetch_not_allowed(self, git):
90 with self.assertRaises(fetch.FetchNotAllowedError): 190 with self.assertRaises(fetch.FetchNotAllowedError):
91 fetch.GitBackend().checkout( 191 fetch.GitBackend().checkout(
92 'repo', 'revision', 'dir', allow_fetch=False) 192 'repo', 'refs/heads/master', 'dir', allow_fetch=False)
93 193
94 @mock.patch('os.path.isdir') 194 @mock.patch('os.path.isdir')
95 @mock.patch('recipe_engine.fetch.GitBackend.Git._execute') 195 @mock.patch('recipe_engine.fetch.GitBackend.Git._execute')
96 def test_unclean_filesystem(self, git, isdir): 196 def test_unclean_filesystem(self, git, isdir):
97 isdir.side_effect = [True, False] 197 isdir.side_effect = [True, False]
98 with self.assertRaises(fetch.UncleanFilesystemError): 198 with self.assertRaises(fetch.UncleanFilesystemError):
99 fetch.GitBackend().checkout( 199 fetch.GitBackend().checkout(
100 'repo', 'revision', 'dir', allow_fetch=False) 200 'repo', 'refs/heads/master', 'dir', allow_fetch=False)
101 isdir.assert_has_calls([ 201 isdir.assert_has_calls([
102 mock.call('dir'), 202 mock.call('dir'),
103 mock.call('dir/.git'), 203 mock.call('dir/.git'),
104 ]) 204 ])
105 205
106 @mock.patch('os.path.isdir')
107 @mock.patch('recipe_engine.fetch.GitBackend.Git._execute')
108 def test_origin_mismatch(self, git, isdir):
109 git.return_value = 'not-repo'
110 isdir.return_value = True
111
112 # This should not raise UncleanFilesystemError, but instead
113 # set the right origin automatically.
114 fetch.GitBackend().checkout(
115 'repo', 'revision', 'dir', allow_fetch=False)
116
117 isdir.assert_has_calls([
118 mock.call('dir'),
119 mock.call('dir/.git'),
120 ])
121 git.assert_has_calls([
122 mock.call('GIT', '-C', 'dir', 'config', 'remote.origin.url', 'repo'),
123 ])
124
125 @mock.patch('os.path.isdir')
126 @mock.patch('recipe_engine.fetch.GitBackend.Git._execute')
127 def test_rev_parse_fail(self, git, isdir):
128 git.side_effect = [
129 None,
130 subprocess42.CalledProcessError(1, ['fakecmd']),
131 None,
132 None,
133 ]
134 isdir.return_value = True
135 fetch.GitBackend().checkout(
136 'repo', 'revision', 'dir', allow_fetch=True)
137 isdir.assert_has_calls([
138 mock.call('dir'),
139 mock.call('dir/.git'),
140 ])
141 git.assert_has_calls([
142 mock.call('GIT', '-C', 'dir', 'config', 'remote.origin.url', 'repo'),
143 mock.call('GIT', '-C', 'dir', 'rev-parse', '-q', '--verify',
144 'revision^{commit}'),
145 mock.call('GIT', '-C', 'dir', 'fetch'),
146 mock.call('GIT', '-C', 'dir', 'reset', '-q', '--hard', 'revision'),
147 ])
148
149 @mock.patch('os.path.isdir')
150 @mock.patch('recipe_engine.fetch.GitBackend.Git._execute')
151 def test_rev_parse_fetch_not_allowed(self, git, isdir):
152 git.side_effect = [
153 None,
154 subprocess42.CalledProcessError(1, ['fakecmd']),
155 ]
156 isdir.return_value = True
157 with self.assertRaises(fetch.FetchNotAllowedError):
158 fetch.GitBackend().checkout(
159 'repo', 'revision', 'dir', allow_fetch=False)
160 isdir.assert_has_calls([
161 mock.call('dir'),
162 mock.call('dir/.git'),
163 ])
164 git.assert_has_calls([
165 mock.call('GIT', '-C', 'dir', 'config', 'remote.origin.url', 'repo'),
166 mock.call('GIT', '-C', 'dir', 'rev-parse', '-q', '--verify',
167 'revision^{commit}'),
168 ])
169
170 @mock.patch('recipe_engine.fetch.GitBackend.Git._execute') 206 @mock.patch('recipe_engine.fetch.GitBackend.Git._execute')
171 def test_commit_metadata(self, git): 207 def test_commit_metadata(self, git):
172 git.side_effect = ['author', 'message'] 208 git.side_effect = ['author', 'message']
173 result = fetch.GitBackend().commit_metadata( 209 result = fetch.GitBackend().commit_metadata(
174 'repo', 'revision', 'dir', allow_fetch=True) 210 'repo', 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa', 'dir',
211 allow_fetch=True)
175 self.assertEqual(result, { 212 self.assertEqual(result, {
176 'author': 'author', 213 'author': 'author',
177 'message': 'message', 214 'message': 'message',
178 }) 215 })
179 git.assert_has_calls([ 216 git.assert_has_calls([
180 mock.call('GIT', '-C', 'dir', 'show', '-s', '--pretty=%aE', 'revision'), 217 mock_git_dir('show', '-s', '--pretty=%aE',
181 mock.call('GIT', '-C', 'dir', 'show', '-s', '--pretty=%B', 'revision'), 218 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'),
219 mock_git_dir('show', '-s', '--pretty=%B',
220 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'),
182 ]) 221 ])
183 222
184 223
185 class TestGitiles(unittest.TestCase): 224 class TestGitiles(unittest.TestCase):
186 def setUp(self): 225 def setUp(self):
187 requests_ssl.disable_check() 226 requests_ssl.disable_check()
188 227
189 @mock.patch('__builtin__.open', mock.mock_open()) 228 @mock.patch('__builtin__.open', mock.mock_open())
190 @mock.patch('shutil.rmtree') 229 @mock.patch('shutil.rmtree')
191 @mock.patch('os.makedirs') 230 @mock.patch('os.makedirs')
(...skipping 148 matching lines...) Expand 10 before | Expand all | Expand 10 after
340 'author': 'author', 379 'author': 'author',
341 'message': 'message', 380 'message': 'message',
342 }) 381 })
343 self.assertEqual(counts['sleeps'], 4) 382 self.assertEqual(counts['sleeps'], 4)
344 requests_get.assert_has_calls([ 383 requests_get.assert_has_calls([
345 mock.call('repo/+/revision?format=JSON'), 384 mock.call('repo/+/revision?format=JSON'),
346 ] * 5) 385 ] * 5)
347 386
348 if __name__ == '__main__': 387 if __name__ == '__main__':
349 unittest.main() 388 unittest.main()
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698