PageRenderTime 36ms CodeModel.GetById 23ms RepoModel.GetById 1ms app.codeStats 0ms

/integration_tests/describe_trash_list.py

https://gitlab.com/loic-corbasson/trash-cli
Python | 301 lines | 291 code | 9 blank | 1 comment | 0 complexity | 06ddb8632a567eb13053f20f9900a430 MD5 | raw file
  1. # Copyright (C) 2011 Andrea Francia Trivolzio(PV) Italy
  2. import os
  3. from trashcli.trash import ListCmd
  4. from files import (write_file, require_empty_dir, make_sticky_dir,
  5. make_unsticky_dir, make_unreadable_file, make_empty_file,
  6. make_parent_for)
  7. from nose.tools import istest
  8. from .output_collector import OutputCollector
  9. from trashinfo import (
  10. a_trashinfo,
  11. a_trashinfo_without_date,
  12. a_trashinfo_without_path,
  13. a_trashinfo_with_invalid_date)
  14. from textwrap import dedent
  15. class Setup(object):
  16. def setUp(self):
  17. require_empty_dir('XDG_DATA_HOME')
  18. require_empty_dir('topdir')
  19. self.user = TrashListUser(
  20. environ = {'XDG_DATA_HOME': 'XDG_DATA_HOME'})
  21. self.home_trashcan = FakeTrashDir('XDG_DATA_HOME/Trash')
  22. self.add_trashinfo = self.home_trashcan.add_trashinfo
  23. def when_dir_is_sticky(self, path):
  24. make_sticky_dir(path)
  25. def when_dir_exists_unsticky(self, path):
  26. make_unsticky_dir(path)
  27. @istest
  28. class describe_trash_list(Setup):
  29. @istest
  30. def should_output_the_help_message(self):
  31. self.user.run('trash-list', '--help')
  32. self.user.should_read_output(dedent("""\
  33. Usage: trash-list [OPTIONS...]
  34. List trashed files
  35. Options:
  36. --version show program's version number and exit
  37. -h, --help show this help message and exit
  38. Report bugs to http://code.google.com/p/trash-cli/issues
  39. """))
  40. @istest
  41. def should_output_nothing_when_trashcan_is_empty(self):
  42. self.user.run_trash_list()
  43. self.user.should_read_output('')
  44. @istest
  45. def should_output_deletion_date_and_path(self):
  46. self.add_trashinfo('/aboslute/path', '2001-02-03T23:55:59')
  47. self.user.run_trash_list()
  48. self.user.should_read_output( "2001-02-03 23:55:59 /aboslute/path\n")
  49. @istest
  50. def should_output_info_for_multiple_files(self):
  51. self.add_trashinfo("/file1", "2000-01-01T00:00:01")
  52. self.add_trashinfo("/file2", "2000-01-01T00:00:02")
  53. self.add_trashinfo("/file3", "2000-01-01T00:00:03")
  54. self.user.run_trash_list()
  55. self.user.should_read_output( "2000-01-01 00:00:01 /file1\n"
  56. "2000-01-01 00:00:02 /file2\n"
  57. "2000-01-01 00:00:03 /file3\n")
  58. @istest
  59. def should_output_unknown_dates_with_question_marks(self):
  60. self.home_trashcan.having_file(a_trashinfo_without_date())
  61. self.user.run_trash_list()
  62. self.user.should_read_output("????-??-?? ??:??:?? /path\n")
  63. @istest
  64. def should_output_invalid_dates_using_question_marks(self):
  65. self.home_trashcan.having_file(a_trashinfo_with_invalid_date())
  66. self.user.run_trash_list()
  67. self.user.should_read_output("????-??-?? ??:??:?? /path\n")
  68. @istest
  69. def should_warn_about_empty_trashinfos(self):
  70. self.home_trashcan.touch('empty.trashinfo')
  71. self.user.run_trash_list()
  72. self.user.should_read_error(
  73. "Parse Error: XDG_DATA_HOME/Trash/info/empty.trashinfo: "
  74. "Unable to parse Path.\n")
  75. @istest
  76. def should_warn_about_unreadable_trashinfo(self):
  77. self.home_trashcan.having_unreadable('unreadable.trashinfo')
  78. self.user.run_trash_list()
  79. self.user.should_read_error(
  80. "[Errno 13] Permission denied: "
  81. "'XDG_DATA_HOME/Trash/info/unreadable.trashinfo'\n")
  82. @istest
  83. def should_warn_about_unexistent_path_entry(self):
  84. self.home_trashcan.having_file(a_trashinfo_without_path())
  85. self.user.run_trash_list()
  86. self.user.should_read_error(
  87. "Parse Error: XDG_DATA_HOME/Trash/info/1.trashinfo: "
  88. "Unable to parse Path.\n")
  89. self.user.should_read_output('')
  90. @istest
  91. class with_a_top_trash_dir(Setup):
  92. def setUp(self):
  93. super(type(self),self).setUp()
  94. self.top_trashdir1 = FakeTrashDir('topdir/.Trash/123')
  95. self.user.set_fake_uid(123)
  96. self.user.add_volume('topdir')
  97. @istest
  98. def should_list_its_contents_if_parent_is_sticky(self):
  99. self.when_dir_is_sticky('topdir/.Trash')
  100. self.and_contains_a_valid_trashinfo()
  101. self.user.run_trash_list()
  102. self.user.should_read_output("2000-01-01 00:00:00 topdir/file1\n")
  103. @istest
  104. def and_should_warn_if_parent_is_not_sticky(self):
  105. self.when_dir_exists_unsticky('topdir/.Trash')
  106. self.and_dir_exists('topdir/.Trash/123')
  107. self.user.run_trash_list()
  108. self.user.should_read_error("TrashDir skipped because parent not sticky: topdir/.Trash/123\n")
  109. @istest
  110. def but_it_should_not_warn_when_the_parent_is_unsticky_but_there_is_no_trashdir(self):
  111. self.when_dir_exists_unsticky('topdir/.Trash')
  112. self.but_does_not_exists_any('topdir/.Trash/123')
  113. self.user.run_trash_list()
  114. self.user.should_read_error("")
  115. @istest
  116. def should_ignore_trash_from_a_unsticky_topdir(self):
  117. self.when_dir_exists_unsticky('topdir/.Trash')
  118. self.and_contains_a_valid_trashinfo()
  119. self.user.run_trash_list()
  120. self.user.should_read_output("")
  121. @istest
  122. def it_should_ignore_Trash_is_a_symlink(self):
  123. self.when_is_a_symlink_to_a_dir('topdir/.Trash')
  124. self.and_contains_a_valid_trashinfo()
  125. self.user.run_trash_list()
  126. self.user.should_read_output('')
  127. @istest
  128. def and_should_warn_about_it(self):
  129. self.when_is_a_symlink_to_a_dir('topdir/.Trash')
  130. self.and_contains_a_valid_trashinfo()
  131. self.user.run_trash_list()
  132. self.user.should_read_error('TrashDir skipped because parent not sticky: topdir/.Trash/123\n')
  133. def but_does_not_exists_any(self, path):
  134. assert not os.path.exists(path)
  135. def and_dir_exists(self, path):
  136. os.mkdir(path)
  137. assert os.path.isdir(path)
  138. def and_contains_a_valid_trashinfo(self):
  139. self.top_trashdir1.add_trashinfo('file1', '2000-01-01T00:00:00')
  140. def when_is_a_symlink_to_a_dir(self, path):
  141. dest = "%s-dest" % path
  142. os.mkdir(dest)
  143. rel_dest = os.path.basename(dest)
  144. os.symlink(rel_dest, path)
  145. @istest
  146. class describe_when_a_file_is_in_alternate_top_trashdir(Setup):
  147. @istest
  148. def should_list_contents_of_alternate_trashdir(self):
  149. self.user.set_fake_uid(123)
  150. self.user.add_volume('topdir')
  151. self.top_trashdir2 = FakeTrashDir('topdir/.Trash-123')
  152. self.top_trashdir2.add_trashinfo('file', '2000-01-01T00:00:00')
  153. self.user.run_trash_list()
  154. self.user.should_read_output("2000-01-01 00:00:00 topdir/file\n")
  155. @istest
  156. class describe_trash_list_with_raw_option:
  157. def setup(self):
  158. self.having_XDG_DATA_HOME('XDG_DATA_HOME')
  159. self.running('trash-list', '--raw')
  160. @istest
  161. def output_should_contains_trashinfo_paths(self):
  162. from nose import SkipTest; raise SkipTest()
  163. self.having_trashinfo('foo.trashinfo')
  164. self.output_should_contain_line(
  165. 'XDG_DATA_HOME/Trash/info/foo.trashinfo')
  166. @istest
  167. def output_should_contains_backup_copy_paths(self):
  168. from nose import SkipTest; raise SkipTest()
  169. self.having_trashinfo('foo.trashinfo')
  170. self.output_should_contain_line(
  171. 'XDG_DATA_HOME/Trash/files/foo')
  172. def having_XDG_DATA_HOME(self, value):
  173. self.XDG_DATA_HOME = value
  174. def running(self, *argv):
  175. user = TrashListUser( environ = {'XDG_DATA_HOME': self.XDG_DATA_HOME})
  176. user.run(argv)
  177. self.output = user.output()
  178. def output_should_contain_line(self, line):
  179. assert line in self.output_lines()
  180. def output_lines(self):
  181. return [line.rstrip('\n') for line in self.output.splitlines()]
  182. class FakeTrashDir:
  183. def __init__(self, path):
  184. self.path = path + '/info'
  185. self.number = 1
  186. def touch(self, path_relative_to_info_dir):
  187. make_empty_file(self.join(path_relative_to_info_dir))
  188. def having_unreadable(self, path_relative_to_info_dir):
  189. path = self.join(path_relative_to_info_dir)
  190. make_unreadable_file(path)
  191. def join(self, path_relative_to_info_dir):
  192. import os
  193. return os.path.join(self.path, path_relative_to_info_dir)
  194. def having_file(self, contents):
  195. path = '%(info_dir)s/%(name)s.trashinfo' % { 'info_dir' : self.path,
  196. 'name' : str(self.number)}
  197. make_parent_for(path)
  198. write_file(path, contents)
  199. self.number += 1
  200. self.path_of_last_file_added = path
  201. def add_trashinfo(self, escaped_path_entry, formatted_deletion_date):
  202. self.having_file(a_trashinfo(escaped_path_entry, formatted_deletion_date))
  203. class TrashListUser:
  204. def __init__(self, environ={}):
  205. self.stdout = OutputCollector()
  206. self.stderr = OutputCollector()
  207. self.environ = environ
  208. self.fake_getuid = self.error
  209. self.volumes = []
  210. def run_trash_list(self):
  211. self.run('trash-list')
  212. def run(self,*argv):
  213. from trashcli.trash import FileSystemReader
  214. file_reader = FileSystemReader()
  215. file_reader.list_volumes = lambda: self.volumes
  216. ListCmd(
  217. out = self.stdout,
  218. err = self.stderr,
  219. environ = self.environ,
  220. getuid = self.fake_getuid,
  221. file_reader = file_reader,
  222. list_volumes = lambda: self.volumes,
  223. ).run(*argv)
  224. def set_fake_uid(self, uid):
  225. self.fake_getuid = lambda: uid
  226. def add_volume(self, mount_point):
  227. self.volumes.append(mount_point)
  228. def error(self):
  229. raise ValueError()
  230. def should_read_output(self, expected_value):
  231. self.stdout.assert_equal_to(expected_value)
  232. def should_read_error(self, expected_value):
  233. self.stderr.assert_equal_to(expected_value)
  234. def output(self):
  235. return self.stdout.getvalue()