Merge "Camera: improve accuracy of sensor fusion test" into nyc-mr1-dev
diff --git a/apps/CameraITS/tests/scene0/test_metadata.py b/apps/CameraITS/tests/scene0/test_metadata.py
index ed1426b..e5fbba5 100644
--- a/apps/CameraITS/tests/scene0/test_metadata.py
+++ b/apps/CameraITS/tests/scene0/test_metadata.py
@@ -97,9 +97,12 @@
print "Assert field of view: %.1f degrees" % fov
assert 30 <= fov <= 130
- hyperfocal = 1.0 / props["android.lens.info.hyperfocalDistance"]
- print "Assert hyperfocal distance: %.2f m" % hyperfocal
- assert 0.02 <= hyperfocal
+ if its.caps.lens_approx_calibrated(props):
+ diopter_hyperfocal = props["android.lens.info.hyperfocalDistance"]
+ if diopter_hyperfocal != 0.0:
+ hyperfocal = 1.0 / diopter_hyperfocal
+ print "Assert hyperfocal distance: %.2f m" % hyperfocal
+ assert 0.02 <= hyperfocal
def getval(expr, default=None):
diff --git a/apps/CtsVerifier/AndroidManifest.xml b/apps/CtsVerifier/AndroidManifest.xml
index 824ab67..6f29335 100644
--- a/apps/CtsVerifier/AndroidManifest.xml
+++ b/apps/CtsVerifier/AndroidManifest.xml
@@ -1246,6 +1246,18 @@
</intent-filter>
</service>
+ <activity android:name=".notifications.ShortcutThrottlingResetActivity"
+ android:label="@string/shortcut_reset_test"
+ android:configChanges="mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|screenLayout|fontScale|uiMode|orientation|screenSize|smallestScreenSize|layoutDirection">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.cts.intent.category.MANUAL_TEST" />
+ </intent-filter>
+ <meta-data android:name="test_category" android:value="@string/test_category_notifications" />
+ <meta-data android:name="test_excluded_features"
+ android:value="android.hardware.type.watch:android.software.leanback" />
+ </activity>
+
<activity android:name=".vr.VrListenerVerifierActivity"
android:label="@string/vr_tests">
<intent-filter>
diff --git a/apps/CtsVerifier/res/values/strings.xml b/apps/CtsVerifier/res/values/strings.xml
index 2406b84..d6b0f47 100644
--- a/apps/CtsVerifier/res/values/strings.xml
+++ b/apps/CtsVerifier/res/values/strings.xml
@@ -1204,6 +1204,16 @@
<string name="package_priority_default">Find \"%s\" in the \"Notifications\" settings panel, and disallow it to Override Do Not Disturb.</string>
<string name="package_priority_user_order">Check that ranker respects user priorities.</string>
+ <string name="shortcut_reset_test">Shortcut Reset Rate-limiting Test</string>
+ <string name="shortcut_reset_info">This test checks that when an inline-reply happens, ShortcutManager\'s rate-limiting
+ is reset.</string>
+ <string name="shortcut_reset_bot">Verifying that the CTS Robot helper package is installed.</string>
+ <string name="shortcut_reset_start">Showing the notification.</string>
+ <string name="shortcut_reset_prompt_inline_reply">Open the notification shade,
+ find the \"Shortcut Reset Rate-limiting Test\" notification, type something in the inline-reply field and
+ press the send button.</string>
+ <string name="shortcut_reset_check_result">Check ShortcutManager rate-limit has been reset.</string>
+
<string name="attention_test">Notification Attention Management Test</string>
<string name="attention_info">This test checks that the NotificationManagerService is
respecting user preferences about notification ranking and filtering.
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/PackagePriorityVerifierActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/PackagePriorityVerifierActivity.java
index 5870981..b40ecc6 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/PackagePriorityVerifierActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/PackagePriorityVerifierActivity.java
@@ -39,7 +39,7 @@
private static final String ACTION_CANCEL = "com.android.cts.robot.ACTION_CANCEL";
private static final String EXTRA_ID = "ID";
private static final String EXTRA_NOTIFICATION = "NOTIFICATION";
- private static final String NOTIFICATION_BOT_PACKAGE = "com.android.cts.robot";
+ static final String NOTIFICATION_BOT_PACKAGE = "com.android.cts.robot";
private CharSequence mAppLabel;
@Override
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/ShortcutThrottlingResetActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/ShortcutThrottlingResetActivity.java
new file mode 100644
index 0000000..313ebfa
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/ShortcutThrottlingResetActivity.java
@@ -0,0 +1,207 @@
+/*
+ * 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 com.android.cts.verifier.notifications;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.View;
+import android.view.ViewGroup;
+
+import com.android.cts.verifier.R;
+
+import java.security.SecureRandom;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicReference;
+
+/**
+ * Test to make sure, when an inline reply happens, the shortcut manager rate-limiting must
+ * be reset.
+ *
+ * We use the "BOT" apk here, because rate-limiting will be reset when an app shows an activity
+ * too -- so as long as this (or any) test activity is shown, CTS verifier won't be rate-limited.
+ */
+public class ShortcutThrottlingResetActivity extends InteractiveVerifierActivity {
+ private static final String TAG = "ShortcutThrottlingReset";
+
+ private static final String NOTIFICATION_BOT_PACKAGE
+ = PackagePriorityVerifierActivity.NOTIFICATION_BOT_PACKAGE;
+
+ private static final String ACTION_RESET_SETUP_NOTIFICATION =
+ "com.android.cts.robot.ACTION_RESET_SETUP_NOTIFICATION";
+
+ private static final String EXTRA_NOTIFICATION_TITLE = "EXTRA_NOTIFICATION_TITLE";
+ private static final String EXTRA_RESET_REPLY_PACKAGE = "EXTRA_RESET_REPLY_PACKAGE";
+ private static final String EXTRA_RESET_REPLY_ACTION = "EXTRA_RESET_REPLY_ACTION";
+ private static final String EXTRA_RESET_REPLY_ERROR = "EXTRA_RESET_REPLY_ERROR";
+
+ private static final String SUCCESS = "**SUCCESS**";
+
+ private String mReplyAction;
+
+ private final AtomicReference<Intent> mReplyIntent = new AtomicReference<>(null);
+
+ @Override
+ int getTitleResource() {
+ return R.string.shortcut_reset_test;
+ }
+
+ @Override
+ int getInstructionsResource() {
+ return R.string.shortcut_reset_info;
+ }
+
+ @Override
+ protected void onCreate(Bundle savedState) {
+ super.onCreate(savedState);
+
+ // Generate an unique reply action and register the reply receiver.
+ mReplyAction = "reply_" + new SecureRandom().nextLong();
+ final IntentFilter replyFilter = new IntentFilter(mReplyAction);
+ registerReceiver(mReplyReceiver, replyFilter);
+ }
+
+ @Override
+ protected void onDestroy() {
+ unregisterReceiver(mReplyReceiver);
+ super.onDestroy();
+ }
+
+ @Override
+ protected List<InteractiveTestCase> createTestItems() {
+ List<InteractiveTestCase> tests = new ArrayList<>();
+ tests.add(new CheckForBot());
+ tests.add(new SetupNotification());
+ tests.add(new WaitForTestReply());
+ tests.add(new CheckResult());
+ return tests;
+ }
+
+
+ private final BroadcastReceiver mReplyReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ Log.i(TAG, "Received reply from robot helper: " + intent);
+ mReplyIntent.set(intent);
+ }
+ };
+
+
+ /** Make sure the helper package is installed. */
+ protected class CheckForBot extends InteractiveTestCase {
+ @Override
+ View inflate(ViewGroup parent) {
+ return createAutoItem(parent, R.string.shortcut_reset_bot);
+ }
+
+ @Override
+ void test() {
+ PackageManager pm = mContext.getPackageManager();
+ try {
+ pm.getPackageInfo(NOTIFICATION_BOT_PACKAGE, 0);
+ status = PASS;
+ } catch (PackageManager.NameNotFoundException e) {
+ status = FAIL;
+ logFail("You must install the CTS Robot helper, aka " + NOTIFICATION_BOT_PACKAGE);
+ }
+ next();
+ }
+ }
+
+ /**
+ * Request the bot apk to show the notification.
+ */
+ protected class SetupNotification extends InteractiveTestCase {
+ @Override
+ View inflate(ViewGroup parent) {
+ return createAutoItem(parent, R.string.shortcut_reset_start);
+ }
+
+ @Override
+ void test() {
+ final Intent intent = new Intent(ACTION_RESET_SETUP_NOTIFICATION);
+ intent.setPackage(NOTIFICATION_BOT_PACKAGE);
+
+ intent.putExtra(EXTRA_NOTIFICATION_TITLE, getResources().getString(getTitleResource()));
+
+ intent.putExtra(EXTRA_RESET_REPLY_PACKAGE, getPackageName());
+ intent.putExtra(EXTRA_RESET_REPLY_ACTION, mReplyAction);
+
+ intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES);
+ sendBroadcast(intent);
+ status = PASS;
+ next();
+ }
+ }
+
+ /**
+ * Let the human tester do an inline reply, and wait for the reply broadcast from the bot apk.
+ */
+ protected class WaitForTestReply extends InteractiveTestCase {
+ @Override
+ View inflate(ViewGroup parent) {
+ return createAutoItem(parent, R.string.shortcut_reset_prompt_inline_reply);
+ }
+
+ @Override
+ void test() {
+ final Intent replyIntent = mReplyIntent.get();
+ if (replyIntent == null) {
+ // Reply not received yet.
+ status = RETEST;
+ delay();
+ return;
+ }
+ status = PASS;
+ next();
+ }
+ }
+
+ /**
+ * Check the reply from the bot apk.
+ */
+ protected class CheckResult extends InteractiveTestCase {
+ @Override
+ View inflate(ViewGroup parent) {
+ return createAutoItem(parent, R.string.shortcut_reset_check_result);
+ }
+
+ @Override
+ void test() {
+ final Intent replyIntent = mReplyIntent.get();
+ if (replyIntent == null) {
+ logFail("Internal error, replyIntent shouldn't be null here.");
+ status = FAIL;
+ return;
+ }
+ final String error = replyIntent.getStringExtra(EXTRA_RESET_REPLY_ERROR);
+ if (SUCCESS.equals(error)) {
+ status = PASS;
+ next();
+ return;
+ }
+ logFail("Test failed. Error message=" + error);
+ status = FAIL;
+ next();
+ }
+ }
+}
diff --git a/apps/NotificationBot/AndroidManifest.xml b/apps/NotificationBot/AndroidManifest.xml
index b63791f..0388cbc 100644
--- a/apps/NotificationBot/AndroidManifest.xml
+++ b/apps/NotificationBot/AndroidManifest.xml
@@ -39,10 +39,10 @@
<intent-filter>
<action android:name="com.android.cts.robot.ACTION_POST" />
<action android:name="com.android.cts.robot.ACTION_CANCEL" />
+ <action android:name="com.android.cts.robot.ACTION_RESET_SETUP_NOTIFICATION" />
+ <action android:name="com.android.cts.robot.ACTION_INLINE_REPLY" />
</intent-filter>
</receiver>
-
-
</application>
</manifest>
diff --git a/apps/NotificationBot/src/com/android/cts/robot/NotificationBot.java b/apps/NotificationBot/src/com/android/cts/robot/NotificationBot.java
index 2aa5f41..746b840 100644
--- a/apps/NotificationBot/src/com/android/cts/robot/NotificationBot.java
+++ b/apps/NotificationBot/src/com/android/cts/robot/NotificationBot.java
@@ -15,14 +15,23 @@
*/
package com.android.cts.robot;
-import android.app.Activity;
import android.app.Notification;
+import android.app.Notification.Action;
import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.app.RemoteInput;
import android.content.BroadcastReceiver;
+import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.ShortcutInfo;
+import android.content.pm.ShortcutManager;
+import android.os.SystemClock;
import android.util.Log;
+import java.util.ArrayList;
+import java.util.List;
+
public class NotificationBot extends BroadcastReceiver {
private static final String TAG = "NotificationBot";
@@ -30,6 +39,21 @@
private static final String EXTRA_NOTIFICATION = "NOTIFICATION";
private static final String ACTION_POST = "com.android.cts.robot.ACTION_POST";
private static final String ACTION_CANCEL = "com.android.cts.robot.ACTION_CANCEL";
+ private static final String ACTION_RESET_SETUP_NOTIFICATION =
+ "com.android.cts.robot.ACTION_RESET_SETUP_NOTIFICATION";
+
+ private static final String ACTION_INLINE_REPLY =
+ "com.android.cts.robot.ACTION_INLINE_REPLY";
+
+ private static final String EXTRA_RESET_REPLY_PACKAGE = "EXTRA_RESET_REPLY_PACKAGE";
+ private static final String EXTRA_RESET_REPLY_ACTION = "EXTRA_RESET_REPLY_ACTION";
+ private static final String EXTRA_NOTIFICATION_TITLE = "EXTRA_NOTIFICATION_TITLE";
+
+ private static final String EXTRA_RESET_REPLY_ERROR = "EXTRA_RESET_REPLY_ERROR";
+
+ private static final String EXTRA_RESET_REQUEST_INTENT = "EXTRA_RESET_REQUEST_INTENT";
+
+ private static final String SUCCESS = "**SUCCESS**";
@Override
public void onReceive(Context context, Intent intent) {
@@ -60,8 +84,112 @@
(NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
noMa.cancel(id);
+ } else if (ACTION_RESET_SETUP_NOTIFICATION.equals(intent.getAction())) {
+ testShortcutResetSetupNotification(context, intent);
+
+ } else if (ACTION_INLINE_REPLY.equals(intent.getAction())) {
+ testShortcutResetInlineReplyReceived(context, intent);
+
} else {
Log.i(TAG, "received unexpected action: " + intent.getAction());
}
}
+
+ /**
+ * Test start request from CTS verifier. Show a notification with inline reply, which will
+ * trigger {@link #testShortcutResetInlineReplyReceived}.
+ */
+ private static void testShortcutResetSetupNotification(Context context, Intent intent) {
+ final NotificationManager nm = context.getSystemService(NotificationManager.class);
+ nm.cancelAll();
+
+ final ShortcutManager sm = context.getSystemService(ShortcutManager.class);
+
+ final List<ShortcutInfo> EMPTY_LIST = new ArrayList<>();
+
+ long timeout = SystemClock.elapsedRealtime() + 10 * 1000;
+
+ // First, make sure this package is throttled.
+ while (!sm.isRateLimitingActive()) {
+ sm.setDynamicShortcuts(EMPTY_LIST);
+ try {
+ Thread.sleep(0);
+ } catch (InterruptedException e) {
+ }
+ if (SystemClock.elapsedRealtime() >= timeout) {
+ sendShortcutResetReply(context, intent,
+ "ShortcutMager rate-limiting not activated.");
+ return;
+ }
+ }
+
+ // Show a notification with inline reply.
+ final PendingIntent receiverIntent =
+ PendingIntent.getBroadcast(context, 0,
+ new Intent(ACTION_INLINE_REPLY)
+ .setComponent(new ComponentName(context, NotificationBot.class))
+ .putExtra(EXTRA_RESET_REQUEST_INTENT, intent),
+ PendingIntent.FLAG_UPDATE_CURRENT);
+ final RemoteInput ri = new RemoteInput.Builder("result")
+ .setLabel("Type something here and press send button").build();
+
+ final Notification.Builder nb = new Notification.Builder(context)
+ .setContentTitle(intent.getStringExtra(EXTRA_NOTIFICATION_TITLE))
+ .setSmallIcon(android.R.drawable.ic_popup_sync)
+ .addAction(new Action.Builder(0,
+ "Type something here and press send button", receiverIntent)
+ .addRemoteInput(ri)
+ .build());
+ context.getSystemService(NotificationManager.class).notify(0, nb.build());
+ }
+
+ /**
+ * Invoked when the inline reply from {@link #testShortcutResetSetupNotification} is performed.
+ *
+ * Check the shortcut manager rate-limiting state, and post the reply to CTS verifier.
+ */
+ private static void testShortcutResetInlineReplyReceived(Context context, Intent intent) {
+ Log.i(TAG, "Inline reply received");
+
+ final NotificationManager nm = context.getSystemService(NotificationManager.class);
+ nm.cancelAll();
+
+ // Close notification shade.
+ context.sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS));
+
+ // Check if rate-limiting has been reset.
+ final ShortcutManager sm = context.getSystemService(ShortcutManager.class);
+
+ String error;
+ final boolean success = !sm.isRateLimitingActive();
+ if (success) {
+ error = SUCCESS;
+ } else {
+ error = "Inline reply received, but ShortcutManager rate-limiting is still active.";
+ }
+
+ // Send back the result.
+ sendShortcutResetReply(context,
+ intent.getParcelableExtra(EXTRA_RESET_REQUEST_INTENT), error);
+ }
+
+ /**
+ * Reply an error message, or {@link #SUCCESS} for success, to CTS verifier for shortcut manager
+ * reset rate-limiting test.
+
+ * @param requestIntent original intent sent from the verifier to
+ * {@link #testShortcutResetSetupNotification}.
+ * @param error error message, or {@link #SUCCESS} if success.
+ */
+ private static void sendShortcutResetReply(Context context, Intent requestIntent, String error) {
+ final Intent replyIntent = new Intent();
+ replyIntent.setAction(requestIntent.getStringExtra(EXTRA_RESET_REPLY_ACTION));
+ replyIntent.putExtra(EXTRA_RESET_REPLY_ERROR, error);
+
+ if (error != null) {
+ Log.e(TAG, error);
+ }
+
+ context.sendBroadcast(replyIntent);
+ }
}
diff --git a/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/DeviceInfo.java b/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/DeviceInfo.java
index 8aa3380..750e45f 100644
--- a/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/DeviceInfo.java
+++ b/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/DeviceInfo.java
@@ -87,7 +87,7 @@
mResultCode = ResultCode.COMPLETED;
}
} catch (Exception e) {
- failed("Could not collect device info: " + e.getMessage());
+ failed("Could not collect device info", e);
}
}
@@ -136,6 +136,12 @@
Log.e(LOG_TAG, message, exception);
}
+ private void failed(String message, Throwable exception) {
+ mResultCode = ResultCode.FAILED;
+ mErrorMessage = message;
+ Log.e(LOG_TAG, message, exception);
+ }
+
private void failed(String message) {
mResultCode = ResultCode.FAILED;
mErrorMessage = message;
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/DeviceInfoStore.java b/common/device-side/util/src/com/android/compatibility/common/util/DeviceInfoStore.java
index 9f1bda5..735b955 100644
--- a/common/device-side/util/src/com/android/compatibility/common/util/DeviceInfoStore.java
+++ b/common/device-side/util/src/com/android/compatibility/common/util/DeviceInfoStore.java
@@ -135,9 +135,7 @@
*/
@Override
public void addResult(String name, float value) throws IOException {
- checkName(name);
- mJsonWriter.name(name);
- mJsonWriter.value(value);
+ addResult(name, (double) value);
}
/**
@@ -146,8 +144,12 @@
@Override
public void addResult(String name, double value) throws IOException {
checkName(name);
- mJsonWriter.name(name);
- mJsonWriter.value(value);
+ if (isDoubleNaNOrInfinite(value)) {
+ return;
+ } else {
+ mJsonWriter.name(name);
+ mJsonWriter.value(value);
+ }
}
/**
@@ -203,13 +205,11 @@
*/
@Override
public void addArrayResult(String name, float[] array) throws IOException {
- checkName(name);
- mJsonWriter.name(name);
- mJsonWriter.beginArray();
- for (float value : checkArray(array)) {
- mJsonWriter.value(value);
+ double[] doubleArray = new double[array.length];
+ for (int i = 0; i < array.length; i++) {
+ doubleArray[i] = array[i];
}
- mJsonWriter.endArray();
+ addArrayResult(name, doubleArray);
}
/**
@@ -221,6 +221,9 @@
mJsonWriter.name(name);
mJsonWriter.beginArray();
for (double value : checkArray(array)) {
+ if (isDoubleNaNOrInfinite(value)) {
+ continue;
+ }
mJsonWriter.value(value);
}
mJsonWriter.endArray();
diff --git a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/command/CompatibilityConsole.java b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/command/CompatibilityConsole.java
index ce5d023..b8ab089 100644
--- a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/command/CompatibilityConsole.java
+++ b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/command/CompatibilityConsole.java
@@ -216,8 +216,8 @@
helpBuilder.append("Options:\n");
helpBuilder.append(" --session/-s <session_id>: The session used to create a subplan.\n");
helpBuilder.append(" --name/-n <subplan_name>: The name of the new subplan.\n");
- helpBuilder.append(" --result/-r <status>: Which results to include in the subplan. ");
- helpBuilder.append("One of passed, failed, not_executed. Repeatable.\n");
+ helpBuilder.append(" --result-type/-r <status>: Which results to include in the");
+ helpBuilder.append(" subplan. One of passed, failed, not_executed. Repeatable.\n");
return helpBuilder.toString();
}
diff --git a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/result/SubPlanCreator.java b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/result/SubPlanCreator.java
index 1c697e4..cbe3d2e 100644
--- a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/result/SubPlanCreator.java
+++ b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/result/SubPlanCreator.java
@@ -20,9 +20,13 @@
import com.android.compatibility.common.tradefed.testtype.ISubPlan;
import com.android.compatibility.common.tradefed.testtype.SubPlan;
import com.android.compatibility.common.tradefed.util.OptionHelper;
+import com.android.compatibility.common.util.ICaseResult;
import com.android.compatibility.common.util.IInvocationResult;
+import com.android.compatibility.common.util.IModuleResult;
+import com.android.compatibility.common.util.ITestResult;
import com.android.compatibility.common.util.ResultHandler;
import com.android.compatibility.common.util.TestFilter;
+import com.android.compatibility.common.util.TestStatus;
import com.android.ddmlib.Log.LogLevel;
import com.android.tradefed.config.ArgsOptionParser;
import com.android.tradefed.config.ConfigurationException;
@@ -35,11 +39,12 @@
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
-import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
+import java.util.Collections;
import java.util.HashSet;
-import java.util.List;
+import java.util.HashMap;
+import java.util.Map;
import java.util.Set;
/**
@@ -47,9 +52,18 @@
*/
public class SubPlanCreator {
- private static final String PASSED = "passed";
- private static final String FAILED = "failed";
- private static final String NOT_EXECUTED = "not_executed";
+ // result types
+ public static final String PASSED = "passed";
+ public static final String FAILED = "failed";
+ public static final String NOT_EXECUTED = "not_executed";
+ // static mapping of result types to TestStatuses
+ private static final Map<String, TestStatus> mStatusMap;
+ static {
+ Map<String, TestStatus> statusMap = new HashMap<String, TestStatus>();
+ statusMap.put(PASSED, TestStatus.PASS);
+ statusMap.put(FAILED, TestStatus.FAIL);
+ mStatusMap = Collections.unmodifiableMap(statusMap);
+ }
@Option (name = "name", shortName = 'n', description = "the name of the subplan to create",
importance=Importance.IF_UNSET)
@@ -59,21 +73,21 @@
importance=Importance.IF_UNSET)
private Integer mSessionId = null;
- @Option (name = "result", shortName = 'r',
+ @Option (name = "result-type", shortName = 'r',
description = "the result type to include. One of passed, failed, not_executed."
+ " Option may be repeated",
importance=Importance.IF_UNSET)
- private Set<String> mResultFilterStrings = new HashSet<String>();
+ private Set<String> mResultTypes = new HashSet<String>();
@Option(name = CompatibilityTest.INCLUDE_FILTER_OPTION,
description = "the include module filters to apply.",
importance = Importance.NEVER)
- private List<String> mIncludeFilters = new ArrayList<>();
+ private Set<String> mIncludeFilters = new HashSet<String>();
@Option(name = CompatibilityTest.EXCLUDE_FILTER_OPTION,
description = "the exclude module filters to apply.",
importance = Importance.NEVER)
- private List<String> mExcludeFilters = new ArrayList<>();
+ private Set<String> mExcludeFilters = new HashSet<String>();
@Option(name = CompatibilityTest.MODULE_OPTION, shortName = 'm',
description = "the test module to run.",
@@ -87,7 +101,7 @@
@Option(name = CompatibilityTest.ABI_OPTION, shortName = 'a',
description = "the abi to test.",
- importance = Importance.IF_UNSET)
+ importance = Importance.NEVER)
private String mAbiName = null;
File mSubPlanFile = null;
@@ -104,10 +118,26 @@
/**
* Create a {@link SubPlanCreator} using the specified option values.
*/
- public SubPlanCreator(String name, int session, Collection<String> resultFilterStrings) {
+ public SubPlanCreator(String name, int session, Collection<String> resultTypes) {
mSubPlanName = name;
mSessionId = session;
- mResultFilterStrings.addAll(resultFilterStrings);
+ mResultTypes.addAll(resultTypes);
+ }
+
+ /**
+ * Set the result from which to derive the subplan.
+ * @param result
+ */
+ public void setResult(IInvocationResult result) {
+ mResult = result;
+ }
+
+ /**
+ * Add a result type from which to derive the subplan. PASSED, FAILED, or NOT_EXECUTED
+ * @param resultType
+ */
+ public void addResultType(String resultType) {
+ mResultTypes.add(resultType);
}
/**
@@ -137,8 +167,7 @@
/**
* Create a subplan derived from a result.
* <p/>
- * {@link Option} values must all be set before this is called.
- *
+ * {@link Option} values must be set before this is called.
* @param build
* @return subplan
* @throws ConfigurationException
@@ -147,56 +176,135 @@
throws ConfigurationException {
setupFields(buildHelper);
ISubPlan subPlan = new SubPlan();
- // At least one of the following three is true
- boolean notExecuted = mResultFilterStrings.contains(NOT_EXECUTED);
- boolean passed = mResultFilterStrings.contains(PASSED);
- boolean failed = mResultFilterStrings.contains(FAILED);
- if (notExecuted) {
- // if including not_executed tests, include filters from previous session to
- // track which tests must run
- subPlan.addAllIncludeFilters(new HashSet<String>(mIncludeFilters));
- subPlan.addAllExcludeFilters(new HashSet<String>(mExcludeFilters));
- if (mModuleName != null) {
- subPlan.addIncludeFilter(
- new TestFilter(mAbiName, mModuleName, mTestName).toString());
- }
- if (!passed) {
- subPlan.excludePassed(mResult);
- }
- if (!failed) {
- subPlan.excludeFailed(mResult);
- }
- } else {
- // if only including executed tests, add each filter explicitly without filters from
- // previous session
- if (passed) {
- subPlan.includePassed(mResult);
- }
- if (failed) {
- subPlan.includeFailed(mResult);
- }
+
+ // add filters from previous session to track which tests must run
+ subPlan.addAllIncludeFilters(mIncludeFilters);
+ subPlan.addAllExcludeFilters(mExcludeFilters);
+ if (mModuleName != null) {
+ subPlan.addIncludeFilter(new TestFilter(mAbiName, mModuleName, mTestName).toString());
}
+ for (IModuleResult module : mResult.getModules()) {
+ if (shouldRunModule(module)) {
+ Set<TestStatus> statusesToRun = getStatusesToRun();
+ TestFilter moduleInclude =
+ new TestFilter(module.getAbi(), module.getName(), null /*test*/);
+ if (shouldRunEntireModule(module)) {
+ // include entire module
+ subPlan.addIncludeFilter(moduleInclude.toString());
+ } else if (mResultTypes.contains(NOT_EXECUTED) && !module.isDone()) {
+ // add module include and test excludes
+ subPlan.addIncludeFilter(moduleInclude.toString());
+ for (ICaseResult caseResult : module.getResults()) {
+ for (ITestResult testResult : caseResult.getResults()) {
+ if (!statusesToRun.contains(testResult.getResultStatus())) {
+ TestFilter testExclude = new TestFilter(module.getAbi(),
+ module.getName(), testResult.getFullName());
+ subPlan.addExcludeFilter(testExclude.toString());
+ }
+ }
+ }
+ } else {
+ // Not-executed tests should not be rerun and/or this module is completed
+ // In any such case, it suffices to add includes for each test to rerun
+ for (ICaseResult caseResult : module.getResults()) {
+ for (ITestResult testResult : caseResult.getResults()) {
+ if (statusesToRun.contains(testResult.getResultStatus())) {
+ TestFilter testInclude = new TestFilter(module.getAbi(),
+ module.getName(), testResult.getFullName());
+ subPlan.addIncludeFilter(testInclude.toString());
+ }
+ }
+ }
+ }
+ } else {
+ // module should not run, exclude entire module
+ TestFilter moduleExclude =
+ new TestFilter(module.getAbi(), module.getName(), null /*test*/);
+ subPlan.addExcludeFilter(moduleExclude.toString());
+ }
+ }
return subPlan;
}
/**
+ * Whether any tests within the given {@link IModuleResult} should be run, based on
+ * the content of mResultTypes.
+ * @param module
+ * @return true if at least one test in the module should run
+ */
+ private boolean shouldRunModule(IModuleResult module) {
+ if (mResultTypes.contains(NOT_EXECUTED) && !module.isDone()) {
+ // module has not executed tests that the subplan should run
+ return true;
+ }
+ for (TestStatus status : getStatusesToRun()) {
+ if (module.countResults(status) > 0) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Whether all tests within the given {@link IModuleResult} should be run, based on
+ * the content of mResultTypes.
+ * @param module
+ * @return true if every test in the module should run
+ */
+ private boolean shouldRunEntireModule(IModuleResult module) {
+ if (!mResultTypes.contains(NOT_EXECUTED) && !module.isDone()) {
+ // module has not executed tests that the subplan should not run
+ return false;
+ }
+ Set<TestStatus> statusesToRun = getStatusesToRun();
+ for (TestStatus status : TestStatus.values()) {
+ if (!statusesToRun.contains(status)) {
+ // status is a TestStatus we don't want to run
+ if (module.countResults(status) > 0) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Retrieves a {@link Set} of {@link TestStatus}es to run, based on the content of
+ * mResultTypes. Does not account for result type NOT_EXECUTED, since no such TestStatus
+ * exists.
+ * @return set of TestStatuses to run
+ */
+ private Set<TestStatus> getStatusesToRun() {
+ Set<TestStatus> statusesToRun = new HashSet<TestStatus>();
+ for (String resultType : mResultTypes) {
+ // no test status exists for not-executed tests
+ if (resultType != NOT_EXECUTED) {
+ statusesToRun.add(mStatusMap.get(resultType));
+ }
+ }
+ return statusesToRun;
+ }
+
+ /**
* Ensure that all {@Option}s and fields are populated with valid values.
* @param buildHelper
* @throws ConfigurationException if any option has an invalid value
*/
private void setupFields(CompatibilityBuildHelper buildHelper) throws ConfigurationException {
- if (mSessionId == null) {
- throw new ConfigurationException("Missing --session argument");
- }
- try {
- mResult = ResultHandler.findResult(buildHelper.getResultsDir(), mSessionId);
- } catch (FileNotFoundException e) {
- throw new RuntimeException(e);
- }
if (mResult == null) {
- throw new IllegalArgumentException(String.format(
- "Could not find session with id %d", mSessionId));
+ if (mSessionId == null) {
+ throw new ConfigurationException("Missing --session argument");
+ }
+ try {
+ mResult = ResultHandler.findResult(buildHelper.getResultsDir(), mSessionId);
+ } catch (FileNotFoundException e) {
+ throw new RuntimeException(e);
+ }
+ if (mResult == null) {
+ throw new IllegalArgumentException(String.format(
+ "Could not find session with id %d", mSessionId));
+ }
}
String retryCommandLineArgs = mResult.getCommandLineArgs();
@@ -210,18 +318,15 @@
}
}
- if (mResultFilterStrings.isEmpty()) {
+ if (mResultTypes.isEmpty()) {
// add all valid values, include all tests of all statuses
- mResultFilterStrings.addAll(
+ mResultTypes.addAll(
new HashSet<String>(Arrays.asList(PASSED, FAILED, NOT_EXECUTED)));
}
// validate all test status values
- for (String filterString : mResultFilterStrings) {
- if (!filterString.equals(PASSED)
- && !filterString.equals(FAILED)
- && !filterString.equals(NOT_EXECUTED)) {
- throw new ConfigurationException(String.format("result filter string %s invalid",
- filterString));
+ for (String type : mResultTypes) {
+ if (!type.equals(PASSED) && !type.equals(FAILED) && !type.equals(NOT_EXECUTED)) {
+ throw new ConfigurationException(String.format("result type %s invalid", type));
}
}
@@ -239,10 +344,19 @@
}
}
+ /**
+ * Helper to create a plan name if none is explicitly set
+ */
private String createPlanName() {
StringBuilder sb = new StringBuilder();
- sb.append(String.join("_", mResultFilterStrings));
- sb.append(Integer.toString(mSessionId));
+ sb.append(String.join("_", mResultTypes));
+ sb.append("_");
+ if (mSessionId != null) {
+ sb.append(Integer.toString(mSessionId));
+ sb.append("_");
+ }
+ // use unique start time for name
+ sb.append(CompatibilityBuildHelper.getDirSuffix(mResult.getStartTime()));
return sb.toString();
}
}
diff --git a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/CompatibilityTest.java b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/CompatibilityTest.java
index 70e3588..9bd8647 100644
--- a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/CompatibilityTest.java
+++ b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/CompatibilityTest.java
@@ -18,6 +18,7 @@
import com.android.compatibility.SuiteInfo;
import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
+import com.android.compatibility.common.tradefed.result.SubPlanCreator;
import com.android.compatibility.common.tradefed.targetprep.NetworkConnectivityChecker;
import com.android.compatibility.common.tradefed.targetprep.SystemStatusChecker;
import com.android.compatibility.common.tradefed.util.OptionHelper;
@@ -115,12 +116,12 @@
@Option(name = INCLUDE_FILTER_OPTION,
description = "the include module filters to apply.",
importance = Importance.ALWAYS)
- private List<String> mIncludeFilters = new ArrayList<>();
+ private Set<String> mIncludeFilters = new HashSet<>();
@Option(name = EXCLUDE_FILTER_OPTION,
description = "the exclude module filters to apply.",
importance = Importance.ALWAYS)
- private List<String> mExcludeFilters = new ArrayList<>();
+ private Set<String> mExcludeFilters = new HashSet<>();
@Option(name = MODULE_OPTION,
shortName = 'm',
@@ -611,15 +612,26 @@
}
}
- ISubPlan retryPlan = new SubPlan();
- retryPlan.excludePassed(result); // always exclude passed tests on retry
+ SubPlanCreator retryPlanCreator = new SubPlanCreator();
+ retryPlanCreator.setResult(result);
if (RetryType.FAILED.equals(mRetryType)) {
- retryPlan.includeFailed(result); // retry only failed tests
+ // retry only failed tests
+ retryPlanCreator.addResultType(SubPlanCreator.FAILED);
} else if (RetryType.NOT_EXECUTED.equals(mRetryType)){
- retryPlan.excludeFailed(result); // retry only not executed tests
+ // retry only not executed tests
+ retryPlanCreator.addResultType(SubPlanCreator.NOT_EXECUTED);
+ } else {
+ // retry both failed and not executed tests
+ retryPlanCreator.addResultType(SubPlanCreator.FAILED);
+ retryPlanCreator.addResultType(SubPlanCreator.NOT_EXECUTED);
}
- mIncludeFilters.addAll(retryPlan.getIncludeFilters());
- mExcludeFilters.addAll(retryPlan.getExcludeFilters());
+ try {
+ ISubPlan retryPlan = retryPlanCreator.createSubPlan(mBuildHelper);
+ mIncludeFilters.addAll(retryPlan.getIncludeFilters());
+ mExcludeFilters.addAll(retryPlan.getExcludeFilters());
+ } catch (ConfigurationException e) {
+ throw new RuntimeException ("Failed to create subplan for retry", e);
+ }
}
if (mSubPlan != null) {
try {
@@ -670,8 +682,8 @@
}
/* Helper method designed to remove filters in a list not applicable to the given module */
- private static void cleanFilters(List<String> filters, String module) {
- List<String> cleanedFilters = new ArrayList<String>();
+ private static void cleanFilters(Set<String> filters, String module) {
+ Set<String> cleanedFilters = new HashSet<String>();
for (String filter : filters) {
if (module.equals(TestFilter.createFrom(filter).getName())) {
cleanedFilters.add(filter); // Module name matches, filter passes
diff --git a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/IModuleRepo.java b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/IModuleRepo.java
index 7cbf7a3..540373b 100644
--- a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/IModuleRepo.java
+++ b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/IModuleRepo.java
@@ -47,8 +47,8 @@
* Initializes the repository.
*/
void initialize(int shards, File testsDir, Set<IAbi> abis, List<String> deviceTokens,
- List<String> testArgs, List<String> moduleArgs, List<String> mIncludeFilters,
- List<String> mExcludeFilters, IBuildInfo buildInfo);
+ List<String> testArgs, List<String> moduleArgs, Set<String> mIncludeFilters,
+ Set<String> mExcludeFilters, IBuildInfo buildInfo);
/**
* @return a {@link Map} of all modules to run on the device referenced by the given serial.
diff --git a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/ISubPlan.java b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/ISubPlan.java
index adc0227..eda40b2 100644
--- a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/ISubPlan.java
+++ b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/ISubPlan.java
@@ -15,7 +15,6 @@
*/
package com.android.compatibility.common.tradefed.testtype;
-import com.android.compatibility.common.util.IInvocationResult;
import com.android.tradefed.testtype.ITestFilterReceiver;
import com.android.tradefed.util.xml.AbstractXmlParser.ParseException;
@@ -37,30 +36,6 @@
public void parse(InputStream xmlInputStream) throws ParseException;
/**
- * Add include filters for {@link ITestResult}s that have passed.
- * @param result the {@link IInvocationResult} from which to read {@link TestStatus}es
- */
- public void includePassed(IInvocationResult result);
-
- /**
- * Add include filters for {@link ITestResult}s that have failed.
- * @param result the {@link IInvocationResult} from which to read {@link TestStatus}es
- */
- public void includeFailed(IInvocationResult result);
-
- /**
- * Add exclude filters for {@link ITestResult}s that have passed.
- * @param result the {@link IInvocationResult} from which to read {@link TestStatus}es
- */
- public void excludePassed(IInvocationResult result);
-
- /**
- * Add exclude filters for {@link ITestResult}s that have failed.
- * @param result the {@link IInvocationResult} from which to read {@link TestStatus}es
- */
- public void excludeFailed(IInvocationResult result);
-
- /**
* Retrieve the set of include filters previously added or parsed from XML.
* @return a set of include filter strings
*/
diff --git a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/ModuleRepo.java b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/ModuleRepo.java
index c4ff1b0..3d1b234 100644
--- a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/ModuleRepo.java
+++ b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/ModuleRepo.java
@@ -233,8 +233,8 @@
*/
@Override
public void initialize(int shards, File testsDir, Set<IAbi> abis, List<String> deviceTokens,
- List<String> testArgs, List<String> moduleArgs, List<String> includeFilters,
- List<String> excludeFilters, IBuildInfo buildInfo) {
+ List<String> testArgs, List<String> moduleArgs, Set<String> includeFilters,
+ Set<String> excludeFilters, IBuildInfo buildInfo) {
CLog.d("Initializing ModuleRepo\nShards:%d\nTests Dir:%s\nABIs:%s\nDevice Tokens:%s\n" +
"Test Args:%s\nModule Args:%s\nIncludes:%s\nExcludes:%s",
shards, testsDir.getAbsolutePath(), abis, deviceTokens, testArgs, moduleArgs,
@@ -355,7 +355,7 @@
return shardedList;
}
- private static void addFilters(List<String> stringFilters,
+ private static void addFilters(Set<String> stringFilters,
Map<String, List<TestFilter>> filters, Set<IAbi> abis) {
for (String filterString : stringFilters) {
TestFilter filter = TestFilter.createFrom(filterString);
diff --git a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/SubPlan.java b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/SubPlan.java
index 80ca30e..54d2869 100644
--- a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/SubPlan.java
+++ b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/SubPlan.java
@@ -15,13 +15,7 @@
*/
package com.android.compatibility.common.tradefed.testtype;
-import com.android.compatibility.common.util.ICaseResult;
-import com.android.compatibility.common.util.IInvocationResult;
-import com.android.compatibility.common.util.IModuleResult;
-import com.android.compatibility.common.util.ITestResult;
-
import com.android.compatibility.common.util.TestFilter;
-import com.android.compatibility.common.util.TestStatus;
import com.android.tradefed.util.xml.AbstractXmlParser;
import org.kxml2.io.KXmlSerializer;
@@ -66,88 +60,6 @@
* {@inheritDoc}
*/
@Override
- public void includePassed(IInvocationResult result) {
- for (IModuleResult module : result.getModules()) {
- if (module.isPassed()) {
- // Whole module passed, exclude
- TestFilter filter =
- new TestFilter(module.getAbi(), module.getName(), null /*test*/);
- mIncludes.add(filter.toString());
- } else {
- for (ICaseResult testResultList : module.getResults()) {
- for (ITestResult testResult : testResultList.getResults(TestStatus.PASS)) {
- // Test passed, exclude
- TestFilter filter = new TestFilter(
- module.getAbi(), module.getName(), testResult.getFullName());
- mIncludes.add(filter.toString());
- }
- }
- }
- }
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public void includeFailed(IInvocationResult result) {
- for (IModuleResult moduleResult : result.getModules()) {
- for (ICaseResult caseResult : moduleResult.getResults()) {
- for (ITestResult testResult : caseResult.getResults(TestStatus.FAIL)) {
- // Test failed, include for retry
- TestFilter filter = new TestFilter(moduleResult.getAbi(),
- moduleResult.getName(), testResult.getFullName());
- mIncludes.add(filter.toString());
- }
- }
- }
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public void excludePassed(IInvocationResult result) {
- for (IModuleResult module : result.getModules()) {
- if (module.isPassed()) {
- // Whole module passed, exclude
- TestFilter filter =
- new TestFilter(module.getAbi(), module.getName(), null /*test*/);
- mExcludes.add(filter.toString());
- } else {
- for (ICaseResult testResultList : module.getResults()) {
- for (ITestResult testResult : testResultList.getResults(TestStatus.PASS)) {
- // Test passed, exclude
- TestFilter filter = new TestFilter(
- module.getAbi(), module.getName(), testResult.getFullName());
- mExcludes.add(filter.toString());
- }
- }
- }
- }
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public void excludeFailed(IInvocationResult result) {
- for (IModuleResult moduleResult : result.getModules()) {
- for (ICaseResult caseResult : moduleResult.getResults()) {
- for (ITestResult testResult : caseResult.getResults(TestStatus.FAIL)) {
- // Test failed, include for retry
- TestFilter filter = new TestFilter(moduleResult.getAbi(),
- moduleResult.getName(), testResult.getFullName());
- mExcludes.add(filter.toString());
- }
- }
- }
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
public void addIncludeFilter(String filter) {
mIncludes.add(filter);
}
diff --git a/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/result/SubPlanCreatorTest.java b/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/result/SubPlanCreatorTest.java
index 2ad7260..192aabb 100644
--- a/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/result/SubPlanCreatorTest.java
+++ b/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/result/SubPlanCreatorTest.java
@@ -117,13 +117,13 @@
ISubPlan plan = mSubPlanCreator.createSubPlan(mBuildHelper);
Set<String> planIncludes = plan.getIncludeFilters();
Set<String> planExcludes = plan.getExcludeFilters();
- TestFilter passingTest1 =
- new TestFilter(ABI, NAME_A, String.format("%s#%s", CLASS_A, METHOD_1));
- TestFilter passingTest2 =
- new TestFilter(ABI, NAME_B, String.format("%s#%s", CLASS_B, METHOD_4));
+ TestFilter mf1 = new TestFilter(ABI, NAME_A, null);
+ TestFilter tf1 = new TestFilter(ABI, NAME_A, String.format("%s#%s", CLASS_A, METHOD_1));
+ TestFilter tf3 = new TestFilter(ABI, NAME_B, String.format("%s#%s", CLASS_B, METHOD_3));
assertTrue(planIncludes.contains("CtsMyModuleTestCases")); // command-line '-m' arg
- assertTrue(planExcludes.contains(passingTest1.toString()));
- assertTrue(planExcludes.contains(passingTest2.toString()));
+ assertTrue(planIncludes.contains(mf1.toString())); // include module with not-executed test
+ assertTrue(planExcludes.contains(tf1.toString())); // exclude passing test in that module
+ assertTrue(planIncludes.contains(tf3.toString())); // include failure in executed module
}
private void populateResults() throws Exception {
diff --git a/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/testtype/ModuleRepoTest.java b/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/testtype/ModuleRepoTest.java
index 3b8e4cb..5a0ebed 100644
--- a/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/testtype/ModuleRepoTest.java
+++ b/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/testtype/ModuleRepoTest.java
@@ -59,8 +59,8 @@
private static final List<String> DEVICE_TOKENS = new ArrayList<>();
private static final List<String> TEST_ARGS= new ArrayList<>();
private static final List<String> MODULE_ARGS = new ArrayList<>();
- private static final List<String> INCLUDES = new ArrayList<>();
- private static final List<String> EXCLUDES = new ArrayList<>();
+ private static final Set<String> INCLUDES = new HashSet<>();
+ private static final Set<String> EXCLUDES = new HashSet<>();
private static final Set<String> FILES = new HashSet<>();
private static final String FILENAME = "%s.config";
private static final String ABI_32 = "armeabi-v7a";
@@ -184,9 +184,9 @@
}
public void testFiltering() throws Exception {
- List<String> includeFilters = new ArrayList<>();
+ Set<String> includeFilters = new HashSet<>();
includeFilters.add(MODULE_NAME_A);
- List<String> excludeFilters = new ArrayList<>();
+ Set<String> excludeFilters = new HashSet<>();
excludeFilters.add(ID_A_32);
excludeFilters.add(MODULE_NAME_B);
mRepo.initialize(1, mTestsDir, ABIS, DEVICE_TOKENS, TEST_ARGS, MODULE_ARGS, includeFilters,
@@ -234,8 +234,8 @@
abis.add(new Abi(ABI_64, "64"));
ArrayList<String> emptyList = new ArrayList<>();
- mRepo.initialize(3, mTestsDir, abis, DEVICE_TOKENS, emptyList, emptyList, emptyList,
- emptyList, mBuild);
+ mRepo.initialize(3, mTestsDir, abis, DEVICE_TOKENS, emptyList, emptyList, INCLUDES,
+ EXCLUDES, mBuild);
List<IModuleDef> modules = new ArrayList<>();
modules.addAll(mRepo.getLargeModules());
diff --git a/common/util/src/com/android/compatibility/common/util/InfoStore.java b/common/util/src/com/android/compatibility/common/util/InfoStore.java
index 6c5cc0d..b8014f7 100644
--- a/common/util/src/com/android/compatibility/common/util/InfoStore.java
+++ b/common/util/src/com/android/compatibility/common/util/InfoStore.java
@@ -189,4 +189,8 @@
}
return value;
}
+
+ protected static boolean isDoubleNaNOrInfinite(Double value) {
+ return Double.isNaN(value) || Double.isInfinite(value);
+ }
}
diff --git a/hostsidetests/appsecurity/test-apps/DocumentClient/src/com/android/cts/documentclient/ScopedDirectoryAccessClientTest.java b/hostsidetests/appsecurity/test-apps/DocumentClient/src/com/android/cts/documentclient/ScopedDirectoryAccessClientTest.java
index 5747adf..dec8769 100644
--- a/hostsidetests/appsecurity/test-apps/DocumentClient/src/com/android/cts/documentclient/ScopedDirectoryAccessClientTest.java
+++ b/hostsidetests/appsecurity/test-apps/DocumentClient/src/com/android/cts/documentclient/ScopedDirectoryAccessClientTest.java
@@ -246,10 +246,14 @@
}
public void testRemovePackageStep1UserDenies() throws Exception {
+ if (!supportedHardware()) return;
+
deniesOnceForAllTest(getPrimaryVolume(), DIRECTORY_NOTIFICATIONS);
}
public void testRemovePackageStep2UserAcceptsDoNotClear() throws Exception {
+ if (!supportedHardware()) return;
+
userAcceptsTest(getPrimaryVolume(), DIRECTORY_NOTIFICATIONS);
}
diff --git a/hostsidetests/net/app/src/com/android/cts/net/hostside/AbstractAppIdleTestCase.java b/hostsidetests/net/app/src/com/android/cts/net/hostside/AbstractAppIdleTestCase.java
index ba56665..78ba4b9 100644
--- a/hostsidetests/net/app/src/com/android/cts/net/hostside/AbstractAppIdleTestCase.java
+++ b/hostsidetests/net/app/src/com/android/cts/net/hostside/AbstractAppIdleTestCase.java
@@ -16,6 +16,7 @@
package com.android.cts.net.hostside;
+import android.os.SystemClock;
import android.util.Log;
/**
@@ -138,4 +139,40 @@
assertsForegroundAlwaysHasNetworkAccess();
assertBackgroundNetworkAccess(true);
}
+
+ public void testAppIdleNetworkAccess_whenCharging() throws Exception {
+ if (!isSupported()) return;
+
+ // Check that app is paroled when charging
+ setAppIdle(true);
+ assertBackgroundNetworkAccess(false);
+ turnBatteryOn();
+ assertBackgroundNetworkAccess(true);
+ turnBatteryOff();
+ assertBackgroundNetworkAccess(false);
+
+ // Check that app is restricted when not idle but power-save is on
+ setAppIdle(false);
+ assertBackgroundNetworkAccess(true);
+ setBatterySaverMode(true);
+ assertBackgroundNetworkAccess(false);
+ turnBatteryOn();
+ assertBackgroundNetworkAccess(true);
+
+ // And when no longer charging, it still has network access, since it's not idle
+ turnBatteryOff();
+ assertBackgroundNetworkAccess(true);
+ }
+
+ public void testAppIdle_toast() throws Exception {
+ if (!isSupported()) return;
+
+ setAppIdle(true);
+ assertAppIdle(true);
+ assertEquals("Shown", showToast());
+ assertAppIdle(true);
+ // Wait for a couple of seconds for the toast to actually be shown
+ SystemClock.sleep(2000);
+ assertAppIdle(true);
+ }
}
diff --git a/hostsidetests/net/app/src/com/android/cts/net/hostside/AbstractRestrictBackgroundNetworkTestCase.java b/hostsidetests/net/app/src/com/android/cts/net/hostside/AbstractRestrictBackgroundNetworkTestCase.java
index 9245a6f..9c9ec0b 100644
--- a/hostsidetests/net/app/src/com/android/cts/net/hostside/AbstractRestrictBackgroundNetworkTestCase.java
+++ b/hostsidetests/net/app/src/com/android/cts/net/hostside/AbstractRestrictBackgroundNetworkTestCase.java
@@ -64,6 +64,8 @@
"com.android.cts.net.hostside.app2.action.RECEIVER_READY";
static final String ACTION_SEND_NOTIFICATION =
"com.android.cts.net.hostside.app2.action.SEND_NOTIFICATION";
+ static final String ACTION_SHOW_TOAST =
+ "com.android.cts.net.hostside.app2.action.SHOW_TOAST";
private static final String EXTRA_ACTION = "com.android.cts.net.hostside.app2.extra.ACTION";
private static final String EXTRA_RECEIVER_NAME =
"com.android.cts.net.hostside.app2.extra.RECEIVER_NAME";
@@ -782,6 +784,17 @@
mContext.sendBroadcast(intent);
}
+ protected String showToast() {
+ final Intent intent = new Intent(ACTION_SHOW_TOAST);
+ intent.setPackage(TEST_APP2_PKG);
+ Log.d(TAG, "Sending request to show toast");
+ try {
+ return sendOrderedBroadcast(intent, 3 * SECOND_IN_MS);
+ } catch (Exception e) {
+ return "";
+ }
+ }
+
private String toString(int status) {
switch (status) {
case RESTRICT_BACKGROUND_STATUS_DISABLED:
diff --git a/hostsidetests/net/app2/AndroidManifest.xml b/hostsidetests/net/app2/AndroidManifest.xml
index 1fa49ba..adf0045 100644
--- a/hostsidetests/net/app2/AndroidManifest.xml
+++ b/hostsidetests/net/app2/AndroidManifest.xml
@@ -46,6 +46,7 @@
<action android:name="com.android.cts.net.hostside.app2.action.GET_RESTRICT_BACKGROUND_STATUS" />
<action android:name="com.android.cts.net.hostside.app2.action.CHECK_NETWORK" />
<action android:name="com.android.cts.net.hostside.app2.action.SEND_NOTIFICATION" />
+ <action android:name="com.android.cts.net.hostside.app2.action.SHOW_TOAST" />
</intent-filter>
</receiver>
</application>
diff --git a/hostsidetests/net/app2/src/com/android/cts/net/hostside/app2/Common.java b/hostsidetests/net/app2/src/com/android/cts/net/hostside/app2/Common.java
index 8806e3b..e07c0f5 100644
--- a/hostsidetests/net/app2/src/com/android/cts/net/hostside/app2/Common.java
+++ b/hostsidetests/net/app2/src/com/android/cts/net/hostside/app2/Common.java
@@ -38,6 +38,8 @@
"com.android.cts.net.hostside.app2.action.FINISH_ACTIVITY";
static final String ACTION_SEND_NOTIFICATION =
"com.android.cts.net.hostside.app2.action.SEND_NOTIFICATION";
+ static final String ACTION_SHOW_TOAST =
+ "com.android.cts.net.hostside.app2.action.SHOW_TOAST";
static final String EXTRA_ACTION = "com.android.cts.net.hostside.app2.extra.ACTION";
static final String EXTRA_RECEIVER_NAME =
"com.android.cts.net.hostside.app2.extra.RECEIVER_NAME";
diff --git a/hostsidetests/net/app2/src/com/android/cts/net/hostside/app2/MyBroadcastReceiver.java b/hostsidetests/net/app2/src/com/android/cts/net/hostside/app2/MyBroadcastReceiver.java
index 6d01b15..733c3aa 100644
--- a/hostsidetests/net/app2/src/com/android/cts/net/hostside/app2/MyBroadcastReceiver.java
+++ b/hostsidetests/net/app2/src/com/android/cts/net/hostside/app2/MyBroadcastReceiver.java
@@ -23,6 +23,7 @@
import static com.android.cts.net.hostside.app2.Common.ACTION_GET_RESTRICT_BACKGROUND_STATUS;
import static com.android.cts.net.hostside.app2.Common.ACTION_RECEIVER_READY;
import static com.android.cts.net.hostside.app2.Common.ACTION_SEND_NOTIFICATION;
+import static com.android.cts.net.hostside.app2.Common.ACTION_SHOW_TOAST;
import static com.android.cts.net.hostside.app2.Common.EXTRA_ACTION;
import static com.android.cts.net.hostside.app2.Common.EXTRA_NOTIFICATION_ID;
import static com.android.cts.net.hostside.app2.Common.EXTRA_NOTIFICATION_TYPE;
@@ -51,6 +52,7 @@
import android.net.NetworkInfo;
import android.os.Bundle;
import android.util.Log;
+import android.widget.Toast;
import java.net.HttpURLConnection;
import java.net.URL;
@@ -104,6 +106,9 @@
case ACTION_SEND_NOTIFICATION:
sendNotification(context, intent);
break;
+ case ACTION_SHOW_TOAST:
+ showToast(context);
+ break;
default:
Log.e(TAG, "received unexpected action: " + action);
}
@@ -302,4 +307,9 @@
((NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE))
.notify(notificationId, notification);
}
+
+ private void showToast(Context context) {
+ Toast.makeText(context, "Toast from CTS test", Toast.LENGTH_SHORT).show();
+ setResultData("Shown");
+ }
}
diff --git a/hostsidetests/net/src/com/android/cts/net/HostsideRestrictBackgroundNetworkTests.java b/hostsidetests/net/src/com/android/cts/net/HostsideRestrictBackgroundNetworkTests.java
index 7d5f817..faf75d9 100644
--- a/hostsidetests/net/src/com/android/cts/net/HostsideRestrictBackgroundNetworkTests.java
+++ b/hostsidetests/net/src/com/android/cts/net/HostsideRestrictBackgroundNetworkTests.java
@@ -171,6 +171,22 @@
"testBackgroundNetworkAccess_enabled");
}
+ public void testAppIdleNonMetered_whenCharging() throws Exception {
+ runDeviceTests(TEST_PKG, TEST_PKG + ".AppIdleNonMeteredTest",
+ "testAppIdleNetworkAccess_whenCharging");
+ }
+
+ public void testAppIdleMetered_whenCharging() throws Exception {
+ runDeviceTests(TEST_PKG, TEST_PKG + ".AppIdleMeteredTest",
+ "testAppIdleNetworkAccess_whenCharging");
+ }
+
+ public void testAppIdle_toast() throws Exception {
+ // Check that showing a toast doesn't bring an app out of standby
+ runDeviceTests(TEST_PKG, TEST_PKG + ".AppIdleNonMeteredTest",
+ "testAppIdle_toast");
+ }
+
/********************
* Doze Mode tests. *
********************/
diff --git a/tests/camera/src/android/hardware/camera2/cts/DngCreatorTest.java b/tests/camera/src/android/hardware/camera2/cts/DngCreatorTest.java
index 6ab55b0..8f32d52 100644
--- a/tests/camera/src/android/hardware/camera2/cts/DngCreatorTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/DngCreatorTest.java
@@ -40,6 +40,7 @@
import android.media.Image;
import android.media.ImageReader;
import android.os.ConditionVariable;
+import android.os.SystemClock;
import android.util.Log;
import android.util.Pair;
import android.util.Size;
@@ -54,8 +55,10 @@
import java.util.Arrays;
import java.util.Calendar;
import java.util.Collections;
+import java.util.Date;
import java.util.List;
import java.util.TimeZone;
+import java.text.SimpleDateFormat;
import static android.hardware.camera2.cts.helpers.AssertHelpers.*;
@@ -243,10 +246,19 @@
previewListener));
captureListeners.add(previewListener);
+ Date beforeCaptureDate = new Date();
Pair<List<Image>, CaptureResult> resultPair = captureSingleRawShot(activeArraySize,
captureReaders, /*waitForAe*/false, captureListeners);
+ Date afterCaptureDate = new Date();
CameraCharacteristics characteristics = mStaticInfo.getCharacteristics();
+ if (VERBOSE) {
+ Log.v(TAG, "Sensor timestamp (ms): " +
+ resultPair.second.get(CaptureResult.SENSOR_TIMESTAMP) / 1000000);
+ Log.v(TAG, "SystemClock.elapsedRealtimeNanos (ms): " +
+ SystemClock.elapsedRealtimeNanos() / 1000000);
+ Log.v(TAG, "SystemClock.uptimeMillis(): " + SystemClock.uptimeMillis());
+ }
// Test simple writeImage, no header checks
DngCreator dngCreator = new DngCreator(characteristics, resultPair.second);
Location l = new Location("test");
@@ -264,7 +276,7 @@
String filePath = DEBUG_FILE_NAME_BASE + "/camera_thumb_" + deviceId + "_" +
DEBUG_DNG_FILE;
- // Write out captured DNG file for the first camera device if setprop is enabled
+ // Write out captured DNG file for the first camera device
fileStream = new FileOutputStream(filePath);
fileStream.write(outputStream.toByteArray());
fileStream.flush();
@@ -292,6 +304,30 @@
exifInterface.getAttributeInt(ExifInterface.TAG_ORIENTATION,
ExifInterface.ORIENTATION_UNDEFINED));
+ // Verify the date/time
+ final SimpleDateFormat dngDateTimeStampFormat =
+ new SimpleDateFormat("yyyy:MM:dd HH:mm:ss");
+ dngDateTimeStampFormat.setLenient(false);
+
+ String dateTimeString =
+ exifInterface.getAttribute(ExifInterface.TAG_DATETIME);
+ assertTrue(dateTimeString != null);
+
+ Date dateTime = dngDateTimeStampFormat.parse(dateTimeString);
+ long captureTimeMs = dateTime.getTime();
+
+ Log.i(TAG, "DNG DateTime tag: " + dateTimeString);
+ Log.i(TAG, "Before capture time: " + beforeCaptureDate.getTime());
+ Log.i(TAG, "Capture time: " + captureTimeMs);
+ Log.i(TAG, "After capture time: " + afterCaptureDate.getTime());
+
+ // Offset beforeCaptureTime by 1 second to account for rounding down of
+ // DNG tag
+ long beforeCaptureTimeMs = beforeCaptureDate.getTime() - 1000;
+ long afterCaptureTimeMs = afterCaptureDate.getTime();
+ assertTrue(captureTimeMs >= beforeCaptureTimeMs);
+ assertTrue(captureTimeMs <= afterCaptureTimeMs);
+
if (!VERBOSE) {
// Delete the captured DNG file.
File dngFile = new File(filePath);
diff --git a/tests/tests/media/src/android/media/cts/DeviceUtils.java b/tests/tests/media/src/android/media/cts/DeviceUtils.java
index 41afce1..c2b1c32 100644
--- a/tests/tests/media/src/android/media/cts/DeviceUtils.java
+++ b/tests/tests/media/src/android/media/cts/DeviceUtils.java
@@ -16,14 +16,21 @@
package android.media.cts;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+
import android.media.AudioDeviceInfo;
import android.media.AudioManager;
+import android.util.Log;
+
/* package */ class DeviceUtils {
private static final String TAG = "DeviceUtils";
/* package */ static boolean hasOutputDevice(AudioManager audioMgr) {
-
AudioDeviceInfo[] devices = audioMgr.getDevices(AudioManager.GET_DEVICES_OUTPUTS);
return devices.length != 0;
}
@@ -32,4 +39,27 @@
AudioDeviceInfo[] devices = audioMgr.getDevices(AudioManager.GET_DEVICES_INPUTS);
return devices.length != 0;
}
+
+ /* package */ static boolean isTVDevice(Context context) {
+ return context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK);
+ }
+
+ /*
+ * HDMI
+ */
+ /* package */ static boolean isHDMIConnected(Context context) {
+ // configure the IntentFilter
+ IntentFilter intentFilter = new IntentFilter();
+ intentFilter.addAction(AudioManager.ACTION_HDMI_AUDIO_PLUG);
+ Intent intent = context.registerReceiver(null, intentFilter);
+
+ String action = intent.getAction();
+ boolean isHDMIConnected = false;
+ if (action.equals(AudioManager.ACTION_HDMI_AUDIO_PLUG)) {
+ isHDMIConnected =
+ intent.getIntExtra(AudioManager.EXTRA_AUDIO_PLUG_STATE, 0) != 0;
+ }
+
+ return isHDMIConnected;
+ }
}
diff --git a/tests/tests/media/src/android/media/cts/EnumDevicesTest.java b/tests/tests/media/src/android/media/cts/EnumDevicesTest.java
index 94af087..e3ed453 100644
--- a/tests/tests/media/src/android/media/cts/EnumDevicesTest.java
+++ b/tests/tests/media/src/android/media/cts/EnumDevicesTest.java
@@ -28,10 +28,14 @@
import android.test.AndroidTestCase;
+import android.util.Log;
+
/**
* TODO: Insert description here. (generated by pmclean)
*/
public class EnumDevicesTest extends AndroidTestCase {
+ private static final String TAG = "EnumDevicesTest";
+
private AudioManager mAudioManager;
boolean mAddCallbackCalled = false;
@@ -54,27 +58,41 @@
assertTrue(deviceList != null);
assertTrue(deviceList.length == 0);
+ PackageManager pkgMgr = mContext.getPackageManager();
+
+ boolean isTvDevice = DeviceUtils.isTVDevice(mContext);
+
int numOutputDevices = 0;
- if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUDIO_OUTPUT)) {
+ if (pkgMgr.hasSystemFeature(PackageManager.FEATURE_AUDIO_OUTPUT)) {
// test OUTPUTS
deviceList = mAudioManager.getDevices(AudioManager.GET_DEVICES_OUTPUTS);
assertTrue(deviceList != null);
- numOutputDevices = deviceList.length;
- assertTrue(numOutputDevices != 0);
- // all should be "sinks"
+ numOutputDevices = deviceList.length;
+ if (numOutputDevices == 0) {
+ boolean isHDMIConnected = DeviceUtils.isHDMIConnected(mContext);
+ if (isTvDevice && !isHDMIConnected) {
+ Log.w(TAG, "getDevices test: failure due to missing reported output " +
+ "or the test is run on a TV device with no HDMI connected");
+ }
+ assertTrue("getDevices test: failure due to missing HDMI connection " +
+ "or missing output", false);
+ }
+
+ // any reported output devices should be "sinks"
for(int index = 0; index < numOutputDevices; index++) {
assertTrue(deviceList[index].isSink());
}
}
int numInputDevices = 0;
- if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_MICROPHONE)) {
+ if (pkgMgr.hasSystemFeature(PackageManager.FEATURE_MICROPHONE)) {
// test INPUTS
deviceList = mAudioManager.getDevices(AudioManager.GET_DEVICES_INPUTS);
assertTrue(deviceList != null);
+
numInputDevices = deviceList.length;
- assertTrue(numInputDevices != 0);
+ assertTrue(numOutputDevices != 0);
// all should be "sources"
for(int index = 0; index < numInputDevices; index++) {
@@ -86,6 +104,7 @@
if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUDIO_OUTPUT) &&
mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_MICROPHONE)) {
deviceList = mAudioManager.getDevices(AudioManager.GET_DEVICES_ALL);
+ assertTrue(deviceList != null);
assertTrue(deviceList.length == (numOutputDevices + numInputDevices));
}
}
diff --git a/tests/tests/os/src/android/os/cts/SecurityPatchTest.java b/tests/tests/os/src/android/os/cts/SecurityPatchTest.java
index 4aaf7f2..2a5e360 100644
--- a/tests/tests/os/src/android/os/cts/SecurityPatchTest.java
+++ b/tests/tests/os/src/android/os/cts/SecurityPatchTest.java
@@ -32,7 +32,7 @@
private static final String SECURITY_PATCH_DATE_ERROR =
"ro.build.version.security_patch should be \"%d-%02d\" or later. Found \"%s\"";
private static final int SECURITY_PATCH_YEAR = 2016;
- private static final int SECURITY_PATCH_MONTH = 10;
+ private static final int SECURITY_PATCH_MONTH = 11;
private boolean mSkipTests = false;
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/SingleSourceForEachTest.java b/tests/tests/renderscript/src/android/renderscript/cts/SingleSourceForEachTest.java
index e005013..a55d847 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/SingleSourceForEachTest.java
+++ b/tests/tests/renderscript/src/android/renderscript/cts/SingleSourceForEachTest.java
@@ -83,6 +83,12 @@
}
public void testKernelLaunchWithOptions() {
+ // Initialize the testOutputAlloc and baselineOutputAlloc to be the same
+ // as the input. When we the check the entire output later, the lower half
+ // of the output Allocations should also be the same.
+ baselineOutputAlloc.copyFrom(testInputArray);
+ testOutputAlloc.copyFrom(testInputArray);
+
Script.LaunchOptions sc = new Script.LaunchOptions();
sc.setX(0, X);
sc.setY(0, Y / 2);
diff --git a/tests/tests/systemintents/Android.mk b/tests/tests/systemintents/Android.mk
new file mode 100644
index 0000000..1af6702
--- /dev/null
+++ b/tests/tests/systemintents/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)
+
+# Tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_PACKAGE_NAME := CtsSystemIntentTestCases
+
+LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner
+
+LOCAL_SDK_VERSION := test_current
+
+include $(BUILD_CTS_PACKAGE)
diff --git a/tests/tests/systemintents/AndroidManifest.xml b/tests/tests/systemintents/AndroidManifest.xml
new file mode 100644
index 0000000..da0cbac
--- /dev/null
+++ b/tests/tests/systemintents/AndroidManifest.xml
@@ -0,0 +1,29 @@
+<?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.systemintents.cts">
+
+ <application>
+ <uses-library android:name="android.test.runner" />
+ </application>
+
+ <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+ android:targetPackage="android.systemintents.cts"
+ android:label="System intent tests"/>
+</manifest>
\ No newline at end of file
diff --git a/tests/tests/systemintents/AndroidTest.xml b/tests/tests/systemintents/AndroidTest.xml
new file mode 100644
index 0000000..eceb909
--- /dev/null
+++ b/tests/tests/systemintents/AndroidTest.xml
@@ -0,0 +1,25 @@
+<?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 system intent test cases">
+ <target_preparer class="com.android.compatibility.common.tradefed.targetprep.ApkInstaller">
+ <option name="cleanup-apks" value="true" />
+ <option name="test-file-name" value="CtsSystemIntentTestCases.apk" />
+ </target_preparer>
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+ <option name="package" value="android.systemintents.cts" />
+ </test>
+</configuration>
\ No newline at end of file
diff --git a/tests/tests/systemintents/src/android/systemintents/cts/TestSystemIntents.java b/tests/tests/systemintents/src/android/systemintents/cts/TestSystemIntents.java
new file mode 100644
index 0000000..c572629
--- /dev/null
+++ b/tests/tests/systemintents/src/android/systemintents/cts/TestSystemIntents.java
@@ -0,0 +1,102 @@
+/*
+ * 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.systemintents.cts;
+
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.res.Configuration;
+import android.net.Uri;
+import android.provider.Settings;
+import android.support.test.filters.MediumTest;
+import android.test.AndroidTestCase;
+import android.util.Log;
+
+import org.junit.Rule;
+import org.junit.Test;
+
+@MediumTest
+public class TestSystemIntents extends AndroidTestCase {
+ /*
+ * List of activity intents defined by the system. Activities to handle each of these
+ * intents must all exist.
+ *
+ * They are Intents here rather than simply action strings so that the test can
+ * easily accommodate data URIs or similar for correct resolution.
+ *
+ * The flags associated with each intent indicate kinds of device on which the given
+ * UI intent is *not* applicable.
+ */
+
+ private static final int EXCLUDE_TV = 1 << 0;
+ private static final int EXCLUDE_WATCH = 1 << 1;
+ private static final int EXCLUDE_NON_TELEPHONY = 1 << 2;
+
+ class IntentEntry {
+ public int flags;
+ public Intent intent;
+
+ public IntentEntry(int f, Intent i) {
+ flags = f;
+ intent = i;
+ }
+ }
+
+ @Rule
+ private final IntentEntry[] mTestIntents = {
+ /* Settings-namespace intent actions */
+ new IntentEntry(0, new Intent(Settings.ACTION_SETTINGS)),
+ new IntentEntry(0, new Intent(Settings.ACTION_WEBVIEW_SETTINGS)),
+ new IntentEntry(0, new Intent(Settings.ACTION_APPLICATION_DEVELOPMENT_SETTINGS)),
+ new IntentEntry(0, new Intent(Settings.ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS)),
+ new IntentEntry(0, new Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS)
+ .setData(Uri.parse("package:android.systemintents.cts"))),
+ new IntentEntry(0, new Intent(Settings.ACTION_IGNORE_BACKGROUND_DATA_RESTRICTIONS_SETTINGS)
+ .setData(Uri.parse("package:android.systemintents.cts"))),
+ new IntentEntry(0, new Intent(Settings.ACTION_HOME_SETTINGS)),
+ new IntentEntry(EXCLUDE_NON_TELEPHONY,
+ new Intent(Settings.ACTION_APN_SETTINGS)),
+ new IntentEntry(EXCLUDE_TV|EXCLUDE_WATCH,
+ new Intent(Settings.ACTION_NOTIFICATION_POLICY_ACCESS_SETTINGS))
+ };
+
+ @Test
+ public void testSystemIntents() {
+ final PackageManager pm = getContext().getPackageManager();
+ int productFlags = 0;
+
+ if (pm.hasSystemFeature(PackageManager.FEATURE_LEANBACK)) {
+ productFlags |= EXCLUDE_TV;
+ }
+
+ if (!pm.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
+ productFlags |= EXCLUDE_NON_TELEPHONY;
+ }
+
+ final Configuration config = getContext().getResources().getConfiguration();
+ if ((config.uiMode & Configuration.UI_MODE_TYPE_WATCH) != 0) {
+ productFlags |= EXCLUDE_WATCH;
+ }
+
+ for (IntentEntry e : mTestIntents) {
+ if ((productFlags & e.flags) == 0) {
+ final ResolveInfo ri = pm.resolveActivity(e.intent, PackageManager.MATCH_DEFAULT_ONLY);
+ assertTrue("API intent " + e.intent + " not implemented by any activity", ri != null);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/tests/tests/telephony/src/android/telephony/cts/SmsMessageTest.java b/tests/tests/telephony/src/android/telephony/cts/SmsMessageTest.java
index 2406ad8..e247775 100644
--- a/tests/tests/telephony/src/android/telephony/cts/SmsMessageTest.java
+++ b/tests/tests/telephony/src/android/telephony/cts/SmsMessageTest.java
@@ -114,7 +114,7 @@
// Test create from null Pdu
sms = SmsMessage.createFromPdu(null, SmsMessage.FORMAT_3GPP);
- assertNotNull(sms);
+ assertNull(sms);
// Test create from long Pdu
pdu = "07912160130310F2040B915121927786F300036060924180008A0DA"
diff --git a/tests/tests/view/AndroidManifest.xml b/tests/tests/view/AndroidManifest.xml
index 7f7bd8c..1061ff3 100644
--- a/tests/tests/view/AndroidManifest.xml
+++ b/tests/tests/view/AndroidManifest.xml
@@ -241,6 +241,14 @@
</intent-filter>
</activity>
+ <activity android:name="android.view.cts.DragDropActivity"
+ android:label="DragDropActivity">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST" />
+ </intent-filter>
+ </activity>
+
<activity android:name="android.view.cts.surfacevalidator.CapturedActivity"
android:theme="@style/WhiteBackgroundTheme">
<intent-filter>
diff --git a/tests/tests/view/res/layout/drag_drop_layout.xml b/tests/tests/view/res/layout/drag_drop_layout.xml
new file mode 100644
index 0000000..3800d99
--- /dev/null
+++ b/tests/tests/view/res/layout/drag_drop_layout.xml
@@ -0,0 +1,50 @@
+<?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.
+ -->
+
+<LinearLayout
+ android:id="@+id/drag_drop_activity_main"
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+ <FrameLayout
+ android:id="@+id/container"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_margin="64px"
+ android:background="#BBBBBB">
+ <FrameLayout
+ android:id="@+id/subcontainer"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_margin="64px"
+ android:background="#666666">
+ <View
+ android:id="@+id/inner"
+ android:layout_width="64px"
+ android:layout_height="64px"
+ android:layout_margin="64px"
+ android:background="#00FF00" />
+ </FrameLayout>
+ </FrameLayout>
+ <View
+ android:id="@+id/draggable"
+ android:layout_width="64px"
+ android:layout_height="64px"
+ android:layout_margin="64px"
+ android:background="#0000FF" />
+</LinearLayout>
diff --git a/tests/tests/view/src/android/view/cts/DragDropActivity.java b/tests/tests/view/src/android/view/cts/DragDropActivity.java
new file mode 100644
index 0000000..b4324e3
--- /dev/null
+++ b/tests/tests/view/src/android/view/cts/DragDropActivity.java
@@ -0,0 +1,31 @@
+/*
+ * 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.view.cts;
+
+import android.view.cts.R;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+public class DragDropActivity extends Activity {
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.drag_drop_layout);
+ }
+}
\ No newline at end of file
diff --git a/tests/tests/view/src/android/view/cts/DragDropTest.java b/tests/tests/view/src/android/view/cts/DragDropTest.java
new file mode 100644
index 0000000..968dc7b
--- /dev/null
+++ b/tests/tests/view/src/android/view/cts/DragDropTest.java
@@ -0,0 +1,592 @@
+/*
+ * 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.view.cts;
+
+import com.android.internal.util.ArrayUtils;
+
+import android.app.Instrumentation;
+import android.app.UiAutomation;
+import android.content.pm.PackageManager;
+import android.os.SystemClock;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+import android.view.DragEvent;
+import android.view.InputDevice;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.lang.InterruptedException;
+import java.lang.StringBuilder;
+import java.lang.Thread;
+import java.util.ArrayList;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.Objects;
+
+import static junit.framework.TestCase.*;
+
+@RunWith(AndroidJUnit4.class)
+public class DragDropTest {
+ static final String TAG = "DragDropTest";
+
+ final Instrumentation mInstrumentation = InstrumentationRegistry.getInstrumentation();
+ final UiAutomation mAutomation = mInstrumentation.getUiAutomation();
+
+ @Rule
+ public ActivityTestRule<DragDropActivity> mActivityRule =
+ new ActivityTestRule<>(DragDropActivity.class);
+
+ private DragDropActivity mActivity;
+
+ private CountDownLatch mEndReceived;
+
+ static boolean equal(DragEvent ev1, DragEvent ev2) {
+ return ev1.getAction() == ev2.getAction() &&
+ ev1.getX() == ev2.getX() &&
+ ev1.getY() == ev2.getY() &&
+ Objects.equals(ev1.getClipData(), ev2.getClipData()) &&
+ Objects.equals(ev1.getClipDescription(), ev2.getClipDescription()) &&
+ Objects.equals(ev1.getDragAndDropPermissions(), ev2.getDragAndDropPermissions()) &&
+ Objects.equals(ev1.getLocalState(), ev2.getLocalState()) &&
+ ev1.getResult() == ev2.getResult();
+ }
+
+ class LogEntry {
+ public View v;
+ public DragEvent ev;
+
+ public LogEntry(View v, DragEvent ev) {
+ this.v = v;
+ this.ev = DragEvent.obtain(ev);
+ }
+
+ public boolean eq(LogEntry other) {
+ return v == other.v && equal(ev, other.ev);
+ }
+ }
+
+ // Actual and expected sequences of events.
+ // While the test is running, logs should be accessed only from the main thread.
+ final private ArrayList<LogEntry> mActual = new ArrayList<LogEntry> ();
+ final private ArrayList<LogEntry> mExpected = new ArrayList<LogEntry> ();
+
+ static private DragEvent obtainDragEvent(int action, int x, int y, boolean result) {
+ return DragEvent.obtain(action, x, y, null, null, null, null, result);
+ }
+
+ private void logEvent(View v, DragEvent ev) {
+ if (ev.getAction() == DragEvent.ACTION_DRAG_ENDED) {
+ mEndReceived.countDown();
+ }
+ mActual.add(new LogEntry(v, ev));
+ }
+
+ // Add expected event for a view, with zero coordinates.
+ private void expectEvent5(int action, int viewId) {
+ View v = mActivity.findViewById(viewId);
+ mExpected.add(new LogEntry(v, obtainDragEvent(action, 0, 0, false)));
+ }
+
+ // Add expected event for a view.
+ private void expectEndEvent(int viewId, int x, int y, boolean result) {
+ View v = mActivity.findViewById(viewId);
+ mExpected.add(new LogEntry(v, obtainDragEvent(DragEvent.ACTION_DRAG_ENDED, x, y, result)));
+ }
+
+ // Add expected successful-end event for a view.
+ private void expectEndEventSuccess(int viewId) {
+ expectEndEvent(viewId, 0, 0, true);
+ }
+
+ // Add expected failed-end event for a view, with the release coordinates shifted by 6 relative
+ // to the left-upper corner of a view with id releaseViewId.
+ private void expectEndEventFailure6(int viewId, int releaseViewId) {
+ View v = mActivity.findViewById(viewId);
+ View release = mActivity.findViewById(releaseViewId);
+ int [] releaseLoc = release.getLocationOnScreen();
+ mExpected.add(new LogEntry(v, obtainDragEvent(DragEvent.ACTION_DRAG_ENDED,
+ releaseLoc[0] + 6, releaseLoc[1] + 6, false)));
+ }
+
+ // Add expected event for a view, with coordinates over view locationViewId, with the specified
+ // offset from the location view's upper-left corner.
+ private void expectEventWithOffset(int action, int viewId, int locationViewId, int offset) {
+ View v = mActivity.findViewById(viewId);
+ View locationView = mActivity.findViewById(locationViewId);
+ int [] viewLocation = v.getLocationOnScreen();
+ int [] locationViewLocation = locationView.getLocationOnScreen();
+ mExpected.add(new LogEntry(v, obtainDragEvent(action,
+ locationViewLocation[0] - viewLocation[0] + offset,
+ locationViewLocation[1] - viewLocation[1] + offset, false)));
+ }
+
+ private void expectEvent5(int action, int viewId, int locationViewId) {
+ expectEventWithOffset(action, viewId, locationViewId, 5);
+ }
+
+ // See comment for injectMouse6 on why we need both *5 and *6 methods.
+ private void expectEvent6(int action, int viewId, int locationViewId) {
+ expectEventWithOffset(action, viewId, locationViewId, 6);
+ }
+
+ // Inject mouse event over a given view, with specified offset from its left-upper corner.
+ private void injectMouseWithOffset(int viewId, int action, int offset) {
+ runOnMain(() -> {
+ View v = mActivity.findViewById(viewId);
+ int [] destLoc = v.getLocationOnScreen();
+ long downTime = SystemClock.uptimeMillis();
+ MotionEvent event = MotionEvent.obtain(downTime, downTime, action,
+ destLoc[0] + offset, destLoc[1] + offset, 1);
+ event.setSource(InputDevice.SOURCE_TOUCHSCREEN);
+ mAutomation.injectInputEvent(event, false);
+ });
+
+ // Wait till the mouse event generates drag events. Also, some waiting needed because the
+ // system seems to collapse too frequent mouse events.
+ try {
+ Thread.sleep(100);
+ } catch (Exception e) {
+ fail("Exception while wait: " + e);
+ }
+ }
+
+ // Inject mouse event over a given view, with offset 5 from its left-upper corner.
+ private void injectMouse5(int viewId, int action) {
+ injectMouseWithOffset(viewId, action, 5);
+ }
+
+ // Inject mouse event over a given view, with offset 6 from its left-upper corner.
+ // We need both injectMouse5 and injectMouse6 if we want to inject 2 events in a row in the same
+ // view, and want them to produce distinct drag events or simply drag events with different
+ // coordinates.
+ private void injectMouse6(int viewId, int action) {
+ injectMouseWithOffset(viewId, action, 6);
+ }
+
+ private String logToString(ArrayList<LogEntry> log) {
+ StringBuilder sb = new StringBuilder();
+ for (int i = 0; i < log.size(); ++i) {
+ LogEntry e = log.get(i);
+ sb.append("#").append(i + 1).append(": ").append(e.ev).append(" @ ").
+ append(e.v.toString()).append('\n');
+ }
+ return sb.toString();
+ }
+
+ private void failWithLogs(String message) {
+ fail(message + ":\nExpected event sequence:\n" + logToString(mExpected) +
+ "\nActual event sequence:\n" + logToString(mActual));
+ }
+
+ private void verifyEventLog() {
+ try {
+ assertTrue("Timeout while waiting for END event",
+ mEndReceived.await(1, TimeUnit.SECONDS));
+ } catch (InterruptedException e) {
+ fail("Got InterruptedException while waiting for END event");
+ }
+
+ // Verify the log.
+ runOnMain(() -> {
+ if (mExpected.size() != mActual.size()) {
+ failWithLogs("Actual log has different size than expected");
+ }
+
+ for (int i = 0; i < mActual.size(); ++i) {
+ if (!mActual.get(i).eq(mExpected.get(i))) {
+ failWithLogs("Actual event #" + (i + 1) + " is different from expected");
+ }
+ }
+ });
+ }
+
+ private boolean init() {
+ // Only run for non-watch devices
+ if (mActivity.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH)) {
+ return false;
+ }
+ return true;
+ }
+
+ @Before
+ public void setUp() {
+ mActivity = mActivityRule.getActivity();
+ mEndReceived = new CountDownLatch(1);
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ mActual.clear();
+ mExpected.clear();
+ }
+
+ // Sets handlers on all views in a tree, which log the event and return false.
+ private void setRejectingHandlersOnTree(View v) {
+ v.setOnDragListener((_v, ev) -> {
+ logEvent(_v, ev);
+ return false;
+ });
+
+ if (v instanceof ViewGroup) {
+ ViewGroup group = (ViewGroup) v;
+ for (int i = 0; i < group.getChildCount(); ++i) {
+ setRejectingHandlersOnTree(group.getChildAt(i));
+ }
+ }
+ }
+
+ private void runOnMain(Runnable runner) {
+ mInstrumentation.runOnMainSync(runner);
+ }
+
+ private void startDrag() {
+ // Mouse down. Required for the drag to start.
+ injectMouse5(R.id.draggable, MotionEvent.ACTION_DOWN);
+
+ runOnMain(() -> {
+ // Start drag.
+ View v = mActivity.findViewById(R.id.draggable);
+ assertTrue("Couldn't start drag",
+ v.startDragAndDrop(null, new View.DragShadowBuilder(v), null, 0));
+ });
+ }
+
+ /**
+ * Tests that no drag-drop events are sent to views that aren't supposed to receive them.
+ */
+ @Test
+ public void testNoExtraEvents() throws Exception {
+ if (!init()) {
+ return;
+ }
+
+ runOnMain(() -> {
+ // Tell all views in layout to return false to all events, and log them.
+ setRejectingHandlersOnTree(mActivity.findViewById(R.id.drag_drop_activity_main));
+
+ // Override handlers for the inner view and its parent to return true.
+ mActivity.findViewById(R.id.inner).setOnDragListener((v, ev) -> {
+ logEvent(v, ev);
+ return true;
+ });
+ mActivity.findViewById(R.id.subcontainer).setOnDragListener((v, ev) -> {
+ logEvent(v, ev);
+ return true;
+ });
+ });
+
+ startDrag();
+
+ // Move mouse to the outmost view. This shouldn't generate any events since it returned
+ // false to STARTED.
+ injectMouse5(R.id.container, MotionEvent.ACTION_MOVE);
+ // Release mouse over the inner view. This produces DROP there.
+ injectMouse5(R.id.inner, MotionEvent.ACTION_UP);
+
+ expectEvent5(DragEvent.ACTION_DRAG_STARTED, R.id.inner, R.id.draggable);
+ expectEvent5(DragEvent.ACTION_DRAG_STARTED, R.id.subcontainer, R.id.draggable);
+ expectEvent5(DragEvent.ACTION_DRAG_STARTED, R.id.container, R.id.draggable);
+ expectEvent5(DragEvent.ACTION_DRAG_STARTED, R.id.draggable, R.id.draggable);
+ expectEvent5(DragEvent.ACTION_DRAG_STARTED, R.id.drag_drop_activity_main, R.id.draggable);
+
+ expectEvent5(DragEvent.ACTION_DRAG_ENTERED, R.id.inner);
+ expectEvent5(DragEvent.ACTION_DROP, R.id.inner, R.id.inner);
+
+ expectEndEventSuccess(R.id.inner);
+ expectEndEventSuccess(R.id.subcontainer);
+
+ verifyEventLog();
+ }
+
+ /**
+ * Tests events over a non-accepting view with an accepting child get delivered to that view's
+ * parent.
+ */
+ @Test
+ public void testBlackHole() throws Exception {
+ if (!init()) {
+ return;
+ }
+
+ runOnMain(() -> {
+ // Accepting child.
+ mActivity.findViewById(R.id.inner).setOnDragListener((v, ev) -> {
+ logEvent(v, ev);
+ return true;
+ });
+ // Non-accepting parent of that child.
+ mActivity.findViewById(R.id.subcontainer).setOnDragListener((v, ev) -> {
+ logEvent(v, ev);
+ return false;
+ });
+ // Accepting parent of the previous view.
+ mActivity.findViewById(R.id.container).setOnDragListener((v, ev) -> {
+ logEvent(v, ev);
+ return true;
+ });
+ });
+
+ startDrag();
+
+ // Move mouse to the non-accepting view.
+ injectMouse5(R.id.subcontainer, MotionEvent.ACTION_MOVE);
+ // Release mouse over the non-accepting view, with different coordinates.
+ injectMouse6(R.id.subcontainer, MotionEvent.ACTION_UP);
+
+ expectEvent5(DragEvent.ACTION_DRAG_STARTED, R.id.inner, R.id.draggable);
+ expectEvent5(DragEvent.ACTION_DRAG_STARTED, R.id.subcontainer, R.id.draggable);
+ expectEvent5(DragEvent.ACTION_DRAG_STARTED, R.id.container, R.id.draggable);
+
+ expectEvent5(DragEvent.ACTION_DRAG_ENTERED, R.id.container);
+ expectEvent5(DragEvent.ACTION_DRAG_LOCATION, R.id.container, R.id.subcontainer);
+ expectEvent6(DragEvent.ACTION_DROP, R.id.container, R.id.subcontainer);
+
+ expectEndEventSuccess(R.id.inner);
+ expectEndEventSuccess(R.id.container);
+
+ verifyEventLog();
+ }
+
+ /**
+ * Tests generation of ENTER/EXIT events.
+ */
+ @Test
+ public void testEnterExit() throws Exception {
+ if (!init()) {
+ return;
+ }
+
+ runOnMain(() -> {
+ // The setup is same as for testBlackHole.
+
+ // Accepting child.
+ mActivity.findViewById(R.id.inner).setOnDragListener((v, ev) -> {
+ logEvent(v, ev);
+ return true;
+ });
+ // Non-accepting parent of that child.
+ mActivity.findViewById(R.id.subcontainer).setOnDragListener((v, ev) -> {
+ logEvent(v, ev);
+ return false;
+ });
+ // Accepting parent of the previous view.
+ mActivity.findViewById(R.id.container).setOnDragListener((v, ev) -> {
+ logEvent(v, ev);
+ return true;
+ });
+
+ });
+
+ startDrag();
+
+ // Move mouse to the non-accepting view, then to the inner one, then back to the
+ // non-accepting view, then release over the inner.
+ injectMouse5(R.id.subcontainer, MotionEvent.ACTION_MOVE);
+ injectMouse5(R.id.inner, MotionEvent.ACTION_MOVE);
+ injectMouse5(R.id.subcontainer, MotionEvent.ACTION_MOVE);
+ injectMouse5(R.id.inner, MotionEvent.ACTION_UP);
+
+ expectEvent5(DragEvent.ACTION_DRAG_STARTED, R.id.inner, R.id.draggable);
+ expectEvent5(DragEvent.ACTION_DRAG_STARTED, R.id.subcontainer, R.id.draggable);
+ expectEvent5(DragEvent.ACTION_DRAG_STARTED, R.id.container, R.id.draggable);
+
+ expectEvent5(DragEvent.ACTION_DRAG_ENTERED, R.id.container);
+ expectEvent5(DragEvent.ACTION_DRAG_LOCATION, R.id.container, R.id.subcontainer);
+ expectEvent5(DragEvent.ACTION_DRAG_EXITED, R.id.container);
+
+ expectEvent5(DragEvent.ACTION_DRAG_ENTERED, R.id.inner);
+ expectEvent5(DragEvent.ACTION_DRAG_LOCATION, R.id.inner, R.id.inner);
+ expectEvent5(DragEvent.ACTION_DRAG_EXITED, R.id.inner);
+
+ expectEvent5(DragEvent.ACTION_DRAG_ENTERED, R.id.container);
+ expectEvent5(DragEvent.ACTION_DRAG_LOCATION, R.id.container, R.id.subcontainer);
+ expectEvent5(DragEvent.ACTION_DRAG_EXITED, R.id.container);
+
+ expectEvent5(DragEvent.ACTION_DRAG_ENTERED, R.id.inner);
+ expectEvent5(DragEvent.ACTION_DROP, R.id.inner, R.id.inner);
+
+ expectEndEventSuccess(R.id.inner);
+ expectEndEventSuccess(R.id.container);
+
+ verifyEventLog();
+ }
+ /**
+ * Tests events over a non-accepting view that has no accepting ancestors.
+ */
+ @Test
+ public void testOverNowhere() throws Exception {
+ if (!init()) {
+ return;
+ }
+
+ runOnMain(() -> {
+ // Accepting child.
+ mActivity.findViewById(R.id.inner).setOnDragListener((v, ev) -> {
+ logEvent(v, ev);
+ return true;
+ });
+ // Non-accepting parent of that child.
+ mActivity.findViewById(R.id.subcontainer).setOnDragListener((v, ev) -> {
+ logEvent(v, ev);
+ return false;
+ });
+ });
+
+ startDrag();
+
+ // Move mouse to the non-accepting view, then to accepting view, and back, and drop there.
+ injectMouse5(R.id.subcontainer, MotionEvent.ACTION_MOVE);
+ injectMouse5(R.id.inner, MotionEvent.ACTION_MOVE);
+ injectMouse5(R.id.subcontainer, MotionEvent.ACTION_MOVE);
+ injectMouse6(R.id.subcontainer, MotionEvent.ACTION_UP);
+
+ expectEvent5(DragEvent.ACTION_DRAG_STARTED, R.id.inner, R.id.draggable);
+ expectEvent5(DragEvent.ACTION_DRAG_STARTED, R.id.subcontainer, R.id.draggable);
+
+ expectEvent5(DragEvent.ACTION_DRAG_ENTERED, R.id.inner);
+ expectEvent5(DragEvent.ACTION_DRAG_LOCATION, R.id.inner, R.id.inner);
+ expectEvent5(DragEvent.ACTION_DRAG_EXITED, R.id.inner);
+
+ expectEndEventFailure6(R.id.inner, R.id.subcontainer);
+
+ verifyEventLog();
+ }
+
+ /**
+ * Tests that events are properly delivered to a view that is in the middle of the accepting
+ * hierarchy.
+ */
+ @Test
+ public void testAcceptingGroupInTheMiddle() throws Exception {
+ if (!init()) {
+ return;
+ }
+
+ runOnMain(() -> {
+ // Set accepting handlers to the inner view and its 2 ancestors.
+ mActivity.findViewById(R.id.inner).setOnDragListener((v, ev) -> {
+ logEvent(v, ev);
+ return true;
+ });
+ mActivity.findViewById(R.id.subcontainer).setOnDragListener((v, ev) -> {
+ logEvent(v, ev);
+ return true;
+ });
+ mActivity.findViewById(R.id.container).setOnDragListener((v, ev) -> {
+ logEvent(v, ev);
+ return true;
+ });
+ });
+
+ startDrag();
+
+ // Move mouse to the outmost container, then move to the subcontainer and drop there.
+ injectMouse5(R.id.container, MotionEvent.ACTION_MOVE);
+ injectMouse5(R.id.subcontainer, MotionEvent.ACTION_MOVE);
+ injectMouse6(R.id.subcontainer, MotionEvent.ACTION_UP);
+
+ expectEvent5(DragEvent.ACTION_DRAG_STARTED, R.id.inner, R.id.draggable);
+ expectEvent5(DragEvent.ACTION_DRAG_STARTED, R.id.subcontainer, R.id.draggable);
+ expectEvent5(DragEvent.ACTION_DRAG_STARTED, R.id.container, R.id.draggable);
+
+ expectEvent5(DragEvent.ACTION_DRAG_ENTERED, R.id.container);
+ expectEvent5(DragEvent.ACTION_DRAG_LOCATION, R.id.container, R.id.container);
+ expectEvent5(DragEvent.ACTION_DRAG_EXITED, R.id.container);
+
+ expectEvent5(DragEvent.ACTION_DRAG_ENTERED, R.id.subcontainer);
+ expectEvent5(DragEvent.ACTION_DRAG_LOCATION, R.id.subcontainer, R.id.subcontainer);
+ expectEvent6(DragEvent.ACTION_DROP, R.id.subcontainer, R.id.subcontainer);
+
+ expectEndEventSuccess(R.id.inner);
+ expectEndEventSuccess(R.id.subcontainer);
+ expectEndEventSuccess(R.id.container);
+
+ verifyEventLog();
+ }
+
+ /**
+ * Tests that state_drag_hovered and state_drag_can_accept are set correctly.
+ */
+ @Test
+ public void testDrawableState() throws Exception {
+ if (!init()) {
+ return;
+ }
+
+ runOnMain(() -> {
+ // Set accepting handler for the inner view.
+ mActivity.findViewById(R.id.inner).setOnDragListener((v, ev) -> {
+ logEvent(v, ev);
+ return true;
+ });
+ assertFalse(ArrayUtils.contains(
+ mActivity.findViewById(R.id.inner).getDrawableState(),
+ android.R.attr.state_drag_can_accept));
+ });
+
+ startDrag();
+
+ runOnMain(() -> {
+ assertFalse(ArrayUtils.contains(
+ mActivity.findViewById(R.id.inner).getDrawableState(),
+ android.R.attr.state_drag_hovered));
+ assertTrue(ArrayUtils.contains(
+ mActivity.findViewById(R.id.inner).getDrawableState(),
+ android.R.attr.state_drag_can_accept));
+ });
+
+ // Move mouse into the view.
+ injectMouse5(R.id.inner, MotionEvent.ACTION_MOVE);
+ runOnMain(() -> {
+ assertTrue(ArrayUtils.contains(
+ mActivity.findViewById(R.id.inner).getDrawableState(),
+ android.R.attr.state_drag_hovered));
+ });
+
+ // Move out.
+ injectMouse5(R.id.subcontainer, MotionEvent.ACTION_MOVE);
+ runOnMain(() -> {
+ assertFalse(ArrayUtils.contains(
+ mActivity.findViewById(R.id.inner).getDrawableState(),
+ android.R.attr.state_drag_hovered));
+ });
+
+ // Move in.
+ injectMouse5(R.id.inner, MotionEvent.ACTION_MOVE);
+ runOnMain(() -> {
+ assertTrue(ArrayUtils.contains(
+ mActivity.findViewById(R.id.inner).getDrawableState(),
+ android.R.attr.state_drag_hovered));
+ });
+
+ // Release there.
+ injectMouse5(R.id.inner, MotionEvent.ACTION_UP);
+ runOnMain(() -> {
+ assertFalse(ArrayUtils.contains(
+ mActivity.findViewById(R.id.inner).getDrawableState(),
+ android.R.attr.state_drag_hovered));
+ });
+ }
+}
\ No newline at end of file
diff --git a/tests/tests/widget/AndroidManifest.xml b/tests/tests/widget/AndroidManifest.xml
index 224966d..df6013d 100644
--- a/tests/tests/widget/AndroidManifest.xml
+++ b/tests/tests/widget/AndroidManifest.xml
@@ -380,6 +380,14 @@
</intent-filter>
</activity>
+ <activity android:name="android.widget.cts.TimePickerDialogCtsActivity"
+ android:label="TimePickerDialogCtsActivity">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST" />
+ </intent-filter>
+ </activity>
+
<activity android:name="android.widget.cts.CalendarViewCtsActivity"
android:label="CalendarViewCtsActivity">
<intent-filter>
diff --git a/tests/tests/widget/res/values-w320dp-h426dp/integers.xml b/tests/tests/widget/res/values-w320dp-h426dp/integers.xml
new file mode 100644
index 0000000..a9d049c
--- /dev/null
+++ b/tests/tests/widget/res/values-w320dp-h426dp/integers.xml
@@ -0,0 +1,20 @@
+<?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.
+-->
+
+<resources>
+ <integer name="date_picker_mode">2</integer>
+ <integer name="time_picker_mode">2</integer>
+</resources>
diff --git a/tests/tests/widget/res/values-w426dp-h320dp/integers.xml b/tests/tests/widget/res/values-w426dp-h320dp/integers.xml
new file mode 100644
index 0000000..a9d049c
--- /dev/null
+++ b/tests/tests/widget/res/values-w426dp-h320dp/integers.xml
@@ -0,0 +1,20 @@
+<?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.
+-->
+
+<resources>
+ <integer name="date_picker_mode">2</integer>
+ <integer name="time_picker_mode">2</integer>
+</resources>
diff --git a/tests/tests/widget/res/values/integers.xml b/tests/tests/widget/res/values/integers.xml
new file mode 100644
index 0000000..b2c1e65
--- /dev/null
+++ b/tests/tests/widget/res/values/integers.xml
@@ -0,0 +1,20 @@
+<?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.
+-->
+
+<resources>
+ <integer name="date_picker_mode">1</integer>
+ <integer name="time_picker_mode">1</integer>
+</resources>
diff --git a/tests/tests/widget/res/values/styles.xml b/tests/tests/widget/res/values/styles.xml
index 345b450..8529d73 100644
--- a/tests/tests/widget/res/values/styles.xml
+++ b/tests/tests/widget/res/values/styles.xml
@@ -193,6 +193,11 @@
<item name="android:windowSwipeToDismiss">false</item>
</style>
+ <style name="Theme_Holo_With_Material_Pickers" parent="@android:style/Theme.Holo">
+ <item name="android:timePickerStyle">@android:style/Widget.Material.TimePicker</item>
+ <item name="android:datePickerStyle">@android:style/Widget.Material.DatePicker</item>
+ </style>
+
<style name="PopupEmptyStyle" />
<style name="TabWidgetCustomStyle" parent="android:Widget.TabWidget">
diff --git a/tests/tests/widget/src/android/widget/cts/DatePickerDialogTest.java b/tests/tests/widget/src/android/widget/cts/DatePickerDialogTest.java
index 1477f73..74d0ff5 100644
--- a/tests/tests/widget/src/android/widget/cts/DatePickerDialogTest.java
+++ b/tests/tests/widget/src/android/widget/cts/DatePickerDialogTest.java
@@ -22,6 +22,7 @@
import android.content.Context;
import android.test.ActivityInstrumentationTestCase2;
import android.test.UiThreadTest;
+import android.widget.DatePicker;
/**
* Test {@link DatePickerDialog}.
@@ -48,7 +49,16 @@
new DatePickerDialog(mActivity, AlertDialog.THEME_TRADITIONAL, null, 1970, 1, 1);
- new DatePickerDialog(mActivity, AlertDialog.THEME_HOLO_DARK, null, 1970, 1, 1);
+ // Ensure the picker is shown using the Holo-style layout.
+ DatePickerDialog holoDialog = new DatePickerDialog(mActivity, AlertDialog.THEME_HOLO_DARK,
+ null, 1970, 1, 1);
+ assertEquals(DatePicker.MODE_SPINNER, holoDialog.getDatePicker().getMode());
+
+ // Ensure the picker is shown using the Material-style layout where available.
+ DatePickerDialog holoCalendarDialog = new DatePickerDialog(mActivity,
+ R.style.Theme_Holo_With_Material_Pickers, null, 1970, 1, 1);
+ final int expectedMode = mActivity.getResources().getInteger(R.integer.date_picker_mode);
+ assertEquals(expectedMode, holoCalendarDialog.getDatePicker().getMode());
new DatePickerDialog(mActivity,
android.R.style.Theme_Material_Dialog_Alert, null, 1970, 1, 1);
diff --git a/tests/tests/widget/src/android/widget/cts/ListPopupWindowTest.java b/tests/tests/widget/src/android/widget/cts/ListPopupWindowTest.java
index b50f8c9..b882c0c 100644
--- a/tests/tests/widget/src/android/widget/cts/ListPopupWindowTest.java
+++ b/tests/tests/widget/src/android/widget/cts/ListPopupWindowTest.java
@@ -508,7 +508,10 @@
final int[] lastChildOnScreenXY = new int[2];
lastListChild.getLocationOnScreen(lastChildOnScreenXY);
- assertTrue(lastChildOnScreenXY[1] + lastListChild.getHeight() <= promptViewOnScreenXY[1]);
+ // The child is above the prompt. They may overlap, as in the case
+ // when the list items do not all fit on screen, but this is still
+ // correct.
+ assertTrue(lastChildOnScreenXY[1] <= promptViewOnScreenXY[1]);
}
@Presubmit
diff --git a/tests/tests/widget/src/android/widget/cts/TimePickerDialogCtsActivity.java b/tests/tests/widget/src/android/widget/cts/TimePickerDialogCtsActivity.java
new file mode 100644
index 0000000..f5c544b
--- /dev/null
+++ b/tests/tests/widget/src/android/widget/cts/TimePickerDialogCtsActivity.java
@@ -0,0 +1,33 @@
+/*
+ * 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.widget.cts;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+/**
+ * A minimal application for TimePickerDialog test.
+ */
+public class TimePickerDialogCtsActivity extends Activity {
+ /**
+ * Called with the activity is first created.
+ */
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ }
+}
diff --git a/tests/tests/widget/src/android/widget/cts/TimePickerDialogTest.java b/tests/tests/widget/src/android/widget/cts/TimePickerDialogTest.java
new file mode 100644
index 0000000..73ddc04
--- /dev/null
+++ b/tests/tests/widget/src/android/widget/cts/TimePickerDialogTest.java
@@ -0,0 +1,72 @@
+/*
+ * 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.widget.cts;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.DatePickerDialog;
+import android.app.TimePickerDialog;
+import android.test.ActivityInstrumentationTestCase2;
+import android.test.UiThreadTest;
+import android.widget.TimePicker;
+
+/**
+ * Test {@link TimePickerDialog}.
+ */
+public class TimePickerDialogTest extends
+ ActivityInstrumentationTestCase2<TimePickerDialogCtsActivity> {
+
+ private Activity mActivity;
+
+ public TimePickerDialogTest() {
+ super(TimePickerDialogCtsActivity.class);
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ mActivity = getActivity();
+ }
+
+ @UiThreadTest
+ public void testConstructor() {
+ new TimePickerDialog(mActivity, null, 7, 0, true);
+
+ new TimePickerDialog(mActivity, AlertDialog.THEME_TRADITIONAL, null, 7, 0, true);
+
+ // Ensure the picker is shown using the Holo-style layout.
+ TimePickerDialog holoDialog = new TimePickerDialog(mActivity, AlertDialog.THEME_HOLO_DARK,
+ null, 7, 0, true);
+ assertEquals(TimePicker.MODE_SPINNER, holoDialog.getTimePicker().getMode());
+
+ // Ensure the picker is shown using the Material-style layout where available.
+ TimePickerDialog holoClockDialog = new TimePickerDialog(mActivity,
+ R.style.Theme_Holo_With_Material_Pickers, null, 7, 0, true);
+ final int expectedMode = mActivity.getResources().getInteger(R.integer.time_picker_mode);
+ assertEquals(expectedMode, holoClockDialog.getTimePicker().getMode());
+
+ new TimePickerDialog(mActivity,
+ android.R.style.Theme_Material_Dialog_Alert, null, 7, 0, true);
+
+ try {
+ new TimePickerDialog(null, null, 7, 0, true);
+ fail("should throw NullPointerException");
+ } catch (Exception e) {
+ }
+ }
+
+}
diff --git a/tools/cts-device-info/src/com/android/cts/deviceinfo/SampleDeviceInfo.java b/tools/cts-device-info/src/com/android/cts/deviceinfo/SampleDeviceInfo.java
index 8d3d7ada..67db187 100644
--- a/tools/cts-device-info/src/com/android/cts/deviceinfo/SampleDeviceInfo.java
+++ b/tools/cts-device-info/src/com/android/cts/deviceinfo/SampleDeviceInfo.java
@@ -33,6 +33,7 @@
double[] doubles = {Double.MAX_VALUE, Double.MIN_VALUE};
int[] ints = {Integer.MAX_VALUE, Integer.MIN_VALUE};
long[] longs = {Long.MAX_VALUE, Long.MIN_VALUE};
+ float[] floats = {Float.MAX_VALUE, Float.MIN_VALUE, Float.NaN};
// Group Foo
store.startGroup("foo");
@@ -49,12 +50,16 @@
store.addArrayResult("bar_double", doubles);
store.addArrayResult("bar_int", ints);
store.addArrayResult("bar_long", longs);
+ store.addArrayResult("bar_float", floats);
store.endGroup(); // bar
store.addResult("foo_double", Double.MAX_VALUE);
store.addResult("foo_int", Integer.MAX_VALUE);
store.addResult("foo_long", Long.MAX_VALUE);
store.addResult("foo_string", "foo-string");
+ store.addResult("foo_float_nan", Float.NaN);
+ store.addResult("foo_float_max", Float.MAX_VALUE + 1);
+ store.addResult("foo_float_min", Float.MIN_VALUE - 1);
store.endGroup(); // foo
}
}
diff --git a/tools/cts-tradefed/res/config/cts-known-failures.xml b/tools/cts-tradefed/res/config/cts-known-failures.xml
index e7b08cb..84528d5 100644
--- a/tools/cts-tradefed/res/config/cts-known-failures.xml
+++ b/tools/cts-tradefed/res/config/cts-known-failures.xml
@@ -207,6 +207,9 @@
<option name="compatibility:exclude-filter" value="CtsUsageStatsTestCases android.app.usage.cts.UsageStatsTest#testPackageUsageStatsIntervals" />
<option name="compatibility:exclude-filter" value="CtsUsageStatsTestCases android.app.usage.cts.UsageStatsTest#testUsageEventsParceling" />
+ <!-- b/31469490 -->
+ <option name="compatibility:exclude-filter" value="CtsViewTestCases android.view.cts.DragDropTest" />
+
<!-- b/23238984 -->
<option name="compatibility:exclude-filter" value="CtsVoiceSettingsTestCases android.voicesettings.cts.ZenModeTest#testAll" />