TclArgParser.cc 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155
  1. #include "catch.hpp"
  2. #include "TclArgParser.hh"
  3. #include "Interpreter.hh"
  4. #include "span.hh"
  5. #include <optional>
  6. using namespace openmsx;
  7. TEST_CASE("TclArgParser")
  8. {
  9. Interpreter interp;
  10. // variables (possibly) filled in by parser
  11. int int1 = -1;
  12. std::optional<int> int2;
  13. double dbl1 = -1.0;
  14. std::optional<double> dbl2;
  15. std::string s1;
  16. std::optional<std::string> s2;
  17. std::vector<int> ints;
  18. bool flag = false;
  19. // description of the parser
  20. const ArgsInfo table[] = {
  21. valueArg("-int1", int1),
  22. valueArg("-int2", int2),
  23. valueArg("-double1", dbl1),
  24. valueArg("-double2", dbl2),
  25. valueArg("-string1", s1),
  26. valueArg("-string2", s2),
  27. valueArg("-ints", ints),
  28. flagArg("-flag", flag),
  29. };
  30. SECTION("empty") {
  31. span<const TclObject, 0> in;
  32. auto out = parseTclArgs(interp, in, table);
  33. CHECK(out.empty()); // no args
  34. CHECK(int1 == -1); // other stuff unchanged
  35. CHECK(!int2);
  36. CHECK(!flag);
  37. }
  38. SECTION("only normal args") {
  39. TclObject in[] = { TclObject("arg1"), TclObject(2), TclObject(3) };
  40. auto out = parseTclArgs(interp, in, table);
  41. CHECK(out.size() == 3);
  42. CHECK(out[0] == "arg1");
  43. CHECK(out[1] == "2");
  44. CHECK(out[2] == "3");
  45. CHECK(int1 == -1); // other stuff unchanged
  46. CHECK(!int2);
  47. }
  48. SECTION("(regular) integer option") {
  49. TclObject in[] = { TclObject("-int1"), TclObject(123) };
  50. auto out = parseTclArgs(interp, in, table);
  51. CHECK(out.empty()); // no regular args
  52. CHECK(int1 == 123); // this has a new value
  53. CHECK(!int2); // other stuff unchanged
  54. }
  55. SECTION("(optional) integer option") {
  56. TclObject in[] = { TclObject("-int2"), TclObject(456) };
  57. auto out = parseTclArgs(interp, in, table);
  58. CHECK(out.empty()); // no regular args
  59. CHECK(int1 == -1); // this is unchanged (or was it explicitly set to -1 ;-)
  60. CHECK(int2); // with an optional<int> we can check that it was set or not
  61. CHECK(*int2 == 456);
  62. }
  63. SECTION("(regular) double option") {
  64. TclObject in[] = { TclObject("-double1"), TclObject(2.72) };
  65. auto out = parseTclArgs(interp, in, table);
  66. CHECK(out.empty()); // no regular args
  67. CHECK(dbl1 == 2.72); // this has a new value
  68. }
  69. SECTION("(regular) string option") {
  70. TclObject in[] = { TclObject("-string1"), TclObject("foobar") };
  71. auto out = parseTclArgs(interp, in, table);
  72. CHECK(out.empty()); // no regular args
  73. CHECK(s1 == "foobar"); // this has a new value
  74. }
  75. SECTION("flag value") {
  76. TclObject in[] = { TclObject("-flag") };
  77. auto out = parseTclArgs(interp, in, table);
  78. CHECK(out.empty()); // no regular args
  79. CHECK(flag); // flag was set
  80. }
  81. SECTION("multiple options and args") {
  82. TclObject in[] = { TclObject("bla"), TclObject("-int2"), TclObject(789), TclObject("qwerty"),
  83. TclObject("-double1"), TclObject("6.28"), TclObject("-string2"), TclObject("bar"),
  84. TclObject("zyxwv"), TclObject("-flag"), TclObject("-int1"), TclObject("-30"),
  85. };
  86. auto out = parseTclArgs(interp, in, table);
  87. CHECK(out.size() == 3);
  88. CHECK(out[0] == "bla");
  89. CHECK(out[1] == "qwerty");
  90. CHECK(out[2] == "zyxwv");
  91. CHECK(int1 == -30);
  92. CHECK(int2); CHECK(*int2 == 789);
  93. CHECK(dbl1 == 6.28);
  94. CHECK(!dbl2);
  95. CHECK(s1 == "");
  96. CHECK(s2); CHECK(*s2 == "bar");
  97. CHECK(flag);
  98. }
  99. SECTION("set same option twice") {
  100. TclObject in[] = { TclObject("-int1"), TclObject(123), TclObject("bla"), TclObject("-int1"), TclObject(234) };
  101. auto out = parseTclArgs(interp, in, table);
  102. CHECK(out.size() == 1);
  103. CHECK(int1 == 234); // take the value of the last option
  104. }
  105. SECTION("vector<T> accepts repeated options") {
  106. TclObject in[] = { TclObject("-ints"), TclObject(11), TclObject("-ints"), TclObject(22) };
  107. auto out = parseTclArgs(interp, in, table);
  108. CHECK(out.empty());
  109. CHECK(ints.size() == 2);
  110. CHECK(ints[0] == 11);
  111. CHECK(ints[1] == 22);
  112. }
  113. SECTION("no options after --") {
  114. TclObject in[] = { TclObject("-int1"), TclObject(123), TclObject("--"), TclObject("-int1"), TclObject(234) };
  115. auto out = parseTclArgs(interp, in, table);
  116. CHECK(out.size() == 2);
  117. CHECK(out[0] == "-int1");
  118. CHECK(out[1] == "234");
  119. CHECK(int1 == 123); // take the value of the option before --
  120. }
  121. SECTION("missing value for option") {
  122. TclObject in[] = { TclObject("bla"), TclObject("-int1") };
  123. CHECK_THROWS(parseTclArgs(interp, in, table));
  124. }
  125. SECTION("non-integer value for integer-option") {
  126. TclObject in[] = { TclObject("-int1"), TclObject("bla") };
  127. CHECK_THROWS(parseTclArgs(interp, in, table));
  128. }
  129. SECTION("non-double value for double-option") {
  130. TclObject in[] = { TclObject("-double2"), TclObject("bla") };
  131. CHECK_THROWS(parseTclArgs(interp, in, table));
  132. }
  133. SECTION("unknown option") {
  134. TclObject in[] = { TclObject("-bla"), TclObject("bla") };
  135. CHECK_THROWS(parseTclArgs(interp, in, table));
  136. }
  137. }