Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion tests/test_list/cmd/test_end_to_end_list.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ def test_help(self):
self.assertEqual(reformat_help_message("""\
usage: trash-list [-h] [--print-completion {bash,zsh,tcsh}] [--version]
[--volumes] [--trash-dirs] [--trash-dir TRASH_DIRS]
[--all-users]
[--all-users] [--sort {date,path,none}]

List trashed files

Expand All @@ -68,6 +68,9 @@ def test_help(self):
--trash-dir TRASH_DIRS
specify the trash directory to use
--all-users list trashcans of all the users
--sort {date,path,none}
sort trashed files by date, path, or none (default:
'none')

Report bugs to https://github.com/andreafrancia/trash-cli/issues
"""), result.stderr + result.reformatted_help())
Expand Down
131 changes: 131 additions & 0 deletions tests/test_list/cmd/test_trash_list.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# Copyright (C) 2011-2024 Andrea Francia Trivolzio(PV) Italy

import datetime

from tests.test_list.cmd.support.trash_list_user import trash_list_user

user = trash_list_user
Expand Down Expand Up @@ -74,3 +76,132 @@ def test_should_warn_about_unexistent_path_entry(self, user):
"Parse Error: /xdg-data-home/Trash/info/foo.trashinfo: "
"Unable to parse Path.\n",
'')

def test_sort_by_date(self, user):
user.home_trash_dir().add_trashinfo4('/file_b', "2000-01-01 00:00:03")
user.home_trash_dir().add_trashinfo4('/file_a', "2000-01-01 00:00:01")
user.home_trash_dir().add_trashinfo4('/file_c', "2000-01-01 00:00:02")

output = user.run_trash_list('--sort=date')

assert output.stdout == (
"2000-01-01 00:00:01 /file_a\n"
"2000-01-01 00:00:02 /file_c\n"
"2000-01-01 00:00:03 /file_b\n")

def test_sort_by_date_same_timestamp_sorts_by_path(self, user):
user.home_trash_dir().add_trashinfo4('/file_c', "2000-01-01 00:00:01")
user.home_trash_dir().add_trashinfo4('/file_a', "2000-01-01 00:00:01")
user.home_trash_dir().add_trashinfo4('/file_b', "2000-01-01 00:00:01")

output = user.run_trash_list('--sort=date')

assert output.stdout == (
"2000-01-01 00:00:01 /file_a\n"
"2000-01-01 00:00:01 /file_b\n"
"2000-01-01 00:00:01 /file_c\n")

def test_sort_by_path(self, user):
user.home_trash_dir().add_trashinfo4('/file_c', "2000-01-01 00:00:01")
user.home_trash_dir().add_trashinfo4('/file_a', "2000-01-01 00:00:03")
user.home_trash_dir().add_trashinfo4('/file_b', "2000-01-01 00:00:02")

output = user.run_trash_list('--sort=path')

assert output.stdout == (
"2000-01-01 00:00:03 /file_a\n"
"2000-01-01 00:00:02 /file_b\n"
"2000-01-01 00:00:01 /file_c\n")

def test_sort_by_path_same_path_sorts_by_date(self, user):
user.home_trash_dir().add_trashinfo4('/same_file', "2000-01-01 00:00:03")
user.home_trash_dir().add_trashinfo4('/same_file', "2000-01-01 00:00:01")
user.home_trash_dir().add_trashinfo4('/same_file', "2000-01-01 00:00:02")

output = user.run_trash_list('--sort=path')

assert output.stdout == (
"2000-01-01 00:00:01 /same_file\n"
"2000-01-01 00:00:02 /same_file\n"
"2000-01-01 00:00:03 /same_file\n")

def test_sort_none_lists_all_entries(self, user):
user.home_trash_dir().add_trashinfo4('/file_b', "2000-01-01 00:00:03")
user.home_trash_dir().add_trashinfo4('/file_a', "2000-01-01 00:00:01")
user.home_trash_dir().add_trashinfo4('/file_c', "2000-01-01 00:00:02")

output = user.run_trash_list('--sort=none')

assert output.all_lines() == {"2000-01-01 00:00:01 /file_a",
"2000-01-01 00:00:02 /file_c",
"2000-01-01 00:00:03 /file_b"}

def test_sort_by_date_unknown_dates_sort_last(self, user):
user.home_trash_dir().add_trashinfo4('/known', "2000-01-01 00:00:01")
user.home_trash_dir().add_trashinfo_without_date('a-no-date')
user.home_trash_dir().add_trashinfo_wrong_date('b-bad-date',
'Wrong date')

output = user.run_trash_list('--sort=date')

assert output.stdout == (
"2000-01-01 00:00:01 /known\n"
"????-??-?? ??:??:?? /a-no-date\n"
"????-??-?? ??:??:?? /b-bad-date\n")

def test_sort_by_path_with_unknown_dates(self, user):
user.home_trash_dir().add_trashinfo_without_date('z-no-date')
user.home_trash_dir().add_trashinfo4('/a-known', "2000-01-01 00:00:01")

output = user.run_trash_list('--sort=path')

assert output.stdout == (
"2000-01-01 00:00:01 /a-known\n"
"????-??-?? ??:??:?? /z-no-date\n")

def test_sort_with_parse_errors_keeps_errors_on_stderr(self, user):
user.home_trash_dir().add_trashinfo4('/file_b', "2000-01-01 00:00:02")
user.home_trash_dir().add_trashinfo4('/file_a', "2000-01-01 00:00:01")
user.home_trash_dir().add_trashinfo_content('broken', '')

output = user.run_trash_list('--sort=date')

assert output.stdout == (
"2000-01-01 00:00:01 /file_a\n"
"2000-01-01 00:00:02 /file_b\n")
assert output.stderr == (
"Parse Error: /xdg-data-home/Trash/info/broken.trashinfo: "
"Unable to parse Path.\n")

def test_sort_by_path_with_files_flag(self, user):
user.home_trash_dir().add_trashinfo4('/file_b', "2000-01-01 00:00:01")
user.home_trash_dir().add_trashinfo4('/file_a', "2000-01-01 00:00:01")

output = user.run_trash_list('--sort=path', '--files')

lines = output.stdout.splitlines()
assert len(lines) == 2
assert lines[0].startswith("2000-01-01 00:00:01 /file_a -> ")
assert lines[1].startswith("2000-01-01 00:00:01 /file_b -> ")

def test_sort_by_path_with_size_flag(self, user):
user.home_trash_dir().add_trashed_file('big', '/file_a', 'X' * 10000)
user.home_trash_dir().add_trashed_file('small', '/file_b', 'X')

output = user.run_trash_list('--size', '--sort=path')

assert output.stdout == (
"10000 /file_a\n"
"1 /file_b\n")

def test_sort_by_date_with_size_flag(self, user):
user.home_trash_dir().add_trashed_file(
'a', '/file_a', 'X' * 100, datetime.datetime(2000, 1, 1, 0, 0, 2))
user.home_trash_dir().add_trashed_file(
'b', '/file_b', 'X', datetime.datetime(2000, 1, 1, 0, 0, 1))

output = user.run_trash_list('--size', '--sort=date')

assert output.stdout == (
"1 /file_b\n"
"100 /file_a\n")
15 changes: 15 additions & 0 deletions tests/test_list/components/test_trash_list_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,5 +45,20 @@ def test_files_on(self):

assert True == args.show_files

def test_sort_default(self):
args = self.parse([])

assert 'none' == args.sort

def test_sort_date(self):
args = self.parse(['--sort=date'])

assert 'date' == args.sort

def test_sort_path(self):
args = self.parse(['--sort=path'])

assert 'path' == args.sort

def parse(self, args):
return self.parser.parse_list_args(args, 'trash-list')
36 changes: 28 additions & 8 deletions trashcli/list/list_trash_action.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
from trashcli.lib.trash_dir_reader import TrashDirReader
from trashcli.list.extractors import DeletionDateExtractor
from trashcli.list.extractors import SizeExtractor
from trashcli.parse_trashinfo.maybe_parse_deletion_date import \
maybe_parse_deletion_date
from trashcli.parse_trashinfo.parse_path import parse_path
from trashcli.parse_trashinfo.parser_error import ParseError
from trashcli.trash_dirs_scanner import trash_dir_found
Expand All @@ -24,6 +26,7 @@ class ListTrashArgs(
('attribute_to_print', str),
('show_files', bool),
('all_users', bool),
('sort', str),
])):
pass

Expand All @@ -49,12 +52,26 @@ def __init__(self,
def run_action(self,
args, # type: ListTrashArgs
):
for message in ListTrash(self.environ,
self.uid,
self.selector,
self.dir_reader,
self.content_reader).list_all_trash(args):
self.print_event(message)
events = ListTrash(self.environ,
self.uid,
self.selector,
self.dir_reader,
self.content_reader).list_all_trash(args)
if args.sort != 'none':
events = list(events)
output_positions = [i for i, e in enumerate(events)
if isinstance(e, Output)]
outputs = [events[i] for i in output_positions]
if args.sort == 'date':
outputs.sort(key=lambda e: (e.deletion_date,
e.original_location))
elif args.sort == 'path':
outputs.sort(key=lambda e: (e.original_location,
e.deletion_date))
for pos, sorted_output in zip(output_positions, outputs):
events[pos] = sorted_output
for event in events:
self.print_event(event)

def print_event(self, event):
if isinstance(event, Error):
Expand Down Expand Up @@ -128,6 +145,7 @@ def _print_trashinfo(self,
else:
attribute = extractor.extract_attribute(trashinfo_path,
contents)
deletion_date = "%s" % maybe_parse_deletion_date(contents)
original_location = os.path.join(volume, relative_location)

if show_files:
Expand All @@ -136,7 +154,7 @@ def _print_trashinfo(self,
original_file)
else:
line = format_line(attribute, original_location)
yield Output(line)
yield Output(line, deletion_date, original_location)

def top_trashdir_skipped_because_parent_is_symlink(self, trashdir):
return "TrashDir skipped because parent is symlink: %s" % trashdir
Expand All @@ -158,8 +176,10 @@ def __init__(self, error):


class Output(Event):
def __init__(self, message):
def __init__(self, message, deletion_date, original_location):
self.message = message
self.deletion_date = deletion_date
self.original_location = original_location


def format_line(attribute, original_location):
Expand Down
9 changes: 8 additions & 1 deletion trashcli/list/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,12 @@ def __init__(self, prog):
action='store_true',
dest='all_users',
help='list trashcans of all the users')
self.parser.add_argument('--sort',
choices=['date', 'path', 'none'],
default='none',
dest='sort',
help="sort trashed files by date, path, "
"or none (default: 'none')")
self.parser.add_argument('--python',
dest='action',
action='store_const',
Expand Down Expand Up @@ -98,7 +104,8 @@ def parse_list_args(self,
trash_dirs=parsed.trash_dirs,
attribute_to_print=parsed.attribute_to_print,
show_files=parsed.show_files,
all_users=parsed.all_users
all_users=parsed.all_users,
sort=parsed.sort
)
if parsed.action == ListAction.print_python_executable:
return PrintPythonExecutableArgs()
Expand Down