/*
 * 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 <gtest/gtest.h>
#include "timer.h"
#include "common_timer_errors.h"
#include <iostream>
#include <thread>
#include <chrono>
#include <stdatomic.h>
#include <sys/time.h>
using namespace testing::ext;
using namespace std;

namespace OHOS {
namespace {
int64_t CurMs()
{
    struct timeval tpend;
    gettimeofday(&tpend, nullptr);
    return (tpend.tv_sec * 1000000 + tpend.tv_usec) / 1000;
}

class UtilsTimerTest : public testing::Test {
public :
    static void SetUpTestCase(void);
    static void TearDownTestCase(void);
    void SetUp();
    void TearDown();
};

void UtilsTimerTest::SetUpTestCase(void)
{
}

void UtilsTimerTest::TearDownTestCase(void)
{
}

void UtilsTimerTest::SetUp(void)
{
}

void UtilsTimerTest::TearDown(void)
{
}

std::atomic<int> g_data1(0);
void TimeOutCallback1()
{
    g_data1 += 1;
}

std::atomic<int> g_data2(0);
void TimeOutCallback2()
{
    g_data2 += 1;
}

/*
 * @tc.name: testTimer001
 * @tc.desc: timer unit test
 *
 * temporarily offline for kernel difference
HWTEST_F(UtilsTimerTest, testTimer001, TestSize.Level0)
{
    g_data1 = 0;
    Utils::Timer timer("test_timer");
    uint32_t ret = timer.Setup();
    EXPECT_EQ(Utils::TIMER_ERR_OK, ret);
    uint32_t timerId = timer.Register(TimeOutCallback1, 1);
    std::this_thread::sleep_for(std::chrono::milliseconds(8));
    timer.Unregister(timerId);
    std::this_thread::sleep_for(std::chrono::milliseconds(10));
    timer.Shutdown();
    EXPECT_GE(g_data1, 2);
    EXPECT_GE(10, g_data1);
*/

/*
 * @tc.name: testTimer002
 * @tc.desc: timer unit test
 */
HWTEST_F(UtilsTimerTest, testTimer002, TestSize.Level0)
{
    g_data1 = 0;
    Utils::Timer timer("test_timer");
    uint32_t ret = timer.Setup();
    EXPECT_EQ(Utils::TIMER_ERR_OK, ret);
    timer.Register(TimeOutCallback1, 1, true);
    std::this_thread::sleep_for(std::chrono::milliseconds(15));
    timer.Shutdown();
    EXPECT_EQ(1, g_data1);
}

/*
 * @tc.name: testTimer003
 * @tc.desc: timer unit test
 */
HWTEST_F(UtilsTimerTest, testTimer003, TestSize.Level0)
{
    g_data1 = 0;
    g_data2 = 0;
    Utils::Timer timer("test_timer");
    uint32_t ret = timer.Setup();
    EXPECT_EQ(Utils::TIMER_ERR_OK, ret);
    timer.Register(TimeOutCallback1, 1);
    timer.Register(TimeOutCallback2, 50);
    std::this_thread::sleep_for(std::chrono::milliseconds(500));
    timer.Shutdown();
    EXPECT_GE(g_data1, 8);
    EXPECT_GE(g_data2, 2);
}

/*
 * @tc.name: testTimer004
 * @tc.desc: timer unit test
 */
HWTEST_F(UtilsTimerTest, testTimer004, TestSize.Level0)
{
    g_data1 = 0;
    Utils::Timer timer("test_timer");
    uint32_t ret = timer.Setup();
    EXPECT_EQ(Utils::TIMER_ERR_OK, ret);
    timer.Register(TimeOutCallback1, 1);
    timer.Register(TimeOutCallback1, 2);
    std::this_thread::sleep_for(std::chrono::milliseconds(30));
    timer.Shutdown();
    EXPECT_GE(g_data1, 5);
}

class A {
public:
    explicit A(int data) : data_(data), timer_("ATimer") {}
    ~A() = default;
    bool Init();
    bool StartTimer(int milliseconds, bool once);
    void StopTimer();
    int GetData() const {return data_;}
private:
    void TimeOutProc()
    {
        data_ -= 1;
    };
    int data_;
    Utils::Timer timer_;
};

bool A::Init()
{
    return timer_.Setup() == Utils::TIMER_ERR_OK;
}

bool A::StartTimer(int milliseconds, bool once)
{
    uint32_t timerId = timer_.Register(std::bind(&A::TimeOutProc, this), milliseconds, once);
    return timerId != Utils::TIMER_ERR_DEAL_FAILED;
}

void A::StopTimer()
{
    timer_.Shutdown();
}

/*
 * @tc.name: testTimer005
 * @tc.desc: timer unit test
 *
 * temporarily offline for kernel difference
HWTEST_F(UtilsTimerTest, testTimer005, TestSize.Level0)
{
    A a(10);
    EXPECT_TRUE(a.Init());
    EXPECT_TRUE(a.StartTimer(1, false));
    std::this_thread::sleep_for(std::chrono::milliseconds(10));
    a.StopTimer();
    EXPECT_GE(8, a.GetData());
}
*/

/*
 * @tc.name: testTimer006
 * @tc.desc: timer unit test
 */
HWTEST_F(UtilsTimerTest, testTimer006, TestSize.Level0)
{
    A a(10);
    EXPECT_TRUE(a.Init());
    EXPECT_TRUE(a.StartTimer(1, true));
    std::this_thread::sleep_for(std::chrono::milliseconds(20));
    a.StopTimer();
    EXPECT_EQ(9, a.GetData());
}

/*
 * @tc.name: testTimer007
 * @tc.desc: abnormal case
 */
HWTEST_F(UtilsTimerTest, testTimer007, TestSize.Level0)
{
    g_data1 = 0;
    Utils::Timer timer("test_timer", -1);
    uint32_t ret = timer.Setup();
    EXPECT_EQ(Utils::TIMER_ERR_OK, ret);

    uint32_t timerId = 0;
    for (uint32_t i = 0; i < 10; i++) {
        timerId = timer.Register(TimeOutCallback1, 7, true);
        std::this_thread::sleep_for(std::chrono::milliseconds(10));
    }
    timer.Unregister(timerId);
    timer.Unregister(timerId);

    timer.Shutdown();
    timer.Shutdown(false);
    EXPECT_GE(g_data1, 5);
}

/*
 * @tc.name: testTimer008
 * @tc.desc: timer sleep test for ivi
 */
HWTEST_F(UtilsTimerTest, testTimer008, TestSize.Level0)
{
    g_data1 = 0;
    Utils::Timer timer("test_timer");
    uint32_t ret = timer.Setup();
    EXPECT_EQ(Utils::TIMER_ERR_OK, ret);
    timer.Register(TimeOutCallback1, 10);

    for (int i = 0; i < 11; i++) {
        int64_t pre = CurMs();
        std::this_thread::sleep_for(std::chrono::milliseconds(10));
        int64_t cur = CurMs();
        EXPECT_GE(cur - pre, 10);
    }
    timer.Shutdown();
    EXPECT_GE(g_data1, 10);
}

/*
 * @tc.name: testTimer009
 * @tc.desc: recursive test
 */
void DoFunc(Utils::Timer &timer, int &count)
{
    (void)timer.Register(
        [&timer, &count]() {
            count += 1;
            if (count > 9) {
                return;
            }
            DoFunc(timer, count);
        },
        10, true);
    g_data1++;
}

void DoFunc2(Utils::Timer &timer, int &count)
{
    (void)timer.Register(
        [&timer, &count]() {
            count += 1;
            if (count > 9) {
                return;
            }
            DoFunc2(timer, count);
        },
        10, true);
    g_data1++;
}

HWTEST_F(UtilsTimerTest, testTimer009, TestSize.Level0)
{
    g_data1 = 0;
    Utils::Timer timer("test_timer");
    uint32_t ret = timer.Setup();
    EXPECT_EQ(Utils::TIMER_ERR_OK, ret);

    int cnt = 0, cnt1 = 0;
    DoFunc(timer, cnt);
    DoFunc2(timer, cnt1);
    std::this_thread::sleep_for(std::chrono::milliseconds(50));
    EXPECT_GE(g_data1, 5);  /* 8 for max */
    EXPECT_GE(14, g_data1); /* 10 for min */
    std::this_thread::sleep_for(std::chrono::milliseconds(50));
    timer.Shutdown();
    EXPECT_GE(g_data1, 10); /* 18 for max */
}

/*
 * @tc.name: testTimer010
 * @tc.desc: once timer register
 */
HWTEST_F(UtilsTimerTest, testTimer010, TestSize.Level0)
{
    g_data1 = 0;
    Utils::Timer timer("test_timer");
    uint32_t ret = timer.Setup();
    EXPECT_EQ(Utils::TIMER_ERR_OK, ret);
    timer.Register(TimeOutCallback1, 10, true);
    timer.Register(TimeOutCallback1, 10);
    timer.Register(TimeOutCallback1, 10, true);
    timer.Register(TimeOutCallback1, 10);
    std::this_thread::sleep_for(std::chrono::milliseconds(52));
    timer.Shutdown();
    EXPECT_GE(g_data1, 8); /* 12 for max */
}

/*
 * @tc.name: testTimer011
 * @tc.desc: once timer register
 */
HWTEST_F(UtilsTimerTest, testTimer011, TestSize.Level0)
{
    g_data1 = 0;
    Utils::Timer timer("test_timer");
    uint32_t ret = timer.Setup();
    EXPECT_EQ(Utils::TIMER_ERR_OK, ret);
    timer.Register(TimeOutCallback1, 10);
    timer.Register(TimeOutCallback1, 10, true);
    timer.Register(TimeOutCallback1, 10);
    timer.Register(TimeOutCallback1, 10, true);
    std::this_thread::sleep_for(std::chrono::milliseconds(52));
    timer.Shutdown();
    EXPECT_GE(g_data1, 8); /* 12 for max */
}

/*
 * @tc.name: testTimer012
 * @tc.desc: Test double setup.
 */
HWTEST_F(UtilsTimerTest, testTimer012, TestSize.Level0)
{
    g_data1 = 0;
    Utils::Timer timer("test_timer");
    uint32_t ret = timer.Setup();
    EXPECT_EQ(Utils::TIMER_ERR_OK, ret);
    ret = timer.Setup();
    EXPECT_EQ(Utils::TIMER_ERR_INVALID_VALUE, ret);

    timer.Shutdown();
}

/*
 * @tc.name: testTimer013
 * @tc.desc: Test uncommon operations.
 */
HWTEST_F(UtilsTimerTest, testTimer013, TestSize.Level0)
{
    g_data1 = 0;
    Utils::Timer timer("test_timer", -1);
    uint32_t ret = timer.Setup();
    EXPECT_EQ(Utils::TIMER_ERR_OK, ret);
    std::this_thread::sleep_for(std::chrono::milliseconds(1));
    timer.Shutdown();

    Utils::Timer timer1("test_timer_1");
    ret = timer1.Setup();
    EXPECT_EQ(Utils::TIMER_ERR_OK, ret);
    std::this_thread::sleep_for(std::chrono::milliseconds(1));
    timer1.Shutdown(false);
}
}  // namespace
}  // namespace OHOS