tinfoil.py 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252
  1. #
  2. # Copyright OpenEmbedded Contributors
  3. #
  4. # SPDX-License-Identifier: MIT
  5. #
  6. import os
  7. import re
  8. import time
  9. import logging
  10. import bb.tinfoil
  11. from oeqa.selftest.case import OESelftestTestCase
  12. class TinfoilTests(OESelftestTestCase):
  13. """ Basic tests for the tinfoil API """
  14. def test_getvar(self):
  15. with bb.tinfoil.Tinfoil() as tinfoil:
  16. tinfoil.prepare(True)
  17. machine = tinfoil.config_data.getVar('MACHINE')
  18. if not machine:
  19. self.fail('Unable to get MACHINE value - returned %s' % machine)
  20. def test_expand(self):
  21. with bb.tinfoil.Tinfoil() as tinfoil:
  22. tinfoil.prepare(True)
  23. expr = '${@os.getpid()}'
  24. pid = tinfoil.config_data.expand(expr)
  25. if not pid:
  26. self.fail('Unable to expand "%s" - returned %s' % (expr, pid))
  27. def test_getvar_bb_origenv(self):
  28. with bb.tinfoil.Tinfoil() as tinfoil:
  29. tinfoil.prepare(True)
  30. origenv = tinfoil.config_data.getVar('BB_ORIGENV', False)
  31. if not origenv:
  32. self.fail('Unable to get BB_ORIGENV value - returned %s' % origenv)
  33. self.assertEqual(origenv.getVar('HOME', False), os.environ['HOME'])
  34. def test_parse_recipe(self):
  35. with bb.tinfoil.Tinfoil() as tinfoil:
  36. tinfoil.prepare(config_only=False, quiet=2)
  37. testrecipe = 'mdadm'
  38. best = tinfoil.find_best_provider(testrecipe)
  39. if not best:
  40. self.fail('Unable to find recipe providing %s' % testrecipe)
  41. rd = tinfoil.parse_recipe_file(best[3])
  42. self.assertEqual(testrecipe, rd.getVar('PN'))
  43. def test_parse_virtual_recipe(self):
  44. with bb.tinfoil.Tinfoil() as tinfoil:
  45. tinfoil.prepare(config_only=False, quiet=2)
  46. testrecipe = 'nativesdk-gcc'
  47. best = tinfoil.find_best_provider(testrecipe)
  48. if not best:
  49. self.fail('Unable to find recipe providing %s' % testrecipe)
  50. rd = tinfoil.parse_recipe_file(best[3])
  51. self.assertEqual(testrecipe, rd.getVar('PN'))
  52. self.assertIsNotNone(rd.getVar('FILE_LAYERNAME'))
  53. def test_parse_recipe_copy_expand(self):
  54. with bb.tinfoil.Tinfoil() as tinfoil:
  55. tinfoil.prepare(config_only=False, quiet=2)
  56. testrecipe = 'mdadm'
  57. best = tinfoil.find_best_provider(testrecipe)
  58. if not best:
  59. self.fail('Unable to find recipe providing %s' % testrecipe)
  60. rd = tinfoil.parse_recipe_file(best[3])
  61. # Check we can get variable values
  62. self.assertEqual(testrecipe, rd.getVar('PN'))
  63. # Check that expanding a value that includes a variable reference works
  64. self.assertEqual(testrecipe, rd.getVar('BPN'))
  65. # Now check that changing the referenced variable's value in a copy gives that
  66. # value when expanding
  67. localdata = bb.data.createCopy(rd)
  68. localdata.setVar('PN', 'hello')
  69. self.assertEqual('hello', localdata.getVar('BPN'))
  70. # The config_data API to parse_recipe_file is used by:
  71. # layerindex-web layerindex/update_layer.py
  72. def test_parse_recipe_custom_data(self):
  73. with bb.tinfoil.Tinfoil() as tinfoil:
  74. tinfoil.prepare(config_only=False, quiet=2)
  75. localdata = bb.data.createCopy(tinfoil.config_data)
  76. localdata.setVar("TESTVAR", "testval")
  77. testrecipe = 'mdadm'
  78. best = tinfoil.find_best_provider(testrecipe)
  79. if not best:
  80. self.fail('Unable to find recipe providing %s' % testrecipe)
  81. rd = tinfoil.parse_recipe_file(best[3], config_data=localdata)
  82. self.assertEqual("testval", rd.getVar('TESTVAR'))
  83. def test_parse_virtual_recipe_custom_data(self):
  84. with bb.tinfoil.Tinfoil() as tinfoil:
  85. tinfoil.prepare(config_only=False, quiet=2)
  86. localdata = bb.data.createCopy(tinfoil.config_data)
  87. localdata.setVar("TESTVAR", "testval")
  88. testrecipe = 'nativesdk-gcc'
  89. best = tinfoil.find_best_provider(testrecipe)
  90. if not best:
  91. self.fail('Unable to find recipe providing %s' % testrecipe)
  92. rd = tinfoil.parse_recipe_file(best[3], config_data=localdata)
  93. self.assertEqual("testval", rd.getVar('TESTVAR'))
  94. def test_list_recipes(self):
  95. with bb.tinfoil.Tinfoil() as tinfoil:
  96. tinfoil.prepare(config_only=False, quiet=2)
  97. # Check pkg_pn
  98. checkpns = ['tar', 'automake', 'coreutils', 'm4-native', 'nativesdk-gcc']
  99. pkg_pn = tinfoil.cooker.recipecaches[''].pkg_pn
  100. for pn in checkpns:
  101. self.assertIn(pn, pkg_pn)
  102. # Check pkg_fn
  103. checkfns = {'nativesdk-gcc': '^virtual:nativesdk:.*', 'coreutils': '.*/coreutils_.*.bb'}
  104. for fn, pn in tinfoil.cooker.recipecaches[''].pkg_fn.items():
  105. if pn in checkpns:
  106. if pn in checkfns:
  107. self.assertTrue(re.match(checkfns[pn], fn), 'Entry for %s: %s did not match %s' % (pn, fn, checkfns[pn]))
  108. checkpns.remove(pn)
  109. if checkpns:
  110. self.fail('Unable to find pkg_fn entries for: %s' % ', '.join(checkpns))
  111. def test_wait_event(self):
  112. with bb.tinfoil.Tinfoil() as tinfoil:
  113. tinfoil.prepare(config_only=True)
  114. tinfoil.set_event_mask(['bb.event.FilesMatchingFound', 'bb.command.CommandCompleted', 'bb.command.CommandFailed', 'bb.command.CommandExit'])
  115. # Need to drain events otherwise events that were masked may still be in the queue
  116. while tinfoil.wait_event():
  117. pass
  118. pattern = 'conf'
  119. res = tinfoil.run_command('testCookerCommandEvent', pattern, handle_events=False)
  120. self.assertTrue(res)
  121. eventreceived = False
  122. commandcomplete = False
  123. start = time.time()
  124. # Wait for maximum 60s in total so we'd detect spurious heartbeat events for example
  125. while (not (eventreceived == True and commandcomplete == True)
  126. and (time.time() - start < 60)):
  127. # if we received both events (on let's say a good day), we are done
  128. event = tinfoil.wait_event(1)
  129. if event:
  130. if isinstance(event, bb.command.CommandCompleted):
  131. commandcomplete = True
  132. elif isinstance(event, bb.event.FilesMatchingFound):
  133. self.assertEqual(pattern, event._pattern)
  134. self.assertIn('A', event._matches)
  135. self.assertIn('B', event._matches)
  136. eventreceived = True
  137. elif isinstance(event, logging.LogRecord):
  138. continue
  139. else:
  140. self.fail('Unexpected event: %s' % event)
  141. self.assertTrue(commandcomplete, 'Timed out waiting for CommandCompleted event from bitbake server (Matching event received: %s)' % str(eventreceived))
  142. self.assertTrue(eventreceived, 'Did not receive FilesMatchingFound event from bitbake server')
  143. def test_setvariable_clean(self):
  144. # First check that setVariable affects the datastore
  145. with bb.tinfoil.Tinfoil() as tinfoil:
  146. tinfoil.prepare(config_only=True)
  147. tinfoil.run_command('setVariable', 'TESTVAR', 'specialvalue')
  148. self.assertEqual(tinfoil.config_data.getVar('TESTVAR'), 'specialvalue', 'Value set using setVariable is not reflected in client-side getVar()')
  149. # Now check that the setVariable's effects are no longer present
  150. # (this may legitimately break in future if we stop reinitialising
  151. # the datastore, in which case we'll have to reconsider use of
  152. # setVariable entirely)
  153. with bb.tinfoil.Tinfoil() as tinfoil:
  154. tinfoil.prepare(config_only=True)
  155. self.assertNotEqual(tinfoil.config_data.getVar('TESTVAR'), 'specialvalue', 'Value set using setVariable is still present!')
  156. # Now check that setVar on the main datastore works (uses setVariable internally)
  157. with bb.tinfoil.Tinfoil() as tinfoil:
  158. tinfoil.prepare(config_only=True)
  159. tinfoil.config_data.setVar('TESTVAR', 'specialvalue')
  160. value = tinfoil.run_command('getVariable', 'TESTVAR')
  161. self.assertEqual(value, 'specialvalue', 'Value set using config_data.setVar() is not reflected in config_data.getVar()')
  162. def test_datastore_operations(self):
  163. with bb.tinfoil.Tinfoil() as tinfoil:
  164. tinfoil.prepare(config_only=True)
  165. # Test setVarFlag() / getVarFlag()
  166. tinfoil.config_data.setVarFlag('TESTVAR', 'flagname', 'flagval')
  167. value = tinfoil.config_data.getVarFlag('TESTVAR', 'flagname')
  168. self.assertEqual(value, 'flagval', 'Value set using config_data.setVarFlag() is not reflected in config_data.getVarFlag()')
  169. # Test delVarFlag()
  170. tinfoil.config_data.setVarFlag('TESTVAR', 'otherflag', 'othervalue')
  171. tinfoil.config_data.delVarFlag('TESTVAR', 'flagname')
  172. value = tinfoil.config_data.getVarFlag('TESTVAR', 'flagname')
  173. self.assertEqual(value, None, 'Varflag deleted using config_data.delVarFlag() is not reflected in config_data.getVarFlag()')
  174. value = tinfoil.config_data.getVarFlag('TESTVAR', 'otherflag')
  175. self.assertEqual(value, 'othervalue', 'Varflag deleted using config_data.delVarFlag() caused unrelated flag to be removed')
  176. # Test delVar()
  177. tinfoil.config_data.setVar('TESTVAR', 'varvalue')
  178. value = tinfoil.config_data.getVar('TESTVAR')
  179. self.assertEqual(value, 'varvalue', 'Value set using config_data.setVar() is not reflected in config_data.getVar()')
  180. tinfoil.config_data.delVar('TESTVAR')
  181. value = tinfoil.config_data.getVar('TESTVAR')
  182. self.assertEqual(value, None, 'Variable deleted using config_data.delVar() appears to still have a value')
  183. # Test renameVar()
  184. tinfoil.config_data.setVar('TESTVAROLD', 'origvalue')
  185. tinfoil.config_data.renameVar('TESTVAROLD', 'TESTVARNEW')
  186. value = tinfoil.config_data.getVar('TESTVAROLD')
  187. self.assertEqual(value, None, 'Variable renamed using config_data.renameVar() still seems to exist')
  188. value = tinfoil.config_data.getVar('TESTVARNEW')
  189. self.assertEqual(value, 'origvalue', 'Variable renamed using config_data.renameVar() does not appear with new name')
  190. # Test overrides
  191. tinfoil.config_data.setVar('TESTVAR', 'original')
  192. tinfoil.config_data.setVar('TESTVAR:overrideone', 'one')
  193. tinfoil.config_data.setVar('TESTVAR:overridetwo', 'two')
  194. tinfoil.config_data.appendVar('OVERRIDES', ':overrideone')
  195. value = tinfoil.config_data.getVar('TESTVAR')
  196. self.assertEqual(value, 'one', 'Variable overrides not functioning correctly')
  197. def test_variable_history(self):
  198. # Basic test to ensure that variable history works when tracking=True
  199. with bb.tinfoil.Tinfoil(tracking=True) as tinfoil:
  200. tinfoil.prepare(config_only=False, quiet=2)
  201. # Note that _tracking for any datastore we get will be
  202. # false here, that's currently expected - so we can't check
  203. # for that
  204. history = tinfoil.config_data.varhistory.variable('DL_DIR')
  205. for entry in history:
  206. if entry['file'].endswith('/bitbake.conf'):
  207. if entry['op'] in ['set', 'set?']:
  208. break
  209. else:
  210. self.fail('Did not find history entry setting DL_DIR in bitbake.conf. History: %s' % history)
  211. # Check it works for recipes as well
  212. testrecipe = 'zlib'
  213. rd = tinfoil.parse_recipe(testrecipe)
  214. history = rd.varhistory.variable('LICENSE')
  215. bbfound = -1
  216. recipefound = -1
  217. for i, entry in enumerate(history):
  218. if entry['file'].endswith('/bitbake.conf'):
  219. if entry['detail'] == 'INVALID' and entry['op'] in ['set', 'set?']:
  220. bbfound = i
  221. elif entry['file'].endswith('.bb'):
  222. if entry['op'] == 'set':
  223. recipefound = i
  224. if bbfound == -1:
  225. self.fail('Did not find history entry setting LICENSE in bitbake.conf parsing %s recipe. History: %s' % (testrecipe, history))
  226. if recipefound == -1:
  227. self.fail('Did not find history entry setting LICENSE in %s recipe. History: %s' % (testrecipe, history))
  228. if bbfound > recipefound:
  229. self.fail('History entry setting LICENSE in %s recipe and in bitbake.conf in wrong order. History: %s' % (testrecipe, history))