OLD | NEW |
1 # Copyright 2014 The Chromium Authors. All rights reserved. | 1 # Copyright 2014 The Chromium Authors. All rights reserved. |
2 # Use of this source code is governed by a BSD-style license that can be | 2 # Use of this source code is governed by a BSD-style license that can be |
3 # found in the LICENSE file. | 3 # found in the LICENSE file. |
4 | 4 |
5 import collections | 5 import collections |
| 6 import glob |
6 import json | 7 import json |
7 import logging | 8 import logging |
8 import os | 9 import os |
| 10 import shutil |
9 import sys | 11 import sys |
10 import tempfile | 12 import tempfile |
11 | 13 |
12 from cStringIO import StringIO | 14 from cStringIO import StringIO |
13 | 15 |
14 from infra.services.gnumbd import inner_loop as gnumbd | 16 from infra.services.gnumbd import inner_loop |
15 | 17 |
16 from infra.services.gnumbd.support import config_ref, data, git | 18 from infra.libs import git2 |
| 19 from infra.libs.git2 import config_ref |
| 20 from infra.libs.git2 import data |
17 | 21 |
18 from infra.services.gnumbd.test import gnumbd_smoketests | 22 from infra.services.gnumbd.test import inner_loop_test_definitions |
19 | 23 |
20 from infra.ext import testing_support # pylint: disable=W0611 | 24 from infra.ext import testing_support # pylint: disable=W0611 |
21 from testing_support import expect_tests # pylint: disable=F0401 | 25 from testing_support import expect_tests # pylint: disable=F0401 |
22 | 26 |
23 BASE_PATH = os.path.dirname(os.path.abspath(__file__)) | 27 BASE_PATH = os.path.dirname(os.path.abspath(__file__)) |
24 | 28 |
25 | 29 |
26 # TODO(iannucci): Make these first class data library objects | 30 # TODO(iannucci): Make these first class data library objects |
27 class GitEntry(object): | 31 class GitEntry(object): |
28 typ = None | 32 typ = None |
(...skipping 29 matching lines...) Expand all Loading... |
58 self.entries = entries | 62 self.entries = entries |
59 | 63 |
60 def intern(self, repo): | 64 def intern(self, repo): |
61 with tempfile.TemporaryFile() as tf: | 65 with tempfile.TemporaryFile() as tf: |
62 for path, entry in self.entries.iteritems(): | 66 for path, entry in self.entries.iteritems(): |
63 tf.write('%s %s %s\t%s' % | 67 tf.write('%s %s %s\t%s' % |
64 (entry.mode, entry.typ, entry.intern(repo), path)) | 68 (entry.mode, entry.typ, entry.intern(repo), path)) |
65 tf.seek(0) | 69 tf.seek(0) |
66 return repo.run('mktree', '-z', stdin=tf).strip() | 70 return repo.run('mktree', '-z', stdin=tf).strip() |
67 | 71 |
68 class TestRef(git.Ref): | 72 class TestRef(git2.Ref): |
69 def synthesize_commit(self, message, number=None, tree=None, svn=False, | 73 def synthesize_commit(self, message, number=None, tree=None, svn=False, |
70 footers=None): | 74 footers=None): |
71 footers = footers or collections.OrderedDict() | 75 footers = footers or collections.OrderedDict() |
72 if number is not None: | 76 if number is not None: |
73 if svn: | 77 if svn: |
74 footers[gnumbd.GIT_SVN_ID] = [ | 78 footers[inner_loop.GIT_SVN_ID] = [ |
75 'svn://repo/path@%s 0039d316-1c4b-4281-b951-d872f2087c98' % number] | 79 'svn://repo/path@%s 0039d316-1c4b-4281-b951-d872f2087c98' % number] |
76 else: | 80 else: |
77 footers[gnumbd.COMMIT_POSITION] = [ | 81 footers[inner_loop.COMMIT_POSITION] = [ |
78 gnumbd.FMT_COMMIT_POSITION(self, number)] | 82 inner_loop.FMT_COMMIT_POSITION(self, number)] |
79 | 83 |
80 commit = self.repo.synthesize_commit(self.commit, message, footers=footers, | 84 commit = self.repo.synthesize_commit(self.commit, message, footers=footers, |
81 tree=tree) | 85 tree=tree) |
82 self.update_to(commit) | 86 self.update_to(commit) |
83 return commit | 87 return commit |
84 | 88 |
85 | 89 |
86 class TestClock(object): | 90 class TestClock(object): |
87 def __init__(self): | 91 def __init__(self): |
88 self._time = 1402589336 | 92 self._time = 1402589336 |
89 | 93 |
90 def time(self): | 94 def time(self): |
91 self._time += 10 | 95 self._time += 10 |
92 return self._time | 96 return self._time |
93 | 97 |
94 | 98 |
95 class TestConfigRef(config_ref.ConfigRef): | 99 class TestConfigRef(config_ref.ConfigRef): |
96 def update(self, **values): | 100 def update(self, **values): |
97 new_config = self.current | 101 new_config = self.current |
98 new_config.update(values) | 102 new_config.update(values) |
99 self.ref.synthesize_commit( | 103 self.ref.synthesize_commit( |
100 'update(%r)' % values.keys(), | 104 'update(%r)' % values.keys(), |
101 tree=GitTree({'config.json': GitFile(json.dumps(new_config))})) | 105 tree=GitTree({'config.json': GitFile(json.dumps(new_config))})) |
102 | 106 |
103 | 107 |
104 class TestRepo(git.Repo): | 108 class TestRepo(git2.Repo): |
105 def __init__(self, short_name, tmpdir, clock, mirror_of=None): | 109 def __init__(self, short_name, tmpdir, clock, mirror_of=None): |
106 super(TestRepo, self).__init__(mirror_of or 'local test repo') | 110 super(TestRepo, self).__init__(mirror_of or 'local test repo') |
107 self._short_name = short_name | 111 self._short_name = short_name |
108 self.repos_dir = tmpdir | 112 self.repos_dir = tmpdir |
109 | 113 |
110 if mirror_of is None: | 114 if mirror_of is None: |
111 self._repo_path = tempfile.mkdtemp(dir=self.repos_dir, suffix='.git') | 115 self._repo_path = tempfile.mkdtemp(dir=self.repos_dir, suffix='.git') |
112 self.run('init', '--bare') | 116 self.run('init', '--bare') |
113 | 117 |
114 self._clock = clock | 118 self._clock = clock |
115 | 119 |
116 # pylint: disable=W0212 | 120 # pylint: disable=W0212 |
117 repo_path = property(lambda self: self._repo_path) | 121 repo_path = property(lambda self: self._repo_path) |
118 | 122 |
119 def __getitem__(self, refstr): | 123 def __getitem__(self, refstr): |
120 return TestRef(self, refstr) | 124 return TestRef(self, refstr) |
121 | 125 |
122 def synthesize_commit(self, parent, message, tree=None, footers=None): | 126 def synthesize_commit(self, parent, message, tree=None, footers=None): |
123 tree = tree or GitTree({'file': GitFile('contents')}) | 127 tree = tree or GitTree({'file': GitFile('contents')}) |
124 tree = tree.intern(self) if isinstance(tree, GitTree) else tree | 128 tree = tree.intern(self) if isinstance(tree, GitTree) else tree |
125 assert isinstance(tree, str) | 129 assert isinstance(tree, str) |
126 | 130 |
127 parents = [parent.hsh] if parent is not git.INVALID else [] | 131 parents = [parent.hsh] if parent is not git2.INVALID else [] |
128 | 132 |
129 timestamp = data.CommitTimestamp(self._clock.time(), '+', 8, 0) | 133 timestamp = data.CommitTimestamp(self._clock.time(), '+', 8, 0) |
130 user = data.CommitUser('Test User', 'test_user@example.com', timestamp) | 134 user = data.CommitUser('Test User', 'test_user@example.com', timestamp) |
131 | 135 |
132 return self.get_commit(self.intern(data.CommitData( | 136 return self.get_commit(self.intern(data.CommitData( |
133 tree, parents, user, user, (), message.splitlines(), | 137 tree, parents, user, user, (), message.splitlines(), |
134 data.CommitData.merge_lines([], footers or {}) | 138 data.CommitData.merge_lines([], footers or {}) |
135 ), 'commit')) | 139 ), 'commit')) |
136 | 140 |
137 def snap(self, include_committer=False, include_config=False): | 141 def snap(self, include_committer=False, include_config=False): |
138 ret = {} | 142 ret = {} |
139 if include_committer: | 143 if include_committer: |
140 fmt = '%H%x00committer %cn <%ce> %ci%n%n%B%x00%x00' | 144 fmt = '%H%x00committer %cn <%ce> %ci%n%n%B%x00%x00' |
141 else: | 145 else: |
142 fmt = '%H%x00%B%x00%x00' | 146 fmt = '%H%x00%B%x00%x00' |
143 for ref in (r.ref for r in self.refglob('*')): | 147 for ref in (r.ref for r in self.refglob('*')): |
144 if ref == gnumbd.DEFAULT_CONFIG_REF and not include_config: | 148 if ref == inner_loop.DEFAULT_CONFIG_REF and not include_config: |
145 continue | 149 continue |
146 log = self.run('log', ref, '--format=%s' % fmt) | 150 log = self.run('log', ref, '--format=%s' % fmt) |
147 ret[ref] = collections.OrderedDict( | 151 ret[ref] = collections.OrderedDict( |
148 (commit, message.splitlines()) | 152 (commit, message.splitlines()) |
149 for commit, message in ( | 153 for commit, message in ( |
150 r.split('\0') for r in log.split('\0\0\n') if r) | 154 r.split('\0') for r in log.split('\0\0\n') if r) |
151 ) | 155 ) |
152 return ret | 156 return ret |
153 | 157 |
154 def __repr__(self): | 158 def __repr__(self): |
155 return 'TestRepo(%r)' % self._short_name | 159 return 'TestRepo(%r)' % self._short_name |
156 | 160 |
157 | 161 |
158 def RunTest(tmpdir, test_name): | 162 def RunTest(tmpdir, test_name): |
159 ret = [] | 163 ret = [] |
160 clock = TestClock() | 164 clock = TestClock() |
161 origin = TestRepo('origin', tmpdir, clock) | 165 origin = TestRepo('origin', tmpdir, clock) |
162 local = TestRepo('local', tmpdir, clock, origin.repo_path) | 166 local = TestRepo('local', tmpdir, clock, origin.repo_path) |
163 | 167 |
164 cref = TestConfigRef(origin[gnumbd.DEFAULT_CONFIG_REF]) | 168 cref = TestConfigRef(origin[inner_loop.DEFAULT_CONFIG_REF]) |
165 cref.update(enabled_refglobs=['refs/heads/*'], interval=0) | 169 cref.update(enabled_refglobs=['refs/heads/*'], interval=0) |
166 | 170 |
167 def checkpoint(message, include_committer=False, include_config=False): | 171 def checkpoint(message, include_committer=False, include_config=False): |
168 ret.append([message, {'origin': origin.snap(include_committer, | 172 ret.append([message, {'origin': origin.snap(include_committer, |
169 include_config)}]) | 173 include_config)}]) |
170 | 174 |
171 def run(include_log=True): | 175 def run(include_log=True): |
172 stdout = sys.stdout | 176 stdout = sys.stdout |
173 stderr = sys.stderr | 177 stderr = sys.stderr |
174 | 178 |
175 if include_log: | 179 if include_log: |
176 logout = StringIO() | 180 logout = StringIO() |
177 root_logger = logging.getLogger() | 181 root_logger = logging.getLogger() |
178 log_level = root_logger.getEffectiveLevel() | 182 log_level = root_logger.getEffectiveLevel() |
179 shandler = logging.StreamHandler(logout) | 183 shandler = logging.StreamHandler(logout) |
180 shandler.setFormatter( | 184 shandler.setFormatter( |
181 logging.Formatter('%(levelname)s: %(message)s')) | 185 logging.Formatter('%(levelname)s: %(message)s')) |
182 root_logger.addHandler(shandler) | 186 root_logger.addHandler(shandler) |
183 root_logger.setLevel(logging.INFO) | 187 root_logger.setLevel(logging.INFO) |
184 | 188 |
185 try: | 189 try: |
186 sys.stderr = sys.stdout = open(os.devnull, 'w') | 190 sys.stderr = sys.stdout = open(os.devnull, 'w') |
187 local.reify() | 191 local.reify() |
188 gnumbd.inner_loop(local, cref, clock) | 192 inner_loop.inner_loop(local, cref, clock) |
189 except Exception: # pragma: no cover | 193 except Exception: # pragma: no cover |
190 import traceback | 194 import traceback |
191 ret.append(traceback.format_exc().splitlines()) | 195 ret.append(traceback.format_exc().splitlines()) |
192 finally: | 196 finally: |
193 sys.stdout = stdout | 197 sys.stdout = stdout |
194 sys.stderr = stderr | 198 sys.stderr = stderr |
195 | 199 |
196 if include_log: | 200 if include_log: |
197 root_logger.removeHandler(shandler) | 201 root_logger.removeHandler(shandler) |
198 root_logger.setLevel(log_level) | 202 root_logger.setLevel(log_level) |
199 ret.append({'log output': logout.getvalue().splitlines()}) | 203 ret.append({'log output': logout.getvalue().splitlines()}) |
200 | 204 |
201 gnumbd_smoketests.GNUMBD_TESTS[test_name]( | 205 inner_loop_test_definitions.GNUMBD_TESTS[test_name]( |
202 origin, local, cref, run, checkpoint) | 206 origin, local, cref, run, checkpoint) |
203 | 207 |
204 return expect_tests.Result(ret) | 208 return expect_tests.Result(ret) |
205 | 209 |
206 | 210 |
207 def GenTests(tmpdir): | 211 @expect_tests.generator |
208 for test_name, test in gnumbd_smoketests.GNUMBD_TESTS.iteritems(): | 212 def GenTests(): |
| 213 suffix = '.gnumbd_inner_loop' |
| 214 tmpdir = tempfile.mkdtemp(suffix) |
| 215 for p in glob.glob(os.path.join(os.path.dirname(tmpdir), '*'+suffix)): |
| 216 if p != tmpdir: # pragma: no cover |
| 217 shutil.rmtree(p) |
| 218 |
| 219 yield expect_tests.Cleanup(expect_tests.FuncCall(shutil.rmtree, tmpdir)) |
| 220 |
| 221 for test_name, test in inner_loop_test_definitions.GNUMBD_TESTS.iteritems(): |
209 yield expect_tests.Test( | 222 yield expect_tests.Test( |
210 __package__ + '.' + test_name, | 223 __package__ + '.' + test_name, |
211 expect_tests.FuncCall(RunTest, tmpdir, test_name), | 224 expect_tests.FuncCall(RunTest, tmpdir, test_name), |
212 os.path.join(BASE_PATH, 'gnumbd_smoketests.expected'), | 225 expect_base=test_name, ext='yaml', break_funcs=[test], |
213 test_name, 'yaml', break_funcs=[test]) | 226 covers=( |
| 227 expect_tests.Test.covers_obj(RunTest) + |
| 228 expect_tests.Test.covers_obj(inner_loop_test_definitions) |
| 229 )) |
OLD | NEW |