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/windowed_inspect_numeric_property.h"
16
17 #include <pw_async/fake_dispatcher_fixture.h>
18
19 #include "pw_bluetooth_sapphire/internal/host/testing/inspect.h"
20
21 #ifndef NINSPECT
22
23 namespace bt {
24
25 namespace {
26
27 using namespace ::inspect::testing;
28
29 template <typename T>
30 class TestProperty {
31 public:
32 using ValueCallback = fit::function<void(const T& value)>;
33 TestProperty() = default;
TestProperty(T value,ValueCallback cb)34 TestProperty(T value, ValueCallback cb)
35 : value_(value), value_cb_(std::move(cb)) {}
36
Add(const T & value)37 void Add(const T& value) {
38 value_ += value;
39 if (value_cb_) {
40 value_cb_(value_);
41 }
42 }
43
Subtract(const T & value)44 void Subtract(const T& value) {
45 value_ -= value;
46 if (value_cb_) {
47 value_cb_(value_);
48 }
49 }
50
51 private:
52 T value_;
53 fit::function<void(const T& value)> value_cb_;
54 };
55
56 using WindowedProperty = WindowedInspectNumericProperty<TestProperty<int>, int>;
57 using WindowedInspectNumericPropertyTest =
58 pw::async::test::FakeDispatcherFixture;
59
TEST_F(WindowedInspectNumericPropertyTest,AddTwoValues)60 TEST_F(WindowedInspectNumericPropertyTest, AddTwoValues) {
61 constexpr pw::chrono::SystemClock::duration kExpiryDuration =
62 std::chrono::minutes(3);
63 WindowedProperty windowed_prop(dispatcher(), kExpiryDuration);
64 int value = 0;
65 auto value_cb = [&](auto val) { value = val; };
66 windowed_prop.SetProperty(TestProperty<int>(0, value_cb));
67
68 windowed_prop.Add(1);
69 EXPECT_EQ(value, 1);
70 RunFor(std::chrono::minutes(1));
71 EXPECT_EQ(value, 1);
72
73 windowed_prop.Add(2);
74 EXPECT_EQ(value, 3);
75 RunFor(std::chrono::minutes(1));
76 EXPECT_EQ(value, 3);
77
78 // Let first value expire.
79 RunFor(std::chrono::minutes(1));
80 EXPECT_EQ(value, 2);
81 // Let second value expire.
82 RunFor(std::chrono::minutes(1));
83 EXPECT_EQ(value, 0);
84
85 // Ensure timer doesn't fire again.
86 RunFor(kExpiryDuration);
87 EXPECT_EQ(value, 0);
88 }
89
TEST_F(WindowedInspectNumericPropertyTest,AddTwoValuesAtSameTime)90 TEST_F(WindowedInspectNumericPropertyTest, AddTwoValuesAtSameTime) {
91 constexpr pw::chrono::SystemClock::duration kExpiryDuration =
92 std::chrono::minutes(3);
93 WindowedProperty windowed_prop(dispatcher(), kExpiryDuration);
94 int value = 0;
95 auto value_cb = [&](auto val) { value = val; };
96 windowed_prop.SetProperty(TestProperty<int>(0, value_cb));
97
98 windowed_prop.Add(1);
99 windowed_prop.Add(2);
100 EXPECT_EQ(value, 3);
101 RunFor(std::chrono::minutes(1));
102 EXPECT_EQ(value, 3);
103 RunFor(std::chrono::minutes(2));
104 EXPECT_EQ(value, 0);
105
106 // Ensure timer doesn't fire again.
107 RunFor(kExpiryDuration);
108 EXPECT_EQ(value, 0);
109 }
110
TEST_F(WindowedInspectNumericPropertyTest,AddValueThenExpireThenAddValue)111 TEST_F(WindowedInspectNumericPropertyTest, AddValueThenExpireThenAddValue) {
112 constexpr pw::chrono::SystemClock::duration kExpiryDuration =
113 std::chrono::minutes(3);
114 WindowedProperty windowed_prop(dispatcher(), kExpiryDuration);
115 int value = 0;
116 auto value_cb = [&](auto val) { value = val; };
117 windowed_prop.SetProperty(TestProperty<int>(0, value_cb));
118
119 windowed_prop.Add(1);
120 EXPECT_EQ(value, 1);
121 RunFor(kExpiryDuration);
122 EXPECT_EQ(value, 0);
123
124 windowed_prop.Add(2);
125 EXPECT_EQ(value, 2);
126 RunFor(kExpiryDuration);
127 EXPECT_EQ(value, 0);
128
129 // Ensure timer doesn't fire again.
130 RunFor(kExpiryDuration);
131 EXPECT_EQ(value, 0);
132 }
133
TEST_F(WindowedInspectNumericPropertyTest,AddTwoValuesWithinResolutionIntervalExpiresBothSimultaneously)134 TEST_F(WindowedInspectNumericPropertyTest,
135 AddTwoValuesWithinResolutionIntervalExpiresBothSimultaneously) {
136 constexpr pw::chrono::SystemClock::duration kExpiryDuration =
137 std::chrono::minutes(3);
138 constexpr pw::chrono::SystemClock::duration kResolution =
139 std::chrono::seconds(3);
140 WindowedProperty windowed_prop(dispatcher(), kExpiryDuration, kResolution);
141 int value = 0;
142 auto value_cb = [&](auto val) { value = val; };
143 windowed_prop.SetProperty(TestProperty<int>(0, value_cb));
144
145 // First two values are within kResolution of each other in time.
146 windowed_prop.Add(1);
147 constexpr pw::chrono::SystemClock::duration kTinyDuration =
148 std::chrono::milliseconds(1);
149 RunFor(kTinyDuration);
150 windowed_prop.Add(1);
151 EXPECT_EQ(value, 2);
152
153 // Third value is spaced kResolution apart from the first value.
154 RunFor(kResolution - kTinyDuration);
155 windowed_prop.Add(1);
156 EXPECT_EQ(value, 3);
157
158 // Let first value expire.
159 RunFor(kExpiryDuration - kResolution);
160
161 // First and second values should have expired because they were merged.
162 EXPECT_EQ(value, 1);
163
164 // Let third value expire.
165 RunFor(kResolution);
166 EXPECT_EQ(value, 0);
167 }
168
TEST_F(WindowedInspectNumericPropertyTest,SetPropertyClearsValueAndTimer)169 TEST_F(WindowedInspectNumericPropertyTest, SetPropertyClearsValueAndTimer) {
170 constexpr pw::chrono::SystemClock::duration kExpiryDuration =
171 std::chrono::minutes(3);
172 WindowedProperty windowed_prop(dispatcher(), kExpiryDuration);
173 int value_0 = 0;
174 auto value_cb_0 = [&](auto val) { value_0 = val; };
175 windowed_prop.SetProperty(TestProperty<int>(0, value_cb_0));
176
177 windowed_prop.Add(1);
178 EXPECT_EQ(value_0, 1);
179 int value_1 = 0;
180 auto value_cb_1 = [&](auto val) { value_1 = val; };
181 windowed_prop.SetProperty(TestProperty<int>(0, value_cb_1));
182 // Ensure timer doesn't fire.
183 RunFor(kExpiryDuration);
184 EXPECT_EQ(value_0, 1);
185 EXPECT_EQ(value_1, 0);
186
187 // Ensure values can be added to new property.
188 windowed_prop.Add(3);
189 EXPECT_EQ(value_0, 1);
190 EXPECT_EQ(value_1, 3);
191 RunFor(kExpiryDuration);
192 EXPECT_EQ(value_0, 1);
193 EXPECT_EQ(value_1, 0);
194 }
195
TEST_F(WindowedInspectNumericPropertyTest,AttachInspectRealIntProperty)196 TEST_F(WindowedInspectNumericPropertyTest, AttachInspectRealIntProperty) {
197 ::inspect::Inspector inspector;
198 auto& root = inspector.GetRoot();
199
200 constexpr pw::chrono::SystemClock::duration kExpiryDuration =
201 std::chrono::minutes(3);
202 WindowedInspectIntProperty windowed_property(dispatcher(), kExpiryDuration);
203 windowed_property.AttachInspect(root, "windowed");
204
205 auto hierarchy = ::inspect::ReadFromVmo(inspector.DuplicateVmo());
206 ASSERT_TRUE(hierarchy.is_ok());
207 EXPECT_THAT(
208 hierarchy.take_value(),
209 AllOf(NodeMatches(PropertyList(ElementsAre(IntIs("windowed", 0))))));
210
211 windowed_property.Add(7);
212
213 hierarchy = ::inspect::ReadFromVmo(inspector.DuplicateVmo());
214 ASSERT_TRUE(hierarchy.is_ok());
215 EXPECT_THAT(
216 hierarchy.take_value(),
217 AllOf(NodeMatches(PropertyList(ElementsAre(IntIs("windowed", 7))))));
218 }
219
220 } // namespace
221
222 } // namespace bt
223
224 #endif // NINSPECT
225