Skip to content
This repository was archived by the owner on Nov 10, 2023. It is now read-only.

Commit ddf2941

Browse files
authored
Android App Bundles - Add test cases for dynamic feature modules (#2609)
* Prepared test data for dynamic feature module. Each feature has some distinctly unique characteristics: 1. kotlin -> Feature written in Kotlin and will be available on-demand 2. java -> Feature written in Java and will be available conditionally for minsdk=21 and maxSdk=24 3. native -> Feature written in Java using JNI and will be available on-demand 4. initialInstall -> Feature written in Java and will be available at install-time * Setup workspace with kotlinc path
1 parent 6afcd69 commit ddf2941

40 files changed

Lines changed: 568 additions & 4 deletions

File tree

.idea/vcs.xml

Lines changed: 4 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

test/com/facebook/buck/android/AndroidAppBundleIntegrationTest.java

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,11 +38,14 @@
3838
import com.facebook.buck.testutil.integration.ProjectWorkspace;
3939
import com.facebook.buck.testutil.integration.TestDataHelper;
4040
import com.facebook.buck.testutil.integration.ZipInspector;
41+
import com.facebook.buck.util.MoreStringsForTests;
4142
import com.facebook.buck.util.zip.ZipConstants;
4243
import java.io.IOException;
4344
import java.nio.file.Files;
4445
import java.nio.file.Path;
46+
import java.nio.file.Paths;
4547
import java.util.Date;
48+
import java.util.regex.Pattern;
4649
import java.util.zip.ZipEntry;
4750
import java.util.zip.ZipInputStream;
4851
import org.apache.commons.compress.archivers.zip.ZipUtil;
@@ -62,6 +65,8 @@ public void setUp() throws IOException {
6265
workspace =
6366
TestDataHelper.createProjectWorkspaceForScenario(this, "android_project", tmpFolder);
6467
workspace.setUp();
68+
workspace.addTemplateToWorkspace(Paths.get("test/com/facebook/buck/toolchains/kotlin"));
69+
setWorkspaceCompilationMode(workspace);
6570

6671
AssumeAndroidPlatform.get(workspace).assumeSdkIsAvailable();
6772
AssumeAndroidPlatform.get(workspace).assumeNdkIsAvailable();
@@ -181,4 +186,72 @@ public void testAppBundleWithMultipleModules() throws IOException {
181186
zipInspector.assertFileExists("small_with_no_resource_deps/manifest/AndroidManifest.xml");
182187
zipInspector.assertFileExists("small_with_no_resource_deps/resources.pb");
183188
}
189+
190+
@Test
191+
public void testAppBundleWithDynamicFeatures() throws IOException {
192+
String target = "//apps/dynamic_features:app_dynamic_features";
193+
ProcessResult result = workspace.runBuckCommand("build", target);
194+
result.assertSuccess();
195+
196+
Path aab =
197+
workspace.getPath(
198+
BuildTargetPaths.getGenPath(
199+
filesystem, BuildTargetFactory.newInstance(target), "%s.signed.aab"));
200+
201+
ZipInspector zipInspector = new ZipInspector(aab);
202+
203+
// Verify dex are built for specific module
204+
zipInspector.assertFileExists("base/dex/classes.dex");
205+
zipInspector.assertFileExists("native/dex/classes.dex");
206+
zipInspector.assertFileExists("java/dex/classes.dex");
207+
zipInspector.assertFileExists("kotlin/dex/classes.dex");
208+
zipInspector.assertFileExists("initialInstall/dex/classes.dex");
209+
210+
// Verify native libs are present only in native module
211+
zipInspector.assertFileExists("native/lib/x86/libprebuilt.so");
212+
zipInspector.assertFileDoesNotExist("base/lib/x86/libprebuilt.so");
213+
214+
// Verify only x86 libs are present in the generated bundle file
215+
zipInspector.assertFileExists("native/lib/x86/libprebuilt.so");
216+
zipInspector.assertFileDoesNotExist("base/lib/arm64-v8a/libprebuilt.so");
217+
218+
// Verify manifest properties and delivery modes of respective modules
219+
zipInspector.assertFileExists("base/manifest/AndroidManifest.xml");
220+
zipInspector.assertFileExists("native/manifest/AndroidManifest.xml");
221+
zipInspector.assertFileExists("java/manifest/AndroidManifest.xml");
222+
zipInspector.assertFileExists("kotlin/manifest/AndroidManifest.xml");
223+
zipInspector.assertFileExists("initialInstall/manifest/AndroidManifest.xml");
224+
225+
String nativeManifestContent = new String(
226+
zipInspector.getFileContents("native/manifest/AndroidManifest.xml"));
227+
assertTrue(Pattern.compile(".*(split[\\u0000-\\u007F]+native).*").matcher(
228+
MoreStringsForTests.containsIgnoringPlatformNewlines(nativeManifestContent).toString()).matches());
229+
assertTrue(nativeManifestContent.contains("on-demand"));
230+
231+
String javaManifestContent = new String(
232+
zipInspector.getFileContents("java/manifest/AndroidManifest.xml"));
233+
assertTrue(Pattern.compile(".*(split[\\u0000-\\u007F]+java).*").matcher(
234+
MoreStringsForTests.containsIgnoringPlatformNewlines(javaManifestContent).toString()).matches());
235+
assertTrue(javaManifestContent.contains("install-time"));
236+
assertTrue(javaManifestContent.contains("conditions"));
237+
238+
String kotlinManifestContent = new String(
239+
zipInspector.getFileContents("kotlin/manifest/AndroidManifest.xml"));
240+
assertTrue(Pattern.compile(".*(split[\\u0000-\\u007F]+kotlin).*").matcher(
241+
MoreStringsForTests.containsIgnoringPlatformNewlines(kotlinManifestContent).toString()).matches());
242+
assertTrue(kotlinManifestContent.contains("on-demand"));
243+
244+
String initialInstallManifestContent = new String(
245+
zipInspector.getFileContents("initialInstall/manifest/AndroidManifest.xml"));
246+
assertTrue(Pattern.compile(".*(split[\\u0000-\\u007F]+initialInstall).*").matcher(
247+
MoreStringsForTests.containsIgnoringPlatformNewlines(initialInstallManifestContent).toString()).matches());
248+
assertTrue(initialInstallManifestContent.contains("install-time"));
249+
250+
// Verify base manifest should include details of all feature Manifest files
251+
String baseManifestContent = new String(zipInspector.getFileContents("base/manifest/AndroidManifest.xml"));
252+
assertTrue(baseManifestContent.contains("OnDemandNativeFeatureActivity"));
253+
assertTrue(baseManifestContent.contains("ConditionalJavaFeatureActivity"));
254+
assertTrue(baseManifestContent.contains("OnDemandKotlinFeatureActivity"));
255+
assertTrue(baseManifestContent.contains("AtInstallFeatureActivity"));
256+
}
184257
}

test/com/facebook/buck/android/BUCK

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -743,6 +743,7 @@ java_test(
743743
"//test/com/facebook/buck/step:testutil",
744744
"//test/com/facebook/buck/testutil:testutil",
745745
"//test/com/facebook/buck/testutil/integration:util",
746+
"//test/com/facebook/buck/util:testutil",
746747
"//third-party/java/bundletool:bundletool",
747748
"//third-party/java/commons-compress:commons-compress",
748749
"//third-party/java/guava:guava",
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<?xml version='1.0' encoding='utf-8'?>
2+
<manifest xmlns:android='http://schemas.android.com/apk/res/android' package='com.example'
3+
android:versionCode="1" android:versionName="1.0" xmlns:tools="http://schemas.android.com/tools"
4+
xmlns:dist="http://schemas.android.com/apk/distribution">
5+
6+
<!-- To remove multiple dist:module related tags, we are required to use removeAll merge rule marker.
7+
This change will no longer be required once ManifestMerger tool is updated from Buck source code. -->
8+
<dist:module tools:node="removeAll"/>
9+
10+
<application
11+
android:icon='@drawable/app_icon'
12+
android:label='@string/app_name'>
13+
</application>
14+
</manifest>
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
android_bundle(
2+
name = "app_dynamic_features",
3+
# Configurations needed for dynamic features: Start
4+
application_module_configs = {
5+
"kotlin": ["//kotlin/com/sample/dynamic_features/on_demand:src_release"],
6+
"java": ["//java/com/sample/dynamic_features/conditional:src_release"],
7+
"native": ["//native/dynamic_features/on_demand:src_release"],
8+
"initialInstall": ["//java/com/sample/dynamic_features/at_install:src_release"],
9+
},
10+
application_modules_with_manifest = {
11+
"kotlin",
12+
"java",
13+
"native",
14+
"initialInstall"
15+
},
16+
module_manifest_skeleton = "ModuleManifest.xml",
17+
use_dynamic_feature = True,
18+
use_split_dex = True,
19+
# Configurations needed for dynamic features: End
20+
bundle_config_file = "bundle-config.json",
21+
cpu_filters = [
22+
"x86",
23+
],
24+
keystore = "//keystores:debug",
25+
manifest_skeleton = "AndroidManifest.xml",
26+
package_type = "debug",
27+
primary_dex_patterns = [
28+
"/MyApplication^",
29+
],
30+
deps = [
31+
"//java/com/sample/app:app",
32+
"//res/com/sample/dynamic_features/base:base",
33+
"//kotlin/com/sample/dynamic_features/on_demand:src_release",
34+
"//java/com/sample/dynamic_features/conditional:src_release",
35+
"//native/dynamic_features/on_demand:src_release",
36+
"//java/com/sample/dynamic_features/at_install:src_release",
37+
],
38+
)
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<manifest
3+
xmlns:android="http://schemas.android.com/apk/res/android"
4+
package="com.example"
5+
split="${split}" android:versionCode="1"/>
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
{
2+
"compression": {},
3+
"optimizations": {
4+
"splits_config": {
5+
"split_dimension": [
6+
{
7+
"value": "ABI",
8+
"negate": false
9+
},
10+
{
11+
"value": "SCREEN_DENSITY",
12+
"negate": true
13+
},
14+
{
15+
"value": "LANGUAGE",
16+
"negate": true
17+
}
18+
]
19+
}
20+
}
21+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<?xml version='1.0' encoding='utf-8'?>
2+
<manifest xmlns:android='http://schemas.android.com/apk/res/android'
3+
xmlns:dist="http://schemas.android.com/apk/distribution"
4+
package='com.example'
5+
android:versionCode="1"
6+
android:versionName="1.0">
7+
8+
<application>
9+
<activity
10+
android:exported='true'
11+
android:name='com.sample.dynamic_features.at_install.AtInstallFeatureActivity'/>
12+
</application>
13+
14+
<!-- Configure at-install delivery -->
15+
<dist:module
16+
dist:instant="false"
17+
dist:title="@string/title_initial_install">
18+
<dist:delivery>
19+
<dist:install-time/>
20+
</dist:delivery>
21+
<dist:fusing dist:include="true"/>
22+
</dist:module>
23+
24+
</manifest>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/*
2+
* Copyright 2014-present Facebook, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License"); you may
5+
* not use this file except in compliance with the License. You may obtain
6+
* a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12+
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13+
* License for the specific language governing permissions and limitations
14+
* under the License.
15+
*/
16+
17+
package com.sample.dynamic_features.at_install;
18+
19+
import android.app.Activity;
20+
import android.os.Bundle;
21+
import android.widget.TextView;
22+
import com.sample.initialInstall.R;
23+
24+
public class AtInstallFeatureActivity extends Activity {
25+
26+
@Override
27+
protected void onCreate(Bundle savedInstanceState) {
28+
super.onCreate(savedInstanceState);
29+
setContentView(R.layout.layout_initial_install);
30+
31+
TextView tv = (TextView) findViewById(R.id.initial_install_text_view);
32+
tv.setText(R.string.initial_install_text);
33+
}
34+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
android_library(
2+
name = "src_release",
3+
srcs = glob(["*.java"]),
4+
manifest = "AndroidManifest.xml",
5+
visibility = [
6+
"PUBLIC",
7+
],
8+
deps = [
9+
"//res/com/sample/dynamic_features/at_install:initialInstall",
10+
],
11+
)

0 commit comments

Comments
 (0)