Merge tag 'mips-pull-2019-11-16' of git://git.denx.de/u-boot-mips
[oweals/u-boot.git] / test / py / tests / test_fs / conftest.py
index 3437accc971f9ee8a27cb0a0e8d0a1fa8e31df48..43eeb4be0baf0a7b127996782d5a570b7874c3fd 100644 (file)
@@ -10,40 +10,115 @@ from subprocess import call, check_call, check_output, CalledProcessError
 from fstest_defs import *
 
 supported_fs_basic = ['fat16', 'fat32', 'ext4']
+supported_fs_ext = ['fat16', 'fat32']
+supported_fs_mkdir = ['fat16', 'fat32']
+supported_fs_unlink = ['fat16', 'fat32']
 
 #
 # Filesystem test specific setup
 #
 def pytest_addoption(parser):
+    """Enable --fs-type option.
+
+    See pytest_configure() about how it works.
+
+    Args:
+        parser: Pytest command-line parser.
+
+    Returns:
+        Nothing.
+    """
     parser.addoption('--fs-type', action='append', default=None,
         help='Targeting Filesystem Types')
 
 def pytest_configure(config):
+    """Restrict a file system(s) to be tested.
+
+    A file system explicitly named with --fs-type option is selected
+    if it belongs to a default supported_fs_xxx list.
+    Multiple options can be specified.
+
+    Args:
+        config: Pytest configuration.
+
+    Returns:
+        Nothing.
+    """
     global supported_fs_basic
+    global supported_fs_ext
+    global supported_fs_mkdir
+    global supported_fs_unlink
 
     def intersect(listA, listB):
         return  [x for x in listA if x in listB]
 
     supported_fs = config.getoption('fs_type')
     if supported_fs:
-        print("*** FS TYPE modified: %s" % supported_fs)
+        print('*** FS TYPE modified: %s' % supported_fs)
         supported_fs_basic =  intersect(supported_fs, supported_fs_basic)
+        supported_fs_ext =  intersect(supported_fs, supported_fs_ext)
+        supported_fs_mkdir =  intersect(supported_fs, supported_fs_mkdir)
+        supported_fs_unlink =  intersect(supported_fs, supported_fs_unlink)
 
 def pytest_generate_tests(metafunc):
+    """Parametrize fixtures, fs_obj_xxx
+
+    Each fixture will be parametrized with a corresponding support_fs_xxx
+    list.
+
+    Args:
+        metafunc: Pytest test function.
+
+    Returns:
+        Nothing.
+    """
     if 'fs_obj_basic' in metafunc.fixturenames:
         metafunc.parametrize('fs_obj_basic', supported_fs_basic,
             indirect=True, scope='module')
+    if 'fs_obj_ext' in metafunc.fixturenames:
+        metafunc.parametrize('fs_obj_ext', supported_fs_ext,
+            indirect=True, scope='module')
+    if 'fs_obj_mkdir' in metafunc.fixturenames:
+        metafunc.parametrize('fs_obj_mkdir', supported_fs_mkdir,
+            indirect=True, scope='module')
+    if 'fs_obj_unlink' in metafunc.fixturenames:
+        metafunc.parametrize('fs_obj_unlink', supported_fs_unlink,
+            indirect=True, scope='module')
 
 #
 # Helper functions
 #
 def fstype_to_ubname(fs_type):
+    """Convert a file system type to an U-boot specific string
+
+    A generated string can be used as part of file system related commands
+    or a config name in u-boot. Currently fat16 and fat32 are handled
+    specifically.
+
+    Args:
+        fs_type: File system type.
+
+    Return:
+        A corresponding string for file system type.
+    """
     if re.match('fat', fs_type):
         return 'fat'
     else:
         return fs_type
 
 def check_ubconfig(config, fs_type):
+    """Check whether a file system is enabled in u-boot configuration.
+
+    This function is assumed to be called in a fixture function so that
+    the whole test cases will be skipped if a given file system is not
+    enabled.
+
+    Args:
+        fs_type: File system type.
+
+    Return:
+        Nothing.
+    """
     if not config.buildconfig.get('config_cmd_%s' % fs_type, None):
         pytest.skip('.config feature "CMD_%s" not enabled' % fs_type.upper())
     if not config.buildconfig.get('config_%s_write' % fs_type, None):
@@ -51,6 +126,16 @@ def check_ubconfig(config, fs_type):
         % fs_type.upper())
 
 def mk_fs(config, fs_type, size, id):
+    """Create a file system volume.
+
+    Args:
+        fs_type: File system type.
+        size: Size of file system in MiB.
+        id: Prefix string of volume's file name.
+
+    Return:
+        Nothing.
+    """
     fs_img = '%s.%s.img' % (id, fs_type)
     fs_img = config.persistent_data_dir + '/' + fs_img
 
@@ -81,7 +166,15 @@ def mk_fs(config, fs_type, size, id):
 
 # from test/py/conftest.py
 def tool_is_in_path(tool):
-    for path in os.environ["PATH"].split(os.pathsep):
+    """Check whether a given command is available on host.
+
+    Args:
+        tool: Command name.
+
+    Return:
+        True if available, False if not.
+    """
+    for path in os.environ['PATH'].split(os.pathsep):
         fn = os.path.join(path, tool)
         if os.path.isfile(fn) and os.access(fn, os.X_OK):
             return True
@@ -90,6 +183,16 @@ def tool_is_in_path(tool):
 fuse_mounted = False
 
 def mount_fs(fs_type, device, mount_point):
+    """Mount a volume.
+
+    Args:
+        fs_type: File system type.
+        device: Volume's file name.
+        mount_point: Mount point.
+
+    Return:
+        Nothing.
+    """
     global fuse_mounted
 
     fuse_mounted = False
@@ -99,9 +202,9 @@ def mount_fs(fs_type, device, mount_point):
             check_call('guestmount -a %s -m /dev/sda %s'
                 % (device, mount_point), shell=True)
         else:
-            mount_opt = "loop,rw"
+            mount_opt = 'loop,rw'
             if re.match('fat', fs_type):
-                mount_opt += ",umask=0000"
+                mount_opt += ',umask=0000'
 
             check_call('sudo mount -o %s %s %s'
                 % (mount_opt, device, mount_point), shell=True)
@@ -111,7 +214,15 @@ def mount_fs(fs_type, device, mount_point):
     except CalledProcessError:
         raise
 
-def umount_fs(fs_type, mount_point):
+def umount_fs(mount_point):
+    """Unmount a volume.
+
+    Args:
+        mount_point: Mount point.
+
+    Return:
+        Nothing.
+    """
     if fuse_mounted:
         call('sync')
         call('guestunmount %s' % mount_point, shell=True)
@@ -125,6 +236,16 @@ def umount_fs(fs_type, mount_point):
 # NOTE: yield_fixture was deprecated since pytest-3.0
 @pytest.yield_fixture()
 def fs_obj_basic(request, u_boot_config):
+    """Set up a file system to be used in basic fs test.
+
+    Args:
+        request: Pytest request object.
+       u_boot_config: U-boot configuration.
+
+    Return:
+        A fixture for basic fs test, i.e. a triplet of file system type,
+        volume file name and  a list of MD5 hashes.
+    """
     fs_type = request.param
     fs_img = ''
 
@@ -205,14 +326,200 @@ def fs_obj_basic(request, u_boot_config):
            % big_file, shell=True)
         md5val.append(out.split()[0])
 
-        umount_fs(fs_type, mount_dir)
+        umount_fs(mount_dir)
     except CalledProcessError:
         pytest.skip('Setup failed for filesystem: ' + fs_type)
         return
     else:
         yield [fs_ubtype, fs_img, md5val]
     finally:
-        umount_fs(fs_type, mount_dir)
+        umount_fs(mount_dir)
+        call('rmdir %s' % mount_dir, shell=True)
+        if fs_img:
+            call('rm -f %s' % fs_img, shell=True)
+
+#
+# Fixture for extended fs test
+#
+# NOTE: yield_fixture was deprecated since pytest-3.0
+@pytest.yield_fixture()
+def fs_obj_ext(request, u_boot_config):
+    """Set up a file system to be used in extended fs test.
+
+    Args:
+        request: Pytest request object.
+       u_boot_config: U-boot configuration.
+
+    Return:
+        A fixture for extended fs test, i.e. a triplet of file system type,
+        volume file name and  a list of MD5 hashes.
+    """
+    fs_type = request.param
+    fs_img = ''
+
+    fs_ubtype = fstype_to_ubname(fs_type)
+    check_ubconfig(u_boot_config, fs_ubtype)
+
+    mount_dir = u_boot_config.persistent_data_dir + '/mnt'
+
+    min_file = mount_dir + '/' + MIN_FILE
+    tmp_file = mount_dir + '/tmpfile'
+
+    try:
+
+        # 128MiB volume
+        fs_img = mk_fs(u_boot_config, fs_type, 0x8000000, '128MB')
+
+        # Mount the image so we can populate it.
+        check_call('mkdir -p %s' % mount_dir, shell=True)
+        mount_fs(fs_type, fs_img, mount_dir)
+
+        # Create a test directory
+        check_call('mkdir %s/dir1' % mount_dir, shell=True)
+
+        # Create a small file and calculate md5
+        check_call('dd if=/dev/urandom of=%s bs=1K count=20'
+            % min_file, shell=True)
+        out = check_output(
+            'dd if=%s bs=1K 2> /dev/null | md5sum'
+            % min_file, shell=True)
+        md5val = [ out.split()[0] ]
+
+        # Calculate md5sum of Test Case 4
+        check_call('dd if=%s of=%s bs=1K count=20'
+            % (min_file, tmp_file), shell=True)
+        check_call('dd if=%s of=%s bs=1K seek=5 count=20'
+            % (min_file, tmp_file), shell=True)
+        out = check_output('dd if=%s bs=1K 2> /dev/null | md5sum'
+            % tmp_file, shell=True)
+        md5val.append(out.split()[0])
+
+        # Calculate md5sum of Test Case 5
+        check_call('dd if=%s of=%s bs=1K count=20'
+            % (min_file, tmp_file), shell=True)
+        check_call('dd if=%s of=%s bs=1K seek=5 count=5'
+            % (min_file, tmp_file), shell=True)
+        out = check_output('dd if=%s bs=1K 2> /dev/null | md5sum'
+            % tmp_file, shell=True)
+        md5val.append(out.split()[0])
+
+        # Calculate md5sum of Test Case 7
+        check_call('dd if=%s of=%s bs=1K count=20'
+            % (min_file, tmp_file), shell=True)
+        check_call('dd if=%s of=%s bs=1K seek=20 count=20'
+            % (min_file, tmp_file), shell=True)
+        out = check_output('dd if=%s bs=1K 2> /dev/null | md5sum'
+            % tmp_file, shell=True)
+        md5val.append(out.split()[0])
+
+        check_call('rm %s' % tmp_file, shell=True)
+        umount_fs(mount_dir)
+    except CalledProcessError:
+        pytest.skip('Setup failed for filesystem: ' + fs_type)
+        return
+    else:
+        yield [fs_ubtype, fs_img, md5val]
+    finally:
+        umount_fs(mount_dir)
+        call('rmdir %s' % mount_dir, shell=True)
+        if fs_img:
+            call('rm -f %s' % fs_img, shell=True)
+
+#
+# Fixture for mkdir test
+#
+# NOTE: yield_fixture was deprecated since pytest-3.0
+@pytest.yield_fixture()
+def fs_obj_mkdir(request, u_boot_config):
+    """Set up a file system to be used in mkdir test.
+
+    Args:
+        request: Pytest request object.
+       u_boot_config: U-boot configuration.
+
+    Return:
+        A fixture for mkdir test, i.e. a duplet of file system type and
+        volume file name.
+    """
+    fs_type = request.param
+    fs_img = ''
+
+    fs_ubtype = fstype_to_ubname(fs_type)
+    check_ubconfig(u_boot_config, fs_ubtype)
+
+    try:
+        # 128MiB volume
+        fs_img = mk_fs(u_boot_config, fs_type, 0x8000000, '128MB')
+    except:
+        pytest.skip('Setup failed for filesystem: ' + fs_type)
+    else:
+        yield [fs_ubtype, fs_img]
+    finally:
+        if fs_img:
+            call('rm -f %s' % fs_img, shell=True)
+
+#
+# Fixture for unlink test
+#
+# NOTE: yield_fixture was deprecated since pytest-3.0
+@pytest.yield_fixture()
+def fs_obj_unlink(request, u_boot_config):
+    """Set up a file system to be used in unlink test.
+
+    Args:
+        request: Pytest request object.
+       u_boot_config: U-boot configuration.
+
+    Return:
+        A fixture for unlink test, i.e. a duplet of file system type and
+        volume file name.
+    """
+    fs_type = request.param
+    fs_img = ''
+
+    fs_ubtype = fstype_to_ubname(fs_type)
+    check_ubconfig(u_boot_config, fs_ubtype)
+
+    mount_dir = u_boot_config.persistent_data_dir + '/mnt'
+
+    try:
+
+        # 128MiB volume
+        fs_img = mk_fs(u_boot_config, fs_type, 0x8000000, '128MB')
+
+        # Mount the image so we can populate it.
+        check_call('mkdir -p %s' % mount_dir, shell=True)
+        mount_fs(fs_type, fs_img, mount_dir)
+
+        # Test Case 1 & 3
+        check_call('mkdir %s/dir1' % mount_dir, shell=True)
+        check_call('dd if=/dev/urandom of=%s/dir1/file1 bs=1K count=1'
+                                    % mount_dir, shell=True)
+        check_call('dd if=/dev/urandom of=%s/dir1/file2 bs=1K count=1'
+                                    % mount_dir, shell=True)
+
+        # Test Case 2
+        check_call('mkdir %s/dir2' % mount_dir, shell=True)
+       for i in range(0, 20):
+           check_call('mkdir %s/dir2/0123456789abcdef%02x'
+                                    % (mount_dir, i), shell=True)
+
+        # Test Case 4
+        check_call('mkdir %s/dir4' % mount_dir, shell=True)
+
+        # Test Case 5, 6 & 7
+        check_call('mkdir %s/dir5' % mount_dir, shell=True)
+        check_call('dd if=/dev/urandom of=%s/dir5/file1 bs=1K count=1'
+                                    % mount_dir, shell=True)
+
+        umount_fs(mount_dir)
+    except CalledProcessError:
+        pytest.skip('Setup failed for filesystem: ' + fs_type)
+        return
+    else:
+        yield [fs_ubtype, fs_img]
+    finally:
+        umount_fs(mount_dir)
         call('rmdir %s' % mount_dir, shell=True)
         if fs_img:
             call('rm -f %s' % fs_img, shell=True)