summaryrefslogtreecommitdiffstats
path: root/bitbake/lib/prserv/tests.py
diff options
context:
space:
mode:
Diffstat (limited to 'bitbake/lib/prserv/tests.py')
-rw-r--r--bitbake/lib/prserv/tests.py386
1 files changed, 386 insertions, 0 deletions
diff --git a/bitbake/lib/prserv/tests.py b/bitbake/lib/prserv/tests.py
new file mode 100644
index 0000000000..8765b129f2
--- /dev/null
+++ b/bitbake/lib/prserv/tests.py
@@ -0,0 +1,386 @@
1#! /usr/bin/env python3
2#
3# Copyright (C) 2024 BitBake Contributors
4#
5# SPDX-License-Identifier: GPL-2.0-only
6#
7
8from . import create_server, create_client, increase_revision, revision_greater, revision_smaller, _revision_greater_or_equal
9import prserv.db as db
10from bb.asyncrpc import InvokeError
11import logging
12import os
13import sys
14import tempfile
15import unittest
16import socket
17import subprocess
18from pathlib import Path
19
20THIS_DIR = Path(__file__).parent
21BIN_DIR = THIS_DIR.parent.parent / "bin"
22
23version = "dummy-1.0-r0"
24pkgarch = "core2-64"
25other_arch = "aarch64"
26
27checksumX = "51bf8189dbe9ea81fa6dd89608bf19380c437a9cf12f6c6239887801ba4ab4f0"
28checksum0 = "51bf8189dbe9ea81fa6dd89608bf19380c437a9cf12f6c6239887801ba4ab4a0"
29checksum1 = "51bf8189dbe9ea81fa6dd89608bf19380c437a9cf12f6c6239887801ba4ab4a1"
30checksum2 = "51bf8189dbe9ea81fa6dd89608bf19380c437a9cf12f6c6239887801ba4ab4a2"
31checksum3 = "51bf8189dbe9ea81fa6dd89608bf19380c437a9cf12f6c6239887801ba4ab4a3"
32checksum4 = "51bf8189dbe9ea81fa6dd89608bf19380c437a9cf12f6c6239887801ba4ab4a4"
33checksum5 = "51bf8189dbe9ea81fa6dd89608bf19380c437a9cf12f6c6239887801ba4ab4a5"
34checksum6 = "51bf8189dbe9ea81fa6dd89608bf19380c437a9cf12f6c6239887801ba4ab4a6"
35checksum7 = "51bf8189dbe9ea81fa6dd89608bf19380c437a9cf12f6c6239887801ba4ab4a7"
36checksum8 = "51bf8189dbe9ea81fa6dd89608bf19380c437a9cf12f6c6239887801ba4ab4a8"
37checksum9 = "51bf8189dbe9ea81fa6dd89608bf19380c437a9cf12f6c6239887801ba4ab4a9"
38checksum10 = "51bf8189dbe9ea81fa6dd89608bf19380c437a9cf12f6c6239887801ba4ab4aa"
39
40def server_prefunc(server, name):
41 logging.basicConfig(level=logging.DEBUG, filename='prserv-%s.log' % name, filemode='w',
42 format='%(levelname)s %(filename)s:%(lineno)d %(message)s')
43 server.logger.debug("Running server %s" % name)
44 sys.stdout = open('prserv-stdout-%s.log' % name, 'w')
45 sys.stderr = sys.stdout
46
47class PRTestSetup(object):
48
49 def start_server(self, name, dbfile, upstream=None, read_only=False, prefunc=server_prefunc):
50
51 def cleanup_server(server):
52 if server.process.exitcode is not None:
53 return
54 server.process.terminate()
55 server.process.join()
56
57 server = create_server(socket.gethostbyname("localhost") + ":0",
58 dbfile,
59 upstream=upstream,
60 read_only=read_only)
61
62 server.serve_as_process(prefunc=prefunc, args=(name,))
63 self.addCleanup(cleanup_server, server)
64
65 return server
66
67 def start_client(self, server_address):
68 def cleanup_client(client):
69 client.close()
70
71 client = create_client(server_address)
72 self.addCleanup(cleanup_client, client)
73
74 return client
75
76class FunctionTests(unittest.TestCase):
77
78 def setUp(self):
79 self.temp_dir = tempfile.TemporaryDirectory(prefix='bb-prserv')
80 self.addCleanup(self.temp_dir.cleanup)
81
82 def test_increase_revision(self):
83 self.assertEqual(increase_revision("1"), "2")
84 self.assertEqual(increase_revision("1.0"), "1.1")
85 self.assertEqual(increase_revision("1.1.1"), "1.1.2")
86 self.assertEqual(increase_revision("1.1.1.3"), "1.1.1.4")
87 self.assertRaises(ValueError, increase_revision, "1.a")
88 self.assertRaises(ValueError, increase_revision, "1.")
89 self.assertRaises(ValueError, increase_revision, "")
90
91 def test_revision_greater_or_equal(self):
92 self.assertTrue(_revision_greater_or_equal("2", "2"))
93 self.assertTrue(_revision_greater_or_equal("2", "1"))
94 self.assertTrue(_revision_greater_or_equal("10", "2"))
95 self.assertTrue(_revision_greater_or_equal("1.10", "1.2"))
96 self.assertFalse(_revision_greater_or_equal("1.2", "1.10"))
97 self.assertTrue(_revision_greater_or_equal("1.10", "1"))
98 self.assertTrue(_revision_greater_or_equal("1.10.1", "1.10"))
99 self.assertFalse(_revision_greater_or_equal("1.10.1", "1.10.2"))
100 self.assertTrue(_revision_greater_or_equal("1.10.1", "1.10.1"))
101 self.assertTrue(_revision_greater_or_equal("1.10.1", "1"))
102 self.assertTrue(revision_greater("1.20", "1.3"))
103 self.assertTrue(revision_smaller("1.3", "1.20"))
104
105 # DB tests
106
107 def test_db(self):
108 dbfile = os.path.join(self.temp_dir.name, "testtable.sqlite3")
109
110 self.db = db.PRData(dbfile)
111 self.table = self.db["PRMAIN"]
112
113 self.table.store_value(version, pkgarch, checksum0, "0")
114 self.table.store_value(version, pkgarch, checksum1, "1")
115 # "No history" mode supports multiple PRs for the same checksum
116 self.table.store_value(version, pkgarch, checksum0, "2")
117 self.table.store_value(version, pkgarch, checksum2, "1.0")
118
119 self.assertTrue(self.table.test_package(version, pkgarch))
120 self.assertFalse(self.table.test_package(version, other_arch))
121
122 self.assertTrue(self.table.test_value(version, pkgarch, "0"))
123 self.assertTrue(self.table.test_value(version, pkgarch, "1"))
124 self.assertTrue(self.table.test_value(version, pkgarch, "2"))
125
126 self.assertEqual(self.table.find_package_max_value(version, pkgarch), "2")
127
128 self.assertEqual(self.table.find_min_value(version, pkgarch, checksum0), "0")
129 self.assertEqual(self.table.find_max_value(version, pkgarch, checksum0), "2")
130
131 # Test history modes
132 self.assertEqual(self.table.find_value(version, pkgarch, checksum0, True), "0")
133 self.assertEqual(self.table.find_value(version, pkgarch, checksum0, False), "2")
134
135 self.assertEqual(self.table.find_new_subvalue(version, pkgarch, "3"), "3.0")
136 self.assertEqual(self.table.find_new_subvalue(version, pkgarch, "1"), "1.1")
137
138 # Revision comparison tests
139 self.table.store_value(version, pkgarch, checksum1, "1.3")
140 self.table.store_value(version, pkgarch, checksum1, "1.20")
141 self.assertEqual(self.table.find_min_value(version, pkgarch, checksum1), "1")
142 self.assertEqual(self.table.find_max_value(version, pkgarch, checksum1), "1.20")
143
144class PRBasicTests(PRTestSetup, unittest.TestCase):
145
146 def setUp(self):
147 self.temp_dir = tempfile.TemporaryDirectory(prefix='bb-prserv')
148 self.addCleanup(self.temp_dir.cleanup)
149
150 dbfile = os.path.join(self.temp_dir.name, "prtest-basic.sqlite3")
151
152 self.server1 = self.start_server("basic", dbfile)
153 self.client1 = self.start_client(self.server1.address)
154
155 def test_basic(self):
156
157 # Checks on non existing configuration
158
159 result = self.client1.test_pr(version, pkgarch, checksum0)
160 self.assertIsNone(result, "test_pr should return 'None' for a non existing PR")
161
162 result = self.client1.test_package(version, pkgarch)
163 self.assertFalse(result, "test_package should return 'False' for a non existing PR")
164
165 result = self.client1.max_package_pr(version, pkgarch)
166 self.assertIsNone(result, "max_package_pr should return 'None' for a non existing PR")
167
168 # Add a first configuration
169
170 result = self.client1.getPR(version, pkgarch, checksum0)
171 self.assertEqual(result, "0", "getPR: initial PR of a package should be '0'")
172
173 result = self.client1.test_pr(version, pkgarch, checksum0)
174 self.assertEqual(result, "0", "test_pr should return '0' here, matching the result of getPR")
175
176 result = self.client1.test_package(version, pkgarch)
177 self.assertTrue(result, "test_package should return 'True' for an existing PR")
178
179 result = self.client1.max_package_pr(version, pkgarch)
180 self.assertEqual(result, "0", "max_package_pr should return '0' in the current test series")
181
182 # Check that the same request gets the same value
183
184 result = self.client1.getPR(version, pkgarch, checksum0)
185 self.assertEqual(result, "0", "getPR: asking for the same PR a second time in a row should return the same value.")
186
187 # Add new configurations
188
189 result = self.client1.getPR(version, pkgarch, checksum1)
190 self.assertEqual(result, "1", "getPR: second PR of a package should be '1'")
191
192 result = self.client1.test_pr(version, pkgarch, checksum1)
193 self.assertEqual(result, "1", "test_pr should return '1' here, matching the result of getPR")
194
195 result = self.client1.max_package_pr(version, pkgarch)
196 self.assertEqual(result, "1", "max_package_pr should return '1' in the current test series")
197
198 result = self.client1.getPR(version, pkgarch, checksum2)
199 self.assertEqual(result, "2", "getPR: second PR of a package should be '2'")
200
201 result = self.client1.test_pr(version, pkgarch, checksum2)
202 self.assertEqual(result, "2", "test_pr should return '2' here, matching the result of getPR")
203
204 result = self.client1.max_package_pr(version, pkgarch)
205 self.assertEqual(result, "2", "max_package_pr should return '2' in the current test series")
206
207 result = self.client1.getPR(version, pkgarch, checksum3)
208 self.assertEqual(result, "3", "getPR: second PR of a package should be '3'")
209
210 result = self.client1.test_pr(version, pkgarch, checksum3)
211 self.assertEqual(result, "3", "test_pr should return '3' here, matching the result of getPR")
212
213 result = self.client1.max_package_pr(version, pkgarch)
214 self.assertEqual(result, "3", "max_package_pr should return '3' in the current test series")
215
216 # Ask again for the first configuration
217
218 result = self.client1.getPR(version, pkgarch, checksum0)
219 self.assertEqual(result, "4", "getPR: should return '4' in this configuration")
220
221 # Ask again with explicit "no history" mode
222
223 result = self.client1.getPR(version, pkgarch, checksum0, False)
224 self.assertEqual(result, "4", "getPR: should return '4' in this configuration")
225
226 # Ask again with explicit "history" mode. This should return the first recorded PR for checksum0
227
228 result = self.client1.getPR(version, pkgarch, checksum0, True)
229 self.assertEqual(result, "0", "getPR: should return '0' in this configuration")
230
231 # Check again that another pkgarg resets the counters
232
233 result = self.client1.test_pr(version, other_arch, checksum0)
234 self.assertIsNone(result, "test_pr should return 'None' for a non existing PR")
235
236 result = self.client1.test_package(version, other_arch)
237 self.assertFalse(result, "test_package should return 'False' for a non existing PR")
238
239 result = self.client1.max_package_pr(version, other_arch)
240 self.assertIsNone(result, "max_package_pr should return 'None' for a non existing PR")
241
242 # Now add the configuration
243
244 result = self.client1.getPR(version, other_arch, checksum0)
245 self.assertEqual(result, "0", "getPR: initial PR of a package should be '0'")
246
247 result = self.client1.test_pr(version, other_arch, checksum0)
248 self.assertEqual(result, "0", "test_pr should return '0' here, matching the result of getPR")
249
250 result = self.client1.test_package(version, other_arch)
251 self.assertTrue(result, "test_package should return 'True' for an existing PR")
252
253 result = self.client1.max_package_pr(version, other_arch)
254 self.assertEqual(result, "0", "max_package_pr should return '0' in the current test series")
255
256 result = self.client1.is_readonly()
257 self.assertFalse(result, "Server should not be described as 'read-only'")
258
259class PRUpstreamTests(PRTestSetup, unittest.TestCase):
260
261 def setUp(self):
262
263 self.temp_dir = tempfile.TemporaryDirectory(prefix='bb-prserv')
264 self.addCleanup(self.temp_dir.cleanup)
265
266 dbfile2 = os.path.join(self.temp_dir.name, "prtest-upstream2.sqlite3")
267 self.server2 = self.start_server("upstream2", dbfile2)
268 self.client2 = self.start_client(self.server2.address)
269
270 dbfile1 = os.path.join(self.temp_dir.name, "prtest-upstream1.sqlite3")
271 self.server1 = self.start_server("upstream1", dbfile1, upstream=self.server2.address)
272 self.client1 = self.start_client(self.server1.address)
273
274 dbfile0 = os.path.join(self.temp_dir.name, "prtest-local.sqlite3")
275 self.server0 = self.start_server("local", dbfile0, upstream=self.server1.address)
276 self.client0 = self.start_client(self.server0.address)
277 self.shared_db = dbfile0
278
279 def test_upstream_and_readonly(self):
280
281 # For identical checksums, all servers should return the same PR
282
283 result = self.client2.getPR(version, pkgarch, checksum0)
284 self.assertEqual(result, "0", "getPR: initial PR of a package should be '0'")
285
286 result = self.client1.getPR(version, pkgarch, checksum0)
287 self.assertEqual(result, "0", "getPR: initial PR of a package should be '0' (same as upstream)")
288
289 result = self.client0.getPR(version, pkgarch, checksum0)
290 self.assertEqual(result, "0", "getPR: initial PR of a package should be '0' (same as upstream)")
291
292 # Now introduce new checksums on server1 for, same version
293
294 result = self.client1.getPR(version, pkgarch, checksum1)
295 self.assertEqual(result, "0.0", "getPR: first PR of a package which has a different checksum upstream should be '0.0'")
296
297 result = self.client1.getPR(version, pkgarch, checksum2)
298 self.assertEqual(result, "0.1", "getPR: second PR of a package that has a different checksum upstream should be '0.1'")
299
300 # Now introduce checksums on server0 for, same version
301
302 result = self.client1.getPR(version, pkgarch, checksum1)
303 self.assertEqual(result, "0.2", "getPR: can't decrease for known PR")
304
305 result = self.client1.getPR(version, pkgarch, checksum2)
306 self.assertEqual(result, "0.3")
307
308 result = self.client1.max_package_pr(version, pkgarch)
309 self.assertEqual(result, "0.3")
310
311 result = self.client0.getPR(version, pkgarch, checksum3)
312 self.assertEqual(result, "0.3.0", "getPR: first PR of a package that doesn't exist upstream should be '0.3.0'")
313
314 result = self.client0.getPR(version, pkgarch, checksum4)
315 self.assertEqual(result, "0.3.1", "getPR: second PR of a package that doesn't exist upstream should be '0.3.1'")
316
317 result = self.client0.getPR(version, pkgarch, checksum3)
318 self.assertEqual(result, "0.3.2")
319
320 # More upstream updates
321 # Here, we assume no communication between server2 and server0. server2 only impacts server0
322 # after impacting server1
323
324 self.assertEqual(self.client2.getPR(version, pkgarch, checksum5), "1")
325 self.assertEqual(self.client1.getPR(version, pkgarch, checksum6), "1.0")
326 self.assertEqual(self.client1.getPR(version, pkgarch, checksum7), "1.1")
327 self.assertEqual(self.client0.getPR(version, pkgarch, checksum8), "1.1.0")
328 self.assertEqual(self.client0.getPR(version, pkgarch, checksum9), "1.1.1")
329
330 # "history" mode tests
331
332 self.assertEqual(self.client2.getPR(version, pkgarch, checksum0, True), "0")
333 self.assertEqual(self.client1.getPR(version, pkgarch, checksum2, True), "0.1")
334 self.assertEqual(self.client0.getPR(version, pkgarch, checksum3, True), "0.3.0")
335
336 # More "no history" mode tests
337
338 self.assertEqual(self.client2.getPR(version, pkgarch, checksum0), "2")
339 self.assertEqual(self.client1.getPR(version, pkgarch, checksum0), "2") # Same as upstream
340 self.assertEqual(self.client0.getPR(version, pkgarch, checksum0), "2") # Same as upstream
341 self.assertEqual(self.client1.getPR(version, pkgarch, checksum7), "3") # This could be surprising, but since the previous revision was "2", increasing it yields "3".
342 # We don't know how many upstream servers we have
343 # Start read-only server with server1 as upstream
344 self.server_ro = self.start_server("local-ro", self.shared_db, upstream=self.server1.address, read_only=True)
345 self.client_ro = self.start_client(self.server_ro.address)
346
347 self.assertTrue(self.client_ro.is_readonly(), "Database should be described as 'read-only'")
348
349 # Checks on non existing configurations
350 self.assertIsNone(self.client_ro.test_pr(version, pkgarch, checksumX))
351 self.assertFalse(self.client_ro.test_package("unknown", pkgarch))
352
353 # Look up existing configurations
354 self.assertEqual(self.client_ro.getPR(version, pkgarch, checksum0), "3") # "no history" mode
355 self.assertEqual(self.client_ro.getPR(version, pkgarch, checksum0, True), "0") # "history" mode
356 self.assertEqual(self.client_ro.getPR(version, pkgarch, checksum3), "3")
357 self.assertEqual(self.client_ro.getPR(version, pkgarch, checksum3, True), "0.3.0")
358 self.assertEqual(self.client_ro.max_package_pr(version, pkgarch), "2") # normal as "3" was never saved
359
360 # Try to insert a new value. Here this one is know upstream.
361 self.assertEqual(self.client_ro.getPR(version, pkgarch, checksum7), "3")
362 # Try to insert a completely new value. As the max upstream value is already "3", it should be "3.0"
363 self.assertEqual(self.client_ro.getPR(version, pkgarch, checksum10), "3.0")
364 # Same with another value which only exists in the upstream upstream server
365 # This time, as the upstream server doesn't know it, it will ask its upstream server. So that's a known one.
366 self.assertEqual(self.client_ro.getPR(version, pkgarch, checksum9), "3")
367
368class ScriptTests(unittest.TestCase):
369
370 def setUp(self):
371
372 self.temp_dir = tempfile.TemporaryDirectory(prefix='bb-prserv')
373 self.addCleanup(self.temp_dir.cleanup)
374 self.dbfile = os.path.join(self.temp_dir.name, "prtest.sqlite3")
375
376 def test_1_start_bitbake_prserv(self):
377 try:
378 subprocess.check_call([BIN_DIR / "bitbake-prserv", "--start", "-f", self.dbfile])
379 except subprocess.CalledProcessError as e:
380 self.fail("Failed to start bitbake-prserv: %s" % e.returncode)
381
382 def test_2_stop_bitbake_prserv(self):
383 try:
384 subprocess.check_call([BIN_DIR / "bitbake-prserv", "--stop"])
385 except subprocess.CalledProcessError as e:
386 self.fail("Failed to stop bitbake-prserv: %s" % e.returncode)