blob: fae32aa53c2954d80ab760770b732f5579ceb2a5 [file] [log] [blame]
// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "remoting/host/linux/gvariant_ref.h"
#include <glib.h>
#include <algorithm>
#include <array>
#include <compare>
#include <cstddef>
#include <cstdint>
#include <iterator>
#include <map>
#include <optional>
#include <set>
#include <string>
#include <string_view>
#include <tuple>
#include <utility>
#include <variant>
#include <vector>
#include "base/check.h"
#include "base/types/expected.h"
#include "remoting/host/linux/gvariant_type.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace remoting::gvariant {
namespace {
// A type whose mapping provides a From static method.
template <typename T, Type C>
concept ProvidesFrom = requires(T v) { GVariantRef<C>::template From<T>(v); };
// A type whose mapping provides a TryFrom static method.
template <typename T, Type C>
concept ProvidesTryFrom =
requires(T v) { GVariantRef<C>::template TryFrom<T>(v); };
// A type whose mapping provides an Into static method for the provided Type.
template <typename T, Type C>
concept ProvidesInto = requires(GVariantRef<C> v) { v.template Into<T>(); };
// A type whose mapping provides a TryInto static method.
template <typename T, Type C>
concept ProvidesTryInto =
requires(GVariantRef<C> v) { v.template TryInto<T>(); };
template <Type Parse, Type Return = Parse>
GVariantRef<Return> GVariantParse(const char* text) {
GVariant* variant =
g_variant_parse(Parse.gvariant_type(), text, nullptr, nullptr, nullptr);
CHECK(variant);
return GVariantRef<Return>::TakeUnchecked(variant);
}
} // namespace
TEST(GVariantRefTest, EncodesBasicTypes) {
EXPECT_EQ(GVariantParse<"b">("true"), GVariantRef<"b">::From(true));
EXPECT_EQ(GVariantParse<"y">("5"), GVariantRef<"y">::From(std::uint8_t{5}));
EXPECT_EQ(GVariantParse<"n">("-17"),
GVariantRef<"n">::From(std::int16_t{-17}));
EXPECT_EQ(GVariantParse<"q">("103"),
GVariantRef<"q">::From(std::uint16_t{103}));
EXPECT_EQ(GVariantParse<"i">("603"),
GVariantRef<"i">::From(std::int32_t{603}));
EXPECT_EQ(GVariantParse<"u">("57"),
GVariantRef<"u">::From(std::uint32_t{57}));
EXPECT_EQ(GVariantParse<"x">("-2037"),
GVariantRef<"x">::From(std::int64_t{-2037}));
EXPECT_EQ(GVariantParse<"t">("169"),
GVariantRef<"t">::From(std::uint64_t{169}));
EXPECT_EQ(GVariantParse<"d">("17.5"), GVariantRef<"d">::From(double{17.5}));
static_assert(ProvidesFrom<std::int32_t, "i">);
static_assert(!ProvidesFrom<std::int32_t, "u">);
static_assert(ProvidesFrom<std::int32_t, "*">);
static_assert(ProvidesFrom<std::int32_t, "?">);
static_assert(!ProvidesTryFrom<std::int32_t, "u">);
}
TEST(GVariantRefTest, DecodesBasicTypes) {
EXPECT_EQ(true, GVariantParse<"b">("true").Into<bool>());
EXPECT_EQ(base::ok(std::uint8_t{124}),
(GVariantParse<"y", "*">("124").TryInto<std::uint8_t>()));
EXPECT_EQ(std::int16_t{93}, GVariantParse<"n">("93").Into<std::int16_t>());
EXPECT_EQ(base::ok(std::uint16_t{567}),
(GVariantParse<"q", "*">("567").TryInto<std::uint16_t>()));
EXPECT_EQ(std::int32_t{-91643},
GVariantParse<"i">("-91643").Into<std::int32_t>());
EXPECT_EQ(base::ok(std::uint32_t{7832}),
(GVariantParse<"u", "*">("7832").TryInto<std::uint32_t>()));
EXPECT_EQ(std::int64_t{9582},
GVariantParse<"x">("9582").Into<std::int64_t>());
EXPECT_EQ(base::ok(std::uint64_t{3245}),
(GVariantParse<"t", "*">("3245").TryInto<std::uint64_t>()));
EXPECT_EQ(double{72.5}, GVariantParse<"d">("72.5").Into<double>());
EXPECT_FALSE((GVariantParse<"i", "*">("-12").TryInto<double>().has_value()));
static_assert(ProvidesInto<std::int32_t, "i">);
static_assert(!ProvidesInto<std::int32_t, "u">);
static_assert(ProvidesTryInto<std::int32_t, "*">);
static_assert(ProvidesTryInto<std::int32_t, "?">);
static_assert(!ProvidesTryInto<std::int32_t, "u">);
}
TEST(GVariantRefTest, Strings) {
EXPECT_EQ(base::ok(GVariantParse<"s">("'Hello sailor!'")),
GVariantRef<>::TryFrom(std::string("Hello sailor!")));
EXPECT_EQ(GVariantParse<"s">("'Ahoy sailor!'"),
GVariantFrom(std::string_view("Ahoy sailor!")));
EXPECT_EQ(GVariantParse<"s">("'So long sailor!'"),
GVariantFrom("So long sailor!"));
EXPECT_EQ(
base::ok(std::string("Welcome back!")),
(GVariantParse<"s", "*">("'Welcome back!'").TryInto<std::string>()));
EXPECT_EQ(std::string("Welcome back, again!"),
GVariantParse<"s">("'Welcome back, again!'").Into<std::string>());
EXPECT_FALSE(GVariantRef<"s">::TryFrom("Invalid\x85string").has_value());
EXPECT_FALSE(
(GVariantParse<"i", "*">("-5").TryInto<std::string>().has_value()));
static_assert(!ProvidesTryInto<std::string_view, "*">);
static_assert(!ProvidesTryInto<char*, "*">);
static_assert(!ProvidesTryInto<const char*, "*">);
}
TEST(GVariantRefTest, Optional) {
EXPECT_EQ(GVariantParse<"mi">("just 16"),
GVariantRef<"*">::From(std::optional(std::int32_t{16})));
EXPECT_EQ(GVariantParse<"md">("nothing"),
GVariantRef<"md">::From(std::optional<double>()));
EXPECT_EQ(GVariantParse<"ms">("just 'banana'"),
GVariantFrom(std::optional("banana")));
EXPECT_EQ(GVariantParse<"ms">("nothing"),
GVariantRef<"ms">::From(std::optional<std::string_view>()));
// Indefinite type is okay if the option contains a concrete value.
EXPECT_EQ(
base::ok(GVariantParse<"mn">("just 539")),
GVariantRef<>::TryFrom(std::optional(GVariantFrom(std::int16_t{539}))));
EXPECT_EQ(
std::optional(std::string("apple")),
GVariantParse<"ms">("just 'apple'").Into<std::optional<std::string>>());
EXPECT_EQ(
std::optional<std::uint32_t>(),
GVariantParse<"mu">("nothing").Into<std::optional<std::uint32_t>>());
EXPECT_EQ(base::ok(std::optional(std::string("peach"))),
(GVariantParse<"ms", "*">("just 'peach'")
.TryInto<std::optional<std::string>>()));
EXPECT_EQ(base::ok(std::optional<std::uint32_t>()),
(GVariantParse<"mi", "*">("nothing")
.TryInto<std::optional<std::int32_t>>()));
EXPECT_FALSE((GVariantParse<"i", "*">("15")
.TryInto<std::optional<std::int32_t>>()
.has_value()));
EXPECT_FALSE((GVariantParse<"mi", "*">("27")
.TryInto<std::optional<std::uint8_t>>()
.has_value()));
// Can't create empty invariant of indefinite type.
EXPECT_FALSE(
GVariantRef<>::TryFrom(std::optional<GVariantRef<>>()).has_value());
// An indefinite type can't be used with From(), but can be used with other
// operations supported by the inner type.
static_assert(!ProvidesFrom<std::optional<GVariantRef<>>, "m*"> &&
ProvidesTryFrom<std::optional<GVariantRef<>>, "m*"> &&
ProvidesInto<std::optional<GVariantRef<>>, "m*"> &&
ProvidesTryInto<std::optional<GVariantRef<>>, "m*">);
}
TEST(GVariantRefTest, Vector) {
EXPECT_EQ(GVariantParse<"as">("[]"),
GVariantFrom(std::vector<const char*>()));
EXPECT_EQ(GVariantParse<"an">("[5, -2, 45, -367, 97, -8]"),
GVariantFrom(std::vector<std::int16_t>{5, -2, 45, -367, 97, -8}));
EXPECT_EQ(base::ok(std::vector<double>()),
(GVariantParse<"ad", "*">("[]").TryInto<std::vector<double>>()));
EXPECT_EQ(
(std::vector<std::string>{"cabbage", "broccoli", "kale", "cauliflower"}),
GVariantParse<"as">("['cabbage', 'broccoli', 'kale', 'cauliflower']")
.Into<std::vector<std::string>>());
EXPECT_FALSE((GVariantParse<"i", "*">("7")
.TryInto<std::vector<std::int32_t>>()
.has_value()));
EXPECT_FALSE((GVariantParse<"ai", "*">("[]")
.TryInto<std::vector<std::uint8_t>>()
.has_value()));
// An indefinite type can't be used with From(), but can be used with other
// operations supported by the inner type.
static_assert(!ProvidesFrom<std::vector<GVariantRef<>>, "a*"> &&
ProvidesTryFrom<std::vector<GVariantRef<>>, "a*"> &&
ProvidesInto<std::vector<GVariantRef<>>, "a*"> &&
ProvidesTryInto<std::vector<GVariantRef<>>, "a*">);
}
TEST(GVariantRefTest, Map) {
EXPECT_EQ(GVariantParse<"a{us}">("{}"),
GVariantRef<"*">::From(std::map<std::uint32_t, const char*>()));
EXPECT_EQ(
GVariantParse<"a{sb}">(
"{'a': true, 'b': false, 'c': false, 'd': false, 'e': true}"),
GVariantFrom(std::map<std::string_view, bool>{
{"a", true}, {"b", false}, {"c", false}, {"d", false}, {"e", true}}));
EXPECT_EQ(GVariantParse<"a{ib}">(
"{1: true, 2: false, 3: false, 4: false, 5: true}"),
GVariantRef<"a{ib}">::From(std::map<std::int32_t, bool>{
{1, true}, {2, false}, {3, false}, {4, false}, {5, true}}));
EXPECT_EQ(base::ok(std::map<bool, std::uint8_t>()),
(GVariantParse<"a{by}", "*">("{}")
.TryInto<std::map<bool, std::uint8_t>>()));
EXPECT_EQ(
(std::map<std::int32_t, double>{{1, 1}, {2, 0.5}, {4, 0.25}, {8, 0.125}}),
(GVariantParse<"a{id}">("{1: 1, 2: 0.5, 4: 0.25, 8: 0.125}")
.Into<std::map<int32_t, double>>()));
EXPECT_FALSE((GVariantParse<"i", "*">("-3")
.TryInto<std::map<std::uint32_t, std::uint8_t>>()
.has_value()));
EXPECT_FALSE((GVariantParse<"a{ib}", "*">("{}")
.TryInto<std::map<std::uint32_t, std::uint8_t>>()
.has_value()));
// An indefinite type can't be used with From(), but can be used with other
// operations supported by the inner types.
using IndefiniteValue = std::map<std::int64_t, GVariantRef<>>;
using IndefiniteKey = std::map<GVariantRef<"?">, Boxed<GVariantRef<>>>;
static_assert(!ProvidesFrom<IndefiniteValue, "a{x*}"> &&
ProvidesTryFrom<IndefiniteValue, "a{x*}"> &&
ProvidesInto<IndefiniteValue, "a{x*}"> &&
ProvidesTryInto<IndefiniteValue, "a{x*}">);
static_assert(!ProvidesFrom<IndefiniteKey, "a{?v}"> &&
ProvidesTryFrom<IndefiniteKey, "a{?v}"> &&
ProvidesInto<IndefiniteKey, "a{?v}"> &&
ProvidesTryInto<IndefiniteKey, "a{?v}">);
}
TEST(GVariantRefTest, Pair) {
EXPECT_EQ(GVariantParse<"{us}">("{31, 'xyzzy'}"),
GVariantFrom(std::pair<std::uint32_t, const char*>(31, "xyzzy")));
EXPECT_EQ(GVariantParse<"{xt}">("{-64, 64}"),
GVariantRef<"{xt}">::From(
std::pair<std::int64_t, std::uint64_t>(-64, 64)));
EXPECT_EQ(base::ok(std::pair<bool, std::uint8_t>(true, 9)),
(GVariantParse<"{by}", "*">("{true, 9}")
.TryInto<std::pair<bool, std::uint8_t>>()));
EXPECT_EQ((std::pair<double, std::string>(6.5, "poof")),
(GVariantParse<"{ds}">("{6.5, 'poof'}")
.Into<std::pair<double, std::string>>()));
EXPECT_FALSE((GVariantParse<"{uu}", "*">("{5, 7}")
.TryInto<std::pair<std::uint32_t, std::uint8_t>>()
.has_value()));
}
TEST(GVariantRefTest, Range) {
EXPECT_EQ(GVariantParse<"ab">("[]"), GVariantFrom(std::array<bool, 0>()));
EXPECT_EQ(GVariantParse<"as">("['1', '2', '3', '4', '5']"),
GVariantFrom(std::set<std::string>{"5", "4", "3", "2", "1"}));
// An indefinite type can't be used with From(), but can be used with
// TryFrom().
static_assert(!ProvidesFrom<std::array<GVariantRef<>, 4>, "a*"> &&
ProvidesTryFrom<std::array<GVariantRef<>, 4>, "a*">);
}
TEST(GVariantRefTest, Tuple) {
EXPECT_EQ(GVariantParse<"()">("()"), GVariantRef<"*">::From(std::tuple()));
EXPECT_EQ(GVariantParse<"(ybmds)">("(63, true, 3.0, 'Hello!')"),
GVariantFrom(std::tuple(std::uint8_t{63}, true,
std::optional(double{3.0}), "Hello!")));
EXPECT_EQ(base::ok(std::tuple()),
(GVariantParse<"()", "*">("()").TryInto<std::tuple<>>()));
EXPECT_EQ(
std::tuple(std::string("fancy"), false, double{-6.75}, std::int64_t{512}),
(GVariantParse<"(sbdx)">("('fancy', false, -6.75, 512)")
.Into<std::tuple<std::string, bool, double, std::int64_t>>()));
EXPECT_FALSE((GVariantParse<"i", "*">("4")
.TryInto<std::tuple<std::int32_t>>()
.has_value()));
EXPECT_FALSE(
(GVariantParse<"(sisi)", "*">("('a', 1, 'b', 2)")
.TryInto<std::tuple<std::string, std::int32_t, std::string>>()
.has_value()));
EXPECT_FALSE(
(GVariantParse<"(sisi)", "*">("('a', 1, 'b', 2)")
.TryInto<std::tuple<std::string, bool, std::string, std::int32_t>>()
.has_value()));
}
TEST(GVariantRefTest, Variant) {
using Strings = std::variant<const char*, std::string, std::string_view>;
using BasicArrays =
std::variant<std::vector<double>, std::vector<std::int32_t>,
std::vector<bool>>;
using Mixed = std::variant<uint8_t, double, std::optional<std::string_view>,
GVariantRef<"?">>;
EXPECT_EQ(GVariantParse<"s">("'asparagus'"),
GVariantFrom(Strings(std::in_place_index<0>, "asparagus")));
EXPECT_EQ(GVariantParse<"s">("'broccoli'"),
GVariantFrom(Strings(std::in_place_index<1>, "broccoli")));
EXPECT_EQ(GVariantParse<"s">("'carrot'"),
GVariantFrom(Strings(std::in_place_index<2>, "carrot")));
EXPECT_EQ(GVariantParse<"ad">("[2.0, 3.5]"),
GVariantRef<"a?">::From(
BasicArrays(std::in_place_index<0>, std::vector{2.0, 3.5})));
EXPECT_EQ(GVariantParse<"ai">("[28, 16]"),
GVariantRef<"a?">::From(
BasicArrays(std::in_place_index<1>, std::vector{28, 16})));
EXPECT_EQ(GVariantParse<"ab">("[true, false]"),
GVariantRef<"a?">::From(
BasicArrays(std::in_place_index<2>, std::vector{true, false})));
EXPECT_EQ(GVariantParse<"y">("6"),
GVariantFrom(Mixed(std::in_place_index<0>, 6)));
EXPECT_EQ(GVariantParse<"d">("8.25"),
GVariantFrom(Mixed(std::in_place_index<1>, 8.25)));
EXPECT_EQ(GVariantParse<"ms">("just 'delicata squash'"),
GVariantFrom(Mixed(std::in_place_index<2>, "delicata squash")));
EXPECT_EQ(Strings(std::in_place_index<1>, "eggplant"),
GVariantParse<"s">("'eggplant'").Into<Strings>());
EXPECT_EQ(BasicArrays(std::in_place_index<2>, std::vector{true, false, true}),
GVariantParse<"ab">("[true, false, true]").Into<BasicArrays>());
EXPECT_EQ(base::ok(Mixed(std::in_place_index<0>, 12)),
(GVariantParse<"y", "*">("12").TryInto<Mixed>()));
EXPECT_EQ(Mixed(std::in_place_index<1>, 9.75),
GVariantParse<"d">("9.75").Into<GVariantRef<"?">>().Into<Mixed>());
EXPECT_EQ(
Mixed(std::in_place_index<3>, GVariantRef<"?">::From(std::int32_t{93})),
GVariantParse<"i">("93").Into<Mixed>());
// Doesn't match type string.
EXPECT_FALSE((GVariantParse<"i", "*">("16").TryInto<Strings>().has_value()));
// Matches indefinite type string, but not any of the variants
EXPECT_FALSE(
(GVariantParse<"a{sv}", "*">("{}").TryInto<Mixed>().has_value()));
// Only matches a variant that doesn't provide TryInto().
EXPECT_FALSE(
(GVariantParse<"ms", "*">("just 'fennel'").TryInto<Mixed>().has_value()));
static_assert(Mapping<Strings>::kType == Type("s"));
static_assert(Mapping<BasicArrays>::kType == Type("a?"));
static_assert(Mapping<Mixed>::kType == Type("*"));
static_assert(ProvidesTryInto<Strings, "*">);
static_assert(ProvidesInto<Strings, "s">);
static_assert(!ProvidesTryInto<std::variant<std::string_view>, "*">);
static_assert(ProvidesFrom<BasicArrays, "*">);
static_assert(!ProvidesFrom<BasicArrays, "ai">);
static_assert(ProvidesInto<BasicArrays, "ai">);
static_assert(!ProvidesInto<BasicArrays, "*">);
}
TEST(GVariantRefTest, NestedGVariantRef) {
EXPECT_EQ(
base::ok(GVariantParse<"(sb)">("('X-387', true)")),
GVariantRef<"(s?)">::TryFrom(std::tuple(GVariantRef<"*">::From("X-387"),
GVariantRef<"*">::From(true))));
EXPECT_EQ(GVariantParse<"i">("6"),
GVariantRef<"?">::From(GVariantRef<"i">::From(std::int32_t{6})));
auto tuple = GVariantParse<"(si)">("('Spiff', 8521)")
.Into<std::tuple<GVariantRef<"s">, GVariantRef<>>>();
EXPECT_EQ(std::string("Spiff"), std::get<0>(tuple).Into<std::string>());
EXPECT_EQ(base::ok(std::int32_t{8521}),
std::get<1>(tuple).TryInto<std::int32_t>());
EXPECT_EQ(base::ok(true),
GVariantRef<"b">::From(true)
.Into<GVariantRef<"?">>()
.TryInto<GVariantRef<"b">>()
.transform([](auto v) { return v.template Into<bool>(); }));
EXPECT_FALSE(
GVariantRef<"i">::TryFrom(GVariantRef<"?">::From(std::uint8_t{45}))
.has_value());
EXPECT_FALSE(
GVariantRef<"?">::From(29.5).TryInto<GVariantRef<"u">>().has_value());
static_assert(ProvidesInto<GVariantRef<"a*">, "av">);
static_assert(ProvidesFrom<GVariantRef<"av">, "a*">);
static_assert(!ProvidesInto<GVariantRef<"av">, "a*">);
static_assert(!ProvidesFrom<GVariantRef<"a*">, "av">);
static_assert(ProvidesTryInto<GVariantRef<"av">, "a*">);
static_assert(ProvidesTryFrom<GVariantRef<"a*">, "av">);
static_assert(!ProvidesTryInto<GVariantRef<"av">, "ar">);
static_assert(!ProvidesTryFrom<GVariantRef<"ar">, "av">);
}
TEST(GVariantRefTest, Ignored) {
EXPECT_TRUE((GVariantParse<"a{sv}", "*">("{'one': <uint32 1>, 'two': <2.0>}")
.TryInto<Ignored>()
.has_value()));
// Any GVariantRef can be converted to an Ignored, but not the other way
// around.
static_assert(!ProvidesFrom<Ignored, "*"> && !ProvidesTryFrom<Ignored, "*"> &&
ProvidesInto<Ignored, "*"> && ProvidesTryInto<Ignored, "*">);
}
TEST(GVariantRefTest, Boxed) {
EXPECT_EQ(GVariantParse<"v">("<'beep'>"), GVariantFrom(Boxed{"beep"}));
std::vector<std::string> value = {"chirp", "whistle"};
EXPECT_EQ(GVariantParse<"v">("<['chirp', 'whistle']>"),
GVariantFrom(BoxedRef(value)));
EXPECT_EQ((Boxed<std::variant<std::uint16_t, std::string>>{"boop"}),
(GVariantParse<"v">("<'boop'>")
.TryInto<Boxed<std::variant<std::uint16_t, std::string>>>()));
EXPECT_EQ(Boxed{GVariantFrom(std::int64_t{-204})},
GVariantParse<"v">("<int64 -204>").Into<Boxed<GVariantRef<>>>());
EXPECT_FALSE((GVariantParse<"i", "*">("-27")
.TryInto<Boxed<std::int32_t>>()
.has_value()));
EXPECT_FALSE((GVariantParse<"v", "*">("<'buzz'>")
.TryInto<Boxed<std::int32_t>>()
.has_value()));
// A boxed value is definite even if the inner type is not.
static_assert(ProvidesFrom<std::vector<Boxed<GVariantRef<>>>, "*">);
}
TEST(GVariantRefTest, FilledMaybe) {
EXPECT_EQ(GVariantParse<"ms">("just 'alpaca'"),
GVariantFrom(FilledMaybe{"alpaca"}));
// Since the value is always guaranteed to be present, indefinite types (whose
// actual type is only known at runtime) can be used with From.
EXPECT_EQ(
GVariantParse<"mx">("-362"),
GVariantFrom(FilledMaybe{GVariantRef<"*">::From(std::int64_t{-362})}));
std::vector<std::uint16_t> value = {1, 2, 3};
EXPECT_EQ(GVariantParse<"maq">("just [1, 2, 3]"),
GVariantFrom(FilledMaybeRef(value)));
EXPECT_EQ(
base::ok(FilledMaybe{true}),
(GVariantParse<"mb", "*">("just true").TryInto<FilledMaybe<bool>>()));
EXPECT_FALSE((GVariantParse<"u", "*">("45")
.TryInto<FilledMaybe<std::uint32_t>>()
.has_value()));
EXPECT_FALSE((GVariantParse<"mu", "*">("nothing")
.TryInto<FilledMaybe<std::uint32_t>>()
.has_value()));
EXPECT_FALSE((GVariantParse<"mi", "*">("just 45")
.TryInto<FilledMaybe<std::uint32_t>>()
.has_value()));
}
TEST(GVariantRefTest, EmptyArrayOf) {
EXPECT_EQ(GVariantParse<"ai">("[]"),
GVariantRef<"ai">::From(EmptyArrayOf<"i">()));
EXPECT_TRUE((
GVariantParse<"ai", "*">("[]").TryInto<EmptyArrayOf<"i">>().has_value()));
EXPECT_FALSE((
GVariantParse<"au", "*">("[]").TryInto<EmptyArrayOf<"i">>().has_value()));
EXPECT_FALSE((GVariantParse<"ai", "*">("[7]")
.TryInto<EmptyArrayOf<"i">>()
.has_value()));
}
TEST(GVariantRefTest, ObjectPath) {
EXPECT_FALSE(ObjectPath::TryFrom("invalid path").has_value());
auto path = ObjectPath::TryFrom("/valid/path");
ASSERT_TRUE(path.has_value());
EXPECT_EQ(GVariantParse<"o">("'/valid/path'"), GVariantFrom(path.value()));
EXPECT_EQ(GVariantParse<"o">("'/valid/path'"),
GVariantFrom(ObjectPathCStr(path.value())));
EXPECT_EQ(path.value(),
GVariantParse<"o">("'/valid/path'").Into<ObjectPath>());
EXPECT_EQ(GVariantParse<"o">("'/path/constant'"),
GVariantFrom(ObjectPathCStr("/path/constant")));
EXPECT_FALSE((GVariantParse<"s", "*">("'/valid/path'")
.TryInto<ObjectPath>()
.has_value()));
}
TEST(GVariantRefTest, TypeSignature) {
EXPECT_FALSE(TypeSignature::TryFrom("invalid signature").has_value());
auto signature = TypeSignature::TryFrom("goodsig");
ASSERT_TRUE(signature.has_value());
EXPECT_EQ(GVariantParse<"g">("'goodsig'"), GVariantFrom(signature.value()));
EXPECT_EQ(GVariantParse<"g">("'goodsig'"),
GVariantFrom(TypeSignatureCStr(signature.value())));
EXPECT_EQ(signature.value(),
GVariantParse<"g">("'goodsig'").Into<TypeSignature>());
EXPECT_EQ(GVariantParse<"g">("'gonstsig'"),
GVariantFrom(TypeSignatureCStr("gonstsig")));
EXPECT_FALSE((GVariantParse<"s", "*">("'goodsig'")
.TryInto<TypeSignature>()
.has_value()));
}
TEST(GVariantRefTest, FromReferences) {
std::int32_t int32 = 57;
std::string string = "check";
std::vector<std::uint8_t> data{1, 2, 3, 4, 5};
Boxed<std::vector<std::uint8_t>&> boxed_data{data};
EXPECT_EQ(GVariantParse<"(isv)">("(57, 'check', <[byte 1, 2, 3, 4, 5]>)"),
GVariantFrom(std::forward_as_tuple(int32, string, boxed_data)));
}
TEST(GVariantRefTest, Destructure) {
std::int32_t a, b;
std::string c;
std::int64_t d, f;
std::uint64_t e, g;
std::uint32_t h;
GVariantRef<> i;
GVariantParse<"(ii{s((xt)(xt))}uv)">(
"(1, 2, {'3', ((4, 5), (6, 7))}, 8, <just byte 9>)")
.Destructure(
a, b,
std::forward_as_tuple(
c, std::forward_as_tuple(std::tie(d, e), std::tie(f, g))),
h, std::tie(i));
EXPECT_EQ(1, a);
EXPECT_EQ(2, b);
EXPECT_EQ("3", c);
EXPECT_EQ(4, d);
EXPECT_EQ(5u, e);
EXPECT_EQ(6, f);
EXPECT_EQ(7u, g);
EXPECT_EQ(8u, h);
EXPECT_EQ(GVariantFrom(std::optional<std::uint8_t>(9)), i);
}
TEST(GVariantRefTest, TryDestructure) {
std::int32_t a, b;
std::string c;
std::int64_t d, f;
std::uint64_t e, g;
std::uint32_t h;
std::uint8_t i;
ASSERT_TRUE((GVariantParse<"(ii{sa(xt)}umy)", "*">(
"(1, 2, {'3', [(4, 5), (6, 7)]}, 8, just 9)")
.TryDestructure(a, b,
std::forward_as_tuple(
c, std::forward_as_tuple(
std::tie(d, e), std::tie(f, g))),
h, std::tie(i))
.has_value()));
EXPECT_EQ(1, a);
EXPECT_EQ(2, b);
EXPECT_EQ("3", c);
EXPECT_EQ(4, d);
EXPECT_EQ(5u, e);
EXPECT_EQ(6, f);
EXPECT_EQ(7u, g);
EXPECT_EQ(8u, h);
EXPECT_EQ(9, i);
std::int16_t x;
EXPECT_FALSE(
(GVariantParse<"mn", "*">("nothing").TryDestructure(x).has_value()));
std::uint16_t y;
std::uint16_t z;
EXPECT_FALSE(
GVariantParse<"aq">("[1, 2, 3]").TryDestructure(y, z).has_value());
}
TEST(GVariantRefTest, Iteration) {
auto variant = GVariantParse<"as">("['a', 'b', 'c', 'd', 'e']");
std::vector<std::string> result;
for (auto elem : variant) {
result.push_back(elem.Into<std::string>());
}
EXPECT_EQ((std::vector<std::string>{"a", "b", "c", "d", "e"}), result);
std::vector<std::string> rev_result;
std::for_each(std::reverse_iterator(variant.end()),
std::reverse_iterator(variant.begin()), [&](const auto& elem) {
rev_result.push_back(elem.template Into<std::string>());
});
EXPECT_EQ((std::vector<std::string>{"e", "d", "c", "b", "a"}), rev_result);
EXPECT_EQ(std::partial_ordering::less, variant.begin() <=> variant.end());
EXPECT_EQ(std::partial_ordering::equivalent,
variant.begin() <=> variant.begin());
EXPECT_EQ(5, variant.end() - variant.begin());
EXPECT_EQ("c", (*(variant.begin() + 2)).string_view());
EXPECT_EQ("c", (*(2 + variant.begin())).string_view());
EXPECT_EQ("d", (*(variant.end() - 2)).string_view());
auto inc = variant.begin();
inc += 1;
inc += 2;
EXPECT_EQ("d", (*inc).string_view());
auto dec = variant.end();
dec -= 1;
dec -= 2;
EXPECT_EQ("c", (*dec).string_view());
EXPECT_EQ("e", variant.begin()[4].string_view());
}
TEST(GVariantRefTest, TupleLike) {
auto [a, b, c, d] = GVariantParse<"(iuxt)">("(1, 2, 3, 4)");
EXPECT_EQ(1, a.Into<std::int32_t>());
EXPECT_EQ(2u, b.Into<std::uint32_t>());
EXPECT_EQ(3, c.Into<std::int64_t>());
EXPECT_EQ(4u, d.Into<std::uint64_t>());
}
TEST(GVariantRefTest, Lookup) {
auto vardict = GVariantParse<"a{sv}">("{'a': <true>, 'b': <int32 5>}");
auto dict2 = GVariantParse<"a{ib}">("{1: false, 3: true, 5: true}");
EXPECT_EQ(GVariantFrom(Boxed{true}), vardict.LookUp("a"));
EXPECT_EQ(GVariantFrom(Boxed<std::int32_t>{5}), vardict.LookUp("b"));
EXPECT_EQ(std::nullopt, vardict.LookUp("c"));
EXPECT_EQ(GVariantFrom(false), dict2.LookUp(std::int32_t{1}));
EXPECT_EQ(std::nullopt, dict2.LookUp(std::int32_t{2}));
EXPECT_EQ(GVariantFrom(true), dict2.LookUp(std::int32_t{3}));
EXPECT_EQ(std::nullopt, dict2.LookUp(std::int32_t{4}));
EXPECT_EQ(GVariantFrom(true), dict2.LookUp(std::int32_t{5}));
}
} // namespace remoting::gvariant