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