toolsunit.pas 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386
  1. unit ToolsUnit;
  2. {$IFDEF FPC}
  3. {$mode objfpc}{$H+}
  4. {$ENDIF}
  5. interface
  6. uses
  7. Classes, SysUtils, DB, testdecorator, FmtBCD;
  8. Const MaxDataSet = 35;
  9. type
  10. { TDBConnector }
  11. TDBConnectorClass = class of TDBConnector;
  12. TDBConnector = class(TPersistent)
  13. private
  14. FChangedDatasets : array[0..MaxDataSet] of boolean;
  15. FUsedDatasets : TFPList;
  16. FChangedFieldDataset : boolean;
  17. protected
  18. procedure SetTestUniDirectional(const AValue: boolean); virtual;
  19. function GetTestUniDirectional: boolean; virtual;
  20. // These methods should be implemented by any descendents
  21. // They are called eacht time a test need a TDataset descendent
  22. Function InternalGetNDataset(n : integer) : TDataset; virtual; abstract;
  23. Function InternalGetFieldDataset : TDataSet; virtual; abstract;
  24. // These methods should be implemented by any descendents
  25. // They are called only once in the constructor. They can be used
  26. // to create the tables on disk, or on a DB-Server
  27. procedure CreateNDatasets; virtual; abstract;
  28. procedure CreateFieldDataset; virtual; abstract;
  29. // These methods are called after each test in which a dataset is used
  30. // by calling GetXXXDataset with Achange=true
  31. // They should reset all data to their right/initial values.
  32. procedure ResetNDatasets; virtual;
  33. procedure ResetFieldDataset; virtual;
  34. // These methods are called only once in the destructor.
  35. // They should clean up all mess, like tables on disk or on a DB-server
  36. procedure DropNDatasets; virtual; abstract;
  37. procedure DropFieldDataset; virtual; abstract;
  38. public
  39. constructor create; virtual;
  40. destructor destroy; override;
  41. procedure DataEvent(dataset :TDataset);
  42. Function GetNDataset(n : integer) : TDataset; overload;
  43. Function GetNDataset(AChange : Boolean; n : integer) : TDataset; overload;
  44. Function GetFieldDataset : TDataSet; overload;
  45. Function GetFieldDataset(AChange : Boolean) : TDataSet; overload;
  46. Function GetTraceDataset(AChange : Boolean) : TDataset; virtual;
  47. procedure StartTest;
  48. procedure StopTest;
  49. property TestUniDirectional: boolean read GetTestUniDirectional write SetTestUniDirectional;
  50. end;
  51. { TDBBasicsTestSetup }
  52. TDBBasicsTestSetup = class(TTestSetup)
  53. protected
  54. procedure OneTimeSetup; override;
  55. procedure OneTimeTearDown; override;
  56. end;
  57. { TTestDataLink }
  58. TTestDataLink = class(TDataLink)
  59. protected
  60. procedure DataSetScrolled(Distance: Integer); override;
  61. procedure DataSetChanged; override;
  62. {$IFDEF fpc}
  63. procedure DataEvent(Event: TDataEvent; Info: Ptrint); override;
  64. {$ELSE}
  65. procedure DataEvent(Event: TDataEvent; Info: longint); override;
  66. {$ENDIF}
  67. end;
  68. const
  69. DataEventnames : Array [TDataEvent] of String[21] =
  70. ('deFieldChange', 'deRecordChange', 'deDataSetChange', 'deDataSetScroll',
  71. 'deLayoutChange', 'deUpdateRecord', 'deUpdateState', 'deCheckBrowseMode',
  72. 'dePropertyChange', 'deFieldListChange', 'deFocusControl' ,'deParentScroll',
  73. 'deConnectChange', 'deReconcileError', 'deDisabledStateChange');
  74. const
  75. testValuesCount = 25;
  76. testFloatValues : Array[0..testValuesCount-1] of double = (-maxSmallint-1,-maxSmallint,-256,-255,-128,-127,-1,0,1,127,128,255,256,maxSmallint,maxSmallint+1,0.123456,-0.123456,4.35,12.434E7,9.876e-5,123.45678,2.4,3.2,0.4,23);
  77. testCurrencyValues : Array[0..testValuesCount-1] of currency = (-100,-65.5,-54.34,-43.34,-2.50,-0.2,45.40,0.3,45.4,127,128,255,256,45,0.3,45.4,127,128,255,256,45,1234.56,43.23,43.43,99.88);
  78. testFmtBCDValues : Array[0..testValuesCount-1] of string = ('-100','-65.5','-54.3333','-43.3334','-2.5','-0.234567','45.4','0.3','45.414585','127','128','255','256','45','0.3','45.4','127','128','255','256','45','1234.56789','43.23','43.500001','99.88');
  79. testIntValues : Array[0..testValuesCount-1] of integer = (-maxInt,-maxInt+1,-maxSmallint-1,-maxSmallint,-256,-255,-128,-127,-1,0,1,127,128,255,256,maxSmallint,maxSmallint+1,MaxInt-1,MaxInt,100,130,150,-150,-132,234);
  80. testSmallIntValues : Array[0..testValuesCount-1] of smallint = (-maxSmallint,-maxSmallint+1,-256,-255,-128,-127,-1,0,1,127,128,255,256,maxSmallint,maxSmallint-1,100,110,120,130,150,-150,-132,234,231,42);
  81. testLargeIntValues : Array[0..testValuesCount-1] of LargeInt = ( -$7fffffffffffffff,-$7ffffffffffffffe,-maxInt-1,-maxInt+1,-maxSmallint,-maxSmallint+1,-256,-255,-128,-127,-1,0,1,127,128,255,256,maxSmallint,maxSmallint-1,maxSmallint+1,MaxInt-1,MaxInt,$7fffffffffffffff-1,$7fffffffffffffff,235253244);
  82. testBooleanValues : Array[0..testValuesCount-1] of boolean = (true,false,false,true,true,false,false,true,false,true,true,true,false,false,false,false,true,true,true,true,false,true,true,false,false);
  83. testStringValues : Array[0..testValuesCount-1] of string = (
  84. '',
  85. 'a',
  86. 'ab',
  87. 'abc',
  88. 'abcd',
  89. 'abcde',
  90. 'abcdef',
  91. 'abcdefg',
  92. 'abcdefgh',
  93. 'abcdefghi',
  94. 'abcdefghij',
  95. 'lMnOpQrStU',
  96. '1234567890',
  97. '_!@#$%^&*(',
  98. '_!@#$%^&*(',
  99. ' ''quotes'' ',
  100. ')-;:/?.<>',
  101. '~`|{}- =', // note that there's no \ (backslash) since some db's uses that as escape-character
  102. ' WRaP ',
  103. 'wRaP ',
  104. ' wRAP',
  105. 'this',
  106. // 'is',
  107. 'fun',
  108. 'VB7^',
  109. 'vdfbst'
  110. );
  111. testDateValues : Array[0..testValuesCount-1] of string = (
  112. '2000-01-01',
  113. '1999-12-31',
  114. '2004-02-29',
  115. '2004-03-01',
  116. '1991-02-28',
  117. '1991-03-01',
  118. '2040-10-16',
  119. '1977-09-29',
  120. '1800-03-30',
  121. '1650-05-10',
  122. '1754-06-04',
  123. '0904-04-12',
  124. '0199-07-09',
  125. '0001-01-01',
  126. '0031-11-02',
  127. '1899-12-29',
  128. '1899-12-30',
  129. '1899-12-31',
  130. '1977-09-29',
  131. '1917-12-29',
  132. '0079-11-29',
  133. '1997-11-29',
  134. '0001-01-01',
  135. '1997-11-29',
  136. '1900-01-01'
  137. );
  138. var dbtype,
  139. dbconnectorname,
  140. dbconnectorparams,
  141. dbname,
  142. dbuser,
  143. dbhostname,
  144. dbpassword,
  145. dbQuoteChars : string;
  146. DataEvents : string;
  147. DBConnector : TDBConnector;
  148. testValues : Array [TFieldType,0..testvaluescount -1] of string;
  149. procedure InitialiseDBConnector;
  150. procedure FreeDBConnector;
  151. implementation
  152. uses
  153. inifiles;
  154. var DBConnectorRefCount: integer;
  155. constructor TDBConnector.create;
  156. begin
  157. CreateFieldDataset;
  158. CreateNDatasets;
  159. FUsedDatasets := TFPList.Create;
  160. end;
  161. destructor TDBConnector.destroy;
  162. begin
  163. if assigned(FUsedDatasets) then FUsedDatasets.Destroy;
  164. DropNDatasets;
  165. DropFieldDataset;
  166. end;
  167. function TDBConnector.GetTestUniDirectional: boolean;
  168. begin
  169. result := false;
  170. end;
  171. procedure TDBConnector.SetTestUniDirectional(const AValue: boolean);
  172. begin
  173. raise exception.create('Connector does not support tests for unidirectional datasets');
  174. end;
  175. procedure TDBConnector.ResetNDatasets;
  176. begin
  177. DropNDatasets;
  178. CreateNDatasets;
  179. end;
  180. procedure TDBConnector.ResetFieldDataset;
  181. begin
  182. DropFieldDataset;
  183. CreateFieldDataset;
  184. end;
  185. procedure TDBConnector.DataEvent(dataset : tdataset);
  186. begin
  187. DataEvents := DataEvents + 'DataEvent' + ';';
  188. end;
  189. function TDBConnector.GetNDataset(n: integer): TDataset;
  190. begin
  191. Result := GetNDataset(False,n);
  192. end;
  193. procedure ReadIniFile;
  194. var IniFile : TIniFile;
  195. begin
  196. IniFile := TIniFile.Create(getcurrentdir + PathDelim + 'database.ini');
  197. dbtype:='';
  198. if Paramcount>0 then
  199. dbtype := ParamStr(1);
  200. if (dbtype='') or not inifile.SectionExists(dbtype) then
  201. dbtype := IniFile.ReadString('Database','Type','');
  202. dbconnectorname := IniFile.ReadString(dbtype,'Connector','');
  203. dbname := IniFile.ReadString(dbtype,'Name','');
  204. dbuser := IniFile.ReadString(dbtype,'User','');
  205. dbhostname := IniFile.ReadString(dbtype,'Hostname','');
  206. dbpassword := IniFile.ReadString(dbtype,'Password','');
  207. dbconnectorparams := IniFile.ReadString(dbtype,'ConnectorParams','');
  208. dbquotechars := IniFile.ReadString(dbtype,'QuoteChars','"');
  209. IniFile.Free;
  210. end;
  211. procedure InitialiseDBConnector;
  212. var DBConnectorClass : TPersistentClass;
  213. i : integer;
  214. begin
  215. if DBConnectorRefCount>0 then exit;
  216. testValues[ftString] := testStringValues;
  217. testValues[ftFixedChar] := testStringValues;
  218. testValues[ftFMTBcd] := testFmtBCDValues;
  219. for i := 0 to testValuesCount-1 do
  220. begin
  221. testValues[ftFloat,i] := FloatToStr(testFloatValues[i]);
  222. testValues[ftSmallint,i] := IntToStr(testSmallIntValues[i]);
  223. testValues[ftInteger,i] := IntToStr(testIntValues[i]);
  224. testValues[ftLargeint,i] := IntToStr(testLargeIntValues[i]);
  225. // The decimalseparator was set to a comma for currencies and to a dot for ftBCD values.
  226. // But why is not clear to me. For Postgres it works now, with a dot for both types.
  227. // DecimalSeparator:=',';
  228. DecimalSeparator:='.';
  229. testValues[ftCurrency,i] := CurrToStr(testCurrencyValues[i]);
  230. // DecimalSeparator:='.';
  231. testValues[ftBCD,i] := CurrToStr(testCurrencyValues[i]);
  232. testValues[ftDate,i] := DateToStr(StrToDate(testDateValues[i], 'yyyy/mm/dd', '-'));
  233. end;
  234. if dbconnectorname = '' then raise Exception.Create('There is no db-connector specified');
  235. DBConnectorClass := GetClass('T'+dbconnectorname+'DBConnector');
  236. if assigned(DBConnectorClass) then
  237. DBConnector := TDBConnectorClass(DBConnectorClass).create
  238. else Raise Exception.Create('Unknown db-connector specified');
  239. inc(DBConnectorRefCount);
  240. end;
  241. procedure FreeDBConnector;
  242. begin
  243. dec(DBConnectorRefCount);
  244. if DBConnectorRefCount=0 then
  245. FreeAndNil(DBConnector);
  246. end;
  247. { TTestDataLink }
  248. {$IFDEF FPC}
  249. procedure TTestDataLink.DataSetScrolled(Distance: Integer);
  250. begin
  251. DataEvents := DataEvents + 'DataSetScrolled' + ':' + inttostr(Distance) + ';';
  252. inherited DataSetScrolled(Distance);
  253. end;
  254. procedure TTestDataLink.DataSetChanged;
  255. begin
  256. DataEvents := DataEvents + 'DataSetChanged;';
  257. inherited DataSetChanged;
  258. end;
  259. procedure TTestDataLink.DataEvent(Event: TDataEvent; Info: Ptrint);
  260. {$ELSE}
  261. procedure TTestDataLink.DataEvent(Event: TDataEvent; Info: Longint);
  262. {$ENDIF}
  263. begin
  264. if Event <> deFieldChange then
  265. DataEvents := DataEvents + DataEventnames[Event] + ':' + inttostr(info) + ';'
  266. else
  267. DataEvents := DataEvents + DataEventnames[Event] + ':' + TField(info).FieldName + ';';
  268. inherited DataEvent(Event, Info);
  269. end;
  270. { TDBConnector }
  271. function TDBConnector.GetNDataset(AChange : Boolean; n: integer): TDataset;
  272. begin
  273. if AChange then FChangedDatasets[n] := True;
  274. Result := InternalGetNDataset(n);
  275. FUsedDatasets.Add(Result);
  276. end;
  277. function TDBConnector.GetFieldDataset: TDataSet;
  278. begin
  279. Result := GetFieldDataset(False);
  280. end;
  281. function TDBConnector.GetFieldDataset(AChange: Boolean): TDataSet;
  282. begin
  283. if AChange then FChangedFieldDataset := True;
  284. Result := InternalGetFieldDataset;
  285. FUsedDatasets.Add(Result);
  286. end;
  287. function TDBConnector.GetTraceDataset(AChange: Boolean): TDataset;
  288. begin
  289. result := GetNDataset(AChange,15);
  290. end;
  291. procedure TDBConnector.StartTest;
  292. begin
  293. // Do nothing?
  294. end;
  295. procedure TDBConnector.StopTest;
  296. var i : integer;
  297. ds : TDataset;
  298. begin
  299. for i := 0 to FUsedDatasets.Count -1 do
  300. begin
  301. ds := tdataset(FUsedDatasets[i]);
  302. if ds.active then ds.Close;
  303. ds.Free;
  304. end;
  305. FUsedDatasets.Clear;
  306. if FChangedFieldDataset then ResetFieldDataset;
  307. for i := 0 to MaxDataSet do if FChangedDatasets[i] then
  308. begin
  309. ResetNDatasets;
  310. fillchar(FChangedDatasets,sizeof(FChangedDatasets),ord(False));
  311. break;
  312. end;
  313. end;
  314. { TDBBasicsTestSetup }
  315. procedure TDBBasicsTestSetup.OneTimeSetup;
  316. begin
  317. InitialiseDBConnector;
  318. end;
  319. procedure TDBBasicsTestSetup.OneTimeTearDown;
  320. begin
  321. FreeDBConnector;
  322. end;
  323. initialization
  324. ReadIniFile;
  325. DBConnectorRefCount:=0;
  326. end.