CTS Test for quota exceeded callback in new Backup API.
Test creates large file using helper app, runs backup for it,
check that the callback was called.
Bug: 28061809
Change-Id: Ibfb95129df459ce124286a602ddb61fafa8cf2fc
diff --git a/OldCtsTestCaseList.mk b/OldCtsTestCaseList.mk
index 2e7de5e..577dcdf 100644
--- a/OldCtsTestCaseList.mk
+++ b/OldCtsTestCaseList.mk
@@ -86,6 +86,7 @@
CtsAssistService \
CtsAssistApp \
CtsAtraceTestApp \
+ CtsBackupApp \
CtsCertInstallerApp \
CtsContactDirectoryProvider \
CtsCustomizationApp \
@@ -154,6 +155,7 @@
CtsAppTestCases \
CtsAppWidgetTestCases \
CtsAssistTestCases \
+ CtsBackupTestCases \
CtsBluetoothTestCases \
CtsBrowserTestCases \
CtsCalendarcommon2TestCases \
diff --git a/tests/backup/Android.mk b/tests/backup/Android.mk
new file mode 100644
index 0000000..93e55aa
--- /dev/null
+++ b/tests/backup/Android.mk
@@ -0,0 +1,41 @@
+# Copyright (C) 2016 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.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+# don't include this package in any target
+LOCAL_MODULE_TAGS := optional
+# and when built explicitly put it in the data partition
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+
+LOCAL_JAVA_LIBRARIES := android.test.runner telephony-common voip-common org.apache.http.legacy
+
+LOCAL_STATIC_JAVA_LIBRARIES := ctsdeviceutil ctstestrunner ctstestserver mockito-target
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+# Tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts
+
+LOCAL_PACKAGE_NAME := CtsBackupTestCases
+
+LOCAL_CTS_MODULE_CONFIG := $(LOCAL_PATH)/Old$(CTS_MODULE_TEST_CONFIG)
+
+LOCAL_SDK_VERSION := test_current
+
+include $(BUILD_CTS_PACKAGE)
+
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/tests/backup/AndroidManifest.xml b/tests/backup/AndroidManifest.xml
new file mode 100644
index 0000000..333045a
--- /dev/null
+++ b/tests/backup/AndroidManifest.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2016 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="android.backup.cts">
+
+ <application>
+ <uses-library android:name="android.test.runner" />
+ </application>
+
+ <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+ android:targetPackage="android.backup.cts"
+ android:label="CTS tests of Android Backup/Restore">
+ <meta-data android:name="listener"
+ android:value="com.android.cts.runner.CtsTestRunListener" />
+ </instrumentation>
+
+</manifest>
+
diff --git a/tests/backup/AndroidTest.xml b/tests/backup/AndroidTest.xml
new file mode 100644
index 0000000..ce5d261
--- /dev/null
+++ b/tests/backup/AndroidTest.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2016 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
+ -->
+<configuration description="Config for CTS Backup test cases">
+ <target_preparer class="com.android.compatibility.common.tradefed.targetprep.ApkInstaller">
+ <option name="cleanup-apks" value="true" />
+ <option name="test-file-name" value="CtsBackupApp.apk" />
+ <option name="test-file-name" value="CtsBackupTestCases.apk" />
+ </target_preparer>
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+ <option name="package" value="android.backup.cts" />
+ </test>
+</configuration>
diff --git a/tests/backup/app/Android.mk b/tests/backup/app/Android.mk
new file mode 100644
index 0000000..cffb1d2
--- /dev/null
+++ b/tests/backup/app/Android.mk
@@ -0,0 +1,34 @@
+# Copyright (C) 2016 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.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_PACKAGE_NAME := CtsBackupApp
+
+# Tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts
+
+LOCAL_STATIC_JAVA_LIBRARIES := \
+ ctsdeviceutil \
+ ctstestrunner
+
+include $(BUILD_CTS_SUPPORT_PACKAGE)
diff --git a/tests/backup/app/AndroidManifest.xml b/tests/backup/app/AndroidManifest.xml
new file mode 100644
index 0000000..1507bc2
--- /dev/null
+++ b/tests/backup/app/AndroidManifest.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2016 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="android.backup.app" >
+
+ <application
+ android:allowBackup="true"
+ android:backupAgent="BackupCtsBackupAgent"
+ android:label="Android Backup CTS App"
+ android:fullBackupOnly="true">
+ <activity
+ android:name=".MainActivity"
+ android:label="Android Backup CTS App" >
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ </application>
+</manifest>
diff --git a/tests/backup/app/src/android/backup/app/BackupCtsBackupAgent.java b/tests/backup/app/src/android/backup/app/BackupCtsBackupAgent.java
new file mode 100644
index 0000000..f6fb8c1
--- /dev/null
+++ b/tests/backup/app/src/android/backup/app/BackupCtsBackupAgent.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2016 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 android.backup.app;
+
+import android.app.backup.BackupAgent;
+import android.app.backup.BackupDataInput;
+import android.app.backup.BackupDataOutput;
+import android.app.backup.FullBackupDataOutput;
+import android.os.ParcelFileDescriptor;
+import android.util.Log;
+
+import java.io.IOException;
+
+/*
+ * Backup agent for Backup CTS App.
+ *
+ * Logs callbacks into logcat.
+ */
+public class BackupCtsBackupAgent extends BackupAgent {
+
+ @Override
+ public void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data,
+ ParcelFileDescriptor newState) throws IOException {
+ Log.d(MainActivity.TAG, "Backup requested");
+ }
+
+ @Override
+ public void onRestore(BackupDataInput data, int appVersionCode,
+ ParcelFileDescriptor newState) throws IOException {
+ Log.d(MainActivity.TAG, "Restore requested");
+ }
+
+ @Override
+ public void onFullBackup(FullBackupDataOutput data) throws IOException {
+ Log.d(MainActivity.TAG, "Full backup requested");
+ super.onFullBackup(data);
+ }
+
+ @Override
+ public void onQuotaExceeded(long backupDataBytes, long quotaBytes) {
+ Log.d(MainActivity.TAG, "Quota exceeded!");
+ }
+}
diff --git a/tests/backup/app/src/android/backup/app/MainActivity.java b/tests/backup/app/src/android/backup/app/MainActivity.java
new file mode 100644
index 0000000..93c6dd6
--- /dev/null
+++ b/tests/backup/app/src/android/backup/app/MainActivity.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2016 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 android.backup.app;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.util.Log;
+
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.Random;
+
+/**
+ * The activity could be invoked to create a test file.
+ *
+ * Here is an example of a call:
+ *
+ * am start -a android.intent.action.MAIN \
+ * -c android.intent.category.LAUNCHER \
+ * -n android.backup.app/android.backup.app.MainActivity \
+ * -e file_size 10000000
+ *
+ * "File created!" string is printed in logcat when file is created.
+ */
+public class MainActivity extends Activity {
+ public static final String TAG = "BackupCTSApp";
+
+ private static final String FILE_NAME = "file_name";
+ private static final String FILE_SIZE_EXTRA = "file_size";
+ private static final int DATA_CHUNK_SIZE = 1024 * 1024;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceActivity) {
+ super.onCreate(savedInstanceActivity);
+ if (getIntent().hasExtra(FILE_SIZE_EXTRA)) {
+ int fileSize = Integer.parseInt(getIntent().getStringExtra(FILE_SIZE_EXTRA));
+ Log.i(TAG, "Creating file of size: " + fileSize);
+ try {
+ createFile(fileSize);
+ Log.d(TAG, "File created!");
+ } catch (IOException e) {
+ Log.e(TAG, "IOException: " + e);
+ }
+ } else {
+ Log.d(TAG, "No file size was provided");
+ }
+ }
+
+ private void createFile(int size) throws IOException {
+ byte[] bytes = new byte[DATA_CHUNK_SIZE];
+ new Random().nextBytes(bytes);
+ File f = new File(getFilesDir().getAbsolutePath(), FILE_NAME);
+ f.getParentFile().mkdirs();
+ try (BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(f))) {
+ while (size > 0) {
+ int bytesToWrite = Math.min(size, DATA_CHUNK_SIZE);
+ bos.write(bytes, 0, bytesToWrite);
+ size -= bytesToWrite;
+ }
+ }
+ }
+}
diff --git a/tests/backup/src/android/backup/cts/BackupQuotaTest.java b/tests/backup/src/android/backup/cts/BackupQuotaTest.java
new file mode 100644
index 0000000..6122ad0
--- /dev/null
+++ b/tests/backup/src/android/backup/cts/BackupQuotaTest.java
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2016 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 android.backup.cts;
+
+import android.app.Instrumentation;
+import android.os.ParcelFileDescriptor;
+import android.test.InstrumentationTestCase;
+import android.util.Log;
+
+import java.io.BufferedReader;
+import java.io.FileInputStream;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.nio.charset.StandardCharsets;
+import java.util.concurrent.TimeUnit;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Verifies receiving quotaExceeded() callback on full backup.
+ *
+ * Uses test app that creates large file and receives the callback.
+ * {@link com.android.internal.backup.LocalTransport} is used, it has size quota 25MB.
+ */
+public class BackupQuotaTest extends InstrumentationTestCase {
+ private static final String APP_LOG_TAG = "BackupCTSApp";
+
+ private static final String LOCAL_TRANSPORT =
+ "android/com.android.internal.backup.LocalTransport";
+ private static final int LOCAL_TRANSPORT_EXCEEDING_FILE_SIZE = 30 * 1024 * 1024;
+ private static final String BACKUP_APP_NAME = "android.backup.app";
+
+ private static final int SMALL_LOGCAT_DELAY = 1000;
+
+ private boolean wasBackupEnabled;
+ private String oldTransport;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ // Enable backup and select local backup transport
+ assertTrue("LocalTransport should be available.", hasBackupTransport(LOCAL_TRANSPORT));
+ wasBackupEnabled = enableBackup(true);
+ oldTransport = setBackupTransport(LOCAL_TRANSPORT);
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ // Return old transport
+ setBackupTransport(oldTransport);
+ enableBackup(wasBackupEnabled);
+ super.tearDown();
+ }
+
+ public void testQuotaExceeded() throws Exception {
+ exec("logcat --clear");
+ exec("setprop log.tag." + APP_LOG_TAG +" VERBOSE");
+ // Launch test app and create file exceeding limit for local transport
+ exec("am start -W -a android.intent.action.MAIN " +
+ "-c android.intent.category.LAUNCHER " +
+ "-n " + BACKUP_APP_NAME + "/" + BACKUP_APP_NAME +".MainActivity " +
+ "-e file_size " + LOCAL_TRANSPORT_EXCEEDING_FILE_SIZE);
+ assertTrue("File was not created", waitForLogcat("File created!", 30));
+
+ // Request backup and wait for quota exceeded event in logcat
+ exec("bmgr backupnow " + BACKUP_APP_NAME);
+ assertTrue("Quota exceeded event is not received", waitForLogcat("Quota exceeded!", 10));
+ }
+
+ private boolean enableBackup(boolean enable) throws Exception {
+ boolean previouslyEnabled;
+ String output = exec("bmgr enabled");
+ Pattern pattern = Pattern.compile("^Backup Manager currently (enabled|disabled)$");
+ Matcher matcher = pattern.matcher(output.trim());
+ if (matcher.find()) {
+ previouslyEnabled = "enabled".equals(matcher.group(1));
+ } else {
+ throw new RuntimeException("non-parsable output setting bmgr enabled: " + output);
+ }
+
+ exec("bmgr enable " + enable);
+ return previouslyEnabled;
+ }
+
+ private String setBackupTransport(String transport) throws Exception {
+ String output = exec("bmgr transport " + transport);
+ Pattern pattern = Pattern.compile("\\(formerly (.*)\\)$");
+ Matcher matcher = pattern.matcher(output);
+ if (matcher.find()) {
+ return matcher.group(1);
+ } else {
+ throw new RuntimeException("non-parsable output setting bmgr transport: " + output);
+ }
+ }
+
+ private boolean hasBackupTransport(String transport) throws Exception {
+ String output = exec("bmgr list transports");
+ for (String t : output.split(" ")) {
+ if (transport.equals(t)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private boolean waitForLogcat(String logcatString, int maxTimeoutInSeconds) throws Exception {
+ long timeout = System.currentTimeMillis() + TimeUnit.SECONDS.toMillis(maxTimeoutInSeconds);
+ while (timeout >= System.currentTimeMillis()) {
+ FileInputStream fis = executeStreamedShellCommand(getInstrumentation(),
+ "logcat -v brief -d " + APP_LOG_TAG + ":* *:S");
+ BufferedReader log = new BufferedReader(new InputStreamReader(fis));
+ String line;
+ while ((line = log.readLine()) != null) {
+ if (line.contains(logcatString)) {
+ return true;
+ }
+ }
+ closeQuietly(log);
+ // In case the key has not been found, wait for the log to update before
+ // performing the next search.
+ Thread.sleep(SMALL_LOGCAT_DELAY);
+ }
+ return false;
+ }
+
+ private String exec(String command) throws Exception {
+ BufferedReader br = null;
+ try (InputStream in = executeStreamedShellCommand(getInstrumentation(), command)) {
+ br = new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8));
+ String str = null;
+ StringBuilder out = new StringBuilder();
+ while ((str = br.readLine()) != null) {
+ out.append(str);
+ }
+ return out.toString();
+ } finally {
+ if (br != null) {
+ closeQuietly(br);
+ }
+ }
+ }
+
+ private static FileInputStream executeStreamedShellCommand(Instrumentation instrumentation,
+ String command) throws Exception {
+ final ParcelFileDescriptor pfd =
+ instrumentation.getUiAutomation().executeShellCommand(command);
+ return new FileInputStream(pfd.getFileDescriptor());
+ }
+
+ private static void closeQuietly(AutoCloseable closeable) {
+ if (closeable != null) {
+ try {
+ closeable.close();
+ } catch (RuntimeException rethrown) {
+ throw rethrown;
+ } catch (Exception ignored) {
+ }
+ }
+ }
+}