1*61c4878aSAndroid Build Coastguard Worker // Copyright 2021 The Pigweed Authors
2*61c4878aSAndroid Build Coastguard Worker //
3*61c4878aSAndroid Build Coastguard Worker // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4*61c4878aSAndroid Build Coastguard Worker // use this file except in compliance with the License. You may obtain a copy of
5*61c4878aSAndroid Build Coastguard Worker // the License at
6*61c4878aSAndroid Build Coastguard Worker //
7*61c4878aSAndroid Build Coastguard Worker // https://www.apache.org/licenses/LICENSE-2.0
8*61c4878aSAndroid Build Coastguard Worker //
9*61c4878aSAndroid Build Coastguard Worker // Unless required by applicable law or agreed to in writing, software
10*61c4878aSAndroid Build Coastguard Worker // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11*61c4878aSAndroid Build Coastguard Worker // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12*61c4878aSAndroid Build Coastguard Worker // License for the specific language governing permissions and limitations under
13*61c4878aSAndroid Build Coastguard Worker // the License.
14*61c4878aSAndroid Build Coastguard Worker #include "pw_persistent_ram/persistent.h"
15*61c4878aSAndroid Build Coastguard Worker
16*61c4878aSAndroid Build Coastguard Worker #include <type_traits>
17*61c4878aSAndroid Build Coastguard Worker
18*61c4878aSAndroid Build Coastguard Worker #include "pw_random/xor_shift.h"
19*61c4878aSAndroid Build Coastguard Worker #include "pw_unit_test/framework.h"
20*61c4878aSAndroid Build Coastguard Worker
21*61c4878aSAndroid Build Coastguard Worker namespace pw::persistent_ram {
22*61c4878aSAndroid Build Coastguard Worker namespace {
23*61c4878aSAndroid Build Coastguard Worker
24*61c4878aSAndroid Build Coastguard Worker class PersistentTest : public ::testing::Test {
25*61c4878aSAndroid Build Coastguard Worker protected:
PersistentTest()26*61c4878aSAndroid Build Coastguard Worker PersistentTest() { ZeroPersistentMemory(); }
27*61c4878aSAndroid Build Coastguard Worker
28*61c4878aSAndroid Build Coastguard Worker // Emulate invalidation of persistent section(s).
ZeroPersistentMemory()29*61c4878aSAndroid Build Coastguard Worker void ZeroPersistentMemory() { memset(&buffer_, 0, sizeof(buffer_)); }
30*61c4878aSAndroid Build Coastguard Worker
31*61c4878aSAndroid Build Coastguard Worker // Allocate a chunk of aligned storage that can be independently controlled.
32*61c4878aSAndroid Build Coastguard Worker std::aligned_storage_t<sizeof(Persistent<uint32_t>),
33*61c4878aSAndroid Build Coastguard Worker alignof(Persistent<uint32_t>)>
34*61c4878aSAndroid Build Coastguard Worker buffer_;
35*61c4878aSAndroid Build Coastguard Worker };
36*61c4878aSAndroid Build Coastguard Worker
TEST_F(PersistentTest,DefaultConstructionAndDestruction)37*61c4878aSAndroid Build Coastguard Worker TEST_F(PersistentTest, DefaultConstructionAndDestruction) {
38*61c4878aSAndroid Build Coastguard Worker { // Emulate a boot where the persistent sections were invalidated.
39*61c4878aSAndroid Build Coastguard Worker // Although the fixture always does this, we do this an extra time to be
40*61c4878aSAndroid Build Coastguard Worker // 100% confident that an integrity check cannot be accidentally selected
41*61c4878aSAndroid Build Coastguard Worker // which results in reporting there is valid data when zero'd.
42*61c4878aSAndroid Build Coastguard Worker ZeroPersistentMemory();
43*61c4878aSAndroid Build Coastguard Worker auto& persistent = *(new (&buffer_) Persistent<uint32_t>());
44*61c4878aSAndroid Build Coastguard Worker EXPECT_FALSE(persistent.has_value());
45*61c4878aSAndroid Build Coastguard Worker
46*61c4878aSAndroid Build Coastguard Worker persistent = 42;
47*61c4878aSAndroid Build Coastguard Worker ASSERT_TRUE(persistent.has_value());
48*61c4878aSAndroid Build Coastguard Worker EXPECT_EQ(42u, persistent.value());
49*61c4878aSAndroid Build Coastguard Worker
50*61c4878aSAndroid Build Coastguard Worker persistent.~Persistent(); // Emulate shutdown / global destructors.
51*61c4878aSAndroid Build Coastguard Worker }
52*61c4878aSAndroid Build Coastguard Worker
53*61c4878aSAndroid Build Coastguard Worker { // Emulate a boot where persistent memory was kept as is.
54*61c4878aSAndroid Build Coastguard Worker auto& persistent = *(new (&buffer_) Persistent<uint32_t>());
55*61c4878aSAndroid Build Coastguard Worker ASSERT_TRUE(persistent.has_value());
56*61c4878aSAndroid Build Coastguard Worker EXPECT_EQ(42u, persistent.value());
57*61c4878aSAndroid Build Coastguard Worker }
58*61c4878aSAndroid Build Coastguard Worker }
59*61c4878aSAndroid Build Coastguard Worker
TEST_F(PersistentTest,Reset)60*61c4878aSAndroid Build Coastguard Worker TEST_F(PersistentTest, Reset) {
61*61c4878aSAndroid Build Coastguard Worker { // Emulate a boot where the persistent sections were invalidated.
62*61c4878aSAndroid Build Coastguard Worker auto& persistent = *(new (&buffer_) Persistent<uint32_t>());
63*61c4878aSAndroid Build Coastguard Worker persistent = 42u;
64*61c4878aSAndroid Build Coastguard Worker EXPECT_TRUE(persistent.has_value());
65*61c4878aSAndroid Build Coastguard Worker persistent.Invalidate();
66*61c4878aSAndroid Build Coastguard Worker
67*61c4878aSAndroid Build Coastguard Worker persistent.~Persistent(); // Emulate shutdown / global destructors.
68*61c4878aSAndroid Build Coastguard Worker }
69*61c4878aSAndroid Build Coastguard Worker
70*61c4878aSAndroid Build Coastguard Worker { // Emulate a boot where persistent memory was kept as is.
71*61c4878aSAndroid Build Coastguard Worker auto& persistent = *(new (&buffer_) Persistent<uint32_t>());
72*61c4878aSAndroid Build Coastguard Worker EXPECT_FALSE(persistent.has_value());
73*61c4878aSAndroid Build Coastguard Worker }
74*61c4878aSAndroid Build Coastguard Worker }
75*61c4878aSAndroid Build Coastguard Worker
TEST_F(PersistentTest,Emplace)76*61c4878aSAndroid Build Coastguard Worker TEST_F(PersistentTest, Emplace) {
77*61c4878aSAndroid Build Coastguard Worker auto& persistent = *(new (&buffer_) Persistent<uint32_t>());
78*61c4878aSAndroid Build Coastguard Worker EXPECT_FALSE(persistent.has_value());
79*61c4878aSAndroid Build Coastguard Worker
80*61c4878aSAndroid Build Coastguard Worker persistent.emplace(42u);
81*61c4878aSAndroid Build Coastguard Worker ASSERT_TRUE(persistent.has_value());
82*61c4878aSAndroid Build Coastguard Worker EXPECT_EQ(42u, persistent.value());
83*61c4878aSAndroid Build Coastguard Worker }
84*61c4878aSAndroid Build Coastguard Worker
85*61c4878aSAndroid Build Coastguard Worker class MutablePersistentTest : public ::testing::Test {
86*61c4878aSAndroid Build Coastguard Worker protected:
87*61c4878aSAndroid Build Coastguard Worker struct Coordinate {
88*61c4878aSAndroid Build Coastguard Worker int x;
89*61c4878aSAndroid Build Coastguard Worker int y;
90*61c4878aSAndroid Build Coastguard Worker int z;
91*61c4878aSAndroid Build Coastguard Worker };
MutablePersistentTest()92*61c4878aSAndroid Build Coastguard Worker MutablePersistentTest() { ZeroPersistentMemory(); }
93*61c4878aSAndroid Build Coastguard Worker
94*61c4878aSAndroid Build Coastguard Worker // Emulate invalidation of persistent section(s).
ZeroPersistentMemory()95*61c4878aSAndroid Build Coastguard Worker void ZeroPersistentMemory() { memset(&buffer_, 0, sizeof(buffer_)); }
RandomFillMemory()96*61c4878aSAndroid Build Coastguard Worker void RandomFillMemory() {
97*61c4878aSAndroid Build Coastguard Worker random::XorShiftStarRng64 rng(0x9ad75);
98*61c4878aSAndroid Build Coastguard Worker rng.Get(span<std::byte>(reinterpret_cast<std::byte*>(&buffer_),
99*61c4878aSAndroid Build Coastguard Worker sizeof(buffer_)));
100*61c4878aSAndroid Build Coastguard Worker }
101*61c4878aSAndroid Build Coastguard Worker
102*61c4878aSAndroid Build Coastguard Worker // Allocate a chunk of aligned storage that can be independently controlled.
103*61c4878aSAndroid Build Coastguard Worker std::aligned_storage_t<sizeof(Persistent<Coordinate>),
104*61c4878aSAndroid Build Coastguard Worker alignof(Persistent<Coordinate>)>
105*61c4878aSAndroid Build Coastguard Worker buffer_;
106*61c4878aSAndroid Build Coastguard Worker };
107*61c4878aSAndroid Build Coastguard Worker
TEST_F(MutablePersistentTest,DefaultConstructionAndDestruction)108*61c4878aSAndroid Build Coastguard Worker TEST_F(MutablePersistentTest, DefaultConstructionAndDestruction) {
109*61c4878aSAndroid Build Coastguard Worker {
110*61c4878aSAndroid Build Coastguard Worker // Emulate a boot where the persistent sections were invalidated.
111*61c4878aSAndroid Build Coastguard Worker // Although the fixture always does this, we do this an extra time to be
112*61c4878aSAndroid Build Coastguard Worker // 100% confident that an integrity check cannot be accidentally selected
113*61c4878aSAndroid Build Coastguard Worker // which results in reporting there is valid data when zero'd.
114*61c4878aSAndroid Build Coastguard Worker ZeroPersistentMemory();
115*61c4878aSAndroid Build Coastguard Worker auto& persistent = *(new (&buffer_) Persistent<Coordinate>());
116*61c4878aSAndroid Build Coastguard Worker EXPECT_FALSE(persistent.has_value());
117*61c4878aSAndroid Build Coastguard Worker
118*61c4878aSAndroid Build Coastguard Worker // Default construct of a Coordinate.
119*61c4878aSAndroid Build Coastguard Worker persistent.emplace(Coordinate({.x = 5, .y = 6, .z = 7}));
120*61c4878aSAndroid Build Coastguard Worker ASSERT_TRUE(persistent.has_value());
121*61c4878aSAndroid Build Coastguard Worker {
122*61c4878aSAndroid Build Coastguard Worker auto mutable_persistent = persistent.mutator();
123*61c4878aSAndroid Build Coastguard Worker mutable_persistent->x = 42;
124*61c4878aSAndroid Build Coastguard Worker (*mutable_persistent).y = 1337;
125*61c4878aSAndroid Build Coastguard Worker mutable_persistent->z = -99;
126*61c4878aSAndroid Build Coastguard Worker ASSERT_FALSE(persistent.has_value());
127*61c4878aSAndroid Build Coastguard Worker }
128*61c4878aSAndroid Build Coastguard Worker
129*61c4878aSAndroid Build Coastguard Worker EXPECT_EQ(1337, persistent.value().y);
130*61c4878aSAndroid Build Coastguard Worker EXPECT_EQ(-99, persistent.value().z);
131*61c4878aSAndroid Build Coastguard Worker
132*61c4878aSAndroid Build Coastguard Worker persistent.~Persistent(); // Emulate shutdown / global destructors.
133*61c4878aSAndroid Build Coastguard Worker }
134*61c4878aSAndroid Build Coastguard Worker
135*61c4878aSAndroid Build Coastguard Worker {
136*61c4878aSAndroid Build Coastguard Worker // Emulate a boot where persistent memory was kept as is.
137*61c4878aSAndroid Build Coastguard Worker auto& persistent = *(new (&buffer_) Persistent<Coordinate>());
138*61c4878aSAndroid Build Coastguard Worker ASSERT_TRUE(persistent.has_value());
139*61c4878aSAndroid Build Coastguard Worker EXPECT_EQ(42, persistent.value().x);
140*61c4878aSAndroid Build Coastguard Worker }
141*61c4878aSAndroid Build Coastguard Worker }
142*61c4878aSAndroid Build Coastguard Worker
TEST_F(MutablePersistentTest,ResetObject)143*61c4878aSAndroid Build Coastguard Worker TEST_F(MutablePersistentTest, ResetObject) {
144*61c4878aSAndroid Build Coastguard Worker {
145*61c4878aSAndroid Build Coastguard Worker // Emulate a boot where the persistent sections were lost and ended up in
146*61c4878aSAndroid Build Coastguard Worker // random data.
147*61c4878aSAndroid Build Coastguard Worker RandomFillMemory();
148*61c4878aSAndroid Build Coastguard Worker auto& persistent = *(new (&buffer_) Persistent<Coordinate>());
149*61c4878aSAndroid Build Coastguard Worker
150*61c4878aSAndroid Build Coastguard Worker // Default construct of a Coordinate.
151*61c4878aSAndroid Build Coastguard Worker ASSERT_FALSE(persistent.has_value());
152*61c4878aSAndroid Build Coastguard Worker {
153*61c4878aSAndroid Build Coastguard Worker auto mutable_persistent = persistent.mutator(GetterAction::kReset);
154*61c4878aSAndroid Build Coastguard Worker mutable_persistent->x = 42;
155*61c4878aSAndroid Build Coastguard Worker }
156*61c4878aSAndroid Build Coastguard Worker
157*61c4878aSAndroid Build Coastguard Worker EXPECT_EQ(42, persistent.value().x);
158*61c4878aSAndroid Build Coastguard Worker EXPECT_EQ(0, persistent.value().y);
159*61c4878aSAndroid Build Coastguard Worker EXPECT_EQ(0, persistent.value().z);
160*61c4878aSAndroid Build Coastguard Worker
161*61c4878aSAndroid Build Coastguard Worker persistent.~Persistent(); // Emulate shutdown / global destructors.
162*61c4878aSAndroid Build Coastguard Worker }
163*61c4878aSAndroid Build Coastguard Worker
164*61c4878aSAndroid Build Coastguard Worker {
165*61c4878aSAndroid Build Coastguard Worker // Emulate a boot where persistent memory was kept as is.
166*61c4878aSAndroid Build Coastguard Worker auto& persistent = *(new (&buffer_) Persistent<Coordinate>());
167*61c4878aSAndroid Build Coastguard Worker ASSERT_TRUE(persistent.has_value());
168*61c4878aSAndroid Build Coastguard Worker EXPECT_EQ(42, persistent.value().x);
169*61c4878aSAndroid Build Coastguard Worker EXPECT_EQ(0, persistent.value().y);
170*61c4878aSAndroid Build Coastguard Worker }
171*61c4878aSAndroid Build Coastguard Worker }
172*61c4878aSAndroid Build Coastguard Worker
173*61c4878aSAndroid Build Coastguard Worker } // namespace
174*61c4878aSAndroid Build Coastguard Worker } // namespace pw::persistent_ram
175