aboutsummaryrefslogtreecommitdiffstats
path: root/3rdparty/pybind11/tests/test_copy_move.py
blob: 9fef089339ca5f59d09262bab26a2cb3217d97f7 (plain)
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
import pytest

from pybind11_tests import copy_move_policies as m


def test_lacking_copy_ctor():
    with pytest.raises(RuntimeError) as excinfo:
        m.lacking_copy_ctor.get_one()
    assert "is non-copyable!" in str(excinfo.value)


def test_lacking_move_ctor():
    with pytest.raises(RuntimeError) as excinfo:
        m.lacking_move_ctor.get_one()
    assert "is neither movable nor copyable!" in str(excinfo.value)


def test_move_and_copy_casts():
    """Cast some values in C++ via custom type casters and count the number of moves/copies."""

    cstats = m.move_and_copy_cstats()
    c_m, c_mc, c_c = (
        cstats["MoveOnlyInt"],
        cstats["MoveOrCopyInt"],
        cstats["CopyOnlyInt"],
    )

    # The type move constructions/assignments below each get incremented: the move assignment comes
    # from the type_caster load; the move construction happens when extracting that via a cast or
    # loading into an argument.
    assert m.move_and_copy_casts(3) == 18
    assert c_m.copy_assignments + c_m.copy_constructions == 0
    assert c_m.move_assignments == 2
    assert c_m.move_constructions >= 2
    assert c_mc.alive() == 0
    assert c_mc.copy_assignments + c_mc.copy_constructions == 0
    assert c_mc.move_assignments == 2
    assert c_mc.move_constructions >= 2
    assert c_c.alive() == 0
    assert c_c.copy_assignments == 2
    assert c_c.copy_constructions >= 2
    assert c_m.alive() + c_mc.alive() + c_c.alive() == 0


def test_move_and_copy_loads():
    """Call some functions that load arguments via custom type casters and count the number of
    moves/copies."""

    cstats = m.move_and_copy_cstats()
    c_m, c_mc, c_c = (
        cstats["MoveOnlyInt"],
        cstats["MoveOrCopyInt"],
        cstats["CopyOnlyInt"],
    )

    assert m.move_only(10) == 10  # 1 move, c_m
    assert m.move_or_copy(11) == 11  # 1 move, c_mc
    assert m.copy_only(12) == 12  # 1 copy, c_c
    assert m.move_pair((13, 14)) == 27  # 1 c_m move, 1 c_mc move
    assert m.move_tuple((15, 16, 17)) == 48  # 2 c_m moves, 1 c_mc move
    assert m.copy_tuple((18, 19)) == 37  # 2 c_c copies
    # Direct constructions: 2 c_m moves, 2 c_mc moves, 1 c_c copy
    # Extra moves/copies when moving pairs/tuples: 3 c_m, 3 c_mc, 2 c_c
    assert m.move_copy_nested((1, ((2, 3, (4,)), 5))) == 15

    assert c_m.copy_assignments + c_m.copy_constructions == 0
    assert c_m.move_assignments == 6
    assert c_m.move_constructions == 9
    assert c_mc.copy_assignments + c_mc.copy_constructions == 0
    assert c_mc.move_assignments == 5
    assert c_mc.move_constructions == 8
    assert c_c.copy_assignments == 4
    assert c_c.copy_constructions == 6
    assert c_m.alive() + c_mc.alive() + c_c.alive() == 0


@pytest.mark.skipif(not m.has_optional, reason="no <optional>")
def test_move_and_copy_load_optional():
    """Tests move/copy loads of std::optional arguments"""

    cstats = m.move_and_copy_cstats()
    c_m, c_mc, c_c = (
        cstats["MoveOnlyInt"],
        cstats["MoveOrCopyInt"],
        cstats["CopyOnlyInt"],
    )

    # The extra move/copy constructions below come from the std::optional move (which has to move
    # its arguments):
    assert m.move_optional(10) == 10  # c_m: 1 move assign, 2 move construct
    assert m.move_or_copy_optional(11) == 11  # c_mc: 1 move assign, 2 move construct
    assert m.copy_optional(12) == 12  # c_c: 1 copy assign, 2 copy construct
    # 1 move assign + move construct moves each of c_m, c_mc, 1 c_c copy
    # +1 move/copy construct each from moving the tuple
    # +1 move/copy construct each from moving the optional (which moves the tuple again)
    assert m.move_optional_tuple((3, 4, 5)) == 12

    assert c_m.copy_assignments + c_m.copy_constructions == 0
    assert c_m.move_assignments == 2
    assert c_m.move_constructions == 5
    assert c_mc.copy_assignments + c_mc.copy_constructions == 0
    assert c_mc.move_assignments == 2
    assert c_mc.move_constructions == 5
    assert c_c.copy_assignments == 2
    assert c_c.copy_constructions == 5
    assert c_m.alive() + c_mc.alive() + c_c.alive() == 0


def test_private_op_new():
    """An object with a private `operator new` cannot be returned by value"""

    with pytest.raises(RuntimeError) as excinfo:
        m.private_op_new_value()
    assert "is neither movable nor copyable" in str(excinfo.value)

    assert m.private_op_new_reference().value == 1


def test_move_fallback():
    """#389: rvp::move should fall-through to copy on non-movable objects"""

    m1 = m.get_moveissue1(1)
    assert m1.value == 1
    m2 = m.get_moveissue2(2)
    assert m2.value == 2


def test_pytype_rvalue_cast():
    """Make sure that cast from pytype rvalue to other pytype works"""

    value = m.get_pytype_rvalue_castissue(1.0)
    assert value == 1