Add unit tests for multi-file calls

This commit is contained in:
Andrew Senetar 2020-06-03 00:57:59 -05:00
parent 0d1d5ab40a
commit 41721642e2
Signed by: arsenetar
GPG Key ID: C63300DCE48AB2F1
2 changed files with 138 additions and 42 deletions

View File

@ -6,6 +6,7 @@ from os import path as op
import send2trash.plat_other
from send2trash.plat_other import send2trash as s2t
from send2trash.compat import PY3
try:
from configparser import ConfigParser
except ImportError:
@ -15,18 +16,22 @@ from tempfile import mkdtemp, NamedTemporaryFile, mktemp
import shutil
import stat
import sys
# Could still use cleaning up. But no longer relies on ramfs.
HOMETRASH = send2trash.plat_other.HOMETRASH
def touch(path):
with open(path, 'a'):
with open(path, "a"):
os.utime(path, None)
class TestHomeTrash(unittest.TestCase):
def setUp(self):
self.file = NamedTemporaryFile(
dir=op.expanduser("~"), prefix='send2trash_test', delete=False)
dir=op.expanduser("~"), prefix="send2trash_test", delete=False
)
def test_trash(self):
s2t(self.file.name)
@ -34,8 +39,35 @@ class TestHomeTrash(unittest.TestCase):
def tearDown(self):
name = op.basename(self.file.name)
os.remove(op.join(HOMETRASH, 'files', name))
os.remove(op.join(HOMETRASH, 'info', name+'.trashinfo'))
os.remove(op.join(HOMETRASH, "files", name))
os.remove(op.join(HOMETRASH, "info", name + ".trashinfo"))
class TestHomeMultiTrash(unittest.TestCase):
def setUp(self):
self.files = list(
map(
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():
@ -43,11 +75,12 @@ def _filesys_enc():
# Get canonical name of codec
return codecs.lookup(enc).name
@unittest.skipIf(_filesys_enc() == 'ascii', 'ASCII filesystem')
@unittest.skipIf(_filesys_enc() == "ascii", "ASCII filesystem")
class TestUnicodeTrash(unittest.TestCase):
def setUp(self):
self.name = u'send2trash_tést1'
self.file = op.join(op.expanduser(b'~'), self.name.encode('utf-8'))
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):
@ -62,10 +95,11 @@ class TestUnicodeTrash(unittest.TestCase):
if op.exists(self.file):
os.remove(self.file)
trash_file = op.join(HOMETRASH, 'files', self.name)
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'))
os.remove(op.join(HOMETRASH, "info", self.name + ".trashinfo"))
#
# Tests for files on some other volume than the user's home directory.
@ -76,25 +110,31 @@ class TestUnicodeTrash(unittest.TestCase):
#
class TestExtVol(unittest.TestCase):
def setUp(self):
self.trashTopdir = mkdtemp(prefix='s2t')
self.trashTopdir = mkdtemp(prefix="s2t")
if PY3:
trashTopdir_b = os.fsencode(self.trashTopdir)
else:
trashTopdir_b = self.trashTopdir
self.fileName = 'test.txt'
self.fileName = "test.txt"
self.filePath = op.join(self.trashTopdir, self.fileName)
touch(self.filePath)
self.old_ismount = old_ismount = op.ismount
self.old_getdev = send2trash.plat_other.get_dev
def s_getdev(path):
from send2trash.plat_other import is_parent
st = os.lstat(path)
if is_parent(self.trashTopdir, path):
return 'dev'
return "dev"
return st.st_dev
def s_ismount(path):
if op.realpath(path) in (op.realpath(self.trashTopdir), op.realpath(trashTopdir_b)):
if op.realpath(path) in (
op.realpath(self.trashTopdir),
op.realpath(trashTopdir_b),
):
return True
return old_ismount(path)
@ -106,23 +146,40 @@ class TestExtVol(unittest.TestCase):
send2trash.plat_other.os.path.ismount = self.old_ismount
shutil.rmtree(self.trashTopdir)
class TestTopdirTrash(TestExtVol):
def setUp(self):
TestExtVol.setUp(self)
# Create a .Trash dir w/ a sticky bit
self.trashDir = op.join(self.trashTopdir, '.Trash')
os.mkdir(self.trashDir, 0o777|stat.S_ISVTX)
self.trashDir = op.join(self.trashTopdir, ".Trash")
os.mkdir(self.trashDir, 0o777 | stat.S_ISVTX)
def test_trash(self):
s2t(self.filePath)
self.assertFalse(op.exists(self.filePath))
self.assertTrue(op.exists(op.join(self.trashDir, str(os.getuid()), 'files', self.fileName)))
self.assertTrue(op.exists(op.join(self.trashDir, str(os.getuid()), 'info', self.fileName + '.trashinfo')))
self.assertTrue(
op.exists(op.join(self.trashDir, str(os.getuid()), "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))
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
class TestTopdirTrashFallback(TestExtVol):
@ -130,13 +187,23 @@ class TestTopdirTrashFallback(TestExtVol):
touch(self.filePath)
s2t(self.filePath)
self.assertFalse(op.exists(self.filePath))
self.assertTrue(op.exists(op.join(self.trashTopdir, '.Trash-' + str(os.getuid()), 'files', self.fileName)))
self.assertTrue(
op.exists(
op.join(
self.trashTopdir,
".Trash-" + str(os.getuid()),
"files",
self.fileName,
)
)
)
# Test failure
class TestTopdirFailure(TestExtVol):
def setUp(self):
TestExtVol.setUp(self)
os.chmod(self.trashTopdir, 0o500) # not writable to induce the exception
os.chmod(self.trashTopdir, 0o500) # not writable to induce the exception
def test_trash(self):
with self.assertRaises(OSError):
@ -144,9 +211,10 @@ class TestTopdirFailure(TestExtVol):
self.assertTrue(op.exists(self.filePath))
def tearDown(self):
os.chmod(self.trashTopdir, 0o700) # writable to allow deletion
os.chmod(self.trashTopdir, 0o700) # writable to allow deletion
TestExtVol.tearDown(self)
# Make sure it will find the mount point properly for a file in a symlinked path
class TestSymlink(TestExtVol):
def setUp(self):
@ -154,21 +222,31 @@ class TestSymlink(TestExtVol):
# 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('~'))
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)
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)
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)))
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__':
if __name__ == "__main__":
unittest.main()

View File

@ -9,12 +9,16 @@ from tempfile import gettempdir
from send2trash import send2trash as s2t
@unittest.skipIf(sys.platform != 'win32', 'Windows only')
@unittest.skipIf(sys.platform != "win32", "Windows only")
class TestNormal(unittest.TestCase):
def setUp(self):
self.dirname = '\\\\?\\' + op.join(gettempdir(), 'python.send2trash')
self.file = op.join(self.dirname, 'testfile.txt')
self.dirname = "\\\\?\\" + op.join(gettempdir(), "python.send2trash")
self.file = op.join(self.dirname, "testfile.txt")
self._create_tree(self.file)
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)
@ -23,29 +27,38 @@ class TestNormal(unittest.TestCase):
dirname = op.dirname(path)
if not op.isdir(dirname):
os.makedirs(dirname)
with open(path, 'w') as writer:
writer.write('send2trash test')
with open(path, "w") as writer:
writer.write("send2trash test")
def test_trash_file(self):
s2t(self.file)
self.assertFalse(op.exists(self.file))
def test_trash_multifile(self):
s2t(self.files)
self.assertFalse(any([op.exists(file) for file in self.files]))
def test_file_not_found(self):
file = op.join(self.dirname, 'otherfile.txt')
file = op.join(self.dirname, "otherfile.txt")
self.assertRaises(WindowsError, s2t, file)
@unittest.skipIf(sys.platform != 'win32', 'Windows only')
@unittest.skipIf(sys.platform != "win32", "Windows only")
class TestLongPath(unittest.TestCase):
def setUp(self):
filename = 'A' * 100
self.dirname = '\\\\?\\' + op.join(gettempdir(), filename)
self.file = op.join(
filename = "A" * 100
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')
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)
@ -54,16 +67,21 @@ class TestLongPath(unittest.TestCase):
dirname = op.dirname(path)
if not op.isdir(dirname):
os.makedirs(dirname)
with open(path, 'w') as writer:
writer.write('Looong filename!')
with open(path, "w") as writer:
writer.write("Looong filename!")
def test_trash_file(self):
s2t(self.file)
self.assertFalse(op.exists(self.file))
def test_trash_multifile(self):
s2t(self.files)
self.assertFalse(any([op.exists(file) for file in self.files]))
@unittest.skipIf(
op.splitdrive(os.getcwd())[0] != op.splitdrive(gettempdir())[0],
'Cannot trash long path from other drive')
"Cannot trash long path from other drive",
)
def test_trash_folder(self):
s2t(self.dirname)
self.assertFalse(op.exists(self.dirname))