| Brad Bishop | c342db3 | 2019-05-15 21:57:59 -0400 | [diff] [blame] | 1 | # | 
|  | 2 | # SPDX-License-Identifier: MIT | 
|  | 3 | # | 
|  | 4 |  | 
| Brad Bishop | d7bf8c1 | 2018-02-25 22:55:05 -0500 | [diff] [blame] | 5 | import os | 
|  | 6 | import re | 
|  | 7 | import time | 
|  | 8 | import logging | 
|  | 9 | import bb.tinfoil | 
|  | 10 |  | 
|  | 11 | from oeqa.selftest.case import OESelftestTestCase | 
|  | 12 | from oeqa.utils.commands import runCmd | 
| Brad Bishop | d7bf8c1 | 2018-02-25 22:55:05 -0500 | [diff] [blame] | 13 |  | 
|  | 14 | class TinfoilTests(OESelftestTestCase): | 
|  | 15 | """ Basic tests for the tinfoil API """ | 
|  | 16 |  | 
| Brad Bishop | d7bf8c1 | 2018-02-25 22:55:05 -0500 | [diff] [blame] | 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 |  | 
| Brad Bishop | d7bf8c1 | 2018-02-25 22:55:05 -0500 | [diff] [blame] | 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 |  | 
| Brad Bishop | d7bf8c1 | 2018-02-25 22:55:05 -0500 | [diff] [blame] | 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 |  | 
| Brad Bishop | d7bf8c1 | 2018-02-25 22:55:05 -0500 | [diff] [blame] | 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 |  | 
| Brad Bishop | d7bf8c1 | 2018-02-25 22:55:05 -0500 | [diff] [blame] | 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 |  | 
| Brad Bishop | d7bf8c1 | 2018-02-25 22:55:05 -0500 | [diff] [blame] | 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 |  | 
| Brad Bishop | d7bf8c1 | 2018-02-25 22:55:05 -0500 | [diff] [blame] | 86 | def test_wait_event(self): | 
|  | 87 | with bb.tinfoil.Tinfoil() as tinfoil: | 
|  | 88 | tinfoil.prepare(config_only=True) | 
|  | 89 |  | 
| Andrew Geissler | 9aee500 | 2022-03-30 16:27:02 +0000 | [diff] [blame^] | 90 | tinfoil.set_event_mask(['bb.event.FilesMatchingFound', 'bb.command.CommandCompleted', 'bb.command.CommandFailed', 'bb.command.CommandExit']) | 
| Brad Bishop | d7bf8c1 | 2018-02-25 22:55:05 -0500 | [diff] [blame] | 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' | 
| Andrew Geissler | 9aee500 | 2022-03-30 16:27:02 +0000 | [diff] [blame^] | 97 | res = tinfoil.run_command('testCookerCommandEvent', pattern, handle_events=False) | 
| Brad Bishop | d7bf8c1 | 2018-02-25 22:55:05 -0500 | [diff] [blame] | 98 | self.assertTrue(res) | 
|  | 99 |  | 
|  | 100 | eventreceived = False | 
|  | 101 | commandcomplete = False | 
|  | 102 | start = time.time() | 
| Andrew Geissler | d1e8949 | 2021-02-12 15:35:20 -0600 | [diff] [blame] | 103 | # Wait for maximum 60s in total so we'd detect spurious heartbeat events for example | 
| Andrew Geissler | d1e8949 | 2021-02-12 15:35:20 -0600 | [diff] [blame] | 104 | while (not (eventreceived == True and commandcomplete == True) | 
|  | 105 | and (time.time() - start < 60)): | 
|  | 106 | # if we received both events (on let's say a good day), we are done | 
| Brad Bishop | d7bf8c1 | 2018-02-25 22:55:05 -0500 | [diff] [blame] | 107 | event = tinfoil.wait_event(1) | 
|  | 108 | if event: | 
|  | 109 | if isinstance(event, bb.command.CommandCompleted): | 
|  | 110 | commandcomplete = True | 
|  | 111 | elif isinstance(event, bb.event.FilesMatchingFound): | 
|  | 112 | self.assertEqual(pattern, event._pattern) | 
| Patrick Williams | 93c203f | 2021-10-06 16:15:23 -0500 | [diff] [blame] | 113 | self.assertIn('A', event._matches) | 
|  | 114 | self.assertIn('B', event._matches) | 
| Brad Bishop | d7bf8c1 | 2018-02-25 22:55:05 -0500 | [diff] [blame] | 115 | eventreceived = True | 
|  | 116 | elif isinstance(event, logging.LogRecord): | 
|  | 117 | continue | 
|  | 118 | else: | 
|  | 119 | self.fail('Unexpected event: %s' % event) | 
|  | 120 |  | 
| Andrew Geissler | 9aee500 | 2022-03-30 16:27:02 +0000 | [diff] [blame^] | 121 | self.assertTrue(commandcomplete, 'Timed out waiting for CommandCompleted event from bitbake server (Matching event received: %s)' % str(eventreceived)) | 
| Brad Bishop | d7bf8c1 | 2018-02-25 22:55:05 -0500 | [diff] [blame] | 122 | self.assertTrue(eventreceived, 'Did not receive FilesMatchingFound event from bitbake server') | 
|  | 123 |  | 
| Brad Bishop | d7bf8c1 | 2018-02-25 22:55:05 -0500 | [diff] [blame] | 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 |  | 
| Brad Bishop | d7bf8c1 | 2018-02-25 22:55:05 -0500 | [diff] [blame] | 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') | 
| Patrick Williams | 213cb26 | 2021-08-07 19:21:33 -0500 | [diff] [blame] | 176 | tinfoil.config_data.setVar('TESTVAR:overrideone', 'one') | 
|  | 177 | tinfoil.config_data.setVar('TESTVAR:overridetwo', 'two') | 
| Brad Bishop | d7bf8c1 | 2018-02-25 22:55:05 -0500 | [diff] [blame] | 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)) |