summaryrefslogtreecommitdiff
path: root/src/compat/option_test.cpp
diff options
context:
space:
mode:
authorBen Longbons <b.r.longbons@gmail.com>2014-09-30 16:20:07 -0700
committerBen Longbons <b.r.longbons@gmail.com>2014-10-04 16:17:30 -0700
commit2b7f56003f27e04868bba2052e5395f68a5ad817 (patch)
treef05ffd6e8d771c7abe85c0a9dadb3836854b6a2d /src/compat/option_test.cpp
parent77563ec532c8b51134c3020fb3c70a8bed8457fb (diff)
downloadtmwa-2b7f56003f27e04868bba2052e5395f68a5ad817.tar.gz
tmwa-2b7f56003f27e04868bba2052e5395f68a5ad817.tar.bz2
tmwa-2b7f56003f27e04868bba2052e5395f68a5ad817.tar.xz
tmwa-2b7f56003f27e04868bba2052e5395f68a5ad817.zip
Implement Option<T> and Borrowed<T>
Diffstat (limited to 'src/compat/option_test.cpp')
-rw-r--r--src/compat/option_test.cpp399
1 files changed, 399 insertions, 0 deletions
diff --git a/src/compat/option_test.cpp b/src/compat/option_test.cpp
new file mode 100644
index 0000000..e4be147
--- /dev/null
+++ b/src/compat/option_test.cpp
@@ -0,0 +1,399 @@
+#include "option.hpp"
+// option_test.cpp - Testsuite for a type that may or may not exist
+//
+// Copyright © 2014 Ben Longbons <b.r.longbons@gmail.com>
+//
+// This file is part of The Mana World (Athena server)
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+#include <gtest/gtest.h>
+
+#include "../strings/literal.hpp"
+
+#include "borrow.hpp"
+
+#include "../diagnostics.hpp"
+//#include "../poison.hpp"
+
+
+namespace tmwa
+{
+TEST(Option, somenone)
+{
+ {
+ option::Option<int> opt = option::None;
+ opt = option::None;
+ }
+ {
+ option::Option<int> opt = option::None<int>;
+ opt = option::None<int>;
+ }
+ {
+ option::Option<int> opt = option::None<int>();
+ opt = option::None<int>();
+ }
+ {
+ option::Option<int> opt = option::Some(123);
+ opt = option::Some(123);
+ }
+ {
+ option::Option<int> opt = option::Some<int>(123);
+ opt = option::Some<int>(123);
+ }
+}
+TEST(Option, somenonenocopy)
+{
+ struct Foo
+ {
+ Foo() = default;
+ Foo(Foo&&) = default;
+ Foo(const Foo&) = delete;
+ Foo& operator = (Foo&&) = default;
+ Foo& operator = (const Foo&) = delete;
+ };
+ {
+ option::Option<Foo> opt = option::None;
+ opt = option::None;
+ }
+ // clang <= 3.4 is buggy
+ // since clang doesn't version, there is no way to restrict it to clang 3.5+
+#ifndef __clang__
+ {
+ option::Option<Foo> opt = option::None<Foo>;
+ opt = option::None<Foo>;
+ }
+#endif
+ {
+ option::Option<Foo> opt = option::None<Foo>();
+ opt = option::None<Foo>();
+ }
+ {
+ option::Option<Foo> opt = option::Some(Foo());
+ opt = option::Some(Foo());
+ }
+ {
+ option::Option<Foo> opt = option::Some<Foo>(Foo());
+ opt = option::Some<Foo>(Foo());
+ }
+}
+TEST(Option, customrepr)
+{
+ int iv = 123;
+ Borrowed<int> i = borrow(iv);
+
+ EXPECT_EQ(&iv, as_raw_pointer(Some(i)));
+
+ {
+ option::Option<Borrowed<int>> opt = option::None;
+ opt = option::None;
+ }
+ {
+ option::Option<Borrowed<int>> opt = option::None<Borrowed<int>>;
+ opt = option::None<Borrowed<int>>;
+ }
+ {
+ option::Option<Borrowed<int>> opt = option::None<Borrowed<int>>();
+ opt = option::None<Borrowed<int>>();
+ }
+ {
+ option::Option<Borrowed<int>> opt = option::Some(i);
+ opt = option::Some(i);
+ }
+ {
+ option::Option<Borrowed<int>> opt = option::Some<Borrowed<int>>(i);
+ opt = option::Some<Borrowed<int>>(i);
+ }
+}
+
+TEST(Option, def)
+{
+ struct Tracked
+ {
+ int id;
+ int gen;
+
+ Tracked(int i, int g=0) : id(i), gen(g) {}
+ Tracked(Tracked&&) = default;
+ Tracked(const Tracked& r) : id(r.id), gen(r.gen + 1) {}
+ Tracked& operator = (Tracked&&) = default;
+ Tracked& operator = (const Tracked& r) { id = r.id; gen = r.gen + 1; return *this; }
+
+ bool operator == (const Tracked& r) const
+ {
+ return this->id == r.id && this->gen == r.gen;
+ }
+ bool operator != (const Tracked& r) const
+ {
+ return !(*this == r);
+ }
+ };
+
+ {
+ option::Option<Tracked> o = option::None;
+ EXPECT_EQ(o.move_or(Tracked(1)), Tracked(1));
+ EXPECT_EQ(o.copy_or(Tracked(2)), Tracked(2));
+ Tracked t3(3);
+ Tracked& r3 = o.ref_or(t3);
+ EXPECT_EQ(&r3, &t3);
+ Tracked t4(4);
+ Tracked *r4 = o.ptr_or(&t4);
+ EXPECT_EQ(r4, &t4);
+ EXPECT_EQ(o.ptr_or(nullptr), nullptr);
+ }
+ {
+ const option::Option<Tracked> o = option::None;
+ EXPECT_EQ(o.copy_or(Tracked(2)), Tracked(2));
+ Tracked t3(3);
+ const Tracked& r3 = o.ref_or(t3);
+ EXPECT_EQ(&r3, &t3);
+ Tracked t4(4);
+ const Tracked *r4 = o.ptr_or(&t4);
+ EXPECT_EQ(r4, &t4);
+ EXPECT_EQ(o.ptr_or(nullptr), nullptr);
+ }
+ {
+ option::Option<Tracked> o = option::Some(Tracked(0));
+ EXPECT_EQ(o.move_or(Tracked(1)), Tracked(0));
+ EXPECT_EQ(o.ptr_or(nullptr), nullptr);
+ o = option::Some(Tracked(0));
+ EXPECT_EQ(o.copy_or(Tracked(2)), Tracked(0, 1));
+ Tracked t3(3);
+ Tracked& r3 = o.ref_or(t3);
+ EXPECT_NE(&r3, &t3);
+ Tracked t4(4);
+ Tracked *r4 = o.ptr_or(&t4);
+ EXPECT_NE(r4, &t4);
+ EXPECT_NE(o.ptr_or(nullptr), nullptr);
+ EXPECT_EQ(&r3, r4);
+ EXPECT_EQ(r4, reinterpret_cast<Tracked *>(&o));
+ }
+ {
+ const option::Option<Tracked> o = option::Some(Tracked(0));
+ EXPECT_EQ(o.copy_or(Tracked(2)), Tracked(0, 1));
+ Tracked t3(3);
+ const Tracked& r3 = o.ref_or(t3);
+ EXPECT_NE(&r3, &t3);
+ Tracked t4(4);
+ const Tracked *r4 = o.ptr_or(&t4);
+ EXPECT_NE(r4, &t4);
+ EXPECT_NE(o.ptr_or(nullptr), nullptr);
+ EXPECT_EQ(&r3, r4);
+ EXPECT_EQ(r4, reinterpret_cast<const Tracked *>(&o));
+ }
+}
+
+TEST(Option, map)
+{
+ struct Foo
+ {
+ Foo() = default;
+ Foo(Foo&&) = default;
+ Foo(const Foo&) = delete;
+ Foo& operator = (Foo&&) = default;
+ Foo& operator = (const Foo&) = delete;
+ };
+
+ // move
+ {
+ option::Option<Foo> o = option::None;
+ EXPECT_EQ(o.ptr_or(nullptr), nullptr);
+ option::Option<int> i = o.move_map([](Foo){ return 0; });
+ EXPECT_EQ(o.ptr_or(nullptr), nullptr);
+ EXPECT_EQ(i.ptr_or(nullptr), nullptr);
+ }
+ {
+ option::Option<Foo> o = option::Some(Foo());
+ EXPECT_NE(o.ptr_or(nullptr), nullptr);
+ option::Option<int> i = o.move_map([](Foo){ return 1; });
+ EXPECT_EQ(o.ptr_or(nullptr), nullptr);
+ EXPECT_NE(i.ptr_or(nullptr), nullptr);
+ EXPECT_EQ(i.copy_or(0), 1);
+ }
+ // mut ref
+ {
+ option::Option<Foo> o = option::None;
+ EXPECT_EQ(o.ptr_or(nullptr), nullptr);
+ option::Option<int> i = o.map([](Foo&){ return 0; });
+ EXPECT_EQ(o.ptr_or(nullptr), nullptr);
+ EXPECT_EQ(i.ptr_or(nullptr), nullptr);
+ }
+ {
+ option::Option<Foo> o = option::Some(Foo());
+ EXPECT_NE(o.ptr_or(nullptr), nullptr);
+ option::Option<int> i = o.map([](Foo&){ return 1; });
+ EXPECT_NE(o.ptr_or(nullptr), nullptr);
+ EXPECT_NE(i.ptr_or(nullptr), nullptr);
+ EXPECT_EQ(i.copy_or(0), 1);
+ }
+ // const ref
+ {
+ option::Option<Foo> o = option::None;
+ EXPECT_EQ(o.ptr_or(nullptr), nullptr);
+ option::Option<int> i = o.map([](const Foo&){ return 0; });
+ EXPECT_EQ(o.ptr_or(nullptr), nullptr);
+ EXPECT_EQ(i.ptr_or(nullptr), nullptr);
+ }
+ {
+ option::Option<Foo> o = option::Some(Foo());
+ EXPECT_NE(o.ptr_or(nullptr), nullptr);
+ option::Option<int> i = o.map([](const Foo&){ return 1; });
+ EXPECT_NE(o.ptr_or(nullptr), nullptr);
+ EXPECT_NE(i.ptr_or(nullptr), nullptr);
+ EXPECT_EQ(i.copy_or(0), 1);
+ }
+}
+
+#if __cplusplus >= 201300 // c++14 as given by gcc 4.9
+# define DECLTYPE_AUTO decltype(auto)
+#else
+# define DECLTYPE_AUTO auto&&
+#endif
+
+TEST(Option, unwrap)
+{
+ int x;
+
+ Option<int> v = Some(1);
+ Option<int>& l = v;
+ Option<int>&& r = std::move(v);
+ const Option<int> cv = v;
+ // significantly, see the mut
+ const Option<int>& cl = v;
+ const Option<int>&& cr = std::move(v);
+
+ auto fv = [&]() -> Option<int> { return v; };
+ auto fl = [&]() -> Option<int>& { return l; };
+ auto fr = [&]() -> Option<int>&& { return std::move(r); };
+ auto fcv = [&]() -> const Option<int> { return v; };
+ auto fcl = [&]() -> const Option<int>& { return l; };
+ auto fcr = [&]() -> const Option<int>&& { return std::move(r); };
+
+ DIAG_PUSH();
+ DIAG_I(useless_cast);
+
+#define CHECK(v, t) \
+ { \
+ DECLTYPE_AUTO out = TRY_UNWRAP(v, abort() ); \
+ DECLTYPE_AUTO cmp = static_cast<t>(x); \
+ static_assert(std::is_same<decltype(out), decltype(cmp)>::value, #v); \
+ }
+
+ CHECK(v, int&);
+ CHECK(cv, const int&);
+ CHECK(l, int&);
+ CHECK(cl, const int&);
+ CHECK(r, int&);
+ CHECK(cr, const int&);
+ // repeat the same forcing expressions, since that matters with decltype
+ CHECK((v), int&);
+ CHECK((cv), const int&);
+ CHECK((l), int&);
+ CHECK((cl), const int&);
+ CHECK((r), int&);
+ CHECK((cr), const int&);
+
+ CHECK(fv(), int);
+ CHECK(fcv(), int);
+ CHECK(fl(), int&);
+ CHECK(fcl(), const int&);
+ CHECK(fr(), int&&);
+ CHECK(fcr(), const int&&);
+
+ DIAG_POP();
+#undef CHECK
+
+ v = None; TRY_UNWRAP(v, v = Some(1));
+ v = None; TRY_UNWRAP(l, v = Some(1));
+ v = None; TRY_UNWRAP(cl, v = Some(1));
+ v = None; TRY_UNWRAP(r, v = Some(1));
+ v = None; TRY_UNWRAP(cr, v = Some(1));
+
+ v = None; TRY_UNWRAP(fl(), v = Some(1));
+ v = None; TRY_UNWRAP(fcl(), v = Some(1));
+ v = None; TRY_UNWRAP(fr(), v = Some(1));
+ v = None; TRY_UNWRAP(fcr(), v = Some(1));
+}
+
+TEST(Option, flatten)
+{
+ using option::Option;
+ using option::Some;
+ using option::None;
+
+ struct Foo
+ {
+ int x;
+ };
+ auto f1 = Some(Foo{42});
+ auto f2 = Some(f1);
+ auto f3 = Some(f2);
+ EXPECT_EQ(flatten(f1).copy_or(Foo{404}).x, 42);
+ EXPECT_EQ(flatten(f2).copy_or(Foo{404}).x, 42);
+ EXPECT_EQ(flatten(f3).copy_or(Foo{404}).x, 42);
+
+ decltype(f1) n1 = None;
+ decltype(f2) n2a = None;
+ decltype(f2) n2b = Some(n1);
+ decltype(f3) n3a = None;
+ decltype(f3) n3b = Some(n2a);
+ decltype(f3) n3c = Some(n2b);
+ EXPECT_EQ(flatten(n1).copy_or(Foo{404}).x, 404);
+ EXPECT_EQ(flatten(n2a).copy_or(Foo{404}).x, 404);
+ EXPECT_EQ(flatten(n2b).copy_or(Foo{404}).x, 404);
+ EXPECT_EQ(flatten(n3a).copy_or(Foo{404}).x, 404);
+ EXPECT_EQ(flatten(n3b).copy_or(Foo{404}).x, 404);
+ EXPECT_EQ(flatten(n3c).copy_or(Foo{404}).x, 404);
+}
+
+#define EQ(a, b) ({ EXPECT_TRUE(a == b); EXPECT_FALSE(a != b); EXPECT_FALSE(a < b); EXPECT_TRUE(a <= b); EXPECT_FALSE(a > b); EXPECT_TRUE(a >= b); })
+#define LT(a, b) ({ EXPECT_FALSE(a == b); EXPECT_TRUE(a != b); EXPECT_TRUE(a < b); EXPECT_TRUE(a <= b); EXPECT_FALSE(a > b); EXPECT_FALSE(a >= b); })
+#define GT(a, b) ({ EXPECT_FALSE(a == b); EXPECT_TRUE(a != b); EXPECT_FALSE(a < b); EXPECT_FALSE(a <= b); EXPECT_TRUE(a > b); EXPECT_TRUE(a >= b); })
+
+TEST(Option, cmp)
+{
+ using option::Option;
+ using option::Some;
+ using option::None;
+
+ Option<int> none = None;
+
+ EQ(none, none);
+ EQ(none, None);
+ LT(none, Some(-1));
+ LT(none, Some(0));
+ LT(none, Some(1));
+ EQ((None), none);
+ // EQ((None), None); // actually a function template
+ LT((None), Some(-1));
+ LT((None), Some(0));
+ LT((None), Some(1));
+ GT(Some(-1), none);
+ GT(Some(-1), None);
+ EQ(Some(-1), Some(-1));
+ LT(Some(-1), Some(0));
+ LT(Some(-1), Some(1));
+ GT(Some(0), none);
+ GT(Some(0), None);
+ GT(Some(0), Some(-1));
+ EQ(Some(0), Some(0));
+ LT(Some(0), Some(1));
+ GT(Some(1), none);
+ GT(Some(1), None);
+ GT(Some(1), Some(-1));
+ GT(Some(1), Some(0));
+ EQ(Some(1), Some(1));
+}
+
+} // namespace tmwa