ArgParse.cpp
1 /*************************************************************************************************** 2 3 Zyan Core Library (Zycore-C) 4 5 Original Author : Joel Hoener 6 7 * Permission is hereby granted, free of charge, to any person obtaining a copy 8 * of this software and associated documentation files (the "Software"), to deal 9 * in the Software without restriction, including without limitation the rights 10 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 * copies of the Software, and to permit persons to whom the Software is 12 * furnished to do so, subject to the following conditions: 13 * 14 * The above copyright notice and this permission notice shall be included in all 15 * copies or substantial portions of the Software. 16 * 17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 * SOFTWARE. 24 25 ***************************************************************************************************/ 26 27 /** 28 * @file 29 * @brief Tests the the arg parse implementation. 30 */ 31 32 #include <string_view> 33 34 #include <gtest/gtest.h> 35 #include <Zycore/ArgParse.h> 36 #include <Zycore/LibC.h> 37 38 /* ============================================================================================== */ 39 /* Helpers */ 40 /* ============================================================================================== */ 41 42 auto cvt_string_view(const ZyanStringView *sv) 43 { 44 const char* buf; 45 if (ZYAN_FAILED(ZyanStringViewGetData(sv, &buf))) throw std::exception{}; 46 ZyanUSize len; 47 if (ZYAN_FAILED(ZyanStringViewGetSize(sv, &len))) throw std::exception{}; 48 49 return std::string_view{buf, len}; 50 } 51 52 /* ============================================================================================== */ 53 /* Tests */ 54 /* ============================================================================================== */ 55 56 /* ---------------------------------------------------------------------------------------------- */ 57 /* Unnamed args */ 58 /* ---------------------------------------------------------------------------------------------- */ 59 60 static auto UnnamedArgTest(ZyanU64 min, ZyanU64 max) 61 { 62 const char* argv[] 63 { 64 "./test", "a", "xxx" 65 }; 66 67 ZyanArgParseConfig cfg 68 { 69 argv, // argv 70 3, // argc 71 min, // min_unnamed_args 72 max, // max_unnamed_args 73 nullptr // args 74 }; 75 76 ZyanVector parsed; 77 const char* err_tok = nullptr; 78 ZYAN_MEMSET(&parsed, 0, sizeof(parsed)); 79 auto status = ZyanArgParse(&cfg, &parsed, &err_tok); 80 return std::make_tuple(status, parsed, err_tok); 81 } 82 83 TEST(UnnamedArgs, TooFew) 84 { 85 auto [status, parsed, err_tok] = UnnamedArgTest(5, 5); 86 ASSERT_EQ(status, ZYAN_STATUS_TOO_FEW_ARGS); 87 ASSERT_STREQ(err_tok, nullptr); 88 } 89 90 TEST(UnnamedArgs, TooMany) 91 { 92 auto [status, parsed, err_tok] = UnnamedArgTest(1, 1); 93 ASSERT_EQ(status, ZYAN_STATUS_TOO_MANY_ARGS); 94 ASSERT_STREQ(err_tok, "xxx"); 95 } 96 97 TEST(UnnamedArgs, PerfectFit) 98 { 99 auto [status, parsed, err_tok] = UnnamedArgTest(2, 2); 100 ASSERT_TRUE(ZYAN_SUCCESS(status)); 101 102 ZyanUSize size; 103 ASSERT_TRUE(ZYAN_SUCCESS(ZyanVectorGetSize(&parsed, &size))); 104 ASSERT_EQ(size, 2); 105 106 auto arg = (const ZyanArgParseArg*)ZyanVectorGet(&parsed, 0); 107 ASSERT_NE(arg, nullptr); 108 ASSERT_TRUE(arg->has_value); 109 ASSERT_EQ(cvt_string_view(&arg->value), "a"); 110 111 arg = (const ZyanArgParseArg*)ZyanVectorGet(&parsed, 1); 112 ASSERT_NE(arg, nullptr); 113 ASSERT_TRUE(arg->has_value); 114 ASSERT_EQ(cvt_string_view(&arg->value), "xxx"); 115 } 116 117 /* ---------------------------------------------------------------------------------------------- */ 118 /* Dash args */ 119 /* ---------------------------------------------------------------------------------------------- */ 120 121 TEST(DashArg, MixedBoolAndValueArgs) 122 { 123 const char* argv[] 124 { 125 "./test", "-aio42", "-n", "xxx" 126 }; 127 128 ZyanArgParseDefinition args[] 129 { 130 {"-o", ZYAN_FALSE, ZYAN_FALSE}, 131 {"-a", ZYAN_TRUE, ZYAN_FALSE}, 132 {"-n", ZYAN_FALSE, ZYAN_FALSE}, 133 {"-i", ZYAN_TRUE, ZYAN_FALSE}, 134 {nullptr, ZYAN_FALSE, ZYAN_FALSE} 135 }; 136 137 ZyanArgParseConfig cfg 138 { 139 argv, // argv 140 4, // argc 141 0, // min_unnamed_args 142 0, // max_unnamed_args 143 args // args 144 }; 145 146 ZyanVector parsed; 147 ZYAN_MEMSET(&parsed, 0, sizeof(parsed)); 148 auto status = ZyanArgParse(&cfg, &parsed, nullptr); 149 ASSERT_TRUE(ZYAN_SUCCESS(status)); 150 151 ZyanUSize size; 152 ASSERT_TRUE(ZYAN_SUCCESS(ZyanVectorGetSize(&parsed, &size))); 153 ASSERT_EQ(size, 4); 154 155 const ZyanArgParseArg* arg; 156 ASSERT_TRUE(ZYAN_SUCCESS(ZyanVectorGetPointer(&parsed, 0, (const void**)&arg))); 157 ASSERT_STREQ(arg->def->name, "-a"); 158 ASSERT_FALSE(arg->has_value); 159 160 ASSERT_TRUE(ZYAN_SUCCESS(ZyanVectorGetPointer(&parsed, 1, (const void**)&arg))); 161 ASSERT_STREQ(arg->def->name, "-i"); 162 ASSERT_FALSE(arg->has_value); 163 164 ASSERT_TRUE(ZYAN_SUCCESS(ZyanVectorGetPointer(&parsed, 2, (const void**)&arg))); 165 ASSERT_STREQ(arg->def->name, "-o"); 166 ASSERT_TRUE(arg->has_value); 167 ASSERT_EQ(cvt_string_view(&arg->value), "42"); 168 169 ASSERT_TRUE(ZYAN_SUCCESS(ZyanVectorGetPointer(&parsed, 3, (const void**)&arg))); 170 ASSERT_STREQ(arg->def->name, "-n"); 171 ASSERT_TRUE(arg->has_value); 172 ASSERT_EQ(cvt_string_view(&arg->value), "xxx"); 173 } 174 175 /* ---------------------------------------------------------------------------------------------- */ 176 /* Double dash args */ 177 /* ---------------------------------------------------------------------------------------------- */ 178 179 TEST(DoubleDashArg, PerfectFit) 180 { 181 const char* argv[] 182 { 183 "./test", "--help", "--stuff", "1337" 184 }; 185 186 ZyanArgParseDefinition args[] 187 { 188 {"--help", ZYAN_TRUE, ZYAN_FALSE}, 189 {"--stuff", ZYAN_FALSE, ZYAN_FALSE}, 190 {nullptr, ZYAN_FALSE, ZYAN_FALSE} 191 }; 192 193 ZyanArgParseConfig cfg 194 { 195 argv, // argv 196 4, // argc 197 0, // min_unnamed_args 198 0, // max_unnamed_args 199 args // args 200 }; 201 202 ZyanVector parsed; 203 ZYAN_MEMSET(&parsed, 0, sizeof(parsed)); 204 auto status = ZyanArgParse(&cfg, &parsed, nullptr); 205 ASSERT_TRUE(ZYAN_SUCCESS(status)); 206 207 ZyanUSize size; 208 ASSERT_TRUE(ZYAN_SUCCESS(ZyanVectorGetSize(&parsed, &size))); 209 ASSERT_EQ(size, 2); 210 211 const ZyanArgParseArg* arg; 212 ASSERT_TRUE(ZYAN_SUCCESS(ZyanVectorGetPointer(&parsed, 0, (const void**)&arg))); 213 ASSERT_STREQ(arg->def->name, "--help"); 214 ASSERT_FALSE(arg->has_value); 215 216 ASSERT_TRUE(ZYAN_SUCCESS(ZyanVectorGetPointer(&parsed, 1, (const void**)&arg))); 217 ASSERT_STREQ(arg->def->name, "--stuff"); 218 ASSERT_TRUE(arg->has_value); 219 ASSERT_EQ(cvt_string_view(&arg->value), "1337"); 220 } 221 222 /* ---------------------------------------------------------------------------------------------- */ 223 /* Mixed */ 224 /* ---------------------------------------------------------------------------------------------- */ 225 226 TEST(MixedArgs, MissingRequiredArg) 227 { 228 const char* argv[] 229 { 230 "./test", "blah.c", "woof.moo" 231 }; 232 233 ZyanArgParseDefinition args[] 234 { 235 {"--feature-xyz", ZYAN_TRUE, ZYAN_FALSE}, 236 {"-n", ZYAN_FALSE, ZYAN_TRUE}, 237 {nullptr, ZYAN_FALSE, ZYAN_FALSE} 238 }; 239 240 ZyanArgParseConfig cfg 241 { 242 argv, // argv 243 3, // argc 244 0, // min_unnamed_args 245 100, // max_unnamed_args 246 args // args 247 }; 248 249 ZyanVector parsed; 250 ZYAN_MEMSET(&parsed, 0, sizeof(parsed)); 251 const char* err_tok = nullptr; 252 auto status = ZyanArgParse(&cfg, &parsed, &err_tok); 253 ASSERT_EQ(status, ZYAN_STATUS_REQUIRED_ARG_MISSING); 254 ASSERT_STREQ(err_tok, "-n"); 255 } 256 257 TEST(MixedArgs, Stuff) 258 { 259 const char* argv[] 260 { 261 "./test", "--feature-xyz", "-n5", "blah.c", "woof.moo" 262 }; 263 264 ZyanArgParseDefinition args[] 265 { 266 {"--feature-xyz", ZYAN_TRUE, ZYAN_FALSE}, 267 {"-n", ZYAN_FALSE, ZYAN_FALSE}, 268 {nullptr, ZYAN_FALSE, ZYAN_FALSE} 269 }; 270 271 ZyanArgParseConfig cfg 272 { 273 argv, // argv 274 5, // argc 275 0, // min_unnamed_args 276 100, // max_unnamed_args 277 args // args 278 }; 279 280 ZyanVector parsed; 281 ZYAN_MEMSET(&parsed, 0, sizeof(parsed)); 282 auto status = ZyanArgParse(&cfg, &parsed, nullptr); 283 ASSERT_TRUE(ZYAN_SUCCESS(status)); 284 285 ZyanUSize size; 286 ASSERT_TRUE(ZYAN_SUCCESS(ZyanVectorGetSize(&parsed, &size))); 287 ASSERT_EQ(size, 4); 288 289 const ZyanArgParseArg* arg; 290 ASSERT_TRUE(ZYAN_SUCCESS(ZyanVectorGetPointer(&parsed, 0, (const void**)&arg))); 291 ASSERT_STREQ(arg->def->name, "--feature-xyz"); 292 ASSERT_FALSE(arg->has_value); 293 294 ASSERT_TRUE(ZYAN_SUCCESS(ZyanVectorGetPointer(&parsed, 1, (const void**)&arg))); 295 ASSERT_STREQ(arg->def->name, "-n"); 296 ASSERT_TRUE(arg->has_value); 297 ASSERT_EQ(cvt_string_view(&arg->value), "5"); 298 299 ASSERT_TRUE(ZYAN_SUCCESS(ZyanVectorGetPointer(&parsed, 2, (const void**)&arg))); 300 ASSERT_EQ(arg->def, nullptr); 301 ASSERT_TRUE(arg->has_value); 302 ASSERT_EQ(cvt_string_view(&arg->value), "blah.c"); 303 304 ASSERT_TRUE(ZYAN_SUCCESS(ZyanVectorGetPointer(&parsed, 3, (const void**)&arg))); 305 ASSERT_EQ(arg->def, nullptr); 306 ASSERT_TRUE(arg->has_value); 307 ASSERT_EQ(cvt_string_view(&arg->value), "woof.moo"); 308 } 309 310 /* ============================================================================================== */ 311 /* Entry point */ 312 /* ============================================================================================== */ 313 314 int main(int argc, char **argv) 315 { 316 ::testing::InitGoogleTest(&argc, argv); 317 return RUN_ALL_TESTS(); 318 } 319 320 /* ============================================================================================== */