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