xref: /aosp_15_r20/external/pigweed/pw_persistent_ram/persistent_test.cc (revision 61c4878ac05f98d0ceed94b57d316916de578985)
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