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 "view.hh"
5 #include <cstdint>
6 #include <cstring>
7 #include <iterator>
8 #include <string>
9 
10 using namespace openmsx;
11 
12 TEST_CASE("TclObject, constructors")
13 {
14  Interpreter interp;
15  SECTION("default") {
16  TclObject t;
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(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") {
61  TclObject t0 = makeTclList();
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") {
74  TclObject t0 = makeTclDict();
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 
88 TEST_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 
119 TEST_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  uint8_t buf[] = {1, 2, 3};
149  t = span<uint8_t>{buf, sizeof(buf)};
150  auto result = t.getBinary();
151  CHECK(result.size() == sizeof(buf));
152  CHECK(memcmp(buf, result.data(), result.size()) == 0);
153  // 'buf' was copied into 't'
154  CHECK(result.data() != &buf[0]);
155  CHECK(result[0] == 1);
156  buf[0] = 99;
157  CHECK(result[0] == 1);
158  }
159  SECTION("copy") {
160  TclObject t2(true);
161  REQUIRE(t2.getString() == "1");
162  t = t2;
163  CHECK(t .getString() == "1");
164  CHECK(t2.getString() == "1");
165  t2 = false;
166  CHECK(t .getString() == "1");
167  CHECK(t2.getString() == "0");
168  }
169  SECTION("move") {
170  TclObject t2(true);
171  REQUIRE(t2.getString() == "1");
172  t = std::move(t2);
173  CHECK(t .getString() == "1");
174  t2 = false;
175  CHECK(t .getString() == "1");
176  CHECK(t2.getString() == "0");
177  }
178 }
179 
180 TEST_CASE("TclObject, addListElement")
181 {
182  Interpreter interp;
183 
184  SECTION("no error") {
185  TclObject t;
186  CHECK(t.getListLength(interp) == 0);
187  t.addListElement("foo bar");
188  CHECK(t.getListLength(interp) == 1);
189  t.addListElement(false);
190  CHECK(t.getListLength(interp) == 2);
191  t.addListElement(33);
192  CHECK(t.getListLength(interp) == 3);
193  t.addListElement(9.23);
194  CHECK(t.getListLength(interp) == 4);
195  TclObject t2("bla");
196  t.addListElement(t2);
197  CHECK(t.getListLength(interp) == 5);
198  t.addListElement(std::string("qux"));
199  CHECK(t.getListLength(interp) == 6);
200  uint8_t buf[] = {'x', 'y', 'z'};
201  t.addListElement(span<uint8_t>{buf, sizeof(buf)});
202  CHECK(t.getListLength(interp) == 7);
203 
204  TclObject l0 = t.getListIndex(interp, 0);
205  TclObject l1 = t.getListIndex(interp, 1);
206  TclObject l2 = t.getListIndex(interp, 2);
207  TclObject l3 = t.getListIndex(interp, 3);
208  TclObject l4 = t.getListIndex(interp, 4);
209  TclObject l5 = t.getListIndex(interp, 5);
210  TclObject l6 = t.getListIndex(interp, 6);
211  CHECK(l0.getString() == "foo bar");
212  CHECK(l1.getString() == "0");
213  CHECK(l2.getString() == "33");
214  CHECK(l3.getString() == "9.23");
215  CHECK(l4.getString() == "bla");
216  CHECK(l5.getString() == "qux");
217  CHECK(l6.getString() == "xyz");
218 
219  CHECK(t.getString() == "{foo bar} 0 33 9.23 bla qux xyz");
220  }
221  SECTION("error") {
222  TclObject t("{foo"); // invalid list representation
223  CHECK_THROWS(t.getListLength(interp));
224  CHECK_THROWS(t.addListElement(123));
225  }
226 }
227 
228 TEST_CASE("TclObject, addListElements")
229 {
230  Interpreter interp;
231  int ints[] = {7, 6, 5};
232  double doubles[] = {1.2, 5.6};
233 
234  SECTION("no error") {
235  TclObject t;
236  CHECK(t.getListLength(interp) == 0);
237  // iterator-pair
238  t.addListElements(std::begin(ints), std::end(ints));
239  CHECK(t.getListLength(interp) == 3);
240  CHECK(t.getListIndex(interp, 1).getString() == "6");
241  // range (array)
242  t.addListElements(doubles);
243  CHECK(t.getListLength(interp) == 5);
244  CHECK(t.getListIndex(interp, 3).getString() == "1.2");
245  // view::transform
246  t.addListElements(view::transform(ints, [](int i) { return 2 * i; }));
247  CHECK(t.getListLength(interp) == 8);
248  CHECK(t.getListIndex(interp, 7).getString() == "10");
249  // multiple
250  t.addListElement("one", 2, 3.14);
251  CHECK(t.getListLength(interp) == 11);
252  CHECK(t.getListIndex(interp, 8).getString() == "one");
253  CHECK(t.getListIndex(interp, 9).getString() == "2");
254  CHECK(t.getListIndex(interp, 10).getString() == "3.14");
255  }
256  SECTION("error") {
257  TclObject t("{foo"); // invalid list representation
258  CHECK_THROWS(t.addListElements(std::begin(doubles), std::end(doubles)));
259  CHECK_THROWS(t.addListElements(ints));
260  }
261 }
262 
263 TEST_CASE("TclObject, addDictKeyValue(s)")
264 {
265  Interpreter interp;
267 
268  t.addDictKeyValue("one", 1);
269  CHECK(t.getDictValue(interp, "one").getInt(interp) == 1);
270  CHECK(t.getDictValue(interp, "two").getString() == "");
271  CHECK(t.getDictValue(interp, "three").getString() == "");
272 
273  t.addDictKeyValues("two", 2, "three", 3.14);
274  CHECK(t.getDictValue(interp, "one").getInt(interp) == 1);
275  CHECK(t.getDictValue(interp, "two").getInt(interp) == 2);
276  CHECK(t.getDictValue(interp, "three").getDouble(interp) == 3.14);
277 
278  t.addDictKeyValues("four", false, "one", "een");
279  CHECK(t.getDictValue(interp, "one").getString() == "een");
280  CHECK(t.getDictValue(interp, "two").getInt(interp) == 2);
281  CHECK(t.getDictValue(interp, "three").getDouble(interp) == 3.14);
282  CHECK(t.getDictValue(interp, "four").getBoolean(interp) == false);
283 }
284 
285 // there are no setting functions (yet?) for dicts
286 
287 TEST_CASE("TclObject, getXXX")
288 {
289  Interpreter interp;
290  TclObject t0;
291  TclObject t1("Off");
292  TclObject t2(1);
293  TclObject t3(2.71828);
294 
295  SECTION("getString") { // never fails
296  CHECK(t0.getString() == "");
297  CHECK(t1.getString() == "Off");
298  CHECK(t2.getString() == "1");
299  CHECK(t3.getString() == "2.71828");
300  }
301  SECTION("getInt") {
302  CHECK_THROWS(t0.getInt(interp));
303  CHECK_THROWS(t1.getInt(interp));
304  CHECK (t2.getInt(interp) == 1);
305  CHECK_THROWS(t3.getInt(interp));
306  }
307  SECTION("getBoolean") {
308  CHECK_THROWS(t0.getBoolean(interp));
309  CHECK (t1.getBoolean(interp) == false);
310  CHECK (t2.getBoolean(interp) == true);
311  CHECK (t3.getBoolean(interp) == true);
312  }
313  SECTION("getDouble") {
314  CHECK_THROWS(t0.getDouble(interp));
315  CHECK_THROWS(t1.getDouble(interp));
316  CHECK (t2.getDouble(interp) == 1.0);
317  CHECK (t3.getDouble(interp) == 2.71828);
318  }
319 }
320 
321 // getBinary() already tested above
322 // getListLength and getListIndex() already tested above
323 
324 TEST_CASE("TclObject, getDictValue")
325 {
326  Interpreter interp;
327 
328  SECTION("no error") {
329  TclObject t("one 1 two 2.0 three drie");
330  CHECK(t.getDictValue(interp, TclObject("two" )).getString() == "2.0");
331  CHECK(t.getDictValue(interp, TclObject("one" )).getString() == "1");
332  CHECK(t.getDictValue(interp, TclObject("three")).getString() == "drie");
333  // missing key -> empty string .. can be improved when needed
334  CHECK(t.getDictValue(interp, TclObject("four" )).getString() == "");
335  }
336  SECTION("invalid dict") {
337  TclObject t("{foo");
338  CHECK_THROWS(t.getDictValue(interp, TclObject("foo")));
339  }
340 }
341 
342 TEST_CASE("TclObject, STL interface on Tcl list")
343 {
344  Interpreter interp;
345 
346  SECTION("empty") {
347  TclObject t;
348  CHECK(t.size() == 0);
349  CHECK(t.empty() == true);
350  CHECK(t.begin() == t.end());
351  }
352  SECTION("not empty") {
353  TclObject t("1 1 2 3 5 8 13 21 34 55");
354  CHECK(t.size() == 10);
355  CHECK(t.empty() == false);
356  auto b = t.begin();
357  auto e = t.end();
358  CHECK(std::distance(b, e) == 10);
359  CHECK(*b == "1");
360  std::advance(b, 5);
361  CHECK(*b == "8");
362  ++b;
363  CHECK(*b == "13");
364  std::advance(b, 4);
365  CHECK(b == e);
366  }
367  SECTION("invalid list") {
368  // acts as if the list is empty .. can be improved when needed
369  TclObject t("{foo bar qux");
370  CHECK(t.size() == 0);
371  CHECK(t.empty() == true);
372  CHECK(t.begin() == t.end());
373  }
374 }
375 
376 TEST_CASE("TclObject, evalBool")
377 {
378  Interpreter interp;
379  CHECK(TclObject("23 == (20 + 3)").evalBool(interp) == true);
380  CHECK(TclObject("1 >= (6-2)" ).evalBool(interp) == false);
381  CHECK_THROWS(TclObject("bla").evalBool(interp));
382 }
383 
384 TEST_CASE("TclObject, executeCommand")
385 {
386  Interpreter interp;
387  CHECK(TclObject("return foobar").executeCommand(interp).getString() == "foobar");
388  CHECK(TclObject("set n 2").executeCommand(interp).getString() == "2");
389  TclObject cmd("string repeat bla $n");
390  CHECK(cmd.executeCommand(interp, true).getString() == "blabla");
391  CHECK(TclObject("incr n").executeCommand(interp).getString() == "3");
392  CHECK(cmd.executeCommand(interp, true).getString() == "blablabla");
393 
394  CHECK_THROWS(TclObject("qux").executeCommand(interp));
395 }
396 
397 TEST_CASE("TclObject, operator==, operator!=")
398 {
399  Interpreter interp;
400  TclObject t0;
401  TclObject t1("foo");
402  TclObject t2("bar qux");
403  TclObject t3("foo");
404 
405  CHECK( t0 == t0 ); CHECK(!(t0 != t0));
406  CHECK(!(t0 == t1)); CHECK( t0 != t1 );
407  CHECK(!(t0 == t2)); CHECK( t0 != t2 );
408  CHECK(!(t0 == t3)); CHECK( t0 != t3 );
409  CHECK( t1 == t1 ); CHECK(!(t1 != t1));
410  CHECK(!(t1 == t2)); CHECK( t1 != t2 );
411  CHECK( t1 == t3 ); CHECK(!(t1 != t3));
412  CHECK( t2 == t2 ); CHECK(!(t2 != t2));
413  CHECK(!(t2 == t3)); CHECK( t2 != t3 );
414  CHECK( t3 == t3 ); CHECK(!(t3 != t3));
415 
416  CHECK(t0 == "" ); CHECK(!(t0 != "" )); CHECK("" == t0); CHECK(!("" != t0));
417  CHECK(t0 != "foo"); CHECK(!(t0 == "foo")); CHECK("foo" != t0); CHECK(!("foo" == t0));
418  CHECK(t1 != "" ); CHECK(!(t1 == "" )); CHECK("" != t1); CHECK(!("" == t1));
419  CHECK(t1 == "foo"); CHECK(!(t1 != "foo")); CHECK("foo" == t1); CHECK(!("foo" != t1));
420  CHECK(t2 != "" ); CHECK(!(t2 == "" )); CHECK("" != t2); CHECK(!("" == t2));
421  CHECK(t2 != "foo"); CHECK(!(t2 == "foo")); CHECK("foo" != t2); CHECK(!("foo" == t2));
422 }
423 
424 // skipped XXTclHasher
TclObject t
TEST_CASE("TclObject, constructors")
CHECK(t.getDictValue(interp, "one").getInt(interp)==1)
bool getBoolean(Interpreter &interp) const
Definition: TclObject.cc:91
TclObject executeCommand(Interpreter &interp, bool compile=false)
Interpret this TclObject as a command and execute it.
Definition: TclObject.cc:181
unsigned getListLength(Interpreter &interp) const
Definition: TclObject.cc:125
TclObject getListIndex(Interpreter &interp, unsigned index) const
Definition: TclObject.cc:143
bool empty() const
Definition: TclObject.hh:169
double getDouble(Interpreter &interp) const
Definition: TclObject.cc:101
void addListElement(const T &t)
Definition: TclObject.hh:130
auto begin() const
Definition: TclObject.hh:170
void addListElements(ITER first, ITER last)
Definition: TclObject.hh:131
auto end() const
Definition: TclObject.hh:171
int getInt(Interpreter &interp) const
Definition: TclObject.cc:72
void addDictKeyValue(const Key &key, const Value &value)
Definition: TclObject.hh:144
void addDictKeyValues(Args &&... args)
Definition: TclObject.hh:147
span< const uint8_t > getBinary() const
Definition: TclObject.cc:118
TclObject getDictValue(Interpreter &interp, const TclObject &key) const
Definition: TclObject.cc:161
unsigned size() const
Definition: TclObject.hh:168
zstring_view getString() const
Definition: TclObject.cc:111
Definition: span.hh:126
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:9
TclObject makeTclList(Args &&... args)
Definition: TclObject.hh:291
TclObject makeTclDict(Args &&... args)
Definition: TclObject.hh:297
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:306
constexpr auto begin(const zstring_view &x)
Definition: zstring_view.hh:82
constexpr auto end(const zstring_view &x)
Definition: zstring_view.hh:83