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

Side by Side Diff: third_party/recipe_engine/unittests/multi_repo_test.py

Issue 1241323004: Cross-repo recipe package system. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/tools/build
Patch Set: Moved show_me_the_modules into recipe_engine Created 5 years, 4 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 | Annotate | Revision Log
OLDNEW
(Empty)
1 #!/usr/bin/env python
2
3 import contextlib
4 import copy
5 import datetime
6 import json
7 import os
8 import re
9 import shutil
10 import subprocess
11 import sys
12 import tempfile
13 import time
14 import unittest
15
16 RECIPE_ENGINE = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
17 THIRD_PARTY = os.path.dirname(RECIPE_ENGINE)
18 sys.path.insert(0, THIRD_PARTY)
19
20 from google import protobuf
21 from recipe_engine import package
22 from recipe_engine import package_pb2
23
24 @contextlib.contextmanager
25 def _in_directory(target_dir):
iannucci 2015/08/06 23:57:13 why two copies? :D
26 old_dir = os.getcwd()
27 os.chdir(target_dir)
28 try:
29 yield
30 finally:
31 os.chdir(old_dir)
32
33
34 def _updated_deps(inp, updates):
35 if inp is None:
36 return updates
37
38 outp = inp.__class__()
39 outp.CopyFrom(inp)
40 for dep in outp.deps:
41 if dep.project_id in updates:
42 dep.revision = updates[dep.project_id]
43 return outp
44
45
46 def _get_dep(inp, dep_id):
47 for dep in inp.deps:
48 if dep.project_id == dep_id:
49 return dep
50 else:
51 raise Exception('Dependency %s not found in %s' % (dep, inp))
52
53
54 def _to_text(buf):
55 return protobuf.text_format.MessageToString(buf)
56
57
58 def _recstrify(thing):
iannucci 2015/08/06 23:57:13 don't use?
luqui 2015/08/20 22:45:25 Done.
59 if isinstance(thing, basestring):
60 return str(thing)
61 elif isinstance(thing, dict):
62 out = {}
63 for k,v in thing.iteritems():
64 out[str(k)] = _recstrify(v)
65 return out
66 elif isinstance(thing, list):
67 return map(_recstrify, thing)
68 else:
69 return thing
70
71
72 class MultiRepoTest(unittest.TestCase):
73 def _run_cmd(self, cmd, env=None):
74 subprocess.call(cmd, env=env)
75
76 def _create_repo(self, name, spec):
77 repo_dir = os.path.join(self._root_dir, name)
78 os.mkdir(repo_dir)
79 with _in_directory(repo_dir):
80 self._run_cmd(['git', 'init'])
81 config_file = os.path.join('infra', 'config', 'recipes.cfg')
82 os.makedirs(os.path.dirname(config_file))
83 package.ProtoFile(config_file).write(spec)
84 self._run_cmd(['git', 'add', config_file])
85 self._run_cmd(['git', 'commit', '-m', 'New recipe package'])
86 rev = subprocess.check_output(['git', 'rev-parse', 'HEAD']).strip()
87 return {
88 'root': repo_dir,
89 'revision': rev,
90 'spec': spec,
91 }
92
93 def _commit_in_repo(self, repo, message='Empty commit'):
94 with _in_directory(repo['root']):
95 env = dict(os.environ)
96 self._run_cmd(['git', 'commit', '-a', '--allow-empty', '-m', message],
97 env=env)
98 rev = subprocess.check_output(['git', 'rev-parse', 'HEAD']).strip()
99 return {
100 'root': repo['root'],
101 'revision': rev,
102 'spec': repo['spec'],
103 }
104
105 def setUp(self):
106 self.maxDiff = None
107
108 self._root_dir = tempfile.mkdtemp()
109 self._recipe_tool = os.path.join(
110 os.path.dirname(os.path.dirname(os.path.abspath(__file__))),
111 'recipes.py')
112
113 def tearDown(self):
114 shutil.rmtree(self._root_dir)
115
116 def _repo_setup(self, repo_deps):
117 # In order to avoid a topsort, we require that repo names are in
118 # alphebetical dependency order -- i.e. later names depend on earlier
119 # ones.
120 repos = {}
121 for k in sorted(repo_deps):
122 repos[k] = self._create_repo(k, package_pb2.Package(
123 api_version=1,
124 project_id=k,
125 recipes_path='',
126 deps=[
127 package_pb2.DepSpec(
128 project_id=d,
129 url=repos[d]['root'],
130 branch='master',
131 revision=repos[d]['revision'],
132 )
133 for d in repo_deps[k]
134 ],
135 ))
136 return repos
137
138 def _run_roll(self, repo, expect_updates, commit=False):
139 with _in_directory(repo['root']):
140 stdout, _ = (
141 subprocess.Popen(['python', self._recipe_tool, 'roll'],
142 stdout=subprocess.PIPE)
143 .communicate())
144 if expect_updates:
145 self.assertRegexpMatches(stdout, r'Wrote \S*recipes.cfg')
146 else:
147 self.assertRegexpMatches(stdout, r'No consistent rolls found')
148
149 if commit:
150 assert expect_updates, 'Cannot commit when not expecting updates'
151 git_match = re.search(r'^git commit .*', stdout, re.MULTILINE)
152 self.assertTrue(git_match)
153 git_command = git_match.group(0)
154 subprocess.call(git_command, shell=True)
155 rev = subprocess.check_output(['git', 'rev-parse', 'HEAD']).strip()
156 return {
157 'root': repo['root'],
158 'revision': rev,
159 'spec': repo['spec'],
160 }
161
162
163 def _get_spec(self, repo):
164 proto_file = package.ProtoFile(
165 os.path.join(repo['root'], 'infra', 'config', 'recipes.cfg'))
166 return proto_file.read()
167
168 def test_empty_roll(self):
169 repos = self._repo_setup({
170 'a': [],
171 'b': [ 'a' ],
172 })
173 self._run_roll(repos['b'], expect_updates=False)
174
175 def test_simple_roll(self):
176 repos = self._repo_setup({
177 'a': [],
178 'b': ['a'],
179 })
180 new_a = self._commit_in_repo(repos['a'])
181 self._run_roll(repos['b'], expect_updates=True)
182 self.assertEqual(
183 _to_text(self._get_spec(repos['b'])),
184 _to_text(_updated_deps(repos['b']['spec'], {
185 'a': new_a['revision'],
186 })))
187 self._run_roll(repos['b'], expect_updates=False)
188
189 def test_indepdendent_roll(self):
190 repos = self._repo_setup({
191 'b': [],
192 'c': [],
193 'd': ['b', 'c'],
194 })
195 new_b = self._commit_in_repo(repos['b'])
196 new_c = self._commit_in_repo(repos['c'])
197 self._run_roll(repos['d'], expect_updates=True)
iannucci 2015/08/06 23:57:13 when we allow multi-roll ops, this should probably
198 # There is no guarantee on the order the two updates come in.
199 # (Usually we sort by date but these commits are within 1 second)
200 # However after one roll we expect only one of the two updates to
201 # have come in.
202 d_spec = self._get_spec(repos['d'])
203 self.assertTrue(
204 (_get_dep(d_spec, 'b').revision == new_b['revision'])
205 != (_get_dep(d_spec, 'c').revision == new_c['revision']))
206 self._run_roll(repos['d'], expect_updates=True)
207 self.assertEqual(
208 _to_text(self._get_spec(repos['d'])),
209 _to_text(_updated_deps(repos['d']['spec'], {
210 'b': new_b['revision'],
211 'c': new_c['revision'],
212 })))
213 self._run_roll(repos['d'], expect_updates=False)
214
215 def test_dependent_roll(self):
iannucci 2015/08/06 23:57:13 add example test for adding/deleting a dependency
216 repos = self._repo_setup({
217 'a': [],
218 'b': ['a'],
219 'c': ['a'],
220 'd': ['b', 'c'],
221 })
222 new_a = self._commit_in_repo(repos['a'])
223 new_b = self._run_roll(repos['b'], expect_updates=True, commit=True)
224 new_c = self._run_roll(repos['c'], expect_updates=True, commit=True)
225
226 # We only expect one roll here because to roll b without c would
227 # result in an inconsistent revision of a, so we should skip it.
228 self._run_roll(repos['d'], expect_updates=True)
229 d_spec = self._get_spec(repos['d'])
230 self.assertEqual(
231 _to_text(self._get_spec(repos['d'])),
232 _to_text(_updated_deps(repos['d']['spec'], {
233 'b': new_b['revision'],
234 'c': new_c['revision'],
235 })))
236 self._run_roll(repos['d'], expect_updates=False)
237
238 if __name__ == '__main__':
239 unittest.main()
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698