Fix missing mutex release when aborting builds

Currently when zuul aborts a build due to e.g. abandoning a change the
mutex doesn't get released. This blocks all further jobs requiring the
mutex until zuul is restarted. This adds test cases for detecting this
and the missing mutex releases.

This is a port from I37e69310fed045c5a41bd4eccb151c8826f342ea to
zuulv3 branch.

Change-Id: I5e78bdf4e37227027e3a16fb88bae1a0c2002aaf
diff --git a/tests/unit/test_scheduler.py b/tests/unit/test_scheduler.py
index 309b815..dc6f70a 100755
--- a/tests/unit/test_scheduler.py
+++ b/tests/unit/test_scheduler.py
@@ -2246,6 +2246,66 @@
         self.assertEqual(B.reported, 1)
         self.assertFalse('test-mutex' in self.sched.mutex.mutexes)
 
+    def test_mutex_abandon(self):
+        "Test abandon with job mutexes"
+        self.updateConfigLayout('layout-mutex')
+        self.sched.reconfigure(self.config)
+
+        self.launch_server.hold_jobs_in_build = True
+
+        tenant = self.sched.abide.tenants.get('openstack')
+        check_pipeline = tenant.layout.pipelines['check']
+
+        A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
+        self.assertFalse('test-mutex' in self.sched.mutex.mutexes)
+
+        self.fake_gerrit.addEvent(A.getPatchsetCreatedEvent(1))
+        self.waitUntilSettled()
+
+        self.assertTrue('test-mutex' in self.sched.mutex.mutexes)
+
+        self.fake_gerrit.addEvent(A.getChangeAbandonedEvent())
+        self.waitUntilSettled()
+
+        # The check pipeline should be empty
+        items = check_pipeline.getAllItems()
+        self.assertEqual(len(items), 0)
+
+        # The mutex should be released
+        self.assertFalse('test-mutex' in self.sched.mutex.mutexes)
+
+        self.launch_server.hold_jobs_in_build = False
+        self.launch_server.release()
+        self.waitUntilSettled()
+
+    def test_mutex_reconfigure(self):
+        "Test reconfigure with job mutexes"
+        self.updateConfigLayout('layout-mutex')
+        self.sched.reconfigure(self.config)
+
+        self.launch_server.hold_jobs_in_build = True
+
+        A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
+        self.assertFalse('test-mutex' in self.sched.mutex.mutexes)
+
+        self.fake_gerrit.addEvent(A.getPatchsetCreatedEvent(1))
+        self.waitUntilSettled()
+
+        self.assertTrue('test-mutex' in self.sched.mutex.mutexes)
+
+        self.updateConfigLayout('layout-mutex-reconfiguration')
+        self.sched.reconfigure(self.config)
+        self.waitUntilSettled()
+
+        self.launch_server.release('project-test1')
+        self.waitUntilSettled()
+
+        # There should be no builds anymore
+        self.assertEqual(len(self.builds), 0)
+
+        # The mutex should be released
+        self.assertFalse('test-mutex' in self.sched.mutex.mutexes)
+
     def test_live_reconfiguration(self):
         "Test that live reconfiguration works"
         self.launch_server.hold_jobs_in_build = True