2
0

toolsunit.pas 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666
  1. unit ToolsUnit;
  2. {$IFDEF FPC}
  3. {$mode objfpc}{$H+}
  4. {$ENDIF}
  5. interface
  6. uses
  7. Classes, SysUtils, DB, testdecorator, fpcunit;
  8. Const
  9. // Number of "N" test datasets (as opposed to FieldDatasets) that will be created
  10. // The connectors should have these records prepared in their Create*Dataset procedures.
  11. MaxDataSet = 35;
  12. // Number of records in a trace dataset:
  13. NForTraceDataset = 15;
  14. type
  15. { TDBConnector }
  16. TDBConnectorClass = class of TDBConnector;
  17. TDBConnector = class(TPersistent)
  18. private
  19. FLogTimeFormat: TFormatSettings; //for error logging only
  20. FFormatSettings: TFormatSettings;
  21. FChangedFieldDataset : boolean;
  22. function GetCharSize: integer;
  23. protected
  24. FChangedDatasets : array[0..MaxDataSet] of boolean;
  25. FUsedDatasets : TFPList;
  26. procedure ClearDatasets; virtual;
  27. procedure SetTestUniDirectional(const AValue: boolean); virtual;
  28. function GetTestUniDirectional: boolean; virtual;
  29. // These methods should be implemented by all descendents
  30. // They are called each time a test needs a TDataset descendent
  31. // n: the dataset index to return (also number of records in set)
  32. // Presupposes that Create*Dataset(s) has been called already.
  33. Function InternalGetNDataset(n : integer) : TDataset; virtual; abstract;
  34. Function InternalGetFieldDataset : TDataSet; virtual; abstract;
  35. // These methods should be implemented by all descendents
  36. // They are called e.g. in the constructor. They can be used
  37. // to create the tables on disk, or on a DB server
  38. procedure CreateNDatasets; virtual; abstract;
  39. procedure CreateFieldDataset; virtual; abstract;
  40. // These methods are called after each test in which a dataset is used
  41. // by calling GetXXXDataset with Achange=true
  42. // They should reset all data to their right/initial values.
  43. procedure ResetNDatasets; virtual;
  44. procedure ResetFieldDataset; virtual;
  45. // These methods are called e.g. in the destructor.
  46. // They should clean up all mess, like tables on disk or on a DB server
  47. procedure DropNDatasets; virtual; abstract;
  48. procedure DropFieldDataset; virtual; abstract;
  49. // If logging is enabled, writes Message to log file and flushes
  50. // Logging uses tab-separated columns
  51. procedure LogMessage(Category,Message: string);
  52. public
  53. constructor Create; virtual;
  54. destructor Destroy; override;
  55. procedure DataEvent(dataset :TDataset);
  56. Function GetNDataset(n : integer) : TDataset; overload;
  57. Function GetNDataset(AChange : Boolean; n : integer) : TDataset; overload;
  58. Function GetFieldDataset : TDataSet; overload;
  59. Function GetFieldDataset(AChange : Boolean) : TDataSet; overload;
  60. // Gets a dataset that tracks calculation of calculated fields etc.
  61. Function GetTraceDataset(AChange : Boolean) : TDataset; virtual;
  62. // Run before a test is started
  63. procedure StartTest(TestName: string);
  64. // Run after a test is stopped
  65. procedure StopTest(TestName: string);
  66. property TestUniDirectional: boolean read GetTestUniDirectional write SetTestUniDirectional;
  67. property FormatSettings: TFormatSettings read FFormatSettings;
  68. property CharSize: integer read GetCharSize;
  69. end;
  70. { TTestDataLink }
  71. TTestDataLink = class(TDataLink)
  72. protected
  73. procedure DataSetScrolled(Distance: Integer); override;
  74. procedure DataSetChanged; override;
  75. {$IFDEF fpc}
  76. procedure DataEvent(Event: TDataEvent; Info: Ptrint); override;
  77. {$ELSE}
  78. procedure DataEvent(Event: TDataEvent; Info: longint); override;
  79. {$ENDIF}
  80. end;
  81. { TDBBasicsTestSetup }
  82. TDBBasicsTestSetup = class(TTestSetup)
  83. protected
  84. procedure OneTimeSetup; override;
  85. procedure OneTimeTearDown; override;
  86. end;
  87. { TDBBasicsTestCase }
  88. TDBBasicsTestCase = class(TTestCase)
  89. protected
  90. procedure SetUp; override;
  91. procedure TearDown; override;
  92. // Verify whether all values in FieldDataset are present and correct
  93. procedure CheckFieldDatasetValues(ADataSet: TDataSet);
  94. // Verify whether all values in NDataset are present and correct
  95. procedure CheckNDatasetValues(ADataSet: TDataSet; n: integer);
  96. end;
  97. const
  98. DataEventnames : Array [TDataEvent] of String[21] =
  99. ('deFieldChange', 'deRecordChange', 'deDataSetChange', 'deDataSetScroll',
  100. 'deLayoutChange', 'deUpdateRecord', 'deUpdateState', 'deCheckBrowseMode',
  101. 'dePropertyChange', 'deFieldListChange', 'deFocusControl' ,'deParentScroll',
  102. 'deConnectChange', 'deReconcileError', 'deDisabledStateChange');
  103. const
  104. testValuesCount = 25;
  105. testSingleValues : Array[0..testValuesCount-1] of single = (-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.4567,2.4,3.2,0.4,-2.3);
  106. 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.4567,2.4,3.2,0.4,23);
  107. testCurrencyValues : Array[0..testValuesCount-1] of currency = (-MaxLongInt-1,-MaxSmallint-1,-256,-255,-43.34,-2.5,-0.21,0,0.32,45.45,256,45,1234.56,12.34,0.12,MaxSmallInt+1,MaxLongInt+1,-6871947.67,68719476736,2748779069.44,922337203685.47,-92233720368547,99999999999999,-9223372036854.25,-9223372036854.7);
  108. 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');
  109. 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);
  110. testWordValues : Array[0..testValuesCount-1] of Word = (1,2,3,4,5,6,7,8,0,1,127,128,255,256,maxSmallint,maxSmallint+1,maxSmallInt-1,maxSmallInt,65535,100,130,150,151,132,234);
  111. 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);
  112. testByteValues: Array[0..testValuesCount-1] of Byte = (1,2,3,4,5,6,7,8,0,1,127,128,255,0,0,0,0,0,0,0,0,0,0,0,0);
  113. testShortintValues: Array[0..testValuesCount-1] of ShortInt = (1,2,3,4,5,6,7,8,0,1,-128,-127,-64,64,126,127,0,0,0,0,0,0,0,0,0);
  114. testLongWordValues : Array[0..testValuesCount-1] of LongWord = (1,2,3,4,5,6,7,8,0,1,$FF,$FFFF,$FFFFFF,$FFFFFFFF,0,0,0,0,0,0,0,0,0,0,0);
  115. 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);
  116. 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);
  117. testStringValues : Array[0..testValuesCount-1] of string = (
  118. '',
  119. 'a',
  120. 'ab',
  121. 'abc',
  122. 'abcd',
  123. 'abcde',
  124. 'abcdef',
  125. 'abcdefg',
  126. 'abcdefgh',
  127. 'abcdefghi',
  128. 'abcdefghij',
  129. 'lMnOpQrStU',
  130. '1234567890',
  131. '_!@#$%^&*(',
  132. '_!@#$%^&*(',
  133. ' ''quotes'' ',
  134. ')-;:/?.<>',
  135. '~`|{}- =', // note that there's no \ (backslash) since some db's uses that as escape-character
  136. ' WRaP ',
  137. 'wRaP ',
  138. ' wRAP',
  139. 'this',
  140. // 'is',
  141. 'fun',
  142. 'VB7^',
  143. 'vdfbst'
  144. );
  145. testDateValues : Array[0..testValuesCount-1] of string = (
  146. '2000-01-01',
  147. '1999-12-31',
  148. '2004-02-29',
  149. '2004-03-01',
  150. '1991-02-28',
  151. '1991-03-01',
  152. '1997-11-29',
  153. '2040-10-16',
  154. '1977-09-29',
  155. '1977-12-31',
  156. '1917-12-29',
  157. '1900-01-01',
  158. '1899-12-31',
  159. '1899-12-30',
  160. '1899-12-29',
  161. '1800-03-30',
  162. '1754-06-04',
  163. '1753-01-01',
  164. '1650-05-10',
  165. '0904-04-12',
  166. '0199-07-09',
  167. '0079-11-29',
  168. '0031-11-02',
  169. '0001-12-31',
  170. '0001-01-01'
  171. );
  172. testTimeValues : Array[0..testValuesCount-1] of string = (
  173. '10:45:12.000',
  174. '00:00:00.000',
  175. '24:00:00.000',
  176. '33:25:15.000',
  177. '04:59:16.000',
  178. '05:45:59.000',
  179. '11:45:12.000',
  180. '12:45:12.000',
  181. '14:45:14.000',
  182. '14:45:52.000',
  183. '15:35:12.000',
  184. '16:35:42.000',
  185. '16:45:12.000',
  186. '18:45:22.000',
  187. '19:45:12.000',
  188. '16:45:12.010',
  189. '13:55:12.200',
  190. '13:46:12.543',
  191. '15:35:12.000',
  192. '17:25:12.530',
  193. '19:45:12.003',
  194. '10:54:12.999',
  195. '12:25:12.000',
  196. '20:15:12.758',
  197. '23:59:59.000'
  198. );
  199. var dbtype,
  200. dbconnectorname,
  201. dbconnectorparams,
  202. dbname,
  203. dbuser,
  204. dbhostname,
  205. dbpassword,
  206. dbcharset,
  207. dblogfilename,
  208. dbQuoteChars : string;
  209. dblogfile : TextFile;
  210. DataEvents : string;
  211. DBConnector : TDBConnector;
  212. testValues : Array [TFieldType,0..testvaluescount -1] of string;
  213. procedure InitialiseDBConnector;
  214. procedure FreeDBConnector;
  215. function DateTimeToTimeString(d: tdatetime) : string;
  216. function TimeStringToDateTime(d: String): TDateTime;
  217. function StringToByteArray(const s: ansistring): Variant;
  218. implementation
  219. uses
  220. inifiles, FmtBCD, Variants;
  221. var DBConnectorRefCount: integer;
  222. { TDBConnector }
  223. constructor TDBConnector.Create;
  224. begin
  225. FFormatSettings.DecimalSeparator:='.';
  226. FFormatSettings.ThousandSeparator:=#0;
  227. FFormatSettings.DateSeparator:='-';
  228. FFormatSettings.TimeSeparator:=':';
  229. FFormatSettings.ShortDateFormat:='yyyy/mm/dd';
  230. FFormatSettings.LongTimeFormat:='hh:nn:ss.zzz';
  231. // Set up time format for logging output:
  232. // ISO 8601 type date string so logging is uniform across machines
  233. FLogTimeFormat.DecimalSeparator:='.';
  234. FLogTimeFormat.ThousandSeparator:=#0;
  235. FLogTimeFormat.DateSeparator:='-';
  236. FLogTimeFormat.TimeSeparator:=':';
  237. FLogTimeFormat.ShortDateFormat:='yyyy-mm-dd';
  238. FLogTimeFormat.LongTimeFormat:='hh:nn:ss';
  239. FUsedDatasets := TFPList.Create;
  240. CreateFieldDataset;
  241. CreateNDatasets;
  242. end;
  243. Procedure TDBConnector.ClearDatasets;
  244. begin
  245. DropNDatasets;
  246. DropFieldDataset;
  247. end;
  248. destructor TDBConnector.Destroy;
  249. begin
  250. if assigned(FUsedDatasets) then
  251. FUsedDatasets.Destroy;
  252. ClearDatasets;
  253. Inherited;
  254. end;
  255. function TDBConnector.GetTestUniDirectional: boolean;
  256. begin
  257. result := false;
  258. end;
  259. procedure TDBConnector.SetTestUniDirectional(const AValue: boolean);
  260. begin
  261. raise exception.create('Connector does not support tests for unidirectional datasets');
  262. end;
  263. procedure TDBConnector.DataEvent(dataset: TDataset);
  264. begin
  265. DataEvents := DataEvents + 'DataEvent' + ';';
  266. end;
  267. procedure TDBConnector.ResetNDatasets;
  268. begin
  269. DropNDatasets;
  270. CreateNDatasets;
  271. end;
  272. procedure TDBConnector.ResetFieldDataset;
  273. begin
  274. DropFieldDataset;
  275. CreateFieldDataset;
  276. end;
  277. function TDBConnector.GetNDataset(n: integer): TDataset;
  278. begin
  279. Result := GetNDataset(False,n);
  280. end;
  281. function TDBConnector.GetNDataset(AChange : Boolean; n: integer): TDataset;
  282. begin
  283. if AChange then FChangedDatasets[n] := True;
  284. Result := InternalGetNDataset(n);
  285. FUsedDatasets.Add(Result);
  286. end;
  287. function TDBConnector.GetFieldDataset: TDataSet;
  288. begin
  289. Result := GetFieldDataset(False);
  290. end;
  291. function TDBConnector.GetFieldDataset(AChange: Boolean): TDataSet;
  292. begin
  293. if AChange then FChangedFieldDataset := True;
  294. Result := InternalGetFieldDataset;
  295. FUsedDatasets.Add(Result);
  296. end;
  297. function TDBConnector.GetTraceDataset(AChange: Boolean): TDataset;
  298. begin
  299. result := GetNDataset(AChange,NForTraceDataset);
  300. end;
  301. procedure TDBConnector.StartTest(TestName: string);
  302. begin
  303. // Log if necessary
  304. LogMessage('Test','Starting test '+TestName);
  305. end;
  306. procedure TDBConnector.StopTest(TestName: string);
  307. var i : integer;
  308. ds : TDataset;
  309. begin
  310. LogMessage('Test','Stopping test '+TestName);
  311. for i := 0 to FUsedDatasets.Count -1 do
  312. begin
  313. ds := tdataset(FUsedDatasets[i]);
  314. if ds.active then ds.Close;
  315. ds.Free;
  316. end;
  317. FUsedDatasets.Clear;
  318. if FChangedFieldDataset then ResetFieldDataset;
  319. for i := 0 to MaxDataSet do if FChangedDatasets[i] then
  320. begin
  321. ResetNDatasets;
  322. fillchar(FChangedDatasets,sizeof(FChangedDatasets),ord(False));
  323. break;
  324. end;
  325. end;
  326. procedure TDBConnector.LogMessage(Category,Message: string);
  327. begin
  328. if dblogfilename<>'' then //double check: only if logging enabled
  329. begin
  330. try
  331. Message:=StringReplace(Message, #9, '\t', [rfReplaceAll, rfIgnoreCase]);
  332. Message:=StringReplace(Message, LineEnding, '\n', [rfReplaceAll, rfIgnoreCase]);
  333. writeln(dbLogFile, TimeToStr(Now(), FLogTimeFormat) + #9 +
  334. Category + #9 +
  335. Message);
  336. Flush(dbLogFile); //in case tests crash
  337. except
  338. // ignore log file errors
  339. end;
  340. end;
  341. end;
  342. function TDBConnector.GetCharSize: integer;
  343. begin
  344. case LowerCase(dbcharset) of
  345. 'utf8','utf-8','utf8mb4':
  346. Result := 4;
  347. else
  348. Result := 1;
  349. end;
  350. end;
  351. { TTestDataLink }
  352. procedure TTestDataLink.DataSetScrolled(Distance: Integer);
  353. begin
  354. DataEvents := DataEvents + 'DataSetScrolled' + ':' + inttostr(Distance) + ';';
  355. inherited DataSetScrolled(Distance);
  356. end;
  357. procedure TTestDataLink.DataSetChanged;
  358. begin
  359. DataEvents := DataEvents + 'DataSetChanged;';
  360. inherited DataSetChanged;
  361. end;
  362. {$IFDEF FPC}
  363. procedure TTestDataLink.DataEvent(Event: TDataEvent; Info: Ptrint);
  364. {$ELSE}
  365. procedure TTestDataLink.DataEvent(Event: TDataEvent; Info: Longint);
  366. {$ENDIF}
  367. begin
  368. if Event <> deFieldChange then
  369. DataEvents := DataEvents + DataEventnames[Event] + ':' + inttostr(info) + ';'
  370. else
  371. DataEvents := DataEvents + DataEventnames[Event] + ':' + TField(info).FieldName + ';';
  372. inherited DataEvent(Event, Info);
  373. end;
  374. { TDBBasicsTestSetup }
  375. procedure TDBBasicsTestSetup.OneTimeSetup;
  376. begin
  377. InitialiseDBConnector;
  378. end;
  379. procedure TDBBasicsTestSetup.OneTimeTearDown;
  380. begin
  381. FreeDBConnector;
  382. end;
  383. { TDBBasicsTestCase }
  384. procedure TDBBasicsTestCase.SetUp;
  385. begin
  386. inherited SetUp;
  387. DBConnector.StartTest(TestName);
  388. end;
  389. procedure TDBBasicsTestCase.TearDown;
  390. begin
  391. DBConnector.StopTest(TestName);
  392. inherited TearDown;
  393. end;
  394. procedure TDBBasicsTestCase.CheckFieldDatasetValues(ADataSet: TDataSet);
  395. var i: integer;
  396. begin
  397. with ADataSet do
  398. begin
  399. First;
  400. for i := 0 to testValuesCount-1 do
  401. begin
  402. CheckEquals(i, FieldByName('ID').AsInteger, 'ID');
  403. CheckEquals(testStringValues[i], FieldByName('FSTRING').AsString, 'FSTRING');
  404. CheckEquals(testIntValues[i], FieldByName('FINTEGER').AsInteger, 'FINTEGER');
  405. CheckEquals(testLargeIntValues[i], FieldByName('FLARGEINT').AsLargeInt, 'FLARGEINT');
  406. Next;
  407. end;
  408. CheckTrue(Eof, 'Eof');
  409. end;
  410. end;
  411. procedure TDBBasicsTestCase.CheckNDatasetValues(ADataSet: TDataSet; n: integer);
  412. var i: integer;
  413. begin
  414. with ADataSet do
  415. begin
  416. First;
  417. for i := 1 to n do
  418. begin
  419. CheckEquals(i, FieldByName('ID').AsInteger, 'ID');
  420. CheckEquals('TestName' + inttostr(i), FieldByName('NAME').AsString, 'NAME');
  421. Next;
  422. end;
  423. CheckTrue(Eof, 'Eof');
  424. end;
  425. end;
  426. procedure ReadIniFile;
  427. var IniFile : TIniFile;
  428. begin
  429. IniFile := TIniFile.Create(GetCurrentDir + PathDelim + 'database.ini');
  430. dbtype:='';
  431. if ParamCount>0 then
  432. dbtype := ParamStr(1);
  433. if (dbtype='') or not IniFile.SectionExists(dbtype) then
  434. dbtype := IniFile.ReadString('Database','Type','');
  435. dbconnectorname := IniFile.ReadString(dbtype,'Connector','');
  436. dbname := IniFile.ReadString(dbtype,'Name','');
  437. dbuser := IniFile.ReadString(dbtype,'User','');
  438. dbhostname := IniFile.ReadString(dbtype,'Hostname','');
  439. dbpassword := IniFile.ReadString(dbtype,'Password','');
  440. dbcharset := IniFile.ReadString(dbtype,'CharSet','');
  441. dbconnectorparams := IniFile.ReadString(dbtype,'ConnectorParams','');
  442. dblogfilename := IniFile.ReadString(dbtype,'LogFile','');
  443. dbquotechars := IniFile.ReadString(dbtype,'QuoteChars','"');
  444. IniFile.Free;
  445. end;
  446. procedure SetupLog;
  447. begin
  448. if dblogfilename<>'' then
  449. begin
  450. try
  451. AssignFile(dblogfile,dblogfilename);
  452. if not(FileExists(dblogfilename)) then
  453. begin
  454. ReWrite(dblogfile);
  455. CloseFile(dblogfile);
  456. end;
  457. Append(dblogfile);
  458. except
  459. dblogfilename:=''; //rest of code relies on this as a log switch
  460. end;
  461. end;
  462. end;
  463. procedure CloseLog;
  464. begin
  465. if dblogfilename<>'' then
  466. begin
  467. try
  468. CloseFile(dbLogFile);
  469. except
  470. // Ignore log file errors
  471. end;
  472. end;
  473. end;
  474. procedure InitialiseDBConnector;
  475. var DBConnectorClass : TPersistentClass;
  476. i : integer;
  477. FormatSettings : TFormatSettings;
  478. begin
  479. if DBConnectorRefCount>0 then exit;
  480. FormatSettings.DecimalSeparator:='.';
  481. FormatSettings.ThousandSeparator:=#0;
  482. testValues[ftString] := testStringValues;
  483. testValues[ftFixedChar] := testStringValues;
  484. testValues[ftTime] := testTimeValues;
  485. testValues[ftDate] := testDateValues;
  486. testValues[ftBlob] := testStringValues;
  487. testValues[ftMemo] := testStringValues;
  488. testValues[ftWideString] := testStringValues;
  489. testValues[ftWideMemo] := testStringValues;
  490. testValues[ftFMTBcd] := testFmtBCDValues;
  491. for i := 0 to testValuesCount-1 do
  492. begin
  493. testValues[ftBoolean,i] := BoolToStr(testBooleanValues[i], True);
  494. testValues[ftFloat,i] := FloatToStr(testFloatValues[i],FormatSettings);
  495. testValues[ftSmallint,i] := IntToStr(testSmallIntValues[i]);
  496. testValues[ftInteger,i] := IntToStr(testIntValues[i]);
  497. testValues[ftWord,i] := IntToStr(testWordValues[i]);
  498. testValues[ftLargeint,i] := IntToStr(testLargeIntValues[i]);
  499. testValues[ftLongWord,i] := IntToStr(testLongWordValues[i]);
  500. testValues[ftCurrency,i] := CurrToStr(testCurrencyValues[i],FormatSettings);
  501. testValues[ftBCD,i] := CurrToStr(testCurrencyValues[i],FormatSettings);
  502. // For date '0001-01-01' other time-part like '00:00:00' causes "Invalid variant type cast", because of < MinDateTime constant
  503. if (testDateValues[i]>'0001-01-01') and (testTimeValues[i]>='00:00:01') and (testTimeValues[i]<'24:00:00') then
  504. testValues[ftDateTime,i] := testDateValues[i] + ' ' + testTimeValues[i]
  505. else
  506. testValues[ftDateTime,i] := testDateValues[i];
  507. testValues[ftShortInt,i] := IntToStr(testShortIntValues[i]);
  508. testValues[ftByte,i] := IntToStr(testByteValues[i]);
  509. testValues[ftExtended,i] := FloatToStr(testFloatValues[i]);
  510. testValues[ftSingle,i] := FloatToStr(testSingleValues[i]);
  511. end;
  512. if dbconnectorname = '' then raise Exception.Create('There is no db connector specified');
  513. DBConnectorClass := GetClass('T'+dbconnectorname+'DBConnector');
  514. if assigned(DBConnectorClass) then
  515. DBConnector := TDBConnectorClass(DBConnectorClass).create
  516. else Raise Exception.Create('Unknown db connector specified: ' + 'T'+dbconnectorname+'DBConnector');
  517. inc(DBConnectorRefCount);
  518. end;
  519. procedure FreeDBConnector;
  520. begin
  521. dec(DBConnectorRefCount);
  522. if DBConnectorRefCount=0 then
  523. FreeAndNil(DBConnector);
  524. end;
  525. function DateTimeToTimeString(d: tdatetime): string;
  526. var
  527. millisecond: word;
  528. second : word;
  529. minute : word;
  530. hour : word;
  531. begin
  532. // Format the datetime in the format hh:nn:ss.zzz, where the hours can be bigger then 23.
  533. DecodeTime(d,hour,minute,second,millisecond);
  534. hour := hour + (trunc(d) * 24);
  535. result := Format('%.2d:%.2d:%.2d.%.3d',[hour,minute,second,millisecond]);
  536. end;
  537. function TimeStringToDateTime(d: String): TDateTime;
  538. var
  539. millisecond: word;
  540. second : word;
  541. minute : word;
  542. hour : word;
  543. days : word;
  544. begin
  545. // Convert the string in the format hh:nn:ss.zzz to a datetime.
  546. hour := strtoint(copy(d,1,2));
  547. minute := strtoint(copy(d,4,2));
  548. second := strtoint(copy(d,7,2));
  549. millisecond := strtoint(copy(d,10,3));
  550. days := hour div 24;
  551. hour := hour mod 24;
  552. result := ComposeDateTime(days,EncodeTime(hour,minute,second,millisecond));
  553. end;
  554. function StringToByteArray(const s: ansistring): Variant;
  555. var P: Pointer;
  556. Len: integer;
  557. begin
  558. Len := Length(s) * SizeOf(AnsiChar);
  559. Result := VarArrayCreate([0, Len-1], varByte);
  560. P := VarArrayLock(Result);
  561. try
  562. Move(s[1], P^, Len);
  563. finally
  564. VarArrayUnlock(Result);
  565. end;
  566. end;
  567. initialization
  568. ReadIniFile;
  569. SetupLog;
  570. DBConnectorRefCount:=0;
  571. finalization
  572. CloseLog;
  573. end.