From 92c4403fd1b346762b27cabdbbac4ffc9a3f5ff8 Mon Sep 17 00:00:00 2001 From: Yann Dirson Date: Fri, 29 Jul 2022 13:28:26 +0200 Subject: [PATCH 01/15] Coverage: test_cpio: recursive add to archive Adds more data to the archive (a copy of the same file, from another wd). Uses the "." special case for even more coverage. Coverage change: xcp.cpiofile: 50% -> 51.7% total: 38.4% -> 39% Signed-off-by: Yann Dirson --- tests/test_cpio.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/test_cpio.py b/tests/test_cpio.py index 33084484..f63bc2e3 100644 --- a/tests/test_cpio.py +++ b/tests/test_cpio.py @@ -73,6 +73,10 @@ def archiveCreate(self, fn, fmt='w'): arc = CpioFile.open(fn, fmt) f = arc.getcpioinfo('archive/data') arc.addfile(f, open('archive/data')) + # test recursively add "." + os.chdir('archive') + arc.add(".") + os.chdir("..") # TODO add self crafted file arc.close() # special case for XZ, test check type (crc32) From 925e330afc18f948654323001970f560c0d8ebd6 Mon Sep 17 00:00:00 2001 From: Yann Dirson Date: Wed, 20 Jul 2022 16:10:21 +0200 Subject: [PATCH 02/15] Coverage: test_cpio: exercise "extractall()" and check its output Coverage change: xcp.cpiofile: 51.7% -> 58.1% total: 39% -> 41.1% Signed-off-by: Yann Dirson --- tests/test_cpio.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/tests/test_cpio.py b/tests/test_cpio.py index f63bc2e3..85ff62d8 100644 --- a/tests/test_cpio.py +++ b/tests/test_cpio.py @@ -53,7 +53,7 @@ def setUp(self): self.doXZ = False def tearDown(self): - check_call("rm -rf archive archive.cpio*") + check_call("rm -rf archive archive.cpio* archive2") # TODO check with file (like 'r:*') # TODO use cat to check properly for pipes @@ -66,7 +66,15 @@ def archiveExtract(self, fn, fmt='r|*'): self.assertEqual(len(data), f.size) self.assertEqual(self.md5data, md5(data).hexdigest()) found = True + arc.close() self.assertTrue(found) + # extract with extractall and compare + arc = CpioFile.open(fn, fmt) + check_call("rm -rf archive2") + os.rename('archive', 'archive2') + arc.extractall() + check_call("diff -rq archive2 archive") + arc.close() def archiveCreate(self, fn, fmt='w'): os.unlink(fn) From d8fdc3dd824565d04eb955bb446212323f50274a Mon Sep 17 00:00:00 2001 From: Yann Dirson Date: Fri, 29 Jul 2022 14:03:36 +0200 Subject: [PATCH 03/15] Coverage: test_pci: new test from snippet in pci.py Coverage change: xcp.pci: 40.9% -> 76.3% total: 41.1% -> 43% Signed-off-by: Yann Dirson --- tests/data/lspci-mn | 36 +++++++++++++++++++++++++++++++ tests/data/pci.ids | 29 +++++++++++++++++++++++++ tests/test_pci.py | 52 ++++++++++++++++++++++++++++++++++++++++++++- xcp/pci.py | 16 -------------- 4 files changed, 116 insertions(+), 17 deletions(-) create mode 100644 tests/data/lspci-mn create mode 100644 tests/data/pci.ids diff --git a/tests/data/lspci-mn b/tests/data/lspci-mn new file mode 100644 index 00000000..4450e6d3 --- /dev/null +++ b/tests/data/lspci-mn @@ -0,0 +1,36 @@ +00:00.0 "0600" "1022" "1630" "1022" "1630" +00:00.2 "0806" "1022" "1631" "1022" "1631" +00:01.0 "0600" "1022" "1632" "" "" +00:01.1 "0604" "1022" "1633" "" "" +00:02.0 "0600" "1022" "1632" "" "" +00:02.1 "0604" "1022" "1634" "" "" +00:02.2 "0604" "1022" "1634" "" "" +00:02.3 "0604" "1022" "1634" "" "" +00:08.0 "0600" "1022" "1632" "" "" +00:08.1 "0604" "1022" "1635" "" "" +00:08.2 "0604" "1022" "1635" "" "" +00:14.0 "0c05" "1022" "790b" -r51 "1462" "12ac" +00:14.3 "0601" "1022" "790e" -r51 "1462" "12ac" +00:18.0 "0600" "1022" "1448" "" "" +00:18.1 "0600" "1022" "1449" "" "" +00:18.2 "0600" "1022" "144a" "" "" +00:18.3 "0600" "1022" "144b" "" "" +00:18.4 "0600" "1022" "144c" "" "" +00:18.5 "0600" "1022" "144d" "" "" +00:18.6 "0600" "1022" "144e" "" "" +00:18.7 "0600" "1022" "144f" "" "" +01:00.0 "0604" "1002" "1478" -rc1 "" "" +02:00.0 "0604" "1002" "1479" "" "" +03:00.0 "0380" "1002" "7340" -rc1 "1462" "12ac" +03:00.1 "0403" "1002" "ab38" "1462" "12ac" +04:00.0 "0280" "8086" "2723" -r1a "8086" "0084" +05:00.0 "0200" "10ec" "8168" -r15 "1462" "12ac" +06:00.0 "0108" "144d" "a808" -p02 "144d" "a801" +07:00.0 "0300" "1002" "1636" -rc6 "1462" "12ac" +07:00.2 "1080" "1022" "15df" "1022" "15df" +07:00.3 "0c03" "1022" "1639" -p30 "1462" "12ac" +07:00.4 "0c03" "1022" "1639" -p30 "1462" "12ac" +07:00.5 "0480" "1022" "15e2" -r01 "1462" "12ac" +07:00.6 "0403" "1022" "15e3" "1462" "12ac" +08:00.0 "0106" "1022" "7901" -r81 -p01 "1462" "12ac" +08:00.1 "0106" "1022" "7901" -r81 -p01 "1462" "12ac" diff --git a/tests/data/pci.ids b/tests/data/pci.ids new file mode 100644 index 00000000..acaccbb1 --- /dev/null +++ b/tests/data/pci.ids @@ -0,0 +1,29 @@ +## Excerpt from /usr/share/hwdata/pci.ids + +# Vendors, devices and subsystems. Please keep sorted. + +# Syntax: +# vendor vendor_name +# device device_name <-- single tab +# subvendor subdevice subsystem_name <-- two tabs + +1002 Advanced Micro Devices, Inc. [AMD/ATI] + 1314 Wrestler HDMI Audio + 174b 1001 PURE Fusion Mini + 1636 Renoir + 7340 Navi 14 [Radeon RX 5500/5500M / Pro 5500M] + +# List of known device classes, subclasses and programming interfaces + +# Syntax: +# C class class_name +# subclass subclass_name <-- single tab +# prog-if prog-if_name <-- two tabs + +C 03 Display controller + 00 VGA compatible controller + 00 VGA controller + 01 8514 controller + 01 XGA compatible controller + 02 3D controller + 80 Display controller diff --git a/tests/test_pci.py b/tests/test_pci.py index 65fcd796..59af4828 100644 --- a/tests/test_pci.py +++ b/tests/test_pci.py @@ -1,8 +1,10 @@ #!/usr/bin/env python import unittest, sys, os, os.path as path +import subprocess +from mock import patch, Mock -from xcp.pci import PCI +from xcp.pci import PCI, PCIIds, PCIDevices class TestInvalid(unittest.TestCase): @@ -56,5 +58,53 @@ def test_equality(self): self.assertEqual(PCI("0000:00:00.0"), PCI("00:00.0")) +class TestPCIIds(unittest.TestCase): + def tests_nodb(self): + with patch("xcp.pci.os.path.exists") as exists_mock: + exists_mock.return_value = False + with self.assertRaises(Exception): + PCIIds.read() + exists_mock.assert_called_once_with("/usr/share/hwdata/pci.ids") + + def tests_videoclass(self): + with patch("xcp.pci.os.path.exists") as exists_mock, \ + patch("xcp.pci.open") as open_mock, \ + open("tests/data/pci.ids") as fake_data: + exists_mock.return_value = True + open_mock.return_value.__iter__ = Mock(return_value=iter(fake_data)) + ids = PCIIds.read() + exists_mock.assert_called_once_with("/usr/share/hwdata/pci.ids") + open_mock.assert_called_once_with("/usr/share/hwdata/pci.ids") + video_class = ids.lookupClass('Display controller') + self.assertEqual(video_class, ['03']) + + with patch("xcp.pci.subprocess.Popen") as popen_mock, \ + open("tests/data/lspci-mn") as fake_data: + popen_mock.return_value.stdout.__iter__ = Mock(return_value=iter(fake_data)) + devs = PCIDevices() + popen_mock.assert_called_once_with(['lspci', '-mn'], bufsize = 1, + stdout = subprocess.PIPE) + sorted_devices = sorted(devs.findByClass(video_class), + key=lambda x: x['id']) + self.assertEqual(len(sorted_devices), 2) + + for (video_dev, + num_functions, + vendor, + device, + ) in zip(sorted_devices, + (1, 5), + ("Advanced Micro Devices, Inc. [AMD/ATI]", + "Advanced Micro Devices, Inc. [AMD/ATI]"), + ("Navi 14 [Radeon RX 5500/5500M / Pro 5500M]", + "Renoir"), + ): + self.assertEqual(len(devs.findRelatedFunctions(video_dev['id'])), num_functions) + self.assertEqual(ids.findVendor(video_dev['vendor']), vendor) + self.assertEqual(ids.findDevice(video_dev['vendor'], video_dev['device']), device) + + self.assertEqual(len(devs.findRelatedFunctions('00:18.1')), 7) + + if __name__ == "__main__": sys.exit(unittest.main()) diff --git a/xcp/pci.py b/xcp/pci.py index c1fc90e3..8b371152 100644 --- a/xcp/pci.py +++ b/xcp/pci.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python - # Copyright (c) 2013, Citrix Inc. # All rights reserved. # @@ -314,17 +312,3 @@ def pci_sbdfi_to_nic(sbdfi, nics): raise Exception("Insufficient NICs with PCI SBDF %s (Found %d, wanted at least %d)" % (value, len(matching_nics), index)) return matching_nics[index] - - -if __name__ == "__main__": - IDS = PCIIds.read() - VIDEO_CLASS = IDS.lookupClass('Display controller') - - DEVS = PCIDevices() - for video_dev in DEVS.findByClass(VIDEO_CLASS): - print video_dev['id'], IDS.findVendor(video_dev['vendor']), \ - IDS.findDevice(video_dev['vendor'], video_dev['device']) - print DEVS.findRelatedFunctions(video_dev['id']) - print DEVS.findRelatedFunctions('00:1d.1') - - From 02468d43685f14cc3ea43cebd24dc85294683c9d Mon Sep 17 00:00:00 2001 From: Yann Dirson Date: Fri, 29 Jul 2022 14:04:04 +0200 Subject: [PATCH 04/15] Coverage: new test_cmd from snippet in cmd.py Coverage change: xcp.cmd: 0% -> 78.0% total: 43% -> 44.2% Signed-off-by: Yann Dirson --- tests/test_cmd.py | 54 +++++++++++++++++++++++++++++++++++++++++++++++ xcp/cmd.py | 11 ---------- 2 files changed, 54 insertions(+), 11 deletions(-) create mode 100644 tests/test_cmd.py diff --git a/tests/test_cmd.py b/tests/test_cmd.py new file mode 100644 index 00000000..c6304873 --- /dev/null +++ b/tests/test_cmd.py @@ -0,0 +1,54 @@ +import unittest +from mock import patch, Mock, DEFAULT + +from xcp.cmd import OutputCache + +class TestCache(unittest.TestCase): + def setUp(self): + self.c = OutputCache() + + def test_fileContents(self): + with patch("xcp.cmd.open") as open_mock: + open_mock.return_value.readlines = Mock(return_value=["line1\n", "line2\n"]) + + # uncached fileContents + data = self.c.fileContents('/tmp/foo') + open_mock.assert_called_once_with("/tmp/foo") + self.assertEqual(data, "line1\nline2\n") + + # rerun as cached + open_mock.reset_mock() + data = self.c.fileContents('/tmp/foo') + open_mock.assert_not_called() + self.assertEqual(data, "line1\nline2\n") + + # rerun after clearing cache + open_mock.reset_mock() + self.c.clearCache() + data = self.c.fileContents('/tmp/foo') + open_mock.assert_called_once_with("/tmp/foo") + self.assertEqual(data, "line1\nline2\n") + + def test_runCmd(self): + output_data = "line1\nline2\n" + with patch("xcp.cmd.subprocess.Popen") as popen_mock: + # mock Popen .communicate and .returncode for + # `output_data`on stdout, nothing on stderr, and exit + # value of 42 + communicate_mock = Mock(return_value=(output_data, "")) + popen_mock.return_value.communicate = communicate_mock + def communicate_side_effect(_input_text): + popen_mock.return_value.returncode = 42 + return DEFAULT + communicate_mock.side_effect = communicate_side_effect + + # uncached runCmd + data = self.c.runCmd(['ls', '/tmp'], True) + popen_mock.assert_called_once() + self.assertEqual(data, (42, output_data)) + + # rerun as cached + popen_mock.reset_mock() + data = self.c.runCmd(['ls', '/tmp'], True) + popen_mock.assert_not_called() + self.assertEqual(data, (42, output_data)) diff --git a/xcp/cmd.py b/xcp/cmd.py index b51eaa8a..ff039484 100644 --- a/xcp/cmd.py +++ b/xcp/cmd.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python - # Copyright (c) 2013, Citrix Inc. # All rights reserved. # @@ -89,12 +87,3 @@ def runCmd(self, command, with_stdout = False, with_stderr = False, inputtext = def clearCache(self): self.cache.clear() - -if __name__ == '__main__': - c = OutputCache() - print c.fileContents('/tmp/foo') - print c.fileContents('/tmp/foo') - c.clearCache() - print c.fileContents('/tmp/foo') - print c.runCmd(['ls', '/tmp'], True) - print c.runCmd(['ls', '/tmp'], True) From b1a5152698deb9645b9c4efbcf1edb32b2790343 Mon Sep 17 00:00:00 2001 From: Yann Dirson Date: Fri, 22 Jul 2022 17:28:03 +0200 Subject: [PATCH 05/15] Coverage: new test_xmlunwrap from snippet in xmlunwrap.py Coverage change: xcp.xmlunwrap: 0% -> 68.6%% total: 44.2% -> 45.2% Signed-off-by: Yann Dirson --- tests/test_xmlunwrap.py | 37 +++++++++++++++++++++++++++++++++++++ xcp/xmlunwrap.py | 27 --------------------------- 2 files changed, 37 insertions(+), 27 deletions(-) create mode 100644 tests/test_xmlunwrap.py diff --git a/tests/test_xmlunwrap.py b/tests/test_xmlunwrap.py new file mode 100644 index 00000000..f71d778f --- /dev/null +++ b/tests/test_xmlunwrap.py @@ -0,0 +1,37 @@ +import unittest +import xml.dom.minidom + +from xcp.xmlunwrap import (getElementsByTagName, getText, getMapAttribute, + getStrAttribute, XmlUnwrapError) + +class TestXmlUnwrap(unittest.TestCase): + def setUp(self): + a_text = """ + text1 + text2 + """ + xmldoc = xml.dom.minidom.parseString(a_text) + self.top_el = xmldoc.documentElement + + def test(self): + self.assertEqual(self.top_el.tagName, "installation") + + self.assertEqual([getText(el) + for el in getElementsByTagName(self.top_el, ["fred"])], + ["text1", "text2"]) + + x = getMapAttribute(self.top_el, ["mode"], [('test', 42), ('stuff', 77)]) + self.assertEqual(x, 42) + x = getMapAttribute(self.top_el, ["made"], [('test', 42), ('stuff', 77)], + default='stuff') + self.assertEqual(x, 77) + + x = getStrAttribute(self.top_el, ["mode"]) + self.assertEqual(x, "test") + x = getStrAttribute(self.top_el, ["made"]) + self.assertEqual(x, "") + x = getStrAttribute(self.top_el, ["made"], None) + self.assertEqual(x, None) + + with self.assertRaises(XmlUnwrapError): + x = getStrAttribute(self.top_el, ["made"], mandatory=True) diff --git a/xcp/xmlunwrap.py b/xcp/xmlunwrap.py index 3256cab3..654b84bf 100644 --- a/xcp/xmlunwrap.py +++ b/xcp/xmlunwrap.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python - # Copyright (c) 2013, Citrix Inc. # All rights reserved. # @@ -25,9 +23,6 @@ """xmlunwrap - general methods to unwrap XML elements & attributes""" -import xml.dom.minidom - - class XmlUnwrapError(Exception): pass @@ -87,25 +82,3 @@ def getMapAttribute(el, attrs, mapping, default = None): k_list = list(k) return v[k_list.index(key)] - -if __name__ == '__main__': - - a_text = """ - text1 - text2 - """ - xmldoc = xml.dom.minidom.parseString(a_text) - top_el = xmldoc.documentElement - - print top_el.tagName - - for el in getElementsByTagName(top_el, ["fred"]): - print getText(el) - - print getMapAttribute(top_el, ["mode"], [('test', 42), ('stuff', 77)]) - print getMapAttribute(top_el, ["made"], [('test', 42), ('stuff', 77)], default = 'stuff') - - print getStrAttribute(top_el, ["mode"]) - print getStrAttribute(top_el, ["made"]) - print getStrAttribute(top_el, ["made"], None) - print getStrAttribute(top_el, ["made"], mandatory = True) From cc0121e5f7b790bf607a864e6ab619f877e72d35 Mon Sep 17 00:00:00 2001 From: Yann Dirson Date: Mon, 25 Jul 2022 11:15:27 +0200 Subject: [PATCH 06/15] Coverage: new test_bootloader We may want to do some real checks on the output, this first version only exercises the code. Includes ad-hoc testing of write/read methods for grub/extlinux from a grub2 configuration, for lack of representative versions of those deprecated config files. Ad-hoc test for extlinux reveals inconsistent typing, and is marked as failing for now: print("serial %d %d" % (self.serial['port'], > self.serial['baud']), file=fh) E TypeError: %d format: a number is required, not str Coverage change: xcp.bootloader: 0% -> 59.1%% total: 45.2% -> 53.4% Signed-off-by: Yann Dirson --- tests/data/grub.cfg | 35 ++++++++++++++++++++++++++++++++++ tests/test_bootloader.py | 41 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 76 insertions(+) create mode 100644 tests/data/grub.cfg create mode 100644 tests/test_bootloader.py diff --git a/tests/data/grub.cfg b/tests/data/grub.cfg new file mode 100644 index 00000000..c6d2b4a1 --- /dev/null +++ b/tests/data/grub.cfg @@ -0,0 +1,35 @@ +serial --unit=0 --speed=115200 +terminal_input serial console +terminal_output serial console +set default=0 +set timeout=5 +menuentry 'XCP-ng' { + search --label --set root root-vgdorj + multiboot2 /boot/xen.gz dom0_mem=7584M,max:7584M watchdog ucode=scan dom0_max_vcpus=1-16 crashkernel=256M,below=4G console=vga vga=mode-0x0311 + module2 /boot/vmlinuz-4.19-xen root=LABEL=root-vgdorj ro nolvm hpet=disable console=hvc0 console=tty0 quiet vga=785 splash plymouth.ignore-serial-consoles + module2 /boot/initrd-4.19-xen.img +} +menuentry 'XCP-ng (Serial)' { + search --label --set root root-vgdorj + multiboot2 /boot/xen.gz com1=115200,8n1 console=com1,vga dom0_mem=7584M,max:7584M watchdog ucode=scan dom0_max_vcpus=1-16 crashkernel=256M,below=4G + module2 /boot/vmlinuz-4.19-xen root=LABEL=root-vgdorj ro nolvm hpet=disable console=tty0 console=hvc0 + module2 /boot/initrd-4.19-xen.img +} +menuentry 'XCP-ng in Safe Mode' { + search --label --set root root-vgdorj + multiboot2 /boot/xen.gz nosmp noreboot noirqbalance no-mce no-bootscrub no-numa no-hap no-mmcfg max_cstate=0 nmi=ignore allow_unsafe dom0_mem=7584M,max:7584M com1=115200,8n1 console=com1,vga + module2 /boot/vmlinuz-4.19-xen earlyprintk=xen root=LABEL=root-vgdorj ro nolvm hpet=disable console=tty0 console=hvc0 + module2 /boot/initrd-4.19-xen.img +} +menuentry 'XCP-ng (Xen 4.13.1 / Linux 4.19.0+1)' { + search --label --set root root-vgdorj + multiboot2 /boot/xen-fallback.gz dom0_mem=7584M,max:7584M watchdog ucode=scan dom0_max_vcpus=1-16 crashkernel=256M,below=4G + module2 /boot/vmlinuz-fallback root=LABEL=root-vgdorj ro nolvm hpet=disable console=hvc0 console=tty0 + module2 /boot/initrd-fallback.img +} +menuentry 'XCP-ng (Serial, Xen 4.13.1 / Linux 4.19.0+1)' { + search --label --set root root-vgdorj + multiboot2 /boot/xen-fallback.gz com1=115200,8n1 console=com1,vga dom0_mem=7584M,max:7584M watchdog ucode=scan dom0_max_vcpus=1-16 crashkernel=256M,below=4G + module2 /boot/vmlinuz-fallback root=LABEL=root-vgdorj ro nolvm hpet=disable console=tty0 console=hvc0 + module2 /boot/initrd-fallback.img +} diff --git a/tests/test_bootloader.py b/tests/test_bootloader.py new file mode 100644 index 00000000..35a20f26 --- /dev/null +++ b/tests/test_bootloader.py @@ -0,0 +1,41 @@ +import unittest +import os +import subprocess +from tempfile import NamedTemporaryFile + +from xcp.bootloader import Bootloader + +class TestBootloader(unittest.TestCase): + def test_grub2(self): + bl = Bootloader.readGrub2("tests/data/grub.cfg") + with NamedTemporaryFile("w") as temp: + bl.writeGrub2(temp.name) + # get a diff + proc = subprocess.Popen(["diff", "tests/data/grub.cfg", temp.name], + stdout = subprocess.PIPE, + universal_newlines=True) + for line in proc.stdout: + # FIXME: check is entirely ad-hoc, should we have a diff at all ? + self.assertRegexpMatches(line, r"^(5a6,13$|>)") + + proc.stdout.close() + proc.wait() + + +class TestBootloaderAdHoc(unittest.TestCase): + def setUp(self): + self.bl = Bootloader.readGrub2("tests/data/grub.cfg") + + def test_grub(self): + with NamedTemporaryFile("w", delete=False) as temp: + self.bl.writeGrub(temp) + bl2 = Bootloader.readGrub(temp.name) + os.unlink(temp.name) + + @unittest.expectedFailure + def test_extlinux(self): + # FIXME that one triggers a format error, real inconsistency bug ? + with NamedTemporaryFile("w", delete=False) as temp: + self.bl.writeExtLinux(temp) + bl2 = Bootloader.readExtLinux(temp.name) + os.unlink(temp.name) From 7c609a4952ea3afe6892712156fa7ab74e655b33 Mon Sep 17 00:00:00 2001 From: Yann Dirson Date: Mon, 25 Jul 2022 15:38:07 +0200 Subject: [PATCH 07/15] xcp.bootloader: relax format strings for serial config File format parsers do not all agree whether to store some parameters as int or str. This relax the format strings to accept both. Fixes creating an extlinux config from a grub2 config. Coverage change: xcp.bootloader: 59.1% -> 72.8% total: 53.4% -> 55.3% Signed-off-by: Yann Dirson --- tests/test_bootloader.py | 2 -- xcp/bootloader.py | 8 ++++---- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/tests/test_bootloader.py b/tests/test_bootloader.py index 35a20f26..ca48204e 100644 --- a/tests/test_bootloader.py +++ b/tests/test_bootloader.py @@ -32,9 +32,7 @@ def test_grub(self): bl2 = Bootloader.readGrub(temp.name) os.unlink(temp.name) - @unittest.expectedFailure def test_extlinux(self): - # FIXME that one triggers a format error, real inconsistency bug ? with NamedTemporaryFile("w", delete=False) as temp: self.bl.writeExtLinux(temp) bl2 = Bootloader.readExtLinux(temp.name) diff --git a/xcp/bootloader.py b/xcp/bootloader.py index e1af85f6..51e362e8 100644 --- a/xcp/bootloader.py +++ b/xcp/bootloader.py @@ -466,10 +466,10 @@ def writeExtLinux(self, dst_file = None): if self.serial: if self.serial.get('flow', None) is None: - print >> fh, "serial %d %d" % (self.serial['port'], + print >> fh, "serial %s %s" % (self.serial['port'], self.serial['baud']) else: - print >> fh, "serial %d %d %s" % (self.serial['port'], + print >> fh, "serial %s %s %s" % (self.serial['port'], self.serial['baud'], self.serial['flow']) if self.default: @@ -507,7 +507,7 @@ def writeGrub(self, dst_file = None): print >> fh, "# location " + self.location if self.serial: - print >> fh, "serial --unit=%d --speed=%s" % (self.serial['port'], + print >> fh, "serial --unit=%s --speed=%s" % (self.serial['port'], self.serial['baud']) print >> fh, "terminal --timeout=10 console serial" else: @@ -540,7 +540,7 @@ def writeGrub2(self, dst_file = None): fh = open(dst_file, 'w') if self.serial: - print >> fh, "serial --unit=%d --speed=%s" % (self.serial['port'], + print >> fh, "serial --unit=%s --speed=%s" % (self.serial['port'], self.serial['baud']) print >> fh, "terminal_input serial console" print >> fh, "terminal_output serial console" From 8333f8b0cb372705f8ffeb9694b9328ce8dad231 Mon Sep 17 00:00:00 2001 From: Yann Dirson Date: Tue, 26 Jul 2022 16:08:49 +0200 Subject: [PATCH 08/15] Coverage: new tests for xcp.accessor and xcp.repository Only FileAccessor is actually covered by these tests. HTTPAccessor tests are provided, but are skipped by default, as they currently need network access to xcp-ng.org repository. We will want at some point to mock the HTTP access. Coverage change: xcp.accessor: 0% -> 30.0% xcp.repository: 0% -> 72.2% total: 55.3% -> 62.7% Signed-off-by: Yann Dirson --- tests/data/repo/.treeinfo | 34 ++++++++++++++++++ tests/data/repo/XS-PACKAGES | 0 tests/data/repo/XS-REPOSITORY | 14 ++++++++ tests/data/repo/XS-REPOSITORY-LIST | 0 tests/data/repo/repodata/repomd.xml | 55 +++++++++++++++++++++++++++++ tests/test_accessor.py | 21 +++++++++++ tests/test_repository.py | 25 +++++++++++++ 7 files changed, 149 insertions(+) create mode 100644 tests/data/repo/.treeinfo create mode 100644 tests/data/repo/XS-PACKAGES create mode 100644 tests/data/repo/XS-REPOSITORY create mode 100644 tests/data/repo/XS-REPOSITORY-LIST create mode 100644 tests/data/repo/repodata/repomd.xml create mode 100644 tests/test_accessor.py create mode 100644 tests/test_repository.py diff --git a/tests/data/repo/.treeinfo b/tests/data/repo/.treeinfo new file mode 100644 index 00000000..e49cb4fc --- /dev/null +++ b/tests/data/repo/.treeinfo @@ -0,0 +1,34 @@ +[platform] +name = XCP +version = 3.2.1 + +[branding] +name = XCP-ng +version = 8.2.1 + +[build] +number = release/yangtze/master/58 + +[keys] +key1 = RPM-GPG-KEY-CH-8 +key2 = RPM-GPG-KEY-CH-8-LCM +key3 = RPM-GPG-KEY-Platform-V1 + +[general] +name = XCP-ng-8.2.1 +family = XCP-ng +timestamp = 1645700813.00 +variant = +version = 8.2.1 +packagedir = +arch = x86_64 + +[images-x86_64] +kernel = boot/pxelinux/mboot.c32 +initrd = boot/vmlinuz +boot.iso = boot/xen.gz + +[images-xen] +kernel = boot/pxelinux/mboot.c32 +initrd = boot/vmlinuz + diff --git a/tests/data/repo/XS-PACKAGES b/tests/data/repo/XS-PACKAGES new file mode 100644 index 00000000..e69de29b diff --git a/tests/data/repo/XS-REPOSITORY b/tests/data/repo/XS-REPOSITORY new file mode 100644 index 00000000..8213385d --- /dev/null +++ b/tests/data/repo/XS-REPOSITORY @@ -0,0 +1,14 @@ + + + + Description + + + + diff --git a/tests/data/repo/XS-REPOSITORY-LIST b/tests/data/repo/XS-REPOSITORY-LIST new file mode 100644 index 00000000..e69de29b diff --git a/tests/data/repo/repodata/repomd.xml b/tests/data/repo/repodata/repomd.xml new file mode 100644 index 00000000..43ef48d8 --- /dev/null +++ b/tests/data/repo/repodata/repomd.xml @@ -0,0 +1,55 @@ + + + 1645708616 + + f8454f26592a0669368825d673edc18a067d8a434f2d3ceaa03cdb45e72572e8 + 6f2d69e90edbe7b3eb83654995dc07c6e577d8fc2f9ee7ec7f4f206ed486218b + + 1645708616 + 208220 + 1449213 + + + 4e678f6ce4fc197faef2c75645d00c79ee8b70d3210aa2f692fe432ed223c695 + cea7a040a15876aba2238f3b8910cd48ded102ff0d4976df81903a3bc4ae8586 + + 1645708616 + 265582 + 2704673 + + + a19f12f68c74459bc9fc7887376408b4c8dc9e84876ebbf3301afec79f110a7b + 46a83a4100e33249a95462d7ed54cb71ee4a178db8f2346b83f1ba5f1a5d47dc + + 1645708616 + 204512 + 1054253 + + + 34a89554a4214fde5b473ddcf1d9be796801e8e8676cda860a3d032a7e8b10d8 + 1fc99d5403906556e8fc91d2121816354cac667741f1a04f51988b5cc3e01b98 + + 1645708616 + 419050 + 1754112 + 10 + + + f4c416c2cf253741749420300109cf8a046e0cdd27c269e69795a9ecc092669b + 1de3d29150b9da80b10c95b913c91369f3b7d03bb2ed57eedf511038e4537c50 + + 1645708616 + 323942 + 1380352 + 10 + + + 4a192c51fff556d2f1fdbb8f90b5454558c910a50674e65b7cdfacd5f791fdb5 + 23dbdc6ce9ece7f1d3ac3d7b31cd61b4019e96ef01903975516fd5e3083de32a + + 1645708616 + 243295 + 988160 + 10 + + diff --git a/tests/test_accessor.py b/tests/test_accessor.py new file mode 100644 index 00000000..ade787e6 --- /dev/null +++ b/tests/test_accessor.py @@ -0,0 +1,21 @@ +import unittest + +import xcp.accessor + +class TestAccessor(unittest.TestCase): + def test_http(self): + raise unittest.SkipTest("comment out if you really mean it") + a = xcp.accessor.createAccessor("https://updates.xcp-ng.org/netinstall/8.2.1", True) + a.start() + self.assertTrue(a.access('.treeinfo')) + self.assertFalse(a.access('no_such_file')) + self.assertEqual(a.lastError, 404) + a.finish() + + def test_file(self): + a = xcp.accessor.createAccessor("file://tests/data/repo/", True) + a.start() + self.assertTrue(a.access('.treeinfo')) + self.assertFalse(a.access('no_such_file')) + self.assertEqual(a.lastError, 404) + a.finish() diff --git a/tests/test_repository.py b/tests/test_repository.py new file mode 100644 index 00000000..833627d0 --- /dev/null +++ b/tests/test_repository.py @@ -0,0 +1,25 @@ +import unittest + +import xcp.accessor +from xcp import repository +from xcp.version import Version + +class TestRepository(unittest.TestCase): + def test_http(self): + raise unittest.SkipTest("comment out if you really mean it") + a = xcp.accessor.createAccessor("https://updates.xcp-ng.org/netinstall/8.2.1", True) + repo_ver = repository.BaseRepository.getRepoVer(a) + self.assertEqual(repo_ver, Version([3, 2, 1])) + product_ver = repository.BaseRepository.getProductVersion(a) + self.assertEqual(product_ver, Version([8, 2, 1])) + repos = repository.BaseRepository.findRepositories(a) + self.assertEqual(len(repos), 1) + + def test_file(self): + a = xcp.accessor.createAccessor("file://tests/data/repo/", True) + repo_ver = repository.BaseRepository.getRepoVer(a) + self.assertEqual(repo_ver, Version([3, 2, 1])) + product_ver = repository.BaseRepository.getProductVersion(a) + self.assertEqual(product_ver, Version([8, 2, 1])) + repos = repository.BaseRepository.findRepositories(a) + self.assertEqual(len(repos), 1) From c25da360f14deedd2fa77de8efdfe7396cb0e464 Mon Sep 17 00:00:00 2001 From: Yann Dirson Date: Wed, 27 Jul 2022 11:46:05 +0200 Subject: [PATCH 09/15] coverage: new test_environ: parse a sample "xensource-inventory" Coverage change: xcp.environ: 0% -> 80.0% total: 62.7% -> 63.3% Signed-off-by: Yann Dirson --- tests/data/inventory/etc/xensource-inventory | 23 ++++++++++++++++++++ tests/test_environ.py | 8 +++++++ 2 files changed, 31 insertions(+) create mode 100644 tests/data/inventory/etc/xensource-inventory create mode 100644 tests/test_environ.py diff --git a/tests/data/inventory/etc/xensource-inventory b/tests/data/inventory/etc/xensource-inventory new file mode 100644 index 00000000..950472c0 --- /dev/null +++ b/tests/data/inventory/etc/xensource-inventory @@ -0,0 +1,23 @@ +PRIMARY_DISK='/dev/disk/by-id/scsi-123456789012345678901234567899012' +PRODUCT_VERSION='8.2.1' +DOM0_VCPUS='16' +CONTROL_DOMAIN_UUID='3b1dccee-9da4-49c6-8250-e3725e0e4b2d' +DOM0_MEM='7584' +COMPANY_NAME_SHORT='Open Source' +MANAGEMENT_ADDRESS_TYPE='IPv4' +PARTITION_LAYOUT='ROOT,BACKUP,LOG,BOOT,SWAP,SR' +PRODUCT_VERSION_TEXT='8.2' +PRODUCT_BRAND='XCP-ng' +INSTALLATION_UUID='b8b63475-d28c-44d4-9d1e-c38298ad3a0d' +PRODUCT_VERSION_TEXT_SHORT='8.2' +BRAND_CONSOLE='XCP-ng Center' +PRODUCT_NAME='xenenterprise' +MANAGEMENT_INTERFACE='xapi1' +COMPANY_PRODUCT_BRAND='XCP-ng' +PLATFORM_VERSION='3.2.1' +BUILD_NUMBER='release/yangtze/master/58' +PLATFORM_NAME='XCP' +BRAND_CONSOLE_URL='https://xcp-ng.org' +BACKUP_PARTITION='/dev/disk/by-id/scsi-123456789012345678901234567899012-part2' +INSTALLATION_DATE='2021-08-03 16:39:24.077945' +COMPANY_NAME='Open Source' diff --git a/tests/test_environ.py b/tests/test_environ.py new file mode 100644 index 00000000..8b0da44e --- /dev/null +++ b/tests/test_environ.py @@ -0,0 +1,8 @@ +import unittest +import xcp.environ + +class TestEnviron(unittest.TestCase): + def test_read_inventory(self): + inventory = xcp.environ.readInventory(root="tests/data/inventory") + self.assertEqual(inventory["COMPANY_PRODUCT_BRAND"], 'XCP-ng') + self.assertEqual(inventory["COMPANY_NAME"], 'Open Source') From c364a314dc03b3e66b17c827953ed065b3ee03c5 Mon Sep 17 00:00:00 2001 From: Yann Dirson Date: Wed, 27 Jul 2022 15:13:22 +0200 Subject: [PATCH 10/15] tests: remove unused imports, direct test execution Direct test execution only brings us dependency on `sys` :) Signed-off-by: Yann Dirson --- tests/test_biosdevname.py | 6 +----- tests/test_cpio.py | 11 ++++------- tests/test_dom0.py | 9 ++------- tests/test_ifrename_dynamic.py | 9 ++------- tests/test_ifrename_logic.py | 9 +++------ tests/test_ifrename_static.py | 9 ++------- tests/test_mac.py | 7 +------ tests/test_pci.py | 8 +------- 8 files changed, 16 insertions(+), 52 deletions(-) diff --git a/tests/test_biosdevname.py b/tests/test_biosdevname.py index 07133bec..5616afb0 100644 --- a/tests/test_biosdevname.py +++ b/tests/test_biosdevname.py @@ -1,6 +1,5 @@ -#!/usr/bin/env python +import unittest -import unittest, sys, os, os.path as path from xcp.net.biosdevname import has_ppn_quirks class TestQuirks(unittest.TestCase): @@ -24,6 +23,3 @@ def test_ppn_true(self): {"SMBIOS Instance": 1} ])) - -if __name__ == "__main__": - sys.exit(unittest.main()) diff --git a/tests/test_cpio.py b/tests/test_cpio.py index 85ff62d8..16a4a890 100644 --- a/tests/test_cpio.py +++ b/tests/test_cpio.py @@ -1,10 +1,10 @@ -#!/usr/bin/env python - -import unittest, sys, os, os.path as path +import os +import shutil +import subprocess +import unittest import warnings from xcp.cpiofile import CpioFile, CpioInfo -import subprocess, shutil try: from hashlib import md5 @@ -115,6 +115,3 @@ def test_xz(self): raise unittest.SkipTest("lzma package or xz tool not available") print 'Running test for XZ' self.doArchive('archive.cpio.xz', 'xz') - -if __name__ == "__main__": - unittest.main() diff --git a/tests/test_dom0.py b/tests/test_dom0.py index 2fc1746d..bf198341 100644 --- a/tests/test_dom0.py +++ b/tests/test_dom0.py @@ -1,9 +1,7 @@ -#!/usr/bin/env python - -import unittest, sys, os +import unittest +from mock import patch, Mock from xcp.dom0 import default_memory, parse_mem, default_vcpus -from mock import patch, Mock class TestDom0(unittest.TestCase): @@ -112,6 +110,3 @@ def test_default_vcpus(self): for host_pcpus, mem, expected in test_values: calculated = default_vcpus(host_pcpus, mem) self.assertEqual(calculated, expected) - -if __name__ == "__main__": - sys.exit(unittest.main()) diff --git a/tests/test_ifrename_dynamic.py b/tests/test_ifrename_dynamic.py index 2048f762..1eb28a74 100644 --- a/tests/test_ifrename_dynamic.py +++ b/tests/test_ifrename_dynamic.py @@ -1,7 +1,6 @@ -#!/usr/bin/env python - -import unittest, sys, os, os.path as path, logging import json +import logging +import unittest from copy import deepcopy try: @@ -175,7 +174,3 @@ def test_one_ibft_lastboot(self): self.assertEqual(json.loads(dr.write(False)), {'lastboot': [], 'old': []}) - - -if __name__ == "__main__": - sys.exit(unittest.main()) diff --git a/tests/test_ifrename_logic.py b/tests/test_ifrename_logic.py index 326f8acb..9fa9255c 100644 --- a/tests/test_ifrename_logic.py +++ b/tests/test_ifrename_logic.py @@ -1,6 +1,6 @@ -#!/usr/bin/env python - -import unittest, sys, os, os.path as path, logging +import logging +import sys +import unittest from copy import deepcopy try: @@ -651,6 +651,3 @@ def test_oldstate_input(self): self.assertNotRaises(OldStateError, rename, [], [], [], [self.s123]) - -if __name__ == "__main__": - sys.exit(unittest.main()) diff --git a/tests/test_ifrename_static.py b/tests/test_ifrename_static.py index 8b72b607..16909142 100644 --- a/tests/test_ifrename_static.py +++ b/tests/test_ifrename_static.py @@ -1,6 +1,5 @@ -#!/usr/bin/env python - -import unittest, sys, os, os.path as path, logging +import logging +import unittest from copy import deepcopy try: @@ -473,7 +472,3 @@ def test_two_valid(self): ) self.assertEqual(sr.write(False), desired_result) - - -if __name__ == "__main__": - sys.exit(unittest.main()) diff --git a/tests/test_mac.py b/tests/test_mac.py index 77ef2e79..310e17e3 100644 --- a/tests/test_mac.py +++ b/tests/test_mac.py @@ -1,6 +1,4 @@ -#!/usr/bin/env python - -import unittest, sys, os, os.path as path +import unittest from xcp.net.mac import MAC @@ -235,6 +233,3 @@ def test_keys(self): self.assertEqual(d[m1], "zero") self.assertEqual(d[m2], "ascending") self.assertEqual(d[m3], "random") - -if __name__ == "__main__": - sys.exit(unittest.main()) diff --git a/tests/test_pci.py b/tests/test_pci.py index 59af4828..736b55de 100644 --- a/tests/test_pci.py +++ b/tests/test_pci.py @@ -1,7 +1,5 @@ -#!/usr/bin/env python - -import unittest, sys, os, os.path as path import subprocess +import unittest from mock import patch, Mock from xcp.pci import PCI, PCIIds, PCIDevices @@ -104,7 +102,3 @@ def tests_videoclass(self): self.assertEqual(ids.findDevice(video_dev['vendor'], video_dev['device']), device) self.assertEqual(len(devs.findRelatedFunctions('00:18.1')), 7) - - -if __name__ == "__main__": - sys.exit(unittest.main()) From 57a35b0cc2697012fe79daecc257093aa5cf5764 Mon Sep 17 00:00:00 2001 From: Yann Dirson Date: Wed, 27 Jul 2022 15:12:00 +0200 Subject: [PATCH 11/15] Coverage: test_biosdevname: add a test of all_devices_all_names() Coverage change: xcp.net.biosdevname: 41.5% to 87.8% total: 63.3% -> 63.8% Signed-off-by: Yann Dirson --- tests/data/all_ethN.biosdevname | 68 +++++++++++++++++++++++++++++++++ tests/data/physical.biosdevname | 68 +++++++++++++++++++++++++++++++++ tests/test_biosdevname.py | 25 +++++++++++- 3 files changed, 160 insertions(+), 1 deletion(-) create mode 100644 tests/data/all_ethN.biosdevname create mode 100644 tests/data/physical.biosdevname diff --git a/tests/data/all_ethN.biosdevname b/tests/data/all_ethN.biosdevname new file mode 100644 index 00000000..63bdc506 --- /dev/null +++ b/tests/data/all_ethN.biosdevname @@ -0,0 +1,68 @@ +BIOS device: eth0 +Kernel name: eth0 +Permanent MAC: EC:F4:BB:C3:AF:A8 +Assigned MAC : EC:F4:BB:C3:AF:A8 +Driver: ixgbe +Driver version: 5.9.4 +Firmware version: 0x8000095c, 19.5.12 +Bus Info: 0000:01:00.0 +PCI name : 0000:01:00.0 +PCI Slot : embedded +SMBIOS Device Type: Ethernet +SMBIOS Instance: 1 +SMBIOS Label: Integrated NIC 1 +sysfs Label: NIC1 +Embedded Index: 1 + +BIOS device: eth1 +Kernel name: eth1 +Permanent MAC: EC:F4:BB:C3:AF:AA +Assigned MAC : EC:F4:BB:C3:AF:AA +Driver: ixgbe +Driver version: 5.9.4 +Firmware version: 0x8000095c, 19.5.12 +Bus Info: 0000:01:00.1 +PCI name : 0000:01:00.1 +PCI Slot : embedded +SMBIOS Device Type: Ethernet +SMBIOS Instance: 2 +SMBIOS Label: Integrated NIC 2 +sysfs Label: NIC2 +Embedded Index: 2 + +BIOS device: eth2 +Kernel name: eth2 +Permanent MAC: EC:F4:BB:C3:AF:AC +Assigned MAC : EC:F4:BB:C3:AF:AC +Driver: igb +Driver version: 5.3.5.20 +Firmware version: 1.67, 0x80000fab, 19.5.12 +Bus Info: 0000:07:00.0 +PCI name : 0000:07:00.0 +PCI Slot : embedded +SMBIOS Device Type: Ethernet +SMBIOS Instance: 3 +SMBIOS Label: Integrated NIC 3 +sysfs Label: NIC3 +VPD Port: 3 +VPD Index: 1 +VPD PCI master: 0000:07:00.0 + +BIOS device: eth3 +Kernel name: eth3 +Permanent MAC: EC:F4:BB:C3:AF:AD +Assigned MAC : EC:F4:BB:C3:AF:AD +Driver: igb +Driver version: 5.3.5.20 +Firmware version: 1.67, 0x80000fab, 19.5.12 +Bus Info: 0000:07:00.1 +PCI name : 0000:07:00.1 +PCI Slot : embedded +SMBIOS Device Type: Ethernet +SMBIOS Instance: 4 +SMBIOS Label: Integrated NIC 4 +sysfs Label: NIC4 +VPD Port: 4 +VPD Index: 1 +VPD PCI master: 0000:07:00.1 + diff --git a/tests/data/physical.biosdevname b/tests/data/physical.biosdevname new file mode 100644 index 00000000..3e0611d7 --- /dev/null +++ b/tests/data/physical.biosdevname @@ -0,0 +1,68 @@ +BIOS device: em1 +Kernel name: eth0 +Permanent MAC: EC:F4:BB:C3:AF:A8 +Assigned MAC : EC:F4:BB:C3:AF:A8 +Driver: ixgbe +Driver version: 5.9.4 +Firmware version: 0x8000095c, 19.5.12 +Bus Info: 0000:01:00.0 +PCI name : 0000:01:00.0 +PCI Slot : embedded +SMBIOS Device Type: Ethernet +SMBIOS Instance: 1 +SMBIOS Label: Integrated NIC 1 +sysfs Label: NIC1 +Embedded Index: 1 + +BIOS device: em2 +Kernel name: eth1 +Permanent MAC: EC:F4:BB:C3:AF:AA +Assigned MAC : EC:F4:BB:C3:AF:AA +Driver: ixgbe +Driver version: 5.9.4 +Firmware version: 0x8000095c, 19.5.12 +Bus Info: 0000:01:00.1 +PCI name : 0000:01:00.1 +PCI Slot : embedded +SMBIOS Device Type: Ethernet +SMBIOS Instance: 2 +SMBIOS Label: Integrated NIC 2 +sysfs Label: NIC2 +Embedded Index: 2 + +BIOS device: em3_1 +Kernel name: eth2 +Permanent MAC: EC:F4:BB:C3:AF:AC +Assigned MAC : EC:F4:BB:C3:AF:AC +Driver: igb +Driver version: 5.3.5.20 +Firmware version: 1.67, 0x80000fab, 19.5.12 +Bus Info: 0000:07:00.0 +PCI name : 0000:07:00.0 +PCI Slot : embedded +SMBIOS Device Type: Ethernet +SMBIOS Instance: 3 +SMBIOS Label: Integrated NIC 3 +sysfs Label: NIC3 +VPD Port: 3 +VPD Index: 1 +VPD PCI master: 0000:07:00.0 + +BIOS device: em4_1 +Kernel name: eth3 +Permanent MAC: EC:F4:BB:C3:AF:AD +Assigned MAC : EC:F4:BB:C3:AF:AD +Driver: igb +Driver version: 5.3.5.20 +Firmware version: 1.67, 0x80000fab, 19.5.12 +Bus Info: 0000:07:00.1 +PCI name : 0000:07:00.1 +PCI Slot : embedded +SMBIOS Device Type: Ethernet +SMBIOS Instance: 4 +SMBIOS Label: Integrated NIC 4 +sysfs Label: NIC4 +VPD Port: 4 +VPD Index: 1 +VPD PCI master: 0000:07:00.1 + diff --git a/tests/test_biosdevname.py b/tests/test_biosdevname.py index 5616afb0..75a3a266 100644 --- a/tests/test_biosdevname.py +++ b/tests/test_biosdevname.py @@ -1,6 +1,7 @@ import unittest +from mock import patch, Mock -from xcp.net.biosdevname import has_ppn_quirks +from xcp.net.biosdevname import has_ppn_quirks, all_devices_all_names class TestQuirks(unittest.TestCase): @@ -23,3 +24,25 @@ def test_ppn_true(self): {"SMBIOS Instance": 1} ])) +class TestDeviceNames(unittest.TestCase): + def test(self): + with patch("xcp.net.biosdevname.Popen") as popen_mock: + with open("tests/data/physical.biosdevname") as f: + fake_output_1 = f.read() + with open("tests/data/all_ethN.biosdevname") as f: + fake_output_2 = f.read() + communicate_mock = Mock(side_effect=iter([(fake_output_1, ""), + (fake_output_2, "")])) + popen_mock.return_value.communicate = communicate_mock + popen_mock.return_value.returncode = 0 + + devices = all_devices_all_names() + + # check after the fact that we mocked the proper calls + self.assertEqual(popen_mock.call_count, 2) + calls = popen_mock.call_args_list + self.assertEqual(calls[0].args[0], ['/sbin/biosdevname', '--policy', 'physical', '-d']) + self.assertEqual(calls[1].args[0], ['/sbin/biosdevname', '--policy', 'all_ethN', '-d']) + + self.assertEqual(devices['eth0']['BIOS device'], + {'all_ethN': 'eth0', 'physical': 'em1'}) From 1f19cc9695d03118cebb5cef87ef5382ad66275f Mon Sep 17 00:00:00 2001 From: Yann Dirson Date: Wed, 27 Jul 2022 15:25:00 +0200 Subject: [PATCH 12/15] xcp.net.biosname: remove dead code This function is private, not called, and if called would not do what the docstring says (does not pass "eth" as argument to the command called). Coverage change: xcp.net.biosdevname: 87.8% -> 94.6% total: 63.8% -> 63.9% Signed-off-by: Yann Dirson --- xcp/net/biosdevname.py | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/xcp/net/biosdevname.py b/xcp/net/biosdevname.py index e8166807..78995056 100644 --- a/xcp/net/biosdevname.py +++ b/xcp/net/biosdevname.py @@ -35,19 +35,6 @@ __ALL_POLICIES = [ "physical", "all_ethN" ] -def __run_single_device(eth, policy = "physical"): - """ - Run 'biosdevname -i eth' for a specified policy. - Return (stdout, stderr, returncode) tuple. - """ - - proc = Popen(["/sbin/biosdevname", "--policy", policy, - "-i"], stdout=PIPE, stderr=PIPE) - - stdout, stderr = proc.communicate() - - return ( stdout, stderr, proc.returncode ) - def __run_all_devices(policy = "physical"): """ Run 'biosdevname -d' for a specified policy. From 935f9324572e7c02fb2e06b54d38d8c42e55ceb1 Mon Sep 17 00:00:00 2001 From: Yann Dirson Date: Fri, 29 Jul 2022 15:06:34 +0200 Subject: [PATCH 13/15] Coverage: test_cpio: add a test for CpioFileCompat Coverage change: xcp.cpiofile: 58.1% -> 62.1% total: 63.9% -> 65.2% Signed-off-by: Yann Dirson --- tests/test_cpio.py | 42 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/tests/test_cpio.py b/tests/test_cpio.py index 16a4a890..84763b26 100644 --- a/tests/test_cpio.py +++ b/tests/test_cpio.py @@ -4,7 +4,7 @@ import unittest import warnings -from xcp.cpiofile import CpioFile, CpioInfo +from xcp.cpiofile import CpioFile, CpioInfo, CpioFileCompat, CPIO_PLAIN, CPIO_GZIPPED try: from hashlib import md5 @@ -115,3 +115,43 @@ def test_xz(self): raise unittest.SkipTest("lzma package or xz tool not available") print 'Running test for XZ' self.doArchive('archive.cpio.xz', 'xz') + + # CpioFileCompat testing + + def archiveExtractCompat(self, fn, comp): + arc = CpioFileCompat(fn, mode="r", compression={"": CPIO_PLAIN, + "gz": CPIO_GZIPPED}[comp]) + found = False + for f in arc.namelist(): + info = arc.getinfo(f) + if info.isfile(): + data = arc.read(f) + self.assertEqual(len(data), info.size) + self.assertEqual(self.md5data, md5(data).hexdigest()) + found = True + arc.close() + self.assertTrue(found) + + def archiveCreateCompat(self, fn, comp): + if os.path.exists(fn): + os.unlink(fn) + arc = CpioFileCompat(fn, mode="w", compression={"": CPIO_PLAIN, + "gz": CPIO_GZIPPED}[comp]) + arc.write('archive/data') + arc.close() + self.archiveExtract(fn) + + def doArchiveCompat(self, fn, fmt): + self.archiveExtractCompat(fn, fmt) + + fn2 = "archive2" + fn[len("archive"):] + self.archiveCreateCompat(fn2, fmt) + self.archiveExtractCompat(fn2, fmt) + + def test_compat_plain(self): + self.doArchiveCompat('archive.cpio', '') + + def test_compat_gz(self): + # FIXME: this test exhibits "unclosed file" warnings when run + # under `-Wd` + self.doArchiveCompat('archive.cpio.gz', 'gz') From 8244c49d424fa897895f72ba6f662080a70d9372 Mon Sep 17 00:00:00 2001 From: Yann Dirson Date: Tue, 9 Aug 2022 11:59:52 +0200 Subject: [PATCH 14/15] Coverage: add a test for Bootloader.newDefault Coverage change: xcp.bootloader: 72.8% -> 81.9% total: 65.4% -> 66.6% Signed-off-by: Yann Dirson --- tests/data/grub-linux.cfg | 5 +++++ tests/test_bootloader.py | 22 +++++++++++++++++++++- 2 files changed, 26 insertions(+), 1 deletion(-) create mode 100644 tests/data/grub-linux.cfg diff --git a/tests/data/grub-linux.cfg b/tests/data/grub-linux.cfg new file mode 100644 index 00000000..e98e5d19 --- /dev/null +++ b/tests/data/grub-linux.cfg @@ -0,0 +1,5 @@ +set default=0 +menuentry 'linux' { + linux /boot/vmlinuz-1 ro + initrd /boot/initrd.img-1 +} diff --git a/tests/test_bootloader.py b/tests/test_bootloader.py index ca48204e..2e5700b5 100644 --- a/tests/test_bootloader.py +++ b/tests/test_bootloader.py @@ -1,7 +1,8 @@ import unittest import os +import shutil import subprocess -from tempfile import NamedTemporaryFile +from tempfile import NamedTemporaryFile, mkdtemp from xcp.bootloader import Bootloader @@ -21,6 +22,25 @@ def test_grub2(self): proc.stdout.close() proc.wait() +class TestLinuxBootloader(unittest.TestCase): + def setUp(self): + self.tmpdir = mkdtemp(prefix="testbl") + bootdir = os.path.join(self.tmpdir, "boot") + grubdir = os.path.join(bootdir, "grub") + os.makedirs(grubdir) + shutil.copyfile("tests/data/grub-linux.cfg", os.path.join(grubdir, "grub.cfg")) + with open(os.path.join(bootdir, "vmlinuz-1"), "w"): + pass + with open(os.path.join(bootdir, "vmlinuz-2"), "w"): + pass + with open(os.path.join(bootdir, "initrd.img-1"), "w"): + pass + with open(os.path.join(bootdir, "initrd.img-2"), "w"): + pass + def tearDown(self): + shutil.rmtree(self.tmpdir) + def test_grub2_newdefault(self): + Bootloader.newDefault("/boot/vmlinuz-2", "/boot/initrd.img-2", root=self.tmpdir) class TestBootloaderAdHoc(unittest.TestCase): def setUp(self): From 056238e032dadbe448d884525517e384d2305cf6 Mon Sep 17 00:00:00 2001 From: Yann Dirson Date: Wed, 20 Jul 2022 15:59:46 +0200 Subject: [PATCH 15/15] cpiofile: flag known issue in apparently-unused code Signed-off-by: Yann Dirson --- xcp/cpiofile.py | 1 + 1 file changed, 1 insertion(+) diff --git a/xcp/cpiofile.py b/xcp/cpiofile.py index d6163849..a490aeff 100755 --- a/xcp/cpiofile.py +++ b/xcp/cpiofile.py @@ -1158,6 +1158,7 @@ def xzopen(cls, name, mode="r", fileobj=None, compresslevel=6): if fileobj is not None: fileobj = _XZProxy(fileobj, mode) else: + # FIXME: not compatible with python3 API fileobj = lzma.LZMAFile(name, mode, options={'level': compresslevel, 'dict_size': 20 }) try: