diff options
Diffstat (limited to 'tests')
74 files changed, 5064 insertions, 135 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/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..b62db51 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() { @@ -498,4 +516,8 @@ public class CallbackProxy extends Handler implements EventSender, LayoutTestCon 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..9f580a3 100644 --- a/tests/DumpRenderTree/src/com/android/dumprendertree/FileFilter.java +++ b/tests/DumpRenderTree/src/com/android/dumprendertree/FileFilter.java @@ -73,43 +73,73 @@ 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("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 +202,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..15288b5 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,7 @@ public interface LayoutTestController { public void setGeolocationPermission(boolean allow); public void overridePreference(String key, boolean value); + + // For XSSAuditor tests + public void setXSSAuditorEnabled(boolean flag); } 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..8c7254c 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; @@ -99,6 +101,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 +121,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); @@ -159,6 +164,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 +180,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 +250,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 +343,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 +377,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(); + } } } @@ -473,6 +504,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 +525,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 +590,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); @@ -676,15 +694,28 @@ public class TestShellActivity extends Activity implements LayoutTestController } @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 +754,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 +772,10 @@ public class TestShellActivity extends Activity implements LayoutTestController mEventSender.clearTouchPoints(); mEventSender.clearTouchMetaState(); mPageFinished = false; - mOneHundredPercentComplete = false; mDumpWebKitData = false; mGetDrawtime = false; mSaveImagePath = null; + setDefaultWebSettings(mWebView); } private long[] getDrawWebViewTime(WebView view, int count) { @@ -779,7 +812,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 +820,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 +845,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 +862,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 +874,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 +889,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,6 +903,9 @@ 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() + diff --git a/tests/DumpRenderTree2/Android.mk b/tests/DumpRenderTree2/Android.mk new file mode 100644 index 0000000..948ad72 --- /dev/null +++ b/tests/DumpRenderTree2/Android.mk @@ -0,0 +1,12 @@ +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_MODULE_TAGS := tests + +LOCAL_SRC_FILES := $(call all-subdir-java-files) + +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..9f6097a --- /dev/null +++ b/tests/DumpRenderTree2/AndroidManifest.xml @@ -0,0 +1,45 @@ +<?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> + <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> + + <activity android:name=".TestsListActivity" + android:label="Tests' list activity"> + </activity> + + <activity android:name=".LayoutTestsExecutor" + android:label="Layout tests' executor" + android:process=":executor"> + </activity> + + <service android:name="ManagerService"> + </service> + </application> + + <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/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/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..8ff5e63 --- /dev/null +++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/LayoutTestController.java @@ -0,0 +1,92 @@ +/* + * 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); + } +}
\ No newline at end of file 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..063cb73 --- /dev/null +++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/LayoutTestsExecutor.java @@ -0,0 +1,541 @@ +/* + * 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.Callback; +import android.webkit.WebStorage.QuotaUpdater; + +import java.io.File; +import java.util.List; + +/** + * 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; + + 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; + + 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 mSetGeolocationPermissionCalled; + private boolean mGeolocationPermission; + + 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); + runNextTest(); + } + + @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, Callback callback) { + if (mSetGeolocationPermissionCalled) { + callback.invoke(origin, mGeolocationPermission, false); + } + } + }; + + /** 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); + + 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"); + + /** + * 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); + } + + private void runNextTest() { + assert mCurrentState == CurrentState.IDLE : "mCurrentState = " + mCurrentState.name(); + + if (mTestsList.isEmpty()) { + onAllTestsFinished(); + return; + } + + mCurrentTestRelativePath = mTestsList.remove(0); + 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()); + } + + 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_PREMISSION = 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_PREMISSION: + mSetGeolocationPermissionCalled = true; + mGeolocationPermission = msg.arg1 == 1; + break; + + default: + Log.w(LOG_TAG + "::handleMessage", "Message code does not exist: " + msg.what); + break; + } + } + }; + + private void resetLayoutTestController() { + mCanOpenWindows = false; + mDumpDatabaseCallbacks = false; + mSetGeolocationPermissionCalled = false; + mGeolocationPermission = false; + } + + 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_PREMISSION); + msg.arg1 = allow ? 1 : 0; + msg.sendToTarget(); + } +}
\ No newline at end of file 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..3c77a55 --- /dev/null +++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/ManagerService.java @@ -0,0 +1,169 @@ +/* + * 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"; + + /** 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; + + private Handler mIncomingHandler = new Handler() { + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case MSG_PROCESS_ACTUAL_RESULTS: + Log.d(LOG_TAG + ".mIncomingHandler", msg.getData().getString("relativePath")); + onActualResultsObtained(msg.getData()); + break; + + case MSG_ALL_TESTS_FINISHED: + mSummarizer.summarize(); + break; + } + } + }; + + private Messenger mMessenger = new Messenger(mIncomingHandler); + + private FileFilter mFileFilter; + private Summarizer mSummarizer; + + @Override + public void onCreate() { + super.onCreate(); + + mFileFilter = new FileFilter(TESTS_ROOT_DIR_PATH); + mSummarizer = new Summarizer(mFileFilter, RESULTS_ROOT_DIR_PATH); + } + + @Override + public IBinder onBind(Intent intent) { + return mMessenger.getBinder(); + } + + private void onActualResultsObtained(Bundle bundle) { + AbstractResult results = + AbstractResult.TestType.valueOf(bundle.getString("type")).createResult(bundle); + 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..56822f3 --- /dev/null +++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/Summarizer.java @@ -0,0 +1,314 @@ +/* + * 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 java.io.File; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Date; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** + * 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;" + + "</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_SUMMARY_RELATIVE_PATH = "summary.html"; + + 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; + + 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 summarize() { + 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_SUMMARY_RELATIVE_PATH), + html.toString().getBytes(), false); + } + + private void createTopSummaryTable(StringBuilder html) { + int total = mFailedNotIgnoredTests.size() + + mPassedNotIgnoredTests.size() + + mIgnoredTests.size(); + SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss"); + html.append("<h1> - total of " + total + " tests - "); + html.append(dateFormat.format(new Date()) + "</h1>"); + + html.append("<table class=\"summary\">"); + 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("LayoutTestController") && + additionalTextOutputString.contains("has no method")) { + html.append(" <span class=\"listItem noLtc\">LTC 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..a402ae1 --- /dev/null +++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/TestsListActivity.java @@ -0,0 +1,101 @@ +/* + * 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 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 (TODO). + */ +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; + + @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); + + new TestsListPreloaderThread(path, doneMsg).start(); + } + + /** + * (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); + intent.putStringArrayListExtra(LayoutTestsExecutor.EXTRA_TESTS_LIST, + new ArrayList<String>(mTestsList.subList(startFrom, mTotalTestCount))); + intent.putExtra(LayoutTestsExecutor.EXTRA_TEST_INDEX, startFrom); + 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..f76105d --- /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); + return; + } + + /** 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/ui/DirListActivity.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/ui/DirListActivity.java new file mode 100644 index 0000000..661a8ec --- /dev/null +++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/ui/DirListActivity.java @@ -0,0 +1,400 @@ +/* + * 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); + + 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..73994f7 --- /dev/null +++ b/tests/HwAccelerationTest/AndroidManifest.xml @@ -0,0 +1,175 @@ +<?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="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> + + </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/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/layout/list_activity.xml b/tests/HwAccelerationTest/res/layout/list_activity.xml new file mode 100644 index 0000000..f548f53 --- /dev/null +++ b/tests/HwAccelerationTest/res/layout/list_activity.xml @@ -0,0 +1,53 @@ +<?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"> + + <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/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/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..c638958 --- /dev/null +++ b/tests/HwAccelerationTest/src/com/google/android/test/hwui/ListActivity.java @@ -0,0 +1,104 @@ +/* + * 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.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); + } + + 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..fd7a1e6 --- /dev/null +++ b/tests/HwAccelerationTest/src/com/google/android/test/hwui/QuickRejectActivity.java @@ -0,0 +1,62 @@ +/* + * 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.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); + + 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/TextActivity.java b/tests/HwAccelerationTest/src/com/google/android/test/hwui/TextActivity.java new file mode 100644 index 0000000..3b5cf43 --- /dev/null +++ b/tests/HwAccelerationTest/src/com/google/android/test/hwui/TextActivity.java @@ -0,0 +1,70 @@ +/* + * 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; + + CustomTextView(Context c) { + super(c); + + mMediumPaint = new Paint(); + mMediumPaint.setAntiAlias(true); + mMediumPaint.setColor(0xffff0000); + mLargePaint = new Paint(); + mLargePaint.setAntiAlias(true); + mLargePaint.setTextSize(36.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); + canvas.drawText("Hello OpenGL renderer!", 100, 200, mLargePaint); + + 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 |
