diff options
Diffstat (limited to 'tests')
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 Binary files differnew file mode 100644 index 0000000..5b3fcec --- /dev/null +++ b/tests/DumpRenderTree2/res/drawable/folder.png diff --git a/tests/DumpRenderTree2/res/drawable/runtest.png b/tests/DumpRenderTree2/res/drawable/runtest.png Binary files differnew file mode 100644 index 0000000..910c654 --- /dev/null +++ b/tests/DumpRenderTree2/res/drawable/runtest.png 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 = '▼ ';" + + " } else {" + + " element.style.display = 'none';" + + " triangle.innerHTML = '▶ ';" + + " }" + + " }" + + "</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\">■ </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 + "\">▶ </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\">■ </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 = " "; + + if (diff.text.endsWith("\n")) { + lineNums.add(i++); + /** TODO: Think of better way to replace stuff */ + line += begSpan + diff.text.replace(" ", " ") + + endSpan + br; + lines.add(line); + line = ""; + } else { + line += begSpan + diff.text.replace(" ", " ") + 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 Binary files differnew file mode 100644 index 0000000..60fbdf5 --- /dev/null +++ b/tests/HwAccelerationTest/res/drawable-hdpi/icon.png diff --git a/tests/HwAccelerationTest/res/drawable-hdpi/sunset1.jpg b/tests/HwAccelerationTest/res/drawable-hdpi/sunset1.jpg Binary files differnew file mode 100644 index 0000000..92851f3 --- /dev/null +++ b/tests/HwAccelerationTest/res/drawable-hdpi/sunset1.jpg diff --git a/tests/HwAccelerationTest/res/drawable-hdpi/sunset2.png b/tests/HwAccelerationTest/res/drawable-hdpi/sunset2.png Binary files differnew file mode 100644 index 0000000..3258ee7 --- /dev/null +++ b/tests/HwAccelerationTest/res/drawable-hdpi/sunset2.png diff --git a/tests/HwAccelerationTest/res/drawable-hdpi/sunset3.png b/tests/HwAccelerationTest/res/drawable-hdpi/sunset3.png Binary files differnew file mode 100644 index 0000000..6508b27 --- /dev/null +++ b/tests/HwAccelerationTest/res/drawable-hdpi/sunset3.png diff --git a/tests/HwAccelerationTest/res/drawable/icon.png b/tests/HwAccelerationTest/res/drawable/icon.png Binary files differnew file mode 100644 index 0000000..cb40a19 --- /dev/null +++ b/tests/HwAccelerationTest/res/drawable/icon.png diff --git a/tests/HwAccelerationTest/res/drawable/sunset1.jpg b/tests/HwAccelerationTest/res/drawable/sunset1.jpg Binary files differnew file mode 100644 index 0000000..92851f3 --- /dev/null +++ b/tests/HwAccelerationTest/res/drawable/sunset1.jpg diff --git a/tests/HwAccelerationTest/res/drawable/sunset2.png b/tests/HwAccelerationTest/res/drawable/sunset2.png Binary files differnew file mode 100644 index 0000000..3258ee7 --- /dev/null +++ b/tests/HwAccelerationTest/res/drawable/sunset2.png diff --git a/tests/HwAccelerationTest/res/drawable/sunset3.png b/tests/HwAccelerationTest/res/drawable/sunset3.png Binary files differnew file mode 100644 index 0000000..6508b27 --- /dev/null +++ b/tests/HwAccelerationTest/res/drawable/sunset3.png 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 Binary files differnew file mode 100644 index 0000000..23d33e5 --- /dev/null +++ b/tests/StatusBar/res/drawable-hdpi/app_gmail.png diff --git a/tests/StatusBar/res/drawable-hdpi/app_phone.png b/tests/StatusBar/res/drawable-hdpi/app_phone.png Binary files differnew file mode 100644 index 0000000..772e7d3 --- /dev/null +++ b/tests/StatusBar/res/drawable-hdpi/app_phone.png diff --git a/tests/StatusBar/res/drawable-hdpi/ic_statusbar_chat.png b/tests/StatusBar/res/drawable-hdpi/ic_statusbar_chat.png Binary files differnew file mode 100644 index 0000000..e3b4e2b --- /dev/null +++ b/tests/StatusBar/res/drawable-hdpi/ic_statusbar_chat.png diff --git a/tests/StatusBar/res/drawable-hdpi/ic_statusbar_email.png b/tests/StatusBar/res/drawable-hdpi/ic_statusbar_email.png Binary files differnew file mode 100644 index 0000000..8cefd36 --- /dev/null +++ b/tests/StatusBar/res/drawable-hdpi/ic_statusbar_email.png diff --git a/tests/StatusBar/res/drawable-hdpi/ic_statusbar_missedcall.png b/tests/StatusBar/res/drawable-hdpi/ic_statusbar_missedcall.png Binary files differnew file mode 100644 index 0000000..4feec28 --- /dev/null +++ b/tests/StatusBar/res/drawable-hdpi/ic_statusbar_missedcall.png diff --git a/tests/StatusBar/res/drawable-hdpi/icon1.png b/tests/StatusBar/res/drawable-hdpi/icon1.png Binary files differnew file mode 100644 index 0000000..0485257 --- /dev/null +++ b/tests/StatusBar/res/drawable-hdpi/icon1.png diff --git a/tests/StatusBar/res/drawable-hdpi/icon2.png b/tests/StatusBar/res/drawable-hdpi/icon2.png Binary files differnew file mode 100644 index 0000000..a630986 --- /dev/null +++ b/tests/StatusBar/res/drawable-hdpi/icon2.png diff --git a/tests/StatusBar/res/drawable-hdpi/icon3.png b/tests/StatusBar/res/drawable-hdpi/icon3.png Binary files differnew file mode 100644 index 0000000..9d72b66 --- /dev/null +++ b/tests/StatusBar/res/drawable-hdpi/icon3.png diff --git a/tests/StatusBar/res/drawable-hdpi/icon4.png b/tests/StatusBar/res/drawable-hdpi/icon4.png Binary files differnew file mode 100644 index 0000000..a4a40f2 --- /dev/null +++ b/tests/StatusBar/res/drawable-hdpi/icon4.png diff --git a/tests/StatusBar/res/drawable/app_gmail.png b/tests/StatusBar/res/drawable-mdpi/app_gmail.png Binary files differindex beaaacf..beaaacf 100644 --- a/tests/StatusBar/res/drawable/app_gmail.png +++ b/tests/StatusBar/res/drawable-mdpi/app_gmail.png diff --git a/tests/StatusBar/res/drawable/app_phone.png b/tests/StatusBar/res/drawable-mdpi/app_phone.png Binary files differindex 2748c1c..2748c1c 100644 --- a/tests/StatusBar/res/drawable/app_phone.png +++ b/tests/StatusBar/res/drawable-mdpi/app_phone.png diff --git a/tests/StatusBar/res/drawable/ic_statusbar_chat.png b/tests/StatusBar/res/drawable-mdpi/ic_statusbar_chat.png Binary files differindex e08cddb..e08cddb 100644 --- a/tests/StatusBar/res/drawable/ic_statusbar_chat.png +++ b/tests/StatusBar/res/drawable-mdpi/ic_statusbar_chat.png diff --git a/tests/StatusBar/res/drawable/ic_statusbar_email.png b/tests/StatusBar/res/drawable-mdpi/ic_statusbar_email.png Binary files differindex 19c9005..19c9005 100644 --- a/tests/StatusBar/res/drawable/ic_statusbar_email.png +++ b/tests/StatusBar/res/drawable-mdpi/ic_statusbar_email.png diff --git a/tests/StatusBar/res/drawable/ic_statusbar_missedcall.png b/tests/StatusBar/res/drawable-mdpi/ic_statusbar_missedcall.png Binary files differindex ced62d7..ced62d7 100644 --- a/tests/StatusBar/res/drawable/ic_statusbar_missedcall.png +++ b/tests/StatusBar/res/drawable-mdpi/ic_statusbar_missedcall.png diff --git a/tests/StatusBar/res/drawable/icon1.png b/tests/StatusBar/res/drawable-mdpi/icon1.png Binary files differindex abfb6fa..abfb6fa 100644 --- a/tests/StatusBar/res/drawable/icon1.png +++ b/tests/StatusBar/res/drawable-mdpi/icon1.png diff --git a/tests/StatusBar/res/drawable/icon2.png b/tests/StatusBar/res/drawable-mdpi/icon2.png Binary files differindex 564b38b..564b38b 100644 --- a/tests/StatusBar/res/drawable/icon2.png +++ b/tests/StatusBar/res/drawable-mdpi/icon2.png diff --git a/tests/StatusBar/res/drawable/icon3.png b/tests/StatusBar/res/drawable-mdpi/icon3.png Binary files differindex e765d8f..e765d8f 100644 --- a/tests/StatusBar/res/drawable/icon3.png +++ b/tests/StatusBar/res/drawable-mdpi/icon3.png diff --git a/tests/StatusBar/res/drawable/icon4.png b/tests/StatusBar/res/drawable-mdpi/icon4.png Binary files differindex 5f33885..5f33885 100644 --- a/tests/StatusBar/res/drawable/icon4.png +++ b/tests/StatusBar/res/drawable-mdpi/icon4.png 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() { |