13f085823Sopenharmony_ci/*
23f085823Sopenharmony_ci * Copyright (c) 2021 Huawei Device Co., Ltd.
33f085823Sopenharmony_ci * Licensed under the Apache License, Version 2.0 (the "License");
43f085823Sopenharmony_ci * you may not use this file except in compliance with the License.
53f085823Sopenharmony_ci * You may obtain a copy of the License at
63f085823Sopenharmony_ci *
73f085823Sopenharmony_ci *     http://www.apache.org/licenses/LICENSE-2.0
83f085823Sopenharmony_ci *
93f085823Sopenharmony_ci * Unless required by applicable law or agreed to in writing, software
103f085823Sopenharmony_ci * distributed under the License is distributed on an "AS IS" BASIS,
113f085823Sopenharmony_ci * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
123f085823Sopenharmony_ci * See the License for the specific language governing permissions and
133f085823Sopenharmony_ci * limitations under the License.
143f085823Sopenharmony_ci */
153f085823Sopenharmony_ci
163f085823Sopenharmony_ci#include <perf.h>
173f085823Sopenharmony_ci#include <gtest/gtest-message.h>
183f085823Sopenharmony_ci#include <gtest/gtest-test-part.h>
193f085823Sopenharmony_ci#include <gtest/gtest.h>
203f085823Sopenharmony_ci#include <istream>
213f085823Sopenharmony_ci#include <libxml/globals.h>
223f085823Sopenharmony_ci#include <libxml/xmlstring.h>
233f085823Sopenharmony_ci#include <list>
243f085823Sopenharmony_ci#include <map>
253f085823Sopenharmony_ci#include <securec.h>
263f085823Sopenharmony_ci#include <type_traits>
273f085823Sopenharmony_ci#include <string>
283f085823Sopenharmony_ci
293f085823Sopenharmony_ci#include <sstream>
303f085823Sopenharmony_ci#include <libxml/parser.h>
313f085823Sopenharmony_ciusing namespace std;
323f085823Sopenharmony_ci
333f085823Sopenharmony_cinamespace OHOS {
343f085823Sopenharmony_cinamespace TestAW {
353f085823Sopenharmony_ci
363f085823Sopenharmony_ci#define ERR_MSG(...)    fprintf(stderr, __VA_ARGS__)
373f085823Sopenharmony_ci#define INF_MSG(...)    fprintf(stdout, __VA_ARGS__)
383f085823Sopenharmony_ci#define DBG_MSG(...)    fprintf(stdout, __VA_ARGS__)
393f085823Sopenharmony_ci
403f085823Sopenharmony_ci#define ID_LARGER_IS_BETTER    true
413f085823Sopenharmony_ci#define ID_SMALLER_IS_BETTER   false
423f085823Sopenharmony_ci
433f085823Sopenharmony_cinamespace {
443f085823Sopenharmony_ci    const auto XML_TAG_ROOT       = "configuration";
453f085823Sopenharmony_ci    const auto XML_TAG_DATETIME   = "datetime";
463f085823Sopenharmony_ci    const auto XML_TAG_URL        = "url";
473f085823Sopenharmony_ci    const auto XML_TAG_CASENAME   = "name";
483f085823Sopenharmony_ci    const int ID_PROPERTY_LENGTH = 64;
493f085823Sopenharmony_ci}
503f085823Sopenharmony_ci
513f085823Sopenharmony_ci// BaseLineManager
523f085823Sopenharmony_ciBaseLineManager::BaseLineManager()
533f085823Sopenharmony_ci    : m_bNoBaseline(false)
543f085823Sopenharmony_ci{
553f085823Sopenharmony_ci}
563f085823Sopenharmony_ci
573f085823Sopenharmony_ciBaseLineManager::BaseLineManager(const string path)
583f085823Sopenharmony_ci    : m_bNoBaseline(false)
593f085823Sopenharmony_ci{
603f085823Sopenharmony_ci    LoadConfig(path);
613f085823Sopenharmony_ci}
623f085823Sopenharmony_ci
633f085823Sopenharmony_ciBaseLineManager::~BaseLineManager()
643f085823Sopenharmony_ci{
653f085823Sopenharmony_ci    if (m_bNoBaseline) {
663f085823Sopenharmony_ci        ERR_MSG("[ WARNING  ] no baseline loaded, please manual check output value is OK.\n");
673f085823Sopenharmony_ci    }
683f085823Sopenharmony_ci}
693f085823Sopenharmony_ci
703f085823Sopenharmony_ci// parser configuration file in json format.
713f085823Sopenharmony_cibool BaseLineManager::LoadConfig(const string path)
723f085823Sopenharmony_ci{
733f085823Sopenharmony_ci    m_bNoBaseline = false;
743f085823Sopenharmony_ci    if (!ReadXmlFile(path)) {
753f085823Sopenharmony_ci        ERR_MSG("[ WARNING  ] failed to load config from %s\n", path.c_str());
763f085823Sopenharmony_ci        m_bNoBaseline = true;
773f085823Sopenharmony_ci        return false;
783f085823Sopenharmony_ci    }
793f085823Sopenharmony_ci
803f085823Sopenharmony_ci    INF_MSG("[ PERF     ] Load BaseLines from: %s\n", path.c_str());
813f085823Sopenharmony_ci    return true;
823f085823Sopenharmony_ci}
833f085823Sopenharmony_ci
843f085823Sopenharmony_civoid ParseProperties(const xmlNode currNode, map<string, string>& properties)
853f085823Sopenharmony_ci{
863f085823Sopenharmony_ci    for (auto attrs = currNode.properties; attrs != nullptr; attrs = attrs->next) {
873f085823Sopenharmony_ci        auto name = attrs->name;
883f085823Sopenharmony_ci        if (name == nullptr) {
893f085823Sopenharmony_ci            continue;
903f085823Sopenharmony_ci        }
913f085823Sopenharmony_ci        auto value = xmlGetProp(&currNode, name);
923f085823Sopenharmony_ci        if (value == nullptr) {
933f085823Sopenharmony_ci            continue;
943f085823Sopenharmony_ci        }
953f085823Sopenharmony_ci        string propName(reinterpret_cast<const char*>(name));
963f085823Sopenharmony_ci        string propValue(reinterpret_cast<char*>(value));
973f085823Sopenharmony_ci        properties[propName] = std::move(propValue);
983f085823Sopenharmony_ci        xmlFree(value);
993f085823Sopenharmony_ci    }
1003f085823Sopenharmony_ci}
1013f085823Sopenharmony_ci
1023f085823Sopenharmony_cibool BaseLineManager::ReadXmlFile(string baselinePath)
1033f085823Sopenharmony_ci{
1043f085823Sopenharmony_ci    xmlDocPtr ptrXmlDoc = xmlReadFile(baselinePath.c_str(), nullptr, XML_PARSE_NOBLANKS);
1053f085823Sopenharmony_ci    if (ptrXmlDoc == nullptr) {
1063f085823Sopenharmony_ci        return false;
1073f085823Sopenharmony_ci    }
1083f085823Sopenharmony_ci
1093f085823Sopenharmony_ci    xmlNodePtr ptrRootNode = xmlDocGetRootElement(ptrXmlDoc);
1103f085823Sopenharmony_ci    if (ptrRootNode == nullptr || ptrRootNode->name == nullptr ||
1113f085823Sopenharmony_ci        xmlStrcmp(ptrRootNode->name, reinterpret_cast<const xmlChar*>(XML_TAG_ROOT))) {
1123f085823Sopenharmony_ci        xmlFreeDoc(ptrXmlDoc);
1133f085823Sopenharmony_ci        return false;
1143f085823Sopenharmony_ci    }
1153f085823Sopenharmony_ci
1163f085823Sopenharmony_ci    map<string, string> properties;
1173f085823Sopenharmony_ci    ParseProperties(*ptrRootNode, properties);
1183f085823Sopenharmony_ci    if (properties.count(XML_TAG_DATETIME) == 1) {
1193f085823Sopenharmony_ci        m_bastCfg.date = properties[XML_TAG_DATETIME];
1203f085823Sopenharmony_ci    }
1213f085823Sopenharmony_ci    if (properties.count(XML_TAG_URL) == 1) {
1223f085823Sopenharmony_ci        m_bastCfg.date = properties[XML_TAG_URL];
1233f085823Sopenharmony_ci    }
1243f085823Sopenharmony_ci
1253f085823Sopenharmony_ci    xmlNodePtr currNodePtr = ptrRootNode->xmlChildrenNode;
1263f085823Sopenharmony_ci    for (; currNodePtr != nullptr; currNodePtr = currNodePtr->next) {
1273f085823Sopenharmony_ci        if (currNodePtr->name == nullptr || currNodePtr->type == XML_COMMENT_NODE) {
1283f085823Sopenharmony_ci            xmlFreeDoc(ptrXmlDoc);
1293f085823Sopenharmony_ci            return false;
1303f085823Sopenharmony_ci        }
1313f085823Sopenharmony_ci
1323f085823Sopenharmony_ci        map<string, string> properties_temp;
1333f085823Sopenharmony_ci        ParseProperties(*currNodePtr, properties_temp);
1343f085823Sopenharmony_ci        m_bastCfg.items.push_back(properties_temp);
1353f085823Sopenharmony_ci    }
1363f085823Sopenharmony_ci
1373f085823Sopenharmony_ci    xmlFreeDoc(ptrXmlDoc);
1383f085823Sopenharmony_ci    return true;
1393f085823Sopenharmony_ci}
1403f085823Sopenharmony_ci
1413f085823Sopenharmony_cibool BaseLineManager::IsNoBaseline()
1423f085823Sopenharmony_ci{
1433f085823Sopenharmony_ci    return m_bNoBaseline;
1443f085823Sopenharmony_ci}
1453f085823Sopenharmony_ci
1463f085823Sopenharmony_cidouble BaseLineManager::StrtoDouble(const string& str)
1473f085823Sopenharmony_ci{
1483f085823Sopenharmony_ci    istringstream iss(str);
1493f085823Sopenharmony_ci    double num;
1503f085823Sopenharmony_ci    iss >> num;
1513f085823Sopenharmony_ci    return num;
1523f085823Sopenharmony_ci}
1533f085823Sopenharmony_ci
1543f085823Sopenharmony_cibool BaseLineManager::GetExtraValueDouble(const string testcaseName, const string extra, double &value)
1553f085823Sopenharmony_ci{
1563f085823Sopenharmony_ci    if (testcaseName == "" || extra == "") {
1573f085823Sopenharmony_ci        DBG_MSG("[ ERROR    ] invalid arguments: testcaseName=%s, extra=%s\n", testcaseName.c_str(), extra.c_str());
1583f085823Sopenharmony_ci        return false;
1593f085823Sopenharmony_ci    }
1603f085823Sopenharmony_ci
1613f085823Sopenharmony_ci    for (auto iter = m_bastCfg.items.begin(); iter != m_bastCfg.items.end(); iter++) {
1623f085823Sopenharmony_ci        map<string, string> properties = *iter;
1633f085823Sopenharmony_ci        if (properties.count(XML_TAG_CASENAME) == 1 && properties[XML_TAG_CASENAME] == testcaseName) {
1643f085823Sopenharmony_ci            if (properties.count(extra) == 1) {
1653f085823Sopenharmony_ci                value = StrtoDouble(properties[extra]);
1663f085823Sopenharmony_ci            }
1673f085823Sopenharmony_ci            break;
1683f085823Sopenharmony_ci        }
1693f085823Sopenharmony_ci    }
1703f085823Sopenharmony_ci
1713f085823Sopenharmony_ci    return true;
1723f085823Sopenharmony_ci}
1733f085823Sopenharmony_ci
1743f085823Sopenharmony_ci// GtestPerfTestCase
1753f085823Sopenharmony_ciGtestPerfTestCase::GtestPerfTestCase(BaseLineManager* pManager,
1763f085823Sopenharmony_ci    testing::Test *tester,
1773f085823Sopenharmony_ci    int caseVersion,
1783f085823Sopenharmony_ci    std::string testClassName,
1793f085823Sopenharmony_ci    std::string testInterfaceName)
1803f085823Sopenharmony_ci{
1813f085823Sopenharmony_ci    m_pManager = pManager;
1823f085823Sopenharmony_ci    m_pTester = tester;
1833f085823Sopenharmony_ci    m_dCaseVersion = caseVersion;
1843f085823Sopenharmony_ci    m_strCaseName = "";
1853f085823Sopenharmony_ci    m_strTestClassName = testClassName;
1863f085823Sopenharmony_ci    m_strTestInterfaceName = testInterfaceName;
1873f085823Sopenharmony_ci
1883f085823Sopenharmony_ci    // get test case name from GTEST API.
1893f085823Sopenharmony_ci    // should be use tester->XXX() instead of this.
1903f085823Sopenharmony_ci    if (tester != nullptr && ::testing::UnitTest::GetInstance() != nullptr) {
1913f085823Sopenharmony_ci        m_strCaseName = string(::testing::UnitTest::GetInstance()->current_test_info()->name());
1923f085823Sopenharmony_ci    }
1933f085823Sopenharmony_ci
1943f085823Sopenharmony_ci    // start initialize.
1953f085823Sopenharmony_ci    Initialize();
1963f085823Sopenharmony_ci}
1973f085823Sopenharmony_ci
1983f085823Sopenharmony_cibool GtestPerfTestCase::SetBaseLine(string testcaseName)
1993f085823Sopenharmony_ci{
2003f085823Sopenharmony_ci    if (testcaseName == "") {
2013f085823Sopenharmony_ci        return false;
2023f085823Sopenharmony_ci    }
2033f085823Sopenharmony_ci
2043f085823Sopenharmony_ci    m_strCaseName = testcaseName;
2053f085823Sopenharmony_ci
2063f085823Sopenharmony_ci    return Initialize();
2073f085823Sopenharmony_ci}
2083f085823Sopenharmony_ci
2093f085823Sopenharmony_civoid GtestPerfTestCase::ResetValues()
2103f085823Sopenharmony_ci{
2113f085823Sopenharmony_ci    m_bHasBaseLine = false;
2123f085823Sopenharmony_ci    m_dbBaseLine = -1.0;
2133f085823Sopenharmony_ci    m_bHasLastValue = false;
2143f085823Sopenharmony_ci    m_dbLastValue = -1.0;
2153f085823Sopenharmony_ci    m_bHasFloatRange = false;
2163f085823Sopenharmony_ci    m_dbFloatRange = -1.0;
2173f085823Sopenharmony_ci
2183f085823Sopenharmony_ci    m_bTestResult = false;
2193f085823Sopenharmony_ci    m_dbTestResult = -1.0;
2203f085823Sopenharmony_ci}
2213f085823Sopenharmony_ci
2223f085823Sopenharmony_cibool GtestPerfTestCase::Initialize()
2233f085823Sopenharmony_ci{
2243f085823Sopenharmony_ci    // clear all values.
2253f085823Sopenharmony_ci    ResetValues();
2263f085823Sopenharmony_ci    if (m_strCaseName == "" || m_pManager == nullptr) {
2273f085823Sopenharmony_ci        return false;
2283f085823Sopenharmony_ci    }
2293f085823Sopenharmony_ci
2303f085823Sopenharmony_ci    // get baseline value
2313f085823Sopenharmony_ci    m_bHasBaseLine = m_pManager->GetExtraValueDouble(m_strCaseName, "baseline", m_dbBaseLine);
2323f085823Sopenharmony_ci    if (!m_bHasBaseLine) {
2333f085823Sopenharmony_ci        return false;
2343f085823Sopenharmony_ci    }
2353f085823Sopenharmony_ci
2363f085823Sopenharmony_ci    // get last test value from config.
2373f085823Sopenharmony_ci    m_bHasLastValue = m_pManager->GetExtraValueDouble(m_strCaseName, "lastvalue", m_dbLastValue);
2383f085823Sopenharmony_ci
2393f085823Sopenharmony_ci    // get float range value from config.
2403f085823Sopenharmony_ci    m_bHasFloatRange = m_pManager->GetExtraValueDouble(m_strCaseName, "floatrange", m_dbFloatRange);
2413f085823Sopenharmony_ci    // check values is valid, and update them.
2423f085823Sopenharmony_ci    if (m_bHasFloatRange && (m_dbFloatRange < 0 || m_dbFloatRange >= 1)) {
2433f085823Sopenharmony_ci        DBG_MSG("[ ERROR    ] %s has invalid float range: %f.\n", m_strCaseName.c_str(), m_dbFloatRange);
2443f085823Sopenharmony_ci        m_bHasFloatRange = false;
2453f085823Sopenharmony_ci    }
2463f085823Sopenharmony_ci
2473f085823Sopenharmony_ci    if (!m_bHasFloatRange) {
2483f085823Sopenharmony_ci        m_dbFloatRange = 0.0;
2493f085823Sopenharmony_ci    }
2503f085823Sopenharmony_ci
2513f085823Sopenharmony_ci    if (!m_bHasLastValue) {
2523f085823Sopenharmony_ci        m_dbLastValue = m_dbBaseLine;
2533f085823Sopenharmony_ci    }
2543f085823Sopenharmony_ci
2553f085823Sopenharmony_ci    return true;
2563f085823Sopenharmony_ci}
2573f085823Sopenharmony_ci
2583f085823Sopenharmony_ci// return true if testValue >= baseline value
2593f085823Sopenharmony_cibool GtestPerfTestCase::ExpectLarger(double testValue)
2603f085823Sopenharmony_ci{
2613f085823Sopenharmony_ci    return ExpectValue(testValue, ID_LARGER_IS_BETTER);
2623f085823Sopenharmony_ci}
2633f085823Sopenharmony_ci
2643f085823Sopenharmony_ci// return true if testValue <= baseline value
2653f085823Sopenharmony_cibool GtestPerfTestCase::ExpectSmaller(double testValue)
2663f085823Sopenharmony_ci{
2673f085823Sopenharmony_ci    return ExpectValue(testValue, ID_SMALLER_IS_BETTER);
2683f085823Sopenharmony_ci}
2693f085823Sopenharmony_ci
2703f085823Sopenharmony_cibool GtestPerfTestCase::ExpectValue(double testValue, bool isLargerBetter)
2713f085823Sopenharmony_ci{
2723f085823Sopenharmony_ci    if (m_strCaseName == "") {
2733f085823Sopenharmony_ci        ERR_MSG("[ ERROR    ] failed to get testcase name.\n");
2743f085823Sopenharmony_ci        return false;
2753f085823Sopenharmony_ci    }
2763f085823Sopenharmony_ci
2773f085823Sopenharmony_ci    m_bTestResult = false;
2783f085823Sopenharmony_ci    m_dbTestResult = testValue;
2793f085823Sopenharmony_ci
2803f085823Sopenharmony_ci    // check pass or failed.
2813f085823Sopenharmony_ci    if (m_pManager != nullptr && m_pManager->IsNoBaseline()) {
2823f085823Sopenharmony_ci        // no baseline.json is loaded at startup.
2833f085823Sopenharmony_ci        // set result to TRUE, please check testValue manually.
2843f085823Sopenharmony_ci        m_bTestResult = true;
2853f085823Sopenharmony_ci        EXPECT_TRUE(m_bTestResult);
2863f085823Sopenharmony_ci    } else if (!m_bHasBaseLine) {
2873f085823Sopenharmony_ci        ERR_MSG("[ ERROR    ] %s has NO baseline.\n", m_strCaseName.c_str());
2883f085823Sopenharmony_ci        EXPECT_TRUE(m_bHasBaseLine);
2893f085823Sopenharmony_ci    } else {
2903f085823Sopenharmony_ci        double baseValue = -1;
2913f085823Sopenharmony_ci        if (isLargerBetter) {
2923f085823Sopenharmony_ci            baseValue = (m_dbLastValue >= m_dbBaseLine) ? m_dbLastValue : m_dbBaseLine;
2933f085823Sopenharmony_ci            EXPECT_GE(testValue, (baseValue * (1.0 - m_dbFloatRange)));
2943f085823Sopenharmony_ci            m_bTestResult = (testValue >= (baseValue * (1.0 - m_dbFloatRange))) ? true : false;
2953f085823Sopenharmony_ci        } else {
2963f085823Sopenharmony_ci            baseValue = (m_dbLastValue <= m_dbBaseLine) ? m_dbLastValue : m_dbBaseLine;
2973f085823Sopenharmony_ci            EXPECT_LE(testValue, (baseValue * (1.0 + m_dbFloatRange)));
2983f085823Sopenharmony_ci            m_bTestResult = (testValue <= (baseValue * (1.0 + m_dbFloatRange))) ? true : false;
2993f085823Sopenharmony_ci        }
3003f085823Sopenharmony_ci    }
3013f085823Sopenharmony_ci
3023f085823Sopenharmony_ci    // save result.
3033f085823Sopenharmony_ci    SaveResult(testValue);
3043f085823Sopenharmony_ci
3053f085823Sopenharmony_ci    return m_bTestResult;
3063f085823Sopenharmony_ci}
3073f085823Sopenharmony_ci
3083f085823Sopenharmony_cibool GtestPerfTestCase::SaveResult(double testValue)
3093f085823Sopenharmony_ci{
3103f085823Sopenharmony_ci    char buffer[ID_PROPERTY_LENGTH] = {0};
3113f085823Sopenharmony_ci
3123f085823Sopenharmony_ci    if (m_pTester == nullptr) {
3133f085823Sopenharmony_ci        ERR_MSG("[ ERROR    ] m_pTester is nullptr.\n");
3143f085823Sopenharmony_ci        return false;
3153f085823Sopenharmony_ci    }
3163f085823Sopenharmony_ci
3173f085823Sopenharmony_ci    INF_MSG("[ PERF     ] %s: baseline:%f, test_result: %f\n", m_strCaseName.c_str(), m_dbBaseLine, testValue);
3183f085823Sopenharmony_ci
3193f085823Sopenharmony_ci    (void)memset_s(buffer, sizeof(buffer), 0, sizeof(buffer));
3203f085823Sopenharmony_ci    if (snprintf_s(buffer, sizeof(buffer), sizeof(buffer) - 1, "%g", m_dbBaseLine) > 0) {
3213f085823Sopenharmony_ci        m_pTester->RecordProperty("baseline", buffer);
3223f085823Sopenharmony_ci    }
3233f085823Sopenharmony_ci
3243f085823Sopenharmony_ci    (void)memset_s(buffer, sizeof(buffer), 0, sizeof(buffer));
3253f085823Sopenharmony_ci    if (snprintf_s(buffer, sizeof(buffer), sizeof(buffer) - 1, "%d", m_dCaseVersion) > 0) {
3263f085823Sopenharmony_ci        m_pTester->RecordProperty("tc_version", buffer);
3273f085823Sopenharmony_ci    }
3283f085823Sopenharmony_ci
3293f085823Sopenharmony_ci    (void)memset_s(buffer, sizeof(buffer), 0, sizeof(buffer));
3303f085823Sopenharmony_ci    if (snprintf_s(buffer, sizeof(buffer), sizeof(buffer) - 1, "%g", m_dbLastValue) > 0) {
3313f085823Sopenharmony_ci        m_pTester->RecordProperty("lastvalue", m_bHasLastValue ? buffer : "");
3323f085823Sopenharmony_ci    }
3333f085823Sopenharmony_ci
3343f085823Sopenharmony_ci    (void)memset_s(buffer, sizeof(buffer), 0, sizeof(buffer));
3353f085823Sopenharmony_ci    if (snprintf_s(buffer, sizeof(buffer), sizeof(buffer) - 1, "%g", testValue) > 0) {
3363f085823Sopenharmony_ci        m_pTester->RecordProperty("value", buffer);
3373f085823Sopenharmony_ci    }
3383f085823Sopenharmony_ci
3393f085823Sopenharmony_ci    m_pTester->RecordProperty("category", "performance");
3403f085823Sopenharmony_ci    m_pTester->RecordProperty("test_class", m_strTestClassName.c_str());
3413f085823Sopenharmony_ci    m_pTester->RecordProperty("test_interface", m_strTestInterfaceName.c_str());
3423f085823Sopenharmony_ci
3433f085823Sopenharmony_ci    return true;
3443f085823Sopenharmony_ci}
3453f085823Sopenharmony_ci} // namespace TestAW
3463f085823Sopenharmony_ci} // namespace OHOS
347