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 "distributeddb_tools_test.h"
17 #include <cstring>
18 #include <dirent.h>
19 #include <fstream>
20 #include <openssl/rand.h>
21 #include <random>
22 #include <set>
23 #include <sys/types.h>
24 
25 #include "db_common.h"
26 #include "db_constant.h"
27 #include "generic_single_ver_kv_entry.h"
28 #include "platform_specific.h"
29 #include "single_ver_data_packet.h"
30 #include "value_hash_calc.h"
31 
32 using namespace DistributedDB;
33 namespace DistributedDBTest {
GetCurrentDir(std::string & dir)34 int DistributedDBToolsTest::GetCurrentDir(std::string &dir)
35 {
36     static const int maxFileLength = 1024;
37     dir = "";
38     char buffer[maxFileLength] = {0};
39     int length = readlink("/proc/self/exe", buffer, maxFileLength);
40     if (length < 0 || length >= maxFileLength) {
41         LOGE("read directory err length:%d", length);
42         return -E_LENGTH_ERROR;
43     }
44     LOGD("DIR = %s", buffer);
45     dir = buffer;
46     if (dir.rfind("/") == std::string::npos && dir.rfind("\\") == std::string::npos) {
47         LOGE("current patch format err");
48         return -E_INVALID_PATH;
49     }
50 
51     if (dir.rfind("/") != std::string::npos) {
52         dir.erase(dir.rfind("/") + 1);
53     }
54     return E_OK;
55 }
56 
TestDirInit(std::string & dir)57 void DistributedDBToolsTest::TestDirInit(std::string &dir)
58 {
59     if (GetCurrentDir(dir) != E_OK) {
60         dir = "/";
61     }
62 
63     dir.append("testDbDir");
64     DIR *dirTmp = opendir(dir.c_str());
65     if (dirTmp == nullptr) {
66         if (OS::MakeDBDirectory(dir) != 0) {
67             LOGI("MakeDirectory err!");
68             dir = "/";
69             return;
70         }
71     } else {
72         closedir(dirTmp);
73     }
74 }
75 
RemoveTestDbFiles(const std::string & dir)76 int DistributedDBToolsTest::RemoveTestDbFiles(const std::string &dir)
77 {
78     bool isExisted = OS::CheckPathExistence(dir);
79     if (!isExisted) {
80         return E_OK;
81     }
82 
83     int nFile = 0;
84     std::string dirName;
85     struct dirent *direntPtr = nullptr;
86     DIR *dirPtr = opendir(dir.c_str());
87     if (dirPtr == nullptr) {
88         LOGE("opendir error!");
89         return -E_INVALID_PATH;
90     }
91     while (true) {
92         direntPtr = readdir(dirPtr);
93         // condition to exit the loop
94         if (direntPtr == nullptr) {
95             break;
96         }
97         // only remove all *.db files
98         std::string str(direntPtr->d_name);
99         if (str == "." || str == "..") {
100             continue;
101         }
102         dirName.clear();
103         dirName.append(dir).append("/").append(str);
104         if (direntPtr->d_type == DT_DIR) {
105             RemoveTestDbFiles(dirName);
106             rmdir(dirName.c_str());
107         } else if (remove(dirName.c_str()) != 0) {
108             LOGI("remove file: %s failed!", dirName.c_str());
109             continue;
110         }
111         nFile++;
112     }
113     closedir(dirPtr);
114     LOGI("Total %d test db files are removed!", nFile);
115     return 0;
116 }
117 
GetRandomKeyValue(std::vector<uint8_t> & value,uint32_t defaultSize)118 void DistributedDBToolsTest::GetRandomKeyValue(std::vector<uint8_t> &value, uint32_t defaultSize)
119 {
120     uint32_t randSize = 0;
121     if (defaultSize == 0) {
122         uint8_t simSize = 0;
123         RAND_bytes(&simSize, 1);
124         randSize = (simSize == 0) ? 1 : simSize;
125     } else {
126         randSize = defaultSize;
127     }
128 
129     value.resize(randSize);
130     RAND_bytes(value.data(), randSize);
131 }
132 
SyncTestWithQuery(KvStoreNbDelegate * delegate,const std::vector<std::string> & devices,SyncMode mode,std::map<std::string,DBStatus> & statuses,const Query & query)133 DBStatus DistributedDBToolsTest::SyncTestWithQuery(KvStoreNbDelegate* delegate,
134     const std::vector<std::string>& devices, SyncMode mode,
135     std::map<std::string, DBStatus>& statuses, const Query &query)
136 {
137     std::mutex syncLock;
138     std::condition_variable syncCondVar;
139     statuses.clear();
140     DBStatus callStatus = delegate->Sync(devices, mode,
141         [&statuses, &syncLock, &syncCondVar](const std::map<std::string, DBStatus>& statusMap) {
142             statuses = statusMap;
143             std::unique_lock<std::mutex> innerlock(syncLock);
144             syncCondVar.notify_one();
145         }, query, false);
146     std::unique_lock<std::mutex> lock(syncLock);
147     syncCondVar.wait(lock, [callStatus, &statuses]() {
148             if (callStatus != OK) {
149                 return true;
150             }
151             return !statuses.empty();
152         });
153     return callStatus;
154 }
155 
SyncTest(KvStoreNbDelegate * delegate,const std::vector<std::string> & devices,SyncMode mode,std::map<std::string,DBStatus> & statuses)156 DBStatus DistributedDBToolsTest::SyncTest(KvStoreNbDelegate* delegate, const std::vector<std::string>& devices,
157     SyncMode mode, std::map<std::string, DBStatus>& statuses)
158 {
159     std::mutex syncLock;
160     std::condition_variable syncCondVar;
161     statuses.clear();
162     DBStatus callStatus = delegate->Sync(devices, mode,
163         [&statuses,  &syncLock, &syncCondVar](const std::map<std::string, DBStatus>& statusMap) {
164             statuses = statusMap;
165             std::unique_lock<std::mutex> innerlock(syncLock);
166             syncCondVar.notify_one();
167         }, false);
168     std::unique_lock<std::mutex> lock(syncLock);
169     syncCondVar.wait(lock, [callStatus, &statuses]() {
170             if (callStatus != OK) {
171                 return true;
172             }
173             return !statuses.empty();
174         });
175     return callStatus;
176 }
177 
KvStoreObserverTest()178 KvStoreObserverTest::KvStoreObserverTest() : callCount_(0), isCleared_(false)
179 {}
180 
OnChange(const KvStoreChangedData & data)181 void KvStoreObserverTest::OnChange(const KvStoreChangedData& data)
182 {
183     callCount_++;
184     inserted_ = data.GetEntriesInserted();
185     updated_ = data.GetEntriesUpdated();
186     deleted_ = data.GetEntriesDeleted();
187     isCleared_ = data.IsCleared();
188     LOGD("Onchangedata :%zu -- %zu -- %zu -- %d", inserted_.size(), updated_.size(), deleted_.size(), isCleared_);
189     LOGD("Onchange() called success!");
190 }
191 
CreateDataBase(const std::string & dbUri)192 sqlite3 *RdbTestUtils::CreateDataBase(const std::string &dbUri)
193 {
194     sqlite3 *db = nullptr;
195     if (sqlite3_open_v2(dbUri.c_str(), &db, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, nullptr) != SQLITE_OK) {
196         if (db != nullptr) {
197             (void)sqlite3_close_v2(db);
198             db = nullptr;
199         }
200     }
201     return db;
202 }
203 
ExecSql(sqlite3 * db,const std::string & sql)204 int RdbTestUtils::ExecSql(sqlite3 *db, const std::string &sql)
205 {
206     if (db == nullptr || sql.empty()) {
207         return -E_INVALID_ARGS;
208     }
209     char *errMsg = nullptr;
210     int errCode = sqlite3_exec(db, sql.c_str(), nullptr, nullptr, &errMsg);
211     if (errCode != SQLITE_OK && errMsg != nullptr) {
212         LOGE("Execute sql failed. %d err: %s", errCode, errMsg);
213     }
214     sqlite3_free(errMsg);
215     return errCode;
216 }
217 
CreateDeviceTable(sqlite3 * db,const std::string & table,const std::string & device)218 int RdbTestUtils::CreateDeviceTable(sqlite3 *db, const std::string &table, const std::string &device)
219 {
220     std::string deviceTable = DBCommon::GetDistributedTableName(device, table);
221     TableInfo baseTbl;
222     if (SQLiteUtils::AnalysisSchema(db, table, baseTbl) != E_OK) {
223         return -1;
224     }
225     if (SQLiteUtils::CreateSameStuTable(db, baseTbl, deviceTable) != E_OK) {
226         return -1;
227     }
228     if (SQLiteUtils::CloneIndexes(db, table, deviceTable) != E_OK) {
229         return -1;
230     }
231     return 0;
232 }
233 }