1 /*
2 * Copyright (c) 2025 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 #include <gtest/gtest.h>
16
17 #include <atomic>
18 #include <iostream>
19 #include <random>
20 #include <string>
21 #include <thread>
22
23 #include "gdb_errors.h"
24 #include "executor_pool.h"
25 #include "grd_adapter_manager.h"
26 #include "gdb_helper.h"
27 #include "gdb_store.h"
28 #include "path.h"
29
30 using namespace testing::ext;
31 using namespace OHOS::DistributedDataAip;
32 class GdbMultiThreadTest : public testing::Test {
33 public:
34 static void SetUpTestCase(void);
35 static void TearDownTestCase(void);
36 void SetUp();
37 void TearDown();
38 void VerifyPersonInfo(const GraphValue &person, const std::string &name, const int32_t &age);
39 void MatchAndVerifyPerson(const std::string &name, const int32_t &age, std::shared_ptr<DBStore> store);
40 void MultiThreadExecuteInsert(std::shared_ptr<DBStore> store);
41 void MultiThreadExecuteDBStore();
42 void MultiThreadInsertRelation(std::shared_ptr<DBStore> store);
43 void MultiThreadQueryRelation(std::shared_ptr<DBStore> store);
44 std::string RandomString(size_t length);
45
46 protected:
47 std::atomic<int> counter;
48 static const std::string databaseName;
49 static const std::string databasePath;
50 static const std::string createGraphGql;
51 std::shared_ptr<OHOS::ExecutorPool> executors_;
52 };
53 const std::string GdbMultiThreadTest::databaseName = "execute_test";
54 const std::string GdbMultiThreadTest::databasePath = "/data";
55 const std::string GdbMultiThreadTest::createGraphGql = "CREATE GRAPH test { "
56 "(person:Person {name STRING, age INT, sex BOOL DEFAULT false}),"
57 "(dog:Dog {name STRING, age INT}), "
58 "(person) -[:Friend]-> (person) "
59 "};";
60
SetUpTestCase(void)61 void GdbMultiThreadTest::SetUpTestCase(void)
62 {
63 if (!IsSupportArkDataDb()) {
64 GTEST_SKIP() << "Current testcase is not compatible from current gdb";
65 return;
66 }
67 }
68
TearDownTestCase(void)69 void GdbMultiThreadTest::TearDownTestCase(void)
70 {
71 }
72
SetUp()73 void GdbMultiThreadTest::SetUp()
74 {
75 if (!IsSupportArkDataDb()) {
76 GTEST_SKIP() << "Current testcase is not compatible from current gdb";
77 return;
78 }
79 int32_t maxThread = 5;
80 int32_t minThread = 0;
81 executors_ = std::make_shared<OHOS::ExecutorPool>(maxThread, minThread);
82 }
83
TearDown()84 void GdbMultiThreadTest::TearDown()
85 {
86 executors_ = nullptr;
87 }
88
89 /**
90 * @tc.name: MultiThread_GetDBStore_0001
91 * @tc.desc: Start two threads to open the database.
92 * @tc.type: FUNC
93 */
94 HWTEST_F(GdbMultiThreadTest, MultiThread_GetDBStore_0001, TestSize.Level2)
95 {
96 counter.store(0);
97 MultiThreadExecuteDBStore();
98 MultiThreadExecuteDBStore();
99 while (true) {
100 if (counter.load() == 100) {
101 break;
102 }
103 }
104 }
105
106 /**
107 * @tc.name: MultiThreadExecuteDBStore
108 * @tc.desc: Start the database in the current thread for 50 times.
109 * @tc.type: FUNC
110 */
MultiThreadExecuteDBStore()111 void GdbMultiThreadTest::MultiThreadExecuteDBStore()
112 {
113 executors_->Execute([this]() {
114 int errCode = E_OK;
115 std::string dbName = RandomString(10) + RandomString(10);
116 constexpr int32_t COUNT = 50;
117 for (uint32_t j = 0; j < COUNT; j++) {
118 auto config = StoreConfig(dbName + std::to_string(j), databasePath);
119 GDBHelper::DeleteDBStore(config);
120 auto store = GDBHelper::GetDBStore(config, errCode);
121 auto result = store->ExecuteGql(createGraphGql);
122 EXPECT_NE(store, nullptr);
123 EXPECT_EQ(result.first, E_OK);
124 ASSERT_NE(result.second, nullptr);
125
126 result = store->ExecuteGql("DROP GRAPH test");
127 EXPECT_NE(store, nullptr);
128 EXPECT_EQ(result.first, E_OK);
129 GDBHelper::DeleteDBStore(config);
130 counter.fetch_add(1, std::memory_order_relaxed);
131 }
132 });
133 }
134
135 /**
136 * @tc.name: MultiThread_GetDBStore_0002
137 * @tc.desc: Start two threads to test the insertion Vertex and Edge.
138 * @tc.type: FUNC
139 */
140 HWTEST_F(GdbMultiThreadTest, MultiThread_GetDBStore_0002, TestSize.Level2)
141 {
142 int errCode = E_OK;
143 auto config = StoreConfig(databaseName + "test22", databasePath);
144 auto store = GDBHelper::GetDBStore(config, errCode);
145 auto result = store->ExecuteGql(createGraphGql);
146 EXPECT_NE(store, nullptr);
147 EXPECT_EQ(result.first, E_OK);
148 ASSERT_NE(result.second, nullptr);
149 counter.store(0);
150 MultiThreadExecuteInsert(store);
151 sleep(1);
152 MultiThreadExecuteInsert(store);
153 while (true) {
154 // counter ++ 200
155 if (counter.load() == 200) {
156 break;
157 }
158 }
159 sleep(1);
160 result = store->ExecuteGql("DROP GRAPH test");
161 EXPECT_NE(store, nullptr);
162 EXPECT_EQ(result.first, E_OK);
163 GDBHelper::DeleteDBStore(config);
164 }
165
166 /**
167 * @tc.name: MultiThreadExecuteInsert
168 * @tc.desc: Start threads to test the insertion Vertex and Edge.
169 * @tc.type: FUNC
170 */
MultiThreadExecuteInsert(std::shared_ptr<DBStore> store)171 void GdbMultiThreadTest::MultiThreadExecuteInsert(std::shared_ptr<DBStore> store)
172 {
173 executors_->Execute([this, &store]() {
174 std::string nameStr = RandomString(10) + RandomString(10);
175 constexpr int32_t MAX_COUNT = 100;
176 for (uint32_t j = 0; j < MAX_COUNT; j++) {
177 std::string name = nameStr + std::to_string(j);
178 auto result = store->ExecuteGql("INSERT (:Person {name: '" + name + "', age: 11});");
179 EXPECT_EQ(result.first, E_OK);
180 constexpr int32_t AGE_11 = 11;
181 MatchAndVerifyPerson(name, AGE_11, store);
182
183 std::string name2 = name + std::to_string(j);
184 result = store->ExecuteGql("INSERT (:Person {name: '" + name2 + "', age: 22});");
185 EXPECT_EQ(result.first, E_OK);
186 constexpr int32_t AGE = 22;
187 MatchAndVerifyPerson(name2, AGE, store);
188
189 std::string name3 = name2 + std::to_string(j);
190 result = store->ExecuteGql("INSERT (:Person {name: '" + name3 + "', age: 33});");
191 EXPECT_EQ(result.first, E_OK);
192 constexpr int32_t AGE_33 = 33;
193 MatchAndVerifyPerson(name3, AGE_33, store);
194
195 result = store->ExecuteGql("MATCH (p1:Person {name: '" + name + "'}), (p2:Person {name: '" + name2 +
196 "'}) INSERT (p1)-[:Friend]->(p2);");
197 EXPECT_EQ(result.first, E_OK);
198 result = store->ExecuteGql("MATCH (p1:Person {name: '" + name2 + "'}), (p2:Person {name: '" + name3 +
199 "'}) INSERT (p1)-[:Friend]->(p2);");
200 EXPECT_EQ(result.first, E_OK);
201
202 result = store->QueryGql(
203 "MATCH (person:Person {name: '" + name + "'})-[relation:Friend]->() RETURN person, relation;");
204 ASSERT_EQ(result.first, E_OK);
205
206 auto result2 = store->QueryGql("MATCH path=(a:Person {name: '" + name +
207 "'})-[]->{2, 2}(b:Person {name: '" + name3 + "'}) RETURN path;");
208 ASSERT_EQ(result2.first, E_OK);
209 counter.fetch_add(1, std::memory_order_relaxed);
210 }
211 });
212 }
213
214 /**
215 * @tc.name: MultiThread_GetDBStore_0003
216 * @tc.desc: Start two threads to insert edges of the same Vertex, and start another thread to query edges.
217 * @tc.type: FUNC
218 */
219 HWTEST_F(GdbMultiThreadTest, MultiThread_GetDBStore_0003, TestSize.Level2)
220 {
221 int errCode = E_OK;
222 auto config = StoreConfig(databaseName + "test33", databasePath);
223 auto store = GDBHelper::GetDBStore(config, errCode);
224 auto result = store->ExecuteGql(createGraphGql);
225 EXPECT_NE(store, nullptr);
226 EXPECT_EQ(result.first, E_OK);
227 ASSERT_NE(result.second, nullptr);
228 counter.store(0);
229
230 std::string name = "zhangsan";
231 result = store->ExecuteGql("INSERT (:Person {name: '" + name + "', age: 11});");
232 EXPECT_EQ(result.first, E_OK);
233 constexpr int32_t AGE_11 = 11;
234 MatchAndVerifyPerson(name, AGE_11, store);
235 //insert Vertex and edge
236 MultiThreadInsertRelation(store);
237 MultiThreadInsertRelation(store);
238 sleep(1);
239 //query Vertex and edge
240 MultiThreadExecuteInsert(store);
241 while (true) {
242 if (counter.load() == 300) {
243 break;
244 }
245 }
246 // query match count is 200
247 result =
248 store->QueryGql("MATCH (person:Person {name: '" + name + "'})-[relation:Friend]->() RETURN person, relation;");
249 ASSERT_EQ(result.first, E_OK);
250 EXPECT_EQ(result.second->GetAllData().size(), 200);
251
252 result = store->ExecuteGql("DROP GRAPH test");
253 EXPECT_NE(store, nullptr);
254 EXPECT_EQ(result.first, E_OK);
255 GDBHelper::DeleteDBStore(config);
256 }
257
MultiThreadInsertRelation(std::shared_ptr<DBStore> store)258 void GdbMultiThreadTest::MultiThreadInsertRelation(std::shared_ptr<DBStore> store)
259 {
260 executors_->Execute([this, &store]() {
261 std::string nameStr = RandomString(10) + RandomString(10);
262 std::string name = "zhangsan";
263 constexpr int32_t MAX_COUNT = 100;
264 for (uint32_t j = 0; j < MAX_COUNT; j++) {
265 std::string name2 = nameStr + std::to_string(j);
266 auto result = store->ExecuteGql("INSERT (:Person {name: '" + name2 + "', age: 22});");
267 EXPECT_EQ(result.first, E_OK);
268 constexpr int32_t AGE = 22;
269 MatchAndVerifyPerson(name2, AGE, store);
270
271 result = store->ExecuteGql("MATCH (p1:Person {name: '" + name + "'}), (p2:Person {name: '" + name2 +
272 "'}) INSERT (p1)-[:Friend]->(p2);");
273 EXPECT_EQ(result.first, E_OK);
274 counter.fetch_add(1, std::memory_order_relaxed);
275 }
276 });
277 }
278
MultiThreadQueryRelation(std::shared_ptr<DBStore> store)279 void GdbMultiThreadTest::MultiThreadQueryRelation(std::shared_ptr<DBStore> store)
280 {
281 executors_->Execute([this, &store]() {
282 std::string name = "zhangsan";
283 constexpr int32_t MAX_COUNT = 100;
284 for (uint32_t j = 0; j < MAX_COUNT; j++) {
285 auto result = store->QueryGql(
286 "MATCH (person:Person {name: '" + name + "'})-[relation:Friend]->() RETURN person, relation;");
287 ASSERT_EQ(result.first, E_OK);
288 counter.fetch_add(1, std::memory_order_relaxed);
289 }
290 });
291 }
292
RandomString(size_t length)293 std::string GdbMultiThreadTest::RandomString(size_t length)
294 {
295 const std::string letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
296 std::random_device rd;
297 std::mt19937 gen(rd());
298 std::uniform_int_distribution<> dis(0, letters.size() - 1);
299
300 std::string randomStr;
301 for (size_t i = 0; i < length; ++i) {
302 randomStr.push_back(letters[dis(gen)]);
303 }
304 return randomStr;
305 }
306
MatchAndVerifyPerson(const std::string & name,const int32_t & age,std::shared_ptr<DBStore> store)307 void GdbMultiThreadTest::MatchAndVerifyPerson(
308 const std::string &name, const int32_t &age, std::shared_ptr<DBStore> store)
309 {
310 EXPECT_NE(store, nullptr);
311 auto gql = "MATCH (person:Person {name: '" + name + "', age: " + std::to_string(age) + "}) RETURN person;";
312 auto result = store->QueryGql(gql);
313 ASSERT_EQ(result.first, E_OK);
314 ASSERT_NE(result.second, nullptr);
315 ASSERT_EQ(result.second->GetAllData().size(), 1);
316 GraphValue person = result.second->GetAllData()[0]["person"];
317 VerifyPersonInfo(person, name, age);
318 }
319
VerifyPersonInfo(const GraphValue & person,const std::string & name,const int32_t & age)320 void GdbMultiThreadTest::VerifyPersonInfo(const GraphValue &person, const std::string &name, const int32_t &age)
321 {
322 auto expectSize = 3;
323 ASSERT_TRUE(std::holds_alternative<std::shared_ptr<Vertex>>(person));
324 auto personVertex = std::get<std::shared_ptr<Vertex>>(person);
325 EXPECT_EQ(personVertex->GetLabel(), "Person");
326 ASSERT_EQ(personVertex->GetProperties().size(), expectSize);
327
328 auto nameDb = personVertex->GetProperties().find("name");
329 ASSERT_NE(nameDb, personVertex->GetProperties().end());
330 ASSERT_TRUE(std::holds_alternative<std::string>(nameDb->second));
331 EXPECT_EQ(std::get<std::string>(nameDb->second), name);
332
333 auto ageDb = personVertex->GetProperties().find("age");
334 ASSERT_NE(ageDb, personVertex->GetProperties().end());
335 ASSERT_TRUE(std::holds_alternative<int64_t>(ageDb->second));
336 EXPECT_EQ(std::get<int64_t>(ageDb->second), age);
337
338 auto sex = personVertex->GetProperties().find("sex");
339 ASSERT_NE(sex, personVertex->GetProperties().end());
340 ASSERT_TRUE(std::holds_alternative<int64_t>(sex->second));
341 EXPECT_EQ(std::get<int64_t>(sex->second), 0);
342 }