/*
 * Copyright (C) 2024 HiHope Open Source Organization.
 * 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 <csignal>
#include <ctime>
#include <iostream>
#include <unistd.h>
#include <gtest/gtest.h>
#include "securec.h"

using namespace testing::ext;
static bool g_timerExpired = false;
static bool g_sigalRm = false;
static const int DELAY_TIME = 100000;

void TimeHandler(union sigval sv)
{
    g_timerExpired = true;
}
void SigalrmHandler(int sig)
{
    g_sigalRm = true;
}

class TimerCreateApiTest : public testing::Test {
public:
static void SetUpTestCase();
static void TearDownTestCase();
void SetUp();
void TearDown();
private:
};
void TimerCreateApiTest::SetUp()
{
    g_timerExpired = false;
    g_sigalRm = false;
}
void TimerCreateApiTest::TearDown()
{
    sighandler_t oldHandler = signal(SIGRTMIN, SIG_DFL);
    EXPECT_NE(oldHandler, SIG_ERR);
}
void TimerCreateApiTest::SetUpTestCase()
{
}
void TimerCreateApiTest::TearDownTestCase()
{
}

/*
 * @tc.number : SUB_KERNEL_SYSCALL_TIMER_CREATE_0100
 * @tc.name   : TimerCreateRealtimeAndGetoverrunSuccess_0001
 * @tc.desc   : Test the timer_create and timer_getoverrun with CLOCK_REALTIME.
 * @tc.size   : MediumTest
 * @tc.type   : Function
 * @tc.level  : Level 1
 */
HWTEST_F(TimerCreateApiTest, TimerCreateRealtimeAndGetoverrunSuccess_0001, Function | MediumTest | Level1)
{
    timer_t timerId;
    struct sigevent sev;
    const clockid_t CLOCK_ID = CLOCK_REALTIME;
    struct itimerspec its = {
        .it_interval = {
            .tv_sec = 0,
            .tv_nsec = 4000,
        },
        .it_value = {
            .tv_sec = 0,
            .tv_nsec = 5000,
        },
    };

    sev.sigev_notify = SIGEV_THREAD;
    sev.sigev_signo = SIGRTMIN;
    sev.sigev_notify_function = TimeHandler;

    EXPECT_EQ(timer_create(CLOCK_ID, &sev, &timerId), 0);
    EXPECT_EQ(timer_settime(timerId, 0, &its, nullptr), 0);

    usleep(DELAY_TIME);
    EXPECT_TRUE(g_timerExpired);

    int overruns = timer_getoverrun(timerId);
    EXPECT_TRUE(overruns >= 0);

    int timerRes = timer_delete(timerId);
    EXPECT_EQ(timerRes, 0);
}

/*
 * @tc.number : SUB_KERNEL_SYSCALL_TIMER_CREATE_0200
 * @tc.name   : TimerCreateMonotonicSuccess_0002
 * @tc.desc   : Test the timer_create with CLOCK_MONOTONIC.
 * @tc.size   : MediumTest
 * @tc.type   : Function
 * @tc.level  : Level 1
 */
HWTEST_F(TimerCreateApiTest, TimerCreateMonotonicSuccess_0002, Function | MediumTest | Level1)
{
    timer_t timerId;
    struct itimerspec currValue;
    const clockid_t CLOCK_ID = CLOCK_MONOTONIC;
    signal(SIGALRM, SigalrmHandler);
    struct itimerspec its = {
        .it_interval = {
            .tv_sec = 0,
            .tv_nsec = 0,
        },
        .it_value = {
            .tv_sec = 0,
            .tv_nsec = 5000,
        },
    };

    struct sigevent sev = {
        .sigev_signo = SIGALRM,
        .sigev_notify = SIGEV_SIGNAL,
        .sigev_notify_function = TimeHandler,
    };

    EXPECT_EQ(timer_create(CLOCK_ID, &sev, &timerId), 0);
    EXPECT_EQ(timer_settime(timerId, 0, &its, nullptr), 0);

    usleep(DELAY_TIME);
    EXPECT_EQ(timer_gettime(timerId, &currValue), 0);
    EXPECT_TRUE(currValue.it_value.tv_sec < 1 && currValue.it_value.tv_nsec < 5000);

    int timerRes = timer_delete(timerId);
    EXPECT_EQ(timerRes, 0);
}

/*
 * @tc.number : SUB_KERNEL_SYSCALL_TIMER_CREATE_0300
 * @tc.name   : TimerCreateRealTimeAlarmSuccess_0003
 * @tc.desc   : Test the timer_create with CLOCK_REALTIME_ALARM.
 * @tc.size   : MediumTest
 * @tc.type   : Function
 * @tc.level  : Level 1
 */
HWTEST_F(TimerCreateApiTest, TimerCreateRealTimeAlarmSuccess_0003, Function | MediumTest | Level1)
{
    timer_t timerId;
    const clockid_t CLOCK_ID = CLOCK_REALTIME_ALARM;
    sighandler_t oldHandler = signal(SIGALRM, SigalrmHandler);
    EXPECT_NE(oldHandler, SIG_ERR);
    struct sigevent sev = {
        .sigev_signo = SIGALRM,
        .sigev_notify = SIGEV_SIGNAL,
        .sigev_notify_function = TimeHandler,
    };
    struct itimerspec its = {
        .it_interval = {
            .tv_sec = 0,
            .tv_nsec = 0,
        },
        .it_value = {
            .tv_sec = 0,
            .tv_nsec = 50,
        },
    };

    EXPECT_EQ(timer_create(CLOCK_ID, &sev, &timerId), 0);
    EXPECT_EQ(timer_settime(timerId, 0, &its, nullptr), 0);

    usleep(DELAY_TIME);

    EXPECT_FALSE(g_timerExpired);
    EXPECT_TRUE(g_sigalRm);

    int timerRes = timer_delete(timerId);
    EXPECT_EQ(timerRes, 0);
}

/*
 * @tc.number : SUB_KERNEL_SYSCALL_TIMER_CREATE_0400
 * @tc.name   : TimeSetTimeOldValueSuccess_0004
 * @tc.desc   : Test the timer_settime get the old value.
 * @tc.size   : MediumTest
 * @tc.type   : Function
 * @tc.level  : Level 1
 */
HWTEST_F(TimerCreateApiTest, TimeSetTimeOldValueSuccess_0004, Function | MediumTest | Level1)
{
    timer_t timerId;
    struct itimerspec oldValue;
    struct itimerspec oldValue2;
    struct itimerspec newValue = {
        .it_interval = {
            .tv_sec = 1,
            .tv_nsec = 0,
        },
        .it_value = {
            .tv_sec = 0,
            .tv_nsec = 50,
        },
    };
    EXPECT_EQ(timer_create(CLOCK_REALTIME, nullptr, &timerId), 0);

    EXPECT_EQ(timer_settime(timerId, 0, &newValue, &oldValue), 0);
    EXPECT_EQ(oldValue.it_value.tv_sec, 0);
    EXPECT_EQ(oldValue.it_value.tv_nsec, 0);
    EXPECT_EQ(oldValue.it_interval.tv_sec, 0);
    EXPECT_EQ(oldValue.it_interval.tv_nsec, 0);

    newValue.it_value.tv_sec = 0;
    newValue.it_value.tv_nsec = 50;
    newValue.it_interval.tv_sec = 1;
    newValue.it_interval.tv_nsec = 0;
    EXPECT_EQ(timer_settime(timerId, 0, &newValue, &oldValue2), 0);
    EXPECT_EQ(oldValue2.it_interval.tv_sec, 1);
    EXPECT_EQ(oldValue2.it_interval.tv_nsec, 0);

    int timerRes = timer_delete(timerId);
    EXPECT_EQ(timerRes, 0);
}

/*
 * @tc.number : SUB_KERNEL_SYSCALL_TIMER_SETTIME_0500
 * @tc.name   : TimerSetTimeAbstimeSuccess_0005
 * @tc.desc   : Test the timer_settime with TIMER_ABSTIME.
 * @tc.size   : MediumTest
 * @tc.type   : Function
 * @tc.level  : Level 1
 */
HWTEST_F(TimerCreateApiTest, TimerSetTimeAbstimeSuccess_0005, Function | MediumTest | Level1)
{
    timer_t timerId;
    struct itimerspec newValue;
    struct itimerspec oldValue;
    const clockid_t CLOCK_ID = CLOCK_REALTIME;

    // Set the timer current time plus 1 ms
    struct timespec now;
    clock_gettime(CLOCK_ID, &now);
    newValue.it_value.tv_sec = now.tv_sec;
    newValue.it_value.tv_nsec = now.tv_nsec + 1000000;
    if (newValue.it_value.tv_nsec >= 1000000000) {
        newValue.it_value.tv_nsec -= 1000000000;
        newValue.it_value.tv_sec += 1;
    }
    newValue.it_interval.tv_sec = 0;
    newValue.it_interval.tv_nsec = 0;

    struct sigevent sev;
    sev.sigev_notify = SIGEV_THREAD;
    sev.sigev_notify_function = TimeHandler;
    sev.sigev_value.sival_ptr = &g_timerExpired;

    EXPECT_EQ(timer_create(CLOCK_ID, &sev, &timerId), 0);

    EXPECT_EQ(timer_settime(timerId, TIMER_ABSTIME, &newValue, &oldValue), 0);

    usleep(DELAY_TIME);
    EXPECT_TRUE(g_timerExpired);

    int timerRes = timer_delete(timerId);
    EXPECT_EQ(timerRes, 0);
}

/*
 * @tc.number : SUB_KERNEL_SYSCALL_TIMER_CREATE_0600
 * @tc.name   : TimerCreateBoottimeAlarmSuccess_0006
 * @tc.desc   : Test the timer_create with CLOCK_BOOTTIME_ALARM.
 * @tc.size   : MediumTest
 * @tc.type   : Function
 * @tc.level  : Level 1
 */
HWTEST_F(TimerCreateApiTest, TimerCreateBoottimeAlarmSuccess_0006, Function | MediumTest | Level1)
{
    timer_t timerId;
    const clockid_t CLOCK_ID = CLOCK_BOOTTIME_ALARM;
    sighandler_t oldHandler = signal(SIGALRM, SigalrmHandler);
    EXPECT_NE(oldHandler, SIG_ERR);
    struct sigevent sev = {
        .sigev_notify = SIGEV_SIGNAL,
        .sigev_signo = SIGALRM,
    };
    struct itimerspec its = {
        .it_interval = {
            .tv_sec = 0,
            .tv_nsec = 0,
        },
        .it_value = {
            .tv_sec = 0,
            .tv_nsec = 50,
        },
    };

    EXPECT_EQ(timer_create(CLOCK_ID, &sev, &timerId), 0);
    EXPECT_EQ(timer_settime(timerId, 0, &its, nullptr), 0);

    usleep(DELAY_TIME);

    EXPECT_TRUE(g_sigalRm);

    int timerRes = timer_delete(timerId);
    EXPECT_EQ(timerRes, 0);

    signal(SIGALRM, oldHandler);
}