blob: 6aca4898ef8c0eeb6596f1242e0f15e3b1486ebb [file] [log] [blame]
// Copyright 2025 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/dbus/utils/variant.h"
#include <map>
#include <memory>
#include <string>
#include <tuple>
#include <utility>
#include <vector>
#include "components/dbus/utils/read_value.h"
#include "components/dbus/utils/write_value.h"
#include "dbus/message.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace dbus_utils {
namespace {
TEST(DBusVariantTest, DefaultConstruction) {
Variant v;
EXPECT_TRUE(v.signature().empty());
}
TEST(DBusVariantTest, WrapSignature) {
using TestTuple = std::tuple<int32_t, std::string>;
using TestMap = std::map<uint16_t, bool>;
using TestVector = std::vector<double>;
EXPECT_EQ(Variant::Wrap<"b">(true).signature(), "b");
EXPECT_EQ(Variant::Wrap<"y">(uint8_t{1}).signature(), "y");
EXPECT_EQ(Variant::Wrap<"n">(int16_t{2}).signature(), "n");
EXPECT_EQ(Variant::Wrap<"q">(uint16_t{3}).signature(), "q");
EXPECT_EQ(Variant::Wrap<"i">(int32_t{4}).signature(), "i");
EXPECT_EQ(Variant::Wrap<"u">(uint32_t{5}).signature(), "u");
EXPECT_EQ(Variant::Wrap<"x">(int64_t{6}).signature(), "x");
EXPECT_EQ(Variant::Wrap<"t">(uint64_t{7}).signature(), "t");
EXPECT_EQ(Variant::Wrap<"d">(8.0).signature(), "d");
EXPECT_EQ(Variant::Wrap<"s">(std::string("nine")).signature(), "s");
EXPECT_EQ(Variant::Wrap<"o">(dbus::ObjectPath("/ten")).signature(), "o");
EXPECT_EQ(Variant::Wrap<"h">(base::ScopedFD()).signature(), "h");
// Containers
EXPECT_EQ(Variant::Wrap<"ad">(TestVector{1.0}).signature(), "ad");
EXPECT_EQ(Variant::Wrap<"a{qb}">(TestMap{{1, true}}).signature(), "a{qb}");
EXPECT_EQ(Variant::Wrap<"(is)">(TestTuple{1, "s"}).signature(), "(is)");
// Nested Variant
auto inner_variant = Variant::Wrap<"s">(std::string("hello"));
auto outer_variant = Variant::Wrap<"v">(std::move(inner_variant));
EXPECT_EQ(outer_variant.signature(), "v");
}
TEST(DBusVariantTest, VariantRoundTripString) {
const std::string kInnerString = "hello variant";
auto original_variant = dbus_utils::Variant::Wrap<"s">(kInnerString);
EXPECT_EQ(original_variant.signature(), "s");
std::unique_ptr<dbus::Response> response = dbus::Response::CreateEmpty();
dbus::MessageWriter writer(response.get());
internal::WriteValue(writer, original_variant);
// Test correct type retrieval and signature on Read
dbus::MessageReader reader(response.get());
auto variant_read_back = internal::ReadValue<dbus_utils::Variant>(reader);
ASSERT_TRUE(variant_read_back) << "Failed to read variant";
EXPECT_EQ(variant_read_back->signature(), "s");
auto str_opt = std::move(*variant_read_back).Take<std::string>();
ASSERT_TRUE(str_opt.has_value());
EXPECT_EQ(*str_opt, kInnerString);
EXPECT_TRUE(variant_read_back->signature().empty()); // Emptied after Take
// Test type mismatch clears signature
dbus::MessageReader reader_for_mismatch(response.get());
auto variant_for_mismatch =
internal::ReadValue<dbus_utils::Variant>(reader_for_mismatch);
ASSERT_TRUE(variant_for_mismatch)
<< "Failed to read variant for mismatch test";
EXPECT_EQ(variant_for_mismatch->signature(), "s");
auto int_opt = std::move(*variant_for_mismatch).Take<int32_t>();
EXPECT_FALSE(int_opt.has_value());
EXPECT_TRUE(variant_for_mismatch->signature().empty());
}
TEST(DBusVariantTest, VariantRoundTripInt32) {
const int32_t kInnerInt = 42;
auto original_variant = dbus_utils::Variant::Wrap<"i">(kInnerInt);
EXPECT_EQ(original_variant.signature(), "i");
std::unique_ptr<dbus::Response> response = dbus::Response::CreateEmpty();
dbus::MessageWriter writer(response.get());
internal::WriteValue(writer, original_variant);
// Test correct type retrieval
dbus::MessageReader reader(response.get());
auto variant_read_back = internal::ReadValue<dbus_utils::Variant>(reader);
ASSERT_TRUE(variant_read_back) << "Failed to read variant";
EXPECT_EQ(variant_read_back->signature(), "i");
auto int_opt = std::move(*variant_read_back).Take<int32_t>();
ASSERT_TRUE(int_opt.has_value());
EXPECT_EQ(*int_opt, kInnerInt);
EXPECT_TRUE(variant_read_back->signature().empty());
}
TEST(DBusVariantTest, VariantRoundTripTestStruct2) {
using TestTuple = std::tuple<int32_t, std::string, std::vector<double>>;
const TestTuple kExpectedData = {101, "complex", {1.5, 2.5, 3.5}};
auto original_variant = Variant::Wrap<"(isad)">(kExpectedData);
EXPECT_EQ(original_variant.signature(), "(isad)");
std::unique_ptr<dbus::Response> response = dbus::Response::CreateEmpty();
dbus::MessageWriter writer(response.get());
internal::WriteValue(writer, original_variant);
dbus::MessageReader reader(response.get());
auto variant_read_back = internal::ReadValue<Variant>(reader);
ASSERT_TRUE(variant_read_back) << "Failed to read variant";
EXPECT_EQ(variant_read_back->signature(), "(isad)");
auto data_read = std::move(*variant_read_back).Take<TestTuple>();
EXPECT_TRUE(variant_read_back->signature().empty());
ASSERT_TRUE(data_read.has_value());
EXPECT_EQ(*data_read, kExpectedData);
}
TEST(DBusVariantTest, VariantRoundTripMap) {
const std::map<std::string, int32_t> kInnerMap = {
{"one", 1}, {"two", 2}, {"three", 3}};
auto original_variant = dbus_utils::Variant::Wrap<"a{si}">(kInnerMap);
EXPECT_EQ(original_variant.signature(), "a{si}");
std::unique_ptr<dbus::Response> response = dbus::Response::CreateEmpty();
dbus::MessageWriter writer(response.get());
internal::WriteValue(writer, original_variant);
dbus::MessageReader reader(response.get());
auto variant_read_back = internal::ReadValue<dbus_utils::Variant>(reader);
ASSERT_TRUE(variant_read_back) << "Failed to read variant";
EXPECT_EQ(variant_read_back->signature(), "a{si}");
auto map_opt =
std::move(*variant_read_back).Take<std::map<std::string, int32_t>>();
ASSERT_TRUE(map_opt.has_value());
EXPECT_TRUE(variant_read_back->signature().empty());
EXPECT_EQ(*map_opt, kInnerMap);
}
TEST(DBusVariantTest, VariantRoundTripVectorOfTuples) {
using TestSimpleTuple = std::tuple<int32_t, std::string>;
const std::vector<TestSimpleTuple> kInnerVec = {{1, "a"}, {2, "b"}};
auto original_variant = dbus_utils::Variant::Wrap<"a(is)">(kInnerVec);
EXPECT_EQ(original_variant.signature(), "a(is)");
std::unique_ptr<dbus::Response> response = dbus::Response::CreateEmpty();
dbus::MessageWriter writer(response.get());
internal::WriteValue(writer, original_variant);
dbus::MessageReader reader(response.get());
auto variant_read_back = internal::ReadValue<dbus_utils::Variant>(reader);
ASSERT_TRUE(variant_read_back) << "Failed to read variant";
EXPECT_EQ(variant_read_back->signature(), "a(is)");
auto vec_opt =
std::move(*variant_read_back).Take<std::vector<TestSimpleTuple>>();
ASSERT_TRUE(vec_opt.has_value());
EXPECT_TRUE(variant_read_back->signature().empty());
EXPECT_EQ(*vec_opt, kInnerVec);
}
TEST(DBusVariantTest, VariantRoundTripArrayOfInts) {
const std::vector<int32_t> kInnerArray = {1, 2, 3, 4};
auto original_variant = dbus_utils::Variant::Wrap<"ai">(kInnerArray);
EXPECT_EQ(original_variant.signature(), "ai");
std::unique_ptr<dbus::Response> response = dbus::Response::CreateEmpty();
dbus::MessageWriter writer(response.get());
internal::WriteValue(writer, original_variant);
dbus::MessageReader reader(response.get());
auto variant_read_back = internal::ReadValue<dbus_utils::Variant>(reader);
ASSERT_TRUE(variant_read_back) << "Failed to read variant";
EXPECT_EQ(variant_read_back->signature(), "ai");
auto vec_opt = std::move(*variant_read_back).Take<std::vector<int32_t>>();
ASSERT_TRUE(vec_opt.has_value());
EXPECT_TRUE(variant_read_back->signature().empty());
EXPECT_EQ(*vec_opt, kInnerArray);
}
TEST(DBusVariantTest, VariantOfVariantHoldingInt) {
auto original_outer_variant = dbus_utils::Variant::Wrap<"v">(
dbus_utils::Variant::Wrap<"i">(static_cast<int32_t>(12345)));
EXPECT_EQ(original_outer_variant.signature(), "v");
std::unique_ptr<dbus::Response> response = dbus::Response::CreateEmpty();
dbus::MessageWriter writer(response.get());
internal::WriteValue(writer, original_outer_variant);
dbus::MessageReader reader(response.get());
auto outer_variant_read_back =
internal::ReadValue<dbus_utils::Variant>(reader);
ASSERT_TRUE(outer_variant_read_back) << "Failed to read outer variant";
EXPECT_EQ(outer_variant_read_back->signature(), "v");
EXPECT_FALSE(reader.HasMoreData());
auto inner_variant_opt =
std::move(*outer_variant_read_back).Take<dbus_utils::Variant>();
EXPECT_TRUE(outer_variant_read_back->signature().empty());
ASSERT_TRUE(inner_variant_opt.has_value())
<< "Outer variant did not contain an inner variant.";
EXPECT_EQ(inner_variant_opt->signature(), "i");
// Test successful Get from inner variant.
auto int_opt = std::move(*inner_variant_opt).Take<int32_t>();
ASSERT_TRUE(int_opt.has_value())
<< "Inner variant did not contain an int32_t.";
EXPECT_EQ(*int_opt, 12345);
EXPECT_TRUE(inner_variant_opt->signature().empty());
}
TEST(DBusVariantTest, VariantGetTypeSafety) {
const int32_t kInnerInt = 99;
auto original_variant = dbus_utils::Variant::Wrap<"i">(kInnerInt);
std::unique_ptr<dbus::Response> response = dbus::Response::CreateEmpty();
dbus::MessageWriter writer(response.get());
internal::WriteValue(writer, original_variant);
// Read the variant and try to Get the wrong type.
dbus::MessageReader reader1(response.get());
auto variant1 = internal::ReadValue<dbus_utils::Variant>(reader1);
ASSERT_TRUE(variant1);
auto str_opt = std::move(*variant1).Take<std::string>();
EXPECT_FALSE(str_opt.has_value());
// The variant is not consumed on failed Take. Read it again to Get the
// correct type.
dbus::MessageReader reader2(response.get());
auto variant = internal::ReadValue<dbus_utils::Variant>(reader2);
ASSERT_TRUE(variant);
EXPECT_EQ(variant->signature(), "i");
auto int_opt = std::move(*variant).Take<int32_t>();
ASSERT_TRUE(int_opt.has_value());
EXPECT_EQ(*int_opt, kInnerInt);
}
TEST(DBusVariantTest, VariantIsEmptyAfterSuccessfulTake) {
auto variant = Variant::Wrap<"s">(std::string("some string"));
// Take the value. This consumes the variant.
auto value = std::move(variant).Take<std::string>();
ASSERT_TRUE(value.has_value());
// The variant is now in a moved-from state, which the implementation defines
// as empty.
EXPECT_TRUE(variant.signature().empty());
}
TEST(DBusVariantTest, VariantIsEmptyAfterFailedTake) {
auto variant = Variant::Wrap<"s">(std::string("another string"));
ASSERT_EQ(variant.signature(), "s");
// Attempt to take the wrong type.
auto wrong_value = std::move(variant).Take<int32_t>();
ASSERT_FALSE(wrong_value.has_value());
// The variant should have been cleared.
EXPECT_TRUE(variant.signature().empty());
}
} // namespace
} // namespace dbus_utils