blob: a2f47c9c41bcb1a2d5e8863666620fe2832d9121 [file] [log] [blame]
// Copyright 2022 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package priority_test
import (
"fmt"
"math"
"math/rand"
"testing"
"go.chromium.org/chromiumos/test/plan/internal/compatibility/priority"
"github.com/google/go-cmp/cmp"
"go.chromium.org/chromiumos/infra/proto/go/testplans"
)
func TestRandomWeighted_MixedPriorities(t *testing.T) {
boardPriorityList := &testplans.BoardPriorityList{
BoardPriorities: []*testplans.BoardPriority{
{
SkylabBoard: "boardA",
Priority: -100,
},
{
SkylabBoard: "boardB",
Priority: -99,
},
{
SkylabBoard: "boardC",
Priority: -50,
},
{
SkylabBoard: "boardD",
Priority: 100,
},
},
}
selector := priority.NewRandomWeightedSelector(
rand.New(rand.NewSource(7)), boardPriorityList,
)
counts := make(map[string]int)
for i := 0; i < 1000; i++ {
selected, err := selector.Select("DUT_POOL_QUOTA", []string{"boardA", "boardB", "boardC", "boardD", "undefinedBoard"})
if err != nil {
t.Fatal(err)
}
counts[selected] += 1
}
// The probabilities are computed from configured priorities as follows:
// 1. Flip signs: 100, 99, 50, -100.
// 2. Shift so minimum is 1 (and add default probability for the undefined
// board): 201, 200, 151, 1, 101
// 3. Divide by the sum (654): 0.3073, 0.3058, 0.2309, 0.0015, 0.1544
expected := map[string]int{
"boardA": 322,
"boardB": 303,
"boardC": 221,
"boardD": 5,
"undefinedBoard": 149,
}
if diff := cmp.Diff(expected, counts); diff != "" {
t.Errorf("Unexpected counts of chosen boards (-want +got): %s", diff)
}
}
func TestRandomWeighted_AllPositivePriorities(t *testing.T) {
boardPriorityList := &testplans.BoardPriorityList{
BoardPriorities: []*testplans.BoardPriority{
{
SkylabBoard: "boardA",
Priority: 100,
},
{
SkylabBoard: "boardB",
Priority: 50,
},
{
SkylabBoard: "boardC",
Priority: 10,
},
},
}
selector := priority.NewRandomWeightedSelector(
rand.New(rand.NewSource(7)), boardPriorityList,
)
counts := make(map[string]int)
for i := 0; i < 1000; i++ {
selected, err := selector.Select("DUT_POOL_QUOTA", []string{"boardA", "boardB", "boardC", "undefinedBoard"})
if err != nil {
t.Fatal(err)
}
counts[selected] += 1
}
// The probabilities are computed from configured priorities as follows:
// 1. Flip signs: -100, -50, -10
// 2. Shift so minimum is 1 (and add default probability for the undefined
// board): 1, 51, 91, 101
// 3. Divide by the sum (244): 0.0041, 0.2090, 0.3730, 0.4139
expected := map[string]int{
"boardA": 9,
"boardB": 202,
"boardC": 377,
"undefinedBoard": 412,
}
if diff := cmp.Diff(expected, counts); diff != "" {
t.Errorf("Unexpected counts of chosen boards (-want +got): %s", diff)
}
}
func TestRandomWeighted_AllNegativePriorities(t *testing.T) {
boardPriorityList := &testplans.BoardPriorityList{
BoardPriorities: []*testplans.BoardPriority{
{
SkylabBoard: "boardA",
Priority: -100,
},
{
SkylabBoard: "boardB",
Priority: -50,
},
{
SkylabBoard: "boardC",
Priority: -10,
},
},
}
selector := priority.NewRandomWeightedSelector(
rand.New(rand.NewSource(7)), boardPriorityList,
)
counts := make(map[string]int)
for i := 0; i < 1000; i++ {
selected, err := selector.Select("DUT_POOL_QUOTA", []string{"boardA", "boardB", "boardC", "undefinedBoard"})
if err != nil {
t.Fatal(err)
}
counts[selected] += 1
}
// The probabilities are computed from configured priorities as follows:
// 1. Flip signs: 100, 50, 10
// 2. Shift so minimum is 1 (and add default probability for the undefined
// board): 101, 51, 11, 1
// 3. Divide by the sum (164): 0.6159, 0.3110, 0.0671, 0.0061
expected := map[string]int{
"boardA": 629,
"boardB": 292,
"boardC": 68,
"undefinedBoard": 11,
}
if diff := cmp.Diff(expected, counts); diff != "" {
t.Errorf("Unexpected counts of chosen boards (-want +got): %s", diff)
}
}
func TestRandomWeighted_SingleBoard(t *testing.T) {
boardPriorityList := &testplans.BoardPriorityList{
BoardPriorities: []*testplans.BoardPriority{
{
SkylabBoard: "boardA",
Priority: -100,
},
{
SkylabBoard: "boardB",
Priority: -50,
},
{
SkylabBoard: "boardC",
Priority: -10,
},
},
}
selector := priority.NewRandomWeightedSelector(
rand.New(rand.NewSource(7)), boardPriorityList,
)
selected, err := selector.Select("DUT_POOL_QUOTA", []string{"boardA"})
if err != nil {
t.Fatal(err)
}
if selected != "boardA" {
t.Errorf("expected \"boardA\" to be selected, got %q", selected)
}
}
func TestRandomWeighted_NoBoards(t *testing.T) {
boardPriorityList := &testplans.BoardPriorityList{
BoardPriorities: []*testplans.BoardPriority{
{
SkylabBoard: "boardA",
Priority: -100,
},
{
SkylabBoard: "boardB",
Priority: -50,
},
{
SkylabBoard: "boardC",
Priority: -10,
},
},
}
selector := priority.NewRandomWeightedSelector(
rand.New(rand.NewSource(7)), boardPriorityList,
)
_, err := selector.Select("DUT_POOL_QUOTA", []string{})
if err == nil {
t.Error("expected error from calling Select with no boards.")
}
}
func TestRandomWeighted_LargePriorities(t *testing.T) {
boardPriorityList := &testplans.BoardPriorityList{
BoardPriorities: []*testplans.BoardPriority{
{
SkylabBoard: "boardA",
Priority: -int32(math.Pow(2, 30)),
},
{
SkylabBoard: "boardB",
Priority: -int32(math.Pow(2, 29)),
},
{
SkylabBoard: "boardC",
Priority: int32(math.Pow(2, 21)),
},
},
}
selector := priority.NewRandomWeightedSelector(
rand.New(rand.NewSource(7)), boardPriorityList,
)
counts := make(map[string]int)
for i := 0; i < 1000; i++ {
selected, err := selector.Select("DUT_POOL_QUOTA", []string{"boardA", "boardB", "boardC", "undefinedBoard"})
if err != nil {
t.Fatal(err)
}
counts[selected] += 1
}
// boardA should be chosen approximately twice as much as boardB. boardC
// should be chosen approximately 1 / 512 choices.
expected := map[string]int{
"boardA": 679,
"boardB": 318,
"undefinedBoard": 3,
}
if diff := cmp.Diff(expected, counts); diff != "" {
t.Errorf("Unexpected counts of chosen boards (-want +got): %s", diff)
}
}
func TestRandomWeighted_UnconfiguredPool(t *testing.T) {
boardPriorityList := &testplans.BoardPriorityList{
BoardPriorities: []*testplans.BoardPriority{
{
SkylabBoard: "boardA",
Priority: -500,
},
{
SkylabBoard: "boardB",
Priority: -100,
},
{
SkylabBoard: "boardC",
Priority: 1000,
},
},
}
selector := priority.NewRandomWeightedSelector(
rand.New(rand.NewSource(7)), boardPriorityList,
)
counts := make(map[string]int)
for i := 0; i < 1000; i++ {
selected, err := selector.Select("unconfiguredpool", []string{"boardA", "boardB", "boardC", "undefinedBoard"})
if err != nil {
t.Fatal(err)
}
counts[selected] += 1
}
// Since there is no configuration for the pool, every board gets default
// priority, and should be chosen equally.
expected := map[string]int{
"boardA": 247,
"boardB": 258,
"boardC": 239,
"undefinedBoard": 256,
}
if diff := cmp.Diff(expected, counts); diff != "" {
t.Errorf("Unexpected counts of chosen boards (-want +got): %s", diff)
}
}
func TestRandomWeighted_selectPriority(t *testing.T) {
boardPriorityList := &testplans.BoardPriorityList{
BoardPriorities: []*testplans.BoardPriority{
{
SkylabBoard: "boardA",
Priority: -100,
},
{
SkylabBoard: "boardB",
Priority: -99,
},
{
SkylabBoard: "boardC",
Priority: -50,
},
{
SkylabBoard: "boardD",
Priority: 100,
},
},
}
selector := priority.NewRandomWeightedSelector(
rand.New(rand.NewSource(7)), boardPriorityList,
)
counts := make(map[string]int)
for i := 0; i < 1000; i++ {
selected, err := selector.SelectPriority("DUT_POOL_QUOTA", []string{"boardA", "boardB", "boardC", "boardD", "boardC", "boardD", "undefinedBoard"})
if err != nil {
t.Fatal(err)
}
counts[fmt.Sprintf("%d_%s", selected.GetIndex(), selected.GetBoard())] += 1
}
// The probabilities are computed from configured priorities as follows:
// 1. Flip signs: 100, 99, 50, -100, 50, -100.
// 2. Shift so minimum is 1 (and add default probability for the undefined
// board): 201, 200, 151, 1, 151, 1, 101
// 3. Divide by the sum (806): 0.249, 0.248, 0.187, 0.001, 0.187, 0.001, 0.125
// Because the same random seed is used: intentional for reusing test results
// (http://shortn/_ULRFyxUaU8), the test will always return the same result.
expected := map[string]int{
"0_boardA": 256,
"1_boardB": 236,
"2_boardC": 183,
"3_boardD": 3,
"4_boardC": 202,
"5_boardD": 5,
"6_undefinedBoard": 115,
}
if diff := cmp.Diff(expected, counts); diff != "" {
t.Errorf("Unexpected counts of chosen boards (-want +got): %s", diff)
}
}