/* * Copyright (c) 2021 Huawei Device Co., Ltd. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace std; namespace OHOS { namespace TestAW { #define ERR_MSG(...) fprintf(stderr, __VA_ARGS__) #define INF_MSG(...) fprintf(stdout, __VA_ARGS__) #define DBG_MSG(...) fprintf(stdout, __VA_ARGS__) #define ID_LARGER_IS_BETTER true #define ID_SMALLER_IS_BETTER false namespace { const auto XML_TAG_ROOT = "configuration"; const auto XML_TAG_DATETIME = "datetime"; const auto XML_TAG_URL = "url"; const auto XML_TAG_CASENAME = "name"; const int ID_PROPERTY_LENGTH = 64; } // BaseLineManager BaseLineManager::BaseLineManager() : m_bNoBaseline(false) { } BaseLineManager::BaseLineManager(const string path) : m_bNoBaseline(false) { LoadConfig(path); } BaseLineManager::~BaseLineManager() { if (m_bNoBaseline) { ERR_MSG("[ WARNING ] no baseline loaded, please manual check output value is OK.\n"); } } // parser configuration file in json format. bool BaseLineManager::LoadConfig(const string path) { m_bNoBaseline = false; if (!ReadXmlFile(path)) { ERR_MSG("[ WARNING ] failed to load config from %s\n", path.c_str()); m_bNoBaseline = true; return false; } INF_MSG("[ PERF ] Load BaseLines from: %s\n", path.c_str()); return true; } void ParseProperties(const xmlNode currNode, map& properties) { for (auto attrs = currNode.properties; attrs != nullptr; attrs = attrs->next) { auto name = attrs->name; if (name == nullptr) { continue; } auto value = xmlGetProp(&currNode, name); if (value == nullptr) { continue; } string propName(reinterpret_cast(name)); string propValue(reinterpret_cast(value)); properties[propName] = std::move(propValue); xmlFree(value); } } bool BaseLineManager::ReadXmlFile(string baselinePath) { xmlDocPtr ptrXmlDoc = xmlReadFile(baselinePath.c_str(), nullptr, XML_PARSE_NOBLANKS); if (ptrXmlDoc == nullptr) { return false; } xmlNodePtr ptrRootNode = xmlDocGetRootElement(ptrXmlDoc); if (ptrRootNode == nullptr || ptrRootNode->name == nullptr || xmlStrcmp(ptrRootNode->name, reinterpret_cast(XML_TAG_ROOT))) { xmlFreeDoc(ptrXmlDoc); return false; } map properties; ParseProperties(*ptrRootNode, properties); if (properties.count(XML_TAG_DATETIME) == 1) { m_bastCfg.date = properties[XML_TAG_DATETIME]; } if (properties.count(XML_TAG_URL) == 1) { m_bastCfg.date = properties[XML_TAG_URL]; } xmlNodePtr currNodePtr = ptrRootNode->xmlChildrenNode; for (; currNodePtr != nullptr; currNodePtr = currNodePtr->next) { if (currNodePtr->name == nullptr || currNodePtr->type == XML_COMMENT_NODE) { xmlFreeDoc(ptrXmlDoc); return false; } map properties_temp; ParseProperties(*currNodePtr, properties_temp); m_bastCfg.items.push_back(properties_temp); } xmlFreeDoc(ptrXmlDoc); return true; } bool BaseLineManager::IsNoBaseline() { return m_bNoBaseline; } double BaseLineManager::StrtoDouble(const string& str) { istringstream iss(str); double num; iss >> num; return num; } bool BaseLineManager::GetExtraValueDouble(const string testcaseName, const string extra, double &value) { if (testcaseName == "" || extra == "") { DBG_MSG("[ ERROR ] invalid arguments: testcaseName=%s, extra=%s\n", testcaseName.c_str(), extra.c_str()); return false; } for (auto iter = m_bastCfg.items.begin(); iter != m_bastCfg.items.end(); iter++) { map properties = *iter; if (properties.count(XML_TAG_CASENAME) == 1 && properties[XML_TAG_CASENAME] == testcaseName) { if (properties.count(extra) == 1) { value = StrtoDouble(properties[extra]); } break; } } return true; } // GtestPerfTestCase GtestPerfTestCase::GtestPerfTestCase(BaseLineManager* pManager, testing::Test *tester, int caseVersion, std::string testClassName, std::string testInterfaceName) { m_pManager = pManager; m_pTester = tester; m_dCaseVersion = caseVersion; m_strCaseName = ""; m_strTestClassName = testClassName; m_strTestInterfaceName = testInterfaceName; // get test case name from GTEST API. // should be use tester->XXX() instead of this. if (tester != nullptr && ::testing::UnitTest::GetInstance() != nullptr) { m_strCaseName = string(::testing::UnitTest::GetInstance()->current_test_info()->name()); } // start initialize. Initialize(); } bool GtestPerfTestCase::SetBaseLine(string testcaseName) { if (testcaseName == "") { return false; } m_strCaseName = testcaseName; return Initialize(); } void GtestPerfTestCase::ResetValues() { m_bHasBaseLine = false; m_dbBaseLine = -1.0; m_bHasLastValue = false; m_dbLastValue = -1.0; m_bHasFloatRange = false; m_dbFloatRange = -1.0; m_bTestResult = false; m_dbTestResult = -1.0; } bool GtestPerfTestCase::Initialize() { // clear all values. ResetValues(); if (m_strCaseName == "" || m_pManager == nullptr) { return false; } // get baseline value m_bHasBaseLine = m_pManager->GetExtraValueDouble(m_strCaseName, "baseline", m_dbBaseLine); if (!m_bHasBaseLine) { return false; } // get last test value from config. m_bHasLastValue = m_pManager->GetExtraValueDouble(m_strCaseName, "lastvalue", m_dbLastValue); // get float range value from config. m_bHasFloatRange = m_pManager->GetExtraValueDouble(m_strCaseName, "floatrange", m_dbFloatRange); // check values is valid, and update them. if (m_bHasFloatRange && (m_dbFloatRange < 0 || m_dbFloatRange >= 1)) { DBG_MSG("[ ERROR ] %s has invalid float range: %f.\n", m_strCaseName.c_str(), m_dbFloatRange); m_bHasFloatRange = false; } if (!m_bHasFloatRange) { m_dbFloatRange = 0.0; } if (!m_bHasLastValue) { m_dbLastValue = m_dbBaseLine; } return true; } // return true if testValue >= baseline value bool GtestPerfTestCase::ExpectLarger(double testValue) { return ExpectValue(testValue, ID_LARGER_IS_BETTER); } // return true if testValue <= baseline value bool GtestPerfTestCase::ExpectSmaller(double testValue) { return ExpectValue(testValue, ID_SMALLER_IS_BETTER); } bool GtestPerfTestCase::ExpectValue(double testValue, bool isLargerBetter) { if (m_strCaseName == "") { ERR_MSG("[ ERROR ] failed to get testcase name.\n"); return false; } m_bTestResult = false; m_dbTestResult = testValue; // check pass or failed. if (m_pManager != nullptr && m_pManager->IsNoBaseline()) { // no baseline.json is loaded at startup. // set result to TRUE, please check testValue manually. m_bTestResult = true; EXPECT_TRUE(m_bTestResult); } else if (!m_bHasBaseLine) { ERR_MSG("[ ERROR ] %s has NO baseline.\n", m_strCaseName.c_str()); EXPECT_TRUE(m_bHasBaseLine); } else { double baseValue = -1; if (isLargerBetter) { baseValue = (m_dbLastValue >= m_dbBaseLine) ? m_dbLastValue : m_dbBaseLine; EXPECT_GE(testValue, (baseValue * (1.0 - m_dbFloatRange))); m_bTestResult = (testValue >= (baseValue * (1.0 - m_dbFloatRange))) ? true : false; } else { baseValue = (m_dbLastValue <= m_dbBaseLine) ? m_dbLastValue : m_dbBaseLine; EXPECT_LE(testValue, (baseValue * (1.0 + m_dbFloatRange))); m_bTestResult = (testValue <= (baseValue * (1.0 + m_dbFloatRange))) ? true : false; } } // save result. SaveResult(testValue); return m_bTestResult; } bool GtestPerfTestCase::SaveResult(double testValue) { char buffer[ID_PROPERTY_LENGTH] = {0}; if (m_pTester == nullptr) { ERR_MSG("[ ERROR ] m_pTester is nullptr.\n"); return false; } INF_MSG("[ PERF ] %s: baseline:%f, test_result: %f\n", m_strCaseName.c_str(), m_dbBaseLine, testValue); (void)memset_s(buffer, sizeof(buffer), 0, sizeof(buffer)); if (snprintf_s(buffer, sizeof(buffer), sizeof(buffer) - 1, "%g", m_dbBaseLine) > 0) { m_pTester->RecordProperty("baseline", buffer); } (void)memset_s(buffer, sizeof(buffer), 0, sizeof(buffer)); if (snprintf_s(buffer, sizeof(buffer), sizeof(buffer) - 1, "%d", m_dCaseVersion) > 0) { m_pTester->RecordProperty("tc_version", buffer); } (void)memset_s(buffer, sizeof(buffer), 0, sizeof(buffer)); if (snprintf_s(buffer, sizeof(buffer), sizeof(buffer) - 1, "%g", m_dbLastValue) > 0) { m_pTester->RecordProperty("lastvalue", m_bHasLastValue ? buffer : ""); } (void)memset_s(buffer, sizeof(buffer), 0, sizeof(buffer)); if (snprintf_s(buffer, sizeof(buffer), sizeof(buffer) - 1, "%g", testValue) > 0) { m_pTester->RecordProperty("value", buffer); } m_pTester->RecordProperty("category", "performance"); m_pTester->RecordProperty("test_class", m_strTestClassName.c_str()); m_pTester->RecordProperty("test_interface", m_strTestInterfaceName.c_str()); return true; } } // namespace TestAW } // namespace OHOS