Merge changes Id4223972,I3de213da into androidx-main

* changes:
  Add test data to test input definition.
  Store generated sources from tests in build output.
diff --git a/privacysandbox/tools/tools-apicompiler/build.gradle b/privacysandbox/tools/tools-apicompiler/build.gradle
index c17bdce..6abb2a1 100644
--- a/privacysandbox/tools/tools-apicompiler/build.gradle
+++ b/privacysandbox/tools/tools-apicompiler/build.gradle
@@ -45,20 +45,24 @@
             dir: "${SdkHelperKt.getSdkPath(project)}/platforms/$SupportConfig.COMPILE_SDK_VERSION/",
             include: "android.jar"
     ))
-    // Get AIDL compiler path and framework.aidl path and pass to tests for code generation.
-    def aidlCompilerPath = "${SdkHelperKt.getSdkPath(project)}/build-tools/${SupportConfig.buildToolsVersion(project)}/aidl"
-    def frameworkAidlPath = "${SdkHelperKt.getSdkPath(project)}/platforms/${SupportConfig.COMPILE_SDK_VERSION}/framework.aidl"
-    test {
-        inputs.files(aidlCompilerPath)
-                .withPropertyName("aidl_compiler_path")
-                .withPathSensitivity(PathSensitivity.NAME_ONLY)
-        inputs.files(frameworkAidlPath)
-                .withPropertyName("framework_aidl_path")
-                .withPathSensitivity(PathSensitivity.NAME_ONLY)
-        doFirst {
-            systemProperty "aidl_compiler_path", aidlCompilerPath
-            systemProperty "framework_aidl_path", frameworkAidlPath
-        }
+}
+
+// Get AIDL compiler path and framework.aidl path and pass to tests for code generation.
+def aidlCompilerPath = "${SdkHelperKt.getSdkPath(project)}/build-tools/${SupportConfig.buildToolsVersion(project)}/aidl"
+def frameworkAidlPath = "${SdkHelperKt.getSdkPath(project)}/platforms/${SupportConfig.COMPILE_SDK_VERSION}/framework.aidl"
+def testGeneratedSourcesPath = "${project.buildDir}/testGeneratedSources"
+test {
+    inputs.files(aidlCompilerPath)
+            .withPropertyName("aidl_compiler_path")
+            .withPathSensitivity(PathSensitivity.NAME_ONLY)
+    inputs.files(frameworkAidlPath)
+            .withPropertyName("framework_aidl_path")
+            .withPathSensitivity(PathSensitivity.NAME_ONLY)
+    inputs.dir("src/test/test-data").withPathSensitivity(PathSensitivity.RELATIVE)
+    doFirst {
+        systemProperty "aidl_compiler_path", aidlCompilerPath
+        systemProperty "framework_aidl_path", frameworkAidlPath
+        systemProperty "test_output_dir", testGeneratedSourcesPath
     }
 }
 
diff --git a/privacysandbox/tools/tools-apicompiler/src/test/java/androidx/privacysandbox/tools/apicompiler/AbstractApiCompilerDiffTest.kt b/privacysandbox/tools/tools-apicompiler/src/test/java/androidx/privacysandbox/tools/apicompiler/AbstractApiCompilerDiffTest.kt
new file mode 100644
index 0000000..cd0f1b0
--- /dev/null
+++ b/privacysandbox/tools/tools-apicompiler/src/test/java/androidx/privacysandbox/tools/apicompiler/AbstractApiCompilerDiffTest.kt
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * 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 androidx.privacysandbox.tools.apicompiler
+
+import androidx.privacysandbox.tools.testing.AbstractDiffTest
+import androidx.privacysandbox.tools.testing.CompilationTestHelper
+import androidx.room.compiler.processing.util.Source
+import java.nio.file.Path
+import kotlin.io.path.createDirectories
+import kotlin.io.path.createFile
+import kotlin.io.path.writeText
+
+/** Base test class for API Compiler diff testing. */
+abstract class AbstractApiCompilerDiffTest : AbstractDiffTest() {
+
+    open val extraProcessorOptions: Map<String, String> = mapOf()
+
+    override fun generateSources(
+        inputSources: List<Source>,
+        outputDirectory: Path
+    ): List<Source> {
+        val result = compileWithPrivacySandboxKspCompiler(inputSources, extraProcessorOptions)
+        CompilationTestHelper.assertThat(result).succeeds()
+        val sources = result.generatedSources
+
+        // Writing generated sources to expected output directory.
+        sources.forEach { source ->
+            outputDirectory.resolve(source.relativePath).apply {
+                parent?.createDirectories()
+                createFile()
+                writeText(source.contents)
+            }
+        }
+        return sources
+    }
+}
\ No newline at end of file
diff --git a/privacysandbox/tools/tools-apicompiler/src/test/java/androidx/privacysandbox/tools/apicompiler/FullFeaturedSdkApiCompilerDiffTest.kt b/privacysandbox/tools/tools-apicompiler/src/test/java/androidx/privacysandbox/tools/apicompiler/FullFeaturedSdkApiCompilerDiffTest.kt
new file mode 100644
index 0000000..8f53ceb
--- /dev/null
+++ b/privacysandbox/tools/tools-apicompiler/src/test/java/androidx/privacysandbox/tools/apicompiler/FullFeaturedSdkApiCompilerDiffTest.kt
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * 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 androidx.privacysandbox.tools.apicompiler
+
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+/** Test the Privacy Sandbox API Compiler with an SDK that uses all available features. */
+@RunWith(JUnit4::class)
+class FullFeaturedSdkApiCompilerDiffTest : AbstractApiCompilerDiffTest() {
+    override val subdirectoryName = "fullfeaturedsdk"
+    override val relativePathsToExpectedAidlClasses = listOf(
+        "com/mysdk/ICancellationSignal.java",
+        "com/mysdk/IMyCallback.java",
+        "com/mysdk/IMyInterface.java",
+        "com/mysdk/IMyInterfaceTransactionCallback.java",
+        "com/mysdk/IMySdk.java",
+        "com/mysdk/IMySecondInterface.java",
+        "com/mysdk/IMyUiInterface.java",
+        "com/mysdk/IMyUiInterfaceCoreLibInfoAndBinderWrapper.java",
+        "com/mysdk/IMyUiInterfaceTransactionCallback.java",
+        "com/mysdk/IMySecondInterfaceTransactionCallback.java",
+        "com/mysdk/IResponseTransactionCallback.java",
+        "com/mysdk/IStringTransactionCallback.java",
+        "com/mysdk/IUnitTransactionCallback.java",
+        "com/mysdk/IListResponseTransactionCallback.java",
+        "com/mysdk/IListIntTransactionCallback.java",
+        "com/mysdk/IListLongTransactionCallback.java",
+        "com/mysdk/IListDoubleTransactionCallback.java",
+        "com/mysdk/IListStringTransactionCallback.java",
+        "com/mysdk/IListBooleanTransactionCallback.java",
+        "com/mysdk/IListFloatTransactionCallback.java",
+        "com/mysdk/IListCharTransactionCallback.java",
+        "com/mysdk/IListShortTransactionCallback.java",
+        "com/mysdk/ParcelableRequest.java",
+        "com/mysdk/ParcelableResponse.java",
+        "com/mysdk/ParcelableStackFrame.java",
+        "com/mysdk/ParcelableInnerValue.java",
+        "com/mysdk/PrivacySandboxThrowableParcel.java",
+    )
+}
\ No newline at end of file
diff --git a/privacysandbox/tools/tools-apicompiler/src/test/java/androidx/privacysandbox/tools/apicompiler/FullFeaturedSdkTest.kt b/privacysandbox/tools/tools-apicompiler/src/test/java/androidx/privacysandbox/tools/apicompiler/FullFeaturedSdkTest.kt
deleted file mode 100644
index 9e0343d..0000000
--- a/privacysandbox/tools/tools-apicompiler/src/test/java/androidx/privacysandbox/tools/apicompiler/FullFeaturedSdkTest.kt
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * Copyright 2022 The Android Open Source Project
- *
- * 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 androidx.privacysandbox.tools.apicompiler
-
-import androidx.privacysandbox.tools.testing.CompilationTestHelper.assertThat
-import androidx.privacysandbox.tools.testing.loadSourcesFromDirectory
-import java.io.File
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
-
-/** Test the Privacy Sandbox API Compiler with an SDK that uses all available features. */
-@RunWith(JUnit4::class)
-class FullFeaturedSdkTest {
-    @Test
-    fun compileServiceInterface_ok() {
-        val inputTestDataDir = File("src/test/test-data/fullfeaturedsdk/input")
-        val outputTestDataDir = File("src/test/test-data/fullfeaturedsdk/output")
-        val inputSources = loadSourcesFromDirectory(inputTestDataDir)
-        val expectedKotlinSources = loadSourcesFromDirectory(outputTestDataDir)
-
-        val result = compileWithPrivacySandboxKspCompiler(inputSources)
-        assertThat(result).succeeds()
-
-        val expectedAidlFilepath = listOf(
-            "com/mysdk/ICancellationSignal.java",
-            "com/mysdk/IMyCallback.java",
-            "com/mysdk/IMyInterface.java",
-            "com/mysdk/IMyInterfaceTransactionCallback.java",
-            "com/mysdk/IMySdk.java",
-            "com/mysdk/IMySecondInterface.java",
-            "com/mysdk/IMyUiInterface.java",
-            "com/mysdk/IMyUiInterfaceCoreLibInfoAndBinderWrapper.java",
-            "com/mysdk/IMyUiInterfaceTransactionCallback.java",
-            "com/mysdk/IMySecondInterfaceTransactionCallback.java",
-            "com/mysdk/IResponseTransactionCallback.java",
-            "com/mysdk/IStringTransactionCallback.java",
-            "com/mysdk/IUnitTransactionCallback.java",
-            "com/mysdk/IListResponseTransactionCallback.java",
-            "com/mysdk/IListIntTransactionCallback.java",
-            "com/mysdk/IListLongTransactionCallback.java",
-            "com/mysdk/IListDoubleTransactionCallback.java",
-            "com/mysdk/IListStringTransactionCallback.java",
-            "com/mysdk/IListBooleanTransactionCallback.java",
-            "com/mysdk/IListFloatTransactionCallback.java",
-            "com/mysdk/IListCharTransactionCallback.java",
-            "com/mysdk/IListShortTransactionCallback.java",
-            "com/mysdk/ParcelableRequest.java",
-            "com/mysdk/ParcelableResponse.java",
-            "com/mysdk/ParcelableStackFrame.java",
-            "com/mysdk/ParcelableInnerValue.java",
-            "com/mysdk/PrivacySandboxThrowableParcel.java",
-        )
-        assertThat(result).hasAllExpectedGeneratedSourceFilesAndContent(
-            expectedKotlinSources,
-            expectedAidlFilepath
-        )
-    }
-}
\ No newline at end of file
diff --git a/privacysandbox/tools/tools-apicompiler/src/test/java/androidx/privacysandbox/tools/apicompiler/SdkWithPackagesApiCompilerDiffTest.kt b/privacysandbox/tools/tools-apicompiler/src/test/java/androidx/privacysandbox/tools/apicompiler/SdkWithPackagesApiCompilerDiffTest.kt
new file mode 100644
index 0000000..d9558f0
--- /dev/null
+++ b/privacysandbox/tools/tools-apicompiler/src/test/java/androidx/privacysandbox/tools/apicompiler/SdkWithPackagesApiCompilerDiffTest.kt
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * 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 androidx.privacysandbox.tools.apicompiler
+
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+/**
+ *  Test the Privacy Sandbox API Compiler with an SDK that defines an interface in another package.
+ */
+@RunWith(JUnit4::class)
+class SdkWithPackagesApiCompilerDiffTest : AbstractApiCompilerDiffTest() {
+    override val subdirectoryName = "sdkwithpackages"
+    override val relativePathsToExpectedAidlClasses = listOf(
+        "com/myotherpackage/IMyOtherPackageInterface.java",
+        "com/myotherpackage/ParcelableMyOtherPackageDataClass.java",
+        "com/mysdk/ICancellationSignal.java",
+        "com/mysdk/IListIntTransactionCallback.java",
+        "com/mysdk/IMyOtherPackageDataClassTransactionCallback.java",
+        "com/mysdk/IUnitTransactionCallback.java",
+        "com/mysdk/IMyOtherPackageInterfaceTransactionCallback.java",
+        "com/mysdk/IMySdk.java",
+        "com/mysdk/ParcelableStackFrame.java",
+        "com/mysdk/IStringTransactionCallback.java",
+        "com/mysdk/IMyMainPackageInterfaceTransactionCallback.java",
+        "com/mysdk/IMyMainPackageInterface.java",
+        "com/mysdk/PrivacySandboxThrowableParcel.java"
+    )
+}
\ No newline at end of file
diff --git a/privacysandbox/tools/tools-apicompiler/src/test/java/androidx/privacysandbox/tools/apicompiler/SdkWithPackagesTest.kt b/privacysandbox/tools/tools-apicompiler/src/test/java/androidx/privacysandbox/tools/apicompiler/SdkWithPackagesTest.kt
deleted file mode 100644
index 7928c89..0000000
--- a/privacysandbox/tools/tools-apicompiler/src/test/java/androidx/privacysandbox/tools/apicompiler/SdkWithPackagesTest.kt
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Copyright 2022 The Android Open Source Project
- *
- * 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 androidx.privacysandbox.tools.apicompiler
-
-import androidx.privacysandbox.tools.testing.CompilationTestHelper.assertThat
-import androidx.privacysandbox.tools.testing.loadSourcesFromDirectory
-import java.io.File
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
-
-/**
- *  Test the Privacy Sandbox API Compiler with an SDK that defines an interface in another package.
- */
-@RunWith(JUnit4::class)
-class SdkWithPackagesTest {
-    @Test
-    fun compileServiceInterface_ok() {
-        val inputTestDataDir = File("src/test/test-data/sdkwithpackages/input")
-        val outputTestDataDir = File("src/test/test-data/sdkwithpackages/output")
-        val inputSources = loadSourcesFromDirectory(inputTestDataDir)
-        val expectedKotlinSources = loadSourcesFromDirectory(outputTestDataDir)
-
-        val result = compileWithPrivacySandboxKspCompiler(inputSources)
-        assertThat(result).succeeds()
-
-        val expectedAidlFilepath = listOf(
-            "com/myotherpackage/IMyOtherPackageInterface.java",
-            "com/myotherpackage/ParcelableMyOtherPackageDataClass.java",
-            "com/mysdk/ICancellationSignal.java",
-            "com/mysdk/IListIntTransactionCallback.java",
-            "com/mysdk/IMyOtherPackageDataClassTransactionCallback.java",
-            "com/mysdk/IUnitTransactionCallback.java",
-            "com/mysdk/IMyOtherPackageInterfaceTransactionCallback.java",
-            "com/mysdk/IMySdk.java",
-            "com/mysdk/ParcelableStackFrame.java",
-            "com/mysdk/IStringTransactionCallback.java",
-            "com/mysdk/IMyMainPackageInterfaceTransactionCallback.java",
-            "com/mysdk/IMyMainPackageInterface.java",
-            "com/mysdk/PrivacySandboxThrowableParcel.java"
-        )
-        assertThat(result).hasAllExpectedGeneratedSourceFilesAndContent(
-            expectedKotlinSources,
-            expectedAidlFilepath
-        )
-    }
-}
\ No newline at end of file
diff --git a/privacysandbox/tools/tools-apicompiler/src/test/java/androidx/privacysandbox/tools/apicompiler/TestUtils.kt b/privacysandbox/tools/tools-apicompiler/src/test/java/androidx/privacysandbox/tools/apicompiler/TestUtils.kt
index 553be79..e57803a 100644
--- a/privacysandbox/tools/tools-apicompiler/src/test/java/androidx/privacysandbox/tools/apicompiler/TestUtils.kt
+++ b/privacysandbox/tools/tools-apicompiler/src/test/java/androidx/privacysandbox/tools/apicompiler/TestUtils.kt
@@ -17,6 +17,7 @@
 package androidx.privacysandbox.tools.apicompiler
 
 import androidx.privacysandbox.tools.testing.CompilationTestHelper
+import androidx.privacysandbox.tools.testing.TestEnvironment
 import androidx.room.compiler.processing.util.Source
 import androidx.room.compiler.processing.util.compiler.TestCompilationResult
 
@@ -33,13 +34,8 @@
     val provider = PrivacySandboxKspCompiler.Provider()
 
     val processorOptions = buildMap {
-        val aidlCompilerPath = (System.getProperty("aidl_compiler_path")
-            ?: throw IllegalArgumentException("aidl_compiler_path flag not set."))
-        put("aidl_compiler_path", aidlCompilerPath)
-        val frameworkAidlPath = (System.getProperty("framework_aidl_path")
-            ?: throw IllegalArgumentException("framework_aidl_path flag not set."))
-        put("aidl_compiler_path", aidlCompilerPath)
-        put("framework_aidl_path", frameworkAidlPath)
+        put("aidl_compiler_path", TestEnvironment.aidlCompilerPath.toString())
+        put("framework_aidl_path", TestEnvironment.frameworkAidlPath.toString())
         putAll(extraProcessorOptions)
     }
 
diff --git a/privacysandbox/tools/tools-apicompiler/src/test/java/androidx/privacysandbox/tools/apicompiler/WithoutRuntimeLibrarySdkApiCompilerDiffTest.kt b/privacysandbox/tools/tools-apicompiler/src/test/java/androidx/privacysandbox/tools/apicompiler/WithoutRuntimeLibrarySdkApiCompilerDiffTest.kt
new file mode 100644
index 0000000..8fcee4c
--- /dev/null
+++ b/privacysandbox/tools/tools-apicompiler/src/test/java/androidx/privacysandbox/tools/apicompiler/WithoutRuntimeLibrarySdkApiCompilerDiffTest.kt
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * 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 androidx.privacysandbox.tools.apicompiler
+
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@RunWith(JUnit4::class)
+class WithoutRuntimeLibrarySdkApiCompilerDiffTest : AbstractApiCompilerDiffTest() {
+    override val subdirectoryName = "withoutruntimelibrarysdk"
+    override val extraProcessorOptions = mapOf("skip_sdk_runtime_compat_library" to "true")
+    override val relativePathsToExpectedAidlClasses = listOf(
+        "com/mysdk/ICancellationSignal.java",
+        "com/mysdk/IWithoutRuntimeLibrarySdk.java",
+        "com/mysdk/IStringTransactionCallback.java",
+        "com/mysdk/ParcelableStackFrame.java",
+        "com/mysdk/PrivacySandboxThrowableParcel.java",
+    )
+}
\ No newline at end of file
diff --git a/privacysandbox/tools/tools-apicompiler/src/test/java/androidx/privacysandbox/tools/apicompiler/WithoutRuntimeLibrarySdkTest.kt b/privacysandbox/tools/tools-apicompiler/src/test/java/androidx/privacysandbox/tools/apicompiler/WithoutRuntimeLibrarySdkTest.kt
deleted file mode 100644
index fa4ec0d..0000000
--- a/privacysandbox/tools/tools-apicompiler/src/test/java/androidx/privacysandbox/tools/apicompiler/WithoutRuntimeLibrarySdkTest.kt
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright 2022 The Android Open Source Project
- *
- * 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 androidx.privacysandbox.tools.apicompiler
-
-import androidx.privacysandbox.tools.testing.CompilationTestHelper.assertThat
-import androidx.privacysandbox.tools.testing.loadSourcesFromDirectory
-import java.io.File
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
-
-@RunWith(JUnit4::class)
-class WithoutRuntimeLibrarySdkTest {
-    @Test
-    fun compileServiceInterface_ok() {
-        val inputTestDataDir = File("src/test/test-data/withoutruntimelibrarysdk/input")
-        val outputTestDataDir = File("src/test/test-data/withoutruntimelibrarysdk/output")
-        val inputSources = loadSourcesFromDirectory(inputTestDataDir)
-        val expectedKotlinSources = loadSourcesFromDirectory(outputTestDataDir)
-
-        val result = compileWithPrivacySandboxKspCompiler(
-            inputSources,
-            extraProcessorOptions = mapOf("skip_sdk_runtime_compat_library" to "true")
-        )
-        assertThat(result).succeeds()
-
-        val expectedAidlFilepath = listOf(
-            "com/mysdk/ICancellationSignal.java",
-            "com/mysdk/IWithoutRuntimeLibrarySdk.java",
-            "com/mysdk/IStringTransactionCallback.java",
-            "com/mysdk/ParcelableStackFrame.java",
-            "com/mysdk/PrivacySandboxThrowableParcel.java",
-        )
-        assertThat(result).hasAllExpectedGeneratedSourceFilesAndContent(
-            expectedKotlinSources,
-            expectedAidlFilepath
-        )
-    }
-}
\ No newline at end of file
diff --git a/privacysandbox/tools/tools-apigenerator/build.gradle b/privacysandbox/tools/tools-apigenerator/build.gradle
index afe4b88..5ddde21 100644
--- a/privacysandbox/tools/tools-apigenerator/build.gradle
+++ b/privacysandbox/tools/tools-apigenerator/build.gradle
@@ -52,20 +52,24 @@
             dir: "${SdkHelperKt.getSdkPath(project)}/platforms/$SupportConfig.COMPILE_SDK_VERSION/",
             include: "android.jar"
     ))
-    // Get AIDL compiler path and framework.aidl path and pass to tests for code generation.
-    def aidlCompilerPath = "${SdkHelperKt.getSdkPath(project)}/build-tools/${SupportConfig.buildToolsVersion(project)}/aidl"
-    def frameworkAidlPath = "${SdkHelperKt.getSdkPath(project)}/platforms/${SupportConfig.COMPILE_SDK_VERSION}/framework.aidl"
-    test {
-        inputs.files(aidlCompilerPath)
-                .withPropertyName("aidl_compiler_path")
-                .withPathSensitivity(PathSensitivity.NAME_ONLY)
-        inputs.files(frameworkAidlPath)
-                .withPropertyName("framework_aidl_path")
-                .withPathSensitivity(PathSensitivity.NAME_ONLY)
-        doFirst {
-            systemProperty "aidl_compiler_path", aidlCompilerPath
-            systemProperty "framework_aidl_path", frameworkAidlPath
-        }
+}
+
+// Get AIDL compiler path and framework.aidl path and pass to tests for code generation.
+def aidlCompilerPath = "${SdkHelperKt.getSdkPath(project)}/build-tools/${SupportConfig.buildToolsVersion(project)}/aidl"
+def frameworkAidlPath = "${SdkHelperKt.getSdkPath(project)}/platforms/${SupportConfig.COMPILE_SDK_VERSION}/framework.aidl"
+def testGeneratedSourcesPath = "${project.buildDir}/testGeneratedSources"
+test {
+    inputs.files(aidlCompilerPath)
+            .withPropertyName("aidl_compiler_path")
+            .withPathSensitivity(PathSensitivity.NAME_ONLY)
+    inputs.files(frameworkAidlPath)
+            .withPropertyName("framework_aidl_path")
+            .withPathSensitivity(PathSensitivity.NAME_ONLY)
+    inputs.dir("src/test/test-data").withPathSensitivity(PathSensitivity.RELATIVE)
+    doFirst {
+        systemProperty "aidl_compiler_path", aidlCompilerPath
+        systemProperty "framework_aidl_path", frameworkAidlPath
+        systemProperty "test_output_dir", testGeneratedSourcesPath
     }
 }
 
diff --git a/privacysandbox/tools/tools-apigenerator/src/test/java/androidx/privacysandbox/tools/apigenerator/AbstractApiGeneratorDiffTest.kt b/privacysandbox/tools/tools-apigenerator/src/test/java/androidx/privacysandbox/tools/apigenerator/AbstractApiGeneratorDiffTest.kt
new file mode 100644
index 0000000..feb81d0
--- /dev/null
+++ b/privacysandbox/tools/tools-apigenerator/src/test/java/androidx/privacysandbox/tools/apigenerator/AbstractApiGeneratorDiffTest.kt
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * 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 androidx.privacysandbox.tools.apigenerator
+
+import androidx.privacysandbox.tools.core.Metadata
+import androidx.privacysandbox.tools.testing.AbstractDiffTest
+import androidx.privacysandbox.tools.testing.CompilationTestHelper.assertCompiles
+import androidx.privacysandbox.tools.testing.TestEnvironment
+import androidx.privacysandbox.tools.testing.loadSourcesFromDirectory
+import androidx.room.compiler.processing.util.Source
+import java.nio.file.Path
+import org.junit.Test
+
+/**
+ * Base test for API Compiler diff test. It calls the API Packager to generate true-to-production
+ * SDK API descriptors, invokes the generator and compiles the generated sources.
+ */
+abstract class AbstractApiGeneratorDiffTest : AbstractDiffTest() {
+
+    override fun generateSources(
+        inputSources: List<Source>,
+        outputDirectory: Path
+    ): List<Source> {
+        val descriptors =
+            compileIntoInterfaceDescriptorsJar(
+                inputSources,
+                mapOf(Metadata.filePath to Metadata.toolMetadata.toByteArray())
+            )
+        val generator = PrivacySandboxApiGenerator()
+        generator.generate(
+            descriptors,
+            TestEnvironment.aidlCompilerPath, TestEnvironment.frameworkAidlPath, outputDirectory)
+        return loadSourcesFromDirectory(outputDirectory.toFile())
+    }
+
+    @Test
+    fun generatedSourcesCompile() {
+        assertCompiles(generatedSources)
+    }
+}
\ No newline at end of file
diff --git a/privacysandbox/tools/tools-apigenerator/src/test/java/androidx/privacysandbox/tools/apigenerator/BaseApiGeneratorTest.kt b/privacysandbox/tools/tools-apigenerator/src/test/java/androidx/privacysandbox/tools/apigenerator/BaseApiGeneratorTest.kt
deleted file mode 100644
index 349bc7f..0000000
--- a/privacysandbox/tools/tools-apigenerator/src/test/java/androidx/privacysandbox/tools/apigenerator/BaseApiGeneratorTest.kt
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright 2022 The Android Open Source Project
- *
- * 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 androidx.privacysandbox.tools.apigenerator
-
-import androidx.privacysandbox.tools.core.Metadata
-import androidx.privacysandbox.tools.testing.CompilationTestHelper.assertCompiles
-import androidx.privacysandbox.tools.testing.hasAllExpectedGeneratedSourceFilesAndContent
-import androidx.privacysandbox.tools.testing.loadSourcesFromDirectory
-import androidx.room.compiler.processing.util.Source
-import java.io.File
-import java.nio.file.Files
-import kotlin.io.path.Path
-import org.junit.Test
-
-abstract class BaseApiGeneratorTest {
-    abstract val inputDirectory: File
-    abstract val outputDirectory: File
-    abstract val relativePathsToExpectedAidlClasses: List<String>
-
-    private val generatedSources: List<Source> by lazy {
-        val descriptors =
-            compileIntoInterfaceDescriptorsJar(
-                loadSourcesFromDirectory(inputDirectory),
-                mapOf(Metadata.filePath to Metadata.toolMetadata.toByteArray())
-            )
-        val aidlCompilerPath = System.getProperty("aidl_compiler_path")?.let(::Path)
-            ?: throw IllegalArgumentException("aidl_compiler_path flag not set.")
-        val frameworkAidlPath = System.getProperty("framework_aidl_path")?.let(::Path)
-            ?: throw IllegalArgumentException("framework_aidl_path flag not set.")
-
-        val generator = PrivacySandboxApiGenerator()
-
-        val outputDir = Files.createTempDirectory("output").also { it.toFile().deleteOnExit() }
-        generator.generate(descriptors, aidlCompilerPath, frameworkAidlPath, outputDir)
-        loadSourcesFromDirectory(outputDir.toFile())
-    }
-
-    @Test
-    fun generatedApi_compiles() {
-        assertCompiles(generatedSources)
-    }
-
-    @Test
-    fun generatedApi_hasExpectedContents() {
-        val expectedKotlinSources = loadSourcesFromDirectory(outputDirectory)
-        hasAllExpectedGeneratedSourceFilesAndContent(
-            generatedSources,
-            expectedKotlinSources,
-            relativePathsToExpectedAidlClasses
-        )
-    }
-}
\ No newline at end of file
diff --git a/privacysandbox/tools/tools-apigenerator/src/test/java/androidx/privacysandbox/tools/apigenerator/CallbacksApiGeneratorTest.kt b/privacysandbox/tools/tools-apigenerator/src/test/java/androidx/privacysandbox/tools/apigenerator/CallbacksApiGeneratorDiffTest.kt
similarity index 81%
rename from privacysandbox/tools/tools-apigenerator/src/test/java/androidx/privacysandbox/tools/apigenerator/CallbacksApiGeneratorTest.kt
rename to privacysandbox/tools/tools-apigenerator/src/test/java/androidx/privacysandbox/tools/apigenerator/CallbacksApiGeneratorDiffTest.kt
index 03bc25d..2c60fa7 100644
--- a/privacysandbox/tools/tools-apigenerator/src/test/java/androidx/privacysandbox/tools/apigenerator/CallbacksApiGeneratorTest.kt
+++ b/privacysandbox/tools/tools-apigenerator/src/test/java/androidx/privacysandbox/tools/apigenerator/CallbacksApiGeneratorDiffTest.kt
@@ -16,14 +16,12 @@
 
 package androidx.privacysandbox.tools.apigenerator
 
-import java.io.File
 import org.junit.runner.RunWith
 import org.junit.runners.JUnit4
 
 @RunWith(JUnit4::class)
-class CallbacksApiGeneratorTest : BaseApiGeneratorTest() {
-    override val inputDirectory = File("src/test/test-data/callbacks/input")
-    override val outputDirectory = File("src/test/test-data/callbacks/output")
+class CallbacksApiGeneratorDiffTest : AbstractApiGeneratorDiffTest() {
+    override val subdirectoryName = "callbacks"
     override val relativePathsToExpectedAidlClasses = listOf(
         "com/sdkwithcallbacks/IMyInterface.java",
         "com/sdkwithcallbacks/ISdkCallback.java",
diff --git a/privacysandbox/tools/tools-apigenerator/src/test/java/androidx/privacysandbox/tools/apigenerator/InterfaceApiGeneratorTest.kt b/privacysandbox/tools/tools-apigenerator/src/test/java/androidx/privacysandbox/tools/apigenerator/InterfaceApiGeneratorDiffTest.kt
similarity index 84%
rename from privacysandbox/tools/tools-apigenerator/src/test/java/androidx/privacysandbox/tools/apigenerator/InterfaceApiGeneratorTest.kt
rename to privacysandbox/tools/tools-apigenerator/src/test/java/androidx/privacysandbox/tools/apigenerator/InterfaceApiGeneratorDiffTest.kt
index 29664ab..e77fda56 100644
--- a/privacysandbox/tools/tools-apigenerator/src/test/java/androidx/privacysandbox/tools/apigenerator/InterfaceApiGeneratorTest.kt
+++ b/privacysandbox/tools/tools-apigenerator/src/test/java/androidx/privacysandbox/tools/apigenerator/InterfaceApiGeneratorDiffTest.kt
@@ -16,14 +16,12 @@
 
 package androidx.privacysandbox.tools.apigenerator
 
-import java.io.File
 import org.junit.runner.RunWith
 import org.junit.runners.JUnit4
 
 @RunWith(JUnit4::class)
-class InterfaceApiGeneratorTest : BaseApiGeneratorTest() {
-    override val inputDirectory = File("src/test/test-data/interfaces/input")
-    override val outputDirectory = File("src/test/test-data/interfaces/output")
+class InterfaceApiGeneratorDiffTest : AbstractApiGeneratorDiffTest() {
+    override val subdirectoryName = "interfaces"
     override val relativePathsToExpectedAidlClasses = listOf(
         "com/sdk/IMySdk.java",
         "com/sdk/IMyInterface.java",
diff --git a/privacysandbox/tools/tools-apigenerator/src/test/java/androidx/privacysandbox/tools/apigenerator/PrimitivesApiGeneratorTest.kt b/privacysandbox/tools/tools-apigenerator/src/test/java/androidx/privacysandbox/tools/apigenerator/PrimitivesApiGeneratorDiffTest.kt
similarity index 86%
rename from privacysandbox/tools/tools-apigenerator/src/test/java/androidx/privacysandbox/tools/apigenerator/PrimitivesApiGeneratorTest.kt
rename to privacysandbox/tools/tools-apigenerator/src/test/java/androidx/privacysandbox/tools/apigenerator/PrimitivesApiGeneratorDiffTest.kt
index 7437b18..bf87924 100644
--- a/privacysandbox/tools/tools-apigenerator/src/test/java/androidx/privacysandbox/tools/apigenerator/PrimitivesApiGeneratorTest.kt
+++ b/privacysandbox/tools/tools-apigenerator/src/test/java/androidx/privacysandbox/tools/apigenerator/PrimitivesApiGeneratorDiffTest.kt
@@ -16,14 +16,12 @@
 
 package androidx.privacysandbox.tools.apigenerator
 
-import java.io.File
 import org.junit.runner.RunWith
 import org.junit.runners.JUnit4
 
 @RunWith(JUnit4::class)
-class PrimitivesApiGeneratorTest : BaseApiGeneratorTest() {
-    override val inputDirectory = File("src/test/test-data/primitives/input")
-    override val outputDirectory = File("src/test/test-data/primitives/output")
+class PrimitivesApiGeneratorDiffTest : AbstractApiGeneratorDiffTest() {
+    override val subdirectoryName = "primitives"
     override val relativePathsToExpectedAidlClasses = listOf(
         "com/mysdk/ITestSandboxSdk.java",
         "com/mysdk/IBooleanTransactionCallback.java",
diff --git a/privacysandbox/tools/tools-apigenerator/src/test/java/androidx/privacysandbox/tools/apigenerator/ValuesApiGeneratorTest.kt b/privacysandbox/tools/tools-apigenerator/src/test/java/androidx/privacysandbox/tools/apigenerator/ValuesApiGeneratorDiffTest.kt
similarity index 85%
rename from privacysandbox/tools/tools-apigenerator/src/test/java/androidx/privacysandbox/tools/apigenerator/ValuesApiGeneratorTest.kt
rename to privacysandbox/tools/tools-apigenerator/src/test/java/androidx/privacysandbox/tools/apigenerator/ValuesApiGeneratorDiffTest.kt
index c729c44..526e75b 100644
--- a/privacysandbox/tools/tools-apigenerator/src/test/java/androidx/privacysandbox/tools/apigenerator/ValuesApiGeneratorTest.kt
+++ b/privacysandbox/tools/tools-apigenerator/src/test/java/androidx/privacysandbox/tools/apigenerator/ValuesApiGeneratorDiffTest.kt
@@ -16,14 +16,12 @@
 
 package androidx.privacysandbox.tools.apigenerator
 
-import java.io.File
 import org.junit.runner.RunWith
 import org.junit.runners.JUnit4
 
 @RunWith(JUnit4::class)
-class ValuesApiGeneratorTest : BaseApiGeneratorTest() {
-    override val inputDirectory = File("src/test/test-data/values/input")
-    override val outputDirectory = File("src/test/test-data/values/output")
+class ValuesApiGeneratorDiffTest : AbstractApiGeneratorDiffTest() {
+    override val subdirectoryName = "values"
     override val relativePathsToExpectedAidlClasses = listOf(
         "com/sdkwithvalues/IMyInterface.java",
         "com/sdkwithvalues/ISdkInterface.java",
diff --git a/privacysandbox/tools/tools-core/build.gradle b/privacysandbox/tools/tools-core/build.gradle
index 7f2ce6b..9e56006 100644
--- a/privacysandbox/tools/tools-core/build.gradle
+++ b/privacysandbox/tools/tools-core/build.gradle
@@ -42,20 +42,22 @@
             dir: "${SdkHelperKt.getSdkPath(project)}/platforms/$SupportConfig.COMPILE_SDK_VERSION/",
             include: "android.jar"
     ))
-    // Get AIDL compiler path and framework.aidl path and pass to tests for code generation.
-    def aidlCompilerPath = "${SdkHelperKt.getSdkPath(project)}/build-tools/${SupportConfig.buildToolsVersion(project)}/aidl"
-    def frameworkAidlPath = "${SdkHelperKt.getSdkPath(project)}/platforms/${SupportConfig.COMPILE_SDK_VERSION}/framework.aidl"
-    test {
-        inputs.files(aidlCompilerPath)
-                .withPropertyName("aidl_compiler_path")
-                .withPathSensitivity(PathSensitivity.NAME_ONLY)
-        inputs.files(frameworkAidlPath)
-                .withPropertyName("framework_aidl_path")
-                .withPathSensitivity(PathSensitivity.NAME_ONLY)
-        doFirst {
-            systemProperty "aidl_compiler_path", aidlCompilerPath
-            systemProperty "framework_aidl_path", frameworkAidlPath
-        }
+}
+
+// Get AIDL compiler path and framework.aidl path and pass to tests for code generation.
+def aidlCompilerPath = "${SdkHelperKt.getSdkPath(project)}/build-tools/${SupportConfig.buildToolsVersion(project)}/aidl"
+def frameworkAidlPath = "${SdkHelperKt.getSdkPath(project)}/platforms/${SupportConfig.COMPILE_SDK_VERSION}/framework.aidl"
+test {
+    inputs.files(aidlCompilerPath)
+            .withPropertyName("aidl_compiler_path")
+            .withPathSensitivity(PathSensitivity.NAME_ONLY)
+    inputs.files(frameworkAidlPath)
+            .withPropertyName("framework_aidl_path")
+            .withPathSensitivity(PathSensitivity.NAME_ONLY)
+    inputs.dir("src/test/test-data").withPathSensitivity(PathSensitivity.RELATIVE)
+    doFirst {
+        systemProperty "aidl_compiler_path", aidlCompilerPath
+        systemProperty "framework_aidl_path", frameworkAidlPath
     }
 }
 
diff --git a/privacysandbox/tools/tools-testing/src/main/java/androidx/privacysandbox/tools/testing/AbstractDiffTest.kt b/privacysandbox/tools/tools-testing/src/main/java/androidx/privacysandbox/tools/testing/AbstractDiffTest.kt
new file mode 100644
index 0000000..bd362a5
--- /dev/null
+++ b/privacysandbox/tools/tools-testing/src/main/java/androidx/privacysandbox/tools/testing/AbstractDiffTest.kt
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * 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 androidx.privacysandbox.tools.testing
+
+import androidx.room.compiler.processing.util.Source
+import com.google.common.truth.Truth
+import java.io.File
+import java.nio.file.Path
+import kotlin.io.path.Path
+import org.junit.Test
+
+/** Base test class for diff testing Privacy Sandbox tool output. */
+abstract class AbstractDiffTest {
+    /** Name for the subdirectory used to read input and expected sources. */
+    abstract val subdirectoryName: String
+
+    /**
+     * List of relative paths to expected AIDL files. We will assert that they are present in the
+     * final output but we won't check their contents.
+     */
+    abstract val relativePathsToExpectedAidlClasses: List<String>
+
+    /**
+     * Generates the sources and stores them in the given [outputDirectory].
+     * @param inputSources List of input sources read from the test-data directory with
+     * [subdirectoryName].
+     */
+    abstract fun generateSources(
+        inputSources: List<Source>,
+        outputDirectory: Path,
+    ): List<Source>
+
+    protected val generatedSources: List<Source> by lazy {
+        val inputSources =
+            loadSourcesFromDirectory(File("src/test/test-data/$subdirectoryName/input"))
+        outputDir.toFile().also {
+            if (it.exists()) {
+                it.deleteRecursively()
+            }
+            it.mkdirs()
+        }
+        generateSources(inputSources, outputDir)
+    }
+
+    @Test
+    fun generatedSourcesHaveExpectedContents() {
+        val expectedKotlinSources =
+            loadSourcesFromDirectory(File("src/test/test-data/$subdirectoryName/output"))
+
+        val expectedRelativePaths =
+            expectedKotlinSources.map(Source::relativePath) + relativePathsToExpectedAidlClasses
+        Truth.assertThat(generatedSources.map(Source::relativePath))
+            .containsExactlyElementsIn(expectedRelativePaths)
+
+        val actualRelativePathMap = generatedSources.associateBy(Source::relativePath)
+        for (expectedKotlinSource in expectedKotlinSources) {
+            Truth.assertWithMessage(
+                "Contents of generated file ${expectedKotlinSource.relativePath} don't " +
+                    "match golden. Here's the path to generated sources: $outputDir"
+            ).that(actualRelativePathMap[expectedKotlinSource.relativePath]?.contents)
+                .isEqualTo(expectedKotlinSource.contents)
+        }
+    }
+
+    private val outputDir: Path by lazy {
+        requireNotNull(System.getProperty("test_output_dir")) {
+            "test_output_dir not set for diff test."
+        }.let { Path(it).resolve(subdirectoryName) }
+    }
+}
\ No newline at end of file
diff --git a/privacysandbox/tools/tools-testing/src/main/java/androidx/privacysandbox/tools/testing/TestEnvironment.kt b/privacysandbox/tools/tools-testing/src/main/java/androidx/privacysandbox/tools/testing/TestEnvironment.kt
new file mode 100644
index 0000000..2a65ddd
--- /dev/null
+++ b/privacysandbox/tools/tools-testing/src/main/java/androidx/privacysandbox/tools/testing/TestEnvironment.kt
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * 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 androidx.privacysandbox.tools.testing
+
+import kotlin.io.path.Path
+
+object TestEnvironment {
+    val aidlCompilerPath = requireNotNull(System.getProperty("aidl_compiler_path")) {
+        "aidl_compiler_path flag not set"
+    }.let(::Path)
+
+    val frameworkAidlPath = requireNotNull(System.getProperty("framework_aidl_path")) {
+        "framework_aidl_path flag not set."
+    }.let(::Path)
+}
\ No newline at end of file