blob: 4f7d6cbc350ed0c346b4b3133cbad2d46adda768 [file] [log] [blame]
// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "third_party/blink/public/common/tokens/multi_token.h"
#include <algorithm>
#include <variant>
#include "base/types/token_type.h"
#include "base/unguessable_token.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace blink {
using FooToken = base::TokenType<class FooTokenTag>;
using BarToken = base::TokenType<class BarTokenTag>;
using BazToken = base::TokenType<class BazTokenTag>;
static_assert(internal::IsBaseToken<FooToken>);
static_assert(!internal::IsBaseToken<int>);
static_assert(internal::AreAllUnique<int>);
static_assert(!internal::AreAllUnique<int, int>);
static_assert(!internal::AreAllUnique<int, char, int>);
static_assert(internal::IsCompatible<BazToken, FooToken, BarToken, BazToken>);
static_assert(!internal::IsCompatible<BazToken, FooToken, BarToken>);
using FooBarToken = MultiToken<FooToken, BarToken>;
using FooBarBazToken = MultiToken<FooToken, BarToken, BazToken>;
static_assert(FooBarBazToken::IndexOf<FooToken>() == FooBarBazToken::Tag{0});
static_assert(FooBarBazToken::IndexOf<BarToken>() == FooBarBazToken::Tag{1});
static_assert(FooBarBazToken::IndexOf<BazToken>() == FooBarBazToken::Tag{2});
TEST(MultiTokenTest, MultiTokenWorks) {
// Test default initialization.
FooBarToken token1;
EXPECT_FALSE(token1.value().is_empty());
EXPECT_EQ(FooBarToken::Tag{0}, token1.variant_index());
EXPECT_TRUE(token1.Is<FooToken>());
EXPECT_FALSE(token1.Is<BarToken>());
// Test copy construction.
BarToken bar = BarToken();
FooBarToken token2(bar);
EXPECT_EQ(token2.value(), bar.value());
EXPECT_FALSE(token2.value().is_empty());
EXPECT_EQ(FooBarToken::Tag{1}, token2.variant_index());
EXPECT_FALSE(token2.Is<FooToken>());
EXPECT_TRUE(token2.Is<BarToken>());
// Test assignment.
FooBarToken token3;
token3 = token2;
EXPECT_EQ(token3.value(), token2.value());
EXPECT_FALSE(token3.value().is_empty());
EXPECT_EQ(token2.variant_index(), token3.variant_index());
EXPECT_FALSE(token3.Is<FooToken>());
EXPECT_TRUE(token3.Is<BarToken>());
// Test hasher.
EXPECT_EQ(FooBarToken::Hasher()(token2),
base::UnguessableTokenHash()(token2.value()));
// Test string representation.
EXPECT_EQ(token2.ToString(), token2.value().ToString());
// Test type conversions.
FooToken foo(token1.value());
EXPECT_EQ(foo, token1.GetAs<FooToken>());
EXPECT_EQ(token2.GetAs<BarToken>(), token3.GetAs<BarToken>());
}
TEST(MultiTokenTest, Comparison) {
// Tests comparisons between:
// - two multi tokens that hold different types and underlying values
// - two multi tokens that hold the same type and underlying value
{
FooBarToken token1 = FooToken();
FooBarToken token2 = BarToken();
FooBarToken token3 = token2;
EXPECT_FALSE(token1 == token2);
EXPECT_TRUE(token1 != token2);
EXPECT_TRUE(token2 == token3);
EXPECT_FALSE(token2 != token3);
// std::variant orders by index. If the indices are equal (e.g. the same
// type is held in both), then the comparison operator of the held type is
// used.
EXPECT_TRUE(token1 < token2);
EXPECT_TRUE(token1 < token3);
EXPECT_FALSE(token2 < token3);
EXPECT_TRUE(token1 <= token2);
EXPECT_TRUE(token1 <= token3);
EXPECT_TRUE(token2 <= token3);
EXPECT_TRUE(token3 <= token2);
EXPECT_FALSE(token1 > token2);
EXPECT_FALSE(token1 > token3);
EXPECT_FALSE(token2 > token3);
EXPECT_FALSE(token1 >= token2);
EXPECT_FALSE(token1 >= token3);
EXPECT_TRUE(token2 >= token3);
EXPECT_TRUE(token3 >= token2);
}
// Tests comparisons between two multi tokens that hold the same type but
// different underlying values.
{
// Necessary because std::minmax() returns a pair of references.
FooToken foo1;
FooToken foo2;
const auto& [lesser, greater] = std::minmax(foo1, foo2);
FooBarToken token1 = lesser;
FooBarToken token2 = greater;
EXPECT_FALSE(token1 == token2);
EXPECT_TRUE(token1 != token2);
EXPECT_TRUE(token1 < token2);
EXPECT_FALSE(token2 < token1);
EXPECT_FALSE(token1 > token2);
EXPECT_TRUE(token2 > token1);
EXPECT_TRUE(token1 <= token2);
EXPECT_FALSE(token2 <= token1);
EXPECT_FALSE(token1 >= token2);
EXPECT_TRUE(token2 >= token1);
}
}
TEST(MultiTokenTest, Visit) {
struct Visitor {
std::string_view operator()(const FooToken& token) { return "FooToken"; }
std::string_view operator()(const BarToken& token) { return "BarToken"; }
std::string_view operator()(const BazToken& token) { return "BazToken"; }
};
FooBarBazToken token(FooToken{});
EXPECT_EQ(token.Visit(Visitor()), "FooToken");
token = BarToken{};
EXPECT_EQ(token.Visit(Visitor()), "BarToken");
token = BazToken{};
EXPECT_EQ(token.Visit(Visitor()), "BazToken");
}
TEST(MultiTokenTest, CompatibleConstruction) {
{
FooBarToken foo_bar_token(FooToken{});
FooBarBazToken foo_bar_baz_token(foo_bar_token);
EXPECT_EQ(FooBarBazToken::Tag{0}, foo_bar_baz_token.variant_index());
}
{
FooBarToken foo_bar_token(BarToken{});
FooBarBazToken foo_bar_baz_token(foo_bar_token);
EXPECT_EQ(FooBarBazToken::Tag{1}, foo_bar_baz_token.variant_index());
}
}
TEST(MultiTokenTest, CompatibleAssignment) {
FooBarBazToken foo_bar_baz_token;
{
FooBarToken foo_bar_token(FooToken{});
foo_bar_baz_token = foo_bar_token;
EXPECT_EQ(FooBarBazToken::Tag{0}, foo_bar_baz_token.variant_index());
}
{
FooBarToken foo_bar_token(BarToken{});
foo_bar_baz_token = foo_bar_token;
EXPECT_EQ(FooBarBazToken::Tag{1}, foo_bar_baz_token.variant_index());
}
}
} // namespace blink