1#
2# Copyright (c) 2015, Intel Corporation.
3#
4# SPDX-License-Identifier: GPL-2.0-only
5#
6# AUTHORS
7# Ed Bartosh <ed.bartosh@linux.intel.com>
8
9"""Test cases for wic."""
10
11import os
12import sys
13import unittest
14import hashlib
15
16from glob import glob
17from shutil import rmtree, copy
18from tempfile import NamedTemporaryFile
19
20from oeqa.selftest.case import OESelftestTestCase
21from oeqa.core.decorator import OETestTag
22from oeqa.core.decorator.data import skipIfNotArch
23from oeqa.utils.commands import runCmd, bitbake, get_bb_var, get_bb_vars, runqemu
24
25
26def extract_files(debugfs_output):
27    """
28    extract file names from the output of debugfs -R 'ls -p',
29    which looks like this:
30
31     /2/040755/0/0/.//\n
32     /2/040755/0/0/..//\n
33     /11/040700/0/0/lost+found^M//\n
34     /12/040755/1002/1002/run//\n
35     /13/040755/1002/1002/sys//\n
36     /14/040755/1002/1002/bin//\n
37     /80/040755/1002/1002/var//\n
38     /92/040755/1002/1002/tmp//\n
39    """
40    # NOTE the occasional ^M in file names
41    return [line.split('/')[5].strip() for line in \
42            debugfs_output.strip().split('/\n')]
43
44def files_own_by_root(debugfs_output):
45    for line in debugfs_output.strip().split('/\n'):
46        if line.split('/')[3:5] != ['0', '0']:
47            print(debugfs_output)
48            return False
49    return True
50
51class WicTestCase(OESelftestTestCase):
52    """Wic test class."""
53
54    image_is_ready = False
55    wicenv_cache = {}
56
57    def setUpLocal(self):
58        """This code is executed before each test method."""
59        self.resultdir = os.path.join(self.builddir, "wic-tmp")
60        super(WicTestCase, self).setUpLocal()
61
62        # Do this here instead of in setUpClass as the base setUp does some
63        # clean up which can result in the native tools built earlier in
64        # setUpClass being unavailable.
65        if not WicTestCase.image_is_ready:
66            if self.td['USE_NLS'] != 'yes':
67                self.skipTest('wic-tools needs USE_NLS=yes')
68
69            bitbake('wic-tools core-image-minimal core-image-minimal-mtdutils')
70            WicTestCase.image_is_ready = True
71        rmtree(self.resultdir, ignore_errors=True)
72
73    def tearDownLocal(self):
74        """Remove resultdir as it may contain images."""
75        rmtree(self.resultdir, ignore_errors=True)
76        super(WicTestCase, self).tearDownLocal()
77
78    def _get_image_env_path(self, image):
79        """Generate and obtain the path to <image>.env"""
80        if image not in WicTestCase.wicenv_cache:
81            bitbake('%s -c do_rootfs_wicenv' % image)
82            stdir = get_bb_var('STAGING_DIR', image)
83            machine = self.td["MACHINE"]
84            WicTestCase.wicenv_cache[image] = os.path.join(stdir, machine, 'imgdata')
85        return WicTestCase.wicenv_cache[image]
86
87class CLITests(OESelftestTestCase):
88    def test_version(self):
89        """Test wic --version"""
90        runCmd('wic --version')
91
92    def test_help(self):
93        """Test wic --help and wic -h"""
94        runCmd('wic --help')
95        runCmd('wic -h')
96
97    def test_createhelp(self):
98        """Test wic create --help"""
99        runCmd('wic create --help')
100
101    def test_listhelp(self):
102        """Test wic list --help"""
103        runCmd('wic list --help')
104
105    def test_help_create(self):
106        """Test wic help create"""
107        runCmd('wic help create')
108
109    def test_help_list(self):
110        """Test wic help list"""
111        runCmd('wic help list')
112
113    def test_help_overview(self):
114        """Test wic help overview"""
115        runCmd('wic help overview')
116
117    def test_help_plugins(self):
118        """Test wic help plugins"""
119        runCmd('wic help plugins')
120
121    def test_help_kickstart(self):
122        """Test wic help kickstart"""
123        runCmd('wic help kickstart')
124
125    def test_list_images(self):
126        """Test wic list images"""
127        runCmd('wic list images')
128
129    def test_list_source_plugins(self):
130        """Test wic list source-plugins"""
131        runCmd('wic list source-plugins')
132
133    def test_listed_images_help(self):
134        """Test wic listed images help"""
135        output = runCmd('wic list images').output
136        imagelist = [line.split()[0] for line in output.splitlines()]
137        for image in imagelist:
138            runCmd('wic list %s help' % image)
139
140    def test_unsupported_subcommand(self):
141        """Test unsupported subcommand"""
142        self.assertNotEqual(0, runCmd('wic unsupported', ignore_status=True).status)
143
144    def test_no_command(self):
145        """Test wic without command"""
146        self.assertEqual(1, runCmd('wic', ignore_status=True).status)
147
148class Wic(WicTestCase):
149    def test_build_image_name(self):
150        """Test wic create wictestdisk --image-name=core-image-minimal"""
151        cmd = "wic create wictestdisk --image-name=core-image-minimal -o %s" % self.resultdir
152        runCmd(cmd)
153        self.assertEqual(1, len(glob(os.path.join (self.resultdir, "wictestdisk-*.direct"))))
154
155    @skipIfNotArch(['i586', 'i686', 'x86_64'])
156    def test_gpt_image(self):
157        """Test creation of core-image-minimal with gpt table and UUID boot"""
158        cmd = "wic create directdisk-gpt --image-name core-image-minimal -o %s" % self.resultdir
159        runCmd(cmd)
160        self.assertEqual(1, len(glob(os.path.join(self.resultdir, "directdisk-*.direct"))))
161
162    @skipIfNotArch(['i586', 'i686', 'x86_64'])
163    def test_iso_image(self):
164        """Test creation of hybrid iso image with legacy and EFI boot"""
165        config = 'INITRAMFS_IMAGE = "core-image-minimal-initramfs"\n'\
166                 'MACHINE_FEATURES:append = " efi"\n'\
167                 'DEPENDS:pn-core-image-minimal += "syslinux"\n'
168        self.append_config(config)
169        bitbake('core-image-minimal core-image-minimal-initramfs')
170        self.remove_config(config)
171        cmd = "wic create mkhybridiso --image-name core-image-minimal -o %s" % self.resultdir
172        runCmd(cmd)
173        self.assertEqual(1, len(glob(os.path.join(self.resultdir, "HYBRID_ISO_IMG-*.direct"))))
174        self.assertEqual(1, len(glob(os.path.join (self.resultdir, "HYBRID_ISO_IMG-*.iso"))))
175
176    @skipIfNotArch(['i586', 'i686', 'x86_64'])
177    def test_qemux86_directdisk(self):
178        """Test creation of qemux-86-directdisk image"""
179        cmd = "wic create qemux86-directdisk -e core-image-minimal -o %s" % self.resultdir
180        runCmd(cmd)
181        self.assertEqual(1, len(glob(os.path.join(self.resultdir, "qemux86-directdisk-*direct"))))
182
183    @skipIfNotArch(['i586', 'i686', 'x86_64', 'aarch64'])
184    def test_mkefidisk(self):
185        """Test creation of mkefidisk image"""
186        cmd = "wic create mkefidisk -e core-image-minimal -o %s" % self.resultdir
187        runCmd(cmd)
188        self.assertEqual(1, len(glob(os.path.join(self.resultdir, "mkefidisk-*direct"))))
189
190    @skipIfNotArch(['i586', 'i686', 'x86_64'])
191    def test_bootloader_config(self):
192        """Test creation of directdisk-bootloader-config image"""
193        config = 'DEPENDS:pn-core-image-minimal += "syslinux"\n'
194        self.append_config(config)
195        bitbake('core-image-minimal')
196        self.remove_config(config)
197        cmd = "wic create directdisk-bootloader-config -e core-image-minimal -o %s" % self.resultdir
198        runCmd(cmd)
199        self.assertEqual(1, len(glob(os.path.join(self.resultdir, "directdisk-bootloader-config-*direct"))))
200
201    @skipIfNotArch(['i586', 'i686', 'x86_64', 'aarch64'])
202    def test_systemd_bootdisk(self):
203        """Test creation of systemd-bootdisk image"""
204        config = 'MACHINE_FEATURES:append = " efi"\n'
205        self.append_config(config)
206        bitbake('core-image-minimal')
207        self.remove_config(config)
208        cmd = "wic create systemd-bootdisk -e core-image-minimal -o %s" % self.resultdir
209        runCmd(cmd)
210        self.assertEqual(1, len(glob(os.path.join(self.resultdir, "systemd-bootdisk-*direct"))))
211
212    def test_efi_bootpart(self):
213        """Test creation of efi-bootpart image"""
214        cmd = "wic create mkefidisk -e core-image-minimal -o %s" % self.resultdir
215        kimgtype = get_bb_var('KERNEL_IMAGETYPE', 'core-image-minimal')
216        self.append_config('IMAGE_EFI_BOOT_FILES = "%s;kernel"\n' % kimgtype)
217        runCmd(cmd)
218        sysroot = get_bb_var('RECIPE_SYSROOT_NATIVE', 'wic-tools')
219        images = glob(os.path.join(self.resultdir, "mkefidisk-*.direct"))
220        result = runCmd("wic ls %s:1/ -n %s" % (images[0], sysroot))
221        self.assertIn("kernel",result.output)
222
223    def test_sdimage_bootpart(self):
224        """Test creation of sdimage-bootpart image"""
225        cmd = "wic create sdimage-bootpart -e core-image-minimal -o %s" % self.resultdir
226        kimgtype = get_bb_var('KERNEL_IMAGETYPE', 'core-image-minimal')
227        self.write_config('IMAGE_BOOT_FILES = "%s"\n' % kimgtype)
228        runCmd(cmd)
229        self.assertEqual(1, len(glob(os.path.join(self.resultdir, "sdimage-bootpart-*direct"))))
230
231    # TODO this doesn't have to be x86-specific
232    @skipIfNotArch(['i586', 'i686', 'x86_64'])
233    def test_default_output_dir(self):
234        """Test default output location"""
235        for fname in glob("directdisk-*.direct"):
236            os.remove(fname)
237        config = 'DEPENDS:pn-core-image-minimal += "syslinux"\n'
238        self.append_config(config)
239        bitbake('core-image-minimal')
240        self.remove_config(config)
241        cmd = "wic create directdisk -e core-image-minimal"
242        runCmd(cmd)
243        self.assertEqual(1, len(glob("directdisk-*.direct")))
244
245    @skipIfNotArch(['i586', 'i686', 'x86_64'])
246    def test_build_artifacts(self):
247        """Test wic create directdisk providing all artifacts."""
248        bb_vars = get_bb_vars(['STAGING_DATADIR', 'RECIPE_SYSROOT_NATIVE'],
249                              'wic-tools')
250        bb_vars.update(get_bb_vars(['DEPLOY_DIR_IMAGE', 'IMAGE_ROOTFS'],
251                                   'core-image-minimal'))
252        bbvars = {key.lower(): value for key, value in bb_vars.items()}
253        bbvars['resultdir'] = self.resultdir
254        runCmd("wic create directdisk "
255                        "-b %(staging_datadir)s "
256                        "-k %(deploy_dir_image)s "
257                        "-n %(recipe_sysroot_native)s "
258                        "-r %(image_rootfs)s "
259                        "-o %(resultdir)s" % bbvars)
260        self.assertEqual(1, len(glob(os.path.join(self.resultdir, "directdisk-*.direct"))))
261
262    def test_compress_gzip(self):
263        """Test compressing an image with gzip"""
264        runCmd("wic create wictestdisk "
265                                   "--image-name core-image-minimal "
266                                   "-c gzip -o %s" % self.resultdir)
267        self.assertEqual(1, len(glob(os.path.join(self.resultdir, "wictestdisk-*.direct.gz"))))
268
269    def test_compress_bzip2(self):
270        """Test compressing an image with bzip2"""
271        runCmd("wic create wictestdisk "
272                                   "--image-name=core-image-minimal "
273                                   "-c bzip2 -o %s" % self.resultdir)
274        self.assertEqual(1, len(glob(os.path.join(self.resultdir, "wictestdisk-*.direct.bz2"))))
275
276    def test_compress_xz(self):
277        """Test compressing an image with xz"""
278        runCmd("wic create wictestdisk "
279                                   "--image-name=core-image-minimal "
280                                   "--compress-with=xz -o %s" % self.resultdir)
281        self.assertEqual(1, len(glob(os.path.join(self.resultdir, "wictestdisk-*.direct.xz"))))
282
283    def test_wrong_compressor(self):
284        """Test how wic breaks if wrong compressor is provided"""
285        self.assertEqual(2, runCmd("wic create wictestdisk "
286                                   "--image-name=core-image-minimal "
287                                   "-c wrong -o %s" % self.resultdir,
288                                   ignore_status=True).status)
289
290    def test_debug_short(self):
291        """Test -D option"""
292        runCmd("wic create wictestdisk "
293                                   "--image-name=core-image-minimal "
294                                   "-D -o %s" % self.resultdir)
295        self.assertEqual(1, len(glob(os.path.join(self.resultdir, "wictestdisk-*.direct"))))
296        self.assertEqual(1, len(glob(os.path.join(self.resultdir, "tmp.wic*"))))
297
298    def test_debug_long(self):
299        """Test --debug option"""
300        runCmd("wic create wictestdisk "
301                                   "--image-name=core-image-minimal "
302                                   "--debug -o %s" % self.resultdir)
303        self.assertEqual(1, len(glob(os.path.join(self.resultdir, "wictestdisk-*.direct"))))
304        self.assertEqual(1, len(glob(os.path.join(self.resultdir, "tmp.wic*"))))
305
306    def test_skip_build_check_short(self):
307        """Test -s option"""
308        runCmd("wic create wictestdisk "
309                                   "--image-name=core-image-minimal "
310                                   "-s -o %s" % self.resultdir)
311        self.assertEqual(1, len(glob(os.path.join(self.resultdir, "wictestdisk-*.direct"))))
312
313    def test_skip_build_check_long(self):
314        """Test --skip-build-check option"""
315        runCmd("wic create wictestdisk "
316                                   "--image-name=core-image-minimal "
317                                   "--skip-build-check "
318                                   "--outdir %s" % self.resultdir)
319        self.assertEqual(1, len(glob(os.path.join(self.resultdir, "wictestdisk-*.direct"))))
320
321    def test_build_rootfs_short(self):
322        """Test -f option"""
323        runCmd("wic create wictestdisk "
324                                   "--image-name=core-image-minimal "
325                                   "-f -o %s" % self.resultdir)
326        self.assertEqual(1, len(glob(os.path.join(self.resultdir, "wictestdisk-*.direct"))))
327
328    def test_build_rootfs_long(self):
329        """Test --build-rootfs option"""
330        runCmd("wic create wictestdisk "
331                                   "--image-name=core-image-minimal "
332                                   "--build-rootfs "
333                                   "--outdir %s" % self.resultdir)
334        self.assertEqual(1, len(glob(os.path.join(self.resultdir, "wictestdisk-*.direct"))))
335
336    # TODO this doesn't have to be x86-specific
337    @skipIfNotArch(['i586', 'i686', 'x86_64'])
338    def test_rootfs_indirect_recipes(self):
339        """Test usage of rootfs plugin with rootfs recipes"""
340        runCmd("wic create directdisk-multi-rootfs "
341                        "--image-name=core-image-minimal "
342                        "--rootfs rootfs1=core-image-minimal "
343                        "--rootfs rootfs2=core-image-minimal "
344                        "--outdir %s" % self.resultdir)
345        self.assertEqual(1, len(glob(os.path.join(self.resultdir, "directdisk-multi-rootfs*.direct"))))
346
347    # TODO this doesn't have to be x86-specific
348    @skipIfNotArch(['i586', 'i686', 'x86_64'])
349    def test_rootfs_artifacts(self):
350        """Test usage of rootfs plugin with rootfs paths"""
351        bb_vars = get_bb_vars(['STAGING_DATADIR', 'RECIPE_SYSROOT_NATIVE'],
352                              'wic-tools')
353        bb_vars.update(get_bb_vars(['DEPLOY_DIR_IMAGE', 'IMAGE_ROOTFS'],
354                                   'core-image-minimal'))
355        bbvars = {key.lower(): value for key, value in bb_vars.items()}
356        bbvars['wks'] = "directdisk-multi-rootfs"
357        bbvars['resultdir'] = self.resultdir
358        runCmd("wic create %(wks)s "
359                        "--bootimg-dir=%(staging_datadir)s "
360                        "--kernel-dir=%(deploy_dir_image)s "
361                        "--native-sysroot=%(recipe_sysroot_native)s "
362                        "--rootfs-dir rootfs1=%(image_rootfs)s "
363                        "--rootfs-dir rootfs2=%(image_rootfs)s "
364                        "--outdir %(resultdir)s" % bbvars)
365        self.assertEqual(1, len(glob(os.path.join(self.resultdir, "%(wks)s-*.direct" % bbvars))))
366
367    def test_exclude_path(self):
368        """Test --exclude-path wks option."""
369
370        oldpath = os.environ['PATH']
371        os.environ['PATH'] = get_bb_var("PATH", "wic-tools")
372
373        try:
374            wks_file = 'temp.wks'
375            with open(wks_file, 'w') as wks:
376                rootfs_dir = get_bb_var('IMAGE_ROOTFS', 'core-image-minimal')
377                wks.write("""
378part / --source rootfs --ondisk mmcblk0 --fstype=ext4 --exclude-path usr
379part /usr --source rootfs --ondisk mmcblk0 --fstype=ext4 --rootfs-dir %s/usr
380part /etc --source rootfs --ondisk mmcblk0 --fstype=ext4 --exclude-path bin/ --rootfs-dir %s/usr"""
381                          % (rootfs_dir, rootfs_dir))
382            runCmd("wic create %s -e core-image-minimal -o %s" \
383                                       % (wks_file, self.resultdir))
384
385            os.remove(wks_file)
386            wicout = glob(os.path.join(self.resultdir, "%s-*direct" % 'temp'))
387            self.assertEqual(1, len(wicout))
388
389            wicimg = wicout[0]
390
391            # verify partition size with wic
392            res = runCmd("parted -m %s unit b p 2>/dev/null" % wicimg)
393
394            # parse parted output which looks like this:
395            # BYT;\n
396            # /var/tmp/wic/build/tmpfwvjjkf_-201611101222-hda.direct:200MiB:file:512:512:msdos::;\n
397            # 1:0.00MiB:200MiB:200MiB:ext4::;\n
398            partlns = res.output.splitlines()[2:]
399
400            self.assertEqual(3, len(partlns))
401
402            for part in [1, 2, 3]:
403                part_file = os.path.join(self.resultdir, "selftest_img.part%d" % part)
404                partln = partlns[part-1].split(":")
405                self.assertEqual(7, len(partln))
406                start = int(partln[1].rstrip("B")) / 512
407                length = int(partln[3].rstrip("B")) / 512
408                runCmd("dd if=%s of=%s skip=%d count=%d" %
409                                           (wicimg, part_file, start, length))
410
411            # Test partition 1, should contain the normal root directories, except
412            # /usr.
413            res = runCmd("debugfs -R 'ls -p' %s 2>/dev/null" % \
414                             os.path.join(self.resultdir, "selftest_img.part1"))
415            files = extract_files(res.output)
416            self.assertIn("etc", files)
417            self.assertNotIn("usr", files)
418
419            # Partition 2, should contain common directories for /usr, not root
420            # directories.
421            res = runCmd("debugfs -R 'ls -p' %s 2>/dev/null" % \
422                             os.path.join(self.resultdir, "selftest_img.part2"))
423            files = extract_files(res.output)
424            self.assertNotIn("etc", files)
425            self.assertNotIn("usr", files)
426            self.assertIn("share", files)
427
428            # Partition 3, should contain the same as partition 2, including the bin
429            # directory, but not the files inside it.
430            res = runCmd("debugfs -R 'ls -p' %s 2>/dev/null" % \
431                             os.path.join(self.resultdir, "selftest_img.part3"))
432            files = extract_files(res.output)
433            self.assertNotIn("etc", files)
434            self.assertNotIn("usr", files)
435            self.assertIn("share", files)
436            self.assertIn("bin", files)
437            res = runCmd("debugfs -R 'ls -p bin' %s 2>/dev/null" % \
438                             os.path.join(self.resultdir, "selftest_img.part3"))
439            files = extract_files(res.output)
440            self.assertIn(".", files)
441            self.assertIn("..", files)
442            self.assertEqual(2, len(files))
443
444            for part in [1, 2, 3]:
445                part_file = os.path.join(self.resultdir, "selftest_img.part%d" % part)
446                os.remove(part_file)
447
448        finally:
449            os.environ['PATH'] = oldpath
450
451    def test_include_path(self):
452        """Test --include-path wks option."""
453
454        oldpath = os.environ['PATH']
455        os.environ['PATH'] = get_bb_var("PATH", "wic-tools")
456
457        try:
458            include_path = os.path.join(self.resultdir, 'test-include')
459            os.makedirs(include_path)
460            with open(os.path.join(include_path, 'test-file'), 'w') as t:
461                t.write("test\n")
462            wks_file = os.path.join(include_path, 'temp.wks')
463            with open(wks_file, 'w') as wks:
464                rootfs_dir = get_bb_var('IMAGE_ROOTFS', 'core-image-minimal')
465                wks.write("""
466part /part1 --source rootfs --ondisk mmcblk0 --fstype=ext4
467part /part2 --source rootfs --ondisk mmcblk0 --fstype=ext4 --include-path %s"""
468                          % (include_path))
469            runCmd("wic create %s -e core-image-minimal -o %s" \
470                                       % (wks_file, self.resultdir))
471
472            part1 = glob(os.path.join(self.resultdir, 'temp-*.direct.p1'))[0]
473            part2 = glob(os.path.join(self.resultdir, 'temp-*.direct.p2'))[0]
474
475            # Test partition 1, should not contain 'test-file'
476            res = runCmd("debugfs -R 'ls -p' %s 2>/dev/null" % (part1))
477            files = extract_files(res.output)
478            self.assertNotIn('test-file', files)
479            self.assertEqual(True, files_own_by_root(res.output))
480
481            # Test partition 2, should contain 'test-file'
482            res = runCmd("debugfs -R 'ls -p' %s 2>/dev/null" % (part2))
483            files = extract_files(res.output)
484            self.assertIn('test-file', files)
485            self.assertEqual(True, files_own_by_root(res.output))
486
487        finally:
488            os.environ['PATH'] = oldpath
489
490    def test_include_path_embeded(self):
491        """Test --include-path wks option."""
492
493        oldpath = os.environ['PATH']
494        os.environ['PATH'] = get_bb_var("PATH", "wic-tools")
495
496        try:
497            include_path = os.path.join(self.resultdir, 'test-include')
498            os.makedirs(include_path)
499            with open(os.path.join(include_path, 'test-file'), 'w') as t:
500                t.write("test\n")
501            wks_file = os.path.join(include_path, 'temp.wks')
502            with open(wks_file, 'w') as wks:
503                wks.write("""
504part / --source rootfs  --fstype=ext4 --include-path %s --include-path core-image-minimal-mtdutils export/"""
505                          % (include_path))
506            runCmd("wic create %s -e core-image-minimal -o %s" \
507                                       % (wks_file, self.resultdir))
508
509            part1 = glob(os.path.join(self.resultdir, 'temp-*.direct.p1'))[0]
510
511            res = runCmd("debugfs -R 'ls -p' %s 2>/dev/null" % (part1))
512            files = extract_files(res.output)
513            self.assertIn('test-file', files)
514            self.assertEqual(True, files_own_by_root(res.output))
515
516            res = runCmd("debugfs -R 'ls -p /export/etc/' %s 2>/dev/null" % (part1))
517            files = extract_files(res.output)
518            self.assertIn('passwd', files)
519            self.assertEqual(True, files_own_by_root(res.output))
520
521        finally:
522            os.environ['PATH'] = oldpath
523
524    def test_include_path_errors(self):
525        """Test --include-path wks option error handling."""
526        wks_file = 'temp.wks'
527
528        # Absolute argument.
529        with open(wks_file, 'w') as wks:
530            wks.write("part / --source rootfs --fstype=ext4 --include-path core-image-minimal-mtdutils /export")
531        self.assertNotEqual(0, runCmd("wic create %s -e core-image-minimal -o %s" \
532                                      % (wks_file, self.resultdir), ignore_status=True).status)
533        os.remove(wks_file)
534
535        # Argument pointing to parent directory.
536        with open(wks_file, 'w') as wks:
537            wks.write("part / --source rootfs --fstype=ext4 --include-path core-image-minimal-mtdutils ././..")
538        self.assertNotEqual(0, runCmd("wic create %s -e core-image-minimal -o %s" \
539                                      % (wks_file, self.resultdir), ignore_status=True).status)
540        os.remove(wks_file)
541
542        # 3 Argument pointing to parent directory.
543        with open(wks_file, 'w') as wks:
544            wks.write("part / --source rootfs --fstype=ext4 --include-path core-image-minimal-mtdutils export/ dummy")
545        self.assertNotEqual(0, runCmd("wic create %s -e core-image-minimal -o %s" \
546                                      % (wks_file, self.resultdir), ignore_status=True).status)
547        os.remove(wks_file)
548
549    def test_exclude_path_errors(self):
550        """Test --exclude-path wks option error handling."""
551        wks_file = 'temp.wks'
552
553        # Absolute argument.
554        with open(wks_file, 'w') as wks:
555            wks.write("part / --source rootfs --ondisk mmcblk0 --fstype=ext4 --exclude-path /usr")
556        self.assertNotEqual(0, runCmd("wic create %s -e core-image-minimal -o %s" \
557                                      % (wks_file, self.resultdir), ignore_status=True).status)
558        os.remove(wks_file)
559
560        # Argument pointing to parent directory.
561        with open(wks_file, 'w') as wks:
562            wks.write("part / --source rootfs --ondisk mmcblk0 --fstype=ext4 --exclude-path ././..")
563        self.assertNotEqual(0, runCmd("wic create %s -e core-image-minimal -o %s" \
564                                      % (wks_file, self.resultdir), ignore_status=True).status)
565        os.remove(wks_file)
566
567    def test_permissions(self):
568        """Test permissions are respected"""
569
570        # prepare wicenv and rootfs
571        bitbake('core-image-minimal core-image-minimal-mtdutils -c do_rootfs_wicenv')
572
573        oldpath = os.environ['PATH']
574        os.environ['PATH'] = get_bb_var("PATH", "wic-tools")
575
576        t_normal = """
577part / --source rootfs --fstype=ext4
578"""
579        t_exclude = """
580part / --source rootfs --fstype=ext4 --exclude-path=home
581"""
582        t_multi = """
583part / --source rootfs --ondisk sda --fstype=ext4
584part /export --source rootfs --rootfs=core-image-minimal-mtdutils --fstype=ext4
585"""
586        t_change = """
587part / --source rootfs --ondisk sda --fstype=ext4 --exclude-path=etc/   
588part /etc --source rootfs --fstype=ext4 --change-directory=etc
589"""
590        tests = [t_normal, t_exclude, t_multi, t_change]
591
592        try:
593            for test in tests:
594                include_path = os.path.join(self.resultdir, 'test-include')
595                os.makedirs(include_path)
596                wks_file = os.path.join(include_path, 'temp.wks')
597                with open(wks_file, 'w') as wks:
598                    wks.write(test)
599                runCmd("wic create %s -e core-image-minimal -o %s" \
600                                       % (wks_file, self.resultdir))
601
602                for part in glob(os.path.join(self.resultdir, 'temp-*.direct.p*')):
603                    res = runCmd("debugfs -R 'ls -p' %s 2>/dev/null" % (part))
604                    self.assertEqual(True, files_own_by_root(res.output))
605
606                config = 'IMAGE_FSTYPES += "wic"\nWKS_FILE = "%s"\n' % wks_file
607                self.append_config(config)
608                bitbake('core-image-minimal')
609                tmpdir = os.path.join(get_bb_var('WORKDIR', 'core-image-minimal'),'build-wic')
610
611                # check each partition for permission
612                for part in glob(os.path.join(tmpdir, 'temp-*.direct.p*')):
613                    res = runCmd("debugfs -R 'ls -p' %s 2>/dev/null" % (part))
614                    self.assertTrue(files_own_by_root(res.output)
615                        ,msg='Files permission incorrect using wks set "%s"' % test)
616
617                # clean config and result directory for next cases
618                self.remove_config(config)
619                rmtree(self.resultdir, ignore_errors=True)
620
621        finally:
622            os.environ['PATH'] = oldpath
623
624    def test_change_directory(self):
625        """Test --change-directory wks option."""
626
627        oldpath = os.environ['PATH']
628        os.environ['PATH'] = get_bb_var("PATH", "wic-tools")
629
630        try:
631            include_path = os.path.join(self.resultdir, 'test-include')
632            os.makedirs(include_path)
633            wks_file = os.path.join(include_path, 'temp.wks')
634            with open(wks_file, 'w') as wks:
635                wks.write("part /etc --source rootfs --fstype=ext4 --change-directory=etc")
636            runCmd("wic create %s -e core-image-minimal -o %s" \
637                                       % (wks_file, self.resultdir))
638
639            part1 = glob(os.path.join(self.resultdir, 'temp-*.direct.p1'))[0]
640
641            res = runCmd("debugfs -R 'ls -p' %s 2>/dev/null" % (part1))
642            files = extract_files(res.output)
643            self.assertIn('passwd', files)
644
645        finally:
646            os.environ['PATH'] = oldpath
647
648    def test_change_directory_errors(self):
649        """Test --change-directory wks option error handling."""
650        wks_file = 'temp.wks'
651
652        # Absolute argument.
653        with open(wks_file, 'w') as wks:
654            wks.write("part / --source rootfs --fstype=ext4 --change-directory /usr")
655        self.assertNotEqual(0, runCmd("wic create %s -e core-image-minimal -o %s" \
656                                      % (wks_file, self.resultdir), ignore_status=True).status)
657        os.remove(wks_file)
658
659        # Argument pointing to parent directory.
660        with open(wks_file, 'w') as wks:
661            wks.write("part / --source rootfs --fstype=ext4 --change-directory ././..")
662        self.assertNotEqual(0, runCmd("wic create %s -e core-image-minimal -o %s" \
663                                      % (wks_file, self.resultdir), ignore_status=True).status)
664        os.remove(wks_file)
665
666    def test_no_fstab_update(self):
667        """Test --no-fstab-update wks option."""
668
669        oldpath = os.environ['PATH']
670        os.environ['PATH'] = get_bb_var("PATH", "wic-tools")
671
672        # Get stock fstab from base-files recipe
673        bitbake('base-files -c do_install')
674        bf_fstab = os.path.join(get_bb_var('D', 'base-files'), 'etc', 'fstab')
675        self.assertEqual(True, os.path.exists(bf_fstab))
676        bf_fstab_md5sum = runCmd('md5sum %s 2>/dev/null' % bf_fstab).output.split(" ")[0]
677
678        try:
679            no_fstab_update_path = os.path.join(self.resultdir, 'test-no-fstab-update')
680            os.makedirs(no_fstab_update_path)
681            wks_file = os.path.join(no_fstab_update_path, 'temp.wks')
682            with open(wks_file, 'w') as wks:
683                wks.writelines(['part / --source rootfs --fstype=ext4 --label rootfs\n',
684                                'part /mnt/p2 --source rootfs --rootfs-dir=core-image-minimal ',
685                                '--fstype=ext4 --label p2 --no-fstab-update\n'])
686            runCmd("wic create %s -e core-image-minimal -o %s" \
687                                       % (wks_file, self.resultdir))
688
689            part_fstab_md5sum = []
690            for i in range(1, 3):
691                part = glob(os.path.join(self.resultdir, 'temp-*.direct.p') + str(i))[0]
692                part_fstab = runCmd("debugfs -R 'cat etc/fstab' %s 2>/dev/null" % (part))
693                part_fstab_md5sum.append(hashlib.md5((part_fstab.output + "\n\n").encode('utf-8')).hexdigest())
694
695            # '/etc/fstab' in partition 2 should contain the same stock fstab file
696            # as the one installed by the base-file recipe.
697            self.assertEqual(bf_fstab_md5sum, part_fstab_md5sum[1])
698
699            # '/etc/fstab' in partition 1 should contain an updated fstab file.
700            self.assertNotEqual(bf_fstab_md5sum, part_fstab_md5sum[0])
701
702        finally:
703            os.environ['PATH'] = oldpath
704
705    def test_no_fstab_update_errors(self):
706        """Test --no-fstab-update wks option error handling."""
707        wks_file = 'temp.wks'
708
709        # Absolute argument.
710        with open(wks_file, 'w') as wks:
711            wks.write("part / --source rootfs --fstype=ext4 --no-fstab-update /etc")
712        self.assertNotEqual(0, runCmd("wic create %s -e core-image-minimal -o %s" \
713                                      % (wks_file, self.resultdir), ignore_status=True).status)
714        os.remove(wks_file)
715
716        # Argument pointing to parent directory.
717        with open(wks_file, 'w') as wks:
718            wks.write("part / --source rootfs --fstype=ext4 --no-fstab-update ././..")
719        self.assertNotEqual(0, runCmd("wic create %s -e core-image-minimal -o %s" \
720                                      % (wks_file, self.resultdir), ignore_status=True).status)
721        os.remove(wks_file)
722
723    def test_extra_space(self):
724        """Test --extra-space wks option."""
725        extraspace = 1024**3
726        runCmd("wic create wictestdisk "
727                                   "--image-name core-image-minimal "
728                                   "--extra-space %i -o %s" % (extraspace ,self.resultdir))
729        wicout = glob(os.path.join(self.resultdir, "wictestdisk-*.direct"))
730        self.assertEqual(1, len(wicout))
731        size = os.path.getsize(wicout[0])
732        self.assertTrue(size > extraspace)
733
734class Wic2(WicTestCase):
735
736    def test_bmap_short(self):
737        """Test generation of .bmap file -m option"""
738        cmd = "wic create wictestdisk -e core-image-minimal -m -o %s" % self.resultdir
739        runCmd(cmd)
740        self.assertEqual(1, len(glob(os.path.join(self.resultdir, "wictestdisk-*direct"))))
741        self.assertEqual(1, len(glob(os.path.join(self.resultdir, "wictestdisk-*direct.bmap"))))
742
743    def test_bmap_long(self):
744        """Test generation of .bmap file --bmap option"""
745        cmd = "wic create wictestdisk -e core-image-minimal --bmap -o %s" % self.resultdir
746        runCmd(cmd)
747        self.assertEqual(1, len(glob(os.path.join(self.resultdir, "wictestdisk-*direct"))))
748        self.assertEqual(1, len(glob(os.path.join(self.resultdir, "wictestdisk-*direct.bmap"))))
749
750    def test_image_env(self):
751        """Test generation of <image>.env files."""
752        image = 'core-image-minimal'
753        imgdatadir = self._get_image_env_path(image)
754
755        bb_vars = get_bb_vars(['IMAGE_BASENAME', 'WICVARS'], image)
756        basename = bb_vars['IMAGE_BASENAME']
757        self.assertEqual(basename, image)
758        path = os.path.join(imgdatadir, basename) + '.env'
759        self.assertTrue(os.path.isfile(path))
760
761        wicvars = set(bb_vars['WICVARS'].split())
762        # filter out optional variables
763        wicvars = wicvars.difference(('DEPLOY_DIR_IMAGE', 'IMAGE_BOOT_FILES',
764                                      'INITRD', 'INITRD_LIVE', 'ISODIR','INITRAMFS_IMAGE',
765                                      'INITRAMFS_IMAGE_BUNDLE', 'INITRAMFS_LINK_NAME',
766                                      'APPEND', 'IMAGE_EFI_BOOT_FILES'))
767        with open(path) as envfile:
768            content = dict(line.split("=", 1) for line in envfile)
769            # test if variables used by wic present in the .env file
770            for var in wicvars:
771                self.assertTrue(var in content, "%s is not in .env file" % var)
772                self.assertTrue(content[var])
773
774    def test_image_vars_dir_short(self):
775        """Test image vars directory selection -v option"""
776        image = 'core-image-minimal'
777        imgenvdir = self._get_image_env_path(image)
778        native_sysroot = get_bb_var("RECIPE_SYSROOT_NATIVE", "wic-tools")
779
780        runCmd("wic create wictestdisk "
781                                   "--image-name=%s -v %s -n %s -o %s"
782                                   % (image, imgenvdir, native_sysroot,
783                                      self.resultdir))
784        self.assertEqual(1, len(glob(os.path.join(self.resultdir, "wictestdisk-*direct"))))
785
786    def test_image_vars_dir_long(self):
787        """Test image vars directory selection --vars option"""
788        image = 'core-image-minimal'
789        imgenvdir = self._get_image_env_path(image)
790        native_sysroot = get_bb_var("RECIPE_SYSROOT_NATIVE", "wic-tools")
791
792        runCmd("wic create wictestdisk "
793                                   "--image-name=%s "
794                                   "--vars %s "
795                                   "--native-sysroot %s "
796                                   "--outdir %s"
797                                   % (image, imgenvdir, native_sysroot,
798                                      self.resultdir))
799        self.assertEqual(1, len(glob(os.path.join(self.resultdir, "wictestdisk-*direct"))))
800
801    # TODO this test could also work on aarch64
802    @skipIfNotArch(['i586', 'i686', 'x86_64'])
803    def test_wic_image_type(self):
804        """Test building wic images by bitbake"""
805        config = 'IMAGE_FSTYPES += "wic"\nWKS_FILE = "wic-image-minimal"\n'\
806                 'MACHINE_FEATURES:append = " efi"\n'
807        self.append_config(config)
808        image = 'wic-image-minimal'
809        bitbake(image)
810        self.remove_config(config)
811
812        bb_vars = get_bb_vars(['DEPLOY_DIR_IMAGE', 'IMAGE_LINK_NAME'], image)
813        prefix = os.path.join(bb_vars['DEPLOY_DIR_IMAGE'], '%s.' % bb_vars['IMAGE_LINK_NAME'])
814
815        # check if we have result image and manifests symlinks
816        # pointing to existing files
817        for suffix in ('wic', 'manifest'):
818            path = prefix + suffix
819            self.assertTrue(os.path.islink(path))
820            self.assertTrue(os.path.isfile(os.path.realpath(path)))
821
822    # TODO this should work on aarch64
823    @skipIfNotArch(['i586', 'i686', 'x86_64'])
824    @OETestTag("runqemu")
825    def test_qemu(self):
826        """Test wic-image-minimal under qemu"""
827        config = 'IMAGE_FSTYPES += "wic"\nWKS_FILE = "wic-image-minimal"\n'\
828                 'MACHINE_FEATURES:append = " efi"\n'
829        self.append_config(config)
830        bitbake('wic-image-minimal')
831        self.remove_config(config)
832
833        with runqemu('wic-image-minimal', ssh=False, runqemuparams='nographic') as qemu:
834            cmd = "mount | grep '^/dev/' | cut -f1,3 -d ' ' | egrep -c -e '/dev/sda1 /boot' " \
835                  "-e '/dev/root /|/dev/sda2 /' -e '/dev/sda3 /media' -e '/dev/sda4 /mnt'"
836            status, output = qemu.run_serial(cmd)
837            self.assertEqual(1, status, 'Failed to run command "%s": %s' % (cmd, output))
838            self.assertEqual(output, '4')
839            cmd = "grep UUID=2c71ef06-a81d-4735-9d3a-379b69c6bdba /etc/fstab"
840            status, output = qemu.run_serial(cmd)
841            self.assertEqual(1, status, 'Failed to run command "%s": %s' % (cmd, output))
842            self.assertEqual(output, 'UUID=2c71ef06-a81d-4735-9d3a-379b69c6bdba\t/media\text4\tdefaults\t0\t0')
843
844    @skipIfNotArch(['i586', 'i686', 'x86_64'])
845    @OETestTag("runqemu")
846    def test_qemu_efi(self):
847        """Test core-image-minimal efi image under qemu"""
848        config = 'IMAGE_FSTYPES = "wic"\nWKS_FILE = "mkefidisk.wks"\n'
849        self.append_config(config)
850        bitbake('core-image-minimal ovmf')
851        self.remove_config(config)
852
853        with runqemu('core-image-minimal', ssh=False,
854                     runqemuparams='nographic ovmf', image_fstype='wic') as qemu:
855            cmd = "grep sda. /proc/partitions  |wc -l"
856            status, output = qemu.run_serial(cmd)
857            self.assertEqual(1, status, 'Failed to run command "%s": %s' % (cmd, output))
858            self.assertEqual(output, '3')
859
860    @staticmethod
861    def _make_fixed_size_wks(size):
862        """
863        Create a wks of an image with a single partition. Size of the partition is set
864        using --fixed-size flag. Returns a tuple: (path to wks file, wks image name)
865        """
866        with NamedTemporaryFile("w", suffix=".wks", delete=False) as tempf:
867            wkspath = tempf.name
868            tempf.write("part " \
869                     "--source rootfs --ondisk hda --align 4 --fixed-size %d "
870                     "--fstype=ext4\n" % size)
871
872        return wkspath
873
874    def _get_wic_partitions(self, wkspath, native_sysroot=None, ignore_status=False):
875        p = runCmd("wic create %s -e core-image-minimal -o %s" % (wkspath, self.resultdir),
876                   ignore_status=ignore_status)
877
878        if p.status:
879            return (p, None)
880
881        wksname = os.path.splitext(os.path.basename(wkspath))[0]
882
883        wicout = glob(os.path.join(self.resultdir, "%s-*direct" % wksname))
884
885        if not wicout:
886            return (p, None)
887
888        wicimg = wicout[0]
889
890        if not native_sysroot:
891            native_sysroot = get_bb_var("RECIPE_SYSROOT_NATIVE", "wic-tools")
892
893        # verify partition size with wic
894        res = runCmd("parted -m %s unit kib p 2>/dev/null" % wicimg,
895                     native_sysroot=native_sysroot)
896
897        # parse parted output which looks like this:
898        # BYT;\n
899        # /var/tmp/wic/build/tmpfwvjjkf_-201611101222-hda.direct:200MiB:file:512:512:msdos::;\n
900        # 1:0.00MiB:200MiB:200MiB:ext4::;\n
901        return (p, res.output.splitlines()[2:])
902
903    def test_fixed_size(self):
904        """
905        Test creation of a simple image with partition size controlled through
906        --fixed-size flag
907        """
908        wkspath = Wic2._make_fixed_size_wks(200)
909        _, partlns = self._get_wic_partitions(wkspath)
910        os.remove(wkspath)
911
912        self.assertEqual(partlns, [
913                        "1:4.00kiB:204804kiB:204800kiB:ext4::;",
914                        ])
915
916    def test_fixed_size_error(self):
917        """
918        Test creation of a simple image with partition size controlled through
919        --fixed-size flag. The size of partition is intentionally set to 1MiB
920        in order to trigger an error in wic.
921        """
922        wkspath = Wic2._make_fixed_size_wks(1)
923        p, _ = self._get_wic_partitions(wkspath, ignore_status=True)
924        os.remove(wkspath)
925
926        self.assertNotEqual(p.status, 0, "wic exited successfully when an error was expected:\n%s" % p.output)
927
928    def test_offset(self):
929        native_sysroot = get_bb_var("RECIPE_SYSROOT_NATIVE", "wic-tools")
930
931        with NamedTemporaryFile("w", suffix=".wks") as tempf:
932            # Test that partitions are placed at the correct offsets, default KB
933            tempf.write("bootloader --ptable gpt\n" \
934                        "part /    --source rootfs --ondisk hda --offset 32     --fixed-size 100M --fstype=ext4\n" \
935                        "part /bar                 --ondisk hda --offset 102432 --fixed-size 100M --fstype=ext4\n")
936            tempf.flush()
937
938            _, partlns = self._get_wic_partitions(tempf.name, native_sysroot)
939            self.assertEqual(partlns, [
940                "1:32.0kiB:102432kiB:102400kiB:ext4:primary:;",
941                "2:102432kiB:204832kiB:102400kiB:ext4:primary:;",
942                ])
943
944        with NamedTemporaryFile("w", suffix=".wks") as tempf:
945            # Test that partitions are placed at the correct offsets, same with explicit KB
946            tempf.write("bootloader --ptable gpt\n" \
947                        "part /    --source rootfs --ondisk hda --offset 32K     --fixed-size 100M --fstype=ext4\n" \
948                        "part /bar                 --ondisk hda --offset 102432K --fixed-size 100M --fstype=ext4\n")
949            tempf.flush()
950
951            _, partlns = self._get_wic_partitions(tempf.name, native_sysroot)
952            self.assertEqual(partlns, [
953                "1:32.0kiB:102432kiB:102400kiB:ext4:primary:;",
954                "2:102432kiB:204832kiB:102400kiB:ext4:primary:;",
955                ])
956
957        with NamedTemporaryFile("w", suffix=".wks") as tempf:
958            # Test that partitions are placed at the correct offsets using MB
959            tempf.write("bootloader --ptable gpt\n" \
960                        "part /    --source rootfs --ondisk hda --offset 32K  --fixed-size 100M --fstype=ext4\n" \
961                        "part /bar                 --ondisk hda --offset 101M --fixed-size 100M --fstype=ext4\n")
962            tempf.flush()
963
964            _, partlns = self._get_wic_partitions(tempf.name, native_sysroot)
965            self.assertEqual(partlns, [
966                "1:32.0kiB:102432kiB:102400kiB:ext4:primary:;",
967                "2:103424kiB:205824kiB:102400kiB:ext4:primary:;",
968                ])
969
970        with NamedTemporaryFile("w", suffix=".wks") as tempf:
971            # Test that partitions can be placed on a 512 byte sector boundary
972            tempf.write("bootloader --ptable gpt\n" \
973                        "part /    --source rootfs --ondisk hda --offset 65s --fixed-size 99M --fstype=ext4\n" \
974                        "part /bar                 --ondisk hda --offset 102432 --fixed-size 100M --fstype=ext4\n")
975            tempf.flush()
976
977            _, partlns = self._get_wic_partitions(tempf.name, native_sysroot)
978            self.assertEqual(partlns, [
979                "1:32.5kiB:101408kiB:101376kiB:ext4:primary:;",
980                "2:102432kiB:204832kiB:102400kiB:ext4:primary:;",
981                ])
982
983        with NamedTemporaryFile("w", suffix=".wks") as tempf:
984            # Test that a partition can be placed immediately after a MSDOS partition table
985            tempf.write("bootloader --ptable msdos\n" \
986                        "part /    --source rootfs --ondisk hda --offset 1s --fixed-size 100M --fstype=ext4\n")
987            tempf.flush()
988
989            _, partlns = self._get_wic_partitions(tempf.name, native_sysroot)
990            self.assertEqual(partlns, [
991                "1:0.50kiB:102400kiB:102400kiB:ext4::;",
992                ])
993
994        with NamedTemporaryFile("w", suffix=".wks") as tempf:
995            # Test that image creation fails if the partitions would overlap
996            tempf.write("bootloader --ptable gpt\n" \
997                        "part /    --source rootfs --ondisk hda --offset 32     --fixed-size 100M --fstype=ext4\n" \
998                        "part /bar                 --ondisk hda --offset 102431 --fixed-size 100M --fstype=ext4\n")
999            tempf.flush()
1000
1001            p, _ = self._get_wic_partitions(tempf.name, ignore_status=True)
1002            self.assertNotEqual(p.status, 0, "wic exited successfully when an error was expected:\n%s" % p.output)
1003
1004        with NamedTemporaryFile("w", suffix=".wks") as tempf:
1005            # Test that partitions are not allowed to overlap with the booloader
1006            tempf.write("bootloader --ptable gpt\n" \
1007                        "part /    --source rootfs --ondisk hda --offset 8 --fixed-size 100M --fstype=ext4\n")
1008            tempf.flush()
1009
1010            p, _ = self._get_wic_partitions(tempf.name, ignore_status=True)
1011            self.assertNotEqual(p.status, 0, "wic exited successfully when an error was expected:\n%s" % p.output)
1012
1013    def test_extra_space(self):
1014        native_sysroot = get_bb_var("RECIPE_SYSROOT_NATIVE", "wic-tools")
1015
1016        with NamedTemporaryFile("w", suffix=".wks") as tempf:
1017            tempf.write("bootloader --ptable gpt\n" \
1018                        "part /     --source rootfs --ondisk hda --extra-space 200M --fstype=ext4\n")
1019            tempf.flush()
1020
1021            _, partlns = self._get_wic_partitions(tempf.name, native_sysroot)
1022            self.assertEqual(len(partlns), 1)
1023            size = partlns[0].split(':')[3]
1024            self.assertRegex(size, r'^[0-9]+kiB$')
1025            size = int(size[:-3])
1026            self.assertGreaterEqual(size, 204800)
1027
1028    # TODO this test could also work on aarch64
1029    @skipIfNotArch(['i586', 'i686', 'x86_64'])
1030    @OETestTag("runqemu")
1031    def test_rawcopy_plugin_qemu(self):
1032        """Test rawcopy plugin in qemu"""
1033        # build ext4 and then use it for a wic image
1034        config = 'IMAGE_FSTYPES = "ext4"\n'
1035        self.append_config(config)
1036        bitbake('core-image-minimal')
1037        image_link_name = get_bb_var('IMAGE_LINK_NAME', 'core-image-minimal')
1038        self.remove_config(config)
1039
1040        config = 'IMAGE_FSTYPES = "wic"\n' \
1041                 'IMAGE_LINK_NAME_CORE_IMAGE_MINIMAL = "%s"\n'\
1042                 'WKS_FILE = "test_rawcopy_plugin.wks.in"\n'\
1043                 % image_link_name
1044        self.append_config(config)
1045        bitbake('core-image-minimal-mtdutils')
1046        self.remove_config(config)
1047
1048        with runqemu('core-image-minimal-mtdutils', ssh=False,
1049                     runqemuparams='nographic', image_fstype='wic') as qemu:
1050            cmd = "grep sda. /proc/partitions  |wc -l"
1051            status, output = qemu.run_serial(cmd)
1052            self.assertEqual(1, status, 'Failed to run command "%s": %s' % (cmd, output))
1053            self.assertEqual(output, '2')
1054
1055    def _rawcopy_plugin(self, fstype):
1056        """Test rawcopy plugin"""
1057        image = 'core-image-minimal'
1058        bb_vars = get_bb_vars(['DEPLOY_DIR_IMAGE', 'IMAGE_LINK_NAME'], image)
1059        params = ',unpack' if fstype.endswith('.gz') else ''
1060        with NamedTemporaryFile("w", suffix=".wks") as wks:
1061            wks.write('part / --source rawcopy --sourceparams="file=%s.%s%s"\n'\
1062                      % (bb_vars['IMAGE_LINK_NAME'], fstype, params))
1063            wks.flush()
1064            cmd = "wic create %s -e %s -o %s" % (wks.name, image, self.resultdir)
1065            runCmd(cmd)
1066            wksname = os.path.splitext(os.path.basename(wks.name))[0]
1067            out = glob(os.path.join(self.resultdir, "%s-*direct" % wksname))
1068            self.assertEqual(1, len(out))
1069
1070    def test_rawcopy_plugin(self):
1071        self._rawcopy_plugin('ext4')
1072
1073    def test_rawcopy_plugin_unpack(self):
1074        fstype = 'ext4.gz'
1075        config = 'IMAGE_FSTYPES = "%s"\n' % fstype
1076        self.append_config(config)
1077        self.assertEqual(0, bitbake('core-image-minimal').status)
1078        self.remove_config(config)
1079        self._rawcopy_plugin(fstype)
1080
1081    def test_empty_plugin(self):
1082        """Test empty plugin"""
1083        config = 'IMAGE_FSTYPES = "wic"\nWKS_FILE = "test_empty_plugin.wks"\n'
1084        self.append_config(config)
1085        image = 'core-image-minimal'
1086        bitbake(image)
1087        self.remove_config(config)
1088        bb_vars = get_bb_vars(['DEPLOY_DIR_IMAGE', 'IMAGE_LINK_NAME'], image)
1089        image_path = os.path.join(bb_vars['DEPLOY_DIR_IMAGE'], '%s.wic' % bb_vars['IMAGE_LINK_NAME'])
1090        self.assertTrue(os.path.exists(image_path))
1091
1092        sysroot = get_bb_var('RECIPE_SYSROOT_NATIVE', 'wic-tools')
1093
1094        # Fstype column from 'wic ls' should be empty for the second partition
1095        # as listed in test_empty_plugin.wks
1096        result = runCmd("wic ls %s -n %s | awk -F ' ' '{print $1 \" \" $5}' | grep '^2' | wc -w" % (image_path, sysroot))
1097        self.assertEqual('1', result.output)
1098
1099    @skipIfNotArch(['i586', 'i686', 'x86_64'])
1100    @OETestTag("runqemu")
1101    def test_biosplusefi_plugin_qemu(self):
1102        """Test biosplusefi plugin in qemu"""
1103        config = 'IMAGE_FSTYPES = "wic"\nWKS_FILE = "test_biosplusefi_plugin.wks"\nMACHINE_FEATURES:append = " efi"\n'
1104        self.append_config(config)
1105        bitbake('core-image-minimal')
1106        self.remove_config(config)
1107
1108        with runqemu('core-image-minimal', ssh=False,
1109                     runqemuparams='nographic', image_fstype='wic') as qemu:
1110            # Check that we have ONLY two /dev/sda* partitions (/boot and /)
1111            cmd = "grep sda. /proc/partitions | wc -l"
1112            status, output = qemu.run_serial(cmd)
1113            self.assertEqual(1, status, 'Failed to run command "%s": %s' % (cmd, output))
1114            self.assertEqual(output, '2')
1115            # Check that /dev/sda1 is /boot and that either /dev/root OR /dev/sda2 is /
1116            cmd = "mount | grep '^/dev/' | cut -f1,3 -d ' ' | egrep -c -e '/dev/sda1 /boot' -e '/dev/root /|/dev/sda2 /'"
1117            status, output = qemu.run_serial(cmd)
1118            self.assertEqual(1, status, 'Failed to run command "%s": %s' % (cmd, output))
1119            self.assertEqual(output, '2')
1120            # Check that /boot has EFI bootx64.efi (required for EFI)
1121            cmd = "ls /boot/EFI/BOOT/bootx64.efi | wc -l"
1122            status, output = qemu.run_serial(cmd)
1123            self.assertEqual(1, status, 'Failed to run command "%s": %s' % (cmd, output))
1124            self.assertEqual(output, '1')
1125            # Check that "BOOTABLE" flag is set on boot partition (required for PC-Bios)
1126            # Trailing "cat" seems to be required; otherwise run_serial() sends back echo of the input command
1127            cmd = "fdisk -l /dev/sda | grep /dev/sda1 | awk {print'$2'} | cat"
1128            status, output = qemu.run_serial(cmd)
1129            self.assertEqual(1, status, 'Failed to run command "%s": %s' % (cmd, output))
1130            self.assertEqual(output, '*')
1131
1132    @skipIfNotArch(['i586', 'i686', 'x86_64'])
1133    def test_biosplusefi_plugin(self):
1134        """Test biosplusefi plugin"""
1135        # Wic generation below may fail depending on the order of the unittests
1136        # This is because bootimg-pcbios (that bootimg-biosplusefi uses) generate its MBR inside STAGING_DATADIR directory
1137        #    which may or may not exists depending on what was built already
1138        # If an image hasn't been built yet, directory ${STAGING_DATADIR}/syslinux won't exists and _get_bootimg_dir()
1139        #   will raise with "Couldn't find correct bootimg_dir"
1140        # The easiest way to work-around this issue is to make sure we already built an image here, hence the bitbake call
1141        config = 'IMAGE_FSTYPES = "wic"\nWKS_FILE = "test_biosplusefi_plugin.wks"\nMACHINE_FEATURES:append = " efi"\n'
1142        self.append_config(config)
1143        bitbake('core-image-minimal')
1144        self.remove_config(config)
1145
1146        img = 'core-image-minimal'
1147        with NamedTemporaryFile("w", suffix=".wks") as wks:
1148            wks.writelines(['part /boot --active --source bootimg-biosplusefi --sourceparams="loader=grub-efi"\n',
1149                            'part / --source rootfs --fstype=ext4 --align 1024 --use-uuid\n'\
1150                            'bootloader --timeout=0 --append="console=ttyS0,115200n8"\n'])
1151            wks.flush()
1152            cmd = "wic create %s -e %s -o %s" % (wks.name, img, self.resultdir)
1153            runCmd(cmd)
1154            wksname = os.path.splitext(os.path.basename(wks.name))[0]
1155            out = glob(os.path.join(self.resultdir, "%s-*.direct" % wksname))
1156            self.assertEqual(1, len(out))
1157
1158    @skipIfNotArch(['i586', 'i686', 'x86_64', 'aarch64'])
1159    def test_uefi_kernel(self):
1160        """ Test uefi-kernel in wic """
1161        config = 'IMAGE_EFI_BOOT_FILES="/etc/fstab;testfile"\nIMAGE_FSTYPES = "wic"\nWKS_FILE = "test_uefikernel.wks"\nMACHINE_FEATURES:append = " efi"\n'
1162        self.append_config(config)
1163        bitbake('core-image-minimal')
1164        self.remove_config(config)
1165
1166        img = 'core-image-minimal'
1167        with NamedTemporaryFile("w", suffix=".wks") as wks:
1168            wks.writelines(['part /boot --source bootimg-efi --sourceparams="loader=uefi-kernel"\n'
1169                            'part / --source rootfs --fstype=ext4 --align 1024 --use-uuid\n'\
1170                            'bootloader --timeout=0 --append="console=ttyS0,115200n8"\n'])
1171            wks.flush()
1172            cmd = "wic create %s -e %s -o %s" % (wks.name, img, self.resultdir)
1173            runCmd(cmd)
1174            wksname = os.path.splitext(os.path.basename(wks.name))[0]
1175            out = glob(os.path.join(self.resultdir, "%s-*.direct" % wksname))
1176            self.assertEqual(1, len(out))
1177
1178    # TODO this test could also work on aarch64
1179    @skipIfNotArch(['i586', 'i686', 'x86_64'])
1180    @OETestTag("runqemu")
1181    def test_efi_plugin_unified_kernel_image_qemu(self):
1182        """Test efi plugin's Unified Kernel Image feature in qemu"""
1183        config = 'IMAGE_FSTYPES = "wic"\n'\
1184                 'INITRAMFS_IMAGE = "core-image-minimal-initramfs"\n'\
1185                 'WKS_FILE = "test_efi_plugin.wks"\n'\
1186                 'MACHINE_FEATURES:append = " efi"\n'
1187        self.append_config(config)
1188        bitbake('core-image-minimal core-image-minimal-initramfs ovmf')
1189        self.remove_config(config)
1190
1191        with runqemu('core-image-minimal', ssh=False,
1192                     runqemuparams='nographic ovmf', image_fstype='wic') as qemu:
1193            # Check that /boot has EFI bootx64.efi (required for EFI)
1194            cmd = "ls /boot/EFI/BOOT/bootx64.efi | wc -l"
1195            status, output = qemu.run_serial(cmd)
1196            self.assertEqual(1, status, 'Failed to run command "%s": %s' % (cmd, output))
1197            self.assertEqual(output, '1')
1198            # Check that /boot has EFI/Linux/linux.efi (required for Unified Kernel Images auto detection)
1199            cmd = "ls /boot/EFI/Linux/linux.efi | wc -l"
1200            status, output = qemu.run_serial(cmd)
1201            self.assertEqual(1, status, 'Failed to run command "%s": %s' % (cmd, output))
1202            self.assertEqual(output, '1')
1203            # Check that /boot doesn't have loader/entries/boot.conf (Unified Kernel Images are auto detected by the bootloader)
1204            cmd = "ls /boot/loader/entries/boot.conf 2&>/dev/null | wc -l"
1205            status, output = qemu.run_serial(cmd)
1206            self.assertEqual(1, status, 'Failed to run command "%s": %s' % (cmd, output))
1207            self.assertEqual(output, '0')
1208
1209    def test_fs_types(self):
1210        """Test filesystem types for empty and not empty partitions"""
1211        img = 'core-image-minimal'
1212        with NamedTemporaryFile("w", suffix=".wks") as wks:
1213            wks.writelines(['part ext2   --fstype ext2     --source rootfs\n',
1214                            'part btrfs  --fstype btrfs    --source rootfs --size 40M\n',
1215                            'part squash --fstype squashfs --source rootfs\n',
1216                            'part swap   --fstype swap --size 1M\n',
1217                            'part emptyvfat   --fstype vfat   --size 1M\n',
1218                            'part emptymsdos  --fstype msdos  --size 1M\n',
1219                            'part emptyext2   --fstype ext2   --size 1M\n',
1220                            'part emptybtrfs  --fstype btrfs  --size 150M\n'])
1221            wks.flush()
1222            cmd = "wic create %s -e %s -o %s" % (wks.name, img, self.resultdir)
1223            runCmd(cmd)
1224            wksname = os.path.splitext(os.path.basename(wks.name))[0]
1225            out = glob(os.path.join(self.resultdir, "%s-*direct" % wksname))
1226            self.assertEqual(1, len(out))
1227
1228    def test_kickstart_parser(self):
1229        """Test wks parser options"""
1230        with NamedTemporaryFile("w", suffix=".wks") as wks:
1231            wks.writelines(['part / --fstype ext3 --source rootfs --system-id 0xFF '\
1232                            '--overhead-factor 1.2 --size 100k\n'])
1233            wks.flush()
1234            cmd = "wic create %s -e core-image-minimal -o %s" % (wks.name, self.resultdir)
1235            runCmd(cmd)
1236            wksname = os.path.splitext(os.path.basename(wks.name))[0]
1237            out = glob(os.path.join(self.resultdir, "%s-*direct" % wksname))
1238            self.assertEqual(1, len(out))
1239
1240    def test_image_bootpart_globbed(self):
1241        """Test globbed sources with image-bootpart plugin"""
1242        img = "core-image-minimal"
1243        cmd = "wic create sdimage-bootpart -e %s -o %s" % (img, self.resultdir)
1244        config = 'IMAGE_BOOT_FILES = "%s*"' % get_bb_var('KERNEL_IMAGETYPE', img)
1245        self.append_config(config)
1246        runCmd(cmd)
1247        self.remove_config(config)
1248        self.assertEqual(1, len(glob(os.path.join(self.resultdir, "sdimage-bootpart-*direct"))))
1249
1250    def test_sparse_copy(self):
1251        """Test sparse_copy with FIEMAP and SEEK_HOLE filemap APIs"""
1252        libpath = os.path.join(self.td['COREBASE'], 'scripts', 'lib', 'wic')
1253        sys.path.insert(0, libpath)
1254        from  filemap import FilemapFiemap, FilemapSeek, sparse_copy, ErrorNotSupp
1255        with NamedTemporaryFile("w", suffix=".wic-sparse") as sparse:
1256            src_name = sparse.name
1257            src_size = 1024 * 10
1258            sparse.truncate(src_size)
1259            # write one byte to the file
1260            with open(src_name, 'r+b') as sfile:
1261                sfile.seek(1024 * 4)
1262                sfile.write(b'\x00')
1263            dest = sparse.name + '.out'
1264            # copy src file to dest using different filemap APIs
1265            for api in (FilemapFiemap, FilemapSeek, None):
1266                if os.path.exists(dest):
1267                    os.unlink(dest)
1268                try:
1269                    sparse_copy(sparse.name, dest, api=api)
1270                except ErrorNotSupp:
1271                    continue # skip unsupported API
1272                dest_stat = os.stat(dest)
1273                self.assertEqual(dest_stat.st_size, src_size)
1274                # 8 blocks is 4K (physical sector size)
1275                self.assertEqual(dest_stat.st_blocks, 8)
1276            os.unlink(dest)
1277
1278    def test_mkfs_extraopts(self):
1279        """Test wks option --mkfs-extraopts for empty and not empty partitions"""
1280        img = 'core-image-minimal'
1281        with NamedTemporaryFile("w", suffix=".wks") as wks:
1282            wks.writelines(
1283                ['part ext2   --fstype ext2     --source rootfs --mkfs-extraopts "-D -F -i 8192"\n',
1284                 "part btrfs  --fstype btrfs    --source rootfs --size 40M --mkfs-extraopts='--quiet'\n",
1285                 'part squash --fstype squashfs --source rootfs --mkfs-extraopts "-no-sparse -b 4096"\n',
1286                 'part emptyvfat   --fstype vfat   --size 1M --mkfs-extraopts "-S 1024 -s 64"\n',
1287                 'part emptymsdos  --fstype msdos  --size 1M --mkfs-extraopts "-S 1024 -s 64"\n',
1288                 'part emptyext2   --fstype ext2   --size 1M --mkfs-extraopts "-D -F -i 8192"\n',
1289                 'part emptybtrfs  --fstype btrfs  --size 100M --mkfs-extraopts "--mixed -K"\n'])
1290            wks.flush()
1291            cmd = "wic create %s -e %s -o %s" % (wks.name, img, self.resultdir)
1292            runCmd(cmd)
1293            wksname = os.path.splitext(os.path.basename(wks.name))[0]
1294            out = glob(os.path.join(self.resultdir, "%s-*direct" % wksname))
1295            self.assertEqual(1, len(out))
1296
1297    @skipIfNotArch(['i586', 'i686', 'x86_64'])
1298    @OETestTag("runqemu")
1299    def test_expand_mbr_image(self):
1300        """Test wic write --expand command for mbr image"""
1301        # build an image
1302        config = 'IMAGE_FSTYPES = "wic"\nWKS_FILE = "directdisk.wks"\n'
1303        self.append_config(config)
1304        image = 'core-image-minimal'
1305        bitbake(image)
1306
1307        # get path to the image
1308        bb_vars = get_bb_vars(['DEPLOY_DIR_IMAGE', 'IMAGE_LINK_NAME'], image)
1309        image_path = os.path.join(bb_vars['DEPLOY_DIR_IMAGE'], '%s.wic' % bb_vars['IMAGE_LINK_NAME'])
1310
1311        self.remove_config(config)
1312
1313        try:
1314            # expand image to 1G
1315            new_image_path = None
1316            with NamedTemporaryFile(mode='wb', suffix='.wic.exp',
1317                                    dir=bb_vars['DEPLOY_DIR_IMAGE'], delete=False) as sparse:
1318                sparse.truncate(1024 ** 3)
1319                new_image_path = sparse.name
1320
1321            sysroot = get_bb_var('RECIPE_SYSROOT_NATIVE', 'wic-tools')
1322            cmd = "wic write -n %s --expand 1:0 %s %s" % (sysroot, image_path, new_image_path)
1323            runCmd(cmd)
1324
1325            # check if partitions are expanded
1326            orig = runCmd("wic ls %s -n %s" % (image_path, sysroot))
1327            exp = runCmd("wic ls %s -n %s" % (new_image_path, sysroot))
1328            orig_sizes = [int(line.split()[3]) for line in orig.output.split('\n')[1:]]
1329            exp_sizes = [int(line.split()[3]) for line in exp.output.split('\n')[1:]]
1330            self.assertEqual(orig_sizes[0], exp_sizes[0]) # first partition is not resized
1331            self.assertTrue(orig_sizes[1] < exp_sizes[1])
1332
1333            # Check if all free space is partitioned
1334            result = runCmd("%s/usr/sbin/sfdisk -F %s" % (sysroot, new_image_path))
1335            self.assertTrue("0 B, 0 bytes, 0 sectors" in result.output)
1336
1337            os.rename(image_path, image_path + '.bak')
1338            os.rename(new_image_path, image_path)
1339
1340            # Check if it boots in qemu
1341            with runqemu('core-image-minimal', ssh=False, runqemuparams='nographic') as qemu:
1342                cmd = "ls /etc/"
1343                status, output = qemu.run_serial('true')
1344                self.assertEqual(1, status, 'Failed to run command "%s": %s' % (cmd, output))
1345        finally:
1346            if os.path.exists(new_image_path):
1347                os.unlink(new_image_path)
1348            if os.path.exists(image_path + '.bak'):
1349                os.rename(image_path + '.bak', image_path)
1350
1351    def test_gpt_partition_name(self):
1352        """Test --part-name argument to set partition name in GPT table"""
1353        config = 'IMAGE_FSTYPES += "wic"\nWKS_FILE = "test_gpt_partition_name.wks"\n'
1354        self.append_config(config)
1355        image = 'core-image-minimal'
1356        bitbake(image)
1357        self.remove_config(config)
1358        deploy_dir = get_bb_var('DEPLOY_DIR_IMAGE')
1359        bb_vars = get_bb_vars(['DEPLOY_DIR_IMAGE', 'IMAGE_LINK_NAME'], image)
1360        image_path = os.path.join(bb_vars['DEPLOY_DIR_IMAGE'], '%s.wic' % bb_vars['IMAGE_LINK_NAME'])
1361
1362        sysroot = get_bb_var('RECIPE_SYSROOT_NATIVE', 'wic-tools')
1363
1364        # Image is created
1365        self.assertTrue(os.path.exists(image_path), "image file %s doesn't exist" % image_path)
1366
1367        # Check the names of the three partitions
1368        # as listed in test_gpt_partition_name.wks
1369        result = runCmd("%s/usr/sbin/sfdisk --part-label %s 1" % (sysroot, image_path))
1370        self.assertEqual('boot-A', result.output)
1371        result = runCmd("%s/usr/sbin/sfdisk --part-label %s 2" % (sysroot, image_path))
1372        self.assertEqual('root-A', result.output)
1373        # When the --part-name is not defined, the partition name is equal to the --label
1374        result = runCmd("%s/usr/sbin/sfdisk --part-label %s 3" % (sysroot, image_path))
1375        self.assertEqual('ext-space', result.output)
1376
1377class ModifyTests(WicTestCase):
1378    def test_wic_ls(self):
1379        """Test listing image content using 'wic ls'"""
1380        runCmd("wic create wictestdisk "
1381                                   "--image-name=core-image-minimal "
1382                                   "-D -o %s" % self.resultdir)
1383        images = glob(os.path.join(self.resultdir, "wictestdisk-*.direct"))
1384        self.assertEqual(1, len(images))
1385
1386        sysroot = get_bb_var('RECIPE_SYSROOT_NATIVE', 'wic-tools')
1387
1388        # list partitions
1389        result = runCmd("wic ls %s -n %s" % (images[0], sysroot))
1390        self.assertEqual(3, len(result.output.split('\n')))
1391
1392        # list directory content of the first partition
1393        result = runCmd("wic ls %s:1/ -n %s" % (images[0], sysroot))
1394        self.assertEqual(6, len(result.output.split('\n')))
1395
1396    def test_wic_cp(self):
1397        """Test copy files and directories to the the wic image."""
1398        runCmd("wic create wictestdisk "
1399                                   "--image-name=core-image-minimal "
1400                                   "-D -o %s" % self.resultdir)
1401        images = glob(os.path.join(self.resultdir, "wictestdisk-*.direct"))
1402        self.assertEqual(1, len(images))
1403
1404        sysroot = get_bb_var('RECIPE_SYSROOT_NATIVE', 'wic-tools')
1405
1406        # list directory content of the first partition
1407        result = runCmd("wic ls %s:1/ -n %s" % (images[0], sysroot))
1408        self.assertEqual(6, len(result.output.split('\n')))
1409
1410        with NamedTemporaryFile("w", suffix=".wic-cp") as testfile:
1411            testfile.write("test")
1412
1413            # copy file to the partition
1414            runCmd("wic cp %s %s:1/ -n %s" % (testfile.name, images[0], sysroot))
1415
1416            # check if file is there
1417            result = runCmd("wic ls %s:1/ -n %s" % (images[0], sysroot))
1418            self.assertEqual(7, len(result.output.split('\n')))
1419            self.assertTrue(os.path.basename(testfile.name) in result.output)
1420
1421            # prepare directory
1422            testdir = os.path.join(self.resultdir, 'wic-test-cp-dir')
1423            testsubdir = os.path.join(testdir, 'subdir')
1424            os.makedirs(os.path.join(testsubdir))
1425            copy(testfile.name, testdir)
1426
1427            # copy directory to the partition
1428            runCmd("wic cp %s %s:1/ -n %s" % (testdir, images[0], sysroot))
1429
1430            # check if directory is there
1431            result = runCmd("wic ls %s:1/ -n %s" % (images[0], sysroot))
1432            self.assertEqual(8, len(result.output.split('\n')))
1433            self.assertTrue(os.path.basename(testdir) in result.output)
1434
1435            # copy the file from the partition and check if it success
1436            dest = '%s-cp' % testfile.name
1437            runCmd("wic cp %s:1/%s %s -n %s" % (images[0],
1438                    os.path.basename(testfile.name), dest, sysroot))
1439            self.assertTrue(os.path.exists(dest))
1440
1441
1442    def test_wic_rm(self):
1443        """Test removing files and directories from the the wic image."""
1444        runCmd("wic create mkefidisk "
1445                                   "--image-name=core-image-minimal "
1446                                   "-D -o %s" % self.resultdir)
1447        images = glob(os.path.join(self.resultdir, "mkefidisk-*.direct"))
1448        self.assertEqual(1, len(images))
1449
1450        sysroot = get_bb_var('RECIPE_SYSROOT_NATIVE', 'wic-tools')
1451        # Not bulletproof but hopefully sufficient
1452        kerneltype = get_bb_var('KERNEL_IMAGETYPE', 'virtual/kernel')
1453
1454        # list directory content of the first partition
1455        result = runCmd("wic ls %s:1 -n %s" % (images[0], sysroot))
1456        self.assertIn('\n%s ' % kerneltype.upper(), result.output)
1457        self.assertIn('\nEFI          <DIR>     ', result.output)
1458
1459        # remove file. EFI partitions are case-insensitive so exercise that too
1460        runCmd("wic rm %s:1/%s -n %s" % (images[0], kerneltype.lower(), sysroot))
1461
1462        # remove directory
1463        runCmd("wic rm %s:1/efi -n %s" % (images[0], sysroot))
1464
1465        # check if they're removed
1466        result = runCmd("wic ls %s:1 -n %s" % (images[0], sysroot))
1467        self.assertNotIn('\n%s        ' % kerneltype.upper(), result.output)
1468        self.assertNotIn('\nEFI          <DIR>     ', result.output)
1469
1470    def test_wic_ls_ext(self):
1471        """Test listing content of the ext partition using 'wic ls'"""
1472        runCmd("wic create wictestdisk "
1473                                   "--image-name=core-image-minimal "
1474                                   "-D -o %s" % self.resultdir)
1475        images = glob(os.path.join(self.resultdir, "wictestdisk-*.direct"))
1476        self.assertEqual(1, len(images))
1477
1478        sysroot = get_bb_var('RECIPE_SYSROOT_NATIVE', 'wic-tools')
1479
1480        # list directory content of the second ext4 partition
1481        result = runCmd("wic ls %s:2/ -n %s" % (images[0], sysroot))
1482        self.assertTrue(set(['bin', 'home', 'proc', 'usr', 'var', 'dev', 'lib', 'sbin']).issubset(
1483                            set(line.split()[-1] for line in result.output.split('\n') if line)))
1484
1485    def test_wic_cp_ext(self):
1486        """Test copy files and directories to the ext partition."""
1487        runCmd("wic create wictestdisk "
1488                                   "--image-name=core-image-minimal "
1489                                   "-D -o %s" % self.resultdir)
1490        images = glob(os.path.join(self.resultdir, "wictestdisk-*.direct"))
1491        self.assertEqual(1, len(images))
1492
1493        sysroot = get_bb_var('RECIPE_SYSROOT_NATIVE', 'wic-tools')
1494
1495        # list directory content of the ext4 partition
1496        result = runCmd("wic ls %s:2/ -n %s" % (images[0], sysroot))
1497        dirs = set(line.split()[-1] for line in result.output.split('\n') if line)
1498        self.assertTrue(set(['bin', 'home', 'proc', 'usr', 'var', 'dev', 'lib', 'sbin']).issubset(dirs))
1499
1500        with NamedTemporaryFile("w", suffix=".wic-cp") as testfile:
1501            testfile.write("test")
1502
1503            # copy file to the partition
1504            runCmd("wic cp %s %s:2/ -n %s" % (testfile.name, images[0], sysroot))
1505
1506            # check if file is there
1507            result = runCmd("wic ls %s:2/ -n %s" % (images[0], sysroot))
1508            newdirs = set(line.split()[-1] for line in result.output.split('\n') if line)
1509            self.assertEqual(newdirs.difference(dirs), set([os.path.basename(testfile.name)]))
1510
1511            # check if the file to copy is in the partition
1512            result = runCmd("wic ls %s:2/etc/ -n %s" % (images[0], sysroot))
1513            self.assertTrue('fstab' in [line.split()[-1] for line in result.output.split('\n') if line])
1514
1515            # copy file from the partition, replace the temporary file content with it and
1516            # check for the file size to validate the copy
1517            runCmd("wic cp %s:2/etc/fstab %s -n %s" % (images[0], testfile.name, sysroot))
1518            self.assertTrue(os.stat(testfile.name).st_size > 0)
1519
1520
1521    def test_wic_rm_ext(self):
1522        """Test removing files from the ext partition."""
1523        runCmd("wic create mkefidisk "
1524                                   "--image-name=core-image-minimal "
1525                                   "-D -o %s" % self.resultdir)
1526        images = glob(os.path.join(self.resultdir, "mkefidisk-*.direct"))
1527        self.assertEqual(1, len(images))
1528
1529        sysroot = get_bb_var('RECIPE_SYSROOT_NATIVE', 'wic-tools')
1530
1531        # list directory content of the /etc directory on ext4 partition
1532        result = runCmd("wic ls %s:2/etc/ -n %s" % (images[0], sysroot))
1533        self.assertTrue('fstab' in [line.split()[-1] for line in result.output.split('\n') if line])
1534
1535        # remove file
1536        runCmd("wic rm %s:2/etc/fstab -n %s" % (images[0], sysroot))
1537
1538        # check if it's removed
1539        result = runCmd("wic ls %s:2/etc/ -n %s" % (images[0], sysroot))
1540        self.assertTrue('fstab' not in [line.split()[-1] for line in result.output.split('\n') if line])
1541
1542        # remove non-empty directory
1543        runCmd("wic rm -r %s:2/etc/ -n %s" % (images[0], sysroot))
1544
1545        # check if it's removed
1546        result = runCmd("wic ls %s:2/ -n %s" % (images[0], sysroot))
1547        self.assertTrue('etc' not in [line.split()[-1] for line in result.output.split('\n') if line])
1548