class ReusingTestCaseComponent(TestCaseComponent): """A version of TestCaseComponent that reuses existing disk images rather than create its own. It will, however, delete these disk images after use. This class knows which disk images to reuse by the reusedComponents parameter passed in at object instantiation. This is a list of other TestCaseComponent instances. This class will reuse all disk images from each instance in that list, in the order given. A typical pipeline of components would thus look like this: class ComponentA(ReusableTestCaseComponent): ... class ComponentB(ReusingTestCaseComponent): ... ComponentA -> ComponentB A component may also derive from both, if it's in the middle of the pipeline: class ComponentA(ReusableTestCaseComponent): ... class ComponentB(ReusableTestCaseComponent, ReusingTestCaseComponent): ... class ComponentC(ReusingTestCaseComponent): ... ComponentA -> ComponentB -> ComponentC """ def __init__(self, reusedComponents=None): """Create a new ReusingTestCaseComponent. reusedComponents is a list of other TestCaseComponent objects that this instance should make use of. All disk images in that list will be used by this instance, and all will be cleaned up at the end of the test. """ TestCaseComponent.__init__(self) if reusedComponents is None: self._reusedComponents = [] else: self._reusedComponents = reusedComponents def setupDisks(self, ksdata): self._storage = InstallerStorage() for component in self._reusedComponents: self._disks.update(component._disks) for (name, image) in self._disks.items(): self._storage.disk_images[name] = image self._storage.reset()
class setupDiskImagesNonZeroSizeTestCase(unittest.TestCase): """ Test if size of disk images is > 0. Related: rhbz#1252703. This test emulates how anaconda configures its storage. """ disks = {"disk1": Size("2 GiB")} def setUp(self): self.storage = InstallerStorage() # anaconda first configures disk images for (name, size) in iter(self.disks.items()): path = util.create_sparse_tempfile(name, size) self.storage.disk_images[name] = path # at this point the DMLinearDevice has correct size self.storage.setup_disk_images() # emulates setting the anaconda flags which later update flags.image_install = True # no kickstart available ksdata = kickstart.AnacondaKSHandler([]) # anaconda calls storage_initialize regardless of whether or not # this is an image install. Somewhere along the line this will # execute setup_disk_images() once more and the DMLinearDevice created # in this second execution has size 0 with patch('blivet.flags'): storage_initialize(self.storage, ksdata, []) def tearDown(self): self.storage.reset() self.storage.devicetree.teardown_disk_images() for fn in self.storage.disk_images.values(): if os.path.exists(fn): os.unlink(fn) flags.image_install = False def runTest(self): disk = self.storage.disks[0] self.assertEqual(disk.name, list(self.disks.keys())[0]) for d in self.storage.devicetree.devices: if d == disk or disk.depends_on(d): self.assertTrue(d.size > 0)
class setupDiskImagesNonZeroSizeTestCase(unittest.TestCase): """ Test if size of disk images is > 0. Related: rhbz#1252703. This test emulates how anaconda configures its storage. """ disks = {"disk1": Size("2 GiB")} def setUp(self): self.storage = InstallerStorage() # anaconda first configures disk images for (name, size) in iter(self.disks.items()): path = util.create_sparse_tempfile(name, size) self.storage.disk_images[name] = path # at this point the DMLinearDevice has correct size self.storage.setup_disk_images() # no kickstart available ksdata = kickstart.AnacondaKSHandler([]) # anaconda calls storage_initialize regardless of whether or not # this is an image install. Somewhere along the line this will # execute setup_disk_images() once more and the DMLinearDevice created # in this second execution has size 0 with patch('blivet.flags'): storage_initialize(self.storage, ksdata, []) def tearDown(self): self.storage.reset() self.storage.devicetree.teardown_disk_images() for fn in self.storage.disk_images.values(): if os.path.exists(fn): os.unlink(fn) def runTest(self): disk = self.storage.disks[0] self.assertEqual(disk.name, list(self.disks.keys())[0]) for d in self.storage.devicetree.devices: if d == disk or disk.depends_on(d): self.assertTrue(d.size > 0)
class TestCaseComponent(object): """A TestCaseComponent is one individual test that runs as part of a TestCase. It consists of a set of disk images provided by self.disksToCreate, a kickstart storage snippet provided by self.ks, and an expected error condition provided by self.expectedExceptionType and self.expectedExceptionText. If the TestCaseComponent is expected to succeed, these latter two should just be set to None. A TestCaseComponent succeeds in the following cases: * No exception is encountered, and self.expectedExceptionType is None. A TestCaseComponent fails in all other cases. Class attributes: name -- An identifying string given to this TestCaseComponent. """ name = "" def __init__(self): """Create a new TestCaseComponent instance. This __init__ method should typically do very little. However, subclasses must be sure to set self.disksToCreate. This attribute is a list of (disk name, blivet.Size) tuples that will be used in this test. Disks given in this list will be automatically created by setupDisks and destroyed by tearDownDisks. """ self._disks = {} self._storage = None self.disksToCreate = [] @property def ks(self): """Return the storage-specific portion of a kickstart file used for performing this test. The kickstart snippet must be provided as text. Only storage commands will be tested. No bootloader actions will be performed. """ return "" def setupDisks(self, ksdata): """Create all disk images given by self.disksToCreate and initialize the storage module. Subclasses may override this method, but they should be sure to call the base method as well. """ self._storage = InstallerStorage() for (name, size) in self.disksToCreate: self._disks[name] = blivet.util.create_sparse_tempfile(name, size) self._storage.disk_images[name] = self._disks[name] self._storage.reset() def tearDownDisks(self): """Disable any disk images used by this test component and remove their image files from the host system. Subclasses may override this method, but they should call the base method as well to make sure the images get destroyed. """ # pylint: disable=undefined-variable self._storage.devicetree.teardown_disk_images() for d in self._disks.values(): os.unlink(d) @property def expectedExceptionType(self): """Should this test component be expected to fail, this property returns the exception type. If this component is not expected to fail, this property returns None. All components that are expected to fail should override this property. """ return None @property def expectedExceptionText(self): """Should this test component be expected to fail, this property returns a regular expression that the raised exception's text must match. Otherwise, this property returns None. All components that are expected to fail should override this property. """ return None def _text_matches(self, s): prog = re.compile(self.expectedExceptionText) for line in s.splitlines(): match = prog.match(line) if match: return match return None def _run(self): # Set up disks/blivet. try: # Parse the kickstart using anaconda's parser, since it has more # advanced error detection. This also requires having storage set # up first. parser = AnacondaKSParser(AnacondaKSHandler()) parser.readKickstartFromString(self.ks) self.setupDisks(parser.handler) configure_storage(self._storage, parser.handler) self._storage.devicetree.teardown_all() self._storage.do_it() except (StorageConfigurationError, BootloaderConfigurationError) as e: # anaconda handles expected kickstart errors (like parsing busted # input files) by printing the error and quitting. For testing, an # error might be expected so we should compare the result here with # what is expected. if self.expectedExceptionType and isinstance( e, self.expectedExceptionType): # We expected an exception, and we got one of the correct type. # If it also contains the string we were expecting, then the # test case passes. Otherwise, it's a failure. if self.expectedExceptionText and self._text_matches(str(e)): return else: raise FailedTest(str(e), self.expectedExceptionText) else: # We either got an exception when we were not expecting one, # or we got one of a type other than what we were expecting. # Either of these cases indicates a failure of the test case. raise FailedTest(e, self.expectedExceptionType) finally: self.tearDownDisks() if self.expectedExceptionType: raise FailedTest(None, self.expectedExceptionType)
class TestCaseComponent(object): """A TestCaseComponent is one individual test that runs as part of a TestCase. It consists of a set of disk images provided by self.disksToCreate, a kickstart storage snippet provided by self.ks, and an expected error condition provided by self.expectedExceptionType and self.expectedExceptionText. If the TestCaseComponent is expected to succeed, these latter two should just be set to None. A TestCaseComponent succeeds in the following cases: * No exception is encountered, and self.expectedExceptionType is None. A TestCaseComponent fails in all other cases. Class attributes: name -- An identifying string given to this TestCaseComponent. """ name = "" def __init__(self): """Create a new TestCaseComponent instance. This __init__ method should typically do very little. However, subclasses must be sure to set self.disksToCreate. This attribute is a list of (disk name, blivet.Size) tuples that will be used in this test. Disks given in this list will be automatically created by setupDisks and destroyed by tearDownDisks. """ self._disks = {} self._storage = None self.disksToCreate = [] @property def ks(self): """Return the storage-specific portion of a kickstart file used for performing this test. The kickstart snippet must be provided as text. Only storage commands will be tested. No bootloader actions will be performed. """ return "" def setupDisks(self, ksdata): """Create all disk images given by self.disksToCreate and initialize the storage module. Subclasses may override this method, but they should be sure to call the base method as well. """ self._storage = InstallerStorage() # blivet only sets up the bootloader in installer_mode. We don't # want installer_mode, though, because that involves running lots # of programs on the host and setting up all sorts of other things. # Thus, we set it up manually. from pyanaconda.bootloader import get_bootloader self._storage._bootloader = get_bootloader() for (name, size) in self.disksToCreate: self._disks[name] = blivet.util.create_sparse_tempfile(name, size) self._storage.disk_images[name] = self._disks[name] self._storage.reset() def tearDownDisks(self): """Disable any disk images used by this test component and remove their image files from the host system. Subclasses may override this method, but they should call the base method as well to make sure the images get destroyed. """ # pylint: disable=undefined-variable self._storage.devicetree.teardown_disk_images() for d in self._disks.values(): os.unlink(d) @property def expectedExceptionType(self): """Should this test component be expected to fail, this property returns the exception type. If this component is not expected to fail, this property returns None. All components that are expected to fail should override this property. """ return None @property def expectedExceptionText(self): """Should this test component be expected to fail, this property returns a regular expression that the raised exception's text must match. Otherwise, this property returns None. All components that are expected to fail should override this property. """ return None def _text_matches(self, s): prog = re.compile(self.expectedExceptionText) for line in s.splitlines(): match = prog.match(line) if match: return match return None def _run(self): # Set up disks/blivet. try: # Parse the kickstart using anaconda's parser, since it has more # advanced error detection. This also requires having storage set # up first. parser = AnacondaKSParser(AnacondaKSHandler()) parser.readKickstartFromString(self.ks) self.setupDisks(parser.handler) configure_storage(self._storage, parser.handler) update_storage_ksdata(self._storage, parser.handler) self._storage.devicetree.teardown_all() self._storage.do_it() except (StorageConfigurationError, BootloaderConfigurationError) as e: # anaconda handles expected kickstart errors (like parsing busted # input files) by printing the error and quitting. For testing, an # error might be expected so we should compare the result here with # what is expected. if self.expectedExceptionType and isinstance(e, self.expectedExceptionType): # We expected an exception, and we got one of the correct type. # If it also contains the string we were expecting, then the # test case passes. Otherwise, it's a failure. if self.expectedExceptionText and self._text_matches(str(e)): return else: raise FailedTest(str(e), self.expectedExceptionText) else: # We either got an exception when we were not expecting one, # or we got one of a type other than what we were expecting. # Either of these cases indicates a failure of the test case. raise FailedTest(e, self.expectedExceptionType) finally: self.tearDownDisks() if self.expectedExceptionType: raise FailedTest(None, self.expectedExceptionType)
class ReusingTestCaseComponent(TestCaseComponent): """A version of TestCaseComponent that reuses existing disk images rather than create its own. It will, however, delete these disk images after use. This class knows which disk images to reuse by the reusedComponents parameter passed in at object instantiation. This is a list of other TestCaseComponent instances. This class will reuse all disk images from each instance in that list, in the order given. A typical pipeline of components would thus look like this: class ComponentA(ReusableTestCaseComponent): ... class ComponentB(ReusingTestCaseComponent): ... ComponentA -> ComponentB A component may also derive from both, if it's in the middle of the pipeline: class ComponentA(ReusableTestCaseComponent): ... class ComponentB(ReusableTestCaseComponent, ReusingTestCaseComponent): ... class ComponentC(ReusingTestCaseComponent): ... ComponentA -> ComponentB -> ComponentC """ def __init__(self, reusedComponents=None): """Create a new ReusingTestCaseComponent. reusedComponents is a list of other TestCaseComponent objects that this instance should make use of. All disk images in that list will be used by this instance, and all will be cleaned up at the end of the test. """ TestCaseComponent.__init__(self) if reusedComponents is None: self._reusedComponents = [] else: self._reusedComponents = reusedComponents def setupDisks(self, ksdata): self._storage = InstallerStorage() # See comment in super class's method. from pyanaconda.bootloader import get_bootloader self._storage._bootloader = get_bootloader() for component in self._reusedComponents: self._disks.update(component._disks) for (name, image) in self._disks.items(): self._storage.disk_images[name] = image self._storage.reset()