diff --git a/tests/dd_tests/dd_test.py b/tests/dd_tests/dd_test.py new file mode 100644 index 000000000000..a254eba3881c --- /dev/null +++ b/tests/dd_tests/dd_test.py @@ -0,0 +1,252 @@ +#!/usr/bin/python +# unit tests for driver disk utilities (utils/dd) + +import os +import shutil +import unittest +import tempfile +import subprocess + +from contextlib import contextmanager +from collections import namedtuple +from rpmfluff import SourceFile, SimpleRpmBuild, expectedArch + +TOP_SRCDIR = os.environ.get("top_srcdir", "../..") +UTILDIR = os.path.join(TOP_SRCDIR, "utils/dd") + +# helpers for calling the utilities +Driver = namedtuple("Driver", "source name flags description") +def dd_list(dd_path, kernel_ver, anaconda_ver): + out = subprocess.check_output([os.path.join(UTILDIR, "dd_list"), + '-d', dd_path, '-k', kernel_ver, '-a', anaconda_ver], + stderr=open('/dev/null')) + return [Driver(*d.split('\n',3)) for d in out.split('\n---\n')[:-1]] + +def dd_extract(rpm_path, outdir, kernel_ver, flags='-blmf'): + out = subprocess.check_output([os.path.join(UTILDIR, "dd_extract"), + flags, '-r', rpm_path, '-d', outdir, '-k', kernel_ver], + stderr=subprocess.STDOUT) + return out + +# helpers for creating RPMs to test with +@contextmanager +def in_tempdir(prefix='tmp'): + oldcwd = os.getcwd() + tmpdir = tempfile.mkdtemp(prefix=prefix) + os.chdir(tmpdir) + yield + os.chdir(oldcwd) + shutil.rmtree(tmpdir) + +def make_rpm(outdir, name='test', version='1.0', release='1', arch=None, + for_anaconda_ver=None, for_kernel_ver=None, + payload=None): + p = SimpleRpmBuild(name, version, release) + if for_anaconda_ver: + p.add_provides('installer-enhancement = %s' % for_anaconda_ver) + if for_kernel_ver: + p.add_provides('kernel-modules >= %s' % for_kernel_ver) + if payload is None: + payload = [] + for item in payload: + p.add_installed_file(item.path, + SourceFile(item.srcpath, item.contents), + **item.kwargs) + with in_tempdir("anaconda-test-dd."): + p.make() + rpmfile = p.get_built_rpm(arch or expectedArch) + outfile = os.path.join(outdir, os.path.basename(rpmfile)) + shutil.move(rpmfile, outfile) + return p + +class RPMFile(object): + """simple container object for information about RPM payloads""" + def __init__(self, path, srcpath=None, contents='', **kwargs): + self.path = path + self.srcpath = srcpath or os.path.basename(path) + self.contents = contents + self.kwargs = kwargs + +binfile = RPMFile( + path="/usr/bin/fun", + contents="#!/bin/sh\necho WHEE\n", + mode="0755" +) +libfile = RPMFile( + path="/usr/lib/fun.so", + contents="I AM TOTALLY A SHARED LIBRARY", + mode="0755" +) +fwfile = RPMFile( + path="/lib/firmware/fun.fw", + contents="HELLO I AM FIRMWARE" +) +kofile = RPMFile( + path="/lib/modules/KERNELVER/extra/net/fun.ko", + contents="KERNEL MODULE??? YOU BETCHA" +) + +# Finally, the actual test cases +class ASelfTestCase(unittest.TestCase): + def setUp(self): + self.tmpdir = tempfile.mkdtemp(prefix="dd_tests.") + + def tearDown(self): + shutil.rmtree(self.tmpdir) + + def test_rpmfluff_simple(self): + """check if rpmfluff is working""" + p = make_rpm(outdir=self.tmpdir) + rpmfile = os.path.basename(p.get_built_rpm(expectedArch)) + self.assertTrue(rpmfile in os.listdir(self.tmpdir)) + + def test_rpmfluff_payload(self): + """check if rpmfluff can add files to built RPMs""" + p = make_rpm(outdir=self.tmpdir, payload=(binfile, kofile)) + rpmfile = os.path.basename(p.get_built_rpm(expectedArch)) + self.assertTrue(rpmfile in os.listdir(self.tmpdir)) + + def test_utils_exist(self): + """check that the dd utilities exist""" + self.assertTrue("dd_list" in os.listdir(UTILDIR)) + self.assertTrue("dd_extract" in os.listdir(UTILDIR)) + +class DD_List_TestCase(unittest.TestCase): + def setUp(self): + self.tmpdir = tempfile.mkdtemp(prefix="dd_tests.") + self.k_ver = "4.1.4-333" + self.a_ver = "22.0" + + def dd_list(self, dd_dir=None, kernel_ver=None, anaconda_ver=None): + return dd_list(dd_dir or self.tmpdir, + kernel_ver or self.k_ver, + anaconda_ver or self.a_ver) + + def tearDown(self): + shutil.rmtree(self.tmpdir) + + def test_dd_list(self): + """dd_list: check output format""" + rpm = make_rpm(self.tmpdir, for_kernel_ver=self.k_ver) + drivers = self.dd_list() + self.assertEqual(len(drivers), 1) + d = drivers[0] + self.assertEqual(d.name, rpm.name) + self.assertEqual(d.description, rpm.basePackage.description) + self.assertTrue(d.description) + self.assertTrue(os.path.exists(d.source)) + self.assertIn("modules", d.flags) + self.assertIn("firmwares", d.flags) + self.assertNotIn("binaries", d.flags) + self.assertNotIn("libraries", d.flags) + + def test_dd_list_multiple(self): + """dd_list: multiple outputs for multiple packages""" + names = ['fun', 'even_more_fun', 'too_much_fun'] + for name in names: + make_rpm(self.tmpdir, name=name, for_kernel_ver=self.k_ver) + drivers = self.dd_list() + self.assertEqual(len(drivers), len(names)) + self.assertEqual(set(d.name for d in drivers), set(names)) + + def test_dd_list_binaries(self): + """dd_list: 'Provides:installer-enhancement' implies bins/libs""" + make_rpm(self.tmpdir, for_anaconda_ver=self.a_ver) + drivers = self.dd_list() + self.assertFalse(drivers == []) + d = drivers[0] + self.assertIn("binaries", d.flags) + self.assertIn("libraries", d.flags) + self.assertNotIn("modules", d.flags) + self.assertNotIn("firmwares", d.flags) + + def test_dd_list_old_kmods(self): + """dd_list: ignore kmods if our kernel is too old""" + make_rpm(self.tmpdir, for_kernel_ver="5.0.1-555") + self.assertEqual(self.dd_list(), []) + + def test_dd_list_z_stream_kmods(self): + """dd_list: accept kmods for z-stream kernels (#1207831)""" + make_rpm(self.tmpdir, for_kernel_ver=self.k_ver) + drivers = self.dd_list(kernel_ver=self.k_ver+".3") + self.assertNotEqual(drivers, []) + d = drivers[0] + self.assertIn("modules", d.flags) + + def test_dd_list_anaconda_old(self): + """dd_list: ignore installer-enhancements if version doesn't match""" + make_rpm(self.tmpdir, for_anaconda_ver="23.0") + self.assertEqual(self.dd_list(), []) + + def test_dd_list_no_rpms(self): + """dd_list: empty directory returns no results""" + self.assertEqual(self.dd_list(), []) + + def test_dd_list_missing_dir(self): + """dd_list: missing directory returns no results""" + self.assertEqual(self.dd_list(dd_dir="/non/existent/path"), []) + +def listfiles(dirname): + return set(os.path.join(root,f) + for root, dirs, files in os.walk(dirname) + for f in files) + +class DD_Extract_TestCase(unittest.TestCase): + @classmethod + def setUpClass(self): + self.k_ver = "4.1.4-333" + self.a_ver = "22.0" + self.tmpdir = tempfile.mkdtemp(prefix="dd_tests.") + self.rpmpayload = (binfile, kofile, fwfile, libfile) + make_rpm(self.tmpdir, payload=self.rpmpayload) + (self.rpmfile,) = listfiles(self.tmpdir) + + def setUp(self): + self.outdir = os.path.join(self.tmpdir, "outdir") + os.mkdir(self.outdir) + + def dd_extract(self, flags='-bmlf'): + dd_extract(self.rpmfile, self.outdir, self.k_ver, flags=flags) + return listfiles(self.outdir) + + def tearDown(self): + shutil.rmtree(self.outdir) + + @classmethod + def tearDownClass(self): + shutil.rmtree(self.tmpdir) + + def test_dd_extract(self): + """dd_extract: files are extracted correctly""" + self.dd_extract() + for f in self.rpmpayload: + self.assertEqual(f.contents, open(self.outdir+f.path).read()) + + def test_dd_extract_chmod(self): + """dd_extract: files get correct mode (#1222056)""" + self.dd_extract() + for f in self.rpmpayload: + if 'mode' in f.kwargs: + binmode = os.stat(self.outdir+f.path).st_mode + expectmode = int(f.kwargs['mode'],8) + self.assertEqual(binmode & expectmode, expectmode) + + def test_dd_extract_modules(self): + """dd_extract: using --modules extracts only .ko files""" + outfiles = self.dd_extract(flags='--modules') + self.assertEqual(outfiles, set([self.outdir+kofile.path])) + + def test_dd_extract_binaries(self): + """dd_extract: using --binaries extracts only /bin, /sbin, etc.""" + outfiles = self.dd_extract(flags='--binaries') + self.assertEqual(outfiles, set([self.outdir+binfile.path])) + + def test_dd_extract_libs(self): + """dd_extract: using --libraries extracts only /lib etc.""" + outfiles = self.dd_extract(flags='--libraries') + self.assertEqual(outfiles, set([self.outdir+libfile.path])) + + def test_dd_extract_firmware(self): + """dd_extract: using --firmwares extracts only /lib/firmware""" + outfiles = self.dd_extract(flags='--firmwares') + self.assertEqual(outfiles, set([self.outdir+fwfile.path]))