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.py388
1 files changed, 388 insertions, 0 deletions
diff --git a/bitbake/lib/prserv/tests.py b/bitbake/lib/prserv/tests.py
new file mode 100644
index 0000000000..df0c003003
--- /dev/null
+++ b/bitbake/lib/prserv/tests.py
@@ -0,0 +1,388 @@
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.assertEqual(increase_revision("9"), "10")
88 self.assertEqual(increase_revision("1.9"), "1.10")
89 self.assertRaises(ValueError, increase_revision, "1.a")
90 self.assertRaises(ValueError, increase_revision, "1.")
91 self.assertRaises(ValueError, increase_revision, "")
92
93 def test_revision_greater_or_equal(self):
94 self.assertTrue(_revision_greater_or_equal("2", "2"))
95 self.assertTrue(_revision_greater_or_equal("2", "1"))
96 self.assertTrue(_revision_greater_or_equal("10", "2"))
97 self.assertTrue(_revision_greater_or_equal("1.10", "1.2"))
98 self.assertFalse(_revision_greater_or_equal("1.2", "1.10"))
99 self.assertTrue(_revision_greater_or_equal("1.10", "1"))
100 self.assertTrue(_revision_greater_or_equal("1.10.1", "1.10"))
101 self.assertFalse(_revision_greater_or_equal("1.10.1", "1.10.2"))
102 self.assertTrue(_revision_greater_or_equal("1.10.1", "1.10.1"))
103 self.assertTrue(_revision_greater_or_equal("1.10.1", "1"))
104 self.assertTrue(revision_greater("1.20", "1.3"))
105 self.assertTrue(revision_smaller("1.3", "1.20"))
106
107 # DB tests
108
109 def test_db(self):
110 dbfile = os.path.join(self.temp_dir.name, "testtable.sqlite3")
111
112 self.db = db.PRData(dbfile)
113 self.table = self.db["PRMAIN"]
114
115 self.table.store_value(version, pkgarch, checksum0, "0")
116 self.table.store_value(version, pkgarch, checksum1, "1")
117 # "No history" mode supports multiple PRs for the same checksum
118 self.table.store_value(version, pkgarch, checksum0, "2")
119 self.table.store_value(version, pkgarch, checksum2, "1.0")
120
121 self.assertTrue(self.table.test_package(version, pkgarch))
122 self.assertFalse(self.table.test_package(version, other_arch))
123
124 self.assertTrue(self.table.test_value(version, pkgarch, "0"))
125 self.assertTrue(self.table.test_value(version, pkgarch, "1"))
126 self.assertTrue(self.table.test_value(version, pkgarch, "2"))
127
128 self.assertEqual(self.table.find_package_max_value(version, pkgarch), "2")
129
130 self.assertEqual(self.table.find_min_value(version, pkgarch, checksum0), "0")
131 self.assertEqual(self.table.find_max_value(version, pkgarch, checksum0), "2")
132
133 # Test history modes
134 self.assertEqual(self.table.find_value(version, pkgarch, checksum0, True), "0")
135 self.assertEqual(self.table.find_value(version, pkgarch, checksum0, False), "2")
136
137 self.assertEqual(self.table.find_new_subvalue(version, pkgarch, "3"), "3.0")
138 self.assertEqual(self.table.find_new_subvalue(version, pkgarch, "1"), "1.1")
139
140 # Revision comparison tests
141 self.table.store_value(version, pkgarch, checksum1, "1.3")
142 self.table.store_value(version, pkgarch, checksum1, "1.20")
143 self.assertEqual(self.table.find_min_value(version, pkgarch, checksum1), "1")
144 self.assertEqual(self.table.find_max_value(version, pkgarch, checksum1), "1.20")
145
146class PRBasicTests(PRTestSetup, unittest.TestCase):
147
148 def setUp(self):
149 self.temp_dir = tempfile.TemporaryDirectory(prefix='bb-prserv')
150 self.addCleanup(self.temp_dir.cleanup)
151
152 dbfile = os.path.join(self.temp_dir.name, "prtest-basic.sqlite3")
153
154 self.server1 = self.start_server("basic", dbfile)
155 self.client1 = self.start_client(self.server1.address)
156
157 def test_basic(self):
158
159 # Checks on non existing configuration
160
161 result = self.client1.test_pr(version, pkgarch, checksum0)
162 self.assertIsNone(result, "test_pr should return 'None' for a non existing PR")
163
164 result = self.client1.test_package(version, pkgarch)
165 self.assertFalse(result, "test_package should return 'False' for a non existing PR")
166
167 result = self.client1.max_package_pr(version, pkgarch)
168 self.assertIsNone(result, "max_package_pr should return 'None' for a non existing PR")
169
170 # Add a first configuration
171
172 result = self.client1.getPR(version, pkgarch, checksum0)
173 self.assertEqual(result, "0", "getPR: initial PR of a package should be '0'")
174
175 result = self.client1.test_pr(version, pkgarch, checksum0)
176 self.assertEqual(result, "0", "test_pr should return '0' here, matching the result of getPR")
177
178 result = self.client1.test_package(version, pkgarch)
179 self.assertTrue(result, "test_package should return 'True' for an existing PR")
180
181 result = self.client1.max_package_pr(version, pkgarch)
182 self.assertEqual(result, "0", "max_package_pr should return '0' in the current test series")
183
184 # Check that the same request gets the same value
185
186 result = self.client1.getPR(version, pkgarch, checksum0)
187 self.assertEqual(result, "0", "getPR: asking for the same PR a second time in a row should return the same value.")
188
189 # Add new configurations
190
191 result = self.client1.getPR(version, pkgarch, checksum1)
192 self.assertEqual(result, "1", "getPR: second PR of a package should be '1'")
193
194 result = self.client1.test_pr(version, pkgarch, checksum1)
195 self.assertEqual(result, "1", "test_pr should return '1' here, matching the result of getPR")
196
197 result = self.client1.max_package_pr(version, pkgarch)
198 self.assertEqual(result, "1", "max_package_pr should return '1' in the current test series")
199
200 result = self.client1.getPR(version, pkgarch, checksum2)
201 self.assertEqual(result, "2", "getPR: second PR of a package should be '2'")
202
203 result = self.client1.test_pr(version, pkgarch, checksum2)
204 self.assertEqual(result, "2", "test_pr should return '2' here, matching the result of getPR")
205
206 result = self.client1.max_package_pr(version, pkgarch)
207 self.assertEqual(result, "2", "max_package_pr should return '2' in the current test series")
208
209 result = self.client1.getPR(version, pkgarch, checksum3)
210 self.assertEqual(result, "3", "getPR: second PR of a package should be '3'")
211
212 result = self.client1.test_pr(version, pkgarch, checksum3)
213 self.assertEqual(result, "3", "test_pr should return '3' here, matching the result of getPR")
214
215 result = self.client1.max_package_pr(version, pkgarch)
216 self.assertEqual(result, "3", "max_package_pr should return '3' in the current test series")
217
218 # Ask again for the first configuration
219
220 result = self.client1.getPR(version, pkgarch, checksum0)
221 self.assertEqual(result, "4", "getPR: should return '4' in this configuration")
222
223 # Ask again with explicit "no history" mode
224
225 result = self.client1.getPR(version, pkgarch, checksum0, False)
226 self.assertEqual(result, "4", "getPR: should return '4' in this configuration")
227
228 # Ask again with explicit "history" mode. This should return the first recorded PR for checksum0
229
230 result = self.client1.getPR(version, pkgarch, checksum0, True)
231 self.assertEqual(result, "0", "getPR: should return '0' in this configuration")
232
233 # Check again that another pkgarg resets the counters
234
235 result = self.client1.test_pr(version, other_arch, checksum0)
236 self.assertIsNone(result, "test_pr should return 'None' for a non existing PR")
237
238 result = self.client1.test_package(version, other_arch)
239 self.assertFalse(result, "test_package should return 'False' for a non existing PR")
240
241 result = self.client1.max_package_pr(version, other_arch)
242 self.assertIsNone(result, "max_package_pr should return 'None' for a non existing PR")
243
244 # Now add the configuration
245
246 result = self.client1.getPR(version, other_arch, checksum0)
247 self.assertEqual(result, "0", "getPR: initial PR of a package should be '0'")
248
249 result = self.client1.test_pr(version, other_arch, checksum0)
250 self.assertEqual(result, "0", "test_pr should return '0' here, matching the result of getPR")
251
252 result = self.client1.test_package(version, other_arch)
253 self.assertTrue(result, "test_package should return 'True' for an existing PR")
254
255 result = self.client1.max_package_pr(version, other_arch)
256 self.assertEqual(result, "0", "max_package_pr should return '0' in the current test series")
257
258 result = self.client1.is_readonly()
259 self.assertFalse(result, "Server should not be described as 'read-only'")
260
261class PRUpstreamTests(PRTestSetup, unittest.TestCase):
262
263 def setUp(self):
264
265 self.temp_dir = tempfile.TemporaryDirectory(prefix='bb-prserv')
266 self.addCleanup(self.temp_dir.cleanup)
267
268 dbfile2 = os.path.join(self.temp_dir.name, "prtest-upstream2.sqlite3")
269 self.server2 = self.start_server("upstream2", dbfile2)
270 self.client2 = self.start_client(self.server2.address)
271
272 dbfile1 = os.path.join(self.temp_dir.name, "prtest-upstream1.sqlite3")
273 self.server1 = self.start_server("upstream1", dbfile1, upstream=self.server2.address)
274 self.client1 = self.start_client(self.server1.address)
275
276 dbfile0 = os.path.join(self.temp_dir.name, "prtest-local.sqlite3")
277 self.server0 = self.start_server("local", dbfile0, upstream=self.server1.address)
278 self.client0 = self.start_client(self.server0.address)
279 self.shared_db = dbfile0
280
281 def test_upstream_and_readonly(self):
282
283 # For identical checksums, all servers should return the same PR
284
285 result = self.client2.getPR(version, pkgarch, checksum0)
286 self.assertEqual(result, "0", "getPR: initial PR of a package should be '0'")
287
288 result = self.client1.getPR(version, pkgarch, checksum0)
289 self.assertEqual(result, "0", "getPR: initial PR of a package should be '0' (same as upstream)")
290
291 result = self.client0.getPR(version, pkgarch, checksum0)
292 self.assertEqual(result, "0", "getPR: initial PR of a package should be '0' (same as upstream)")
293
294 # Now introduce new checksums on server1 for, same version
295
296 result = self.client1.getPR(version, pkgarch, checksum1)
297 self.assertEqual(result, "0.0", "getPR: first PR of a package which has a different checksum upstream should be '0.0'")
298
299 result = self.client1.getPR(version, pkgarch, checksum2)
300 self.assertEqual(result, "0.1", "getPR: second PR of a package that has a different checksum upstream should be '0.1'")
301
302 # Now introduce checksums on server0 for, same version
303
304 result = self.client1.getPR(version, pkgarch, checksum1)
305 self.assertEqual(result, "0.2", "getPR: can't decrease for known PR")
306
307 result = self.client1.getPR(version, pkgarch, checksum2)
308 self.assertEqual(result, "0.3")
309
310 result = self.client1.max_package_pr(version, pkgarch)
311 self.assertEqual(result, "0.3")
312
313 result = self.client0.getPR(version, pkgarch, checksum3)
314 self.assertEqual(result, "0.3.0", "getPR: first PR of a package that doesn't exist upstream should be '0.3.0'")
315
316 result = self.client0.getPR(version, pkgarch, checksum4)
317 self.assertEqual(result, "0.3.1", "getPR: second PR of a package that doesn't exist upstream should be '0.3.1'")
318
319 result = self.client0.getPR(version, pkgarch, checksum3)
320 self.assertEqual(result, "0.3.2")
321
322 # More upstream updates
323 # Here, we assume no communication between server2 and server0. server2 only impacts server0
324 # after impacting server1
325
326 self.assertEqual(self.client2.getPR(version, pkgarch, checksum5), "1")
327 self.assertEqual(self.client1.getPR(version, pkgarch, checksum6), "1.0")
328 self.assertEqual(self.client1.getPR(version, pkgarch, checksum7), "1.1")
329 self.assertEqual(self.client0.getPR(version, pkgarch, checksum8), "1.1.0")
330 self.assertEqual(self.client0.getPR(version, pkgarch, checksum9), "1.1.1")
331
332 # "history" mode tests
333
334 self.assertEqual(self.client2.getPR(version, pkgarch, checksum0, True), "0")
335 self.assertEqual(self.client1.getPR(version, pkgarch, checksum2, True), "0.1")
336 self.assertEqual(self.client0.getPR(version, pkgarch, checksum3, True), "0.3.0")
337
338 # More "no history" mode tests
339
340 self.assertEqual(self.client2.getPR(version, pkgarch, checksum0), "2")
341 self.assertEqual(self.client1.getPR(version, pkgarch, checksum0), "2") # Same as upstream
342 self.assertEqual(self.client0.getPR(version, pkgarch, checksum0), "2") # Same as upstream
343 self.assertEqual(self.client1.getPR(version, pkgarch, checksum7), "3") # This could be surprising, but since the previous revision was "2", increasing it yields "3".
344 # We don't know how many upstream servers we have
345 # Start read-only server with server1 as upstream
346 self.server_ro = self.start_server("local-ro", self.shared_db, upstream=self.server1.address, read_only=True)
347 self.client_ro = self.start_client(self.server_ro.address)
348
349 self.assertTrue(self.client_ro.is_readonly(), "Database should be described as 'read-only'")
350
351 # Checks on non existing configurations
352 self.assertIsNone(self.client_ro.test_pr(version, pkgarch, checksumX))
353 self.assertFalse(self.client_ro.test_package("unknown", pkgarch))
354
355 # Look up existing configurations
356 self.assertEqual(self.client_ro.getPR(version, pkgarch, checksum0), "3") # "no history" mode
357 self.assertEqual(self.client_ro.getPR(version, pkgarch, checksum0, True), "0") # "history" mode
358 self.assertEqual(self.client_ro.getPR(version, pkgarch, checksum3), "3")
359 self.assertEqual(self.client_ro.getPR(version, pkgarch, checksum3, True), "0.3.0")
360 self.assertEqual(self.client_ro.max_package_pr(version, pkgarch), "2") # normal as "3" was never saved
361
362 # Try to insert a new value. Here this one is know upstream.
363 self.assertEqual(self.client_ro.getPR(version, pkgarch, checksum7), "3")
364 # Try to insert a completely new value. As the max upstream value is already "3", it should be "3.0"
365 self.assertEqual(self.client_ro.getPR(version, pkgarch, checksum10), "3.0")
366 # Same with another value which only exists in the upstream upstream server
367 # This time, as the upstream server doesn't know it, it will ask its upstream server. So that's a known one.
368 self.assertEqual(self.client_ro.getPR(version, pkgarch, checksum9), "3")
369
370class ScriptTests(unittest.TestCase):
371
372 def setUp(self):
373
374 self.temp_dir = tempfile.TemporaryDirectory(prefix='bb-prserv')
375 self.addCleanup(self.temp_dir.cleanup)
376 self.dbfile = os.path.join(self.temp_dir.name, "prtest.sqlite3")
377
378 def test_1_start_bitbake_prserv(self):
379 try:
380 subprocess.check_call([BIN_DIR / "bitbake-prserv", "--start", "-f", self.dbfile])
381 except subprocess.CalledProcessError as e:
382 self.fail("Failed to start bitbake-prserv: %s" % e.returncode)
383
384 def test_2_stop_bitbake_prserv(self):
385 try:
386 subprocess.check_call([BIN_DIR / "bitbake-prserv", "--stop"])
387 except subprocess.CalledProcessError as e:
388 self.fail("Failed to stop bitbake-prserv: %s" % e.returncode)