am 0ee0fa2f: (-s ours) Reconcile with jb-mr1.1-release - do not merge
# Via The Android Automerger (3) and The Android Open Source Project (1)
* commit '0ee0fa2f625c61ee6e631dceb0ee81918fd96df3':
diff --git a/Android.mk b/Android.mk
index 4251262..dbcc97b 100644
--- a/Android.mk
+++ b/Android.mk
@@ -14,6 +14,7 @@
# limitations under the License.
#
+include cts/suite/pts/PtsBenchmarkingList.mk
include cts/CtsBuild.mk
include cts/CtsCoverage.mk
include $(call all-subdir-makefiles)
diff --git a/CtsBuild.mk b/CtsBuild.mk
index d396006..6e57086 100644
--- a/CtsBuild.mk
+++ b/CtsBuild.mk
@@ -36,6 +36,10 @@
$(foreach lib,$(1),$(HOST_OUT_JAVA_LIBRARIES)/$(lib).jar)
endef
+define cts-get-ui-lib-paths
+ $(foreach lib,$(1),$(CTS_TESTCASES_OUT)/$(lib).jar)
+endef
+
define cts-get-native-paths
$(foreach exe,$(1),$(call intermediates-dir-for,EXECUTABLES,$(exe))/$(exe))
endef
diff --git a/CtsCoverage.mk b/CtsCoverage.mk
index 61ad9c9..8d67a99 100644
--- a/CtsCoverage.mk
+++ b/CtsCoverage.mk
@@ -24,7 +24,7 @@
coverage_out := $(HOST_OUT)/cts-api-coverage
-api_text_description := $(SRC_API_DIR)/current.txt
+api_text_description := frameworks/base/api/current.txt
api_xml_description := $(coverage_out)/api.xml
$(api_xml_description) : $(api_text_description) $(APICHECK)
$(hide) echo "Converting API file to XML: $@"
diff --git a/CtsTestCaseList.mk b/CtsTestCaseList.mk
index 5511ebd..1e794f9 100644
--- a/CtsTestCaseList.mk
+++ b/CtsTestCaseList.mk
@@ -29,6 +29,7 @@
CtsMultiUserStorageApp
cts_support_packages := \
+ $(PTS_SUPPORT_PACKAGES) \
CtsAccelerationTestStubs \
CtsDelegatingAccessibilityService \
CtsDeviceAdmin \
@@ -38,6 +39,7 @@
CtsTestStubs \
SignatureTest \
TestDeviceSetup \
+ CtsUiAutomatorApp \
$(cts_security_apps_list)
cts_external_packages := \
@@ -51,6 +53,7 @@
# Test packages that require an associated test package XML.
cts_test_packages := \
+ $(PTS_TEST_PACKAGES) \
CtsAccelerationTestCases \
CtsAccountManagerTestCases \
CtsAccessibilityServiceTestCases \
@@ -62,8 +65,10 @@
CtsCalendarcommon2TestCases \
CtsContentTestCases \
CtsDatabaseTestCases \
+ CtsDisplayTestCases \
CtsDpiTestCases \
CtsDpiTestCases2 \
+ CtsDreamsTestCases \
CtsDrmTestCases \
CtsEffectTestCases \
CtsExampleTestCases \
@@ -76,6 +81,7 @@
CtsLocationTestCases \
CtsMediaStressTestCases \
CtsMediaTestCases \
+ CtsNativeOpenGLTestCases \
CtsNdefTestCases \
CtsNetTestCases \
CtsOpenGLTestCases \
@@ -107,6 +113,7 @@
# Host side only tests
cts_host_libraries := \
+ $(PTS_HOST_CASES) \
CtsAppSecurityTests \
CtsMonkeyTestCases
@@ -115,17 +122,23 @@
NativeMediaTest_SL \
NativeMediaTest_XA
+cts_ui_tests := \
+ CtsUiAutomatorTests
+
# All the files that will end up under the repository/testcases
# directory of the final CTS distribution.
CTS_TEST_CASES := $(call cts-get-lib-paths,$(cts_host_libraries)) \
$(call cts-get-package-paths,$(cts_test_packages)) \
- $(call cts-get-native-paths,$(cts_native_exes))
+ $(call cts-get-native-paths,$(cts_native_exes)) \
+ $(call cts-get-ui-lib-paths,$(cts_ui_tests))
# All the XMLs that will end up under the repository/testcases
# and that need to be created before making the final CTS distribution.
CTS_TEST_XMLS := $(call cts-get-test-xmls,$(cts_host_libraries)) \
$(call cts-get-test-xmls,$(cts_test_packages)) \
- $(call cts-get-test-xmls,$(cts_native_exes))
+ $(call cts-get-test-xmls,$(cts_native_exes)) \
+ $(call cts-get-test-xmls,$(cts_ui_tests))
+
# The following files will be placed in the tools directory of the CTS distribution
CTS_TOOLS_LIST :=
diff --git a/apps/CtsVerifier/Android.mk b/apps/CtsVerifier/Android.mk
index aa3f117..c14728e 100644
--- a/apps/CtsVerifier/Android.mk
+++ b/apps/CtsVerifier/Android.mk
@@ -36,7 +36,7 @@
# Builds and launches CTS Verifier on a device.
.PHONY: cts-verifier
cts-verifier: CtsVerifier adb
- adb install -r $(ANDROID_PRODUCT_OUT)/data/app/CtsVerifier.apk \
+ adb install -r $(PRODUCT_OUT)/data/app/CtsVerifier.apk \
&& adb shell "am start -n com.android.cts.verifier/.CtsVerifierActivity"
#
diff --git a/apps/CtsVerifier/AndroidManifest.xml b/apps/CtsVerifier/AndroidManifest.xml
index 6cd196c..bce422d 100644
--- a/apps/CtsVerifier/AndroidManifest.xml
+++ b/apps/CtsVerifier/AndroidManifest.xml
@@ -18,7 +18,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.cts.verifier"
android:versionCode="1"
- android:versionName="4.2_r3">
+ android:versionName="1337">
<!-- Using 10 for more complete NFC support... -->
<uses-sdk android:minSdkVersion="10"></uses-sdk>
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/location/GpsTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/location/GpsTestActivity.java
index bb12b2b..da1ee88 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/location/GpsTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/location/GpsTestActivity.java
@@ -95,25 +95,25 @@
case 1:
// Test GPS with minTime = 0
mLocationVerifier = new LocationVerifier(this, mLocationManager,
- LocationManager.GPS_PROVIDER, 0, 8);
+ LocationManager.GPS_PROVIDER, 0, 8, false);
mLocationVerifier.start();
break;
case 2:
// Test GPS with minTime = 1s
mLocationVerifier = new LocationVerifier(this, mLocationManager,
- LocationManager.GPS_PROVIDER, 1 * 1000, 8);
+ LocationManager.GPS_PROVIDER, 1 * 1000, 8, false);
mLocationVerifier.start();
break;
case 3:
// Test GPS with minTime = 5s
mLocationVerifier = new LocationVerifier(this, mLocationManager,
- LocationManager.GPS_PROVIDER, 5 * 1000, 8);
+ LocationManager.GPS_PROVIDER, 5 * 1000, 8, false);
mLocationVerifier.start();
break;
case 4:
// Test GPS with minTime = 15s
mLocationVerifier = new LocationVerifier(this, mLocationManager,
- LocationManager.GPS_PROVIDER, 15 * 1000, 8);
+ LocationManager.GPS_PROVIDER, 15 * 1000, 8, false);
mLocationVerifier.start();
break;
case 5:
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/location/LocationVerifier.java b/apps/CtsVerifier/src/com/android/cts/verifier/location/LocationVerifier.java
index 3315ba3..ee4068f 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/location/LocationVerifier.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/location/LocationVerifier.java
@@ -66,6 +66,7 @@
private long mLastPassiveTimestamp = -1;
private int mNumActiveUpdates = 0;
private int mNumPassiveUpdates = 0;
+ private boolean mIsMockProvider = false;
private boolean mRunning = false;
private boolean mActiveLocationArrive = false;
@@ -89,6 +90,11 @@
mCb.log("(ignored) active " + mProvider + " update (" + delta + "ms)");
return;
}
+ if (location.isFromMockProvider() != mIsMockProvider) {
+ fail("location coming from \"" + mProvider +
+ "\" provider reports isFromMockProvider() to be " +
+ location.isFromMockProvider());
+ }
mActiveDeltas.add(delta);
mCb.log("active " + mProvider + " update (" + delta + "ms)");
@@ -171,6 +177,11 @@
mCb.log("(ignored) passive " + mProvider + " update (" + delta + "ms)");
return;
}
+ if (location.isFromMockProvider() != mIsMockProvider) {
+ fail("location coming from \"" + mProvider +
+ "\" provider reports isFromMockProvider() to be " +
+ location.isFromMockProvider());
+ }
mPassiveDeltas.add(delta);
mCb.log("passive " + mProvider + " update (" + delta + "ms)");
@@ -185,7 +196,7 @@
}
public LocationVerifier(PassFailLog cb, LocationManager locationManager,
- String provider, long requestedInterval, int numUpdates) {
+ String provider, long requestedInterval, int numUpdates, boolean isMockProvider) {
mProvider = provider;
mInterval = requestedInterval;
// timeout at 60 seconds after interval time
@@ -196,6 +207,7 @@
mHandler = new Handler(this);
mActiveListener = new ActiveListener();
mPassiveListener = new PassiveListener();
+ mIsMockProvider = isMockProvider;
}
public void start() {
diff --git a/build/config.mk b/build/config.mk
index c9fa709..b737ef7 100644
--- a/build/config.mk
+++ b/build/config.mk
@@ -14,4 +14,6 @@
BUILD_CTS_EXECUTABLE := cts/build/test_executable.mk
BUILD_CTS_PACKAGE := cts/build/test_package.mk
+BUILD_CTS_GTEST_PACKAGE := cts/build/test_gtest_package.mk
BUILD_CTS_HOST_JAVA_LIBRARY := cts/build/test_host_java_library.mk
+BUILD_CTS_UI_JAVA_LIBRARY := cts/build/test_uiautomator.mk
diff --git a/build/test_gtest_package.mk b/build/test_gtest_package.mk
new file mode 100644
index 0000000..2f4c9bb
--- /dev/null
+++ b/build/test_gtest_package.mk
@@ -0,0 +1,49 @@
+# Copyright 2012 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.
+
+#
+# Builds a package and defines a rule to generate the associated test
+# package XML needed by CTS.
+#
+# Replace "include $(BUILD_PACKAGE)" with "include $(BUILD_CTS_GTEST_PACKAGE)"
+#
+
+# Disable by default so "m cts" will work in emulator builds
+LOCAL_DEX_PREOPT := false
+
+include $(BUILD_PACKAGE)
+
+cts_package_apk := $(CTS_TESTCASES_OUT)/$(LOCAL_PACKAGE_NAME).apk
+cts_package_xml := $(CTS_TESTCASES_OUT)/$(LOCAL_PACKAGE_NAME).xml
+
+$(cts_package_apk): PRIVATE_PACKAGE := $(LOCAL_PACKAGE_NAME)
+$(cts_package_apk): $(call intermediates-dir-for,APPS,$(LOCAL_PACKAGE_NAME))/package.apk | $(ACP)
+ $(hide) mkdir -p $(CTS_TESTCASES_OUT)
+ $(hide) $(ACP) -fp $(call intermediates-dir-for,APPS,$(PRIVATE_PACKAGE))/package.apk $@
+
+$(cts_package_xml): PRIVATE_PATH := $(LOCAL_PATH)
+$(cts_package_xml): PRIVATE_TEST_PACKAGE := android.$(notdir $(LOCAL_PATH))
+$(cts_package_xml): PRIVATE_EXECUTABLE := $(LOCAL_MODULE)
+$(cts_package_xml): PRIVATE_MANIFEST := $(LOCAL_PATH)/AndroidManifest.xml
+$(cts_package_xml): $(addprefix $(LOCAL_PATH)/,$(LOCAL_SRC_FILES)) $(CTS_NATIVE_TEST_SCANNER) $(CTS_XML_GENERATOR)
+ $(hide) echo Generating test description for wrapped native package $(PRIVATE_EXECUTABLE)
+ $(hide) mkdir -p $(CTS_TESTCASES_OUT)
+ $(hide) $(CTS_NATIVE_TEST_SCANNER) -s $(PRIVATE_PATH) \
+ -t $(PRIVATE_TEST_PACKAGE) | \
+ $(CTS_XML_GENERATOR) -t wrappednative \
+ -m $(PRIVATE_MANIFEST) \
+ -n $(PRIVATE_EXECUTABLE) \
+ -p $(PRIVATE_TEST_PACKAGE) \
+ -e $(CTS_EXPECTATIONS) \
+ -o $@
diff --git a/build/test_package.mk b/build/test_package.mk
index 071acee..aa9a4dd 100644
--- a/build/test_package.mk
+++ b/build/test_package.mk
@@ -35,7 +35,12 @@
$(cts_package_xml): PRIVATE_PATH := $(LOCAL_PATH)
$(cts_package_xml): PRIVATE_INSTRUMENTATION := $(LOCAL_INSTRUMENTATION_FOR)
$(cts_package_xml): PRIVATE_PACKAGE := $(LOCAL_PACKAGE_NAME)
-$(cts_package_xml): PRIVATE_TEST_PACKAGE := android.$(notdir $(LOCAL_PATH))
+ifneq ($(filter cts/suite/pts/%, $(LOCAL_PATH)),) # PTS
+PRIVATE_CTS_TEST_PACKAGE_NANE_ := com.android.pts.$(notdir $(LOCAL_PATH))
+else # CTS
+PRIVATE_CTS_TEST_PACKAGE_NANE_ := android.$(notdir $(LOCAL_PATH))
+endif # PTS
+$(cts_package_xml): PRIVATE_TEST_PACKAGE := $(PRIVATE_CTS_TEST_PACKAGE_NANE_)
$(cts_package_xml): PRIVATE_MANIFEST := $(LOCAL_PATH)/AndroidManifest.xml
$(cts_package_xml): PRIVATE_TEST_TYPE := $(if $(LOCAL_CTS_TEST_RUNNER),$(LOCAL_CTS_TEST_RUNNER),'')
$(cts_package_xml): $(call intermediates-dir-for,APPS,$(LOCAL_PACKAGE_NAME))/package.apk $(CTS_EXPECTATIONS) $(CTS_JAVA_TEST_SCANNER_DOCLET) $(CTS_JAVA_TEST_SCANNER) $(CTS_XML_GENERATOR)
diff --git a/build/test_uiautomator.mk b/build/test_uiautomator.mk
new file mode 100644
index 0000000..255f70b
--- /dev/null
+++ b/build/test_uiautomator.mk
@@ -0,0 +1,49 @@
+# Copyright (C) 2012 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.
+
+#
+# Builds a uiautomator library and defines a rule to generate the associated test
+# package XML needed by CTS.
+#
+
+include $(BUILD_JAVA_LIBRARY)
+
+cts_library_xml := $(CTS_TESTCASES_OUT)/$(LOCAL_MODULE).xml
+cts_library_jar := $(CTS_TESTCASES_OUT)/$(LOCAL_MODULE).jar
+
+$(cts_library_jar): PRIVATE_MODULE := $(LOCAL_MODULE)
+$(cts_library_jar): $(call intermediates-dir-for,JAVA_LIBRARIES,$(LOCAL_MODULE))/javalib.jar | $(ACP)
+ $(hide) mkdir -p $(CTS_TESTCASES_OUT)
+ $(hide) $(ACP) -fp $(call intermediates-dir-for,JAVA_LIBRARIES,$(PRIVATE_MODULE))/javalib.jar $@
+
+$(cts_library_xml): PRIVATE_PATH := $(LOCAL_PATH)/src
+$(cts_library_xml): PRIVATE_TEST_APP_PACKAGE := $(LOCAL_CTS_TEST_APP_PACKAGE)
+$(cts_library_xml): PRIVATE_TEST_PACKAGE := $(LOCAL_CTS_TEST_PACKAGE)
+$(cts_library_xml): PRIVATE_TEST_APK := $(LOCAL_CTS_TEST_APK)
+$(cts_library_xml): PRIVATE_LIBRARY := $(LOCAL_MODULE)
+$(cts_library_xml): PRIVATE_JAR_PATH := $(LOCAL_MODULE).jar
+$(cts_library_xml): $(call intermediates-dir-for,JAVA_LIBRARIES,$(LOCAL_MODULE))/javalib.jar $(CTS_EXPECTATIONS) $(CTS_JAVA_TEST_SCANNER_DOCLET) $(CTS_JAVA_TEST_SCANNER) $(CTS_XML_GENERATOR)
+ $(hide) echo Generating test description for uiautomator library $(PRIVATE_LIBRARY)
+ $(hide) mkdir -p $(CTS_TESTCASES_OUT)
+ $(hide) $(CTS_JAVA_TEST_SCANNER) -s $(PRIVATE_PATH) \
+ -d $(CTS_JAVA_TEST_SCANNER_DOCLET) | \
+ $(CTS_XML_GENERATOR) -t uiAutomator \
+ -i $(PRIVATE_TEST_APK) \
+ -j $(PRIVATE_JAR_PATH) \
+ -a $(PRIVATE_TEST_PACKAGE) \
+ -n $(PRIVATE_LIBRARY) \
+ -p $(PRIVATE_TEST_PACKAGE) \
+ -r $(PRIVATE_TEST_APP_PACKAGE) \
+ -e $(CTS_EXPECTATIONS) \
+ -o $@
diff --git a/libs/testserver/src/android/webkit/cts/CtsTestServer.java b/libs/testserver/src/android/webkit/cts/CtsTestServer.java
index 16d57ea..a18d329 100755
--- a/libs/testserver/src/android/webkit/cts/CtsTestServer.java
+++ b/libs/testserver/src/android/webkit/cts/CtsTestServer.java
@@ -423,11 +423,20 @@
}
/**
+ * Hook for adding stuffs for HTTP POST. Default implementation does nothing.
+ * @return null to use the default response mechanism of sending the requested uri as it is.
+ * Otherwise, the whole response should be handled inside onPost.
+ */
+ protected HttpResponse onPost(HttpRequest request) throws Exception {
+ return null;
+ }
+
+ /**
* Generate a response to the given request.
* @throws InterruptedException
* @throws IOException
*/
- private HttpResponse getResponse(HttpRequest request) throws InterruptedException, IOException {
+ private HttpResponse getResponse(HttpRequest request) throws Exception {
RequestLine requestLine = request.getRequestLine();
HttpResponse response = null;
String uriString = requestLine.getUri();
@@ -441,6 +450,13 @@
}
}
+ if (requestLine.getMethod().equals("POST")) {
+ HttpResponse responseOnPost = onPost(request);
+ if (responseOnPost != null) {
+ return responseOnPost;
+ }
+ }
+
URI uri = URI.create(uriString);
String path = uri.getPath();
String query = uri.getQuery();
@@ -544,12 +560,11 @@
Header[] cookies = request.getHeaders("Cookie");
Pattern p = Pattern.compile("count=(\\d+)");
StringBuilder cookieString = new StringBuilder(100);
+ cookieString.append(cookies.length);
int count = 0;
for (Header cookie : cookies) {
+ cookieString.append("|");
String value = cookie.getValue();
- if (cookieString.length() > 0) {
- cookieString.append("|");
- }
cookieString.append(value);
Matcher m = p.matcher(value);
if (m.find()) {
@@ -864,7 +879,7 @@
}
@Override
- public Void call() throws IOException, InterruptedException, HttpException {
+ public Void call() throws Exception {
HttpResponse response = mServer.getResponse(mRequest);
mConnection.sendResponseHeader(response);
mConnection.sendResponseEntity(response);
diff --git a/tests/tests/openglperf/src/android/openglperf/cts/RenderingWatchDog.java b/libs/util/src/android/cts/util/WatchDog.java
similarity index 91%
rename from tests/tests/openglperf/src/android/openglperf/cts/RenderingWatchDog.java
rename to libs/util/src/android/cts/util/WatchDog.java
index 4872af2..3db90e9 100644
--- a/tests/tests/openglperf/src/android/openglperf/cts/RenderingWatchDog.java
+++ b/libs/util/src/android/cts/util/WatchDog.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.openglperf.cts;
+package android.cts.util;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
@@ -26,14 +26,14 @@
* class for checking if rendering function is alive or not.
* panic if watch-dog is not reset over certain amount of time
*/
-public class RenderingWatchDog implements Runnable {
- private static final String TAG = "RenderingWatchDog";
+public class WatchDog implements Runnable {
+ private static final String TAG = "WatchDog";
private Thread mThread;
private Semaphore mSemaphore;
private volatile boolean mStopRequested;
private final long mTimeoutInMilliSecs;
- public RenderingWatchDog(long timeoutInMilliSecs) {
+ public WatchDog(long timeoutInMilliSecs) {
mTimeoutInMilliSecs = timeoutInMilliSecs;
}
diff --git a/suite/pts/tools/tradefed/Android.mk b/libs/wrappedgtest/Android.mk
similarity index 74%
rename from suite/pts/tools/tradefed/Android.mk
rename to libs/wrappedgtest/Android.mk
index 09e49b1..f89ba9d 100644
--- a/suite/pts/tools/tradefed/Android.mk
+++ b/libs/wrappedgtest/Android.mk
@@ -1,4 +1,4 @@
-# Copyright (C) 2011 The Android Open Source Project
+# Copyright 2012 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.
@@ -12,11 +12,14 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-LOCAL_PATH := $(call my-dir)
+LOCAL_PATH:= $(call my-dir)
+
include $(CLEAR_VARS)
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
LOCAL_MODULE_TAGS := optional
-LOCAL_PREBUILT_EXECUTABLES := pts-tradefed
-include $(BUILD_HOST_PREBUILT)
+LOCAL_MODULE := ctswrappedgtest
+include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/libs/wrappedgtest/src/WrappedGTestActivity.java b/libs/wrappedgtest/src/WrappedGTestActivity.java
new file mode 100644
index 0000000..0633a5b
--- /dev/null
+++ b/libs/wrappedgtest/src/WrappedGTestActivity.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2012 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.test.wrappedgtest;
+
+import android.app.Activity;
+import android.app.Instrumentation;
+import android.os.Bundle;
+
+public class WrappedGTestActivity extends Activity {
+
+ private WrappedGTestInstrumentation mInstrumentation;
+
+ public void setInstrumentation(WrappedGTestInstrumentation instrumentation) {
+ mInstrumentation = instrumentation;
+ }
+
+ public int runGTests() {
+ return runTests(this);
+ }
+
+ public void sendStatus(String output) {
+ Bundle outputBundle = new Bundle();
+ outputBundle.putString("gtest", output);
+ mInstrumentation.sendStatus(1, outputBundle);
+ }
+
+ protected static native int runTests(WrappedGTestActivity activity);
+}
diff --git a/libs/wrappedgtest/src/WrappedGTestInstrumentation.java b/libs/wrappedgtest/src/WrappedGTestInstrumentation.java
new file mode 100644
index 0000000..b29aaab
--- /dev/null
+++ b/libs/wrappedgtest/src/WrappedGTestInstrumentation.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2012 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.test.wrappedgtest;
+
+import android.app.Activity;
+import android.app.Instrumentation;
+import android.app.KeyguardManager;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.Intent;
+import android.os.Bundle;
+import android.util.Log;
+
+
+public class WrappedGTestInstrumentation extends Instrumentation {
+
+ private static final String TAG = "WrappedGTestInstrumentation";
+ private WrappedGTestActivity mActivity;
+ protected Class mActivityClass;
+
+ public WrappedGTestInstrumentation() {
+ }
+
+ @Override
+ public void onCreate(Bundle arguments) {
+ // attempt to disable keyguard, if current test has permission to do so
+ if (getContext().checkCallingOrSelfPermission(android.Manifest.permission.DISABLE_KEYGUARD)
+ == PackageManager.PERMISSION_GRANTED) {
+ Log.i(TAG, "Disabling keyguard");
+ KeyguardManager keyguardManager =
+ (KeyguardManager) getContext().getSystemService(Context.KEYGUARD_SERVICE);
+ keyguardManager.newKeyguardLock("cts").disableKeyguard();
+ } else {
+ Log.i(TAG, "Test lacks permission to disable keyguard. " +
+ "UI based tests may fail if keyguard is up");
+ }
+ super.onCreate(arguments);
+ start();
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart();
+
+ Intent intent = new Intent(getTargetContext(), mActivityClass);
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+
+ mActivity = (WrappedGTestActivity)startActivitySync(intent);
+ mActivity.setInstrumentation(this);
+ mActivity.runGTests();
+
+ finish(Activity.RESULT_OK, new Bundle());
+ }
+}
diff --git a/suite/pts/Android.mk b/suite/pts/Android.mk
index 07b30e3..c141484 100644
--- a/suite/pts/Android.mk
+++ b/suite/pts/Android.mk
@@ -14,5 +14,4 @@
# limitations under the License.
#
-include cts/suite/pts/PtsBuild.mk
include $(call all-subdir-makefiles)
diff --git a/suite/pts/PtsBenchmarkingList.mk b/suite/pts/PtsBenchmarkingList.mk
new file mode 100644
index 0000000..7c0ad47
--- /dev/null
+++ b/suite/pts/PtsBenchmarkingList.mk
@@ -0,0 +1,36 @@
+#
+# Copyright (C) 2012 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 definitions for PTS
+
+# New packages should be added here
+PTS_TEST_PACKAGES := \
+ PtsDeviceFilePerf \
+ PtsDeviceUi \
+ PtsDeviceDram \
+ PtsDeviceSimpleCpu \
+ PtsDeviceBrowserBench \
+ PtsDeviceVideoPerf \
+ PtsDeviceOpenGl
+
+PTS_SUPPORT_PACKAGES := \
+ PtsDeviceTaskswitchingAppA \
+ PtsDeviceTaskswitchingAppB \
+ PtsDeviceTaskswitchingControl
+
+PTS_HOST_CASES := \
+ PtsHostBootup \
+ PtsHostUi
diff --git a/suite/pts/PtsBuild.mk b/suite/pts/PtsBuild.mk
deleted file mode 100644
index 18306b9..0000000
--- a/suite/pts/PtsBuild.mk
+++ /dev/null
@@ -1,152 +0,0 @@
-#
-# Copyright (C) 2012 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.
-#
-
-# several makefiles for CTS merged for PTS
-
-LOCAL_PATH:= $(call my-dir)
-
-# New packages should be added here
-PTS_TEST_PACKAGES := \
- PtsDeviceFilePerf \
- PtsDeviceUi \
- PtsDeviceDram
-
-
-PTS_SUPPORT_PACKAGES := \
- TestDeviceSetup \
- PtsDeviceTaskswitchingAppA \
- PtsDeviceTaskswitchingAppB \
- PtsDeviceTaskswitchingControl \
- com.replica.replicaisland \
- PtsDeviceBrowserLauncher
-
-PTS_HOST_CASES := \
- PtsHostBootup \
- PtsHostUi \
- PtsHostBrowser
-
-PTS_HOST_LIBS := \
- $(HOST_OUT_JAVA_LIBRARIES)/ptscommonutilhost.jar \
- $(HOST_OUT_JAVA_LIBRARIES)/ptshostutil.jar
-
-BUILD_PTS_PACKAGE := cts/suite/pts/build/test_package.mk
-BUILD_PTS_HOST_JAVA_LIBRARY := cts/suite/pts/build/test_host_java_library.mk
-
-PTS_JAVA_TEST_SCANNER := $(HOST_OUT_EXECUTABLES)/cts-java-scanner
-PTS_JAVA_TEST_SCANNER_DOCLET := $(HOST_OUT_JAVA_LIBRARIES)/cts-java-scanner-doclet.jar
-
-# Generator of test XMLs from scanner output.
-PTS_XML_GENERATOR := $(HOST_OUT_EXECUTABLES)/cts-xml-generator
-
-# File indicating which tests should be blacklisted due to problems.
-PTS_EXPECTATIONS := cts/suite/pts/expectations/knownfailures.txt
-
-PTS_TESTCASES_OUT := $(HOST_OUT)/pts-testcases
-
-define pts-get-package-paths
- $(foreach pkg,$(1),$(PTS_TESTCASES_OUT)/$(pkg).apk)
-endef
-
-define pts-get-test-xmls
- $(foreach name,$(1),$(PTS_TESTCASES_OUT)/$(name).xml)
-endef
-
-define pts-get-lib-paths
- $(foreach lib,$(1),$(HOST_OUT_JAVA_LIBRARIES)/$(lib).jar)
-endef
-
-PTS_TEST_CASE_LIST := \
- $(PTS_SUPPORT_PACKAGES)
-
-PTS_TEST_CASES := \
- $(call pts-get-package-paths,$(PTS_TEST_PACKAGES)) \
- $(call pts-get-lib-paths,$(PTS_HOST_CASES))
-
-PTS_TEST_XMLS := \
- $(call pts-get-test-xmls,$(PTS_TEST_PACKAGES)) \
- $(call pts-get-test-xmls,$(PTS_HOST_CASES))
-
-pts_dir := $(HOST_OUT)/pts
-pts_tools_src_dir := cts/tools
-
-pts_name := android-pts
-
-DDMLIB_JAR := $(HOST_OUT_JAVA_LIBRARIES)/ddmlib-prebuilt.jar
-junit_host_jar := $(HOST_OUT_JAVA_LIBRARIES)/junit.jar
-HOSTTESTLIB_JAR := $(HOST_OUT_JAVA_LIBRARIES)/hosttestlib.jar
-TF_JAR := $(HOST_OUT_JAVA_LIBRARIES)/tradefed-prebuilt.jar
-PTS_TF_JAR := $(HOST_OUT_JAVA_LIBRARIES)/cts-tradefed.jar
-PTS_TF_EXEC := $(HOST_OUT_EXECUTABLES)/pts-tradefed
-PTS_TF_README := $(pts_tools_src_dir)/tradefed-host/README
-
-
-DEFAULT_TEST_PLAN := $(pts_dir)/$(pts_name)/resource/plans/PTS.xml
-
-$(pts_dir)/all_pts_files_stamp: PRIVATE_JUNIT_HOST_JAR := $(junit_host_jar)
-
-$(pts_dir)/all_pts_files_stamp: $(PTS_TEST_CASES) $(PTS_TEST_CASE_LIST) $(junit_host_jar) $(HOSTTESTLIB_JAR) $(PTS_HOST_LIBRARY_JARS) $(TF_JAR) $(VMTESTSTF_JAR) $(PTS_TF_JAR) $(PTS_TF_EXEC) $(PTS_TF_README) $(ACP) $(PTS_HOST_LIBS)
-# Make necessary directory for PTS
- $(hide) rm -rf $(PRIVATE_PTS_DIR)
- $(hide) mkdir -p $(TMP_DIR)
- $(hide) mkdir -p $(PRIVATE_DIR)/docs
- $(hide) mkdir -p $(PRIVATE_DIR)/tools
- $(hide) mkdir -p $(PRIVATE_DIR)/repository/testcases
- $(hide) mkdir -p $(PRIVATE_DIR)/repository/plans
-# Copy executable and JARs to PTS directory
- $(hide) $(ACP) -fp $(DDMLIB_JAR) $(PRIVATE_JUNIT_HOST_JAR) $(HOSTTESTLIB_JAR) $(PTS_HOST_LIBRARY_JARS) $(TF_JAR) $(PTS_TF_JAR) $(PTS_TF_EXEC) $(PTS_TF_README) $(PTS_HOST_LIBS) $(PRIVATE_DIR)/tools
-# Change mode of the executables
- $(foreach apk,$(PTS_TEST_CASE_LIST),$(call copy-testcase-apk,$(apk)))
- $(foreach testcase,$(PTS_TEST_CASES),$(call copy-testcase,$(testcase)))
- $(hide) touch $@
-
-# Generate the default test plan for User.
-# Usage: buildCts.py <testRoot> <ctsOutputDir> <tempDir> <androidRootDir> <docletPath>
-
-$(DEFAULT_TEST_PLAN): $(pts_dir)/all_pts_files_stamp $(pts_tools_src_dir)/utils/buildCts.py $(HOST_OUT_JAVA_LIBRARIES)/descGen.jar $(PTS_TEST_XMLS) | $(ACP)
- $(hide) $(ACP) -fp $(PTS_TEST_XMLS) $(PRIVATE_DIR)/repository/testcases
- $(hide) $(pts_tools_src_dir)/utils/buildCts.py cts/suite/pts $(PRIVATE_DIR) $(TMP_DIR) \
- $(TOP) $(HOST_OUT_JAVA_LIBRARIES)/descGen.jar -pts
-
-# Package PTS and clean up.
-INTERNAL_PTS_TARGET := $(pts_dir)/$(pts_name).zip
-$(INTERNAL_PTS_TARGET): PRIVATE_NAME := $(pts_name)
-$(INTERNAL_PTS_TARGET): PRIVATE_PTS_DIR := $(pts_dir)
-$(INTERNAL_PTS_TARGET): PRIVATE_DIR := $(pts_dir)/$(pts_name)
-$(INTERNAL_PTS_TARGET): TMP_DIR := $(pts_dir)/temp
-$(INTERNAL_PTS_TARGET): $(pts_dir)/all_pts_files_stamp $(DEFAULT_TEST_PLAN)
- $(hide) echo "Package PTS: $@"
- $(hide) cd $(dir $@) && zip -rq $(notdir $@) $(PRIVATE_NAME)
-
-.PHONY: pts
-pts: $(INTERNAL_PTS_TARGET)
-cts: pts
-# generate PTS during CTS build
-ifneq ($(filter cts, $(MAKECMDGOALS)),)
-$(call dist-for-goals,cts,$(INTERNAL_PTS_TARGET))
-endif
-
-define copy-testcase-apk
-
-$(hide) $(ACP) -fp $(call intermediates-dir-for,APPS,$(1))/package.apk \
- $(PRIVATE_DIR)/repository/testcases/$(1).apk
-
-endef
-
-define copy-testcase
-
-$(hide) $(ACP) -fp $(1) $(PRIVATE_DIR)/repository/testcases/$(notdir $1)
-
-endef
diff --git a/suite/pts/build/test_host_java_library.mk b/suite/pts/build/test_host_java_library.mk
deleted file mode 100644
index 3f410d1..0000000
--- a/suite/pts/build/test_host_java_library.mk
+++ /dev/null
@@ -1,38 +0,0 @@
-# Copyright (C) 2012 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.
-
-#
-# Builds a host library and defines a rule to generate the associated test
-# package XML needed by PTS.
-#
-
-include $(BUILD_HOST_JAVA_LIBRARY)
-
-pts_library_xml := $(PTS_TESTCASES_OUT)/$(LOCAL_MODULE).xml
-
-$(pts_library_xml): PRIVATE_PATH := $(LOCAL_PATH)/src
-$(pts_library_xml): PRIVATE_TEST_PACKAGE := $(LOCAL_PTS_TEST_PACKAGE)
-$(pts_library_xml): PRIVATE_LIBRARY := $(LOCAL_MODULE)
-$(pts_library_xml): PRIVATE_JAR_PATH := $(LOCAL_MODULE).jar
-$(pts_library_xml): $(HOST_OUT_JAVA_LIBRARIES)/$(LOCAL_MODULE).jar $(PTS_EXPECTATIONS) $(PTS_JAVA_TEST_SCANNER_DOCLET) $(PTS_JAVA_TEST_SCANNER) $(PTS_XML_GENERATOR)
- $(hide) echo Generating test description for host library $(PRIVATE_LIBRARY)
- $(hide) mkdir -p $(PTS_TESTCASES_OUT)
- $(hide) $(PTS_JAVA_TEST_SCANNER) -s $(PRIVATE_PATH) \
- -d $(PTS_JAVA_TEST_SCANNER_DOCLET) | \
- $(PTS_XML_GENERATOR) -t hostSideOnly \
- -j $(PRIVATE_JAR_PATH) \
- -n $(PRIVATE_LIBRARY) \
- -p $(PRIVATE_TEST_PACKAGE) \
- -e $(PTS_EXPECTATIONS) \
- -o $@
diff --git a/suite/pts/build/test_package.mk b/suite/pts/build/test_package.mk
deleted file mode 100644
index f677c3b..0000000
--- a/suite/pts/build/test_package.mk
+++ /dev/null
@@ -1,49 +0,0 @@
-# Copyright (C) 2012 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.
-
-# copied from cts/build. modified for PTS
-
-# Disable by default so "m pts" will work in emulator builds
-LOCAL_DEX_PREOPT := false
-
-include $(BUILD_PACKAGE)
-
-pts_package_apk := $(PTS_TESTCASES_OUT)/$(LOCAL_PACKAGE_NAME).apk
-pts_package_xml := $(PTS_TESTCASES_OUT)/$(LOCAL_PACKAGE_NAME).xml
-
-$(pts_package_apk): PRIVATE_PACKAGE := $(LOCAL_PACKAGE_NAME)
-$(pts_package_apk): $(call intermediates-dir-for,APPS,$(LOCAL_PACKAGE_NAME))/package.apk | $(ACP)
- $(hide) mkdir -p $(PTS_TESTCASES_OUT)
- $(hide) $(ACP) -fp $< $@
-
-$(pts_package_xml): PRIVATE_PATH := $(LOCAL_PATH)
-$(pts_package_xml): PRIVATE_INSTRUMENTATION := $(LOCAL_INSTRUMENTATION_FOR)
-$(pts_package_xml): PRIVATE_PACKAGE := $(LOCAL_PACKAGE_NAME)
-$(pts_package_xml): PRIVATE_TEST_PACKAGE := com.android.pts.$(notdir $(LOCAL_PATH))
-$(pts_package_xml): PRIVATE_MANIFEST := $(LOCAL_PATH)/AndroidManifest.xml
-$(pts_package_xml): PRIVATE_TEST_TYPE := $(if $(LOCAL_PTS_TEST_RUNNER),$(LOCAL_PTS_TEST_RUNNER),'')
-$(pts_package_xml): $(call intermediates-dir-for,APPS,$(LOCAL_PACKAGE_NAME))/package.apk $(PTS_EXPECTATIONS) $(PTS_JAVA_TEST_SCANNER_DOCLET) $(PTS_JAVA_TEST_SCANNER) $(PTS_XML_GENERATOR)
- $(hide) echo Generating test description for java package $(PRIVATE_PACKAGE)
- $(hide) mkdir -p $(PTS_TESTCASES_OUT)
- $(hide) $(PTS_JAVA_TEST_SCANNER) \
- -s $(PRIVATE_PATH) \
- -d $(PTS_JAVA_TEST_SCANNER_DOCLET) | \
- $(PTS_XML_GENERATOR) \
- -t $(PRIVATE_TEST_TYPE) \
- -m $(PRIVATE_MANIFEST) \
- -i "$(PRIVATE_INSTRUMENTATION)" \
- -n $(PRIVATE_PACKAGE) \
- -p $(PRIVATE_TEST_PACKAGE) \
- -e $(PTS_EXPECTATIONS) \
- -o $@
diff --git a/suite/pts/hostTests/browser/browserlauncher/Android.mk b/suite/pts/deviceTests/browserbench/Android.mk
similarity index 92%
rename from suite/pts/hostTests/browser/browserlauncher/Android.mk
rename to suite/pts/deviceTests/browserbench/Android.mk
index 0592017..1ce0e49 100644
--- a/suite/pts/hostTests/browser/browserlauncher/Android.mk
+++ b/suite/pts/deviceTests/browserbench/Android.mk
@@ -24,10 +24,10 @@
LOCAL_SRC_FILES := $(call all-java-files-under, src)
-LOCAL_PACKAGE_NAME := PtsDeviceBrowserLauncher
+LOCAL_PACKAGE_NAME := PtsDeviceBrowserBench
LOCAL_SDK_VERSION := 16
-include $(BUILD_PTS_PACKAGE)
+include $(BUILD_CTS_PACKAGE)
include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/suite/pts/hostTests/browser/browserlauncher/AndroidManifest.xml b/suite/pts/deviceTests/browserbench/AndroidManifest.xml
similarity index 100%
rename from suite/pts/hostTests/browser/browserlauncher/AndroidManifest.xml
rename to suite/pts/deviceTests/browserbench/AndroidManifest.xml
diff --git a/suite/pts/hostTests/browser/browserlauncher/assets/octane/base.js b/suite/pts/deviceTests/browserbench/assets/octane/base.js
similarity index 100%
rename from suite/pts/hostTests/browser/browserlauncher/assets/octane/base.js
rename to suite/pts/deviceTests/browserbench/assets/octane/base.js
diff --git a/suite/pts/hostTests/browser/browserlauncher/assets/octane/box2d.js b/suite/pts/deviceTests/browserbench/assets/octane/box2d.js
similarity index 100%
rename from suite/pts/hostTests/browser/browserlauncher/assets/octane/box2d.js
rename to suite/pts/deviceTests/browserbench/assets/octane/box2d.js
diff --git a/suite/pts/hostTests/browser/browserlauncher/assets/octane/code-load.js b/suite/pts/deviceTests/browserbench/assets/octane/code-load.js
similarity index 100%
rename from suite/pts/hostTests/browser/browserlauncher/assets/octane/code-load.js
rename to suite/pts/deviceTests/browserbench/assets/octane/code-load.js
diff --git a/suite/pts/hostTests/browser/browserlauncher/assets/octane/crypto.js b/suite/pts/deviceTests/browserbench/assets/octane/crypto.js
similarity index 100%
rename from suite/pts/hostTests/browser/browserlauncher/assets/octane/crypto.js
rename to suite/pts/deviceTests/browserbench/assets/octane/crypto.js
diff --git a/suite/pts/hostTests/browser/browserlauncher/assets/octane/css/bootstrap-responsive.css b/suite/pts/deviceTests/browserbench/assets/octane/css/bootstrap-responsive.css
similarity index 100%
rename from suite/pts/hostTests/browser/browserlauncher/assets/octane/css/bootstrap-responsive.css
rename to suite/pts/deviceTests/browserbench/assets/octane/css/bootstrap-responsive.css
diff --git a/suite/pts/hostTests/browser/browserlauncher/assets/octane/css/bootstrap.css b/suite/pts/deviceTests/browserbench/assets/octane/css/bootstrap.css
similarity index 100%
rename from suite/pts/hostTests/browser/browserlauncher/assets/octane/css/bootstrap.css
rename to suite/pts/deviceTests/browserbench/assets/octane/css/bootstrap.css
diff --git a/suite/pts/hostTests/browser/browserlauncher/assets/octane/css/docs.css b/suite/pts/deviceTests/browserbench/assets/octane/css/docs.css
similarity index 100%
rename from suite/pts/hostTests/browser/browserlauncher/assets/octane/css/docs.css
rename to suite/pts/deviceTests/browserbench/assets/octane/css/docs.css
diff --git a/suite/pts/hostTests/browser/browserlauncher/assets/octane/deltablue.js b/suite/pts/deviceTests/browserbench/assets/octane/deltablue.js
similarity index 100%
rename from suite/pts/hostTests/browser/browserlauncher/assets/octane/deltablue.js
rename to suite/pts/deviceTests/browserbench/assets/octane/deltablue.js
diff --git a/suite/pts/hostTests/browser/browserlauncher/assets/octane/earley-boyer.js b/suite/pts/deviceTests/browserbench/assets/octane/earley-boyer.js
similarity index 100%
rename from suite/pts/hostTests/browser/browserlauncher/assets/octane/earley-boyer.js
rename to suite/pts/deviceTests/browserbench/assets/octane/earley-boyer.js
diff --git a/suite/pts/hostTests/browser/browserlauncher/assets/octane/gbemu.js b/suite/pts/deviceTests/browserbench/assets/octane/gbemu.js
similarity index 100%
rename from suite/pts/hostTests/browser/browserlauncher/assets/octane/gbemu.js
rename to suite/pts/deviceTests/browserbench/assets/octane/gbemu.js
diff --git a/suite/pts/hostTests/browser/browserlauncher/assets/octane/ico/apple-touch-icon-114-precomposed.png b/suite/pts/deviceTests/browserbench/assets/octane/ico/apple-touch-icon-114-precomposed.png
similarity index 100%
rename from suite/pts/hostTests/browser/browserlauncher/assets/octane/ico/apple-touch-icon-114-precomposed.png
rename to suite/pts/deviceTests/browserbench/assets/octane/ico/apple-touch-icon-114-precomposed.png
Binary files differ
diff --git a/suite/pts/hostTests/browser/browserlauncher/assets/octane/ico/apple-touch-icon-144-precomposed.png b/suite/pts/deviceTests/browserbench/assets/octane/ico/apple-touch-icon-144-precomposed.png
similarity index 100%
rename from suite/pts/hostTests/browser/browserlauncher/assets/octane/ico/apple-touch-icon-144-precomposed.png
rename to suite/pts/deviceTests/browserbench/assets/octane/ico/apple-touch-icon-144-precomposed.png
Binary files differ
diff --git a/suite/pts/hostTests/browser/browserlauncher/assets/octane/ico/apple-touch-icon-57-precomposed.png b/suite/pts/deviceTests/browserbench/assets/octane/ico/apple-touch-icon-57-precomposed.png
similarity index 100%
rename from suite/pts/hostTests/browser/browserlauncher/assets/octane/ico/apple-touch-icon-57-precomposed.png
rename to suite/pts/deviceTests/browserbench/assets/octane/ico/apple-touch-icon-57-precomposed.png
Binary files differ
diff --git a/suite/pts/hostTests/browser/browserlauncher/assets/octane/ico/apple-touch-icon-72-precomposed.png b/suite/pts/deviceTests/browserbench/assets/octane/ico/apple-touch-icon-72-precomposed.png
similarity index 100%
rename from suite/pts/hostTests/browser/browserlauncher/assets/octane/ico/apple-touch-icon-72-precomposed.png
rename to suite/pts/deviceTests/browserbench/assets/octane/ico/apple-touch-icon-72-precomposed.png
Binary files differ
diff --git a/suite/pts/hostTests/browser/browserlauncher/assets/octane/ico/favicon.ico b/suite/pts/deviceTests/browserbench/assets/octane/ico/favicon.ico
similarity index 100%
rename from suite/pts/hostTests/browser/browserlauncher/assets/octane/ico/favicon.ico
rename to suite/pts/deviceTests/browserbench/assets/octane/ico/favicon.ico
Binary files differ
diff --git a/suite/pts/hostTests/browser/browserlauncher/assets/octane/index.html b/suite/pts/deviceTests/browserbench/assets/octane/index.html
similarity index 98%
rename from suite/pts/hostTests/browser/browserlauncher/assets/octane/index.html
rename to suite/pts/deviceTests/browserbench/assets/octane/index.html
index 0e2a79d..704123f 100644
--- a/suite/pts/hostTests/browser/browserlauncher/assets/octane/index.html
+++ b/suite/pts/deviceTests/browserbench/assets/octane/index.html
@@ -15,6 +15,8 @@
<script src="js/bootstrap-transition.js"></script>
<script src="js/bootstrap-collapse.js"></script>
<!-- Octane benchmark code -->
+<!-- PTS -->
+<script type="text/javascript" src="pts_report.js"></script>
<script type="text/javascript" src="base.js"></script>
<script type="text/javascript" src="richards.js"></script>
<script type="text/javascript" src="deltablue.js"></script>
@@ -43,7 +45,8 @@
}
function AddResult(name, result) {
- console.log(name + ': ' + result);
+ //PTS
+ PtsReport(name, result, false);
var box = document.getElementById("Result-" + name);
box.innerHTML = result;
}
@@ -65,8 +68,8 @@
} else {
status.innerHTML = "Octane Score (incomplete): " + score;
}
- // print the result for the host
- console.log(status.innerHTML);
+ //PTS
+ PtsReport("Octane Score" + (success ? " " : "(incomplete)"), score, true);
document.getElementById("progress-bar-container").style.visibility = 'hidden';
document.getElementById("bottom-text").style.visibility = 'visible';
document.getElementById("inside-anchor").removeChild(document.getElementById("bar-appendix"));
diff --git a/suite/pts/hostTests/browser/browserlauncher/assets/octane/js/bootstrap-collapse.js b/suite/pts/deviceTests/browserbench/assets/octane/js/bootstrap-collapse.js
similarity index 100%
rename from suite/pts/hostTests/browser/browserlauncher/assets/octane/js/bootstrap-collapse.js
rename to suite/pts/deviceTests/browserbench/assets/octane/js/bootstrap-collapse.js
diff --git a/suite/pts/hostTests/browser/browserlauncher/assets/octane/js/bootstrap-transition.js b/suite/pts/deviceTests/browserbench/assets/octane/js/bootstrap-transition.js
similarity index 100%
rename from suite/pts/hostTests/browser/browserlauncher/assets/octane/js/bootstrap-transition.js
rename to suite/pts/deviceTests/browserbench/assets/octane/js/bootstrap-transition.js
diff --git a/suite/pts/hostTests/browser/browserlauncher/assets/octane/js/jquery.js b/suite/pts/deviceTests/browserbench/assets/octane/js/jquery.js
similarity index 100%
rename from suite/pts/hostTests/browser/browserlauncher/assets/octane/js/jquery.js
rename to suite/pts/deviceTests/browserbench/assets/octane/js/jquery.js
diff --git a/suite/pts/hostTests/browser/browserlauncher/assets/octane/mandreel.js b/suite/pts/deviceTests/browserbench/assets/octane/mandreel.js
similarity index 100%
rename from suite/pts/hostTests/browser/browserlauncher/assets/octane/mandreel.js
rename to suite/pts/deviceTests/browserbench/assets/octane/mandreel.js
diff --git a/suite/pts/hostTests/browser/browserlauncher/assets/octane/navier-stokes.js b/suite/pts/deviceTests/browserbench/assets/octane/navier-stokes.js
similarity index 100%
rename from suite/pts/hostTests/browser/browserlauncher/assets/octane/navier-stokes.js
rename to suite/pts/deviceTests/browserbench/assets/octane/navier-stokes.js
diff --git a/suite/pts/hostTests/browser/browserlauncher/assets/octane/pdfjs.js b/suite/pts/deviceTests/browserbench/assets/octane/pdfjs.js
similarity index 100%
rename from suite/pts/hostTests/browser/browserlauncher/assets/octane/pdfjs.js
rename to suite/pts/deviceTests/browserbench/assets/octane/pdfjs.js
diff --git a/suite/pts/deviceTests/browserbench/assets/octane/pts_report.html b/suite/pts/deviceTests/browserbench/assets/octane/pts_report.html
new file mode 100644
index 0000000..40e2de6
--- /dev/null
+++ b/suite/pts/deviceTests/browserbench/assets/octane/pts_report.html
@@ -0,0 +1,8 @@
+<!doctype html>
+<html>
+ <head>
+ <title>pts_report</title>
+ </head>
+ <body>
+ </body>
+</html>
diff --git a/suite/pts/deviceTests/browserbench/assets/octane/pts_report.js b/suite/pts/deviceTests/browserbench/assets/octane/pts_report.js
new file mode 100644
index 0000000..fb320ae
--- /dev/null
+++ b/suite/pts/deviceTests/browserbench/assets/octane/pts_report.js
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2013 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.
+ */
+
+/**
+ * Utility to report benchmarking result via HTTP POST
+ * to PTS.
+ * @param msg message to add to the report
+ * @param score resulting score
+ * @param isFinal true if this is the last / final score
+ */
+function PtsReport(msg, score, isFinal)
+{
+ req = new XMLHttpRequest();
+ req.open("POST", "pts_report.html?final=" + (isFinal ? "1" : "0") +
+ "&score=" + score + "&message=" + msg, false);
+ req.send(null)
+}
+
diff --git a/suite/pts/hostTests/browser/browserlauncher/assets/octane/raytrace.js b/suite/pts/deviceTests/browserbench/assets/octane/raytrace.js
similarity index 100%
rename from suite/pts/hostTests/browser/browserlauncher/assets/octane/raytrace.js
rename to suite/pts/deviceTests/browserbench/assets/octane/raytrace.js
diff --git a/suite/pts/hostTests/browser/browserlauncher/assets/octane/regexp.js b/suite/pts/deviceTests/browserbench/assets/octane/regexp.js
similarity index 100%
rename from suite/pts/hostTests/browser/browserlauncher/assets/octane/regexp.js
rename to suite/pts/deviceTests/browserbench/assets/octane/regexp.js
diff --git a/suite/pts/hostTests/browser/browserlauncher/assets/octane/richards.js b/suite/pts/deviceTests/browserbench/assets/octane/richards.js
similarity index 100%
rename from suite/pts/hostTests/browser/browserlauncher/assets/octane/richards.js
rename to suite/pts/deviceTests/browserbench/assets/octane/richards.js
diff --git a/suite/pts/hostTests/browser/browserlauncher/assets/octane/run.js b/suite/pts/deviceTests/browserbench/assets/octane/run.js
similarity index 100%
rename from suite/pts/hostTests/browser/browserlauncher/assets/octane/run.js
rename to suite/pts/deviceTests/browserbench/assets/octane/run.js
diff --git a/suite/pts/hostTests/browser/browserlauncher/assets/octane/splay.js b/suite/pts/deviceTests/browserbench/assets/octane/splay.js
similarity index 100%
rename from suite/pts/hostTests/browser/browserlauncher/assets/octane/splay.js
rename to suite/pts/deviceTests/browserbench/assets/octane/splay.js
diff --git a/suite/pts/deviceTests/browserbench/src/com/android/pts/browser/BrowserBenchTest.java b/suite/pts/deviceTests/browserbench/src/com/android/pts/browser/BrowserBenchTest.java
new file mode 100644
index 0000000..07f7763
--- /dev/null
+++ b/suite/pts/deviceTests/browserbench/src/com/android/pts/browser/BrowserBenchTest.java
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2012 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.pts.browser;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.cts.util.TimeoutReq;
+import android.cts.util.WatchDog;
+import android.net.Uri;
+import android.provider.Browser;
+import android.util.Log;
+import android.webkit.cts.CtsTestServer;
+
+import com.android.pts.util.PtsAndroidTestCase;
+import com.android.pts.util.ResultType;
+import com.android.pts.util.ResultUnit;
+import com.android.pts.util.Stat;
+
+import java.net.URLDecoder;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.TimeUnit;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.http.HttpRequest;
+import org.apache.http.HttpResponse;
+import org.apache.http.RequestLine;
+/**
+ * Browser benchmarking.
+ * It launches an activity with URL and wait for POST from the client.
+ */
+public class BrowserBenchTest extends PtsAndroidTestCase {
+ private static final String TAG = "BrowserBench";
+ private static final boolean DEBUG = false;
+ private static final String OCTANE_START_FILE = "octane/index.html";
+ private static final String ROBOHORNET_START_FILE = "robohornet/robohornet.html";
+ private static final String HOST_COMPLETION_BROADCAST = "com.android.pts.browser.completion";
+ // time-out for watch-dog. POST should happen within this time.
+ private static long BROWSER_POST_TIMEOUT_IN_MS = 10 * 60 * 1000L;
+ // watch-dog will time-out first. So make it long enough.
+ private static long BROWSER_COMPLETION_TIMEOUT_IN_MS = 60 * 60 * 1000L;
+ private static final String HTTP_USER_AGENT = "User-Agent";
+ private CtsTestServer mWebServer;
+ // used for final score
+ private ResultType mTypeNonFinal = ResultType.NEUTRAL;
+ private ResultUnit mUnitNonFinal = ResultUnit.NONE;
+ // used for all other scores
+ private ResultType mTypeFinal = ResultType.NEUTRAL;
+ private ResultUnit mUnitFinal = ResultUnit.SCORE;
+ private WatchDog mWatchDog;
+ private CountDownLatch mLatch;
+ // can be changed by each test before starting
+ private volatile int mNumberRepeat;
+ /** tells how many tests have run up to now */
+ private volatile int mRunIndex;
+ /** stores results for each runs. last entry will be the final score. */
+ private LinkedHashMap<String, double[]> mResultsMap;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ mWebServer = new CtsTestServer(getContext()) {
+ @Override
+ protected HttpResponse onPost(HttpRequest request) throws Exception {
+ // post uri will look like "pts_report.html?final=1&score=10.1&message=hello"
+ RequestLine requestLine = request.getRequestLine();
+ String uriString = URLDecoder.decode(requestLine.getUri(), "UTF-8");
+ if (DEBUG) {
+ Log.i(TAG, "uri:" + uriString);
+ }
+ String resultRe =
+ ".*pts_report.html\\?final=([\\d])&score=([\\d]+\\.?[\\d]*)&message=([\\w][\\w ]*)";
+ Pattern resultPattern = Pattern.compile(resultRe);
+ Matcher matchResult = resultPattern.matcher(uriString);
+ if (matchResult.find()) {
+ int isFinal = Integer.parseInt(matchResult.group(1));
+ double score = Double.parseDouble(matchResult.group(2));
+ String message = matchResult.group(3);
+ Log.i(TAG, message + ":" + score);
+ if (!mResultsMap.containsKey(message)) {
+ mResultsMap.put(message, new double[mNumberRepeat]);
+ }
+ double[] scores = mResultsMap.get(message);
+ scores[mRunIndex] = score;
+ if (isFinal == 1) {
+ String userAgent = request.getFirstHeader(HTTP_USER_AGENT).getValue();
+ getReportLog().printValue(HTTP_USER_AGENT + "=" + userAgent, 0,
+ ResultType.NEUTRAL, ResultUnit.NONE);
+ mLatch.countDown();
+ }
+ mWatchDog.reset();
+ }
+ return null; // default response is OK as it will be ignored by client anyway.
+ }
+ };
+ mResultsMap = new LinkedHashMap<String, double[]>();
+ mWatchDog = new WatchDog(BROWSER_POST_TIMEOUT_IN_MS);
+ mWatchDog.start();
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ mWatchDog.stop();
+ mWebServer.shutdown();
+ mWebServer = null;
+ mResultsMap = null;
+ super.tearDown();
+ }
+
+ @TimeoutReq(minutes = 60)
+ public void testOctane() throws InterruptedException {
+ String url = mWebServer.getAssetUrl(OCTANE_START_FILE) + "?auto=1";
+ final int kRepeat = 5;
+ doTest(url, ResultType.LOWER_BETTER, ResultUnit.MS,
+ ResultType.HIGHER_BETTER, ResultUnit.SCORE, kRepeat);
+ }
+
+ private void doTest(String url, ResultType typeNonFinal, ResultUnit unitNonFinal,
+ ResultType typeFinal, ResultUnit unitFinal, int numberRepeat)
+ throws InterruptedException {
+ mTypeNonFinal = typeNonFinal;
+ mUnitNonFinal = unitNonFinal;
+ mTypeFinal = typeFinal;
+ mUnitFinal = unitFinal;
+ mNumberRepeat = numberRepeat;
+ Uri uri = Uri.parse(url);
+ for (mRunIndex = 0; mRunIndex < numberRepeat; mRunIndex++) {
+ Log.i(TAG, mRunIndex + "-th round");
+ mLatch = new CountDownLatch(1);
+ Intent intent = new Intent(Intent.ACTION_VIEW, uri);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ // force using only one window or tab
+ intent.putExtra(Browser.EXTRA_APPLICATION_ID, getContext().getPackageName());
+ getContext().startActivity(intent);
+ boolean ok = mLatch.await(BROWSER_COMPLETION_TIMEOUT_IN_MS, TimeUnit.MILLISECONDS);
+ assertTrue("timed-out", ok);
+ }
+ // it is somewhat awkward to handle the last one specially with Map
+ int numberEntries = mResultsMap.size();
+ int numberToProcess = 1;
+ for (Map.Entry<String, double[]> entry : mResultsMap.entrySet()) {
+ String message = entry.getKey();
+ double[] scores = entry.getValue();
+ if (numberToProcess == numberEntries) { // final score
+ // store the whole results first
+ getReportLog().printArray(message, scores, mTypeFinal, mUnitFinal);
+ getReportLog().printSummary(message, Stat.getAverage(scores), mTypeFinal,
+ mUnitFinal);
+ } else { // interim results
+ getReportLog().printArray(message, scores, mTypeNonFinal, mUnitNonFinal);
+ }
+ numberToProcess++;
+ }
+ }
+}
diff --git a/suite/pts/deviceTests/dram/Android.mk b/suite/pts/deviceTests/dram/Android.mk
index dbe0ad3..47e5b52 100644
--- a/suite/pts/deviceTests/dram/Android.mk
+++ b/suite/pts/deviceTests/dram/Android.mk
@@ -30,6 +30,6 @@
LOCAL_SDK_VERSION := 16
-include $(BUILD_PTS_PACKAGE)
+include $(BUILD_CTS_PACKAGE)
include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/suite/pts/deviceTests/dram/jni/MemoryNativeJni.cpp b/suite/pts/deviceTests/dram/jni/MemoryNativeJni.cpp
index 0c429b7..a54ab71 100644
--- a/suite/pts/deviceTests/dram/jni/MemoryNativeJni.cpp
+++ b/suite/pts/deviceTests/dram/jni/MemoryNativeJni.cpp
@@ -19,14 +19,14 @@
#include <string.h>
#include <sys/time.h>
-long currentTimeMillis()
+double currentTimeMillis()
{
struct timeval tv;
gettimeofday(&tv, (struct timezone *) NULL);
- return (long)tv.tv_sec * 1000 + tv.tv_usec / 1000;
+ return tv.tv_sec * 1000.0 + tv.tv_usec / 1000.0;
}
-extern "C" JNIEXPORT jlong JNICALL Java_com_android_pts_dram_MemoryNative_runMemcpy(JNIEnv* env,
+extern "C" JNIEXPORT jdouble JNICALL Java_com_android_pts_dram_MemoryNative_runMemcpy(JNIEnv* env,
jclass clazz, jint bufferSize, jint repetition)
{
char* src = new char[bufferSize];
@@ -39,13 +39,33 @@
}
memset(src, 0, bufferSize);
memset(dst, 0, bufferSize);
- long start = currentTimeMillis();
+ double start = currentTimeMillis();
for (int i = 0; i < repetition; i++) {
memcpy(dst, src, bufferSize);
src[bufferSize - 1] = i & 0xff;
}
- long end = currentTimeMillis();
+ double end = currentTimeMillis();
delete[] src;
delete[] dst;
return end - start;
}
+
+extern "C" JNIEXPORT jdouble JNICALL Java_com_android_pts_dram_MemoryNative_runMemset(JNIEnv* env,
+ jclass clazz, jint bufferSize, jint repetition, jint c)
+{
+ char* dst = new char[bufferSize];
+ if (dst == NULL) {
+ delete[] dst;
+ env->ThrowNew(env->FindClass("java/lang/OutOfMemoryError"), "No memory");
+ return -1;
+ }
+ memset(dst, 0, bufferSize);
+ double start = currentTimeMillis();
+ for (int i = 0; i < repetition; i++) {
+ memset(dst, (c + i) & 0xff, bufferSize);
+ }
+ double end = currentTimeMillis();
+ delete[] dst;
+ return end - start;
+}
+
diff --git a/suite/pts/deviceTests/dram/src/com/android/pts/dram/BandwidthTest.java b/suite/pts/deviceTests/dram/src/com/android/pts/dram/BandwidthTest.java
index c731cc1..771a77c 100644
--- a/suite/pts/deviceTests/dram/src/com/android/pts/dram/BandwidthTest.java
+++ b/suite/pts/deviceTests/dram/src/com/android/pts/dram/BandwidthTest.java
@@ -21,6 +21,8 @@
import android.util.Log;
import android.view.WindowManager;
+import com.android.pts.util.ResultType;
+import com.android.pts.util.ResultUnit;
import com.android.pts.util.PtsAndroidTestCase;
import com.android.pts.util.ReportLog;
import com.android.pts.util.Stat;
@@ -37,6 +39,14 @@
private static final int REPEAT_IN_EACH_CALL = 100;
private static final int KB = 1024;
private static final int MB = 1024 * 1024;
+ private static final int MEMSET_CHAR = 0xa5;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ // warm-up
+ MemoryNative.runMemcpy(2 * MB, 100);
+ }
public void testMemcpyK004() {
doRunMemcpy(4 * KB);
@@ -90,15 +100,74 @@
doRunMemcpy(16 * MB);
}
+ public void testMemsetK004() {
+ doRunMemset(4 * KB);
+ }
+
+ public void testMemsetK008() {
+ doRunMemset(8 * KB);
+ }
+
+ public void testMemsetK016() {
+ doRunMemset(16 * KB);
+ }
+
+ public void testMemsetK032() {
+ doRunMemset(32 * KB);
+ }
+
+ public void testMemsetK064() {
+ doRunMemset(64 * KB);
+ }
+
+ public void testMemsetK128() {
+ doRunMemset(128 * KB);
+ }
+
+ public void testMemsetK256() {
+ doRunMemset(256 * KB);
+ }
+
+ public void testMemsetK512() {
+ doRunMemset(512 * KB);
+ }
+
+ public void testMemsetM001() {
+ doRunMemset(1 * MB);
+ }
+
+ public void testMemsetM002() {
+ doRunMemset(2 * MB);
+ }
+
+ public void testMemsetM004() {
+ doRunMemset(4 * MB);
+ }
+
+ public void testMemsetM008() {
+ doRunMemset(8 * MB);
+ }
+
+ public void testMemsetM016() {
+ doRunMemset(16 * MB);
+ }
+
private void doRunMemcpy(int bufferSize) {
double[] result = new double[REPETITION];
- for (int i = 0; i < REPETITION; i++) {
- result[i] = MemoryNative.runMemcpy(bufferSize, REPEAT_IN_EACH_CALL);
+ int repeatInEachCall = REPEAT_IN_EACH_CALL;
+ if (bufferSize < (1 * MB)) {
+ // too small buffer size finishes too early to give accurate result.
+ repeatInEachCall *= (1 * MB / bufferSize);
}
- getReportLog().printArray("ms", result, false);
+ for (int i = 0; i < REPETITION; i++) {
+ result[i] = MemoryNative.runMemcpy(bufferSize, repeatInEachCall);
+ }
+ getReportLog().printArray("memcpy time", result, ResultType.LOWER_BETTER,
+ ResultUnit.MS);
double[] mbps = ReportLog.calcRatePerSecArray(
- (double)bufferSize * REPEAT_IN_EACH_CALL / 1024.0 / 1024.0, result);
- getReportLog().printArray("MB/s", mbps, true);
+ (double)bufferSize * repeatInEachCall / 1024.0 / 1024.0, result);
+ getReportLog().printArray("memcpy throughput", mbps, ResultType.HIGHER_BETTER,
+ ResultUnit.MBPS);
Stat.StatResult stat = Stat.getStat(mbps);
WindowManager wm = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);
Point size = new Point();
@@ -107,8 +176,35 @@
double pixels = size.x * size.y;
// now this represents how many times the whole screen can be copied in a sec.
double screensPerSecAverage = stat.mAverage / pixels * 1024.0 * 1024.0 / 4.0;
- double screensPerSecStddev = stat.mStddev / pixels * 1024.0 * 1024.0 / 4.0;
- getReportLog().printSummary("screen copies per sec", screensPerSecAverage,
- screensPerSecStddev);
+ getReportLog().printSummary("memcpy in fps", screensPerSecAverage,
+ ResultType.HIGHER_BETTER, ResultUnit.FPS);
+ }
+
+ private void doRunMemset(int bufferSize) {
+ double[] result = new double[REPETITION];
+ int repeatInEachCall = REPEAT_IN_EACH_CALL;
+ if (bufferSize < (1 * MB)) {
+ // too small buffer size finishes too early to give accurate result.
+ repeatInEachCall *= (1 * MB / bufferSize);
+ }
+ for (int i = 0; i < REPETITION; i++) {
+ result[i] = MemoryNative.runMemset(bufferSize, repeatInEachCall, MEMSET_CHAR);
+ }
+ getReportLog().printArray("memset time", result, ResultType.LOWER_BETTER,
+ ResultUnit.MS);
+ double[] mbps = ReportLog.calcRatePerSecArray(
+ (double)bufferSize * repeatInEachCall / 1024.0 / 1024.0, result);
+ getReportLog().printArray("memset throughput", mbps, ResultType.HIGHER_BETTER,
+ ResultUnit.MBPS);
+ Stat.StatResult stat = Stat.getStat(mbps);
+ WindowManager wm = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);
+ Point size = new Point();
+ wm.getDefaultDisplay().getSize(size);
+ Log.i(TAG, " x " + size.x + " y " + size.y);
+ double pixels = size.x * size.y;
+ // now this represents how many times the whole screen can be copied in a sec.
+ double screensPerSecAverage = stat.mAverage / pixels * 1024.0 * 1024.0 / 4.0;
+ getReportLog().printSummary("memset in fps", screensPerSecAverage,
+ ResultType.HIGHER_BETTER, ResultUnit.FPS);
}
}
diff --git a/suite/pts/deviceTests/dram/src/com/android/pts/dram/MemoryNative.java b/suite/pts/deviceTests/dram/src/com/android/pts/dram/MemoryNative.java
index 991de1e..71b60a0 100644
--- a/suite/pts/deviceTests/dram/src/com/android/pts/dram/MemoryNative.java
+++ b/suite/pts/deviceTests/dram/src/com/android/pts/dram/MemoryNative.java
@@ -27,5 +27,15 @@
* @param repeatition
* @return time spent in copying in ms.
*/
- public static native long runMemcpy(int bufferSize, int repetition);
+ public static native double runMemcpy(int bufferSize, int repetition);
+
+ /**
+ * run memset for given number of repetition from a source to a destination buffers
+ * with each having the size of bufferSize.
+ * @param bufferSize
+ * @param repetition
+ * @param c char to set. Only LSBs will be used to get char value.
+ * @return time spent in memset in ms.
+ */
+ public static native double runMemset(int bufferSize, int repetition, int c);
}
diff --git a/suite/pts/deviceTests/filesystemperf/Android.mk b/suite/pts/deviceTests/filesystemperf/Android.mk
index b42d48e..ca7996c 100644
--- a/suite/pts/deviceTests/filesystemperf/Android.mk
+++ b/suite/pts/deviceTests/filesystemperf/Android.mk
@@ -28,5 +28,5 @@
LOCAL_SDK_VERSION := 16
-include $(BUILD_PTS_PACKAGE)
+include $(BUILD_CTS_PACKAGE)
diff --git a/suite/pts/deviceTests/filesystemperf/src/com/android/pts/filesystemperf/FileUtil.java b/suite/pts/deviceTests/filesystemperf/src/com/android/pts/filesystemperf/FileUtil.java
index 14fc45a..087cf0d 100644
--- a/suite/pts/deviceTests/filesystemperf/src/com/android/pts/filesystemperf/FileUtil.java
+++ b/suite/pts/deviceTests/filesystemperf/src/com/android/pts/filesystemperf/FileUtil.java
@@ -19,7 +19,6 @@
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
-import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
@@ -28,6 +27,8 @@
import com.android.pts.util.MeasureRun;
import com.android.pts.util.MeasureTime;
+import com.android.pts.util.ResultType;
+import com.android.pts.util.ResultUnit;
import com.android.pts.util.ReportLog;
import com.android.pts.util.Stat;
import com.android.pts.util.SystemUtil;
@@ -298,12 +299,14 @@
randomFile.close();
double[] mbps = ReportLog.calcRatePerSecArray((double)fileSize / runsInOneGo / 1024 / 1024,
times);
- report.printArray("MB/s",
- mbps, true);
- report.printArray("Rd amount", rdAmount, true);
+ report.printArray("read throughput",
+ mbps, ResultType.HIGHER_BETTER, ResultUnit.MBPS);
+ // This is just the amount of IO returned from kernel. So this is performance neutral.
+ report.printArray("read amount", rdAmount, ResultType.NEUTRAL, ResultUnit.BYTE);
Stat.StatResult stat = Stat.getStat(mbps);
- report.printSummary("MB/s", stat.mAverage, stat.mStddev);
+ report.printSummary("read throughput", stat.mAverage, ResultType.HIGHER_BETTER,
+ ResultUnit.MBPS);
}
/**
@@ -348,12 +351,14 @@
randomFile.close();
double[] mbps = ReportLog.calcRatePerSecArray((double)fileSize / runsInOneGo / 1024 / 1024,
times);
- report.printArray("MB/s",
- mbps, true);
- report.printArray("Wr amount", wrAmount, true);
+ report.printArray("write throughput",
+ mbps, ResultType.HIGHER_BETTER, ResultUnit.MBPS);
+ report.printArray("write amount", wrAmount, ResultType.NEUTRAL,
+ ResultUnit.BYTE);
Stat.StatResult stat = Stat.getStat(mbps);
- report.printSummary("MB/s", stat.mAverage, stat.mStddev);
+ report.printSummary("write throughput", stat.mAverage, ResultType.HIGHER_BETTER,
+ ResultUnit.MBPS);
}
/**
@@ -386,11 +391,12 @@
randomFile.close();
double[] mbps = ReportLog.calcRatePerSecArray((double)bufferSize / 1024 / 1024,
times);
- report.printArray(i + "-th round MB/s",
- mbps, true);
+ report.printArray(i + "-th round throughput",
+ mbps, ResultType.HIGHER_BETTER, ResultUnit.MBPS);
ReportLog.copyArray(mbps, mbpsAll, i * numberRepeatInOneRun);
}
Stat.StatResult stat = Stat.getStat(mbpsAll);
- report.printSummary("MB/s", stat.mAverage, stat.mStddev);
+ report.printSummary("update throughput", stat.mAverage, ResultType.HIGHER_BETTER,
+ ResultUnit.MBPS);
}
}
diff --git a/suite/pts/deviceTests/filesystemperf/src/com/android/pts/filesystemperf/SequentialRWTest.java b/suite/pts/deviceTests/filesystemperf/src/com/android/pts/filesystemperf/SequentialRWTest.java
index 252c59e..4a9c3dd 100644
--- a/suite/pts/deviceTests/filesystemperf/src/com/android/pts/filesystemperf/SequentialRWTest.java
+++ b/suite/pts/deviceTests/filesystemperf/src/com/android/pts/filesystemperf/SequentialRWTest.java
@@ -19,6 +19,8 @@
import android.cts.util.TimeoutReq;
import com.android.pts.util.MeasureRun;
import com.android.pts.util.MeasureTime;
+import com.android.pts.util.ResultType;
+import com.android.pts.util.ResultUnit;
import com.android.pts.util.PtsAndroidTestCase;
import com.android.pts.util.ReportLog;
import com.android.pts.util.Stat;
@@ -47,7 +49,8 @@
public void testSingleSequentialWrite() throws Exception {
final int numberOfFiles =(int)(FileUtil.getFileSizeExceedingMemory(
getContext(), BUFFER_SIZE) / BUFFER_SIZE);
- getReportLog().printValue("files", numberOfFiles);
+ getReportLog().printValue("files", numberOfFiles, ResultType.NEUTRAL,
+ ResultUnit.COUNT);
final byte[] data = FileUtil.generateRandomData(BUFFER_SIZE);
final File[] files = FileUtil.createNewFiles(getContext(), DIR_SEQ_WR,
numberOfFiles);
@@ -61,11 +64,13 @@
}
});
double[] mbps = ReportLog.calcRatePerSecArray((double)BUFFER_SIZE / 1024 / 1024, times);
- getReportLog().printArray("try " + numberOfFiles + " files, result MB/s",
- mbps, true);
- getReportLog().printArray("Wr amount", wrAmount, true);
+ getReportLog().printArray("write throughput",
+ mbps, ResultType.HIGHER_BETTER, ResultUnit.MBPS);
+ getReportLog().printArray("write amount", wrAmount, ResultType.NEUTRAL,
+ ResultUnit.BYTE);
Stat.StatResult stat = Stat.getStat(mbps);
- getReportLog().printSummary("MB/s", stat.mAverage, stat.mStddev);
+ getReportLog().printSummary("write throughput", stat.mAverage, ResultType.HIGHER_BETTER,
+ ResultUnit.MBPS);
}
@TimeoutReq(minutes = 60)
@@ -83,8 +88,9 @@
final File file = FileUtil.createNewFilledFile(getContext(),
DIR_SEQ_RD, fileSize);
long finish = System.currentTimeMillis();
- getReportLog().printValue("write size " + fileSize + " result MB/s",
- ReportLog.calcRatePerSec((double)fileSize / 1024 / 1024, finish - start));
+ getReportLog().printValue("write throughput for test file of length " + fileSize,
+ ReportLog.calcRatePerSec((double)fileSize / 1024 / 1024, finish - start),
+ ResultType.HIGHER_BETTER, ResultUnit.MBPS);
final int NUMBER_READ = 10;
@@ -103,9 +109,10 @@
}
});
double[] mbps = ReportLog.calcRatePerSecArray((double)fileSize / 1024 / 1024, times);
- getReportLog().printArray("read MB/s",
- mbps, true);
+ getReportLog().printArray("read throughput",
+ mbps, ResultType.HIGHER_BETTER, ResultUnit.MBPS);
Stat.StatResult stat = Stat.getStat(mbps);
- getReportLog().printSummary("MB/s", stat.mAverage, stat.mStddev);
+ getReportLog().printSummary("read throughput", stat.mAverage, ResultType.HIGHER_BETTER,
+ ResultUnit.MBPS);
}
}
diff --git a/suite/pts/hostTests/browser/browserlauncher/Android.mk b/suite/pts/deviceTests/opengl/Android.mk
similarity index 83%
copy from suite/pts/hostTests/browser/browserlauncher/Android.mk
copy to suite/pts/deviceTests/opengl/Android.mk
index 0592017..377562c 100644
--- a/suite/pts/hostTests/browser/browserlauncher/Android.mk
+++ b/suite/pts/deviceTests/opengl/Android.mk
@@ -20,14 +20,16 @@
LOCAL_JAVA_LIBRARIES := android.test.runner
-LOCAL_STATIC_JAVA_LIBRARIES := ptsutil ctsutil ctstestrunner ctstestserver
+LOCAL_STATIC_JAVA_LIBRARIES := ptsutil ctsutil ctstestrunner
+
+LOCAL_JNI_SHARED_LIBRARIES := libptsopengl_jni
LOCAL_SRC_FILES := $(call all-java-files-under, src)
-LOCAL_PACKAGE_NAME := PtsDeviceBrowserLauncher
+LOCAL_PACKAGE_NAME := PtsDeviceOpenGl
LOCAL_SDK_VERSION := 16
-include $(BUILD_PTS_PACKAGE)
+include $(BUILD_CTS_PACKAGE)
include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/suite/pts/deviceTests/opengl/AndroidManifest.xml b/suite/pts/deviceTests/opengl/AndroidManifest.xml
new file mode 100644
index 0000000..a91766f
--- /dev/null
+++ b/suite/pts/deviceTests/opengl/AndroidManifest.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.pts.opengl"
+ android:versionCode="1"
+ android:versionName="1.0" >
+
+ <uses-sdk
+ android:minSdkVersion="16" />
+
+ <uses-feature
+ android:glEsVersion="0x00020000"
+ android:required="true" />
+
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />\
+ <uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
+
+ <application android:allowBackup="false" >
+ <uses-library android:name="android.test.runner" />
+
+ <activity
+ android:name="com.android.pts.opengl.primitive.GLActivity"
+ android:theme="@android:style/Theme.NoTitleBar.Fullscreen" >
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ </application>
+
+ <instrumentation
+ android:name="android.test.InstrumentationCtsTestRunner"
+ android:label="OpenGL ES 2.0 Benchmark"
+ android:targetPackage="com.android.pts.opengl" />
+
+</manifest>
\ No newline at end of file
diff --git a/suite/pts/hostTests/ptshostutil/Android.mk b/suite/pts/deviceTests/opengl/jni/Android.mk
similarity index 67%
copy from suite/pts/hostTests/ptshostutil/Android.mk
copy to suite/pts/deviceTests/opengl/jni/Android.mk
index 14e786a..b872b02 100644
--- a/suite/pts/hostTests/ptshostutil/Android.mk
+++ b/suite/pts/deviceTests/opengl/jni/Android.mk
@@ -11,19 +11,21 @@
# 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_SRC_FILES := \
- $(call all-java-files-under, src)
-
-LOCAL_JAVA_LIBRARIES := cts-tradefed tradefed-prebuilt ddmlib-prebuilt junit
+LOCAL_MODULE := libptsopengl_jni
LOCAL_MODULE_TAGS := optional
-LOCAL_MODULE := ptshostutil
+# Get all cpp files but not hidden files
+LOCAL_SRC_FILES := $(patsubst ./%,%, $(shell cd $(LOCAL_PATH); \
+ find . -name "*.cpp" -and -not -name ".*"))
-include $(BUILD_HOST_JAVA_LIBRARY)
+LOCAL_C_INCLUDES := $(JNI_H_INCLUDE)
+LOCAL_SHARED_LIBRARIES := libEGL libGLESv2 libandroid libutils
+
+include $(BUILD_SHARED_LIBRARY)
\ No newline at end of file
diff --git a/suite/pts/deviceTests/opengl/jni/GLNative.cpp b/suite/pts/deviceTests/opengl/jni/GLNative.cpp
new file mode 100644
index 0000000..48b87a5
--- /dev/null
+++ b/suite/pts/deviceTests/opengl/jni/GLNative.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2013 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.
+ */
+#include <jni.h>
+
+extern "C" JNIEXPORT jboolean JNICALL
+Java_com_android_pts_opengl_primitive_GLActivity_startBenchmark(
+ JNIEnv* env, jclass clazz, jint numFrames, jdoubleArray frameTimes) {
+ return true;
+}
+
+extern "C" JNIEXPORT void JNICALL
+Java_com_android_pts_opengl_primitive_GLActivity_setupFullPipelineBenchmark(
+ JNIEnv* env, jclass clazz, jobject surface, jint workload) {
+}
+
+extern "C" JNIEXPORT void JNICALL
+Java_com_android_pts_opengl_primitive_GLActivity_setupPixelOutputBenchmark(
+ JNIEnv* env, jclass clazz, jobject surface, jint workload) {
+}
+
+extern "C" JNIEXPORT void JNICALL
+Java_com_android_pts_opengl_primitive_GLActivity_setupShaderPerfBenchmark(
+ JNIEnv* env, jclass clazz, jobject surface, jint workload) {
+}
+
+extern "C" JNIEXPORT void JNICALL
+Java_com_android_pts_opengl_primitive_GLActivity_setupContextSwitchBenchmark(
+ JNIEnv* env, jclass clazz, jobject surface, jint workload) {
+}
diff --git a/suite/pts/deviceTests/opengl/src/com/android/pts/opengl/primitive/Benchmark.java b/suite/pts/deviceTests/opengl/src/com/android/pts/opengl/primitive/Benchmark.java
new file mode 100644
index 0000000..d61f45d
--- /dev/null
+++ b/suite/pts/deviceTests/opengl/src/com/android/pts/opengl/primitive/Benchmark.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2013 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.pts.opengl.primitive;
+
+/**
+ * Represents the different primitive benchmarks.
+ */
+public enum Benchmark {
+ FullPipeline,
+ PixelOutput,
+ ShaderPerf,
+ ContextSwitch;
+}
diff --git a/suite/pts/deviceTests/opengl/src/com/android/pts/opengl/primitive/GLActivity.java b/suite/pts/deviceTests/opengl/src/com/android/pts/opengl/primitive/GLActivity.java
new file mode 100644
index 0000000..0caa576
--- /dev/null
+++ b/suite/pts/deviceTests/opengl/src/com/android/pts/opengl/primitive/GLActivity.java
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2013 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.pts.opengl.primitive;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.cts.util.WatchDog;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+import android.view.Surface;
+
+import com.android.pts.util.ResultType;
+import com.android.pts.util.ResultUnit;
+
+public class GLActivity extends Activity {
+
+ public final static String TAG = "GLActivity";
+ /**
+ * Holds the name of the benchmark to run.
+ */
+ public final static String INTENT_EXTRA_BENCHMARK_NAME = "benchmark_name";
+ /**
+ * Holds the number of milliseconds to wait before timing out.
+ */
+ public final static String INTENT_EXTRA_TIMEOUT = "timeout";
+ /**
+ * The minimum number of frames per second the device must achieve to pass.
+ */
+ public final static String INTENT_EXTRA_MIN_FPS = "min_fps";
+ /**
+ * The number of frames to render for each workload.
+ */
+ public final static String INTENT_EXTRA_NUM_FRAMES = "num_frames";
+
+ private Worker runner;
+ private volatile Exception mException;
+ private volatile Surface mSurface;
+
+ private Benchmark mBenchmark;
+ private int mTimeout;
+ private double mMinFps;
+ private int mNumFrames;
+ private volatile int mWorkload = 0;
+
+ @Override
+ public void onCreate(Bundle data) {
+ super.onCreate(data);
+ System.loadLibrary("ptsopengl_jni");
+ Intent intent = getIntent();
+ mBenchmark = Benchmark.valueOf(intent.getStringExtra(INTENT_EXTRA_BENCHMARK_NAME));
+ mTimeout = intent.getIntExtra(INTENT_EXTRA_TIMEOUT, 0);
+ mMinFps = intent.getDoubleExtra(INTENT_EXTRA_MIN_FPS, 0);
+ mNumFrames = intent.getIntExtra(INTENT_EXTRA_NUM_FRAMES, 0);
+
+ Log.i(TAG, "Benchmark: " + mBenchmark);
+ Log.i(TAG, "Time Out: " + mTimeout);
+ Log.i(TAG, "Min FPS: " + mMinFps);
+ Log.i(TAG, "Num Frames: " + mNumFrames);
+
+ SurfaceView surfaceView = new SurfaceView(this);
+ surfaceView.getHolder().addCallback(new SurfaceHolder.Callback() {
+ @Override
+ public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
+ mSurface = holder.getSurface();
+ }
+
+ @Override
+ public void surfaceCreated(SurfaceHolder holder) {}
+
+ @Override
+ public void surfaceDestroyed(SurfaceHolder holder) {}
+ });
+ setContentView(surfaceView);
+ }
+
+ public int waitForCompletion() throws Exception {
+ // Creates, starts and waits for a worker to run the benchmark.
+ runner = new Worker();
+ runner.start();
+ runner.join();
+ if (mException != null) {
+ throw mException;
+ }
+ return mWorkload;
+ }
+
+ private static native void setupFullPipelineBenchmark(Surface surface, int workload);
+
+ private static native void setupPixelOutputBenchmark(Surface surface, int workload);
+
+ private static native void setupShaderPerfBenchmark(Surface surface, int workload);
+
+ private static native void setupContextSwitchBenchmark(Surface surface, int workload);
+
+ private static native boolean startBenchmark(int numFrames, double[] frameTimes);
+
+ /**
+ * This thread runs the benchmarks, freeing the UI thread.
+ */
+ private class Worker extends Thread {
+
+ private volatile boolean repeat = true;
+ private WatchDog watchDog;
+
+ @Override
+ public void run() {
+ // Creates a watchdog to ensure a iteration doesn't exceed the timeout.
+ watchDog = new WatchDog(mTimeout);
+ watchDog.start();
+ // Used to record the start and end time of the iteration.
+ double[] times = new double[2];
+ while (repeat) {
+ // The workload to use for this iteration.
+ int wl = mWorkload + 1;
+ // Setup the benchmark.
+ switch (mBenchmark) {
+ case FullPipeline:
+ setupFullPipelineBenchmark(mSurface, wl);
+ break;
+ case PixelOutput:
+ setupPixelOutputBenchmark(mSurface, wl);
+ break;
+ case ShaderPerf:
+ setupShaderPerfBenchmark(mSurface, wl);
+ break;
+ case ContextSwitch:
+ setupContextSwitchBenchmark(mSurface, wl);
+ break;
+ }
+ // Start benchmark.
+ if (!startBenchmark(mNumFrames, times)) {
+ mException = new Exception("Could not run benchmark");
+ repeat = false;
+ } else {
+ // Calculate FPS.
+ double totalTimeTaken = times[1] - times[0];
+ double meanFps = mNumFrames * 1000.0f / totalTimeTaken;
+ Log.i(TAG, "Workload: " + wl);
+ Log.i(TAG, "MeanFPS: " + meanFps);
+ if (meanFps >= mMinFps) {
+ // Iteration passed, proceed to next one.
+ mWorkload++;
+ } else {
+ // Iteration failed.
+ repeat = false;
+ }
+ }
+ // Resets the watchdog for the next iteration.
+ watchDog.reset();
+ }
+ watchDog.stop();
+ }
+
+ }
+}
diff --git a/suite/pts/deviceTests/opengl/src/com/android/pts/opengl/primitive/GLBenchmark.java b/suite/pts/deviceTests/opengl/src/com/android/pts/opengl/primitive/GLBenchmark.java
new file mode 100644
index 0000000..ad3159f
--- /dev/null
+++ b/suite/pts/deviceTests/opengl/src/com/android/pts/opengl/primitive/GLBenchmark.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2013 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.pts.opengl.primitive;
+
+import android.content.Intent;
+
+import com.android.pts.util.PtsActivityInstrumentationTestCase2;
+import com.android.pts.util.ResultType;
+import com.android.pts.util.ResultUnit;
+
+/**
+ * Runs the Primitive OpenGL ES 2.0 Benchmarks.
+ */
+public class GLBenchmark extends PtsActivityInstrumentationTestCase2<GLActivity> {
+
+ private static final double MIN_FPS = 50;
+
+ public GLBenchmark() {
+ super(GLActivity.class);
+ }
+
+ /**
+ * Runs the full OpenGL ES 2.0 pipeline test.
+ *
+ * @throws Exception If the benchmark could not be run.
+ */
+ public void testFullPipeline() throws Exception {
+ runBenchmark(Benchmark.FullPipeline, 500, 10000);
+ }
+
+ /**
+ * Runs the pixel output test.
+ *
+ * @throws Exception If the benchmark could not be run.
+ */
+ public void testPixelOutput() throws Exception {
+ runBenchmark(Benchmark.PixelOutput, 500, 10000);
+ }
+
+ /**
+ * Runs the shader performance test.
+ *
+ * @throws Exception If the benchmark could not be run.
+ */
+ public void testShaderPerf() throws Exception {
+ // TODO(stuartscott): Not yet implemented
+ // runBenchmark(Benchmark.ShaderPerf, 500, 10000);
+ }
+
+ /**
+ * Runs the OpenGL context switch overhead test.
+ *
+ * @throws Exception If the benchmark could not be run.
+ */
+ public void testContextSwitch() throws Exception {
+ runBenchmark(Benchmark.ContextSwitch, 500, 10000);
+ }
+
+ /**
+ * Runs the specified test.
+ *
+ * @param benchmark An enum representing the benchmark to run.
+ * @param numFrames The number of frames to render.
+ * @param timeout The milliseconds to wait for an iteration of the benchmark before timing out.
+ * @throws Exception If the benchmark could not be run.
+ */
+ private void runBenchmark(Benchmark benchmark, int numFrames, int timeout) throws Exception {
+ String benchmarkName = benchmark.toString();
+ Intent intent = new Intent();
+ intent.putExtra(GLActivity.INTENT_EXTRA_BENCHMARK_NAME, benchmarkName);
+ intent.putExtra(GLActivity.INTENT_EXTRA_TIMEOUT, timeout);
+ intent.putExtra(GLActivity.INTENT_EXTRA_MIN_FPS, MIN_FPS);
+ intent.putExtra(GLActivity.INTENT_EXTRA_NUM_FRAMES, numFrames);
+
+ GLActivity activity = null;
+ setActivityIntent(intent);
+ try {
+ activity = getActivity();
+ int workload = activity.waitForCompletion();
+ // represents the maximum workload it can do whilst maintaining MIN_FPS.
+ getReportLog()
+ .printSummary("Workload", workload, ResultType.HIGHER_BETTER, ResultUnit.SCORE);
+ } finally {
+ if (activity != null) {
+ activity.finish();
+ }
+ }
+ }
+}
diff --git a/suite/pts/hostTests/browser/browserlauncher/Android.mk b/suite/pts/deviceTests/simplecpu/Android.mk
similarity index 83%
copy from suite/pts/hostTests/browser/browserlauncher/Android.mk
copy to suite/pts/deviceTests/simplecpu/Android.mk
index 0592017..69db471 100644
--- a/suite/pts/hostTests/browser/browserlauncher/Android.mk
+++ b/suite/pts/deviceTests/simplecpu/Android.mk
@@ -20,14 +20,16 @@
LOCAL_JAVA_LIBRARIES := android.test.runner
-LOCAL_STATIC_JAVA_LIBRARIES := ptsutil ctsutil ctstestrunner ctstestserver
+LOCAL_STATIC_JAVA_LIBRARIES := ptsutil ctsutil ctstestrunner
+
+LOCAL_JNI_SHARED_LIBRARIES := libptscpu_jni
LOCAL_SRC_FILES := $(call all-java-files-under, src)
-LOCAL_PACKAGE_NAME := PtsDeviceBrowserLauncher
+LOCAL_PACKAGE_NAME := PtsDeviceSimpleCpu
LOCAL_SDK_VERSION := 16
-include $(BUILD_PTS_PACKAGE)
+include $(BUILD_CTS_PACKAGE)
include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/suite/pts/deviceTests/simplecpu/AndroidManifest.xml b/suite/pts/deviceTests/simplecpu/AndroidManifest.xml
new file mode 100644
index 0000000..930bcbd
--- /dev/null
+++ b/suite/pts/deviceTests/simplecpu/AndroidManifest.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2012 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.pts.simplecpu">
+
+ <uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+
+ <application>
+ <uses-library android:name="android.test.runner" />
+ </application>
+ <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+ android:targetPackage="com.android.pts.simplecpu"
+ android:label="Vesy simple CPU benchmarking" />
+</manifest>
diff --git a/suite/pts/hostTests/ptshostutil/Android.mk b/suite/pts/deviceTests/simplecpu/jni/Android.mk
similarity index 76%
rename from suite/pts/hostTests/ptshostutil/Android.mk
rename to suite/pts/deviceTests/simplecpu/jni/Android.mk
index 14e786a..9c34e0e 100644
--- a/suite/pts/hostTests/ptshostutil/Android.mk
+++ b/suite/pts/deviceTests/simplecpu/jni/Android.mk
@@ -11,19 +11,21 @@
# 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_SRC_FILES := \
- $(call all-java-files-under, src)
-
-LOCAL_JAVA_LIBRARIES := cts-tradefed tradefed-prebuilt ddmlib-prebuilt junit
+LOCAL_MODULE := libptscpu_jni
LOCAL_MODULE_TAGS := optional
-LOCAL_MODULE := ptshostutil
+LOCAL_SRC_FILES := CpuNativeJni.cpp
-include $(BUILD_HOST_JAVA_LIBRARY)
+LOCAL_C_INCLUDES := $(JNI_H_INCLUDE)
+LOCAL_SHARED_LIBRARIES := libnativehelper
+
+LOCAL_SDK_VERSION := 14
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/suite/pts/deviceTests/simplecpu/jni/CpuNativeJni.cpp b/suite/pts/deviceTests/simplecpu/jni/CpuNativeJni.cpp
new file mode 100644
index 0000000..851f35e
--- /dev/null
+++ b/suite/pts/deviceTests/simplecpu/jni/CpuNativeJni.cpp
@@ -0,0 +1,270 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+#include <jni.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/time.h>
+
+/* Code from now to qsort_local all copied from bionic source.
+ * The code is duplicated here to remove dependency on optimized bionic
+ */
+static __inline char *med3(char *, char *, char *, int (*)(const void *, const void *));
+static __inline void swapfunc(char *, char *, int, int);
+
+#define min(a, b) (a) < (b) ? a : b
+
+/*
+ * Qsort routine from Bentley & McIlroy's "Engineering a Sort Function".
+ */
+#define swapcode(TYPE, parmi, parmj, n) { \
+ long i = (n) / sizeof (TYPE); \
+ TYPE *pi = (TYPE *) (parmi); \
+ TYPE *pj = (TYPE *) (parmj); \
+ do { \
+ TYPE t = *pi; \
+ *pi++ = *pj; \
+ *pj++ = t; \
+ } while (--i > 0); \
+}
+
+#define SWAPINIT(a, es) swaptype = ((char *)a - (char *)0) % sizeof(long) || \
+ es % sizeof(long) ? 2 : es == sizeof(long)? 0 : 1;
+
+static __inline void
+swapfunc(char *a, char *b, int n, int swaptype)
+{
+ if (swaptype <= 1)
+ swapcode(long, a, b, n)
+ else
+ swapcode(char, a, b, n)
+}
+
+#define swap(a, b) \
+ if (swaptype == 0) { \
+ long t = *(long *)(a); \
+ *(long *)(a) = *(long *)(b); \
+ *(long *)(b) = t; \
+ } else \
+ swapfunc(a, b, es, swaptype)
+
+#define vecswap(a, b, n) if ((n) > 0) swapfunc(a, b, n, swaptype)
+
+static __inline char *
+med3(char *a, char *b, char *c, int (*cmp)(const void *, const void *))
+{
+ return cmp(a, b) < 0 ?
+ (cmp(b, c) < 0 ? b : (cmp(a, c) < 0 ? c : a ))
+ :(cmp(b, c) > 0 ? b : (cmp(a, c) < 0 ? a : c ));
+}
+
+void
+qsort_local(void *aa, size_t n, size_t es, int (*cmp)(const void *, const void *))
+{
+ char *pa, *pb, *pc, *pd, *pl, *pm, *pn;
+ int d, r, swaptype, swap_cnt;
+ char *a = (char*)aa;
+
+loop: SWAPINIT(a, es);
+ swap_cnt = 0;
+ if (n < 7) {
+ for (pm = (char *)a + es; pm < (char *) a + n * es; pm += es)
+ for (pl = pm; pl > (char *) a && cmp(pl - es, pl) > 0;
+ pl -= es)
+ swap(pl, pl - es);
+ return;
+ }
+ pm = (char *)a + (n / 2) * es;
+ if (n > 7) {
+ pl = (char *)a;
+ pn = (char *)a + (n - 1) * es;
+ if (n > 40) {
+ d = (n / 8) * es;
+ pl = med3(pl, pl + d, pl + 2 * d, cmp);
+ pm = med3(pm - d, pm, pm + d, cmp);
+ pn = med3(pn - 2 * d, pn - d, pn, cmp);
+ }
+ pm = med3(pl, pm, pn, cmp);
+ }
+ swap(a, pm);
+ pa = pb = (char *)a + es;
+
+ pc = pd = (char *)a + (n - 1) * es;
+ for (;;) {
+ while (pb <= pc && (r = cmp(pb, a)) <= 0) {
+ if (r == 0) {
+ swap_cnt = 1;
+ swap(pa, pb);
+ pa += es;
+ }
+ pb += es;
+ }
+ while (pb <= pc && (r = cmp(pc, a)) >= 0) {
+ if (r == 0) {
+ swap_cnt = 1;
+ swap(pc, pd);
+ pd -= es;
+ }
+ pc -= es;
+ }
+ if (pb > pc)
+ break;
+ swap(pb, pc);
+ swap_cnt = 1;
+ pb += es;
+ pc -= es;
+ }
+ if (swap_cnt == 0) { /* Switch to insertion sort */
+ for (pm = (char *) a + es; pm < (char *) a + n * es; pm += es)
+ for (pl = pm; pl > (char *) a && cmp(pl - es, pl) > 0;
+ pl -= es)
+ swap(pl, pl - es);
+ return;
+ }
+
+ pn = (char *)a + n * es;
+ r = min(pa - (char *)a, pb - pa);
+ vecswap(a, pb - r, r);
+ r = min(pd - pc, pn - pd - (int)es);
+ vecswap(pb, pn - r, r);
+ if ((r = pb - pa) > (int)es)
+ qsort_local(a, r / es, es, cmp);
+ if ((r = pd - pc) > (int)es) {
+ /* Iterate rather than recurse to save stack space */
+ a = pn - r;
+ n = r / es;
+ goto loop;
+ }
+ /* qsort(pn - r, r / es, es, cmp); */
+}
+
+/* code duplication ends here */
+
+/**
+ * Util for getting time stamp
+ */
+double currentTimeMillis()
+{
+ struct timeval tv;
+ gettimeofday(&tv, (struct timezone *) NULL);
+ return tv.tv_sec * 1000.0 + tv.tv_usec / 1000.0;
+}
+
+/**
+ * Initialize given array randomly for the given seed
+ */
+template <typename T> void randomInitArray(T* array, int len, unsigned int seed)
+{
+ srand(seed);
+ for (int i = 0; i < len; i++) {
+ array[i] = (T) rand();
+ }
+}
+
+/**
+ * comparison function for int, for qsort
+ */
+int cmpint(const void* p1, const void* p2)
+{
+ return *(int*)p1 - *(int*)p2;
+}
+
+extern "C" JNIEXPORT jdouble JNICALL Java_com_android_pts_simplecpu_CpuNative_runSort(JNIEnv* env,
+ jclass clazz, jint numberElements, jint repetition)
+{
+ int* data = new int[numberElements];
+ if (data == NULL) {
+ env->ThrowNew(env->FindClass("java/lang/OutOfMemoryError"), "No memory");
+ return -1;
+ }
+ double totalTime = 0;
+ for (int i = 0; i < repetition; i++) {
+ randomInitArray<int>(data, numberElements, 0);
+ double start = currentTimeMillis();
+ qsort_local(data, numberElements, sizeof(int), cmpint);
+ double end = currentTimeMillis();
+ totalTime += (end - start);
+ }
+ delete[] data;
+ return totalTime;
+}
+
+
+/**
+ * Do matrix multiplication, C = A x B with all matrices having dimension of n x n
+ * The implementation is not in the most efficient, but it is good enough for benchmarking purpose.
+ * @param n should be multiple of 8
+ */
+void doMatrixMultiplication(float* A, float* B, float* C, int n)
+{
+ // batch size
+ const int M = 8;
+ for (int i = 0; i < n; i++) {
+ for (int j = 0; j < n; j += M) {
+ float sum[M];
+ for (int k = 0; k < M; k++) {
+ sum[k] = 0;
+ }
+ // re-use the whole cache line for accessing B.
+ // otherwise, the whole line will be read and only one value will be used.
+
+ for (int k = 0; k < n; k++) {
+ float a = A[i * n + k];
+ sum[0] += a * B[k * n + j];
+ sum[1] += a * B[k * n + j + 1];
+ sum[2] += a * B[k * n + j + 2];
+ sum[3] += a * B[k * n + j + 3];
+ sum[4] += a * B[k * n + j + 4];
+ sum[5] += a * B[k * n + j + 5];
+ sum[6] += a * B[k * n + j + 6];
+ sum[7] += a * B[k * n + j + 7];
+ }
+ for (int k = 0; k < M; k++) {
+ C[i * n + j + k] = sum[k];
+ }
+ }
+ }
+}
+
+extern "C" JNIEXPORT jdouble JNICALL Java_com_android_pts_simplecpu_CpuNative_runMatrixMultiplication(
+ JNIEnv* env, jclass clazz, jint n, jint repetition)
+{
+ // C = A x B
+ float* A = new float[n * n];
+ float* B = new float[n * n];
+ float* C = new float[n * n];
+ if ((A == NULL) || (B == NULL) || (C == NULL)) {
+ delete[] A;
+ delete[] B;
+ delete[] C;
+ env->ThrowNew(env->FindClass("java/lang/OutOfMemoryError"), "No memory");
+ return -1;
+ }
+ double totalTime = 0;
+ for (int i = 0; i < repetition; i++) {
+ randomInitArray<float>(A, n * n, 0);
+ randomInitArray<float>(B, n * n, 1);
+ double start = currentTimeMillis();
+ doMatrixMultiplication(A, B, C, n);
+ double end = currentTimeMillis();
+ totalTime += (end - start);
+ }
+ delete[] A;
+ delete[] B;
+ delete[] C;
+ return totalTime;
+}
+
diff --git a/suite/pts/deviceTests/simplecpu/src/com/android/pts/simplecpu/CpuNative.java b/suite/pts/deviceTests/simplecpu/src/com/android/pts/simplecpu/CpuNative.java
new file mode 100644
index 0000000..43c4b50
--- /dev/null
+++ b/suite/pts/deviceTests/simplecpu/src/com/android/pts/simplecpu/CpuNative.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2012 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.pts.simplecpu;
+
+public class CpuNative {
+ static {
+ System.loadLibrary("ptscpu_jni");
+ }
+ /**
+ * run qsort for given number of repetition
+ * with each having the size of bufferSize.
+ * @param numberElements
+ * @param repeatition
+ * @return time taken for computation, added for all repetition in ms
+ */
+ public static native double runSort(int numberElements, int repetition);
+
+ /**
+ * run matrix multiplication of (n x n) x (n x n)
+ *
+ * @param n dimension, should be multiple of 8
+ * @param repetition
+ * @return time taken for computation, added for all repetition in ms
+ */
+ public static native double runMatrixMultiplication(int n, int repetition);
+}
diff --git a/suite/pts/deviceTests/simplecpu/src/com/android/pts/simplecpu/SimpleCpuTest.java b/suite/pts/deviceTests/simplecpu/src/com/android/pts/simplecpu/SimpleCpuTest.java
new file mode 100644
index 0000000..8b17171
--- /dev/null
+++ b/suite/pts/deviceTests/simplecpu/src/com/android/pts/simplecpu/SimpleCpuTest.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2012 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.pts.simplecpu;
+
+import android.cts.util.TimeoutReq;
+
+import com.android.pts.util.ResultType;
+import com.android.pts.util.ResultUnit;
+import com.android.pts.util.PtsAndroidTestCase;
+import com.android.pts.util.ReportLog;
+import com.android.pts.util.Stat;
+
+/**
+ * Very simple CPU benchmarking to check the basic capability of CPU.
+ * Cases include
+ * qsort
+ * matrix multiplication (for floating point performance)
+ */
+public class SimpleCpuTest extends PtsAndroidTestCase {
+ private static final String TAG = "BandwidthTest";
+ private static final int KB = 1024;
+ private static final int MB = 1024 * 1024;
+ private static final int NUMBER_REPEAT = 20;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ warmUpCpu();
+ }
+
+ public void testSort004KB() {
+ doTestSort(NUMBER_REPEAT, 4 * KB);
+ }
+
+ public void testSort128KB() {
+ doTestSort(NUMBER_REPEAT, 128 * KB);
+ }
+
+ public void testSort001MB() {
+ doTestSort(NUMBER_REPEAT, 1 * MB);
+ }
+
+ // will fit into L1
+ public void testMatrixMultiplication032() {
+ doMatrixMultiplication(NUMBER_REPEAT, 32);
+ }
+
+ // mostly fit into L2
+ public void testMatrixMultiplication128() {
+ doMatrixMultiplication(NUMBER_REPEAT, 128);
+ }
+
+ // may fit into L2
+ public void testMatrixMultiplication200() {
+ doMatrixMultiplication(NUMBER_REPEAT, 200);
+ }
+
+ public void testMatrixMultiplication400() {
+ doMatrixMultiplication(NUMBER_REPEAT, 400);
+ }
+
+ // will exceed L2
+ @TimeoutReq(minutes = 20)
+ public void testMatrixMultiplication600() {
+ doMatrixMultiplication(NUMBER_REPEAT, 600);
+ }
+
+ /**
+ * run some code to force full CPU freq.
+ */
+ private void warmUpCpu() {
+ CpuNative.runSort(1 * MB, 10);
+ }
+
+ /**
+ * qsort test
+ * @param numberRepeat
+ * @param arrayLength
+ */
+ private void doTestSort(int numberRepeat, int arrayLength) {
+ final int numberRepeatInEachCall = 10;
+ double[] result = new double[numberRepeat];
+ for (int i = 0; i < numberRepeat; i++) {
+ result[i] = CpuNative.runSort(arrayLength, numberRepeatInEachCall);
+ }
+ getReportLog().printArray("sorting time", result, ResultType.LOWER_BETTER,
+ ResultUnit.MS);
+ Stat.StatResult stat = Stat.getStat(result);
+ getReportLog().printSummary("sorting time", stat.mAverage, ResultType.LOWER_BETTER,
+ ResultUnit.MS);
+ }
+
+ /**
+ * Matrix multiplication test, nxn matrix multiplication
+ * @param numberRepeat
+ * @param n should be multiple of 8
+ */
+ private void doMatrixMultiplication(int numberRepeat, int n) {
+ assertTrue(n % 8 == 0);
+ final int numberRepeatInEachCall = 10;
+ double[] result = new double[numberRepeat];
+ for (int i = 0; i < numberRepeat; i++) {
+ result[i] = CpuNative.runMatrixMultiplication(n, numberRepeatInEachCall);
+ }
+ getReportLog().printArray("matrix mutiplication time", result, ResultType.LOWER_BETTER,
+ ResultUnit.MS);
+ Stat.StatResult stat = Stat.getStat(result);
+ getReportLog().printSummary("matrix mutiplication time", stat.mAverage,
+ ResultType.LOWER_BETTER, ResultUnit.MS);
+ }
+
+}
diff --git a/suite/pts/deviceTests/ui/Android.mk b/suite/pts/deviceTests/ui/Android.mk
index 28db8a1..3790342 100644
--- a/suite/pts/deviceTests/ui/Android.mk
+++ b/suite/pts/deviceTests/ui/Android.mk
@@ -24,12 +24,18 @@
LOCAL_STATIC_JAVA_LIBRARIES := ptsutil ctsutil ctstestrunner
+LOCAL_JNI_SHARED_LIBRARIES := libctsopenglperf_jni
+
LOCAL_SRC_FILES := $(call all-java-files-under, src)
+# re-use existing openglperf code for CTS. Do not include any test from that dir.
+LOCAL_SRC_FILES += $(filter-out %Test.java,$(call all-java-files-under, ../../../../tests/tests/openglperf/src))
+# do not use its own assets for this package.
+LOCAL_ASSET_DIR := $(LOCAL_PATH)/../../../../tests/tests/openglperf/assets
LOCAL_PACKAGE_NAME := PtsDeviceUi
LOCAL_SDK_VERSION := 16
-include $(BUILD_PTS_PACKAGE)
+include $(BUILD_CTS_PACKAGE)
diff --git a/suite/pts/deviceTests/ui/AndroidManifest.xml b/suite/pts/deviceTests/ui/AndroidManifest.xml
index b5e4937b..6925b5d 100644
--- a/suite/pts/deviceTests/ui/AndroidManifest.xml
+++ b/suite/pts/deviceTests/ui/AndroidManifest.xml
@@ -31,6 +31,7 @@
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
+ <activity android:name="android.openglperf.cts.GlPlanetsActivity" />
</application>
<instrumentation android:name="android.test.InstrumentationCtsTestRunner"
android:targetPackage="com.android.pts.ui"
diff --git a/suite/pts/deviceTests/ui/src/com/android/pts/ui/FpsTest.java b/suite/pts/deviceTests/ui/src/com/android/pts/ui/FpsTest.java
new file mode 100644
index 0000000..a2fd8d9
--- /dev/null
+++ b/suite/pts/deviceTests/ui/src/com/android/pts/ui/FpsTest.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2012 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.pts.ui;
+
+import android.content.Context;
+import android.content.Intent;
+import android.openglperf.cts.GlPlanetsActivity;
+import android.util.Log;
+import android.view.Display;
+import android.view.WindowManager;
+
+import com.android.pts.util.ResultType;
+import com.android.pts.util.ResultUnit;
+import com.android.pts.util.PtsActivityInstrumentationTestCase2;
+import com.android.pts.util.ReportLog;
+import com.android.pts.util.Stat;
+
+/**
+ * measure time taken to render n frames with OpenGL.
+ * This will measure the jankiness of the Gl rendering.
+ * If some frames are delayed, total time will take longer than n x refresh_rate
+ */
+public class FpsTest extends PtsActivityInstrumentationTestCase2<GlPlanetsActivity> {
+ private static final String TAG = "FpsTest";
+ private static final int NUM_FRAMES_TO_RENDER = 60 * 60;
+ private static final long RENDERING_TIMEOUT = NUM_FRAMES_TO_RENDER / 10;
+ // error of this much in ms in refresh interval is not considered as jankiness
+ // note that this margin is set to be little big to consider that there can be additional delay
+ // measurement on some devices showed many jankiness around 2 to 3 ms,
+ // which should not be counted
+ private static final double REFRESH_INTERVAL_THRESHHOLD_IN_MS = 4.0;
+ private GlPlanetsActivity mActivity;
+
+ public FpsTest() {
+ super(GlPlanetsActivity.class);
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ mActivity = null;
+ super.tearDown();
+ }
+
+ public void testFrameIntervals() throws Exception {
+ Intent intent = new Intent();
+ intent.putExtra(GlPlanetsActivity.INTENT_EXTRA_NUM_FRAMES,
+ NUM_FRAMES_TO_RENDER);
+ intent.putExtra(GlPlanetsActivity.INTENT_EXTRA_NUM_PLANETS, 0);
+ intent.putExtra(GlPlanetsActivity.INTENT_EXTRA_USE_VBO_VERTICES, true);
+ intent.putExtra(GlPlanetsActivity.INTENT_EXTRA_USE_VBO_INDICES, true);
+ intent.putExtra(GlPlanetsActivity.INTENT_EXTRA_NUM_INDEX_BUFFERS, 10);
+
+ setActivityIntent(intent);
+ mActivity = getActivity();
+ boolean waitResult = mActivity.waitForGlPlanetsCompletionWithTimeout(RENDERING_TIMEOUT);
+ assertTrue("timeout while waiting for rendering completion", waitResult);
+
+ int[] frameInterval = mActivity.getFrameInterval();
+ assertTrue(frameInterval.length == NUM_FRAMES_TO_RENDER);
+ double fpsMeasured = mActivity.getAverageFps();
+ WindowManager wm = (WindowManager)mActivity.getSystemService(Context.WINDOW_SERVICE);
+ Display dpy = wm.getDefaultDisplay();
+ double fpsNominal = dpy.getRefreshRate();
+ double frameIntervalNominalInMs = 1000.0 / fpsNominal;
+ int jankNumber = 0;
+ // only count positive ( = real delay)
+ double maxDelay = 0;
+ // first one not valid, and should be thrown away
+ double[] intervals = new double[NUM_FRAMES_TO_RENDER - 1];
+ double[] jankiness = new double[NUM_FRAMES_TO_RENDER - 1];
+ double deltaAccumulated = 0;
+ for (int i = 0; i < NUM_FRAMES_TO_RENDER - 1; i++) {
+ intervals[i] = frameInterval[i + 1];
+ double delay = (double)intervals[i] - frameIntervalNominalInMs;
+ if (Math.abs(delay) > REFRESH_INTERVAL_THRESHHOLD_IN_MS) {
+ // Falling here does not necessarily mean jank as it may be catching up
+ // the previous delay. Basically count the first delay as jank, but subsequent
+ // variation should be checked with accumulated delay
+ double delayAdjusted = 0;
+ if (deltaAccumulated == 0) { // This is the first delay. always consider as total
+ jankNumber++;
+ delayAdjusted = delay;
+ } else { // delay already happened
+ double deltaFromLastRefresh = deltaAccumulated - Math.floor(deltaAccumulated /
+ frameIntervalNominalInMs) * frameIntervalNominalInMs;
+ if (deltaAccumulated < 0) {
+ // adjust as the above operation makes delay positive
+ deltaFromLastRefresh -= frameIntervalNominalInMs;
+ }
+ delayAdjusted = delay + deltaFromLastRefresh;
+ if (Math.abs(delayAdjusted) > REFRESH_INTERVAL_THRESHHOLD_IN_MS) {
+ jankNumber++;
+ } else { // caught up
+ delayAdjusted = 0;
+ deltaAccumulated = 0;
+ }
+ }
+ deltaAccumulated += delay;
+ if (delayAdjusted > maxDelay) {
+ maxDelay = delayAdjusted;
+ }
+ jankiness[i] = delayAdjusted;
+ } else {
+ jankiness[i] = 0;
+ deltaAccumulated = 0;
+ }
+ }
+ Log.i(TAG, " fps nominal " + fpsNominal + " fps measured " + fpsMeasured);
+ getReportLog().printArray("intervals", intervals, ResultType.NEUTRAL,
+ ResultUnit.MS);
+ getReportLog().printArray("jankiness", jankiness, ResultType.LOWER_BETTER,
+ ResultUnit.MS);
+ getReportLog().printValue("number of jank", jankNumber, ResultType.LOWER_BETTER,
+ ResultUnit.COUNT);
+ getReportLog().printSummary("max jank delay", maxDelay, ResultType.LOWER_BETTER,
+ ResultUnit.MS);
+ }
+}
diff --git a/suite/pts/deviceTests/ui/src/com/android/pts/ui/ScrollingActivity.java b/suite/pts/deviceTests/ui/src/com/android/pts/ui/ScrollingActivity.java
index 7bdfff9..fbec65d 100644
--- a/suite/pts/deviceTests/ui/src/com/android/pts/ui/ScrollingActivity.java
+++ b/suite/pts/deviceTests/ui/src/com/android/pts/ui/ScrollingActivity.java
@@ -26,8 +26,10 @@
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
-public class ScrollingActivity extends ListActivity implements OnScrollListener
-{
+/**
+ * Activity for measuring scrolling time of long list.
+ */
+public class ScrollingActivity extends ListActivity implements OnScrollListener {
static final String TAG = "ScrollingActivity";
private static final int NUMBER_ELEMENTS = 10000;
private static final int SCROLL_TIME_IN_MS = 1;
@@ -36,8 +38,7 @@
private CountDownLatch mLatchStop = null;
private int mTargetLoc;
- public void onCreate(Bundle icicle)
- {
+ public void onCreate(Bundle icicle) {
super.onCreate(icicle);
for (int i = 0; i < NUMBER_ELEMENTS; i++) {
mItems[i] = Integer.toString(i);
@@ -46,7 +47,6 @@
android.R.layout.simple_list_item_1, mItems));
ListView view = getListView();
view.setOnScrollListener(this);
- //view.setVelocityScale(100.0f);
}
public boolean scrollToTop() {
@@ -75,15 +75,6 @@
mLatchStop = null;
return result;
}
- public void onStop()
- {
- super.onStop();
- }
-
- public void onResume()
- {
- super.onResume();
- }
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
diff --git a/suite/pts/deviceTests/ui/src/com/android/pts/ui/ScrollingTest.java b/suite/pts/deviceTests/ui/src/com/android/pts/ui/ScrollingTest.java
index 8e4bbed..9fd43de 100644
--- a/suite/pts/deviceTests/ui/src/com/android/pts/ui/ScrollingTest.java
+++ b/suite/pts/deviceTests/ui/src/com/android/pts/ui/ScrollingTest.java
@@ -18,6 +18,8 @@
import android.cts.util.TimeoutReq;
import com.android.pts.util.MeasureRun;
import com.android.pts.util.MeasureTime;
+import com.android.pts.util.ResultType;
+import com.android.pts.util.ResultUnit;
import com.android.pts.util.PtsActivityInstrumentationTestCase2;
import com.android.pts.util.ReportLog;
import com.android.pts.util.Stat;
@@ -65,8 +67,10 @@
assertTrue(activity.scrollToTop());
}
});
- getReportLog().printArray("ms", results, false);
+ getReportLog().printArray("scrolling time", results, ResultType.LOWER_BETTER,
+ ResultUnit.MS);
Stat.StatResult stat = Stat.getStat(results);
- getReportLog().printSummary("Time ms", stat.mAverage, stat.mStddev);
+ getReportLog().printSummary("scrolling time", stat.mAverage, ResultType.LOWER_BETTER,
+ ResultUnit.MS);
}
}
diff --git a/suite/pts/hostTests/browser/browserlauncher/Android.mk b/suite/pts/deviceTests/videoperf/Android.mk
similarity index 75%
copy from suite/pts/hostTests/browser/browserlauncher/Android.mk
copy to suite/pts/deviceTests/videoperf/Android.mk
index 0592017..055c9f7 100644
--- a/suite/pts/hostTests/browser/browserlauncher/Android.mk
+++ b/suite/pts/deviceTests/videoperf/Android.mk
@@ -1,4 +1,4 @@
-# Copyright (C) 2012 The Android Open Source Project
+# Copyright (C) 2013 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.
@@ -20,14 +20,13 @@
LOCAL_JAVA_LIBRARIES := android.test.runner
-LOCAL_STATIC_JAVA_LIBRARIES := ptsutil ctsutil ctstestrunner ctstestserver
+LOCAL_STATIC_JAVA_LIBRARIES := ptsutil ctsutil ctstestrunner
LOCAL_SRC_FILES := $(call all-java-files-under, src)
-LOCAL_PACKAGE_NAME := PtsDeviceBrowserLauncher
+LOCAL_PACKAGE_NAME := PtsDeviceVideoPerf
LOCAL_SDK_VERSION := 16
-include $(BUILD_PTS_PACKAGE)
+include $(BUILD_CTS_PACKAGE)
-include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/suite/pts/deviceTests/videoperf/AndroidManifest.xml b/suite/pts/deviceTests/videoperf/AndroidManifest.xml
new file mode 100644
index 0000000..fdef1ef
--- /dev/null
+++ b/suite/pts/deviceTests/videoperf/AndroidManifest.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.pts.videoperf">
+
+ <uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+
+ <application>
+ <uses-library android:name="android.test.runner" />
+ </application>
+ <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+ android:targetPackage="com.android.pts.videoperf"
+ android:label="UI Latency measurement" />
+</manifest>
diff --git a/suite/pts/deviceTests/videoperf/src/com/android/pts/videoperf/VideoEncoderDecoderTest.java b/suite/pts/deviceTests/videoperf/src/com/android/pts/videoperf/VideoEncoderDecoderTest.java
new file mode 100644
index 0000000..b5ed9c2
--- /dev/null
+++ b/suite/pts/deviceTests/videoperf/src/com/android/pts/videoperf/VideoEncoderDecoderTest.java
@@ -0,0 +1,779 @@
+/*
+ * Copyright (C) 2013 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.pts.videoperf;
+
+import android.graphics.Point;
+import android.media.MediaCodec;
+import android.media.MediaCodecInfo;
+import android.media.MediaCodecInfo.CodecCapabilities;
+import android.media.MediaCodecInfo.CodecProfileLevel;
+import android.media.MediaCodecList;
+import android.media.MediaFormat;
+import android.util.Log;
+
+import com.android.pts.util.PtsAndroidTestCase;
+import com.android.pts.util.ResultType;
+import com.android.pts.util.ResultUnit;
+import com.android.pts.util.Stat;
+
+import java.nio.ByteBuffer;
+import java.lang.System;
+import java.util.Random;
+import java.util.Vector;
+
+/**
+ * This tries to test video encoder / decoder performance by running encoding / decoding
+ * without displaying the raw data. To make things simpler, encoder is used to encode synthetic
+ * data and decoder is used to decode the encoded video. This approach does not work where
+ * there is only decoder. Performance index is total time taken for encoding and decoding
+ * the whole frames.
+ * To prevent sacrificing quality for faster encoding / decoding, randomly selected pixels are
+ * compared with the original image. As the pixel comparison can slow down the decoding process,
+ * only some randomly selected pixels are compared. As there can be only one performance index,
+ * error above certain threshhold in pixel value will be treated as an error.
+ */
+public class VideoEncoderDecoderTest extends PtsAndroidTestCase {
+ private static final String TAG = "VideoEncoderDecoderTest";
+ // this wait time affects fps as too big value will work as a blocker if device fps
+ // is not very high.
+ private static final long VIDEO_CODEC_WAIT_TIME_US = 5000;
+ private static final boolean VERBOSE = false;
+ private static final String VIDEO_AVC = "video/avc";
+ private static final int TOTAL_FRAMES = 300;
+ private static final int NUMBER_OF_REPEAT = 10;
+ // i frame interval for encoder
+ private static final int KEY_I_FRAME_INTERVAL = 5;
+
+ private static final int Y_CLAMP_MIN = 16;
+ private static final int Y_CLAMP_MAX = 235;
+ private static final int YUV_PLANE_ADDITIONAL_LENGTH = 200;
+ private ByteBuffer mYBuffer;
+ private ByteBuffer mUVBuffer;
+ // if input raw data is semi-planar
+ private boolean mSrcSemiPlanar;
+ // if output raw data is semi-planar
+ private boolean mDstSemiPlanar;
+ private int mBufferWidth;
+ private int mBufferHeight;
+ private int mVideoWidth;
+ private int mVideoHeight;
+
+ private Vector<ByteBuffer> mEncodedOutputBuffer;
+ // check this many pixels per each decoded frame
+ // checking too many points decreases decoder frame rates a lot.
+ private static final int PIXEL_CHECK_PER_FRAME = 1000;
+ // RMS error in pixel values above this will be treated as error.
+ private static final double PIXEL_RMS_ERROR_MARGAIN = 20.0;
+ private Random mRandom;
+
+ @Override
+ protected void setUp() throws Exception {
+ mEncodedOutputBuffer = new Vector<ByteBuffer>(TOTAL_FRAMES * 2);
+ // Use time as a seed, hoping to prevent checking pixels in the same pattern
+ long now = System.currentTimeMillis();
+ mRandom = new Random(now);
+ super.setUp();
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ mEncodedOutputBuffer.clear();
+ mEncodedOutputBuffer = null;
+ mYBuffer = null;
+ mUVBuffer = null;
+ mRandom = null;
+ super.tearDown();
+ }
+
+ public void testAvc0176x0144() throws Exception {
+ doTest(VIDEO_AVC, 176, 144, NUMBER_OF_REPEAT);
+ }
+
+ public void testAvc0352x0288() throws Exception {
+ doTest(VIDEO_AVC, 352, 288, NUMBER_OF_REPEAT);
+ }
+
+ public void testAvc0720x0480() throws Exception {
+ doTest(VIDEO_AVC, 720, 480, NUMBER_OF_REPEAT);
+ }
+
+ public void testAvc1280x0720() throws Exception {
+ doTest(VIDEO_AVC, 1280, 720, NUMBER_OF_REPEAT);
+ }
+
+ /**
+ * resolution intentionally set to 1072 not 1080
+ * as 1080 is not multiple of 16, and it requires additional setting like stride
+ * which is not specified in API documentation.
+ */
+ public void testAvc1920x1072() throws Exception {
+ doTest(VIDEO_AVC, 1920, 1072, NUMBER_OF_REPEAT);
+ }
+
+ /**
+ * Used to pass codec information for given codec type / width / height
+ */
+ private class CodecInfo {
+ public int mBitRate = 0;
+ public int mFps = 0;
+ public boolean mSupportSemiPlanar = false;
+ public boolean mSupportPlanar = false;
+ }
+
+ /**
+ * Check if given codec with given (w,h) is supported.
+ * @param mimeType codec type in mime format like "video/avc"
+ * @param w video width
+ * @param h video height
+ * @param isEncoder whether the codec is encoder or decoder
+ * @return null if the configuration is not supported.
+ */
+ private CodecInfo getSupportedFormatInfo(String mimeType, int w, int h, boolean isEncoder) {
+ CodecCapabilities cap = getCodecCapability(mimeType, isEncoder);
+ if (cap == null) { // not supported
+ return null;
+ }
+ CodecInfo info = new CodecInfo();
+ for (int color : cap.colorFormats) {
+ if (color == CodecCapabilities.COLOR_FormatYUV420SemiPlanar) {
+ info.mSupportSemiPlanar = true;
+ }
+ if (color == CodecCapabilities.COLOR_FormatYUV420Planar) {
+ info.mSupportPlanar = true;
+ }
+ }
+ printIntArray("supported colors", cap.colorFormats);
+ // either YUV420 planar or semiplanar should be supported
+ if (!info.mSupportPlanar && !info.mSupportSemiPlanar) {
+ Log.i(TAG, "no supported color format");
+ return null;
+ }
+
+ if (mimeType.equals(VIDEO_AVC)) {
+ int highestLevel = 0;
+ for (CodecProfileLevel lvl : cap.profileLevels) {
+ if (lvl.level > highestLevel) {
+ highestLevel = lvl.level;
+ }
+ }
+ Log.i(TAG, "Avc highest level " + Integer.toHexString(highestLevel));
+ int maxW = 0;
+ int maxH = 0;
+ int bitRate = 0;
+ double fps = 0; // frame rate for the max resolution
+ switch(highestLevel) {
+ // Do not support Level 1 to 2.
+ case CodecProfileLevel.AVCLevel1:
+ case CodecProfileLevel.AVCLevel11:
+ case CodecProfileLevel.AVCLevel12:
+ case CodecProfileLevel.AVCLevel13:
+ case CodecProfileLevel.AVCLevel1b:
+ case CodecProfileLevel.AVCLevel2:
+ fail();
+ break;
+ case CodecProfileLevel.AVCLevel21:
+ maxW = 352;
+ maxH = 576;
+ bitRate = 4000000;
+ fps = 25;
+ break;
+ case CodecProfileLevel.AVCLevel22:
+ maxW = 720;
+ maxH = 480;
+ bitRate = 4000000;
+ fps = 15;
+ break;
+ case CodecProfileLevel.AVCLevel3:
+ maxW = 720;
+ maxH = 480;
+ bitRate = 10000000;
+ fps = 30;
+ break;
+ case CodecProfileLevel.AVCLevel31:
+ maxW = 1280;
+ maxH = 720;
+ bitRate = 14000000;
+ fps = 30;
+ break;
+ case CodecProfileLevel.AVCLevel32:
+ maxW = 1280;
+ maxH = 720;
+ bitRate = 20000000;
+ fps = 60;
+ break;
+ case CodecProfileLevel.AVCLevel4:
+ maxW = 1920;
+ maxH = 1080;
+ bitRate = 20000000;
+ fps = 30.1;
+ break;
+ case CodecProfileLevel.AVCLevel41:
+ maxW = 1920;
+ maxH = 1080;
+ bitRate = 50000000;
+ fps = 30.1;
+ break;
+ case CodecProfileLevel.AVCLevel42:
+ maxW = 2048;
+ maxH = 1080;
+ bitRate = 50000000;
+ fps = 60;
+ break;
+ case CodecProfileLevel.AVCLevel5:
+ maxW = 3672;
+ maxH = 1536;
+ bitRate = 135000000;
+ fps = 26.7;
+ break;
+ case CodecProfileLevel.AVCLevel51:
+ maxW = 4096;
+ maxH = 2304;
+ bitRate = 240000000;
+ fps = 26.7;
+ break;
+ default:
+ maxW = 4096;
+ maxH = 2304;
+ bitRate = 240000000;
+ fps = 26.7;
+ break;
+ }
+ if ((w > maxW) || (h > maxH)) {
+ Log.i(TAG, "Requested resolution (" + w + "," + h + ") exceeds (" +
+ maxW + "," + maxH + ")");
+ return null;
+ }
+ info.mFps = (int)(fps * maxW * maxH / (w * h));
+ info.mBitRate = bitRate;
+ Log.i(TAG, "AVC Level " + Integer.toHexString(highestLevel) + " bit rate " + bitRate +
+ " fps " + info.mFps);
+ }
+ return info;
+ }
+
+ /**
+ * Run encoding / decoding test for given mimeType of codec
+ * @param mimeType like video/avc
+ * @param w video width
+ * @param h video height
+ * @param numberRepeat how many times to repeat the encoding / decoding process
+ */
+ private void doTest(String mimeType, int w, int h, int numberRepeat) throws Exception {
+ CodecInfo infoEnc = getSupportedFormatInfo(mimeType, w, h, true);
+ if (infoEnc == null) {
+ Log.i(TAG, "Codec " + mimeType + "with " + w + "," + h + " not supported");
+ return;
+ }
+ CodecInfo infoDec = getSupportedFormatInfo(mimeType, w, h, false);
+ assertNotNull(infoDec);
+ mVideoWidth = w;
+ mVideoHeight = h;
+ initYUVPlane(w + YUV_PLANE_ADDITIONAL_LENGTH, h + YUV_PLANE_ADDITIONAL_LENGTH,
+ infoEnc.mSupportSemiPlanar, infoDec.mSupportSemiPlanar);
+ double[] encoderFpsResults = new double[numberRepeat];
+ double[] decoderFpsResults = new double[numberRepeat];
+ double[] totalFpsResults = new double[numberRepeat];
+ double[] decoderRmsErrorResults = new double[numberRepeat];
+ for (int i = 0; i < numberRepeat; i++) {
+ MediaFormat format = new MediaFormat();
+ format.setString(MediaFormat.KEY_MIME, mimeType);
+ format.setInteger(MediaFormat.KEY_BIT_RATE, infoEnc.mBitRate);
+ format.setInteger(MediaFormat.KEY_WIDTH, w);
+ format.setInteger(MediaFormat.KEY_HEIGHT, h);
+ format.setInteger(MediaFormat.KEY_COLOR_FORMAT,
+ infoEnc.mSupportSemiPlanar ? CodecCapabilities.COLOR_FormatYUV420SemiPlanar :
+ CodecCapabilities.COLOR_FormatYUV420Planar);
+ format.setInteger(MediaFormat.KEY_FRAME_RATE, infoEnc.mFps);
+ format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, KEY_I_FRAME_INTERVAL);
+ double encodingTime = runEncoder(VIDEO_AVC, format, TOTAL_FRAMES);
+ // re-initialize format for decoder
+ format = new MediaFormat();
+ format.setString(MediaFormat.KEY_MIME, mimeType);
+ format.setInteger(MediaFormat.KEY_WIDTH, w);
+ format.setInteger(MediaFormat.KEY_HEIGHT, h);
+ format.setInteger(MediaFormat.KEY_COLOR_FORMAT,
+ infoDec.mSupportSemiPlanar ? CodecCapabilities.COLOR_FormatYUV420SemiPlanar :
+ CodecCapabilities.COLOR_FormatYUV420Planar);
+ double[] decoderResult = runDecoder(VIDEO_AVC, format);
+ double decodingTime = decoderResult[0];
+ decoderRmsErrorResults[i] = decoderResult[1];
+ encoderFpsResults[i] = (double)TOTAL_FRAMES / encodingTime * 1000.0;
+ decoderFpsResults[i] = (double)TOTAL_FRAMES / decodingTime * 1000.0;
+ totalFpsResults[i] = (double)TOTAL_FRAMES / (encodingTime + decodingTime) * 1000.0;
+
+ // clear things for re-start
+ mEncodedOutputBuffer.clear();
+ // it will be good to clean everything to make every run the same.
+ System.gc();
+ }
+ getReportLog().printArray("encoder", encoderFpsResults, ResultType.HIGHER_BETTER,
+ ResultUnit.FPS);
+ getReportLog().printArray("rms error", decoderRmsErrorResults, ResultType.LOWER_BETTER,
+ ResultUnit.NONE);
+ getReportLog().printArray("decoder", decoderFpsResults, ResultType.HIGHER_BETTER,
+ ResultUnit.FPS);
+ getReportLog().printArray("encoder decoder", totalFpsResults, ResultType.HIGHER_BETTER,
+ ResultUnit.FPS);
+ getReportLog().printSummary("encoder decoder", Stat.getAverage(totalFpsResults),
+ ResultType.HIGHER_BETTER, ResultUnit.FPS);
+ // make sure that rms error is not too big.
+ for (int i = 0; i < numberRepeat; i++) {
+ assertTrue(decoderRmsErrorResults[i] < PIXEL_RMS_ERROR_MARGAIN);
+ }
+ }
+
+ /**
+ * run encoder benchmarking
+ * @param mimeType encoder type like video/avc
+ * @param format format of media to encode
+ * @param totalFrames total number of frames to encode
+ * @return time taken in ms to encode the frames. This does not include initialization time.
+ */
+ private double runEncoder(String mimeType, MediaFormat format, int totalFrames) {
+ MediaCodec codec = MediaCodec.createEncoderByType(mimeType);
+ try {
+ codec.configure(
+ format,
+ null /* surface */,
+ null /* crypto */,
+ MediaCodec.CONFIGURE_FLAG_ENCODE);
+ } catch (IllegalStateException e) {
+ Log.e(TAG, "codec '" + mimeType + "' failed configuration.");
+ assertTrue("codec '" + mimeType + "' failed configuration.", false);
+ }
+ codec.start();
+ ByteBuffer[] codecInputBuffers = codec.getInputBuffers();
+ ByteBuffer[] codecOutputBuffers = codec.getOutputBuffers();
+
+ int numBytesSubmitted = 0;
+ int numBytesDequeued = 0;
+ int inFramesCount = 0;
+ long start = System.currentTimeMillis();
+ while (true) {
+ int index;
+
+ if (inFramesCount < totalFrames) {
+ index = codec.dequeueInputBuffer(VIDEO_CODEC_WAIT_TIME_US /* timeoutUs */);
+ if (index != MediaCodec.INFO_TRY_AGAIN_LATER) {
+ int size = queueInputBufferEncoder(
+ codec, codecInputBuffers, index, inFramesCount,
+ (inFramesCount == (totalFrames - 1)) ?
+ MediaCodec.BUFFER_FLAG_END_OF_STREAM : 0);
+ inFramesCount++;
+ numBytesSubmitted += size;
+ if (VERBOSE) {
+ Log.d(TAG, "queued " + size + " bytes of input data, frame " +
+ (inFramesCount - 1));
+ }
+
+ }
+ }
+ MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();
+ index = codec.dequeueOutputBuffer(info, VIDEO_CODEC_WAIT_TIME_US /* timeoutUs */);
+ if (index == MediaCodec.INFO_TRY_AGAIN_LATER) {
+ } else if (index == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
+ } else if (index == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
+ codecOutputBuffers = codec.getOutputBuffers();
+ } else if (index >= 0) {
+ dequeueOutputBufferEncoder(codec, codecOutputBuffers, index, info);
+ numBytesDequeued += info.size;
+ if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
+ if (VERBOSE) {
+ Log.d(TAG, "dequeued output EOS.");
+ }
+ break;
+ }
+ if (VERBOSE) {
+ Log.d(TAG, "dequeued " + info.size + " bytes of output data.");
+ }
+ }
+ }
+ long finish = System.currentTimeMillis();
+ if (VERBOSE) {
+ Log.d(TAG, "queued a total of " + numBytesSubmitted + "bytes, "
+ + "dequeued " + numBytesDequeued + " bytes.");
+ }
+ codec.stop();
+ codec.release();
+ codec = null;
+ return (double)(finish - start);
+ }
+
+ /**
+ * Fills input buffer for encoder from YUV buffers.
+ * @return size of enqueued data.
+ */
+ private int queueInputBufferEncoder(
+ MediaCodec codec, ByteBuffer[] inputBuffers, int index, int frameCount, int flags) {
+ ByteBuffer buffer = inputBuffers[index];
+ buffer.clear();
+
+ Point origin = getOrigin(frameCount);
+ // Y color first
+ int srcOffsetY = origin.x + origin.y * mBufferWidth;
+ final byte[] yBuffer = mYBuffer.array();
+ for (int i = 0; i < mVideoHeight; i++) {
+ buffer.put(yBuffer, srcOffsetY, mVideoWidth);
+ srcOffsetY += mBufferWidth;
+ }
+ if (mSrcSemiPlanar) {
+ int srcOffsetU = origin.y / 2 * mBufferWidth + origin.x / 2 * 2;
+ final byte[] uvBuffer = mUVBuffer.array();
+ for (int i = 0; i < mVideoHeight / 2; i++) {
+ buffer.put(uvBuffer, srcOffsetU, mVideoWidth);
+ srcOffsetU += mBufferWidth;
+ }
+ } else {
+ int srcOffsetU = origin.y / 2 * mBufferWidth / 2 + origin.x / 2;
+ int srcOffsetV = srcOffsetU + mBufferWidth / 2 * mBufferHeight / 2;
+ final byte[] uvBuffer = mUVBuffer.array();
+ for (int i = 0; i < mVideoHeight /2; i++) { //U only
+ buffer.put(uvBuffer, srcOffsetU, mVideoWidth / 2);
+ srcOffsetU += mBufferWidth / 2;
+ }
+ for (int i = 0; i < mVideoHeight /2; i++) { //V only
+ buffer.put(uvBuffer, srcOffsetV, mVideoWidth / 2);
+ srcOffsetV += mBufferWidth / 2;
+ }
+ }
+ int size = mVideoHeight * mVideoWidth * 3 /2;
+
+ codec.queueInputBuffer(index, 0 /* offset */, size, 0 /* timeUs */, flags);
+ if (VERBOSE && (frameCount == 0)) {
+ printByteArray("Y ", mYBuffer.array(), 0, 20);
+ printByteArray("UV ", mUVBuffer.array(), 0, 20);
+ printByteArray("UV ", mUVBuffer.array(), mBufferWidth * 60, 20);
+ }
+ return size;
+ }
+
+ /**
+ * Dequeue encoded data from output buffer and store for later usage.
+ */
+ private void dequeueOutputBufferEncoder(
+ MediaCodec codec, ByteBuffer[] outputBuffers,
+ int index, MediaCodec.BufferInfo info) {
+ ByteBuffer output = outputBuffers[index];
+ output.clear();
+ int l = info.size;
+ ByteBuffer copied = ByteBuffer.allocate(l);
+ output.get(copied.array(), 0, l);
+ mEncodedOutputBuffer.add(copied);
+ codec.releaseOutputBuffer(index, false /* render */);
+ }
+
+ /**
+ * run encoder benchmarking with encoded stream stored from encoding phase
+ * @param mimeType encoder type like video/avc
+ * @param format format of media to decode
+ * @return returns length-2 array with 0: time for decoding, 1 : rms error of pixels
+ */
+ private double[] runDecoder(String mimeType, MediaFormat format) {
+ MediaCodec codec = MediaCodec.createDecoderByType(mimeType);
+ codec.configure(format, null /* surface */, null /* crypto */, 0 /* flags */);
+ codec.start();
+ ByteBuffer[] codecInputBuffers = codec.getInputBuffers();
+ ByteBuffer[] codecOutputBuffers = codec.getOutputBuffers();
+
+ double totalErrorSquared = 0;
+
+ MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();
+ boolean sawOutputEOS = false;
+ int inputLeft = mEncodedOutputBuffer.size();
+ int inputBufferCount = 0;
+ int outFrameCount = 0;
+ YUVValue expected = new YUVValue();
+ YUVValue decoded = new YUVValue();
+ long start = System.currentTimeMillis();
+ while (!sawOutputEOS) {
+ if (inputLeft > 0) {
+ int inputBufIndex = codec.dequeueInputBuffer(VIDEO_CODEC_WAIT_TIME_US);
+
+ if (inputBufIndex >= 0) {
+ ByteBuffer dstBuf = codecInputBuffers[inputBufIndex];
+ dstBuf.clear();
+ ByteBuffer src = mEncodedOutputBuffer.get(inputBufferCount);
+ int writeSize = src.capacity();
+ dstBuf.put(src.array(), 0, writeSize);
+ codec.queueInputBuffer(
+ inputBufIndex,
+ 0 /* offset */,
+ writeSize,
+ 0,
+ (inputLeft == 1) ? MediaCodec.BUFFER_FLAG_END_OF_STREAM : 0);
+ inputLeft --;
+ inputBufferCount ++;
+ }
+ }
+
+ int res = codec.dequeueOutputBuffer(info, VIDEO_CODEC_WAIT_TIME_US);
+ if (res >= 0) {
+ int outputBufIndex = res;
+ ByteBuffer buf = codecOutputBuffers[outputBufIndex];
+ if (VERBOSE && (outFrameCount == 0)) {
+ printByteBuffer("Y ", buf, 0, 20);
+ printByteBuffer("UV ", buf, mVideoWidth * mVideoHeight, 20);
+ printByteBuffer("UV ", buf, mVideoWidth * mVideoHeight + mVideoWidth * 60, 20);
+ }
+ Point origin = getOrigin(outFrameCount);
+ for (int i = 0; i < PIXEL_CHECK_PER_FRAME; i++) {
+ int w = mRandom.nextInt(mVideoWidth);
+ int h = mRandom.nextInt(mVideoHeight);
+ getPixelValuesFromYUVBuffers(origin.x, origin.y, w, h, expected);
+ getPixelValuesFromOutputBuffer(buf, w, h, decoded);
+ if (VERBOSE) {
+ Log.i(TAG, outFrameCount + "-" + i + "- th round expcted " + expected.mY +
+ "," + expected.mU + "," + expected.mV + " decoded " + decoded.mY +
+ "," + decoded.mU + "," + decoded.mV);
+ }
+ totalErrorSquared += expected.calcErrorSquared(decoded);
+ }
+ codec.releaseOutputBuffer(outputBufIndex, false /* render */);
+ if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
+ Log.d(TAG, "saw output EOS.");
+ sawOutputEOS = true;
+ }
+ outFrameCount++;
+ } else if (res == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
+ codecOutputBuffers = codec.getOutputBuffers();
+ Log.d(TAG, "output buffers have changed.");
+ } else if (res == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
+ MediaFormat oformat = codec.getOutputFormat();
+ Log.d(TAG, "output format has changed to " + oformat);
+ int colorFormat = oformat.getInteger(MediaFormat.KEY_COLOR_FORMAT);
+ if (colorFormat == CodecCapabilities.COLOR_FormatYUV420SemiPlanar ) {
+ mDstSemiPlanar = true;
+ } else if (colorFormat == CodecCapabilities.COLOR_FormatYUV420Planar ) {
+ mDstSemiPlanar = false;
+ } else {
+ fail("unsupported color format " + Integer.toHexString(colorFormat));
+ }
+ }
+ }
+ long finish = System.currentTimeMillis();
+ codec.stop();
+ codec.release();
+ codec = null;
+ assertTrue(outFrameCount >= TOTAL_FRAMES);
+ // divide by 3 as sum is done for Y, U, V.
+ double errorRms = Math.sqrt(totalErrorSquared / PIXEL_CHECK_PER_FRAME / outFrameCount / 3);
+ double[] result = { (double) finish - start, errorRms };
+ return result;
+ }
+
+ /**
+ * returns origin in the absolute frame for given frame count.
+ * The video scene is moving by moving origin per each frame.
+ */
+ private Point getOrigin(int frameCount) {
+ if (frameCount < 100) {
+ return new Point(2 * frameCount, 0);
+ } else if (frameCount < 200) {
+ return new Point(200, (frameCount - 100) * 2);
+ } else {
+ if (frameCount > 300) { // for safety
+ frameCount = 300;
+ }
+ return new Point(600 - frameCount * 2, 600 - frameCount * 2);
+ }
+ }
+
+ /**
+ * initialize reference YUV plane
+ * @param w This should be YUV_PLANE_ADDITIONAL_LENGTH pixels bigger than video resolution
+ * to allow movements
+ * @param h This should be YUV_PLANE_ADDITIONAL_LENGTH pixels bigger than video resolution
+ * to allow movements
+ * @param semiPlanarEnc
+ * @param semiPlanarDec
+ */
+ private void initYUVPlane(int w, int h, boolean semiPlanarEnc, boolean semiPlanarDec) {
+ int bufferSizeY = w * h;
+ mYBuffer = ByteBuffer.allocate(bufferSizeY);
+ mUVBuffer = ByteBuffer.allocate(bufferSizeY / 2);
+ mSrcSemiPlanar = semiPlanarEnc;
+ mDstSemiPlanar = semiPlanarDec;
+ mBufferWidth = w;
+ mBufferHeight = h;
+ final byte[] yArray = mYBuffer.array();
+ final byte[] uvArray = mUVBuffer.array();
+ for (int i = 0; i < h; i++) {
+ for (int j = 0; j < w; j++) {
+ yArray[i * w + j] = clampY((i + j) & 0xff);
+ }
+ }
+ if (semiPlanarEnc) {
+ for (int i = 0; i < h/2; i++) {
+ for (int j = 0; j < w/2; j++) {
+ uvArray[i * w + 2 * j] = (byte) (i & 0xff);
+ uvArray[i * w + 2 * j + 1] = (byte) (j & 0xff);
+ }
+ }
+ } else { // planar, U first, then V
+ int vOffset = bufferSizeY / 4;
+ for (int i = 0; i < h/2; i++) {
+ for (int j = 0; j < w/2; j++) {
+ uvArray[i * w/2 + j] = (byte) (i & 0xff);
+ uvArray[i * w/2 + vOffset + j] = (byte) (j & 0xff);
+ }
+ }
+ }
+ }
+
+ /**
+ * class to store pixel values in YUV
+ *
+ */
+ public class YUVValue {
+ public byte mY;
+ public byte mU;
+ public byte mV;
+ public YUVValue() {
+ }
+
+ public boolean equalTo(YUVValue other) {
+ return (mY == other.mY) && (mU == other.mU) && (mV == other.mV);
+ }
+
+ public double calcErrorSquared(YUVValue other) {
+ double yDelta = mY - other.mY;
+ double uDelta = mU - other.mU;
+ double vDelta = mV - other.mV;
+ return yDelta * yDelta + uDelta * uDelta + vDelta * vDelta;
+ }
+ }
+
+ /**
+ * Read YUV values from given position (x,y) for given origin (originX, originY)
+ * The whole data is already available from YBuffer and UVBuffer.
+ * @param result pass the result via this. This is for avoiding creating / destroying too many
+ * instances
+ */
+ private void getPixelValuesFromYUVBuffers(int originX, int originY, int x, int y,
+ YUVValue result) {
+ result.mY = mYBuffer.get((originY + y) * mBufferWidth + (originX + x));
+ if (mSrcSemiPlanar) {
+ int index = (originY + y) / 2 * mBufferWidth + (originX + x) / 2 * 2;
+ //Log.d(TAG, "YUV " + originX + "," + originY + "," + x + "," + y + "," + index);
+ result.mU = mUVBuffer.get(index);
+ result.mV = mUVBuffer.get(index + 1);
+ } else {
+ int vOffset = mBufferWidth * mBufferHeight / 4;
+ int index = (originY + y) / 2 * mBufferWidth / 2 + (originX + x) / 2;
+ result.mU = mUVBuffer.get(index);
+ result.mV = mUVBuffer.get(vOffset + index);
+ }
+ }
+
+ /**
+ * Read YUV pixels from decoded output buffer for give (x, y) position
+ * Output buffer is composed of Y parts followed by U/V
+ * @param result pass the result via this. This is for avoiding creating / destroying too many
+ * instances
+ */
+ private void getPixelValuesFromOutputBuffer(ByteBuffer buffer, int x, int y, YUVValue result) {
+ result.mY = buffer.get(y * mVideoWidth + x);
+ if (mDstSemiPlanar) {
+ int index = mVideoWidth * mVideoHeight + y / 2 * mVideoWidth + x / 2 * 2;
+ //Log.d(TAG, "Decoded " + x + "," + y + "," + index);
+ result.mU = buffer.get(index);
+ result.mV = buffer.get(index + 1);
+ } else {
+ int vOffset = mVideoWidth * mVideoHeight / 4;
+ int index = mVideoWidth * mVideoHeight + y / 2 * mVideoWidth / 2 + x / 2;
+ result.mU = buffer.get(index);
+ result.mV = buffer.get(index + vOffset);
+ }
+ }
+
+ /**
+ * Y cannot have full range. clamp it to prevent invalid value.
+ */
+ private byte clampY(int y) {
+ if (y < Y_CLAMP_MIN) {
+ y = Y_CLAMP_MIN;
+ } else if (y > Y_CLAMP_MAX) {
+ y = Y_CLAMP_MAX;
+ }
+ return (byte) (y & 0xff);
+ }
+
+ /**
+ * Search for given codecName and returns CodecCapabilities if found
+ * @param codecName
+ * @param isEncoder true for encoder, false for decoder
+ * @return null if the codec is not supported
+ */
+ private CodecCapabilities getCodecCapability(
+ String codecName, boolean isEncoder) {
+ int codecCount = MediaCodecList.getCodecCount();
+ for (int i = 0; i < codecCount; ++i) {
+ MediaCodecInfo info = MediaCodecList.getCodecInfoAt(i);
+ String[] types = info.getSupportedTypes();
+ if (isEncoder != info.isEncoder()) {
+ continue;
+ }
+ for (int j = 0; j < types.length; ++j) {
+ if (types[j].compareTo(codecName) == 0) {
+ CodecCapabilities cap = info.getCapabilitiesForType(types[j]);
+ Log.i(TAG, "Use codec " + info.getName());
+ return cap;
+ }
+ }
+ }
+ return null;
+ }
+
+ /// for debugging
+ private void printIntArray(String msg, int[] data) {
+ StringBuilder builder = new StringBuilder();
+ builder.append(msg);
+ builder.append(":");
+ for (int e : data) {
+ builder.append(Integer.toHexString(e));
+ builder.append(",");
+ }
+ builder.deleteCharAt(builder.length() - 1);
+ Log.i(TAG, builder.toString());
+ }
+
+ /// for debugging
+ private void printByteArray(String msg, byte[] data, int offset, int len) {
+ StringBuilder builder = new StringBuilder();
+ builder.append(msg);
+ builder.append(":");
+ for (int i = offset; i < offset + len; i++) {
+ builder.append(Integer.toHexString(data[i]));
+ builder.append(",");
+ }
+ builder.deleteCharAt(builder.length() - 1);
+ Log.i(TAG, builder.toString());
+ }
+
+ /// for debugging
+ private void printByteBuffer(String msg, ByteBuffer data, int offset, int len) {
+ StringBuilder builder = new StringBuilder();
+ builder.append(msg);
+ builder.append(":");
+ for (int i = offset; i < offset + len; i++) {
+ builder.append(Integer.toHexString(data.get(i)));
+ builder.append(",");
+ }
+ builder.deleteCharAt(builder.length() - 1);
+ Log.i(TAG, builder.toString());
+ }
+}
diff --git a/suite/pts/expectations/knownfailures.txt b/suite/pts/expectations/knownfailures.txt
deleted file mode 100644
index 0d4f101..0000000
--- a/suite/pts/expectations/knownfailures.txt
+++ /dev/null
@@ -1,2 +0,0 @@
-[
-]
diff --git a/suite/pts/hostTests/bootup/Android.mk b/suite/pts/hostTests/bootup/Android.mk
index 4d779a0..1872284 100644
--- a/suite/pts/hostTests/bootup/Android.mk
+++ b/suite/pts/hostTests/bootup/Android.mk
@@ -24,7 +24,7 @@
LOCAL_JAVA_LIBRARIES := cts-tradefed tradefed-prebuilt ddmlib-prebuilt junit ptscommonutilhost
-LOCAL_PTS_TEST_PACKAGE := com.android.pts.bootup
+LOCAL_CTS_TEST_PACKAGE := com.android.pts.bootup
-include $(BUILD_PTS_HOST_JAVA_LIBRARY)
+include $(BUILD_CTS_HOST_JAVA_LIBRARY)
diff --git a/suite/pts/hostTests/bootup/src/com/android/pts/bootup/BootupTimeTest.java b/suite/pts/hostTests/bootup/src/com/android/pts/bootup/BootupTimeTest.java
index e78289b..dd7b834 100644
--- a/suite/pts/hostTests/bootup/src/com/android/pts/bootup/BootupTimeTest.java
+++ b/suite/pts/hostTests/bootup/src/com/android/pts/bootup/BootupTimeTest.java
@@ -19,6 +19,8 @@
import android.cts.util.TimeoutReq;
import com.android.pts.util.MeasureRun;
import com.android.pts.util.MeasureTime;
+import com.android.pts.util.ResultType;
+import com.android.pts.util.ResultUnit;
import com.android.pts.util.ReportLog;
import com.android.pts.util.Stat;
import com.android.pts.util.Stat.StatResult;
@@ -65,9 +67,11 @@
rebootDevice();
}
});
- mReport.printArray("time in ms", result, false);
+ mReport.printArray("bootup time", result, ResultType.LOWER_BETTER,
+ ResultUnit.MS);
StatResult stat = Stat.getStat(result);
- mReport.printSummary("time in ms", stat.mAverage, stat.mStddev);
+ mReport.printSummary("bootup time", stat.mAverage, ResultType.LOWER_BETTER,
+ ResultUnit.MS);
}
private void rebootDevice() throws DeviceNotAvailableException {
diff --git a/suite/pts/hostTests/browser/Android.mk b/suite/pts/hostTests/browser/Android.mk
deleted file mode 100644
index 9e45477..0000000
--- a/suite/pts/hostTests/browser/Android.mk
+++ /dev/null
@@ -1,31 +0,0 @@
-# Copyright (C) 2012 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_SRC_FILES := $(call all-java-files-under, src)
-
-LOCAL_MODULE_TAGS := optional
-
-LOCAL_MODULE := PtsHostBrowser
-
-LOCAL_JAVA_LIBRARIES := cts-tradefed tradefed-prebuilt ddmlib-prebuilt junit ptscommonutilhost ptshostutil
-
-LOCAL_PTS_TEST_PACKAGE := com.android.pts.browser
-
-include $(BUILD_PTS_HOST_JAVA_LIBRARY)
-
-include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/suite/pts/hostTests/browser/browserlauncher/src/com/android/pts/browser/LaunchBrowserTest.java b/suite/pts/hostTests/browser/browserlauncher/src/com/android/pts/browser/LaunchBrowserTest.java
deleted file mode 100644
index a9e48f3..0000000
--- a/suite/pts/hostTests/browser/browserlauncher/src/com/android/pts/browser/LaunchBrowserTest.java
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * Copyright (C) 2012 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.pts.browser;
-
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.net.Uri;
-import android.webkit.cts.CtsTestServer;
-import com.android.pts.util.PtsAndroidTestCase;
-
-import java.util.concurrent.Semaphore;
-import java.util.concurrent.TimeUnit;
-
-/**
- * Device side code to run browser benchmarking.
- * It launches an activity with URL and wait for broadcast sent from host.
- * It is host's responsibility to send broadcast after parsing browser's result.
- */
-public class LaunchBrowserTest extends PtsAndroidTestCase {
-
- private static final String OCTANE_START_FILE = "octane/index.html";
- private static final String HOST_COMPLETION_BROADCAST = "com.android.pts.browser.completion";
- private static long BROWSER_COMPLETION_TIMEOUT = 10 * 60;
- private CtsTestServer mWebServer;
- private HostBroadcastReceiver mReceiver;
-
- @Override
- protected void setUp() throws Exception {
- super.setUp();
- mWebServer = new CtsTestServer(getContext());
- mReceiver = new HostBroadcastReceiver();
- mReceiver.register(getContext(), HOST_COMPLETION_BROADCAST);
- }
-
- @Override
- protected void tearDown() throws Exception {
- mReceiver.unregister(getContext());
- mWebServer.shutdown();
- mWebServer = null;
- super.tearDown();
- }
-
- public void testOctane() throws InterruptedException {
- String url = mWebServer.getAssetUrl(OCTANE_START_FILE) + "?auto=1";
- Uri uri = Uri.parse(url);
- Intent intent = new Intent(Intent.ACTION_VIEW, uri);
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- getContext().startActivity(intent);
- mReceiver.waitForBroadcast(BROWSER_COMPLETION_TIMEOUT);
- }
-
- class HostBroadcastReceiver extends BroadcastReceiver {
- private final Semaphore mSemaphore = new Semaphore(0);
-
- public void register(Context context, String actionName) {
- IntentFilter filter = new IntentFilter();
- filter.addAction(actionName);
- context.registerReceiver(this, filter);
- }
-
- public void unregister(Context context) {
- context.unregisterReceiver(this);
- }
-
- public boolean waitForBroadcast(long timeoutInSec) throws InterruptedException {
- return mSemaphore.tryAcquire(timeoutInSec, TimeUnit.SECONDS);
- }
-
- @Override
- public void onReceive(Context context, Intent intent) {
- mSemaphore.release();
- }
- }
-}
diff --git a/suite/pts/hostTests/browser/src/com/android/pts/browser/BrowserTest.java b/suite/pts/hostTests/browser/src/com/android/pts/browser/BrowserTest.java
deleted file mode 100644
index b5400ab..0000000
--- a/suite/pts/hostTests/browser/src/com/android/pts/browser/BrowserTest.java
+++ /dev/null
@@ -1,159 +0,0 @@
-/*
- * Copyright (C) 2012 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.pts.browser;
-
-import android.cts.util.TimeoutReq;
-
-import com.android.cts.tradefed.build.CtsBuildHelper;
-import com.android.ddmlib.Log;
-import com.android.ddmlib.testrunner.RemoteAndroidTestRunner;
-import com.android.pts.ptsutil.LogcatLineReceiver;
-import com.android.pts.util.ReportLog;
-import com.android.pts.util.Stat;
-import com.android.tradefed.build.IBuildInfo;
-import com.android.tradefed.device.DeviceNotAvailableException;
-import com.android.tradefed.device.ITestDevice;
-import com.android.tradefed.result.CollectingTestListener;
-import com.android.tradefed.result.TestRunResult;
-import com.android.tradefed.testtype.DeviceTestCase;
-import com.android.tradefed.testtype.IBuildReceiver;
-
-import java.io.File;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-/**
- * Run browser benchmarking.
- * Benchmarking result is printed via logcat (JavaScript console.log method).
- * Corresponding device side test should be running to make browser benchmarking working,
- * And the device test expects HOST_COMPLETION_BROADCAST broadcast from host as device
- * test cannot detect the completion from browser side.
- */
-public class BrowserTest extends DeviceTestCase implements IBuildReceiver {
- private static final String BROADCAST_CMD = "am broadcast -a com.android.pts.browser.completion";
- private static final String TAG = "BrowserTest";
- private static final String CTS_RUNNER = "android.test.InstrumentationCtsTestRunner";
- private static final String PACKAGE = "com.android.pts.browser";
- private static final String APK = "PtsDeviceBrowserLauncher.apk";
- private static final long LOGCAT_TIMEOUT_IN_SEC = 10 * 60L;
- private static final String LOGCAT_FILTER = " browser:D chromium:D *:S";
- private static final long REBOOT_WAIT_TIME_IN_MS = 2 * 60 * 1000L;
-
- private CtsBuildHelper mBuild;
- private ITestDevice mDevice;
- private LogcatLineReceiver mReceiver;
- private ReportLog mReport;
-
- private volatile boolean mIgnoreLine = true;
- private double mResult;
-
- @Override
- public void setBuild(IBuildInfo buildInfo) {
- mBuild = CtsBuildHelper.createBuildHelper(buildInfo);
- }
-
- @Override
- protected void setUp() throws Exception {
- super.setUp();
- mReport = new ReportLog();
- mDevice = getDevice();
- mDevice.uninstallPackage(PACKAGE);
- File app = mBuild.getTestApp(APK);
- mDevice.installPackage(app, false);
- }
-
- @Override
- protected void tearDown() throws Exception {
- super.tearDown();
- mDevice.uninstallPackage(PACKAGE);
- mReport.throwReportToHost();
- }
-
- @TimeoutReq(minutes = 40)
- public void testOctane() throws Exception {
- String resultPattern = "([A-Z][\\d\\w]+): ([\\d]+)";
- String summaryPattern = "(Octane Score.*): ([\\d]+)";
- int numberRepeat = 5;
- double[] results = new double[numberRepeat];
- for (int i = 0; i < numberRepeat; i++) {
- Log.i(TAG, i + "-th round");
- // browser will not refresh if the page is already loaded.
- mDevice.reboot();
- Thread.sleep(REBOOT_WAIT_TIME_IN_MS);
- results[i] = runBenchmarking("testOctane", resultPattern, summaryPattern);
- }
- mReport.printArray("scores", results, true);
- Stat.StatResult stat = Stat.getStat(results);
- mReport.printSummary("Score", stat.mAverage, stat.mStddev);
- }
-
- private double runBenchmarking(String testMethodName, String resultPattern,
- String summaryPattern) throws DeviceNotAvailableException, InterruptedException {
- RemoteAndroidTestRunner testRunner = new RemoteAndroidTestRunner(PACKAGE, CTS_RUNNER,
- mDevice.getIDevice());
- testRunner.setMethodName("com.android.pts.browser.LaunchBrowserTest", testMethodName);
- CollectingTestListener listener = new CollectingTestListener();
- mIgnoreLine = true;
- startLogMonitoring(resultPattern, summaryPattern);
- // hack to ignore already captured logcat as the monitor will get it again.
- // Checking time and skipping may be a better logic, but simply throwing away also works.
- Thread.sleep(5000);
- mIgnoreLine = false;
- Log.i(TAG, "start to run test in device");
- mDevice.runInstrumentationTests(testRunner, listener);
- TestRunResult result = listener.getCurrentRunResults();
- if (result.isRunFailure()) {
- fail(result.getRunFailureMessage());
- }
- if (result.getNumPassedTests() == 0) {
- fail("maybe timeout");
- }
- stopLogMonitoring();
- return mResult;
- }
-
- void startLogMonitoring(String resultPattern, String summaryPattern)
- throws InterruptedException, DeviceNotAvailableException {
- final Pattern result = Pattern.compile(resultPattern);
- final Pattern summary = Pattern.compile(summaryPattern);
- mReceiver = new LogcatLineReceiver(mDevice, LOGCAT_FILTER, LOGCAT_TIMEOUT_IN_SEC) {
- @Override
- public void processALine(String line) throws DeviceNotAvailableException {
- Log.i(TAG, "processALine " + line + " ignore " + mIgnoreLine);
- if (mIgnoreLine) {
- return;
- }
- Matcher matchResult = result.matcher(line);
- if (matchResult.find()) {
- mReport.printValue(matchResult.group(1),
- Double.parseDouble(matchResult.group(2)));
- }
- Matcher matchSummary = summary.matcher(line);
- if (matchSummary.find()) {
- mResult = Double.parseDouble(matchSummary.group(2));
- mReport.printValue(matchSummary.group(1), mResult);
- mDevice.executeShellCommand(BROADCAST_CMD);
- }
- }
- };
- mReceiver.start();
- }
-
- void stopLogMonitoring() {
- mReceiver.stop();
- }
-}
diff --git a/suite/pts/hostTests/uihost/Android.mk b/suite/pts/hostTests/uihost/Android.mk
index 77673a2..65742e2 100644
--- a/suite/pts/hostTests/uihost/Android.mk
+++ b/suite/pts/hostTests/uihost/Android.mk
@@ -24,8 +24,8 @@
LOCAL_JAVA_LIBRARIES := cts-tradefed tradefed-prebuilt ddmlib-prebuilt junit ptscommonutilhost
-LOCAL_PTS_TEST_PACKAGE := com.android.pts.uihost
+LOCAL_CTS_TEST_PACKAGE := com.android.pts.uihost
-include $(BUILD_PTS_HOST_JAVA_LIBRARY)
+include $(BUILD_CTS_HOST_JAVA_LIBRARY)
include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/suite/pts/hostTests/uihost/appA/Android.mk b/suite/pts/hostTests/uihost/appA/Android.mk
index 7dce44c..4d31cb4 100644
--- a/suite/pts/hostTests/uihost/appA/Android.mk
+++ b/suite/pts/hostTests/uihost/appA/Android.mk
@@ -30,6 +30,6 @@
LOCAL_SDK_VERSION := 16
-include $(BUILD_PTS_PACKAGE)
+include $(BUILD_CTS_PACKAGE)
diff --git a/suite/pts/hostTests/uihost/appB/Android.mk b/suite/pts/hostTests/uihost/appB/Android.mk
index caa1090..bb1b5d7 100644
--- a/suite/pts/hostTests/uihost/appB/Android.mk
+++ b/suite/pts/hostTests/uihost/appB/Android.mk
@@ -30,6 +30,6 @@
LOCAL_SDK_VERSION := 16
-include $(BUILD_PTS_PACKAGE)
+include $(BUILD_CTS_PACKAGE)
diff --git a/suite/pts/hostTests/uihost/control/Android.mk b/suite/pts/hostTests/uihost/control/Android.mk
index 86ceb7c..dec5ad1 100644
--- a/suite/pts/hostTests/uihost/control/Android.mk
+++ b/suite/pts/hostTests/uihost/control/Android.mk
@@ -30,4 +30,4 @@
LOCAL_SDK_VERSION := 16
-include $(BUILD_PTS_PACKAGE)
+include $(BUILD_CTS_PACKAGE)
diff --git a/suite/pts/hostTests/uihost/control/src/com/android/pts/taskswitching/control/TaskswitchingDeviceTest.java b/suite/pts/hostTests/uihost/control/src/com/android/pts/taskswitching/control/TaskswitchingDeviceTest.java
index fc5bc04..5147eaf 100644
--- a/suite/pts/hostTests/uihost/control/src/com/android/pts/taskswitching/control/TaskswitchingDeviceTest.java
+++ b/suite/pts/hostTests/uihost/control/src/com/android/pts/taskswitching/control/TaskswitchingDeviceTest.java
@@ -27,6 +27,8 @@
import com.android.pts.util.MeasureRun;
import com.android.pts.util.MeasureTime;
+import com.android.pts.util.ResultType;
+import com.android.pts.util.ResultUnit;
import com.android.pts.util.PtsAndroidTestCase;
import com.android.pts.util.Stat;
@@ -81,9 +83,11 @@
}
}
});
- getReportLog().printArray("ms", results, false);
+ getReportLog().printArray("taskswitching time", results, ResultType.LOWER_BETTER,
+ ResultUnit.MS);
Stat.StatResult stat = Stat.getStat(results);
- getReportLog().printSummary("Time ms", stat.mAverage, stat.mStddev);
+ getReportLog().printSummary("taskswitching time", stat.mAverage,
+ ResultType.LOWER_BETTER, ResultUnit.MS);
}
private void startActivity(String packageName, String activityName) {
diff --git a/suite/pts/hostTests/uihost/src/com/android/pts/uihost/InstallTimeTest.java b/suite/pts/hostTests/uihost/src/com/android/pts/uihost/InstallTimeTest.java
index 4bfcde1..056e0f4 100644
--- a/suite/pts/hostTests/uihost/src/com/android/pts/uihost/InstallTimeTest.java
+++ b/suite/pts/hostTests/uihost/src/com/android/pts/uihost/InstallTimeTest.java
@@ -16,37 +16,26 @@
package com.android.pts.uihost;
-import android.cts.util.TimeoutReq;
-
import com.android.cts.tradefed.build.CtsBuildHelper;
-import com.android.ddmlib.Log;
-import com.android.ddmlib.testrunner.RemoteAndroidTestRunner;
-import com.android.ddmlib.testrunner.TestIdentifier;
import com.android.pts.util.MeasureRun;
import com.android.pts.util.MeasureTime;
-import com.android.pts.util.PtsException;
+import com.android.pts.util.ResultType;
+import com.android.pts.util.ResultUnit;
import com.android.pts.util.ReportLog;
import com.android.pts.util.Stat;
import com.android.pts.util.Stat.StatResult;
import com.android.tradefed.build.IBuildInfo;
-import com.android.tradefed.device.DeviceNotAvailableException;
import com.android.tradefed.device.ITestDevice;
-import com.android.tradefed.device.TestDeviceOptions;
-import com.android.tradefed.result.CollectingTestListener;
-import com.android.tradefed.result.TestResult;
-import com.android.tradefed.result.TestRunResult;
import com.android.tradefed.testtype.DeviceTestCase;
import com.android.tradefed.testtype.IBuildReceiver;
import java.io.File;
-import java.util.Map;
+
/**
* Test to measure installation time of a APK.
*/
public class InstallTimeTest extends DeviceTestCase implements IBuildReceiver {
- private static final String TAG = "InstallTimeTest";
- private final static String CTS_RUNNER = "android.test.InstrumentationCtsTestRunner";
private CtsBuildHelper mBuild;
private ITestDevice mDevice;
private ReportLog mReport = null;
@@ -89,9 +78,11 @@
device.installPackage(app, false);
}
});
- mReport.printArray("time in ms", result, false);
+ mReport.printArray("install time", result, ResultType.LOWER_BETTER,
+ ResultUnit.MS);
StatResult stat = Stat.getStat(result);
- mReport.printSummary("time in ms", stat.mAverage, stat.mStddev);
+ mReport.printSummary("install time", stat.mAverage, ResultType.LOWER_BETTER,
+ ResultUnit.MS);
}
}
diff --git a/suite/pts/lib/commonutil/Android.mk b/suite/pts/lib/commonutil/Android.mk
index 27e0ba7..152f862 100644
--- a/suite/pts/lib/commonutil/Android.mk
+++ b/suite/pts/lib/commonutil/Android.mk
@@ -35,6 +35,8 @@
LOCAL_MODULE_TAGS := optional
+LOCAL_JAVA_LIBRARIES := junit
+
LOCAL_MODULE := ptscommonutilhost
include $(BUILD_HOST_JAVA_LIBRARY)
\ No newline at end of file
diff --git a/suite/pts/lib/commonutil/src/com/android/pts/util/ReportLog.java b/suite/pts/lib/commonutil/src/com/android/pts/util/ReportLog.java
index 0d69c2f..8a5e624 100644
--- a/suite/pts/lib/commonutil/src/com/android/pts/util/ReportLog.java
+++ b/suite/pts/lib/commonutil/src/com/android/pts/util/ReportLog.java
@@ -19,17 +19,17 @@
import java.util.LinkedList;
import java.util.List;
+import junit.framework.Assert;
+
/**
* Utility class to print performance measurement result back to host.
* For now, throws know exception with message.
*
* Format:
- * LOG_SEPARATOR : separates each log
- * Message = log [LOG_SEPARATOR log]*
- * log for single value = classMethodName:line_number|header|d|value
- * log for array = classMethodName:line_number|header|da|values|
- * average average_value min|max value stddev value
+ * Message = summary log SUMMARY_SEPARATOR [LOG_SEPARATOR log]*
+ * summary = message|target|unit|type|value, target can be " " if there is no target set.
+ * log for array = classMethodName:line_number|message|unit|type|space seSummaryparated values
*/
public class ReportLog {
private static final String LOG_SEPARATOR = "+++";
@@ -39,42 +39,80 @@
private List<String> mMessages = new LinkedList<String> ();
private String mSummary = null;
protected static int mDepth = 3;
+
/**
- * print given value to the report
- * @param header string to explain the contents. It can be unit for the value.
- * @param val
+ * print array of values to output log
*/
- public void printValue(String header, double val) {
- String message = getClassMethodNames(mDepth, true) + LOG_ELEM_SEPARATOR + header +
- LOG_ELEM_SEPARATOR + "d" + LOG_ELEM_SEPARATOR + val;
- mMessages.add(message);
- printLog(message);
+ public void printArray(String message, double[] values, ResultType type,
+ ResultUnit unit) {
+ doPrintArray(message, values, type, unit);
}
/**
- * array version of printValue
- * @param header
- * @param val
- * @param addMin add minimum to the result. If false, add maximum to the result
+ * Print a value to output log
*/
- public void printArray(String header, double[] val, boolean addMin) {
+ public void printValue(String message, double value, ResultType type,
+ ResultUnit unit) {
+ double[] vals = { value };
+ doPrintArray(message, vals, type, unit);
+ }
+
+ private void doPrintArray(String message, double[] values, ResultType type,
+ ResultUnit unit) {
StringBuilder builder = new StringBuilder();
- builder.append(getClassMethodNames(mDepth, true) + LOG_ELEM_SEPARATOR + header +
- LOG_ELEM_SEPARATOR + "da" + LOG_ELEM_SEPARATOR);
- for (double v : val) {
+ // note mDepth + 1 as this function will be called by printVaue or printArray
+ // and we need caller of printValue / printArray
+ builder.append(getClassMethodNames(mDepth + 1, true) + LOG_ELEM_SEPARATOR + message +
+ LOG_ELEM_SEPARATOR + type.getXmlString() + LOG_ELEM_SEPARATOR +
+ unit.getXmlString() + LOG_ELEM_SEPARATOR);
+ for (double v : values) {
builder.append(v);
builder.append(" ");
}
- Stat.StatResult stat = Stat.getStat(val);
- builder.append(LOG_ELEM_SEPARATOR + "average " + stat.mAverage +
- (addMin ? (" min " + stat.mMin) : (" max " + stat.mMax)) + " stddev " + stat.mStddev);
mMessages.add(builder.toString());
printLog(builder.toString());
}
- public void printSummary(String header, double average, double stddev) {
- mSummary = header + LOG_ELEM_SEPARATOR + "average " + average + LOG_ELEM_SEPARATOR +
- "stddev " + stddev;
+ /**
+ * record the result of benchmarking with performance target.
+ * Depending on the ResultType, the function can fail if the result
+ * does not meet the target. For example, for the type of HIGHER_BETTER,
+ * value of 1.0 with target of 2.0 will fail.
+ *
+ * @param message message to be printed in the final report
+ * @param target target performance for the benchmarking
+ * @param value measured value
+ * @param type
+ * @param unit
+ */
+ public void printSummaryWithTarget(String message, double target, double value,
+ ResultType type, ResultUnit unit) {
+ mSummary = message + LOG_ELEM_SEPARATOR + target + LOG_ELEM_SEPARATOR + type.getXmlString()
+ + LOG_ELEM_SEPARATOR + unit.getXmlString() + LOG_ELEM_SEPARATOR + value;
+ boolean resultOk = true;
+ if (type == ResultType.HIGHER_BETTER) {
+ resultOk = value >= target;
+ } else if (type == ResultType.LOWER_BETTER) {
+ resultOk = value <= target;
+ }
+ if (!resultOk) {
+ Assert.fail("Measured result " + value + " does not meet perf target " + target +
+ " with type " + type.getXmlString());
+ }
+ }
+
+ /**
+ * For standard report summary without target value.
+ * Note that this function will not fail as there is no target.
+ * @param messsage
+ * @param value
+ * @param type type of the value
+ * @param unit unit of the data
+ */
+ public void printSummary(String message, double value, ResultType type,
+ ResultUnit unit) {
+ mSummary = message + LOG_ELEM_SEPARATOR + " " + LOG_ELEM_SEPARATOR + type.getXmlString() +
+ LOG_ELEM_SEPARATOR + unit.getXmlString() + LOG_ELEM_SEPARATOR + value;
}
public void throwReportToHost() throws PtsException {
@@ -114,9 +152,6 @@
/**
* array version of calcRatePerSecArray
- * @param change
- * @param timeInMSec
- * @return
*/
public static double[] calcRatePerSecArray(double change, double[] timeInMSec) {
double[] result = new double[timeInMSec.length];
@@ -134,9 +169,6 @@
/**
* copy array from src to dst with given offset in dst.
* dst should be big enough to hold src
- * @param src
- * @param dst
- * @param dstOffset
*/
public static void copyArray(double[] src, double[] dst, int dstOffset) {
for (int i = 0; i < src.length; i++) {
@@ -146,8 +178,6 @@
/**
* get classname.methodname from call stack of the current thread
- *
- * @return
*/
public static String getClassMethodNames() {
return getClassMethodNames(mDepth, false);
@@ -155,14 +185,13 @@
private static String getClassMethodNames(int depth, boolean addLineNumber) {
StackTraceElement[] elements = Thread.currentThread().getStackTrace();
- String names = elements[depth].getClassName() + "." + elements[depth].getMethodName() +
+ String names = elements[depth].getClassName() + "#" + elements[depth].getMethodName() +
(addLineNumber ? ":" + elements[depth].getLineNumber() : "");
return names;
}
/**
* to be overridden by child to print message to be passed
- * @param msg
*/
protected void printLog(String msg) {
diff --git a/suite/pts/lib/commonutil/src/com/android/pts/util/ResultType.java b/suite/pts/lib/commonutil/src/com/android/pts/util/ResultType.java
new file mode 100644
index 0000000..499654d
--- /dev/null
+++ b/suite/pts/lib/commonutil/src/com/android/pts/util/ResultType.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2012 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.pts.util;
+
+/**
+ * Enum for distinguishing performance results.
+ */
+public enum ResultType {
+ /** lower score shows better performance */
+ LOWER_BETTER("lowerBetter"),
+ /** higher score shows better performance */
+ HIGHER_BETTER("higherBetter"),
+ /** This value is not directly correlated with performance. */
+ NEUTRAL("neutral"),
+ /** presence of this type requires some attention although it may not be an error. */
+ WARNING("warning");
+
+ final private String mXmlString;
+ ResultType(String xmlString) {
+ mXmlString = xmlString;
+ }
+
+ /**
+ * Return string used in CTS XML report
+ */
+ public String getXmlString() {
+ return mXmlString;
+ }
+}
diff --git a/suite/pts/lib/commonutil/src/com/android/pts/util/ResultUnit.java b/suite/pts/lib/commonutil/src/com/android/pts/util/ResultUnit.java
new file mode 100644
index 0000000..b234cb5
--- /dev/null
+++ b/suite/pts/lib/commonutil/src/com/android/pts/util/ResultUnit.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2012 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.pts.util;
+
+/**
+ * Enum for representing the unit of performance results.
+ *
+ */
+public enum ResultUnit {
+ /** for value with no unit */
+ NONE,
+ /** milli-seconds */
+ MS,
+ /** frames per second */
+ FPS,
+ /** operations per second */
+ OPS,
+ /** kilo-bytes-per-second, not bits-per-second */
+ KBPS,
+ /** mega-bytes-per-second */
+ MBPS,
+ /** amount of data, bytes */
+ BYTE,
+ /** tell how many times it did happen. */
+ COUNT,
+ /** unit for benchmarking with generic score. */
+ SCORE;
+
+ /**
+ * Return string used in CTS XML report
+ */
+ public String getXmlString() {
+ return name().toLowerCase();
+ }
+}
+
diff --git a/suite/pts/lib/commonutil/src/com/android/pts/util/Stat.java b/suite/pts/lib/commonutil/src/com/android/pts/util/Stat.java
index aae51ec..d56f59e 100644
--- a/suite/pts/lib/commonutil/src/com/android/pts/util/Stat.java
+++ b/suite/pts/lib/commonutil/src/com/android/pts/util/Stat.java
@@ -22,6 +22,9 @@
*/
public class Stat {
+ /**
+ * Collection of statistical propertirs like average, max, min, and stddev
+ */
public static class StatResult {
public double mAverage;
public double mMin;
@@ -35,6 +38,9 @@
}
}
+ /**
+ * Calculate statistics properties likes average, min, max, and stddev for the given array
+ */
public static StatResult getStat(double[] data) {
double average = data[0];
double min = data[0];
@@ -57,6 +63,20 @@
return new StatResult(average, min, max, stddev);
}
+ /**
+ * return the average value of the passed array
+ */
+ public static double getAverage(double[] data) {
+ double sum = data[0];
+ for (int i = 1; i < data.length; i++) {
+ sum += data[i];
+ }
+ return sum / data.length;
+ }
+
+ /**
+ * return the minimum value of the passed array
+ */
public static double getMin(double[] data) {
double min = data[0];
for (int i = 1; i < data.length; i++) {
@@ -67,6 +87,9 @@
return min;
}
+ /**
+ * return the maximum value of the passed array
+ */
public static double getMax(double[] data) {
double max = data[0];
for (int i = 1; i < data.length; i++) {
diff --git a/suite/pts/tools/tradefed/pts-tradefed b/suite/pts/tools/tradefed/pts-tradefed
deleted file mode 100755
index 901ad8c..0000000
--- a/suite/pts/tools/tradefed/pts-tradefed
+++ /dev/null
@@ -1,86 +0,0 @@
-#!/bin/bash
-
-# Copyright (C) 2011 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.
-
-# launcher script for cts-tradefed harness for PTS
-# can be used from an Android build environment, or a standalone pts zip
-
-checkFile() {
- if [ ! -f "$1" ]; then
- echo "Unable to locate $1"
- exit
- fi;
-}
-
-checkPath() {
- if ! type -P $1 &> /dev/null; then
- echo "Unable to find $1 in path."
- exit
- fi;
-}
-
-checkPath adb
-checkPath java
-
-# check java version
-JAVA_VERSION=$(java -version 2>&1 | head -n 1 | grep '[ "]1\.6[\. "$$]')
-if [ "${JAVA_VERSION}" == "" ]; then
- echo "Wrong java version. 1.6 is required."
- exit
-fi
-
-# check debug flag and set up remote debugging
-if [ -n "${TF_DEBUG}" ]; then
- if [ -z "${TF_DEBUG_PORT}" ]; then
- TF_DEBUG_PORT=10088
- fi
- RDBG_FLAG=-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=${TF_DEBUG_PORT}
-fi
-
-
-# check if in Android build env
-if [ ! -z ${ANDROID_BUILD_TOP} ]; then
- HOST=`uname`
- if [ "$HOST" == "Linux" ]; then
- OS="linux-x86"
- elif [ "$HOST" == "Darwin" ]; then
- OS="darwin-x86"
- else
- echo "Unrecognized OS"
- exit
- fi;
- PTS_ROOT=${ANDROID_BUILD_TOP}/out/host/${OS}/pts
- if [ ! -d ${PTS_ROOT} ]; then
- echo "Could not find $PTS_ROOT in Android build environment. Try 'make pts'"
- exit
- fi;
-fi;
-
-if [ -z ${PTS_ROOT} ]; then
- # assume we're in an extracted pts install
- PTS_ROOT="$(dirname $0)/../.."
-fi;
-
-JAR_DIR=${PTS_ROOT}/android-pts/tools
-JARS="ddmlib-prebuilt.jar tradefed-prebuilt.jar hosttestlib.jar cts-tradefed.jar ptscommonutilhost.jar ptshostutil.jar"
-
-for JAR in $JARS; do
- checkFile ${JAR_DIR}/${JAR}
- JAR_PATH=${JAR_PATH}:${JAR_DIR}/${JAR}
-done
-
-java $RDBG_FLAG \
- -cp ${JAR_PATH} -DCTS_ROOT=${PTS_ROOT} -DPTS=1 com.android.cts.tradefed.command.CtsConsole "$@"
-
diff --git a/tests/AndroidManifest.xml b/tests/AndroidManifest.xml
index 590ee36..e9b802a 100644
--- a/tests/AndroidManifest.xml
+++ b/tests/AndroidManifest.xml
@@ -244,6 +244,14 @@
</intent-filter>
</activity>
+ <activity android:name="android.widget.cts.GridLayoutStubActivity"
+ android:label="GridLayoutStubActivity">
+ <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.LayoutDirectionStubActivity"
android:label="LayoutDirectionStubActivity">
<intent-filter>
diff --git a/tests/SignatureTest/Android.mk b/tests/SignatureTest/Android.mk
index cc0d53c..0b51f25 100644
--- a/tests/SignatureTest/Android.mk
+++ b/tests/SignatureTest/Android.mk
@@ -33,7 +33,11 @@
# To be passed in on command line
CTS_API_VERSION ?= current
+ifeq (current,$(CTS_API_VERSION))
+android_api_description := frameworks/base/api/$(CTS_API_VERSION).txt
+else
android_api_description := $(SRC_API_DIR)/$(CTS_API_VERSION).txt
+endif
# Can't call local-intermediates-dir directly here because we have to
# include BUILD_PACAKGE first. Can't include BUILD_PACKAGE first
@@ -41,75 +45,50 @@
# hack.
intermediates.COMMON := $(call intermediates-dir-for,APPS,$(LOCAL_PACKAGE_NAME),,COMMON)
signature_res_dir := $(intermediates.COMMON)/genres
-LOCAL_RESOURCE_DIR := $(signature_res_dir)
-
-# These shell commands allow us to get around the package.mk check for
-# empty/non-existent resouce dirs (which ours would be). If it finds
-# an empty/non-existent resource dir, R_file_stamp doesn't contain a
-# target and our stuff never gets copied.
-# TODO: fix package.mk so we can avoid this hack
-fake_resource_check := $(signature_res_dir)/raw/fake_resource_check
-
-$(shell \
- if [ ! -f $(fake_resource_check) ]; then \
- mkdir -p $(dir $(fake_resource_check)); \
- touch $(fake_resource_check); \
- fi \
- )
+LOCAL_RESOURCE_DIR := $(signature_res_dir) $(LOCAL_PATH)/res
include $(BUILD_PACKAGE)
-copied_res_stamp := $(intermediates.COMMON)/copyres.stamp
generated_res_stamp := $(intermediates.COMMON)/genres.stamp
-api_ver_path := $(intermediates.COMMON)
-api_ver_file := $(api_ver_path)/api_ver_is_$(CTS_API_VERSION)
+api_ver_file := $(intermediates.COMMON)/api_ver_is_$(CTS_API_VERSION)
# The api_ver_file keeps track of which api version was last built.
# By only ever having one of these magic files in existance and making
# sure the generated resources rule depend on it, we can ensure that
# the proper version of the api resource gets generated.
$(api_ver_file):
- $(hide) rm -f $(api_ver_path)/api_ver_is_*
- $(hide) touch $@
+ $(hide) rm -f $(dir $@)/api_ver_is_* \
+ && mkdir -p $(dir $@) && touch $@
android_api_xml_description := $(intermediates.COMMON)/api.xml
-$(android_api_xml_description): PRIVATE_INPUT_FILE := $(android_api_description)
-$(android_api_xml_description): $(android_api_description) $(APICHECK)
- $(hide) echo "Convert api file to xml: $@"
- $(hide) $(APICHECK_COMMAND) -convert2xml $(PRIVATE_INPUT_FILE) $@
-
-static_res_deps := $(call find-subdir-assets,$(LOCAL_PATH)/res)
-$(copied_res_stamp): PRIVATE_PATH := $(LOCAL_PATH)
-$(copied_res_stamp): PRIVATE_MODULE := $(LOCAL_MODULE)
-$(copied_res_stamp): PRIVATE_RES_DIR := $(signature_res_dir)
-$(copied_res_stamp): FAKE_RESOURCE_DIR := $(dir $(fake_resource_check))
-$(copied_res_stamp): FAKE_RESOURCE_CHECK := $(fake_resource_check)
-$(copied_res_stamp): $(foreach res,$(static_res_deps),$(LOCAL_PATH)/res/${res}) | $(ACP)
- $(hide) echo "Copy resources: $(PRIVATE_MODULE)"
- $(hide) rm -f $@
- $(hide) rm -rf $(PRIVATE_RES_DIR)
- $(hide) mkdir -p $(PRIVATE_RES_DIR)
- $(hide) if [ ! -f $(FAKE_RESOURCE_CHECK) ]; \
- then mkdir -p $(FAKE_RESOURCE_DIR); \
- touch $(FAKE_RESOURCE_CHECK); \
- fi
- $(hide) $(ACP) -rd $(PRIVATE_PATH)/res/* $(PRIVATE_RES_DIR)/
- $(hide) touch $@
+$(android_api_xml_description): $(android_api_description) | $(APICHECK)
+ @ echo "Convert api file to xml: $@"
+ @ mkdir -p $(dir $@)
+ $(hide) $(APICHECK_COMMAND) -convert2xml $< $@
# Split up config/api/1.xml by "package" and save the files as the
# resource files of SignatureTest.
$(generated_res_stamp): PRIVATE_PATH := $(LOCAL_PATH)
$(generated_res_stamp): PRIVATE_MODULE := $(LOCAL_MODULE)
$(generated_res_stamp): PRIVATE_RES_DIR := $(signature_res_dir)
+$(generated_res_stamp): PRIVATE_API_XML_DESC := $(android_api_xml_description)
$(generated_res_stamp): $(api_ver_file)
-$(generated_res_stamp): $(copied_res_stamp) $(android_api_xml_description)
- $(hide) echo "Copy generated resources: $(PRIVATE_MODULE)"
- $(hide) rm -f $@
+$(generated_res_stamp): $(android_api_xml_description)
+ @ echo "Copy generated resources: $(PRIVATE_MODULE)"
$(hide) python cts/tools/utils/android_api_description_splitter.py \
- $(android_api_xml_description) $(PRIVATE_RES_DIR) package
+ $(PRIVATE_API_XML_DESC) $(PRIVATE_RES_DIR) package
$(hide) touch $@
-$(R_file_stamp): $(generated_res_stamp) $(copied_res_stamp)
+$(R_file_stamp): $(generated_res_stamp)
+
+# clean up temp vars
+android_api_xml_description :=
+api_ver_file :=
+generated_res_stamp :=
+signature_res_dir :=
+android_api_description :=
+CTS_API_VERSION :=
+
# Use the folloing include to make our test apk.
include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/tests/assets/webkit/test_firstPage.html b/tests/assets/webkit/test_firstPage.html
index 4a2f9b0..18a5548 100644
--- a/tests/assets/webkit/test_firstPage.html
+++ b/tests/assets/webkit/test_firstPage.html
@@ -17,6 +17,6 @@
<title>First page</title>
<body>
<h1>First page</h1>
- <a herf="webView_goBackAndForward_test_secondPage.html">Go to second page</a>
+ <a href="webView_goBackAndForward_test_secondPage.html">Go to second page</a>
</body>
</html>
diff --git a/tests/assets/webkit/test_secondPage.html b/tests/assets/webkit/test_secondPage.html
index 952d43f..ea8e2a1 100644
--- a/tests/assets/webkit/test_secondPage.html
+++ b/tests/assets/webkit/test_secondPage.html
@@ -17,6 +17,6 @@
<title>Second page</title>
<body>
<h1>Second page</h1>
- <a herf="webView_goBackAndForward_test_thirdPage.html">Go to third page</a>
+ <a href="webView_goBackAndForward_test_thirdPage.html">Go to third page</a>
</body>
</html>
diff --git a/tests/src/android/view/inputmethod/cts/InputMethodInfoStub.java b/tests/res/layout/gridlayout_layout.xml
similarity index 63%
copy from tests/src/android/view/inputmethod/cts/InputMethodInfoStub.java
copy to tests/res/layout/gridlayout_layout.xml
index 0eeefbe..54b3b2c 100644
--- a/tests/src/android/view/inputmethod/cts/InputMethodInfoStub.java
+++ b/tests/res/layout/gridlayout_layout.xml
@@ -1,5 +1,6 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2012 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.
@@ -12,12 +13,11 @@
* 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.inputmethod.cts;
-
-import android.inputmethodservice.InputMethodService;
-
-public class InputMethodInfoStub extends InputMethodService {
-
-}
+ -->
+<GridLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ >
+</GridLayout>
diff --git a/tests/res/layout/viewtreeobserver_layout.xml b/tests/res/layout/viewtreeobserver_layout.xml
index 461a4bc..66e98b2 100644
--- a/tests/res/layout/viewtreeobserver_layout.xml
+++ b/tests/res/layout/viewtreeobserver_layout.xml
@@ -20,17 +20,19 @@
android:layout_width="match_parent"
android:layout_height="wrap_content">
- <ListView android:id="@+id/listview1"
+ <View android:id="@+id/view1"
android:layout_width="match_parent"
- android:layout_height="wrap_content"/>
+ android:layout_height="10dip"
+ android:focusable="true"/>
- <ListView android:id="@+id/listview2"
+ <View android:id="@+id/view2"
android:layout_width="match_parent"
- android:layout_height="wrap_content"/>
+ android:layout_height="10dip"
+ android:focusable="true"/>
<Button android:id="@+id/button1"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content" />
+ android:layout_width="wrap_content"
+ android:layout_height="10dip" />
</LinearLayout>
diff --git a/tests/res/raw/set_target_api_11.bc b/tests/res/raw/set_target_api_11.bc
index babbd9e..fa04be6 100644
--- a/tests/res/raw/set_target_api_11.bc
+++ b/tests/res/raw/set_target_api_11.bc
Binary files differ
diff --git a/tests/res/raw/set_target_api_12.bc b/tests/res/raw/set_target_api_12.bc
index 92ee8de..abe314f 100644
--- a/tests/res/raw/set_target_api_12.bc
+++ b/tests/res/raw/set_target_api_12.bc
Binary files differ
diff --git a/tests/res/raw/set_target_api_13.bc b/tests/res/raw/set_target_api_13.bc
index c842b98..4fa5b75 100644
--- a/tests/res/raw/set_target_api_13.bc
+++ b/tests/res/raw/set_target_api_13.bc
Binary files differ
diff --git a/tests/res/raw/set_target_api_14.bc b/tests/res/raw/set_target_api_14.bc
index 24d2481..876b37c 100644
--- a/tests/res/raw/set_target_api_14.bc
+++ b/tests/res/raw/set_target_api_14.bc
Binary files differ
diff --git a/tests/res/raw/set_target_api_15.bc b/tests/res/raw/set_target_api_15.bc
index a6e86d4..d2de6d9 100644
--- a/tests/res/raw/set_target_api_15.bc
+++ b/tests/res/raw/set_target_api_15.bc
Binary files differ
diff --git a/tests/res/raw/set_target_api_16.bc b/tests/res/raw/set_target_api_16.bc
index 5ded6fd..ca9aa65 100644
--- a/tests/res/raw/set_target_api_16.bc
+++ b/tests/res/raw/set_target_api_16.bc
Binary files differ
diff --git a/tests/res/raw/set_target_api_17.bc b/tests/res/raw/set_target_api_17.bc
new file mode 100644
index 0000000..b08b389
--- /dev/null
+++ b/tests/res/raw/set_target_api_17.bc
Binary files differ
diff --git a/tests/res/raw/set_target_api_too_high.bc b/tests/res/raw/set_target_api_too_high.bc
new file mode 100644
index 0000000..95b2026
--- /dev/null
+++ b/tests/res/raw/set_target_api_too_high.bc
Binary files differ
diff --git a/tests/src/android/provider/cts/MediaStoreAudioTestHelper.java b/tests/src/android/provider/cts/MediaStoreAudioTestHelper.java
index 7bcd0c7..a87cd3a 100644
--- a/tests/src/android/provider/cts/MediaStoreAudioTestHelper.java
+++ b/tests/src/android/provider/cts/MediaStoreAudioTestHelper.java
@@ -221,6 +221,60 @@
}
}
+ public static class Audio3 extends Audio1 {
+ private Audio3() {
+ }
+
+ private static Audio3 sInstance = new Audio3();
+
+ public static Audio3 getInstance() {
+ return sInstance;
+ }
+
+ @Override
+ public ContentValues getContentValues(boolean isInternal) {
+ ContentValues values = super.getContentValues(isInternal);
+ values.put(Media.DATA, values.getAsString(Media.DATA) + "_3");
+ return values;
+ }
+ }
+
+ public static class Audio4 extends Audio1 {
+ private Audio4() {
+ }
+
+ private static Audio4 sInstance = new Audio4();
+
+ public static Audio4 getInstance() {
+ return sInstance;
+ }
+
+ @Override
+ public ContentValues getContentValues(boolean isInternal) {
+ ContentValues values = super.getContentValues(isInternal);
+ values.put(Media.DATA, values.getAsString(Media.DATA) + "_4");
+ return values;
+ }
+ }
+
+ public static class Audio5 extends Audio1 {
+ private Audio5() {
+ }
+
+ private static Audio5 sInstance = new Audio5();
+
+ public static Audio5 getInstance() {
+ return sInstance;
+ }
+
+ @Override
+ public ContentValues getContentValues(boolean isInternal) {
+ ContentValues values = super.getContentValues(isInternal);
+ values.put(Media.DATA, values.getAsString(Media.DATA) + "_5");
+ return values;
+ }
+ }
+
// These constants are not part of the public API
public static final String EXTERNAL_VOLUME_NAME = "external";
public static final String INTERNAL_VOLUME_NAME = "internal";
diff --git a/tests/src/android/renderscript/cts/setelementat.rs b/tests/src/android/renderscript/cts/setelementat.rs
new file mode 100644
index 0000000..75e96f5
--- /dev/null
+++ b/tests/src/android/renderscript/cts/setelementat.rs
@@ -0,0 +1,51 @@
+#pragma version(1)
+#pragma rs java_package_name(android.renderscript.cts)
+
+#include "shared.rsh"
+
+int memset_toValue = 0;
+
+int compare_value = 0;
+int compare_failure = 2;
+int failure_value = 0;
+
+uint32_t dimX = 0;
+uint32_t dimY = 0;
+rs_allocation array;
+
+void memset(int *aout) {
+ *aout = memset_toValue;
+ return;
+}
+
+void compare(const int *ain) {
+ if (*ain != compare_value) {
+ rsAtomicCas(&compare_failure, 2, -1);
+ failure_value = *ain;
+ }
+ return;
+}
+
+void getCompareResult(int* aout) {
+ *aout = compare_failure;
+}
+
+void setLargeArray(const int *ain, uint32_t x) {
+ int source = 10;
+ if (x == 0) {
+ for (uint32_t i = 0; i < dimX; i++) {
+ rsSetElementAt(array, &source, i);
+ }
+ }
+}
+
+void setLargeArray2D(const int *ain, uint32_t x) {
+ int source = 10;
+ if (x == 0) {
+ for (uint32_t y = 0; y < dimY; y++) {
+ for (uint32_t xtemp = 0; xtemp < dimX; xtemp++) {
+ rsSetElementAt(array, &source, xtemp, y);
+ }
+ }
+ }
+}
diff --git a/tests/src/android/view/inputmethod/cts/InputMethodInfoStub.java b/tests/src/android/view/inputmethod/cts/InputMethodSettingsActivityStub.java
similarity index 84%
rename from tests/src/android/view/inputmethod/cts/InputMethodInfoStub.java
rename to tests/src/android/view/inputmethod/cts/InputMethodSettingsActivityStub.java
index 0eeefbe..58aa364 100644
--- a/tests/src/android/view/inputmethod/cts/InputMethodInfoStub.java
+++ b/tests/src/android/view/inputmethod/cts/InputMethodSettingsActivityStub.java
@@ -16,8 +16,8 @@
package android.view.inputmethod.cts;
-import android.inputmethodservice.InputMethodService;
+import android.preference.PreferenceActivity;
-public class InputMethodInfoStub extends InputMethodService {
+public class InputMethodSettingsActivityStub extends PreferenceActivity {
}
diff --git a/tests/src/android/widget/cts/GridLayoutStubActivity.java b/tests/src/android/widget/cts/GridLayoutStubActivity.java
new file mode 100644
index 0000000..3fa0f28
--- /dev/null
+++ b/tests/src/android/widget/cts/GridLayoutStubActivity.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2012 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;
+import com.android.cts.stub.R;
+
+/**
+ * A minimal application for {@link android.widget.GridLayout} test.
+ */
+public class GridLayoutStubActivity extends Activity {
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.gridlayout_layout);
+ }
+}
diff --git a/tests/tests/accessibility/src/android/view/accessibility/cts/AccessibilityEventTest.java b/tests/tests/accessibility/src/android/view/accessibility/cts/AccessibilityEventTest.java
index 9b46a9b..58b32d6 100644
--- a/tests/tests/accessibility/src/android/view/accessibility/cts/AccessibilityEventTest.java
+++ b/tests/tests/accessibility/src/android/view/accessibility/cts/AccessibilityEventTest.java
@@ -31,7 +31,7 @@
public class AccessibilityEventTest extends TestCase {
/** The number of properties of the {@link AccessibilityEvent} class. */
- private static final int NON_STATIC_FIELD_COUNT = 30;
+ private static final int NON_STATIC_FIELD_COUNT = 28;
/**
* Test that no new fields have been added without updating the
@@ -253,7 +253,6 @@
((Message) expectedEvent.getParcelableData()).what,
((Message) receivedEvent.getParcelableData()).what);
- AccessibilityRecord expectedRecord = expectedEvent.getRecord(0);
AccessibilityRecord receivedRecord = receivedEvent.getRecord(0);
AccessibilityRecordTest.assertEqualAccessibilityRecord(expectedEvent, receivedRecord);
}
diff --git a/tests/tests/accessibility/src/android/view/accessibility/cts/AccessibilityNodeInfoTest.java b/tests/tests/accessibility/src/android/view/accessibility/cts/AccessibilityNodeInfoTest.java
index e0ffaf7..f26e83c 100644
--- a/tests/tests/accessibility/src/android/view/accessibility/cts/AccessibilityNodeInfoTest.java
+++ b/tests/tests/accessibility/src/android/view/accessibility/cts/AccessibilityNodeInfoTest.java
@@ -30,7 +30,7 @@
public class AccessibilityNodeInfoTest extends AndroidTestCase {
/** The number of properties of the {@link AccessibilityNodeInfo} class. */
- private static final int NON_STATIC_FIELD_COUNT = 19;
+ private static final int NON_STATIC_FIELD_COUNT = 18;
@SmallTest
public void testMarshaling() throws Exception {
@@ -124,6 +124,7 @@
info.setMovementGranularities(AccessibilityNodeInfo.MOVEMENT_GRANULARITY_LINE);
info.setLabeledBy(new View(getContext()));
info.setLabelFor(new View(getContext()));
+ info.setViewId("foo.bar:id/baz");
}
/**
@@ -180,6 +181,8 @@
assertSame("movementGranularities has incorrect value",
expectedInfo.getMovementGranularities(),
receivedInfo.getMovementGranularities());
+ assertEquals("viewId has incorrect value", expectedInfo.getViewId(),
+ receivedInfo.getViewId());
}
/**
@@ -211,5 +214,6 @@
assertFalse("accessibilityFocused not properly recycled", info.isAccessibilityFocused());
assertSame("movementGranularities not properly recycled", 0,
info.getMovementGranularities());
+ assertNull("viewId not properly recycled", info.getViewId());
}
}
diff --git a/tests/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityTextTraversalTest.java b/tests/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityTextTraversalTest.java
index 517b71a..a508452 100644
--- a/tests/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityTextTraversalTest.java
+++ b/tests/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityTextTraversalTest.java
@@ -17,6 +17,7 @@
import android.os.Bundle;
import android.test.suitebuilder.annotation.MediumTest;
import android.text.Selection;
+import android.text.TextUtils;
import android.view.View;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;
@@ -86,7 +87,7 @@
(event.getEventType() ==
AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY
&& event.getAction() ==
- AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY
+ AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY
&& event.getPackageName().equals(getActivity().getPackageName())
&& event.getClassName().equals(View.class.getName())
&& event.getContentDescription().toString().equals(
@@ -94,7 +95,7 @@
&& event.getFromIndex() == 0
&& event.getToIndex() == 1
&& event.getMovementGranularity() ==
- AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER);
+ AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER);
}
}, TIMEOUT_ASYNC_PROCESSING);
@@ -116,7 +117,7 @@
(event.getEventType() ==
AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY
&& event.getAction() ==
- AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY
+ AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY
&& event.getPackageName().equals(getActivity().getPackageName())
&& event.getClassName().equals(View.class.getName())
&& event.getContentDescription().toString().equals(
@@ -124,7 +125,7 @@
&& event.getFromIndex() == 1
&& event.getToIndex() == 2
&& event.getMovementGranularity() ==
- AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER);
+ AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER);
}
}, TIMEOUT_ASYNC_PROCESSING);
@@ -146,7 +147,7 @@
(event.getEventType() ==
AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY
&& event.getAction() ==
- AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY
+ AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY
&& event.getPackageName().equals(getActivity().getPackageName())
&& event.getClassName().equals(View.class.getName())
&& event.getContentDescription().toString().equals(
@@ -154,7 +155,7 @@
&& event.getFromIndex() == 2
&& event.getToIndex() == 3
&& event.getMovementGranularity() ==
- AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER);
+ AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER);
}
}, TIMEOUT_ASYNC_PROCESSING);
@@ -165,7 +166,37 @@
assertFalse(getInteractionBridge().performAction(text,
AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY, arguments));
- // Move to the next character and wait for an event.
+ // Move to the previous character and wait for an event.
+ AccessibilityEvent fourthExpected = getInteractionBridge()
+ .executeCommandAndWaitForAccessibilityEvent(new Runnable() {
+ @Override
+ public void run() {
+ getInteractionBridge().performAction(text,
+ AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY, arguments);
+ }
+ }, new AccessibilityEventFilter() {
+ @Override
+ public boolean accept(AccessibilityEvent event) {
+ return
+ (event.getEventType() ==
+ AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY
+ && event.getAction() ==
+ AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY
+ && event.getPackageName().equals(getActivity().getPackageName())
+ && event.getClassName().equals(View.class.getName())
+ && event.getContentDescription().toString().equals(
+ getString(R.string.a_b))
+ && event.getFromIndex() == 2
+ && event.getToIndex() == 3
+ && event.getMovementGranularity() ==
+ AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER);
+ }
+ }, TIMEOUT_ASYNC_PROCESSING);
+
+ // Make sure we got the expected event.
+ assertNotNull(fourthExpected);
+
+ // Move to the previous character and wait for an event.
AccessibilityEvent fifthExpected = getInteractionBridge()
.executeCommandAndWaitForAccessibilityEvent(new Runnable() {
@Override
@@ -180,7 +211,7 @@
(event.getEventType() ==
AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY
&& event.getAction() ==
- AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY
+ AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY
&& event.getPackageName().equals(getActivity().getPackageName())
&& event.getClassName().equals(View.class.getName())
&& event.getContentDescription().toString().equals(
@@ -188,7 +219,7 @@
&& event.getFromIndex() == 1
&& event.getToIndex() == 2
&& event.getMovementGranularity() ==
- AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER);
+ AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER);
}
}, TIMEOUT_ASYNC_PROCESSING);
@@ -210,7 +241,7 @@
(event.getEventType() ==
AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY
&& event.getAction() ==
- AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY
+ AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY
&& event.getPackageName().equals(getActivity().getPackageName())
&& event.getClassName().equals(View.class.getName())
&& event.getContentDescription().toString().equals(
@@ -218,7 +249,7 @@
&& event.getFromIndex() == 0
&& event.getToIndex() == 1
&& event.getMovementGranularity() ==
- AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER);
+ AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER);
}
}, TIMEOUT_ASYNC_PROCESSING);
@@ -269,7 +300,7 @@
(event.getEventType() ==
AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY
&& event.getAction() ==
- AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY
+ AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY
&& event.getPackageName().equals(getActivity().getPackageName())
&& event.getClassName().equals(View.class.getName())
&& event.getContentDescription().toString().equals(
@@ -277,7 +308,7 @@
&& event.getFromIndex() == 0
&& event.getToIndex() == 3
&& event.getMovementGranularity() ==
- AccessibilityNodeInfo.MOVEMENT_GRANULARITY_WORD);
+ AccessibilityNodeInfo.MOVEMENT_GRANULARITY_WORD);
}
}, TIMEOUT_ASYNC_PROCESSING);
@@ -299,7 +330,7 @@
(event.getEventType() ==
AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY
&& event.getAction() ==
- AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY
+ AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY
&& event.getPackageName().equals(getActivity().getPackageName())
&& event.getClassName().equals(View.class.getName())
&& event.getContentDescription().toString().equals(
@@ -307,7 +338,7 @@
&& event.getFromIndex() == 4
&& event.getToIndex() == 7
&& event.getMovementGranularity() ==
- AccessibilityNodeInfo.MOVEMENT_GRANULARITY_WORD);
+ AccessibilityNodeInfo.MOVEMENT_GRANULARITY_WORD);
}
}, TIMEOUT_ASYNC_PROCESSING);
@@ -329,7 +360,7 @@
(event.getEventType() ==
AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY
&& event.getAction() ==
- AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY
+ AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY
&& event.getPackageName().equals(getActivity().getPackageName())
&& event.getClassName().equals(View.class.getName())
&& event.getContentDescription().toString().equals(
@@ -337,7 +368,7 @@
&& event.getFromIndex() == 8
&& event.getToIndex() == 11
&& event.getMovementGranularity() ==
- AccessibilityNodeInfo.MOVEMENT_GRANULARITY_WORD);
+ AccessibilityNodeInfo.MOVEMENT_GRANULARITY_WORD);
}
}, TIMEOUT_ASYNC_PROCESSING);
@@ -363,7 +394,7 @@
(event.getEventType() ==
AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY
&& event.getAction() ==
- AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY
+ AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY
&& event.getPackageName().equals(getActivity().getPackageName())
&& event.getClassName().equals(View.class.getName())
&& event.getContentDescription().toString().equals(
@@ -371,7 +402,7 @@
&& event.getFromIndex() == 8
&& event.getToIndex() == 11
&& event.getMovementGranularity() ==
- AccessibilityNodeInfo.MOVEMENT_GRANULARITY_WORD);
+ AccessibilityNodeInfo.MOVEMENT_GRANULARITY_WORD);
}
}, TIMEOUT_ASYNC_PROCESSING);
@@ -393,7 +424,7 @@
(event.getEventType() ==
AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY
&& event.getAction() ==
- AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY
+ AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY
&& event.getPackageName().equals(getActivity().getPackageName())
&& event.getClassName().equals(View.class.getName())
&& event.getContentDescription().toString().equals(
@@ -401,7 +432,7 @@
&& event.getFromIndex() == 4
&& event.getToIndex() == 7
&& event.getMovementGranularity() ==
- AccessibilityNodeInfo.MOVEMENT_GRANULARITY_WORD);
+ AccessibilityNodeInfo.MOVEMENT_GRANULARITY_WORD);
}
}, TIMEOUT_ASYNC_PROCESSING);
@@ -423,7 +454,7 @@
(event.getEventType() ==
AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY
&& event.getAction() ==
- AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY
+ AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY
&& event.getPackageName().equals(getActivity().getPackageName())
&& event.getClassName().equals(View.class.getName())
&& event.getContentDescription().toString().equals(
@@ -431,7 +462,7 @@
&& event.getFromIndex() == 0
&& event.getToIndex() == 3
&& event.getMovementGranularity() ==
- AccessibilityNodeInfo.MOVEMENT_GRANULARITY_WORD);
+ AccessibilityNodeInfo.MOVEMENT_GRANULARITY_WORD);
}
}, TIMEOUT_ASYNC_PROCESSING);
@@ -484,7 +515,7 @@
(event.getEventType() ==
AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY
&& event.getAction() ==
- AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY
+ AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY
&& event.getPackageName().equals(getActivity().getPackageName())
&& event.getClassName().equals(TextView.class.getName())
&& event.getText().size() > 0
@@ -492,7 +523,7 @@
&& event.getFromIndex() == 0
&& event.getToIndex() == 1
&& event.getMovementGranularity() ==
- AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER);
+ AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER);
}
}, TIMEOUT_ASYNC_PROCESSING);
@@ -518,7 +549,7 @@
(event.getEventType() ==
AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY
&& event.getAction() ==
- AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY
+ AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY
&& event.getPackageName().equals(getActivity().getPackageName())
&& event.getClassName().equals(TextView.class.getName())
&& event.getText().size() > 0
@@ -526,7 +557,7 @@
&& event.getFromIndex() == 1
&& event.getToIndex() == 2
&& event.getMovementGranularity() ==
- AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER);
+ AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER);
}
}, TIMEOUT_ASYNC_PROCESSING);
@@ -552,7 +583,7 @@
(event.getEventType() ==
AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY
&& event.getAction() ==
- AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY
+ AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY
&& event.getPackageName().equals(getActivity().getPackageName())
&& event.getClassName().equals(TextView.class.getName())
&& event.getText().size() > 0
@@ -560,7 +591,7 @@
&& event.getFromIndex() == 2
&& event.getToIndex() == 3
&& event.getMovementGranularity() ==
- AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER);
+ AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER);
}
}, TIMEOUT_ASYNC_PROCESSING);
@@ -579,7 +610,7 @@
assertEquals(3, Selection.getSelectionStart(textView.getText()));
assertEquals(3, Selection.getSelectionEnd(textView.getText()));
- // Move to the next character and wait for an event.
+ // Move to the previous character and wait for an event.
AccessibilityEvent fifthExpected = getInteractionBridge()
.executeCommandAndWaitForAccessibilityEvent(new Runnable() {
@Override
@@ -594,15 +625,15 @@
(event.getEventType() ==
AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY
&& event.getAction() ==
- AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY
+ AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY
&& event.getPackageName().equals(getActivity().getPackageName())
&& event.getClassName().equals(TextView.class.getName())
&& event.getText().size() > 0
&& event.getText().get(0).toString().equals(getString(R.string.a_b))
- && event.getFromIndex() == 1
- && event.getToIndex() == 2
+ && event.getFromIndex() == 2
+ && event.getToIndex() == 3
&& event.getMovementGranularity() ==
- AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER);
+ AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER);
}
}, TIMEOUT_ASYNC_PROCESSING);
@@ -613,7 +644,7 @@
assertEquals(2, Selection.getSelectionStart(textView.getText()));
assertEquals(2, Selection.getSelectionEnd(textView.getText()));
- // Move to the next character and wait for an event.
+ // Move to the previous character and wait for an event.
AccessibilityEvent sixthExpected = getInteractionBridge()
.executeCommandAndWaitForAccessibilityEvent(new Runnable() {
@Override
@@ -628,15 +659,15 @@
(event.getEventType() ==
AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY
&& event.getAction() ==
- AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY
+ AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY
&& event.getPackageName().equals(getActivity().getPackageName())
&& event.getClassName().equals(TextView.class.getName())
&& event.getText().size() > 0
&& event.getText().get(0).toString().equals(getString(R.string.a_b))
- && event.getFromIndex() == 0
- && event.getToIndex() == 1
+ && event.getFromIndex() == 1
+ && event.getToIndex() == 2
&& event.getMovementGranularity() ==
- AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER);
+ AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER);
}
}, TIMEOUT_ASYNC_PROCESSING);
@@ -647,6 +678,40 @@
assertEquals(1, Selection.getSelectionStart(textView.getText()));
assertEquals(1, Selection.getSelectionEnd(textView.getText()));
+ // Move to the previous character and wait for an event.
+ AccessibilityEvent seventhExpected = getInteractionBridge()
+ .executeCommandAndWaitForAccessibilityEvent(new Runnable() {
+ @Override
+ public void run() {
+ getInteractionBridge().performAction(text,
+ AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY, arguments);
+ }
+ }, new AccessibilityEventFilter() {
+ @Override
+ public boolean accept(AccessibilityEvent event) {
+ return
+ (event.getEventType() ==
+ AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY
+ && event.getAction() ==
+ AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY
+ && event.getPackageName().equals(getActivity().getPackageName())
+ && event.getClassName().equals(TextView.class.getName())
+ && event.getText().size() > 0
+ && event.getText().get(0).toString().equals(getString(R.string.a_b))
+ && event.getFromIndex() == 0
+ && event.getToIndex() == 1
+ && event.getMovementGranularity() ==
+ AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER);
+ }
+ }, TIMEOUT_ASYNC_PROCESSING);
+
+ // Make sure we got the expected event.
+ assertNotNull(seventhExpected);
+
+ // Verify the selection position.
+ assertEquals(0, Selection.getSelectionStart(textView.getText()));
+ assertEquals(0, Selection.getSelectionEnd(textView.getText()));
+
// Make sure there is no previous.
assertFalse(getInteractionBridge().performAction(text,
AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY, arguments));
@@ -657,6 +722,284 @@
}
@MediumTest
+ public void testActionNextAndPreviousAtGranularityCharacterOverTextExtend()
+ throws Exception {
+ final EditText editText = (EditText) getActivity().findViewById(R.id.edit);
+
+ getInstrumentation().runOnMainSync(new Runnable() {
+ @Override
+ public void run() {
+ editText.setText(getString(R.string.a_b));
+ Selection.removeSelection(editText.getText());
+ }
+ });
+
+ final AccessibilityNodeInfo text = getInteractionBridge()
+ .findAccessibilityNodeInfoByTextFromRoot(getString(R.string.a_b));
+
+ final int granularities = text.getMovementGranularities();
+ assertEquals(granularities, AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER
+ | AccessibilityNodeInfo.MOVEMENT_GRANULARITY_WORD
+ | AccessibilityNodeInfo.MOVEMENT_GRANULARITY_LINE
+ | AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PARAGRAPH
+ | AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PAGE);
+
+ final Bundle arguments = new Bundle();
+ arguments.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT,
+ AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER);
+ arguments.putBoolean(AccessibilityNodeInfo.ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN, true);
+
+ // Move to the next character and wait for an event.
+ AccessibilityEvent firstExpected = getInteractionBridge()
+ .executeCommandAndWaitForAccessibilityEvent(new Runnable() {
+ @Override
+ public void run() {
+ getInteractionBridge().performAction(text,
+ AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY, arguments);
+ }
+ }, new AccessibilityEventFilter() {
+ @Override
+ public boolean accept(AccessibilityEvent event) {
+ return
+ (event.getEventType() ==
+ AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY
+ && event.getAction() ==
+ AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY
+ && event.getPackageName().equals(getActivity().getPackageName())
+ && event.getClassName().equals(EditText.class.getName())
+ && event.getText().size() > 0
+ && event.getText().get(0).toString().equals(getString(R.string.a_b))
+ && event.getFromIndex() == 0
+ && event.getToIndex() == 1
+ && event.getMovementGranularity() ==
+ AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER);
+ }
+ }, TIMEOUT_ASYNC_PROCESSING);
+
+ // Make sure we got the expected event.
+ assertNotNull(firstExpected);
+
+ // Verify the selection position.
+ assertEquals(0, Selection.getSelectionStart(editText.getText()));
+ assertEquals(1, Selection.getSelectionEnd(editText.getText()));
+
+ // Move to the next character and wait for an event.
+ AccessibilityEvent secondExpected = getInteractionBridge()
+ .executeCommandAndWaitForAccessibilityEvent(new Runnable() {
+ @Override
+ public void run() {
+ getInteractionBridge().performAction(text,
+ AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY, arguments);
+ }
+ }, new AccessibilityEventFilter() {
+ @Override
+ public boolean accept(AccessibilityEvent event) {
+ return
+ (event.getEventType() ==
+ AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY
+ && event.getAction() ==
+ AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY
+ && event.getPackageName().equals(getActivity().getPackageName())
+ && event.getClassName().equals(EditText.class.getName())
+ && event.getText().size() > 0
+ && event.getText().get(0).toString().equals(getString(R.string.a_b))
+ && event.getFromIndex() == 1
+ && event.getToIndex() == 2
+ && event.getMovementGranularity() ==
+ AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER);
+ }
+ }, TIMEOUT_ASYNC_PROCESSING);
+
+ // Make sure we got the expected event.
+ assertNotNull(secondExpected);
+
+ // Verify the selection position.
+ assertEquals(0, Selection.getSelectionStart(editText.getText()));
+ assertEquals(2, Selection.getSelectionEnd(editText.getText()));
+
+ // Move to the next character and wait for an event.
+ AccessibilityEvent thirdExpected = getInteractionBridge()
+ .executeCommandAndWaitForAccessibilityEvent(new Runnable() {
+ @Override
+ public void run() {
+ getInteractionBridge().performAction(text,
+ AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY, arguments);
+ }
+ }, new AccessibilityEventFilter() {
+ @Override
+ public boolean accept(AccessibilityEvent event) {
+ return
+ (event.getEventType() ==
+ AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY
+ && event.getAction() ==
+ AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY
+ && event.getPackageName().equals(getActivity().getPackageName())
+ && event.getClassName().equals(EditText.class.getName())
+ && event.getText().size() > 0
+ && event.getText().get(0).toString().equals(getString(R.string.a_b))
+ && event.getFromIndex() == 2
+ && event.getToIndex() == 3
+ && event.getMovementGranularity() ==
+ AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER);
+ }
+ }, TIMEOUT_ASYNC_PROCESSING);
+
+ // Make sure we got the expected event.
+ assertNotNull(thirdExpected);
+
+ // Verify the selection position.
+ assertEquals(0, Selection.getSelectionStart(editText.getText()));
+ assertEquals(3, Selection.getSelectionEnd(editText.getText()));
+
+ // Make sure there is no next.
+ assertFalse(getInteractionBridge().performAction(text,
+ AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY, arguments));
+
+ // Verify the selection position.
+ assertEquals(0, Selection.getSelectionStart(editText.getText()));
+ assertEquals(3, Selection.getSelectionEnd(editText.getText()));
+
+ // Focus the view so we can change selection.
+ getInstrumentation().runOnMainSync(new Runnable() {
+ @Override
+ public void run() {
+ editText.setFocusable(true);
+ editText.requestFocus();
+ }
+ });
+
+ // Put selection at the end of the text.
+ Bundle setSelectionArgs = new Bundle();
+ setSelectionArgs.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_START_INT, 3);
+ setSelectionArgs.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_END_INT, 3);
+ assertTrue(getInteractionBridge().performAction(text,
+ AccessibilityNodeInfo.ACTION_SET_SELECTION, setSelectionArgs));
+
+ // Verify the selection position.
+ assertEquals(3, Selection.getSelectionStart(editText.getText()));
+ assertEquals(3, Selection.getSelectionEnd(editText.getText()));
+
+ // Unfocus the view so we can get rid of the soft-keyboard.
+ getInstrumentation().runOnMainSync(new Runnable() {
+ @Override
+ public void run() {
+ editText.clearFocus();
+ editText.setFocusable(false);
+ }
+ });
+
+ // Move to the previous character and wait for an event.
+ AccessibilityEvent fifthExpected = getInteractionBridge()
+ .executeCommandAndWaitForAccessibilityEvent(new Runnable() {
+ @Override
+ public void run() {
+ getInteractionBridge().performAction(text,
+ AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY, arguments);
+ }
+ }, new AccessibilityEventFilter() {
+ @Override
+ public boolean accept(AccessibilityEvent event) {
+ return
+ (event.getEventType() ==
+ AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY
+ && event.getAction() ==
+ AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY
+ && event.getPackageName().equals(getActivity().getPackageName())
+ && event.getClassName().equals(EditText.class.getName())
+ && event.getText().size() > 0
+ && event.getText().get(0).toString().equals(getString(R.string.a_b))
+ && event.getFromIndex() == 2
+ && event.getToIndex() == 3
+ && event.getMovementGranularity() ==
+ AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER);
+ }
+ }, TIMEOUT_ASYNC_PROCESSING);
+
+ // Make sure we got the expected event.
+ assertNotNull(fifthExpected);
+
+ // Verify the selection position.
+ assertEquals(2, Selection.getSelectionStart(editText.getText()));
+ assertEquals(3, Selection.getSelectionEnd(editText.getText()));
+
+ // Move to the previous character and wait for an event.
+ AccessibilityEvent sixthExpected = getInteractionBridge()
+ .executeCommandAndWaitForAccessibilityEvent(new Runnable() {
+ @Override
+ public void run() {
+ getInteractionBridge().performAction(text,
+ AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY, arguments);
+ }
+ }, new AccessibilityEventFilter() {
+ @Override
+ public boolean accept(AccessibilityEvent event) {
+ return
+ (event.getEventType() ==
+ AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY
+ && event.getAction() ==
+ AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY
+ && event.getPackageName().equals(getActivity().getPackageName())
+ && event.getClassName().equals(EditText.class.getName())
+ && event.getText().size() > 0
+ && event.getText().get(0).toString().equals(getString(R.string.a_b))
+ && event.getFromIndex() == 1
+ && event.getToIndex() == 2
+ && event.getMovementGranularity() ==
+ AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER);
+ }
+ }, TIMEOUT_ASYNC_PROCESSING);
+
+ // Make sure we got the expected event.
+ assertNotNull(sixthExpected);
+
+ // Verify the selection position.
+ assertEquals(1, Selection.getSelectionStart(editText.getText()));
+ assertEquals(3, Selection.getSelectionEnd(editText.getText()));
+
+ // Move to the previous character and wait for an event.
+ AccessibilityEvent seventhExpected = getInteractionBridge()
+ .executeCommandAndWaitForAccessibilityEvent(new Runnable() {
+ @Override
+ public void run() {
+ getInteractionBridge().performAction(text,
+ AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY, arguments);
+ }
+ }, new AccessibilityEventFilter() {
+ @Override
+ public boolean accept(AccessibilityEvent event) {
+ return
+ (event.getEventType() ==
+ AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY
+ && event.getAction() ==
+ AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY
+ && event.getPackageName().equals(getActivity().getPackageName())
+ && event.getClassName().equals(EditText.class.getName())
+ && event.getText().size() > 0
+ && event.getText().get(0).toString().equals(getString(R.string.a_b))
+ && event.getFromIndex() == 0
+ && event.getToIndex() == 1
+ && event.getMovementGranularity() ==
+ AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER);
+ }
+ }, TIMEOUT_ASYNC_PROCESSING);
+
+ // Make sure we got the expected event.
+ assertNotNull(seventhExpected);
+
+ // Verify the selection position.
+ assertEquals(0, Selection.getSelectionStart(editText.getText()));
+ assertEquals(3, Selection.getSelectionEnd(editText.getText()));
+
+ // Make sure there is no previous.
+ assertFalse(getInteractionBridge().performAction(text,
+ AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY, arguments));
+
+ // Verify the selection position.
+ assertEquals(0, Selection.getSelectionStart(editText.getText()));
+ assertEquals(3, Selection.getSelectionEnd(editText.getText()));
+ }
+
+ @MediumTest
public void testActionNextAndPreviousAtGranularityWordOverText() throws Exception {
final TextView textView = (TextView) getActivity().findViewById(R.id.text);
@@ -696,7 +1039,7 @@
(event.getEventType() ==
AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY
&& event.getAction() ==
- AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY
+ AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY
&& event.getPackageName().equals(getActivity().getPackageName())
&& event.getClassName().equals(TextView.class.getName())
&& event.getText().size() > 0
@@ -704,7 +1047,7 @@
&& event.getFromIndex() == 0
&& event.getToIndex() == 3
&& event.getMovementGranularity() ==
- AccessibilityNodeInfo.MOVEMENT_GRANULARITY_WORD);
+ AccessibilityNodeInfo.MOVEMENT_GRANULARITY_WORD);
}
}, TIMEOUT_ASYNC_PROCESSING);
@@ -730,7 +1073,7 @@
(event.getEventType() ==
AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY
&& event.getAction() ==
- AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY
+ AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY
&& event.getPackageName().equals(getActivity().getPackageName())
&& event.getClassName().equals(TextView.class.getName())
&& event.getText().size() > 0
@@ -738,7 +1081,7 @@
&& event.getFromIndex() == 4
&& event.getToIndex() == 7
&& event.getMovementGranularity() ==
- AccessibilityNodeInfo.MOVEMENT_GRANULARITY_WORD);
+ AccessibilityNodeInfo.MOVEMENT_GRANULARITY_WORD);
}
}, TIMEOUT_ASYNC_PROCESSING);
@@ -764,7 +1107,7 @@
(event.getEventType() ==
AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY
&& event.getAction() ==
- AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY
+ AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY
&& event.getPackageName().equals(getActivity().getPackageName())
&& event.getClassName().equals(TextView.class.getName())
&& event.getText().size() > 0
@@ -772,7 +1115,7 @@
&& event.getFromIndex() == 8
&& event.getToIndex() == 11
&& event.getMovementGranularity() ==
- AccessibilityNodeInfo.MOVEMENT_GRANULARITY_WORD);
+ AccessibilityNodeInfo.MOVEMENT_GRANULARITY_WORD);
}
}, TIMEOUT_ASYNC_PROCESSING);
@@ -806,7 +1149,7 @@
(event.getEventType() ==
AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY
&& event.getAction() ==
- AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY
+ AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY
&& event.getPackageName().equals(getActivity().getPackageName())
&& event.getClassName().equals(TextView.class.getName())
&& event.getText().size() > 0
@@ -814,7 +1157,7 @@
&& event.getFromIndex() == 8
&& event.getToIndex() == 11
&& event.getMovementGranularity() ==
- AccessibilityNodeInfo.MOVEMENT_GRANULARITY_WORD);
+ AccessibilityNodeInfo.MOVEMENT_GRANULARITY_WORD);
}
}, TIMEOUT_ASYNC_PROCESSING);
@@ -840,7 +1183,7 @@
(event.getEventType() ==
AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY
&& event.getAction() ==
- AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY
+ AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY
&& event.getPackageName().equals(getActivity().getPackageName())
&& event.getClassName().equals(TextView.class.getName())
&& event.getText().size() > 0
@@ -848,7 +1191,7 @@
&& event.getFromIndex() == 4
&& event.getToIndex() == 7
&& event.getMovementGranularity() ==
- AccessibilityNodeInfo.MOVEMENT_GRANULARITY_WORD);
+ AccessibilityNodeInfo.MOVEMENT_GRANULARITY_WORD);
}
}, TIMEOUT_ASYNC_PROCESSING);
@@ -874,7 +1217,7 @@
(event.getEventType() ==
AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY
&& event.getAction() ==
- AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY
+ AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY
&& event.getPackageName().equals(getActivity().getPackageName())
&& event.getClassName().equals(TextView.class.getName())
&& event.getText().size() > 0
@@ -882,7 +1225,7 @@
&& event.getFromIndex() == 0
&& event.getToIndex() == 3
&& event.getMovementGranularity() ==
- AccessibilityNodeInfo.MOVEMENT_GRANULARITY_WORD);
+ AccessibilityNodeInfo.MOVEMENT_GRANULARITY_WORD);
}
}, TIMEOUT_ASYNC_PROCESSING);
@@ -903,6 +1246,279 @@
}
@MediumTest
+ public void testActionNextAndPreviousAtGranularityWordOverTextExtend() throws Exception {
+ final EditText editText = (EditText) getActivity().findViewById(R.id.edit);
+
+ getInstrumentation().runOnMainSync(new Runnable() {
+ @Override
+ public void run() {
+ editText.setText(getString(R.string.foo_bar_baz));
+ Selection.removeSelection(editText.getText());
+ }
+ });
+
+ final AccessibilityNodeInfo text = getInteractionBridge()
+ .findAccessibilityNodeInfoByTextFromRoot(getString(R.string.foo_bar_baz));
+
+ final int granularities = text.getMovementGranularities();
+ assertEquals(granularities, AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER
+ | AccessibilityNodeInfo.MOVEMENT_GRANULARITY_WORD
+ | AccessibilityNodeInfo.MOVEMENT_GRANULARITY_LINE
+ | AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PARAGRAPH
+ | AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PAGE);
+
+ final Bundle arguments = new Bundle();
+ arguments.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT,
+ AccessibilityNodeInfo.MOVEMENT_GRANULARITY_WORD);
+ arguments.putBoolean(AccessibilityNodeInfo.ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN, true);
+
+ // Move to the next word and wait for an event.
+ AccessibilityEvent firstExpected = getInteractionBridge()
+ .executeCommandAndWaitForAccessibilityEvent(new Runnable() {
+ @Override
+ public void run() {
+ getInteractionBridge().performAction(text,
+ AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY, arguments);
+ }
+ }, new AccessibilityEventFilter() {
+ @Override
+ public boolean accept(AccessibilityEvent event) {
+ return
+ (event.getEventType() ==
+ AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY
+ && event.getAction() ==
+ AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY
+ && event.getPackageName().equals(getActivity().getPackageName())
+ && event.getClassName().equals(EditText.class.getName())
+ && event.getText().size() > 0
+ && event.getText().get(0).toString().equals(getString(R.string.foo_bar_baz))
+ && event.getFromIndex() == 0
+ && event.getToIndex() == 3
+ && event.getMovementGranularity() ==
+ AccessibilityNodeInfo.MOVEMENT_GRANULARITY_WORD);
+ }
+ }, TIMEOUT_ASYNC_PROCESSING);
+
+ // Make sure we got the expected event.
+ assertNotNull(firstExpected);
+
+ // Verify the selection position.
+ assertEquals(0, Selection.getSelectionStart(editText.getText()));
+ assertEquals(3, Selection.getSelectionEnd(editText.getText()));
+
+ // Move to the next word and wait for an event.
+ AccessibilityEvent secondExpected = getInteractionBridge()
+ .executeCommandAndWaitForAccessibilityEvent(new Runnable() {
+ @Override
+ public void run() {
+ getInteractionBridge().performAction(text,
+ AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY, arguments);
+ }
+ }, new AccessibilityEventFilter() {
+ @Override
+ public boolean accept(AccessibilityEvent event) {
+ return
+ (event.getEventType() ==
+ AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY
+ && event.getAction() ==
+ AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY
+ && event.getPackageName().equals(getActivity().getPackageName())
+ && event.getClassName().equals(EditText.class.getName())
+ && event.getText().size() > 0
+ && event.getText().get(0).toString().equals(getString(R.string.foo_bar_baz))
+ && event.getFromIndex() == 4
+ && event.getToIndex() == 7
+ && event.getMovementGranularity() ==
+ AccessibilityNodeInfo.MOVEMENT_GRANULARITY_WORD);
+ }
+ }, TIMEOUT_ASYNC_PROCESSING);
+
+ // Make sure we got the expected event.
+ assertNotNull(secondExpected);
+
+ // Verify the selection position.
+ assertEquals(0, Selection.getSelectionStart(editText.getText()));
+ assertEquals(7, Selection.getSelectionEnd(editText.getText()));
+
+ // Move to the next word and wait for an event.
+ AccessibilityEvent thirdExpected = getInteractionBridge()
+ .executeCommandAndWaitForAccessibilityEvent(new Runnable() {
+ @Override
+ public void run() {
+ getInteractionBridge().performAction(text,
+ AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY, arguments);
+ }
+ }, new AccessibilityEventFilter() {
+ @Override
+ public boolean accept(AccessibilityEvent event) {
+ return
+ (event.getEventType() ==
+ AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY
+ && event.getAction() ==
+ AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY
+ && event.getPackageName().equals(getActivity().getPackageName())
+ && event.getClassName().equals(EditText.class.getName())
+ && event.getText().size() > 0
+ && event.getText().get(0).toString().equals(getString(R.string.foo_bar_baz))
+ && event.getFromIndex() == 8
+ && event.getToIndex() == 11
+ && event.getMovementGranularity() ==
+ AccessibilityNodeInfo.MOVEMENT_GRANULARITY_WORD);
+ }
+ }, TIMEOUT_ASYNC_PROCESSING);
+
+ // Make sure we got the expected event.
+ assertNotNull(thirdExpected);
+
+ // Verify the selection position.
+ assertEquals(0, Selection.getSelectionStart(editText.getText()));
+ assertEquals(11, Selection.getSelectionEnd(editText.getText()));
+
+ // Make sure there is no next.
+ assertFalse(getInteractionBridge().performAction(text,
+ AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY, arguments));
+
+ // Focus the view so we can change selection.
+ getInstrumentation().runOnMainSync(new Runnable() {
+ @Override
+ public void run() {
+ editText.setFocusable(true);
+ editText.requestFocus();
+ }
+ });
+
+ // Put selection at the end of the text.
+ Bundle setSelectionArgs = new Bundle();
+ setSelectionArgs.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_START_INT, 11);
+ setSelectionArgs.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_END_INT, 11);
+ assertTrue(getInteractionBridge().performAction(text,
+ AccessibilityNodeInfo.ACTION_SET_SELECTION, setSelectionArgs));
+
+ // Verify the selection position.
+ assertEquals(11, Selection.getSelectionStart(editText.getText()));
+ assertEquals(11, Selection.getSelectionEnd(editText.getText()));
+
+ // Unfocus the view so we can get rid of the soft-keyboard.
+ getInstrumentation().runOnMainSync(new Runnable() {
+ @Override
+ public void run() {
+ editText.clearFocus();
+ editText.setFocusable(false);
+ }
+ });
+
+ // Move to the next word and wait for an event.
+ AccessibilityEvent fourthExpected = getInteractionBridge()
+ .executeCommandAndWaitForAccessibilityEvent(new Runnable() {
+ @Override
+ public void run() {
+ getInteractionBridge().performAction(text,
+ AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY, arguments);
+ }
+ }, new AccessibilityEventFilter() {
+ @Override
+ public boolean accept(AccessibilityEvent event) {
+ return
+ (event.getEventType() ==
+ AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY
+ && event.getAction() ==
+ AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY
+ && event.getPackageName().equals(getActivity().getPackageName())
+ && event.getClassName().equals(EditText.class.getName())
+ && event.getText().size() > 0
+ && event.getText().get(0).toString().equals(getString(R.string.foo_bar_baz))
+ && event.getFromIndex() == 8
+ && event.getToIndex() == 11
+ && event.getMovementGranularity() ==
+ AccessibilityNodeInfo.MOVEMENT_GRANULARITY_WORD);
+ }
+ }, TIMEOUT_ASYNC_PROCESSING);
+
+ // Make sure we got the expected event.
+ assertNotNull(fourthExpected);
+
+ // Verify the selection position.
+ assertEquals(8, Selection.getSelectionStart(editText.getText()));
+ assertEquals(11, Selection.getSelectionEnd(editText.getText()));
+
+ // Move to the next word and wait for an event.
+ AccessibilityEvent fifthExpected = getInteractionBridge()
+ .executeCommandAndWaitForAccessibilityEvent(new Runnable() {
+ @Override
+ public void run() {
+ getInteractionBridge().performAction(text,
+ AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY, arguments);
+ }
+ }, new AccessibilityEventFilter() {
+ @Override
+ public boolean accept(AccessibilityEvent event) {
+ return
+ (event.getEventType() ==
+ AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY
+ && event.getAction() ==
+ AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY
+ && event.getPackageName().equals(getActivity().getPackageName())
+ && event.getClassName().equals(EditText.class.getName())
+ && event.getText().size() > 0
+ && event.getText().get(0).toString().equals(getString(R.string.foo_bar_baz))
+ && event.getFromIndex() == 4
+ && event.getToIndex() == 7
+ && event.getMovementGranularity() ==
+ AccessibilityNodeInfo.MOVEMENT_GRANULARITY_WORD);
+ }
+ }, TIMEOUT_ASYNC_PROCESSING);
+
+ // Make sure we got the expected event.
+ assertNotNull(fifthExpected);
+
+ // Verify the selection position.
+ assertEquals(4, Selection.getSelectionStart(editText.getText()));
+ assertEquals(11, Selection.getSelectionEnd(editText.getText()));
+
+ // Move to the next character and wait for an event.
+ AccessibilityEvent sixthExpected = getInteractionBridge()
+ .executeCommandAndWaitForAccessibilityEvent(new Runnable() {
+ @Override
+ public void run() {
+ getInteractionBridge().performAction(text,
+ AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY, arguments);
+ }
+ }, new AccessibilityEventFilter() {
+ @Override
+ public boolean accept(AccessibilityEvent event) {
+ return
+ (event.getEventType() ==
+ AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY
+ && event.getAction() ==
+ AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY
+ && event.getPackageName().equals(getActivity().getPackageName())
+ && event.getClassName().equals(EditText.class.getName())
+ && event.getText().size() > 0
+ && event.getText().get(0).toString().equals(getString(R.string.foo_bar_baz))
+ && event.getFromIndex() == 0
+ && event.getToIndex() == 3
+ && event.getMovementGranularity() ==
+ AccessibilityNodeInfo.MOVEMENT_GRANULARITY_WORD);
+ }
+ }, TIMEOUT_ASYNC_PROCESSING);
+
+ // Make sure we got the expected event.
+ assertNotNull(sixthExpected);
+
+ // Verify the selection position.
+ assertEquals(0, Selection.getSelectionStart(editText.getText()));
+ assertEquals(11, Selection.getSelectionEnd(editText.getText()));
+
+ // Make sure there is no previous.
+ assertFalse(getInteractionBridge().performAction(text,
+ AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY, arguments));
+
+ // Verify the selection position.
+ assertEquals(0, Selection.getSelectionStart(editText.getText()));
+ assertEquals(11, Selection.getSelectionEnd(editText.getText()));
+ }
+
+ @MediumTest
public void testActionNextAndPreviousAtGranularityLineOverText() throws Exception {
final TextView textView = (TextView) getActivity().findViewById(R.id.text);
@@ -942,7 +1558,7 @@
(event.getEventType() ==
AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY
&& event.getAction() ==
- AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY
+ AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY
&& event.getPackageName().equals(getActivity().getPackageName())
&& event.getClassName().equals(TextView.class.getName())
&& event.getText().size() > 0
@@ -951,7 +1567,7 @@
&& event.getFromIndex() == 0
&& event.getToIndex() == 25
&& event.getMovementGranularity() ==
- AccessibilityNodeInfo.MOVEMENT_GRANULARITY_LINE);
+ AccessibilityNodeInfo.MOVEMENT_GRANULARITY_LINE);
}
}, TIMEOUT_ASYNC_PROCESSING);
@@ -977,7 +1593,7 @@
(event.getEventType() ==
AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY
&& event.getAction() ==
- AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY
+ AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY
&& event.getPackageName().equals(getActivity().getPackageName())
&& event.getClassName().equals(TextView.class.getName())
&& event.getText().size() > 0
@@ -986,7 +1602,7 @@
&& event.getFromIndex() == 25
&& event.getToIndex() == 53
&& event.getMovementGranularity() ==
- AccessibilityNodeInfo.MOVEMENT_GRANULARITY_LINE);
+ AccessibilityNodeInfo.MOVEMENT_GRANULARITY_LINE);
}
}, TIMEOUT_ASYNC_PROCESSING);
@@ -1012,7 +1628,7 @@
(event.getEventType() ==
AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY
&& event.getAction() ==
- AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY
+ AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY
&& event.getPackageName().equals(getActivity().getPackageName())
&& event.getClassName().equals(TextView.class.getName())
&& event.getText().size() > 0
@@ -1021,7 +1637,7 @@
&& event.getFromIndex() == 53
&& event.getToIndex() == 60
&& event.getMovementGranularity() ==
- AccessibilityNodeInfo.MOVEMENT_GRANULARITY_LINE);
+ AccessibilityNodeInfo.MOVEMENT_GRANULARITY_LINE);
}
}, TIMEOUT_ASYNC_PROCESSING);
@@ -1055,7 +1671,7 @@
(event.getEventType() ==
AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY
&& event.getAction() ==
- AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY
+ AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY
&& event.getPackageName().equals(getActivity().getPackageName())
&& event.getClassName().equals(TextView.class.getName())
&& event.getText().size() > 0
@@ -1064,7 +1680,7 @@
&& event.getFromIndex() == 53
&& event.getToIndex() == 60
&& event.getMovementGranularity() ==
- AccessibilityNodeInfo.MOVEMENT_GRANULARITY_LINE);
+ AccessibilityNodeInfo.MOVEMENT_GRANULARITY_LINE);
}
}, TIMEOUT_ASYNC_PROCESSING);
@@ -1090,7 +1706,7 @@
(event.getEventType() ==
AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY
&& event.getAction() ==
- AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY
+ AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY
&& event.getPackageName().equals(getActivity().getPackageName())
&& event.getClassName().equals(TextView.class.getName())
&& event.getText().size() > 0
@@ -1099,7 +1715,7 @@
&& event.getFromIndex() == 25
&& event.getToIndex() == 53
&& event.getMovementGranularity() ==
- AccessibilityNodeInfo.MOVEMENT_GRANULARITY_LINE);
+ AccessibilityNodeInfo.MOVEMENT_GRANULARITY_LINE);
}
}, TIMEOUT_ASYNC_PROCESSING);
@@ -1125,7 +1741,7 @@
(event.getEventType() ==
AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY
&& event.getAction() ==
- AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY
+ AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY
&& event.getPackageName().equals(getActivity().getPackageName())
&& event.getClassName().equals(TextView.class.getName())
&& event.getText().size() > 0
@@ -1134,7 +1750,7 @@
&& event.getFromIndex() == 0
&& event.getToIndex() == 25
&& event.getMovementGranularity() ==
- AccessibilityNodeInfo.MOVEMENT_GRANULARITY_LINE);
+ AccessibilityNodeInfo.MOVEMENT_GRANULARITY_LINE);
}
}, TIMEOUT_ASYNC_PROCESSING);
@@ -1155,6 +1771,281 @@
}
@MediumTest
+ public void testActionNextAndPreviousAtGranularityLineOverTextExtend() throws Exception {
+ final EditText editText = (EditText) getActivity().findViewById(R.id.edit);
+
+ getInstrumentation().runOnMainSync(new Runnable() {
+ @Override
+ public void run() {
+ editText.setText(getString(R.string.android_wiki_short));
+ Selection.removeSelection(editText.getText());
+ }
+ });
+
+ final AccessibilityNodeInfo text = getInteractionBridge()
+ .findAccessibilityNodeInfoByTextFromRoot(getString(R.string.android_wiki_short));
+
+ final int granularities = text.getMovementGranularities();
+ assertEquals(granularities, AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER
+ | AccessibilityNodeInfo.MOVEMENT_GRANULARITY_WORD
+ | AccessibilityNodeInfo.MOVEMENT_GRANULARITY_LINE
+ | AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PARAGRAPH
+ | AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PAGE);
+
+ final Bundle arguments = new Bundle();
+ arguments.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT,
+ AccessibilityNodeInfo.MOVEMENT_GRANULARITY_LINE);
+ arguments.putBoolean(AccessibilityNodeInfo.ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN, true);
+
+ // Move to the next line and wait for an event.
+ AccessibilityEvent firstExpected = getInteractionBridge()
+ .executeCommandAndWaitForAccessibilityEvent(new Runnable() {
+ @Override
+ public void run() {
+ getInteractionBridge().performAction(text,
+ AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY, arguments);
+ }
+ }, new AccessibilityEventFilter() {
+ @Override
+ public boolean accept(AccessibilityEvent event) {
+ return
+ (event.getEventType() ==
+ AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY
+ && event.getAction() ==
+ AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY
+ && event.getPackageName().equals(getActivity().getPackageName())
+ && event.getClassName().equals(EditText.class.getName())
+ && event.getText().size() > 0
+ && event.getText().get(0).toString().equals(getString(
+ R.string.android_wiki_short))
+ && event.getFromIndex() == 0
+ && event.getToIndex() == 19
+ && event.getMovementGranularity() ==
+ AccessibilityNodeInfo.MOVEMENT_GRANULARITY_LINE);
+ }
+ }, TIMEOUT_ASYNC_PROCESSING);
+
+ // Make sure we got the expected event.
+ assertNotNull(firstExpected);
+
+ // Verify the selection position.
+ assertEquals(0, Selection.getSelectionStart(editText.getText()));
+ assertEquals(19, Selection.getSelectionEnd(editText.getText()));
+
+ // Move to the next line and wait for an event.
+ AccessibilityEvent secondExpected = getInteractionBridge()
+ .executeCommandAndWaitForAccessibilityEvent(new Runnable() {
+ @Override
+ public void run() {
+ getInteractionBridge().performAction(text,
+ AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY, arguments);
+ }
+ }, new AccessibilityEventFilter() {
+ @Override
+ public boolean accept(AccessibilityEvent event) {
+ return
+ (event.getEventType() ==
+ AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY
+ && event.getAction() ==
+ AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY
+ && event.getPackageName().equals(getActivity().getPackageName())
+ && event.getClassName().equals(EditText.class.getName())
+ && event.getText().size() > 0
+ && event.getText().get(0).toString().equals(getString(
+ R.string.android_wiki_short))
+ && event.getFromIndex() == 19
+ && event.getToIndex() == 35
+ && event.getMovementGranularity() ==
+ AccessibilityNodeInfo.MOVEMENT_GRANULARITY_LINE);
+ }
+ }, TIMEOUT_ASYNC_PROCESSING);
+
+ // Make sure we got the expected event.
+ assertNotNull(secondExpected);
+
+ // Verify the selection position.
+ assertEquals(0, Selection.getSelectionStart(editText.getText()));
+ assertEquals(35, Selection.getSelectionEnd(editText.getText()));
+
+ // Move to the next line and wait for an event.
+ AccessibilityEvent thirdExpected = getInteractionBridge()
+ .executeCommandAndWaitForAccessibilityEvent(new Runnable() {
+ @Override
+ public void run() {
+ getInteractionBridge().performAction(text,
+ AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY, arguments);
+ }
+ }, new AccessibilityEventFilter() {
+ @Override
+ public boolean accept(AccessibilityEvent event) {
+ return
+ (event.getEventType() ==
+ AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY
+ && event.getAction() ==
+ AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY
+ && event.getPackageName().equals(getActivity().getPackageName())
+ && event.getClassName().equals(EditText.class.getName())
+ && event.getText().size() > 0
+ && event.getText().get(0).toString().equals(getString(
+ R.string.android_wiki_short))
+ && event.getFromIndex() == 35
+ && event.getToIndex() == 53
+ && event.getMovementGranularity() ==
+ AccessibilityNodeInfo.MOVEMENT_GRANULARITY_LINE);
+ }
+ }, TIMEOUT_ASYNC_PROCESSING);
+
+ // Make sure we got the expected event.
+ assertNotNull(thirdExpected);
+
+ // Verify the selection position.
+ assertEquals(0, Selection.getSelectionStart(editText.getText()));
+ assertEquals(53, Selection.getSelectionEnd(editText.getText()));
+
+ // Focus the view so we can change selection.
+ getInstrumentation().runOnMainSync(new Runnable() {
+ @Override
+ public void run() {
+ editText.setFocusable(true);
+ editText.requestFocus();
+ }
+ });
+
+ // Put selection at the end of the text.
+ Bundle setSelectionArgs = new Bundle();
+ setSelectionArgs.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_START_INT, 53);
+ setSelectionArgs.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_END_INT, 53);
+ assertTrue(getInteractionBridge().performAction(text,
+ AccessibilityNodeInfo.ACTION_SET_SELECTION, setSelectionArgs));
+
+ // Verify the selection position.
+ assertEquals(53, Selection.getSelectionStart(editText.getText()));
+ assertEquals(53, Selection.getSelectionEnd(editText.getText()));
+
+ // Unocus the view so we can hide the keyboard.
+ getInstrumentation().runOnMainSync(new Runnable() {
+ @Override
+ public void run() {
+ editText.clearFocus();
+ editText.setFocusable(false);
+ }
+ });
+
+ // Move to the previous line and wait for an event.
+ AccessibilityEvent fourthExpected = getInteractionBridge()
+ .executeCommandAndWaitForAccessibilityEvent(new Runnable() {
+ @Override
+ public void run() {
+ getInteractionBridge().performAction(text,
+ AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY, arguments);
+ }
+ }, new AccessibilityEventFilter() {
+ @Override
+ public boolean accept(AccessibilityEvent event) {
+ return
+ (event.getEventType() ==
+ AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY
+ && event.getAction() ==
+ AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY
+ && event.getPackageName().equals(getActivity().getPackageName())
+ && event.getClassName().equals(EditText.class.getName())
+ && event.getText().size() > 0
+ && event.getText().get(0).toString().equals(getString(
+ R.string.android_wiki_short))
+ && event.getFromIndex() == 35
+ && event.getToIndex() == 53
+ && event.getMovementGranularity() ==
+ AccessibilityNodeInfo.MOVEMENT_GRANULARITY_LINE);
+ }
+ }, TIMEOUT_ASYNC_PROCESSING);
+
+ // Make sure we got the expected event.
+ assertNotNull(fourthExpected);
+
+ // Verify the selection position.
+ assertEquals(35, Selection.getSelectionStart(editText.getText()));
+ assertEquals(53, Selection.getSelectionEnd(editText.getText()));
+
+ // Move to the previous line and wait for an event.
+ AccessibilityEvent fifthExpected = getInteractionBridge()
+ .executeCommandAndWaitForAccessibilityEvent(new Runnable() {
+ @Override
+ public void run() {
+ getInteractionBridge().performAction(text,
+ AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY, arguments);
+ }
+ }, new AccessibilityEventFilter() {
+ @Override
+ public boolean accept(AccessibilityEvent event) {
+ return
+ (event.getEventType() ==
+ AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY
+ && event.getAction() ==
+ AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY
+ && event.getPackageName().equals(getActivity().getPackageName())
+ && event.getClassName().equals(EditText.class.getName())
+ && event.getText().size() > 0
+ && event.getText().get(0).toString().equals(getString(
+ R.string.android_wiki_short))
+ && event.getFromIndex() == 19
+ && event.getToIndex() == 35
+ && event.getMovementGranularity() ==
+ AccessibilityNodeInfo.MOVEMENT_GRANULARITY_LINE);
+ }
+ }, TIMEOUT_ASYNC_PROCESSING);
+
+ // Make sure we got the expected event.
+ assertNotNull(fifthExpected);
+
+ // Verify the selection position.
+ assertEquals(19, Selection.getSelectionStart(editText.getText()));
+ assertEquals(53, Selection.getSelectionEnd(editText.getText()));
+
+ // Move to the previous line and wait for an event.
+ AccessibilityEvent sixthExpected = getInteractionBridge()
+ .executeCommandAndWaitForAccessibilityEvent(new Runnable() {
+ @Override
+ public void run() {
+ getInteractionBridge().performAction(text,
+ AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY, arguments);
+ }
+ }, new AccessibilityEventFilter() {
+ @Override
+ public boolean accept(AccessibilityEvent event) {
+ return
+ (event.getEventType() ==
+ AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY
+ && event.getAction() ==
+ AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY
+ && event.getPackageName().equals(getActivity().getPackageName())
+ && event.getClassName().equals(EditText.class.getName())
+ && event.getText().size() > 0
+ && event.getText().get(0).toString().equals(getString(
+ R.string.android_wiki_short))
+ && event.getFromIndex() == 0
+ && event.getToIndex() == 19
+ && event.getMovementGranularity() ==
+ AccessibilityNodeInfo.MOVEMENT_GRANULARITY_LINE);
+ }
+ }, TIMEOUT_ASYNC_PROCESSING);
+
+ // Make sure we got the expected event.
+ assertNotNull(sixthExpected);
+
+ // Verify the selection position.
+ assertEquals(0, Selection.getSelectionStart(editText.getText()));
+ assertEquals(53, Selection.getSelectionEnd(editText.getText()));
+
+ // Make sure there is no previous.
+ assertFalse(getInteractionBridge().performAction(text,
+ AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY, arguments));
+
+ // Verify the selection position.
+ assertEquals(0, Selection.getSelectionStart(editText.getText()));
+ assertEquals(53, Selection.getSelectionEnd(editText.getText()));
+ }
+
+ @MediumTest
public void testActionNextAndPreviousAtGranularityPageOverText() throws Exception {
final EditText editText = (EditText) getActivity().findViewById(R.id.edit);
@@ -1195,7 +2086,7 @@
(event.getEventType() ==
AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY
&& event.getAction() ==
- AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY
+ AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY
&& event.getPackageName().equals(getActivity().getPackageName())
&& event.getClassName().equals(EditText.class.getName())
&& event.getText().size() > 0
@@ -1204,7 +2095,7 @@
&& event.getFromIndex() == 0
&& event.getToIndex() == 139
&& event.getMovementGranularity() ==
- AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PAGE);
+ AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PAGE);
}
}, TIMEOUT_ASYNC_PROCESSING);
@@ -1230,7 +2121,7 @@
(event.getEventType() ==
AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY
&& event.getAction() ==
- AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY
+ AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY
&& event.getPackageName().equals(getActivity().getPackageName())
&& event.getClassName().equals(EditText.class.getName())
&& event.getText().size() > 0
@@ -1239,7 +2130,7 @@
&& event.getFromIndex() == 139
&& event.getToIndex() == 285
&& event.getMovementGranularity() ==
- AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PAGE);
+ AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PAGE);
}
}, TIMEOUT_ASYNC_PROCESSING);
@@ -1265,7 +2156,7 @@
(event.getEventType() ==
AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY
&& event.getAction() ==
- AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY
+ AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY
&& event.getPackageName().equals(getActivity().getPackageName())
&& event.getClassName().equals(EditText.class.getName())
&& event.getText().size() > 0
@@ -1274,7 +2165,7 @@
&& event.getFromIndex() == 285
&& event.getToIndex() == 436
&& event.getMovementGranularity() ==
- AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PAGE);
+ AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PAGE);
}
}, TIMEOUT_ASYNC_PROCESSING);
@@ -1308,7 +2199,7 @@
(event.getEventType() ==
AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY
&& event.getAction() ==
- AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY
+ AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY
&& event.getPackageName().equals(getActivity().getPackageName())
&& event.getClassName().equals(EditText.class.getName())
&& event.getText().size() > 0
@@ -1317,7 +2208,7 @@
&& event.getFromIndex() == 285
&& event.getToIndex() == 436
&& event.getMovementGranularity() ==
- AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PAGE);
+ AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PAGE);
}
}, TIMEOUT_ASYNC_PROCESSING);
@@ -1343,7 +2234,7 @@
(event.getEventType() ==
AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY
&& event.getAction() ==
- AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY
+ AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY
&& event.getPackageName().equals(getActivity().getPackageName())
&& event.getClassName().equals(EditText.class.getName())
&& event.getText().size() > 0
@@ -1352,7 +2243,7 @@
&& event.getFromIndex() == 139
&& event.getToIndex() == 285
&& event.getMovementGranularity() ==
- AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PAGE);
+ AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PAGE);
}
}, TIMEOUT_ASYNC_PROCESSING);
@@ -1378,7 +2269,7 @@
(event.getEventType() ==
AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY
&& event.getAction() ==
- AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY
+ AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY
&& event.getPackageName().equals(getActivity().getPackageName())
&& event.getClassName().equals(EditText.class.getName())
&& event.getText().size() > 0
@@ -1387,7 +2278,7 @@
&& event.getFromIndex() == 0
&& event.getToIndex() == 139
&& event.getMovementGranularity() ==
- AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PAGE);
+ AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PAGE);
}
}, TIMEOUT_ASYNC_PROCESSING);
@@ -1408,6 +2299,285 @@
}
@MediumTest
+ public void testActionNextAndPreviousAtGranularityPageOverTextExtend() throws Exception {
+ final EditText editText = (EditText) getActivity().findViewById(R.id.edit);
+
+ getInstrumentation().runOnMainSync(new Runnable() {
+ @Override
+ public void run() {
+ editText.setText(getString(R.string.android_wiki));
+ Selection.removeSelection(editText.getText());
+ }
+ });
+
+ final AccessibilityNodeInfo text = getInteractionBridge()
+ .findAccessibilityNodeInfoByTextFromRoot(getString(R.string.android_wiki));
+
+ final int granularities = text.getMovementGranularities();
+ assertEquals(granularities, AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER
+ | AccessibilityNodeInfo.MOVEMENT_GRANULARITY_WORD
+ | AccessibilityNodeInfo.MOVEMENT_GRANULARITY_LINE
+ | AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PARAGRAPH
+ | AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PAGE);
+
+ final Bundle arguments = new Bundle();
+ arguments.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT,
+ AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PAGE);
+ arguments.putBoolean(AccessibilityNodeInfo.ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN, true);
+
+ // Move to the next page and wait for an event.
+ AccessibilityEvent firstExpected = getInteractionBridge()
+ .executeCommandAndWaitForAccessibilityEvent(new Runnable() {
+ @Override
+ public void run() {
+ getInteractionBridge().performAction(text,
+ AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY, arguments);
+ }
+ }, new AccessibilityEventFilter() {
+ @Override
+ public boolean accept(AccessibilityEvent event) {
+ return
+ (event.getEventType() ==
+ AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY
+ && event.getAction() ==
+ AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY
+ && event.getPackageName().equals(getActivity().getPackageName())
+ && event.getClassName().equals(EditText.class.getName())
+ && event.getText().size() > 0
+ && event.getText().get(0).toString().equals(getString(
+ R.string.android_wiki))
+ && event.getFromIndex() == 0
+ && event.getToIndex() == 139
+ && event.getMovementGranularity() ==
+ AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PAGE);
+ }
+ }, TIMEOUT_ASYNC_PROCESSING);
+
+ // Make sure we got the expected event.
+ assertNotNull(firstExpected);
+
+ // Verify the selection position.
+ assertEquals(0, Selection.getSelectionStart(editText.getText()));
+ assertEquals(139, Selection.getSelectionEnd(editText.getText()));
+
+ // Move to the next page and wait for an event.
+ AccessibilityEvent secondExpected = getInteractionBridge()
+ .executeCommandAndWaitForAccessibilityEvent(new Runnable() {
+ @Override
+ public void run() {
+ getInteractionBridge().performAction(text,
+ AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY, arguments);
+ }
+ }, new AccessibilityEventFilter() {
+ @Override
+ public boolean accept(AccessibilityEvent event) {
+ return
+ (event.getEventType() ==
+ AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY
+ && event.getAction() ==
+ AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY
+ && event.getPackageName().equals(getActivity().getPackageName())
+ && event.getClassName().equals(EditText.class.getName())
+ && event.getText().size() > 0
+ && event.getText().get(0).toString().equals(getString(
+ R.string.android_wiki))
+ && event.getFromIndex() == 139
+ && event.getToIndex() == 285
+ && event.getMovementGranularity() ==
+ AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PAGE);
+ }
+ }, TIMEOUT_ASYNC_PROCESSING);
+
+ // Make sure we got the expected event.
+ assertNotNull(secondExpected);
+
+ // Verify the selection position.
+ assertEquals(0, Selection.getSelectionStart(editText.getText()));
+ assertEquals(285, Selection.getSelectionEnd(editText.getText()));
+
+ // Move to the next page and wait for an event.
+ AccessibilityEvent thirdExpected = getInteractionBridge()
+ .executeCommandAndWaitForAccessibilityEvent(new Runnable() {
+ @Override
+ public void run() {
+ getInteractionBridge().performAction(text,
+ AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY, arguments);
+ }
+ }, new AccessibilityEventFilter() {
+ @Override
+ public boolean accept(AccessibilityEvent event) {
+ return
+ (event.getEventType() ==
+ AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY
+ && event.getAction() ==
+ AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY
+ && event.getPackageName().equals(getActivity().getPackageName())
+ && event.getClassName().equals(EditText.class.getName())
+ && event.getText().size() > 0
+ && event.getText().get(0).toString().equals(getString(
+ R.string.android_wiki))
+ && event.getFromIndex() == 285
+ && event.getToIndex() == 436
+ && event.getMovementGranularity() ==
+ AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PAGE);
+ }
+ }, TIMEOUT_ASYNC_PROCESSING);
+
+ // Make sure we got the expected event.
+ assertNotNull(thirdExpected);
+
+ // Verify the selection position.
+ assertEquals(0, Selection.getSelectionStart(editText.getText()));
+ assertEquals(436, Selection.getSelectionEnd(editText.getText()));
+
+ // Make sure there is no next.
+ assertFalse(getInteractionBridge().performAction(text,
+ AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY, arguments));
+
+ // Focus the view so we can change selection.
+ getInstrumentation().runOnMainSync(new Runnable() {
+ @Override
+ public void run() {
+ editText.setFocusable(true);
+ editText.requestFocus();
+ }
+ });
+
+ // Put selection at the end of the text.
+ Bundle setSelectionArgs = new Bundle();
+ setSelectionArgs.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_START_INT, 436);
+ setSelectionArgs.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_END_INT, 436);
+ assertTrue(getInteractionBridge().performAction(text,
+ AccessibilityNodeInfo.ACTION_SET_SELECTION, setSelectionArgs));
+
+ // Verify the selection position.
+ assertEquals(436, Selection.getSelectionStart(editText.getText()));
+ assertEquals(436, Selection.getSelectionEnd(editText.getText()));
+
+ // Unfocus the view so we can hide the soft-keyboard.
+ getInstrumentation().runOnMainSync(new Runnable() {
+ @Override
+ public void run() {
+ editText.clearFocus();
+ editText.setFocusable(false);
+ }
+ });
+
+ // Move to the previous page and wait for an event.
+ AccessibilityEvent fourthExpected = getInteractionBridge()
+ .executeCommandAndWaitForAccessibilityEvent(new Runnable() {
+ @Override
+ public void run() {
+ getInteractionBridge().performAction(text,
+ AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY, arguments);
+ }
+ }, new AccessibilityEventFilter() {
+ @Override
+ public boolean accept(AccessibilityEvent event) {
+ return
+ (event.getEventType() ==
+ AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY
+ && event.getAction() ==
+ AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY
+ && event.getPackageName().equals(getActivity().getPackageName())
+ && event.getClassName().equals(EditText.class.getName())
+ && event.getText().size() > 0
+ && event.getText().get(0).toString().equals(getString(
+ R.string.android_wiki))
+ && event.getFromIndex() == 285
+ && event.getToIndex() == 436
+ && event.getMovementGranularity() ==
+ AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PAGE);
+ }
+ }, TIMEOUT_ASYNC_PROCESSING);
+
+ // Make sure we got the expected event.
+ assertNotNull(fourthExpected);
+
+ // Verify the selection position.
+ assertEquals(285, Selection.getSelectionStart(editText.getText()));
+ assertEquals(436, Selection.getSelectionEnd(editText.getText()));
+
+ // Move to the previous page and wait for an event.
+ AccessibilityEvent fifthExpected = getInteractionBridge()
+ .executeCommandAndWaitForAccessibilityEvent(new Runnable() {
+ @Override
+ public void run() {
+ getInteractionBridge().performAction(text,
+ AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY, arguments);
+ }
+ }, new AccessibilityEventFilter() {
+ @Override
+ public boolean accept(AccessibilityEvent event) {
+ return
+ (event.getEventType() ==
+ AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY
+ && event.getAction() ==
+ AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY
+ && event.getPackageName().equals(getActivity().getPackageName())
+ && event.getClassName().equals(EditText.class.getName())
+ && event.getText().size() > 0
+ && event.getText().get(0).toString().equals(getString(
+ R.string.android_wiki))
+ && event.getFromIndex() == 139
+ && event.getToIndex() == 285
+ && event.getMovementGranularity() ==
+ AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PAGE);
+ }
+ }, TIMEOUT_ASYNC_PROCESSING);
+
+ // Make sure we got the expected event.
+ assertNotNull(fifthExpected);
+
+ // Verify the selection position.
+ assertEquals(139, Selection.getSelectionStart(editText.getText()));
+ assertEquals(436, Selection.getSelectionEnd(editText.getText()));
+
+ // Move to the previous page and wait for an event.
+ AccessibilityEvent sixthExpected = getInteractionBridge()
+ .executeCommandAndWaitForAccessibilityEvent(new Runnable() {
+ @Override
+ public void run() {
+ getInteractionBridge().performAction(text,
+ AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY, arguments);
+ }
+ }, new AccessibilityEventFilter() {
+ @Override
+ public boolean accept(AccessibilityEvent event) {
+ return
+ (event.getEventType() ==
+ AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY
+ && event.getAction() ==
+ AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY
+ && event.getPackageName().equals(getActivity().getPackageName())
+ && event.getClassName().equals(EditText.class.getName())
+ && event.getText().size() > 0
+ && event.getText().get(0).toString().equals(getString(
+ R.string.android_wiki))
+ && event.getFromIndex() == 0
+ && event.getToIndex() == 139
+ && event.getMovementGranularity() ==
+ AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PAGE);
+ }
+ }, TIMEOUT_ASYNC_PROCESSING);
+
+ // Make sure we got the expected event.
+ assertNotNull(sixthExpected);
+
+ // Verify the selection position.
+ assertEquals(0, Selection.getSelectionStart(editText.getText()));
+ assertEquals(436, Selection.getSelectionEnd(editText.getText()));
+
+ // Make sure there is no previous.
+ assertFalse(getInteractionBridge().performAction(text,
+ AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY, arguments));
+
+ // Verify the selection position.
+ assertEquals(0, Selection.getSelectionStart(editText.getText()));
+ assertEquals(436, Selection.getSelectionEnd(editText.getText()));
+ }
+
+ @MediumTest
public void testActionNextAndPreviousAtGranularityParagraphOverText() throws Exception {
final TextView textView = (TextView) getActivity().findViewById(R.id.edit);
@@ -1447,7 +2617,7 @@
(event.getEventType() ==
AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY
&& event.getAction() ==
- AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY
+ AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY
&& event.getPackageName().equals(getActivity().getPackageName())
&& event.getClassName().equals(EditText.class.getName())
&& event.getText().size() > 0
@@ -1455,8 +2625,8 @@
R.string.android_wiki_paragraphs))
&& event.getFromIndex() == 2
&& event.getToIndex() == 104
- && event.getMovementGranularity()
- == AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PARAGRAPH);
+ && event.getMovementGranularity() ==
+ AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PARAGRAPH);
}
}, TIMEOUT_ASYNC_PROCESSING);
@@ -1482,7 +2652,7 @@
(event.getEventType() ==
AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY
&& event.getAction() ==
- AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY
+ AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY
&& event.getPackageName().equals(getActivity().getPackageName())
&& event.getClassName().equals(EditText.class.getName())
&& event.getText().size() > 0
@@ -1491,7 +2661,7 @@
&& event.getFromIndex() == 106
&& event.getToIndex() == 267
&& event.getMovementGranularity() ==
- AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PARAGRAPH);
+ AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PARAGRAPH);
}
}, TIMEOUT_ASYNC_PROCESSING);
@@ -1517,7 +2687,7 @@
(event.getEventType() ==
AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY
&& event.getAction() ==
- AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY
+ AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY
&& event.getPackageName().equals(getActivity().getPackageName())
&& event.getClassName().equals(EditText.class.getName())
&& event.getText().size() > 0
@@ -1526,7 +2696,7 @@
&& event.getFromIndex() == 268
&& event.getToIndex() == 582
&& event.getMovementGranularity() ==
- AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PARAGRAPH);
+ AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PARAGRAPH);
}
}, TIMEOUT_ASYNC_PROCESSING);
@@ -1560,7 +2730,7 @@
(event.getEventType() ==
AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY
&& event.getAction() ==
- AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY
+ AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY
&& event.getPackageName().equals(getActivity().getPackageName())
&& event.getClassName().equals(EditText.class.getName())
&& event.getText().size() > 0
@@ -1569,7 +2739,7 @@
&& event.getFromIndex() == 268
&& event.getToIndex() == 582
&& event.getMovementGranularity() ==
- AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PARAGRAPH);
+ AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PARAGRAPH);
}
}, TIMEOUT_ASYNC_PROCESSING);
@@ -1595,7 +2765,7 @@
(event.getEventType() ==
AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY
&& event.getAction() ==
- AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY
+ AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY
&& event.getPackageName().equals(getActivity().getPackageName())
&& event.getClassName().equals(EditText.class.getName())
&& event.getText().size() > 0
@@ -1604,7 +2774,7 @@
&& event.getFromIndex() == 106
&& event.getToIndex() == 267
&& event.getMovementGranularity() ==
- AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PARAGRAPH);
+ AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PARAGRAPH);
}
}, TIMEOUT_ASYNC_PROCESSING);
@@ -1630,7 +2800,7 @@
(event.getEventType() ==
AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY
&& event.getAction() ==
- AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY
+ AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY
&& event.getPackageName().equals(getActivity().getPackageName())
&& event.getClassName().equals(EditText.class.getName())
&& event.getText().size() > 0
@@ -1638,8 +2808,8 @@
R.string.android_wiki_paragraphs))
&& event.getFromIndex() == 2
&& event.getToIndex() == 104
- && event.getMovementGranularity()
- == AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PARAGRAPH);
+ && event.getMovementGranularity() ==
+ AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PARAGRAPH);
}
}, TIMEOUT_ASYNC_PROCESSING);
@@ -1658,4 +2828,349 @@
assertEquals(2, Selection.getSelectionStart(textView.getText()));
assertEquals(2, Selection.getSelectionEnd(textView.getText()));
}
+
+ @MediumTest
+ public void testActionNextAndPreviousAtGranularityParagraphOverTextExtend() throws Exception {
+ final EditText editText = (EditText) getActivity().findViewById(R.id.edit);
+
+ getInstrumentation().runOnMainSync(new Runnable() {
+ @Override
+ public void run() {
+ editText.setText(getString(R.string.android_wiki_paragraphs));
+ Selection.removeSelection(editText.getText());
+ }
+ });
+
+ final AccessibilityNodeInfo text = getInteractionBridge()
+ .findAccessibilityNodeInfoByTextFromRoot(getString(R.string.android_wiki_short));
+
+ final int granularities = text.getMovementGranularities();
+ assertEquals(granularities, AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER
+ | AccessibilityNodeInfo.MOVEMENT_GRANULARITY_WORD
+ | AccessibilityNodeInfo.MOVEMENT_GRANULARITY_LINE
+ | AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PARAGRAPH
+ | AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PAGE);
+
+ final Bundle arguments = new Bundle();
+ arguments.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT,
+ AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PARAGRAPH);
+ arguments.putBoolean(AccessibilityNodeInfo.ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN, true);
+
+ // Move to the next paragraph and wait for an event.
+ AccessibilityEvent firstExpected = getInteractionBridge()
+ .executeCommandAndWaitForAccessibilityEvent(new Runnable() {
+ @Override
+ public void run() {
+ getInteractionBridge().performAction(text,
+ AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY, arguments);
+ }
+ }, new AccessibilityEventFilter() {
+ @Override
+ public boolean accept(AccessibilityEvent event) {
+ return
+ (event.getEventType() ==
+ AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY
+ && event.getAction() ==
+ AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY
+ && event.getPackageName().equals(getActivity().getPackageName())
+ && event.getClassName().equals(EditText.class.getName())
+ && event.getText().size() > 0
+ && event.getText().get(0).toString().equals(getString(
+ R.string.android_wiki_paragraphs))
+ && event.getFromIndex() == 2
+ && event.getToIndex() == 104
+ && event.getMovementGranularity() ==
+ AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PARAGRAPH);
+ }
+ }, TIMEOUT_ASYNC_PROCESSING);
+
+ // Make sure we got the expected event.
+ assertNotNull(firstExpected);
+
+ // Verify the selection position.
+ assertEquals(2, Selection.getSelectionStart(editText.getText()));
+ assertEquals(104, Selection.getSelectionEnd(editText.getText()));
+
+ // Move to the next paragraph and wait for an event.
+ AccessibilityEvent secondExpected = getInteractionBridge()
+ .executeCommandAndWaitForAccessibilityEvent(new Runnable() {
+ @Override
+ public void run() {
+ getInteractionBridge().performAction(text,
+ AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY, arguments);
+ }
+ }, new AccessibilityEventFilter() {
+ @Override
+ public boolean accept(AccessibilityEvent event) {
+ return
+ (event.getEventType() ==
+ AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY
+ && event.getAction() ==
+ AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY
+ && event.getPackageName().equals(getActivity().getPackageName())
+ && event.getClassName().equals(EditText.class.getName())
+ && event.getText().size() > 0
+ && event.getText().get(0).toString().equals(getString(
+ R.string.android_wiki_paragraphs))
+ && event.getFromIndex() == 106
+ && event.getToIndex() == 267
+ && event.getMovementGranularity() ==
+ AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PARAGRAPH);
+ }
+ }, TIMEOUT_ASYNC_PROCESSING);
+
+ // Make sure we got the expected event.
+ assertNotNull(secondExpected);
+
+ // Verify the selection position.
+ assertEquals(2, Selection.getSelectionStart(editText.getText()));
+ assertEquals(267, Selection.getSelectionEnd(editText.getText()));
+
+ // Move to the next paragraph and wait for an event.
+ AccessibilityEvent thirdExpected = getInteractionBridge()
+ .executeCommandAndWaitForAccessibilityEvent(new Runnable() {
+ @Override
+ public void run() {
+ getInteractionBridge().performAction(text,
+ AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY, arguments);
+ }
+ }, new AccessibilityEventFilter() {
+ @Override
+ public boolean accept(AccessibilityEvent event) {
+ return
+ (event.getEventType() ==
+ AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY
+ && event.getAction() ==
+ AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY
+ && event.getPackageName().equals(getActivity().getPackageName())
+ && event.getClassName().equals(EditText.class.getName())
+ && event.getText().size() > 0
+ && event.getText().get(0).toString().equals(getString(
+ R.string.android_wiki_paragraphs))
+ && event.getFromIndex() == 268
+ && event.getToIndex() == 582
+ && event.getMovementGranularity() ==
+ AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PARAGRAPH);
+ }
+ }, TIMEOUT_ASYNC_PROCESSING);
+
+ // Make sure we got the expected event.
+ assertNotNull(thirdExpected);
+
+ // Verify the selection position.
+ assertEquals(2, Selection.getSelectionStart(editText.getText()));
+ assertEquals(582, Selection.getSelectionEnd(editText.getText()));
+
+ // Make sure there is no next.
+ assertFalse(getInteractionBridge().performAction(text,
+ AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY, arguments));
+
+ // Verify the selection position.
+ assertEquals(2, Selection.getSelectionStart(editText.getText()));
+ assertEquals(582, Selection.getSelectionEnd(editText.getText()));
+
+ // Focus the view so we can change selection.
+ getInstrumentation().runOnMainSync(new Runnable() {
+ @Override
+ public void run() {
+ editText.setFocusable(true);
+ editText.requestFocus();
+ }
+ });
+
+ // Put selection at the end of the text.
+ Bundle setSelectionArgs = new Bundle();
+ setSelectionArgs.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_START_INT, 582);
+ setSelectionArgs.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_END_INT, 582);
+ assertTrue(getInteractionBridge().performAction(text,
+ AccessibilityNodeInfo.ACTION_SET_SELECTION, setSelectionArgs));
+
+ // Verify the selection position.
+ assertEquals(582, Selection.getSelectionStart(editText.getText()));
+ assertEquals(582, Selection.getSelectionEnd(editText.getText()));
+
+ // Unfocus the view so we can get rid of the soft-keyboard.
+ getInstrumentation().runOnMainSync(new Runnable() {
+ @Override
+ public void run() {
+ editText.clearFocus();
+ editText.setFocusable(false);
+ }
+ });
+
+ // Move to the previous paragraph and wait for an event.
+ AccessibilityEvent fourthExpected = getInteractionBridge()
+ .executeCommandAndWaitForAccessibilityEvent(new Runnable() {
+ @Override
+ public void run() {
+ getInteractionBridge().performAction(text,
+ AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY, arguments);
+ }
+ }, new AccessibilityEventFilter() {
+ @Override
+ public boolean accept(AccessibilityEvent event) {
+ return
+ (event.getEventType() ==
+ AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY
+ && event.getAction() ==
+ AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY
+ && event.getPackageName().equals(getActivity().getPackageName())
+ && event.getClassName().equals(EditText.class.getName())
+ && event.getText().size() > 0
+ && event.getText().get(0).toString().equals(getString(
+ R.string.android_wiki_paragraphs))
+ && event.getFromIndex() == 268
+ && event.getToIndex() == 582
+ && event.getMovementGranularity() ==
+ AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PARAGRAPH);
+ }
+ }, TIMEOUT_ASYNC_PROCESSING);
+
+ // Make sure we got the expected event.
+ assertNotNull(fourthExpected);
+
+ // Verify the selection position.
+ assertEquals(268, Selection.getSelectionStart(editText.getText()));
+ assertEquals(582, Selection.getSelectionEnd(editText.getText()));
+
+ // Move to the previous paragraph and wait for an event.
+ AccessibilityEvent fifthExpected = getInteractionBridge()
+ .executeCommandAndWaitForAccessibilityEvent(new Runnable() {
+ @Override
+ public void run() {
+ getInteractionBridge().performAction(text,
+ AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY, arguments);
+ }
+ }, new AccessibilityEventFilter() {
+ @Override
+ public boolean accept(AccessibilityEvent event) {
+ return
+ (event.getEventType() ==
+ AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY
+ && event.getAction() ==
+ AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY
+ && event.getPackageName().equals(getActivity().getPackageName())
+ && event.getClassName().equals(EditText.class.getName())
+ && event.getText().size() > 0
+ && event.getText().get(0).toString().equals(getString(
+ R.string.android_wiki_paragraphs))
+ && event.getFromIndex() == 106
+ && event.getToIndex() == 267
+ && event.getMovementGranularity() ==
+ AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PARAGRAPH);
+ }
+ }, TIMEOUT_ASYNC_PROCESSING);
+
+ // Make sure we got the expected event.
+ assertNotNull(fifthExpected);
+
+ // Verify the selection position.
+ assertEquals(106, Selection.getSelectionStart(editText.getText()));
+ assertEquals(582, Selection.getSelectionEnd(editText.getText()));
+
+ // Move to the previous paragraph and wait for an event.
+ AccessibilityEvent sixthExpected = getInteractionBridge()
+ .executeCommandAndWaitForAccessibilityEvent(new Runnable() {
+ @Override
+ public void run() {
+ getInteractionBridge().performAction(text,
+ AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY, arguments);
+ }
+ }, new AccessibilityEventFilter() {
+ @Override
+ public boolean accept(AccessibilityEvent event) {
+ return
+ (event.getEventType() ==
+ AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY
+ && event.getAction() ==
+ AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY
+ && event.getPackageName().equals(getActivity().getPackageName())
+ && event.getClassName().equals(EditText.class.getName())
+ && event.getText().size() > 0
+ && event.getText().get(0).toString().equals(getString(
+ R.string.android_wiki_paragraphs))
+ && event.getFromIndex() == 2
+ && event.getToIndex() == 104
+ && event.getMovementGranularity() ==
+ AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PARAGRAPH);
+ }
+ }, TIMEOUT_ASYNC_PROCESSING);
+
+ // Make sure we got the expected event.
+ assertNotNull(sixthExpected);
+
+ // Verify the selection position.
+ assertEquals(2, Selection.getSelectionStart(editText.getText()));
+ assertEquals(582, Selection.getSelectionEnd(editText.getText()));
+
+ // Make sure there is no previous.
+ assertFalse(getInteractionBridge().performAction(text,
+ AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY, arguments));
+
+ // Verify the selection position.
+ assertEquals(2, Selection.getSelectionStart(editText.getText()));
+ assertEquals(582, Selection.getSelectionEnd(editText.getText()));
+ }
+
+ @MediumTest
+ public void testTextEditingActions() throws Exception {
+ final EditText editText = (EditText) getActivity().findViewById(R.id.edit);
+ final String textContent = getString(R.string.foo_bar_baz);
+
+ getInstrumentation().runOnMainSync(new Runnable() {
+ @Override
+ public void run() {
+ editText.setFocusable(true);
+ editText.requestFocus();
+ editText.setText(getString(R.string.foo_bar_baz));
+ Selection.removeSelection(editText.getText());
+ }
+ });
+
+ final AccessibilityNodeInfo text = getInteractionBridge()
+ .findAccessibilityNodeInfoByTextFromRoot(getString(R.string.foo_bar_baz));
+
+ // Select all text.
+ Bundle arguments = new Bundle();
+ arguments.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_START_INT, 0);
+ arguments.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_END_INT,
+ editText.getText().length());
+ assertTrue(getInteractionBridge().performAction(text,
+ AccessibilityNodeInfo.ACTION_SET_SELECTION, arguments));
+
+ // Copy the selected text.
+ getInteractionBridge().performAction(text, AccessibilityNodeInfo.ACTION_COPY);
+
+ // Set selection at the end.
+ final int textLength = editText.getText().length();
+ arguments.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_START_INT, textLength);
+ arguments.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_END_INT, textLength);
+ assertTrue(getInteractionBridge().performAction(text,
+ AccessibilityNodeInfo.ACTION_SET_SELECTION, arguments));
+
+ // Verify the selection position.
+ assertEquals(textLength, Selection.getSelectionStart(editText.getText()));
+ assertEquals(textLength, Selection.getSelectionEnd(editText.getText()));
+
+ // Paste the selected text.
+ assertTrue(getInteractionBridge().performAction(text,
+ AccessibilityNodeInfo.ACTION_PASTE));
+
+ // Verify the content.
+ assertEquals(editText.getText().toString(), textContent + " " + textContent);
+
+ // Select all text.
+ arguments.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_START_INT, 0);
+ arguments.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_END_INT,
+ editText.getText().length());
+ assertTrue(getInteractionBridge().performAction(text,
+ AccessibilityNodeInfo.ACTION_SET_SELECTION, arguments));
+
+ // Cut the selected text.
+ assertTrue(getInteractionBridge().performAction(text,
+ AccessibilityNodeInfo.ACTION_CUT));
+
+ // Verify the content.
+ assertTrue(TextUtils.isEmpty(editText.getText()));
+ }
}
diff --git a/tests/tests/app/Android.mk b/tests/tests/app/Android.mk
index 8d5877d..d4387b4 100644
--- a/tests/tests/app/Android.mk
+++ b/tests/tests/app/Android.mk
@@ -21,7 +21,7 @@
# and when built explicitly put it in the data partition
LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
-LOCAL_JAVA_LIBRARIES := android.test.runner telephony-common
+LOCAL_JAVA_LIBRARIES := android.test.runner telephony-common voip-common
LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner
diff --git a/tests/tests/content/src/android/content/cts/ContentProviderTest.java b/tests/tests/content/src/android/content/cts/ContentProviderTest.java
index 2baa904..34b1e8c 100644
--- a/tests/tests/content/src/android/content/cts/ContentProviderTest.java
+++ b/tests/tests/content/src/android/content/cts/ContentProviderTest.java
@@ -249,12 +249,12 @@
IContentProvider iContentProvider = new IContentProvider() {
@Override
- public int bulkInsert(Uri url, ContentValues[] initialValues) {
+ public int bulkInsert(String callingPkg, Uri url, ContentValues[] initialValues) {
return 0;
}
@Override
- public int delete(Uri url, String selection, String[] selectionArgs) {
+ public int delete(String callingPkg, Uri url, String selection, String[] selectionArgs) {
return 0;
}
@@ -264,36 +264,36 @@
}
@Override
- public Uri insert(Uri url, ContentValues initialValues) {
+ public Uri insert(String callingPkg, Uri url, ContentValues initialValues) {
return null;
}
@Override
- public ParcelFileDescriptor openFile(Uri url, String mode) {
+ public ParcelFileDescriptor openFile(String callingPkg, Uri url, String mode) {
return null;
}
@Override
- public AssetFileDescriptor openAssetFile(Uri url, String mode) {
+ public AssetFileDescriptor openAssetFile(String callingPkg, Uri url, String mode) {
return null;
}
@Override
- public ContentProviderResult[] applyBatch(
+ public ContentProviderResult[] applyBatch(String callingPkg,
ArrayList<ContentProviderOperation> operations)
throws RemoteException, OperationApplicationException {
return null;
}
@Override
- public Cursor query(Uri url, String[] projection, String selection,
+ public Cursor query(String callingPkg, Uri url, String[] projection, String selection,
String[] selectionArgs, String sortOrder,
ICancellationSignal cancellationSignal) {
return null;
}
@Override
- public int update(Uri url, ContentValues values, String selection,
+ public int update(String callingPkg, Uri url, ContentValues values, String selection,
String[] selectionArgs) {
return 0;
}
@@ -304,7 +304,7 @@
}
@Override
- public Bundle call(String method, String request, Bundle args) {
+ public Bundle call(String callingPkg, String method, String request, Bundle args) {
return null;
}
@@ -314,7 +314,8 @@
}
@Override
- public AssetFileDescriptor openTypedAssetFile(Uri url, String mimeType, Bundle opts)
+ public AssetFileDescriptor openTypedAssetFile(String callingPkg, Uri url,
+ String mimeType, Bundle opts)
throws RemoteException, FileNotFoundException {
return null;
}
diff --git a/suite/pts/hostTests/browser/browserlauncher/Android.mk b/tests/tests/display/Android.mk
similarity index 66%
copy from suite/pts/hostTests/browser/browserlauncher/Android.mk
copy to tests/tests/display/Android.mk
index 0592017..ec5b40d 100644
--- a/suite/pts/hostTests/browser/browserlauncher/Android.mk
+++ b/tests/tests/display/Android.mk
@@ -13,21 +13,25 @@
# limitations under the License.
LOCAL_PATH:= $(call my-dir)
+
include $(CLEAR_VARS)
# don't include this package in any target
LOCAL_MODULE_TAGS := optional
+# and when built explicitly put it in the data partition
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
LOCAL_JAVA_LIBRARIES := android.test.runner
-LOCAL_STATIC_JAVA_LIBRARIES := ptsutil ctsutil ctstestrunner ctstestserver
+LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner
LOCAL_SRC_FILES := $(call all-java-files-under, src)
-LOCAL_PACKAGE_NAME := PtsDeviceBrowserLauncher
+LOCAL_PACKAGE_NAME := CtsDisplayTestCases
-LOCAL_SDK_VERSION := 16
+LOCAL_SDK_VERSION := current
-include $(BUILD_PTS_PACKAGE)
+# This test runner sets up/cleans up the device before/after running the tests.
+LOCAL_CTS_TEST_RUNNER := com.android.cts.tradefed.testtype.DisplayTestRunner
-include $(call all-makefiles-under,$(LOCAL_PATH))
+include $(BUILD_CTS_PACKAGE)
diff --git a/tests/tests/display/AndroidManifest.xml b/tests/tests/display/AndroidManifest.xml
new file mode 100644
index 0000000..1ddce5a
--- /dev/null
+++ b/tests/tests/display/AndroidManifest.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.cts.display">
+
+ <uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
+ <application>
+ <uses-library android:name="android.test.runner" />
+ </application>
+
+ <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+ android:targetPackage="com.android.cts.display"
+ android:label="CTS tests of android.view.display"/>
+
+</manifest>
+
diff --git a/tests/tests/display/src/android/display/cts/DisplayTest.java b/tests/tests/display/src/android/display/cts/DisplayTest.java
new file mode 100644
index 0000000..595f7148
--- /dev/null
+++ b/tests/tests/display/src/android/display/cts/DisplayTest.java
@@ -0,0 +1,179 @@
+/*
+ * Copyright (C) 2012 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.display.cts;
+
+import android.content.Context;
+import android.graphics.PixelFormat;
+import android.graphics.Point;
+import android.hardware.display.DisplayManager;
+import android.test.AndroidTestCase;
+import android.util.DisplayMetrics;
+import android.view.Display;
+import android.view.WindowManager;
+
+public class DisplayTest extends AndroidTestCase {
+ // This test is called from DisplayTestRunner which brings up an overlay display on the target
+ // device. The overlay display parameters must match the ones defined there which are
+ // 1281x721/214 (wxh/dpi).
+
+ private static final int SECONDARY_DISPLAY_WIDTH = 1281;
+ private static final int SECONDARY_DISPLAY_HEIGHT = 721;
+ private static final int SECONDARY_DISPLAY_DPI = 214;
+ private static final float SCALE_DENSITY_LOWER_BOUND =
+ (float)(SECONDARY_DISPLAY_DPI - 1) / DisplayMetrics.DENSITY_DEFAULT;
+ private static final float SCALE_DENSITY_UPPER_BOUND =
+ (float)(SECONDARY_DISPLAY_DPI + 1) / DisplayMetrics.DENSITY_DEFAULT;
+ // Matches com.android.internal.R.string.display_manager_overlay_display_name.
+ private static final String OVERLAY_DISPLAY_NAME_PREFIX = "Overlay #";
+
+ private DisplayManager mDisplayManager;
+ private WindowManager mWindowManager;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ mDisplayManager = (DisplayManager)mContext.getSystemService(Context.DISPLAY_SERVICE);
+ mWindowManager = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
+ }
+
+ private boolean isSecondarySize(Display display) {
+ final Point p = new Point();
+ display.getSize(p);
+ return p.x == SECONDARY_DISPLAY_WIDTH && p.y == SECONDARY_DISPLAY_HEIGHT;
+ }
+
+ private Display getSecondaryDisplay(Display[] displays) {
+ for (Display display : displays) {
+ if (isSecondarySize(display)) {
+ return display;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Verify that the getDisplays method returns both a default and an overlay display.
+ */
+ public void testGetDisplays() {
+ Display[] displays = mDisplayManager.getDisplays();
+ assertNotNull(displays);
+ assertTrue(2 <= displays.length);
+ boolean hasDefaultDisplay = false;
+ boolean hasSecondaryDisplay = false;
+ for (Display display : displays) {
+ if (display.getDisplayId() == Display.DEFAULT_DISPLAY) {
+ hasDefaultDisplay = true;
+ }
+ if (isSecondarySize(display)) {
+ hasSecondaryDisplay = true;
+ }
+ }
+ assertTrue(hasDefaultDisplay);
+ assertTrue(hasSecondaryDisplay);
+ }
+
+ /**
+ * Verify that the WindowManager returns the default display.
+ */
+ public void testDefaultDisplay() {
+ assertEquals(Display.DEFAULT_DISPLAY, mWindowManager.getDefaultDisplay().getDisplayId());
+ }
+
+ /**
+ * Verify that there is a secondary display.
+ */
+ public void testSecondaryDisplay() {
+ Display display = getSecondaryDisplay(mDisplayManager.getDisplays());
+ assertNotNull(display);
+ assertTrue(Display.DEFAULT_DISPLAY != display.getDisplayId());
+ }
+
+ /**
+ * Test the properties of the secondary Display.
+ */
+ public void testGetDisplayAttrs() {
+ Display display = getSecondaryDisplay(mDisplayManager.getDisplays());
+
+ assertEquals(SECONDARY_DISPLAY_WIDTH, display.getWidth());
+ assertEquals(SECONDARY_DISPLAY_HEIGHT, display.getHeight());
+
+ Point outSize = new Point();
+ display.getSize(outSize);
+ assertEquals(SECONDARY_DISPLAY_WIDTH, outSize.x);
+ assertEquals(SECONDARY_DISPLAY_HEIGHT, outSize.y);
+
+ assertEquals(0, display.getOrientation());
+
+ assertEquals(PixelFormat.RGBA_8888, display.getPixelFormat());
+
+ assertTrue(0 < display.getRefreshRate());
+
+ assertTrue(display.getName().contains(OVERLAY_DISPLAY_NAME_PREFIX));
+ }
+
+ /**
+ * Test that the getMetrics method fills in correct values.
+ */
+ public void testGetMetrics() {
+ Display display = getSecondaryDisplay(mDisplayManager.getDisplays());
+
+ Point outSize = new Point();
+ display.getSize(outSize);
+
+ DisplayMetrics outMetrics = new DisplayMetrics();
+ outMetrics.setToDefaults();
+ display.getMetrics(outMetrics);
+
+ assertEquals(SECONDARY_DISPLAY_WIDTH, outMetrics.widthPixels);
+ assertEquals(SECONDARY_DISPLAY_HEIGHT, outMetrics.heightPixels);
+
+ // The scale is in [0.1, 3], and density is the scale factor.
+ assertTrue(SCALE_DENSITY_LOWER_BOUND <= outMetrics.density
+ && outMetrics.density <= SCALE_DENSITY_UPPER_BOUND);
+ assertTrue(SCALE_DENSITY_LOWER_BOUND <= outMetrics.scaledDensity
+ && outMetrics.scaledDensity <= SCALE_DENSITY_UPPER_BOUND);
+
+ assertEquals(SECONDARY_DISPLAY_DPI, outMetrics.densityDpi);
+ assertEquals((float)SECONDARY_DISPLAY_DPI, outMetrics.xdpi);
+ assertEquals((float)SECONDARY_DISPLAY_DPI, outMetrics.ydpi);
+ }
+
+ /**
+ * Test that the getCurrentSizeRange method returns correct values.
+ */
+ public void testGetCurrentSizeRange() {
+ Display display = getSecondaryDisplay(mDisplayManager.getDisplays());
+
+ Point smallest = new Point();
+ Point largest = new Point();
+ display.getCurrentSizeRange(smallest, largest);
+
+ assertEquals(SECONDARY_DISPLAY_WIDTH, smallest.x);
+ assertEquals(SECONDARY_DISPLAY_HEIGHT, smallest.y);
+ assertEquals(SECONDARY_DISPLAY_WIDTH, largest.x);
+ assertEquals(SECONDARY_DISPLAY_HEIGHT, largest.y);
+ }
+
+ /**
+ * Test that the getFlags method returns no flag bits set for the overlay display.
+ */
+ public void testFlags() {
+ Display display = getSecondaryDisplay(mDisplayManager.getDisplays());
+
+ assertEquals(0, display.getFlags());
+ }
+}
diff --git a/suite/pts/hostTests/browser/browserlauncher/Android.mk b/tests/tests/dreams/Android.mk
similarity index 67%
copy from suite/pts/hostTests/browser/browserlauncher/Android.mk
copy to tests/tests/dreams/Android.mk
index 0592017..c630d8a 100644
--- a/suite/pts/hostTests/browser/browserlauncher/Android.mk
+++ b/tests/tests/dreams/Android.mk
@@ -13,21 +13,25 @@
# limitations under the License.
LOCAL_PATH:= $(call my-dir)
+
include $(CLEAR_VARS)
-# don't include this package in any target
+LOCAL_PACKAGE_NAME := CtsDreamsTestCases
+
+# Don't include this package in any target.
LOCAL_MODULE_TAGS := optional
+# When built, explicitly put it in the data partition.
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+
+# All tests should include android.test.runner.
LOCAL_JAVA_LIBRARIES := android.test.runner
-LOCAL_STATIC_JAVA_LIBRARIES := ptsutil ctsutil ctstestrunner ctstestserver
+LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner
LOCAL_SRC_FILES := $(call all-java-files-under, src)
-LOCAL_PACKAGE_NAME := PtsDeviceBrowserLauncher
+# Need access to ServiceManager
+#LOCAL_SDK_VERSION := current
-LOCAL_SDK_VERSION := 16
-
-include $(BUILD_PTS_PACKAGE)
-
-include $(call all-makefiles-under,$(LOCAL_PATH))
+include $(BUILD_CTS_PACKAGE)
diff --git a/tests/tests/dreams/AndroidManifest.xml b/tests/tests/dreams/AndroidManifest.xml
new file mode 100644
index 0000000..fb3e564
--- /dev/null
+++ b/tests/tests/dreams/AndroidManifest.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2012 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.cts.dreams">
+
+ <uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
+ <application>
+ <uses-library android:name="android.test.runner" />
+ </application>
+
+ <!-- This is a self-instrumenting test package. -->
+ <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+ android:targetPackage="com.android.cts.dreams"
+ android:label="CTS tests for the android.service.dreams package"/>
+
+</manifest>
+
diff --git a/tests/tests/dreams/src/android/service/dreams/cts/DreamsFeatureTest.java b/tests/tests/dreams/src/android/service/dreams/cts/DreamsFeatureTest.java
new file mode 100644
index 0000000..358a7bf
--- /dev/null
+++ b/tests/tests/dreams/src/android/service/dreams/cts/DreamsFeatureTest.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2012 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.service.dreams.cts;
+
+import android.os.ServiceManager;
+import android.service.dreams.DreamService;
+import android.service.dreams.IDreamManager;
+
+import junit.framework.TestCase;
+
+public class DreamsFeatureTest extends TestCase {
+
+ public void testDreamManagerExists() {
+ IDreamManager service = IDreamManager.Stub.asInterface(
+ ServiceManager.getService(DreamService.DREAM_SERVICE));
+ assertTrue("Dream manager service missing", service != null);
+ }
+
+}
diff --git a/tests/tests/hardware/src/android/hardware/cts/CameraTest.java b/tests/tests/hardware/src/android/hardware/cts/CameraTest.java
index bbb5643..31a72cf 100755
--- a/tests/tests/hardware/src/android/hardware/cts/CameraTest.java
+++ b/tests/tests/hardware/src/android/hardware/cts/CameraTest.java
@@ -53,6 +53,8 @@
import java.util.Iterator;
import java.util.List;
+import junit.framework.AssertionFailedError;
+
/**
* This test case must run with hardware. It can't be tested in emulator
*/
@@ -1713,6 +1715,9 @@
// ignore
}
mCamera.stopPreview();
+ // See if any frame duration violations occurred during preview run
+ AssertionFailedError e = callback.getDurationException();
+ if (e != null) throw(e);
}
// Test the invalid fps cases.
@@ -1741,6 +1746,7 @@
// An array storing the arrival time of the frames in the last second.
private ArrayList<Long> mFrames = new ArrayList<Long>();
private long firstFrameArrivalTime;
+ private AssertionFailedError mDurationException = null;
public void reset(double minFps, double maxFps) {
this.mMinFps = minFps;
@@ -1752,6 +1758,7 @@
+ ". Max frame interval=" + mMaxFrameInterval);
mFrames.clear();
firstFrameArrivalTime = 0;
+ mDurationException = null;
}
// This method tests if the actual fps is between minimum and maximum.
@@ -1783,34 +1790,50 @@
// variance is averaged out.
// Check if the frame interval is too large or too small.
- // x100 = percent, intervalMargin should be bigger than fpsMargin considering that
- // fps will be in the order of 10.
+ // x100 = percent, intervalMargin should be bigger than
+ // fpsMargin considering that fps will be in the order of 10.
double intervalMargin = 0.9;
long lastArrivalTime = mFrames.get(mFrames.size() - 1);
double interval = arrivalTime - lastArrivalTime;
if (LOGV) Log.v(TAG, "Frame interval=" + interval);
- assertTrue("Frame interval (" + interval + "ms) is too large." +
- " mMaxFrameInterval=" + mMaxFrameInterval + "ms",
- interval < mMaxFrameInterval * (1.0 + intervalMargin));
- assertTrue("Frame interval (" + interval + "ms) is too small." +
- " mMinFrameInterval=" + mMinFrameInterval + "ms",
- interval > mMinFrameInterval * (1.0 - intervalMargin));
+ try {
+ assertTrue("Frame interval (" + interval + "ms) is too " +
+ "large. mMaxFrameInterval=" +
+ mMaxFrameInterval + "ms",
+ interval < mMaxFrameInterval *
+ (1.0 + intervalMargin));
+ assertTrue("Frame interval (" + interval + "ms) is too " +
+ "small. mMinFrameInterval=" +
+ mMinFrameInterval + "ms",
+ interval > mMinFrameInterval *
+ (1.0 - intervalMargin));
- // Check if the fps is within range.
- double fpsMargin = 0.5; // x100 = percent
- double avgInterval = (double)(arrivalTime - mFrames.get(0))
- / mFrames.size();
- double fps = 1000.0 / avgInterval;
- assertTrue("Actual fps (" + fps + ") should be larger than " +
- "min fps (" + mMinFps + ")",
- fps >= mMinFps * (1.0 - fpsMargin));
- assertTrue("Actual fps (" + fps + ") should be smaller than " +
- "max fps (" + mMaxFps + ")",
- fps <= mMaxFps * (1.0 + fpsMargin));
+ // Check if the fps is within range.
+ double fpsMargin = 0.5; // x100 = percent
+ double avgInterval = (double)(arrivalTime - mFrames.get(0))
+ / mFrames.size();
+ double fps = 1000.0 / avgInterval;
+ assertTrue("Actual fps (" + fps + ") should be larger " +
+ "than min fps (" + mMinFps + ")",
+ fps >= mMinFps * (1.0 - fpsMargin));
+ assertTrue("Actual fps (" + fps + ") should be smaller" +
+ "than max fps (" + mMaxFps + ")",
+ fps <= mMaxFps * (1.0 + fpsMargin));
+ } catch (AssertionFailedError e) {
+ // Need to throw this only in the test body, instead of in
+ // the callback
+ if (mDurationException == null) {
+ mDurationException = e;
+ }
+ }
}
// Add the arrival time of this frame to the list.
mFrames.add(arrivalTime);
}
+
+ public AssertionFailedError getDurationException() {
+ return mDurationException;
+ }
}
private void assertEquals(Size expected, Size actual) {
diff --git a/tests/tests/location/src/android/location/cts/LocationManagerTest.java b/tests/tests/location/src/android/location/cts/LocationManagerTest.java
index edfda5a..5e823bc 100755
--- a/tests/tests/location/src/android/location/cts/LocationManagerTest.java
+++ b/tests/tests/location/src/android/location/cts/LocationManagerTest.java
@@ -291,7 +291,7 @@
}
/**
- * Helper method to test a location update with given provider
+ * Helper method to test a location update with given mock location provider
*
* @param providerName name of provider to test. Must already exist.
* @throws InterruptedException
@@ -322,6 +322,7 @@
assertEquals(providerName, location.getProvider());
assertEquals(latitude1, location.getLatitude());
assertEquals(longitude1, location.getLongitude());
+ assertEquals(true, location.isFromMockProvider());
// update location without notifying listener
listener.reset();
@@ -373,6 +374,7 @@
assertEquals(TEST_MOCK_PROVIDER_NAME, location.getProvider());
assertEquals(latitude1, location.getLatitude());
assertEquals(longitude1, location.getLongitude());
+ assertEquals(true, location.isFromMockProvider());
// update location without notifying listener
mManager.removeUpdates(listener);
@@ -420,6 +422,7 @@
assertEquals(TEST_MOCK_PROVIDER_NAME, location.getProvider());
assertEquals(latitude1, location.getLatitude());
assertEquals(longitude1, location.getLongitude());
+ assertEquals(true, location.isFromMockProvider());
// update location without receiving broadcast.
mManager.removeUpdates(mPendingIntent);
@@ -509,6 +512,7 @@
assertEquals(TEST_MOCK_PROVIDER_NAME, location.getProvider());
assertEquals(latitude1, location.getLatitude());
assertEquals(longitude1, location.getLongitude());
+ assertEquals(true, location.isFromMockProvider());
mIntentReceiver.clearReceivedIntents();
updateLocation(latitude2, longitude2);
@@ -519,6 +523,7 @@
assertEquals(TEST_MOCK_PROVIDER_NAME, location.getProvider());
assertEquals(latitude2, location.getLatitude());
assertEquals(longitude2, location.getLongitude());
+ assertEquals(true, location.isFromMockProvider());
try {
mManager.getLastKnownLocation(null);
@@ -678,6 +683,7 @@
assertEquals(providerName, location.getProvider());
assertEquals(latitude, location.getLatitude());
assertEquals(longitude, location.getLongitude());
+ assertEquals(true, location.isFromMockProvider());
// Remove the listener.
mManager.removeUpdates(listener);
diff --git a/tests/tests/media/src/android/media/cts/DecoderTest.java b/tests/tests/media/src/android/media/cts/DecoderTest.java
index ac80538..54a670d 100644
--- a/tests/tests/media/src/android/media/cts/DecoderTest.java
+++ b/tests/tests/media/src/android/media/cts/DecoderTest.java
@@ -18,29 +18,28 @@
import com.android.cts.media.R;
-import android.content.Context;
import android.content.res.AssetFileDescriptor;
import android.content.res.Resources;
import android.media.MediaCodec;
import android.media.MediaExtractor;
import android.media.MediaFormat;
-import android.test.AndroidTestCase;
import android.util.Log;
+import android.view.Surface;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
-import java.util.Map;
+import java.util.zip.CRC32;
-public class DecoderTest extends AndroidTestCase {
+public class DecoderTest extends MediaPlayerTestBase {
private static final String TAG = "DecoderTest";
private Resources mResources;
@Override
- public void setContext(Context context) {
- super.setContext(context);
+ protected void setUp() throws Exception {
+ super.setUp();
mResources = mContext.getResources();
}
@@ -91,6 +90,7 @@
masterBuffer[i] = (short) sample;
}
bis.close();
+ masterFd.close();
AssetFileDescriptor testFd = mResources.openRawResourceFd(testinput);
@@ -102,6 +102,7 @@
extractor = new MediaExtractor();
extractor.setDataSource(testFd.getFileDescriptor(), testFd.getStartOffset(),
testFd.getLength());
+ testFd.close();
assertEquals("wrong number of tracks", 1, extractor.getTrackCount());
MediaFormat format = extractor.getTrackFormat(0);
@@ -194,8 +195,6 @@
codec.stop();
codec.release();
- testFd.close();
- masterFd.close();
assertEquals("wrong data size", masterLength, numBytesDecoded);
long avgErrorSquared = (totalErrorSquared / (numBytesDecoded / 2));
@@ -203,5 +202,494 @@
assertTrue("decoding error too big: " + rmse, rmse <= maxerror);
}
+ public void testCodecBasicH264() throws Exception {
+ Surface s = getActivity().getSurfaceHolder().getSurface();
+ int frames1 = countFrames(
+ R.raw.video_480x360_mp4_h264_1000kbps_25fps_aac_stereo_128kbps_44100hz,
+ false, s);
+ assertEquals("wrong number of frames decoded", 240, frames1);
+
+ int frames2 = countFrames(
+ R.raw.video_480x360_mp4_h264_1000kbps_25fps_aac_stereo_128kbps_44100hz,
+ false, null);
+ assertEquals("different number of frames when using Surface", frames1, frames2);
+ }
+
+ public void testCodecBasicH263() throws Exception {
+ Surface s = getActivity().getSurfaceHolder().getSurface();
+ int frames1 = countFrames(
+ R.raw.video_176x144_3gp_h263_300kbps_12fps_aac_stereo_128kbps_22050hz,
+ false, s);
+ assertEquals("wrong number of frames decoded", 122, frames1);
+
+ int frames2 = countFrames(
+ R.raw.video_176x144_3gp_h263_300kbps_12fps_aac_stereo_128kbps_22050hz,
+ false, null);
+ assertEquals("different number of frames when using Surface", frames1, frames2);
+ }
+
+ public void testCodecReconfigH264WithoutSurface() throws Exception {
+ testCodecReconfig(
+ R.raw.video_480x360_mp4_h264_1000kbps_25fps_aac_stereo_128kbps_44100hz, null);
+ }
+
+ public void testCodecReconfigH264WithSurface() throws Exception {
+ Surface s = getActivity().getSurfaceHolder().getSurface();
+ testCodecReconfig(
+ R.raw.video_480x360_mp4_h264_1000kbps_25fps_aac_stereo_128kbps_44100hz, s);
+ }
+
+ public void testCodecReconfigH263WithoutSurface() throws Exception {
+ testCodecReconfig(
+ R.raw.video_176x144_3gp_h263_300kbps_12fps_aac_stereo_128kbps_22050hz, null);
+ }
+
+ public void testCodecReconfigH263WithSurface() throws Exception {
+ Surface s = getActivity().getSurfaceHolder().getSurface();
+ testCodecReconfig(
+ R.raw.video_176x144_3gp_h263_300kbps_12fps_aac_stereo_128kbps_22050hz, s);
+ }
+
+// public void testCodecReconfigOgg() throws Exception {
+// testCodecReconfig(R.raw.sinesweepogg, null);
+// }
+//
+ public void testCodecReconfigMp3() throws Exception {
+ testCodecReconfig(R.raw.sinesweepmp3lame, null);
+ }
+
+ public void testCodecReconfigM4a() throws Exception {
+ testCodecReconfig(R.raw.sinesweepm4a, null);
+ }
+
+ private void testCodecReconfig(int video, Surface s) throws Exception {
+ int frames1 = countFrames(video, false, s);
+ int frames2 = countFrames(video, true, s);
+ assertEquals("different number of frames when reusing codec", frames1, frames2);
+ }
+
+ private int countFrames(int video, boolean reconfigure, Surface s) throws Exception {
+ int numframes = 0;
+
+ AssetFileDescriptor testFd = mResources.openRawResourceFd(video);
+
+ MediaExtractor extractor;
+ MediaCodec codec = null;
+ ByteBuffer[] codecInputBuffers;
+ ByteBuffer[] codecOutputBuffers;
+
+ extractor = new MediaExtractor();
+ extractor.setDataSource(testFd.getFileDescriptor(), testFd.getStartOffset(),
+ testFd.getLength());
+
+ MediaFormat format = extractor.getTrackFormat(0);
+ String mime = format.getString(MediaFormat.KEY_MIME);
+ boolean isAudio = mime.startsWith("audio/");
+
+ codec = MediaCodec.createDecoderByType(mime);
+// if (mime.contains("avc")) {
+// codec = MediaCodec.createByCodecName("OMX.google.h264.decoder");
+// } else if (mime.contains("3gpp")) {
+// codec = MediaCodec.createByCodecName("OMX.google.h263.decoder");
+// }
+ assertNotNull("couldn't find codec", codec);
+ Log.i("@@@@", "using codec: " + codec.getName());
+ codec.configure(format, s /* surface */, null /* crypto */, 0 /* flags */);
+ codec.start();
+ codecInputBuffers = codec.getInputBuffers();
+ codecOutputBuffers = codec.getOutputBuffers();
+
+ if (reconfigure) {
+ codec.stop();
+ codec.configure(format, s /* surface */, null /* crypto */, 0 /* flags */);
+ codec.start();
+ codecInputBuffers = codec.getInputBuffers();
+ codecOutputBuffers = codec.getOutputBuffers();
+ }
+ Log.i("@@@@", "format: " + format);
+
+ extractor.selectTrack(0);
+
+ // start decoding
+ final long kTimeOutUs = 5000;
+ MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();
+ boolean sawInputEOS = false;
+ boolean sawOutputEOS = false;
+ int deadDecoderCounter = 0;
+ while (!sawOutputEOS && deadDecoderCounter < 100) {
+ if (!sawInputEOS) {
+ int inputBufIndex = codec.dequeueInputBuffer(kTimeOutUs);
+
+ if (inputBufIndex >= 0) {
+ ByteBuffer dstBuf = codecInputBuffers[inputBufIndex];
+
+ int sampleSize =
+ extractor.readSampleData(dstBuf, 0 /* offset */);
+
+ long presentationTimeUs = 0;
+
+ if (sampleSize < 0) {
+ Log.d(TAG, "saw input EOS.");
+ sawInputEOS = true;
+ sampleSize = 0;
+ } else {
+ presentationTimeUs = extractor.getSampleTime();
+ }
+
+ int flags = extractor.getSampleFlags();
+
+ codec.queueInputBuffer(
+ inputBufIndex,
+ 0 /* offset */,
+ sampleSize,
+ presentationTimeUs,
+ sawInputEOS ? MediaCodec.BUFFER_FLAG_END_OF_STREAM : 0);
+
+ if (!sawInputEOS) {
+ extractor.advance();
+ }
+ }
+ }
+
+ int res = codec.dequeueOutputBuffer(info, kTimeOutUs);
+
+ deadDecoderCounter++;
+ if (res >= 0) {
+ //Log.d(TAG, "got frame, size " + info.size + "/" + info.presentationTimeUs);
+
+ // Some decoders output a 0-sized buffer at the end. Disregard those.
+ if (info.size > 0) {
+ deadDecoderCounter = 0;
+ if (reconfigure) {
+ // once we've gotten some data out of the decoder, reconfigure it again
+ reconfigure = false;
+ numframes = 0;
+ extractor.seekTo(0, MediaExtractor.SEEK_TO_NEXT_SYNC);
+ sawInputEOS = false;
+ codec.stop();
+ codec.configure(format, s /* surface */, null /* crypto */, 0 /* flags */);
+ codec.start();
+ codecInputBuffers = codec.getInputBuffers();
+ codecOutputBuffers = codec.getOutputBuffers();
+ continue;
+ }
+
+ if (isAudio) {
+ // for audio, count the number of bytes that were decoded, not the number
+ // of access units
+ numframes += info.size;
+ } else {
+ // for video, count the number of video frames
+ numframes++;
+ }
+ }
+ int outputBufIndex = res;
+ codec.releaseOutputBuffer(outputBufIndex, true /* render */);
+
+ if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
+ Log.d(TAG, "saw output EOS.");
+ sawOutputEOS = true;
+ }
+ } else if (res == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
+ codecOutputBuffers = codec.getOutputBuffers();
+
+ Log.d(TAG, "output buffers have changed.");
+ } else if (res == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
+ MediaFormat oformat = codec.getOutputFormat();
+
+ Log.d(TAG, "output format has changed to " + oformat);
+ } else {
+ Log.d(TAG, "no output");
+ }
+ }
+
+ codec.stop();
+ codec.release();
+ testFd.close();
+ return numframes;
+ }
+
+ public void testEOSBehaviorH264() throws Exception {
+ // this video has an I frame at 44
+ testEOSBehavior(R.raw.video_480x360_mp4_h264_1000kbps_25fps_aac_stereo_128kbps_44100hz, 44);
+ testEOSBehavior(R.raw.video_480x360_mp4_h264_1000kbps_25fps_aac_stereo_128kbps_44100hz, 45);
+ testEOSBehavior(R.raw.video_480x360_mp4_h264_1000kbps_25fps_aac_stereo_128kbps_44100hz, 55);
+ }
+
+ public void testEOSBehaviorH263() throws Exception {
+ // this video has an I frame every 12 frames.
+ testEOSBehavior(R.raw.video_176x144_3gp_h263_300kbps_12fps_aac_stereo_128kbps_22050hz, 24);
+ testEOSBehavior(R.raw.video_176x144_3gp_h263_300kbps_12fps_aac_stereo_128kbps_22050hz, 25);
+ testEOSBehavior(R.raw.video_176x144_3gp_h263_300kbps_12fps_aac_stereo_128kbps_22050hz, 48);
+ testEOSBehavior(R.raw.video_176x144_3gp_h263_300kbps_12fps_aac_stereo_128kbps_22050hz, 50);
+ }
+
+ private void testEOSBehavior(int movie, int stopatsample) throws Exception {
+
+ int numframes = 0;
+
+ long [] checksums = new long[stopatsample];
+
+ AssetFileDescriptor testFd = mResources.openRawResourceFd(movie);
+
+ MediaExtractor extractor;
+ MediaCodec codec = null;
+ ByteBuffer[] codecInputBuffers;
+ ByteBuffer[] codecOutputBuffers;
+
+ extractor = new MediaExtractor();
+ extractor.setDataSource(testFd.getFileDescriptor(), testFd.getStartOffset(),
+ testFd.getLength());
+
+ MediaFormat format = extractor.getTrackFormat(0);
+ String mime = format.getString(MediaFormat.KEY_MIME);
+ boolean isAudio = mime.startsWith("audio/");
+
+ codec = MediaCodec.createDecoderByType(mime);
+// if (mime.contains("avc")) {
+// codec = MediaCodec.createByCodecName("OMX.google.h264.decoder");
+// } else if (mime.contains("3gpp")) {
+// codec = MediaCodec.createByCodecName("OMX.google.h263.decoder");
+// }
+ assertNotNull("couldn't find codec", codec);
+ Log.i("@@@@", "using codec: " + codec.getName());
+ codec.configure(format, null /* surface */, null /* crypto */, 0 /* flags */);
+ codec.start();
+ codecInputBuffers = codec.getInputBuffers();
+ codecOutputBuffers = codec.getOutputBuffers();
+
+ extractor.selectTrack(0);
+
+ // start decoding
+ final long kTimeOutUs = 5000;
+ MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();
+ boolean sawInputEOS = false;
+ boolean sawOutputEOS = false;
+ int deadDecoderCounter = 0;
+ int samplenum = 0;
+ while (!sawOutputEOS && deadDecoderCounter < 100) {
+ if (!sawInputEOS) {
+ int inputBufIndex = codec.dequeueInputBuffer(kTimeOutUs);
+
+ if (inputBufIndex >= 0) {
+ ByteBuffer dstBuf = codecInputBuffers[inputBufIndex];
+
+ int sampleSize =
+ extractor.readSampleData(dstBuf, 0 /* offset */);
+// Log.i("@@@@", "read sample " + samplenum + ":" + extractor.getSampleFlags()
+// + " @ " + extractor.getSampleTime() + " size " + sampleSize);
+ samplenum++;
+
+ long presentationTimeUs = 0;
+
+ if (sampleSize < 0 || samplenum >= (stopatsample + 100)) {
+ Log.d(TAG, "saw input EOS.");
+ sawInputEOS = true;
+ sampleSize = 0;
+ } else {
+ presentationTimeUs = extractor.getSampleTime();
+ }
+
+ int flags = extractor.getSampleFlags();
+
+ codec.queueInputBuffer(
+ inputBufIndex,
+ 0 /* offset */,
+ sampleSize,
+ presentationTimeUs,
+ sawInputEOS ? MediaCodec.BUFFER_FLAG_END_OF_STREAM : 0);
+
+ if (!sawInputEOS) {
+ extractor.advance();
+ }
+ }
+ }
+
+ int res = codec.dequeueOutputBuffer(info, kTimeOutUs);
+
+ deadDecoderCounter++;
+ if (res >= 0) {
+
+ // Some decoders output a 0-sized buffer at the end. Disregard those.
+ if (info.size > 0) {
+ deadDecoderCounter = 0;
+
+ if (isAudio) {
+ // for audio, count the number of bytes that were decoded, not the number
+ // of access units
+ numframes += info.size;
+ } else {
+ // for video, count the number of video frames
+ long sum = checksum(codecOutputBuffers[res], info.size);
+ if (numframes < checksums.length) {
+ checksums[numframes] = sum;
+ }
+ numframes++;
+ }
+ }
+// Log.d(TAG, "got frame, size " + info.size + "/" + info.presentationTimeUs +
+// "/" + numframes + "/" + info.flags);
+
+ int outputBufIndex = res;
+ codec.releaseOutputBuffer(outputBufIndex, true /* render */);
+
+ if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
+ Log.d(TAG, "saw output EOS.");
+ sawOutputEOS = true;
+ }
+ } else if (res == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
+ codecOutputBuffers = codec.getOutputBuffers();
+
+ Log.d(TAG, "output buffers have changed.");
+ } else if (res == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
+ MediaFormat oformat = codec.getOutputFormat();
+
+ Log.d(TAG, "output format has changed to " + oformat);
+ } else {
+ Log.d(TAG, "no output");
+ }
+ }
+
+ codec.stop();
+ codec.release();
+ extractor.release();
+
+
+ // We now have checksums for every frame.
+ // Now decode again, but signal EOS right before an index frame, and ensure the frames
+ // prior to that are the same.
+
+ extractor = new MediaExtractor();
+ extractor.setDataSource(testFd.getFileDescriptor(), testFd.getStartOffset(),
+ testFd.getLength());
+
+ codec = MediaCodec.createDecoderByType(mime);
+// if (mime.contains("avc")) {
+// codec = MediaCodec.createByCodecName("OMX.google.h264.decoder");
+// } else if (mime.contains("3gpp")) {
+// codec = MediaCodec.createByCodecName("OMX.google.h263.decoder");
+// }
+ codec.configure(format, null /* surface */, null /* crypto */, 0 /* flags */);
+ codec.start();
+ codecInputBuffers = codec.getInputBuffers();
+ codecOutputBuffers = codec.getOutputBuffers();
+
+ extractor.selectTrack(0);
+
+ // start decoding
+ info = new MediaCodec.BufferInfo();
+ sawInputEOS = false;
+ sawOutputEOS = false;
+ deadDecoderCounter = 0;
+ samplenum = 0;
+ numframes = 0;
+ while (!sawOutputEOS && deadDecoderCounter < 100) {
+ if (!sawInputEOS) {
+ int inputBufIndex = codec.dequeueInputBuffer(kTimeOutUs);
+
+ if (inputBufIndex >= 0) {
+ ByteBuffer dstBuf = codecInputBuffers[inputBufIndex];
+
+ int sampleSize =
+ extractor.readSampleData(dstBuf, 0 /* offset */);
+// Log.i("@@@@", "read sample " + samplenum + ":" + extractor.getSampleFlags()
+// + " @ " + extractor.getSampleTime() + " size " + sampleSize);
+ samplenum++;
+
+ long presentationTimeUs = extractor.getSampleTime();
+
+ if (sampleSize < 0 || samplenum >= stopatsample) {
+ Log.d(TAG, "saw input EOS.");
+ sawInputEOS = true;
+ if (sampleSize < 0) {
+ sampleSize = 0;
+ }
+ }
+
+ int flags = extractor.getSampleFlags();
+
+ codec.queueInputBuffer(
+ inputBufIndex,
+ 0 /* offset */,
+ sampleSize,
+ presentationTimeUs,
+ sawInputEOS ? MediaCodec.BUFFER_FLAG_END_OF_STREAM : 0);
+
+ if (!sawInputEOS) {
+ extractor.advance();
+ }
+ }
+ }
+
+ int res = codec.dequeueOutputBuffer(info, kTimeOutUs);
+
+ deadDecoderCounter++;
+ if (res >= 0) {
+
+ // Some decoders output a 0-sized buffer at the end. Disregard those.
+ if (info.size > 0) {
+ deadDecoderCounter = 0;
+
+ if (isAudio) {
+ // for audio, count the number of bytes that were decoded, not the number
+ // of access units
+ numframes += info.size;
+ } else {
+ // for video, count the number of video frames
+ long sum = checksum(codecOutputBuffers[res], info.size);
+ if (numframes < checksums.length) {
+ assertEquals("frame data mismatch at frame " + numframes,
+ checksums[numframes], sum);
+ }
+ numframes++;
+ }
+ }
+// Log.d(TAG, "got frame, size " + info.size + "/" + info.presentationTimeUs +
+// "/" + numframes + "/" + info.flags);
+
+ int outputBufIndex = res;
+ codec.releaseOutputBuffer(outputBufIndex, true /* render */);
+
+ if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
+ Log.d(TAG, "saw output EOS.");
+ sawOutputEOS = true;
+ }
+ } else if (res == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
+ codecOutputBuffers = codec.getOutputBuffers();
+
+ Log.d(TAG, "output buffers have changed.");
+ } else if (res == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
+ MediaFormat oformat = codec.getOutputFormat();
+
+ Log.d(TAG, "output format has changed to " + oformat);
+ } else {
+ Log.d(TAG, "no output");
+ }
+ }
+
+ codec.stop();
+ codec.release();
+ extractor.release();
+
+ assertEquals("I!=O", samplenum, numframes);
+ assertTrue("last frame didn't have EOS", sawOutputEOS);
+ assertEquals(stopatsample, numframes);
+
+ testFd.close();
+ }
+
+ private long checksum(ByteBuffer buf, int size) {
+ assertTrue(size != 0);
+ assertTrue(size <= buf.capacity());
+ CRC32 crc = new CRC32();
+ int pos = buf.position();
+ buf.rewind();
+ for (int i = 0; i < buf.capacity(); i++) {
+ crc.update(buf.get());
+ }
+ buf.position(pos);
+ return crc.getValue();
+ }
+
}
diff --git a/tests/tests/media/src/android/media/cts/EncodeDecodeTest.java b/tests/tests/media/src/android/media/cts/EncodeDecodeTest.java
new file mode 100644
index 0000000..8a2e0b9
--- /dev/null
+++ b/tests/tests/media/src/android/media/cts/EncodeDecodeTest.java
@@ -0,0 +1,934 @@
+/*
+ * Copyright (C) 2013 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.media.cts;
+
+import android.graphics.SurfaceTexture;
+import android.media.MediaCodec;
+import android.media.MediaCodecInfo;
+import android.media.MediaCodecList;
+import android.media.MediaFormat;
+import android.opengl.EGL14;
+import android.opengl.GLES20;
+import android.opengl.GLES11Ext;
+import android.opengl.GLSurfaceView;
+import android.opengl.Matrix;
+import android.test.AndroidTestCase;
+import android.util.Log;
+import android.view.Surface;
+
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.FloatBuffer;
+import java.util.Arrays;
+
+import javax.microedition.khronos.egl.EGL10;
+import javax.microedition.khronos.egl.EGLConfig;
+import javax.microedition.khronos.egl.EGLContext;
+import javax.microedition.khronos.egl.EGLDisplay;
+import javax.microedition.khronos.egl.EGLSurface;
+import javax.microedition.khronos.opengles.GL;
+import javax.microedition.khronos.opengles.GL10;
+
+
+/**
+ * Generates a series of video frames, encodes them, decodes them, and tests for significant
+ * divergence from the original.
+ * <p>
+ * There are two ways to connect an encoder to a decoder. The first is to pass the output
+ * buffers from the encoder to the input buffers of the decoder, using ByteBuffer.put() to
+ * copy the bytes. With this approach, we need to watch for BUFFER_FLAG_CODEC_CONFIG, and
+ * if seen we use format.setByteBuffer("csd-0") followed by decoder.configure() to pass the
+ * meta-data through.
+ * <p>
+ * The second way is to write the buffers to a file and then stream it back in. With this
+ * approach it is necessary to use a MediaExtractor to retrieve the format info and skip past
+ * the meta-data.
+ * <p>
+ * The former can be done entirely in memory, but requires that the encoder and decoder
+ * operate simultaneously (the I/O buffers are owned by MediaCodec). The latter requires
+ * writing to disk, because MediaExtractor can only accept a file or URL as a source.
+ * <p>
+ * The direct encoder-to-decoder approach isn't currently tested elsewhere in this CTS
+ * package, so we use that here.
+ */
+public class EncodeDecodeTest extends AndroidTestCase {
+ private static final String TAG = "EncodeDecodeTest";
+ private static final boolean VERBOSE = false; // lots of logging
+ private static final boolean DEBUG_SAVE_FILE = false; // save copy of encoded movie
+ private static final String DEBUG_FILE_NAME_BASE = "/sdcard/test.";
+
+ // parameters for the encoder
+ private static final String MIME_TYPE = "video/avc"; // H.264 Advanced Video Coding
+ private static final int BIT_RATE = 1000000; // 1Mbps
+ private static final int FRAME_RATE = 15; // 15fps
+ private static final int IFRAME_INTERVAL = 10; // 10 seconds between I-frames
+
+ // movie length, in frames
+ private static final int NUM_FRAMES = 30; // two seconds of video
+
+ private static final int TEST_Y = 240; // YUV values for colored rect
+ private static final int TEST_U = 220;
+ private static final int TEST_V = 200;
+ private static final int TEST_R0 = 0; // RGB eqivalent of {0,0,0}
+ private static final int TEST_G0 = 136;
+ private static final int TEST_B0 = 0;
+ private static final int TEST_R1 = 255; // RGB equivalent of {240,220,200}
+ private static final int TEST_G1 = 166;
+ private static final int TEST_B1 = 255;
+
+ // size of a frame, in pixels
+ private int mWidth = -1;
+ private int mHeight = -1;
+
+
+ /**
+ * Tests streaming of AVC video through the encoder and decoder. Data is encoded from
+ * a series of byte[] buffers and decoded into ByteBuffers. The output is checked for
+ * validity.
+ */
+ public void testEncodeDecodeVideoFromBufferToBufferQCIF() throws Exception {
+ setSize(176, 144);
+ testEncodeDecodeVideoFromBuffer(false);
+ }
+ public void testEncodeDecodeVideoFromBufferToBufferQVGA() throws Exception {
+ setSize(320, 240);
+ testEncodeDecodeVideoFromBuffer(false);
+ }
+ public void testEncodeDecodeVideoFromBufferToBuffer720p() throws Exception {
+ setSize(1280, 720);
+ testEncodeDecodeVideoFromBuffer(false);
+ }
+
+ /**
+ * Tests streaming of AVC video through the encoder and decoder. Data is encoded from
+ * a series of byte[] buffers and decoded into Surfaces. The output is checked for
+ * validity but some frames may be dropped.
+ * <p>
+ * Because of the way SurfaceTexture.OnFrameAvailableListener works, we need to run this
+ * test on a thread that doesn't have a Looper configured. If we don't, the test will
+ * pass, but we won't actually test the output because we'll never receive the "frame
+ * available" notifications". The CTS test framework seems to be configuring a Looper on
+ * the test thread, so we have to hand control off to a new thread for the duration of
+ * the test.
+ */
+ public void testEncodeDecodeVideoFromBufferToSurfaceQCIF() throws Throwable {
+ setSize(176, 144);
+ BufferToSurfaceWrapper.runTest(this);
+ }
+ public void testEncodeDecodeVideoFromBufferToSurfaceQVGA() throws Throwable {
+ setSize(320, 240);
+ BufferToSurfaceWrapper.runTest(this);
+ }
+ public void testEncodeDecodeVideoFromBufferToSurface720p() throws Throwable {
+ setSize(1280, 720);
+ BufferToSurfaceWrapper.runTest(this);
+
+ }
+
+ /** Wraps testEncodeDecodeVideoFromBuffer(true) */
+ private static class BufferToSurfaceWrapper implements Runnable {
+ private Throwable mThrowable;
+ private EncodeDecodeTest mTest;
+
+ private BufferToSurfaceWrapper(EncodeDecodeTest test) {
+ mTest = test;
+ }
+
+ public void run() {
+ try {
+ mTest.testEncodeDecodeVideoFromBuffer(true);
+ } catch (Throwable th) {
+ mThrowable = th;
+ }
+ }
+
+ /**
+ * Entry point.
+ */
+ public static void runTest(EncodeDecodeTest obj) throws Throwable {
+ BufferToSurfaceWrapper wrapper = new BufferToSurfaceWrapper(obj);
+ Thread th = new Thread(wrapper, "codec test");
+ th.start();
+ th.join();
+ if (wrapper.mThrowable != null) {
+ throw wrapper.mThrowable;
+ }
+ }
+ }
+
+ /**
+ * Sets the desired frame size.
+ */
+ private void setSize(int width, int height) {
+ if ((width % 16) != 0 || (height % 16) != 0) {
+ Log.w(TAG, "WARNING: width or height not multiple of 16");
+ }
+ mWidth = width;
+ mHeight = height;
+ }
+
+ /**
+ * Tests encoding and subsequently decoding video from frames generated into a buffer.
+ * <p>
+ * We encode several frames of a video test pattern using MediaCodec, then decode the
+ * output with MediaCodec and do some simple checks.
+ * <p>
+ * See http://b.android.com/37769 for a discussion of input format pitfalls.
+ */
+ private void testEncodeDecodeVideoFromBuffer(boolean toSurface) throws Exception {
+ MediaCodecInfo codecInfo = selectCodec(MIME_TYPE);
+ if (codecInfo == null) {
+ // Don't fail CTS if they don't have an AVC codec (not here, anyway).
+ Log.e(TAG, "Unable to find an appropriate codec for " + MIME_TYPE);
+ return;
+ }
+ if (VERBOSE) Log.d(TAG, "found codec: " + codecInfo.getName());
+
+ int colorFormat = selectColorFormat(codecInfo, MIME_TYPE);
+ if (VERBOSE) Log.d(TAG, "found colorFormat: " + colorFormat);
+
+ // We avoid the device-specific limitations on width and height by using values that
+ // are multiples of 16, which all tested devices seem to be able to handle.
+ MediaFormat format = MediaFormat.createVideoFormat(MIME_TYPE, mWidth, mHeight);
+
+ // Set some properties. Failing to specify some of these can cause the MediaCodec
+ // configure() call to throw an unhelpful exception.
+ format.setInteger(MediaFormat.KEY_COLOR_FORMAT, colorFormat);
+ format.setInteger(MediaFormat.KEY_BIT_RATE, BIT_RATE);
+ format.setInteger(MediaFormat.KEY_FRAME_RATE, FRAME_RATE);
+ format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, IFRAME_INTERVAL);
+ if (VERBOSE) Log.d(TAG, "format: " + format);
+
+ // Create a MediaCodec for the desired codec, then configure it as an encoder with
+ // our desired properties.
+ MediaCodec encoder = MediaCodec.createByCodecName(codecInfo.getName());
+ encoder.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
+ encoder.start();
+
+ // Create a MediaCodec for the decoder, just based on the MIME type. The various
+ // format details will be passed through the csd-0 meta-data later on.
+ MediaCodec decoder = MediaCodec.createDecoderByType(MIME_TYPE);
+
+ try {
+ encodeDecodeVideoFromBuffer(encoder, colorFormat, decoder, toSurface);
+ } finally {
+ if (VERBOSE) Log.d(TAG, "releasing codecs");
+ encoder.stop();
+ decoder.stop();
+ encoder.release();
+ decoder.release();
+ }
+ }
+
+ /**
+ * Returns the first codec capable of encoding the specified MIME type, or null if no
+ * match was found.
+ */
+ private static MediaCodecInfo selectCodec(String mimeType) {
+ int numCodecs = MediaCodecList.getCodecCount();
+ for (int i = 0; i < numCodecs; i++) {
+ MediaCodecInfo codecInfo = MediaCodecList.getCodecInfoAt(i);
+
+ if (!codecInfo.isEncoder()) {
+ continue;
+ }
+
+ String[] types = codecInfo.getSupportedTypes();
+ for (int j = 0; j < types.length; j++) {
+ if (types[j].equalsIgnoreCase(mimeType)) {
+ return codecInfo;
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns a color format that is supported by the codec and by this test code. If no
+ * match is found, this throws a test failure -- the set of formats known to the test
+ * should be expanded for new platforms.
+ */
+ private static int selectColorFormat(MediaCodecInfo codecInfo, String mimeType) {
+ MediaCodecInfo.CodecCapabilities capabilities = codecInfo.getCapabilitiesForType(mimeType);
+ for (int i = 0; i < capabilities.colorFormats.length; i++) {
+ int colorFormat = capabilities.colorFormats[i];
+ if (isRecognizedFormat(colorFormat)) {
+ return colorFormat;
+ }
+ }
+ fail("couldn't find a good color format for " + codecInfo.getName() + " / " + mimeType);
+ return 0; // not reached
+ }
+
+ /**
+ * Returns true if this is a color format that this test code understands (i.e. we know how
+ * to read and generate frames in this format).
+ */
+ private static boolean isRecognizedFormat(int colorFormat) {
+ switch (colorFormat) {
+ // these are the formats we know how to handle for this test
+ case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Planar:
+ case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420PackedPlanar:
+ case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420SemiPlanar:
+ case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420PackedSemiPlanar:
+ case MediaCodecInfo.CodecCapabilities.COLOR_TI_FormatYUV420PackedSemiPlanar:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ /**
+ * Returns true if the specified color format is semi-planar YUV. Throws an exception
+ * if the color format is not recognized (e.g. not YUV).
+ */
+ private static boolean isSemiPlanarYUV(int colorFormat) {
+ switch (colorFormat) {
+ case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Planar:
+ case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420PackedPlanar:
+ return false;
+ case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420SemiPlanar:
+ case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420PackedSemiPlanar:
+ case MediaCodecInfo.CodecCapabilities.COLOR_TI_FormatYUV420PackedSemiPlanar:
+ return true;
+ default:
+ throw new RuntimeException("unknown format " + colorFormat);
+ }
+ }
+
+ /**
+ * Does the actual work for encoding frames from buffers of byte[].
+ */
+ private void encodeDecodeVideoFromBuffer(MediaCodec encoder, int encoderColorFormat,
+ MediaCodec decoder, boolean toSurface) {
+ final int TIMEOUT_USEC = 10000;
+ ByteBuffer[] encoderInputBuffers = encoder.getInputBuffers();
+ ByteBuffer[] encoderOutputBuffers = encoder.getOutputBuffers();
+ ByteBuffer[] decoderInputBuffers = null;
+ ByteBuffer[] decoderOutputBuffers = null;
+ MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();
+ int decoderColorFormat = -12345; // init to invalid value
+ int generateIndex = 0;
+ int checkIndex = 0;
+ boolean decoderConfigured = false;
+ SurfaceStuff surfaceStuff = null;
+
+ // The size of a frame of video data, in the formats we handle, is stride*sliceHeight
+ // for Y, and (stride/2)*(sliceHeight/2) for each of the Cb and Cr channels. Application
+ // of algebra and assuming that stride==width and sliceHeight==height yields:
+ byte[] frameData = new byte[mWidth * mHeight * 3 / 2];
+
+ // Just out of curiosity.
+ long rawSize = 0;
+ long encodedSize = 0;
+
+ // Save a copy to disk. Useful for debugging the test.
+ FileOutputStream outputStream = null;
+ if (DEBUG_SAVE_FILE) {
+ String fileName = DEBUG_FILE_NAME_BASE + mWidth + "x" + mHeight + ".mp4";
+ try {
+ outputStream = new FileOutputStream(fileName);
+ Log.d(TAG, "encoded output will be saved as " + fileName);
+ } catch (IOException ioe) {
+ Log.w(TAG, "Unable to create debug output file " + fileName);
+ throw new RuntimeException(ioe);
+ }
+ }
+
+ if (toSurface) {
+ surfaceStuff = new SurfaceStuff(mWidth, mHeight);
+ }
+
+ // Loop until the output side is done.
+ boolean inputDone = false;
+ boolean encoderDone = false;
+ boolean outputDone = false;
+ while (!outputDone) {
+ if (VERBOSE) Log.d(TAG, "loop");
+
+ // If we're not done submitting frames, generate a new one and submit it. By
+ // doing this on every loop we're working to ensure that the encoder always has
+ // work to do.
+ //
+ // We don't really want a timeout here, but sometimes there's a delay opening
+ // the encoder device, so a short timeout can keep us from spinning hard.
+ if (!inputDone) {
+ int inputBufIndex = encoder.dequeueInputBuffer(TIMEOUT_USEC);
+ if (VERBOSE) Log.d(TAG, "inputBufIndex=" + inputBufIndex);
+ if (inputBufIndex >= 0) {
+ long ptsUsec = generateIndex * 1000000 / FRAME_RATE;
+ if (generateIndex == NUM_FRAMES) {
+ // Send an empty frame with the end-of-stream flag set. If we set EOS
+ // on a frame with data, that frame data will be ignored, and the
+ // output will be short one frame.
+ encoder.queueInputBuffer(inputBufIndex, 0, 0, ptsUsec,
+ MediaCodec.BUFFER_FLAG_END_OF_STREAM);
+ inputDone = true;
+ if (VERBOSE) Log.d(TAG, "sent input EOS (with zero-length frame)");
+ } else {
+ generateFrame(generateIndex, encoderColorFormat, frameData);
+
+ ByteBuffer inputBuf = encoderInputBuffers[inputBufIndex];
+ // the buffer should be sized to hold one full frame
+ assertTrue(inputBuf.capacity() >= frameData.length);
+ inputBuf.clear();
+ inputBuf.put(frameData);
+
+ encoder.queueInputBuffer(inputBufIndex, 0, frameData.length, ptsUsec, 0);
+ if (VERBOSE) Log.d(TAG, "submitted frame " + generateIndex + " to enc");
+ }
+ generateIndex++;
+ } else {
+ // either all in use, or we timed out during initial setup
+ if (VERBOSE) Log.d(TAG, "input buffer not available");
+ }
+ }
+
+ // Check for output from the encoder. If there's no output yet, we either need to
+ // provide more input, or we need to wait for the encoder to work its magic. We
+ // can't actually tell which is the case, so if we can't get an output buffer right
+ // away we loop around and see if it wants more input.
+ //
+ // Once we get EOS from the encoder, we don't need to do this anymore.
+ if (!encoderDone) {
+ int encoderStatus = encoder.dequeueOutputBuffer(info, TIMEOUT_USEC);
+ if (encoderStatus == MediaCodec.INFO_TRY_AGAIN_LATER) {
+ // no output available yet
+ if (VERBOSE) Log.d(TAG, "no output from encoder available");
+ } else if (encoderStatus == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
+ // not expected for an encoder
+ encoderOutputBuffers = encoder.getOutputBuffers();
+ if (VERBOSE) Log.d(TAG, "encoder output buffers changed");
+ } else if (encoderStatus == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
+ // not expected for an encoder
+ MediaFormat newFormat = encoder.getOutputFormat();
+ if (VERBOSE) Log.d(TAG, "encoder output format changed: " + newFormat);
+ } else if (encoderStatus < 0) {
+ fail("unexpected result from encoder.dequeueOutputBuffer: " + encoderStatus);
+ } else { // encoderStatus >= 0
+ ByteBuffer encodedData = encoderOutputBuffers[encoderStatus];
+ if (encodedData == null) {
+ fail("encoderOutputBuffer " + encoderStatus + " was null");
+ }
+
+ // It's usually necessary to adjust the ByteBuffer values to match BufferInfo.
+ encodedData.position(info.offset);
+ encodedData.limit(info.offset + info.size);
+
+ encodedSize += info.size;
+ if (outputStream != null) {
+ byte[] data = new byte[info.size];
+ encodedData.get(data);
+ encodedData.position(info.offset);
+ try {
+ outputStream.write(data);
+ } catch (IOException ioe) {
+ Log.w(TAG, "failed writing debug data to file");
+ throw new RuntimeException(ioe);
+ }
+ }
+ if ((info.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0) {
+ // Codec config info. Only expected on first packet.
+ assertFalse(decoderConfigured);
+ MediaFormat format =
+ MediaFormat.createVideoFormat(MIME_TYPE, mWidth, mHeight);
+ format.setByteBuffer("csd-0", encodedData);
+ decoder.configure(format, toSurface ? surfaceStuff.getSurface() : null,
+ null, 0);
+ decoder.start();
+ decoderInputBuffers = decoder.getInputBuffers();
+ decoderOutputBuffers = decoder.getOutputBuffers();
+ decoderConfigured = true;
+ if (VERBOSE) Log.d(TAG, "decoder configured (" + info.size + " bytes)");
+ } else {
+ // Get a decoder input buffer, blocking until it's available.
+ assertTrue(decoderConfigured);
+ int inputBufIndex = decoder.dequeueInputBuffer(-1);
+ ByteBuffer inputBuf = decoderInputBuffers[inputBufIndex];
+ inputBuf.clear();
+ inputBuf.put(encodedData);
+ decoder.queueInputBuffer(inputBufIndex, 0, info.size, info.presentationTimeUs,
+ info.flags);
+
+ encoderDone = (info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0;
+ if (VERBOSE) Log.d(TAG, "passed " + info.size + " bytes to decoder"
+ + (encoderDone ? " (EOS)" : ""));
+ }
+
+ encoder.releaseOutputBuffer(encoderStatus, false);
+ }
+ }
+
+ // Check for output from the decoder. We want to do this on every loop to avoid
+ // the possibility of stalling the pipeline. We use a short timeout to avoid
+ // burning CPU if the decoder is hard at work but the next frame isn't quite ready.
+ //
+ // If we're decoding to a Surface, we'll get notified here as usual but the
+ // ByteBuffer references will be null. The data is sent to Surface instead.
+ if (decoderConfigured) {
+ int decoderStatus = decoder.dequeueOutputBuffer(info, TIMEOUT_USEC);
+ if (decoderStatus == MediaCodec.INFO_TRY_AGAIN_LATER) {
+ // no output available yet
+ if (VERBOSE) Log.d(TAG, "no output from decoder available");
+ } else if (decoderStatus == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
+ if (VERBOSE) Log.d(TAG, "decoder output buffers changed");
+ decoderOutputBuffers = decoder.getOutputBuffers();
+ } else if (decoderStatus == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
+ // this happens before the first frame is returned
+ MediaFormat decoderOutputFormat = decoder.getOutputFormat();
+ decoderColorFormat =
+ decoderOutputFormat.getInteger(MediaFormat.KEY_COLOR_FORMAT);
+ if (VERBOSE) Log.d(TAG, "decoder output format changed: " +
+ decoderOutputFormat);
+ } else if (decoderStatus < 0) {
+ fail("unexpected result from deocder.dequeueOutputBuffer: " + decoderStatus);
+ } else { // decoderStatus >= 0
+ if (!toSurface) {
+ ByteBuffer outputFrame = decoderOutputBuffers[decoderStatus];
+
+ outputFrame.position(info.offset);
+ outputFrame.limit(info.offset + info.size);
+
+ rawSize += info.size;
+ if (info.size == 0) {
+ if (VERBOSE) Log.d(TAG, "got empty frame");
+ } else {
+ if (VERBOSE) Log.d(TAG, "decoded, checking frame " + checkIndex);
+ checkFrame(checkIndex++, decoderColorFormat, outputFrame);
+ }
+
+ if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
+ if (VERBOSE) Log.d(TAG, "output EOS");
+ outputDone = true;
+ }
+ decoder.releaseOutputBuffer(decoderStatus, false /*render*/);
+ } else {
+ if (VERBOSE) Log.d(TAG, "surface decoder given buffer " + decoderStatus +
+ " (size=" + info.size + ")");
+ rawSize += info.size;
+ if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
+ if (VERBOSE) Log.d(TAG, "output EOS");
+ outputDone = true;
+ }
+
+ boolean doRender = (info.size != 0);
+
+ // As soon as we call releaseOutputBuffer, the buffer will be forwarded
+ // to SurfaceTexture to convert to a texture. The API doesn't guarantee
+ // that the texture will be available before the call returns, so we
+ // need to wait for the onFrameAvailable callback to fire.
+ decoder.releaseOutputBuffer(decoderStatus, doRender);
+ if (doRender) {
+ if (VERBOSE) Log.d(TAG, "awaiting frame " + checkIndex);
+ surfaceStuff.awaitNewImage(checkIndex++);
+ }
+ }
+ }
+ }
+ }
+
+ if (checkIndex != NUM_FRAMES) {
+ fail("expected " + NUM_FRAMES + " frames, only decoded " + checkIndex);
+ }
+
+ if (VERBOSE) Log.d(TAG, "decoded " + checkIndex + " frames at "
+ + mWidth + "x" + mHeight + ": raw=" + rawSize + ", enc=" + encodedSize);
+ if (outputStream != null) {
+ try {
+ outputStream.close();
+ } catch (IOException ioe) {
+ Log.w(TAG, "failed closing debug file");
+ throw new RuntimeException(ioe);
+ }
+ }
+ }
+
+ /**
+ * Generates data for frame N into the supplied buffer. We have an 8-frame animation
+ * sequence that wraps around. It looks like this:
+ * <pre>
+ * 0 1 2 3
+ * 7 6 5 4
+ * </pre>
+ * We draw one of the eight rectangles and leave the rest set to the zero-fill color.
+ */
+ private void generateFrame(int frameIndex, int colorFormat, byte[] frameData) {
+ final int HALF_WIDTH = mWidth / 2;
+ boolean semiPlanar = isSemiPlanarYUV(colorFormat);
+
+ // Set to zero. In YUV this is a dull green.
+ Arrays.fill(frameData, (byte) 0);
+
+ int startX, startY, countX, countY;
+
+ frameIndex %= 8;
+ //frameIndex = (frameIndex / 8) % 8; // use this instead for debug -- easier to see
+ if (frameIndex < 4) {
+ startX = frameIndex * (mWidth / 4);
+ startY = 0;
+ } else {
+ startX = (7 - frameIndex) * (mWidth / 4);
+ startY = mHeight / 2;
+ }
+
+ for (int y = startY + (mHeight/2) - 1; y >= startY; --y) {
+ for (int x = startX + (mWidth/4) - 1; x >= startX; --x) {
+ if (semiPlanar) {
+ // full-size Y, followed by CbCr pairs at half resolution
+ // e.g. Nexus 4 OMX.qcom.video.encoder.avc COLOR_FormatYUV420SemiPlanar
+ // e.g. Galaxy Nexus OMX.TI.DUCATI1.VIDEO.H264E
+ // OMX_TI_COLOR_FormatYUV420PackedSemiPlanar
+ frameData[y * mWidth + x] = (byte) TEST_Y;
+ if ((x & 0x01) == 0 && (y & 0x01) == 0) {
+ frameData[mWidth*mHeight + y * HALF_WIDTH + x] = (byte) TEST_U;
+ frameData[mWidth*mHeight + y * HALF_WIDTH + x + 1] = (byte) TEST_V;
+ }
+ } else {
+ // full-size Y, followed by quarter-size Cb and quarter-size Cr
+ // e.g. Nexus 10 OMX.Exynos.AVC.Encoder COLOR_FormatYUV420Planar
+ // e.g. Nexus 7 OMX.Nvidia.h264.encoder COLOR_FormatYUV420Planar
+ frameData[y * mWidth + x] = (byte) TEST_Y;
+ if ((x & 0x01) == 0 && (y & 0x01) == 0) {
+ frameData[mWidth*mHeight + (y/2) * HALF_WIDTH + (x/2)] = (byte) TEST_U;
+ frameData[mWidth*mHeight + HALF_WIDTH * (mHeight / 2) +
+ (y/2) * HALF_WIDTH + (x/2)] = (byte) TEST_V;
+ }
+ }
+ }
+ }
+
+ if (false) {
+ // make sure that generate and check agree
+ Log.d(TAG, "SPOT CHECK");
+ checkFrame(frameIndex, colorFormat, ByteBuffer.wrap(frameData));
+ Log.d(TAG, "SPOT CHECK DONE");
+ }
+ }
+
+ /**
+ * Performs a simple check to see if the frame is more or less right.
+ * <p>
+ * See {@link generateFrame} for a description of the layout. The idea is to sample
+ * one pixel from the middle of the 8 regions, and verify that the correct one has
+ * the non-background color. We can't know exactly what the video encoder has done
+ * with our frames, so we just check to see if it looks like more or less the right thing.
+ * <p>
+ * Throws a failure if the frame looks wrong.
+ */
+ private void checkFrame(int frameIndex, int colorFormat, ByteBuffer frameData) {
+ // Check for color formats we don't understand. There is no requirement for video
+ // decoders to use a "mundane" format, so we just give a pass on proprietary formats.
+ // e.g. Nexus 4 0x7FA30C03 OMX_QCOM_COLOR_FormatYUV420PackedSemiPlanar64x32Tile2m8ka
+ if (!isRecognizedFormat(colorFormat)) {
+ Log.d(TAG, "unable to check frame contents for colorFormat=" +
+ Integer.toHexString(colorFormat));
+ return;
+ }
+
+ final int HALF_WIDTH = mWidth / 2;
+ boolean frameFailed = false;
+ boolean semiPlanar = isSemiPlanarYUV(colorFormat);
+
+ for (int i = 0; i < 8; i++) {
+ int x, y;
+ if (i < 4) {
+ x = i * (mWidth / 4) + (mWidth / 8);
+ y = mHeight / 4;
+ } else {
+ x = (7 - i) * (mWidth / 4) + (mWidth / 8);
+ y = (mHeight * 3) / 4;
+ }
+
+ int testY, testU, testV;
+ if (semiPlanar) {
+ // Galaxy Nexus uses OMX_TI_COLOR_FormatYUV420PackedSemiPlanar
+ testY = frameData.get(y * mWidth + x) & 0xff;
+ testU = frameData.get(mWidth*mHeight + 2*(y/2) * HALF_WIDTH + 2*(x/2)) & 0xff;
+ testV = frameData.get(mWidth*mHeight + 2*(y/2) * HALF_WIDTH + 2*(x/2) + 1) & 0xff;
+ } else {
+ // Nexus 10, Nexus 7 use COLOR_FormatYUV420Planar
+ testY = frameData.get(y * mWidth + x) & 0xff;
+ testU = frameData.get(mWidth*mHeight + (y/2) * HALF_WIDTH + (x/2)) & 0xff;
+ testV = frameData.get(mWidth*mHeight + HALF_WIDTH * (mHeight / 2) +
+ (y/2) * HALF_WIDTH + (x/2)) & 0xff;
+ }
+
+ boolean failed = false;
+ if (i == frameIndex % 8) {
+ failed = !isColorClose(testY, TEST_Y) ||
+ !isColorClose(testU, TEST_U) ||
+ !isColorClose(testV, TEST_V);
+ } else {
+ // should be our zeroed-out buffer
+ failed = !isColorClose(testY, 0) ||
+ !isColorClose(testU, 0) ||
+ !isColorClose(testV, 0);
+ }
+ if (failed) {
+ Log.w(TAG, "Bad frame " + frameIndex + " (rect=" + i + ": Y=" + testY +
+ " U=" + testU + " V=" + testV + ")");
+ frameFailed = true;
+ }
+ }
+
+ if (frameFailed) {
+ fail("bad frame (" + frameIndex + ")");
+ }
+ }
+
+ /**
+ * Returns true if the actual color value is close to the expected color value.
+ */
+ static boolean isColorClose(int actual, int expected) {
+ if (expected < 5) {
+ return actual < (expected + 5);
+ } else if (expected > 250) {
+ return actual > (expected - 5);
+ } else {
+ return actual > (expected - 5) && actual < (expected + 5);
+ }
+ }
+
+ /**
+ * Holds state associated with a Surface used for output.
+ * <p>
+ * By default, the Surface will be using a BufferQueue in asynchronous mode, so we
+ * will likely miss a number of frames.
+ */
+ private static class SurfaceStuff implements SurfaceTexture.OnFrameAvailableListener {
+ private static final int EGL_OPENGL_ES2_BIT = 4;
+
+ private EGL10 mEGL;
+ private EGLDisplay mEGLDisplay;
+ private EGLContext mEGLContext;
+ private EGLSurface mEGLSurface;
+
+ private SurfaceTexture mSurfaceTexture;
+ private Surface mSurface;
+
+ private Object mFrameSyncObject = new Object(); // guards mFrameAvailable
+ private boolean mFrameAvailable;
+
+ private int mWidth;
+ private int mHeight;
+
+ private TextureRender mTextureRender;
+
+ public SurfaceStuff(int width, int height) {
+ mWidth = width;
+ mHeight = height;
+
+ eglSetup();
+
+ mTextureRender = new TextureRender();
+ mTextureRender.surfaceCreated();
+
+ // Even if we don't access the SurfaceTexture after the constructor returns, we
+ // still need to keep a reference to it. The Surface doesn't retain a reference
+ // at the Java level, so if we don't either then the object can get GCed, which
+ // causes the native finalizer to run.
+ if (VERBOSE) Log.d(TAG, "textureID=" + mTextureRender.getTextureId());
+ mSurfaceTexture = new SurfaceTexture(mTextureRender.getTextureId());
+
+ // This doesn't work if SurfaceStuff is created on the thread that CTS started for
+ // these test cases.
+ //
+ // The CTS-created thread has a Looper, and the SurfaceTexture constructor will
+ // create a Handler that uses it. The "frame available" message is delivered
+ // there, but since we're not a Looper-based thread we'll never see it. For
+ // this to do anything useful, SurfaceStuff must be created on a thread without
+ // a Looper, so that SurfaceTexture uses the main application Looper instead.
+ //
+ // Java language note: passing "this" out of a constructor is generally unwise,
+ // but we should be able to get away with it here.
+ mSurfaceTexture.setOnFrameAvailableListener(this);
+
+ mSurface = new Surface(mSurfaceTexture);
+ }
+
+ /**
+ * Prepares EGL. We want a GLES 2.0 context and a surface that supports pbuffer.
+ */
+ private void eglSetup() {
+ mEGL = (EGL10)EGLContext.getEGL();
+ mEGLDisplay = mEGL.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
+ if (!mEGL.eglInitialize(mEGLDisplay, null)) {
+ fail("unable to initialize EGL10");
+ }
+
+ // Configure surface for pbuffer and OpenGL ES 2.0. We want enough RGB bits
+ // to be able to tell if the frame is reasonable.
+ int[] attribList = {
+ EGL10.EGL_RED_SIZE, 8,
+ EGL10.EGL_GREEN_SIZE, 8,
+ EGL10.EGL_BLUE_SIZE, 8,
+ EGL10.EGL_SURFACE_TYPE, EGL10.EGL_PBUFFER_BIT,
+ EGL10.EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
+ EGL10.EGL_NONE
+ };
+ EGLConfig[] configs = new EGLConfig[1];
+ int[] numConfigs = new int[1];
+ if (!mEGL.eglChooseConfig(mEGLDisplay, attribList, configs, 1, numConfigs)) {
+ fail("unable to find RGB888+pbuffer EGL config");
+ }
+
+ // Configure context for OpenGL ES 2.0.
+ int[] attrib_list = {
+ EGL14.EGL_CONTEXT_CLIENT_VERSION, 2,
+ EGL10.EGL_NONE
+ };
+ mEGLContext = mEGL.eglCreateContext(mEGLDisplay, configs[0], EGL10.EGL_NO_CONTEXT,
+ attrib_list);
+ checkEglError("eglCreateContext");
+ assertNotNull(mEGLContext);
+
+ // Create a pbuffer surface. By using this for output, we can use glReadPixels
+ // to test values in the output.
+ int[] surfaceAttribs = {
+ EGL10.EGL_WIDTH, mWidth,
+ EGL10.EGL_HEIGHT, mHeight,
+ EGL10.EGL_NONE
+ };
+ mEGLSurface = mEGL.eglCreatePbufferSurface(mEGLDisplay, configs[0], surfaceAttribs);
+ checkEglError("eglCreatePbufferSurface");
+ assertNotNull(mEGLSurface);
+
+ if (!mEGL.eglMakeCurrent(mEGLDisplay, mEGLSurface, mEGLSurface, mEGLContext)) {
+ fail("eglMakeCurrent failed");
+ }
+ }
+
+ /**
+ * Checks for EGL errors.
+ */
+ private void checkEglError(String msg) {
+ boolean failed = false;
+ int error;
+ while ((error = mEGL.eglGetError()) != EGL10.EGL_SUCCESS) {
+ Log.e(TAG, msg + ": EGL error: 0x" + Integer.toHexString(error));
+ failed = true;
+ }
+ if (failed) {
+ fail("EGL error encountered (see log)");
+ }
+ }
+
+
+ /**
+ * Returns the Surface that the MediaCodec will draw onto.
+ */
+ public Surface getSurface() {
+ return mSurface;
+ }
+
+ /**
+ * Latches the next buffer into the texture, and checks it for validity. Must be
+ * called from the thread that created the SurfaceStuff object, after the
+ * onFrameAvailable callback has signaled that new data is available.
+ */
+ public void awaitNewImage(int frameIndex) {
+ final int TIMEOUT_MS = 500;
+
+ synchronized (mFrameSyncObject) {
+ while (!mFrameAvailable) {
+ try {
+ // Wait for onFrameAvailable() to signal us. Use a timeout to avoid
+ // stalling the test if it doesn't arrive.
+ mFrameSyncObject.wait(TIMEOUT_MS);
+ if (!mFrameAvailable) {
+ // TODO: if "spurious wakeup", continue while loop
+ fail("Surface frame wait timed out");
+ }
+ } catch (InterruptedException ie) {
+ // shouldn't happen
+ throw new RuntimeException(ie);
+ }
+ }
+ mFrameAvailable = false;
+ }
+
+ // Latch the data, render it to the Surface, then check how it looks.
+ mTextureRender.checkGlError("before updateTexImage");
+ mSurfaceTexture.updateTexImage();
+ mTextureRender.drawFrame(mSurfaceTexture);
+ checkSurfaceFrame(frameIndex);
+ }
+
+ @Override
+ public void onFrameAvailable(SurfaceTexture st) {
+ if (VERBOSE) Log.d(TAG, "new frame available");
+ synchronized (mFrameSyncObject) {
+ if (mFrameAvailable) {
+ Log.e(TAG, "mFrameAvailable already set, frame could be dropped");
+ fail("dropped a frame");
+ }
+ mFrameAvailable = true;
+ mFrameSyncObject.notifyAll();
+ }
+ }
+
+
+ /**
+ * Checks the frame for correctness.
+ */
+ private void checkSurfaceFrame(int frameIndex) {
+ ByteBuffer pixelBuf = ByteBuffer.allocateDirect(4); // TODO - reuse this
+ boolean frameFailed = false;
+
+ for (int i = 0; i < 8; i++) {
+ // Note the coordinates are inverted on the Y-axis in GL.
+ int x, y;
+ if (i < 4) {
+ x = i * (mWidth / 4) + (mWidth / 8);
+ y = (mHeight * 3) / 4;
+ } else {
+ x = (7 - i) * (mWidth / 4) + (mWidth / 8);
+ y = mHeight / 4;
+ }
+
+ GLES20.glReadPixels(x, y, 1, 1, GL10.GL_RGBA, GL10.GL_UNSIGNED_BYTE, pixelBuf);
+ int r = pixelBuf.get(0) & 0xff;
+ int g = pixelBuf.get(1) & 0xff;
+ int b = pixelBuf.get(2) & 0xff;
+
+ boolean failed = false;
+ if (i == frameIndex % 8) {
+ // colored rect
+ failed = !isColorClose(r, TEST_R1) ||
+ !isColorClose(g, TEST_G1) ||
+ !isColorClose(b, TEST_B1);
+ } else {
+ // zero background color
+ failed = !isColorClose(r, TEST_R0) ||
+ !isColorClose(g, TEST_G0) ||
+ !isColorClose(b, TEST_B0);
+ }
+ if (failed) {
+ Log.w(TAG, "Bad frame " + frameIndex + " (rect=" + i + ": r=" + r +
+ " g=" + g + " b=" + b + ")");
+ frameFailed = true;
+ }
+ }
+
+ if (frameFailed) {
+ fail("bad frame (" + frameIndex + ")");
+ }
+ }
+ }
+}
diff --git a/tests/tests/media/src/android/media/cts/MediaPlayerTest.java b/tests/tests/media/src/android/media/cts/MediaPlayerTest.java
index dda78f8..c6d2985 100644
--- a/tests/tests/media/src/android/media/cts/MediaPlayerTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaPlayerTest.java
@@ -750,7 +750,7 @@
return;
}
for (int i = 0; i < trackInfos.length; ++i) {
- if (trackInfos[i] == null) continue;
+ assertTrue(trackInfos[i] != null);
if (trackInfos[i].getTrackType() ==
MediaPlayer.TrackInfo.MEDIA_TRACK_TYPE_TIMEDTEXT) {
mTimedTextTrackIndex.add(i);
@@ -907,6 +907,35 @@
mMediaPlayer.stop();
}
+ public void testGetTrackInfo() throws Exception {
+ loadResource(R.raw.testvideo_with_2_subtitles);
+ loadSubtitleSource(R.raw.test_subtitle1_srt);
+ loadSubtitleSource(R.raw.test_subtitle2_srt);
+ mMediaPlayer.prepare();
+ mMediaPlayer.start();
+
+ readTimedTextTracks();
+ selectSubtitleTrack(2);
+
+ int count = 0;
+ MediaPlayer.TrackInfo[] trackInfos = mMediaPlayer.getTrackInfo();
+ assertTrue(trackInfos != null && trackInfos.length != 0);
+ for (int i = 0; i < trackInfos.length; ++i) {
+ assertTrue(trackInfos[i] != null);
+ if (trackInfos[i].getTrackType() == MediaPlayer.TrackInfo.MEDIA_TRACK_TYPE_TIMEDTEXT) {
+ String trackLanguage = trackInfos[i].getLanguage();
+ assertTrue(trackLanguage != null);
+ trackLanguage.trim();
+ Log.d(LOG_TAG, "track info lang: " + trackLanguage);
+ assertTrue("Should not see empty track language with our test data.",
+ trackLanguage.length() > 0);
+ count++;
+ }
+ }
+ // There are 4 subtitle tracks in total in our test data.
+ assertEquals(4, count);
+ }
+
public void testCallback() throws Throwable {
final int mp4Duration = 8484;
diff --git a/tests/tests/media/src/android/media/cts/TextureRender.java b/tests/tests/media/src/android/media/cts/TextureRender.java
new file mode 100644
index 0000000..0dcda34
--- /dev/null
+++ b/tests/tests/media/src/android/media/cts/TextureRender.java
@@ -0,0 +1,235 @@
+/*
+ * Copyright (C) 2013 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.media.cts;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.FloatBuffer;
+
+import javax.microedition.khronos.egl.EGLConfig;
+import javax.microedition.khronos.opengles.GL10;
+
+import android.graphics.SurfaceTexture;
+import android.opengl.GLES11Ext;
+import android.opengl.GLES20;
+import android.opengl.GLSurfaceView;
+import android.opengl.Matrix;
+import android.util.Log;
+
+
+/**
+ * Code for rendering a texture onto a surface using OpenGL ES 2.0.
+ */
+class TextureRender {
+ private static final String TAG = "TextureRender";
+
+ private static final int FLOAT_SIZE_BYTES = 4;
+ private static final int TRIANGLE_VERTICES_DATA_STRIDE_BYTES = 5 * FLOAT_SIZE_BYTES;
+ private static final int TRIANGLE_VERTICES_DATA_POS_OFFSET = 0;
+ private static final int TRIANGLE_VERTICES_DATA_UV_OFFSET = 3;
+ private final float[] mTriangleVerticesData = {
+ // X, Y, Z, U, V
+ -1.0f, -1.0f, 0, 0.f, 0.f,
+ 1.0f, -1.0f, 0, 1.f, 0.f,
+ -1.0f, 1.0f, 0, 0.f, 1.f,
+ 1.0f, 1.0f, 0, 1.f, 1.f,
+ };
+
+ private FloatBuffer mTriangleVertices;
+
+ private static final String VERTEX_SHADER =
+ "uniform mat4 uMVPMatrix;\n" +
+ "uniform mat4 uSTMatrix;\n" +
+ "attribute vec4 aPosition;\n" +
+ "attribute vec4 aTextureCoord;\n" +
+ "varying vec2 vTextureCoord;\n" +
+ "void main() {\n" +
+ " gl_Position = uMVPMatrix * aPosition;\n" +
+ " vTextureCoord = (uSTMatrix * aTextureCoord).xy;\n" +
+ "}\n";
+
+ private static final String FRAGMENT_SHADER =
+ "#extension GL_OES_EGL_image_external : require\n" +
+ "precision mediump float;\n" +
+ "varying vec2 vTextureCoord;\n" +
+ "uniform samplerExternalOES sTexture;\n" +
+ "void main() {\n" +
+ " gl_FragColor = texture2D(sTexture, vTextureCoord);\n" +
+ "}\n";
+
+ private float[] mMVPMatrix = new float[16];
+ private float[] mSTMatrix = new float[16];
+
+ private int mProgram;
+ private int mTextureID = -12345;
+ private int muMVPMatrixHandle;
+ private int muSTMatrixHandle;
+ private int maPositionHandle;
+ private int maTextureHandle;
+
+ public TextureRender() {
+ mTriangleVertices = ByteBuffer.allocateDirect(
+ mTriangleVerticesData.length * FLOAT_SIZE_BYTES)
+ .order(ByteOrder.nativeOrder()).asFloatBuffer();
+ mTriangleVertices.put(mTriangleVerticesData).position(0);
+
+ Matrix.setIdentityM(mSTMatrix, 0);
+ }
+
+ public int getTextureId() {
+ return mTextureID;
+ }
+
+ public void drawFrame(SurfaceTexture st) {
+ checkGlError("onDrawFrame start");
+ st.getTransformMatrix(mSTMatrix);
+
+ GLES20.glClearColor(0.0f, 1.0f, 0.0f, 1.0f);
+ GLES20.glClear(GLES20.GL_DEPTH_BUFFER_BIT | GLES20.GL_COLOR_BUFFER_BIT);
+
+ GLES20.glUseProgram(mProgram);
+ checkGlError("glUseProgram");
+
+ GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
+ GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, mTextureID);
+
+ mTriangleVertices.position(TRIANGLE_VERTICES_DATA_POS_OFFSET);
+ GLES20.glVertexAttribPointer(maPositionHandle, 3, GLES20.GL_FLOAT, false,
+ TRIANGLE_VERTICES_DATA_STRIDE_BYTES, mTriangleVertices);
+ checkGlError("glVertexAttribPointer maPosition");
+ GLES20.glEnableVertexAttribArray(maPositionHandle);
+ checkGlError("glEnableVertexAttribArray maPositionHandle");
+
+ mTriangleVertices.position(TRIANGLE_VERTICES_DATA_UV_OFFSET);
+ GLES20.glVertexAttribPointer(maTextureHandle, 3, GLES20.GL_FLOAT, false,
+ TRIANGLE_VERTICES_DATA_STRIDE_BYTES, mTriangleVertices);
+ checkGlError("glVertexAttribPointer maTextureHandle");
+ GLES20.glEnableVertexAttribArray(maTextureHandle);
+ checkGlError("glEnableVertexAttribArray maTextureHandle");
+
+ Matrix.setIdentityM(mMVPMatrix, 0);
+ GLES20.glUniformMatrix4fv(muMVPMatrixHandle, 1, false, mMVPMatrix, 0);
+ GLES20.glUniformMatrix4fv(muSTMatrixHandle, 1, false, mSTMatrix, 0);
+
+ GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
+ checkGlError("glDrawArrays");
+ GLES20.glFinish();
+ }
+
+ public void surfaceCreated() {
+ mProgram = createProgram(VERTEX_SHADER, FRAGMENT_SHADER);
+ if (mProgram == 0) {
+ Log.e(TAG, "failed creating program");
+ return;
+ }
+ maPositionHandle = GLES20.glGetAttribLocation(mProgram, "aPosition");
+ checkGlError("glGetAttribLocation aPosition");
+ if (maPositionHandle == -1) {
+ throw new RuntimeException("Could not get attrib location for aPosition");
+ }
+ maTextureHandle = GLES20.glGetAttribLocation(mProgram, "aTextureCoord");
+ checkGlError("glGetAttribLocation aTextureCoord");
+ if (maTextureHandle == -1) {
+ throw new RuntimeException("Could not get attrib location for aTextureCoord");
+ }
+
+ muMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix");
+ checkGlError("glGetUniformLocation uMVPMatrix");
+ if (muMVPMatrixHandle == -1) {
+ throw new RuntimeException("Could not get attrib location for uMVPMatrix");
+ }
+
+ muSTMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uSTMatrix");
+ checkGlError("glGetUniformLocation uSTMatrix");
+ if (muSTMatrixHandle == -1) {
+ throw new RuntimeException("Could not get attrib location for uSTMatrix");
+ }
+
+
+ int[] textures = new int[1];
+ GLES20.glGenTextures(1, textures, 0);
+
+ mTextureID = textures[0];
+ GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, mTextureID);
+ checkGlError("glBindTexture mTextureID");
+
+ GLES20.glTexParameterf(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MIN_FILTER,
+ GLES20.GL_NEAREST);
+ GLES20.glTexParameterf(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MAG_FILTER,
+ GLES20.GL_LINEAR);
+ GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_WRAP_S,
+ GLES20.GL_CLAMP_TO_EDGE);
+ GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_WRAP_T,
+ GLES20.GL_CLAMP_TO_EDGE);
+ checkGlError("glTexParameter");
+ }
+
+ private int loadShader(int shaderType, String source) {
+ int shader = GLES20.glCreateShader(shaderType);
+ checkGlError("glCreateShader type=" + shaderType);
+ GLES20.glShaderSource(shader, source);
+ GLES20.glCompileShader(shader);
+ int[] compiled = new int[1];
+ GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compiled, 0);
+ if (compiled[0] == 0) {
+ Log.e(TAG, "Could not compile shader " + shaderType + ":");
+ Log.e(TAG, GLES20.glGetShaderInfoLog(shader));
+ GLES20.glDeleteShader(shader);
+ shader = 0;
+ }
+ return shader;
+ }
+
+ private int createProgram(String vertexSource, String fragmentSource) {
+ int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexSource);
+ if (vertexShader == 0) {
+ return 0;
+ }
+ int pixelShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentSource);
+ if (pixelShader == 0) {
+ return 0;
+ }
+
+ int program = GLES20.glCreateProgram();
+ checkGlError("glCreateProgram");
+ if (program == 0) {
+ Log.e(TAG, "Could not create program");
+ }
+ GLES20.glAttachShader(program, vertexShader);
+ checkGlError("glAttachShader");
+ GLES20.glAttachShader(program, pixelShader);
+ checkGlError("glAttachShader");
+ GLES20.glLinkProgram(program);
+ int[] linkStatus = new int[1];
+ GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, linkStatus, 0);
+ if (linkStatus[0] != GLES20.GL_TRUE) {
+ Log.e(TAG, "Could not link program: ");
+ Log.e(TAG, GLES20.glGetProgramInfoLog(program));
+ GLES20.glDeleteProgram(program);
+ program = 0;
+ }
+ return program;
+ }
+
+ public void checkGlError(String op) {
+ int error;
+ while ((error = GLES20.glGetError()) != GLES20.GL_NO_ERROR) {
+ Log.e(TAG, op + ": glError " + error);
+ throw new RuntimeException(op + ": glError " + error);
+ }
+ }
+}
diff --git a/tests/tests/media/src/android/media/cts/VideoSurfaceView.java b/tests/tests/media/src/android/media/cts/VideoSurfaceView.java
index 943a56b..229c6f6 100644
--- a/tests/tests/media/src/android/media/cts/VideoSurfaceView.java
+++ b/tests/tests/media/src/android/media/cts/VideoSurfaceView.java
@@ -19,9 +19,6 @@
import com.android.cts.media.R;
import java.io.IOException;
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
-import java.nio.FloatBuffer;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;
@@ -29,7 +26,6 @@
import android.content.Context;
import android.graphics.SurfaceTexture;
import android.media.MediaPlayer;
-import android.opengl.GLES20;
import android.opengl.GLSurfaceView;
import android.opengl.Matrix;
import android.util.Log;
@@ -73,68 +69,22 @@
}
}
+ /**
+ * A GLSurfaceView implementation that wraps TextureRender. Used to render frames from a
+ * video decoder to a View.
+ */
private static class VideoRender
- implements GLSurfaceView.Renderer, SurfaceTexture.OnFrameAvailableListener {
+ implements GLSurfaceView.Renderer, SurfaceTexture.OnFrameAvailableListener {
private static String TAG = "VideoRender";
- private static final int FLOAT_SIZE_BYTES = 4;
- private static final int TRIANGLE_VERTICES_DATA_STRIDE_BYTES = 5 * FLOAT_SIZE_BYTES;
- private static final int TRIANGLE_VERTICES_DATA_POS_OFFSET = 0;
- private static final int TRIANGLE_VERTICES_DATA_UV_OFFSET = 3;
- private final float[] mTriangleVerticesData = {
- // X, Y, Z, U, V
- -1.0f, -1.0f, 0, 0.f, 0.f,
- 1.0f, -1.0f, 0, 1.f, 0.f,
- -1.0f, 1.0f, 0, 0.f, 1.f,
- 1.0f, 1.0f, 0, 1.f, 1.f,
- };
-
- private FloatBuffer mTriangleVertices;
-
- private final String mVertexShader =
- "uniform mat4 uMVPMatrix;\n" +
- "uniform mat4 uSTMatrix;\n" +
- "attribute vec4 aPosition;\n" +
- "attribute vec4 aTextureCoord;\n" +
- "varying vec2 vTextureCoord;\n" +
- "void main() {\n" +
- " gl_Position = uMVPMatrix * aPosition;\n" +
- " vTextureCoord = (uSTMatrix * aTextureCoord).xy;\n" +
- "}\n";
-
- private final String mFragmentShader =
- "#extension GL_OES_EGL_image_external : require\n" +
- "precision mediump float;\n" +
- "varying vec2 vTextureCoord;\n" +
- "uniform samplerExternalOES sTexture;\n" +
- "void main() {\n" +
- " gl_FragColor = texture2D(sTexture, vTextureCoord);\n" +
- "}\n";
-
- private float[] mMVPMatrix = new float[16];
- private float[] mSTMatrix = new float[16];
-
- private int mProgram;
- private int mTextureID;
- private int muMVPMatrixHandle;
- private int muSTMatrixHandle;
- private int maPositionHandle;
- private int maTextureHandle;
-
- private SurfaceTexture mSurface;
+ private TextureRender mTextureRender;
+ private SurfaceTexture mSurfaceTexture;
private boolean updateSurface = false;
- private static int GL_TEXTURE_EXTERNAL_OES = 0x8D65;
-
private MediaPlayer mMediaPlayer;
public VideoRender(Context context) {
- mTriangleVertices = ByteBuffer.allocateDirect(
- mTriangleVerticesData.length * FLOAT_SIZE_BYTES)
- .order(ByteOrder.nativeOrder()).asFloatBuffer();
- mTriangleVertices.put(mTriangleVerticesData).position(0);
-
- Matrix.setIdentityM(mSTMatrix, 0);
+ mTextureRender = new TextureRender();
}
public void setMediaPlayer(MediaPlayer player) {
@@ -144,97 +94,28 @@
public void onDrawFrame(GL10 glUnused) {
synchronized(this) {
if (updateSurface) {
- mSurface.updateTexImage();
- mSurface.getTransformMatrix(mSTMatrix);
+ mSurfaceTexture.updateTexImage();
updateSurface = false;
}
}
- GLES20.glClearColor(0.0f, 1.0f, 0.0f, 1.0f);
- GLES20.glClear( GLES20.GL_DEPTH_BUFFER_BIT | GLES20.GL_COLOR_BUFFER_BIT);
-
- GLES20.glUseProgram(mProgram);
- checkGlError("glUseProgram");
-
- GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
- GLES20.glBindTexture(GL_TEXTURE_EXTERNAL_OES, mTextureID);
-
- mTriangleVertices.position(TRIANGLE_VERTICES_DATA_POS_OFFSET);
- GLES20.glVertexAttribPointer(maPositionHandle, 3, GLES20.GL_FLOAT, false,
- TRIANGLE_VERTICES_DATA_STRIDE_BYTES, mTriangleVertices);
- checkGlError("glVertexAttribPointer maPosition");
- GLES20.glEnableVertexAttribArray(maPositionHandle);
- checkGlError("glEnableVertexAttribArray maPositionHandle");
-
- mTriangleVertices.position(TRIANGLE_VERTICES_DATA_UV_OFFSET);
- GLES20.glVertexAttribPointer(maTextureHandle, 3, GLES20.GL_FLOAT, false,
- TRIANGLE_VERTICES_DATA_STRIDE_BYTES, mTriangleVertices);
- checkGlError("glVertexAttribPointer maTextureHandle");
- GLES20.glEnableVertexAttribArray(maTextureHandle);
- checkGlError("glEnableVertexAttribArray maTextureHandle");
-
- Matrix.setIdentityM(mMVPMatrix, 0);
- GLES20.glUniformMatrix4fv(muMVPMatrixHandle, 1, false, mMVPMatrix, 0);
- GLES20.glUniformMatrix4fv(muSTMatrixHandle, 1, false, mSTMatrix, 0);
-
- GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
- checkGlError("glDrawArrays");
- GLES20.glFinish();
-
+ mTextureRender.drawFrame(mSurfaceTexture);
}
public void onSurfaceChanged(GL10 glUnused, int width, int height) {
}
public void onSurfaceCreated(GL10 glUnused, EGLConfig config) {
- mProgram = createProgram(mVertexShader, mFragmentShader);
- if (mProgram == 0) {
- return;
- }
- maPositionHandle = GLES20.glGetAttribLocation(mProgram, "aPosition");
- checkGlError("glGetAttribLocation aPosition");
- if (maPositionHandle == -1) {
- throw new RuntimeException("Could not get attrib location for aPosition");
- }
- maTextureHandle = GLES20.glGetAttribLocation(mProgram, "aTextureCoord");
- checkGlError("glGetAttribLocation aTextureCoord");
- if (maTextureHandle == -1) {
- throw new RuntimeException("Could not get attrib location for aTextureCoord");
- }
-
- muMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix");
- checkGlError("glGetUniformLocation uMVPMatrix");
- if (muMVPMatrixHandle == -1) {
- throw new RuntimeException("Could not get attrib location for uMVPMatrix");
- }
-
- muSTMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uSTMatrix");
- checkGlError("glGetUniformLocation uSTMatrix");
- if (muSTMatrixHandle == -1) {
- throw new RuntimeException("Could not get attrib location for uSTMatrix");
- }
-
-
- int[] textures = new int[1];
- GLES20.glGenTextures(1, textures, 0);
-
- mTextureID = textures[0];
- GLES20.glBindTexture(GL_TEXTURE_EXTERNAL_OES, mTextureID);
- checkGlError("glBindTexture mTextureID");
-
- GLES20.glTexParameterf(GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MIN_FILTER,
- GLES20.GL_NEAREST);
- GLES20.glTexParameterf(GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MAG_FILTER,
- GLES20.GL_LINEAR);
+ mTextureRender.surfaceCreated();
/*
* Create the SurfaceTexture that will feed this textureID,
* and pass it to the MediaPlayer
*/
- mSurface = new SurfaceTexture(mTextureID);
- mSurface.setOnFrameAvailableListener(this);
+ mSurfaceTexture = new SurfaceTexture(mTextureRender.getTextureId());
+ mSurfaceTexture.setOnFrameAvailableListener(this);
- Surface surface = new Surface(mSurface);
+ Surface surface = new Surface(mSurfaceTexture);
mMediaPlayer.setSurface(surface);
surface.release();
@@ -252,61 +133,6 @@
synchronized public void onFrameAvailable(SurfaceTexture surface) {
updateSurface = true;
}
-
- private int loadShader(int shaderType, String source) {
- int shader = GLES20.glCreateShader(shaderType);
- if (shader != 0) {
- GLES20.glShaderSource(shader, source);
- GLES20.glCompileShader(shader);
- int[] compiled = new int[1];
- GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compiled, 0);
- if (compiled[0] == 0) {
- Log.e(TAG, "Could not compile shader " + shaderType + ":");
- Log.e(TAG, GLES20.glGetShaderInfoLog(shader));
- GLES20.glDeleteShader(shader);
- shader = 0;
- }
- }
- return shader;
- }
-
- private int createProgram(String vertexSource, String fragmentSource) {
- int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexSource);
- if (vertexShader == 0) {
- return 0;
- }
- int pixelShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentSource);
- if (pixelShader == 0) {
- return 0;
- }
-
- int program = GLES20.glCreateProgram();
- if (program != 0) {
- GLES20.glAttachShader(program, vertexShader);
- checkGlError("glAttachShader");
- GLES20.glAttachShader(program, pixelShader);
- checkGlError("glAttachShader");
- GLES20.glLinkProgram(program);
- int[] linkStatus = new int[1];
- GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, linkStatus, 0);
- if (linkStatus[0] != GLES20.GL_TRUE) {
- Log.e(TAG, "Could not link program: ");
- Log.e(TAG, GLES20.glGetProgramInfoLog(program));
- GLES20.glDeleteProgram(program);
- program = 0;
- }
- }
- return program;
- }
-
- private void checkGlError(String op) {
- int error;
- while ((error = GLES20.glGetError()) != GLES20.GL_NO_ERROR) {
- Log.e(TAG, op + ": glError " + error);
- throw new RuntimeException(op + ": glError " + error);
- }
- }
-
} // End of class VideoRender.
} // End of class VideoSurfaceView.
diff --git a/tests/tests/nativeopengl/Android.mk b/tests/tests/nativeopengl/Android.mk
new file mode 100644
index 0000000..dd19548
--- /dev/null
+++ b/tests/tests/nativeopengl/Android.mk
@@ -0,0 +1,41 @@
+# Copyright 2012 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_PACKAGE_NAME := CtsNativeOpenGLTestCases
+
+# Don't include this package in any target.
+LOCAL_MODULE_TAGS := optional
+
+# When built, explicitly put it in the data partition.
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+
+# All tests should include android.test.runner.
+LOCAL_JAVA_LIBRARIES := android.test.runner
+
+LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner ctswrappedgtest
+
+LOCAL_JNI_SHARED_LIBRARIES := libnativeopengltests
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_SDK_VERSION := current
+
+include $(BUILD_CTS_GTEST_PACKAGE)
+
+# Include the associated library's makefile.
+include $(LOCAL_PATH)/libnativeopengltests/Android.mk
diff --git a/tests/tests/nativeopengl/AndroidManifest.xml b/tests/tests/nativeopengl/AndroidManifest.xml
new file mode 100644
index 0000000..52157b4
--- /dev/null
+++ b/tests/tests/nativeopengl/AndroidManifest.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.opengl.cts">
+
+ <uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
+
+ <application>
+ <uses-library android:name="android.test.runner" />
+ <activity android:name="com.android.opengl.cts.GLTestActivity"/>
+ </application>
+
+ <!-- This is a self-instrumenting test package. -->
+ <instrumentation android:name="GLTestInstrumentation"
+ android:targetPackage="com.android.opengl.cts"
+ android:label="Native OpenGL tests">
+ </instrumentation>
+</manifest>
diff --git a/tests/tests/nativeopengl/libnativeopengltests/Android.mk b/tests/tests/nativeopengl/libnativeopengltests/Android.mk
new file mode 100644
index 0000000..b6ca1cb
--- /dev/null
+++ b/tests/tests/nativeopengl/libnativeopengltests/Android.mk
@@ -0,0 +1,48 @@
+# Copyright 2012 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.
+
+#
+# This is the shared library included by the JNI test app.
+#
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := libnativeopengltests
+
+# Don't include this package in any configuration by default.
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_C_INCLUDES := $(JNI_H_INCLUDE) \
+ bionic \
+ bionic/libstdc++/include \
+ external/gtest/include \
+ external/stlport/stlport
+
+LOCAL_SRC_FILES := \
+ register.cpp \
+ GLTestHelper.cpp \
+ android_test_wrappedgtest_WrappedGTestActivity.cpp \
+ com_android_opengl_cts_GLTestActivity.cpp \
+ tests/GLTest_test.cpp
+
+LOCAL_SHARED_LIBRARIES := libEGL \
+ libGLESv2 \
+ libstlport \
+ libandroid
+
+LOCAL_STATIC_LIBRARIES := libgtest
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/tests/src/android/view/inputmethod/cts/InputMethodInfoStub.java b/tests/tests/nativeopengl/libnativeopengltests/GLTestHelper.cpp
similarity index 66%
copy from tests/src/android/view/inputmethod/cts/InputMethodInfoStub.java
copy to tests/tests/nativeopengl/libnativeopengltests/GLTestHelper.cpp
index 0eeefbe..ed527a6 100644
--- a/tests/src/android/view/inputmethod/cts/InputMethodInfoStub.java
+++ b/tests/tests/nativeopengl/libnativeopengltests/GLTestHelper.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2008 The Android Open Source Project
+ * Copyright 2012 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.
@@ -14,10 +14,16 @@
* limitations under the License.
*/
-package android.view.inputmethod.cts;
+#include "GLTestHelper.h"
-import android.inputmethodservice.InputMethodService;
+using namespace android;
-public class InputMethodInfoStub extends InputMethodService {
+ANativeWindow* GLTestHelper::mWindow;
+ANativeWindow* GLTestHelper::getWindow() {
+ return mWindow;
+}
+
+void GLTestHelper::setWindow(ANativeWindow* value) {
+ mWindow = value;
}
diff --git a/tests/tests/nativeopengl/libnativeopengltests/GLTestHelper.h b/tests/tests/nativeopengl/libnativeopengltests/GLTestHelper.h
new file mode 100644
index 0000000..c0775f0
--- /dev/null
+++ b/tests/tests/nativeopengl/libnativeopengltests/GLTestHelper.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+#ifndef ANDROID_GL_TEST_HELPER_H
+#define ANDROID_GL_TEST_HELPER_H
+
+#include <stdint.h>
+#include <sys/types.h>
+#include <android/native_window.h>
+#include <utils/RefBase.h>
+
+using namespace android;
+
+class GLTestHelper
+{
+private:
+ static ANativeWindow* mWindow;
+public:
+ static ANativeWindow* getWindow();
+ static void setWindow(ANativeWindow* value);
+};
+
+
+
+#endif // ANDROID_GL_TEST_HELPER_H
diff --git a/tests/tests/nativeopengl/libnativeopengltests/android_test_wrappedgtest_WrappedGTestActivity.cpp b/tests/tests/nativeopengl/libnativeopengltests/android_test_wrappedgtest_WrappedGTestActivity.cpp
new file mode 100644
index 0000000..5e1c30e
--- /dev/null
+++ b/tests/tests/nativeopengl/libnativeopengltests/android_test_wrappedgtest_WrappedGTestActivity.cpp
@@ -0,0 +1,128 @@
+/*
+ * Copyright 2012 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.
+ */
+#include <android/log.h>
+#include <gtest/gtest.h>
+#include <jni.h>
+
+using namespace testing;
+
+class GTestListener : public EmptyTestEventListener {
+public:
+ GTestListener(JNIEnv *env, jobject activity)
+ : mActivity(activity), mEnv(env) {
+
+ jclass clazz = env->FindClass(
+ "android/test/wrappedgtest/WrappedGTestActivity");
+ mSendStatusID = env->GetMethodID(clazz, "sendStatus",
+ "(Ljava/lang/String;)V");
+ mMessageBuffer = new char[2048];
+ }
+
+ ~GTestListener() {
+ delete[] mMessageBuffer;
+ }
+
+private:
+ jobject mActivity;
+ JNIEnv * mEnv;
+ jmethodID mSendStatusID;
+ char * mMessageBuffer;
+
+ virtual void OnTestIterationStart(const UnitTest& unit_test,
+ int iteration) {
+ snprintf(mMessageBuffer, sizeof(char) * 2048,
+ "[==========] Running %i tests from %i test cases.",
+ unit_test.test_to_run_count(),
+ unit_test.test_case_to_run_count());
+
+ mEnv->CallVoidMethod(mActivity, mSendStatusID,
+ mEnv->NewStringUTF(mMessageBuffer));
+ }
+
+ virtual void OnTestStart(const TestInfo& test_info) {
+ snprintf(mMessageBuffer, sizeof(char) * 2048, "[ RUN ] %s.%s",
+ test_info.test_case_name(), test_info.name());
+
+ mEnv->CallVoidMethod(mActivity, mSendStatusID,
+ mEnv->NewStringUTF(mMessageBuffer));
+ }
+
+ virtual void OnTestPartResult(const TestPartResult& result) {
+ if (result.type() == TestPartResult::kSuccess) {
+ return;
+ }
+
+ snprintf(mMessageBuffer, sizeof(char) * 2048, "%s:%i: Failure\n%s",
+ result.file_name(), result.line_number(), result.message());
+
+ mEnv->CallVoidMethod(mActivity, mSendStatusID,
+ mEnv->NewStringUTF(mMessageBuffer));
+ }
+
+ virtual void OnTestEnd(const TestInfo& test_info) {
+ const char * result = test_info.result()->Passed() ?
+ "[ OK ] " : "[ FAILED ] ";
+
+ snprintf(mMessageBuffer, sizeof(char) * 2048, "%s%s.%s (%lli ms)",
+ result, test_info.test_case_name(), test_info.name(),
+ test_info.result()->elapsed_time());
+
+ mEnv->CallVoidMethod(mActivity, mSendStatusID,
+ mEnv->NewStringUTF(mMessageBuffer));
+ }
+
+ virtual void OnTestIterationEnd(const UnitTest& unit_test, int iteration) {
+ snprintf(mMessageBuffer, sizeof(char) * 2048,
+ "[==========] %i tests from %i test cases ran. (%lli ms total)",
+ unit_test.test_to_run_count(),
+ unit_test.test_case_to_run_count(), unit_test.elapsed_time());
+
+ mEnv->CallVoidMethod(mActivity, mSendStatusID,
+ mEnv->NewStringUTF(mMessageBuffer));
+ }
+};
+
+static jboolean WrappedGTestActivity_runTests(JNIEnv *env, jobject obj,
+ jobject activity) {
+ // init gtest with no args
+ int argc = 0;
+ InitGoogleTest(&argc, (char**)NULL);
+
+ // delete the default listener
+ TestEventListeners& listeners = UnitTest::GetInstance()->listeners();
+ delete listeners.Release(listeners.default_result_printer());
+
+ // add custom listener
+ GTestListener * listener = new GTestListener(env, activity);
+ listeners.Append(listener);
+
+ // run tests
+ int result = RUN_ALL_TESTS();
+
+ delete listener;
+ return result;
+};
+
+static JNINativeMethod methods[] = {
+ // name, signature, function
+ { "runTests", "(Landroid/test/wrappedgtest/WrappedGTestActivity;)I", (void*)WrappedGTestActivity_runTests },
+};
+
+int register_WrappedGTestActivity(JNIEnv *env) {
+ return env->RegisterNatives(
+ env->FindClass("android/test/wrappedgtest/WrappedGTestActivity"),
+ methods, sizeof(methods) / sizeof(JNINativeMethod));
+};
diff --git a/tests/tests/nativeopengl/libnativeopengltests/com_android_opengl_cts_GLTestActivity.cpp b/tests/tests/nativeopengl/libnativeopengltests/com_android_opengl_cts_GLTestActivity.cpp
new file mode 100644
index 0000000..8544f2a
--- /dev/null
+++ b/tests/tests/nativeopengl/libnativeopengltests/com_android_opengl_cts_GLTestActivity.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2012 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.
+ */
+
+#include <gtest/gtest.h>
+#include <jni.h>
+
+#include "GLTestHelper.h"
+#include <android/native_window_jni.h>
+
+using namespace android;
+
+static void GLTestActivity_setSurface(JNIEnv *env, jobject obj,
+ jobject surface) {
+ ANativeWindow* window = ANativeWindow_fromSurface(env, surface);
+ GLTestHelper::setWindow(window);
+};
+
+static JNINativeMethod methods[] = {
+ // name, signature, function
+ { "setSurface", "(Landroid/view/Surface;)V", (void*)GLTestActivity_setSurface },
+};
+
+int register_GLTestActivity(JNIEnv *env) {
+ return env->RegisterNatives(
+ env->FindClass("com/android/opengl/cts/GLTestActivity"),
+ methods, sizeof(methods) / sizeof(JNINativeMethod));
+};
diff --git a/tests/tests/nativeopengl/libnativeopengltests/register.cpp b/tests/tests/nativeopengl/libnativeopengltests/register.cpp
new file mode 100644
index 0000000..97a8bd4
--- /dev/null
+++ b/tests/tests/nativeopengl/libnativeopengltests/register.cpp
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2012 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.
+ */
+
+#include <jni.h>
+#include <stdlib.h>
+
+/*
+ * This function is called automatically by the system when this
+ * library is loaded. We use it to register all our native functions,
+ * which is the recommended practice for Android.
+ */
+jint JNI_OnLoad(JavaVM *vm, void *reserved) {
+ JNIEnv *env = NULL;
+
+ if (vm->GetEnv((void **) &env, JNI_VERSION_1_4) != JNI_OK) {
+ return JNI_ERR;
+ }
+
+ extern int register_WrappedGTestActivity(JNIEnv *);
+ if (register_WrappedGTestActivity(env)) {
+ return JNI_ERR;
+ }
+
+ extern int register_GLTestActivity(JNIEnv *);
+ if (register_GLTestActivity(env)) {
+ return JNI_ERR;
+ }
+
+ return JNI_VERSION_1_4;
+}
diff --git a/tests/tests/nativeopengl/libnativeopengltests/tests/GLTest_test.cpp b/tests/tests/nativeopengl/libnativeopengltests/tests/GLTest_test.cpp
new file mode 100644
index 0000000..37bfcb5
--- /dev/null
+++ b/tests/tests/nativeopengl/libnativeopengltests/tests/GLTest_test.cpp
@@ -0,0 +1,198 @@
+/*
+ * Copyright 2012 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.
+ */
+
+
+#include <android/native_window.h>
+
+#include <gtest/gtest.h>
+
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+
+
+#include "GLTestHelper.h"
+
+
+namespace android {
+
+class GLTest : public ::testing::Test {
+
+protected:
+
+ GLTest():
+ mEglDisplay(EGL_NO_DISPLAY),
+ mEglSurface(EGL_NO_SURFACE),
+ mEglContext(EGL_NO_CONTEXT) {
+ }
+
+
+ virtual void SetUp() {
+ mEglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+ ASSERT_EQ(EGL_SUCCESS, eglGetError());
+ ASSERT_NE(EGL_NO_DISPLAY, mEglDisplay);
+
+ EGLint majorVersion;
+ EGLint minorVersion;
+ EXPECT_TRUE(eglInitialize(mEglDisplay, &majorVersion, &minorVersion));
+ ASSERT_EQ(EGL_SUCCESS, eglGetError());
+
+ EGLint numConfigs = 0;
+ EXPECT_TRUE(eglChooseConfig(mEglDisplay, getConfigAttribs(), &mGlConfig,
+ 1, &numConfigs));
+ ASSERT_EQ(EGL_SUCCESS, eglGetError());
+
+ char* displaySecsEnv = getenv("GLTEST_DISPLAY_SECS");
+ if (displaySecsEnv != NULL) {
+ mDisplaySecs = atoi(displaySecsEnv);
+ if (mDisplaySecs < 0) {
+ mDisplaySecs = 0;
+ }
+ } else {
+ mDisplaySecs = 0;
+ }
+
+ if (mDisplaySecs > 0) {
+ mEglSurface = eglCreateWindowSurface(mEglDisplay, mGlConfig,
+ GLTestHelper::getWindow(), NULL);
+ } else {
+ EGLint pbufferAttribs[] = {
+ EGL_WIDTH, getSurfaceWidth(),
+ EGL_HEIGHT, getSurfaceHeight(),
+ EGL_NONE };
+
+ mEglSurface = eglCreatePbufferSurface(mEglDisplay, mGlConfig,
+ pbufferAttribs);
+ }
+ ASSERT_EQ(EGL_SUCCESS, eglGetError());
+ ASSERT_NE(EGL_NO_SURFACE, mEglSurface);
+
+ mEglContext = eglCreateContext(mEglDisplay, mGlConfig, EGL_NO_CONTEXT,
+ getContextAttribs());
+ ASSERT_EQ(EGL_SUCCESS, eglGetError());
+ ASSERT_NE(EGL_NO_CONTEXT, mEglContext);
+
+ EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface,
+ mEglContext));
+ ASSERT_EQ(EGL_SUCCESS, eglGetError());
+
+ EGLint w, h;
+ EXPECT_TRUE(eglQuerySurface(mEglDisplay, mEglSurface, EGL_WIDTH, &w));
+ ASSERT_EQ(EGL_SUCCESS, eglGetError());
+ EXPECT_TRUE(eglQuerySurface(mEglDisplay, mEglSurface, EGL_HEIGHT, &h));
+ ASSERT_EQ(EGL_SUCCESS, eglGetError());
+
+ glViewport(0, 0, w, h);
+ ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
+ }
+
+ virtual void TearDown() {
+ // Display the result
+ if (mDisplaySecs > 0 && mEglSurface != EGL_NO_SURFACE) {
+ eglSwapBuffers(mEglDisplay, mEglSurface);
+ sleep(mDisplaySecs);
+ }
+
+ if (mEglContext != EGL_NO_CONTEXT) {
+ eglDestroyContext(mEglDisplay, mEglContext);
+ }
+ if (mEglSurface != EGL_NO_SURFACE) {
+ eglDestroySurface(mEglDisplay, mEglSurface);
+ }
+ if (mEglDisplay != EGL_NO_DISPLAY) {
+ eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE,
+ EGL_NO_CONTEXT);
+ eglTerminate(mEglDisplay);
+ }
+ ASSERT_EQ(EGL_SUCCESS, eglGetError());
+ }
+
+ virtual EGLint const* getConfigAttribs() {
+ static EGLint sDefaultConfigAttribs[] = {
+ EGL_SURFACE_TYPE, EGL_PBUFFER_BIT,
+ EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
+ EGL_RED_SIZE, 8,
+ EGL_GREEN_SIZE, 8,
+ EGL_BLUE_SIZE, 8,
+ EGL_ALPHA_SIZE, 8,
+ EGL_DEPTH_SIZE, 16,
+ EGL_STENCIL_SIZE, 8,
+ EGL_NONE };
+
+ return sDefaultConfigAttribs;
+ }
+
+ virtual EGLint const* getContextAttribs() {
+ static EGLint sDefaultContextAttribs[] = {
+ EGL_CONTEXT_CLIENT_VERSION, 2,
+ EGL_NONE };
+
+ return sDefaultContextAttribs;
+ }
+
+ virtual EGLint getSurfaceWidth() {
+ return 512;
+ }
+
+ virtual EGLint getSurfaceHeight() {
+ return 512;
+ }
+
+ bool checkPixel(GLubyte * actual, GLubyte * expected, int tolerance) {
+ for (int i = 0; i < 4; i++) {
+ if (abs(actual[i] - expected[i]) > tolerance) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ ::testing::AssertionResult AssertPixel(const char* a_expr,
+ const char* e_expr, const char* t_expr, GLubyte * actual,
+ GLubyte * expected, int tolerance) {
+
+ if (checkPixel(actual, expected, tolerance)) {
+ return ::testing::AssertionSuccess();
+ }
+
+ return ::testing::AssertionFailure()
+ << "Pixel comparison failed with tolerance " << tolerance << "\n"
+ << "Actual: r=" << (int)actual[0] << " g=" << (int)actual[1]
+ << " b=" << (int)actual[2] << " a=" << (int)actual[3] << "\n"
+ << "Expected: r=" << (int)expected[0] << " g=" << (int)expected[1]
+ << " b=" << (int)expected[2] << " a=" << (int)expected[3] << "\n";
+ }
+
+ int mDisplaySecs;
+
+ EGLDisplay mEglDisplay;
+ EGLSurface mEglSurface;
+ EGLContext mEglContext;
+ EGLConfig mGlConfig;
+};
+
+TEST_F(GLTest, ClearColorTest) {
+ glClearColor(0.2, 0.2, 0.2, 0.2);
+ glClear(GL_COLOR_BUFFER_BIT);
+ GLubyte expected[4] = { 51, 51, 51, 51 };
+ GLubyte pixel[4];
+ glReadPixels(0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, pixel);
+ ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
+ ASSERT_PRED_FORMAT3(AssertPixel, pixel, expected, 2);
+}
+
+} // namespace android
diff --git a/tests/tests/nativeopengl/src/com/android/opengl/cts/GLTestActivity.java b/tests/tests/nativeopengl/src/com/android/opengl/cts/GLTestActivity.java
new file mode 100644
index 0000000..1633a93
--- /dev/null
+++ b/tests/tests/nativeopengl/src/com/android/opengl/cts/GLTestActivity.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2012 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.opengl.cts;
+
+import android.app.Activity;
+import android.app.Instrumentation;
+import android.content.Intent;
+import android.os.Bundle;
+import android.test.wrappedgtest.WrappedGTestActivity;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+import android.view.Surface;
+
+public class GLTestActivity extends WrappedGTestActivity {
+
+ private SurfaceView mSurfaceView;
+ private SurfaceHolder.Callback mHolderCallback = new SurfaceHolder.Callback() {
+
+ @Override
+ public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
+ setSurface(holder.getSurface());
+ }
+
+ @Override
+ public void surfaceCreated(SurfaceHolder holder) {
+ setSurface(holder.getSurface());
+ }
+
+ @Override
+ public void surfaceDestroyed(SurfaceHolder holder) {
+ }
+ };
+
+ public void onCreate(Bundle data) {
+ super.onCreate(data);
+ mSurfaceView = new SurfaceView(this);
+ mSurfaceView.getHolder().addCallback(mHolderCallback);
+ setContentView(mSurfaceView);
+ System.loadLibrary("nativeopengltests");
+ }
+
+ private static native void setSurface(Surface surface);
+}
diff --git a/tests/tests/nativeopengl/src/com/android/opengl/cts/GLTestInstrumentation.java b/tests/tests/nativeopengl/src/com/android/opengl/cts/GLTestInstrumentation.java
new file mode 100644
index 0000000..913f0eb
--- /dev/null
+++ b/tests/tests/nativeopengl/src/com/android/opengl/cts/GLTestInstrumentation.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2012 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.opengl.cts;
+
+import android.test.wrappedgtest.WrappedGTestInstrumentation;
+
+/**
+ * adb shell am instrument -w com.android.opengl.cts/.GLTestInstrumentation
+ */
+public class GLTestInstrumentation extends WrappedGTestInstrumentation {
+ public GLTestInstrumentation() {
+ mActivityClass = GLTestActivity.class;
+ }
+}
diff --git a/tests/tests/net/Android.mk b/tests/tests/net/Android.mk
index b327392..f69c4c3 100644
--- a/tests/tests/net/Android.mk
+++ b/tests/tests/net/Android.mk
@@ -21,7 +21,7 @@
# and when built explicitly put it in the data partition
LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
-LOCAL_JAVA_LIBRARIES := android.test.runner
+LOCAL_JAVA_LIBRARIES := android.test.runner voip-common
# include CtsTestServer as a temporary hack to free net.cts from cts.stub.
LOCAL_SRC_FILES := $(call all-java-files-under, src)
diff --git a/tests/tests/openglperf/Android.mk b/tests/tests/openglperf/Android.mk
index 2d1767e..e10d1c7 100644
--- a/tests/tests/openglperf/Android.mk
+++ b/tests/tests/openglperf/Android.mk
@@ -23,7 +23,9 @@
LOCAL_JAVA_LIBRARIES := android.test.runner
-LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner
+LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner ctsutil
+
+LOCAL_JNI_SHARED_LIBRARIES := libctsopenglperf_jni
LOCAL_SRC_FILES := $(call all-java-files-under, src)
@@ -35,4 +37,4 @@
include $(BUILD_CTS_PACKAGE)
-#include $(call all-makefiles-under,$(LOCAL_PATH))
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/tests/tests/openglperf/AndroidManifest.xml b/tests/tests/openglperf/AndroidManifest.xml
index 540f190..84bfea4 100644
--- a/tests/tests/openglperf/AndroidManifest.xml
+++ b/tests/tests/openglperf/AndroidManifest.xml
@@ -32,12 +32,9 @@
android:targetPackage="com.android.cts.openglperf"
android:name="android.test.InstrumentationCtsTestRunner" />
- <application
- android:label="@string/app_name" >
+ <application>
<uses-library android:name="android.test.runner" />
- <activity
- android:name="android.openglperf.cts.GlPlanetsActivity"
- android:label="@string/app_name" >
+ <activity android:name="android.openglperf.cts.GlPlanetsActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
diff --git a/suite/pts/hostTests/ptshostutil/Android.mk b/tests/tests/openglperf/jni/Android.mk
similarity index 67%
copy from suite/pts/hostTests/ptshostutil/Android.mk
copy to tests/tests/openglperf/jni/Android.mk
index 14e786a..77db1df 100644
--- a/suite/pts/hostTests/ptshostutil/Android.mk
+++ b/tests/tests/openglperf/jni/Android.mk
@@ -11,19 +11,21 @@
# 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_SRC_FILES := \
- $(call all-java-files-under, src)
-
-LOCAL_JAVA_LIBRARIES := cts-tradefed tradefed-prebuilt ddmlib-prebuilt junit
+LOCAL_MODULE := libctsopenglperf_jni
LOCAL_MODULE_TAGS := optional
-LOCAL_MODULE := ptshostutil
+LOCAL_SRC_FILES := OpenGlPerfNativeJni.cpp
-include $(BUILD_HOST_JAVA_LIBRARY)
+LOCAL_C_INCLUDES := $(JNI_H_INCLUDE) $(call include-path-for, system-core) frameworks/native/opengl/include
+LOCAL_SHARED_LIBRARIES := libnativehelper libcutils libGLES_trace libEGL
+
+LOCAL_SDK_VERSION := 14
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/tests/tests/openglperf/jni/OpenGlPerfNativeJni.cpp b/tests/tests/openglperf/jni/OpenGlPerfNativeJni.cpp
new file mode 100644
index 0000000..63311eb
--- /dev/null
+++ b/tests/tests/openglperf/jni/OpenGlPerfNativeJni.cpp
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+#include <EGL/egl.h>
+#define EGL_EGLEXT_PROTOTYPES // for egl*Sync*
+#include <EGL/eglext.h>
+#include <cutils/log.h>
+#include <jni.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/time.h>
+
+typedef EGLSyncKHR EGLAPIENTRY (*TypeEglCreateSyncKHR)(EGLDisplay dpy, \
+ EGLenum type, const EGLint *attrib_list);
+typedef EGLBoolean EGLAPIENTRY (*TypeEglDestroySyncKHR)(EGLDisplay dpy, \
+ EGLSyncKHR sync);
+typedef EGLint EGLAPIENTRY (*TypeEglClientWaitSyncKHR)(EGLDisplay dpy, \
+ EGLSyncKHR sync, EGLint flags, EGLTimeKHR timeout);
+
+static TypeEglCreateSyncKHR mEglCreateSyncKHR = NULL;
+static TypeEglClientWaitSyncKHR mEglClientWaitSyncKHR = NULL;
+static TypeEglDestroySyncKHR mEglDestroySyncKHR = NULL;
+static bool mInitialized = false;
+static bool mEglKhrFenceSyncSupported = false;
+
+bool IsEglKHRFenceSyncSupported(EGLDisplay& display)
+{
+ if (!mInitialized) {
+ const char* eglExtensions = eglQueryString(display, EGL_EXTENSIONS);
+ if (eglExtensions && strstr(eglExtensions, "EGL_KHR_fence_sync")) {
+ mEglCreateSyncKHR = (TypeEglCreateSyncKHR) eglGetProcAddress("eglCreateSyncKHR");
+ mEglClientWaitSyncKHR =
+ (TypeEglClientWaitSyncKHR) eglGetProcAddress("eglClientWaitSyncKHR");
+ mEglDestroySyncKHR = (TypeEglDestroySyncKHR) eglGetProcAddress("eglDestroySyncKHR");
+ if (mEglCreateSyncKHR != NULL && mEglClientWaitSyncKHR != NULL
+ && mEglDestroySyncKHR != NULL) {
+ mEglKhrFenceSyncSupported = true;
+ }
+ }
+ mInitialized = true;
+ }
+ return mEglKhrFenceSyncSupported;
+}
+
+extern "C" JNIEXPORT \
+jboolean JNICALL Java_android_openglperf_cts_OpenGlPerfNative_waitForEglCompletion(JNIEnv* env,
+ jclass clazz, jlong waitTimeInNs)
+{
+ EGLDisplay dpy = eglGetCurrentDisplay();
+ if (!IsEglKHRFenceSyncSupported(dpy)) {
+ return JNI_FALSE;
+ }
+ EGLSyncKHR sync = mEglCreateSyncKHR(dpy, EGL_SYNC_FENCE_KHR, NULL);
+ if (sync == EGL_NO_SYNC_KHR) {
+ return JNI_FALSE;
+ }
+ jboolean res = JNI_TRUE;
+ EGLint result = mEglClientWaitSyncKHR(dpy, sync, 0, waitTimeInNs);
+ if (result == EGL_FALSE) {
+ ALOGE("FrameCompletion: error waiting for fence: %#x", eglGetError());
+ res = JNI_FALSE;
+ } else if (result == EGL_TIMEOUT_EXPIRED_KHR) {
+ ALOGE("FrameCompletion: timeout waiting for fence");
+ res = JNI_FALSE;
+ }
+ mEglDestroySyncKHR(dpy, sync);
+ return res;
+}
diff --git a/tests/tests/openglperf/res/values/strings.xml b/tests/tests/openglperf/res/values/strings.xml
deleted file mode 100644
index 2cdba7f..0000000
--- a/tests/tests/openglperf/res/values/strings.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2011 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>
- <string name="app_name">OpenGlPerfTest</string>
-</resources>
\ No newline at end of file
diff --git a/tests/tests/openglperf/src/android/openglperf/cts/GLSurfaceViewCustom.java b/tests/tests/openglperf/src/android/openglperf/cts/GLSurfaceViewCustom.java
new file mode 100644
index 0000000..e1a2292
--- /dev/null
+++ b/tests/tests/openglperf/src/android/openglperf/cts/GLSurfaceViewCustom.java
@@ -0,0 +1,1943 @@
+/*
+ * Copyright (C) 2008 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.openglperf.cts;
+
+// This is a copy of f/b/opengl/java/android/opengl/GlSurfaceView.
+// Most of the code is used as it is, but there was a need to add a hook
+// after eglSwapBuffers is called. Also classes not available outside framework is commented out
+// or replaced.
+
+
+import java.io.Writer;
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+
+import javax.microedition.khronos.egl.EGL10;
+import javax.microedition.khronos.egl.EGL11;
+import javax.microedition.khronos.egl.EGLConfig;
+import javax.microedition.khronos.egl.EGLContext;
+import javax.microedition.khronos.egl.EGLDisplay;
+import javax.microedition.khronos.egl.EGLSurface;
+import javax.microedition.khronos.opengles.GL;
+import javax.microedition.khronos.opengles.GL10;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+
+/**
+ * An implementation of SurfaceView that uses the dedicated surface for
+ * displaying OpenGL rendering.
+ * <p>
+ * A GLSurfaceView provides the following features:
+ * <p>
+ * <ul>
+ * <li>Manages a surface, which is a special piece of memory that can be
+ * composited into the Android view system.
+ * <li>Manages an EGL display, which enables OpenGL to render into a surface.
+ * <li>Accepts a user-provided Renderer object that does the actual rendering.
+ * <li>Renders on a dedicated thread to decouple rendering performance from the
+ * UI thread.
+ * <li>Supports both on-demand and continuous rendering.
+ * <li>Optionally wraps, traces, and/or error-checks the renderer's OpenGL calls.
+ * </ul>
+ *
+ * <div class="special reference">
+ * <h3>Developer Guides</h3>
+ * <p>For more information about how to use OpenGL, read the
+ * <a href="{@docRoot}guide/topics/graphics/opengl.html">OpenGL</a> developer guide.</p>
+ * </div>
+ *
+ * <h3>Using GLSurfaceView</h3>
+ * <p>
+ * Typically you use GLSurfaceView by subclassing it and overriding one or more of the
+ * View system input event methods. If your application does not need to override event
+ * methods then GLSurfaceView can be used as-is. For the most part
+ * GLSurfaceView behavior is customized by calling "set" methods rather than by subclassing.
+ * For example, unlike a regular View, drawing is delegated to a separate Renderer object which
+ * is registered with the GLSurfaceView
+ * using the {@link #setRenderer(Renderer)} call.
+ * <p>
+ * <h3>Initializing GLSurfaceView</h3>
+ * All you have to do to initialize a GLSurfaceView is call {@link #setRenderer(Renderer)}.
+ * However, if desired, you can modify the default behavior of GLSurfaceView by calling one or
+ * more of these methods before calling setRenderer:
+ * <ul>
+ * <li>{@link #setDebugFlags(int)}
+ * <li>{@link #setEGLConfigChooser(boolean)}
+ * <li>{@link #setEGLConfigChooser(EGLConfigChooser)}
+ * <li>{@link #setEGLConfigChooser(int, int, int, int, int, int)}
+ * <li>{@link #setGLWrapper(GLWrapper)}
+ * </ul>
+ * <p>
+ * <h4>Specifying the android.view.Surface</h4>
+ * By default GLSurfaceView will create a PixelFormat.RGB_888 format surface. If a translucent
+ * surface is required, call getHolder().setFormat(PixelFormat.TRANSLUCENT).
+ * The exact format of a TRANSLUCENT surface is device dependent, but it will be
+ * a 32-bit-per-pixel surface with 8 bits per component.
+ * <p>
+ * <h4>Choosing an EGL Configuration</h4>
+ * A given Android device may support multiple EGLConfig rendering configurations.
+ * The available configurations may differ in how may channels of data are present, as
+ * well as how many bits are allocated to each channel. Therefore, the first thing
+ * GLSurfaceView has to do when starting to render is choose what EGLConfig to use.
+ * <p>
+ * By default GLSurfaceView chooses a EGLConfig that has an RGB_888 pixel format,
+ * with at least a 16-bit depth buffer and no stencil.
+ * <p>
+ * If you would prefer a different EGLConfig
+ * you can override the default behavior by calling one of the
+ * setEGLConfigChooser methods.
+ * <p>
+ * <h4>Debug Behavior</h4>
+ * You can optionally modify the behavior of GLSurfaceView by calling
+ * one or more of the debugging methods {@link #setDebugFlags(int)},
+ * and {@link #setGLWrapper}. These methods may be called before and/or after setRenderer, but
+ * typically they are called before setRenderer so that they take effect immediately.
+ * <p>
+ * <h4>Setting a Renderer</h4>
+ * Finally, you must call {@link #setRenderer} to register a {@link Renderer}.
+ * The renderer is
+ * responsible for doing the actual OpenGL rendering.
+ * <p>
+ * <h3>Rendering Mode</h3>
+ * Once the renderer is set, you can control whether the renderer draws
+ * continuously or on-demand by calling
+ * {@link #setRenderMode}. The default is continuous rendering.
+ * <p>
+ * <h3>Activity Life-cycle</h3>
+ * A GLSurfaceView must be notified when the activity is paused and resumed. GLSurfaceView clients
+ * are required to call {@link #onPause()} when the activity pauses and
+ * {@link #onResume()} when the activity resumes. These calls allow GLSurfaceView to
+ * pause and resume the rendering thread, and also allow GLSurfaceView to release and recreate
+ * the OpenGL display.
+ * <p>
+ * <h3>Handling events</h3>
+ * <p>
+ * To handle an event you will typically subclass GLSurfaceView and override the
+ * appropriate method, just as you would with any other View. However, when handling
+ * the event, you may need to communicate with the Renderer object
+ * that's running in the rendering thread. You can do this using any
+ * standard Java cross-thread communication mechanism. In addition,
+ * one relatively easy way to communicate with your renderer is
+ * to call
+ * {@link #queueEvent(Runnable)}. For example:
+ * <pre class="prettyprint">
+ * class MyGLSurfaceView extends GLSurfaceView {
+ *
+ * private MyRenderer mMyRenderer;
+ *
+ * public void start() {
+ * mMyRenderer = ...;
+ * setRenderer(mMyRenderer);
+ * }
+ *
+ * public boolean onKeyDown(int keyCode, KeyEvent event) {
+ * if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) {
+ * queueEvent(new Runnable() {
+ * // This method will be called on the rendering
+ * // thread:
+ * public void run() {
+ * mMyRenderer.handleDpadCenter();
+ * }});
+ * return true;
+ * }
+ * return super.onKeyDown(keyCode, event);
+ * }
+ * }
+ * </pre>
+ *
+ */
+public class GLSurfaceViewCustom extends SurfaceView implements SurfaceHolder.Callback {
+ private final static String TAG = "GLSurfaceView";
+ private final static boolean LOG_ATTACH_DETACH = false;
+ private final static boolean LOG_THREADS = false;
+ private final static boolean LOG_PAUSE_RESUME = false;
+ private final static boolean LOG_SURFACE = false;
+ private final static boolean LOG_RENDERER = false;
+ private final static boolean LOG_RENDERER_DRAW_FRAME = false;
+ private final static boolean LOG_EGL = false;
+ /**
+ * The renderer only renders
+ * when the surface is created, or when {@link #requestRender} is called.
+ *
+ * @see #getRenderMode()
+ * @see #setRenderMode(int)
+ * @see #requestRender()
+ */
+ public final static int RENDERMODE_WHEN_DIRTY = 0;
+ /**
+ * The renderer is called
+ * continuously to re-render the scene.
+ *
+ * @see #getRenderMode()
+ * @see #setRenderMode(int)
+ */
+ public final static int RENDERMODE_CONTINUOUSLY = 1;
+
+ /**
+ * Check glError() after every GL call and throw an exception if glError indicates
+ * that an error has occurred. This can be used to help track down which OpenGL ES call
+ * is causing an error.
+ *
+ * @see #getDebugFlags
+ * @see #setDebugFlags
+ */
+ public final static int DEBUG_CHECK_GL_ERROR = 1;
+
+ /**
+ * Log GL calls to the system log at "verbose" level with tag "GLSurfaceView".
+ *
+ * @see #getDebugFlags
+ * @see #setDebugFlags
+ */
+ public final static int DEBUG_LOG_GL_CALLS = 2;
+
+ /**
+ * Standard View constructor. In order to render something, you
+ * must call {@link #setRenderer} to register a renderer.
+ */
+ public GLSurfaceViewCustom(Context context) {
+ super(context);
+ init();
+ }
+
+ /**
+ * Standard View constructor. In order to render something, you
+ * must call {@link #setRenderer} to register a renderer.
+ */
+ public GLSurfaceViewCustom(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ init();
+ }
+
+ @Override
+ protected void finalize() throws Throwable {
+ try {
+ if (mGLThread != null) {
+ // GLThread may still be running if this view was never
+ // attached to a window.
+ mGLThread.requestExitAndWait();
+ }
+ } finally {
+ super.finalize();
+ }
+ }
+
+ private void init() {
+ // Install a SurfaceHolder.Callback so we get notified when the
+ // underlying surface is created and destroyed
+ SurfaceHolder holder = getHolder();
+ holder.addCallback(this);
+ // setFormat is done by SurfaceView in SDK 2.3 and newer. Uncomment
+ // this statement if back-porting to 2.2 or older:
+ // holder.setFormat(PixelFormat.RGB_565);
+ //
+ // setType is not needed for SDK 2.0 or newer. Uncomment this
+ // statement if back-porting this code to older SDKs.
+ // holder.setType(SurfaceHolder.SURFACE_TYPE_GPU);
+ }
+
+ /**
+ * Set the glWrapper. If the glWrapper is not null, its
+ * {@link GLWrapper#wrap(GL)} method is called
+ * whenever a surface is created. A GLWrapper can be used to wrap
+ * the GL object that's passed to the renderer. Wrapping a GL
+ * object enables examining and modifying the behavior of the
+ * GL calls made by the renderer.
+ * <p>
+ * Wrapping is typically used for debugging purposes.
+ * <p>
+ * The default value is null.
+ * @param glWrapper the new GLWrapper
+ */
+ public void setGLWrapper(GLWrapper glWrapper) {
+ mGLWrapper = glWrapper;
+ }
+
+ /**
+ * Set the debug flags to a new value. The value is
+ * constructed by OR-together zero or more
+ * of the DEBUG_CHECK_* constants. The debug flags take effect
+ * whenever a surface is created. The default value is zero.
+ * @param debugFlags the new debug flags
+ * @see #DEBUG_CHECK_GL_ERROR
+ * @see #DEBUG_LOG_GL_CALLS
+ */
+ public void setDebugFlags(int debugFlags) {
+ mDebugFlags = debugFlags;
+ }
+
+ /**
+ * Get the current value of the debug flags.
+ * @return the current value of the debug flags.
+ */
+ public int getDebugFlags() {
+ return mDebugFlags;
+ }
+
+ /**
+ * Control whether the EGL context is preserved when the GLSurfaceView is paused and
+ * resumed.
+ * <p>
+ * If set to true, then the EGL context may be preserved when the GLSurfaceView is paused.
+ * Whether the EGL context is actually preserved or not depends upon whether the
+ * Android device that the program is running on can support an arbitrary number of EGL
+ * contexts or not. Devices that can only support a limited number of EGL contexts must
+ * release the EGL context in order to allow multiple applications to share the GPU.
+ * <p>
+ * If set to false, the EGL context will be released when the GLSurfaceView is paused,
+ * and recreated when the GLSurfaceView is resumed.
+ * <p>
+ *
+ * The default is false.
+ *
+ * @param preserveOnPause preserve the EGL context when paused
+ */
+ public void setPreserveEGLContextOnPause(boolean preserveOnPause) {
+ mPreserveEGLContextOnPause = preserveOnPause;
+ }
+
+ /**
+ * @return true if the EGL context will be preserved when paused
+ */
+ public boolean getPreserveEGLContextOnPause() {
+ return mPreserveEGLContextOnPause;
+ }
+
+ /**
+ * Set the renderer associated with this view. Also starts the thread that
+ * will call the renderer, which in turn causes the rendering to start.
+ * <p>This method should be called once and only once in the life-cycle of
+ * a GLSurfaceView.
+ * <p>The following GLSurfaceView methods can only be called <em>before</em>
+ * setRenderer is called:
+ * <ul>
+ * <li>{@link #setEGLConfigChooser(boolean)}
+ * <li>{@link #setEGLConfigChooser(EGLConfigChooser)}
+ * <li>{@link #setEGLConfigChooser(int, int, int, int, int, int)}
+ * </ul>
+ * <p>
+ * The following GLSurfaceView methods can only be called <em>after</em>
+ * setRenderer is called:
+ * <ul>
+ * <li>{@link #getRenderMode()}
+ * <li>{@link #onPause()}
+ * <li>{@link #onResume()}
+ * <li>{@link #queueEvent(Runnable)}
+ * <li>{@link #requestRender()}
+ * <li>{@link #setRenderMode(int)}
+ * </ul>
+ *
+ * @param renderer the renderer to use to perform OpenGL drawing.
+ */
+ public void setRenderer(Renderer renderer) {
+ checkRenderThreadState();
+ if (mEGLConfigChooser == null) {
+ mEGLConfigChooser = new SimpleEGLConfigChooser(true);
+ }
+ if (mEGLContextFactory == null) {
+ mEGLContextFactory = new DefaultContextFactory();
+ }
+ if (mEGLWindowSurfaceFactory == null) {
+ mEGLWindowSurfaceFactory = new DefaultWindowSurfaceFactory();
+ }
+ mRenderer = renderer;
+ mGLThread = new GLThread(mThisWeakRef);
+ mGLThread.start();
+ }
+
+ /**
+ * Install a custom EGLContextFactory.
+ * <p>If this method is
+ * called, it must be called before {@link #setRenderer(Renderer)}
+ * is called.
+ * <p>
+ * If this method is not called, then by default
+ * a context will be created with no shared context and
+ * with a null attribute list.
+ */
+ public void setEGLContextFactory(EGLContextFactory factory) {
+ checkRenderThreadState();
+ mEGLContextFactory = factory;
+ }
+
+ /**
+ * Install a custom EGLWindowSurfaceFactory.
+ * <p>If this method is
+ * called, it must be called before {@link #setRenderer(Renderer)}
+ * is called.
+ * <p>
+ * If this method is not called, then by default
+ * a window surface will be created with a null attribute list.
+ */
+ public void setEGLWindowSurfaceFactory(EGLWindowSurfaceFactory factory) {
+ checkRenderThreadState();
+ mEGLWindowSurfaceFactory = factory;
+ }
+
+ /**
+ * Install a custom EGLConfigChooser.
+ * <p>If this method is
+ * called, it must be called before {@link #setRenderer(Renderer)}
+ * is called.
+ * <p>
+ * If no setEGLConfigChooser method is called, then by default the
+ * view will choose an EGLConfig that is compatible with the current
+ * android.view.Surface, with a depth buffer depth of
+ * at least 16 bits.
+ * @param configChooser
+ */
+ public void setEGLConfigChooser(EGLConfigChooser configChooser) {
+ checkRenderThreadState();
+ mEGLConfigChooser = configChooser;
+ }
+
+ /**
+ * Install a config chooser which will choose a config
+ * as close to 16-bit RGB as possible, with or without an optional depth
+ * buffer as close to 16-bits as possible.
+ * <p>If this method is
+ * called, it must be called before {@link #setRenderer(Renderer)}
+ * is called.
+ * <p>
+ * If no setEGLConfigChooser method is called, then by default the
+ * view will choose an RGB_888 surface with a depth buffer depth of
+ * at least 16 bits.
+ *
+ * @param needDepth
+ */
+ public void setEGLConfigChooser(boolean needDepth) {
+ setEGLConfigChooser(new SimpleEGLConfigChooser(needDepth));
+ }
+
+ /**
+ * Install a config chooser which will choose a config
+ * with at least the specified depthSize and stencilSize,
+ * and exactly the specified redSize, greenSize, blueSize and alphaSize.
+ * <p>If this method is
+ * called, it must be called before {@link #setRenderer(Renderer)}
+ * is called.
+ * <p>
+ * If no setEGLConfigChooser method is called, then by default the
+ * view will choose an RGB_888 surface with a depth buffer depth of
+ * at least 16 bits.
+ *
+ */
+ public void setEGLConfigChooser(int redSize, int greenSize, int blueSize,
+ int alphaSize, int depthSize, int stencilSize) {
+ setEGLConfigChooser(new ComponentSizeChooser(redSize, greenSize,
+ blueSize, alphaSize, depthSize, stencilSize));
+ }
+
+ /**
+ * Inform the default EGLContextFactory and default EGLConfigChooser
+ * which EGLContext client version to pick.
+ * <p>Use this method to create an OpenGL ES 2.0-compatible context.
+ * Example:
+ * <pre class="prettyprint">
+ * public MyView(Context context) {
+ * super(context);
+ * setEGLContextClientVersion(2); // Pick an OpenGL ES 2.0 context.
+ * setRenderer(new MyRenderer());
+ * }
+ * </pre>
+ * <p>Note: Activities which require OpenGL ES 2.0 should indicate this by
+ * setting @lt;uses-feature android:glEsVersion="0x00020000" /> in the activity's
+ * AndroidManifest.xml file.
+ * <p>If this method is called, it must be called before {@link #setRenderer(Renderer)}
+ * is called.
+ * <p>This method only affects the behavior of the default EGLContexFactory and the
+ * default EGLConfigChooser. If
+ * {@link #setEGLContextFactory(EGLContextFactory)} has been called, then the supplied
+ * EGLContextFactory is responsible for creating an OpenGL ES 2.0-compatible context.
+ * If
+ * {@link #setEGLConfigChooser(EGLConfigChooser)} has been called, then the supplied
+ * EGLConfigChooser is responsible for choosing an OpenGL ES 2.0-compatible config.
+ * @param version The EGLContext client version to choose. Use 2 for OpenGL ES 2.0
+ */
+ public void setEGLContextClientVersion(int version) {
+ checkRenderThreadState();
+ mEGLContextClientVersion = version;
+ }
+
+ /**
+ * Set the rendering mode. When renderMode is
+ * RENDERMODE_CONTINUOUSLY, the renderer is called
+ * repeatedly to re-render the scene. When renderMode
+ * is RENDERMODE_WHEN_DIRTY, the renderer only rendered when the surface
+ * is created, or when {@link #requestRender} is called. Defaults to RENDERMODE_CONTINUOUSLY.
+ * <p>
+ * Using RENDERMODE_WHEN_DIRTY can improve battery life and overall system performance
+ * by allowing the GPU and CPU to idle when the view does not need to be updated.
+ * <p>
+ * This method can only be called after {@link #setRenderer(Renderer)}
+ *
+ * @param renderMode one of the RENDERMODE_X constants
+ * @see #RENDERMODE_CONTINUOUSLY
+ * @see #RENDERMODE_WHEN_DIRTY
+ */
+ public void setRenderMode(int renderMode) {
+ mGLThread.setRenderMode(renderMode);
+ }
+
+ /**
+ * Get the current rendering mode. May be called
+ * from any thread. Must not be called before a renderer has been set.
+ * @return the current rendering mode.
+ * @see #RENDERMODE_CONTINUOUSLY
+ * @see #RENDERMODE_WHEN_DIRTY
+ */
+ public int getRenderMode() {
+ return mGLThread.getRenderMode();
+ }
+
+ /**
+ * Request that the renderer render a frame.
+ * This method is typically used when the render mode has been set to
+ * {@link #RENDERMODE_WHEN_DIRTY}, so that frames are only rendered on demand.
+ * May be called
+ * from any thread. Must not be called before a renderer has been set.
+ */
+ public void requestRender() {
+ mGLThread.requestRender();
+ }
+
+ /**
+ * This method is part of the SurfaceHolder.Callback interface, and is
+ * not normally called or subclassed by clients of GLSurfaceView.
+ */
+ public void surfaceCreated(SurfaceHolder holder) {
+ mGLThread.surfaceCreated();
+ }
+
+ /**
+ * This method is part of the SurfaceHolder.Callback interface, and is
+ * not normally called or subclassed by clients of GLSurfaceView.
+ */
+ public void surfaceDestroyed(SurfaceHolder holder) {
+ // Surface will be destroyed when we return
+ mGLThread.surfaceDestroyed();
+ }
+
+ /**
+ * This method is part of the SurfaceHolder.Callback interface, and is
+ * not normally called or subclassed by clients of GLSurfaceView.
+ */
+ public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
+ mGLThread.onWindowResize(w, h);
+ }
+
+ /**
+ * Inform the view that the activity is paused. The owner of this view must
+ * call this method when the activity is paused. Calling this method will
+ * pause the rendering thread.
+ * Must not be called before a renderer has been set.
+ */
+ public void onPause() {
+ mGLThread.onPause();
+ }
+
+ /**
+ * Inform the view that the activity is resumed. The owner of this view must
+ * call this method when the activity is resumed. Calling this method will
+ * recreate the OpenGL display and resume the rendering
+ * thread.
+ * Must not be called before a renderer has been set.
+ */
+ public void onResume() {
+ mGLThread.onResume();
+ }
+
+ /**
+ * Queue a runnable to be run on the GL rendering thread. This can be used
+ * to communicate with the Renderer on the rendering thread.
+ * Must not be called before a renderer has been set.
+ * @param r the runnable to be run on the GL rendering thread.
+ */
+ public void queueEvent(Runnable r) {
+ mGLThread.queueEvent(r);
+ }
+
+ /**
+ * This method is used as part of the View class and is not normally
+ * called or subclassed by clients of GLSurfaceView.
+ */
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ if (LOG_ATTACH_DETACH) {
+ Log.d(TAG, "onAttachedToWindow reattach =" + mDetached);
+ }
+ if (mDetached && (mRenderer != null)) {
+ int renderMode = RENDERMODE_CONTINUOUSLY;
+ if (mGLThread != null) {
+ renderMode = mGLThread.getRenderMode();
+ }
+ mGLThread = new GLThread(mThisWeakRef);
+ if (renderMode != RENDERMODE_CONTINUOUSLY) {
+ mGLThread.setRenderMode(renderMode);
+ }
+ mGLThread.start();
+ }
+ mDetached = false;
+ }
+
+ /**
+ * This method is used as part of the View class and is not normally
+ * called or subclassed by clients of GLSurfaceView.
+ * Must not be called before a renderer has been set.
+ */
+ @Override
+ protected void onDetachedFromWindow() {
+ if (LOG_ATTACH_DETACH) {
+ Log.d(TAG, "onDetachedFromWindow");
+ }
+ if (mGLThread != null) {
+ mGLThread.requestExitAndWait();
+ }
+ mDetached = true;
+ super.onDetachedFromWindow();
+ }
+
+ // ----------------------------------------------------------------------
+
+ /**
+ * An interface used to wrap a GL interface.
+ * <p>Typically
+ * used for implementing debugging and tracing on top of the default
+ * GL interface. You would typically use this by creating your own class
+ * that implemented all the GL methods by delegating to another GL instance.
+ * Then you could add your own behavior before or after calling the
+ * delegate. All the GLWrapper would do was instantiate and return the
+ * wrapper GL instance:
+ * <pre class="prettyprint">
+ * class MyGLWrapper implements GLWrapper {
+ * GL wrap(GL gl) {
+ * return new MyGLImplementation(gl);
+ * }
+ * static class MyGLImplementation implements GL,GL10,GL11,... {
+ * ...
+ * }
+ * }
+ * </pre>
+ * @see #setGLWrapper(GLWrapper)
+ */
+ public interface GLWrapper {
+ /**
+ * Wraps a gl interface in another gl interface.
+ * @param gl a GL interface that is to be wrapped.
+ * @return either the input argument or another GL object that wraps the input argument.
+ */
+ GL wrap(GL gl);
+ }
+
+ /**
+ * A generic renderer interface.
+ * <p>
+ * The renderer is responsible for making OpenGL calls to render a frame.
+ * <p>
+ * GLSurfaceView clients typically create their own classes that implement
+ * this interface, and then call {@link GLSurfaceView#setRenderer} to
+ * register the renderer with the GLSurfaceView.
+ * <p>
+ *
+ * <div class="special reference">
+ * <h3>Developer Guides</h3>
+ * <p>For more information about how to use OpenGL, read the
+ * <a href="{@docRoot}guide/topics/graphics/opengl.html">OpenGL</a> developer guide.</p>
+ * </div>
+ *
+ * <h3>Threading</h3>
+ * The renderer will be called on a separate thread, so that rendering
+ * performance is decoupled from the UI thread. Clients typically need to
+ * communicate with the renderer from the UI thread, because that's where
+ * input events are received. Clients can communicate using any of the
+ * standard Java techniques for cross-thread communication, or they can
+ * use the {@link GLSurfaceView#queueEvent(Runnable)} convenience method.
+ * <p>
+ * <h3>EGL Context Lost</h3>
+ * There are situations where the EGL rendering context will be lost. This
+ * typically happens when device wakes up after going to sleep. When
+ * the EGL context is lost, all OpenGL resources (such as textures) that are
+ * associated with that context will be automatically deleted. In order to
+ * keep rendering correctly, a renderer must recreate any lost resources
+ * that it still needs. The {@link #onSurfaceCreated(GL10, EGLConfig)} method
+ * is a convenient place to do this.
+ *
+ *
+ * @see #setRenderer(Renderer)
+ */
+ public interface Renderer {
+ /**
+ * Called when the surface is created or recreated.
+ * <p>
+ * Called when the rendering thread
+ * starts and whenever the EGL context is lost. The EGL context will typically
+ * be lost when the Android device awakes after going to sleep.
+ * <p>
+ * Since this method is called at the beginning of rendering, as well as
+ * every time the EGL context is lost, this method is a convenient place to put
+ * code to create resources that need to be created when the rendering
+ * starts, and that need to be recreated when the EGL context is lost.
+ * Textures are an example of a resource that you might want to create
+ * here.
+ * <p>
+ * Note that when the EGL context is lost, all OpenGL resources associated
+ * with that context will be automatically deleted. You do not need to call
+ * the corresponding "glDelete" methods such as glDeleteTextures to
+ * manually delete these lost resources.
+ * <p>
+ * @param gl the GL interface. Use <code>instanceof</code> to
+ * test if the interface supports GL11 or higher interfaces.
+ * @param config the EGLConfig of the created surface. Can be used
+ * to create matching pbuffers.
+ */
+ void onSurfaceCreated(GL10 gl, EGLConfig config);
+
+ /**
+ * Called when the surface changed size.
+ * <p>
+ * Called after the surface is created and whenever
+ * the OpenGL ES surface size changes.
+ * <p>
+ * Typically you will set your viewport here. If your camera
+ * is fixed then you could also set your projection matrix here:
+ * <pre class="prettyprint">
+ * void onSurfaceChanged(GL10 gl, int width, int height) {
+ * gl.glViewport(0, 0, width, height);
+ * // for a fixed camera, set the projection too
+ * float ratio = (float) width / height;
+ * gl.glMatrixMode(GL10.GL_PROJECTION);
+ * gl.glLoadIdentity();
+ * gl.glFrustumf(-ratio, ratio, -1, 1, 1, 10);
+ * }
+ * </pre>
+ * @param gl the GL interface. Use <code>instanceof</code> to
+ * test if the interface supports GL11 or higher interfaces.
+ * @param width
+ * @param height
+ */
+ void onSurfaceChanged(GL10 gl, int width, int height);
+
+ /**
+ * Called to draw the current frame.
+ * <p>
+ * This method is responsible for drawing the current frame.
+ * <p>
+ * The implementation of this method typically looks like this:
+ * <pre class="prettyprint">
+ * void onDrawFrame(GL10 gl) {
+ * gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
+ * //... other gl calls to render the scene ...
+ * }
+ * </pre>
+ * @param gl the GL interface. Use <code>instanceof</code> to
+ * test if the interface supports GL11 or higher interfaces.
+ */
+ void onDrawFrame(GL10 gl);
+
+ /**
+ * called after eglSwapBuffers() is called.
+ * can be used for custom synchronization / measurement.
+ */
+ void onEglSwapBuffers();
+ }
+
+ /**
+ * An interface for customizing the eglCreateContext and eglDestroyContext calls.
+ * <p>
+ * This interface must be implemented by clients wishing to call
+ * {@link GLSurfaceView#setEGLContextFactory(EGLContextFactory)}
+ */
+ public interface EGLContextFactory {
+ EGLContext createContext(EGL10 egl, EGLDisplay display, EGLConfig eglConfig);
+ void destroyContext(EGL10 egl, EGLDisplay display, EGLContext context);
+ }
+
+ private class DefaultContextFactory implements EGLContextFactory {
+ private int EGL_CONTEXT_CLIENT_VERSION = 0x3098;
+
+ public EGLContext createContext(EGL10 egl, EGLDisplay display, EGLConfig config) {
+ int[] attrib_list = {EGL_CONTEXT_CLIENT_VERSION, mEGLContextClientVersion,
+ EGL10.EGL_NONE };
+
+ return egl.eglCreateContext(display, config, EGL10.EGL_NO_CONTEXT,
+ mEGLContextClientVersion != 0 ? attrib_list : null);
+ }
+
+ public void destroyContext(EGL10 egl, EGLDisplay display,
+ EGLContext context) {
+ if (!egl.eglDestroyContext(display, context)) {
+ Log.e("DefaultContextFactory", "display:" + display + " context: " + context);
+ if (LOG_THREADS) {
+ Log.i("DefaultContextFactory", "tid=" + Thread.currentThread().getId());
+ }
+ EglHelper.throwEglException("eglDestroyContex", egl.eglGetError());
+ }
+ }
+ }
+
+ /**
+ * An interface for customizing the eglCreateWindowSurface and eglDestroySurface calls.
+ * <p>
+ * This interface must be implemented by clients wishing to call
+ * {@link GLSurfaceView#setEGLWindowSurfaceFactory(EGLWindowSurfaceFactory)}
+ */
+ public interface EGLWindowSurfaceFactory {
+ /**
+ * @return null if the surface cannot be constructed.
+ */
+ EGLSurface createWindowSurface(EGL10 egl, EGLDisplay display, EGLConfig config,
+ Object nativeWindow);
+ void destroySurface(EGL10 egl, EGLDisplay display, EGLSurface surface);
+ }
+
+ private static class DefaultWindowSurfaceFactory implements EGLWindowSurfaceFactory {
+
+ public EGLSurface createWindowSurface(EGL10 egl, EGLDisplay display,
+ EGLConfig config, Object nativeWindow) {
+ EGLSurface result = null;
+ try {
+ result = egl.eglCreateWindowSurface(display, config, nativeWindow, null);
+ } catch (IllegalArgumentException e) {
+ // This exception indicates that the surface flinger surface
+ // is not valid. This can happen if the surface flinger surface has
+ // been torn down, but the application has not yet been
+ // notified via SurfaceHolder.Callback.surfaceDestroyed.
+ // In theory the application should be notified first,
+ // but in practice sometimes it is not. See b/4588890
+ Log.e(TAG, "eglCreateWindowSurface", e);
+ }
+ return result;
+ }
+
+ public void destroySurface(EGL10 egl, EGLDisplay display,
+ EGLSurface surface) {
+ egl.eglDestroySurface(display, surface);
+ }
+ }
+
+ /**
+ * An interface for choosing an EGLConfig configuration from a list of
+ * potential configurations.
+ * <p>
+ * This interface must be implemented by clients wishing to call
+ * {@link GLSurfaceView#setEGLConfigChooser(EGLConfigChooser)}
+ */
+ public interface EGLConfigChooser {
+ /**
+ * Choose a configuration from the list. Implementors typically
+ * implement this method by calling
+ * {@link EGL10#eglChooseConfig} and iterating through the results. Please consult the
+ * EGL specification available from The Khronos Group to learn how to call eglChooseConfig.
+ * @param egl the EGL10 for the current display.
+ * @param display the current display.
+ * @return the chosen configuration.
+ */
+ EGLConfig chooseConfig(EGL10 egl, EGLDisplay display);
+ }
+
+ private abstract class BaseConfigChooser
+ implements EGLConfigChooser {
+ public BaseConfigChooser(int[] configSpec) {
+ mConfigSpec = filterConfigSpec(configSpec);
+ }
+
+ public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display) {
+ int[] num_config = new int[1];
+ if (!egl.eglChooseConfig(display, mConfigSpec, null, 0,
+ num_config)) {
+ throw new IllegalArgumentException("eglChooseConfig failed");
+ }
+
+ int numConfigs = num_config[0];
+
+ if (numConfigs <= 0) {
+ throw new IllegalArgumentException(
+ "No configs match configSpec");
+ }
+
+ EGLConfig[] configs = new EGLConfig[numConfigs];
+ if (!egl.eglChooseConfig(display, mConfigSpec, configs, numConfigs,
+ num_config)) {
+ throw new IllegalArgumentException("eglChooseConfig#2 failed");
+ }
+ EGLConfig config = chooseConfig(egl, display, configs);
+ if (config == null) {
+ throw new IllegalArgumentException("No config chosen");
+ }
+ return config;
+ }
+
+ abstract EGLConfig chooseConfig(EGL10 egl, EGLDisplay display,
+ EGLConfig[] configs);
+
+ protected int[] mConfigSpec;
+
+ private int[] filterConfigSpec(int[] configSpec) {
+ if (mEGLContextClientVersion != 2) {
+ return configSpec;
+ }
+ /* We know none of the subclasses define EGL_RENDERABLE_TYPE.
+ * And we know the configSpec is well formed.
+ */
+ int len = configSpec.length;
+ int[] newConfigSpec = new int[len + 2];
+ System.arraycopy(configSpec, 0, newConfigSpec, 0, len-1);
+ newConfigSpec[len-1] = EGL10.EGL_RENDERABLE_TYPE;
+ newConfigSpec[len] = 4; /* EGL_OPENGL_ES2_BIT */
+ newConfigSpec[len+1] = EGL10.EGL_NONE;
+ return newConfigSpec;
+ }
+ }
+
+ /**
+ * Choose a configuration with exactly the specified r,g,b,a sizes,
+ * and at least the specified depth and stencil sizes.
+ */
+ private class ComponentSizeChooser extends BaseConfigChooser {
+ public ComponentSizeChooser(int redSize, int greenSize, int blueSize,
+ int alphaSize, int depthSize, int stencilSize) {
+ super(new int[] {
+ EGL10.EGL_RED_SIZE, redSize,
+ EGL10.EGL_GREEN_SIZE, greenSize,
+ EGL10.EGL_BLUE_SIZE, blueSize,
+ EGL10.EGL_ALPHA_SIZE, alphaSize,
+ EGL10.EGL_DEPTH_SIZE, depthSize,
+ EGL10.EGL_STENCIL_SIZE, stencilSize,
+ EGL10.EGL_NONE});
+ mValue = new int[1];
+ mRedSize = redSize;
+ mGreenSize = greenSize;
+ mBlueSize = blueSize;
+ mAlphaSize = alphaSize;
+ mDepthSize = depthSize;
+ mStencilSize = stencilSize;
+ }
+
+ @Override
+ public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display,
+ EGLConfig[] configs) {
+ for (EGLConfig config : configs) {
+ int d = findConfigAttrib(egl, display, config,
+ EGL10.EGL_DEPTH_SIZE, 0);
+ int s = findConfigAttrib(egl, display, config,
+ EGL10.EGL_STENCIL_SIZE, 0);
+ if ((d >= mDepthSize) && (s >= mStencilSize)) {
+ int r = findConfigAttrib(egl, display, config,
+ EGL10.EGL_RED_SIZE, 0);
+ int g = findConfigAttrib(egl, display, config,
+ EGL10.EGL_GREEN_SIZE, 0);
+ int b = findConfigAttrib(egl, display, config,
+ EGL10.EGL_BLUE_SIZE, 0);
+ int a = findConfigAttrib(egl, display, config,
+ EGL10.EGL_ALPHA_SIZE, 0);
+ if ((r == mRedSize) && (g == mGreenSize)
+ && (b == mBlueSize) && (a == mAlphaSize)) {
+ return config;
+ }
+ }
+ }
+ return null;
+ }
+
+ private int findConfigAttrib(EGL10 egl, EGLDisplay display,
+ EGLConfig config, int attribute, int defaultValue) {
+
+ if (egl.eglGetConfigAttrib(display, config, attribute, mValue)) {
+ return mValue[0];
+ }
+ return defaultValue;
+ }
+
+ private int[] mValue;
+ // Subclasses can adjust these values:
+ protected int mRedSize;
+ protected int mGreenSize;
+ protected int mBlueSize;
+ protected int mAlphaSize;
+ protected int mDepthSize;
+ protected int mStencilSize;
+ }
+
+ /**
+ * This class will choose a RGB_888 surface with
+ * or without a depth buffer.
+ *
+ */
+ private class SimpleEGLConfigChooser extends ComponentSizeChooser {
+ public SimpleEGLConfigChooser(boolean withDepthBuffer) {
+ super(8, 8, 8, 0, withDepthBuffer ? 16 : 0, 0);
+ }
+ }
+
+ /**
+ * An EGL helper class.
+ */
+
+ private static class EglHelper {
+ public EglHelper(WeakReference<GLSurfaceViewCustom> glSurfaceViewWeakRef) {
+ mGLSurfaceViewWeakRef = glSurfaceViewWeakRef;
+ }
+
+ /**
+ * Initialize EGL for a given configuration spec.
+ * @param configSpec
+ */
+ public void start() {
+ if (LOG_EGL) {
+ Log.w("EglHelper", "start() tid=" + Thread.currentThread().getId());
+ }
+ /*
+ * Get an EGL instance
+ */
+ mEgl = (EGL10) EGLContext.getEGL();
+
+ /*
+ * Get to the default display.
+ */
+ mEglDisplay = mEgl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
+
+ if (mEglDisplay == EGL10.EGL_NO_DISPLAY) {
+ throw new RuntimeException("eglGetDisplay failed");
+ }
+
+ /*
+ * We can now initialize EGL for that display
+ */
+ int[] version = new int[2];
+ if(!mEgl.eglInitialize(mEglDisplay, version)) {
+ throw new RuntimeException("eglInitialize failed");
+ }
+ GLSurfaceViewCustom view = mGLSurfaceViewWeakRef.get();
+ if (view == null) {
+ mEglConfig = null;
+ mEglContext = null;
+ } else {
+ mEglConfig = view.mEGLConfigChooser.chooseConfig(mEgl, mEglDisplay);
+
+ /*
+ * Create an EGL context. We want to do this as rarely as we can, because an
+ * EGL context is a somewhat heavy object.
+ */
+ mEglContext = view.mEGLContextFactory.createContext(mEgl, mEglDisplay, mEglConfig);
+ }
+ if (mEglContext == null || mEglContext == EGL10.EGL_NO_CONTEXT) {
+ mEglContext = null;
+ throwEglException("createContext");
+ }
+ if (LOG_EGL) {
+ Log.w("EglHelper", "createContext " + mEglContext + " tid=" + Thread.currentThread().getId());
+ }
+
+ mEglSurface = null;
+ }
+
+ /**
+ * Create an egl surface for the current SurfaceHolder surface. If a surface
+ * already exists, destroy it before creating the new surface.
+ *
+ * @return true if the surface was created successfully.
+ */
+ public boolean createSurface() {
+ if (LOG_EGL) {
+ Log.w("EglHelper", "createSurface() tid=" + Thread.currentThread().getId());
+ }
+ /*
+ * Check preconditions.
+ */
+ if (mEgl == null) {
+ throw new RuntimeException("egl not initialized");
+ }
+ if (mEglDisplay == null) {
+ throw new RuntimeException("eglDisplay not initialized");
+ }
+ if (mEglConfig == null) {
+ throw new RuntimeException("mEglConfig not initialized");
+ }
+
+ /*
+ * The window size has changed, so we need to create a new
+ * surface.
+ */
+ destroySurfaceImp();
+
+ /*
+ * Create an EGL surface we can render into.
+ */
+ GLSurfaceViewCustom view = mGLSurfaceViewWeakRef.get();
+ if (view != null) {
+ mEglSurface = view.mEGLWindowSurfaceFactory.createWindowSurface(mEgl,
+ mEglDisplay, mEglConfig, view.getHolder());
+ } else {
+ mEglSurface = null;
+ }
+
+ if (mEglSurface == null || mEglSurface == EGL10.EGL_NO_SURFACE) {
+ int error = mEgl.eglGetError();
+ if (error == EGL10.EGL_BAD_NATIVE_WINDOW) {
+ Log.e("EglHelper", "createWindowSurface returned EGL_BAD_NATIVE_WINDOW.");
+ }
+ return false;
+ }
+
+ /*
+ * Before we can issue GL commands, we need to make sure
+ * the context is current and bound to a surface.
+ */
+ if (!mEgl.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) {
+ /*
+ * Could not make the context current, probably because the underlying
+ * SurfaceView surface has been destroyed.
+ */
+ logEglErrorAsWarning("EGLHelper", "eglMakeCurrent", mEgl.eglGetError());
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Create a GL object for the current EGL context.
+ * @return
+ */
+ GL createGL() {
+
+ GL gl = mEglContext.getGL();
+ GLSurfaceViewCustom view = mGLSurfaceViewWeakRef.get();
+ if (view != null) {
+ if (view.mGLWrapper != null) {
+ gl = view.mGLWrapper.wrap(gl);
+ }
+
+/* if ((view.mDebugFlags & (DEBUG_CHECK_GL_ERROR | DEBUG_LOG_GL_CALLS)) != 0) {
+ int configFlags = 0;
+ Writer log = null;
+ if ((view.mDebugFlags & DEBUG_CHECK_GL_ERROR) != 0) {
+ configFlags |= GLDebugHelper.CONFIG_CHECK_GL_ERROR;
+ }
+ if ((view.mDebugFlags & DEBUG_LOG_GL_CALLS) != 0) {
+ log = new LogWriter();
+ }
+ gl = GLDebugHelper.wrap(gl, configFlags, log);
+ }*/
+ }
+ return gl;
+ }
+
+ /**
+ * Display the current render surface.
+ * @return the EGL error code from eglSwapBuffers.
+ */
+ public int swap() {
+ if (! mEgl.eglSwapBuffers(mEglDisplay, mEglSurface)) {
+ return mEgl.eglGetError();
+ }
+ return EGL10.EGL_SUCCESS;
+ }
+
+ public void destroySurface() {
+ if (LOG_EGL) {
+ Log.w("EglHelper", "destroySurface() tid=" + Thread.currentThread().getId());
+ }
+ destroySurfaceImp();
+ }
+
+ private void destroySurfaceImp() {
+ if (mEglSurface != null && mEglSurface != EGL10.EGL_NO_SURFACE) {
+ mEgl.eglMakeCurrent(mEglDisplay, EGL10.EGL_NO_SURFACE,
+ EGL10.EGL_NO_SURFACE,
+ EGL10.EGL_NO_CONTEXT);
+ GLSurfaceViewCustom view = mGLSurfaceViewWeakRef.get();
+ if (view != null) {
+ view.mEGLWindowSurfaceFactory.destroySurface(mEgl, mEglDisplay, mEglSurface);
+ }
+ mEglSurface = null;
+ }
+ }
+
+ public void finish() {
+ if (LOG_EGL) {
+ Log.w("EglHelper", "finish() tid=" + Thread.currentThread().getId());
+ }
+ if (mEglContext != null) {
+ GLSurfaceViewCustom view = mGLSurfaceViewWeakRef.get();
+ if (view != null) {
+ view.mEGLContextFactory.destroyContext(mEgl, mEglDisplay, mEglContext);
+ }
+ mEglContext = null;
+ }
+ if (mEglDisplay != null) {
+ mEgl.eglTerminate(mEglDisplay);
+ mEglDisplay = null;
+ }
+ }
+
+ private void throwEglException(String function) {
+ throwEglException(function, mEgl.eglGetError());
+ }
+
+ public static void throwEglException(String function, int error) {
+ String message = formatEglError(function, error);
+ if (LOG_THREADS) {
+ Log.e("EglHelper", "throwEglException tid=" + Thread.currentThread().getId() + " "
+ + message);
+ }
+ throw new RuntimeException(message);
+ }
+
+ public static void logEglErrorAsWarning(String tag, String function, int error) {
+ Log.w(tag, formatEglError(function, error));
+ }
+
+ public static String formatEglError(String function, int error) {
+ return function + " failed: " + error;
+ }
+
+ private WeakReference<GLSurfaceViewCustom> mGLSurfaceViewWeakRef;
+ EGL10 mEgl;
+ EGLDisplay mEglDisplay;
+ EGLSurface mEglSurface;
+ EGLConfig mEglConfig;
+ EGLContext mEglContext;
+
+ }
+
+ /**
+ * A generic GL Thread. Takes care of initializing EGL and GL. Delegates
+ * to a Renderer instance to do the actual drawing. Can be configured to
+ * render continuously or on request.
+ *
+ * All potentially blocking synchronization is done through the
+ * sGLThreadManager object. This avoids multiple-lock ordering issues.
+ *
+ */
+ static class GLThread extends Thread {
+ GLThread(WeakReference<GLSurfaceViewCustom> glSurfaceViewWeakRef) {
+ super();
+ mWidth = 0;
+ mHeight = 0;
+ mRequestRender = true;
+ mRenderMode = RENDERMODE_CONTINUOUSLY;
+ mGLSurfaceViewWeakRef = glSurfaceViewWeakRef;
+ }
+
+ @Override
+ public void run() {
+ setName("GLThread " + getId());
+ if (LOG_THREADS) {
+ Log.i("GLThread", "starting tid=" + getId());
+ }
+
+ try {
+ guardedRun();
+ } catch (InterruptedException e) {
+ // fall thru and exit normally
+ } finally {
+ sGLThreadManager.threadExiting(this);
+ }
+ }
+
+ /*
+ * This private method should only be called inside a
+ * synchronized(sGLThreadManager) block.
+ */
+ private void stopEglSurfaceLocked() {
+ if (mHaveEglSurface) {
+ mHaveEglSurface = false;
+ mEglHelper.destroySurface();
+ }
+ }
+
+ /*
+ * This private method should only be called inside a
+ * synchronized(sGLThreadManager) block.
+ */
+ private void stopEglContextLocked() {
+ if (mHaveEglContext) {
+ mEglHelper.finish();
+ mHaveEglContext = false;
+ sGLThreadManager.releaseEglContextLocked(this);
+ }
+ }
+ private void guardedRun() throws InterruptedException {
+ mEglHelper = new EglHelper(mGLSurfaceViewWeakRef);
+ mHaveEglContext = false;
+ mHaveEglSurface = false;
+ try {
+ GL10 gl = null;
+ boolean createEglContext = false;
+ boolean createEglSurface = false;
+ boolean createGlInterface = false;
+ boolean lostEglContext = false;
+ boolean sizeChanged = false;
+ boolean wantRenderNotification = false;
+ boolean doRenderNotification = false;
+ boolean askedToReleaseEglContext = false;
+ int w = 0;
+ int h = 0;
+ Runnable event = null;
+
+ while (true) {
+ synchronized (sGLThreadManager) {
+ while (true) {
+ if (mShouldExit) {
+ return;
+ }
+
+ if (! mEventQueue.isEmpty()) {
+ event = mEventQueue.remove(0);
+ break;
+ }
+
+ // Update the pause state.
+ boolean pausing = false;
+ if (mPaused != mRequestPaused) {
+ pausing = mRequestPaused;
+ mPaused = mRequestPaused;
+ sGLThreadManager.notifyAll();
+ if (LOG_PAUSE_RESUME) {
+ Log.i("GLThread", "mPaused is now " + mPaused + " tid=" + getId());
+ }
+ }
+
+ // Do we need to give up the EGL context?
+ if (mShouldReleaseEglContext) {
+ if (LOG_SURFACE) {
+ Log.i("GLThread", "releasing EGL context because asked to tid=" + getId());
+ }
+ stopEglSurfaceLocked();
+ stopEglContextLocked();
+ mShouldReleaseEglContext = false;
+ askedToReleaseEglContext = true;
+ }
+
+ // Have we lost the EGL context?
+ if (lostEglContext) {
+ stopEglSurfaceLocked();
+ stopEglContextLocked();
+ lostEglContext = false;
+ }
+
+ // When pausing, release the EGL surface:
+ if (pausing && mHaveEglSurface) {
+ if (LOG_SURFACE) {
+ Log.i("GLThread", "releasing EGL surface because paused tid=" + getId());
+ }
+ stopEglSurfaceLocked();
+ }
+
+ // When pausing, optionally release the EGL Context:
+ if (pausing && mHaveEglContext) {
+ GLSurfaceViewCustom view = mGLSurfaceViewWeakRef.get();
+ boolean preserveEglContextOnPause = view == null ?
+ false : view.mPreserveEGLContextOnPause;
+ if (!preserveEglContextOnPause || sGLThreadManager.shouldReleaseEGLContextWhenPausing()) {
+ stopEglContextLocked();
+ if (LOG_SURFACE) {
+ Log.i("GLThread", "releasing EGL context because paused tid=" + getId());
+ }
+ }
+ }
+
+ // When pausing, optionally terminate EGL:
+ if (pausing) {
+ if (sGLThreadManager.shouldTerminateEGLWhenPausing()) {
+ mEglHelper.finish();
+ if (LOG_SURFACE) {
+ Log.i("GLThread", "terminating EGL because paused tid=" + getId());
+ }
+ }
+ }
+
+ // Have we lost the SurfaceView surface?
+ if ((! mHasSurface) && (! mWaitingForSurface)) {
+ if (LOG_SURFACE) {
+ Log.i("GLThread", "noticed surfaceView surface lost tid=" + getId());
+ }
+ if (mHaveEglSurface) {
+ stopEglSurfaceLocked();
+ }
+ mWaitingForSurface = true;
+ mSurfaceIsBad = false;
+ sGLThreadManager.notifyAll();
+ }
+
+ // Have we acquired the surface view surface?
+ if (mHasSurface && mWaitingForSurface) {
+ if (LOG_SURFACE) {
+ Log.i("GLThread", "noticed surfaceView surface acquired tid=" + getId());
+ }
+ mWaitingForSurface = false;
+ sGLThreadManager.notifyAll();
+ }
+
+ if (doRenderNotification) {
+ if (LOG_SURFACE) {
+ Log.i("GLThread", "sending render notification tid=" + getId());
+ }
+ wantRenderNotification = false;
+ doRenderNotification = false;
+ mRenderComplete = true;
+ sGLThreadManager.notifyAll();
+ }
+
+ // Ready to draw?
+ if (readyToDraw()) {
+
+ // If we don't have an EGL context, try to acquire one.
+ if (! mHaveEglContext) {
+ if (askedToReleaseEglContext) {
+ askedToReleaseEglContext = false;
+ } else if (sGLThreadManager.tryAcquireEglContextLocked(this)) {
+ try {
+ mEglHelper.start();
+ } catch (RuntimeException t) {
+ sGLThreadManager.releaseEglContextLocked(this);
+ throw t;
+ }
+ mHaveEglContext = true;
+ createEglContext = true;
+
+ sGLThreadManager.notifyAll();
+ }
+ }
+
+ if (mHaveEglContext && !mHaveEglSurface) {
+ mHaveEglSurface = true;
+ createEglSurface = true;
+ createGlInterface = true;
+ sizeChanged = true;
+ }
+
+ if (mHaveEglSurface) {
+ if (mSizeChanged) {
+ sizeChanged = true;
+ w = mWidth;
+ h = mHeight;
+ wantRenderNotification = true;
+ if (LOG_SURFACE) {
+ Log.i("GLThread",
+ "noticing that we want render notification tid="
+ + getId());
+ }
+
+ // Destroy and recreate the EGL surface.
+ createEglSurface = true;
+
+ mSizeChanged = false;
+ }
+ mRequestRender = false;
+ sGLThreadManager.notifyAll();
+ break;
+ }
+ }
+
+ // By design, this is the only place in a GLThread thread where we wait().
+ if (LOG_THREADS) {
+ Log.i("GLThread", "waiting tid=" + getId()
+ + " mHaveEglContext: " + mHaveEglContext
+ + " mHaveEglSurface: " + mHaveEglSurface
+ + " mPaused: " + mPaused
+ + " mHasSurface: " + mHasSurface
+ + " mSurfaceIsBad: " + mSurfaceIsBad
+ + " mWaitingForSurface: " + mWaitingForSurface
+ + " mWidth: " + mWidth
+ + " mHeight: " + mHeight
+ + " mRequestRender: " + mRequestRender
+ + " mRenderMode: " + mRenderMode);
+ }
+ sGLThreadManager.wait();
+ }
+ } // end of synchronized(sGLThreadManager)
+
+ if (event != null) {
+ event.run();
+ event = null;
+ continue;
+ }
+
+ if (createEglSurface) {
+ if (LOG_SURFACE) {
+ Log.w("GLThread", "egl createSurface");
+ }
+ if (!mEglHelper.createSurface()) {
+ synchronized(sGLThreadManager) {
+ mSurfaceIsBad = true;
+ sGLThreadManager.notifyAll();
+ }
+ continue;
+ }
+ createEglSurface = false;
+ }
+
+ if (createGlInterface) {
+ gl = (GL10) mEglHelper.createGL();
+
+ sGLThreadManager.checkGLDriver(gl);
+ createGlInterface = false;
+ }
+
+ if (createEglContext) {
+ if (LOG_RENDERER) {
+ Log.w("GLThread", "onSurfaceCreated");
+ }
+ GLSurfaceViewCustom view = mGLSurfaceViewWeakRef.get();
+ if (view != null) {
+ view.mRenderer.onSurfaceCreated(gl, mEglHelper.mEglConfig);
+ }
+ createEglContext = false;
+ }
+
+ if (sizeChanged) {
+ if (LOG_RENDERER) {
+ Log.w("GLThread", "onSurfaceChanged(" + w + ", " + h + ")");
+ }
+ GLSurfaceViewCustom view = mGLSurfaceViewWeakRef.get();
+ if (view != null) {
+ view.mRenderer.onSurfaceChanged(gl, w, h);
+ }
+ sizeChanged = false;
+ }
+
+ if (LOG_RENDERER_DRAW_FRAME) {
+ Log.w("GLThread", "onDrawFrame tid=" + getId());
+ }
+ {
+ GLSurfaceViewCustom view = mGLSurfaceViewWeakRef.get();
+ if (view != null) {
+ view.mRenderer.onDrawFrame(gl);
+ }
+ }
+ int swapError = mEglHelper.swap();
+ switch (swapError) {
+ case EGL10.EGL_SUCCESS:
+ GLSurfaceViewCustom view = mGLSurfaceViewWeakRef.get();
+ if (view != null) {
+ view.mRenderer.onEglSwapBuffers();
+ }
+ break;
+ case EGL11.EGL_CONTEXT_LOST:
+ if (LOG_SURFACE) {
+ Log.i("GLThread", "egl context lost tid=" + getId());
+ }
+ lostEglContext = true;
+ break;
+ default:
+ // Other errors typically mean that the current surface is bad,
+ // probably because the SurfaceView surface has been destroyed,
+ // but we haven't been notified yet.
+ // Log the error to help developers understand why rendering stopped.
+ EglHelper.logEglErrorAsWarning("GLThread", "eglSwapBuffers", swapError);
+
+ synchronized(sGLThreadManager) {
+ mSurfaceIsBad = true;
+ sGLThreadManager.notifyAll();
+ }
+ break;
+ }
+
+ if (wantRenderNotification) {
+ doRenderNotification = true;
+ }
+ }
+
+ } finally {
+ /*
+ * clean-up everything...
+ */
+ synchronized (sGLThreadManager) {
+ stopEglSurfaceLocked();
+ stopEglContextLocked();
+ }
+ }
+ }
+
+ public boolean ableToDraw() {
+ return mHaveEglContext && mHaveEglSurface && readyToDraw();
+ }
+
+ private boolean readyToDraw() {
+ return (!mPaused) && mHasSurface && (!mSurfaceIsBad)
+ && (mWidth > 0) && (mHeight > 0)
+ && (mRequestRender || (mRenderMode == RENDERMODE_CONTINUOUSLY));
+ }
+
+ public void setRenderMode(int renderMode) {
+ if ( !((RENDERMODE_WHEN_DIRTY <= renderMode) && (renderMode <= RENDERMODE_CONTINUOUSLY)) ) {
+ throw new IllegalArgumentException("renderMode");
+ }
+ synchronized(sGLThreadManager) {
+ mRenderMode = renderMode;
+ sGLThreadManager.notifyAll();
+ }
+ }
+
+ public int getRenderMode() {
+ synchronized(sGLThreadManager) {
+ return mRenderMode;
+ }
+ }
+
+ public void requestRender() {
+ synchronized(sGLThreadManager) {
+ mRequestRender = true;
+ sGLThreadManager.notifyAll();
+ }
+ }
+
+ public void surfaceCreated() {
+ synchronized(sGLThreadManager) {
+ if (LOG_THREADS) {
+ Log.i("GLThread", "surfaceCreated tid=" + getId());
+ }
+ mHasSurface = true;
+ sGLThreadManager.notifyAll();
+ while((mWaitingForSurface) && (!mExited)) {
+ try {
+ sGLThreadManager.wait();
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ }
+ }
+ }
+ }
+
+ public void surfaceDestroyed() {
+ synchronized(sGLThreadManager) {
+ if (LOG_THREADS) {
+ Log.i("GLThread", "surfaceDestroyed tid=" + getId());
+ }
+ mHasSurface = false;
+ sGLThreadManager.notifyAll();
+ while((!mWaitingForSurface) && (!mExited)) {
+ try {
+ sGLThreadManager.wait();
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ }
+ }
+ }
+ }
+
+ public void onPause() {
+ synchronized (sGLThreadManager) {
+ if (LOG_PAUSE_RESUME) {
+ Log.i("GLThread", "onPause tid=" + getId());
+ }
+ mRequestPaused = true;
+ sGLThreadManager.notifyAll();
+ while ((! mExited) && (! mPaused)) {
+ if (LOG_PAUSE_RESUME) {
+ Log.i("Main thread", "onPause waiting for mPaused.");
+ }
+ try {
+ sGLThreadManager.wait();
+ } catch (InterruptedException ex) {
+ Thread.currentThread().interrupt();
+ }
+ }
+ }
+ }
+
+ public void onResume() {
+ synchronized (sGLThreadManager) {
+ if (LOG_PAUSE_RESUME) {
+ Log.i("GLThread", "onResume tid=" + getId());
+ }
+ mRequestPaused = false;
+ mRequestRender = true;
+ mRenderComplete = false;
+ sGLThreadManager.notifyAll();
+ while ((! mExited) && mPaused && (!mRenderComplete)) {
+ if (LOG_PAUSE_RESUME) {
+ Log.i("Main thread", "onResume waiting for !mPaused.");
+ }
+ try {
+ sGLThreadManager.wait();
+ } catch (InterruptedException ex) {
+ Thread.currentThread().interrupt();
+ }
+ }
+ }
+ }
+
+ public void onWindowResize(int w, int h) {
+ synchronized (sGLThreadManager) {
+ mWidth = w;
+ mHeight = h;
+ mSizeChanged = true;
+ mRequestRender = true;
+ mRenderComplete = false;
+ sGLThreadManager.notifyAll();
+
+ // Wait for thread to react to resize and render a frame
+ while (! mExited && !mPaused && !mRenderComplete
+ && ableToDraw()) {
+ if (LOG_SURFACE) {
+ Log.i("Main thread", "onWindowResize waiting for render complete from tid=" + getId());
+ }
+ try {
+ sGLThreadManager.wait();
+ } catch (InterruptedException ex) {
+ Thread.currentThread().interrupt();
+ }
+ }
+ }
+ }
+
+ public void requestExitAndWait() {
+ // don't call this from GLThread thread or it is a guaranteed
+ // deadlock!
+ synchronized(sGLThreadManager) {
+ mShouldExit = true;
+ sGLThreadManager.notifyAll();
+ while (! mExited) {
+ try {
+ sGLThreadManager.wait();
+ } catch (InterruptedException ex) {
+ Thread.currentThread().interrupt();
+ }
+ }
+ }
+ }
+
+ public void requestReleaseEglContextLocked() {
+ mShouldReleaseEglContext = true;
+ sGLThreadManager.notifyAll();
+ }
+
+ /**
+ * Queue an "event" to be run on the GL rendering thread.
+ * @param r the runnable to be run on the GL rendering thread.
+ */
+ public void queueEvent(Runnable r) {
+ if (r == null) {
+ throw new IllegalArgumentException("r must not be null");
+ }
+ synchronized(sGLThreadManager) {
+ mEventQueue.add(r);
+ sGLThreadManager.notifyAll();
+ }
+ }
+
+ // Once the thread is started, all accesses to the following member
+ // variables are protected by the sGLThreadManager monitor
+ private boolean mShouldExit;
+ private boolean mExited;
+ private boolean mRequestPaused;
+ private boolean mPaused;
+ private boolean mHasSurface;
+ private boolean mSurfaceIsBad;
+ private boolean mWaitingForSurface;
+ private boolean mHaveEglContext;
+ private boolean mHaveEglSurface;
+ private boolean mShouldReleaseEglContext;
+ private int mWidth;
+ private int mHeight;
+ private int mRenderMode;
+ private boolean mRequestRender;
+ private boolean mRenderComplete;
+ private ArrayList<Runnable> mEventQueue = new ArrayList<Runnable>();
+ private boolean mSizeChanged = true;
+
+ // End of member variables protected by the sGLThreadManager monitor.
+
+ private EglHelper mEglHelper;
+
+ /**
+ * Set once at thread construction time, nulled out when the parent view is garbage
+ * called. This weak reference allows the GLSurfaceView to be garbage collected while
+ * the GLThread is still alive.
+ */
+ private WeakReference<GLSurfaceViewCustom> mGLSurfaceViewWeakRef;
+
+ }
+
+ static class LogWriter extends Writer {
+
+ @Override public void close() {
+ flushBuilder();
+ }
+
+ @Override public void flush() {
+ flushBuilder();
+ }
+
+ @Override public void write(char[] buf, int offset, int count) {
+ for(int i = 0; i < count; i++) {
+ char c = buf[offset + i];
+ if ( c == '\n') {
+ flushBuilder();
+ }
+ else {
+ mBuilder.append(c);
+ }
+ }
+ }
+
+ private void flushBuilder() {
+ if (mBuilder.length() > 0) {
+ Log.v("GLSurfaceViewCustom", mBuilder.toString());
+ mBuilder.delete(0, mBuilder.length());
+ }
+ }
+
+ private StringBuilder mBuilder = new StringBuilder();
+ }
+
+
+ private void checkRenderThreadState() {
+ if (mGLThread != null) {
+ throw new IllegalStateException(
+ "setRenderer has already been called for this instance.");
+ }
+ }
+
+ private static class GLThreadManager {
+ private static String TAG = "GLThreadManager";
+
+ public synchronized void threadExiting(GLThread thread) {
+ if (LOG_THREADS) {
+ Log.i("GLThread", "exiting tid=" + thread.getId());
+ }
+ thread.mExited = true;
+ if (mEglOwner == thread) {
+ mEglOwner = null;
+ }
+ notifyAll();
+ }
+
+ /*
+ * Tries once to acquire the right to use an EGL
+ * context. Does not block. Requires that we are already
+ * in the sGLThreadManager monitor when this is called.
+ *
+ * @return true if the right to use an EGL context was acquired.
+ */
+ public boolean tryAcquireEglContextLocked(GLThread thread) {
+ if (mEglOwner == thread || mEglOwner == null) {
+ mEglOwner = thread;
+ notifyAll();
+ return true;
+ }
+ checkGLESVersion();
+ if (mMultipleGLESContextsAllowed) {
+ return true;
+ }
+ // Notify the owning thread that it should release the context.
+ // TODO: implement a fairness policy. Currently
+ // if the owning thread is drawing continuously it will just
+ // reacquire the EGL context.
+ if (mEglOwner != null) {
+ mEglOwner.requestReleaseEglContextLocked();
+ }
+ return false;
+ }
+
+ /*
+ * Releases the EGL context. Requires that we are already in the
+ * sGLThreadManager monitor when this is called.
+ */
+ public void releaseEglContextLocked(GLThread thread) {
+ if (mEglOwner == thread) {
+ mEglOwner = null;
+ }
+ notifyAll();
+ }
+
+ public synchronized boolean shouldReleaseEGLContextWhenPausing() {
+ // Release the EGL context when pausing even if
+ // the hardware supports multiple EGL contexts.
+ // Otherwise the device could run out of EGL contexts.
+ return mLimitedGLESContexts;
+ }
+
+ public synchronized boolean shouldTerminateEGLWhenPausing() {
+ checkGLESVersion();
+ return !mMultipleGLESContextsAllowed;
+ }
+
+ public synchronized void checkGLDriver(GL10 gl) {
+ if (! mGLESDriverCheckComplete) {
+ checkGLESVersion();
+ String renderer = gl.glGetString(GL10.GL_RENDERER);
+ if (mGLESVersion < kGLES_20) {
+ mMultipleGLESContextsAllowed =
+ ! renderer.startsWith(kMSM7K_RENDERER_PREFIX);
+ notifyAll();
+ }
+ mLimitedGLESContexts = !mMultipleGLESContextsAllowed;
+ if (LOG_SURFACE) {
+ Log.w(TAG, "checkGLDriver renderer = \"" + renderer + "\" multipleContextsAllowed = "
+ + mMultipleGLESContextsAllowed
+ + " mLimitedGLESContexts = " + mLimitedGLESContexts);
+ }
+ mGLESDriverCheckComplete = true;
+ }
+ }
+
+ private void checkGLESVersion() {
+ if (! mGLESVersionCheckComplete) {
+ mGLESVersion = kGLES_20; /*SystemProperties.getInt(
+ "ro.opengles.version",
+ ConfigurationInfo.GL_ES_VERSION_UNDEFINED); */
+ if (mGLESVersion >= kGLES_20) {
+ mMultipleGLESContextsAllowed = true;
+ }
+ if (LOG_SURFACE) {
+ Log.w(TAG, "checkGLESVersion mGLESVersion =" +
+ " " + mGLESVersion + " mMultipleGLESContextsAllowed = " + mMultipleGLESContextsAllowed);
+ }
+ mGLESVersionCheckComplete = true;
+ }
+ }
+
+ /**
+ * This check was required for some pre-Android-3.0 hardware. Android 3.0 provides
+ * support for hardware-accelerated views, therefore multiple EGL contexts are
+ * supported on all Android 3.0+ EGL drivers.
+ */
+ private boolean mGLESVersionCheckComplete;
+ private int mGLESVersion;
+ private boolean mGLESDriverCheckComplete;
+ private boolean mMultipleGLESContextsAllowed;
+ private boolean mLimitedGLESContexts;
+ private static final int kGLES_20 = 0x20000;
+ private static final String kMSM7K_RENDERER_PREFIX =
+ "Q3Dimension MSM7500 ";
+ private GLThread mEglOwner;
+ }
+
+ private static final GLThreadManager sGLThreadManager = new GLThreadManager();
+
+ private final WeakReference<GLSurfaceViewCustom> mThisWeakRef =
+ new WeakReference<GLSurfaceViewCustom>(this);
+ private GLThread mGLThread;
+ private Renderer mRenderer;
+ private boolean mDetached;
+ private EGLConfigChooser mEGLConfigChooser;
+ private EGLContextFactory mEGLContextFactory;
+ private EGLWindowSurfaceFactory mEGLWindowSurfaceFactory;
+ private GLWrapper mGLWrapper;
+ private int mDebugFlags;
+ private int mEGLContextClientVersion;
+ private boolean mPreserveEGLContextOnPause;
+}
diff --git a/tests/tests/openglperf/src/android/openglperf/cts/GlPlanetsActivity.java b/tests/tests/openglperf/src/android/openglperf/cts/GlPlanetsActivity.java
index 5a8c0a8..7aa1d21 100644
--- a/tests/tests/openglperf/src/android/openglperf/cts/GlPlanetsActivity.java
+++ b/tests/tests/openglperf/src/android/openglperf/cts/GlPlanetsActivity.java
@@ -34,14 +34,15 @@
public static final String INTENT_EXTRA_USE_VBO_VERTICES = "useVboVertices";
public static final String INTENT_EXTRA_USE_VBO_INDICES = "useVboIndiices";
public static final String INTENT_EXTRA_NUM_FRAMES = "numFrames";
- public static final String INTENT_EXTRA_INDICES_PER_VERTEX = "numIndicesPerVertex";
+ public static final String INTENT_EXTRA_NUM_INDEX_BUFFERS = "numIndexBuffers";
public static final String INTENT_RESULT_FPS = "fps";
public static final String INTENT_RESULT_NUM_TRIANGLES = "numTrigngles";
private final Semaphore mSem = new Semaphore(0);
- private float mFps;
+ private float mAverageFps;
private int mNumTriangles;
+ private int[] mFrameInterval;
private PlanetsSurfaceView mView;
@@ -50,14 +51,23 @@
return mSem.tryAcquire(timeoutInSecs, TimeUnit.SECONDS);
}
- public float getFps() {
- return mFps;
+ public float getAverageFps() {
+ return mAverageFps;
}
public int getNumTriangles() {
return mNumTriangles;
}
+ /**
+ * Time interval between each frame's rendering in ms.
+ * The first value will be invalid, so client should discard them.
+ * @return can return null if INTENT_EXTRA_NUM_FRAMES was not set in intent.
+ */
+ public int[] getFrameInterval() {
+ return mFrameInterval;
+ }
+
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -70,16 +80,17 @@
param.mUseVboForIndices = intent.getBooleanExtra(INTENT_EXTRA_USE_VBO_INDICES,
param.mUseVboForIndices);
param.mNumFrames = intent.getIntExtra(INTENT_EXTRA_NUM_FRAMES, param.mNumFrames);
- param.mNumIndicesPerVertex = intent.getIntExtra(INTENT_EXTRA_INDICES_PER_VERTEX,
+ param.mNumIndicesPerVertex = intent.getIntExtra(INTENT_EXTRA_NUM_INDEX_BUFFERS,
param.mNumIndicesPerVertex);
mView = new PlanetsSurfaceView(this, param, this);
setContentView(mView);
}
@Override
- public void onRenderCompletion(float fps, int numTriangles) {
- mFps = fps;
+ public void onRenderCompletion(float averageFps, int numTriangles, int[] frameInterval) {
+ mAverageFps = averageFps;
mNumTriangles = numTriangles;
+ mFrameInterval = frameInterval;
mSem.release();
}
diff --git a/tests/tests/openglperf/src/android/openglperf/cts/GlVboPerfTest.java b/tests/tests/openglperf/src/android/openglperf/cts/GlVboPerfTest.java
index afd435c..2e9e10b 100644
--- a/tests/tests/openglperf/src/android/openglperf/cts/GlVboPerfTest.java
+++ b/tests/tests/openglperf/src/android/openglperf/cts/GlVboPerfTest.java
@@ -87,14 +87,14 @@
}
private void runRendering(int numPlanets, boolean useVboVertex, boolean useVboIndex,
- int indicesPerVertex) throws Exception {
+ int numIndexBuffers) throws Exception {
Intent intent = new Intent();
intent.putExtra(GlPlanetsActivity.INTENT_EXTRA_NUM_FRAMES,
NUM_FRAMES_TO_RENDER);
intent.putExtra(GlPlanetsActivity.INTENT_EXTRA_NUM_PLANETS, numPlanets);
intent.putExtra(GlPlanetsActivity.INTENT_EXTRA_USE_VBO_VERTICES, useVboVertex);
intent.putExtra(GlPlanetsActivity.INTENT_EXTRA_USE_VBO_INDICES, useVboIndex);
- intent.putExtra(GlPlanetsActivity.INTENT_EXTRA_INDICES_PER_VERTEX, indicesPerVertex);
+ intent.putExtra(GlPlanetsActivity.INTENT_EXTRA_NUM_INDEX_BUFFERS, numIndexBuffers);
setActivityIntent(intent);
final GlPlanetsActivity activity = getActivity();
@@ -102,7 +102,7 @@
.waitForGlPlanetsCompletionWithTimeout(RENDERING_TIMEOUT);
assertTrue("timeout while waiting for rendering completion", waitResult);
- mFps = activity.getFps();
+ mFps = activity.getAverageFps();
mNumTriangles = activity.getNumTriangles();
cleanUpActivity();
diff --git a/tests/tests/openglperf/src/android/openglperf/cts/OpenGlPerfNative.java b/tests/tests/openglperf/src/android/openglperf/cts/OpenGlPerfNative.java
new file mode 100644
index 0000000..d63a640
--- /dev/null
+++ b/tests/tests/openglperf/src/android/openglperf/cts/OpenGlPerfNative.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2012 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.openglperf.cts;
+
+public class OpenGlPerfNative {
+ static {
+ System.loadLibrary("ctsopenglperf_jni");
+ }
+ /**
+ * wait for the completion of the pending Egl/Gl operation using eglClientWaitSyncKHR.
+ * @param waitTimeInNs maximum time to wait in nano secs
+ * @return true for success, false for timeout
+ */
+ public static native boolean waitForEglCompletion(long waitTimeInNs);
+}
diff --git a/tests/tests/openglperf/src/android/openglperf/cts/PlanetsRenderer.java b/tests/tests/openglperf/src/android/openglperf/cts/PlanetsRenderer.java
index 5b90089..b286e46 100644
--- a/tests/tests/openglperf/src/android/openglperf/cts/PlanetsRenderer.java
+++ b/tests/tests/openglperf/src/android/openglperf/cts/PlanetsRenderer.java
@@ -17,10 +17,10 @@
package android.openglperf.cts;
import android.content.Context;
+import android.cts.util.WatchDog;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.opengl.GLES20;
-import android.opengl.GLSurfaceView;
import android.opengl.GLUtils;
import android.opengl.Matrix;
import android.util.Log;
@@ -34,18 +34,21 @@
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;
-
-public class PlanetsRenderer implements GLSurfaceView.Renderer {
+/**
+ * OpenGl renderer rendering given number of planets with different GL configuration.
+ */
+public class PlanetsRenderer implements GLSurfaceViewCustom.Renderer {
private static final String TAG = "PlanetsRenderer";
// texture is from
// http://en.wikipedia.org/wiki/File:Mercator_projection_SW.jpg
private static final String TEXTURE_FILE = "world_512_512.jpg";
+ private static final long EGL_SWAP_BUFFERS_WAIT_TIME_IN_NS = 100 * 1000 * 1000 * 1000L;
private final Context mContext;
private final PlanetsRenderingParam mParam;
private final RenderCompletionListener mListener;
- private final RenderingWatchDog mWatchDog;
+ private final WatchDog mWatchDog;
private final Sphere[] mSpheres;
private final int mNumSpheres;
@@ -65,9 +68,11 @@
private int mFrameCount = 0;
private static final int FPS_DISPLAY_INTERVAL = 50;
private long mLastFPSTime;
+ private long mLastRenderingTime;
// for total FPS measurement
private long mRenderingStartTime;
private long mMeasurementStartTime;
+ private int[] mFrameInterval = null;
private int mProgram; // shader program
private int mMVPMatrixHandle;
@@ -95,7 +100,7 @@
* @param listener
*/
public PlanetsRenderer(Context context, PlanetsRenderingParam param,
- RenderCompletionListener listener, RenderingWatchDog watchDog) {
+ RenderCompletionListener listener, WatchDog watchDog) {
resetTimer();
mContext = context;
mParam = param;
@@ -104,6 +109,9 @@
mNumIndices = mNumSpheres * mParam.mNumIndicesPerVertex;
mSpheres = new Sphere[mNumSpheres];
+ if (mParam.mNumFrames > 0) {
+ mFrameInterval = new int[mParam.mNumFrames];
+ }
printParams();
// for big model, this construction phase takes time...
@@ -121,6 +129,7 @@
measureTime("construction");
}
+ @Override
public void onSurfaceCreated(GL10 glUnused, EGLConfig config) {
mProgram = createProgram(getVertexShader(), getFragmentShader());
if (mProgram == 0) {
@@ -137,31 +146,12 @@
mTextureId = createTexture2D();
}
+ @Override
public void onDrawFrame(GL10 glUnused) {
mWatchDog.reset();
long currentTime = System.currentTimeMillis();
-
mFrameCount++;
- if ((mFrameCount % FPS_DISPLAY_INTERVAL == 0) && (mFrameCount != 0)) {
- float fps = (((float) FPS_DISPLAY_INTERVAL)
- / ((float) (currentTime - mLastFPSTime)) * 1000.0f);
- // FPS is not correct if activity is paused/resumed.
- Log.i(TAG, "FPS " + fps);
- mLastFPSTime = currentTime;
- }
-
- if ((mFrameCount == mParam.mNumFrames) && (mParam.mNumFrames > 0)) {
- long timePassed = currentTime - mRenderingStartTime;
- float fps = ((float) mParam.mNumFrames) / ((float) timePassed) * 1000.0f;
- printGlInfos();
- printParams();
- int numTriangles = mNumSpheres * mSpheres[0].getTotalIndices() / 3;
- Log.i(TAG, "Final FPS " + fps + " Num triangles " + numTriangles);
- if (mListener != null) {
- mListener.onRenderCompletion(fps, numTriangles);
- return;
- }
- }
+ mLastRenderingTime = currentTime;
float angle = 0.090f * ((int) (currentTime % 4000L));
Matrix.setRotateM(mMMatrix, 0, angle, 0, 0, 1.0f);
@@ -232,6 +222,36 @@
}
}
+ @Override
+ public void onEglSwapBuffers() {
+ if (!OpenGlPerfNative.waitForEglCompletion(EGL_SWAP_BUFFERS_WAIT_TIME_IN_NS)) {
+ Log.w(TAG, "time-out or error while waiting for eglSwapBuffers completion");
+ }
+ long currentTime = System.currentTimeMillis();
+ if (mFrameCount == 0) {
+ mRenderingStartTime = currentTime;
+ }
+ if (mFrameCount < mParam.mNumFrames) {
+ mFrameInterval[mFrameCount] = (int)(currentTime - mLastRenderingTime);
+ }
+
+ if ((mFrameCount == mParam.mNumFrames) && (mParam.mNumFrames > 0)) {
+ long timePassed = currentTime - mRenderingStartTime;
+ float fps = ((float) mParam.mNumFrames) / ((float) timePassed) * 1000.0f;
+ printGlInfos();
+ printParams();
+ int numTriangles = mNumSpheres * mSpheres[0].getTotalIndices() / 3;
+ Log.i(TAG, "Final FPS " + fps + " Num triangles " + numTriangles + " start time " +
+ mRenderingStartTime + " finish time " + currentTime);
+ if (mListener != null) {
+ mListener.onRenderCompletion(fps, numTriangles, mFrameInterval);
+ mFrameCount++; // to prevent entering here again
+ return;
+ }
+ }
+ }
+
+ @Override
public void onSurfaceChanged(GL10 glUnused, int width, int height) {
mWidth = width;
mHeight = height;
diff --git a/tests/tests/openglperf/src/android/openglperf/cts/PlanetsSurfaceView.java b/tests/tests/openglperf/src/android/openglperf/cts/PlanetsSurfaceView.java
index 2bfd001..072988d 100644
--- a/tests/tests/openglperf/src/android/openglperf/cts/PlanetsSurfaceView.java
+++ b/tests/tests/openglperf/src/android/openglperf/cts/PlanetsSurfaceView.java
@@ -17,11 +17,11 @@
package android.openglperf.cts;
import android.content.Context;
-import android.opengl.GLSurfaceView;
+import android.cts.util.WatchDog;
-class PlanetsSurfaceView extends GLSurfaceView {
+class PlanetsSurfaceView extends GLSurfaceViewCustom {
private final long RENDERING_TIMEOUT = 1900; // in msec, close to 2 secs
- private final RenderingWatchDog mWatchDog = new RenderingWatchDog(RENDERING_TIMEOUT);
+ private final WatchDog mWatchDog = new WatchDog(RENDERING_TIMEOUT);
public PlanetsSurfaceView(Context context, PlanetsRenderingParam param,
RenderCompletionListener listener) {
@@ -44,4 +44,4 @@
setRenderMode(RENDERMODE_CONTINUOUSLY);
super.onResume();
}
-}
\ No newline at end of file
+}
diff --git a/tests/tests/openglperf/src/android/openglperf/cts/RenderCompletionListener.java b/tests/tests/openglperf/src/android/openglperf/cts/RenderCompletionListener.java
index 1643087..a5bbfa2 100644
--- a/tests/tests/openglperf/src/android/openglperf/cts/RenderCompletionListener.java
+++ b/tests/tests/openglperf/src/android/openglperf/cts/RenderCompletionListener.java
@@ -16,11 +16,15 @@
package android.openglperf.cts;
+/**
+ * Interface used to notify the completion of requested rendering.
+ */
public interface RenderCompletionListener {
/**
- * @param fps total frame rate
+ * @param averageFps average of total frames
* @param numTriangles Number of triangles in geometric model
+ * @param frameInterval interval for each frame in ms. Do not use the first one and the last one.
*/
- void onRenderCompletion(float fps, int numTriangles);
+ void onRenderCompletion(float averageFps, int numTriangles, int[] frameInterval);
}
diff --git a/tests/tests/openglperf/src/android/openglperf/cts/Sphere.java b/tests/tests/openglperf/src/android/openglperf/cts/Sphere.java
index 026ba97..8020db6 100644
--- a/tests/tests/openglperf/src/android/openglperf/cts/Sphere.java
+++ b/tests/tests/openglperf/src/android/openglperf/cts/Sphere.java
@@ -49,7 +49,7 @@
* @param x,y,z the origin of the sphere
* @param r the radius of the sphere
*/
- public Sphere(int nSlices, float x, float y, float z, float r, int indicesPerVertex) {
+ public Sphere(int nSlices, float x, float y, float z, float r, int numIndexBuffers) {
int iMax = nSlices + 1;
int nVertices = iMax * iMax;
@@ -64,17 +64,17 @@
// 3 vertex coords + 2 texture coords
mVertices = ByteBuffer.allocateDirect(nVertices * 5 * FLOAT_SIZE)
.order(ByteOrder.nativeOrder()).asFloatBuffer();
- mIndices = new ShortBuffer[indicesPerVertex];
- mNumIndices = new int[indicesPerVertex];
+ mIndices = new ShortBuffer[numIndexBuffers];
+ mNumIndices = new int[numIndexBuffers];
// first evenly distribute to n-1 buffers, then put remaining ones to the last one.
- int noIndicesPerBuffer = (mTotalIndices / indicesPerVertex / 6) * 6;
- for (int i = 0; i < indicesPerVertex - 1; i++) {
+ int noIndicesPerBuffer = (mTotalIndices / numIndexBuffers / 6) * 6;
+ for (int i = 0; i < numIndexBuffers - 1; i++) {
mNumIndices[i] = noIndicesPerBuffer;
}
- mNumIndices[indicesPerVertex - 1] = mTotalIndices - noIndicesPerBuffer *
- (indicesPerVertex - 1);
+ mNumIndices[numIndexBuffers - 1] = mTotalIndices - noIndicesPerBuffer *
+ (numIndexBuffers - 1);
- for (int i = 0; i < indicesPerVertex; i++) {
+ for (int i = 0; i < numIndexBuffers; i++) {
mIndices[i] = ByteBuffer.allocateDirect(mNumIndices[i] * SHORT_SIZE)
.order(ByteOrder.nativeOrder()).asShortBuffer();
}
@@ -123,7 +123,7 @@
mIndices[bufferNum].put(indexBuffer, 0, mNumIndices[bufferNum]);
mVertices.position(0);
- for (int i = 0; i < indicesPerVertex; i++) {
+ for (int i = 0; i < numIndexBuffers; i++) {
mIndices[i].position(0);
}
}
diff --git a/tests/tests/os/src/android/os/cts/WorkSourceTest.java b/tests/tests/os/src/android/os/cts/WorkSourceTest.java
new file mode 100644
index 0000000..f3986b1
--- /dev/null
+++ b/tests/tests/os/src/android/os/cts/WorkSourceTest.java
@@ -0,0 +1,557 @@
+/*
+ * Copyright (C) 2013 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.os.cts;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+import android.os.WorkSource;
+import android.test.AndroidTestCase;
+
+public class WorkSourceTest extends AndroidTestCase {
+ private Constructor<WorkSource> mConstructWS;
+ private Object[] mConstructWSArgs = new Object[1];
+ private Method mAddUid;
+ private Object[] mAddUidArgs = new Object[1];
+ private Method mAddUidName;
+ private Object[] mAddUidNameArgs = new Object[2];
+ private Method mAddReturningNewbs;
+ private Object[] mAddReturningNewbsArgs = new Object[1];
+ private Method mSetReturningDiffs;
+ private Object[] mSetReturningDiffsArgs = new Object[1];
+ private Method mStripNames;
+ private Object[] mStripNamesArgs = new Object[0];
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ mConstructWS = WorkSource.class.getConstructor(new Class[] { int.class });
+ mAddUid = WorkSource.class.getMethod("add", new Class[] { int.class });
+ mAddUidName = WorkSource.class.getMethod("add", new Class[] { int.class, String.class });
+ mAddReturningNewbs = WorkSource.class.getMethod("addReturningNewbs", new Class[] { WorkSource.class });
+ mSetReturningDiffs = WorkSource.class.getMethod("setReturningDiffs", new Class[] { WorkSource.class });
+ mStripNames = WorkSource.class.getMethod("stripNames", new Class[] { });
+ }
+
+ private WorkSource wsNew(int uid) throws IllegalArgumentException,
+ InstantiationException, IllegalAccessException, InvocationTargetException {
+ mConstructWSArgs[0] = uid;
+ return mConstructWS.newInstance(mConstructWSArgs);
+ }
+
+ private WorkSource wsNew(int[] uids) throws IllegalArgumentException,
+ InstantiationException, IllegalAccessException, InvocationTargetException {
+ WorkSource ws = new WorkSource();
+ for (int i=0; i<uids.length; i++) {
+ wsAdd(ws, uids[i]);
+ }
+ checkWorkSource("Constructed", ws, uids);
+ return ws;
+ }
+
+ private WorkSource wsNew(int[] uids, String[] names) throws IllegalArgumentException,
+ InstantiationException, IllegalAccessException, InvocationTargetException {
+ WorkSource ws = new WorkSource();
+ for (int i=0; i<uids.length; i++) {
+ wsAdd(ws, uids[i], names[i]);
+ }
+ checkWorkSource("Constructed", ws, uids, names);
+ return ws;
+ }
+
+ private boolean wsAdd(WorkSource ws, int uid) throws IllegalArgumentException,
+ InstantiationException, IllegalAccessException, InvocationTargetException {
+ mAddUidArgs[0] = uid;
+ return (Boolean)mAddUid.invoke(ws, mAddUidArgs);
+ }
+
+ private boolean wsAdd(WorkSource ws, int uid, String name) throws IllegalArgumentException,
+ InstantiationException, IllegalAccessException, InvocationTargetException {
+ mAddUidNameArgs[0] = uid;
+ mAddUidNameArgs[1] = name;
+ return (Boolean)mAddUidName.invoke(ws, mAddUidNameArgs);
+ }
+
+ private WorkSource wsAddReturningNewbs(WorkSource ws, WorkSource other) throws IllegalArgumentException,
+ InstantiationException, IllegalAccessException, InvocationTargetException {
+ mAddReturningNewbsArgs[0] = other;
+ return (WorkSource)mAddReturningNewbs.invoke(ws, mAddReturningNewbsArgs);
+ }
+
+ private WorkSource[] wsSetReturningDiffs(WorkSource ws, WorkSource other) throws IllegalArgumentException,
+ InstantiationException, IllegalAccessException, InvocationTargetException {
+ mSetReturningDiffsArgs[0] = other;
+ return (WorkSource[])mSetReturningDiffs.invoke(ws, mSetReturningDiffsArgs);
+ }
+
+ private WorkSource wsStripNames(WorkSource ws) throws IllegalArgumentException,
+ InstantiationException, IllegalAccessException, InvocationTargetException {
+ return (WorkSource)mStripNames.invoke(ws);
+ }
+
+ private void printArrays(StringBuilder sb, int[] uids, String[] names) {
+ sb.append("{ ");
+ for (int i=0; i<uids.length; i++) {
+ if (i > 0) sb.append(", ");
+ sb.append(uids[i]);
+ if (names != null) {
+ sb.append(" ");
+ sb.append(names[i]);
+ }
+ }
+ sb.append(" }");
+ }
+
+ private void failWorkSource(String op, WorkSource ws, int[] uids) {
+ StringBuilder sb = new StringBuilder();
+ sb.append(op);
+ sb.append(": Expected: ");
+ printArrays(sb, uids, null);
+ sb.append(", got: ");
+ sb.append(ws);
+ fail(sb.toString());
+ }
+
+ private void failWorkSource(String op, WorkSource ws, int[] uids, String[] names) {
+ StringBuilder sb = new StringBuilder();
+ sb.append(op);
+ sb.append(": Expected: ");
+ printArrays(sb, uids, names);
+ sb.append(", got: ");
+ sb.append(ws);
+ fail(sb.toString());
+ }
+
+ private void checkWorkSource(String op, WorkSource ws, int[] uids) {
+ if (ws == null || uids == null) {
+ if (ws != null) {
+ fail(op + ": WorkSource is not null " + ws +", but expected null");
+ }
+ if (uids != null) {
+ fail(op + "WorkSource is null, but expected non-null: " + uids);
+ }
+ return;
+ }
+ if (ws.size() != uids.length) {
+ failWorkSource(op, ws, uids);
+ }
+ for (int i=0; i<uids.length; i++) {
+ if (uids[i] != ws.get(i)) {
+ failWorkSource(op, ws, uids);
+ }
+ }
+ }
+
+ private void checkWorkSource(String op, WorkSource ws, int[] uids, String[] names) {
+ if (ws == null || uids == null) {
+ if (ws != null) {
+ fail(op + ": WorkSource is not null " + ws +", but expected null");
+ }
+ if (uids != null) {
+ fail(op + "WorkSource is null, but expected non-null: " + uids);
+ }
+ return;
+ }
+ if (ws.size() != uids.length) {
+ failWorkSource(op, ws, uids, names);
+ }
+ for (int i=0; i<uids.length; i++) {
+ if (uids[i] != ws.get(i) || !names[i].equals(ws.getName(i))) {
+ failWorkSource(op, ws, uids, names);
+ }
+ }
+ }
+
+ public void testConstructEmpty() {
+ checkWorkSource("Empty", new WorkSource(), new int[] { });
+ }
+
+ public void testConstructSingle() throws Exception {
+ checkWorkSource("Single 1", wsNew(1), new int[] { 1 });
+ }
+
+ public void testAddRawOrdered() throws Exception {
+ WorkSource ws = wsNew(1);
+ wsAdd(ws, 2);
+ checkWorkSource("First", ws, new int[] { 1 , 2 });
+ wsAdd(ws, 20);
+ checkWorkSource("Second", ws, new int[] { 1 , 2, 20 });
+ wsAdd(ws, 100);
+ checkWorkSource("Third", ws, new int[] { 1, 2, 20, 100 });
+ }
+
+ public void testAddRawRevOrdered() throws Exception {
+ WorkSource ws = wsNew(100);
+ wsAdd(ws, 20);
+ checkWorkSource("First", ws, new int[] { 20, 100 });
+ wsAdd(ws, 2);
+ checkWorkSource("Second", ws, new int[] { 2, 20, 100 });
+ wsAdd(ws, 1);
+ checkWorkSource("Third", ws, new int[] { 1, 2, 20, 100 });
+ }
+
+ public void testAddRawUnordered() throws Exception {
+ WorkSource ws = wsNew(10);
+ wsAdd(ws, 2);
+ checkWorkSource("First", ws, new int[] { 2, 10 });
+ wsAdd(ws, 5);
+ checkWorkSource("Second", ws, new int[] { 2, 5, 10 });
+ wsAdd(ws, 1);
+ checkWorkSource("Third", ws, new int[] { 1, 2, 5, 10 });
+ wsAdd(ws, 100);
+ checkWorkSource("Fourth", ws, new int[] { 1, 2, 5, 10, 100 });
+ }
+
+ public void testAddWsOrdered() throws Exception {
+ WorkSource ws = wsNew(1);
+ ws.add(wsNew(2));
+ checkWorkSource("First", ws, new int[] { 1 , 2 });
+ ws.add(wsNew(20));
+ checkWorkSource("Second", ws, new int[] { 1 , 2, 20 });
+ ws.add(wsNew(100));
+ checkWorkSource("Third", ws, new int[] { 1 , 2, 20, 100 });
+ }
+
+ public void testAddWsRevOrdered() throws Exception {
+ WorkSource ws = wsNew(100);
+ ws.add(wsNew(20));
+ checkWorkSource("First", ws, new int[] { 20, 100 });
+ ws.add(wsNew(2));
+ checkWorkSource("Second", ws, new int[] { 2, 20, 100 });
+ ws.add(wsNew(1));
+ checkWorkSource("Third", ws, new int[] { 1, 2, 20, 100 });
+ }
+
+ public void testAddWsUnordered() throws Exception {
+ WorkSource ws = wsNew(10);
+ ws.add(wsNew(2));
+ checkWorkSource("First", ws, new int[] { 2, 10 });
+ ws.add(wsNew(5));
+ checkWorkSource("Second", ws, new int[] { 2, 5, 10 });
+ ws.add(wsNew(1));
+ checkWorkSource("Third", ws, new int[] { 1, 2, 5, 10 });
+ ws.add(wsNew(100));
+ checkWorkSource("Fourth", ws, new int[] { 1, 2, 5, 10, 100 });
+ }
+
+ private void doTestCombineTwoUids(int[] lhs, int[] rhs, int[] expected, int[] newbs,
+ int[] gones) throws Exception {
+ WorkSource ws1 = wsNew(lhs);
+ WorkSource ws2 = wsNew(rhs);
+ ws1.add(ws2);
+ checkWorkSource("Add result", ws1, expected);
+ ws1 = wsNew(lhs);
+ WorkSource wsNewbs = wsAddReturningNewbs(ws1, ws2);
+ checkWorkSource("AddReturning result", ws1, expected);
+ checkWorkSource("Newbs", wsNewbs, newbs);
+ ws1 = wsNew(lhs);
+ WorkSource[] res = wsSetReturningDiffs(ws1, ws2);
+ checkWorkSource("SetReturning result", ws1, rhs);
+ checkWorkSource("Newbs", res[0], newbs);
+ checkWorkSource("Gones", res[1], gones);
+ }
+
+ private int[] makeRepeatingIntArray(String[] stringarray, int value) {
+ if (stringarray == null) {
+ return null;
+ }
+ int[] res = new int[stringarray.length];
+ for (int i=0; i<stringarray.length; i++) {
+ res[i] = value;
+ }
+ return res;
+ }
+
+ private void doTestCombineTwoNames(String[] lhsnames, String[] rhsnames,
+ String[] expectednames, String[] newbnames,
+ String[] gonenames) throws Exception {
+ int[] lhs = makeRepeatingIntArray(lhsnames, 0);
+ int[] rhs = makeRepeatingIntArray(rhsnames, 0);
+ int[] expected = makeRepeatingIntArray(expectednames, 0);
+ int[] newbs = makeRepeatingIntArray(newbnames, 0);
+ int[] gones = makeRepeatingIntArray(gonenames, 0);
+ doTestCombineTwoUidsNames(lhs, lhsnames, rhs, rhsnames, expected, expectednames,
+ newbs, newbnames, gones, gonenames);
+ }
+
+ private void doTestCombineTwoUidsNames(int[] lhs, String[] lhsnames, int[] rhs, String[] rhsnames,
+ int[] expected, String[] expectednames, int[] newbs, String[] newbnames,
+ int[] gones, String[] gonenames) throws Exception {
+ WorkSource ws1 = wsNew(lhs, lhsnames);
+ WorkSource ws2 = wsNew(rhs, rhsnames);
+ ws1.add(ws2);
+ checkWorkSource("Add result", ws1, expected, expectednames);
+ ws1 = wsNew(lhs, lhsnames);
+ WorkSource wsNewbs = wsAddReturningNewbs(ws1, ws2);
+ checkWorkSource("AddReturning result", ws1, expected, expectednames);
+ checkWorkSource("Newbs", wsNewbs, newbs, newbnames);
+ ws1 = wsNew(lhs, lhsnames);
+ WorkSource[] res = wsSetReturningDiffs(ws1, ws2);
+ checkWorkSource("SetReturning result", ws1, rhs, rhsnames);
+ checkWorkSource("Newbs", res[0], newbs, newbnames);
+ checkWorkSource("Gones", res[1], gones, gonenames);
+ }
+
+ private String[] makeRepeatingStringArray(int[] intarray, String value) {
+ if (intarray == null) {
+ return null;
+ }
+ String[] res = new String[intarray.length];
+ for (int i=0; i<intarray.length; i++) {
+ res[i] = value;
+ }
+ return res;
+ }
+
+ private String[] makeStringArray(int[] intarray) {
+ if (intarray == null) {
+ return null;
+ }
+ String[] res = new String[intarray.length];
+ for (int i=0; i<intarray.length; i++) {
+ res[i] = Character.toString((char)('A' + intarray[i]));
+ }
+ return res;
+ }
+
+ private void doTestCombineTwo(int[] lhs, int[] rhs, int[] expected, int[] newbs,
+ int[] gones) throws Exception {
+ doTestCombineTwoUids(lhs, rhs, expected, newbs, gones);
+ doTestCombineTwoUidsNames(lhs, makeRepeatingStringArray(lhs, "A"),
+ rhs, makeRepeatingStringArray(rhs, "A"),
+ expected, makeRepeatingStringArray(expected, "A"),
+ newbs, makeRepeatingStringArray(newbs, "A"),
+ gones, makeRepeatingStringArray(gones, "A"));
+ doTestCombineTwoNames(makeStringArray(lhs), makeStringArray(rhs),
+ makeStringArray(expected), makeStringArray(newbs), makeStringArray(gones));
+ }
+
+ public void testCombineMultiFront() throws Exception {
+ doTestCombineTwo(
+ new int[] { 10, 20, 30, 40 },
+ new int[] { 1, 2, 15, 16 },
+ new int[] { 1, 2, 10, 15, 16, 20, 30, 40 },
+ new int[] { 1, 2, 15, 16 },
+ new int[] { 10, 20, 30, 40 });
+ }
+
+ public void testCombineMultiMiddle() throws Exception {
+ doTestCombineTwo(
+ new int[] { 10, 20, 30, 40 },
+ new int[] { 12, 14, 22 },
+ new int[] { 10, 12, 14, 20, 22, 30, 40 },
+ new int[] { 12, 14, 22 },
+ new int[] { 10, 20, 30, 40 });
+ }
+
+ public void testCombineMultiEnd() throws Exception {
+ doTestCombineTwo(
+ new int[] { 10, 20, 30, 40 },
+ new int[] { 35, 45, 50 },
+ new int[] { 10, 20, 30, 35, 40, 45, 50 },
+ new int[] { 35, 45, 50 },
+ new int[] { 10, 20, 30, 40 });
+ }
+
+ public void testCombineMultiFull() throws Exception {
+ doTestCombineTwo(
+ new int[] { 10, 20, 30, 40 },
+ new int[] { 1, 2, 15, 35, 50 },
+ new int[] { 1, 2, 10, 15, 20, 30, 35, 40, 50 },
+ new int[] { 1, 2, 15, 35, 50 },
+ new int[] { 10, 20, 30, 40 });
+ }
+
+ public void testCombineMultiSame() throws Exception {
+ doTestCombineTwo(
+ new int[] { 10, 20, 30, 40 },
+ new int[] { 10, 20, 30 },
+ new int[] { 10, 20, 30, 40 },
+ null,
+ new int[] { 40 });
+ }
+
+ public void testCombineMultiSomeSame() throws Exception {
+ doTestCombineTwo(
+ new int[] { 10, 20, 30, 40 },
+ new int[] { 1, 30, 40 },
+ new int[] { 1, 10, 20, 30, 40 },
+ new int[] { 1 },
+ new int[] { 10, 20 });
+ }
+
+ public void testCombineMultiSomeSameUidsNames() throws Exception {
+ doTestCombineTwoUidsNames(
+ new int[] { 10, 10, 20, 30, 30, 30, 40 },
+ new String[] { "A", "B", "A", "A", "B", "C", "A" },
+ new int[] { 1, 30, 40, 50 },
+ new String[] { "A", "A", "B", "A" },
+ new int[] { 1, 10, 10, 20, 30, 30, 30, 40, 40, 50 },
+ new String[] { "A", "A", "B", "A", "A", "B", "C", "A", "B", "A" },
+ new int[] { 1, 40, 50 },
+ new String[] { "A", "B", "A" },
+ new int[] { 10, 10, 20, 30, 30, 40 },
+ new String[] { "A", "B", "A", "B", "C", "A" });
+ }
+
+ private void doTestRemoveUids(int[] lhs, int[] rhs, int[] result, boolean diff) throws Exception {
+ WorkSource ws1 = wsNew(lhs);
+ WorkSource ws2 = wsNew(rhs);
+ boolean diffres = ws1.remove(ws2);
+ if (diffres != diff) {
+ StringBuilder sb = new StringBuilder();
+ sb.append("Expected diff ");
+ sb.append(diff);
+ sb.append(" but got ");
+ sb.append(diffres);
+ sb.append(" when removing ");
+ sb.append(ws2);
+ sb.append(" from ");
+ sb.append(ws1);
+ fail(sb.toString());
+ }
+ checkWorkSource("Remove", ws1, result);
+ }
+
+ private void doTestRemoveNames(String[] lhsnames, String[] rhsnames,
+ String[] resultnames, boolean diff) throws Exception {
+ int[] lhs = makeRepeatingIntArray(lhsnames, 0);
+ int[] rhs = makeRepeatingIntArray(rhsnames, 0);
+ int[] result = makeRepeatingIntArray(resultnames, 0);
+ WorkSource ws1 = wsNew(lhs, lhsnames);
+ WorkSource ws2 = wsNew(rhs, rhsnames);
+ boolean diffres = ws1.remove(ws2);
+ if (diffres != diff) {
+ StringBuilder sb = new StringBuilder();
+ sb.append("Expected diff ");
+ sb.append(diff);
+ sb.append(" but got ");
+ sb.append(diffres);
+ sb.append(" when removing ");
+ sb.append(ws2);
+ sb.append(" from ");
+ sb.append(ws1);
+ fail(sb.toString());
+ }
+ checkWorkSource("Remove", ws1, result, resultnames);
+ }
+
+ private void doTestRemoveUidsNames(int[] lhs, String[] lhsnames, int[] rhs, String[] rhsnames,
+ int[] result, String[] resultnames, boolean diff) throws Exception {
+ WorkSource ws1 = wsNew(lhs, lhsnames);
+ WorkSource ws2 = wsNew(rhs, rhsnames);
+ boolean diffres = ws1.remove(ws2);
+ if (diffres != diff) {
+ StringBuilder sb = new StringBuilder();
+ sb.append("Expected diff ");
+ sb.append(diff);
+ sb.append(" but got ");
+ sb.append(diffres);
+ sb.append(" when removing ");
+ sb.append(ws2);
+ sb.append(" from ");
+ sb.append(ws1);
+ fail(sb.toString());
+ }
+ checkWorkSource("Remove", ws1, result, resultnames);
+ }
+
+ private void doTestRemove(int[] lhs, int[] rhs, int[] result, boolean diff) throws Exception {
+ doTestRemoveUids(lhs, rhs, result, diff);
+ doTestRemoveUidsNames(lhs, makeRepeatingStringArray(lhs, "A"),
+ rhs, makeRepeatingStringArray(rhs, "A"),
+ result, makeRepeatingStringArray(result, "A"),
+ diff);
+ doTestRemoveNames(makeStringArray(lhs), makeStringArray(rhs),
+ makeStringArray(result), diff);
+ }
+
+ public void testRemoveNone() throws Exception {
+ doTestRemove(
+ new int[] { 10, 20, 30, 40 },
+ new int[] { 1, 2, 35, 50 },
+ new int[] { 10, 20, 30, 40 },
+ false);
+ }
+
+ public void testRemoveMultiFront() throws Exception {
+ doTestRemove(
+ new int[] { 10, 20, 30, 40 },
+ new int[] { 1, 2, 10, 30 },
+ new int[] { 20, 40 },
+ true);
+ }
+
+ public void testRemoveMultiMiddle() throws Exception {
+ doTestRemove(
+ new int[] { 10, 20, 30, 40 },
+ new int[] { 20, 30 },
+ new int[] { 10, 40 },
+ true);
+ }
+
+ public void testRemoveMultiEnd() throws Exception {
+ doTestRemove(
+ new int[] { 10, 20, 30, 40 },
+ new int[] { 30, 40, 50 },
+ new int[] { 10, 20 },
+ true);
+ }
+
+ public void testRemoveMultiFull() throws Exception {
+ doTestRemove(
+ new int[] { 10, 20, 30, 40 },
+ new int[] { 1, 2, 20, 25, 35, 40 },
+ new int[] { 10, 30 },
+ true);
+ }
+
+ public void testRemoveMultiAll() throws Exception {
+ doTestRemove(
+ new int[] { 10, 20, 30, 40 },
+ new int[] { 10, 20, 30, 40 },
+ new int[] { },
+ true);
+ }
+
+ private void doTestStripNames(int[] uids, String[] names, int[] expected) throws Exception {
+ WorkSource ws1 = wsNew(uids, names);
+ WorkSource res = wsStripNames(ws1);
+ checkWorkSource("StripNames", res, expected);
+ }
+
+ public void testStripNamesSimple() throws Exception {
+ doTestStripNames(
+ new int[] { 10, 20, 30, 40 },
+ new String[] { "A", "A", "A", "A" },
+ new int[] { 10, 20, 30, 40 });
+ }
+
+ public void testStripNamesFull() throws Exception {
+ doTestStripNames(
+ new int[] { 10, 10, 10, 10 },
+ new String[] { "A", "B", "C", "D" },
+ new int[] { 10 });
+ }
+
+ public void testStripNamesComplex() throws Exception {
+ doTestStripNames(
+ new int[] { 10, 20, 20, 30, 40, 40 },
+ new String[] { "A", "A", "B", "A", "A", "B" },
+ new int[] { 10, 20, 30, 40 });
+ }
+}
diff --git a/tests/tests/permission2/src/android/permission2/cts/NoReceiveGsmSmsPermissionTest.java b/tests/tests/permission2/src/android/permission2/cts/NoReceiveGsmSmsPermissionTest.java
deleted file mode 100644
index 9b20d1d..0000000
--- a/tests/tests/permission2/src/android/permission2/cts/NoReceiveGsmSmsPermissionTest.java
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright (C) 2009 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.permission2.cts;
-
-import android.app.PendingIntent;
-import android.telephony.gsm.SmsManager;
-
-/**
- * Verify Sms and Mms cannot be received without required permissions.
- * Uses {@link android.telephony.gsm.SmsManager}.
- */
-@SuppressWarnings("deprecation")
-public class NoReceiveGsmSmsPermissionTest extends NoReceiveSmsPermissionTest {
-
- protected void sendSms(PendingIntent sentIntent, PendingIntent deliveryIntent,
- String currentNumber) {
- SmsManager.getDefault().sendTextMessage(currentNumber, null, "test message",
- sentIntent, deliveryIntent);
- }
-}
diff --git a/tests/tests/provider/src/android/provider/cts/CalendarTest.java b/tests/tests/provider/src/android/provider/cts/CalendarTest.java
index e460453..3c10d0c 100644
--- a/tests/tests/provider/src/android/provider/cts/CalendarTest.java
+++ b/tests/tests/provider/src/android/provider/cts/CalendarTest.java
@@ -1769,15 +1769,34 @@
// Test that inserting a valid color index works
ev = EventHelper.getNewEventValues(account, seed++, cal_id, false);
- ev.put(Events.EVENT_COLOR_KEY, ColorHelper.DEFAULT_INDICES[ColorHelper.E_COLOR_0]);
+ final String defaultColorIndex = ColorHelper.DEFAULT_INDICES[ColorHelper.E_COLOR_0];
+ ev.put(Events.EVENT_COLOR_KEY, defaultColorIndex);
Uri uri = mContentResolver.insert(Events.CONTENT_URI, ev);
long eventId2 = ContentUris.parseId(uri);
assertTrue(eventId2 >= 0);
// And updates the event's color to the one in the table
- ev.put(Events.EVENT_COLOR, ColorHelper.DEFAULT_COLORS[ColorHelper.E_COLOR_0]);
+ final int expectedColor = ColorHelper.DEFAULT_COLORS[ColorHelper.E_COLOR_0];
+ ev.put(Events.EVENT_COLOR, expectedColor);
verifyEvent(ev, eventId2);
+ // Test that event iterator has COLOR columns
+ final EntityIterator iterator = EventsEntity.newEntityIterator(mContentResolver.query(
+ ContentUris.withAppendedId(EventsEntity.CONTENT_URI, eventId2),
+ null, null, null, null), mContentResolver);
+ assertTrue("Empty Iterator", iterator.hasNext());
+ final Entity entity = iterator.next();
+ final ContentValues values = entity.getEntityValues();
+ assertTrue("Missing EVENT_COLOR", values.containsKey(EventsEntity.EVENT_COLOR));
+ assertEquals("Wrong EVENT_COLOR",
+ expectedColor,
+ (int) values.getAsInteger(EventsEntity.EVENT_COLOR));
+ assertTrue("Missing EVENT_COLOR_KEY", values.containsKey(EventsEntity.EVENT_COLOR_KEY));
+ assertEquals("Wrong EVENT_COLOR_KEY",
+ defaultColorIndex,
+ values.getAsString(EventsEntity.EVENT_COLOR_KEY));
+ iterator.close();
+
// Test that updating a valid color index also updates the color in an
// event
ev.clear();
diff --git a/tests/tests/provider/src/android/provider/cts/MediaStore_Audio_AlbumsTest.java b/tests/tests/provider/src/android/provider/cts/MediaStore_Audio_AlbumsTest.java
index 100d9e1..b265cbf 100644
--- a/tests/tests/provider/src/android/provider/cts/MediaStore_Audio_AlbumsTest.java
+++ b/tests/tests/provider/src/android/provider/cts/MediaStore_Audio_AlbumsTest.java
@@ -45,12 +45,15 @@
}
public void testGetContentUri() {
- assertNotNull(mContentResolver.query(
+ Cursor c = null;
+ assertNotNull(c = mContentResolver.query(
Albums.getContentUri(MediaStoreAudioTestHelper.INTERNAL_VOLUME_NAME), null, null,
null, null));
- assertNotNull(mContentResolver.query(
+ c.close();
+ assertNotNull(c = mContentResolver.query(
Albums.getContentUri(MediaStoreAudioTestHelper.EXTERNAL_VOLUME_NAME), null, null,
null, null));
+ c.close();
// can not accept any other volume names
String volume = "fakeVolume";
diff --git a/tests/tests/provider/src/android/provider/cts/MediaStore_Audio_ArtistsTest.java b/tests/tests/provider/src/android/provider/cts/MediaStore_Audio_ArtistsTest.java
index 2e3cc1b..9fe31c5 100644
--- a/tests/tests/provider/src/android/provider/cts/MediaStore_Audio_ArtistsTest.java
+++ b/tests/tests/provider/src/android/provider/cts/MediaStore_Audio_ArtistsTest.java
@@ -37,12 +37,15 @@
}
public void testGetContentUri() {
- assertNotNull(mContentResolver.query(
+ Cursor c = null;
+ assertNotNull(c = mContentResolver.query(
Artists.getContentUri(MediaStoreAudioTestHelper.INTERNAL_VOLUME_NAME), null, null,
null, null));
- assertNotNull(mContentResolver.query(
+ c.close();
+ assertNotNull(c = mContentResolver.query(
Artists.getContentUri(MediaStoreAudioTestHelper.INTERNAL_VOLUME_NAME), null, null,
null, null));
+ c.close();
// can not accept any other volume names
String volume = "fakeVolume";
diff --git a/tests/tests/provider/src/android/provider/cts/MediaStore_Audio_Artists_AlbumsTest.java b/tests/tests/provider/src/android/provider/cts/MediaStore_Audio_Artists_AlbumsTest.java
index fdba619..72d9067 100644
--- a/tests/tests/provider/src/android/provider/cts/MediaStore_Audio_Artists_AlbumsTest.java
+++ b/tests/tests/provider/src/android/provider/cts/MediaStore_Audio_Artists_AlbumsTest.java
@@ -39,12 +39,16 @@
}
public void testGetContentUri() {
+ Cursor c = null;
Uri contentUri = MediaStore.Audio.Artists.Albums.getContentUri(
MediaStoreAudioTestHelper.INTERNAL_VOLUME_NAME, 1);
- assertNotNull(mContentResolver.query(contentUri, null, null, null, null));
+ assertNotNull(c = mContentResolver.query(contentUri, null, null, null, null));
+ c.close();
+
contentUri = MediaStore.Audio.Artists.Albums.getContentUri(
MediaStoreAudioTestHelper.EXTERNAL_VOLUME_NAME, 1);
- assertNotNull(mContentResolver.query(contentUri, null, null, null, null));
+ assertNotNull(c = mContentResolver.query(contentUri, null, null, null, null));
+ c.close();
// can not accept any other volume names
String volume = "fakeVolume";
diff --git a/tests/tests/provider/src/android/provider/cts/MediaStore_Audio_GenresTest.java b/tests/tests/provider/src/android/provider/cts/MediaStore_Audio_GenresTest.java
index 1138233..8d41b38 100644
--- a/tests/tests/provider/src/android/provider/cts/MediaStore_Audio_GenresTest.java
+++ b/tests/tests/provider/src/android/provider/cts/MediaStore_Audio_GenresTest.java
@@ -39,14 +39,16 @@
}
public void testGetContentUri() {
- assertNotNull(mContentResolver.query(
+ Cursor c = null;
+ assertNotNull(c = mContentResolver.query(
Genres.getContentUri(MediaStoreAudioTestHelper.EXTERNAL_VOLUME_NAME), null, null,
null, null));
-
+ c.close();
try {
- assertNotNull(mContentResolver.query(
+ assertNotNull(c = mContentResolver.query(
Genres.getContentUri(MediaStoreAudioTestHelper.INTERNAL_VOLUME_NAME), null,
null, null, null));
+ c.close();
fail("Should throw SQLException as the internal datatbase has no genre");
} catch (SQLException e) {
// expected
@@ -98,6 +100,7 @@
// Insert an audio file into the content provider.
ContentValues values = Audio1.getInstance().getContentValues(true);
Uri audioUri = mContentResolver.insert(Media.EXTERNAL_CONTENT_URI, values);
+ assertNotNull(audioUri);
long audioId = ContentUris.parseId(audioUri);
assertTrue(audioId != -1);
@@ -105,6 +108,7 @@
values.clear();
values.put(Genres.NAME, "Soda Pop");
Uri genreUri = mContentResolver.insert(Genres.EXTERNAL_CONTENT_URI, values);
+ assertNotNull(genreUri);
long genreId = ContentUris.parseId(genreUri);
assertTrue(genreId != -1);
@@ -131,6 +135,8 @@
if (cursor != null) {
cursor.close();
}
+ assertEquals(1, mContentResolver.delete(audioUri, null, null));
+ assertEquals(1, mContentResolver.delete(genreUri, null, null));
}
}
}
diff --git a/tests/tests/provider/src/android/provider/cts/MediaStore_Audio_Genres_MembersTest.java b/tests/tests/provider/src/android/provider/cts/MediaStore_Audio_Genres_MembersTest.java
index 2e389b6..7310fa1 100644
--- a/tests/tests/provider/src/android/provider/cts/MediaStore_Audio_Genres_MembersTest.java
+++ b/tests/tests/provider/src/android/provider/cts/MediaStore_Audio_Genres_MembersTest.java
@@ -65,14 +65,17 @@
}
public void testGetContentUri() {
- assertNotNull(mContentResolver.query(
+ Cursor c = null;
+ assertNotNull(c = mContentResolver.query(
Members.getContentUri(MediaStoreAudioTestHelper.EXTERNAL_VOLUME_NAME, 1), null,
null, null, null));
+ c.close();
try {
- assertNotNull(mContentResolver.query(
+ assertNotNull(c = mContentResolver.query(
Members.getContentUri(MediaStoreAudioTestHelper.INTERNAL_VOLUME_NAME, 1), null,
null, null, null));
+ c.close();
fail("Should throw SQLException as the internal datatbase has no genre");
} catch (SQLException e) {
// expected
diff --git a/tests/tests/provider/src/android/provider/cts/MediaStore_Audio_MediaTest.java b/tests/tests/provider/src/android/provider/cts/MediaStore_Audio_MediaTest.java
index 8a4e3ee..82c2342 100644
--- a/tests/tests/provider/src/android/provider/cts/MediaStore_Audio_MediaTest.java
+++ b/tests/tests/provider/src/android/provider/cts/MediaStore_Audio_MediaTest.java
@@ -38,12 +38,15 @@
}
public void testGetContentUri() {
- assertNotNull(mContentResolver.query(
+ Cursor c = null;
+ assertNotNull(c = mContentResolver.query(
Media.getContentUri(MediaStoreAudioTestHelper.INTERNAL_VOLUME_NAME), null, null,
null, null));
- assertNotNull(mContentResolver.query(
+ c.close();
+ assertNotNull(c = mContentResolver.query(
Media.getContentUri(MediaStoreAudioTestHelper.EXTERNAL_VOLUME_NAME), null, null,
null, null));
+ c.close();
// can not accept any other volume names
String volume = "faveVolume";
@@ -51,14 +54,17 @@
}
public void testGetContentUriForPath() {
+ Cursor c = null;
String externalPath = Environment.getExternalStorageDirectory().getPath();
- assertNotNull(mContentResolver.query(Media.getContentUriForPath(externalPath), null, null,
+ assertNotNull(c = mContentResolver.query(Media.getContentUriForPath(externalPath), null, null,
null, null));
+ c.close();
String internalPath =
getInstrumentation().getTargetContext().getFilesDir().getAbsolutePath();
- assertNotNull(mContentResolver.query(Media.getContentUriForPath(internalPath), null, null,
+ assertNotNull(c = mContentResolver.query(Media.getContentUriForPath(internalPath), null, null,
null, null));
+ c.close();
}
public void testStoreAudioMediaInternal() {
diff --git a/tests/tests/provider/src/android/provider/cts/MediaStore_Audio_PlaylistsTest.java b/tests/tests/provider/src/android/provider/cts/MediaStore_Audio_PlaylistsTest.java
index 0d36212..03a81b8 100644
--- a/tests/tests/provider/src/android/provider/cts/MediaStore_Audio_PlaylistsTest.java
+++ b/tests/tests/provider/src/android/provider/cts/MediaStore_Audio_PlaylistsTest.java
@@ -39,15 +39,18 @@
}
public void testGetContentUri() {
- assertNotNull(mContentResolver.query(
+ Cursor c = null;
+ assertNotNull(c = mContentResolver.query(
Playlists.getContentUri(MediaStoreAudioTestHelper.EXTERNAL_VOLUME_NAME), null, null,
null, null));
+ c.close();
// can not accept any other volume names
try {
- assertNotNull(mContentResolver.query(
+ assertNotNull(c = mContentResolver.query(
Playlists.getContentUri(MediaStoreAudioTestHelper.INTERNAL_VOLUME_NAME), null,
null, null, null));
+ c.close();
fail("Should throw SQLException as the internal datatbase has no playlist");
} catch (SQLException e) {
// expected
diff --git a/tests/tests/provider/src/android/provider/cts/MediaStore_Audio_Playlists_MembersTest.java b/tests/tests/provider/src/android/provider/cts/MediaStore_Audio_Playlists_MembersTest.java
index aa524aa..bf97848 100644
--- a/tests/tests/provider/src/android/provider/cts/MediaStore_Audio_Playlists_MembersTest.java
+++ b/tests/tests/provider/src/android/provider/cts/MediaStore_Audio_Playlists_MembersTest.java
@@ -27,6 +27,9 @@
import android.provider.MediaStore.Audio.Playlists.Members;
import android.provider.cts.MediaStoreAudioTestHelper.Audio1;
import android.provider.cts.MediaStoreAudioTestHelper.Audio2;
+import android.provider.cts.MediaStoreAudioTestHelper.Audio3;
+import android.provider.cts.MediaStoreAudioTestHelper.Audio4;
+import android.provider.cts.MediaStoreAudioTestHelper.Audio5;
import android.provider.cts.MediaStoreAudioTestHelper.MockAudioMediaInfo;
import android.test.InstrumentationTestCase;
@@ -114,9 +117,9 @@
mContentResolver = getInstrumentation().getContext().getContentResolver();
mIdOfAudio1 = insertAudioItem(Audio1.getInstance());
mIdOfAudio2 = insertAudioItem(Audio2.getInstance());
- mIdOfAudio3 = insertAudioItem(Audio1.getInstance());
- mIdOfAudio4 = insertAudioItem(Audio1.getInstance());
- mIdOfAudio5 = insertAudioItem(Audio1.getInstance());
+ mIdOfAudio3 = insertAudioItem(Audio3.getInstance());
+ mIdOfAudio4 = insertAudioItem(Audio4.getInstance());
+ mIdOfAudio5 = insertAudioItem(Audio5.getInstance());
}
@Override
diff --git a/tests/tests/provider/src/android/provider/cts/MediaStore_FilesTest.java b/tests/tests/provider/src/android/provider/cts/MediaStore_FilesTest.java
index 1e8a4ac..79b2e57 100644
--- a/tests/tests/provider/src/android/provider/cts/MediaStore_FilesTest.java
+++ b/tests/tests/provider/src/android/provider/cts/MediaStore_FilesTest.java
@@ -84,6 +84,10 @@
assertEquals(1, mResolver.update(fileUri, values, null, null));
assertStringColumn(fileUri, MediaColumns.DATA, updatedPath);
+ // check that inserting a duplicate entry fails
+ Uri foo = mResolver.insert(allFilesUri, values);
+ assertNull(foo);
+
// Delete the file and observe that the file count decreased.
assertEquals(1, mResolver.delete(fileUri, null, null));
assertEquals(fileCount, getFileCount(allFilesUri));
diff --git a/tests/tests/provider/src/android/provider/cts/MediaStore_Images_MediaTest.java b/tests/tests/provider/src/android/provider/cts/MediaStore_Images_MediaTest.java
index fa269e4..740e31b 100644
--- a/tests/tests/provider/src/android/provider/cts/MediaStore_Images_MediaTest.java
+++ b/tests/tests/provider/src/android/provider/cts/MediaStore_Images_MediaTest.java
@@ -178,10 +178,13 @@
}
public void testGetContentUri() {
- assertNotNull(mContentResolver.query(Media.getContentUri("internal"), null, null, null,
+ Cursor c = null;
+ assertNotNull(c = mContentResolver.query(Media.getContentUri("internal"), null, null, null,
null));
- assertNotNull(mContentResolver.query(Media.getContentUri("external"), null, null, null,
+ c.close();
+ assertNotNull(c = mContentResolver.query(Media.getContentUri("external"), null, null, null,
null));
+ c.close();
// can not accept any other volume names
String volume = "fakeVolume";
@@ -294,6 +297,7 @@
} finally {
// delete
assertEquals(1, mContentResolver.delete(uri, null, null));
+ new File(externalPath).delete();
}
}
diff --git a/tests/tests/provider/src/android/provider/cts/MediaStore_Images_ThumbnailsTest.java b/tests/tests/provider/src/android/provider/cts/MediaStore_Images_ThumbnailsTest.java
index 8ecea7b..e8a13a9 100644
--- a/tests/tests/provider/src/android/provider/cts/MediaStore_Images_ThumbnailsTest.java
+++ b/tests/tests/provider/src/android/provider/cts/MediaStore_Images_ThumbnailsTest.java
@@ -211,10 +211,13 @@
}
public void testGetContentUri() {
- assertNotNull(mContentResolver.query(Thumbnails.getContentUri("internal"), null, null,
+ Cursor c = null;
+ assertNotNull(c = mContentResolver.query(Thumbnails.getContentUri("internal"), null, null,
null, null));
- assertNotNull(mContentResolver.query(Thumbnails.getContentUri("external"), null, null,
+ c.close();
+ assertNotNull(c = mContentResolver.query(Thumbnails.getContentUri("external"), null, null,
null, null));
+ c.close();
// can not accept any other volume names
String volume = "fakeVolume";
diff --git a/tests/tests/provider/src/android/provider/cts/MediaStore_Video_MediaTest.java b/tests/tests/provider/src/android/provider/cts/MediaStore_Video_MediaTest.java
index f4d01e3..c3eb0b8 100644
--- a/tests/tests/provider/src/android/provider/cts/MediaStore_Video_MediaTest.java
+++ b/tests/tests/provider/src/android/provider/cts/MediaStore_Video_MediaTest.java
@@ -45,10 +45,13 @@
}
public void testGetContentUri() {
- assertNotNull(mContentResolver.query(Media.getContentUri("internal"), null, null, null,
+ Cursor c = null;
+ assertNotNull(c = mContentResolver.query(Media.getContentUri("internal"), null, null, null,
null));
- assertNotNull(mContentResolver.query(Media.getContentUri("external"), null, null, null,
+ c.close();
+ assertNotNull(c = mContentResolver.query(Media.getContentUri("external"), null, null, null,
null));
+ c.close();
// can not accept any other volume names
String volume = "fakeVolume";
@@ -180,6 +183,7 @@
} finally {
// delete
assertEquals(1, mContentResolver.delete(uri, null, null));
+ new File(externalVideoPath).delete();
}
// check that the video file is removed when deleting the database entry
diff --git a/tests/tests/provider/src/android/provider/cts/MediaStore_Video_ThumbnailsTest.java b/tests/tests/provider/src/android/provider/cts/MediaStore_Video_ThumbnailsTest.java
index 2a4b46c..8f6f729 100644
--- a/tests/tests/provider/src/android/provider/cts/MediaStore_Video_ThumbnailsTest.java
+++ b/tests/tests/provider/src/android/provider/cts/MediaStore_Video_ThumbnailsTest.java
@@ -99,6 +99,8 @@
assertFalse("thumbnail file should no longer exist", new File(path).exists());
}
c.close();
+
+ assertEquals(1, mResolver.delete(videoUri, null, null));
}
private Uri insertVideo() throws IOException {
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/AllocationTest.java b/tests/tests/renderscript/src/android/renderscript/cts/AllocationTest.java
index feac159..4a1533c 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/AllocationTest.java
+++ b/tests/tests/renderscript/src/android/renderscript/cts/AllocationTest.java
@@ -28,6 +28,8 @@
import android.renderscript.Type.Builder;
import android.renderscript.Type.CubemapFace;
+import android.util.Log;
+
public class AllocationTest extends RSBaseGraphics {
// Test power of two and non power of two, equal and non-equal sizes
@@ -188,10 +190,10 @@
void helperCreateFromBitmap(Bitmap B,
Allocation.MipmapControl mc) {
- for (int i = 0; i < 1; i++) {
- for (int j = 0; j < 1; j++) {
- for (int k = 0; k < 1; k++) {
- for (int l = 0; l < 1; l++) {
+ for (int i = 0; i <= 1; i++) {
+ for (int j = 0; j <= 1; j++) {
+ for (int k = 0; k <= 1; k++) {
+ for (int l = 0; l <= 1; l++) {
int u = 0;
u |= (i * Allocation.USAGE_SCRIPT);
u |= (j * Allocation.USAGE_GRAPHICS_TEXTURE);
@@ -684,6 +686,101 @@
}
}
}
+
+ public void testCopyFromAllocation() {
+ int nElemsX = 256;
+ int nElemsY = 32;
+ Type.Builder b = new Type.Builder(mRS, Element.I8(mRS));
+ Type.Builder b2 = new Type.Builder(mRS, Element.I32(mRS));
+ Allocation srcA = Allocation.createTyped(mRS, b.setX(nElemsX).setY(nElemsY).create());
+ Allocation dstA = Allocation.createTyped(mRS, b.setX(nElemsX).setY(nElemsY).create());
+
+ // wrong dimensionality
+ Allocation srcB_bad = Allocation.createTyped(mRS, b.setX(nElemsX).setY(1).create());
+ Allocation srcC_bad = Allocation.createTyped(mRS, b.setX(nElemsY).setY(nElemsX).create());
+ Allocation srcD_bad = Allocation.createTyped(mRS, b.setX(nElemsX*2).setY(nElemsY*2).create());
+
+ // wrong element type
+ Allocation srcE_bad = Allocation.createTyped(mRS, b2.setX(nElemsX).setY(nElemsY).create());
+
+ try {
+ dstA.copyFrom(srcB_bad);
+ fail("should throw RSIllegalArgumentException");
+ } catch (RSIllegalArgumentException e) {
+ }
+
+ try {
+ dstA.copyFrom(srcC_bad);
+ fail("should throw RSIllegalArgumentException");
+ } catch (RSIllegalArgumentException e) {
+ }
+
+ try {
+ dstA.copyFrom(srcD_bad);
+ fail("should throw RSIllegalArgumentException");
+ } catch (RSIllegalArgumentException e) {
+ }
+
+ try {
+ dstA.copyFrom(srcE_bad);
+ fail("should throw RSIllegalArgumentException");
+ } catch (RSIllegalArgumentException e) {
+ }
+
+ dstA.copyFrom(srcA);
+
+ }
+
+ public void testSetElementAt() {
+ Type.Builder b = new Type.Builder(mRS, Element.I32(mRS));
+ Allocation largeArray = Allocation.createTyped(mRS, b.setX(48).create());
+ Allocation singleElement = Allocation.createTyped(mRS, b.setX(1).create());
+
+ ScriptC_setelementat script = new ScriptC_setelementat(mRS, mRes, R.raw.setelementat);
+
+ script.set_memset_toValue(1);
+ script.forEach_memset(singleElement);
+
+ script.set_dimX(48);
+ script.set_array(largeArray);
+
+ script.forEach_setLargeArray(singleElement);
+
+ int[] result = new int[1];
+
+ script.set_compare_value(10);
+ script.forEach_compare(largeArray);
+ script.forEach_getCompareResult(singleElement);
+ singleElement.copyTo(result);
+ assertTrue(result[0] == 2);
+ }
+
+ public void testSetElementAt2D() {
+ Type.Builder b = new Type.Builder(mRS, Element.I32(mRS));
+
+ Allocation singleElement = Allocation.createTyped(mRS, b.setX(1).create());
+ Allocation largeArray = Allocation.createTyped(mRS, b.setX(48).setY(16).create());
+
+ ScriptC_setelementat script = new ScriptC_setelementat(mRS, mRes, R.raw.setelementat);
+
+ script.set_memset_toValue(1);
+ script.forEach_memset(singleElement);
+
+ script.set_dimX(48);
+ script.set_dimY(16);
+ script.set_array(largeArray);
+
+ script.forEach_setLargeArray2D(singleElement);
+
+ int[] result = new int[1];
+
+ script.set_compare_value(10);
+ script.forEach_compare(largeArray);
+ script.forEach_getCompareResult(singleElement);
+ singleElement.copyTo(result);
+ assertTrue(result[0] == 2);
+ }
+
}
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/ElementTest.java b/tests/tests/renderscript/src/android/renderscript/cts/ElementTest.java
index 2ad45b1..ead4528 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/ElementTest.java
+++ b/tests/tests/renderscript/src/android/renderscript/cts/ElementTest.java
@@ -487,11 +487,12 @@
assertEquals(DataKind.PIXEL_RGB, DataKind.valueOf("PIXEL_RGB"));
assertEquals(DataKind.PIXEL_RGBA, DataKind.valueOf("PIXEL_RGBA"));
assertEquals(DataKind.PIXEL_DEPTH, DataKind.valueOf("PIXEL_DEPTH"));
+ assertEquals(DataKind.PIXEL_YUV, DataKind.valueOf("PIXEL_YUV"));
// Make sure no new enums are added
- assertEquals(7, DataKind.values().length);
+ assertEquals(8, DataKind.values().length);
for (DataKind dk : DataKind.values()) {
- if (dk != DataKind.USER) {
+ if (dk != DataKind.USER && dk != DataKind.PIXEL_YUV) {
Element.createPixel(mRS, DataType.UNSIGNED_8, dk);
}
}
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/ImageProcessingTest.java b/tests/tests/renderscript/src/android/renderscript/cts/ImageProcessingTest.java
new file mode 100644
index 0000000..fae20f4
--- /dev/null
+++ b/tests/tests/renderscript/src/android/renderscript/cts/ImageProcessingTest.java
@@ -0,0 +1,338 @@
+/*
+ * Copyright (C) 2012 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.renderscript.cts;
+
+import android.renderscript.Allocation;
+
+import android.renderscript.Byte2;
+import android.renderscript.Byte3;
+import android.renderscript.Byte4;
+
+import android.renderscript.Double2;
+import android.renderscript.Double3;
+import android.renderscript.Double4;
+
+import android.renderscript.Element;
+
+import android.renderscript.Float2;
+import android.renderscript.Float3;
+import android.renderscript.Float4;
+
+import android.renderscript.Int2;
+import android.renderscript.Int3;
+import android.renderscript.Int4;
+
+import android.renderscript.Long2;
+import android.renderscript.Long3;
+import android.renderscript.Long4;
+
+import android.renderscript.RSRuntimeException;
+
+import android.renderscript.Short2;
+import android.renderscript.Short3;
+import android.renderscript.Short4;
+
+import android.renderscript.Matrix4f;
+
+import android.renderscript.Type;
+
+import android.renderscript.ScriptGroup;
+
+import android.renderscript.ScriptIntrinsicBlend;
+import android.renderscript.ScriptIntrinsicBlur;
+import android.renderscript.ScriptIntrinsicColorMatrix;
+import android.renderscript.ScriptIntrinsicConvolve3x3;
+import android.renderscript.ScriptIntrinsicConvolve5x5;
+import android.renderscript.ScriptIntrinsicLUT;
+
+import com.android.cts.stub.R;
+
+public class ImageProcessingTest extends RSBaseCompute {
+ private Allocation a1, a2;
+
+ private final int MAX_RADIUS = 25;
+ private final int dimX = 256;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+
+ Type t = new Type.Builder(mRS, Element.U8_4(mRS)).setX(dimX).setY(dimX).create();
+ a1 = Allocation.createTyped(mRS, t);
+ a2 = Allocation.createTyped(mRS, t);
+ }
+
+ public void testBlur() {
+ ScriptIntrinsicBlur mBlur;
+ mBlur = ScriptIntrinsicBlur.create(mRS, Element.U8_4(mRS));
+
+ Allocation a1_copy, a2_copy;
+ a1_copy = Allocation.createTyped(mRS, a1.getType());
+ a2_copy = Allocation.createTyped(mRS, a2.getType());
+
+ for (int i = 1; i < MAX_RADIUS; i++) {
+
+ a1_copy.copy2DRangeFrom(0, 0, a1.getType().getX(), a1.getType().getY(), a1, 0, 0);
+
+ mBlur.setRadius(i);
+ mBlur.setInput(a1_copy);
+
+ mBlur.forEach(a2_copy);
+
+ // validate
+
+ }
+
+ }
+
+ public void testBlend() {
+ ScriptIntrinsicBlend mBlend;
+ mBlend = ScriptIntrinsicBlend.create(mRS, Element.U8_4(mRS));
+
+ Allocation a1_copy, a2_copy;
+ a1_copy = Allocation.createTyped(mRS, a1.getType());
+ a2_copy = Allocation.createTyped(mRS, a2.getType());
+
+ for (int i = 0; i < 14; i++) {
+
+ a1_copy.copy2DRangeFrom(0, 0, a1.getType().getX(), a1.getType().getY(), a1, 0, 0);
+ a2_copy.copy2DRangeFrom(0, 0, a2.getType().getX(), a2.getType().getY(), a2, 0, 0);
+
+ switch (i) {
+ case 0:
+ mBlend.forEachSrc(a1_copy, a2_copy);
+ break;
+ case 1:
+ mBlend.forEachDst(a1_copy, a2_copy);
+ break;
+ case 2:
+ mBlend.forEachSrcOver(a1_copy, a2_copy);
+ break;
+ case 3:
+ mBlend.forEachDstOver(a1_copy, a2_copy);
+ break;
+ case 4:
+ mBlend.forEachSrcIn(a1_copy, a2_copy);
+ break;
+ case 5:
+ mBlend.forEachDstIn(a1_copy, a2_copy);
+ break;
+ case 6:
+ mBlend.forEachSrcOut(a1_copy, a2_copy);
+ break;
+ case 7:
+ mBlend.forEachDstOut(a1_copy, a2_copy);
+ break;
+ case 8:
+ mBlend.forEachSrcAtop(a1_copy, a2_copy);
+ break;
+ case 9:
+ mBlend.forEachDstAtop(a1_copy, a2_copy);
+ break;
+ case 10:
+ mBlend.forEachXor(a1_copy, a2_copy);
+ break;
+ case 11:
+ mBlend.forEachAdd(a1_copy, a2_copy);
+ break;
+ case 12:
+ mBlend.forEachSubtract(a1_copy, a2_copy);
+ break;
+ case 13:
+ mBlend.forEachMultiply(a1_copy, a2_copy);
+ break;
+ }
+
+ // validate
+
+ }
+
+ }
+
+ public void testColorMatrix() {
+ ScriptIntrinsicColorMatrix mColorMatrix;
+ mColorMatrix = ScriptIntrinsicColorMatrix.create(mRS, Element.U8_4(mRS));
+
+ Allocation a1_copy, a2_copy;
+ a1_copy = Allocation.createTyped(mRS, a1.getType());
+ a2_copy = Allocation.createTyped(mRS, a2.getType());
+
+ Matrix4f m = new Matrix4f();
+ m.set(1, 0, 0.2f);
+ m.set(1, 1, 0.9f);
+ m.set(1, 2, 0.2f);
+
+ //test greyscale
+ mColorMatrix.setGreyscale();
+
+ a1_copy.copy2DRangeFrom(0, 0, a1.getType().getX(), a1.getType().getY(), a1, 0, 0);
+ a2_copy.copy2DRangeFrom(0, 0, a2.getType().getX(), a2.getType().getY(), a2, 0, 0);
+
+ mColorMatrix.forEach(a1_copy, a2_copy);
+
+ //validate greyscale
+
+
+ //test color matrix
+ mColorMatrix.setColorMatrix(m);
+
+ a1_copy.copy2DRangeFrom(0, 0, a1.getType().getX(), a1.getType().getY(), a1, 0, 0);
+ a2_copy.copy2DRangeFrom(0, 0, a2.getType().getX(), a2.getType().getY(), a2, 0, 0);
+
+ mColorMatrix.forEach(a1_copy, a2_copy);
+
+ //validate color matrix
+
+
+ }
+
+
+ public void testConvolve3x3() {
+ ScriptIntrinsicConvolve3x3 mConvolve3x3;
+ mConvolve3x3 = ScriptIntrinsicConvolve3x3.create(mRS, Element.U8_4(mRS));
+
+ Allocation a1_copy, a2_copy;
+ a1_copy = Allocation.createTyped(mRS, a1.getType());
+ a2_copy = Allocation.createTyped(mRS, a2.getType());
+
+ a1_copy.copy2DRangeFrom(0, 0, a1.getType().getX(), a1.getType().getY(), a1, 0, 0);
+
+ float f[] = new float[9];
+ f[0] = 0.f; f[1] = -1.f; f[2] = 0.f;
+ f[3] = -1.f; f[4] = 5.f; f[5] = -1.f;
+ f[6] = 0.f; f[7] = -1.f; f[8] = 0.f;
+
+ mConvolve3x3.setCoefficients(f);
+ mConvolve3x3.setInput(a1_copy);
+ mConvolve3x3.forEach(a2_copy);
+
+ // validate
+
+ }
+
+ public void testConvolve5x5() {
+ ScriptIntrinsicConvolve5x5 mConvolve5x5;
+ mConvolve5x5 = ScriptIntrinsicConvolve5x5.create(mRS, Element.U8_4(mRS));
+
+ Allocation a1_copy, a2_copy;
+ a1_copy = Allocation.createTyped(mRS, a1.getType());
+ a2_copy = Allocation.createTyped(mRS, a2.getType());
+
+ a1_copy.copy2DRangeFrom(0, 0, a1.getType().getX(), a1.getType().getY(), a1, 0, 0);
+
+ float f[] = new float[25];
+ f[0] = -1.f; f[1] = -3.f; f[2] = -4.f; f[3] = -3.f; f[4] = -1.f;
+ f[5] = -3.f; f[6] = 0.f; f[7] = 6.f; f[8] = 0.f; f[9] = -3.f;
+ f[10]= -4.f; f[11]= 6.f; f[12]= 20.f; f[13]= 6.f; f[14]= -4.f;
+ f[15]= -3.f; f[16]= 0.f; f[17]= 6.f; f[18]= 0.f; f[19]= -3.f;
+ f[20]= -1.f; f[21]= -3.f; f[22]= -4.f; f[23]= -3.f; f[24]= -1.f;
+
+ mConvolve5x5.setCoefficients(f);
+ mConvolve5x5.setInput(a1_copy);
+ mConvolve5x5.forEach(a2_copy);
+
+ // validate
+
+ }
+
+ public void testLUT() {
+ ScriptIntrinsicLUT mLUT;
+ mLUT = ScriptIntrinsicLUT.create(mRS, Element.U8_4(mRS));
+
+ Allocation a1_copy, a2_copy;
+ a1_copy = Allocation.createTyped(mRS, a1.getType());
+ a2_copy = Allocation.createTyped(mRS, a2.getType());
+
+ a1_copy.copy2DRangeFrom(0, 0, a1.getType().getX(), a1.getType().getY(), a1, 0, 0);
+
+ for (int ct=0; ct < 256; ct++) {
+ float f = ((float)ct) / 255.f;
+
+ float r = f;
+ if (r < 0.5f) {
+ r = 4.0f * r * r * r;
+ } else {
+ r = 1.0f - r;
+ r = 1.0f - (4.0f * r * r * r);
+ }
+ mLUT.setRed(ct, (int)(r * 255.f + 0.5f));
+
+ float g = f;
+ if (g < 0.5f) {
+ g = 2.0f * g * g;
+ } else {
+ g = 1.0f - g;
+ g = 1.0f - (2.0f * g * g);
+ }
+ mLUT.setGreen(ct, (int)(g * 255.f + 0.5f));
+
+ float b = f * 0.5f + 0.25f;
+ mLUT.setBlue(ct, (int)(b * 255.f + 0.5f));
+ }
+
+ mLUT.forEach(a1_copy, a2_copy);
+
+ // validate
+
+ }
+
+ public void testScriptGroup() {
+ ScriptGroup group;
+
+ ScriptIntrinsicConvolve3x3 mConvolve3x3;
+ ScriptIntrinsicColorMatrix mColorMatrix;
+
+ mConvolve3x3 = ScriptIntrinsicConvolve3x3.create(mRS, Element.U8_4(mRS));
+ mColorMatrix = ScriptIntrinsicColorMatrix.create(mRS, Element.U8_4(mRS));
+
+ Allocation a1_copy, a2_copy;
+ a1_copy = Allocation.createTyped(mRS, a1.getType());
+ a2_copy = Allocation.createTyped(mRS, a2.getType());
+
+ a1_copy.copy2DRangeFrom(0, 0, a1.getType().getX(), a1.getType().getY(), a1, 0, 0);
+
+ float f[] = new float[9];
+ f[0] = 0.f; f[1] = -1.f; f[2] = 0.f;
+ f[3] = -1.f; f[4] = 5.f; f[5] = -1.f;
+ f[6] = 0.f; f[7] = -1.f; f[8] = 0.f;
+
+ mConvolve3x3.setCoefficients(f);
+
+ Matrix4f m = new Matrix4f();
+ m.set(1, 0, 0.2f);
+ m.set(1, 1, 0.9f);
+ m.set(1, 2, 0.2f);
+ mColorMatrix.setColorMatrix(m);
+
+ Type connect = new Type.Builder(mRS, Element.U8_4(mRS)).setX(dimX).setY(dimX).create();
+
+ ScriptGroup.Builder b = new ScriptGroup.Builder(mRS);
+ b.addKernel(mConvolve3x3.getKernelID());
+ b.addKernel(mColorMatrix.getKernelID());
+ b.addConnection(connect, mConvolve3x3.getKernelID(), mColorMatrix.getKernelID());
+ group = b.create();
+
+ mConvolve3x3.setInput(a1_copy);
+ group.setOutput(mColorMatrix.getKernelID(), a2_copy);
+ group.execute();
+
+ // validate
+
+ }
+
+
+}
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/ScriptC_set_target_api_11.java b/tests/tests/renderscript/src/android/renderscript/cts/ScriptC_set_target_api_11.java
index 6ad1748..a16059a 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/ScriptC_set_target_api_11.java
+++ b/tests/tests/renderscript/src/android/renderscript/cts/ScriptC_set_target_api_11.java
@@ -27,7 +27,16 @@
* @hide
*/
public class ScriptC_set_target_api_11 extends ScriptC {
+ private static final String __rs_resource_name = "set_target_api_11";
// Constructor
+ public ScriptC_set_target_api_11(RenderScript rs) {
+ this(rs,
+ rs.getApplicationContext().getResources(),
+ rs.getApplicationContext().getResources().getIdentifier(
+ __rs_resource_name, "raw",
+ rs.getApplicationContext().getPackageName()));
+ }
+
public ScriptC_set_target_api_11(RenderScript rs, Resources resources, int id) {
super(rs, resources, id);
}
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/ScriptC_set_target_api_12.java b/tests/tests/renderscript/src/android/renderscript/cts/ScriptC_set_target_api_12.java
index dc772d5..5c31f94 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/ScriptC_set_target_api_12.java
+++ b/tests/tests/renderscript/src/android/renderscript/cts/ScriptC_set_target_api_12.java
@@ -27,7 +27,16 @@
* @hide
*/
public class ScriptC_set_target_api_12 extends ScriptC {
+ private static final String __rs_resource_name = "set_target_api_12";
// Constructor
+ public ScriptC_set_target_api_12(RenderScript rs) {
+ this(rs,
+ rs.getApplicationContext().getResources(),
+ rs.getApplicationContext().getResources().getIdentifier(
+ __rs_resource_name, "raw",
+ rs.getApplicationContext().getPackageName()));
+ }
+
public ScriptC_set_target_api_12(RenderScript rs, Resources resources, int id) {
super(rs, resources, id);
}
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/ScriptC_set_target_api_13.java b/tests/tests/renderscript/src/android/renderscript/cts/ScriptC_set_target_api_13.java
index e1580e4..e12bf00 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/ScriptC_set_target_api_13.java
+++ b/tests/tests/renderscript/src/android/renderscript/cts/ScriptC_set_target_api_13.java
@@ -27,7 +27,16 @@
* @hide
*/
public class ScriptC_set_target_api_13 extends ScriptC {
+ private static final String __rs_resource_name = "set_target_api_13";
// Constructor
+ public ScriptC_set_target_api_13(RenderScript rs) {
+ this(rs,
+ rs.getApplicationContext().getResources(),
+ rs.getApplicationContext().getResources().getIdentifier(
+ __rs_resource_name, "raw",
+ rs.getApplicationContext().getPackageName()));
+ }
+
public ScriptC_set_target_api_13(RenderScript rs, Resources resources, int id) {
super(rs, resources, id);
}
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/ScriptC_set_target_api_14.java b/tests/tests/renderscript/src/android/renderscript/cts/ScriptC_set_target_api_14.java
index 42d08c6..97879a5 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/ScriptC_set_target_api_14.java
+++ b/tests/tests/renderscript/src/android/renderscript/cts/ScriptC_set_target_api_14.java
@@ -27,7 +27,16 @@
* @hide
*/
public class ScriptC_set_target_api_14 extends ScriptC {
+ private static final String __rs_resource_name = "set_target_api_14";
// Constructor
+ public ScriptC_set_target_api_14(RenderScript rs) {
+ this(rs,
+ rs.getApplicationContext().getResources(),
+ rs.getApplicationContext().getResources().getIdentifier(
+ __rs_resource_name, "raw",
+ rs.getApplicationContext().getPackageName()));
+ }
+
public ScriptC_set_target_api_14(RenderScript rs, Resources resources, int id) {
super(rs, resources, id);
}
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/ScriptC_set_target_api_15.java b/tests/tests/renderscript/src/android/renderscript/cts/ScriptC_set_target_api_15.java
index 6acfede..636a128 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/ScriptC_set_target_api_15.java
+++ b/tests/tests/renderscript/src/android/renderscript/cts/ScriptC_set_target_api_15.java
@@ -27,7 +27,16 @@
* @hide
*/
public class ScriptC_set_target_api_15 extends ScriptC {
+ private static final String __rs_resource_name = "set_target_api_15";
// Constructor
+ public ScriptC_set_target_api_15(RenderScript rs) {
+ this(rs,
+ rs.getApplicationContext().getResources(),
+ rs.getApplicationContext().getResources().getIdentifier(
+ __rs_resource_name, "raw",
+ rs.getApplicationContext().getPackageName()));
+ }
+
public ScriptC_set_target_api_15(RenderScript rs, Resources resources, int id) {
super(rs, resources, id);
}
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/ScriptC_set_target_api_16.java b/tests/tests/renderscript/src/android/renderscript/cts/ScriptC_set_target_api_16.java
index f111005..435316f 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/ScriptC_set_target_api_16.java
+++ b/tests/tests/renderscript/src/android/renderscript/cts/ScriptC_set_target_api_16.java
@@ -27,7 +27,16 @@
* @hide
*/
public class ScriptC_set_target_api_16 extends ScriptC {
+ private static final String __rs_resource_name = "set_target_api_16";
// Constructor
+ public ScriptC_set_target_api_16(RenderScript rs) {
+ this(rs,
+ rs.getApplicationContext().getResources(),
+ rs.getApplicationContext().getResources().getIdentifier(
+ __rs_resource_name, "raw",
+ rs.getApplicationContext().getPackageName()));
+ }
+
public ScriptC_set_target_api_16(RenderScript rs, Resources resources, int id) {
super(rs, resources, id);
}
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/ScriptC_set_target_api_17.java b/tests/tests/renderscript/src/android/renderscript/cts/ScriptC_set_target_api_17.java
new file mode 100644
index 0000000..c706ca6
--- /dev/null
+++ b/tests/tests/renderscript/src/android/renderscript/cts/ScriptC_set_target_api_17.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2011-2012 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.
+ */
+
+/*
+ * This file is auto-generated. DO NOT MODIFY!
+ * The source Renderscript file: set_target_api_17.rs
+ */
+package android.renderscript.cts;
+
+import android.renderscript.*;
+import android.content.res.Resources;
+
+/**
+ * @hide
+ */
+public class ScriptC_set_target_api_17 extends ScriptC {
+ private static final String __rs_resource_name = "set_target_api_17";
+ // Constructor
+ public ScriptC_set_target_api_17(RenderScript rs) {
+ this(rs,
+ rs.getApplicationContext().getResources(),
+ rs.getApplicationContext().getResources().getIdentifier(
+ __rs_resource_name, "raw",
+ rs.getApplicationContext().getPackageName()));
+ }
+
+ public ScriptC_set_target_api_17(RenderScript rs, Resources resources, int id) {
+ super(rs, resources, id);
+ }
+
+ private final static int mExportFuncIdx_check = 0;
+ public void invoke_check(int version) {
+ FieldPacker check_fp = new FieldPacker(4);
+ check_fp.addI32(version);
+ invoke(mExportFuncIdx_check, check_fp);
+ }
+
+}
+
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/ScriptC_set_target_api_too_high.java b/tests/tests/renderscript/src/android/renderscript/cts/ScriptC_set_target_api_too_high.java
new file mode 100644
index 0000000..f54ab46
--- /dev/null
+++ b/tests/tests/renderscript/src/android/renderscript/cts/ScriptC_set_target_api_too_high.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2011-2012 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.
+ */
+
+/*
+ * This file is auto-generated. DO NOT MODIFY!
+ * The source Renderscript file: set_target_api_16.rs
+ */
+package android.renderscript.cts;
+
+import android.renderscript.*;
+import android.content.res.Resources;
+
+/**
+ * @hide
+ */
+public class ScriptC_set_target_api_too_high extends ScriptC {
+ private static final String __rs_resource_name = "set_target_api_too_high";
+ // Constructor
+ public ScriptC_set_target_api_too_high(RenderScript rs) {
+ this(rs,
+ rs.getApplicationContext().getResources(),
+ rs.getApplicationContext().getResources().getIdentifier(
+ __rs_resource_name, "raw",
+ rs.getApplicationContext().getPackageName()));
+ }
+
+ public ScriptC_set_target_api_too_high(RenderScript rs, Resources resources, int id) {
+ super(rs, resources, id);
+ }
+
+ private final static int mExportFuncIdx_check = 0;
+ public void invoke_check(int version) {
+ FieldPacker check_fp = new FieldPacker(4);
+ check_fp.addI32(version);
+ invoke(mExportFuncIdx_check, check_fp);
+ }
+
+}
+
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/ScriptGroupTest.java b/tests/tests/renderscript/src/android/renderscript/cts/ScriptGroupTest.java
index e73195c..64496ef 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/ScriptGroupTest.java
+++ b/tests/tests/renderscript/src/android/renderscript/cts/ScriptGroupTest.java
@@ -262,7 +262,6 @@
compare.forEach_compare(out);
compare.forEach_getCompareResult(resultAlloc);
resultAlloc.copyTo(result);
- Log.e("ARGH", "result = " + result[0]);
assertTrue(result[0] == 2);
}
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/VersionTest.java b/tests/tests/renderscript/src/android/renderscript/cts/VersionTest.java
index 6c9e7e9..c3966a9 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/VersionTest.java
+++ b/tests/tests/renderscript/src/android/renderscript/cts/VersionTest.java
@@ -17,6 +17,7 @@
package android.renderscript.cts;
import android.renderscript.RenderScript.RSMessageHandler;
+import android.renderscript.RSRuntimeException;
import com.android.cts.stub.R;
/**
@@ -82,4 +83,26 @@
waitForMessage();
checkForErrors();
}
+
+ public void testVersion17() {
+ ScriptC_set_target_api_17 test17 =
+ new ScriptC_set_target_api_17(mRS,
+ mRes,
+ R.raw.set_target_api_17);
+ test17.invoke_check(17);
+ waitForMessage();
+ checkForErrors();
+ }
+
+ public void testVersion_too_high() {
+ try {
+ ScriptC_set_target_api_too_high test_too_high =
+ new ScriptC_set_target_api_too_high(mRS,
+ mRes,
+ R.raw.set_target_api_too_high);
+ fail("should throw RSRuntimeException");
+ } catch (RSRuntimeException e) {
+ }
+ checkForErrors();
+ }
}
diff --git a/tests/tests/security/src/android/security/cts/BannedFilesTest.java b/tests/tests/security/src/android/security/cts/BannedFilesTest.java
index ada9aaf..6ce8588 100644
--- a/tests/tests/security/src/android/security/cts/BannedFilesTest.java
+++ b/tests/tests/security/src/android/security/cts/BannedFilesTest.java
@@ -68,7 +68,7 @@
if (!FileUtils.getFileStatus(file, fs, false)) {
return;
}
- assertTrue((fs.mode & FileUtils.S_ISUID) == 0);
- assertTrue((fs.mode & FileUtils.S_ISGID) == 0);
+ assertTrue("File \"" + file + "\" is setUID", (fs.mode & FileUtils.S_ISUID) == 0);
+ assertTrue("File \"" + file + "\" is setGID", (fs.mode & FileUtils.S_ISGID) == 0);
}
}
diff --git a/tests/tests/security/src/android/security/cts/CertificateData.java b/tests/tests/security/src/android/security/cts/CertificateData.java
index 1714461..1b039a5 100644
--- a/tests/tests/security/src/android/security/cts/CertificateData.java
+++ b/tests/tests/security/src/android/security/cts/CertificateData.java
@@ -40,6 +40,7 @@
"40:54:DA:6F:1C:3F:40:74:AC:ED:0F:EC:CD:DB:79:D1:53:FB:90:1D",
"43:F9:B1:10:D5:BA:FD:48:22:52:31:B0:D0:08:2B:37:2F:EF:9A:54",
"F4:8B:11:BF:DE:AB:BE:94:54:20:71:E6:41:DE:6B:BE:88:2B:40:B9",
+ "58:E8:AB:B0:36:15:33:FB:80:F7:9B:1B:6D:29:D3:FF:8D:5F:00:F0",
"96:56:CD:7B:57:96:98:95:D0:E1:41:46:68:06:FB:B8:C6:11:06:87",
"55:A6:72:3E:CB:F2:EC:CD:C3:23:74:70:19:9D:2A:BE:11:E3:81:D1",
"D6:9B:56:11:48:F0:1C:77:C5:45:78:C1:09:26:DF:5B:85:69:76:AD",
@@ -48,6 +49,7 @@
"AD:7E:1C:28:B0:64:EF:8F:60:03:40:20:14:C3:D0:E3:37:0E:B5:8A",
"8D:17:84:D5:37:F3:03:7D:EC:70:FE:57:8B:51:9A:99:E6:10:D7:B0",
"AE:50:83:ED:7C:F4:5C:BC:8F:61:C6:21:FE:68:5D:79:42:21:15:6E",
+ "DA:FA:F7:FA:66:84:EC:06:8F:14:50:BD:C7:C2:81:A5:BC:A9:64:57",
"5F:4E:1F:CF:31:B7:91:3B:85:0B:54:F6:E5:FF:50:1A:2B:6F:C6:CF",
"74:F8:A3:C3:EF:E7:B3:90:06:4B:83:90:3C:21:64:60:20:E5:DF:CE",
"85:B5:FF:67:9B:0C:79:96:1F:C8:6E:44:22:00:46:13:DB:17:92:84",
@@ -64,6 +66,7 @@
"8C:F4:27:FD:79:0C:3A:D1:66:06:8D:E8:1E:57:EF:BB:93:22:72:D4",
"56:E0:FA:C0:3B:8F:18:23:55:18:E5:D3:11:CA:E8:C2:43:31:AB:66",
"02:72:68:29:3E:5F:5D:17:AA:A4:B3:C3:E6:36:1E:1F:92:57:5E:AA",
+ "2F:78:3D:25:52:18:A7:4A:65:39:71:B5:2C:A2:9C:45:15:6F:E9:19",
"97:81:79:50:D8:1C:96:70:CC:34:D8:09:CF:79:44:31:36:7E:F4:74",
"85:A4:08:C0:9C:19:3E:5D:51:58:7D:CD:D6:13:30:FD:8C:DE:37:BF",
"58:11:9F:0E:12:82:87:EA:50:FD:D9:87:45:6F:4F:78:DC:FA:D6:D4",
@@ -98,6 +101,7 @@
"69:BD:8C:F4:9C:D3:00:FB:59:2E:17:93:CA:55:6A:F3:EC:AA:35:FB",
"13:2D:0D:45:53:4B:69:97:CD:B2:D5:C3:39:E2:55:76:60:9B:5C:C6",
"5F:B7:EE:06:33:E2:59:DB:AD:0C:4C:9A:E6:D3:8F:1A:61:C7:DC:25",
+ "49:0A:75:74:DE:87:0A:47:FE:58:EE:F6:C7:6B:EB:C6:0B:12:40:99",
"25:01:90:19:CF:FB:D9:99:1C:B7:68:25:74:8D:94:5F:30:93:95:42",
"79:98:A3:08:E1:4D:65:85:E6:C2:1E:15:3A:71:9F:BA:5A:D3:4A:D9",
"B5:1C:06:7C:EE:2B:0C:3D:F8:55:AB:2D:92:F4:FE:39:D4:E7:0F:0E",
@@ -142,6 +146,7 @@
"21:FC:BD:8E:7F:6C:AF:05:1B:D1:B3:43:EC:A8:E7:61:47:F2:0F:8A",
"5F:3B:8C:F2:F8:10:B3:7D:78:B4:CE:EC:19:19:C3:73:34:B9:C7:74",
"2A:C8:D5:8B:57:CE:BF:2F:49:AF:F2:FC:76:8F:51:14:62:90:7A:41",
+ "96:C9:1B:0B:95:B4:10:98:42:FA:D0:D8:22:79:FE:60:FA:B9:16:83",
"CA:BB:51:67:24:00:58:8E:64:19:F1:D4:08:78:D0:40:3A:A2:02:64",
"5D:98:9C:DB:15:96:11:36:51:65:64:1B:56:0F:DB:EA:2A:C2:3E:F1",
"D8:A6:33:2C:E0:03:6F:B1:85:F6:63:4F:7D:6A:06:65:26:32:28:27",
diff --git a/tests/tests/security/src/android/security/cts/KernelSettingsTest.java b/tests/tests/security/src/android/security/cts/KernelSettingsTest.java
index cc4b6a6..6daaffe 100644
--- a/tests/tests/security/src/android/security/cts/KernelSettingsTest.java
+++ b/tests/tests/security/src/android/security/cts/KernelSettingsTest.java
@@ -19,6 +19,7 @@
import junit.framework.TestCase;
import java.io.BufferedReader;
+import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
@@ -86,6 +87,40 @@
}
}
+ /**
+ * Assert that support for loadable modules is not compiled into the
+ * kernel.
+ *
+ * Loadable modules are often used to implement rootkit like functionality.
+ * In addition, loadable modules enable support for /proc/sys/kernel/modprobe,
+ * which is commonly used by exploit writers to gain root access.
+ *
+ * Support for loadable modules can be removed by editing the Linux kernel
+ * config and removing the CONFIG_KMOD option.
+ */
+ public void testNoLoadableModules() throws IOException {
+ assertFalse(
+ "Support for loadable modules is compiled into the kernel. "
+ + "Loadable modules are often used by rootkits and other "
+ + "exploits and should be disabled. Please remove "
+ + "CONFIG_KMOD from your kernel config and compile "
+ + "all modules directly into the kernel.",
+ new File("/proc/sys/kernel/modprobe").exists());
+ }
+
+ /**
+ * Assert that the kernel config file is not compiled into the kernel.
+ *
+ * Compiling the config file into the kernel leaks the kernel base address
+ * via CONFIG_PHYS_OFFSET. It also wastes a small amount of valuable kernel memory.
+ */
+ public void testNoConfigGz() throws IOException {
+ assertFalse(
+ "/proc/config.gz is readable. Please recompile your "
+ + "kernel with CONFIG_IKCONFIG_PROC disabled",
+ new File("/proc/config.gz").exists());
+ }
+
private String getFile(String filename) throws IOException {
BufferedReader in = null;
try {
diff --git a/tests/tests/text/src/android/text/method/cts/DigitsKeyListenerTest.java b/tests/tests/text/src/android/text/method/cts/DigitsKeyListenerTest.java
index 0a1b5ae..ea50294 100644
--- a/tests/tests/text/src/android/text/method/cts/DigitsKeyListenerTest.java
+++ b/tests/tests/text/src/android/text/method/cts/DigitsKeyListenerTest.java
@@ -64,8 +64,9 @@
* 1. filter "123456", return null.
* 2. filter "a1b2c3d", return "123"
* 3. filter "-a1.b2c3d", return "123"
- * 4. filter Spanned("-a1.b2c3d"), return Spanned("123") and copy spans.
- * 5. filter "", return null
+ * 4. filter "+a1.b2c3d", return "123"
+ * 5. filter Spanned("-a1.b2c3d"), return Spanned("123") and copy spans.
+ * 6. filter "", return null
*/
public void testFilter1() {
String source = "123456";
@@ -87,6 +88,11 @@
dest, 0, dest.length())).toString());
assertEquals(destString, dest.toString());
+ source = "+a1.b2c3d";
+ assertEquals("123", (digitsKeyListener.filter(source, 0, source.length(),
+ dest, 0, dest.length())).toString());
+ assertEquals(destString, dest.toString());
+
Object what = new Object();
Spannable spannableSource = new SpannableString(source);
spannableSource.setSpan(what, 0, spannableSource.length(), Spanned.SPAN_POINT_POINT);
@@ -103,15 +109,21 @@
/**
* Check point:
- * Current accepted characters are '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-'.
+ * Current accepted characters are '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '+'.
* 1. filter "-123456", return null
- * 2. filter "-a1.b2c3d", return "-123"
- * 3. filter "-a1-b2c3d", return "-123"
- * 4. filter "5-a1-b2c3d", return "5123"
- * 5. filter Spanned("5-a1-b2c3d"), return Spanned("5123") and copy spans.
- * 6. filter "", return null
- * 7. filter "-123456" but dest has '-' after dend, return ""
- * 8. filter "-123456" but dest has '-' before dstart, return "123456"
+ * 2. filter "+123456", return null
+ * 3. filter "-a1.b2c3d", return "-123"
+ * 4. filter "-a1-b2c3d", return "-123"
+ * 5. filter "+a1-b2c3d", return "+123"
+ * 6. filter "5-a1-b2c3d", return "5123"
+ * 7. filter "5-a1+b2c3d", return "5123"
+ * 8. filter "+5-a1+b2c3d", return "+5123"
+ * 9. filter Spanned("5-a1-b2c3d"), return Spanned("5123") and copy spans.
+ * 10. filter "", return null
+ * 11. filter "-123456" but dest has '-' after dend, return ""
+ * 12. filter "-123456" but dest has '+' after dend, return ""
+ * 13. filter "-123456" but dest has '-' before dstart, return "123456"
+ * 14. filter "+123456" but dest has '-' before dstart, return "123456"
*/
public void testFilter2() {
String source = "-123456";
@@ -123,6 +135,11 @@
dest, 0, dest.length()));
assertEquals(destString, dest.toString());
+ source = "+123456";
+ assertNull(digitsKeyListener.filter(source, 0, source.length(),
+ dest, 0, dest.length()));
+ assertEquals(destString, dest.toString());
+
source = "-a1.b2c3d";
assertEquals("-123", (digitsKeyListener.filter(source, 0, source.length(),
dest, 0, dest.length())).toString());
@@ -133,11 +150,27 @@
dest, 0, dest.length())).toString());
assertEquals(destString, dest.toString());
+ source = "+a1-b2c3d";
+ assertEquals("+123", (digitsKeyListener.filter(source, 0, source.length(),
+ dest, 0, dest.length())).toString());
+ assertEquals(destString, dest.toString());
+
source = "5-a1-b2c3d";
assertEquals("5123", (digitsKeyListener.filter(source, 0, source.length(),
dest, 0, dest.length())).toString());
assertEquals(destString, dest.toString());
+ source = "5-a1+b2c3d";
+ assertEquals("5123", (digitsKeyListener.filter(source, 0, source.length(),
+ dest, 0, dest.length())).toString());
+ assertEquals(destString, dest.toString());
+
+ source = "+5-a1+b2c3d";
+ assertEquals("+5123", (digitsKeyListener.filter(source, 0, source.length(),
+ dest, 0, dest.length())).toString());
+ assertEquals(destString, dest.toString());
+
+ source = "5-a1+b2c3d";
Object what = new Object();
Spannable spannableSource = new SpannableString(source);
spannableSource.setSpan(what, 0, spannableSource.length(), Spanned.SPAN_POINT_POINT);
@@ -158,11 +191,23 @@
dest, 0, dest.length() - 1)).toString());
assertEquals(endSign, dest.toString());
+ endSign = "789+";
+ dest = new SpannableString(endSign);
+ assertEquals("", (digitsKeyListener.filter(source, 0, source.length(),
+ dest, 0, dest.length() - 1)).toString());
+ assertEquals(endSign, dest.toString());
+
String startSign = "-789";
dest = new SpannableString(startSign);
assertEquals("123456", (digitsKeyListener.filter(source, 0, source.length(),
dest, 1, dest.length())).toString());
assertEquals(startSign, dest.toString());
+
+ source = "+123456";
+ dest = new SpannableString(startSign);
+ assertEquals("123456", (digitsKeyListener.filter(source, 0, source.length(),
+ dest, 1, dest.length())).toString());
+ assertEquals(startSign, dest.toString());
}
/**
@@ -170,12 +215,13 @@
* Current accepted characters are '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '.'.
* 1. filter "123.456", return null
* 2. filter "-a1.b2c3d", return "1.23"
- * 3. filter "a1.b2c3d.", return "123."
- * 4. filter "5.a1.b2c3d", return "51.23"
- * 5. filter Spanned("5.a1.b2c3d"), return Spanned("51.23") and copy spans.
- * 6. filter "", return null
- * 7. filter "123.456" but dest has '.' after dend, return "123456"
- * 8. filter "123.456" but dest has '.' before dstart, return "123456"
+ * 3. filter "+a1.b2c3d", return "1.23"
+ * 4. filter "a1.b2c3d.", return "123."
+ * 5. filter "5.a1.b2c3d", return "51.23"
+ * 6. filter Spanned("5.a1.b2c3d"), return Spanned("51.23") and copy spans.
+ * 7. filter "", return null
+ * 8. filter "123.456" but dest has '.' after dend, return "123456"
+ * 9. filter "123.456" but dest has '.' before dstart, return "123456"
*/
public void testFilter3() {
String source = "123.456";
@@ -192,6 +238,11 @@
dest, 0, dest.length())).toString());
assertEquals(destString, dest.toString());
+ source = "+a1.b2c3d";
+ assertEquals("1.23", (digitsKeyListener.filter(source, 0, source.length(),
+ dest, 0, dest.length())).toString());
+ assertEquals(destString, dest.toString());
+
source = "a1.b2c3d.";
assertEquals("123.", (digitsKeyListener.filter(source, 0, source.length(),
dest, 0, dest.length())).toString());
@@ -231,17 +282,25 @@
/**
* Check point:
- * Current accepted characters are '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '.', '-'.
+ * Current accepted characters are '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '.', '-',
+ * '+'.
* 1. filter "-123.456", return null
- * 2. filter "-a1.b2c3d", return "-1.23"
- * 3. filter "a1.b-2c3d.", return "123."
- * 4. filter "-5.a1.b2c3d", return "-51.23"
- * 5. filter Spanned("-5.a1.b2c3d"), return Spanned("-51.23") and copy spans.
- * 6. filter "", return null
- * 7. filter "-123.456" but dest has '.' after dend, return "-123456"
- * 8. filter "-123.456" but dest has '.' before dstart, return "123456"
- * 9. filter "-123.456" but dest has '-' after dend, return ""
- * 10. filter "-123.456" but dest has '-' before dstart, return "123.456"
+ * 2. filter "+123.456", return null
+ * 3. filter "-a1.b2c3d", return "-1.23"
+ * 4. filter "+a1.b2c3d", return "+1.23"
+ * 5. filter "a1.b-2c+3d.", return "123."
+ * 6. filter "-5.a1.b2c+3d", return "-51.23"
+ * 7. filter "+5.a1.b2c-3d", return "+51.23"
+ * 8. filter Spanned("-5.a1.b2c3d"), return Spanned("-51.23") and copy spans.
+ * 9. filter "", return null
+ * 10. filter "-123.456" but dest has '.' after dend, return "-123456"
+ * 11. filter "-123.456" but dest has '.' before dstart, return "123456"
+ * 12. filter "+123.456" but dest has '.' after dend, return "+123456"
+ * 13. filter "+123.456" but dest has '.' before dstart, return "123456"
+ * 14. filter "-123.456" but dest has '-' after dend, return ""
+ * 15. filter "-123.456" but dest has '+' after dend, return ""
+ * 16. filter "-123.456" but dest has '-' before dstart, return "123.456"
+ * 17. filter "+123.456" but dest has '-' before dstart, return "123.456"
*/
public void testFilter4() {
String source = "-123.456";
@@ -253,21 +312,32 @@
dest, 0, dest.length()));
assertEquals(destString, dest.toString());
+ source = "+123.456";
+ assertNull(digitsKeyListener.filter(source, 0, source.length(),
+ dest, 0, dest.length()));
+ assertEquals(destString, dest.toString());
+
source = "-a1.b2c3d";
assertEquals("-1.23", (digitsKeyListener.filter(source, 0, source.length(),
dest, 0, dest.length())).toString());
assertEquals(destString, dest.toString());
- source = "a1.b-2c3d.";
+ source = "a1.b-2c+3d.";
assertEquals("123.", (digitsKeyListener.filter(source, 0, source.length(),
dest, 0, dest.length())).toString());
assertEquals(destString, dest.toString());
- source = "-5.a1.b2c3d";
+ source = "-5.a1.b2c+3d";
assertEquals("-51.23", (digitsKeyListener.filter(source, 0, source.length(),
dest, 0, dest.length())).toString());
assertEquals(destString, dest.toString());
+ source = "+5.a1.b2c-3d";
+ assertEquals("+51.23", (digitsKeyListener.filter(source, 0, source.length(),
+ dest, 0, dest.length())).toString());
+ assertEquals(destString, dest.toString());
+
+ source = "-5.a1.b2c+3d";
Object what = new Object();
Spannable spannableSource = new SpannableString(source);
spannableSource.setSpan(what, 0, spannableSource.length(), Spanned.SPAN_POINT_POINT);
@@ -294,17 +364,43 @@
dest, 1, dest.length())).toString());
assertEquals(startDecimal, dest.toString());
+ source = "+123.456";
+ endDecimal = "789.";
+ dest = new SpannableString(endDecimal);
+ assertEquals("+123456", (digitsKeyListener.filter(source, 0, source.length(),
+ dest, 0, dest.length() - 1)).toString());
+ assertEquals(endDecimal, dest.toString());
+
+ startDecimal = ".789";
+ dest = new SpannableString(startDecimal);
+ assertEquals("123456", (digitsKeyListener.filter(source, 0, source.length(),
+ dest, 1, dest.length())).toString());
+ assertEquals(startDecimal, dest.toString());
+
+ source = "-123.456";
String endSign = "789-";
dest = new SpannableString(endSign);
assertEquals("", (digitsKeyListener.filter(source, 0, source.length(),
dest, 0, dest.length() - 1)).toString());
assertEquals(endSign, dest.toString());
+ endSign = "789+";
+ dest = new SpannableString(endSign);
+ assertEquals("", (digitsKeyListener.filter(source, 0, source.length(),
+ dest, 0, dest.length() - 1)).toString());
+ assertEquals(endSign, dest.toString());
+
String startSign = "-789";
dest = new SpannableString(startSign);
assertEquals("123.456", (digitsKeyListener.filter(source, 0, source.length(),
dest, 1, dest.length())).toString());
assertEquals(startSign, dest.toString());
+
+ source = "+123.456";
+ dest = new SpannableString(startSign);
+ assertEquals("123.456", (digitsKeyListener.filter(source, 0, source.length(),
+ dest, 1, dest.length())).toString());
+ assertEquals(startSign, dest.toString());
}
/**
@@ -346,12 +442,13 @@
/**
* Scenario description:
- * Current accepted characters are '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-'.
+ * Current accepted characters are '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '+'.
* 1. Press '-' key and check if the content of TextView becomes "-"
* 2. Press '1' key and check if the content of TextView becomes "-1"
* 3. Press '.' key and this key could not be accepted.
- * 4. Press '2' key and check if the content of TextView becomes "-12"
- * 5. Press '-' key and this key could not be accepted,
+ * 4. Press '+' key and this key could not be accepted.
+ * 5. Press '2' key and check if the content of TextView becomes "-12"
+ * 6. Press '-' key and this key could not be accepted,
* because text view accepts minus sign iff it at the beginning.
*/
public void testDigitsKeyListener2() {
@@ -378,6 +475,10 @@
sendKeys(KeyEvent.KEYCODE_PERIOD);
assertEquals("-1", mTextView.getText().toString());
+ // press '+' key.
+ sendKeys(KeyEvent.KEYCODE_PLUS);
+ assertEquals("-1", mTextView.getText().toString());
+
// press '2' key.
sendKeys(KeyEvent.KEYCODE_2);
assertEquals("-12", mTextView.getText().toString());
@@ -391,92 +492,98 @@
* Scenario description:
* Current accepted characters are '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '.'.
* 1. Press '-' key and check if the content of TextView becomes ""
- * 2. Press '1' key and check if the content of TextView becomes "1"
- * 3. Press '.' key and check if the content of TextView becomes "1."
- * 4. Press '2' key and check if the content of TextView becomes "1.2"
- * 5. Press '.' key and this key could not be accepted,
+ * 2. Press '+' key and check if the content of TextView becomes ""
+ * 3. Press '1' key and check if the content of TextView becomes "1"
+ * 4. Press '.' key and check if the content of TextView becomes "1."
+ * 5. Press '2' key and check if the content of TextView becomes "1.2"
+ * 6. Press '.' key and this key could not be accepted,
* because text view accepts only one decimal point per field.
*/
public void testDigitsKeyListener3() {
-// final DigitsKeyListener digitsKeyListener = DigitsKeyListener.getInstance(false, true);
-//
-// mActivity.runOnUiThread(new Runnable() {
-// public void run() {
-// mTextView.setKeyListener(digitsKeyListener);
-// mTextView.requestFocus();
-// }
-// });
-// mInstrumentation.waitForIdleSync();
-// assertEquals("", mTextView.getText().toString());
-//
-// // press '-' key.
-// sendKeys(KeyEvent.KEYCODE_MINUS);
-// assertEquals("", mTextView.getText().toString());
-//
-// // press '1' key.
-// sendKeys(KeyEvent.KEYCODE_1);
-// assertEquals("1", mTextView.getText().toString());
-//
-// // press '.' key.
-// sendKeys(KeyEvent.KEYCODE_PERIOD);
-// assertEquals("1.", mTextView.getText().toString());
-//
-// // press '2' key.
-// sendKeys(KeyEvent.KEYCODE_2);
-// assertEquals("1.2", mTextView.getText().toString());
-//
-// // press '.' key.
-// sendKeys(KeyEvent.KEYCODE_PERIOD);
-// assertEquals("1.2", mTextView.getText().toString());
+ final DigitsKeyListener digitsKeyListener = DigitsKeyListener.getInstance(false, true);
+
+ mActivity.runOnUiThread(new Runnable() {
+ public void run() {
+ mTextView.setKeyListener(digitsKeyListener);
+ mTextView.requestFocus();
+ }
+ });
+ mInstrumentation.waitForIdleSync();
+ assertEquals("", mTextView.getText().toString());
+
+ // press '-' key.
+ sendKeys(KeyEvent.KEYCODE_MINUS);
+ assertEquals("", mTextView.getText().toString());
+
+ // press '+' key.
+ sendKeys(KeyEvent.KEYCODE_PLUS);
+ assertEquals("", mTextView.getText().toString());
+
+ // press '1' key.
+ sendKeys(KeyEvent.KEYCODE_1);
+ assertEquals("1", mTextView.getText().toString());
+
+ // press '.' key.
+ sendKeys(KeyEvent.KEYCODE_PERIOD);
+ assertEquals("1.", mTextView.getText().toString());
+
+ // press '2' key.
+ sendKeys(KeyEvent.KEYCODE_2);
+ assertEquals("1.2", mTextView.getText().toString());
+
+ // press '.' key.
+ sendKeys(KeyEvent.KEYCODE_PERIOD);
+ assertEquals("1.2", mTextView.getText().toString());
}
/**
* Scenario description:
- * Current accepted characters are '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '.'.
- * 1. Press '-' key and check if the content of TextView becomes "-"
- * 2. Press '1' key and check if the content of TextView becomes "-1"
+ * Current accepted characters are '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '+',
+ * '.'.
+ * 1. Press '+' key and check if the content of TextView becomes "+"
+ * 2. Press '1' key and check if the content of TextView becomes "+1"
* 3. Press '.' key and this key could not be accepted.
- * 4. Press '2' key and check if the content of TextView becomes "-12"
+ * 4. Press '2' key and check if the content of TextView becomes "+12"
* 5. Press '-' key and this key could not be accepted,
* because text view accepts minus sign iff it at the beginning.
* 6. Press '.' key and this key could not be accepted,
* because text view accepts only one decimal point per field.
*/
public void testDigitsKeyListener4() {
-// final DigitsKeyListener digitsKeyListener = DigitsKeyListener.getInstance(true, true);
-//
-// mActivity.runOnUiThread(new Runnable() {
-// public void run() {
-// mTextView.setKeyListener(digitsKeyListener);
-// mTextView.requestFocus();
-// }
-// });
-// mInstrumentation.waitForIdleSync();
-// assertEquals("", mTextView.getText().toString());
-//
-// // press '-' key.
-// sendKeys(KeyEvent.KEYCODE_MINUS);
-// assertEquals("-", mTextView.getText().toString());
-//
-// // press '1' key.
-// sendKeys(KeyEvent.KEYCODE_1);
-// assertEquals("-1", mTextView.getText().toString());
-//
-// // press '.' key.
-// sendKeys(KeyEvent.KEYCODE_PERIOD);
-// assertEquals("-1.", mTextView.getText().toString());
-//
-// // press '2' key.
-// sendKeys(KeyEvent.KEYCODE_2);
-// assertEquals("-1.2", mTextView.getText().toString());
-//
-// // press '-' key.
-// sendKeys(KeyEvent.KEYCODE_MINUS);
-// assertEquals("-1.2", mTextView.getText().toString());
-//
-// // press '.' key.
-// sendKeys(KeyEvent.KEYCODE_PERIOD);
-// assertEquals("-1.2", mTextView.getText().toString());
+ final DigitsKeyListener digitsKeyListener = DigitsKeyListener.getInstance(true, true);
+
+ mActivity.runOnUiThread(new Runnable() {
+ public void run() {
+ mTextView.setKeyListener(digitsKeyListener);
+ mTextView.requestFocus();
+ }
+ });
+ mInstrumentation.waitForIdleSync();
+ assertEquals("", mTextView.getText().toString());
+
+ // press '+' key.
+ sendKeys(KeyEvent.KEYCODE_PLUS);
+ assertEquals("+", mTextView.getText().toString());
+
+ // press '1' key.
+ sendKeys(KeyEvent.KEYCODE_1);
+ assertEquals("+1", mTextView.getText().toString());
+
+ // press '.' key.
+ sendKeys(KeyEvent.KEYCODE_PERIOD);
+ assertEquals("+1.", mTextView.getText().toString());
+
+ // press '2' key.
+ sendKeys(KeyEvent.KEYCODE_2);
+ assertEquals("+1.2", mTextView.getText().toString());
+
+ // press '-' key.
+ sendKeys(KeyEvent.KEYCODE_MINUS);
+ assertEquals("+1.2", mTextView.getText().toString());
+
+ // press '.' key.
+ sendKeys(KeyEvent.KEYCODE_PERIOD);
+ assertEquals("+1.2", mTextView.getText().toString());
}
/**
@@ -570,9 +677,9 @@
final char[][] expected = new char[][] {
new char[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' },
- new char[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-' },
+ new char[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '+' },
new char[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '.' },
- new char[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '.' },
+ new char[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '+', '.' },
};
TextMethodUtils.assertEquals(expected[0],
diff --git a/tests/tests/view/src/android/view/cts/DisplayTest.java b/tests/tests/view/src/android/view/cts/DisplayTest.java
deleted file mode 100644
index 489282a..0000000
--- a/tests/tests/view/src/android/view/cts/DisplayTest.java
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Copyright (C) 2009 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.content.Context;
-import android.test.AndroidTestCase;
-import android.util.DisplayMetrics;
-import android.view.Display;
-import android.view.WindowManager;
-
-public class DisplayTest extends AndroidTestCase {
-
- /**
- * Test the properties of Display, they are:
- * 1 index of this display
- * 2 height of this display in pixels
- * 3 width of this display in pixels
- * 4 orientation of this display
- * 5 pixel format of this display
- * 6 refresh rate of this display in frames per second
- * 7 Initialize a DisplayMetrics object from this display's data
- */
- public void testGetDisplayAttrs() {
- Context con = getContext();
- WindowManager windowManager = (WindowManager) con.getSystemService(Context.WINDOW_SERVICE);
- Display display = windowManager.getDefaultDisplay();
-
- assertEquals(Display.DEFAULT_DISPLAY, display.getDisplayId());
- assertTrue(0 < display.getHeight());
- assertTrue(0 < display.getWidth());
- display.getOrientation();
- assertTrue(0 < display.getPixelFormat());
- assertTrue(0 < display.getRefreshRate());
-
- DisplayMetrics outMetrics = new DisplayMetrics();
- outMetrics.setToDefaults();
- display.getMetrics(outMetrics);
- assertEquals(display.getHeight(), outMetrics.heightPixels);
- assertEquals(display.getWidth(), outMetrics.widthPixels);
-
- // The scale is in [0.1, 3], and density is the scale factor.
- assertTrue(0.1f <= outMetrics.density && outMetrics.density <= 3.0f);
- assertTrue(0.1f <= outMetrics.scaledDensity && outMetrics.density <= 3.0f);
- assertTrue(0 < outMetrics.xdpi);
- assertTrue(0 < outMetrics.ydpi);
- }
-}
diff --git a/tests/tests/view/src/android/view/cts/ViewTreeObserverTest.java b/tests/tests/view/src/android/view/cts/ViewTreeObserverTest.java
index c5b56a4..1ee690b 100644
--- a/tests/tests/view/src/android/view/cts/ViewTreeObserverTest.java
+++ b/tests/tests/view/src/android/view/cts/ViewTreeObserverTest.java
@@ -35,7 +35,6 @@
import android.view.ViewTreeObserver.OnTouchModeChangeListener;
import android.widget.Button;
import android.widget.LinearLayout;
-import android.widget.ListView;
import android.widget.ScrollView;
public class ViewTreeObserverTest extends ActivityInstrumentationTestCase2<MockActivity> {
@@ -69,8 +68,15 @@
public void testAddOnGlobalFocusChangeListener() {
final LinearLayout layout = (LinearLayout) mActivity.findViewById(R.id.linearlayout);
- final ListView lv1 = (ListView) mActivity.findViewById(R.id.listview1);
- final ListView lv2 = (ListView) mActivity.findViewById(R.id.listview2);
+ final View view1 = mActivity.findViewById(R.id.view1);
+ final View view2 = mActivity.findViewById(R.id.view2);
+
+ mInstrumentation.runOnMainSync(new Runnable() {
+ @Override
+ public void run() {
+ view1.requestFocus();
+ }
+ });
mViewTreeObserver = layout.getViewTreeObserver();
final MockOnGlobalFocusChangeListener listener = new MockOnGlobalFocusChangeListener();
@@ -80,14 +86,15 @@
mInstrumentation.runOnMainSync(new Runnable() {
@Override
public void run() {
- layout.requestChildFocus(lv1, lv2);
+ view2.requestFocus();
}
});
mInstrumentation.waitForIdleSync();
new PollingCheck() {
@Override
protected boolean check() {
- return listener.hasCalledOnGlobalFocusChanged();
+ return listener.hasCalledOnGlobalFocusChanged()
+ && listener.oldFocus == view1 && listener.newFocus == view2;
}
}.run();
}
@@ -144,16 +151,16 @@
}
public void testAddOnComputeInternalInsetsListener() {
- final ListView lv1 = (ListView) mActivity.findViewById(R.id.listview1);
- mViewTreeObserver = lv1.getViewTreeObserver();
+ final View view1 = mActivity.findViewById(R.id.view1);
+ mViewTreeObserver = view1.getViewTreeObserver();
MockOnComputeInternalInsetsListener listener = new MockOnComputeInternalInsetsListener();
mViewTreeObserver.addOnComputeInternalInsetsListener(listener);
}
public void testRemoveOnComputeInternalInsetsListener() {
- final ListView lv1 = (ListView) mActivity.findViewById(R.id.listview1);
- mViewTreeObserver = lv1.getViewTreeObserver();
+ final View view1 = mActivity.findViewById(R.id.view1);
+ mViewTreeObserver = view1.getViewTreeObserver();
MockOnComputeInternalInsetsListener listener = new MockOnComputeInternalInsetsListener();
mViewTreeObserver.removeOnComputeInternalInsetsListener(listener);
@@ -207,8 +214,15 @@
public void testRemoveOnGlobalFocusChangeListener() {
final LinearLayout layout = (LinearLayout) mActivity.findViewById(R.id.linearlayout);
- final ListView lv1 = (ListView) mActivity.findViewById(R.id.listview1);
- final ListView lv2 = (ListView) mActivity.findViewById(R.id.listview2);
+ final View view1 = mActivity.findViewById(R.id.view1);
+ final View view2 = mActivity.findViewById(R.id.view2);
+
+ mInstrumentation.runOnMainSync(new Runnable() {
+ @Override
+ public void run() {
+ view1.requestFocus();
+ }
+ });
mViewTreeObserver = layout.getViewTreeObserver();
final MockOnGlobalFocusChangeListener listener = new MockOnGlobalFocusChangeListener();
@@ -217,14 +231,15 @@
mInstrumentation.runOnMainSync(new Runnable() {
@Override
public void run() {
- layout.requestChildFocus(lv1, lv2);
+ view2.requestFocus();
}
});
mInstrumentation.waitForIdleSync();
new PollingCheck() {
@Override
protected boolean check() {
- return listener.hasCalledOnGlobalFocusChanged();
+ return listener.hasCalledOnGlobalFocusChanged()
+ && listener.oldFocus == view1 && listener.newFocus == view2;
}
}.run();
@@ -234,7 +249,7 @@
mInstrumentation.runOnMainSync(new Runnable() {
@Override
public void run() {
- layout.requestChildFocus(lv1, lv2);
+ view1.requestFocus();
}
});
mInstrumentation.waitForIdleSync();
@@ -345,10 +360,14 @@
private class MockOnGlobalFocusChangeListener implements OnGlobalFocusChangeListener {
private boolean mCalledOnGlobalFocusChanged = false;
+ View oldFocus;
+ View newFocus;
@Override
public void onGlobalFocusChanged(View oldFocus, View newFocus) {
mCalledOnGlobalFocusChanged = true;
+ this.oldFocus = oldFocus;
+ this.newFocus = newFocus;
}
public boolean hasCalledOnGlobalFocusChanged() {
@@ -357,6 +376,8 @@
public void reset() {
mCalledOnGlobalFocusChanged = false;
+ oldFocus = null;
+ newFocus = null;
}
}
diff --git a/tests/tests/view/src/android/view/inputmethod/cts/InputMethodInfoTest.java b/tests/tests/view/src/android/view/inputmethod/cts/InputMethodInfoTest.java
index 00a8dea..722c1b3 100644
--- a/tests/tests/view/src/android/view/inputmethod/cts/InputMethodInfoTest.java
+++ b/tests/tests/view/src/android/view/inputmethod/cts/InputMethodInfoTest.java
@@ -16,7 +16,6 @@
package android.view.inputmethod.cts;
-
import android.content.ComponentName;
import android.content.Intent;
import android.content.pm.PackageManager;
@@ -27,6 +26,7 @@
import android.util.Printer;
import android.view.inputmethod.InputMethod;
import android.view.inputmethod.InputMethodInfo;
+import android.view.inputmethod.InputMethodSubtype;
import org.xmlpull.v1.XmlPullParserException;
@@ -40,14 +40,40 @@
private CharSequence mLabel;
private String mSettingsActivity;
+ private int mSubtypeNameResId;
+ private int mSubtypeIconResId;
+ private String mSubtypeLocale;
+ private String mSubtypeMode;
+ private String mSubtypeExtraValue_key;
+ private String mSubtypeExtraValue_value;
+ private String mSubtypeExtraValue;
+ private boolean mSubtypeIsAuxiliary;
+ private boolean mSubtypeOverridesImplicitlyEnabledSubtype;
+ private int mSubtypeId;
+ private InputMethodSubtype mInputMethodSubtype;
+
@Override
protected void setUp() throws Exception {
super.setUp();
mPackageName = mContext.getPackageName();
- mClassName = InputMethodInfoStub.class.getName();
+ mClassName = InputMethodSettingsActivityStub.class.getName();
mLabel = "test";
- mSettingsActivity = "android.view.inputmethod.cts.InputMethodInfoStub";
+ mSettingsActivity = "android.view.inputmethod.cts.InputMethodSettingsActivityStub";
mInputMethodInfo = new InputMethodInfo(mPackageName, mClassName, mLabel, mSettingsActivity);
+
+ mSubtypeNameResId = 0;
+ mSubtypeIconResId = 0;
+ mSubtypeLocale = "en_US";
+ mSubtypeMode = "keyboard";
+ mSubtypeExtraValue_key = "key1";
+ mSubtypeExtraValue_value = "value1";
+ mSubtypeExtraValue = "tag," + mSubtypeExtraValue_key + "=" + mSubtypeExtraValue_value;
+ mSubtypeIsAuxiliary = false;
+ mSubtypeOverridesImplicitlyEnabledSubtype = false;
+ mSubtypeId = 99;
+ mInputMethodSubtype = new InputMethodSubtype(mSubtypeNameResId, mSubtypeIconResId,
+ mSubtypeLocale, mSubtypeMode, mSubtypeExtraValue, mSubtypeIsAuxiliary,
+ mSubtypeOverridesImplicitlyEnabledSubtype, mSubtypeId);
}
public void testInputMethodInfoProperties() throws XmlPullParserException, IOException {
@@ -58,7 +84,7 @@
assertEquals(0, mInputMethodInfo.getIsDefaultResourceId());
Intent intent = new Intent(InputMethod.SERVICE_INTERFACE);
- intent.setClass(mContext, InputMethodInfoStub.class);
+ intent.setClass(mContext, InputMethodSettingsActivityStub.class);
PackageManager pm = mContext.getPackageManager();
List<ResolveInfo> ris = pm.queryIntentServices(intent, PackageManager.GET_META_DATA);
for (int i = 0; i < ris.size(); i++) {
@@ -69,6 +95,22 @@
}
}
+ public void testInputMethodSubtypeProperties() {
+ // TODO: Test InputMethodSubtype.getDisplayName()
+ assertEquals(mSubtypeNameResId, mInputMethodSubtype.getNameResId());
+ assertEquals(mSubtypeIconResId, mInputMethodSubtype.getIconResId());
+ assertEquals(mSubtypeLocale, mInputMethodSubtype.getLocale());
+ assertEquals(mSubtypeMode, mInputMethodSubtype.getMode());
+ assertEquals(mSubtypeExtraValue, mInputMethodSubtype.getExtraValue());
+ assertTrue(mInputMethodSubtype.containsExtraValueKey(mSubtypeExtraValue_key));
+ assertEquals(mSubtypeExtraValue_value,
+ mInputMethodSubtype.getExtraValueOf(mSubtypeExtraValue_key));
+ assertEquals(mSubtypeIsAuxiliary, mInputMethodSubtype.isAuxiliary());
+ assertEquals(mSubtypeOverridesImplicitlyEnabledSubtype,
+ mInputMethodSubtype.overridesImplicitlyEnabledSubtype());
+ assertEquals(mSubtypeId, mInputMethodSubtype.hashCode());
+ }
+
private void assertService(ServiceInfo expected, ServiceInfo actual) {
assertEquals(expected.getIconResource(), actual.getIconResource());
assertEquals(expected.labelRes, actual.labelRes);
@@ -110,19 +152,39 @@
assertEquals(expected.toString(), mInputMethodInfo.loadLabel(pm).toString());
}
- public void testWriteToParcel() {
- Parcel p = Parcel.obtain();
+ public void testInputMethodInfoWriteToParcel() {
+ final Parcel p = Parcel.obtain();
mInputMethodInfo.writeToParcel(p, 0);
p.setDataPosition(0);
- InputMethodInfo inputMethodInfo = InputMethodInfo.CREATOR.createFromParcel(p);
+ final InputMethodInfo imi = InputMethodInfo.CREATOR.createFromParcel(p);
- assertEquals(mInputMethodInfo.getPackageName(), inputMethodInfo.getPackageName());
- assertEquals(mInputMethodInfo.getServiceName(), inputMethodInfo.getServiceName());
- assertEquals(mInputMethodInfo.getSettingsActivity(), inputMethodInfo.getSettingsActivity());
- assertEquals(mInputMethodInfo.getId(), inputMethodInfo.getId());
- assertEquals(mInputMethodInfo.getIsDefaultResourceId(), inputMethodInfo
- .getIsDefaultResourceId());
- assertService(mInputMethodInfo.getServiceInfo(), inputMethodInfo.getServiceInfo());
+ assertEquals(mInputMethodInfo.getPackageName(), imi.getPackageName());
+ assertEquals(mInputMethodInfo.getServiceName(), imi.getServiceName());
+ assertEquals(mInputMethodInfo.getSettingsActivity(), imi.getSettingsActivity());
+ assertEquals(mInputMethodInfo.getId(), imi.getId());
+ assertEquals(mInputMethodInfo.getIsDefaultResourceId(), imi.getIsDefaultResourceId());
+ assertService(mInputMethodInfo.getServiceInfo(), imi.getServiceInfo());
+ }
+
+ public void testInputMethodSubtypeWriteToParcel() {
+ final Parcel p = Parcel.obtain();
+ mInputMethodSubtype.writeToParcel(p, 0);
+ p.setDataPosition(0);
+ final InputMethodSubtype subtype = InputMethodSubtype.CREATOR.createFromParcel(p);
+
+ assertEquals(mInputMethodSubtype.containsExtraValueKey(mSubtypeExtraValue_key),
+ subtype.containsExtraValueKey(mSubtypeExtraValue_key));
+ assertEquals(mInputMethodSubtype.getExtraValue(), subtype.getExtraValue());
+ assertEquals(mInputMethodSubtype.getExtraValueOf(mSubtypeExtraValue_key),
+ subtype.getExtraValueOf(mSubtypeExtraValue_key));
+ assertEquals(mInputMethodSubtype.getIconResId(), subtype.getIconResId());
+ assertEquals(mInputMethodSubtype.getLocale(), subtype.getLocale());
+ assertEquals(mInputMethodSubtype.getMode(), subtype.getMode());
+ assertEquals(mInputMethodSubtype.getNameResId(), subtype.getNameResId());
+ assertEquals(mInputMethodSubtype.hashCode(), subtype.hashCode());
+ assertEquals(mInputMethodSubtype.isAuxiliary(), subtype.isAuxiliary());
+ assertEquals(mInputMethodSubtype.overridesImplicitlyEnabledSubtype(),
+ subtype.overridesImplicitlyEnabledSubtype());
}
class MockPrinter implements Printer {
diff --git a/tests/tests/webkit/AndroidManifest.xml b/tests/tests/webkit/AndroidManifest.xml
index 9475451..493762e 100644
--- a/tests/tests/webkit/AndroidManifest.xml
+++ b/tests/tests/webkit/AndroidManifest.xml
@@ -19,6 +19,8 @@
package="com.android.cts.webkit">
<uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
+ <uses-permission android:name="android.permission.ACCESS_LOCATION_EXTRA_COMMANDS"/>
+ <uses-permission android:name="android.permission.ACCESS_MOCK_LOCATION"/>
<application>
<uses-library android:name="android.test.runner" />
</application>
diff --git a/tests/tests/webkit/src/android/webkit/cts/CacheManagerTest.java b/tests/tests/webkit/src/android/webkit/cts/CacheManagerTest.java
deleted file mode 100644
index 66b1e02..0000000
--- a/tests/tests/webkit/src/android/webkit/cts/CacheManagerTest.java
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- * Copyright (C) 2009 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.webkit.cts;
-
-import android.cts.util.PollingCheck;
-import android.test.ActivityInstrumentationTestCase2;
-import android.webkit.CacheManager;
-import android.webkit.CacheManager.CacheResult;
-
-
-import java.util.Map;
-
-public class CacheManagerTest extends ActivityInstrumentationTestCase2<WebViewStubActivity> {
- private static final long CACHEMANAGER_INIT_TIMEOUT = 5000L;
- private static final long NETWORK_OPERATION_TIMEOUT = 10000L;
-
- private CtsTestServer mWebServer;
- private WebViewOnUiThread mOnUiThread;
-
- public CacheManagerTest() {
- super("com.android.cts.stub", WebViewStubActivity.class);
- }
-
- @Override
- protected void setUp() throws Exception {
- super.setUp();
- mOnUiThread = new WebViewOnUiThread(this, getActivity().getWebView());
- }
-
- @Override
- protected void tearDown() throws Exception {
- mOnUiThread.cleanUp();
- if (mWebServer != null) {
- mWebServer.shutdown();
- }
- super.tearDown();
- }
-
- public void testGetCacheFileBaseDir() {
- assertTrue(CacheManager.getCacheFileBaseDir().exists());
- }
-
- public void testCacheTransaction() {
- }
-
- public void testCacheFile() throws Exception {
- mWebServer = new CtsTestServer(getActivity());
- final String url = mWebServer.getAssetUrl(TestHtmlConstants.EMBEDDED_IMG_URL);
-
- // Wait for CacheManager#init() finish.
- new PollingCheck(CACHEMANAGER_INIT_TIMEOUT) {
- @Override
- protected boolean check() {
- return CacheManager.getCacheFileBaseDir() != null;
- }
- }.run();
-
- mOnUiThread.clearCache(true);
- new PollingCheck(NETWORK_OPERATION_TIMEOUT) {
- @Override
- protected boolean check() {
- CacheResult result = CacheManager.getCacheFile(url, null);
- return result == null;
- }
- }.run();
-
- mOnUiThread.loadUrlAndWaitForCompletion(url);
- new PollingCheck(NETWORK_OPERATION_TIMEOUT) {
- @Override
- protected boolean check() {
- CacheResult result = CacheManager.getCacheFile(url, null);
- return result != null;
- }
- }.run();
-
- // Can not test saveCacheFile(), because the output stream is null and
- // saveCacheFile() will throw a NullPointerException. There is no
- // public API to set the output stream.
- }
-
- public void testCacheDisabled() {
- // The cache should always be enabled.
- assertFalse(CacheManager.cacheDisabled());
- }
-}
diff --git a/tests/tests/webkit/src/android/webkit/cts/CacheManager_CacheResultTest.java b/tests/tests/webkit/src/android/webkit/cts/CacheManager_CacheResultTest.java
deleted file mode 100755
index 0c2a51a..0000000
--- a/tests/tests/webkit/src/android/webkit/cts/CacheManager_CacheResultTest.java
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * Copyright (C) 2009 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.webkit.cts;
-
-import android.cts.util.PollingCheck;
-import android.test.ActivityInstrumentationTestCase2;
-import android.webkit.CacheManager;
-import android.webkit.CacheManager.CacheResult;
-import android.webkit.WebView;
-
-
-import org.apache.http.HttpStatus;
-import org.apache.http.impl.cookie.DateUtils;
-
-import java.io.File;
-import java.io.InputStream;
-import java.util.HashMap;
-import java.util.Map;
-
-public class CacheManager_CacheResultTest
- extends ActivityInstrumentationTestCase2<WebViewStubActivity> {
- private static final long NETWORK_OPERATION_TIMEOUT = 10000L;
-
- private CtsTestServer mWebServer;
- private WebViewOnUiThread mOnUiThread;
-
- public CacheManager_CacheResultTest() {
- super("com.android.cts.stub", WebViewStubActivity.class);
- }
-
- @Override
- protected void setUp() throws Exception {
- super.setUp();
- mOnUiThread = new WebViewOnUiThread(this, getActivity().getWebView());
- }
-
- @Override
- protected void tearDown() throws Exception {
- mOnUiThread.cleanUp();
- if (mWebServer != null) {
- mWebServer.shutdown();
- }
- super.tearDown();
- }
-
- public void testCacheResult() throws Exception {
- final long validity = 5 * 60 * 1000; // 5 min
- final long age = 30 * 60 * 1000; // 30 min
- final long tolerance = 5 * 1000; // 5s
-
- mWebServer = new CtsTestServer(getActivity());
- final String url = mWebServer.getAssetUrl(TestHtmlConstants.HELLO_WORLD_URL);
- mWebServer.setDocumentAge(age);
- mWebServer.setDocumentValidity(validity);
-
- mOnUiThread.clearCache(true);
- new PollingCheck(NETWORK_OPERATION_TIMEOUT) {
- @Override
- protected boolean check() {
- CacheResult result =
- CacheManager.getCacheFile(url, null);
- return result == null;
- }
- }.run();
- final long time = System.currentTimeMillis();
- mOnUiThread.loadUrlAndWaitForCompletion(url);
-
- Map<String, String> headers = new HashMap<String, String>();
- CacheResult result = CacheManager.getCacheFile(url, headers);
- assertTrue(headers.isEmpty());
-
- assertNotNull(result);
- assertNotNull(result.getInputStream());
- assertTrue(result.getContentLength() > 0);
- assertNull(result.getETag());
- assertEquals((double)(time - age),
- (double)DateUtils.parseDate(result.getLastModified()).getTime(),
- (double)tolerance);
- File file = new File(CacheManager.getCacheFileBaseDir().getPath(), result.getLocalPath());
- assertTrue(file.exists());
- assertNull(result.getLocation());
- assertEquals("text/html", result.getMimeType());
- assertNull(result.getOutputStream());
- assertEquals((double)(time + validity), (double)result.getExpires(),
- (double)tolerance);
- assertEquals(HttpStatus.SC_OK, result.getHttpStatusCode());
- assertNotNull(result.getEncoding());
-
- result.setEncoding("iso-8859-1");
- assertEquals("iso-8859-1", result.getEncoding());
-
- result.setInputStream(null);
- assertNull(result.getInputStream());
- }
-}
diff --git a/tests/tests/webkit/src/android/webkit/cts/CookieManagerTest.java b/tests/tests/webkit/src/android/webkit/cts/CookieManagerTest.java
index 51eae48..a0b4fa6 100755
--- a/tests/tests/webkit/src/android/webkit/cts/CookieManagerTest.java
+++ b/tests/tests/webkit/src/android/webkit/cts/CookieManagerTest.java
@@ -68,7 +68,7 @@
CtsTestServer server = new CtsTestServer(getActivity(), false);
String url = server.getCookieUrl("conquest.html");
mOnUiThread.loadUrlAndWaitForCompletion(url);
- assertEquals(null, mOnUiThread.getTitle()); // no cookies passed
+ assertEquals("0", mOnUiThread.getTitle()); // no cookies passed
Thread.sleep(500);
assertNull(mCookieManager.getCookie(url));
@@ -77,7 +77,7 @@
url = server.getCookieUrl("war.html");
mOnUiThread.loadUrlAndWaitForCompletion(url);
- assertEquals(null, mOnUiThread.getTitle()); // no cookies passed
+ assertEquals("0", mOnUiThread.getTitle()); // no cookies passed
waitForCookie(url);
String cookie = mCookieManager.getCookie(url);
assertNotNull(cookie);
@@ -89,7 +89,7 @@
url = server.getCookieUrl("famine.html");
mOnUiThread.loadUrlAndWaitForCompletion(url);
- assertEquals("count=0", mOnUiThread.getTitle()); // outgoing cookie
+ assertEquals("1|count=0", mOnUiThread.getTitle()); // outgoing cookie
waitForCookie(url);
cookie = mCookieManager.getCookie(url);
assertNotNull(cookie);
@@ -100,7 +100,7 @@
url = server.getCookieUrl("death.html");
mCookieManager.setCookie(url, "count=41");
mOnUiThread.loadUrlAndWaitForCompletion(url);
- assertEquals("count=41", mOnUiThread.getTitle()); // outgoing cookie
+ assertEquals("1|count=41", mOnUiThread.getTitle()); // outgoing cookie
waitForCookie(url);
cookie = mCookieManager.getCookie(url);
assertNotNull(cookie);
diff --git a/tests/tests/webkit/src/android/webkit/cts/CookieTest.java b/tests/tests/webkit/src/android/webkit/cts/CookieTest.java
index 14a14d0..0c0396a 100644
--- a/tests/tests/webkit/src/android/webkit/cts/CookieTest.java
+++ b/tests/tests/webkit/src/android/webkit/cts/CookieTest.java
@@ -45,23 +45,6 @@
}
}
assertFalse(mCookieManager.hasCookies());
-
- }
-
- public void testParse() {
- String url = "http://www.foo.com";
-
- // basic
- mCookieManager.setCookie(url, "a=b");
- String cookie = mCookieManager.getCookie(url);
- assertTrue(cookie.equals("a=b"));
-
- // quoted
- mCookieManager.setCookie(url, "c=\"d;\"");
- cookie = mCookieManager.getCookie(url);
-
- assertTrue(cookie.contains("a=b"));
- assertTrue(cookie.contains("c=\"d;\""));
}
public void testDomain() {
diff --git a/tests/tests/webkit/src/android/webkit/cts/GeolocationTest.java b/tests/tests/webkit/src/android/webkit/cts/GeolocationTest.java
new file mode 100644
index 0000000..16942e5
--- /dev/null
+++ b/tests/tests/webkit/src/android/webkit/cts/GeolocationTest.java
@@ -0,0 +1,473 @@
+/*
+ * Copyright (C) 2012 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.webkit.cts;
+
+import android.content.Context;
+import android.cts.util.PollingCheck;
+import android.graphics.Bitmap;
+import android.location.Criteria;
+import android.location.Location;
+import android.location.LocationListener;
+import android.location.LocationManager;
+import android.location.LocationProvider;
+import android.os.Bundle;
+import android.os.Looper;
+import android.os.SystemClock;
+import android.test.ActivityInstrumentationTestCase2;
+import android.util.Log;
+import android.webkit.CookieManager;
+import android.webkit.CookieSyncManager;
+import android.webkit.GeolocationPermissions;
+import android.webkit.JavascriptInterface;
+import android.webkit.WebChromeClient;
+import android.webkit.WebResourceResponse;
+import android.webkit.WebView;
+import android.webkit.WebViewClient;
+import android.webkit.cts.WebViewOnUiThread.WaitForLoadedClient;
+import android.webkit.cts.WebViewOnUiThread.WaitForProgressClient;
+
+import java.io.ByteArrayInputStream;
+import java.io.UnsupportedEncodingException;
+import java.util.concurrent.Callable;
+import java.util.Date;
+import java.util.Random;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.Set;
+import java.util.TreeSet;
+
+import junit.framework.Assert;
+
+public class GeolocationTest extends ActivityInstrumentationTestCase2<WebViewStubActivity> {
+
+ // TODO Write additional tests to cover:
+ // - test that the errors are correct
+ // - test that use of gps and network location is correct
+
+ // The URLs does not matter since the tests will intercept the load, but it has to be a real
+ // url, and different domains.
+ private static final String URL_1 = "http://www.example.com";
+ private static final String URL_2 = "http://www.example.org";
+
+ private static final String JS_INTERFACE_NAME = "Android";
+ private static final int POLLING_TIMEOUT = 2000;
+
+ // static HTML page always injected instead of the url loaded
+ private static final String RAW_HTML =
+ "<!DOCTYPE html>\n" +
+ "<html>\n" +
+ " <head>\n" +
+ " <title>Geolocation</title>\n" +
+ " <script>\n" +
+ " function gotPos(position) {\n" +
+ " " + JS_INTERFACE_NAME + ".gotLocation();\n" +
+ " }\n" +
+ " function initiate_getCurrentPosition() {\n" +
+ " navigator.geolocation.getCurrentPosition(\n" +
+ " gotPos,\n" +
+ " handle_errors,\n" +
+ " {maximumAge:1000});\n" +
+ " }\n" +
+ " function handle_errors(error) {\n" +
+ " switch(error.code) {\n" +
+ " case error.PERMISSION_DENIED:\n" +
+ " " + JS_INTERFACE_NAME + ".errorDenied(); break;\n" +
+ " case error.POSITION_UNAVAILABLE:\n" +
+ " " + JS_INTERFACE_NAME + ".errorUnavailable(); break;\n" +
+ " case error.TIMEOUT:\n" +
+ " " + JS_INTERFACE_NAME + ".errorTimeout(); break;\n" +
+ " default: break;\n" +
+ " }\n" +
+ " }\n" +
+ " </script>\n" +
+ " </head>\n" +
+ " <body onload=\"initiate_getCurrentPosition();\">\n" +
+ " </body>\n" +
+ "</html>";
+
+ private JavascriptStatusReceiver mJavascriptStatusReceiver;
+ private LocationManager mLocationManager;
+ private WebViewOnUiThread mOnUiThread;
+
+ public GeolocationTest() throws Exception {
+ super("com.android.cts.stub", WebViewStubActivity.class);
+ }
+
+ // Both this test and WebViewOnUiThread need to override some of the methods on WebViewClient,
+ // so this test sublclasses the WebViewClient from WebViewOnUiThread
+ private static class InterceptClient extends WaitForLoadedClient {
+
+ public InterceptClient(WebViewOnUiThread webViewOnUiThread) throws Exception {
+ super(webViewOnUiThread);
+ }
+
+ @Override
+ public WebResourceResponse shouldInterceptRequest(WebView view, String url) {
+ // Intercept all page loads with the same geolocation enabled page
+ try {
+ return new WebResourceResponse("text/html", "utf-8",
+ new ByteArrayInputStream(RAW_HTML.getBytes("UTF-8")));
+ } catch(java.io.UnsupportedEncodingException e) {
+ return null;
+ }
+ }
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+
+ // Set up a WebView with JavaScript and Geolocation enabled
+ final String GEO_DIR = "geo_test";
+ mOnUiThread = new WebViewOnUiThread(this, getActivity().getWebView());
+ mOnUiThread.getSettings().setJavaScriptEnabled(true);
+ mOnUiThread.getSettings().setGeolocationEnabled(true);
+ mOnUiThread.getSettings().setGeolocationDatabasePath(
+ getActivity().getApplicationContext().getDir(GEO_DIR, 0).getPath());
+
+ // Add a JsInterface to report back to the test when a location is received
+ mJavascriptStatusReceiver = new JavascriptStatusReceiver();
+ mOnUiThread.addJavascriptInterface(mJavascriptStatusReceiver, JS_INTERFACE_NAME);
+
+ // Always intercept all loads with the same geolocation test page
+ mOnUiThread.setWebViewClient(new InterceptClient(mOnUiThread));
+ // Clear all permissions before each test
+ GeolocationPermissions.getInstance().clearAll();
+ // Cache this mostly because the lookup is two lines of code
+ mLocationManager = (LocationManager)getActivity().getApplicationContext()
+ .getSystemService(Context.LOCATION_SERVICE);
+ // Add a test provider before each test to inject a location
+ addTestProvider(LocationManager.NETWORK_PROVIDER);
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ // Remove the test provider after each test
+ try {
+ mLocationManager.removeTestProvider(LocationManager.NETWORK_PROVIDER);
+ } catch (IllegalArgumentException e) {} // Not much to do about this
+ mOnUiThread.cleanUp();
+ // This will null all member and static variables
+ super.tearDown();
+ }
+
+ // Update location with a fixed latitude and longtitude, sets the time to the current time.
+ private void updateLocation(final String providerName) {
+ Location location = new Location(providerName);
+ location.setLatitude(40);
+ location.setLongitude(40);
+ location.setAccuracy(1.0f);
+ location.setTime(java.lang.System.currentTimeMillis());
+ location.setElapsedRealtimeNanos(SystemClock.elapsedRealtimeNanos());
+ mLocationManager.setTestProviderLocation(providerName, location);
+ }
+
+ // Need to set the location just after loading the url. Setting it after each load instead of
+ // using a maximum age.
+ private void loadUrlAndUpdateLocation(String url) {
+ mOnUiThread.loadUrlAndWaitForCompletion(url);
+ updateLocation(LocationManager.NETWORK_PROVIDER);
+ }
+
+ // WebChromeClient that accepts each location for one load. WebChromeClient is used in
+ // WebViewOnUiThread to detect when the page is loaded, so subclassing the one used there.
+ private static class TestSimpleGeolocationRequestWebChromeClient
+ extends WaitForProgressClient {
+ private boolean mReceivedRequest = false;
+ private final boolean mAccept;
+ private final boolean mRetain;
+
+ public TestSimpleGeolocationRequestWebChromeClient(
+ WebViewOnUiThread webViewOnUiThread, boolean accept, boolean retain) {
+ super(webViewOnUiThread);
+ this.mAccept = accept;
+ this.mRetain = retain;
+ }
+
+ @Override
+ public void onGeolocationPermissionsShowPrompt(
+ String origin, GeolocationPermissions.Callback callback) {
+ mReceivedRequest = true;
+ callback.invoke(origin, mAccept, mRetain);
+ }
+ }
+
+ private void addTestProvider(final String providerName) {
+ mLocationManager.addTestProvider(providerName,
+ true, //requiresNetwork,
+ false, // requiresSatellite,
+ true, // requiresCell,
+ false, // hasMonetaryCost,
+ false, // supportsAltitude,
+ false, // supportsSpeed,
+ false, // supportsBearing,
+ Criteria.POWER_MEDIUM, // powerRequirement
+ Criteria.ACCURACY_FINE); // accuracy
+ mLocationManager.setTestProviderEnabled(providerName, true);
+ }
+
+ // Test loading a page and accepting the domain for one load
+ public void testSimpleGeolocationRequestAcceptOnce() throws Exception {
+ final TestSimpleGeolocationRequestWebChromeClient chromeClientAcceptOnce =
+ new TestSimpleGeolocationRequestWebChromeClient(mOnUiThread, true, false);
+ mOnUiThread.setWebChromeClient(chromeClientAcceptOnce);
+ loadUrlAndUpdateLocation(URL_1);
+ Callable<Boolean> receivedRequest = new Callable<Boolean>() {
+ @Override
+ public Boolean call() {
+ return chromeClientAcceptOnce.mReceivedRequest;
+ }
+ };
+ PollingCheck.check("Geolocation prompt not called", POLLING_TIMEOUT, receivedRequest);
+ Callable<Boolean> receivedLocation = new Callable<Boolean>() {
+ @Override
+ public Boolean call() {
+ return mJavascriptStatusReceiver.mHasPosition;
+ }
+ };
+ PollingCheck.check("JS didn't get position", POLLING_TIMEOUT, receivedLocation);
+ chromeClientAcceptOnce.mReceivedRequest = false;
+ // Load URL again, should receive callback again
+ loadUrlAndUpdateLocation(URL_1);
+ PollingCheck.check("Geolocation prompt not called", POLLING_TIMEOUT, receivedRequest);
+ PollingCheck.check("JS didn't get position", POLLING_TIMEOUT, receivedLocation);
+ }
+
+ // Class that waits and checks for a particular value being received
+ private static class ValueCheck<T> extends PollingCheck implements
+ android.webkit.ValueCallback<T> {
+ private boolean mReceived = false;
+ private final T mExpectedValue;
+ private T mReceivedValue = null;
+
+ public ValueCheck(T val) {
+ mExpectedValue = val;
+ }
+
+ @Override
+ protected boolean check() {
+ return mReceived && mExpectedValue.equals(mReceivedValue);
+ }
+ @Override
+ public void onReceiveValue(T value) {
+ mReceived = true;
+ mReceivedValue = value;
+ }
+ }
+
+ // Test loading a page and retaining the domain forever
+ public void testSimpleGeolocationRequestAcceptAlways() throws Exception {
+ final TestSimpleGeolocationRequestWebChromeClient chromeClientAcceptAlways =
+ new TestSimpleGeolocationRequestWebChromeClient(mOnUiThread, true, true);
+ mOnUiThread.setWebChromeClient(chromeClientAcceptAlways);
+ // Load url once, and the callback should accept the domain for all future loads
+ loadUrlAndUpdateLocation(URL_1);
+ Callable<Boolean> receivedRequest = new Callable<Boolean>() {
+ @Override
+ public Boolean call() {
+ return chromeClientAcceptAlways.mReceivedRequest;
+ }
+ };
+ PollingCheck.check("Geolocation prompt not called", POLLING_TIMEOUT, receivedRequest);
+ Callable<Boolean> receivedLocation = new Callable<Boolean>() {
+ @Override
+ public Boolean call() {
+ return mJavascriptStatusReceiver.mHasPosition;
+ }
+ };
+ PollingCheck.check("JS didn't get position", POLLING_TIMEOUT, receivedLocation);
+ chromeClientAcceptAlways.mReceivedRequest = false;
+ mJavascriptStatusReceiver.clearState();
+ // Load the same URL again
+ loadUrlAndUpdateLocation(URL_1);
+ PollingCheck.check("JS didn't get position", POLLING_TIMEOUT, receivedLocation);
+ // Assert prompt for geolocation permission is not called the second time
+ assertFalse(chromeClientAcceptAlways.mReceivedRequest);
+ // Check that the permission is in GeolocationPermissions
+ ValueCheck<Boolean> trueCheck = new ValueCheck<Boolean>(true);
+ GeolocationPermissions.getInstance().getAllowed(URL_1, trueCheck);
+ trueCheck.run();
+ Set<String> acceptedOrigins = new TreeSet<String>();
+ acceptedOrigins.add(URL_1);
+ ValueCheck<Set<String>> originCheck = new ValueCheck<Set<String>>(acceptedOrigins);
+ GeolocationPermissions.getInstance().getOrigins(originCheck);
+ originCheck.run();
+
+ // URL_2 should get a prompt
+ chromeClientAcceptAlways.mReceivedRequest = false;
+ loadUrlAndUpdateLocation(URL_2);
+ // Checking the callback for geolocation permission prompt is called
+ PollingCheck.check("Geolocation prompt not called", POLLING_TIMEOUT, receivedRequest);
+ PollingCheck.check("JS didn't get position", POLLING_TIMEOUT, receivedLocation);
+ acceptedOrigins.add(URL_2);
+ originCheck = new ValueCheck<Set<String>>(acceptedOrigins);
+ GeolocationPermissions.getInstance().getOrigins(originCheck);
+ originCheck.run();
+ // Remove a domain manually that was added by the callback
+ GeolocationPermissions.getInstance().clear(URL_1);
+ acceptedOrigins.remove(URL_1);
+ originCheck = new ValueCheck<Set<String>>(acceptedOrigins);
+ GeolocationPermissions.getInstance().getOrigins(originCheck);
+ originCheck.run();
+ }
+
+ // Test the GeolocationPermissions API
+ public void testGeolocationPermissions() {
+ Set<String> acceptedOrigins = new TreeSet<String>();
+ ValueCheck<Boolean> falseCheck = new ValueCheck<Boolean>(false);
+ GeolocationPermissions.getInstance().getAllowed(URL_2, falseCheck);
+ falseCheck.run();
+ ValueCheck<Set<String>> originCheck = new ValueCheck<Set<String>>(acceptedOrigins);
+ GeolocationPermissions.getInstance().getOrigins(originCheck);
+ originCheck.run();
+
+ // Remove a domain that has not been allowed
+ GeolocationPermissions.getInstance().clear(URL_2);
+ acceptedOrigins.remove(URL_2);
+ originCheck = new ValueCheck<Set<String>>(acceptedOrigins);
+ GeolocationPermissions.getInstance().getOrigins(originCheck);
+ originCheck.run();
+
+ // Add a domain
+ acceptedOrigins.add(URL_2);
+ GeolocationPermissions.getInstance().allow(URL_2);
+ originCheck = new ValueCheck<Set<String>>(acceptedOrigins);
+ GeolocationPermissions.getInstance().getOrigins(originCheck);
+ originCheck.run();
+ ValueCheck<Boolean> trueCheck = new ValueCheck<Boolean>(true);
+ GeolocationPermissions.getInstance().getAllowed(URL_2, trueCheck);
+ trueCheck.run();
+
+ // Add a domain
+ acceptedOrigins.add(URL_1);
+ GeolocationPermissions.getInstance().allow(URL_1);
+ originCheck = new ValueCheck<Set<String>>(acceptedOrigins);
+ GeolocationPermissions.getInstance().getOrigins(originCheck);
+ originCheck.run();
+
+ // Remove a domain that has been allowed
+ GeolocationPermissions.getInstance().clear(URL_2);
+ acceptedOrigins.remove(URL_2);
+ originCheck = new ValueCheck<Set<String>>(acceptedOrigins);
+ GeolocationPermissions.getInstance().getOrigins(originCheck);
+ originCheck.run();
+ falseCheck = new ValueCheck<Boolean>(false);
+ GeolocationPermissions.getInstance().getAllowed(URL_2, falseCheck);
+ falseCheck.run();
+
+ // Try to clear all domains
+ GeolocationPermissions.getInstance().clearAll();
+ acceptedOrigins.clear();
+ originCheck = new ValueCheck<Set<String>>(acceptedOrigins);
+ GeolocationPermissions.getInstance().getOrigins(originCheck);
+ originCheck.run();
+
+ // Add a domain
+ acceptedOrigins.add(URL_1);
+ GeolocationPermissions.getInstance().allow(URL_1);
+ originCheck = new ValueCheck<Set<String>>(acceptedOrigins);
+ GeolocationPermissions.getInstance().getOrigins(originCheck);
+ originCheck.run();
+ }
+
+ // Test loading pages and checks rejecting once and recjecting the domain forever
+ public void testSimpleGeolocationRequestReject() throws Exception {
+ final TestSimpleGeolocationRequestWebChromeClient chromeClientRejectOnce =
+ new TestSimpleGeolocationRequestWebChromeClient(mOnUiThread, false, false);
+ mOnUiThread.setWebChromeClient(chromeClientRejectOnce);
+ // Load url once, and the callback should accept the domain for all future loads
+ mOnUiThread.loadUrlAndWaitForCompletion(URL_1);
+ Callable<Boolean> receivedRequest = new Callable<Boolean>() {
+ @Override
+ public Boolean call() {
+ return chromeClientRejectOnce.mReceivedRequest;
+ }
+ };
+ PollingCheck.check("Geolocation prompt not called", POLLING_TIMEOUT, receivedRequest);
+ Callable<Boolean> locationDenied = new Callable<Boolean>() {
+ @Override
+ public Boolean call() {
+ return mJavascriptStatusReceiver.mDenied;
+ }
+ };
+ PollingCheck.check("JS didn't get position", POLLING_TIMEOUT, locationDenied);
+ // Same result should happen on next run
+ mOnUiThread.loadUrlAndWaitForCompletion(URL_1);
+ PollingCheck.check("Geolocation prompt not called", POLLING_TIMEOUT, receivedRequest);
+ PollingCheck.check("JS didn't get position", POLLING_TIMEOUT, locationDenied);
+
+ // Try to reject forever
+ final TestSimpleGeolocationRequestWebChromeClient chromeClientRejectAlways =
+ new TestSimpleGeolocationRequestWebChromeClient(mOnUiThread, false, true);
+ mOnUiThread.setWebChromeClient(chromeClientRejectAlways);
+ mOnUiThread.loadUrlAndWaitForCompletion(URL_2);
+ PollingCheck.check("Geolocation prompt not called", POLLING_TIMEOUT, receivedRequest);
+ PollingCheck.check("JS didn't get position", POLLING_TIMEOUT, locationDenied);
+ // second load should now not get a prompt
+ chromeClientRejectAlways.mReceivedRequest = false;
+ mOnUiThread.loadUrlAndWaitForCompletion(URL_2);
+ PollingCheck.check("JS didn't get position", POLLING_TIMEOUT, locationDenied);
+ PollingCheck.check("Geolocation prompt not called", POLLING_TIMEOUT, receivedRequest);
+
+ // Test if it gets added to origins
+ Set<String> acceptedOrigins = new TreeSet<String>();
+ acceptedOrigins.add(URL_2);
+ ValueCheck<Set<String>> domainCheck = new ValueCheck<Set<String>>(acceptedOrigins);
+ GeolocationPermissions.getInstance().getOrigins(domainCheck);
+ domainCheck.run();
+ // And now check that getAllowed returns false
+ ValueCheck<Boolean> falseCheck = new ValueCheck<Boolean>(false);
+ GeolocationPermissions.getInstance().getAllowed(URL_1, falseCheck);
+ falseCheck.run();
+ }
+
+ // Object added to the page via AddJavascriptInterface() that is used by the test Javascript to
+ // notify back to Java when a location or error is received.
+ public final static class JavascriptStatusReceiver {
+ public volatile boolean mHasPosition = false;
+ public volatile boolean mDenied = false;
+ public volatile boolean mUnavailable = false;
+ public volatile boolean mTimeout = false;
+
+ public void clearState() {
+ mHasPosition = false;
+ mDenied = false;
+ mUnavailable = false;
+ mTimeout = false;
+ }
+
+ @JavascriptInterface
+ public void errorDenied() {
+ mDenied = true;
+ }
+
+ @JavascriptInterface
+ public void errorUnavailable() {
+ mUnavailable = true;
+ }
+
+ @JavascriptInterface
+ public void errorTimeout() {
+ mTimeout = true;
+ }
+
+ @JavascriptInterface
+ public void gotLocation() {
+ mHasPosition = true;
+ }
+ }
+}
diff --git a/tests/tests/webkit/src/android/webkit/cts/WebHistoryItemTest.java b/tests/tests/webkit/src/android/webkit/cts/WebHistoryItemTest.java
index 4f2113b..2eb81fc 100644
--- a/tests/tests/webkit/src/android/webkit/cts/WebHistoryItemTest.java
+++ b/tests/tests/webkit/src/android/webkit/cts/WebHistoryItemTest.java
@@ -55,7 +55,6 @@
assertEquals(1, list.getSize());
WebHistoryItem item = list.getCurrentItem();
assertNotNull(item);
- int firstId = item.getId();
assertEquals(url, item.getUrl());
assertEquals(url, item.getOriginalUrl());
assertEquals(TestHtmlConstants.HELLO_WORLD_TITLE, item.getTitle());
@@ -69,7 +68,5 @@
item = list.getCurrentItem();
assertNotNull(item);
assertEquals(TestHtmlConstants.BR_TAG_TITLE, item.getTitle());
- int secondId = item.getId();
- assertTrue(firstId != secondId);
}
}
diff --git a/tests/tests/webkit/src/android/webkit/cts/WebSettingsTest.java b/tests/tests/webkit/src/android/webkit/cts/WebSettingsTest.java
index 054217e..73a2d40 100644
--- a/tests/tests/webkit/src/android/webkit/cts/WebSettingsTest.java
+++ b/tests/tests/webkit/src/android/webkit/cts/WebSettingsTest.java
@@ -155,47 +155,6 @@
assertEquals(customUserAgent, mOnUiThread.getTitle());
}
- @SuppressWarnings("deprecation")
- public void testAccessUserAgent() throws Exception {
- startWebServer();
- String url = mWebServer.getUserAgentUrl();
-
- mSettings.setUserAgent(1);
- assertEquals(1, mSettings.getUserAgent());
- mOnUiThread.loadUrlAndWaitForCompletion(url);
- String userAgent1 = mOnUiThread.getTitle();
- assertNotNull(userAgent1);
-
- mSettings.setUserAgent(3);
- assertEquals(1, mSettings.getUserAgent());
- mOnUiThread.loadUrlAndWaitForCompletion(url);
- assertEquals(userAgent1, mOnUiThread.getTitle());
-
- mSettings.setUserAgent(2);
- assertEquals(2, mSettings.getUserAgent());
- mOnUiThread.loadUrlAndWaitForCompletion(url);
- String userAgent2 = mOnUiThread.getTitle();
- assertNotNull(userAgent2);
-
- mSettings.setUserAgent(3);
- assertEquals(2, mSettings.getUserAgent());
- mOnUiThread.loadUrlAndWaitForCompletion(url);
- assertEquals(userAgent2, mOnUiThread.getTitle());
-
- mSettings.setUserAgent(0);
- assertEquals(0, mSettings.getUserAgent());
- mOnUiThread.loadUrlAndWaitForCompletion(url);
- String userAgent0 = mOnUiThread.getTitle();
- assertNotNull(userAgent0);
-
- final String customUserAgent = "Cts/Test";
- mSettings.setUserAgentString(customUserAgent);
- assertEquals(-1, mSettings.getUserAgent());
- mOnUiThread.loadUrlAndWaitForCompletion(url);
- assertEquals(customUserAgent, mOnUiThread.getTitle());
- }
-
-
public void testAccessAllowFileAccess() {
// This test is not compatible with 4.0.3
if ("4.0.3".equals(Build.VERSION.RELEASE)) {
@@ -387,11 +346,9 @@
new PollingCheck(WEBVIEW_TIMEOUT) {
@Override
protected boolean check() {
- String title = mOnUiThread.getTitle();
- return title != null && title.length() > 0;
+ return "Popup blocked".equals(mOnUiThread.getTitle());
}
}.run();
- assertEquals("Popup blocked", mOnUiThread.getTitle());
mSettings.setJavaScriptCanOpenWindowsAutomatically(true);
assertTrue(mSettings.getJavaScriptCanOpenWindowsAutomatically());
@@ -399,15 +356,9 @@
new PollingCheck(WEBVIEW_TIMEOUT) {
@Override
protected boolean check() {
- String title = mOnUiThread.getTitle();
- // The title may not change immediately after loading, so
- // we have to discount the initial "Popup blocked" from the
- // previous load.
- return title != null && title.length() > 0
- && !title.equals("Popup blocked");
+ return "Popup allowed".equals(mOnUiThread.getTitle());
}
}.run();
- assertEquals("Popup allowed", mOnUiThread.getTitle());
}
public void testAccessJavaScriptEnabled() throws Exception {
@@ -417,10 +368,9 @@
new PollingCheck(WEBVIEW_TIMEOUT) {
@Override
protected boolean check() {
- return mOnUiThread.getTitle() != null;
+ return "javascript on".equals(mOnUiThread.getTitle());
}
}.run();
- assertEquals("javascript on", mOnUiThread.getTitle());
mSettings.setJavaScriptEnabled(false);
assertFalse(mSettings.getJavaScriptEnabled());
@@ -428,10 +378,10 @@
new PollingCheck(WEBVIEW_TIMEOUT) {
@Override
protected boolean check() {
- return mOnUiThread.getTitle() != null;
+ return "javascript off".equals(mOnUiThread.getTitle());
}
}.run();
- assertEquals("javascript off", mOnUiThread.getTitle());
+
}
public void testAccessLayoutAlgorithm() {
@@ -444,13 +394,6 @@
assertEquals(WebSettings.LayoutAlgorithm.SINGLE_COLUMN, mSettings.getLayoutAlgorithm());
}
- public void testAccessLightTouchEnabled() {
- assertFalse(mSettings.getLightTouchEnabled());
-
- mSettings.setLightTouchEnabled(true);
- assertTrue(mSettings.getLightTouchEnabled());
- }
-
public void testAccessMinimumFontSize() {
assertEquals(8, mSettings.getMinimumFontSize());
@@ -477,13 +420,6 @@
assertEquals(10, mSettings.getMinimumLogicalFontSize());
}
- public void testAccessNavDump() {
- assertFalse(mSettings.getNavDump());
-
- mSettings.setNavDump(true);
- assertTrue(mSettings.getNavDump());
- }
-
public void testAccessPluginsEnabled() {
assertFalse(mSettings.getPluginsEnabled());
@@ -507,13 +443,6 @@
assertFalse(mSettings.getSaveFormData());
}
- public void testAccessSavePassword() {
- assertTrue(mSettings.getSavePassword());
-
- mSettings.setSavePassword(false);
- assertFalse(mSettings.getSavePassword());
- }
-
public void testAccessTextSize() {
assertEquals(TextSize.NORMAL, mSettings.getTextSize());
@@ -727,7 +656,7 @@
mOnUiThread.clearCache(true);
mOnUiThread.loadUrlAndWaitForCompletion(
mWebServer.getAssetUrl(TestHtmlConstants.HELLO_WORLD_URL));
- assertEquals(TestHtmlConstants.WEBPAGE_NOT_AVAILABLE_TITLE, mOnUiThread.getTitle());
+ assertFalse(TestHtmlConstants.HELLO_WORLD_TITLE.equals(mOnUiThread.getTitle()));
mOnUiThread.loadDataAndWaitForCompletion(getNetworkImageHtml(), "text/html", null);
assertEquals(EMPTY_IMAGE_HEIGHT, mOnUiThread.getTitle());
mOnUiThread.loadDataAndWaitForCompletion(DATA_URL_IMAGE_HTML, "text/html", null);
diff --git a/tests/tests/webkit/src/android/webkit/cts/WebViewTest.java b/tests/tests/webkit/src/android/webkit/cts/WebViewTest.java
index 6c66f52..eff9640 100755
--- a/tests/tests/webkit/src/android/webkit/cts/WebViewTest.java
+++ b/tests/tests/webkit/src/android/webkit/cts/WebViewTest.java
@@ -33,6 +33,8 @@
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
+import android.os.StrictMode;
+import android.os.StrictMode.ThreadPolicy;
import android.os.SystemClock;
import android.test.ActivityInstrumentationTestCase2;
import android.test.UiThreadTest;
@@ -41,8 +43,6 @@
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
-import android.webkit.CacheManager;
-import android.webkit.CacheManager.CacheResult;
import android.webkit.ConsoleMessage;
import android.webkit.DownloadListener;
import android.webkit.JavascriptInterface;
@@ -124,6 +124,18 @@
mWebServer = new CtsTestServer(getActivity(), secure);
}
+ private void stopWebServer() throws Exception {
+ assertNotNull(mWebServer);
+ ThreadPolicy oldPolicy = StrictMode.getThreadPolicy();
+ ThreadPolicy tmpPolicy = new ThreadPolicy.Builder(oldPolicy)
+ .permitNetwork()
+ .build();
+ StrictMode.setThreadPolicy(tmpPolicy);
+ mWebServer.shutdown();
+ mWebServer = null;
+ StrictMode.setThreadPolicy(oldPolicy);
+ }
+
@UiThreadTest
public void testConstructor() {
new WebView(getActivity());
@@ -138,23 +150,11 @@
* http://en.wikipedia.org/wiki/Postal_address#United_States
* http://www.usps.com/
*/
- // full address, invalid zip code
- assertNull(WebView.findAddress("455 LARKSPUR DRIVE CALIFORNIA SPRINGS CALIFORNIA 92926"));
// full address
assertEquals("455 LARKSPUR DRIVE CALIFORNIA SPRINGS CALIFORNIA 92826",
WebView.findAddress("455 LARKSPUR DRIVE CALIFORNIA SPRINGS CALIFORNIA 92826"));
- // full address ( with abbreviated street type and state)
- assertEquals("455 LARKSPUR DR CALIFORNIA SPRINGS CA 92826",
- WebView.findAddress("455 LARKSPUR DR CALIFORNIA SPRINGS CA 92826"));
- // misspell the state ( CALIFORNIA -> CALIFONIA )
- assertNull(WebView.findAddress("455 LARKSPUR DRIVE CALIFORNIA SPRINGS CALIFONIA 92826"));
- // without optional zip code
- assertEquals("455 LARKSPUR DR CALIFORNIA SPRINGS CA",
- WebView.findAddress("455 LARKSPUR DR CALIFORNIA SPRINGS CA"));
- // house number, street name and street type are missing
- assertNull(WebView.findAddress("CALIFORNIA SPRINGS CA"));
- // city & state are missing
- assertNull(WebView.findAddress("455 LARKSPUR DR"));
+ // not an address
+ assertNull(WebView.findAddress("This is not an address: no town, no state, no zip."));
}
@SuppressWarnings("deprecation")
@@ -619,66 +619,6 @@
}.run();
}
- public void testSaveAndRestorePicture() throws Throwable {
- mWebView.setBackgroundColor(Color.CYAN);
- startWebServer(false);
- final String url = mWebServer.getAssetUrl(TestHtmlConstants.BLANK_PAGE_URL);
- mOnUiThread.loadUrlAndWaitForCompletion(url);
- getInstrumentation().waitForIdleSync();
-
- final Bundle bundle = new Bundle();
- final File f = getActivity().getFileStreamPath("snapshot");
- if (f.exists()) {
- f.delete();
- }
-
- try {
- assertTrue(bundle.isEmpty());
- assertEquals(0, f.length());
- assertTrue(mOnUiThread.savePicture(bundle, f));
-
- // File saving is done in a separate thread.
- new PollingCheck() {
- @Override
- protected boolean check() {
- return f.length() > 0;
- }
- }.run();
-
- assertFalse(bundle.isEmpty());
-
- Picture p = Picture.createFromStream(new FileInputStream(f));
- Bitmap b = Bitmap.createBitmap(p.getWidth(), p.getHeight(), Config.ARGB_8888);
- p.draw(new Canvas(b));
- assertBitmapFillWithColor(b, Color.CYAN);
-
- mOnUiThread.setBackgroundColor(Color.WHITE);
- mOnUiThread.reloadAndWaitForCompletion();
- getInstrumentation().waitForIdleSync();
-
- runTestOnUiThread(new Runnable() {
- @Override
- public void run() {
- Bitmap b = Bitmap.createBitmap(mWebView.getWidth(), mWebView.getHeight(),
- Config.ARGB_8888);
- mWebView.draw(new Canvas(b));
- assertBitmapFillWithColor(b, Color.WHITE);
-
- // restorePicture is only supported in software rendering
- mWebView.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
- assertTrue(mWebView.restorePicture(bundle, f));
- }
- });
- getInstrumentation().waitForIdleSync();
- // Cannot test whether the picture has been restored successfully.
- // Drawing the webview into a canvas will draw white, but on the display it is cyan
- } finally {
- if (f.exists()) {
- f.delete();
- }
- }
- }
-
@UiThreadTest
public void testAccessHttpAuthUsernamePassword() {
try {
@@ -741,28 +681,10 @@
}
}
- @UiThreadTest
- public void testSavePassword() {
- WebViewDatabase db = WebViewDatabase.getInstance(getActivity());
- try {
- db.clearUsernamePassword();
-
- String host = "http://localhost:8080";
- String userName = "user";
- String password = "password";
- assertFalse(db.hasUsernamePassword());
- mWebView.savePassword(host, userName, password);
- assertTrue(db.hasUsernamePassword());
- } finally {
- db.clearUsernamePassword();
- }
- }
-
public void testLoadData() throws Throwable {
final String HTML_CONTENT =
"<html><head><title>Hello,World!</title></head><body></body>" +
"</html>";
- assertNull(mOnUiThread.getTitle());
mOnUiThread.loadDataAndWaitForCompletion(HTML_CONTENT,
"text/html", null);
assertEquals("Hello,World!", mOnUiThread.getTitle());
@@ -788,7 +710,6 @@
@UiThreadTest
public void testLoadDataWithBaseUrl() throws Throwable {
- assertNull(mWebView.getTitle());
assertNull(mWebView.getUrl());
String imgUrl = TestHtmlConstants.SMALL_IMG_URL; // relative
@@ -1024,40 +945,6 @@
}
@UiThreadTest
- public void testClearCache() throws Exception {
- final File cacheFileBaseDir = CacheManager.getCacheFileBaseDir();
- mWebView.clearCache(true);
- assertEquals(0, cacheFileBaseDir.list().length);
-
- startWebServer(false);
- final String url = mWebServer.getAssetUrl(TestHtmlConstants.HELLO_WORLD_URL);
- mOnUiThread.loadUrlAndWaitForCompletion(url);
- new PollingCheck(TEST_TIMEOUT) {
- @Override
- protected boolean check() {
- CacheResult result = CacheManager.getCacheFile(url, null);
- return result != null;
- }
- }.run();
- int cacheFileCount = cacheFileBaseDir.list().length;
- assertTrue(cacheFileCount > 0);
-
- mWebView.clearCache(false);
- // the cache files are still there
- // can not check other effects of the method
- assertEquals(cacheFileCount, cacheFileBaseDir.list().length);
-
- mWebView.clearCache(true);
- // check the files are deleted
- new PollingCheck(TEST_TIMEOUT) {
- @Override
- protected boolean check() {
- return cacheFileBaseDir.list().length == 0;
- }
- }.run();
- }
-
- @UiThreadTest
public void testPlatformNotifications() {
WebView.enablePlatformNotifications();
WebView.disablePlatformNotifications();
@@ -1123,7 +1010,7 @@
// focus on first link
handler.reset();
- getInstrumentation().sendKeyDownUpSync(KeyEvent.KEYCODE_DPAD_DOWN);
+ getInstrumentation().sendKeyDownUpSync(KeyEvent.KEYCODE_TAB);
mOnUiThread.requestFocusNodeHref(hrefMsg);
new PollingCheck() {
@Override
@@ -1148,7 +1035,7 @@
handler.reset();
final Message hrefMsg2 = new Message();
hrefMsg2.setTarget(handler);
- getInstrumentation().sendKeyDownUpSync(KeyEvent.KEYCODE_DPAD_DOWN);
+ getInstrumentation().sendKeyDownUpSync(KeyEvent.KEYCODE_TAB);
mOnUiThread.requestFocusNodeHref(hrefMsg2);
new PollingCheck() {
@Override
@@ -1475,21 +1362,30 @@
}
@UiThreadTest
- public void testSetAndGetCertificate() {
- assertNull(mWebView.getCertificate());
- SslCertificate certificate = new SslCertificate("foo", "bar", new Date(42), new Date(43));
- mWebView.setCertificate(certificate);
- assertEquals(certificate, mWebView.getCertificate());
- }
-
- @UiThreadTest
public void testInsecureSiteClearsCertificate() throws Throwable {
- final SslCertificate certificate =
- new SslCertificate("foo", "bar", new Date(42), new Date(43));
+ final class MockWebViewClient extends WaitForLoadedClient {
+ public MockWebViewClient() {
+ super(mOnUiThread);
+ }
+ @Override
+ public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
+ handler.proceed();
+ }
+ }
+
+ startWebServer(true);
+ mOnUiThread.setWebViewClient(new MockWebViewClient());
+ mOnUiThread.loadUrlAndWaitForCompletion(
+ mWebServer.getAssetUrl(TestHtmlConstants.HELLO_WORLD_URL));
+ SslCertificate cert = mWebView.getCertificate();
+ assertNotNull(cert);
+ assertEquals("Android", cert.getIssuedTo().getUName());
+
+ stopWebServer();
+
startWebServer(false);
- final String url = mWebServer.getAssetUrl(TestHtmlConstants.HELLO_WORLD_URL);
- mWebView.setCertificate(certificate);
- mOnUiThread.loadUrlAndWaitForCompletion(url);
+ mOnUiThread.loadUrlAndWaitForCompletion(
+ mWebServer.getAssetUrl(TestHtmlConstants.HELLO_WORLD_URL));
assertNull(mWebView.getCertificate());
}
@@ -1505,11 +1401,17 @@
}
}
+ startWebServer(false);
+ mOnUiThread.loadUrlAndWaitForCompletion(
+ mWebServer.getAssetUrl(TestHtmlConstants.HELLO_WORLD_URL));
+ assertNull(mWebView.getCertificate());
+
+ stopWebServer();
+
startWebServer(true);
- final String url = mWebServer.getAssetUrl(TestHtmlConstants.HELLO_WORLD_URL);
mOnUiThread.setWebViewClient(new MockWebViewClient());
- mWebView.setCertificate(null);
- mOnUiThread.loadUrlAndWaitForCompletion(url);
+ mOnUiThread.loadUrlAndWaitForCompletion(
+ mWebServer.getAssetUrl(TestHtmlConstants.HELLO_WORLD_URL));
SslCertificate cert = mWebView.getCertificate();
assertNotNull(cert);
assertEquals("Android", cert.getIssuedTo().getUName());
@@ -1685,15 +1587,13 @@
// WebViewClient.shouldOverrideUrlLoading() returns false, so
// the WebView will load the new URL.
mOnUiThread.setDownloadListener(listener);
+ mWebView.getSettings().setJavaScriptEnabled(true);
mOnUiThread.loadDataAndWaitForCompletion(
- "<html><body><a href=\"" + url
- + "\">link</a></body></html>",
+ "<html><body onload=\"window.location = \'" + url + "\'\"></body></html>",
"text/html", null);
// Wait for layout to complete before setting focus.
getInstrumentation().waitForIdleSync();
- assertTrue(mOnUiThread.requestFocus(View.FOCUS_DOWN, null));
- getInstrumentation().waitForIdleSync();
- getInstrumentation().sendKeyDownUpSync(KeyEvent.KEYCODE_DPAD_CENTER);
+
new PollingCheck(TEST_TIMEOUT) {
@Override
protected boolean check() {
@@ -1887,7 +1787,7 @@
private void moveFocusDown() throws Throwable {
// send down key and wait for idle
- sendKeys(KeyEvent.KEYCODE_DPAD_DOWN);
+ getInstrumentation().sendKeyDownUpSync(KeyEvent.KEYCODE_TAB);
// waiting for idle isn't always sufficient for the key to be fully processed
Thread.sleep(500);
}
diff --git a/tests/tests/webkit/src/android/webkit/cts/WebView_WebViewTransportTest.java b/tests/tests/webkit/src/android/webkit/cts/WebView_WebViewTransportTest.java
index eaa5e69..dd784be 100644
--- a/tests/tests/webkit/src/android/webkit/cts/WebView_WebViewTransportTest.java
+++ b/tests/tests/webkit/src/android/webkit/cts/WebView_WebViewTransportTest.java
@@ -16,16 +16,22 @@
package android.webkit.cts;
-import android.test.AndroidTestCase;
+import android.test.ActivityInstrumentationTestCase2;
import android.test.UiThreadTest;
import android.webkit.WebView;
import android.webkit.WebView.WebViewTransport;
-public class WebView_WebViewTransportTest extends AndroidTestCase {
+public class WebView_WebViewTransportTest
+ extends ActivityInstrumentationTestCase2<WebViewStubActivity> {
+
+ public WebView_WebViewTransportTest() {
+ super("com.android.cts.stub", WebViewStubActivity.class);
+ }
+
@UiThreadTest
public void testAccessWebView() {
- WebView webView = new WebView(mContext);
+ WebView webView = getActivity().getWebView();
WebViewTransport transport = webView.new WebViewTransport();
assertNull(transport.getWebView());
diff --git a/tests/tests/widget/src/android/widget/cts/GridLayoutTest.java b/tests/tests/widget/src/android/widget/cts/GridLayoutTest.java
new file mode 100644
index 0000000..638fe96
--- /dev/null
+++ b/tests/tests/widget/src/android/widget/cts/GridLayoutTest.java
@@ -0,0 +1,263 @@
+/*
+ * Copyright (C) 2012 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.content.Context;
+import android.test.ActivityInstrumentationTestCase;
+import android.util.AttributeSet;
+import android.util.Xml;
+import android.view.Gravity;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AbsoluteLayout;
+import android.widget.Button;
+import android.widget.GridLayout;
+import android.widget.TextView;
+import com.android.cts.stub.R;
+import org.xmlpull.v1.XmlPullParser;
+
+import static android.view.ViewGroup.LAYOUT_MODE_OPTICAL_BOUNDS;
+import static android.widget.GridLayout.spec;
+
+/**
+ * Test {@link android.widget.GridLayout}.
+ */
+public class GridLayoutTest extends ActivityInstrumentationTestCase<GridLayoutStubActivity> {
+
+ // The size of the off-screen test container in which we we will testing layout.
+ public static final int MAX_X = 2000;
+ public static final int MAX_Y = 2000;
+
+ private static abstract class Alignment {
+ String name;
+ int gravity;
+
+ abstract int getValue(View v);
+
+ protected Alignment(String name, int gravity) {
+ this.name = name;
+ this.gravity = gravity;
+ }
+ }
+
+ private static final Alignment[] HORIZONTAL_ALIGNMENTS = {
+ new Alignment("LEFT", Gravity.LEFT) {
+ @Override
+ int getValue(View v) {
+ return v.getLeft();
+ }
+ },
+ new Alignment("CENTER", Gravity.CENTER_HORIZONTAL) {
+ @Override
+ int getValue(View v) {
+ return (v.getLeft() + v.getRight()) / 2;
+ }
+ },
+ new Alignment("RIGHT", Gravity.RIGHT) {
+ @Override
+ int getValue(View v) {
+ return v.getRight();
+ }
+ },
+ new Alignment("FILL", Gravity.FILL_HORIZONTAL) {
+ @Override
+ int getValue(View v) {
+ return v.getWidth();
+ }
+ }
+ };
+
+ private static final Alignment[] VERTICAL_ALIGNMENTS = {
+ new Alignment("TOP", Gravity.TOP) {
+ @Override
+ int getValue(View v) {
+ return v.getTop();
+ }
+ },
+ new Alignment("CENTER", Gravity.CENTER_VERTICAL) {
+ @Override
+ int getValue(View v) {
+ return (v.getTop() + v.getBottom()) / 2;
+ }
+ },
+ new Alignment("BASELINE", Gravity.NO_GRAVITY) {
+ @Override
+ int getValue(View v) {
+ return v.getTop() + v.getBaseline();
+ }
+ },
+ new Alignment("BOTTOM", Gravity.BOTTOM) {
+ @Override
+ int getValue(View v) {
+ return v.getBottom();
+ }
+ },
+ new Alignment("FILL", Gravity.FILL_VERTICAL) {
+ @Override
+ int getValue(View v) {
+ return v.getHeight();
+ }
+ }
+ };
+
+ private Context mContext;
+
+ public GridLayoutTest() {
+ super("com.android.cts.stub", GridLayoutStubActivity.class);
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ mContext = getInstrumentation().getTargetContext();
+ }
+
+ public void testConstructor() {
+ new GridLayout(mContext);
+
+ new GridLayout(mContext, null);
+
+ XmlPullParser parser = mContext.getResources().getXml(R.layout.gridlayout_layout);
+ AttributeSet attrs = Xml.asAttributeSet(parser);
+ new GridLayout(mContext, attrs);
+
+ try {
+ new GridLayout(null, null);
+ fail("should throw NullPointerException.");
+ } catch (NullPointerException e) {
+ }
+ }
+
+ public void testCheckLayoutParams() {
+ GridLayout gridLayout = new GridLayout(mContext);
+
+ gridLayout.addView(new TextView(mContext), new AbsoluteLayout.LayoutParams(0, 0, 0, 0));
+
+ gridLayout.addView(new TextView(mContext), new GridLayout.LayoutParams(
+ GridLayout.spec(0),
+ GridLayout.spec(0)));
+
+ }
+
+ public void testGenerateDefaultLayoutParams() {
+ GridLayout gridLayout = new GridLayout(mContext);
+ ViewGroup.LayoutParams lp = gridLayout.generateLayoutParams(null);
+ assertNotNull(lp);
+ assertTrue(lp instanceof GridLayout.LayoutParams);
+ assertEquals(ViewGroup.LayoutParams.WRAP_CONTENT, lp.width);
+ assertEquals(ViewGroup.LayoutParams.WRAP_CONTENT, lp.height);
+ }
+
+ private View[][] populate(GridLayout container) {
+ Context context = container.getContext();
+ int N = VERTICAL_ALIGNMENTS.length;
+ int M = HORIZONTAL_ALIGNMENTS.length;
+
+ View[][] table = new View[N + 1][M + 1];
+
+ {
+ TextView v = new TextView(context);
+ GridLayout.LayoutParams lp = new GridLayout.LayoutParams(spec(0), spec(0));
+ lp.setGravity(Gravity.CENTER);
+ v.setText("*");
+ container.addView(v, lp);
+ }
+ for (int i = 0; i < N; i++) {
+ Alignment va = VERTICAL_ALIGNMENTS[i];
+ int row = i + 1;
+ GridLayout.LayoutParams lp = new GridLayout.LayoutParams(spec(row), spec(0));
+ lp.setGravity(va.gravity | Gravity.CENTER_HORIZONTAL);
+ TextView v = new TextView(context);
+ v.setGravity(Gravity.CENTER);
+ v.setText(va.name);
+ container.addView(v, lp);
+ table[row][0] = v;
+ }
+ for (int j = 0; j < M; j++) {
+ Alignment ha = HORIZONTAL_ALIGNMENTS[j];
+ int col = j + 1;
+ GridLayout.LayoutParams lp = new GridLayout.LayoutParams(spec(0), spec(col));
+ lp.setGravity(Gravity.CENTER_VERTICAL | ha.gravity);
+ TextView v = new TextView(context);
+ v.setGravity(Gravity.CENTER);
+ v.setText(ha.name);
+ container.addView(v, lp);
+ table[0][col] = v;
+ }
+ for (int i = 0; i < N; i++) {
+ for (int j = 0; j < M; j++) {
+ Alignment ha = HORIZONTAL_ALIGNMENTS[j];
+ Alignment va = VERTICAL_ALIGNMENTS[i];
+ int row = i + 1;
+ int col = j + 1;
+ GridLayout.LayoutParams lp = new GridLayout.LayoutParams(spec(row), spec(col));
+ lp.setGravity(va.gravity | ha.gravity);
+ TextView v = new Button(context);
+ v.setText("X");
+ v.setTextSize(10 + 5 * row * col);
+ container.addView(v, lp);
+ table[row][col] = v;
+ }
+ }
+ return table;
+ }
+
+ private void testAlignment(int row, int col, Alignment a, View v0, View v1, String group) {
+ int a0 = a.getValue(v0);
+ int a1 = a.getValue(v1);
+ assertEquals("View at row " + row + ", column " + col + " was not " + a.name +
+ " aligned with its title " + group + ";",
+ a0, a1);
+ }
+
+ private void test(GridLayout p, View[][] table) {
+ p.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
+ p.layout(0, 0, MAX_X, MAX_Y);
+
+ int N = VERTICAL_ALIGNMENTS.length;
+ int M = HORIZONTAL_ALIGNMENTS.length;
+
+ // test all horizontal alignments in each column
+ for (int j = 0; j < M; j++) {
+ int col = j + 1;
+ View v0 = table[0][col];
+ Alignment alignment = HORIZONTAL_ALIGNMENTS[j];
+ for (int i = 0; i < N; i++) {
+ int row = i + 1;
+ testAlignment(row, col, alignment, v0, table[row][col], "column");
+ }
+ }
+
+ // test all vertical alignments in each row
+ for (int i = 0; i < N; i++) {
+ int row = i + 1;
+ View v0 = table[row][0];
+ Alignment alignment = VERTICAL_ALIGNMENTS[i];
+ for (int j = 0; j < M; j++) {
+ int col = j + 1;
+ testAlignment(row, col, alignment, v0, table[row][col], "row");
+ }
+ }
+ }
+ public void testAlignment() {
+ GridLayout p = new GridLayout(mContext);
+ View[][] table = populate(p);
+ test(p, table);
+ //p.setLayoutMode(ViewGroup.LAYOUT_MODE_OPTICAL_BOUNDS);
+ //test(p, table);
+ }
+}
diff --git a/tests/tests/widget/src/android/widget/cts/ViewFlipperTest.java b/tests/tests/widget/src/android/widget/cts/ViewFlipperTest.java
index bcb417a..f223466 100644
--- a/tests/tests/widget/src/android/widget/cts/ViewFlipperTest.java
+++ b/tests/tests/widget/src/android/widget/cts/ViewFlipperTest.java
@@ -93,6 +93,7 @@
// wait for a longer time to make sure the view flipping is completed.
waitForViewFlipping(FLIP_INTERVAL + 200);
+ getInstrumentation().waitForIdleSync();
runTestOnUiThread(new Runnable() {
public void run() {
ViewFlipper viewFlipper =
@@ -108,6 +109,7 @@
});
waitForViewFlipping(FLIP_INTERVAL + 200);
+ getInstrumentation().waitForIdleSync();
runTestOnUiThread(new Runnable() {
public void run() {
ViewFlipper viewFlipper =
diff --git a/tests/uiautomator/src/com/android/cts/uiautomatortest/CtsUiAutomatorTest.java b/tests/uiautomator/src/com/android/cts/uiautomatortest/CtsUiAutomatorTest.java
index 5de9805..73bab27 100644
--- a/tests/uiautomator/src/com/android/cts/uiautomatortest/CtsUiAutomatorTest.java
+++ b/tests/uiautomator/src/com/android/cts/uiautomatortest/CtsUiAutomatorTest.java
@@ -343,6 +343,7 @@
Rect tb = textView.getBounds();
UiDevice.getInstance().swipe(tb.right - 20, tb.centerY(), tb.left + 20, tb.centerY(), 50);
+ SystemClock.sleep(100);
assertTrue("UiDevice swipe", "[ 2 ]".equals(textView.getText()));
}
diff --git a/suite/pts/tools/Android.mk b/tests/uiautomator/test-apps/Android.mk
similarity index 98%
rename from suite/pts/tools/Android.mk
rename to tests/uiautomator/test-apps/Android.mk
index c141484..e790e1e 100644
--- a/suite/pts/tools/Android.mk
+++ b/tests/uiautomator/test-apps/Android.mk
@@ -1,4 +1,3 @@
-#
# Copyright (C) 2012 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -12,6 +11,5 @@
# 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.
-#
include $(call all-subdir-makefiles)
diff --git a/tests/uiautomator/test-apps/CtsUiAutomatorApp/Android.mk b/tests/uiautomator/test-apps/CtsUiAutomatorApp/Android.mk
index e30816a..fe4342a 100644
--- a/tests/uiautomator/test-apps/CtsUiAutomatorApp/Android.mk
+++ b/tests/uiautomator/test-apps/CtsUiAutomatorApp/Android.mk
@@ -1,4 +1,3 @@
-#
# Copyright (C) 2012 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -12,7 +11,6 @@
# 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)
@@ -31,4 +29,3 @@
LOCAL_PROGUARD_ENABLED := disabled
include $(BUILD_PACKAGE)
-
diff --git a/tests/uiautomator/test-apps/CtsUiAutomatorApp/AndroidManifest.xml b/tests/uiautomator/test-apps/CtsUiAutomatorApp/AndroidManifest.xml
index 9b3d8b3..87c2d82 100644
--- a/tests/uiautomator/test-apps/CtsUiAutomatorApp/AndroidManifest.xml
+++ b/tests/uiautomator/test-apps/CtsUiAutomatorApp/AndroidManifest.xml
@@ -1,5 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
-
<!--
* Copyright (C) 2011 The Android Open Source Project
*
@@ -44,6 +43,6 @@
android:name="android.support.PARENT_ACTIVITY"
android:value="FragmentActivity" />
</activity>
- </application>
+ </application>
</manifest>
diff --git a/tests/uiautomator/test-apps/CtsUiAutomatorApp/res/layout/activity_main.xml b/tests/uiautomator/test-apps/CtsUiAutomatorApp/res/layout/activity_main.xml
index f0e3bc9..72b68f3 100644
--- a/tests/uiautomator/test-apps/CtsUiAutomatorApp/res/layout/activity_main.xml
+++ b/tests/uiautomator/test-apps/CtsUiAutomatorApp/res/layout/activity_main.xml
@@ -28,4 +28,4 @@
android:layout_centerVertical="true"
android:text="@string/hello_world" />
-</RelativeLayout>
\ No newline at end of file
+</RelativeLayout>
diff --git a/tests/uiautomator/test-apps/CtsUiAutomatorApp/res/menu/activity_main.xml b/tests/uiautomator/test-apps/CtsUiAutomatorApp/res/menu/activity_main.xml
index 73c4db8..c54c287 100644
--- a/tests/uiautomator/test-apps/CtsUiAutomatorApp/res/menu/activity_main.xml
+++ b/tests/uiautomator/test-apps/CtsUiAutomatorApp/res/menu/activity_main.xml
@@ -23,4 +23,4 @@
android:showAsAction="never"
android:title="@string/menu_settings"/>
-</menu>
\ No newline at end of file
+</menu>
diff --git a/tests/uiautomator/test-apps/CtsUiAutomatorApp/res/values/strings.xml b/tests/uiautomator/test-apps/CtsUiAutomatorApp/res/values/strings.xml
index 78b829f..db2450a 100644
--- a/tests/uiautomator/test-apps/CtsUiAutomatorApp/res/values/strings.xml
+++ b/tests/uiautomator/test-apps/CtsUiAutomatorApp/res/values/strings.xml
@@ -1,5 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
-
<!--
* Copyright (C) 2011 The Android Open Source Project
*
diff --git a/tests/uiautomator/test-apps/CtsUiAutomatorApp/res/values/styles.xml b/tests/uiautomator/test-apps/CtsUiAutomatorApp/res/values/styles.xml
index 119c259..bce88ae 100644
--- a/tests/uiautomator/test-apps/CtsUiAutomatorApp/res/values/styles.xml
+++ b/tests/uiautomator/test-apps/CtsUiAutomatorApp/res/values/styles.xml
@@ -19,4 +19,4 @@
<style name="AppTheme" parent="android:Theme.Light" />
-</resources>
\ No newline at end of file
+</resources>
diff --git a/tests/uiautomator/test-apps/CtsUiAutomatorApp/src/com/android/cts/uiautomator/MainActivity.java b/tests/uiautomator/test-apps/CtsUiAutomatorApp/src/com/android/cts/uiautomator/MainActivity.java
index 8d4f144..81833b7 100644
--- a/tests/uiautomator/test-apps/CtsUiAutomatorApp/src/com/android/cts/uiautomator/MainActivity.java
+++ b/tests/uiautomator/test-apps/CtsUiAutomatorApp/src/com/android/cts/uiautomator/MainActivity.java
@@ -13,7 +13,6 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
package com.android.cts.uiautomator;
import android.content.Intent;
diff --git a/tools/cts-java-scanner/src/com/android/cts/javascanner/DocletRunner.java b/tools/cts-java-scanner/src/com/android/cts/javascanner/DocletRunner.java
index ba87156..25a4ec1 100644
--- a/tools/cts-java-scanner/src/com/android/cts/javascanner/DocletRunner.java
+++ b/tools/cts-java-scanner/src/com/android/cts/javascanner/DocletRunner.java
@@ -72,6 +72,7 @@
// PTS adds PtsAndroidTestCase
sourcePath.add("./cts/suite/pts/deviceTests/ptsutil/src");
sourcePath.add("./cts/libs/util/src");
+ sourcePath.add("./frameworks/testing/uiautomator/library/src");
sourcePath.add(sourceDir.toString());
return join(sourcePath, ":");
}
diff --git a/tools/cts-native-scanner/Android.mk b/tools/cts-native-scanner/Android.mk
index f8b1629..8bcff1c 100644
--- a/tools/cts-native-scanner/Android.mk
+++ b/tools/cts-native-scanner/Android.mk
@@ -34,10 +34,5 @@
$(copy-file-to-new-target)
$(hide) chmod 755 $@
-# the other stuff
-# ============================================================
-subdirs := $(addprefix $(LOCAL_PATH)/,$(addsuffix /Android.mk, \
- src \
- ))
-
-include $(subdirs)
+# Build all sub-directories
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/tools/cts-native-scanner/src/com/android/cts/nativescanner/TestScanner.java b/tools/cts-native-scanner/src/com/android/cts/nativescanner/TestScanner.java
index 9411566..0a1b207 100644
--- a/tools/cts-native-scanner/src/com/android/cts/nativescanner/TestScanner.java
+++ b/tools/cts-native-scanner/src/com/android/cts/nativescanner/TestScanner.java
@@ -30,12 +30,14 @@
* Scanner of C++ gTest source files.
*
* It looks for test declarations and outputs a file following this format:
- *
- * class:TestClass1
- * method:testMethod1
- * method:testMethod2
- * class:TestClass2
- * method:testMethod1
+ * suite:TestSuite
+ * case:TestCase1
+ * test:Test1
+ * test:Test2
+ * suite:TestSuite
+ * case:TestCase2
+ * test:Test1
+ * test:Test2
*
*/
class TestScanner {
@@ -71,38 +73,36 @@
if (file.isDirectory()) {
scanDir(file, testNames);
} else {
- scanFile(file, testNames);
+ scanFile(new Scanner(file), testNames);
}
}
}
- // We want to find lines like class SLObjectCreationTest : public ::testing::Test { ...
- // and extract the "SLObjectCreationTest" as group #1
- private static final Pattern CLASS_REGEX =
- Pattern.compile("\\s*class\\s+(\\w+).*");
// We want to find lines like TEST_F(SLObjectCreationTest, testAudioPlayerFromFdCreation) { ...
- // and extract the "testAudioPlayerFromFdCreation" as group #1
+ // and extract the "SLObjectCreationTest" as group #1,
+ // "testAudioPlayerFromFdCreation" as group #2
private static final Pattern METHOD_REGEX =
- Pattern.compile("\\s*TEST_F\\(\\w+,\\s*(\\w+)\\).*");
+ Pattern.compile("\\s*TEST_F\\((\\w+),\\s*(\\w+)\\).*");
- private void scanFile(File file, List<String> testNames) throws FileNotFoundException {
- Scanner scanner = null;
+ public void scanFile(Scanner scanner, List<String> testNames) {
try {
- scanner = new Scanner(file);
+ String lastCase = "";
while (scanner.hasNextLine()) {
String line = scanner.nextLine();
- Matcher matcher = CLASS_REGEX.matcher(line);
- if (matcher.matches()) {
- testNames.add("suite:" + mTestSuite);
- testNames.add("case:" + matcher.group(1));
+
+ Matcher matcher = METHOD_REGEX.matcher(line);
+
+ if (!matcher.matches()) {
continue;
}
- matcher = METHOD_REGEX.matcher(line);
- if (matcher.matches()) {
- testNames.add("test:" + matcher.group(1));
- continue;
+ if (!lastCase.equals(matcher.group(1))) {
+ testNames.add("suite:" + mTestSuite);
+ testNames.add("case:" + matcher.group(1));
+ lastCase = matcher.group(1);
}
+
+ testNames.add("test:" + matcher.group(2));
}
} finally {
if (scanner != null) {
diff --git a/suite/pts/hostTests/ptshostutil/Android.mk b/tools/cts-native-scanner/tests/Android.mk
similarity index 71%
copy from suite/pts/hostTests/ptshostutil/Android.mk
copy to tools/cts-native-scanner/tests/Android.mk
index 14e786a..6044b0d 100644
--- a/suite/pts/hostTests/ptshostutil/Android.mk
+++ b/tools/cts-native-scanner/tests/Android.mk
@@ -1,4 +1,4 @@
-# Copyright (C) 2012 The Android Open Source Project
+# Copyright 2012 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.
@@ -16,14 +16,13 @@
include $(CLEAR_VARS)
-LOCAL_SRC_FILES := \
- $(call all-java-files-under, src)
+# Only compile source java files in this lib
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
-LOCAL_JAVA_LIBRARIES := cts-tradefed tradefed-prebuilt ddmlib-prebuilt junit
-
+LOCAL_MODULE := cts-native-scanner-tests
LOCAL_MODULE_TAGS := optional
-
-LOCAL_MODULE := ptshostutil
+LOCAL_JAVA_LIBRARIES := ddmlib-prebuilt tradefed-prebuilt cts-native-scanner
include $(BUILD_HOST_JAVA_LIBRARY)
+
diff --git a/tools/cts-native-scanner/tests/run_unit_tests.sh b/tools/cts-native-scanner/tests/run_unit_tests.sh
new file mode 100755
index 0000000..a42e42b
--- /dev/null
+++ b/tools/cts-native-scanner/tests/run_unit_tests.sh
@@ -0,0 +1,36 @@
+#!/bin/bash
+
+# Copyright 2012 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.
+
+# helper script for running the cts-tradefed unit tests
+
+checkFile() {
+ if [ ! -f "$1" ]; then
+ echo "Unable to locate $1"
+ exit
+ fi;
+}
+
+JAR_DIR=${ANDROID_HOST_OUT}/framework
+JARS="ddmlib-prebuilt.jar tradefed-prebuilt.jar hosttestlib.jar cts-native-scanner.jar cts-native-scanner-tests.jar"
+
+for JAR in $JARS; do
+ checkFile ${JAR_DIR}/${JAR}
+ JAR_PATH=${JAR_PATH}:${JAR_DIR}/${JAR}
+done
+
+java $RDBG_FLAG \
+ -cp ${JAR_PATH} com.android.tradefed.command.Console run singleCommand host -n --class com.android.cts.nativescanner.UnitTests "$@"
+
diff --git a/tools/cts-native-scanner/tests/src/com/android/cts/nativescanner/TestScannerTest.java b/tools/cts-native-scanner/tests/src/com/android/cts/nativescanner/TestScannerTest.java
new file mode 100644
index 0000000..18732fd
--- /dev/null
+++ b/tools/cts-native-scanner/tests/src/com/android/cts/nativescanner/TestScannerTest.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2012 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.nativescanner;
+
+import com.android.cts.nativescanner.TestScanner;
+
+import junit.framework.TestCase;
+
+import java.io.File;
+import java.io.StringReader;
+import java.lang.StringBuilder;
+import java.util.Scanner;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Iterator;
+
+/**
+ * Unit tests for {@link TestScanner}.
+ */
+public class TestScannerTest extends TestCase {
+
+ public void testScanFile() {
+ TestScanner testScanner = new TestScanner(new File("unused"), "TestSuite");
+
+ String newLine = System.getProperty("line.separator");
+ StringBuilder sb = new StringBuilder();
+ sb.append("foobar" + newLine); // ignored
+ sb.append("TEST_F(TestCase1, TestName1)" + newLine); // valid
+ sb.append("TEST_F(TestCase1, TestName2)" + newLine); // valid
+ sb.append("TEST_F(TestCase2, TestName1) foo" + newLine); // valid
+ sb.append("TEST_F(TestCase2, TestName1 foo)" + newLine); // ignored
+ sb.append("foo TEST_F(TestCase2, TestName1)" + newLine); // ignored
+
+ List<String> names = new ArrayList<String>();
+ Scanner scanner = new Scanner(new StringReader(sb.toString()));
+ testScanner.scanFile(scanner, names);
+ Iterator it = names.iterator();
+
+ assertEquals("suite:TestSuite", it.next());
+ assertEquals("case:TestCase1", it.next());
+ assertEquals("test:TestName1", it.next());
+ assertEquals("test:TestName2", it.next());
+ assertEquals("suite:TestSuite", it.next());
+ assertEquals("case:TestCase2", it.next());
+ assertEquals("test:TestName1", it.next());
+ assertFalse(it.hasNext());
+ scanner.close();
+ }
+}
diff --git a/tools/cts-native-scanner/tests/src/com/android/cts/nativescanner/UnitTests.java b/tools/cts-native-scanner/tests/src/com/android/cts/nativescanner/UnitTests.java
new file mode 100644
index 0000000..13d4e5b
--- /dev/null
+++ b/tools/cts-native-scanner/tests/src/com/android/cts/nativescanner/UnitTests.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2012 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.nativescanner;
+
+import com.android.cts.nativescanner.TestScannerTest;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+/**
+ * A test suite for all cts-native-scanner unit tests.
+ * <p/>
+ * All tests listed here should be self-contained, and do not require any external dependencies
+ */
+public class UnitTests extends TestSuite {
+
+ public UnitTests() {
+ super();
+
+ // result package
+ addTestSuite(TestScannerTest.class);
+ }
+
+ public static Test suite() {
+ return new UnitTests();
+ }
+}
diff --git a/tools/cts-xml-generator/src/com/android/cts/xmlgenerator/CtsXmlGenerator.java b/tools/cts-xml-generator/src/com/android/cts/xmlgenerator/CtsXmlGenerator.java
index ce4fdd7..c5b253a 100644
--- a/tools/cts-xml-generator/src/com/android/cts/xmlgenerator/CtsXmlGenerator.java
+++ b/tools/cts-xml-generator/src/com/android/cts/xmlgenerator/CtsXmlGenerator.java
@@ -49,6 +49,8 @@
String instrumentation = null;
String testType = null;
String jarPath = null;
+ String appNameSpace = null;
+ String targetNameSpace = null;
for (int i = 0; i < args.length; i++) {
if ("-p".equals(args[i])) {
@@ -68,15 +70,17 @@
"Missing value for expectation store")));
} else if ("-o".equals(args[i])) {
outputPath = getArg(args, ++i, "Missing value for output file");
+ } else if ("-a".equals(args[i])) {
+ appNameSpace = getArg(args, ++i, "Missing value for app name space");
+ } else if ("-r".equals(args[i])) {
+ targetNameSpace = getArg(args, ++i, "Missing value for target name space");
} else {
System.err.println("Unsupported flag: " + args[i]);
usage(args);
}
}
- String appNameSpace = null;
String runner = null;
- String targetNameSpace = null;
if (manifestFile != null) {
Document manifest = DocumentBuilderFactory.newInstance().newDocumentBuilder()
diff --git a/tools/tradefed-host/.classpath b/tools/tradefed-host/.classpath
index 275ecf9..09dd0ce 100644
--- a/tools/tradefed-host/.classpath
+++ b/tools/tradefed-host/.classpath
@@ -3,10 +3,10 @@
<classpathentry kind="src" path="src"/>
<classpathentry kind="src" path="res"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
- <classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/3"/>
- <classpathentry combineaccessrules="false" kind="src" path="/ddmlib"/>
- <classpathentry combineaccessrules="false" kind="src" path="/tradefederation"/>
<classpathentry exported="true" kind="var" path="CTS_SRC_ROOT/out/host/common/obj/JAVA_LIBRARIES/ctsdeviceinfolib_intermediates/javalib.jar"/>
<classpathentry exported="true" kind="var" path="CTS_SRC_ROOT/out/host/common/obj/JAVA_LIBRARIES/hosttestlib_intermediates/javalib.jar"/>
+ <classpathentry kind="var" path="CTS_SRC_ROOT/prebuilt/common/ddmlib/ddmlib-prebuilt.jar" sourcepath="/SDK_SRC_ROOT"/>
+ <classpathentry kind="var" path="CTS_SRC_ROOT/prebuilt/common/tradefed/tradefed-prebuilt.jar" sourcepath="/TRADEFED_ROOT/tools/tradefederation/src"/>
+ <classpathentry kind="var" path="CTS_SRC_ROOT/out/host/common/obj/JAVA_LIBRARIES/junit_intermediates/javalib.jar"/>
<classpathentry kind="output" path="bin"/>
</classpath>
diff --git a/tools/tradefed-host/.gitignore b/tools/tradefed-host/.gitignore
new file mode 100644
index 0000000..ba077a4
--- /dev/null
+++ b/tools/tradefed-host/.gitignore
@@ -0,0 +1 @@
+bin
diff --git a/tools/tradefed-host/Android.mk b/tools/tradefed-host/Android.mk
index 7af7993..e8965ab 100644
--- a/tools/tradefed-host/Android.mk
+++ b/tools/tradefed-host/Android.mk
@@ -17,13 +17,16 @@
include $(CLEAR_VARS)
# Only compile source java files in this lib.
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_SRC_FILES := \
+ $(call all-java-files-under, src) \
+ $(call all-java-files-under, ../../suite/pts/hostTests/ptshostutil/src)
+
LOCAL_JAVA_RESOURCE_DIRS := res
LOCAL_MODULE := cts-tradefed
LOCAL_MODULE_TAGS := optional
LOCAL_JAVA_LIBRARIES := ddmlib-prebuilt tradefed-prebuilt hosttestlib
-LOCAL_STATIC_JAVA_LIBRARIES := ctsdeviceinfolib
+LOCAL_STATIC_JAVA_LIBRARIES := ctsdeviceinfolib ptscommonutilhost
include $(BUILD_HOST_JAVA_LIBRARY)
diff --git a/tools/tradefed-host/res/report/cts_result.xsd b/tools/tradefed-host/res/report/cts_result.xsd
index 5e471d5..3f7f384 100644
--- a/tools/tradefed-host/res/report/cts_result.xsd
+++ b/tools/tradefed-host/res/report/cts_result.xsd
@@ -16,8 +16,8 @@
-->
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
- targetNamespace="http://compatibility.android.com/cts_result/1.13"
- xmlns="http://compatibility.android.com/cts_result/1.13"
+ targetNamespace="http://compatibility.android.com/cts_result/1.15"
+ xmlns="http://compatibility.android.com/cts_result/1.15"
elementFormDefault="qualified">
<xs:element name="TestResult">
@@ -32,6 +32,7 @@
<xs:attribute name="endtime" type="xs:string"/>
<xs:attribute name="testPlan" type="xs:string"/>
<xs:attribute name="version" type="xs:string"/>
+ <xs:attribute name="suite" type="xs:string"/>
</xs:complexType>
</xs:element>
@@ -195,6 +196,29 @@
<xs:attribute name="priority" type="xs:string"/>
</xs:complexType>
+<xs:simpleType name="unitType">
+ <xs:restriction base="xs:string">
+ <xs:enumeration value="none"/>
+ <xs:enumeration value="ms"/>
+ <xs:enumeration value="fps"/>
+ <xs:enumeration value="ops"/>
+ <xs:enumeration value="kbps"/>
+ <xs:enumeration value="mbps"/>
+ <xs:enumeration value="byte"/>
+ <xs:enumeration value="count"/>
+ <xs:enumeration value="score"/>
+ </xs:restriction>
+</xs:simpleType>
+
+<xs:simpleType name="scoreTypeType">
+ <xs:restriction base="xs:string">
+ <xs:enumeration value="higherBetter"/>
+ <xs:enumeration value="lowerBetter"/>
+ <xs:enumeration value="neutral"/>
+ <xs:enumeration value="warning"/>
+ </xs:restriction>
+</xs:simpleType>
+
<xs:complexType name="testType">
<xs:sequence>
<xs:element name="FailedScene" minOccurs="0" maxOccurs="1">
@@ -205,6 +229,35 @@
<xs:attribute name="message" type="xs:string"/>
</xs:complexType>
</xs:element>
+ <xs:element name="Summary" minOccurs="0" maxOccurs="1">
+ <xs:complexType>
+ <xs:simpleContent>
+ <xs:extension base="xs:decimal">
+ <xs:attribute name="message" type="xs:string" use="required" />
+ <xs:attribute name="scoreType" type="scoreTypeType" use="required" />
+ <xs:attribute name="unit" type="unitType" use="required" />
+ <xs:attribute name="target" type="xs:decimal" />
+ </xs:extension>
+ </xs:simpleContent>
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="Details" minOccurs="0" maxOccurs="1">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:element name="ValueArray" minOccurs="0" maxOccurs="unbounded">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:element name="Value" type="xs:decimal" minOccurs="0" maxOccurs="unbounded" />
+ </xs:sequence>
+ <xs:attribute name="source" type="xs:string" use="required" />
+ <xs:attribute name="message" type="xs:string" use="required" />
+ <xs:attribute name="scoreType" type="scoreTypeType" use="required" />
+ <xs:attribute name="unit" type="unitType" use="required" />
+ </xs:complexType>
+ </xs:element>
+ </xs:sequence>
+ </xs:complexType>
+ </xs:element>
</xs:sequence>
<xs:attribute name="name" type="xs:string" use="required"/>
<xs:attribute name="result" type="resultType" use="required"/>
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/build/CtsBuildHelper.java b/tools/tradefed-host/src/com/android/cts/tradefed/build/CtsBuildHelper.java
index 95ec69f..61b4b43 100644
--- a/tools/tradefed-host/src/com/android/cts/tradefed/build/CtsBuildHelper.java
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/build/CtsBuildHelper.java
@@ -30,18 +30,12 @@
public class CtsBuildHelper {
static final String CTS_DIR_NAME = "android-cts";
- static final String PTS_DIR_NAME = "android-pts";
- static private boolean mCtsMode = true;
- private final String mSuiteName;
+ private final String mSuiteName = "CTS";
/** The root location of the extracted CTS package */
private final File mRootDir;
/** the {@link CTS_DIR_NAME} directory */
private final File mCtsDir;
- public static void changeToPtsMode() {
- mCtsMode = false;
- }
-
/**
* Creates a {@link CtsBuildHelper}.
*
@@ -50,8 +44,7 @@
*/
public CtsBuildHelper(File rootDir) {
mRootDir = rootDir;
- mSuiteName = mCtsMode? "CTS" : "PTS";
- mCtsDir = new File(mRootDir, mCtsMode ? CTS_DIR_NAME : PTS_DIR_NAME);
+ mCtsDir = new File(mRootDir, CTS_DIR_NAME);
}
/**
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/command/CtsConsole.java b/tools/tradefed-host/src/com/android/cts/tradefed/command/CtsConsole.java
index f014135..a8ca951 100644
--- a/tools/tradefed-host/src/com/android/cts/tradefed/command/CtsConsole.java
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/command/CtsConsole.java
@@ -246,11 +246,6 @@
}
public static void main(String[] args) throws InterruptedException {
- // change to PTS mode before anything else
- String ptsMode = System.getProperty("PTS");
- if ((ptsMode != null) && ptsMode.equals("1")) {
- CtsBuildHelper.changeToPtsMode();
- }
Console console = new CtsConsole();
Console.startConsole(console, args);
}
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/result/CtsXmlResultReporter.java b/tools/tradefed-host/src/com/android/cts/tradefed/result/CtsXmlResultReporter.java
index a2a31e0..2f62d71 100644
--- a/tools/tradefed-host/src/com/android/cts/tradefed/result/CtsXmlResultReporter.java
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/result/CtsXmlResultReporter.java
@@ -58,7 +58,7 @@
private static final String LOG_TAG = "CtsXmlResultReporter";
static final String TEST_RESULT_FILE_NAME = "testResult.xml";
- private static final String CTS_RESULT_FILE_VERSION = "1.13";
+ private static final String CTS_RESULT_FILE_VERSION = "1.14";
private static final String[] CTS_RESULT_RESOURCES = {"cts_result.xsl", "cts_result.css",
"logo.gif", "newrule-green.png"};
@@ -97,6 +97,7 @@
private boolean mIsDeviceInfoRun = false;
private ResultReporter mReporter;
private File mLogDir;
+ private String mSuiteName;
private static final String PTS_PERFORMANCE_EXCEPTION = "com.android.pts.util.PtsException";
private static final Pattern mPtsLogPattern = Pattern.compile(
@@ -141,8 +142,8 @@
mStartTime = getTimestamp();
logResult("Created result dir %s", mReportDir.getName());
}
-
- mReporter = new ResultReporter(mResultServer, ctsBuildHelper.getSuiteName());
+ mSuiteName = ctsBuildHelper.getSuiteName();
+ mReporter = new ResultReporter(mResultServer, mSuiteName);
// TODO: allow customization of log dir
// create a unique directory for saving logs, with same name as result dir
@@ -358,6 +359,7 @@
serializer.attribute(ns, STARTTIME_ATTR, startTime);
serializer.attribute(ns, "endtime", endTime);
serializer.attribute(ns, "version", CTS_RESULT_FILE_VERSION);
+ serializer.attribute(ns, "suite", mSuiteName);
mResults.serialize(serializer);
// TODO: not sure why, but the serializer doesn't like this statement
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/result/Test.java b/tools/tradefed-host/src/com/android/cts/tradefed/result/Test.java
index dd4f31f..72e2e27 100644
--- a/tools/tradefed-host/src/com/android/cts/tradefed/result/Test.java
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/result/Test.java
@@ -15,6 +15,7 @@
*/
package com.android.cts.tradefed.result;
+import com.android.ddmlib.Log;
import com.android.tradefed.result.TestResult;
import org.kxml2.io.KXmlSerializer;
@@ -27,7 +28,6 @@
* Data structure that represents a "Test" result XML element.
*/
class Test extends AbstractXmlPullParser {
-
static final String TAG = "Test";
private static final String NAME_ATTR = "name";
private static final String MESSAGE_ATTR = "message";
@@ -36,7 +36,17 @@
private static final String RESULT_ATTR = "result";
private static final String SCENE_TAG = "FailedScene";
private static final String STACK_TAG = "StackTrace";
+ private static final String SUMMARY_TAG = "Summary";
private static final String DETAILS_TAG = "Details";
+ private static final String VALUEARRAY_TAG = "ValueArray";
+ private static final String VALUE_TAG = "Value";
+ private static final String TARGET_ATTR = "target";
+ private static final String SCORETYPE_ATTR = "scoreType";
+ private static final String UNIT_ATTR = "unit";
+ private static final String SOURCE_ATTR = "source";
+ // separators for the message from PTS
+ private static final String LOG_SEPARATOR = "\\+\\+\\+";
+ private static final String LOG_ELEM_SEPARATOR = "\\|";
private String mName;
private CtsTestStatus mResult;
@@ -44,7 +54,8 @@
private String mEndTime;
private String mMessage;
private String mStackTrace;
- // details passed from pts
+ // summary and details passed from pts
+ private String mSummary;
private String mDetails;
/**
@@ -109,6 +120,14 @@
mMessage = getFailureMessageFromStackTrace(mStackTrace);
}
+ public String getSummary() {
+ return mSummary;
+ }
+
+ public void setSummary(String summary) {
+ mSummary = summary;
+ }
+
public String getDetails() {
return mDetails;
}
@@ -147,17 +166,115 @@
serializer.text(mStackTrace);
serializer.endTag(CtsXmlResultReporter.ns, STACK_TAG);
}
- if (mDetails != null) {
- serializer.startTag(CtsXmlResultReporter.ns, DETAILS_TAG);
- serializer.text(mDetails);
- serializer.endTag(CtsXmlResultReporter.ns, DETAILS_TAG);
- }
serializer.endTag(CtsXmlResultReporter.ns, SCENE_TAG);
}
+ if (mSummary != null) {
+ // <Summary message = "screen copies per sec" scoretype="higherBetter" unit="fps">
+ // 23938.82978723404</Summary>
+ PerfResultSummary summary = parseSummary(mSummary);
+ if (summary != null) {
+ serializer.startTag(CtsXmlResultReporter.ns, SUMMARY_TAG);
+ serializer.attribute(CtsXmlResultReporter.ns, MESSAGE_ATTR, summary.mMessage);
+ if (summary.mTarget.length() != 0 && !summary.mTarget.equals(" ")) {
+ serializer.attribute(CtsXmlResultReporter.ns, TARGET_ATTR, summary.mTarget);
+ }
+ serializer.attribute(CtsXmlResultReporter.ns, SCORETYPE_ATTR, summary.mType);
+ serializer.attribute(CtsXmlResultReporter.ns, UNIT_ATTR, summary.mUnit);
+ serializer.text(summary.mValue);
+ serializer.endTag(CtsXmlResultReporter.ns, SUMMARY_TAG);
+ // add details only if summary is present
+ // <Details>
+ // <ValueArray source=”com.android.pts.dram.BandwidthTest#doRunMemcpy:98”
+ // message=”measure1” unit="ms" scoretype="higherBetter">
+ // <Value>0.0</Value>
+ // <Value>0.1</Value>
+ // </ValueArray>
+ // </Details>
+ if (mDetails != null) {
+ PerfResultDetail[] ds = parseDetails(mDetails);
+ serializer.startTag(CtsXmlResultReporter.ns, DETAILS_TAG);
+ for (PerfResultDetail d : ds) {
+ if (d == null) {
+ continue;
+ }
+ serializer.startTag(CtsXmlResultReporter.ns, VALUEARRAY_TAG);
+ serializer.attribute(CtsXmlResultReporter.ns, SOURCE_ATTR, d.mSource);
+ serializer.attribute(CtsXmlResultReporter.ns, MESSAGE_ATTR,
+ d.mMessage);
+ serializer.attribute(CtsXmlResultReporter.ns, SCORETYPE_ATTR, d.mType);
+ serializer.attribute(CtsXmlResultReporter.ns, UNIT_ATTR, d.mUnit);
+ for (String v : d.mValues) {
+ if (v == null) {
+ continue;
+ }
+ serializer.startTag(CtsXmlResultReporter.ns, VALUE_TAG);
+ serializer.text(v);
+ serializer.endTag(CtsXmlResultReporter.ns, VALUE_TAG);
+ }
+ serializer.endTag(CtsXmlResultReporter.ns, VALUEARRAY_TAG);
+ }
+ serializer.endTag(CtsXmlResultReporter.ns, DETAILS_TAG);
+ }
+ }
+ }
serializer.endTag(CtsXmlResultReporter.ns, TAG);
}
/**
+ * class containing performance result.
+ */
+ public static class PerfResultCommon {
+ public String mMessage;
+ public String mType;
+ public String mUnit;
+ }
+
+ private class PerfResultSummary extends PerfResultCommon {
+ public String mTarget;
+ public String mValue;
+ }
+
+ private class PerfResultDetail extends PerfResultCommon {
+ public String mSource;
+ public String[] mValues;
+ }
+
+ private PerfResultSummary parseSummary(String summary) {
+ String[] elems = summary.split(LOG_ELEM_SEPARATOR);
+ PerfResultSummary r = new PerfResultSummary();
+ if (elems.length < 5) {
+ Log.w(TAG, "wrong message " + summary);
+ return null;
+ }
+ r.mMessage = elems[0];
+ r.mTarget = elems[1];
+ r.mType = elems[2];
+ r.mUnit = elems[3];
+ r.mValue = elems[4];
+ return r;
+ }
+
+ private PerfResultDetail[] parseDetails(String details) {
+ String[] arrays = details.split(LOG_SEPARATOR);
+ PerfResultDetail[] rs = new PerfResultDetail[arrays.length];
+ for (int i = 0; i < arrays.length; i++) {
+ String[] elems = arrays[i].split(LOG_ELEM_SEPARATOR);
+ if (elems.length < 5) {
+ Log.w(TAG, "wrong message " + arrays[i]);
+ continue;
+ }
+ PerfResultDetail r = new PerfResultDetail();
+ r.mSource = elems[0];
+ r.mMessage = elems[1];
+ r.mType = elems[2];
+ r.mUnit = elems[3];
+ r.mValues = elems[4].split(" ");
+ rs[i] = r;
+ }
+ return rs;
+ }
+
+ /**
* Strip out any invalid XML characters that might cause the report to be unviewable.
* http://www.w3.org/TR/REC-xml/#dt-character
*/
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/result/TestPackageResult.java b/tools/tradefed-host/src/com/android/cts/tradefed/result/TestPackageResult.java
index ce1c2f3..015a7ae 100644
--- a/tools/tradefed-host/src/com/android/cts/tradefed/result/TestPackageResult.java
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/result/TestPackageResult.java
@@ -283,7 +283,7 @@
public void reportPerformanceResult(TestIdentifier test, CtsTestStatus status, String summary, String details) {
Test result = findTest(test);
result.setResultStatus(status);
- result.setMessage(summary);
+ result.setSummary(summary);
result.setDetails(details);
}
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/targetprep/SettingsToggler.java b/tools/tradefed-host/src/com/android/cts/tradefed/targetprep/SettingsToggler.java
index 69d4c8a..ff6c4f4 100644
--- a/tools/tradefed-host/src/com/android/cts/tradefed/targetprep/SettingsToggler.java
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/targetprep/SettingsToggler.java
@@ -22,52 +22,98 @@
* {@link SettingsToggler} sets settings by using the "adb shell content" command.
*/
public class SettingsToggler {
+ private static final String GROUP_SECURE = "secure";
+ private static final String GROUP_GLOBAL = "global";
+
+ /** Sets a setting by deleting and then inserting the string value. */
+ public static void setString(ITestDevice device, String group, String name, String value)
+ throws DeviceNotAvailableException {
+ deleteSetting(device, group, name);
+ device.executeShellCommand(
+ "content insert"
+ + " --uri content://settings/" + group
+ + " --bind name:s:" + name
+ + " --bind value:s:" + value);
+ }
/** Sets a secure setting by deleting and then inserting the string value. */
public static void setSecureString(ITestDevice device, String name, String value)
throws DeviceNotAvailableException {
- deleteSecure(device, name);
+ setString(device, GROUP_SECURE, name, value);
+ }
+
+ /** Sets a global setting by deleting and then inserting the string value. */
+ public static void setGlobalString(ITestDevice device, String name, String value)
+ throws DeviceNotAvailableException {
+ setString(device, GROUP_GLOBAL, name, value);
+ }
+
+ /** Sets a setting by deleting and then inserting the int value. */
+ public static void setInt(ITestDevice device, String group, String name, int value)
+ throws DeviceNotAvailableException {
+ deleteSetting(device, group, name);
device.executeShellCommand(
"content insert"
- + " --uri content://settings/secure"
+ + " --uri content://settings/" + group
+ " --bind name:s:" + name
- + " --bind value:s:" + value);
+ + " --bind value:i:" + value);
}
/** Sets a secure setting by deleting and then inserting the int value. */
public static void setSecureInt(ITestDevice device, String name, int value)
throws DeviceNotAvailableException {
- deleteSecure(device, name);
+ setInt(device, GROUP_SECURE, name, value);
+ }
+
+ /** Sets a global setting by deleting and then inserting the int value. */
+ public static void setGlobalInt(ITestDevice device, String name, int value)
+ throws DeviceNotAvailableException {
+ setInt(device, GROUP_GLOBAL, name, value);
+ }
+
+ public static void updateString(ITestDevice device, String group, String name, String value)
+ throws DeviceNotAvailableException {
device.executeShellCommand(
- "content insert"
- + " --uri content://settings/secure"
- + " --bind name:s:" + name
- + " --bind value:i:" + value);
+ "content update"
+ + " --uri content://settings/" + group
+ + " --bind value:s:" + value
+ + " --where \"name='" + name + "'\"");
}
public static void updateSecureString(ITestDevice device, String name, String value)
throws DeviceNotAvailableException {
+ updateString(device, GROUP_SECURE, name, value);
+ }
+
+ public static void updateGlobalString(ITestDevice device, String name, String value)
+ throws DeviceNotAvailableException {
+ updateString(device, GROUP_GLOBAL, name, value);
+ }
+
+ public static void updateInt(ITestDevice device, String group, String name, int value)
+ throws DeviceNotAvailableException {
device.executeShellCommand(
"content update"
- + " --uri content://settings/secure"
- + " --bind value:s:" + value
+ + " --uri content://settings/" + group
+ + " --bind value:i:" + value
+ " --where \"name='" + name + "'\"");
}
public static void updateSecureInt(ITestDevice device, String name, int value)
throws DeviceNotAvailableException {
- device.executeShellCommand(
- "content update"
- + " --uri content://settings/secure"
- + " --bind value:i:" + value
- + " --where \"name='" + name + "'\"");
+ updateInt(device, GROUP_SECURE, name, value);
}
- private static void deleteSecure(ITestDevice device, String name)
+ public static void updateGlobalInt(ITestDevice device, String name, int value)
+ throws DeviceNotAvailableException {
+ updateInt(device, GROUP_GLOBAL, name, value);
+ }
+
+ private static void deleteSetting(ITestDevice device, String group, String name)
throws DeviceNotAvailableException {
device.executeShellCommand(
"content delete"
- + " --uri content://settings/secure"
+ + " --uri content://settings/" + group
+ " --where \"name='" + name + "'\"");
}
}
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/DisplayTestRunner.java b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/DisplayTestRunner.java
new file mode 100644
index 0000000..4c83aa9
--- /dev/null
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/DisplayTestRunner.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2012 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.tradefed.testtype;
+
+import com.android.cts.tradefed.targetprep.SettingsToggler;
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.result.ITestInvocationListener;
+
+/**
+ * Running the display tests requires modification of secure settings to create an overlay display.
+ * Secure settings cannot be changed from device CTS tests since system signature permission is
+ * required. Such settings can be modified by the shell user, so a host side test is used.
+ */
+public class DisplayTestRunner extends InstrumentationApkTest {
+ private static final String OVERLAY_DISPLAY_DEVICES_SETTING_NAME = "overlay_display_devices";
+
+ // Use a non-standard pattern, must match values in tests/tests/display/.../DisplayTest.java
+ private static final String OVERLAY_DISPLAY_DEVICES_SETTING_VALUE = "1281x721/214";
+
+ @Override
+ public void run(ITestInvocationListener listener) throws DeviceNotAvailableException {
+ // CLog.e("run: About to enable overlay display.");
+ SettingsToggler.setGlobalString(getDevice(), OVERLAY_DISPLAY_DEVICES_SETTING_NAME,
+ OVERLAY_DISPLAY_DEVICES_SETTING_VALUE);
+
+ super.run(listener);
+
+ // Tear down overlay display.
+ // CLog.e("run: About to disable overlay display.");
+ SettingsToggler.setGlobalString(getDevice(), OVERLAY_DISPLAY_DEVICES_SETTING_NAME,
+ "");
+ }
+}
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/TestPackageDef.java b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/TestPackageDef.java
index ffbb930..2d11657 100644
--- a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/TestPackageDef.java
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/TestPackageDef.java
@@ -16,6 +16,7 @@
package com.android.cts.tradefed.testtype;
+import com.android.ddmlib.Log.LogLevel;
import com.android.ddmlib.testrunner.TestIdentifier;
import com.android.tradefed.log.LogUtil.CLog;
import com.android.tradefed.testtype.IRemoteTest;
@@ -43,11 +44,15 @@
public static final String HOST_SIDE_ONLY_TEST = "hostSideOnly";
public static final String NATIVE_TEST = "native";
+ public static final String WRAPPED_NATIVE_TEST = "wrappednative";
public static final String VM_HOST_TEST = "vmHostTest";
public static final String ACCESSIBILITY_TEST =
"com.android.cts.tradefed.testtype.AccessibilityTestRunner";
public static final String ACCESSIBILITYSERVICE_TEST =
"com.android.cts.tradefed.testtype.AccessibilityServiceTestRunner";
+ public static final String DISPLAY_TEST =
+ "com.android.cts.tradefed.testtype.DisplayTestRunner";
+ public static final String UIAUTOMATOR_TEST = "uiAutomator";
private static final String SIGNATURE_TEST_METHOD = "testSignature";
private static final String SIGNATURE_TEST_CLASS = "android.tests.sigtest.SimpleSignatureTest";
@@ -219,12 +224,21 @@
return vmHostTest;
} else if (NATIVE_TEST.equals(mTestType)) {
return new GeeTest(mUri, mName);
+ } else if (WRAPPED_NATIVE_TEST.equals(mTestType)) {
+ CLog.d("Creating new wrapped native test for %s", mName);
+ return new WrappedGTest(mAppNameSpace, mUri, mName, mRunner);
} else if (ACCESSIBILITY_TEST.equals(mTestType)) {
AccessibilityTestRunner test = new AccessibilityTestRunner();
return setInstrumentationTest(test, testCaseDir);
} else if (ACCESSIBILITYSERVICE_TEST.equals(mTestType)) {
AccessibilityServiceTestRunner test = new AccessibilityServiceTestRunner();
return setInstrumentationTest(test, testCaseDir);
+ } else if (DISPLAY_TEST.equals(mTestType)) {
+ DisplayTestRunner test = new DisplayTestRunner();
+ return setInstrumentationTest(test, testCaseDir);
+ } else if (UIAUTOMATOR_TEST.equals(mTestType)) {
+ UiAutomatorJarTest uiautomatorTest = new UiAutomatorJarTest();
+ return setUiAutomatorTest(uiautomatorTest);
} else if (mIsSignatureTest) {
// TODO: hardcode the runner/class/method for now, since current package xml points to
// specialized instrumentation. Eventually this special case for signatureTest can be
@@ -285,6 +299,28 @@
}
/**
+ * Populates given {@link UiAutomatorJarTest} with data from the package xml.
+ *
+ * @param uiautomatorTest
+ * @return the populated {@link UiAutomatorJarTest} or <code>null</code>
+ */
+ private IRemoteTest setUiAutomatorTest(UiAutomatorJarTest uiautomatorTest) {
+ uiautomatorTest.setInstallArtifacts(getJarPath());
+ if (mClassName != null) {
+ if (mMethodName != null) {
+ CLog.logAndDisplay(LogLevel.WARN, "ui automator tests don't currently support" +
+ "running individual methods");
+ }
+ uiautomatorTest.addClassName(mClassName);
+ } else {
+ uiautomatorTest.addClassNames(mTestClasses);
+ }
+ uiautomatorTest.setRunName(getUri());
+ uiautomatorTest.setCaptureLogs(false);
+ return uiautomatorTest;
+ }
+
+ /**
* Filter the tests to run based on list of excluded tests, class and method name.
*
* @return the filtered collection of tests
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/UiAutomatorJarTest.java b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/UiAutomatorJarTest.java
new file mode 100644
index 0000000..83cc6d6
--- /dev/null
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/UiAutomatorJarTest.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2011 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.tradefed.testtype;
+
+import com.android.cts.tradefed.build.CtsBuildHelper;
+import com.android.tradefed.build.IBuildInfo;
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.log.LogUtil.CLog;
+import com.android.tradefed.result.ITestInvocationListener;
+import com.android.tradefed.testtype.IBuildReceiver;
+import com.android.tradefed.testtype.UiAutomatorTest;
+
+import java.io.FileNotFoundException;
+import java.util.Arrays;
+
+import junit.framework.Assert;
+
+/**
+ * A {@link UiAutomatorTest} that will install a uiautomator jar before test
+ * execution, and uninstall on execution completion.
+ */
+public class UiAutomatorJarTest extends UiAutomatorTest implements IBuildReceiver {
+
+ // TODO: expose this in parent
+ private static final String SHELL_EXE_BASE = "/data/local/tmp/";
+
+ /** the file names of the CTS jar to install */
+ private String mTestJarFileName;
+
+ private CtsBuildHelper mCtsBuild = null;
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void setBuild(IBuildInfo build) {
+ mCtsBuild = CtsBuildHelper.createBuildHelper(build);
+ }
+
+ /**
+ * Setter for CTS build files needed to perform the test.
+ *
+ * @param testJarName the file name of the jar containing the uiautomator tests
+ */
+ public void setInstallArtifacts(String testJarName) {
+ mTestJarFileName = testJarName;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void run(final ITestInvocationListener listener)
+ throws DeviceNotAvailableException {
+ Assert.assertNotNull("missing device", getDevice());
+ Assert.assertNotNull("missing build", mCtsBuild);
+ Assert.assertNotNull("missing jar to install", mTestJarFileName);
+
+ installJar();
+
+ super.run(listener);
+
+ uninstallJar();
+ }
+
+ private void installJar() throws DeviceNotAvailableException {
+ CLog.d("Installing %s on %s", mTestJarFileName, getDevice().getSerialNumber());
+ String fullJarPath = String.format("%s%s", SHELL_EXE_BASE, mTestJarFileName);
+ try {
+ boolean result = getDevice().pushFile(mCtsBuild.getTestApp(mTestJarFileName),
+ fullJarPath);
+ Assert.assertTrue(String.format("Failed to push file to %s", fullJarPath), result);
+ setTestJarPaths(Arrays.asList(fullJarPath));
+ } catch (FileNotFoundException e) {
+ Assert.fail(String.format("Could not find file %s", mTestJarFileName));
+ }
+ }
+
+ private void uninstallJar() throws DeviceNotAvailableException {
+ CLog.d("Uninstalling %s on %s", mTestJarFileName, getDevice().getSerialNumber());
+ String fullJarPath = String.format("%s%s", SHELL_EXE_BASE, mTestJarFileName);
+ getDevice().executeShellCommand(String.format("rm %s", fullJarPath));
+ }
+}
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/WrappedGTest.java b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/WrappedGTest.java
new file mode 100644
index 0000000..74c15f6
--- /dev/null
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/WrappedGTest.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright 2012 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.tradefed.testtype;
+
+import com.android.cts.tradefed.build.CtsBuildHelper;
+import com.android.ddmlib.testrunner.ITestRunListener;
+import com.android.tradefed.build.IBuildInfo;
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.log.LogUtil.CLog;
+import com.android.tradefed.result.ITestInvocationListener;
+import com.android.tradefed.testtype.IBuildReceiver;
+import com.android.tradefed.testtype.IDeviceTest;
+import com.android.tradefed.testtype.IRemoteTest;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+
+/**
+ * Test runner for wrapped (native) GTests
+ */
+public class WrappedGTest implements IBuildReceiver, IDeviceTest, IRemoteTest {
+
+ private int mMaxTestTimeMs = 1 * 60 * 1000;
+
+ private CtsBuildHelper mCtsBuild;
+ private ITestDevice mDevice;
+
+ private final String mAppNameSpace;
+ private final String mRunner;
+ private final String mName;
+ private final String mUri;
+
+
+ public WrappedGTest(String appNameSpace, String uri, String name, String runner) {
+ mAppNameSpace = appNameSpace;
+ mRunner = runner;
+ mName = name;
+ mUri = uri;
+ }
+
+ @Override
+ public void setBuild(IBuildInfo buildInfo) {
+ mCtsBuild = CtsBuildHelper.createBuildHelper(buildInfo);
+ }
+
+ @Override
+ public void setDevice(ITestDevice device) {
+ mDevice = device;
+ }
+
+ @Override
+ public ITestDevice getDevice() {
+ return mDevice;
+ }
+
+ @Override
+ public void run(ITestInvocationListener listener) throws DeviceNotAvailableException {
+ if (installTest()) {
+ runTest(listener);
+ uninstallTest();
+ } else {
+ CLog.e("Failed to install test");
+ }
+ }
+
+ private boolean installTest() throws DeviceNotAvailableException {
+ try {
+ File testApp = mCtsBuild.getTestApp(String.format("%s.apk", mName));
+ String installCode = mDevice.installPackage(testApp, true);
+
+ if (installCode != null) {
+ CLog.e("Failed to install %s.apk on %s. Reason: %s", mName,
+ mDevice.getSerialNumber(), installCode);
+ return false;
+ }
+ }
+ catch (FileNotFoundException e) {
+ CLog.e("Package %s.apk not found", mName);
+ return false;
+ }
+ return true;
+ }
+
+ private void runTest(ITestRunListener listener) throws DeviceNotAvailableException {
+ WrappedGTestResultParser resultParser = new WrappedGTestResultParser(mUri, listener);
+ resultParser.setFakePackagePrefix(mUri + ".");
+ try {
+ String command = String.format("am instrument -w %s/.%s", mAppNameSpace, mRunner);
+ mDevice.executeShellCommand(command, resultParser, mMaxTestTimeMs, 0);
+ } catch (DeviceNotAvailableException e) {
+ resultParser.flush();
+ throw e;
+ } catch (RuntimeException e) {
+ resultParser.flush();
+ throw e;
+ }
+ }
+
+ private void uninstallTest() throws DeviceNotAvailableException {
+ mDevice.uninstallPackage(mAppNameSpace);
+ }
+}
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/WrappedGTestResultParser.java b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/WrappedGTestResultParser.java
new file mode 100644
index 0000000..cc3c53a
--- /dev/null
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/WrappedGTestResultParser.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright 2012 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.tradefed.testtype;
+
+import com.android.ddmlib.testrunner.ITestRunListener;
+import com.android.tradefed.log.LogUtil.CLog;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.ArrayList;
+
+public class WrappedGTestResultParser extends GeeTestResultParser {
+
+ private boolean mInstrumentationError;
+
+ /**
+ * Creates the WrappedGTestResultParser.
+ *
+ * @param testRunName the test run name to provide to
+ * {@link ITestRunListener#testRunStarted(String, int)}
+ * @param listeners informed of test results as the tests are executing
+ */
+ public WrappedGTestResultParser(String testRunName, Collection<ITestRunListener> listeners) {
+ super(testRunName, listeners);
+ }
+
+ /**
+ * Creates the WrappedGTestResultParser for a single listener.
+ *
+ * @param testRunName the test run name to provide to
+ * {@link ITestRunListener#testRunStarted(String, int)}
+ * @param listener informed of test results as the tests are executing
+ */
+ public WrappedGTestResultParser(String testRunName, ITestRunListener listener) {
+ super(testRunName, listener);
+ }
+
+ /**
+ * Strips the instrumentation information and then forwards
+ * the raw gtest output to the {@link GeeTestResultParser}.
+ */
+ @Override
+ public void processNewLines(String[] lines) {
+ if (mInstrumentationError) {
+ return;
+ }
+
+ String[] gtestOutput = parseInstrumentation(lines);
+ super.processNewLines(gtestOutput);
+ }
+
+ /**
+ * Parses raw instrumentation output and returns the
+ * contained gtest output
+ *
+ * @param lines the raw instrumentation output
+ * @return the gtest output
+ */
+ public String[] parseInstrumentation(String[] lines) {
+ List<String> output = new ArrayList<String>();
+ boolean readMultiLine = false;
+ for (String line : lines) {
+
+ if (line.startsWith("INSTRUMENTATION_RESULT: ")) {
+ CLog.e("Instrumentation Error:");
+ mInstrumentationError = true;
+ }
+
+ if (mInstrumentationError) {
+ CLog.e(line);
+ continue;
+ }
+
+ if (line.startsWith("INSTRUMENTATION_STATUS: gtest=")) {
+ output.add(line.replace("INSTRUMENTATION_STATUS: gtest=", ""));
+ readMultiLine = true;
+ continue;
+ }
+
+ if (line.startsWith("INSTRUMENTATION_")) {
+ readMultiLine = false;
+ continue;
+ }
+
+ if (readMultiLine) {
+ output.add(line);
+ }
+ }
+
+ return output.toArray(new String[output.size()]);
+ }
+}
+
diff --git a/tools/tradefed-host/tests/src/com/android/cts/tradefed/UnitTests.java b/tools/tradefed-host/tests/src/com/android/cts/tradefed/UnitTests.java
index 7dfac13..3c36a3d 100644
--- a/tools/tradefed-host/tests/src/com/android/cts/tradefed/UnitTests.java
+++ b/tools/tradefed-host/tests/src/com/android/cts/tradefed/UnitTests.java
@@ -27,6 +27,7 @@
import com.android.cts.tradefed.testtype.TestPackageDefTest;
import com.android.cts.tradefed.testtype.TestPackageXmlParserTest;
import com.android.cts.tradefed.testtype.TestPlanTest;
+import com.android.cts.tradefed.testtype.WrappedGTestResultParserTest;
import junit.framework.Test;
import junit.framework.TestSuite;
@@ -57,6 +58,7 @@
addTestSuite(TestPackageDefTest.class);
addTestSuite(TestPackageXmlParserTest.class);
addTestSuite(TestPlanTest.class);
+ addTestSuite(WrappedGTestResultParserTest.class);
}
public static Test suite() {
diff --git a/tools/tradefed-host/tests/src/com/android/cts/tradefed/testtype/WrappedGTestResultParserTest.java b/tools/tradefed-host/tests/src/com/android/cts/tradefed/testtype/WrappedGTestResultParserTest.java
new file mode 100644
index 0000000..d386c7a
--- /dev/null
+++ b/tools/tradefed-host/tests/src/com/android/cts/tradefed/testtype/WrappedGTestResultParserTest.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright 2012 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.tradefed.testtype;
+
+import com.android.ddmlib.testrunner.ITestRunListener;
+
+import junit.framework.TestCase;
+
+
+
+/**
+ * Unit tests for {@link WrappedGTestResultParser}.
+ */
+public class WrappedGTestResultParserTest extends TestCase {
+
+ private WrappedGTestResultParser mParser;
+ private final String[] INPUT1 = new String[] {
+ "INSTRUMENTATION_STATUS: gtest=[==========] Running 9 tests from 2 test cases.",
+ "INSTRUMENTATION_STATUS_CODE: 1",
+ "INSTRUMENTATION_STATUS: gtest=[ RUN ] GLTest.Test1",
+ "INSTRUMENTATION_STATUS: gtest=[ OK ] GLTest.Test1 (10 ms)",
+ "INSTRUMENTATION_STATUS: gtest=/tests/SomeTestFile.cpp:1337: Failure",
+ "Value of: 1 == 0",
+ " Actual: false",
+ "Expected: true",
+ "INSTRUMENTATION_STATUS: gtest=[ FAILED ] GLTest.Test2 (1016 ms)",
+ "INSTRUMENTATION_STATUS: gtest=[==========] 2 tests from 1 test cases ran. (17 ms total)",
+ "INSTRUMENTATION_CODE: -1"
+ };
+
+ private final String[] EXPECTED_OUTPUT1 = new String[] {
+ "[==========] Running 9 tests from 2 test cases.",
+ "[ RUN ] GLTest.Test1",
+ "[ OK ] GLTest.Test1 (10 ms)",
+ "/tests/SomeTestFile.cpp:1337: Failure",
+ "Value of: 1 == 0",
+ " Actual: false",
+ "Expected: true",
+ "[ FAILED ] GLTest.Test2 (1016 ms)",
+ "[==========] 2 tests from 1 test cases ran. (17 ms total)",
+ };
+
+ private final String[] INPUT2 = new String[] {
+ "INSTRUMENTATION_STATUS_CODE: 1",
+ "invalid text",
+ "INSTRUMENTATION_STATUS: gtest=[==========] Running 9 tests from 2 test cases.",
+ "INSTRUMENTATION_RESULT: some error",
+ "INSTRUMENTATION_STATUS: gtest=[ RUN ] GLTest.ExpectTestThatShouldBeSuccessful",
+ };
+
+ private final String[] EXPECTED_OUTPUT2 = new String[] {
+ "[==========] Running 9 tests from 2 test cases.",
+ };
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ mParser = new WrappedGTestResultParser("unused", (ITestRunListener)null);
+ }
+
+ private void assertArrayEquals(String[] expected, String[] result) throws Exception {
+ if (expected == null) {
+ assertNull(result);
+ return;
+ }
+
+ assertEquals(expected.length, result.length);
+
+ for (int i = 0; i < expected.length; i++) {
+ assertEquals(expected[i], result[i]);
+ }
+ }
+
+ /**
+ * Test normal case {@link WrappedGTestResultParser#getRawGTestOutput(java.lang.String[])}
+ * with all kinds of valid input lines.
+ */
+ public void testGetRawGTestOutput_valid() throws Exception {
+ assertArrayEquals(EXPECTED_OUTPUT1, mParser.parseInstrumentation(INPUT1));
+ }
+
+ /**
+ * Test normal case {@link WrappedGTestResultParser#getRawGTestOutput(java.lang.String[])}
+ * with a instrumentation error/invalid input.
+ */
+ public void testGetRawGTestOutput_invalid() throws Exception {
+ assertArrayEquals(EXPECTED_OUTPUT2, mParser.parseInstrumentation(INPUT2));
+ }
+}
diff --git a/tools/utils/buildCts.py b/tools/utils/buildCts.py
index 8e23f13..6dc1c70 100755
--- a/tools/utils/buildCts.py
+++ b/tools/utils/buildCts.py
@@ -54,24 +54,20 @@
def __init__(self, argv):
"""Initialize the CtsBuilder from command line arguments."""
- if not (len(argv) == 6 or len(argv)==7):
- print 'Usage: %s <testRoot> <ctsOutputDir> <tempDir> <androidRootDir> <docletPath> [-pts]' % argv[0]
+ if len(argv) != 6:
+ print 'Usage: %s <testRoot> <ctsOutputDir> <tempDir> <androidRootDir> <docletPath>' % argv[0]
print ''
print 'testRoot: Directory under which to search for CTS tests.'
print 'ctsOutputDir: Directory in which the CTS repository should be created.'
print 'tempDir: Directory to use for storing temporary files.'
print 'androidRootDir: Root directory of the Android source tree.'
print 'docletPath: Class path where the DescriptionGenerator doclet can be found.'
- print '-pts: generate plan for PTS.'
sys.exit(1)
self.test_root = sys.argv[1]
self.out_dir = sys.argv[2]
self.temp_dir = sys.argv[3]
self.android_root = sys.argv[4]
self.doclet_path = sys.argv[5]
- self.isCts = True
- if len(argv) ==7 and sys.argv[6] == "-pts":
- self.isCts = False
self.test_repository = os.path.join(self.out_dir, 'repository/testcases')
self.plan_repository = os.path.join(self.out_dir, 'repository/plans')
@@ -103,35 +99,43 @@
doc = tools.XmlFile(description)
packages.append(doc.GetAttr('TestPackage', 'appPackageName'))
- if not self.isCts: # PTS
- plan = tools.TestPlan(packages)
- plan.Include('.*')
- plan.Exclude(r'android\.tests\.sigtest')
- self.__WritePlan(plan, 'PTS')
- return
+ ptsPattern = r'com\.android\.pts\..*'
+ plan = tools.TestPlan(packages)
+ plan.Exclude('.*')
+ plan.Include(ptsPattern)
+ self.__WritePlan(plan, 'PTS')
plan = tools.TestPlan(packages)
plan.Exclude('android\.performance.*')
self.__WritePlan(plan, 'CTS')
self.__WritePlan(plan, 'CTS-TF')
+ plan = tools.TestPlan(packages)
+ plan.Exclude(ptsPattern)
+ plan.Exclude('android\.performance.*')
+ self.__WritePlan(plan, 'SDK')
+
plan.Exclude(r'android\.tests\.sigtest')
plan.Exclude(r'android\.core.*')
self.__WritePlan(plan, 'Android')
plan = tools.TestPlan(packages)
+ plan.Exclude(ptsPattern)
plan.Include(r'android\.core\.tests.*')
self.__WritePlan(plan, 'Java')
plan = tools.TestPlan(packages)
+ plan.Exclude(ptsPattern)
plan.Include(r'android\.core\.vm-tests-tf')
self.__WritePlan(plan, 'VM-TF')
plan = tools.TestPlan(packages)
+ plan.Exclude(ptsPattern)
plan.Include(r'android\.tests\.sigtest')
self.__WritePlan(plan, 'Signature')
plan = tools.TestPlan(packages)
+ plan.Exclude(ptsPattern)
plan.Include(r'android\.tests\.appsecurity')
self.__WritePlan(plan, 'AppSecurity')
@@ -165,9 +169,8 @@
if __name__ == '__main__':
builder = CtsBuilder(sys.argv)
- if builder.isCts:
- result = builder.GenerateTestDescriptions()
- if result != 0:
- sys.exit(result)
+ result = builder.GenerateTestDescriptions()
+ if result != 0:
+ sys.exit(result)
builder.GenerateTestPlans()