devtool.py 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495
  1. import unittest
  2. import os
  3. import logging
  4. import re
  5. import shutil
  6. import tempfile
  7. import glob
  8. import oeqa.utils.ftools as ftools
  9. from oeqa.selftest.base import oeSelfTest
  10. from oeqa.utils.commands import runCmd, bitbake, get_bb_var
  11. from oeqa.utils.decorators import testcase
  12. class DevtoolTests(oeSelfTest):
  13. def test_create_workspace(self):
  14. # Check preconditions
  15. workspacedir = os.path.join(self.builddir, 'workspace')
  16. self.assertTrue(not os.path.exists(workspacedir), 'This test cannot be run with a workspace directory under the build directory')
  17. result = runCmd('bitbake-layers show-layers')
  18. self.assertTrue('/workspace' not in result.output, 'This test cannot be run with a workspace layer in bblayers.conf')
  19. # Try creating a workspace layer with a specific path
  20. tempdir = tempfile.mkdtemp(prefix='devtoolqa')
  21. self.track_for_cleanup(tempdir)
  22. result = runCmd('devtool create-workspace %s' % tempdir)
  23. self.assertTrue(os.path.isfile(os.path.join(tempdir, 'conf', 'layer.conf')))
  24. result = runCmd('bitbake-layers show-layers')
  25. self.assertIn(tempdir, result.output)
  26. # Try creating a workspace layer with the default path
  27. self.track_for_cleanup(workspacedir)
  28. self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
  29. result = runCmd('devtool create-workspace')
  30. self.assertTrue(os.path.isfile(os.path.join(workspacedir, 'conf', 'layer.conf')))
  31. result = runCmd('bitbake-layers show-layers')
  32. self.assertNotIn(tempdir, result.output)
  33. self.assertIn(workspacedir, result.output)
  34. def test_recipetool_create(self):
  35. # Try adding a recipe
  36. tempdir = tempfile.mkdtemp(prefix='devtoolqa')
  37. self.track_for_cleanup(tempdir)
  38. tempsrc = os.path.join(tempdir, 'srctree')
  39. os.makedirs(tempsrc)
  40. recipefile = os.path.join(tempdir, 'logrotate_3.8.7.bb')
  41. srcuri = 'https://fedorahosted.org/releases/l/o/logrotate/logrotate-3.8.7.tar.gz'
  42. result = runCmd('recipetool create -o %s %s -x %s' % (recipefile, srcuri, tempsrc))
  43. self.assertTrue(os.path.isfile(recipefile))
  44. checkvars = {}
  45. checkvars['LICENSE'] = 'GPLv2'
  46. checkvars['LIC_FILES_CHKSUM'] = 'file://COPYING;md5=18810669f13b87348459e611d31ab760'
  47. checkvars['SRC_URI'] = 'https://fedorahosted.org/releases/l/o/logrotate/logrotate-${PV}.tar.gz'
  48. checkvars['SRC_URI[md5sum]'] = '99e08503ef24c3e2e3ff74cc5f3be213'
  49. checkvars['SRC_URI[sha256sum]'] = 'f6ba691f40e30e640efa2752c1f9499a3f9738257660994de70a45fe00d12b64'
  50. with open(recipefile, 'r') as f:
  51. for line in f:
  52. if '=' in line:
  53. splitline = line.split('=', 1)
  54. var = splitline[0].rstrip()
  55. value = splitline[1].strip().strip('"')
  56. if var in checkvars:
  57. needvalue = checkvars.pop(var)
  58. self.assertEqual(value, needvalue)
  59. if line.startswith('inherit '):
  60. inherits = line.split()[1:]
  61. self.assertEqual(checkvars, {}, 'Some variables not found')
  62. def test_recipetool_create_git(self):
  63. # Ensure we have the right data in shlibs/pkgdata
  64. bitbake('libpng pango libx11 libxext')
  65. # Try adding a recipe
  66. tempdir = tempfile.mkdtemp(prefix='devtoolqa')
  67. self.track_for_cleanup(tempdir)
  68. tempsrc = os.path.join(tempdir, 'srctree')
  69. os.makedirs(tempsrc)
  70. recipefile = os.path.join(tempdir, 'libmatchbox.bb')
  71. srcuri = 'git://git.yoctoproject.org/libmatchbox'
  72. result = runCmd('recipetool create -o %s %s -x %s' % (recipefile, srcuri, tempsrc))
  73. self.assertTrue(os.path.isfile(recipefile), 'recipetool did not create recipe file; output:\n%s' % result.output)
  74. checkvars = {}
  75. checkvars['LICENSE'] = 'LGPLv2.1'
  76. checkvars['LIC_FILES_CHKSUM'] = 'file://COPYING;md5=7fbc338309ac38fefcd64b04bb903e34'
  77. checkvars['S'] = '${WORKDIR}/git'
  78. checkvars['PV'] = '1.0+git${SRCPV}'
  79. checkvars['SRC_URI'] = srcuri
  80. checkvars['DEPENDS'] = 'libpng pango libx11 libxext'
  81. inherits = []
  82. with open(recipefile, 'r') as f:
  83. for line in f:
  84. if '=' in line:
  85. splitline = line.split('=', 1)
  86. var = splitline[0].rstrip()
  87. value = splitline[1].strip().strip('"')
  88. if var in checkvars:
  89. needvalue = checkvars.pop(var)
  90. self.assertEqual(value, needvalue)
  91. if line.startswith('inherit '):
  92. inherits = line.split()[1:]
  93. self.assertEqual(checkvars, {}, 'Some variables not found')
  94. self.assertIn('autotools', inherits, 'Missing inherit of autotools')
  95. self.assertIn('pkgconfig', inherits, 'Missing inherit of pkgconfig')
  96. def test_devtool_add(self):
  97. # Check preconditions
  98. workspacedir = os.path.join(self.builddir, 'workspace')
  99. self.assertTrue(not os.path.exists(workspacedir), 'This test cannot be run with a workspace directory under the build directory')
  100. # Fetch source
  101. tempdir = tempfile.mkdtemp(prefix='devtoolqa')
  102. self.track_for_cleanup(tempdir)
  103. url = 'http://www.ivarch.com/programs/sources/pv-1.5.3.tar.bz2'
  104. result = runCmd('wget %s' % url, cwd=tempdir)
  105. result = runCmd('tar xfv pv-1.5.3.tar.bz2', cwd=tempdir)
  106. srcdir = os.path.join(tempdir, 'pv-1.5.3')
  107. self.assertTrue(os.path.isfile(os.path.join(srcdir, 'configure')), 'Unable to find configure script in source directory')
  108. # Test devtool add
  109. self.track_for_cleanup(workspacedir)
  110. self.add_command_to_tearDown('bitbake -c cleansstate pv')
  111. self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
  112. result = runCmd('devtool add pv %s' % srcdir)
  113. self.assertTrue(os.path.exists(os.path.join(workspacedir, 'conf', 'layer.conf')), 'Workspace directory not created')
  114. # Test devtool status
  115. result = runCmd('devtool status')
  116. self.assertIn('pv', result.output)
  117. self.assertIn(srcdir, result.output)
  118. # Clean up anything in the workdir/sysroot/sstate cache (have to do this *after* devtool add since the recipe only exists then)
  119. bitbake('pv -c cleansstate')
  120. # Test devtool build
  121. result = runCmd('devtool build pv')
  122. installdir = get_bb_var('D', 'pv')
  123. self.assertTrue(installdir, 'Could not query installdir variable')
  124. bindir = get_bb_var('bindir', 'pv')
  125. self.assertTrue(bindir, 'Could not query bindir variable')
  126. if bindir[0] == '/':
  127. bindir = bindir[1:]
  128. self.assertTrue(os.path.isfile(os.path.join(installdir, bindir, 'pv')), 'pv binary not found in D')
  129. def test_devtool_add_library(self):
  130. # Check preconditions
  131. workspacedir = os.path.join(self.builddir, 'workspace')
  132. self.assertTrue(not os.path.exists(workspacedir), 'This test cannot be run with a workspace directory under the build directory')
  133. # We don't have the ability to pick up this dependency automatically yet...
  134. bitbake('libusb1')
  135. # Fetch source
  136. tempdir = tempfile.mkdtemp(prefix='devtoolqa')
  137. self.track_for_cleanup(tempdir)
  138. url = 'http://www.intra2net.com/en/developer/libftdi/download/libftdi1-1.1.tar.bz2'
  139. result = runCmd('wget %s' % url, cwd=tempdir)
  140. result = runCmd('tar xfv libftdi1-1.1.tar.bz2', cwd=tempdir)
  141. srcdir = os.path.join(tempdir, 'libftdi1-1.1')
  142. self.assertTrue(os.path.isfile(os.path.join(srcdir, 'CMakeLists.txt')), 'Unable to find CMakeLists.txt in source directory')
  143. # Test devtool add
  144. self.track_for_cleanup(workspacedir)
  145. self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
  146. result = runCmd('devtool add libftdi %s' % srcdir)
  147. self.assertTrue(os.path.exists(os.path.join(workspacedir, 'conf', 'layer.conf')), 'Workspace directory not created')
  148. # Test devtool status
  149. result = runCmd('devtool status')
  150. self.assertIn('libftdi', result.output)
  151. self.assertIn(srcdir, result.output)
  152. # Clean up anything in the workdir/sysroot/sstate cache (have to do this *after* devtool add since the recipe only exists then)
  153. bitbake('libftdi -c cleansstate')
  154. # Test devtool build
  155. result = runCmd('devtool build libftdi')
  156. staging_libdir = get_bb_var('STAGING_LIBDIR', 'libftdi')
  157. self.assertTrue(staging_libdir, 'Could not query STAGING_LIBDIR variable')
  158. self.assertTrue(os.path.isfile(os.path.join(staging_libdir, 'libftdi1.so.2.1.0')), 'libftdi binary not found in STAGING_LIBDIR')
  159. # Test devtool reset
  160. stampprefix = get_bb_var('STAMP', 'libftdi')
  161. result = runCmd('devtool reset libftdi')
  162. result = runCmd('devtool status')
  163. self.assertNotIn('libftdi', result.output)
  164. self.assertTrue(stampprefix, 'Unable to get STAMP value for recipe libftdi')
  165. matches = glob.glob(stampprefix + '*')
  166. self.assertFalse(matches, 'Stamp files exist for recipe libftdi that should have been cleaned')
  167. self.assertFalse(os.path.isfile(os.path.join(staging_libdir, 'libftdi1.so.2.1.0')), 'libftdi binary still found in STAGING_LIBDIR after cleaning')
  168. def test_devtool_modify(self):
  169. # Check preconditions
  170. workspacedir = os.path.join(self.builddir, 'workspace')
  171. self.assertTrue(not os.path.exists(workspacedir), 'This test cannot be run with a workspace directory under the build directory')
  172. # Clean up anything in the workdir/sysroot/sstate cache
  173. bitbake('mdadm -c cleansstate')
  174. # Try modifying a recipe
  175. tempdir = tempfile.mkdtemp(prefix='devtoolqa')
  176. self.track_for_cleanup(tempdir)
  177. self.track_for_cleanup(workspacedir)
  178. self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
  179. self.add_command_to_tearDown('bitbake -c clean mdadm')
  180. result = runCmd('devtool modify mdadm -x %s' % tempdir)
  181. self.assertTrue(os.path.exists(os.path.join(tempdir, 'Makefile')), 'Extracted source could not be found')
  182. self.assertTrue(os.path.isdir(os.path.join(tempdir, '.git')), 'git repository for external source tree not found')
  183. self.assertTrue(os.path.exists(os.path.join(workspacedir, 'conf', 'layer.conf')), 'Workspace directory not created')
  184. matches = glob.glob(os.path.join(workspacedir, 'appends', 'mdadm_*.bbappend'))
  185. self.assertTrue(matches, 'bbappend not created')
  186. # Test devtool status
  187. result = runCmd('devtool status')
  188. self.assertIn('mdadm', result.output)
  189. self.assertIn(tempdir, result.output)
  190. # Check git repo
  191. result = runCmd('git status --porcelain', cwd=tempdir)
  192. self.assertEqual(result.output.strip(), "", 'Created git repo is not clean')
  193. result = runCmd('git symbolic-ref HEAD', cwd=tempdir)
  194. self.assertEqual(result.output.strip(), "refs/heads/devtool", 'Wrong branch in git repo')
  195. # Try building
  196. bitbake('mdadm')
  197. # Try making (minor) modifications to the source
  198. result = runCmd("sed -i 's!^\.TH.*!.TH MDADM 8 \"\" v9.999-custom!' %s" % os.path.join(tempdir, 'mdadm.8.in'))
  199. bitbake('mdadm -c package')
  200. pkgd = get_bb_var('PKGD', 'mdadm')
  201. self.assertTrue(pkgd, 'Could not query PKGD variable')
  202. mandir = get_bb_var('mandir', 'mdadm')
  203. self.assertTrue(mandir, 'Could not query mandir variable')
  204. if mandir[0] == '/':
  205. mandir = mandir[1:]
  206. with open(os.path.join(pkgd, mandir, 'man8', 'mdadm.8'), 'r') as f:
  207. for line in f:
  208. if line.startswith('.TH'):
  209. self.assertEqual(line.rstrip(), '.TH MDADM 8 "" v9.999-custom', 'man file not modified')
  210. # Test devtool reset
  211. stampprefix = get_bb_var('STAMP', 'mdadm')
  212. result = runCmd('devtool reset mdadm')
  213. result = runCmd('devtool status')
  214. self.assertNotIn('mdadm', result.output)
  215. self.assertTrue(stampprefix, 'Unable to get STAMP value for recipe mdadm')
  216. matches = glob.glob(stampprefix + '*')
  217. self.assertFalse(matches, 'Stamp files exist for recipe mdadm that should have been cleaned')
  218. def test_devtool_modify_invalid(self):
  219. # Check preconditions
  220. workspacedir = os.path.join(self.builddir, 'workspace')
  221. self.assertTrue(not os.path.exists(workspacedir), 'This test cannot be run with a workspace directory under the build directory')
  222. # Try modifying some recipes
  223. tempdir = tempfile.mkdtemp(prefix='devtoolqa')
  224. self.track_for_cleanup(tempdir)
  225. self.track_for_cleanup(workspacedir)
  226. self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
  227. testrecipes = 'perf gcc-source kernel-devsrc package-index core-image-minimal meta-toolchain packagegroup-core-sdk meta-ide-support'.split()
  228. for testrecipe in testrecipes:
  229. # Check it's a valid recipe
  230. bitbake('%s -e' % testrecipe)
  231. # devtool extract should fail
  232. result = runCmd('devtool extract %s %s' % (testrecipe, os.path.join(tempdir, testrecipe)), ignore_status=True)
  233. self.assertNotEqual(result.status, 0, 'devtool extract on %s should have failed' % testrecipe)
  234. self.assertNotIn('Fetching ', result.output, 'devtool extract on %s should have errored out before trying to fetch' % testrecipe)
  235. self.assertIn('ERROR: ', result.output, 'devtool extract on %s should have given an ERROR' % testrecipe)
  236. # devtool modify should fail
  237. result = runCmd('devtool modify %s -x %s' % (testrecipe, os.path.join(tempdir, testrecipe)), ignore_status=True)
  238. self.assertNotEqual(result.status, 0, 'devtool modify on %s should have failed' % testrecipe)
  239. self.assertIn('ERROR: ', result.output, 'devtool modify on %s should have given an ERROR' % testrecipe)
  240. def test_devtool_modify_git(self):
  241. # Check preconditions
  242. workspacedir = os.path.join(self.builddir, 'workspace')
  243. self.assertTrue(not os.path.exists(workspacedir), 'This test cannot be run with a workspace directory under the build directory')
  244. testrecipe = 'mkelfimage'
  245. src_uri = get_bb_var('SRC_URI', testrecipe)
  246. self.assertIn('git://', src_uri, 'This test expects the %s recipe to be a git recipe' % testrecipe)
  247. # Clean up anything in the workdir/sysroot/sstate cache
  248. bitbake('%s -c cleansstate' % testrecipe)
  249. # Try modifying a recipe
  250. tempdir = tempfile.mkdtemp(prefix='devtoolqa')
  251. self.track_for_cleanup(tempdir)
  252. self.track_for_cleanup(workspacedir)
  253. self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
  254. self.add_command_to_tearDown('bitbake -c clean %s' % testrecipe)
  255. result = runCmd('devtool modify %s -x %s' % (testrecipe, tempdir))
  256. self.assertTrue(os.path.exists(os.path.join(tempdir, 'Makefile')), 'Extracted source could not be found')
  257. self.assertTrue(os.path.isdir(os.path.join(tempdir, '.git')), 'git repository for external source tree not found')
  258. self.assertTrue(os.path.exists(os.path.join(workspacedir, 'conf', 'layer.conf')), 'Workspace directory not created')
  259. matches = glob.glob(os.path.join(workspacedir, 'appends', 'mkelfimage_*.bbappend'))
  260. self.assertTrue(matches, 'bbappend not created')
  261. # Test devtool status
  262. result = runCmd('devtool status')
  263. self.assertIn(testrecipe, result.output)
  264. self.assertIn(tempdir, result.output)
  265. # Check git repo
  266. result = runCmd('git status --porcelain', cwd=tempdir)
  267. self.assertEqual(result.output.strip(), "", 'Created git repo is not clean')
  268. result = runCmd('git symbolic-ref HEAD', cwd=tempdir)
  269. self.assertEqual(result.output.strip(), "refs/heads/devtool", 'Wrong branch in git repo')
  270. # Try building
  271. bitbake(testrecipe)
  272. def test_devtool_update_recipe(self):
  273. # Check preconditions
  274. workspacedir = os.path.join(self.builddir, 'workspace')
  275. self.assertTrue(not os.path.exists(workspacedir), 'This test cannot be run with a workspace directory under the build directory')
  276. testrecipe = 'minicom'
  277. recipefile = get_bb_var('FILE', testrecipe)
  278. src_uri = get_bb_var('SRC_URI', testrecipe)
  279. self.assertNotIn('git://', src_uri, 'This test expects the %s recipe to NOT be a git recipe' % testrecipe)
  280. result = runCmd('git status . --porcelain', cwd=os.path.dirname(recipefile))
  281. self.assertEqual(result.output.strip(), "", '%s recipe is not clean' % testrecipe)
  282. # First, modify a recipe
  283. tempdir = tempfile.mkdtemp(prefix='devtoolqa')
  284. self.track_for_cleanup(tempdir)
  285. self.track_for_cleanup(workspacedir)
  286. self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
  287. # (don't bother with cleaning the recipe on teardown, we won't be building it)
  288. result = runCmd('devtool modify %s -x %s' % (testrecipe, tempdir))
  289. # Check git repo
  290. self.assertTrue(os.path.isdir(os.path.join(tempdir, '.git')), 'git repository for external source tree not found')
  291. result = runCmd('git status --porcelain', cwd=tempdir)
  292. self.assertEqual(result.output.strip(), "", 'Created git repo is not clean')
  293. result = runCmd('git symbolic-ref HEAD', cwd=tempdir)
  294. self.assertEqual(result.output.strip(), "refs/heads/devtool", 'Wrong branch in git repo')
  295. # Add a couple of commits
  296. # FIXME: this only tests adding, need to also test update and remove
  297. result = runCmd('echo "Additional line" >> README', cwd=tempdir)
  298. result = runCmd('git commit -a -m "Change the README"', cwd=tempdir)
  299. result = runCmd('echo "A new file" > devtool-new-file', cwd=tempdir)
  300. result = runCmd('git add devtool-new-file', cwd=tempdir)
  301. result = runCmd('git commit -m "Add a new file"', cwd=tempdir)
  302. self.add_command_to_tearDown('cd %s; rm %s/*.patch; git checkout %s %s' % (os.path.dirname(recipefile), testrecipe, testrecipe, os.path.basename(recipefile)))
  303. result = runCmd('devtool update-recipe %s' % testrecipe)
  304. result = runCmd('git status . --porcelain', cwd=os.path.dirname(recipefile))
  305. self.assertNotEqual(result.output.strip(), "", '%s recipe should be modified' % testrecipe)
  306. status = result.output.splitlines()
  307. self.assertEqual(len(status), 3, 'Less/more files modified than expected. Entire status:\n%s' % result.output)
  308. for line in status:
  309. if line.endswith('0001-Change-the-README.patch'):
  310. self.assertEqual(line[:3], '?? ', 'Unexpected status in line: %s' % line)
  311. elif line.endswith('0002-Add-a-new-file.patch'):
  312. self.assertEqual(line[:3], '?? ', 'Unexpected status in line: %s' % line)
  313. elif re.search('%s_[^_]*.bb$' % testrecipe, line):
  314. self.assertEqual(line[:3], ' M ', 'Unexpected status in line: %s' % line)
  315. else:
  316. raise AssertionError('Unexpected modified file in status: %s' % line)
  317. def test_devtool_update_recipe_git(self):
  318. # Check preconditions
  319. workspacedir = os.path.join(self.builddir, 'workspace')
  320. self.assertTrue(not os.path.exists(workspacedir), 'This test cannot be run with a workspace directory under the build directory')
  321. testrecipe = 'mtd-utils'
  322. recipefile = get_bb_var('FILE', testrecipe)
  323. src_uri = get_bb_var('SRC_URI', testrecipe)
  324. self.assertIn('git://', src_uri, 'This test expects the %s recipe to be a git recipe' % testrecipe)
  325. result = runCmd('git status . --porcelain', cwd=os.path.dirname(recipefile))
  326. self.assertEqual(result.output.strip(), "", '%s recipe is not clean' % testrecipe)
  327. # First, modify a recipe
  328. tempdir = tempfile.mkdtemp(prefix='devtoolqa')
  329. self.track_for_cleanup(tempdir)
  330. self.track_for_cleanup(workspacedir)
  331. self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
  332. # (don't bother with cleaning the recipe on teardown, we won't be building it)
  333. result = runCmd('devtool modify %s -x %s' % (testrecipe, tempdir))
  334. # Check git repo
  335. self.assertTrue(os.path.isdir(os.path.join(tempdir, '.git')), 'git repository for external source tree not found')
  336. result = runCmd('git status --porcelain', cwd=tempdir)
  337. self.assertEqual(result.output.strip(), "", 'Created git repo is not clean')
  338. result = runCmd('git symbolic-ref HEAD', cwd=tempdir)
  339. self.assertEqual(result.output.strip(), "refs/heads/devtool", 'Wrong branch in git repo')
  340. # Add a couple of commits
  341. # FIXME: this only tests adding, need to also test update and remove
  342. result = runCmd('echo "# Additional line" >> Makefile', cwd=tempdir)
  343. result = runCmd('git commit -a -m "Change the Makefile"', cwd=tempdir)
  344. result = runCmd('echo "A new file" > devtool-new-file', cwd=tempdir)
  345. result = runCmd('git add devtool-new-file', cwd=tempdir)
  346. result = runCmd('git commit -m "Add a new file"', cwd=tempdir)
  347. self.add_command_to_tearDown('cd %s; git checkout %s %s' % (os.path.dirname(recipefile), testrecipe, os.path.basename(recipefile)))
  348. result = runCmd('devtool update-recipe %s' % testrecipe)
  349. result = runCmd('git status . --porcelain', cwd=os.path.dirname(recipefile))
  350. self.assertNotEqual(result.output.strip(), "", '%s recipe should be modified' % testrecipe)
  351. status = result.output.splitlines()
  352. self.assertEqual(len(status), 3, 'Less/more files modified than expected. Entire status:\n%s' % result.output)
  353. for line in status:
  354. if line.endswith('add-exclusion-to-mkfs-jffs2-git-2.patch'):
  355. self.assertEqual(line[:3], ' D ', 'Unexpected status in line: %s' % line)
  356. elif line.endswith('fix-armv7-neon-alignment.patch'):
  357. self.assertEqual(line[:3], ' D ', 'Unexpected status in line: %s' % line)
  358. elif re.search('%s_[^_]*.bb$' % testrecipe, line):
  359. self.assertEqual(line[:3], ' M ', 'Unexpected status in line: %s' % line)
  360. else:
  361. raise AssertionError('Unexpected modified file in status: %s' % line)
  362. result = runCmd('git diff %s' % os.path.basename(recipefile), cwd=os.path.dirname(recipefile))
  363. addlines = ['SRCREV = ".*"', 'SRC_URI = "git://git.infradead.org/mtd-utils.git"']
  364. removelines = ['SRCREV = ".*"', 'SRC_URI = "git://git.infradead.org/mtd-utils.git \\\\', 'file://add-exclusion-to-mkfs-jffs2-git-2.patch \\\\', 'file://fix-armv7-neon-alignment.patch \\\\', '"']
  365. for line in result.output.splitlines():
  366. if line.startswith('+++') or line.startswith('---'):
  367. continue
  368. elif line.startswith('+'):
  369. matched = False
  370. for item in addlines:
  371. if re.match(item, line[1:].strip()):
  372. matched = True
  373. break
  374. self.assertTrue(matched, 'Unexpected diff add line: %s' % line)
  375. elif line.startswith('-'):
  376. matched = False
  377. for item in removelines:
  378. if re.match(item, line[1:].strip()):
  379. matched = True
  380. break
  381. self.assertTrue(matched, 'Unexpected diff remove line: %s' % line)
  382. def test_devtool_extract(self):
  383. # Check preconditions
  384. workspacedir = os.path.join(self.builddir, 'workspace')
  385. self.assertTrue(not os.path.exists(workspacedir), 'This test cannot be run with a workspace directory under the build directory')
  386. tempdir = tempfile.mkdtemp(prefix='devtoolqa')
  387. # Try devtool extract
  388. self.track_for_cleanup(tempdir)
  389. self.track_for_cleanup(workspacedir)
  390. self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
  391. result = runCmd('devtool extract remake %s' % tempdir)
  392. self.assertTrue(os.path.exists(os.path.join(tempdir, 'Makefile.am')), 'Extracted source could not be found')
  393. self.assertTrue(os.path.isdir(os.path.join(tempdir, '.git')), 'git repository for external source tree not found')
  394. def test_devtool_reset_all(self):
  395. # Check preconditions
  396. workspacedir = os.path.join(self.builddir, 'workspace')
  397. self.assertTrue(not os.path.exists(workspacedir), 'This test cannot be run with a workspace directory under the build directory')
  398. tempdir = tempfile.mkdtemp(prefix='devtoolqa')
  399. self.track_for_cleanup(tempdir)
  400. self.track_for_cleanup(workspacedir)
  401. self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
  402. testrecipe1 = 'mdadm'
  403. testrecipe2 = 'cronie'
  404. result = runCmd('devtool modify -x %s %s' % (testrecipe1, os.path.join(tempdir, testrecipe1)))
  405. result = runCmd('devtool modify -x %s %s' % (testrecipe2, os.path.join(tempdir, testrecipe2)))
  406. result = runCmd('devtool build %s' % testrecipe1)
  407. result = runCmd('devtool build %s' % testrecipe2)
  408. stampprefix1 = get_bb_var('STAMP', testrecipe1)
  409. self.assertTrue(stampprefix1, 'Unable to get STAMP value for recipe %s' % testrecipe1)
  410. stampprefix2 = get_bb_var('STAMP', testrecipe2)
  411. self.assertTrue(stampprefix2, 'Unable to get STAMP value for recipe %s' % testrecipe2)
  412. result = runCmd('devtool reset -a')
  413. self.assertIn(testrecipe1, result.output)
  414. self.assertIn(testrecipe2, result.output)
  415. result = runCmd('devtool status')
  416. self.assertNotIn(testrecipe1, result.output)
  417. self.assertNotIn(testrecipe2, result.output)
  418. matches1 = glob.glob(stampprefix1 + '*')
  419. self.assertFalse(matches1, 'Stamp files exist for recipe %s that should have been cleaned' % testrecipe1)
  420. matches2 = glob.glob(stampprefix2 + '*')
  421. self.assertFalse(matches2, 'Stamp files exist for recipe %s that should have been cleaned' % testrecipe2)
  422. def test_devtool_deploy_target(self):
  423. # NOTE: Whilst this test would seemingly be better placed as a runtime test,
  424. # unfortunately the runtime tests run under bitbake and you can't run
  425. # devtool within bitbake (since devtool needs to run bitbake itself).
  426. # Additionally we are testing build-time functionality as well, so
  427. # really this has to be done as an oe-selftest test.
  428. #
  429. # Check preconditions
  430. machine = get_bb_var('MACHINE')
  431. if not machine.startswith('qemu'):
  432. self.skipTest('This test only works with qemu machines')
  433. if not os.path.exists('/etc/runqemu-nosudo'):
  434. self.skipTest('You must set up tap devices with scripts/runqemu-gen-tapdevs before running this test')
  435. workspacedir = os.path.join(self.builddir, 'workspace')
  436. self.assertTrue(not os.path.exists(workspacedir), 'This test cannot be run with a workspace directory under the build directory')
  437. import pexpect
  438. # Definitions
  439. testrecipe = 'mdadm'
  440. testfile = '/sbin/mdadm'
  441. testimage = 'oe-selftest-image'
  442. testhost = '192.168.7.2'
  443. testcommand = '/sbin/mdadm --help'
  444. # Build an image to run
  445. bitbake("%s qemu-native qemu-helper-native" % testimage)
  446. deploy_dir_image = get_bb_var('DEPLOY_DIR_IMAGE')
  447. self.add_command_to_tearDown('bitbake -c clean %s' % testimage)
  448. self.add_command_to_tearDown('rm -f %s/%s*' % (deploy_dir_image, testimage))
  449. # Clean recipe so the first deploy will fail
  450. bitbake("%s -c clean" % testrecipe)
  451. # Try devtool modify
  452. tempdir = tempfile.mkdtemp(prefix='devtoolqa')
  453. self.track_for_cleanup(tempdir)
  454. self.track_for_cleanup(workspacedir)
  455. self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
  456. self.add_command_to_tearDown('bitbake -c clean %s' % testrecipe)
  457. result = runCmd('devtool modify %s -x %s' % (testrecipe, tempdir))
  458. # Test that deploy-target at this point fails (properly)
  459. result = runCmd('devtool deploy-target -n %s root@%s' % (testrecipe, testhost), ignore_status=True)
  460. self.assertNotEqual(result.output, 0, 'devtool deploy-target should have failed, output: %s' % result.output)
  461. self.assertNotIn(result.output, 'Traceback', 'devtool deploy-target should have failed with a proper error not a traceback, output: %s' % result.output)
  462. result = runCmd('devtool build %s' % testrecipe)
  463. # First try a dry-run of deploy-target
  464. result = runCmd('devtool deploy-target -n %s root@%s' % (testrecipe, testhost))
  465. self.assertIn(' %s' % testfile, result.output)
  466. # Boot the image
  467. console = pexpect.spawn('runqemu %s %s qemuparams="-snapshot" nographic' % (machine, testimage))
  468. console.expect("login:", timeout=120)
  469. # Now really test deploy-target
  470. result = runCmd('devtool deploy-target -c %s root@%s' % (testrecipe, testhost))
  471. result = runCmd('ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no root@%s %s' % (testhost, testcommand))
  472. # Test undeploy-target
  473. result = runCmd('devtool undeploy-target -c %s root@%s' % (testrecipe, testhost))
  474. result = runCmd('ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no root@%s %s' % (testhost, testcommand), ignore_status=True)
  475. self.assertNotEqual(result, 0, 'undeploy-target did not remove command as it should have')
  476. console.close()