tcsdfdata.pp 14 KB

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