1// Copyright 2021 Google LLC 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 compliance 16 17import ( 18 "bytes" 19 "fmt" 20 "sort" 21 "strings" 22 "testing" 23 24 "android/soong/tools/compliance/testfs" 25) 26 27func TestPolicy_edgeConditions(t *testing.T) { 28 tests := []struct { 29 name string 30 edge annotated 31 treatAsAggregate bool 32 otherCondition string 33 expectedDepActions []string 34 expectedTargetConditions []string 35 }{ 36 { 37 name: "firstparty", 38 edge: annotated{"apacheBin.meta_lic", "apacheLib.meta_lic", []string{"static"}}, 39 expectedDepActions: []string{}, 40 expectedTargetConditions: []string{}, 41 }, 42 { 43 name: "notice", 44 edge: annotated{"mitBin.meta_lic", "mitLib.meta_lic", []string{"static"}}, 45 expectedDepActions: []string{}, 46 expectedTargetConditions: []string{}, 47 }, 48 { 49 name: "fponlgpl", 50 edge: annotated{"apacheBin.meta_lic", "lgplLib.meta_lic", []string{"static"}}, 51 expectedDepActions: []string{ 52 "apacheBin.meta_lic:lgplLib.meta_lic:restricted_if_statically_linked", 53 "lgplLib.meta_lic:lgplLib.meta_lic:restricted_if_statically_linked", 54 }, 55 expectedTargetConditions: []string{}, 56 }, 57 { 58 name: "fponlgpldynamic", 59 edge: annotated{"apacheBin.meta_lic", "lgplLib.meta_lic", []string{"dynamic"}}, 60 expectedDepActions: []string{}, 61 expectedTargetConditions: []string{}, 62 }, 63 { 64 name: "fpongpl", 65 edge: annotated{"apacheBin.meta_lic", "gplLib.meta_lic", []string{"static"}}, 66 expectedDepActions: []string{ 67 "apacheBin.meta_lic:gplLib.meta_lic:restricted", 68 "gplLib.meta_lic:gplLib.meta_lic:restricted", 69 }, 70 expectedTargetConditions: []string{}, 71 }, 72 { 73 name: "fpongpldynamic", 74 edge: annotated{"apacheBin.meta_lic", "gplLib.meta_lic", []string{"dynamic"}}, 75 expectedDepActions: []string{ 76 "apacheBin.meta_lic:gplLib.meta_lic:restricted", 77 "gplLib.meta_lic:gplLib.meta_lic:restricted", 78 }, 79 expectedTargetConditions: []string{}, 80 }, 81 { 82 name: "independentmodule", 83 edge: annotated{"apacheBin.meta_lic", "gplWithClasspathException.meta_lic", []string{"dynamic"}}, 84 expectedDepActions: []string{}, 85 expectedTargetConditions: []string{}, 86 }, 87 { 88 name: "independentmodulestatic", 89 edge: annotated{"apacheBin.meta_lic", "gplWithClasspathException.meta_lic", []string{"static"}}, 90 expectedDepActions: []string{}, 91 expectedTargetConditions: []string{}, 92 }, 93 { 94 name: "dependentmodule", 95 edge: annotated{"dependentModule.meta_lic", "gplWithClasspathException.meta_lic", []string{"dynamic"}}, 96 expectedDepActions: []string{}, 97 expectedTargetConditions: []string{}, 98 }, 99 100 { 101 name: "lgplonfp", 102 edge: annotated{"lgplBin.meta_lic", "apacheLib.meta_lic", []string{"static"}}, 103 expectedDepActions: []string{}, 104 expectedTargetConditions: []string{"lgplBin.meta_lic:restricted_if_statically_linked"}, 105 }, 106 { 107 name: "lgplonfpdynamic", 108 edge: annotated{"lgplBin.meta_lic", "apacheLib.meta_lic", []string{"dynamic"}}, 109 expectedDepActions: []string{}, 110 expectedTargetConditions: []string{}, 111 }, 112 { 113 name: "gplonfp", 114 edge: annotated{"gplBin.meta_lic", "apacheLib.meta_lic", []string{"static"}}, 115 expectedDepActions: []string{}, 116 expectedTargetConditions: []string{"gplBin.meta_lic:restricted"}, 117 }, 118 { 119 name: "gplcontainer", 120 edge: annotated{"gplContainer.meta_lic", "apacheLib.meta_lic", []string{"static"}}, 121 treatAsAggregate: true, 122 expectedDepActions: []string{}, 123 expectedTargetConditions: []string{"gplContainer.meta_lic:restricted"}, 124 }, 125 { 126 name: "gploncontainer", 127 edge: annotated{"apacheContainer.meta_lic", "apacheLib.meta_lic", []string{"static"}}, 128 treatAsAggregate: true, 129 otherCondition: "gplLib.meta_lic:restricted", 130 expectedDepActions: []string{ 131 "apacheContainer.meta_lic:gplLib.meta_lic:restricted", 132 "apacheLib.meta_lic:gplLib.meta_lic:restricted", 133 "gplLib.meta_lic:gplLib.meta_lic:restricted", 134 }, 135 expectedTargetConditions: []string{}, 136 }, 137 { 138 name: "gplonbin", 139 edge: annotated{"apacheBin.meta_lic", "apacheLib.meta_lic", []string{"static"}}, 140 treatAsAggregate: false, 141 otherCondition: "gplLib.meta_lic:restricted", 142 expectedDepActions: []string{ 143 "apacheBin.meta_lic:gplLib.meta_lic:restricted", 144 "apacheLib.meta_lic:gplLib.meta_lic:restricted", 145 "gplLib.meta_lic:gplLib.meta_lic:restricted", 146 }, 147 expectedTargetConditions: []string{"gplLib.meta_lic:restricted"}, 148 }, 149 { 150 name: "gplonfpdynamic", 151 edge: annotated{"gplBin.meta_lic", "apacheLib.meta_lic", []string{"dynamic"}}, 152 expectedDepActions: []string{}, 153 expectedTargetConditions: []string{"gplBin.meta_lic:restricted"}, 154 }, 155 { 156 name: "independentmodulereverse", 157 edge: annotated{"gplWithClasspathException.meta_lic", "apacheBin.meta_lic", []string{"dynamic"}}, 158 expectedDepActions: []string{}, 159 expectedTargetConditions: []string{}, 160 }, 161 { 162 name: "independentmodulereversestatic", 163 edge: annotated{"gplWithClasspathException.meta_lic", "apacheBin.meta_lic", []string{"static"}}, 164 expectedDepActions: []string{}, 165 expectedTargetConditions: []string{}, 166 }, 167 { 168 name: "dependentmodulereverse", 169 edge: annotated{"gplWithClasspathException.meta_lic", "dependentModule.meta_lic", []string{"dynamic"}}, 170 expectedDepActions: []string{}, 171 expectedTargetConditions: []string{}, 172 }, 173 { 174 name: "ponr", 175 edge: annotated{"proprietary.meta_lic", "gplLib.meta_lic", []string{"static"}}, 176 expectedDepActions: []string{ 177 "proprietary.meta_lic:gplLib.meta_lic:restricted", 178 "gplLib.meta_lic:gplLib.meta_lic:restricted", 179 }, 180 expectedTargetConditions: []string{}, 181 }, 182 { 183 name: "ronp", 184 edge: annotated{"gplBin.meta_lic", "proprietary.meta_lic", []string{"static"}}, 185 expectedDepActions: []string{}, 186 expectedTargetConditions: []string{"gplBin.meta_lic:restricted"}, 187 }, 188 { 189 name: "noticeonb_e_o", 190 edge: annotated{"mitBin.meta_lic", "by_exception.meta_lic", []string{"static"}}, 191 expectedDepActions: []string{}, 192 expectedTargetConditions: []string{}, 193 }, 194 { 195 name: "b_e_oonnotice", 196 edge: annotated{"by_exception.meta_lic", "mitLib.meta_lic", []string{"static"}}, 197 expectedDepActions: []string{}, 198 expectedTargetConditions: []string{}, 199 }, 200 { 201 name: "noticeonrecip", 202 edge: annotated{"mitBin.meta_lic", "mplLib.meta_lic", []string{"static"}}, 203 expectedDepActions: []string{}, 204 expectedTargetConditions: []string{}, 205 }, 206 { 207 name: "reciponnotice", 208 edge: annotated{"mplBin.meta_lic", "mitLib.meta_lic", []string{"static"}}, 209 expectedDepActions: []string{}, 210 expectedTargetConditions: []string{}, 211 }, 212 } 213 for _, tt := range tests { 214 t.Run(tt.name, func(t *testing.T) { 215 fs := make(testfs.TestFS) 216 stderr := &bytes.Buffer{} 217 target := meta[tt.edge.target] + fmt.Sprintf("deps: {\n file: \"%s\"\n", tt.edge.dep) 218 for _, ann := range tt.edge.annotations { 219 target += fmt.Sprintf(" annotations: \"%s\"\n", ann) 220 } 221 fs[tt.edge.target] = []byte(target + "}\n") 222 fs[tt.edge.dep] = []byte(meta[tt.edge.dep]) 223 lg, err := ReadLicenseGraph(&fs, stderr, []string{tt.edge.target}) 224 if err != nil { 225 t.Errorf("unexpected error reading graph: %s", err) 226 return 227 } 228 edge := lg.Edges()[0] 229 // simulate a condition inherited from another edge/dependency. 230 otherTarget := "" 231 otherCondition := "" 232 var otn *TargetNode 233 if len(tt.otherCondition) > 0 { 234 fields := strings.Split(tt.otherCondition, ":") 235 otherTarget = fields[0] 236 otherCondition = fields[1] 237 otn = &TargetNode{name: otherTarget} 238 // other target must exist in graph 239 lg.targets[otherTarget] = otn 240 otn.licenseConditions = LicenseConditionSet(RecognizedConditionNames[otherCondition]) 241 } 242 targets := make(map[string]*TargetNode) 243 targets[edge.target.name] = edge.target 244 targets[edge.dependency.name] = edge.dependency 245 if otn != nil { 246 targets[otn.name] = otn 247 } 248 if tt.expectedDepActions != nil { 249 t.Run("depConditionsPropagatingToTarget", func(t *testing.T) { 250 depConditions := edge.dependency.LicenseConditions() 251 if otherTarget != "" { 252 // simulate a sub-dependency's condition having already propagated up to dep and about to go to target 253 otherCs := otn.LicenseConditions() 254 depConditions |= otherCs 255 } 256 t.Logf("calculate target actions for edge=%s, dep conditions=%#v %s, treatAsAggregate=%v", edge.String(), depConditions, depConditions, tt.treatAsAggregate) 257 csActual := depConditionsPropagatingToTarget(lg, edge, depConditions, tt.treatAsAggregate) 258 t.Logf("calculated target conditions as %#v %s", csActual, csActual) 259 csExpected := NewLicenseConditionSet() 260 for _, triple := range tt.expectedDepActions { 261 fields := strings.Split(triple, ":") 262 expectedConditions := NewLicenseConditionSet() 263 for _, cname := range fields[2:] { 264 expectedConditions = expectedConditions.Plus(RecognizedConditionNames[cname]) 265 } 266 csExpected |= expectedConditions 267 } 268 t.Logf("expected target conditions as %#v %s", csExpected, csExpected) 269 if csActual != csExpected { 270 t.Errorf("unexpected license conditions: got %#v, want %#v", csActual, csExpected) 271 } 272 }) 273 } 274 if tt.expectedTargetConditions != nil { 275 t.Run("targetConditionsPropagatingToDep", func(t *testing.T) { 276 targetConditions := edge.target.LicenseConditions() 277 if otherTarget != "" { 278 targetConditions = targetConditions.Union(otn.licenseConditions) 279 } 280 t.Logf("calculate dep conditions for edge=%s, target conditions=%v, treatAsAggregate=%v", edge.String(), targetConditions.Names(), tt.treatAsAggregate) 281 cs := targetConditionsPropagatingToDep(lg, edge, targetConditions, tt.treatAsAggregate, AllResolutions) 282 t.Logf("calculated dep conditions as %v", cs.Names()) 283 actual := cs.Names() 284 sort.Strings(actual) 285 expected := make([]string, 0) 286 for _, expectedDepCondition := range tt.expectedTargetConditions { 287 expected = append(expected, strings.Split(expectedDepCondition, ":")[1]) 288 } 289 sort.Strings(expected) 290 if len(actual) != len(expected) { 291 t.Errorf("unexpected number of target conditions: got %v with %d conditions, want %v with %d conditions", 292 actual, len(actual), expected, len(expected)) 293 } else { 294 for i := 0; i < len(actual); i++ { 295 if actual[i] != expected[i] { 296 t.Errorf("unexpected target condition at element %d: got %q, want %q", 297 i, actual[i], expected[i]) 298 } 299 } 300 } 301 }) 302 } 303 }) 304 } 305} 306