/ externals / zycore / tests / ArgParse.cpp
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  /* ============================================================================================== */