+ initial implementation of DB Unit Tests

git-svn-id: trunk@3484 -
This commit is contained in:
joost 2006-05-12 15:13:27 +00:00
parent 2341cae336
commit 396eca2908
10 changed files with 2571 additions and 0 deletions

9
.gitattributes vendored
View File

@ -752,6 +752,15 @@ fcl/db/unmaintained/odbc/testtl.pp svneol=native#text/plain
fcl/db/unmaintained/sqlite/Makefile -text
fcl/db/unmaintained/sqlite/Makefile.fpc -text
fcl/db/unmaintained/sqlite/sqlitedataset.pas svneol=native#text/plain
fcl/dbtests/Makefile -text
fcl/dbtests/Makefile.fpc -text
fcl/dbtests/database.ini -text
fcl/dbtests/dbftoolsunit.pas -text
fcl/dbtests/dbtestframework.pas -text
fcl/dbtests/sqldbtoolsunit.pas -text
fcl/dbtests/testdbbasics.pas -text
fcl/dbtests/testsqlfieldtypes.pas -text
fcl/dbtests/toolsunit.pas -text
fcl/fpcunit/Makefile svneol=native#text/plain
fcl/fpcunit/Makefile.fpc svneol=native#text/plain
fcl/fpcunit/README.txt svneol=native#text/plain

1738
fcl/dbtests/Makefile Normal file

File diff suppressed because it is too large Load Diff

16
fcl/dbtests/Makefile.fpc Normal file
View File

@ -0,0 +1,16 @@
#
# Makefile.fpc for DB TestFramework
#
[package]
main=fcl
[target]
examples=dbtestframework
[install]
fpcpackage=y
[default]
fpcdir=../..

39
fcl/dbtests/database.ini Normal file
View File

@ -0,0 +1,39 @@
[Database]
# type
# gives the type of the database-engine. Valid values are:
# * interbase
# * mysql40
# * mysql41
# * mysql50
# * odbc
# * postgresql
# * dbf
type=dbf
# name
# gives the name of the database that should be used.
# This could be a file-name or an alias, dependent on which database-engine is
# used. More information about how to create a dabatase can be find in the
# documentation of the database-engine.
name=/tmp
# user
# name is the name of a user which must have all rights on the selected
# database. If the user has insufficient rights, all or one of the test could
# fail.
# How to set up users and their rights can be found in the database-engine
# documentation.
#user=sysdba
# password
# password is the password of the provided user. If the password is incorrect,
# all or one of the test could fail.
#password=
#hostname=192.168.3.25

View File

@ -0,0 +1,73 @@
unit DBFToolsUnit;
{$IFDEF FPC}
{$mode objfpc}{$H+}
{$ENDIF}
interface
uses
Classes, SysUtils, toolsunit,
db,
Dbf, dbf_fields;
type
{ TDBFDBConnector }
TDBFDBConnector = class(TDBConnector)
private
protected
Function CreateNDataset(n : integer) : TDataset; override;
Procedure FreeNDataset(var ds : TDataset); override;
public
destructor Destroy; override;
end;
implementation
destructor TDBFDBConnector.Destroy;
begin
inherited Destroy;
end;
function TDBFDBConnector.CreateNDataset(n: integer): TDataset;
var countID : integer;
begin
with TDbf.Create(nil) do
begin
FilePath := dbname;
TableName := 'fpdev_'+inttostr(n)+'.db';
FieldDefs.Add('ID',ftInteger);
FieldDefs.Add('NAME',ftString,50);
CreateTable;
Open;
for countId := 1 to n do
begin
Append;
FieldByName('ID').AsInteger := countID;
FieldByName('NAME').AsString := 'TestName'+inttostr(countID);
end;
if state = dsinsert then
Post;
Close;
Free;
end;
// A dataset that has been opened and closed can't be used. Or else the tests
// for a newly generated dataset can't work properly.
Result := TDbf.Create(nil);
with (result as TDbf) do
begin
FilePath := dbname;
TableName := 'fpdev_'+inttostr(n)+'.db';
end;
end;
procedure TDBFDBConnector.FreeNDataset(var ds: TDataset);
begin
if ds.Active then ds.close;
FreeAndNil(ds);
end;
end.

View File

@ -0,0 +1,30 @@
program dbtestframework_console;
{$IFDEF FPC}
{$mode objfpc}{$H+}
{$ENDIF}
{$APPTYPE CONSOLE}
uses
SysUtils,
fpcunit,testregistry,testreport,
testdbbasics;
var
FXMLResultsWriter: TXMLResultsWriter;
testResult: TTestResult;
begin
testResult := TTestResult.Create;
FXMLResultsWriter := TXMLResultsWriter.Create;
try
testResult.AddListener(FXMLResultsWriter);
FXMLResultsWriter.WriteHeader;
GetTestRegistry.Run(testResult);
FXMLResultsWriter.WriteResult(testResult);
finally
testResult.Free;
FXMLResultsWriter.Free;
end;
end.

View File

@ -0,0 +1,153 @@
unit SQLDBToolsUnit;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils, toolsunit,
db,
sqldb, ibconnection, mysql40conn, mysql41conn, mysql50conn, pqconnection,odbcconn,oracleconnection;
type
{ TSQLDBConnector }
TSQLDBConnector = class(TDBConnector)
FConnection : TSQLConnection;
FTransaction : TSQLTransaction;
FQuery : TSQLQuery;
private
procedure CreateFConnection;
procedure CreateFTransaction;
Function CreateQuery : TSQLQuery;
protected
Procedure FreeNDataset(var ds : TDataset); override;
Function CreateNDataset(n : integer) : TDataset; override;
public
destructor Destroy; override;
constructor Create;
property Connection : TSQLConnection read FConnection;
property Transaction : TSQLTransaction read FTransaction;
property Query : TSQLQuery read FQuery;
end;
implementation
procedure TSQLDBConnector.CreateFConnection;
begin
if dbtype = 'mysql40' then Fconnection := tMySQL40Connection.Create(nil);
if dbtype = 'mysql41' then Fconnection := tMySQL41Connection.Create(nil);
if dbtype = 'mysql50' then Fconnection := tMySQL50Connection.Create(nil);
if dbtype = 'postgresql' then Fconnection := tpqConnection.Create(nil);
if dbtype = 'interbase' then Fconnection := tIBConnection.Create(nil);
if dbtype = 'odbc' then Fconnection := tODBCConnection.Create(nil);
if dbtype = 'oracle' then Fconnection := TOracleConnection.Create(nil);
if not assigned(Fconnection) then writeln('Invalid database-type, check if a valid database-type was provided in the file ''database.ini''');
with Fconnection do
begin
DatabaseName := dbname;
UserName := dbuser;
Password := dbpassword;
HostName := dbhostname;
open;
end;
end;
{ TSQLDBConnector }
procedure TSQLDBConnector.CreateFTransaction;
begin
Ftransaction := tsqltransaction.create(nil);
with Ftransaction do
database := Fconnection;
end;
Function TSQLDBConnector.CreateQuery : TSQLQuery;
begin
Result := TSQLQuery.create(nil);
with Result do
begin
database := Fconnection;
transaction := Ftransaction;
end;
end;
destructor TSQLDBConnector.Destroy;
begin
try
if Ftransaction.Active then Ftransaction.Rollback;
Ftransaction.StartTransaction;
Fconnection.ExecuteDirect('DROP TABLE FPDEV');
Ftransaction.Commit;
Except
if Ftransaction.Active then Ftransaction.Rollback
end;
try
if Ftransaction.Active then Ftransaction.Rollback;
Ftransaction.StartTransaction;
Fconnection.ExecuteDirect('DROP TABLE FPDEV2');
Ftransaction.Commit;
Except
if Ftransaction.Active then Ftransaction.Rollback
end;
FreeAndNil(FQuery);
FreeAndNil(FTransaction);
FreeAndNil(FConnection);
inherited Destroy;
end;
constructor TSQLDBConnector.Create;
var countID : integer;
begin
CreateFConnection;
CreateFTransaction;
FQuery := CreateQuery;
FConnection.Transaction := FTransaction;
try
Ftransaction.StartTransaction;
Fconnection.ExecuteDirect('create table FPDEV ( ' +
' ID INT NOT NULL, ' +
' NAME VARCHAR(50) ' +
') ');
FTransaction.CommitRetaining;
for countID := 1 to 35 do
Fconnection.ExecuteDirect('insert into FPDEV (ID,NAME)' +
'values ('+inttostr(countID)+',''TestName'+inttostr(countID)+''')');
Ftransaction.Commit;
except
if Ftransaction.Active then Ftransaction.Rollback
end;
end;
function TSQLDBConnector.CreateNDataset(n: integer): TDataset;
begin
result := CreateQuery;
with result as TSQLQuery do
begin
sql.clear;
sql.add('SELECT ID,NAME FROM FPDEV WHERE ID<'+inttostr(n+1));
end;
end;
procedure TSQLDBConnector.FreeNDataset(var ds: TDataset);
begin
if ds.active then ds.Close;
FreeAndNil(ds);
end;
end.

View File

@ -0,0 +1,284 @@
unit TestDBBasics;
{$IFDEF FPC}
{$mode objfpc}{$H+}
{$ENDIF}
interface
uses
fpcunit, testutils, testregistry, testdecorator,
Classes, SysUtils;
type
{ TTestSQLMechanism }
{ TTestDBBasics }
TTestDBBasics = class(TTestCase)
private
protected
procedure SetUp; override;
procedure TearDown; override;
procedure RunTest; override;
published
procedure TestSelectQueryBasics;
procedure TestPostOnlyInEditState;
procedure TestMove; // bug 5048
procedure TestActiveBufferWhenClosed;
procedure TestEOFBOFClosedDataset;
procedure TestdeFieldListChange;
procedure TestLastAppendCancel; // bug 5058
procedure TestRecNo; // bug 5061
end;
{ TSQLTestSetup }
TDBBasicsTestSetup = class(TTestSetup)
protected
procedure OneTimeSetup; override;
procedure OneTimeTearDown; override;
end;
implementation
uses db, toolsunit;
procedure TTestDBBasics.TestSelectQueryBasics;
var b : TFieldType;
begin
with DBConnector.GetNDataset(1) do
begin
Open;
AssertEquals(1,RecNo);
AssertEquals(1,RecordCount);
AssertEquals(2,FieldCount);
AssertEquals('ID',fields[0].FieldName);
AssertEquals('ID',fields[0].DisplayName); // uitzoeken verschil displaylabel
AssertTrue('The datatype of the field ''ID'' is incorrect, it should be ftInteger',ftInteger=fields[0].DataType);
AssertEquals('NAME',fields[1].FieldName);
AssertEquals('NAME',fields[1].DisplayName);
AssertTrue(ftString=fields[1].DataType);
AssertEquals(1,fields[0].Value);
AssertEquals('TestName1',fields[1].Value);
Close;
end;
end;
procedure TTestDBBasics.TestPostOnlyInEditState;
begin
with DBConnector.GetNDataset(1) do
begin
open;
{$IFDEF FPC}
AssertException('Post was called in a non-edit state',EDatabaseError,@Post);
{$ELSE}
AssertException('Post was called in a non-edit state',EDatabaseError,Post);
{$ENDIF}
end;
end;
procedure TTestDBBasics.TestMove;
var i,count : integer;
aDatasource : TDataSource;
aDatalink : TDataLink;
ABufferCount : Integer;
begin
aDatasource := TDataSource.Create(nil);
aDatalink := TTestDataLink.Create;
aDatalink.DataSource := aDatasource;
ABufferCount := 11;
aDatalink.BufferCount := ABufferCount;
DataEvents := '';
for count := 0 to 32 do with DBConnector.GetNDataset(count) do
begin
aDatasource.DataSet := DBConnector.GetNDataset(count);
i := 1;
Open;
AssertEquals('deUpdateState:0;',DataEvents);
DataEvents := '';
while not EOF do
begin
AssertEquals(i,fields[0].AsInteger);
AssertEquals('TestName'+inttostr(i),fields[1].AsString);
inc(i);
Next;
if (i > ABufferCount) and not EOF then
AssertEquals('deCheckBrowseMode:0;deDataSetScroll:-1;',DataEvents)
else
AssertEquals('deCheckBrowseMode:0;deDataSetScroll:0;',DataEvents);
DataEvents := '';
end;
AssertEquals(count,i-1);
close;
AssertEquals('deUpdateState:0;',DataEvents);
DataEvents := '';
end;
end;
procedure TTestDBBasics.TestdeFieldListChange;
var i,count : integer;
aDatasource : TDataSource;
aDatalink : TDataLink;
begin
aDatasource := TDataSource.Create(nil);
aDatalink := TTestDataLink.Create;
aDatalink.DataSource := aDatasource;
with DBConnector.GetNDataset(1) do
begin
aDatasource.DataSet := DBConnector.GetNDataset(1);
DataEvents := '';
open;
Fields.add(tfield.Create(DBConnector.GetNDataset(1)));
AssertEquals('deUpdateState:0;deFieldListChange:0;',DataEvents);
DataEvents := '';
fields.Clear;
AssertEquals('deFieldListChange:0;',DataEvents)
end;
aDatasource.Free;
aDatalink.Free;
end;
procedure TTestDBBasics.TestActiveBufferWhenClosed;
begin
with DBConnector.GetNDataset(0) do
begin
{$IFDEF fpc}
AssertNull(ActiveBuffer);
{$ENDIF}
open;
AssertFalse('Activebuffer of an empty dataset shouldn''t be nil',ActiveBuffer = nil);
end;
end;
procedure TTestDBBasics.TestEOFBOFClosedDataset;
begin
with DBConnector.GetNDataset(1) do
begin
AssertTrue(EOF);
AssertTrue(BOF);
open;
close;
AssertTrue(EOF);
AssertTrue(BOF);
end;
end;
procedure TTestDBBasics.TestLastAppendCancel;
var count : integer;
begin
for count := 0 to 32 do with DBConnector.GetNDataset(count) do
begin
open;
Last;
Append;
Cancel;
AssertEquals(count,fields[0].asinteger);
AssertEquals(count,RecordCount);
Close;
end;
end;
procedure TTestDBBasics.TestRecNo;
begin
with DBConnector.GetNDataset(0) do
begin
AssertEquals('Failed to get the RecNo from a closed dataset',0,RecNo);
AssertEquals(0,RecordCount);
Open;
AssertEquals(0,RecordCount);
AssertEquals(0,RecNo);
first;
AssertEquals(0,RecordCount);
AssertEquals(0,RecNo);
last;
AssertEquals(0,RecordCount);
AssertEquals(0,RecNo);
append;
AssertEquals(0,RecNo);
AssertEquals(0,RecordCount);
first;
AssertEquals(0,RecNo);
AssertEquals(0,RecordCount);
append;
FieldByName('id').AsInteger := 1;
AssertEquals(0,RecNo);
AssertEquals(0,RecordCount);
first;
AssertEquals(1,RecNo);
AssertEquals(1,RecordCount);
last;
AssertEquals(1,RecNo);
AssertEquals(1,RecordCount);
append;
FieldByName('id').AsInteger := 1;
AssertEquals(0,RecNo);
AssertEquals(1,RecordCount);
Close;
end;
end;
procedure TTestDBBasics.SetUp;
begin
DBConnector.InitialiseDatasets;
end;
procedure TTestDBBasics.TearDown;
var count : integer;
begin
DBConnector.FreeDatasets;
end;
procedure TTestDBBasics.RunTest;
begin
inherited RunTest;
// inherited RunTest;
// inherited RunTest;
end;
{ TSQLTestSetup }
procedure TDBBasicsTestSetup.OneTimeSetup;
begin
InitialiseDBConnector;
end;
procedure TDBBasicsTestSetup.OneTimeTearDown;
begin
FreeAndNil(DBConnector);
end;
initialization
RegisterTestDecorator(TDBBasicsTestSetup, TTestDBBasics);
end.

View File

@ -0,0 +1,91 @@
unit TestSQLFieldTypes;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils, fpcunit, testutils, testregistry;
type
{ TTestFieldTypes }
TTestFieldTypes= class(TTestCase)
protected
procedure SetUp; override;
procedure TearDown; override;
procedure RunTest; override;
published
procedure TestInt;
end;
implementation
uses db,sqldbtoolsunit,toolsunit;
procedure TTestFieldTypes.TestInt;
const
testValuesCount = 17;
testValues : Array[0..testValuesCount-1] of integer = (-maxInt,-maxSmallint-1,-maxSmallint,-256,-255,-128,-127,-1,0,1,127,128,255,256,maxSmallint,maxSmallint+1,MaxInt);
var
i : byte;
begin
TSQLDBConnector(DBConnector).Connection.ExecuteDirect('create table FPDEV2 (ID INT NOT NULL)');
// Firebird/Interbase need a commit after a DDL statement. Not necessary for the other connections
TSQLDBConnector(DBConnector).Transaction.CommitRetaining;
with TSQLDBConnector(DBConnector).Query do
begin
SQL.Clear;
SQL.Add('select * from FPDEV2');
Open;
AssertEquals(1,FieldCount);
AssertEquals('ID',fields[0].FieldName);
AssertEquals('ID',fields[0].DisplayName);
AssertEquals(4,fields[0].DataSize);
AssertTrue(ftInteger=fields[0].DataType);
Close;
for i := 0 to testValuesCount-1 do
TSQLDBConnector(DBConnector).Connection.ExecuteDirect('insert into FPDEV2 (id) values (' + inttostr(testValues[i]) + ')');
Open;
for i := 0 to testValuesCount-1 do
begin
AssertEquals(testValues[i],fields[0].Value);
Next;
end;
end;
TSQLDBConnector(DBConnector).Transaction.Rollback;
end;
procedure TTestFieldTypes.SetUp;
begin
InitialiseDBConnector;
end;
procedure TTestFieldTypes.TearDown;
begin
FreeAndNil(DBConnector);
end;
procedure TTestFieldTypes.RunTest;
begin
if dbtype = 'interbase' then
inherited RunTest;
end;
initialization
RegisterTest(TTestFieldTypes);
end.

138
fcl/dbtests/toolsunit.pas Normal file
View File

@ -0,0 +1,138 @@
unit ToolsUnit;
{$IFDEF FPC}
{$mode objfpc}{$H+}
{$DEFINE SQLDB_AVAILABLE}
{$DEFINE DBF_AVAILABLE}
{$ELSE}
{$DEFINE DBF_AVAILABLE}
{$ENDIF}
interface
uses
Classes, SysUtils, DB;
Const MaxDataSet = 35;
type
{ TDBConnector }
TDBConnector = class(TObject)
private
FDatasets : array[0..MaxDataset] of TDataset;
protected
Procedure FreeNDataset(var ds : TDataset); virtual; abstract;
Function CreateNDataset(n : integer) : TDataset; virtual; abstract;
public
Function GetNDataset(n : integer) : TDataset; virtual;
procedure InitialiseDatasets; virtual;
procedure FreeDatasets; virtual;
end;
{ TTestDataLink }
TTestDataLink = class(TDataLink)
protected
{$IFDEF FPC}
procedure DataEvent(Event: TDataEvent; Info: Ptrint); override;
{$ELSE}
procedure DataEvent(Event: TDataEvent; Info: longint); override;
{$ENDIF}
end;
const
DataEventnames : Array [TDataEvent] of String[19] =
('deFieldChange', 'deRecordChange', 'deDataSetChange',
'deDataSetScroll', 'deLayoutChange', 'deUpdateRecord', 'deUpdateState',
'deCheckBrowseMode', 'dePropertyChange', 'deFieldListChange', 'deFocusControl',
'deParentScroll','deConnectChange','deReconcileError','deDisabledStateChange');
var dbtype,
dbname,
dbuser,
dbhostname,
dbpassword : string;
DBConnector : TDBConnector;
DataEvents : string;
procedure InitialiseDBConnector;
implementation
uses
{$IFDEF SQLDB_AVAILABLE}
sqldbtoolsunit,
{$ENDIF}
{$IFDEF DBF_AVAILABLE}
dbftoolsunit,
{$ENDIF}
inifiles;
procedure ReadIniFile;
var IniFile : TIniFile;
begin
IniFile := TIniFile.Create(getcurrentdir + PathDelim + 'database.ini');
dbtype := IniFile.ReadString('Database','Type','');
dbname := IniFile.ReadString('Database','Name','');
dbuser := IniFile.ReadString('Database','User','');
dbhostname := IniFile.ReadString('Database','Hostname','');
dbpassword := IniFile.ReadString('Database','Password','');
IniFile.Free;
end;
procedure InitialiseDBConnector;
begin
ReadIniFile;
if (1 <> 1) then begin end
{$IFDEF SQLDB_AVAILABLE}
else if dbtype = 'interbase' then DBConnector := TSQLDBConnector.Create
{$ENDIF}
{$IFDEF DBF_AVAILABLE}
else if dbtype = 'dbf' then DBConnector := TDBFDBConnector.Create
{$ENDIF}
else Raise Exception.Create('Invalid database-type specified');
end;
{ TTestDataLink }
{$IFDEF FPC}
procedure TTestDataLink.DataEvent(Event: TDataEvent; Info: Ptrint);
{$ELSE}
procedure TTestDataLink.DataEvent(Event: TDataEvent; Info: Longint);
{$ENDIF}
begin
DataEvents := DataEvents + DataEventnames[Event] + ':' + inttostr(info) + ';';
inherited DataEvent(Event, Info);
end;
{ TDBConnector }
function TDBConnector.GetNDataset(n: integer): TDataset;
begin
Result := FDatasets[n];
end;
procedure TDBConnector.InitialiseDatasets;
var count : integer;
begin
for count := 0 to MaxDataSet do
FDatasets[count] := CreateNDataset(count);
end;
procedure TDBConnector.FreeDatasets;
var count : integer;
begin
for count := 0 to MaxDataSet do if assigned(FDatasets[count]) then
FreeNDataset(FDatasets[count]);
end;
end.