testspecifictdbf.pas 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418
  1. unit testspecifictdbf;
  2. {
  3. Unit tests which are specific to the tdbf dbase/foxpro units.
  4. }
  5. {$IFDEF FPC}
  6. {$mode Delphi}{$H+}
  7. {$ENDIF}
  8. interface
  9. uses
  10. {$IFDEF FPC}
  11. fpcunit, testutils, testregistry, testdecorator,
  12. {$ELSE FPC}
  13. TestFramework,
  14. {$ENDIF FPC}
  15. Classes, SysUtils,
  16. ToolsUnit, dbf;
  17. type
  18. { TTestSpecificTDBF }
  19. TTestSpecificTDBF = class(TTestCase)
  20. private
  21. procedure WriteReadbackTest(ADBFDataset: TDbf; AutoInc: boolean = false);
  22. protected
  23. procedure SetUp; override;
  24. procedure TearDown; override;
  25. published
  26. // Verifies that requested tablelevel is delivered:
  27. procedure TestTableLevel;
  28. // Verifies that writing to memory and writing to disk results in the same data
  29. procedure TestMemoryDBFEqualsDiskDBF;
  30. // Create fields using indexdefs:
  31. procedure TestCreateDatasetFromFielddefs;
  32. // Specifying fields from field objects
  33. procedure TestCreateDatasetFromFields;
  34. // Tries to open a dbf that has not been activated, which should fail:
  35. procedure TestOpenNonExistingDataset_Fails;
  36. // Tests creating a new database with calculated/lookup fields
  37. procedure TestCreationDatasetWithCalcFields;
  38. procedure TestAutoIncField;
  39. // Tests findfirst moves to first record
  40. procedure TestFindFirst;
  41. // Tests findlast moves to last record
  42. procedure TestFindLast;
  43. // Tests findnext moves to next record
  44. procedure TestFindNext;
  45. // Tests findprior
  46. procedure TestFindPrior;
  47. // Tests writing and reading a memo field
  48. procedure TestMemo;
  49. // Tests string field with 254 characters (max for DBase IV)
  50. procedure TestLargeString;
  51. end;
  52. implementation
  53. uses
  54. variants,
  55. FmtBCD,
  56. db, dbf_common, DBFToolsUnit;
  57. { TTestSpecificTDBF }
  58. procedure TTestSpecificTDBF.WriteReadbackTest(ADBFDataset: TDbf;
  59. AutoInc: boolean);
  60. var
  61. i : integer;
  62. begin
  63. // Add sample data
  64. for i := 1 to 10 do
  65. begin
  66. ADBFDataset.Append;
  67. if not AutoInc then
  68. ADBFDataset.FieldByName('ID').AsInteger := i;
  69. ADBFDataset.FieldByName('NAME').AsString := 'TestName' + inttostr(i);
  70. ADBFDataset.Post;
  71. end;
  72. ADBFDataset.first;
  73. for i := 1 to 10 do
  74. begin
  75. CheckEquals(i,ADBFDataset.fieldbyname('ID').asinteger);
  76. CheckEquals('TestName' + inttostr(i),ADBFDataset.fieldbyname('NAME').AsString);
  77. ADBFDataset.next;
  78. end;
  79. CheckTrue(ADBFDataset.EOF);
  80. end;
  81. procedure TTestSpecificTDBF.SetUp;
  82. begin
  83. DBConnector.StartTest;
  84. end;
  85. procedure TTestSpecificTDBF.TearDown;
  86. begin
  87. DBConnector.StopTest;
  88. end;
  89. procedure TTestSpecificTDBF.TestTableLevel;
  90. var
  91. ds : TDBF;
  92. begin
  93. ds := TDBFAutoClean.Create(nil);
  94. DS.FieldDefs.Add('ID',ftInteger);
  95. DS.CreateTable;
  96. DS.Open;
  97. CheckEquals((DS as TDBFAutoClean).UserRequestedTableLevel,DS.TableLevel,'User specified tablelevel should match dbf tablelevel.');
  98. DS.Close;
  99. ds.free;
  100. end;
  101. procedure TTestSpecificTDBF.TestMemoryDBFEqualsDiskDBF;
  102. var
  103. dsfile: TDBF;
  104. dsmem: TDBF;
  105. backingstream: TMemoryStream;
  106. FileName: string;
  107. i: integer;
  108. thefile: TMemoryStream;
  109. begin
  110. backingstream:=TMemoryStream.Create;
  111. thefile:=TMemoryStream.Create;
  112. dsmem:=TDBF.Create(nil);
  113. dsfile:=TDBF.Create(nil);
  114. FileName:=GetTempFileName;
  115. dsfile.FilePathFull:=ExtractFilePath(FileName);
  116. dsfile.TableName:=ExtractFileName(FileName);
  117. dsmem.TableName:=ExtractFileName(FileName);
  118. dsmem.Storage:=stoMemory;
  119. dsmem.UserStream:=backingstream;
  120. // A small number of fields but should be enough
  121. dsfile.FieldDefs.Add('ID',ftInteger);
  122. dsmem.FieldDefs.Add('ID',ftInteger);
  123. dsfile.FieldDefs.Add('NAME',ftString,50);
  124. dsmem.FieldDefs.Add('NAME',ftString,50);
  125. dsfile.CreateTable;
  126. dsmem.CreateTable;
  127. dsfile.Open;
  128. dsmem.Open;
  129. // Some sample data
  130. for i := 1 to 101 do
  131. begin
  132. dsfile.Append;
  133. dsmem.Append;
  134. dsfile.FieldByName('ID').AsInteger := i;
  135. dsmem.FieldByName('ID').AsInteger := i;
  136. dsfile.FieldByName('NAME').AsString := 'TestName' + inttostr(i);
  137. dsmem.FieldByName('NAME').AsString := 'TestName' + inttostr(i);
  138. dsfile.Post;
  139. dsmem.Post;
  140. end;
  141. // By closing, we update the number of records in the header
  142. dsfile.close;
  143. dsmem.close;
  144. dsfile.free;
  145. // Keep dsmem; load file into stream:
  146. thefile.LoadfromFile(FileName);
  147. deletefile(FileName);
  148. CheckEquals(backingstream.size,thefile.size,'Memory backed dbf should have same size as file-backed dbf');
  149. // Now compare stream contents - thereby comparing the file with backingstream
  150. CheckEquals(true,comparemem(thefile.Memory,backingstream.Memory,thefile.size),'Memory backed dbf data should be the same as file-backed dbf');
  151. backingstream.free;
  152. thefile.free;
  153. end;
  154. procedure TTestSpecificTDBF.TestCreateDatasetFromFielddefs;
  155. var
  156. ds : TDBF;
  157. begin
  158. ds := TDBFAutoClean.Create(nil);
  159. DS.FieldDefs.Add('ID',ftInteger);
  160. DS.FieldDefs.Add('NAME',ftString,50);
  161. DS.CreateTable;
  162. DS.Open;
  163. WriteReadbackTest(ds);
  164. DS.Close;
  165. ds.free;
  166. end;
  167. procedure TTestSpecificTDBF.TestCreateDatasetFromFields;
  168. var
  169. ds : TDBF;
  170. f: TField;
  171. begin
  172. ds := TDBFAutoClean.Create(nil);
  173. DS.CreateTable;
  174. F := TIntegerField.Create(ds);
  175. F.FieldName:='ID';
  176. F.DataSet:=ds;
  177. F := TStringField.Create(ds);
  178. F.FieldName:='NAME';
  179. F.DataSet:=ds;
  180. F.Size:=50;
  181. DS.Open;
  182. ds.free;
  183. end;
  184. procedure TTestSpecificTDBF.TestOpenNonExistingDataset_Fails;
  185. var
  186. ds : TDBF;
  187. f: TField;
  188. begin
  189. ds := TDBFAutoClean.Create(nil);
  190. F := TIntegerField.Create(ds);
  191. F.FieldName:='ID';
  192. F.DataSet:=ds;
  193. CheckException(ds.Open,EDbfError);
  194. ds.Free;
  195. ds := TDBFAutoClean.Create(nil);
  196. DS.FieldDefs.Add('ID',ftInteger);
  197. CheckException(ds.Open,EDbfError);
  198. ds.Free;
  199. end;
  200. procedure TTestSpecificTDBF.TestCreationDatasetWithCalcFields;
  201. var
  202. ds : TDBF;
  203. f: TField;
  204. i: integer;
  205. begin
  206. //todo: find out which tablelevels support calculated/lookup fields
  207. ds := TDBFAutoClean.Create(nil);
  208. try
  209. ds.FieldDefs.Add('ID',ftInteger);
  210. ds.FieldDefs.Add('NAME',ftString,50);
  211. ds.CreateTable;
  212. for i:=0 to ds.FieldDefs.Count-1 do
  213. begin
  214. ds.FieldDefs[i].CreateField(ds); // make fields persistent
  215. end;
  216. F := TStringField.Create(ds);
  217. F.FieldKind:=fkCalculated;
  218. F.FieldName:='NAME_CALC';
  219. F.DataSet:=ds;
  220. F.Size:=50;
  221. F.ProviderFlags:=[];
  222. F := TStringField.Create(ds);
  223. F.FieldKind:=fkLookup;
  224. F.FieldName:='NAME_LKP';
  225. F.LookupDataSet:=DBConnector.GetNDataset(5);
  226. F.KeyFields:='ID';
  227. F.LookupKeyFields:='ID';
  228. F.LookupResultField:='NAME';
  229. F.DataSet:=ds;
  230. F.Size:=50;
  231. DS.Open;
  232. WriteReadbackTest(ds);
  233. for i := 0 to ds.FieldDefs.Count-1 do
  234. begin
  235. CheckNotEquals(ds.FieldDefs[i].Name,'NAME_CALC');
  236. CheckNotEquals(ds.FieldDefs[i].Name,'NAME_LKP');
  237. end;
  238. DS.Close;
  239. finally
  240. ds.Free;
  241. end;
  242. end;
  243. procedure TTestSpecificTDBF.TestAutoIncField;
  244. var
  245. ds : TDbf;
  246. f: TField;
  247. begin
  248. ds := TDbfAutoClean.Create(nil);
  249. if ds.TableLevel<7 then
  250. begin
  251. Ignore('Autoinc fields are only supported in tablelevel 7 and higher');
  252. end;
  253. F := TAutoIncField.Create(ds);
  254. F.FieldName:='ID';
  255. F.DataSet:=ds;
  256. F := TStringField.Create(ds);
  257. F.FieldName:='NAME';
  258. F.DataSet:=ds;
  259. F.Size:=50;
  260. DS.CreateTable;
  261. DS.Open;
  262. WriteReadbackTest(ds,True);
  263. DS.Close;
  264. ds.Free;
  265. end;
  266. procedure TTestSpecificTDBF.TestFindFirst;
  267. const
  268. NumRecs=8;
  269. var
  270. DS: TDataSet;
  271. begin
  272. DS:=DBConnector.GetNDataset(NumRecs);
  273. DS.Open;
  274. DS.Last;
  275. CheckEquals(true,DS.FindFirst,'Findfirst should return true');
  276. CheckEquals(1,DS.fieldbyname('ID').asinteger);
  277. end;
  278. procedure TTestSpecificTDBF.TestFindLast;
  279. const
  280. NumRecs=8;
  281. var
  282. DS: TDataSet;
  283. begin
  284. DS:=DBConnector.GetNDataset(NumRecs);
  285. DS.Open;
  286. DS.First;
  287. CheckEquals(true,DS.FindLast,'Findlast should return true');
  288. CheckEquals(NumRecs,DS.fieldbyname('ID').asinteger);
  289. end;
  290. procedure TTestSpecificTDBF.TestFindNext;
  291. const
  292. NumRecs=8;
  293. var
  294. DS: TDataSet;
  295. begin
  296. DS:=DBConnector.GetNDataset(NumRecs);
  297. DS.Open;
  298. DS.First;
  299. CheckEquals(true,DS.FindNext,'FindNext should return true');
  300. CheckEquals(2,DS.fieldbyname('ID').asinteger);
  301. end;
  302. procedure TTestSpecificTDBF.TestFindPrior;
  303. const
  304. NumRecs=8;
  305. var
  306. DS: TDataSet;
  307. begin
  308. DS:=DBConnector.GetNDataset(NumRecs);
  309. DS.Open;
  310. DS.Last;
  311. CheckEquals(true,DS.FindPrior,'FindPrior should return true');
  312. CheckEquals(NumRecs-1,DS.fieldbyname('ID').asinteger);
  313. end;
  314. procedure TTestSpecificTDBF.TestMemo;
  315. var
  316. ds : TDBF;
  317. begin
  318. ds := TDBFAutoClean.Create(nil);
  319. DS.FieldDefs.Add('ID',ftInteger);
  320. DS.FieldDefs.Add('NAME',ftMemo);
  321. DS.CreateTable;
  322. DS.Open;
  323. WriteReadbackTest(ds);
  324. DS.Close;
  325. ds.free;
  326. end;
  327. procedure TTestSpecificTDBF.TestLargeString;
  328. var
  329. ds : TDBF;
  330. MaxStringSize: integer;
  331. TestValue: string;
  332. begin
  333. ds := TDBFAutoClean.Create(nil);
  334. if (ds.TableLevel>=25) then
  335. // (Visual) FoxPro supports 32K
  336. MaxStringSize:=32767
  337. else
  338. // Dbase III..V,7
  339. MaxStringSize:=254;
  340. TestValue:=StringOfChar('a',MaxStringSize);
  341. DS.FieldDefs.Add('ID',ftInteger);
  342. DS.FieldDefs.Add('NAME',ftString,254);
  343. DS.CreateTable;
  344. DS.Open;
  345. // Write & readback test
  346. DS.Append;
  347. DS.FieldByName('ID').AsInteger := 1;
  348. DS.FieldByName('NAME').AsString := TestValue;
  349. DS.Post;
  350. DS.first;
  351. CheckEquals(1,DS.fieldbyname('ID').asinteger,'ID field must match record number');
  352. // If test fails, let's count the number of "a"s instead so we can report that instead of printing out the entire string
  353. CheckEquals(length(TestValue),length(DS.fieldbyname('NAME').AsString),'NAME field length must match test value length');
  354. CheckEquals(TestValue,DS.fieldbyname('NAME').AsString,'NAME field must match test value');
  355. DS.next;
  356. CheckTrue(DS.EOF,'Dataset EOF must be true');
  357. DS.Close;
  358. ds.free;
  359. end;
  360. initialization
  361. {$ifdef fpc}
  362. if uppercase(dbconnectorname)='DBF' then
  363. begin
  364. RegisterTestDecorator(TDBBasicsTestSetup, TTestSpecificTDBF);
  365. end;
  366. {$endif fpc}
  367. end.