diff --git a/.idea/vcs.xml b/.idea/vcs.xml
index f627902d77b..eafa91e5044 100644
--- a/.idea/vcs.xml
+++ b/.idea/vcs.xml
@@ -1,6 +1,6 @@
-
-
-
-
\ No newline at end of file
+
+
+
+
diff --git a/test/com/facebook/buck/android/AndroidAppBundleIntegrationTest.java b/test/com/facebook/buck/android/AndroidAppBundleIntegrationTest.java
index 6046d1e0a1e..537f6d48e62 100644
--- a/test/com/facebook/buck/android/AndroidAppBundleIntegrationTest.java
+++ b/test/com/facebook/buck/android/AndroidAppBundleIntegrationTest.java
@@ -38,11 +38,14 @@
import com.facebook.buck.testutil.integration.ProjectWorkspace;
import com.facebook.buck.testutil.integration.TestDataHelper;
import com.facebook.buck.testutil.integration.ZipInspector;
+import com.facebook.buck.util.MoreStringsForTests;
import com.facebook.buck.util.zip.ZipConstants;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
+import java.nio.file.Paths;
import java.util.Date;
+import java.util.regex.Pattern;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import org.apache.commons.compress.archivers.zip.ZipUtil;
@@ -62,6 +65,8 @@ public void setUp() throws IOException {
workspace =
TestDataHelper.createProjectWorkspaceForScenario(this, "android_project", tmpFolder);
workspace.setUp();
+ workspace.addTemplateToWorkspace(Paths.get("test/com/facebook/buck/toolchains/kotlin"));
+ setWorkspaceCompilationMode(workspace);
AssumeAndroidPlatform.get(workspace).assumeSdkIsAvailable();
AssumeAndroidPlatform.get(workspace).assumeNdkIsAvailable();
@@ -181,4 +186,72 @@ public void testAppBundleWithMultipleModules() throws IOException {
zipInspector.assertFileExists("small_with_no_resource_deps/manifest/AndroidManifest.xml");
zipInspector.assertFileExists("small_with_no_resource_deps/resources.pb");
}
+
+ @Test
+ public void testAppBundleWithDynamicFeatures() throws IOException {
+ String target = "//apps/dynamic_features:app_dynamic_features";
+ ProcessResult result = workspace.runBuckCommand("build", target);
+ result.assertSuccess();
+
+ Path aab =
+ workspace.getPath(
+ BuildTargetPaths.getGenPath(
+ filesystem, BuildTargetFactory.newInstance(target), "%s.signed.aab"));
+
+ ZipInspector zipInspector = new ZipInspector(aab);
+
+ // Verify dex are built for specific module
+ zipInspector.assertFileExists("base/dex/classes.dex");
+ zipInspector.assertFileExists("native/dex/classes.dex");
+ zipInspector.assertFileExists("java/dex/classes.dex");
+ zipInspector.assertFileExists("kotlin/dex/classes.dex");
+ zipInspector.assertFileExists("initialInstall/dex/classes.dex");
+
+ // Verify native libs are present only in native module
+ zipInspector.assertFileExists("native/lib/x86/libprebuilt.so");
+ zipInspector.assertFileDoesNotExist("base/lib/x86/libprebuilt.so");
+
+ // Verify only x86 libs are present in the generated bundle file
+ zipInspector.assertFileExists("native/lib/x86/libprebuilt.so");
+ zipInspector.assertFileDoesNotExist("base/lib/arm64-v8a/libprebuilt.so");
+
+ // Verify manifest properties and delivery modes of respective modules
+ zipInspector.assertFileExists("base/manifest/AndroidManifest.xml");
+ zipInspector.assertFileExists("native/manifest/AndroidManifest.xml");
+ zipInspector.assertFileExists("java/manifest/AndroidManifest.xml");
+ zipInspector.assertFileExists("kotlin/manifest/AndroidManifest.xml");
+ zipInspector.assertFileExists("initialInstall/manifest/AndroidManifest.xml");
+
+ String nativeManifestContent = new String(
+ zipInspector.getFileContents("native/manifest/AndroidManifest.xml"));
+ assertTrue(Pattern.compile(".*(split[\\u0000-\\u007F]+native).*").matcher(
+ MoreStringsForTests.containsIgnoringPlatformNewlines(nativeManifestContent).toString()).matches());
+ assertTrue(nativeManifestContent.contains("on-demand"));
+
+ String javaManifestContent = new String(
+ zipInspector.getFileContents("java/manifest/AndroidManifest.xml"));
+ assertTrue(Pattern.compile(".*(split[\\u0000-\\u007F]+java).*").matcher(
+ MoreStringsForTests.containsIgnoringPlatformNewlines(javaManifestContent).toString()).matches());
+ assertTrue(javaManifestContent.contains("install-time"));
+ assertTrue(javaManifestContent.contains("conditions"));
+
+ String kotlinManifestContent = new String(
+ zipInspector.getFileContents("kotlin/manifest/AndroidManifest.xml"));
+ assertTrue(Pattern.compile(".*(split[\\u0000-\\u007F]+kotlin).*").matcher(
+ MoreStringsForTests.containsIgnoringPlatformNewlines(kotlinManifestContent).toString()).matches());
+ assertTrue(kotlinManifestContent.contains("on-demand"));
+
+ String initialInstallManifestContent = new String(
+ zipInspector.getFileContents("initialInstall/manifest/AndroidManifest.xml"));
+ assertTrue(Pattern.compile(".*(split[\\u0000-\\u007F]+initialInstall).*").matcher(
+ MoreStringsForTests.containsIgnoringPlatformNewlines(initialInstallManifestContent).toString()).matches());
+ assertTrue(initialInstallManifestContent.contains("install-time"));
+
+ // Verify base manifest should include details of all feature Manifest files
+ String baseManifestContent = new String(zipInspector.getFileContents("base/manifest/AndroidManifest.xml"));
+ assertTrue(baseManifestContent.contains("OnDemandNativeFeatureActivity"));
+ assertTrue(baseManifestContent.contains("ConditionalJavaFeatureActivity"));
+ assertTrue(baseManifestContent.contains("OnDemandKotlinFeatureActivity"));
+ assertTrue(baseManifestContent.contains("AtInstallFeatureActivity"));
+ }
}
diff --git a/test/com/facebook/buck/android/BUCK b/test/com/facebook/buck/android/BUCK
index ee3134fc9f3..9be0f43d34a 100644
--- a/test/com/facebook/buck/android/BUCK
+++ b/test/com/facebook/buck/android/BUCK
@@ -743,6 +743,7 @@ java_test(
"//test/com/facebook/buck/step:testutil",
"//test/com/facebook/buck/testutil:testutil",
"//test/com/facebook/buck/testutil/integration:util",
+ "//test/com/facebook/buck/util:testutil",
"//third-party/java/bundletool:bundletool",
"//third-party/java/commons-compress:commons-compress",
"//third-party/java/guava:guava",
diff --git a/test/com/facebook/buck/android/testdata/android_project/apps/dynamic_features/AndroidManifest.xml b/test/com/facebook/buck/android/testdata/android_project/apps/dynamic_features/AndroidManifest.xml
new file mode 100644
index 00000000000..8a5fa33694b
--- /dev/null
+++ b/test/com/facebook/buck/android/testdata/android_project/apps/dynamic_features/AndroidManifest.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
diff --git a/test/com/facebook/buck/android/testdata/android_project/apps/dynamic_features/BUCK.fixture b/test/com/facebook/buck/android/testdata/android_project/apps/dynamic_features/BUCK.fixture
new file mode 100644
index 00000000000..39c05f42352
--- /dev/null
+++ b/test/com/facebook/buck/android/testdata/android_project/apps/dynamic_features/BUCK.fixture
@@ -0,0 +1,38 @@
+android_bundle(
+ name = "app_dynamic_features",
+ # Configurations needed for dynamic features: Start
+ application_module_configs = {
+ "kotlin": ["//kotlin/com/sample/dynamic_features/on_demand:src_release"],
+ "java": ["//java/com/sample/dynamic_features/conditional:src_release"],
+ "native": ["//native/dynamic_features/on_demand:src_release"],
+ "initialInstall": ["//java/com/sample/dynamic_features/at_install:src_release"],
+ },
+ application_modules_with_manifest = {
+ "kotlin",
+ "java",
+ "native",
+ "initialInstall"
+ },
+ module_manifest_skeleton = "ModuleManifest.xml",
+ use_dynamic_feature = True,
+ use_split_dex = True,
+ # Configurations needed for dynamic features: End
+ bundle_config_file = "bundle-config.json",
+ cpu_filters = [
+ "x86",
+ ],
+ keystore = "//keystores:debug",
+ manifest_skeleton = "AndroidManifest.xml",
+ package_type = "debug",
+ primary_dex_patterns = [
+ "/MyApplication^",
+ ],
+ deps = [
+ "//java/com/sample/app:app",
+ "//res/com/sample/dynamic_features/base:base",
+ "//kotlin/com/sample/dynamic_features/on_demand:src_release",
+ "//java/com/sample/dynamic_features/conditional:src_release",
+ "//native/dynamic_features/on_demand:src_release",
+ "//java/com/sample/dynamic_features/at_install:src_release",
+ ],
+)
diff --git a/test/com/facebook/buck/android/testdata/android_project/apps/dynamic_features/ModuleManifest.xml b/test/com/facebook/buck/android/testdata/android_project/apps/dynamic_features/ModuleManifest.xml
new file mode 100644
index 00000000000..d9f0d614ccc
--- /dev/null
+++ b/test/com/facebook/buck/android/testdata/android_project/apps/dynamic_features/ModuleManifest.xml
@@ -0,0 +1,5 @@
+
+
diff --git a/test/com/facebook/buck/android/testdata/android_project/apps/dynamic_features/bundle-config.json b/test/com/facebook/buck/android/testdata/android_project/apps/dynamic_features/bundle-config.json
new file mode 100644
index 00000000000..93375b3391a
--- /dev/null
+++ b/test/com/facebook/buck/android/testdata/android_project/apps/dynamic_features/bundle-config.json
@@ -0,0 +1,21 @@
+{
+ "compression": {},
+ "optimizations": {
+ "splits_config": {
+ "split_dimension": [
+ {
+ "value": "ABI",
+ "negate": false
+ },
+ {
+ "value": "SCREEN_DENSITY",
+ "negate": true
+ },
+ {
+ "value": "LANGUAGE",
+ "negate": true
+ }
+ ]
+ }
+ }
+}
diff --git a/test/com/facebook/buck/android/testdata/android_project/java/com/sample/dynamic_features/at_install/AndroidManifest.xml b/test/com/facebook/buck/android/testdata/android_project/java/com/sample/dynamic_features/at_install/AndroidManifest.xml
new file mode 100644
index 00000000000..534503578f8
--- /dev/null
+++ b/test/com/facebook/buck/android/testdata/android_project/java/com/sample/dynamic_features/at_install/AndroidManifest.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/test/com/facebook/buck/android/testdata/android_project/java/com/sample/dynamic_features/at_install/AtInstallFeatureActivity.java b/test/com/facebook/buck/android/testdata/android_project/java/com/sample/dynamic_features/at_install/AtInstallFeatureActivity.java
new file mode 100644
index 00000000000..f85d9382c1b
--- /dev/null
+++ b/test/com/facebook/buck/android/testdata/android_project/java/com/sample/dynamic_features/at_install/AtInstallFeatureActivity.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2014-present Facebook, Inc.
+ *
+ * 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 com.sample.dynamic_features.at_install;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.widget.TextView;
+import com.sample.initialInstall.R;
+
+public class AtInstallFeatureActivity extends Activity {
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.layout_initial_install);
+
+ TextView tv = (TextView) findViewById(R.id.initial_install_text_view);
+ tv.setText(R.string.initial_install_text);
+ }
+}
diff --git a/test/com/facebook/buck/android/testdata/android_project/java/com/sample/dynamic_features/at_install/BUCK.fixture b/test/com/facebook/buck/android/testdata/android_project/java/com/sample/dynamic_features/at_install/BUCK.fixture
new file mode 100644
index 00000000000..5fecc31598c
--- /dev/null
+++ b/test/com/facebook/buck/android/testdata/android_project/java/com/sample/dynamic_features/at_install/BUCK.fixture
@@ -0,0 +1,11 @@
+android_library(
+ name = "src_release",
+ srcs = glob(["*.java"]),
+ manifest = "AndroidManifest.xml",
+ visibility = [
+ "PUBLIC",
+ ],
+ deps = [
+ "//res/com/sample/dynamic_features/at_install:initialInstall",
+ ],
+)
diff --git a/test/com/facebook/buck/android/testdata/android_project/java/com/sample/dynamic_features/conditional/AndroidManifest.xml b/test/com/facebook/buck/android/testdata/android_project/java/com/sample/dynamic_features/conditional/AndroidManifest.xml
new file mode 100644
index 00000000000..10f8cd55a47
--- /dev/null
+++ b/test/com/facebook/buck/android/testdata/android_project/java/com/sample/dynamic_features/conditional/AndroidManifest.xml
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/test/com/facebook/buck/android/testdata/android_project/java/com/sample/dynamic_features/conditional/BUCK.fixture b/test/com/facebook/buck/android/testdata/android_project/java/com/sample/dynamic_features/conditional/BUCK.fixture
new file mode 100644
index 00000000000..907001fc981
--- /dev/null
+++ b/test/com/facebook/buck/android/testdata/android_project/java/com/sample/dynamic_features/conditional/BUCK.fixture
@@ -0,0 +1,11 @@
+android_library(
+ name = "src_release",
+ srcs = glob(["*.java"]),
+ manifest = "AndroidManifest.xml",
+ visibility = [
+ "PUBLIC",
+ ],
+ deps = [
+ "//res/com/sample/dynamic_features/conditional:java",
+ ],
+)
diff --git a/test/com/facebook/buck/android/testdata/android_project/java/com/sample/dynamic_features/conditional/ConditionalJavaFeatureActivity.java b/test/com/facebook/buck/android/testdata/android_project/java/com/sample/dynamic_features/conditional/ConditionalJavaFeatureActivity.java
new file mode 100644
index 00000000000..ba9e43157c1
--- /dev/null
+++ b/test/com/facebook/buck/android/testdata/android_project/java/com/sample/dynamic_features/conditional/ConditionalJavaFeatureActivity.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2014-present Facebook, Inc.
+ *
+ * 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 com.sample.dynamic_features.conditional;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.widget.TextView;
+import com.sample.java.R;
+
+public class ConditionalJavaFeatureActivity extends Activity {
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.layout_conditional_install);
+
+ TextView tv = (TextView) findViewById(R.id.conditional_java_text_view);
+ tv.setText(R.string.conditional_java_text);
+ }
+}
diff --git a/test/com/facebook/buck/android/testdata/android_project/kotlin/com/sample/dynamic_features/on_demand/AndroidManifest.xml b/test/com/facebook/buck/android/testdata/android_project/kotlin/com/sample/dynamic_features/on_demand/AndroidManifest.xml
new file mode 100644
index 00000000000..b95677e0e9b
--- /dev/null
+++ b/test/com/facebook/buck/android/testdata/android_project/kotlin/com/sample/dynamic_features/on_demand/AndroidManifest.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/test/com/facebook/buck/android/testdata/android_project/kotlin/com/sample/dynamic_features/on_demand/BUCK.fixture b/test/com/facebook/buck/android/testdata/android_project/kotlin/com/sample/dynamic_features/on_demand/BUCK.fixture
new file mode 100644
index 00000000000..bafa6dccfe4
--- /dev/null
+++ b/test/com/facebook/buck/android/testdata/android_project/kotlin/com/sample/dynamic_features/on_demand/BUCK.fixture
@@ -0,0 +1,14 @@
+android_library(
+ name = "src_release",
+ srcs = glob([
+ "OnDemandKotlinFeatureActivity.kt",
+ ]),
+ manifest = "AndroidManifest.xml",
+ language = "KOTLIN",
+ visibility = [
+ "PUBLIC",
+ ],
+ deps = [
+ "//res/com/sample/dynamic_features/on_demand/kotlin:kotlin",
+ ],
+)
diff --git a/test/com/facebook/buck/android/testdata/android_project/kotlin/com/sample/dynamic_features/on_demand/OnDemandKotlinFeatureActivity.kt b/test/com/facebook/buck/android/testdata/android_project/kotlin/com/sample/dynamic_features/on_demand/OnDemandKotlinFeatureActivity.kt
new file mode 100644
index 00000000000..8b6017e4021
--- /dev/null
+++ b/test/com/facebook/buck/android/testdata/android_project/kotlin/com/sample/dynamic_features/on_demand/OnDemandKotlinFeatureActivity.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2014-present Facebook, Inc.
+ *
+ * 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 com.sample.dynamic_features.on_demand;
+
+import android.app.Activity
+import android.os.Bundle
+import android.widget.TextView
+import com.sample.kotlin.R
+
+public class OnDemandKotlinFeatureActivity : Activity() {
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContentView(R.layout.layout_on_demand_kotlin)
+ val tv: TextView = findViewById(R.id.on_demand_kotlin_text_view) as TextView
+ tv.text = getString(R.string.on_demand_kotlin_text)
+ }
+}
diff --git a/test/com/facebook/buck/android/testdata/android_project/native/dynamic_features/on_demand/AndroidManifest.xml b/test/com/facebook/buck/android/testdata/android_project/native/dynamic_features/on_demand/AndroidManifest.xml
new file mode 100644
index 00000000000..d81d1e96e3a
--- /dev/null
+++ b/test/com/facebook/buck/android/testdata/android_project/native/dynamic_features/on_demand/AndroidManifest.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/test/com/facebook/buck/android/testdata/android_project/native/dynamic_features/on_demand/BUCK.fixture b/test/com/facebook/buck/android/testdata/android_project/native/dynamic_features/on_demand/BUCK.fixture
new file mode 100644
index 00000000000..9e0a82c0d8c
--- /dev/null
+++ b/test/com/facebook/buck/android/testdata/android_project/native/dynamic_features/on_demand/BUCK.fixture
@@ -0,0 +1,18 @@
+android_library(
+ name = "src_release",
+ srcs = glob(["*.java"]),
+ manifest = "AndroidManifest.xml",
+ visibility = [
+ "PUBLIC",
+ ],
+ deps = [
+ "//res/com/sample/dynamic_features/on_demand/native:native",
+ ":native_libs",
+ ],
+)
+
+prebuilt_native_library(
+ name = "native_libs",
+ native_libs = "libs",
+ visibility = ["PUBLIC"],
+)
diff --git a/test/com/facebook/buck/android/testdata/android_project/native/dynamic_features/on_demand/OnDemandNativeFeatureActivity.java b/test/com/facebook/buck/android/testdata/android_project/native/dynamic_features/on_demand/OnDemandNativeFeatureActivity.java
new file mode 100644
index 00000000000..e9a2c92bcf9
--- /dev/null
+++ b/test/com/facebook/buck/android/testdata/android_project/native/dynamic_features/on_demand/OnDemandNativeFeatureActivity.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2014-present Facebook, Inc.
+ *
+ * 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 dynamic_features.on_demand;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.widget.TextView;
+import com.sample.ccode.R;
+
+public class OnDemandNativeFeatureActivity extends Activity {
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.layout_on_demand_native);
+
+ TextView tv = (TextView) findViewById(R.id.on_demand_native_text_view);
+ tv.setText(R.string.on_demand_native_text);
+ }
+}
diff --git a/test/com/facebook/buck/android/testdata/android_project/native/dynamic_features/on_demand/libs/arm64-v8a/libprebuilt.so b/test/com/facebook/buck/android/testdata/android_project/native/dynamic_features/on_demand/libs/arm64-v8a/libprebuilt.so
new file mode 100755
index 00000000000..de2559e238a
Binary files /dev/null and b/test/com/facebook/buck/android/testdata/android_project/native/dynamic_features/on_demand/libs/arm64-v8a/libprebuilt.so differ
diff --git a/test/com/facebook/buck/android/testdata/android_project/native/dynamic_features/on_demand/libs/armeabi-v7a/libprebuilt.so b/test/com/facebook/buck/android/testdata/android_project/native/dynamic_features/on_demand/libs/armeabi-v7a/libprebuilt.so
new file mode 100755
index 00000000000..3ccc26fa4e2
Binary files /dev/null and b/test/com/facebook/buck/android/testdata/android_project/native/dynamic_features/on_demand/libs/armeabi-v7a/libprebuilt.so differ
diff --git a/test/com/facebook/buck/android/testdata/android_project/native/dynamic_features/on_demand/libs/x86/libprebuilt.so b/test/com/facebook/buck/android/testdata/android_project/native/dynamic_features/on_demand/libs/x86/libprebuilt.so
new file mode 100755
index 00000000000..6a5651534a6
Binary files /dev/null and b/test/com/facebook/buck/android/testdata/android_project/native/dynamic_features/on_demand/libs/x86/libprebuilt.so differ
diff --git a/test/com/facebook/buck/android/testdata/android_project/native/dynamic_features/on_demand/libs/x86_64/libprebuilt.so b/test/com/facebook/buck/android/testdata/android_project/native/dynamic_features/on_demand/libs/x86_64/libprebuilt.so
new file mode 100755
index 00000000000..6b69f58dfbd
Binary files /dev/null and b/test/com/facebook/buck/android/testdata/android_project/native/dynamic_features/on_demand/libs/x86_64/libprebuilt.so differ
diff --git a/test/com/facebook/buck/android/testdata/android_project/res/com/sample/dynamic_features/at_install/BUCK.fixture b/test/com/facebook/buck/android/testdata/android_project/res/com/sample/dynamic_features/at_install/BUCK.fixture
new file mode 100644
index 00000000000..1f3784fc6c7
--- /dev/null
+++ b/test/com/facebook/buck/android/testdata/android_project/res/com/sample/dynamic_features/at_install/BUCK.fixture
@@ -0,0 +1,10 @@
+android_resource(
+ name = "initialInstall",
+ package = "com.sample.initialInstall",
+ res = "res",
+ visibility = [
+ "PUBLIC",
+ ],
+ deps = [
+ ],
+)
diff --git a/test/com/facebook/buck/android/testdata/android_project/res/com/sample/dynamic_features/at_install/res/layout/layout_initial_install.xml b/test/com/facebook/buck/android/testdata/android_project/res/com/sample/dynamic_features/at_install/res/layout/layout_initial_install.xml
new file mode 100644
index 00000000000..87f9aa58718
--- /dev/null
+++ b/test/com/facebook/buck/android/testdata/android_project/res/com/sample/dynamic_features/at_install/res/layout/layout_initial_install.xml
@@ -0,0 +1,12 @@
+
+
+
+
diff --git a/test/com/facebook/buck/android/testdata/android_project/res/com/sample/dynamic_features/at_install/res/values/strings.xml b/test/com/facebook/buck/android/testdata/android_project/res/com/sample/dynamic_features/at_install/res/values/strings.xml
new file mode 100644
index 00000000000..ade053aab36
--- /dev/null
+++ b/test/com/facebook/buck/android/testdata/android_project/res/com/sample/dynamic_features/at_install/res/values/strings.xml
@@ -0,0 +1,4 @@
+
+
+ String from at-install module
+
diff --git a/test/com/facebook/buck/android/testdata/android_project/res/com/sample/dynamic_features/base/BUCK.fixture b/test/com/facebook/buck/android/testdata/android_project/res/com/sample/dynamic_features/base/BUCK.fixture
new file mode 100644
index 00000000000..a5a6eed742c
--- /dev/null
+++ b/test/com/facebook/buck/android/testdata/android_project/res/com/sample/dynamic_features/base/BUCK.fixture
@@ -0,0 +1,10 @@
+android_resource(
+ name = "base",
+ package = "com.sample",
+ res = "res",
+ visibility = [
+ "PUBLIC",
+ ],
+ deps = [
+ ],
+)
diff --git a/test/com/facebook/buck/android/testdata/android_project/res/com/sample/dynamic_features/base/res/drawable-hdpi/app_icon.png b/test/com/facebook/buck/android/testdata/android_project/res/com/sample/dynamic_features/base/res/drawable-hdpi/app_icon.png
new file mode 100755
index 00000000000..92fc86a7918
Binary files /dev/null and b/test/com/facebook/buck/android/testdata/android_project/res/com/sample/dynamic_features/base/res/drawable-hdpi/app_icon.png differ
diff --git a/test/com/facebook/buck/android/testdata/android_project/res/com/sample/dynamic_features/base/res/drawable-mdpi/app_icon.png b/test/com/facebook/buck/android/testdata/android_project/res/com/sample/dynamic_features/base/res/drawable-mdpi/app_icon.png
new file mode 100755
index 00000000000..9c2dadd0574
Binary files /dev/null and b/test/com/facebook/buck/android/testdata/android_project/res/com/sample/dynamic_features/base/res/drawable-mdpi/app_icon.png differ
diff --git a/test/com/facebook/buck/android/testdata/android_project/res/com/sample/dynamic_features/base/res/drawable-xhdpi/app_icon.png b/test/com/facebook/buck/android/testdata/android_project/res/com/sample/dynamic_features/base/res/drawable-xhdpi/app_icon.png
new file mode 100755
index 00000000000..a307cb64fe3
Binary files /dev/null and b/test/com/facebook/buck/android/testdata/android_project/res/com/sample/dynamic_features/base/res/drawable-xhdpi/app_icon.png differ
diff --git a/test/com/facebook/buck/android/testdata/android_project/res/com/sample/dynamic_features/base/res/values/strings.xml b/test/com/facebook/buck/android/testdata/android_project/res/com/sample/dynamic_features/base/res/values/strings.xml
new file mode 100644
index 00000000000..3313590c264
--- /dev/null
+++ b/test/com/facebook/buck/android/testdata/android_project/res/com/sample/dynamic_features/base/res/values/strings.xml
@@ -0,0 +1,8 @@
+
+
+ Dynamic Features App
+ Initial Installed module title
+ On-demand java module title
+ On-demand kotlin module title
+ On-demand native module title
+
diff --git a/test/com/facebook/buck/android/testdata/android_project/res/com/sample/dynamic_features/conditional/BUCK.fixture b/test/com/facebook/buck/android/testdata/android_project/res/com/sample/dynamic_features/conditional/BUCK.fixture
new file mode 100644
index 00000000000..2ddb755f49d
--- /dev/null
+++ b/test/com/facebook/buck/android/testdata/android_project/res/com/sample/dynamic_features/conditional/BUCK.fixture
@@ -0,0 +1,10 @@
+android_resource(
+ name = "java",
+ package = "com.sample.java",
+ res = "res",
+ visibility = [
+ "PUBLIC",
+ ],
+ deps = [
+ ],
+)
diff --git a/test/com/facebook/buck/android/testdata/android_project/res/com/sample/dynamic_features/conditional/res/layout/layout_conditional_install.xml b/test/com/facebook/buck/android/testdata/android_project/res/com/sample/dynamic_features/conditional/res/layout/layout_conditional_install.xml
new file mode 100644
index 00000000000..26d205ccd64
--- /dev/null
+++ b/test/com/facebook/buck/android/testdata/android_project/res/com/sample/dynamic_features/conditional/res/layout/layout_conditional_install.xml
@@ -0,0 +1,12 @@
+
+
+
+
diff --git a/test/com/facebook/buck/android/testdata/android_project/res/com/sample/dynamic_features/conditional/res/values/strings.xml b/test/com/facebook/buck/android/testdata/android_project/res/com/sample/dynamic_features/conditional/res/values/strings.xml
new file mode 100644
index 00000000000..c72a8ddf012
--- /dev/null
+++ b/test/com/facebook/buck/android/testdata/android_project/res/com/sample/dynamic_features/conditional/res/values/strings.xml
@@ -0,0 +1,4 @@
+
+
+ String from conditional module
+
diff --git a/test/com/facebook/buck/android/testdata/android_project/res/com/sample/dynamic_features/on_demand/kotlin/BUCK.fixture b/test/com/facebook/buck/android/testdata/android_project/res/com/sample/dynamic_features/on_demand/kotlin/BUCK.fixture
new file mode 100644
index 00000000000..e58f50d56ab
--- /dev/null
+++ b/test/com/facebook/buck/android/testdata/android_project/res/com/sample/dynamic_features/on_demand/kotlin/BUCK.fixture
@@ -0,0 +1,10 @@
+android_resource(
+ name = "kotlin",
+ package = "com.sample.kotlin",
+ res = "res",
+ visibility = [
+ "PUBLIC",
+ ],
+ deps = [
+ ],
+)
diff --git a/test/com/facebook/buck/android/testdata/android_project/res/com/sample/dynamic_features/on_demand/kotlin/res/layout/layout_on_demand_kotlin.xml b/test/com/facebook/buck/android/testdata/android_project/res/com/sample/dynamic_features/on_demand/kotlin/res/layout/layout_on_demand_kotlin.xml
new file mode 100644
index 00000000000..37add8d65c7
--- /dev/null
+++ b/test/com/facebook/buck/android/testdata/android_project/res/com/sample/dynamic_features/on_demand/kotlin/res/layout/layout_on_demand_kotlin.xml
@@ -0,0 +1,12 @@
+
+
+
+
diff --git a/test/com/facebook/buck/android/testdata/android_project/res/com/sample/dynamic_features/on_demand/kotlin/res/values/strings.xml b/test/com/facebook/buck/android/testdata/android_project/res/com/sample/dynamic_features/on_demand/kotlin/res/values/strings.xml
new file mode 100644
index 00000000000..e283020171f
--- /dev/null
+++ b/test/com/facebook/buck/android/testdata/android_project/res/com/sample/dynamic_features/on_demand/kotlin/res/values/strings.xml
@@ -0,0 +1,4 @@
+
+
+ String from kotlin on-demand module
+
diff --git a/test/com/facebook/buck/android/testdata/android_project/res/com/sample/dynamic_features/on_demand/native/BUCK.fixture b/test/com/facebook/buck/android/testdata/android_project/res/com/sample/dynamic_features/on_demand/native/BUCK.fixture
new file mode 100644
index 00000000000..57fa074f8b2
--- /dev/null
+++ b/test/com/facebook/buck/android/testdata/android_project/res/com/sample/dynamic_features/on_demand/native/BUCK.fixture
@@ -0,0 +1,10 @@
+android_resource(
+ name = "native",
+ package = "com.sample.ccode",
+ res = "res",
+ visibility = [
+ "PUBLIC",
+ ],
+ deps = [
+ ],
+)
diff --git a/test/com/facebook/buck/android/testdata/android_project/res/com/sample/dynamic_features/on_demand/native/res/layout/layout_on_demand_native.xml b/test/com/facebook/buck/android/testdata/android_project/res/com/sample/dynamic_features/on_demand/native/res/layout/layout_on_demand_native.xml
new file mode 100644
index 00000000000..e64621a5fbb
--- /dev/null
+++ b/test/com/facebook/buck/android/testdata/android_project/res/com/sample/dynamic_features/on_demand/native/res/layout/layout_on_demand_native.xml
@@ -0,0 +1,12 @@
+
+
+
+
diff --git a/test/com/facebook/buck/android/testdata/android_project/res/com/sample/dynamic_features/on_demand/native/res/values/strings.xml b/test/com/facebook/buck/android/testdata/android_project/res/com/sample/dynamic_features/on_demand/native/res/values/strings.xml
new file mode 100644
index 00000000000..e224015f321
--- /dev/null
+++ b/test/com/facebook/buck/android/testdata/android_project/res/com/sample/dynamic_features/on_demand/native/res/values/strings.xml
@@ -0,0 +1,4 @@
+
+
+ String from native on-demand module
+