• 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 #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