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
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_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.
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 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.
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 getopt
81// 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 >= 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
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

Provided by KDAB

Privacy Policy
Improve your Profiling and Debugging skills
Find out more

source code of libc/test/src/unistd/getopt_test.cpp