Add golden tests for systemfeatures codegen

Add a simple golden test suite for systemfeature codegen. This can be
run via atest, or as a standalone script. The golden sources can be
updated via:

  ./frameworks/base/tools/systemfeatures/tests/golden_test.sh --update

Test: atest systemfeatures-gen-golden-tests
Bug: 203143243
Flag: NA

Change-Id: Iacb9558617845132ccfffbae34c333d39c84c065
diff --git a/tools/systemfeatures/Android.bp b/tools/systemfeatures/Android.bp
index aca25eb..a9e6328 100644
--- a/tools/systemfeatures/Android.bp
+++ b/tools/systemfeatures/Android.bp
@@ -5,6 +5,7 @@
     // to get the below license kinds:
     //   SPDX-license-identifier-Apache-2.0
     default_applicable_licenses: ["frameworks_base_license"],
+    default_team: "trendy_team_system_performance",
 }
 
 java_library_host {
@@ -25,8 +26,6 @@
     static_libs: ["systemfeatures-gen-lib"],
 }
 
-// TODO(b/203143243): Add golden diff test for generated sources.
-// Functional runtime behavior is covered in systemfeatures-gen-tests.
 genrule {
     name: "systemfeatures-gen-tests-srcs",
     cmd: "$(location systemfeatures-gen-tool) com.android.systemfeatures.RwNoFeatures --readonly=false > $(location RwNoFeatures.java) && " +
@@ -42,11 +41,12 @@
     tools: ["systemfeatures-gen-tool"],
 }
 
+// Functional runtime behavior testing.
 java_test_host {
     name: "systemfeatures-gen-tests",
     test_suites: ["general-tests"],
     srcs: [
-        "tests/**/*.java",
+        "tests/src/**/*.java",
         ":systemfeatures-gen-tests-srcs",
     ],
     test_options: {
@@ -61,3 +61,33 @@
         "truth",
     ],
 }
+
+// Rename the goldens as they may be copied into the source tree, and we don't
+// need or want the usual `.java` linting (e.g., copyright checks).
+genrule {
+    name: "systemfeatures-gen-tests-golden-srcs",
+    cmd: "for f in $(in); do cp $$f $(genDir)/tests/gen/$$(basename $$f).gen; done",
+    srcs: [":systemfeatures-gen-tests-srcs"],
+    out: [
+        "tests/gen/RwNoFeatures.java.gen",
+        "tests/gen/RoNoFeatures.java.gen",
+        "tests/gen/RwFeatures.java.gen",
+        "tests/gen/RoFeatures.java.gen",
+    ],
+}
+
+// Golden output testing. Golden sources can be updated via:
+//   $ANDROID_BUILD_TOP/frameworks/base/tools/systemfeatures/tests/golden_test.sh --update
+sh_test_host {
+    name: "systemfeatures-gen-golden-tests",
+    src: "tests/golden_test.sh",
+    filename: "systemfeatures-gen-golden-tests.sh",
+    test_config: "tests/systemfeatures-gen-golden-tests.xml",
+    data: [
+        "tests/golden/**/*.java*",
+        ":systemfeatures-gen-tests-golden-srcs",
+    ],
+    test_options: {
+        unit_test: true,
+    },
+}
diff --git a/tools/systemfeatures/src/com/android/systemfeatures/SystemFeaturesGenerator.kt b/tools/systemfeatures/src/com/android/systemfeatures/SystemFeaturesGenerator.kt
index e537ffc..5df453d 100644
--- a/tools/systemfeatures/src/com/android/systemfeatures/SystemFeaturesGenerator.kt
+++ b/tools/systemfeatures/src/com/android/systemfeatures/SystemFeaturesGenerator.kt
@@ -142,6 +142,10 @@
 
         // TODO(b/203143243): Add validation of build vs runtime values to ensure consistency.
         JavaFile.builder(outputClassName.packageName(), classBuilder.build())
+            .indent("    ")
+            .skipJavaLangImports(true)
+            .addFileComment("This file is auto-generated. DO NOT MODIFY.\n")
+            .addFileComment("Args: ${args.joinToString(" \\\n           ")}")
             .build()
             .writeTo(System.out)
     }
@@ -178,6 +182,7 @@
             val methodBuilder =
                 MethodSpec.methodBuilder(methodName)
                     .addModifiers(Modifier.PUBLIC, Modifier.STATIC)
+                    .addJavadoc("Check for ${feature.name}.\n\n@hide")
                     .returns(Boolean::class.java)
                     .addParameter(CONTEXT_CLASS, "context")
 
@@ -228,6 +233,7 @@
             MethodSpec.methodBuilder("maybeHasFeature")
                 .addModifiers(Modifier.PUBLIC, Modifier.STATIC)
                 .addAnnotation(ClassName.get("android.annotation", "Nullable"))
+                .addJavadoc("@hide")
                 .returns(Boolean::class.javaObjectType) // Use object type for nullability
                 .addParameter(String::class.java, "featureName")
                 .addParameter(Int::class.java, "version")
diff --git a/tools/systemfeatures/tests/golden/RoFeatures.java.gen b/tools/systemfeatures/tests/golden/RoFeatures.java.gen
new file mode 100644
index 0000000..724639b
--- /dev/null
+++ b/tools/systemfeatures/tests/golden/RoFeatures.java.gen
@@ -0,0 +1,88 @@
+// This file is auto-generated. DO NOT MODIFY.
+// Args: com.android.systemfeatures.RoFeatures \
+//            --readonly=true \
+//            --feature=WATCH:1 \
+//            --feature=WIFI:0 \
+//            --feature=VULKAN:-1 \
+//            --feature=AUTO: \
+//            --feature-apis=WATCH,PC
+package com.android.systemfeatures;
+
+import android.annotation.Nullable;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import com.android.aconfig.annotations.AssumeFalseForR8;
+import com.android.aconfig.annotations.AssumeTrueForR8;
+
+/**
+ * @hide
+ */
+public final class RoFeatures {
+    /**
+     * Check for FEATURE_WATCH.
+     *
+     * @hide
+     */
+    @AssumeTrueForR8
+    public static boolean hasFeatureWatch(Context context) {
+        return true;
+    }
+
+    /**
+     * Check for FEATURE_PC.
+     *
+     * @hide
+     */
+    public static boolean hasFeaturePc(Context context) {
+        return hasFeatureFallback(context, PackageManager.FEATURE_PC);
+    }
+
+    /**
+     * Check for FEATURE_WIFI.
+     *
+     * @hide
+     */
+    @AssumeTrueForR8
+    public static boolean hasFeatureWifi(Context context) {
+        return true;
+    }
+
+    /**
+     * Check for FEATURE_VULKAN.
+     *
+     * @hide
+     */
+    @AssumeFalseForR8
+    public static boolean hasFeatureVulkan(Context context) {
+        return false;
+    }
+
+    /**
+     * Check for FEATURE_AUTO.
+     *
+     * @hide
+     */
+    @AssumeFalseForR8
+    public static boolean hasFeatureAuto(Context context) {
+        return false;
+    }
+
+    private static boolean hasFeatureFallback(Context context, String featureName) {
+        return context.getPackageManager().hasSystemFeature(featureName, 0);
+    }
+
+    /**
+     * @hide
+     */
+    @Nullable
+    public static Boolean maybeHasFeature(String featureName, int version) {
+        switch (featureName) {
+            case PackageManager.FEATURE_WATCH: return 1 >= version;
+            case PackageManager.FEATURE_WIFI: return 0 >= version;
+            case PackageManager.FEATURE_VULKAN: return -1 >= version;
+            case PackageManager.FEATURE_AUTO: return false;
+            default: break;
+        }
+        return null;
+    }
+}
diff --git a/tools/systemfeatures/tests/golden/RoNoFeatures.java.gen b/tools/systemfeatures/tests/golden/RoNoFeatures.java.gen
new file mode 100644
index 0000000..59c5b4e
--- /dev/null
+++ b/tools/systemfeatures/tests/golden/RoNoFeatures.java.gen
@@ -0,0 +1,35 @@
+// This file is auto-generated. DO NOT MODIFY.
+// Args: com.android.systemfeatures.RoNoFeatures \
+//            --readonly=true \
+//            --feature-apis=WATCH
+package com.android.systemfeatures;
+
+import android.annotation.Nullable;
+import android.content.Context;
+import android.content.pm.PackageManager;
+
+/**
+ * @hide
+ */
+public final class RoNoFeatures {
+    /**
+     * Check for FEATURE_WATCH.
+     *
+     * @hide
+     */
+    public static boolean hasFeatureWatch(Context context) {
+        return hasFeatureFallback(context, PackageManager.FEATURE_WATCH);
+    }
+
+    private static boolean hasFeatureFallback(Context context, String featureName) {
+        return context.getPackageManager().hasSystemFeature(featureName, 0);
+    }
+
+    /**
+     * @hide
+     */
+    @Nullable
+    public static Boolean maybeHasFeature(String featureName, int version) {
+        return null;
+    }
+}
diff --git a/tools/systemfeatures/tests/golden/RwFeatures.java.gen b/tools/systemfeatures/tests/golden/RwFeatures.java.gen
new file mode 100644
index 0000000..6f89759
--- /dev/null
+++ b/tools/systemfeatures/tests/golden/RwFeatures.java.gen
@@ -0,0 +1,65 @@
+// This file is auto-generated. DO NOT MODIFY.
+// Args: com.android.systemfeatures.RwFeatures \
+//            --readonly=false \
+//            --feature=WATCH:1 \
+//            --feature=WIFI:0 \
+//            --feature=VULKAN:-1 \
+//            --feature=AUTO:
+package com.android.systemfeatures;
+
+import android.annotation.Nullable;
+import android.content.Context;
+import android.content.pm.PackageManager;
+
+/**
+ * @hide
+ */
+public final class RwFeatures {
+    /**
+     * Check for FEATURE_WATCH.
+     *
+     * @hide
+     */
+    public static boolean hasFeatureWatch(Context context) {
+        return hasFeatureFallback(context, PackageManager.FEATURE_WATCH);
+    }
+
+    /**
+     * Check for FEATURE_WIFI.
+     *
+     * @hide
+     */
+    public static boolean hasFeatureWifi(Context context) {
+        return hasFeatureFallback(context, PackageManager.FEATURE_WIFI);
+    }
+
+    /**
+     * Check for FEATURE_VULKAN.
+     *
+     * @hide
+     */
+    public static boolean hasFeatureVulkan(Context context) {
+        return hasFeatureFallback(context, PackageManager.FEATURE_VULKAN);
+    }
+
+    /**
+     * Check for FEATURE_AUTO.
+     *
+     * @hide
+     */
+    public static boolean hasFeatureAuto(Context context) {
+        return hasFeatureFallback(context, PackageManager.FEATURE_AUTO);
+    }
+
+    private static boolean hasFeatureFallback(Context context, String featureName) {
+        return context.getPackageManager().hasSystemFeature(featureName, 0);
+    }
+
+    /**
+     * @hide
+     */
+    @Nullable
+    public static Boolean maybeHasFeature(String featureName, int version) {
+        return null;
+    }
+}
diff --git a/tools/systemfeatures/tests/golden/RwNoFeatures.java.gen b/tools/systemfeatures/tests/golden/RwNoFeatures.java.gen
new file mode 100644
index 0000000..2111d56
--- /dev/null
+++ b/tools/systemfeatures/tests/golden/RwNoFeatures.java.gen
@@ -0,0 +1,24 @@
+// This file is auto-generated. DO NOT MODIFY.
+// Args: com.android.systemfeatures.RwNoFeatures \
+//            --readonly=false
+package com.android.systemfeatures;
+
+import android.annotation.Nullable;
+import android.content.Context;
+
+/**
+ * @hide
+ */
+public final class RwNoFeatures {
+    private static boolean hasFeatureFallback(Context context, String featureName) {
+        return context.getPackageManager().hasSystemFeature(featureName, 0);
+    }
+
+    /**
+     * @hide
+     */
+    @Nullable
+    public static Boolean maybeHasFeature(String featureName, int version) {
+        return null;
+    }
+}
diff --git a/tools/systemfeatures/tests/golden_test.sh b/tools/systemfeatures/tests/golden_test.sh
new file mode 100755
index 0000000..c249254
--- /dev/null
+++ b/tools/systemfeatures/tests/golden_test.sh
@@ -0,0 +1,52 @@
+#!/usr/bin/env bash
+
+# Copyright (C) 2024 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.
+
+set -e
+
+GEN_DIR="tests/gen"
+GOLDEN_DIR="tests/golden"
+
+if [[ $(basename $0) == "golden_test.sh" ]]; then
+  # We're running via command-line, so we need to:
+  #   1) manually update generated srcs
+  #   2) use absolute paths
+  if [ -z $ANDROID_BUILD_TOP ]; then
+    echo "You need to source and lunch before you can use this script directly."
+    exit 1
+  fi
+  GEN_DIR="$ANDROID_BUILD_TOP/out/soong/.intermediates/frameworks/base/tools/systemfeatures/systemfeatures-gen-tests-golden-srcs/gen/$GEN_DIR"
+  GOLDEN_DIR="$ANDROID_BUILD_TOP/frameworks/base/tools/systemfeatures/$GOLDEN_DIR"
+  rm -rf "$GEN_DIR"
+  "$ANDROID_BUILD_TOP"/build/soong/soong_ui.bash --make-mode systemfeatures-gen-tests-golden-srcs
+fi
+
+if [[ "$1" == "--update" ]]; then
+  rm -rf "$GOLDEN_DIR"
+  cp -R "$GEN_DIR" "$GOLDEN_DIR"
+  echo "Updated golden test files."
+else
+  echo "Running diff from test output against golden test files..."
+  if diff -ruN "$GOLDEN_DIR" "$GEN_DIR" ; then
+    echo "No changes."
+  else
+    echo
+    echo "----------------------------------------------------------------------------------------"
+    echo "If changes look OK, run:"
+    echo "  \$ANDROID_BUILD_TOP/frameworks/base/tools/systemfeatures/tests/golden_test.sh --update"
+    echo "----------------------------------------------------------------------------------------"
+    exit 1
+  fi
+fi
diff --git a/tools/systemfeatures/tests/Context.java b/tools/systemfeatures/tests/src/Context.java
similarity index 100%
rename from tools/systemfeatures/tests/Context.java
rename to tools/systemfeatures/tests/src/Context.java
diff --git a/tools/systemfeatures/tests/PackageManager.java b/tools/systemfeatures/tests/src/PackageManager.java
similarity index 100%
rename from tools/systemfeatures/tests/PackageManager.java
rename to tools/systemfeatures/tests/src/PackageManager.java
diff --git a/tools/systemfeatures/tests/SystemFeaturesGeneratorTest.java b/tools/systemfeatures/tests/src/SystemFeaturesGeneratorTest.java
similarity index 100%
rename from tools/systemfeatures/tests/SystemFeaturesGeneratorTest.java
rename to tools/systemfeatures/tests/src/SystemFeaturesGeneratorTest.java
diff --git a/tools/systemfeatures/tests/systemfeatures-gen-golden-tests.xml b/tools/systemfeatures/tests/systemfeatures-gen-golden-tests.xml
new file mode 100644
index 0000000..e3a5841
--- /dev/null
+++ b/tools/systemfeatures/tests/systemfeatures-gen-golden-tests.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2024 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.
+-->
+<configuration description="Runs systemfeatures-gen golden diff test">
+    <test class="com.android.tradefed.testtype.binary.ExecutableHostTest" >
+      <option name="binary" value="systemfeatures-gen-golden-tests.sh"/>
+    </test>
+</configuration>