/*
 * Copyright (c) 2021-2022 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 "report_test.h"

#include <bitset>
#include <sys/ioctl.h>

using namespace testing::ext;
namespace OHOS {
namespace Developtools {
namespace HiPerf {
template<class T>
void CompareNumberTest(T &lowValue, T &midValue, T &highValue,
                       ReportKeyCompareFunction &compareFunction)
{
    EXPECT_EQ(compareFunction(lowValue, midValue) < 0, true);
    EXPECT_EQ(compareFunction(lowValue, highValue) < 0, true);
    EXPECT_EQ(compareFunction(highValue, midValue) > 0, true);
    EXPECT_EQ(compareFunction(highValue, lowValue) > 0, true);
}

template<class T>
void CompareStringTest(T &lowValue, T &midValue, T &highValue,
                       ReportKeyCompareFunction &compareFunction)
{
    EXPECT_EQ(compareFunction(lowValue, midValue) < 0, true);
    EXPECT_EQ(compareFunction(lowValue, highValue) < 0, true);
    EXPECT_EQ(compareFunction(highValue, midValue) > 0, true);
    EXPECT_EQ(compareFunction(highValue, lowValue) > 0, true);
}
class ReportTest : public testing::Test {
public:
    static void SetUpTestCase(void);
    static void TearDownTestCase(void);
    void SetUp();
    void TearDown();
    std::unique_ptr<Report> report_ = nullptr;
};

void ReportTest::SetUpTestCase() {}

void ReportTest::TearDownTestCase() {}

void ReportTest::SetUp()
{
    report_ = std::make_unique<Report>();
    report_->configs_.emplace_back("dummy", 0, 0);
    report_->configIdIndexMaps_.emplace(0u, 0u); // id 0 as config 0
}

void ReportTest::TearDown() {}

void GetNumberTest(const ReportItem &item, ReportKeyGetFunction &getFunction, uint64_t number,
                   size_t len)
{
    ASSERT_GE(len, 3u);
    std::string itemNumber = std::to_string(number);
    EXPECT_STREQ(getFunction(item, 0, "%*" PRIu64 "").c_str(), itemNumber.c_str());
    EXPECT_STREQ(getFunction(item, 1, "%*" PRIu64 "").c_str(), itemNumber.c_str());
    EXPECT_STREQ(getFunction(item, 3, "%*" PRIu64 "").c_str(), itemNumber.c_str());
    EXPECT_STREQ(getFunction(item, len + 1, "%*" PRIu64 "").c_str(), (" " + itemNumber).c_str());
    EXPECT_STREQ(getFunction(item, len + 2, "%*" PRIu64 "").c_str(), ("  " + itemNumber).c_str());
}

void GetNumberTest(const ReportItem &item, ReportKeyGetFunction &getFunction, int number,
                   size_t len)
{
    ASSERT_GE(len, 3u);
    std::string itemNumber = std::to_string(number);
    int num = 3;
    int num2 = 2;
    EXPECT_STREQ(getFunction(item, 0, "%*d").c_str(), itemNumber.c_str());
    EXPECT_STREQ(getFunction(item, 1, "%*d").c_str(), itemNumber.c_str());
    EXPECT_STREQ(getFunction(item, num, "%*d").c_str(), itemNumber.c_str());
    EXPECT_STREQ(getFunction(item, len + 1, "%*d").c_str(), (" " + itemNumber).c_str());
    EXPECT_STREQ(getFunction(item, len + num2, "%*d").c_str(), ("  " + itemNumber).c_str());
}

void GetStringTest(const ReportItem &item, ReportKeyGetFunction &getFunction,
                   const std::string_view &itemString, size_t len)
{
    int num = 3;
    int num2 = 2;
    ASSERT_GE(len, 3u);
    EXPECT_STREQ(getFunction(item, 0, "%-*s").c_str(), itemString.data());
    EXPECT_STREQ(getFunction(item, 1, "%-*s").c_str(), itemString.data());
    EXPECT_STREQ(getFunction(item, num, "%-*s").c_str(), itemString.data());
    EXPECT_STREQ(getFunction(item, len + 1, "%-*s").c_str(),
                 (std::string(itemString) + " ").c_str());
    EXPECT_STREQ(getFunction(item, len + num2, "%-*s").c_str(),
                 (std::string(itemString) + "  ").c_str());
}

/**
 * @tc.name: ReportItemCallFrame Same
 * @tc.desc:
 * @tc.type: FUNC
 */
HWTEST_F(ReportTest, ReportItemCallFrameSame, TestSize.Level0)
{
    ReportItemCallFrame a("a", 0x0, "aa", 0, 0);
    ReportItemCallFrame aDuplicated("a", 0x0, "aa", 0, 0);
    ReportItemCallFrame a2("a2", 0x0, "aa", 0, 0);
    ReportItemCallFrame aDiffAddr("a", 0x1234, "aa", 0, 0);
    ReportItemCallFrame aDiffEvent("a", 0, "aa", 1234, 0);
    ReportItemCallFrame aDiffSelfEvent("a", 0, "aa", 0, 1234);
    ReportItemCallFrame aDiffDso("a", 0, "aa1234", 0, 1234);
    ReportItemCallFrame b("b", 0x0, "bb", 0, 0);

    EXPECT_EQ(a == aDuplicated, true);
    EXPECT_EQ(a == a2, false);
    EXPECT_EQ(a == aDiffAddr, false);
    EXPECT_EQ(a == aDiffEvent, true);
    EXPECT_EQ(a == aDiffSelfEvent, true);
    EXPECT_EQ(a == aDiffDso, false);
    EXPECT_EQ(a == b, false);
}

/**
 * @tc.name: CompareSortingEventCount
 * @tc.desc:
 * @tc.type: FUNC
 */
HWTEST_F(ReportTest, CompareSortingEventCount, TestSize.Level2)
{
    ReportItemCallFrame a("a", 0x0, "aa", 0, 0);
    ReportItemCallFrame a2("a", 0x0, "aa", 2, 0);
    ReportItemCallFrame a200("a", 0x0, "aa", 200, 0);

    EXPECT_EQ(ReportItemCallFrame::CompareSortingEventCount(a, a), false);
    EXPECT_EQ(ReportItemCallFrame::CompareSortingEventCount(a, a2), false);
    EXPECT_EQ(ReportItemCallFrame::CompareSortingEventCount(a, a200), false);
    EXPECT_EQ(ReportItemCallFrame::CompareSortingEventCount(a2, a200), false);
    EXPECT_EQ(ReportItemCallFrame::CompareSortingEventCount(a200, a200), false);
    EXPECT_EQ(ReportItemCallFrame::CompareSortingEventCount(a200, a2), true);
    EXPECT_EQ(ReportItemCallFrame::CompareSortingEventCount(a200, a), true);
}

/**
 * @tc.name: OrderCallFrames
 * @tc.desc:
 * @tc.type: FUNC
 */
HWTEST_F(ReportTest, OrderCallFrames, TestSize.Level1)
{
    ReportItemCallFrame a100("a", 0x0, "aa", 100, 0);
    ReportItemCallFrame a200("a", 0x0, "aa", 200, 0);
    ReportItemCallFrame a300("a", 0x0, "aa", 300, 0);
    std::vector<ReportItemCallFrame> callframes;

    // check empty no error
    ReportItemCallFrame::OrderCallFrames(callframes);

    callframes.emplace_back(a100);
    callframes.emplace_back(a200);
    callframes.emplace_back(a200);
    callframes.emplace_back(a300);

    // check order
    ReportItemCallFrame::OrderCallFrames(callframes);
    ASSERT_EQ(callframes.size() == 4u, true);
    EXPECT_EQ(callframes[0] == a300, true);
    EXPECT_EQ(callframes[1] == a200, true);
    EXPECT_EQ(callframes[2] == a200, true);
    EXPECT_EQ(callframes[3] == a100, true);
}

/**
 * @tc.name: ReportItemSame
 * @tc.desc:
 * @tc.type: FUNC
 */
HWTEST_F(ReportTest, ReportItemSame, TestSize.Level1)
{
    ReportItem a(1, 2, "comm", "dso", "func", 0x123, 1000);
    ReportItem aDuplicated(1, 2, "comm", "dso", "func", 0x123, 1000);
    ReportItem aDiffPid(10, 2, "comm", "dso", "func", 0x123, 1000);
    ReportItem aDiffTid(1, 20, "comm", "dso", "func", 0x123, 1000);
    ReportItem aDiffComm(1, 2, "comm0", "dso", "func", 0x123, 1000);
    ReportItem aDiffDso(1, 2, "comm", "dso0", "func", 0x123, 1000);
    ReportItem aDiffFunc(1, 2, "comm", "dso", "func0", 0x123, 1000);
    ReportItem aDiffVaddr(1, 2, "comm", "dso", "func", 0x1230, 1000);
    ReportItem aDiffEventCount(1, 2, "comm", "dso", "func", 0x123, 10000);

    EXPECT_EQ(a == aDuplicated, true);

    EXPECT_EQ(a == aDiffPid, false);
    EXPECT_EQ(a == aDiffTid, false);
    EXPECT_EQ(a == aDiffComm, false);
    EXPECT_EQ(a == aDiffDso, false);
    EXPECT_EQ(a == aDiffFunc, false);
    EXPECT_EQ(a == aDiffVaddr, false);
    EXPECT_EQ(a == aDiffEventCount, true);
}

/**
 * @tc.name: ReportItemCompareSortingEventCount
 * @tc.desc:
 * @tc.type: FUNC
 */
HWTEST_F(ReportTest, ReportItemCompareSortingEventCount, TestSize.Level2)
{
    ReportItem a123(1, 2, "comm", "dso", "func", 0x123, 123);
    ReportItem a1234(1, 2, "comm", "dso", "func", 0x1234, 1234);
    ReportItem a12345(1, 2, "comm", "dso", "func", 0x12345, 12345);

    EXPECT_EQ(ReportItem::CompareSortingEventCount(a123, a123), false);
    EXPECT_EQ(ReportItem::CompareSortingEventCount(a123, a1234), false);
    EXPECT_EQ(ReportItem::CompareSortingEventCount(a123, a12345), false);
    EXPECT_EQ(ReportItem::CompareSortingEventCount(a1234, a12345), false);
    EXPECT_EQ(ReportItem::CompareSortingEventCount(a12345, a12345), false);
    EXPECT_EQ(ReportItem::CompareSortingEventCount(a12345, a1234), true);
    EXPECT_EQ(ReportItem::CompareSortingEventCount(a1234, a123), true);
}

/**
 * @tc.name: CompareEventCount
 * @tc.desc:
 * @tc.type: FUNC
 */
HWTEST_F(ReportTest, CompareEventCount, TestSize.Level2)
{
    ReportItem low(1, 4, "comm", "dso", "func", 0x123, 123);
    ReportItem mid(2, 5, "comm", "dso", "func", 0x1234, 1234);
    ReportItem high(3, 6, "comm", "dso", "func", 0x12345, 12345);
    CompareNumberTest(low, mid, high, ReportItem::CompareEventCount);
}

/**
 * @tc.name: GetEventCount
 * @tc.desc:
 * @tc.type: FUNC
 */
HWTEST_F(ReportTest, GetEventCount, TestSize.Level1)
{
    ReportItem a(123, 4, "comm", "dso", "func", 0x123, 123);
    GetNumberTest(a, ReportItem::GetEventCount, a.eventCount_,
                  std::to_string(a.eventCount_).length());
}

/**
 * @tc.name: ComparePid
 * @tc.desc:
 * @tc.type: FUNC
 */
HWTEST_F(ReportTest, ComparePid, TestSize.Level2)
{
    ReportItem low(1, 4, "comm", "dso", "func", 0x123, 123);
    ReportItem mid(2, 5, "comm", "dso", "func", 0x1234, 1234);
    ReportItem high(3, 6, "comm", "dso", "func", 0x12345, 12345);
    CompareNumberTest(low, mid, high, ReportItem::ComparePid);
}

/**
 * @tc.name: GetPid
 * @tc.desc:
 * @tc.type: FUNC
 */
HWTEST_F(ReportTest, GetPid, TestSize.Level1)
{
    ReportItem a(123, 456, "comm", "dso", "func", 0x123, 123);
    GetNumberTest(a, ReportItem::GetPid, a.pid_, std::to_string(a.pid_).length());
}

/**
 * @tc.name: CompareTid
 * @tc.desc:
 * @tc.type: FUNC
 */
HWTEST_F(ReportTest, CompareTid, TestSize.Level1)
{
    ReportItem low(1, 4, "comm", "dso", "func", 0x123, 123);
    ReportItem mid(2, 5, "comm", "dso", "func", 0x1234, 1234);
    ReportItem high(3, 6, "comm", "dso", "func", 0x12345, 12345);
    CompareNumberTest(low, mid, high, ReportItem::CompareTid);
}

/**
 * @tc.name: GetTid
 * @tc.desc:
 * @tc.type: FUNC
 */
HWTEST_F(ReportTest, GetTid, TestSize.Level1)
{
    ReportItem a(123, 456, "comm", "dso", "func", 0x123, 123);
    GetNumberTest(a, ReportItem::GetTid, a.tid_, std::to_string(a.tid_).length());
}

/**
 * @tc.name: CompareComm
 * @tc.desc:
 * @tc.type: FUNC
 */
HWTEST_F(ReportTest, CompareComm, TestSize.Level2)
{
    ReportItem low(1, 4, "comm", "dso", "func", 0x123, 123);
    ReportItem mid(2, 5, "domm", "dso", "func", 0x1234, 1234);
    ReportItem high(3, 6, "eomm", "dso", "func", 0x12345, 12345);
    CompareStringTest(low, mid, high, ReportItem::CompareComm);
}

/**
 * @tc.name: GetComm
 * @tc.desc:
 * @tc.type: FUNC
 */
HWTEST_F(ReportTest, GetComm, TestSize.Level1)
{
    ReportItem a(123, 4, "comm", "dso", "func", 0x123, 123);
    GetStringTest(a, ReportItem::GetComm, a.comm_, a.comm_.length());
}

/**
 * @tc.name: CompareFunc
 * @tc.desc:
 * @tc.type: FUNC
 */
HWTEST_F(ReportTest, CompareFunc, TestSize.Level1)
{
    ReportItem low(1, 4, "comm", "dso", "func", 0x123, 123);
    ReportItem mid(2, 5, "comm", "dso", "gunc", 0x1234, 1234);
    ReportItem high(3, 6, "comm", "dso", "hunc", 0x12345, 12345);
    CompareStringTest(low, mid, high, ReportItem::CompareFunc);
}

/**
 * @tc.name: GetFunc
 * @tc.desc:
 * @tc.type: FUNC
 */
HWTEST_F(ReportTest, GetFunc, TestSize.Level2)
{
    ReportItem a(123, 4, "comm", "dso", "func", 0x123, 123);
    GetStringTest(a, ReportItem::GetFunc, a.func_, a.func_.length());
}
/**
 * @tc.name: CompareDso
 * @tc.desc:
 * @tc.type: FUNC
 */
HWTEST_F(ReportTest, CompareDso, TestSize.Level1)
{
    ReportItem low(1, 4, "comm", "dso", "func", 0x123, 123);
    ReportItem mid(2, 5, "comm", "eso", "func", 0x1234, 1234);
    ReportItem high(3, 6, "comm", "fso", "func", 0x12345, 12345);
    CompareStringTest(low, mid, high, ReportItem::CompareDso);
}

/**
 * @tc.name: GetDso
 * @tc.desc:
 * @tc.type: FUNC
 */
HWTEST_F(ReportTest, GetDso, TestSize.Level1)
{
    ReportItem a(123, 4, "comm", "dso", "func", 0x123, 123);
    GetStringTest(a, ReportItem::GetDso, a.dso_, a.dso_.length());
}

/**
 * @tc.name: CompareFromDso
 * @tc.desc:
 * @tc.type: FUNC
 */
HWTEST_F(ReportTest, CompareFromDso, TestSize.Level1)
{
    ReportItem low(1, 4, "comm", "dso", "func", 0x123, 123);
    ReportItem mid(2, 5, "comm", "dso", "func", 0x1234, 1234);
    ReportItem high(3, 6, "comm", "dso", "func", 0x12345, 12345);
    low.fromDso_ = "fromDso";
    mid.fromDso_ = "gromDso";
    high.fromDso_ = "hromDso";
    CompareStringTest(low, mid, high, ReportItem::CompareFromDso);
}

/**
 * @tc.name: GetFromDso
 * @tc.desc:
 * @tc.type: FUNC
 */
HWTEST_F(ReportTest, GetFromDso, TestSize.Level2)
{
    ReportItem a(123, 4, "comm", "dso", "func", 0x123, 123);
    a.fromDso_ = "fromDso";
    GetStringTest(a, ReportItem::GetFromDso, a.fromDso_, a.fromDso_.length());
}

/**
 * @tc.name: CompareFromFunc
 * @tc.desc:
 * @tc.type: FUNC
 */
HWTEST_F(ReportTest, CompareFromFunc, TestSize.Level1)
{
    ReportItem low(1, 4, "comm", "dso", "func", 0x123, 123);
    ReportItem mid(2, 5, "comm", "dso", "func", 0x1234, 1234);
    ReportItem high(3, 6, "comm", "dso", "func", 0x12345, 12345);
    low.fromFunc_ = "fromFunc";
    mid.fromFunc_ = "gromFunc";
    high.fromFunc_ = "hromFunc";
    CompareStringTest(low, mid, high, ReportItem::CompareFromFunc);
}

/**
 * @tc.name: GetFromFunc
 * @tc.desc:
 * @tc.type: FUNC
 */
HWTEST_F(ReportTest, GetFromFunc, TestSize.Level1)
{
    ReportItem a(123, 4, "comm", "dso", "func", 0x123, 123);
    a.fromFunc_ = "fromFunc";
    GetStringTest(a, ReportItem::GetFromFunc, a.fromFunc_, a.fromFunc_.length());
}

/**
 * @tc.name: UpdateValueMaxLen
 * @tc.desc:
 * @tc.type: FUNC
 */
HWTEST_F(ReportTest, UpdateValueMaxLen, TestSize.Level2)
{
    ReportKey key = {
        "pid", ReportItem::ComparePid, ReportItem::GetPid, "%*d", std::vector<std::string>(),
    };

    key.UpdateValueMaxLen(1);
    key.UpdateValueMaxLen(11);
    key.UpdateValueMaxLen(111);
    key.UpdateValueMaxLen(12345678);
    key.UpdateValueMaxLen(1);
    key.UpdateValueMaxLen(111);
    EXPECT_STREQ(key.maxValue_.c_str(), "12345678");
    EXPECT_EQ(key.maxLen_, 8u);

    ReportKey func = {
        "func", ReportItem::CompareFunc, ReportItem::GetFunc, "%-*s", std::vector<std::string>(),
    };

    func.UpdateValueMaxLen("1");
    func.UpdateValueMaxLen("11");
    func.UpdateValueMaxLen("111");
    func.UpdateValueMaxLen("12345678");
    func.UpdateValueMaxLen("1");
    func.UpdateValueMaxLen("111");
    EXPECT_STREQ(func.maxValue_.c_str(), "12345678");
    EXPECT_EQ(func.maxLen_, 8u);
}

/**
 * @tc.name: GetValue
 * @tc.desc:
 * @tc.type: FUNC
 */
HWTEST_F(ReportTest, GetValue, TestSize.Level0)
{
    ReportKey key = {
        "pid", ReportItem::ComparePid, ReportItem::GetPid, "%*d", std::vector<std::string>(),
    };
    ReportItem a(123, 4, "comm", "dso", "func", 0x123, 123);

    EXPECT_STREQ(key.GetValue(a).c_str(), "123");
    key.maxLen_ = 10u;
    EXPECT_STREQ(key.GetValue(a).c_str(), "       123");
}

/**
 * @tc.name: ShouldDisplay
 * @tc.desc:
 * @tc.type: FUNC
 */
HWTEST_F(ReportTest, ShouldDisplay, TestSize.Level1)
{
    ReportOption option;
    ReportKey pidKey = {
        "pid", ReportItem::ComparePid, ReportItem::GetPid, "%*d", option.displayPids_,
    };
    ReportKey commKey = {
        "comm", ReportItem::CompareComm, ReportItem::GetComm, "%-*s", option.displayComms_,
    };
    ReportItem a(123, 4, "abc", "dso", "func", 0x123, 123);
    ReportItem b(12, 4, "ab", "dso", "func", 0x123, 123);
    ReportItem c(1, 4, "a", "dso", "func", 0x123, 123);

    option.displayPids_ = {"1"};
    EXPECT_EQ(pidKey.ShouldDisplay(a), false);
    EXPECT_EQ(pidKey.ShouldDisplay(b), false);
    EXPECT_EQ(pidKey.ShouldDisplay(c), true);
    option.displayPids_.clear();

    option.displayComms_ = {"a", "ab"};
    EXPECT_EQ(commKey.ShouldDisplay(a), false);
    EXPECT_EQ(commKey.ShouldDisplay(b), true);
    EXPECT_EQ(commKey.ShouldDisplay(c), true);
    option.displayComms_.clear();

    option.displayComms_ = {"a", "ab", "abc"};
    EXPECT_EQ(commKey.ShouldDisplay(a), true);
    EXPECT_EQ(commKey.ShouldDisplay(b), true);
    EXPECT_EQ(commKey.ShouldDisplay(c), true);
    option.displayComms_.clear();

    option.displayComms_ = {"a", "ab", "abc", "d"};
    EXPECT_EQ(commKey.ShouldDisplay(a), true);
    EXPECT_EQ(commKey.ShouldDisplay(b), true);
    EXPECT_EQ(commKey.ShouldDisplay(c), true);
    option.displayComms_.clear();
}

/**
 * @tc.name: MultiLevelSame
 * @tc.desc:
 * @tc.type: FUNC
 */
HWTEST_F(ReportTest, MultiLevelSame, TestSize.Level1)
{
    class ReportMock : public Report {
    public:
        MOCK_METHOD2(MultiLevelCompare, int(const ReportItem &a, const ReportItem &b));
    } report;
    ReportItem dummy(0, 0, "comm", "", "", 0, 0);

    EXPECT_CALL(report, MultiLevelCompare(testing::_, testing::_)).WillOnce(testing::Return(0));
    EXPECT_EQ(report.MultiLevelSame(dummy, dummy), true);

    EXPECT_CALL(report, MultiLevelCompare(testing::_, testing::_)).WillOnce(testing::Return(1));
    EXPECT_EQ(report.MultiLevelSame(dummy, dummy), false);

    EXPECT_CALL(report, MultiLevelCompare(testing::_, testing::_)).WillOnce(testing::Return(-1));
    EXPECT_EQ(report.MultiLevelSame(dummy, dummy), false);
}

/**
 * @tc.name: MultiLevelSorting
 * @tc.desc:
 * @tc.type: FUNC
 */
HWTEST_F(ReportTest, MultiLevelSorting, TestSize.Level2)
{
    class ReportMock : public Report {
    public:
        MOCK_METHOD2(MultiLevelCompare, int(const ReportItem &a, const ReportItem &b));
    } report;
    ReportItem dummy(0, 0, "comm", "", "", 0, 0);

    EXPECT_CALL(report, MultiLevelCompare(testing::_, testing::_))
        .WillOnce(testing::Return(0))
        .WillOnce(testing::Return(0))
        .WillOnce(testing::Return(0));
    EXPECT_EQ(report.MultiLevelCompare(dummy, dummy), 0);     // 1st
    EXPECT_EQ(report.MultiLevelSorting(dummy, dummy), false); // 2nd 3rd and > 0?

    EXPECT_CALL(report, MultiLevelCompare(testing::_, testing::_))
        .WillOnce(testing::Return(1))
        .WillOnce(testing::Return(1))
        .WillOnce(testing::Return(-1));
    EXPECT_EQ(report.MultiLevelCompare(dummy, dummy), 1);
    EXPECT_EQ(report.MultiLevelSorting(dummy, dummy), true); // > 0?

    EXPECT_CALL(report, MultiLevelCompare(testing::_, testing::_))
        .WillOnce(testing::Return(-1))
        .WillOnce(testing::Return(-1))
        .WillOnce(testing::Return(1));
    EXPECT_EQ(report.MultiLevelCompare(dummy, dummy), -1);
    EXPECT_EQ(report.MultiLevelSorting(dummy, dummy), false); // > 0?
}

/**
 * @tc.name: MultiLevelSameAndUpdateCount
 * @tc.desc:
 * @tc.type: FUNC
 */
HWTEST_F(ReportTest, MultiLevelSameAndUpdateCount, TestSize.Level2)
{
    class ReportMock : public Report {
    public:
        MOCK_METHOD2(MultiLevelCompare, int(const ReportItem &a, const ReportItem &b));
    } report;
    ReportItem dummy100(0, 0, "comm", "", "", 0x0, 100);
    ReportItem dummy200(0, 0, "comm", "", "", 0x0, 200);
    ReportItem dummy300(0, 0, "comm", "", "", 0x0, 300);

    EXPECT_EQ(dummy100.eventCount_, 100u);

    EXPECT_CALL(report, MultiLevelCompare(testing::_, testing::_)).WillOnce(testing::Return(0));
    EXPECT_EQ(report.MultiLevelSameAndUpdateCount(dummy100, dummy200), true);
    // if true , 100 + 200
    EXPECT_EQ(dummy100.eventCount_, 300u);

    EXPECT_CALL(report, MultiLevelCompare(testing::_, testing::_)).WillOnce(testing::Return(1));
    EXPECT_EQ(report.MultiLevelSameAndUpdateCount(dummy200, dummy200), false);
    EXPECT_EQ(dummy200.eventCount_, 200u);
    EXPECT_EQ(dummy300.eventCount_, 300u);

    EXPECT_CALL(report, MultiLevelCompare(testing::_, testing::_)).WillOnce(testing::Return(-1));
    EXPECT_EQ(report.MultiLevelSameAndUpdateCount(dummy200, dummy200), false);
    EXPECT_EQ(dummy200.eventCount_, 200u);
    EXPECT_EQ(dummy300.eventCount_, 300u);
}

/**
 * @tc.name: MergeCallFrameCount
 * @tc.desc:
 * @tc.type: FUNC
 */
HWTEST_F(ReportTest, MergeCallFrameCount, TestSize.Level1)
{
    class ReportMock : public Report {
    public:
        MOCK_METHOD2(MultiLevelCompare, int(const ReportItem &a, const ReportItem &b));
    } report;
    ReportItem dummy100(0, 0, "comm", "", "", 0x0, 100);
    ReportItem dummy200(0, 0, "comm", "", "", 0x0, 200);
    ReportItem dummy300(0, 0, "comm", "", "", 0x0, 300);
    EXPECT_EQ(dummy100.eventCount_, 100u);
    ASSERT_EQ(dummy100.callStacks_.size(), 0u);

    /*
    we have a stack like
    dummy 100
       funcA 100
            funcB 100
                funcC 100
    dummy 200
       funcA 100
            funcB 100
    dummy 300
       funcA 100
            funcC 100

    after merge it should be change to
    dummy 600
       funcA 300
            funcB 200
                funcC 100
            funcC 100
    */
    {
        ReportItemCallFrame &child =
            dummy100.callStacks_.emplace_back("funcA", 0x1234, "dso", 100, 0);
        ReportItemCallFrame &child2 = child.childs.emplace_back("funcB", 0x1234, "dso", 100, 0);
        child2.childs.emplace_back("funcC", 0x1234, "dso", 100, 0);
    }

    {
        ReportItemCallFrame &child =
            dummy200.callStacks_.emplace_back("funcA", 0x1234, "dso", 100, 0);
        child.childs.emplace_back("funcB", 0x1234, "dso", 100, 0);
    }
    {
        ReportItemCallFrame &child =
            dummy300.callStacks_.emplace_back("funcA", 0x1234, "dso", 100, 0);
        child.childs.emplace_back("funcC", 0x1234, "dso", 100, 0);
    }
    dummy100.eventCount_ += dummy200.eventCount_;
    ASSERT_EQ(dummy100.callStacks_.size(), 1u);
    ASSERT_EQ(dummy200.callStacks_.size(), 1u);
    ASSERT_STREQ(dummy100.callStacks_[0].func_.data(), "funcA");
    ASSERT_STREQ(dummy200.callStacks_[0].func_.data(), "funcA");
    report_->MergeCallFrameCount(dummy100, dummy200);

    if (dummy100.callStacks_.size() >= 2) {
        printf("%s\n", dummy100.callStacks_[0].ToDebugString().c_str());
        printf("%s\n", dummy100.callStacks_[1].ToDebugString().c_str());
    }
    ASSERT_EQ(dummy100.callStacks_.size(), 1u);

    dummy100.eventCount_ += dummy300.eventCount_;
    report_->MergeCallFrameCount(dummy100, dummy300);

    ASSERT_EQ(dummy100.callStacks_.size(), 1u);
    ASSERT_STREQ(dummy100.callStacks_[0].func_.data(), "funcA");
    EXPECT_EQ(dummy100.callStacks_[0].eventCount_, 300u);

    ASSERT_STREQ(dummy100.callStacks_[0].childs[0].func_.data(), "funcB");
    EXPECT_EQ(dummy100.callStacks_[0].childs[0].eventCount_, 200u);

    ASSERT_STREQ(dummy100.callStacks_[0].childs[1].func_.data(), "funcC");
    EXPECT_EQ(dummy100.callStacks_[0].childs[1].eventCount_, 100u);

    ASSERT_EQ(dummy100.callStacks_[0].childs[0].childs.size(), 1u);
    ASSERT_STREQ(dummy100.callStacks_[0].childs[0].childs[0].func_.data(), "funcC");
    EXPECT_EQ(dummy100.callStacks_[0].childs[0].childs[0].eventCount_, 100u);
}

/**
 * @tc.name: MultiLevelCompare
 * @tc.desc:
 * @tc.type: FUNC
 */
HWTEST_F(ReportTest, MultiLevelCompare, TestSize.Level1)
{
    report_->option_.sortKeys_ = {"comm", "pid", "tid", "dso", "func"};

    ReportItem a(1, 2, "comm", "dso", "funca", 0x1, 4);
    ReportItem b(2, 3, "comm", "dso", "funcb", 0x2, 3);
    ReportItem c(3, 4, "comm", "dso", "funcc", 0x3, 2);
    ReportItem d(4, 5, "comm", "dso", "funcd", 0x4, 1);

    report_->option_.sortKeys_ = {"comm"};
    EXPECT_EQ(report_->MultiLevelCompare(a, b), 0);

    report_->option_.sortKeys_ = {"comm", "pid"};
    EXPECT_EQ(report_->MultiLevelCompare(a, b), -1);
}

/**
 * @tc.name: AddReportItem
 * @tc.desc:
 * @tc.type: FUNC
 */
HWTEST_F(ReportTest, AddReportItem, TestSize.Level0)
{
    PerfRecordSample sample(false, 0, 0, 1);
    sample.callFrames_.emplace_back(0x1, 0x1234, "dummy", "frame1");
    sample.callFrames_.emplace_back(0x2, 0x1234, "dummy", "frame2");
    sample.callFrames_.emplace_back(0x3, 0x1234, "dummy", "frame3");
    sample.callFrames_.emplace_back(0x3, 0x1234, "dummy", "frame4");

    // caller order should be
    // 4
    //  -> 3
    //       -> 2
    //            -> 1

    report_->AddReportItem(sample, false);
    auto &reportItems = report_->configs_[0].reportItems_;
    ASSERT_EQ(reportItems.size(), 1u);

    report_->AddReportItem(sample, true);
    ASSERT_EQ(reportItems.size(), 2u);

    // no call frame
    ASSERT_EQ(reportItems[0].callStacks_.size(), 0u);
    // have call frame
    ASSERT_EQ(reportItems[1].callStacks_.size(), 1u);

    // first on the end caller
    ASSERT_STREQ(reportItems[1].callStacks_[0].func_.data(), "frame4");
    ASSERT_EQ(reportItems[1].callStacks_[0].childs.size(), 1u);

    // next caller
    ASSERT_STREQ(reportItems[1].callStacks_[0].childs[0].func_.data(), "frame3");
    ASSERT_EQ(reportItems[1].callStacks_[0].childs[0].childs.size(), 1u);

    // next caller
    ASSERT_STREQ(reportItems[1].callStacks_[0].childs[0].childs[0].func_.data(), "frame2");
    ASSERT_EQ(reportItems[1].callStacks_[0].childs[0].childs.size(), 1u);

    // top called
    ASSERT_STREQ(reportItems[1].callStacks_[0].childs[0].childs[0].childs[0].func_.data(),
                 "frame1");
    ASSERT_EQ(reportItems[1].callStacks_[0].childs[0].childs[0].childs[0].childs.size(), 0u);

    report_->AddReportItem(sample, false);
    EXPECT_EQ(reportItems.size(), 3u);
}

/**
 * @tc.name: AddReportItem
 * @tc.desc:
 * @tc.type: FUNC
 */
HWTEST_F(ReportTest, AddReportItemBranch, TestSize.Level1)
{
    class PerfRecordSampleMock : public PerfRecordSample {
    public:
        PerfRecordSampleMock(bool inKernel, u32 pid, u32 tid, u64 period)
            : PerfRecordSample(inKernel, pid, tid, period)
        {
        }
    };
    PerfRecordSampleMock sample(false, 0, 0, 1);
    sample.data_.bnr = 3;
    sample.data_.lbr = new PerfBranchEntry[sample.data_.bnr];
    sample.data_.lbr[0].to = 0x123400;
    sample.data_.lbr[0].from = 0x432100;
    sample.data_.lbr[1].to = 0x123401;
    sample.data_.lbr[1].from = 0x432101;
    sample.data_.lbr[2].to = 0x123402;
    sample.data_.lbr[2].from = 0x432102;

    sample.callFrames_.emplace_back(0x1, 0x1234, "dummy", "frame1");
    sample.callFrames_.emplace_back(0x2, 0x1234, "dummy", "frame2");
    sample.callFrames_.emplace_back(0x3, 0x1234, "dummy", "frame3");
    // vaddr is 0 , will not added
    sample.callFrames_.emplace_back(0x3, 0, "dummy", "frame4");

    report_->AddReportItemBranch(sample);
    // == nbr size
    ASSERT_EQ(report_->configs_[0].reportItems_.size(), 3u);
    // no call stack
    ASSERT_EQ(report_->configs_[0].reportItems_[0].callStacks_.size(), 0u);

    for (auto &reportItem : report_->configs_[0].reportItems_) {
        printf("reportItem %s\n", reportItem.ToDebugString().c_str());
    }
    EXPECT_STREQ(report_->configs_[0].reportItems_[0].func_.data(), "swapper@0x123400");
    EXPECT_STREQ(report_->configs_[0].reportItems_[1].func_.data(), "swapper@0x123401");
    EXPECT_STREQ(report_->configs_[0].reportItems_[2].func_.data(), "swapper@0x123402");
}

HWTEST_F(ReportTest, AddReportItemWithAddCounterReadGroup, TestSize.Level2)
{
    report_->addCounterNames_ = {"counter_a", "counter_b"};
    report_->addCounterIdIndexMaps_ = {{1001, 0}, {2002, 1}};
    report_->configs_[0].addCounterNames_ = report_->addCounterNames_;

    PerfRecordSample sample(false, 1, 2, 3);
    sample.sampleType_ = PERF_SAMPLE_READ;
    sample.readFormat_ = PERF_FORMAT_GROUP | PERF_FORMAT_ID;
    u64 readData[] = {111, 1001, 222, 2002};
    sample.data_.read_nr = 2;
    sample.data_.read_values = readData;
    sample.data_.read_ids = readData + 1;
    sample.callFrames_.emplace_back(0x1, 0x1000, "dummy.so", "funcA");

    report_->AddReportItem(sample, false);
    ASSERT_EQ(report_->configs_[0].reportItems_.size(), 1u);
    const ReportItem &item = report_->configs_[0].reportItems_[0];
    ASSERT_EQ(item.counts_.size(), 2u);
    ASSERT_EQ(item.accCounts_.size(), 2u);
    EXPECT_EQ(item.counts_[0], 111u);
    EXPECT_EQ(item.counts_[1], 222u);
    EXPECT_EQ(item.accCounts_[0], 111u);
    EXPECT_EQ(item.accCounts_[1], 222u);
}

HWTEST_F(ReportTest, MultiLevelSameAndUpdateCountWithAddCounterValues, TestSize.Level2)
{
    ReportItem left(1, 2, "comm", "dso", "func", 0x123, 10);
    ReportItem right(1, 2, "comm", "dso", "func", 0x123, 30);
    left.counts_ = {10, 20};
    left.accCounts_ = {10, 20};
    right.counts_ = {30, 50};
    right.accCounts_ = {30, 50};

    EXPECT_TRUE(report_->MultiLevelSameAndUpdateCount(left, right));
    EXPECT_EQ(left.eventCount_, 40u);
    EXPECT_EQ(left.mergedSampleCount_, 2u);
    ASSERT_EQ(left.accCounts_.size(), 2u);
    EXPECT_EQ(left.accCounts_[0], 40u);
    EXPECT_EQ(left.accCounts_[1], 70u);
    EXPECT_EQ(left.counts_[0], 20u);
    EXPECT_EQ(left.counts_[1], 35u);

    ReportItem leftEmpty(1, 2, "comm", "dso", "func", 0x123, 5);
    ReportItem rightOnly(1, 2, "comm", "dso", "func", 0x123, 7);
    rightOnly.counts_ = {9};
    rightOnly.accCounts_ = {9};
    EXPECT_TRUE(report_->MultiLevelSameAndUpdateCount(leftEmpty, rightOnly));
    ASSERT_EQ(leftEmpty.counts_.size(), 1u);
    ASSERT_EQ(leftEmpty.accCounts_.size(), 1u);
    EXPECT_EQ(leftEmpty.counts_[0], 9u);
    EXPECT_EQ(leftEmpty.accCounts_[0], 9u);

    ReportItem leftMismatch(1, 2, "comm", "dso", "func", 0x123, 1);
    ReportItem rightMismatch(1, 2, "comm", "dso", "func", 0x123, 1);
    leftMismatch.counts_ = {7};
    leftMismatch.accCounts_ = {7};
    rightMismatch.counts_ = {1, 2};
    rightMismatch.accCounts_ = {1, 2};
    EXPECT_TRUE(report_->MultiLevelSameAndUpdateCount(leftMismatch, rightMismatch));
    ASSERT_EQ(leftMismatch.accCounts_.size(), 1u);
    EXPECT_EQ(leftMismatch.accCounts_[0], 7u);
}

HWTEST_F(ReportTest, FormatCounterValuesMismatchSize, TestSize.Level2)
{
    EXPECT_EQ(report_->FormatCounterValues({}, {}), "");
    EXPECT_EQ(report_->FormatCounterValues({1, 2}, {"a"}), "");
    EXPECT_EQ(report_->FormatCounterValues({3, 4}, {"a", "b"}), "a=3,b=4");
}

/**
 * @tc.name: PrepareConsole
 * @tc.desc:
 * @tc.type: FUNC
 */
HWTEST_F(ReportTest, PrepareConsole, TestSize.Level1)
{
    struct winsize w = {0, 0, 0, 0};
    ioctl(fileno(stdout), TIOCGWINSZ, &w);
    report_->PrepareConsole();
    if (w.ws_col != 0) {
        EXPECT_EQ(report_->consoleWidth_, w.ws_col);
    } else {
        EXPECT_EQ(report_->consoleWidth_, report_->ConsoleDefaultWidth);
    }
}

HWTEST_F(ReportTest, OverConfigIndex, TestSize.Level1)
{
    pid_t pid = fork();
    ASSERT_NE(pid, -1);

    if (pid == 0) {
        report_->configIdIndexMaps_.emplace(1000u, 1000u);
        report_->GetConfigName(1000);
        _exit(-2);
    } else {
        printf("exit.\n");
    }
}
} // namespace HiPerf
} // namespace Developtools
} // namespace OHOS