compliance package policy and resolves
package to read, consume, and analyze license metadata and dependency
graph.
Bug: 68860345
Bug: 151177513
Bug: 151953481
Change-Id: Ic08406fa2250a08ad26f2167d934f841c95d9148
diff --git a/tools/compliance/test_util.go b/tools/compliance/test_util.go
index 46df45e..a183b90 100644
--- a/tools/compliance/test_util.go
+++ b/tools/compliance/test_util.go
@@ -30,6 +30,85 @@
license_kinds: "SPDX-license-identifier-Apache-2.0"
license_conditions: "notice"
`
+
+ // GPL starts a test metadata file for GPL 2.0 licensing.
+ GPL = `` +
+`package_name: "Free Software"
+license_kinds: "SPDX-license-identifier-GPL-2.0"
+license_conditions: "restricted"
+`
+
+ // Classpath starts a test metadata file for GPL 2.0 with classpath exception licensing.
+ Classpath = `` +
+`package_name: "Free Software"
+license_kinds: "SPDX-license-identifier-GPL-2.0-with-classpath-exception"
+license_conditions: "restricted"
+`
+
+ // DependentModule starts a test metadata file for a module in the same package as `Classpath`.
+ DependentModule = `` +
+`package_name: "Free Software"
+license_kinds: "SPDX-license-identifier-MIT"
+license_conditions: "notice"
+`
+
+ // LGPL starts a test metadata file for a module with LGPL 2.0 licensing.
+ LGPL = `` +
+`package_name: "Free Library"
+license_kinds: "SPDX-license-identifier-LGPL-2.0"
+license_conditions: "restricted"
+`
+
+ // MPL starts a test metadata file for a module with MPL 2.0 reciprical licensing.
+ MPL = `` +
+`package_name: "Reciprocal"
+license_kinds: "SPDX-license-identifier-MPL-2.0"
+license_conditions: "reciprocal"
+`
+
+ // MIT starts a test metadata file for a module with generic notice (MIT) licensing.
+ MIT = `` +
+`package_name: "Android"
+license_kinds: "SPDX-license-identifier-MIT"
+license_conditions: "notice"
+`
+
+ // Proprietary starts a test metadata file for a module with proprietary licensing.
+ Proprietary = `` +
+`package_name: "Android"
+license_kinds: "legacy_proprietary"
+license_conditions: "proprietary"
+`
+
+ // ByException starts a test metadata file for a module with by_exception_only licensing.
+ ByException = `` +
+`package_name: "Special"
+license_kinds: "legacy_by_exception_only"
+license_conditions: "by_exception_only"
+`
+
+)
+
+var (
+ // meta maps test file names to metadata file content without dependencies.
+ meta = map[string]string{
+ "apacheBin.meta_lic": AOSP,
+ "apacheLib.meta_lic": AOSP,
+ "apacheContainer.meta_lic": AOSP + "is_container: true\n",
+ "dependentModule.meta_lic": DependentModule,
+ "gplWithClasspathException.meta_lic": Classpath,
+ "gplBin.meta_lic": GPL,
+ "gplLib.meta_lic": GPL,
+ "gplContainer.meta_lic": GPL + "is_container: true\n",
+ "lgplBin.meta_lic": LGPL,
+ "lgplLib.meta_lic": LGPL,
+ "mitBin.meta_lic": MIT,
+ "mitLib.meta_lic": MIT,
+ "mplBin.meta_lic": MPL,
+ "mplLib.meta_lic": MPL,
+ "proprietary.meta_lic": Proprietary,
+ "by_exception.meta_lic": ByException,
+ }
)
// toConditionList converts a test data map of condition name to origin names into a ConditionList.
@@ -125,6 +204,98 @@
return l[i].target < l[j].target
}
+
+// annotated describes annotated test data edges to define test graphs.
+type annotated struct {
+ target, dep string
+ annotations []string
+}
+
+func (e annotated) String() string {
+ if e.annotations != nil {
+ return e.target + " -> " + e.dep + " [" + strings.Join(e.annotations, ", ") + "]"
+ }
+ return e.target + " -> " + e.dep
+}
+
+func (e annotated) IsEqualTo(other annotated) bool {
+ if e.target != other.target {
+ return false
+ }
+ if e.dep != other.dep {
+ return false
+ }
+ if len(e.annotations) != len(other.annotations) {
+ return false
+ }
+ a1 := append([]string{}, e.annotations...)
+ a2 := append([]string{}, other.annotations...)
+ for i := 0; i < len(a1); i++ {
+ if a1[i] != a2[i] {
+ return false
+ }
+ }
+ return true
+}
+
+// toGraph converts a list of roots and a list of annotated edges into a test license graph.
+func toGraph(stderr io.Writer, roots []string, edges []annotated) (*LicenseGraph, error) {
+ deps := make(map[string][]annotated)
+ for _, root := range roots {
+ deps[root] = []annotated{}
+ }
+ for _, edge := range edges {
+ if prev, ok := deps[edge.target]; ok {
+ deps[edge.target] = append(prev, edge)
+ } else {
+ deps[edge.target] = []annotated{edge}
+ }
+ if _, ok := deps[edge.dep]; !ok {
+ deps[edge.dep] = []annotated{}
+ }
+ }
+ fs := make(testFS)
+ for file, edges := range deps {
+ body := meta[file]
+ for _, edge := range edges {
+ body += fmt.Sprintf("deps: {\n file: %q\n", edge.dep)
+ for _, ann := range edge.annotations {
+ body += fmt.Sprintf(" annotations: %q\n", ann)
+ }
+ body += "}\n"
+ }
+ fs[file] = []byte(body)
+ }
+
+ return ReadLicenseGraph(&fs, stderr, roots)
+}
+
+
+// byAnnotatedEdge orders edges by target then dep name then annotations.
+type byAnnotatedEdge []annotated
+
+func (l byAnnotatedEdge) Len() int { return len(l) }
+func (l byAnnotatedEdge) Swap(i, j int) { l[i], l[j] = l[j], l[i] }
+func (l byAnnotatedEdge) Less(i, j int) bool {
+ if l[i].target == l[j].target {
+ if l[i].dep == l[j].dep {
+ ai := append([]string{}, l[i].annotations...)
+ aj := append([]string{}, l[j].annotations...)
+ sort.Strings(ai)
+ sort.Strings(aj)
+ for k := 0; k < len(ai) && k < len(aj); k++ {
+ if ai[k] == aj[k] {
+ continue
+ }
+ return ai[k] < aj[k]
+ }
+ return len(ai) < len(aj)
+ }
+ return l[i].dep < l[j].dep
+ }
+ return l[i].target < l[j].target
+}
+
// res describes test data resolutions to define test resolution sets.
type res struct {
attachesTo, actsOn, origin, condition string
@@ -148,6 +319,28 @@
return &ResolutionSet{rmap}
}
+type confl struct {
+ sourceNode, share, privacy string
+}
+
+func toConflictList(lg *LicenseGraph, data []confl) []SourceSharePrivacyConflict {
+ result := make([]SourceSharePrivacyConflict, 0, len(data))
+ for _, c := range data {
+ fields := strings.Split(c.share, ":")
+ oshare := fields[0]
+ cshare := fields[1]
+ fields = strings.Split(c.privacy, ":")
+ oprivacy := fields[0]
+ cprivacy := fields[1]
+ result = append(result, SourceSharePrivacyConflict{
+ newTestNode(lg, c.sourceNode),
+ LicenseCondition{cshare, newTestNode(lg, oshare)},
+ LicenseCondition{cprivacy, newTestNode(lg, oprivacy)},
+ })
+ }
+ return result
+}
+
// checkSameActions compares an actual action set to an expected action set for a test.
func checkSameActions(lg *LicenseGraph, asActual, asExpected actionSet, t *testing.T) {
rsActual := ResolutionSet{make(map[*TargetNode]actionSet)}