1*9e94795aSAndroid Build Coastguard Worker// Copyright 2021 Google LLC 2*9e94795aSAndroid Build Coastguard Worker// 3*9e94795aSAndroid Build Coastguard Worker// Licensed under the Apache License, Version 2.0 (the "License"); 4*9e94795aSAndroid Build Coastguard Worker// you may not use this file except in compliance with the License. 5*9e94795aSAndroid Build Coastguard Worker// You may obtain a copy of the License at 6*9e94795aSAndroid Build Coastguard Worker// 7*9e94795aSAndroid Build Coastguard Worker// http://www.apache.org/licenses/LICENSE-2.0 8*9e94795aSAndroid Build Coastguard Worker// 9*9e94795aSAndroid Build Coastguard Worker// Unless required by applicable law or agreed to in writing, software 10*9e94795aSAndroid Build Coastguard Worker// distributed under the License is distributed on an "AS IS" BASIS, 11*9e94795aSAndroid Build Coastguard Worker// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12*9e94795aSAndroid Build Coastguard Worker// See the License for the specific language governing permissions and 13*9e94795aSAndroid Build Coastguard Worker// limitations under the License. 14*9e94795aSAndroid Build Coastguard Worker 15*9e94795aSAndroid Build Coastguard Workerpackage compliance 16*9e94795aSAndroid Build Coastguard Worker 17*9e94795aSAndroid Build Coastguard Workerimport ( 18*9e94795aSAndroid Build Coastguard Worker "fmt" 19*9e94795aSAndroid Build Coastguard Worker "io" 20*9e94795aSAndroid Build Coastguard Worker "sort" 21*9e94795aSAndroid Build Coastguard Worker "strings" 22*9e94795aSAndroid Build Coastguard Worker "testing" 23*9e94795aSAndroid Build Coastguard Worker 24*9e94795aSAndroid Build Coastguard Worker "android/soong/tools/compliance/testfs" 25*9e94795aSAndroid Build Coastguard Worker) 26*9e94795aSAndroid Build Coastguard Worker 27*9e94795aSAndroid Build Coastguard Workerconst ( 28*9e94795aSAndroid Build Coastguard Worker // AOSP starts a test metadata file for Android Apache-2.0 licensing. 29*9e94795aSAndroid Build Coastguard Worker AOSP = `` + 30*9e94795aSAndroid Build Coastguard Worker `package_name: "Android" 31*9e94795aSAndroid Build Coastguard Workerlicense_kinds: "SPDX-license-identifier-Apache-2.0" 32*9e94795aSAndroid Build Coastguard Workerlicense_conditions: "notice" 33*9e94795aSAndroid Build Coastguard Worker` 34*9e94795aSAndroid Build Coastguard Worker 35*9e94795aSAndroid Build Coastguard Worker // GPL starts a test metadata file for GPL 2.0 licensing. 36*9e94795aSAndroid Build Coastguard Worker GPL = `` + 37*9e94795aSAndroid Build Coastguard Worker `package_name: "Free Software" 38*9e94795aSAndroid Build Coastguard Workerlicense_kinds: "SPDX-license-identifier-GPL-2.0" 39*9e94795aSAndroid Build Coastguard Workerlicense_conditions: "restricted" 40*9e94795aSAndroid Build Coastguard Worker` 41*9e94795aSAndroid Build Coastguard Worker 42*9e94795aSAndroid Build Coastguard Worker // Classpath starts a test metadata file for GPL 2.0 with classpath exception licensing. 43*9e94795aSAndroid Build Coastguard Worker Classpath = `` + 44*9e94795aSAndroid Build Coastguard Worker `package_name: "Free Software" 45*9e94795aSAndroid Build Coastguard Workerlicense_kinds: "SPDX-license-identifier-GPL-2.0-with-classpath-exception" 46*9e94795aSAndroid Build Coastguard Workerlicense_conditions: "permissive" 47*9e94795aSAndroid Build Coastguard Worker` 48*9e94795aSAndroid Build Coastguard Worker 49*9e94795aSAndroid Build Coastguard Worker // DependentModule starts a test metadata file for a module in the same package as `Classpath`. 50*9e94795aSAndroid Build Coastguard Worker DependentModule = `` + 51*9e94795aSAndroid Build Coastguard Worker `package_name: "Free Software" 52*9e94795aSAndroid Build Coastguard Workerlicense_kinds: "SPDX-license-identifier-MIT" 53*9e94795aSAndroid Build Coastguard Workerlicense_conditions: "notice" 54*9e94795aSAndroid Build Coastguard Worker` 55*9e94795aSAndroid Build Coastguard Worker 56*9e94795aSAndroid Build Coastguard Worker // LGPL starts a test metadata file for a module with LGPL 2.0 licensing. 57*9e94795aSAndroid Build Coastguard Worker LGPL = `` + 58*9e94795aSAndroid Build Coastguard Worker `package_name: "Free Library" 59*9e94795aSAndroid Build Coastguard Workerlicense_kinds: "SPDX-license-identifier-LGPL-2.0" 60*9e94795aSAndroid Build Coastguard Workerlicense_conditions: "restricted_if_statically_linked" 61*9e94795aSAndroid Build Coastguard Worker` 62*9e94795aSAndroid Build Coastguard Worker 63*9e94795aSAndroid Build Coastguard Worker // MPL starts a test metadata file for a module with MPL 2.0 reciprical licensing. 64*9e94795aSAndroid Build Coastguard Worker MPL = `` + 65*9e94795aSAndroid Build Coastguard Worker `package_name: "Reciprocal" 66*9e94795aSAndroid Build Coastguard Workerlicense_kinds: "SPDX-license-identifier-MPL-2.0" 67*9e94795aSAndroid Build Coastguard Workerlicense_conditions: "reciprocal" 68*9e94795aSAndroid Build Coastguard Worker` 69*9e94795aSAndroid Build Coastguard Worker 70*9e94795aSAndroid Build Coastguard Worker // MIT starts a test metadata file for a module with generic notice (MIT) licensing. 71*9e94795aSAndroid Build Coastguard Worker MIT = `` + 72*9e94795aSAndroid Build Coastguard Worker `package_name: "Android" 73*9e94795aSAndroid Build Coastguard Workerlicense_kinds: "SPDX-license-identifier-MIT" 74*9e94795aSAndroid Build Coastguard Workerlicense_conditions: "notice" 75*9e94795aSAndroid Build Coastguard Worker` 76*9e94795aSAndroid Build Coastguard Worker 77*9e94795aSAndroid Build Coastguard Worker // Proprietary starts a test metadata file for a module with proprietary licensing. 78*9e94795aSAndroid Build Coastguard Worker Proprietary = `` + 79*9e94795aSAndroid Build Coastguard Worker `package_name: "Android" 80*9e94795aSAndroid Build Coastguard Workerlicense_kinds: "legacy_proprietary" 81*9e94795aSAndroid Build Coastguard Workerlicense_conditions: "proprietary" 82*9e94795aSAndroid Build Coastguard Worker` 83*9e94795aSAndroid Build Coastguard Worker 84*9e94795aSAndroid Build Coastguard Worker // ByException starts a test metadata file for a module with by_exception_only licensing. 85*9e94795aSAndroid Build Coastguard Worker ByException = `` + 86*9e94795aSAndroid Build Coastguard Worker `package_name: "Special" 87*9e94795aSAndroid Build Coastguard Workerlicense_kinds: "legacy_by_exception_only" 88*9e94795aSAndroid Build Coastguard Workerlicense_conditions: "by_exception_only" 89*9e94795aSAndroid Build Coastguard Worker` 90*9e94795aSAndroid Build Coastguard Worker) 91*9e94795aSAndroid Build Coastguard Worker 92*9e94795aSAndroid Build Coastguard Workervar ( 93*9e94795aSAndroid Build Coastguard Worker // meta maps test file names to metadata file content without dependencies. 94*9e94795aSAndroid Build Coastguard Worker meta = map[string]string{ 95*9e94795aSAndroid Build Coastguard Worker "apacheBin.meta_lic": AOSP, 96*9e94795aSAndroid Build Coastguard Worker "apacheLib.meta_lic": AOSP, 97*9e94795aSAndroid Build Coastguard Worker "apacheContainer.meta_lic": AOSP + "is_container: true\n", 98*9e94795aSAndroid Build Coastguard Worker "dependentModule.meta_lic": DependentModule, 99*9e94795aSAndroid Build Coastguard Worker "gplWithClasspathException.meta_lic": Classpath, 100*9e94795aSAndroid Build Coastguard Worker "gplBin.meta_lic": GPL, 101*9e94795aSAndroid Build Coastguard Worker "gplLib.meta_lic": GPL, 102*9e94795aSAndroid Build Coastguard Worker "gplContainer.meta_lic": GPL + "is_container: true\n", 103*9e94795aSAndroid Build Coastguard Worker "lgplBin.meta_lic": LGPL, 104*9e94795aSAndroid Build Coastguard Worker "lgplLib.meta_lic": LGPL, 105*9e94795aSAndroid Build Coastguard Worker "mitBin.meta_lic": MIT, 106*9e94795aSAndroid Build Coastguard Worker "mitLib.meta_lic": MIT, 107*9e94795aSAndroid Build Coastguard Worker "mplBin.meta_lic": MPL, 108*9e94795aSAndroid Build Coastguard Worker "mplLib.meta_lic": MPL, 109*9e94795aSAndroid Build Coastguard Worker "proprietary.meta_lic": Proprietary, 110*9e94795aSAndroid Build Coastguard Worker "by_exception.meta_lic": ByException, 111*9e94795aSAndroid Build Coastguard Worker } 112*9e94795aSAndroid Build Coastguard Worker) 113*9e94795aSAndroid Build Coastguard Worker 114*9e94795aSAndroid Build Coastguard Worker// newTestNode constructs a test node in the license graph. 115*9e94795aSAndroid Build Coastguard Workerfunc newTestNode(lg *LicenseGraph, targetName string) *TargetNode { 116*9e94795aSAndroid Build Coastguard Worker if tn, alreadyExists := lg.targets[targetName]; alreadyExists { 117*9e94795aSAndroid Build Coastguard Worker return tn 118*9e94795aSAndroid Build Coastguard Worker } 119*9e94795aSAndroid Build Coastguard Worker tn := &TargetNode{name: targetName} 120*9e94795aSAndroid Build Coastguard Worker lg.targets[targetName] = tn 121*9e94795aSAndroid Build Coastguard Worker return tn 122*9e94795aSAndroid Build Coastguard Worker} 123*9e94795aSAndroid Build Coastguard Worker 124*9e94795aSAndroid Build Coastguard Worker// newTestCondition constructs a test license condition. 125*9e94795aSAndroid Build Coastguard Workerfunc newTestCondition(conditionName string) LicenseCondition { 126*9e94795aSAndroid Build Coastguard Worker cl := LicenseConditionSetFromNames(conditionName).AsList() 127*9e94795aSAndroid Build Coastguard Worker if len(cl) == 0 { 128*9e94795aSAndroid Build Coastguard Worker panic(fmt.Errorf("attempt to create unrecognized condition: %q", conditionName)) 129*9e94795aSAndroid Build Coastguard Worker } else if len(cl) != 1 { 130*9e94795aSAndroid Build Coastguard Worker panic(fmt.Errorf("unexpected multiple conditions from condition name: %q: got %d, want 1", conditionName, len(cl))) 131*9e94795aSAndroid Build Coastguard Worker } 132*9e94795aSAndroid Build Coastguard Worker lc := cl[0] 133*9e94795aSAndroid Build Coastguard Worker return lc 134*9e94795aSAndroid Build Coastguard Worker} 135*9e94795aSAndroid Build Coastguard Worker 136*9e94795aSAndroid Build Coastguard Worker// newTestConditionSet constructs a test license condition set. 137*9e94795aSAndroid Build Coastguard Workerfunc newTestConditionSet(conditionName []string) LicenseConditionSet { 138*9e94795aSAndroid Build Coastguard Worker cs := LicenseConditionSetFromNames(conditionName...) 139*9e94795aSAndroid Build Coastguard Worker if cs.IsEmpty() { 140*9e94795aSAndroid Build Coastguard Worker panic(fmt.Errorf("attempt to create unrecognized condition: %q", conditionName)) 141*9e94795aSAndroid Build Coastguard Worker } 142*9e94795aSAndroid Build Coastguard Worker return cs 143*9e94795aSAndroid Build Coastguard Worker} 144*9e94795aSAndroid Build Coastguard Worker 145*9e94795aSAndroid Build Coastguard Worker// edge describes test data edges to define test graphs. 146*9e94795aSAndroid Build Coastguard Workertype edge struct { 147*9e94795aSAndroid Build Coastguard Worker target, dep string 148*9e94795aSAndroid Build Coastguard Worker} 149*9e94795aSAndroid Build Coastguard Worker 150*9e94795aSAndroid Build Coastguard Worker// String returns a string representation of the edge. 151*9e94795aSAndroid Build Coastguard Workerfunc (e edge) String() string { 152*9e94795aSAndroid Build Coastguard Worker return e.target + " -> " + e.dep 153*9e94795aSAndroid Build Coastguard Worker} 154*9e94795aSAndroid Build Coastguard Worker 155*9e94795aSAndroid Build Coastguard Worker// byEdge orders edges by target then dep name then annotations. 156*9e94795aSAndroid Build Coastguard Workertype byEdge []edge 157*9e94795aSAndroid Build Coastguard Worker 158*9e94795aSAndroid Build Coastguard Worker// Len returns the count of elements in the slice. 159*9e94795aSAndroid Build Coastguard Workerfunc (l byEdge) Len() int { return len(l) } 160*9e94795aSAndroid Build Coastguard Worker 161*9e94795aSAndroid Build Coastguard Worker// Swap rearranges 2 elements of the slice so that each occupies the other's 162*9e94795aSAndroid Build Coastguard Worker// former position. 163*9e94795aSAndroid Build Coastguard Workerfunc (l byEdge) Swap(i, j int) { l[i], l[j] = l[j], l[i] } 164*9e94795aSAndroid Build Coastguard Worker 165*9e94795aSAndroid Build Coastguard Worker// Less returns true when the `i`th element is lexicographically less than 166*9e94795aSAndroid Build Coastguard Worker// the `j`th element. 167*9e94795aSAndroid Build Coastguard Workerfunc (l byEdge) Less(i, j int) bool { 168*9e94795aSAndroid Build Coastguard Worker if l[i].target == l[j].target { 169*9e94795aSAndroid Build Coastguard Worker return l[i].dep < l[j].dep 170*9e94795aSAndroid Build Coastguard Worker } 171*9e94795aSAndroid Build Coastguard Worker return l[i].target < l[j].target 172*9e94795aSAndroid Build Coastguard Worker} 173*9e94795aSAndroid Build Coastguard Worker 174*9e94795aSAndroid Build Coastguard Worker// annotated describes annotated test data edges to define test graphs. 175*9e94795aSAndroid Build Coastguard Workertype annotated struct { 176*9e94795aSAndroid Build Coastguard Worker target, dep string 177*9e94795aSAndroid Build Coastguard Worker annotations []string 178*9e94795aSAndroid Build Coastguard Worker} 179*9e94795aSAndroid Build Coastguard Worker 180*9e94795aSAndroid Build Coastguard Workerfunc (e annotated) String() string { 181*9e94795aSAndroid Build Coastguard Worker if e.annotations != nil { 182*9e94795aSAndroid Build Coastguard Worker return e.target + " -> " + e.dep + " [" + strings.Join(e.annotations, ", ") + "]" 183*9e94795aSAndroid Build Coastguard Worker } 184*9e94795aSAndroid Build Coastguard Worker return e.target + " -> " + e.dep 185*9e94795aSAndroid Build Coastguard Worker} 186*9e94795aSAndroid Build Coastguard Worker 187*9e94795aSAndroid Build Coastguard Workerfunc (e annotated) IsEqualTo(other annotated) bool { 188*9e94795aSAndroid Build Coastguard Worker if e.target != other.target { 189*9e94795aSAndroid Build Coastguard Worker return false 190*9e94795aSAndroid Build Coastguard Worker } 191*9e94795aSAndroid Build Coastguard Worker if e.dep != other.dep { 192*9e94795aSAndroid Build Coastguard Worker return false 193*9e94795aSAndroid Build Coastguard Worker } 194*9e94795aSAndroid Build Coastguard Worker if len(e.annotations) != len(other.annotations) { 195*9e94795aSAndroid Build Coastguard Worker return false 196*9e94795aSAndroid Build Coastguard Worker } 197*9e94795aSAndroid Build Coastguard Worker a1 := append([]string{}, e.annotations...) 198*9e94795aSAndroid Build Coastguard Worker a2 := append([]string{}, other.annotations...) 199*9e94795aSAndroid Build Coastguard Worker for i := 0; i < len(a1); i++ { 200*9e94795aSAndroid Build Coastguard Worker if a1[i] != a2[i] { 201*9e94795aSAndroid Build Coastguard Worker return false 202*9e94795aSAndroid Build Coastguard Worker } 203*9e94795aSAndroid Build Coastguard Worker } 204*9e94795aSAndroid Build Coastguard Worker return true 205*9e94795aSAndroid Build Coastguard Worker} 206*9e94795aSAndroid Build Coastguard Worker 207*9e94795aSAndroid Build Coastguard Worker// toGraph converts a list of roots and a list of annotated edges into a test license graph. 208*9e94795aSAndroid Build Coastguard Workerfunc toGraph(stderr io.Writer, roots []string, edges []annotated) (*LicenseGraph, error) { 209*9e94795aSAndroid Build Coastguard Worker deps := make(map[string][]annotated) 210*9e94795aSAndroid Build Coastguard Worker for _, root := range roots { 211*9e94795aSAndroid Build Coastguard Worker deps[root] = []annotated{} 212*9e94795aSAndroid Build Coastguard Worker } 213*9e94795aSAndroid Build Coastguard Worker for _, edge := range edges { 214*9e94795aSAndroid Build Coastguard Worker if prev, ok := deps[edge.target]; ok { 215*9e94795aSAndroid Build Coastguard Worker deps[edge.target] = append(prev, edge) 216*9e94795aSAndroid Build Coastguard Worker } else { 217*9e94795aSAndroid Build Coastguard Worker deps[edge.target] = []annotated{edge} 218*9e94795aSAndroid Build Coastguard Worker } 219*9e94795aSAndroid Build Coastguard Worker if _, ok := deps[edge.dep]; !ok { 220*9e94795aSAndroid Build Coastguard Worker deps[edge.dep] = []annotated{} 221*9e94795aSAndroid Build Coastguard Worker } 222*9e94795aSAndroid Build Coastguard Worker } 223*9e94795aSAndroid Build Coastguard Worker fs := make(testfs.TestFS) 224*9e94795aSAndroid Build Coastguard Worker for file, edges := range deps { 225*9e94795aSAndroid Build Coastguard Worker body := meta[file] 226*9e94795aSAndroid Build Coastguard Worker for _, edge := range edges { 227*9e94795aSAndroid Build Coastguard Worker body += fmt.Sprintf("deps: {\n file: %q\n", edge.dep) 228*9e94795aSAndroid Build Coastguard Worker for _, ann := range edge.annotations { 229*9e94795aSAndroid Build Coastguard Worker body += fmt.Sprintf(" annotations: %q\n", ann) 230*9e94795aSAndroid Build Coastguard Worker } 231*9e94795aSAndroid Build Coastguard Worker body += "}\n" 232*9e94795aSAndroid Build Coastguard Worker } 233*9e94795aSAndroid Build Coastguard Worker fs[file] = []byte(body) 234*9e94795aSAndroid Build Coastguard Worker } 235*9e94795aSAndroid Build Coastguard Worker 236*9e94795aSAndroid Build Coastguard Worker return ReadLicenseGraph(&fs, stderr, roots) 237*9e94795aSAndroid Build Coastguard Worker} 238*9e94795aSAndroid Build Coastguard Worker 239*9e94795aSAndroid Build Coastguard Worker// logGraph outputs a representation of the graph to a test log. 240*9e94795aSAndroid Build Coastguard Workerfunc logGraph(lg *LicenseGraph, t *testing.T) { 241*9e94795aSAndroid Build Coastguard Worker t.Logf("license graph:") 242*9e94795aSAndroid Build Coastguard Worker t.Logf(" targets:") 243*9e94795aSAndroid Build Coastguard Worker for _, target := range lg.Targets() { 244*9e94795aSAndroid Build Coastguard Worker t.Logf(" %s%s in package %q", target.Name(), target.LicenseConditions().String(), target.PackageName()) 245*9e94795aSAndroid Build Coastguard Worker } 246*9e94795aSAndroid Build Coastguard Worker t.Logf(" /targets") 247*9e94795aSAndroid Build Coastguard Worker t.Logf(" edges:") 248*9e94795aSAndroid Build Coastguard Worker for _, edge := range lg.Edges() { 249*9e94795aSAndroid Build Coastguard Worker t.Logf(" %s", edge.String()) 250*9e94795aSAndroid Build Coastguard Worker } 251*9e94795aSAndroid Build Coastguard Worker t.Logf(" /edges") 252*9e94795aSAndroid Build Coastguard Worker t.Logf("/license graph") 253*9e94795aSAndroid Build Coastguard Worker} 254*9e94795aSAndroid Build Coastguard Worker 255*9e94795aSAndroid Build Coastguard Worker// byAnnotatedEdge orders edges by target then dep name then annotations. 256*9e94795aSAndroid Build Coastguard Workertype byAnnotatedEdge []annotated 257*9e94795aSAndroid Build Coastguard Worker 258*9e94795aSAndroid Build Coastguard Workerfunc (l byAnnotatedEdge) Len() int { return len(l) } 259*9e94795aSAndroid Build Coastguard Workerfunc (l byAnnotatedEdge) Swap(i, j int) { l[i], l[j] = l[j], l[i] } 260*9e94795aSAndroid Build Coastguard Workerfunc (l byAnnotatedEdge) Less(i, j int) bool { 261*9e94795aSAndroid Build Coastguard Worker if l[i].target == l[j].target { 262*9e94795aSAndroid Build Coastguard Worker if l[i].dep == l[j].dep { 263*9e94795aSAndroid Build Coastguard Worker ai := append([]string{}, l[i].annotations...) 264*9e94795aSAndroid Build Coastguard Worker aj := append([]string{}, l[j].annotations...) 265*9e94795aSAndroid Build Coastguard Worker sort.Strings(ai) 266*9e94795aSAndroid Build Coastguard Worker sort.Strings(aj) 267*9e94795aSAndroid Build Coastguard Worker for k := 0; k < len(ai) && k < len(aj); k++ { 268*9e94795aSAndroid Build Coastguard Worker if ai[k] == aj[k] { 269*9e94795aSAndroid Build Coastguard Worker continue 270*9e94795aSAndroid Build Coastguard Worker } 271*9e94795aSAndroid Build Coastguard Worker return ai[k] < aj[k] 272*9e94795aSAndroid Build Coastguard Worker } 273*9e94795aSAndroid Build Coastguard Worker return len(ai) < len(aj) 274*9e94795aSAndroid Build Coastguard Worker } 275*9e94795aSAndroid Build Coastguard Worker return l[i].dep < l[j].dep 276*9e94795aSAndroid Build Coastguard Worker } 277*9e94795aSAndroid Build Coastguard Worker return l[i].target < l[j].target 278*9e94795aSAndroid Build Coastguard Worker} 279*9e94795aSAndroid Build Coastguard Worker 280*9e94795aSAndroid Build Coastguard Worker// act describes test data resolution actions to define test action sets. 281*9e94795aSAndroid Build Coastguard Workertype act struct { 282*9e94795aSAndroid Build Coastguard Worker actsOn, condition string 283*9e94795aSAndroid Build Coastguard Worker} 284*9e94795aSAndroid Build Coastguard Worker 285*9e94795aSAndroid Build Coastguard Worker// String returns a human-readable string representing the test action. 286*9e94795aSAndroid Build Coastguard Workerfunc (a act) String() string { 287*9e94795aSAndroid Build Coastguard Worker return fmt.Sprintf("%s{%s}", a.actsOn, a.condition) 288*9e94795aSAndroid Build Coastguard Worker} 289*9e94795aSAndroid Build Coastguard Worker 290*9e94795aSAndroid Build Coastguard Worker// toActionSet converts a list of act test data into a test action set. 291*9e94795aSAndroid Build Coastguard Workerfunc toActionSet(lg *LicenseGraph, data []act) ActionSet { 292*9e94795aSAndroid Build Coastguard Worker as := make(ActionSet) 293*9e94795aSAndroid Build Coastguard Worker for _, a := range data { 294*9e94795aSAndroid Build Coastguard Worker actsOn := newTestNode(lg, a.actsOn) 295*9e94795aSAndroid Build Coastguard Worker cs := newTestConditionSet(strings.Split(a.condition, "|")) 296*9e94795aSAndroid Build Coastguard Worker as[actsOn] = cs 297*9e94795aSAndroid Build Coastguard Worker } 298*9e94795aSAndroid Build Coastguard Worker return as 299*9e94795aSAndroid Build Coastguard Worker} 300*9e94795aSAndroid Build Coastguard Worker 301*9e94795aSAndroid Build Coastguard Worker// res describes test data resolutions to define test resolution sets. 302*9e94795aSAndroid Build Coastguard Workertype res struct { 303*9e94795aSAndroid Build Coastguard Worker attachesTo, actsOn, condition string 304*9e94795aSAndroid Build Coastguard Worker} 305*9e94795aSAndroid Build Coastguard Worker 306*9e94795aSAndroid Build Coastguard Worker// toResolutionSet converts a list of res test data into a test resolution set. 307*9e94795aSAndroid Build Coastguard Workerfunc toResolutionSet(lg *LicenseGraph, data []res) ResolutionSet { 308*9e94795aSAndroid Build Coastguard Worker rmap := make(ResolutionSet) 309*9e94795aSAndroid Build Coastguard Worker for _, r := range data { 310*9e94795aSAndroid Build Coastguard Worker attachesTo := newTestNode(lg, r.attachesTo) 311*9e94795aSAndroid Build Coastguard Worker actsOn := newTestNode(lg, r.actsOn) 312*9e94795aSAndroid Build Coastguard Worker if _, ok := rmap[attachesTo]; !ok { 313*9e94795aSAndroid Build Coastguard Worker rmap[attachesTo] = make(ActionSet) 314*9e94795aSAndroid Build Coastguard Worker } 315*9e94795aSAndroid Build Coastguard Worker cs := newTestConditionSet(strings.Split(r.condition, "|")) 316*9e94795aSAndroid Build Coastguard Worker rmap[attachesTo][actsOn] |= cs 317*9e94795aSAndroid Build Coastguard Worker } 318*9e94795aSAndroid Build Coastguard Worker return rmap 319*9e94795aSAndroid Build Coastguard Worker} 320*9e94795aSAndroid Build Coastguard Worker 321*9e94795aSAndroid Build Coastguard Worker// tcond associates a target name with '|' separated string conditions. 322*9e94795aSAndroid Build Coastguard Workertype tcond struct { 323*9e94795aSAndroid Build Coastguard Worker target, conditions string 324*9e94795aSAndroid Build Coastguard Worker} 325*9e94795aSAndroid Build Coastguard Worker 326*9e94795aSAndroid Build Coastguard Worker// action represents a single element of an ActionSet for testing. 327*9e94795aSAndroid Build Coastguard Workertype action struct { 328*9e94795aSAndroid Build Coastguard Worker target *TargetNode 329*9e94795aSAndroid Build Coastguard Worker cs LicenseConditionSet 330*9e94795aSAndroid Build Coastguard Worker} 331*9e94795aSAndroid Build Coastguard Worker 332*9e94795aSAndroid Build Coastguard Worker// String returns a human-readable string representation of the action. 333*9e94795aSAndroid Build Coastguard Workerfunc (a action) String() string { 334*9e94795aSAndroid Build Coastguard Worker return fmt.Sprintf("%s%s", a.target.Name(), a.cs.String()) 335*9e94795aSAndroid Build Coastguard Worker} 336*9e94795aSAndroid Build Coastguard Worker 337*9e94795aSAndroid Build Coastguard Worker// actionList represents an array of actions and a total order defined by 338*9e94795aSAndroid Build Coastguard Worker// target name followed by license condition set. 339*9e94795aSAndroid Build Coastguard Workertype actionList []action 340*9e94795aSAndroid Build Coastguard Worker 341*9e94795aSAndroid Build Coastguard Worker// String returns a human-readable string representation of the list. 342*9e94795aSAndroid Build Coastguard Workerfunc (l actionList) String() string { 343*9e94795aSAndroid Build Coastguard Worker var sb strings.Builder 344*9e94795aSAndroid Build Coastguard Worker fmt.Fprintf(&sb, "[") 345*9e94795aSAndroid Build Coastguard Worker sep := "" 346*9e94795aSAndroid Build Coastguard Worker for _, a := range l { 347*9e94795aSAndroid Build Coastguard Worker fmt.Fprintf(&sb, "%s%s", sep, a.String()) 348*9e94795aSAndroid Build Coastguard Worker sep = ", " 349*9e94795aSAndroid Build Coastguard Worker } 350*9e94795aSAndroid Build Coastguard Worker fmt.Fprintf(&sb, "]") 351*9e94795aSAndroid Build Coastguard Worker return sb.String() 352*9e94795aSAndroid Build Coastguard Worker} 353*9e94795aSAndroid Build Coastguard Worker 354*9e94795aSAndroid Build Coastguard Worker// Len returns the count of elements in the slice. 355*9e94795aSAndroid Build Coastguard Workerfunc (l actionList) Len() int { return len(l) } 356*9e94795aSAndroid Build Coastguard Worker 357*9e94795aSAndroid Build Coastguard Worker// Swap rearranges 2 elements of the slice so that each occupies the other's 358*9e94795aSAndroid Build Coastguard Worker// former position. 359*9e94795aSAndroid Build Coastguard Workerfunc (l actionList) Swap(i, j int) { l[i], l[j] = l[j], l[i] } 360*9e94795aSAndroid Build Coastguard Worker 361*9e94795aSAndroid Build Coastguard Worker// Less returns true when the `i`th element is lexicographically less than 362*9e94795aSAndroid Build Coastguard Worker// the `j`th element. 363*9e94795aSAndroid Build Coastguard Workerfunc (l actionList) Less(i, j int) bool { 364*9e94795aSAndroid Build Coastguard Worker if l[i].target == l[j].target { 365*9e94795aSAndroid Build Coastguard Worker return l[i].cs < l[j].cs 366*9e94795aSAndroid Build Coastguard Worker } 367*9e94795aSAndroid Build Coastguard Worker return l[i].target.Name() < l[j].target.Name() 368*9e94795aSAndroid Build Coastguard Worker} 369*9e94795aSAndroid Build Coastguard Worker 370*9e94795aSAndroid Build Coastguard Worker// asActionList represents the resolved license conditions in a license graph 371*9e94795aSAndroid Build Coastguard Worker// as an actionList for comparison in a test. 372*9e94795aSAndroid Build Coastguard Workerfunc asActionList(lg *LicenseGraph) actionList { 373*9e94795aSAndroid Build Coastguard Worker result := make(actionList, 0, len(lg.targets)) 374*9e94795aSAndroid Build Coastguard Worker for _, target := range lg.targets { 375*9e94795aSAndroid Build Coastguard Worker cs := target.resolution 376*9e94795aSAndroid Build Coastguard Worker if cs.IsEmpty() { 377*9e94795aSAndroid Build Coastguard Worker continue 378*9e94795aSAndroid Build Coastguard Worker } 379*9e94795aSAndroid Build Coastguard Worker result = append(result, action{target, cs}) 380*9e94795aSAndroid Build Coastguard Worker } 381*9e94795aSAndroid Build Coastguard Worker return result 382*9e94795aSAndroid Build Coastguard Worker} 383*9e94795aSAndroid Build Coastguard Worker 384*9e94795aSAndroid Build Coastguard Worker// toActionList converts an array of tcond into an actionList for comparison 385*9e94795aSAndroid Build Coastguard Worker// in a test. 386*9e94795aSAndroid Build Coastguard Workerfunc toActionList(lg *LicenseGraph, actions []tcond) actionList { 387*9e94795aSAndroid Build Coastguard Worker result := make(actionList, 0, len(actions)) 388*9e94795aSAndroid Build Coastguard Worker for _, actn := range actions { 389*9e94795aSAndroid Build Coastguard Worker target := newTestNode(lg, actn.target) 390*9e94795aSAndroid Build Coastguard Worker cs := NewLicenseConditionSet() 391*9e94795aSAndroid Build Coastguard Worker for _, name := range strings.Split(actn.conditions, "|") { 392*9e94795aSAndroid Build Coastguard Worker lc, ok := RecognizedConditionNames[name] 393*9e94795aSAndroid Build Coastguard Worker if !ok { 394*9e94795aSAndroid Build Coastguard Worker panic(fmt.Errorf("Unrecognized test condition name: %q", name)) 395*9e94795aSAndroid Build Coastguard Worker } 396*9e94795aSAndroid Build Coastguard Worker cs = cs.Plus(lc) 397*9e94795aSAndroid Build Coastguard Worker } 398*9e94795aSAndroid Build Coastguard Worker result = append(result, action{target, cs}) 399*9e94795aSAndroid Build Coastguard Worker } 400*9e94795aSAndroid Build Coastguard Worker return result 401*9e94795aSAndroid Build Coastguard Worker} 402*9e94795aSAndroid Build Coastguard Worker 403*9e94795aSAndroid Build Coastguard Worker// confl defines test data for a SourceSharePrivacyConflict as a target name, 404*9e94795aSAndroid Build Coastguard Worker// source condition name, privacy condition name triple. 405*9e94795aSAndroid Build Coastguard Workertype confl struct { 406*9e94795aSAndroid Build Coastguard Worker sourceNode, share, privacy string 407*9e94795aSAndroid Build Coastguard Worker} 408*9e94795aSAndroid Build Coastguard Worker 409*9e94795aSAndroid Build Coastguard Worker// toConflictList converts confl test data into an array of 410*9e94795aSAndroid Build Coastguard Worker// SourceSharePrivacyConflict for comparison in a test. 411*9e94795aSAndroid Build Coastguard Workerfunc toConflictList(lg *LicenseGraph, data []confl) []SourceSharePrivacyConflict { 412*9e94795aSAndroid Build Coastguard Worker result := make([]SourceSharePrivacyConflict, 0, len(data)) 413*9e94795aSAndroid Build Coastguard Worker for _, c := range data { 414*9e94795aSAndroid Build Coastguard Worker fields := strings.Split(c.share, ":") 415*9e94795aSAndroid Build Coastguard Worker cshare := fields[1] 416*9e94795aSAndroid Build Coastguard Worker fields = strings.Split(c.privacy, ":") 417*9e94795aSAndroid Build Coastguard Worker cprivacy := fields[1] 418*9e94795aSAndroid Build Coastguard Worker result = append(result, SourceSharePrivacyConflict{ 419*9e94795aSAndroid Build Coastguard Worker newTestNode(lg, c.sourceNode), 420*9e94795aSAndroid Build Coastguard Worker newTestCondition(cshare), 421*9e94795aSAndroid Build Coastguard Worker newTestCondition(cprivacy), 422*9e94795aSAndroid Build Coastguard Worker }) 423*9e94795aSAndroid Build Coastguard Worker } 424*9e94795aSAndroid Build Coastguard Worker return result 425*9e94795aSAndroid Build Coastguard Worker} 426*9e94795aSAndroid Build Coastguard Worker 427*9e94795aSAndroid Build Coastguard Worker// checkSameActions compares an actual action set to an expected action set for a test. 428*9e94795aSAndroid Build Coastguard Workerfunc checkSameActions(lg *LicenseGraph, asActual, asExpected ActionSet, t *testing.T) { 429*9e94795aSAndroid Build Coastguard Worker rsActual := make(ResolutionSet) 430*9e94795aSAndroid Build Coastguard Worker rsExpected := make(ResolutionSet) 431*9e94795aSAndroid Build Coastguard Worker testNode := newTestNode(lg, "test") 432*9e94795aSAndroid Build Coastguard Worker rsActual[testNode] = asActual 433*9e94795aSAndroid Build Coastguard Worker rsExpected[testNode] = asExpected 434*9e94795aSAndroid Build Coastguard Worker checkSame(rsActual, rsExpected, t) 435*9e94795aSAndroid Build Coastguard Worker} 436*9e94795aSAndroid Build Coastguard Worker 437*9e94795aSAndroid Build Coastguard Worker// checkSame compares an actual resolution set to an expected resolution set for a test. 438*9e94795aSAndroid Build Coastguard Workerfunc checkSame(rsActual, rsExpected ResolutionSet, t *testing.T) { 439*9e94795aSAndroid Build Coastguard Worker t.Logf("actual resolution set: %s", rsActual.String()) 440*9e94795aSAndroid Build Coastguard Worker t.Logf("expected resolution set: %s", rsExpected.String()) 441*9e94795aSAndroid Build Coastguard Worker 442*9e94795aSAndroid Build Coastguard Worker actualTargets := rsActual.AttachesTo() 443*9e94795aSAndroid Build Coastguard Worker sort.Sort(actualTargets) 444*9e94795aSAndroid Build Coastguard Worker 445*9e94795aSAndroid Build Coastguard Worker expectedTargets := rsExpected.AttachesTo() 446*9e94795aSAndroid Build Coastguard Worker sort.Sort(expectedTargets) 447*9e94795aSAndroid Build Coastguard Worker 448*9e94795aSAndroid Build Coastguard Worker t.Logf("actual targets: %s", actualTargets.String()) 449*9e94795aSAndroid Build Coastguard Worker t.Logf("expected targets: %s", expectedTargets.String()) 450*9e94795aSAndroid Build Coastguard Worker 451*9e94795aSAndroid Build Coastguard Worker for _, target := range expectedTargets { 452*9e94795aSAndroid Build Coastguard Worker if !rsActual.AttachesToTarget(target) { 453*9e94795aSAndroid Build Coastguard Worker t.Errorf("unexpected missing target: got AttachesToTarget(%q) is false, want true", target.name) 454*9e94795aSAndroid Build Coastguard Worker continue 455*9e94795aSAndroid Build Coastguard Worker } 456*9e94795aSAndroid Build Coastguard Worker expectedRl := rsExpected.Resolutions(target) 457*9e94795aSAndroid Build Coastguard Worker sort.Sort(expectedRl) 458*9e94795aSAndroid Build Coastguard Worker actualRl := rsActual.Resolutions(target) 459*9e94795aSAndroid Build Coastguard Worker sort.Sort(actualRl) 460*9e94795aSAndroid Build Coastguard Worker if len(expectedRl) != len(actualRl) { 461*9e94795aSAndroid Build Coastguard Worker t.Errorf("unexpected number of resolutions attach to %q: %d elements, %d elements", 462*9e94795aSAndroid Build Coastguard Worker target.name, len(actualRl), len(expectedRl)) 463*9e94795aSAndroid Build Coastguard Worker continue 464*9e94795aSAndroid Build Coastguard Worker } 465*9e94795aSAndroid Build Coastguard Worker for i := 0; i < len(expectedRl); i++ { 466*9e94795aSAndroid Build Coastguard Worker if expectedRl[i].attachesTo.name != actualRl[i].attachesTo.name || expectedRl[i].actsOn.name != actualRl[i].actsOn.name { 467*9e94795aSAndroid Build Coastguard Worker t.Errorf("unexpected resolution attaches to %q at index %d: got %s, want %s", 468*9e94795aSAndroid Build Coastguard Worker target.name, i, actualRl[i].asString(), expectedRl[i].asString()) 469*9e94795aSAndroid Build Coastguard Worker continue 470*9e94795aSAndroid Build Coastguard Worker } 471*9e94795aSAndroid Build Coastguard Worker expectedConditions := expectedRl[i].Resolves() 472*9e94795aSAndroid Build Coastguard Worker actualConditions := actualRl[i].Resolves() 473*9e94795aSAndroid Build Coastguard Worker if expectedConditions != actualConditions { 474*9e94795aSAndroid Build Coastguard Worker t.Errorf("unexpected conditions apply to %q acting on %q: got %#v with names %s, want %#v with names %s", 475*9e94795aSAndroid Build Coastguard Worker target.name, expectedRl[i].actsOn.name, 476*9e94795aSAndroid Build Coastguard Worker actualConditions, actualConditions.Names(), 477*9e94795aSAndroid Build Coastguard Worker expectedConditions, expectedConditions.Names()) 478*9e94795aSAndroid Build Coastguard Worker continue 479*9e94795aSAndroid Build Coastguard Worker } 480*9e94795aSAndroid Build Coastguard Worker } 481*9e94795aSAndroid Build Coastguard Worker 482*9e94795aSAndroid Build Coastguard Worker } 483*9e94795aSAndroid Build Coastguard Worker for _, target := range actualTargets { 484*9e94795aSAndroid Build Coastguard Worker if !rsExpected.AttachesToTarget(target) { 485*9e94795aSAndroid Build Coastguard Worker t.Errorf("unexpected extra target: got expected.AttachesTo(%q) is false, want true", target.name) 486*9e94795aSAndroid Build Coastguard Worker } 487*9e94795aSAndroid Build Coastguard Worker } 488*9e94795aSAndroid Build Coastguard Worker} 489*9e94795aSAndroid Build Coastguard Worker 490*9e94795aSAndroid Build Coastguard Worker// checkResolvesActions compares an actual action set to an expected action set for a test verifying the actual set 491*9e94795aSAndroid Build Coastguard Worker// resolves all of the expected conditions. 492*9e94795aSAndroid Build Coastguard Workerfunc checkResolvesActions(lg *LicenseGraph, asActual, asExpected ActionSet, t *testing.T) { 493*9e94795aSAndroid Build Coastguard Worker rsActual := make(ResolutionSet) 494*9e94795aSAndroid Build Coastguard Worker rsExpected := make(ResolutionSet) 495*9e94795aSAndroid Build Coastguard Worker testNode := newTestNode(lg, "test") 496*9e94795aSAndroid Build Coastguard Worker rsActual[testNode] = asActual 497*9e94795aSAndroid Build Coastguard Worker rsExpected[testNode] = asExpected 498*9e94795aSAndroid Build Coastguard Worker checkResolves(rsActual, rsExpected, t) 499*9e94795aSAndroid Build Coastguard Worker} 500*9e94795aSAndroid Build Coastguard Worker 501*9e94795aSAndroid Build Coastguard Worker// checkResolves compares an actual resolution set to an expected resolution set for a test verifying the actual set 502*9e94795aSAndroid Build Coastguard Worker// resolves all of the expected conditions. 503*9e94795aSAndroid Build Coastguard Workerfunc checkResolves(rsActual, rsExpected ResolutionSet, t *testing.T) { 504*9e94795aSAndroid Build Coastguard Worker t.Logf("actual resolution set: %s", rsActual.String()) 505*9e94795aSAndroid Build Coastguard Worker t.Logf("expected resolution set: %s", rsExpected.String()) 506*9e94795aSAndroid Build Coastguard Worker 507*9e94795aSAndroid Build Coastguard Worker actualTargets := rsActual.AttachesTo() 508*9e94795aSAndroid Build Coastguard Worker sort.Sort(actualTargets) 509*9e94795aSAndroid Build Coastguard Worker 510*9e94795aSAndroid Build Coastguard Worker expectedTargets := rsExpected.AttachesTo() 511*9e94795aSAndroid Build Coastguard Worker sort.Sort(expectedTargets) 512*9e94795aSAndroid Build Coastguard Worker 513*9e94795aSAndroid Build Coastguard Worker t.Logf("actual targets: %s", actualTargets.String()) 514*9e94795aSAndroid Build Coastguard Worker t.Logf("expected targets: %s", expectedTargets.String()) 515*9e94795aSAndroid Build Coastguard Worker 516*9e94795aSAndroid Build Coastguard Worker for _, target := range expectedTargets { 517*9e94795aSAndroid Build Coastguard Worker if !rsActual.AttachesToTarget(target) { 518*9e94795aSAndroid Build Coastguard Worker t.Errorf("unexpected missing target: got AttachesToTarget(%q) is false, want true", target.name) 519*9e94795aSAndroid Build Coastguard Worker continue 520*9e94795aSAndroid Build Coastguard Worker } 521*9e94795aSAndroid Build Coastguard Worker expectedRl := rsExpected.Resolutions(target) 522*9e94795aSAndroid Build Coastguard Worker sort.Sort(expectedRl) 523*9e94795aSAndroid Build Coastguard Worker actualRl := rsActual.Resolutions(target) 524*9e94795aSAndroid Build Coastguard Worker sort.Sort(actualRl) 525*9e94795aSAndroid Build Coastguard Worker if len(expectedRl) != len(actualRl) { 526*9e94795aSAndroid Build Coastguard Worker t.Errorf("unexpected number of resolutions attach to %q: %d elements, %d elements", 527*9e94795aSAndroid Build Coastguard Worker target.name, len(actualRl), len(expectedRl)) 528*9e94795aSAndroid Build Coastguard Worker continue 529*9e94795aSAndroid Build Coastguard Worker } 530*9e94795aSAndroid Build Coastguard Worker for i := 0; i < len(expectedRl); i++ { 531*9e94795aSAndroid Build Coastguard Worker if expectedRl[i].attachesTo.name != actualRl[i].attachesTo.name || expectedRl[i].actsOn.name != actualRl[i].actsOn.name { 532*9e94795aSAndroid Build Coastguard Worker t.Errorf("unexpected resolution attaches to %q at index %d: got %s, want %s", 533*9e94795aSAndroid Build Coastguard Worker target.name, i, actualRl[i].asString(), expectedRl[i].asString()) 534*9e94795aSAndroid Build Coastguard Worker continue 535*9e94795aSAndroid Build Coastguard Worker } 536*9e94795aSAndroid Build Coastguard Worker expectedConditions := expectedRl[i].Resolves() 537*9e94795aSAndroid Build Coastguard Worker actualConditions := actualRl[i].Resolves() 538*9e94795aSAndroid Build Coastguard Worker if expectedConditions != (expectedConditions & actualConditions) { 539*9e94795aSAndroid Build Coastguard Worker t.Errorf("expected conditions missing from %q acting on %q: got %#v with names %s, want %#v with names %s", 540*9e94795aSAndroid Build Coastguard Worker target.name, expectedRl[i].actsOn.name, 541*9e94795aSAndroid Build Coastguard Worker actualConditions, actualConditions.Names(), 542*9e94795aSAndroid Build Coastguard Worker expectedConditions, expectedConditions.Names()) 543*9e94795aSAndroid Build Coastguard Worker continue 544*9e94795aSAndroid Build Coastguard Worker } 545*9e94795aSAndroid Build Coastguard Worker } 546*9e94795aSAndroid Build Coastguard Worker 547*9e94795aSAndroid Build Coastguard Worker } 548*9e94795aSAndroid Build Coastguard Worker for _, target := range actualTargets { 549*9e94795aSAndroid Build Coastguard Worker if !rsExpected.AttachesToTarget(target) { 550*9e94795aSAndroid Build Coastguard Worker t.Errorf("unexpected extra target: got expected.AttachesTo(%q) is false, want true", target.name) 551*9e94795aSAndroid Build Coastguard Worker } 552*9e94795aSAndroid Build Coastguard Worker } 553*9e94795aSAndroid Build Coastguard Worker} 554