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

Commit

Permalink
Android App Bundles - Add test cases for dynamic feature modules (#2609)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
uKetki authored May 17, 2021
1 parent 6afcd69 commit ddf2941
Show file tree
Hide file tree
Showing 40 changed files with 568 additions and 4 deletions.
8 changes: 4 additions & 4 deletions .idea/vcs.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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();
Expand Down Expand Up @@ -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"));
}
}
1 change: 1 addition & 0 deletions test/com/facebook/buck/android/BUCK
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?xml version='1.0' encoding='utf-8'?>
<manifest xmlns:android='http://schemas.android.com/apk/res/android' package='com.example'
android:versionCode="1" android:versionName="1.0" xmlns:tools="http://schemas.android.com/tools"
xmlns:dist="http://schemas.android.com/apk/distribution">

<!-- To remove multiple dist:module related tags, we are required to use removeAll merge rule marker.
This change will no longer be required once ManifestMerger tool is updated from Buck source code. -->
<dist:module tools:node="removeAll"/>

<application
android:icon='@drawable/app_icon'
android:label='@string/app_name'>
</application>
</manifest>
Original file line number Diff line number Diff line change
@@ -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",
],
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example"
split="${split}" android:versionCode="1"/>
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"compression": {},
"optimizations": {
"splits_config": {
"split_dimension": [
{
"value": "ABI",
"negate": false
},
{
"value": "SCREEN_DENSITY",
"negate": true
},
{
"value": "LANGUAGE",
"negate": true
}
]
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?xml version='1.0' encoding='utf-8'?>
<manifest xmlns:android='http://schemas.android.com/apk/res/android'
xmlns:dist="http://schemas.android.com/apk/distribution"
package='com.example'
android:versionCode="1"
android:versionName="1.0">

<application>
<activity
android:exported='true'
android:name='com.sample.dynamic_features.at_install.AtInstallFeatureActivity'/>
</application>

<!-- Configure at-install delivery -->
<dist:module
dist:instant="false"
dist:title="@string/title_initial_install">
<dist:delivery>
<dist:install-time/>
</dist:delivery>
<dist:fusing dist:include="true"/>
</dist:module>

</manifest>
Original file line number Diff line number Diff line change
@@ -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);
}
}
Original file line number Diff line number Diff line change
@@ -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",
],
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<?xml version='1.0' encoding='utf-8'?>
<manifest xmlns:android='http://schemas.android.com/apk/res/android'
xmlns:dist="http://schemas.android.com/apk/distribution"
package='com.example'
android:versionCode="1"
android:versionName="1.0">

<application>
<activity
android:exported='true'
android:name='com.sample.dynamic_features.conditional.ConditionalJavaFeatureActivity'/>
</application>

<!-- Configure conditional delivery -->
<dist:module
dist:instant="false"
dist:title="@string/title_java">
<dist:delivery>
<dist:install-time>
<dist:conditions>
<!-- If you specify conditions, as described in the steps
below, the IDE includes them here. -->
<dist:min-sdk dist:value="21"/>
<dist:max-sdk dist:value="24"/>
</dist:conditions>
</dist:install-time>
</dist:delivery>
<dist:fusing dist:include="true"/>
</dist:module>

</manifest>
Original file line number Diff line number Diff line change
@@ -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",
],
)
Original file line number Diff line number Diff line change
@@ -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);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?xml version='1.0' encoding='utf-8'?>
<manifest xmlns:android='http://schemas.android.com/apk/res/android'
xmlns:dist="http://schemas.android.com/apk/distribution"
package='com.example'
android:versionCode="1"
android:versionName="1.0">

<application>
<activity
android:exported='true'
android:name='com.sample.dynamic_features.on_demand.OnDemandKotlinFeatureActivity'/>
</application>

<!-- Configure on demand delivery -->
<dist:module
dist:instant="false"
dist:title="@string/title_kotlin">
<dist:delivery>
<dist:on-demand/>
</dist:delivery>
<dist:fusing dist:include="true"/>
</dist:module>

</manifest>
Original file line number Diff line number Diff line change
@@ -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",
],
)
Loading

0 comments on commit ddf2941

Please sign in to comment.