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 <perf.h>
17 #include <gtest/gtest-message.h>
18 #include <gtest/gtest-test-part.h>
19 #include <gtest/gtest.h>
20 #include <istream>
21 #include <libxml/globals.h>
22 #include <libxml/xmlstring.h>
23 #include <list>
24 #include <map>
25 #include <securec.h>
26 #include <type_traits>
27 #include <string>
28
29 #include <sstream>
30 #include <libxml/parser.h>
31 using namespace std;
32
33 namespace OHOS {
34 namespace TestAW {
35
36 #define ERR_MSG(...) fprintf(stderr, __VA_ARGS__)
37 #define INF_MSG(...) fprintf(stdout, __VA_ARGS__)
38 #define DBG_MSG(...) fprintf(stdout, __VA_ARGS__)
39
40 #define ID_LARGER_IS_BETTER true
41 #define ID_SMALLER_IS_BETTER false
42
43 namespace {
44 const auto XML_TAG_ROOT = "configuration";
45 const auto XML_TAG_DATETIME = "datetime";
46 const auto XML_TAG_URL = "url";
47 const auto XML_TAG_CASENAME = "name";
48 const int ID_PROPERTY_LENGTH = 64;
49 }
50
51 // BaseLineManager
BaseLineManager()52 BaseLineManager::BaseLineManager()
53 : m_bNoBaseline(false)
54 {
55 }
56
BaseLineManager(const string path)57 BaseLineManager::BaseLineManager(const string path)
58 : m_bNoBaseline(false)
59 {
60 LoadConfig(path);
61 }
62
~BaseLineManager()63 BaseLineManager::~BaseLineManager()
64 {
65 if (m_bNoBaseline) {
66 ERR_MSG("[ WARNING ] no baseline loaded, please manual check output value is OK.\n");
67 }
68 }
69
70 // parser configuration file in json format.
LoadConfig(const string path)71 bool BaseLineManager::LoadConfig(const string path)
72 {
73 m_bNoBaseline = false;
74 if (!ReadXmlFile(path)) {
75 ERR_MSG("[ WARNING ] failed to load config from %s\n", path.c_str());
76 m_bNoBaseline = true;
77 return false;
78 }
79
80 INF_MSG("[ PERF ] Load BaseLines from: %s\n", path.c_str());
81 return true;
82 }
83
ParseProperties(const xmlNode currNode,map<string,string> & properties)84 void ParseProperties(const xmlNode currNode, map<string, string>& properties)
85 {
86 for (auto attrs = currNode.properties; attrs != nullptr; attrs = attrs->next) {
87 auto name = attrs->name;
88 if (name == nullptr) {
89 continue;
90 }
91 auto value = xmlGetProp(&currNode, name);
92 if (value == nullptr) {
93 continue;
94 }
95 string propName(reinterpret_cast<const char*>(name));
96 string propValue(reinterpret_cast<char*>(value));
97 properties[propName] = std::move(propValue);
98 xmlFree(value);
99 }
100 }
101
ReadXmlFile(string baselinePath)102 bool BaseLineManager::ReadXmlFile(string baselinePath)
103 {
104 xmlDocPtr ptrXmlDoc = xmlReadFile(baselinePath.c_str(), nullptr, XML_PARSE_NOBLANKS);
105 if (ptrXmlDoc == nullptr) {
106 return false;
107 }
108
109 xmlNodePtr ptrRootNode = xmlDocGetRootElement(ptrXmlDoc);
110 if (ptrRootNode == nullptr || ptrRootNode->name == nullptr ||
111 xmlStrcmp(ptrRootNode->name, reinterpret_cast<const xmlChar*>(XML_TAG_ROOT))) {
112 xmlFreeDoc(ptrXmlDoc);
113 return false;
114 }
115
116 map<string, string> properties;
117 ParseProperties(*ptrRootNode, properties);
118 if (properties.count(XML_TAG_DATETIME) == 1) {
119 m_bastCfg.date = properties[XML_TAG_DATETIME];
120 }
121 if (properties.count(XML_TAG_URL) == 1) {
122 m_bastCfg.date = properties[XML_TAG_URL];
123 }
124
125 xmlNodePtr currNodePtr = ptrRootNode->xmlChildrenNode;
126 for (; currNodePtr != nullptr; currNodePtr = currNodePtr->next) {
127 if (currNodePtr->name == nullptr || currNodePtr->type == XML_COMMENT_NODE) {
128 xmlFreeDoc(ptrXmlDoc);
129 return false;
130 }
131
132 map<string, string> properties_temp;
133 ParseProperties(*currNodePtr, properties_temp);
134 m_bastCfg.items.push_back(properties_temp);
135 }
136
137 xmlFreeDoc(ptrXmlDoc);
138 return true;
139 }
140
IsNoBaseline()141 bool BaseLineManager::IsNoBaseline()
142 {
143 return m_bNoBaseline;
144 }
145
StrtoDouble(const string & str)146 double BaseLineManager::StrtoDouble(const string& str)
147 {
148 istringstream iss(str);
149 double num;
150 iss >> num;
151 return num;
152 }
153
GetExtraValueDouble(const string testcaseName,const string extra,double & value)154 bool BaseLineManager::GetExtraValueDouble(const string testcaseName, const string extra, double &value)
155 {
156 if (testcaseName == "" || extra == "") {
157 DBG_MSG("[ ERROR ] invalid arguments: testcaseName=%s, extra=%s\n", testcaseName.c_str(), extra.c_str());
158 return false;
159 }
160
161 for (auto iter = m_bastCfg.items.begin(); iter != m_bastCfg.items.end(); iter++) {
162 map<string, string> properties = *iter;
163 if (properties.count(XML_TAG_CASENAME) == 1 && properties[XML_TAG_CASENAME] == testcaseName) {
164 if (properties.count(extra) == 1) {
165 value = StrtoDouble(properties[extra]);
166 }
167 break;
168 }
169 }
170
171 return true;
172 }
173
174 // GtestPerfTestCase
GtestPerfTestCase(BaseLineManager * pManager,testing::Test * tester,int caseVersion,std::string testClassName,std::string testInterfaceName)175 GtestPerfTestCase::GtestPerfTestCase(BaseLineManager* pManager,
176 testing::Test *tester,
177 int caseVersion,
178 std::string testClassName,
179 std::string testInterfaceName)
180 {
181 m_pManager = pManager;
182 m_pTester = tester;
183 m_dCaseVersion = caseVersion;
184 m_strCaseName = "";
185 m_strTestClassName = testClassName;
186 m_strTestInterfaceName = testInterfaceName;
187
188 // get test case name from GTEST API.
189 // should be use tester->XXX() instead of this.
190 if (tester != nullptr && ::testing::UnitTest::GetInstance() != nullptr) {
191 m_strCaseName = string(::testing::UnitTest::GetInstance()->current_test_info()->name());
192 }
193
194 // start initialize.
195 Initialize();
196 }
197
SetBaseLine(string testcaseName)198 bool GtestPerfTestCase::SetBaseLine(string testcaseName)
199 {
200 if (testcaseName == "") {
201 return false;
202 }
203
204 m_strCaseName = testcaseName;
205
206 return Initialize();
207 }
208
ResetValues()209 void GtestPerfTestCase::ResetValues()
210 {
211 m_bHasBaseLine = false;
212 m_dbBaseLine = -1.0;
213 m_bHasLastValue = false;
214 m_dbLastValue = -1.0;
215 m_bHasFloatRange = false;
216 m_dbFloatRange = -1.0;
217
218 m_bTestResult = false;
219 m_dbTestResult = -1.0;
220 }
221
Initialize()222 bool GtestPerfTestCase::Initialize()
223 {
224 // clear all values.
225 ResetValues();
226 if (m_strCaseName == "" || m_pManager == nullptr) {
227 return false;
228 }
229
230 // get baseline value
231 m_bHasBaseLine = m_pManager->GetExtraValueDouble(m_strCaseName, "baseline", m_dbBaseLine);
232 if (!m_bHasBaseLine) {
233 return false;
234 }
235
236 // get last test value from config.
237 m_bHasLastValue = m_pManager->GetExtraValueDouble(m_strCaseName, "lastvalue", m_dbLastValue);
238
239 // get float range value from config.
240 m_bHasFloatRange = m_pManager->GetExtraValueDouble(m_strCaseName, "floatrange", m_dbFloatRange);
241 // check values is valid, and update them.
242 if (m_bHasFloatRange && (m_dbFloatRange < 0 || m_dbFloatRange >= 1)) {
243 DBG_MSG("[ ERROR ] %s has invalid float range: %f.\n", m_strCaseName.c_str(), m_dbFloatRange);
244 m_bHasFloatRange = false;
245 }
246
247 if (!m_bHasFloatRange) {
248 m_dbFloatRange = 0.0;
249 }
250
251 if (!m_bHasLastValue) {
252 m_dbLastValue = m_dbBaseLine;
253 }
254
255 return true;
256 }
257
258 // return true if testValue >= baseline value
ExpectLarger(double testValue)259 bool GtestPerfTestCase::ExpectLarger(double testValue)
260 {
261 return ExpectValue(testValue, ID_LARGER_IS_BETTER);
262 }
263
264 // return true if testValue <= baseline value
ExpectSmaller(double testValue)265 bool GtestPerfTestCase::ExpectSmaller(double testValue)
266 {
267 return ExpectValue(testValue, ID_SMALLER_IS_BETTER);
268 }
269
ExpectValue(double testValue,bool isLargerBetter)270 bool GtestPerfTestCase::ExpectValue(double testValue, bool isLargerBetter)
271 {
272 if (m_strCaseName == "") {
273 ERR_MSG("[ ERROR ] failed to get testcase name.\n");
274 return false;
275 }
276
277 m_bTestResult = false;
278 m_dbTestResult = testValue;
279
280 // check pass or failed.
281 if (m_pManager != nullptr && m_pManager->IsNoBaseline()) {
282 // no baseline.json is loaded at startup.
283 // set result to TRUE, please check testValue manually.
284 m_bTestResult = true;
285 EXPECT_TRUE(m_bTestResult);
286 } else if (!m_bHasBaseLine) {
287 ERR_MSG("[ ERROR ] %s has NO baseline.\n", m_strCaseName.c_str());
288 EXPECT_TRUE(m_bHasBaseLine);
289 } else {
290 double baseValue = -1;
291 if (isLargerBetter) {
292 baseValue = (m_dbLastValue >= m_dbBaseLine) ? m_dbLastValue : m_dbBaseLine;
293 EXPECT_GE(testValue, (baseValue * (1.0 - m_dbFloatRange)));
294 m_bTestResult = (testValue >= (baseValue * (1.0 - m_dbFloatRange))) ? true : false;
295 } else {
296 baseValue = (m_dbLastValue <= m_dbBaseLine) ? m_dbLastValue : m_dbBaseLine;
297 EXPECT_LE(testValue, (baseValue * (1.0 + m_dbFloatRange)));
298 m_bTestResult = (testValue <= (baseValue * (1.0 + m_dbFloatRange))) ? true : false;
299 }
300 }
301
302 // save result.
303 SaveResult(testValue);
304
305 return m_bTestResult;
306 }
307
SaveResult(double testValue)308 bool GtestPerfTestCase::SaveResult(double testValue)
309 {
310 char buffer[ID_PROPERTY_LENGTH] = {0};
311
312 if (m_pTester == nullptr) {
313 ERR_MSG("[ ERROR ] m_pTester is nullptr.\n");
314 return false;
315 }
316
317 INF_MSG("[ PERF ] %s: baseline:%f, test_result: %f\n", m_strCaseName.c_str(), m_dbBaseLine, testValue);
318
319 (void)memset_s(buffer, sizeof(buffer), 0, sizeof(buffer));
320 if (snprintf_s(buffer, sizeof(buffer), sizeof(buffer) - 1, "%g", m_dbBaseLine) > 0) {
321 m_pTester->RecordProperty("baseline", buffer);
322 }
323
324 (void)memset_s(buffer, sizeof(buffer), 0, sizeof(buffer));
325 if (snprintf_s(buffer, sizeof(buffer), sizeof(buffer) - 1, "%d", m_dCaseVersion) > 0) {
326 m_pTester->RecordProperty("tc_version", buffer);
327 }
328
329 (void)memset_s(buffer, sizeof(buffer), 0, sizeof(buffer));
330 if (snprintf_s(buffer, sizeof(buffer), sizeof(buffer) - 1, "%g", m_dbLastValue) > 0) {
331 m_pTester->RecordProperty("lastvalue", m_bHasLastValue ? buffer : "");
332 }
333
334 (void)memset_s(buffer, sizeof(buffer), 0, sizeof(buffer));
335 if (snprintf_s(buffer, sizeof(buffer), sizeof(buffer) - 1, "%g", testValue) > 0) {
336 m_pTester->RecordProperty("value", buffer);
337 }
338
339 m_pTester->RecordProperty("category", "performance");
340 m_pTester->RecordProperty("test_class", m_strTestClassName.c_str());
341 m_pTester->RecordProperty("test_interface", m_strTestInterfaceName.c_str());
342
343 return true;
344 }
345 } // namespace TestAW
346 } // namespace OHOS
347