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 <sys/mman.h>
17#include <cstdio>
18#include <thread>
19#include <memory> /* unique_ptr */
20#include <cstring>
21#include "gtest/gtest.h"
22#include "pm_util.h"
23
24#define private public
25#define protected public
26#include "purgeable_mem.h"
27#undef private
28#undef protected
29
30namespace OHOS {
31namespace PurgeableMem {
32using namespace testing;
33using namespace testing::ext;
34
35static constexpr int PRINT_INTERVAL_SECONDS = 1;
36static constexpr int RECLAIM_INTERVAL_SECONDS = 1;
37static constexpr int MODIFY_INTERVAL_SECONDS = 2;
38void LoopPrintAlphabet(PurgeableMem *pdata, unsigned int loopCount);
39bool ReclaimPurgeable(void);
40void LoopReclaimPurgeable(unsigned int loopCount);
41void ModifyPurgMemByBuilder(PurgeableMem *pdata, std::unique_ptr<PurgeableMemBuilder> mod);
42
43class TestDataBuilder : public PurgeableMemBuilder {
44public:
45    TestDataBuilder(char start, char end)
46    {
47        this->start_ = start;
48        this->end_ = end;
49    }
50
51    bool Build(void *data, size_t size)
52    {
53        if (size <= 0) {
54            return true;
55        }
56        char *str = static_cast<char *>(data);
57        size_t len = 0;
58        for (char ch = start_; ch <= end_ && len < size; ch++) {
59            str[len++] = ch;
60        }
61        str[size - 1] = 0;
62        std::cout << "rebuild addr("<< (unsigned long long)str <<") " <<
63            start_ << "~" << end_ << ", data=[" << str << "]" << std::endl;
64        return true;
65    }
66
67    ~TestDataBuilder()
68    {
69        std::cout << "~TestDataBuilder" << std::endl;
70    }
71
72private:
73    char start_;
74    char end_;
75};
76
77class TestDataModifier : public PurgeableMemBuilder {
78public:
79    TestDataModifier(char from, char to)
80    {
81        this->from_ = from;
82        this->to_ = to;
83    }
84
85    bool Build(void *data, size_t size)
86    {
87        char *str = static_cast<char *>(data);
88        for (size_t i = 0; i < size && str[i]; i++) {
89            if (str[i] == from_) {
90                str[i] = to_;
91            }
92        }
93        return true;
94    }
95
96    ~TestDataModifier()
97    {
98        std::cout << "~TestDataModifier" << std::endl;
99    }
100
101private:
102    char from_;
103    char to_;
104};
105
106class TestBigDataBuilder : public PurgeableMemBuilder {
107public:
108    explicit TestBigDataBuilder(char target)
109    {
110        this->target_ = target;
111    }
112
113    bool Build(void *data, size_t size)
114    {
115        if (size <= 0) {
116            return true;
117        }
118        char *str = static_cast<char *>(data);
119        size_t len = 0;
120        for (char ch = target_; len < size;) {
121            str[len++] = ch;
122        }
123        str[size - 1] = 0;
124        return true;
125    }
126
127    ~TestBigDataBuilder()
128    {
129        std::cout << "~TestBigDataBuilder" << std::endl;
130    }
131
132private:
133    char target_;
134};
135
136class PurgeableCppTest : public testing::Test {
137public:
138    static void SetUpTestCase();
139    static void TearDownTestCase();
140    void SetUp();
141    void TearDown();
142};
143
144void PurgeableCppTest::SetUpTestCase()
145{
146}
147
148void PurgeableCppTest::TearDownTestCase()
149{
150}
151
152void PurgeableCppTest::SetUp()
153{
154}
155
156void PurgeableCppTest::TearDown()
157{
158}
159
160HWTEST_F(PurgeableCppTest, MultiObjCreateTest, TestSize.Level1)
161{
162    const char alphabetFinal[] = "BBCDEFGHIJKLMNOPQRSTUVWXYZ\0";
163    std::unique_ptr<PurgeableMemBuilder> builder1 = std::make_unique<TestDataBuilder>('A', 'Z');
164    std::unique_ptr<PurgeableMemBuilder> builder2 = std::make_unique<TestDataBuilder>('A', 'Z');
165    std::unique_ptr<PurgeableMemBuilder> mod1 = std::make_unique<TestDataModifier>('A', 'B');
166    std::unique_ptr<PurgeableMemBuilder> mod2 = std::make_unique<TestDataModifier>('A', 'B');
167
168    PurgeableMem pobj1(27, std::move(builder1));
169    LoopPrintAlphabet(&pobj1, 1);
170    ModifyPurgMemByBuilder(&pobj1, std::move(mod1));
171    LoopPrintAlphabet(&pobj1, 1);
172    LoopReclaimPurgeable(1);
173
174    PurgeableMem pobj2(27, std::move(builder2));
175    LoopPrintAlphabet(&pobj2, 1);
176    ModifyPurgMemByBuilder(&pobj2, std::move(mod2));
177    LoopPrintAlphabet(&pobj2, 1);
178    LoopReclaimPurgeable(1);
179
180    int ret1 = 1;
181    int ret2 = 1;
182    int times1 = 0;
183    int times2 = 0;
184    while (times1++ < 10) {
185        if (pobj1.BeginRead()) {
186            ret1 = strncmp(alphabetFinal, static_cast<char *>(pobj1.GetContent()), 26);
187            pobj1.EndRead();
188            break;
189        } else {
190            std::cout << __func__ << ": ERROR! BeginRead failed." << std::endl;
191        }
192    }
193
194    while (times2++ < 10) {
195        if (pobj2.BeginRead()) {
196            ret2 = strncmp(alphabetFinal, static_cast<char *>(pobj2.GetContent()), 26);
197            pobj2.EndRead();
198            break;
199        } else {
200            std::cout << __func__ << ": ERROR! BeginRead failed." << std::endl;
201        }
202    }
203
204    EXPECT_EQ(ret1, 0);
205    EXPECT_EQ(ret2, 0);
206}
207
208HWTEST_F(PurgeableCppTest, ReadTest, TestSize.Level1)
209{
210    const char alphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ\0";
211    std::unique_ptr<PurgeableMemBuilder> builder = std::make_unique<TestDataBuilder>('A', 'Z');
212    PurgeableMem *pobj = new PurgeableMem(27, std::move(builder));
213    LoopReclaimPurgeable(1);
214
215    int times = 0;
216    int ret = 1;
217    while (times++ < 10) {
218        if (pobj->BeginRead()) {
219            ret = strncmp(alphabet, static_cast<char *>(pobj->GetContent()), 26);
220            pobj->EndRead();
221            break;
222        } else {
223            std::cout << __func__ << ": ERROR! BeginRead failed." << std::endl;
224        }
225    }
226    delete pobj;
227    pobj = nullptr;
228    EXPECT_EQ(ret, 0);
229}
230
231HWTEST_F(PurgeableCppTest, WriteTest, TestSize.Level1)
232{
233    const char alphabet[] = "CCCDEFGHIJKLMNOPQRSTUVWXYZ\0";
234    std::unique_ptr<PurgeableMemBuilder> builder = std::make_unique<TestDataBuilder>('A', 'Z');
235    PurgeableMem *pobj = new PurgeableMem(27, std::move(builder));
236    LoopReclaimPurgeable(1);
237
238    std::unique_ptr<PurgeableMemBuilder> modA2B = std::make_unique<TestDataModifier>('A', 'B');
239    std::unique_ptr<PurgeableMemBuilder> modB2C = std::make_unique<TestDataModifier>('B', 'C');
240    ModifyPurgMemByBuilder(pobj, std::move(modA2B));
241    ModifyPurgMemByBuilder(pobj, std::move(modB2C));
242
243    int times = 0;
244    int ret = 1;
245    while (times++ < 10) {
246        if (pobj->BeginRead()) {
247            ret = strncmp(alphabet, static_cast<char *>(pobj->GetContent()), 26);
248            pobj->EndRead();
249            break;
250        } else {
251            std::cout << __func__ << ": ERROR! BeginRead failed." << std::endl;
252        }
253    }
254    delete pobj;
255    pobj = nullptr;
256    EXPECT_EQ(ret, 0);
257}
258
259HWTEST_F(PurgeableCppTest, ReadWriteTest, TestSize.Level1)
260{
261    const char alphabet[] = "DDDDEFGHIJKLMNOPQRSTUVWXYZ\0";
262    std::unique_ptr<PurgeableMemBuilder> builder = std::make_unique<TestDataBuilder>('A', 'Z');
263    PurgeableMem *pobj = new PurgeableMem(27, std::move(builder));
264
265    LoopReclaimPurgeable(1);
266    LoopPrintAlphabet(pobj, 1);
267
268    std::unique_ptr<PurgeableMemBuilder> modA2B = std::make_unique<TestDataModifier>('A', 'B');
269    std::unique_ptr<PurgeableMemBuilder> modB2C = std::make_unique<TestDataModifier>('B', 'C');
270    std::unique_ptr<PurgeableMemBuilder> modC2D = std::make_unique<TestDataModifier>('C', 'D');
271    ModifyPurgMemByBuilder(pobj, std::move(modA2B));
272    ModifyPurgMemByBuilder(pobj, std::move(modB2C));
273    ModifyPurgMemByBuilder(pobj, std::move(modC2D));
274
275    int times = 0;
276    int ret = 1;
277    while (times++ < 10) {
278        if (pobj->BeginRead()) {
279            ret = strncmp(alphabet, static_cast<char *>(pobj->GetContent()), 26);
280            pobj->EndRead();
281            break;
282        } else {
283            std::cout << __func__ << ": ERROR! BeginRead failed." << std::endl;
284        }
285    }
286    delete pobj;
287    pobj = nullptr;
288    EXPECT_EQ(ret, 0);
289}
290
291HWTEST_F(PurgeableCppTest, MutiPageReadTest, TestSize.Level1)
292{
293    char alphabet[4098];
294    size_t len = 0;
295    for (char ch = 'A'; len < 4098;) {
296        alphabet[len++] = ch;
297    }
298    alphabet[4097] = 0;
299    std::unique_ptr<PurgeableMemBuilder> builder = std::make_unique<TestBigDataBuilder>('A');
300    PurgeableMem *pobj = new PurgeableMem(4098, std::move(builder));
301
302    LoopReclaimPurgeable(1);
303
304    int times = 0;
305    int ret = 1;
306    while (times++ < 10) {
307        if (pobj->BeginRead()) {
308            ret = strncmp(alphabet, static_cast<char *>(pobj->GetContent()), 4097);
309            pobj->EndRead();
310            break;
311        } else {
312            std::cout << __func__ << ": ERROR! BeginRead failed." << std::endl;
313        }
314    }
315    delete pobj;
316    pobj = nullptr;
317    EXPECT_EQ(ret, 0);
318}
319
320HWTEST_F(PurgeableCppTest, MutiPageWriteTest, TestSize.Level1)
321{
322    char alphabet[4098];
323    size_t len = 0;
324    for (char ch = 'C'; len < 4098;) {
325        alphabet[len++] = ch;
326    }
327    alphabet[4097] = 0;
328    std::unique_ptr<PurgeableMemBuilder> builder = std::make_unique<TestBigDataBuilder>('A');
329    PurgeableMem *pobj = new PurgeableMem(4098, std::move(builder));
330
331    LoopReclaimPurgeable(1);
332
333    std::unique_ptr<PurgeableMemBuilder> modA2B = std::make_unique<TestDataModifier>('A', 'B');
334    std::unique_ptr<PurgeableMemBuilder> modB2C = std::make_unique<TestDataModifier>('B', 'C');
335    ModifyPurgMemByBuilder(pobj, std::move(modA2B));
336    ModifyPurgMemByBuilder(pobj, std::move(modB2C));
337
338    int times = 0;
339    int ret = 1;
340    while (times++ < 10) {
341        if (pobj->BeginRead()) {
342            ret = strncmp(alphabet, static_cast<char *>(pobj->GetContent()), 4097);
343            pobj->EndRead();
344            break;
345        } else {
346            std::cout << __func__ << ": ERROR! BeginRead failed." << std::endl;
347        }
348    }
349    delete pobj;
350    pobj = nullptr;
351    EXPECT_EQ(ret, 0);
352}
353
354HWTEST_F(PurgeableCppTest, MutiPageReadWriteTest, TestSize.Level1)
355{
356    char alphabet[4098];
357    size_t len = 0;
358    for (char ch = 'D'; len < 4098;) {
359        alphabet[len++] = ch;
360    }
361    alphabet[4097] = 0;
362    std::unique_ptr<PurgeableMemBuilder> builder = std::make_unique<TestBigDataBuilder>('A');
363    PurgeableMem *pobj = new PurgeableMem(4098, std::move(builder));
364    LoopReclaimPurgeable(1);
365    LoopPrintAlphabet(pobj, 1);
366
367    std::unique_ptr<PurgeableMemBuilder> modA2B = std::make_unique<TestDataModifier>('A', 'B');
368    std::unique_ptr<PurgeableMemBuilder> modB2C = std::make_unique<TestDataModifier>('B', 'C');
369    std::unique_ptr<PurgeableMemBuilder> modC2D = std::make_unique<TestDataModifier>('C', 'D');
370    ModifyPurgMemByBuilder(pobj, std::move(modA2B));
371    ModifyPurgMemByBuilder(pobj, std::move(modB2C));
372    ModifyPurgMemByBuilder(pobj, std::move(modC2D));
373
374    int times = 0;
375    int ret = 1;
376    while (times++ < 10) {
377        if (pobj->BeginRead()) {
378            ret = strncmp(alphabet, static_cast<char *>(pobj->GetContent()), 4097);
379            pobj->EndRead();
380            break;
381        } else {
382            std::cout << __func__ << ": ERROR! BeginRead failed." << std::endl;
383        }
384    }
385    delete pobj;
386    pobj = nullptr;
387    EXPECT_EQ(ret, 0);
388}
389
390HWTEST_F(PurgeableCppTest, MutiMorePageReadWriteTest, TestSize.Level1)
391{
392    size_t size = 5 * 1024 * 1024;
393    char *alphabet = static_cast<char *>(malloc(size));
394    size_t len = 0;
395    for (char ch = 'D'; len < size;) {
396        alphabet[len++] = ch;
397    }
398    alphabet[size - 1] = 0;
399    std::unique_ptr<PurgeableMemBuilder> builder = std::make_unique<TestBigDataBuilder>('A');
400    PurgeableMem *pobj = new PurgeableMem(size, std::move(builder));
401
402    LoopReclaimPurgeable(1);
403    LoopPrintAlphabet(pobj, 1);
404
405    std::unique_ptr<PurgeableMemBuilder> modA2B = std::make_unique<TestDataModifier>('A', 'B');
406    std::unique_ptr<PurgeableMemBuilder> modB2C = std::make_unique<TestDataModifier>('B', 'C');
407    std::unique_ptr<PurgeableMemBuilder> modC2D = std::make_unique<TestDataModifier>('C', 'D');
408    ModifyPurgMemByBuilder(pobj, std::move(modA2B));
409    ModifyPurgMemByBuilder(pobj, std::move(modB2C));
410    ModifyPurgMemByBuilder(pobj, std::move(modC2D));
411
412    int times = 0;
413    int ret = 1;
414    while (times++ < 10) {
415        if (pobj->BeginRead()) {
416            ret = strncmp(alphabet, static_cast<char *>(pobj->GetContent()), size - 1);
417            pobj->EndRead();
418            break;
419        } else {
420            std::cout << __func__ << ": ERROR! BeginRead failed." << std::endl;
421        }
422    }
423    delete pobj;
424    pobj = nullptr;
425    free(alphabet);
426    alphabet = nullptr;
427    EXPECT_EQ(ret, 0);
428}
429
430HWTEST_F(PurgeableCppTest, InvalidInputSizeTest, TestSize.Level1)
431{
432    std::unique_ptr<PurgeableMemBuilder> builder = std::make_unique<TestDataBuilder>('A', 'Z');
433    PurgeableMem *pobj = new PurgeableMem(0, std::move(builder));
434    LoopReclaimPurgeable(1);
435    bool ret = pobj->BeginRead();
436    if (ret) {
437        pobj->EndRead();
438    }
439    delete pobj;
440    pobj = nullptr;
441    EXPECT_EQ(ret, false);
442}
443
444HWTEST_F(PurgeableCppTest, InvalidInputBuilderTest, TestSize.Level1)
445{
446    PurgeableMem *pobj = new PurgeableMem(27, nullptr);
447    LoopReclaimPurgeable(1);
448    bool ret = pobj->BeginRead();
449    if (ret) {
450        pobj->EndRead();
451    }
452    delete pobj;
453    pobj = nullptr;
454    EXPECT_EQ(ret, false);
455}
456
457HWTEST_F(PurgeableCppTest, ResizeDataTest, TestSize.Level1)
458{
459    std::unique_ptr<PurgeableMemBuilder> builder = std::make_unique<TestDataBuilder>('A', 'Z');
460    PurgeableMem *pobj1 = new PurgeableMem(27, std::move(builder));
461    PurgeableMem *pobj2 = new PurgeableMem(27, std::move(builder));
462    std::function<void()> callback = std::bind(&PurgeableMem::IsDataValid, pobj1);
463    size_t pageSize = PAGE_SIZE;
464    LoopReclaimPurgeable(1);
465
466    size_t newSize = 0;
467    int intdata = 12345;
468    void *data = &intdata;
469    pobj1->ResizeData(newSize);
470    newSize = 1;
471    pobj1->ResizeData(newSize);
472    size_t roundip = ((pobj2->dataSizeInput_ + pageSize) / pageSize) * pageSize;
473    munmap(pobj2->dataPtr_, roundip);
474    pobj2->ResizeData(newSize);
475    roundip = ((pobj2->dataSizeInput_ + pageSize) / pageSize) * pageSize;
476    munmap(pobj2->dataPtr_, roundip);
477    pobj2->dataPtr_ = data;
478    pobj2->ResizeData(newSize);
479    EXPECT_NE(pobj2->dataPtr_, nullptr);
480    pobj1->builder_->DoRebuildSuccessCallback();
481    pobj1->builder_->SetRebuildSuccessCallback(callback);
482    pobj1->builder_->DoRebuildSuccessCallback();
483    pobj1->builder_ = nullptr;
484    pobj1->SetRebuildSuccessCallback(callback);
485    delete pobj1;
486    delete pobj2;
487    pobj1 = nullptr;
488    pobj2 = nullptr;
489}
490
491void LoopPrintAlphabet(PurgeableMem *pdata, unsigned int loopCount)
492{
493    std::cout << "inter " << __func__ << std::endl;
494    for (unsigned int i = 0; i < loopCount; i++) {
495        if (!pdata->BeginRead()) {
496            std::cout << __func__ << ": " << i << ". ERROR! BeginRead failed." << std::endl;
497            break;
498        }
499        pdata->EndRead();
500        std::this_thread::sleep_for(std::chrono::seconds(PRINT_INTERVAL_SECONDS));
501    }
502    std::cout << "quit " << __func__ << std::endl;
503}
504
505bool ReclaimPurgeable(void)
506{
507    FILE *f = fopen("/proc/sys/kernel/purgeable", "w");
508    if (!f) {
509        std::cout << __func__ << ": kernel not support" << std::endl;
510        return false;
511    }
512    bool succ = true;
513    if (fputs("1", f) == EOF) {
514        succ = false;
515    }
516
517    if (fclose(f) == EOF) {
518        std::cout << __func__ << ": close file failed" << std::endl;
519    }
520
521    return succ;
522}
523
524void LoopReclaimPurgeable(unsigned int loopCount)
525{
526    bool ret = false;
527    std::cout << "inter " << __func__ << std::endl;
528    for (unsigned int i = 0; i < loopCount; i++) {
529        ret = ReclaimPurgeable();
530        std::cout << __func__ << ": " << i << ". Reclaim result=" << (ret ? "succ" : "fail") << std::endl;
531        std::this_thread::sleep_for(std::chrono::seconds(RECLAIM_INTERVAL_SECONDS)); /* wait reclaim finish */
532    }
533    std::cout << "quit " << __func__ << std::endl;
534}
535
536void ModifyPurgMemByBuilder(PurgeableMem *pdata, std::unique_ptr<PurgeableMemBuilder> mod)
537{
538    if (!pdata->BeginWrite()) {
539        std::cout << __func__ << ": ERROR! BeginWrite failed." << std::endl;
540        return;
541    }
542    std::this_thread::sleep_for(std::chrono::seconds(MODIFY_INTERVAL_SECONDS));
543    pdata->ModifyContentByBuilder(std::move(mod));
544    pdata->EndWrite();
545}
546} /* namespace PurgeableMem */
547} /* namespace OHOS */
548