Browse Source

extend filesystem tests (#1481)

* extend filesystem tests

* fix test failure when no explicit umask was set

* have black uglify the code

* require case-sensitive fs for test_capitalization_linux

* run black with -l120 instead

* make windows-compatible, fix some minor issues

* mark xfail rather than comment out part of trim_win_path
pull/1491/head
jcfp 5 years ago
committed by GitHub
parent
commit
e9b4340a25
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      sabnzbd/filesystem.py
  2. 3
      tests/requirements.txt
  3. 768
      tests/test_filesystem.py

2
sabnzbd/filesystem.py

@ -252,7 +252,7 @@ def is_obfuscated_filename(filename):
""" Check if this file has an extension, if not, it's """ Check if this file has an extension, if not, it's
probably obfuscated and we don't use it probably obfuscated and we don't use it
""" """
return os.path.splitext(filename)[1] == "" return len(get_ext(filename)) < 2
def real_path(loc, path): def real_path(loc, path):

3
tests/requirements.txt

@ -2,6 +2,7 @@
selenium selenium
requests requests
pytest pytest
pyfakefs
# Only works on Python 3.6+ # Only works on Python 3.6+
black; python_version >= "3.6" black; python_version >= "3.6"

768
tests/test_filesystem.py

@ -16,14 +16,24 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
""" """
tests.test_misc - Testing functions in filesystem.py tests.test_filesystem - Testing functions in filesystem.py
""" """
import os
import stat
import pyfakefs.fake_filesystem_unittest as ffs
import sabnzbd.filesystem as filesystem import sabnzbd.filesystem as filesystem
import sabnzbd.cfg
from tests.testhelper import * from tests.testhelper import *
# Set the global uid for fake filesystems to a non-root user;
# by default this depends on the user running pytest.
global_uid = 1000
ffs.set_uid(global_uid)
class TestFileFolderNameSanitizer: class TestFileFolderNameSanitizer:
def test_empty(self): def test_empty(self):
assert filesystem.sanitize_filename(None) is None assert filesystem.sanitize_filename(None) is None
@ -32,17 +42,30 @@ class TestFileFolderNameSanitizer:
@set_platform("win32") @set_platform("win32")
def test_colon_handling_windows(self): def test_colon_handling_windows(self):
assert filesystem.sanitize_filename("test:aftertest") == "test-aftertest" assert filesystem.sanitize_filename("test:aftertest") == "test-aftertest"
assert filesystem.sanitize_filename(":") == "-"
assert filesystem.sanitize_filename("test:") == "test-"
assert filesystem.sanitize_filename("test: ") == "test-"
# They should act the same # They should act the same
assert filesystem.sanitize_filename("test:aftertest") == filesystem.sanitize_foldername("test:aftertest") assert filesystem.sanitize_filename("test:aftertest") == filesystem.sanitize_foldername("test:aftertest")
# TODO: Add a lot more tests here!
@set_platform("darwin") @set_platform("darwin")
def test_colon_handling_darwin(self): def test_colon_handling_darwin(self):
assert filesystem.sanitize_filename("test:aftertest") == "aftertest" assert filesystem.sanitize_filename("test:aftertest") == "aftertest"
assert filesystem.sanitize_filename(":aftertest") == "aftertest"
assert filesystem.sanitize_filename("::aftertest") == "aftertest"
assert filesystem.sanitize_filename(":after:test") == "test"
# Empty after sanitising with darwin colon handling
assert filesystem.sanitize_filename(":") == "unknown"
assert filesystem.sanitize_filename("test:") == "unknown"
assert filesystem.sanitize_filename("test: ") == "unknown"
@set_platform("linux") @set_platform("linux")
@set_config({"sanitize_safe": False})
def test_colon_handling_other(self): def test_colon_handling_other(self):
assert filesystem.sanitize_filename("test:aftertest") == "test:aftertest" assert filesystem.sanitize_filename("test:aftertest") == "test:aftertest"
assert filesystem.sanitize_filename(":") == ":"
assert filesystem.sanitize_filename("test:") == "test:"
assert filesystem.sanitize_filename("test: ") == "test:"
@set_platform("win32") @set_platform("win32")
def test_win_devices_on_win(self): def test_win_devices_on_win(self):
@ -53,6 +76,7 @@ class TestFileFolderNameSanitizer:
assert filesystem.sanitize_filename("a$mft") == "a$mft" assert filesystem.sanitize_filename("a$mft") == "a$mft"
@set_platform("linux") @set_platform("linux")
@set_config({"sanitize_safe": False})
def test_win_devices_not_win(self): def test_win_devices_not_win(self):
# Linux and Darwin are the same for this # Linux and Darwin are the same for this
assert filesystem.sanitize_filename(None) is None assert filesystem.sanitize_filename(None) is None
@ -61,21 +85,140 @@ class TestFileFolderNameSanitizer:
assert filesystem.sanitize_filename("$mft") == "$mft" assert filesystem.sanitize_filename("$mft") == "$mft"
assert filesystem.sanitize_filename("a$mft") == "a$mft" assert filesystem.sanitize_filename("a$mft") == "a$mft"
@set_platform("linux")
@set_config({"sanitize_safe": False})
def test_file_illegal_chars_linux(self):
assert filesystem.sanitize_filename("test/aftertest") == "test+aftertest"
assert filesystem.sanitize_filename("/test") == "+test"
assert filesystem.sanitize_filename("test/") == "test+"
assert filesystem.sanitize_filename(r"/test\/aftertest/") == r"+test\+aftertest+"
assert filesystem.sanitize_filename("/") == "+"
assert filesystem.sanitize_filename("///") == "+++"
assert filesystem.sanitize_filename("../") == "..+"
assert filesystem.sanitize_filename("../test") == "..+test"
@set_platform("linux")
@set_config({"sanitize_safe": False})
def test_folder_illegal_chars_linux(self):
assert filesystem.sanitize_foldername('test"aftertest') == "test'aftertest"
assert filesystem.sanitize_foldername("test:") == "test-"
assert filesystem.sanitize_foldername("test<>?*|aftertest") == "test<>?*|aftertest"
def test_char_collections(self):
assert len(filesystem.CH_ILLEGAL) == len(filesystem.CH_LEGAL)
assert len(filesystem.CH_ILLEGAL_WIN) == len(filesystem.CH_LEGAL_WIN)
@set_platform("linux")
@set_config({"sanitize_safe": False})
def test_legal_chars_linux(self):
# Illegal on Windows but not on Linux, unless sanitize_safe is active.
# Don't bother with '/' which is illegal in filenames on all platforms.
char_ill = filesystem.CH_ILLEGAL_WIN.replace("/", "")
assert filesystem.sanitize_filename("test" + char_ill + "aftertest") == ("test" + char_ill + "aftertest")
for char in char_ill:
# Try at start, middle, and end of a filename.
assert filesystem.sanitize_filename("test" + char * 2 + "aftertest") == ("test" + char * 2 + "aftertest")
assert filesystem.sanitize_filename("test" + char * 2) == ("test" + char * 2).strip()
assert filesystem.sanitize_filename(char * 2 + "test") == (char * 2 + "test").strip()
@set_platform("linux")
@set_config({"sanitize_safe": True})
def test_sanitize_safe_linux(self):
# Set sanitize_safe to on, simulating Windows-style restrictions.
assert filesystem.sanitize_filename("test" + filesystem.CH_ILLEGAL_WIN + "aftertest") == (
"test" + filesystem.CH_LEGAL_WIN + "aftertest"
)
for index in range(0, len(filesystem.CH_ILLEGAL_WIN)):
char_leg = filesystem.CH_LEGAL_WIN[index]
char_ill = filesystem.CH_ILLEGAL_WIN[index]
assert filesystem.sanitize_filename("test" + char_ill * 2 + "aftertest") == (
"test" + char_leg * 2 + "aftertest"
)
# Illegal chars that also get caught by strip() never make it far
# enough to be replaced by their legal equivalents if they appear
# on either end of the filename.
if char_ill.strip():
assert filesystem.sanitize_filename("test" + char_ill * 2) == ("test" + char_leg * 2)
assert filesystem.sanitize_filename(char_ill * 2 + "test") == (char_leg * 2 + "test")
def test_filename_dot(self):
# All dots should survive in filenames
assert filesystem.sanitize_filename(".test") == ".test"
assert filesystem.sanitize_filename("..test") == "..test"
assert filesystem.sanitize_filename("test.") == "test."
assert filesystem.sanitize_filename("test..") == "test.."
assert filesystem.sanitize_filename("test.aftertest") == "test.aftertest"
assert filesystem.sanitize_filename("test..aftertest") == "test..aftertest"
assert filesystem.sanitize_filename("test.aftertest.") == "test.aftertest."
assert filesystem.sanitize_filename("test.aftertest..") == "test.aftertest.."
def test_foldername_dot(self):
# Dot should be removed from the end of directory names only
assert filesystem.sanitize_foldername(".test") == ".test"
assert filesystem.sanitize_foldername("..test") == "..test"
assert filesystem.sanitize_foldername("test.") == "test"
assert filesystem.sanitize_foldername("test..") == "test"
assert filesystem.sanitize_foldername("test.aftertest") == "test.aftertest"
assert filesystem.sanitize_foldername("test..aftertest") == "test..aftertest"
assert filesystem.sanitize_foldername("test.aftertest.") == "test.aftertest"
assert filesystem.sanitize_foldername("test.aftertest..") == "test.aftertest"
assert filesystem.sanitize_foldername("/test/this.") == "+test+this"
assert filesystem.sanitize_foldername("/test./this.") == "+test.+this"
def test_filename_empty_result(self):
# Nothing remains after sanitizing the filename
assert filesystem.sanitize_filename("\n") == "unknown"
assert filesystem.sanitize_filename("\r\n") == "unknown"
assert filesystem.sanitize_filename("\n\r") == "unknown"
assert filesystem.sanitize_filename("\t\t\t") == "unknown"
assert filesystem.sanitize_filename(" ") == "unknown"
assert filesystem.sanitize_filename(" ") == "unknown"
def test_foldername_empty_result(self):
# Nothing remains after sanitizing the foldername
assert filesystem.sanitize_foldername("\n") == "unknown"
assert filesystem.sanitize_foldername("\r\n") == "unknown"
assert filesystem.sanitize_foldername("\n\r") == "unknown"
assert filesystem.sanitize_foldername("\t\t\t") == "unknown"
assert filesystem.sanitize_foldername(" ") == "unknown"
assert filesystem.sanitize_foldername(" ") == "unknown"
class TestSameFile: class TestSameFile:
def test_nothing_in_common(self): def test_nothing_in_common_win_paths(self):
assert 0 == filesystem.same_file("C:\\", "D:\\") assert 0 == filesystem.same_file("C:\\", "D:\\")
assert 0 == filesystem.same_file("C:\\", "/home/test") assert 0 == filesystem.same_file("C:\\", "/home/test")
def test_nothing_in_common_unix_paths(self):
assert 0 == filesystem.same_file("/home/", "/data/test") assert 0 == filesystem.same_file("/home/", "/data/test")
assert 0 == filesystem.same_file("/test/home/test", "/home/") assert 0 == filesystem.same_file("/test/home/test", "/home/")
assert 0 == filesystem.same_file("/test/../home", "/test")
assert 0 == filesystem.same_file("/test/./test", "/test")
@pytest.mark.skipif(sys.platform.startswith("win"), reason="Not for Windows")
@set_platform("linux")
def test_posix_fun(self):
assert 1 == filesystem.same_file("/test", "/test")
# IEEE 1003.1-2017 par. 4.13 for details
assert 0 == filesystem.same_file("/test", "//test")
assert 1 == filesystem.same_file("/test", "///test")
assert 1 == filesystem.same_file("/test", "/test/")
assert 1 == filesystem.same_file("/test", "/test//")
assert 1 == filesystem.same_file("/test", "/test///")
def test_same(self): def test_same(self):
assert 1 == filesystem.same_file("/home/123", "/home/123") assert 1 == filesystem.same_file("/home/123", "/home/123")
assert 1 == filesystem.same_file("D:\\", "D:\\") assert 1 == filesystem.same_file("D:\\", "D:\\")
assert 1 == filesystem.same_file("/test/../test", "/test")
assert 1 == filesystem.same_file("test/../test", "test")
assert 1 == filesystem.same_file("/test/./test", "/test/test")
assert 1 == filesystem.same_file("./test", "test")
def test_subfolder(self): def test_subfolder(self):
assert 2 == filesystem.same_file("\\\\?\\C:\\", "\\\\?\\C:\\Users\\") assert 2 == filesystem.same_file("\\\\?\\C:\\", "\\\\?\\C:\\Users\\")
assert 2 == filesystem.same_file("/home/test123", "/home/test123/sub") assert 2 == filesystem.same_file("/home/test123", "/home/test123/sub")
assert 2 == filesystem.same_file("/test", "/test/./test")
assert 2 == filesystem.same_file("/home/../test", "/test/./test")
@set_platform("win32") @set_platform("win32")
def test_capitalization(self): def test_capitalization(self):
@ -83,4 +226,621 @@ class TestSameFile:
assert 1 == filesystem.same_file("/HOME/123", "/home/123") assert 1 == filesystem.same_file("/HOME/123", "/home/123")
assert 1 == filesystem.same_file("D:\\", "d:\\") assert 1 == filesystem.same_file("D:\\", "d:\\")
assert 2 == filesystem.same_file("\\\\?\\c:\\", "\\\\?\\C:\\Users\\") assert 2 == filesystem.same_file("\\\\?\\c:\\", "\\\\?\\C:\\Users\\")
assert 2 == filesystem.same_file("/HOME/test123", "/home/test123/sub")
@pytest.mark.skipif(sys.platform.startswith(("win", "darwin")), reason="Requires a case-sensitive filesystem")
@set_platform("linux")
def test_capitalization_linux(self):
assert 2 == filesystem.same_file("/home/test123", "/home/test123/sub")
assert 0 == filesystem.same_file("/test", "/Test")
assert 0 == filesystem.same_file("tesT", "Test")
assert 0 == filesystem.same_file("/test/../Home", "/home")
class TestIsObfuscatedFilename:
def test_obfuscated(self):
# Files are considered obfuscated if they lack an extension
assert filesystem.is_obfuscated_filename(".") is True
assert filesystem.is_obfuscated_filename("..") is True
assert filesystem.is_obfuscated_filename(".test") is True
assert filesystem.is_obfuscated_filename("test.") is True
assert filesystem.is_obfuscated_filename("test.ext.") is True
assert filesystem.is_obfuscated_filename("t.....") is True
assert filesystem.is_obfuscated_filename("a_" + ("test" * 666)) is True
def test_not_obfuscated(self):
assert filesystem.is_obfuscated_filename("test.ext") is False
assert filesystem.is_obfuscated_filename(".test.ext") is False
assert filesystem.is_obfuscated_filename("test..ext") is False
assert filesystem.is_obfuscated_filename("test.ext") is False
assert filesystem.is_obfuscated_filename("test .ext") is False
assert filesystem.is_obfuscated_filename("test. ext") is False
assert filesystem.is_obfuscated_filename("test . ext") is False
assert filesystem.is_obfuscated_filename("a." + ("test" * 666)) is False
class TestClipLongPath:
def test_empty(self):
assert filesystem.clip_path(None) is None
assert filesystem.long_path(None) is None
@set_platform("win32")
def test_clip_path_win(self):
assert filesystem.clip_path(r"\\?\UNC\test") == r"\\test"
assert filesystem.clip_path(r"\\?\F:\test") == r"F:\test"
@set_platform("win32")
def test_nothing_to_clip_win(self):
assert filesystem.clip_path(r"\\test") == r"\\test"
assert filesystem.clip_path(r"F:\test") == r"F:\test"
assert filesystem.clip_path("/test/dir") == "/test/dir"
@set_platform("linux")
def test_clip_path_non_win(self):
# Shouldn't have any effect on platforms other than Windows
assert filesystem.clip_path(r"\\?\UNC\test") == r"\\?\UNC\test"
assert filesystem.clip_path(r"\\?\F:\test") == r"\\?\F:\test"
assert filesystem.clip_path(r"\\test") == r"\\test"
assert filesystem.clip_path(r"F:\test") == r"F:\test"
assert filesystem.clip_path("/test/dir") == "/test/dir"
@set_platform("win32")
def test_long_path_win(self):
assert filesystem.long_path(r"\\test") == r"\\?\UNC\test"
assert filesystem.long_path(r"F:\test") == r"\\?\F:\test"
@set_platform("win32")
def test_nothing_to_lenghten_win(self):
assert filesystem.long_path(r"\\?\UNC\test") == r"\\?\UNC\test"
assert filesystem.long_path(r"\\?\F:\test") == r"\\?\F:\test"
@set_platform("linux")
def test_long_path_non_win(self):
# Shouldn't have any effect on platforms other than Windows
assert filesystem.long_path(r"\\?\UNC\test") == r"\\?\UNC\test"
assert filesystem.long_path(r"\\?\F:\test") == r"\\?\F:\test"
assert filesystem.long_path(r"\\test") == r"\\test"
assert filesystem.long_path(r"F:\test") == r"F:\test"
assert filesystem.long_path("/test/dir") == "/test/dir"
@pytest.mark.skipif(sys.platform.startswith("win"), reason="Broken on Windows")
class TestCheckMountLinux(ffs.TestCase):
# Our collection of fake directories
test_dirs = ["/media/test/dir", "/mnt/TEST/DIR"]
def setUp(self):
self.setUpPyfakefs()
self.fs.path_separator = "/"
self.fs.is_case_sensitive = True
for dir in self.test_dirs:
self.fs.create_dir(dir, perm_bits=755)
# Sanity check the fake filesystem
assert os.path.exists(dir) is True
@set_platform("linux")
def test_bare_mountpoint_linux(self):
assert filesystem.check_mount("/media") is True
assert filesystem.check_mount("/media/") is True
assert filesystem.check_mount("/mnt") is True
assert filesystem.check_mount("/mnt/") is True
@set_platform("linux")
def test_existing_dir_linux(self):
assert filesystem.check_mount("/media/test") is True
assert filesystem.check_mount("/media/test/dir/") is True
assert filesystem.check_mount("/media/test/DIR/") is True
assert filesystem.check_mount("/mnt/TEST") is True
assert filesystem.check_mount("/mnt/TEST/dir/") is True
assert filesystem.check_mount("/mnt/TEST/DIR/") is True
@set_platform("linux")
# Cut down a bit on the waiting time
@set_config({"wait_ext_drive": 1})
def test_dir_nonexistent_linux(self):
# Filesystem is case-sensitive on this platform
assert filesystem.check_mount("/media/TEST") is False # Issue #1457
assert filesystem.check_mount("/media/TesT/") is False
assert filesystem.check_mount("/mnt/TeSt/DIR") is False
assert filesystem.check_mount("/mnt/test/DiR/") is False
@set_platform("linux")
def test_dir_outsider_linux(self):
# Outside of /media and /mnt
assert filesystem.check_mount("/test/that/") is True
# Root directory
assert filesystem.check_mount("/") is True
@pytest.mark.skipif(sys.platform.startswith("win"), reason="Broken on Windows")
class TestCheckMountDarwin(ffs.TestCase):
# Our faked macos directory
test_dir = "/Volumes/test/dir"
def setUp(self):
self.setUpPyfakefs()
self.fs.is_macos = True
self.fs.is_case_sensitive = False
self.fs.path_separator = "/"
self.fs.create_dir(self.test_dir, perm_bits=755)
# Verify the fake filesystem does its thing
assert os.path.exists(self.test_dir) is True
@set_platform("darwin")
def test_bare_mountpoint_darwin(self):
assert filesystem.check_mount("/Volumes") is True
assert filesystem.check_mount("/Volumes/") is True
@set_platform("darwin")
def test_existing_dir_darwin(self):
assert filesystem.check_mount("/Volumes/test") is True
assert filesystem.check_mount("/Volumes/test/dir/") is True
# Filesystem is set case-insensitive for this platform
assert filesystem.check_mount("/VOLUMES/test") is True
assert filesystem.check_mount("/volumes/Test/dir/") is True
@set_platform("darwin")
# Cut down a bit on the waiting time
@set_config({"wait_ext_drive": 1})
def test_dir_nonexistent_darwin(self):
# Within /Volumes
assert filesystem.check_mount("/Volumes/nosuchdir") is False # Issue #1457
assert filesystem.check_mount("/Volumes/noSuchDir/") is False
assert filesystem.check_mount("/Volumes/nosuchDIR/subdir") is False
assert filesystem.check_mount("/Volumes/NOsuchdir/subdir/") is False
@set_platform("darwin")
def test_dir_outsider_darwin(self):
# Outside of /Volumes
assert filesystem.check_mount("/test/that/") is True
# Root directory
assert filesystem.check_mount("/") is True
class TestCheckMountWin(ffs.TestCase):
# Our faked windows directory
test_dir = r"F:\test\dir"
def setUp(self):
self.setUpPyfakefs()
self.fs.is_windows_fs = True
self.fs.is_case_sensitive = False
self.fs.path_separator = "\\"
self.fs.create_dir(self.test_dir)
# Sanity check the fake filesystem
assert os.path.exists(self.test_dir) is True
@set_platform("win32")
def test_existing_dir_win(self):
assert filesystem.check_mount("F:\\test") is True
assert filesystem.check_mount("F:\\test\\dir\\") is True
# Filesystem and drive letters are case-insensitive on this platform
assert filesystem.check_mount("f:\\Test") is True
assert filesystem.check_mount("f:\\test\\DIR\\") is True
@set_platform("win32")
def test_bare_mountpoint_win(self):
assert filesystem.check_mount("F:\\") is True
assert filesystem.check_mount("Z:\\") is False
@set_platform("win32")
def test_dir_nonexistent_win(self):
# The existance of the drive letter is what really matters
assert filesystem.check_mount("F:\\NoSuchDir") is True
assert filesystem.check_mount("F:\\NoSuchDir\\") is True
assert filesystem.check_mount("F:\\NOsuchdir\\subdir") is True
assert filesystem.check_mount("F:\\nosuchDIR\\subdir\\") is True
@set_platform("win32")
# Cut down a bit on the waiting time
@set_config({"wait_ext_drive": 1})
def test_dir_on_nonexistent_drive_win(self):
# Non-existent drive-letter
assert filesystem.check_mount("H:\\NoSuchDir") is False
assert filesystem.check_mount("E:\\NoSuchDir\\") is False
assert filesystem.check_mount("L:\\NOsuchdir\\subdir") is False
assert filesystem.check_mount("L:\\nosuchDIR\\subdir\\") is False
@set_platform("win32")
def test_dir_outsider_win(self):
# Outside the local filesystem
assert filesystem.check_mount("//test/that/") is True
class TestTrimWinPath:
@set_platform("win32")
def test_short_path(self):
assert filesystem.trim_win_path(r"C:\short\path") == r"C:\short\path"
@pytest.mark.xfail(sys.platform == "win32", reason="Bug in trim_win_path")
@set_platform("win32")
def test_long_path_short_segments(self):
test_path = "C:\\" + "A" * 20 + "\\" + "B" * 20 + "\\" + "C" * 20 # Strlen 65
# Current code causes the path to end up with strlen 70 rather than 69 on Windows
assert filesystem.trim_win_path(test_path + "\\" + ("D" * 20)) == test_path + "\\" + "D" * 3
@pytest.mark.skipif(sys.platform.startswith("win"), reason="Broken on Windows")
class TestRecursiveListdir(ffs.TestCase):
# Basic fake filesystem setup stanza
def setUp(self):
self.setUpPyfakefs()
self.fs.path_separator = "/"
self.fs.is_case_sensitive = True
def test_nonexistent_dir(self):
assert filesystem.recursive_listdir("/foo/bar") == []
def test_no_exceptions(self):
test_files = (
"/test/dir/file1.ext",
"/test/dir/file2",
"/test/dir/sub/sub/sub/dir/file3.ext",
)
for file in test_files:
self.fs.create_file(file)
assert os.path.exists(file) is True
# List our fake directory structure
results_subdir = filesystem.recursive_listdir("/test/dir")
assert len(results_subdir) == 3
for entry in test_files:
assert (entry in results_subdir) is True
# List the same directory again, this time using its parent as the function argument.
# Results should be identical, since there's nothing in /test but that one subdirectory
results_parent = filesystem.recursive_listdir("/test")
# Don't make assumptions about the sorting of the lists of results
results_parent.sort()
results_subdir.sort()
assert results_parent == results_subdir
# List that subsubsub-directory; no sorting required for a single result
assert filesystem.recursive_listdir("/test/dir/sub/sub") == ["/test/dir/sub/sub/sub/dir/file3.ext"]
def test_exception_appledouble(self):
# Anything below a .AppleDouble directory should be omitted
test_file = "/foo/bar/.AppleDouble/Oooooo.ps"
self.fs.create_file(test_file)
assert os.path.exists(test_file) is True
assert filesystem.recursive_listdir("/foo") == []
assert filesystem.recursive_listdir("/foo/bar") == []
assert filesystem.recursive_listdir("/foo/bar/.AppleDouble") == []
def test_exception_dsstore(self):
# Anything below a .DS_Store directory should be omitted
for file in (
"/some/FILE",
"/some/.DS_Store/oh.NO",
"/some/.DS_Store/subdir/The.End",
):
self.fs.create_file(file)
assert os.path.exists(file) is True
assert filesystem.recursive_listdir("/some") == ["/some/FILE"]
assert filesystem.recursive_listdir("/some/.DS_Store/") == []
assert filesystem.recursive_listdir("/some/.DS_Store/subdir") == []
def test_invalid_file_argument(self):
# This is obviously not intended use; the function expects a directory
# as its argument, not a file. Test anyway.
test_file = "/dev/sleepy"
self.fs.create_file(test_file)
assert os.path.exists(test_file) is True
assert filesystem.recursive_listdir(test_file) == []
class TestRecursiveListdirWin(ffs.TestCase):
# Basic fake filesystem setup stanza
@set_platform("win32")
def setUp(self):
self.setUpPyfakefs()
self.fs.is_windows_fs = True
self.fs.path_separator = "\\"
self.fs.is_case_sensitive = False
def test_nonexistent_dir(self):
assert filesystem.recursive_listdir(r"F:\foo\bar") == []
def test_no_exceptions(self):
test_files = (
r"f:\test\dir\file1.ext",
r"f:\test\dir\file2",
r"f:\test\dir\sub\sub\sub\dir\file3.ext",
)
for file in test_files:
self.fs.create_file(file)
assert os.path.exists(file) is True
# List our fake directory structure
results_subdir = filesystem.recursive_listdir(r"f:\test\dir")
assert len(results_subdir) == 3
for entry in test_files:
assert (entry in results_subdir) is True
# List the same directory again, this time using its parent as the function argument.
# Results should be identical, since there's nothing in /test but that one subdirectory
results_parent = filesystem.recursive_listdir(r"f:\test")
# Don't make assumptions about the sorting of the lists of results
results_parent.sort()
results_subdir.sort()
assert results_parent == results_subdir
# List that subsubsub-directory; no sorting required for a single result
assert (
filesystem.recursive_listdir(r"F:\test\dir\SUB\sub")[0].lower() == r"f:\test\dir\sub\sub\sub\dir\file3.ext"
)
def test_exception_appledouble(self):
# Anything below a .AppleDouble directory should be omitted
test_file = r"f:\foo\bar\.AppleDouble\Oooooo.ps"
self.fs.create_file(test_file)
assert os.path.exists(test_file) is True
assert filesystem.recursive_listdir(r"f:\foo") == []
assert filesystem.recursive_listdir(r"f:\foo\bar") == []
assert filesystem.recursive_listdir(r"F:\foo\bar\.AppleDouble") == []
def test_exception_dsstore(self):
# Anything below a .DS_Store directory should be omitted
for file in (
r"f:\some\FILE",
r"f:\some\.DS_Store\oh.NO",
r"f:\some\.DS_Store\subdir\The.End",
):
self.fs.create_file(file)
assert os.path.exists(file) is True
assert filesystem.recursive_listdir(r"f:\some") == [r"f:\some\FILE"]
assert filesystem.recursive_listdir(r"f:\some\.DS_Store") == []
assert filesystem.recursive_listdir(r"f:\some\.DS_Store\subdir") == []
def test_invalid_file_argument(self):
# This is obviously not intended use; the function expects a directory
# as its argument, not a file. Test anyway.
test_file = r"f:\dev\sleepy"
self.fs.create_file(test_file)
assert os.path.exists(test_file) is True
assert filesystem.recursive_listdir(test_file) == []
@pytest.mark.skipif(sys.platform.startswith("win"), reason="Broken on Windows")
class TestGetUniquePathFilename(ffs.TestCase):
# Basic fake filesystem setup stanza
def setUp(self):
self.setUpPyfakefs()
self.fs.path_separator = "/"
self.fs.is_case_sensitive = True
# Reduce the waiting time when the function calls check_mount()
@set_config({"wait_ext_drive": 1})
def test_nonexistent_dir(self):
# Absolute path
assert filesystem.get_unique_path("/foo/bar", n=0, create_dir=False) == "/foo/bar"
# Absolute path in a location that matters to check_mount
assert filesystem.get_unique_path("/mnt/foo/bar", n=0, create_dir=False) == "/mnt/foo/bar"
# Relative path
if self.fs.cwd != "/":
os.chdir("/")
assert filesystem.get_unique_path("foo/bar", n=0, create_dir=False) == "foo/bar"
def test_creating_dir(self):
# First call also creates the directory for us
assert filesystem.get_unique_path("/foo/bar", n=0, create_dir=True) == "/foo/bar"
# Verify creation of the path
assert os.path.exists("/foo/bar") is True
# Directories from previous loops get in the way
for dir_n in range(1, 11): # Go high enough for double digits
assert filesystem.get_unique_path("/foo/bar", n=0, create_dir=True) == "/foo/bar." + str(dir_n)
assert os.path.exists("/foo/bar." + str(dir_n)) is True
# Explicitly set parameter n
assert filesystem.get_unique_path("/foo/bar", n=666, create_dir=True) == "/foo/bar.666"
assert os.path.exists("/foo/bar.666") is True
def test_nonexistent_file(self):
assert filesystem.get_unique_filename("/dir/file.name") == "/dir/file.name"
# Relative path
assert filesystem.get_unique_filename("dir/file.name") == "dir/file.name"
def test_existing_file(self):
test_file = "/dir/file.name"
max_obstruct = 11 # High enough for double digits
self.fs.create_file(test_file)
assert os.path.exists(test_file)
# Create obstructions
for n in range(1, max_obstruct):
file_n = "/dir/file." + str(n) + ".name"
self.fs.create_file(file_n)
assert os.path.exists(file_n)
assert filesystem.get_unique_filename(test_file) == "/dir/file." + str(max_obstruct) + ".name"
def test_existing_file_without_extension(self):
test_file = "/some/filename"
# Create obstructions
self.fs.create_file(test_file)
assert os.path.exists(test_file)
assert filesystem.get_unique_filename(test_file) == "/some/filename.1"
class TestGetUniquePathFilenameWin(ffs.TestCase):
# Basic fake filesystem setup stanza
@set_platform("win32")
def setUp(self):
self.setUpPyfakefs()
self.fs.is_windows_fs = True
self.fs.path_separator = "\\"
self.fs.is_case_sensitive = False
# Reduce the waiting time when the function calls check_mount()
@set_config({"wait_ext_drive": 1})
def test_nonexistent_dir(self):
# Absolute path
assert filesystem.get_unique_path(r"C:\No\Such\Dir", n=0, create_dir=False).lower() == r"c:\no\such\dir"
# Relative path
assert filesystem.get_unique_path(r"foo\bar", n=0, create_dir=False).lower() == r"foo\bar"
def test_creating_dir(self):
# First call also creates the directory for us
assert filesystem.get_unique_path(r"C:\foo\BAR", n=0, create_dir=True).lower() == r"c:\foo\bar"
# Verify creation of the path
assert os.path.exists(r"c:\foo\bar") is True
# Directories from previous loops get in the way
for dir_n in range(1, 11): # Go high enough for double digits
assert filesystem.get_unique_path(r"c:\foo\bar", n=0, create_dir=True) == r"c:\foo\bar." + str(dir_n)
assert os.path.exists(r"c:\foo\bar." + str(dir_n)) is True
# Explicitly set parameter n
assert filesystem.get_unique_path(r"c:\Foo\Bar", n=666, create_dir=True).lower() == r"c:\foo\bar.666"
assert os.path.exists(r"c:\foo\bar.666") is True
def test_nonexistent_file(self):
assert filesystem.get_unique_filename(r"C:\DIR\file.name").lower() == r"c:\dir\file.name"
# Relative path
assert filesystem.get_unique_filename(r"DIR\file.name").lower() == r"dir\file.name"
def test_existing_file(self):
test_file = r"C:\dir\file.name"
max_obstruct = 11 # High enough for double digits
self.fs.create_file(test_file)
assert os.path.exists(test_file)
# Create obstructions
for n in range(1, max_obstruct):
file_n = r"C:\dir\file." + str(n) + ".name"
self.fs.create_file(file_n)
assert os.path.exists(file_n)
assert filesystem.get_unique_filename(test_file).lower() == r"c:\dir\file." + str(max_obstruct) + ".name"
def test_existing_file_without_extension(self):
test_file = r"c:\some\filename"
# Create obstructions
self.fs.create_file(test_file)
assert os.path.exists(test_file)
assert filesystem.get_unique_filename(test_file).lower() == r"c:\some\filename.1"
class TestSetPermissionsWin(ffs.TestCase):
@set_platform("win32")
def test_win32(self):
# Should not do or return anything on Windows
assert filesystem.set_permissions(r"F:\who\cares", recursive=False) is None
@pytest.mark.skipif(sys.platform.startswith("win"), reason="Broken on Windows")
class TestSetPermissions(ffs.TestCase):
# Basic fake filesystem setup stanza
def setUp(self):
self.setUpPyfakefs()
self.fs.path_separator = "/"
self.fs.is_case_sensitive = True
self.fs.umask = int("0755", 8) # rwxr-xr-x
def _runner(self, perms_test, perms_after):
"""
Generic test runner for permissions testing. The umask is set per test
via the relevant sab config option; the fileystem parameter in setUp().
Note that the umask set in the environment before starting the program
also affects the results if sabnzbd.cfg.umask isn't set.
Arguments:
str perms_test: permissions for test objects, chmod style "0755".
str perms_after: expected permissions after completion of the test.
"""
perms_test = int(perms_test, 8)
if sabnzbd.cfg.umask():
perms_after = int(perms_after, 8)
else:
perms_after = int("0777", 8) & (sabnzbd.ORG_UMASK ^ int("0777", 8))
# Setup and verify fake dir
test_dir = "/test"
try:
self.fs.create_dir(test_dir, perms_test)
except PermissionError:
ffs.set_uid(0)
self.fs.create_dir(test_dir, perms_test)
assert os.path.exists(test_dir) is True
assert stat.filemode(os.stat(test_dir).st_mode) == "d" + stat.filemode(perms_test)[1:]
# Setup and verify fake files
for file in (
"foobar",
"file.ext",
"sub/dir/.nzb",
"another/sub/dir/WithSome.File",
):
file = os.path.join(test_dir, file)
try:
self.fs.create_file(file, perms_test)
except PermissionError:
try:
ffs.set_uid(0)
self.fs.create_file(file, perms_test)
except Exception:
# Skip creating files, if not even using root gets the job done.
break
assert os.path.exists(file) is True
assert stat.filemode(os.stat(file).st_mode)[1:] == stat.filemode(perms_test)[1:]
# Set permissions, recursive by default
filesystem.set_permissions(test_dir)
# Check the results
for root, dirs, files in os.walk(test_dir):
for dir in [os.path.join(root, d) for d in dirs]:
# Permissions on directories should now match perms_after
assert stat.filemode(os.stat(dir).st_mode) == "d" + stat.filemode(perms_after)[1:]
for file in [os.path.join(root, f) for f in files]:
# Files also shouldn't have any executable or special bits set
assert (
stat.filemode(os.stat(file).st_mode)[1:]
== stat.filemode(
perms_after & ~(stat.S_ISUID | stat.S_ISGID | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH)
)[1:]
)
# Cleanup
ffs.set_uid(0)
self.fs.remove_object(test_dir)
assert os.path.exists(test_dir) is False
ffs.set_uid(global_uid)
@set_platform("linux")
@set_config({"umask": ""})
def test_dir0777_empty_umask_setting(self):
# World writable directory
self._runner("0777", "0700")
@set_platform("linux")
@set_config({"umask": ""})
def test_dir0450_empty_umask_setting(self):
# Insufficient access
self._runner("0450", "0700")
@set_platform("linux")
@set_config({"umask": ""})
def test_dir0000_empty_umask_setting(self):
# Weird directory permissions
self._runner("0000", "0700")
@set_platform("linux")
@set_config({"umask": "0760"})
def test_dir0777_umask0760_setting(self):
# World-writable directory, umask 760
self._runner("0777", "0760")
@set_platform("linux")
@set_config({"umask": "0617"})
def test_dir0450_umask0617_setting(self):
# Insufficient access, weird umask
self._runner("0450", "0717")
@set_platform("linux")
@set_config({"umask": "0000"})
def test_dir0405_umask0000_setting(self):
# Insufficient access on all fronts, weird umask
self._runner("0405", "0700")
@set_platform("linux")
@set_config({"umask": "2455"})
def test_dir0444_umask2455_setting(self):
# Insufficient access, weird umask with setgid
self._runner("0444", "2755")
@set_platform("linux")
@set_config({"umask": "4755"})
def test_dir1755_umask4755_setting(self):
# Sticky bit on directory, umask with setuid
self._runner("1755", "4755")

Loading…
Cancel
Save