168 lines · cpp
1//===-- Unittests for getopt ----------------------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include "src/unistd/getopt.h"10#include "test/UnitTest/Test.h"11 12#include "src/__support/CPP/array.h"13#include "src/stdio/fflush.h"14#include "src/stdio/fopencookie.h"15 16using LIBC_NAMESPACE::cpp::array;17 18namespace test_globals {19char *optarg;20int optind = 1;21int optopt;22int opterr = 1;23 24unsigned optpos;25} // namespace test_globals26 27// This can't be a constructor because it will get run before the constructor28// which sets the default state in getopt.29void set_state(FILE *errstream) {30 LIBC_NAMESPACE::impl::set_getopt_state(31 &test_globals::optarg, &test_globals::optind, &test_globals::optopt,32 &test_globals::optpos, &test_globals::opterr, errstream);33}34 35static void my_memcpy(char *dest, const char *src, size_t size) {36 for (size_t i = 0; i < size; i++)37 dest[i] = src[i];38}39 40ssize_t cookie_write(void *cookie, const char *buf, size_t size) {41 char **pos = static_cast<char **>(cookie);42 my_memcpy(*pos, buf, size);43 *pos += size;44 return size;45}46 47static cookie_io_functions_t cookie{nullptr, &cookie_write, nullptr, nullptr};48 49// TODO: <stdio> could be either llvm-libc's or the system libc's. The former50// doesn't currently support fmemopen but does have fopencookie. In the future51// just use that instead. This memopen does no error checking for the size52// of the buffer, etc.53FILE *memopen(char **pos) {54 return LIBC_NAMESPACE::fopencookie(pos, "w", cookie);55}56 57struct LlvmLibcGetoptTest : public LIBC_NAMESPACE::testing::Test {58 FILE *errstream;59 char buf[256];60 char *pos = buf;61 62 void reset_errstream() { pos = buf; }63 const char *get_error_msg() {64 LIBC_NAMESPACE::fflush(errstream);65 return buf;66 }67 68 void SetUp() override {69 ASSERT_TRUE(!!(errstream = memopen(&pos)));70 set_state(errstream);71 ASSERT_EQ(test_globals::optind, 1);72 }73 74 void TearDown() override {75 test_globals::optind = 1;76 test_globals::opterr = 1;77 }78};79 80// This is safe because getopt doesn't currently permute argv like GNU's getopt81// does so this just helps silence warnings.82char *operator""_c(const char *c, size_t) { return const_cast<char *>(c); }83 84TEST_F(LlvmLibcGetoptTest, NoMatch) {85 array<char *, 3> argv{"prog"_c, "arg1"_c, nullptr};86 87 // optind >= argc88 EXPECT_EQ(LIBC_NAMESPACE::getopt(1, argv.data(), "..."), -1);89 90 // argv[optind] == nullptr91 test_globals::optind = 2;92 EXPECT_EQ(LIBC_NAMESPACE::getopt(100, argv.data(), "..."), -1);93 94 // argv[optind][0] != '-'95 test_globals::optind = 1;96 EXPECT_EQ(LIBC_NAMESPACE::getopt(2, argv.data(), "a"), -1);97 ASSERT_EQ(test_globals::optind, 1);98 99 // argv[optind] == "-"100 argv[1] = "-"_c;101 EXPECT_EQ(LIBC_NAMESPACE::getopt(2, argv.data(), "a"), -1);102 ASSERT_EQ(test_globals::optind, 1);103 104 // argv[optind] == "--", then return -1 and incremement optind105 argv[1] = "--"_c;106 EXPECT_EQ(LIBC_NAMESPACE::getopt(2, argv.data(), "a"), -1);107 EXPECT_EQ(test_globals::optind, 2);108}109 110TEST_F(LlvmLibcGetoptTest, WrongMatch) {111 array<char *, 3> argv{"prog"_c, "-b"_c, nullptr};112 113 EXPECT_EQ(LIBC_NAMESPACE::getopt(2, argv.data(), "a"), int('?'));114 EXPECT_EQ(test_globals::optopt, (int)'b');115 EXPECT_EQ(test_globals::optind, 1);116 EXPECT_STREQ(get_error_msg(), "prog: illegal option -- b\n");117}118 119TEST_F(LlvmLibcGetoptTest, OpterrFalse) {120 array<char *, 3> argv{"prog"_c, "-b"_c, nullptr};121 122 test_globals::opterr = 0;123 set_state(errstream);124 EXPECT_EQ(LIBC_NAMESPACE::getopt(2, argv.data(), "a"), int('?'));125 EXPECT_EQ(test_globals::optopt, (int)'b');126 EXPECT_EQ(test_globals::optind, 1);127 EXPECT_STREQ(get_error_msg(), "");128}129 130TEST_F(LlvmLibcGetoptTest, MissingArg) {131 array<char *, 3> argv{"prog"_c, "-b"_c, nullptr};132 133 EXPECT_EQ(LIBC_NAMESPACE::getopt(2, argv.data(), ":b:"), (int)':');134 ASSERT_EQ(test_globals::optind, 1);135 EXPECT_STREQ(get_error_msg(), "prog: option requires an argument -- b\n");136 reset_errstream();137 EXPECT_EQ(LIBC_NAMESPACE::getopt(2, argv.data(), "b:"), int('?'));138 EXPECT_EQ(test_globals::optind, 1);139 EXPECT_STREQ(get_error_msg(), "prog: option requires an argument -- b\n");140}141 142TEST_F(LlvmLibcGetoptTest, ParseArgInCurrent) {143 array<char *, 3> argv{"prog"_c, "-barg"_c, nullptr};144 145 EXPECT_EQ(LIBC_NAMESPACE::getopt(2, argv.data(), "b:"), (int)'b');146 EXPECT_STREQ(test_globals::optarg, "arg");147 EXPECT_EQ(test_globals::optind, 2);148}149 150TEST_F(LlvmLibcGetoptTest, ParseArgInNext) {151 array<char *, 4> argv{"prog"_c, "-b"_c, "arg"_c, nullptr};152 153 EXPECT_EQ(LIBC_NAMESPACE::getopt(3, argv.data(), "b:"), (int)'b');154 EXPECT_STREQ(test_globals::optarg, "arg");155 EXPECT_EQ(test_globals::optind, 3);156}157 158TEST_F(LlvmLibcGetoptTest, ParseMultiInOne) {159 array<char *, 3> argv{"prog"_c, "-abc"_c, nullptr};160 161 EXPECT_EQ(LIBC_NAMESPACE::getopt(2, argv.data(), "abc"), (int)'a');162 ASSERT_EQ(test_globals::optind, 1);163 EXPECT_EQ(LIBC_NAMESPACE::getopt(2, argv.data(), "abc"), (int)'b');164 ASSERT_EQ(test_globals::optind, 1);165 EXPECT_EQ(LIBC_NAMESPACE::getopt(2, argv.data(), "abc"), (int)'c');166 EXPECT_EQ(test_globals::optind, 2);167}168