diff --git a/junit/junit.go b/junit/junit.go index be0313a3..65117a23 100644 --- a/junit/junit.go +++ b/junit/junit.go @@ -236,5 +236,27 @@ func formatDuration(d time.Duration) string { // formatOutput combines the lines from the given output into a single string. func formatOutput(output []string) string { - return strings.Join(output, "\n") + return escapeIllegalChars(strings.Join(output, "\n")) +} + +func escapeIllegalChars(str string) string { + return strings.Map(func(r rune) rune { + if isInCharacterRange(r) { + return r + } + return '�' + }, str) +} + +// Decide whether the given rune is in the XML Character Range, per +// the Char production of https://www.xml.com/axml/testaxml.htm, +// Section 2.2 Characters. +// Form: encoding/xml/xml.go +func isInCharacterRange(r rune) (inrange bool) { + return r == 0x09 || + r == 0x0A || + r == 0x0D || + r >= 0x20 && r <= 0xD7FF || + r >= 0xE000 && r <= 0xFFFD || + r >= 0x10000 && r <= 0x10FFFF } diff --git a/junit/junit_test.go b/junit/junit_test.go index a19478cb..4c51c764 100644 --- a/junit/junit_test.go +++ b/junit/junit_test.go @@ -26,6 +26,11 @@ func TestCreateFromReport(t *testing.T) { Result: gtr.Pass, Output: []string{"ok"}, }, + { + Name: "TestEscapeOutput", + Result: gtr.Pass, + Output: []string{"\x00\v\f \t\\"}, + }, { Name: "TestFail", Result: gtr.Fail, @@ -47,14 +52,14 @@ func TestCreateFromReport(t *testing.T) { } want := Testsuites{ - Tests: 6, + Tests: 7, Errors: 3, Failures: 1, Skipped: 1, Suites: []Testsuite{ { Name: "package/name", - Tests: 6, + Tests: 7, Errors: 3, ID: 0, Failures: 1, @@ -72,6 +77,12 @@ func TestCreateFromReport(t *testing.T) { Time: "0.000", SystemOut: &Output{Data: "ok"}, }, + { + Name: "TestEscapeOutput", + Classname: "package/name", + Time: "0.000", + SystemOut: &Output{Data: `��� \`}, + }, { Name: "TestFail", Classname: "package/name",