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(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 = 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;
266  TclObject t;
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
auto transform(Range &&range, UnaryOp op)
Definition: view.hh:312
auto distance(octet_iterator first, octet_iterator last)
auto begin() const
Definition: TclObject.hh:160
Definition: span.hh:34
string_view getString() const
Definition: TclObject.cc:102
unsigned getListLength(Interpreter &interp) const
Definition: TclObject.cc:116
TclObject makeTclDict(Args &&... args)
Definition: TclObject.hh:286
bool empty() const
Definition: TclObject.hh:159
void advance(octet_iterator &it, distance_type n, octet_iterator end)
auto begin(const string_view &x)
Definition: string_view.hh:151
unsigned size() const
Definition: TclObject.hh:158
span< const uint8_t > getBinary() const
Definition: TclObject.cc:109
Thanks to enen for testing this on a real cartridge:
Definition: Autofire.cc:5
TclObject getListIndex(Interpreter &interp, unsigned index) const
Definition: TclObject.cc:134
TclObject getDictValue(Interpreter &interp, const TclObject &key) const
Definition: TclObject.cc:152
TEST_CASE("TclObject, constructors")
bool getBoolean(Interpreter &interp) const
Definition: TclObject.cc:82
double getDouble(Interpreter &interp) const
Definition: TclObject.cc:92
This class implements a (close approximation) of the std::string_view class.
Definition: string_view.hh:16
int getInt(Interpreter &interp) const
Definition: TclObject.cc:72
void addListElement(T t)
Definition: TclObject.hh:121
void addDictKeyValues(Args &&... args)
Definition: TclObject.hh:138
imat4 l4(ivec4(1, 2, 3, 4), ivec4(3, 4, 5, 6), ivec4(5, 6, 7, 0), ivec4(7, 8, 9, 0))
imat3 l3(ivec3(0, 2, 3), ivec3(4, 5, 6), ivec3(7, 8, 9))
CHECK(t.getDictValue(interp, "one").getInt(interp)==1)
void addDictKeyValue(const Key &key, const Value &value)
Definition: TclObject.hh:135
auto end() const
Definition: TclObject.hh:161
void addListElements(ITER first, ITER last)
Definition: TclObject.hh:122
TclObject makeTclList(Args &&... args)
Definition: TclObject.hh:280
TclObject executeCommand(Interpreter &interp, bool compile=false)
Interpret this TclObject as a command and execute it.
Definition: TclObject.cc:172
TclObject t
auto end(const string_view &x)
Definition: string_view.hh:152