summaryrefslogtreecommitdiffstats
path: root/tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests')
-rw-r--r--tests/BrowserTestPlugin/jni/PluginObject.h2
-rw-r--r--tests/BrowserTestPlugin/jni/event/EventPlugin.cpp2
-rw-r--r--tests/BrowserTestPlugin/jni/event/EventPlugin.h2
-rw-r--r--tests/BrowserTestPlugin/jni/main.cpp20
-rw-r--r--tests/DumpRenderTree/AndroidManifest.xml9
-rwxr-xr-xtests/DumpRenderTree/assets/run_layout_tests.py2
-rw-r--r--tests/DumpRenderTree/src/com/android/dumprendertree/CallbackProxy.java34
-rw-r--r--tests/DumpRenderTree/src/com/android/dumprendertree/FileFilter.java51
-rw-r--r--tests/DumpRenderTree/src/com/android/dumprendertree/FsUtils.java15
-rw-r--r--tests/DumpRenderTree/src/com/android/dumprendertree/LayoutTestController.java54
-rw-r--r--tests/DumpRenderTree/src/com/android/dumprendertree/LayoutTestsAutoTest.java46
-rw-r--r--tests/DumpRenderTree/src/com/android/dumprendertree/Menu.java15
-rw-r--r--tests/DumpRenderTree/src/com/android/dumprendertree/TestShellActivity.java199
-rw-r--r--tests/DumpRenderTree2/Android.mk29
-rw-r--r--tests/DumpRenderTree2/AndroidManifest.xml58
-rwxr-xr-xtests/DumpRenderTree2/assets/run_apache2.py107
-rwxr-xr-xtests/DumpRenderTree2/assets/run_layout_tests.py65
-rw-r--r--tests/DumpRenderTree2/res/drawable/folder.pngbin0 -> 4865 bytes
-rw-r--r--tests/DumpRenderTree2/res/drawable/runtest.pngbin0 -> 3146 bytes
-rw-r--r--tests/DumpRenderTree2/res/layout/dirlist_row.xml43
-rw-r--r--tests/DumpRenderTree2/res/values/strings.xml28
-rw-r--r--tests/DumpRenderTree2/src/com/android/dumprendertree2/AbstractResult.java145
-rw-r--r--tests/DumpRenderTree2/src/com/android/dumprendertree2/AdditionalTextOutput.java118
-rw-r--r--tests/DumpRenderTree2/src/com/android/dumprendertree2/CrashedDummyResult.java85
-rw-r--r--tests/DumpRenderTree2/src/com/android/dumprendertree2/EventSender.java110
-rw-r--r--tests/DumpRenderTree2/src/com/android/dumprendertree2/EventSenderImpl.java563
-rw-r--r--tests/DumpRenderTree2/src/com/android/dumprendertree2/FileFilter.java288
-rw-r--r--tests/DumpRenderTree2/src/com/android/dumprendertree2/FsUtils.java78
-rw-r--r--tests/DumpRenderTree2/src/com/android/dumprendertree2/LayoutTestController.java100
-rw-r--r--tests/DumpRenderTree2/src/com/android/dumprendertree2/LayoutTestsExecutor.java603
-rw-r--r--tests/DumpRenderTree2/src/com/android/dumprendertree2/ManagerService.java249
-rw-r--r--tests/DumpRenderTree2/src/com/android/dumprendertree2/Summarizer.java391
-rw-r--r--tests/DumpRenderTree2/src/com/android/dumprendertree2/TestsListActivity.java173
-rw-r--r--tests/DumpRenderTree2/src/com/android/dumprendertree2/TestsListPreloaderThread.java121
-rw-r--r--tests/DumpRenderTree2/src/com/android/dumprendertree2/TextResult.java229
-rw-r--r--tests/DumpRenderTree2/src/com/android/dumprendertree2/VisualDiffUtils.java207
-rw-r--r--tests/DumpRenderTree2/src/com/android/dumprendertree2/scriptsupport/OnEverythingFinishedCallback.java25
-rw-r--r--tests/DumpRenderTree2/src/com/android/dumprendertree2/scriptsupport/ScriptTestRunner.java37
-rw-r--r--tests/DumpRenderTree2/src/com/android/dumprendertree2/scriptsupport/Starter.java74
-rw-r--r--tests/DumpRenderTree2/src/com/android/dumprendertree2/ui/DirListActivity.java404
-rw-r--r--tests/HwAccelerationTest/Android.mk26
-rw-r--r--tests/HwAccelerationTest/AndroidManifest.xml212
-rw-r--r--tests/HwAccelerationTest/res/drawable-hdpi/icon.pngbin0 -> 5141 bytes
-rw-r--r--tests/HwAccelerationTest/res/drawable-hdpi/sunset1.jpgbin0 -> 25019 bytes
-rw-r--r--tests/HwAccelerationTest/res/drawable-hdpi/sunset2.pngbin0 -> 55763 bytes
-rw-r--r--tests/HwAccelerationTest/res/drawable-hdpi/sunset3.pngbin0 -> 45781 bytes
-rw-r--r--tests/HwAccelerationTest/res/drawable/icon.pngbin0 -> 3133 bytes
-rw-r--r--tests/HwAccelerationTest/res/drawable/sunset1.jpgbin0 -> 25019 bytes
-rw-r--r--tests/HwAccelerationTest/res/drawable/sunset2.pngbin0 -> 55763 bytes
-rw-r--r--tests/HwAccelerationTest/res/drawable/sunset3.pngbin0 -> 45781 bytes
-rw-r--r--tests/HwAccelerationTest/res/layout/list_activity.xml78
-rw-r--r--tests/HwAccelerationTest/res/layout/stack.xml33
-rw-r--r--tests/HwAccelerationTest/res/layout/stack_item.xml38
-rw-r--r--tests/HwAccelerationTest/src/com/google/android/test/hwui/AdvancedBlendActivity.java138
-rw-r--r--tests/HwAccelerationTest/src/com/google/android/test/hwui/AlphaLayersActivity.java141
-rw-r--r--tests/HwAccelerationTest/src/com/google/android/test/hwui/BitmapsActivity.java96
-rw-r--r--tests/HwAccelerationTest/src/com/google/android/test/hwui/BitmapsAlphaActivity.java82
-rw-r--r--tests/HwAccelerationTest/src/com/google/android/test/hwui/BitmapsRectActivity.java77
-rw-r--r--tests/HwAccelerationTest/src/com/google/android/test/hwui/ColorFiltersActivity.java97
-rw-r--r--tests/HwAccelerationTest/src/com/google/android/test/hwui/LayersActivity.java110
-rw-r--r--tests/HwAccelerationTest/src/com/google/android/test/hwui/LinesActivity.java109
-rw-r--r--tests/HwAccelerationTest/src/com/google/android/test/hwui/ListActivity.java116
-rw-r--r--tests/HwAccelerationTest/src/com/google/android/test/hwui/MoreShadersActivity.java141
-rw-r--r--tests/HwAccelerationTest/src/com/google/android/test/hwui/NinePatchesActivity.java41
-rw-r--r--tests/HwAccelerationTest/src/com/google/android/test/hwui/PathsActivity.java144
-rw-r--r--tests/HwAccelerationTest/src/com/google/android/test/hwui/QuickRejectActivity.java89
-rw-r--r--tests/HwAccelerationTest/src/com/google/android/test/hwui/RotationActivity.java63
-rw-r--r--tests/HwAccelerationTest/src/com/google/android/test/hwui/ShadersActivity.java132
-rw-r--r--tests/HwAccelerationTest/src/com/google/android/test/hwui/SimplePathsActivity.java40
-rw-r--r--tests/HwAccelerationTest/src/com/google/android/test/hwui/StackActivity.java59
-rw-r--r--tests/HwAccelerationTest/src/com/google/android/test/hwui/TextActivity.java108
-rw-r--r--tests/HwAccelerationTest/src/com/google/android/test/hwui/Transform3dActivity.java97
-rw-r--r--tests/HwAccelerationTest/src/com/google/android/test/hwui/XfermodeActivity.java137
-rw-r--r--tests/StatusBar/res/drawable-hdpi/app_gmail.pngbin0 -> 8000 bytes
-rw-r--r--tests/StatusBar/res/drawable-hdpi/app_phone.pngbin0 -> 7459 bytes
-rw-r--r--tests/StatusBar/res/drawable-hdpi/ic_statusbar_chat.pngbin0 -> 2343 bytes
-rw-r--r--tests/StatusBar/res/drawable-hdpi/ic_statusbar_email.pngbin0 -> 2674 bytes
-rw-r--r--tests/StatusBar/res/drawable-hdpi/ic_statusbar_missedcall.pngbin0 -> 1989 bytes
-rw-r--r--tests/StatusBar/res/drawable-hdpi/icon1.pngbin0 -> 1363 bytes
-rw-r--r--tests/StatusBar/res/drawable-hdpi/icon2.pngbin0 -> 1532 bytes
-rw-r--r--tests/StatusBar/res/drawable-hdpi/icon3.pngbin0 -> 829 bytes
-rw-r--r--tests/StatusBar/res/drawable-hdpi/icon4.pngbin0 -> 525 bytes
-rw-r--r--tests/StatusBar/res/drawable-mdpi/app_gmail.png (renamed from tests/StatusBar/res/drawable/app_gmail.png)bin5405 -> 5405 bytes
-rw-r--r--tests/StatusBar/res/drawable-mdpi/app_phone.png (renamed from tests/StatusBar/res/drawable/app_phone.png)bin5443 -> 5443 bytes
-rw-r--r--tests/StatusBar/res/drawable-mdpi/ic_statusbar_chat.png (renamed from tests/StatusBar/res/drawable/ic_statusbar_chat.png)bin829 -> 829 bytes
-rw-r--r--tests/StatusBar/res/drawable-mdpi/ic_statusbar_email.png (renamed from tests/StatusBar/res/drawable/ic_statusbar_email.png)bin1077 -> 1077 bytes
-rw-r--r--tests/StatusBar/res/drawable-mdpi/ic_statusbar_missedcall.png (renamed from tests/StatusBar/res/drawable/ic_statusbar_missedcall.png)bin720 -> 720 bytes
-rw-r--r--tests/StatusBar/res/drawable-mdpi/icon1.png (renamed from tests/StatusBar/res/drawable/icon1.png)bin616 -> 616 bytes
-rw-r--r--tests/StatusBar/res/drawable-mdpi/icon2.png (renamed from tests/StatusBar/res/drawable/icon2.png)bin614 -> 614 bytes
-rw-r--r--tests/StatusBar/res/drawable-mdpi/icon3.png (renamed from tests/StatusBar/res/drawable/icon3.png)bin2969 -> 2969 bytes
-rw-r--r--tests/StatusBar/res/drawable-mdpi/icon4.png (renamed from tests/StatusBar/res/drawable/icon4.png)bin2918 -> 2918 bytes
-rw-r--r--tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java14
92 files changed, 7044 insertions, 155 deletions
diff --git a/tests/BrowserTestPlugin/jni/PluginObject.h b/tests/BrowserTestPlugin/jni/PluginObject.h
index a058d4a..037367e 100644
--- a/tests/BrowserTestPlugin/jni/PluginObject.h
+++ b/tests/BrowserTestPlugin/jni/PluginObject.h
@@ -65,7 +65,7 @@ class SubPlugin {
public:
SubPlugin(NPP inst) : m_inst(inst) {}
virtual ~SubPlugin() {}
- virtual int16 handleEvent(const ANPEvent* evt) = 0;
+ virtual int16_t handleEvent(const ANPEvent* evt) = 0;
NPP inst() const { return m_inst; }
diff --git a/tests/BrowserTestPlugin/jni/event/EventPlugin.cpp b/tests/BrowserTestPlugin/jni/event/EventPlugin.cpp
index 2eff394..91f1b3d 100644
--- a/tests/BrowserTestPlugin/jni/event/EventPlugin.cpp
+++ b/tests/BrowserTestPlugin/jni/event/EventPlugin.cpp
@@ -138,7 +138,7 @@ void EventPlugin::printToDiv(const char* text, int length) {
browser->memfree(beginMem);
}
-int16 EventPlugin::handleEvent(const ANPEvent* evt) {
+int16_t EventPlugin::handleEvent(const ANPEvent* evt) {
switch (evt->eventType) {
case kDraw_ANPEventType: {
diff --git a/tests/BrowserTestPlugin/jni/event/EventPlugin.h b/tests/BrowserTestPlugin/jni/event/EventPlugin.h
index 88b7c9d..043be85 100644
--- a/tests/BrowserTestPlugin/jni/event/EventPlugin.h
+++ b/tests/BrowserTestPlugin/jni/event/EventPlugin.h
@@ -32,7 +32,7 @@ class EventPlugin : public SubPlugin {
public:
EventPlugin(NPP inst);
virtual ~EventPlugin();
- virtual int16 handleEvent(const ANPEvent* evt);
+ virtual int16_t handleEvent(const ANPEvent* evt);
private:
void drawPlugin(const ANPBitmap& bitmap, const ANPRectI& clip);
diff --git a/tests/BrowserTestPlugin/jni/main.cpp b/tests/BrowserTestPlugin/jni/main.cpp
index 402a7e2..511180c 100644
--- a/tests/BrowserTestPlugin/jni/main.cpp
+++ b/tests/BrowserTestPlugin/jni/main.cpp
@@ -34,19 +34,19 @@
NPNetscapeFuncs* browser;
#define EXPORT __attribute__((visibility("default")))
-NPError NPP_New(NPMIMEType pluginType, NPP instance, uint16 mode, int16 argc,
+NPError NPP_New(NPMIMEType pluginType, NPP instance, uint16_t mode, int16_t argc,
char* argn[], char* argv[], NPSavedData* saved);
NPError NPP_Destroy(NPP instance, NPSavedData** save);
NPError NPP_SetWindow(NPP instance, NPWindow* window);
NPError NPP_NewStream(NPP instance, NPMIMEType type, NPStream* stream,
- NPBool seekable, uint16* stype);
+ NPBool seekable, uint16_t* stype);
NPError NPP_DestroyStream(NPP instance, NPStream* stream, NPReason reason);
-int32 NPP_WriteReady(NPP instance, NPStream* stream);
-int32 NPP_Write(NPP instance, NPStream* stream, int32 offset, int32 len,
+int32_t NPP_WriteReady(NPP instance, NPStream* stream);
+int32_t NPP_Write(NPP instance, NPStream* stream, int32_t offset, int32_t len,
void* buffer);
void NPP_StreamAsFile(NPP instance, NPStream* stream, const char* fname);
void NPP_Print(NPP instance, NPPrint* platformPrint);
-int16 NPP_HandleEvent(NPP instance, void* event);
+int16_t NPP_HandleEvent(NPP instance, void* event);
void NPP_URLNotify(NPP instance, const char* URL, NPReason reason,
void* notifyData);
NPError NPP_GetValue(NPP instance, NPPVariable variable, void *value);
@@ -129,7 +129,7 @@ const char *NP_GetMIMEDescription(void)
return "application/x-browsertestplugin:btp:Android Browser Test Plugin";
}
-NPError NPP_New(NPMIMEType pluginType, NPP instance, uint16 mode, int16 argc,
+NPError NPP_New(NPMIMEType pluginType, NPP instance, uint16_t mode, int16_t argc,
char* argn[], char* argv[], NPSavedData* saved)
{
@@ -188,7 +188,7 @@ NPError NPP_SetWindow(NPP instance, NPWindow* window)
return NPERR_NO_ERROR;
}
-NPError NPP_NewStream(NPP instance, NPMIMEType type, NPStream* stream, NPBool seekable, uint16* stype)
+NPError NPP_NewStream(NPP instance, NPMIMEType type, NPStream* stream, NPBool seekable, uint16_t* stype)
{
*stype = NP_ASFILEONLY;
return NPERR_NO_ERROR;
@@ -199,12 +199,12 @@ NPError NPP_DestroyStream(NPP instance, NPStream* stream, NPReason reason)
return NPERR_NO_ERROR;
}
-int32 NPP_WriteReady(NPP instance, NPStream* stream)
+int32_t NPP_WriteReady(NPP instance, NPStream* stream)
{
return 0;
}
-int32 NPP_Write(NPP instance, NPStream* stream, int32 offset, int32 len, void* buffer)
+int32_t NPP_Write(NPP instance, NPStream* stream, int32_t offset, int32_t len, void* buffer)
{
return 0;
}
@@ -217,7 +217,7 @@ void NPP_Print(NPP instance, NPPrint* platformPrint)
{
}
-int16 NPP_HandleEvent(NPP instance, void* event)
+int16_t NPP_HandleEvent(NPP instance, void* event)
{
PluginObject *obj = reinterpret_cast<PluginObject*>(instance->pdata);
const ANPEvent* evt = reinterpret_cast<const ANPEvent*>(event);
diff --git a/tests/DumpRenderTree/AndroidManifest.xml b/tests/DumpRenderTree/AndroidManifest.xml
index 03b7e26..c151251 100644
--- a/tests/DumpRenderTree/AndroidManifest.xml
+++ b/tests/DumpRenderTree/AndroidManifest.xml
@@ -18,15 +18,18 @@
<application android:name="HTMLHostApp">
<uses-library android:name="android.test.runner" />
<activity android:name="Menu" android:label="Dump Render Tree"
- android:screenOrientation="portrait">
+ android:screenOrientation="portrait"
+ android:theme="@android:style/Theme.Light">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.TEST" />
</intent-filter>
</activity>
<activity android:name="TestShellActivity" android:launchMode="singleTop"
- android:screenOrientation="portrait"/>
- <activity android:name="ReliabilityTestActivity" android:screenOrientation="portrait"/>
+ android:screenOrientation="portrait"
+ android:theme="@android:style/Theme.Light"/>
+ <activity android:name="ReliabilityTestActivity" android:screenOrientation="portrait"
+ android:theme="@android:style/Theme.Light"/>
</application>
<instrumentation android:name=".LayoutTestsAutoRunner"
diff --git a/tests/DumpRenderTree/assets/run_layout_tests.py b/tests/DumpRenderTree/assets/run_layout_tests.py
index b6e7bf3..ceac5d2 100755
--- a/tests/DumpRenderTree/assets/run_layout_tests.py
+++ b/tests/DumpRenderTree/assets/run_layout_tests.py
@@ -176,7 +176,7 @@ def main(options, args):
# Count crashed tests.
crashed_tests = []
- timeout_ms = '30000'
+ timeout_ms = '15000'
if options.time_out_ms:
timeout_ms = options.time_out_ms
diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/CallbackProxy.java b/tests/DumpRenderTree/src/com/android/dumprendertree/CallbackProxy.java
index 5780c43..740f544 100644
--- a/tests/DumpRenderTree/src/com/android/dumprendertree/CallbackProxy.java
+++ b/tests/DumpRenderTree/src/com/android/dumprendertree/CallbackProxy.java
@@ -74,6 +74,8 @@ public class CallbackProxy extends Handler implements EventSender, LayoutTestCon
private static final int LAYOUT_SET_CAN_OPEN_WINDOWS = 42;
private static final int SET_GEOLOCATION_PERMISSION = 43;
private static final int OVERRIDE_PREFERENCE = 44;
+ private static final int LAYOUT_DUMP_CHILD_FRAMES_TEXT = 45;
+ private static final int SET_XSS_AUDITOR_ENABLED = 46;
CallbackProxy(EventSender eventSender,
LayoutTestController layoutTestController) {
@@ -175,7 +177,11 @@ public class CallbackProxy extends Handler implements EventSender, LayoutTestCon
break;
case LAYOUT_DUMP_TEXT:
- mLayoutTestController.dumpAsText();
+ mLayoutTestController.dumpAsText(msg.arg1 == 1);
+ break;
+
+ case LAYOUT_DUMP_CHILD_FRAMES_TEXT:
+ mLayoutTestController.dumpChildFramesAsText();
break;
case LAYOUT_DUMP_HISTORY:
@@ -273,6 +279,10 @@ public class CallbackProxy extends Handler implements EventSender, LayoutTestCon
boolean value = msg.getData().getBoolean("value");
mLayoutTestController.overridePreference(key, value);
break;
+
+ case SET_XSS_AUDITOR_ENABLED:
+ mLayoutTestController.setXSSAuditorEnabled(msg.arg1 == 1);
+ break;
}
}
@@ -377,7 +387,15 @@ public class CallbackProxy extends Handler implements EventSender, LayoutTestCon
}
public void dumpAsText() {
- obtainMessage(LAYOUT_DUMP_TEXT).sendToTarget();
+ obtainMessage(LAYOUT_DUMP_TEXT, 0).sendToTarget();
+ }
+
+ public void dumpAsText(boolean enablePixelTests) {
+ obtainMessage(LAYOUT_DUMP_TEXT, enablePixelTests ? 1 : 0).sendToTarget();
+ }
+
+ public void dumpChildFramesAsText() {
+ obtainMessage(LAYOUT_DUMP_CHILD_FRAMES_TEXT).sendToTarget();
}
public void dumpBackForwardList() {
@@ -492,10 +510,22 @@ public class CallbackProxy extends Handler implements EventSender, LayoutTestCon
obtainMessage(SET_GEOLOCATION_PERMISSION, allow ? 1 : 0, 0).sendToTarget();
}
+ public void setMockDeviceOrientation(boolean canProvideAlpha, double alpha,
+ boolean canProvideBeta, double beta, boolean canProvideGamma, double gamma) {
+ // Configuration is in WebKit, so stay on WebCore thread, but go via the TestShellActivity
+ // as we need access to the Webview.
+ mLayoutTestController.setMockDeviceOrientation(canProvideAlpha, alpha, canProvideBeta, beta,
+ canProvideGamma, gamma);
+ }
+
public void overridePreference(String key, boolean value) {
Message message = obtainMessage(OVERRIDE_PREFERENCE);
message.getData().putString("key", key);
message.getData().putBoolean("value", value);
message.sendToTarget();
}
+
+ public void setXSSAuditorEnabled(boolean flag) {
+ obtainMessage(SET_XSS_AUDITOR_ENABLED, flag ? 1 : 0, 0).sendToTarget();
+ }
}
diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/FileFilter.java b/tests/DumpRenderTree/src/com/android/dumprendertree/FileFilter.java
index 77fd3ed..4cff3de 100644
--- a/tests/DumpRenderTree/src/com/android/dumprendertree/FileFilter.java
+++ b/tests/DumpRenderTree/src/com/android/dumprendertree/FileFilter.java
@@ -73,43 +73,74 @@ public class FileFilter {
static final String[] ignoreTestList = {
"editing/selection/move-left-right.html", // Causes DumpRenderTree to hang
+ "fast/js/excessive-comma-usage.html", // Tests huge initializer list, causes OOM.
"fast/js/regexp-charclass-crash.html", // RegExp is too large, causing OOM
"fast/regex/test1.html", // Causes DumpRenderTree to hang with V8
- "fast/regex/slow.html" // Causes DumpRenderTree to hang with V8
+ "fast/regex/slow.html", // Causes DumpRenderTree to hang with V8
+ "ietestcenter/Javascript/15.4.4.15-3-14.html", // hangs the layout tests
+ // http://b/issue?id=2889595
+ "ietestcenter/Javascript/15.4.4.15-3-29.html", // hangs the layout tests
+ // http://b/issue?id=2889596
+ "ietestcenter/Javascript/15.4.4.15-3-8.html" // hangs the layout tests
+ // http://b/issue?id=2889598
};
static void fillIgnoreResultList() {
- // This first block of tests are for HTML5 features, for which Android
+ // This first block of tests are for features for which Android
// should pass all tests. They are skipped only temporarily.
// TODO: Fix these failing tests and remove them from this list.
+ ignoreResultList.add("fast/events/touch/basic-multi-touch-events.html"); // Requires multi-touch
+ ignoreResultList.add("fast/events/touch/touch-target.html"); // Requires multi-touch
ignoreResultList.add("http/tests/appcache/empty-manifest.html"); // flaky
+ ignoreResultList.add("http/tests/appcache/fallback.html"); // http://b/issue?id=2713004
ignoreResultList.add("http/tests/appcache/foreign-iframe-main.html"); // flaky - skips states
ignoreResultList.add("http/tests/appcache/manifest-with-empty-file.html"); // flaky
+ ignoreResultList.add("http/tests/appcache/origin-quota.html"); // needs clearAllApplicationCaches(), see http://b/issue?id=2944196
ignoreResultList.add("storage/database-lock-after-reload.html"); // Succeeds but DumpRenderTree does not read result correctly
ignoreResultList.add("storage/hash-change-with-xhr.html"); // Succeeds but DumpRenderTree does not read result correctly
-
- // Will always fail
- ignoreResultList.add("dom/svg/level3/xpath"); // XPath not supported
+ ignoreResultList.add("storage/open-database-creation-callback-isolated-world.html"); // Requires layoutTestController.evaluateScriptInIsolatedWorld()
+ ignoreResultList.add("storage/statement-error-callback-isolated-world.html"); // Requires layoutTestController.evaluateScriptInIsolatedWorld()
+ ignoreResultList.add("storage/statement-success-callback-isolated-world.html"); // Requires layoutTestController.evaluateScriptInIsolatedWorld()
+ ignoreResultList.add("storage/transaction-callback-isolated-world.html"); // Requires layoutTestController.evaluateScriptInIsolatedWorld()
+ ignoreResultList.add("storage/transaction-error-callback-isolated-world.html"); // Requires layoutTestController.evaluateScriptInIsolatedWorld()
+ ignoreResultList.add("storage/transaction-success-callback-isolated-world.html"); // Requires layoutTestController.evaluateScriptInIsolatedWorld()
+
+ // Expected failures due to unsupported features.
+ ignoreResultList.add("fast/events/touch/touch-coords-in-zoom-and-scroll.html"); // Requires eventSender.zoomPageIn(),zoomPageOut()
ignoreResultList.add("fast/workers"); // workers not supported
- ignoreResultList.add("fast/xpath"); // XPath not supported
ignoreResultList.add("http/tests/eventsource/workers"); // workers not supported
ignoreResultList.add("http/tests/workers"); // workers not supported
ignoreResultList.add("http/tests/xmlhttprequest/workers"); // workers not supported
ignoreResultList.add("storage/domstorage/localstorage/private-browsing-affects-storage.html"); // private browsing not supported
ignoreResultList.add("storage/domstorage/sessionstorage/private-browsing-affects-storage.html"); // private browsing not supported
+ ignoreResultList.add("storage/indexeddb"); // indexeddb not supported
ignoreResultList.add("storage/private-browsing-readonly.html"); // private browsing not supported
ignoreResultList.add("websocket/tests/workers"); // workers not supported
+ // Expected failures due to missing expected results
+ ignoreResultList.add("dom/xhtml/level3/core/canonicalform08.xhtml");
+ ignoreResultList.add("dom/xhtml/level3/core/canonicalform09.xhtml");
+ ignoreResultList.add("dom/xhtml/level3/core/documentgetinputencoding03.xhtml");
+ ignoreResultList.add("dom/xhtml/level3/core/entitygetinputencoding02.xhtml");
+ ignoreResultList.add("dom/xhtml/level3/core/entitygetxmlversion02.xhtml");
+ ignoreResultList.add("dom/xhtml/level3/core/nodegetbaseuri05.xhtml");
+ ignoreResultList.add("dom/xhtml/level3/core/nodegetbaseuri07.xhtml");
+ ignoreResultList.add("dom/xhtml/level3/core/nodegetbaseuri09.xhtml");
+ ignoreResultList.add("dom/xhtml/level3/core/nodegetbaseuri10.xhtml");
+ ignoreResultList.add("dom/xhtml/level3/core/nodegetbaseuri11.xhtml");
+ ignoreResultList.add("dom/xhtml/level3/core/nodegetbaseuri15.xhtml");
+ ignoreResultList.add("dom/xhtml/level3/core/nodegetbaseuri17.xhtml");
+ ignoreResultList.add("dom/xhtml/level3/core/nodegetbaseuri18.xhtml");
+ ignoreResultList.add("dom/xhtml/level3/core/nodelookupnamespaceuri01.xhtml");
+ ignoreResultList.add("dom/xhtml/level3/core/nodelookupprefix19.xhtml");
+
// TODO: These need to be triaged
ignoreResultList.add("fast/css/case-transform.html"); // will not fix #619707
ignoreResultList.add("fast/dom/Element/offsetLeft-offsetTop-body-quirk.html"); // different screen size result in extra spaces in Apple compared to us
ignoreResultList.add("fast/dom/Window/Plug-ins.html"); // need test plugin
- ignoreResultList.add("fast/dom/Window/window-properties.html"); // xslt and xpath elements missing from property list
ignoreResultList.add("fast/dom/Window/window-screen-properties.html"); // pixel depth
ignoreResultList.add("fast/dom/Window/window-xy-properties.html"); // requires eventSender.mouseDown(),mouseUp()
ignoreResultList.add("fast/dom/attribute-namespaces-get-set.html"); // http://b/733229
- ignoreResultList.add("fast/dom/gc-9.html"); // requires xpath support
- ignoreResultList.add("fast/dom/global-constructors.html"); // requires xslt and xpath support
ignoreResultList.add("fast/dom/object-embed-plugin-scripting.html"); // dynamic plugins not supported
ignoreResultList.add("fast/dom/tabindex-clamp.html"); // there is extra spacing in the file due to multiple input boxes fitting on one line on Apple, ours are wrapped. Space at line ends are stripped.
ignoreResultList.add("fast/events/anchor-image-scrolled-x-y.html"); // requires eventSender.mouseDown(),mouseUp()
@@ -172,8 +203,6 @@ public class FileFilter {
ignoreResultList.add("fast/replaced/image-map.html"); // requires eventSender.mouseDown(),mouseUp()
ignoreResultList.add("fast/text/plain-text-line-breaks.html"); // extra spacing because iFrames rendered next to each other on Apple
ignoreResultList.add("profiler"); // profiler is not supported
- ignoreResultList.add("svg"); // svg is not supported
-
}
}
diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/FsUtils.java b/tests/DumpRenderTree/src/com/android/dumprendertree/FsUtils.java
index 6cfce41..5d34e25 100644
--- a/tests/DumpRenderTree/src/com/android/dumprendertree/FsUtils.java
+++ b/tests/DumpRenderTree/src/com/android/dumprendertree/FsUtils.java
@@ -49,8 +49,13 @@ public class FsUtils {
//no creation of instances
}
- public static void findLayoutTestsRecursively(BufferedOutputStream bos,
+ /**
+ * @return the number of tests in the list.
+ */
+ public static int writeLayoutTestListRecursively(BufferedOutputStream bos,
String dir, boolean ignoreResultsInDir) throws IOException {
+
+ int testCount = 0;
Log.v(LOGTAG, "Searching tests under " + dir);
File d = new File(dir);
@@ -68,7 +73,7 @@ public class FsUtils {
// If this is not a test directory, we don't recurse into it.
if (!FileFilter.isNonTestDir(s)) {
Log.v(LOGTAG, "Recursing on " + s);
- findLayoutTestsRecursively(bos, s, ignoreResultsInDir);
+ testCount += writeLayoutTestListRecursively(bos, s, ignoreResultsInDir);
}
continue;
}
@@ -79,7 +84,9 @@ public class FsUtils {
continue;
}
- if ((s.toLowerCase().endsWith(".html") || s.toLowerCase().endsWith(".xml"))
+ if ((s.toLowerCase().endsWith(".html")
+ || s.toLowerCase().endsWith(".xml")
+ || s.toLowerCase().endsWith(".xhtml"))
&& !s.endsWith("TEMPLATE.html")) {
Log.v(LOGTAG, "Recording " + s);
bos.write(s.getBytes());
@@ -88,8 +95,10 @@ public class FsUtils {
bos.write((" IGNORE_RESULT").getBytes());
}
bos.write('\n');
+ testCount++;
}
}
+ return testCount;
}
public static void updateTestStatus(String statusFile, String s) {
diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/LayoutTestController.java b/tests/DumpRenderTree/src/com/android/dumprendertree/LayoutTestController.java
index 9236345..9be2f1c 100644
--- a/tests/DumpRenderTree/src/com/android/dumprendertree/LayoutTestController.java
+++ b/tests/DumpRenderTree/src/com/android/dumprendertree/LayoutTestController.java
@@ -18,29 +18,30 @@ package com.android.dumprendertree;
public interface LayoutTestController {
- public void dumpAsText();
- public void waitUntilDone();
- public void notifyDone();
-
- // Force a redraw of the page
- public void display();
- // Used with pixel dumps of content
- public void testRepaint();
-
- // If the page title changes, add the information to the output.
- public void dumpTitleChanges();
- public void dumpBackForwardList();
- public void dumpChildFrameScrollPositions();
- public void dumpEditingCallbacks();
-
- // Show/Hide window for window.onBlur() testing
- public void setWindowIsKey(boolean b);
- // Mac function, used to disable events going to the window
- public void setMainFrameIsFirstResponder(boolean b);
-
- public void dumpSelectionRect();
-
- // invalidate and draw one line at a time of the web view.
+ public void dumpAsText(boolean enablePixelTests);
+ public void dumpChildFramesAsText();
+ public void waitUntilDone();
+ public void notifyDone();
+
+ // Force a redraw of the page
+ public void display();
+ // Used with pixel dumps of content
+ public void testRepaint();
+
+ // If the page title changes, add the information to the output.
+ public void dumpTitleChanges();
+ public void dumpBackForwardList();
+ public void dumpChildFrameScrollPositions();
+ public void dumpEditingCallbacks();
+
+ // Show/Hide window for window.onBlur() testing
+ public void setWindowIsKey(boolean b);
+ // Mac function, used to disable events going to the window
+ public void setMainFrameIsFirstResponder(boolean b);
+
+ public void dumpSelectionRect();
+
+ // invalidate and draw one line at a time of the web view.
public void repaintSweepHorizontally();
// History testing functions
@@ -67,4 +68,11 @@ public interface LayoutTestController {
public void setGeolocationPermission(boolean allow);
public void overridePreference(String key, boolean value);
+
+ // For XSSAuditor tests
+ public void setXSSAuditorEnabled(boolean flag);
+
+ // For DeviceOrientation tests
+ public void setMockDeviceOrientation(boolean canProvideAlpha, double alpha,
+ boolean canProvideBeta, double beta, boolean canProvideGamma, double gamma);
}
diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/LayoutTestsAutoTest.java b/tests/DumpRenderTree/src/com/android/dumprendertree/LayoutTestsAutoTest.java
index 9ccf549..132f17a 100644
--- a/tests/DumpRenderTree/src/com/android/dumprendertree/LayoutTestsAutoTest.java
+++ b/tests/DumpRenderTree/src/com/android/dumprendertree/LayoutTestsAutoTest.java
@@ -20,9 +20,7 @@ import com.android.dumprendertree.TestShellActivity.DumpDataType;
import com.android.dumprendertree.forwarder.AdbUtils;
import com.android.dumprendertree.forwarder.ForwardService;
-import android.app.Instrumentation;
import android.content.Intent;
-import android.os.Bundle;
import android.os.Environment;
import android.test.ActivityInstrumentationTestCase2;
import android.util.Log;
@@ -158,18 +156,11 @@ public class LayoutTestsAutoTest extends ActivityInstrumentationTestCase2<TestSh
private String mJsEngine;
private String mTestPathPrefix;
private boolean mFinished;
+ private int mTestCount;
+ private int mResumeIndex;
public LayoutTestsAutoTest() {
- super("com.android.dumprendertree", TestShellActivity.class);
- }
-
- // This function writes the result of the layout test to
- // Am status so that it can be picked up from a script.
- private void passOrFailCallback(String file, boolean result) {
- Instrumentation inst = getInstrumentation();
- Bundle bundle = new Bundle();
- bundle.putBoolean(file, result);
- inst.sendStatus(0, bundle);
+ super(TestShellActivity.class);
}
private void getTestList() {
@@ -190,6 +181,7 @@ public class LayoutTestsAutoTest extends ActivityInstrumentationTestCase2<TestSh
} catch (Exception e) {
Log.e(LOGTAG, "Error while reading test list : " + e.getMessage());
}
+ mTestCount = mTestList.size();
}
private void resumeTestList() {
@@ -200,6 +192,7 @@ public class LayoutTestsAutoTest extends ActivityInstrumentationTestCase2<TestSh
if (mTestList.elementAt(i).equals(line)) {
mTestList = new Vector<String>(mTestList.subList(i+1, mTestList.size()));
mTestListIgnoreResult = new Vector<Boolean>(mTestListIgnoreResult.subList(i+1, mTestListIgnoreResult.size()));
+ mResumeIndex = i + 1;
break;
}
}
@@ -232,14 +225,22 @@ public class LayoutTestsAutoTest extends ActivityInstrumentationTestCase2<TestSh
// The generic result is at <path>/<name>-expected.txt
// First try the Android-specific result at
// platform/android-<js-engine>/<path>/<name>-expected.txt
+ // then
+ // platform/android/<path>/<name>-expected.txt
int pos = test.lastIndexOf('.');
if (pos == -1)
return null;
String genericExpectedResult = test.substring(0, pos) + "-expected.txt";
String androidExpectedResultsDir = "platform/android-" + mJsEngine + "/";
- String androidExpectedResult =
- genericExpectedResult.replaceFirst(LAYOUT_TESTS_ROOT, LAYOUT_TESTS_ROOT + androidExpectedResultsDir);
+ String androidExpectedResult = genericExpectedResult.replaceFirst(LAYOUT_TESTS_ROOT,
+ LAYOUT_TESTS_ROOT + androidExpectedResultsDir);
File f = new File(androidExpectedResult);
+ if (f.exists())
+ return androidExpectedResult;
+ androidExpectedResultsDir = "platform/android/";
+ androidExpectedResult = genericExpectedResult.replaceFirst(LAYOUT_TESTS_ROOT,
+ LAYOUT_TESTS_ROOT + androidExpectedResultsDir);
+ f = new File(androidExpectedResult);
return f.exists() ? androidExpectedResult : genericExpectedResult;
}
@@ -300,7 +301,7 @@ public class LayoutTestsAutoTest extends ActivityInstrumentationTestCase2<TestSh
}
}
- private void runTestAndWaitUntilDone(TestShellActivity activity, String test, int timeout, boolean ignoreResult) {
+ private void runTestAndWaitUntilDone(TestShellActivity activity, String test, int timeout, boolean ignoreResult, int testNumber) {
activity.setCallback(new TestShellCallback() {
public void finished() {
synchronized (LayoutTestsAutoTest.this) {
@@ -336,6 +337,9 @@ public class LayoutTestsAutoTest extends ActivityInstrumentationTestCase2<TestSh
intent.putExtra(TestShellActivity.TEST_URL, FsUtils.getTestUrl(test));
intent.putExtra(TestShellActivity.RESULT_FILE, resultFile);
intent.putExtra(TestShellActivity.TIMEOUT_IN_MILLIS, timeout);
+ intent.putExtra(TestShellActivity.TOTAL_TEST_COUNT, mTestCount);
+ intent.putExtra(TestShellActivity.CURRENT_TEST_NUMBER, testNumber);
+ intent.putExtra(TestShellActivity.STOP_ON_REF_ERROR, true);
activity.startActivity(intent);
// Wait until done.
@@ -375,8 +379,8 @@ public class LayoutTestsAutoTest extends ActivityInstrumentationTestCase2<TestSh
// Read settings
mTestPathPrefix = (new File(LAYOUT_TESTS_ROOT + runner.mTestPath)).getAbsolutePath();
mRebaselineResults = runner.mRebaseline;
- // JSC is the default JavaScript engine.
- mJsEngine = runner.mJsEngine == null ? "jsc" : runner.mJsEngine;
+ // V8 is the default JavaScript engine.
+ mJsEngine = runner.mJsEngine == null ? "v8" : runner.mJsEngine;
int timeout = runner.mTimeoutInMillis;
if (timeout <= 0) {
@@ -393,7 +397,7 @@ public class LayoutTestsAutoTest extends ActivityInstrumentationTestCase2<TestSh
resumeTestList();
TestShellActivity activity = getActivity();
- activity.setDefaultDumpDataType(DumpDataType.DUMP_AS_TEXT);
+ activity.setDefaultDumpDataType(DumpDataType.EXT_REPR);
// Run tests.
int addr = -1;
@@ -410,7 +414,9 @@ public class LayoutTestsAutoTest extends ActivityInstrumentationTestCase2<TestSh
boolean ignoreResult = mTestListIgnoreResult.elementAt(i);
FsUtils.updateTestStatus(TEST_STATUS_FILE, s);
// Run tests
- runTestAndWaitUntilDone(activity, s, runner.mTimeoutInMillis, ignoreResult);
+ // i is 0 based, but test count is 1 based so add 1 to i here.
+ runTestAndWaitUntilDone(activity, s, runner.mTimeoutInMillis, ignoreResult,
+ i + 1 + mResumeIndex);
}
FsUtils.updateTestStatus(TEST_STATUS_FILE, "#DONE");
@@ -435,7 +441,7 @@ public class LayoutTestsAutoTest extends ActivityInstrumentationTestCase2<TestSh
try {
File tests_list = new File(LAYOUT_TESTS_LIST_FILE);
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(tests_list, false));
- FsUtils.findLayoutTestsRecursively(bos, getTestPath(), false); // Don't ignore results
+ FsUtils.writeLayoutTestListRecursively(bos, getTestPath(), false); // Don't ignore results
bos.flush();
bos.close();
} catch (Exception e) {
diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/Menu.java b/tests/DumpRenderTree/src/com/android/dumprendertree/Menu.java
index 9c4b572..0b00d65 100644
--- a/tests/DumpRenderTree/src/com/android/dumprendertree/Menu.java
+++ b/tests/DumpRenderTree/src/com/android/dumprendertree/Menu.java
@@ -53,29 +53,38 @@ public class Menu extends FileList {
intent.setClass(this, TestShellActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
intent.putExtra(TestShellActivity.TEST_URL, "file://" + filename);
+ intent.putExtra(TestShellActivity.TOTAL_TEST_COUNT, 1);
+ intent.putExtra(TestShellActivity.CURRENT_TEST_NUMBER, 1);
startActivity(intent);
}
@Override
void processDirectory(String path, boolean selection) {
- generateTestList(path);
+ int testCount = generateTestList(path);
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setClass(this, TestShellActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
intent.putExtra(TestShellActivity.UI_AUTO_TEST, LAYOUT_TESTS_LIST_FILE);
+ intent.putExtra(TestShellActivity.TOTAL_TEST_COUNT, testCount);
+ // TestShellActivity will process this intent once and increment the test index
+ // before running the first test, so pass 0 here to allow for that.
+ intent.putExtra(TestShellActivity.CURRENT_TEST_NUMBER, 0);
startActivity(intent);
}
- private void generateTestList(String path) {
+ private int generateTestList(String path) {
+ int testCount = 0;
try {
File tests_list = new File(LAYOUT_TESTS_LIST_FILE);
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(tests_list, false));
- FsUtils.findLayoutTestsRecursively(bos, path, false); // Don't ignore results
+ testCount = FsUtils.writeLayoutTestListRecursively(
+ bos, path, false); // Don't ignore results
bos.flush();
bos.close();
} catch (Exception e) {
Log.e(LOGTAG, "Error when creating test list: " + e.getMessage());
}
+ return testCount;
}
}
diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/TestShellActivity.java b/tests/DumpRenderTree/src/com/android/dumprendertree/TestShellActivity.java
index 7475719..40af8c0 100644
--- a/tests/DumpRenderTree/src/com/android/dumprendertree/TestShellActivity.java
+++ b/tests/DumpRenderTree/src/com/android/dumprendertree/TestShellActivity.java
@@ -35,6 +35,8 @@ import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.ViewGroup;
+import android.view.Window;
+import android.webkit.ConsoleMessage;
import android.webkit.GeolocationPermissions;
import android.webkit.HttpAuthHandler;
import android.webkit.JsPromptResult;
@@ -55,6 +57,7 @@ import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashMap;
+import java.util.Iterator;
import java.util.Map;
import java.util.Vector;
@@ -99,6 +102,8 @@ public class TestShellActivity extends Activity implements LayoutTestController
Log.v(LOGTAG, "message sent to WebView to dump text.");
switch (mDumpDataType) {
case DUMP_AS_TEXT:
+ callback.arg1 = mDumpTopFrameAsText ? 1 : 0;
+ callback.arg2 = mDumpChildFramesAsText ? 1 : 0;
mWebView.documentAsText(callback);
break;
case EXT_REPR:
@@ -117,6 +122,7 @@ public class TestShellActivity extends Activity implements LayoutTestController
@Override
protected void onCreate(Bundle icicle) {
super.onCreate(icicle);
+ requestWindowFeature(Window.FEATURE_PROGRESS);
LinearLayout contentView = new LinearLayout(this);
contentView.setOrientation(LinearLayout.VERTICAL);
@@ -145,6 +151,9 @@ public class TestShellActivity extends Activity implements LayoutTestController
if (intent != null) {
executeIntent(intent);
}
+
+ // This is asynchronous, but it gets processed by WebCore before it starts loading pages.
+ mWebView.useMockDeviceOrientation();
}
@Override
@@ -159,6 +168,9 @@ public class TestShellActivity extends Activity implements LayoutTestController
return;
}
+ mTotalTestCount = intent.getIntExtra(TOTAL_TEST_COUNT, mTotalTestCount);
+ mCurrentTestNumber = intent.getIntExtra(CURRENT_TEST_NUMBER, mCurrentTestNumber);
+
mTestUrl = intent.getStringExtra(TEST_URL);
if (mTestUrl == null) {
mUiAutoTestPath = intent.getStringExtra(UI_AUTO_TEST);
@@ -172,6 +184,11 @@ public class TestShellActivity extends Activity implements LayoutTestController
mTimeoutInMillis = intent.getIntExtra(TIMEOUT_IN_MILLIS, 0);
mGetDrawtime = intent.getBooleanExtra(GET_DRAW_TIME, false);
mSaveImagePath = intent.getStringExtra(SAVE_IMAGE);
+ mStopOnRefError = intent.getBooleanExtra(STOP_ON_REF_ERROR, false);
+ setTitle("Test " + mCurrentTestNumber + " of " + mTotalTestCount);
+ float ratio = (float)mCurrentTestNumber / mTotalTestCount;
+ int progress = (int)(ratio * Window.PROGRESS_END);
+ getWindow().setFeatureInt(Window.FEATURE_PROGRESS, progress);
Log.v(LOGTAG, " Loading " + mTestUrl);
mWebView.loadUrl(mTestUrl);
@@ -237,6 +254,7 @@ public class TestShellActivity extends Activity implements LayoutTestController
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
intent.putExtra(TestShellActivity.TEST_URL, FsUtils.getTestUrl(url));
+ intent.putExtra(TestShellActivity.CURRENT_TEST_NUMBER, ++mCurrentTestNumber);
intent.putExtra(TIMEOUT_IN_MILLIS, 10000);
executeIntent(intent);
}
@@ -329,14 +347,29 @@ public class TestShellActivity extends Activity implements LayoutTestController
// .......................................
// LayoutTestController Functions
- public void dumpAsText() {
+ public void dumpAsText(boolean enablePixelTests) {
+ // Added after webkit update to r63859. See trac.webkit.org/changeset/63730.
+ if (enablePixelTests) {
+ Log.v(LOGTAG, "dumpAsText(enablePixelTests == true) not implemented on Android!");
+ }
+
mDumpDataType = DumpDataType.DUMP_AS_TEXT;
+ mDumpTopFrameAsText = true;
if (mWebView != null) {
String url = mWebView.getUrl();
Log.v(LOGTAG, "dumpAsText called: "+url);
}
}
+ public void dumpChildFramesAsText() {
+ mDumpDataType = DumpDataType.DUMP_AS_TEXT;
+ mDumpChildFramesAsText = true;
+ if (mWebView != null) {
+ String url = mWebView.getUrl();
+ Log.v(LOGTAG, "dumpChildFramesAsText called: "+url);
+ }
+ }
+
public void waitUntilDone() {
mWaitUntilDone = true;
String url = mWebView.getUrl();
@@ -348,7 +381,9 @@ public class TestShellActivity extends Activity implements LayoutTestController
Log.v(LOGTAG, "notifyDone called: " + url);
if (mWaitUntilDone) {
mWaitUntilDone = false;
- mChromeClient.onProgressChanged(mWebView, 101);
+ if (!mRequestedWebKitData && !mTimedOut && !finished()) {
+ requestWebKitData();
+ }
}
}
@@ -459,8 +494,25 @@ public class TestShellActivity extends Activity implements LayoutTestController
* Sets the Geolocation permission state to be used for all future requests.
*/
public void setGeolocationPermission(boolean allow) {
- mGeolocationPermissionSet = true;
+ mIsGeolocationPermissionSet = true;
mGeolocationPermission = allow;
+
+ if (mPendingGeolocationPermissionCallbacks != null) {
+ Iterator iter = mPendingGeolocationPermissionCallbacks.keySet().iterator();
+ while (iter.hasNext()) {
+ GeolocationPermissions.Callback callback =
+ (GeolocationPermissions.Callback) iter.next();
+ String origin = (String) mPendingGeolocationPermissionCallbacks.get(callback);
+ callback.invoke(origin, mGeolocationPermission, false);
+ }
+ mPendingGeolocationPermissionCallbacks = null;
+ }
+ }
+
+ public void setMockDeviceOrientation(boolean canProvideAlpha, double alpha,
+ boolean canProvideBeta, double beta, boolean canProvideGamma, double gamma) {
+ mWebView.setMockDeviceOrientation(canProvideAlpha, alpha, canProvideBeta, beta,
+ canProvideGamma, gamma);
}
public void overridePreference(String key, boolean value) {
@@ -473,6 +525,10 @@ public class TestShellActivity extends Activity implements LayoutTestController
}
}
+ public void setXSSAuditorEnabled (boolean flag) {
+ mWebView.getSettings().setXSSAuditorEnabled(flag);
+ }
+
private final WebViewClient mViewClient = new WebViewClient(){
@Override
public void onPageFinished(WebView view, String url) {
@@ -490,11 +546,30 @@ public class TestShellActivity extends Activity implements LayoutTestController
drawPageToFile(mSaveImagePath + "/" + name + ".png", mWebView);
}
}
+
// Calling finished() will check if we've met all the conditions for completing
- // this test and move to the next one if we are ready.
+ // this test and move to the next one if we are ready. Otherwise we ask WebCore to
+ // dump the page.
if (finished()) {
return;
}
+
+ if (!mWaitUntilDone && !mRequestedWebKitData && !mTimedOut) {
+ requestWebKitData();
+ } else {
+ if (mWaitUntilDone) {
+ Log.v(LOGTAG, "page finished loading but waiting for notifyDone to be called: " + url);
+ }
+
+ if (mRequestedWebKitData) {
+ Log.v(LOGTAG, "page finished loading but webkit data has already been requested: " + url);
+ }
+
+ if (mTimedOut) {
+ Log.v(LOGTAG, "page finished loading but already timed out: " + url);
+ }
+ }
+
super.onPageFinished(view, url);
}
@@ -536,44 +611,8 @@ public class TestShellActivity extends Activity implements LayoutTestController
private final WebChromeClient mChromeClient = new WebChromeClient() {
@Override
- public void onProgressChanged(WebView view, int newProgress) {
-
- // notifyDone calls this with 101%. We only want to update this flag if this
- // is the real call from WebCore.
- if (newProgress == 100) {
- mOneHundredPercentComplete = true;
- }
-
- // With the flag updated, we can now proceed as normal whether the progress update came from
- // WebCore or notifyDone.
- if (newProgress >= 100) {
- // finished() will check if we are ready to move to the next test and do so if we are.
- if (finished()) {
- return;
- }
-
- if (!mTimedOut && !mWaitUntilDone && !mRequestedWebKitData) {
- String url = mWebView.getUrl();
- Log.v(LOGTAG, "Finished: "+ url);
- requestWebKitData();
- } else {
- String url = mWebView.getUrl();
- if (mTimedOut) {
- Log.v(LOGTAG, "Timed out before finishing: " + url);
- } else if (mWaitUntilDone) {
- Log.v(LOGTAG, "Waiting for notifyDone: " + url);
- } else if (mRequestedWebKitData) {
- Log.v(LOGTAG, "Requested webkit data ready: " + url);
- }
- }
- }
- }
-
- @Override
public void onReceivedTitle(WebView view, String title) {
- if (title.length() > 30)
- title = "..."+title.substring(title.length()-30);
- setTitle(title);
+ setTitle("Test " + mCurrentTestNumber + " of " + mTotalTestCount + ": "+ title);
if (mDumpTitleChanges) {
mTitleChanges.append("TITLE CHANGED: ");
mTitleChanges.append(title);
@@ -670,21 +709,40 @@ public class TestShellActivity extends Activity implements LayoutTestController
@Override
public void onGeolocationPermissionsShowPrompt(String origin,
GeolocationPermissions.Callback callback) {
- if (mGeolocationPermissionSet) {
+ if (mIsGeolocationPermissionSet) {
callback.invoke(origin, mGeolocationPermission, false);
+ return;
+ }
+ if (mPendingGeolocationPermissionCallbacks == null) {
+ mPendingGeolocationPermissionCallbacks =
+ new HashMap<GeolocationPermissions.Callback, String>();
}
+ mPendingGeolocationPermissionCallbacks.put(callback, origin);
}
@Override
- public void onConsoleMessage(String message, int lineNumber,
- String sourceID) {
+ public boolean onConsoleMessage(ConsoleMessage consoleMessage) {
+ String msg = "CONSOLE MESSAGE: line " + consoleMessage.lineNumber() + ": "
+ + consoleMessage.message() + "\n";
if (mConsoleMessages == null) {
mConsoleMessages = new StringBuffer();
}
- String consoleMessage = "CONSOLE MESSAGE: line "
- + lineNumber +": "+ message +"\n";
- mConsoleMessages.append(consoleMessage);
- Log.v(LOGTAG, "LOG: "+consoleMessage);
+ mConsoleMessages.append(msg);
+ Log.v(LOGTAG, "LOG: " + msg);
+ // the rationale here is that if there's an error of either type, and the test was
+ // waiting for "notifyDone" signal to finish, then there's no point in waiting
+ // anymore because the JS execution is already terminated at this point and a
+ // "notifyDone" will never come out so it's just wasting time till timeout kicks in
+ if ((msg.contains("Uncaught ReferenceError:") || msg.contains("Uncaught TypeError:"))
+ && mWaitUntilDone && mStopOnRefError) {
+ Log.w(LOGTAG, "Terminating test case on uncaught ReferenceError or TypeError.");
+ mHandler.postDelayed(new Runnable() {
+ public void run() {
+ notifyDone();
+ }
+ }, 500);
+ }
+ return true;
}
@Override
@@ -723,13 +781,15 @@ public class TestShellActivity extends Activity implements LayoutTestController
private static class NewWindowWebView extends WebView {
public NewWindowWebView(Context context, Map<String, Object> jsIfaces) {
- super(context, null, 0, jsIfaces);
+ super(context, null, 0, jsIfaces, false);
}
}
private void resetTestStatus() {
mWaitUntilDone = false;
mDumpDataType = mDefaultDumpDataType;
+ mDumpTopFrameAsText = false;
+ mDumpChildFramesAsText = false;
mTimedOut = false;
mDumpTitleChanges = false;
mRequestedWebKitData = false;
@@ -739,10 +799,12 @@ public class TestShellActivity extends Activity implements LayoutTestController
mEventSender.clearTouchPoints();
mEventSender.clearTouchMetaState();
mPageFinished = false;
- mOneHundredPercentComplete = false;
mDumpWebKitData = false;
mGetDrawtime = false;
mSaveImagePath = null;
+ setDefaultWebSettings(mWebView);
+ mIsGeolocationPermissionSet = false;
+ mPendingGeolocationPermissionCallbacks = null;
}
private long[] getDrawWebViewTime(WebView view, int count) {
@@ -779,7 +841,7 @@ public class TestShellActivity extends Activity implements LayoutTestController
}
private boolean canMoveToNextTest() {
- return (mDumpWebKitData && mOneHundredPercentComplete && mPageFinished && !mWaitUntilDone) || mTimedOut;
+ return (mDumpWebKitData && mPageFinished && !mWaitUntilDone) || mTimedOut;
}
private void setupWebViewForLayoutTests(WebView webview, CallbackProxy callbackProxy) {
@@ -787,6 +849,19 @@ public class TestShellActivity extends Activity implements LayoutTestController
return;
}
+ setDefaultWebSettings(webview);
+
+ webview.setWebChromeClient(mChromeClient);
+ webview.setWebViewClient(mViewClient);
+ // Setting a touch interval of -1 effectively disables the optimisation in WebView
+ // that stops repeated touch events flooding WebCore. The Event Sender only sends a
+ // single event rather than a stream of events (like what would generally happen in
+ // a real use of touch events in a WebView) and so if the WebView drops the event,
+ // the test will fail as the test expects one callback for every touch it synthesizes.
+ webview.setTouchInterval(-1);
+ }
+
+ public void setDefaultWebSettings(WebView webview) {
WebSettings settings = webview.getSettings();
settings.setAppCacheEnabled(true);
settings.setAppCachePath(getApplicationContext().getCacheDir().getPath());
@@ -799,15 +874,7 @@ public class TestShellActivity extends Activity implements LayoutTestController
settings.setDatabasePath(getDir("databases",0).getAbsolutePath());
settings.setDomStorageEnabled(true);
settings.setWorkersEnabled(false);
-
- webview.setWebChromeClient(mChromeClient);
- webview.setWebViewClient(mViewClient);
- // Setting a touch interval of -1 effectively disables the optimisation in WebView
- // that stops repeated touch events flooding WebCore. The Event Sender only sends a
- // single event rather than a stream of events (like what would generally happen in
- // a real use of touch events in a WebView) and so if the WebView drops the event,
- // the test will fail as the test expects one callback for every touch it synthesizes.
- webview.setTouchInterval(-1);
+ settings.setXSSAuditorEnabled(false);
}
private WebView mWebView;
@@ -824,6 +891,9 @@ public class TestShellActivity extends Activity implements LayoutTestController
private String mSaveImagePath;
private BufferedReader mTestListReader;
private boolean mGetDrawtime;
+ private int mTotalTestCount;
+ private int mCurrentTestNumber;
+ private boolean mStopOnRefError;
// States
private boolean mTimedOut;
@@ -833,6 +903,8 @@ public class TestShellActivity extends Activity implements LayoutTestController
// Layout test controller variables.
private DumpDataType mDumpDataType;
private DumpDataType mDefaultDumpDataType = DumpDataType.EXT_REPR;
+ private boolean mDumpTopFrameAsText;
+ private boolean mDumpChildFramesAsText;
private boolean mWaitUntilDone;
private boolean mDumpTitleChanges;
private StringBuffer mTitleChanges;
@@ -846,7 +918,6 @@ public class TestShellActivity extends Activity implements LayoutTestController
private boolean mPageFinished = false;
private boolean mDumpWebKitData = false;
- private boolean mOneHundredPercentComplete = false;
static final String TIMEOUT_STR = "**Test timeout";
@@ -861,11 +932,15 @@ public class TestShellActivity extends Activity implements LayoutTestController
static final String UI_AUTO_TEST = "UiAutoTest";
static final String GET_DRAW_TIME = "GetDrawTime";
static final String SAVE_IMAGE = "SaveImage";
+ static final String TOTAL_TEST_COUNT = "TestCount";
+ static final String CURRENT_TEST_NUMBER = "TestNumber";
+ static final String STOP_ON_REF_ERROR = "StopOnReferenceError";
static final int DRAW_RUNS = 5;
static final String DRAW_TIME_LOG = Environment.getExternalStorageDirectory() +
"/android/page_draw_time.txt";
- private boolean mGeolocationPermissionSet;
+ private boolean mIsGeolocationPermissionSet;
private boolean mGeolocationPermission;
+ private Map mPendingGeolocationPermissionCallbacks;
}
diff --git a/tests/DumpRenderTree2/Android.mk b/tests/DumpRenderTree2/Android.mk
new file mode 100644
index 0000000..81fc633
--- /dev/null
+++ b/tests/DumpRenderTree2/Android.mk
@@ -0,0 +1,29 @@
+#
+# Copyright (C) 2010 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 := tests
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+
+LOCAL_JAVA_LIBRARIES := android.test.runner
+
+LOCAL_STATIC_JAVA_LIBRARIES := diff_match_patch
+
+LOCAL_PACKAGE_NAME := DumpRenderTree2
+
+include $(BUILD_PACKAGE) \ No newline at end of file
diff --git a/tests/DumpRenderTree2/AndroidManifest.xml b/tests/DumpRenderTree2/AndroidManifest.xml
new file mode 100644
index 0000000..dd0c4e9
--- /dev/null
+++ b/tests/DumpRenderTree2/AndroidManifest.xml
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+Copyright (C) 2010 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.dumprendertree2">
+ <application>
+ <uses-library android:name="android.test.runner" />
+
+ <activity android:name=".ui.DirListActivity"
+ android:label="Dump Render Tree 2"
+ android:configChanges="orientation">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+
+ <!-- android:launchMode="singleTask" is there so we only have a one instance
+ of this activity. However, it doesn't seem to work exactly like described in the
+ documentation, because the behaviour of the application suggest
+ there is only a single task for all 3 activities. We don't understand
+ how exactly it all works, but at the moment it works just fine.
+ It can lead to some weird behaviour in the future. -->
+ <activity android:name=".TestsListActivity"
+ android:label="Tests' list activity"
+ android:launchMode="singleTask">
+ </activity>
+
+ <activity android:name=".LayoutTestsExecutor"
+ android:label="Layout tests' executor"
+ android:process=":executor">
+ </activity>
+
+ <service android:name="ManagerService">
+ </service>
+ </application>
+
+ <instrumentation android:name="com.android.dumprendertree2.scriptsupport.ScriptTestRunner"
+ android:targetPackage="com.android.dumprendertree2"
+ android:label="Layout tests script runner" />
+
+ <uses-permission android:name="android.permission.INTERNET" />
+ <uses-permission android:name="android.permission.WRITE_SDCARD" />
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+ <uses-permission android:name="android.permission.WAKE_LOCK" />
+</manifest> \ No newline at end of file
diff --git a/tests/DumpRenderTree2/assets/run_apache2.py b/tests/DumpRenderTree2/assets/run_apache2.py
new file mode 100755
index 0000000..f404090
--- /dev/null
+++ b/tests/DumpRenderTree2/assets/run_apache2.py
@@ -0,0 +1,107 @@
+#!/usr/bin/python
+#
+# Copyright (C) 2010 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.
+#
+"""Start, stop, or restart apache2 server.
+
+ Apache2 must be installed with mod_php!
+
+ Usage:
+ run_apache2.py start|stop|restart
+"""
+
+import sys
+import os
+import subprocess
+import logging
+
+def main():
+ if len(sys.argv) < 2:
+ run_cmd = ""
+ else:
+ run_cmd = sys.argv[1]
+
+ #Setup logging class
+ logging.basicConfig(level=logging.INFO, format='%(message)s')
+
+ if not run_cmd in ("start", "stop", "restart"):
+ logging.info("illegal argument: " + run_cmd)
+ logging.info("Usage: python run_apache2.py start|stop|restart")
+ return
+
+ #Create /tmp/WebKit if it doesn't exist. This is needed for various files used by apache2
+ tmp_WebKit = os.path.join("/tmp", "WebKit")
+ if not os.path.exists(tmp_WebKit):
+ os.mkdir(tmp_WebKit)
+
+ #Get the path to android tree root based on the script location.
+ #Basically we go 5 levels up
+ parent = os.pardir
+ script_location = os.path.abspath(os.path.dirname(sys.argv[0]))
+ android_tree_root = os.path.join(script_location, parent, parent, parent, parent, parent)
+ android_tree_root = os.path.normpath(android_tree_root)
+
+ #Prepare the command to set ${APACHE_RUN_USER} and ${APACHE_RUN_GROUP}
+ envvars_path = os.path.join("/etc", "apache2", "envvars")
+ export_envvars_cmd = "source " + envvars_path
+
+ error_log_path = os.path.join(tmp_WebKit, "apache2-error.log")
+
+ #Prepare the command to (re)start/stop the server with specified settings
+ apache2_restart_cmd = "apache2 -k " + run_cmd
+ directives = " -c \"ServerRoot " + android_tree_root + "\""
+ directives += " -c \"DocumentRoot " + os.path.join("external", "webkit") + "\""
+
+ #This directive is commented out in apache2-debian-httpd.conf for some reason
+ #However, it is useful to browse through tests in the browser, so it's added here.
+ #One thing to note is that because of problems with mod_dir and port numbers, mod_dir
+ #is turned off. That means that there _must_ be a trailing slash at the end of URL
+ #for auto indexes to work correctly.
+ directives += " -c \"LoadModule autoindex_module /usr/lib/apache2/modules/mod_autoindex.so\""
+
+ directives += " -c \"ErrorLog " + error_log_path +"\""
+ directives += " -c \"SSLCertificateFile " + os.path.join ("external", "webkit", "LayoutTests",
+ "http", "conf", "webkit-httpd.pem") + "\""
+ directives += " -c \"User ${APACHE_RUN_USER}\""
+ directives += " -c \"Group ${APACHE_RUN_GROUP}\""
+ directives += " -C \"TypesConfig " + os.path.join("/etc", "mime.types") + "\""
+ conf_file_cmd = " -f " + os.path.join(android_tree_root, "external", "webkit", "LayoutTests",
+ "http", "conf", "apache2-debian-httpd.conf")
+
+ #Try to execute the commands
+ logging.info("Will " + run_cmd + " apache2 server.")
+ cmd = export_envvars_cmd + " && " + apache2_restart_cmd + directives + conf_file_cmd
+ p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ (out, err) = p.communicate()
+
+ #Output the stdout from the command to console
+ logging.info(out)
+
+ #Report any errors
+ if p.returncode != 0:
+ logging.info("!! ERRORS:")
+
+ if err.find(envvars_path) != -1:
+ logging.info(err)
+ elif err.find('command not found') != -1:
+ logging.info("apache2 is probably not installed")
+ else:
+ logging.info(err)
+ logging.info("Try looking in " + error_log_path + " for details")
+ else:
+ logging.info("OK")
+
+if __name__ == "__main__":
+ main();
diff --git a/tests/DumpRenderTree2/assets/run_layout_tests.py b/tests/DumpRenderTree2/assets/run_layout_tests.py
new file mode 100755
index 0000000..b13d8c9
--- /dev/null
+++ b/tests/DumpRenderTree2/assets/run_layout_tests.py
@@ -0,0 +1,65 @@
+#!/usr/bin/python
+
+"""Run layout tests on the device.
+
+ It runs the specified tests on the device, downloads the summaries to the temporary directory
+ and opens html details in the default browser.
+
+ Usage:
+ run_layout_tests.py PATH
+"""
+
+import sys
+import os
+import subprocess
+import logging
+import webbrowser
+import tempfile
+
+#TODO: These should not be hardcoded
+RESULTS_ABSOLUTE_PATH = "/sdcard/android/LayoutTests-results/"
+DETAILS_HTML = "details.html"
+SUMMARY_TXT = "summary.txt"
+
+def main():
+ if len(sys.argv) > 1:
+ path = sys.argv[1]
+ else:
+ path = ""
+
+ logging.basicConfig(level=logging.INFO, format='%(message)s')
+
+ tmpdir = tempfile.gettempdir()
+
+ # Run the tests in path
+ cmd = "adb shell am instrument "
+ cmd += "-e class com.android.dumprendertree2.scriptsupport.Starter#startLayoutTests "
+ cmd += "-e path \"" + path + "\" "
+ cmd +="-w com.android.dumprendertree2/com.android.dumprendertree2.scriptsupport.ScriptTestRunner"
+
+ logging.info("Running the tests...")
+ subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()
+
+ logging.info("Downloading the summaries...")
+
+ # Download the txt summary to tmp folder
+ summary_txt_tmp_path = os.path.join(tmpdir, SUMMARY_TXT)
+ cmd = "adb pull " + RESULTS_ABSOLUTE_PATH + SUMMARY_TXT + " " + summary_txt_tmp_path
+ subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()
+
+ # Download the html summary to tmp folder
+ details_html_tmp_path = os.path.join(tmpdir, DETAILS_HTML)
+ cmd = "adb pull " + RESULTS_ABSOLUTE_PATH + DETAILS_HTML + " " + details_html_tmp_path
+ subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()
+
+ # Print summary to console
+ logging.info("All done.\n")
+ cmd = "cat " + summary_txt_tmp_path
+ os.system(cmd)
+ logging.info("")
+
+ # Open the browser with summary
+ webbrowser.open(details_html_tmp_path)
+
+if __name__ == "__main__":
+ main();
diff --git a/tests/DumpRenderTree2/res/drawable/folder.png b/tests/DumpRenderTree2/res/drawable/folder.png
new file mode 100644
index 0000000..5b3fcec
--- /dev/null
+++ b/tests/DumpRenderTree2/res/drawable/folder.png
Binary files differ
diff --git a/tests/DumpRenderTree2/res/drawable/runtest.png b/tests/DumpRenderTree2/res/drawable/runtest.png
new file mode 100644
index 0000000..910c654
--- /dev/null
+++ b/tests/DumpRenderTree2/res/drawable/runtest.png
Binary files differ
diff --git a/tests/DumpRenderTree2/res/layout/dirlist_row.xml b/tests/DumpRenderTree2/res/layout/dirlist_row.xml
new file mode 100644
index 0000000..e5578a6
--- /dev/null
+++ b/tests/DumpRenderTree2/res/layout/dirlist_row.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+Copyright (C) 2010 The Android Open Source Project
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="horizontal"
+ android:gravity="center_vertical"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content">
+
+ <ImageView
+ android:id="@+id/icon"
+ android:layout_width="80px"
+ android:adjustViewBounds="true"
+ android:paddingLeft="15px"
+ android:paddingRight="15px"
+ android:paddingTop="15px"
+ android:paddingBottom="15px"
+ android:layout_height="wrap_content"
+ />
+
+ <TextView
+ android:id="@+id/label"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:minHeight="60px"
+ android:gravity="center_vertical"
+ android:textSize="14sp"
+ />
+
+</LinearLayout> \ No newline at end of file
diff --git a/tests/DumpRenderTree2/res/values/strings.xml b/tests/DumpRenderTree2/res/values/strings.xml
new file mode 100644
index 0000000..5fd1eb9
--- /dev/null
+++ b/tests/DumpRenderTree2/res/values/strings.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+Copyright (C) 2010 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="dialog_run_abort_dir_title_prefix">Directory:</string>
+ <string name="dialog_run_abort_dir_msg">This will run all the tests in this directory and all
+ the subdirectories. It may take a few hours!</string>
+ <string name="dialog_run_abort_dir_ok_button">Run tests!</string>
+ <string name="dialog_run_abort_dir_abort_button">Abort</string>
+
+ <string name="dialog_progress_title">Loading items.</string>
+ <string name="dialog_progress_msg">Please wait...</string>
+
+ <string name="runner_preloading_title">Preloading tests...</string>
+</resources> \ No newline at end of file
diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/AbstractResult.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/AbstractResult.java
new file mode 100644
index 0000000..d68930c
--- /dev/null
+++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/AbstractResult.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2010 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.dumprendertree2;
+
+import android.os.Bundle;
+import android.os.Message;
+import android.webkit.WebView;
+
+/**
+ * A class that represent a result of the test. It is responsible for returning the result's
+ * raw data and generating its own diff in HTML format.
+ */
+public abstract class AbstractResult implements Comparable<AbstractResult> {
+
+ private static final String LOG_TAG = "AbstractResult";
+
+ public enum TestType {
+ TEXT {
+ @Override
+ public AbstractResult createResult(Bundle bundle) {
+ return new TextResult(bundle);
+ }
+ },
+ RENDER_TREE {
+ @Override
+ public AbstractResult createResult(Bundle bundle) {
+ /** TODO: RenderTree tests are not yet supported */
+ return null;
+ }
+ };
+
+ public abstract AbstractResult createResult(Bundle bundle);
+ }
+
+ public enum ResultCode {
+ PASS("Passed"),
+ FAIL_RESULT_DIFFERS("Result differs"),
+ FAIL_NO_EXPECTED_RESULT("No expected result"),
+ FAIL_TIMED_OUT("Timed out"),
+ FAIL_CRASHED("Crashed");
+
+ private String mTitle;
+
+ private ResultCode(String title) {
+ mTitle = title;
+ }
+
+ @Override
+ public String toString() {
+ return mTitle;
+ }
+ }
+
+ String mAdditionalTextOutputString;
+
+ public int compareTo(AbstractResult another) {
+ return getRelativePath().compareTo(another.getRelativePath());
+ }
+
+ public void setAdditionalTextOutputString(String additionalTextOutputString) {
+ mAdditionalTextOutputString = additionalTextOutputString;
+ }
+
+ public String getAdditionalTextOutputString() {
+ return mAdditionalTextOutputString;
+ }
+
+ /**
+ * Makes the result object obtain the results of the test from the webview
+ * and store them in the format that suits itself bests. This method is asynchronous.
+ * The message passed as a parameter is a message that should be sent to its target
+ * when the result finishes obtaining the result.
+ *
+ * @param webview
+ * @param resultObtainedMsg
+ */
+ public abstract void obtainActualResults(WebView webview, Message resultObtainedMsg);
+
+ public abstract void setExpectedImageResult(byte[] expectedResult);
+
+ public abstract void setExpectedTextResult(String expectedResult);
+
+ /**
+ * Returns result's image data that can be written to the disk. It can be null
+ * if there is an error of some sort or for example the test times out.
+ *
+ * <p> Some tests will not provide data (like text tests)
+ *
+ * @return
+ * results image data
+ */
+ public abstract byte[] getActualImageResult();
+
+ /**
+ * Returns result's text data. It can be null
+ * if there is an error of some sort or for example the test times out.
+ *
+ * @return
+ * results text data
+ */
+ public abstract String getActualTextResult();
+
+ /**
+ * Returns the code of this result.
+ *
+ * @return
+ * the code of this result
+ */
+ public abstract ResultCode getResultCode();
+
+ /**
+ * Return the type of the result data.
+ *
+ * @return
+ * the type of the result data.
+ */
+ public abstract TestType getType();
+
+ public abstract String getRelativePath();
+
+ /**
+ * Returns a piece of HTML code that presents a visual diff between a result and
+ * the expected result.
+ *
+ * @return
+ * a piece of HTML code with a visual diff between the result and the expected result
+ */
+ public abstract String getDiffAsHtml();
+
+ public abstract Bundle getBundle();
+} \ No newline at end of file
diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/AdditionalTextOutput.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/AdditionalTextOutput.java
new file mode 100644
index 0000000..8fca629
--- /dev/null
+++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/AdditionalTextOutput.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2010 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.dumprendertree2;
+
+import android.util.Log;
+import android.webkit.ConsoleMessage;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+
+/**
+ * A class that stores consoles messages, database callbacks, alert messages, etc.
+ */
+public class AdditionalTextOutput {
+ private static final String LOG_TAG = "AdditionalTextOutput";
+
+ /**
+ * Ordering of enums is important as it determines ordering of the toString method!
+ * StringBuilders will be printed in the order the corresponding types appear here.
+ */
+ private enum OutputType {
+ JS_DIALOG,
+ EXCEEDED_DB_QUOTA_MESSAGE,
+ CONSOLE_MESSAGE;
+ }
+
+ StringBuilder[] mOutputs = new StringBuilder[OutputType.values().length];
+
+ private StringBuilder getStringBuilderForType(OutputType outputType) {
+ int index = outputType.ordinal();
+ if (mOutputs[index] == null) {
+ mOutputs[index] = new StringBuilder();
+ }
+ return mOutputs[index];
+ }
+
+ public void appendExceededDbQuotaMessage(String urlString, String databaseIdentifier) {
+ StringBuilder output = getStringBuilderForType(OutputType.EXCEEDED_DB_QUOTA_MESSAGE);
+
+ String protocol = "";
+ String host = "";
+ int port = 0;
+
+ try {
+ URL url = new URL(urlString);
+ protocol = url.getProtocol();
+ host = url.getHost();
+ if (url.getPort() > -1) {
+ port = url.getPort();
+ }
+ } catch (MalformedURLException e) {
+ Log.e(LOG_TAG + "::appendDatabaseCallback", e.getMessage());
+ }
+
+ output.append("UI DELEGATE DATABASE CALLBACK: ");
+ output.append("exceededDatabaseQuotaForSecurityOrigin:{");
+ output.append(protocol + ", " + host + ", " + port + "} ");
+ output.append("database:" + databaseIdentifier + "\n");
+ }
+
+ public void appendConsoleMessage(ConsoleMessage consoleMessage) {
+ StringBuilder output = getStringBuilderForType(OutputType.CONSOLE_MESSAGE);
+
+ output.append("CONSOLE MESSAGE: line " + consoleMessage.lineNumber());
+ output.append(": " + consoleMessage.message() + "\n");
+ }
+
+ public void appendJsAlert(String message) {
+ StringBuilder output = getStringBuilderForType(OutputType.JS_DIALOG);
+
+ output.append("ALERT: ");
+ output.append(message);
+ output.append('\n');
+ }
+
+ public void appendJsConfirm(String message) {
+ StringBuilder output = getStringBuilderForType(OutputType.JS_DIALOG);
+
+ output.append("CONFIRM: ");
+ output.append(message);
+ output.append('\n');
+ }
+
+ public void appendJsPrompt(String message, String defaultValue) {
+ StringBuilder output = getStringBuilderForType(OutputType.JS_DIALOG);
+
+ output.append("PROMPT: ");
+ output.append(message);
+ output.append(", default text: ");
+ output.append(defaultValue);
+ output.append('\n');
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder result = new StringBuilder();
+ for (int i = 0; i < mOutputs.length; i++) {
+ if (mOutputs[i] != null) {
+ result.append(mOutputs[i].toString());
+ }
+ }
+ return result.toString();
+ }
+} \ No newline at end of file
diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/CrashedDummyResult.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/CrashedDummyResult.java
new file mode 100644
index 0000000..a793dab
--- /dev/null
+++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/CrashedDummyResult.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2010 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.dumprendertree2;
+
+import android.os.Bundle;
+import android.os.Message;
+import android.webkit.WebView;
+
+/**
+ * A dummy class representing test that crashed.
+ */
+public class CrashedDummyResult extends AbstractResult {
+ String mRelativePath;
+
+ public CrashedDummyResult(String relativePath) {
+ mRelativePath = relativePath;
+ }
+
+ @Override
+ public byte[] getActualImageResult() {
+ return null;
+ }
+
+ @Override
+ public String getActualTextResult() {
+ return null;
+ }
+
+ @Override
+ public Bundle getBundle() {
+ /** TODO: */
+ return null;
+ }
+
+ @Override
+ public String getDiffAsHtml() {
+ /** TODO: Probably show at least expected results */
+ return "Ooops, I crashed...";
+ }
+
+ @Override
+ public String getRelativePath() {
+ return mRelativePath;
+ }
+
+ @Override
+ public ResultCode getResultCode() {
+ return ResultCode.FAIL_CRASHED;
+ }
+
+ @Override
+ public TestType getType() {
+ return null;
+ }
+
+ @Override
+ public void obtainActualResults(WebView webview, Message resultObtainedMsg) {
+ /** This method is not applicable for this type of result */
+ assert false;
+ }
+
+ @Override
+ public void setExpectedImageResult(byte[] expectedResult) {
+ /** TODO */
+ }
+
+ @Override
+ public void setExpectedTextResult(String expectedResult) {
+ /** TODO */
+ }
+} \ No newline at end of file
diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/EventSender.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/EventSender.java
new file mode 100644
index 0000000..5b7cbc4
--- /dev/null
+++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/EventSender.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2010 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.dumprendertree2;
+
+import android.webkit.WebView;
+
+/**
+ * A class that acts as a JS interface for webview to mock various touch events,
+ * mouse actions and key presses.
+ *
+ * The methods here just call corresponding methods on EventSenderImpl
+ * that contains the logic of how to execute the methods.
+ */
+public class EventSender {
+ EventSenderImpl mEventSenderImpl = new EventSenderImpl();
+
+ public void reset(WebView webView) {
+ mEventSenderImpl.reset(webView);
+ }
+
+ public void enableDOMUIEventLogging(int domNode) {
+ mEventSenderImpl.enableDOMUIEventLogging(domNode);
+ }
+
+ public void fireKeyboardEventsToElement(int domNode) {
+ mEventSenderImpl.fireKeyboardEventsToElement(domNode);
+ }
+
+ public void keyDown(String character, String[] withModifiers) {
+ mEventSenderImpl.keyDown(character, withModifiers);
+ }
+
+ public void keyDown(String character) {
+ keyDown(character, null);
+ }
+
+ public void leapForward(int milliseconds) {
+ mEventSenderImpl.leapForward(milliseconds);
+ }
+
+ public void mouseClick() {
+ mEventSenderImpl.mouseClick();
+ }
+
+ public void mouseDown() {
+ mEventSenderImpl.mouseDown();
+ }
+
+ public void mouseMoveTo(int x, int y) {
+ mEventSenderImpl.mouseMoveTo(x, y);
+ }
+
+ public void mouseUp() {
+ mEventSenderImpl.mouseUp();
+ }
+
+ public void touchStart() {
+ mEventSenderImpl.touchStart();
+ }
+
+ public void addTouchPoint(int x, int y) {
+ mEventSenderImpl.addTouchPoint(x, y);
+ }
+
+ public void updateTouchPoint(int id, int x, int y) {
+ mEventSenderImpl.updateTouchPoint(id, x, y);
+ }
+
+ public void setTouchModifier(String modifier, boolean enabled) {
+ mEventSenderImpl.setTouchModifier(modifier, enabled);
+ }
+
+ public void touchMove() {
+ mEventSenderImpl.touchMove();
+ }
+
+ public void releaseTouchPoint(int id) {
+ mEventSenderImpl.releaseTouchPoint(id);
+ }
+
+ public void touchEnd() {
+ mEventSenderImpl.touchEnd();
+ }
+
+ public void touchCancel() {
+ mEventSenderImpl.touchCancel();
+ }
+
+ public void clearTouchPoints() {
+ mEventSenderImpl.clearTouchPoints();
+ }
+
+ public void cancelTouchPoint(int id) {
+ mEventSenderImpl.cancelTouchPoint(id);
+ }
+} \ No newline at end of file
diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/EventSenderImpl.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/EventSenderImpl.java
new file mode 100644
index 0000000..93e6137
--- /dev/null
+++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/EventSenderImpl.java
@@ -0,0 +1,563 @@
+/*
+ * Copyright (C) 2010 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.dumprendertree2;
+
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.os.SystemClock;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+import android.webkit.WebView;
+
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * An implementation of EventSender
+ */
+public class EventSenderImpl {
+ private static final String LOG_TAG = "EventSenderImpl";
+
+ private static final int MSG_ENABLE_DOM_UI_EVENT_LOGGING = 0;
+ private static final int MSG_FIRE_KEYBOARD_EVENTS_TO_ELEMENT = 1;
+ private static final int MSG_LEAP_FORWARD = 2;
+
+ private static final int MSG_KEY_DOWN = 3;
+
+ private static final int MSG_MOUSE_DOWN = 4;
+ private static final int MSG_MOUSE_UP = 5;
+ private static final int MSG_MOUSE_CLICK = 6;
+ private static final int MSG_MOUSE_MOVE_TO = 7;
+
+ private static final int MSG_ADD_TOUCH_POINT = 8;
+ private static final int MSG_TOUCH_START = 9;
+ private static final int MSG_UPDATE_TOUCH_POINT = 10;
+ private static final int MSG_TOUCH_MOVE = 11;
+ private static final int MSG_CLEAR_TOUCH_POINTS = 12;
+ private static final int MSG_TOUCH_CANCEL = 13;
+ private static final int MSG_RELEASE_TOUCH_POINT = 14;
+ private static final int MSG_TOUCH_END = 15;
+ private static final int MSG_SET_TOUCH_MODIFIER = 16;
+ private static final int MSG_CANCEL_TOUCH_POINT = 17;
+
+ public static class TouchPoint {
+ WebView mWebView;
+ private int mX;
+ private int mY;
+ private long mDownTime;
+ private boolean mReleased = false;
+ private boolean mMoved = false;
+ private boolean mCancelled = false;
+
+ public TouchPoint(WebView webView, int x, int y) {
+ mWebView = webView;
+ mX = scaleX(x);
+ mY = scaleY(y);
+ }
+
+ public int getX() {
+ return mX;
+ }
+
+ public int getY() {
+ return mY;
+ }
+
+ public boolean hasMoved() {
+ return mMoved;
+ }
+
+ public void move(int newX, int newY) {
+ mX = scaleX(newX);
+ mY = scaleY(newY);
+ mMoved = true;
+ }
+
+ public void resetHasMoved() {
+ mMoved = false;
+ }
+
+ public long getDownTime() {
+ return mDownTime;
+ }
+
+ public void setDownTime(long downTime) {
+ mDownTime = downTime;
+ }
+
+ public boolean isReleased() {
+ return mReleased;
+ }
+
+ public void release() {
+ mReleased = true;
+ }
+
+ public boolean isCancelled() {
+ return mCancelled;
+ }
+
+ public void cancel() {
+ mCancelled = true;
+ }
+
+ private int scaleX(int x) {
+ return (int)(x * mWebView.getScale()) - mWebView.getScrollX();
+ }
+
+ private int scaleY(int y) {
+ return (int)(y * mWebView.getScale()) - mWebView.getScrollY();
+ }
+ }
+
+ private List<TouchPoint> mTouchPoints;
+ private int mTouchMetaState;
+ private int mMouseX;
+ private int mMouseY;
+
+ private WebView mWebView;
+
+ private Handler mEventSenderHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ TouchPoint touchPoint;
+ Bundle bundle;
+ KeyEvent event;
+
+ switch (msg.what) {
+ case MSG_ENABLE_DOM_UI_EVENT_LOGGING:
+ /** TODO: implement */
+ break;
+
+ case MSG_FIRE_KEYBOARD_EVENTS_TO_ELEMENT:
+ /** TODO: implement */
+ break;
+
+ case MSG_LEAP_FORWARD:
+ /** TODO: implement */
+ break;
+
+ case MSG_KEY_DOWN:
+ bundle = (Bundle)msg.obj;
+ String character = bundle.getString("character");
+ String[] withModifiers = bundle.getStringArray("withModifiers");
+
+ if (withModifiers != null && withModifiers.length > 0) {
+ for (int i = 0; i < withModifiers.length; i++) {
+ executeKeyEvent(KeyEvent.ACTION_DOWN,
+ modifierToKeyCode(withModifiers[i]));
+ }
+ }
+ executeKeyEvent(KeyEvent.ACTION_DOWN,
+ charToKeyCode(character.toLowerCase().toCharArray()[0]));
+ break;
+
+ /** MOUSE */
+
+ case MSG_MOUSE_DOWN:
+ /** TODO: Implement */
+ break;
+
+ case MSG_MOUSE_UP:
+ /** TODO: Implement */
+ break;
+
+ case MSG_MOUSE_CLICK:
+ /** TODO: Implement */
+ break;
+
+ case MSG_MOUSE_MOVE_TO:
+ int x = msg.arg1;
+ int y = msg.arg2;
+
+ event = null;
+ if (x > mMouseX) {
+ event = new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_RIGHT);
+ } else if (x < mMouseX) {
+ event = new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_LEFT);
+ }
+ if (event != null) {
+ mWebView.onKeyDown(event.getKeyCode(), event);
+ mWebView.onKeyUp(event.getKeyCode(), event);
+ }
+
+ event = null;
+ if (y > mMouseY) {
+ event = new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_DOWN);
+ } else if (y < mMouseY) {
+ event = new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_UP);
+ }
+ if (event != null) {
+ mWebView.onKeyDown(event.getKeyCode(), event);
+ mWebView.onKeyUp(event.getKeyCode(), event);
+ }
+
+ mMouseX = x;
+ mMouseY = y;
+ break;
+
+ /** TOUCH */
+
+ case MSG_ADD_TOUCH_POINT:
+ getTouchPoints().add(new TouchPoint(mWebView,
+ msg.arg1, msg.arg2));
+ if (getTouchPoints().size() > 1) {
+ Log.w(LOG_TAG + "::MSG_ADD_TOUCH_POINT", "Added more than one touch point");
+ }
+ break;
+
+ case MSG_TOUCH_START:
+ /**
+ * FIXME: At the moment we don't support multi-touch. Hence, we only examine
+ * the first touch point. In future this method will need rewriting.
+ */
+ if (getTouchPoints().isEmpty()) {
+ return;
+ }
+ touchPoint = getTouchPoints().get(0);
+
+ touchPoint.setDownTime(SystemClock.uptimeMillis());
+ executeTouchEvent(touchPoint, MotionEvent.ACTION_DOWN);
+ break;
+
+ case MSG_UPDATE_TOUCH_POINT:
+ bundle = (Bundle)msg.obj;
+
+ int id = bundle.getInt("id");
+ if (id >= getTouchPoints().size()) {
+ Log.w(LOG_TAG + "::MSG_UPDATE_TOUCH_POINT", "TouchPoint out of bounds: "
+ + id);
+ break;
+ }
+
+ getTouchPoints().get(id).move(bundle.getInt("x"), bundle.getInt("y"));
+ break;
+
+ case MSG_TOUCH_MOVE:
+ /**
+ * FIXME: At the moment we don't support multi-touch. Hence, we only examine
+ * the first touch point. In future this method will need rewriting.
+ */
+ if (getTouchPoints().isEmpty()) {
+ return;
+ }
+ touchPoint = getTouchPoints().get(0);
+
+ if (!touchPoint.hasMoved()) {
+ return;
+ }
+ executeTouchEvent(touchPoint, MotionEvent.ACTION_MOVE);
+ touchPoint.resetHasMoved();
+ break;
+
+ case MSG_CANCEL_TOUCH_POINT:
+ if (msg.arg1 >= getTouchPoints().size()) {
+ Log.w(LOG_TAG + "::MSG_RELEASE_TOUCH_POINT", "TouchPoint out of bounds: "
+ + msg.arg1);
+ break;
+ }
+
+ getTouchPoints().get(msg.arg1).cancel();
+ break;
+
+ case MSG_TOUCH_CANCEL:
+ /**
+ * FIXME: At the moment we don't support multi-touch. Hence, we only examine
+ * the first touch point. In future this method will need rewriting.
+ */
+ if (getTouchPoints().isEmpty()) {
+ return;
+ }
+ touchPoint = getTouchPoints().get(0);
+
+ if (touchPoint.isCancelled()) {
+ executeTouchEvent(touchPoint, MotionEvent.ACTION_CANCEL);
+ }
+ break;
+
+ case MSG_RELEASE_TOUCH_POINT:
+ if (msg.arg1 >= getTouchPoints().size()) {
+ Log.w(LOG_TAG + "::MSG_RELEASE_TOUCH_POINT", "TouchPoint out of bounds: "
+ + msg.arg1);
+ break;
+ }
+
+ getTouchPoints().get(msg.arg1).release();
+ break;
+
+ case MSG_TOUCH_END:
+ /**
+ * FIXME: At the moment we don't support multi-touch. Hence, we only examine
+ * the first touch point. In future this method will need rewriting.
+ */
+ if (getTouchPoints().isEmpty()) {
+ return;
+ }
+ touchPoint = getTouchPoints().get(0);
+
+ executeTouchEvent(touchPoint, MotionEvent.ACTION_UP);
+ if (touchPoint.isReleased()) {
+ getTouchPoints().remove(0);
+ touchPoint = null;
+ }
+ break;
+
+ case MSG_SET_TOUCH_MODIFIER:
+ bundle = (Bundle)msg.obj;
+ String modifier = bundle.getString("modifier");
+ boolean enabled = bundle.getBoolean("enabled");
+
+ int mask = 0;
+ if ("alt".equals(modifier.toLowerCase())) {
+ mask = KeyEvent.META_ALT_ON;
+ } else if ("shift".equals(modifier.toLowerCase())) {
+ mask = KeyEvent.META_SHIFT_ON;
+ } else if ("ctrl".equals(modifier.toLowerCase())) {
+ mask = KeyEvent.META_SYM_ON;
+ }
+
+ if (enabled) {
+ mTouchMetaState |= mask;
+ } else {
+ mTouchMetaState &= ~mask;
+ }
+
+ break;
+
+ case MSG_CLEAR_TOUCH_POINTS:
+ getTouchPoints().clear();
+ break;
+
+ default:
+ break;
+ }
+ }
+ };
+
+ public void reset(WebView webView) {
+ mWebView = webView;
+ mTouchPoints = null;
+ mTouchMetaState = 0;
+ mMouseX = 0;
+ mMouseY = 0;
+ }
+
+ public void enableDOMUIEventLogging(int domNode) {
+ Message msg = mEventSenderHandler.obtainMessage(MSG_ENABLE_DOM_UI_EVENT_LOGGING);
+ msg.arg1 = domNode;
+ msg.sendToTarget();
+ }
+
+ public void fireKeyboardEventsToElement(int domNode) {
+ Message msg = mEventSenderHandler.obtainMessage(MSG_FIRE_KEYBOARD_EVENTS_TO_ELEMENT);
+ msg.arg1 = domNode;
+ msg.sendToTarget();
+ }
+
+ public void leapForward(int milliseconds) {
+ Message msg = mEventSenderHandler.obtainMessage(MSG_LEAP_FORWARD);
+ msg.arg1 = milliseconds;
+ msg.sendToTarget();
+ }
+
+ public void keyDown(String character, String[] withModifiers) {
+ Bundle bundle = new Bundle();
+ bundle.putString("character", character);
+ bundle.putStringArray("withModifiers", withModifiers);
+ mEventSenderHandler.obtainMessage(MSG_KEY_DOWN, bundle).sendToTarget();
+ }
+
+ /** MOUSE */
+
+ public void mouseDown() {
+ mEventSenderHandler.sendEmptyMessage(MSG_MOUSE_DOWN);
+ }
+
+ public void mouseUp() {
+ mEventSenderHandler.sendEmptyMessage(MSG_MOUSE_UP);
+ }
+
+ public void mouseClick() {
+ mEventSenderHandler.sendEmptyMessage(MSG_MOUSE_CLICK);
+ }
+
+ public void mouseMoveTo(int x, int y) {
+ mEventSenderHandler.obtainMessage(MSG_MOUSE_MOVE_TO, x, y).sendToTarget();
+ }
+
+ /** TOUCH */
+
+ public void addTouchPoint(int x, int y) {
+ mEventSenderHandler.obtainMessage(MSG_ADD_TOUCH_POINT, x, y).sendToTarget();
+ }
+
+ public void touchStart() {
+ mEventSenderHandler.sendEmptyMessage(MSG_TOUCH_START);
+ }
+
+ public void updateTouchPoint(int id, int x, int y) {
+ Bundle bundle = new Bundle();
+ bundle.putInt("id", id);
+ bundle.putInt("x", x);
+ bundle.putInt("y", y);
+ mEventSenderHandler.obtainMessage(MSG_UPDATE_TOUCH_POINT, bundle).sendToTarget();
+ }
+
+ public void touchMove() {
+ mEventSenderHandler.sendEmptyMessage(MSG_TOUCH_MOVE);
+ }
+
+ public void cancelTouchPoint(int id) {
+ Message msg = mEventSenderHandler.obtainMessage(MSG_CANCEL_TOUCH_POINT);
+ msg.arg1 = id;
+ msg.sendToTarget();
+ }
+
+ public void touchCancel() {
+ mEventSenderHandler.sendEmptyMessage(MSG_TOUCH_CANCEL);
+ }
+
+ public void releaseTouchPoint(int id) {
+ Message msg = mEventSenderHandler.obtainMessage(MSG_RELEASE_TOUCH_POINT);
+ msg.arg1 = id;
+ msg.sendToTarget();
+ }
+
+ public void touchEnd() {
+ mEventSenderHandler.sendEmptyMessage(MSG_TOUCH_END);
+ }
+
+ public void setTouchModifier(String modifier, boolean enabled) {
+ Bundle bundle = new Bundle();
+ bundle.putString("modifier", modifier);
+ bundle.putBoolean("enabled", enabled);
+ mEventSenderHandler.obtainMessage(MSG_SET_TOUCH_MODIFIER, bundle).sendToTarget();
+ }
+
+ public void clearTouchPoints() {
+ mEventSenderHandler.sendEmptyMessage(MSG_CLEAR_TOUCH_POINTS);
+ }
+
+ private List<TouchPoint> getTouchPoints() {
+ if (mTouchPoints == null) {
+ mTouchPoints = new LinkedList<TouchPoint>();
+ }
+
+ return mTouchPoints;
+ }
+
+ private void executeTouchEvent(TouchPoint touchPoint, int action) {
+ MotionEvent event =
+ MotionEvent.obtain(touchPoint.getDownTime(), SystemClock.uptimeMillis(),
+ action, touchPoint.getX(), touchPoint.getY(), mTouchMetaState);
+ mWebView.onTouchEvent(event);
+ }
+
+ private void executeKeyEvent(int action, int keyCode) {
+ KeyEvent event = new KeyEvent(action, keyCode);
+ mWebView.onKeyDown(event.getKeyCode(), event);
+ }
+
+ /**
+ * Assumes lowercase chars, case needs to be handled by calling function.
+ */
+ private static int charToKeyCode(char c) {
+ // handle numbers
+ if (c >= '0' && c <= '9') {
+ int offset = c - '0';
+ return KeyEvent.KEYCODE_0 + offset;
+ }
+
+ // handle characters
+ if (c >= 'a' && c <= 'z') {
+ int offset = c - 'a';
+ return KeyEvent.KEYCODE_A + offset;
+ }
+
+ // handle all others
+ switch (c) {
+ case '*':
+ return KeyEvent.KEYCODE_STAR;
+
+ case '#':
+ return KeyEvent.KEYCODE_POUND;
+
+ case ',':
+ return KeyEvent.KEYCODE_COMMA;
+
+ case '.':
+ return KeyEvent.KEYCODE_PERIOD;
+
+ case '\t':
+ return KeyEvent.KEYCODE_TAB;
+
+ case ' ':
+ return KeyEvent.KEYCODE_SPACE;
+
+ case '\n':
+ return KeyEvent.KEYCODE_ENTER;
+
+ case '\b':
+ case 0x7F:
+ return KeyEvent.KEYCODE_DEL;
+
+ case '~':
+ return KeyEvent.KEYCODE_GRAVE;
+
+ case '-':
+ return KeyEvent.KEYCODE_MINUS;
+
+ case '=':
+ return KeyEvent.KEYCODE_EQUALS;
+
+ case '(':
+ return KeyEvent.KEYCODE_LEFT_BRACKET;
+
+ case ')':
+ return KeyEvent.KEYCODE_RIGHT_BRACKET;
+
+ case '\\':
+ return KeyEvent.KEYCODE_BACKSLASH;
+
+ case ';':
+ return KeyEvent.KEYCODE_SEMICOLON;
+
+ case '\'':
+ return KeyEvent.KEYCODE_APOSTROPHE;
+
+ case '/':
+ return KeyEvent.KEYCODE_SLASH;
+
+ default:
+ return c;
+ }
+ }
+
+ private static int modifierToKeyCode(String modifier) {
+ if (modifier.equals("ctrlKey")) {
+ return KeyEvent.KEYCODE_ALT_LEFT;
+ } else if (modifier.equals("shiftKey")) {
+ return KeyEvent.KEYCODE_SHIFT_LEFT;
+ } else if (modifier.equals("altKey")) {
+ return KeyEvent.KEYCODE_SYM;
+ }
+
+ return KeyEvent.KEYCODE_UNKNOWN;
+ }
+} \ No newline at end of file
diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/FileFilter.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/FileFilter.java
new file mode 100644
index 0000000..cf82d24
--- /dev/null
+++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/FileFilter.java
@@ -0,0 +1,288 @@
+/*
+ * Copyright (C) 2010 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.dumprendertree2;
+
+import android.util.Log;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * A utility to filter out some files/directories from the views and tests that run.
+ */
+public class FileFilter {
+ private static final String LOG_TAG = "FileFilter";
+
+ private static final String TEST_EXPECTATIONS_TXT_PATH =
+ "platform/android/test_expectations.txt";
+
+ private static final String TOKEN_SKIP = "SKIP";
+ private static final String TOKEN_IGNORE_RESULT = "IGNORE_RESULT";
+ private static final String TOKEN_SLOW = "SLOW";
+
+ private final Set<String> mSkipList = new HashSet<String>();
+ private final Set<String> mIgnoreResultList = new HashSet<String>();
+ private final Set<String> mSlowList = new HashSet<String>();
+
+ private final String mRootDirPath;
+
+ public FileFilter(String rootDirPath) {
+ /** It may or may not contain a trailing slash */
+ this.mRootDirPath = rootDirPath;
+
+ reloadConfiguration();
+ }
+
+ private static final String trimTrailingSlashIfPresent(String path) {
+ File file = new File(path);
+ return file.getPath();
+ }
+
+ public void reloadConfiguration() {
+ File txt_exp = new File(mRootDirPath, TEST_EXPECTATIONS_TXT_PATH);
+
+ BufferedReader bufferedReader;
+ try {
+ bufferedReader =
+ new BufferedReader(new FileReader(txt_exp));
+
+ String line;
+ String entry;
+ String[] parts;
+ String path;
+ Set<String> tokens;
+ Boolean skipped;
+ while (true) {
+ line = bufferedReader.readLine();
+ if (line == null) {
+ break;
+ }
+
+ /** Remove the comment and trim */
+ entry = line.split("//", 2)[0].trim();
+
+ /** Omit empty lines, advance to next line */
+ if (entry.isEmpty()) {
+ continue;
+ }
+
+ /** Split on whitespace into path part and the rest */
+ parts = entry.split("\\s", 2);
+
+ /** At this point parts.length >= 1 */
+ if (parts.length == 1) {
+ Log.w(LOG_TAG + "::reloadConfiguration",
+ "There are no options specified for the test!");
+ continue;
+ }
+
+ path = trimTrailingSlashIfPresent(parts[0]);
+
+ /** Split on whitespace */
+ tokens = new HashSet<String>(Arrays.asList(parts[1].split("\\s", 0)));
+
+ /** Chose the right collections to add to */
+ skipped = false;
+ if (tokens.contains(TOKEN_SKIP)) {
+ mSkipList.add(path);
+ skipped = true;
+ }
+
+ /** If test is on skip list we ignore any further options */
+ if (skipped) {
+ continue;
+ }
+
+ if (tokens.contains(TOKEN_IGNORE_RESULT)) {
+ mIgnoreResultList.add(path);
+ }
+
+ if (tokens.contains(TOKEN_SLOW)) {
+ mSlowList.add(path);
+ }
+ }
+ } catch (FileNotFoundException e) {
+ Log.w(LOG_TAG + "::reloadConfiguration", "File not found: " + txt_exp.getPath());
+ } catch (IOException e) {
+ Log.e(LOG_TAG + "::reloadConfiguration", "IOException: " + e.getMessage());
+ }
+ }
+
+ /**
+ * Checks if test is supposed to be skipped.
+ *
+ * <p>
+ * Path given should relative within LayoutTests folder, e.g. fast/dom/foo.html
+ *
+ * @param testPath
+ * - a relative path within LayoutTests folder
+ * @return if the test is supposed to be skipped
+ */
+ public boolean isSkip(String testPath) {
+ for (String prefix : getPrefixes(testPath)) {
+ if (mSkipList.contains(prefix)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Checks if test result is supposed to be ignored.
+ *
+ * <p>
+ * Path given should relative within LayoutTests folder, e.g. fast/dom/foo.html
+ *
+ * @param testPath
+ * - a relative path within LayoutTests folder
+ * @return if the test result is supposed to be ignored
+ */
+ public boolean isIgnoreRes(String testPath) {
+ for (String prefix : getPrefixes(testPath)) {
+ if (mIgnoreResultList.contains(prefix)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Checks if test is slow and should have timeout increased.
+ *
+ * <p>
+ * Path given should relative within LayoutTests folder, e.g. fast/dom/foo.html
+ *
+ * @param testPath
+ * - a relative path within LayoutTests folder
+ * @return if the test is slow and should have timeout increased.
+ */
+ public boolean isSlow(String testPath) {
+ for (String prefix : getPrefixes(testPath)) {
+ if (mSlowList.contains(prefix)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Returns the list of all path prefixes of the given path.
+ *
+ * <p>
+ * e.g. this/is/a/path returns the list: this this/is this/is/a this/is/a/path
+ *
+ * @param path
+ * @return the list of all path prefixes of the given path.
+ */
+ private static List<String> getPrefixes(String path) {
+ File file = new File(path);
+ List<String> prefixes = new ArrayList<String>(8);
+
+ do {
+ prefixes.add(file.getPath());
+ file = file.getParentFile();
+ } while (file != null);
+
+ return prefixes;
+ }
+
+ /**
+ * Checks if the directory may contain tests or contains just helper files.
+ *
+ * @param dirName
+ * @return
+ * if the directory may contain tests
+ */
+ public static boolean isTestDir(String dirName) {
+ return (!dirName.equals("script-tests")
+ && !dirName.equals("resources") && !dirName.startsWith("."));
+ }
+
+ /**
+ * Checks if the file is a test.
+ * Currently we run .html and .xhtml tests.
+ *
+ * @param testName
+ * @return
+ * if the file is a test
+ */
+ public static boolean isTestFile(String testName) {
+ return testName.endsWith(".html") || testName.endsWith(".xhtml");
+ }
+
+ /**
+ * Return the path to the file relative to the tests root dir
+ *
+ * @param filePath
+ * @return
+ * the path relative to the tests root dir
+ */
+ public String getRelativePath(String filePath) {
+ File rootDir = new File(mRootDirPath);
+ return filePath.replaceFirst(rootDir.getPath() + File.separator, "");
+ }
+
+ /**
+ * Return the path to the file relative to the tests root dir
+ *
+ * @param filePath
+ * @return
+ * the path relative to the tests root dir
+ */
+ public String getRelativePath(File file) {
+ return getRelativePath(file.getAbsolutePath());
+ }
+
+ public File getAbsoluteFile(String relativePath) {
+ return new File(mRootDirPath, relativePath);
+ }
+
+ public String getAboslutePath(String relativePath) {
+ return getAbsoluteFile(relativePath).getAbsolutePath();
+ }
+
+ /**
+ * If the path contains extension (e.g .foo at the end of the file) then it changes
+ * this (.foo) into newEnding (so it has to contain the dot if we want to preserve it).
+ *
+ * <p>If the path doesn't contain an extension, it adds the ending to the path.
+ *
+ * @param relativePath
+ * @param newEnding
+ * @return
+ * a new path, containing the newExtension
+ */
+ public static String setPathEnding(String relativePath, String newEnding) {
+ int dotPos = relativePath.lastIndexOf('.');
+ if (dotPos == -1) {
+ return relativePath + newEnding;
+ }
+
+ return relativePath.substring(0, dotPos) + newEnding;
+ }
+} \ No newline at end of file
diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/FsUtils.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/FsUtils.java
new file mode 100644
index 0000000..212c187
--- /dev/null
+++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/FsUtils.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2010 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.dumprendertree2;
+
+import android.util.Log;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ *
+ */
+public class FsUtils {
+ public static final String LOG_TAG = "FsUtils";
+
+ public static void writeDataToStorage(File file, byte[] bytes, boolean append) {
+ Log.d(LOG_TAG + "::writeDataToStorage", file.getAbsolutePath());
+ try {
+ OutputStream outputStream = null;
+ try {
+ file.getParentFile().mkdirs();
+ file.createNewFile();
+ Log.d(LOG_TAG + "::writeDataToStorage", "File created.");
+ outputStream = new FileOutputStream(file, append);
+ outputStream.write(bytes);
+ } finally {
+ if (outputStream != null) {
+ outputStream.close();
+ }
+ }
+ } catch (IOException e) {
+ Log.e(LOG_TAG + "::writeDataToStorage", e.getMessage());
+ }
+ }
+
+ public static byte[] readDataFromStorage(File file) {
+ if (!file.exists()) {
+ Log.d(LOG_TAG + "::readDataFromStorage", "File does not exist: "
+ + file.getAbsolutePath());
+ return null;
+ }
+
+ byte[] bytes = null;
+ try {
+ FileInputStream fis = null;
+ try {
+ fis = new FileInputStream(file);
+ bytes = new byte[(int) file.length()];
+ fis.read(bytes);
+ } finally {
+ if (fis != null) {
+ fis.close();
+ }
+ }
+ } catch (IOException e) {
+ Log.e(LOG_TAG + "::readDataFromStorage", e.getMessage());
+ }
+
+ return bytes;
+ }
+} \ No newline at end of file
diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/LayoutTestController.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/LayoutTestController.java
new file mode 100644
index 0000000..6db9571
--- /dev/null
+++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/LayoutTestController.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2010 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.dumprendertree2;
+
+import android.net.Uri;
+import android.util.Log;
+import android.webkit.MockGeolocation;
+import android.webkit.WebStorage;
+
+import java.io.File;
+
+/**
+ * A class that is registered as JS interface for webview in LayoutTestExecutor
+ */
+public class LayoutTestController {
+ private static final String LOG_TAG = "LayoutTestController";
+
+ LayoutTestsExecutor mLayoutTestsExecutor;
+
+ public LayoutTestController(LayoutTestsExecutor layoutTestsExecutor) {
+ mLayoutTestsExecutor = layoutTestsExecutor;
+ }
+
+ public void waitUntilDone() {
+ mLayoutTestsExecutor.waitUntilDone();
+ }
+
+ public void notifyDone() {
+ mLayoutTestsExecutor.notifyDone();
+ }
+
+ public void dumpAsText() {
+ dumpAsText(false);
+ }
+
+ public void dumpAsText(boolean enablePixelTest) {
+ mLayoutTestsExecutor.dumpAsText(enablePixelTest);
+ }
+
+ public void dumpChildFramesAsText() {
+ mLayoutTestsExecutor.dumpChildFramesAsText();
+ }
+
+ public void clearAllDatabases() {
+ Log.w(LOG_TAG + "::clearAllDatabases", "called");
+ WebStorage.getInstance().deleteAllData();
+ }
+
+ public void setCanOpenWindows() {
+ mLayoutTestsExecutor.setCanOpenWindows();
+ }
+
+ public void dumpDatabaseCallbacks() {
+ mLayoutTestsExecutor.dumpDatabaseCallbacks();
+ }
+
+ public void setDatabaseQuota(long quota) {
+ /** TODO: Reset this before every test! */
+ Log.w(LOG_TAG + "::setDatabaseQuota", "called with: " + quota);
+ WebStorage.getInstance().setQuotaForOrigin(Uri.fromFile(new File("")).toString(),
+ quota);
+ }
+
+ public void setGeolocationPermission(boolean allow) {
+ mLayoutTestsExecutor.setGeolocationPermission(allow);
+ }
+
+ public void setMockGeolocationPosition(double latitude, double longitude, double accuracy) {
+ Log.w(LOG_TAG + "::setMockGeolocationPosition", "latitude: " + latitude +
+ " longitude: " + longitude + " accuracy: " + accuracy);
+ MockGeolocation.getInstance().setPosition(latitude, longitude, accuracy);
+ }
+
+ public void setMockGeolocationError(int code, String message) {
+ Log.w(LOG_TAG + "::setMockGeolocationError", "code: " + code + " message: " + message);
+ MockGeolocation.getInstance().setError(code, message);
+ }
+
+ public void setMockDeviceOrientation(boolean canProvideAlpha, double alpha,
+ boolean canProvideBeta, double beta, boolean canProvideGamma, double gamma) {
+ // Configuration is in WebKit, so stay on WebCore thread, but go via LayoutTestsExecutor
+ // as we need access to the Webview.
+ mLayoutTestsExecutor.setMockDeviceOrientation(
+ canProvideAlpha, alpha, canProvideBeta, beta, canProvideGamma, gamma);
+ }
+}
diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/LayoutTestsExecutor.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/LayoutTestsExecutor.java
new file mode 100644
index 0000000..4737657
--- /dev/null
+++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/LayoutTestsExecutor.java
@@ -0,0 +1,603 @@
+/*
+ * Copyright (C) 2010 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.dumprendertree2;
+
+import android.app.Activity;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Environment;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.Messenger;
+import android.os.PowerManager;
+import android.os.RemoteException;
+import android.os.PowerManager.WakeLock;
+import android.util.Log;
+import android.view.Window;
+import android.webkit.ConsoleMessage;
+import android.webkit.JsPromptResult;
+import android.webkit.JsResult;
+import android.webkit.WebChromeClient;
+import android.webkit.WebSettings;
+import android.webkit.WebView;
+import android.webkit.WebViewClient;
+import android.webkit.GeolocationPermissions;
+import android.webkit.WebStorage.QuotaUpdater;
+
+import java.io.File;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * This activity executes the test. It contains WebView and logic of LayoutTestController
+ * functions. It runs in a separate process and sends the results of running the test
+ * to ManagerService. The reason why is to handle crashing (test that crashes brings down
+ * whole process with it).
+ */
+public class LayoutTestsExecutor extends Activity {
+
+ private enum CurrentState {
+ IDLE,
+ RENDERING_PAGE,
+ WAITING_FOR_ASYNCHRONOUS_TEST,
+ OBTAINING_RESULT;
+
+ public boolean isRunningState() {
+ return this == CurrentState.RENDERING_PAGE ||
+ this == CurrentState.WAITING_FOR_ASYNCHRONOUS_TEST;
+ }
+ }
+
+ /** TODO: make it a setting */
+ static final String TESTS_ROOT_DIR_PATH =
+ Environment.getExternalStorageDirectory() +
+ File.separator + "android" +
+ File.separator + "LayoutTests";
+
+ private static final String LOG_TAG = "LayoutTestExecutor";
+
+ public static final String EXTRA_TESTS_LIST = "TestsList";
+ public static final String EXTRA_TEST_INDEX = "TestIndex";
+
+ private static final int MSG_ACTUAL_RESULT_OBTAINED = 0;
+ private static final int MSG_TEST_TIMED_OUT = 1;
+
+ private static final int DEFAULT_TIME_OUT_MS = 15 * 1000;
+
+ /** A list of tests that remain to run since last crash */
+ private List<String> mTestsList;
+
+ /**
+ * This is a number of currently running test. It is 0-based and doesn't reset after
+ * the crash. Initial index is passed to LayoutTestsExecuter in the intent that starts
+ * it.
+ */
+ private int mCurrentTestIndex;
+
+ /** The total number of tests to run, doesn't reset after crash */
+ private int mTotalTestCount;
+
+ private WebView mCurrentWebView;
+ private String mCurrentTestRelativePath;
+ private String mCurrentTestUri;
+ private CurrentState mCurrentState = CurrentState.IDLE;
+
+ private boolean mCurrentTestTimedOut;
+ private AbstractResult mCurrentResult;
+ private AdditionalTextOutput mCurrentAdditionalTextOutput;
+
+ private LayoutTestController mLayoutTestController = new LayoutTestController(this);
+ private boolean mCanOpenWindows;
+ private boolean mDumpDatabaseCallbacks;
+ private boolean mIsGeolocationPermissionSet;
+ private boolean mGeolocationPermission;
+ private Map mPendingGeolocationPermissionCallbacks;
+
+ private EventSender mEventSender = new EventSender();
+
+ private WakeLock mScreenDimLock;
+
+ /** COMMUNICATION WITH ManagerService */
+
+ private Messenger mManagerServiceMessenger;
+
+ private ServiceConnection mServiceConnection = new ServiceConnection() {
+
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ mManagerServiceMessenger = new Messenger(service);
+ startTests();
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ /** TODO */
+ }
+ };
+
+ private final Handler mResultHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_ACTUAL_RESULT_OBTAINED:
+ onActualResultsObtained();
+ break;
+
+ case MSG_TEST_TIMED_OUT:
+ onTestTimedOut();
+ break;
+
+ default:
+ break;
+ }
+ }
+ };
+
+ /** WEBVIEW CONFIGURATION */
+
+ private WebViewClient mWebViewClient = new WebViewClient() {
+ @Override
+ public void onPageFinished(WebView view, String url) {
+ /** Some tests fire up many page loads, we don't want to detect them */
+ if (!url.equals(mCurrentTestUri)) {
+ return;
+ }
+
+ if (mCurrentState == CurrentState.RENDERING_PAGE) {
+ onTestFinished();
+ }
+ }
+ };
+
+ private WebChromeClient mWebChromeClient = new WebChromeClient() {
+ @Override
+ public void onExceededDatabaseQuota(String url, String databaseIdentifier,
+ long currentQuota, long estimatedSize, long totalUsedQuota,
+ QuotaUpdater quotaUpdater) {
+ /** TODO: This should be recorded as part of the text result */
+ /** TODO: The quota should also probably be reset somehow for every test? */
+ if (mDumpDatabaseCallbacks) {
+ getCurrentAdditionalTextOutput().appendExceededDbQuotaMessage(url,
+ databaseIdentifier);
+ }
+ quotaUpdater.updateQuota(currentQuota + 5 * 1024 * 1024);
+ }
+
+ @Override
+ public boolean onJsAlert(WebView view, String url, String message, JsResult result) {
+ getCurrentAdditionalTextOutput().appendJsAlert(message);
+ result.confirm();
+ return true;
+ }
+
+ @Override
+ public boolean onJsConfirm(WebView view, String url, String message, JsResult result) {
+ getCurrentAdditionalTextOutput().appendJsConfirm(message);
+ result.confirm();
+ return true;
+ }
+
+ @Override
+ public boolean onJsPrompt(WebView view, String url, String message, String defaultValue,
+ JsPromptResult result) {
+ getCurrentAdditionalTextOutput().appendJsPrompt(message, defaultValue);
+ result.confirm();
+ return true;
+ }
+
+ @Override
+ public boolean onConsoleMessage(ConsoleMessage consoleMessage) {
+ getCurrentAdditionalTextOutput().appendConsoleMessage(consoleMessage);
+ return true;
+ }
+
+ @Override
+ public boolean onCreateWindow(WebView view, boolean dialog, boolean userGesture,
+ Message resultMsg) {
+ WebView.WebViewTransport transport = (WebView.WebViewTransport)resultMsg.obj;
+ /** By default windows cannot be opened, so just send null back. */
+ WebView newWindowWebView = null;
+
+ if (mCanOpenWindows) {
+ /**
+ * We never display the new window, just create the view and allow it's content to
+ * execute and be recorded by the executor.
+ */
+ newWindowWebView = new WebView(LayoutTestsExecutor.this);
+ setupWebView(newWindowWebView);
+ }
+
+ transport.setWebView(newWindowWebView);
+ resultMsg.sendToTarget();
+ return true;
+ }
+
+ @Override
+ public void onGeolocationPermissionsShowPrompt(String origin,
+ GeolocationPermissions.Callback callback) {
+ if (mIsGeolocationPermissionSet) {
+ callback.invoke(origin, mGeolocationPermission, false);
+ return;
+ }
+ if (mPendingGeolocationPermissionCallbacks == null) {
+ mPendingGeolocationPermissionCallbacks =
+ new HashMap<GeolocationPermissions.Callback, String>();
+ }
+ mPendingGeolocationPermissionCallbacks.put(callback, origin);
+ }
+ };
+
+ /** IMPLEMENTATION */
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ requestWindowFeature(Window.FEATURE_PROGRESS);
+
+ Intent intent = getIntent();
+ mTestsList = intent.getStringArrayListExtra(EXTRA_TESTS_LIST);
+ mCurrentTestIndex = intent.getIntExtra(EXTRA_TEST_INDEX, -1);
+ mTotalTestCount = mCurrentTestIndex + mTestsList.size();
+
+ PowerManager pm = (PowerManager)getSystemService(Context.POWER_SERVICE);
+ mScreenDimLock = pm.newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK
+ | PowerManager.ON_AFTER_RELEASE, "WakeLock in LayoutTester");
+ mScreenDimLock.acquire();
+
+ bindService(new Intent(this, ManagerService.class), mServiceConnection,
+ Context.BIND_AUTO_CREATE);
+ }
+
+ private void reset() {
+ WebView previousWebView = mCurrentWebView;
+
+ resetLayoutTestController();
+
+ mCurrentTestTimedOut = false;
+ mCurrentResult = null;
+ mCurrentAdditionalTextOutput = null;
+
+ mCurrentWebView = new WebView(this);
+ setupWebView(mCurrentWebView);
+
+ mEventSender.reset(mCurrentWebView);
+
+ setContentView(mCurrentWebView);
+ if (previousWebView != null) {
+ Log.d(LOG_TAG + "::reset", "previousWebView != null");
+ previousWebView.destroy();
+ }
+ }
+
+ private void setupWebView(WebView webView) {
+ webView.setWebViewClient(mWebViewClient);
+ webView.setWebChromeClient(mWebChromeClient);
+ webView.addJavascriptInterface(mLayoutTestController, "layoutTestController");
+ webView.addJavascriptInterface(mEventSender, "eventSender");
+
+ /**
+ * Setting a touch interval of -1 effectively disables the optimisation in WebView
+ * that stops repeated touch events flooding WebCore. The Event Sender only sends a
+ * single event rather than a stream of events (like what would generally happen in
+ * a real use of touch events in a WebView) and so if the WebView drops the event,
+ * the test will fail as the test expects one callback for every touch it synthesizes.
+ */
+ webView.setTouchInterval(-1);
+
+ WebSettings webViewSettings = webView.getSettings();
+ webViewSettings.setAppCacheEnabled(true);
+ webViewSettings.setAppCachePath(getApplicationContext().getCacheDir().getPath());
+ webViewSettings.setAppCacheMaxSize(Long.MAX_VALUE);
+ webViewSettings.setJavaScriptEnabled(true);
+ webViewSettings.setJavaScriptCanOpenWindowsAutomatically(true);
+ webViewSettings.setSupportMultipleWindows(true);
+ webViewSettings.setLayoutAlgorithm(WebSettings.LayoutAlgorithm.NORMAL);
+ webViewSettings.setDatabaseEnabled(true);
+ webViewSettings.setDatabasePath(getDir("databases", 0).getAbsolutePath());
+ webViewSettings.setDomStorageEnabled(true);
+ webViewSettings.setWorkersEnabled(false);
+ webViewSettings.setXSSAuditorEnabled(false);
+
+ // This is asynchronous, but it gets processed by WebCore before it starts loading pages.
+ mCurrentWebView.useMockDeviceOrientation();
+ }
+
+ private void startTests() {
+ try {
+ Message serviceMsg =
+ Message.obtain(null, ManagerService.MSG_FIRST_TEST);
+
+ Bundle bundle = new Bundle();
+ if (!mTestsList.isEmpty()) {
+ bundle.putString("firstTest", mTestsList.get(0));
+ bundle.putInt("index", mCurrentTestIndex);
+ }
+
+ serviceMsg.setData(bundle);
+ mManagerServiceMessenger.send(serviceMsg);
+ } catch (RemoteException e) {
+ Log.e(LOG_TAG + "::startTests", e.getMessage());
+ }
+
+ runNextTest();
+ }
+
+ private void runNextTest() {
+ assert mCurrentState == CurrentState.IDLE : "mCurrentState = " + mCurrentState.name();
+
+ if (mTestsList.isEmpty()) {
+ onAllTestsFinished();
+ return;
+ }
+
+ mCurrentTestRelativePath = mTestsList.remove(0);
+ Log.d(LOG_TAG + "::runNextTest", "Start: " + mCurrentTestRelativePath +
+ "(" + mCurrentTestIndex + ")");
+ mCurrentTestUri =
+ Uri.fromFile(new File(TESTS_ROOT_DIR_PATH, mCurrentTestRelativePath)).toString();
+
+ reset();
+
+ /** Start time-out countdown and the test */
+ mCurrentState = CurrentState.RENDERING_PAGE;
+ mResultHandler.sendEmptyMessageDelayed(MSG_TEST_TIMED_OUT, DEFAULT_TIME_OUT_MS);
+ mCurrentWebView.loadUrl(mCurrentTestUri);
+ }
+
+ private void onTestTimedOut() {
+ assert mCurrentState.isRunningState() : "mCurrentState = " + mCurrentState.name();
+
+ mCurrentTestTimedOut = true;
+
+ /**
+ * While it is theoretically possible that the test times out because
+ * of webview becoming unresponsive, it is very unlikely. Therefore it's
+ * assumed that obtaining results (that calls various webview methods)
+ * will not itself hang.
+ */
+ obtainActualResultsFromWebView();
+ }
+
+ private void onTestFinished() {
+ assert mCurrentState.isRunningState() : "mCurrentState = " + mCurrentState.name();
+
+ obtainActualResultsFromWebView();
+ }
+
+ private void obtainActualResultsFromWebView() {
+ /**
+ * If the result has not been set by the time the test finishes we create
+ * a default type of result.
+ */
+ if (mCurrentResult == null) {
+ /** TODO: Default type should be RenderTreeResult. We don't support it now. */
+ mCurrentResult = new TextResult(mCurrentTestRelativePath);
+ }
+
+ mCurrentState = CurrentState.OBTAINING_RESULT;
+
+ mCurrentResult.obtainActualResults(mCurrentWebView,
+ mResultHandler.obtainMessage(MSG_ACTUAL_RESULT_OBTAINED));
+ }
+
+ private void onActualResultsObtained() {
+ assert mCurrentState == CurrentState.OBTAINING_RESULT
+ : "mCurrentState = " + mCurrentState.name();
+
+ mCurrentState = CurrentState.IDLE;
+
+ mResultHandler.removeMessages(MSG_TEST_TIMED_OUT);
+ reportResultToService();
+ mCurrentTestIndex++;
+ updateProgressBar();
+ runNextTest();
+ }
+
+ private void reportResultToService() {
+ if (mCurrentAdditionalTextOutput != null) {
+ mCurrentResult.setAdditionalTextOutputString(mCurrentAdditionalTextOutput.toString());
+ }
+
+ try {
+ Message serviceMsg =
+ Message.obtain(null, ManagerService.MSG_PROCESS_ACTUAL_RESULTS);
+
+ Bundle bundle = mCurrentResult.getBundle();
+ bundle.putInt("testIndex", mCurrentTestIndex);
+ if (mCurrentTestTimedOut) {
+ bundle.putString("resultCode", AbstractResult.ResultCode.FAIL_TIMED_OUT.name());
+ }
+ if (!mTestsList.isEmpty()) {
+ bundle.putString("nextTest", mTestsList.get(0));
+ }
+
+ serviceMsg.setData(bundle);
+ mManagerServiceMessenger.send(serviceMsg);
+ } catch (RemoteException e) {
+ Log.e(LOG_TAG + "::reportResultToService", e.getMessage());
+ }
+ }
+
+ private void updateProgressBar() {
+ getWindow().setFeatureInt(Window.FEATURE_PROGRESS,
+ mCurrentTestIndex * Window.PROGRESS_END / mTotalTestCount);
+ setTitle(mCurrentTestIndex * 100 / mTotalTestCount + "% " +
+ "(" + mCurrentTestIndex + "/" + mTotalTestCount + ")");
+ }
+
+ private void onAllTestsFinished() {
+ mScreenDimLock.release();
+
+ try {
+ Message serviceMsg =
+ Message.obtain(null, ManagerService.MSG_ALL_TESTS_FINISHED);
+ mManagerServiceMessenger.send(serviceMsg);
+ } catch (RemoteException e) {
+ Log.e(LOG_TAG + "::onAllTestsFinished", e.getMessage());
+ }
+
+ unbindService(mServiceConnection);
+ }
+
+ private AdditionalTextOutput getCurrentAdditionalTextOutput() {
+ if (mCurrentAdditionalTextOutput == null) {
+ mCurrentAdditionalTextOutput = new AdditionalTextOutput();
+ }
+ return mCurrentAdditionalTextOutput;
+ }
+
+ /** LAYOUT TEST CONTROLLER */
+
+ private static final int MSG_WAIT_UNTIL_DONE = 0;
+ private static final int MSG_NOTIFY_DONE = 1;
+ private static final int MSG_DUMP_AS_TEXT = 2;
+ private static final int MSG_DUMP_CHILD_FRAMES_AS_TEXT = 3;
+ private static final int MSG_SET_CAN_OPEN_WINDOWS = 4;
+ private static final int MSG_DUMP_DATABASE_CALLBACKS = 5;
+ private static final int MSG_SET_GEOLOCATION_PERMISSION = 6;
+
+ Handler mLayoutTestControllerHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ assert mCurrentState.isRunningState() : "mCurrentState = " + mCurrentState.name();
+
+ switch (msg.what) {
+ case MSG_WAIT_UNTIL_DONE:
+ mCurrentState = CurrentState.WAITING_FOR_ASYNCHRONOUS_TEST;
+ break;
+
+ case MSG_NOTIFY_DONE:
+ if (mCurrentState == CurrentState.WAITING_FOR_ASYNCHRONOUS_TEST) {
+ onTestFinished();
+ }
+ break;
+
+ case MSG_DUMP_AS_TEXT:
+ if (mCurrentResult == null) {
+ mCurrentResult = new TextResult(mCurrentTestRelativePath);
+ }
+
+ assert mCurrentResult instanceof TextResult
+ : "mCurrentResult instanceof" + mCurrentResult.getClass().getName();
+
+ break;
+
+ case MSG_DUMP_CHILD_FRAMES_AS_TEXT:
+ /** If dumpAsText was not called we assume that the result should be text */
+ if (mCurrentResult == null) {
+ mCurrentResult = new TextResult(mCurrentTestRelativePath);
+ }
+
+ assert mCurrentResult instanceof TextResult
+ : "mCurrentResult instanceof" + mCurrentResult.getClass().getName();
+
+ ((TextResult)mCurrentResult).setDumpChildFramesAsText(true);
+ break;
+
+ case MSG_SET_CAN_OPEN_WINDOWS:
+ mCanOpenWindows = true;
+ break;
+
+ case MSG_DUMP_DATABASE_CALLBACKS:
+ mDumpDatabaseCallbacks = true;
+ break;
+
+ case MSG_SET_GEOLOCATION_PERMISSION:
+ mIsGeolocationPermissionSet = true;
+ mGeolocationPermission = msg.arg1 == 1;
+
+ if (mPendingGeolocationPermissionCallbacks != null) {
+ Iterator iter = mPendingGeolocationPermissionCallbacks.keySet().iterator();
+ while (iter.hasNext()) {
+ GeolocationPermissions.Callback callback =
+ (GeolocationPermissions.Callback) iter.next();
+ String origin = (String) mPendingGeolocationPermissionCallbacks.get(callback);
+ callback.invoke(origin, mGeolocationPermission, false);
+ }
+ mPendingGeolocationPermissionCallbacks = null;
+ }
+ break;
+
+ default:
+ Log.w(LOG_TAG + "::handleMessage", "Message code does not exist: " + msg.what);
+ break;
+ }
+ }
+ };
+
+ private void resetLayoutTestController() {
+ mCanOpenWindows = false;
+ mDumpDatabaseCallbacks = false;
+ mIsGeolocationPermissionSet = false;
+ mPendingGeolocationPermissionCallbacks = null;
+ }
+
+ public void waitUntilDone() {
+ Log.w(LOG_TAG + "::waitUntilDone", "called");
+ mLayoutTestControllerHandler.sendEmptyMessage(MSG_WAIT_UNTIL_DONE);
+ }
+
+ public void notifyDone() {
+ Log.w(LOG_TAG + "::notifyDone", "called");
+ mLayoutTestControllerHandler.sendEmptyMessage(MSG_NOTIFY_DONE);
+ }
+
+ public void dumpAsText(boolean enablePixelTest) {
+ Log.w(LOG_TAG + "::dumpAsText(" + enablePixelTest + ")", "called");
+ /** TODO: Implement */
+ if (enablePixelTest) {
+ Log.w(LOG_TAG + "::dumpAsText", "enablePixelTest not implemented, switching to false");
+ }
+ mLayoutTestControllerHandler.sendEmptyMessage(MSG_DUMP_AS_TEXT);
+ }
+
+ public void dumpChildFramesAsText() {
+ Log.w(LOG_TAG + "::dumpChildFramesAsText", "called");
+ mLayoutTestControllerHandler.sendEmptyMessage(MSG_DUMP_CHILD_FRAMES_AS_TEXT);
+ }
+
+ public void setCanOpenWindows() {
+ Log.w(LOG_TAG + "::setCanOpenWindows", "called");
+ mLayoutTestControllerHandler.sendEmptyMessage(MSG_SET_CAN_OPEN_WINDOWS);
+ }
+
+ public void dumpDatabaseCallbacks() {
+ Log.w(LOG_TAG + "::dumpDatabaseCallbacks:", "called");
+ mLayoutTestControllerHandler.sendEmptyMessage(MSG_DUMP_DATABASE_CALLBACKS);
+ }
+
+ public void setGeolocationPermission(boolean allow) {
+ Log.w(LOG_TAG + "::setGeolocationPermission", "called");
+ Message msg = mLayoutTestControllerHandler.obtainMessage(MSG_SET_GEOLOCATION_PERMISSION);
+ msg.arg1 = allow ? 1 : 0;
+ msg.sendToTarget();
+ }
+
+ public void setMockDeviceOrientation(boolean canProvideAlpha, double alpha,
+ boolean canProvideBeta, double beta, boolean canProvideGamma, double gamma) {
+ mCurrentWebView.setMockDeviceOrientation(canProvideAlpha, alpha, canProvideBeta, beta,
+ canProvideGamma, gamma);
+ }
+}
diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/ManagerService.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/ManagerService.java
new file mode 100644
index 0000000..31026d6
--- /dev/null
+++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/ManagerService.java
@@ -0,0 +1,249 @@
+/*
+ * Copyright (C) 2010 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.dumprendertree2;
+
+import android.app.Service;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.Environment;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.Messenger;
+import android.util.Log;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A service that handles managing the results of tests, informing of crashes, generating
+ * summaries, etc.
+ */
+public class ManagerService extends Service {
+
+ private static final String LOG_TAG = "ManagerService";
+
+ private static final int MSG_TEST_CRASHED = 0;
+
+ private static final int CRASH_TIMEOUT_MS = 20 * 1000;
+
+ /** TODO: make it a setting */
+ static final String TESTS_ROOT_DIR_PATH =
+ Environment.getExternalStorageDirectory() +
+ File.separator + "android" +
+ File.separator + "LayoutTests";
+
+ /** TODO: make it a setting */
+ static final String RESULTS_ROOT_DIR_PATH =
+ Environment.getExternalStorageDirectory() +
+ File.separator + "android" +
+ File.separator + "LayoutTests-results";
+
+ /** TODO: Make it a setting */
+ private static final List<String> EXPECTED_RESULT_LOCATION_RELATIVE_DIR_PREFIXES =
+ new ArrayList<String>(3);
+ {
+ EXPECTED_RESULT_LOCATION_RELATIVE_DIR_PREFIXES.add("platform" + File.separator +
+ "android-v8" + File.separator);
+ EXPECTED_RESULT_LOCATION_RELATIVE_DIR_PREFIXES.add("platform" + File.separator +
+ "android" + File.separator);
+ EXPECTED_RESULT_LOCATION_RELATIVE_DIR_PREFIXES.add("");
+ }
+
+ /** TODO: Make these settings */
+ private static final String TEXT_RESULT_EXTENSION = "txt";
+ private static final String IMAGE_RESULT_EXTENSION = "png";
+
+ static final int MSG_PROCESS_ACTUAL_RESULTS = 0;
+ static final int MSG_ALL_TESTS_FINISHED = 1;
+ static final int MSG_FIRST_TEST = 2;
+
+ /**
+ * This handler is purely for IPC. It is used to create mMessenger
+ * that generates a binder returned in onBind method.
+ */
+ private Handler mIncomingHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_FIRST_TEST:
+ mSummarizer.reset();
+ Bundle bundle = msg.getData();
+ ensureNextTestSetup(bundle.getString("firstTest"), bundle.getInt("index"));
+ break;
+
+ case MSG_PROCESS_ACTUAL_RESULTS:
+ Log.d(LOG_TAG + ".mIncomingHandler", msg.getData().getString("relativePath"));
+ onActualResultsObtained(msg.getData());
+ break;
+
+ case MSG_ALL_TESTS_FINISHED:
+ mSummarizer.setTestsRelativePath(mAllTestsRelativePath);
+ mSummarizer.summarize();
+ Intent intent = new Intent(ManagerService.this, TestsListActivity.class);
+ intent.setAction(Intent.ACTION_SHUTDOWN);
+ /** This flag is needed because we send the intent from the service */
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ startActivity(intent);
+ break;
+ }
+ }
+ };
+
+ private Messenger mMessenger = new Messenger(mIncomingHandler);
+
+ private Handler mCrashMessagesHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ if (msg.what == MSG_TEST_CRASHED) {
+ onTestCrashed();
+ }
+ }
+ };
+
+ private FileFilter mFileFilter;
+ private Summarizer mSummarizer;
+
+ private String mCurrentlyRunningTest;
+ private int mCurrentlyRunningTestIndex;
+
+ private String mAllTestsRelativePath;
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+
+ mFileFilter = new FileFilter(TESTS_ROOT_DIR_PATH);
+ mSummarizer = new Summarizer(mFileFilter, RESULTS_ROOT_DIR_PATH);
+ }
+
+ @Override
+ public int onStartCommand(Intent intent, int flags, int startId) {
+ mAllTestsRelativePath = intent.getStringExtra("path");
+ assert mAllTestsRelativePath != null;
+ return START_STICKY;
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return mMessenger.getBinder();
+ }
+
+ private void onActualResultsObtained(Bundle bundle) {
+ mCrashMessagesHandler.removeMessages(MSG_TEST_CRASHED);
+ ensureNextTestSetup(bundle.getString("nextTest"), bundle.getInt("testIndex") + 1);
+
+ AbstractResult results =
+ AbstractResult.TestType.valueOf(bundle.getString("type")).createResult(bundle);
+
+ handleResults(results);
+ }
+
+ private void ensureNextTestSetup(String nextTest, int index) {
+ if (nextTest == null) {
+ return;
+ }
+
+ mCurrentlyRunningTest = nextTest;
+ mCurrentlyRunningTestIndex = index;
+ mCrashMessagesHandler.sendEmptyMessageDelayed(MSG_TEST_CRASHED, CRASH_TIMEOUT_MS);
+ }
+
+ /**
+ * This sends an intent to TestsListActivity to restart LayoutTestsExecutor.
+ * The more detailed description of the flow is in the comment of onNewIntent
+ * method in TestsListActivity.
+ */
+ private void onTestCrashed() {
+ handleResults(new CrashedDummyResult(mCurrentlyRunningTest));
+
+ Log.w(LOG_TAG + "::onTestCrashed", mCurrentlyRunningTest +
+ "(" + mCurrentlyRunningTestIndex + ")");
+
+ Intent intent = new Intent(this, TestsListActivity.class);
+ intent.setAction(Intent.ACTION_REBOOT);
+ /** This flag is needed because we send the intent from the service */
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ intent.putExtra("crashedTestIndex", mCurrentlyRunningTestIndex);
+ startActivity(intent);
+ }
+
+ private void handleResults(AbstractResult results) {
+ String relativePath = results.getRelativePath();
+ results.setExpectedTextResult(getExpectedTextResult(relativePath));
+ results.setExpectedImageResult(getExpectedImageResult(relativePath));
+
+ dumpActualTextResult(results);
+ dumpActualImageResult(results);
+
+ mSummarizer.appendTest(results);
+ }
+
+ private void dumpActualTextResult(AbstractResult result) {
+ String testPath = result.getRelativePath();
+ String actualTextResult = result.getActualTextResult();
+ if (actualTextResult == null) {
+ return;
+ }
+
+ String resultPath = FileFilter.setPathEnding(testPath, "-actual." + TEXT_RESULT_EXTENSION);
+ FsUtils.writeDataToStorage(new File(RESULTS_ROOT_DIR_PATH, resultPath),
+ actualTextResult.getBytes(), false);
+ }
+
+ private void dumpActualImageResult(AbstractResult result) {
+ String testPath = result.getRelativePath();
+ byte[] actualImageResult = result.getActualImageResult();
+ if (actualImageResult == null) {
+ return;
+ }
+
+ String resultPath = FileFilter.setPathEnding(testPath,
+ "-actual." + IMAGE_RESULT_EXTENSION);
+ FsUtils.writeDataToStorage(new File(RESULTS_ROOT_DIR_PATH, resultPath),
+ actualImageResult, false);
+ }
+
+ public static String getExpectedTextResult(String relativePath) {
+ byte[] result = getExpectedResult(relativePath, TEXT_RESULT_EXTENSION);
+ if (result != null) {
+ return new String(result);
+ }
+ return null;
+ }
+
+ public static byte[] getExpectedImageResult(String relativePath) {
+ return getExpectedResult(relativePath, IMAGE_RESULT_EXTENSION);
+ }
+
+ private static byte[] getExpectedResult(String relativePath, String extension) {
+ String originalRelativePath =
+ FileFilter.setPathEnding(relativePath, "-expected." + extension);
+
+ byte[] bytes = null;
+ List<String> locations = EXPECTED_RESULT_LOCATION_RELATIVE_DIR_PREFIXES;
+
+ int size = EXPECTED_RESULT_LOCATION_RELATIVE_DIR_PREFIXES.size();
+ for (int i = 0; bytes == null && i < size; i++) {
+ relativePath = locations.get(i) + originalRelativePath;
+ bytes = FsUtils.readDataFromStorage(new File(TESTS_ROOT_DIR_PATH, relativePath));
+ }
+
+ return bytes;
+ }
+} \ No newline at end of file
diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/Summarizer.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/Summarizer.java
new file mode 100644
index 0000000..43bc0b1
--- /dev/null
+++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/Summarizer.java
@@ -0,0 +1,391 @@
+/*
+ * Copyright (C) 2010 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.dumprendertree2;
+
+import android.content.res.AssetManager;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.os.Build;
+import android.util.DisplayMetrics;
+
+import java.io.File;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Date;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * A class that collects information about tests that ran and can create HTML
+ * files with summaries and easy navigation.
+ */
+public class Summarizer {
+
+ private static final String LOG_TAG = "Summarizer";
+
+ private static final String CSS =
+ "<style type=\"text/css\">" +
+ "* {" +
+ " font-family: Verdana;" +
+ " border: 0;" +
+ " margin: 0;" +
+ " padding: 0;}" +
+ "body {" +
+ " margin: 10px;}" +
+ "h1 {" +
+ " font-size: 24px;" +
+ " margin: 4px 0 4px 0;}" +
+ "h2 {" +
+ " font-size:18px;" +
+ " text-transform: uppercase;" +
+ " margin: 20px 0 3px 0;}" +
+ "h3, h3 a {" +
+ " font-size: 14px;" +
+ " color: black;" +
+ " text-decoration: none;" +
+ " margin-bottom: 4px;}" +
+ "h3 a span.path {" +
+ " text-decoration: underline;}" +
+ "h3 span.tri {" +
+ " text-decoration: none;" +
+ " float: left;" +
+ " width: 20px;}" +
+ "h3 span.sqr {" +
+ " text-decoration: none;" +
+ " color: #8ee100;" +
+ " float: left;" +
+ " width: 20px;}" +
+ "h3 img {" +
+ " width: 8px;" +
+ " margin-right: 4px;}" +
+ "div.diff {" +
+ " margin-bottom: 25px;}" +
+ "div.diff a {" +
+ " font-size: 12px;" +
+ " color: #888;}" +
+ "table.visual_diff {" +
+ " border-bottom: 0px solid;" +
+ " border-collapse: collapse;" +
+ " width: 100%;" +
+ " margin-bottom: 2px;}" +
+ "table.visual_diff tr.headers td {" +
+ " border-bottom: 1px solid;" +
+ " border-top: 0;" +
+ " padding-bottom: 3px;}" +
+ "table.visual_diff tr.results td {" +
+ " border-top: 1px dashed;" +
+ " border-right: 1px solid;" +
+ " font-size: 15px;" +
+ " vertical-align: top;}" +
+ "table.visual_diff tr.results td.line_count {" +
+ " background-color:#aaa;" +
+ " min-width:20px;" +
+ " text-align: right;" +
+ " border-right: 1px solid;" +
+ " border-left: 1px solid;" +
+ " padding: 2px 1px 2px 0px;}" +
+ "table.visual_diff tr.results td.line {" +
+ " padding: 2px 0px 2px 4px;" +
+ " border-right: 1px solid;" +
+ " width: 49.8%;}" +
+ "table.visual_diff tr.footers td {" +
+ " border-top: 1px solid;" +
+ " border-bottom: 0;}" +
+ "table.visual_diff tr td.space {" +
+ " border: 0;" +
+ " width: 0.4%}" +
+ "div.space {" +
+ " margin-top:4px;}" +
+ "span.eql {" +
+ " background-color: #f3f3f3;}" +
+ "span.del {" +
+ " background-color: #ff8888; }" +
+ "span.ins {" +
+ " background-color: #88ff88; }" +
+ "span.fail {" +
+ " color: red;}" +
+ "span.pass {" +
+ " color: green;}" +
+ "span.time_out {" +
+ " color: orange;}" +
+ "table.summary {" +
+ " border: 1px solid black;" +
+ " margin-top: 20px;}" +
+ "table.summary td {" +
+ " padding: 3px;}" +
+ "span.listItem {" +
+ " font-size: 11px;" +
+ " font-weight: normal;" +
+ " text-transform: uppercase;" +
+ " padding: 3px;" +
+ " -webkit-border-radius: 4px;}" +
+ "span." + AbstractResult.ResultCode.PASS.name() + "{" +
+ " background-color: #8ee100;" +
+ " color: black;}" +
+ "span." + AbstractResult.ResultCode.FAIL_RESULT_DIFFERS.name() + "{" +
+ " background-color: #ccc;" +
+ " color: black;}" +
+ "span." + AbstractResult.ResultCode.FAIL_NO_EXPECTED_RESULT.name() + "{" +
+ " background-color: #a700e4;" +
+ " color: #fff;}" +
+ "span." + AbstractResult.ResultCode.FAIL_TIMED_OUT.name() + "{" +
+ " background-color: #f3cb00;" +
+ " color: black;}" +
+ "span." + AbstractResult.ResultCode.FAIL_CRASHED.name() + "{" +
+ " background-color: #c30000;" +
+ " color: #fff;}" +
+ "span.noLtc {" +
+ " background-color: #944000;" +
+ " color: #fff;}" +
+ "span.noEventSender {" +
+ " background-color: #815600;" +
+ " color: #fff;}" +
+ "</style>";
+
+ private static final String SCRIPT =
+ "<script type=\"text/javascript\">" +
+ " function toggleDisplay(id) {" +
+ " element = document.getElementById(id);" +
+ " triangle = document.getElementById('tri.' + id);" +
+ " if (element.style.display == 'none') {" +
+ " element.style.display = 'inline';" +
+ " triangle.innerHTML = '&#x25bc; ';" +
+ " } else {" +
+ " element.style.display = 'none';" +
+ " triangle.innerHTML = '&#x25b6; ';" +
+ " }" +
+ " }" +
+ "</script>";
+
+ /** TODO: Make it a setting */
+ private static final String HTML_DETAILS_RELATIVE_PATH = "details.html";
+ private static final String TXT_SUMMARY_RELATIVE_PATH = "summary.txt";
+
+ private int mCrashedTestsCount = 0;
+ private List<AbstractResult> mFailedNotIgnoredTests = new ArrayList<AbstractResult>();
+ private List<AbstractResult> mIgnoredTests = new ArrayList<AbstractResult>();
+ private List<String> mPassedNotIgnoredTests = new ArrayList<String>();
+
+ private FileFilter mFileFilter;
+ private String mResultsRootDirPath;
+
+ private String mTestsRelativePath;
+
+ private Date mDate;
+
+ public Summarizer(FileFilter fileFilter, String resultsRootDirPath) {
+ mFileFilter = fileFilter;
+ mResultsRootDirPath = resultsRootDirPath;
+ }
+
+ public void appendTest(AbstractResult result) {
+ String relativePath = result.getRelativePath();
+
+ if (result.getResultCode() == AbstractResult.ResultCode.FAIL_CRASHED) {
+ mCrashedTestsCount++;
+ }
+
+ if (mFileFilter.isIgnoreRes(relativePath)) {
+ mIgnoredTests.add(result);
+ } else if (result.getResultCode() == AbstractResult.ResultCode.PASS) {
+ mPassedNotIgnoredTests.add(relativePath);
+ } else {
+ mFailedNotIgnoredTests.add(result);
+ }
+ }
+
+ public void setTestsRelativePath(String testsRelativePath) {
+ mTestsRelativePath = testsRelativePath;
+ }
+
+ public void summarize() {
+ createHtmlDetails();
+ createTxtSummary();
+ }
+
+ public void reset() {
+ mCrashedTestsCount = 0;
+ mFailedNotIgnoredTests.clear();
+ mIgnoredTests.clear();
+ mPassedNotIgnoredTests.clear();
+ mDate = new Date();
+ }
+
+ private void createTxtSummary() {
+ StringBuilder txt = new StringBuilder();
+
+ SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
+ txt.append(mTestsRelativePath + "\n");
+ txt.append("Date: " + dateFormat.format(mDate) + "\n");
+ txt.append("Build fingerprint: " + Build.FINGERPRINT + "\n");
+ txt.append("WebKit version: " + getWebKitVersionFromUserAgentString() + "\n");
+
+ txt.append("TOTAL: " + getTotalTestCount() + "\n");
+ if (mCrashedTestsCount > 0) {
+ txt.append("CRASHED (total among all tests): " + mCrashedTestsCount + "\n");
+ txt.append("-------------");
+ }
+ txt.append("FAILED: " + mFailedNotIgnoredTests.size() + "\n");
+ txt.append("IGNORED: " + mIgnoredTests.size() + "\n");
+ txt.append("PASSED: " + mPassedNotIgnoredTests.size() + "\n");
+
+ FsUtils.writeDataToStorage(new File(mResultsRootDirPath, TXT_SUMMARY_RELATIVE_PATH),
+ txt.toString().getBytes(), false);
+ }
+
+ private void createHtmlDetails() {
+ StringBuilder html = new StringBuilder();
+
+ html.append("<html><head>");
+ html.append(CSS);
+ html.append(SCRIPT);
+ html.append("</head><body>");
+
+ createTopSummaryTable(html);
+
+ createResultsListWithDiff(html, "Failed", mFailedNotIgnoredTests);
+
+ createResultsListWithDiff(html, "Ignored", mIgnoredTests);
+
+ createResultsListNoDiff(html, "Passed", mPassedNotIgnoredTests);
+
+ html.append("</body></html>");
+
+ FsUtils.writeDataToStorage(new File(mResultsRootDirPath, HTML_DETAILS_RELATIVE_PATH),
+ html.toString().getBytes(), false);
+ }
+
+ private int getTotalTestCount() {
+ return mFailedNotIgnoredTests.size() +
+ mPassedNotIgnoredTests.size() +
+ mIgnoredTests.size();
+ }
+
+ private String getWebKitVersionFromUserAgentString() {
+ Resources resources = new Resources(new AssetManager(), new DisplayMetrics(),
+ new Configuration());
+ String userAgent =
+ resources.getString(com.android.internal.R.string.web_user_agent);
+
+ Matcher matcher = Pattern.compile("AppleWebKit/([0-9]+?\\.[0-9])").matcher(userAgent);
+ if (matcher.find()) {
+ return matcher.group(1);
+ }
+ return "unknown";
+ }
+
+ private void createTopSummaryTable(StringBuilder html) {
+ SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
+ html.append("<h1>" + mTestsRelativePath + "</h1>");
+ html.append("<h3>" + "Date: " + dateFormat.format(new Date()) + "</h3>");
+ html.append("<h3>" + "Build fingerprint: " + Build.FINGERPRINT + "</h3>");
+ html.append("<h3>" + "WebKit version: " + getWebKitVersionFromUserAgentString() + "</h3>");
+
+ html.append("<table class=\"summary\">");
+ createSummaryTableRow(html, "TOTAL", getTotalTestCount());
+ createSummaryTableRow(html, "CRASHED", mCrashedTestsCount);
+ createSummaryTableRow(html, "FAILED", mFailedNotIgnoredTests.size());
+ createSummaryTableRow(html, "IGNORED", mIgnoredTests.size());
+ createSummaryTableRow(html, "PASSED", mPassedNotIgnoredTests.size());
+ html.append("</table>");
+ }
+
+ private void createSummaryTableRow(StringBuilder html, String caption, int size) {
+ html.append("<tr>");
+ html.append(" <td>" + caption + "</td>");
+ html.append(" <td>" + size + "</td>");
+ html.append("</tr>");
+ }
+
+ private void createResultsListWithDiff(StringBuilder html, String title,
+ List<AbstractResult> resultsList) {
+ String relativePath;
+ String id = "";
+ AbstractResult.ResultCode resultCode;
+
+ Collections.sort(resultsList);
+ html.append("<h2>" + title + " [" + resultsList.size() + "]</h2>");
+ for (AbstractResult result : resultsList) {
+ relativePath = result.getRelativePath();
+ resultCode = result.getResultCode();
+
+ html.append("<h3>");
+
+ if (resultCode == AbstractResult.ResultCode.PASS) {
+ html.append("<span class=\"sqr\">&#x25a0; </span>");
+ html.append("<span class=\"path\">" + relativePath + "</span>");
+ } else {
+ /**
+ * Technically, two different paths could end up being the same, because
+ * ':' is a valid character in a path. However, it is probably not going
+ * to cause any problems in this case
+ */
+ id = relativePath.replace(File.separator, ":");
+ html.append("<a href=\"#\" onClick=\"toggleDisplay('" + id + "');");
+ html.append("return false;\">");
+ html.append("<span class=\"tri\" id=\"tri." + id + "\">&#x25b6; </span>");
+ html.append("<span class=\"path\">" + relativePath + "</span>");
+ html.append("</a>");
+ }
+
+ html.append(" <span class=\"listItem " + resultCode.name() + "\">");
+ html.append(resultCode.toString());
+ html.append("</span>");
+
+ /** Detect missing LTC function */
+ String additionalTextOutputString = result.getAdditionalTextOutputString();
+ if (additionalTextOutputString != null &&
+ additionalTextOutputString.contains("com.android.dumprendertree") &&
+ additionalTextOutputString.contains("has no method")) {
+ if (additionalTextOutputString.contains("LayoutTestController")) {
+ html.append(" <span class=\"listItem noLtc\">LTC function missing</span>");
+ }
+ if (additionalTextOutputString.contains("EventSender")) {
+ html.append(" <span class=\"listItem noEventSender\">");
+ html.append("ES function missing</span>");
+ }
+ }
+
+ html.append("</h3>");
+
+ if (resultCode != AbstractResult.ResultCode.PASS) {
+ html.append("<div class=\"diff\" style=\"display: none;\" id=\"" + id + "\">");
+ html.append(result.getDiffAsHtml());
+ html.append("<a href=\"#\" onClick=\"toggleDisplay('" + id + "');");
+ html.append("return false;\">Hide</a>");
+ html.append("</div>");
+ }
+
+ html.append("<div class=\"space\"></div>");
+ }
+ }
+
+ private void createResultsListNoDiff(StringBuilder html, String title,
+ List<String> resultsList) {
+ Collections.sort(resultsList);
+ html.append("<h2>Passed [" + resultsList.size() + "]</h2>");
+ for (String result : resultsList) {
+ html.append("<h3>");
+ html.append("<span class=\"sqr\">&#x25a0; </span>");
+ html.append("<span class=\"path\">" + result + "</span>");
+ html.append("</h3>");
+ html.append("<div class=\"space\"></div>");
+ }
+ }
+} \ No newline at end of file
diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/TestsListActivity.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/TestsListActivity.java
new file mode 100644
index 0000000..4965fd9
--- /dev/null
+++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/TestsListActivity.java
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2010 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.dumprendertree2;
+
+import android.app.Activity;
+import android.app.ProgressDialog;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.view.Window;
+
+import com.android.dumprendertree2.scriptsupport.OnEverythingFinishedCallback;
+
+import java.util.ArrayList;
+
+/**
+ * An Activity that generates a list of tests and sends the intent to
+ * LayoutTestsExecuter to run them. It also restarts the LayoutTestsExecuter
+ * after it crashes.
+ */
+public class TestsListActivity extends Activity {
+
+ private static final int MSG_TEST_LIST_PRELOADER_DONE = 0;
+
+ /** Constants for adding extras to an intent */
+ public static final String EXTRA_TEST_PATH = "TestPath";
+
+ private static ProgressDialog sProgressDialog;
+
+ private Handler mHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_TEST_LIST_PRELOADER_DONE:
+ sProgressDialog.dismiss();
+ mTestsList = (ArrayList<String>)msg.obj;
+ mTotalTestCount = mTestsList.size();
+ restartExecutor(0);
+ break;
+ }
+ }
+ };
+
+ private ArrayList<String> mTestsList;
+ private int mTotalTestCount;
+
+ private OnEverythingFinishedCallback mOnEverythingFinishedCallback;
+ private boolean mEverythingFinished;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ /** Prepare the progress dialog */
+ sProgressDialog = new ProgressDialog(TestsListActivity.this);
+ sProgressDialog.setCancelable(false);
+ sProgressDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
+ sProgressDialog.setTitle(R.string.dialog_progress_title);
+ sProgressDialog.setMessage(getText(R.string.dialog_progress_msg));
+
+ requestWindowFeature(Window.FEATURE_PROGRESS);
+
+ Intent intent = getIntent();
+ if (!intent.getAction().equals(Intent.ACTION_RUN)) {
+ return;
+ }
+ String path = intent.getStringExtra(EXTRA_TEST_PATH);
+
+ sProgressDialog.show();
+ Message doneMsg = Message.obtain(mHandler, MSG_TEST_LIST_PRELOADER_DONE);
+
+ Intent serviceIntent = new Intent(this, ManagerService.class);
+ serviceIntent.putExtra("path", path);
+ startService(serviceIntent);
+
+ new TestsListPreloaderThread(path, doneMsg).start();
+ }
+
+ @Override
+ protected void onNewIntent(Intent intent) {
+ if (intent.getAction().equals(Intent.ACTION_REBOOT)) {
+ onCrashIntent(intent);
+ } else if (intent.getAction().equals(Intent.ACTION_SHUTDOWN)) {
+ onEverythingFinishedIntent(intent);
+ }
+ }
+
+ /**
+ * This method handles an intent that comes from ManageService when crash is detected.
+ * The intent contains an index in mTestsList of the test that crashed. TestsListActivity
+ * restarts the LayoutTestsExecutor from the following test in mTestsList, by sending
+ * an intent to it. This new intent contains a list of remaining tests to run,
+ * total count of all tests, and the index of the first test to run after restarting.
+ * LayoutTestExecutor runs then as usual, sending reports to ManagerService. If it
+ * detects the crash it sends a new intent and the flow repeats.
+ */
+ private void onCrashIntent(Intent intent) {
+ int nextTestToRun = intent.getIntExtra("crashedTestIndex", -1) + 1;
+ if (nextTestToRun > 0 && nextTestToRun <= mTotalTestCount) {
+ restartExecutor(nextTestToRun);
+ }
+ }
+
+ public void registerOnEverythingFinishedCallback(OnEverythingFinishedCallback callback) {
+ mOnEverythingFinishedCallback = callback;
+ if (mEverythingFinished) {
+ mOnEverythingFinishedCallback.onFinished();
+ }
+ }
+
+ private void onEverythingFinishedIntent(Intent intent) {
+ /** TODO: Show some kind of summary to the user */
+ mEverythingFinished = true;
+ if (mOnEverythingFinishedCallback != null) {
+ mOnEverythingFinishedCallback.onFinished();
+ }
+ }
+
+ @Override
+ protected void onSaveInstanceState(Bundle outState) {
+ outState.putStringArrayList("testsList", mTestsList);
+ outState.putInt("totalCount", mTotalTestCount);
+
+ super.onSaveInstanceState(outState);
+ }
+
+ @Override
+ protected void onRestoreInstanceState(Bundle savedInstanceState) {
+ super.onRestoreInstanceState(savedInstanceState);
+
+ mTestsList = savedInstanceState.getStringArrayList("testsList");
+ mTotalTestCount = savedInstanceState.getInt("totalCount");
+ }
+
+ /**
+ * (Re)starts the executer activity from the given test number (inclusive, 0-based).
+ * This number is an index in mTestsList, not the sublist passed in the intent.
+ *
+ * @param startFrom
+ * test index in mTestsList to start the tests from (inclusive, 0-based)
+ */
+ private void restartExecutor(int startFrom) {
+ Intent intent = new Intent();
+ intent.setClass(this, LayoutTestsExecutor.class);
+ intent.setAction(Intent.ACTION_RUN);
+
+ if (startFrom < mTotalTestCount) {
+ intent.putStringArrayListExtra(LayoutTestsExecutor.EXTRA_TESTS_LIST,
+ new ArrayList<String>(mTestsList.subList(startFrom, mTotalTestCount)));
+ intent.putExtra(LayoutTestsExecutor.EXTRA_TEST_INDEX, startFrom);
+ } else {
+ intent.putStringArrayListExtra(LayoutTestsExecutor.EXTRA_TESTS_LIST,
+ new ArrayList<String>());
+ }
+
+ startActivity(intent);
+ }
+} \ No newline at end of file
diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/TestsListPreloaderThread.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/TestsListPreloaderThread.java
new file mode 100644
index 0000000..2145af7
--- /dev/null
+++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/TestsListPreloaderThread.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2010 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.dumprendertree2;
+
+import android.os.Environment;
+import android.os.Message;
+import android.util.Log;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.LinkedList;
+
+/**
+ * A Thread that is responsible for generating a lists of tests to run.
+ */
+public class TestsListPreloaderThread extends Thread {
+
+ private static final String LOG_TAG = "TestsListPreloaderThread";
+
+ /** TODO: make it a setting */
+ private static final String TESTS_ROOT_DIR_PATH =
+ Environment.getExternalStorageDirectory() +
+ File.separator + "android" +
+ File.separator + "LayoutTests";
+
+ /** A list containing relative paths of tests to run */
+ private ArrayList<String> mTestsList = new ArrayList<String>();
+
+ private FileFilter mFileFilter;
+
+ /**
+ * A relative path to the folder with the tests we want to run or particular test.
+ * Used up to and including preloadTests().
+ */
+ private String mRelativePath;
+
+ private Message mDoneMsg;
+
+ /**
+ * The given path must be relative to the root dir.
+ *
+ * @param path
+ * @param doneMsg
+ */
+ public TestsListPreloaderThread(String path, Message doneMsg) {
+ mFileFilter = new FileFilter(TESTS_ROOT_DIR_PATH);
+ mRelativePath = path;
+ mDoneMsg = doneMsg;
+ }
+
+ @Override
+ public void run() {
+ /** Check if the path is correct */
+ File file = new File(TESTS_ROOT_DIR_PATH, mRelativePath);
+ if (!file.exists()) {
+ Log.e(LOG_TAG + "::run", "Path does not exist: " + mRelativePath);
+ } else {
+ /** Populate the tests' list accordingly */
+ if (file.isDirectory()) {
+ preloadTests(mRelativePath);
+ } else {
+ mTestsList.add(mRelativePath);
+ }
+ }
+
+ mDoneMsg.obj = mTestsList;
+ mDoneMsg.sendToTarget();
+ }
+
+ /**
+ * Loads all the tests from the given folders and all the subfolders
+ * into mTestsList.
+ *
+ * @param dirRelativePath
+ */
+ private void preloadTests(String dirRelativePath) {
+ LinkedList<String> foldersList = new LinkedList<String>();
+ foldersList.add(dirRelativePath);
+
+ String relativePath;
+ String currentDirRelativePath;
+ String itemName;
+ File[] items;
+ while (!foldersList.isEmpty()) {
+ currentDirRelativePath = foldersList.removeFirst();
+ items = new File(TESTS_ROOT_DIR_PATH, currentDirRelativePath).listFiles();
+ for (File item : items) {
+ itemName = item.getName();
+ relativePath = currentDirRelativePath + File.separator + itemName;
+
+ if (item.isDirectory() && FileFilter.isTestDir(itemName)) {
+ foldersList.add(relativePath);
+ continue;
+ }
+
+ if (FileFilter.isTestFile(itemName)) {
+ if (!mFileFilter.isSkip(relativePath)) {
+ mTestsList.add(relativePath);
+ } else {
+ //mSummarizer.addSkippedTest(relativePath);
+ /** TODO: Summarizer is now in service - figure out how to send the info */
+ }
+ }
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/TextResult.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/TextResult.java
new file mode 100644
index 0000000..0f864e5
--- /dev/null
+++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/TextResult.java
@@ -0,0 +1,229 @@
+/*
+ * Copyright (C) 2010 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.dumprendertree2;
+
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.webkit.WebView;
+
+import name.fraser.neil.plaintext.diff_match_patch;
+
+import java.util.LinkedList;
+
+/**
+ * A result object for which the expected output is text. It does not have an image
+ * expected result.
+ *
+ * <p>Created if layoutTestController.dumpAsText() was called.
+ */
+public class TextResult extends AbstractResult {
+
+ private static final int MSG_DOCUMENT_AS_TEXT = 0;
+
+ private String mExpectedResult;
+ private String mActualResult;
+ private String mRelativePath;
+ private ResultCode mResultCode;
+ private Message mResultObtainedMsg;
+
+ private boolean mDumpChildFramesAsText;
+
+ private Handler mHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ if (msg.what == MSG_DOCUMENT_AS_TEXT) {
+ mActualResult = (String)msg.obj;
+ mResultObtainedMsg.sendToTarget();
+ }
+ }
+ };
+
+ public TextResult(String relativePath) {
+ mRelativePath = relativePath;
+ }
+
+ public void setDumpChildFramesAsText(boolean dumpChildFramesAsText) {
+ mDumpChildFramesAsText = dumpChildFramesAsText;
+ }
+
+ /**
+ * Used to recreate the Result when received by the service.
+ *
+ * @param bundle
+ * bundle with data used to recreate the result
+ */
+ public TextResult(Bundle bundle) {
+ mExpectedResult = bundle.getString("expectedTextualResult");
+ mActualResult = bundle.getString("actualTextualResult");
+ setAdditionalTextOutputString(bundle.getString("additionalTextOutputString"));
+ mRelativePath = bundle.getString("relativePath");
+ String resultCode = bundle.getString("resultCode");
+ if (resultCode != null) {
+ mResultCode = ResultCode.valueOf(resultCode);
+ }
+ }
+
+ @Override
+ public ResultCode getResultCode() {
+ if (mResultCode != null) {
+ return mResultCode;
+ }
+
+ if (mExpectedResult == null) {
+ mResultCode = AbstractResult.ResultCode.FAIL_NO_EXPECTED_RESULT;
+ } else if (!mExpectedResult.equals(mActualResult)) {
+ mResultCode = AbstractResult.ResultCode.FAIL_RESULT_DIFFERS;
+ } else {
+ mResultCode = AbstractResult.ResultCode.PASS;
+ }
+
+ return mResultCode;
+ }
+
+ @Override
+ public byte[] getActualImageResult() {
+ return null;
+ }
+
+ @Override
+ public String getActualTextResult() {
+ String additionalTextResultString = getAdditionalTextOutputString();
+ if (additionalTextResultString != null) {
+ return additionalTextResultString+ mActualResult;
+ }
+
+ return mActualResult;
+ }
+
+ @Override
+ public void setExpectedImageResult(byte[] expectedResult) {
+ /** This method is not applicable to this type of result */
+ }
+
+ @Override
+ public void setExpectedTextResult(String expectedResult) {
+ mExpectedResult = expectedResult;
+ }
+
+ @Override
+ public String getDiffAsHtml() {
+ StringBuilder html = new StringBuilder();
+ html.append("<table class=\"visual_diff\">");
+ html.append(" <tr class=\"headers\">");
+ html.append(" <td colspan=\"2\">Expected result:</td>");
+ html.append(" <td class=\"space\"></td>");
+ html.append(" <td colspan=\"2\">Actual result:</td>");
+ html.append(" </tr>");
+
+ if (mExpectedResult == null || mActualResult == null) {
+ appendNullsHtml(html);
+ } else {
+ appendDiffHtml(html);
+ }
+
+ html.append(" <tr class=\"footers\">");
+ html.append(" <td colspan=\"2\"></td>");
+ html.append(" <td class=\"space\"></td>");
+ html.append(" <td colspan=\"2\"></td>");
+ html.append(" </tr>");
+ html.append("</table>");
+
+ return html.toString();
+ }
+
+ private void appendDiffHtml(StringBuilder html) {
+ LinkedList<diff_match_patch.Diff> diffs =
+ new diff_match_patch().diff_main(mExpectedResult, mActualResult);
+
+ diffs = VisualDiffUtils.splitDiffsOnNewline(diffs);
+
+ LinkedList<String> expectedLines = new LinkedList<String>();
+ LinkedList<Integer> expectedLineNums = new LinkedList<Integer>();
+ LinkedList<String> actualLines = new LinkedList<String>();
+ LinkedList<Integer> actualLineNums = new LinkedList<Integer>();
+
+ VisualDiffUtils.generateExpectedResultLines(diffs, expectedLineNums, expectedLines);
+ VisualDiffUtils.generateActualResultLines(diffs, actualLineNums, actualLines);
+
+ html.append(VisualDiffUtils.getHtml(expectedLineNums, expectedLines,
+ actualLineNums, actualLines));
+ }
+
+ private void appendNullsHtml(StringBuilder html) {
+ /** TODO: Create a separate row for each line of not null result */
+ html.append(" <tr class=\"results\">");
+ html.append(" <td class=\"line_count\">");
+ html.append(" </td>");
+ html.append(" <td class=\"line\">");
+ if (mExpectedResult == null) {
+ html.append("Expected result was NULL");
+ } else {
+ html.append(mExpectedResult.replace("\n", "<br />"));
+ }
+ html.append(" </td>");
+ html.append(" <td class=\"space\"></td>");
+ html.append(" <td class=\"line_count\">");
+ html.append(" </td>");
+ html.append(" <td class=\"line\">");
+ if (mActualResult == null) {
+ html.append("Actual result was NULL");
+ } else {
+ html.append(mActualResult.replace("\n", "<br />"));
+ }
+ html.append(" </td>");
+ html.append(" </tr>");
+ }
+
+ @Override
+ public TestType getType() {
+ return TestType.TEXT;
+ }
+
+ @Override
+ public void obtainActualResults(WebView webview, Message resultObtainedMsg) {
+ mResultObtainedMsg = resultObtainedMsg;
+ Message msg = mHandler.obtainMessage(MSG_DOCUMENT_AS_TEXT);
+
+ /**
+ * arg1 - should dump top frame as text
+ * arg2 - should dump child frames as text
+ */
+ msg.arg1 = 1;
+ msg.arg2 = mDumpChildFramesAsText ? 1 : 0;
+ webview.documentAsText(msg);
+ }
+
+ @Override
+ public Bundle getBundle() {
+ Bundle bundle = new Bundle();
+ bundle.putString("expectedTextualResult", mExpectedResult);
+ bundle.putString("actualTextualResult", getActualTextResult());
+ bundle.putString("additionalTextOutputString", getAdditionalTextOutputString());
+ bundle.putString("relativePath", mRelativePath);
+ if (mResultCode != null) {
+ bundle.putString("resultCode", mResultCode.name());
+ }
+ bundle.putString("type", getType().name());
+ return bundle;
+ }
+
+ @Override
+ public String getRelativePath() {
+ return mRelativePath;
+ }
+} \ No newline at end of file
diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/VisualDiffUtils.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/VisualDiffUtils.java
new file mode 100644
index 0000000..250b6bc
--- /dev/null
+++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/VisualDiffUtils.java
@@ -0,0 +1,207 @@
+/*
+ * Copyright (C) 2010 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.dumprendertree2;
+
+import name.fraser.neil.plaintext.diff_match_patch;
+
+import java.util.LinkedList;
+
+/**
+ * Helper methods fo TextResult.getDiffAsHtml()
+ */
+public class VisualDiffUtils {
+
+ private static final int DONT_PRINT_LINE_NUMBER = -1;
+
+ /**
+ * Preprocesses the list of diffs so that new line characters appear only at the end of
+ * diff.text
+ *
+ * @param diffs
+ * @return
+ * LinkedList of diffs where new line character appears only on the end of
+ * diff.text
+ */
+ public static LinkedList<diff_match_patch.Diff> splitDiffsOnNewline(
+ LinkedList<diff_match_patch.Diff> diffs) {
+ LinkedList<diff_match_patch.Diff> newDiffs = new LinkedList<diff_match_patch.Diff>();
+
+ String[] parts;
+ int lengthMinusOne;
+ for (diff_match_patch.Diff diff : diffs) {
+ parts = diff.text.split("\n", -1);
+ if (parts.length == 1) {
+ newDiffs.add(diff);
+ continue;
+ }
+
+ lengthMinusOne = parts.length - 1;
+ for (int i = 0; i < lengthMinusOne; i++) {
+ newDiffs.add(new diff_match_patch.Diff(diff.operation, parts[i] + "\n"));
+ }
+ if (!parts[lengthMinusOne].isEmpty()) {
+ newDiffs.add(new diff_match_patch.Diff(diff.operation, parts[lengthMinusOne]));
+ }
+ }
+
+ return newDiffs;
+ }
+
+ public static void generateExpectedResultLines(LinkedList<diff_match_patch.Diff> diffs,
+ LinkedList<Integer> lineNums, LinkedList<String> lines) {
+ String delSpan = "<span class=\"del\">";
+ String eqlSpan = "<span class=\"eql\">";
+
+ String line = "";
+ int i = 1;
+ for (diff_match_patch.Diff diff : diffs) {
+ switch (diff.operation) {
+ case DELETE:
+ line = processDiff(diff, lineNums, lines, line, i, delSpan);
+ if (line.equals("")) {
+ i++;
+ }
+ break;
+
+ case INSERT:
+ if (diff.text.endsWith("\n")) {
+ lineNums.add(DONT_PRINT_LINE_NUMBER);
+ lines.add("");
+ }
+ break;
+
+ case EQUAL:
+ line = processDiff(diff, lineNums, lines, line, i, eqlSpan);
+ if (line.equals("")) {
+ i++;
+ }
+ break;
+ }
+ }
+
+ if (!line.isEmpty()) {
+ lines.add(line);
+ lineNums.add(i);
+ }
+ }
+
+ public static void generateActualResultLines(LinkedList<diff_match_patch.Diff> diffs,
+ LinkedList<Integer> lineNums, LinkedList<String> lines) {
+ String insSpan = "<span class=\"ins\">";
+ String eqlSpan = "<span class=\"eql\">";
+
+ String line = "";
+ int i = 1;
+ for (diff_match_patch.Diff diff : diffs) {
+ switch (diff.operation) {
+ case INSERT:
+ line = processDiff(diff, lineNums, lines, line, i, insSpan);
+ if (line.equals("")) {
+ i++;
+ }
+ break;
+
+ case DELETE:
+ if (diff.text.endsWith("\n")) {
+ lineNums.add(DONT_PRINT_LINE_NUMBER);
+ lines.add("");
+ }
+ break;
+
+ case EQUAL:
+ line = processDiff(diff, lineNums, lines, line, i, eqlSpan);
+ if (line.equals("")) {
+ i++;
+ }
+ break;
+ }
+ }
+
+ if (!line.isEmpty()) {
+ lines.add(line);
+ lineNums.add(i);
+ }
+ }
+
+ /**
+ * Generate or append a line for a given diff and add it to given collections if necessary.
+ * It puts diffs in HTML spans.
+ *
+ * @param diff
+ * @param lineNums
+ * @param lines
+ * @param line
+ * @param i
+ * @param begSpan
+ * @return
+ */
+ public static String processDiff(diff_match_patch.Diff diff, LinkedList<Integer> lineNums,
+ LinkedList<String> lines, String line, int i, String begSpan) {
+ String endSpan = "</span>";
+ String br = "&nbsp;";
+
+ if (diff.text.endsWith("\n")) {
+ lineNums.add(i++);
+ /** TODO: Think of better way to replace stuff */
+ line += begSpan + diff.text.replace(" ", "&nbsp;&nbsp;")
+ + endSpan + br;
+ lines.add(line);
+ line = "";
+ } else {
+ line += begSpan + diff.text.replace(" ", "&nbsp;&nbsp;") + endSpan;
+ }
+
+ return line;
+ }
+
+ public static String getHtml(LinkedList<Integer> lineNums1, LinkedList<String> lines1,
+ LinkedList<Integer> lineNums2, LinkedList<String> lines2) {
+ StringBuilder html = new StringBuilder();
+ int lineNum;
+ int size = lines1.size();
+ for (int i = 0; i < size; i++) {
+ html.append("<tr class=\"results\">");
+
+ html.append(" <td class=\"line_count\">");
+ lineNum = lineNums1.removeFirst();
+ if (lineNum > 0) {
+ html.append(lineNum);
+ }
+ html.append(" </td>");
+
+ html.append(" <td class=\"line\">");
+ html.append(lines1.removeFirst());
+ html.append(" </td>");
+
+ html.append(" <td class=\"space\"></td>");
+
+ html.append(" <td class=\"line_count\">");
+ lineNum = lineNums2.removeFirst();
+ if (lineNum > 0) {
+ html.append(lineNum);
+ }
+ html.append(" </td>");
+
+ html.append(" <td class=\"line\">");
+ html.append(lines2.removeFirst());
+ html.append(" </td>");
+
+ html.append("</tr>");
+ }
+ return html.toString();
+ }
+}
diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/scriptsupport/OnEverythingFinishedCallback.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/scriptsupport/OnEverythingFinishedCallback.java
new file mode 100644
index 0000000..e1d4364
--- /dev/null
+++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/scriptsupport/OnEverythingFinishedCallback.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2010 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.dumprendertree2.scriptsupport;
+
+/**
+ * Callback used to inform scriptsupport.Starter that everything is finished and
+ * we can exit
+ */
+public interface OnEverythingFinishedCallback {
+ public void onFinished();
+} \ No newline at end of file
diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/scriptsupport/ScriptTestRunner.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/scriptsupport/ScriptTestRunner.java
new file mode 100644
index 0000000..78f58d5
--- /dev/null
+++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/scriptsupport/ScriptTestRunner.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2010 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.dumprendertree2.scriptsupport;
+
+import android.os.Bundle;
+import android.test.InstrumentationTestRunner;
+
+/**
+ * Extends InstrumentationTestRunner to allow the script to pass arguments to the application
+ */
+public class ScriptTestRunner extends InstrumentationTestRunner {
+ String mTestsRelativePath;
+
+ @Override
+ public void onCreate(Bundle arguments) {
+ mTestsRelativePath = arguments.getString("path");
+ super.onCreate(arguments);
+ }
+
+ public String getTestsRelativePath() {
+ return mTestsRelativePath;
+ }
+} \ No newline at end of file
diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/scriptsupport/Starter.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/scriptsupport/Starter.java
new file mode 100644
index 0000000..ddfae69
--- /dev/null
+++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/scriptsupport/Starter.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2010 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.dumprendertree2.scriptsupport;
+
+import android.content.Intent;
+import android.test.ActivityInstrumentationTestCase2;
+import android.util.Log;
+
+import com.android.dumprendertree2.TestsListActivity;
+
+/**
+ * A class which provides methods that can be invoked by a script running on the host machine to
+ * run the tests.
+ *
+ * It starts a TestsListActivity and does not return until all the tests finish executing.
+ */
+public class Starter extends ActivityInstrumentationTestCase2<TestsListActivity> {
+ private static final String LOG_TAG = "Starter";
+ private boolean mEverythingFinished;
+
+ public Starter() {
+ super(TestsListActivity.class);
+ }
+
+ /**
+ * This method is called from adb to start executing the tests. It doesn't return
+ * until everything is finished so that the script can wait for the end if it needs
+ * to.
+ */
+ public void startLayoutTests() {
+ ScriptTestRunner runner = (ScriptTestRunner)getInstrumentation();
+ String relativePath = runner.getTestsRelativePath();
+
+ Intent intent = new Intent();
+ intent.setClassName("com.android.dumprendertree2", "TestsListActivity");
+ intent.setAction(Intent.ACTION_RUN);
+ intent.putExtra(TestsListActivity.EXTRA_TEST_PATH, relativePath);
+ setActivityIntent(intent);
+ getActivity().registerOnEverythingFinishedCallback(new OnEverythingFinishedCallback() {
+ /** This method is safe to call on any thread */
+ @Override
+ public void onFinished() {
+ synchronized (Starter.this) {
+ mEverythingFinished = true;
+ Starter.this.notifyAll();
+ }
+ }
+ });
+
+ synchronized (this) {
+ while (!mEverythingFinished) {
+ try {
+ this.wait();
+ } catch (InterruptedException e) {
+ Log.e(LOG_TAG + "::startLayoutTests", e.getMessage());
+ }
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/ui/DirListActivity.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/ui/DirListActivity.java
new file mode 100644
index 0000000..af0d7d1
--- /dev/null
+++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/ui/DirListActivity.java
@@ -0,0 +1,404 @@
+/*
+ * Copyright (C) 2010 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.dumprendertree2.ui;
+
+import com.android.dumprendertree2.FileFilter;
+import com.android.dumprendertree2.TestsListActivity;
+import com.android.dumprendertree2.R;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.ListActivity;
+import android.app.ProgressDialog;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.res.Configuration;
+import android.os.Bundle;
+import android.os.Environment;
+import android.os.Handler;
+import android.os.Message;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+import android.widget.ImageView;
+import android.widget.ListView;
+import android.widget.TextView;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * An Activity that allows navigating through tests folders and choosing folders or tests to run.
+ */
+public class DirListActivity extends ListActivity {
+
+ private static final String LOG_TAG = "DirListActivity";
+ private static final String ROOT_DIR_PATH =
+ Environment.getExternalStorageDirectory() +
+ File.separator + "android" +
+ File.separator + "LayoutTests";
+
+ /** TODO: This is just a guess - think of a better way to achieve it */
+ private static final int MEAN_TITLE_CHAR_SIZE = 13;
+
+ private static final int PROGRESS_DIALOG_DELAY_MS = 200;
+
+ /** Code for the dialog, used in showDialog and onCreateDialog */
+ private static final int DIALOG_RUN_ABORT_DIR = 0;
+
+ /** Messages codes */
+ private static final int MSG_LOADED_ITEMS = 0;
+ private static final int MSG_SHOW_PROGRESS_DIALOG = 1;
+
+ /** Initialized lazily before first sProgressDialog.show() */
+ private static ProgressDialog sProgressDialog;
+
+ private ListView mListView;
+
+ /** This is a relative path! */
+ private String mCurrentDirPath;
+
+ /**
+ * TODO: This should not be a constant, but rather be configurable from somewhere.
+ */
+ private String mRootDirPath = ROOT_DIR_PATH;
+
+ private FileFilter mFileFilter;
+
+ /**
+ * A thread responsible for loading the contents of the directory from sd card
+ * and sending them via Message to main thread that then loads them into
+ * ListView
+ */
+ private class LoadListItemsThread extends Thread {
+ private Handler mHandler;
+ private String mRelativePath;
+
+ public LoadListItemsThread(String relativePath, Handler handler) {
+ mRelativePath = relativePath;
+ mHandler = handler;
+ }
+
+ @Override
+ public void run() {
+ Message msg = mHandler.obtainMessage(MSG_LOADED_ITEMS);
+ msg.obj = getDirList(mRelativePath);
+ mHandler.sendMessage(msg);
+ }
+ }
+
+ /**
+ * Very simple object to use inside ListView as an item.
+ */
+ private static class ListItem implements Comparable<ListItem> {
+ private String mRelativePath;
+ private String mName;
+ private boolean mIsDirectory;
+
+ public ListItem(String relativePath, boolean isDirectory) {
+ mRelativePath = relativePath;
+ mName = new File(relativePath).getName();
+ mIsDirectory = isDirectory;
+ }
+
+ public boolean isDirectory() {
+ return mIsDirectory;
+ }
+
+ public String getRelativePath() {
+ return mRelativePath;
+ }
+
+ public String getName() {
+ return mName;
+ }
+
+ @Override
+ public int compareTo(ListItem another) {
+ return mRelativePath.compareTo(another.getRelativePath());
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (!(o instanceof ListItem)) {
+ return false;
+ }
+
+ return mRelativePath.equals(((ListItem) o).getRelativePath());
+ }
+
+ @Override
+ public int hashCode() {
+ return mRelativePath.hashCode();
+ }
+
+ }
+
+ /**
+ * A custom adapter that sets the proper icon and label in the list view.
+ */
+ private static class DirListAdapter extends ArrayAdapter<ListItem> {
+ private Activity mContext;
+ private ListItem[] mItems;
+
+ public DirListAdapter(Activity context, ListItem[] items) {
+ super(context, R.layout.dirlist_row, items);
+
+ mContext = context;
+ mItems = items;
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ LayoutInflater inflater = mContext.getLayoutInflater();
+ View row = inflater.inflate(R.layout.dirlist_row, null);
+
+ TextView label = (TextView) row.findViewById(R.id.label);
+ label.setText(mItems[position].getName());
+
+ ImageView icon = (ImageView) row.findViewById(R.id.icon);
+ if (mItems[position].isDirectory()) {
+ icon.setImageResource(R.drawable.folder);
+ } else {
+ icon.setImageResource(R.drawable.runtest);
+ }
+
+ return row;
+ }
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ mFileFilter = new FileFilter(ROOT_DIR_PATH);
+ mListView = getListView();
+
+ mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
+ @Override
+ public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+ ListItem item = (ListItem) parent.getItemAtPosition(position);
+
+ if (item.isDirectory()) {
+ showDir(item.getRelativePath());
+ } else {
+ /** Run the test */
+ Intent intent = new Intent();
+ intent.setClass(DirListActivity.this, TestsListActivity.class);
+ intent.setAction(Intent.ACTION_RUN);
+ intent.putExtra(TestsListActivity.EXTRA_TEST_PATH, item.getRelativePath());
+ startActivity(intent);
+ }
+ }
+ });
+
+ mListView.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
+ @Override
+ public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
+ ListItem item = (ListItem) parent.getItemAtPosition(position);
+
+ if (item.isDirectory()) {
+ Bundle arguments = new Bundle(1);
+ arguments.putString("name", item.getName());
+ arguments.putString("relativePath", item.getRelativePath());
+ showDialog(DIALOG_RUN_ABORT_DIR, arguments);
+ } else {
+ /** TODO: Maybe show some info about a test? */
+ }
+
+ return true;
+ }
+ });
+
+ /** All the paths are relative to test root dir where possible */
+ showDir("");
+ }
+
+ @Override
+ /**
+ * Moves to the parent directory if one exists. Does not allow to move above
+ * the test 'root' directory.
+ */
+ public void onBackPressed() {
+ File currentDirParent = new File(mCurrentDirPath).getParentFile();
+ if (currentDirParent != null) {
+ showDir(currentDirParent.getPath());
+ } else {
+ showDir("");
+ }
+ }
+
+ /**
+ * Prevents the activity from recreating on change of orientation. The title needs to
+ * be recalculated.
+ */
+ @Override
+ public void onConfigurationChanged(Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+
+ setTitle(shortenTitle(mCurrentDirPath));
+ }
+
+ @Override
+ protected Dialog onCreateDialog(int id, final Bundle args) {
+ Dialog dialog = null;
+ AlertDialog.Builder builder = new AlertDialog.Builder(this);
+
+ switch (id) {
+ case DIALOG_RUN_ABORT_DIR:
+ builder.setTitle(getText(R.string.dialog_run_abort_dir_title_prefix) + " " +
+ args.getString("name"));
+ builder.setMessage(R.string.dialog_run_abort_dir_msg);
+ builder.setCancelable(true);
+
+ builder.setPositiveButton(R.string.dialog_run_abort_dir_ok_button,
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ removeDialog(DIALOG_RUN_ABORT_DIR);
+ /** Run the tests */
+ Intent intent = new Intent();
+ intent.setClass(DirListActivity.this, TestsListActivity.class);
+ intent.setAction(Intent.ACTION_RUN);
+ intent.putExtra(TestsListActivity.EXTRA_TEST_PATH,
+ args.getString("relativePath"));
+ startActivity(intent);
+ }
+ });
+
+ builder.setNegativeButton(R.string.dialog_run_abort_dir_abort_button,
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ removeDialog(DIALOG_RUN_ABORT_DIR);
+ }
+ });
+
+ dialog = builder.create();
+ dialog.setOnCancelListener(new DialogInterface.OnCancelListener() {
+ @Override
+ public void onCancel(DialogInterface dialog) {
+ removeDialog(DIALOG_RUN_ABORT_DIR);
+ }
+ });
+ break;
+ }
+
+ return dialog;
+ }
+
+ /**
+ * Loads the contents of dir into the list view.
+ *
+ * @param dirPath
+ * directory to load into list view
+ */
+ private void showDir(String dirPath) {
+ mCurrentDirPath = dirPath;
+
+ /** Show progress dialog with a delay */
+ final Handler delayedDialogHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ if (msg.what == MSG_SHOW_PROGRESS_DIALOG) {
+ if (sProgressDialog == null) {
+ sProgressDialog = new ProgressDialog(DirListActivity.this);
+ sProgressDialog.setCancelable(false);
+ sProgressDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
+ sProgressDialog.setTitle(R.string.dialog_progress_title);
+ sProgressDialog.setMessage(getText(R.string.dialog_progress_msg));
+ }
+ sProgressDialog.show();
+ }
+ }
+ };
+ Message msgShowDialog = delayedDialogHandler.obtainMessage(MSG_SHOW_PROGRESS_DIALOG);
+ delayedDialogHandler.sendMessageDelayed(msgShowDialog, PROGRESS_DIALOG_DELAY_MS);
+
+ /** Delegate loading contents from SD card to a new thread */
+ new LoadListItemsThread(mCurrentDirPath, new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ if (msg.what == MSG_LOADED_ITEMS) {
+ setListAdapter(new DirListAdapter(DirListActivity.this,
+ (ListItem[])msg.obj));
+ delayedDialogHandler.removeMessages(MSG_SHOW_PROGRESS_DIALOG);
+ setTitle(shortenTitle(mCurrentDirPath));
+ if (sProgressDialog != null) {
+ sProgressDialog.dismiss();
+ }
+ }
+ }
+ }).start();
+ }
+
+ /**
+ * TODO: find a neat way to determine number of characters that fit in the title
+ * bar.
+ * */
+ private String shortenTitle(String title) {
+ if (title.equals("")) {
+ return "Tests' root dir:";
+ }
+ int charCount = mListView.getWidth() / MEAN_TITLE_CHAR_SIZE;
+
+ if (title.length() > charCount) {
+ return "..." + title.substring(title.length() - charCount);
+ } else {
+ return title;
+ }
+ }
+
+ /**
+ * Return the array with contents of the given directory.
+ * First it contains the subfolders, then the files. Both sorted
+ * alphabetically.
+ *
+ * The dirPath is relative.
+ */
+ private ListItem[] getDirList(String dirPath) {
+ File dir = new File(mRootDirPath, dirPath);
+
+ if (!dir.exists()) {
+ return new ListItem[0];
+ }
+
+ List<ListItem> subDirs = new ArrayList<ListItem>();
+ List<ListItem> subFiles = new ArrayList<ListItem>();
+
+ for (File item : dir.listFiles()) {
+ if (item.isDirectory() && FileFilter.isTestDir(item.getName())) {
+ subDirs.add(new ListItem(mFileFilter.getRelativePath(item), true));
+ } else if (FileFilter.isTestFile(item.getName())) {
+ subFiles.add(new ListItem(mFileFilter.getRelativePath(item), false));
+ }
+ }
+
+ Collections.sort(subDirs);
+ Collections.sort(subFiles);
+
+ /** Concatenate the two lists */
+ subDirs.addAll(subFiles);
+
+ return subDirs.toArray(new ListItem[subDirs.size()]);
+ }
+} \ No newline at end of file
diff --git a/tests/HwAccelerationTest/Android.mk b/tests/HwAccelerationTest/Android.mk
new file mode 100644
index 0000000..d4743f9
--- /dev/null
+++ b/tests/HwAccelerationTest/Android.mk
@@ -0,0 +1,26 @@
+#
+# Copyright (C) 2010 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-subdir-java-files)
+
+LOCAL_PACKAGE_NAME := HwAccelerationTest
+
+LOCAL_MODULE_TAGS := tests
+
+include $(BUILD_PACKAGE)
diff --git a/tests/HwAccelerationTest/AndroidManifest.xml b/tests/HwAccelerationTest/AndroidManifest.xml
new file mode 100644
index 0000000..564f13e
--- /dev/null
+++ b/tests/HwAccelerationTest/AndroidManifest.xml
@@ -0,0 +1,212 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 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.google.android.test.hwui">
+
+ <application
+ android:label="HwUi"
+ android:hardwareAccelerated="true">
+
+ <activity
+ android:name="AlphaLayersActivity"
+ android:label="_αLayers">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+
+ <activity
+ android:name="LayersActivity"
+ android:label="_Layers"
+ android:theme="@android:style/Theme.Translucent.NoTitleBar">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+
+ <activity
+ android:name="XfermodeActivity"
+ android:label="_Xfermodes"
+ android:theme="@android:style/Theme.Translucent.NoTitleBar">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+
+ <activity
+ android:name="BitmapsActivity"
+ android:label="_Bitmaps"
+ android:theme="@android:style/Theme.Translucent.NoTitleBar">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+
+ <activity
+ android:name="BitmapsAlphaActivity"
+ android:label="_BitmapsAlpha"
+ android:theme="@android:style/Theme.Translucent.NoTitleBar">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+
+ <activity
+ android:name="BitmapsRectActivity"
+ android:label="_BitmapsRect"
+ android:theme="@android:style/Theme.Translucent.NoTitleBar">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+
+ <activity
+ android:name="NinePatchesActivity"
+ android:label="_9patch">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+
+ <activity
+ android:name="QuickRejectActivity"
+ android:label="_QuickReject">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+
+ <activity
+ android:name="RotationActivity"
+ android:label="_Rotation">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+
+ <activity
+ android:name="ShadersActivity"
+ android:label="_Shaders">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+
+ <activity
+ android:name="TextActivity"
+ android:label="_Text"
+ android:theme="@android:style/Theme.NoTitleBar">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+
+ <activity
+ android:name="ListActivity"
+ android:label="_List">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+
+ <activity
+ android:name="MoreShadersActivity"
+ android:label="_Shaders2">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+
+ <activity
+ android:name="ColorFiltersActivity"
+ android:label="_ColorFilters">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+
+ <activity
+ android:name="LinesActivity"
+ android:label="_Lines"
+ android:hardwareAccelerated="false">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+
+ <activity
+ android:name="PathsActivity"
+ android:label="_Paths">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+
+ <activity
+ android:name="Transform3dActivity"
+ android:label="_3d">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+
+ <activity
+ android:name="SimplePathsActivity"
+ android:label="_SimplePaths">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+
+ <activity
+ android:name="AdvancedBlendActivity"
+ android:label="_AdvancedBlend">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+
+ <activity
+ android:name="StackActivity"
+ android:label="_Stacks">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+
+ </application>
+</manifest>
diff --git a/tests/HwAccelerationTest/res/drawable-hdpi/icon.png b/tests/HwAccelerationTest/res/drawable-hdpi/icon.png
new file mode 100644
index 0000000..60fbdf5
--- /dev/null
+++ b/tests/HwAccelerationTest/res/drawable-hdpi/icon.png
Binary files differ
diff --git a/tests/HwAccelerationTest/res/drawable-hdpi/sunset1.jpg b/tests/HwAccelerationTest/res/drawable-hdpi/sunset1.jpg
new file mode 100644
index 0000000..92851f3
--- /dev/null
+++ b/tests/HwAccelerationTest/res/drawable-hdpi/sunset1.jpg
Binary files differ
diff --git a/tests/HwAccelerationTest/res/drawable-hdpi/sunset2.png b/tests/HwAccelerationTest/res/drawable-hdpi/sunset2.png
new file mode 100644
index 0000000..3258ee7
--- /dev/null
+++ b/tests/HwAccelerationTest/res/drawable-hdpi/sunset2.png
Binary files differ
diff --git a/tests/HwAccelerationTest/res/drawable-hdpi/sunset3.png b/tests/HwAccelerationTest/res/drawable-hdpi/sunset3.png
new file mode 100644
index 0000000..6508b27
--- /dev/null
+++ b/tests/HwAccelerationTest/res/drawable-hdpi/sunset3.png
Binary files differ
diff --git a/tests/HwAccelerationTest/res/drawable/icon.png b/tests/HwAccelerationTest/res/drawable/icon.png
new file mode 100644
index 0000000..cb40a19
--- /dev/null
+++ b/tests/HwAccelerationTest/res/drawable/icon.png
Binary files differ
diff --git a/tests/HwAccelerationTest/res/drawable/sunset1.jpg b/tests/HwAccelerationTest/res/drawable/sunset1.jpg
new file mode 100644
index 0000000..92851f3
--- /dev/null
+++ b/tests/HwAccelerationTest/res/drawable/sunset1.jpg
Binary files differ
diff --git a/tests/HwAccelerationTest/res/drawable/sunset2.png b/tests/HwAccelerationTest/res/drawable/sunset2.png
new file mode 100644
index 0000000..3258ee7
--- /dev/null
+++ b/tests/HwAccelerationTest/res/drawable/sunset2.png
Binary files differ
diff --git a/tests/HwAccelerationTest/res/drawable/sunset3.png b/tests/HwAccelerationTest/res/drawable/sunset3.png
new file mode 100644
index 0000000..6508b27
--- /dev/null
+++ b/tests/HwAccelerationTest/res/drawable/sunset3.png
Binary files differ
diff --git a/tests/HwAccelerationTest/res/layout/list_activity.xml b/tests/HwAccelerationTest/res/layout/list_activity.xml
new file mode 100644
index 0000000..6bba370
--- /dev/null
+++ b/tests/HwAccelerationTest/res/layout/list_activity.xml
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <LinearLayout
+ android:orientation="horizontal"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+
+ <Button
+ android:layout_width="0dip"
+ android:layout_weight="1.0"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="10dip"
+ android:layout_marginRight="3dip"
+
+ android:text="Add" />
+
+ <Button
+ android:layout_width="0dip"
+ android:layout_weight="1.0"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="3dip"
+ android:layout_marginRight="10dip"
+
+ android:text="Remove" />
+
+ </LinearLayout>
+
+ <ListView
+ android:id="@+id/list"
+ android:layout_width="match_parent"
+ android:layout_height="0dip"
+ android:layout_weight="1.0" />
+
+ <LinearLayout
+ android:orientation="horizontal"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+
+ <Button
+ android:layout_width="0dip"
+ android:layout_weight="1.0"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="10dip"
+ android:layout_marginRight="3dip"
+
+ android:text="Add" />
+
+ <Button
+ android:layout_width="0dip"
+ android:layout_weight="1.0"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="3dip"
+ android:layout_marginRight="10dip"
+
+ android:text="Remove" />
+
+ </LinearLayout>
+
+</LinearLayout>
diff --git a/tests/HwAccelerationTest/res/layout/stack.xml b/tests/HwAccelerationTest/res/layout/stack.xml
new file mode 100644
index 0000000..b4d2d73a
--- /dev/null
+++ b/tests/HwAccelerationTest/res/layout/stack.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 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.
+-->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:paddingTop="0dp"
+ android:paddingBottom="0dp"
+ android:paddingLeft="12dp"
+ android:paddingRight="12dp"
+ android:focusable="true">
+ <StackView
+ android:id="@+id/stack_view"
+ android:layout_width="348px"
+ android:layout_height="374px"
+ android:layout_gravity="center"
+ android:background="#00000000"
+ android:cacheColorHint="#00000000"
+ android:autoStart="true" />
+</FrameLayout>
diff --git a/tests/HwAccelerationTest/res/layout/stack_item.xml b/tests/HwAccelerationTest/res/layout/stack_item.xml
new file mode 100644
index 0000000..3504018
--- /dev/null
+++ b/tests/HwAccelerationTest/res/layout/stack_item.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 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.
+-->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/stack_item"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent">
+ <FrameLayout
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent">
+ <ImageView android:id="@+id/textview_icon"
+ android:layout_height="250dip"
+ android:layout_width="250dip"
+ android:layout_gravity="center" />
+ <TextView android:id="@+id/mini_text"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center" />
+ </FrameLayout>
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:visibility="gone"
+ android:layout_gravity="center" />
+</FrameLayout> \ No newline at end of file
diff --git a/tests/HwAccelerationTest/src/com/google/android/test/hwui/AdvancedBlendActivity.java b/tests/HwAccelerationTest/src/com/google/android/test/hwui/AdvancedBlendActivity.java
new file mode 100644
index 0000000..6c80a6d
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/google/android/test/hwui/AdvancedBlendActivity.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2010 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.google.android.test.hwui;
+
+import android.app.Activity;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.BitmapShader;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.ComposeShader;
+import android.graphics.LinearGradient;
+import android.graphics.Matrix;
+import android.graphics.Paint;
+import android.graphics.PorterDuff;
+import android.graphics.Shader;
+import android.os.Bundle;
+import android.view.View;
+
+@SuppressWarnings({"UnusedDeclaration"})
+public class AdvancedBlendActivity extends Activity {
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ setContentView(new ShadersView(this));
+ }
+
+ static class ShadersView extends View {
+ private BitmapShader mScaledShader;
+ private int mTexWidth;
+ private int mTexHeight;
+ private Paint mPaint;
+ private float mDrawWidth;
+ private float mDrawHeight;
+ private LinearGradient mHorGradient;
+ private ComposeShader mComposeShader;
+ private ComposeShader mCompose2Shader;
+ private ComposeShader mCompose3Shader;
+ private ComposeShader mCompose4Shader;
+ private ComposeShader mCompose5Shader;
+ private ComposeShader mCompose6Shader;
+ private BitmapShader mScaled2Shader;
+
+ ShadersView(Context c) {
+ super(c);
+
+ Bitmap texture = BitmapFactory.decodeResource(c.getResources(), R.drawable.sunset1);
+ mTexWidth = texture.getWidth();
+ mTexHeight = texture.getHeight();
+ mDrawWidth = mTexWidth * 2.2f;
+ mDrawHeight = mTexHeight * 1.2f;
+
+ mScaledShader = new BitmapShader(texture, Shader.TileMode.MIRROR,
+ Shader.TileMode.MIRROR);
+ Matrix m2 = new Matrix();
+ m2.setScale(0.5f, 0.5f);
+ mScaledShader.setLocalMatrix(m2);
+
+ mScaled2Shader = new BitmapShader(texture, Shader.TileMode.MIRROR,
+ Shader.TileMode.MIRROR);
+ Matrix m3 = new Matrix();
+ m3.setScale(0.1f, 0.1f);
+ mScaled2Shader.setLocalMatrix(m3);
+
+ mHorGradient = new LinearGradient(0.0f, 0.0f, mDrawWidth, 0.0f,
+ Color.BLACK, Color.WHITE, Shader.TileMode.CLAMP);
+
+ mComposeShader = new ComposeShader(mScaledShader, mHorGradient,
+ PorterDuff.Mode.DARKEN);
+ mCompose2Shader = new ComposeShader(mScaledShader, mHorGradient,
+ PorterDuff.Mode.LIGHTEN);
+ mCompose3Shader = new ComposeShader(mScaledShader, mHorGradient,
+ PorterDuff.Mode.MULTIPLY);
+ mCompose4Shader = new ComposeShader(mScaledShader, mHorGradient,
+ PorterDuff.Mode.SCREEN);
+ mCompose5Shader = new ComposeShader(mScaledShader, mHorGradient,
+ PorterDuff.Mode.ADD);
+ mCompose6Shader = new ComposeShader(mHorGradient, mScaledShader,
+ PorterDuff.Mode.OVERLAY);
+
+ mPaint = new Paint();
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ super.onDraw(canvas);
+ canvas.drawRGB(255, 255, 255);
+
+ canvas.save();
+ canvas.translate(40.0f, 40.0f);
+
+ mPaint.setShader(mComposeShader);
+ canvas.drawRect(0.0f, 0.0f, mDrawWidth, mDrawHeight, mPaint);
+
+ canvas.translate(0.0f, 40.0f + mDrawHeight);
+ mPaint.setShader(mCompose2Shader);
+ canvas.drawRect(0.0f, 0.0f, mDrawWidth, mDrawHeight, mPaint);
+
+ canvas.translate(0.0f, 40.0f + mDrawHeight);
+ mPaint.setShader(mCompose3Shader);
+ canvas.drawRect(0.0f, 0.0f, mDrawWidth, mDrawHeight, mPaint);
+
+ canvas.restore();
+
+ canvas.save();
+ canvas.translate(40.0f + mDrawWidth + 40.0f, 40.0f);
+
+ mPaint.setShader(mCompose4Shader);
+ canvas.drawRect(0.0f, 0.0f, mDrawWidth, mDrawHeight, mPaint);
+
+ canvas.translate(0.0f, 40.0f + mDrawHeight);
+ mPaint.setShader(mCompose5Shader);
+ canvas.drawRect(0.0f, 0.0f, mDrawWidth, mDrawHeight, mPaint);
+
+ canvas.translate(0.0f, 40.0f + mDrawHeight);
+ mPaint.setShader(mCompose6Shader);
+ canvas.drawRect(0.0f, 0.0f, mDrawWidth, mDrawHeight, mPaint);
+
+ canvas.restore();
+ }
+ }
+}
diff --git a/tests/HwAccelerationTest/src/com/google/android/test/hwui/AlphaLayersActivity.java b/tests/HwAccelerationTest/src/com/google/android/test/hwui/AlphaLayersActivity.java
new file mode 100644
index 0000000..0217a05
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/google/android/test/hwui/AlphaLayersActivity.java
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2010 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.google.android.test.hwui;
+
+import android.app.Activity;
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.Gravity;
+import android.view.View;
+import android.view.animation.AlphaAnimation;
+import android.view.animation.Animation;
+import android.widget.FrameLayout;
+
+@SuppressWarnings({"UnusedDeclaration"})
+public class AlphaLayersActivity extends Activity {
+ private static final String LOG_TAG = "HwUi";
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ DirtyBitmapView container = new DirtyBitmapView(this);
+
+ ColorView color = new ColorView(this);
+ container.addView(color, new DirtyBitmapView.LayoutParams(
+ dipToPx(this, 100), dipToPx(this, 100), Gravity.CENTER));
+
+ AlphaAnimation a = new AlphaAnimation(1.0f, 0.0f);
+ a.setDuration(2000);
+ a.setRepeatCount(Animation.INFINITE);
+ a.setRepeatMode(Animation.REVERSE);
+ color.startAnimation(a);
+
+ setContentView(container);
+ }
+
+ @SuppressWarnings({"UnusedDeclaration"})
+ static int dipToPx(Context c, int dip) {
+ return (int) (c.getResources().getDisplayMetrics().density * dip + 0.5f);
+ }
+
+ static class ColorView extends View {
+ ColorView(Context c) {
+ super(c);
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ super.onDraw(canvas);
+ canvas.drawRGB(0, 255, 0);
+ }
+ }
+
+ static class DirtyBitmapView extends FrameLayout {
+ private final Paint mPaint;
+
+ DirtyBitmapView(Context c) {
+ super(c);
+ mPaint = new Paint();
+ }
+
+ @Override
+ public void dispatchDraw(Canvas canvas) {
+ canvas.drawRGB(255, 255, 255);
+
+ mPaint.setColor(0xffff0000);
+ canvas.drawRect(200.0f, 0.0f, 220.0f, 20.0f, mPaint);
+
+ canvas.save();
+ canvas.clipRect(20.0f, 0.0f, 40.0f, 20.0f);
+ Log.d(LOG_TAG, "clipRect = " + canvas.getClipBounds());
+ Log.d(LOG_TAG, "rejected = " + canvas.quickReject(100.0f, 100.0f, 110.0f, 110.0f,
+ Canvas.EdgeType.BW));
+ Log.d(LOG_TAG, "rejected = " + canvas.quickReject(25.0f, 5.0f, 30.0f, 10.0f,
+ Canvas.EdgeType.BW));
+ canvas.restore();
+
+ canvas.save();
+ canvas.scale(2.0f, 2.0f);
+ canvas.clipRect(20.0f, 0.0f, 40.0f, 20.0f);
+ Log.d(LOG_TAG, "clipRect = " + canvas.getClipBounds());
+ Log.d(LOG_TAG, "rejected = " + canvas.quickReject(50.0f, 50.0f, 60.0f, 60.0f,
+ Canvas.EdgeType.BW));
+ Log.d(LOG_TAG, "rejected = " + canvas.quickReject(25.0f, 5.0f, 30.0f, 10.0f,
+ Canvas.EdgeType.BW));
+ canvas.restore();
+
+ canvas.save();
+ canvas.translate(20.0f, 20.0f);
+ canvas.clipRect(20.0f, 0.0f, 40.0f, 20.0f);
+ Log.d(LOG_TAG, "clipRect = " + canvas.getClipBounds());
+ Log.d(LOG_TAG, "rejected = " + canvas.quickReject(80.0f, 80.0f, 90.0f, 90.0f,
+ Canvas.EdgeType.BW));
+ Log.d(LOG_TAG, "rejected = " + canvas.quickReject(25.0f, 5.0f, 30.0f, 10.0f,
+ Canvas.EdgeType.BW));
+ canvas.restore();
+
+ canvas.save();
+ canvas.scale(2.0f, 2.0f);
+ canvas.clipRect(20.0f, 0.0f, 40.0f, 20.0f);
+
+ mPaint.setColor(0xff00ff00);
+ canvas.drawRect(0.0f, 0.0f, 20.0f, 20.0f, mPaint);
+
+ mPaint.setColor(0xff0000ff);
+ canvas.drawRect(20.0f, 0.0f, 40.0f, 20.0f, mPaint);
+
+ canvas.restore();
+
+ final int restoreTo = canvas.save();
+ canvas.saveLayerAlpha(0.0f, 100.0f, getWidth(), 150.0f, 127,
+ Canvas.HAS_ALPHA_LAYER_SAVE_FLAG | Canvas.CLIP_TO_LAYER_SAVE_FLAG);
+ mPaint.setColor(0xff0000ff);
+ canvas.drawRect(0.0f, 100.0f, 40.0f, 150.0f, mPaint);
+ mPaint.setColor(0xff00ffff);
+ canvas.drawRect(40.0f, 100.0f, 140.0f, 150.0f, mPaint);
+ mPaint.setColor(0xffff00ff);
+ canvas.drawRect(140.0f, 100.0f, 240.0f, 150.0f, mPaint);
+ canvas.restoreToCount(restoreTo);
+
+ super.dispatchDraw(canvas);
+ }
+ }
+}
diff --git a/tests/HwAccelerationTest/src/com/google/android/test/hwui/BitmapsActivity.java b/tests/HwAccelerationTest/src/com/google/android/test/hwui/BitmapsActivity.java
new file mode 100644
index 0000000..cfa8d3c
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/google/android/test/hwui/BitmapsActivity.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2010 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.google.android.test.hwui;
+
+import android.app.Activity;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffXfermode;
+import android.os.Bundle;
+import android.view.Gravity;
+import android.view.View;
+import android.view.animation.Animation;
+import android.view.animation.ScaleAnimation;
+import android.widget.FrameLayout;
+
+@SuppressWarnings({"UnusedDeclaration"})
+public class BitmapsActivity extends Activity {
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ final BitmapsView view = new BitmapsView(this);
+ final FrameLayout layout = new FrameLayout(this);
+ layout.addView(view, new FrameLayout.LayoutParams(480, 800, Gravity.CENTER));
+ setContentView(layout);
+
+ ScaleAnimation a = new ScaleAnimation(1.0f, 2.0f, 1.0f, 2.0f,
+ ScaleAnimation.RELATIVE_TO_SELF, 0.5f,
+ ScaleAnimation.RELATIVE_TO_SELF,0.5f);
+ a.setDuration(2000);
+ a.setRepeatCount(Animation.INFINITE);
+ a.setRepeatMode(Animation.REVERSE);
+ view.startAnimation(a);
+ }
+
+ static class BitmapsView extends View {
+ private Paint mBitmapPaint;
+ private final Bitmap mBitmap1;
+ private final Bitmap mBitmap2;
+ private final PorterDuffXfermode mDstIn;
+
+ BitmapsView(Context c) {
+ super(c);
+
+ mBitmap1 = BitmapFactory.decodeResource(c.getResources(), R.drawable.sunset1);
+ mBitmap2 = BitmapFactory.decodeResource(c.getResources(), R.drawable.sunset2);
+
+ mBitmapPaint = new Paint();
+ mDstIn = new PorterDuffXfermode(PorterDuff.Mode.DST_IN);
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ super.onDraw(canvas);
+
+ canvas.translate(120.0f, 50.0f);
+ canvas.drawBitmap(mBitmap1, 0.0f, 0.0f, mBitmapPaint);
+
+ canvas.translate(0.0f, mBitmap1.getHeight());
+ canvas.translate(0.0f, 25.0f);
+ canvas.drawBitmap(mBitmap2, 0.0f, 0.0f, null);
+
+ mBitmapPaint.setAlpha(127);
+ canvas.translate(0.0f, mBitmap2.getHeight());
+ canvas.translate(0.0f, 25.0f);
+ canvas.drawBitmap(mBitmap1, 0.0f, 0.0f, mBitmapPaint);
+
+ mBitmapPaint.setAlpha(255);
+ canvas.translate(0.0f, mBitmap1.getHeight());
+ canvas.translate(0.0f, 25.0f);
+ mBitmapPaint.setColor(0xffff0000);
+ canvas.drawRect(0.0f, 0.0f, mBitmap2.getWidth(), mBitmap2.getHeight(), mBitmapPaint);
+ mBitmapPaint.setXfermode(mDstIn);
+ canvas.drawBitmap(mBitmap2, 0.0f, 0.0f, mBitmapPaint);
+
+ mBitmapPaint.reset();
+ }
+ }
+}
diff --git a/tests/HwAccelerationTest/src/com/google/android/test/hwui/BitmapsAlphaActivity.java b/tests/HwAccelerationTest/src/com/google/android/test/hwui/BitmapsAlphaActivity.java
new file mode 100644
index 0000000..f6fd8fe
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/google/android/test/hwui/BitmapsAlphaActivity.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2010 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.google.android.test.hwui;
+
+import android.app.Activity;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.Gravity;
+import android.view.View;
+import android.widget.FrameLayout;
+
+@SuppressWarnings({"UnusedDeclaration"})
+public class BitmapsAlphaActivity extends Activity {
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ final BitmapsView view = new BitmapsView(this);
+ final FrameLayout layout = new FrameLayout(this);
+ layout.addView(view, new FrameLayout.LayoutParams(480, 800, Gravity.CENTER));
+ setContentView(layout);
+ }
+
+ static class BitmapsView extends View {
+ private Paint mBitmapPaint;
+ private final Bitmap mBitmap1;
+ private final Bitmap mBitmap2;
+ private Bitmap mBitmap3;
+
+ BitmapsView(Context c) {
+ super(c);
+
+ Log.d("OpenGLRenderer", "Loading sunset1, default options");
+ mBitmap1 = BitmapFactory.decodeResource(c.getResources(), R.drawable.sunset1);
+ Log.d("OpenGLRenderer", "Loading sunset2, default options");
+ mBitmap2 = BitmapFactory.decodeResource(c.getResources(), R.drawable.sunset2);
+ Log.d("OpenGLRenderer", "Loading sunset3, forcing ARGB-8888");
+ BitmapFactory.Options opts = new BitmapFactory.Options();
+ opts.inPreferredConfig = Bitmap.Config.ARGB_8888;
+ mBitmap3 = BitmapFactory.decodeResource(c.getResources(), R.drawable.sunset3, opts);
+ Log.d("OpenGLRenderer", " has bitmap alpha? " + mBitmap3.hasAlpha());
+
+ mBitmapPaint = new Paint();
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ super.onDraw(canvas);
+
+ Log.d("OpenGLRenderer", "================= Draw");
+
+ canvas.translate(120.0f, 50.0f);
+ canvas.drawBitmap(mBitmap1, 0.0f, 0.0f, mBitmapPaint);
+
+ canvas.translate(0.0f, mBitmap1.getHeight());
+ canvas.translate(0.0f, 25.0f);
+ canvas.drawBitmap(mBitmap2, 0.0f, 0.0f, null);
+
+ canvas.translate(0.0f, mBitmap2.getHeight());
+ canvas.translate(0.0f, 25.0f);
+ canvas.drawBitmap(mBitmap3, 0.0f, 0.0f, null);
+ }
+ }
+}
diff --git a/tests/HwAccelerationTest/src/com/google/android/test/hwui/BitmapsRectActivity.java b/tests/HwAccelerationTest/src/com/google/android/test/hwui/BitmapsRectActivity.java
new file mode 100644
index 0000000..f8726c2
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/google/android/test/hwui/BitmapsRectActivity.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2010 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.google.android.test.hwui;
+
+import android.app.Activity;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.os.Bundle;
+import android.view.View;
+
+@SuppressWarnings({"UnusedDeclaration"})
+public class BitmapsRectActivity extends Activity {
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ final BitmapsView view = new BitmapsView(this);
+ setContentView(view);
+ }
+
+ static class BitmapsView extends View {
+ private Paint mBitmapPaint;
+ private final Bitmap mBitmap1;
+ private final Bitmap mBitmap2;
+ private final Rect mSrcRect;
+ private final RectF mDstRect;
+ private final RectF mDstRect2;
+
+ BitmapsView(Context c) {
+ super(c);
+
+ mBitmap1 = BitmapFactory.decodeResource(c.getResources(), R.drawable.sunset1);
+ mBitmap2 = BitmapFactory.decodeResource(c.getResources(), R.drawable.sunset2);
+
+ mBitmapPaint = new Paint();
+ mBitmapPaint.setFilterBitmap(true);
+
+ final float fourth = mBitmap1.getWidth() / 4.0f;
+ final float half = mBitmap1.getHeight() / 2.0f;
+ mSrcRect = new Rect((int) fourth, (int) (half - half / 2.0f),
+ (int) (fourth + fourth), (int) (half + half / 2.0f));
+ mDstRect = new RectF(fourth, half - half / 2.0f, fourth + fourth, half + half / 2.0f);
+ mDstRect2 = new RectF(fourth, half - half / 2.0f,
+ (fourth + fourth) * 3.0f, (half + half / 2.0f) * 3.0f);
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ super.onDraw(canvas);
+
+ canvas.translate(120.0f, 50.0f);
+ canvas.drawBitmap(mBitmap1, mSrcRect, mDstRect, mBitmapPaint);
+
+ canvas.translate(0.0f, mBitmap1.getHeight());
+ canvas.translate(-100.0f, 25.0f);
+ canvas.drawBitmap(mBitmap1, mSrcRect, mDstRect2, mBitmapPaint);
+ }
+ }
+} \ No newline at end of file
diff --git a/tests/HwAccelerationTest/src/com/google/android/test/hwui/ColorFiltersActivity.java b/tests/HwAccelerationTest/src/com/google/android/test/hwui/ColorFiltersActivity.java
new file mode 100644
index 0000000..49e1eaa
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/google/android/test/hwui/ColorFiltersActivity.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2010 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.google.android.test.hwui;
+
+import android.app.Activity;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Canvas;
+import android.graphics.ColorMatrix;
+import android.graphics.ColorMatrixColorFilter;
+import android.graphics.LightingColorFilter;
+import android.graphics.Paint;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffColorFilter;
+import android.os.Bundle;
+import android.view.View;
+
+@SuppressWarnings({"UnusedDeclaration"})
+public class ColorFiltersActivity extends Activity {
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ final BitmapsView view = new BitmapsView(this);
+ setContentView(view);
+ }
+
+ static class BitmapsView extends View {
+ private final Bitmap mBitmap1;
+ private final Bitmap mBitmap2;
+ private final Paint mColorMatrixPaint;
+ private final Paint mLightingPaint;
+ private final Paint mBlendPaint;
+
+ BitmapsView(Context c) {
+ super(c);
+
+ mBitmap1 = BitmapFactory.decodeResource(c.getResources(), R.drawable.sunset1);
+ mBitmap2 = BitmapFactory.decodeResource(c.getResources(), R.drawable.sunset2);
+
+ mColorMatrixPaint = new Paint();
+ final ColorMatrix colorMatrix = new ColorMatrix();
+ colorMatrix.setSaturation(0);
+ mColorMatrixPaint.setColorFilter(new ColorMatrixColorFilter(colorMatrix));
+
+ mLightingPaint = new Paint();
+ mLightingPaint.setColorFilter(new LightingColorFilter(0x0060ffff, 0x00101030));
+
+ mBlendPaint = new Paint();
+ mBlendPaint.setColorFilter(new PorterDuffColorFilter(0x7f990040,
+ PorterDuff.Mode.SRC_OVER));
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ super.onDraw(canvas);
+
+ canvas.drawARGB(255, 255, 255, 255);
+
+ canvas.save();
+ canvas.translate(120.0f, 50.0f);
+ canvas.drawBitmap(mBitmap1, 0.0f, 0.0f, mColorMatrixPaint);
+
+ canvas.translate(0.0f, 50.0f + mBitmap1.getHeight());
+ canvas.drawBitmap(mBitmap1, 0.0f, 0.0f, mLightingPaint);
+
+ canvas.translate(0.0f, 50.0f + mBitmap1.getHeight());
+ canvas.drawBitmap(mBitmap1, 0.0f, 0.0f, mBlendPaint);
+ canvas.restore();
+
+ canvas.save();
+ canvas.translate(120.0f + mBitmap1.getWidth() + 120.0f, 50.0f);
+ canvas.drawBitmap(mBitmap2, 0.0f, 0.0f, mColorMatrixPaint);
+
+ canvas.translate(0.0f, 50.0f + mBitmap2.getHeight());
+ canvas.drawBitmap(mBitmap2, 0.0f, 0.0f, mLightingPaint);
+
+ canvas.translate(0.0f, 50.0f + mBitmap2.getHeight());
+ canvas.drawBitmap(mBitmap2, 0.0f, 0.0f, mBlendPaint);
+ canvas.restore();
+ }
+ }
+}
diff --git a/tests/HwAccelerationTest/src/com/google/android/test/hwui/LayersActivity.java b/tests/HwAccelerationTest/src/com/google/android/test/hwui/LayersActivity.java
new file mode 100644
index 0000000..437cd1c
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/google/android/test/hwui/LayersActivity.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2010 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.google.android.test.hwui;
+
+import android.app.Activity;
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffXfermode;
+import android.os.Bundle;
+import android.view.View;
+
+@SuppressWarnings({"UnusedDeclaration"})
+public class LayersActivity extends Activity {
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(new LayersView(this));
+ }
+
+ static class LayersView extends View {
+ private Paint mLayerPaint;
+ private final Paint mRectPaint;
+
+ LayersView(Context c) {
+ super(c);
+
+ mLayerPaint = new Paint();
+ mRectPaint = new Paint();
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ super.onDraw(canvas);
+
+ canvas.translate(140.0f, 100.0f);
+
+ //canvas.drawRGB(255, 255, 255);
+
+ int count = canvas.saveLayer(0.0f, 0.0f, 200.0f, 100.0f, mLayerPaint,
+ Canvas.ALL_SAVE_FLAG);
+
+ mRectPaint.setColor(0xffff0000);
+ canvas.drawRect(0.0f, 0.0f, 200.0f, 100.0f, mRectPaint);
+
+ canvas.restoreToCount(count);
+
+ canvas.translate(0.0f, 125.0f);
+
+ count = canvas.saveLayer(0.0f, 0.0f, 200.0f, 100.0f, mLayerPaint,
+ Canvas.ALL_SAVE_FLAG);
+
+ mRectPaint.setColor(0xff00ff00);
+ mRectPaint.setAlpha(50);
+ canvas.drawRect(0.0f, 0.0f, 200.0f, 100.0f, mRectPaint);
+
+ canvas.restoreToCount(count);
+
+ canvas.translate(25.0f, 125.0f);
+
+ mRectPaint.setColor(0xff0000ff);
+ mRectPaint.setAlpha(255);
+ canvas.drawRect(0.0f, 0.0f, 100.0f, 50.0f, mRectPaint);
+
+ mLayerPaint.setAlpha(127);
+ mLayerPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_OUT));
+ count = canvas.saveLayer(50.0f, 25.0f, 150.0f, 75.0f, mLayerPaint,
+ Canvas.ALL_SAVE_FLAG);
+
+ mRectPaint.setColor(0xffff0000);
+ canvas.drawRect(50.0f, 25.0f, 150.0f, 75.0f, mRectPaint);
+
+ canvas.restoreToCount(count);
+
+ canvas.translate(0.0f, 125.0f);
+
+ mRectPaint.setColor(0xff0000ff);
+ mRectPaint.setAlpha(255);
+ canvas.drawRect(0.0f, 0.0f, 100.0f, 50.0f, mRectPaint);
+
+ mLayerPaint.setColor(0xffff0000);
+ mLayerPaint.setAlpha(127);
+ mLayerPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_ATOP));
+ count = canvas.saveLayer(50.0f, 25.0f, 150.0f, 75.0f, mLayerPaint,
+ Canvas.ALL_SAVE_FLAG);
+
+ mRectPaint.setColor(0xffff0000);
+ canvas.drawRect(50.0f, 25.0f, 150.0f, 75.0f, mRectPaint);
+
+ canvas.restoreToCount(count);
+
+ mLayerPaint = new Paint();
+ }
+ }
+}
diff --git a/tests/HwAccelerationTest/src/com/google/android/test/hwui/LinesActivity.java b/tests/HwAccelerationTest/src/com/google/android/test/hwui/LinesActivity.java
new file mode 100644
index 0000000..c800d42
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/google/android/test/hwui/LinesActivity.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2010 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.google.android.test.hwui;
+
+import android.app.Activity;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.BitmapShader;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.os.Bundle;
+import android.view.View;
+
+@SuppressWarnings({"UnusedDeclaration"})
+public class LinesActivity extends Activity {
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ final LinesView view = new LinesView(this);
+ setContentView(view);
+ }
+
+ static class LinesView extends View {
+ private final Bitmap mBitmap1;
+ private final Paint mSmallPaint;
+ private final Paint mMediumPaint;
+ private final Paint mLargePaint;
+ private final BitmapShader mShader;
+ private final float[] mPoints;
+ private final Paint mAlphaPaint;
+
+ LinesView(Context c) {
+ super(c);
+
+ mBitmap1 = BitmapFactory.decodeResource(c.getResources(), R.drawable.sunset1);
+
+ mSmallPaint = new Paint();
+ mSmallPaint.setAntiAlias(true);
+ mSmallPaint.setColor(0xffff0000);
+ mSmallPaint.setStrokeWidth(1.0f);
+
+ mMediumPaint = new Paint();
+ mMediumPaint.setAntiAlias(true);
+ mMediumPaint.setColor(0xff0000ff);
+ mMediumPaint.setStrokeWidth(4.0f);
+
+ mLargePaint = new Paint();
+ mLargePaint.setAntiAlias(true);
+ mLargePaint.setColor(0xff00ff00);
+ mLargePaint.setStrokeWidth(15.0f);
+
+ mAlphaPaint = new Paint();
+ mAlphaPaint.setAntiAlias(true);
+ mAlphaPaint.setColor(0x7fff0050);
+ mAlphaPaint.setStrokeWidth(10.0f);
+
+ mShader = new BitmapShader(mBitmap1, BitmapShader.TileMode.MIRROR,
+ BitmapShader.TileMode.MIRROR);
+
+ mPoints = new float[] {
+ 62.0f, 0.0f, 302.0f, 400.0f,
+ 302.0f, 400.0f, 352.0f, 400.0f,
+ 352.0f, 400.0f, 352.0f, 500.0f
+ };
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ super.onDraw(canvas);
+
+ canvas.drawARGB(255, 255, 255, 255);
+
+ canvas.save();
+ canvas.translate(100.0f, 20.0f);
+
+ canvas.drawLine(0.0f, 0.0f, 40.0f, 400.0f, mSmallPaint);
+ canvas.drawLine(5.0f, 0.0f, 95.0f, 400.0f, mMediumPaint);
+ canvas.drawLine(22.0f, 0.0f, 162.0f, 400.0f, mLargePaint);
+
+ mLargePaint.setShader(mShader);
+ canvas.drawLine(42.0f, 0.0f, 222.0f, 400.0f, mLargePaint);
+ mLargePaint.setShader(null);
+
+ canvas.drawLines(mPoints, mAlphaPaint);
+
+ canvas.translate(120.0f, 0.0f);
+ mAlphaPaint.setShader(mShader);
+ canvas.drawLines(mPoints, mAlphaPaint);
+ mAlphaPaint.setShader(null);
+
+ canvas.restore();
+ }
+ }
+}
diff --git a/tests/HwAccelerationTest/src/com/google/android/test/hwui/ListActivity.java b/tests/HwAccelerationTest/src/com/google/android/test/hwui/ListActivity.java
new file mode 100644
index 0000000..94b936b
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/google/android/test/hwui/ListActivity.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2010 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.google.android.test.hwui;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.res.Resources;
+import android.os.Bundle;
+import android.util.DisplayMetrics;
+import android.view.ContextMenu;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+import android.widget.ListAdapter;
+import android.widget.ListView;
+import android.widget.TextView;
+
+@SuppressWarnings({"UnusedDeclaration"})
+public class ListActivity extends Activity {
+ private static final String[] DATA_LIST = {
+ "Afghanistan", "Albania", "Algeria", "American Samoa", "Andorra",
+ "Angola", "Anguilla", "Antarctica", "Antigua and Barbuda", "Argentina",
+ "Armenia", "Aruba", "Australia", "Austria", "Azerbaijan",
+ "Bahrain", "Bangladesh", "Barbados", "Belarus", "Belgium",
+ "Belize", "Benin", "Bermuda", "Bhutan", "Bolivia",
+ "Bosnia and Herzegovina", "Botswana", "Bouvet Island", "Brazil",
+ "British Indian Ocean Territory", "British Virgin Islands", "Brunei", "Bulgaria",
+ "Burkina Faso", "Burundi", "Cote d'Ivoire", "Cambodia", "Cameroon", "Canada", "Cape Verde",
+ "Cayman Islands", "Central African Republic", "Chad", "Chile", "China",
+ "Christmas Island", "Cocos (Keeling) Islands", "Colombia", "Comoros", "Congo",
+ "Cook Islands", "Costa Rica", "Croatia", "Cuba", "Cyprus", "Czech Republic",
+ "Democratic Republic of the Congo", "Denmark", "Djibouti", "Dominica", "Dominican Republic",
+ "East Timor", "Ecuador", "Egypt", "El Salvador", "Equatorial Guinea", "Eritrea",
+ "Estonia", "Ethiopia", "Faeroe Islands", "Falkland Islands", "Fiji", "Finland",
+ "Former Yugoslav Republic of Macedonia", "France", "French Guiana", "French Polynesia",
+ "French Southern Territories", "Gabon", "Georgia", "Germany", "Ghana", "Gibraltar",
+ "Greece", "Greenland", "Grenada", "Guadeloupe", "Guam", "Guatemala", "Guinea", "Guinea-Bissau",
+ "Guyana", "Haiti", "Heard Island and McDonald Islands", "Honduras", "Hong Kong", "Hungary",
+ "Iceland", "India", "Indonesia", "Iran", "Iraq", "Ireland", "Israel", "Italy", "Jamaica",
+ "Japan", "Jordan", "Kazakhstan", "Kenya", "Kiribati", "Kuwait", "Kyrgyzstan", "Laos",
+ "Latvia", "Lebanon", "Lesotho", "Liberia", "Libya", "Liechtenstein", "Lithuania", "Luxembourg",
+ "Macau", "Madagascar", "Malawi", "Malaysia", "Maldives", "Mali", "Malta", "Marshall Islands",
+ "Martinique", "Mauritania", "Mauritius", "Mayotte", "Mexico", "Micronesia", "Moldova",
+ "Monaco", "Mongolia", "Montserrat", "Morocco", "Mozambique", "Myanmar", "Namibia",
+ "Nauru", "Nepal", "Netherlands", "Netherlands Antilles", "New Caledonia", "New Zealand",
+ "Nicaragua", "Niger", "Nigeria", "Niue", "Norfolk Island", "North Korea", "Northern Marianas",
+ "Norway", "Oman", "Pakistan", "Palau", "Panama", "Papua New Guinea", "Paraguay", "Peru",
+ "Philippines", "Pitcairn Islands", "Poland", "Portugal", "Puerto Rico", "Qatar",
+ "Reunion", "Romania", "Russia", "Rwanda", "Sqo Tome and Principe", "Saint Helena",
+ "Saint Kitts and Nevis", "Saint Lucia", "Saint Pierre and Miquelon",
+ "Saint Vincent and the Grenadines", "Samoa", "San Marino", "Saudi Arabia", "Senegal",
+ "Seychelles", "Sierra Leone", "Singapore", "Slovakia", "Slovenia", "Solomon Islands",
+ "Somalia", "South Africa", "South Georgia and the South Sandwich Islands", "South Korea",
+ "Spain", "Sri Lanka", "Sudan", "Suriname", "Svalbard and Jan Mayen", "Swaziland", "Sweden",
+ "Switzerland", "Syria", "Taiwan", "Tajikistan", "Tanzania", "Thailand", "The Bahamas",
+ "The Gambia", "Togo", "Tokelau", "Tonga", "Trinidad and Tobago", "Tunisia", "Turkey",
+ "Turkmenistan", "Turks and Caicos Islands", "Tuvalu", "Virgin Islands", "Uganda",
+ "Ukraine", "United Arab Emirates", "United Kingdom",
+ "United States", "United States Minor Outlying Islands", "Uruguay", "Uzbekistan",
+ "Vanuatu", "Vatican City", "Venezuela", "Vietnam", "Wallis and Futuna", "Western Sahara",
+ "Yemen", "Yugoslavia", "Zambia", "Zimbabwe"
+ };
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.list_activity);
+
+ ListAdapter adapter = new SimpleListAdapter(this);
+
+ ListView list = (ListView) findViewById(R.id.list);
+ list.setAdapter(adapter);
+
+ registerForContextMenu(list);
+ }
+
+ @Override
+ public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
+ super.onCreateContextMenu(menu, v, menuInfo);
+ menu.setHeaderTitle("Context menu");
+ menu.add("List item 1");
+ menu.add("List item 2");
+ menu.add("List item 3");
+ }
+
+ private static class SimpleListAdapter extends ArrayAdapter<String> {
+ public SimpleListAdapter(Context context) {
+ super(context, android.R.layout.simple_list_item_1, DATA_LIST);
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ TextView v = (TextView) super.getView(position, convertView, parent);
+ final Resources r = getContext().getResources();
+ final DisplayMetrics metrics = r.getDisplayMetrics();
+ v.setCompoundDrawablePadding((int) (6 * metrics.density + 0.5f));
+ v.setCompoundDrawablesWithIntrinsicBounds(r.getDrawable(R.drawable.icon),
+ null, null, null);
+ return v;
+ }
+ }
+} \ No newline at end of file
diff --git a/tests/HwAccelerationTest/src/com/google/android/test/hwui/MoreShadersActivity.java b/tests/HwAccelerationTest/src/com/google/android/test/hwui/MoreShadersActivity.java
new file mode 100644
index 0000000..f43eeba
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/google/android/test/hwui/MoreShadersActivity.java
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2010 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.google.android.test.hwui;
+
+import android.app.Activity;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.BitmapShader;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.ColorFilter;
+import android.graphics.ComposeShader;
+import android.graphics.LightingColorFilter;
+import android.graphics.LinearGradient;
+import android.graphics.Matrix;
+import android.graphics.Paint;
+import android.graphics.PorterDuff;
+import android.graphics.Shader;
+import android.os.Bundle;
+import android.view.View;
+
+@SuppressWarnings({"UnusedDeclaration"})
+public class MoreShadersActivity extends Activity {
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ setContentView(new ShadersView(this));
+ }
+
+ static class ShadersView extends View {
+ private BitmapShader mScaledShader;
+ private int mTexWidth;
+ private int mTexHeight;
+ private Paint mPaint;
+ private float mDrawWidth;
+ private float mDrawHeight;
+ private LinearGradient mHorGradient;
+ private LinearGradient mVertGradient;
+ private ComposeShader mComposeShader;
+ private ComposeShader mCompose2Shader;
+ private Paint mLargePaint;
+ private BitmapShader mScaled2Shader;
+ private ColorFilter mColorFilter;
+
+ ShadersView(Context c) {
+ super(c);
+
+ Bitmap texture = BitmapFactory.decodeResource(c.getResources(), R.drawable.sunset1);
+ mTexWidth = texture.getWidth();
+ mTexHeight = texture.getHeight();
+ mDrawWidth = mTexWidth * 2.2f;
+ mDrawHeight = mTexHeight * 1.2f;
+
+ mScaledShader = new BitmapShader(texture, Shader.TileMode.MIRROR,
+ Shader.TileMode.MIRROR);
+ Matrix m2 = new Matrix();
+ m2.setScale(0.5f, 0.5f);
+ mScaledShader.setLocalMatrix(m2);
+
+ mScaled2Shader = new BitmapShader(texture, Shader.TileMode.MIRROR,
+ Shader.TileMode.MIRROR);
+ Matrix m3 = new Matrix();
+ m3.setScale(0.1f, 0.1f);
+ mScaled2Shader.setLocalMatrix(m3);
+
+ mHorGradient = new LinearGradient(0.0f, 0.0f, mDrawWidth, 0.0f,
+ Color.RED, 0x7f00ff00, Shader.TileMode.CLAMP);
+
+ mVertGradient = new LinearGradient(0.0f, 0.0f, 0.0f, mDrawHeight / 2.0f,
+ Color.YELLOW, Color.MAGENTA, Shader.TileMode.MIRROR);
+
+ mComposeShader = new ComposeShader(mScaledShader, mHorGradient,
+ PorterDuff.Mode.SRC_OVER);
+ mCompose2Shader = new ComposeShader(mHorGradient, mScaledShader,
+ PorterDuff.Mode.SRC_OUT);
+
+ mColorFilter = new LightingColorFilter(0x0060ffff, 0x00101030);
+
+ mLargePaint = new Paint();
+ mLargePaint.setAntiAlias(true);
+ mLargePaint.setTextSize(36.0f);
+ mLargePaint.setColor(0xff000000);
+
+ mPaint = new Paint();
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ super.onDraw(canvas);
+ canvas.drawRGB(255, 255, 255);
+
+ canvas.save();
+ canvas.translate(40.0f, 40.0f);
+
+ mPaint.setShader(mComposeShader);
+ canvas.drawRect(0.0f, 0.0f, mDrawWidth, mDrawHeight, mPaint);
+
+ canvas.translate(0.0f, 40.0f + mDrawHeight);
+ mPaint.setShader(mCompose2Shader);
+ canvas.drawRect(0.0f, 0.0f, mDrawWidth, mDrawHeight, mPaint);
+
+ canvas.restore();
+
+ canvas.save();
+ canvas.translate(40.0f + mDrawWidth + 40.0f, 40.0f);
+
+ mLargePaint.setShader(mHorGradient);
+ canvas.drawText("OpenGL rendering", 0.0f, 20.0f, mLargePaint);
+
+ mLargePaint.setShader(mScaled2Shader);
+ canvas.drawText("OpenGL rendering", 0.0f, 60.0f, mLargePaint);
+
+ mLargePaint.setShader(mCompose2Shader);
+ mLargePaint.setColorFilter(mColorFilter);
+ canvas.drawText("OpenGL rendering", 0.0f, 100.0f, mLargePaint);
+ mLargePaint.setColorFilter(null);
+
+ canvas.translate(0.0f, 40.0f + mDrawHeight);
+ mLargePaint.setShader(mVertGradient);
+ canvas.drawText("OpenGL rendering", 0.0f, 20.0f, mLargePaint);
+
+ canvas.restore();
+ }
+ }
+}
diff --git a/tests/HwAccelerationTest/src/com/google/android/test/hwui/NinePatchesActivity.java b/tests/HwAccelerationTest/src/com/google/android/test/hwui/NinePatchesActivity.java
new file mode 100644
index 0000000..3268fbf
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/google/android/test/hwui/NinePatchesActivity.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2010 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.google.android.test.hwui;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.Gravity;
+import android.widget.Button;
+import android.widget.FrameLayout;
+
+@SuppressWarnings({"UnusedDeclaration"})
+public class NinePatchesActivity extends Activity {
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ FrameLayout layout = new FrameLayout(this);
+ Button b = new Button(this);
+ b.setLayoutParams(new FrameLayout.LayoutParams(FrameLayout.LayoutParams.WRAP_CONTENT,
+ FrameLayout.LayoutParams.WRAP_CONTENT, Gravity.CENTER));
+ b.setText("9 patches");
+ layout.addView(b);
+ layout.setBackgroundColor(0xffffffff);
+
+ setContentView(layout);
+ }
+}
diff --git a/tests/HwAccelerationTest/src/com/google/android/test/hwui/PathsActivity.java b/tests/HwAccelerationTest/src/com/google/android/test/hwui/PathsActivity.java
new file mode 100644
index 0000000..39d9942
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/google/android/test/hwui/PathsActivity.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2010 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.google.android.test.hwui;
+
+import android.app.Activity;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.BitmapShader;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Path;
+import android.graphics.RectF;
+import android.os.Bundle;
+import android.view.View;
+
+@SuppressWarnings({"UnusedDeclaration"})
+public class PathsActivity extends Activity {
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ final PathsView view = new PathsView(this);
+ setContentView(view);
+ }
+
+ static class PathsView extends View {
+ private final Bitmap mBitmap1;
+ private final Paint mSmallPaint;
+ private final Paint mMediumPaint;
+ private final Paint mLargePaint;
+ private final BitmapShader mShader;
+ private final Path mPath;
+ private final RectF mPathBounds;
+ private final Paint mBoundsPaint;
+ private final Bitmap mBitmap;
+ private final float mOffset;
+ private final Paint mLinePaint;
+
+ PathsView(Context c) {
+ super(c);
+
+ mBitmap1 = BitmapFactory.decodeResource(c.getResources(), R.drawable.sunset1);
+
+ mSmallPaint = new Paint();
+ mSmallPaint.setAntiAlias(true);
+ mSmallPaint.setColor(0xffff0000);
+ mSmallPaint.setStrokeWidth(1.0f);
+ mSmallPaint.setStyle(Paint.Style.STROKE);
+
+ mLinePaint = new Paint();
+ mLinePaint.setAntiAlias(true);
+ mLinePaint.setColor(0xffff00ff);
+ mLinePaint.setStrokeWidth(1.0f);
+ mLinePaint.setStyle(Paint.Style.STROKE);
+
+ mMediumPaint = new Paint();
+ mMediumPaint.setAntiAlias(true);
+ mMediumPaint.setColor(0xff0000ff);
+ mMediumPaint.setStrokeWidth(10.0f);
+ mMediumPaint.setStyle(Paint.Style.STROKE);
+
+ mLargePaint = new Paint();
+ mLargePaint.setAntiAlias(true);
+ mLargePaint.setColor(0xff00ff00);
+ mLargePaint.setStrokeWidth(15.0f);
+ mLargePaint.setStyle(Paint.Style.FILL);
+
+ mShader = new BitmapShader(mBitmap1, BitmapShader.TileMode.MIRROR,
+ BitmapShader.TileMode.MIRROR);
+
+ mPath = new Path();
+ mPath.moveTo(0.0f, 0.0f);
+ mPath.cubicTo(0.0f, 0.0f, 100.0f, 150.0f, 100.0f, 200.0f);
+ mPath.cubicTo(100.0f, 200.0f, 50.0f, 300.0f, -80.0f, 200.0f);
+ mPath.cubicTo(-80.0f, 200.0f, 100.0f, 200.0f, 200.0f, 0.0f);
+
+ mPathBounds = new RectF();
+ mPath.computeBounds(mPathBounds, true);
+
+ mBoundsPaint = new Paint();
+ mBoundsPaint.setColor(0x4000ff00);
+
+ mOffset = mMediumPaint.getStrokeWidth();
+ final int width = (int) (mPathBounds.width() + mOffset * 3.0f + 0.5f);
+ final int height = (int) (mPathBounds.height() + mOffset * 3.0f + 0.5f);
+ mBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ALPHA_8);
+ Canvas canvas = new Canvas(mBitmap);
+ canvas.translate(-mPathBounds.left + mOffset * 1.5f, -mPathBounds.top + mOffset * 1.5f);
+ canvas.drawPath(mPath, mMediumPaint);
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ super.onDraw(canvas);
+
+ canvas.drawARGB(255, 255, 255, 255);
+
+ canvas.save();
+ canvas.translate(200.0f, 60.0f);
+ canvas.drawPath(mPath, mSmallPaint);
+
+ canvas.translate(350.0f, 0.0f);
+ canvas.drawPath(mPath, mMediumPaint);
+
+ mLargePaint.setShader(mShader);
+ canvas.translate(350.0f, 0.0f);
+ canvas.drawPath(mPath, mLargePaint);
+ mLargePaint.setShader(null);
+ canvas.restore();
+
+ canvas.save();
+ canvas.translate(200.0f, 360.0f);
+ canvas.drawPath(mPath, mSmallPaint);
+ canvas.drawRect(mPathBounds, mBoundsPaint);
+
+ canvas.translate(350.0f, 0.0f);
+ canvas.drawBitmap(mBitmap, mPathBounds.left - mOffset * 1.5f,
+ mPathBounds.top - mOffset * 1.5f, null);
+ canvas.drawRect(mPathBounds, mBoundsPaint);
+ canvas.drawLine(0.0f, -360.0f, 0.0f, 500.0f, mLinePaint);
+
+ mLargePaint.setShader(mShader);
+ canvas.translate(350.0f, 0.0f);
+ canvas.drawPath(mPath, mLargePaint);
+ canvas.drawRect(mPathBounds, mBoundsPaint);
+ mLargePaint.setShader(null);
+ canvas.restore();
+ }
+ }
+}
diff --git a/tests/HwAccelerationTest/src/com/google/android/test/hwui/QuickRejectActivity.java b/tests/HwAccelerationTest/src/com/google/android/test/hwui/QuickRejectActivity.java
new file mode 100644
index 0000000..2ba249a
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/google/android/test/hwui/QuickRejectActivity.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2010 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.google.android.test.hwui;
+
+import android.app.Activity;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.View;
+
+@SuppressWarnings({"UnusedDeclaration"})
+public class QuickRejectActivity extends Activity {
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ final QuickRejectView view = new QuickRejectView(this);
+ setContentView(view);
+ }
+
+ static class QuickRejectView extends View {
+ private Paint mBitmapPaint;
+ private final Bitmap mBitmap1;
+
+ QuickRejectView(Context c) {
+ super(c);
+
+ mBitmap1 = BitmapFactory.decodeResource(c.getResources(), R.drawable.sunset1);
+
+ mBitmapPaint = new Paint();
+ mBitmapPaint.setFilterBitmap(true);
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ super.onDraw(canvas);
+
+ int count = canvas.getSaveCount();
+ Log.d("OpenGLRenderer", "count=" + count);
+ count = canvas.save();
+ Log.d("OpenGLRenderer", "count after save=" + count);
+ count = canvas.getSaveCount();
+ Log.d("OpenGLRenderer", "getSaveCount after save=" + count);
+ canvas.restore();
+ count = canvas.getSaveCount();
+ Log.d("OpenGLRenderer", "count after restore=" + count);
+ canvas.save();
+ Log.d("OpenGLRenderer", "count after save=" + canvas.getSaveCount());
+ canvas.save();
+ Log.d("OpenGLRenderer", "count after save=" + canvas.getSaveCount());
+ canvas.save();
+ Log.d("OpenGLRenderer", "count after save=" + canvas.getSaveCount());
+ canvas.restoreToCount(count);
+ count = canvas.getSaveCount();
+ Log.d("OpenGLRenderer", "count after restoreToCount=" + count);
+ count = canvas.saveLayer(0, 0, 10, 10, mBitmapPaint, Canvas.ALL_SAVE_FLAG);
+ Log.d("OpenGLRenderer", "count after saveLayer=" + count);
+ count = canvas.getSaveCount();
+ Log.d("OpenGLRenderer", "getSaveCount after saveLayer=" + count);
+ canvas.restore();
+ count = canvas.getSaveCount();
+ Log.d("OpenGLRenderer", "count after restore=" + count);
+
+ canvas.save();
+ canvas.clipRect(0.0f, 0.0f, 40.0f, 40.0f);
+ canvas.drawBitmap(mBitmap1, 0.0f, 0.0f, mBitmapPaint);
+ canvas.drawBitmap(mBitmap1, -mBitmap1.getWidth(), 0.0f, mBitmapPaint);
+ canvas.drawBitmap(mBitmap1, 50.0f, 0.0f, mBitmapPaint);
+ canvas.restore();
+ }
+ }
+} \ No newline at end of file
diff --git a/tests/HwAccelerationTest/src/com/google/android/test/hwui/RotationActivity.java b/tests/HwAccelerationTest/src/com/google/android/test/hwui/RotationActivity.java
new file mode 100644
index 0000000..e629cb8
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/google/android/test/hwui/RotationActivity.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2010 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.google.android.test.hwui;
+
+import android.app.Activity;
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.os.Bundle;
+import android.view.View;
+
+@SuppressWarnings({"UnusedDeclaration"})
+public class RotationActivity extends Activity {
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ DrawingView container = new DrawingView(this);
+
+ setContentView(container);
+ }
+
+ @SuppressWarnings({"UnusedDeclaration"})
+ static int dipToPx(Context c, int dip) {
+ return (int) (c.getResources().getDisplayMetrics().density * dip + 0.5f);
+ }
+
+ static class DrawingView extends View {
+ private final Paint mPaint;
+
+ DrawingView(Context c) {
+ super(c);
+ mPaint = new Paint();
+ mPaint.setAntiAlias(true);
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ canvas.save();
+ canvas.translate(dipToPx(getContext(), 400), dipToPx(getContext(), 200));
+ canvas.rotate(45.0f);
+ canvas.drawRGB(255, 255, 255);
+ mPaint.setColor(0xffff0000);
+ canvas.drawRect(-80.0f, -80.0f, 80.0f, 80.0f, mPaint);
+ canvas.drawRect(0.0f, 0.0f, 220.0f, 220.0f, mPaint);
+ canvas.restore();
+ }
+ }
+}
diff --git a/tests/HwAccelerationTest/src/com/google/android/test/hwui/ShadersActivity.java b/tests/HwAccelerationTest/src/com/google/android/test/hwui/ShadersActivity.java
new file mode 100644
index 0000000..0cd1426
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/google/android/test/hwui/ShadersActivity.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2010 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.google.android.test.hwui;
+
+import android.app.Activity;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.BitmapShader;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.LinearGradient;
+import android.graphics.Matrix;
+import android.graphics.Paint;
+import android.graphics.Shader;
+import android.os.Bundle;
+import android.view.View;
+
+@SuppressWarnings({"UnusedDeclaration"})
+public class ShadersActivity extends Activity {
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ setContentView(new ShadersView(this));
+ }
+
+ static class ShadersView extends View {
+ private BitmapShader mRepeatShader;
+ private BitmapShader mTranslatedShader;
+ private BitmapShader mScaledShader;
+ private int mTexWidth;
+ private int mTexHeight;
+ private Paint mPaint;
+ private float mDrawWidth;
+ private float mDrawHeight;
+ private LinearGradient mHorGradient;
+ private LinearGradient mDiagGradient;
+ private LinearGradient mVertGradient;
+
+ ShadersView(Context c) {
+ super(c);
+
+ Bitmap texture = BitmapFactory.decodeResource(c.getResources(), R.drawable.sunset1);
+ mTexWidth = texture.getWidth();
+ mTexHeight = texture.getHeight();
+ mDrawWidth = mTexWidth * 2.2f;
+ mDrawHeight = mTexHeight * 1.2f;
+
+ mRepeatShader = new BitmapShader(texture, Shader.TileMode.REPEAT,
+ Shader.TileMode.REPEAT);
+
+ mTranslatedShader = new BitmapShader(texture, Shader.TileMode.REPEAT,
+ Shader.TileMode.REPEAT);
+ Matrix m1 = new Matrix();
+ m1.setTranslate(mTexWidth / 2.0f, mTexHeight / 2.0f);
+ m1.postRotate(45, 0, 0);
+ mTranslatedShader.setLocalMatrix(m1);
+
+ mScaledShader = new BitmapShader(texture, Shader.TileMode.MIRROR,
+ Shader.TileMode.MIRROR);
+ Matrix m2 = new Matrix();
+ m2.setScale(0.5f, 0.5f);
+ mScaledShader.setLocalMatrix(m2);
+
+ mHorGradient = new LinearGradient(0.0f, 0.0f, mDrawWidth, 0.0f,
+ Color.RED, Color.GREEN, Shader.TileMode.CLAMP);
+
+ mDiagGradient = new LinearGradient(0.0f, 0.0f, mDrawWidth / 1.5f, mDrawHeight,
+ Color.BLUE, Color.MAGENTA, Shader.TileMode.CLAMP);
+
+ mVertGradient = new LinearGradient(0.0f, 0.0f, 0.0f, mDrawHeight / 2.0f,
+ Color.YELLOW, Color.MAGENTA, Shader.TileMode.MIRROR);
+
+ mPaint = new Paint();
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ super.onDraw(canvas);
+ //canvas.drawRGB(255, 255, 255);
+
+ // Bitmap shaders
+ canvas.save();
+ canvas.translate(40.0f, 40.0f);
+
+ mPaint.setShader(mRepeatShader);
+ canvas.drawRect(0.0f, 0.0f, mDrawWidth, mDrawHeight, mPaint);
+
+ canvas.translate(0.0f, 40.0f + mDrawHeight);
+ mPaint.setShader(mTranslatedShader);
+ canvas.drawRect(0.0f, 0.0f, mDrawWidth, mDrawHeight, mPaint);
+
+ canvas.translate(0.0f, 40.0f + mDrawHeight);
+ mPaint.setShader(mScaledShader);
+ canvas.drawRect(0.0f, 0.0f, mDrawWidth, mDrawHeight, mPaint);
+
+ canvas.restore();
+
+ // Gradients
+ canvas.save();
+ canvas.translate(40.0f + mDrawWidth + 40.0f, 40.0f);
+
+ mPaint.setShader(mHorGradient);
+ canvas.drawRect(0.0f, 0.0f, mDrawWidth, mDrawHeight, mPaint);
+
+ canvas.translate(0.0f, 40.0f + mDrawHeight);
+ mPaint.setShader(mDiagGradient);
+ canvas.drawRect(0.0f, 0.0f, mDrawWidth, mDrawHeight, mPaint);
+
+ canvas.translate(0.0f, 40.0f + mDrawHeight);
+ mPaint.setShader(mVertGradient);
+ canvas.drawRect(0.0f, 0.0f, mDrawWidth, mDrawHeight, mPaint);
+
+ canvas.restore();
+ }
+ }
+}
diff --git a/tests/HwAccelerationTest/src/com/google/android/test/hwui/SimplePathsActivity.java b/tests/HwAccelerationTest/src/com/google/android/test/hwui/SimplePathsActivity.java
new file mode 100644
index 0000000..071a118
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/google/android/test/hwui/SimplePathsActivity.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2010 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.google.android.test.hwui;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.Gravity;
+import android.widget.EditText;
+import android.widget.FrameLayout;
+
+@SuppressWarnings({"UnusedDeclaration"})
+public class SimplePathsActivity extends Activity {
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ FrameLayout layout = new FrameLayout(this);
+ EditText text = new EditText(this);
+ layout.addView(text, new FrameLayout.LayoutParams(600, 350, Gravity.CENTER));
+ text.setText("This is an example of an EditText widget \n" +
+ "using simple paths to create the selection.");
+ //text.setSelection(0, text.getText().length());
+
+ setContentView(layout);
+ }
+} \ No newline at end of file
diff --git a/tests/HwAccelerationTest/src/com/google/android/test/hwui/StackActivity.java b/tests/HwAccelerationTest/src/com/google/android/test/hwui/StackActivity.java
new file mode 100644
index 0000000..5c8db6e
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/google/android/test/hwui/StackActivity.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2010 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.google.android.test.hwui;
+
+import android.app.Activity;
+import android.graphics.drawable.Drawable;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+import android.widget.ImageView;
+import android.widget.StackView;
+import android.widget.TextView;
+
+@SuppressWarnings({"UnusedDeclaration"})
+public class StackActivity extends Activity {
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ setContentView(R.layout.stack);
+
+ StackView stack = (StackView) findViewById(R.id.stack_view);
+ stack.setAdapter(new ArrayAdapter<Drawable>(this, android.R.layout.simple_list_item_1,
+ android.R.id.text1, new Drawable[] {
+ getResources().getDrawable(R.drawable.sunset1),
+ getResources().getDrawable(R.drawable.sunset2),
+ }) {
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ View item = convertView;
+ if (item == null) {
+ item = LayoutInflater.from(getContext()).inflate(
+ R.layout.stack_item, null, false);
+ }
+ ((ImageView) item.findViewById(R.id.textview_icon)).setImageDrawable(
+ getItem(position % getCount()));
+ ((TextView) item.findViewById(R.id.mini_text)).setText("" + position);
+ return item;
+ }
+ });
+ stack.setDisplayedChild(0);
+ }
+}
diff --git a/tests/HwAccelerationTest/src/com/google/android/test/hwui/TextActivity.java b/tests/HwAccelerationTest/src/com/google/android/test/hwui/TextActivity.java
new file mode 100644
index 0000000..abe9d5e
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/google/android/test/hwui/TextActivity.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2010 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.google.android.test.hwui;
+
+import android.app.Activity;
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.os.Bundle;
+import android.view.View;
+
+@SuppressWarnings({"UnusedDeclaration"})
+public class TextActivity extends Activity {
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ setContentView(new CustomTextView(this));
+ }
+
+ static class CustomTextView extends View {
+ private final Paint mMediumPaint;
+ private final Paint mLargePaint;
+ private final Paint mStrikePaint;
+ private final Paint mScaledPaint;
+
+ CustomTextView(Context c) {
+ super(c);
+
+ mMediumPaint = new Paint();
+ mMediumPaint.setAntiAlias(true);
+ mMediumPaint.setColor(0xffff0000);
+
+ mLargePaint = new Paint();
+ mLargePaint.setAntiAlias(true);
+ mLargePaint.setTextSize(36.0f);
+
+ mStrikePaint = new Paint();
+ mStrikePaint.setAntiAlias(true);
+ mStrikePaint.setTextSize(16.0f);
+ mStrikePaint.setUnderlineText(true);
+
+ mScaledPaint = new Paint();
+ mScaledPaint.setAntiAlias(true);
+ mScaledPaint.setTextSize(16.0f);
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ super.onDraw(canvas);
+ canvas.drawRGB(255, 255, 255);
+
+ canvas.drawText("Hello OpenGL renderer!", 100, 20, mMediumPaint);
+ mMediumPaint.setTextAlign(Paint.Align.CENTER);
+ canvas.drawText("Hello OpenGL renderer!", 100, 40, mMediumPaint);
+ mMediumPaint.setTextAlign(Paint.Align.RIGHT);
+ canvas.drawText("Hello OpenGL renderer!", 100, 60, mMediumPaint);
+ mMediumPaint.setTextAlign(Paint.Align.LEFT);
+ canvas.drawText("Hello OpenGL renderer!", 100, 100, mMediumPaint);
+ mMediumPaint.setShadowLayer(2.5f, 0.0f, 0.0f, 0xff000000);
+ canvas.drawText("Hello OpenGL renderer!", 100, 150, mMediumPaint);
+ mMediumPaint.clearShadowLayer();
+ canvas.drawText("Hello OpenGL renderer!", 100, 200, mLargePaint);
+
+ mLargePaint.setShadowLayer(2.5f, 3.0f, 3.0f, 0xff000000);
+ canvas.drawText("Hello OpenGL renderer!", 100, 400, mLargePaint);
+ mLargePaint.setShadowLayer(3.0f, 3.0f, 3.0f, 0xff00ff00);
+ mLargePaint.setAlpha(100);
+ canvas.drawText("Hello OpenGL renderer!", 100, 500, mLargePaint);
+ mLargePaint.setAlpha(255);
+ mLargePaint.clearShadowLayer();
+
+ canvas.drawText("Hello OpenGL renderer!", 500, 40, mStrikePaint);
+ mStrikePaint.setStrikeThruText(true);
+ canvas.drawText("Hello OpenGL renderer!", 500, 70, mStrikePaint);
+ mStrikePaint.setUnderlineText(false);
+ canvas.drawText("Hello OpenGL renderer!", 500, 100, mStrikePaint);
+ mStrikePaint.setStrikeThruText(false);
+ mStrikePaint.setUnderlineText(true);
+
+ mScaledPaint.setTextScaleX(0.5f);
+ canvas.drawText("Hello OpenGL renderer!", 500, 200, mScaledPaint);
+ mScaledPaint.setTextScaleX(2.0f);
+ canvas.drawText("Hello OpenGL renderer!", 500, 230, mScaledPaint);
+ mScaledPaint.setTextScaleX(1.0f);
+ canvas.drawText("Hello OpenGL renderer!", 500, 260, mScaledPaint);
+
+ canvas.save();
+ canvas.clipRect(150.0f, 220.0f, 450.0f, 320.0f);
+ canvas.drawText("Hello OpenGL renderer!", 100, 300, mLargePaint);
+ canvas.restore();
+ }
+ }
+} \ No newline at end of file
diff --git a/tests/HwAccelerationTest/src/com/google/android/test/hwui/Transform3dActivity.java b/tests/HwAccelerationTest/src/com/google/android/test/hwui/Transform3dActivity.java
new file mode 100644
index 0000000..6134cde
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/google/android/test/hwui/Transform3dActivity.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2010 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.google.android.test.hwui;
+
+import android.app.Activity;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Camera;
+import android.graphics.Canvas;
+import android.graphics.Matrix;
+import android.os.Bundle;
+import android.view.View;
+
+@SuppressWarnings({"UnusedDeclaration"})
+public class Transform3dActivity extends Activity {
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ final Transform3dView view = new Transform3dView(this);
+ setContentView(view);
+ }
+
+ static class Transform3dView extends View {
+ private final Bitmap mBitmap1;
+ private Camera mCamera;
+ private Matrix mMatrix;
+
+ Transform3dView(Context c) {
+ super(c);
+
+ mBitmap1 = BitmapFactory.decodeResource(c.getResources(), R.drawable.sunset1);
+ mCamera = new Camera();
+ mMatrix = new Matrix();
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ super.onDraw(canvas);
+
+ canvas.drawARGB(255, 255, 255, 255);
+
+ final float centerX = getWidth() / 2.0f - mBitmap1.getWidth() / 2.0f;
+ final float centerY = getHeight() / 2.0f - mBitmap1.getHeight() / 2.0f;
+ final Camera camera = mCamera;
+
+ final Matrix matrix = mMatrix;
+
+ rotate(centerX, centerY, camera, matrix, 32.0f);
+ drawBitmap(canvas, centerX, centerY, 0.0f, matrix);
+
+ rotate(centerX, centerY, camera, matrix, 12.0f);
+ drawBitmap(canvas, centerX, centerY, -mBitmap1.getWidth(), matrix);
+
+ rotate(centerX, centerY, camera, matrix, 52.0f);
+ drawBitmap(canvas, centerX, centerY, mBitmap1.getWidth(), matrix);
+
+ rotate(centerX, centerY, camera, matrix, 122.0f);
+ drawBitmap(canvas, centerX, centerY, mBitmap1.getWidth() * 2.0f, matrix);
+
+ }
+
+ private void drawBitmap(Canvas canvas, float centerX, float centerY, float offset,
+ Matrix matrix) {
+ canvas.save();
+ canvas.translate(offset, 0.0f);
+ canvas.concat(matrix);
+ canvas.drawBitmap(mBitmap1, centerX, centerY, null);
+ canvas.restore();
+ }
+
+ private void rotate(float centerX, float centerY, Camera camera,
+ Matrix matrix, float angle) {
+ camera.save();
+ camera.rotateY(angle);
+ camera.getMatrix(matrix);
+ camera.restore();
+
+ matrix.preTranslate(-centerX, -centerY);
+ matrix.postTranslate(centerX, centerY);
+ }
+ }
+}
diff --git a/tests/HwAccelerationTest/src/com/google/android/test/hwui/XfermodeActivity.java b/tests/HwAccelerationTest/src/com/google/android/test/hwui/XfermodeActivity.java
new file mode 100644
index 0000000..8c81f02
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/google/android/test/hwui/XfermodeActivity.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2010 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.google.android.test.hwui;
+
+import android.app.Activity;
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffXfermode;
+import android.os.Bundle;
+import android.view.View;
+
+import static android.graphics.PorterDuff.Mode;
+
+@SuppressWarnings({"UnusedDeclaration"})
+public class XfermodeActivity extends Activity {
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ setContentView(new XfermodesView(this));
+ }
+
+ static class XfermodesView extends View {
+ private final Paint mBluePaint;
+ private final Paint mRedPaint;
+
+ XfermodesView(Context c) {
+ super(c);
+
+ mBluePaint = new Paint();
+ mBluePaint.setColor(0xff0000ff);
+
+ mRedPaint = new Paint();
+ mRedPaint.setColor(0x7fff0000);
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ super.onDraw(canvas);
+ //canvas.drawRGB(255, 255, 255);
+
+ canvas.translate(100.0f, 100.0f);
+
+ // SRC modes
+ canvas.save();
+
+ drawRects(canvas, Mode.SRC_OVER);
+ canvas.translate(0.0f, 100.0f);
+
+ drawRects(canvas, Mode.SRC_IN);
+ canvas.translate(0.0f, 100.0f);
+
+ drawRects(canvas, Mode.SRC_OUT);
+ canvas.translate(0.0f, 100.0f);
+
+ drawRects(canvas, Mode.SRC_ATOP);
+ canvas.translate(0.0f, 100.0f);
+
+ drawRects(canvas, Mode.SRC);
+
+ canvas.restore();
+
+ canvas.translate(100.0f, 0.0f);
+
+ // DST modes
+ canvas.save();
+
+ drawRects(canvas, Mode.DST_OVER);
+ canvas.translate(0.0f, 100.0f);
+
+ drawRects(canvas, Mode.DST_IN);
+ canvas.translate(0.0f, 100.0f);
+
+ drawRects(canvas, Mode.DST_OUT);
+ canvas.translate(0.0f, 100.0f);
+
+ drawRects(canvas, Mode.DST_ATOP);
+ canvas.translate(0.0f, 100.0f);
+
+ drawRects(canvas, Mode.DST);
+
+ canvas.restore();
+
+ canvas.translate(100.0f, 0.0f);
+
+ // Other modes
+ canvas.save();
+
+ drawRects(canvas, Mode.CLEAR);
+ canvas.translate(0.0f, 100.0f);
+
+ drawRects(canvas, Mode.XOR);
+
+ canvas.translate(0.0f, 100.0f);
+
+ mBluePaint.setAlpha(127);
+ canvas.drawRect(0.0f, 0.0f, 50.0f, 50.0f, mBluePaint);
+
+ canvas.translate(0.0f, 100.0f);
+
+ mBluePaint.setAlpha(10);
+ mBluePaint.setColor(0x7f0000ff);
+ canvas.drawRect(0.0f, 0.0f, 50.0f, 50.0f, mBluePaint);
+
+ mBluePaint.setColor(0xff0000ff);
+ mBluePaint.setAlpha(255);
+
+ canvas.restore();
+ }
+
+ private void drawRects(Canvas canvas, PorterDuff.Mode mode) {
+ canvas.drawRect(0.0f, 0.0f, 50.0f, 50.0f, mBluePaint);
+
+ canvas.save();
+ canvas.translate(25.0f, 25.0f);
+ mRedPaint.setXfermode(new PorterDuffXfermode(mode));
+ canvas.drawRect(0.0f, 0.0f, 50.0f, 50.0f, mRedPaint);
+ canvas.restore();
+ }
+ }
+}
diff --git a/tests/StatusBar/res/drawable-hdpi/app_gmail.png b/tests/StatusBar/res/drawable-hdpi/app_gmail.png
new file mode 100644
index 0000000..23d33e5
--- /dev/null
+++ b/tests/StatusBar/res/drawable-hdpi/app_gmail.png
Binary files differ
diff --git a/tests/StatusBar/res/drawable-hdpi/app_phone.png b/tests/StatusBar/res/drawable-hdpi/app_phone.png
new file mode 100644
index 0000000..772e7d3
--- /dev/null
+++ b/tests/StatusBar/res/drawable-hdpi/app_phone.png
Binary files differ
diff --git a/tests/StatusBar/res/drawable-hdpi/ic_statusbar_chat.png b/tests/StatusBar/res/drawable-hdpi/ic_statusbar_chat.png
new file mode 100644
index 0000000..e3b4e2b
--- /dev/null
+++ b/tests/StatusBar/res/drawable-hdpi/ic_statusbar_chat.png
Binary files differ
diff --git a/tests/StatusBar/res/drawable-hdpi/ic_statusbar_email.png b/tests/StatusBar/res/drawable-hdpi/ic_statusbar_email.png
new file mode 100644
index 0000000..8cefd36
--- /dev/null
+++ b/tests/StatusBar/res/drawable-hdpi/ic_statusbar_email.png
Binary files differ
diff --git a/tests/StatusBar/res/drawable-hdpi/ic_statusbar_missedcall.png b/tests/StatusBar/res/drawable-hdpi/ic_statusbar_missedcall.png
new file mode 100644
index 0000000..4feec28
--- /dev/null
+++ b/tests/StatusBar/res/drawable-hdpi/ic_statusbar_missedcall.png
Binary files differ
diff --git a/tests/StatusBar/res/drawable-hdpi/icon1.png b/tests/StatusBar/res/drawable-hdpi/icon1.png
new file mode 100644
index 0000000..0485257
--- /dev/null
+++ b/tests/StatusBar/res/drawable-hdpi/icon1.png
Binary files differ
diff --git a/tests/StatusBar/res/drawable-hdpi/icon2.png b/tests/StatusBar/res/drawable-hdpi/icon2.png
new file mode 100644
index 0000000..a630986
--- /dev/null
+++ b/tests/StatusBar/res/drawable-hdpi/icon2.png
Binary files differ
diff --git a/tests/StatusBar/res/drawable-hdpi/icon3.png b/tests/StatusBar/res/drawable-hdpi/icon3.png
new file mode 100644
index 0000000..9d72b66
--- /dev/null
+++ b/tests/StatusBar/res/drawable-hdpi/icon3.png
Binary files differ
diff --git a/tests/StatusBar/res/drawable-hdpi/icon4.png b/tests/StatusBar/res/drawable-hdpi/icon4.png
new file mode 100644
index 0000000..a4a40f2
--- /dev/null
+++ b/tests/StatusBar/res/drawable-hdpi/icon4.png
Binary files differ
diff --git a/tests/StatusBar/res/drawable/app_gmail.png b/tests/StatusBar/res/drawable-mdpi/app_gmail.png
index beaaacf..beaaacf 100644
--- a/tests/StatusBar/res/drawable/app_gmail.png
+++ b/tests/StatusBar/res/drawable-mdpi/app_gmail.png
Binary files differ
diff --git a/tests/StatusBar/res/drawable/app_phone.png b/tests/StatusBar/res/drawable-mdpi/app_phone.png
index 2748c1c..2748c1c 100644
--- a/tests/StatusBar/res/drawable/app_phone.png
+++ b/tests/StatusBar/res/drawable-mdpi/app_phone.png
Binary files differ
diff --git a/tests/StatusBar/res/drawable/ic_statusbar_chat.png b/tests/StatusBar/res/drawable-mdpi/ic_statusbar_chat.png
index e08cddb..e08cddb 100644
--- a/tests/StatusBar/res/drawable/ic_statusbar_chat.png
+++ b/tests/StatusBar/res/drawable-mdpi/ic_statusbar_chat.png
Binary files differ
diff --git a/tests/StatusBar/res/drawable/ic_statusbar_email.png b/tests/StatusBar/res/drawable-mdpi/ic_statusbar_email.png
index 19c9005..19c9005 100644
--- a/tests/StatusBar/res/drawable/ic_statusbar_email.png
+++ b/tests/StatusBar/res/drawable-mdpi/ic_statusbar_email.png
Binary files differ
diff --git a/tests/StatusBar/res/drawable/ic_statusbar_missedcall.png b/tests/StatusBar/res/drawable-mdpi/ic_statusbar_missedcall.png
index ced62d7..ced62d7 100644
--- a/tests/StatusBar/res/drawable/ic_statusbar_missedcall.png
+++ b/tests/StatusBar/res/drawable-mdpi/ic_statusbar_missedcall.png
Binary files differ
diff --git a/tests/StatusBar/res/drawable/icon1.png b/tests/StatusBar/res/drawable-mdpi/icon1.png
index abfb6fa..abfb6fa 100644
--- a/tests/StatusBar/res/drawable/icon1.png
+++ b/tests/StatusBar/res/drawable-mdpi/icon1.png
Binary files differ
diff --git a/tests/StatusBar/res/drawable/icon2.png b/tests/StatusBar/res/drawable-mdpi/icon2.png
index 564b38b..564b38b 100644
--- a/tests/StatusBar/res/drawable/icon2.png
+++ b/tests/StatusBar/res/drawable-mdpi/icon2.png
Binary files differ
diff --git a/tests/StatusBar/res/drawable/icon3.png b/tests/StatusBar/res/drawable-mdpi/icon3.png
index e765d8f..e765d8f 100644
--- a/tests/StatusBar/res/drawable/icon3.png
+++ b/tests/StatusBar/res/drawable-mdpi/icon3.png
Binary files differ
diff --git a/tests/StatusBar/res/drawable/icon4.png b/tests/StatusBar/res/drawable-mdpi/icon4.png
index 5f33885..5f33885 100644
--- a/tests/StatusBar/res/drawable/icon4.png
+++ b/tests/StatusBar/res/drawable-mdpi/icon4.png
Binary files differ
diff --git a/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java b/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java
index 37976ee..16b3001 100644
--- a/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java
+++ b/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java
@@ -633,20 +633,6 @@ public class NotificationTestList extends TestActivity
}
},
- new Test("Ticker") {
- public void run() {
- Notification not = new Notification(
- R.drawable.app_gmail,
- "New mail from joeo@example.com, on the topic of very long ticker texts",
- System.currentTimeMillis());
- not.setLatestEventInfo(NotificationTestList.this,
- "A new message awaits",
- "The contents are very interesting and important",
- makeIntent());
- mNM.notify(1, not);
- }
- },
-
new Test("Crash") {
public void run()
{