Add presubmit for kernel config/code changes

We don't allow kernel configs to be changed at the same time as code,
and to avoid having to comment on it on code review, add a presubmit
hook to check for it.

BUG=chromium-os:34143
TEST=run new unit test

Change-Id: Ia8e824d79235c1913276e8b9b6cca659943ab85d
Reviewed-on: https://gerrit.chromium.org/gerrit/32187
Commit-Ready: Olof Johansson <olofj@chromium.org>
Tested-by: Olof Johansson <olofj@chromium.org>
Reviewed-by: Mike Frysinger <vapier@chromium.org>
diff --git a/pre-upload.py b/pre-upload.py
index 7be4ea7..d913d2b 100755
--- a/pre-upload.py
+++ b/pre-upload.py
@@ -359,6 +359,12 @@
 def _run_checkpatch_no_tree(project, commit):
   return _run_checkpatch(project, commit, ['--no-tree'])
 
+def _kernel_configcheck(project, commit):
+  """Makes sure kernel config changes are not mixed with code changes"""
+  files = _get_affected_files(commit)
+  if not len(_filter_files(files, [r'chromeos/config'])) in [0, len(files)]:
+    return HookFailure('Changes to chromeos/config/ and regular files must '
+                       'be in separate commits:\n%s' % '\n'.join(files))
 
 def _run_json_check(project, commit):
   """Checks that all JSON files are syntactically valid."""
@@ -426,8 +432,9 @@
 # A dictionary of project-specific hooks(callbacks), indexed by project name.
 # dict[project] = [callback1, callback2]
 _PROJECT_SPECIFIC_HOOKS = {
-    "chromiumos/third_party/kernel": [_run_checkpatch],
-    "chromiumos/third_party/kernel-next": [_run_checkpatch],
+    "chromiumos/third_party/kernel": [_run_checkpatch, _kernel_configcheck],
+    "chromiumos/third_party/kernel-next": [_run_checkpatch,
+                                           _kernel_configcheck],
     "chromiumos/third_party/u-boot": [_run_checkpatch_no_tree,
                                       _check_change_has_branch_field],
     "chromiumos/platform/ec": [_run_checkpatch_no_tree,
diff --git a/pre-upload_unittest.py b/pre-upload_unittest.py
index 5c4398d..946b4a4 100755
--- a/pre-upload_unittest.py
+++ b/pre-upload_unittest.py
@@ -56,6 +56,44 @@
                        for line in [3, 4, 8]],
                       failure.items)
 
+class CheckKernelConfig(unittest.TestCase):
+  def tearDown(self):
+    self.mocker.UnsetStubs()
+
+  def runTest(self):
+    self.mocker = mox.Mox();
+
+    # Mixed changes, should fail
+    self.mocker.StubOutWithMock(pre_upload, '_get_affected_files')
+    pre_upload._get_affected_files(mox.IgnoreArg()).AndReturn(
+        ['/kernel/files/chromeos/config/base.config',
+         '/kernel/files/arch/arm/mach-exynos/mach-exynos5-dt.c'
+        ])
+    self.mocker.ReplayAll()
+    failure = pre_upload._kernel_configcheck('PROJECT', 'COMMIT')
+    self.assertTrue(failure)
+
+    # Code-only changes, should pass
+    self.mocker.UnsetStubs()
+    self.mocker.StubOutWithMock(pre_upload, '_get_affected_files')
+    pre_upload._get_affected_files(mox.IgnoreArg()).AndReturn(
+        ['/kernel/files/Makefile',
+         '/kernel/files/arch/arm/mach-exynos/mach-exynos5-dt.c'
+        ])
+    self.mocker.ReplayAll()
+    failure = pre_upload._kernel_configcheck('PROJECT', 'COMMIT')
+    self.assertFalse(failure)
+
+    # Config-only changes, should pass
+    self.mocker.UnsetStubs()
+    self.mocker.StubOutWithMock(pre_upload, '_get_affected_files')
+    pre_upload._get_affected_files(mox.IgnoreArg()).AndReturn(
+        ['/kernel/files/chromeos/config/base.config',
+        ])
+    self.mocker.ReplayAll()
+    failure = pre_upload._kernel_configcheck('PROJECT', 'COMMIT')
+    self.assertFalse(failure)
+
 
 if __name__ == '__main__':
   unittest.main()