TclObject_test.cc 12 KB

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