1/*
2 * Copyright (c) 2024 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_memory.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(OH_PurgeableMemory *pdata, unsigned int loopCount);
46bool ReclaimPurgeable(void);
47void LoopReclaimPurgeable(unsigned int loopCount);
48void ModifyPurgMemByFunc(OH_PurgeableMemory *pdata, OH_PurgeableMemory_ModifyFunc Modfunc, void *param);
49
50class PurgeableMemoryTest : public testing::Test {
51public:
52    static void SetUpTestCase();
53    static void TearDownTestCase();
54    void SetUp();
55    void TearDown();
56};
57
58void PurgeableMemoryTest::SetUpTestCase()
59{
60}
61
62void PurgeableMemoryTest::TearDownTestCase()
63{
64}
65
66void PurgeableMemoryTest::SetUp()
67{
68}
69
70void PurgeableMemoryTest::TearDown()
71{
72}
73
74HWTEST_F(PurgeableMemoryTest, MultiObjCreateTest, TestSize.Level1)
75{
76    const char alphabetFinal[] = "BBCDEFGHIJKLMNOPQRSTUVWXYZ\0";
77    struct AlphabetInitParam initPara = {'A', 'Z'};
78    OH_PurgeableMemory *pobj1 = OH_PurgeableMemory_Create(27, InitAlphabet, &initPara);
79    LoopPrintAlphabet(pobj1, 1);
80    struct AlphabetModifyParam a2b = {'A', 'B'};
81    ModifyPurgMemByFunc(pobj1, ModifyAlphabetX2Y, static_cast<void *>(&a2b));
82    LoopPrintAlphabet(pobj1, 1);
83    LoopReclaimPurgeable(1);
84
85    if (OH_PurgeableMemory_BeginRead(pobj1)) {
86        ASSERT_STREQ(alphabetFinal, static_cast<char *>(OH_PurgeableMemory_GetContent(pobj1)));
87        OH_PurgeableMemory_EndRead(pobj1);
88    }
89
90    EXPECT_EQ(OH_PurgeableMemory_Destroy(pobj1), true);
91}
92
93
94HWTEST_F(PurgeableMemoryTest, WriteTest, TestSize.Level1)
95{
96    const char alphabet[] = "CCCDEFGHIJKLMNOPQRSTUVWXYZ\0";
97    struct AlphabetInitParam initPara = {'A', 'Z'};
98    OH_PurgeableMemory *pobj = OH_PurgeableMemory_Create(27, InitAlphabet, &initPara);
99    LoopReclaimPurgeable(1);
100
101    struct AlphabetModifyParam a2b = {'A', 'B'};
102    struct AlphabetModifyParam b2c = {'B', 'C'};
103    ModifyPurgMemByFunc(pobj, ModifyAlphabetX2Y, static_cast<void *>(&a2b));
104    ModifyPurgMemByFunc(pobj, ModifyAlphabetX2Y, static_cast<void *>(&b2c));
105
106    if (OH_PurgeableMemory_BeginRead(pobj)) {
107        ASSERT_STREQ(alphabet, static_cast<char *>(OH_PurgeableMemory_GetContent(pobj)));
108        OH_PurgeableMemory_EndRead(pobj);
109    } else {
110        std::cout << __func__ << ": ERROR! BeginRead failed." << std::endl;
111    }
112
113    OH_PurgeableMemory_Destroy(pobj);
114    LoopReclaimPurgeable(3);
115}
116
117bool InitData(void *data, size_t size, char start, char end)
118{
119    char *str = (char *)data;
120    size_t len = 0;
121    for (char ch = start; ch <= end && len < size; ch++) {
122        str[len++] = ch;
123    }
124    str[len] = 0;
125    return true;
126}
127
128bool InitAlphabet(void *data, size_t size, void *param)
129{
130    struct AlphabetInitParam *para = (struct AlphabetInitParam *)param;
131    std::cout << "inter " << __func__ << std::endl;
132    bool ret = InitData(data, size, para->start, para->end);
133    std::cout << "quit " << __func__ << ": " << para->start << "-" << para->end <<
134        ", data=[" << (char *)data << "]" << ", ret=" << (ret ? "true" : "false") << std::endl;
135    return ret;
136}
137
138bool ModifyData(void *data, size_t size, char src, char dst)
139{
140    char *str = (char *)data;
141    size_t i = 0;
142    for (; i < size && str[i]; i++) {
143        if (str[i] == src) {
144            str[i] = dst;
145        }
146    }
147    str[i] = 0;
148    return true;
149}
150
151bool ModifyAlphabetX2Y(void *data, size_t size, void *param)
152{
153    struct AlphabetModifyParam *para = (struct AlphabetModifyParam *)param;
154    std::cout << "inter " << __func__ << ": " << para->src << "->" << para->dst <<
155        ", data=[" << (char *)data << "]" << std::endl;
156    bool ret = ModifyData(data, size, para->src, para->dst);
157    std::cout << "quit , data=[" << (char *)data << "]" << __func__ <<
158        ", ret=" << (ret ? "true" : "false") << std::endl;
159    return ret;
160}
161
162void LoopPrintAlphabet(OH_PurgeableMemory *pdata, unsigned int loopCount)
163{
164    std::cout << "inter " << __func__ << std::endl;
165    for (unsigned int i = 0; i < loopCount; i++) {
166        if (!OH_PurgeableMemory_BeginRead(pdata)) {
167            std::cout << __func__ << ": " << i << ". ERROR! BeginRead failed." << std::endl;
168            break;
169        }
170        std::cout << __func__ << ": " << i << ". data=[" <<
171            (char *)OH_PurgeableMemory_GetContent(pdata) << "]" << std::endl;
172        OH_PurgeableMemory_EndRead(pdata);
173        std::this_thread::sleep_for(std::chrono::seconds(PRINT_INTERVAL_SECONDS));
174    }
175    std::cout << "quit " << __func__ << std::endl;
176}
177
178bool ReclaimPurgeable(void)
179{
180    FILE *f = fopen("/proc/sys/kernel/purgeable", "w");
181    if (!f) {
182        std::cout << __func__ << ": open file failed" << std::endl;
183        return false;
184    }
185    bool succ = true;
186    if (fputs("1", f) == EOF) {
187        succ = false;
188    }
189
190    if (fclose(f) == EOF) {
191        std::cout << __func__ << ": close file failed" << std::endl;
192    }
193
194    return succ;
195}
196
197void LoopReclaimPurgeable(unsigned int loopCount)
198{
199    bool ret = false;
200    std::cout << "inter " << __func__ << std::endl;
201    for (unsigned int i = 0; i < loopCount; i++) {
202        ret = ReclaimPurgeable();
203        std::cout << __func__ << ": " << i << ". Reclaim result=" << (ret ? "succ" : "fail") << std::endl;
204        std::this_thread::sleep_for(std::chrono::seconds(RECLAIM_INTERVAL_SECONDS)); /* wait reclaim finish */
205    }
206    std::cout << "quit " << __func__ << std::endl;
207}
208
209void ModifyPurgMemByFunc(OH_PurgeableMemory *pdata, OH_PurgeableMemory_ModifyFunc Modfunc, void *param)
210{
211    if (OH_PurgeableMemory_BeginWrite(pdata)) {
212        std::this_thread::sleep_for(std::chrono::seconds(MODIFY_INTERVAL_SECONDS));
213        OH_PurgeableMemory_AppendModify(pdata, Modfunc, param);
214        std::cout<< __func__ << " after mod data=[" << (char *)OH_PurgeableMemory_GetContent(pdata) << "]" << std::endl;
215
216        std::cout << __func__ << " data=[" << (char *)OH_PurgeableMemory_GetContent(pdata) << "]" << std::endl;
217        OH_PurgeableMemory_EndWrite(pdata);
218    }
219}
220} /* namespace */
221