diff mbox series

[06/41] oeqa/core/utils/concurrencytest: Don't buffer output in the forwarding results

Message ID 20261115084845.701136-6-richard.purdie@linuxfoundation.org
State New
Headers show
Series [01/41] oeqa/loader: Drop code for pre python 3.5 | expand

Commit Message

Richard Purdie Nov. 15, 2026, 8:48 a.m. UTC
BBThreadsafeForwardingResult set buffer = True, so the forwarding result
for each test process swapped sys.stdout and sys.stderr for buffers in
its startTest() and restored them in stopTest(). These run in separate
reader threads without any locking, and each result records the streams
to restore when it is created, which happens after the reader threads
for earlier processes have started. sys.stdout/stderr could be left
pointing at a buffer after the tests ran, losing later output and making
any later forked test process fail at sys.stdout.fileno().

The output of the test processes comes through the passthrough buffer
and is given to the final result, which does its own buffering under the
semaphore, so don't buffer in the forwarding results. Add a test which
checks the streams are restored after concurrent runs.

AI-Generated: Uses Claude Opus 5.5
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
---
 meta/lib/oeqa/core/tests/test_concurrency.py | 11 +++++++++++
 meta/lib/oeqa/core/utils/concurrencytest.py  |  4 +++-
 2 files changed, 14 insertions(+), 1 deletion(-)
diff mbox series

Patch

diff --git a/meta/lib/oeqa/core/tests/test_concurrency.py b/meta/lib/oeqa/core/tests/test_concurrency.py
index b44c006d532..f7b1f554c96 100644
--- a/meta/lib/oeqa/core/tests/test_concurrency.py
+++ b/meta/lib/oeqa/core/tests/test_concurrency.py
@@ -6,6 +6,7 @@ 
 #
 
 import os
+import sys
 import unittest
 from unittest import mock
 
@@ -53,5 +54,15 @@  class TestConcurrency(TestBase):
             for other in {1, 2, 3} - {n}:
                 self.assertNotIn("testStreams%d" % other, output)
 
+    def test_concurrent_streams_restored(self):
+        # Each process' results are read in a separate thread; after running,
+        # sys.stdout and sys.stderr must be the original streams again. Any
+        # problem here is a race, so run several times.
+        stdout, stderr = sys.stdout, sys.stderr
+        for i in range(50):
+            self._run(['results'], processes=2)
+            self.assertIs(sys.stdout, stdout)
+            self.assertIs(sys.stderr, stderr)
+
 if __name__ == '__main__':
     unittest.main()
diff --git a/meta/lib/oeqa/core/utils/concurrencytest.py b/meta/lib/oeqa/core/utils/concurrencytest.py
index 455b70faff2..24b391cdb96 100644
--- a/meta/lib/oeqa/core/utils/concurrencytest.py
+++ b/meta/lib/oeqa/core/utils/concurrencytest.py
@@ -55,7 +55,9 @@  class BBThreadsafeForwardingResult(ThreadsafeForwardingResult):
         self.threadnum = threadnum
         self.totalinprocess = totalinprocess
         self.totaltests = totaltests
-        self.buffer = True
+        # Don't set self.buffer, the output comes through outputbuf. Buffering
+        # here would swap sys.stdout/stderr in each reader thread, unlocked,
+        # and could leave them replaced after the tests have run.
         self.outputbuf = output
         # The process' output accumulates in outputbuf; this is how much of
         # it was given to earlier results