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