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