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