testlib.cc 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356
  1. #include <algorithm>
  2. #include <regex>
  3. #include <thread>
  4. #include <chrono>
  5. #include <unistd.h>
  6. #include "testlib.h"
  7. int verbosity = 0;
  8. int failed = 0;
  9. int count = 0;
  10. std::vector<std::pair<const char*, bool>> section;
  11. std::unique_ptr<R::Connection> conn;
  12. // std::string to_string(const R::Cursor&) {
  13. // return "<Cursor>";
  14. // }
  15. std::string to_string(const R::Term& query) {
  16. return to_string(query.get_datum());
  17. }
  18. std::string to_string(const R::Datum& datum) {
  19. return datum.as_json();
  20. }
  21. std::string to_string(const R::Object& object) {
  22. auto it = object.find("special");
  23. if (it != object.end()) {
  24. std::string type = *(it->second).get_string();
  25. auto bag = object.find(type);
  26. if (bag != object.end()) {
  27. return to_string((R::Datum)bag->second);
  28. }
  29. }
  30. return to_string((R::Datum)object);
  31. }
  32. std::string to_string(const R::Error& error) {
  33. return "Error(\"" + error.message + "\")";
  34. }
  35. void enter_section(const char* name) {
  36. if (verbosity == 0) {
  37. section.emplace_back(name, true);
  38. } else {
  39. printf("%sSection %s\n", indent(), name);
  40. section.emplace_back(name, false);
  41. }
  42. }
  43. void section_cleanup() {
  44. R::db("test").table_list().for_each([=](R::Var table) {
  45. return R::db("test").table_drop(*table);
  46. }).run(*conn);
  47. }
  48. void exit_section() {
  49. section.pop_back();
  50. }
  51. std::string to_string(const err& error) {
  52. return "Error(\"" + error.convert_type() + ": " + error.message + "\")";
  53. }
  54. bool equal(const R::Error& a, const err& b) {
  55. // @TODO: I think the proper solution to this proble is to in fact create
  56. // a hierarchy of exception types. This would not only simplify these
  57. // error cases, but could be of great use to the user.
  58. std::string error_type = b.convert_type();
  59. if (error_type == "ReqlServerCompileError" &&
  60. a.message.find("ReqlCompileError") != std::string::npos) {
  61. return true;
  62. }
  63. return b.trim_message(a.message) == (error_type + ": " + b.message);
  64. }
  65. bool match(const char* pattern, const char* string) {
  66. return std::regex_match(string, std::regex(pattern));
  67. }
  68. bool equal(const R::Error& a, const err_regex& b) {
  69. if (b.message == "Object keys must be strings" &&
  70. a.message == "runtime error: Expected type STRING but found NUMBER.") {
  71. return true;
  72. }
  73. return match(b.regex().c_str(), a.message.c_str());
  74. }
  75. std::string to_string(const err_regex& error) {
  76. return "err_regex(" + error.type + ", " + error.message + ")";
  77. }
  78. R::Object partial(R::Object&& object) {
  79. return R::Object{{"special", "partial"}, {"partial", std::move(object)}};
  80. }
  81. R::Datum uuid() {
  82. return R::Object{{"special", "uuid"}};
  83. }
  84. R::Object arrlen(int n, R::Datum&& datum) {
  85. return R::Object{{"special", "arrlen"},{"len",n},{"of",datum}};
  86. }
  87. R::Object arrlen(int n) {
  88. return R::Object{{"special", "arrlen"},{"len",n}};
  89. }
  90. std::string repeat(std::string&& s, int n) {
  91. std::string string;
  92. string.reserve(n * s.size());
  93. for (int i = 0; i < n; ++i) {
  94. string.append(s);
  95. }
  96. return string;
  97. }
  98. R::Term fetch(R::Cursor& cursor, int count, double timeout) {
  99. // printf("fetch(..., %d, %lf)\n", count, timeout);
  100. R::Array array;
  101. int deadline = time(NULL) + int(timeout);
  102. for (int i = 0; count == -1 || i < count; ++i) {
  103. // printf("fetching next (%d)\n", i);
  104. time_t now = time(NULL);
  105. if (now > deadline) break;
  106. try {
  107. array.emplace_back(cursor.next(deadline - now));
  108. // printf("got %s\n", write_datum(array[array.size()-1]).c_str());
  109. } catch (const R::Error &e) {
  110. if (e.message != "next: No more data") {
  111. throw e; // rethrow
  112. }
  113. break;
  114. } catch (const R::TimeoutException &e){
  115. // printf("fetch timeout\n");
  116. break;
  117. }
  118. }
  119. return expr(std::move(array));
  120. }
  121. R::Object bag(R::Array&& array) {
  122. return R::Object{{"special", "bag"}, {"bag", std::move(array)}};
  123. };
  124. R::Object bag(R::Datum&& d) {
  125. return R::Object{{"special", "bag"}, {"bag", std::move(d)}};
  126. };
  127. std::string string_key(const R::Datum& datum) {
  128. const std::string* string = datum.get_string();
  129. if (string) return *string;
  130. return datum.as_json();
  131. }
  132. bool falsey(R::Datum&& datum) {
  133. bool* boolean = datum.get_boolean();
  134. if (boolean) return !*boolean;
  135. double* number = datum.get_number();
  136. if (number) return *number == 0;
  137. return false;
  138. }
  139. bool equal(const R::Datum& got, const R::Datum& expected) {
  140. const std::string* string = expected.get_string();
  141. if (string) {
  142. const R::Binary* binary = got.get_binary();
  143. if (binary) {
  144. return *binary == R::Binary(*string);
  145. }
  146. }
  147. if (expected.get_object() && expected.get_field("$reql_type$")) {
  148. if (!got.get_field("$reql_type$")) {
  149. R::Datum datum = got.to_raw();
  150. if (datum.get_field("$reql_type$")) {
  151. return equal(datum, expected);
  152. }
  153. }
  154. }
  155. if (got.get_object() && got.get_field("$reql_type$")) {
  156. const std::string* type = got.get_field("$reql_type$")->get_string();
  157. if (type && *type == "GROUPED_DATA" &&
  158. (!expected.get_object() || !expected.get_field("$reql_type$"))) {
  159. const R::Array* data = got.get_field("data")->get_array();
  160. R::Object object;
  161. for (R::Datum it : *data) {
  162. object.emplace(string_key(it.extract_nth(0)), it.extract_nth(1));
  163. }
  164. return equal(object, expected);
  165. }
  166. }
  167. do {
  168. if (!expected.get_object()) break;
  169. if(!expected.get_field("special")) break;
  170. const std::string* type = expected.get_field("special")->get_string();
  171. if (!type) break;
  172. if (*type == "bag") {
  173. const R::Datum* bag_datum = expected.get_field("bag");
  174. if (!bag_datum || !bag_datum->get_array()) {
  175. break;
  176. }
  177. R::Array bag = *bag_datum->get_array();
  178. const R::Array* array = got.get_array();
  179. if (!array) {
  180. return false;
  181. }
  182. if (bag.size() != array->size()) {
  183. return false;
  184. }
  185. for (const auto& it : *array) {
  186. auto ref = std::find(bag.begin(), bag.end(), it);
  187. if (ref == bag.end()) return false;
  188. bag.erase(ref);
  189. }
  190. return true;
  191. } else if (*type == "arrlen") {
  192. const R::Datum* len_datum = expected.get_field("len");
  193. if (!len_datum) break;
  194. const double *len = len_datum->get_number();
  195. if (!len) break;
  196. const R::Array* array = got.get_array();
  197. if (!array) break;
  198. return array->size() == *len;
  199. } else if (*type == "partial") {
  200. const R::Object* object = got.get_object();
  201. if (object) {
  202. const R::Datum* partial_datum = expected.get_field("partial");
  203. if (!partial_datum) break;
  204. const R::Object* partial = partial_datum->get_object();
  205. if (!partial) break;
  206. for (const auto& it : *partial) {
  207. if (!object->count(it.first) || !equal((*object).at(it.first), it.second)) {
  208. return false;
  209. }
  210. return true;
  211. }
  212. }
  213. const R::Array* array = got.get_array();
  214. if (array) {
  215. const R::Datum* partial_datum = expected.get_field("partial");
  216. if (!partial_datum) break;
  217. const R::Array* partial = partial_datum->get_array();
  218. if (!partial) break;
  219. for (const auto& want : *partial) {
  220. bool match = false;
  221. for (const auto& have : *array) {
  222. if (equal(have, want)) {
  223. match = true;
  224. break;
  225. }
  226. }
  227. if (match == false) return false;
  228. }
  229. return true;
  230. }
  231. } else if(*type == "uuid") {
  232. const std::string* string = got.get_string();
  233. if (string && string->size() == 36) {
  234. return true;
  235. }
  236. } else if (*type == "regex") {
  237. const R::Datum* regex_datum = expected.get_field("regex");
  238. if (!regex_datum) break;
  239. const std::string* regex = regex_datum->get_string();
  240. if (!regex) break;
  241. const std::string* str = got.get_string();
  242. if (!str) break;
  243. return match(regex->c_str(), str->c_str());
  244. }
  245. } while(0);
  246. const R::Object* got_object = got.get_object();
  247. const R::Object* expected_object = expected.get_object();
  248. if (got_object && expected_object) {
  249. R::Object have = *got_object;
  250. for (const auto& it : *expected_object) {
  251. auto other = have.find(it.first);
  252. if (other == have.end()) return false;
  253. if (!equal(other->second, it.second)) return false;
  254. have.erase(other);
  255. }
  256. for (auto& it : have) {
  257. if (!falsey(std::move(it.second))) {
  258. return false;
  259. }
  260. }
  261. return true;
  262. }
  263. const R::Array* got_array = got.get_array();
  264. const R::Array* expected_array = expected.get_array();
  265. if (got_array && expected_array) {
  266. if (got_array->size() != expected_array->size()) return false;
  267. for (R::Array::const_iterator i = got_array->begin(), j = expected_array->begin();
  268. i < got_array->end();
  269. i++, j++) {
  270. if(!equal(*i, *j)) return false;
  271. }
  272. return true;
  273. }
  274. return got == expected;
  275. }
  276. R::Object partial(R::Array&& array) {
  277. return R::Object{{"special", "partial"}, {"partial", std::move(array)}};
  278. }
  279. R::Object regex(const char* pattern) {
  280. return R::Object{{"special", "regex"}, {"regex", pattern}};
  281. }
  282. void clean_slate() {
  283. R::table_list().for_each([](R::Var t){ return R::table_drop(*t); });
  284. R::db("rethinkdb").table("_debug_scratch").delete_().run(*conn);
  285. }
  286. const char* indent() {
  287. static const char spaces[] = " ";
  288. return spaces + sizeof(spaces) - 1 - 2 * section.size();
  289. }
  290. std::string truncate(std::string&& string) {
  291. if (string.size() > 200) {
  292. return string.substr(0, 197) + "...";
  293. }
  294. return string;
  295. }
  296. int len(const R::Datum& d) {
  297. const R::Array* arr = d.get_array();
  298. if (!arr) throw ("testlib: len: expected an array but got " + to_string(d));
  299. return arr->size();
  300. }
  301. R::Term wait(int n) {
  302. std::this_thread::sleep_for(std::chrono::seconds(n));
  303. return R::expr(n);
  304. }
  305. R::Datum nil = R::Nil();
  306. R::Array append(R::Array lhs, R::Array rhs) {
  307. if (lhs.empty()) {
  308. return rhs;
  309. }
  310. lhs.reserve(lhs.size() + rhs.size());
  311. std::move(std::begin(rhs), std::end(rhs), std::back_inserter(lhs));
  312. return lhs;
  313. }