| // 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 |