Updates for tests on windows

- Other platform tests WIP
- Windows long directory tests WIP
This commit is contained in:
Andrew Senetar 2021-03-02 19:33:44 -06:00
parent a324923ffa
commit 9f76fbf036
Signed by: arsenetar
GPG Key ID: C63300DCE48AB2F1
2 changed files with 353 additions and 334 deletions

View File

@ -1,10 +1,9 @@
# encoding: utf-8 # encoding: utf-8
import pytest
import codecs import codecs
import unittest
import os import os
import sys
from os import path as op from os import path as op
import send2trash.plat_other
from send2trash.plat_other import send2trash as s2t
from send2trash.compat import PY3 from send2trash.compat import PY3
try: try:
@ -12,241 +11,241 @@ try:
except ImportError: except ImportError:
# py2 # py2
from ConfigParser import ConfigParser from ConfigParser import ConfigParser
from tempfile import mkdtemp, NamedTemporaryFile, mktemp from tempfile import mkdtemp, NamedTemporaryFile, mktemp
import shutil import shutil
import stat import stat
import sys
if sys.platform != "win32":
import send2trash.plat_other
from send2trash.plat_other import send2trash as s2t
HOMETRASH = send2trash.plat_other.HOMETRASH
else:
pytest.skip("Skipping non-windows tests", allow_module_level=True)
# Could still use cleaning up. But no longer relies on ramfs. # Could still use cleaning up. But no longer relies on ramfs.
# def touch(path):
HOMETRASH = send2trash.plat_other.HOMETRASH # with open(path, "a"):
# os.utime(path, None)
def touch(path): # class TestHomeTrash(unittest.TestCase):
with open(path, "a"): # def setUp(self):
os.utime(path, None) # self.file = NamedTemporaryFile(
# dir=op.expanduser("~"), prefix="send2trash_test", delete=False
# )
# def test_trash(self):
# s2t(self.file.name)
# self.assertFalse(op.exists(self.file.name))
# def tearDown(self):
# name = op.basename(self.file.name)
# os.remove(op.join(HOMETRASH, "files", name))
# os.remove(op.join(HOMETRASH, "info", name + ".trashinfo"))
class TestHomeTrash(unittest.TestCase): # class TestHomeMultiTrash(unittest.TestCase):
def setUp(self): # def setUp(self):
self.file = NamedTemporaryFile( # self.files = list(
dir=op.expanduser("~"), prefix="send2trash_test", delete=False # map(
) # lambda index: NamedTemporaryFile(
# dir=op.expanduser("~"),
# prefix="send2trash_test{}".format(index),
# delete=False,
# ),
# range(10),
# )
# )
def test_trash(self): # def test_multitrash(self):
s2t(self.file.name) # filenames = [file.name for file in self.files]
self.assertFalse(op.exists(self.file.name)) # s2t(filenames)
# self.assertFalse(any([op.exists(filename) for filename in filenames]))
def tearDown(self): # def tearDown(self):
name = op.basename(self.file.name) # filenames = [op.basename(file.name) for file in self.files]
os.remove(op.join(HOMETRASH, "files", name)) # [os.remove(op.join(HOMETRASH, "files", filename)) for filename in filenames]
os.remove(op.join(HOMETRASH, "info", name + ".trashinfo")) # [
# os.remove(op.join(HOMETRASH, "info", filename + ".trashinfo"))
# for filename in filenames
# ]
class TestHomeMultiTrash(unittest.TestCase): # def _filesys_enc():
def setUp(self): # enc = sys.getfilesystemencoding()
self.files = list( # # Get canonical name of codec
map( # return codecs.lookup(enc).name
lambda index: NamedTemporaryFile(
dir=op.expanduser("~"),
prefix="send2trash_test{}".format(index),
delete=False,
),
range(10),
)
)
def test_multitrash(self):
filenames = [file.name for file in self.files]
s2t(filenames)
self.assertFalse(any([op.exists(filename) for filename in filenames]))
def tearDown(self):
filenames = [op.basename(file.name) for file in self.files]
[os.remove(op.join(HOMETRASH, "files", filename)) for filename in filenames]
[
os.remove(op.join(HOMETRASH, "info", filename + ".trashinfo"))
for filename in filenames
]
def _filesys_enc(): # @unittest.skipIf(_filesys_enc() == "ascii", "ASCII filesystem")
enc = sys.getfilesystemencoding() # class TestUnicodeTrash(unittest.TestCase):
# Get canonical name of codec # def setUp(self):
return codecs.lookup(enc).name # self.name = u"send2trash_tést1"
# self.file = op.join(op.expanduser(b"~"), self.name.encode("utf-8"))
# touch(self.file)
# def test_trash_bytes(self):
# s2t(self.file)
# assert not op.exists(self.file)
# def test_trash_unicode(self):
# s2t(self.file.decode(sys.getfilesystemencoding()))
# assert not op.exists(self.file)
# def tearDown(self):
# if op.exists(self.file):
# os.remove(self.file)
# trash_file = op.join(HOMETRASH, "files", self.name)
# if op.exists(trash_file):
# os.remove(trash_file)
# os.remove(op.join(HOMETRASH, "info", self.name + ".trashinfo"))
@unittest.skipIf(_filesys_enc() == "ascii", "ASCII filesystem") # #
class TestUnicodeTrash(unittest.TestCase): # # Tests for files on some other volume than the user's home directory.
def setUp(self): # #
self.name = u"send2trash_tést1" # # What we need to stub:
self.file = op.join(op.expanduser(b"~"), self.name.encode("utf-8")) # # * plat_other.get_dev (to make sure the file will not be on the home dir dev)
touch(self.file) # # * os.path.ismount (to make our topdir look like a top dir)
# #
# class TestExtVol(unittest.TestCase):
# def setUp(self):
# self.trashTopdir = mkdtemp(prefix="s2t")
# if PY3:
# trashTopdir_b = os.fsencode(self.trashTopdir)
# else:
# trashTopdir_b = self.trashTopdir
# self.fileName = "test.txt"
# self.filePath = op.join(self.trashTopdir, self.fileName)
# touch(self.filePath)
def test_trash_bytes(self): # self.old_ismount = old_ismount = op.ismount
s2t(self.file) # self.old_getdev = send2trash.plat_other.get_dev
assert not op.exists(self.file)
def test_trash_unicode(self): # def s_getdev(path):
s2t(self.file.decode(sys.getfilesystemencoding())) # from send2trash.plat_other import is_parent
assert not op.exists(self.file)
def tearDown(self): # st = os.lstat(path)
if op.exists(self.file): # if is_parent(self.trashTopdir, path):
os.remove(self.file) # return "dev"
# return st.st_dev
trash_file = op.join(HOMETRASH, "files", self.name) # def s_ismount(path):
if op.exists(trash_file): # if op.realpath(path) in (
os.remove(trash_file) # op.realpath(self.trashTopdir),
os.remove(op.join(HOMETRASH, "info", self.name + ".trashinfo")) # op.realpath(trashTopdir_b),
# ):
# return True
# return old_ismount(path)
# send2trash.plat_other.os.path.ismount = s_ismount
# send2trash.plat_other.get_dev = s_getdev
# def tearDown(self):
# send2trash.plat_other.get_dev = self.old_getdev
# send2trash.plat_other.os.path.ismount = self.old_ismount
# shutil.rmtree(self.trashTopdir)
# # class TestTopdirTrash(TestExtVol):
# Tests for files on some other volume than the user's home directory. # def setUp(self):
# # TestExtVol.setUp(self)
# What we need to stub: # # Create a .Trash dir w/ a sticky bit
# * plat_other.get_dev (to make sure the file will not be on the home dir dev) # self.trashDir = op.join(self.trashTopdir, ".Trash")
# * os.path.ismount (to make our topdir look like a top dir) # os.mkdir(self.trashDir, 0o777 | stat.S_ISVTX)
#
class TestExtVol(unittest.TestCase):
def setUp(self):
self.trashTopdir = mkdtemp(prefix="s2t")
if PY3:
trashTopdir_b = os.fsencode(self.trashTopdir)
else:
trashTopdir_b = self.trashTopdir
self.fileName = "test.txt"
self.filePath = op.join(self.trashTopdir, self.fileName)
touch(self.filePath)
self.old_ismount = old_ismount = op.ismount # def test_trash(self):
self.old_getdev = send2trash.plat_other.get_dev # s2t(self.filePath)
# self.assertFalse(op.exists(self.filePath))
def s_getdev(path): # self.assertTrue(
from send2trash.plat_other import is_parent # op.exists(op.join(self.trashDir, str(os.getuid()), "files", self.fileName))
# )
st = os.lstat(path) # self.assertTrue(
if is_parent(self.trashTopdir, path): # op.exists(
return "dev" # op.join(
return st.st_dev # self.trashDir,
# str(os.getuid()),
def s_ismount(path): # "info",
if op.realpath(path) in ( # self.fileName + ".trashinfo",
op.realpath(self.trashTopdir), # )
op.realpath(trashTopdir_b), # )
): # )
return True # # info relative path (if another test is added, with the same fileName/Path,
return old_ismount(path) # # then it gets renamed etc.)
# cfg = ConfigParser()
send2trash.plat_other.os.path.ismount = s_ismount # cfg.read(
send2trash.plat_other.get_dev = s_getdev # op.join(
# self.trashDir, str(os.getuid()), "info", self.fileName + ".trashinfo"
def tearDown(self): # )
send2trash.plat_other.get_dev = self.old_getdev # )
send2trash.plat_other.os.path.ismount = self.old_ismount # self.assertEqual(self.fileName, cfg.get("Trash Info", "Path", raw=True))
shutil.rmtree(self.trashTopdir)
class TestTopdirTrash(TestExtVol): # # Test .Trash-UID
def setUp(self): # class TestTopdirTrashFallback(TestExtVol):
TestExtVol.setUp(self) # def test_trash(self):
# Create a .Trash dir w/ a sticky bit # touch(self.filePath)
self.trashDir = op.join(self.trashTopdir, ".Trash") # s2t(self.filePath)
os.mkdir(self.trashDir, 0o777 | stat.S_ISVTX) # self.assertFalse(op.exists(self.filePath))
# self.assertTrue(
def test_trash(self): # op.exists(
s2t(self.filePath) # op.join(
self.assertFalse(op.exists(self.filePath)) # self.trashTopdir,
self.assertTrue( # ".Trash-" + str(os.getuid()),
op.exists(op.join(self.trashDir, str(os.getuid()), "files", self.fileName)) # "files",
) # self.fileName,
self.assertTrue( # )
op.exists( # )
op.join( # )
self.trashDir,
str(os.getuid()),
"info",
self.fileName + ".trashinfo",
)
)
)
# info relative path (if another test is added, with the same fileName/Path,
# then it gets renamed etc.)
cfg = ConfigParser()
cfg.read(
op.join(
self.trashDir, str(os.getuid()), "info", self.fileName + ".trashinfo"
)
)
self.assertEqual(self.fileName, cfg.get("Trash Info", "Path", raw=True))
# Test .Trash-UID # # Test failure
class TestTopdirTrashFallback(TestExtVol): # class TestTopdirFailure(TestExtVol):
def test_trash(self): # def setUp(self):
touch(self.filePath) # TestExtVol.setUp(self)
s2t(self.filePath) # os.chmod(self.trashTopdir, 0o500) # not writable to induce the exception
self.assertFalse(op.exists(self.filePath))
self.assertTrue( # def test_trash(self):
op.exists( # with self.assertRaises(OSError):
op.join( # s2t(self.filePath)
self.trashTopdir, # self.assertTrue(op.exists(self.filePath))
".Trash-" + str(os.getuid()),
"files", # def tearDown(self):
self.fileName, # os.chmod(self.trashTopdir, 0o700) # writable to allow deletion
) # TestExtVol.tearDown(self)
)
)
# Test failure # # Make sure it will find the mount point properly for a file in a symlinked path
class TestTopdirFailure(TestExtVol): # class TestSymlink(TestExtVol):
def setUp(self): # def setUp(self):
TestExtVol.setUp(self) # TestExtVol.setUp(self)
os.chmod(self.trashTopdir, 0o500) # not writable to induce the exception # # Use mktemp (race conditioney but no symlink equivalent)
# # Since is_parent uses realpath(), and our getdev uses is_parent,
# # this should work
# self.slDir = mktemp(prefix="s2t", dir=op.expanduser("~"))
def test_trash(self): # os.mkdir(op.join(self.trashTopdir, "subdir"), 0o700)
with self.assertRaises(OSError): # self.filePath = op.join(self.trashTopdir, "subdir", self.fileName)
s2t(self.filePath) # touch(self.filePath)
self.assertTrue(op.exists(self.filePath)) # os.symlink(op.join(self.trashTopdir, "subdir"), self.slDir)
def tearDown(self): # def test_trash(self):
os.chmod(self.trashTopdir, 0o700) # writable to allow deletion # s2t(op.join(self.slDir, self.fileName))
TestExtVol.tearDown(self) # self.assertFalse(op.exists(self.filePath))
# self.assertTrue(
# op.exists(
# op.join(
# self.trashTopdir,
# ".Trash-" + str(os.getuid()),
# "files",
# self.fileName,
# )
# )
# )
# def tearDown(self):
# Make sure it will find the mount point properly for a file in a symlinked path # os.remove(self.slDir)
class TestSymlink(TestExtVol): # TestExtVol.tearDown(self)
def setUp(self):
TestExtVol.setUp(self)
# Use mktemp (race conditioney but no symlink equivalent)
# Since is_parent uses realpath(), and our getdev uses is_parent,
# this should work
self.slDir = mktemp(prefix="s2t", dir=op.expanduser("~"))
os.mkdir(op.join(self.trashTopdir, "subdir"), 0o700)
self.filePath = op.join(self.trashTopdir, "subdir", self.fileName)
touch(self.filePath)
os.symlink(op.join(self.trashTopdir, "subdir"), self.slDir)
def test_trash(self):
s2t(op.join(self.slDir, self.fileName))
self.assertFalse(op.exists(self.filePath))
self.assertTrue(
op.exists(
op.join(
self.trashTopdir,
".Trash-" + str(os.getuid()),
"files",
self.fileName,
)
)
)
def tearDown(self):
os.remove(self.slDir)
TestExtVol.tearDown(self)
if __name__ == "__main__":
unittest.main()

View File

@ -1,10 +1,9 @@
# coding: utf-8 # encoding: utf-8
import os import os
import shutil import shutil
import sys import sys
import unittest import pytest
from os import path as op from os import path as op
from tempfile import gettempdir
from send2trash import send2trash as s2t from send2trash import send2trash as s2t
@ -12,144 +11,165 @@ from send2trash import send2trash as s2t
if sys.platform == "win32": if sys.platform == "win32":
from send2trash.plat_win_modern import send2trash as s2t_modern from send2trash.plat_win_modern import send2trash as s2t_modern
from send2trash.plat_win_legacy import send2trash as s2t_legacy from send2trash.plat_win_legacy import send2trash as s2t_legacy
else:
pytest.skip("Skipping windows-only tests", allow_module_level=True)
@unittest.skipIf(sys.platform != "win32", "Windows only") def _create_tree(path):
class TestNormal(unittest.TestCase): dirname = op.dirname(path)
def setUp(self): if not op.isdir(dirname):
self.dirname = "\\\\?\\" + op.join(gettempdir(), "python.send2trash") os.makedirs(dirname)
self.file = op.join(self.dirname, "testfile.txt") with open(path, "w") as writer:
self._create_tree(self.file) writer.write("send2trash test")
self.files = [
op.join(self.dirname, "testfile{}.txt".format(index)) for index in range(10)
]
[self._create_tree(file) for file in self.files]
def tearDown(self):
shutil.rmtree(self.dirname, ignore_errors=True)
def _create_tree(self, path):
dirname = op.dirname(path)
if not op.isdir(dirname):
os.makedirs(dirname)
with open(path, "w") as writer:
writer.write("send2trash test")
def _trash_file(self, fcn):
fcn(self.file)
self.assertFalse(op.exists(self.file))
def _trash_multifile(self, fcn):
fcn(self.files)
self.assertFalse(any([op.exists(file) for file in self.files]))
def _file_not_found(self, fcn):
file = op.join(self.dirname, "otherfile.txt")
self.assertRaises(WindowsError, fcn, file)
def test_trash_file(self):
self._trash_file(s2t)
def test_trash_multifile(self):
self._trash_multifile(s2t)
def test_file_not_found(self):
self._file_not_found(s2t)
def test_trash_file_modern(self):
self._trash_file(s2t_modern)
def test_trash_multifile_modern(self):
self._trash_multifile(s2t_modern)
def test_file_not_found_modern(self):
self._file_not_found(s2t_modern)
def test_trash_file_legacy(self):
self._trash_file(s2t_legacy)
def test_trash_multifile_legacy(self):
self._trash_multifile(s2t_legacy)
def test_file_not_found_legacy(self):
self._file_not_found(s2t_legacy)
@unittest.skipIf(sys.platform != "win32", "Windows only") @pytest.fixture
class TestLongPath(unittest.TestCase): def testdir(tmp_path):
def setUp(self): dirname = "\\\\?\\" + str(tmp_path)
self.functions = {s2t: "auto", s2t_legacy: "legacy", s2t_modern: "modern"} yield dirname
filename = "A" * 100 shutil.rmtree(dirname, ignore_errors=True)
self.dirname = "\\\\?\\" + op.join(gettempdir(), filename)
path = op.join(
self.dirname,
filename,
filename, # From there, the path is not trashable from Explorer
filename,
filename + "{}.txt",
)
self.file = path.format("")
self._create_tree(self.file)
self.files = [path.format(index) for index in range(10)]
[self._create_tree(file) for file in self.files]
def tearDown(self):
shutil.rmtree(self.dirname, ignore_errors=True)
def _create_tree(self, path): @pytest.fixture
dirname = op.dirname(path) def testfile(testdir):
if not op.isdir(dirname): file = op.join(testdir, "testfile.txt")
os.makedirs(dirname) _create_tree(file)
with open(path, "w") as writer: yield file
writer.write("Looong filename!") # Note dir will cleanup the file
def _trash_file(self, fcn):
fcn(self.file)
self.assertFalse(op.exists(self.file))
def _trash_multifile(self, fcn): @pytest.fixture
fcn(self.files) def testfiles(testdir):
self.assertFalse(any([op.exists(file) for file in self.files])) files = [op.join(testdir, "testfile{}.txt".format(index)) for index in range(10)]
[_create_tree(file) for file in files]
yield files
# Note dir will cleanup the files
def _trash_folder(self, fcn):
fcn(self.dirname)
self.assertFalse(op.exists(self.dirname))
def test_trash_file(self): def _trash_folder(dir, fcn):
self._trash_file(s2t) fcn(dir)
assert op.exists(dir) is False
def test_trash_multifile(self):
self._trash_multifile(s2t)
@unittest.skipIf( def _trash_file(file, fcn):
op.splitdrive(os.getcwd())[0] != op.splitdrive(gettempdir())[0], fcn(file)
"Cannot trash long path from other drive", assert op.exists(file) is False
)
def test_trash_folder(self):
self._trash_folder(s2t)
def test_trash_file_modern(self):
self._trash_file(s2t_modern)
def test_trash_multifile_modern(self): def _trash_multifile(files, fcn):
self._trash_multifile(s2t_modern) fcn(files)
assert any([op.exists(file) for file in files]) is False
@unittest.skipIf(
op.splitdrive(os.getcwd())[0] != op.splitdrive(gettempdir())[0],
"Cannot trash long path from other drive",
)
def test_trash_folder_modern(self):
self._trash_folder(s2t_modern)
def test_trash_file_legacy(self): def _file_not_found(dir, fcn):
self._trash_file(s2t_legacy) file = op.join(dir, "otherfile.txt")
pytest.raises(WindowsError, fcn, file)
def test_trash_multifile_legacy(self):
self._trash_multifile(s2t_legacy)
@unittest.skipIf( def test_trash_folder(testdir):
op.splitdrive(os.getcwd())[0] != op.splitdrive(gettempdir())[0], _trash_folder(testdir, s2t)
"Cannot trash long path from other drive",
)
def test_trash_folder_legacy(self): def test_trash_file(testfile):
self._trash_folder(s2t_legacy) _trash_file(testfile, s2t)
def test_trash_multifile(testfiles):
_trash_multifile(testfiles, s2t)
def test_file_not_found(testdir):
_file_not_found(testdir, s2t)
def test_trash_folder_modern(testdir):
_trash_folder(testdir, s2t_modern)
def test_trash_file_modern(testfile):
_trash_file(testfile, s2t_modern)
def test_trash_multifile_modern(testfiles):
_trash_multifile(testfiles, s2t_modern)
def test_file_not_found_modern(testdir):
_file_not_found(testdir, s2t_modern)
def test_trash_folder_legacy(testdir):
_trash_folder(testdir, s2t_legacy)
def test_trash_file_legacy(testfile):
_trash_file(testfile, s2t_legacy)
def test_trash_multifile_legacy(testfiles):
_trash_multifile(testfiles, s2t_legacy)
def test_file_not_found_legacy(testdir):
_file_not_found(testdir, s2t_legacy)
# Long path tests
@pytest.fixture
def longdir(tmp_path):
dirname = "\\\\?\\" + str(tmp_path)
name = "A" * 100
yield op.join(dirname, name, name, name)
shutil.rmtree(dirname, ignore_errors=True)
@pytest.fixture
def longfile(longdir):
name = "A" * 100
path = op.join(longdir, name + "{}.txt")
file = path.format("")
_create_tree(file)
yield file
@pytest.fixture
def longfiles(longdir):
name = "A" * 100
path = op.join(longdir, name + "{}.txt")
files = [path.format(index) for index in range(10)]
[_create_tree(file) for file in files]
yield files
# NOTE: both legacy and modern test "pass" on windows, but actually are not moving files to the
# recycle bin, this was tested on latest windows 10, thought to have worked previously
def test_trash_long_file_modern(longfile):
_trash_file(longfile, s2t_modern)
def test_trash_long_multifile_modern(longfiles):
_trash_multifile(longfiles, s2t_modern)
# @pytest.skipif(
# op.splitdrive(os.getcwd())[0] != op.splitdrive(gettempdir())[0],
# "Cannot trash long path from other drive",
# )
# def test_trash_long_folder_modern(self):
# self._trash_folder(s2t_modern)
def test_trash_long_file_legacy(longfile):
_trash_file(longfile, s2t_legacy)
def test_trash_long_multifile_legacy(longfiles):
_trash_multifile(longfiles, s2t_legacy)
# @pytest.skipif(
# op.splitdrive(os.getcwd())[0] != op.splitdrive(gettempdir())[0],
# "Cannot trash long path from other drive",
# )
# def test_trash_long_folder_legacy(self):
# self._trash_folder(s2t_legacy)