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