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/Android.bp b/tools/compliance/Android.bp
index 648bc9a..e56a471 100644
--- a/tools/compliance/Android.bp
+++ b/tools/compliance/Android.bp
@@ -24,6 +24,14 @@
"condition.go",
"conditionset.go",
"graph.go",
+ "policy/policy.go",
+ "policy/resolve.go",
+ "policy/resolvenotices.go",
+ "policy/resolveshare.go",
+ "policy/resolveprivacy.go",
+ "policy/shareprivacyconflicts.go",
+ "policy/shipped.go",
+ "policy/walk.go",
"readgraph.go",
"resolution.go",
"resolutionset.go",
@@ -32,6 +40,14 @@
"condition_test.go",
"conditionset_test.go",
"readgraph_test.go",
+ "policy/policy_test.go",
+ "policy/resolve_test.go",
+ "policy/resolvenotices_test.go",
+ "policy/resolveshare_test.go",
+ "policy/resolveprivacy_test.go",
+ "policy/shareprivacyconflicts_test.go",
+ "policy/shipped_test.go",
+ "policy/walk_test.go",
"resolutionset_test.go",
"test_util.go",
],
diff --git a/tools/compliance/policy/policy.go b/tools/compliance/policy/policy.go
new file mode 100644
index 0000000..9dab05b
--- /dev/null
+++ b/tools/compliance/policy/policy.go
@@ -0,0 +1,238 @@
+// Copyright 2021 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package compliance
+
+import (
+ "regexp"
+ "strings"
+)
+
+var (
+ // ImpliesUnencumbered lists the condition names representing an author attempt to disclaim copyright.
+ ImpliesUnencumbered = ConditionNames{"unencumbered"}
+
+ // ImpliesPermissive lists the condition names representing copyrighted but "licensed without policy requirements".
+ ImpliesPermissive = ConditionNames{"permissive"}
+
+ // ImpliesNotice lists the condition names implying a notice or attribution policy.
+ ImpliesNotice = ConditionNames{"unencumbered", "permissive", "notice", "reciprocal", "restricted", "proprietary", "by_exception_only"}
+
+ // ImpliesReciprocal lists the condition names implying a local source-sharing policy.
+ ImpliesReciprocal = ConditionNames{"reciprocal"}
+
+ // Restricted lists the condition names implying an infectious source-sharing policy.
+ ImpliesRestricted = ConditionNames{"restricted"}
+
+ // ImpliesProprietary lists the condition names implying a confidentiality policy.
+ ImpliesProprietary = ConditionNames{"proprietary"}
+
+ // ImpliesByExceptionOnly lists the condition names implying a policy for "license review and approval before use".
+ ImpliesByExceptionOnly = ConditionNames{"proprietary", "by_exception_only"}
+
+ // ImpliesPrivate lists the condition names implying a source-code privacy policy.
+ ImpliesPrivate = ConditionNames{"proprietary"}
+
+ // ImpliesShared lists the condition names implying a source-code sharing policy.
+ ImpliesShared = ConditionNames{"reciprocal", "restricted"}
+)
+
+var (
+ anyLgpl = regexp.MustCompile(`^SPDX-license-identifier-LGPL.*`)
+ versionedGpl = regexp.MustCompile(`^SPDX-license-identifier-GPL-\p{N}.*`)
+ genericGpl = regexp.MustCompile(`^SPDX-license-identifier-GPL$`)
+ ccBySa = regexp.MustCompile(`^SPDX-license-identifier-CC-BY.*-SA.*`)
+)
+
+// Resolution happens in two passes:
+//
+// 1. A bottom-up traversal propagates license conditions up to targets from
+// dendencies as needed.
+//
+// 2. For each condition of interest, a top-down traversal adjusts the attached
+// conditions pushing restricted down from targets into linked dependencies.
+//
+// The behavior of the 2 passes gets controlled by the 2 functions below.
+//
+// The first function controls what happens during the bottom-up traversal. In
+// general conditions flow up through static links but not other dependencies;
+// except, restricted sometimes flows up through dynamic links.
+//
+// In general, too, the originating target gets acted on to resolve the
+// condition (e.g. providing notice), but again restricted is special in that
+// it requires acting on (i.e. sharing source of) both the originating module
+// and the target using the module.
+//
+// The latter function controls what happens during the top-down traversal. In
+// general, only restricted conditions flow down at all, and only through
+// static links.
+//
+// Not all restricted licenses are create equal. Some have special rules or
+// exceptions. e.g. LGPL or "with classpath excption".
+
+// depActionsApplicableToTarget returns the actions which propagate up an
+// edge from dependency to target.
+//
+// This function sets the policy for the bottom-up traversal and how conditions
+// flow up the graph from dependencies to targets.
+//
+// If a pure aggregation is built into a derivative work that is not a pure
+// aggregation, per policy it ceases to be a pure aggregation in the context of
+// that derivative work. The `treatAsAggregate` parameter will be false for
+// non-aggregates and for aggregates in non-aggregate contexts.
+func depActionsApplicableToTarget(e TargetEdge, depActions actionSet, treatAsAggregate bool) actionSet {
+ result := make(actionSet)
+ if edgeIsDerivation(e) {
+ result.addSet(depActions)
+ for _, cs := range depActions.byName(ImpliesRestricted) {
+ result.add(e.Target(), cs)
+ }
+ return result
+ }
+ if !edgeIsDynamicLink(e) {
+ return result
+ }
+
+ restricted := depActions.byName(ImpliesRestricted)
+ for actsOn, cs := range restricted {
+ for _, lc := range cs.AsList() {
+ hasGpl := false
+ hasLgpl := false
+ hasClasspath := false
+ hasGeneric := false
+ hasOther := false
+ for _, kind := range lc.origin.LicenseKinds() {
+ if strings.HasSuffix(kind, "-with-classpath-exception") {
+ hasClasspath = true
+ } else if anyLgpl.MatchString(kind) {
+ hasLgpl = true
+ } else if versionedGpl.MatchString(kind) {
+ hasGpl = true
+ } else if genericGpl.MatchString(kind) {
+ hasGeneric = true
+ } else if kind == "legacy_restricted" || ccBySa.MatchString(kind) {
+ hasOther = true
+ }
+ }
+ if hasOther || hasGpl {
+ result.addCondition(actsOn, lc)
+ result.addCondition(e.Target(), lc)
+ continue
+ }
+ if hasClasspath && !edgeNodesAreIndependentModules(e) {
+ result.addCondition(actsOn, lc)
+ result.addCondition(e.Target(), lc)
+ continue
+ }
+ if hasLgpl || hasClasspath {
+ continue
+ }
+ if !hasGeneric {
+ continue
+ }
+ result.addCondition(actsOn, lc)
+ result.addCondition(e.Target(), lc)
+ }
+ }
+ return result
+}
+
+// targetConditionsApplicableToDep returns the conditions which propagate down
+// an edge from target to dependency.
+//
+// This function sets the policy for the top-down traversal and how conditions
+// flow down the graph from targets to dependencies.
+//
+// If a pure aggregation is built into a derivative work that is not a pure
+// aggregation, per policy it ceases to be a pure aggregation in the context of
+// that derivative work. The `treatAsAggregate` parameter will be false for
+// non-aggregates and for aggregates in non-aggregate contexts.
+func targetConditionsApplicableToDep(e TargetEdge, targetConditions *LicenseConditionSet, treatAsAggregate bool) *LicenseConditionSet {
+ result := targetConditions.Copy()
+
+ // reverse direction -- none of these apply to things depended-on, only to targets depending-on.
+ result.RemoveAllByName(ConditionNames{"unencumbered", "permissive", "notice", "reciprocal", "proprietary", "by_exception_only"})
+
+ if !edgeIsDerivation(e) && !edgeIsDynamicLink(e) {
+ // target is not a derivative work of dependency and is not linked to dependency
+ result.RemoveAllByName(ImpliesRestricted)
+ return result
+ }
+ if treatAsAggregate {
+ // If the author of a pure aggregate licenses it restricted, apply restricted to immediate dependencies.
+ // Otherwise, restricted does not propagate back down to dependencies.
+ restricted := result.ByName(ImpliesRestricted).AsList()
+ for _, lc := range restricted {
+ if lc.origin.name != e.e.target {
+ result.Remove(lc)
+ }
+ }
+ return result
+ }
+ if edgeIsDerivation(e) {
+ return result
+ }
+ restricted := result.ByName(ImpliesRestricted).AsList()
+ for _, lc := range restricted {
+ hasGpl := false
+ hasLgpl := false
+ hasClasspath := false
+ hasGeneric := false
+ hasOther := false
+ for _, kind := range lc.origin.LicenseKinds() {
+ if strings.HasSuffix(kind, "-with-classpath-exception") {
+ hasClasspath = true
+ } else if anyLgpl.MatchString(kind) {
+ hasLgpl = true
+ } else if versionedGpl.MatchString(kind) {
+ hasGpl = true
+ } else if genericGpl.MatchString(kind) {
+ hasGeneric = true
+ } else if kind == "legacy_restricted" || ccBySa.MatchString(kind) {
+ hasOther = true
+ }
+ }
+ if hasOther || hasGpl {
+ continue
+ }
+ if hasClasspath && !edgeNodesAreIndependentModules(e) {
+ continue
+ }
+ if hasGeneric && !hasLgpl && !hasClasspath {
+ continue
+ }
+ result.Remove(lc)
+ }
+ return result
+}
+
+// edgeIsDynamicLink returns true for edges representing shared libraries
+// linked dynamically at runtime.
+func edgeIsDynamicLink(e TargetEdge) bool {
+ return e.e.annotations.HasAnnotation("dynamic")
+}
+
+// edgeIsDerivation returns true for edges where the target is a derivative
+// work of dependency.
+func edgeIsDerivation(e TargetEdge) bool {
+ isDynamic := e.e.annotations.HasAnnotation("dynamic")
+ isToolchain := e.e.annotations.HasAnnotation("toolchain")
+ return !isDynamic && !isToolchain
+}
+
+// edgeNodesAreIndependentModules returns true for edges where the target and
+// dependency are independent modules.
+func edgeNodesAreIndependentModules(e TargetEdge) bool {
+ return e.Target().PackageName() != e.Dependency().PackageName()
+}
diff --git a/tools/compliance/policy/policy_test.go b/tools/compliance/policy/policy_test.go
new file mode 100644
index 0000000..aea307f
--- /dev/null
+++ b/tools/compliance/policy/policy_test.go
@@ -0,0 +1,300 @@
+// Copyright 2021 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package compliance
+
+import (
+ "bytes"
+ "fmt"
+ "sort"
+ "strings"
+ "testing"
+)
+
+func TestPolicy_edgeConditions(t *testing.T) {
+ tests := []struct {
+ name string
+ edge annotated
+ treatAsAggregate bool
+ otherCondition string
+ expectedDepActions []string
+ expectedTargetConditions []string
+ }{
+ {
+ name: "firstparty",
+ edge: annotated{"apacheBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
+ expectedDepActions: []string{"apacheLib.meta_lic:apacheLib.meta_lic:notice"},
+ expectedTargetConditions: []string{},
+ },
+ {
+ name: "notice",
+ edge: annotated{"mitBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
+ expectedDepActions: []string{"mitLib.meta_lic:mitLib.meta_lic:notice"},
+ expectedTargetConditions: []string{},
+ },
+ {
+ name: "fponlgpl",
+ edge: annotated{"apacheBin.meta_lic", "lgplLib.meta_lic", []string{"static"}},
+ expectedDepActions: []string{
+ "apacheBin.meta_lic:lgplLib.meta_lic:restricted",
+ "lgplLib.meta_lic:lgplLib.meta_lic:restricted",
+ },
+ expectedTargetConditions: []string{},
+ },
+ {
+ name: "fponlgpldynamic",
+ edge: annotated{"apacheBin.meta_lic", "lgplLib.meta_lic", []string{"dynamic"}},
+ expectedDepActions: []string{},
+ expectedTargetConditions: []string{},
+ },
+ {
+ name: "fpongpl",
+ edge: annotated{"apacheBin.meta_lic", "gplLib.meta_lic", []string{"static"}},
+ expectedDepActions: []string{
+ "apacheBin.meta_lic:gplLib.meta_lic:restricted",
+ "gplLib.meta_lic:gplLib.meta_lic:restricted",
+ },
+ expectedTargetConditions: []string{},
+ },
+ {
+ name: "fpongpldynamic",
+ edge: annotated{"apacheBin.meta_lic", "gplLib.meta_lic", []string{"dynamic"}},
+ expectedDepActions: []string{
+ "apacheBin.meta_lic:gplLib.meta_lic:restricted",
+ "gplLib.meta_lic:gplLib.meta_lic:restricted",
+ },
+ expectedTargetConditions: []string{},
+ },
+ {
+ name: "independentmodule",
+ edge: annotated{"apacheBin.meta_lic", "gplWithClasspathException.meta_lic", []string{"dynamic"}},
+ expectedDepActions: []string{},
+ expectedTargetConditions: []string{},
+ },
+ {
+ name: "independentmodulestatic",
+ edge: annotated{"apacheBin.meta_lic", "gplWithClasspathException.meta_lic", []string{"static"}},
+ expectedDepActions: []string{
+ "apacheBin.meta_lic:gplWithClasspathException.meta_lic:restricted",
+ "gplWithClasspathException.meta_lic:gplWithClasspathException.meta_lic:restricted",
+ },
+ expectedTargetConditions: []string{},
+ },
+ {
+ name: "dependentmodule",
+ edge: annotated{"dependentModule.meta_lic", "gplWithClasspathException.meta_lic", []string{"dynamic"}},
+ expectedDepActions: []string{
+ "dependentModule.meta_lic:gplWithClasspathException.meta_lic:restricted",
+ "gplWithClasspathException.meta_lic:gplWithClasspathException.meta_lic:restricted",
+ },
+ expectedTargetConditions: []string{},
+ },
+
+ {
+ name: "lgplonfp",
+ edge: annotated{"lgplBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
+ expectedDepActions: []string{"apacheLib.meta_lic:apacheLib.meta_lic:notice"},
+ expectedTargetConditions: []string{"lgplBin.meta_lic:restricted"},
+ },
+ {
+ name: "lgplonfpdynamic",
+ edge: annotated{"lgplBin.meta_lic", "apacheLib.meta_lic", []string{"dynamic"}},
+ expectedDepActions: []string{},
+ expectedTargetConditions: []string{},
+ },
+ {
+ name: "gplonfp",
+ edge: annotated{"gplBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
+ expectedDepActions: []string{"apacheLib.meta_lic:apacheLib.meta_lic:notice"},
+ expectedTargetConditions: []string{"gplBin.meta_lic:restricted"},
+ },
+ {
+ name: "gplcontainer",
+ edge: annotated{"gplContainer.meta_lic", "apacheLib.meta_lic", []string{"static"}},
+ treatAsAggregate: true,
+ expectedDepActions: []string{"apacheLib.meta_lic:apacheLib.meta_lic:notice"},
+ expectedTargetConditions: []string{"gplContainer.meta_lic:restricted"},
+ },
+ {
+ name: "gploncontainer",
+ edge: annotated{"apacheContainer.meta_lic", "apacheLib.meta_lic", []string{"static"}},
+ treatAsAggregate: true,
+ otherCondition: "gplLib.meta_lic:restricted",
+ expectedDepActions: []string{
+ "apacheContainer.meta_lic:gplLib.meta_lic:restricted",
+ "apacheLib.meta_lic:apacheLib.meta_lic:notice",
+ "apacheLib.meta_lic:gplLib.meta_lic:restricted",
+ "gplLib.meta_lic:gplLib.meta_lic:restricted",
+ },
+ expectedTargetConditions: []string{},
+ },
+ {
+ name: "gplonbin",
+ edge: annotated{"apacheBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
+ treatAsAggregate: false,
+ otherCondition: "gplLib.meta_lic:restricted",
+ expectedDepActions: []string{
+ "apacheBin.meta_lic:gplLib.meta_lic:restricted",
+ "apacheLib.meta_lic:apacheLib.meta_lic:notice",
+ "apacheLib.meta_lic:gplLib.meta_lic:restricted",
+ "gplLib.meta_lic:gplLib.meta_lic:restricted",
+ },
+ expectedTargetConditions: []string{"gplLib.meta_lic:restricted"},
+ },
+ {
+ name: "gplonfpdynamic",
+ edge: annotated{"gplBin.meta_lic", "apacheLib.meta_lic", []string{"dynamic"}},
+ expectedDepActions: []string{},
+ expectedTargetConditions: []string{"gplBin.meta_lic:restricted"},
+ },
+ {
+ name: "independentmodulereverse",
+ edge: annotated{"gplWithClasspathException.meta_lic", "apacheBin.meta_lic", []string{"dynamic"}},
+ expectedDepActions: []string{},
+ expectedTargetConditions: []string{},
+ },
+ {
+ name: "independentmodulereversestatic",
+ edge: annotated{"gplWithClasspathException.meta_lic", "apacheBin.meta_lic", []string{"static"}},
+ expectedDepActions: []string{"apacheBin.meta_lic:apacheBin.meta_lic:notice"},
+ expectedTargetConditions: []string{"gplWithClasspathException.meta_lic:restricted"},
+ },
+ {
+ name: "dependentmodulereverse",
+ edge: annotated{"gplWithClasspathException.meta_lic", "dependentModule.meta_lic", []string{"dynamic"}},
+ expectedDepActions: []string{},
+ expectedTargetConditions: []string{"gplWithClasspathException.meta_lic:restricted"},
+ },
+ {
+ name: "ponr",
+ edge: annotated{"proprietary.meta_lic", "gplLib.meta_lic", []string{"static"}},
+ expectedDepActions: []string{
+ "proprietary.meta_lic:gplLib.meta_lic:restricted",
+ "gplLib.meta_lic:gplLib.meta_lic:restricted",
+ },
+ expectedTargetConditions: []string{},
+ },
+ {
+ name: "ronp",
+ edge: annotated{"gplBin.meta_lic", "proprietary.meta_lic", []string{"static"}},
+ expectedDepActions: []string{"proprietary.meta_lic:proprietary.meta_lic:proprietary"},
+ expectedTargetConditions: []string{"gplBin.meta_lic:restricted"},
+ },
+ {
+ name: "noticeonb_e_o",
+ edge: annotated{"mitBin.meta_lic", "by_exception.meta_lic", []string{"static"}},
+ expectedDepActions: []string{"by_exception.meta_lic:by_exception.meta_lic:by_exception_only"},
+ expectedTargetConditions: []string{},
+ },
+ {
+ name: "b_e_oonnotice",
+ edge: annotated{"by_exception.meta_lic", "mitLib.meta_lic", []string{"static"}},
+ expectedDepActions: []string{"mitLib.meta_lic:mitLib.meta_lic:notice"},
+ expectedTargetConditions: []string{},
+ },
+ {
+ name: "noticeonrecip",
+ edge: annotated{"mitBin.meta_lic", "mplLib.meta_lic", []string{"static"}},
+ expectedDepActions: []string{"mplLib.meta_lic:mplLib.meta_lic:reciprocal"},
+ expectedTargetConditions: []string{},
+ },
+ {
+ name: "reciponnotice",
+ edge: annotated{"mplBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
+ expectedDepActions: []string{"mitLib.meta_lic:mitLib.meta_lic:notice"},
+ expectedTargetConditions: []string{},
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ fs := make(testFS)
+ stderr := &bytes.Buffer{}
+ target := meta[tt.edge.target] + fmt.Sprintf("deps: {\n file: \"%s\"\n", tt.edge.dep)
+ for _, ann := range tt.edge.annotations {
+ target += fmt.Sprintf(" annotations: \"%s\"\n", ann)
+ }
+ fs[tt.edge.target] = []byte(target + "}\n")
+ fs[tt.edge.dep] = []byte(meta[tt.edge.dep])
+ lg, err := ReadLicenseGraph(&fs, stderr, []string{tt.edge.target})
+ if err != nil {
+ t.Errorf("unexpected error reading graph: %w", err)
+ return
+ }
+ // simulate a condition inherited from another edge/dependency.
+ otherTarget := ""
+ otherCondition := ""
+ if len(tt.otherCondition) > 0 {
+ fields := strings.Split(tt.otherCondition, ":")
+ otherTarget = fields[0]
+ otherCondition = fields[1]
+ // other target must exist in graph
+ lg.targets[otherTarget] = &TargetNode{name: otherTarget}
+ lg.targets[otherTarget].proto.LicenseConditions = append(lg.targets[otherTarget].proto.LicenseConditions, otherCondition)
+ }
+ if tt.expectedDepActions != nil {
+ depActions := make(actionSet)
+ depActions[lg.targets[tt.edge.dep]] = lg.targets[tt.edge.dep].LicenseConditions()
+ if otherTarget != "" {
+ // simulate a sub-dependency's condition having already propagated up to dep and about to go to target
+ otherCs := lg.targets[otherTarget].LicenseConditions()
+ depActions[lg.targets[tt.edge.dep]].AddSet(otherCs)
+ depActions[lg.targets[otherTarget]] = otherCs
+ }
+ asActual := depActionsApplicableToTarget(lg.Edges()[0], depActions, tt.treatAsAggregate)
+ asExpected := make(actionSet)
+ for _, triple := range tt.expectedDepActions {
+ fields := strings.Split(triple, ":")
+ actsOn := lg.targets[fields[0]]
+ origin := lg.targets[fields[1]]
+ expectedConditions := newLicenseConditionSet()
+ expectedConditions.add(origin, fields[2:]...)
+ if _, ok := asExpected[actsOn]; ok {
+ asExpected[actsOn].AddSet(expectedConditions)
+ } else {
+ asExpected[actsOn] = expectedConditions
+ }
+ }
+
+ checkSameActions(lg, asActual, asExpected, t)
+ }
+ if tt.expectedTargetConditions != nil {
+ targetConditions := lg.TargetNode(tt.edge.target).LicenseConditions()
+ if otherTarget != "" {
+ targetConditions.add(lg.targets[otherTarget], otherCondition)
+ }
+ cs := targetConditionsApplicableToDep(
+ lg.Edges()[0],
+ targetConditions,
+ tt.treatAsAggregate)
+ actual := make([]string, 0, cs.Count())
+ for _, lc := range cs.AsList() {
+ actual = append(actual, lc.asString(":"))
+ }
+ sort.Strings(actual)
+ sort.Strings(tt.expectedTargetConditions)
+ if len(actual) != len(tt.expectedTargetConditions) {
+ t.Errorf("unexpected number of target conditions: got %v with %d conditions, want %v with %d conditions",
+ actual, len(actual), tt.expectedTargetConditions, len(tt.expectedTargetConditions))
+ } else {
+ for i := 0; i < len(actual); i++ {
+ if actual[i] != tt.expectedTargetConditions[i] {
+ t.Errorf("unexpected target condition at element %d: got %q, want %q",
+ i, actual[i], tt.expectedTargetConditions[i])
+ }
+ }
+ }
+ }
+ })
+ }
+}
diff --git a/tools/compliance/policy/resolve.go b/tools/compliance/policy/resolve.go
new file mode 100644
index 0000000..9962e68
--- /dev/null
+++ b/tools/compliance/policy/resolve.go
@@ -0,0 +1,217 @@
+// Copyright 2021 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package compliance
+
+// ResolveBottomUpConditions performs a bottom-up walk of the LicenseGraph
+// propagating conditions up the graph as necessary according to the properties
+// of each edge and according to each license condition in question.
+//
+// Subsequent top-down walks of the graph will filter some resolutions and may
+// introduce new resolutions.
+//
+// e.g. if a "restricted" condition applies to a binary, it also applies to all
+// of the statically-linked libraries and the transitive closure of their static
+// dependencies; even if neither they nor the transitive closure of their
+// dependencies originate any "restricted" conditions. The bottom-up walk will
+// not resolve the library and its transitive closure, but the later top-down
+// walk will.
+func ResolveBottomUpConditions(lg *LicenseGraph) *ResolutionSet {
+
+ // short-cut if already walked and cached
+ lg.mu.Lock()
+ rs := lg.rsBU
+ lg.mu.Unlock()
+
+ if rs != nil {
+ return rs
+ }
+
+ // must be indexed for fast lookup
+ lg.indexForward()
+
+ rs = newResolutionSet()
+
+ // cmap contains an entry for every target that was previously walked as a pure aggregate only.
+ cmap := make(map[string]bool)
+
+ var walk func(f string, treatAsAggregate bool) actionSet
+
+ walk = func(f string, treatAsAggregate bool) actionSet {
+ target := lg.targets[f]
+ result := make(actionSet)
+ result[target] = newLicenseConditionSet()
+ result[target].add(target, target.proto.LicenseConditions...)
+ if preresolved, ok := rs.resolutions[target]; ok {
+ if treatAsAggregate {
+ result.addSet(preresolved)
+ return result
+ }
+ if _, asAggregate := cmap[f]; !asAggregate {
+ result.addSet(preresolved)
+ return result
+ }
+ // previously walked in a pure aggregate context,
+ // needs to walk again in non-aggregate context
+ delete(cmap, f)
+ }
+ if treatAsAggregate {
+ cmap[f] = true
+ }
+
+ // add all the conditions from all the dependencies
+ for _, edge := range lg.index[f] {
+ // walk dependency to get its conditions
+ as := walk(edge.dependency, treatAsAggregate && lg.targets[edge.dependency].IsContainer())
+
+ // turn those into the conditions that apply to the target
+ as = depActionsApplicableToTarget(TargetEdge{lg, edge}, as, treatAsAggregate)
+
+ // add them to the result
+ result.addSet(as)
+ }
+
+ // record these conditions as applicable to the target
+ rs.addConditions(target, result)
+ rs.addSelf(target, result.byName(ImpliesRestricted))
+
+ // return this up the tree
+ return result
+ }
+
+ // walk each of the roots
+ for _, r := range lg.rootFiles {
+ _ = walk(r, lg.targets[r].IsContainer())
+ }
+
+ // if not yet cached, save the result
+ lg.mu.Lock()
+ if lg.rsBU == nil {
+ lg.rsBU = rs
+ } else {
+ // if we end up with 2, release the later for garbage collection
+ rs = lg.rsBU
+ }
+ lg.mu.Unlock()
+
+ return rs
+}
+
+// ResolveTopDownCondtions performs a top-down walk of the LicenseGraph
+// resolving all reachable nodes for `condition`. Policy establishes the rules
+// for transforming and propagating resolutions down the graph.
+//
+// e.g. For current policy, none of the conditions propagate from target to
+// dependency except restricted. For restricted, the policy is to share the
+// source of any libraries linked to restricted code and to provide notice.
+func ResolveTopDownConditions(lg *LicenseGraph) *ResolutionSet {
+
+ // short-cut if already walked and cached
+ lg.mu.Lock()
+ rs := lg.rsTD
+ lg.mu.Unlock()
+
+ if rs != nil {
+ return rs
+ }
+
+ // start with the conditions propagated up the graph
+ rs = ResolveBottomUpConditions(lg)
+
+ // rmap maps 'appliesTo' targets to their applicable conditions
+ //
+ // rmap is the resulting ResolutionSet
+ rmap := make(map[*TargetNode]actionSet)
+ for attachesTo, as := range rs.resolutions {
+ rmap[attachesTo] = as.copy()
+ }
+
+ path := make([]*dependencyEdge, 0, 32)
+
+ var walk func(f string, cs *LicenseConditionSet, treatAsAggregate bool)
+
+ walk = func(f string, cs *LicenseConditionSet, treatAsAggregate bool) {
+ fnode := lg.targets[f]
+ if !cs.IsEmpty() {
+ parentsAllAggregate := true
+ for _, e := range path {
+ target := lg.targets[e.target]
+ if _, ok := rmap[target]; !ok {
+ rmap[target] = make(actionSet)
+ }
+ rmap[target].add(fnode, cs)
+ if !target.IsContainer() {
+ parentsAllAggregate = false
+ break
+ }
+ }
+ if parentsAllAggregate {
+ if _, ok := rmap[fnode]; !ok {
+ rmap[fnode] = make(actionSet)
+ }
+ rmap[fnode].add(fnode, cs)
+ }
+ }
+ // add conditions attached to `f`
+ cs = cs.Copy()
+ for _, fcs := range rs.resolutions[fnode] {
+ cs.AddSet(fcs)
+ }
+ // for each dependency
+ for _, edge := range lg.index[f] {
+ e := TargetEdge{lg, edge}
+ // dcs holds the dpendency conditions inherited from the target
+ dcs := targetConditionsApplicableToDep(e, cs, treatAsAggregate)
+ if dcs.IsEmpty() {
+ if !treatAsAggregate || (!edgeIsDerivation(e) && !edgeIsDynamicLink(e)) {
+ continue
+ }
+ }
+ path = append(path, edge)
+ // add the conditions to the dependency
+ walk(edge.dependency, dcs, treatAsAggregate && lg.targets[edge.dependency].IsContainer())
+ path = path[:len(path)-1]
+ }
+ }
+
+ // walk each of the roots
+ for _, r := range lg.rootFiles {
+ as, ok := rs.resolutions[lg.targets[r]]
+ if !ok {
+ // no conditions in root or transitive closure of dependencies
+ continue
+ }
+ if as.isEmpty() {
+ continue
+ }
+
+ path = path[:0]
+ // add the conditions to the root and its transitive closure
+ walk(r, newLicenseConditionSet(), lg.targets[r].IsContainer())
+ }
+
+ rs = &ResolutionSet{rmap}
+
+ // if not yet cached, save the result
+ lg.mu.Lock()
+ if lg.rsTD == nil {
+ lg.rsTD = rs
+ } else {
+ // if we end up with 2, release the later for garbage collection
+ rs = lg.rsTD
+ }
+ lg.mu.Unlock()
+
+ return rs
+}
diff --git a/tools/compliance/policy/resolve_test.go b/tools/compliance/policy/resolve_test.go
new file mode 100644
index 0000000..aa5bb2a
--- /dev/null
+++ b/tools/compliance/policy/resolve_test.go
@@ -0,0 +1,755 @@
+// Copyright 2021 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package compliance
+
+import (
+ "bytes"
+ "testing"
+)
+
+func TestResolveBottomUpConditions(t *testing.T) {
+ tests := []struct {
+ name string
+ roots []string
+ edges []annotated
+ expectedResolutions []res
+ }{
+ {
+ name: "firstparty",
+ roots: []string{"apacheBin.meta_lic"},
+ edges: []annotated{
+ {"apacheBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"apacheBin.meta_lic", "apacheLib.meta_lic", "apacheLib.meta_lic", "notice"},
+ {"apacheLib.meta_lic", "apacheLib.meta_lic", "apacheLib.meta_lic", "notice"},
+ },
+ },
+ {
+ name: "firstpartytool",
+ roots: []string{"apacheBin.meta_lic"},
+ edges: []annotated{
+ {"apacheBin.meta_lic", "apacheLib.meta_lic", []string{"toolchain"}},
+ },
+ expectedResolutions: []res{
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"apacheLib.meta_lic", "apacheLib.meta_lic", "apacheLib.meta_lic", "notice"},
+ },
+ },
+ {
+ name: "firstpartydeep",
+ roots: []string{"apacheContainer.meta_lic"},
+ edges: []annotated{
+ {"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
+ {"apacheBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"apacheContainer.meta_lic", "apacheContainer.meta_lic", "apacheContainer.meta_lic", "notice"},
+ {"apacheContainer.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"apacheContainer.meta_lic", "apacheLib.meta_lic", "apacheLib.meta_lic", "notice"},
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"apacheBin.meta_lic", "apacheLib.meta_lic", "apacheLib.meta_lic", "notice"},
+ {"apacheLib.meta_lic", "apacheLib.meta_lic", "apacheLib.meta_lic", "notice"},
+ },
+ },
+ {
+ name: "firstpartywide",
+ roots: []string{"apacheContainer.meta_lic"},
+ edges: []annotated{
+ {"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
+ {"apacheContainer.meta_lic", "apacheLib.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"apacheContainer.meta_lic", "apacheContainer.meta_lic", "apacheContainer.meta_lic", "notice"},
+ {"apacheContainer.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"apacheContainer.meta_lic", "apacheLib.meta_lic", "apacheLib.meta_lic", "notice"},
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"apacheLib.meta_lic", "apacheLib.meta_lic", "apacheLib.meta_lic", "notice"},
+ },
+ },
+ {
+ name: "firstpartydynamic",
+ roots: []string{"apacheBin.meta_lic"},
+ edges: []annotated{
+ {"apacheBin.meta_lic", "apacheLib.meta_lic", []string{"dynamic"}},
+ },
+ expectedResolutions: []res{
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"apacheLib.meta_lic", "apacheLib.meta_lic", "apacheLib.meta_lic", "notice"},
+ },
+ },
+ {
+ name: "firstpartydynamicdeep",
+ roots: []string{"apacheContainer.meta_lic"},
+ edges: []annotated{
+ {"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
+ {"apacheBin.meta_lic", "apacheLib.meta_lic", []string{"dynamic"}},
+ },
+ expectedResolutions: []res{
+ {"apacheContainer.meta_lic", "apacheContainer.meta_lic", "apacheContainer.meta_lic", "notice"},
+ {"apacheContainer.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"apacheLib.meta_lic", "apacheLib.meta_lic", "apacheLib.meta_lic", "notice"},
+ },
+ },
+ {
+ name: "firstpartydynamicwide",
+ roots: []string{"apacheContainer.meta_lic"},
+ edges: []annotated{
+ {"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
+ {"apacheContainer.meta_lic", "apacheLib.meta_lic", []string{"dynamic"}},
+ },
+ expectedResolutions: []res{
+ {"apacheContainer.meta_lic", "apacheContainer.meta_lic", "apacheContainer.meta_lic", "notice"},
+ {"apacheContainer.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"apacheLib.meta_lic", "apacheLib.meta_lic", "apacheLib.meta_lic", "notice"},
+ },
+ },
+ {
+ name: "restricted",
+ roots: []string{"apacheBin.meta_lic"},
+ edges: []annotated{
+ {"apacheBin.meta_lic", "gplLib.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"apacheBin.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"gplLib.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "restrictedtool",
+ roots: []string{"apacheBin.meta_lic"},
+ edges: []annotated{
+ {"apacheBin.meta_lic", "gplLib.meta_lic", []string{"toolchain"}},
+ },
+ expectedResolutions: []res{
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"gplLib.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "restricteddeep",
+ roots: []string{"apacheContainer.meta_lic"},
+ edges: []annotated{
+ {"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
+ {"apacheBin.meta_lic", "gplLib.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"apacheContainer.meta_lic", "apacheContainer.meta_lic", "apacheContainer.meta_lic", "notice"},
+ {"apacheContainer.meta_lic", "apacheContainer.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"apacheContainer.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"apacheContainer.meta_lic", "apacheBin.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"apacheContainer.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"apacheBin.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"gplLib.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "restrictedwide",
+ roots: []string{"apacheContainer.meta_lic"},
+ edges: []annotated{
+ {"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
+ {"apacheContainer.meta_lic", "gplLib.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"apacheContainer.meta_lic", "apacheContainer.meta_lic", "apacheContainer.meta_lic", "notice"},
+ {"apacheContainer.meta_lic", "apacheContainer.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"apacheContainer.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"apacheContainer.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"gplLib.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "restricteddynamic",
+ roots: []string{"apacheBin.meta_lic"},
+ edges: []annotated{
+ {"apacheBin.meta_lic", "gplLib.meta_lic", []string{"dynamic"}},
+ },
+ expectedResolutions: []res{
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"apacheBin.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"gplLib.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "restricteddynamicdeep",
+ roots: []string{"apacheContainer.meta_lic"},
+ edges: []annotated{
+ {"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
+ {"apacheBin.meta_lic", "gplLib.meta_lic", []string{"dynamic"}},
+ },
+ expectedResolutions: []res{
+ {"apacheContainer.meta_lic", "apacheContainer.meta_lic", "apacheContainer.meta_lic", "notice"},
+ {"apacheContainer.meta_lic", "apacheContainer.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"apacheContainer.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"apacheContainer.meta_lic", "apacheBin.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"apacheContainer.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"apacheBin.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"gplLib.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "restricteddynamicwide",
+ roots: []string{"apacheContainer.meta_lic"},
+ edges: []annotated{
+ {"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
+ {"apacheContainer.meta_lic", "gplLib.meta_lic", []string{"dynamic"}},
+ },
+ expectedResolutions: []res{
+ {"apacheContainer.meta_lic", "apacheContainer.meta_lic", "apacheContainer.meta_lic", "notice"},
+ {"apacheContainer.meta_lic", "apacheContainer.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"apacheContainer.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"apacheContainer.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"gplLib.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "weakrestricted",
+ roots: []string{"apacheBin.meta_lic"},
+ edges: []annotated{
+ {"apacheBin.meta_lic", "lgplLib.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "lgplLib.meta_lic", "restricted"},
+ {"apacheBin.meta_lic", "lgplLib.meta_lic", "lgplLib.meta_lic", "restricted"},
+ {"lgplLib.meta_lic", "lgplLib.meta_lic", "lgplLib.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "weakrestrictedtool",
+ roots: []string{"apacheBin.meta_lic"},
+ edges: []annotated{
+ {"apacheBin.meta_lic", "lgplLib.meta_lic", []string{"toolchain"}},
+ },
+ expectedResolutions: []res{
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"lgplLib.meta_lic", "lgplLib.meta_lic", "lgplLib.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "weakrestricteddeep",
+ roots: []string{"apacheContainer.meta_lic"},
+ edges: []annotated{
+ {"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
+ {"apacheBin.meta_lic", "lgplLib.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"apacheContainer.meta_lic", "apacheContainer.meta_lic", "apacheContainer.meta_lic", "notice"},
+ {"apacheContainer.meta_lic", "apacheContainer.meta_lic", "lgplLib.meta_lic", "restricted"},
+ {"apacheContainer.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"apacheContainer.meta_lic", "apacheBin.meta_lic", "lgplLib.meta_lic", "restricted"},
+ {"apacheContainer.meta_lic", "lgplLib.meta_lic", "lgplLib.meta_lic", "restricted"},
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "lgplLib.meta_lic", "restricted"},
+ {"apacheBin.meta_lic", "lgplLib.meta_lic", "lgplLib.meta_lic", "restricted"},
+ {"lgplLib.meta_lic", "lgplLib.meta_lic", "lgplLib.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "weakrestrictedwide",
+ roots: []string{"apacheContainer.meta_lic"},
+ edges: []annotated{
+ {"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
+ {"apacheContainer.meta_lic", "lgplLib.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"apacheContainer.meta_lic", "apacheContainer.meta_lic", "apacheContainer.meta_lic", "notice"},
+ {"apacheContainer.meta_lic", "apacheContainer.meta_lic", "lgplLib.meta_lic", "restricted"},
+ {"apacheContainer.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"apacheContainer.meta_lic", "lgplLib.meta_lic", "lgplLib.meta_lic", "restricted"},
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"lgplLib.meta_lic", "lgplLib.meta_lic", "lgplLib.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "weakrestricteddynamic",
+ roots: []string{"apacheBin.meta_lic"},
+ edges: []annotated{
+ {"apacheBin.meta_lic", "lgplLib.meta_lic", []string{"dynamic"}},
+ },
+ expectedResolutions: []res{
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"lgplLib.meta_lic", "lgplLib.meta_lic", "lgplLib.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "weakrestricteddynamicdeep",
+ roots: []string{"apacheContainer.meta_lic"},
+ edges: []annotated{
+ {"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
+ {"apacheBin.meta_lic", "lgplLib.meta_lic", []string{"dynamic"}},
+ },
+ expectedResolutions: []res{
+ {"apacheContainer.meta_lic", "apacheContainer.meta_lic", "apacheContainer.meta_lic", "notice"},
+ {"apacheContainer.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"lgplLib.meta_lic", "lgplLib.meta_lic", "lgplLib.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "weakrestricteddynamicwide",
+ roots: []string{"apacheContainer.meta_lic"},
+ edges: []annotated{
+ {"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
+ {"apacheContainer.meta_lic", "lgplLib.meta_lic", []string{"dynamic"}},
+ },
+ expectedResolutions: []res{
+ {"apacheContainer.meta_lic", "apacheContainer.meta_lic", "apacheContainer.meta_lic", "notice"},
+ {"apacheContainer.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"lgplLib.meta_lic", "lgplLib.meta_lic", "lgplLib.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "classpath",
+ roots: []string{"apacheBin.meta_lic"},
+ edges: []annotated{
+ {"apacheBin.meta_lic", "gplWithClasspathException.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
+ {"apacheBin.meta_lic", "gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
+ {"gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "classpathdependent",
+ roots: []string{"dependentModule.meta_lic"},
+ edges: []annotated{
+ {"dependentModule.meta_lic", "gplWithClasspathException.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"dependentModule.meta_lic", "dependentModule.meta_lic", "dependentModule.meta_lic", "notice"},
+ {"dependentModule.meta_lic", "dependentModule.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
+ {"dependentModule.meta_lic", "gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
+ {"gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "classpathdynamic",
+ roots: []string{"apacheBin.meta_lic"},
+ edges: []annotated{
+ {"apacheBin.meta_lic", "gplWithClasspathException.meta_lic", []string{"dynamic"}},
+ },
+ expectedResolutions: []res{
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "classpathdependentdynamic",
+ roots: []string{"dependentModule.meta_lic"},
+ edges: []annotated{
+ {"dependentModule.meta_lic", "gplWithClasspathException.meta_lic", []string{"dynamic"}},
+ },
+ expectedResolutions: []res{
+ {"dependentModule.meta_lic", "dependentModule.meta_lic", "dependentModule.meta_lic", "notice"},
+ {"dependentModule.meta_lic", "dependentModule.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
+ {"dependentModule.meta_lic", "gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
+ {"gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
+ },
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ stderr := &bytes.Buffer{}
+ lg, err := toGraph(stderr, tt.roots, tt.edges)
+ if err != nil {
+ t.Errorf("unexpected test data error: got %w, want no error", err)
+ return
+ }
+ expectedRs := toResolutionSet(lg, tt.expectedResolutions)
+ actualRs := ResolveBottomUpConditions(lg)
+ checkSame(actualRs, expectedRs, t)
+ })
+ }
+}
+
+func TestResolveTopDownConditions(t *testing.T) {
+ tests := []struct {
+ name string
+ roots []string
+ edges []annotated
+ expectedResolutions []res
+ }{
+ {
+ name: "firstparty",
+ roots: []string{"apacheBin.meta_lic"},
+ edges: []annotated{
+ {"apacheBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"apacheBin.meta_lic", "apacheLib.meta_lic", "apacheLib.meta_lic", "notice"},
+ {"apacheLib.meta_lic", "apacheLib.meta_lic", "apacheLib.meta_lic", "notice"},
+ },
+ },
+ {
+ name: "firstpartydynamic",
+ roots: []string{"apacheBin.meta_lic"},
+ edges: []annotated{
+ {"apacheBin.meta_lic", "apacheLib.meta_lic", []string{"dynamic"}},
+ },
+ expectedResolutions: []res{
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"apacheLib.meta_lic", "apacheLib.meta_lic", "apacheLib.meta_lic", "notice"},
+ },
+ },
+ {
+ name: "restricted",
+ roots: []string{"apacheBin.meta_lic"},
+ edges: []annotated{
+ {"apacheBin.meta_lic", "gplLib.meta_lic", []string{"static"}},
+ {"apacheBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"apacheBin.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"apacheBin.meta_lic", "mitLib.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"apacheBin.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
+ {"gplLib.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"mitLib.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
+ },
+ },
+ {
+ name: "restrictedtool",
+ roots: []string{"apacheBin.meta_lic"},
+ edges: []annotated{
+ {"apacheBin.meta_lic", "gplBin.meta_lic", []string{"toolchain"}},
+ {"apacheBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"apacheBin.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
+ {"gplBin.meta_lic", "gplBin.meta_lic", "gplBin.meta_lic", "restricted"},
+ {"mitLib.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
+ },
+ },
+ {
+ name: "restricteddeep",
+ roots: []string{"apacheContainer.meta_lic"},
+ edges: []annotated{
+ {"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
+ {"apacheContainer.meta_lic", "mitBin.meta_lic", []string{"static"}},
+ {"apacheBin.meta_lic", "gplLib.meta_lic", []string{"static"}},
+ {"apacheBin.meta_lic", "mplLib.meta_lic", []string{"static"}},
+ {"mitBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"apacheContainer.meta_lic", "apacheContainer.meta_lic", "apacheContainer.meta_lic", "notice"},
+ {"apacheContainer.meta_lic", "apacheContainer.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"apacheContainer.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"apacheContainer.meta_lic", "apacheBin.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"apacheContainer.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"apacheContainer.meta_lic", "mitBin.meta_lic", "mitBin.meta_lic", "notice"},
+ {"apacheContainer.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
+ {"apacheContainer.meta_lic", "mplLib.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"apacheContainer.meta_lic", "mplLib.meta_lic", "mplLib.meta_lic", "reciprocal"},
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"apacheBin.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"apacheBin.meta_lic", "mplLib.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"apacheBin.meta_lic", "mplLib.meta_lic", "mplLib.meta_lic", "reciprocal"},
+ {"gplLib.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"mitBin.meta_lic", "mitBin.meta_lic", "mitBin.meta_lic", "notice"},
+ {"mitBin.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
+ {"mitLib.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
+ {"mplLib.meta_lic", "mplLib.meta_lic", "mplLib.meta_lic", "reciprocal"},
+ },
+ },
+ {
+ name: "restrictedwide",
+ roots: []string{"apacheContainer.meta_lic"},
+ edges: []annotated{
+ {"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
+ {"apacheContainer.meta_lic", "gplLib.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"apacheContainer.meta_lic", "apacheContainer.meta_lic", "apacheContainer.meta_lic", "notice"},
+ {"apacheContainer.meta_lic", "apacheContainer.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"apacheContainer.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"apacheContainer.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"gplLib.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "restricteddynamic",
+ roots: []string{"apacheBin.meta_lic"},
+ edges: []annotated{
+ {"apacheBin.meta_lic", "gplLib.meta_lic", []string{"dynamic"}},
+ {"apacheBin.meta_lic", "mitLib.meta_lic", []string{"dynamic"}},
+ },
+ expectedResolutions: []res{
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"apacheBin.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"apacheBin.meta_lic", "mitLib.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"gplLib.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"mitLib.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
+ },
+ },
+ {
+ name: "restricteddynamicdeep",
+ roots: []string{"apacheContainer.meta_lic"},
+ edges: []annotated{
+ {"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
+ {"apacheContainer.meta_lic", "mitBin.meta_lic", []string{"static"}},
+ {"apacheBin.meta_lic", "gplLib.meta_lic", []string{"dynamic"}},
+ {"apacheBin.meta_lic", "mplLib.meta_lic", []string{"dynamic"}},
+ {"mitBin.meta_lic", "mitLib.meta_lic", []string{"dynamic"}},
+ },
+ expectedResolutions: []res{
+ {"apacheContainer.meta_lic", "apacheContainer.meta_lic", "apacheContainer.meta_lic", "notice"},
+ {"apacheContainer.meta_lic", "apacheContainer.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"apacheContainer.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"apacheContainer.meta_lic", "apacheBin.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"apacheContainer.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"apacheContainer.meta_lic", "mitBin.meta_lic", "mitBin.meta_lic", "notice"},
+ {"apacheContainer.meta_lic", "mplLib.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"apacheBin.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"apacheBin.meta_lic", "mplLib.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"gplLib.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"mitBin.meta_lic", "mitBin.meta_lic", "mitBin.meta_lic", "notice"},
+ {"mitLib.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
+ {"mplLib.meta_lic", "mplLib.meta_lic", "mplLib.meta_lic", "reciprocal"},
+ },
+ },
+ {
+ name: "restricteddynamicwide",
+ roots: []string{"apacheContainer.meta_lic"},
+ edges: []annotated{
+ {"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
+ {"apacheContainer.meta_lic", "gplLib.meta_lic", []string{"dynamic"}},
+ },
+ expectedResolutions: []res{
+ {"apacheContainer.meta_lic", "apacheContainer.meta_lic", "apacheContainer.meta_lic", "notice"},
+ {"apacheContainer.meta_lic", "apacheContainer.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"apacheContainer.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"apacheContainer.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"gplLib.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "weakrestricted",
+ roots: []string{"apacheBin.meta_lic"},
+ edges: []annotated{
+ {"apacheBin.meta_lic", "lgplLib.meta_lic", []string{"static"}},
+ {"apacheBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "lgplLib.meta_lic", "restricted"},
+ {"apacheBin.meta_lic", "lgplLib.meta_lic", "lgplLib.meta_lic", "restricted"},
+ {"apacheBin.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
+ {"apacheBin.meta_lic", "mitLib.meta_lic", "lgplLib.meta_lic", "restricted"},
+ {"lgplLib.meta_lic", "lgplLib.meta_lic", "lgplLib.meta_lic", "restricted"},
+ {"mitLib.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
+ },
+ },
+ {
+ name: "weakrestrictedtool",
+ roots: []string{"apacheBin.meta_lic"},
+ edges: []annotated{
+ {"apacheBin.meta_lic", "lgplBin.meta_lic", []string{"toolchain"}},
+ {"apacheBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"apacheBin.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
+ {"lgplBin.meta_lic", "lgplBin.meta_lic", "lgplBin.meta_lic", "restricted"},
+ {"mitLib.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
+ },
+ },
+ {
+ name: "weakrestricteddeep",
+ roots: []string{"apacheContainer.meta_lic"},
+ edges: []annotated{
+ {"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
+ {"apacheBin.meta_lic", "lgplLib.meta_lic", []string{"static"}},
+ {"apacheBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"apacheContainer.meta_lic", "apacheContainer.meta_lic", "apacheContainer.meta_lic", "notice"},
+ {"apacheContainer.meta_lic", "apacheContainer.meta_lic", "lgplLib.meta_lic", "restricted"},
+ {"apacheContainer.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"apacheContainer.meta_lic", "apacheBin.meta_lic", "lgplLib.meta_lic", "restricted"},
+ {"apacheContainer.meta_lic", "lgplLib.meta_lic", "lgplLib.meta_lic", "restricted"},
+ {"apacheContainer.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
+ {"apacheContainer.meta_lic", "mitLib.meta_lic", "lgplLib.meta_lic", "restricted"},
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "lgplLib.meta_lic", "restricted"},
+ {"apacheBin.meta_lic", "lgplLib.meta_lic", "lgplLib.meta_lic", "restricted"},
+ {"apacheBin.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
+ {"apacheBin.meta_lic", "mitLib.meta_lic", "lgplLib.meta_lic", "restricted"},
+ {"lgplLib.meta_lic", "lgplLib.meta_lic", "lgplLib.meta_lic", "restricted"},
+ {"mitLib.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
+ },
+ },
+ {
+ name: "weakrestrictedwide",
+ roots: []string{"apacheContainer.meta_lic"},
+ edges: []annotated{
+ {"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
+ {"apacheContainer.meta_lic", "lgplLib.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"apacheContainer.meta_lic", "apacheContainer.meta_lic", "apacheContainer.meta_lic", "notice"},
+ {"apacheContainer.meta_lic", "apacheContainer.meta_lic", "lgplLib.meta_lic", "restricted"},
+ {"apacheContainer.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"apacheContainer.meta_lic", "lgplLib.meta_lic", "lgplLib.meta_lic", "restricted"},
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"lgplLib.meta_lic", "lgplLib.meta_lic", "lgplLib.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "weakrestricteddynamic",
+ roots: []string{"apacheBin.meta_lic"},
+ edges: []annotated{
+ {"apacheBin.meta_lic", "lgplLib.meta_lic", []string{"dynamic"}},
+ {"apacheBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"apacheBin.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
+ {"lgplLib.meta_lic", "lgplLib.meta_lic", "lgplLib.meta_lic", "restricted"},
+ {"mitLib.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
+ },
+ },
+ {
+ name: "weakrestricteddynamicdeep",
+ roots: []string{"apacheContainer.meta_lic"},
+ edges: []annotated{
+ {"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
+ {"apacheBin.meta_lic", "lgplLib.meta_lic", []string{"dynamic"}},
+ },
+ expectedResolutions: []res{
+ {"apacheContainer.meta_lic", "apacheContainer.meta_lic", "apacheContainer.meta_lic", "notice"},
+ {"apacheContainer.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"lgplLib.meta_lic", "lgplLib.meta_lic", "lgplLib.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "weakrestricteddynamicwide",
+ roots: []string{"apacheContainer.meta_lic"},
+ edges: []annotated{
+ {"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
+ {"apacheContainer.meta_lic", "lgplLib.meta_lic", []string{"dynamic"}},
+ },
+ expectedResolutions: []res{
+ {"apacheContainer.meta_lic", "apacheContainer.meta_lic", "apacheContainer.meta_lic", "notice"},
+ {"apacheContainer.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"lgplLib.meta_lic", "lgplLib.meta_lic", "lgplLib.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "classpath",
+ roots: []string{"apacheBin.meta_lic"},
+ edges: []annotated{
+ {"apacheBin.meta_lic", "gplWithClasspathException.meta_lic", []string{"static"}},
+ {"apacheBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
+ {"apacheBin.meta_lic", "gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
+ {"apacheBin.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
+ {"apacheBin.meta_lic", "mitLib.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
+ {"gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
+ {"mitLib.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
+ },
+ },
+ {
+ name: "classpathdependent",
+ roots: []string{"dependentModule.meta_lic"},
+ edges: []annotated{
+ {"dependentModule.meta_lic", "gplWithClasspathException.meta_lic", []string{"static"}},
+ {"dependentModule.meta_lic", "mitLib.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"dependentModule.meta_lic", "dependentModule.meta_lic", "dependentModule.meta_lic", "notice"},
+ {"dependentModule.meta_lic", "dependentModule.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
+ {"dependentModule.meta_lic", "gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
+ {"dependentModule.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
+ {"dependentModule.meta_lic", "mitLib.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
+ {"gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
+ {"mitLib.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
+ },
+ },
+ {
+ name: "classpathdynamic",
+ roots: []string{"apacheBin.meta_lic"},
+ edges: []annotated{
+ {"apacheBin.meta_lic", "gplWithClasspathException.meta_lic", []string{"dynamic"}},
+ {"apacheBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"apacheBin.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
+ {"gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
+ {"mitLib.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
+ },
+ },
+ {
+ name: "classpathdependentdynamic",
+ roots: []string{"dependentModule.meta_lic"},
+ edges: []annotated{
+ {"dependentModule.meta_lic", "gplWithClasspathException.meta_lic", []string{"dynamic"}},
+ {"dependentModule.meta_lic", "mitLib.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"dependentModule.meta_lic", "dependentModule.meta_lic", "dependentModule.meta_lic", "notice"},
+ {"dependentModule.meta_lic", "dependentModule.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
+ {"dependentModule.meta_lic", "gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
+ {"dependentModule.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
+ {"dependentModule.meta_lic", "mitLib.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
+ {"gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
+ {"mitLib.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
+ },
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ stderr := &bytes.Buffer{}
+ lg, err := toGraph(stderr, tt.roots, tt.edges)
+ if err != nil {
+ t.Errorf("unexpected test data error: got %w, want no error", err)
+ return
+ }
+ expectedRs := toResolutionSet(lg, tt.expectedResolutions)
+ actualRs := ResolveTopDownConditions(lg)
+ checkSame(actualRs, expectedRs, t)
+ })
+ }
+}
diff --git a/tools/compliance/policy/resolvenotices.go b/tools/compliance/policy/resolvenotices.go
new file mode 100644
index 0000000..80b5e02
--- /dev/null
+++ b/tools/compliance/policy/resolvenotices.go
@@ -0,0 +1,21 @@
+// Copyright 2021 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package compliance
+
+// ResolveNotices implements the policy for notices.
+func ResolveNotices(lg *LicenseGraph) *ResolutionSet {
+ rs := ResolveTopDownConditions(lg)
+ return WalkResolutionsForCondition(lg, rs, ImpliesNotice)
+}
diff --git a/tools/compliance/policy/resolvenotices_test.go b/tools/compliance/policy/resolvenotices_test.go
new file mode 100644
index 0000000..b428d5b
--- /dev/null
+++ b/tools/compliance/policy/resolvenotices_test.go
@@ -0,0 +1,467 @@
+// Copyright 2021 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package compliance
+
+import (
+ "bytes"
+ "testing"
+)
+
+func TestResolveNotices(t *testing.T) {
+ tests := []struct {
+ name string
+ roots []string
+ edges []annotated
+ expectedResolutions []res
+ }{
+ {
+ name: "firstparty",
+ roots: []string{"apacheBin.meta_lic"},
+ edges: []annotated{
+ {"apacheBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"apacheBin.meta_lic", "apacheLib.meta_lic", "apacheLib.meta_lic", "notice"},
+ },
+ },
+ {
+ name: "firstpartydynamic",
+ roots: []string{"apacheBin.meta_lic"},
+ edges: []annotated{
+ {"apacheBin.meta_lic", "apacheLib.meta_lic", []string{"dynamic"}},
+ },
+ expectedResolutions: []res{
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ },
+ },
+ {
+ name: "firstpartydynamicshipped",
+ roots: []string{"apacheBin.meta_lic", "apacheLib.meta_lic"},
+ edges: []annotated{
+ {"apacheBin.meta_lic", "apacheLib.meta_lic", []string{"dynamic"}},
+ },
+ expectedResolutions: []res{
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"apacheLib.meta_lic", "apacheLib.meta_lic", "apacheLib.meta_lic", "notice"},
+ },
+ },
+ {
+ name: "restricted",
+ roots: []string{"apacheBin.meta_lic"},
+ edges: []annotated{
+ {"apacheBin.meta_lic", "gplLib.meta_lic", []string{"static"}},
+ {"apacheBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"apacheBin.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"apacheBin.meta_lic", "mitLib.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"apacheBin.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
+ },
+ },
+ {
+ name: "restrictedtool",
+ roots: []string{"apacheBin.meta_lic"},
+ edges: []annotated{
+ {"apacheBin.meta_lic", "gplBin.meta_lic", []string{"toolchain"}},
+ {"apacheBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"apacheBin.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
+ },
+ },
+ {
+ name: "restricteddeep",
+ roots: []string{"apacheContainer.meta_lic"},
+ edges: []annotated{
+ {"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
+ {"apacheContainer.meta_lic", "mitBin.meta_lic", []string{"static"}},
+ {"apacheBin.meta_lic", "gplLib.meta_lic", []string{"static"}},
+ {"apacheBin.meta_lic", "mplLib.meta_lic", []string{"static"}},
+ {"mitBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"apacheContainer.meta_lic", "apacheContainer.meta_lic", "apacheContainer.meta_lic", "notice"},
+ {"apacheContainer.meta_lic", "apacheContainer.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"apacheContainer.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"apacheContainer.meta_lic", "apacheBin.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"apacheContainer.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"apacheContainer.meta_lic", "mitBin.meta_lic", "mitBin.meta_lic", "notice"},
+ {"apacheContainer.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
+ {"apacheContainer.meta_lic", "mplLib.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"apacheContainer.meta_lic", "mplLib.meta_lic", "mplLib.meta_lic", "reciprocal"},
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"apacheBin.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"apacheBin.meta_lic", "mplLib.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"apacheBin.meta_lic", "mplLib.meta_lic", "mplLib.meta_lic", "reciprocal"},
+ {"mitBin.meta_lic", "mitBin.meta_lic", "mitBin.meta_lic", "notice"},
+ {"mitBin.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
+ },
+ },
+ {
+ name: "restrictedwide",
+ roots: []string{"apacheContainer.meta_lic"},
+ edges: []annotated{
+ {"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
+ {"apacheContainer.meta_lic", "gplLib.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"apacheContainer.meta_lic", "apacheContainer.meta_lic", "apacheContainer.meta_lic", "notice"},
+ {"apacheContainer.meta_lic", "apacheContainer.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"apacheContainer.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"apacheContainer.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"gplLib.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "restricteddynamic",
+ roots: []string{"apacheBin.meta_lic"},
+ edges: []annotated{
+ {"apacheBin.meta_lic", "gplLib.meta_lic", []string{"dynamic"}},
+ {"apacheBin.meta_lic", "mitLib.meta_lic", []string{"dynamic"}},
+ },
+ expectedResolutions: []res{
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "gplLib.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "restricteddynamicshipped",
+ roots: []string{"apacheBin.meta_lic", "mitLib.meta_lic"},
+ edges: []annotated{
+ {"apacheBin.meta_lic", "gplLib.meta_lic", []string{"dynamic"}},
+ {"apacheBin.meta_lic", "mitLib.meta_lic", []string{"dynamic"}},
+ },
+ expectedResolutions: []res{
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"apacheBin.meta_lic", "mitLib.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"mitLib.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
+ },
+ },
+ {
+ name: "restricteddynamicdeep",
+ roots: []string{"apacheContainer.meta_lic"},
+ edges: []annotated{
+ {"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
+ {"apacheContainer.meta_lic", "mitBin.meta_lic", []string{"static"}},
+ {"apacheBin.meta_lic", "gplLib.meta_lic", []string{"dynamic"}},
+ {"apacheBin.meta_lic", "mplLib.meta_lic", []string{"dynamic"}},
+ {"mitBin.meta_lic", "mitLib.meta_lic", []string{"dynamic"}},
+ },
+ expectedResolutions: []res{
+ {"apacheContainer.meta_lic", "apacheContainer.meta_lic", "apacheContainer.meta_lic", "notice"},
+ {"apacheContainer.meta_lic", "apacheContainer.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"apacheContainer.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"apacheContainer.meta_lic", "apacheBin.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"apacheContainer.meta_lic", "mitBin.meta_lic", "mitBin.meta_lic", "notice"},
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"mitBin.meta_lic", "mitBin.meta_lic", "mitBin.meta_lic", "notice"},
+ },
+ },
+ {
+ name: "restricteddynamicwide",
+ roots: []string{"apacheContainer.meta_lic"},
+ edges: []annotated{
+ {"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
+ {"apacheContainer.meta_lic", "gplLib.meta_lic", []string{"dynamic"}},
+ },
+ expectedResolutions: []res{
+ {"apacheContainer.meta_lic", "apacheContainer.meta_lic", "apacheContainer.meta_lic", "notice"},
+ {"apacheContainer.meta_lic", "apacheContainer.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"apacheContainer.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ },
+ },
+ {
+ name: "restricteddynamicwideshipped",
+ roots: []string{"apacheContainer.meta_lic", "gplLib.meta_lic"},
+ edges: []annotated{
+ {"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
+ {"apacheContainer.meta_lic", "gplLib.meta_lic", []string{"dynamic"}},
+ },
+ expectedResolutions: []res{
+ {"apacheContainer.meta_lic", "apacheContainer.meta_lic", "apacheContainer.meta_lic", "notice"},
+ {"apacheContainer.meta_lic", "apacheContainer.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"apacheContainer.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"apacheContainer.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"gplLib.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "weakrestricted",
+ roots: []string{"apacheBin.meta_lic"},
+ edges: []annotated{
+ {"apacheBin.meta_lic", "lgplLib.meta_lic", []string{"static"}},
+ {"apacheBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "lgplLib.meta_lic", "restricted"},
+ {"apacheBin.meta_lic", "lgplLib.meta_lic", "lgplLib.meta_lic", "restricted"},
+ {"apacheBin.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
+ {"apacheBin.meta_lic", "mitLib.meta_lic", "lgplLib.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "weakrestrictedtool",
+ roots: []string{"apacheBin.meta_lic"},
+ edges: []annotated{
+ {"apacheBin.meta_lic", "lgplBin.meta_lic", []string{"toolchain"}},
+ {"apacheBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"apacheBin.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
+ },
+ },
+ {
+ name: "weakrestrictedtoolshipped",
+ roots: []string{"apacheBin.meta_lic", "lgplBin.meta_lic"},
+ edges: []annotated{
+ {"apacheBin.meta_lic", "lgplBin.meta_lic", []string{"toolchain"}},
+ {"apacheBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"apacheBin.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
+ {"lgplBin.meta_lic", "lgplBin.meta_lic", "lgplBin.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "weakrestricteddeep",
+ roots: []string{"apacheContainer.meta_lic"},
+ edges: []annotated{
+ {"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
+ {"apacheBin.meta_lic", "lgplLib.meta_lic", []string{"static"}},
+ {"apacheBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"apacheContainer.meta_lic", "apacheContainer.meta_lic", "apacheContainer.meta_lic", "notice"},
+ {"apacheContainer.meta_lic", "apacheContainer.meta_lic", "lgplLib.meta_lic", "restricted"},
+ {"apacheContainer.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"apacheContainer.meta_lic", "apacheBin.meta_lic", "lgplLib.meta_lic", "restricted"},
+ {"apacheContainer.meta_lic", "lgplLib.meta_lic", "lgplLib.meta_lic", "restricted"},
+ {"apacheContainer.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
+ {"apacheContainer.meta_lic", "mitLib.meta_lic", "lgplLib.meta_lic", "restricted"},
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "lgplLib.meta_lic", "restricted"},
+ {"apacheBin.meta_lic", "lgplLib.meta_lic", "lgplLib.meta_lic", "restricted"},
+ {"apacheBin.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
+ {"apacheBin.meta_lic", "mitLib.meta_lic", "lgplLib.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "weakrestrictedwide",
+ roots: []string{"apacheContainer.meta_lic"},
+ edges: []annotated{
+ {"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
+ {"apacheContainer.meta_lic", "lgplLib.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"apacheContainer.meta_lic", "apacheContainer.meta_lic", "apacheContainer.meta_lic", "notice"},
+ {"apacheContainer.meta_lic", "apacheContainer.meta_lic", "lgplLib.meta_lic", "restricted"},
+ {"apacheContainer.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"apacheContainer.meta_lic", "lgplLib.meta_lic", "lgplLib.meta_lic", "restricted"},
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"lgplLib.meta_lic", "lgplLib.meta_lic", "lgplLib.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "weakrestricteddynamic",
+ roots: []string{"apacheBin.meta_lic"},
+ edges: []annotated{
+ {"apacheBin.meta_lic", "lgplLib.meta_lic", []string{"dynamic"}},
+ {"apacheBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"apacheBin.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
+ },
+ },
+ {
+ name: "weakrestricteddynamicshipped",
+ roots: []string{"apacheBin.meta_lic", "lgplLib.meta_lic"},
+ edges: []annotated{
+ {"apacheBin.meta_lic", "lgplLib.meta_lic", []string{"dynamic"}},
+ {"apacheBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"apacheBin.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
+ {"lgplLib.meta_lic", "lgplLib.meta_lic", "lgplLib.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "weakrestricteddynamicdeep",
+ roots: []string{"apacheContainer.meta_lic"},
+ edges: []annotated{
+ {"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
+ {"apacheBin.meta_lic", "lgplLib.meta_lic", []string{"dynamic"}},
+ },
+ expectedResolutions: []res{
+ {"apacheContainer.meta_lic", "apacheContainer.meta_lic", "apacheContainer.meta_lic", "notice"},
+ {"apacheContainer.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ },
+ },
+ {
+ name: "weakrestricteddynamicdeepshipped",
+ roots: []string{"apacheContainer.meta_lic", "lgplLib.meta_lic"},
+ edges: []annotated{
+ {"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
+ {"apacheBin.meta_lic", "lgplLib.meta_lic", []string{"dynamic"}},
+ },
+ expectedResolutions: []res{
+ {"apacheContainer.meta_lic", "apacheContainer.meta_lic", "apacheContainer.meta_lic", "notice"},
+ {"apacheContainer.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"lgplLib.meta_lic", "lgplLib.meta_lic", "lgplLib.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "weakrestricteddynamicwide",
+ roots: []string{"apacheContainer.meta_lic"},
+ edges: []annotated{
+ {"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
+ {"apacheContainer.meta_lic", "lgplLib.meta_lic", []string{"dynamic"}},
+ },
+ expectedResolutions: []res{
+ {"apacheContainer.meta_lic", "apacheContainer.meta_lic", "apacheContainer.meta_lic", "notice"},
+ {"apacheContainer.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ },
+ },
+ {
+ name: "weakrestricteddynamicwideshipped",
+ roots: []string{"apacheContainer.meta_lic", "lgplLib.meta_lic"},
+ edges: []annotated{
+ {"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
+ {"apacheContainer.meta_lic", "lgplLib.meta_lic", []string{"dynamic"}},
+ },
+ expectedResolutions: []res{
+ {"apacheContainer.meta_lic", "apacheContainer.meta_lic", "apacheContainer.meta_lic", "notice"},
+ {"apacheContainer.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"lgplLib.meta_lic", "lgplLib.meta_lic", "lgplLib.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "classpath",
+ roots: []string{"apacheBin.meta_lic"},
+ edges: []annotated{
+ {"apacheBin.meta_lic", "gplWithClasspathException.meta_lic", []string{"static"}},
+ {"apacheBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
+ {"apacheBin.meta_lic", "gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
+ {"apacheBin.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
+ {"apacheBin.meta_lic", "mitLib.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "classpathdependent",
+ roots: []string{"dependentModule.meta_lic"},
+ edges: []annotated{
+ {"dependentModule.meta_lic", "gplWithClasspathException.meta_lic", []string{"static"}},
+ {"dependentModule.meta_lic", "mitLib.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"dependentModule.meta_lic", "dependentModule.meta_lic", "dependentModule.meta_lic", "notice"},
+ {"dependentModule.meta_lic", "dependentModule.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
+ {"dependentModule.meta_lic", "gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
+ {"dependentModule.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
+ {"dependentModule.meta_lic", "mitLib.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "classpathdynamic",
+ roots: []string{"apacheBin.meta_lic"},
+ edges: []annotated{
+ {"apacheBin.meta_lic", "gplWithClasspathException.meta_lic", []string{"dynamic"}},
+ {"apacheBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"apacheBin.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
+ },
+ },
+ {
+ name: "classpathdynamicshipped",
+ roots: []string{"apacheBin.meta_lic", "gplWithClasspathException.meta_lic"},
+ edges: []annotated{
+ {"apacheBin.meta_lic", "gplWithClasspathException.meta_lic", []string{"dynamic"}},
+ {"apacheBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"apacheBin.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
+ {"gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "classpathdependentdynamic",
+ roots: []string{"dependentModule.meta_lic"},
+ edges: []annotated{
+ {"dependentModule.meta_lic", "gplWithClasspathException.meta_lic", []string{"dynamic"}},
+ {"dependentModule.meta_lic", "mitLib.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"dependentModule.meta_lic", "dependentModule.meta_lic", "dependentModule.meta_lic", "notice"},
+ {"dependentModule.meta_lic", "dependentModule.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
+ {"dependentModule.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
+ {"dependentModule.meta_lic", "mitLib.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "classpathdependentdynamicshipped",
+ roots: []string{"dependentModule.meta_lic", "gplWithClasspathException.meta_lic"},
+ edges: []annotated{
+ {"dependentModule.meta_lic", "gplWithClasspathException.meta_lic", []string{"dynamic"}},
+ {"dependentModule.meta_lic", "mitLib.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"dependentModule.meta_lic", "dependentModule.meta_lic", "dependentModule.meta_lic", "notice"},
+ {"dependentModule.meta_lic", "dependentModule.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
+ {"dependentModule.meta_lic", "gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
+ {"dependentModule.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
+ {"dependentModule.meta_lic", "mitLib.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
+ {"gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
+ },
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ stderr := &bytes.Buffer{}
+ lg, err := toGraph(stderr, tt.roots, tt.edges)
+ if err != nil {
+ t.Errorf("unexpected test data error: got %w, want no error", err)
+ return
+ }
+ expectedRs := toResolutionSet(lg, tt.expectedResolutions)
+ actualRs := ResolveNotices(lg)
+ checkSame(actualRs, expectedRs, t)
+ })
+ }
+}
diff --git a/tools/compliance/policy/resolveprivacy.go b/tools/compliance/policy/resolveprivacy.go
new file mode 100644
index 0000000..dabbc62
--- /dev/null
+++ b/tools/compliance/policy/resolveprivacy.go
@@ -0,0 +1,21 @@
+// Copyright 2021 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package compliance
+
+// ResolveSourcePrivacy implements the policy for source privacy.
+func ResolveSourcePrivacy(lg *LicenseGraph) *ResolutionSet {
+ rs := ResolveTopDownConditions(lg)
+ return WalkResolutionsForCondition(lg, rs, ImpliesPrivate)
+}
diff --git a/tools/compliance/policy/resolveprivacy_test.go b/tools/compliance/policy/resolveprivacy_test.go
new file mode 100644
index 0000000..25772bb
--- /dev/null
+++ b/tools/compliance/policy/resolveprivacy_test.go
@@ -0,0 +1,87 @@
+// Copyright 2021 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package compliance
+
+import (
+ "bytes"
+ "testing"
+)
+
+func TestResolveSourcePrivacy(t *testing.T) {
+ tests := []struct {
+ name string
+ roots []string
+ edges []annotated
+ expectedResolutions []res
+ }{
+ {
+ name: "firstparty",
+ roots: []string{"apacheBin.meta_lic"},
+ edges: []annotated{
+ {"apacheBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{},
+ },
+ {
+ name: "notice",
+ roots: []string{"mitBin.meta_lic"},
+ edges: []annotated{
+ {"mitBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{},
+ },
+ {
+ name: "lgpl",
+ roots: []string{"lgplBin.meta_lic"},
+ edges: []annotated{
+ {"lgplBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{},
+ },
+ {
+ name: "proprietaryonresricted",
+ roots: []string{"proprietary.meta_lic"},
+ edges: []annotated{
+ {"proprietary.meta_lic", "gplLib.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"proprietary.meta_lic", "proprietary.meta_lic", "proprietary.meta_lic", "proprietary"},
+ },
+ },
+ {
+ name: "restrictedonproprietary",
+ roots: []string{"gplBin.meta_lic"},
+ edges: []annotated{
+ {"gplBin.meta_lic", "proprietary.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"gplBin.meta_lic", "proprietary.meta_lic", "proprietary.meta_lic", "proprietary"},
+ },
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ stderr := &bytes.Buffer{}
+ lg, err := toGraph(stderr, tt.roots, tt.edges)
+ if err != nil {
+ t.Errorf("unexpected test data error: got %w, want no error", err)
+ return
+ }
+ expectedRs := toResolutionSet(lg, tt.expectedResolutions)
+ actualRs := ResolveSourcePrivacy(lg)
+ checkSame(actualRs, expectedRs, t)
+ })
+ }
+}
diff --git a/tools/compliance/policy/resolveshare.go b/tools/compliance/policy/resolveshare.go
new file mode 100644
index 0000000..24efd28
--- /dev/null
+++ b/tools/compliance/policy/resolveshare.go
@@ -0,0 +1,21 @@
+// Copyright 2021 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package compliance
+
+// ResolveSourceSharing implements the policy for source-sharing.
+func ResolveSourceSharing(lg *LicenseGraph) *ResolutionSet {
+ rs := ResolveTopDownConditions(lg)
+ return WalkResolutionsForCondition(lg, rs, ImpliesShared)
+}
diff --git a/tools/compliance/policy/resolveshare_test.go b/tools/compliance/policy/resolveshare_test.go
new file mode 100644
index 0000000..7371ccf
--- /dev/null
+++ b/tools/compliance/policy/resolveshare_test.go
@@ -0,0 +1,295 @@
+// Copyright 2021 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package compliance
+
+import (
+ "bytes"
+ "testing"
+)
+
+func TestResolveSourceSharing(t *testing.T) {
+ tests := []struct {
+ name string
+ roots []string
+ edges []annotated
+ expectedResolutions []res
+ }{
+ {
+ name: "independentmodulerestricted",
+ roots: []string{"apacheBin.meta_lic"},
+ edges: []annotated{
+ {"apacheBin.meta_lic", "gplWithClasspathException.meta_lic", []string{"dynamic"}},
+ },
+ expectedResolutions: []res{},
+ },
+ {
+ name: "independentmodulerestrictedshipped",
+ roots: []string{"apacheBin.meta_lic", "gplWithClasspathException.meta_lic"},
+ edges: []annotated{
+ {"apacheBin.meta_lic", "gplWithClasspathException.meta_lic", []string{"dynamic"}},
+ },
+ expectedResolutions: []res{
+ {"gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "independentmodulestaticrestricted",
+ roots: []string{"apacheBin.meta_lic"},
+ edges: []annotated{
+ {"apacheBin.meta_lic", "gplWithClasspathException.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
+ {"apacheBin.meta_lic", "gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "dependentmodulerestricted",
+ roots: []string{"dependentModule.meta_lic"},
+ edges: []annotated{
+ {"dependentModule.meta_lic", "gplWithClasspathException.meta_lic", []string{"dynamic"}},
+ },
+ expectedResolutions: []res{
+ {"dependentModule.meta_lic", "dependentModule.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "dependentmodulerestrictedshipclasspath",
+ roots: []string{"dependentModule.meta_lic", "gplWithClasspathException.meta_lic"},
+ edges: []annotated{
+ {"dependentModule.meta_lic", "gplWithClasspathException.meta_lic", []string{"dynamic"}},
+ },
+ expectedResolutions: []res{
+ {"dependentModule.meta_lic", "dependentModule.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
+ {"dependentModule.meta_lic", "gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
+ {"gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "lgplonfprestricted",
+ roots: []string{"lgplBin.meta_lic"},
+ edges: []annotated{
+ {"lgplBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"lgplBin.meta_lic", "lgplBin.meta_lic", "lgplBin.meta_lic", "restricted"},
+ {"lgplBin.meta_lic", "apacheLib.meta_lic", "lgplBin.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "lgplonfpdynamicrestricted",
+ roots: []string{"lgplBin.meta_lic"},
+ edges: []annotated{
+ {"lgplBin.meta_lic", "apacheLib.meta_lic", []string{"dynamic"}},
+ },
+ expectedResolutions: []res{
+ {"lgplBin.meta_lic", "lgplBin.meta_lic", "lgplBin.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "lgplonfpdynamicrestrictedshiplib",
+ roots: []string{"lgplBin.meta_lic", "apacheLib.meta_lic"},
+ edges: []annotated{
+ {"lgplBin.meta_lic", "apacheLib.meta_lic", []string{"dynamic"}},
+ },
+ expectedResolutions: []res{
+ {"lgplBin.meta_lic", "lgplBin.meta_lic", "lgplBin.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "gplonfprestricted",
+ roots: []string{"gplBin.meta_lic"},
+ edges: []annotated{
+ {"gplBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"gplBin.meta_lic", "gplBin.meta_lic", "gplBin.meta_lic", "restricted"},
+ {"gplBin.meta_lic", "apacheLib.meta_lic", "gplBin.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "gplcontainerrestricted",
+ roots: []string{"gplContainer.meta_lic"},
+ edges: []annotated{
+ {"gplContainer.meta_lic", "apacheLib.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"gplContainer.meta_lic", "gplContainer.meta_lic", "gplContainer.meta_lic", "restricted"},
+ {"gplContainer.meta_lic", "apacheLib.meta_lic", "gplContainer.meta_lic", "restricted"},
+ {"apacheLib.meta_lic", "apacheLib.meta_lic", "gplContainer.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "gploncontainerrestricted",
+ roots: []string{"apacheContainer.meta_lic"},
+ edges: []annotated{
+ {"apacheContainer.meta_lic", "apacheLib.meta_lic", []string{"static"}},
+ {"apacheContainer.meta_lic", "gplLib.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"apacheContainer.meta_lic", "apacheContainer.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"apacheContainer.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"gplLib.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "gplonbinrestricted",
+ roots: []string{"apacheBin.meta_lic"},
+ edges: []annotated{
+ {"apacheBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
+ {"apacheBin.meta_lic", "gplLib.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"apacheBin.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"apacheBin.meta_lic", "apacheLib.meta_lic", "gplLib.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "gplonfpdynamicrestricted",
+ roots: []string{"gplBin.meta_lic"},
+ edges: []annotated{
+ {"gplBin.meta_lic", "apacheLib.meta_lic", []string{"dynamic"}},
+ },
+ expectedResolutions: []res{
+ {"gplBin.meta_lic", "gplBin.meta_lic", "gplBin.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "gplonfpdynamicrestrictedshiplib",
+ roots: []string{"gplBin.meta_lic", "apacheLib.meta_lic"},
+ edges: []annotated{
+ {"gplBin.meta_lic", "apacheLib.meta_lic", []string{"dynamic"}},
+ },
+ expectedResolutions: []res{
+ {"gplBin.meta_lic", "gplBin.meta_lic", "gplBin.meta_lic", "restricted"},
+ {"gplBin.meta_lic", "apacheLib.meta_lic", "gplBin.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "independentmodulereverserestricted",
+ roots: []string{"gplWithClasspathException.meta_lic"},
+ edges: []annotated{
+ {"gplWithClasspathException.meta_lic", "apacheBin.meta_lic", []string{"dynamic"}},
+ },
+ expectedResolutions: []res{
+ {"gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "independentmodulereversestaticrestricted",
+ roots: []string{"gplWithClasspathException.meta_lic"},
+ edges: []annotated{
+ {"gplWithClasspathException.meta_lic", "apacheBin.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
+ {"gplWithClasspathException.meta_lic", "apacheBin.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "dependentmodulereverserestricted",
+ roots: []string{"gplWithClasspathException.meta_lic"},
+ edges: []annotated{
+ {"gplWithClasspathException.meta_lic", "dependentModule.meta_lic", []string{"dynamic"}},
+ },
+ expectedResolutions: []res{
+ {"gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "dependentmodulereverserestrictedshipdependent",
+ roots: []string{"gplWithClasspathException.meta_lic", "dependentModule.meta_lic"},
+ edges: []annotated{
+ {"gplWithClasspathException.meta_lic", "dependentModule.meta_lic", []string{"dynamic"}},
+ },
+ expectedResolutions: []res{
+ {"gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
+ {"gplWithClasspathException.meta_lic", "dependentModule.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "ponrrestricted",
+ roots: []string{"proprietary.meta_lic"},
+ edges: []annotated{
+ {"proprietary.meta_lic", "gplLib.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"proprietary.meta_lic", "proprietary.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"proprietary.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "ronprestricted",
+ roots: []string{"gplBin.meta_lic"},
+ edges: []annotated{
+ {"gplBin.meta_lic", "proprietary.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"gplBin.meta_lic", "gplBin.meta_lic", "gplBin.meta_lic", "restricted"},
+ {"gplBin.meta_lic", "proprietary.meta_lic", "gplBin.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "noticeonb_e_orestricted",
+ roots: []string{"mitBin.meta_lic"},
+ edges: []annotated{
+ {"mitBin.meta_lic", "by_exception.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{},
+ },
+ {
+ name: "b_e_oonnoticerestricted",
+ roots: []string{"by_exception.meta_lic"},
+ edges: []annotated{
+ {"by_exception.meta_lic", "mitLib.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{},
+ },
+ {
+ name: "noticeonreciprecip",
+ roots: []string{"mitBin.meta_lic"},
+ edges: []annotated{
+ {"mitBin.meta_lic", "mplLib.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"mitBin.meta_lic", "mplLib.meta_lic", "mplLib.meta_lic", "reciprocal"},
+ },
+ },
+ {
+ name: "reciponnoticerecip",
+ roots: []string{"mplBin.meta_lic"},
+ edges: []annotated{
+ {"mplBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"mplBin.meta_lic", "mplBin.meta_lic", "mplBin.meta_lic", "reciprocal"},
+ },
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ stderr := &bytes.Buffer{}
+ lg, err := toGraph(stderr, tt.roots, tt.edges)
+ if err != nil {
+ t.Errorf("unexpected test data error: got %w, want no error", err)
+ return
+ }
+ expectedRs := toResolutionSet(lg, tt.expectedResolutions)
+ actualRs := ResolveSourceSharing(lg)
+ checkSame(actualRs, expectedRs, t)
+ })
+ }
+}
diff --git a/tools/compliance/policy/shareprivacyconflicts.go b/tools/compliance/policy/shareprivacyconflicts.go
new file mode 100644
index 0000000..dabdff5
--- /dev/null
+++ b/tools/compliance/policy/shareprivacyconflicts.go
@@ -0,0 +1,91 @@
+// Copyright 2021 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package compliance
+
+import (
+ "fmt"
+)
+
+// SourceSharePrivacyConflict describes an individual conflict between a source-sharing
+// condition and a source privacy condition
+type SourceSharePrivacyConflict struct {
+ SourceNode *TargetNode
+ ShareCondition LicenseCondition
+ PrivacyCondition LicenseCondition
+}
+
+// Error returns a string describing the conflict.
+func (conflict SourceSharePrivacyConflict) Error() string {
+ return fmt.Sprintf("%s %s from %s and must share from %s %s\n",
+ conflict.SourceNode.name,
+ conflict.PrivacyCondition.name, conflict.PrivacyCondition.origin.name,
+ conflict.ShareCondition.name, conflict.ShareCondition.origin.name)
+}
+
+// IsEqualTo returns true when `conflict` and `other` describe the same conflict.
+func (conflict SourceSharePrivacyConflict) IsEqualTo(other SourceSharePrivacyConflict) bool {
+ return conflict.SourceNode.name == other.SourceNode.name &&
+ conflict.ShareCondition.name == other.ShareCondition.name &&
+ conflict.ShareCondition.origin.name == other.ShareCondition.origin.name &&
+ conflict.PrivacyCondition.name == other.PrivacyCondition.name &&
+ conflict.PrivacyCondition.origin.name == other.PrivacyCondition.origin.name
+}
+
+// ConflictingSharedPrivateSource lists all of the targets where conflicting conditions to
+// share the source and to keep the source private apply to the target.
+func ConflictingSharedPrivateSource(lg *LicenseGraph) []SourceSharePrivacyConflict {
+ // shareSource is the set of all source-sharing resolutions.
+ shareSource := ResolveSourceSharing(lg)
+ if shareSource.IsEmpty() {
+ return []SourceSharePrivacyConflict{}
+ }
+
+ // privateSource is the set of all source privacy resolutions.
+ privateSource := ResolveSourcePrivacy(lg)
+ if privateSource.IsEmpty() {
+ return []SourceSharePrivacyConflict{}
+ }
+
+ // combined is the combination of source-sharing and source privacy.
+ combined := JoinResolutionSets(shareSource, privateSource)
+
+ // size is the size of the result
+ size := 0
+ for _, actsOn := range combined.ActsOn() {
+ rl := combined.ResolutionsByActsOn(actsOn)
+ size += rl.CountConditionsByName(ImpliesShared) * rl.CountConditionsByName(ImpliesPrivate)
+ }
+ if size == 0 {
+ return []SourceSharePrivacyConflict{}
+ }
+ result := make([]SourceSharePrivacyConflict, 0, size)
+ for _, actsOn := range combined.ActsOn() {
+ rl := combined.ResolutionsByActsOn(actsOn)
+ if len(rl) == 0 {
+ continue
+ }
+
+ pconditions := rl.ByName(ImpliesPrivate).AllConditions().AsList()
+ ssconditions := rl.ByName(ImpliesShared).AllConditions().AsList()
+
+ // report all conflicting condition combinations
+ for _, p := range pconditions {
+ for _, ss := range ssconditions {
+ result = append(result, SourceSharePrivacyConflict{actsOn, ss, p})
+ }
+ }
+ }
+ return result
+}
diff --git a/tools/compliance/policy/shareprivacyconflicts_test.go b/tools/compliance/policy/shareprivacyconflicts_test.go
new file mode 100644
index 0000000..162c1fe
--- /dev/null
+++ b/tools/compliance/policy/shareprivacyconflicts_test.go
@@ -0,0 +1,129 @@
+// Copyright 2021 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package compliance
+
+import (
+ "bytes"
+ "sort"
+ "testing"
+)
+
+// byConflict orders conflicts by target then share then privacy
+type byConflict []SourceSharePrivacyConflict
+
+// Len returns the count of elements in the slice.
+func (l byConflict) Len() int { return len(l) }
+
+// Swap rearranged 2 elements so that each occupies the other's former
+// position.
+func (l byConflict) Swap(i, j int) { l[i], l[j] = l[j], l[i] }
+
+// Less returns true when the `i`th element is lexicographically less than
+// the `j`th element.
+func (l byConflict) Less(i, j int) bool {
+ if l[i].SourceNode.name == l[j].SourceNode.name {
+ if l[i].ShareCondition.origin.name == l[j].ShareCondition.origin.name {
+ if l[i].ShareCondition.name == l[j].ShareCondition.name {
+ if l[i].PrivacyCondition.origin.name == l[j].PrivacyCondition.origin.name {
+ return l[i].PrivacyCondition.name < l[j].PrivacyCondition.name
+ }
+ return l[i].PrivacyCondition.origin.name < l[j].PrivacyCondition.origin.name
+ }
+ return l[i].ShareCondition.name < l[j].ShareCondition.name
+ }
+ return l[i].ShareCondition.origin.name < l[j].ShareCondition.origin.name
+ }
+ return l[i].SourceNode.name < l[j].SourceNode.name
+}
+
+func TestConflictingSharedPrivateSource(t *testing.T) {
+ tests := []struct {
+ name string
+ roots []string
+ edges []annotated
+ expectedConflicts []confl
+ }{
+ {
+ name: "firstparty",
+ roots: []string{"apacheBin.meta_lic"},
+ edges: []annotated{
+ {"apacheBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
+ },
+ expectedConflicts: []confl{},
+ },
+ {
+ name: "notice",
+ roots: []string{"mitBin.meta_lic"},
+ edges: []annotated{
+ {"mitBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
+ },
+ expectedConflicts: []confl{},
+ },
+ {
+ name: "lgpl",
+ roots: []string{"lgplBin.meta_lic"},
+ edges: []annotated{
+ {"lgplBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
+ },
+ expectedConflicts: []confl{},
+ },
+ {
+ name: "proprietaryonrestricted",
+ roots: []string{"proprietary.meta_lic"},
+ edges: []annotated{
+ {"proprietary.meta_lic", "gplLib.meta_lic", []string{"static"}},
+ },
+ expectedConflicts: []confl{
+ {"proprietary.meta_lic", "gplLib.meta_lic:restricted", "proprietary.meta_lic:proprietary"},
+ },
+ },
+ {
+ name: "restrictedonproprietary",
+ roots: []string{"gplBin.meta_lic"},
+ edges: []annotated{
+ {"gplBin.meta_lic", "proprietary.meta_lic", []string{"static"}},
+ },
+ expectedConflicts: []confl{
+ {"proprietary.meta_lic", "gplBin.meta_lic:restricted", "proprietary.meta_lic:proprietary"},
+ },
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ stderr := &bytes.Buffer{}
+ lg, err := toGraph(stderr, tt.roots, tt.edges)
+ if err != nil {
+ t.Errorf("unexpected test data error: got %w, want no error", err)
+ return
+ }
+ expectedConflicts := toConflictList(lg, tt.expectedConflicts)
+ actualConflicts := ConflictingSharedPrivateSource(lg)
+ sort.Sort(byConflict(expectedConflicts))
+ sort.Sort(byConflict(actualConflicts))
+ if len(expectedConflicts) != len(actualConflicts) {
+ t.Errorf("unexpected number of share/privacy conflicts: got %v with %d conflicts, want %v with %d conflicts",
+ actualConflicts, len(actualConflicts), expectedConflicts, len(expectedConflicts))
+ } else {
+ for i := 0; i < len(actualConflicts); i++ {
+ if !actualConflicts[i].IsEqualTo(expectedConflicts[i]) {
+ t.Errorf("unexpected share/privacy conflict at element %d: got %q, want %q",
+ i, actualConflicts[i].Error(), expectedConflicts[i].Error())
+ }
+ }
+ }
+
+ })
+ }
+}
diff --git a/tools/compliance/policy/shipped.go b/tools/compliance/policy/shipped.go
new file mode 100644
index 0000000..74eb343
--- /dev/null
+++ b/tools/compliance/policy/shipped.go
@@ -0,0 +1,54 @@
+// Copyright 2021 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package compliance
+
+// ShippedNodes returns the set of nodes in a license graph where the target or
+// a derivative work gets distributed. (caches result)
+func ShippedNodes(lg *LicenseGraph) *TargetNodeSet {
+ lg.mu.Lock()
+ shipped := lg.shippedNodes
+ lg.mu.Unlock()
+ if shipped != nil {
+ return shipped
+ }
+
+ tset := make(map[*TargetNode]bool)
+
+ WalkTopDown(lg, func(lg *LicenseGraph, tn *TargetNode, path TargetEdgePath) bool {
+ if _, alreadyWalked := tset[tn]; alreadyWalked {
+ return false
+ }
+ if len(path) > 0 {
+ if !edgeIsDerivation(path[len(path)-1]) {
+ return false
+ }
+ }
+ tset[tn] = true
+ return true
+ })
+
+ shipped = &TargetNodeSet{tset}
+
+ lg.mu.Lock()
+ if lg.shippedNodes == nil {
+ lg.shippedNodes = shipped
+ } else {
+ // if we end up with 2, release the later for garbage collection.
+ shipped = lg.shippedNodes
+ }
+ lg.mu.Unlock()
+
+ return shipped
+}
diff --git a/tools/compliance/policy/shipped_test.go b/tools/compliance/policy/shipped_test.go
new file mode 100644
index 0000000..53a8469
--- /dev/null
+++ b/tools/compliance/policy/shipped_test.go
@@ -0,0 +1,130 @@
+// Copyright 2021 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package compliance
+
+import (
+ "bytes"
+ "sort"
+ "testing"
+)
+
+func TestShippedNodes(t *testing.T) {
+ tests := []struct {
+ name string
+ roots []string
+ edges []annotated
+ expectedNodes []string
+ }{
+ {
+ name: "singleton",
+ roots: []string{"apacheLib.meta_lic"},
+ edges: []annotated{},
+ expectedNodes: []string{"apacheLib.meta_lic"},
+ },
+ {
+ name: "simplebinary",
+ roots: []string{"apacheBin.meta_lic"},
+ edges: []annotated{
+ {"apacheBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
+ },
+ expectedNodes: []string{"apacheBin.meta_lic", "apacheLib.meta_lic"},
+ },
+ {
+ name: "simpledynamic",
+ roots: []string{"apacheBin.meta_lic"},
+ edges: []annotated{
+ {"apacheBin.meta_lic", "lgplLib.meta_lic", []string{"dynamic"}},
+ },
+ expectedNodes: []string{"apacheBin.meta_lic"},
+ },
+ {
+ name: "container",
+ roots: []string{"apacheContainer.meta_lic"},
+ edges: []annotated{
+ {"apacheContainer.meta_lic", "apacheLib.meta_lic", []string{"static"}},
+ {"apacheContainer.meta_lic", "gplLib.meta_lic", []string{"static"}},
+ },
+ expectedNodes: []string{
+ "apacheContainer.meta_lic",
+ "apacheLib.meta_lic",
+ "gplLib.meta_lic",
+ },
+ },
+ {
+ name: "binary",
+ roots: []string{"apacheBin.meta_lic"},
+ edges: []annotated{
+ {"apacheBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
+ {"apacheBin.meta_lic", "gplLib.meta_lic", []string{"static"}},
+ },
+ expectedNodes: []string{
+ "apacheBin.meta_lic",
+ "apacheLib.meta_lic",
+ "gplLib.meta_lic",
+ },
+ },
+ {
+ name: "binarydynamic",
+ roots: []string{"apacheBin.meta_lic"},
+ edges: []annotated{
+ {"apacheBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
+ {"apacheBin.meta_lic", "gplLib.meta_lic", []string{"dynamic"}},
+ },
+ expectedNodes: []string{
+ "apacheBin.meta_lic",
+ "apacheLib.meta_lic",
+ },
+ },
+ {
+ name: "containerdeep",
+ roots: []string{"apacheContainer.meta_lic"},
+ edges: []annotated{
+ {"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
+ {"apacheBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
+ {"apacheLib.meta_lic", "gplLib.meta_lic", []string{"dynamic"}},
+ },
+ expectedNodes: []string{
+ "apacheContainer.meta_lic",
+ "apacheBin.meta_lic",
+ "apacheLib.meta_lic",
+ },
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ stderr := &bytes.Buffer{}
+ lg, err := toGraph(stderr, tt.roots, tt.edges)
+ if err != nil {
+ t.Errorf("unexpected test data error: got %w, want no error", err)
+ return
+ }
+ expectedNodes := append([]string{}, tt.expectedNodes...)
+ actualNodes := ShippedNodes(lg).Names()
+ sort.Strings(expectedNodes)
+ sort.Strings(actualNodes)
+ if len(expectedNodes) != len(actualNodes) {
+ t.Errorf("unexpected number of shipped nodes: got %v with %d nodes, want %v with %d nodes",
+ actualNodes, len(actualNodes), expectedNodes, len(expectedNodes))
+ return
+ }
+ for i := 0; i < len(actualNodes); i++ {
+ if expectedNodes[i] != actualNodes[i] {
+ t.Errorf("unexpected node at index %d: got %q in %v, want %q in %v",
+ i, actualNodes[i], actualNodes, expectedNodes[i], expectedNodes)
+ }
+ }
+ })
+ }
+}
diff --git a/tools/compliance/policy/walk.go b/tools/compliance/policy/walk.go
new file mode 100644
index 0000000..8b6737d
--- /dev/null
+++ b/tools/compliance/policy/walk.go
@@ -0,0 +1,76 @@
+// Copyright 2021 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package compliance
+
+// VisitNode is called for each root and for each walked dependency node by
+// WalkTopDown. When VisitNode returns true, WalkTopDown will proceed to walk
+// down the dependences of the node
+type VisitNode func(*LicenseGraph, *TargetNode, TargetEdgePath) bool
+
+// WalkTopDown does a top-down walk of `lg` calling `visit` and descending
+// into depenencies when `visit` returns true.
+func WalkTopDown(lg *LicenseGraph, visit VisitNode) {
+ path := NewTargetEdgePath(32)
+
+ // must be indexed for fast lookup
+ lg.indexForward()
+
+ var walk func(f string)
+ walk = func(f string) {
+ visitChildren := visit(lg, lg.targets[f], *path)
+ if !visitChildren {
+ return
+ }
+ for _, edge := range lg.index[f] {
+ path.Push(TargetEdge{lg, edge})
+ walk(edge.dependency)
+ path.Pop()
+ }
+ }
+
+ for _, r := range lg.rootFiles {
+ path.Clear()
+ walk(r)
+ }
+}
+
+// WalkResolutionsForCondition performs a top-down walk of the LicenseGraph
+// resolving all distributed works for condition `names`.
+func WalkResolutionsForCondition(lg *LicenseGraph, rs *ResolutionSet, names ConditionNames) *ResolutionSet {
+ shipped := ShippedNodes(lg)
+
+ // rmap maps 'attachesTo' targets to the `actsOn` targets and applicable conditions
+ //
+ // rmap is the resulting ResolutionSet
+ rmap := make(map[*TargetNode]actionSet)
+
+ WalkTopDown(lg, func(lg *LicenseGraph, tn *TargetNode, _ TargetEdgePath) bool {
+ if _, ok := rmap[tn]; ok {
+ return false
+ }
+ if !shipped.Contains(tn) {
+ return false
+ }
+ if as, ok := rs.resolutions[tn]; ok {
+ fas := as.byActsOn(shipped).byName(names)
+ if !fas.isEmpty() {
+ rmap[tn] = fas
+ }
+ }
+ return tn.IsContainer() // descend into containers
+ })
+
+ return &ResolutionSet{rmap}
+}
diff --git a/tools/compliance/policy/walk_test.go b/tools/compliance/policy/walk_test.go
new file mode 100644
index 0000000..2eef702
--- /dev/null
+++ b/tools/compliance/policy/walk_test.go
@@ -0,0 +1,629 @@
+// Copyright 2021 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package compliance
+
+import (
+ "bytes"
+ "testing"
+)
+
+func TestWalkResolutionsForCondition(t *testing.T) {
+ tests := []struct {
+ name string
+ condition ConditionNames
+ roots []string
+ edges []annotated
+ expectedResolutions []res
+ }{
+ {
+ name: "firstparty",
+ condition: ImpliesNotice,
+ roots: []string{"apacheBin.meta_lic"},
+ edges: []annotated{
+ {"apacheBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"apacheBin.meta_lic", "apacheLib.meta_lic", "apacheLib.meta_lic", "notice"},
+ },
+ },
+ {
+ name: "notice",
+ condition: ImpliesNotice,
+ roots: []string{"mitBin.meta_lic"},
+ edges: []annotated{
+ {"mitBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"mitBin.meta_lic", "mitBin.meta_lic", "mitBin.meta_lic", "notice"},
+ {"mitBin.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
+ },
+ },
+ {
+ name: "fponlgplnotice",
+ condition: ImpliesNotice,
+ roots: []string{"apacheBin.meta_lic"},
+ edges: []annotated{
+ {"apacheBin.meta_lic", "lgplLib.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "lgplLib.meta_lic", "restricted"},
+ {"apacheBin.meta_lic", "lgplLib.meta_lic", "lgplLib.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "fponlgpldynamicnotice",
+ condition: ImpliesNotice,
+ roots: []string{"apacheBin.meta_lic"},
+ edges: []annotated{
+ {"apacheBin.meta_lic", "lgplLib.meta_lic", []string{"dynamic"}},
+ },
+ expectedResolutions: []res{
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ },
+ },
+ {
+ name: "independentmodulenotice",
+ condition: ImpliesNotice,
+ roots: []string{"apacheBin.meta_lic"},
+ edges: []annotated{
+ {"apacheBin.meta_lic", "gplWithClasspathException.meta_lic", []string{"dynamic"}},
+ },
+ expectedResolutions: []res{
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ },
+ },
+ {
+ name: "independentmodulerestricted",
+ condition: ImpliesRestricted,
+ roots: []string{"apacheBin.meta_lic"},
+ edges: []annotated{
+ {"apacheBin.meta_lic", "gplWithClasspathException.meta_lic", []string{"dynamic"}},
+ },
+ expectedResolutions: []res{},
+ },
+ {
+ name: "independentmodulestaticnotice",
+ condition: ImpliesNotice,
+ roots: []string{"apacheBin.meta_lic"},
+ edges: []annotated{
+ {"apacheBin.meta_lic", "gplWithClasspathException.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
+ {"apacheBin.meta_lic", "gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "independentmodulestaticrestricted",
+ condition: ImpliesRestricted,
+ roots: []string{"apacheBin.meta_lic"},
+ edges: []annotated{
+ {"apacheBin.meta_lic", "gplWithClasspathException.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
+ {"apacheBin.meta_lic", "gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "dependentmodulenotice",
+ condition: ImpliesNotice,
+ roots: []string{"dependentModule.meta_lic"},
+ edges: []annotated{
+ {"dependentModule.meta_lic", "gplWithClasspathException.meta_lic", []string{"dynamic"}},
+ },
+ expectedResolutions: []res{
+ {"dependentModule.meta_lic", "dependentModule.meta_lic", "dependentModule.meta_lic", "notice"},
+ {"dependentModule.meta_lic", "dependentModule.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "dependentmodulerestricted",
+ condition: ImpliesRestricted,
+ roots: []string{"dependentModule.meta_lic"},
+ edges: []annotated{
+ {"dependentModule.meta_lic", "gplWithClasspathException.meta_lic", []string{"dynamic"}},
+ },
+ expectedResolutions: []res{
+ {"dependentModule.meta_lic", "dependentModule.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "lgplonfpnotice",
+ condition: ImpliesNotice,
+ roots: []string{"lgplBin.meta_lic"},
+ edges: []annotated{
+ {"lgplBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"lgplBin.meta_lic", "lgplBin.meta_lic", "lgplBin.meta_lic", "restricted"},
+ {"lgplBin.meta_lic", "apacheLib.meta_lic", "apacheLib.meta_lic", "notice"},
+ {"lgplBin.meta_lic", "apacheLib.meta_lic", "lgplBin.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "lgplonfprestricted",
+ condition: ImpliesRestricted,
+ roots: []string{"lgplBin.meta_lic"},
+ edges: []annotated{
+ {"lgplBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"lgplBin.meta_lic", "lgplBin.meta_lic", "lgplBin.meta_lic", "restricted"},
+ {"lgplBin.meta_lic", "apacheLib.meta_lic", "lgplBin.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "lgplonfpdynamicnotice",
+ condition: ImpliesNotice,
+ roots: []string{"lgplBin.meta_lic"},
+ edges: []annotated{
+ {"lgplBin.meta_lic", "apacheLib.meta_lic", []string{"dynamic"}},
+ },
+ expectedResolutions: []res{
+ {"lgplBin.meta_lic", "lgplBin.meta_lic", "lgplBin.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "lgplonfpdynamicrestricted",
+ condition: ImpliesRestricted,
+ roots: []string{"lgplBin.meta_lic"},
+ edges: []annotated{
+ {"lgplBin.meta_lic", "apacheLib.meta_lic", []string{"dynamic"}},
+ },
+ expectedResolutions: []res{
+ {"lgplBin.meta_lic", "lgplBin.meta_lic", "lgplBin.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "gplonfpnotice",
+ condition: ImpliesNotice,
+ roots: []string{"gplBin.meta_lic"},
+ edges: []annotated{
+ {"gplBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"gplBin.meta_lic", "gplBin.meta_lic", "gplBin.meta_lic", "restricted"},
+ {"gplBin.meta_lic", "apacheLib.meta_lic", "apacheLib.meta_lic", "notice"},
+ {"gplBin.meta_lic", "apacheLib.meta_lic", "gplBin.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "gplonfprestricted",
+ condition: ImpliesRestricted,
+ roots: []string{"gplBin.meta_lic"},
+ edges: []annotated{
+ {"gplBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"gplBin.meta_lic", "gplBin.meta_lic", "gplBin.meta_lic", "restricted"},
+ {"gplBin.meta_lic", "apacheLib.meta_lic", "gplBin.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "gplcontainernotice",
+ condition: ImpliesNotice,
+ roots: []string{"gplContainer.meta_lic"},
+ edges: []annotated{
+ {"gplContainer.meta_lic", "apacheLib.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"gplContainer.meta_lic", "gplContainer.meta_lic", "gplContainer.meta_lic", "restricted"},
+ {"gplContainer.meta_lic", "apacheLib.meta_lic", "apacheLib.meta_lic", "notice"},
+ {"gplContainer.meta_lic", "apacheLib.meta_lic", "gplContainer.meta_lic", "restricted"},
+ {"apacheLib.meta_lic", "apacheLib.meta_lic", "apacheLib.meta_lic", "notice"},
+ {"apacheLib.meta_lic", "apacheLib.meta_lic", "gplContainer.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "gplcontainerrestricted",
+ condition: ImpliesRestricted,
+ roots: []string{"gplContainer.meta_lic"},
+ edges: []annotated{
+ {"gplContainer.meta_lic", "apacheLib.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"gplContainer.meta_lic", "gplContainer.meta_lic", "gplContainer.meta_lic", "restricted"},
+ {"gplContainer.meta_lic", "apacheLib.meta_lic", "gplContainer.meta_lic", "restricted"},
+ {"apacheLib.meta_lic", "apacheLib.meta_lic", "gplContainer.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "gploncontainernotice",
+ condition: ImpliesNotice,
+ roots: []string{"apacheContainer.meta_lic"},
+ edges: []annotated{
+ {"apacheContainer.meta_lic", "apacheLib.meta_lic", []string{"static"}},
+ {"apacheContainer.meta_lic", "gplLib.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"apacheContainer.meta_lic", "apacheContainer.meta_lic", "apacheContainer.meta_lic", "notice"},
+ {"apacheContainer.meta_lic", "apacheContainer.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"apacheContainer.meta_lic", "apacheLib.meta_lic", "apacheLib.meta_lic", "notice"},
+ {"apacheContainer.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"apacheLib.meta_lic", "apacheLib.meta_lic", "apacheLib.meta_lic", "notice"},
+ {"gplLib.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "gploncontainerrestricted",
+ condition: ImpliesRestricted,
+ roots: []string{"apacheContainer.meta_lic"},
+ edges: []annotated{
+ {"apacheContainer.meta_lic", "apacheLib.meta_lic", []string{"static"}},
+ {"apacheContainer.meta_lic", "gplLib.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"apacheContainer.meta_lic", "apacheContainer.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"apacheContainer.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"gplLib.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "gplonbinnotice",
+ condition: ImpliesNotice,
+ roots: []string{"apacheBin.meta_lic"},
+ edges: []annotated{
+ {"apacheBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
+ {"apacheBin.meta_lic", "gplLib.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"apacheBin.meta_lic", "apacheLib.meta_lic", "apacheLib.meta_lic", "notice"},
+ {"apacheBin.meta_lic", "apacheLib.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"apacheBin.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "gplonbinrestricted",
+ condition: ImpliesRestricted,
+ roots: []string{"apacheBin.meta_lic"},
+ edges: []annotated{
+ {"apacheBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
+ {"apacheBin.meta_lic", "gplLib.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"apacheBin.meta_lic", "apacheBin.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"apacheBin.meta_lic", "apacheLib.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"apacheBin.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "gplonfpdynamicnotice",
+ condition: ImpliesNotice,
+ roots: []string{"gplBin.meta_lic"},
+ edges: []annotated{
+ {"gplBin.meta_lic", "apacheLib.meta_lic", []string{"dynamic"}},
+ },
+ expectedResolutions: []res{
+ {"gplBin.meta_lic", "gplBin.meta_lic", "gplBin.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "gplonfpdynamicrestricted",
+ condition: ImpliesRestricted,
+ roots: []string{"gplBin.meta_lic"},
+ edges: []annotated{
+ {"gplBin.meta_lic", "apacheLib.meta_lic", []string{"dynamic"}},
+ },
+ expectedResolutions: []res{
+ {"gplBin.meta_lic", "gplBin.meta_lic", "gplBin.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "gplonfpdynamicrestrictedshipped",
+ condition: ImpliesRestricted,
+ roots: []string{"gplBin.meta_lic", "apacheLib.meta_lic"},
+ edges: []annotated{
+ {"gplBin.meta_lic", "apacheLib.meta_lic", []string{"dynamic"}},
+ },
+ expectedResolutions: []res{
+ {"gplBin.meta_lic", "gplBin.meta_lic", "gplBin.meta_lic", "restricted"},
+ {"gplBin.meta_lic", "apacheLib.meta_lic", "gplBin.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "independentmodulereversenotice",
+ condition: ImpliesNotice,
+ roots: []string{"gplWithClasspathException.meta_lic"},
+ edges: []annotated{
+ {"gplWithClasspathException.meta_lic", "apacheBin.meta_lic", []string{"dynamic"}},
+ },
+ expectedResolutions: []res{
+ {"gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "independentmodulereverserestricted",
+ condition: ImpliesRestricted,
+ roots: []string{"gplWithClasspathException.meta_lic"},
+ edges: []annotated{
+ {"gplWithClasspathException.meta_lic", "apacheBin.meta_lic", []string{"dynamic"}},
+ },
+ expectedResolutions: []res{
+ {"gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "independentmodulereverserestrictedshipped",
+ condition: ImpliesRestricted,
+ roots: []string{"gplWithClasspathException.meta_lic", "apacheBin.meta_lic"},
+ edges: []annotated{
+ {"gplWithClasspathException.meta_lic", "apacheBin.meta_lic", []string{"dynamic"}},
+ },
+ expectedResolutions: []res{
+ {"gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "independentmodulereversestaticnotice",
+ condition: ImpliesNotice,
+ roots: []string{"gplWithClasspathException.meta_lic"},
+ edges: []annotated{
+ {"gplWithClasspathException.meta_lic", "apacheBin.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
+ {"gplWithClasspathException.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+ {"gplWithClasspathException.meta_lic", "apacheBin.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "independentmodulereversestaticrestricted",
+ condition: ImpliesRestricted,
+ roots: []string{"gplWithClasspathException.meta_lic"},
+ edges: []annotated{
+ {"gplWithClasspathException.meta_lic", "apacheBin.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
+ {"gplWithClasspathException.meta_lic", "apacheBin.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "dependentmodulereversenotice",
+ condition: ImpliesNotice,
+ roots: []string{"gplWithClasspathException.meta_lic"},
+ edges: []annotated{
+ {"gplWithClasspathException.meta_lic", "dependentModule.meta_lic", []string{"dynamic"}},
+ },
+ expectedResolutions: []res{
+ {"gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "dependentmodulereverserestricted",
+ condition: ImpliesRestricted,
+ roots: []string{"gplWithClasspathException.meta_lic"},
+ edges: []annotated{
+ {"gplWithClasspathException.meta_lic", "dependentModule.meta_lic", []string{"dynamic"}},
+ },
+ expectedResolutions: []res{
+ {"gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "dependentmodulereverserestrictedshipped",
+ condition: ImpliesRestricted,
+ roots: []string{"gplWithClasspathException.meta_lic", "dependentModule.meta_lic"},
+ edges: []annotated{
+ {"gplWithClasspathException.meta_lic", "dependentModule.meta_lic", []string{"dynamic"}},
+ },
+ expectedResolutions: []res{
+ {"gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
+ {"gplWithClasspathException.meta_lic", "dependentModule.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "ponrnotice",
+ condition: ImpliesNotice,
+ roots: []string{"proprietary.meta_lic"},
+ edges: []annotated{
+ {"proprietary.meta_lic", "gplLib.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"proprietary.meta_lic", "proprietary.meta_lic", "proprietary.meta_lic", "proprietary"},
+ {"proprietary.meta_lic", "proprietary.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"proprietary.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "ponrrestricted",
+ condition: ImpliesRestricted,
+ roots: []string{"proprietary.meta_lic"},
+ edges: []annotated{
+ {"proprietary.meta_lic", "gplLib.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"proprietary.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
+ {"proprietary.meta_lic", "proprietary.meta_lic", "gplLib.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "ponrproprietary",
+ condition: ImpliesProprietary,
+ roots: []string{"proprietary.meta_lic"},
+ edges: []annotated{
+ {"proprietary.meta_lic", "gplLib.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"proprietary.meta_lic", "proprietary.meta_lic", "proprietary.meta_lic", "proprietary"},
+ },
+ },
+ {
+ name: "ronpnotice",
+ condition: ImpliesNotice,
+ roots: []string{"gplBin.meta_lic"},
+ edges: []annotated{
+ {"gplBin.meta_lic", "proprietary.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"gplBin.meta_lic", "gplBin.meta_lic", "gplBin.meta_lic", "restricted"},
+ {"gplBin.meta_lic", "proprietary.meta_lic", "proprietary.meta_lic", "proprietary"},
+ {"gplBin.meta_lic", "proprietary.meta_lic", "gplBin.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "ronprestricted",
+ condition: ImpliesRestricted,
+ roots: []string{"gplBin.meta_lic"},
+ edges: []annotated{
+ {"gplBin.meta_lic", "proprietary.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"gplBin.meta_lic", "gplBin.meta_lic", "gplBin.meta_lic", "restricted"},
+ {"gplBin.meta_lic", "proprietary.meta_lic", "gplBin.meta_lic", "restricted"},
+ },
+ },
+ {
+ name: "ronpproprietary",
+ condition: ImpliesProprietary,
+ roots: []string{"gplBin.meta_lic"},
+ edges: []annotated{
+ {"gplBin.meta_lic", "proprietary.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"gplBin.meta_lic", "proprietary.meta_lic", "proprietary.meta_lic", "proprietary"},
+ },
+ },
+ {
+ name: "noticeonb_e_onotice",
+ condition: ImpliesNotice,
+ roots: []string{"mitBin.meta_lic"},
+ edges: []annotated{
+ {"mitBin.meta_lic", "by_exception.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"mitBin.meta_lic", "mitBin.meta_lic", "mitBin.meta_lic", "notice"},
+ {"mitBin.meta_lic", "by_exception.meta_lic", "by_exception.meta_lic", "by_exception_only"},
+ },
+ },
+ {
+ name: "noticeonb_e_orestricted",
+ condition: ImpliesRestricted,
+ roots: []string{"mitBin.meta_lic"},
+ edges: []annotated{
+ {"mitBin.meta_lic", "by_exception.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{},
+ },
+ {
+ name: "noticeonb_e_ob_e_o",
+ condition: ImpliesByExceptionOnly,
+ roots: []string{"mitBin.meta_lic"},
+ edges: []annotated{
+ {"mitBin.meta_lic", "by_exception.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"mitBin.meta_lic", "by_exception.meta_lic", "by_exception.meta_lic", "by_exception_only"},
+ },
+ },
+ {
+ name: "b_e_oonnoticenotice",
+ condition: ImpliesNotice,
+ roots: []string{"by_exception.meta_lic"},
+ edges: []annotated{
+ {"by_exception.meta_lic", "mitLib.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"by_exception.meta_lic", "by_exception.meta_lic", "by_exception.meta_lic", "by_exception_only"},
+ {"by_exception.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
+ },
+ },
+ {
+ name: "b_e_oonnoticerestricted",
+ condition: ImpliesRestricted,
+ roots: []string{"by_exception.meta_lic"},
+ edges: []annotated{
+ {"by_exception.meta_lic", "mitLib.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{},
+ },
+ {
+ name: "b_e_oonnoticeb_e_o",
+ condition: ImpliesByExceptionOnly,
+ roots: []string{"by_exception.meta_lic"},
+ edges: []annotated{
+ {"by_exception.meta_lic", "mitLib.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"by_exception.meta_lic", "by_exception.meta_lic", "by_exception.meta_lic", "by_exception_only"},
+ },
+ },
+ {
+ name: "noticeonrecipnotice",
+ condition: ImpliesNotice,
+ roots: []string{"mitBin.meta_lic"},
+ edges: []annotated{
+ {"mitBin.meta_lic", "mplLib.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"mitBin.meta_lic", "mitBin.meta_lic", "mitBin.meta_lic", "notice"},
+ {"mitBin.meta_lic", "mplLib.meta_lic", "mplLib.meta_lic", "reciprocal"},
+ },
+ },
+ {
+ name: "noticeonreciprecip",
+ condition: ImpliesReciprocal,
+ roots: []string{"mitBin.meta_lic"},
+ edges: []annotated{
+ {"mitBin.meta_lic", "mplLib.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"mitBin.meta_lic", "mplLib.meta_lic", "mplLib.meta_lic", "reciprocal"},
+ },
+ },
+ {
+ name: "reciponnoticenotice",
+ condition: ImpliesNotice,
+ roots: []string{"mplBin.meta_lic"},
+ edges: []annotated{
+ {"mplBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"mplBin.meta_lic", "mplBin.meta_lic", "mplBin.meta_lic", "reciprocal"},
+ {"mplBin.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
+ },
+ },
+ {
+ name: "reciponnoticerecip",
+ condition: ImpliesReciprocal,
+ roots: []string{"mplBin.meta_lic"},
+ edges: []annotated{
+ {"mplBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
+ },
+ expectedResolutions: []res{
+ {"mplBin.meta_lic", "mplBin.meta_lic", "mplBin.meta_lic", "reciprocal"},
+ },
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ stderr := &bytes.Buffer{}
+ lg, err := toGraph(stderr, tt.roots, tt.edges)
+ if err != nil {
+ t.Errorf("unexpected test data error: got %w, want no error", err)
+ return
+ }
+ expectedRs := toResolutionSet(lg, tt.expectedResolutions)
+ actualRs := WalkResolutionsForCondition(lg, ResolveTopDownConditions(lg), tt.condition)
+ checkSame(actualRs, expectedRs, t)
+ })
+ }
+}
diff --git a/tools/compliance/resolutionset_test.go b/tools/compliance/resolutionset_test.go
index 265357a..e50e823 100644
--- a/tools/compliance/resolutionset_test.go
+++ b/tools/compliance/resolutionset_test.go
@@ -125,10 +125,10 @@
rsShare := toResolutionSet(lg, share)
- if rsShare.AttachedToTarget(newTestNode(lg, "binc")) {
+ if rsShare.AttachesToTarget(newTestNode(lg, "binc")) {
t.Errorf("unexpected AttachedToTarget(\"binc\"): got true, want false")
}
- if !rsShare.AttachedToTarget(newTestNode(lg, "image")) {
+ if !rsShare.AttachesToTarget(newTestNode(lg, "image")) {
t.Errorf("unexpected AttachedToTarget(\"image\"): got false want true")
}
}
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)}