Switch to late-binding inheritance

Change-Id: Ia44c8df825098b93efb4f9c9bfd43275b6d74378
diff --git a/tests/fixtures/config/branch-variants/git/project-config/zuul.yaml b/tests/fixtures/config/branch-variants/git/project-config/zuul.yaml
index 89d98a9..161e5a1 100644
--- a/tests/fixtures/config/branch-variants/git/project-config/zuul.yaml
+++ b/tests/fixtures/config/branch-variants/git/project-config/zuul.yaml
@@ -11,6 +11,26 @@
       gerrit:
         Verified: -1
 
+- pipeline:
+    name: gate
+    manager: dependent
+    trigger:
+      gerrit:
+        - event: comment-added
+          approval:
+            - Approved: 1
+    success:
+      gerrit:
+        Verified: 2
+        submit: true
+    failure:
+      gerrit:
+        Verified: -2
+    start:
+      gerrit:
+        Verified: 0
+    precedence: high
+
 - job:
     name: base
     parent: null
@@ -23,3 +43,14 @@
     name: project-config
     check:
       jobs: []
+    gate:
+      jobs:
+        - noop
+
+- project:
+    name: puppet-integration
+    check:
+      jobs: []
+    gate:
+      jobs:
+        - noop
diff --git a/tests/fixtures/config/branch-variants/git/puppet-integration/.zuul.yaml b/tests/fixtures/config/branch-variants/git/puppet-integration/.zuul.yaml
index 2545208..322927f 100644
--- a/tests/fixtures/config/branch-variants/git/puppet-integration/.zuul.yaml
+++ b/tests/fixtures/config/branch-variants/git/puppet-integration/.zuul.yaml
@@ -11,6 +11,8 @@
     name: puppet-lint
     parent: puppet-module-base
     run: playbooks/run-lint
+    tags:
+      - master
 
 - project-template:
     name: puppet-check-jobs
diff --git a/tests/fixtures/config/branch-variants/git/puppet-integration/stable.zuul.yaml b/tests/fixtures/config/branch-variants/git/puppet-integration/stable.zuul.yaml
new file mode 100644
index 0000000..4701b80
--- /dev/null
+++ b/tests/fixtures/config/branch-variants/git/puppet-integration/stable.zuul.yaml
@@ -0,0 +1,26 @@
+- job:
+    name: puppet-base
+    pre-run: playbooks/prepare-node-common
+
+- job:
+    name: puppet-module-base
+    parent: puppet-base
+    pre-run: playbooks/prepare-node-unit
+
+- job:
+    name: puppet-lint
+    parent: puppet-module-base
+    run: playbooks/run-lint
+    tags:
+      - stable
+
+- project-template:
+    name: puppet-check-jobs
+    check:
+      jobs:
+        - puppet-lint
+
+- project:
+    name: puppet-integration
+    templates:
+      - puppet-check-jobs
diff --git a/tests/fixtures/layouts/parent-matchers.yaml b/tests/fixtures/layouts/parent-matchers.yaml
new file mode 100644
index 0000000..d53741e
--- /dev/null
+++ b/tests/fixtures/layouts/parent-matchers.yaml
@@ -0,0 +1,34 @@
+- pipeline:
+    name: check
+    manager: independent
+    trigger:
+      gerrit:
+        - event: patchset-created
+    success:
+      gerrit:
+        Verified: 1
+    failure:
+      gerrit:
+        Verified: -1
+
+- job:
+    name: base
+    parent: null
+
+- job:
+    name: parent-job
+    files: foo.txt
+
+- job:
+    name: parent-job
+    files: bar.txt
+    
+- job:
+    name: child-job
+    parent: parent-job
+
+- project:
+    name: org/project
+    check:
+      jobs:
+        - child-job
diff --git a/tests/unit/test_model.py b/tests/unit/test_model.py
index ae24f06..27a1d98 100644
--- a/tests/unit/test_model.py
+++ b/tests/unit/test_model.py
@@ -15,6 +15,7 @@
 
 import os
 import random
+from unittest import skip
 
 import fixtures
 import testtools
@@ -146,6 +147,7 @@
                 "Unable to modify final job"):
             job.applyVariant(bad_final)
 
+    @skip("This test relied on early-binding inheritance")
     def test_job_auth_inheritance(self):
         tenant = self.tenant
         layout = self.layout
diff --git a/tests/unit/test_scheduler.py b/tests/unit/test_scheduler.py
index 22d1139..1b5f9dd 100755
--- a/tests/unit/test_scheduler.py
+++ b/tests/unit/test_scheduler.py
@@ -4968,6 +4968,39 @@
         self.gearman_server.release()
         self.waitUntilSettled()
 
+    @simple_layout('layouts/parent-matchers.yaml')
+    def test_parent_matchers(self):
+        "Test that if a job's parent does not match, the job does not run"
+        A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
+        self.fake_gerrit.addEvent(A.getPatchsetCreatedEvent(1))
+        self.waitUntilSettled()
+
+        self.assertHistory([])
+
+        files = {'foo.txt': ''}
+        B = self.fake_gerrit.addFakeChange('org/project', 'master', 'B',
+                                           files=files)
+        self.fake_gerrit.addEvent(B.getPatchsetCreatedEvent(1))
+        self.waitUntilSettled()
+
+        files = {'bar.txt': ''}
+        C = self.fake_gerrit.addFakeChange('org/project', 'master', 'C',
+                                           files=files)
+        self.fake_gerrit.addEvent(C.getPatchsetCreatedEvent(1))
+        self.waitUntilSettled()
+
+        files = {'foo.txt': '', 'bar.txt': ''}
+        D = self.fake_gerrit.addFakeChange('org/project', 'master', 'D',
+                                           files=files)
+        self.fake_gerrit.addEvent(D.getPatchsetCreatedEvent(1))
+        self.waitUntilSettled()
+
+        self.assertHistory([
+            dict(name='child-job', result='SUCCESS', changes='2,1'),
+            dict(name='child-job', result='SUCCESS', changes='3,1'),
+            dict(name='child-job', result='SUCCESS', changes='4,1'),
+        ])
+
 
 class TestExecutor(ZuulTestCase):
     tenant_config_file = 'config/single-tenant/main.yaml'
diff --git a/tests/unit/test_v3.py b/tests/unit/test_v3.py
index 77c73dd..2a2a446 100755
--- a/tests/unit/test_v3.py
+++ b/tests/unit/test_v3.py
@@ -153,13 +153,12 @@
         # Thus it should fail.
         self.assertEqual(A.reported, 1)
         self.assertEqual(A.patchsets[-1]['approvals'][0]['value'], '-1')
-        self.assertIn('Unable to inherit from final job', A.messages[0])
+        self.assertIn('Unable to modify final job', A.messages[0])
 
 
 class TestBranchVariants(ZuulTestCase):
     tenant_config_file = 'config/branch-variants/main.yaml'
 
-    @skip("This is broken until the next change")
     def test_branch_variants(self):
         # Test branch variants of jobs with inheritance
         self.executor_server.hold_jobs_in_build = True
@@ -209,6 +208,47 @@
         self.executor_server.release()
         self.waitUntilSettled()
 
+    def test_branch_variants_divergent(self):
+        # Test branches can diverge and become independent
+        self.executor_server.hold_jobs_in_build = True
+        # This creates a new branch with a copy of the config in master
+        self.create_branch('puppet-integration', 'stable')
+        self.fake_gerrit.addEvent(
+            self.fake_gerrit.getFakeBranchCreatedEvent(
+                'puppet-integration', 'stable'))
+        self.waitUntilSettled()
+
+        with open(os.path.join(FIXTURE_DIR,
+                               'config/branch-variants/git/',
+                               'puppet-integration/stable.zuul.yaml')) as f:
+            config = f.read()
+
+        file_dict = {'.zuul.yaml': config}
+        C = self.fake_gerrit.addFakeChange('puppet-integration', 'stable', 'C',
+                                           files=file_dict)
+        C.addApproval('Code-Review', 2)
+        self.fake_gerrit.addEvent(C.addApproval('Approved', 1))
+        self.waitUntilSettled()
+        self.fake_gerrit.addEvent(C.getChangeMergedEvent())
+        self.waitUntilSettled()
+
+        A = self.fake_gerrit.addFakeChange('puppet-integration', 'master', 'A')
+        self.fake_gerrit.addEvent(A.getPatchsetCreatedEvent(1))
+        self.waitUntilSettled()
+        B = self.fake_gerrit.addFakeChange('puppet-integration', 'stable', 'B')
+        self.fake_gerrit.addEvent(B.getPatchsetCreatedEvent(1))
+        self.waitUntilSettled()
+
+        self.assertEqual(self.builds[0].parameters['zuul']['jobtags'],
+                         ['master'])
+
+        self.assertEqual(self.builds[1].parameters['zuul']['jobtags'],
+                         ['stable'])
+
+        self.executor_server.hold_jobs_in_build = False
+        self.executor_server.release()
+        self.waitUntilSettled()
+
 
 class TestInRepoConfig(ZuulTestCase):
     # A temporary class to hold new tests while others are disabled