xref: /aosp_15_r20/external/llvm-libc/test/src/unistd/getopt_test.cpp (revision 71db0c75aadcf003ffe3238005f61d7618a3fead)
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-exception
6 //
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 
16 using LIBC_NAMESPACE::cpp::array;
17 
18 namespace test_globals {
19 char *optarg;
20 int optind = 1;
21 int optopt;
22 int opterr = 1;
23 
24 unsigned optpos;
25 } // namespace test_globals
26 
27 // This can't be a constructor because it will get run before the constructor
28 // which sets the default state in getopt.
set_state(FILE * errstream)29 void 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 
my_memcpy(char * dest,const char * src,size_t size)35 static 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 
cookie_write(void * cookie,const char * buf,size_t size)40 ssize_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 
47 static 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 former
50 // doesn't currently support fmemopen but does have fopencookie. In the future
51 // just use that instead. This memopen does no error checking for the size
52 // of the buffer, etc.
memopen(char ** pos)53 FILE *memopen(char **pos) {
54   return LIBC_NAMESPACE::fopencookie(pos, "w", cookie);
55 }
56 
57 struct LlvmLibcGetoptTest : public LIBC_NAMESPACE::testing::Test {
58   FILE *errstream;
59   char buf[256];
60   char *pos = buf;
61 
reset_errstreamLlvmLibcGetoptTest62   void reset_errstream() { pos = buf; }
get_error_msgLlvmLibcGetoptTest63   const char *get_error_msg() {
64     LIBC_NAMESPACE::fflush(errstream);
65     return buf;
66   }
67 
SetUpLlvmLibcGetoptTest68   void SetUp() override {
69     ASSERT_TRUE(!!(errstream = memopen(&pos)));
70     set_state(errstream);
71     ASSERT_EQ(test_globals::optind, 1);
72   }
73 
TearDownLlvmLibcGetoptTest74   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 getopt
81 // does so this just helps silence warnings.
operator ""_c(const char * c,size_t)82 char *operator"" _c(const char *c, size_t) { return const_cast<char *>(c); }
83 
TEST_F(LlvmLibcGetoptTest,NoMatch)84 TEST_F(LlvmLibcGetoptTest, NoMatch) {
85   array<char *, 3> argv{"prog"_c, "arg1"_c, nullptr};
86 
87   // optind >= argc
88   EXPECT_EQ(LIBC_NAMESPACE::getopt(1, argv.data(), "..."), -1);
89 
90   // argv[optind] == nullptr
91   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 optind
105   argv[1] = "--"_c;
106   EXPECT_EQ(LIBC_NAMESPACE::getopt(2, argv.data(), "a"), -1);
107   EXPECT_EQ(test_globals::optind, 2);
108 }
109 
TEST_F(LlvmLibcGetoptTest,WrongMatch)110 TEST_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 
TEST_F(LlvmLibcGetoptTest,OpterrFalse)119 TEST_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 
TEST_F(LlvmLibcGetoptTest,MissingArg)130 TEST_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 
TEST_F(LlvmLibcGetoptTest,ParseArgInCurrent)142 TEST_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 
TEST_F(LlvmLibcGetoptTest,ParseArgInNext)150 TEST_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 
TEST_F(LlvmLibcGetoptTest,ParseMutliInOne)158 TEST_F(LlvmLibcGetoptTest, ParseMutliInOne) {
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