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 "component_test/test_result_recorder.h"
17
18 #include <chrono>
19 #include <ctime>
20 #include <filesystem>
21 #include <fstream>
22 #include <iomanip>
23 #include <iostream>
24 #include <regex>
25 #include <sstream>
26 #include <string>
27 #include <sys/stat.h>
28 #include <vector>
29
30 #include "base/json/json_util.h"
31 #include "base/log/log.h"
32 #include "core/common/task_runner_adapter_factory.h"
33
34 namespace OHOS::Ace::ComponentTest {
35
36 #if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__)
37 const std::string file_sep = "\\";
38 #else
39 const std::string file_sep = "/";
40 #endif
41
CreateDirectories(const std::string & filePath)42 int CreateDirectories(const std::string& filePath)
43 {
44 int result = 0;
45 #ifdef _WIN32
46 result = mkdir(filePath.data());
47 #else
48 result = mkdir(filePath.data(), S_IRUSR | S_IWUSR | S_IXUSR);
49 #endif
50 return result;
51 }
52
FileExists(const std::string & path)53 int FileExists(const std::string& path)
54 {
55 std::ofstream outFile(path);
56 if (!outFile.is_open()) {
57 return 1;
58 }
59 return 0;
60 }
61
SplitString(const std::string & outPath)62 std::vector<std::string> SplitString(const std::string& outPath)
63 {
64 auto p = file_sep + file_sep;
65 std::regex delimiter(p);
66 std::sregex_token_iterator iter(outPath.begin(), outPath.end(), delimiter, -1);
67 std::sregex_token_iterator end;
68 std::vector<std::string> tokens;
69 while (iter != end) {
70 tokens.push_back(*iter++);
71 }
72 return tokens;
73 }
74
GetCurrentTime()75 std::string GetCurrentTime()
76 {
77 std::chrono::time_point<std::chrono::system_clock, std::chrono::microseconds> tpMicro =
78 std::chrono::time_point_cast<std::chrono::microseconds>(std::chrono::system_clock::now());
79 time_t totalMicroSeconds = tpMicro.time_since_epoch().count();
80 return std::to_string(totalMicroSeconds);
81 }
82
HandleRelativeFolder(std::string & folderPath)83 std::string HandleRelativeFolder(std::string& folderPath)
84 {
85 std::string resultPath;
86 std::string relativePath;
87 std::string currentPath;
88 char tmp[PATH_MAX];
89 if (getcwd(tmp, sizeof(tmp)) == NULL) {
90 LOGE("get current directory failed!");
91 } else {
92 currentPath = std::string(tmp);
93 }
94 auto pos = folderPath.rfind("..");
95 if (pos != std::string::npos) {
96 std::string currentPathAfterSplit;
97 std::vector<std::string> splitInputResult = SplitString(folderPath);
98 int count = 0;
99 for (const auto& i : splitInputResult) {
100 if (i == "..") {
101 count++;
102 } else {
103 relativePath += file_sep + i;
104 }
105 }
106 std::vector<std::string> splitCurrentPathResult = SplitString(currentPath);
107 for (std::size_t i = 0; i < splitCurrentPathResult.size() - count; i++) {
108 currentPathAfterSplit += splitCurrentPathResult[i] + file_sep;
109 }
110 resultPath = currentPathAfterSplit + relativePath.substr(1, relativePath.size() - 1);
111 } else {
112 auto beginWithDot = folderPath.find(".") == 0;
113 if (!beginWithDot) {
114 resultPath = currentPath + file_sep + folderPath;
115 } else {
116 auto path = folderPath.substr(1, folderPath.size() - 1);
117 resultPath = currentPath + file_sep + path;
118 }
119 }
120 return resultPath;
121 }
122
HandleAbsoluteFolder(std::string & folderPath)123 std::string HandleAbsoluteFolder(std::string& folderPath)
124 {
125 std::string disk;
126 std::string resultPath;
127 size_t pos = folderPath.find(file_sep);
128 if (pos != std::string::npos) {
129 disk = folderPath.substr(0, pos);
130 }
131 resultPath = disk;
132 std::vector<std::string> result = SplitString(folderPath);
133 for (std::size_t i = 1; i < result.size(); i++) {
134 resultPath += file_sep + result[i];
135 }
136 return resultPath;
137 }
138
Initialize(const std::string & out)139 void TestResultRecorder::Initialize(const std::string& out)
140 {
141 testRecord_.result = Result::ERROR;
142 testRecord_.info = "Component test terminated unexpectedly";
143 testRecord_.position = "Unknown";
144 outPath_ = out;
145 if (outPath_ == "") {
146 return;
147 }
148 std::string absolutePath;
149 std::string resultPath;
150 auto isAbsolute = outPath_.find(":") == std::string::npos;
151 if (!isAbsolute) {
152 absolutePath = HandleAbsoluteFolder(outPath_);
153 } else {
154 absolutePath = HandleRelativeFolder(outPath_);
155 }
156 std::string disk;
157 size_t pos = absolutePath.find(file_sep);
158 if (pos != std::string::npos) {
159 disk = absolutePath.substr(0, pos);
160 }
161 resultPath = disk;
162 std::vector<std::string> result = SplitString(absolutePath);
163 for (std::size_t i = 1; i < result.size(); i++) {
164 resultPath += file_sep + result[i];
165 bool isDir = resultPath.find(".json") == std::string::npos;
166 if (isDir) {
167 CreateDirectories(resultPath);
168 if (i == (result.size() - 1)) {
169 const std::string fileName = GetCurrentTime();
170 resultPath += file_sep + fileName + ".json";
171 }
172 }
173 if (FileExists(resultPath) != 0) {
174 LOGE("File \"%{public}s\" not exist", resultPath.c_str());
175 }
176 }
177 outPath_ = resultPath;
178 }
179
WriteJson()180 void TestResultRecorder::WriteJson()
181 {
182 auto jsonNode = JsonUtil::Create(true);
183 auto jsonDetail = JsonUtil::Create(true);
184 for (auto i : testRecord_.assertResults) {
185 auto jSingle = JsonUtil::Create(true);
186 jSingle->Put("position", i.position.c_str());
187 jSingle->Put("result", i.GetTestCaseResultString().c_str());
188 jSingle->Put("message", i.info.c_str());
189 jsonDetail->Put(jSingle);
190 }
191 jsonNode->Put("test_case_name", attr_.GetTestCaseName().c_str());
192 jsonNode->Put("test_case_type", attr_.GetTestCaseTypeString().c_str());
193 jsonNode->Put("test_case_size", attr_.GetTestCaseSizeString().c_str());
194 jsonNode->Put("test_case_level", attr_.GetTestCaseLevelString().c_str());
195 jsonNode->Put("message", testRecord_.info.c_str());
196 jsonNode->Put("result", testRecord_.result);
197 if (testRecord_.result > 0) {
198 jsonNode->Put("error_position", testRecord_.position.c_str());
199 }
200 jsonNode->Put("details", jsonDetail);
201 std::string str = jsonNode->ToString();
202 if (outPath_ != "") {
203 std::fstream file;
204 file.open(outPath_, std::ios::in | std::ios::out | std::ios::trunc);
205 if (file.is_open()) {
206 file << str << std::endl;
207 file.close();
208 } else {
209 LOGE("open file failed");
210 }
211 } else {
212 LOGI("Json Result: %{public}s", str.c_str());
213 }
214 }
215
Record(const std::string & info,const std::string & position,Result result)216 void TestResultRecorder::Record(const std::string& info, const std::string& position, Result result)
217 {
218 if (result != Result::PASS) {
219 testRecord_.info = info;
220 testRecord_.result = result;
221 testRecord_.position = position;
222 }
223 testRecord_.assertResults.emplace_back(AssertResult { position, result, info });
224 WriteJson();
225 }
226
Finish()227 void TestResultRecorder::Finish()
228 {
229 if (testRecord_.assertResults.empty()) {
230 testRecord_.result = Result::PASS;
231 testRecord_.info = "";
232 testRecord_.position = "";
233 } else {
234 auto last = testRecord_.assertResults.back();
235 if (last.result == Result::PASS) {
236 testRecord_.info = "";
237 testRecord_.position = "";
238 } else {
239 testRecord_.info = last.info;
240 testRecord_.position = last.position;
241 }
242 testRecord_.result = last.result;
243 }
244 WriteJson();
245 }
246 } // namespace OHOS::Ace::ComponentTest
247