blob: 13135d7d5005a324de2afc0987967529cb6198e0 [file] [log] [blame]
Mike Frysinger4f994402019-09-13 17:40:45 -04001#!/usr/bin/env python3
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
Keigo Oka7e880ac2019-07-03 15:03:43 +090011import datetime
David Jamesc3b68b32013-04-03 09:17:03 -070012import os
13import sys
Jon Salz98255932012-08-18 14:48:02 +080014
Mike Frysingerfd481ce2019-09-13 18:14:48 -040015import mock
16
Mike Frysingerbf8b91c2014-02-01 02:50:27 -050017import errors
18
Jon Salz98255932012-08-18 14:48:02 +080019# pylint: disable=W0212
Mike Frysinger65d93c12014-02-01 02:59:46 -050020# We access private members of the pre_upload module all over the place.
21
Mike Frysinger55f85b52014-12-18 14:45:21 -050022# Make sure we can find the chromite paths.
23sys.path.insert(0, os.path.join(os.path.dirname(os.path.realpath(__file__)),
24 '..', '..'))
Jon Salz98255932012-08-18 14:48:02 +080025
Mike Frysingerfd481ce2019-09-13 18:14:48 -040026# The sys.path monkey patching confuses the linter.
27# pylint: disable=wrong-import-position
Mike Frysinger71e643e2019-09-13 17:26:39 -040028from chromite.lib import constants
29from chromite.lib import cros_build_lib
Mike Frysinger65d93c12014-02-01 02:59:46 -050030from chromite.lib import cros_test_lib
Mike Frysingerd3bd32c2014-11-24 23:34:29 -050031from chromite.lib import git
Daniel Erata350fd32014-09-29 14:02:34 -070032from chromite.lib import osutils
Mike Frysinger65d93c12014-02-01 02:59:46 -050033
34
Jon Salz98255932012-08-18 14:48:02 +080035pre_upload = __import__('pre-upload')
36
37
Alex Deymo643ac4c2015-09-03 10:40:50 -070038def ProjectNamed(project_name):
39 """Wrapper to create a Project with just the name"""
40 return pre_upload.Project(project_name, None, None)
41
42
Mike Frysingerb2496652019-09-12 23:35:46 -040043class PreUploadTestCase(cros_test_lib.MockTestCase):
44 """Common test case base."""
45
46 def setUp(self):
47 pre_upload.CACHE.clear()
48
49
50class TryUTF8DecodeTest(PreUploadTestCase):
Mike Frysingerae409522014-02-01 03:16:11 -050051 """Verify we sanely handle unicode content."""
52
Mike Frysinger71e643e2019-09-13 17:26:39 -040053 def setUp(self):
Mike Frysinger7bb709f2019-09-29 23:20:12 -040054 self.rc_mock = self.PatchObject(cros_build_lib, 'run')
Mike Frysinger71e643e2019-09-13 17:26:39 -040055
56 def _run(self, content):
57 """Helper for round tripping through _run_command."""
58 self.rc_mock.return_value = cros_build_lib.CommandResult(
59 output=content, returncode=0)
60 return pre_upload._run_command([])
61
62 def testEmpty(self):
63 """Check empty output."""
64 ret = self._run(b'')
65 self.assertEqual('', ret)
66
67 if sys.version_info.major < 3:
68 ret = self._run('')
69 self.assertEqual(u'', ret)
70
71 def testAscii(self):
72 """Check ascii output."""
73 ret = self._run(b'abc')
74 self.assertEqual('abc', ret)
75
76 if sys.version_info.major < 3:
77 ret = self._run('abc')
78 self.assertEqual(u'abc', ret)
79
80 def testUtf8(self):
81 """Check valid UTF-8 output."""
82 text = u'你好布萊恩'
83 ret = self._run(text.encode('utf-8'))
84 self.assertEqual(text, ret)
85
86 def testBinary(self):
87 """Check binary (invalid UTF-8) output."""
88 ret = self._run(b'hi \x80 there')
Mike Frysinger8a4e8942019-09-16 23:43:49 -040089 self.assertEqual(u'hi \ufffd there', ret)
Jon Salz98255932012-08-18 14:48:02 +080090
91
Mike Frysingerb2496652019-09-12 23:35:46 -040092class CheckNoLongLinesTest(PreUploadTestCase):
Mike Frysingerae409522014-02-01 03:16:11 -050093 """Tests for _check_no_long_lines."""
94
Jon Salz98255932012-08-18 14:48:02 +080095 def setUp(self):
Mike Frysinger1459d362014-12-06 13:53:23 -050096 self.diff_mock = self.PatchObject(pre_upload, '_get_file_diff')
Jon Salz98255932012-08-18 14:48:02 +080097
Shuhei Takahashiabc20f32017-07-10 19:35:45 +090098 def testCheck(self):
99 self.PatchObject(pre_upload, '_get_affected_files', return_value=['x.py'])
Mike Frysinger1459d362014-12-06 13:53:23 -0500100 self.diff_mock.return_value = [
Mike Frysinger24dd3c52019-08-17 14:22:48 -0400101 (1, u'x' * 80), # OK
102 (2, '\x80' * 80), # OK
103 (3, u'x' * 81), # Too long
104 (4, '\x80' * 81), # Too long
105 (5, u'See http://' + (u'x' * 80)), # OK (URL)
106 (6, u'See https://' + (u'x' * 80)), # OK (URL)
107 (7, u'# define ' + (u'x' * 80)), # OK (compiler directive)
108 (8, u'#define' + (u'x' * 74)), # Too long
Mike Frysinger1459d362014-12-06 13:53:23 -0500109 ]
Alex Deymo643ac4c2015-09-03 10:40:50 -0700110 failure = pre_upload._check_no_long_lines(ProjectNamed('PROJECT'), 'COMMIT')
Jon Salz98255932012-08-18 14:48:02 +0800111 self.assertTrue(failure)
Ayato Tokubi5aae3f72020-01-16 17:43:47 +0900112 self.assertEqual('Found lines longer than the limit (first 5 shown):',
Mike Frysinger8a4e8942019-09-16 23:43:49 -0400113 failure.msg)
Ayato Tokubi5aae3f72020-01-16 17:43:47 +0900114 self.assertEqual(['x.py, line %d, 81 chars, over 80 chars' %
115 x for x in [3, 4, 8]],
Mike Frysinger8a4e8942019-09-16 23:43:49 -0400116 failure.items)
Jon Salz98255932012-08-18 14:48:02 +0800117
George Burgess IVf9f79eb2019-07-09 20:12:55 -0700118 def testCheckTreatsOwnersFilesSpecially(self):
119 affected_files = self.PatchObject(pre_upload, '_get_affected_files')
120
121 mock_files = (
122 ('foo-OWNERS', False),
123 ('OWNERS', True),
124 ('/path/to/OWNERS', True),
125 ('/path/to/OWNERS.foo', True),
126 )
127
128 mock_lines = (
129 (u'x' * 81, False),
130 (u'foo file:' + u'x' * 80, True),
131 (u'include ' + u'x' * 80, True),
132 )
133 assert all(len(line) > 80 for line, _ in mock_lines)
134
135 for file_name, is_owners in mock_files:
136 affected_files.return_value = [file_name]
137 for line, is_ok in mock_lines:
138 self.diff_mock.return_value = [(1, line)]
139 failure = pre_upload._check_no_long_lines(ProjectNamed('PROJECT'),
140 'COMMIT')
141
142 assert_msg = 'file: %r; line: %r' % (file_name, line)
143 if is_owners and is_ok:
144 self.assertFalse(failure, assert_msg)
145 else:
146 self.assertTrue(failure, assert_msg)
Ayato Tokubi5aae3f72020-01-16 17:43:47 +0900147 self.assertIn('Found lines longer than the limit', failure.msg,
George Burgess IVf9f79eb2019-07-09 20:12:55 -0700148 assert_msg)
149
Shuhei Takahashiabc20f32017-07-10 19:35:45 +0900150 def testIncludeOptions(self):
151 self.PatchObject(pre_upload,
152 '_get_affected_files',
153 return_value=['foo.txt'])
Mike Frysinger24dd3c52019-08-17 14:22:48 -0400154 self.diff_mock.return_value = [(1, u'x' * 81)]
Shuhei Takahashiabc20f32017-07-10 19:35:45 +0900155 self.assertFalse(pre_upload._check_no_long_lines(
156 ProjectNamed('PROJECT'), 'COMMIT'))
157 self.assertTrue(pre_upload._check_no_long_lines(
158 ProjectNamed('PROJECT'), 'COMMIT', options=['--include_regex=foo']))
159
160 def testExcludeOptions(self):
161 self.PatchObject(pre_upload,
162 '_get_affected_files',
163 return_value=['foo.py'])
Mike Frysinger24dd3c52019-08-17 14:22:48 -0400164 self.diff_mock.return_value = [(1, u'x' * 81)]
Shuhei Takahashiabc20f32017-07-10 19:35:45 +0900165 self.assertTrue(pre_upload._check_no_long_lines(
166 ProjectNamed('PROJECT'), 'COMMIT'))
167 self.assertFalse(pre_upload._check_no_long_lines(
168 ProjectNamed('PROJECT'), 'COMMIT', options=['--exclude_regex=foo']))
169
Ayato Tokubi5aae3f72020-01-16 17:43:47 +0900170 def testSpecialLineLength(self):
171 mock_lines = (
172 (u'x' * 101, True),
173 (u'x' * 100, False),
174 (u'x' * 81, False),
175 (u'x' * 80, False),
176 )
177 self.PatchObject(pre_upload,
178 '_get_affected_files',
179 return_value=['foo.java'])
180 for line, is_ok in mock_lines:
181 self.diff_mock.return_value = [(1, line)]
182 if is_ok:
183 self.assertTrue(pre_upload._check_no_long_lines(
184 ProjectNamed('PROJECT'), 'COMMIT'))
185 else:
186 self.assertFalse(pre_upload._check_no_long_lines(
187 ProjectNamed('PROJECT'), 'COMMIT'))
188
Mike Frysingerae409522014-02-01 03:16:11 -0500189
Mike Frysingerb2496652019-09-12 23:35:46 -0400190class CheckTabbedIndentsTest(PreUploadTestCase):
Prathmesh Prabhuc5254652016-12-22 12:58:05 -0800191 """Tests for _check_tabbed_indents."""
Mike Frysingerb2496652019-09-12 23:35:46 -0400192
Prathmesh Prabhuc5254652016-12-22 12:58:05 -0800193 def setUp(self):
Shuhei Takahashiabc20f32017-07-10 19:35:45 +0900194 self.PatchObject(pre_upload,
195 '_get_affected_files',
196 return_value=['x.ebuild'])
Prathmesh Prabhuc5254652016-12-22 12:58:05 -0800197 self.diff_mock = self.PatchObject(pre_upload, '_get_file_diff')
198
199 def test_good_cases(self):
200 self.diff_mock.return_value = [
201 (1, u'no_tabs_anywhere'),
202 (2, u' leading_tab_only'),
203 (3, u' leading_tab another_tab'),
204 (4, u' leading_tab trailing_too '),
205 (5, u' leading_tab then_spaces_trailing '),
206 ]
207 failure = pre_upload._check_tabbed_indents(ProjectNamed('PROJECT'),
208 'COMMIT')
209 self.assertIsNone(failure)
210
211 def test_bad_cases(self):
212 self.diff_mock.return_value = [
213 (1, u' leading_space'),
214 (2, u' tab_followed_by_space'),
215 (3, u' space_followed_by_tab'),
216 (4, u' mix_em_up'),
217 (5, u' mix_on_both_sides '),
218 ]
219 failure = pre_upload._check_tabbed_indents(ProjectNamed('PROJECT'),
220 'COMMIT')
221 self.assertTrue(failure)
Mike Frysinger8a4e8942019-09-16 23:43:49 -0400222 self.assertEqual('Found a space in indentation (must be all tabs):',
223 failure.msg)
224 self.assertEqual(['x.ebuild, line %d' % x for x in range(1, 6)],
225 failure.items)
Prathmesh Prabhuc5254652016-12-22 12:58:05 -0800226
227
Mike Frysingerb2496652019-09-12 23:35:46 -0400228class CheckProjectPrefix(PreUploadTestCase, cros_test_lib.TempDirTestCase):
Daniel Erata350fd32014-09-29 14:02:34 -0700229 """Tests for _check_project_prefix."""
230
231 def setUp(self):
232 self.orig_cwd = os.getcwd()
233 os.chdir(self.tempdir)
234 self.file_mock = self.PatchObject(pre_upload, '_get_affected_files')
235 self.desc_mock = self.PatchObject(pre_upload, '_get_commit_desc')
236
237 def tearDown(self):
238 os.chdir(self.orig_cwd)
239
240 def _WriteAliasFile(self, filename, project):
241 """Writes a project name to a file, creating directories if needed."""
242 os.makedirs(os.path.dirname(filename))
243 osutils.WriteFile(filename, project)
244
245 def testInvalidPrefix(self):
246 """Report an error when the prefix doesn't match the base directory."""
247 self.file_mock.return_value = ['foo/foo.cc', 'foo/subdir/baz.cc']
248 self.desc_mock.return_value = 'bar: Some commit'
Alex Deymo643ac4c2015-09-03 10:40:50 -0700249 failure = pre_upload._check_project_prefix(ProjectNamed('PROJECT'),
250 'COMMIT')
Daniel Erata350fd32014-09-29 14:02:34 -0700251 self.assertTrue(failure)
Mike Frysinger8a4e8942019-09-16 23:43:49 -0400252 self.assertEqual('The commit title for changes affecting only foo should '
253 'start with "foo: "', failure.msg)
Daniel Erata350fd32014-09-29 14:02:34 -0700254
255 def testValidPrefix(self):
256 """Use a prefix that matches the base directory."""
257 self.file_mock.return_value = ['foo/foo.cc', 'foo/subdir/baz.cc']
258 self.desc_mock.return_value = 'foo: Change some files.'
Alex Deymo643ac4c2015-09-03 10:40:50 -0700259 self.assertFalse(
260 pre_upload._check_project_prefix(ProjectNamed('PROJECT'), 'COMMIT'))
Daniel Erata350fd32014-09-29 14:02:34 -0700261
262 def testAliasFile(self):
263 """Use .project_alias to override the project name."""
264 self._WriteAliasFile('foo/.project_alias', 'project')
265 self.file_mock.return_value = ['foo/foo.cc', 'foo/subdir/bar.cc']
266 self.desc_mock.return_value = 'project: Use an alias.'
Alex Deymo643ac4c2015-09-03 10:40:50 -0700267 self.assertFalse(
268 pre_upload._check_project_prefix(ProjectNamed('PROJECT'), 'COMMIT'))
Daniel Erata350fd32014-09-29 14:02:34 -0700269
270 def testAliasFileWithSubdirs(self):
271 """Check that .project_alias is used when only modifying subdirectories."""
272 self._WriteAliasFile('foo/.project_alias', 'project')
273 self.file_mock.return_value = [
274 'foo/subdir/foo.cc',
275 'foo/subdir/bar.cc'
276 'foo/subdir/blah/baz.cc'
277 ]
278 self.desc_mock.return_value = 'project: Alias with subdirs.'
Alex Deymo643ac4c2015-09-03 10:40:50 -0700279 self.assertFalse(
280 pre_upload._check_project_prefix(ProjectNamed('PROJECT'), 'COMMIT'))
Daniel Erata350fd32014-09-29 14:02:34 -0700281
282
Mike Frysingerb2496652019-09-12 23:35:46 -0400283class CheckFilePathCharTypeTest(PreUploadTestCase):
Satoru Takabayashi15d17a52018-08-06 11:12:15 +0900284 """Tests for _check_filepath_chartype."""
285
286 def setUp(self):
287 self.diff_mock = self.PatchObject(pre_upload, '_get_file_diff')
288
289 def testCheck(self):
290 self.PatchObject(pre_upload, '_get_affected_files', return_value=['x.cc'])
291 self.diff_mock.return_value = [
292 (1, 'base::FilePath'), # OK
293 (2, 'base::FilePath::CharType'), # NG
294 (3, 'base::FilePath::StringType'), # NG
295 (4, 'base::FilePath::StringPieceType'), # NG
Satoru Takabayashi4ca37922018-08-08 10:16:38 +0900296 (5, 'base::FilePath::FromUTF8Unsafe'), # NG
297 (6, 'FilePath::CharType'), # NG
298 (7, 'FilePath::StringType'), # NG
299 (8, 'FilePath::StringPieceType'), # NG
300 (9, 'FilePath::FromUTF8Unsafe'), # NG
301 (10, 'AsUTF8Unsafe'), # NG
302 (11, 'FILE_PATH_LITERAL'), # NG
Satoru Takabayashi15d17a52018-08-06 11:12:15 +0900303 ]
304 failure = pre_upload._check_filepath_chartype(ProjectNamed('PROJECT'),
305 'COMMIT')
306 self.assertTrue(failure)
Mike Frysinger8a4e8942019-09-16 23:43:49 -0400307 self.assertEqual(
Satoru Takabayashi15d17a52018-08-06 11:12:15 +0900308 'Please assume FilePath::CharType is char (crbug.com/870621):',
309 failure.msg)
Mike Frysinger8a4e8942019-09-16 23:43:49 -0400310 self.assertEqual(['x.cc, line 2 has base::FilePath::CharType',
311 'x.cc, line 3 has base::FilePath::StringType',
312 'x.cc, line 4 has base::FilePath::StringPieceType',
313 'x.cc, line 5 has base::FilePath::FromUTF8Unsafe',
314 'x.cc, line 6 has FilePath::CharType',
315 'x.cc, line 7 has FilePath::StringType',
316 'x.cc, line 8 has FilePath::StringPieceType',
317 'x.cc, line 9 has FilePath::FromUTF8Unsafe',
318 'x.cc, line 10 has AsUTF8Unsafe',
319 'x.cc, line 11 has FILE_PATH_LITERAL'],
320 failure.items)
Satoru Takabayashi15d17a52018-08-06 11:12:15 +0900321
322
Mike Frysingerb2496652019-09-12 23:35:46 -0400323class CheckKernelConfig(PreUploadTestCase):
Mike Frysingerae409522014-02-01 03:16:11 -0500324 """Tests for _kernel_configcheck."""
325
Mike Frysinger1459d362014-12-06 13:53:23 -0500326 def setUp(self):
327 self.file_mock = self.PatchObject(pre_upload, '_get_affected_files')
328
329 def testMixedChanges(self):
330 """Mixing of changes should fail."""
331 self.file_mock.return_value = [
332 '/kernel/files/chromeos/config/base.config',
333 '/kernel/files/arch/arm/mach-exynos/mach-exynos5-dt.c'
334 ]
Olof Johanssona96810f2012-09-04 16:20:03 -0700335 failure = pre_upload._kernel_configcheck('PROJECT', 'COMMIT')
336 self.assertTrue(failure)
337
Mike Frysinger1459d362014-12-06 13:53:23 -0500338 def testCodeOnly(self):
339 """Code-only changes should pass."""
340 self.file_mock.return_value = [
341 '/kernel/files/Makefile',
342 '/kernel/files/arch/arm/mach-exynos/mach-exynos5-dt.c'
343 ]
Olof Johanssona96810f2012-09-04 16:20:03 -0700344 failure = pre_upload._kernel_configcheck('PROJECT', 'COMMIT')
345 self.assertFalse(failure)
346
Mike Frysinger1459d362014-12-06 13:53:23 -0500347 def testConfigOnlyChanges(self):
348 """Config-only changes should pass."""
349 self.file_mock.return_value = [
350 '/kernel/files/chromeos/config/base.config',
351 ]
Olof Johanssona96810f2012-09-04 16:20:03 -0700352 failure = pre_upload._kernel_configcheck('PROJECT', 'COMMIT')
353 self.assertFalse(failure)
354
Jon Salz98255932012-08-18 14:48:02 +0800355
Mike Frysingerb2496652019-09-12 23:35:46 -0400356class CheckJson(PreUploadTestCase):
Mike Frysinger908be682018-01-04 02:21:50 -0500357 """Tests for _run_json_check."""
358
359 def setUp(self):
360 self.file_mock = self.PatchObject(pre_upload, '_get_affected_files')
361 self.content_mock = self.PatchObject(pre_upload, '_get_file_content')
362
363 def testNoJson(self):
364 """Nothing should be checked w/no JSON files."""
365 self.file_mock.return_value = [
366 '/foo/bar.txt',
367 '/readme.md',
368 ]
369 ret = pre_upload._run_json_check('PROJECT', 'COMMIT')
370 self.assertIsNone(ret)
371
372 def testValidJson(self):
373 """We should accept valid json files."""
374 self.file_mock.return_value = [
375 '/foo/bar.txt',
376 '/data.json',
377 ]
378 self.content_mock.return_value = '{}'
379 ret = pre_upload._run_json_check('PROJECT', 'COMMIT')
380 self.assertIsNone(ret)
381 self.content_mock.assert_called_once_with('/data.json', 'COMMIT')
382
383 def testInvalidJson(self):
384 """We should reject invalid json files."""
385 self.file_mock.return_value = [
386 '/foo/bar.txt',
387 '/data.json',
388 ]
389 self.content_mock.return_value = '{'
390 ret = pre_upload._run_json_check('PROJECT', 'COMMIT')
391 self.assertIsNotNone(ret)
392 self.content_mock.assert_called_once_with('/data.json', 'COMMIT')
393
394
Mike Frysingerb2496652019-09-12 23:35:46 -0400395class CheckManifests(PreUploadTestCase):
Mike Frysingeraae3cb52018-01-03 16:49:33 -0500396 """Tests _check_manifests."""
397
398 def setUp(self):
399 self.file_mock = self.PatchObject(pre_upload, '_get_affected_files')
400 self.content_mock = self.PatchObject(pre_upload, '_get_file_content')
401
402 def testNoManifests(self):
403 """Nothing should be checked w/no Manifest files."""
404 self.file_mock.return_value = [
405 '/foo/bar.txt',
406 '/readme.md',
407 '/manifest',
408 '/Manifest.txt',
409 ]
410 ret = pre_upload._check_manifests('PROJECT', 'COMMIT')
411 self.assertIsNone(ret)
412
413 def testValidManifest(self):
414 """Accept valid Manifest files."""
415 self.file_mock.return_value = [
416 '/foo/bar.txt',
417 '/cat/pkg/Manifest',
418 ]
Mike Frysinger24dd3c52019-08-17 14:22:48 -0400419 self.content_mock.return_value = """# Comment and blank lines.
Mike Frysingeraae3cb52018-01-03 16:49:33 -0500420
421DIST lines
Mike Frysinger24dd3c52019-08-17 14:22:48 -0400422"""
Mike Frysingeraae3cb52018-01-03 16:49:33 -0500423 ret = pre_upload._check_manifests('PROJECT', 'COMMIT')
424 self.assertIsNone(ret)
425 self.content_mock.assert_called_once_with('/cat/pkg/Manifest', 'COMMIT')
426
427 def _testReject(self, content):
428 """Make sure |content| is rejected."""
429 self.file_mock.return_value = ('/Manifest',)
430 self.content_mock.reset_mock()
431 self.content_mock.return_value = content
432 ret = pre_upload._check_manifests('PROJECT', 'COMMIT')
433 self.assertIsNotNone(ret)
434 self.content_mock.assert_called_once_with('/Manifest', 'COMMIT')
435
436 def testRejectBlank(self):
437 """Reject empty manifests."""
438 self._testReject('')
439
440 def testNoTrailingNewLine(self):
441 """Reject manifests w/out trailing newline."""
442 self._testReject('DIST foo')
443
444 def testNoLeadingBlankLines(self):
445 """Reject manifests w/leading blank lines."""
446 self._testReject('\nDIST foo\n')
447
448 def testNoTrailingBlankLines(self):
449 """Reject manifests w/trailing blank lines."""
450 self._testReject('DIST foo\n\n')
451
452 def testNoLeadingWhitespace(self):
453 """Reject manifests w/lines w/leading spaces."""
454 self._testReject(' DIST foo\n')
455 self._testReject(' # Comment\n')
456
457 def testNoTrailingWhitespace(self):
458 """Reject manifests w/lines w/trailing spaces."""
459 self._testReject('DIST foo \n')
460 self._testReject('# Comment \n')
461 self._testReject(' \n')
462
463 def testOnlyDistLines(self):
464 """Only allow DIST lines in here."""
465 self._testReject('EBUILD foo\n')
466
467
Mike Frysingerb2496652019-09-12 23:35:46 -0400468class CheckPortageMakeUseVar(PreUploadTestCase):
Daniel Erat9d203ff2015-02-17 10:12:21 -0700469 """Tests for _check_portage_make_use_var."""
470
471 def setUp(self):
472 self.file_mock = self.PatchObject(pre_upload, '_get_affected_files')
473 self.content_mock = self.PatchObject(pre_upload, '_get_file_content')
474
475 def testMakeConfOmitsOriginalUseValue(self):
476 """Fail for make.conf that discards the previous value of $USE."""
477 self.file_mock.return_value = ['make.conf']
Mike Frysinger71e643e2019-09-13 17:26:39 -0400478 self.content_mock.return_value = u'USE="foo"\nUSE="${USE} bar"'
Daniel Erat9d203ff2015-02-17 10:12:21 -0700479 failure = pre_upload._check_portage_make_use_var('PROJECT', 'COMMIT')
480 self.assertTrue(failure, failure)
481
482 def testMakeConfCorrectUsage(self):
483 """Succeed for make.conf that preserves the previous value of $USE."""
484 self.file_mock.return_value = ['make.conf']
Mike Frysinger71e643e2019-09-13 17:26:39 -0400485 self.content_mock.return_value = u'USE="${USE} foo"\nUSE="${USE}" bar'
Daniel Erat9d203ff2015-02-17 10:12:21 -0700486 failure = pre_upload._check_portage_make_use_var('PROJECT', 'COMMIT')
487 self.assertFalse(failure, failure)
488
489 def testMakeDefaultsReferencesOriginalUseValue(self):
490 """Fail for make.defaults that refers to a not-yet-set $USE value."""
491 self.file_mock.return_value = ['make.defaults']
Mike Frysinger71e643e2019-09-13 17:26:39 -0400492 self.content_mock.return_value = u'USE="${USE} foo"'
Daniel Erat9d203ff2015-02-17 10:12:21 -0700493 failure = pre_upload._check_portage_make_use_var('PROJECT', 'COMMIT')
494 self.assertTrue(failure, failure)
495
496 # Also check for "$USE" without curly brackets.
Mike Frysinger71e643e2019-09-13 17:26:39 -0400497 self.content_mock.return_value = u'USE="$USE foo"'
Daniel Erat9d203ff2015-02-17 10:12:21 -0700498 failure = pre_upload._check_portage_make_use_var('PROJECT', 'COMMIT')
499 self.assertTrue(failure, failure)
500
501 def testMakeDefaultsOverwritesUseValue(self):
502 """Fail for make.defaults that discards its own $USE value."""
503 self.file_mock.return_value = ['make.defaults']
Mike Frysinger71e643e2019-09-13 17:26:39 -0400504 self.content_mock.return_value = u'USE="foo"\nUSE="bar"'
Daniel Erat9d203ff2015-02-17 10:12:21 -0700505 failure = pre_upload._check_portage_make_use_var('PROJECT', 'COMMIT')
506 self.assertTrue(failure, failure)
507
508 def testMakeDefaultsCorrectUsage(self):
509 """Succeed for make.defaults that sets and preserves $USE."""
510 self.file_mock.return_value = ['make.defaults']
Mike Frysinger71e643e2019-09-13 17:26:39 -0400511 self.content_mock.return_value = u'USE="foo"\nUSE="${USE}" bar'
Daniel Erat9d203ff2015-02-17 10:12:21 -0700512 failure = pre_upload._check_portage_make_use_var('PROJECT', 'COMMIT')
513 self.assertFalse(failure, failure)
514
515
Mike Frysingerb2496652019-09-12 23:35:46 -0400516class CheckEbuildEapi(PreUploadTestCase):
Mike Frysingerbf8b91c2014-02-01 02:50:27 -0500517 """Tests for _check_ebuild_eapi."""
518
Alex Deymo643ac4c2015-09-03 10:40:50 -0700519 PORTAGE_STABLE = ProjectNamed('chromiumos/overlays/portage-stable')
Mike Frysingerbf8b91c2014-02-01 02:50:27 -0500520
521 def setUp(self):
522 self.file_mock = self.PatchObject(pre_upload, '_get_affected_files')
523 self.content_mock = self.PatchObject(pre_upload, '_get_file_content')
524 self.diff_mock = self.PatchObject(pre_upload, '_get_file_diff',
525 side_effect=Exception())
526
527 def testSkipUpstreamOverlays(self):
528 """Skip ebuilds found in upstream overlays."""
529 self.file_mock.side_effect = Exception()
530 ret = pre_upload._check_ebuild_eapi(self.PORTAGE_STABLE, 'HEAD')
Mike Frysinger8a4e8942019-09-16 23:43:49 -0400531 self.assertIsNone(ret)
Mike Frysingerbf8b91c2014-02-01 02:50:27 -0500532
533 # Make sure our condition above triggers.
534 self.assertRaises(Exception, pre_upload._check_ebuild_eapi, 'o', 'HEAD')
535
536 def testSkipNonEbuilds(self):
537 """Skip non-ebuild files."""
538 self.content_mock.side_effect = Exception()
539
540 self.file_mock.return_value = ['some-file', 'ebuild/dir', 'an.ebuild~']
Alex Deymo643ac4c2015-09-03 10:40:50 -0700541 ret = pre_upload._check_ebuild_eapi(ProjectNamed('overlay'), 'HEAD')
Mike Frysinger8a4e8942019-09-16 23:43:49 -0400542 self.assertIsNone(ret)
Mike Frysingerbf8b91c2014-02-01 02:50:27 -0500543
544 # Make sure our condition above triggers.
545 self.file_mock.return_value.append('a/real.ebuild')
Alex Deymo643ac4c2015-09-03 10:40:50 -0700546 self.assertRaises(Exception, pre_upload._check_ebuild_eapi,
547 ProjectNamed('o'), 'HEAD')
Mike Frysingerbf8b91c2014-02-01 02:50:27 -0500548
549 def testSkipSymlink(self):
550 """Skip files that are just symlinks."""
551 self.file_mock.return_value = ['a-r1.ebuild']
Mike Frysinger71e643e2019-09-13 17:26:39 -0400552 self.content_mock.return_value = u'a.ebuild'
Alex Deymo643ac4c2015-09-03 10:40:50 -0700553 ret = pre_upload._check_ebuild_eapi(ProjectNamed('overlay'), 'HEAD')
Mike Frysinger8a4e8942019-09-16 23:43:49 -0400554 self.assertIsNone(ret)
Mike Frysingerbf8b91c2014-02-01 02:50:27 -0500555
556 def testRejectEapiImplicit0Content(self):
557 """Reject ebuilds that do not declare EAPI (so it's 0)."""
558 self.file_mock.return_value = ['a.ebuild']
559
Mike Frysinger71e643e2019-09-13 17:26:39 -0400560 self.content_mock.return_value = u"""# Header
Mike Frysingerbf8b91c2014-02-01 02:50:27 -0500561IUSE="foo"
562src_compile() { }
563"""
Alex Deymo643ac4c2015-09-03 10:40:50 -0700564 ret = pre_upload._check_ebuild_eapi(ProjectNamed('overlay'), 'HEAD')
Mike Frysingerb81102f2014-11-21 00:33:35 -0500565 self.assertTrue(isinstance(ret, errors.HookFailure))
Mike Frysingerbf8b91c2014-02-01 02:50:27 -0500566
567 def testRejectExplicitEapi1Content(self):
568 """Reject ebuilds that do declare old EAPI explicitly."""
569 self.file_mock.return_value = ['a.ebuild']
570
Mike Frysinger71e643e2019-09-13 17:26:39 -0400571 template = u"""# Header
Mike Frysingerbf8b91c2014-02-01 02:50:27 -0500572EAPI=%s
573IUSE="foo"
574src_compile() { }
575"""
576 # Make sure we only check the first EAPI= setting.
Mike Frysinger948284a2018-02-01 15:22:56 -0500577 self.content_mock.return_value = template % '1\nEAPI=60'
Alex Deymo643ac4c2015-09-03 10:40:50 -0700578 ret = pre_upload._check_ebuild_eapi(ProjectNamed('overlay'), 'HEAD')
Mike Frysingerb81102f2014-11-21 00:33:35 -0500579 self.assertTrue(isinstance(ret, errors.HookFailure))
Mike Frysingerbf8b91c2014-02-01 02:50:27 -0500580
581 # Verify we handle double quotes too.
582 self.content_mock.return_value = template % '"1"'
Alex Deymo643ac4c2015-09-03 10:40:50 -0700583 ret = pre_upload._check_ebuild_eapi(ProjectNamed('overlay'), 'HEAD')
Mike Frysingerb81102f2014-11-21 00:33:35 -0500584 self.assertTrue(isinstance(ret, errors.HookFailure))
Mike Frysingerbf8b91c2014-02-01 02:50:27 -0500585
586 # Verify we handle single quotes too.
587 self.content_mock.return_value = template % "'1'"
Alex Deymo643ac4c2015-09-03 10:40:50 -0700588 ret = pre_upload._check_ebuild_eapi(ProjectNamed('overlay'), 'HEAD')
Mike Frysingerb81102f2014-11-21 00:33:35 -0500589 self.assertTrue(isinstance(ret, errors.HookFailure))
Mike Frysingerbf8b91c2014-02-01 02:50:27 -0500590
Mike Frysinger948284a2018-02-01 15:22:56 -0500591 def testAcceptExplicitNewEapiContent(self):
Mike Frysingerbf8b91c2014-02-01 02:50:27 -0500592 """Accept ebuilds that do declare new EAPI explicitly."""
593 self.file_mock.return_value = ['a.ebuild']
594
Mike Frysinger71e643e2019-09-13 17:26:39 -0400595 template = u"""# Header
Mike Frysingerbf8b91c2014-02-01 02:50:27 -0500596EAPI=%s
597IUSE="foo"
598src_compile() { }
599"""
600 # Make sure we only check the first EAPI= setting.
Mike Frysinger948284a2018-02-01 15:22:56 -0500601 self.content_mock.return_value = template % '6\nEAPI=1'
Alex Deymo643ac4c2015-09-03 10:40:50 -0700602 ret = pre_upload._check_ebuild_eapi(ProjectNamed('overlay'), 'HEAD')
Mike Frysinger8a4e8942019-09-16 23:43:49 -0400603 self.assertIsNone(ret)
Mike Frysingerbf8b91c2014-02-01 02:50:27 -0500604
605 # Verify we handle double quotes too.
606 self.content_mock.return_value = template % '"5"'
Alex Deymo643ac4c2015-09-03 10:40:50 -0700607 ret = pre_upload._check_ebuild_eapi(ProjectNamed('overlay'), 'HEAD')
Mike Frysinger8a4e8942019-09-16 23:43:49 -0400608 self.assertIsNone(ret)
Mike Frysingerbf8b91c2014-02-01 02:50:27 -0500609
610 # Verify we handle single quotes too.
611 self.content_mock.return_value = template % "'5-hdepend'"
Alex Deymo643ac4c2015-09-03 10:40:50 -0700612 ret = pre_upload._check_ebuild_eapi(ProjectNamed('overlay'), 'HEAD')
Mike Frysinger8a4e8942019-09-16 23:43:49 -0400613 self.assertIsNone(ret)
Mike Frysingerbf8b91c2014-02-01 02:50:27 -0500614
615
Mike Frysingerb2496652019-09-12 23:35:46 -0400616class CheckEbuildKeywords(PreUploadTestCase):
Mike Frysinger5c9e58d2014-09-09 03:32:50 -0400617 """Tests for _check_ebuild_keywords."""
618
619 def setUp(self):
620 self.file_mock = self.PatchObject(pre_upload, '_get_affected_files')
621 self.content_mock = self.PatchObject(pre_upload, '_get_file_content')
622
623 def testNoEbuilds(self):
624 """If no ebuilds are found, do not scan."""
625 self.file_mock.return_value = ['a.file', 'ebuild-is-not.foo']
626
Alex Deymo643ac4c2015-09-03 10:40:50 -0700627 ret = pre_upload._check_ebuild_keywords(ProjectNamed('overlay'), 'HEAD')
Mike Frysinger8a4e8942019-09-16 23:43:49 -0400628 self.assertIsNone(ret)
Mike Frysinger5c9e58d2014-09-09 03:32:50 -0400629
630 self.assertEqual(self.content_mock.call_count, 0)
631
632 def testSomeEbuilds(self):
633 """If ebuilds are found, only scan them."""
634 self.file_mock.return_value = ['a.file', 'blah', 'foo.ebuild', 'cow']
Mike Frysinger71e643e2019-09-13 17:26:39 -0400635 self.content_mock.return_value = u''
Mike Frysinger5c9e58d2014-09-09 03:32:50 -0400636
Alex Deymo643ac4c2015-09-03 10:40:50 -0700637 ret = pre_upload._check_ebuild_keywords(ProjectNamed('overlay'), 'HEAD')
Mike Frysinger8a4e8942019-09-16 23:43:49 -0400638 self.assertIsNone(ret)
Mike Frysinger5c9e58d2014-09-09 03:32:50 -0400639
640 self.assertEqual(self.content_mock.call_count, 1)
641
642 def _CheckContent(self, content, fails):
643 """Test helper for inputs/outputs.
644
645 Args:
646 content: The ebuild content to test.
647 fails: Whether |content| should trigger a hook failure.
648 """
649 self.file_mock.return_value = ['a.ebuild']
650 self.content_mock.return_value = content
651
Alex Deymo643ac4c2015-09-03 10:40:50 -0700652 ret = pre_upload._check_ebuild_keywords(ProjectNamed('overlay'), 'HEAD')
Mike Frysinger5c9e58d2014-09-09 03:32:50 -0400653 if fails:
Mike Frysingerb81102f2014-11-21 00:33:35 -0500654 self.assertTrue(isinstance(ret, errors.HookFailure))
Mike Frysinger5c9e58d2014-09-09 03:32:50 -0400655 else:
Mike Frysinger8a4e8942019-09-16 23:43:49 -0400656 self.assertIsNone(ret)
Mike Frysinger5c9e58d2014-09-09 03:32:50 -0400657
658 self.assertEqual(self.content_mock.call_count, 1)
659
660 def testEmpty(self):
661 """Check KEYWORDS= is accepted."""
Mike Frysinger71e643e2019-09-13 17:26:39 -0400662 self._CheckContent(u'# HEADER\nKEYWORDS=\nblah\n', False)
Mike Frysinger5c9e58d2014-09-09 03:32:50 -0400663
664 def testEmptyQuotes(self):
665 """Check KEYWORDS="" is accepted."""
Mike Frysinger71e643e2019-09-13 17:26:39 -0400666 self._CheckContent(u'# HEADER\nKEYWORDS=" "\nblah\n', False)
Mike Frysinger5c9e58d2014-09-09 03:32:50 -0400667
668 def testStableGlob(self):
669 """Check KEYWORDS=* is accepted."""
Mike Frysinger71e643e2019-09-13 17:26:39 -0400670 self._CheckContent(u'# HEADER\nKEYWORDS="\t*\t"\nblah\n', False)
Mike Frysinger5c9e58d2014-09-09 03:32:50 -0400671
672 def testUnstableGlob(self):
673 """Check KEYWORDS=~* is accepted."""
Mike Frysinger71e643e2019-09-13 17:26:39 -0400674 self._CheckContent(u'# HEADER\nKEYWORDS="~* "\nblah\n', False)
Mike Frysinger5c9e58d2014-09-09 03:32:50 -0400675
676 def testRestrictedGlob(self):
677 """Check KEYWORDS=-* is accepted."""
Mike Frysinger71e643e2019-09-13 17:26:39 -0400678 self._CheckContent(u'# HEADER\nKEYWORDS="\t-* arm"\nblah\n', False)
Mike Frysinger5c9e58d2014-09-09 03:32:50 -0400679
680 def testMissingGlobs(self):
681 """Reject KEYWORDS missing any globs."""
Mike Frysinger71e643e2019-09-13 17:26:39 -0400682 self._CheckContent(u'# HEADER\nKEYWORDS="~arm x86"\nblah\n', True)
Mike Frysinger5c9e58d2014-09-09 03:32:50 -0400683
684
Mike Frysingerb2496652019-09-12 23:35:46 -0400685class CheckEbuildVirtualPv(PreUploadTestCase):
Mike Frysingercd363c82014-02-01 05:20:18 -0500686 """Tests for _check_ebuild_virtual_pv."""
687
Alex Deymo643ac4c2015-09-03 10:40:50 -0700688 PORTAGE_STABLE = ProjectNamed('chromiumos/overlays/portage-stable')
689 CHROMIUMOS_OVERLAY = ProjectNamed('chromiumos/overlays/chromiumos')
690 BOARD_OVERLAY = ProjectNamed('chromiumos/overlays/board-overlays')
691 PRIVATE_OVERLAY = ProjectNamed('chromeos/overlays/overlay-link-private')
692 PRIVATE_VARIANT_OVERLAY = ProjectNamed('chromeos/overlays/'
693 'overlay-variant-daisy-spring-private')
Mike Frysingercd363c82014-02-01 05:20:18 -0500694
695 def setUp(self):
696 self.file_mock = self.PatchObject(pre_upload, '_get_affected_files')
697
698 def testNoVirtuals(self):
699 """Skip non virtual packages."""
700 self.file_mock.return_value = ['some/package/package-3.ebuild']
Alex Deymo643ac4c2015-09-03 10:40:50 -0700701 ret = pre_upload._check_ebuild_virtual_pv(ProjectNamed('overlay'), 'H')
Mike Frysinger8a4e8942019-09-16 23:43:49 -0400702 self.assertIsNone(ret)
Mike Frysingercd363c82014-02-01 05:20:18 -0500703
704 def testCommonVirtuals(self):
705 """Non-board overlays should use PV=1."""
706 template = 'virtual/foo/foo-%s.ebuild'
707 self.file_mock.return_value = [template % '1']
708 ret = pre_upload._check_ebuild_virtual_pv(self.CHROMIUMOS_OVERLAY, 'H')
Mike Frysinger8a4e8942019-09-16 23:43:49 -0400709 self.assertIsNone(ret)
Mike Frysingercd363c82014-02-01 05:20:18 -0500710
711 self.file_mock.return_value = [template % '2']
712 ret = pre_upload._check_ebuild_virtual_pv(self.CHROMIUMOS_OVERLAY, 'H')
Mike Frysingerb81102f2014-11-21 00:33:35 -0500713 self.assertTrue(isinstance(ret, errors.HookFailure))
Mike Frysingercd363c82014-02-01 05:20:18 -0500714
715 def testPublicBoardVirtuals(self):
716 """Public board overlays should use PV=2."""
717 template = 'overlay-lumpy/virtual/foo/foo-%s.ebuild'
718 self.file_mock.return_value = [template % '2']
719 ret = pre_upload._check_ebuild_virtual_pv(self.BOARD_OVERLAY, 'H')
Mike Frysinger8a4e8942019-09-16 23:43:49 -0400720 self.assertIsNone(ret)
Mike Frysingercd363c82014-02-01 05:20:18 -0500721
722 self.file_mock.return_value = [template % '2.5']
723 ret = pre_upload._check_ebuild_virtual_pv(self.BOARD_OVERLAY, 'H')
Mike Frysingerb81102f2014-11-21 00:33:35 -0500724 self.assertTrue(isinstance(ret, errors.HookFailure))
Mike Frysingercd363c82014-02-01 05:20:18 -0500725
726 def testPublicBoardVariantVirtuals(self):
727 """Public board variant overlays should use PV=2.5."""
728 template = 'overlay-variant-lumpy-foo/virtual/foo/foo-%s.ebuild'
729 self.file_mock.return_value = [template % '2.5']
730 ret = pre_upload._check_ebuild_virtual_pv(self.BOARD_OVERLAY, 'H')
Mike Frysinger8a4e8942019-09-16 23:43:49 -0400731 self.assertIsNone(ret)
Mike Frysingercd363c82014-02-01 05:20:18 -0500732
733 self.file_mock.return_value = [template % '3']
734 ret = pre_upload._check_ebuild_virtual_pv(self.BOARD_OVERLAY, 'H')
Mike Frysingerb81102f2014-11-21 00:33:35 -0500735 self.assertTrue(isinstance(ret, errors.HookFailure))
Mike Frysingercd363c82014-02-01 05:20:18 -0500736
737 def testPrivateBoardVirtuals(self):
738 """Private board overlays should use PV=3."""
739 template = 'virtual/foo/foo-%s.ebuild'
740 self.file_mock.return_value = [template % '3']
741 ret = pre_upload._check_ebuild_virtual_pv(self.PRIVATE_OVERLAY, 'H')
Mike Frysinger8a4e8942019-09-16 23:43:49 -0400742 self.assertIsNone(ret)
Mike Frysingercd363c82014-02-01 05:20:18 -0500743
744 self.file_mock.return_value = [template % '3.5']
745 ret = pre_upload._check_ebuild_virtual_pv(self.PRIVATE_OVERLAY, 'H')
Mike Frysingerb81102f2014-11-21 00:33:35 -0500746 self.assertTrue(isinstance(ret, errors.HookFailure))
Mike Frysingercd363c82014-02-01 05:20:18 -0500747
748 def testPrivateBoardVariantVirtuals(self):
749 """Private board variant overlays should use PV=3.5."""
750 template = 'virtual/foo/foo-%s.ebuild'
751 self.file_mock.return_value = [template % '3.5']
752 ret = pre_upload._check_ebuild_virtual_pv(self.PRIVATE_VARIANT_OVERLAY, 'H')
Mike Frysinger8a4e8942019-09-16 23:43:49 -0400753 self.assertIsNone(ret)
Mike Frysingercd363c82014-02-01 05:20:18 -0500754
Bernie Thompsone5ee1822016-01-12 14:22:23 -0800755 def testSpecialVirtuals(self):
756 """Some cases require deeper versioning and can be >= 4."""
757 template = 'virtual/foo/foo-%s.ebuild'
Mike Frysingercd363c82014-02-01 05:20:18 -0500758 self.file_mock.return_value = [template % '4']
759 ret = pre_upload._check_ebuild_virtual_pv(self.PRIVATE_VARIANT_OVERLAY, 'H')
Mike Frysinger8a4e8942019-09-16 23:43:49 -0400760 self.assertIsNone(ret)
Mike Frysingercd363c82014-02-01 05:20:18 -0500761
Bernie Thompsone5ee1822016-01-12 14:22:23 -0800762 self.file_mock.return_value = [template % '4.5']
763 ret = pre_upload._check_ebuild_virtual_pv(self.PRIVATE_VARIANT_OVERLAY, 'H')
Mike Frysinger8a4e8942019-09-16 23:43:49 -0400764 self.assertIsNone(ret)
Mike Frysinger98638102014-08-28 00:15:08 -0400765
Mike Frysingerb2496652019-09-12 23:35:46 -0400766class CheckCrosLicenseCopyrightHeader(PreUploadTestCase):
Alex Deymof5792ce2015-08-24 22:50:08 -0700767 """Tests for _check_cros_license."""
Mike Frysinger98638102014-08-28 00:15:08 -0400768
769 def setUp(self):
770 self.file_mock = self.PatchObject(pre_upload, '_get_affected_files')
771 self.content_mock = self.PatchObject(pre_upload, '_get_file_content')
772
773 def testOldHeaders(self):
774 """Accept old header styles."""
775 HEADERS = (
Mike Frysinger71e643e2019-09-13 17:26:39 -0400776 (u'#!/bin/sh\n'
777 u'# Copyright (c) 2012 The Chromium OS Authors. All rights reserved.\n'
778 u'# Use of this source code is governed by a BSD-style license that'
779 u' can be\n'
780 u'# found in the LICENSE file.\n'),
781 (u'// Copyright 2010-2013 The Chromium OS Authors. All rights reserved.'
782 u'\n// Use of this source code is governed by a BSD-style license that'
783 u' can be\n'
784 u'// found in the LICENSE file.\n'),
Mike Frysinger98638102014-08-28 00:15:08 -0400785 )
786 self.file_mock.return_value = ['file']
787 for header in HEADERS:
788 self.content_mock.return_value = header
Keigo Oka7e880ac2019-07-03 15:03:43 +0900789 self.assertFalse(pre_upload._check_cros_license('proj', 'sha1'))
790
791 def testNewFileYear(self):
792 """Added files should have the current year in license header."""
793 year = datetime.datetime.now().year
794 HEADERS = (
Mike Frysinger71e643e2019-09-13 17:26:39 -0400795 (u'// Copyright 2016 The Chromium OS Authors. All rights reserved.\n'
796 u'// Use of this source code is governed by a BSD-style license that'
797 u' can be\n'
798 u'// found in the LICENSE file.\n'),
799 (u'// Copyright %d The Chromium OS Authors. All rights reserved.\n'
800 u'// Use of this source code is governed by a BSD-style license that'
801 u' can be\n'
802 u'// found in the LICENSE file.\n') % year,
Keigo Oka7e880ac2019-07-03 15:03:43 +0900803 )
804 want_error = (True, False)
805 def fake_get_affected_files(_, relative, include_adds=True):
806 _ = relative
807 if include_adds:
808 return ['file']
809 else:
810 return []
811
812 self.file_mock.side_effect = fake_get_affected_files
813 for i, header in enumerate(HEADERS):
814 self.content_mock.return_value = header
815 if want_error[i]:
816 self.assertTrue(pre_upload._check_cros_license('proj', 'sha1'))
817 else:
818 self.assertFalse(pre_upload._check_cros_license('proj', 'sha1'))
Mike Frysinger98638102014-08-28 00:15:08 -0400819
820 def testRejectC(self):
821 """Reject the (c) in newer headers."""
822 HEADERS = (
Mike Frysinger71e643e2019-09-13 17:26:39 -0400823 (u'// Copyright (c) 2015 The Chromium OS Authors. All rights reserved.'
824 u'\n'
825 u'// Use of this source code is governed by a BSD-style license that'
826 u' can be\n'
827 u'// found in the LICENSE file.\n'),
828 (u'// Copyright (c) 2020 The Chromium OS Authors. All rights reserved.'
829 u'\n'
830 u'// Use of this source code is governed by a BSD-style license that'
831 u' can be\n'
832 u'// found in the LICENSE file.\n'),
Mike Frysinger98638102014-08-28 00:15:08 -0400833 )
834 self.file_mock.return_value = ['file']
835 for header in HEADERS:
836 self.content_mock.return_value = header
Keigo Oka7e880ac2019-07-03 15:03:43 +0900837 self.assertTrue(pre_upload._check_cros_license('proj', 'sha1'))
Alex Deymof5792ce2015-08-24 22:50:08 -0700838
Brian Norris68838dd2018-09-26 18:30:24 -0700839 def testNoLeadingSpace(self):
840 """Allow headers without leading space (e.g., not a source comment)"""
841 HEADERS = (
Mike Frysinger71e643e2019-09-13 17:26:39 -0400842 (u'Copyright 2018 The Chromium OS Authors. All rights reserved.\n'
843 u'Use of this source code is governed by a BSD-style license that '
844 u'can be\n'
845 u'found in the LICENSE file.\n'),
Brian Norris68838dd2018-09-26 18:30:24 -0700846 )
847 self.file_mock.return_value = ['file']
848 for header in HEADERS:
849 self.content_mock.return_value = header
Keigo Oka7e880ac2019-07-03 15:03:43 +0900850 self.assertFalse(pre_upload._check_cros_license('proj', 'sha1'))
Brian Norris68838dd2018-09-26 18:30:24 -0700851
Keigo Oka9732e382019-06-28 17:44:59 +0900852 def testNoExcludedGolang(self):
853 """Don't exclude .go files for license checks."""
854 self.file_mock.return_value = ['foo/main.go']
Mike Frysinger71e643e2019-09-13 17:26:39 -0400855 self.content_mock.return_value = u'package main\nfunc main() {}'
Keigo Oka7e880ac2019-07-03 15:03:43 +0900856 self.assertTrue(pre_upload._check_cros_license('proj', 'sha1'))
Keigo Oka9732e382019-06-28 17:44:59 +0900857
Ken Turnerd07564b2018-02-08 17:57:59 +1100858 def testIgnoreExcludedPaths(self):
859 """Ignores excluded paths for license checks."""
860 self.file_mock.return_value = ['foo/OWNERS']
Mike Frysinger71e643e2019-09-13 17:26:39 -0400861 self.content_mock.return_value = u'owner@chromium.org'
Keigo Oka7e880ac2019-07-03 15:03:43 +0900862 self.assertFalse(pre_upload._check_cros_license('proj', 'sha1'))
Ken Turnerd07564b2018-02-08 17:57:59 +1100863
Chris McDonald7b63c8e2019-04-25 10:27:27 -0600864 def testIgnoreTopLevelExcludedPaths(self):
865 """Ignores excluded paths for license checks."""
866 self.file_mock.return_value = ['OWNERS']
Mike Frysinger71e643e2019-09-13 17:26:39 -0400867 self.content_mock.return_value = u'owner@chromium.org'
Keigo Oka7e880ac2019-07-03 15:03:43 +0900868 self.assertFalse(pre_upload._check_cros_license('proj', 'sha1'))
Alex Deymof5792ce2015-08-24 22:50:08 -0700869
Mike Frysingerb2496652019-09-12 23:35:46 -0400870class CheckAOSPLicenseCopyrightHeader(PreUploadTestCase):
Alex Deymof5792ce2015-08-24 22:50:08 -0700871 """Tests for _check_aosp_license."""
872
873 def setUp(self):
874 self.file_mock = self.PatchObject(pre_upload, '_get_affected_files')
875 self.content_mock = self.PatchObject(pre_upload, '_get_file_content')
876
877 def testHeaders(self):
878 """Accept old header styles."""
879 HEADERS = (
Mike Frysinger71e643e2019-09-13 17:26:39 -0400880 u"""//
Alex Deymof5792ce2015-08-24 22:50:08 -0700881// Copyright (C) 2011 The Android Open Source Project
882//
883// Licensed under the Apache License, Version 2.0 (the "License");
884// you may not use this file except in compliance with the License.
885// You may obtain a copy of the License at
886//
887// http://www.apache.org/licenses/LICENSE-2.0
888//
889// Unless required by applicable law or agreed to in writing, software
890// distributed under the License is distributed on an "AS IS" BASIS,
891// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
892// See the License for the specific language governing permissions and
893// limitations under the License.
894//
895""",
Mike Frysinger71e643e2019-09-13 17:26:39 -0400896 u"""#
Alex Deymof5792ce2015-08-24 22:50:08 -0700897# Copyright (c) 2015 The Android Open Source Project
898#
899# Licensed under the Apache License, Version 2.0 (the "License");
900# you may not use this file except in compliance with the License.
901# You may obtain a copy of the License at
902#
903# http://www.apache.org/licenses/LICENSE-2.0
904#
905# Unless required by applicable law or agreed to in writing, software
906# distributed under the License is distributed on an "AS IS" BASIS,
907# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
908# See the License for the specific language governing permissions and
909# limitations under the License.
910#
911""",
912 )
913 self.file_mock.return_value = ['file']
914 for header in HEADERS:
915 self.content_mock.return_value = header
Mike Frysinger8a4e8942019-09-16 23:43:49 -0400916 self.assertIsNone(pre_upload._check_aosp_license('proj', 'sha1'))
Alex Deymof5792ce2015-08-24 22:50:08 -0700917
918 def testRejectNoLinesAround(self):
919 """Reject headers missing the empty lines before/after the license."""
920 HEADERS = (
Mike Frysinger71e643e2019-09-13 17:26:39 -0400921 u"""# Copyright (c) 2015 The Android Open Source Project
Alex Deymof5792ce2015-08-24 22:50:08 -0700922#
923# Licensed under the Apache License, Version 2.0 (the "License");
924# you may not use this file except in compliance with the License.
925# You may obtain a copy of the License at
926#
927# http://www.apache.org/licenses/LICENSE-2.0
928#
929# Unless required by applicable law or agreed to in writing, software
930# distributed under the License is distributed on an "AS IS" BASIS,
931# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
932# See the License for the specific language governing permissions and
933# limitations under the License.
934""",
935 )
936 self.file_mock.return_value = ['file']
937 for header in HEADERS:
938 self.content_mock.return_value = header
Mike Frysinger8a4e8942019-09-16 23:43:49 -0400939 self.assertIsNotNone(pre_upload._check_aosp_license('proj', 'sha1'))
Mike Frysinger98638102014-08-28 00:15:08 -0400940
Ken Turnerd07564b2018-02-08 17:57:59 +1100941 def testIgnoreExcludedPaths(self):
942 """Ignores excluded paths for license checks."""
943 self.file_mock.return_value = ['foo/OWNERS']
Mike Frysinger71e643e2019-09-13 17:26:39 -0400944 self.content_mock.return_value = u'owner@chromium.org'
Mike Frysinger8a4e8942019-09-16 23:43:49 -0400945 self.assertIsNone(pre_upload._check_aosp_license('proj', 'sha1'))
Ken Turnerd07564b2018-02-08 17:57:59 +1100946
Chris McDonald7b63c8e2019-04-25 10:27:27 -0600947 def testIgnoreTopLevelExcludedPaths(self):
948 """Ignores excluded paths for license checks."""
949 self.file_mock.return_value = ['OWNERS']
Mike Frysinger71e643e2019-09-13 17:26:39 -0400950 self.content_mock.return_value = u'owner@chromium.org'
Mike Frysinger8a4e8942019-09-16 23:43:49 -0400951 self.assertIsNone(pre_upload._check_aosp_license('proj', 'sha1'))
952
Mike Frysinger98638102014-08-28 00:15:08 -0400953
Mike Frysingerb2496652019-09-12 23:35:46 -0400954class CheckLayoutConfTestCase(PreUploadTestCase):
Mike Frysingerd7734522015-02-26 16:12:43 -0500955 """Tests for _check_layout_conf."""
956
957 def setUp(self):
958 self.file_mock = self.PatchObject(pre_upload, '_get_affected_files')
959 self.content_mock = self.PatchObject(pre_upload, '_get_file_content')
960
961 def assertAccepted(self, files, project='project', commit='fake sha1'):
962 """Assert _check_layout_conf accepts |files|."""
963 self.file_mock.return_value = files
964 ret = pre_upload._check_layout_conf(project, commit)
Mike Frysinger8a4e8942019-09-16 23:43:49 -0400965 self.assertIsNone(ret, msg='rejected with:\n%s' % ret)
Mike Frysingerd7734522015-02-26 16:12:43 -0500966
967 def assertRejected(self, files, project='project', commit='fake sha1'):
968 """Assert _check_layout_conf rejects |files|."""
969 self.file_mock.return_value = files
970 ret = pre_upload._check_layout_conf(project, commit)
971 self.assertTrue(isinstance(ret, errors.HookFailure))
972
973 def GetLayoutConf(self, filters=()):
974 """Return a valid layout.conf with |filters| lines removed."""
975 all_lines = [
Mike Frysinger71e643e2019-09-13 17:26:39 -0400976 u'masters = portage-stable chromiumos',
977 u'profile-formats = portage-2 profile-default-eapi',
978 u'profile_eapi_when_unspecified = 5-progress',
979 u'repo-name = link',
980 u'thin-manifests = true',
981 u'use-manifests = strict',
Mike Frysingerd7734522015-02-26 16:12:43 -0500982 ]
983
984 lines = []
985 for line in all_lines:
986 for filt in filters:
987 if line.startswith(filt):
988 break
989 else:
990 lines.append(line)
991
Mike Frysinger71e643e2019-09-13 17:26:39 -0400992 return u'\n'.join(lines)
Mike Frysingerd7734522015-02-26 16:12:43 -0500993
994 def testNoFilesToCheck(self):
995 """Don't blow up when there are no layout.conf files."""
996 self.assertAccepted([])
997
998 def testRejectRepoNameFile(self):
999 """If profiles/repo_name is set, kick it out."""
1000 self.assertRejected(['profiles/repo_name'])
1001
1002 def testAcceptValidLayoutConf(self):
1003 """Accept a fully valid layout.conf."""
1004 self.content_mock.return_value = self.GetLayoutConf()
1005 self.assertAccepted(['metadata/layout.conf'])
1006
1007 def testAcceptUnknownKeys(self):
1008 """Accept keys we don't explicitly know about."""
Mike Frysinger71e643e2019-09-13 17:26:39 -04001009 self.content_mock.return_value = self.GetLayoutConf() + u'\nzzz-top = ok'
Mike Frysingerd7734522015-02-26 16:12:43 -05001010 self.assertAccepted(['metadata/layout.conf'])
1011
1012 def testRejectUnsorted(self):
1013 """Reject an unsorted layout.conf."""
Mike Frysinger71e643e2019-09-13 17:26:39 -04001014 self.content_mock.return_value = u'zzz-top = bad\n' + self.GetLayoutConf()
Mike Frysingerd7734522015-02-26 16:12:43 -05001015 self.assertRejected(['metadata/layout.conf'])
1016
1017 def testRejectMissingThinManifests(self):
1018 """Reject a layout.conf missing thin-manifests."""
1019 self.content_mock.return_value = self.GetLayoutConf(
Mike Frysinger71e643e2019-09-13 17:26:39 -04001020 filters=[u'thin-manifests'])
Mike Frysingerd7734522015-02-26 16:12:43 -05001021 self.assertRejected(['metadata/layout.conf'])
1022
1023 def testRejectMissingUseManifests(self):
1024 """Reject a layout.conf missing use-manifests."""
1025 self.content_mock.return_value = self.GetLayoutConf(
Mike Frysinger71e643e2019-09-13 17:26:39 -04001026 filters=[u'use-manifests'])
Mike Frysingerd7734522015-02-26 16:12:43 -05001027 self.assertRejected(['metadata/layout.conf'])
1028
1029 def testRejectMissingEapiFallback(self):
1030 """Reject a layout.conf missing profile_eapi_when_unspecified."""
1031 self.content_mock.return_value = self.GetLayoutConf(
Mike Frysinger71e643e2019-09-13 17:26:39 -04001032 filters=[u'profile_eapi_when_unspecified'])
Mike Frysingerd7734522015-02-26 16:12:43 -05001033 self.assertRejected(['metadata/layout.conf'])
1034
1035 def testRejectMissingRepoName(self):
1036 """Reject a layout.conf missing repo-name."""
Mike Frysinger71e643e2019-09-13 17:26:39 -04001037 self.content_mock.return_value = self.GetLayoutConf(filters=[u'repo-name'])
Mike Frysingerd7734522015-02-26 16:12:43 -05001038 self.assertRejected(['metadata/layout.conf'])
1039
1040
Mike Frysingerb2496652019-09-12 23:35:46 -04001041class CommitMessageTestCase(PreUploadTestCase):
Mike Frysinger4a22bf02014-10-31 13:53:35 -04001042 """Test case for funcs that check commit messages."""
1043
1044 def setUp(self):
1045 self.msg_mock = self.PatchObject(pre_upload, '_get_commit_desc')
1046
1047 @staticmethod
1048 def CheckMessage(_project, _commit):
1049 raise AssertionError('Test class must declare CheckMessage')
1050 # This dummy return is to silence pylint warning W1111 so we don't have to
1051 # enable it for all the call sites below.
1052 return 1 # pylint: disable=W0101
1053
Alex Deymo643ac4c2015-09-03 10:40:50 -07001054 def assertMessageAccepted(self, msg, project=ProjectNamed('project'),
1055 commit='1234'):
Mike Frysinger4a22bf02014-10-31 13:53:35 -04001056 """Assert _check_change_has_bug_field accepts |msg|."""
1057 self.msg_mock.return_value = msg
1058 ret = self.CheckMessage(project, commit)
Mike Frysinger8a4e8942019-09-16 23:43:49 -04001059 self.assertIsNone(ret)
Mike Frysinger4a22bf02014-10-31 13:53:35 -04001060
Alex Deymo643ac4c2015-09-03 10:40:50 -07001061 def assertMessageRejected(self, msg, project=ProjectNamed('project'),
1062 commit='1234'):
Mike Frysinger4a22bf02014-10-31 13:53:35 -04001063 """Assert _check_change_has_bug_field rejects |msg|."""
1064 self.msg_mock.return_value = msg
1065 ret = self.CheckMessage(project, commit)
1066 self.assertTrue(isinstance(ret, errors.HookFailure))
1067
1068
1069class CheckCommitMessageBug(CommitMessageTestCase):
1070 """Tests for _check_change_has_bug_field."""
1071
Alex Deymo643ac4c2015-09-03 10:40:50 -07001072 AOSP_PROJECT = pre_upload.Project(name='overlay', dir='', remote='aosp')
1073 CROS_PROJECT = pre_upload.Project(name='overlay', dir='', remote='cros')
1074
Mike Frysinger4a22bf02014-10-31 13:53:35 -04001075 @staticmethod
1076 def CheckMessage(project, commit):
1077 return pre_upload._check_change_has_bug_field(project, commit)
1078
1079 def testNormal(self):
1080 """Accept a commit message w/a valid BUG."""
Alex Deymo643ac4c2015-09-03 10:40:50 -07001081 self.assertMessageAccepted('\nBUG=chromium:1234\n', self.CROS_PROJECT)
Alex Deymo643ac4c2015-09-03 10:40:50 -07001082 self.assertMessageAccepted('\nBUG=b:1234\n', self.CROS_PROJECT)
1083
1084 self.assertMessageAccepted('\nBug: 1234\n', self.AOSP_PROJECT)
1085 self.assertMessageAccepted('\nBug:1234\n', self.AOSP_PROJECT)
Mike Frysinger4a22bf02014-10-31 13:53:35 -04001086
1087 def testNone(self):
1088 """Accept BUG=None."""
Alex Deymo643ac4c2015-09-03 10:40:50 -07001089 self.assertMessageAccepted('\nBUG=None\n', self.CROS_PROJECT)
1090 self.assertMessageAccepted('\nBUG=none\n', self.CROS_PROJECT)
1091 self.assertMessageAccepted('\nBug: None\n', self.AOSP_PROJECT)
1092 self.assertMessageAccepted('\nBug:none\n', self.AOSP_PROJECT)
1093
1094 for project in (self.AOSP_PROJECT, self.CROS_PROJECT):
1095 self.assertMessageRejected('\nBUG=NONE\n', project)
Mike Frysinger4a22bf02014-10-31 13:53:35 -04001096
1097 def testBlank(self):
1098 """Reject blank values."""
Alex Deymo643ac4c2015-09-03 10:40:50 -07001099 for project in (self.AOSP_PROJECT, self.CROS_PROJECT):
1100 self.assertMessageRejected('\nBUG=\n', project)
1101 self.assertMessageRejected('\nBUG= \n', project)
1102 self.assertMessageRejected('\nBug:\n', project)
1103 self.assertMessageRejected('\nBug: \n', project)
Mike Frysinger4a22bf02014-10-31 13:53:35 -04001104
1105 def testNotFirstLine(self):
1106 """Reject the first line."""
Alex Deymo643ac4c2015-09-03 10:40:50 -07001107 for project in (self.AOSP_PROJECT, self.CROS_PROJECT):
1108 self.assertMessageRejected('BUG=None\n\n\n', project)
Mike Frysinger4a22bf02014-10-31 13:53:35 -04001109
1110 def testNotInline(self):
1111 """Reject not at the start of line."""
Alex Deymo643ac4c2015-09-03 10:40:50 -07001112 for project in (self.AOSP_PROJECT, self.CROS_PROJECT):
1113 self.assertMessageRejected('\n BUG=None\n', project)
1114 self.assertMessageRejected('\n\tBUG=None\n', project)
Mike Frysinger4a22bf02014-10-31 13:53:35 -04001115
1116 def testOldTrackers(self):
1117 """Reject commit messages using old trackers."""
Mike Frysingera2f28252017-10-27 22:26:14 -04001118 self.assertMessageRejected('\nBUG=chrome-os-partner:1234\n',
1119 self.CROS_PROJECT)
Alex Deymo643ac4c2015-09-03 10:40:50 -07001120 self.assertMessageRejected('\nBUG=chromium-os:1234\n', self.CROS_PROJECT)
Mike Frysinger4a22bf02014-10-31 13:53:35 -04001121
1122 def testNoTrackers(self):
1123 """Reject commit messages w/invalid trackers."""
Alex Deymo643ac4c2015-09-03 10:40:50 -07001124 self.assertMessageRejected('\nBUG=booga:1234\n', self.CROS_PROJECT)
1125 self.assertMessageRejected('\nBUG=br:1234\n', self.CROS_PROJECT)
Mike Frysinger4a22bf02014-10-31 13:53:35 -04001126
1127 def testMissing(self):
1128 """Reject commit messages w/no BUG line."""
1129 self.assertMessageRejected('foo\n')
1130
1131 def testCase(self):
1132 """Reject bug lines that are not BUG."""
1133 self.assertMessageRejected('\nbug=none\n')
1134
1135
1136class CheckCommitMessageCqDepend(CommitMessageTestCase):
1137 """Tests for _check_change_has_valid_cq_depend."""
1138
1139 @staticmethod
1140 def CheckMessage(project, commit):
1141 return pre_upload._check_change_has_valid_cq_depend(project, commit)
1142
1143 def testNormal(self):
Jason D. Clinton299e3222019-05-23 09:42:03 -06001144 """Accept valid Cq-Depends line."""
Luigi Semenzatob8c7d7d2019-06-03 09:43:21 -07001145 self.assertMessageAccepted('\nCq-Depend: chromium:1234\nChange-Id: I123')
Mike Frysinger4a22bf02014-10-31 13:53:35 -04001146
1147 def testInvalid(self):
Jason D. Clinton299e3222019-05-23 09:42:03 -06001148 """Reject invalid Cq-Depends line."""
1149 self.assertMessageRejected('\nCq-Depend=chromium=1234\n')
1150 self.assertMessageRejected('\nCq-Depend: None\n')
Luigi Semenzatob8c7d7d2019-06-03 09:43:21 -07001151 self.assertMessageRejected('\nCq-Depend: chromium:1234\n\nChange-Id: I123')
Mike Frysingere39d0cd2019-11-25 13:30:06 -05001152 self.assertMessageRejected('\nCQ-DEPEND=1234\n')
Mike Frysinger4a22bf02014-10-31 13:53:35 -04001153
1154
Bernie Thompsonf8fea992016-01-14 10:27:18 -08001155class CheckCommitMessageContribution(CommitMessageTestCase):
1156 """Tests for _check_change_is_contribution."""
1157
1158 @staticmethod
1159 def CheckMessage(project, commit):
1160 return pre_upload._check_change_is_contribution(project, commit)
1161
1162 def testNormal(self):
1163 """Accept a commit message which is a contribution."""
1164 self.assertMessageAccepted('\nThis is cool code I am contributing.\n')
1165
1166 def testFailureLowerCase(self):
1167 """Deny a commit message which is not a contribution."""
1168 self.assertMessageRejected('\nThis is not a contribution.\n')
1169
1170 def testFailureUpperCase(self):
1171 """Deny a commit message which is not a contribution."""
1172 self.assertMessageRejected('\nNOT A CONTRIBUTION\n')
1173
1174
Mike Frysinger4a22bf02014-10-31 13:53:35 -04001175class CheckCommitMessageTest(CommitMessageTestCase):
1176 """Tests for _check_change_has_test_field."""
1177
1178 @staticmethod
1179 def CheckMessage(project, commit):
1180 return pre_upload._check_change_has_test_field(project, commit)
1181
1182 def testNormal(self):
1183 """Accept a commit message w/a valid TEST."""
1184 self.assertMessageAccepted('\nTEST=i did it\n')
1185
1186 def testNone(self):
1187 """Accept TEST=None."""
1188 self.assertMessageAccepted('\nTEST=None\n')
1189 self.assertMessageAccepted('\nTEST=none\n')
1190
1191 def testBlank(self):
1192 """Reject blank values."""
1193 self.assertMessageRejected('\nTEST=\n')
1194 self.assertMessageRejected('\nTEST= \n')
1195
1196 def testNotFirstLine(self):
1197 """Reject the first line."""
1198 self.assertMessageRejected('TEST=None\n\n\n')
1199
1200 def testNotInline(self):
1201 """Reject not at the start of line."""
1202 self.assertMessageRejected('\n TEST=None\n')
1203 self.assertMessageRejected('\n\tTEST=None\n')
1204
1205 def testMissing(self):
1206 """Reject commit messages w/no TEST line."""
1207 self.assertMessageRejected('foo\n')
1208
1209 def testCase(self):
1210 """Reject bug lines that are not TEST."""
1211 self.assertMessageRejected('\ntest=none\n')
1212
1213
1214class CheckCommitMessageChangeId(CommitMessageTestCase):
1215 """Tests for _check_change_has_proper_changeid."""
1216
1217 @staticmethod
1218 def CheckMessage(project, commit):
1219 return pre_upload._check_change_has_proper_changeid(project, commit)
1220
1221 def testNormal(self):
1222 """Accept a commit message w/a valid Change-Id."""
1223 self.assertMessageAccepted('foo\n\nChange-Id: I1234\n')
1224
1225 def testBlank(self):
1226 """Reject blank values."""
1227 self.assertMessageRejected('\nChange-Id:\n')
1228 self.assertMessageRejected('\nChange-Id: \n')
1229
1230 def testNotFirstLine(self):
1231 """Reject the first line."""
1232 self.assertMessageRejected('TEST=None\n\n\n')
1233
1234 def testNotInline(self):
1235 """Reject not at the start of line."""
1236 self.assertMessageRejected('\n Change-Id: I1234\n')
1237 self.assertMessageRejected('\n\tChange-Id: I1234\n')
1238
1239 def testMissing(self):
1240 """Reject commit messages missing the line."""
1241 self.assertMessageRejected('foo\n')
1242
1243 def testCase(self):
1244 """Reject bug lines that are not Change-Id."""
1245 self.assertMessageRejected('\nchange-id: I1234\n')
1246 self.assertMessageRejected('\nChange-id: I1234\n')
1247 self.assertMessageRejected('\nChange-ID: I1234\n')
1248
1249 def testEnd(self):
1250 """Reject Change-Id's that are not last."""
1251 self.assertMessageRejected('\nChange-Id: I1234\nbar\n')
1252
Mike Frysinger02b88bd2014-11-21 00:29:38 -05001253 def testSobTag(self):
1254 """Permit s-o-b tags to follow the Change-Id."""
1255 self.assertMessageAccepted('foo\n\nChange-Id: I1234\nSigned-off-by: Hi\n')
1256
LaMont Jones237f3ef2020-01-22 10:40:52 -07001257 def testCqClTag(self):
1258 """Permit Cq-Cl-Tag tags to follow the Change-Id."""
1259 self.assertMessageAccepted('foo\n\nChange-Id: I1234\nCq-Cl-Tag: Hi\n')
1260
Mike Frysinger4a22bf02014-10-31 13:53:35 -04001261
Jack Neus8edbf642019-07-10 16:08:31 -06001262class CheckCommitMessageNoOEM(CommitMessageTestCase):
1263 """Tests for _check_change_no_include_oem."""
1264
1265 @staticmethod
1266 def CheckMessage(project, commit):
1267 return pre_upload._check_change_no_include_oem(project, commit)
1268
1269 def testNormal(self):
1270 """Accept a commit message w/o reference to an OEM/ODM."""
1271 self.assertMessageAccepted('foo\n')
1272
1273 def testHasOEM(self):
1274 """Catch commit messages referencing OEMs."""
1275 self.assertMessageRejected('HP Project\n\n')
1276 self.assertMessageRejected('hewlett-packard\n')
1277 self.assertMessageRejected('Hewlett\nPackard\n')
1278 self.assertMessageRejected('Dell Chromebook\n\n\n')
1279 self.assertMessageRejected('product@acer.com\n')
1280 self.assertMessageRejected('This is related to Asus\n')
1281 self.assertMessageRejected('lenovo machine\n')
1282
1283 def testHasODM(self):
1284 """Catch commit messages referencing ODMs."""
1285 self.assertMessageRejected('new samsung laptop\n\n')
1286 self.assertMessageRejected('pegatron(ems) project\n')
1287 self.assertMessageRejected('new Wistron device\n')
1288
1289 def testContainsOEM(self):
1290 """Check that the check handles word boundaries properly."""
1291 self.assertMessageAccepted('oheahpohea')
1292 self.assertMessageAccepted('Play an Asus7 guitar chord.\n\n')
1293
1294 def testTag(self):
1295 """Check that the check ignores tags."""
1296 self.assertMessageAccepted(
1297 'Harmless project\n'
1298 'Reviewed-by: partner@asus.corp-partner.google.com\n'
1299 'Tested-by: partner@hp.corp-partner.google.com\n'
1300 'Signed-off-by: partner@dell.corp-partner.google.com\n'
1301 'Commit-Queue: partner@lenovo.corp-partner.google.com\n'
Jack Neus8edbf642019-07-10 16:08:31 -06001302 'CC: partner@acer.corp-partner.google.com\n'
1303 'Acked-by: partner@hewlett-packard.corp-partner.google.com\n')
1304 self.assertMessageRejected(
1305 'Asus project\n'
1306 'Reviewed-by: partner@asus.corp-partner.google.com')
Mike Frysingerbb34a222019-07-31 14:40:46 -04001307 self.assertMessageRejected(
1308 'my project\n'
1309 'Bad-tag: partner@asus.corp-partner.google.com')
Jack Neus8edbf642019-07-10 16:08:31 -06001310
1311
Mike Frysinger36b2ebc2014-10-31 14:02:03 -04001312class CheckCommitMessageStyle(CommitMessageTestCase):
1313 """Tests for _check_commit_message_style."""
1314
1315 @staticmethod
1316 def CheckMessage(project, commit):
1317 return pre_upload._check_commit_message_style(project, commit)
1318
1319 def testNormal(self):
1320 """Accept valid commit messages."""
1321 self.assertMessageAccepted('one sentence.\n')
1322 self.assertMessageAccepted('some.module: do it!\n')
1323 self.assertMessageAccepted('one line\n\nmore stuff here.')
1324
1325 def testNoBlankSecondLine(self):
1326 """Reject messages that have stuff on the second line."""
1327 self.assertMessageRejected('one sentence.\nbad fish!\n')
1328
1329 def testFirstLineMultipleSentences(self):
1330 """Reject messages that have more than one sentence in the summary."""
1331 self.assertMessageRejected('one sentence. two sentence!\n')
1332
1333 def testFirstLineTooLone(self):
1334 """Reject first lines that are too long."""
1335 self.assertMessageRejected('o' * 200)
1336
1337
Mike Frysinger292b45d2014-11-25 01:17:10 -05001338def DiffEntry(src_file=None, dst_file=None, src_mode=None, dst_mode='100644',
1339 status='M'):
1340 """Helper to create a stub RawDiffEntry object"""
1341 if src_mode is None:
1342 if status == 'A':
1343 src_mode = '000000'
1344 elif status == 'M':
1345 src_mode = dst_mode
1346 elif status == 'D':
1347 src_mode = dst_mode
1348 dst_mode = '000000'
1349
1350 src_sha = dst_sha = 'abc'
1351 if status == 'D':
1352 dst_sha = '000000'
1353 elif status == 'A':
1354 src_sha = '000000'
1355
1356 return git.RawDiffEntry(src_mode=src_mode, dst_mode=dst_mode, src_sha=src_sha,
1357 dst_sha=dst_sha, status=status, score=None,
1358 src_file=src_file, dst_file=dst_file)
1359
1360
Mike Frysingerb2496652019-09-12 23:35:46 -04001361class HelpersTest(PreUploadTestCase, cros_test_lib.TempDirTestCase):
Mike Frysingerd3bd32c2014-11-24 23:34:29 -05001362 """Various tests for utility functions."""
1363
Daniel Erate3ea3fc2015-02-13 15:27:52 -07001364 def setUp(self):
1365 self.orig_cwd = os.getcwd()
1366 os.chdir(self.tempdir)
1367
Mike Frysingerd3bd32c2014-11-24 23:34:29 -05001368 self.PatchObject(git, 'RawDiff', return_value=[
1369 # A modified normal file.
Mike Frysinger292b45d2014-11-25 01:17:10 -05001370 DiffEntry(src_file='buildbot/constants.py', status='M'),
Mike Frysingerd3bd32c2014-11-24 23:34:29 -05001371 # A new symlink file.
Mike Frysinger292b45d2014-11-25 01:17:10 -05001372 DiffEntry(dst_file='scripts/cros_env_whitelist', dst_mode='120000',
1373 status='A'),
Mike Frysingerd3bd32c2014-11-24 23:34:29 -05001374 # A deleted file.
Mike Frysinger292b45d2014-11-25 01:17:10 -05001375 DiffEntry(src_file='scripts/sync_sonic.py', status='D'),
Mike Frysingerd3bd32c2014-11-24 23:34:29 -05001376 ])
1377
Daniel Erate3ea3fc2015-02-13 15:27:52 -07001378 def tearDown(self):
1379 os.chdir(self.orig_cwd)
1380
1381 def _WritePresubmitIgnoreFile(self, subdir, data):
1382 """Writes to a .presubmitignore file in the passed-in subdirectory."""
1383 directory = os.path.join(self.tempdir, subdir)
1384 if not os.path.exists(directory):
1385 os.makedirs(directory)
1386 osutils.WriteFile(os.path.join(directory, pre_upload._IGNORE_FILE), data)
1387
Mike Frysingerd3bd32c2014-11-24 23:34:29 -05001388 def testGetAffectedFilesNoDeletesNoRelative(self):
1389 """Verify _get_affected_files() works w/no delete & not relative."""
Mike Frysingerd3bd32c2014-11-24 23:34:29 -05001390 path = os.getcwd()
1391 files = pre_upload._get_affected_files('HEAD', include_deletes=False,
1392 relative=False)
1393 exp_files = [os.path.join(path, 'buildbot/constants.py')]
Mike Frysinger8a4e8942019-09-16 23:43:49 -04001394 self.assertEqual(files, exp_files)
Mike Frysingerd3bd32c2014-11-24 23:34:29 -05001395
1396 def testGetAffectedFilesDeletesNoRelative(self):
1397 """Verify _get_affected_files() works w/delete & not relative."""
Mike Frysingerd3bd32c2014-11-24 23:34:29 -05001398 path = os.getcwd()
1399 files = pre_upload._get_affected_files('HEAD', include_deletes=True,
1400 relative=False)
1401 exp_files = [os.path.join(path, 'buildbot/constants.py'),
1402 os.path.join(path, 'scripts/sync_sonic.py')]
Mike Frysinger8a4e8942019-09-16 23:43:49 -04001403 self.assertEqual(files, exp_files)
Mike Frysingerd3bd32c2014-11-24 23:34:29 -05001404
1405 def testGetAffectedFilesNoDeletesRelative(self):
1406 """Verify _get_affected_files() works w/no delete & relative."""
Mike Frysingerd3bd32c2014-11-24 23:34:29 -05001407 files = pre_upload._get_affected_files('HEAD', include_deletes=False,
1408 relative=True)
1409 exp_files = ['buildbot/constants.py']
Mike Frysinger8a4e8942019-09-16 23:43:49 -04001410 self.assertEqual(files, exp_files)
Mike Frysingerd3bd32c2014-11-24 23:34:29 -05001411
1412 def testGetAffectedFilesDeletesRelative(self):
1413 """Verify _get_affected_files() works w/delete & relative."""
Mike Frysingerd3bd32c2014-11-24 23:34:29 -05001414 files = pre_upload._get_affected_files('HEAD', include_deletes=True,
1415 relative=True)
1416 exp_files = ['buildbot/constants.py', 'scripts/sync_sonic.py']
Mike Frysinger8a4e8942019-09-16 23:43:49 -04001417 self.assertEqual(files, exp_files)
Mike Frysingerd3bd32c2014-11-24 23:34:29 -05001418
Mike Frysinger292b45d2014-11-25 01:17:10 -05001419 def testGetAffectedFilesDetails(self):
1420 """Verify _get_affected_files() works w/full_details."""
Mike Frysinger292b45d2014-11-25 01:17:10 -05001421 files = pre_upload._get_affected_files('HEAD', full_details=True,
1422 relative=True)
Mike Frysinger8a4e8942019-09-16 23:43:49 -04001423 self.assertEqual(files[0].src_file, 'buildbot/constants.py')
Mike Frysinger292b45d2014-11-25 01:17:10 -05001424
Daniel Erate3ea3fc2015-02-13 15:27:52 -07001425 def testGetAffectedFilesPresubmitIgnoreDirectory(self):
1426 """Verify .presubmitignore can be used to exclude a directory."""
1427 self._WritePresubmitIgnoreFile('.', 'buildbot/')
Mike Frysinger8a4e8942019-09-16 23:43:49 -04001428 self.assertEqual(pre_upload._get_affected_files('HEAD', relative=True), [])
Daniel Erate3ea3fc2015-02-13 15:27:52 -07001429
1430 def testGetAffectedFilesPresubmitIgnoreDirectoryWildcard(self):
1431 """Verify .presubmitignore can be used with a directory wildcard."""
1432 self._WritePresubmitIgnoreFile('.', '*/constants.py')
Mike Frysinger8a4e8942019-09-16 23:43:49 -04001433 self.assertEqual(pre_upload._get_affected_files('HEAD', relative=True), [])
Daniel Erate3ea3fc2015-02-13 15:27:52 -07001434
1435 def testGetAffectedFilesPresubmitIgnoreWithinDirectory(self):
1436 """Verify .presubmitignore can be placed in a subdirectory."""
1437 self._WritePresubmitIgnoreFile('buildbot', '*.py')
Mike Frysinger8a4e8942019-09-16 23:43:49 -04001438 self.assertEqual(pre_upload._get_affected_files('HEAD', relative=True), [])
Daniel Erate3ea3fc2015-02-13 15:27:52 -07001439
1440 def testGetAffectedFilesPresubmitIgnoreDoesntMatch(self):
1441 """Verify .presubmitignore has no effect when it doesn't match a file."""
1442 self._WritePresubmitIgnoreFile('buildbot', '*.txt')
Mike Frysinger8a4e8942019-09-16 23:43:49 -04001443 self.assertEqual(pre_upload._get_affected_files('HEAD', relative=True),
1444 ['buildbot/constants.py'])
Daniel Erate3ea3fc2015-02-13 15:27:52 -07001445
1446 def testGetAffectedFilesPresubmitIgnoreAddedFile(self):
1447 """Verify .presubmitignore matches added files."""
1448 self._WritePresubmitIgnoreFile('.', 'buildbot/\nscripts/')
Mike Frysinger8a4e8942019-09-16 23:43:49 -04001449 self.assertEqual(pre_upload._get_affected_files('HEAD', relative=True,
1450 include_symlinks=True),
1451 [])
Daniel Erate3ea3fc2015-02-13 15:27:52 -07001452
1453 def testGetAffectedFilesPresubmitIgnoreSkipIgnoreFile(self):
1454 """Verify .presubmitignore files are automatically skipped."""
1455 self.PatchObject(git, 'RawDiff', return_value=[
1456 DiffEntry(src_file='.presubmitignore', status='M')
1457 ])
Mike Frysinger8a4e8942019-09-16 23:43:49 -04001458 self.assertEqual(pre_upload._get_affected_files('HEAD', relative=True), [])
Mike Frysinger292b45d2014-11-25 01:17:10 -05001459
Mike Frysingerf9d41b32017-02-23 15:20:04 -05001460
Mike Frysingerb2496652019-09-12 23:35:46 -04001461class ExecFilesTest(PreUploadTestCase):
Mike Frysingerf9d41b32017-02-23 15:20:04 -05001462 """Tests for _check_exec_files."""
1463
1464 def setUp(self):
1465 self.diff_mock = self.PatchObject(git, 'RawDiff')
1466
1467 def testNotExec(self):
1468 """Do not flag files that are not executable."""
1469 self.diff_mock.return_value = [
1470 DiffEntry(src_file='make.conf', dst_mode='100644', status='A'),
1471 ]
Mike Frysinger8a4e8942019-09-16 23:43:49 -04001472 self.assertIsNone(pre_upload._check_exec_files('proj', 'commit'))
Mike Frysingerf9d41b32017-02-23 15:20:04 -05001473
1474 def testExec(self):
1475 """Flag files that are executable."""
1476 self.diff_mock.return_value = [
1477 DiffEntry(src_file='make.conf', dst_mode='100755', status='A'),
1478 ]
Mike Frysinger8a4e8942019-09-16 23:43:49 -04001479 self.assertIsNotNone(pre_upload._check_exec_files('proj', 'commit'))
Mike Frysingerf9d41b32017-02-23 15:20:04 -05001480
1481 def testDeletedExec(self):
1482 """Ignore bad files that are being deleted."""
1483 self.diff_mock.return_value = [
1484 DiffEntry(src_file='make.conf', dst_mode='100755', status='D'),
1485 ]
Mike Frysinger8a4e8942019-09-16 23:43:49 -04001486 self.assertIsNone(pre_upload._check_exec_files('proj', 'commit'))
Mike Frysingerf9d41b32017-02-23 15:20:04 -05001487
1488 def testModifiedExec(self):
1489 """Flag bad files that weren't exec, but now are."""
1490 self.diff_mock.return_value = [
1491 DiffEntry(src_file='make.conf', src_mode='100644', dst_mode='100755',
1492 status='M'),
1493 ]
Mike Frysinger8a4e8942019-09-16 23:43:49 -04001494 self.assertIsNotNone(pre_upload._check_exec_files('proj', 'commit'))
Mike Frysingerf9d41b32017-02-23 15:20:04 -05001495
1496 def testNormalExec(self):
1497 """Don't flag normal files (e.g. scripts) that are executable."""
1498 self.diff_mock.return_value = [
1499 DiffEntry(src_file='foo.sh', dst_mode='100755', status='A'),
1500 ]
Mike Frysinger8a4e8942019-09-16 23:43:49 -04001501 self.assertIsNone(pre_upload._check_exec_files('proj', 'commit'))
Mike Frysingerf9d41b32017-02-23 15:20:04 -05001502
1503
Mike Frysingerb2496652019-09-12 23:35:46 -04001504class CheckForUprev(PreUploadTestCase, cros_test_lib.TempDirTestCase):
Mike Frysinger292b45d2014-11-25 01:17:10 -05001505 """Tests for _check_for_uprev."""
1506
1507 def setUp(self):
1508 self.file_mock = self.PatchObject(git, 'RawDiff')
1509
1510 def _Files(self, files):
1511 """Create |files| in the tempdir and return full paths to them."""
1512 for obj in files:
1513 if obj.status == 'D':
1514 continue
1515 if obj.dst_file is None:
1516 f = obj.src_file
1517 else:
1518 f = obj.dst_file
1519 osutils.Touch(os.path.join(self.tempdir, f), makedirs=True)
1520 return files
1521
1522 def assertAccepted(self, files, project='project', commit='fake sha1'):
1523 """Assert _check_for_uprev accepts |files|."""
1524 self.file_mock.return_value = self._Files(files)
Alex Deymo643ac4c2015-09-03 10:40:50 -07001525 ret = pre_upload._check_for_uprev(ProjectNamed(project), commit,
1526 project_top=self.tempdir)
Mike Frysinger8a4e8942019-09-16 23:43:49 -04001527 self.assertIsNone(ret)
Mike Frysinger292b45d2014-11-25 01:17:10 -05001528
1529 def assertRejected(self, files, project='project', commit='fake sha1'):
1530 """Assert _check_for_uprev rejects |files|."""
1531 self.file_mock.return_value = self._Files(files)
Alex Deymo643ac4c2015-09-03 10:40:50 -07001532 ret = pre_upload._check_for_uprev(ProjectNamed(project), commit,
1533 project_top=self.tempdir)
Mike Frysinger292b45d2014-11-25 01:17:10 -05001534 self.assertTrue(isinstance(ret, errors.HookFailure))
1535
1536 def testWhitelistOverlay(self):
1537 """Skip checks on whitelisted overlays."""
1538 self.assertAccepted([DiffEntry(src_file='cat/pkg/pkg-0.ebuild')],
1539 project='chromiumos/overlays/portage-stable')
1540
1541 def testWhitelistFiles(self):
1542 """Skip checks on whitelisted files."""
1543 files = ['ChangeLog', 'Manifest', 'metadata.xml']
1544 self.assertAccepted([DiffEntry(src_file=os.path.join('c', 'p', x),
1545 status='M')
1546 for x in files])
1547
1548 def testRejectBasic(self):
1549 """Reject ebuilds missing uprevs."""
1550 self.assertRejected([DiffEntry(src_file='c/p/p-0.ebuild', status='M')])
1551
1552 def testNewPackage(self):
1553 """Accept new ebuilds w/out uprevs."""
1554 self.assertAccepted([DiffEntry(src_file='c/p/p-0.ebuild', status='A')])
1555 self.assertAccepted([DiffEntry(src_file='c/p/p-0-r12.ebuild', status='A')])
1556
1557 def testModifiedFilesOnly(self):
1558 """Reject ebuilds w/out uprevs and changes in files/."""
1559 osutils.Touch(os.path.join(self.tempdir, 'cat/pkg/pkg-0.ebuild'),
1560 makedirs=True)
1561 self.assertRejected([DiffEntry(src_file='cat/pkg/files/f', status='A')])
1562 self.assertRejected([DiffEntry(src_file='cat/pkg/files/g', status='M')])
1563
1564 def testFilesNoEbuilds(self):
1565 """Ignore changes to paths w/out ebuilds."""
1566 self.assertAccepted([DiffEntry(src_file='cat/pkg/files/f', status='A')])
1567 self.assertAccepted([DiffEntry(src_file='cat/pkg/files/g', status='M')])
1568
1569 def testModifiedFilesWithUprev(self):
1570 """Accept ebuilds w/uprevs and changes in files/."""
1571 self.assertAccepted([DiffEntry(src_file='c/p/files/f', status='A'),
1572 DiffEntry(src_file='c/p/p-0.ebuild', status='A')])
1573 self.assertAccepted([
1574 DiffEntry(src_file='c/p/files/f', status='M'),
1575 DiffEntry(src_file='c/p/p-0-r1.ebuild', src_mode='120000',
1576 dst_file='c/p/p-0-r2.ebuild', dst_mode='120000', status='R')])
1577
Gwendal Grignoua3086c32014-12-09 11:17:22 -08001578 def testModifiedFilesWith9999(self):
1579 """Accept 9999 ebuilds and changes in files/."""
1580 self.assertAccepted([DiffEntry(src_file='c/p/files/f', status='M'),
1581 DiffEntry(src_file='c/p/p-9999.ebuild', status='M')])
1582
C Shapiroae157ae2017-09-18 16:24:03 -06001583 def testModifiedFilesIn9999SubDirWithout9999Change(self):
1584 """Accept changes in files/ with a parent 9999 ebuild"""
1585 ebuild_9999_file = os.path.join(self.tempdir, 'c/p/p-9999.ebuild')
1586 os.makedirs(os.path.dirname(ebuild_9999_file))
1587 osutils.WriteFile(ebuild_9999_file, 'fake')
1588 self.assertAccepted([DiffEntry(src_file='c/p/files/f', status='M')])
1589
Mike Frysingerd3bd32c2014-11-24 23:34:29 -05001590
Mike Frysingerb2496652019-09-12 23:35:46 -04001591class DirectMainTest(PreUploadTestCase, cros_test_lib.TempDirTestCase):
Mike Frysinger55f85b52014-12-18 14:45:21 -05001592 """Tests for direct_main()"""
1593
1594 def setUp(self):
1595 self.hooks_mock = self.PatchObject(pre_upload, '_run_project_hooks',
1596 return_value=None)
1597
1598 def testNoArgs(self):
1599 """If run w/no args, should check the current dir."""
1600 ret = pre_upload.direct_main([])
1601 self.assertEqual(ret, 0)
1602 self.hooks_mock.assert_called_once_with(
1603 mock.ANY, proj_dir=os.getcwd(), commit_list=[], presubmit=mock.ANY)
1604
1605 def testExplicitDir(self):
1606 """Verify we can run on a diff dir."""
1607 # Use the chromite dir since we know it exists.
1608 ret = pre_upload.direct_main(['--dir', constants.CHROMITE_DIR])
1609 self.assertEqual(ret, 0)
1610 self.hooks_mock.assert_called_once_with(
1611 mock.ANY, proj_dir=constants.CHROMITE_DIR, commit_list=[],
1612 presubmit=mock.ANY)
1613
1614 def testBogusProject(self):
1615 """A bogus project name should be fine (use default settings)."""
1616 # Use the chromite dir since we know it exists.
1617 ret = pre_upload.direct_main(['--dir', constants.CHROMITE_DIR,
1618 '--project', 'foooooooooo'])
1619 self.assertEqual(ret, 0)
1620 self.hooks_mock.assert_called_once_with(
1621 'foooooooooo', proj_dir=constants.CHROMITE_DIR, commit_list=[],
1622 presubmit=mock.ANY)
1623
1624 def testBogustProjectNoDir(self):
1625 """Make sure --dir is detected even with --project."""
1626 ret = pre_upload.direct_main(['--project', 'foooooooooo'])
1627 self.assertEqual(ret, 0)
1628 self.hooks_mock.assert_called_once_with(
1629 'foooooooooo', proj_dir=os.getcwd(), commit_list=[],
1630 presubmit=mock.ANY)
1631
1632 def testNoGitDir(self):
1633 """We should die when run on a non-git dir."""
1634 self.assertRaises(pre_upload.BadInvocation, pre_upload.direct_main,
1635 ['--dir', self.tempdir])
1636
1637 def testNoDir(self):
1638 """We should die when run on a missing dir."""
1639 self.assertRaises(pre_upload.BadInvocation, pre_upload.direct_main,
1640 ['--dir', os.path.join(self.tempdir, 'foooooooo')])
1641
1642 def testCommitList(self):
1643 """Any args on the command line should be treated as commits."""
1644 commits = ['sha1', 'sha2', 'shaaaaaaaaaaaan']
1645 ret = pre_upload.direct_main(commits)
1646 self.assertEqual(ret, 0)
1647 self.hooks_mock.assert_called_once_with(
1648 mock.ANY, proj_dir=mock.ANY, commit_list=commits, presubmit=mock.ANY)
1649
1650
Mike Frysingerb2496652019-09-12 23:35:46 -04001651class CheckRustfmtTest(PreUploadTestCase):
Fletcher Woodruffce1cb1b2019-08-16 15:59:32 -06001652 """Tests for _check_rustfmt."""
1653
1654 def setUp(self):
1655 self.content_mock = self.PatchObject(pre_upload, '_get_file_content')
1656
1657 def testBadRustFile(self):
1658 self.PatchObject(pre_upload, '_get_affected_files', return_value=['a.rs'])
1659 # Bad because it's missing trailing newline.
Mike Frysingere85b0062019-08-20 15:10:33 -04001660 content = 'fn main() {}'
1661 self.content_mock.return_value = content
1662 self.PatchObject(pre_upload, '_run_command', return_value=content + '\n')
Fletcher Woodruffce1cb1b2019-08-16 15:59:32 -06001663 failure = pre_upload._check_rustfmt(ProjectNamed('PROJECT'), 'COMMIT')
1664 self.assertIsNotNone(failure)
Mike Frysinger8a4e8942019-09-16 23:43:49 -04001665 self.assertEqual('Files not formatted with rustfmt: '
1666 "(run 'cargo fmt' to fix)",
1667 failure.msg)
1668 self.assertEqual(['a.rs'], failure.items)
Fletcher Woodruffce1cb1b2019-08-16 15:59:32 -06001669
1670 def testGoodRustFile(self):
1671 self.PatchObject(pre_upload, '_get_affected_files', return_value=['a.rs'])
Mike Frysingere85b0062019-08-20 15:10:33 -04001672 content = 'fn main() {}\n'
1673 self.content_mock.return_value = content
1674 self.PatchObject(pre_upload, '_run_command', return_value=content)
Fletcher Woodruffce1cb1b2019-08-16 15:59:32 -06001675 failure = pre_upload._check_rustfmt(ProjectNamed('PROJECT'), 'COMMIT')
1676 self.assertIsNone(failure)
1677
1678 def testFilterNonRustFiles(self):
1679 self.PatchObject(pre_upload, '_get_affected_files',
1680 return_value=['a.cc', 'a.rsa', 'a.irs', 'rs.cc'])
1681 self.content_mock.return_value = 'fn main() {\n}'
1682 failure = pre_upload._check_rustfmt(ProjectNamed('PROJECT'), 'COMMIT')
1683 self.assertIsNone(failure)
1684
1685
Jon Salz98255932012-08-18 14:48:02 +08001686if __name__ == '__main__':
Mike Frysinger884a8dd2015-05-17 03:43:43 -04001687 cros_test_lib.main(module=__name__)