Files
build_soong/android/licenses_test.go
Paul Duffin e96108d797 Add baseline test for license with sdk
This change adds a test that includes license modules that are used by
modules which are part of an sdk but which does not yet copy the
license module into the snapshot. It includes the refactoring changes
needed to allow license modules to be used in an sdk test and provides
a baseline against which future changes can be compared.

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

838 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
})
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
}