• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 #include <stdio.h>
17 
18 using LIBC_NAMESPACE::cpp::array;
19 
20 namespace test_globals {
21 char *optarg;
22 int optind = 1;
23 int optopt;
24 int opterr = 1;
25 
26 unsigned optpos;
27 } // namespace test_globals
28 
29 // This can't be a constructor because it will get run before the constructor
30 // which sets the default state in getopt.
set_state(FILE * errstream)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);
35 }
36 
my_memcpy(char * dest,const char * src,size_t size)37 static void my_memcpy(char *dest, const char *src, size_t size) {
38   for (size_t i = 0; i < size; i++)
39     dest[i] = src[i];
40 }
41 
cookie_write(void * cookie,const char * buf,size_t size)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);
45   *pos += size;
46   return size;
47 }
48 
49 static cookie_io_functions_t cookie{nullptr, &cookie_write, nullptr, nullptr};
50 
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.
memopen(char ** pos)55 FILE *memopen(char **pos) {
56   return LIBC_NAMESPACE::fopencookie(pos, "w", cookie);
57 }
58 
59 struct LlvmLibcGetoptTest : public LIBC_NAMESPACE::testing::Test {
60   FILE *errstream;
61   char buf[256];
62   char *pos = buf;
63 
reset_errstreamLlvmLibcGetoptTest64   void reset_errstream() { pos = buf; }
get_error_msgLlvmLibcGetoptTest65   const char *get_error_msg() {
66     LIBC_NAMESPACE::fflush(errstream);
67     return buf;
68   }
69 
SetUpLlvmLibcGetoptTest70   void SetUp() override {
71     ASSERT_TRUE(!!(errstream = memopen(&pos)));
72     set_state(errstream);
73     ASSERT_EQ(test_globals::optind, 1);
74   }
75 
TearDownLlvmLibcGetoptTest76   void TearDown() override {
77     test_globals::optind = 1;
78     test_globals::opterr = 1;
79   }
80 };
81 
82 // This is safe because getopt doesn't currently permute argv like GNU's getopt
83 // does so this just helps silence warnings.
operator ""_c(const char * c,size_t)84 char *operator"" _c(const char *c, size_t) { return const_cast<char *>(c); }
85 
TEST_F(LlvmLibcGetoptTest,NoMatch)86 TEST_F(LlvmLibcGetoptTest, NoMatch) {
87   array<char *, 3> argv{"prog"_c, "arg1"_c, nullptr};
88 
89   // optind >= argc
90   EXPECT_EQ(LIBC_NAMESPACE::getopt(1, argv.data(), "..."), -1);
91 
92   // argv[optind] == nullptr
93   test_globals::optind = 2;
94   EXPECT_EQ(LIBC_NAMESPACE::getopt(100, argv.data(), "..."), -1);
95 
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);
100 
101   // argv[optind] == "-"
102   argv[1] = "-"_c;
103   EXPECT_EQ(LIBC_NAMESPACE::getopt(2, argv.data(), "a"), -1);
104   ASSERT_EQ(test_globals::optind, 1);
105 
106   // argv[optind] == "--", then return -1 and incremement optind
107   argv[1] = "--"_c;
108   EXPECT_EQ(LIBC_NAMESPACE::getopt(2, argv.data(), "a"), -1);
109   EXPECT_EQ(test_globals::optind, 2);
110 }
111 
TEST_F(LlvmLibcGetoptTest,WrongMatch)112 TEST_F(LlvmLibcGetoptTest, WrongMatch) {
113   array<char *, 3> argv{"prog"_c, "-b"_c, nullptr};
114 
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");
119 }
120 
TEST_F(LlvmLibcGetoptTest,OpterrFalse)121 TEST_F(LlvmLibcGetoptTest, OpterrFalse) {
122   array<char *, 3> argv{"prog"_c, "-b"_c, nullptr};
123 
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(), "");
130 }
131 
TEST_F(LlvmLibcGetoptTest,MissingArg)132 TEST_F(LlvmLibcGetoptTest, MissingArg) {
133   array<char *, 3> argv{"prog"_c, "-b"_c, nullptr};
134 
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");
138   reset_errstream();
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");
142 }
143 
TEST_F(LlvmLibcGetoptTest,ParseArgInCurrent)144 TEST_F(LlvmLibcGetoptTest, ParseArgInCurrent) {
145   array<char *, 3> argv{"prog"_c, "-barg"_c, nullptr};
146 
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);
150 }
151 
TEST_F(LlvmLibcGetoptTest,ParseArgInNext)152 TEST_F(LlvmLibcGetoptTest, ParseArgInNext) {
153   array<char *, 4> argv{"prog"_c, "-b"_c, "arg"_c, nullptr};
154 
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);
158 }
159 
TEST_F(LlvmLibcGetoptTest,ParseMutliInOne)160 TEST_F(LlvmLibcGetoptTest, ParseMutliInOne) {
161   array<char *, 3> argv{"prog"_c, "-abc"_c, nullptr};
162 
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);
169 }
170