1 /*
2 * Copyright (c) 2022 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://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,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include <cstdio>
17 #include <thread>
18 #include <memory> /* unique_ptr */
19
20 #include "gtest/gtest.h"
21 #include "purgeable_mem.h"
22
23 namespace OHOS {
24 namespace PurgeableMem {
25 using namespace testing;
26 using namespace testing::ext;
27
28 static constexpr int PRINT_INTERVAL_SECONDS = 1;
29 static constexpr int RECLAIM_INTERVAL_SECONDS = 1;
30 static constexpr int MODIFY_INTERVAL_SECONDS = 2;
31 void LoopPrintAlphabet(PurgeableMem *pdata, unsigned int loopCount);
32 bool ReclaimPurgeable(void);
33 void LoopReclaimPurgeable(unsigned int loopCount);
34 void ModifyPurgMemByBuilder(PurgeableMem *pdata, std::unique_ptr<PurgeableMemBuilder> mod);
35
36 class TestDataBuilder : public PurgeableMemBuilder {
37 public:
TestDataBuilder(char start,char end)38 TestDataBuilder(char start, char end)
39 {
40 this->start = start;
41 this->end = end;
42 }
43
Build(void * data,size_t size)44 bool Build(void *data, size_t size)
45 {
46 char *str = (char *)data;
47 size_t len = 0;
48 for (char ch = start; ch <= end && len < size; ch++) {
49 str[len++] = ch;
50 }
51 str[len] = 0;
52 std::cout << "rebuild addr("<< (unsigned long long)str <<") " <<
53 start << "~" << end << ", data=[" << str << "]" << std::endl;
54 return true;
55 }
56
~TestDataBuilder()57 ~TestDataBuilder()
58 {
59 std::cout << "~TestDataBuilder" << std::endl;
60 }
61
62 private:
63 char start, end;
64 };
65
66 class TestDataModifier : public PurgeableMemBuilder {
67 public:
TestDataModifier(char from,char to)68 TestDataModifier(char from, char to)
69 {
70 this->from = from;
71 this->to = to;
72 }
73
Build(void * data,size_t size)74 bool Build(void *data, size_t size)
75 {
76 char *str = (char *)data;
77 for (size_t i = 0; str[i] && i < size; i++) {
78 if (str[i] == from) {
79 str[i] = to;
80 }
81 }
82 std::cout << "modify addr("<< (unsigned long long)str <<") " <<
83 from << "->" << to << ", data=[" << str << "]" << std::endl;
84 return true;
85 }
86
~TestDataModifier()87 ~TestDataModifier()
88 {
89 std::cout << "~TestDataModifier" << std::endl;
90 }
91
92 private:
93 char from, to;
94 };
95
96 class PurgeableCppTest : public testing::Test {
97 public:
98 static void SetUpTestCase();
99 static void TearDownTestCase();
100 void SetUp();
101 void TearDown();
102 };
103
SetUpTestCase()104 void PurgeableCppTest::SetUpTestCase()
105 {
106 }
107
TearDownTestCase()108 void PurgeableCppTest::TearDownTestCase()
109 {
110 }
111
SetUp()112 void PurgeableCppTest::SetUp()
113 {
114 }
115
TearDown()116 void PurgeableCppTest::TearDown()
117 {
118 }
119 /*
120 * @tc.number MULTI_OBJ_CREAT_Test_0100
121 * @tc.name Test Creat purgeable
122 * @tc.desc [C- SOFTWARE -0200]
123 */
124 HWTEST_F(PurgeableCppTest, MultiObjCreateTest, TestSize.Level1)
125 {
126 const char alphabetFinal[] = "BBCDEFGHIJKLMNOPQRSTUVWXYZ\0";
127 std::unique_ptr<PurgeableMemBuilder> builder = std::make_unique<TestDataBuilder>('A', 'Z');
128 std::unique_ptr<PurgeableMemBuilder> builder2 = std::make_unique<TestDataBuilder>('A', 'Z');
129 PurgeableMem pobj1(27, std::move(builder));
130 std::unique_ptr<PurgeableMemBuilder> mod = std::make_unique<TestDataModifier>('A', 'B');
131 std::unique_ptr<PurgeableMemBuilder> mod2 = std::make_unique<TestDataModifier>('A', 'B');
132
133 LoopPrintAlphabet(&pobj1, 1);
134 ModifyPurgMemByBuilder(&pobj1, std::move(mod));
135 LoopPrintAlphabet(&pobj1, 1);
136 LoopReclaimPurgeable(1);
137
138 PurgeableMem pobj2(27, std::move(builder2));
139 LoopPrintAlphabet(&pobj2, 1);
140 ModifyPurgMemByBuilder(&pobj2, std::move(mod2));
141 LoopPrintAlphabet(&pobj2, 1);
142
143 if (pobj1.BeginRead()) {
144 ASSERT_STREQ(alphabetFinal, (char *)(pobj1.GetContent()));
145 pobj1.EndRead();
146 } else {
147 std::cout << __func__ << ": ERROR! BeginRead failed." << std::endl;
148 }
149
150 if (pobj2.BeginRead()) {
151 ASSERT_STREQ(alphabetFinal, (char *)(pobj2.GetContent()));
152 pobj2.EndRead();
153 } else {
154 std::cout << __func__ << ": ERROR! BeginRead failed." << std::endl;
155 }
156 }
157 /*
158 * @tc.number PURGEABLE_READ_Test_0100
159 * @tc.name Test READ purgeable
160 * @tc.desc [C- SOFTWARE -0200]
161 */
162 HWTEST_F(PurgeableCppTest, ReadTest, TestSize.Level1)
163 {
164 const char alphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ\0";
165 std::unique_ptr<PurgeableMemBuilder> builder = std::make_unique<TestDataBuilder>('A', 'Z');
166 PurgeableMem *pobj = new PurgeableMem(27, std::move(builder));
167
168 std::thread reclaimThread(LoopReclaimPurgeable, (unsigned int)(-1));
169 pthread_t reclaimPid = reclaimThread.native_handle();
170 reclaimThread.detach();
171
172 unsigned int loopCount = 3;
173 /* loop read content */
174 for (unsigned int i = 0; i < loopCount; i++) {
175 if (!pobj->BeginRead()) {
176 std::cout << __func__ << ": ERROR! BeginRead failed." << std::endl;
177 continue;
178 }
179 ASSERT_STREQ(alphabet, (char *)(pobj->GetContent()));
180 pobj->EndRead();
181 }
182
183 pthread_cancel(reclaimPid); /* destroy reclaimThread */
184 delete pobj;
185 pobj = nullptr;
186 }
187 /*
188 * @tc.number PURGEABLE_WRITE_Test_0100
189 * @tc.name Test WRITE purgeable
190 * @tc.desc [C- SOFTWARE -0200]
191 */
192 HWTEST_F(PurgeableCppTest, WriteTest, TestSize.Level1)
193 {
194 const char alphabet[] = "CCCDEFGHIJKLMNOPQRSTUVWXYZ\0";
195 std::unique_ptr<PurgeableMemBuilder> builder = std::make_unique<TestDataBuilder>('A', 'Z');
196 PurgeableMem *pobj = new PurgeableMem(27, std::move(builder));
197 std::thread reclaimThread(LoopReclaimPurgeable, (unsigned int)(-1));
198 pthread_t reclaimPid = reclaimThread.native_handle();
199 reclaimThread.detach();
200
201 std::unique_ptr<PurgeableMemBuilder> modA2B = std::make_unique<TestDataModifier>('A', 'B');
202 std::unique_ptr<PurgeableMemBuilder> modB2C = std::make_unique<TestDataModifier>('B', 'C');
203 ModifyPurgMemByBuilder(pobj, std::move(modA2B));
204 ModifyPurgMemByBuilder(pobj, std::move(modB2C));
205
206 if (pobj->BeginRead()) {
207 ASSERT_STREQ(alphabet, (char *)(pobj->GetContent()));
208 pobj->EndRead();
209 } else {
210 std::cout << __func__ << ": ERROR! BeginRead failed." << std::endl;
211 }
212
213 pthread_cancel(reclaimPid); /* destroy reclaimThread */
214 delete pobj;
215 pobj = nullptr;
216 LoopReclaimPurgeable(3);
217 }
218 /*
219 * @tc.number PURGEABLE_READ_Test_0100
220 * @tc.name Test READWRITE purgeable
221 * @tc.desc [C- SOFTWARE -0200]
222 */
223 HWTEST_F(PurgeableCppTest, ReadWriteTest, TestSize.Level1)
224 {
225 const char alphabet[] = "DDDDEFGHIJKLMNOPQRSTUVWXYZ\0";
226 std::unique_ptr<PurgeableMemBuilder> builder = std::make_unique<TestDataBuilder>('A', 'Z');
227 PurgeableMem *pobj = new PurgeableMem(27, std::move(builder));
228 /* loop reclaim thread */
229 std::thread reclaimThread(LoopReclaimPurgeable, (unsigned int)(-1));
230 pthread_t reclaimPid = reclaimThread.native_handle();
231 reclaimThread.detach();
232 /* loop read thread */
233 std::thread readThread(LoopPrintAlphabet, pobj, (unsigned int)(-1));
234 pthread_t readPid = readThread.native_handle();
235 readThread.detach();
236
237 std::unique_ptr<PurgeableMemBuilder> modA2B = std::make_unique<TestDataModifier>('A', 'B');
238 std::unique_ptr<PurgeableMemBuilder> modB2C = std::make_unique<TestDataModifier>('B', 'C');
239 std::unique_ptr<PurgeableMemBuilder> modC2D = std::make_unique<TestDataModifier>('C', 'D');
240 ModifyPurgMemByBuilder(pobj, std::move(modA2B));
241 ModifyPurgMemByBuilder(pobj, std::move(modB2C));
242 ModifyPurgMemByBuilder(pobj, std::move(modC2D));
243
244 if (pobj->BeginRead()) {
245 ASSERT_STREQ(alphabet, (char *)(pobj->GetContent()));
246 pobj->EndRead();
247 } else {
248 std::cout << __func__ << ": ERROR! BeginRead failed." << std::endl;
249 }
250
251 pthread_cancel(readPid); /* destroy readThread */
252 pthread_cancel(reclaimPid); /* destroy reclaimThread */
253 std::this_thread::sleep_for(std::chrono::seconds(2 * PRINT_INTERVAL_SECONDS));
254 delete pobj;
255 pobj = nullptr;
256 }
257
LoopPrintAlphabet(PurgeableMem * pdata,unsigned int loopCount)258 void LoopPrintAlphabet(PurgeableMem *pdata, unsigned int loopCount)
259 {
260 std::cout << "inter " << __func__ << std::endl;
261 for (unsigned int i = 0; i < loopCount; i++) {
262 if (!pdata->BeginRead()) {
263 std::cout << __func__ << ": " << i << ". ERROR! BeginRead failed." << std::endl;
264 break;
265 }
266 std::cout << __func__ << ": " << i << ". data=[" <<
267 (char *)(pdata->GetContent()) << "]" << std::endl;
268 pdata->EndRead();
269 std::this_thread::sleep_for(std::chrono::seconds(PRINT_INTERVAL_SECONDS));
270 }
271 std::cout << "quit " << __func__ << std::endl;
272 }
273
ReclaimPurgeable(void)274 bool ReclaimPurgeable(void)
275 {
276 FILE *f = fopen("/proc/sys/kernel/purgeable", "w");
277 if (!f) {
278 std::cout << __func__ << ": open file failed" << std::endl;
279 return false;
280 }
281 bool succ = true;
282 if (fputs("1", f) == EOF) {
283 succ = false;
284 }
285
286 if (fclose(f) == EOF) {
287 std::cout << __func__ << ": close file failed" << std::endl;
288 }
289
290 return succ;
291 }
292
LoopReclaimPurgeable(unsigned int loopCount)293 void LoopReclaimPurgeable(unsigned int loopCount)
294 {
295 bool ret = false;
296 std::cout << "inter " << __func__ << std::endl;
297 for (unsigned int i = 0; i < loopCount; i++) {
298 ret = ReclaimPurgeable();
299 std::cout << __func__ << ": " << i << ". Reclaim result=" << (ret ? "succ" : "fail") << std::endl;
300 std::this_thread::sleep_for(std::chrono::seconds(RECLAIM_INTERVAL_SECONDS)); /* wait reclaim finish */
301 }
302 std::cout << "quit " << __func__ << std::endl;
303 }
304
ModifyPurgMemByBuilder(PurgeableMem * pdata,std::unique_ptr<PurgeableMemBuilder> mod)305 void ModifyPurgMemByBuilder(PurgeableMem *pdata, std::unique_ptr<PurgeableMemBuilder> mod)
306 {
307 if (!pdata->BeginWrite()) {
308 std::cout << __func__ << ": ERROR! BeginWrite failed." << std::endl;
309 return;
310 }
311 std::this_thread::sleep_for(std::chrono::seconds(MODIFY_INTERVAL_SECONDS));
312 std::cout << __func__ << " before mod data=[" << (char *)(pdata->GetContent()) << "]" << std::endl;
313 pdata->ModifyContentByBuilder(std::move(mod));
314 std::cout<< __func__ << " after mod data=[" << (char *)(pdata->GetContent()) << "]" << std::endl;
315
316 std::cout << __func__ << " data=[" << (char *)(pdata->GetContent()) << "]" << std::endl;
317 pdata->EndWrite();
318 }
319 } /* namespace PurgeableMem */
320 } /* namespace OHOS */
321