910e62b5创建于 1月15日历史提交
// 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 <unistd.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());
  WriteValue(writer, original_variant);

  // Test correct type retrieval and signature on Read
  dbus::MessageReader reader(response.get());
  auto variant_read_back = 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 =
      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());
  WriteValue(writer, original_variant);

  // Test correct type retrieval
  dbus::MessageReader reader(response.get());
  auto variant_read_back = 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());
  WriteValue(writer, original_variant);

  dbus::MessageReader reader(response.get());
  auto variant_read_back = 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());
  WriteValue(writer, original_variant);

  dbus::MessageReader reader(response.get());
  auto variant_read_back = 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());
  WriteValue(writer, original_variant);

  dbus::MessageReader reader(response.get());
  auto variant_read_back = 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());
  WriteValue(writer, original_variant);

  dbus::MessageReader reader(response.get());
  auto variant_read_back = 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());
  WriteValue(writer, original_outer_variant);

  dbus::MessageReader reader(response.get());
  auto outer_variant_read_back = 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());
  WriteValue(writer, original_variant);

  // Read the variant and try to Get the wrong type.
  dbus::MessageReader reader1(response.get());
  auto variant1 = 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 = 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());
}

TEST(DBusVariantTest, ConstructorConvertsTypes) {
  auto variant = Variant::Wrap<"i">(123);
  EXPECT_EQ(variant.signature(), "i");
  EXPECT_EQ(std::move(variant).Take<int32_t>(), 123);

  variant = Variant::Wrap<"n">(123);
  EXPECT_EQ(variant.signature(), "n");
  EXPECT_EQ(std::move(variant).Take<int16_t>(), 123);

  variant = Variant::Wrap<"y">(123);
  EXPECT_EQ(variant.signature(), "y");
  EXPECT_EQ(std::move(variant).Take<uint8_t>(), 123);
}

TEST(DBusVariantTest, Equality) {
  // Empty variants.
  EXPECT_EQ(Variant(), Variant());

  // Primitives.
  EXPECT_EQ(Variant::Wrap<"i">(123), Variant::Wrap<"i">(123));
  EXPECT_NE(Variant::Wrap<"i">(123), Variant::Wrap<"i">(456));

  // Different signature.
  EXPECT_NE(Variant::Wrap<"i">(123), Variant::Wrap<"u">(123));
  EXPECT_NE(Variant::Wrap<"i">(123), Variant());
  EXPECT_NE(Variant(), Variant::Wrap<"i">(123));

  EXPECT_EQ(Variant::Wrap<"s">("hello"), Variant::Wrap<"s">("hello"));
  EXPECT_NE(Variant::Wrap<"s">("hello"), Variant::Wrap<"s">("world"));

  // Containers.
  std::vector<int32_t> vec1 = {1, 2, 3};
  std::vector<int32_t> vec2 = {1, 2, 3};
  std::vector<int32_t> vec3 = {1, 2, 4};
  EXPECT_EQ(Variant::Wrap<"ai">(vec1), Variant::Wrap<"ai">(vec2));
  EXPECT_NE(Variant::Wrap<"ai">(vec1), Variant::Wrap<"ai">(vec3));

  // Nested variants.
  EXPECT_EQ(Variant::Wrap<"v">(Variant::Wrap<"i">(123)),
            Variant::Wrap<"v">(Variant::Wrap<"i">(123)));
  EXPECT_NE(Variant::Wrap<"v">(Variant::Wrap<"i">(123)),
            Variant::Wrap<"v">(Variant::Wrap<"i">(456)));

  // Moved-from variants (empty).
  Variant v1 = Variant::Wrap<"i">(123);
  Variant v2 = std::move(v1);
  EXPECT_EQ(v1, Variant());  // v1 is empty after move.
  EXPECT_EQ(v2, Variant::Wrap<"i">(123));

  // ScopedFD (comparing FDs).
  int fds1[2];
  int fds2[2];
  ASSERT_EQ(pipe(fds1), 0);
  ASSERT_EQ(pipe(fds2), 0);

  base::ScopedFD scoped_fd1_read(fds1[0]);
  base::ScopedFD scoped_fd1_write(fds1[1]);
  base::ScopedFD scoped_fd2_read(fds2[0]);
  base::ScopedFD scoped_fd2_write(fds2[1]);

  // Test inequality of different FDs.
  EXPECT_NE(Variant::Wrap<"h">(std::move(scoped_fd1_read)),
            Variant::Wrap<"h">(std::move(scoped_fd2_read)));

  // Test equality with empty FDs.
  EXPECT_EQ(Variant::Wrap<"h">(base::ScopedFD()),
            Variant::Wrap<"h">(base::ScopedFD()));
}

}  // namespace
}  // namespace dbus_utils