openMSX
TclObject_test.cc
Go to the documentation of this file.
1#include "catch.hpp"
2#include "Interpreter.hh"
3#include "TclObject.hh"
4#include "ranges.hh"
5#include "view.hh"
6#include <cstdint>
7#include <iterator>
8#include <string>
9
10using namespace openmsx;
11
12TEST_CASE("TclObject, constructors")
13{
14 Interpreter interp;
15 SECTION("default") {
17 CHECK(t.getString() == "");
18 }
19 SECTION("string_view") {
20 TclObject t1("foo"); // const char*
21 CHECK(t1.getString() == "foo");
22 TclObject t2(std::string("bar")); // string
23 CHECK(t2.getString() == "bar");
24 TclObject t3(std::string_view("qux")); // string_view
25 CHECK(t3.getString() == "qux");
26 }
27 SECTION("bool") {
28 TclObject t1(true);
29 CHECK(t1.getString() == "1");
30 TclObject t2(false);
31 CHECK(t2.getString() == "0");
32 }
33 SECTION("int") {
34 TclObject t(42);
35 CHECK(t.getString() == "42");
36 }
37 SECTION("double") {
38 TclObject t(6.28);
39 CHECK(t.getString() == "6.28");
40 }
41 SECTION("binary") {
42 auto buf = std::to_array<uint8_t>({'a', 'b', 'c'});
43 TclObject t(std::span<uint8_t>{buf.data(), buf.size()});
44 CHECK(t.getString() == "abc");
45 }
46 SECTION("copy") {
47 TclObject t1("bar");
48 TclObject t2 = t1;
49 CHECK(t1.getString() == "bar");
50 CHECK(t2.getString() == "bar");
51 t1 = 123;
52 CHECK(t1.getString() == "123");
53 CHECK(t2.getString() == "bar");
54 }
55 SECTION("move") {
56 TclObject t1("bar");
57 TclObject t2 = std::move(t1);
58 CHECK(t2.getString() == "bar");
59 }
60 SECTION("list") {
62 CHECK(t0.getListLength(interp) == 0);
63
64 TclObject t1 = makeTclList(1);
65 CHECK(t1.getListLength(interp) == 1);
66 CHECK(t1.getListIndex(interp, 0).getInt(interp) == 1);
67
68 TclObject t2 = makeTclList("foo", 2.72);
69 CHECK(t2.getListLength(interp) == 2);
70 CHECK(t2.getListIndex(interp, 0).getString() == "foo");
71 CHECK(t2.getListIndex(interp, 1).getDouble(interp) == 2.72);
72 }
73 SECTION("dict") {
75 CHECK(t0.getDictValue(interp, "foo").getString() == "");
76 CHECK(t0.getDictValue(interp, "bar").getString() == "");
77
78 TclObject t1 = makeTclDict("foo", true);
79 CHECK(t1.getDictValue(interp, "foo").getBoolean(interp) == true);
80 CHECK(t1.getDictValue(interp, "bar").getString() == "");
81
82 TclObject t2 = makeTclDict("foo", 123, "bar", "qux");
83 CHECK(t2.getDictValue(interp, "foo").getInt(interp) == 123);
84 CHECK(t2.getDictValue(interp, "bar").getString() == "qux");
85 }
86}
87
88TEST_CASE("TclObject, assignment")
89{
90 Interpreter interp;
91 SECTION("copy") {
92 TclObject t1(123);
93 TclObject t2(987);
94 REQUIRE(t1 != t2);
95 t2 = t1;
96 CHECK(t1 == t2);
97 CHECK(t1.getString() == "123");
98 CHECK(t2.getString() == "123");
99 t1 = 456;
100 CHECK(t1 != t2);
101 CHECK(t1.getString() == "456");
102 CHECK(t2.getString() == "123");
103 }
104 SECTION("move") {
105 TclObject t1(123);
106 TclObject t2(987);
107 REQUIRE(t1 != t2);
108 t2 = std::move(t1);
109 CHECK(t2.getString() == "123");
110 t1 = 456;
111 CHECK(t1 != t2);
112 CHECK(t1.getString() == "456");
113 CHECK(t2.getString() == "123");
114 }
115}
116
117// skipped getTclObject() / getTclObjectNonConst()
118
119TEST_CASE("TclObject, operator=")
120{
121 Interpreter interp;
122 TclObject t(123);
123 REQUIRE(t.getString() == "123");
124
125 SECTION("string") {
126 t = "foo";
127 CHECK(t.getString() == "foo");
128 t = std::string("bar");
129 CHECK(t.getString() == "bar");
130 t = std::string_view("qux");
131 CHECK(t.getString() == "qux");
132 }
133 SECTION("int") {
134 t = 42;
135 CHECK(t.getString() == "42");
136 }
137 SECTION("bool") {
138 t = true;
139 CHECK(t.getString() == "1");
140 t = false;
141 CHECK(t.getString() == "0");
142 }
143 SECTION("double") {
144 t = -3.14;
145 CHECK(t.getString() == "-3.14");
146 }
147 SECTION("binary") {
148 std::array<uint8_t, 3> buf = {1, 2, 3};
149 t = std::span{buf};
150 auto result = t.getBinary();
151 CHECK(ranges::equal(buf, result));
152 // 'buf' was copied into 't'
153 CHECK(result.data() != &buf[0]);
154 CHECK(result[0] == 1);
155 buf[0] = 99;
156 CHECK(result[0] == 1);
157 }
158 SECTION("copy") {
159 TclObject t2(true);
160 REQUIRE(t2.getString() == "1");
161 t = t2;
162 CHECK(t .getString() == "1");
163 CHECK(t2.getString() == "1");
164 t2 = false;
165 CHECK(t .getString() == "1");
166 CHECK(t2.getString() == "0");
167 }
168 SECTION("move") {
169 TclObject t2(true);
170 REQUIRE(t2.getString() == "1");
171 t = std::move(t2);
172 CHECK(t .getString() == "1");
173 t2 = false;
174 CHECK(t .getString() == "1");
175 CHECK(t2.getString() == "0");
176 }
177}
178
179TEST_CASE("TclObject, addListElement")
180{
181 Interpreter interp;
182
183 SECTION("no error") {
184 TclObject t;
185 CHECK(t.getListLength(interp) == 0);
186 t.addListElement("foo bar");
187 CHECK(t.getListLength(interp) == 1);
188 t.addListElement(false);
189 CHECK(t.getListLength(interp) == 2);
190 t.addListElement(33);
191 CHECK(t.getListLength(interp) == 3);
192 t.addListElement(9.23);
193 CHECK(t.getListLength(interp) == 4);
194 TclObject t2("bla");
195 t.addListElement(t2);
196 CHECK(t.getListLength(interp) == 5);
197 t.addListElement(std::string("qux"));
198 CHECK(t.getListLength(interp) == 6);
199 auto buf = std::to_array<uint8_t>({'x', 'y', 'z'});
200 t.addListElement(std::span<uint8_t>{buf.data(), buf.size()});
201 CHECK(t.getListLength(interp) == 7);
202
203 TclObject l0 = t.getListIndex(interp, 0);
204 TclObject l1 = t.getListIndex(interp, 1);
205 TclObject l2 = t.getListIndex(interp, 2);
206 TclObject l3 = t.getListIndex(interp, 3);
207 TclObject l4 = t.getListIndex(interp, 4);
208 TclObject l5 = t.getListIndex(interp, 5);
209 TclObject l6 = t.getListIndex(interp, 6);
210 CHECK(l0.getString() == "foo bar");
211 CHECK(l1.getString() == "0");
212 CHECK(l2.getString() == "33");
213 CHECK(l3.getString() == "9.23");
214 CHECK(l4.getString() == "bla");
215 CHECK(l5.getString() == "qux");
216 CHECK(l6.getString() == "xyz");
217
218 CHECK(t.getString() == "{foo bar} 0 33 9.23 bla qux xyz");
219 }
220 SECTION("error") {
221 TclObject t("{foo"); // invalid list representation
222 CHECK_THROWS(t.getListLength(interp));
223 CHECK_THROWS(t.addListElement(123));
224 }
225}
226
227TEST_CASE("TclObject, addListElements")
228{
229 Interpreter interp;
230 std::array ints = {7, 6, 5};
231 std::array doubles = {1.2, 5.6};
232
233 SECTION("no error") {
234 TclObject t;
235 CHECK(t.getListLength(interp) == 0);
236 // iterator-pair
237 t.addListElements(std::begin(ints), std::end(ints));
238 CHECK(t.getListLength(interp) == 3);
239 CHECK(t.getListIndex(interp, 1).getString() == "6");
240 // range (array)
241 t.addListElements(doubles);
242 CHECK(t.getListLength(interp) == 5);
243 CHECK(t.getListIndex(interp, 3).getString() == "1.2");
244 // view::transform
245 t.addListElements(view::transform(ints, [](int i) { return 2 * i; }));
246 CHECK(t.getListLength(interp) == 8);
247 CHECK(t.getListIndex(interp, 7).getString() == "10");
248 // multiple
249 t.addListElement("one", 2, 3.14);
250 CHECK(t.getListLength(interp) == 11);
251 CHECK(t.getListIndex(interp, 8).getString() == "one");
252 CHECK(t.getListIndex(interp, 9).getString() == "2");
253 CHECK(t.getListIndex(interp, 10).getString() == "3.14");
254 }
255 SECTION("error") {
256 TclObject t("{foo"); // invalid list representation
257 CHECK_THROWS(t.addListElements(std::begin(doubles), std::end(doubles)));
258 CHECK_THROWS(t.addListElements(ints));
259 }
260}
261
262TEST_CASE("TclObject, addDictKeyValue(s)")
263{
264 Interpreter interp;
266
268 CHECK(t.getDictValue(interp, "one").getInt(interp) == 1);
269 CHECK(t.getDictValue(interp, "two").getString() == "");
270 CHECK(t.getDictValue(interp, "three").getString() == "");
271
272 t.addDictKeyValues("two", 2, "three", 3.14);
273 CHECK(t.getDictValue(interp, "one").getInt(interp) == 1);
274 CHECK(t.getDictValue(interp, "two").getInt(interp) == 2);
275 CHECK(t.getDictValue(interp, "three").getDouble(interp) == 3.14);
276
277 t.addDictKeyValues("four", false, "one", "een");
278 CHECK(t.getDictValue(interp, "one").getString() == "een");
279 CHECK(t.getDictValue(interp, "two").getInt(interp) == 2);
280 CHECK(t.getDictValue(interp, "three").getDouble(interp) == 3.14);
281 CHECK(t.getDictValue(interp, "four").getBoolean(interp) == false);
282}
283
284// there are no setting functions (yet?) for dicts
285
286TEST_CASE("TclObject, getXXX")
287{
288 Interpreter interp;
289 TclObject t0;
290 TclObject t1("Off");
291 TclObject t2(1);
292 TclObject t3(2.71828);
293
294 SECTION("getString") { // never fails
295 CHECK(t0.getString() == "");
296 CHECK(t1.getString() == "Off");
297 CHECK(t2.getString() == "1");
298 CHECK(t3.getString() == "2.71828");
299 }
300 SECTION("getInt") {
301 CHECK_THROWS(t0.getInt(interp));
302 CHECK_THROWS(t1.getInt(interp));
303 CHECK (t2.getInt(interp) == 1);
304 CHECK_THROWS(t3.getInt(interp));
305 }
306 SECTION("getBoolean") {
307 CHECK_THROWS(t0.getBoolean(interp));
308 CHECK (t1.getBoolean(interp) == false);
309 CHECK (t2.getBoolean(interp) == true);
310 CHECK (t3.getBoolean(interp) == true);
311 }
312 SECTION("getDouble") {
313 CHECK_THROWS(t0.getDouble(interp));
314 CHECK_THROWS(t1.getDouble(interp));
315 CHECK (t2.getDouble(interp) == 1.0);
316 CHECK (t3.getDouble(interp) == 2.71828);
317 }
318}
319
320// getBinary() already tested above
321// getListLength and getListIndex() already tested above
322
323TEST_CASE("TclObject, getDictValue")
324{
325 Interpreter interp;
326
327 SECTION("no error") {
328 TclObject t("one 1 two 2.0 three drie");
329 CHECK(t.getDictValue(interp, TclObject("two" )).getString() == "2.0");
330 CHECK(t.getDictValue(interp, TclObject("one" )).getString() == "1");
331 CHECK(t.getDictValue(interp, TclObject("three")).getString() == "drie");
332 // missing key -> empty string .. can be improved when needed
333 CHECK(t.getDictValue(interp, TclObject("four" )).getString() == "");
334 }
335 SECTION("invalid dict") {
336 TclObject t("{foo");
337 CHECK_THROWS(t.getDictValue(interp, TclObject("foo")));
338 }
339}
340
341TEST_CASE("TclObject, STL interface on Tcl list")
342{
343 Interpreter interp;
344
345 SECTION("empty") {
346 TclObject t;
347 CHECK(t.size() == 0);
348 CHECK(t.empty() == true);
349 CHECK(t.begin() == t.end());
350 }
351 SECTION("not empty") {
352 TclObject t("1 1 2 3 5 8 13 21 34 55");
353 CHECK(t.size() == 10);
354 CHECK(t.empty() == false);
355 auto b = t.begin();
356 auto e = t.end();
357 CHECK(std::distance(b, e) == 10);
358 CHECK(*b == "1");
359 std::advance(b, 5);
360 CHECK(*b == "8");
361 ++b;
362 CHECK(*b == "13");
363 std::advance(b, 4);
364 CHECK(b == e);
365 }
366 SECTION("invalid list") {
367 // acts as if the list is empty .. can be improved when needed
368 TclObject t("{foo bar qux");
369 CHECK(t.size() == 0);
370 CHECK(t.empty() == true);
371 CHECK(t.begin() == t.end());
372 }
373}
374
375TEST_CASE("TclObject, evalBool")
376{
377 Interpreter interp;
378 CHECK(TclObject("23 == (20 + 3)").evalBool(interp) == true);
379 CHECK(TclObject("1 >= (6-2)" ).evalBool(interp) == false);
380 CHECK_THROWS(TclObject("bla").evalBool(interp));
381}
382
383TEST_CASE("TclObject, executeCommand")
384{
385 Interpreter interp;
386 CHECK(TclObject("return foobar").executeCommand(interp).getString() == "foobar");
387 CHECK(TclObject("set n 2").executeCommand(interp).getString() == "2");
388 TclObject cmd("string repeat bla $n");
389 CHECK(cmd.executeCommand(interp, true).getString() == "blabla");
390 CHECK(TclObject("incr n").executeCommand(interp).getString() == "3");
391 CHECK(cmd.executeCommand(interp, true).getString() == "blablabla");
392
393 CHECK_THROWS(TclObject("qux").executeCommand(interp));
394}
395
396TEST_CASE("TclObject, operator==, operator!=")
397{
398 Interpreter interp;
399 TclObject t0;
400 TclObject t1("foo");
401 TclObject t2("bar qux");
402 TclObject t3("foo");
403
404 CHECK( t0 == t0 ); CHECK(!(t0 != t0));
405 CHECK(!(t0 == t1)); CHECK( t0 != t1 );
406 CHECK(!(t0 == t2)); CHECK( t0 != t2 );
407 CHECK(!(t0 == t3)); CHECK( t0 != t3 );
408 CHECK( t1 == t1 ); CHECK(!(t1 != t1));
409 CHECK(!(t1 == t2)); CHECK( t1 != t2 );
410 CHECK( t1 == t3 ); CHECK(!(t1 != t3));
411 CHECK( t2 == t2 ); CHECK(!(t2 != t2));
412 CHECK(!(t2 == t3)); CHECK( t2 != t3 );
413 CHECK( t3 == t3 ); CHECK(!(t3 != t3));
414
415 CHECK(t0 == "" ); CHECK(!(t0 != "" )); CHECK("" == t0); CHECK(!("" != t0));
416 CHECK(t0 != "foo"); CHECK(!(t0 == "foo")); CHECK("foo" != t0); CHECK(!("foo" == t0));
417 CHECK(t1 != "" ); CHECK(!(t1 == "" )); CHECK("" != t1); CHECK(!("" == t1));
418 CHECK(t1 == "foo"); CHECK(!(t1 != "foo")); CHECK("foo" == t1); CHECK(!("foo" != t1));
419 CHECK(t2 != "" ); CHECK(!(t2 == "" )); CHECK("" != t2); CHECK(!("" == t2));
420 CHECK(t2 != "foo"); CHECK(!(t2 == "foo")); CHECK("foo" != t2); CHECK(!("foo" == t2));
421}
422
423// skipped XXTclHasher
TclObject t
TEST_CASE("TclObject, constructors")
CHECK(t.getDictValue(interp, "one").getInt(interp)==1)
bool getBoolean(Interpreter &interp) const
Definition TclObject.cc:88
TclObject executeCommand(Interpreter &interp, bool compile=false)
Interpret this TclObject as a command and execute it.
Definition TclObject.cc:248
iterator begin() const
Definition TclObject.hh:179
unsigned getListLength(Interpreter &interp) const
Definition TclObject.cc:155
TclObject getListIndex(Interpreter &interp, unsigned index) const
Definition TclObject.cc:173
bool empty() const
Definition TclObject.hh:178
double getDouble(Interpreter &interp) const
Definition TclObject.cc:122
void addListElement(const T &t)
Definition TclObject.hh:131
std::span< const uint8_t > getBinary() const
Definition TclObject.cc:148
void addListElements(ITER first, ITER last)
Definition TclObject.hh:132
int getInt(Interpreter &interp) const
Definition TclObject.cc:69
void addDictKeyValue(const Key &key, const Value &value)
Definition TclObject.hh:145
void addDictKeyValues(Args &&... args)
Definition TclObject.hh:148
TclObject getDictValue(Interpreter &interp, const TclObject &key) const
Definition TclObject.cc:209
unsigned size() const
Definition TclObject.hh:177
zstring_view getString() const
Definition TclObject.cc:141
iterator end() const
Definition TclObject.hh:180
imat3 l3(ivec3(0, 2, 3), ivec3(4, 5, 6), ivec3(7, 8, 9))
imat4 l4(ivec4(1, 2, 3, 4), ivec4(3, 4, 5, 6), ivec4(5, 6, 7, 0), ivec4(7, 8, 9, 0))
This file implemented 3 utility functions:
Definition Autofire.cc:11
TclObject makeTclList(Args &&... args)
Definition TclObject.hh:293
TclObject makeTclDict(Args &&... args)
Definition TclObject.hh:299
bool equal(InputRange1 &&range1, InputRange2 &&range2, Pred pred={}, Proj1 proj1={}, Proj2 proj2={})
Definition ranges.hh:368
constexpr auto transform(Range &&range, UnaryOp op)
Definition view.hh:520