Skip to content

Commit

Permalink
Make junitXMLTestReport output deterministic by iterating over a slic…
Browse files Browse the repository at this point in the history
…e instead of a map, add test
  • Loading branch information
SarahFrench committed Jan 10, 2025
1 parent 2bdb205 commit 4a06b78
Show file tree
Hide file tree
Showing 2 changed files with 112 additions and 1 deletion.
21 changes: 20 additions & 1 deletion internal/command/artifact/junit.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"bytes"
"encoding/xml"
"os"
"slices"
"strconv"
"strings"

Expand Down Expand Up @@ -98,7 +99,9 @@ func junitXMLTestReport(suite *moduletest.Suite) ([]byte, error) {
errorsName := xml.Name{Local: "errors"}

enc.EncodeToken(xml.StartElement{Name: suitesName})
for _, file := range suite.Files {

sortedFiles := suiteFilesAsSortedList(suite.Files) // to ensure consistent ordering in XML
for _, file := range sortedFiles {
// Each test file is modelled as a "test suite".

// First we'll count the number of tests and number of failures/errors
Expand Down Expand Up @@ -224,3 +227,19 @@ func junitXMLTestReport(suite *moduletest.Suite) ([]byte, error) {
enc.Close()
return buf.Bytes(), nil
}

func suiteFilesAsSortedList(files map[string]*moduletest.File) []*moduletest.File {
fileNames := make([]string, len(files))
i := 0
for k := range files {
fileNames[i] = k
i++
}
slices.Sort(fileNames)

sortedFiles := make([]*moduletest.File, len(files))
for i, name := range fileNames {
sortedFiles[i] = files[name]
}
return sortedFiles
}
92 changes: 92 additions & 0 deletions internal/command/artifact/junit_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
package artifact

import (
"testing"

"github.com/hashicorp/terraform/internal/moduletest"
)

func Test_suiteFilesAsSortedList(t *testing.T) {
cases := map[string]struct {
Suite *moduletest.Suite
ExpectedNames map[int]string
}{
"no test files": {
Suite: &moduletest.Suite{},
},
"3 test files ordered in map": {
Suite: &moduletest.Suite{
Status: moduletest.Skip,
Files: map[string]*moduletest.File{
"test_file_1.tftest.hcl": {
Name: "test_file_1.tftest.hcl",
Status: moduletest.Skip,
Runs: []*moduletest.Run{},
},
"test_file_2.tftest.hcl": {
Name: "test_file_2.tftest.hcl",
Status: moduletest.Skip,
Runs: []*moduletest.Run{},
},
"test_file_3.tftest.hcl": {
Name: "test_file_3.tftest.hcl",
Status: moduletest.Skip,
Runs: []*moduletest.Run{},
},
},
},
ExpectedNames: map[int]string{
0: "test_file_1.tftest.hcl",
1: "test_file_2.tftest.hcl",
2: "test_file_3.tftest.hcl",
},
},
"3 test files unordered in map": {
Suite: &moduletest.Suite{
Status: moduletest.Skip,
Files: map[string]*moduletest.File{
"test_file_3.tftest.hcl": {
Name: "test_file_3.tftest.hcl",
Status: moduletest.Skip,
Runs: []*moduletest.Run{},
},
"test_file_1.tftest.hcl": {
Name: "test_file_1.tftest.hcl",
Status: moduletest.Skip,
Runs: []*moduletest.Run{},
},
"test_file_2.tftest.hcl": {
Name: "test_file_2.tftest.hcl",
Status: moduletest.Skip,
Runs: []*moduletest.Run{},
},
},
},
ExpectedNames: map[int]string{
0: "test_file_1.tftest.hcl",
1: "test_file_2.tftest.hcl",
2: "test_file_3.tftest.hcl",
},
},
}

for tn, tc := range cases {
t.Run(tn, func(t *testing.T) {
list := suiteFilesAsSortedList(tc.Suite.Files)

if len(tc.ExpectedNames) != len(tc.Suite.Files) {
t.Fatalf("expected there to be %d items, got %d", len(tc.ExpectedNames), len(tc.Suite.Files))
}

if len(tc.ExpectedNames) == 0 {
return
}

for k, v := range tc.ExpectedNames {
if list[k].Name != v {
t.Fatalf("expected element %d in sorted list to be named %s, got %s", k, v, list[k].Name)
}
}
})
}
}

0 comments on commit 4a06b78

Please sign in to comment.