tests: add the base functionality test
authorArtem Bityutskiy <artem.bityutskiy@intel.com>
Tue, 20 Nov 2012 07:36:57 +0000 (09:36 +0200)
committerArtem Bityutskiy <artem.bityutskiy@intel.com>
Tue, 20 Nov 2012 14:56:35 +0000 (16:56 +0200)
Change-Id: I035f71eadda2d2da83a1c8776f035cc1d5ccde12
Signed-off-by: Artem Bityutskiy <artem.bityutskiy@intel.com>
TODO
tests/test_api_base.py [new file with mode: 0644]

diff --git a/TODO b/TODO
index 64ecda9..2189fea 100644 (file)
--- a/TODO
+++ b/TODO
@@ -1,5 +1,4 @@
 1. bmaptool create is slow and can be improved if we use the FIEMAP ioctl
    better. Currently we are calling it for each block.
-2. Create a test-suite
-3. When bmaptool is killed or Ctrl-C'ed half-way through, it should delete
+2. When bmaptool is killed or Ctrl-C'ed half-way through, it should delete
    the files it creates.
diff --git a/tests/test_api_base.py b/tests/test_api_base.py
new file mode 100644 (file)
index 0000000..43838b3
--- /dev/null
@@ -0,0 +1,133 @@
+""" This test verifies the base bmap creation and copying API functionality. It
+generates a random sparse file, then creates a bmap fir this file and copies it
+to a different file using the bmap. Then it compares the original random sparse
+file and the copy and verifies that they are identical. """
+
+# Disable the following pylint recommendations:
+#   *  Too many instance attributes - R0902
+#   *  Too many public methods - R0904
+# pylint: disable=R0902,R0904
+
+import os
+import tempfile
+import random
+import filecmp
+import unittest
+from bmaptools import BmapCreate, BmapCopy, BmapHelpers
+
+# Size of the image to generate and test
+IMAGE_SIZE = 65 * 1024 * 1024
+
+def create_random_sparse_file(file_obj, size):
+    """ Create a sparse file with randomly distributed holes. The mapped areas
+    are filled with random data. """
+
+    block_size = BmapHelpers.get_block_size(file_obj)
+    blocks_cnt = (size + block_size - 1) / block_size
+
+    for block in xrange(0, blocks_cnt):
+        is_mapped = bool(random.randint(0, 1))
+
+        if is_mapped:
+            file_obj.seek(block * block_size)
+            file_obj.write(bytearray(os.urandom(block_size)))
+        else:
+            file_obj.truncate((block + 1) * block_size)
+
+    file_obj.flush()
+
+class TestCreateCopy(unittest.TestCase):
+    """" A basic test for the bmap creation and copying functionality. It first
+    generates a bmap for a sparse file, and then copies the sparse file to a
+    different file, and then checks that all the blocks were copied. The
+    original sparse file is generated randomly. The test entry point is the
+    'test()' method. """
+
+    # Pylint does not like the 'setUP' and 'tearDown' names - mute it.
+    # pylint: disable=C0103
+    def setUp(self):
+        """ Initialize the test - called by the unittest framework before the
+        test starts. """
+
+        self._image_size = IMAGE_SIZE
+
+        # Create and open a temporary file for the image
+        (file_obj, self._image_path) = tempfile.mkstemp()
+        self._f_image = os.fdopen(file_obj, "wb+")
+
+        # Create and open a temporary file for a copy of the copy
+        (file_obj, self._copy_path) = tempfile.mkstemp()
+        self._f_copy = os.fdopen(file_obj, "wb+")
+
+        # Create and open 2 temporary files for the bmap
+        (file_obj, self._bmap1_path) = tempfile.mkstemp()
+        self._f_bmap1 = os.fdopen(file_obj, "w+")
+        (file_obj, self._bmap2_path) = tempfile.mkstemp()
+        self._f_bmap2 = os.fdopen(file_obj, "w+")
+
+    def tearDown(self):
+        """ The clean-up method - called by the unittest framework when the
+        test finishes. """
+
+        self._f_image.close()
+        os.remove(self._image_path)
+
+        self._f_copy.close()
+        os.remove(self._copy_path)
+
+        self._f_bmap1.close()
+        os.remove(self._bmap1_path)
+        self._f_bmap2.close()
+        os.remove(self._bmap2_path)
+
+    # pylint: enable=C0103
+
+    def test(self):
+        """ The test entry point. Executed by the unittest framework after the
+        'setUp()' method. """
+
+        # Create a sparse file with randomly distributed holes
+        create_random_sparse_file(self._f_image, self._image_size)
+
+        #
+        # Pass 1: generate the bmap, copy and compare
+        #
+
+        # Create bmap for the random sparse file
+        creator = BmapCreate.BmapCreate(self._image_path, self._bmap1_path)
+        creator.generate()
+
+        # Copy the random sparse file to a different file using bmap
+        writer = BmapCopy.BmapCopy(self._image_path, self._copy_path,
+                                   self._bmap1_path)
+        writer.copy(False, True)
+
+        # Compare the original file and the copy are identical
+        filecmp.cmp(self._image_path, self._copy_path, False)
+
+        #
+        # Pass 2: same as pass 1, but use file objects instead of paths
+        #
+
+        creator = BmapCreate.BmapCreate(self._f_image, self._f_bmap2)
+        creator.generate()
+
+        writer = BmapCopy.BmapCopy(self._f_image, self._f_copy, self._f_bmap2)
+        writer.copy(False, True)
+
+        filecmp.cmp(self._image_path, self._copy_path, False)
+
+        # Make sure the bmap files generated at pass 1 and pass 2 are identical
+        filecmp.cmp(self._bmap1_path, self._bmap2_path, False)
+
+        #
+        # Pass 3: repeat pass 2 to make sure the same 'BmapCreate' and
+        # 'BmapCopy' objects can be used more than once.
+        #
+        creator.generate()
+        creator.generate()
+        writer.copy(False, True)
+        writer.sync()
+        writer.copy(False, True)
+        filecmp.cmp(self._image_path, self._copy_path, False)
+        filecmp.cmp(self._bmap1_path, self._bmap2_path, False)