blob: 4d7b02590d8af7515e0bab78efd74a47a96514f8 [file] [log] [blame]
// Copyright 2017 The Abseil Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "absl/types/variant.h"
#include <memory>
#include <string>
#include <variant>
#include "gmock/gmock.h"
#include "gtest/gtest.h"
namespace absl {
namespace {
using ::testing::DoubleEq;
using ::testing::Pointee;
using ::testing::VariantWith;
struct Convertible2;
struct Convertible1 {
Convertible1() {}
Convertible1(const Convertible1&) {}
Convertible1& operator=(const Convertible1&) { return *this; }
// implicit conversion from Convertible2
Convertible1(const Convertible2&) {} // NOLINT(runtime/explicit)
};
struct Convertible2 {
Convertible2() {}
Convertible2(const Convertible2&) {}
Convertible2& operator=(const Convertible2&) { return *this; }
// implicit conversion from Convertible1
Convertible2(const Convertible1&) {} // NOLINT(runtime/explicit)
};
TEST(VariantTest, TestRvalueConversion) {
std::variant<Convertible1, Convertible2> v(
ConvertVariantTo<std::variant<Convertible1, Convertible2>>(
(std::variant<Convertible2, Convertible1>(Convertible1()))));
ASSERT_TRUE(absl::holds_alternative<Convertible1>(v));
v = ConvertVariantTo<std::variant<Convertible1, Convertible2>>(
std::variant<Convertible2, Convertible1>(Convertible2()));
ASSERT_TRUE(absl::holds_alternative<Convertible2>(v));
}
TEST(VariantTest, TestLvalueConversion) {
std::variant<Convertible2, Convertible1> source((Convertible1()));
std::variant<Convertible1, Convertible2> v(
ConvertVariantTo<std::variant<Convertible1, Convertible2>>(source));
ASSERT_TRUE(absl::holds_alternative<Convertible1>(v));
source = Convertible2();
v = ConvertVariantTo<std::variant<Convertible1, Convertible2>>(source);
ASSERT_TRUE(absl::holds_alternative<Convertible2>(v));
}
TEST(VariantTest, TestMoveConversion) {
using Variant = std::variant<std::unique_ptr<const int>,
std::unique_ptr<const std::string>>;
using OtherVariant =
std::variant<std::unique_ptr<int>, std::unique_ptr<std::string>>;
Variant var(
ConvertVariantTo<Variant>(OtherVariant{std::make_unique<int>(0)}));
ASSERT_TRUE(absl::holds_alternative<std::unique_ptr<const int>>(var));
ASSERT_NE(absl::get<std::unique_ptr<const int>>(var), nullptr);
EXPECT_EQ(0, *absl::get<std::unique_ptr<const int>>(var));
var = ConvertVariantTo<Variant>(
OtherVariant(std::make_unique<std::string>("foo")));
ASSERT_TRUE(absl::holds_alternative<std::unique_ptr<const std::string>>(var));
EXPECT_EQ("foo", *absl::get<std::unique_ptr<const std::string>>(var));
}
TEST(VariantTest, DoesNotMoveFromLvalues) {
// We use shared_ptr here because it's both copyable and movable, and
// a moved-from shared_ptr is guaranteed to be null, so we can detect
// whether moving or copying has occurred.
using Variant = std::variant<std::shared_ptr<const int>,
std::shared_ptr<const std::string>>;
using OtherVariant =
std::variant<std::shared_ptr<int>, std::shared_ptr<std::string>>;
Variant v1(std::make_shared<const int>(0));
// Test copy constructor
Variant v2(v1);
EXPECT_EQ(absl::get<std::shared_ptr<const int>>(v1),
absl::get<std::shared_ptr<const int>>(v2));
// Test copy-assignment operator
v1 = std::make_shared<const std::string>("foo");
v2 = v1;
EXPECT_EQ(absl::get<std::shared_ptr<const std::string>>(v1),
absl::get<std::shared_ptr<const std::string>>(v2));
// Test converting copy constructor
OtherVariant other(std::make_shared<int>(0));
Variant v3(ConvertVariantTo<Variant>(other));
EXPECT_EQ(absl::get<std::shared_ptr<int>>(other),
absl::get<std::shared_ptr<const int>>(v3));
other = std::make_shared<std::string>("foo");
v3 = ConvertVariantTo<Variant>(other);
EXPECT_EQ(absl::get<std::shared_ptr<std::string>>(other),
absl::get<std::shared_ptr<const std::string>>(v3));
}
TEST(VariantTest, TestRvalueConversionViaConvertVariantTo) {
variant<Convertible1, Convertible2> v(
ConvertVariantTo<std::variant<Convertible1, Convertible2>>(
(std::variant<Convertible2, Convertible1>(Convertible1()))));
ASSERT_TRUE(absl::holds_alternative<Convertible1>(v));
v = ConvertVariantTo<std::variant<Convertible1, Convertible2>>(
std::variant<Convertible2, Convertible1>(Convertible2()));
ASSERT_TRUE(absl::holds_alternative<Convertible2>(v));
}
TEST(VariantTest, TestLvalueConversionViaConvertVariantTo) {
variant<Convertible2, Convertible1> source((Convertible1()));
variant<Convertible1, Convertible2> v(
ConvertVariantTo<std::variant<Convertible1, Convertible2>>(source));
ASSERT_TRUE(absl::holds_alternative<Convertible1>(v));
source = Convertible2();
v = ConvertVariantTo<std::variant<Convertible1, Convertible2>>(source);
ASSERT_TRUE(absl::holds_alternative<Convertible2>(v));
}
TEST(VariantTest, TestMoveConversionViaConvertVariantTo) {
using Variant = std::variant<std::unique_ptr<const int>,
std::unique_ptr<const std::string>>;
using OtherVariant =
std::variant<std::unique_ptr<int>, std::unique_ptr<std::string>>;
Variant var(
ConvertVariantTo<Variant>(OtherVariant{std::make_unique<int>(3)}));
EXPECT_THAT(absl::get_if<std::unique_ptr<const int>>(&var),
Pointee(Pointee(3)));
var = ConvertVariantTo<Variant>(
OtherVariant(std::make_unique<std::string>("foo")));
EXPECT_THAT(absl::get_if<std::unique_ptr<const std::string>>(&var),
Pointee(Pointee(std::string("foo"))));
}
} // namespace
} // namespace absl