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