source: test/test_path.py

Last change on this file was 1833:cc9e84b0a418, checked in by Stefan Schwarzer <sschwarzer@…>, 5 months ago
Use `ScriptedSession` in `TestAcceptEitherBytesOrUnicode`
File size: 24.8 KB
Line 
1# Copyright (C) 2003-2019, Stefan Schwarzer <sschwarzer@sschwarzer.net>
2# and ftputil contributors (see `doc/contributors.txt`)
3# See the file LICENSE for licensing terms.
4
5import datetime
6import ftplib
7import time
8
9import pytest
10
11import ftputil
12import ftputil.error
13import ftputil.tool
14
15from test import test_base
16from test import scripted_session
17
18
19Call = scripted_session.Call
20
21
22class TestPath:
23    """Test operations in `FTPHost.path`."""
24
25    # TODO: Add unit tests for changes for ticket #113
26    # (commits [b4c9b089b6b8] and [4027740cdd2d]).
27    def test_regular_isdir_isfile_islink(self):
28        """Test regular `FTPHost._Path.isdir/isfile/islink`."""
29        # Test a path which isn't there.
30        script = [
31          Call("__init__"),
32          Call("pwd", result="/"),
33          # `isdir` call
34          Call("cwd", args=("/",)),
35          Call("cwd", args=("/",)),
36          Call("dir", args=("",), result=""),
37          Call("cwd", args=("/",)),
38          # `isfile` call
39          Call("cwd", args=("/",)),
40          Call("cwd", args=("/",)),
41          Call("dir", args=("",), result=""),
42          Call("cwd", args=("/",)),
43          # `islink` call
44          Call("cwd", args=("/",)),
45          Call("cwd", args=("/",)),
46          Call("dir", args=("",), result=""),
47          Call("cwd", args=("/",)),
48          Call("close")
49        ]
50        with test_base.ftp_host_factory(scripted_session.factory(script)) as host:
51            host.stat_cache.disable()
52            assert not host.path.isdir("notthere")
53            assert not host.path.isfile("notthere")
54            assert not host.path.islink("notthere")
55        # This checks additional code (see ticket #66).
56        script = [
57          Call("__init__"),
58          Call("pwd", result="/"),
59          # `isdir` call
60          Call("cwd", args=("/",)),
61          Call("cwd", args=("/",)),
62          Call("dir", args=("",), result=""),
63          Call("cwd", args=("/",)),
64          # `isfile` call
65          Call("cwd", args=("/",)),
66          Call("cwd", args=("/",)),
67          Call("dir", args=("",), result=""),
68          Call("cwd", args=("/",)),
69          # `islink` call
70          Call("cwd", args=("/",)),
71          Call("cwd", args=("/",)),
72          Call("dir", args=("",), result=""),
73          Call("cwd", args=("/",)),
74          Call("close")
75        ]
76        with test_base.ftp_host_factory(scripted_session.factory(script)) as host:
77            host.stat_cache.disable()
78            assert not host.path.isdir("/notthere/notthere")
79            assert not host.path.isfile("/notthere/notthere")
80            assert not host.path.islink("/notthere/notthere")
81        # Test a directory.
82        test_dir = "/some_dir"
83        dir_line = test_base.dir_line(mode_string="dr-xr-xr-x",
84                                      datetime_=datetime.datetime.now(),
85                                      name=test_dir.lstrip("/"))
86        script = [
87          Call("__init__"),
88          Call("pwd", result="/"),
89          # `isdir` call
90          Call("cwd", args=("/",)),
91          Call("cwd", args=("/",)),
92          Call("dir", args=("",), result=dir_line),
93          Call("cwd", args=("/",)),
94          # `isfile` call
95          Call("cwd", args=("/",)),
96          Call("cwd", args=("/",)),
97          Call("dir", args=("",), result=dir_line),
98          Call("cwd", args=("/",)),
99          # `islink` call
100          Call("cwd", args=("/",)),
101          Call("cwd", args=("/",)),
102          Call("dir", args=("",), result=dir_line),
103          Call("cwd", args=("/",)),
104          Call("close")
105        ]
106        with test_base.ftp_host_factory(scripted_session.factory(script)) as host:
107            host.stat_cache.disable()
108            assert host.path.isdir(test_dir)
109            assert not host.path.isfile(test_dir)
110            assert not host.path.islink(test_dir)
111        # Test a file.
112        test_file = "/some_file"
113        dir_line = test_base.dir_line(datetime_=datetime.datetime.now(),
114                                      name=test_file.lstrip("/"))
115        script = [
116          Call("__init__"),
117          Call("pwd", result="/"),
118          # `isdir` call
119          Call("cwd", args=("/",)),
120          Call("cwd", args=("/",)),
121          Call("dir", args=("",), result=dir_line),
122          Call("cwd", args=("/",)),
123          # `isfile` call
124          Call("cwd", args=("/",)),
125          Call("cwd", args=("/",)),
126          Call("dir", args=("",), result=dir_line),
127          Call("cwd", args=("/",)),
128          # `islink` call
129          Call("cwd", args=("/",)),
130          Call("cwd", args=("/",)),
131          Call("dir", args=("",), result=dir_line),
132          Call("cwd", args=("/",)),
133          Call("close")
134        ]
135        with test_base.ftp_host_factory(scripted_session.factory(script)) as host:
136            host.stat_cache.disable()
137            assert not host.path.isdir(test_file)
138            assert host.path.isfile(test_file)
139            assert not host.path.islink(test_file)
140        # Test a link. Since the link target doesn't exist, neither
141        # `isdir` nor `isfile` return `True`.
142        test_link = "/some_link"
143        dir_line = test_base.dir_line(mode_string="lrwxrwxrwx",
144                                      datetime_=datetime.datetime.now(),
145                                      name=test_link.lstrip("/"),
146                                      link_target="nonexistent")
147        script = [
148          Call("__init__"),
149          Call("pwd", result="/"),
150          # `isdir` call
151          Call("cwd", args=("/",)),
152          Call("cwd", args=("/",)),
153          #  Look for `/some_link`
154          Call("dir", args=("",), result=dir_line),
155          Call("cwd", args=("/",)),
156          Call("cwd", args=("/",)),
157          Call("cwd", args=("/",)),
158          #  Look for `/nonexistent`
159          Call("dir", args=("",), result=dir_line),
160          Call("cwd", args=("/",)),
161          # `isfile` call
162          Call("cwd", args=("/",)),
163          Call("cwd", args=("/",)),
164          #  Look for `/some_link`
165          Call("dir", args=("",), result=dir_line),
166          Call("cwd", args=("/",)),
167          Call("cwd", args=("/",)),
168          Call("cwd", args=("/",)),
169          #  Look for `/nonexistent`
170          Call("dir", args=("",), result=dir_line),
171          Call("cwd", args=("/",)),
172          # `islink` call
173          Call("cwd", args=("/",)),
174          Call("cwd", args=("/",)),
175          #  Look for `/some_link`. `islink` doesn't try to dereference
176          #  the link.
177          Call("dir", args=("",), result=dir_line),
178          Call("cwd", args=("/",)),
179          Call("close")
180        ]
181        with test_base.ftp_host_factory(scripted_session.factory(script)) as host:
182            host.stat_cache.disable()
183            assert not host.path.isdir(test_link)
184            assert not host.path.isfile(test_link)
185            assert host.path.islink(test_link)
186
187    def test_workaround_for_spaces(self):
188        """Test whether the workaround for space-containing paths is used."""
189        # Test a file name containing spaces.
190        test_file = "/home/dir with spaces/file with spaces"
191        dir_line1 = test_base.dir_line(mode_string="dr-xr-xr-x",
192                                       datetime_=datetime.datetime.now(),
193                                       name="home")
194        dir_line2 = test_base.dir_line(mode_string="dr-xr-xr-x",
195                                       datetime_=datetime.datetime.now(),
196                                       name="dir with spaces")
197        dir_line3 = test_base.dir_line(mode_string="-r--r--r--",
198                                       datetime_=datetime.datetime.now(),
199                                       name="file with spaces")
200        script = [
201          Call("__init__"),
202          Call("pwd", result="/"),
203          # `isdir` call
204          Call("cwd", args=("/",)),
205          Call("cwd", args=("/",)),
206          Call("dir", args=("",), result=dir_line1),
207          Call("cwd", args=("/",)),
208
209          Call("cwd", args=("/",)),
210          Call("cwd", args=("/home",)),
211          Call("dir", args=("",), result=dir_line2),
212          Call("cwd", args=("/",)),
213          Call("cwd", args=("/",)),
214          Call("cwd", args=("/home/dir with spaces",)),
215          Call("dir", args=("",), result=dir_line3),
216          Call("cwd", args=("/",)),
217          # `isfile` call
218          Call("cwd", args=("/",)),
219          Call("cwd", args=("/",)),
220          Call("dir", args=("",), result=dir_line1),
221          Call("cwd", args=("/",)),
222          Call("cwd", args=("/",)),
223          Call("cwd", args=("/home",)),
224          Call("dir", args=("",), result=dir_line2),
225          Call("cwd", args=("/",)),
226          Call("cwd", args=("/",)),
227          Call("cwd", args=("/home/dir with spaces",)),
228          Call("dir", args=("",), result=dir_line3),
229          Call("cwd", args=("/",)),
230          # `islink` call
231          Call("cwd", args=("/",)),
232          Call("cwd", args=("/",)),
233          Call("dir", args=("",), result=dir_line1),
234          Call("cwd", args=("/",)),
235          Call("cwd", args=("/",)),
236          Call("cwd", args=("/home",)),
237          Call("dir", args=("",), result=dir_line2),
238          Call("cwd", args=("/",)),
239          Call("cwd", args=("/",)),
240          Call("cwd", args=("/home/dir with spaces",)),
241          Call("dir", args=("",), result=dir_line3),
242          Call("cwd", args=("/",)),
243          Call("close"),
244        ]
245        with test_base.ftp_host_factory(scripted_session.factory(script)) as host:
246            host.stat_cache.disable()
247            assert not host.path.isdir(test_file)
248            assert host.path.isfile(test_file)
249            assert not host.path.islink(test_file)
250
251    def test_inaccessible_home_directory_and_whitespace_workaround(self):
252        "Test combination of inaccessible home directory + whitespace in path."
253        script = [
254          Call("__init__"),
255          Call("pwd", result="/"),
256          Call("cwd", result=ftplib.error_perm),
257          Call("close")
258        ]
259        with test_base.ftp_host_factory(scripted_session.factory(script)) as host:
260            with pytest.raises(ftputil.error.InaccessibleLoginDirError):
261                host._dir("/home dir")
262
263    def test_isdir_isfile_islink_with_dir_failure(self):
264        """
265        Test failing `FTPHost._Path.isdir/isfile/islink` because of
266        failing `_dir` call.
267        """
268        script = [
269          Call("__init__"),
270          Call("pwd", result="/"),
271          Call("cwd", args=("/",)),
272          Call("cwd", args=("/",)),
273          Call("dir", args=("",), result=ftplib.error_perm),
274          Call("cwd", args=("/",)),
275          Call("close")
276        ]
277        FTPOSError = ftputil.error.FTPOSError
278        # Test if exceptions are propagated.
279        with test_base.ftp_host_factory(scripted_session.factory(script)) as host:
280            with pytest.raises(FTPOSError):
281                host.path.isdir("index.html")
282        with test_base.ftp_host_factory(scripted_session.factory(script)) as host:
283            with pytest.raises(FTPOSError):
284                host.path.isfile("index.html")
285        with test_base.ftp_host_factory(scripted_session.factory(script)) as host:
286            with pytest.raises(FTPOSError):
287                host.path.islink("index.html")
288
289    def test_isdir_isfile_with_infinite_link_chain(self):
290        """
291        Test if `isdir` and `isfile` return `False` if they encounter
292        an infinite link chain.
293        """
294        # `/home/bad_link` links to `/home/subdir/bad_link`, which
295        # links back to `/home/bad_link` etc.
296        dir_line1 = test_base.dir_line(mode_string="dr-xr-xr-x",
297                                       datetime_=datetime.datetime.now(),
298                                       name="home")
299        dir_line2 = test_base.dir_line(mode_string="lrwxrwxrwx",
300                                       datetime_=datetime.datetime.now(),
301                                       name="bad_link",
302                                       link_target="subdir/bad_link")
303        dir_line3 = test_base.dir_line(mode_string="dr-xr-xr-x",
304                                       datetime_=datetime.datetime.now(),
305                                       name="subdir")
306        dir_line4 = test_base.dir_line(mode_string="lrwxrwxrwx",
307                                       datetime_=datetime.datetime.now(),
308                                       name="bad_link",
309                                       link_target="/home/bad_link")
310        script = [
311          Call("__init__"),
312          Call("pwd", result="/"),
313          Call("cwd", args=("/",)),
314          Call("cwd", args=("/",)),
315          Call("dir", args=("",), result=dir_line1),
316          Call("cwd", args=("/",)),
317          Call("cwd", args=("/",)),
318          Call("cwd", args=("/home",)),
319          Call("dir", args=("",), result=dir_line2+"\n"+dir_line3),
320          Call("cwd", args=("/",)),
321          Call("cwd", args=("/",)),
322          Call("cwd", args=("/home/subdir",)),
323          Call("dir", args=("",), result=dir_line4),
324          Call("cwd", args=("/",)),
325          Call("close")
326        ]
327        with test_base.ftp_host_factory(scripted_session.factory(script)) as host:
328            assert host.path.isdir("/home/bad_link") is False
329        with test_base.ftp_host_factory(scripted_session.factory(script)) as host:
330            assert host.path.isfile("/home/bad_link") is False
331
332    def test_exists(self):
333        """Test `FTPHost.path.exists`."""
334        # Regular use of `exists`
335        dir_line1 = test_base.dir_line(datetime_=datetime.datetime.now(),
336                                       name="some_file")
337        script = [
338          Call("__init__"),
339          Call("pwd", result="/"),
340          # `exists("some_file")`
341          Call("cwd", args=("/",)),
342          Call("cwd", args=("/",)),
343          Call("dir", args=("",), result=dir_line1),
344          Call("cwd", args=("/",)),
345          # `exists("notthere")`
346          Call("cwd", args=("/",)),
347          Call("cwd", args=("/",)),
348          Call("dir", args=("",), result=dir_line1),
349          Call("cwd", args=("/",)),
350          # `exists` with failing `dir` call
351          Call("cwd", args=("/",)),
352          Call("cwd", args=("/",)),
353          Call("dir", args=("",), result=ftplib.error_perm),
354          Call("cwd", args=("/",)),
355          Call("close")
356        ]
357        with test_base.ftp_host_factory(scripted_session.factory(script)) as host:
358            host.stat_cache.disable()
359            assert host.path.exists("some_file")
360            assert not host.path.exists("notthere")
361            # Test if exceptions are propagated.
362            with pytest.raises(ftputil.error.FTPOSError):
363                host.path.exists("some_file")
364
365
366class TestAcceptEitherBytesOrUnicode:
367
368    def _test_method_string_types(self, method, path):
369        expected_type = type(path)
370        assert isinstance(method(path), expected_type)
371
372    def test_methods_that_take_and_return_one_string(self):
373        """
374        Test whether the same string type as for the argument is returned.
375        """
376        method_names = ["abspath", "basename", "dirname", "join", "normcase",
377                        "normpath"]
378        script = [
379          Call("__init__"),
380          Call("pwd", result="/"),
381          Call("close")
382        ]
383        with test_base.ftp_host_factory(scripted_session.factory(script)) as host:
384            for method_name in method_names:
385                method = getattr(host.path, method_name)
386                self._test_method_string_types(method, "/")
387                self._test_method_string_types(method, ".")
388                self._test_method_string_types(method, b"/")
389                self._test_method_string_types(method, b".")
390
391    def test_methods_that_take_a_string_and_return_a_bool(self):
392        """Test whether the methods accept byte and unicode strings."""
393        as_bytes = ftputil.tool.as_bytes
394        script = [
395          Call("__init__"),
396          Call("pwd", result="/"),
397          # `exists` test 1
398          Call("cwd", args=("/",)),
399          Call("cwd", args=("/",)),
400          Call("dir",
401               args=("",),
402               result=test_base.dir_line(name="ä",
403                                         datetime_=datetime.datetime.now())),
404          Call("cwd", args=("/",)),
405          # `exists` test 2
406          Call("cwd", args=("/",)),
407          Call("cwd", args=("/",)),
408          Call("dir",
409               args=("",),
410               result=test_base.dir_line(name="ä",
411                                         datetime_=datetime.datetime.now())),
412          Call("cwd", args=("/",)),
413          # `isdir` test 1
414          Call("cwd", args=("/",)),
415          Call("cwd", args=("/",)),
416          Call("dir",
417               args=("",),
418               result=test_base.dir_line(mode_string="dr-xr-xr-x",
419                                         name="ä",
420                                         datetime_=datetime.datetime.now())),
421          Call("cwd", args=("/",)),
422          # `isdir` test 2
423          Call("cwd", args=("/",)),
424          Call("cwd", args=("/",)),
425          Call("dir",
426               args=("",),
427               result=test_base.dir_line(mode_string="dr-xr-xr-x",
428                                         name="ä",
429                                         datetime_=datetime.datetime.now())),
430          Call("cwd", args=("/",)),
431          # `isfile` test 1
432          Call("cwd", args=("/",)),
433          Call("cwd", args=("/",)),
434          Call("dir",
435               args=("",),
436               result=test_base.dir_line(name="ö",
437                                         datetime_=datetime.datetime.now())),
438          Call("cwd", args=("/",)),
439          # `isfile` test 2
440          Call("cwd", args=("/",)),
441          Call("cwd", args=("/",)),
442          Call("dir",
443               args=("",),
444               result=test_base.dir_line(name="ö",
445                                         datetime_=datetime.datetime.now())),
446          Call("cwd", args=("/",)),
447          # `islink` test 1
448          Call("cwd", args=("/",)),
449          Call("cwd", args=("/",)),
450          Call("dir",
451               args=("",),
452               result=test_base.dir_line(mode_string="lrwxrwxrwx",
453                                         name="ü",
454                                         datetime_=datetime.datetime.now(),
455                                         link_target="unimportant")),
456          Call("cwd", args=("/",)),
457          # `islink` test 2
458          Call("cwd", args=("/",)),
459          Call("cwd", args=("/",)),
460          Call("dir",
461               args=("",),
462               result=test_base.dir_line(mode_string="lrwxrwxrwx",
463                                         name="ü",
464                                         datetime_=datetime.datetime.now(),
465                                         link_target="unimportant")),
466          Call("cwd", args=("/",)),
467          Call("close")
468        ]
469        with test_base.ftp_host_factory(scripted_session.factory(script)) as host:
470            host.stat_cache.disable()
471            # `isabs`
472            assert not host.path.isabs("ä")
473            assert not host.path.isabs(as_bytes("ä"))
474            # `exists`
475            assert host.path.exists("ä")
476            assert host.path.exists(as_bytes("ä"))
477            # `isdir`, `isfile`, `islink`
478            assert host.path.isdir("ä")
479            assert host.path.isdir(as_bytes("ä"))
480            assert host.path.isfile("ö")
481            assert host.path.isfile(as_bytes("ö"))
482            assert host.path.islink("ü")
483            assert host.path.islink(as_bytes("ü"))
484
485    def test_join(self):
486        """
487        Test whether `FTPHost.path.join` accepts only arguments of
488        the same string type and returns the same string type.
489        """
490        as_bytes = ftputil.tool.as_bytes
491        script = [
492          Call("__init__"),
493          Call("pwd", result="/"),
494          Call("close")
495        ]
496        with test_base.ftp_host_factory(scripted_session.factory(script)) as host:
497            host.stat_cache.disable()
498            join = host.path.join
499            # Only unicode
500            parts = ["ä", "ö", "ü"]
501            result = join(*parts)
502            assert result == "ä/ö/ü"
503            # Only bytes
504            parts = [as_bytes(s) for s in parts]
505            result = join(*parts)
506            assert result == as_bytes("ä/ö/ü")
507            # Mixture of unicode and bytes
508            parts = ["ä", as_bytes("ö")]
509            with pytest.raises(TypeError):
510                join(*parts)
511            parts = [as_bytes("ä"), as_bytes("ö"), "ü"]
512            with pytest.raises(TypeError):
513                join(*parts)
514
515    def test_getmtime(self):
516        """
517        Test whether `FTPHost.path.getmtime` accepts byte and unicode
518        paths.
519        """
520        as_bytes = ftputil.tool.as_bytes
521        now = datetime.datetime.now()
522        script = [
523          Call("__init__"),
524          Call("pwd", result="/"),
525          # `getmtime` call 1
526          Call("cwd", args=("/",)),
527          Call("cwd", args=("/",)),
528          Call("dir",
529               args=("",),
530               result=test_base.dir_line(name="ä",
531                                         datetime_=now)),
532          Call("cwd", args=("/",)),
533          # `getmtime` call 2
534          Call("cwd", args=("/",)),
535          Call("cwd", args=("/",)),
536          Call("dir",
537               args=("",),
538               result=test_base.dir_line(name="ä",
539                                         datetime_=now)),
540          Call("cwd", args=("/",)),
541          Call("close")
542        ]
543        with test_base.ftp_host_factory(scripted_session.factory(script)) as host:
544            host.stat_cache.disable()
545            expected_mtime = now.timestamp()
546            # We don't care about the _exact_ time, so don't bother with
547            # timezone differences. Instead, do a simple sanity check.
548            day = 24 * 60 * 60  # seconds
549            mtime_makes_sense = (lambda mtime: expected_mtime - day <= mtime <=
550                                               expected_mtime + day)
551            assert mtime_makes_sense(host.path.getmtime("ä"))
552            assert mtime_makes_sense(host.path.getmtime(as_bytes("ä")))
553
554    def test_getsize(self):
555        """
556        Test whether `FTPHost.path.getsize` accepts byte and unicode paths.
557        """
558        as_bytes = ftputil.tool.as_bytes
559        now = datetime.datetime.now()
560        script = [
561          Call("__init__"),
562          Call("pwd", result="/"),
563          # `getsize` call 1
564          Call("cwd", args=("/",)),
565          Call("cwd", args=("/",)),
566          Call("dir",
567               args=("",),
568               result=test_base.dir_line(name="ä",
569                                         size=512,
570                                         datetime_=now)),
571          Call("cwd", args=("/",)),
572          # `getsize` call 2
573          Call("cwd", args=("/",)),
574          Call("cwd", args=("/",)),
575          Call("dir",
576               args=("",),
577               result=test_base.dir_line(name="ä",
578                                         size=512,
579                                         datetime_=now)),
580          Call("cwd", args=("/",)),
581          Call("close")
582        ]
583        with test_base.ftp_host_factory(scripted_session.factory(script)) as host:
584            host.stat_cache.disable()
585            assert host.path.getsize("ä") == 512
586            assert host.path.getsize(as_bytes("ä")) == 512
587
588    def test_walk(self):
589        """Test whether `FTPHost.path.walk` accepts bytes and unicode paths."""
590        as_bytes = ftputil.tool.as_bytes
591        now = datetime.datetime.now()
592        script = [
593          Call("__init__"),
594          Call("pwd", result="/"),
595          # `walk` call 1
596          Call("cwd", args=("/",)),
597          Call("cwd", args=("/",)),
598          Call("dir",
599               args=("",),
600               result=test_base.dir_line(mode_string="dr-xr-xr-x",
601                                         name="ä",
602                                         size=512,
603                                         datetime_=now)),
604          Call("cwd", args=("/",)),
605          Call("cwd", args=("/",)),
606          Call("cwd", args=("/ä",)),
607          #  Assume directory `ä` is empty.
608          Call("dir", args=("",), result=""),
609          Call("cwd", args=("/",)),
610          # `walk` call 2
611          Call("cwd", args=("/",)),
612          Call("cwd", args=("/",)),
613          Call("dir",
614               args=("",),
615               result=test_base.dir_line(mode_string="dr-xr-xr-x",
616                                         name="ä",
617                                         size=512,
618                                         datetime_=now)),
619          Call("cwd", args=("/",)),
620          Call("cwd", args=("/",)),
621          Call("cwd", args=("/ä",)),
622          #  Assume directory `ä` is empty.
623          Call("dir", args=("",), result=""),
624          Call("cwd", args=("/",)),
625          Call("close")
626        ]
627        def noop(arg, top, names):
628            del names[:]
629        with test_base.ftp_host_factory(scripted_session.factory(script)) as host:
630            host.stat_cache.disable()
631            host.path.walk("ä", func=noop, arg=None)
632            host.path.walk(as_bytes("ä"), func=noop, arg=None)
Note: See TracBrowser for help on using the repository browser.