TestCsvParser.cpp 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344
  1. /*
  2. * Copyright (C) 2015 Enrico Mariotti <enricomariotti@yahoo.it>
  3. * Copyright (C) 2017 KeePassXC Team <team@keepassxc.org>
  4. *
  5. * This program is free software: you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation, either version 2 or (at your option)
  8. * version 3 of the License.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License
  16. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. #include "TestCsvParser.h"
  19. #include <QTest>
  20. QTEST_GUILESS_MAIN(TestCsvParser)
  21. void TestCsvParser::initTestCase()
  22. {
  23. parser.reset(new CsvParser());
  24. }
  25. void TestCsvParser::init()
  26. {
  27. file.reset(new QTemporaryFile());
  28. if (not file->open()) {
  29. QFAIL("Cannot open file!");
  30. }
  31. parser->setBackslashSyntax(false);
  32. parser->setComment('#');
  33. parser->setFieldSeparator(',');
  34. parser->setTextQualifier(QChar('"'));
  35. }
  36. void TestCsvParser::cleanup()
  37. {
  38. file->remove();
  39. }
  40. /****************** TEST CASES ******************/
  41. void TestCsvParser::testMissingQuote()
  42. {
  43. parser->setTextQualifier(':');
  44. QTextStream out(file.data());
  45. out << "A,B\n:BM,1";
  46. QEXPECT_FAIL("", "Bad format", Continue);
  47. QVERIFY(parser->parse(file.data()));
  48. t = parser->getCsvTable();
  49. QWARN(parser->getStatus().toLatin1());
  50. }
  51. void TestCsvParser::testMalformed()
  52. {
  53. parser->setTextQualifier(':');
  54. QTextStream out(file.data());
  55. out << "A,B,C\n:BM::,1,:2:";
  56. QEXPECT_FAIL("", "Bad format", Continue);
  57. QVERIFY(parser->parse(file.data()));
  58. t = parser->getCsvTable();
  59. QWARN(parser->getStatus().toLatin1());
  60. }
  61. void TestCsvParser::testBackslashSyntax()
  62. {
  63. parser->setBackslashSyntax(true);
  64. parser->setTextQualifier(QChar('X'));
  65. QTextStream out(file.data());
  66. // attended result: one"\t\"wo
  67. out << "Xone\\\"\\\\t\\\\\\\"w\noX\n"
  68. << "X13X,X2\\X,X,\"\"3\"X\r" << "3,X\"4\"X,,\n"
  69. << "XX\n"
  70. << "\\";
  71. QVERIFY(parser->parse(file.data()));
  72. t = parser->getCsvTable();
  73. QVERIFY(t.at(0).at(0) == "one\"\\t\\\"w\no");
  74. QVERIFY(t.at(1).at(0) == "13");
  75. QVERIFY(t.at(1).at(1) == "2X,");
  76. QVERIFY(t.at(1).at(2) == "\"\"3\"X");
  77. QVERIFY(t.at(2).at(0) == "3");
  78. QVERIFY(t.at(2).at(1) == "\"4\"");
  79. QVERIFY(t.at(2).at(2) == "");
  80. QVERIFY(t.at(2).at(3) == "");
  81. QVERIFY(t.at(3).at(0) == "\\");
  82. QVERIFY(t.size() == 4);
  83. }
  84. void TestCsvParser::testQuoted()
  85. {
  86. QTextStream out(file.data());
  87. out << "ro,w,\"end, of \"\"\"\"\"\"row\"\"\"\"\"\n"
  88. << "2\n";
  89. QVERIFY(parser->parse(file.data()));
  90. t = parser->getCsvTable();
  91. QVERIFY(t.at(0).at(0) == "ro");
  92. QVERIFY(t.at(0).at(1) == "w");
  93. QVERIFY(t.at(0).at(2) == "end, of \"\"\"row\"\"");
  94. QVERIFY(t.at(1).at(0) == "2");
  95. QVERIFY(t.size() == 2);
  96. }
  97. void TestCsvParser::testEmptySimple()
  98. {
  99. QTextStream out(file.data());
  100. out << "";
  101. QVERIFY(parser->parse(file.data()));
  102. t = parser->getCsvTable();
  103. QVERIFY(t.isEmpty());
  104. }
  105. void TestCsvParser::testEmptyQuoted()
  106. {
  107. QTextStream out(file.data());
  108. out << "\"\"";
  109. QVERIFY(parser->parse(file.data()));
  110. t = parser->getCsvTable();
  111. QVERIFY(t.isEmpty());
  112. }
  113. void TestCsvParser::testEmptyNewline()
  114. {
  115. QTextStream out(file.data());
  116. out << "\"\n\"";
  117. QVERIFY(parser->parse(file.data()));
  118. t = parser->getCsvTable();
  119. QVERIFY(t.isEmpty());
  120. }
  121. void TestCsvParser::testEmptyFile()
  122. {
  123. QVERIFY(parser->parse(file.data()));
  124. t = parser->getCsvTable();
  125. QVERIFY(t.isEmpty());
  126. }
  127. void TestCsvParser::testNewline()
  128. {
  129. QTextStream out(file.data());
  130. out << "1,2\n\n\n";
  131. QVERIFY(parser->parse(file.data()));
  132. t = parser->getCsvTable();
  133. QVERIFY(t.size() == 1);
  134. QVERIFY(t.at(0).at(0) == "1");
  135. QVERIFY(t.at(0).at(1) == "2");
  136. }
  137. void TestCsvParser::testCR()
  138. {
  139. QTextStream out(file.data());
  140. out << "1,2\r3,4";
  141. QVERIFY(parser->parse(file.data()));
  142. t = parser->getCsvTable();
  143. QVERIFY(t.size() == 2);
  144. QVERIFY(t.at(0).at(0) == "1");
  145. QVERIFY(t.at(0).at(1) == "2");
  146. QVERIFY(t.at(1).at(0) == "3");
  147. QVERIFY(t.at(1).at(1) == "4");
  148. }
  149. void TestCsvParser::testLF()
  150. {
  151. QTextStream out(file.data());
  152. out << "1,2\n3,4";
  153. QVERIFY(parser->parse(file.data()));
  154. t = parser->getCsvTable();
  155. QVERIFY(t.size() == 2);
  156. QVERIFY(t.at(0).at(0) == "1");
  157. QVERIFY(t.at(0).at(1) == "2");
  158. QVERIFY(t.at(1).at(0) == "3");
  159. QVERIFY(t.at(1).at(1) == "4");
  160. }
  161. void TestCsvParser::testCRLF()
  162. {
  163. QTextStream out(file.data());
  164. out << "1,2\r\n3,4";
  165. QVERIFY(parser->parse(file.data()));
  166. t = parser->getCsvTable();
  167. QVERIFY(t.size() == 2);
  168. QVERIFY(t.at(0).at(0) == "1");
  169. QVERIFY(t.at(0).at(1) == "2");
  170. QVERIFY(t.at(1).at(0) == "3");
  171. QVERIFY(t.at(1).at(1) == "4");
  172. }
  173. void TestCsvParser::testComments()
  174. {
  175. QTextStream out(file.data());
  176. out << " #one\n"
  177. << " \t # two, three \r\n"
  178. << " #, sing\t with\r" << " #\t me!\n"
  179. << "useful,text #1!";
  180. QVERIFY(parser->parse(file.data()));
  181. t = parser->getCsvTable();
  182. QVERIFY(t.size() == 1);
  183. QVERIFY(t.at(0).at(0) == "useful");
  184. QVERIFY(t.at(0).at(1) == "text #1!");
  185. }
  186. void TestCsvParser::testColumns()
  187. {
  188. QTextStream out(file.data());
  189. out << "1,2\n"
  190. << ",,,,,,,,,a\n"
  191. << "a,b,c,d\n";
  192. QVERIFY(parser->parse(file.data()));
  193. t = parser->getCsvTable();
  194. QVERIFY(parser->getCsvCols() == 10);
  195. }
  196. void TestCsvParser::testSimple()
  197. {
  198. QTextStream out(file.data());
  199. out << ",,2\r,2,3\n"
  200. << "A,,B\"\n"
  201. << " ,,\n";
  202. QVERIFY(parser->parse(file.data()));
  203. t = parser->getCsvTable();
  204. QVERIFY(t.size() == 4);
  205. QVERIFY(t.at(0).at(0) == "");
  206. QVERIFY(t.at(0).at(1) == "");
  207. QVERIFY(t.at(0).at(2) == "2");
  208. QVERIFY(t.at(1).at(0) == "");
  209. QVERIFY(t.at(1).at(1) == "2");
  210. QVERIFY(t.at(1).at(2) == "3");
  211. QVERIFY(t.at(2).at(0) == "A");
  212. QVERIFY(t.at(2).at(1) == "");
  213. QVERIFY(t.at(2).at(2) == "B\"");
  214. QVERIFY(t.at(3).at(0) == " ");
  215. QVERIFY(t.at(3).at(1) == "");
  216. QVERIFY(t.at(3).at(2) == "");
  217. }
  218. void TestCsvParser::testSeparator()
  219. {
  220. parser->setFieldSeparator('\t');
  221. QTextStream out(file.data());
  222. out << "\t\t2\r\t2\t3\n"
  223. << "A\t\tB\"\n"
  224. << " \t\t\n";
  225. QVERIFY(parser->parse(file.data()));
  226. t = parser->getCsvTable();
  227. QVERIFY(t.size() == 4);
  228. QVERIFY(t.at(0).at(0) == "");
  229. QVERIFY(t.at(0).at(1) == "");
  230. QVERIFY(t.at(0).at(2) == "2");
  231. QVERIFY(t.at(1).at(0) == "");
  232. QVERIFY(t.at(1).at(1) == "2");
  233. QVERIFY(t.at(1).at(2) == "3");
  234. QVERIFY(t.at(2).at(0) == "A");
  235. QVERIFY(t.at(2).at(1) == "");
  236. QVERIFY(t.at(2).at(2) == "B\"");
  237. QVERIFY(t.at(3).at(0) == " ");
  238. QVERIFY(t.at(3).at(1) == "");
  239. QVERIFY(t.at(3).at(2) == "");
  240. }
  241. void TestCsvParser::testMultiline()
  242. {
  243. parser->setTextQualifier(QChar(':'));
  244. QTextStream out(file.data());
  245. out << ":1\r\n2a::b:,:3\r4:\n"
  246. << "2\n";
  247. QVERIFY(parser->parse(file.data()));
  248. t = parser->getCsvTable();
  249. QVERIFY(t.at(0).at(0) == "1\n2a:b");
  250. QVERIFY(t.at(0).at(1) == "3\n4");
  251. QVERIFY(t.at(1).at(0) == "2");
  252. QVERIFY(t.size() == 2);
  253. }
  254. void TestCsvParser::testEmptyReparsing()
  255. {
  256. parser->parse(nullptr);
  257. QVERIFY(parser->reparse());
  258. t = parser->getCsvTable();
  259. QVERIFY(t.isEmpty());
  260. }
  261. void TestCsvParser::testReparsing()
  262. {
  263. QTextStream out(file.data());
  264. out << ":te\r\nxt1:,:te\rxt2:,:end of \"this\n string\":\n"
  265. << "2\n";
  266. QVERIFY(parser->parse(file.data()));
  267. t = parser->getCsvTable();
  268. QEXPECT_FAIL("", "Wrong qualifier", Continue);
  269. QVERIFY(t.at(0).at(0) == "te\nxt1");
  270. parser->setTextQualifier(QChar(':'));
  271. QVERIFY(parser->reparse());
  272. t = parser->getCsvTable();
  273. QVERIFY(t.at(0).at(0) == "te\nxt1");
  274. QVERIFY(t.at(0).at(1) == "te\nxt2");
  275. QVERIFY(t.at(0).at(2) == "end of \"this\n string\"");
  276. QVERIFY(t.at(1).at(0) == "2");
  277. QVERIFY(t.size() == 2);
  278. }
  279. void TestCsvParser::testQualifier()
  280. {
  281. parser->setTextQualifier(QChar('X'));
  282. QTextStream out(file.data());
  283. out << "X1X,X2XX,X,\"\"3\"\"\"X\r" << "3,X\"4\"X,,\n";
  284. QVERIFY(parser->parse(file.data()));
  285. t = parser->getCsvTable();
  286. QVERIFY(t.size() == 2);
  287. QVERIFY(t.at(0).at(0) == "1");
  288. QVERIFY(t.at(0).at(1) == "2X,");
  289. QVERIFY(t.at(0).at(2) == "\"\"3\"\"\"X");
  290. QVERIFY(t.at(1).at(0) == "3");
  291. QVERIFY(t.at(1).at(1) == "\"4\"");
  292. QVERIFY(t.at(1).at(2) == "");
  293. QVERIFY(t.at(1).at(3) == "");
  294. }
  295. void TestCsvParser::testUnicode()
  296. {
  297. // QString m("Texte en fran\u00e7ais");
  298. // CORRECT QString g("\u20AC");
  299. // CORRECT QChar g(0x20AC);
  300. // ERROR QChar g("\u20AC");
  301. parser->setFieldSeparator(QChar('A'));
  302. QTextStream out(file.data());
  303. out.setCodec("UTF-8");
  304. out << QString("€1A2śA\"3śAż\"Ażac");
  305. QVERIFY(parser->parse(file.data()));
  306. t = parser->getCsvTable();
  307. QVERIFY(t.size() == 1);
  308. QVERIFY(t.at(0).at(0) == "€1");
  309. QVERIFY(t.at(0).at(1) == "2ś");
  310. QVERIFY(t.at(0).at(2) == "3śAż");
  311. QVERIFY(t.at(0).at(3) == "żac");
  312. }