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