/*
 * Copyright (c) 2024 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 <errno.h>
#include <signal.h>
#include <string.h>
#include <sys/wait.h>
#include <sys/select.h>
#include <sigchain.h>
#include "fortify_test.h"
#include "functionalext.h"
#include "test.h"

#define STABILITY_LOOP_TIMES 10

/**
 * @tc.name     : fd_set_0010
 * @tc.desc     : test FD_SET with normal fd
 * @tc.level    : Level 1
 */
static void fd_set_0010(void)
{
    fd_set readfds;
    FD_ZERO(&readfds);
    int sockfd = 0;
    FD_SET(sockfd, &readfds);
    EXPECT_EQ(fd_set_0010, (&readfds)->fds_bits[0], 1);

    return;
}

/**
 * @tc.name     : fd_set_0020
 * @tc.desc     : test FD_SET with fd < 0
 * @tc.level    : Level 2
 */
static void fd_set_0020(void)
{
    struct sigaction sigabrt = {
        .sa_handler = SignalHandler,
    };
    sigaction(SIGABRT, &sigabrt, NULL);

    int status;
    int pid = fork();
    fd_set readfds;
    int sockfd = -1;
    switch (pid) {
        case -1:
            t_error("fork failed: %s\n", strerror(errno));
            break;
        case 0:
            FD_ZERO(&readfds);
            FD_SET(sockfd, &readfds);
            exit(0);
        default:
            waitpid(pid, &status, WUNTRACED);
            TEST(WIFEXITED(status) == 0);
            TEST(WIFSTOPPED(status) == 1);
            TEST(WSTOPSIG(status) == SIGSTOP);
            kill(pid, SIGCONT);
            break;
    }

    return;
}

/**
 * @tc.name     : fd_set_0030
 * @tc.desc     : test FD_SET with fd >= 1024
 * @tc.level    : Level 2
 */
static void fd_set_0030(void)
{
    struct sigaction sigabrt = {
        .sa_handler = SignalHandler,
    };
    sigaction(SIGABRT, &sigabrt, NULL);

    int status;
    int pid = fork();
    fd_set readfds;
    int sockfd = 1024;
    switch (pid) {
        case -1:
            t_error("fork failed: %s\n", strerror(errno));
            break;
        case 0:
            FD_ZERO(&readfds);
            FD_SET(sockfd, &readfds);
            exit(0);
        default:
            waitpid(pid, &status, WUNTRACED);
            TEST(WIFEXITED(status) == 0);
            TEST(WIFSTOPPED(status) == 1);
            TEST(WSTOPSIG(status) == SIGSTOP);
            kill(pid, SIGCONT);
            break;
    }

    return;
}

/**
 * @tc.name     : fd_clr_0010
 * @tc.desc     : test FD_CLR with normal fd
 * @tc.level    : Level 1
 */
static void fd_clr_0010(void)
{
    fd_set readfds;
    FD_ZERO(&readfds);
    int sockfd = 0;
    FD_SET(sockfd, &readfds);
    FD_CLR(sockfd, &readfds);
    EXPECT_EQ(fd_set_0010, (&readfds)->fds_bits[0], 0);
    
    return;
}

/**
 * @tc.name     : fd_clr_0020
 * @tc.desc     : test FD_CLR with fd < 0
 * @tc.level    : Level 2
 */
static void fd_clr_0020(void)
{
    struct sigaction sigabrt = {
        .sa_handler = SignalHandler,
    };
    sigaction(SIGABRT, &sigabrt, NULL);

    int status;
    int pid = fork();
    fd_set readfds;
    int sockfd = -1;
    switch (pid) {
        case -1:
            t_error("fork failed: %s\n", strerror(errno));
            break;
        case 0:
            FD_ZERO(&readfds);
            FD_CLR(sockfd, &readfds);
            exit(0);
        default:
            waitpid(pid, &status, WUNTRACED);
            TEST(WIFEXITED(status) == 0);
            TEST(WIFSTOPPED(status) == 1);
            TEST(WSTOPSIG(status) == SIGSTOP);
            kill(pid, SIGCONT);
            break;
    }

    return;
}

/**
 * @tc.name     : fd_clr_0030
 * @tc.desc     : test FD_CLR with fd >= 1024
 * @tc.level    : Level 2
 */
static void fd_clr_0030(void)
{
    struct sigaction sigabrt = {
        .sa_handler = SignalHandler,
    };
    sigaction(SIGABRT, &sigabrt, NULL);

    int status;
    int pid = fork();
    fd_set readfds;
    int sockfd = 1024;
    switch (pid) {
        case -1:
            t_error("fork failed: %s\n", strerror(errno));
            break;
        case 0:
            FD_ZERO(&readfds);
            FD_CLR(sockfd, &readfds);
            exit(0);
        default:
            waitpid(pid, &status, WUNTRACED);
            TEST(WIFEXITED(status) == 0);
            TEST(WIFSTOPPED(status) == 1);
            TEST(WSTOPSIG(status) == SIGSTOP);
            kill(pid, SIGCONT);
            break;
    }

    return;
}

/**
 * @tc.name     : fd_isset_0010
 * @tc.desc     : test FD_ISSET with normal fd
 * @tc.level    : Level 1
 */
static void fd_isset_0010(void)
{
    fd_set readfds;
    FD_ZERO(&readfds);
    int sockfd = 0;
    FD_SET(sockfd, &readfds);
    int res = FD_ISSET(sockfd, &readfds);
    EXPECT_EQ(fd_set_0010, res, 1);
    
    return;
}

/**
 * @tc.name     : fd_isset_0020
 * @tc.desc     : test FD_ISSET with fd < 0
 * @tc.level    : Level 2
 */
static void fd_isset_0020(void)
{
    fd_set readfds;
    FD_ZERO(&readfds);
    int sockfd = -1;
    int res = FD_ISSET(sockfd, &readfds);
    EXPECT_EQ(fd_set_0010, res, 0);
    
    return;
}

/**
 * @tc.name     : fd_isset_0030
 * @tc.desc     : test FD_ISSET with fd >= 1024
 * @tc.level    : Level 2
 */
static void fd_isset_0030(void)
{
    fd_set readfds;
    FD_ZERO(&readfds);
    int sockfd = 1024;
    int res = FD_ISSET(sockfd, &readfds);
    EXPECT_EQ(fd_set_0010, res, 0);
    
    return;
}

#ifndef MUSL_EXTERNAL_FUNCTION
/**
 * @tc.name     : __fdelt_chk_0010
 * @tc.desc     : test __fdelt_chk stub return value and errno
 * @tc.level    : Level 1
 */
static void __fdelt_chk_0010(void)
{
    errno = 0;
    long int ret = __fdelt_chk(0);
    EXPECT_LONGEQ(__fdelt_chk_0010, ret, -1);
    EXPECT_EQ(__fdelt_chk_0010, errno, ENOSYS);
}
#else
/**
 * @tc.name     : __fdelt_chk_0020
 * @tc.desc     : test __fdelt_chk with normal fd
 * @tc.level    : Level 1
 */
static void __fdelt_chk_0020(void)
{
    EXPECT_EQ(__fdelt_chk_0020, __fdelt_chk(0), 0);
    EXPECT_EQ(__fdelt_chk_0020, __fdelt_chk(NFDBITS - 1), 0);
    EXPECT_EQ(__fdelt_chk_0020, __fdelt_chk(NFDBITS), 1);
    EXPECT_EQ(__fdelt_chk_0020, __fdelt_chk(FD_SETSIZE - 1), (FD_SETSIZE - 1) / NFDBITS);
}

/**
 * @tc.name     : __fdelt_chk_0030
 * @tc.desc     : test __fdelt_chk with fd < 0
 * @tc.level    : Level 2
 */
static void __fdelt_chk_0030(void)
{
    struct sigaction sigabrt = {
        .sa_handler = SignalHandler,
    };
    sigaction(SIGABRT, &sigabrt, NULL);

    int status;
    int pid = fork();
    switch (pid) {
        case -1:
            t_error("fork failed: %s\n", strerror(errno));
            break;
        case 0:
            __fdelt_chk(-1);
            exit(0);
        default:
            waitpid(pid, &status, WUNTRACED);
            TEST(WIFEXITED(status) == 0);
            TEST(WIFSTOPPED(status) == 1);
            TEST(WSTOPSIG(status) == SIGSTOP);
            kill(pid, SIGCONT);
            break;
    }
}

/**
 * @tc.name     : __fdelt_chk_0040
 * @tc.desc     : test __fdelt_chk with fd >= FD_SETSIZE
 * @tc.level    : Level 2
 */
static void __fdelt_chk_0040(void)
{
    struct sigaction sigabrt = {
        .sa_handler = SignalHandler,
    };
    sigaction(SIGABRT, &sigabrt, NULL);

    int status;
    int pid = fork();
    switch (pid) {
        case -1:
            t_error("fork failed: %s\n", strerror(errno));
            break;
        case 0:
            __fdelt_chk(FD_SETSIZE);
            exit(0);
        default:
            waitpid(pid, &status, WUNTRACED);
            TEST(WIFEXITED(status) == 0);
            TEST(WIFSTOPPED(status) == 1);
            TEST(WSTOPSIG(status) == SIGSTOP);
            kill(pid, SIGCONT);
            break;
    }
}

/**
 * @tc.name     : __fdelt_chk_0050
 * @tc.desc     : test __fdelt_chk stability with repeated boundary checks
 * @tc.level    : Level 1
 */
static void __fdelt_chk_0050(void)
{
    for (int round = 0; round < STABILITY_LOOP_TIMES; round++) {
        for (int fd = 0; fd < FD_SETSIZE; fd++) {
            EXPECT_EQ(__fdelt_chk_0050, __fdelt_chk(fd), fd / NFDBITS);
        }
    }
}
#endif

int main(int argc, char *argv[])
{
    remove_all_special_handler(SIGABRT);
    fd_set_0010();
    fd_set_0020();
    fd_set_0030();
    fd_clr_0010();
    fd_clr_0020();
    fd_clr_0030();
    fd_isset_0010();
    fd_isset_0020();
    fd_isset_0030();
#ifndef MUSL_EXTERNAL_FUNCTION
    __fdelt_chk_0010();
#else
    __fdelt_chk_0020();
    __fdelt_chk_0030();
    __fdelt_chk_0040();
    __fdelt_chk_0050();
#endif
    return t_status;
}