Merge "Verify updated font is used in app process." into sc-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
2c973f164b
@@ -19,6 +19,11 @@
|
||||
<!-- This test requires root to side load fs-verity cert. -->
|
||||
<target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer" />
|
||||
|
||||
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
|
||||
<option name="cleanup-apks" value="true" />
|
||||
<option name="test-file-name" value="EmojiRenderingTestApp.apk" />
|
||||
</target_preparer>
|
||||
|
||||
<target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
|
||||
<option name="cleanup" value="true" />
|
||||
<option name="push" value="UpdatableSystemFontTestCert.der->/data/local/tmp/UpdatableSystemFontTestCert.der" />
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
// Copyright (C) 2021 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.
|
||||
|
||||
package {
|
||||
// See: http://go/android-license-faq
|
||||
// A large-scale-change added 'default_applicable_licenses' to import
|
||||
// all of the 'license_kinds' from "frameworks_base_license"
|
||||
// to get the below license kinds:
|
||||
// SPDX-license-identifier-Apache-2.0
|
||||
default_applicable_licenses: ["frameworks_base_license"],
|
||||
}
|
||||
|
||||
android_test_helper_app {
|
||||
name: "EmojiRenderingTestApp",
|
||||
manifest: "AndroidManifest.xml",
|
||||
srcs: ["src/**/*.java"],
|
||||
test_suites: [
|
||||
"general-tests",
|
||||
"vts",
|
||||
],
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
* Copyright (C) 2021 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.
|
||||
-->
|
||||
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.android.emojirenderingtestapp">
|
||||
<application>
|
||||
<activity android:name=".EmojiRenderingTestActivity"/>
|
||||
</application>
|
||||
</manifest>
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright (C) 2021 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.
|
||||
*/
|
||||
|
||||
package com.android.emojirenderingtestapp;
|
||||
|
||||
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
|
||||
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.os.Bundle;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
/** Test app to render an emoji. */
|
||||
public class EmojiRenderingTestActivity extends Activity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
LinearLayout container = new LinearLayout(this);
|
||||
container.setOrientation(LinearLayout.VERTICAL);
|
||||
TextView textView = new TextView(this);
|
||||
textView.setText("\uD83E\uDD72"); // 🥲
|
||||
container.addView(textView, new LinearLayout.LayoutParams(MATCH_PARENT, WRAP_CONTENT));
|
||||
setContentView(container);
|
||||
}
|
||||
}
|
||||
@@ -36,7 +36,6 @@ import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
@@ -47,6 +46,9 @@ import java.util.regex.Pattern;
|
||||
@RunWith(DeviceJUnit4ClassRunner.class)
|
||||
public class UpdatableSystemFontTest extends BaseHostJUnit4Test {
|
||||
|
||||
private static final String SYSTEM_FONTS_DIR = "/system/fonts/";
|
||||
private static final String DATA_FONTS_DIR = "/data/fonts/files/";
|
||||
|
||||
private static final String CERT_PATH = "/data/local/tmp/UpdatableSystemFontTestCert.der";
|
||||
|
||||
private static final Pattern PATTERN_FONT = Pattern.compile("path = ([^, \n]*)");
|
||||
@@ -72,6 +74,14 @@ public class UpdatableSystemFontTest extends BaseHostJUnit4Test {
|
||||
private static final String TEST_NOTO_COLOR_EMOJI_VPLUS2_TTF_FSV_SIG =
|
||||
"/data/local/tmp/UpdatableSystemFontTestNotoColorEmojiVPlus2.ttf.fsv_sig";
|
||||
|
||||
private static final String EMOJI_RENDERING_TEST_APP_ID = "com.android.emojirenderingtestapp";
|
||||
private static final String EMOJI_RENDERING_TEST_ACTIVITY =
|
||||
EMOJI_RENDERING_TEST_APP_ID + "/.EmojiRenderingTestActivity";
|
||||
|
||||
private interface ThrowingSupplier<T> {
|
||||
T get() throws Exception;
|
||||
}
|
||||
|
||||
@Rule
|
||||
public final AddFsVerityCertRule mAddFsverityCertRule =
|
||||
new AddFsVerityCertRule(this, CERT_PATH);
|
||||
@@ -91,7 +101,10 @@ public class UpdatableSystemFontTest extends BaseHostJUnit4Test {
|
||||
expectRemoteCommandToSucceed(String.format("cmd font update %s %s",
|
||||
TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF, TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF_FSV_SIG));
|
||||
String fontPath = getFontPath(NOTO_COLOR_EMOJI_TTF);
|
||||
assertThat(fontPath).startsWith("/data/fonts/files/");
|
||||
assertThat(fontPath).startsWith(DATA_FONTS_DIR);
|
||||
// The updated font should be readable and unmodifiable.
|
||||
expectRemoteCommandToSucceed("cat " + fontPath + " > /dev/null");
|
||||
expectRemoteCommandToFail("echo -n '' >> " + fontPath);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -102,8 +115,12 @@ public class UpdatableSystemFontTest extends BaseHostJUnit4Test {
|
||||
expectRemoteCommandToSucceed(String.format("cmd font update %s %s",
|
||||
TEST_NOTO_COLOR_EMOJI_VPLUS2_TTF, TEST_NOTO_COLOR_EMOJI_VPLUS2_TTF_FSV_SIG));
|
||||
String fontPath2 = getFontPath(NOTO_COLOR_EMOJI_TTF);
|
||||
assertThat(fontPath2).startsWith("/data/fonts/files/");
|
||||
assertThat(fontPath2).startsWith(DATA_FONTS_DIR);
|
||||
assertThat(fontPath2).isNotEqualTo(fontPath);
|
||||
// The new file should be readable.
|
||||
expectRemoteCommandToSucceed("cat " + fontPath2 + " > /dev/null");
|
||||
// The old file should be still readable.
|
||||
expectRemoteCommandToSucceed("cat " + fontPath + " > /dev/null");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -119,24 +136,13 @@ public class UpdatableSystemFontTest extends BaseHostJUnit4Test {
|
||||
expectRemoteCommandToSucceed(String.format("cmd font update %s %s",
|
||||
TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF, TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF_FSV_SIG));
|
||||
String fontPath3 = getFontPath(NOTO_COLOR_EMOJI_TTF);
|
||||
assertThat(fontPath).startsWith("/data/fonts/files/");
|
||||
assertThat(fontPath).startsWith(DATA_FONTS_DIR);
|
||||
assertThat(fontPath2).isNotEqualTo(fontPath);
|
||||
assertThat(fontPath2).startsWith("/data/fonts/files/");
|
||||
assertThat(fontPath3).startsWith("/data/fonts/files/");
|
||||
assertThat(fontPath2).startsWith(DATA_FONTS_DIR);
|
||||
assertThat(fontPath3).startsWith(DATA_FONTS_DIR);
|
||||
assertThat(fontPath3).isNotEqualTo(fontPath);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updatedFont_dataFileIsImmutableAndReadable() throws Exception {
|
||||
expectRemoteCommandToSucceed(String.format("cmd font update %s %s",
|
||||
TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF, TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF_FSV_SIG));
|
||||
String fontPath = getFontPath(NOTO_COLOR_EMOJI_TTF);
|
||||
assertThat(fontPath).startsWith("/data");
|
||||
|
||||
expectRemoteCommandToFail("echo -n '' >> " + fontPath);
|
||||
expectRemoteCommandToSucceed("cat " + fontPath + " > /dev/null");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateFont_invalidCert() throws Exception {
|
||||
expectRemoteCommandToFail(String.format("cmd font update %s %s",
|
||||
@@ -157,12 +163,38 @@ public class UpdatableSystemFontTest extends BaseHostJUnit4Test {
|
||||
TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF, TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF_FSV_SIG));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void launchApp() throws Exception {
|
||||
String fontPath = getFontPath(NOTO_COLOR_EMOJI_TTF);
|
||||
assertThat(fontPath).startsWith(SYSTEM_FONTS_DIR);
|
||||
expectRemoteCommandToSucceed("am force-stop " + EMOJI_RENDERING_TEST_APP_ID);
|
||||
expectRemoteCommandToSucceed("am start-activity -n " + EMOJI_RENDERING_TEST_ACTIVITY);
|
||||
waitUntil(TimeUnit.SECONDS.toMillis(5), () ->
|
||||
isFileOpenedBy(fontPath, EMOJI_RENDERING_TEST_APP_ID));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void launchApp_afterUpdateFont() throws Exception {
|
||||
String originalFontPath = getFontPath(NOTO_COLOR_EMOJI_TTF);
|
||||
assertThat(originalFontPath).startsWith(SYSTEM_FONTS_DIR);
|
||||
expectRemoteCommandToSucceed(String.format("cmd font update %s %s",
|
||||
TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF, TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF_FSV_SIG));
|
||||
String updatedFontPath = getFontPath(NOTO_COLOR_EMOJI_TTF);
|
||||
assertThat(updatedFontPath).startsWith(DATA_FONTS_DIR);
|
||||
expectRemoteCommandToSucceed("am force-stop " + EMOJI_RENDERING_TEST_APP_ID);
|
||||
expectRemoteCommandToSucceed("am start-activity -n " + EMOJI_RENDERING_TEST_ACTIVITY);
|
||||
// The original font should NOT be opened by the app.
|
||||
waitUntil(TimeUnit.SECONDS.toMillis(5), () ->
|
||||
isFileOpenedBy(updatedFontPath, EMOJI_RENDERING_TEST_APP_ID)
|
||||
&& !isFileOpenedBy(originalFontPath, EMOJI_RENDERING_TEST_APP_ID));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void reboot() throws Exception {
|
||||
expectRemoteCommandToSucceed(String.format("cmd font update %s %s",
|
||||
TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF, TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF_FSV_SIG));
|
||||
String fontPath = getFontPath(NOTO_COLOR_EMOJI_TTF);
|
||||
assertThat(fontPath).startsWith("/data/fonts/files/");
|
||||
assertThat(fontPath).startsWith(DATA_FONTS_DIR);
|
||||
|
||||
expectRemoteCommandToSucceed("stop");
|
||||
expectRemoteCommandToSucceed("start");
|
||||
@@ -210,16 +242,40 @@ public class UpdatableSystemFontTest extends BaseHostJUnit4Test {
|
||||
});
|
||||
}
|
||||
|
||||
private void waitUntil(long timeoutMillis, Supplier<Boolean> func) {
|
||||
private void waitUntil(long timeoutMillis, ThrowingSupplier<Boolean> func) {
|
||||
long untilMillis = System.currentTimeMillis() + timeoutMillis;
|
||||
do {
|
||||
if (func.get()) return;
|
||||
try {
|
||||
if (func.get()) return;
|
||||
Thread.sleep(100);
|
||||
} catch (InterruptedException e) {
|
||||
throw new AssertionError("Interrupted", e);
|
||||
} catch (Exception e) {
|
||||
throw new AssertionError("Unexpected exception", e);
|
||||
}
|
||||
} while (System.currentTimeMillis() < untilMillis);
|
||||
throw new AssertionError("Timed out");
|
||||
}
|
||||
|
||||
private boolean isFileOpenedBy(String path, String appId) throws DeviceNotAvailableException {
|
||||
String pid = pidOf(appId);
|
||||
if (pid.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
CommandResult result = getDevice().executeShellV2Command(
|
||||
String.format("lsof -t -p %s '%s'", pid, path));
|
||||
if (result.getStatus() != CommandStatus.SUCCESS) {
|
||||
return false;
|
||||
}
|
||||
// The file is open if the output of lsof is non-empty.
|
||||
return !result.getStdout().trim().isEmpty();
|
||||
}
|
||||
|
||||
private String pidOf(String appId) throws DeviceNotAvailableException {
|
||||
CommandResult result = getDevice().executeShellV2Command("pidof " + appId);
|
||||
if (result.getStatus() != CommandStatus.SUCCESS) {
|
||||
return "";
|
||||
}
|
||||
return result.getStdout().trim();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user