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