#include "src/unistd/getopt.h"
#include "test/UnitTest/Test.h"
#include "src/__support/CPP/array.h"
#include "src/stdio/fflush.h"
#include "src/stdio/fopencookie.h"
#include <stdio.h>
using LIBC_NAMESPACE::cpp::array;
namespace test_globals {
char *optarg;
int optind = 1;
int optopt;
int opterr = 1;
unsigned optpos;
}
void set_state(FILE *errstream) {
LIBC_NAMESPACE::impl::set_getopt_state(
&test_globals::optarg, &test_globals::optind, &test_globals::optopt,
&test_globals::optpos, &test_globals::opterr, errstream);
}
static void my_memcpy(char *dest, const char *src, size_t size) {
for (size_t i = 0; i < size; i++)
dest[i] = src[i];
}
ssize_t cookie_write(void *cookie, const char *buf, size_t size) {
char **pos = static_cast<char **>(cookie);
my_memcpy(*pos, buf, size);
*pos += size;
return size;
}
static cookie_io_functions_t cookie{nullptr, &cookie_write, nullptr, nullptr};
FILE *memopen(char **pos) {
return LIBC_NAMESPACE::fopencookie(pos, "w", cookie);
}
struct LlvmLibcGetoptTest : public LIBC_NAMESPACE::testing::Test {
FILE *errstream;
char buf[256];
char *pos = buf;
void reset_errstream() { pos = buf; }
const char *get_error_msg() {
LIBC_NAMESPACE::fflush(errstream);
return buf;
}
void SetUp() override {
ASSERT_TRUE(!!(errstream = memopen(&pos)));
set_state(errstream);
ASSERT_EQ(test_globals::optind, 1);
}
void TearDown() override {
test_globals::optind = 1;
test_globals::opterr = 1;
}
};
char *operator"" _c(const char *c, size_t) { return const_cast<char *>(c); }
TEST_F(LlvmLibcGetoptTest, NoMatch) {
array<char *, 3> argv{"prog"_c, "arg1"_c, nullptr};
EXPECT_EQ(LIBC_NAMESPACE::getopt(1, argv.data(), "..."), -1);
test_globals::optind = 2;
EXPECT_EQ(LIBC_NAMESPACE::getopt(100, argv.data(), "..."), -1);
test_globals::optind = 1;
EXPECT_EQ(LIBC_NAMESPACE::getopt(2, argv.data(), "a"), -1);
ASSERT_EQ(test_globals::optind, 1);
argv[1] = "-"_c;
EXPECT_EQ(LIBC_NAMESPACE::getopt(2, argv.data(), "a"), -1);
ASSERT_EQ(test_globals::optind, 1);
argv[1] = "--"_c;
EXPECT_EQ(LIBC_NAMESPACE::getopt(2, argv.data(), "a"), -1);
EXPECT_EQ(test_globals::optind, 2);
}
TEST_F(LlvmLibcGetoptTest, WrongMatch) {
array<char *, 3> argv{"prog"_c, "-b"_c, nullptr};
EXPECT_EQ(LIBC_NAMESPACE::getopt(2, argv.data(), "a"), int('?'));
EXPECT_EQ(test_globals::optopt, (int)'b');
EXPECT_EQ(test_globals::optind, 1);
EXPECT_STREQ(get_error_msg(), "prog: illegal option -- b\n");
}
TEST_F(LlvmLibcGetoptTest, OpterrFalse) {
array<char *, 3> argv{"prog"_c, "-b"_c, nullptr};
test_globals::opterr = 0;
set_state(errstream);
EXPECT_EQ(LIBC_NAMESPACE::getopt(2, argv.data(), "a"), int('?'));
EXPECT_EQ(test_globals::optopt, (int)'b');
EXPECT_EQ(test_globals::optind, 1);
EXPECT_STREQ(get_error_msg(), "");
}
TEST_F(LlvmLibcGetoptTest, MissingArg) {
array<char *, 3> argv{"prog"_c, "-b"_c, nullptr};
EXPECT_EQ(LIBC_NAMESPACE::getopt(2, argv.data(), ":b:"), (int)':');
ASSERT_EQ(test_globals::optind, 1);
EXPECT_STREQ(get_error_msg(), "prog: option requires an argument -- b\n");
reset_errstream();
EXPECT_EQ(LIBC_NAMESPACE::getopt(2, argv.data(), "b:"), int('?'));
EXPECT_EQ(test_globals::optind, 1);
EXPECT_STREQ(get_error_msg(), "prog: option requires an argument -- b\n");
}
TEST_F(LlvmLibcGetoptTest, ParseArgInCurrent) {
array<char *, 3> argv{"prog"_c, "-barg"_c, nullptr};
EXPECT_EQ(LIBC_NAMESPACE::getopt(2, argv.data(), "b:"), (int)'b');
EXPECT_STREQ(test_globals::optarg, "arg");
EXPECT_EQ(test_globals::optind, 2);
}
TEST_F(LlvmLibcGetoptTest, ParseArgInNext) {
array<char *, 4> argv{"prog"_c, "-b"_c, "arg"_c, nullptr};
EXPECT_EQ(LIBC_NAMESPACE::getopt(3, argv.data(), "b:"), (int)'b');
EXPECT_STREQ(test_globals::optarg, "arg");
EXPECT_EQ(test_globals::optind, 3);
}
TEST_F(LlvmLibcGetoptTest, ParseMutliInOne) {
array<char *, 3> argv{"prog"_c, "-abc"_c, nullptr};
EXPECT_EQ(LIBC_NAMESPACE::getopt(2, argv.data(), "abc"), (int)'a');
ASSERT_EQ(test_globals::optind, 1);
EXPECT_EQ(LIBC_NAMESPACE::getopt(2, argv.data(), "abc"), (int)'b');
ASSERT_EQ(test_globals::optind, 1);
EXPECT_EQ(LIBC_NAMESPACE::getopt(2, argv.data(), "abc"), (int)'c');
EXPECT_EQ(test_globals::optind, 2);
}