1 // Copyright 2023 The Pigweed Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4 // use this file except in compliance with the License. You may obtain a copy of
5 // the License at
6 //
7 // https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 // License for the specific language governing permissions and limitations under
13 // the License.
14
15 #include "pw_bluetooth_sapphire/internal/host/common/inspectable.h"
16
17 #include <gmock/gmock.h>
18
19 #include "pw_bluetooth_sapphire/internal/host/testing/inspect.h"
20 #include "pw_unit_test/framework.h"
21
22 #ifndef NINSPECT
23
24 namespace bt {
25
26 namespace {
27
28 using namespace inspect::testing;
29
30 template <typename T>
31 class TestProperty {
32 public:
33 using ValueCallback = fit::function<void(const T& value)>;
34 TestProperty() = default;
TestProperty(T value,ValueCallback cb)35 TestProperty(T value, ValueCallback cb)
36 : value_(value), value_cb_(std::move(cb)) {}
37
Set(const T & value)38 void Set(const T& value) {
39 value_ = value;
40 if (value_cb_) {
41 value_cb_(value_);
42 }
43 }
44
45 private:
46 T value_;
47 fit::function<void(const T& value)> value_cb_;
48 };
49
50 struct TestValue {
TestValuebt::__anon1dbb00630111::TestValue51 explicit TestValue(int val) : value(val) {}
set_valuebt::__anon1dbb00630111::TestValue52 void set_value(int val) { value = val; }
53 int value;
54 };
55
56 struct StringValue {
ToStringbt::__anon1dbb00630111::StringValue57 std::string ToString() const { return value; }
58 std::string value;
59 };
60
61 } // namespace
62
63 using TestInspectable = Inspectable<int, TestProperty<int>, int>;
64
TEST(InspectableTest,SetPropertyChangesProperty)65 TEST(InspectableTest, SetPropertyChangesProperty) {
66 std::optional<int> prop_value_0;
67 auto prop_cb_0 = [&](auto value) { prop_value_0 = value; };
68
69 TestInspectable inspectable(0, TestProperty<int>(1, prop_cb_0));
70 EXPECT_EQ(0, *inspectable);
71 ASSERT_TRUE(prop_value_0.has_value());
72 EXPECT_EQ(0, prop_value_0.value());
73
74 std::optional<int> prop_value_1;
75 auto prop_cb_1 = [&](auto value) { prop_value_1 = value; };
76 inspectable.SetProperty(TestProperty<int>(1, prop_cb_1));
77 ASSERT_TRUE(prop_value_1.has_value());
78 // New property should be updated with current value.
79 EXPECT_EQ(0, prop_value_1.value());
80
81 inspectable.Set(2);
82 // Old property should not be updated.
83 ASSERT_TRUE(prop_value_0.has_value());
84 EXPECT_EQ(0, prop_value_0.value());
85 // New property should be updated.
86 ASSERT_TRUE(prop_value_1.has_value());
87 EXPECT_EQ(2, prop_value_1.value());
88 }
89
TEST(InspectableTest,ConstructionWithValueOnly)90 TEST(InspectableTest, ConstructionWithValueOnly) {
91 TestInspectable inspectable(2);
92 EXPECT_EQ(2, *inspectable);
93
94 // Updating property should be a no-op, but value should still be updated.
95 inspectable.Set(1);
96 EXPECT_EQ(1, *inspectable);
97 }
98
TEST(InspectableTest,PropertyValueUpdatedOnConstruction)99 TEST(InspectableTest, PropertyValueUpdatedOnConstruction) {
100 std::optional<int> prop_value;
101 auto prop_cb = [&](auto value) { prop_value = value; };
102
103 TestInspectable inspectable(0, TestProperty<int>(1, std::move(prop_cb)));
104 EXPECT_EQ(0, *inspectable);
105 ASSERT_TRUE(prop_value.has_value());
106 // Property value should not still be 1.
107 EXPECT_EQ(0, prop_value.value());
108 }
109
TEST(InspectableTest,Set)110 TEST(InspectableTest, Set) {
111 std::optional<int> prop_value;
112 auto prop_cb = [&](auto value) { prop_value = value; };
113
114 TestInspectable inspectable(0, TestProperty<int>(0, std::move(prop_cb)));
115 inspectable.Set(1);
116 EXPECT_EQ(1, *inspectable);
117 ASSERT_TRUE(prop_value.has_value());
118 EXPECT_EQ(1, prop_value.value());
119 }
120
TEST(InspectableTest,UpdateValueThroughMutable)121 TEST(InspectableTest, UpdateValueThroughMutable) {
122 std::optional<int> prop_value;
123 auto prop_cb = [&](auto value) { prop_value = value; };
124
125 Inspectable<TestValue, TestProperty<int>, int> inspectable(
126 TestValue(0),
127 TestProperty<int>(0, std::move(prop_cb)),
128 [](const TestValue& v) { return v.value; });
129 inspectable.Mutable()->set_value(1);
130 EXPECT_EQ(1, inspectable->value);
131 ASSERT_TRUE(prop_value.has_value());
132 EXPECT_EQ(1, prop_value.value());
133 }
134
TEST(InspectableTest,MakeToStringInspectConvertFunction)135 TEST(InspectableTest, MakeToStringInspectConvertFunction) {
136 const auto kPropertyName = "test_property";
137 inspect::Inspector inspector;
138 auto& root = inspector.GetRoot();
139
140 StringInspectable inspectable(StringValue{""},
141 root.CreateString(kPropertyName, ""),
142 MakeToStringInspectConvertFunction());
143
144 const std::string kExpectedValue = "fuchsia";
145 inspectable.Mutable()->value = kExpectedValue;
146 EXPECT_EQ(kExpectedValue, inspectable->value);
147
148 auto hierarchy = inspect::ReadFromVmo(inspector.DuplicateVmo());
149 ASSERT_TRUE(hierarchy.is_ok());
150 EXPECT_THAT(hierarchy.take_value(),
151 AllOf(NodeMatches(PropertyList(
152 ElementsAre(StringIs(kPropertyName, kExpectedValue))))));
153 }
154
TEST(InspectableTest,MakeContainerOfToStringConvertFunction)155 TEST(InspectableTest, MakeContainerOfToStringConvertFunction) {
156 const auto kPropertyName = "test_property";
157 inspect::Inspector inspector;
158 auto& root = inspector.GetRoot();
159
160 std::array values = {
161 StringValue{"fuchsia"}, StringValue{"purple"}, StringValue{"magenta"}};
162 StringInspectable inspectable(
163 std::move(values),
164 root.CreateString(kPropertyName, ""),
165 MakeContainerOfToStringConvertFunction(
166 {.prologue = "", .delimiter = "", .epilogue = ""}));
167
168 auto hierarchy = inspect::ReadFromVmo(inspector.DuplicateVmo());
169 ASSERT_TRUE(hierarchy.is_ok());
170 EXPECT_THAT(hierarchy.take_value(),
171 AllOf(NodeMatches(PropertyList(ElementsAre(
172 StringIs(kPropertyName, "fuchsiapurplemagenta"))))));
173 }
174
TEST(InspectableTest,InspectRealStringProperty)175 TEST(InspectableTest, InspectRealStringProperty) {
176 const auto kPropertyName = "test_property";
177
178 inspect::Inspector inspector;
179 auto& root = inspector.GetRoot();
180 StringInspectable inspectable(std::string("A"));
181 inspectable.AttachInspect(root, kPropertyName);
182
183 auto hierarchy = inspect::ReadFromVmo(inspector.DuplicateVmo());
184 ASSERT_TRUE(hierarchy.is_ok());
185 EXPECT_THAT(hierarchy.take_value(),
186 AllOf(NodeMatches(
187 PropertyList(ElementsAre(StringIs(kPropertyName, "A"))))));
188
189 inspectable.Set("B");
190
191 hierarchy = inspect::ReadFromVmo(inspector.DuplicateVmo());
192 ASSERT_TRUE(hierarchy.is_ok());
193 EXPECT_THAT(hierarchy.take_value(),
194 AllOf(NodeMatches(
195 PropertyList(ElementsAre(StringIs(kPropertyName, "B"))))));
196 }
197
198 } // namespace bt
199
200 #endif // NINSPECT
201