tests.py 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171
  1. #! /usr/bin/env python3
  2. #
  3. # Copyright (C) 2018-2019 Garmin Ltd.
  4. #
  5. # SPDX-License-Identifier: GPL-2.0-only
  6. #
  7. from . import create_server, create_client
  8. import hashlib
  9. import logging
  10. import multiprocessing
  11. import os
  12. import sys
  13. import tempfile
  14. import threading
  15. import unittest
  16. import socket
  17. class TestHashEquivalenceServer(object):
  18. METHOD = 'TestMethod'
  19. def _run_server(self):
  20. # logging.basicConfig(level=logging.DEBUG, filename='bbhashserv.log', filemode='w',
  21. # format='%(levelname)s %(filename)s:%(lineno)d %(message)s')
  22. self.server.serve_forever()
  23. def setUp(self):
  24. if sys.version_info < (3, 5, 0):
  25. self.skipTest('Python 3.5 or later required')
  26. self.temp_dir = tempfile.TemporaryDirectory(prefix='bb-hashserv')
  27. self.dbfile = os.path.join(self.temp_dir.name, 'db.sqlite')
  28. self.server = create_server(self.get_server_addr(), self.dbfile)
  29. self.server_thread = multiprocessing.Process(target=self._run_server)
  30. self.server_thread.start()
  31. self.client = create_client(self.server.address)
  32. def tearDown(self):
  33. # Shutdown server
  34. s = getattr(self, 'server', None)
  35. if s is not None:
  36. self.server_thread.terminate()
  37. self.server_thread.join()
  38. self.client.close()
  39. self.temp_dir.cleanup()
  40. def test_create_hash(self):
  41. # Simple test that hashes can be created
  42. taskhash = '35788efcb8dfb0a02659d81cf2bfd695fb30faf9'
  43. outhash = '2765d4a5884be49b28601445c2760c5f21e7e5c0ee2b7e3fce98fd7e5970796f'
  44. unihash = 'f46d3fbb439bd9b921095da657a4de906510d2cd'
  45. result = self.client.get_unihash(self.METHOD, taskhash)
  46. self.assertIsNone(result, msg='Found unexpected task, %r' % result)
  47. result = self.client.report_unihash(taskhash, self.METHOD, outhash, unihash)
  48. self.assertEqual(result['unihash'], unihash, 'Server returned bad unihash')
  49. def test_create_equivalent(self):
  50. # Tests that a second reported task with the same outhash will be
  51. # assigned the same unihash
  52. taskhash = '53b8dce672cb6d0c73170be43f540460bfc347b4'
  53. outhash = '5a9cb1649625f0bf41fc7791b635cd9c2d7118c7f021ba87dcd03f72b67ce7a8'
  54. unihash = 'f37918cc02eb5a520b1aff86faacbc0a38124646'
  55. result = self.client.report_unihash(taskhash, self.METHOD, outhash, unihash)
  56. self.assertEqual(result['unihash'], unihash, 'Server returned bad unihash')
  57. # Report a different task with the same outhash. The returned unihash
  58. # should match the first task
  59. taskhash2 = '3bf6f1e89d26205aec90da04854fbdbf73afe6b4'
  60. unihash2 = 'af36b199320e611fbb16f1f277d3ee1d619ca58b'
  61. result = self.client.report_unihash(taskhash2, self.METHOD, outhash, unihash2)
  62. self.assertEqual(result['unihash'], unihash, 'Server returned bad unihash')
  63. def test_duplicate_taskhash(self):
  64. # Tests that duplicate reports of the same taskhash with different
  65. # outhash & unihash always return the unihash from the first reported
  66. # taskhash
  67. taskhash = '8aa96fcffb5831b3c2c0cb75f0431e3f8b20554a'
  68. outhash = 'afe240a439959ce86f5e322f8c208e1fedefea9e813f2140c81af866cc9edf7e'
  69. unihash = '218e57509998197d570e2c98512d0105985dffc9'
  70. self.client.report_unihash(taskhash, self.METHOD, outhash, unihash)
  71. result = self.client.get_unihash(self.METHOD, taskhash)
  72. self.assertEqual(result, unihash)
  73. outhash2 = '0904a7fe3dc712d9fd8a74a616ddca2a825a8ee97adf0bd3fc86082c7639914d'
  74. unihash2 = 'ae9a7d252735f0dafcdb10e2e02561ca3a47314c'
  75. self.client.report_unihash(taskhash, self.METHOD, outhash2, unihash2)
  76. result = self.client.get_unihash(self.METHOD, taskhash)
  77. self.assertEqual(result, unihash)
  78. outhash3 = '77623a549b5b1a31e3732dfa8fe61d7ce5d44b3370f253c5360e136b852967b4'
  79. unihash3 = '9217a7d6398518e5dc002ed58f2cbbbc78696603'
  80. self.client.report_unihash(taskhash, self.METHOD, outhash3, unihash3)
  81. result = self.client.get_unihash(self.METHOD, taskhash)
  82. self.assertEqual(result, unihash)
  83. def test_huge_message(self):
  84. # Simple test that hashes can be created
  85. taskhash = 'c665584ee6817aa99edfc77a44dd853828279370'
  86. outhash = '3c979c3db45c569f51ab7626a4651074be3a9d11a84b1db076f5b14f7d39db44'
  87. unihash = '90e9bc1d1f094c51824adca7f8ea79a048d68824'
  88. result = self.client.get_unihash(self.METHOD, taskhash)
  89. self.assertIsNone(result, msg='Found unexpected task, %r' % result)
  90. siginfo = "0" * (self.client.max_chunk * 4)
  91. result = self.client.report_unihash(taskhash, self.METHOD, outhash, unihash, {
  92. 'outhash_siginfo': siginfo
  93. })
  94. self.assertEqual(result['unihash'], unihash, 'Server returned bad unihash')
  95. result = self.client.get_taskhash(self.METHOD, taskhash, True)
  96. self.assertEqual(result['taskhash'], taskhash)
  97. self.assertEqual(result['unihash'], unihash)
  98. self.assertEqual(result['method'], self.METHOD)
  99. self.assertEqual(result['outhash'], outhash)
  100. self.assertEqual(result['outhash_siginfo'], siginfo)
  101. def test_stress(self):
  102. def query_server(failures):
  103. client = Client(self.server.address)
  104. try:
  105. for i in range(1000):
  106. taskhash = hashlib.sha256()
  107. taskhash.update(str(i).encode('utf-8'))
  108. taskhash = taskhash.hexdigest()
  109. result = client.get_unihash(self.METHOD, taskhash)
  110. if result != taskhash:
  111. failures.append("taskhash mismatch: %s != %s" % (result, taskhash))
  112. finally:
  113. client.close()
  114. # Report hashes
  115. for i in range(1000):
  116. taskhash = hashlib.sha256()
  117. taskhash.update(str(i).encode('utf-8'))
  118. taskhash = taskhash.hexdigest()
  119. self.client.report_unihash(taskhash, self.METHOD, taskhash, taskhash)
  120. failures = []
  121. threads = [threading.Thread(target=query_server, args=(failures,)) for t in range(100)]
  122. for t in threads:
  123. t.start()
  124. for t in threads:
  125. t.join()
  126. self.assertFalse(failures)
  127. class TestHashEquivalenceUnixServer(TestHashEquivalenceServer, unittest.TestCase):
  128. def get_server_addr(self):
  129. return "unix://" + os.path.join(self.temp_dir.name, 'sock')
  130. class TestHashEquivalenceTCPServer(TestHashEquivalenceServer, unittest.TestCase):
  131. def get_server_addr(self):
  132. # Some hosts cause asyncio module to misbehave, when IPv6 is not enabled.
  133. # If IPv6 is enabled, it should be safe to use localhost directly, in general
  134. # case it is more reliable to resolve the IP address explicitly.
  135. return socket.gethostbyname("localhost") + ":0"