@@ -35,6 +35,7 @@ tests = ["bb.tests.codeparser",
"bb.tests.fetch_import",
"bb.tests.parse",
"bb.tests.runqueue",
+ "bb.tests.server",
"bb.tests.setup",
"bb.tests.siggen",
"bb.tests.utils",
@@ -377,7 +377,7 @@ class ProcessServer():
lock = bb.utils.lockfile(lockfile, shared=False, retry=False, block=False)
if not lock:
newlockcontents = get_lock_contents(lockfile)
- if not newlockcontents[0].startswith([f"{os.getpid()}\n", f"{os.getpid()} "]):
+ if not newlockcontents[0].startswith((f"{os.getpid()}\n", f"{os.getpid()} ")):
# A new server was started, the lockfile contents changed, we can exit
serverlog("Lockfile now contains different contents, exiting: " + str(newlockcontents))
return
new file mode 100644
@@ -0,0 +1,57 @@
+#
+# BitBake Tests for server/process.py
+#
+# SPDX-License-Identifier: GPL-2.0-only
+#
+
+import os
+import tempfile
+import unittest
+from unittest.mock import Mock, patch
+
+from bb.server.process import ProcessServer
+
+
+class ProcessServerTests(unittest.TestCase):
+ def test_shutdown_contended_lock(self):
+ pid = os.getpid()
+ for owner in (str(pid), str(pid) + "0"):
+ for suffix in ("\n", " 127.0.0.1:12345\n"):
+ with self.subTest(owner=owner, suffix=suffix):
+ with tempfile.TemporaryDirectory() as tmpdir:
+ lockname = os.path.join(tmpdir, "bitbake.lock")
+ sockname = os.path.join(tmpdir, "bitbake.sock")
+ with open(lockname, "w") as stream:
+ stream.write(owner + suffix)
+ with open(sockname, "w"):
+ pass
+
+ lock = Mock()
+ sock = Mock()
+ server = ProcessServer(lock, lockname, sock, sockname,
+ 0, (None, None))
+ server.cooker = Mock()
+ server.quit = True
+ acquired_lock = Mock()
+ own_lock = owner == str(pid)
+ attempts = [None, acquired_lock] if own_lock else [None]
+
+ with patch("bb.server.process.bb.utils.set_process_name"), \
+ patch("bb.server.process.os.path.exists", return_value=True), \
+ patch("bb.server.process.bb.utils.lockfile", side_effect=attempts) as acquire, \
+ patch("bb.server.process.bb.utils.unlockfile") as unlock, \
+ patch("bb.server.process.time.sleep") as sleep, \
+ patch("bb.server.process.serverlog"):
+ server.main()
+
+ lock.close.assert_called_once_with()
+ sock.close.assert_called_once_with()
+ server.cooker.shutdown.assert_called_once_with(True, idle=False)
+ self.assertEqual(acquire.call_count, 2 if own_lock else 1)
+ acquire.assert_called_with(lockname, shared=False, retry=False, block=False)
+ if own_lock:
+ sleep.assert_called_once_with(0.1)
+ unlock.assert_called_once_with(acquired_lock)
+ else:
+ sleep.assert_not_called()
+ unlock.assert_not_called()
Pass a tuple to str.startswith() when checking a contended lock during shutdown. The list raises TypeError before the server can retry its own lock or recognize a replacement server. Add a deterministic shutdown regression test covering PID-only and XML-RPC lock records, including a replacement PID sharing the old PID's prefix. Verified failure before the fix and success with bitbake-selftest bb.tests.server afterwards. AI-Generated: Uses GitHub Copilot Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Signed-off-by: Reda Maher <eng.redamaher@gmail.com> --- bin/bitbake-selftest | 1 + lib/bb/server/process.py | 2 +- lib/bb/tests/server.py | 57 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 59 insertions(+), 1 deletion(-) create mode 100644 lib/bb/tests/server.py