am da1bdb40: am 5cd66baf: Merge "Add a test for a dalvik interface cache bug."
* commit 'da1bdb40e08de6ceae1b6e6735ff3549b937f26e':
Add a test for a dalvik interface cache bug.
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..933a961 100644
--- a/CtsTestCaseList.mk
+++ b/CtsTestCaseList.mk
@@ -38,6 +38,7 @@
CtsTestStubs \
SignatureTest \
TestDeviceSetup \
+ CtsUiAutomatorApp \
$(cts_security_apps_list)
cts_external_packages := \
@@ -62,8 +63,10 @@
CtsCalendarcommon2TestCases \
CtsContentTestCases \
CtsDatabaseTestCases \
+ CtsDisplayTestCases \
CtsDpiTestCases \
CtsDpiTestCases2 \
+ CtsDreamsTestCases \
CtsDrmTestCases \
CtsEffectTestCases \
CtsExampleTestCases \
@@ -76,6 +79,7 @@
CtsLocationTestCases \
CtsMediaStressTestCases \
CtsMediaTestCases \
+ CtsNativeOpenGLTestCases \
CtsNdefTestCases \
CtsNetTestCases \
CtsOpenGLTestCases \
@@ -115,17 +119,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/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_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..ac338dd 100755
--- a/libs/testserver/src/android/webkit/cts/CtsTestServer.java
+++ b/libs/testserver/src/android/webkit/cts/CtsTestServer.java
@@ -544,12 +544,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()) {
diff --git a/libs/wrappedgtest/Android.mk b/libs/wrappedgtest/Android.mk
new file mode 100644
index 0000000..f89ba9d
--- /dev/null
+++ b/libs/wrappedgtest/Android.mk
@@ -0,0 +1,25 @@
+# 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_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_MODULE_TAGS := optional
+
+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/PtsBuild.mk b/suite/pts/PtsBuild.mk
index 18306b9..23e6d58 100644
--- a/suite/pts/PtsBuild.mk
+++ b/suite/pts/PtsBuild.mk
@@ -22,7 +22,8 @@
PTS_TEST_PACKAGES := \
PtsDeviceFilePerf \
PtsDeviceUi \
- PtsDeviceDram
+ PtsDeviceDram \
+ PtsDeviceSimpleCpu
PTS_SUPPORT_PACKAGES := \
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..d675edc 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,7 @@
import android.util.Log;
import android.view.WindowManager;
+import com.android.pts.util.PerfResultType;
import com.android.pts.util.PtsAndroidTestCase;
import com.android.pts.util.ReportLog;
import com.android.pts.util.Stat;
@@ -109,6 +110,6 @@
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);
+ PerfResultType.HIGHER_BETTER, screensPerSecStddev);
}
}
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..1e39441 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
@@ -28,6 +28,7 @@
import com.android.pts.util.MeasureRun;
import com.android.pts.util.MeasureTime;
+import com.android.pts.util.PerfResultType;
import com.android.pts.util.ReportLog;
import com.android.pts.util.Stat;
import com.android.pts.util.SystemUtil;
@@ -303,7 +304,7 @@
report.printArray("Rd amount", rdAmount, true);
Stat.StatResult stat = Stat.getStat(mbps);
- report.printSummary("MB/s", stat.mAverage, stat.mStddev);
+ report.printSummary("MB/s", stat.mAverage, PerfResultType.HIGHER_BETTER, stat.mStddev);
}
/**
@@ -353,7 +354,7 @@
report.printArray("Wr amount", wrAmount, true);
Stat.StatResult stat = Stat.getStat(mbps);
- report.printSummary("MB/s", stat.mAverage, stat.mStddev);
+ report.printSummary("MB/s", stat.mAverage, PerfResultType.HIGHER_BETTER, stat.mStddev);
}
/**
@@ -391,6 +392,6 @@
ReportLog.copyArray(mbps, mbpsAll, i * numberRepeatInOneRun);
}
Stat.StatResult stat = Stat.getStat(mbpsAll);
- report.printSummary("MB/s", stat.mAverage, stat.mStddev);
+ report.printSummary("MB/s", stat.mAverage, PerfResultType.HIGHER_BETTER, stat.mStddev);
}
}
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..52dcb16 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,7 @@
import android.cts.util.TimeoutReq;
import com.android.pts.util.MeasureRun;
import com.android.pts.util.MeasureTime;
+import com.android.pts.util.PerfResultType;
import com.android.pts.util.PtsAndroidTestCase;
import com.android.pts.util.ReportLog;
import com.android.pts.util.Stat;
@@ -65,7 +66,8 @@
mbps, true);
getReportLog().printArray("Wr amount", wrAmount, true);
Stat.StatResult stat = Stat.getStat(mbps);
- getReportLog().printSummary("MB/s", stat.mAverage, stat.mStddev);
+ getReportLog().printSummary("MB/s", stat.mAverage, PerfResultType.HIGHER_BETTER,
+ stat.mStddev);
}
@TimeoutReq(minutes = 60)
@@ -106,6 +108,7 @@
getReportLog().printArray("read MB/s",
mbps, true);
Stat.StatResult stat = Stat.getStat(mbps);
- getReportLog().printSummary("MB/s", stat.mAverage, stat.mStddev);
+ getReportLog().printSummary("MB/s", stat.mAverage, PerfResultType.HIGHER_BETTER,
+ stat.mStddev);
}
}
diff --git a/suite/pts/deviceTests/simplecpu/Android.mk b/suite/pts/deviceTests/simplecpu/Android.mk
new file mode 100644
index 0000000..988ddc3
--- /dev/null
+++ b/suite/pts/deviceTests/simplecpu/Android.mk
@@ -0,0 +1,35 @@
+# 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)
+
+# don't include this package in any target
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_JAVA_LIBRARIES := android.test.runner
+
+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 := PtsDeviceSimpleCpu
+
+LOCAL_SDK_VERSION := 16
+
+include $(BUILD_PTS_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/deviceTests/simplecpu/jni/Android.mk b/suite/pts/deviceTests/simplecpu/jni/Android.mk
new file mode 100644
index 0000000..9c34e0e
--- /dev/null
+++ b/suite/pts/deviceTests/simplecpu/jni/Android.mk
@@ -0,0 +1,31 @@
+# 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_MODULE := libptscpu_jni
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SRC_FILES := CpuNativeJni.cpp
+
+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..ecf56e6
--- /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
+ */
+long currentTimeMillis()
+{
+ struct timeval tv;
+ gettimeofday(&tv, (struct timezone *) NULL);
+ return (long)tv.tv_sec * 1000 + tv.tv_usec / 1000;
+}
+
+/**
+ * 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 jlong 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;
+ }
+ long totalTime = 0;
+ for (int i = 0; i < repetition; i++) {
+ randomInitArray<int>(data, numberElements, 0);
+ long start = currentTimeMillis();
+ qsort_local(data, numberElements, sizeof(int), cmpint);
+ long 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 jlong 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;
+ }
+ long totalTime = 0;
+ for (int i = 0; i < repetition; i++) {
+ randomInitArray<float>(A, n * n, 0);
+ randomInitArray<float>(B, n * n, 1);
+ long start = currentTimeMillis();
+ doMatrixMultiplication(A, B, C, n);
+ long 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..666ff88
--- /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 spent in sorting in ms.
+ */
+ public static native long 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
+ */
+ public static native long 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..4231250
--- /dev/null
+++ b/suite/pts/deviceTests/simplecpu/src/com/android/pts/simplecpu/SimpleCpuTest.java
@@ -0,0 +1,123 @@
+/*
+ * 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.PerfResultType;
+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("ms", result, false);
+ Stat.StatResult stat = Stat.getStat(result);
+ getReportLog().printSummary("ms", stat.mAverage, PerfResultType.LOWER_BETTER,
+ stat.mStddev);
+ }
+
+ /**
+ * 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("ms", result, false);
+ Stat.StatResult stat = Stat.getStat(result);
+ getReportLog().printSummary("ms", stat.mAverage, PerfResultType.LOWER_BETTER,
+ stat.mStddev);
+ }
+
+}
diff --git a/suite/pts/deviceTests/ui/Android.mk b/suite/pts/deviceTests/ui/Android.mk
index 28db8a1..731cbc7 100644
--- a/suite/pts/deviceTests/ui/Android.mk
+++ b/suite/pts/deviceTests/ui/Android.mk
@@ -24,7 +24,13 @@
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
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..d6d5f06
--- /dev/null
+++ b/suite/pts/deviceTests/ui/src/com/android/pts/ui/FpsTest.java
@@ -0,0 +1,127 @@
+/*
+ * 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.PerfResultType;
+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 ms", intervals, false);
+ getReportLog().printArray("jankiness ms", jankiness, false);
+ getReportLog().printSummaryFull("Frame interval",
+ "max delay ms", maxDelay, PerfResultType.LOWER_BETTER,
+ "number of jank", jankNumber, PerfResultType.LOWER_BETTER);
+ }
+}
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..5324669 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,7 @@
import android.cts.util.TimeoutReq;
import com.android.pts.util.MeasureRun;
import com.android.pts.util.MeasureTime;
+import com.android.pts.util.PerfResultType;
import com.android.pts.util.PtsActivityInstrumentationTestCase2;
import com.android.pts.util.ReportLog;
import com.android.pts.util.Stat;
@@ -67,6 +68,7 @@
});
getReportLog().printArray("ms", results, false);
Stat.StatResult stat = Stat.getStat(results);
- getReportLog().printSummary("Time ms", stat.mAverage, stat.mStddev);
+ getReportLog().printSummary("Time ms", stat.mAverage, PerfResultType.LOWER_BETTER,
+ stat.mStddev);
}
}
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..2e99bb8 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,7 @@
import android.cts.util.TimeoutReq;
import com.android.pts.util.MeasureRun;
import com.android.pts.util.MeasureTime;
+import com.android.pts.util.PerfResultType;
import com.android.pts.util.ReportLog;
import com.android.pts.util.Stat;
import com.android.pts.util.Stat.StatResult;
@@ -67,7 +68,7 @@
});
mReport.printArray("time in ms", result, false);
StatResult stat = Stat.getStat(result);
- mReport.printSummary("time in ms", stat.mAverage, stat.mStddev);
+ mReport.printSummary("time in ms", stat.mAverage, PerfResultType.LOWER_BETTER, stat.mStddev);
}
private void rebootDevice() throws DeviceNotAvailableException {
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
index b5400ab..54f2eb0 100644
--- a/suite/pts/hostTests/browser/src/com/android/pts/browser/BrowserTest.java
+++ b/suite/pts/hostTests/browser/src/com/android/pts/browser/BrowserTest.java
@@ -22,6 +22,7 @@
import com.android.ddmlib.Log;
import com.android.ddmlib.testrunner.RemoteAndroidTestRunner;
import com.android.pts.ptsutil.LogcatLineReceiver;
+import com.android.pts.util.PerfResultType;
import com.android.pts.util.ReportLog;
import com.android.pts.util.Stat;
import com.android.tradefed.build.IBuildInfo;
@@ -98,7 +99,8 @@
}
mReport.printArray("scores", results, true);
Stat.StatResult stat = Stat.getStat(results);
- mReport.printSummary("Score", stat.mAverage, stat.mStddev);
+ // Octane score is higher-better
+ mReport.printSummary("Score", stat.mAverage, PerfResultType.HIGHER_BETTER, stat.mStddev);
}
private double runBenchmarking(String testMethodName, String resultPattern,
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..da9d8e2 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,7 @@
import com.android.pts.util.MeasureRun;
import com.android.pts.util.MeasureTime;
+import com.android.pts.util.PerfResultType;
import com.android.pts.util.PtsAndroidTestCase;
import com.android.pts.util.Stat;
@@ -83,7 +84,8 @@
});
getReportLog().printArray("ms", results, false);
Stat.StatResult stat = Stat.getStat(results);
- getReportLog().printSummary("Time ms", stat.mAverage, stat.mStddev);
+ getReportLog().printSummary("Time ms", stat.mAverage, PerfResultType.LOWER_BETTER,
+ stat.mStddev);
}
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..dedf19c 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
@@ -24,6 +24,7 @@
import com.android.ddmlib.testrunner.TestIdentifier;
import com.android.pts.util.MeasureRun;
import com.android.pts.util.MeasureTime;
+import com.android.pts.util.PerfResultType;
import com.android.pts.util.PtsException;
import com.android.pts.util.ReportLog;
import com.android.pts.util.Stat;
@@ -91,7 +92,8 @@
});
mReport.printArray("time in ms", result, false);
StatResult stat = Stat.getStat(result);
- mReport.printSummary("time in ms", stat.mAverage, stat.mStddev);
+ mReport.printSummary("time in ms", stat.mAverage, PerfResultType.LOWER_BETTER,
+ stat.mStddev);
}
}
diff --git a/suite/pts/lib/commonutil/src/com/android/pts/util/PerfResultType.java b/suite/pts/lib/commonutil/src/com/android/pts/util/PerfResultType.java
new file mode 100644
index 0000000..0d0f13d
--- /dev/null
+++ b/suite/pts/lib/commonutil/src/com/android/pts/util/PerfResultType.java
@@ -0,0 +1,28 @@
+/*
+ * 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 distinguish performance results.
+ * HIGHER_BETTER: better performance with higher value
+ * LOWER_BETTER: better performance with lower value
+ *
+ */
+public enum PerfResultType {
+ LOWER_BETTER,
+ HIGHER_BETTER;
+}
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..721a196 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
@@ -39,6 +39,7 @@
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.
@@ -72,9 +73,33 @@
printLog(builder.toString());
}
- public void printSummary(String header, double average, double stddev) {
- mSummary = header + LOG_ELEM_SEPARATOR + "average " + average + LOG_ELEM_SEPARATOR +
- "stddev " + stddev;
+ /**
+ * For standard report summary with average and stddev
+ * @param header
+ * @param average
+ * @param type type of average value. stddev does not need type.
+ * @param stddev
+ */
+ public void printSummary(String header, double average, PerfResultType type, double stddev) {
+ mSummary = header + LOG_ELEM_SEPARATOR + "average " + average + " " + type.ordinal() +
+ LOG_ELEM_SEPARATOR + "stddev " + stddev + " " +
+ PerfResultType.LOWER_BETTER.ordinal();
+ }
+
+ /**
+ * For a report with two representative values with custom key strings
+ * @param header
+ * @param key1 String key for val1
+ * @param val1
+ * @param val1type
+ * @param key2 String key for val2
+ * @param val2
+ * @param val2type
+ */
+ public void printSummaryFull(String header, String key1, double val1, PerfResultType val1type,
+ String key2, double val2, PerfResultType val2type) {
+ mSummary = header + LOG_ELEM_SEPARATOR + key1 + " " + val1 + " " + val1type.ordinal() +
+ LOG_ELEM_SEPARATOR + key2 + " " + val2 + " " + val2type.ordinal();
}
public void throwReportToHost() throws PtsException {
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..e1753e5 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
diff --git a/tests/res/layout/gridlayout_layout.xml b/tests/res/layout/gridlayout_layout.xml
new file mode 100644
index 0000000..54b3b2c
--- /dev/null
+++ b/tests/res/layout/gridlayout_layout.xml
@@ -0,0 +1,23 @@
+<?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.
+ -->
+<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/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/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/display/Android.mk b/tests/tests/display/Android.mk
new file mode 100644
index 0000000..ec5b40d
--- /dev/null
+++ b/tests/tests/display/Android.mk
@@ -0,0 +1,37 @@
+# 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)
+
+# 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 := ctstestrunner
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_PACKAGE_NAME := CtsDisplayTestCases
+
+LOCAL_SDK_VERSION := current
+
+# 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 $(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/tests/tests/dreams/Android.mk b/tests/tests/dreams/Android.mk
new file mode 100644
index 0000000..c630d8a
--- /dev/null
+++ b/tests/tests/dreams/Android.mk
@@ -0,0 +1,37 @@
+# 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_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 := ctstestrunner
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+# Need access to ServiceManager
+#LOCAL_SDK_VERSION := current
+
+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/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/tests/nativeopengl/libnativeopengltests/GLTestHelper.cpp b/tests/tests/nativeopengl/libnativeopengltests/GLTestHelper.cpp
new file mode 100644
index 0000000..ed527a6
--- /dev/null
+++ b/tests/tests/nativeopengl/libnativeopengltests/GLTestHelper.cpp
@@ -0,0 +1,29 @@
+/*
+ * 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 "GLTestHelper.h"
+
+using namespace android;
+
+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/openglperf/Android.mk b/tests/tests/openglperf/Android.mk
index 2d1767e..548335b 100644
--- a/tests/tests/openglperf/Android.mk
+++ b/tests/tests/openglperf/Android.mk
@@ -25,6 +25,8 @@
LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner
+LOCAL_JNI_SHARED_LIBRARIES := libctsopenglperf_jni
+
LOCAL_SRC_FILES := $(call all-java-files-under, src)
LOCAL_PACKAGE_NAME := CtsOpenGlPerfTestCases
@@ -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/tests/tests/openglperf/jni/Android.mk b/tests/tests/openglperf/jni/Android.mk
new file mode 100644
index 0000000..77db1df
--- /dev/null
+++ b/tests/tests/openglperf/jni/Android.mk
@@ -0,0 +1,31 @@
+# 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_MODULE := libctsopenglperf_jni
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SRC_FILES := OpenGlPerfNativeJni.cpp
+
+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..155ddeb 100644
--- a/tests/tests/openglperf/src/android/openglperf/cts/PlanetsRenderer.java
+++ b/tests/tests/openglperf/src/android/openglperf/cts/PlanetsRenderer.java
@@ -20,7 +20,6 @@
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,13 +33,16 @@
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;
@@ -65,9 +67,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;
@@ -104,6 +108,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 +128,7 @@
measureTime("construction");
}
+ @Override
public void onSurfaceCreated(GL10 glUnused, EGLConfig config) {
mProgram = createProgram(getVertexShader(), getFragmentShader());
if (mProgram == 0) {
@@ -137,31 +145,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 +221,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..f64cc3f 100644
--- a/tests/tests/openglperf/src/android/openglperf/cts/PlanetsSurfaceView.java
+++ b/tests/tests/openglperf/src/android/openglperf/cts/PlanetsSurfaceView.java
@@ -17,9 +17,9 @@
package android.openglperf.cts;
import android.content.Context;
-import android.opengl.GLSurfaceView;
-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);
@@ -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/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/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..1c34087 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/AllocationTest.java
+++ b/tests/tests/renderscript/src/android/renderscript/cts/AllocationTest.java
@@ -188,10 +188,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);
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/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/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..7af4243 100644
--- a/tests/tests/security/src/android/security/cts/CertificateData.java
+++ b/tests/tests/security/src/android/security/cts/CertificateData.java
@@ -48,6 +48,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 +65,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 +100,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",
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/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/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..c4defca
--- /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 receivedRequest = false;
+ private final boolean accept;
+ private final boolean retain;
+
+ public TestSimpleGeolocationRequestWebChromeClient(
+ WebViewOnUiThread webViewOnUiThread, boolean accept, boolean retain) {
+ super(webViewOnUiThread);
+ this.accept = accept;
+ this.retain = retain;
+ }
+
+ @Override
+ public void onGeolocationPermissionsShowPrompt(
+ String origin, GeolocationPermissions.Callback callback) {
+ receivedRequest = true;
+ callback.invoke(origin, accept, retain);
+ }
+ }
+
+ 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.receivedRequest;
+ }
+ };
+ PollingCheck.check("Geolocation prompt called", POLLING_TIMEOUT, receivedRequest);
+ Callable<Boolean> receivedLocation = new Callable<Boolean>() {
+ @Override
+ public Boolean call() {
+ return mJavascriptStatusReceiver.hasPosition;
+ }
+ };
+ PollingCheck.check("JS got position", POLLING_TIMEOUT, receivedLocation);
+ chromeClientAcceptOnce.receivedRequest = false;
+ // Load URL again, should receive callback again
+ loadUrlAndUpdateLocation(URL_1);
+ PollingCheck.check("Geolocation prompt called", POLLING_TIMEOUT, receivedRequest);
+ PollingCheck.check("JS got 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 received = false;
+ private final T expectedValue;
+ private T receivedValue = null;
+
+ public ValueCheck(T val) {
+ expectedValue = val;
+ }
+
+ @Override
+ protected boolean check() {
+ return received && expectedValue.equals(receivedValue);
+ }
+ @Override
+ public void onReceiveValue(T value) {
+ received = true;
+ receivedValue = 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.receivedRequest;
+ }
+ };
+ PollingCheck.check("Geolocation prompt called", POLLING_TIMEOUT, receivedRequest);
+ Callable<Boolean> receivedLocation = new Callable<Boolean>() {
+ @Override
+ public Boolean call() {
+ return mJavascriptStatusReceiver.hasPosition;
+ }
+ };
+ PollingCheck.check("JS got position", POLLING_TIMEOUT, receivedLocation);
+ chromeClientAcceptAlways.receivedRequest = false;
+ mJavascriptStatusReceiver.clearState();
+ // Load the same URL again
+ loadUrlAndUpdateLocation(URL_1);
+ PollingCheck.check("JS got position", POLLING_TIMEOUT, receivedLocation);
+ // Assert prompt for geolocation permission is not called the second time
+ assertFalse(chromeClientAcceptAlways.receivedRequest);
+ // 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.receivedRequest = false;
+ loadUrlAndUpdateLocation(URL_2);
+ // Checking the callback for geolocation permission prompt is called
+ PollingCheck.check("Geolocation prompt called", POLLING_TIMEOUT, receivedRequest);
+ PollingCheck.check("JS got 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.receivedRequest;
+ }
+ };
+ PollingCheck.check("Geolocation prompt called", POLLING_TIMEOUT, receivedRequest);
+ Callable<Boolean> locationDenied = new Callable<Boolean>() {
+ @Override
+ public Boolean call() {
+ return mJavascriptStatusReceiver.denied;
+ }
+ };
+ PollingCheck.check("JS got position", POLLING_TIMEOUT, locationDenied);
+ // Same result should happen on next run
+ mOnUiThread.loadUrlAndWaitForCompletion(URL_1);
+ PollingCheck.check("Geolocation prompt called", POLLING_TIMEOUT, receivedRequest);
+ PollingCheck.check("JS got 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 called", POLLING_TIMEOUT, receivedRequest);
+ PollingCheck.check("JS got position", POLLING_TIMEOUT, locationDenied);
+ // second load should now not get a prompt
+ chromeClientRejectAlways.receivedRequest = false;
+ mOnUiThread.loadUrlAndWaitForCompletion(URL_2);
+ PollingCheck.check("JS got position", POLLING_TIMEOUT, locationDenied);
+ PollingCheck.check("Geolocation prompt 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 hasPosition = false;
+ public volatile boolean denied = false;
+ public volatile boolean unavailable = false;
+ public volatile boolean timeout = false;
+
+ public void clearState() {
+ hasPosition = false;
+ denied = false;
+ unavailable = false;
+ timeout = false;
+ }
+
+ @JavascriptInterface
+ public void errorDenied() {
+ denied = true;
+ }
+
+ @JavascriptInterface
+ public void errorUnavailable() {
+ unavailable = true;
+ }
+
+ @JavascriptInterface
+ public void errorTimeout() {
+ timeout = true;
+ }
+
+ @JavascriptInterface
+ public void gotLocation() {
+ hasPosition = 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..42991d8 100644
--- a/tests/tests/webkit/src/android/webkit/cts/WebSettingsTest.java
+++ b/tests/tests/webkit/src/android/webkit/cts/WebSettingsTest.java
@@ -164,7 +164,7 @@
assertEquals(1, mSettings.getUserAgent());
mOnUiThread.loadUrlAndWaitForCompletion(url);
String userAgent1 = mOnUiThread.getTitle();
- assertNotNull(userAgent1);
+ assertEquals(userAgent1, mSettings.getUserAgentString());
mSettings.setUserAgent(3);
assertEquals(1, mSettings.getUserAgent());
@@ -175,7 +175,7 @@
assertEquals(2, mSettings.getUserAgent());
mOnUiThread.loadUrlAndWaitForCompletion(url);
String userAgent2 = mOnUiThread.getTitle();
- assertNotNull(userAgent2);
+ assertEquals(userAgent2, mSettings.getUserAgentString());
mSettings.setUserAgent(3);
assertEquals(2, mSettings.getUserAgent());
@@ -186,7 +186,7 @@
assertEquals(0, mSettings.getUserAgent());
mOnUiThread.loadUrlAndWaitForCompletion(url);
String userAgent0 = mOnUiThread.getTitle();
- assertNotNull(userAgent0);
+ assertEquals(userAgent0, mSettings.getUserAgentString());
final String customUserAgent = "Cts/Test";
mSettings.setUserAgentString(customUserAgent);
@@ -387,11 +387,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 +397,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 +409,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 +419,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() {
@@ -727,7 +718,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..07a54d1 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());
@@ -762,7 +774,6 @@
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 +799,6 @@
@UiThreadTest
public void testLoadDataWithBaseUrl() throws Throwable {
- assertNull(mWebView.getTitle());
assertNull(mWebView.getUrl());
String imgUrl = TestHtmlConstants.SMALL_IMG_URL; // relative
@@ -1024,40 +1034,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();
@@ -1475,21 +1451,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 +1490,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 +1676,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() {
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/uiautomator/Android.mk b/tests/uiautomator/Android.mk
new file mode 100644
index 0000000..b5bfae3
--- /dev/null
+++ b/tests/uiautomator/Android.mk
@@ -0,0 +1,34 @@
+# 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_MODULE_TAGS := optional
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA)/local/tmp
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_MODULE := CtsUiAutomatorTests
+LOCAL_JAVA_LIBRARIES := uiautomator_sdk_v16
+LOCAL_PROGUARD_ENABLED := disabled
+LOCAL_CTS_TEST_APK := CtsUiAutomatorApp
+LOCAL_CTS_TEST_APP_PACKAGE := com.android.cts.uiautomator
+LOCAL_CTS_TEST_PACKAGE := android.uiautomator
+
+include $(BUILD_CTS_UI_JAVA_LIBRARY)
+
+# Build the test APK using its own makefile, and any other CTS-related packages
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/tests/uiautomator/test-apps/Android.mk b/tests/uiautomator/test-apps/Android.mk
new file mode 100644
index 0000000..e790e1e
--- /dev/null
+++ b/tests/uiautomator/test-apps/Android.mk
@@ -0,0 +1,15 @@
+# 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 $(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 73ab901..e3689f7 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/tools/cts-native-scanner/tests/Android.mk b/tools/cts-native-scanner/tests/Android.mk
new file mode 100644
index 0000000..6044b0d
--- /dev/null
+++ b/tools/cts-native-scanner/tests/Android.mk
@@ -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.
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+# Only compile source java files in this lib
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_MODULE := cts-native-scanner-tests
+LOCAL_MODULE_TAGS := optional
+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/res/report/cts_result.xsd b/tools/tradefed-host/res/report/cts_result.xsd
index 5e471d5..74eec37 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.14"
+ xmlns="http://compatibility.android.com/cts_result/1.14"
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>
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/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));
+ }
+}