1 // Copyright (C) 2016-2020 Jonathan Müller <jonathanmueller.dev@gmail.com>
2 // This file is subject to the license terms in the LICENSE file
3 // found in the top-level directory of this distribution.
4
5 #include <type_safe/constrained_type.hpp>
6
7 #include <catch.hpp>
8
9 using namespace type_safe;
10
11 struct test_verifier
12 {
13 static bool expected;
14
15 template <typename T, typename Predicate>
verifytest_verifier16 static T&& verify(T&& value, const Predicate& p)
17 {
18 REQUIRE(p(value) == expected);
19 return std::forward<T>(value);
20 }
21 };
22
23 bool test_verifier::expected;
24
25 struct test_predicate
26 {
operator ()test_predicate27 bool operator()(int i) const
28 {
29 return i != -1;
30 }
31 };
32
33 TEST_CASE("constrained_type")
34 {
35 using my_int = constrained_type<int, test_predicate, test_verifier>;
36
37 SECTION("constructor")
38 {
39 test_verifier::expected = true;
40 my_int a(5);
41 REQUIRE(a.get_value() == 5);
42 my_int b(-4);
43 REQUIRE(b.get_value() == -4);
44
45 test_verifier::expected = false;
46 my_int c(-1);
47 REQUIRE(c.get_value() == -1);
48 }
49 SECTION("assignment")
50 {
51 test_verifier::expected = true;
52 my_int a(5);
53 a = 2;
54 a = 3;
55 a = -2;
56
57 test_verifier::expected = false;
58 a = -1;
59 }
60 SECTION("constrain")
61 {
62 test_verifier::expected = true;
63 my_int a = constrain<test_verifier>(5, test_predicate{});
64 REQUIRE(a.get_value() == 5);
65 my_int b = constrain<test_verifier>(-4, test_predicate{});
66 REQUIRE(b.get_value() == -4);
67
68 test_verifier::expected = false;
69 my_int c = constrain<test_verifier>(-1, test_predicate{});
70 REQUIRE(c.get_value() == -1);
71 }
72 SECTION("modify()")
73 {
74 // with() is the same
75 test_verifier::expected = true;
76 my_int a(4);
77 {
78 auto modify = a.modify();
79 modify.get() += 4;
80 }
81 REQUIRE(a.get_value() == 8);
82 {
83 auto modify = a.modify();
84 modify.get() -= 5;
85 modify.get() = 2;
86 }
87 REQUIRE(a.get_value() == 2);
88
89 {
90 auto modify = a.modify();
91 modify.get() = -1;
92 test_verifier::expected = false;
93 }
94 REQUIRE(a.get_value() == -1);
95 try
96 {
97 auto modify = a.modify();
98 modify.get() = -1;
99 throw 0;
100 }
101 catch (...)
102 {
103 REQUIRE(a.get_value() == -1);
104 }
105 }
106 }
107
108 TEST_CASE("constrained_ref")
109 {
110 using my_ref = constrained_ref<int, test_predicate, test_verifier>;
111
112 auto valid1 = 5;
113 auto valid2 = -4;
114 auto invalid = -1;
115
116 SECTION("constructor")
117 {
118 test_verifier::expected = true;
119 my_ref a(valid1);
120 REQUIRE(a.get_value() == 5);
121 my_ref b(valid2);
122 REQUIRE(b.get_value() == -4);
123
124 test_verifier::expected = false;
125 my_ref c(invalid);
126 REQUIRE(c.get_value() == -1);
127 }
128 SECTION("modify()")
129 {
130 // with() is the same
131 test_verifier::expected = true;
132 my_ref a(valid1);
133 {
134 auto modify = a.modify();
135 modify.get() += 3;
136 }
137 REQUIRE(a.get_value() == 8);
138 {
139 auto modify = a.modify();
140 modify.get() -= 5;
141 modify.get() = 2;
142 }
143 REQUIRE(a.get_value() == 2);
144
145 {
146 auto modify = a.modify();
147 modify.get() = -1;
148 test_verifier::expected = false;
149 }
150 REQUIRE(a.get_value() == -1);
151 try
152 {
153 auto modify = a.modify();
154 modify.get() = -1;
155 throw 0;
156 }
157 catch (...)
158 {
159 REQUIRE(a.get_value() == -1);
160 }
161 }
162 }
163
164 TEST_CASE("throwing_verifier")
165 {
166 auto dummy1 = 0, dummy2 = 0;
167
168 constrained_type<int*, constraints::non_null, throwing_verifier> a(&dummy1);
169 REQUIRE_NOTHROW((a = &dummy2, true));
170 REQUIRE_THROWS_AS(a = static_cast<int*>(nullptr), constrain_error);
171
172 constrained_type<int*, constraints::non_null, throwing_verifier> b
173 = sanitize(&dummy2, constraints::non_null{});
174 REQUIRE_NOTHROW((b = &dummy2, true));
175 REQUIRE_THROWS_AS(b = static_cast<int*>(nullptr), constrain_error);
176 }
177
178 TEST_CASE("constraints::non_null")
179 {
180 #ifndef TYPE_SAFE_TEST_NO_STATIC_ASSERT
181 // conversion checks
182 using ptr = constrained_type<int*, constraints::non_null>;
183 static_assert(std::is_constructible<ptr, int*>::value, "");
184 static_assert(!std::is_constructible<ptr, std::nullptr_t>::value, "");
185 static_assert(std::is_assignable<ptr, int*>::value, "");
186 static_assert(!std::is_assignable<ptr, std::nullptr_t>::value, "");
187 #endif
188
189 constraints::non_null p;
190 REQUIRE(!p(static_cast<int*>(nullptr)));
191 REQUIRE(!p(static_cast<int*>(0)));
192
193 int a;
194 REQUIRE(p(&a));
195 }
196
197 struct my_container
198 {
199 bool empty;
200 };
201
empty(my_container c)202 bool empty(my_container c)
203 {
204 return c.empty;
205 }
206
207 TEST_CASE("constraints::non_empty")
208 {
209 constraints::non_empty p;
210 REQUIRE(p(std::string("hi")));
211 REQUIRE(!p(std::string()));
212
213 REQUIRE(p(my_container{false}));
214 REQUIRE(!p(my_container{true}));
215 }
216
217 TEST_CASE("constraints::non_default")
218 {
219 constraints::non_default p;
220 REQUIRE(p(5));
221 REQUIRE(p(-1));
222 REQUIRE(!p(int()));
223 }
224
225 TEST_CASE("constraints::non_invalid")
226 {
227 constraints::non_invalid p;
228
229 REQUIRE(p(&p));
230 REQUIRE(!p(static_cast<void*>(nullptr)));
231
232 REQUIRE(!p(false));
233 REQUIRE(p(true));
234
235 struct my_bool
236 {
237 bool b;
238
operator boolmy_bool239 explicit operator bool() const noexcept
240 {
241 return b;
242 }
243 };
244 REQUIRE(p(my_bool{true}));
245 REQUIRE(!p(my_bool{false}));
246 }
247