xref: /aosp_15_r20/cts/tests/mediapc/requirements/requirementsdata_test.go (revision b7c941bb3fa97aba169d73cee0bed2de8ac964bf)
1// Copyright (C) 2024 The Android Open Source Project
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package requirementsdata_test
16
17import (
18	"slices"
19	"testing"
20
21	"google.golang.org/protobuf/proto"
22
23	"google3/third_party/android/mediapc_requirements/requirements"
24	pb "cts/test/mediapc/requirements/requirements_go_proto"
25	"github.com/google/go-cmp/cmp"
26	"github.com/google/go-cmp/cmp/cmpopts"
27
28	_ "embed"
29)
30
31// MPC Requirements data from requirements.txtpb
32//
33//go:embed requirements.binbp
34var reqBinary []byte
35
36func TestUniqueRequirementIDs(t *testing.T) {
37	reqList := mustUnmarshalRequirementList(t)
38
39	// Requirement ids must be unique
40	ids := make(map[string]bool)
41	for _, req := range reqList.GetRequirements() {
42		id := req.GetId()
43		if ids[id] {
44			t.Errorf("requirement [%s] is a duplicate", id)
45		}
46		ids[id] = true
47	}
48}
49
50func TestUniqueRequirementNames(t *testing.T) {
51	reqList := mustUnmarshalRequirementList(t)
52
53	// Requirement names must be unique
54	nameToID := make(map[string]string) // name to id
55	for _, req := range reqList.GetRequirements() {
56		if req.HasName() {
57			name := req.GetName()
58			if nameToID[name] != "" {
59				t.Errorf("the name %q of requirement [%s] is a duplicate of requirement [%s]'s name", req.GetId(), name, nameToID[name])
60			}
61			nameToID[name] = req.GetId()
62		}
63	}
64
65}
66
67func TestAllTestConfigsSpecifiedAndUsed(t *testing.T) {
68	reqList := mustUnmarshalRequirementList(t)
69
70	for _, req := range reqList.GetRequirements() {
71		if !req.HasName() {
72			continue // Do not check requirements that are not implemented yet
73		}
74
75		t.Run(req.GetName(), func(t *testing.T) {
76
77			specifiedTestConfigs := []string{}
78			for id := range req.GetTestConfigs() {
79				specifiedTestConfigs = append(specifiedTestConfigs, id)
80			}
81
82			usedTestConfigs := []string{}
83			for _, spec := range req.GetSpecs() {
84				if !slices.Contains(usedTestConfigs, spec.GetTestConfigId()) {
85					usedTestConfigs = append(usedTestConfigs, spec.GetTestConfigId())
86				}
87			}
88
89			if diff := cmp.Diff(specifiedTestConfigs, usedTestConfigs, cmpopts.SortSlices(
90				func(a, b string) bool { return a < b })); diff != "" {
91				t.Errorf("Specified test configs do not match used test configs (-want +got):\n%s", diff)
92			}
93		})
94	}
95}
96
97func TestConfigMeasurementsValid(t *testing.T) {
98	reqList := mustUnmarshalRequirementList(t)
99
100	for _, req := range reqList.GetRequirements() {
101		if !req.HasName() {
102			continue // Do not check requirements that are not implemented yet
103		}
104
105		t.Run(req.GetName(), func(t *testing.T) {
106			for measurementName, measurement := range req.GetMeasurements() {
107				if measurement.GetComparison() != pb.Comparison_COMPARISON_CONFIG {
108					continue // Do not check measurements that are not config measurements
109				}
110
111				t.Run(measurementName, func(t *testing.T) {
112					measurementValues := make(map[string]*pb.RequiredValue)
113					for _, spec := range req.GetSpecs() {
114						val, ok := measurementValues[spec.GetTestConfigId()]
115						if !ok {
116							measurementValues[spec.GetTestConfigId()] = spec.GetRequiredValues()[measurementName]
117						} else if !proto.Equal(val, spec.GetRequiredValues()[measurementName]) {
118							t.Errorf("Test config [%s] has multiple different values for measurement [%s]: [%v] and [%v]", spec.GetTestConfigId(), measurementName, spec.GetRequiredValues()[measurementName], val)
119						}
120					}
121				})
122			}
123
124		})
125	}
126}
127
128func TestConfigVariantsValid(t *testing.T) {
129	reqList := mustUnmarshalRequirementList(t)
130
131	for _, req := range reqList.GetRequirements() {
132		if !req.HasName() {
133			continue // Do not check requirements that are not implemented yet
134		}
135
136		t.Run(req.GetName(), func(t *testing.T) {
137			for configID := range req.GetTestConfigs() {
138
139				// Check that all test configs have the same variants
140				t.Run(configID, func(t *testing.T) {
141					specToVariants := make(map[int64][]string)
142					for mpc, spec := range req.GetSpecs() {
143						if spec.GetTestConfigId() == configID {
144							specToVariants[mpc] = []string{}
145							for variantID := range spec.GetVariantSpecs() {
146								specToVariants[mpc] = append(specToVariants[mpc], variantID)
147							}
148						}
149					}
150
151					prev := []string{}
152					for _, variants := range specToVariants {
153						if len(prev) > 0 {
154							if diff := cmp.Diff(prev, variants, cmpopts.SortSlices(
155								func(a, b string) bool { return a < b })); diff != "" {
156								t.Errorf("Test config [%s] missing variants (-want +got):\n%s", configID, diff)
157							}
158						}
159						prev = variants
160					}
161				})
162			}
163		})
164	}
165}
166
167func TestProtoFieldNumbersAreUniqueAndValid(t *testing.T) {
168	reqList := mustUnmarshalRequirementList(t)
169
170	usedReqNumbers := make(map[int32]bool)
171	for _, req := range reqList.GetRequirements() {
172		for testConfigID, testConfig := range req.GetTestConfigs() {
173			if !testConfig.HasProtoFieldNumber() {
174				continue
175			}
176
177			if usedReqNumbers[testConfig.GetProtoFieldNumber()] {
178				t.Errorf("Test config [%s] has the same proto field number [%d] as another test config", testConfigID, testConfig.GetProtoFieldNumber())
179			} else if testConfig.GetProtoFieldNumber() <= 0 {
180				t.Errorf("Test config [%s] has an invalid proto field number [%d]", testConfigID, testConfig.GetProtoFieldNumber())
181			} else {
182				usedReqNumbers[testConfig.GetProtoFieldNumber()] = true
183			}
184		}
185
186		t.Run(req.GetId(), func(t *testing.T) {
187			usedMeasurementNumbers := make(map[int32]bool)
188
189			for _, measurement := range req.GetMeasurements() {
190				if !measurement.HasProtoFieldNumber() {
191					continue
192				}
193
194				if usedMeasurementNumbers[measurement.GetProtoFieldNumber()] {
195					t.Errorf("Measurement [%s] has the same proto field number [%d] as another measurement", measurement.GetId(), measurement.GetProtoFieldNumber())
196				} else if measurement.GetProtoFieldNumber() <= 2 {
197					t.Errorf("Measurement [%s] has an invalid proto field number [%d]", measurement.GetId(), measurement.GetProtoFieldNumber())
198				} else {
199					usedMeasurementNumbers[measurement.GetProtoFieldNumber()] = true
200				}
201			}
202		})
203	}
204}
205
206func mustUnmarshalRequirementList(t *testing.T) *pb.RequirementList {
207	t.Helper()
208	reqList, err := requirements.UnmarshalRequirementList(reqBinary)
209	if err != nil {
210		t.Fatalf("failed to unmarshal reqBinary: %v", err)
211	}
212	return reqList
213}
214