1 /*
2 * Copyright (c) 2023 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 #ifdef RELATIONAL_STORE
16 #include <gtest/gtest.h>
17 #include <iostream>
18 #include "cloud/cloud_storage_utils.h"
19 #include "cloud_db_constant.h"
20 #include "distributeddb_data_generate_unit_test.h"
21 #include "distributeddb_tools_unit_test.h"
22 #include "process_system_api_adapter_impl.h"
23 #include "relational_store_instance.h"
24 #include "relational_store_manager.h"
25 #include "runtime_config.h"
26 #include "sqlite_relational_store.h"
27 #include "sqlite_relational_utils.h"
28 #include "store_observer.h"
29 #include "time_helper.h"
30 #include "virtual_asset_loader.h"
31 #include "virtual_cloud_data_translate.h"
32 #include "virtual_cloud_db.h"
33 #include "mock_asset_loader.h"
34
35 using namespace testing::ext;
36 using namespace DistributedDB;
37 using namespace DistributedDBUnitTest;
38 using namespace std;
39
40 namespace {
41 string g_storeID = "Relational_Store_SYNC";
42 const string g_tableName1 = "worker1";
43 const string g_tableName2 = "worker2";
44 const string g_tableName3 = "worker3";
45 const string g_tableName4 = "worker4";
46 const string DEVICE_CLOUD = "cloud_dev";
47 const string DB_SUFFIX = ".db";
48 const int64_t g_syncWaitTime = 60;
49 const int g_arrayHalfSub = 2;
50 int g_syncIndex = 0;
51 string g_testDir;
52 string g_storePath;
53 std::mutex g_processMutex;
54 std::condition_variable g_processCondition;
55 std::shared_ptr<VirtualCloudDb> g_virtualCloudDb;
56 std::shared_ptr<VirtualAssetLoader> g_virtualAssetLoader;
57 DistributedDB::RelationalStoreManager g_mgr(APP_ID, USER_ID);
58 RelationalStoreObserverUnitTest *g_observer = nullptr;
59 RelationalStoreDelegate *g_delegate = nullptr;
60 SyncProcess g_syncProcess;
61 using CloudSyncStatusCallback = std::function<void(const std::map<std::string, SyncProcess> &onProcess)>;
62 const std::string CREATE_LOCAL_TABLE_SQL =
63 "CREATE TABLE IF NOT EXISTS " + g_tableName1 + "(" \
64 "name TEXT PRIMARY KEY," \
65 "height REAL ," \
66 "married BOOLEAN ," \
67 "photo BLOB NOT NULL," \
68 "assert BLOB," \
69 "age INT);";
70 const std::string INTEGER_PRIMARY_KEY_TABLE_SQL =
71 "CREATE TABLE IF NOT EXISTS " + g_tableName2 + "(" \
72 "id INTEGER PRIMARY KEY," \
73 "name TEXT ," \
74 "height REAL ," \
75 "photo BLOB ," \
76 "asserts BLOB," \
77 "age INT);";
78 const std::string DROP_INTEGER_PRIMARY_KEY_TABLE_SQL = "DROP TABLE " + g_tableName2 + ";";
79 const std::string CREATE_LOCAL_TABLE_WITHOUT_PRIMARY_KEY_SQL =
80 "CREATE TABLE IF NOT EXISTS " + g_tableName3 + "(" \
81 "name TEXT," \
82 "height REAL ," \
83 "married BOOLEAN ," \
84 "photo BLOB NOT NULL," \
85 "assert BLOB," \
86 "age INT);";
87 const std::string INTEGER_PRIMARY_KEY_TABLE_SQL_WRONG_SYNC_MODE =
88 "CREATE TABLE IF NOT EXISTS " + g_tableName4 + "(" \
89 "id INTEGER PRIMARY KEY," \
90 "name TEXT ," \
91 "height REAL ," \
92 "photo BLOB ," \
93 "asserts BLOB," \
94 "age INT);";
95 const std::vector<Field> g_cloudFiled1 = {
96 {"name", TYPE_INDEX<std::string>, true}, {"height", TYPE_INDEX<double>},
97 {"married", TYPE_INDEX<bool>}, {"photo", TYPE_INDEX<Bytes>, false, false},
98 {"assert", TYPE_INDEX<Asset>}, {"age", TYPE_INDEX<int64_t>}
99 };
100 const std::vector<Field> g_invalidCloudFiled1 = {
101 {"name", TYPE_INDEX<std::string>, true}, {"height", TYPE_INDEX<int>},
102 {"married", TYPE_INDEX<bool>}, {"photo", TYPE_INDEX<Bytes>, false, false},
103 {"assert", TYPE_INDEX<Bytes>}, {"age", TYPE_INDEX<int64_t>}
104 };
105 const std::vector<Field> g_cloudFiled2 = {
106 {"id", TYPE_INDEX<int64_t>, true}, {"name", TYPE_INDEX<std::string>},
107 {"height", TYPE_INDEX<double>}, {"photo", TYPE_INDEX<Bytes>},
108 {"asserts", TYPE_INDEX<Assets>}, {"age", TYPE_INDEX<int64_t>}
109 };
110 const std::vector<Field> g_cloudFiledWithOutPrimaryKey3 = {
111 {"name", TYPE_INDEX<std::string>, false, true}, {"height", TYPE_INDEX<double>},
112 {"married", TYPE_INDEX<bool>}, {"photo", TYPE_INDEX<Bytes>, false, false},
113 {"assert", TYPE_INDEX<Bytes>}, {"age", TYPE_INDEX<int64_t>}
114 };
115 const std::vector<std::string> g_tables = {g_tableName1, g_tableName2};
116 const std::vector<std::string> g_tablesPKey = {g_cloudFiled1[0].colName, g_cloudFiled2[0].colName};
117 const std::vector<string> g_prefix = {"Local", ""};
118 const Asset g_localAsset = {
119 .version = 1, .name = "Phone", .assetId = "0", .subpath = "/local/sync", .uri = "/local/sync",
120 .modifyTime = "123456", .createTime = "", .size = "256", .hash = "ASE"
121 };
122 const Asset g_cloudAsset = {
123 .version = 2, .name = "Phone", .assetId = "0", .subpath = "/local/sync", .uri = "/cloud/sync",
124 .modifyTime = "123456", .createTime = "0", .size = "1024", .hash = "DEC"
125 };
126
CreateUserDBAndTable(sqlite3 * & db)127 void CreateUserDBAndTable(sqlite3 *&db)
128 {
129 EXPECT_EQ(RelationalTestUtils::ExecSql(db, "PRAGMA journal_mode=WAL;"), SQLITE_OK);
130 EXPECT_EQ(RelationalTestUtils::ExecSql(db, CREATE_LOCAL_TABLE_SQL), SQLITE_OK);
131 EXPECT_EQ(RelationalTestUtils::ExecSql(db, INTEGER_PRIMARY_KEY_TABLE_SQL), SQLITE_OK);
132 EXPECT_EQ(RelationalTestUtils::ExecSql(db, CREATE_LOCAL_TABLE_WITHOUT_PRIMARY_KEY_SQL), SQLITE_OK);
133 }
134
InsertUserTableRecord(sqlite3 * & db,int64_t begin,int64_t count,int64_t photoSize,bool assetIsNull)135 void InsertUserTableRecord(sqlite3 *&db, int64_t begin, int64_t count, int64_t photoSize, bool assetIsNull)
136 {
137 std::string photo(photoSize, 'v');
138 int errCode;
139 std::vector<uint8_t> assetBlob;
140 for (int64_t i = begin; i < begin + count; ++i) {
141 Asset asset = g_localAsset;
142 asset.name = asset.name + std::to_string(i);
143 RuntimeContext::GetInstance()->AssetToBlob(asset, assetBlob);
144 string sql = "INSERT OR REPLACE INTO " + g_tableName1
145 + " (name, height, married, photo, assert, age) VALUES ('Local" + std::to_string(i) +
146 "', '175.8', '0', '" + photo + "', ? , '18');";
147 sqlite3_stmt *stmt = nullptr;
148 ASSERT_EQ(SQLiteUtils::GetStatement(db, sql, stmt), E_OK);
149 if (assetIsNull) {
150 ASSERT_EQ(sqlite3_bind_null(stmt, 1), SQLITE_OK);
151 } else {
152 ASSERT_EQ(SQLiteUtils::BindBlobToStatement(stmt, 1, assetBlob, false), E_OK);
153 }
154 EXPECT_EQ(SQLiteUtils::StepWithRetry(stmt), SQLiteUtils::MapSQLiteErrno(SQLITE_DONE));
155 SQLiteUtils::ResetStatement(stmt, true, errCode);
156 }
157 for (int64_t i = begin; i < begin + count; ++i) {
158 std::vector<Asset> assets;
159 Asset asset = g_localAsset;
160 asset.name = g_localAsset.name + std::to_string(i);
161 assets.push_back(asset);
162 asset.name = g_localAsset.name + std::to_string(i + 1);
163 assets.push_back(asset);
164 RuntimeContext::GetInstance()->AssetsToBlob(assets, assetBlob);
165 string sql = "INSERT OR REPLACE INTO " + g_tableName2
166 + " (id, name, height, photo, asserts, age) VALUES ('" + std::to_string(i) + "', 'Local"
167 + std::to_string(i) + "', '155.10', '"+ photo + "', ? , '21');";
168 sqlite3_stmt *stmt = nullptr;
169 ASSERT_EQ(SQLiteUtils::GetStatement(db, sql, stmt), E_OK);
170 if (assetIsNull) {
171 ASSERT_EQ(sqlite3_bind_null(stmt, 1), E_OK);
172 } else {
173 ASSERT_EQ(SQLiteUtils::BindBlobToStatement(stmt, 1, assetBlob, false), E_OK);
174 }
175 EXPECT_EQ(SQLiteUtils::StepWithRetry(stmt), SQLiteUtils::MapSQLiteErrno(SQLITE_DONE));
176 SQLiteUtils::ResetStatement(stmt, true, errCode);
177 }
178 LOGD("insert user record worker1[primary key]:[Local%" PRId64 " - Local%" PRId64
179 ") , worker2[primary key]:[%" PRId64 "- %" PRId64")", begin, count, begin, count);
180 }
181
InsertCloudTableRecord(int64_t begin,int64_t count,int64_t photoSize,bool assetIsNull)182 void InsertCloudTableRecord(int64_t begin, int64_t count, int64_t photoSize, bool assetIsNull)
183 {
184 std::vector<uint8_t> photo(photoSize, 'v');
185 std::vector<VBucket> record1;
186 std::vector<VBucket> extend1;
187 std::vector<VBucket> record2;
188 std::vector<VBucket> extend2;
189 Timestamp now = TimeHelper::GetSysCurrentTime();
190 for (int64_t i = begin; i < begin + count; ++i) {
191 VBucket data;
192 data.insert_or_assign("name", "Cloud" + std::to_string(i));
193 data.insert_or_assign("height", 166.0); // 166.0 is random double value
194 data.insert_or_assign("married", false);
195 data.insert_or_assign("photo", photo);
196 data.insert_or_assign("age", 13L);
197 Asset asset = g_cloudAsset;
198 asset.name = asset.name + std::to_string(i);
199 assetIsNull ? data.insert_or_assign("assert", Nil()) : data.insert_or_assign("assert", asset);
200 record1.push_back(data);
201 VBucket log;
202 log.insert_or_assign(CloudDbConstant::CREATE_FIELD, (int64_t)now / CloudDbConstant::TEN_THOUSAND + i);
203 log.insert_or_assign(CloudDbConstant::MODIFY_FIELD, (int64_t)now / CloudDbConstant::TEN_THOUSAND + i);
204 log.insert_or_assign(CloudDbConstant::DELETE_FIELD, false);
205 extend1.push_back(log);
206
207 std::vector<Asset> assets;
208 data.insert_or_assign("id", i);
209 data.insert_or_assign("height", 180.3); // 180.3 is random double value
210 for (int64_t j = i; j <= i + 2; j++) { // 2 extra num
211 asset.name = g_cloudAsset.name + std::to_string(j);
212 assets.push_back(asset);
213 }
214 data.erase("assert");
215 data.erase("married");
216 assetIsNull ? data.insert_or_assign("asserts", Nil()) : data.insert_or_assign("asserts", assets);
217 record2.push_back(data);
218 extend2.push_back(log);
219 }
220 ASSERT_EQ(g_virtualCloudDb->BatchInsert(g_tableName1, std::move(record1), extend1), DBStatus::OK);
221 ASSERT_EQ(g_virtualCloudDb->BatchInsert(g_tableName2, std::move(record2), extend2), DBStatus::OK);
222 LOGD("insert cloud record worker1[primary key]:[cloud%" PRId64 " - cloud%" PRId64
223 ") , worker2[primary key]:[%" PRId64 "- %" PRId64")", begin, count, begin, count);
224 std::this_thread::sleep_for(std::chrono::milliseconds(count));
225 }
226
GetCloudDbSchema(DataBaseSchema & dataBaseSchema)227 void GetCloudDbSchema(DataBaseSchema &dataBaseSchema)
228 {
229 TableSchema tableSchema1 = {
230 .name = g_tableName1,
231 .fields = g_cloudFiled1
232 };
233 TableSchema tableSchema2 = {
234 .name = g_tableName2,
235 .fields = g_cloudFiled2
236 };
237 TableSchema tableSchemaWithOutPrimaryKey = {
238 .name = g_tableName3,
239 .fields = g_cloudFiledWithOutPrimaryKey3
240 };
241 TableSchema tableSchema4 = {
242 .name = g_tableName4,
243 .fields = g_cloudFiled2
244 };
245 dataBaseSchema.tables.push_back(tableSchema1);
246 dataBaseSchema.tables.push_back(tableSchema2);
247 dataBaseSchema.tables.push_back(tableSchemaWithOutPrimaryKey);
248 dataBaseSchema.tables.push_back(tableSchema4);
249 }
250
QueryCountCallback(void * data,int count,char ** colValue,char ** colName)251 int QueryCountCallback(void *data, int count, char **colValue, char **colName)
252 {
253 if (count != 1) {
254 return 0;
255 }
256 auto expectCount = reinterpret_cast<int64_t>(data);
257 EXPECT_EQ(strtol(colValue[0], nullptr, 10), expectCount); // 10: decimal
258 return 0;
259 }
260
CheckCloudRecordNum(sqlite3 * & db,std::vector<std::string> tableList,std::vector<int> countList)261 void CheckCloudRecordNum(sqlite3 *&db, std::vector<std::string> tableList, std::vector<int> countList)
262 {
263 int i = 0;
264 for (const auto &tableName: tableList) {
265 std::string sql = "select count(*) from " + DBCommon::GetLogTableName(tableName) +
266 " where device = 'cloud'" + " and cloud_gid is not null and cloud_gid != '' and flag & 0x2 = 0;";
267 EXPECT_EQ(sqlite3_exec(db, sql.c_str(), QueryCountCallback,
268 reinterpret_cast<void *>(countList[i]), nullptr), SQLITE_OK);
269 i++;
270 }
271 }
272
CheckCleanLogNum(sqlite3 * & db,const std::vector<std::string> tableList,int count)273 void CheckCleanLogNum(sqlite3 *&db, const std::vector<std::string> tableList, int count)
274 {
275 for (const auto &tableName: tableList) {
276 std::string sql1 = "select count(*) from " + DBCommon::GetLogTableName(tableName) +
277 " where device = 'cloud';";
278 EXPECT_EQ(sqlite3_exec(db, sql1.c_str(), QueryCountCallback,
279 reinterpret_cast<void *>(count), nullptr), SQLITE_OK);
280 std::string sql2 = "select count(*) from " + DBCommon::GetLogTableName(tableName) +
281 " where cloud_gid " + " is not null and cloud_gid != '';";
282 EXPECT_EQ(sqlite3_exec(db, sql2.c_str(), QueryCountCallback,
283 reinterpret_cast<void *>(count), nullptr), SQLITE_OK);
284 std::string sql3 = "select count(*) from " + DBCommon::GetLogTableName(tableName) +
285 " where flag & 0x02 = 0;";
286 EXPECT_EQ(sqlite3_exec(db, sql3.c_str(), QueryCountCallback,
287 reinterpret_cast<void *>(count), nullptr), SQLITE_OK);
288 }
289 }
290
CheckCleanDataAndLogNum(sqlite3 * & db,const std::vector<std::string> tableList,int count,std::vector<int> localNum)291 void CheckCleanDataAndLogNum(sqlite3 *&db, const std::vector<std::string> tableList, int count,
292 std::vector<int> localNum)
293 {
294 int i = 0;
295 for (const auto &tableName: tableList) {
296 std::string sql1 = "select count(*) from " + DBCommon::GetLogTableName(tableName) +
297 " where device = 'cloud';";
298 EXPECT_EQ(sqlite3_exec(db, sql1.c_str(), QueryCountCallback,
299 reinterpret_cast<void *>(count), nullptr), SQLITE_OK);
300 std::string sql2 = "select count(*) from " + DBCommon::GetLogTableName(tableName) + " where cloud_gid "
301 " is not null and cloud_gid != '';";
302 EXPECT_EQ(sqlite3_exec(db, sql2.c_str(), QueryCountCallback,
303 reinterpret_cast<void *>(count), nullptr), SQLITE_OK);
304 std::string sql3 = "select count(*) from " + DBCommon::GetLogTableName(tableName) +
305 " where flag & 0x02 = 0;";
306 EXPECT_EQ(sqlite3_exec(db, sql3.c_str(), QueryCountCallback,
307 reinterpret_cast<void *>(count), nullptr), SQLITE_OK);
308 std::string local_sql = "select count(*) from " + tableName +";";
309 EXPECT_EQ(sqlite3_exec(db, local_sql.c_str(), QueryCountCallback,
310 reinterpret_cast<void *>(localNum[i]), nullptr), SQLITE_OK);
311 i++;
312 }
313 }
314
InitProcessForCleanCloudData1(const uint32_t & cloudCount,std::vector<SyncProcess> & expectProcess)315 void InitProcessForCleanCloudData1(const uint32_t &cloudCount, std::vector<SyncProcess> &expectProcess)
316 {
317 // cloudCount also means data count in one batch
318 expectProcess.clear();
319 std::vector<TableProcessInfo> infos;
320 uint32_t index = 1;
321 infos.push_back(TableProcessInfo{
322 FINISHED, {index, cloudCount, cloudCount, 0}, {0, 0, 0, 0}
323 });
324 infos.push_back(TableProcessInfo{
325 PREPARED, {0, 0, 0, 0}, {0, 0, 0, 0}
326 });
327
328 infos.push_back(TableProcessInfo{
329 FINISHED, {index, cloudCount, cloudCount, 0}, {0, 0, 0, 0}
330 });
331 infos.push_back(TableProcessInfo{
332 FINISHED, {index, cloudCount, cloudCount, 0}, {0, 0, 0, 0}
333 });
334
335 for (size_t i = 0; i < infos.size() / g_arrayHalfSub; ++i) {
336 SyncProcess syncProcess;
337 syncProcess.errCode = OK;
338 syncProcess.process = i == infos.size() ? FINISHED : PROCESSING;
339 syncProcess.tableProcess.insert_or_assign(g_tables[0], std::move(infos[g_arrayHalfSub * i]));
340 syncProcess.tableProcess.insert_or_assign(g_tables[1], std::move(infos[g_arrayHalfSub * i + 1]));
341 expectProcess.push_back(syncProcess);
342 }
343 }
344
GetCallback(SyncProcess & syncProcess,CloudSyncStatusCallback & callback,std::vector<SyncProcess> & expectProcess)345 void GetCallback(SyncProcess &syncProcess, CloudSyncStatusCallback &callback,
346 std::vector<SyncProcess> &expectProcess)
347 {
348 g_syncIndex = 0;
349 callback = [&syncProcess, &expectProcess](const std::map<std::string, SyncProcess> &process) {
350 LOGI("devices size = %d", process.size());
351 ASSERT_EQ(process.size(), 1u);
352 syncProcess = std::move(process.begin()->second);
353 ASSERT_EQ(process.begin()->first, DEVICE_CLOUD);
354 ASSERT_NE(syncProcess.tableProcess.empty(), true);
355 LOGI("current sync process status:%d, db status:%d ", syncProcess.process, syncProcess.errCode);
356 std::for_each(g_tables.begin(), g_tables.end(), [&](const auto &item) {
357 auto table1 = syncProcess.tableProcess.find(item);
358 if (table1 != syncProcess.tableProcess.end()) {
359 LOGI("table[%s], table process status:%d, [downloadInfo](batchIndex:%u, total:%u, successCount:%u, "
360 "failCount:%u) [uploadInfo](batchIndex:%u, total:%u, successCount:%u,failCount:%u",
361 item.c_str(), table1->second.process, table1->second.downLoadInfo.batchIndex,
362 table1->second.downLoadInfo.total, table1->second.downLoadInfo.successCount,
363 table1->second.downLoadInfo.failCount, table1->second.upLoadInfo.batchIndex,
364 table1->second.upLoadInfo.total, table1->second.upLoadInfo.successCount,
365 table1->second.upLoadInfo.failCount);
366 }
367 });
368 if (expectProcess.empty()) {
369 if (syncProcess.process == FINISHED) {
370 g_processCondition.notify_one();
371 }
372 return;
373 }
374 ASSERT_LE(static_cast<size_t>(g_syncIndex), expectProcess.size());
375 for (size_t i = 0; i < g_tables.size(); ++i) {
376 SyncProcess head = expectProcess[g_syncIndex];
377 for (auto &expect : head.tableProcess) {
378 auto real = syncProcess.tableProcess.find(expect.first);
379 ASSERT_NE(real, syncProcess.tableProcess.end());
380 EXPECT_EQ(expect.second.process, real->second.process);
381 EXPECT_EQ(expect.second.downLoadInfo.batchIndex, real->second.downLoadInfo.batchIndex);
382 EXPECT_EQ(expect.second.downLoadInfo.total, real->second.downLoadInfo.total);
383 EXPECT_EQ(expect.second.downLoadInfo.successCount, real->second.downLoadInfo.successCount);
384 EXPECT_EQ(expect.second.downLoadInfo.failCount, real->second.downLoadInfo.failCount);
385 EXPECT_EQ(expect.second.upLoadInfo.batchIndex, real->second.upLoadInfo.batchIndex);
386 EXPECT_EQ(expect.second.upLoadInfo.total, real->second.upLoadInfo.total);
387 EXPECT_EQ(expect.second.upLoadInfo.successCount, real->second.upLoadInfo.successCount);
388 EXPECT_EQ(expect.second.upLoadInfo.failCount, real->second.upLoadInfo.failCount);
389 }
390 }
391 g_syncIndex++;
392 if (syncProcess.process == FINISHED) {
393 g_processCondition.notify_one();
394 }
395 };
396 }
397
WaitForSyncFinish(SyncProcess & syncProcess,const int64_t & waitTime)398 void WaitForSyncFinish(SyncProcess &syncProcess, const int64_t &waitTime)
399 {
400 std::unique_lock<std::mutex> lock(g_processMutex);
401 bool result = g_processCondition.wait_for(lock, std::chrono::seconds(waitTime), [&syncProcess]() {
402 return syncProcess.process == FINISHED;
403 });
404 ASSERT_EQ(result, true);
405 LOGD("-------------------sync end--------------");
406 }
407
CloseDb()408 void CloseDb()
409 {
410 delete g_observer;
411 g_virtualCloudDb = nullptr;
412 if (g_delegate != nullptr) {
413 EXPECT_EQ(g_mgr.CloseStore(g_delegate), DBStatus::OK);
414 g_delegate = nullptr;
415 }
416 }
417
418 class DistributedDBCloudInterfacesRelationalRemoveDeviceDataTest : public testing::Test {
419 public:
420 static void SetUpTestCase(void);
421 static void TearDownTestCase(void);
422 void SetUp();
423 void TearDown();
424 protected:
425 sqlite3 *db = nullptr;
426 };
427
SetUpTestCase(void)428 void DistributedDBCloudInterfacesRelationalRemoveDeviceDataTest::SetUpTestCase(void)
429 {
430 DistributedDBToolsUnitTest::TestDirInit(g_testDir);
431 g_storePath = g_testDir + "/" + g_storeID + DB_SUFFIX;
432 LOGI("The test db is:%s", g_testDir.c_str());
433 RuntimeConfig::SetCloudTranslate(std::make_shared<VirtualCloudDataTranslate>());
434 }
435
TearDownTestCase(void)436 void DistributedDBCloudInterfacesRelationalRemoveDeviceDataTest::TearDownTestCase(void)
437 {}
438
SetUp(void)439 void DistributedDBCloudInterfacesRelationalRemoveDeviceDataTest::SetUp(void)
440 {
441 if (DistributedDBToolsUnitTest::RemoveTestDbFiles(g_testDir) != 0) {
442 LOGE("rm test db files error.");
443 }
444 DistributedDBToolsUnitTest::PrintTestCaseInfo();
445 LOGD("Test dir is %s", g_testDir.c_str());
446 db = RelationalTestUtils::CreateDataBase(g_storePath);
447 ASSERT_NE(db, nullptr);
448 CreateUserDBAndTable(db);
449 g_observer = new (std::nothrow) RelationalStoreObserverUnitTest();
450 ASSERT_NE(g_observer, nullptr);
451 ASSERT_EQ(g_mgr.OpenStore(g_storePath, g_storeID, RelationalStoreDelegate::Option { .observer = g_observer },
452 g_delegate), DBStatus::OK);
453 ASSERT_NE(g_delegate, nullptr);
454 ASSERT_EQ(g_delegate->CreateDistributedTable(g_tableName1, CLOUD_COOPERATION), DBStatus::OK);
455 ASSERT_EQ(g_delegate->CreateDistributedTable(g_tableName2, CLOUD_COOPERATION), DBStatus::OK);
456 ASSERT_EQ(g_delegate->CreateDistributedTable(g_tableName3, CLOUD_COOPERATION), DBStatus::OK);
457 g_virtualCloudDb = make_shared<VirtualCloudDb>();
458 g_virtualAssetLoader = make_shared<VirtualAssetLoader>();
459 g_syncProcess = {};
460 ASSERT_EQ(g_delegate->SetCloudDB(g_virtualCloudDb), DBStatus::OK);
461 ASSERT_EQ(g_delegate->SetIAssetLoader(g_virtualAssetLoader), DBStatus::OK);
462 // sync before setting cloud db schema,it should return SCHEMA_MISMATCH
463 Query query = Query::Select().FromTable(g_tables);
464 CloudSyncStatusCallback callback;
465 ASSERT_EQ(g_delegate->Sync({DEVICE_CLOUD}, SYNC_MODE_CLOUD_MERGE, query, callback, g_syncWaitTime),
466 DBStatus::SCHEMA_MISMATCH);
467 DataBaseSchema dataBaseSchema;
468 GetCloudDbSchema(dataBaseSchema);
469 ASSERT_EQ(g_delegate->SetCloudDbSchema(dataBaseSchema), DBStatus::OK);
470 }
471
TearDown(void)472 void DistributedDBCloudInterfacesRelationalRemoveDeviceDataTest::TearDown(void)
473 {
474 EXPECT_EQ(sqlite3_close_v2(db), SQLITE_OK);
475 if (DistributedDBToolsUnitTest::RemoveTestDbFiles(g_testDir) != 0) {
476 LOGE("rm test db files error.");
477 }
478 }
479
480 /*
481 * @tc.name: CleanCloudDataTest001
482 * @tc.desc: Test FLAG_ONLY mode of RemoveDeviceData, and invalid mode else.
483 * @tc.type: FUNC
484 * @tc.require:
485 * @tc.author: huangboxin
486 */
487 HWTEST_F(DistributedDBCloudInterfacesRelationalRemoveDeviceDataTest, CleanCloudDataTest001, TestSize.Level0)
488 {
489 int64_t paddingSize = 10;
490 int localCount = 10;
491 int cloudCount = 20;
492 InsertCloudTableRecord(0, cloudCount, paddingSize, false);
493 InsertUserTableRecord(db, 0, localCount, paddingSize, false);
494 Query query = Query::Select().FromTable(g_tables);
495 std::vector<SyncProcess> expectProcess;
496 InitProcessForCleanCloudData1(cloudCount, expectProcess);
497 CloudSyncStatusCallback callback;
498 GetCallback(g_syncProcess, callback, expectProcess);
499 ASSERT_EQ(g_delegate->Sync({DEVICE_CLOUD}, SYNC_MODE_CLOUD_FORCE_PULL, query, callback, g_syncWaitTime),
500 DBStatus::OK);
501 WaitForSyncFinish(g_syncProcess, g_syncWaitTime);
502 std::string device = "";
503 CheckCloudRecordNum(db, g_tables, {20, 20});
504 ASSERT_EQ(g_delegate->RemoveDeviceData(device, FLAG_ONLY), DBStatus::OK);
505 CheckCleanLogNum(db, g_tables, 0);
506
507 ASSERT_EQ(g_delegate->RemoveDeviceData(device, ClearMode(BUTT + 1)), DBStatus::INVALID_ARGS);
508 ASSERT_EQ(g_delegate->RemoveDeviceData(device, ClearMode(-1)), DBStatus::INVALID_ARGS);
509
510 CloseDb();
511 }
512
513 /*
514 * @tc.name: CleanCloudDataTest002
515 * @tc.desc: Test FLAG_AND_DATA mode of RemoveDeviceData
516 * @tc.type: FUNC
517 * @tc.require:
518 * @tc.author: huangboxin
519 */
520 HWTEST_F(DistributedDBCloudInterfacesRelationalRemoveDeviceDataTest, CleanCloudDataTest002, TestSize.Level0)
521 {
522 int64_t paddingSize = 10;
523 int localCount = 10;
524 int cloudCount = 20;
525 InsertCloudTableRecord(0, cloudCount, paddingSize, false);
526 InsertUserTableRecord(db, 0, localCount, paddingSize, false);
527 Query query = Query::Select().FromTable(g_tables);
528 std::vector<SyncProcess> expectProcess;
529 InitProcessForCleanCloudData1(cloudCount, expectProcess);
530 CloudSyncStatusCallback callback;
531 GetCallback(g_syncProcess, callback, expectProcess);
532 ASSERT_EQ(g_delegate->Sync({DEVICE_CLOUD}, SYNC_MODE_CLOUD_FORCE_PULL, query, callback, g_syncWaitTime),
533 DBStatus::OK);
534 WaitForSyncFinish(g_syncProcess, g_syncWaitTime);
535 std::string device = "";
536 CheckCloudRecordNum(db, g_tables, {20, 20}); // 20 means cloud data num
537 ASSERT_EQ(g_delegate->RemoveDeviceData(device, FLAG_AND_DATA), DBStatus::OK);
538 CheckCleanDataAndLogNum(db, g_tables, 0, {localCount, 0});
539 CloseDb();
540 }
541
542 /*
543 * @tc.name: CleanCloudDataTest003
544 * @tc.desc: Test FLAG_ONLY mode of RemoveDeviceData concurrently with Sync
545 * @tc.type: FUNC
546 * @tc.require:
547 * @tc.author: huangboxin
548 */
549 HWTEST_F(DistributedDBCloudInterfacesRelationalRemoveDeviceDataTest, CleanCloudDataTest003, TestSize.Level0)
550 {
551 /**
552 * @tc.steps: step1. make data: 10 records on local and 20 records on cloud
553 */
554 int64_t paddingSize = 10;
555 int localCount = 10;
556 int cloudCount = 20;
557 InsertCloudTableRecord(0, cloudCount, paddingSize, false);
558 InsertUserTableRecord(db, 0, localCount, paddingSize, false);
559 /**
560 * @tc.steps: step2. call Sync with cloud force pull strategy, and after that, local will has 20 records.
561 */
562 Query query = Query::Select().FromTable(g_tables);
563 std::vector<SyncProcess> expectProcess;
564 InitProcessForCleanCloudData1(cloudCount, expectProcess);
565 CloudSyncStatusCallback callback;
566 GetCallback(g_syncProcess, callback, expectProcess);
567 ASSERT_EQ(g_delegate->Sync({DEVICE_CLOUD}, SYNC_MODE_CLOUD_FORCE_PULL, query, callback, g_syncWaitTime),
568 DBStatus::OK);
569 WaitForSyncFinish(g_syncProcess, g_syncWaitTime);
570 CheckCloudRecordNum(db, g_tables, {20, 20}); // 20 means cloud data num
571
572 /**
573 * @tc.steps: step3. insert 10 records into local, so local will has 20 local records and 20 cloud records.
574 */
575 InsertUserTableRecord(db, 21, localCount, paddingSize, false); // 21 means insert start index
576 /**
577 * @tc.steps: step4. call RemoveDeviceData synchronize with Sync with cloud force push strategy.
578 */
579 g_syncProcess = {};
580 std::vector<SyncProcess> expectProcess2;
581 InitProcessForCleanCloudData1(cloudCount, expectProcess2);
582 CloudSyncStatusCallback callback2;
583 GetCallback(g_syncProcess, callback2, expectProcess2);
584 std::string device = "";
585
__anon93bd12370502() 586 std::thread thread1([&]() {
587 ASSERT_EQ(g_delegate->RemoveDeviceData(device, FLAG_AND_DATA), DBStatus::OK);
588 });
__anon93bd12370602() 589 std::thread thread2([&]() {
590 ASSERT_EQ(g_delegate->Sync({DEVICE_CLOUD}, SYNC_MODE_CLOUD_FORCE_PULL, query, callback2, g_syncWaitTime),
591 DBStatus::OK);
592 LOGD("-------------------sync end--------------");
593 });
594 thread1.join();
595 thread2.join();
596 WaitForSyncFinish(g_syncProcess, g_syncWaitTime);
597 CheckCleanLogNum(db, g_tables, 20);
598 LOGD("================================== test clean cloud data 003 end ===================================");
599 CloseDb();
600 }
601
InitGetCloudSyncTaskCountTest001(sqlite3 * & db)602 static void InitGetCloudSyncTaskCountTest001(sqlite3 *&db)
603 {
604 int64_t localCount = 20;
605 int64_t cloudCount = 10;
606 int64_t paddingSize = 100;
607 InsertUserTableRecord(db, 0, localCount, paddingSize, false);
608 InsertCloudTableRecord(0, cloudCount, paddingSize, false);
609 }
610 /*
611 * @tc.name: GetCloudSyncTaskCountTest001
612 * @tc.desc: Test FLAG_ONLY mode of RemoveDeviceData concurrently with Sync
613 * @tc.type: FUNC
614 * @tc.require:
615 * @tc.author: huangboxin
616 */
617 HWTEST_F(DistributedDBCloudInterfacesRelationalRemoveDeviceDataTest, GetCloudSyncTaskCountTest001, TestSize.Level0)
618 {
619 InitGetCloudSyncTaskCountTest001(db);
620 Query query = Query::Select().FromTable(g_tables);
621 std::mutex dataMutex1, dataMutex2;
622 std::condition_variable cv1, cv2;
623 bool finish1 = false, finish2 = false;
624 /**
625 * @tc.steps: step1. Call Sync once.
626 * @tc.expected: OK.
627 */
628 CloudSyncStatusCallback callback1 = [&dataMutex1, &cv1, &finish1](
__anon93bd12370702( const std::map<std::string, SyncProcess> &process) 629 const std::map<std::string, SyncProcess> &process) {
630 std::map<std::string, SyncProcess> syncProcess;
631 {
632 std::lock_guard<std::mutex> autoLock(dataMutex1);
633 syncProcess = process;
634 if (syncProcess[DEVICE_CLOUD].process == FINISHED) {
635 finish1 = true;
636 }
637 }
638 cv1.notify_one();
639 };
640 /**
641 * @tc.steps: step2. Call Sync twice.
642 * @tc.expected: OK.
643 */
644 ASSERT_EQ(g_delegate->Sync({DEVICE_CLOUD}, SYNC_MODE_CLOUD_MERGE, query, callback1, g_syncWaitTime), DBStatus::OK);
645
646 CloudSyncStatusCallback callback2 = [&dataMutex2, &cv2, &finish2](
__anon93bd12370802( const std::map<std::string, SyncProcess> &process) 647 const std::map<std::string, SyncProcess> &process) {
648 std::map<std::string, SyncProcess> syncProcess;
649 {
650 std::lock_guard<std::mutex> autoLock(dataMutex2);
651 syncProcess = process;
652 if (syncProcess[DEVICE_CLOUD].process == FINISHED) {
653 finish2 = true;
654 }
655 }
656 cv2.notify_one();
657 };
658 ASSERT_EQ(g_delegate->Sync({DEVICE_CLOUD}, SYNC_MODE_CLOUD_MERGE, query, callback2, g_syncWaitTime), DBStatus::OK);
659 /**
660 * @tc.steps: step3. Call Get Cloud Sync Task Count
661 * @tc.expected: OK.
662 */
663 EXPECT_EQ(g_delegate->GetCloudSyncTaskCount(), 2); // 2 is task count
664 /**
665 * @tc.steps: step3. Wait For Sync Task Finished
666 * @tc.expected: OK.
667 */
668 {
669 std::unique_lock<std::mutex> uniqueLock(dataMutex1);
__anon93bd12370902null670 cv1.wait(uniqueLock, [&finish1] {
671 return finish1;
672 });
673 }
674 {
675 std::unique_lock<std::mutex> uniqueLock(dataMutex2);
__anon93bd12370a02null676 cv2.wait(uniqueLock, [&finish2] {
677 return finish2;
678 });
679 }
680 CloseDb();
681 }
682
683 /*
684 * @tc.name: CleanCloudDataTest004
685 * @tc.desc: Test RemoveDeviceData when cloudSchema doesn't have local table
686 * @tc.type: FUNC
687 * @tc.require:
688 * @tc.author: huangboxin
689 */
690 HWTEST_F(DistributedDBCloudInterfacesRelationalRemoveDeviceDataTest, CleanCloudDataTest004, TestSize.Level0)
691 {
692 DataBaseSchema dataBaseSchema;
693 TableSchema tableSchema1 = {
694 .name = "table_not_existed",
695 .fields = g_cloudFiled1
696 };
697 dataBaseSchema.tables.push_back(tableSchema1);
698 GetCloudDbSchema(dataBaseSchema);
699 ASSERT_EQ(g_delegate->SetCloudDbSchema(dataBaseSchema), DBStatus::OK);
700 std::string device = "";
701 ASSERT_EQ(g_delegate->RemoveDeviceData(device, FLAG_AND_DATA), DBStatus::OK);
702 CloseDb();
703 }
704
705 }
706 #endif // RELATIONAL_STORE