blob: 25bf7dad4fc96a0087d7f6e7fc10f674dc867c01 [file] [log] [blame]
// Copyright 2019 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package testsharder
import (
"encoding/json"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"reflect"
"sort"
"strings"
"testing"
"go.fuchsia.dev/fuchsia/tools/build"
)
var barTestModifier = TestModifier{
Name: "bar_tests",
TotalRuns: 2,
}
var bazTestModifier = TestModifier{
Name: "baz_host_tests",
OS: linux,
}
func TestLoadTestModifiers(t *testing.T) {
areEqual := func(a, b []TestModifier) bool {
stringify := func(modifier TestModifier) string {
return fmt.Sprintf("%#v", modifier)
}
sort := func(list []TestModifier) {
sort.Slice(list[:], func(i, j int) bool {
return stringify(list[i]) < stringify(list[j])
})
}
sort(a)
sort(b)
return reflect.DeepEqual(a, b)
}
tmpDir := t.TempDir()
initial := []TestModifier{barTestModifier, bazTestModifier}
modifiersPath := filepath.Join(tmpDir, "test_modifiers.json")
m, err := os.Create(modifiersPath)
if err != nil {
t.Fatal(err)
}
defer m.Close()
if err := json.NewEncoder(m).Encode(&initial); err != nil {
t.Fatal(err)
}
actual, err := LoadTestModifiers(modifiersPath)
if err != nil {
t.Fatalf("failed to load test modifiers: %v", err)
}
bazOut := bazTestModifier
barOut := barTestModifier
barOut.OS = ""
expected := []TestModifier{barOut, bazOut}
if !areEqual(expected, actual) {
t.Fatalf("test modifiers not properly loaded:\nexpected:\n%+v\nactual:\n%+v", expected, actual)
}
}
func TestAffectedModifiers(t *testing.T) {
affectedTests := []string{
"affected-arm64", "affected-linux", "affected-mac", "affected-host+target", "affected-AEMU", "affected-other-device",
}
// Add a newline to the end of the file to test that it still calculates the
// correct number of affected tests even with extra whitespace.
affectedTestsFileContents := strings.Join(affectedTests, "\n") + "\n"
name := mkTempFile(t, affectedTestsFileContents)
const maxAttempts = 2
t.Run("not multiplied if over threshold", func(t *testing.T) {
mods, err := AffectedModifiers(nil, name, maxAttempts, len(affectedTests)-1)
if err != nil {
t.Errorf("AffectedModifiers() returned failed: %v", err)
}
for _, mod := range mods {
if mod.MaxAttempts != maxAttempts {
t.Errorf("%s.MaxAttempts is %d, want %d", mod.Name, mod.MaxAttempts, maxAttempts)
}
if !mod.Affected {
t.Errorf("%s.Affected is false, want true", mod.Name)
}
if mod.TotalRuns >= 0 {
t.Errorf("%s.TotalRuns is %d, want -1", mod.Name, mod.TotalRuns)
}
}
})
t.Run("multiplied", func(t *testing.T) {
specs := []build.TestSpec{
{Test: build.Test{Name: "affected-arm64", OS: linux, CPU: "arm64"}},
{Test: build.Test{Name: "affected-linux", OS: linux, CPU: x64}},
{Test: build.Test{Name: "affected-mac", OS: "mac", CPU: x64}},
{Test: build.Test{Name: "affected-host+target", OS: linux, CPU: x64},
Envs: []build.Environment{{Dimensions: build.DimensionSet{DeviceType: "AEMU"}}}},
{Test: build.Test{Name: "affected-AEMU", OS: fuchsia, CPU: x64},
Envs: []build.Environment{{Dimensions: build.DimensionSet{DeviceType: "AEMU"}}}},
{Test: build.Test{Name: "affected-other-device", OS: fuchsia, CPU: x64},
Envs: []build.Environment{{Dimensions: build.DimensionSet{DeviceType: "other-device"}}}},
{Test: build.Test{Name: "not-affected"}},
}
nameToShouldBeMultiplied := map[string]bool{
"affected-arm64": false,
"affected-linux": true,
"affected-mac": false,
"affected-host+target": false,
"affected-AEMU": true,
"affected-other-device": false,
}
mods, err := AffectedModifiers(specs, name, maxAttempts, len(affectedTests))
if err != nil {
t.Errorf("AffectedModifiers() returned failed: %v", err)
}
for _, mod := range mods {
shouldBeMultiplied := nameToShouldBeMultiplied[mod.Name]
if shouldBeMultiplied {
if mod.MaxAttempts != 0 {
t.Errorf("%s.MaxAttempts is %d, want 0", mod.Name, mod.MaxAttempts)
}
if mod.TotalRuns != 0 {
t.Errorf("%s.TotalRuns is %d, want 0", mod.Name, mod.MaxAttempts)
}
} else {
if mod.MaxAttempts != maxAttempts {
t.Errorf("%s.MaxAttempts is %d, want %d", mod.Name, mod.MaxAttempts, maxAttempts)
}
if !mod.Affected {
t.Errorf("%s.Affected is false, want true", mod.Name)
}
if mod.TotalRuns >= 0 {
t.Errorf("%s.TotalRuns is %d, want -1", mod.Name, mod.TotalRuns)
}
}
}
})
}
// mkTempFile returns a new temporary file with the specified content that will
// be cleaned up automatically.
func mkTempFile(t *testing.T, content string) string {
name := filepath.Join(t.TempDir(), "foo")
if err := ioutil.WriteFile(name, []byte(content), 0o600); err != nil {
t.Fatal(err)
}
return name
}