blob: 57642fdbbd35630c80dac3134c13cda7926599b5 [file] [log] [blame]
Daniel Erat9d203ff2015-02-17 10:12:21 -07001#!/usr/bin/python2
Jon Salz98255932012-08-18 14:48:02 +08002# -*- coding: utf-8 -*-
3# Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
4# Use of this source code is governed by a BSD-style license that can be
5# found in the LICENSE file.
6
Mike Frysingerae409522014-02-01 03:16:11 -05007"""Unittests for pre-upload.py."""
8
Mike Frysinger09d6a3d2013-10-08 22:21:03 -04009from __future__ import print_function
10
David Jamesc3b68b32013-04-03 09:17:03 -070011import os
12import sys
Jon Salz98255932012-08-18 14:48:02 +080013
Mike Frysingerbf8b91c2014-02-01 02:50:27 -050014import errors
15
Jon Salz98255932012-08-18 14:48:02 +080016# pylint: disable=W0212
Mike Frysinger65d93c12014-02-01 02:59:46 -050017# We access private members of the pre_upload module all over the place.
18
Mike Frysinger55f85b52014-12-18 14:45:21 -050019# Make sure we can find the chromite paths.
20sys.path.insert(0, os.path.join(os.path.dirname(os.path.realpath(__file__)),
21 '..', '..'))
Jon Salz98255932012-08-18 14:48:02 +080022
Mike Frysinger55f85b52014-12-18 14:45:21 -050023from chromite.cbuildbot import constants
Mike Frysinger65d93c12014-02-01 02:59:46 -050024from chromite.lib import cros_test_lib
Mike Frysingerd3bd32c2014-11-24 23:34:29 -050025from chromite.lib import git
Daniel Erata350fd32014-09-29 14:02:34 -070026from chromite.lib import osutils
Mike Frysinger65d93c12014-02-01 02:59:46 -050027
Mike Frysinger55f85b52014-12-18 14:45:21 -050028# Needs to be after chromite imports so we use the bundled copy.
29import mock
30
Mike Frysinger65d93c12014-02-01 02:59:46 -050031
Jon Salz98255932012-08-18 14:48:02 +080032pre_upload = __import__('pre-upload')
33
34
Mike Frysinger65d93c12014-02-01 02:59:46 -050035class TryUTF8DecodeTest(cros_test_lib.TestCase):
Mike Frysingerae409522014-02-01 03:16:11 -050036 """Verify we sanely handle unicode content."""
37
Jon Salz98255932012-08-18 14:48:02 +080038 def runTest(self):
39 self.assertEquals(u'', pre_upload._try_utf8_decode(''))
40 self.assertEquals(u'abc', pre_upload._try_utf8_decode('abc'))
Daniel Erat9d203ff2015-02-17 10:12:21 -070041 self.assertEquals(
42 u'你好布萊恩',
43 pre_upload._try_utf8_decode('你好布萊恩'))
Jon Salz98255932012-08-18 14:48:02 +080044 # Invalid UTF-8
45 self.assertEquals('\x80', pre_upload._try_utf8_decode('\x80'))
46
47
Mike Frysinger1459d362014-12-06 13:53:23 -050048class CheckNoLongLinesTest(cros_test_lib.MockTestCase):
Mike Frysingerae409522014-02-01 03:16:11 -050049 """Tests for _check_no_long_lines."""
50
Jon Salz98255932012-08-18 14:48:02 +080051 def setUp(self):
Mike Frysinger1459d362014-12-06 13:53:23 -050052 self.PatchObject(pre_upload, '_get_affected_files', return_value=['x.py'])
53 self.PatchObject(pre_upload, '_filter_files', return_value=['x.py'])
54 self.diff_mock = self.PatchObject(pre_upload, '_get_file_diff')
Jon Salz98255932012-08-18 14:48:02 +080055
Jon Salz98255932012-08-18 14:48:02 +080056 def runTest(self):
Mike Frysinger1459d362014-12-06 13:53:23 -050057 self.diff_mock.return_value = [
58 (1, u"x" * 80), # OK
59 (2, "\x80" * 80), # OK
60 (3, u"x" * 81), # Too long
61 (4, "\x80" * 81), # Too long
62 (5, u"See http://" + (u"x" * 80)), # OK (URL)
63 (6, u"See https://" + (u"x" * 80)), # OK (URL)
64 (7, u"# define " + (u"x" * 80)), # OK (compiler directive)
65 (8, u"#define" + (u"x" * 74)), # Too long
66 ]
Jon Salz98255932012-08-18 14:48:02 +080067 failure = pre_upload._check_no_long_lines('PROJECT', 'COMMIT')
68 self.assertTrue(failure)
69 self.assertEquals('Found lines longer than 80 characters (first 5 shown):',
70 failure.msg)
71 self.assertEquals(['x.py, line %d, 81 chars' % line
72 for line in [3, 4, 8]],
73 failure.items)
74
Mike Frysingerae409522014-02-01 03:16:11 -050075
Daniel Erata350fd32014-09-29 14:02:34 -070076class CheckProjectPrefix(cros_test_lib.MockTempDirTestCase):
77 """Tests for _check_project_prefix."""
78
79 def setUp(self):
80 self.orig_cwd = os.getcwd()
81 os.chdir(self.tempdir)
82 self.file_mock = self.PatchObject(pre_upload, '_get_affected_files')
83 self.desc_mock = self.PatchObject(pre_upload, '_get_commit_desc')
84
85 def tearDown(self):
86 os.chdir(self.orig_cwd)
87
88 def _WriteAliasFile(self, filename, project):
89 """Writes a project name to a file, creating directories if needed."""
90 os.makedirs(os.path.dirname(filename))
91 osutils.WriteFile(filename, project)
92
93 def testInvalidPrefix(self):
94 """Report an error when the prefix doesn't match the base directory."""
95 self.file_mock.return_value = ['foo/foo.cc', 'foo/subdir/baz.cc']
96 self.desc_mock.return_value = 'bar: Some commit'
97 failure = pre_upload._check_project_prefix('PROJECT', 'COMMIT')
98 self.assertTrue(failure)
99 self.assertEquals(('The commit title for changes affecting only foo' +
100 ' should start with "foo: "'), failure.msg)
101
102 def testValidPrefix(self):
103 """Use a prefix that matches the base directory."""
104 self.file_mock.return_value = ['foo/foo.cc', 'foo/subdir/baz.cc']
105 self.desc_mock.return_value = 'foo: Change some files.'
106 self.assertFalse(pre_upload._check_project_prefix('PROJECT', 'COMMIT'))
107
108 def testAliasFile(self):
109 """Use .project_alias to override the project name."""
110 self._WriteAliasFile('foo/.project_alias', 'project')
111 self.file_mock.return_value = ['foo/foo.cc', 'foo/subdir/bar.cc']
112 self.desc_mock.return_value = 'project: Use an alias.'
113 self.assertFalse(pre_upload._check_project_prefix('PROJECT', 'COMMIT'))
114
115 def testAliasFileWithSubdirs(self):
116 """Check that .project_alias is used when only modifying subdirectories."""
117 self._WriteAliasFile('foo/.project_alias', 'project')
118 self.file_mock.return_value = [
119 'foo/subdir/foo.cc',
120 'foo/subdir/bar.cc'
121 'foo/subdir/blah/baz.cc'
122 ]
123 self.desc_mock.return_value = 'project: Alias with subdirs.'
124 self.assertFalse(pre_upload._check_project_prefix('PROJECT', 'COMMIT'))
125
126
Mike Frysinger1459d362014-12-06 13:53:23 -0500127class CheckKernelConfig(cros_test_lib.MockTestCase):
Mike Frysingerae409522014-02-01 03:16:11 -0500128 """Tests for _kernel_configcheck."""
129
Mike Frysinger1459d362014-12-06 13:53:23 -0500130 def setUp(self):
131 self.file_mock = self.PatchObject(pre_upload, '_get_affected_files')
132
133 def testMixedChanges(self):
134 """Mixing of changes should fail."""
135 self.file_mock.return_value = [
136 '/kernel/files/chromeos/config/base.config',
137 '/kernel/files/arch/arm/mach-exynos/mach-exynos5-dt.c'
138 ]
Olof Johanssona96810f2012-09-04 16:20:03 -0700139 failure = pre_upload._kernel_configcheck('PROJECT', 'COMMIT')
140 self.assertTrue(failure)
141
Mike Frysinger1459d362014-12-06 13:53:23 -0500142 def testCodeOnly(self):
143 """Code-only changes should pass."""
144 self.file_mock.return_value = [
145 '/kernel/files/Makefile',
146 '/kernel/files/arch/arm/mach-exynos/mach-exynos5-dt.c'
147 ]
Olof Johanssona96810f2012-09-04 16:20:03 -0700148 failure = pre_upload._kernel_configcheck('PROJECT', 'COMMIT')
149 self.assertFalse(failure)
150
Mike Frysinger1459d362014-12-06 13:53:23 -0500151 def testConfigOnlyChanges(self):
152 """Config-only changes should pass."""
153 self.file_mock.return_value = [
154 '/kernel/files/chromeos/config/base.config',
155 ]
Olof Johanssona96810f2012-09-04 16:20:03 -0700156 failure = pre_upload._kernel_configcheck('PROJECT', 'COMMIT')
157 self.assertFalse(failure)
158
Jon Salz98255932012-08-18 14:48:02 +0800159
Daniel Erat9d203ff2015-02-17 10:12:21 -0700160class CheckPortageMakeUseVar(cros_test_lib.MockTestCase):
161 """Tests for _check_portage_make_use_var."""
162
163 def setUp(self):
164 self.file_mock = self.PatchObject(pre_upload, '_get_affected_files')
165 self.content_mock = self.PatchObject(pre_upload, '_get_file_content')
166
167 def testMakeConfOmitsOriginalUseValue(self):
168 """Fail for make.conf that discards the previous value of $USE."""
169 self.file_mock.return_value = ['make.conf']
170 self.content_mock.return_value = 'USE="foo"\nUSE="${USE} bar"'
171 failure = pre_upload._check_portage_make_use_var('PROJECT', 'COMMIT')
172 self.assertTrue(failure, failure)
173
174 def testMakeConfCorrectUsage(self):
175 """Succeed for make.conf that preserves the previous value of $USE."""
176 self.file_mock.return_value = ['make.conf']
177 self.content_mock.return_value = 'USE="${USE} foo"\nUSE="${USE}" bar'
178 failure = pre_upload._check_portage_make_use_var('PROJECT', 'COMMIT')
179 self.assertFalse(failure, failure)
180
181 def testMakeDefaultsReferencesOriginalUseValue(self):
182 """Fail for make.defaults that refers to a not-yet-set $USE value."""
183 self.file_mock.return_value = ['make.defaults']
184 self.content_mock.return_value = 'USE="${USE} foo"'
185 failure = pre_upload._check_portage_make_use_var('PROJECT', 'COMMIT')
186 self.assertTrue(failure, failure)
187
188 # Also check for "$USE" without curly brackets.
189 self.content_mock.return_value = 'USE="$USE foo"'
190 failure = pre_upload._check_portage_make_use_var('PROJECT', 'COMMIT')
191 self.assertTrue(failure, failure)
192
193 def testMakeDefaultsOverwritesUseValue(self):
194 """Fail for make.defaults that discards its own $USE value."""
195 self.file_mock.return_value = ['make.defaults']
196 self.content_mock.return_value = 'USE="foo"\nUSE="bar"'
197 failure = pre_upload._check_portage_make_use_var('PROJECT', 'COMMIT')
198 self.assertTrue(failure, failure)
199
200 def testMakeDefaultsCorrectUsage(self):
201 """Succeed for make.defaults that sets and preserves $USE."""
202 self.file_mock.return_value = ['make.defaults']
203 self.content_mock.return_value = 'USE="foo"\nUSE="${USE}" bar'
204 failure = pre_upload._check_portage_make_use_var('PROJECT', 'COMMIT')
205 self.assertFalse(failure, failure)
206
207
Mike Frysingerbf8b91c2014-02-01 02:50:27 -0500208class CheckEbuildEapi(cros_test_lib.MockTestCase):
209 """Tests for _check_ebuild_eapi."""
210
211 PORTAGE_STABLE = 'chromiumos/overlays/portage-stable'
212
213 def setUp(self):
214 self.file_mock = self.PatchObject(pre_upload, '_get_affected_files')
215 self.content_mock = self.PatchObject(pre_upload, '_get_file_content')
216 self.diff_mock = self.PatchObject(pre_upload, '_get_file_diff',
217 side_effect=Exception())
218
219 def testSkipUpstreamOverlays(self):
220 """Skip ebuilds found in upstream overlays."""
221 self.file_mock.side_effect = Exception()
222 ret = pre_upload._check_ebuild_eapi(self.PORTAGE_STABLE, 'HEAD')
223 self.assertEqual(ret, None)
224
225 # Make sure our condition above triggers.
226 self.assertRaises(Exception, pre_upload._check_ebuild_eapi, 'o', 'HEAD')
227
228 def testSkipNonEbuilds(self):
229 """Skip non-ebuild files."""
230 self.content_mock.side_effect = Exception()
231
232 self.file_mock.return_value = ['some-file', 'ebuild/dir', 'an.ebuild~']
233 ret = pre_upload._check_ebuild_eapi('overlay', 'HEAD')
234 self.assertEqual(ret, None)
235
236 # Make sure our condition above triggers.
237 self.file_mock.return_value.append('a/real.ebuild')
238 self.assertRaises(Exception, pre_upload._check_ebuild_eapi, 'o', 'HEAD')
239
240 def testSkipSymlink(self):
241 """Skip files that are just symlinks."""
242 self.file_mock.return_value = ['a-r1.ebuild']
243 self.content_mock.return_value = 'a.ebuild'
244 ret = pre_upload._check_ebuild_eapi('overlay', 'HEAD')
245 self.assertEqual(ret, None)
246
247 def testRejectEapiImplicit0Content(self):
248 """Reject ebuilds that do not declare EAPI (so it's 0)."""
249 self.file_mock.return_value = ['a.ebuild']
250
251 self.content_mock.return_value = """# Header
252IUSE="foo"
253src_compile() { }
254"""
255 ret = pre_upload._check_ebuild_eapi('overlay', 'HEAD')
Mike Frysingerb81102f2014-11-21 00:33:35 -0500256 self.assertTrue(isinstance(ret, errors.HookFailure))
Mike Frysingerbf8b91c2014-02-01 02:50:27 -0500257
258 def testRejectExplicitEapi1Content(self):
259 """Reject ebuilds that do declare old EAPI explicitly."""
260 self.file_mock.return_value = ['a.ebuild']
261
262 template = """# Header
263EAPI=%s
264IUSE="foo"
265src_compile() { }
266"""
267 # Make sure we only check the first EAPI= setting.
268 self.content_mock.return_value = template % '1\nEAPI=4'
269 ret = pre_upload._check_ebuild_eapi('overlay', 'HEAD')
Mike Frysingerb81102f2014-11-21 00:33:35 -0500270 self.assertTrue(isinstance(ret, errors.HookFailure))
Mike Frysingerbf8b91c2014-02-01 02:50:27 -0500271
272 # Verify we handle double quotes too.
273 self.content_mock.return_value = template % '"1"'
274 ret = pre_upload._check_ebuild_eapi('overlay', 'HEAD')
Mike Frysingerb81102f2014-11-21 00:33:35 -0500275 self.assertTrue(isinstance(ret, errors.HookFailure))
Mike Frysingerbf8b91c2014-02-01 02:50:27 -0500276
277 # Verify we handle single quotes too.
278 self.content_mock.return_value = template % "'1'"
279 ret = pre_upload._check_ebuild_eapi('overlay', 'HEAD')
Mike Frysingerb81102f2014-11-21 00:33:35 -0500280 self.assertTrue(isinstance(ret, errors.HookFailure))
Mike Frysingerbf8b91c2014-02-01 02:50:27 -0500281
282 def testAcceptExplicitEapi4Content(self):
283 """Accept ebuilds that do declare new EAPI explicitly."""
284 self.file_mock.return_value = ['a.ebuild']
285
286 template = """# Header
287EAPI=%s
288IUSE="foo"
289src_compile() { }
290"""
291 # Make sure we only check the first EAPI= setting.
292 self.content_mock.return_value = template % '4\nEAPI=1'
293 ret = pre_upload._check_ebuild_eapi('overlay', 'HEAD')
294 self.assertEqual(ret, None)
295
296 # Verify we handle double quotes too.
297 self.content_mock.return_value = template % '"5"'
298 ret = pre_upload._check_ebuild_eapi('overlay', 'HEAD')
299 self.assertEqual(ret, None)
300
301 # Verify we handle single quotes too.
302 self.content_mock.return_value = template % "'5-hdepend'"
303 ret = pre_upload._check_ebuild_eapi('overlay', 'HEAD')
304 self.assertEqual(ret, None)
305
306
Mike Frysinger5c9e58d2014-09-09 03:32:50 -0400307class CheckEbuildKeywords(cros_test_lib.MockTestCase):
308 """Tests for _check_ebuild_keywords."""
309
310 def setUp(self):
311 self.file_mock = self.PatchObject(pre_upload, '_get_affected_files')
312 self.content_mock = self.PatchObject(pre_upload, '_get_file_content')
313
314 def testNoEbuilds(self):
315 """If no ebuilds are found, do not scan."""
316 self.file_mock.return_value = ['a.file', 'ebuild-is-not.foo']
317
318 ret = pre_upload._check_ebuild_keywords('overlay', 'HEAD')
319 self.assertEqual(ret, None)
320
321 self.assertEqual(self.content_mock.call_count, 0)
322
323 def testSomeEbuilds(self):
324 """If ebuilds are found, only scan them."""
325 self.file_mock.return_value = ['a.file', 'blah', 'foo.ebuild', 'cow']
326 self.content_mock.return_value = ''
327
328 ret = pre_upload._check_ebuild_keywords('overlay', 'HEAD')
329 self.assertEqual(ret, None)
330
331 self.assertEqual(self.content_mock.call_count, 1)
332
333 def _CheckContent(self, content, fails):
334 """Test helper for inputs/outputs.
335
336 Args:
337 content: The ebuild content to test.
338 fails: Whether |content| should trigger a hook failure.
339 """
340 self.file_mock.return_value = ['a.ebuild']
341 self.content_mock.return_value = content
342
343 ret = pre_upload._check_ebuild_keywords('overlay', 'HEAD')
344 if fails:
Mike Frysingerb81102f2014-11-21 00:33:35 -0500345 self.assertTrue(isinstance(ret, errors.HookFailure))
Mike Frysinger5c9e58d2014-09-09 03:32:50 -0400346 else:
347 self.assertEqual(ret, None)
348
349 self.assertEqual(self.content_mock.call_count, 1)
350
351 def testEmpty(self):
352 """Check KEYWORDS= is accepted."""
353 self._CheckContent('# HEADER\nKEYWORDS=\nblah\n', False)
354
355 def testEmptyQuotes(self):
356 """Check KEYWORDS="" is accepted."""
357 self._CheckContent('# HEADER\nKEYWORDS=" "\nblah\n', False)
358
359 def testStableGlob(self):
360 """Check KEYWORDS=* is accepted."""
361 self._CheckContent('# HEADER\nKEYWORDS="\t*\t"\nblah\n', False)
362
363 def testUnstableGlob(self):
364 """Check KEYWORDS=~* is accepted."""
365 self._CheckContent('# HEADER\nKEYWORDS="~* "\nblah\n', False)
366
367 def testRestrictedGlob(self):
368 """Check KEYWORDS=-* is accepted."""
369 self._CheckContent('# HEADER\nKEYWORDS="\t-* arm"\nblah\n', False)
370
371 def testMissingGlobs(self):
372 """Reject KEYWORDS missing any globs."""
373 self._CheckContent('# HEADER\nKEYWORDS="~arm x86"\nblah\n', True)
374
375
Mike Frysingercd363c82014-02-01 05:20:18 -0500376class CheckEbuildVirtualPv(cros_test_lib.MockTestCase):
377 """Tests for _check_ebuild_virtual_pv."""
378
379 PORTAGE_STABLE = 'chromiumos/overlays/portage-stable'
380 CHROMIUMOS_OVERLAY = 'chromiumos/overlays/chromiumos'
381 BOARD_OVERLAY = 'chromiumos/overlays/board-overlays'
382 PRIVATE_OVERLAY = 'chromeos/overlays/overlay-link-private'
383 PRIVATE_VARIANT_OVERLAY = ('chromeos/overlays/'
384 'overlay-variant-daisy-spring-private')
385
386 def setUp(self):
387 self.file_mock = self.PatchObject(pre_upload, '_get_affected_files')
388
389 def testNoVirtuals(self):
390 """Skip non virtual packages."""
391 self.file_mock.return_value = ['some/package/package-3.ebuild']
392 ret = pre_upload._check_ebuild_virtual_pv('overlay', 'H')
393 self.assertEqual(ret, None)
394
395 def testCommonVirtuals(self):
396 """Non-board overlays should use PV=1."""
397 template = 'virtual/foo/foo-%s.ebuild'
398 self.file_mock.return_value = [template % '1']
399 ret = pre_upload._check_ebuild_virtual_pv(self.CHROMIUMOS_OVERLAY, 'H')
400 self.assertEqual(ret, None)
401
402 self.file_mock.return_value = [template % '2']
403 ret = pre_upload._check_ebuild_virtual_pv(self.CHROMIUMOS_OVERLAY, 'H')
Mike Frysingerb81102f2014-11-21 00:33:35 -0500404 self.assertTrue(isinstance(ret, errors.HookFailure))
Mike Frysingercd363c82014-02-01 05:20:18 -0500405
406 def testPublicBoardVirtuals(self):
407 """Public board overlays should use PV=2."""
408 template = 'overlay-lumpy/virtual/foo/foo-%s.ebuild'
409 self.file_mock.return_value = [template % '2']
410 ret = pre_upload._check_ebuild_virtual_pv(self.BOARD_OVERLAY, 'H')
411 self.assertEqual(ret, None)
412
413 self.file_mock.return_value = [template % '2.5']
414 ret = pre_upload._check_ebuild_virtual_pv(self.BOARD_OVERLAY, 'H')
Mike Frysingerb81102f2014-11-21 00:33:35 -0500415 self.assertTrue(isinstance(ret, errors.HookFailure))
Mike Frysingercd363c82014-02-01 05:20:18 -0500416
417 def testPublicBoardVariantVirtuals(self):
418 """Public board variant overlays should use PV=2.5."""
419 template = 'overlay-variant-lumpy-foo/virtual/foo/foo-%s.ebuild'
420 self.file_mock.return_value = [template % '2.5']
421 ret = pre_upload._check_ebuild_virtual_pv(self.BOARD_OVERLAY, 'H')
422 self.assertEqual(ret, None)
423
424 self.file_mock.return_value = [template % '3']
425 ret = pre_upload._check_ebuild_virtual_pv(self.BOARD_OVERLAY, 'H')
Mike Frysingerb81102f2014-11-21 00:33:35 -0500426 self.assertTrue(isinstance(ret, errors.HookFailure))
Mike Frysingercd363c82014-02-01 05:20:18 -0500427
428 def testPrivateBoardVirtuals(self):
429 """Private board overlays should use PV=3."""
430 template = 'virtual/foo/foo-%s.ebuild'
431 self.file_mock.return_value = [template % '3']
432 ret = pre_upload._check_ebuild_virtual_pv(self.PRIVATE_OVERLAY, 'H')
433 self.assertEqual(ret, None)
434
435 self.file_mock.return_value = [template % '3.5']
436 ret = pre_upload._check_ebuild_virtual_pv(self.PRIVATE_OVERLAY, 'H')
Mike Frysingerb81102f2014-11-21 00:33:35 -0500437 self.assertTrue(isinstance(ret, errors.HookFailure))
Mike Frysingercd363c82014-02-01 05:20:18 -0500438
439 def testPrivateBoardVariantVirtuals(self):
440 """Private board variant overlays should use PV=3.5."""
441 template = 'virtual/foo/foo-%s.ebuild'
442 self.file_mock.return_value = [template % '3.5']
443 ret = pre_upload._check_ebuild_virtual_pv(self.PRIVATE_VARIANT_OVERLAY, 'H')
444 self.assertEqual(ret, None)
445
446 self.file_mock.return_value = [template % '4']
447 ret = pre_upload._check_ebuild_virtual_pv(self.PRIVATE_VARIANT_OVERLAY, 'H')
Mike Frysingerb81102f2014-11-21 00:33:35 -0500448 self.assertTrue(isinstance(ret, errors.HookFailure))
Mike Frysingercd363c82014-02-01 05:20:18 -0500449
Mike Frysinger98638102014-08-28 00:15:08 -0400450
Mike Frysinger98638102014-08-28 00:15:08 -0400451class CheckLicenseCopyrightHeader(cros_test_lib.MockTestCase):
452 """Tests for _check_license."""
453
454 def setUp(self):
455 self.file_mock = self.PatchObject(pre_upload, '_get_affected_files')
456 self.content_mock = self.PatchObject(pre_upload, '_get_file_content')
457
458 def testOldHeaders(self):
459 """Accept old header styles."""
460 HEADERS = (
461 ('#!/bin/sh\n'
462 '# Copyright (c) 2012 The Chromium OS Authors. All rights reserved.\n'
463 '# Use of this source code is governed by a BSD-style license that'
464 ' can be\n'
465 '# found in the LICENSE file.\n'),
466 ('// Copyright 2010-13 The Chromium OS Authors. All rights reserved.\n'
467 '// Use of this source code is governed by a BSD-style license that'
468 ' can be\n'
469 '// found in the LICENSE file.\n'),
470 )
471 self.file_mock.return_value = ['file']
472 for header in HEADERS:
473 self.content_mock.return_value = header
474 self.assertEqual(None, pre_upload._check_license('proj', 'sha1'))
475
476 def testRejectC(self):
477 """Reject the (c) in newer headers."""
478 HEADERS = (
479 ('// Copyright (c) 2015 The Chromium OS Authors. All rights reserved.\n'
480 '// Use of this source code is governed by a BSD-style license that'
481 ' can be\n'
482 '// found in the LICENSE file.\n'),
483 ('// Copyright (c) 2020 The Chromium OS Authors. All rights reserved.\n'
484 '// Use of this source code is governed by a BSD-style license that'
485 ' can be\n'
486 '// found in the LICENSE file.\n'),
487 )
488 self.file_mock.return_value = ['file']
489 for header in HEADERS:
490 self.content_mock.return_value = header
491 self.assertNotEqual(None, pre_upload._check_license('proj', 'sha1'))
492
493
Mike Frysingerd7734522015-02-26 16:12:43 -0500494class CheckLayoutConfTestCase(cros_test_lib.MockTestCase):
495 """Tests for _check_layout_conf."""
496
497 def setUp(self):
498 self.file_mock = self.PatchObject(pre_upload, '_get_affected_files')
499 self.content_mock = self.PatchObject(pre_upload, '_get_file_content')
500
501 def assertAccepted(self, files, project='project', commit='fake sha1'):
502 """Assert _check_layout_conf accepts |files|."""
503 self.file_mock.return_value = files
504 ret = pre_upload._check_layout_conf(project, commit)
505 self.assertEqual(ret, None, msg='rejected with:\n%s' % ret)
506
507 def assertRejected(self, files, project='project', commit='fake sha1'):
508 """Assert _check_layout_conf rejects |files|."""
509 self.file_mock.return_value = files
510 ret = pre_upload._check_layout_conf(project, commit)
511 self.assertTrue(isinstance(ret, errors.HookFailure))
512
513 def GetLayoutConf(self, filters=()):
514 """Return a valid layout.conf with |filters| lines removed."""
515 all_lines = [
516 'masters = portage-stable chromiumos',
517 'profile-formats = portage-2 profile-default-eapi',
518 'profile_eapi_when_unspecified = 5-progress',
519 'repo-name = link',
520 'thin-manifests = true',
521 'use-manifests = true',
522 ]
523
524 lines = []
525 for line in all_lines:
526 for filt in filters:
527 if line.startswith(filt):
528 break
529 else:
530 lines.append(line)
531
532 return '\n'.join(lines)
533
534 def testNoFilesToCheck(self):
535 """Don't blow up when there are no layout.conf files."""
536 self.assertAccepted([])
537
538 def testRejectRepoNameFile(self):
539 """If profiles/repo_name is set, kick it out."""
540 self.assertRejected(['profiles/repo_name'])
541
542 def testAcceptValidLayoutConf(self):
543 """Accept a fully valid layout.conf."""
544 self.content_mock.return_value = self.GetLayoutConf()
545 self.assertAccepted(['metadata/layout.conf'])
546
547 def testAcceptUnknownKeys(self):
548 """Accept keys we don't explicitly know about."""
549 self.content_mock.return_value = self.GetLayoutConf() + '\nzzz-top = ok'
550 self.assertAccepted(['metadata/layout.conf'])
551
552 def testRejectUnsorted(self):
553 """Reject an unsorted layout.conf."""
554 self.content_mock.return_value = 'zzz-top = bad\n' + self.GetLayoutConf()
555 self.assertRejected(['metadata/layout.conf'])
556
557 def testRejectMissingThinManifests(self):
558 """Reject a layout.conf missing thin-manifests."""
559 self.content_mock.return_value = self.GetLayoutConf(
560 filters=['thin-manifests'])
561 self.assertRejected(['metadata/layout.conf'])
562
563 def testRejectMissingUseManifests(self):
564 """Reject a layout.conf missing use-manifests."""
565 self.content_mock.return_value = self.GetLayoutConf(
566 filters=['use-manifests'])
567 self.assertRejected(['metadata/layout.conf'])
568
569 def testRejectMissingEapiFallback(self):
570 """Reject a layout.conf missing profile_eapi_when_unspecified."""
571 self.content_mock.return_value = self.GetLayoutConf(
572 filters=['profile_eapi_when_unspecified'])
573 self.assertRejected(['metadata/layout.conf'])
574
575 def testRejectMissingRepoName(self):
576 """Reject a layout.conf missing repo-name."""
577 self.content_mock.return_value = self.GetLayoutConf(filters=['repo-name'])
578 self.assertRejected(['metadata/layout.conf'])
579
580
Mike Frysinger4a22bf02014-10-31 13:53:35 -0400581class CommitMessageTestCase(cros_test_lib.MockTestCase):
582 """Test case for funcs that check commit messages."""
583
584 def setUp(self):
585 self.msg_mock = self.PatchObject(pre_upload, '_get_commit_desc')
586
587 @staticmethod
588 def CheckMessage(_project, _commit):
589 raise AssertionError('Test class must declare CheckMessage')
590 # This dummy return is to silence pylint warning W1111 so we don't have to
591 # enable it for all the call sites below.
592 return 1 # pylint: disable=W0101
593
594 def assertMessageAccepted(self, msg, project='project', commit='1234'):
595 """Assert _check_change_has_bug_field accepts |msg|."""
596 self.msg_mock.return_value = msg
597 ret = self.CheckMessage(project, commit)
598 self.assertEqual(ret, None)
599
600 def assertMessageRejected(self, msg, project='project', commit='1234'):
601 """Assert _check_change_has_bug_field rejects |msg|."""
602 self.msg_mock.return_value = msg
603 ret = self.CheckMessage(project, commit)
604 self.assertTrue(isinstance(ret, errors.HookFailure))
605
606
607class CheckCommitMessageBug(CommitMessageTestCase):
608 """Tests for _check_change_has_bug_field."""
609
610 @staticmethod
611 def CheckMessage(project, commit):
612 return pre_upload._check_change_has_bug_field(project, commit)
613
614 def testNormal(self):
615 """Accept a commit message w/a valid BUG."""
616 self.assertMessageAccepted('\nBUG=chromium:1234\n')
617 self.assertMessageAccepted('\nBUG=chrome-os-partner:1234\n')
Stefan Sauerd74ad562015-03-10 10:14:28 +0100618 self.assertMessageAccepted('\nBUG=b:1234\n')
Mike Frysinger4a22bf02014-10-31 13:53:35 -0400619
620 def testNone(self):
621 """Accept BUG=None."""
622 self.assertMessageAccepted('\nBUG=None\n')
623 self.assertMessageAccepted('\nBUG=none\n')
624 self.assertMessageRejected('\nBUG=NONE\n')
625
626 def testBlank(self):
627 """Reject blank values."""
628 self.assertMessageRejected('\nBUG=\n')
629 self.assertMessageRejected('\nBUG= \n')
630
631 def testNotFirstLine(self):
632 """Reject the first line."""
633 self.assertMessageRejected('BUG=None\n\n\n')
634
635 def testNotInline(self):
636 """Reject not at the start of line."""
637 self.assertMessageRejected('\n BUG=None\n')
638 self.assertMessageRejected('\n\tBUG=None\n')
639
640 def testOldTrackers(self):
641 """Reject commit messages using old trackers."""
642 self.assertMessageRejected('\nBUG=chromium-os:1234\n')
643
644 def testNoTrackers(self):
645 """Reject commit messages w/invalid trackers."""
646 self.assertMessageRejected('\nBUG=booga:1234\n')
Stefan Sauerd74ad562015-03-10 10:14:28 +0100647 self.assertMessageRejected('\nBUG=br:1234\n')
Mike Frysinger4a22bf02014-10-31 13:53:35 -0400648
649 def testMissing(self):
650 """Reject commit messages w/no BUG line."""
651 self.assertMessageRejected('foo\n')
652
653 def testCase(self):
654 """Reject bug lines that are not BUG."""
655 self.assertMessageRejected('\nbug=none\n')
656
657
658class CheckCommitMessageCqDepend(CommitMessageTestCase):
659 """Tests for _check_change_has_valid_cq_depend."""
660
661 @staticmethod
662 def CheckMessage(project, commit):
663 return pre_upload._check_change_has_valid_cq_depend(project, commit)
664
665 def testNormal(self):
666 """Accept valid CQ-DEPENDs line."""
667 self.assertMessageAccepted('\nCQ-DEPEND=CL:1234\n')
668
669 def testInvalid(self):
670 """Reject invalid CQ-DEPENDs line."""
671 self.assertMessageRejected('\nCQ-DEPEND=CL=1234\n')
672 self.assertMessageRejected('\nCQ-DEPEND=None\n')
673
674
675class CheckCommitMessageTest(CommitMessageTestCase):
676 """Tests for _check_change_has_test_field."""
677
678 @staticmethod
679 def CheckMessage(project, commit):
680 return pre_upload._check_change_has_test_field(project, commit)
681
682 def testNormal(self):
683 """Accept a commit message w/a valid TEST."""
684 self.assertMessageAccepted('\nTEST=i did it\n')
685
686 def testNone(self):
687 """Accept TEST=None."""
688 self.assertMessageAccepted('\nTEST=None\n')
689 self.assertMessageAccepted('\nTEST=none\n')
690
691 def testBlank(self):
692 """Reject blank values."""
693 self.assertMessageRejected('\nTEST=\n')
694 self.assertMessageRejected('\nTEST= \n')
695
696 def testNotFirstLine(self):
697 """Reject the first line."""
698 self.assertMessageRejected('TEST=None\n\n\n')
699
700 def testNotInline(self):
701 """Reject not at the start of line."""
702 self.assertMessageRejected('\n TEST=None\n')
703 self.assertMessageRejected('\n\tTEST=None\n')
704
705 def testMissing(self):
706 """Reject commit messages w/no TEST line."""
707 self.assertMessageRejected('foo\n')
708
709 def testCase(self):
710 """Reject bug lines that are not TEST."""
711 self.assertMessageRejected('\ntest=none\n')
712
713
714class CheckCommitMessageChangeId(CommitMessageTestCase):
715 """Tests for _check_change_has_proper_changeid."""
716
717 @staticmethod
718 def CheckMessage(project, commit):
719 return pre_upload._check_change_has_proper_changeid(project, commit)
720
721 def testNormal(self):
722 """Accept a commit message w/a valid Change-Id."""
723 self.assertMessageAccepted('foo\n\nChange-Id: I1234\n')
724
725 def testBlank(self):
726 """Reject blank values."""
727 self.assertMessageRejected('\nChange-Id:\n')
728 self.assertMessageRejected('\nChange-Id: \n')
729
730 def testNotFirstLine(self):
731 """Reject the first line."""
732 self.assertMessageRejected('TEST=None\n\n\n')
733
734 def testNotInline(self):
735 """Reject not at the start of line."""
736 self.assertMessageRejected('\n Change-Id: I1234\n')
737 self.assertMessageRejected('\n\tChange-Id: I1234\n')
738
739 def testMissing(self):
740 """Reject commit messages missing the line."""
741 self.assertMessageRejected('foo\n')
742
743 def testCase(self):
744 """Reject bug lines that are not Change-Id."""
745 self.assertMessageRejected('\nchange-id: I1234\n')
746 self.assertMessageRejected('\nChange-id: I1234\n')
747 self.assertMessageRejected('\nChange-ID: I1234\n')
748
749 def testEnd(self):
750 """Reject Change-Id's that are not last."""
751 self.assertMessageRejected('\nChange-Id: I1234\nbar\n')
752
Mike Frysinger02b88bd2014-11-21 00:29:38 -0500753 def testSobTag(self):
754 """Permit s-o-b tags to follow the Change-Id."""
755 self.assertMessageAccepted('foo\n\nChange-Id: I1234\nSigned-off-by: Hi\n')
756
Mike Frysinger4a22bf02014-10-31 13:53:35 -0400757
Mike Frysinger36b2ebc2014-10-31 14:02:03 -0400758class CheckCommitMessageStyle(CommitMessageTestCase):
759 """Tests for _check_commit_message_style."""
760
761 @staticmethod
762 def CheckMessage(project, commit):
763 return pre_upload._check_commit_message_style(project, commit)
764
765 def testNormal(self):
766 """Accept valid commit messages."""
767 self.assertMessageAccepted('one sentence.\n')
768 self.assertMessageAccepted('some.module: do it!\n')
769 self.assertMessageAccepted('one line\n\nmore stuff here.')
770
771 def testNoBlankSecondLine(self):
772 """Reject messages that have stuff on the second line."""
773 self.assertMessageRejected('one sentence.\nbad fish!\n')
774
775 def testFirstLineMultipleSentences(self):
776 """Reject messages that have more than one sentence in the summary."""
777 self.assertMessageRejected('one sentence. two sentence!\n')
778
779 def testFirstLineTooLone(self):
780 """Reject first lines that are too long."""
781 self.assertMessageRejected('o' * 200)
782
783
Mike Frysinger292b45d2014-11-25 01:17:10 -0500784def DiffEntry(src_file=None, dst_file=None, src_mode=None, dst_mode='100644',
785 status='M'):
786 """Helper to create a stub RawDiffEntry object"""
787 if src_mode is None:
788 if status == 'A':
789 src_mode = '000000'
790 elif status == 'M':
791 src_mode = dst_mode
792 elif status == 'D':
793 src_mode = dst_mode
794 dst_mode = '000000'
795
796 src_sha = dst_sha = 'abc'
797 if status == 'D':
798 dst_sha = '000000'
799 elif status == 'A':
800 src_sha = '000000'
801
802 return git.RawDiffEntry(src_mode=src_mode, dst_mode=dst_mode, src_sha=src_sha,
803 dst_sha=dst_sha, status=status, score=None,
804 src_file=src_file, dst_file=dst_file)
805
806
Daniel Erate3ea3fc2015-02-13 15:27:52 -0700807class HelpersTest(cros_test_lib.MockTempDirTestCase):
Mike Frysingerd3bd32c2014-11-24 23:34:29 -0500808 """Various tests for utility functions."""
809
Daniel Erate3ea3fc2015-02-13 15:27:52 -0700810 def setUp(self):
811 self.orig_cwd = os.getcwd()
812 os.chdir(self.tempdir)
813
Mike Frysingerd3bd32c2014-11-24 23:34:29 -0500814 self.PatchObject(git, 'RawDiff', return_value=[
815 # A modified normal file.
Mike Frysinger292b45d2014-11-25 01:17:10 -0500816 DiffEntry(src_file='buildbot/constants.py', status='M'),
Mike Frysingerd3bd32c2014-11-24 23:34:29 -0500817 # A new symlink file.
Mike Frysinger292b45d2014-11-25 01:17:10 -0500818 DiffEntry(dst_file='scripts/cros_env_whitelist', dst_mode='120000',
819 status='A'),
Mike Frysingerd3bd32c2014-11-24 23:34:29 -0500820 # A deleted file.
Mike Frysinger292b45d2014-11-25 01:17:10 -0500821 DiffEntry(src_file='scripts/sync_sonic.py', status='D'),
Mike Frysingerd3bd32c2014-11-24 23:34:29 -0500822 ])
823
Daniel Erate3ea3fc2015-02-13 15:27:52 -0700824 def tearDown(self):
825 os.chdir(self.orig_cwd)
826
827 def _WritePresubmitIgnoreFile(self, subdir, data):
828 """Writes to a .presubmitignore file in the passed-in subdirectory."""
829 directory = os.path.join(self.tempdir, subdir)
830 if not os.path.exists(directory):
831 os.makedirs(directory)
832 osutils.WriteFile(os.path.join(directory, pre_upload._IGNORE_FILE), data)
833
Mike Frysingerd3bd32c2014-11-24 23:34:29 -0500834 def testGetAffectedFilesNoDeletesNoRelative(self):
835 """Verify _get_affected_files() works w/no delete & not relative."""
Mike Frysingerd3bd32c2014-11-24 23:34:29 -0500836 path = os.getcwd()
837 files = pre_upload._get_affected_files('HEAD', include_deletes=False,
838 relative=False)
839 exp_files = [os.path.join(path, 'buildbot/constants.py')]
840 self.assertEquals(files, exp_files)
841
842 def testGetAffectedFilesDeletesNoRelative(self):
843 """Verify _get_affected_files() works w/delete & not relative."""
Mike Frysingerd3bd32c2014-11-24 23:34:29 -0500844 path = os.getcwd()
845 files = pre_upload._get_affected_files('HEAD', include_deletes=True,
846 relative=False)
847 exp_files = [os.path.join(path, 'buildbot/constants.py'),
848 os.path.join(path, 'scripts/sync_sonic.py')]
849 self.assertEquals(files, exp_files)
850
851 def testGetAffectedFilesNoDeletesRelative(self):
852 """Verify _get_affected_files() works w/no delete & relative."""
Mike Frysingerd3bd32c2014-11-24 23:34:29 -0500853 files = pre_upload._get_affected_files('HEAD', include_deletes=False,
854 relative=True)
855 exp_files = ['buildbot/constants.py']
856 self.assertEquals(files, exp_files)
857
858 def testGetAffectedFilesDeletesRelative(self):
859 """Verify _get_affected_files() works w/delete & relative."""
Mike Frysingerd3bd32c2014-11-24 23:34:29 -0500860 files = pre_upload._get_affected_files('HEAD', include_deletes=True,
861 relative=True)
862 exp_files = ['buildbot/constants.py', 'scripts/sync_sonic.py']
863 self.assertEquals(files, exp_files)
864
Mike Frysinger292b45d2014-11-25 01:17:10 -0500865 def testGetAffectedFilesDetails(self):
866 """Verify _get_affected_files() works w/full_details."""
Mike Frysinger292b45d2014-11-25 01:17:10 -0500867 files = pre_upload._get_affected_files('HEAD', full_details=True,
868 relative=True)
869 self.assertEquals(files[0].src_file, 'buildbot/constants.py')
870
Daniel Erate3ea3fc2015-02-13 15:27:52 -0700871 def testGetAffectedFilesPresubmitIgnoreDirectory(self):
872 """Verify .presubmitignore can be used to exclude a directory."""
873 self._WritePresubmitIgnoreFile('.', 'buildbot/')
874 self.assertEquals(pre_upload._get_affected_files('HEAD', relative=True), [])
875
876 def testGetAffectedFilesPresubmitIgnoreDirectoryWildcard(self):
877 """Verify .presubmitignore can be used with a directory wildcard."""
878 self._WritePresubmitIgnoreFile('.', '*/constants.py')
879 self.assertEquals(pre_upload._get_affected_files('HEAD', relative=True), [])
880
881 def testGetAffectedFilesPresubmitIgnoreWithinDirectory(self):
882 """Verify .presubmitignore can be placed in a subdirectory."""
883 self._WritePresubmitIgnoreFile('buildbot', '*.py')
884 self.assertEquals(pre_upload._get_affected_files('HEAD', relative=True), [])
885
886 def testGetAffectedFilesPresubmitIgnoreDoesntMatch(self):
887 """Verify .presubmitignore has no effect when it doesn't match a file."""
888 self._WritePresubmitIgnoreFile('buildbot', '*.txt')
889 self.assertEquals(pre_upload._get_affected_files('HEAD', relative=True),
890 ['buildbot/constants.py'])
891
892 def testGetAffectedFilesPresubmitIgnoreAddedFile(self):
893 """Verify .presubmitignore matches added files."""
894 self._WritePresubmitIgnoreFile('.', 'buildbot/\nscripts/')
895 self.assertEquals(pre_upload._get_affected_files('HEAD', relative=True,
896 include_symlinks=True),
897 [])
898
899 def testGetAffectedFilesPresubmitIgnoreSkipIgnoreFile(self):
900 """Verify .presubmitignore files are automatically skipped."""
901 self.PatchObject(git, 'RawDiff', return_value=[
902 DiffEntry(src_file='.presubmitignore', status='M')
903 ])
904 self.assertEquals(pre_upload._get_affected_files('HEAD', relative=True), [])
Mike Frysinger292b45d2014-11-25 01:17:10 -0500905
906class CheckForUprev(cros_test_lib.MockTempDirTestCase):
907 """Tests for _check_for_uprev."""
908
909 def setUp(self):
910 self.file_mock = self.PatchObject(git, 'RawDiff')
911
912 def _Files(self, files):
913 """Create |files| in the tempdir and return full paths to them."""
914 for obj in files:
915 if obj.status == 'D':
916 continue
917 if obj.dst_file is None:
918 f = obj.src_file
919 else:
920 f = obj.dst_file
921 osutils.Touch(os.path.join(self.tempdir, f), makedirs=True)
922 return files
923
924 def assertAccepted(self, files, project='project', commit='fake sha1'):
925 """Assert _check_for_uprev accepts |files|."""
926 self.file_mock.return_value = self._Files(files)
927 ret = pre_upload._check_for_uprev(project, commit, project_top=self.tempdir)
928 self.assertEqual(ret, None)
929
930 def assertRejected(self, files, project='project', commit='fake sha1'):
931 """Assert _check_for_uprev rejects |files|."""
932 self.file_mock.return_value = self._Files(files)
933 ret = pre_upload._check_for_uprev(project, commit, project_top=self.tempdir)
934 self.assertTrue(isinstance(ret, errors.HookFailure))
935
936 def testWhitelistOverlay(self):
937 """Skip checks on whitelisted overlays."""
938 self.assertAccepted([DiffEntry(src_file='cat/pkg/pkg-0.ebuild')],
939 project='chromiumos/overlays/portage-stable')
940
941 def testWhitelistFiles(self):
942 """Skip checks on whitelisted files."""
943 files = ['ChangeLog', 'Manifest', 'metadata.xml']
944 self.assertAccepted([DiffEntry(src_file=os.path.join('c', 'p', x),
945 status='M')
946 for x in files])
947
948 def testRejectBasic(self):
949 """Reject ebuilds missing uprevs."""
950 self.assertRejected([DiffEntry(src_file='c/p/p-0.ebuild', status='M')])
951
952 def testNewPackage(self):
953 """Accept new ebuilds w/out uprevs."""
954 self.assertAccepted([DiffEntry(src_file='c/p/p-0.ebuild', status='A')])
955 self.assertAccepted([DiffEntry(src_file='c/p/p-0-r12.ebuild', status='A')])
956
957 def testModifiedFilesOnly(self):
958 """Reject ebuilds w/out uprevs and changes in files/."""
959 osutils.Touch(os.path.join(self.tempdir, 'cat/pkg/pkg-0.ebuild'),
960 makedirs=True)
961 self.assertRejected([DiffEntry(src_file='cat/pkg/files/f', status='A')])
962 self.assertRejected([DiffEntry(src_file='cat/pkg/files/g', status='M')])
963
964 def testFilesNoEbuilds(self):
965 """Ignore changes to paths w/out ebuilds."""
966 self.assertAccepted([DiffEntry(src_file='cat/pkg/files/f', status='A')])
967 self.assertAccepted([DiffEntry(src_file='cat/pkg/files/g', status='M')])
968
969 def testModifiedFilesWithUprev(self):
970 """Accept ebuilds w/uprevs and changes in files/."""
971 self.assertAccepted([DiffEntry(src_file='c/p/files/f', status='A'),
972 DiffEntry(src_file='c/p/p-0.ebuild', status='A')])
973 self.assertAccepted([
974 DiffEntry(src_file='c/p/files/f', status='M'),
975 DiffEntry(src_file='c/p/p-0-r1.ebuild', src_mode='120000',
976 dst_file='c/p/p-0-r2.ebuild', dst_mode='120000', status='R')])
977
Gwendal Grignoua3086c32014-12-09 11:17:22 -0800978 def testModifiedFilesWith9999(self):
979 """Accept 9999 ebuilds and changes in files/."""
980 self.assertAccepted([DiffEntry(src_file='c/p/files/f', status='M'),
981 DiffEntry(src_file='c/p/p-9999.ebuild', status='M')])
982
Mike Frysingerd3bd32c2014-11-24 23:34:29 -0500983
Mike Frysinger55f85b52014-12-18 14:45:21 -0500984class DirectMainTest(cros_test_lib.MockTempDirTestCase):
985 """Tests for direct_main()"""
986
987 def setUp(self):
988 self.hooks_mock = self.PatchObject(pre_upload, '_run_project_hooks',
989 return_value=None)
990
991 def testNoArgs(self):
992 """If run w/no args, should check the current dir."""
993 ret = pre_upload.direct_main([])
994 self.assertEqual(ret, 0)
995 self.hooks_mock.assert_called_once_with(
996 mock.ANY, proj_dir=os.getcwd(), commit_list=[], presubmit=mock.ANY)
997
998 def testExplicitDir(self):
999 """Verify we can run on a diff dir."""
1000 # Use the chromite dir since we know it exists.
1001 ret = pre_upload.direct_main(['--dir', constants.CHROMITE_DIR])
1002 self.assertEqual(ret, 0)
1003 self.hooks_mock.assert_called_once_with(
1004 mock.ANY, proj_dir=constants.CHROMITE_DIR, commit_list=[],
1005 presubmit=mock.ANY)
1006
1007 def testBogusProject(self):
1008 """A bogus project name should be fine (use default settings)."""
1009 # Use the chromite dir since we know it exists.
1010 ret = pre_upload.direct_main(['--dir', constants.CHROMITE_DIR,
1011 '--project', 'foooooooooo'])
1012 self.assertEqual(ret, 0)
1013 self.hooks_mock.assert_called_once_with(
1014 'foooooooooo', proj_dir=constants.CHROMITE_DIR, commit_list=[],
1015 presubmit=mock.ANY)
1016
1017 def testBogustProjectNoDir(self):
1018 """Make sure --dir is detected even with --project."""
1019 ret = pre_upload.direct_main(['--project', 'foooooooooo'])
1020 self.assertEqual(ret, 0)
1021 self.hooks_mock.assert_called_once_with(
1022 'foooooooooo', proj_dir=os.getcwd(), commit_list=[],
1023 presubmit=mock.ANY)
1024
1025 def testNoGitDir(self):
1026 """We should die when run on a non-git dir."""
1027 self.assertRaises(pre_upload.BadInvocation, pre_upload.direct_main,
1028 ['--dir', self.tempdir])
1029
1030 def testNoDir(self):
1031 """We should die when run on a missing dir."""
1032 self.assertRaises(pre_upload.BadInvocation, pre_upload.direct_main,
1033 ['--dir', os.path.join(self.tempdir, 'foooooooo')])
1034
1035 def testCommitList(self):
1036 """Any args on the command line should be treated as commits."""
1037 commits = ['sha1', 'sha2', 'shaaaaaaaaaaaan']
1038 ret = pre_upload.direct_main(commits)
1039 self.assertEqual(ret, 0)
1040 self.hooks_mock.assert_called_once_with(
1041 mock.ANY, proj_dir=mock.ANY, commit_list=commits, presubmit=mock.ANY)
1042
1043
Jon Salz98255932012-08-18 14:48:02 +08001044if __name__ == '__main__':
Mike Frysinger884a8dd2015-05-17 03:43:43 -04001045 cros_test_lib.main(module=__name__)