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

Side by Side Diff: tests/gclient_scm_test.py

Issue 391075: Revert 32057, 32058, 32059, 32062 because they still have unwanted side-effects. (Closed)
Patch Set: Created 11 years, 1 month 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
« no previous file with comments | « tests/gcl_unittest.py ('k') | tests/gclient_test.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 #!/usr/bin/python 1 #!/usr/bin/python
2 # 2 #
3 # Copyright 2008-2009 Google Inc. All Rights Reserved. 3 # Copyright 2008-2009 Google Inc. All Rights Reserved.
4 # 4 #
5 # Licensed under the Apache License, Version 2.0 (the "License"); 5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License. 6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at 7 # You may obtain a copy of the License at
8 # 8 #
9 # http://www.apache.org/licenses/LICENSE-2.0 9 # http://www.apache.org/licenses/LICENSE-2.0
10 # 10 #
(...skipping 15 matching lines...) Expand all
26 from super_mox import mox, SuperMoxBaseTestBase 26 from super_mox import mox, SuperMoxBaseTestBase
27 27
28 28
29 class BaseTestCase(GCBaseTestCase): 29 class BaseTestCase(GCBaseTestCase):
30 def setUp(self): 30 def setUp(self):
31 GCBaseTestCase.setUp(self) 31 GCBaseTestCase.setUp(self)
32 self.mox.StubOutWithMock(gclient_scm.gclient_utils, 'FileRead') 32 self.mox.StubOutWithMock(gclient_scm.gclient_utils, 'FileRead')
33 self.mox.StubOutWithMock(gclient_scm.gclient_utils, 'FileWrite') 33 self.mox.StubOutWithMock(gclient_scm.gclient_utils, 'FileWrite')
34 self.mox.StubOutWithMock(gclient_scm.gclient_utils, 'SubprocessCall') 34 self.mox.StubOutWithMock(gclient_scm.gclient_utils, 'SubprocessCall')
35 self.mox.StubOutWithMock(gclient_scm.gclient_utils, 'RemoveDirectory') 35 self.mox.StubOutWithMock(gclient_scm.gclient_utils, 'RemoveDirectory')
36 self._CaptureSVNInfo = gclient_scm.scm.SVN.CaptureInfo 36 self._CaptureSVNInfo = gclient_scm.CaptureSVNInfo
37 self.mox.StubOutWithMock(gclient_scm.scm.SVN, 'Capture') 37 self.mox.StubOutWithMock(gclient_scm, 'CaptureSVN')
38 self.mox.StubOutWithMock(gclient_scm.scm.SVN, 'CaptureInfo') 38 self.mox.StubOutWithMock(gclient_scm, 'CaptureSVNInfo')
39 self.mox.StubOutWithMock(gclient_scm.scm.SVN, 'CaptureStatus') 39 self.mox.StubOutWithMock(gclient_scm, 'CaptureSVNStatus')
40 self.mox.StubOutWithMock(gclient_scm.scm.SVN, 'Run') 40 self.mox.StubOutWithMock(gclient_scm, 'RunSVN')
41 self.mox.StubOutWithMock(gclient_scm.scm.SVN, 'RunAndGetFileList') 41 self.mox.StubOutWithMock(gclient_scm, 'RunSVNAndGetFileList')
42 self._scm_wrapper = gclient_scm.CreateSCM 42 self._scm_wrapper = gclient_scm.CreateSCM
43 43
44 44
45 class SVNWrapperTestCase(BaseTestCase): 45 class SVNWrapperTestCase(BaseTestCase):
46 class OptionsObject(object): 46 class OptionsObject(object):
47 def __init__(self, test_case, verbose=False, revision=None): 47 def __init__(self, test_case, verbose=False, revision=None):
48 self.verbose = verbose 48 self.verbose = verbose
49 self.revision = revision 49 self.revision = revision
50 self.manually_grab_svn_rev = True 50 self.manually_grab_svn_rev = True
51 self.deps_os = None 51 self.deps_os = None
52 self.force = False 52 self.force = False
53 self.nohooks = False 53 self.nohooks = False
54 54
55 def Options(self, *args, **kwargs): 55 def Options(self, *args, **kwargs):
56 return self.OptionsObject(self, *args, **kwargs) 56 return self.OptionsObject(self, *args, **kwargs)
57 57
58 def setUp(self): 58 def setUp(self):
59 BaseTestCase.setUp(self) 59 BaseTestCase.setUp(self)
60 self.root_dir = self.Dir() 60 self.root_dir = self.Dir()
61 self.args = self.Args() 61 self.args = self.Args()
62 self.url = self.Url() 62 self.url = self.Url()
63 self.relpath = 'asf' 63 self.relpath = 'asf'
64 64
65 def testDir(self): 65 def testDir(self):
66 members = [ 66 members = [
67 'COMMAND', 'Capture', 'CaptureHeadRevision', 'CaptureInfo', 67 'FullUrlForRelativeUrl', 'RunCommand', 'cleanup', 'diff', 'export',
68 'CaptureStatus', 'DiffItem', 'FullUrlForRelativeUrl', 'GetFileProperty', 68 'pack', 'relpath', 'revert', 'revinfo', 'runhooks', 'scm_name', 'status',
69 'IsMoved', 'Run', 'RunAndFilterOutput', 'RunAndGetFileList', 69 'update', 'url',
70 'RunCommand', 'cleanup', 'diff', 'export', 'pack', 'relpath', 'revert',
71 'revinfo', 'runhooks', 'scm_name', 'status', 'update', 'url',
72 ] 70 ]
73 71
74 # If you add a member, be sure to add the relevant test! 72 # If you add a member, be sure to add the relevant test!
75 self.compareMembers(self._scm_wrapper(), members) 73 self.compareMembers(self._scm_wrapper(), members)
76 74
77 def testUnsupportedSCM(self): 75 def testUnsupportedSCM(self):
78 args = [self.url, self.root_dir, self.relpath] 76 args = [self.url, self.root_dir, self.relpath]
79 kwargs = {'scm_name' : 'foo'} 77 kwargs = {'scm_name' : 'foo'}
80 exception_msg = 'Unsupported scm %(scm_name)s' % kwargs 78 exception_msg = 'Unsupported scm %(scm_name)s' % kwargs
81 self.assertRaisesError(exception_msg, self._scm_wrapper, *args, **kwargs) 79 self.assertRaisesError(exception_msg, self._scm_wrapper, *args, **kwargs)
(...skipping 26 matching lines...) Expand all
108 options = self.Options(verbose=True) 106 options = self.Options(verbose=True)
109 base_path = gclient_scm.os.path.join(self.root_dir, self.relpath) 107 base_path = gclient_scm.os.path.join(self.root_dir, self.relpath)
110 gclient_scm.os.path.isdir(base_path).AndReturn(False) 108 gclient_scm.os.path.isdir(base_path).AndReturn(False)
111 # It'll to a checkout instead. 109 # It'll to a checkout instead.
112 gclient_scm.os.path.exists(gclient_scm.os.path.join(base_path, '.git') 110 gclient_scm.os.path.exists(gclient_scm.os.path.join(base_path, '.git')
113 ).AndReturn(False) 111 ).AndReturn(False)
114 print("\n_____ %s is missing, synching instead" % self.relpath) 112 print("\n_____ %s is missing, synching instead" % self.relpath)
115 # Checkout. 113 # Checkout.
116 gclient_scm.os.path.exists(base_path).AndReturn(False) 114 gclient_scm.os.path.exists(base_path).AndReturn(False)
117 files_list = self.mox.CreateMockAnything() 115 files_list = self.mox.CreateMockAnything()
118 gclient_scm.scm.SVN.RunAndGetFileList(options, 116 gclient_scm.RunSVNAndGetFileList(options, ['checkout', self.url, base_path],
119 ['checkout', self.url, base_path], 117 self.root_dir, files_list)
120 self.root_dir, files_list)
121 118
122 self.mox.ReplayAll() 119 self.mox.ReplayAll()
123 scm = self._scm_wrapper(url=self.url, root_dir=self.root_dir, 120 scm = self._scm_wrapper(url=self.url, root_dir=self.root_dir,
124 relpath=self.relpath) 121 relpath=self.relpath)
125 scm.revert(options, self.args, files_list) 122 scm.revert(options, self.args, files_list)
126 123
127 def testRevertNone(self): 124 def testRevertNone(self):
128 options = self.Options(verbose=True) 125 options = self.Options(verbose=True)
129 base_path = gclient_scm.os.path.join(self.root_dir, self.relpath) 126 base_path = gclient_scm.os.path.join(self.root_dir, self.relpath)
130 gclient_scm.os.path.isdir(base_path).AndReturn(True) 127 gclient_scm.os.path.isdir(base_path).AndReturn(True)
131 gclient_scm.scm.SVN.CaptureStatus(base_path).AndReturn([]) 128 gclient_scm.CaptureSVNStatus(base_path).AndReturn([])
132 gclient_scm.scm.SVN.RunAndGetFileList(options, 129 gclient_scm.RunSVNAndGetFileList(options, ['update', '--revision', 'BASE'],
133 ['update', '--revision', 'BASE'], 130 base_path, mox.IgnoreArg())
134 base_path, mox.IgnoreArg())
135 131
136 self.mox.ReplayAll() 132 self.mox.ReplayAll()
137 scm = self._scm_wrapper(url=self.url, root_dir=self.root_dir, 133 scm = self._scm_wrapper(url=self.url, root_dir=self.root_dir,
138 relpath=self.relpath) 134 relpath=self.relpath)
139 file_list = [] 135 file_list = []
140 scm.revert(options, self.args, file_list) 136 scm.revert(options, self.args, file_list)
141 137
142 def testRevert2Files(self): 138 def testRevert2Files(self):
143 options = self.Options(verbose=True) 139 options = self.Options(verbose=True)
144 base_path = gclient_scm.os.path.join(self.root_dir, self.relpath) 140 base_path = gclient_scm.os.path.join(self.root_dir, self.relpath)
145 gclient_scm.os.path.isdir(base_path).AndReturn(True) 141 gclient_scm.os.path.isdir(base_path).AndReturn(True)
146 items = [ 142 items = [
147 ('M ', 'a'), 143 ('M ', 'a'),
148 ('A ', 'b'), 144 ('A ', 'b'),
149 ] 145 ]
150 file_path1 = gclient_scm.os.path.join(base_path, 'a') 146 file_path1 = gclient_scm.os.path.join(base_path, 'a')
151 file_path2 = gclient_scm.os.path.join(base_path, 'b') 147 file_path2 = gclient_scm.os.path.join(base_path, 'b')
152 gclient_scm.scm.SVN.CaptureStatus(base_path).AndReturn(items) 148 gclient_scm.CaptureSVNStatus(base_path).AndReturn(items)
153 gclient_scm.os.path.exists(file_path1).AndReturn(True) 149 gclient_scm.os.path.exists(file_path1).AndReturn(True)
154 gclient_scm.os.path.isfile(file_path1).AndReturn(True) 150 gclient_scm.os.path.isfile(file_path1).AndReturn(True)
155 gclient_scm.os.remove(file_path1) 151 gclient_scm.os.remove(file_path1)
156 gclient_scm.os.path.exists(file_path2).AndReturn(True) 152 gclient_scm.os.path.exists(file_path2).AndReturn(True)
157 gclient_scm.os.path.isfile(file_path2).AndReturn(True) 153 gclient_scm.os.path.isfile(file_path2).AndReturn(True)
158 gclient_scm.os.remove(file_path2) 154 gclient_scm.os.remove(file_path2)
159 gclient_scm.scm.SVN.RunAndGetFileList(options, 155 gclient_scm.RunSVNAndGetFileList(options, ['update', '--revision', 'BASE'],
160 ['update', '--revision', 'BASE'], 156 base_path, mox.IgnoreArg())
161 base_path, mox.IgnoreArg())
162 print(gclient_scm.os.path.join(base_path, 'a')) 157 print(gclient_scm.os.path.join(base_path, 'a'))
163 print(gclient_scm.os.path.join(base_path, 'b')) 158 print(gclient_scm.os.path.join(base_path, 'b'))
164 159
165 self.mox.ReplayAll() 160 self.mox.ReplayAll()
166 scm = self._scm_wrapper(url=self.url, root_dir=self.root_dir, 161 scm = self._scm_wrapper(url=self.url, root_dir=self.root_dir,
167 relpath=self.relpath) 162 relpath=self.relpath)
168 file_list = [] 163 file_list = []
169 scm.revert(options, self.args, file_list) 164 scm.revert(options, self.args, file_list)
170 165
171 def testRevertDirectory(self): 166 def testRevertDirectory(self):
172 options = self.Options(verbose=True) 167 options = self.Options(verbose=True)
173 base_path = gclient_scm.os.path.join(self.root_dir, self.relpath) 168 base_path = gclient_scm.os.path.join(self.root_dir, self.relpath)
174 gclient_scm.os.path.isdir(base_path).AndReturn(True) 169 gclient_scm.os.path.isdir(base_path).AndReturn(True)
175 items = [ 170 items = [
176 ('~ ', 'a'), 171 ('~ ', 'a'),
177 ] 172 ]
178 gclient_scm.scm.SVN.CaptureStatus(base_path).AndReturn(items) 173 gclient_scm.CaptureSVNStatus(base_path).AndReturn(items)
179 file_path = gclient_scm.os.path.join(base_path, 'a') 174 file_path = gclient_scm.os.path.join(base_path, 'a')
180 print(file_path) 175 print(file_path)
181 gclient_scm.os.path.exists(file_path).AndReturn(True) 176 gclient_scm.os.path.exists(file_path).AndReturn(True)
182 gclient_scm.os.path.isfile(file_path).AndReturn(False) 177 gclient_scm.os.path.isfile(file_path).AndReturn(False)
183 gclient_scm.os.path.isdir(file_path).AndReturn(True) 178 gclient_scm.os.path.isdir(file_path).AndReturn(True)
184 gclient_scm.gclient_utils.RemoveDirectory(file_path) 179 gclient_scm.gclient_utils.RemoveDirectory(file_path)
185 file_list1 = [] 180 file_list1 = []
186 gclient_scm.scm.SVN.RunAndGetFileList(options, 181 gclient_scm.RunSVNAndGetFileList(options, ['update', '--revision', 'BASE'],
187 ['update', '--revision', 'BASE'], 182 base_path, mox.IgnoreArg())
188 base_path, mox.IgnoreArg())
189 183
190 self.mox.ReplayAll() 184 self.mox.ReplayAll()
191 scm = self._scm_wrapper(url=self.url, root_dir=self.root_dir, 185 scm = self._scm_wrapper(url=self.url, root_dir=self.root_dir,
192 relpath=self.relpath) 186 relpath=self.relpath)
193 file_list2 = [] 187 file_list2 = []
194 scm.revert(options, self.args, file_list2) 188 scm.revert(options, self.args, file_list2)
195 189
196 def testStatus(self): 190 def testStatus(self):
197 options = self.Options(verbose=True) 191 options = self.Options(verbose=True)
198 base_path = gclient_scm.os.path.join(self.root_dir, self.relpath) 192 base_path = gclient_scm.os.path.join(self.root_dir, self.relpath)
199 gclient_scm.os.path.isdir(base_path).AndReturn(True) 193 gclient_scm.os.path.isdir(base_path).AndReturn(True)
200 gclient_scm.scm.SVN.RunAndGetFileList(options, 194 gclient_scm.RunSVNAndGetFileList(options, ['status'] + self.args,
201 ['status'] + self.args, 195 base_path, []).AndReturn(None)
202 base_path, []).AndReturn(None)
203 196
204 self.mox.ReplayAll() 197 self.mox.ReplayAll()
205 scm = self._scm_wrapper(url=self.url, root_dir=self.root_dir, 198 scm = self._scm_wrapper(url=self.url, root_dir=self.root_dir,
206 relpath=self.relpath) 199 relpath=self.relpath)
207 file_list = [] 200 file_list = []
208 self.assertEqual(scm.status(options, self.args, file_list), None) 201 self.assertEqual(scm.status(options, self.args, file_list), None)
209 202
210 203
211 # TODO(maruel): TEST REVISIONS!!! 204 # TODO(maruel): TEST REVISIONS!!!
212 # TODO(maruel): TEST RELOCATE!!! 205 # TODO(maruel): TEST RELOCATE!!!
213 def testUpdateCheckout(self): 206 def testUpdateCheckout(self):
214 options = self.Options(verbose=True) 207 options = self.Options(verbose=True)
215 base_path = gclient_scm.os.path.join(self.root_dir, self.relpath) 208 base_path = gclient_scm.os.path.join(self.root_dir, self.relpath)
216 file_info = gclient_scm.gclient_utils.PrintableObject() 209 file_info = gclient_scm.gclient_utils.PrintableObject()
217 file_info.root = 'blah' 210 file_info.root = 'blah'
218 file_info.url = self.url 211 file_info.url = self.url
219 file_info.uuid = 'ABC' 212 file_info.uuid = 'ABC'
220 file_info.revision = 42 213 file_info.revision = 42
221 gclient_scm.os.path.exists(gclient_scm.os.path.join(base_path, '.git') 214 gclient_scm.os.path.exists(gclient_scm.os.path.join(base_path, '.git')
222 ).AndReturn(False) 215 ).AndReturn(False)
223 # Checkout. 216 # Checkout.
224 gclient_scm.os.path.exists(base_path).AndReturn(False) 217 gclient_scm.os.path.exists(base_path).AndReturn(False)
225 files_list = self.mox.CreateMockAnything() 218 files_list = self.mox.CreateMockAnything()
226 gclient_scm.scm.SVN.RunAndGetFileList(options, 219 gclient_scm.RunSVNAndGetFileList(options, ['checkout', self.url,
227 ['checkout', self.url, base_path], 220 base_path], self.root_dir, files_list)
228 self.root_dir, files_list)
229 self.mox.ReplayAll() 221 self.mox.ReplayAll()
230 scm = self._scm_wrapper(url=self.url, root_dir=self.root_dir, 222 scm = self._scm_wrapper(url=self.url, root_dir=self.root_dir,
231 relpath=self.relpath) 223 relpath=self.relpath)
232 scm.update(options, (), files_list) 224 scm.update(options, (), files_list)
233 225
234 def testUpdateUpdate(self): 226 def testUpdateUpdate(self):
235 options = self.Options(verbose=True) 227 options = self.Options(verbose=True)
236 base_path = gclient_scm.os.path.join(self.root_dir, self.relpath) 228 base_path = gclient_scm.os.path.join(self.root_dir, self.relpath)
237 options.force = True 229 options.force = True
238 options.nohooks = False 230 options.nohooks = False
239 file_info = { 231 file_info = {
240 'Repository Root': 'blah', 232 'Repository Root': 'blah',
241 'URL': self.url, 233 'URL': self.url,
242 'UUID': 'ABC', 234 'UUID': 'ABC',
243 'Revision': 42, 235 'Revision': 42,
244 } 236 }
245 gclient_scm.os.path.exists(gclient_scm.os.path.join(base_path, '.git') 237 gclient_scm.os.path.exists(gclient_scm.os.path.join(base_path, '.git')
246 ).AndReturn(False) 238 ).AndReturn(False)
247 # Checkout or update. 239 # Checkout or update.
248 gclient_scm.os.path.exists(base_path).AndReturn(True) 240 gclient_scm.os.path.exists(base_path).AndReturn(True)
249 gclient_scm.scm.SVN.CaptureInfo( 241 gclient_scm.CaptureSVNInfo(gclient_scm.os.path.join(base_path, "."), '.'
250 gclient_scm.os.path.join(base_path, "."), '.' 242 ).AndReturn(file_info)
251 ).AndReturn(file_info)
252 # Cheat a bit here. 243 # Cheat a bit here.
253 gclient_scm.scm.SVN.CaptureInfo(file_info['URL'], '.').AndReturn(file_info) 244 gclient_scm.CaptureSVNInfo(file_info['URL'], '.').AndReturn(file_info)
254 additional_args = [] 245 additional_args = []
255 if options.manually_grab_svn_rev: 246 if options.manually_grab_svn_rev:
256 additional_args = ['--revision', str(file_info['Revision'])] 247 additional_args = ['--revision', str(file_info['Revision'])]
257 files_list = [] 248 files_list = []
258 gclient_scm.scm.SVN.RunAndGetFileList( 249 gclient_scm.RunSVNAndGetFileList(options,
259 options, 250 ['update', base_path] + additional_args,
260 ['update', base_path] + additional_args, 251 self.root_dir, files_list)
261 self.root_dir, files_list)
262 252
263 self.mox.ReplayAll() 253 self.mox.ReplayAll()
264 scm = self._scm_wrapper(url=self.url, root_dir=self.root_dir, 254 scm = self._scm_wrapper(url=self.url, root_dir=self.root_dir,
265 relpath=self.relpath) 255 relpath=self.relpath)
266 scm.update(options, (), files_list) 256 scm.update(options, (), files_list)
267 257
268 def testUpdateGit(self): 258 def testUpdateGit(self):
269 options = self.Options(verbose=True) 259 options = self.Options(verbose=True)
270 file_path = gclient_scm.os.path.join(self.root_dir, self.relpath, '.git') 260 file_path = gclient_scm.os.path.join(self.root_dir, self.relpath, '.git')
271 gclient_scm.os.path.exists(file_path).AndReturn(True) 261 gclient_scm.os.path.exists(file_path).AndReturn(True)
(...skipping 87 matching lines...) Expand 10 before | Expand all | Expand 10 after
359 self.base_path = gclient_scm.os.path.join(self.root_dir, self.relpath) 349 self.base_path = gclient_scm.os.path.join(self.root_dir, self.relpath)
360 self.enabled = self.CreateGitRepo(self.sample_git_import, self.base_path) 350 self.enabled = self.CreateGitRepo(self.sample_git_import, self.base_path)
361 SuperMoxBaseTestBase.setUp(self) 351 SuperMoxBaseTestBase.setUp(self)
362 352
363 def tearDown(self): 353 def tearDown(self):
364 SuperMoxBaseTestBase.tearDown(self) 354 SuperMoxBaseTestBase.tearDown(self)
365 shutil.rmtree(self.root_dir) 355 shutil.rmtree(self.root_dir)
366 356
367 def testDir(self): 357 def testDir(self):
368 members = [ 358 members = [
369 'COMMAND', 'Capture', 'CaptureStatus', 'FullUrlForRelativeUrl', 359 'FullUrlForRelativeUrl', 'RunCommand', 'cleanup', 'diff', 'export',
370 'RunCommand', 'cleanup', 'diff', 'export', 'relpath', 'revert', 360 'relpath', 'revert', 'revinfo', 'runhooks', 'scm_name', 'status',
371 'revinfo', 'runhooks', 'scm_name', 'status', 'update', 'url', 361 'update', 'url',
372 ] 362 ]
373 363
374 # If you add a member, be sure to add the relevant test! 364 # If you add a member, be sure to add the relevant test!
375 self.compareMembers(gclient_scm.CreateSCM(url=self.url), members) 365 self.compareMembers(gclient_scm.CreateSCM(url=self.url), members)
376 366
377 def testRevertMissing(self): 367 def testRevertMissing(self):
378 if not self.enabled: 368 if not self.enabled:
379 return 369 return
380 options = self.Options() 370 options = self.Options()
381 file_path = gclient_scm.os.path.join(self.base_path, 'a') 371 file_path = gclient_scm.os.path.join(self.base_path, 'a')
(...skipping 130 matching lines...) Expand 10 before | Expand all | Expand 10 after
512 relpath=self.relpath) 502 relpath=self.relpath)
513 rev_info = scm.revinfo(options, (), None) 503 rev_info = scm.revinfo(options, (), None)
514 self.assertEquals(rev_info, '069c602044c5388d2d15c3f875b057c852003458') 504 self.assertEquals(rev_info, '069c602044c5388d2d15c3f875b057c852003458')
515 505
516 506
517 if __name__ == '__main__': 507 if __name__ == '__main__':
518 import unittest 508 import unittest
519 unittest.main() 509 unittest.main()
520 510
521 # vim: ts=2:sw=2:tw=80:et: 511 # vim: ts=2:sw=2:tw=80:et:
OLDNEW
« no previous file with comments | « tests/gcl_unittest.py ('k') | tests/gclient_test.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698