Add ability to restrict jobs to specific files.

Add an additional job parameter, 'file', that will cause that
job to only run if the change touches files that match the
specification.

Change-Id: I8c8fd3d029e02e338fd1dd266443b9ac56c0e5ac
Reviewed-on: https://review.openstack.org/23710
Reviewed-by: Clark Boylan <clark.boylan@gmail.com>
Reviewed-by: Jeremy Stanley <fungi@yuggoth.org>
Reviewed-by: Monty Taylor <mordred@inaugust.com>
Approved: James E. Blair <corvus@inaugust.com>
Tested-by: Jenkins
diff --git a/tests/fixtures/layout.yaml b/tests/fixtures/layout.yaml
index cab97b9..5276d83 100644
--- a/tests/fixtures/layout.yaml
+++ b/tests/fixtures/layout.yaml
@@ -43,6 +43,9 @@
     hold-following-changes: true
   - name: nonvoting-project-test2
     voting: false
+  - name: project-testfile
+    files:
+      - '.*-requires'
 
 projects:
   - name: org/project
@@ -51,10 +54,12 @@
       - project-merge:
         - project-test1
         - project-test2
+        - project-testfile
     gate:
       - project-merge:
         - project-test1
         - project-test2
+        - project-testfile
     post:
       - project-post
 
diff --git a/tests/fixtures/layouts/good_layout.yaml b/tests/fixtures/layouts/good_layout.yaml
index 112d2a6..76a76d9 100644
--- a/tests/fixtures/layouts/good_layout.yaml
+++ b/tests/fixtures/layouts/good_layout.yaml
@@ -45,6 +45,9 @@
   - name: test-merge2
     success-pattern: http://logs.example.com/{change.number}/{change.patchset}/{pipeline.name}/{job.name}/{build.number}/success
     failure-pattern: http://logs.example.com/{change.number}/{change.patchset}/{pipeline.name}/{job.name}/{build.number}/fail
+  - name: project-testfile
+    files:
+      - 'tools/.*-requires'
 
 projects:
   - name: test-org/test
diff --git a/tests/test_scheduler.py b/tests/test_scheduler.py
index 8c7b211..5fc221a 100644
--- a/tests/test_scheduler.py
+++ b/tests/test_scheduler.py
@@ -192,7 +192,7 @@
         self.addPatchset()
         self.data['submitRecords'] = self.getSubmitRecords()
 
-    def addPatchset(self, files=None, large=False):
+    def addPatchset(self, files=[], large=False):
         self.latest_patchset += 1
         if files:
             fn = files[0]
@@ -202,12 +202,15 @@
         c = add_fake_change_to_repo(self.project, self.branch,
                                     self.number, self.latest_patchset,
                                     msg, fn, large)
+        ps_files = [{'file': '/COMMIT_MSG',
+                     'type': 'ADDED'},
+                    {'file': 'README',
+                     'type': 'MODIFIED'}]
+        for f in files:
+            ps_files.append({'file': f, 'type': 'ADDED'})
         d = {'approvals': [],
              'createdOn': time.time(),
-             'files': [{'file': '/COMMIT_MSG',
-                        'type': 'ADDED'},
-                       {'file': 'README',
-                        'type': 'MODIFIED'}],
+             'files': ps_files,
              'number': str(self.latest_patchset),
              'ref': 'refs/changes/1/%s/%s' % (self.number,
                                               self.latest_patchset),
@@ -1966,3 +1969,28 @@
         assert D.data['status'] == 'MERGED'
         assert D.reported == 2
         self.assertEmptyQueues()
+
+    def test_file_jobs(self):
+        "Test that file jobs run only when appropriate"
+        A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
+        A.addPatchset(['pip-requires'])
+        B = self.fake_gerrit.addFakeChange('org/project', 'master', 'B')
+        A.addApproval('CRVW', 2)
+        B.addApproval('CRVW', 2)
+        self.fake_gerrit.addEvent(A.addApproval('APRV', 1))
+        self.fake_gerrit.addEvent(B.addApproval('APRV', 1))
+        self.waitUntilSettled()
+
+        jobs = self.fake_jenkins.all_jobs
+        finished_jobs = self.fake_jenkins.job_history
+
+        testfile_jobs = [x for x in finished_jobs
+                         if x.name == 'project-testfile']
+
+        assert len(testfile_jobs) == 1
+        assert testfile_jobs[0].changes == '1,2'
+        assert A.data['status'] == 'MERGED'
+        assert A.reported == 2
+        assert B.data['status'] == 'MERGED'
+        assert B.reported == 2
+        self.assertEmptyQueues()