luau/tests/Variant.test.cpp
Andy Friesen c33700e473
Sync to upstream/release/552 (#735)
* Reduce the stack utilization of type checking.
* Improve the error message that's reported when a delimiting comma is
missing from a table literal. eg
```lua
local t = {
    first = 1
    second = 2
}```
2022-11-04 10:33:22 -07:00

252 lines
5.5 KiB
C++

// 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 <string>
#include <ostream>
#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<int, Foo> v1;
Variant<Foo, int> v2;
REQUIRE(get_if<int>(&v1));
CHECK(*get_if<int>(&v1) == 0);
CHECK(!get_if<Foo>(&v1));
REQUIRE(get_if<Foo>(&v2));
CHECK(get_if<Foo>(&v2)->x == 42);
}
TEST_CASE("Create")
{
Variant<int, Foo> v1 = 1;
Variant<Foo, int> v2 = Foo{2};
Foo f = {3};
Variant<Foo, int> v3 = f;
REQUIRE(get_if<int>(&v1));
CHECK(*get_if<int>(&v1) == 1);
REQUIRE(get_if<Foo>(&v2));
CHECK(get_if<Foo>(&v2)->x == 2);
REQUIRE(get_if<Foo>(&v3));
CHECK(get_if<Foo>(&v3)->x == 3);
}
TEST_CASE("Emplace")
{
{
Variant<int, Bar> v1;
CHECK(0 == Bar::count);
int& i = v1.emplace<int>(5);
CHECK(5 == i);
CHECK(0 == Bar::count);
CHECK(get_if<int>(&v1) == &i);
Bar& bar = v1.emplace<Bar>(11);
CHECK(22 == bar.prop);
CHECK(1 == Bar::count);
CHECK(get_if<Bar>(&v1) == &bar);
}
CHECK(0 == Bar::count);
}
TEST_CASE("NonPOD")
{
// initialize (copy)
std::string s1 = "hello";
Variant<std::string, int> v1 = s1;
CHECK(*get_if<std::string>(&v1) == "hello");
// initialize (move)
Variant<std::string, int> v2 = std::string("hello");
CHECK(*get_if<std::string>(&v2) == "hello");
// move-assign
v2 = std::string("this is a long string that doesn't fit into the small buffer");
CHECK(*get_if<std::string>(&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<std::string>(&v2) == "this is another long string, and this time we're copying it");
// copy ctor
Variant<std::string, int> v3 = v2;
CHECK(*get_if<std::string>(&v2) == "this is another long string, and this time we're copying it");
CHECK(*get_if<std::string>(&v3) == "this is another long string, and this time we're copying it");
// move ctor
Variant<std::string, int> v4 = std::move(v3);
CHECK(*get_if<std::string>(&v2) == "this is another long string, and this time we're copying it");
CHECK(*get_if<std::string>(&v3) == ""); // moved-from variant has an empty string now
CHECK(*get_if<std::string>(&v4) == "this is another long string, and this time we're copying it");
}
TEST_CASE("Equality")
{
Variant<int, std::string> v1 = std::string("hi");
Variant<int, std::string> v2 = std::string("me");
Variant<int, std::string> v3 = 1;
Variant<int, std::string> v4 = 0;
Variant<int, std::string> 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<std::string, int> v1 = std::string("123");
Variant<std::string, int> v2 = 45;
const Variant<std::string, int>& v1c = v1;
const Variant<std::string, int>& 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<MoveOnly> v1 = MoveOnly{};
Variant<MoveOnly> v2 = std::move(v1);
}
TEST_CASE("MoveWithCopyableAlternative")
{
Variant<std::string, MoveOnly> v1 = std::string{"Hello, world! I am longer than a normal hello world string to avoid SSO."};
Variant<std::string, MoveOnly> v2 = std::move(v1);
std::string* s1 = get_if<std::string>(&v1);
REQUIRE(s1);
CHECK(*s1 == "");
std::string* s2 = get_if<std::string>(&v2);
REQUIRE(s2);
CHECK(*s2 == "Hello, world! I am longer than a normal hello world string to avoid SSO.");
}
TEST_SUITE_END();