2
0

digestanalyst.pas 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436
  1. unit digestanalyst;
  2. {$mode ObjFPC}{$H+}
  3. interface
  4. uses
  5. Classes, SysUtils, tsstring, tsutils, tstypes, tsdb;
  6. Type
  7. // Program configuration
  8. TDigestConfig = record
  9. databasename: string;
  10. host: string;
  11. username: string;
  12. password: string;
  13. port: integer;
  14. testsrcdir: string;
  15. relsrcdir: string;
  16. verbose: string;
  17. sql: string;
  18. end;
  19. { TDBDigestAnalyzer }
  20. TDBDigestAnalyzer = Class(TObject)
  21. private
  22. FDB : TTestSQL;
  23. LongLogFile : TStrings;
  24. UnknownLines : integer;
  25. UseLongLog : Boolean;
  26. FCurLongLogLine : Integer;
  27. FLongLogRestartCount : Integer;
  28. FPrefix : String;
  29. // Call global verbose with prefix to message.
  30. procedure Verbose(aLevel : TVerboseLevel; const aMsg : string);
  31. // Get the execute log for a given test
  32. function GetExecuteLog(Line, FN: String): String;
  33. // Get the IDs from all config parameters: OS, Log,
  34. function GetIDs(var aData: TTestRunData): Boolean;
  35. // Check that all IDS needed for a test run are <>-1
  36. function CheckIDs(var aData: TTestRunData): Boolean;
  37. // process a log file.
  38. procedure Processfile(const aFileName: String; var aData: TTestRunData);
  39. // Update the test run statistics.
  40. procedure UpdateTestRun(const aData: TTestRunData);
  41. // Get contents from longlog
  42. function GetContentsFromLongLog(Line: String; out IsFOund : Boolean): String;
  43. // Get Log from file line
  44. function GetLog(Line, FN: String): String;
  45. public
  46. constructor Create(aDB : TTestSQL; const aPrefix : String);
  47. // Extract the status from a log line. Will change the log line.
  48. class function AnalyseLine(var Line: string; var Status: TTestStatus): Boolean;
  49. // Extract test filename from a log line
  50. class procedure ExtractTestFileName(var Line: string);
  51. // Analyse the file.
  52. procedure Analyse(aConfig : TDigestConfig; aData : TTestRunData);
  53. // Save test result. Return true if a NEW test result record was created (and the result must be counted)
  54. function SaveTestResult(var aResult: TTestResultData): Boolean;
  55. // DB connection to use
  56. property DB : TTestSQL read FDB;
  57. end;
  58. implementation
  59. constructor TDBDigestAnalyzer.Create(aDB: TTestSQL; const aPrefix: String);
  60. begin
  61. FDB:=aDB;
  62. FPrefix:=aPrefix;
  63. end;
  64. procedure TDBDigestAnalyzer.Verbose(aLevel: TVerboseLevel; const aMsg: string);
  65. begin
  66. tsutils.Verbose(aLevel,FPrefix+aMsg);
  67. end;
  68. function TDBDigestAnalyzer.CheckIDs(var aData : TTestRunData): Boolean;
  69. begin
  70. If aData.CategoryID=-1 then
  71. aData.CategoryID:=1;
  72. Result:=(aData.CPUID<>-1) and (aData.OSID<>-1) and (aData.VersionID<>-1);
  73. if Result then
  74. exit;
  75. If aData.CPUID=-1 then
  76. Verbose(V_WARNING,'NO ID for CPU "'+aData.CPU+'" found.');
  77. If aData.OSID=-1 then
  78. Verbose(V_WARNING,'NO ID for OS "'+aData.OS+'" found.');
  79. If aData.VersionID=-1 then
  80. Verbose(V_WARNING,'NO ID for version "'+aData.Version+'" found.');
  81. end;
  82. procedure TDBDigestAnalyzer.Analyse(aConfig: TDigestConfig; aData : TTestRunData);
  83. begin
  84. FDB.RelSrcDir:=aConfig.relsrcdir;
  85. FDB.TestSrcDir:=aConfig.testsrcdir;
  86. if (aData.longlogfile<>'') and FileExists(aData.longlogfile) then
  87. begin
  88. LongLogFile:=TStringList.Create;
  89. LongLogFile.LoadFromFile(aData.longlogfile);
  90. UseLongLog:=LongLogFile.Count>0;
  91. end;
  92. if not GetIDS(aData) then
  93. exit;
  94. ProcessFile(aData.logfile,aData);
  95. UpdateTestRun(aData);
  96. end;
  97. function TDBDigestAnalyzer.GetIDs(var aData : TTestRunData): Boolean;
  98. begin
  99. Result := False;
  100. aData.CPUID := FDB.GetCPUID(aData.CPU);
  101. aData.OSID := FDB.GetOSID(aData.OS);
  102. aData.VersionID := FDB.GetVersionID(aData.Version);
  103. if aData.Category='' then
  104. aData.Category:='Compiler/RTL';
  105. aData.CategoryID := FDB.GetCategoryID(aData.Category);
  106. aData.PlatformID := FDB.GetPlatformID(aData,True);
  107. If (Round(aData.Date)=0) then
  108. aData.Date:=Date;
  109. Result:=CheckIDS(aData);
  110. if not Result then
  111. Exit;
  112. aData.RunID:=FDB.GetRunID(aData);
  113. If (aData.RunID<>-1) then
  114. FDB.CleanTestRun(aData.RunID)
  115. else
  116. aData.RunID:=FDB.AddRun(aData);
  117. Result:=aData.RunID<>-1;
  118. if not Result then
  119. begin
  120. Verbose(V_Error,'Could not insert new testrun record!');
  121. exit;
  122. end;
  123. end;
  124. class procedure TDBDigestAnalyzer.ExtractTestFileName(var Line: string);
  125. Var I : integer;
  126. begin
  127. I:=Pos(' ',Line);
  128. If (I<>0) then
  129. Line:=Copy(Line,1,I-1);
  130. end;
  131. class function TDBDigestAnalyzer.AnalyseLine(var Line: string; var Status: TTestStatus): Boolean;
  132. Var
  133. TS : TTestStatus;
  134. begin
  135. Result:=False;
  136. For TS:=FirstStatus to LastStatus do
  137. begin
  138. Result:=Pos(StatusText[TS],Line)=1;
  139. If Result then
  140. begin
  141. Status:=TS;
  142. Delete(Line,1,Length(StatusText[TS]));
  143. ExtractTestFileName(Line);
  144. Break;
  145. end;
  146. end;
  147. end;
  148. const
  149. SeparationLine = '>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>';
  150. function TDBDigestAnalyzer.GetContentsFromLongLog(Line: String; out IsFOund : Boolean): String;
  151. Function GetLongLogLine : String;
  152. begin
  153. Result:=LongLogFile[FCurLongLogLine];
  154. Inc(FCurLongLogLine);
  155. end;
  156. Function HaveLongLogLine : Boolean; inline;
  157. begin
  158. Result:=FCurLongLogLine<LongLogFile.Count;
  159. end;
  160. var
  161. S : String;
  162. IsFirst : boolean;
  163. InternalErrorPos : Integer;
  164. begin
  165. Result:='';
  166. IsFound:=False;
  167. { The "internalerror generated" message is not present in compilation log }
  168. InternalErrorPos:=pos(' internalerror generated',Line);
  169. if (InternalErrorPos>0) then
  170. begin
  171. Line:=Copy(Line,1,InternalErrorPos-1);
  172. end;IsFirst:=true;
  173. IsFound:=false;
  174. While HaveLongLogLine do
  175. begin
  176. S:=GetLongLogLine;
  177. if FCurLongLogLine=1 then
  178. begin
  179. { At start of file there is a separation line }
  180. if (pos(Line,S)=0) and (pos(SeparationLine,S)>=1) then
  181. S:=GetLongLogLine
  182. end;
  183. if pos(Line,S)>=1 then
  184. begin
  185. IsFound:=true;
  186. while HaveLongLogLine do
  187. begin
  188. S:=GetLongLogLine;
  189. { End of file marker }
  190. if (Not HaveLongLogLine) or (pos(SeparationLine,S)=1) then
  191. begin
  192. { Do not skip separation line, if it also contains something else }
  193. if HaveLongLogLine and (S<>SeparationLine) and (FCurlonglogline>0) then
  194. begin
  195. Verbose(V_Warning,'Line "'+S+'" is not a pure separation line');
  196. Dec(FCurlonglogline);
  197. end;
  198. exit;
  199. end;
  200. if length(Result)<MaxLogSize then
  201. Result:=Result+S+LineEnding;
  202. if pos(SeparationLine,S)>1 then
  203. exit;
  204. end;
  205. end
  206. else if IsFirst then
  207. begin
  208. Verbose(V_Warning,'Line "'+Line+'" not found as next "'+S+'"');
  209. IsFirst:=false;
  210. end;
  211. end;
  212. if not IsFound then
  213. begin
  214. Verbose(V_Warning,'Line "'+Line+'" not found. Starting over');
  215. FCurlongLogLine:=0; // Reset
  216. Inc(FLongLogRestartCount);
  217. end;
  218. end;
  219. function TDBDigestAnalyzer.GetLog(Line, FN: String): String;
  220. var
  221. IsFound : boolean;
  222. begin
  223. if UseLongLog then
  224. begin
  225. Result:=GetContentsFromLongLog(Line,IsFound);
  226. if not IsFound then
  227. Result:=GetContentsFromLongLog(Line,IsFound);
  228. exit;
  229. end;
  230. FN:=ChangeFileExt(FN,'.log');
  231. { packages tests have ../ replaced by root/ }
  232. if not FileExists(FN) and (Copy(FN,1,3)='../') then
  233. FN:='root/'+Copy(FN,4,length(FN));
  234. If FileExists(FN) then
  235. Result:=GetFileContents(FN)
  236. else
  237. begin
  238. Verbose(V_Warning,'File "'+FN+'" not found');
  239. Result:='';
  240. end;
  241. end;
  242. function TDBDigestAnalyzer.GetExecuteLog(Line, FN: String): String;
  243. var
  244. IsFound : Boolean;
  245. begin
  246. if UseLongLog then
  247. begin
  248. Result:=GetContentsFromLongLog(Line,IsFound);
  249. if not IsFound then
  250. Result:=GetContentsFromLongLog(Line,IsFound);
  251. exit;
  252. end;
  253. FN:=ChangeFileExt(FN,'.elg');
  254. { packages tests have ../ replaced by root/ }
  255. if not FileExists(FN) and (Copy(FN,1,3)='../') then
  256. FN:='root/'+Copy(FN,4,length(FN));
  257. If FileExists(FN) then
  258. Result:=GetFileContents(FN)
  259. else
  260. begin
  261. Verbose(V_Warning,'File "'+FN+'" not found');
  262. Result:='';
  263. end;
  264. end;
  265. function TDBDigestAnalyzer.SaveTestResult(var aResult: TTestResultData): Boolean;
  266. var
  267. lLast : TTestResultData;
  268. lNewID : Int64;
  269. begin
  270. Result:=False;
  271. // Get last result for this test.
  272. lLast:=FDB.GetLastTestResult(aResult.TestID,aResult.PlatformID);
  273. if (aResult.Date<lLast.Date) then
  274. exit; // Do not save earlier results
  275. if not aResult.ResultDiffers(lLast) then
  276. exit; // do not save identical results
  277. // Need to save.
  278. lNewID:=FDB.AddTestResult(aResult);
  279. aResult.ID:=lNewId;
  280. // Save current in lastresult
  281. Result:=(LLast.ID<>lNewID);
  282. if Result then
  283. begin
  284. // When new, save previous.
  285. FDB.AddLastResult(aResult.TestID,aResult.PlatformID,lNewID);
  286. FDB.AddPreviousResult(aResult.TestID,aResult.PlatformID,LLast.ID);
  287. end;
  288. end;
  289. procedure TDBDigestAnalyzer.Processfile(const aFileName: String; var aData: TTestRunData);
  290. var
  291. logfile : TStrings;
  292. fullline,line,prevLine : string;
  293. TS : TTestStatus;
  294. lPrev,lResult : TTestResultData;
  295. begin
  296. lPrev:=Default(TTestResultData);
  297. lResult:=Default(TTestResultData);
  298. // init data common to the whole testrun
  299. lResult.RunID:=aData.RunID;
  300. lResult.PlatFormID:=aData.PlatFormID;
  301. lResult.Date:=aData.Date;
  302. lPrev.RunID:=aData.RunID;
  303. lPrev.PlatformID:=aData.PlatformID;
  304. lPrev.TestID:=-1; // Init no test
  305. lPrev.Date:=aData.Date;
  306. for TS in TTestStatus do
  307. aData.StatusCount[TS]:=0;
  308. PrevLine:='';
  309. logfile:=TStringList.Create;
  310. try
  311. LogFile.Capacity:=20000;
  312. LogFile.LoadFromFile(aFileName);
  313. For FullLine in LogFile do
  314. begin
  315. line:=fullline;
  316. TS:=stInvalid;
  317. lResult.TestResult:=TS;
  318. If not AnalyseLine(line,TS) then
  319. begin
  320. Inc(UnknownLines);
  321. Verbose(V_Warning,'Unknown line: "'+fullline+'"');
  322. end
  323. else
  324. begin
  325. Verbose(V_NORMAL,'Analysing result for test '+fullLine);
  326. lResult.TestID:=FDB.RequireTestID(line);
  327. if lResult.TestID=-1 then
  328. begin
  329. Verbose(V_Warning,'No test ID: "'+fullline+'", skipping');
  330. Continue;
  331. end;
  332. If ExpectRun[TS] then
  333. begin
  334. { Count multiple compilation only once,
  335. will be decremented later unless test is orphan }
  336. if lPrev.TestID<>lResult.TestID then
  337. Inc(aData.StatusCount[TS]);
  338. // We expect a log line with log result, save info in lPrev
  339. lPrev.TestResult:=TS;
  340. lPrev.TestID:=lResult.TestID;
  341. PrevLine:=line;
  342. end
  343. else
  344. begin
  345. // New test, insert previous result
  346. if (lPrev.TestID<>-1)
  347. and ExpectRun[lPrev.TestResult]
  348. and (lPrev.TestID<>lResult.TestID) then
  349. begin
  350. { This can only happen if a Successfully compiled message
  351. is not followed by any other line about the same test }
  352. SaveTestResult(lPrev);
  353. Verbose(V_Warning,'Orphaned test: "'+prevline+'"');
  354. end;
  355. { Remove previous count if same test appears once more }
  356. if (lPrev.TestID<>-1) and (lPrev.TestID=lResult.TestID) then
  357. Dec(aData.StatusCount[lprev.testResult]);
  358. // same test, so now we have run result
  359. lPrev.TestID:=-1;
  360. lResult.TestResult:=TS;
  361. If (lResult.TestID<>-1) then
  362. begin
  363. If Not (TestOK[TS] or TestSkipped[TS]) then
  364. begin
  365. lResult.Log:=GetExecuteLog(Fullline,Line);
  366. if pos(failed_to_compile,lResult.Log)=1 then
  367. lResult.Log:=GetLog(Fullline,Line);
  368. end
  369. else
  370. lResult.Log:='';
  371. if SaveTestResult(lResult) then
  372. Verbose(V_Debug,'New result '+StatusText[lResult.TestResult]+' for line '+line);
  373. Inc(aData.StatusCount[TS]);
  374. lPrev.TestResult:=TS;
  375. lPrev.TestID:=lResult.TestID;
  376. end;
  377. end
  378. end
  379. end;
  380. finally
  381. Logfile.Free;
  382. end;
  383. end;
  384. procedure TDBDigestAnalyzer.UpdateTestRun(const aData : TTestRunData);
  385. begin
  386. FDB.UpdateTestRun(aData);
  387. end;
  388. end.