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> 31using namespace std; 32 33namespace OHOS { 34namespace 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 43namespace { 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 52BaseLineManager::BaseLineManager() 53 : m_bNoBaseline(false) 54{ 55} 56 57BaseLineManager::BaseLineManager(const string path) 58 : m_bNoBaseline(false) 59{ 60 LoadConfig(path); 61} 62 63BaseLineManager::~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. 71bool 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 84void 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 102bool 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 141bool BaseLineManager::IsNoBaseline() 142{ 143 return m_bNoBaseline; 144} 145 146double BaseLineManager::StrtoDouble(const string& str) 147{ 148 istringstream iss(str); 149 double num; 150 iss >> num; 151 return num; 152} 153 154bool 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 175GtestPerfTestCase::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 198bool GtestPerfTestCase::SetBaseLine(string testcaseName) 199{ 200 if (testcaseName == "") { 201 return false; 202 } 203 204 m_strCaseName = testcaseName; 205 206 return Initialize(); 207} 208 209void 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 222bool 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 259bool GtestPerfTestCase::ExpectLarger(double testValue) 260{ 261 return ExpectValue(testValue, ID_LARGER_IS_BETTER); 262} 263 264// return true if testValue <= baseline value 265bool GtestPerfTestCase::ExpectSmaller(double testValue) 266{ 267 return ExpectValue(testValue, ID_SMALLER_IS_BETTER); 268} 269 270bool 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 308bool 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