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