blob: 786278bdff6549eabba867cc04ef808975825683 [file] [log] [blame] [edit]
/*
* Copyright (C) 2024 Samuel Weinig <sam@webkit.org>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "config.h"
#include "LifecycleLogger.h"
#include "MoveOnly.h"
#include "RefLogger.h"
#include <wtf/CompactVariant.h>
#include <wtf/GetPtr.h>
struct EmptyStruct {
constexpr bool operator==(const EmptyStruct&) const = default;
};
struct SmallEnoughStruct {
float value { 0 };
constexpr bool operator==(const SmallEnoughStruct&) const = default;
constexpr bool operator==(float other) const { return value == other; };
};
struct TooBigStruct {
double value { 0 };
constexpr bool operator==(const TooBigStruct&) const = default;
constexpr bool operator==(double other) const { return value == other; };
};
// Treat LifecycleLogger as smart pointer to allow its use in CompactVariant.
template<> struct WTF::IsSmartPtr<TestWebKitAPI::LifecycleLogger> {
static constexpr bool value = true;
};
template<> struct WTF::CompactVariantTraits<TooBigStruct> {
static constexpr bool hasAlternativeRepresentation = true;
static constexpr uint64_t encodeFromArguments(double value)
{
return static_cast<uint64_t>(std::bit_cast<uint32_t>(static_cast<float>(value)));
}
static constexpr uint64_t encode(const TooBigStruct& value)
{
return static_cast<uint64_t>(std::bit_cast<uint32_t>(static_cast<float>(value.value)));
}
static constexpr uint64_t encode(TooBigStruct&& value)
{
return static_cast<uint64_t>(std::bit_cast<uint32_t>(static_cast<float>(value.value)));
}
static constexpr TooBigStruct decode(uint64_t value)
{
return { std::bit_cast<float>(static_cast<uint32_t>(value)) };
}
};
namespace TestWebKitAPI {
TEST(WTF_CompactVariant, Pointers)
{
int testInt = 1;
float testFloat = 2.0f;
CompactVariant<int*, float*> variant = &testInt;
EXPECT_TRUE(WTF::holdsAlternative<int*>(variant));
EXPECT_FALSE(WTF::holdsAlternative<float*>(variant));
WTF::switchOn(variant,
[&](int* const& value) { EXPECT_EQ(*value, 1); },
[&](float* const& value) { FAIL(); }
);
variant = &testFloat;
EXPECT_FALSE(WTF::holdsAlternative<int*>(variant));
EXPECT_TRUE(WTF::holdsAlternative<float*>(variant));
WTF::switchOn(variant,
[&](int* const& value) { FAIL(); },
[&](float* const& value) { EXPECT_EQ(*value, 2.0f); }
);
}
#if CPU(ADDRESS64)
TEST(WTF_CompactVariant, Pointers64)
{
int testInt = 1;
void* testPtr = reinterpret_cast<void*>(0x2600'1234'5678'9abcULL);
CompactVariant<int*, void*> variant = &testInt;
EXPECT_TRUE(WTF::holdsAlternative<int*>(variant));
EXPECT_FALSE(WTF::holdsAlternative<void*>(variant));
WTF::switchOn(variant,
[&](int* const& value) { EXPECT_EQ(*value, 1); },
[&](void* const& value) { FAIL(); }
);
variant = testPtr;
EXPECT_FALSE(WTF::holdsAlternative<int*>(variant));
EXPECT_TRUE(WTF::holdsAlternative<void*>(variant));
WTF::switchOn(variant,
[&](int* const& value) { FAIL(); },
[&](void* const& value) { EXPECT_EQ(reinterpret_cast<uintptr_t>(value), 0x2600'1234'5678'9abcULL); }
);
}
#endif
TEST(WTF_CompactVariant, SmartPointers)
{
{
RefLogger testRefLogger("testRefLogger");
Ref<RefLogger> ref(testRefLogger);
CompactVariant<Ref<RefLogger>, std::unique_ptr<double>> variant { WTF::InPlaceType<Ref<RefLogger>>, WTF::move(ref) };
EXPECT_TRUE(WTF::holdsAlternative<Ref<RefLogger>>(variant));
EXPECT_FALSE(WTF::holdsAlternative<std::unique_ptr<double>>(variant));
WTF::switchOn(variant,
[&](const Ref<RefLogger>&) { SUCCEED(); },
[&](const std::unique_ptr<double>&) { FAIL(); }
);
variant = std::make_unique<double>(2.0);
EXPECT_FALSE(WTF::holdsAlternative<Ref<RefLogger>>(variant));
EXPECT_TRUE(WTF::holdsAlternative<std::unique_ptr<double>>(variant));
WTF::switchOn(variant,
[&](const Ref<RefLogger>&) { FAIL(); },
[&](const std::unique_ptr<double>& value) { EXPECT_EQ(*value, 2.0); }
);
}
ASSERT_STREQ("ref(testRefLogger) deref(testRefLogger) ", takeLogStr().c_str());
}
TEST(WTF_CompactVariant, SmallScalars)
{
float testFloat = 2.0f;
CompactVariant<int*, float*, float> variant = 3.0f;
EXPECT_FALSE(WTF::holdsAlternative<int*>(variant));
EXPECT_FALSE(WTF::holdsAlternative<float*>(variant));
EXPECT_TRUE(WTF::holdsAlternative<float>(variant));
WTF::switchOn(variant,
[&](int* const& value) { FAIL(); },
[&](float* const& value) { FAIL(); },
[&](const float& value) { EXPECT_EQ(value, 3.0f); }
);
variant = &testFloat;
EXPECT_FALSE(WTF::holdsAlternative<int*>(variant));
EXPECT_TRUE(WTF::holdsAlternative<float*>(variant));
EXPECT_FALSE(WTF::holdsAlternative<float>(variant));
WTF::switchOn(variant,
[&](int* const& value) { FAIL(); },
[&](float* const& value) { EXPECT_EQ(*value, 2.0f); },
[&](const float& value) { FAIL(); }
);
}
TEST(WTF_CompactVariant, EmptyStruct)
{
float testFloat = 2.0f;
CompactVariant<int*, float*, EmptyStruct> variant = EmptyStruct { };
EXPECT_FALSE(WTF::holdsAlternative<int*>(variant));
EXPECT_FALSE(WTF::holdsAlternative<float*>(variant));
EXPECT_TRUE(WTF::holdsAlternative<EmptyStruct>(variant));
WTF::switchOn(variant,
[&](int* const& value) { FAIL(); },
[&](float* const& value) { FAIL(); },
[&](const EmptyStruct&) { SUCCEED(); }
);
variant = &testFloat;
EXPECT_FALSE(WTF::holdsAlternative<int*>(variant));
EXPECT_TRUE(WTF::holdsAlternative<float*>(variant));
EXPECT_FALSE(WTF::holdsAlternative<EmptyStruct>(variant));
WTF::switchOn(variant,
[&](int* const& value) { FAIL(); },
[&](float* const& value) { EXPECT_EQ(*value, 2.0f); },
[&](const EmptyStruct&) { FAIL(); }
);
}
TEST(WTF_CompactVariant, SmallEnoughStruct)
{
float testFloat = 2.0f;
CompactVariant<int*, float*, SmallEnoughStruct> variant = SmallEnoughStruct { 3.0f };
EXPECT_FALSE(WTF::holdsAlternative<int*>(variant));
EXPECT_FALSE(WTF::holdsAlternative<float*>(variant));
EXPECT_TRUE(WTF::holdsAlternative<SmallEnoughStruct>(variant));
WTF::switchOn(variant,
[&](int* const& value) { FAIL(); },
[&](float* const& value) { FAIL(); },
[&](const SmallEnoughStruct& value) { EXPECT_EQ(value.value, 3.0f); }
);
variant = &testFloat;
EXPECT_FALSE(WTF::holdsAlternative<int*>(variant));
EXPECT_TRUE(WTF::holdsAlternative<float*>(variant));
EXPECT_FALSE(WTF::holdsAlternative<SmallEnoughStruct>(variant));
WTF::switchOn(variant,
[&](int* const& value) { FAIL(); },
[&](float* const& value) { EXPECT_EQ(*value, 2.0f); },
[&](const SmallEnoughStruct&) { FAIL(); }
);
}
TEST(WTF_CompactVariant, TooBigStruct)
{
float testFloat = 2.0f;
CompactVariant<int*, float*, TooBigStruct> variant = TooBigStruct { 4.0 };
EXPECT_FALSE(WTF::holdsAlternative<int*>(variant));
EXPECT_FALSE(WTF::holdsAlternative<float*>(variant));
EXPECT_TRUE(WTF::holdsAlternative<TooBigStruct>(variant));
WTF::switchOn(variant,
[&](int* const& value) { FAIL(); },
[&](float* const& value) { FAIL(); },
[&](const TooBigStruct value) { EXPECT_EQ(value.value, 4.0); }
);
variant = &testFloat;
EXPECT_FALSE(WTF::holdsAlternative<int*>(variant));
EXPECT_TRUE(WTF::holdsAlternative<float*>(variant));
EXPECT_FALSE(WTF::holdsAlternative<TooBigStruct>(variant));
WTF::switchOn(variant,
[&](int* const& value) { FAIL(); },
[&](float* const& value) { EXPECT_EQ(*value, 2.0f); },
[&](const TooBigStruct) { FAIL(); }
);
variant = TooBigStruct { 5.0 };
CompactVariant<int*, float*, TooBigStruct> movedToVariant = WTF::move(variant);
EXPECT_TRUE(variant.valueless_by_move());
EXPECT_FALSE(WTF::holdsAlternative<int*>(movedToVariant));
EXPECT_FALSE(WTF::holdsAlternative<float*>(movedToVariant));
EXPECT_TRUE(WTF::holdsAlternative<TooBigStruct>(movedToVariant));
WTF::switchOn(movedToVariant,
[&](int* const& value) { FAIL(); },
[&](float* const& value) { FAIL(); },
[&](const TooBigStruct value) { EXPECT_EQ(value.value, 5.0); }
);
variant = movedToVariant;
EXPECT_FALSE(WTF::holdsAlternative<int*>(movedToVariant));
EXPECT_FALSE(WTF::holdsAlternative<float*>(movedToVariant));
EXPECT_TRUE(WTF::holdsAlternative<TooBigStruct>(movedToVariant));
EXPECT_FALSE(WTF::holdsAlternative<int*>(variant));
EXPECT_FALSE(WTF::holdsAlternative<float*>(variant));
EXPECT_TRUE(WTF::holdsAlternative<TooBigStruct>(variant));
WTF::switchOn(movedToVariant,
[&](int* const& value) { FAIL(); },
[&](float* const& value) { FAIL(); },
[&](const TooBigStruct value) { EXPECT_EQ(value.value, 5.0); }
);
WTF::switchOn(variant,
[&](int* const& value) { FAIL(); },
[&](float* const& value) { FAIL(); },
[&](const TooBigStruct value) { EXPECT_EQ(value.value, 5.0); }
);
}
TEST(WTF_CompactVariant, MoveOnlyStruct)
{
float testFloat = 2.0f;
CompactVariant<int*, float*, MoveOnly> variant = MoveOnly { 5u };
EXPECT_FALSE(WTF::holdsAlternative<int*>(variant));
EXPECT_FALSE(WTF::holdsAlternative<float*>(variant));
EXPECT_TRUE(WTF::holdsAlternative<MoveOnly>(variant));
WTF::switchOn(variant,
[&](int* const& value) { FAIL(); },
[&](float* const& value) { FAIL(); },
[&](const MoveOnly& value) { EXPECT_EQ(value.value(), 5u); }
);
variant = &testFloat;
EXPECT_FALSE(WTF::holdsAlternative<int*>(variant));
EXPECT_TRUE(WTF::holdsAlternative<float*>(variant));
EXPECT_FALSE(WTF::holdsAlternative<MoveOnly>(variant));
WTF::switchOn(variant,
[&](int* const& value) { FAIL(); },
[&](float* const& value) { EXPECT_EQ(*value, 2.0f); },
[&](const MoveOnly&) { FAIL(); }
);
variant = MoveOnly { 6u };
EXPECT_FALSE(WTF::holdsAlternative<int*>(variant));
EXPECT_FALSE(WTF::holdsAlternative<float*>(variant));
EXPECT_TRUE(WTF::holdsAlternative<MoveOnly>(variant));
WTF::switchOn(variant,
[&](int* const& value) { FAIL(); },
[&](float* const& value) { FAIL(); },
[&](const MoveOnly& value) { EXPECT_EQ(value.value(), 6u); }
);
}
TEST(WTF_CompactVariant, ValuelessByMove)
{
int testInt = 1;
CompactVariant<int*, float*> variant = &testInt;
EXPECT_FALSE(variant.valueless_by_move());
CompactVariant<int*, float*> other = WTF::move(variant);
EXPECT_FALSE(other.valueless_by_move());
EXPECT_TRUE(variant.valueless_by_move());
// Test copying the "valueless_by_move" variant.
CompactVariant<int*, float*> copy = variant;
EXPECT_TRUE(variant.valueless_by_move());
EXPECT_TRUE(copy.valueless_by_move());
// Test re-moving the "valueless_by_move" variant.
CompactVariant<int*, float*> moved = WTF::move(variant);
EXPECT_TRUE(variant.valueless_by_move());
EXPECT_TRUE(moved.valueless_by_move());
}
TEST(WTF_CompactVariant, ArgumentAssignment)
{
{
CompactVariant<float, LifecycleLogger> variant = LifecycleLogger("compact");
ASSERT_STREQ("construct(compact) move-construct(compact) destruct(<default>) ", takeLogStr().c_str());
}
ASSERT_STREQ("destruct(compact) ", takeLogStr().c_str());
}
TEST(WTF_CompactVariant, ArgumentConstruct)
{
{
CompactVariant<float, LifecycleLogger> variant { LifecycleLogger("compact") };
ASSERT_STREQ("construct(compact) move-construct(compact) destruct(<default>) ", takeLogStr().c_str());
}
ASSERT_STREQ("destruct(compact) ", takeLogStr().c_str());
}
TEST(WTF_CompactVariant, ArgumentConstructInPlaceType)
{
{
CompactVariant<float, LifecycleLogger> variant { WTF::InPlaceType<LifecycleLogger>, "compact" };
ASSERT_STREQ("construct(compact) ", takeLogStr().c_str());
}
ASSERT_STREQ("destruct(compact) ", takeLogStr().c_str());
}
TEST(WTF_CompactVariant, ArgumentConstructInPlaceIndex)
{
{
CompactVariant<float, LifecycleLogger> variant { WTF::InPlaceIndex<1>, "compact" };
ASSERT_STREQ("construct(compact) ", takeLogStr().c_str());
}
ASSERT_STREQ("destruct(compact) ", takeLogStr().c_str());
}
TEST(WTF_CompactVariant, ArgumentMoveConstruct)
{
{
LifecycleLogger lifecycleLogger("compact");
CompactVariant<float, LifecycleLogger> variant { WTF::move(lifecycleLogger) };
ASSERT_STREQ("construct(compact) move-construct(compact) ", takeLogStr().c_str());
}
ASSERT_STREQ("destruct(compact) destruct(<default>) ", takeLogStr().c_str());
}
TEST(WTF_CompactVariant, ArgumentCopyConstruct)
{
{
LifecycleLogger lifecycleLogger("compact");
CompactVariant<float, LifecycleLogger> variant { lifecycleLogger };
ASSERT_STREQ("construct(compact) copy-construct(compact) ", takeLogStr().c_str());
}
ASSERT_STREQ("destruct(compact) destruct(compact) ", takeLogStr().c_str());
}
TEST(WTF_CompactVariant, ArgumentMoveAssignment)
{
{
LifecycleLogger lifecycleLogger("compact");
CompactVariant<float, LifecycleLogger> variant = WTF::move(lifecycleLogger);
ASSERT_STREQ("construct(compact) move-construct(compact) ", takeLogStr().c_str());
}
ASSERT_STREQ("destruct(compact) destruct(<default>) ", takeLogStr().c_str());
}
TEST(WTF_CompactVariant, ArgumentCopyAssignment)
{
{
LifecycleLogger lifecycleLogger("compact");
CompactVariant<float, LifecycleLogger> variant = lifecycleLogger;
ASSERT_STREQ("construct(compact) copy-construct(compact) ", takeLogStr().c_str());
}
ASSERT_STREQ("destruct(compact) destruct(compact) ", takeLogStr().c_str());
}
TEST(WTF_CompactVariant, CopyConstruct)
{
{
CompactVariant<float, LifecycleLogger> variant { WTF::InPlaceType<LifecycleLogger>, "compact" };
CompactVariant<float, LifecycleLogger> other { variant };
ASSERT_STREQ("construct(compact) copy-construct(compact) ", takeLogStr().c_str());
}
ASSERT_STREQ("destruct(compact) destruct(compact) ", takeLogStr().c_str());
}
TEST(WTF_CompactVariant, CopyAssignment)
{
{
CompactVariant<float, LifecycleLogger> variant { WTF::InPlaceType<LifecycleLogger>, "compact" };
CompactVariant<float, LifecycleLogger> other = variant;
ASSERT_STREQ("construct(compact) copy-construct(compact) ", takeLogStr().c_str());
}
ASSERT_STREQ("destruct(compact) destruct(compact) ", takeLogStr().c_str());
}
TEST(WTF_CompactVariant, MoveConstruct)
{
{
CompactVariant<float, LifecycleLogger> variant { WTF::InPlaceType<LifecycleLogger>, "compact" };
CompactVariant<float, LifecycleLogger> other { WTF::move(variant) };
ASSERT_STREQ("construct(compact) move-construct(compact) ", takeLogStr().c_str());
}
ASSERT_STREQ("destruct(compact) ", takeLogStr().c_str());
}
TEST(WTF_CompactVariant, MoveAssignment)
{
{
CompactVariant<float, LifecycleLogger> variant { WTF::InPlaceType<LifecycleLogger>, "compact" };
CompactVariant<float, LifecycleLogger> other = WTF::move(variant);
ASSERT_STREQ("construct(compact) move-construct(compact) ", takeLogStr().c_str());
}
ASSERT_STREQ("destruct(compact) ", takeLogStr().c_str());
}
TEST(WTF_CompactVariant, ConstructThenReassign)
{
{
CompactVariant<float, LifecycleLogger> variant { WTF::InPlaceType<LifecycleLogger>, "compact" };
variant = 1.0f;
ASSERT_STREQ("construct(compact) destruct(compact) ", takeLogStr().c_str());
}
ASSERT_STREQ("", takeLogStr().c_str());
}
TEST(WTF_CompactVariant, ArgumentReassignment)
{
{
CompactVariant<float, LifecycleLogger> variant { 1.0f };
variant = LifecycleLogger { "compact" };
ASSERT_STREQ("construct(compact) move-construct(compact) destruct(<default>) ", takeLogStr().c_str());
}
ASSERT_STREQ("destruct(compact) ", takeLogStr().c_str());
}
TEST(WTF_CompactVariant, ArgumentCopyReassignment)
{
{
CompactVariant<float, LifecycleLogger> variant { 1.0f };
LifecycleLogger lifecycleLogger { "compact" };
variant = lifecycleLogger;
ASSERT_STREQ("construct(compact) copy-construct(compact) ", takeLogStr().c_str());
}
ASSERT_STREQ("destruct(compact) destruct(compact) ", takeLogStr().c_str());
}
TEST(WTF_CompactVariant, ArgumentMoveReassignment)
{
{
CompactVariant<float, LifecycleLogger> variant { 1.0f };
LifecycleLogger lifecycleLogger { "compact" };
variant = WTF::move(lifecycleLogger);
ASSERT_STREQ("construct(compact) move-construct(compact) ", takeLogStr().c_str());
}
ASSERT_STREQ("destruct(<default>) destruct(compact) ", takeLogStr().c_str());
}
TEST(WTF_CompactVariant, EmplaceType)
{
{
CompactVariant<float, LifecycleLogger> variant { 1.0f };
variant.emplace<LifecycleLogger>("compact");
ASSERT_STREQ("construct(compact) ", takeLogStr().c_str());
}
ASSERT_STREQ("destruct(compact) ", takeLogStr().c_str());
}
TEST(WTF_CompactVariant, EmplaceIndex)
{
{
CompactVariant<float, LifecycleLogger> variant { 1.0f };
variant.emplace<1>("compact");
ASSERT_STREQ("construct(compact) ", takeLogStr().c_str());
}
ASSERT_STREQ("destruct(compact) ", takeLogStr().c_str());
}
TEST(WTF_CompactVariant, SwitchOn)
{
// `switchOn` should not cause any lifecycle events.
{
CompactVariant<float, LifecycleLogger> variant { WTF::InPlaceType<LifecycleLogger>, "compact" };
WTF::switchOn(variant,
[&](const float&) { },
[&](const LifecycleLogger&) { }
);
ASSERT_STREQ("construct(compact) ", takeLogStr().c_str());
}
ASSERT_STREQ("destruct(compact) ", takeLogStr().c_str());
}
} // namespace TestWebKitAPI