platform_build_soong/android/licenses_test.go
Paul Duffin ec0836af3a Switch Effective_license_text from []string to Paths
Effective_license_text contains paths to files that are copied from
one module to another and so need to be converted to Paths within the
context of the owning module as the paths are relative to the owning
module's directory.

The previous code did convert the license_text property to paths but
converted it back to strings again which was confusing and does not
follow the normal pattern.

Bug: 181569894
Test: m nothing
Change-Id: Iea09ee7f3de1187a2c3e41455ca83b0233d904b2
2021-05-11 08:24:59 +01:00

837 lines
21 KiB
Go

package android
import (
"testing"
"github.com/google/blueprint"
)
var licensesTests = []struct {
name string
fs MockFS
expectedErrors []string
effectiveLicenses map[string][]string
effectiveInheritedLicenses map[string][]string
effectivePackage map[string]string
effectiveNotices map[string][]string
effectiveKinds map[string][]string
effectiveConditions map[string][]string
}{
{
name: "invalid module type without licenses property",
fs: map[string][]byte{
"top/Blueprints": []byte(`
mock_bad_module {
name: "libexample",
}`),
},
expectedErrors: []string{`module type "mock_bad_module" must have an applicable licenses property`},
},
{
name: "license must exist",
fs: map[string][]byte{
"top/Blueprints": []byte(`
mock_library {
name: "libexample",
licenses: ["notice"],
}`),
},
expectedErrors: []string{`"libexample" depends on undefined module "notice"`},
},
{
name: "all good",
fs: map[string][]byte{
"top/Blueprints": []byte(`
license_kind {
name: "notice",
conditions: ["shownotice"],
}
license {
name: "top_Apache2",
license_kinds: ["notice"],
package_name: "topDog",
license_text: ["LICENSE", "NOTICE"],
}
mock_library {
name: "libexample1",
licenses: ["top_Apache2"],
}`),
"top/nested/Blueprints": []byte(`
mock_library {
name: "libnested",
licenses: ["top_Apache2"],
}`),
"other/Blueprints": []byte(`
mock_library {
name: "libother",
licenses: ["top_Apache2"],
}`),
},
effectiveLicenses: map[string][]string{
"libexample1": []string{"top_Apache2"},
"libnested": []string{"top_Apache2"},
"libother": []string{"top_Apache2"},
},
effectiveKinds: map[string][]string{
"libexample1": []string{"notice"},
"libnested": []string{"notice"},
"libother": []string{"notice"},
},
effectivePackage: map[string]string{
"libexample1": "topDog",
"libnested": "topDog",
"libother": "topDog",
},
effectiveConditions: map[string][]string{
"libexample1": []string{"shownotice"},
"libnested": []string{"shownotice"},
"libother": []string{"shownotice"},
},
effectiveNotices: map[string][]string{
"libexample1": []string{"top/LICENSE", "top/NOTICE"},
"libnested": []string{"top/LICENSE", "top/NOTICE"},
"libother": []string{"top/LICENSE", "top/NOTICE"},
},
},
// Defaults propagation tests
{
// Check that licenses is the union of the defaults modules.
name: "defaults union, basic",
fs: map[string][]byte{
"top/Blueprints": []byte(`
license_kind {
name: "top_notice",
conditions: ["notice"],
}
license {
name: "top_other",
license_kinds: ["top_notice"],
}
mock_defaults {
name: "libexample_defaults",
licenses: ["top_other"],
}
mock_library {
name: "libexample",
licenses: ["nested_other"],
defaults: ["libexample_defaults"],
}
mock_library {
name: "libsamepackage",
deps: ["libexample"],
}`),
"top/nested/Blueprints": []byte(`
license_kind {
name: "nested_notice",
conditions: ["notice"],
}
license {
name: "nested_other",
license_kinds: ["nested_notice"],
}
mock_library {
name: "libnested",
deps: ["libexample"],
}`),
"other/Blueprints": []byte(`
mock_library {
name: "libother",
deps: ["libexample"],
}`),
},
effectiveLicenses: map[string][]string{
"libexample": []string{"nested_other", "top_other"},
"libsamepackage": []string{},
"libnested": []string{},
"libother": []string{},
},
effectiveInheritedLicenses: map[string][]string{
"libexample": []string{"nested_other", "top_other"},
"libsamepackage": []string{"nested_other", "top_other"},
"libnested": []string{"nested_other", "top_other"},
"libother": []string{"nested_other", "top_other"},
},
effectiveKinds: map[string][]string{
"libexample": []string{"nested_notice", "top_notice"},
"libsamepackage": []string{},
"libnested": []string{},
"libother": []string{},
},
effectiveConditions: map[string][]string{
"libexample": []string{"notice"},
"libsamepackage": []string{},
"libnested": []string{},
"libother": []string{},
},
},
{
name: "defaults union, multiple defaults",
fs: map[string][]byte{
"top/Blueprints": []byte(`
license {
name: "top",
}
mock_defaults {
name: "libexample_defaults_1",
licenses: ["other"],
}
mock_defaults {
name: "libexample_defaults_2",
licenses: ["top_nested"],
}
mock_library {
name: "libexample",
defaults: ["libexample_defaults_1", "libexample_defaults_2"],
}
mock_library {
name: "libsamepackage",
deps: ["libexample"],
}`),
"top/nested/Blueprints": []byte(`
license {
name: "top_nested",
license_text: ["LICENSE.txt"],
}
mock_library {
name: "libnested",
deps: ["libexample"],
}`),
"other/Blueprints": []byte(`
license {
name: "other",
}
mock_library {
name: "libother",
deps: ["libexample"],
}`),
"outsider/Blueprints": []byte(`
mock_library {
name: "liboutsider",
deps: ["libexample"],
}`),
},
effectiveLicenses: map[string][]string{
"libexample": []string{"other", "top_nested"},
"libsamepackage": []string{},
"libnested": []string{},
"libother": []string{},
"liboutsider": []string{},
},
effectiveInheritedLicenses: map[string][]string{
"libexample": []string{"other", "top_nested"},
"libsamepackage": []string{"other", "top_nested"},
"libnested": []string{"other", "top_nested"},
"libother": []string{"other", "top_nested"},
"liboutsider": []string{"other", "top_nested"},
},
effectiveKinds: map[string][]string{
"libexample": []string{},
"libsamepackage": []string{},
"libnested": []string{},
"libother": []string{},
"liboutsider": []string{},
},
effectiveNotices: map[string][]string{
"libexample": []string{"top/nested/LICENSE.txt"},
"libsamepackage": []string{},
"libnested": []string{},
"libother": []string{},
"liboutsider": []string{},
},
},
// Defaults module's defaults_licenses tests
{
name: "defaults_licenses invalid",
fs: map[string][]byte{
"top/Blueprints": []byte(`
mock_defaults {
name: "top_defaults",
licenses: ["notice"],
}`),
},
expectedErrors: []string{`"top_defaults" depends on undefined module "notice"`},
},
{
name: "defaults_licenses overrides package default",
fs: map[string][]byte{
"top/Blueprints": []byte(`
package {
default_applicable_licenses: ["by_exception_only"],
}
license {
name: "by_exception_only",
}
license {
name: "notice",
}
mock_defaults {
name: "top_defaults",
licenses: ["notice"],
}
mock_library {
name: "libexample",
}
mock_library {
name: "libdefaults",
defaults: ["top_defaults"],
}`),
},
effectiveLicenses: map[string][]string{
"libexample": []string{"by_exception_only"},
"libdefaults": []string{"notice"},
},
effectiveInheritedLicenses: map[string][]string{
"libexample": []string{"by_exception_only"},
"libdefaults": []string{"notice"},
},
},
// Package default_applicable_licenses tests
{
name: "package default_applicable_licenses must exist",
fs: map[string][]byte{
"top/Blueprints": []byte(`
package {
default_applicable_licenses: ["notice"],
}`),
},
expectedErrors: []string{`"//top" depends on undefined module "notice"`},
},
{
// This test relies on the default licenses being legacy_public.
name: "package default_applicable_licenses property used when no licenses specified",
fs: map[string][]byte{
"top/Blueprints": []byte(`
package {
default_applicable_licenses: ["top_notice"],
}
license {
name: "top_notice",
}
mock_library {
name: "libexample",
}`),
"outsider/Blueprints": []byte(`
mock_library {
name: "liboutsider",
deps: ["libexample"],
}`),
},
effectiveLicenses: map[string][]string{
"libexample": []string{"top_notice"},
"liboutsider": []string{},
},
effectiveInheritedLicenses: map[string][]string{
"libexample": []string{"top_notice"},
"liboutsider": []string{"top_notice"},
},
},
{
name: "package default_applicable_licenses not inherited to subpackages",
fs: map[string][]byte{
"top/Blueprints": []byte(`
package {
default_applicable_licenses: ["top_notice"],
}
license {
name: "top_notice",
}
mock_library {
name: "libexample",
}`),
"top/nested/Blueprints": []byte(`
package {
default_applicable_licenses: ["outsider"],
}
mock_library {
name: "libnested",
}`),
"top/other/Blueprints": []byte(`
mock_library {
name: "libother",
}`),
"outsider/Blueprints": []byte(`
license {
name: "outsider",
}
mock_library {
name: "liboutsider",
deps: ["libexample", "libother", "libnested"],
}`),
},
effectiveLicenses: map[string][]string{
"libexample": []string{"top_notice"},
"libnested": []string{"outsider"},
"libother": []string{},
"liboutsider": []string{},
},
effectiveInheritedLicenses: map[string][]string{
"libexample": []string{"top_notice"},
"libnested": []string{"outsider"},
"libother": []string{},
"liboutsider": []string{"top_notice", "outsider"},
},
},
{
name: "verify that prebuilt dependencies are included",
fs: map[string][]byte{
"prebuilts/Blueprints": []byte(`
license {
name: "prebuilt"
}
prebuilt {
name: "module",
licenses: ["prebuilt"],
}`),
"top/sources/source_file": nil,
"top/sources/Blueprints": []byte(`
license {
name: "top_sources"
}
source {
name: "module",
licenses: ["top_sources"],
}`),
"top/other/source_file": nil,
"top/other/Blueprints": []byte(`
source {
name: "other",
deps: [":module"],
}`),
},
effectiveLicenses: map[string][]string{
"other": []string{},
},
effectiveInheritedLicenses: map[string][]string{
"other": []string{"prebuilt", "top_sources"},
},
},
{
name: "verify that prebuilt dependencies are ignored for licenses reasons (preferred)",
fs: map[string][]byte{
"prebuilts/Blueprints": []byte(`
license {
name: "prebuilt"
}
prebuilt {
name: "module",
licenses: ["prebuilt"],
prefer: true,
}`),
"top/sources/source_file": nil,
"top/sources/Blueprints": []byte(`
license {
name: "top_sources"
}
source {
name: "module",
licenses: ["top_sources"],
}`),
"top/other/source_file": nil,
"top/other/Blueprints": []byte(`
source {
name: "other",
deps: [":module"],
}`),
},
effectiveLicenses: map[string][]string{
"other": []string{},
},
effectiveInheritedLicenses: map[string][]string{
"module": []string{"prebuilt", "top_sources"},
"other": []string{"prebuilt", "top_sources"},
},
},
}
func TestLicenses(t *testing.T) {
for _, test := range licensesTests {
t.Run(test.name, func(t *testing.T) {
// Customize the common license text fixture factory.
result := GroupFixturePreparers(
prepareForLicenseTest,
FixtureRegisterWithContext(func(ctx RegistrationContext) {
ctx.RegisterModuleType("mock_bad_module", newMockLicensesBadModule)
ctx.RegisterModuleType("mock_library", newMockLicensesLibraryModule)
ctx.RegisterModuleType("mock_defaults", defaultsLicensesFactory)
}),
test.fs.AddToFixture(),
).
ExtendWithErrorHandler(FixtureExpectsAllErrorsToMatchAPattern(test.expectedErrors)).
RunTest(t)
if test.effectiveLicenses != nil {
checkEffectiveLicenses(t, result, test.effectiveLicenses)
}
if test.effectivePackage != nil {
checkEffectivePackage(t, result, test.effectivePackage)
}
if test.effectiveNotices != nil {
checkEffectiveNotices(t, result, test.effectiveNotices)
}
if test.effectiveKinds != nil {
checkEffectiveKinds(t, result, test.effectiveKinds)
}
if test.effectiveConditions != nil {
checkEffectiveConditions(t, result, test.effectiveConditions)
}
if test.effectiveInheritedLicenses != nil {
checkEffectiveInheritedLicenses(t, result, test.effectiveInheritedLicenses)
}
})
}
}
func checkEffectiveLicenses(t *testing.T, result *TestResult, effectiveLicenses map[string][]string) {
actualLicenses := make(map[string][]string)
result.Context.Context.VisitAllModules(func(m blueprint.Module) {
if _, ok := m.(*licenseModule); ok {
return
}
if _, ok := m.(*licenseKindModule); ok {
return
}
if _, ok := m.(*packageModule); ok {
return
}
module, ok := m.(Module)
if !ok {
t.Errorf("%q not a module", m.Name())
return
}
base := module.base()
if base == nil {
return
}
actualLicenses[m.Name()] = base.commonProperties.Effective_licenses
})
for moduleName, expectedLicenses := range effectiveLicenses {
licenses, ok := actualLicenses[moduleName]
if !ok {
licenses = []string{}
}
if !compareUnorderedStringArrays(expectedLicenses, licenses) {
t.Errorf("effective licenses mismatch for module %q: expected %q, found %q", moduleName, expectedLicenses, licenses)
}
}
}
func checkEffectiveInheritedLicenses(t *testing.T, result *TestResult, effectiveInheritedLicenses map[string][]string) {
actualLicenses := make(map[string][]string)
result.Context.Context.VisitAllModules(func(m blueprint.Module) {
if _, ok := m.(*licenseModule); ok {
return
}
if _, ok := m.(*licenseKindModule); ok {
return
}
if _, ok := m.(*packageModule); ok {
return
}
module, ok := m.(Module)
if !ok {
t.Errorf("%q not a module", m.Name())
return
}
base := module.base()
if base == nil {
return
}
inherited := make(map[string]bool)
for _, l := range base.commonProperties.Effective_licenses {
inherited[l] = true
}
result.Context.Context.VisitDepsDepthFirst(m, func(c blueprint.Module) {
if _, ok := c.(*licenseModule); ok {
return
}
if _, ok := c.(*licenseKindModule); ok {
return
}
if _, ok := c.(*packageModule); ok {
return
}
cmodule, ok := c.(Module)
if !ok {
t.Errorf("%q not a module", c.Name())
return
}
cbase := cmodule.base()
if cbase == nil {
return
}
for _, l := range cbase.commonProperties.Effective_licenses {
inherited[l] = true
}
})
actualLicenses[m.Name()] = []string{}
for l := range inherited {
actualLicenses[m.Name()] = append(actualLicenses[m.Name()], l)
}
})
for moduleName, expectedInheritedLicenses := range effectiveInheritedLicenses {
licenses, ok := actualLicenses[moduleName]
if !ok {
licenses = []string{}
}
if !compareUnorderedStringArrays(expectedInheritedLicenses, licenses) {
t.Errorf("effective inherited licenses mismatch for module %q: expected %q, found %q", moduleName, expectedInheritedLicenses, licenses)
}
}
}
func checkEffectivePackage(t *testing.T, result *TestResult, effectivePackage map[string]string) {
actualPackage := make(map[string]string)
result.Context.Context.VisitAllModules(func(m blueprint.Module) {
if _, ok := m.(*licenseModule); ok {
return
}
if _, ok := m.(*licenseKindModule); ok {
return
}
if _, ok := m.(*packageModule); ok {
return
}
module, ok := m.(Module)
if !ok {
t.Errorf("%q not a module", m.Name())
return
}
base := module.base()
if base == nil {
return
}
if base.commonProperties.Effective_package_name == nil {
actualPackage[m.Name()] = ""
} else {
actualPackage[m.Name()] = *base.commonProperties.Effective_package_name
}
})
for moduleName, expectedPackage := range effectivePackage {
packageName, ok := actualPackage[moduleName]
if !ok {
packageName = ""
}
if expectedPackage != packageName {
t.Errorf("effective package mismatch for module %q: expected %q, found %q", moduleName, expectedPackage, packageName)
}
}
}
func checkEffectiveNotices(t *testing.T, result *TestResult, effectiveNotices map[string][]string) {
actualNotices := make(map[string][]string)
result.Context.Context.VisitAllModules(func(m blueprint.Module) {
if _, ok := m.(*licenseModule); ok {
return
}
if _, ok := m.(*licenseKindModule); ok {
return
}
if _, ok := m.(*packageModule); ok {
return
}
module, ok := m.(Module)
if !ok {
t.Errorf("%q not a module", m.Name())
return
}
base := module.base()
if base == nil {
return
}
actualNotices[m.Name()] = base.commonProperties.Effective_license_text.Strings()
})
for moduleName, expectedNotices := range effectiveNotices {
notices, ok := actualNotices[moduleName]
if !ok {
notices = []string{}
}
if !compareUnorderedStringArrays(expectedNotices, notices) {
t.Errorf("effective notice files mismatch for module %q: expected %q, found %q", moduleName, expectedNotices, notices)
}
}
}
func checkEffectiveKinds(t *testing.T, result *TestResult, effectiveKinds map[string][]string) {
actualKinds := make(map[string][]string)
result.Context.Context.VisitAllModules(func(m blueprint.Module) {
if _, ok := m.(*licenseModule); ok {
return
}
if _, ok := m.(*licenseKindModule); ok {
return
}
if _, ok := m.(*packageModule); ok {
return
}
module, ok := m.(Module)
if !ok {
t.Errorf("%q not a module", m.Name())
return
}
base := module.base()
if base == nil {
return
}
actualKinds[m.Name()] = base.commonProperties.Effective_license_kinds
})
for moduleName, expectedKinds := range effectiveKinds {
kinds, ok := actualKinds[moduleName]
if !ok {
kinds = []string{}
}
if !compareUnorderedStringArrays(expectedKinds, kinds) {
t.Errorf("effective license kinds mismatch for module %q: expected %q, found %q", moduleName, expectedKinds, kinds)
}
}
}
func checkEffectiveConditions(t *testing.T, result *TestResult, effectiveConditions map[string][]string) {
actualConditions := make(map[string][]string)
result.Context.Context.VisitAllModules(func(m blueprint.Module) {
if _, ok := m.(*licenseModule); ok {
return
}
if _, ok := m.(*licenseKindModule); ok {
return
}
if _, ok := m.(*packageModule); ok {
return
}
module, ok := m.(Module)
if !ok {
t.Errorf("%q not a module", m.Name())
return
}
base := module.base()
if base == nil {
return
}
actualConditions[m.Name()] = base.commonProperties.Effective_license_conditions
})
for moduleName, expectedConditions := range effectiveConditions {
conditions, ok := actualConditions[moduleName]
if !ok {
conditions = []string{}
}
if !compareUnorderedStringArrays(expectedConditions, conditions) {
t.Errorf("effective license conditions mismatch for module %q: expected %q, found %q", moduleName, expectedConditions, conditions)
}
}
}
func compareUnorderedStringArrays(expected, actual []string) bool {
if len(expected) != len(actual) {
return false
}
s := make(map[string]int)
for _, v := range expected {
s[v] += 1
}
for _, v := range actual {
c, ok := s[v]
if !ok {
return false
}
if c < 1 {
return false
}
s[v] -= 1
}
return true
}
type mockLicensesBadProperties struct {
Visibility []string
}
type mockLicensesBadModule struct {
ModuleBase
DefaultableModuleBase
properties mockLicensesBadProperties
}
func newMockLicensesBadModule() Module {
m := &mockLicensesBadModule{}
base := m.base()
m.AddProperties(&base.nameProperties, &m.properties)
base.generalProperties = m.GetProperties()
base.customizableProperties = m.GetProperties()
// The default_visibility property needs to be checked and parsed by the visibility module during
// its checking and parsing phases so make it the primary visibility property.
setPrimaryVisibilityProperty(m, "visibility", &m.properties.Visibility)
initAndroidModuleBase(m)
InitDefaultableModule(m)
return m
}
func (m *mockLicensesBadModule) GenerateAndroidBuildActions(ModuleContext) {
}
type mockLicensesLibraryProperties struct {
Deps []string
}
type mockLicensesLibraryModule struct {
ModuleBase
DefaultableModuleBase
properties mockLicensesLibraryProperties
}
func newMockLicensesLibraryModule() Module {
m := &mockLicensesLibraryModule{}
m.AddProperties(&m.properties)
InitAndroidArchModule(m, HostAndDeviceSupported, MultilibCommon)
InitDefaultableModule(m)
return m
}
type dependencyLicensesTag struct {
blueprint.BaseDependencyTag
name string
}
func (j *mockLicensesLibraryModule) DepsMutator(ctx BottomUpMutatorContext) {
ctx.AddVariationDependencies(nil, dependencyLicensesTag{name: "mockdeps"}, j.properties.Deps...)
}
func (p *mockLicensesLibraryModule) GenerateAndroidBuildActions(ModuleContext) {
}
type mockLicensesDefaults struct {
ModuleBase
DefaultsModuleBase
}
func defaultsLicensesFactory() Module {
m := &mockLicensesDefaults{}
InitDefaultsModule(m)
return m
}