test_virtual_functions.py 8.11 KB
Newer Older
Dean Moldovan's avatar
Dean Moldovan committed
1
import pytest
2
import pybind11_tests
Dean Moldovan's avatar
Dean Moldovan committed
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
from pybind11_tests import ConstructorStats


def test_override(capture, msg):
    from pybind11_tests import (ExampleVirt, runExampleVirt, runExampleVirtVirtual,
                                runExampleVirtBool)

    class ExtendedExampleVirt(ExampleVirt):
        def __init__(self, state):
            super(ExtendedExampleVirt, self).__init__(state + 1)
            self.data = "Hello world"

        def run(self, value):
            print('ExtendedExampleVirt::run(%i), calling parent..' % value)
            return super(ExtendedExampleVirt, self).run(value + 1)

        def run_bool(self):
            print('ExtendedExampleVirt::run_bool()')
            return False

23
24
25
        def get_string1(self):
            return "override1"

Dean Moldovan's avatar
Dean Moldovan committed
26
27
28
        def pure_virtual(self):
            print('ExtendedExampleVirt::pure_virtual(): %s' % self.data)

29
30
31
32
33
34
35
    class ExtendedExampleVirt2(ExtendedExampleVirt):
        def __init__(self, state):
            super(ExtendedExampleVirt2, self).__init__(state + 1)

        def get_string2(self):
            return "override2"

Dean Moldovan's avatar
Dean Moldovan committed
36
37
38
    ex12 = ExampleVirt(10)
    with capture:
        assert runExampleVirt(ex12, 20) == 30
39
40
41
    assert capture == """
        Original implementation of ExampleVirt::run(state=10, value=20, str1=default1, str2=default2)
    """  # noqa: E501 line too long
Dean Moldovan's avatar
Dean Moldovan committed
42
43
44
45
46
47
48
49
50
51

    with pytest.raises(RuntimeError) as excinfo:
        runExampleVirtVirtual(ex12)
    assert msg(excinfo.value) == 'Tried to call pure virtual function "ExampleVirt::pure_virtual"'

    ex12p = ExtendedExampleVirt(10)
    with capture:
        assert runExampleVirt(ex12p, 20) == 32
    assert capture == """
        ExtendedExampleVirt::run(20), calling parent..
52
        Original implementation of ExampleVirt::run(state=11, value=21, str1=override1, str2=default2)
53
    """  # noqa: E501 line too long
Dean Moldovan's avatar
Dean Moldovan committed
54
55
56
57
58
59
60
    with capture:
        assert runExampleVirtBool(ex12p) is False
    assert capture == "ExtendedExampleVirt::run_bool()"
    with capture:
        runExampleVirtVirtual(ex12p)
    assert capture == "ExtendedExampleVirt::pure_virtual(): Hello world"

61
62
63
64
65
66
    ex12p2 = ExtendedExampleVirt2(15)
    with capture:
        assert runExampleVirt(ex12p2, 50) == 68
    assert capture == """
        ExtendedExampleVirt::run(50), calling parent..
        Original implementation of ExampleVirt::run(state=17, value=51, str1=override1, str2=override2)
67
    """  # noqa: E501 line too long
68

Dean Moldovan's avatar
Dean Moldovan committed
69
    cstats = ConstructorStats.get(ExampleVirt)
70
71
    assert cstats.alive() == 3
    del ex12, ex12p, ex12p2
Dean Moldovan's avatar
Dean Moldovan committed
72
    assert cstats.alive() == 0
73
    assert cstats.values() == ['10', '11', '17']
Dean Moldovan's avatar
Dean Moldovan committed
74
75
76
77
    assert cstats.copy_constructions == 0
    assert cstats.move_constructions >= 0


78
def test_inheriting_repeat():
Dean Moldovan's avatar
Dean Moldovan committed
79
80
    from pybind11_tests import A_Repeat, B_Repeat, C_Repeat, D_Repeat, A_Tpl, B_Tpl, C_Tpl, D_Tpl

81
    class AR(A_Repeat):
Dean Moldovan's avatar
Dean Moldovan committed
82
83
84
        def unlucky_number(self):
            return 99

85
    class AT(A_Tpl):
Dean Moldovan's avatar
Dean Moldovan committed
86
87
88
        def unlucky_number(self):
            return 999

89
    obj = AR()
90
    assert obj.say_something(3) == "hihihi"
Dean Moldovan's avatar
Dean Moldovan committed
91
    assert obj.unlucky_number() == 99
92
    assert obj.say_everything() == "hi 99"
Dean Moldovan's avatar
Dean Moldovan committed
93

94
    obj = AT()
95
    assert obj.say_something(3) == "hihihi"
Dean Moldovan's avatar
Dean Moldovan committed
96
    assert obj.unlucky_number() == 999
97
    assert obj.say_everything() == "hi 999"
Dean Moldovan's avatar
Dean Moldovan committed
98
99

    for obj in [B_Repeat(), B_Tpl()]:
100
        assert obj.say_something(3) == "B says hi 3 times"
Dean Moldovan's avatar
Dean Moldovan committed
101
102
        assert obj.unlucky_number() == 13
        assert obj.lucky_number() == 7.0
103
        assert obj.say_everything() == "B says hi 1 times 13"
Dean Moldovan's avatar
Dean Moldovan committed
104
105

    for obj in [C_Repeat(), C_Tpl()]:
106
        assert obj.say_something(3) == "B says hi 3 times"
Dean Moldovan's avatar
Dean Moldovan committed
107
108
        assert obj.unlucky_number() == 4444
        assert obj.lucky_number() == 888.0
109
        assert obj.say_everything() == "B says hi 1 times 4444"
Dean Moldovan's avatar
Dean Moldovan committed
110

111
    class CR(C_Repeat):
Dean Moldovan's avatar
Dean Moldovan committed
112
113
114
        def lucky_number(self):
            return C_Repeat.lucky_number(self) + 1.25

115
    obj = CR()
116
    assert obj.say_something(3) == "B says hi 3 times"
Dean Moldovan's avatar
Dean Moldovan committed
117
118
    assert obj.unlucky_number() == 4444
    assert obj.lucky_number() == 889.25
119
    assert obj.say_everything() == "B says hi 1 times 4444"
Dean Moldovan's avatar
Dean Moldovan committed
120

121
    class CT(C_Tpl):
Dean Moldovan's avatar
Dean Moldovan committed
122
123
        pass

124
    obj = CT()
125
    assert obj.say_something(3) == "B says hi 3 times"
Dean Moldovan's avatar
Dean Moldovan committed
126
127
    assert obj.unlucky_number() == 4444
    assert obj.lucky_number() == 888.0
128
    assert obj.say_everything() == "B says hi 1 times 4444"
Dean Moldovan's avatar
Dean Moldovan committed
129

130
    class CCR(CR):
Dean Moldovan's avatar
Dean Moldovan committed
131
        def lucky_number(self):
132
            return CR.lucky_number(self) * 10
Dean Moldovan's avatar
Dean Moldovan committed
133

134
    obj = CCR()
135
    assert obj.say_something(3) == "B says hi 3 times"
Dean Moldovan's avatar
Dean Moldovan committed
136
137
    assert obj.unlucky_number() == 4444
    assert obj.lucky_number() == 8892.5
138
    assert obj.say_everything() == "B says hi 1 times 4444"
Dean Moldovan's avatar
Dean Moldovan committed
139

140
    class CCT(CT):
Dean Moldovan's avatar
Dean Moldovan committed
141
        def lucky_number(self):
142
            return CT.lucky_number(self) * 1000
Dean Moldovan's avatar
Dean Moldovan committed
143

144
    obj = CCT()
145
    assert obj.say_something(3) == "B says hi 3 times"
Dean Moldovan's avatar
Dean Moldovan committed
146
147
    assert obj.unlucky_number() == 4444
    assert obj.lucky_number() == 888000.0
148
    assert obj.say_everything() == "B says hi 1 times 4444"
Dean Moldovan's avatar
Dean Moldovan committed
149

150
    class DR(D_Repeat):
Dean Moldovan's avatar
Dean Moldovan committed
151
152
153
154
155
156
157
        def unlucky_number(self):
            return 123

        def lucky_number(self):
            return 42.0

    for obj in [D_Repeat(), D_Tpl()]:
158
        assert obj.say_something(3) == "B says hi 3 times"
Dean Moldovan's avatar
Dean Moldovan committed
159
160
        assert obj.unlucky_number() == 4444
        assert obj.lucky_number() == 888.0
161
        assert obj.say_everything() == "B says hi 1 times 4444"
Dean Moldovan's avatar
Dean Moldovan committed
162

163
    obj = DR()
164
    assert obj.say_something(3) == "B says hi 3 times"
Dean Moldovan's avatar
Dean Moldovan committed
165
166
    assert obj.unlucky_number() == 123
    assert obj.lucky_number() == 42.0
167
    assert obj.say_everything() == "B says hi 1 times 123"
Dean Moldovan's avatar
Dean Moldovan committed
168

169
    class DT(D_Tpl):
Dean Moldovan's avatar
Dean Moldovan committed
170
        def say_something(self, times):
171
            return "DT says:" + (' quack' * times)
Dean Moldovan's avatar
Dean Moldovan committed
172
173
174
175
176
177
178

        def unlucky_number(self):
            return 1234

        def lucky_number(self):
            return -4.25

179
180
    obj = DT()
    assert obj.say_something(3) == "DT says: quack quack quack"
Dean Moldovan's avatar
Dean Moldovan committed
181
182
    assert obj.unlucky_number() == 1234
    assert obj.lucky_number() == -4.25
183
    assert obj.say_everything() == "DT says: quack 1234"
184

185
    class DT2(DT):
186
        def say_something(self, times):
187
            return "DT2: " + ('QUACK' * times)
188
189
190
191

        def unlucky_number(self):
            return -3

192
    class BT(B_Tpl):
193
        def say_something(self, times):
194
195
            return "BT" * times

196
197
        def unlucky_number(self):
            return -7
198

199
200
201
        def lucky_number(self):
            return -1.375

202
203
    obj = BT()
    assert obj.say_something(3) == "BTBTBT"
204
205
    assert obj.unlucky_number() == -7
    assert obj.lucky_number() == -1.375
206
207
    assert obj.say_everything() == "BT -7"

Dean Moldovan's avatar
Dean Moldovan committed
208

209
210
@pytest.mark.skipif(not hasattr(pybind11_tests, 'NCVirt'),
                    reason="NCVirt test broken on ICPC")
211
def test_move_support():
Dean Moldovan's avatar
Dean Moldovan committed
212
213
214
215
216
    from pybind11_tests import NCVirt, NonCopyable, Movable

    class NCVirtExt(NCVirt):
        def get_noncopyable(self, a, b):
            # Constructs and returns a new instance:
217
            nc = NonCopyable(a * a, b * b)
Dean Moldovan's avatar
Dean Moldovan committed
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
            return nc

        def get_movable(self, a, b):
            # Return a referenced copy
            self.movable = Movable(a, b)
            return self.movable

    class NCVirtExt2(NCVirt):
        def get_noncopyable(self, a, b):
            # Keep a reference: this is going to throw an exception
            self.nc = NonCopyable(a, b)
            return self.nc

        def get_movable(self, a, b):
            # Return a new instance without storing it
            return Movable(a, b)

    ncv1 = NCVirtExt()
236
237
    assert ncv1.print_nc(2, 3) == "36"
    assert ncv1.print_movable(4, 5) == "9"
Dean Moldovan's avatar
Dean Moldovan committed
238
    ncv2 = NCVirtExt2()
239
    assert ncv2.print_movable(7, 7) == "14"
Dean Moldovan's avatar
Dean Moldovan committed
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
    # Don't check the exception message here because it differs under debug/non-debug mode
    with pytest.raises(RuntimeError):
        ncv2.print_nc(9, 9)

    nc_stats = ConstructorStats.get(NonCopyable)
    mv_stats = ConstructorStats.get(Movable)
    assert nc_stats.alive() == 1
    assert mv_stats.alive() == 1
    del ncv1, ncv2
    assert nc_stats.alive() == 0
    assert mv_stats.alive() == 0
    assert nc_stats.values() == ['4', '9', '9', '9']
    assert mv_stats.values() == ['4', '5', '7', '7']
    assert nc_stats.copy_constructions == 0
    assert mv_stats.copy_constructions == 1
    assert nc_stats.move_constructions >= 0
    assert mv_stats.move_constructions >= 0