diff --git a/DUnitX.Loggers.XML.NUnit.pas b/DUnitX.Loggers.XML.NUnit.pas index acad666e..c9a04b5e 100644 --- a/DUnitX.Loggers.XML.NUnit.pas +++ b/DUnitX.Loggers.XML.NUnit.pas @@ -44,6 +44,57 @@ implementation uses TypInfo; +function IsValidXMLChar(wc: WideChar): Boolean; +begin + case Word(wc) of + $0009, $000A, $000C, $000D, + $0020..$D7FF, + $E000..$FFFD, // Standard Unicode chars below $FFFF + $D800..$DBFF, // High surrogate of Unicode character = $10000 - $10FFFF + $DC00..$DFFF: // Low surrogate of Unicode character = $10000 - $10FFFF + result := True; + else + result := False; + end; +end; + +function StripInvalidXML(const s: string): string; +var + i, count: Integer; +begin + count := Length(s); + setLength(result, count); + for i := 1 to Count do // Iterate + begin + if IsValidXMLChar(WideChar(s[i])) then + result[i] := s[i] + else + result[i] := ' '; + end; // for} +end; + +function EscapeForXML(const value: string; const isAttribute: boolean = True; const isCDATASection : Boolean = False): string; +begin + result := StripInvalidXML(value); + if isCDATASection then + begin + Result := StringReplace(Result, ']]>', ']>',[rfReplaceAll]); + exit; + end; + + //note we are avoiding replacing & with &amp; !! + Result := StringReplace(result, '&', '[[-xy-amp--]]',[rfReplaceAll]); + Result := StringReplace(result, '&', '&',[rfReplaceAll]); + Result := StringReplace(result, '[[-xy-amp--]]', '&amp;',[rfReplaceAll]); + Result := StringReplace(result, '<', '<',[rfReplaceAll]); + Result := StringReplace(result, '>', '>',[rfReplaceAll]); + + if isAttribute then + begin + Result := StringReplace(result, '''', ''',[rfReplaceAll]); + Result := StringReplace(result, '"', '"',[rfReplaceAll]); + end; +end; { TDUnitXXMLNUnitLogger } @@ -114,6 +165,7 @@ procedure LogFixture(const fixture : IFixtureResult; level : integer); sTime := Format('%.3f',[RunResults.Duration.TotalSeconds]); sDate := FormatDateTime('yyyy-MM-dd',RunResults.StartTime); + WriteXMLLine(''); WriteXMLLine(Format('', [sExeName,RunResults.TestCount,RunResults.ErrorCount,RunResults.FailureCount,RunResults.IgnoredCount,RunResults.IgnoredCount,sDate,sTime])); sExeName := ExtractFileName(sExeName); @@ -258,14 +310,14 @@ procedure TDUnitXXMLNUnitLogger.WriteTestResult(const testResult: ITestResult); Indent; WriteXMLLine(''); Indent; - WriteXMLLine(Format('',[testResult.Message])); + WriteXMLLine(Format('',[EscapeForXML(testResult.Message, False, True)])); Outdent; WriteXMLLine(''); Outdent; Indent; WriteXMLLine(''); Indent; - WriteXMLLine(Format('',[testResult.StackTrace])); + WriteXMLLine(Format('',[EscapeForXML(testResult.StackTrace, False, True)])); Outdent; WriteXMLLine(''); Outdent; @@ -279,7 +331,7 @@ procedure TDUnitXXMLNUnitLogger.WriteTestResult(const testResult: ITestResult); Indent; WriteXMLLine(''); Indent; - WriteXMLLine(Format('',[testResult.Message])); + WriteXMLLine(Format('',[EscapeForXML(testResult.Message, False, True)])); Outdent; WriteXMLLine(''); Outdent;