tests.py 18 KB

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