blob: 890a3e51f215f95f0543c185200f8563b0d9c67d [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
Alex Deymof5792ce2015-08-24 22:50:08 -0700451class CheckCrosLicenseCopyrightHeader(cros_test_lib.MockTestCase):
452 """Tests for _check_cros_license."""
Mike Frysinger98638102014-08-28 00:15:08 -0400453
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
Alex Deymof5792ce2015-08-24 22:50:08 -0700474 self.assertEqual(None, pre_upload._check_cros_license('proj', 'sha1'))
Mike Frysinger98638102014-08-28 00:15:08 -0400475
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
Alex Deymof5792ce2015-08-24 22:50:08 -0700491 self.assertNotEqual(None, pre_upload._check_cros_license('proj', 'sha1'))
492
493
494class CheckAOSPLicenseCopyrightHeader(cros_test_lib.MockTestCase):
495 """Tests for _check_aosp_license."""
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 testHeaders(self):
502 """Accept old header styles."""
503 HEADERS = (
504 """//
505// Copyright (C) 2011 The Android Open Source Project
506//
507// Licensed under the Apache License, Version 2.0 (the "License");
508// you may not use this file except in compliance with the License.
509// You may obtain a copy of the License at
510//
511// http://www.apache.org/licenses/LICENSE-2.0
512//
513// Unless required by applicable law or agreed to in writing, software
514// distributed under the License is distributed on an "AS IS" BASIS,
515// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
516// See the License for the specific language governing permissions and
517// limitations under the License.
518//
519""",
520 """#
521# Copyright (c) 2015 The Android Open Source Project
522#
523# Licensed under the Apache License, Version 2.0 (the "License");
524# you may not use this file except in compliance with the License.
525# You may obtain a copy of the License at
526#
527# http://www.apache.org/licenses/LICENSE-2.0
528#
529# Unless required by applicable law or agreed to in writing, software
530# distributed under the License is distributed on an "AS IS" BASIS,
531# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
532# See the License for the specific language governing permissions and
533# limitations under the License.
534#
535""",
536 )
537 self.file_mock.return_value = ['file']
538 for header in HEADERS:
539 self.content_mock.return_value = header
540 self.assertEqual(None, pre_upload._check_aosp_license('proj', 'sha1'))
541
542 def testRejectNoLinesAround(self):
543 """Reject headers missing the empty lines before/after the license."""
544 HEADERS = (
545 """# Copyright (c) 2015 The Android Open Source Project
546#
547# Licensed under the Apache License, Version 2.0 (the "License");
548# you may not use this file except in compliance with the License.
549# You may obtain a copy of the License at
550#
551# http://www.apache.org/licenses/LICENSE-2.0
552#
553# Unless required by applicable law or agreed to in writing, software
554# distributed under the License is distributed on an "AS IS" BASIS,
555# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
556# See the License for the specific language governing permissions and
557# limitations under the License.
558""",
559 )
560 self.file_mock.return_value = ['file']
561 for header in HEADERS:
562 self.content_mock.return_value = header
563 self.assertNotEqual(None, pre_upload._check_aosp_license('proj', 'sha1'))
Mike Frysinger98638102014-08-28 00:15:08 -0400564
565
Mike Frysingerd7734522015-02-26 16:12:43 -0500566class CheckLayoutConfTestCase(cros_test_lib.MockTestCase):
567 """Tests for _check_layout_conf."""
568
569 def setUp(self):
570 self.file_mock = self.PatchObject(pre_upload, '_get_affected_files')
571 self.content_mock = self.PatchObject(pre_upload, '_get_file_content')
572
573 def assertAccepted(self, files, project='project', commit='fake sha1'):
574 """Assert _check_layout_conf accepts |files|."""
575 self.file_mock.return_value = files
576 ret = pre_upload._check_layout_conf(project, commit)
577 self.assertEqual(ret, None, msg='rejected with:\n%s' % ret)
578
579 def assertRejected(self, files, project='project', commit='fake sha1'):
580 """Assert _check_layout_conf rejects |files|."""
581 self.file_mock.return_value = files
582 ret = pre_upload._check_layout_conf(project, commit)
583 self.assertTrue(isinstance(ret, errors.HookFailure))
584
585 def GetLayoutConf(self, filters=()):
586 """Return a valid layout.conf with |filters| lines removed."""
587 all_lines = [
588 'masters = portage-stable chromiumos',
589 'profile-formats = portage-2 profile-default-eapi',
590 'profile_eapi_when_unspecified = 5-progress',
591 'repo-name = link',
592 'thin-manifests = true',
593 'use-manifests = true',
594 ]
595
596 lines = []
597 for line in all_lines:
598 for filt in filters:
599 if line.startswith(filt):
600 break
601 else:
602 lines.append(line)
603
604 return '\n'.join(lines)
605
606 def testNoFilesToCheck(self):
607 """Don't blow up when there are no layout.conf files."""
608 self.assertAccepted([])
609
610 def testRejectRepoNameFile(self):
611 """If profiles/repo_name is set, kick it out."""
612 self.assertRejected(['profiles/repo_name'])
613
614 def testAcceptValidLayoutConf(self):
615 """Accept a fully valid layout.conf."""
616 self.content_mock.return_value = self.GetLayoutConf()
617 self.assertAccepted(['metadata/layout.conf'])
618
619 def testAcceptUnknownKeys(self):
620 """Accept keys we don't explicitly know about."""
621 self.content_mock.return_value = self.GetLayoutConf() + '\nzzz-top = ok'
622 self.assertAccepted(['metadata/layout.conf'])
623
624 def testRejectUnsorted(self):
625 """Reject an unsorted layout.conf."""
626 self.content_mock.return_value = 'zzz-top = bad\n' + self.GetLayoutConf()
627 self.assertRejected(['metadata/layout.conf'])
628
629 def testRejectMissingThinManifests(self):
630 """Reject a layout.conf missing thin-manifests."""
631 self.content_mock.return_value = self.GetLayoutConf(
632 filters=['thin-manifests'])
633 self.assertRejected(['metadata/layout.conf'])
634
635 def testRejectMissingUseManifests(self):
636 """Reject a layout.conf missing use-manifests."""
637 self.content_mock.return_value = self.GetLayoutConf(
638 filters=['use-manifests'])
639 self.assertRejected(['metadata/layout.conf'])
640
641 def testRejectMissingEapiFallback(self):
642 """Reject a layout.conf missing profile_eapi_when_unspecified."""
643 self.content_mock.return_value = self.GetLayoutConf(
644 filters=['profile_eapi_when_unspecified'])
645 self.assertRejected(['metadata/layout.conf'])
646
647 def testRejectMissingRepoName(self):
648 """Reject a layout.conf missing repo-name."""
649 self.content_mock.return_value = self.GetLayoutConf(filters=['repo-name'])
650 self.assertRejected(['metadata/layout.conf'])
651
652
Mike Frysinger4a22bf02014-10-31 13:53:35 -0400653class CommitMessageTestCase(cros_test_lib.MockTestCase):
654 """Test case for funcs that check commit messages."""
655
656 def setUp(self):
657 self.msg_mock = self.PatchObject(pre_upload, '_get_commit_desc')
658
659 @staticmethod
660 def CheckMessage(_project, _commit):
661 raise AssertionError('Test class must declare CheckMessage')
662 # This dummy return is to silence pylint warning W1111 so we don't have to
663 # enable it for all the call sites below.
664 return 1 # pylint: disable=W0101
665
666 def assertMessageAccepted(self, msg, project='project', commit='1234'):
667 """Assert _check_change_has_bug_field accepts |msg|."""
668 self.msg_mock.return_value = msg
669 ret = self.CheckMessage(project, commit)
670 self.assertEqual(ret, None)
671
672 def assertMessageRejected(self, msg, project='project', commit='1234'):
673 """Assert _check_change_has_bug_field rejects |msg|."""
674 self.msg_mock.return_value = msg
675 ret = self.CheckMessage(project, commit)
676 self.assertTrue(isinstance(ret, errors.HookFailure))
677
678
679class CheckCommitMessageBug(CommitMessageTestCase):
680 """Tests for _check_change_has_bug_field."""
681
682 @staticmethod
683 def CheckMessage(project, commit):
684 return pre_upload._check_change_has_bug_field(project, commit)
685
686 def testNormal(self):
687 """Accept a commit message w/a valid BUG."""
688 self.assertMessageAccepted('\nBUG=chromium:1234\n')
689 self.assertMessageAccepted('\nBUG=chrome-os-partner:1234\n')
Stefan Sauerd74ad562015-03-10 10:14:28 +0100690 self.assertMessageAccepted('\nBUG=b:1234\n')
Mike Frysinger4a22bf02014-10-31 13:53:35 -0400691
692 def testNone(self):
693 """Accept BUG=None."""
694 self.assertMessageAccepted('\nBUG=None\n')
695 self.assertMessageAccepted('\nBUG=none\n')
696 self.assertMessageRejected('\nBUG=NONE\n')
697
698 def testBlank(self):
699 """Reject blank values."""
700 self.assertMessageRejected('\nBUG=\n')
701 self.assertMessageRejected('\nBUG= \n')
702
703 def testNotFirstLine(self):
704 """Reject the first line."""
705 self.assertMessageRejected('BUG=None\n\n\n')
706
707 def testNotInline(self):
708 """Reject not at the start of line."""
709 self.assertMessageRejected('\n BUG=None\n')
710 self.assertMessageRejected('\n\tBUG=None\n')
711
712 def testOldTrackers(self):
713 """Reject commit messages using old trackers."""
714 self.assertMessageRejected('\nBUG=chromium-os:1234\n')
715
716 def testNoTrackers(self):
717 """Reject commit messages w/invalid trackers."""
718 self.assertMessageRejected('\nBUG=booga:1234\n')
Stefan Sauerd74ad562015-03-10 10:14:28 +0100719 self.assertMessageRejected('\nBUG=br:1234\n')
Mike Frysinger4a22bf02014-10-31 13:53:35 -0400720
721 def testMissing(self):
722 """Reject commit messages w/no BUG line."""
723 self.assertMessageRejected('foo\n')
724
725 def testCase(self):
726 """Reject bug lines that are not BUG."""
727 self.assertMessageRejected('\nbug=none\n')
728
729
730class CheckCommitMessageCqDepend(CommitMessageTestCase):
731 """Tests for _check_change_has_valid_cq_depend."""
732
733 @staticmethod
734 def CheckMessage(project, commit):
735 return pre_upload._check_change_has_valid_cq_depend(project, commit)
736
737 def testNormal(self):
738 """Accept valid CQ-DEPENDs line."""
739 self.assertMessageAccepted('\nCQ-DEPEND=CL:1234\n')
740
741 def testInvalid(self):
742 """Reject invalid CQ-DEPENDs line."""
743 self.assertMessageRejected('\nCQ-DEPEND=CL=1234\n')
744 self.assertMessageRejected('\nCQ-DEPEND=None\n')
745
746
747class CheckCommitMessageTest(CommitMessageTestCase):
748 """Tests for _check_change_has_test_field."""
749
750 @staticmethod
751 def CheckMessage(project, commit):
752 return pre_upload._check_change_has_test_field(project, commit)
753
754 def testNormal(self):
755 """Accept a commit message w/a valid TEST."""
756 self.assertMessageAccepted('\nTEST=i did it\n')
757
758 def testNone(self):
759 """Accept TEST=None."""
760 self.assertMessageAccepted('\nTEST=None\n')
761 self.assertMessageAccepted('\nTEST=none\n')
762
763 def testBlank(self):
764 """Reject blank values."""
765 self.assertMessageRejected('\nTEST=\n')
766 self.assertMessageRejected('\nTEST= \n')
767
768 def testNotFirstLine(self):
769 """Reject the first line."""
770 self.assertMessageRejected('TEST=None\n\n\n')
771
772 def testNotInline(self):
773 """Reject not at the start of line."""
774 self.assertMessageRejected('\n TEST=None\n')
775 self.assertMessageRejected('\n\tTEST=None\n')
776
777 def testMissing(self):
778 """Reject commit messages w/no TEST line."""
779 self.assertMessageRejected('foo\n')
780
781 def testCase(self):
782 """Reject bug lines that are not TEST."""
783 self.assertMessageRejected('\ntest=none\n')
784
785
786class CheckCommitMessageChangeId(CommitMessageTestCase):
787 """Tests for _check_change_has_proper_changeid."""
788
789 @staticmethod
790 def CheckMessage(project, commit):
791 return pre_upload._check_change_has_proper_changeid(project, commit)
792
793 def testNormal(self):
794 """Accept a commit message w/a valid Change-Id."""
795 self.assertMessageAccepted('foo\n\nChange-Id: I1234\n')
796
797 def testBlank(self):
798 """Reject blank values."""
799 self.assertMessageRejected('\nChange-Id:\n')
800 self.assertMessageRejected('\nChange-Id: \n')
801
802 def testNotFirstLine(self):
803 """Reject the first line."""
804 self.assertMessageRejected('TEST=None\n\n\n')
805
806 def testNotInline(self):
807 """Reject not at the start of line."""
808 self.assertMessageRejected('\n Change-Id: I1234\n')
809 self.assertMessageRejected('\n\tChange-Id: I1234\n')
810
811 def testMissing(self):
812 """Reject commit messages missing the line."""
813 self.assertMessageRejected('foo\n')
814
815 def testCase(self):
816 """Reject bug lines that are not Change-Id."""
817 self.assertMessageRejected('\nchange-id: I1234\n')
818 self.assertMessageRejected('\nChange-id: I1234\n')
819 self.assertMessageRejected('\nChange-ID: I1234\n')
820
821 def testEnd(self):
822 """Reject Change-Id's that are not last."""
823 self.assertMessageRejected('\nChange-Id: I1234\nbar\n')
824
Mike Frysinger02b88bd2014-11-21 00:29:38 -0500825 def testSobTag(self):
826 """Permit s-o-b tags to follow the Change-Id."""
827 self.assertMessageAccepted('foo\n\nChange-Id: I1234\nSigned-off-by: Hi\n')
828
Mike Frysinger4a22bf02014-10-31 13:53:35 -0400829
Mike Frysinger36b2ebc2014-10-31 14:02:03 -0400830class CheckCommitMessageStyle(CommitMessageTestCase):
831 """Tests for _check_commit_message_style."""
832
833 @staticmethod
834 def CheckMessage(project, commit):
835 return pre_upload._check_commit_message_style(project, commit)
836
837 def testNormal(self):
838 """Accept valid commit messages."""
839 self.assertMessageAccepted('one sentence.\n')
840 self.assertMessageAccepted('some.module: do it!\n')
841 self.assertMessageAccepted('one line\n\nmore stuff here.')
842
843 def testNoBlankSecondLine(self):
844 """Reject messages that have stuff on the second line."""
845 self.assertMessageRejected('one sentence.\nbad fish!\n')
846
847 def testFirstLineMultipleSentences(self):
848 """Reject messages that have more than one sentence in the summary."""
849 self.assertMessageRejected('one sentence. two sentence!\n')
850
851 def testFirstLineTooLone(self):
852 """Reject first lines that are too long."""
853 self.assertMessageRejected('o' * 200)
854
855
Mike Frysinger292b45d2014-11-25 01:17:10 -0500856def DiffEntry(src_file=None, dst_file=None, src_mode=None, dst_mode='100644',
857 status='M'):
858 """Helper to create a stub RawDiffEntry object"""
859 if src_mode is None:
860 if status == 'A':
861 src_mode = '000000'
862 elif status == 'M':
863 src_mode = dst_mode
864 elif status == 'D':
865 src_mode = dst_mode
866 dst_mode = '000000'
867
868 src_sha = dst_sha = 'abc'
869 if status == 'D':
870 dst_sha = '000000'
871 elif status == 'A':
872 src_sha = '000000'
873
874 return git.RawDiffEntry(src_mode=src_mode, dst_mode=dst_mode, src_sha=src_sha,
875 dst_sha=dst_sha, status=status, score=None,
876 src_file=src_file, dst_file=dst_file)
877
878
Daniel Erate3ea3fc2015-02-13 15:27:52 -0700879class HelpersTest(cros_test_lib.MockTempDirTestCase):
Mike Frysingerd3bd32c2014-11-24 23:34:29 -0500880 """Various tests for utility functions."""
881
Daniel Erate3ea3fc2015-02-13 15:27:52 -0700882 def setUp(self):
883 self.orig_cwd = os.getcwd()
884 os.chdir(self.tempdir)
885
Mike Frysingerd3bd32c2014-11-24 23:34:29 -0500886 self.PatchObject(git, 'RawDiff', return_value=[
887 # A modified normal file.
Mike Frysinger292b45d2014-11-25 01:17:10 -0500888 DiffEntry(src_file='buildbot/constants.py', status='M'),
Mike Frysingerd3bd32c2014-11-24 23:34:29 -0500889 # A new symlink file.
Mike Frysinger292b45d2014-11-25 01:17:10 -0500890 DiffEntry(dst_file='scripts/cros_env_whitelist', dst_mode='120000',
891 status='A'),
Mike Frysingerd3bd32c2014-11-24 23:34:29 -0500892 # A deleted file.
Mike Frysinger292b45d2014-11-25 01:17:10 -0500893 DiffEntry(src_file='scripts/sync_sonic.py', status='D'),
Mike Frysingerd3bd32c2014-11-24 23:34:29 -0500894 ])
895
Daniel Erate3ea3fc2015-02-13 15:27:52 -0700896 def tearDown(self):
897 os.chdir(self.orig_cwd)
898
899 def _WritePresubmitIgnoreFile(self, subdir, data):
900 """Writes to a .presubmitignore file in the passed-in subdirectory."""
901 directory = os.path.join(self.tempdir, subdir)
902 if not os.path.exists(directory):
903 os.makedirs(directory)
904 osutils.WriteFile(os.path.join(directory, pre_upload._IGNORE_FILE), data)
905
Mike Frysingerd3bd32c2014-11-24 23:34:29 -0500906 def testGetAffectedFilesNoDeletesNoRelative(self):
907 """Verify _get_affected_files() works w/no delete & not relative."""
Mike Frysingerd3bd32c2014-11-24 23:34:29 -0500908 path = os.getcwd()
909 files = pre_upload._get_affected_files('HEAD', include_deletes=False,
910 relative=False)
911 exp_files = [os.path.join(path, 'buildbot/constants.py')]
912 self.assertEquals(files, exp_files)
913
914 def testGetAffectedFilesDeletesNoRelative(self):
915 """Verify _get_affected_files() works w/delete & not relative."""
Mike Frysingerd3bd32c2014-11-24 23:34:29 -0500916 path = os.getcwd()
917 files = pre_upload._get_affected_files('HEAD', include_deletes=True,
918 relative=False)
919 exp_files = [os.path.join(path, 'buildbot/constants.py'),
920 os.path.join(path, 'scripts/sync_sonic.py')]
921 self.assertEquals(files, exp_files)
922
923 def testGetAffectedFilesNoDeletesRelative(self):
924 """Verify _get_affected_files() works w/no delete & relative."""
Mike Frysingerd3bd32c2014-11-24 23:34:29 -0500925 files = pre_upload._get_affected_files('HEAD', include_deletes=False,
926 relative=True)
927 exp_files = ['buildbot/constants.py']
928 self.assertEquals(files, exp_files)
929
930 def testGetAffectedFilesDeletesRelative(self):
931 """Verify _get_affected_files() works w/delete & relative."""
Mike Frysingerd3bd32c2014-11-24 23:34:29 -0500932 files = pre_upload._get_affected_files('HEAD', include_deletes=True,
933 relative=True)
934 exp_files = ['buildbot/constants.py', 'scripts/sync_sonic.py']
935 self.assertEquals(files, exp_files)
936
Mike Frysinger292b45d2014-11-25 01:17:10 -0500937 def testGetAffectedFilesDetails(self):
938 """Verify _get_affected_files() works w/full_details."""
Mike Frysinger292b45d2014-11-25 01:17:10 -0500939 files = pre_upload._get_affected_files('HEAD', full_details=True,
940 relative=True)
941 self.assertEquals(files[0].src_file, 'buildbot/constants.py')
942
Daniel Erate3ea3fc2015-02-13 15:27:52 -0700943 def testGetAffectedFilesPresubmitIgnoreDirectory(self):
944 """Verify .presubmitignore can be used to exclude a directory."""
945 self._WritePresubmitIgnoreFile('.', 'buildbot/')
946 self.assertEquals(pre_upload._get_affected_files('HEAD', relative=True), [])
947
948 def testGetAffectedFilesPresubmitIgnoreDirectoryWildcard(self):
949 """Verify .presubmitignore can be used with a directory wildcard."""
950 self._WritePresubmitIgnoreFile('.', '*/constants.py')
951 self.assertEquals(pre_upload._get_affected_files('HEAD', relative=True), [])
952
953 def testGetAffectedFilesPresubmitIgnoreWithinDirectory(self):
954 """Verify .presubmitignore can be placed in a subdirectory."""
955 self._WritePresubmitIgnoreFile('buildbot', '*.py')
956 self.assertEquals(pre_upload._get_affected_files('HEAD', relative=True), [])
957
958 def testGetAffectedFilesPresubmitIgnoreDoesntMatch(self):
959 """Verify .presubmitignore has no effect when it doesn't match a file."""
960 self._WritePresubmitIgnoreFile('buildbot', '*.txt')
961 self.assertEquals(pre_upload._get_affected_files('HEAD', relative=True),
962 ['buildbot/constants.py'])
963
964 def testGetAffectedFilesPresubmitIgnoreAddedFile(self):
965 """Verify .presubmitignore matches added files."""
966 self._WritePresubmitIgnoreFile('.', 'buildbot/\nscripts/')
967 self.assertEquals(pre_upload._get_affected_files('HEAD', relative=True,
968 include_symlinks=True),
969 [])
970
971 def testGetAffectedFilesPresubmitIgnoreSkipIgnoreFile(self):
972 """Verify .presubmitignore files are automatically skipped."""
973 self.PatchObject(git, 'RawDiff', return_value=[
974 DiffEntry(src_file='.presubmitignore', status='M')
975 ])
976 self.assertEquals(pre_upload._get_affected_files('HEAD', relative=True), [])
Mike Frysinger292b45d2014-11-25 01:17:10 -0500977
978class CheckForUprev(cros_test_lib.MockTempDirTestCase):
979 """Tests for _check_for_uprev."""
980
981 def setUp(self):
982 self.file_mock = self.PatchObject(git, 'RawDiff')
983
984 def _Files(self, files):
985 """Create |files| in the tempdir and return full paths to them."""
986 for obj in files:
987 if obj.status == 'D':
988 continue
989 if obj.dst_file is None:
990 f = obj.src_file
991 else:
992 f = obj.dst_file
993 osutils.Touch(os.path.join(self.tempdir, f), makedirs=True)
994 return files
995
996 def assertAccepted(self, files, project='project', commit='fake sha1'):
997 """Assert _check_for_uprev accepts |files|."""
998 self.file_mock.return_value = self._Files(files)
999 ret = pre_upload._check_for_uprev(project, commit, project_top=self.tempdir)
1000 self.assertEqual(ret, None)
1001
1002 def assertRejected(self, files, project='project', commit='fake sha1'):
1003 """Assert _check_for_uprev rejects |files|."""
1004 self.file_mock.return_value = self._Files(files)
1005 ret = pre_upload._check_for_uprev(project, commit, project_top=self.tempdir)
1006 self.assertTrue(isinstance(ret, errors.HookFailure))
1007
1008 def testWhitelistOverlay(self):
1009 """Skip checks on whitelisted overlays."""
1010 self.assertAccepted([DiffEntry(src_file='cat/pkg/pkg-0.ebuild')],
1011 project='chromiumos/overlays/portage-stable')
1012
1013 def testWhitelistFiles(self):
1014 """Skip checks on whitelisted files."""
1015 files = ['ChangeLog', 'Manifest', 'metadata.xml']
1016 self.assertAccepted([DiffEntry(src_file=os.path.join('c', 'p', x),
1017 status='M')
1018 for x in files])
1019
1020 def testRejectBasic(self):
1021 """Reject ebuilds missing uprevs."""
1022 self.assertRejected([DiffEntry(src_file='c/p/p-0.ebuild', status='M')])
1023
1024 def testNewPackage(self):
1025 """Accept new ebuilds w/out uprevs."""
1026 self.assertAccepted([DiffEntry(src_file='c/p/p-0.ebuild', status='A')])
1027 self.assertAccepted([DiffEntry(src_file='c/p/p-0-r12.ebuild', status='A')])
1028
1029 def testModifiedFilesOnly(self):
1030 """Reject ebuilds w/out uprevs and changes in files/."""
1031 osutils.Touch(os.path.join(self.tempdir, 'cat/pkg/pkg-0.ebuild'),
1032 makedirs=True)
1033 self.assertRejected([DiffEntry(src_file='cat/pkg/files/f', status='A')])
1034 self.assertRejected([DiffEntry(src_file='cat/pkg/files/g', status='M')])
1035
1036 def testFilesNoEbuilds(self):
1037 """Ignore changes to paths w/out ebuilds."""
1038 self.assertAccepted([DiffEntry(src_file='cat/pkg/files/f', status='A')])
1039 self.assertAccepted([DiffEntry(src_file='cat/pkg/files/g', status='M')])
1040
1041 def testModifiedFilesWithUprev(self):
1042 """Accept ebuilds w/uprevs and changes in files/."""
1043 self.assertAccepted([DiffEntry(src_file='c/p/files/f', status='A'),
1044 DiffEntry(src_file='c/p/p-0.ebuild', status='A')])
1045 self.assertAccepted([
1046 DiffEntry(src_file='c/p/files/f', status='M'),
1047 DiffEntry(src_file='c/p/p-0-r1.ebuild', src_mode='120000',
1048 dst_file='c/p/p-0-r2.ebuild', dst_mode='120000', status='R')])
1049
Gwendal Grignoua3086c32014-12-09 11:17:22 -08001050 def testModifiedFilesWith9999(self):
1051 """Accept 9999 ebuilds and changes in files/."""
1052 self.assertAccepted([DiffEntry(src_file='c/p/files/f', status='M'),
1053 DiffEntry(src_file='c/p/p-9999.ebuild', status='M')])
1054
Mike Frysingerd3bd32c2014-11-24 23:34:29 -05001055
Mike Frysinger55f85b52014-12-18 14:45:21 -05001056class DirectMainTest(cros_test_lib.MockTempDirTestCase):
1057 """Tests for direct_main()"""
1058
1059 def setUp(self):
1060 self.hooks_mock = self.PatchObject(pre_upload, '_run_project_hooks',
1061 return_value=None)
1062
1063 def testNoArgs(self):
1064 """If run w/no args, should check the current dir."""
1065 ret = pre_upload.direct_main([])
1066 self.assertEqual(ret, 0)
1067 self.hooks_mock.assert_called_once_with(
1068 mock.ANY, proj_dir=os.getcwd(), commit_list=[], presubmit=mock.ANY)
1069
1070 def testExplicitDir(self):
1071 """Verify we can run on a diff dir."""
1072 # Use the chromite dir since we know it exists.
1073 ret = pre_upload.direct_main(['--dir', constants.CHROMITE_DIR])
1074 self.assertEqual(ret, 0)
1075 self.hooks_mock.assert_called_once_with(
1076 mock.ANY, proj_dir=constants.CHROMITE_DIR, commit_list=[],
1077 presubmit=mock.ANY)
1078
1079 def testBogusProject(self):
1080 """A bogus project name should be fine (use default settings)."""
1081 # Use the chromite dir since we know it exists.
1082 ret = pre_upload.direct_main(['--dir', constants.CHROMITE_DIR,
1083 '--project', 'foooooooooo'])
1084 self.assertEqual(ret, 0)
1085 self.hooks_mock.assert_called_once_with(
1086 'foooooooooo', proj_dir=constants.CHROMITE_DIR, commit_list=[],
1087 presubmit=mock.ANY)
1088
1089 def testBogustProjectNoDir(self):
1090 """Make sure --dir is detected even with --project."""
1091 ret = pre_upload.direct_main(['--project', 'foooooooooo'])
1092 self.assertEqual(ret, 0)
1093 self.hooks_mock.assert_called_once_with(
1094 'foooooooooo', proj_dir=os.getcwd(), commit_list=[],
1095 presubmit=mock.ANY)
1096
1097 def testNoGitDir(self):
1098 """We should die when run on a non-git dir."""
1099 self.assertRaises(pre_upload.BadInvocation, pre_upload.direct_main,
1100 ['--dir', self.tempdir])
1101
1102 def testNoDir(self):
1103 """We should die when run on a missing dir."""
1104 self.assertRaises(pre_upload.BadInvocation, pre_upload.direct_main,
1105 ['--dir', os.path.join(self.tempdir, 'foooooooo')])
1106
1107 def testCommitList(self):
1108 """Any args on the command line should be treated as commits."""
1109 commits = ['sha1', 'sha2', 'shaaaaaaaaaaaan']
1110 ret = pre_upload.direct_main(commits)
1111 self.assertEqual(ret, 0)
1112 self.hooks_mock.assert_called_once_with(
1113 mock.ANY, proj_dir=mock.ANY, commit_list=commits, presubmit=mock.ANY)
1114
1115
Jon Salz98255932012-08-18 14:48:02 +08001116if __name__ == '__main__':
Mike Frysinger884a8dd2015-05-17 03:43:43 -04001117 cros_test_lib.main(module=__name__)