From b46451d02d2660cdf46338b2e535467bf39e5164 Mon Sep 17 00:00:00 2001 From: Prashanth Pai Date: Mon, 13 Jun 2016 15:55:49 +0530 Subject: Expose glfs_readdirplus_r This patch does the following: * Implements Volume.listdir_with_stat() API which internally invokes glfs_readdirplus_r to return directory entries along with stat for each entry. * Implements Volume.scandir() which is similar to os.scandir() present in Python 3.5 * Makes Dir class iterable. * Enables Dir class to raise OSError when glfs_readdir* calls fail. Previously, these failures were silently being ignored and treated as a case of EOF. Change-Id: Id918c39a7ef3882553e9bcd3fbf9455ee1c25a83 Signed-off-by: Prashanth Pai --- test/functional/libgfapi-python-tests.py | 35 +++++++++++- test/unit/gluster/test_gfapi.py | 94 +++++++++++++++++++++++++++++++- 2 files changed, 124 insertions(+), 5 deletions(-) (limited to 'test') diff --git a/test/functional/libgfapi-python-tests.py b/test/functional/libgfapi-python-tests.py index 7dab995..1e6db3e 100644 --- a/test/functional/libgfapi-python-tests.py +++ b/test/functional/libgfapi-python-tests.py @@ -10,17 +10,19 @@ import unittest import os +import stat import types import errno import hashlib import threading - -from gluster.gfapi import File, Volume -from gluster.exceptions import LibgfapiException, Error from test import get_test_config from ConfigParser import NoSectionError, NoOptionError from uuid import uuid4 +from gluster.api import Stat +from gluster.gfapi import File, Volume, DirEntry +from gluster.exceptions import LibgfapiException, Error + config = get_test_config() if config: try: @@ -845,6 +847,33 @@ class DirOpsTest(unittest.TestCase): dir_list.sort() self.assertEqual(dir_list, ["testfile0", "testfile1", "testfile2"]) + def test_listdir_with_stat(self): + dir_list = self.vol.listdir_with_stat(self.dir_path) + dir_list_sorted = sorted(dir_list, key=lambda tup: tup[0]) + for index, (name, stat_info) in enumerate(dir_list_sorted): + self.assertEqual(name, 'testfile%s' % (index)) + self.assertTrue(isinstance(stat_info, Stat)) + self.assertTrue(stat.S_ISREG(stat_info.st_mode)) + self.assertEqual(stat_info.st_size, len(self.data)) + + # Error - path does not exist + self.assertRaises(OSError, + self.vol.listdir_with_stat, 'non-existent-dir') + + def test_scandir(self): + entries = [] + for entry in self.vol.scandir(self.dir_path): + self.assertTrue(isinstance(entry, DirEntry)) + entries.append(entry) + + entries_sorted = sorted(entries, key=lambda e: e.name) + for index, entry in enumerate(entries_sorted): + self.assertEqual(entry.name, 'testfile%s' % (index)) + self.assertTrue(entry.is_file()) + self.assertFalse(entry.is_dir()) + self.assertTrue(isinstance(entry.stat(), Stat)) + self.assertEqual(entry.stat().st_size, len(self.data)) + def test_makedirs(self): name = self.dir_path + "/subd1/subd2/subd3" self.vol.makedirs(name, 0755) diff --git a/test/unit/gluster/test_gfapi.py b/test/unit/gluster/test_gfapi.py index a859c2f..d4a9b52 100644 --- a/test/unit/gluster/test_gfapi.py +++ b/test/unit/gluster/test_gfapi.py @@ -17,7 +17,7 @@ import time import math import errno -from gluster.gfapi import File, Dir, Volume +from gluster.gfapi import File, Dir, Volume, DirEntry from gluster import api from gluster.exceptions import LibgfapiException from nose import SkipTest @@ -622,7 +622,7 @@ class TestVolume(unittest.TestCase): dirent3.d_name = "." dirent3.d_reclen = 1 mock_Dir_next = Mock() - mock_Dir_next.side_effect = [dirent1, dirent2, dirent3, None] + mock_Dir_next.side_effect = [dirent1, dirent2, dirent3, StopIteration] with nested(patch("gluster.gfapi.api.glfs_opendir", mock_glfs_opendir), @@ -638,6 +638,96 @@ class TestVolume(unittest.TestCase): with patch("gluster.gfapi.api.glfs_opendir", mock_glfs_opendir): self.assertRaises(OSError, self.vol.listdir, "test.txt") + def test_listdir_with_stat_success(self): + mock_glfs_opendir = Mock() + mock_glfs_opendir.return_value = 2 + + dirent1 = api.Dirent() + dirent1.d_name = "mockfile" + dirent1.d_reclen = 8 + stat1 = api.Stat() + stat1.st_nlink = 1 + dirent2 = api.Dirent() + dirent2.d_name = "mockdir" + dirent2.d_reclen = 7 + stat2 = api.Stat() + stat2.st_nlink = 2 + dirent3 = api.Dirent() + dirent3.d_name = "." + dirent3.d_reclen = 1 + stat3 = api.Stat() + stat3.n_link = 2 + mock_Dir_next = Mock() + mock_Dir_next.side_effect = [(dirent1, stat1), + (dirent2, stat2), + (dirent3, stat3), + StopIteration] + + with nested(patch("gluster.gfapi.api.glfs_opendir", + mock_glfs_opendir), + patch("gluster.gfapi.Dir.next", mock_Dir_next)): + d = self.vol.listdir_with_stat("testdir") + self.assertEqual(len(d), 2) + self.assertEqual(d[0][0], 'mockfile') + self.assertEqual(d[0][1].st_nlink, 1) + self.assertEqual(d[1][0], 'mockdir') + self.assertEqual(d[1][1].st_nlink, 2) + + def test_listdir_with_stat_fail_exception(self): + mock_glfs_opendir = Mock() + mock_glfs_opendir.return_value = None + with patch("gluster.gfapi.api.glfs_opendir", mock_glfs_opendir): + self.assertRaises(OSError, self.vol.listdir_with_stat, "dir") + + def test_scandir_success(self): + mock_glfs_opendir = Mock() + mock_glfs_opendir.return_value = 2 + + dirent1 = api.Dirent() + dirent1.d_name = "mockfile" + dirent1.d_reclen = 8 + stat1 = api.Stat() + stat1.st_nlink = 1 + stat1.st_mode = 33188 + dirent2 = api.Dirent() + dirent2.d_name = "mockdir" + dirent2.d_reclen = 7 + stat2 = api.Stat() + stat2.st_nlink = 2 + stat2.st_mode = 16877 + dirent3 = api.Dirent() + dirent3.d_name = "." + dirent3.d_reclen = 1 + stat3 = api.Stat() + stat3.n_link = 2 + stat3.st_mode = 16877 + mock_Dir_next = Mock() + mock_Dir_next.side_effect = [(dirent1, stat1), + (dirent2, stat2), + (dirent3, stat3), + StopIteration] + + with nested(patch("gluster.gfapi.api.glfs_opendir", + mock_glfs_opendir), + patch("gluster.gfapi.Dir.next", mock_Dir_next)): + i = 0 + for entry in self.vol.scandir("testdir"): + self.assertTrue(isinstance(entry, DirEntry)) + if entry.name == 'mockfile': + self.assertEqual(entry.path, 'testdir/mockfile') + self.assertTrue(entry.is_file()) + self.assertFalse(entry.is_dir()) + self.assertEqual(entry.stat().st_nlink, 1) + elif entry.name == 'mockdir': + self.assertEqual(entry.path, 'testdir/mockdir') + self.assertTrue(entry.is_dir()) + self.assertFalse(entry.is_file()) + self.assertEqual(entry.stat().st_nlink, 2) + else: + self.fail("Unexpected entry") + i = i + 1 + self.assertEqual(i, 2) + def test_listxattr_success(self): def mock_glfs_listxattr(fs, path, buf, buflen): if buf: -- cgit