tcsdfdata.pp 14 KB

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