dbfexporttestcase1.pas 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495
  1. unit dbfexporttestcase1;
  2. {$mode objfpc}{$H+}
  3. interface
  4. uses
  5. Classes, SysUtils, Fpcunit, Testutils, Testregistry, DB, fpdbfexport,
  6. BufDataset, dateutils;
  7. type
  8. { TTestDBFExport1 }
  9. TTestDBFExport1 = class(Ttestcase)
  10. const
  11. KeepFilesAfterTest = false;
  12. //Change if you want to keep export files for further testing
  13. private
  14. procedure FillTestData;
  15. protected
  16. FTestDataset: TBufDataset;
  17. FExportTempDir: string; //where we store exported files in these tests
  18. procedure FillRecord(const RowNumber: integer; const Teststring: string;
  19. const TestGUID: string; const TestInteger: integer;
  20. const TestExtended: extended; const TestDatetime: Tdatetime;
  21. const TestBoolean: boolean);
  22. procedure Setup; override;
  23. procedure Teardown; override;
  24. published
  25. procedure TestDBExportRuns;
  26. end;
  27. implementation
  28. function FileSize(FileName: string): integer;
  29. // LCL has similar function, but we don't want to depend on that.
  30. var
  31. SearchResult: TSearchRec;
  32. begin
  33. Result := 0;
  34. if FindFirst(FileName, faAnyFile, SearchResult) = 0 then
  35. begin
  36. try
  37. Result := SearchResult.Size;
  38. finally
  39. FindClose(SearchResult);
  40. end;
  41. end;
  42. end;
  43. procedure TTestDBFExport1.TestDBExportRuns;
  44. var
  45. Export: TFPDBFExport;
  46. ExportSettings: TDBFExportFormatSettings;
  47. NumberExported: integer;
  48. begin
  49. Export := TFPDBFExport.Create(nil);
  50. ExportSettings:=TDBFExportFormatSettings.Create(true);
  51. try
  52. //Don't override decimal separator
  53. ExportSettings.TableFormat:=tfDBaseVII; //dbase IV seems to have a 10 character field name limit
  54. Export.FormatSettings:=ExportSettings;
  55. Export.Dataset := FTestDataset;
  56. Export.FileName := FExportTempDir + 'dbfexporttest.dbf';
  57. NumberExported := Export.Execute;
  58. FTestDataset.Close;
  59. AssertEquals('Number of records exported', NumberExported, FTestDataset.RecordCount);
  60. AssertTrue('Output file created', FileExists(Export.FileName));
  61. AssertTrue('Output file has contents', (FileSize(Export.FileName) > 0));
  62. finally
  63. if (KeepFilesAfterTest = False) then
  64. begin
  65. DeleteFile(Export.FileName);
  66. end;
  67. ExportSettings.Free;
  68. Export.Free;
  69. end;
  70. end;
  71. procedure TTestDBFExport1.FillTestData;
  72. var
  73. RowNumber: integer; //Keep track of how many rows we inserted
  74. TestBoolean: boolean;
  75. TestDateTime: TDateTime;
  76. TestExtended: extended;
  77. //yes, a lot of precision; we can convert to single/double if required
  78. TestInteger: integer;
  79. TestGuid: string;
  80. TestString: string;
  81. begin
  82. FTestDataset.Close;
  83. RowNumber := 0;
  84. //for memds:
  85. //FTestDataset.Clear(False); //memds: clear out any data
  86. //FTestDataset.Fields.Clear; //bufds: clear out any data, but also FIELDDEFS: don't use
  87. FTestDataset.Open;
  88. // Fill some test data
  89. // First row: positive numerical values, late dates/times, strings with special chars (tab, linefeed, ; > <)
  90. FTestDataset.Append;
  91. TestBoolean := True;
  92. TestDateTime := EncodeDate(9999, 12, 31) + EncodeTime(23, 59, 59, 999);
  93. TestExtended := 42.424242424242424242424242424242;
  94. TestInteger := Round(TestExtended);
  95. TestGuid := '{21EC2020-3AEA-1069-A2DD-08002B30309D}';
  96. TestString := 'Douglas Adams less than: < greater than > tab:' +
  97. #9 + 'crlf:' + #13 + #10 +
  98. '國缺界广欠廣界界东缺. Haddock drinks rosé (ros, e accent aigu), водка (wodka cyrillic) and ούζο (ouzo Greek) but prefers Loch Lomond whiskey.';
  99. RowNumber := RowNumber + 1;
  100. FillRecord(RowNumber, Teststring, TestGUID, Testinteger, Testextended,
  101. Testdatetime, Testboolean);
  102. FTestDataset.Post;
  103. // Second row: negative numerical values, early dates/times, strings with maximum field width and Greek, east asian (multibyte) characters
  104. FTestDataset.Append;
  105. TestBoolean := False;
  106. TestDateTime := EncodeDate(1, 1, 1) + EncodeTime(0, 0, 0, 1);
  107. TestExtended := -42.424242424242424242424242424242;
  108. TestInteger := Round(TestExtended);
  109. TestGuid := '{FFFFFFFF-FFFF-FFFF-FFFF-FFFFFFFFFFFF}';
  110. TestString := 'ARMA virumque cano, Troiae qui primus ab oris' +
  111. #13 + #10 + 'Italiam, fato profugus, Laviniaque venit' + #13 +
  112. #10 + 'litora, multum ille et terris iactatus et alto' + #13 +
  113. #10 + 'vi superum saevae memorem Iunonis ob iram;' + #13 + #10 +
  114. 'multa quoque et bello passus, dum conderet urbem,' + #13 + #10 +
  115. 'inferretque deos Latio, genus unde Latinum,' + #13 + #10 +
  116. 'Albanique patres, atque altae moenia Romae.' + #13 + #10 + #13 +
  117. #10 + 'Musa, mihi causas memora, quo numine laeso,' + #13 + #10 +
  118. 'quidve dolens, regina deum tot volvere casus' + #13 + #10 +
  119. 'insignem pietate virum, tot adire labores' + #13 + #10 +
  120. 'impulerit. Tantaene animis caelestibus irae?' + #13 + #10 +
  121. #13 + #10 + 'Urbs antiqua fuit, Tyrii tenuere coloni,' + #13 +
  122. #10 + 'Karthago, Italiam contra Tiberinaque longe' + #13 + #10 +
  123. 'ostia, dives opum studiisque asperrima belli;' + #13 + #10 +
  124. 'quam Iuno fertur terris magis omnibus unam' + #13 + #10 +
  125. 'posthabita coluisse Samo; hic illius arma,' + #13 + #10 +
  126. 'hic currus fuit; hoc regnum dea gentibus esse,' + #13 + #10 +
  127. 'si qua fata sinant, iam tum tenditque fovetque.' + #13 + #10 +
  128. 'Progeniem sed enim Troiano a sanguine duci' + #13 + #10 +
  129. 'audierat, Tyrias olim quae verteret arces;' + #13 + #10 +
  130. 'hinc populum late regem belloque superbum' + #13 + #10 +
  131. 'venturum excidio Libyae: sic volvere Parcas.' + #13 + #10 +
  132. 'Id metuens, veterisque memor Saturnia belli,' + #13 + #10 +
  133. 'prima quod ad Troiam pro caris gesserat Argis---' + #13 + #10 +
  134. 'necdum etiam causae irarum saevique dolores' + #13 + #10 +
  135. 'exciderant animo: manet alta mente repostum' + #13 + #10 +
  136. 'iudicium Paridis spretaeque iniuria formae,' + #13 + #10 +
  137. 'et genus invisum, et rapti Ganymedis honores.' + #13 + #10 +
  138. 'His accensa super, iactatos aequore toto' + #13 + #10 +
  139. 'Troas, reliquias Danaum atque immitis Achilli,' + #13 + #10 +
  140. 'arcebat longe Latio, multosque per annos' + #13 + #10 +
  141. 'errabant, acti fatis, maria omnia circum.' + #13 + #10 +
  142. 'Tantae molis erat Romanam condere gentem!';
  143. RowNumber := RowNumber + 1;
  144. FillRecord(RowNumber, Teststring, TestGUID, Testinteger, Testextended,
  145. Testdatetime, Testboolean);
  146. FTestDataset.Post;
  147. // Third row: empty/zero numerical values, dates/times, strings
  148. FTestDataset.Append;
  149. TestBoolean := False;
  150. TestDateTime := EncodeDate(1, 1, 1) + EncodeTime(0, 0, 0, 0);
  151. TestExtended := 0;
  152. TestInteger := Round(TestExtended);
  153. TestGuid := '{3F2504E0-4F89-11D3-9A0C-0305E82C3301}';
  154. TestString := '';
  155. RowNumber := RowNumber + 1;
  156. FillRecord(RowNumber, Teststring, TestGUID, Testinteger, Testextended,
  157. Testdatetime, Testboolean);
  158. FTestDataset.Post;
  159. // Fourth row: plausible data
  160. FTestDataset.Append;
  161. TestBoolean := True;
  162. TestDateTime := EncodeDate(2005, 9, 10) + EncodeTime(13, 52, 18, 0);
  163. TestExtended := 42;
  164. TestInteger := Round(TestExtended);
  165. TestString := 'The answer to life, the universe, and everything';
  166. RowNumber := RowNumber + 1;
  167. FillRecord(RowNumber, Teststring, TestGUID, Testinteger, Testextended,
  168. Testdatetime, Testboolean);
  169. FTestDataset.Post;
  170. // Make sure recordcount is correct:
  171. FTestDataset.Last;
  172. FTestDataset.First;
  173. AssertEquals('Number of records in test dataset', RowNumber, FTestDataset.RecordCount);
  174. end;
  175. procedure TTestDBFExport1.Setup;
  176. const
  177. NumberOfDecimals = 2;
  178. NumberOfBytes = 10;
  179. var
  180. FieldDef: TFieldDef;
  181. begin
  182. FExportTempDir := GetTempDir(False);
  183. FTestDataset := TBufDataset.Create(nil);
  184. {Tweaked for dbf export}
  185. {We should cover all data types defined in FPC:
  186. FPC maps "external" types such as ftOracleBlob to
  187. internal types, but that can be overridden, which is done
  188. by e.g. IBX and mseide.
  189. So it makes sense to keep as many datatypes in the exporter code as possible: it documents the mappings and allows other people to use these types without the exporter breaking.
  190. }
  191. {Sorted by datatype; commented out what doesn't work at the moment in bufdataset
  192. See http://docwiki.embarcadero.com/VCL/en/DB.TField.Size for overview of field sizes in the competition product ;)
  193. Apparently ftGuid also needs size...
  194. }
  195. {
  196. FieldDef := FTestDataset.FieldDefs.AddFieldDef;
  197. FieldDef.Name := 'ftADT';
  198. FieldDef.DataType := ftADT;
  199. FieldDef.Size := 4096;//large but hopefully not too large for memory.
  200. }
  201. {
  202. FieldDef := FTestDataset.FieldDefs.AddFieldDef;
  203. FieldDef.Name := 'ftArray';
  204. FieldDef.DataType := ftArray;
  205. FieldDef.Size := 10;//the number of elements in the array
  206. FieldDef := FTestDataset.FieldDefs.AddFieldDef;
  207. FieldDef.Name := 'ftAutoInc';
  208. FieldDef.DataType := ftAutoInc;
  209. }
  210. FieldDef := FTestDataset.FieldDefs.AddFieldDef;
  211. FieldDef.Name := 'ftBCD';
  212. FieldDef.DataType := ftBCD;
  213. FieldDef.Size := NumberOfDecimals;
  214. //Size is the number of digits after the decimal place
  215. FieldDef := FTestDataset.FieldDefs.AddFieldDef;
  216. //Note dbf 3 has a 10 character field length limit
  217. FieldDef.Name := 'ftBlob_4096';
  218. FieldDef.DataType := ftBlob;
  219. FieldDef.Size := 4096;//large but hopefully not too large for memory.
  220. FieldDef := FTestDataset.FieldDefs.AddFieldDef;
  221. FieldDef.Name := 'ftBoolean';
  222. FieldDef.DataType := ftBoolean;
  223. FieldDef := FTestDataset.FieldDefs.AddFieldDef;
  224. FieldDef.Name := 'ftBytes';
  225. FieldDef.DataType := ftBytes;
  226. FieldDef.Size := NumberOfBytes;
  227. FieldDef := FTestDataset.FieldDefs.AddFieldDef;
  228. FieldDef.Name := 'ftCurrency';
  229. FieldDef.DataType := ftCurrency;
  230. {
  231. FieldDef := FTestDataset.FieldDefs.AddFieldDef;
  232. FieldDef.Name := 'ftCursor';
  233. FieldDef.DataType := ftCursor;
  234. }
  235. {
  236. FieldDef := FTestDataset.FieldDefs.AddFieldDef;
  237. FieldDef.Name := 'ftDataSet';
  238. FieldDef.DataType := ftDataSet;
  239. }
  240. FieldDef := FTestDataset.FieldDefs.AddFieldDef;
  241. FieldDef.Name := 'ftDate';
  242. FieldDef.DataType := ftDate;
  243. FieldDef := FTestDataset.FieldDefs.AddFieldDef;
  244. FieldDef.Name := 'ftDateTime';
  245. FieldDef.DataType := ftDateTime;
  246. FieldDef := FTestDataset.FieldDefs.AddFieldDef;
  247. FieldDef.Name := 'ftDBaseOle';
  248. FieldDef.DataType := ftDBaseOle;
  249. FieldDef.Size := 4096;//large but hopefully not too large for memory.
  250. FieldDef := FTestDataset.FieldDefs.AddFieldDef;
  251. FieldDef.Name := 'ftFixedChar_2';
  252. FieldDef.DataType := ftFixedChar;
  253. FieldDef.Size := NumberOfDecimals;
  254. FieldDef := FTestDataset.FieldDefs.AddFieldDef;
  255. FieldDef.Name := 'ftFixedWideChar_2';
  256. FieldDef.DataType := ftFixedWideChar;
  257. FieldDef.Size := NumberOfBytes;
  258. FieldDef := FTestDataset.FieldDefs.AddFieldDef;
  259. FieldDef.Name := 'ftFloat';
  260. FieldDef.DataType := ftFloat;
  261. FieldDef := FTestDataset.FieldDefs.AddFieldDef;
  262. FieldDef.Name := 'ftFMTBcd';
  263. FieldDef.DataType := ftFMTBcd;
  264. FieldDef.Size := NumberOfDecimals; //the number of digits after the decimal place.
  265. FieldDef := FTestDataset.FieldDefs.AddFieldDef;
  266. FieldDef.Name := 'ftFmtMemo';
  267. FieldDef.DataType := ftFmtMemo;
  268. FieldDef.Size := 4096;//large but hopefully not too large for memory.
  269. FieldDef := FTestDataset.FieldDefs.AddFieldDef;
  270. FieldDef.Name := 'ftGraphic';
  271. FieldDef.DataType := ftGraphic;
  272. FieldDef.Size := 4096;//large but hopefully not too large for memory.
  273. FieldDef := FTestDataset.FieldDefs.AddFieldDef;
  274. FieldDef.Name := 'ftGuid';
  275. FieldDef.DataType := ftGuid;
  276. FieldDef.Size := 38;
  277. //Apparently right answer is not 42; had to look up 38 in source code.
  278. {
  279. FieldDef := FTestDataset.FieldDefs.AddFieldDef;
  280. FieldDef.Name := 'ftIDispatch';
  281. FieldDef.DataType := ftIDispatch;
  282. }
  283. FieldDef := FTestDataset.FieldDefs.AddFieldDef;
  284. FieldDef.Name := 'ftInteger';
  285. FieldDef.DataType := ftInteger;
  286. {
  287. FieldDef := FTestDataset.FieldDefs.AddFieldDef;
  288. FieldDef.Name := 'ftInterface';
  289. FieldDef.DataType := ftInterface;
  290. }
  291. FieldDef := FTestDataset.FieldDefs.AddFieldDef;
  292. FieldDef.Name := 'ftLargeint';
  293. FieldDef.DataType := ftLargeint;
  294. FieldDef := FTestDataset.FieldDefs.AddFieldDef;
  295. FieldDef.Name := 'ftMemo';
  296. FieldDef.DataType := ftMemo;
  297. FieldDef.Size := 4096;//large but hopefully not too large for memory.
  298. FieldDef := FTestDataset.FieldDefs.AddFieldDef;
  299. FieldDef.Name := 'ftOraBlob';
  300. FieldDef.DataType := ftOraBlob;
  301. FieldDef := FTestDataset.FieldDefs.AddFieldDef;
  302. FieldDef.Name := 'ftOraClob';
  303. FieldDef.DataType := ftOraClob;
  304. FieldDef := FTestDataset.FieldDefs.AddFieldDef;
  305. FieldDef.Name := 'ftParadoxOle';
  306. FieldDef.DataType := ftParadoxOle;
  307. FieldDef.Size := 4096;//large but hopefully not too large for memory.
  308. {
  309. FieldDef := FTestDataset.FieldDefs.AddFieldDef;
  310. FieldDef.Name := 'ftReference';
  311. FieldDef.DataType := ftReference;
  312. }
  313. FieldDef := FTestDataset.FieldDefs.AddFieldDef;
  314. FieldDef.Name := 'ftSmallInt';
  315. FieldDef.DataType := ftInteger;
  316. FieldDef := FTestDataset.FieldDefs.AddFieldDef;
  317. FieldDef.Name := 'ftString_1';
  318. FieldDef.DataType := ftString;
  319. FieldDef.Size := 1;
  320. FieldDef := FTestDataset.FieldDefs.AddFieldDef;
  321. FieldDef.Name := 'ftString_256'; //1 character more than many db string types support
  322. FieldDef.DataType := ftString;
  323. FieldDef.Size := 256;
  324. FieldDef := FTestDataset.FieldDefs.AddFieldDef;
  325. FieldDef.Name := 'ftTime';
  326. FieldDef.DataType := ftTime;
  327. {
  328. FieldDef := FTestDataset.FieldDefs.AddFieldDef;
  329. FieldDef.Name := 'ftTimeStamp';
  330. FieldDef.DataType := ftTimeStamp;
  331. }
  332. {
  333. //Bufdataset probably doesn't support this
  334. FieldDef := FTestDataset.FieldDefs.AddFieldDef;
  335. // DBF 10 character limit comes into play here:
  336. FieldDef.Name := 'ftTypedBin';
  337. FieldDef.DataType := ftTypedBinary;
  338. FieldDef.Size := 4096;//large but hopefully not too large for memory.
  339. }
  340. FieldDef.Name := 'ftVariant';
  341. FieldDef.DataType := ftVariant;
  342. FieldDef.Size := NumberOfBytes;
  343. FieldDef := FTestDataset.FieldDefs.AddFieldDef;
  344. FieldDef.Name := 'ftVarBytes';
  345. FieldDef.DataType := ftVarBytes;
  346. FieldDef.Size := NumberOfBytes;
  347. FieldDef := FTestDataset.FieldDefs.AddFieldDef;
  348. FieldDef.Name := 'ftWideMemo';
  349. FieldDef.DataType := ftWideMemo;
  350. FieldDef := FTestDataset.FieldDefs.AddFieldDef;
  351. FieldDef.Name := 'ftWideString256';
  352. FieldDef.DataType := ftWideString;
  353. FieldDef.Size := 256;
  354. FieldDef := FTestDataset.FieldDefs.AddFieldDef;
  355. FieldDef.Name := 'ftWord';
  356. FieldDef.DataType := ftWord;
  357. //Finally, a long field name that should trigger
  358. //field renaming code in dbf export
  359. //(dbase VII supports up to 32 characters, others up to 10)
  360. FieldDef := FTestDataset.FieldDefs.AddFieldDef;
  361. FieldDef.Name := 'AVeryLongFieldDataTypeDoesNotMatter';
  362. FieldDef.DataType := ftString;
  363. FieldDef.Size := 256;
  364. //Createtable is needed if you use a memds
  365. //FTestDataset.CreateTable;
  366. //CreateDataset is needed if you use a bufdataset
  367. FTestDataset.CreateDataSet;
  368. // Fill dataset with test data
  369. FillTestData;
  370. end;
  371. procedure TTestDBFExport1.FillRecord(const RowNumber: integer;
  372. const TestString: string; const TestGUID: string; const TestInteger: integer;
  373. const TestExtended: extended; const TestDatetime: Tdatetime;
  374. const TestBoolean: boolean);
  375. var
  376. FieldCounter: integer;
  377. begin
  378. {As our bufdataset doesn't support these datatypes, don't use them:
  379. ftAutoInc -> exists but doesn't seem to return any data.
  380. ftCursor
  381. ftDataSet
  382. ftInterface
  383. ftReference
  384. ftTimeStamp}
  385. FTestDataset.FieldByName('ftBCD').AsFloat := Testextended;
  386. FTestDataset.FieldByName('ftBlob_4096').AsString := Teststring;
  387. FTestDataset.FieldByName('ftBoolean').AsBoolean := Testboolean;
  388. FTestDataset.FieldByName('ftBytes').AsString := Teststring;
  389. FTestDataset.FieldByName('ftCurrency').Ascurrency := Testextended;
  390. FTestDataset.FieldByName('ftDate').AsDateTime := Testdatetime;
  391. FTestDataset.FieldByName('ftDateTime').AsDateTime := Testdatetime;
  392. FTestDataset.FieldByName('ftDBaseOle').AsString := Teststring;
  393. FTestDataset.FieldByName('ftFixedChar_2').AsString := Teststring;
  394. FTestDataset.FieldByName('ftFixedWideChar_2').AsString := Teststring;
  395. FTestDataset.FieldByName('ftFloat').AsFloat := Testextended;
  396. FTestDataset.FieldByName('ftFMTBcd').AsFloat := Testextended;
  397. FTestDataset.FieldByName('ftFmtMemo').AsString := Teststring;
  398. FTestDataset.FieldByName('ftGraphic').AsString := Teststring;
  399. FTestDataset.FieldByName('ftGuid').AsString := TestGUID;
  400. FTestDataset.FieldByName('ftInteger').AsInteger := Testinteger;
  401. FTestDataset.FieldByName('ftLargeint').AsInteger := Testinteger;
  402. FTestDataset.FieldByName('ftMemo').AsString := Teststring;
  403. FTestDataset.FieldByName('ftOraBlob').AsString := Teststring;
  404. {
  405. FTestDataset.FieldByName('ftOraClob').AsString := Teststring;
  406. }
  407. FTestDataset.FieldByName('ftParadoxOle').AsString := Teststring;
  408. FTestDataset.FieldByName('ftSmallInt').AsInteger := Testinteger;
  409. FTestDataset.FieldByName('ftString_1').AsString := Teststring;
  410. FTestDataset.FieldByName('ftString_256').AsString := Teststring;
  411. FTestDataset.FieldByName('ftTime').AsDateTime := Testdatetime;
  412. {
  413. FTestDataset.FieldByName('ftTypedBin').AsString := Teststring;
  414. }
  415. FTestDataSet.FieldByName('ftVarBytes').AsString := TestString;
  416. FTestDataSet.FieldByName('ftVariant').AsString := TestString;
  417. FTestDataset.FieldByName('ftWideMemo').AsString := Teststring;
  418. FTestDataset.FieldByName('ftWideString256').AsString := Teststring;
  419. FTestDataset.FieldByName('ftWord').AsInteger := Abs(Testinteger);
  420. FTestDataset.FieldByName('AVeryLongFieldDataTypeDoesNotMatter').AsString := Teststring;
  421. end;
  422. procedure TTestDBFExport1.Teardown;
  423. begin
  424. FTestDataset.Free;
  425. end;
  426. initialization
  427. Registertest(TTestDBFExport1);
  428. end.