source: test/test_with_statement.py @ 1819:66015241a6d5

Last change on this file since 1819:66015241a6d5 was 1819:66015241a6d5, checked in by Stefan Schwarzer <sschwarzer@…>, 2 years ago
Add docstrings to test methods
File size: 6.6 KB
Line 
1# Copyright (C) 2008-2019, Stefan Schwarzer <sschwarzer@sschwarzer.net>
2# and ftputil contributors (see `doc/contributors.txt`)
3# See the file LICENSE for licensing terms.
4
5import ftplib
6import io
7import pytest
8
9import ftputil.error
10
11import test.scripted_session as scripted_session
12from test import test_base
13from test.test_file import InaccessibleDirSession, ReadMockSession
14
15
16Call = scripted_session.Call
17
18
19# Exception raised by client code, i. e. code using ftputil. Used to
20# test the behavior in case of client exceptions.
21class ClientCodeException(Exception):
22    pass
23
24
25#
26# Test cases
27#
28class TestHostContextManager:
29
30    def test_normal_operation(self):
31        """
32        If an `FTPHost` instance is created, it should be closed by the host
33        context manager.
34        """
35        script = [
36          Call(method_name="__init__"),
37          Call(method_name="pwd", result="/"),
38          Call(method_name="close")
39        ]
40        with test_base.ftp_host_factory(scripted_session.factory(script)) as host:
41            assert host.closed is False
42        assert host.closed is True
43
44    def test_ftputil_exception(self):
45        """
46        If an `ftplib.FTP` method raises an exception, it should be caught by
47        the host context manager and the host object should be closed.
48        """
49        script = [
50          # Since `__init__` raises an exception, `pwd` isn't called. However,
51          # `close` is called via the context manager.
52          Call(method_name="__init__", result=ftplib.error_perm),
53          Call(method_name="close")
54        ]
55        with pytest.raises(ftputil.error.FTPOSError):
56            with test_base.ftp_host_factory(scripted_session.factory(script)) as host:
57                pass
58        # We arrived here, that's fine. Because the `FTPHost` object
59        # wasn't successfully constructed, the assignment to `host`
60        # shouldn't have happened.
61        assert "host" not in locals()
62
63    def test_client_code_exception(self):
64        """
65        If client code raises an exception in the context manager block, the
66        host object should be closed by the context manager.
67        """
68        script = [
69          Call(method_name="__init__"),
70          Call(method_name="pwd", result="/"),
71          Call(method_name="close")
72        ]
73        try:
74            with test_base.ftp_host_factory(scripted_session.factory(script)) as host:
75                assert host.closed is False
76                raise ClientCodeException()
77        except ClientCodeException:
78            assert host.closed is True
79        else:
80            pytest.fail("`ClientCodeException` not raised")
81
82
83class TestFileContextManager:
84
85    def test_normal_operation(self):
86        """
87        If an `FTPFile` object is created in the `FTPFile` context manager, the
88        context manager should close the file at the end of the `with` block.
89        """
90        host_script = [
91          Call(method_name="__init__"),
92          Call(method_name="pwd", result="/"),
93          Call(method_name="close")
94        ]
95        file_script = [
96          Call(method_name="__init__"),
97          Call(method_name="pwd", result="/"),
98          Call(method_name="cwd", result=None, args=("/",)),
99          Call(method_name="voidcmd", result=None, args=("TYPE I",)),
100          Call(method_name="transfercmd",
101               result=io.BytesIO(b"line 1\nline 2"),
102               args=("RETR dummy", None)),
103          Call(method_name="voidresp", result=None),
104          Call(method_name="close")
105        ]
106        multisession_factory = scripted_session.factory(host_script, file_script)
107        with test_base.ftp_host_factory(multisession_factory) as host:
108            with host.open("dummy", "r") as fobj:
109                assert fobj.closed is False
110                data = fobj.readline()
111                assert data == "line 1\n"
112                assert fobj.closed is False
113            assert fobj.closed is True
114
115    def test_ftputil_exception(self):
116        """
117        If an `ftplib.FTP` method raises an exception, the `FTPFile` context
118        manager should try to close the file.
119        """
120        host_script = [
121          Call(method_name="__init__"),
122          Call(method_name="pwd", result="/"),
123          Call(method_name="close")
124        ]
125        file_script = [
126          Call(method_name="__init__"),
127          Call(method_name="pwd", result="/"),
128          Call(method_name="cwd", result=None, args=("/",)),
129          Call(method_name="voidcmd", result=None, args=("TYPE I",)),
130          # Raise exception. `voidresp` therefore won't be called, but `close`
131          # will be called by the context manager.
132          Call(method_name="transfercmd",
133               result=ftplib.error_perm,
134               args=("STOR inaccessible", None)),
135          # Call(method_name="voidresp", result=None),
136          Call(method_name="close")
137        ]
138        multisession_factory = scripted_session.factory(host_script, file_script)
139        with test_base.ftp_host_factory(multisession_factory) as host:
140            with pytest.raises(ftputil.error.FTPIOError):
141                # This should fail.
142                with host.open("/inaccessible", "w") as fobj:
143                    pass
144            # The file construction shouldn't have succeeded, so `fobj`
145            # should be absent from the local namespace.
146            assert "fobj" not in locals()
147
148    def test_client_code_exception(self):
149        """
150        If client code raises an exception in the `FTPFile` context manager
151        block, the file object should be closed by the context manager.
152        """
153        host_script = [
154          Call(method_name="__init__"),
155          Call(method_name="pwd", result="/"),
156          Call(method_name="close")
157        ]
158        file_script = [
159          Call(method_name="__init__"),
160          Call(method_name="pwd", result="/"),
161          Call(method_name="cwd", result=None, args=("/",)),
162          Call(method_name="voidcmd", result=None, args=("TYPE I",)),
163          Call(method_name="transfercmd",
164               result=io.BytesIO(b""),
165               args=("RETR dummy", None)),
166          Call(method_name="voidresp", result=None),
167          Call(method_name="close")
168        ]
169        multisession_factory = scripted_session.factory(host_script, file_script)
170        with test_base.ftp_host_factory(multisession_factory) as host:
171            try:
172                with host.open("dummy", "r") as fobj:
173                    assert fobj.closed is False
174                    raise ClientCodeException()
175            except ClientCodeException:
176                assert fobj.closed is True
177            else:
178                pytest.fail("`ClientCodeException` not raised")
Note: See TracBrowser for help on using the repository browser.