File: test_facade.py

package info (click to toggle)
python-plyer 2.1.0-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 1,808 kB
  • sloc: python: 13,395; sh: 217; makefile: 177
file content (183 lines) | stat: -rw-r--r-- 5,654 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
'''
TestFacade
==========

Tested platforms:

* Android
* iOS
* Windows
* MacOS
* Linux
'''

import unittest

import sys
from types import MethodType

from unittest.mock import Mock, patch

import plyer


def mock_platform_module(mod, platform, cls):
    '''
    Create a stub module for a specific platform. This module contains:

    * class inheriting from facade implementing the desired feature
    * 'instance' function returning an instance of the implementing class
    '''

    # assemble an instance returned from the instance() function
    # which is created from a dynamically created class
    # <class '<mod>.<platform><cls>'> e.g.:
    # <class 'plyer.platforms.win.dummy . WinDummy'>
    stub_inst = Mock(
        __module__=mod,
        __class__=type(
            '{}{}'.format(platform.title(), cls), (object, ), {
                '__module__': mod
            }
        ),
    )

    # manual 'return_value' assign to Mock, so that the instance() call
    # can return stub_inst's own instance instead of creating another
    # unnecessary Mock object
    stub_inst.return_value = stub_inst

    # bind custom function returning the class name to stub_inst instance,
    # so that instance().show() call requires 'self' i.e. instance parameter
    # for the function to access the instance's class name
    stub_inst.show = MethodType(lambda slf: slf, stub_inst)

    stub_mod = Mock(instance=stub_inst)
    return stub_mod


# dummy pyjnius class to silence the import + config
class DummyJnius:
    '''
    Mocked PyJNIus module.
    '''

    def __init__(self, *args, **kwargs):
        class JavaClass:
            '''
            Mocked PyJNIus JavaClass object.
            '''

            def __init__(self):
                self.ANDROID_VERSION = None
                self.SDK_INT = 1
                self.mActivity = None

        self.autoclass = lambda *a, **kw: JavaClass()


class TestFacade(unittest.TestCase):
    '''
    TestCase for plyer.utils.Proxy and plyer.facades.
    '''

    def test_facade_existing_platforms(self):
        '''
        Test for returning an object for Android API implementation
        from Proxy object using a dynamically generated dummy objects.
        '''
        _original = plyer.utils.platform

        for plat in {'android', 'ios', 'win', 'macosx', 'linux'}:
            plyer.utils.platform = plat

            if plat == 'android':
                # android platform automatically imports jnius
                sys.modules['jnius'] = DummyJnius()

            # create stub module with instance func and class
            stub_mod = mock_platform_module(
                mod='plyer.platforms.{}.dummy'.format(plat),
                platform=plyer.utils.platform,
                cls='Dummy'
            )

            proxy_cls = plyer.utils.Proxy
            target = 'builtins.__import__'

            with patch(target=target, return_value=stub_mod):
                dummy = proxy_cls('dummy', stub_mod)

                self.assertEqual(
                    str(dummy.__class__).split("'")[1],
                    'plyer.platforms.{}.dummy.{}Dummy'.format(
                        plat, plat.title()
                    )
                )
                self.assertEqual(
                    str(dummy.show().__class__).split("'")[1],
                    'plyer.platforms.{}.dummy.{}Dummy'.format(
                        plat, plat.title()
                    )
                )

        plyer.utils.platform = _original

    def test_facade_unknown(self):
        '''
        Test fallback of Proxy to facade if there
        is no such requested platform.
        '''

        _original = plyer.utils.platform
        plyer.utils.platform = 'unknown'

        # no 'unknown' platform (folder), fallback to facade
        class MockedProxy(plyer.utils.Proxy):
            '''
            Partially mocked Proxy class, so that we pull the error
            from traceback.print_exc to the test and check the calls.
            '''

            # _ensure_obj is called only once, to either
            # get the platform object or fall back to facade
            # therefore the three self.asserts below will return
            # different values
            expected_asserts = [True, False, False]

            def _ensure_obj(inst):
                # called once, prints to stderr

                # mock stderr because traceback.print_exc uses it
                # https://github.com/python/cpython/blob/
                # 16dfca4d829e45f36e71bf43f83226659ce49315/Lib/traceback.py#L99
                sys.stderr = Mock()

                # call the original function to trigger
                # ImportError warnings in stderr
                super(MockedProxy, inst)._ensure_obj()

                # Traceback (most recent call last):
                #   File "/app/plyer/utils.py", line 88, in _ensure_obj
                #     mod = __import__(module, fromlist='.')
                # ImportError: No module named unknown.dummy

                # must not use self.assertX
                # (has to be checked on the go!)
                expected_bool = MockedProxy.expected_asserts.pop(0)
                call_count = sys.stderr.write.call_count
                assert (call_count == 6) == expected_bool, call_count

                # return stderr to the original state
                sys.stderr = sys.__stderr__

        proxy_cls = MockedProxy
        facade = Mock()
        dummy = proxy_cls('dummy', facade)

        self.assertEqual(dummy._mock_new_parent, facade)
        plyer.utils.platform = _original


if __name__ == '__main__':
    unittest.main()