// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details #include "Luau/Variant.h" #include #include #include "doctest.h" using namespace Luau; struct Foo { int x = 42; }; struct Bar { explicit Bar(int x) : prop(x * 2) { ++count; } ~Bar() { --count; } int prop; static int count; }; int Bar::count = 0; TEST_SUITE_BEGIN("Variant"); TEST_CASE("DefaultCtor") { Variant v1; Variant v2; REQUIRE(get_if(&v1)); CHECK(*get_if(&v1) == 0); CHECK(!get_if(&v1)); REQUIRE(get_if(&v2)); CHECK(get_if(&v2)->x == 42); } TEST_CASE("Create") { Variant v1 = 1; Variant v2 = Foo{2}; Foo f = {3}; Variant v3 = f; REQUIRE(get_if(&v1)); CHECK(*get_if(&v1) == 1); REQUIRE(get_if(&v2)); CHECK(get_if(&v2)->x == 2); REQUIRE(get_if(&v3)); CHECK(get_if(&v3)->x == 3); } TEST_CASE("Emplace") { { Variant v1; CHECK(0 == Bar::count); int& i = v1.emplace(5); CHECK(5 == i); CHECK(0 == Bar::count); CHECK(get_if(&v1) == &i); Bar& bar = v1.emplace(11); CHECK(22 == bar.prop); CHECK(1 == Bar::count); CHECK(get_if(&v1) == &bar); } CHECK(0 == Bar::count); } TEST_CASE("NonPOD") { // initialize (copy) std::string s1 = "hello"; Variant v1 = s1; CHECK(*get_if(&v1) == "hello"); // initialize (move) Variant v2 = std::string("hello"); CHECK(*get_if(&v2) == "hello"); // move-assign v2 = std::string("this is a long string that doesn't fit into the small buffer"); CHECK(*get_if(&v2) == "this is a long string that doesn't fit into the small buffer"); // copy-assign std::string s2("this is another long string, and this time we're copying it"); v2 = s2; CHECK(*get_if(&v2) == "this is another long string, and this time we're copying it"); // copy ctor Variant v3 = v2; CHECK(*get_if(&v2) == "this is another long string, and this time we're copying it"); CHECK(*get_if(&v3) == "this is another long string, and this time we're copying it"); // move ctor Variant v4 = std::move(v3); CHECK(*get_if(&v2) == "this is another long string, and this time we're copying it"); CHECK(*get_if(&v3) == ""); // moved-from variant has an empty string now CHECK(*get_if(&v4) == "this is another long string, and this time we're copying it"); } TEST_CASE("Equality") { Variant v1 = std::string("hi"); Variant v2 = std::string("me"); Variant v3 = 1; Variant v4 = 0; Variant v5; CHECK(v1 == v1); CHECK(v1 != v2); CHECK(v1 != v3); CHECK(v3 != v4); CHECK(v4 == v5); } struct ToStringVisitor { std::string operator()(const std::string& v) { return v; } std::string operator()(int v) { return std::to_string(v); } }; struct IncrementVisitor { void operator()(std::string& v) { v += "1"; } void operator()(int& v) { v += 1; } }; TEST_CASE("Visit") { Variant v1 = std::string("123"); Variant v2 = 45; const Variant& v1c = v1; const Variant& v2c = v2; // void-returning visitor, const variants std::string r1; visit( [&](const auto& v) { r1 += ToStringVisitor()(v); }, v1c ); visit( [&](const auto& v) { r1 += ToStringVisitor()(v); }, v2c ); CHECK(r1 == "12345"); // value-returning visitor, const variants std::string r2; r2 += visit(ToStringVisitor(), v1c); r2 += visit(ToStringVisitor(), v2c); CHECK(r2 == "12345"); // void-returning visitor, mutable variant visit(IncrementVisitor(), v1); visit(IncrementVisitor(), v2); CHECK(visit(ToStringVisitor(), v1) == "1231"); CHECK(visit(ToStringVisitor(), v2) == "46"); // value-returning visitor, mutable variant std::string r3; r3 += visit( [&](auto& v) { IncrementVisitor()(v); return ToStringVisitor()(v); }, v1 ); r3 += visit( [&](auto& v) { IncrementVisitor()(v); return ToStringVisitor()(v); }, v2 ); CHECK(r3 == "1231147"); } struct MoveOnly { MoveOnly() = default; MoveOnly(const MoveOnly&) = delete; MoveOnly& operator=(const MoveOnly&) = delete; MoveOnly(MoveOnly&&) = default; MoveOnly& operator=(MoveOnly&&) = default; }; TEST_CASE("Move") { Variant v1 = MoveOnly{}; Variant v2 = std::move(v1); } TEST_CASE("MoveWithCopyableAlternative") { Variant v1 = std::string{"Hello, world! I am longer than a normal hello world string to avoid SSO."}; Variant v2 = std::move(v1); std::string* s1 = get_if(&v1); REQUIRE(s1); CHECK(*s1 == ""); std::string* s2 = get_if(&v2); REQUIRE(s2); CHECK(*s2 == "Hello, world! I am longer than a normal hello world string to avoid SSO."); } TEST_SUITE_END();