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 <climits>
18#include <thread>
19
20#include "gtest/gtest.h"
21#include "purgeable_mem_c.h"
22
23namespace {
24using namespace testing;
25using namespace testing::ext;
26
27struct AlphabetInitParam {
28    char start;
29    char end;
30};
31
32struct AlphabetModifyParam {
33    char src;
34    char dst;
35};
36
37static constexpr int PRINT_INTERVAL_SECONDS = 1;
38static constexpr int RECLAIM_INTERVAL_SECONDS = 1;
39static constexpr int MODIFY_INTERVAL_SECONDS = 2;
40
41bool InitData(void *data, size_t size, char start, char end);
42bool ModifyData(void *data, size_t size, char src, char dst);
43bool InitAlphabet(void *data, size_t size, void *param);
44bool ModifyAlphabetX2Y(void *data, size_t size, void *param);
45void LoopPrintAlphabet(struct PurgMem *pdata, unsigned int loopCount);
46bool ReclaimPurgeable(void);
47void LoopReclaimPurgeable(unsigned int loopCount);
48void ModifyPurgMemByFunc(struct PurgMem *pdata, PurgMemModifyFunc Modfunc, void *param);
49
50class PurgeableCTest : public testing::Test {
51public:
52    static void SetUpTestCase();
53    static void TearDownTestCase();
54    void SetUp();
55    void TearDown();
56};
57
58void PurgeableCTest::SetUpTestCase()
59{
60}
61
62void PurgeableCTest::TearDownTestCase()
63{
64}
65
66void PurgeableCTest::SetUp()
67{
68}
69
70void PurgeableCTest::TearDown()
71{
72}
73
74HWTEST_F(PurgeableCTest, MultiObjCreateTest, TestSize.Level1)
75{
76    const char alphabetFinal[] = "BBCDEFGHIJKLMNOPQRSTUVWXYZ\0";
77    struct AlphabetInitParam initPara = {'A', 'Z'};
78    PurgMemCreate(0, InitAlphabet, &initPara);
79    struct PurgMem *pobj1 = PurgMemCreate(27, InitAlphabet, &initPara);
80    LoopPrintAlphabet(pobj1, 1);
81    struct AlphabetModifyParam a2b = {'A', 'B'};
82    ModifyPurgMemByFunc(pobj1, ModifyAlphabetX2Y, static_cast<void *>(&a2b));
83    LoopPrintAlphabet(pobj1, 1);
84    LoopReclaimPurgeable(1);
85
86    struct PurgMem *pobj2 = PurgMemCreate(27, InitAlphabet, &initPara);
87    LoopPrintAlphabet(pobj2, 1);
88    ModifyPurgMemByFunc(pobj2, ModifyAlphabetX2Y, static_cast<void *>(&a2b));
89    LoopPrintAlphabet(pobj2, 1);
90
91    if (PurgMemBeginRead(pobj1)) {
92        ASSERT_STREQ(alphabetFinal, static_cast<char *>(PurgMemGetContent(pobj1)));
93        PurgMemEndRead(pobj1);
94    } else {
95        std::cout << __func__ << ": ERROR! BeginRead failed." << std::endl;
96    }
97
98    if (PurgMemBeginRead(pobj2)) {
99        ASSERT_STREQ(alphabetFinal, static_cast<char *>(PurgMemGetContent(pobj2)));
100        PurgMemEndRead(pobj2);
101    } else {
102        std::cout << __func__ << ": ERROR! BeginRead failed." << std::endl;
103    }
104
105    PurgMemDestroy(pobj2);
106    PurgMemDestroy(pobj1);
107}
108
109HWTEST_F(PurgeableCTest, ReadTest, TestSize.Level1)
110{
111    const char alphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ\0";
112    struct AlphabetInitParam initPara = {'A', 'Z'};
113    struct PurgMem *pobj = PurgMemCreate(27, InitAlphabet, &initPara);
114    LoopReclaimPurgeable(1);
115
116    unsigned int loopCount = 3;
117    /* loop read content */
118    for (unsigned int i = 0; i < loopCount; i++) {
119        if (!PurgMemBeginRead(pobj)) {
120            std::cout << __func__ << ": ERROR! BeginRead failed." << std::endl;
121            continue;
122        }
123        ASSERT_STREQ(alphabet, static_cast<char *>(PurgMemGetContent(pobj)));
124        PurgMemEndRead(pobj);
125    }
126
127    PurgMemDestroy(pobj);
128}
129
130HWTEST_F(PurgeableCTest, WriteTest, TestSize.Level1)
131{
132    const char alphabet[] = "CCCDEFGHIJKLMNOPQRSTUVWXYZ\0";
133    struct AlphabetInitParam initPara = {'A', 'Z'};
134    struct PurgMem *pobj = PurgMemCreate(27, InitAlphabet, &initPara);
135    LoopReclaimPurgeable(1);
136
137    struct AlphabetModifyParam a2b = {'A', 'B'};
138    struct AlphabetModifyParam b2c = {'B', 'C'};
139    ModifyPurgMemByFunc(pobj, ModifyAlphabetX2Y, static_cast<void *>(&a2b));
140    ModifyPurgMemByFunc(pobj, ModifyAlphabetX2Y, static_cast<void *>(&b2c));
141
142    if (PurgMemBeginRead(pobj)) {
143        ASSERT_STREQ(alphabet, static_cast<char *>(PurgMemGetContent(pobj)));
144        PurgMemEndRead(pobj);
145    } else {
146        std::cout << __func__ << ": ERROR! BeginRead failed." << std::endl;
147    }
148
149    PurgMemDestroy(pobj);
150    LoopReclaimPurgeable(3);
151}
152
153HWTEST_F(PurgeableCTest, ReadWriteTest, TestSize.Level1)
154{
155    const char alphabet[] = "DDDDEFGHIJKLMNOPQRSTUVWXYZ\0";
156    struct AlphabetInitParam initPara = {'A', 'Z'};
157    struct PurgMem *pobj = PurgMemCreate(27, InitAlphabet, &initPara);
158    LoopReclaimPurgeable(1);
159    LoopPrintAlphabet(pobj, 1);
160
161    struct AlphabetModifyParam a2b = {'A', 'B'};
162    struct AlphabetModifyParam b2c = {'B', 'C'};
163    struct AlphabetModifyParam c2d = {'C', 'D'};
164    ModifyPurgMemByFunc(pobj, ModifyAlphabetX2Y, static_cast<void *>(&a2b));
165    ModifyPurgMemByFunc(pobj, ModifyAlphabetX2Y, static_cast<void *>(&b2c));
166    ModifyPurgMemByFunc(pobj, ModifyAlphabetX2Y, static_cast<void *>(&c2d));
167
168    if (PurgMemBeginRead(pobj)) {
169        ASSERT_STREQ(alphabet, static_cast<char *>(PurgMemGetContent(pobj)));
170        PurgMemEndRead(pobj);
171    } else {
172        std::cout << __func__ << ": ERROR! BeginRead failed." << std::endl;
173    }
174
175    PurgMemDestroy(pobj);
176}
177
178bool InitData(void *data, size_t size, char start, char end)
179{
180    char *str = (char *)data;
181    size_t len = 0;
182    for (char ch = start; ch <= end && len < size; ch++) {
183        str[len++] = ch;
184    }
185    str[len] = 0;
186    return true;
187}
188
189bool InitAlphabet(void *data, size_t size, void *param)
190{
191    struct AlphabetInitParam *para = (struct AlphabetInitParam *)param;
192    std::cout << "inter " << __func__ << std::endl;
193    bool ret = InitData(data, size, para->start, para->end);
194    std::cout << "quit " << __func__ << ": " << para->start << "-" << para->end <<
195        ", data=[" << (char *)data << "]" << ", ret=" << (ret ? "true" : "false") << std::endl;
196    return ret;
197}
198
199bool ModifyData(void *data, size_t size, char src, char dst)
200{
201    char *str = (char *)data;
202    size_t i = 0;
203    for (; i < size && str[i]; i++) {
204        if (str[i] == src) {
205            str[i] = dst;
206        }
207    }
208    str[i] = 0;
209    return true;
210}
211
212bool ModifyAlphabetX2Y(void *data, size_t size, void *param)
213{
214    struct AlphabetModifyParam *para = (struct AlphabetModifyParam *)param;
215    std::cout << "inter " << __func__ << ": " << para->src << "->" << para->dst <<
216        ", data=[" << (char *)data << "]" << std::endl;
217    bool ret = ModifyData(data, size, para->src, para->dst);
218    std::cout << "quit , data=[" << (char *)data << "]" << __func__ <<
219        ", ret=" << (ret ? "true" : "false") << std::endl;
220    return ret;
221}
222
223void LoopPrintAlphabet(struct PurgMem *pdata, unsigned int loopCount)
224{
225    std::cout << "inter " << __func__ << std::endl;
226    for (unsigned int i = 0; i < loopCount; i++) {
227        if (!PurgMemBeginRead(pdata)) {
228            std::cout << __func__ << ": " << i << ". ERROR! BeginRead failed." << std::endl;
229            break;
230        }
231        std::cout << __func__ << ": " << i << ". data=[" <<
232            (char *)PurgMemGetContent(pdata) << "]" << std::endl;
233        PurgMemEndRead(pdata);
234        std::this_thread::sleep_for(std::chrono::seconds(PRINT_INTERVAL_SECONDS));
235    }
236    std::cout << "quit " << __func__ << std::endl;
237}
238
239bool ReclaimPurgeable(void)
240{
241    FILE *f = fopen("/proc/sys/kernel/purgeable", "w");
242    if (!f) {
243        std::cout << __func__ << ": open file failed" << std::endl;
244        return false;
245    }
246    bool succ = true;
247    if (fputs("1", f) == EOF) {
248        succ = false;
249    }
250
251    if (fclose(f) == EOF) {
252        std::cout << __func__ << ": close file failed" << std::endl;
253    }
254
255    return succ;
256}
257
258void LoopReclaimPurgeable(unsigned int loopCount)
259{
260    bool ret = false;
261    std::cout << "inter " << __func__ << std::endl;
262    for (unsigned int i = 0; i < loopCount; i++) {
263        ret = ReclaimPurgeable();
264        std::cout << __func__ << ": " << i << ". Reclaim result=" << (ret ? "succ" : "fail") << std::endl;
265        std::this_thread::sleep_for(std::chrono::seconds(RECLAIM_INTERVAL_SECONDS)); /* wait reclaim finish */
266    }
267    std::cout << "quit " << __func__ << std::endl;
268}
269
270void ModifyPurgMemByFunc(struct PurgMem *pdata, PurgMemModifyFunc Modfunc, void *param)
271{
272    if (!PurgMemBeginWrite(pdata)) {
273        std::cout << __func__ << ": ERROR! BeginWrite failed." << std::endl;
274        return;
275    }
276    std::this_thread::sleep_for(std::chrono::seconds(MODIFY_INTERVAL_SECONDS));
277    std::cout << __func__ << " before mod data=[" << (char *)PurgMemGetContent(pdata) << "]" << std::endl;
278    PurgMemAppendModify(pdata, Modfunc, param);
279    std::cout<< __func__ << " after mod data=[" << (char *)PurgMemGetContent(pdata) << "]" << std::endl;
280
281    std::cout << __func__ << " data=[" << (char *)PurgMemGetContent(pdata) << "]" << std::endl;
282    PurgMemEndWrite(pdata);
283}
284} /* namespace */
285