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