Invoke aapt2 to generate resource table in tests

This will call aapt2 to generate the resource table based on values of a res folder

Currently the builder supports adding strings and drawables.

Bug: 287398085
Change-Id: I40ca6f28034ceecff7740ea325d071c5eb03eb49
diff --git a/src/test/java/com/android/tools/r8/ToolHelper.java b/src/test/java/com/android/tools/r8/ToolHelper.java
index 258da3a..ccf07e8 100644
--- a/src/test/java/com/android/tools/r8/ToolHelper.java
+++ b/src/test/java/com/android/tools/r8/ToolHelper.java
@@ -210,6 +210,8 @@
   public static final Path DESUGARED_JDK_11_LIB_JAR =
       Paths.get(OPEN_JDK_DIR + "desugar_jdk_libs_11/desugar_jdk_libs.jar");
 
+  public static final Path AAPT2 = Paths.get(THIRD_PARTY_DIR, "aapt2", "aapt2");
+
   public static Path getDesugarLibConversions(CustomConversionVersion legacy) {
     return legacy == CustomConversionVersion.LEGACY
         ? Paths.get(LIBS_DIR, "library_desugar_conversions_legacy.jar")
@@ -1679,6 +1681,13 @@
     return runProcess(builder);
   }
 
+  public static ProcessResult runAapt2(String... args) throws IOException {
+    ArrayList<String> cmd = Lists.newArrayList(AAPT2.toString());
+    cmd.addAll(Lists.newArrayList(args));
+    ProcessBuilder builder = new ProcessBuilder(cmd);
+    return runProcess(builder);
+  }
+
   public static ProcessResult forkD8(Path dir, String... args)
       throws IOException, InterruptedException {
     return forkJava(dir, D8.class, args);
diff --git a/src/test/java/com/android/tools/r8/androidresources/AndroidResourceTestingUtils.java b/src/test/java/com/android/tools/r8/androidresources/AndroidResourceTestingUtils.java
index b58587b..bf9ee64 100644
--- a/src/test/java/com/android/tools/r8/androidresources/AndroidResourceTestingUtils.java
+++ b/src/test/java/com/android/tools/r8/androidresources/AndroidResourceTestingUtils.java
@@ -3,47 +3,158 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.androidresources;
 
-import java.nio.charset.StandardCharsets;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.FileUtils;
+import com.google.common.collect.MoreCollectors;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.TreeMap;
+import org.junit.rules.TemporaryFolder;
 
 public class AndroidResourceTestingUtils {
 
-  // Taken from default empty android studio activity template
-  public static String TEST_MANIFEST =
+  public static String SIMPLE_MANIFEST_WITH_STRING =
       "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
           + "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n"
-          + "    xmlns:tools=\"http://schemas.android.com/tools\">\n"
-          + "\n"
-          + "    <application\n"
-          + "        android:allowBackup=\"true\"\n"
-          + "        android:dataExtractionRules=\"@xml/data_extraction_rules\"\n"
-          + "        android:fullBackupContent=\"@xml/backup_rules\"\n"
-          + "        android:icon=\"@mipmap/ic_launcher\"\n"
-          + "        android:label=\"@string/app_name\"\n"
-          + "        android:roundIcon=\"@mipmap/ic_launcher_round\"\n"
-          + "        android:supportsRtl=\"true\"\n"
-          + "        android:theme=\"@style/Theme.MyApplication\"\n"
-          + "        tools:targetApi=\"31\">\n"
-          + "        <activity\n"
-          + "            android:name=\".MainActivity\"\n"
-          + "            android:exported=\"true\"\n"
-          + "            android:label=\"@string/app_name\"\n"
-          + "            android:theme=\"@style/Theme.MyApplication.NoActionBar\">\n"
-          + "            <intent-filter>\n"
-          + "                <action android:name=\"android.intent.action.MAIN\" />\n"
-          + "\n"
-          + "                <category android:name=\"android.intent.category.LAUNCHER\" />\n"
-          + "            </intent-filter>\n"
-          + "\n"
-          + "            <meta-data\n"
-          + "                android:name=\"android.app.lib_name\"\n"
-          + "                android:value=\"\" />\n"
-          + "        </activity>\n"
+          + "          package=\"com.android.tools.r8\">\n"
+          + "    <application android:label=\"@string/app_name\">\n"
           + "    </application>\n"
-          + "\n"
-          + "</manifest>";
+          + "</manifest>\n"
+          + "\n";
 
-  // TODO(287399385): Add testing utils for generating/consuming resource tables.
-  public static byte[] TEST_RESOURCE_TABLE = "RESOURCE_TABLE".getBytes(StandardCharsets.UTF_8);
+  public static class AndroidTestRClass {
+    private final Path javaFilePath;
+    private final Path rootDirectory;
+
+    AndroidTestRClass(Path rootDirectory) throws IOException {
+      this.rootDirectory = rootDirectory;
+      this.javaFilePath =
+          Files.walk(rootDirectory)
+              .filter(path -> path.endsWith("R.java"))
+              .collect(MoreCollectors.onlyElement());
+    }
+
+    public Path getJavaFilePath() {
+      return javaFilePath;
+    }
+
+    public Path getRootDirectory() {
+      return rootDirectory;
+    }
+  }
+
+  public static class AndroidTestResource {
+    private final AndroidTestRClass rClass;
+    private final Path resourceZip;
+
+    AndroidTestResource(AndroidTestRClass rClass, Path resourceZip) {
+      this.rClass = rClass;
+      this.resourceZip = resourceZip;
+    }
+
+    public AndroidTestRClass getRClass() {
+      return rClass;
+    }
+
+    public Path getResourceZip() {
+      return resourceZip;
+    }
+  }
+
+  public static class AndroidTestResourceBuilder {
+    private String manifest;
+    private Map<String, String> stringValues = new TreeMap<>();
+    private Map<String, byte[]> drawables = new TreeMap<>();
+
+    AndroidTestResourceBuilder withManifest(String manifest) {
+      this.manifest = manifest;
+      return this;
+    }
+
+    AndroidTestResourceBuilder withSimpleManifest() {
+      this.manifest = SIMPLE_MANIFEST_WITH_STRING;
+      return this;
+    }
+
+    AndroidTestResourceBuilder addStringValue(String name, String value) {
+      stringValues.put(name, value);
+      return this;
+    }
+
+    AndroidTestResourceBuilder addDrawable(String name, byte[] value) {
+      drawables.put(name, value);
+      return this;
+    }
+
+    AndroidTestResource build(TemporaryFolder temp) throws IOException {
+      Path manifestPath =
+          FileUtils.writeTextFile(temp.newFile("AndroidManifest.xml").toPath(), this.manifest);
+      Path resFolder = temp.newFolder("res").toPath();
+      if (stringValues.size() > 0) {
+        FileUtils.writeTextFile(
+            temp.newFolder("res", "values").toPath().resolve("strings.xml"),
+            createStringResourceXml());
+      }
+      if (drawables.size() > 0) {
+        for (Entry<String, byte[]> entry : drawables.entrySet()) {
+          FileUtils.writeToFile(
+              temp.newFolder("res", "drawable").toPath().resolve(entry.getKey()),
+              null,
+              entry.getValue());
+        }
+      }
+
+      Path output = temp.newFile("resources.zip").toPath();
+      Path rClassOutput = temp.newFolder("aapt_R_class").toPath();
+      compileWithAapt2(resFolder, manifestPath, rClassOutput, output, temp);
+      return new AndroidTestResource(new AndroidTestRClass(rClassOutput), output);
+    }
+
+    private String createStringResourceXml() {
+      StringBuilder stringBuilder = new StringBuilder("<resources>\n");
+      stringValues.forEach(
+          (name, value) ->
+              stringBuilder.append("<string name=\"" + name + "\">" + value + "</string>\n"));
+      stringBuilder.append("</resources>");
+      return stringBuilder.toString();
+    }
+  }
+
+  public static void compileWithAapt2(
+      Path resFolder, Path manifest, Path rClassFolder, Path resourceZip, TemporaryFolder temp)
+      throws IOException {
+    Path compileOutput = temp.newFile("compiled.zip").toPath();
+    ProcessResult compileProcessResult =
+        ToolHelper.runAapt2(
+            "compile", "-o", compileOutput.toString(), "--dir", resFolder.toString());
+    failOnError(compileProcessResult);
+
+    ProcessResult linkProcesResult =
+        ToolHelper.runAapt2(
+            "link",
+            "-I",
+            ToolHelper.getAndroidJar(AndroidApiLevel.S).toString(),
+            "-o",
+            resourceZip.toString(),
+            "--java",
+            rClassFolder.toString(),
+            "--manifest",
+            manifest.toString(),
+            "--proto-format",
+            compileOutput.toString());
+    failOnError(linkProcesResult);
+  }
+
+  private static void failOnError(ProcessResult processResult) {
+    if (processResult.exitCode != 0) {
+      throw new RuntimeException("Failed aapt2: \n" + processResult);
+    }
+  }
 
   // The below byte arrays are lifted from the resource shrinkers DummyContent
 
diff --git a/src/test/java/com/android/tools/r8/androidresources/AndroidResourcesPassthroughTest.java b/src/test/java/com/android/tools/r8/androidresources/AndroidResourcesPassthroughTest.java
index f0bb82a..31359f3 100644
--- a/src/test/java/com/android/tools/r8/androidresources/AndroidResourcesPassthroughTest.java
+++ b/src/test/java/com/android/tools/r8/androidresources/AndroidResourcesPassthroughTest.java
@@ -3,19 +3,22 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.androidresources;
 
-import static org.junit.Assert.assertTrue;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertThat;
 
 import com.android.tools.r8.ArchiveProtoAndroidResourceConsumer;
 import com.android.tools.r8.ArchiveProtoAndroidResourceProvider;
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.androidresources.AndroidResourceTestingUtils.AndroidTestResource;
+import com.android.tools.r8.androidresources.AndroidResourceTestingUtils.AndroidTestResourceBuilder;
 import com.android.tools.r8.origin.PathOrigin;
+import com.android.tools.r8.utils.FileUtils;
 import com.android.tools.r8.utils.ZipUtils;
-import com.android.tools.r8.utils.ZipUtils.ZipBuilder;
-import java.nio.charset.StandardCharsets;
+import java.nio.charset.Charset;
 import java.nio.file.Path;
-import java.util.Arrays;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
@@ -38,14 +41,14 @@
     String manifestPath = "AndroidManifest.xml";
     String resourcePath = "resources.pb";
     String pngPath = "res/drawable/foo.png";
-    String xmlPath = "res/xml/bar.xml";
-    Path resources =
-        ZipBuilder.builder(temp.newFile("resources.zip").toPath())
-            .addText(manifestPath, AndroidResourceTestingUtils.TEST_MANIFEST)
-            .addBytes(resourcePath, AndroidResourceTestingUtils.TEST_RESOURCE_TABLE)
-            .addBytes(pngPath, AndroidResourceTestingUtils.TINY_PNG)
-            .addBytes(xmlPath, AndroidResourceTestingUtils.TINY_PROTO_XML)
-            .build();
+
+    AndroidTestResource testResource =
+        new AndroidTestResourceBuilder()
+            .withSimpleManifest()
+            .addStringValue("app_name", "The App")
+            .addDrawable("foo.png", AndroidResourceTestingUtils.TINY_PNG)
+            .build(temp);
+    Path resources = testResource.getResourceZip();
     Path output = temp.newFile("resources_out.zip").toPath();
     testForR8(parameters.getBackend())
         .addInnerClasses(getClass())
@@ -59,20 +62,19 @@
         .addKeepMainRule(FooBar.class)
         .run(parameters.getRuntime(), FooBar.class)
         .assertSuccessWithOutputLines("Hello World");
-    assertTrue(
-        Arrays.equals(
-            ZipUtils.readSingleEntry(output, manifestPath),
-            AndroidResourceTestingUtils.TEST_MANIFEST.getBytes(StandardCharsets.UTF_8)));
-    assertTrue(
-        Arrays.equals(
-            ZipUtils.readSingleEntry(output, resourcePath),
-            AndroidResourceTestingUtils.TEST_RESOURCE_TABLE));
-    assertTrue(
-        Arrays.equals(
-            ZipUtils.readSingleEntry(output, pngPath), AndroidResourceTestingUtils.TINY_PNG));
-    assertTrue(
-        Arrays.equals(
-            ZipUtils.readSingleEntry(output, xmlPath), AndroidResourceTestingUtils.TINY_PROTO_XML));
+    assertArrayEquals(
+        ZipUtils.readSingleEntry(output, manifestPath),
+        ZipUtils.readSingleEntry(resources, manifestPath));
+    assertArrayEquals(
+        ZipUtils.readSingleEntry(output, resourcePath),
+        ZipUtils.readSingleEntry(resources, resourcePath));
+    assertArrayEquals(
+        ZipUtils.readSingleEntry(output, pngPath), ZipUtils.readSingleEntry(resources, pngPath));
+    String rClassContent =
+        FileUtils.readTextFile(
+            testResource.getRClass().getJavaFilePath(), Charset.defaultCharset());
+    assertThat(rClassContent, containsString("app_name"));
+    assertThat(rClassContent, containsString("foo"));
   }
 
   public static class FooBar {