From: Joshua Watt <jpewhacker@gmail.com>
To: bitbake-devel@lists.openembedded.org
Cc: Joshua Watt <JPEWhacker@gmail.com>
Subject: [bitbake-devel][PATCH v3 09/11] hashserv: tests: Add more upstream tests
Date: Fri, 28 Aug 2026 09:58:16 -0600 [thread overview]
Message-ID: <20260828155942.1219468-10-JPEWhacker@gmail.com> (raw)
In-Reply-To: <20260828155942.1219468-1-JPEWhacker@gmail.com>
Adds a test to verify that the batch API works properly with an upstream
server and also a test to verify that if the upstream server restarts or
is temporarily disconnected that the client recovers properly.
Signed-off-by: Joshua Watt <JPEWhacker@gmail.com>
---
lib/hashserv/tests.py | 152 +++++++++++++++++++++++++++++++++++++++---
1 file changed, 141 insertions(+), 11 deletions(-)
diff --git a/lib/hashserv/tests.py b/lib/hashserv/tests.py
index a7ce7425e..bb227c161 100644
--- a/lib/hashserv/tests.py
+++ b/lib/hashserv/tests.py
@@ -5,12 +5,13 @@
# SPDX-License-Identifier: GPL-2.0-only
#
-from . import create_server, create_client
+from . import create_server, create_client, create_async_client
from .server import DEFAULT_ANON_PERMS, ALL_PERMISSIONS
from bb.asyncrpc import InvokeError
import hashlib
import logging
from bb import multiprocessing
+import asyncio
import os
import sys
import tempfile
@@ -41,19 +42,15 @@ class HashEquivalenceTestSetup(object):
server_index = 0
client_index = 0
- def start_server(self, dbpath=None, upstream=None, read_only=False, prefunc=server_prefunc, anon_perms=DEFAULT_ANON_PERMS, admin_username=None, admin_password=None):
+ def start_server(self, dbpath=None, upstream=None, read_only=False, prefunc=server_prefunc, anon_perms=DEFAULT_ANON_PERMS, admin_username=None, admin_password=None, addr=None):
self.server_index += 1
+ if addr is None:
+ addr = self.get_server_addr(self.server_index)
+
if dbpath is None:
dbpath = self.make_dbpath()
- def cleanup_server(server):
- if server.process.exitcode is not None:
- return
-
- server.process.terminate()
- server.process.join()
-
- server = create_server(self.get_server_addr(self.server_index),
+ server = create_server(addr,
dbpath,
upstream=upstream,
read_only=read_only,
@@ -63,7 +60,7 @@ class HashEquivalenceTestSetup(object):
server.dbpath = dbpath
server.serve_as_process(prefunc=prefunc, args=(self.server_index,))
- self.addCleanup(cleanup_server, server)
+ self.addCleanup(self.stop_server, server)
return server
@@ -83,6 +80,13 @@ class HashEquivalenceTestSetup(object):
self.server = self.start_server()
return self.server.address
+ def stop_server(self, server):
+ if not server or server.process.exitcode is not None:
+ return
+
+ server.process.terminate()
+ server.process.join()
+
def start_auth_server(self):
auth_server = self.start_server(self.server.dbpath, anon_perms=[], admin_username="admin", admin_password="password")
self.auth_server_address = auth_server.address
@@ -476,6 +480,132 @@ class HashEquivalenceCommonTests(object):
self.assertEqual(result['taskhash'], taskhash9, 'Server failed to copy unihash from upstream')
self.assertEqual(result['method'], self.METHOD)
+ def test_upstream_batch(self):
+ down_server = self.start_server(upstream=self.server.address)
+ down_client = self.start_client(down_server.address)
+
+ taskhash1 = '8aa96fcffb5831b3c2c0cb75f0431e3f8b20554a'
+ outhash1 = 'afe240a439959ce86f5e322f8c208e1fedefea9e813f2140c81af866cc9edf7e'
+ unihash1 = '5b521d8a12683086cc08bc2c6d94a7a2dcff17eba53b9911e145d51164689380'
+ self.client.report_unihash(taskhash1, self.METHOD, outhash1, unihash1)
+
+ taskhash2 = "e3da00593d6a7fb435c7e2114976c59c5fd6d561"
+ outhash2 = "1cf8713e645f491eb9c959d20b5cae1c47133a292626dda9b10709857cbe688a"
+ unihash2 = "7aebef07d66a8c0f92d0c4f65ec8b1fbb850a3693c53827b8774b64fa9a8a9fe"
+ self.client.report_unihash(taskhash2, self.METHOD, outhash2, unihash2)
+
+ taskhash3 = '35788efcb8dfb0a02659d81cf2bfd695fb30faf9'
+ outhash3 = '2765d4a5884be49b28601445c2760c5f21e7e5c0ee2b7e3fce98fd7e5970796f'
+ unihash3 = 'a69ec97f5af2e21e1a1f9cc8896965515d5559425666f734e245a3d40cee33d9'
+ self.client.report_unihash(taskhash3, self.METHOD, outhash3, unihash3)
+
+ def query_generator():
+ yield unihash1
+ yield unihash2
+ yield unihash3
+ yield "cc74784b2c0ad5b378a6b783c74c518d2c46b8b52fba29cb39a8430d742440d7"
+
+ results = down_client.unihash_exists_batch(query_generator())
+ self.assertEqual(results, [True, True, True, False])
+
+ def test_upstream_interrupted(self):
+ up_server = self.start_server()
+ down_server = self.start_server(upstream=up_server.address)
+
+ def restart_upstream():
+ nonlocal up_server
+
+ self.stop_server(up_server)
+ up_server = self.start_server(addr=up_server.address, dbpath=up_server.dbpath)
+
+ # Report some hashes
+ with self.start_client(up_server.address) as up_client:
+ taskhash1 = '8aa96fcffb5831b3c2c0cb75f0431e3f8b20554a'
+ outhash1 = 'afe240a439959ce86f5e322f8c208e1fedefea9e813f2140c81af866cc9edf7e'
+ unihash1 = '5b521d8a12683086cc08bc2c6d94a7a2dcff17eba53b9911e145d51164689380'
+ up_client.report_unihash(taskhash1, self.METHOD, outhash1, unihash1)
+
+ taskhash2 = "e3da00593d6a7fb435c7e2114976c59c5fd6d561"
+ outhash2 = "1cf8713e645f491eb9c959d20b5cae1c47133a292626dda9b10709857cbe688a"
+ unihash2 = "7aebef07d66a8c0f92d0c4f65ec8b1fbb850a3693c53827b8774b64fa9a8a9fe"
+ up_client.report_unihash(taskhash2, self.METHOD, outhash2, unihash2)
+
+ taskhash3 = '35788efcb8dfb0a02659d81cf2bfd695fb30faf9'
+ outhash3 = '2765d4a5884be49b28601445c2760c5f21e7e5c0ee2b7e3fce98fd7e5970796f'
+ unihash3 = 'a69ec97f5af2e21e1a1f9cc8896965515d5559425666f734e245a3d40cee33d9'
+ up_client.report_unihash(taskhash3, self.METHOD, outhash3, unihash3)
+
+ restart_upstream()
+
+ with self.start_client(up_server.address) as up_client:
+ # Verify that reported hashes are correct after restaring server
+ self.assertTrue(up_client.unihash_exists(unihash1))
+ self.assertClientGetHash(up_client, taskhash1, unihash1)
+
+ self.assertTrue(up_client.unihash_exists(unihash2))
+ self.assertClientGetHash(up_client, taskhash2, unihash2)
+
+ async def check_unihashes():
+ async with await create_async_client(down_server.address) as down_client:
+ async with down_client.unihash_exists_stream() as stream:
+ await stream.send_query(unihash1)
+ r = await stream.get_result()
+ self.assertTrue(r)
+
+ restart_upstream()
+
+ await stream.send_query(unihash2)
+ r = await stream.get_result()
+ self.assertTrue(r)
+
+ await stream.send_query(unihash3)
+ r = await stream.get_result()
+ self.assertTrue(r)
+
+ asyncio.run(check_unihashes())
+
+ def test_upstream_lost(self):
+ up_server = self.start_server()
+ down_server = self.start_server(upstream=up_server.address)
+
+ def restart_upstream():
+ nonlocal up_server
+
+ self.stop_server(up_server)
+ up_server = self.start_server(addr=up_server.address, dbpath=up_server.dbpath)
+
+ # Report some hashes
+ with self.start_client(up_server.address) as up_client:
+ taskhash1 = '8aa96fcffb5831b3c2c0cb75f0431e3f8b20554a'
+ outhash1 = 'afe240a439959ce86f5e322f8c208e1fedefea9e813f2140c81af866cc9edf7e'
+ unihash1 = '5b521d8a12683086cc08bc2c6d94a7a2dcff17eba53b9911e145d51164689380'
+ up_client.report_unihash(taskhash1, self.METHOD, outhash1, unihash1)
+
+ taskhash2 = "e3da00593d6a7fb435c7e2114976c59c5fd6d561"
+ outhash2 = "1cf8713e645f491eb9c959d20b5cae1c47133a292626dda9b10709857cbe688a"
+ unihash2 = "7aebef07d66a8c0f92d0c4f65ec8b1fbb850a3693c53827b8774b64fa9a8a9fe"
+ up_client.report_unihash(taskhash2, self.METHOD, outhash2, unihash2)
+
+ taskhash3 = '35788efcb8dfb0a02659d81cf2bfd695fb30faf9'
+ outhash3 = '2765d4a5884be49b28601445c2760c5f21e7e5c0ee2b7e3fce98fd7e5970796f'
+ unihash3 = 'a69ec97f5af2e21e1a1f9cc8896965515d5559425666f734e245a3d40cee33d9'
+ up_client.report_unihash(taskhash3, self.METHOD, outhash3, unihash3)
+
+ async def check_unihashes():
+ async with await create_async_client(down_server.address) as down_client:
+ with self.assertRaises(ConnectionError):
+ async with down_client.unihash_exists_stream() as stream:
+ await stream.send_query(unihash1)
+ r = await stream.get_result()
+ self.assertTrue(r)
+
+ self.stop_server(up_server)
+
+ await stream.send_query(unihash2)
+ r = await stream.get_result()
+
+ asyncio.run(check_unihashes())
+
def test_unihash_exsits(self):
taskhash, outhash, unihash = self.create_test_hash(self.client)
self.assertTrue(self.client.unihash_exists(unihash))
--
2.55.0
next prev parent reply other threads:[~2026-08-28 15:59 UTC|newest]
Thread overview: 35+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-07-24 21:25 [bitbake-devel][PATCH 0/6] hashserv: Pipeline Upstream Queries Joshua Watt
2026-07-24 21:25 ` [bitbake-devel][PATCH 1/6] hashserv: client: Add asynchronous streaming API Joshua Watt
2026-07-24 21:25 ` [bitbake-devel][PATCH 2/6] hashserv: server: Fix formatting Joshua Watt
2026-07-24 21:25 ` [bitbake-devel][PATCH 3/6] hashserv: server: Add queued streaming API Joshua Watt
2026-07-24 21:25 ` [bitbake-devel][PATCH 4/6] hashserv: server: Use streaming and queue API for upstream unihash queries Joshua Watt
2026-07-24 21:25 ` [bitbake-devel][PATCH 5/6] hashserv: server: Use streaming and queue API for upstream exist queries Joshua Watt
2026-07-24 21:25 ` [bitbake-devel][PATCH 6/6] hashserv: tests: Add test for upstream pipelining Joshua Watt
2026-07-26 13:50 ` [bitbake-devel][PATCH 0/6] hashserv: Pipeline Upstream Queries Richard Purdie
2026-07-30 18:30 ` [bitbake-devel][PATCH v2 00/10] " Joshua Watt
2026-07-30 18:30 ` [bitbake-devel][PATCH v2 01/10] asyncrpc: Add Task Group Joshua Watt
2026-07-30 18:30 ` [bitbake-devel][PATCH v2 02/10] asyncrpc: serv: Use " Joshua Watt
2026-07-30 18:30 ` [bitbake-devel][PATCH v2 03/10] asyncrpc: serv: Cancel all clients on server stop Joshua Watt
2026-07-30 18:30 ` [bitbake-devel][PATCH v2 04/10] hashserv: tests: Improve test logging Joshua Watt
2026-07-30 18:30 ` [bitbake-devel][PATCH v2 05/10] hashserv: client: Add asynchronous streaming API Joshua Watt
2026-07-30 18:30 ` [bitbake-devel][PATCH v2 06/10] hashserv: server: Add queued " Joshua Watt
2026-07-30 18:31 ` [bitbake-devel][PATCH v2 07/10] hashserv: server: Use streaming and queue API for upstream unihash queries Joshua Watt
2026-07-30 18:31 ` [bitbake-devel][PATCH v2 08/10] hashserv: server: Use streaming and queue API for upstream exist queries Joshua Watt
2026-07-30 18:31 ` [bitbake-devel][PATCH v2 09/10] hashserv: tests: Add more upstream tests Joshua Watt
2026-07-30 18:31 ` [bitbake-devel][PATCH v2 10/10] hashserv: tests: Add test for upstream pipelining Joshua Watt
2026-07-31 13:05 ` [PATCH v2 00/10] hashserv: Pipeline Upstream Queries Michal Sieron
2026-08-26 20:05 ` [bitbake-devel] " Joshua Watt
2026-08-26 22:48 ` Michal Sieron
2026-08-27 18:57 ` [bitbake-devel] " Joshua Watt
2026-08-28 15:58 ` [bitbake-devel][PATCH v3 00/11] " Joshua Watt
2026-08-28 15:58 ` [bitbake-devel][PATCH v3 01/11] asyncrpc: Add Task Group Joshua Watt
2026-08-28 15:58 ` [bitbake-devel][PATCH v3 02/11] asyncrpc: serv: Use " Joshua Watt
2026-08-28 15:58 ` [bitbake-devel][PATCH v3 03/11] asyncrpc: serv: Cancel all clients on server stop Joshua Watt
2026-08-28 15:58 ` [bitbake-devel][PATCH v3 04/11] hashserv: tests: Improve test logging Joshua Watt
2026-08-28 15:58 ` [bitbake-devel][PATCH v3 05/11] hashserv: client: Add asynchronous streaming API Joshua Watt
2026-08-28 15:58 ` [bitbake-devel][PATCH v3 06/11] hashserv: server: Add queued " Joshua Watt
2026-08-28 15:58 ` [bitbake-devel][PATCH v3 07/11] hashserv: server: Use streaming and queue API for upstream unihash queries Joshua Watt
2026-08-28 15:58 ` [bitbake-devel][PATCH v3 08/11] hashserv: server: Use streaming and queue API for upstream exist queries Joshua Watt
2026-08-28 15:58 ` Joshua Watt [this message]
2026-08-28 15:58 ` [bitbake-devel][PATCH v3 10/11] hashserv: tests: Add test for upstream pipelining Joshua Watt
2026-08-28 15:58 ` [bitbake-devel][PATCH v3 11/11] hashserv: server: Fix upstream get-unihash miss truncating stream Joshua Watt
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260828155942.1219468-10-JPEWhacker@gmail.com \
--to=jpewhacker@gmail.com \
--cc=bitbake-devel@lists.openembedded.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.