1 /*
2  * Copyright (c) 2021 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 <fstream>
17 #include <gtest/gtest.h>
18 
19 #include "db_errno.h"
20 #include "distributeddb_tools_unit_test.h"
21 #include "package_file.h"
22 #include "platform_specific.h"
23 #include "securec.h"
24 #include "value_hash_calc.h"
25 
26 using namespace testing::ext;
27 using namespace DistributedDB;
28 using namespace DistributedDBUnitTest;
29 using namespace std;
30 
31 namespace {
32     string g_testPath;
33     string g_sourcePath = "/source/";
34     string g_packageResultPath = "/package_result/";
35     string g_unpackResultPath = "/unpack_result/";
36     FileInfo g_fileInfo;
37     const string PACKAGE_RESULT_FILE_NAME = "package_result.dat";
38     const string NON_EXIST_PATH = "/nonexist/";
39     const string FILE_NAME_1 = "file1.txt";
40     const string FILE_NAME_2 = "file2.dat";
41     const string FILE_CONTENT_1 = "Hello world.";
42     const int FILE_CONTENT_2_LEN = 4;
43     const char FILE_CONTENT_2[FILE_CONTENT_2_LEN] = {0x5B, 0x3A, 0x29, 0x3E};
44     const int BUFFER_SIZE = 4096;
45     const int DEVICE_ID_LEN = 32;
46     const vector<uint8_t> DIVICE_ID = {'a', 'e', 'i', 'o', 'u'};
47 
RemovePath(const string & path)48     void RemovePath(const string &path)
49     {
50         list<OS::FileAttr> files;
51         int errCode = OS::GetFileAttrFromPath(path, files);
52         ASSERT_EQ(errCode, E_OK);
53         for (auto file : files) {
54             string fileName = path + "/" + file.fileName;
55             switch (file.fileType) {
56                 case OS::FILE:
57                     (void)remove(fileName.c_str());
58                     break;
59                 case OS::PATH:
60                     if (file.fileName != "." && file.fileName != "..") {
61                         RemovePath(fileName);
62                     }
63                     break;
64                 default:
65                     break;
66             }
67         }
68         (void)OS::RemoveDBDirectory(path);
69     }
70 
CompareFileName(const OS::FileAttr & file1,const OS::FileAttr & file2)71     bool CompareFileName(const OS::FileAttr &file1, const OS::FileAttr &file2)
72     {
73         return file1.fileName <= file2.fileName;
74     }
75 
ComparePath(const string & path1,const string & path2)76     void ComparePath(const string &path1, const string &path2)
77     {
78         list<OS::FileAttr> files1;
79         int errCode = OS::GetFileAttrFromPath(path1, files1);
80         ASSERT_EQ(errCode, E_OK);
81         files1.sort(CompareFileName);
82         list<OS::FileAttr> files2;
83         errCode = OS::GetFileAttrFromPath(path2, files2);
84         ASSERT_EQ(errCode, E_OK);
85         files2.sort(CompareFileName);
86         ASSERT_EQ(files1.size(), files2.size());
87         auto fileIter1 = files1.begin();
88         auto fileIter2 = files2.begin();
89         vector<char> buffer1(BUFFER_SIZE, 0);
90         vector<char> buffer2(BUFFER_SIZE, 0);
91         string bufferStr1;
92         string bufferStr2;
93         for (; fileIter1 != files1.end() && fileIter2 != files2.end(); fileIter1++, fileIter2++) {
94             ASSERT_STREQ(fileIter1->fileName.c_str(), fileIter2->fileName.c_str());
95             ASSERT_EQ(fileIter1->fileType, fileIter2->fileType);
96             ASSERT_EQ(fileIter1->fileLen, fileIter2->fileLen);
97             if (fileIter1->fileType != OS::FILE) {
98                 continue;
99             }
100             ifstream file1(path1 + fileIter1->fileName, ios::out | ios::binary);
101             ASSERT_TRUE(file1.is_open());
102             ifstream file2(path2 + fileIter2->fileName, ios::out | ios::binary);
103             ASSERT_TRUE(file2.is_open());
104             buffer1.assign(BUFFER_SIZE, 0);
105             buffer2.assign(BUFFER_SIZE, 0);
106             for (file1.read(buffer1.data(), BUFFER_SIZE), file2.read(buffer2.data(), BUFFER_SIZE);
107                 !(file1.eof() || file2.eof());
108                 file1.read(buffer1.data(), BUFFER_SIZE), file2.read(buffer2.data(), BUFFER_SIZE)) {
109                 bufferStr1.assign(buffer1.begin(), buffer1.end());
110                 bufferStr2.assign(buffer2.begin(), buffer2.end());
111                 ASSERT_STREQ(bufferStr1.c_str(), bufferStr2.c_str());
112             }
113             file1.close();
114             file2.close();
115             bufferStr1.assign(buffer1.begin(), buffer1.end());
116             bufferStr2.assign(buffer2.begin(), buffer2.end());
117             ASSERT_STREQ(bufferStr1.c_str(), bufferStr2.c_str());
118         }
119     }
120 }
121 
122 class DistributedDBFilePackageTest : public testing::Test {
123 public:
124     static void SetUpTestCase(void);
125     static void TearDownTestCase(void);
126     void SetUp();
127     void TearDown();
128 };
129 
SetUpTestCase(void)130 void DistributedDBFilePackageTest::SetUpTestCase(void)
131 {
132     DistributedDBToolsUnitTest::TestDirInit(g_testPath);
133     g_sourcePath = g_testPath + g_sourcePath;
134     g_packageResultPath = g_testPath + g_packageResultPath;
135     g_unpackResultPath = g_testPath + g_unpackResultPath;
136     (void)OS::MakeDBDirectory(g_sourcePath);
137     ofstream file1(g_sourcePath + FILE_NAME_1, ios::out | ios::binary | ios::trunc);
138     ASSERT_TRUE(file1.is_open());
139     file1.write(FILE_CONTENT_1.c_str(), FILE_CONTENT_1.size());
140     file1.close();
141     ofstream file2(g_sourcePath + FILE_NAME_2, ios::out | ios::binary | ios::trunc);
142     ASSERT_TRUE(file2.is_open());
143     file2.write(FILE_CONTENT_2, FILE_CONTENT_2_LEN);
144     file2.close();
145     g_fileInfo.dbType = 1;
146     ValueHashCalc calc;
147     int errCode = calc.Initialize();
148     ASSERT_EQ(errCode, E_OK);
149     errCode = calc.Update(DIVICE_ID);
150     ASSERT_EQ(errCode, E_OK);
151     vector<uint8_t> deviceIDVec;
152     errCode = calc.GetResult(deviceIDVec);
153     ASSERT_EQ(errCode, E_OK);
154     g_fileInfo.deviceID.resize(DEVICE_ID_LEN);
155     g_fileInfo.deviceID.assign(deviceIDVec.begin(), deviceIDVec.end());
156 }
157 
TearDownTestCase(void)158 void DistributedDBFilePackageTest::TearDownTestCase(void)
159 {
160     RemovePath(g_testPath);
161 }
162 
SetUp(void)163 void DistributedDBFilePackageTest::SetUp(void)
164 {
165     DistributedDBToolsUnitTest::PrintTestCaseInfo();
166     (void)OS::MakeDBDirectory(g_packageResultPath);
167     (void)OS::MakeDBDirectory(g_unpackResultPath);
168 }
169 
TearDown(void)170 void DistributedDBFilePackageTest::TearDown(void)
171 {
172     RemovePath(g_packageResultPath);
173     RemovePath(g_unpackResultPath);
174 }
175 
176 /**
177   * @tc.name: PackageFileTest001
178   * @tc.desc: Test file package and unpack functions.
179   * @tc.type: FUNC
180   * @tc.require: AR000D4879
181   * @tc.author: liujialei
182   */
183 HWTEST_F(DistributedDBFilePackageTest, PackageFileTest001, TestSize.Level1)
184 {
185     int errCode = PackageFile::PackageFiles(g_sourcePath, g_packageResultPath + PACKAGE_RESULT_FILE_NAME, g_fileInfo);
186     ASSERT_EQ(errCode, E_OK);
187     FileInfo fileInfo;
188     errCode = PackageFile::UnpackFile(g_packageResultPath + PACKAGE_RESULT_FILE_NAME, g_unpackResultPath, fileInfo);
189     ASSERT_EQ(errCode, E_OK);
190     ComparePath(g_sourcePath, g_unpackResultPath);
191     ASSERT_EQ(fileInfo.dbType, g_fileInfo.dbType);
192     return;
193 }
194 
195 /**
196   * @tc.name: PackageFileTest002
197   * @tc.desc: Test file package if source path is not exist.
198   * @tc.type: FUNC
199   * @tc.require: AR000D4879
200   * @tc.author: liujialei
201   */
202 HWTEST_F(DistributedDBFilePackageTest, PackageFileTest002, TestSize.Level1)
203 {
204     int errCode = PackageFile::PackageFiles(g_sourcePath + NON_EXIST_PATH,
205         g_packageResultPath + PACKAGE_RESULT_FILE_NAME, g_fileInfo);
206     ASSERT_EQ(errCode, -E_INVALID_PATH);
207     return;
208 }
209 
210 /**
211   * @tc.name: PackageFileTest003
212   * @tc.desc: Test file package if result path is not exist.
213   * @tc.type: FUNC
214   * @tc.require: AR000D4879
215   * @tc.author: liujialei
216   */
217 HWTEST_F(DistributedDBFilePackageTest, PackageFileTest003, TestSize.Level1)
218 {
219     int errCode = PackageFile::PackageFiles(g_sourcePath,
220         g_packageResultPath + NON_EXIST_PATH + PACKAGE_RESULT_FILE_NAME, g_fileInfo);
221     ASSERT_EQ(errCode, -E_INVALID_PATH);
222     return;
223 }
224 
225 /**
226   * @tc.name: PackageFileTest004
227   * @tc.desc: Test file package if source path is empty.
228   * @tc.type: FUNC
229   * @tc.require: AR000D4879
230   * @tc.author: liujialei
231   */
232 HWTEST_F(DistributedDBFilePackageTest, PackageFileTest004, TestSize.Level1)
233 {
234     // Clear source files.
235     RemovePath(g_sourcePath);
236     (void)OS::MakeDBDirectory(g_sourcePath);
237     // Test function.
238     int errCode = PackageFile::PackageFiles(g_sourcePath, g_packageResultPath + PACKAGE_RESULT_FILE_NAME, g_fileInfo);
239     ASSERT_EQ(errCode, -E_EMPTY_PATH);
240     // Create source files again.
241     ofstream file1(g_sourcePath + FILE_NAME_1, ios::out | ios::binary | ios::trunc);
242     ASSERT_TRUE(file1.is_open());
243     file1.write(FILE_CONTENT_1.c_str(), FILE_CONTENT_1.size());
244     file1.close();
245     ofstream file2(g_sourcePath + FILE_NAME_2, ios::out | ios::binary | ios::trunc);
246     ASSERT_TRUE(file2.is_open());
247     file2.write(FILE_CONTENT_2, 4);
248     file2.close();
249     return;
250 }
251 
252 /**
253   * @tc.name: PackageFileTest005
254   * @tc.desc: Test file unpack if source file is not exist.
255   * @tc.type: FUNC
256   * @tc.require: AR000D4879
257   * @tc.author: liujialei
258   */
259 HWTEST_F(DistributedDBFilePackageTest, PackageFileTest005, TestSize.Level1)
260 {
261     FileInfo fileInfo;
262     int errCode = PackageFile::UnpackFile(g_packageResultPath + PACKAGE_RESULT_FILE_NAME, g_unpackResultPath, fileInfo);
263     ASSERT_EQ(errCode, -E_INVALID_PATH);
264     return;
265 }
266 
267 /**
268   * @tc.name: PackageFileTest006
269   * @tc.desc: Test file unpack if result path is not exist.
270   * @tc.type: FUNC
271   * @tc.require: AR000D4879
272   * @tc.author: liujialei
273   */
274 HWTEST_F(DistributedDBFilePackageTest, PackageFileTest006, TestSize.Level1)
275 {
276     int errCode = PackageFile::PackageFiles(g_sourcePath, g_packageResultPath + PACKAGE_RESULT_FILE_NAME, g_fileInfo);
277     ASSERT_EQ(errCode, E_OK);
278     FileInfo fileInfo;
279     errCode = PackageFile::UnpackFile(g_packageResultPath + PACKAGE_RESULT_FILE_NAME,
280         g_unpackResultPath + NON_EXIST_PATH, fileInfo);
281     ASSERT_EQ(errCode, -E_INVALID_PATH);
282     return;
283 }
284 
285 /**
286   * @tc.name: PackageFileTest007
287   * @tc.desc: Test file unpack if magic check failed.
288   * @tc.type: FUNC
289   * @tc.require: AR000D4879
290   * @tc.author: liujialei
291   */
292 HWTEST_F(DistributedDBFilePackageTest, PackageFileTest007, TestSize.Level1)
293 {
294     int errCode = PackageFile::PackageFiles(g_sourcePath, g_packageResultPath + PACKAGE_RESULT_FILE_NAME, g_fileInfo);
295     ASSERT_EQ(errCode, E_OK);
296     // Change package file header.
297     const string REPLACE_FILE_HEADER = "test";
298     fstream file(g_packageResultPath + PACKAGE_RESULT_FILE_NAME, ios::in | ios::out | ios::binary);
299     ASSERT_TRUE(file.is_open());
300     file.seekp(0, ios_base::beg);
301     file.write(REPLACE_FILE_HEADER.c_str(), REPLACE_FILE_HEADER.size());
302     file.close();
303     // Unpack file.
304     FileInfo fileInfo;
305     errCode = PackageFile::UnpackFile(g_packageResultPath + PACKAGE_RESULT_FILE_NAME, g_unpackResultPath, fileInfo);
306     ASSERT_EQ(errCode, -E_INVALID_FILE);
307     return;
308 }
309 
310 /**
311   * @tc.name: PackageFileTest008
312   * @tc.desc: Test file unpack if checksum check failed.
313   * @tc.type: FUNC
314   * @tc.require: AR000D4879
315   * @tc.author: liujialei
316   */
317 HWTEST_F(DistributedDBFilePackageTest, PackageFileTest008, TestSize.Level1)
318 {
319     int errCode = PackageFile::PackageFiles(g_sourcePath, g_packageResultPath + PACKAGE_RESULT_FILE_NAME, g_fileInfo);
320     ASSERT_EQ(errCode, E_OK);
321     // Rewrite package file without file tail.
322     ifstream fileIn(g_packageResultPath + PACKAGE_RESULT_FILE_NAME, ios::in | ios::binary);
323     ASSERT_TRUE(fileIn.is_open());
324     fileIn.seekg(0, ios_base::end);
325     int fileLen = fileIn.tellg();
326     fileIn.seekg(0, ios_base::beg);
327     const int CUT_TAIL = 3;
328     int bufferLen = fileLen > BUFFER_SIZE ? BUFFER_SIZE : fileLen - CUT_TAIL;
329     vector<char> buffer(bufferLen, 0);
330     fileIn.read(buffer.data(), buffer.size());
331     fileIn.close();
332     ofstream fileOut(g_packageResultPath + PACKAGE_RESULT_FILE_NAME, ios::out | ios::binary | ios::trunc);
333     ASSERT_TRUE(fileOut.is_open());
334     fileOut.write(buffer.data(), buffer.size());
335     fileOut.close();
336     // Unpack file.
337     FileInfo fileInfo;
338     errCode = PackageFile::UnpackFile(g_packageResultPath + PACKAGE_RESULT_FILE_NAME, g_unpackResultPath, fileInfo);
339     ASSERT_EQ(errCode, -E_INVALID_FILE);
340     return;
341 }
342 
343