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