blob: 67b2e8096e5e130790f6ed2b200d07eb71e8c8e1 [file]
// Copyright 2026 Google Inc. All rights reserved.
//
// 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.
// Tests for strict dependencies enforcement in Java modules.
package java
import (
"android/soong/android"
"testing"
)
func TestStrictDeps(t *testing.T) {
t.Parallel()
tests := []struct {
name string
value string // "warn", "error", "off", or ""
}{
{
name: "warn",
value: "warn",
},
{
name: "error",
value: "error",
},
{
name: "off",
value: "off",
},
{
name: "omitted",
value: "",
},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
var strictDepsStr string
if tc.value != "" {
strictDepsStr = `strict_deps: "` + tc.value + `",`
}
strictDepsEnabled := tc.value == "warn" || tc.value == "error"
result := android.GroupFixturePreparers(
prepareForJavaTest,
).RunTestWithBp(t, `
java_library {
name: "foo",
srcs: ["a.java"],
libs: ["bar"],
`+strictDepsStr+`
}
java_library {
name: "foo_sharded",
srcs: ["a.java", "b.java"],
libs: ["bar"],
javac_shard_size: 1,
`+strictDepsStr+`
}
java_library {
name: "foo_kotlin",
srcs: ["a.kt"],
libs: ["bar"],
`+strictDepsStr+`
}
java_library {
name: "bar",
srcs: ["b.java"],
static_libs: ["baz"],
}
java_library {
name: "baz",
srcs: ["c.java"],
}
java_plugin {
name: "soong_java_strict_deps_plugin",
srcs: ["plugin.java"],
}
kotlin_plugin {
name: "soong_kotlin_strict_deps_plugin",
srcs: ["plugin.java"],
}
`)
foo := result.ModuleForTests(t, "foo", "android_common").Module()
// Verify that strict deps plugins are added as dependencies
if strictDepsEnabled {
hasPlugin := false
hasKotlinPlugin := false
result.VisitDirectDeps(foo, func(dep android.Module) {
name := result.ModuleName(dep)
if name == "soong_java_strict_deps_plugin" {
hasPlugin = true
}
if name == "soong_kotlin_strict_deps_plugin" {
hasKotlinPlugin = true
}
})
android.AssertBoolEquals(t, "soong_java_strict_deps_plugin dependency", true, hasPlugin)
android.AssertBoolEquals(t, "soong_kotlin_strict_deps_plugin dependency", true, hasKotlinPlugin)
}
fooLib := foo.(*Library)
classpathStrings := fooLib.directClasspath.Strings()
if strictDepsEnabled {
// Verify both compilation pathways (incremental and sharded/full) generate the JavaStrictDeps plugin flag
fooIncRule := result.ModuleForTests(t, "foo", "android_common").Rule("javac")
fooShardedRule := result.ModuleForTests(t, "foo_sharded", "android_common").Rule("javac")
fooIncRspPath := result.ModuleForTests(t, "foo", "android_common").Output("javac/strict_deps.rsp").Output.String()
android.AssertStringDoesContain(t, "foo (incremental) javac flags", fooIncRule.Args["javacFlags"], "-Xplugin:\"JavaStrictDeps "+fooIncRspPath+" "+tc.value+"\"")
fooShardedRspPath := result.ModuleForTests(t, "foo_sharded", "android_common").Output("javac/shard0/strict_deps.rsp").Output.String()
android.AssertStringDoesContain(t, "foo_sharded (full/sharded) javac flags", fooShardedRule.Args["javacFlags"], "-Xplugin:\"JavaStrictDeps "+fooShardedRspPath+" "+tc.value+"\"")
fooIncRsp := android.ContentFromFileRuleForTests(t, result.TestContext, result.ModuleForTests(t, "foo", "android_common").Output("javac/strict_deps.rsp"))
android.AssertStringDoesContain(t, "foo (incremental) rsp file contents include bar", fooIncRsp, "bar.jar")
android.AssertStringDoesNotContain(t, "foo (incremental) rsp file contents EXCLUDE baz", fooIncRsp, "baz.jar")
fooKotlinRule := result.ModuleForTests(t, "foo_kotlin", "android_common").Rule("kotlinc")
fooKotlinRspPath := result.ModuleForTests(t, "foo_kotlin", "android_common").Output("strict_deps.rsp").Output.String()
android.AssertStringDoesContain(t, "foo_kotlin kotlinc flags (-Xplugin)", fooKotlinRule.Args["kotlincFlags"], "-Xplugin=")
android.AssertStringDoesContain(t, "foo_kotlin kotlinc flags (-P rsp)", fooKotlinRule.Args["kotlincFlags"], "-P plugin:com.android.strictdeps:rsp="+fooKotlinRspPath)
android.AssertStringDoesContain(t, "foo_kotlin kotlinc flags (-P level)", fooKotlinRule.Args["kotlincFlags"], "-P plugin:com.android.strictdeps:level="+tc.value)
} else {
// When strict_deps is off, directClasspath shouldn't be populated for injection whitelisting
if len(classpathStrings) > 0 {
t.Errorf("Expected directClasspath to be empty when strict_deps is off/omitted, but got: %v", classpathStrings)
}
fooIncRule := result.ModuleForTests(t, "foo", "android_common").Rule("javac")
fooShardedRule := result.ModuleForTests(t, "foo_sharded", "android_common").Rule("javac")
android.AssertStringDoesNotContain(t, "foo (incremental) javac flags should NOT have plugin", fooIncRule.Args["javacFlags"], "-Xplugin:\"JavaStrictDeps")
android.AssertStringDoesNotContain(t, "foo_sharded javac flags should NOT have plugin", fooShardedRule.Args["javacFlags"], "-Xplugin:\"JavaStrictDeps")
fooKotlinRule := result.ModuleForTests(t, "foo_kotlin", "android_common").Rule("kotlinc")
android.AssertStringDoesNotContain(t, "foo_kotlin kotlinc flags should NOT have plugin", fooKotlinRule.Args["kotlincFlags"], "-Xplugin=")
}
})
}
}
func TestStrictDepsWrapper(t *testing.T) {
t.Parallel()
result := android.GroupFixturePreparers(
prepareForJavaTest,
).RunTestWithBp(t, `
java_library {
name: "foo",
srcs: ["a.java"],
libs: ["bar_wrapper"],
strict_deps: "error",
}
java_library {
name: "bar_wrapper",
libs: ["bar"],
}
java_library {
name: "bar",
srcs: ["b.java"],
static_libs: ["baz"],
}
java_library {
name: "baz",
srcs: ["c.java"],
}
java_plugin {
name: "soong_java_strict_deps_plugin",
srcs: ["plugin.java"],
}
kotlin_plugin {
name: "soong_kotlin_strict_deps_plugin",
srcs: ["plugin.java"],
}
`)
fooIncRule := result.ModuleForTests(t, "foo", "android_common").Rule("javac")
fooIncRspPath := result.ModuleForTests(t, "foo", "android_common").Output("javac/strict_deps.rsp").Output.String()
android.AssertStringDoesContain(t, "foo javac flags for wrapper test", fooIncRule.Args["javacFlags"], "-Xplugin:\"JavaStrictDeps "+fooIncRspPath+" error\"")
}