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 uint8_t buf[] = {'a', 'b', 'c'};
43 TclObject t(std::span<uint8_t>{buf, sizeof(buf)});
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 uint8_t buf[] = {'x', 'y', 'z'};
200 t.addListElement(std::span<uint8_t>{buf, sizeof(buf)});
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 int ints[] = {7, 6, 5};
231 double 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:92
TclObject executeCommand(Interpreter &interp, bool compile=false)
Interpret this TclObject as a command and execute it.
Definition: TclObject.cc:190
iterator begin() const
Definition: TclObject.hh:169
unsigned getListLength(Interpreter &interp) const
Definition: TclObject.cc:134
TclObject getListIndex(Interpreter &interp, unsigned index) const
Definition: TclObject.cc:152
bool empty() const
Definition: TclObject.hh:168
double getDouble(Interpreter &interp) const
Definition: TclObject.cc:110
void addListElement(const T &t)
Definition: TclObject.hh:128
std::span< const uint8_t > getBinary() const
Definition: TclObject.cc:127
void addListElements(ITER first, ITER last)
Definition: TclObject.hh:129
int getInt(Interpreter &interp) const
Definition: TclObject.cc:73
void addDictKeyValue(const Key &key, const Value &value)
Definition: TclObject.hh:142
void addDictKeyValues(Args &&... args)
Definition: TclObject.hh:145
TclObject getDictValue(Interpreter &interp, const TclObject &key) const
Definition: TclObject.cc:170
unsigned size() const
Definition: TclObject.hh:167
zstring_view getString() const
Definition: TclObject.cc:120
iterator end() const
Definition: TclObject.hh:170
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))
constexpr double e
Definition: Math.hh:21
This file implemented 3 utility functions:
Definition: Autofire.cc:9
TclObject makeTclList(Args &&... args)
Definition: TclObject.hh:283
TclObject makeTclDict(Args &&... args)
Definition: TclObject.hh:289
bool equal(InputRange1 &&range1, InputRange2 &&range2, Pred pred={}, Proj1 proj1={}, Proj2 proj2={})
Definition: ranges.hh:343
void advance(octet_iterator &it, distance_type n, octet_iterator end)
auto distance(octet_iterator first, octet_iterator last)
constexpr auto transform(Range &&range, UnaryOp op)
Definition: view.hh:458
constexpr auto begin(const zstring_view &x)
constexpr auto end(const zstring_view &x)