test_range.cxx 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555
  1. #include <pqxx/range>
  2. #include <pqxx/strconv>
  3. #include "../test_helpers.hxx"
  4. namespace
  5. {
  6. void test_range_construct()
  7. {
  8. using optint = std::optional<int>;
  9. using oibound = pqxx::inclusive_bound<std::optional<int>>;
  10. using oxbound = pqxx::inclusive_bound<std::optional<int>>;
  11. PQXX_CHECK_THROWS(
  12. (pqxx::range<optint>{oibound{optint{}}, oibound{optint{}}}),
  13. pqxx::argument_error, "Inclusive bound accepted a null.");
  14. PQXX_CHECK_THROWS(
  15. (pqxx::range<optint>{oxbound{optint{}}, oxbound{optint{}}}),
  16. pqxx::argument_error, "Exclusive bound accepted a null.");
  17. using ibound = pqxx::inclusive_bound<int>;
  18. PQXX_CHECK_THROWS(
  19. (pqxx::range<int>{ibound{1}, ibound{0}}), pqxx::range_error,
  20. "Range constructor accepted backwards range.");
  21. PQXX_CHECK_THROWS(
  22. (pqxx::range<float>{
  23. pqxx::inclusive_bound<float>{-1000.0},
  24. pqxx::inclusive_bound<float>{-std::numeric_limits<float>::infinity()}}),
  25. pqxx::range_error,
  26. "Was able to construct range with infinity bound at the wrong end.");
  27. }
  28. void test_range_equality()
  29. {
  30. using range = pqxx::range<int>;
  31. using ibound = pqxx::inclusive_bound<int>;
  32. using xbound = pqxx::exclusive_bound<int>;
  33. using ubound = pqxx::no_bound;
  34. PQXX_CHECK_EQUAL(
  35. range{}, range{}, "Default-constructed range is not consistent.");
  36. PQXX_CHECK_EQUAL(
  37. (range{xbound{0}, xbound{0}}), (range{xbound{5}, xbound{5}}),
  38. "Empty ranges at different values are not equal.");
  39. PQXX_CHECK_EQUAL(
  40. (range{ubound{}, ubound{}}), (range{ubound{}, ubound{}}),
  41. "Universal range is inconsistent.");
  42. PQXX_CHECK_EQUAL(
  43. (range{ibound{5}, ibound{8}}), (range{ibound{5}, ibound{8}}),
  44. "Inclusive range is inconsistent.");
  45. PQXX_CHECK_EQUAL(
  46. (range{xbound{5}, xbound{8}}), (range{xbound{5}, xbound{8}}),
  47. "Exclusive range is inconsistent.");
  48. PQXX_CHECK_EQUAL(
  49. (range{xbound{5}, ibound{8}}), (range{xbound{5}, ibound{8}}),
  50. "Left-exclusive interval is not equal to itself.");
  51. PQXX_CHECK_EQUAL(
  52. (range{ibound{5}, xbound{8}}), (range{ibound{5}, xbound{8}}),
  53. "Right-exclusive interval is not equal to itself.");
  54. PQXX_CHECK_EQUAL(
  55. (range{ubound{}, ibound{8}}), (range{ubound{}, ibound{8}}),
  56. "Unlimited lower bound does not compare equal to same.");
  57. PQXX_CHECK_EQUAL(
  58. (range{ibound{8}, ubound{}}), (range{ibound{8}, ubound{}}),
  59. "Unlimited upper bound does not compare equal to same.");
  60. PQXX_CHECK_NOT_EQUAL(
  61. (range{ibound{5}, ibound{8}}), (range{xbound{5}, ibound{8}}),
  62. "Equality does not detect inclusive vs. exclusive lower bound.");
  63. PQXX_CHECK_NOT_EQUAL(
  64. (range{ibound{5}, ibound{8}}), (range{ubound{}, ibound{8}}),
  65. "Equality does not detect inclusive vs. unlimited lower bound.");
  66. PQXX_CHECK_NOT_EQUAL(
  67. (range{xbound{5}, ibound{8}}), (range{ubound{}, ibound{8}}),
  68. "Equality does not detect exclusive vs. unlimited lower bound.");
  69. PQXX_CHECK_NOT_EQUAL(
  70. (range{ibound{5}, ibound{8}}), (range{ibound{5}, xbound{8}}),
  71. "Equality does not detect inclusive vs. exclusive upper bound.");
  72. PQXX_CHECK_NOT_EQUAL(
  73. (range{ibound{5}, ibound{8}}), (range{ibound{5}, ubound{}}),
  74. "Equality does not detect inclusive vs. unlimited upper bound.");
  75. PQXX_CHECK_NOT_EQUAL(
  76. (range{ibound{5}, xbound{8}}), (range{ibound{5}, ubound{}}),
  77. "Equality does not detect exclusive vs. unlimited upper bound.");
  78. PQXX_CHECK_NOT_EQUAL(
  79. (range{ibound{5}, ibound{8}}), (range{ibound{4}, ibound{8}}),
  80. "Equality does not compare lower inclusive bound value.");
  81. PQXX_CHECK_NOT_EQUAL(
  82. (range{xbound{5}, ibound{8}}), (range{xbound{4}, ibound{8}}),
  83. "Equality does not compare lower exclusive bound value.");
  84. PQXX_CHECK_NOT_EQUAL(
  85. (range{xbound{5}, ibound{8}}), (range{xbound{5}, ibound{7}}),
  86. "Equality does not compare upper inclusive bound value.");
  87. PQXX_CHECK_NOT_EQUAL(
  88. (range{xbound{5}, xbound{8}}), (range{xbound{5}, xbound{7}}),
  89. "Equality does not compare lower exclusive bound value.");
  90. }
  91. void test_range_empty()
  92. {
  93. using range = pqxx::range<int>;
  94. using ibound = pqxx::inclusive_bound<int>;
  95. using xbound = pqxx::exclusive_bound<int>;
  96. using ubound = pqxx::no_bound;
  97. PQXX_CHECK((range{}.empty()), "Default-constructed range is not empty.");
  98. PQXX_CHECK(
  99. (range{ibound{10}, xbound{10}}).empty(),
  100. "Right-exclusive zero-length interval is not empty.");
  101. PQXX_CHECK(
  102. (range{xbound{10}, ibound{10}}).empty(),
  103. "Left-exclusive zero-length interval is not empty.");
  104. PQXX_CHECK(
  105. (range{xbound{10}, xbound{10}}).empty(),
  106. "Exclusive zero-length interval is not empty.");
  107. PQXX_CHECK(
  108. not(range{ibound{10}, ibound{10}}).empty(),
  109. "Inclusive zero-length interval is empty.");
  110. PQXX_CHECK(
  111. not(range{xbound{10}, ibound{11}}.empty()),
  112. "Interval is incorrectly empty.");
  113. PQXX_CHECK(
  114. not(range{ubound{}, ubound{}}.empty()),
  115. "Double-unlimited interval is empty.");
  116. PQXX_CHECK(
  117. not(range{ubound{}, xbound{0}}.empty()),
  118. "Left-unlimited interval is empty.");
  119. PQXX_CHECK(
  120. not(range{xbound{0}, ubound{}}.empty()),
  121. "Right-unlimited interval is empty.");
  122. }
  123. void test_range_contains()
  124. {
  125. using range = pqxx::range<int>;
  126. using ibound = pqxx::inclusive_bound<int>;
  127. using xbound = pqxx::exclusive_bound<int>;
  128. using ubound = pqxx::no_bound;
  129. PQXX_CHECK(not(range{}.contains(-1)), "Empty range contains a value.");
  130. PQXX_CHECK(not(range{}.contains(0)), "Empty range contains a value.");
  131. PQXX_CHECK(not(range{}.contains(1)), "Empty range contains a value.");
  132. PQXX_CHECK(
  133. not(range{ibound{5}, ibound{8}}.contains(4)),
  134. "Inclusive range contains value outside its left bound.");
  135. PQXX_CHECK(
  136. (range{ibound{5}, ibound{8}}.contains(5)),
  137. "Inclusive range does not contain value on its left bound.");
  138. PQXX_CHECK(
  139. (range{ibound{5}, ibound{8}}.contains(6)),
  140. "Inclusive range does not contain value inside it.");
  141. PQXX_CHECK(
  142. (range{ibound{5}, ibound{8}}.contains(8)),
  143. "Inclusive range does not contain value on its right bound.");
  144. PQXX_CHECK(
  145. not(range{ibound{5}, ibound{8}}.contains(9)),
  146. "Inclusive range contains value outside its right bound.");
  147. PQXX_CHECK(
  148. not(range{ibound{5}, xbound{8}}.contains(4)),
  149. "Left-inclusive range contains value outside its left bound.");
  150. PQXX_CHECK(
  151. (range{ibound{5}, xbound{8}}.contains(5)),
  152. "Left-inclusive range does not contain value on its left bound.");
  153. PQXX_CHECK(
  154. (range{ibound{5}, xbound{8}}.contains(6)),
  155. "Left-inclusive range does not contain value inside it.");
  156. PQXX_CHECK(
  157. not(range{ibound{5}, xbound{8}}.contains(8)),
  158. "Left-inclusive range contains value on its right bound.");
  159. PQXX_CHECK(
  160. not(range{ibound{5}, xbound{8}}.contains(9)),
  161. "Left-inclusive range contains value outside its right bound.");
  162. PQXX_CHECK(
  163. not(range{xbound{5}, ibound{8}}.contains(4)),
  164. "Right-inclusive range contains value outside its left bound.");
  165. PQXX_CHECK(
  166. not(range{xbound{5}, ibound{8}}.contains(5)),
  167. "Right-inclusive range does contains value on its left bound.");
  168. PQXX_CHECK(
  169. (range{xbound{5}, ibound{8}}.contains(6)),
  170. "Right-inclusive range does not contain value inside it.");
  171. PQXX_CHECK(
  172. (range{xbound{5}, ibound{8}}.contains(8)),
  173. "Right-inclusive range does not contain value on its right bound.");
  174. PQXX_CHECK(
  175. not(range{xbound{5}, ibound{8}}.contains(9)),
  176. "Right-inclusive range contains value outside its right bound.");
  177. PQXX_CHECK(
  178. not(range{xbound{5}, xbound{8}}.contains(4)),
  179. "Exclusive range contains value outside its left bound.");
  180. PQXX_CHECK(
  181. not(range{xbound{5}, xbound{8}}.contains(5)),
  182. "Exclusive range contains value on its left bound.");
  183. PQXX_CHECK(
  184. (range{xbound{5}, xbound{8}}.contains(6)),
  185. "Exclusive range does not contain value inside it.");
  186. PQXX_CHECK(
  187. not(range{xbound{5}, xbound{8}}.contains(8)),
  188. "Exclusive range does contains value on its right bound.");
  189. PQXX_CHECK(
  190. not(range{xbound{5}, xbound{8}}.contains(9)),
  191. "Exclusive range contains value outside its right bound.");
  192. PQXX_CHECK(
  193. (range{ubound{}, ibound{8}}.contains(7)),
  194. "Right-inclusive range does not contain value inside it.");
  195. PQXX_CHECK(
  196. (range{ubound{}, ibound{8}}.contains(8)),
  197. "Right-inclusive range does not contain value on its right bound.");
  198. PQXX_CHECK(
  199. not(range{ubound{}, ibound{8}}.contains(9)),
  200. "Right-inclusive range contains value outside its right bound.");
  201. PQXX_CHECK(
  202. (range{ubound{}, xbound{8}}.contains(7)),
  203. "Right-exclusive range does not contain value inside it.");
  204. PQXX_CHECK(
  205. not(range{ubound{}, xbound{8}}.contains(8)),
  206. "Right-exclusive range contains value on its right bound.");
  207. PQXX_CHECK(
  208. not(range{ubound{}, xbound{8}}.contains(9)),
  209. "Right-exclusive range contains value outside its right bound.");
  210. PQXX_CHECK(
  211. not(range{ibound{5}, ubound{}}.contains(4)),
  212. "Left-inclusive range contains value outside its left bound.");
  213. PQXX_CHECK(
  214. (range{ibound{5}, ubound{}}.contains(5)),
  215. "Left-inclusive range does not contain value on its left bound.");
  216. PQXX_CHECK(
  217. (range{ibound{5}, ubound{}}.contains(6)),
  218. "Left-inclusive range does not contain value inside it.");
  219. PQXX_CHECK(
  220. not(range{xbound{5}, ubound{}}.contains(4)),
  221. "Left-exclusive range contains value outside its left bound.");
  222. PQXX_CHECK(
  223. not(range{xbound{5}, ubound{}}.contains(5)),
  224. "Left-exclusive range contains value on its left bound.");
  225. PQXX_CHECK(
  226. (range{xbound{5}, ubound{}}.contains(6)),
  227. "Left-exclusive range does not contain value inside it.");
  228. PQXX_CHECK(
  229. (range{ubound{}, ubound{}}.contains(-1)), "Value not in universal range.");
  230. PQXX_CHECK(
  231. (range{ubound{}, ubound{}}.contains(0)), "Value not in universal range.");
  232. PQXX_CHECK(
  233. (range{ubound{}, ubound{}}.contains(1)), "Value not in universal range.");
  234. }
  235. void test_float_range_contains()
  236. {
  237. using range = pqxx::range<double>;
  238. using ibound = pqxx::inclusive_bound<double>;
  239. using xbound = pqxx::exclusive_bound<double>;
  240. using ubound = pqxx::no_bound;
  241. using limits = std::numeric_limits<double>;
  242. constexpr auto inf{limits::infinity()};
  243. PQXX_CHECK(
  244. not(range{ibound{4.0}, ibound{8.0}}.contains(3.9)),
  245. "Float inclusive range contains value beyond its lower bound.");
  246. PQXX_CHECK(
  247. (range{ibound{4.0}, ibound{8.0}}.contains(4.0)),
  248. "Float inclusive range does not contain its lower bound value.");
  249. PQXX_CHECK(
  250. (range{ibound{4.0}, ibound{8.0}}.contains(5.0)),
  251. "Float inclusive range does not contain value inside it.");
  252. PQXX_CHECK(
  253. (range{ibound{0}, ibound{inf}}).contains(9999.0),
  254. "Range to infinity did not include large number.");
  255. PQXX_CHECK(
  256. not(range{ibound{0}, ibound{inf}}.contains(-0.1)),
  257. "Range to infinity includes number outside it.");
  258. PQXX_CHECK(
  259. (range{ibound{0}, xbound{inf}}.contains(9999.0)),
  260. "Range to exclusive infinity did not include large number.");
  261. PQXX_CHECK(
  262. (range{ibound{0}, ibound{inf}}).contains(inf),
  263. "Range to inclusive infinity does not include infinity.");
  264. PQXX_CHECK(
  265. not(range{ibound{0}, xbound{inf}}.contains(inf)),
  266. "Range to exclusive infinity includes infinity.");
  267. PQXX_CHECK(
  268. (range{ibound{0}, ubound{}}).contains(inf),
  269. "Right-unlimited range does not include infinity.");
  270. PQXX_CHECK(
  271. (range{ibound{-inf}, ibound{0}}).contains(-9999.0),
  272. "Range from infinity did not include large negative number.");
  273. PQXX_CHECK(
  274. not(range{ibound{-inf}, ibound{0}}.contains(0.1)),
  275. "Range from infinity includes number outside it.");
  276. PQXX_CHECK(
  277. (range{xbound{-inf}, ibound{0}}).contains(-9999.0),
  278. "Range from exclusive infinity did not include large negative number.");
  279. PQXX_CHECK(
  280. (range{ibound{-inf}, ibound{0}}).contains(-inf),
  281. "Range from inclusive infinity does not include negative infinity.");
  282. PQXX_CHECK(
  283. not(range{xbound{-inf}, ibound{0}}).contains(-inf),
  284. "Range to infinity exclusive includes negative infinity.");
  285. PQXX_CHECK(
  286. (range{ubound{}, ibound{0}}).contains(-inf),
  287. "Left-unlimited range does not include negative infinity.");
  288. }
  289. void test_range_subset()
  290. {
  291. using range = pqxx::range<int>;
  292. using traits = pqxx::string_traits<range>;
  293. std::string_view subsets[][2]{
  294. {"empty", "empty"}, {"(,)", "empty"}, {"(0,1)", "empty"},
  295. {"(,)", "[-10,10]"}, {"(,)", "(-10,10)"}, {"(,)", "(,)"},
  296. {"(,10)", "(,10)"}, {"(,10)", "(,9)"}, {"(,10]", "(,10)"},
  297. {"(,10]", "(,10]"}, {"(1,)", "(10,)"}, {"(1,)", "(9,)"},
  298. {"[1,)", "(10,)"}, {"[1,)", "[10,)"}, {"[0,5]", "[1,4]"},
  299. {"(0,5)", "[1,4]"},
  300. };
  301. for (auto const [super, sub] : subsets)
  302. PQXX_CHECK(
  303. traits::from_string(super).contains(traits::from_string(sub)),
  304. pqxx::internal::concat(
  305. "Range '", super, "' did not contain '", sub, "'."));
  306. std::string_view non_subsets[][2]{
  307. {"empty", "[0,0]"}, {"empty", "(,)"}, {"[-10,10]", "(,)"},
  308. {"(-10,10)", "(,)"}, {"(,9)", "(,10)"}, {"(,10)", "(,10]"},
  309. {"[1,4]", "[0,4]"}, {"[1,4]", "[1,5]"}, {"(0,10)", "[0,10]"},
  310. {"(0,10)", "(0,10]"}, {"(0,10)", "[0,10)"},
  311. };
  312. for (auto const [super, sub] : non_subsets)
  313. PQXX_CHECK(
  314. not traits::from_string(super).contains(traits::from_string(sub)),
  315. pqxx::internal::concat("Range '", super, "' contained '", sub, "'."));
  316. }
  317. void test_range_to_string()
  318. {
  319. using range = pqxx::range<int>;
  320. using ibound = pqxx::inclusive_bound<int>;
  321. using xbound = pqxx::exclusive_bound<int>;
  322. using ubound = pqxx::no_bound;
  323. PQXX_CHECK_EQUAL(
  324. pqxx::to_string(range{}), "empty", "Empty range came out wrong.");
  325. PQXX_CHECK_EQUAL(
  326. pqxx::to_string(range{ibound{5}, ibound{8}}), "[5,8]",
  327. "Inclusive range came out wrong.");
  328. PQXX_CHECK_EQUAL(
  329. pqxx::to_string(range{xbound{5}, ibound{8}}), "(5,8]",
  330. "Left-exclusive range came out wrong.");
  331. PQXX_CHECK_EQUAL(
  332. pqxx::to_string(range{ibound{5}, xbound{8}}), "[5,8)",
  333. "Right-exclusive range came out wrong.");
  334. PQXX_CHECK_EQUAL(
  335. pqxx::to_string(range{xbound{5}, xbound{8}}), "(5,8)",
  336. "Exclusive range came out wrong.");
  337. // Unlimited boundaries can use brackets or parentheses. Doesn't matter.
  338. // We cheat and use some white-box knowledge of our implementation here.
  339. PQXX_CHECK_EQUAL(
  340. pqxx::to_string(range{ubound{}, ubound{}}), "(,)",
  341. "Universal range came out unexpected.");
  342. PQXX_CHECK_EQUAL(
  343. pqxx::to_string(range{ubound{}, ibound{8}}), "(,8]",
  344. "Left-unlimited range came out unexpected.");
  345. PQXX_CHECK_EQUAL(
  346. pqxx::to_string(range{ubound{}, xbound{8}}), "(,8)",
  347. "Left-unlimited range came out unexpected.");
  348. PQXX_CHECK_EQUAL(
  349. pqxx::to_string(range{ibound{5}, ubound{}}), "[5,)",
  350. "Right-unlimited range came out unexpected.");
  351. PQXX_CHECK_EQUAL(
  352. pqxx::to_string(range{xbound{5}, ubound{}}), "(5,)",
  353. "Right-unlimited range came out unexpected.");
  354. }
  355. void test_parse_range()
  356. {
  357. using range = pqxx::range<int>;
  358. using ubound = pqxx::no_bound;
  359. using traits = pqxx::string_traits<range>;
  360. constexpr std::string_view empties[]{"empty", "EMPTY", "eMpTy"};
  361. for (auto empty : empties)
  362. PQXX_CHECK(
  363. traits::from_string(empty).empty(),
  364. pqxx::internal::concat(
  365. "This was supposed to produce an empty range: '", empty, "'"));
  366. constexpr std::string_view universals[]{"(,)", "[,)", "(,]", "[,]"};
  367. for (auto univ : universals)
  368. PQXX_CHECK_EQUAL(
  369. traits::from_string(univ), (range{ubound{}, ubound{}}),
  370. pqxx::internal::concat(
  371. "This was supposed to produce a universal range: '", univ, "'"));
  372. PQXX_CHECK(
  373. traits::from_string("(0,10]").lower_bound().is_exclusive(),
  374. "Exclusive lower bound did not parse right.");
  375. PQXX_CHECK(
  376. traits::from_string("[0,10]").lower_bound().is_inclusive(),
  377. "Inclusive lower bound did not parse right.");
  378. PQXX_CHECK(
  379. traits::from_string("(0,10)").upper_bound().is_exclusive(),
  380. "Exclusive upper bound did not parse right.");
  381. PQXX_CHECK(
  382. traits::from_string("[0,10]").upper_bound().is_inclusive(),
  383. "Inclusive upper bound did not parse right.");
  384. PQXX_CHECK_EQUAL(
  385. *traits::from_string("(\"0\",\"10\")").lower_bound().value(), 0,
  386. "Quoted range boundary did not parse right.");
  387. PQXX_CHECK_EQUAL(
  388. *traits::from_string("(\"0\",\"10\")").upper_bound().value(), 10,
  389. "Quoted upper boundary did not parse right.");
  390. auto floats{
  391. pqxx::string_traits<pqxx::range<double>>::from_string("(0,1.0)")};
  392. PQXX_CHECK_GREATER(
  393. *floats.lower_bound().value(), -0.001,
  394. "Float lower bound is out of range.");
  395. PQXX_CHECK_LESS(
  396. *floats.lower_bound().value(), 0.001,
  397. "Float lower bound is out of range.");
  398. PQXX_CHECK_GREATER(
  399. *floats.upper_bound().value(), 0.999,
  400. "Float upper bound is out of range.");
  401. PQXX_CHECK_LESS(
  402. *floats.upper_bound().value(), 1.001,
  403. "Float upper bound is out of range.");
  404. }
  405. void test_parse_bad_range()
  406. {
  407. using range = pqxx::range<int>;
  408. using conv_err = pqxx::conversion_error;
  409. using traits = pqxx::string_traits<range>;
  410. constexpr std::string_view bad_ranges[]{
  411. "", "x", "e", "empt", "emptyy", "()",
  412. "[]", "(empty)", "(empty, 0)", "(0, empty)", ",", "(,",
  413. ",)", "(1,2,3)", "(4,5x)", "(null, 0)", "[0, 1.0]", "[1.0, 0]",
  414. };
  415. for (auto bad : bad_ranges)
  416. PQXX_CHECK_THROWS(
  417. pqxx::ignore_unused(traits::from_string(bad)), conv_err,
  418. pqxx::internal::concat(
  419. "This range wasn't supposed to parse: '", bad, "'"));
  420. }
  421. /// Parse ranges lhs and rhs, return their intersection as a string.
  422. template<typename TYPE>
  423. std::string intersect(std::string_view lhs, std::string_view rhs)
  424. {
  425. using traits = pqxx::string_traits<pqxx::range<TYPE>>;
  426. return pqxx::to_string(traits::from_string(lhs) & traits::from_string(rhs));
  427. }
  428. void test_range_intersection()
  429. {
  430. // Intersections and their expected results, in text form.
  431. // Each row contains two ranges, and their intersection.
  432. std::string_view intersections[][3]{
  433. {"empty", "empty", "empty"},
  434. {"(,)", "empty", "empty"},
  435. {"[,]", "empty", "empty"},
  436. {"empty", "[0,10]", "empty"},
  437. {"(,)", "(,)", "(,)"},
  438. {"(,)", "(5,8)", "(5,8)"},
  439. {"(,)", "[5,8)", "[5,8)"},
  440. {"(,)", "(5,8]", "(5,8]"},
  441. {"(,)", "[5,8]", "[5,8]"},
  442. {"(-1000,10)", "(0,1000)", "(0,10)"},
  443. {"[-1000,10)", "(0,1000)", "(0,10)"},
  444. {"(-1000,10]", "(0,1000)", "(0,10]"},
  445. {"[-1000,10]", "(0,1000)", "(0,10]"},
  446. {"[0,100]", "[0,100]", "[0,100]"},
  447. {"[0,100]", "[0,100)", "[0,100)"},
  448. {"[0,100]", "(0,100]", "(0,100]"},
  449. {"[0,100]", "(0,100)", "(0,100)"},
  450. {"[0,10]", "[11,20]", "empty"},
  451. {"[0,10]", "(11,20]", "empty"},
  452. {"[0,10]", "[11,20)", "empty"},
  453. {"[0,10]", "(11,20)", "empty"},
  454. {"[0,10]", "[10,11]", "[10,10]"},
  455. {"[0,10)", "[10,11]", "empty"},
  456. {"[0,10]", "(10,11]", "empty"},
  457. {"[0,10)", "(10,11]", "empty"},
  458. };
  459. for (auto [left, right, expected] : intersections)
  460. {
  461. PQXX_CHECK_EQUAL(
  462. intersect<int>(left, right), expected,
  463. pqxx::internal::concat(
  464. "Intersection of '", left, "' and '", right,
  465. " produced unexpected result."));
  466. PQXX_CHECK_EQUAL(
  467. intersect<int>(right, left), expected,
  468. pqxx::internal::concat(
  469. "Intersection of '", left, "' and '", right, " was asymmetric."));
  470. }
  471. }
  472. void test_range_conversion()
  473. {
  474. std::string_view const ranges[]{
  475. "empty", "(,)", "(,10)", "(0,)", "[0,10]", "[0,10)", "(0,10]", "(0,10)",
  476. };
  477. for (auto r : ranges)
  478. {
  479. auto const shortr{pqxx::from_string<pqxx::range<short>>(r)};
  480. pqxx::range<int> intr{shortr};
  481. PQXX_CHECK_EQUAL(
  482. pqxx::to_string(intr), r, "Converted range looks different.");
  483. }
  484. }
  485. PQXX_REGISTER_TEST(test_range_construct);
  486. PQXX_REGISTER_TEST(test_range_equality);
  487. PQXX_REGISTER_TEST(test_range_empty);
  488. PQXX_REGISTER_TEST(test_range_contains);
  489. PQXX_REGISTER_TEST(test_float_range_contains);
  490. PQXX_REGISTER_TEST(test_range_subset);
  491. PQXX_REGISTER_TEST(test_range_to_string);
  492. PQXX_REGISTER_TEST(test_parse_range);
  493. PQXX_REGISTER_TEST(test_parse_bad_range);
  494. PQXX_REGISTER_TEST(test_range_intersection);
  495. PQXX_REGISTER_TEST(test_range_conversion);
  496. } // namespace