tcsdfdata.pp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492
  1. unit tcsdfdata;
  2. // Tests specific functionality of sdfdataset (multiline etc)
  3. {$mode objfpc}{$H+}
  4. interface
  5. uses
  6. Classes, SysUtils, Fpcunit, Testutils, Testregistry, testdecorator,
  7. dateutils,sdfdata,ToolsUnit;
  8. type
  9. { Ttestsdfspecific }
  10. Ttestsdfspecific = class(Ttestcase)
  11. private
  12. procedure TestEmptyFieldContents;
  13. protected
  14. TestDataset: TSDFDataset;
  15. procedure Setup; override;
  16. procedure Teardown; override;
  17. published
  18. procedure TestEmptyFileHeader;
  19. procedure TestEmptyFileNoHeader;
  20. procedure TestSingleLineHeader;
  21. procedure TestSingleLineNoHeader;
  22. procedure TestOutput;
  23. {
  24. November 2012: this test tests again sdf;
  25. however sdfdataset should comply with RFC4180 CSV, see issue #22980
  26. todo: rewrite test to RFC4180
  27. procedure TestInputOurFormat;
  28. }
  29. procedure TestDelimitedTextOutput;
  30. procedure TestEmptyHeader;
  31. Procedure TestEmptyHeader2;
  32. Procedure TestEmptyHeaderStripTrailingDelimiters;
  33. Procedure TestStripTrailingDelimiters;
  34. end;
  35. implementation
  36. procedure Ttestsdfspecific.TestEmptyFileHeader;
  37. // An empty file should return 0 records even if it has a header
  38. const
  39. InputFilename='empty.csv';
  40. begin
  41. TestDataSet.Close;
  42. if FileExists(InputFilename) then DeleteFile(InputFilename);
  43. TestDataset.FileMustExist:=false;
  44. TestDataset.FirstLineAsSchema := True;
  45. TestDataset.FileName:=InputFilename;
  46. TestDataset.Open;
  47. TestDataset.Last;
  48. TestDataset.First;
  49. AssertEquals('Number of records in test dataset', 0, TestDataset.RecordCount);
  50. TestDataset.Close;
  51. end;
  52. procedure Ttestsdfspecific.TestEmptyFileNoHeader;
  53. // An empty file should return 0 records even if it has a header
  54. const
  55. InputFilename='empty.csv';
  56. begin
  57. TestDataSet.Close;
  58. if FileExists(InputFilename) then DeleteFile(InputFilename);
  59. TestDataset.FileMustExist:=false;
  60. TestDataset.FirstLineAsSchema := false;
  61. TestDataset.FileName:=InputFilename;
  62. TestDataset.Open;
  63. TestDataset.Last;
  64. TestDataset.First;
  65. AssertEquals('Number of records in test dataset', 0, TestDataset.RecordCount);
  66. TestDataset.Close;
  67. end;
  68. procedure Ttestsdfspecific.TestSingleLineHeader;
  69. // A file with a single data line and header should return 1 records
  70. const
  71. InputFilename='singleh.csv';
  72. var
  73. FileStrings: TStringList;
  74. begin
  75. TestDataSet.Close;
  76. if FileExists(InputFilename) then DeleteFile(InputFilename);
  77. FileStrings:=TStringList.Create;
  78. try
  79. FileStrings.Add('ID,NAME,BIRTHDAY');
  80. FileStrings.Add('1,SimpleName,31-12-1976');
  81. FileStrings.SaveToFile(InputFileName);
  82. finally
  83. FileStrings.Free;
  84. end;
  85. TestDataset.FileMustExist:=false;
  86. TestDataset.FirstLineAsSchema := True;
  87. TestDataset.FileName:=InputFilename;
  88. TestDataset.Open;
  89. TestDataset.Last;
  90. TestDataset.First;
  91. AssertEquals('RecNo', 1, TestDataset.RecNo);
  92. AssertEquals('RecordCount', 1, TestDataset.RecordCount);
  93. TestDataset.Close;
  94. end;
  95. procedure Ttestsdfspecific.TestSingleLineNoHeader;
  96. // A file with a single data line, no header should return 1 records
  97. const
  98. InputFilename='single.csv';
  99. var
  100. FileStrings: TStringList;
  101. begin
  102. TestDataSet.Close;
  103. if FileExists(InputFilename) then DeleteFile(InputFilename);
  104. FileStrings:=TStringList.Create;
  105. try
  106. FileStrings.Add('1,SimpleName,31-12-1976');
  107. FileStrings.SaveToFile(InputFileName);
  108. finally
  109. FileStrings.Free;
  110. end;
  111. TestDataset.FileMustExist:=false;
  112. TestDataset.FirstLineAsSchema := False;
  113. TestDataset.FileName:=InputFilename;
  114. TestDataset.Open;
  115. TestDataset.Last;
  116. TestDataset.First;
  117. AssertEquals('RecNo', 1, TestDataset.RecNo);
  118. AssertEquals('RecordCount', 1, TestDataset.RecordCount);
  119. TestDataset.Close;
  120. end;
  121. procedure Ttestsdfspecific.TestOutput;
  122. // Basic assignment test: assign some difficult data to records and
  123. // see if the recordcount is correct.
  124. const
  125. OutputFilename='output.csv';
  126. begin
  127. TestDataSet.Close;
  128. if FileExists(OutputFilename) then DeleteFile(OutputFileName);
  129. TestDataset.FileName:=OutputFileName;
  130. TestDataset.Open;
  131. // Fill test data
  132. TestDataset.Append;
  133. TestDataset.FieldByName('ID').AsInteger := 1;
  134. // Data with quotes
  135. TestDataset.FieldByName('NAME').AsString := 'J"T"';
  136. TestDataset.FieldByName('BIRTHDAY').AsDateTime := ScanDateTime('yyyymmdd', '19761231', 1);
  137. TestDataset.Post;
  138. TestDataset.Append;
  139. TestDataset.FieldByName('ID').AsInteger := 2;
  140. // Data with delimiter
  141. TestDataset.FieldByName('NAME').AsString := 'Hello'+TestDataset.Delimiter+' goodbye';
  142. TestDataset.FieldByName('BIRTHDAY').AsDateTime := ScanDateTime('yyyymmdd', '19761231', 1);
  143. TestDataset.Post;
  144. TestDataset.Append;
  145. TestDataset.FieldByName('ID').AsInteger := 3;
  146. //Data with delimiter and quote (to test 19376)
  147. TestDataset.FieldByName('NAME').AsString := 'Delimiter,"and";quote';
  148. TestDataset.FieldByName('BIRTHDAY').AsDateTime := ScanDateTime('yyyymmdd', '19761231', 1);
  149. TestDataset.Post;
  150. TestDataset.Append;
  151. TestDataset.FieldByName('ID').AsInteger := 4;
  152. // Regular data
  153. TestDataset.FieldByName('NAME').AsString := 'Just a long line of text without anything special';
  154. TestDataset.FieldByName('BIRTHDAY').AsDateTime := ScanDateTime('yyyymmdd', '19761231', 1);
  155. TestDataset.Post;
  156. TestDataset.Last;
  157. AssertEquals('RecNo', 4, TestDataset.RecNo);
  158. TestDataset.RecNo := 2;
  159. AssertEquals('RecNo', 2, TestDataset.RecNo);
  160. AssertEquals(2, TestDataset.FieldByName('ID').AsInteger);
  161. AssertEquals('RecordCount', 4, TestDataset.RecordCount);
  162. TestDataset.Close;
  163. end;
  164. {
  165. procedure Ttestsdfspecific.TestInputOurFormat;
  166. // Test if input works as expected: output is written according to specs and read in.
  167. // Mainly check if reading quotes is according to Delphi sdf specs and works.
  168. // See test results from bug 19610 for evidence that the strings below should work.
  169. // If this works, we can switch to this and be RFC 4180 compliant and Delphi compliant.
  170. const
  171. OutputFileName='input.csv';
  172. //Value1 is the on disk format; it should translate to Expected1
  173. Value1='"Delimiter,""and"";quote"';
  174. Expected1='Delimiter,"and";quote';
  175. Value2='"J""T"""';
  176. Expected2='J"T"';
  177. Value3='Just a long line';
  178. Expected3='Just a long line';
  179. //Note: Delphi can read this, see evidence in bug 19610 (the "quoted and space" value)
  180. Value4='"Just a quoted long line"';
  181. Expected4='Just a quoted long line';
  182. // Delphi can read multiline, see evidence in bug 19610 (the multiline entry)
  183. Value5='"quoted_multi'+#13+#10+'line"';
  184. Expected5='quoted_multi'+#13+#10+'line';
  185. Value6='"Delimiter,and;quoted"';
  186. Expected6='Delimiter,and;quoted';
  187. Value7='"A random""quote"';
  188. Expected7='A random"quote';
  189. var
  190. FileStrings: TStringList;
  191. begin
  192. TestDataset.Close;
  193. TestDataset.AllowMultiLine:=true;
  194. if FileExists(OutputFilename) then DeleteFile(OutputFileName);
  195. FileStrings:=TStringList.Create;
  196. try
  197. FileStrings.Add('ID,NAME,BIRTHDAY');
  198. FileStrings.Add('1,'+Value1+',31-12-1976');
  199. FileStrings.Add('2,'+Value2+',31-12-1976');
  200. FileStrings.Add('3,'+Value3+',31-12-1976');
  201. FileStrings.Add('4,'+Value4+',31-12-1976');
  202. FileStrings.Add('5,'+Value5+',31-12-1976');
  203. FileStrings.Add('6,'+Value6+',31-12-1976');
  204. FileStrings.Add('7,'+Value7+',31-12-1976');
  205. FileStrings.SaveToFile(OutputFileName);
  206. finally
  207. FileStrings.Free;
  208. end;
  209. // Load our dataset
  210. TestDataset.FileName:=OutputFileName;
  211. TestDataset.Open;
  212. TestDataset.First;
  213. AssertEquals(Expected1, TestDataSet.FieldByName('NAME').AsString);
  214. TestDataSet.Next;
  215. AssertEquals(Expected2, TestDataSet.FieldByName('NAME').AsString);
  216. TestDataSet.Next;
  217. AssertEquals(Expected3, TestDataSet.FieldByName('NAME').AsString);
  218. TestDataSet.Next;
  219. AssertEquals(Expected4, TestDataSet.FieldByName('NAME').AsString);
  220. TestDataSet.Next;
  221. AssertEquals(Expected5, TestDataSet.FieldByName('NAME').AsString);
  222. TestDataSet.Next;
  223. AssertEquals(Expected6, TestDataSet.FieldByName('NAME').AsString);
  224. TestDataSet.Next;
  225. AssertEquals(Expected7, TestDataSet.FieldByName('NAME').AsString);
  226. end;
  227. }
  228. procedure Ttestsdfspecific.TestDelimitedTextOutput;
  229. // Test if saving and loading data keeps the original values.
  230. // Mainly check if writing & reading quotes works.
  231. // to do: more fully test RFC4180
  232. const
  233. OutputFileName='delim.csv';
  234. Value1='Delimiter,"and";quote';
  235. Value2='J"T"';
  236. Value3='Just a long line';
  237. Value4='Just a quoted long line';
  238. Value5='multi'+#13+#10+'line';
  239. Value6='Delimiter,and;done';
  240. Value7='Some "random" quotes';
  241. Var
  242. F : Text;
  243. begin
  244. TestDataset.Close;
  245. TestDataset.AllowMultiLine:=true;
  246. TestDataset.FirstLineAsSchema:=true;
  247. if FileExists(OutputFileName) then DeleteFile(OutputFileName);
  248. Assign(F,OutputFileName);
  249. Rewrite(F);
  250. Writeln(F,'Field1,Field2,Field3,Field4,Field5,Field6,Field7');
  251. Writeln(F,'"Delimiter,""and"";quote","J""T""",Just a long line,"Just a quoted long line","multi');
  252. Writeln(F,'line","Delimiter,and;done","Some ""random"" quotes"');
  253. Close(F);
  254. // Load our dataset
  255. TestDataset.FileName:=OutputFileName;
  256. TestDataset.Open;
  257. // AssertEquals('Field count',7,TEstDataset.Fielddefs.Count);
  258. // AssertEquals('Record count',1,TEstDataset.RecordCount);
  259. TestDataset.First;
  260. AssertEquals('Field1',Value1, TestDataSet.Fields[0].AsString);
  261. AssertEquals('Field2',Value2, TestDataSet.Fields[1].AsString);
  262. AssertEquals('Field3',Value3, TestDataSet.Fields[2].AsString);
  263. AssertEquals('Field4',Value4, TestDataSet.Fields[3].AsString);
  264. AssertEquals('Field5',Value5, TestDataSet.Fields[4].AsString);
  265. AssertEquals('Field6',Value6, TestDataSet.Fields[5].AsString);
  266. AssertEquals('Field7',Value7, TestDataSet.Fields[6].AsString);
  267. end;
  268. procedure Ttestsdfspecific.TestEmptyHeader;
  269. const
  270. OutputFileName='delim.csv';
  271. Var
  272. F : Text;
  273. begin
  274. TestDataset.Close;
  275. TestDataset.AllowMultiLine:=False;
  276. if FileExists(OutputFileName) then DeleteFile(OutputFileName);
  277. Assign(F,OutputFileName);
  278. Rewrite(F);
  279. Writeln(F,'1;2;3;;5');
  280. Close(F);
  281. TestDataset.FirstLineAsSchema:=True;
  282. TestDataset.Delimiter := ';';
  283. TestDataset.FileName:=OutputFileName;
  284. TestDataset.Open;
  285. AssertEquals('FieldDefs.Count',5,TestDataset.FieldDefs.Count);
  286. AssertEquals('RecordCount', 0, TestDataset.RecordCount);
  287. end;
  288. procedure Ttestsdfspecific.TestEmptyHeader2;
  289. const
  290. OutputFileName='delim.csv';
  291. Var
  292. F : Text;
  293. S : String;
  294. begin
  295. TestDataset.Close;
  296. TestDataset.AllowMultiLine:=False;
  297. if FileExists(OutputFileName) then DeleteFile(OutputFileName);
  298. Assign(F,OutputFileName);
  299. Rewrite(F);
  300. Writeln(F,'value1;value2;;;');
  301. Close(F);
  302. TestDataset.FirstLineAsSchema:=False;
  303. TestDataset.Delimiter := ';';
  304. TestDataset.FileName:=OutputFileName;
  305. TestDataset.Schema.Clear;
  306. TestDataset.Open;
  307. AssertEquals('FieldDefs.Count',5,TestDataset.FieldDefs.Count);
  308. AssertEquals('RecordCount', 1, TestDataset.RecordCount);
  309. TestDataset.Edit;
  310. TestDataset.Fields[0].AsString:='Value1';
  311. TestDataset.Post;
  312. TestDataset.Close;
  313. Assign(F,OutputFileName);
  314. Reset(F);
  315. ReadLn(F,S);
  316. Close(F);
  317. AssertEquals('No data lost','Value1;value2;;;',S);
  318. end;
  319. procedure Ttestsdfspecific.TestEmptyHeaderStripTrailingDelimiters;
  320. const
  321. OutputFileName='delim.csv';
  322. Var
  323. F : Text;
  324. S : String;
  325. begin
  326. TestDataset.Close;
  327. TestDataset.AllowMultiLine:=False;
  328. if FileExists(OutputFileName) then DeleteFile(OutputFileName);
  329. Assign(F,OutputFileName);
  330. Rewrite(F);
  331. Writeln(F,'value1;value2;;;');
  332. Close(F);
  333. TestDataset.StripTrailingDelimiters:=True;
  334. TestDataset.FirstLineAsSchema:=False;
  335. TestDataset.Delimiter := ';';
  336. TestDataset.FileName:=OutputFileName;
  337. TestDataset.Schema.Clear;
  338. TestDataset.Open;
  339. AssertEquals('FieldDefs.Count',2,TestDataset.FieldDefs.Count);
  340. TestDataset.Edit;
  341. TestDataset.Fields[0].AsString:='Value1';
  342. TestDataset.Post;
  343. TestDataset.Close;
  344. Assign(F,OutputFileName);
  345. Reset(F);
  346. ReadLn(F,S);
  347. Close(F);
  348. AssertEquals('No data lost','Value1;value2',S);
  349. end;
  350. procedure Ttestsdfspecific.TestStripTrailingDelimiters;
  351. const
  352. OutputFileName='delim.csv';
  353. Var
  354. F : Text;
  355. S,S2 : String;
  356. begin
  357. TestDataset.Close;
  358. TestDataset.AllowMultiLine:=False;
  359. if FileExists(OutputFileName) then DeleteFile(OutputFileName);
  360. Assign(F,OutputFileName);
  361. Rewrite(F);
  362. Writeln(F,'value1;value2;;;');
  363. Writeln(F,'value1;value2;;;');
  364. Close(F);
  365. TestDataset.StripTrailingDelimiters:=True;
  366. TestDataset.FirstLineAsSchema:=False;
  367. TestDataset.Delimiter := ';';
  368. TestDataset.FileName:=OutputFileName;
  369. TestDataset.Schema.Clear;
  370. TestDataset.Open;
  371. AssertEquals('FieldDefs.Count',2,TestDataset.FieldDefs.Count);
  372. TestDataset.Edit;
  373. TestDataset.Fields[0].AsString:='Value1';
  374. TestDataset.Post;
  375. TestDataset.Close;
  376. Assign(F,OutputFileName);
  377. Reset(F);
  378. ReadLn(F,S);
  379. ReadLn(F,S2);
  380. Close(F);
  381. AssertEquals('Headers lost','Value1;value2',S);
  382. AssertEquals('Data lost','Value1;value2',S);
  383. end;
  384. procedure Ttestsdfspecific.TestEmptyFieldContents;
  385. const
  386. OutputFileName='delim.csv';
  387. Var
  388. F : Text;
  389. begin
  390. TestDataset.Close;
  391. TestDataset.AllowMultiLine:=False;
  392. if FileExists(OutputFileName) then DeleteFile(OutputFileName);
  393. Assign(F,OutputFileName);
  394. Rewrite(F);
  395. Writeln(F,'1;2;3;;5');
  396. Writeln(F,'11;12;13;;15');
  397. Close(F);
  398. TestDataset.FirstLineAsSchema:=True;
  399. TestDataset.Delimiter := ';';
  400. TestDataset.FileName:=OutputFileName;
  401. TestDataset.Open;
  402. AssertEquals('FieldDefs.Count',5,TestDataset.FieldDefs.Count);
  403. AssertEquals('RecordCount',1,TestDataset.RecordCount);
  404. end;
  405. procedure Ttestsdfspecific.Setup;
  406. begin
  407. TestDataset := TSDFDataset.Create(nil);
  408. TestDataset.Delimiter := ',';
  409. TestDataset.FileMustExist:=false;
  410. TestDataset.FirstLineAsSchema := True;
  411. TestDataset.Schema.Add('ID');
  412. TestDataset.Schema.Add('NAME');
  413. TestDataset.Schema.Add('BIRTHDAY');
  414. end;
  415. procedure Ttestsdfspecific.Teardown;
  416. begin
  417. try
  418. TestDataset.Close;
  419. except
  420. //swallow
  421. end;
  422. TestDataset.Free;
  423. try
  424. //DeleteFile(FCSVFileName);
  425. except
  426. //swallow
  427. end;
  428. end;
  429. initialization
  430. // Only run these tests if we are running
  431. // sdf tests. After all, running these when testing
  432. // e.g. SQL RDBMS doesn't make sense.
  433. if uppercase(dbconnectorname)='SDFDS' then
  434. begin
  435. Registertest(Ttestsdfspecific);
  436. end;
  437. end.