summaryrefslogtreecommitdiffstats
path: root/tests/auto/corelib/tools/quniquehandle/tst_quniquehandle.cpp
blob: ed46999e737c9205c487755f0f92a061ac936c66 (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
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
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only

#include <private/quniquehandle_p.h>

#include <QTest>

QT_USE_NAMESPACE;

// clang-format off
namespace GlobalResource {

std::array<bool, 3> s_resources = { false, false, false };

using handle = size_t;
constexpr handle s_invalidHandle = static_cast<handle>(-1);

handle open()
{
    const auto it = std::find_if(s_resources.begin(), s_resources.end(),
                                 [](bool resource) {
                                     return !resource;
                                 });

    if (it == s_resources.end())
        return s_invalidHandle;

    *it = true;

    return std::distance(s_resources.begin(), it);
}

bool open(handle* dest)
{
    const handle resource = open();

    if (resource == s_invalidHandle)
        return false;

    *dest = resource;
    return true;
}

bool close(handle h)
{
    if (h >= s_resources.size())
        return false; // Invalid handle

    if (!s_resources[h])
        return false; // Un-allocated resource

    s_resources[h] = false;
    return true;
}

bool isOpen(handle h)
{
    return s_resources[h];
}

void reset()
{
    std::fill(s_resources.begin(), s_resources.end(), false);
}

bool isReset()
{
    return std::all_of(s_resources.begin(), s_resources.end(), [](bool res) {
        return !res;
    });
}

} // namespace GlobalResource

struct TestTraits
{
    using Type = GlobalResource::handle;

    static bool close(Type handle)
    {
        return GlobalResource::close(handle);
    }

    static Type invalidValue() noexcept
    {
        return GlobalResource::s_invalidHandle;
    }
};

using Handle = QUniqueHandle<TestTraits>;

class tst_QUniqueHandle : public QObject
{
    Q_OBJECT

private slots:

    void init() const
    {
        GlobalResource::reset();
    }

    void cleanup() const
    {
        QVERIFY(GlobalResource::isReset());
    }

    void defaultConstructor_initializesToInvalidHandle() const
    {
        const Handle h;
        QCOMPARE_EQ(h.get(), TestTraits::invalidValue());
    }

    void constructor_initializesToValid_whenCalledWithValidHandle() const
    {
        const auto res = GlobalResource::open();

        const Handle h{ res };

        QCOMPARE_EQ(h.get(), res);
    }

    void copyConstructor_and_assignmentOperator_areDeleted() const
    {
        static_assert(!std::is_copy_constructible_v<Handle> && !std::is_copy_assignable_v<Handle>);
    }

    void moveConstructor_movesOwnershipAndResetsSource() const
    {
        Handle source{ GlobalResource::open() };
        const Handle dest{ std::move(source) };

        QVERIFY(!source.isValid());
        QVERIFY(dest.isValid());
        QVERIFY(GlobalResource::isOpen(dest.get()));
    }

    void moveAssignment_movesOwnershipAndResetsSource() const
    {
        Handle source{ GlobalResource::open() };
        Handle dest;
        dest = { std::move(source) };

        QVERIFY(!source.isValid());
        QVERIFY(dest.isValid());
        QVERIFY(GlobalResource::isOpen(dest.get()));
    }

    void isValid_returnsFalse_onlyWhenHandleIsInvalid() const
    {
        const Handle invalid;
        QVERIFY(!invalid.isValid());

        const Handle valid{ GlobalResource::open() };
        QVERIFY(valid.isValid());
    }

    void destructor_callsClose_whenHandleIsValid()
    {
        {
            const Handle h0{ GlobalResource::open() };
            const Handle h1{ GlobalResource::open() };
            const Handle h2{ GlobalResource::open() };
            QVERIFY(!GlobalResource::isReset());
        }

        QVERIFY(GlobalResource::isReset());
    }

    void operatorBool_returnsFalse_onlyWhenHandleIsInvalid() const
    {
        const Handle invalid;
        QVERIFY(!invalid);

        const Handle valid{ GlobalResource::open() };
        QVERIFY(valid);
    }

    void get_returnsValue() const
    {
        const Handle invalid;
        QCOMPARE_EQ(invalid.get(), GlobalResource::s_invalidHandle);

        const auto resource = GlobalResource::open();
        const Handle valid{ resource };
        QCOMPARE_EQ(valid.get(), resource);
    }

    void reset_resetsPreviousValueAndTakesOwnership() const
    {
        const auto resource0 = GlobalResource::open();
        const auto resource1 = GlobalResource::open();

        Handle h1{ resource0 };
        h1.reset(resource1);

        QVERIFY(!GlobalResource::isOpen(resource0));
        QVERIFY(GlobalResource::isOpen(resource1));
    }

    void release_returnsInvalidResource_whenCalledOnInvalidHandle() const
    {
        Handle h;
        QCOMPARE_EQ(h.release(), GlobalResource::s_invalidHandle);
    }

    void release_releasesOwnershipAndReturnsResource_whenHandleOwnsObject() const
    {
        GlobalResource::handle resource{ GlobalResource::open() };
        GlobalResource::handle released{};
        {
            Handle h{ resource };
            released = h.release();
        }
        QVERIFY(GlobalResource::isOpen(resource));
        QCOMPARE_EQ(resource, released);

        GlobalResource::close(resource);
    }

    void swap_swapsOwnership() const
    {
        const auto resource0 = GlobalResource::open();
        const auto resource1 = GlobalResource::open();

        Handle h0{ resource0 };
        Handle h1{ resource1 };

        std::swap(h0, h1);

        QCOMPARE_EQ(h0.get(), resource1);
        QCOMPARE_EQ(h1.get(), resource0);
    }

    void comparison_behavesAsInt_whenHandleTypeIsInt_data() const
    {
        QTest::addColumn<int>("lhs");
        QTest::addColumn<int>("rhs");

        QTest::addRow("lhs == rhs") << 1 << 1;
        QTest::addRow("lhs < rhs") << 0 << 1;
        QTest::addRow("lhs > rhs") << 1 << 0;
    }

    void comparison_behavesAsInt_whenHandleTypeIsInt() const
    {
        struct IntTraits
        {
            using Type = int;

            static bool close(Type)
            {
                return true;
            }

            static Type invalidValue() noexcept
            {
                return INT_MAX;
            }
        };

        using Handle = QUniqueHandle<IntTraits>;

        QFETCH(int, lhs);
        QFETCH(int, rhs);

        QCOMPARE_EQ(Handle{ lhs } == Handle{ rhs }, lhs == rhs);
        QCOMPARE_EQ(Handle{ lhs } != Handle{ rhs }, lhs != rhs);
        QCOMPARE_EQ(Handle{ lhs } < Handle{ rhs }, lhs < rhs);
        QCOMPARE_EQ(Handle{ lhs } <= Handle{ rhs }, lhs <= rhs);
        QCOMPARE_EQ(Handle{ lhs } > Handle{ rhs }, lhs > rhs);
        QCOMPARE_EQ(Handle{ lhs } >= Handle{ rhs },  lhs >= rhs);

        QCOMPARE_EQ(Handle{ }, Handle{ });
    }

    void sort_sortsHandles() const
    {
        const auto resource0 = GlobalResource::open();
        const auto resource1 = GlobalResource::open();

        QVERIFY(resource1 > resource0); // Precondition of underlying allocator

        Handle h0{ resource0 };
        Handle h1{ resource1 };

        std::vector<Handle> handles;
        handles.push_back(std::move(h1));
        handles.push_back(std::move(h0));

        std::sort(handles.begin(), handles.end());

        QCOMPARE_LT(handles.front(), handles.back());
        QCOMPARE_LT(handles.front().get(), handles.back().get());
    }

    void addressOf_returnsAddressOfHandle() const
    {
        Handle h;
        QVERIFY(GlobalResource::open(&h));
        QVERIFY(h.isValid());
    }

};

// clang-format on
QTEST_MAIN(tst_QUniqueHandle)
#include "tst_quniquehandle.moc"