1 //===-- Unittests for getopt ----------------------------------------------===//
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-exception
7 //===----------------------------------------------------------------------===//
9 #include "src/unistd/getopt.h"
10 #include "test/UnitTest/Test.h"
12 #include "src/__support/CPP/array.h"
13 #include "src/stdio/fflush.h"
14 #include "src/stdio/fopencookie.h"
18 using LIBC_NAMESPACE::cpp::array
;
20 namespace test_globals
{
27 } // namespace test_globals
29 // This can't be a constructor because it will get run before the constructor
30 // which sets the default state in getopt.
31 void set_state(FILE *errstream
) {
32 LIBC_NAMESPACE::impl::set_getopt_state(
33 &test_globals::optarg
, &test_globals::optind
, &test_globals::optopt
,
34 &test_globals::optpos
, &test_globals::opterr
, errstream
);
37 static void my_memcpy(char *dest
, const char *src
, size_t size
) {
38 for (size_t i
= 0; i
< size
; i
++)
42 ssize_t
cookie_write(void *cookie
, const char *buf
, size_t size
) {
43 char **pos
= static_cast<char **>(cookie
);
44 my_memcpy(*pos
, buf
, size
);
49 static cookie_io_functions_t cookie
{nullptr, &cookie_write
, nullptr, nullptr};
51 // TODO: <stdio> could be either llvm-libc's or the system libc's. The former
52 // doesn't currently support fmemopen but does have fopencookie. In the future
53 // just use that instead. This memopen does no error checking for the size
54 // of the buffer, etc.
55 FILE *memopen(char **pos
) {
56 return LIBC_NAMESPACE::fopencookie(pos
, "w", cookie
);
59 struct LlvmLibcGetoptTest
: public LIBC_NAMESPACE::testing::Test
{
64 void reset_errstream() { pos
= buf
; }
65 const char *get_error_msg() {
66 LIBC_NAMESPACE::fflush(errstream
);
70 void SetUp() override
{
71 ASSERT_TRUE(!!(errstream
= memopen(&pos
)));
73 ASSERT_EQ(test_globals::optind
, 1);
76 void TearDown() override
{
77 test_globals::optind
= 1;
78 test_globals::opterr
= 1;
82 // This is safe because getopt doesn't currently permute argv like GNU's getopt
83 // does so this just helps silence warnings.
84 char *operator"" _c(const char *c
, size_t) { return const_cast<char *>(c
); }
86 TEST_F(LlvmLibcGetoptTest
, NoMatch
) {
87 array
<char *, 3> argv
{"prog"_c
, "arg1"_c
, nullptr};
90 EXPECT_EQ(LIBC_NAMESPACE::getopt(1, argv
.data(), "..."), -1);
92 // argv[optind] == nullptr
93 test_globals::optind
= 2;
94 EXPECT_EQ(LIBC_NAMESPACE::getopt(100, argv
.data(), "..."), -1);
96 // argv[optind][0] != '-'
97 test_globals::optind
= 1;
98 EXPECT_EQ(LIBC_NAMESPACE::getopt(2, argv
.data(), "a"), -1);
99 ASSERT_EQ(test_globals::optind
, 1);
101 // argv[optind] == "-"
103 EXPECT_EQ(LIBC_NAMESPACE::getopt(2, argv
.data(), "a"), -1);
104 ASSERT_EQ(test_globals::optind
, 1);
106 // argv[optind] == "--", then return -1 and incremement optind
108 EXPECT_EQ(LIBC_NAMESPACE::getopt(2, argv
.data(), "a"), -1);
109 EXPECT_EQ(test_globals::optind
, 2);
112 TEST_F(LlvmLibcGetoptTest
, WrongMatch
) {
113 array
<char *, 3> argv
{"prog"_c
, "-b"_c
, nullptr};
115 EXPECT_EQ(LIBC_NAMESPACE::getopt(2, argv
.data(), "a"), int('?'));
116 EXPECT_EQ(test_globals::optopt
, (int)'b');
117 EXPECT_EQ(test_globals::optind
, 1);
118 EXPECT_STREQ(get_error_msg(), "prog: illegal option -- b\n");
121 TEST_F(LlvmLibcGetoptTest
, OpterrFalse
) {
122 array
<char *, 3> argv
{"prog"_c
, "-b"_c
, nullptr};
124 test_globals::opterr
= 0;
125 set_state(errstream
);
126 EXPECT_EQ(LIBC_NAMESPACE::getopt(2, argv
.data(), "a"), int('?'));
127 EXPECT_EQ(test_globals::optopt
, (int)'b');
128 EXPECT_EQ(test_globals::optind
, 1);
129 EXPECT_STREQ(get_error_msg(), "");
132 TEST_F(LlvmLibcGetoptTest
, MissingArg
) {
133 array
<char *, 3> argv
{"prog"_c
, "-b"_c
, nullptr};
135 EXPECT_EQ(LIBC_NAMESPACE::getopt(2, argv
.data(), ":b:"), (int)':');
136 ASSERT_EQ(test_globals::optind
, 1);
137 EXPECT_STREQ(get_error_msg(), "prog: option requires an argument -- b\n");
139 EXPECT_EQ(LIBC_NAMESPACE::getopt(2, argv
.data(), "b:"), int('?'));
140 EXPECT_EQ(test_globals::optind
, 1);
141 EXPECT_STREQ(get_error_msg(), "prog: option requires an argument -- b\n");
144 TEST_F(LlvmLibcGetoptTest
, ParseArgInCurrent
) {
145 array
<char *, 3> argv
{"prog"_c
, "-barg"_c
, nullptr};
147 EXPECT_EQ(LIBC_NAMESPACE::getopt(2, argv
.data(), "b:"), (int)'b');
148 EXPECT_STREQ(test_globals::optarg
, "arg");
149 EXPECT_EQ(test_globals::optind
, 2);
152 TEST_F(LlvmLibcGetoptTest
, ParseArgInNext
) {
153 array
<char *, 4> argv
{"prog"_c
, "-b"_c
, "arg"_c
, nullptr};
155 EXPECT_EQ(LIBC_NAMESPACE::getopt(3, argv
.data(), "b:"), (int)'b');
156 EXPECT_STREQ(test_globals::optarg
, "arg");
157 EXPECT_EQ(test_globals::optind
, 3);
160 TEST_F(LlvmLibcGetoptTest
, ParseMutliInOne
) {
161 array
<char *, 3> argv
{"prog"_c
, "-abc"_c
, nullptr};
163 EXPECT_EQ(LIBC_NAMESPACE::getopt(2, argv
.data(), "abc"), (int)'a');
164 ASSERT_EQ(test_globals::optind
, 1);
165 EXPECT_EQ(LIBC_NAMESPACE::getopt(2, argv
.data(), "abc"), (int)'b');
166 ASSERT_EQ(test_globals::optind
, 1);
167 EXPECT_EQ(LIBC_NAMESPACE::getopt(2, argv
.data(), "abc"), (int)'c');
168 EXPECT_EQ(test_globals::optind
, 2);