summaryrefslogtreecommitdiff
path: root/Externals/WIL/tests/test_objects.h
blob: 3da2005b18919ba308f751fe0d6dfe500e079cdc (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
#pragma once

#include "catch.hpp"

// Useful for validating that the copy constructor is never called (e.g. to validate perfect forwarding). Note that
// the copy constructor/assignment operator are not deleted since we want to be able to validate in scenarios that
// require CopyConstructible (e.g. for wistd::function)
struct fail_on_copy
{
    fail_on_copy() = default;

    fail_on_copy(const fail_on_copy&)
    {
        FAIL("Copy constructor invoked for fail_on_copy type");
    }

    fail_on_copy(fail_on_copy&&) = default;

    fail_on_copy& operator=(const fail_on_copy&)
    {
        FAIL("Copy assignment operator invoked for fail_on_copy type");
        return *this;
    }

    fail_on_copy& operator=(fail_on_copy&&) = default;
};

// Useful for validating that objects get copied e.g. as opposed to capturing a reference
struct value_holder
{
    int value = 0xbadf00d;

    ~value_holder()
    {
        value = 0xbadf00d;
    }
};

// Useful for validating that functions, etc. are callable with move-only types
// Example real type that is move only is Microsoft::WRL::Wrappers::HString
struct cannot_copy
{
    cannot_copy() = default;
    cannot_copy(const cannot_copy&) = delete;
    cannot_copy& operator=(const cannot_copy&) = delete;

    cannot_copy(cannot_copy&&) = default;
    cannot_copy& operator=(cannot_copy&&) = default;
};

// State for object_counter type. This has the unfortunate side effect that the object_counter type cannot be used in
// contexts that require a default constructible type, but has the nice property that it allows for tests to run
// concurrently
struct object_counter_state
{
    volatile LONG constructed_count = 0;
    volatile LONG destructed_count = 0;
    volatile LONG copy_count = 0;
    volatile LONG move_count = 0;

    LONG instance_count()
    {
        return constructed_count - destructed_count;
    }
};

struct object_counter
{
    object_counter_state* state;

    object_counter(object_counter_state& s) :
        state(&s)
    {
        ::InterlockedIncrement(&state->constructed_count);
    }

    object_counter(const object_counter& other) :
        state(other.state)
    {
        ::InterlockedIncrement(&state->constructed_count);
        ::InterlockedIncrement(&state->copy_count);
    }

    object_counter(object_counter&& other) WI_NOEXCEPT :
        state(other.state)
    {
        ::InterlockedIncrement(&state->constructed_count);
        ::InterlockedIncrement(&state->move_count);
    }

    ~object_counter()
    {
        ::InterlockedIncrement(&state->destructed_count);
        state = nullptr;
    }

    object_counter& operator=(const object_counter&)
    {
        ::InterlockedIncrement(&state->copy_count);
        return *this;
    }

    object_counter& operator=(object_counter&&) WI_NOEXCEPT
    {
        ::InterlockedIncrement(&state->move_count);
        return *this;
    }
};