/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2023. All rights reserved.
 * 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 <fcntl.h>
#include <hwext/gtest-ext.h>
#include <hwext/gtest-tag.h>
#include <memory>
#include <string>

#include "parser/ptreader_parser/ptreader_parser.h"
#include "parser/common_types.h"
#include "string_help.h"
#include "trace_streamer_selector.h"

using namespace testing::ext;
using namespace SysTuning::TraceStreamer;
using namespace SysTuning::base;

namespace SysTuning {
namespace TraceStreamer {
class PtreaderParserTest : public ::testing::Test {
public:
    void SetUp()
    {
        stream_.InitFilter();
    }

    void TearDown() {}

public:
    SysTuning::TraceStreamer::TraceStreamerSelector stream_ = {};
    const std::string dbPath_ = "../../test/resource/out.db";
};

/**
 * @tc.name: ParseNoData
 * @tc.desc: Test ParseTraceDataSegment interface Parse empty memory
 * @tc.type: FUNC
 */
HWTEST_F(PtreaderParserTest, ParseNoData, TestSize.Level1)
{
    TS_LOGI("test2-1");
    auto buf = std::make_unique<uint8_t[]>(1);
    PtreaderParser ptreaderParser(stream_.traceDataCache_.get(), stream_.streamFilters_.get());
    ptreaderParser.ParseTraceDataSegment(std::move(buf), 1);
    ptreaderParser.WaitForParserEnd();
    EXPECT_TRUE(ptreaderParser.TraceCommentLines() == 0);
    EXPECT_TRUE(ptreaderParser.ParsedTraceValidLines() == 0);
    EXPECT_TRUE(ptreaderParser.ParsedTraceInvalidLines() == 0);
}

/**
 * @tc.name: ParseNoDataWhithLineFlag
 * @tc.desc: Test ParseTraceDataSegment interface Parse "\n"
 * @tc.type: FUNC
 */
HWTEST_F(PtreaderParserTest, ParseNoDataWhithLineFlag, TestSize.Level1)
{
    TS_LOGI("test2-2");
    constexpr uint32_t bufSize = 1024;
    auto buf = std::make_unique<uint8_t[]>(bufSize);
    auto realBufSize = strlen(" \n");
    if (memcpy_s(buf.get(), bufSize, " \n", realBufSize)) {
        EXPECT_TRUE(false);
        return;
    }
    PtreaderParser ptreaderParser(stream_.traceDataCache_.get(), stream_.streamFilters_.get());
    ptreaderParser.ParseTraceDataSegment(std::move(buf), realBufSize);
    ptreaderParser.WaitForParserEnd();
    stream_.traceDataCache_->ExportDatabase(dbPath_);
    EXPECT_TRUE(access(dbPath_.c_str(), F_OK) == 0);
    EXPECT_TRUE(ptreaderParser.TraceCommentLines() == 0);
    EXPECT_TRUE(ptreaderParser.ParsedTraceValidLines() == 0);
    EXPECT_TRUE(ptreaderParser.ParsedTraceInvalidLines() == 1);
}

/**
 * @tc.name: ParseInvalidData
 * @tc.desc: Test ParseTraceDataSegment interface Parse invalid string
 * @tc.type: FUNC
 */
HWTEST_F(PtreaderParserTest, ParseInvalidData, TestSize.Level1)
{
    TS_LOGI("test2-3");
    constexpr uint32_t bufSize = 1024;
    auto buf = std::make_unique<uint8_t[]>(bufSize);
    auto realBufSize = strlen("0123456789\n");
    if (memcpy_s(buf.get(), bufSize, "0123456789\n", realBufSize)) {
        EXPECT_TRUE(false);
        return;
    }
    PtreaderParser ptreaderParser(stream_.traceDataCache_.get(), stream_.streamFilters_.get());
    ptreaderParser.ParseTraceDataSegment(std::move(buf), realBufSize);
    ptreaderParser.WaitForParserEnd();
    stream_.traceDataCache_->ExportDatabase(dbPath_);
    EXPECT_TRUE(access(dbPath_.c_str(), F_OK) == 0);
    EXPECT_TRUE(ptreaderParser.TraceCommentLines() == 0);
    EXPECT_TRUE(ptreaderParser.ParsedTraceValidLines() == 0);
    EXPECT_TRUE(ptreaderParser.ParsedTraceInvalidLines() == 1);
}

/**
 * @tc.name: ParseComment
 * @tc.desc: Test ParseTraceDataSegment interface Parse Multiline data
 * @tc.type: FUNC
 */
HWTEST_F(PtreaderParserTest, ParseComment, TestSize.Level1)
{
    TS_LOGI("test2-4");
    constexpr uint32_t bufSize = 1024;
    auto buf = std::make_unique<uint8_t[]>(bufSize);
    auto realBufSize = strlen("TRACE: \n# tracer: nop \n# \n");
    if (memcpy_s(buf.get(), bufSize, "TRACE: \n# tracer: nop \n# \n", realBufSize)) {
        EXPECT_TRUE(false);
        return;
    }
    PtreaderParser ptreaderParser(stream_.traceDataCache_.get(), stream_.streamFilters_.get());
    ptreaderParser.ParseTraceDataSegment(std::move(buf), realBufSize);
    ptreaderParser.WaitForParserEnd();
    stream_.traceDataCache_->ExportDatabase(dbPath_);
    EXPECT_TRUE(access(dbPath_.c_str(), F_OK) == 0);
    EXPECT_TRUE(ptreaderParser.TraceCommentLines() == 2);
    EXPECT_TRUE(ptreaderParser.ParsedTraceValidLines() == 0);
    EXPECT_TRUE(ptreaderParser.ParsedTraceInvalidLines() == 1);
}

/**
 * @tc.name: ParseInvalidLines
 * @tc.desc: Test ParseTraceDataSegment interface Parse Multiline Invalid data
 * @tc.type: FUNC
 */
HWTEST_F(PtreaderParserTest, ParseInvalidLines, TestSize.Level1)
{
    TS_LOGI("test2-5");
    constexpr uint32_t bufSize = 1024;
    auto buf = std::make_unique<uint8_t[]>(bufSize);
    auto realBufSize = strlen(" \nafafda\n");
    if (memcpy_s(buf.get(), bufSize, " \nafafda\n", realBufSize)) {
        EXPECT_TRUE(false);
        return;
    }
    PtreaderParser ptreaderParser(stream_.traceDataCache_.get(), stream_.streamFilters_.get());
    ptreaderParser.ParseTraceDataSegment(std::move(buf), realBufSize);
    ptreaderParser.WaitForParserEnd();
    stream_.traceDataCache_->ExportDatabase(dbPath_);
    EXPECT_TRUE(access(dbPath_.c_str(), F_OK) == 0);
    EXPECT_TRUE(ptreaderParser.TraceCommentLines() == 0);
    EXPECT_TRUE(ptreaderParser.ParsedTraceValidLines() == 0);
    EXPECT_TRUE(ptreaderParser.ParsedTraceInvalidLines() == 2);
}

/**
 * @tc.name: ParseNormal
 * @tc.desc: Test ParseTraceDataItem interface Parse normal data
 * @tc.type: FUNC
 */
HWTEST_F(PtreaderParserTest, ParseNormal, TestSize.Level1)
{
    TS_LOGI("test2-6");
    std::string str(
        "ACCS0-2716  ( 2519) [000] ...1 168758.662861: binder_transaction: \
        transaction=25137708 dest_node=4336 dest_proc=924 dest_thread=0 reply=0 flags=0x10 code=0x3 \n");
    PtreaderParser ptreaderParser(stream_.traceDataCache_.get(), stream_.streamFilters_.get());
    ptreaderParser.ParseTraceDataItem(str);
    ptreaderParser.WaitForParserEnd();

    EXPECT_TRUE(ptreaderParser.TraceCommentLines() == 0);
    EXPECT_TRUE(ptreaderParser.ParsedTraceValidLines() == 1);
    EXPECT_TRUE(ptreaderParser.ParsedTraceInvalidLines() == 0);
}

/**
 * @tc.name: LineParser_abnormal_pid_err
 * @tc.desc: Test ParseTraceDataItem interface Parse data with error pid
 * @tc.type: FUNC
 */
HWTEST_F(PtreaderParserTest, LineParser_abnormal_pid_err, TestSize.Level1)
{
    TS_LOGI("test2-7");
    std::string str(
        "ACCS0-27X6  ( 2519) [000] ...1 168758.662861: binder_transaction: \
        transaction=25137708 dest_node=4336 dest_proc=924 dest_thread=0 reply=0 flags=0x10 code=0x3 \n");
    PtreaderParser ptreaderParser(stream_.traceDataCache_.get(), stream_.streamFilters_.get());
    ptreaderParser.ParseTraceDataItem(str);
    ptreaderParser.WaitForParserEnd();

    EXPECT_TRUE(ptreaderParser.TraceCommentLines() == 0);
    EXPECT_TRUE(ptreaderParser.ParsedTraceValidLines() == 0);
    EXPECT_TRUE(ptreaderParser.ParsedTraceInvalidLines() == 1);
}

/**
 * @tc.name: LineParserWithInvalidCpu
 * @tc.desc: Test ParseTraceDataItem interface Parse data with invalid cpu
 * @tc.type: FUNC
 */
HWTEST_F(PtreaderParserTest, LineParserWithInvalidCpu, TestSize.Level1)
{
    TS_LOGI("test2-8");
    std::string str(
        "ACCS0-2716  ( 2519) [00X] ...1 168758.662861: binder_transaction: \
        transaction=25137708 dest_node=4336 dest_proc=924 dest_thread=0 reply=0 flags=0x10 code=0x3 \n");
    PtreaderParser ptreaderParser(stream_.traceDataCache_.get(), stream_.streamFilters_.get());
    ptreaderParser.ParseTraceDataItem(str);
    ptreaderParser.WaitForParserEnd();

    EXPECT_TRUE(ptreaderParser.TraceCommentLines() == 0);
    EXPECT_TRUE(ptreaderParser.ParsedTraceValidLines() == 0);
    EXPECT_TRUE(ptreaderParser.ParsedTraceInvalidLines() == 1);
}

/**
 * @tc.name: LineParserWithInvalidTs
 * @tc.desc: Test ParseTraceDataItem interface Parse data with invalid ts
 * @tc.type: FUNC
 */
HWTEST_F(PtreaderParserTest, LineParserWithInvalidTs, TestSize.Level1)
{
    TS_LOGI("test2-9");
    std::string str(
        "ACCS0-2716  ( 2519) [000] ...1 168758.662X61: binder_transaction: \
        transaction=25137708 dest_node=4336 dest_proc=924 dest_thread=0 reply=0 flags=0x10 code=0x3 \n");
    PtreaderParser ptreaderParser(stream_.traceDataCache_.get(), stream_.streamFilters_.get());
    ptreaderParser.ParseTraceDataItem(str);
    ptreaderParser.WaitForParserEnd();

    EXPECT_TRUE(ptreaderParser.TraceCommentLines() == 0);
    EXPECT_TRUE(ptreaderParser.ParsedTraceValidLines() == 0);
    EXPECT_TRUE(ptreaderParser.ParsedTraceInvalidLines() == 1);
}

/**
 * @tc.name: NomalHtmlBytraceFile
 * @tc.desc: Test ParseTraceDataItem interface Parse normal html format bytrace data
 * @tc.type: FUNC
 */
HWTEST_F(PtreaderParserTest, NomalHtmlBytraceFile, TestSize.Level1)
{
    TS_LOGI("test2-10");
    constexpr uint32_t bufSize = 1024;
    auto buf = std::make_unique<uint8_t[]>(bufSize);
    char realBuf[] =
        "<!DOCTYPE html>\r\n<html>\r\n<script class=\"trace-data\" type=\"application/text\">\r\n"
        "ACCS0-2716  ( 2519) [000] ...1 168758.662861: binder_transaction: "
        "transaction=25137708 dest_node=4336 dest_proc=924 dest_thread=0 reply=0 flags=0x10 code=0x3 \r\n"
        "</script>\r\n</html>\n";
    auto realBufSize = sizeof(realBuf);

    if (memcpy_s(buf.get(), bufSize, realBuf, realBufSize)) {
        EXPECT_TRUE(false);
        return;
    }
    PtreaderParser ptreaderParser(stream_.traceDataCache_.get(), stream_.streamFilters_.get());
    ptreaderParser.ParseTraceDataSegment(std::move(buf), realBufSize);
    ptreaderParser.WaitForParserEnd();
    stream_.traceDataCache_->ExportDatabase(dbPath_);
    EXPECT_TRUE(access(dbPath_.c_str(), F_OK) == 0);
    EXPECT_TRUE(ptreaderParser.TraceCommentLines() == 0);
    EXPECT_TRUE(ptreaderParser.ParsedTraceValidLines() == 1);
    EXPECT_TRUE(ptreaderParser.ParsedTraceInvalidLines() == 0);
}

/**
 * @tc.name: MultiScriptHtmlBytraceFile
 * @tc.desc: Test ParseTraceDataItem interface Parse html format bytrace data with multiple script section
 * @tc.type: FUNC
 */
HWTEST_F(PtreaderParserTest, MultiScriptHtmlBytraceFile, TestSize.Level1)
{
    TS_LOGI("test2-11");
    constexpr uint32_t bufSize = 1024;
    auto buf = std::make_unique<uint8_t[]>(bufSize);
    char realBuf[] =
        "<!DOCTYPE html>\r\n<html>\r\n<script class=\"trace-data\" type=\"application/text\">\r\n"
        "ACCS0-2716  ( 2519) [000] ...1 168758.662861: binder_transaction: "
        "transaction=25137708 dest_node=4336 dest_proc=924 dest_thread=0 reply=0 flags=0x10 code=0x3 \r\n"
        "</script>\r\n<script class=\"trace-data\" type=\"application/text\">\r\n"
        "{\"traceEvents\": [{\"category\": \"process_argv\", \"name\": \"process_argv\", \"args\": "
        "{\"argv\": [\"c:\\\\platform-tools\\\\systrace\\\\systrace.py\", \"--from-file\", \"d:\\\\trace_output\", "
        "\"-o\", \"output.html\"]}, "
        "\"pid\": 18892, \"ts\": 13988802989.6, \"tid\": 4464, \"ph\": \"M\"}], \"metadata\": {\"clock-domain\": "
        "\"SYSTRACE\"}}  </script>"
        "</html>\n";
    auto realBufSize = sizeof(realBuf);
    if (memcpy_s(buf.get(), bufSize, realBuf, realBufSize)) {
        EXPECT_TRUE(false);
        return;
    }
    PtreaderParser ptreaderParser(stream_.traceDataCache_.get(), stream_.streamFilters_.get());
    ptreaderParser.ParseTraceDataSegment(std::move(buf), realBufSize);
    ptreaderParser.WaitForParserEnd();
    stream_.traceDataCache_->ExportDatabase(dbPath_);
    EXPECT_TRUE(access(dbPath_.c_str(), F_OK) == 0);
    EXPECT_TRUE(ptreaderParser.TraceCommentLines() == 0);
    EXPECT_TRUE(ptreaderParser.ParsedTraceValidLines() == 1);
    EXPECT_TRUE(ptreaderParser.ParsedTraceInvalidLines() == 1);
}

} // namespace TraceStreamer
} // namespace SysTuning