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