123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577 |
- unit testspecifictdbf;
- {
- Unit tests which are specific to the tdbf dbase/foxpro units.
- }
- {$IFDEF FPC}
- {$mode Delphi}{$H+}
- {$ENDIF}
- interface
- uses
- {$IFDEF FPC}
- fpcunit, testregistry,
- {$ELSE FPC}
- TestFramework,
- {$ENDIF FPC}
- Classes, SysUtils,
- ToolsUnit, dbf;
- type
- { TTestSpecificTDBF }
- TTestSpecificTDBF = class(TTestCase)
- private
- // Writes data to dataset and verifies readback is correct
- procedure WriteReadbackTest(ADBFDataset: TDbf; AutoInc: boolean = false);
- protected
- procedure SetUp; override;
- procedure TearDown; override;
- published
- // Verifies that requested tablelevel is delivered:
- procedure TestTableLevel;
- // Verifies that writing to memory and writing to disk results in the same data
- procedure TestMemoryDBFEqualsDiskDBF;
- // Create fields using indexdefs:
- procedure TestCreateDatasetFromFielddefs;
- // Specifying fields from field objects
- procedure TestCreateDatasetFromFields;
- // Tries to open a dbf that has not been activated, which should fail:
- procedure TestOpenNonExistingDataset_Fails;
- // Tests creating a new database with calculated/lookup fields
- procedure TestCreationDatasetWithCalcFields;
- // Tests autoincrement field (only in tablelevels that support it)
- procedure TestAutoIncField;
- // Tests findfirst moves to first record
- procedure TestFindFirst;
- // Tests findlast moves to last record
- procedure TestFindLast;
- // Tests findnext moves to next record
- procedure TestFindNext;
- // Tests findprior
- procedure TestFindPrior;
- // Tests writing and reading a memo field
- procedure TestMemo;
- // Tests like TestMemo, but closes and reopens in memory file
- // in between. Data should still be there.
- procedure TestMemoClose;
- // Same as TestMemoClose except added index stream
- procedure TestIndexClose;
- // Tests string field with
- // 254 characters (max for DBase IV)
- // 32767 characters (FoxPro, Visual FoxPro)
- procedure TestLargeString;
- // Tests codepage in created dbf equals requested codepage
- procedure TestCodePage;
- end;
- implementation
- uses
- variants,
- FmtBCD,
- db, dbf_common, DBFToolsUnit;
- { TTestSpecificTDBF }
- procedure TTestSpecificTDBF.WriteReadbackTest(ADBFDataset: TDbf;
- AutoInc: boolean);
- const
- MaxRecs = 10;
- var
- i : integer;
- begin
- // Add sample data
- for i := 1 to MaxRecs do
- begin
- ADBFDataset.Append;
- if not AutoInc then
- ADBFDataset.FieldByName('ID').AsInteger := i;
- ADBFDataset.FieldByName('NAME').AsString := 'TestName' + inttostr(i);
- ADBFDataset.Post;
- end;
- // Verify sample data is correct
- ADBFDataset.first;
- for i := 1 to MaxRecs do
- begin
- CheckEquals(i,ADBFDataset.fieldbyname('ID').asinteger);
- CheckEquals('TestName' + inttostr(i),ADBFDataset.fieldbyname('NAME').AsString);
- ADBFDataset.next;
- end;
- CheckTrue(ADBFDataset.EOF,'After reading all records the dataset should show EOF');
- end;
- procedure TTestSpecificTDBF.SetUp;
- begin
- DBConnector.StartTest(TestName);
- end;
- procedure TTestSpecificTDBF.TearDown;
- begin
- DBConnector.StopTest(TestName);
- end;
- procedure TTestSpecificTDBF.TestTableLevel;
- var
- ds : TDBF;
- begin
- ds := TDBFAutoClean.Create(nil);
- if ((DS as TDBFAutoClean).UserRequestedTableLevel=25) then
- ignore('Foxpro (tablelevel 25) may write data out in dBase IV (tablelevel 4) format.');
- DS.FieldDefs.Add('ID',ftInteger);
- DS.CreateTable;
- DS.Open;
- CheckEquals((DS as TDBFAutoClean).UserRequestedTableLevel,DS.TableLevel,'User specified tablelevel should match dbf tablelevel.');
- DS.Close;
- ds.free;
- end;
- procedure TTestSpecificTDBF.TestMemoryDBFEqualsDiskDBF;
- var
- dsfile: TDBF;
- dsmem: TDBF;
- backingstream: TMemoryStream;
- FileName: string;
- i: integer;
- thefile: TMemoryStream;
- begin
- backingstream:=TMemoryStream.Create;
- thefile:=TMemoryStream.Create;
- dsmem:=TDBF.Create(nil);
- dsfile:=TDBF.Create(nil);
- FileName:=GetTempFileName;
- dsfile.FilePathFull:=ExtractFilePath(FileName);
- dsfile.TableName:=ExtractFileName(FileName);
- dsmem.TableName:=ExtractFileName(FileName);
- dsmem.Storage:=stoMemory;
- dsmem.UserStream:=backingstream;
- // A small number of fields but should be enough
- dsfile.FieldDefs.Add('ID',ftInteger);
- dsmem.FieldDefs.Add('ID',ftInteger);
- dsfile.FieldDefs.Add('NAME',ftString,50);
- dsmem.FieldDefs.Add('NAME',ftString,50);
- dsfile.CreateTable;
- dsmem.CreateTable;
- dsfile.Open;
- dsmem.Open;
- // Some sample data
- for i := 1 to 101 do
- begin
- dsfile.Append;
- dsmem.Append;
- dsfile.FieldByName('ID').AsInteger := i;
- dsmem.FieldByName('ID').AsInteger := i;
- dsfile.FieldByName('NAME').AsString := 'TestName' + inttostr(i);
- dsmem.FieldByName('NAME').AsString := 'TestName' + inttostr(i);
- dsfile.Post;
- dsmem.Post;
- end;
- // By closing, we update the number of records in the header
- dsfile.close;
- dsmem.close;
- dsfile.free;
- // Keep dsmem; load file into stream:
- thefile.LoadfromFile(FileName);
- deletefile(FileName);
- CheckEquals(backingstream.size,thefile.size,'Memory backed dbf should have same size as file-backed dbf');
- // Now compare stream contents - thereby comparing the file with backingstream
- CheckEquals(true,comparemem(thefile.Memory,backingstream.Memory,thefile.size),'Memory backed dbf data should be the same as file-backed dbf');
- backingstream.free;
- thefile.free;
- end;
- procedure TTestSpecificTDBF.TestCreateDatasetFromFielddefs;
- var
- ds : TDBF;
- begin
- ds := TDBFAutoClean.Create(nil);
- DS.FieldDefs.Add('ID',ftInteger);
- DS.FieldDefs.Add('NAME',ftString,50);
- DS.CreateTable;
- DS.Open;
- WriteReadbackTest(ds);
- DS.Close;
- ds.free;
- end;
- procedure TTestSpecificTDBF.TestCreateDatasetFromFields;
- var
- ds : TDBF;
- f: TField;
- begin
- ds := TDBFAutoClean.Create(nil);
- DS.CreateTable;
- F := TIntegerField.Create(ds);
- F.FieldName:='ID';
- F.DataSet:=ds;
- F := TStringField.Create(ds);
- F.FieldName:='NAME';
- F.DataSet:=ds;
- F.Size:=50;
- DS.Open;
- ds.free;
- end;
- procedure TTestSpecificTDBF.TestOpenNonExistingDataset_Fails;
- var
- ds : TDBF;
- f: TField;
- begin
- ds := TDBFAutoClean.Create(nil);
- F := TIntegerField.Create(ds);
- F.FieldName:='ID';
- F.DataSet:=ds;
- CheckException(ds.Open,EDbfError);
- ds.Free;
- ds := TDBFAutoClean.Create(nil);
- DS.FieldDefs.Add('ID',ftInteger);
- CheckException(ds.Open,EDbfError);
- ds.Free;
- end;
- procedure TTestSpecificTDBF.TestCreationDatasetWithCalcFields;
- var
- ds : TDBF;
- f: TField;
- i: integer;
- begin
- //todo: find out which tablelevels support calculated/lookup fields
- ds := TDBFAutoClean.Create(nil);
- try
- ds.FieldDefs.Add('ID',ftInteger);
- ds.FieldDefs.Add('NAME',ftString,50);
- ds.CreateTable;
- for i:=0 to ds.FieldDefs.Count-1 do
- begin
- ds.FieldDefs[i].CreateField(ds); // make fields persistent
- end;
- F := TStringField.Create(ds);
- F.FieldKind:=fkCalculated;
- F.FieldName:='NAME_CALC';
- F.DataSet:=ds;
- F.Size:=50;
- F.ProviderFlags:=[];
- F := TStringField.Create(ds);
- F.FieldKind:=fkLookup;
- F.FieldName:='NAME_LKP';
- F.LookupDataSet:=DBConnector.GetNDataset(5);
- F.KeyFields:='ID';
- F.LookupKeyFields:='ID';
- F.LookupResultField:='NAME';
- F.DataSet:=ds;
- F.Size:=50;
- DS.Open;
- WriteReadbackTest(ds);
- for i := 0 to ds.FieldDefs.Count-1 do
- begin
- CheckNotEquals(ds.FieldDefs[i].Name,'NAME_CALC');
- CheckNotEquals(ds.FieldDefs[i].Name,'NAME_LKP');
- end;
- DS.Close;
- finally
- ds.Free;
- end;
- end;
- procedure TTestSpecificTDBF.TestAutoIncField;
- var
- ds : TDbf;
- f: TField;
- begin
- ds := TDbfAutoClean.Create(nil);
- if ds.TableLevel<7 then
- begin
- Ignore('Autoinc fields are only supported in tablelevel 7 and higher');
- end;
- F := TAutoIncField.Create(ds);
- F.FieldName:='ID';
- F.DataSet:=ds;
- F := TStringField.Create(ds);
- F.FieldName:='NAME';
- F.DataSet:=ds;
- F.Size:=50;
- DS.CreateTable;
- DS.Open;
- WriteReadbackTest(ds,True);
- DS.Close;
- ds.Free;
- end;
- procedure TTestSpecificTDBF.TestFindFirst;
- const
- NumRecs=8;
- var
- DS: TDataSet;
- begin
- DS:=DBConnector.GetNDataset(NumRecs);
- DS.Open;
- DS.Last;
- CheckEquals(true,DS.FindFirst,'Findfirst should return true');
- CheckEquals(1,DS.fieldbyname('ID').asinteger);
- end;
- procedure TTestSpecificTDBF.TestFindLast;
- const
- NumRecs=8;
- var
- DS: TDataSet;
- begin
- DS:=DBConnector.GetNDataset(NumRecs);
- DS.Open;
- DS.First;
- CheckEquals(true,DS.FindLast,'Findlast should return true');
- CheckEquals(NumRecs,DS.fieldbyname('ID').asinteger);
- end;
- procedure TTestSpecificTDBF.TestFindNext;
- const
- NumRecs=8;
- var
- DS: TDataSet;
- begin
- DS:=DBConnector.GetNDataset(NumRecs);
- DS.Open;
- DS.First;
- CheckEquals(true,DS.FindNext,'FindNext should return true');
- CheckEquals(2,DS.fieldbyname('ID').asinteger);
- end;
- procedure TTestSpecificTDBF.TestFindPrior;
- const
- NumRecs=8;
- var
- DS: TDataSet;
- begin
- DS:=DBConnector.GetNDataset(NumRecs);
- DS.Open;
- DS.Last;
- CheckEquals(true,DS.FindPrior,'FindPrior should return true');
- CheckEquals(NumRecs-1,DS.fieldbyname('ID').asinteger);
- end;
- procedure TTestSpecificTDBF.TestMemo;
- var
- ds : TDBF;
- begin
- ds := TDBFAutoClean.Create(nil);
- DS.FieldDefs.Add('ID',ftInteger);
- DS.FieldDefs.Add('NAME',ftMemo);
- DS.OpenMode:=omAutoCreate; //let dbf code create memo etc files when needed
- DS.CreateTable;
- DS.Open;
- WriteReadbackTest(ds);
- DS.Close;
- ds.free;
- end;
- procedure TTestSpecificTDBF.TestMemoClose;
- const
- MaxRecs = 10;
- var
- ds : TDBF;
- i: integer;
- DBFStream: TMemoryStream;
- MemoStream: TMemoryStream;
- begin
- ds := TDBF.Create(nil);
- DBFStream:=TMemoryStream.Create;
- MemoStream:=TMemoryStream.Create;
- DS.Storage:=stoMemory;
- DS.UserStream:=DBFStream;
- DS.UserMemoStream:=MemoStream;
- DS.FieldDefs.Add('ID',ftInteger);
- DS.FieldDefs.Add('NAME',ftMemo);
- DS.OpenMode:=omAutoCreate; //let dbf code create memo etc files when needed
- DS.CreateTable;
-
- DS.Open;
- for i := 1 to MaxRecs do
- begin
- DS.Append;
- DS.FieldByName('ID').AsInteger := i;
- DS.FieldByName('NAME').AsString := 'TestName' + inttostr(i);
- DS.Post;
- end;
- DS.Close; //in old implementations, this erased memo memory
-
- DS.Open;
- DS.First;
- for i := 1 to MaxRecs do
- begin
- CheckEquals(i,DS.fieldbyname('ID').asinteger);
- CheckEquals('TestName' + inttostr(i),DS.fieldbyname('NAME').AsString);
- DS.next;
- end;
- CheckTrue(DS.EOF,'After reading all records the dataset should show EOF');
- DS.Close;
-
- ds.free;
- DBFStream.Free;
- MemoStream.Free;
- end;
- procedure TTestSpecificTDBF.TestIndexClose;
- const
- MaxRecs = 10;
- var
- ds : TDBF;
- i: integer;
- DBFStream: TMemoryStream;
- IndexStream: TMemoryStream;
- MemoStream: TMemoryStream;
- begin
- ds := TDBF.Create(nil);
- DBFStream:=TMemoryStream.Create;
- IndexStream:=TMemoryStream.Create;
- MemoStream:=TMemoryStream.Create;
- DS.Storage:=stoMemory;
- DS.UserStream:=DBFStream;
- DS.UserIndexStream:=IndexStream;
- DS.UserMemoStream:=MemoStream;
- DS.FieldDefs.Add('ID',ftInteger);
- DS.FieldDefs.Add('NAME',ftMemo);
- DS.OpenMode:=omAutoCreate; //let dbf code create memo etc files when needed
- DS.CreateTable;
- DS.Exclusive:=true;//needed for index
- DS.Open;
- DS.AddIndex('idxID','ID', [ixPrimary, ixUnique]);
- DS.Close;
- DS.Exclusive:=false;
- DS.Open;
- for i := 1 to MaxRecs do
- begin
- DS.Append;
- DS.FieldByName('ID').AsInteger := i;
- DS.FieldByName('NAME').AsString := 'TestName' + inttostr(i);
- DS.Post;
- end;
- DS.Close; //in old implementations, this erased memo memory
- // Check streams have content
- CheckNotEquals(0,DBFStream.Size,'DBF stream should have content');
- CheckNotEquals(0,IndexStream.Size,'Index stream should have content');
- CheckNotEquals(0,MemoStream.Size,'Memo stream should have content');
- DS.Open;
- DS.First;
- for i := 1 to MaxRecs do
- begin
- CheckEquals(i,DS.fieldbyname('ID').asinteger);
- CheckEquals('TestName' + inttostr(i),DS.fieldbyname('NAME').AsString);
- DS.next;
- end;
- CheckTrue(DS.EOF,'After reading all records the dataset should show EOF');
- DS.Close;
- ds.free;
- DBFStream.Free;
- IndexStream.Free;
- MemoStream.Free;
- end;
- procedure TTestSpecificTDBF.TestLargeString;
- var
- ds : TDBF;
- MaxStringSize: integer;
- TestValue: string;
- begin
- ds := TDBFAutoClean.Create(nil);
- if (ds.TableLevel>=25) then
- // (Visual) FoxPro supports 32K
- MaxStringSize:=32767
- else
- // Dbase III..V,7
- MaxStringSize:=254;
- TestValue:=StringOfChar('a',MaxStringSize);
- DS.FieldDefs.Add('ID',ftInteger);
- DS.FieldDefs.Add('NAME',ftString,MaxStringSize);
- DS.CreateTable;
- DS.Open;
- // Write & readback test
- DS.Append;
- DS.FieldByName('ID').AsInteger := 1;
- DS.FieldByName('NAME').AsString := TestValue;
- DS.Post;
- DS.first;
- CheckEquals(1,DS.fieldbyname('ID').asinteger,'ID field must match record number');
- // If test fails, let's count the number of "a"s instead so we can report that instead of printing out the entire string
- CheckEquals(length(TestValue),length(DS.fieldbyname('NAME').AsString),'NAME field length must match test value length');
- CheckEquals(TestValue,DS.fieldbyname('NAME').AsString,'NAME field must match test value');
- DS.next;
- CheckTrue(DS.EOF,'Dataset EOF must be true');
- DS.Close;
- ds.free;
- end;
- procedure TTestSpecificTDBF.TestCodePage;
- const
- // Chose non-default (i.e. 437,850,1252) cps
- DOSCodePage=865; //Nordic ms dos
- DOSLanguageID=$66; //... corresponding language ID (according to VFP docs; other sources say $65)
- WindowsCodePage=1251; //Russian windows
- WindowsLanguageID=$C9; //.... corresponding language ID
- var
- RequestLanguageID: integer; //dbf language ID marker (byte 29)
- CorrespondingCodePage: integer;
- ds : TDBF;
- begin
- ds := TDBFAutoClean.Create(nil);
- if ((DS as TDBFAutoClean).UserRequestedTableLevel=25) then
- ignore('Foxpro (tablelevel 25) may write data out in dBase IV (tablelevel 4) format.');
- DS.FieldDefs.Add('ID',ftInteger);
- if ((DS as TDBFAutoClean).UserRequestedTableLevel in [7,30]) then
- begin
- RequestLanguageID:=WindowsLanguageID;
- CorrespondingCodePage:=WindowsCodePage //Visual FoxPro, DBase7
- end
- else
- begin
- RequestLanguageID:=DOSLanguageID;
- CorrespondingCodePage:=DOSCodePage;
- end;
- (DS as TDBFAutoClean).LanguageID:=RequestLanguageID;
- DS.CreateTable;
- DS.Open;
- CheckEquals(CorrespondingCodePage,DS.CodePage,'DBF codepage should match requested codeapage.');
- DS.Close;
- ds.free;
- end;
- initialization
- {$ifdef fpc}
- if uppercase(dbconnectorname)='DBF' then
- begin
- RegisterTestDecorator(TDBBasicsTestSetup, TTestSpecificTDBF);
- end;
- {$endif fpc}
- end.
|