From 9f76fbf036f2315d9236bb53380013a517f5f32f Mon Sep 17 00:00:00 2001 From: Andrew Senetar Date: Tue, 2 Mar 2021 19:33:44 -0600 Subject: [PATCH] Updates for tests on windows - Other platform tests WIP - Windows long directory tests WIP --- tests/test_plat_other.py | 415 +++++++++++++++++++-------------------- tests/test_plat_win.py | 272 +++++++++++++------------ 2 files changed, 353 insertions(+), 334 deletions(-) diff --git a/tests/test_plat_other.py b/tests/test_plat_other.py index 32ff306..38a48c4 100644 --- a/tests/test_plat_other.py +++ b/tests/test_plat_other.py @@ -1,10 +1,9 @@ # encoding: utf-8 +import pytest import codecs -import unittest import os +import sys from os import path as op -import send2trash.plat_other -from send2trash.plat_other import send2trash as s2t from send2trash.compat import PY3 try: @@ -12,241 +11,241 @@ try: except ImportError: # py2 from ConfigParser import ConfigParser + from tempfile import mkdtemp, NamedTemporaryFile, mktemp import shutil 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. - -HOMETRASH = send2trash.plat_other.HOMETRASH +# def touch(path): +# with open(path, "a"): +# os.utime(path, None) -def touch(path): - 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 +# ) + +# 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): - def setUp(self): - self.file = NamedTemporaryFile( - dir=op.expanduser("~"), prefix="send2trash_test", delete=False - ) +# 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_trash(self): - s2t(self.file.name) - self.assertFalse(op.exists(self.file.name)) +# 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): - name = op.basename(self.file.name) - os.remove(op.join(HOMETRASH, "files", name)) - os.remove(op.join(HOMETRASH, "info", name + ".trashinfo")) +# 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 +# ] -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(): +# enc = sys.getfilesystemencoding() +# # Get canonical name of codec +# return codecs.lookup(enc).name -def _filesys_enc(): - enc = sys.getfilesystemencoding() - # Get canonical name of codec - return codecs.lookup(enc).name +# @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")) +# 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): - def setUp(self): - self.name = u"send2trash_tést1" - self.file = op.join(op.expanduser(b"~"), self.name.encode("utf-8")) - touch(self.file) +# # +# # Tests for files on some other volume than the user's home directory. +# # +# # What we need to stub: +# # * plat_other.get_dev (to make sure the file will not be on the home dir dev) +# # * 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): - s2t(self.file) - assert not op.exists(self.file) +# self.old_ismount = old_ismount = op.ismount +# self.old_getdev = send2trash.plat_other.get_dev - def test_trash_unicode(self): - s2t(self.file.decode(sys.getfilesystemencoding())) - assert not op.exists(self.file) +# def s_getdev(path): +# from send2trash.plat_other import is_parent - def tearDown(self): - if op.exists(self.file): - os.remove(self.file) +# st = os.lstat(path) +# if is_parent(self.trashTopdir, path): +# return "dev" +# return st.st_dev - 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")) +# def s_ismount(path): +# if op.realpath(path) in ( +# op.realpath(self.trashTopdir), +# 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) -# -# Tests for files on some other volume than the user's home directory. -# -# What we need to stub: -# * plat_other.get_dev (to make sure the file will not be on the home dir dev) -# * 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) +# 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.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 st.st_dev - - def s_ismount(path): - if op.realpath(path) in ( - op.realpath(self.trashTopdir), - 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) +# 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", +# ) +# ) +# ) +# # 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)) -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) - - 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", - ) - ) - ) - # 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 +# class TestTopdirTrashFallback(TestExtVol): +# def test_trash(self): +# 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, +# ) +# ) +# ) -# Test .Trash-UID -class TestTopdirTrashFallback(TestExtVol): - def test_trash(self): - 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, - ) - ) - ) +# # Test failure +# class TestTopdirFailure(TestExtVol): +# def setUp(self): +# TestExtVol.setUp(self) +# os.chmod(self.trashTopdir, 0o500) # not writable to induce the exception + +# def test_trash(self): +# with self.assertRaises(OSError): +# s2t(self.filePath) +# self.assertTrue(op.exists(self.filePath)) + +# def tearDown(self): +# os.chmod(self.trashTopdir, 0o700) # writable to allow deletion +# TestExtVol.tearDown(self) -# Test failure -class TestTopdirFailure(TestExtVol): - def setUp(self): - TestExtVol.setUp(self) - os.chmod(self.trashTopdir, 0o500) # not writable to induce the exception +# # Make sure it will find the mount point properly for a file in a symlinked path +# class TestSymlink(TestExtVol): +# 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("~")) - def test_trash(self): - with self.assertRaises(OSError): - s2t(self.filePath) - self.assertTrue(op.exists(self.filePath)) +# 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 tearDown(self): - os.chmod(self.trashTopdir, 0o700) # writable to allow deletion - TestExtVol.tearDown(self) +# 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, +# ) +# ) +# ) - -# Make sure it will find the mount point properly for a file in a symlinked path -class TestSymlink(TestExtVol): - 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() +# def tearDown(self): +# os.remove(self.slDir) +# TestExtVol.tearDown(self) diff --git a/tests/test_plat_win.py b/tests/test_plat_win.py index 5f0a4d5..9df6dda 100644 --- a/tests/test_plat_win.py +++ b/tests/test_plat_win.py @@ -1,10 +1,9 @@ -# coding: utf-8 +# encoding: utf-8 import os import shutil import sys -import unittest +import pytest from os import path as op -from tempfile import gettempdir from send2trash import send2trash as s2t @@ -12,144 +11,165 @@ from send2trash import send2trash as s2t if sys.platform == "win32": from send2trash.plat_win_modern import send2trash as s2t_modern 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") -class TestNormal(unittest.TestCase): - def setUp(self): - 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) - - 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) +def _create_tree(path): + dirname = op.dirname(path) + if not op.isdir(dirname): + os.makedirs(dirname) + with open(path, "w") as writer: + writer.write("send2trash test") -@unittest.skipIf(sys.platform != "win32", "Windows only") -class TestLongPath(unittest.TestCase): - def setUp(self): - self.functions = {s2t: "auto", s2t_legacy: "legacy", s2t_modern: "modern"} - 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", - ) - 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] +@pytest.fixture +def testdir(tmp_path): + dirname = "\\\\?\\" + str(tmp_path) + yield dirname + shutil.rmtree(dirname, ignore_errors=True) - 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("Looong filename!") +@pytest.fixture +def testfile(testdir): + file = op.join(testdir, "testfile.txt") + _create_tree(file) + yield file + # 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): - fcn(self.files) - self.assertFalse(any([op.exists(file) for file in self.files])) +@pytest.fixture +def testfiles(testdir): + 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): - self._trash_file(s2t) +def _trash_folder(dir, fcn): + fcn(dir) + assert op.exists(dir) is False - def test_trash_multifile(self): - self._trash_multifile(s2t) - @unittest.skipIf( - op.splitdrive(os.getcwd())[0] != op.splitdrive(gettempdir())[0], - "Cannot trash long path from other drive", - ) - def test_trash_folder(self): - self._trash_folder(s2t) +def _trash_file(file, fcn): + fcn(file) + assert op.exists(file) is False - def test_trash_file_modern(self): - self._trash_file(s2t_modern) - def test_trash_multifile_modern(self): - self._trash_multifile(s2t_modern) +def _trash_multifile(files, fcn): + 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): - self._trash_file(s2t_legacy) +def _file_not_found(dir, fcn): + file = op.join(dir, "otherfile.txt") + pytest.raises(WindowsError, fcn, file) - def test_trash_multifile_legacy(self): - self._trash_multifile(s2t_legacy) - @unittest.skipIf( - op.splitdrive(os.getcwd())[0] != op.splitdrive(gettempdir())[0], - "Cannot trash long path from other drive", - ) - def test_trash_folder_legacy(self): - self._trash_folder(s2t_legacy) +def test_trash_folder(testdir): + _trash_folder(testdir, s2t) + + +def test_trash_file(testfile): + _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)