123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436 |
- unit digestanalyst;
- {$mode ObjFPC}{$H+}
- interface
- uses
- Classes, SysUtils, tsstring, tsutils, tstypes, tsdb;
- Type
- // Program configuration
- TDigestConfig = record
- databasename: string;
- host: string;
- username: string;
- password: string;
- port: integer;
- testsrcdir: string;
- relsrcdir: string;
- verbose: string;
- sql: string;
- end;
- { TDBDigestAnalyzer }
- TDBDigestAnalyzer = Class(TObject)
- private
- FDB : TTestSQL;
- LongLogFile : TStrings;
- UnknownLines : integer;
- UseLongLog : Boolean;
- FCurLongLogLine : Integer;
- FLongLogRestartCount : Integer;
- FPrefix : String;
- // Call global verbose with prefix to message.
- procedure Verbose(aLevel : TVerboseLevel; const aMsg : string);
- // Get the execute log for a given test
- function GetExecuteLog(Line, FN: String): String;
- // Get the IDs from all config parameters: OS, Log,
- function GetIDs(var aData: TTestRunData): Boolean;
- // Check that all IDS needed for a test run are <>-1
- function CheckIDs(var aData: TTestRunData): Boolean;
- // process a log file.
- procedure Processfile(const aFileName: String; var aData: TTestRunData);
- // Update the test run statistics.
- procedure UpdateTestRun(const aData: TTestRunData);
- // Get contents from longlog
- function GetContentsFromLongLog(Line: String; out IsFOund : Boolean): String;
- // Get Log from file line
- function GetLog(Line, FN: String): String;
- public
- constructor Create(aDB : TTestSQL; const aPrefix : String);
- // Extract the status from a log line. Will change the log line.
- class function AnalyseLine(var Line: string; var Status: TTestStatus): Boolean;
- // Extract test filename from a log line
- class procedure ExtractTestFileName(var Line: string);
- // Analyse the file.
- procedure Analyse(aConfig : TDigestConfig; aData : TTestRunData);
- // Save test result. Return true if a NEW test result record was created (and the result must be counted)
- function SaveTestResult(var aResult: TTestResultData): Boolean;
- // DB connection to use
- property DB : TTestSQL read FDB;
- end;
- implementation
- constructor TDBDigestAnalyzer.Create(aDB: TTestSQL; const aPrefix: String);
- begin
- FDB:=aDB;
- FPrefix:=aPrefix;
- end;
- procedure TDBDigestAnalyzer.Verbose(aLevel: TVerboseLevel; const aMsg: string);
- begin
- tsutils.Verbose(aLevel,FPrefix+aMsg);
- end;
- function TDBDigestAnalyzer.CheckIDs(var aData : TTestRunData): Boolean;
- begin
- If aData.CategoryID=-1 then
- aData.CategoryID:=1;
- Result:=(aData.CPUID<>-1) and (aData.OSID<>-1) and (aData.VersionID<>-1);
- if Result then
- exit;
- If aData.CPUID=-1 then
- Verbose(V_WARNING,'NO ID for CPU "'+aData.CPU+'" found.');
- If aData.OSID=-1 then
- Verbose(V_WARNING,'NO ID for OS "'+aData.OS+'" found.');
- If aData.VersionID=-1 then
- Verbose(V_WARNING,'NO ID for version "'+aData.Version+'" found.');
- end;
- procedure TDBDigestAnalyzer.Analyse(aConfig: TDigestConfig; aData : TTestRunData);
- begin
- FDB.RelSrcDir:=aConfig.relsrcdir;
- FDB.TestSrcDir:=aConfig.testsrcdir;
- if (aData.longlogfile<>'') and FileExists(aData.longlogfile) then
- begin
- LongLogFile:=TStringList.Create;
- LongLogFile.LoadFromFile(aData.longlogfile);
- UseLongLog:=LongLogFile.Count>0;
- end;
- if not GetIDS(aData) then
- exit;
- ProcessFile(aData.logfile,aData);
- UpdateTestRun(aData);
- end;
- function TDBDigestAnalyzer.GetIDs(var aData : TTestRunData): Boolean;
- begin
- Result := False;
- aData.CPUID := FDB.GetCPUID(aData.CPU);
- aData.OSID := FDB.GetOSID(aData.OS);
- aData.VersionID := FDB.GetVersionID(aData.Version);
- if aData.Category='' then
- aData.Category:='Compiler/RTL';
- aData.CategoryID := FDB.GetCategoryID(aData.Category);
- aData.PlatformID := FDB.GetPlatformID(aData,True);
- If (Round(aData.Date)=0) then
- aData.Date:=Date;
- Result:=CheckIDS(aData);
- if not Result then
- Exit;
- aData.RunID:=FDB.GetRunID(aData);
- If (aData.RunID<>-1) then
- FDB.CleanTestRun(aData.RunID)
- else
- aData.RunID:=FDB.AddRun(aData);
- Result:=aData.RunID<>-1;
- if not Result then
- begin
- Verbose(V_Error,'Could not insert new testrun record!');
- exit;
- end;
- end;
- class procedure TDBDigestAnalyzer.ExtractTestFileName(var Line: string);
- Var I : integer;
- begin
- I:=Pos(' ',Line);
- If (I<>0) then
- Line:=Copy(Line,1,I-1);
- end;
- class function TDBDigestAnalyzer.AnalyseLine(var Line: string; var Status: TTestStatus): Boolean;
- Var
- TS : TTestStatus;
- begin
- Result:=False;
- For TS:=FirstStatus to LastStatus do
- begin
- Result:=Pos(StatusText[TS],Line)=1;
- If Result then
- begin
- Status:=TS;
- Delete(Line,1,Length(StatusText[TS]));
- ExtractTestFileName(Line);
- Break;
- end;
- end;
- end;
- const
- SeparationLine = '>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>';
- function TDBDigestAnalyzer.GetContentsFromLongLog(Line: String; out IsFOund : Boolean): String;
- Function GetLongLogLine : String;
- begin
- Result:=LongLogFile[FCurLongLogLine];
- Inc(FCurLongLogLine);
- end;
- Function HaveLongLogLine : Boolean; inline;
- begin
- Result:=FCurLongLogLine<LongLogFile.Count;
- end;
- var
- S : String;
- IsFirst : boolean;
- InternalErrorPos : Integer;
- begin
- Result:='';
- IsFound:=False;
- { The "internalerror generated" message is not present in compilation log }
- InternalErrorPos:=pos(' internalerror generated',Line);
- if (InternalErrorPos>0) then
- begin
- Line:=Copy(Line,1,InternalErrorPos-1);
- end;IsFirst:=true;
- IsFound:=false;
- While HaveLongLogLine do
- begin
- S:=GetLongLogLine;
- if FCurLongLogLine=1 then
- begin
- { At start of file there is a separation line }
- if (pos(Line,S)=0) and (pos(SeparationLine,S)>=1) then
- S:=GetLongLogLine
- end;
- if pos(Line,S)>=1 then
- begin
- IsFound:=true;
- while HaveLongLogLine do
- begin
- S:=GetLongLogLine;
- { End of file marker }
- if (Not HaveLongLogLine) or (pos(SeparationLine,S)=1) then
- begin
- { Do not skip separation line, if it also contains something else }
- if HaveLongLogLine and (S<>SeparationLine) and (FCurlonglogline>0) then
- begin
- Verbose(V_Warning,'Line "'+S+'" is not a pure separation line');
- Dec(FCurlonglogline);
- end;
- exit;
- end;
- if length(Result)<MaxLogSize then
- Result:=Result+S+LineEnding;
- if pos(SeparationLine,S)>1 then
- exit;
- end;
- end
- else if IsFirst then
- begin
- Verbose(V_Warning,'Line "'+Line+'" not found as next "'+S+'"');
- IsFirst:=false;
- end;
- end;
- if not IsFound then
- begin
- Verbose(V_Warning,'Line "'+Line+'" not found. Starting over');
- FCurlongLogLine:=0; // Reset
- Inc(FLongLogRestartCount);
- end;
- end;
- function TDBDigestAnalyzer.GetLog(Line, FN: String): String;
- var
- IsFound : boolean;
- begin
- if UseLongLog then
- begin
- Result:=GetContentsFromLongLog(Line,IsFound);
- if not IsFound then
- Result:=GetContentsFromLongLog(Line,IsFound);
- exit;
- end;
- FN:=ChangeFileExt(FN,'.log');
- { packages tests have ../ replaced by root/ }
- if not FileExists(FN) and (Copy(FN,1,3)='../') then
- FN:='root/'+Copy(FN,4,length(FN));
- If FileExists(FN) then
- Result:=GetFileContents(FN)
- else
- begin
- Verbose(V_Warning,'File "'+FN+'" not found');
- Result:='';
- end;
- end;
- function TDBDigestAnalyzer.GetExecuteLog(Line, FN: String): String;
- var
- IsFound : Boolean;
- begin
- if UseLongLog then
- begin
- Result:=GetContentsFromLongLog(Line,IsFound);
- if not IsFound then
- Result:=GetContentsFromLongLog(Line,IsFound);
- exit;
- end;
- FN:=ChangeFileExt(FN,'.elg');
- { packages tests have ../ replaced by root/ }
- if not FileExists(FN) and (Copy(FN,1,3)='../') then
- FN:='root/'+Copy(FN,4,length(FN));
- If FileExists(FN) then
- Result:=GetFileContents(FN)
- else
- begin
- Verbose(V_Warning,'File "'+FN+'" not found');
- Result:='';
- end;
- end;
- function TDBDigestAnalyzer.SaveTestResult(var aResult: TTestResultData): Boolean;
- var
- lLast : TTestResultData;
- lNewID : Int64;
- begin
- Result:=False;
- // Get last result for this test.
- lLast:=FDB.GetLastTestResult(aResult.TestID,aResult.PlatformID);
- if (aResult.Date<lLast.Date) then
- exit; // Do not save earlier results
- if not aResult.ResultDiffers(lLast) then
- exit; // do not save identical results
- // Need to save.
- lNewID:=FDB.AddTestResult(aResult);
- aResult.ID:=lNewId;
- // Save current in lastresult
- Result:=(LLast.ID<>lNewID);
- if Result then
- begin
- // When new, save previous.
- FDB.AddLastResult(aResult.TestID,aResult.PlatformID,lNewID);
- FDB.AddPreviousResult(aResult.TestID,aResult.PlatformID,LLast.ID);
- end;
- end;
- procedure TDBDigestAnalyzer.Processfile(const aFileName: String; var aData: TTestRunData);
- var
- logfile : TStrings;
- fullline,line,prevLine : string;
- TS : TTestStatus;
- lPrev,lResult : TTestResultData;
- begin
- lPrev:=Default(TTestResultData);
- lResult:=Default(TTestResultData);
- // init data common to the whole testrun
- lResult.RunID:=aData.RunID;
- lResult.PlatFormID:=aData.PlatFormID;
- lResult.Date:=aData.Date;
- lPrev.RunID:=aData.RunID;
- lPrev.PlatformID:=aData.PlatformID;
- lPrev.TestID:=-1; // Init no test
- lPrev.Date:=aData.Date;
- for TS in TTestStatus do
- aData.StatusCount[TS]:=0;
- PrevLine:='';
- logfile:=TStringList.Create;
- try
- LogFile.Capacity:=20000;
- LogFile.LoadFromFile(aFileName);
- For FullLine in LogFile do
- begin
- line:=fullline;
- TS:=stInvalid;
- lResult.TestResult:=TS;
- If not AnalyseLine(line,TS) then
- begin
- Inc(UnknownLines);
- Verbose(V_Warning,'Unknown line: "'+fullline+'"');
- end
- else
- begin
- Verbose(V_NORMAL,'Analysing result for test '+fullLine);
- lResult.TestID:=FDB.RequireTestID(line);
- if lResult.TestID=-1 then
- begin
- Verbose(V_Warning,'No test ID: "'+fullline+'", skipping');
- Continue;
- end;
- If ExpectRun[TS] then
- begin
- { Count multiple compilation only once,
- will be decremented later unless test is orphan }
- if lPrev.TestID<>lResult.TestID then
- Inc(aData.StatusCount[TS]);
- // We expect a log line with log result, save info in lPrev
- lPrev.TestResult:=TS;
- lPrev.TestID:=lResult.TestID;
- PrevLine:=line;
- end
- else
- begin
- // New test, insert previous result
- if (lPrev.TestID<>-1)
- and ExpectRun[lPrev.TestResult]
- and (lPrev.TestID<>lResult.TestID) then
- begin
- { This can only happen if a Successfully compiled message
- is not followed by any other line about the same test }
- SaveTestResult(lPrev);
- Verbose(V_Warning,'Orphaned test: "'+prevline+'"');
- end;
- { Remove previous count if same test appears once more }
- if (lPrev.TestID<>-1) and (lPrev.TestID=lResult.TestID) then
- Dec(aData.StatusCount[lprev.testResult]);
- // same test, so now we have run result
- lPrev.TestID:=-1;
- lResult.TestResult:=TS;
- If (lResult.TestID<>-1) then
- begin
- If Not (TestOK[TS] or TestSkipped[TS]) then
- begin
- lResult.Log:=GetExecuteLog(Fullline,Line);
- if pos(failed_to_compile,lResult.Log)=1 then
- lResult.Log:=GetLog(Fullline,Line);
- end
- else
- lResult.Log:='';
- if SaveTestResult(lResult) then
- Verbose(V_Debug,'New result '+StatusText[lResult.TestResult]+' for line '+line);
- Inc(aData.StatusCount[TS]);
- lPrev.TestResult:=TS;
- lPrev.TestID:=lResult.TestID;
- end;
- end
- end
- end;
- finally
- Logfile.Free;
- end;
- end;
- procedure TDBDigestAnalyzer.UpdateTestRun(const aData : TTestRunData);
- begin
- FDB.UpdateTestRun(aData);
- end;
- end.
|