diff options
95 files changed, 5366 insertions, 1956 deletions
diff --git a/JavaLibrary.mk b/JavaLibrary.mk index eb2f791..70037e8 100644 --- a/JavaLibrary.mk +++ b/JavaLibrary.mk @@ -85,7 +85,7 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES := $(call all-test-java-files-under,annotation) LOCAL_JAVA_RESOURCE_DIRS := $(test_resource_dirs) LOCAL_NO_STANDARD_LIBRARIES := true -LOCAL_JAVA_LIBRARIES := core caliper core-tests-support +LOCAL_JAVA_LIBRARIES := core core-tests-support LOCAL_DX_FLAGS := --core-library LOCAL_MODULE_TAGS := tests LOCAL_MODULE := core-tests-annotation @@ -95,7 +95,7 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES := $(call all-test-java-files-under,archive) LOCAL_JAVA_RESOURCE_DIRS := $(test_resource_dirs) LOCAL_NO_STANDARD_LIBRARIES := true -LOCAL_JAVA_LIBRARIES := core caliper core-tests-support +LOCAL_JAVA_LIBRARIES := core core-tests-support LOCAL_DX_FLAGS := --core-library LOCAL_MODULE_TAGS := tests LOCAL_MODULE := core-tests-archive @@ -105,7 +105,7 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES := $(call all-test-java-files-under,concurrent) LOCAL_JAVA_RESOURCE_DIRS := $(test_resource_dirs) LOCAL_NO_STANDARD_LIBRARIES := true -LOCAL_JAVA_LIBRARIES := core caliper core-tests-support +LOCAL_JAVA_LIBRARIES := core core-tests-support LOCAL_DX_FLAGS := --core-library LOCAL_MODULE_TAGS := tests LOCAL_MODULE := core-tests-concurrent @@ -115,7 +115,7 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES := $(call all-test-java-files-under,crypto) LOCAL_JAVA_RESOURCE_DIRS := $(test_resource_dirs) LOCAL_NO_STANDARD_LIBRARIES := true -LOCAL_JAVA_LIBRARIES := core caliper core-tests-support +LOCAL_JAVA_LIBRARIES := core core-tests-support LOCAL_DX_FLAGS := --core-library LOCAL_MODULE_TAGS := tests LOCAL_MODULE := core-tests-crypto @@ -125,7 +125,7 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES := $(call all-test-java-files-under,dom) LOCAL_JAVA_RESOURCE_DIRS := $(test_resource_dirs) LOCAL_NO_STANDARD_LIBRARIES := true -LOCAL_JAVA_LIBRARIES := core caliper core-tests-support +LOCAL_JAVA_LIBRARIES := core core-tests-support LOCAL_DX_FLAGS := --core-library LOCAL_MODULE_TAGS := tests LOCAL_MODULE := core-tests-dom @@ -135,7 +135,7 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES := $(call all-test-java-files-under,icu) LOCAL_JAVA_RESOURCE_DIRS := $(test_resource_dirs) LOCAL_NO_STANDARD_LIBRARIES := true -LOCAL_JAVA_LIBRARIES := core caliper core-tests-support +LOCAL_JAVA_LIBRARIES := core core-tests-support LOCAL_DX_FLAGS := --core-library LOCAL_MODULE_TAGS := tests LOCAL_MODULE := core-tests-icu @@ -145,7 +145,7 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES := $(call all-test-java-files-under,logging) LOCAL_JAVA_RESOURCE_DIRS := $(test_resource_dirs) LOCAL_NO_STANDARD_LIBRARIES := true -LOCAL_JAVA_LIBRARIES := core caliper core-tests-support +LOCAL_JAVA_LIBRARIES := core core-tests-support LOCAL_DX_FLAGS := --core-library LOCAL_MODULE_TAGS := tests LOCAL_MODULE := core-tests-logging @@ -155,7 +155,7 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES := $(call all-test-java-files-under,luni-kernel) LOCAL_JAVA_RESOURCE_DIRS := $(test_resource_dirs) LOCAL_NO_STANDARD_LIBRARIES := true -LOCAL_JAVA_LIBRARIES := core caliper core-tests-support +LOCAL_JAVA_LIBRARIES := core core-tests-support LOCAL_DX_FLAGS := --core-library LOCAL_MODULE_TAGS := tests LOCAL_MODULE := core-tests-luni-kernel @@ -169,7 +169,7 @@ LOCAL_NO_STANDARD_LIBRARIES := true # together, so it has compile-time dependencies on all the other test # libraries. # TODO: we should have a bogus module that just contains tests.AllTests for speed. -LOCAL_JAVA_LIBRARIES := core caliper core-tests-support +LOCAL_JAVA_LIBRARIES := core core-tests-support LOCAL_JAVA_LIBRARIES += core-tests-annotation core-tests-archive LOCAL_JAVA_LIBRARIES += core-tests-concurrent core-tests-crypto LOCAL_JAVA_LIBRARIES += core-tests-dom core-tests-icu core-tests-logging @@ -186,7 +186,7 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES := $(call all-test-java-files-under,math) LOCAL_JAVA_RESOURCE_DIRS := $(test_resource_dirs) LOCAL_NO_STANDARD_LIBRARIES := true -LOCAL_JAVA_LIBRARIES := core caliper core-tests-support +LOCAL_JAVA_LIBRARIES := core core-tests-support LOCAL_DX_FLAGS := --core-library LOCAL_MODULE_TAGS := tests LOCAL_MODULE := core-tests-math @@ -196,7 +196,7 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES := $(call all-test-java-files-under,nio) LOCAL_JAVA_RESOURCE_DIRS := $(test_resource_dirs) LOCAL_NO_STANDARD_LIBRARIES := true -LOCAL_JAVA_LIBRARIES := core caliper core-tests-support +LOCAL_JAVA_LIBRARIES := core core-tests-support LOCAL_DX_FLAGS := --core-library LOCAL_MODULE_TAGS := tests LOCAL_MODULE := core-tests-nio @@ -206,7 +206,7 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES := $(call all-test-java-files-under,nio_char) LOCAL_JAVA_RESOURCE_DIRS := $(test_resource_dirs) LOCAL_NO_STANDARD_LIBRARIES := true -LOCAL_JAVA_LIBRARIES := core caliper core-tests-support +LOCAL_JAVA_LIBRARIES := core core-tests-support LOCAL_DX_FLAGS := --core-library LOCAL_MODULE_TAGS := tests LOCAL_MODULE := core-tests-nio_char @@ -216,7 +216,7 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES := $(call all-test-java-files-under,prefs) LOCAL_JAVA_RESOURCE_DIRS := $(test_resource_dirs) LOCAL_NO_STANDARD_LIBRARIES := true -LOCAL_JAVA_LIBRARIES := core caliper core-tests-support +LOCAL_JAVA_LIBRARIES := core core-tests-support LOCAL_DX_FLAGS := --core-library LOCAL_MODULE_TAGS := tests LOCAL_MODULE := core-tests-prefs @@ -226,7 +226,7 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES := $(call all-test-java-files-under,regex) LOCAL_JAVA_RESOURCE_DIRS := $(test_resource_dirs) LOCAL_NO_STANDARD_LIBRARIES := true -LOCAL_JAVA_LIBRARIES := core caliper core-tests-support +LOCAL_JAVA_LIBRARIES := core core-tests-support LOCAL_DX_FLAGS := --core-library LOCAL_MODULE_TAGS := tests LOCAL_MODULE := core-tests-regex @@ -236,7 +236,7 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES := $(call all-test-java-files-under,security) LOCAL_JAVA_RESOURCE_DIRS := $(test_resource_dirs) LOCAL_NO_STANDARD_LIBRARIES := true -LOCAL_JAVA_LIBRARIES := core caliper core-tests-support +LOCAL_JAVA_LIBRARIES := core core-tests-support LOCAL_DX_FLAGS := --core-library LOCAL_MODULE_TAGS := tests LOCAL_MODULE := core-tests-security @@ -246,7 +246,7 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES := $(call all-test-java-files-under,sql) LOCAL_JAVA_RESOURCE_DIRS := $(test_resource_dirs) LOCAL_NO_STANDARD_LIBRARIES := true -LOCAL_JAVA_LIBRARIES := core caliper core-tests-support +LOCAL_JAVA_LIBRARIES := core core-tests-support LOCAL_DX_FLAGS := --core-library LOCAL_MODULE_TAGS := tests LOCAL_MODULE := core-tests-sql @@ -256,7 +256,7 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES := $(call all-test-java-files-under,suncompat) LOCAL_JAVA_RESOURCE_DIRS := $(test_resource_dirs) LOCAL_NO_STANDARD_LIBRARIES := true -LOCAL_JAVA_LIBRARIES := core caliper core-tests-support +LOCAL_JAVA_LIBRARIES := core core-tests-support LOCAL_DX_FLAGS := --core-library LOCAL_MODULE_TAGS := tests LOCAL_MODULE := core-tests-suncompat @@ -266,7 +266,7 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES := $(call all-test-java-files-under,support) LOCAL_JAVA_RESOURCE_DIRS := $(test_resource_dirs) LOCAL_NO_STANDARD_LIBRARIES := true -LOCAL_JAVA_LIBRARIES := core caliper +LOCAL_JAVA_LIBRARIES := core LOCAL_DX_FLAGS := --core-library LOCAL_MODULE_TAGS := tests LOCAL_MODULE := core-tests-support @@ -276,7 +276,7 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES := $(call all-test-java-files-under,text) LOCAL_JAVA_RESOURCE_DIRS := $(test_resource_dirs) LOCAL_NO_STANDARD_LIBRARIES := true -LOCAL_JAVA_LIBRARIES := core caliper core-tests-support +LOCAL_JAVA_LIBRARIES := core core-tests-support LOCAL_DX_FLAGS := --core-library LOCAL_MODULE_TAGS := tests LOCAL_MODULE := core-tests-text @@ -286,7 +286,7 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES := $(call all-test-java-files-under,x-net) LOCAL_JAVA_RESOURCE_DIRS := $(test_resource_dirs) LOCAL_NO_STANDARD_LIBRARIES := true -LOCAL_JAVA_LIBRARIES := core caliper core-tests-support +LOCAL_JAVA_LIBRARIES := core core-tests-support LOCAL_DX_FLAGS := --core-library LOCAL_MODULE_TAGS := tests LOCAL_MODULE := core-tests-x-net @@ -296,7 +296,7 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES := $(call all-test-java-files-under,xml) LOCAL_JAVA_RESOURCE_DIRS := $(test_resource_dirs) LOCAL_NO_STANDARD_LIBRARIES := true -LOCAL_JAVA_LIBRARIES := core caliper core-tests-support +LOCAL_JAVA_LIBRARIES := core core-tests-support LOCAL_DX_FLAGS := --core-library LOCAL_MODULE_TAGS := tests LOCAL_MODULE := core-tests-xml diff --git a/dalvik/src/main/java/dalvik/system/PathClassLoader.java b/dalvik/src/main/java/dalvik/system/PathClassLoader.java index 597eb5b..5d7333e 100644 --- a/dalvik/src/main/java/dalvik/system/PathClassLoader.java +++ b/dalvik/src/main/java/dalvik/system/PathClassLoader.java @@ -37,14 +37,14 @@ import dalvik.system.DexFile; * of files and directories in the local file system, but does not attempt to * load classes from the network. Android uses this class for its system class * loader and for its application class loader(s). - * + * * @since Android 1.0 */ public class PathClassLoader extends ClassLoader { private final String path; private final String libPath; - + private boolean initialized; private String[] mPaths; @@ -58,17 +58,17 @@ public class PathClassLoader extends ClassLoader { * and directories. This method is equivalent to calling * {@link #PathClassLoader(String, String, ClassLoader)} with a * {@code null} value for the second argument (see description there). - * + * * @param path * the list of files and directories - * + * * @param parent * the parent class loader */ public PathClassLoader(String path, ClassLoader parent) { this(path, null, parent); } - + /** * Creates a {@code PathClassLoader} that operates on two given lists of * files and directories. The entries of the first list should be one of the @@ -81,14 +81,14 @@ public class PathClassLoader extends ClassLoader { * The entries of the second list should be directories containing native * library files. Both lists are separated using the character specified by * the "path.separator" system property, which, on Android, defaults to ":". - * + * * @param path * the list of files and directories containing classes and * resources - * + * * @param libPath * the list of directories containing native libraries - * + * * @param parent * the parent class loader */ @@ -101,23 +101,23 @@ public class PathClassLoader extends ClassLoader { this.path = path; this.libPath = libPath; } - + private synchronized void ensureInit() { if (initialized) { return; } - + initialized = true; - + mPaths = path.split(":"); int length = mPaths.length; - + //System.out.println("PathClassLoader: " + mPaths); mFiles = new File[length]; mZips = new ZipFile[length]; mDexs = new DexFile[length]; - boolean wantDex = + boolean wantDex = System.getProperty("android.vm.dexfile", "").equals("true"); /* open all Zip and DEX files up front */ @@ -132,7 +132,7 @@ public class PathClassLoader extends ClassLoader { } catch (IOException ioex) { // expecting IOException and ZipException - //System.out.println("Failed opening '" + archive + "': " + ioex); + //System.out.println("Failed opening '" + pathFile + "': " + ioex); //ioex.printStackTrace(); } if (wantDex) { @@ -151,7 +151,7 @@ public class PathClassLoader extends ClassLoader { String pathList = System.getProperty("java.library.path", "."); String pathSep = System.getProperty("path.separator", ":"); String fileSep = System.getProperty("file.separator", "/"); - + if (libPath != null) { if (pathList.length() > 0) { pathList += pathSep + libPath; @@ -177,7 +177,7 @@ public class PathClassLoader extends ClassLoader { /** * Finds a class. This method is called by {@code loadClass()} after the * parent ClassLoader has failed to find a loaded class of the same name. - * + * * @param name * The "binary name" of the class to search for, in a * human-readable form like "java.lang.String" or @@ -190,7 +190,7 @@ public class PathClassLoader extends ClassLoader { protected Class<?> findClass(String name) throws ClassNotFoundException { ensureInit(); - + //System.out.println("PathClassLoader " + this + ": findClass '" + name + "'"); byte[] data = null; @@ -234,7 +234,7 @@ public class PathClassLoader extends ClassLoader { } } } - + return defineClass(name, data, 0, data.length); } */ @@ -247,7 +247,7 @@ public class PathClassLoader extends ClassLoader { * Finds a resource. This method is called by {@code getResource()} after * the parent ClassLoader has failed to find a loaded resource of the same * name. - * + * * @param name * The name of the resource to find * @return the location of the resource as a URL, or {@code null} if the @@ -260,7 +260,7 @@ public class PathClassLoader extends ClassLoader { //java.util.logging.Logger.global.severe("findResource: " + name); int length = mPaths.length; - + for (int i = 0; i < length; i++) { URL result = findResource(name, i); if(result != null) { @@ -273,7 +273,7 @@ public class PathClassLoader extends ClassLoader { /** * Finds an enumeration of URLs for the resource with the specified name. - * + * * @param resName * the name of the resource to find. * @return an enumeration of {@code URL} objects for the requested resource. @@ -285,7 +285,7 @@ public class PathClassLoader extends ClassLoader { int length = mPaths.length; ArrayList<URL> results = new ArrayList<URL>(); - + for (int i = 0; i < length; i++) { URL result = findResource(resName, i); if(result != null) { @@ -294,7 +294,7 @@ public class PathClassLoader extends ClassLoader { } return new EnumerateListArray<URL>(results); } - + private URL findResource(String name, int i) { File pathFile = mFiles[i]; ZipFile zip = mZips[i]; @@ -317,7 +317,7 @@ public class PathClassLoader extends ClassLoader { if (dataFile.exists()) { //System.out.println(" found resource " + name); try { - // Same as archive case regarding URL construction. + // Same as archive case regarding URL construction. return dataFile.toURL(); } catch (MalformedURLException e) { @@ -414,7 +414,7 @@ public class PathClassLoader extends ClassLoader { /** * Finds a native library. This method is called after the parent * ClassLoader has failed to find a native library of the same name. - * + * * @param libname * The name of the library to find * @return the complete path of the library, or {@code null} if the library @@ -451,7 +451,7 @@ public class PathClassLoader extends ClassLoader { * scattered across different JAR files being loaded by different * ClassLoaders. Rather unlikely, and given that this whole thing is more or * less a workaround, probably not worth the effort. - * + * * @param name * the name of the class * @return the package information for the class, or {@code null} if there @@ -462,15 +462,15 @@ public class PathClassLoader extends ClassLoader { if (name != null && !"".equals(name)) { synchronized(this) { Package pack = super.getPackage(name); - + if (pack == null) { pack = definePackage(name, "Unknown", "0.0", "Unknown", "Unknown", "0.0", "Unknown", null); } - + return pack; - } + } } - + return null; } @@ -495,4 +495,8 @@ public class PathClassLoader extends ClassLoader { return (T) mList.get(i++); } }; + + public String toString () { + return getClass().getName() + "[" + path + "]"; + } } diff --git a/dom/src/test/java/org/w3c/domts/EventMonitor.java b/dom/src/test/java/org/w3c/domts/EventMonitor.java deleted file mode 100644 index 909ca4e..0000000 --- a/dom/src/test/java/org/w3c/domts/EventMonitor.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (c) 2001-2004 World Wide Web Consortium, - * (Massachusetts Institute of Technology, Institut National de - * Recherche en Informatique et en Automatique, Keio University). All - * Rights Reserved. This program is distributed under the W3C's Software - * Intellectual Property License. This program is distributed in the - * hope that it will be useful, but WITHOUT ANY WARRANTY; without even - * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. - * See W3C License http://www.w3.org/Consortium/Legal/ for more details. - */ - -package org.w3c.domts; - -import java.util.ArrayList; -import java.util.List; - -import org.w3c.dom.events.Event; -import org.w3c.dom.events.EventListener; - -/** - * This is a utility implementation of EventListener - * that captures all events and provides access - * to lists of all events by mode - */ -public class EventMonitor - implements EventListener { - private final List atEvents = new ArrayList(); - private final List bubbledEvents = new ArrayList(); - private final List capturedEvents = new ArrayList(); - private final List allEvents = new ArrayList(); - - public EventMonitor() { - } - - public void handleEvent(Event evt) { - switch (evt.getEventPhase()) { - case Event.CAPTURING_PHASE: - capturedEvents.add(evt); - break; - - case Event.BUBBLING_PHASE: - bubbledEvents.add(evt); - break; - - case Event.AT_TARGET: - atEvents.add(evt); - break; - } - allEvents.add(evt); - } - - public List getAllEvents() { - return new ArrayList(allEvents); - } - - public List getBubbledEvents() { - return new ArrayList(bubbledEvents); - } - - public List getAtEvents() { - return new ArrayList(atEvents); - } - - public List getCapturedEvents() { - return new ArrayList(capturedEvents); - } -} diff --git a/include/UniquePtr.h b/include/UniquePtr.h new file mode 100644 index 0000000..f5c7c2c --- /dev/null +++ b/include/UniquePtr.h @@ -0,0 +1,231 @@ +/* + * 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. + */ + +#ifndef UNIQUE_PTR_H_included +#define UNIQUE_PTR_H_included + +#include <cstdlib> // For NULL. + +// Default deleter for pointer types. +template <typename T> +struct DefaultDelete { + enum { type_must_be_complete = sizeof(T) }; + DefaultDelete() {} + void operator()(T* p) const { + delete p; + } +}; + +// Default deleter for array types. +template <typename T> +struct DefaultDelete<T[]> { + enum { type_must_be_complete = sizeof(T) }; + void operator()(T* p) const { + delete[] p; + } +}; + +// A smart pointer that deletes the given pointer on destruction. +// Equivalent to C++0x's std::unique_ptr (a combination of boost::scoped_ptr +// and boost::scoped_array). +// Named to be in keeping with Android style but also to avoid +// collision with any other implementation, until we can switch over +// to unique_ptr. +// Use thus: +// UniquePtr<C> c(new C); +template <typename T, typename D = DefaultDelete<T> > +class UniquePtr { +public: + // Construct a new UniquePtr, taking ownership of the given raw pointer. + explicit UniquePtr(T* ptr = NULL) : mPtr(ptr) { + } + + ~UniquePtr() { + reset(); + } + + // Accessors. + T& operator*() const { return *mPtr; } + T* operator->() const { return mPtr; } + T* get() const { return mPtr; } + + // Returns the raw pointer and hands over ownership to the caller. + // The pointer will not be deleted by UniquePtr. + T* release() { + T* result = mPtr; + mPtr = NULL; + return result; + } + + // Takes ownership of the given raw pointer. + // If this smart pointer previously owned a different raw pointer, that + // raw pointer will be freed. + void reset(T* ptr = NULL) { + if (ptr != mPtr) { + D()(mPtr); + mPtr = ptr; + } + } + +private: + // The raw pointer. + T* mPtr; + + // Comparing unique pointers is probably a mistake, since they're unique. + template <typename T2> bool operator==(const UniquePtr<T2>& p) const; + template <typename T2> bool operator!=(const UniquePtr<T2>& p) const; + + // Disallow copy and assignment. + UniquePtr(const UniquePtr&); + void operator=(const UniquePtr&); +}; + +// Partial specialization for array types. Like std::unique_ptr, this removes +// operator* and operator-> but adds operator[]. +template <typename T, typename D> +class UniquePtr<T[], D> { +public: + explicit UniquePtr(T* ptr = NULL) : mPtr(ptr) { + } + + ~UniquePtr() { + reset(); + } + + T& operator[](size_t i) const { + return mPtr[i]; + } + T* get() const { return mPtr; } + + T* release() { + T* result = mPtr; + mPtr = NULL; + return result; + } + + void reset(T* ptr = NULL) { + if (ptr != mPtr) { + D()(mPtr); + mPtr = ptr; + } + } + +private: + T* mPtr; + + // Disallow copy and assignment. + UniquePtr(const UniquePtr&); + void operator=(const UniquePtr&); +}; + +#if UNIQUE_PTR_TESTS + +// Run these tests with: +// g++ -g -DUNIQUE_PTR_TESTS -x c++ UniquePtr.h && ./a.out + +#include <stdio.h> + +static void assert(bool b) { + if (!b) { + fprintf(stderr, "FAIL\n"); + abort(); + } + fprintf(stderr, "OK\n"); +} +static int cCount = 0; +struct C { + C() { ++cCount; } + ~C() { --cCount; } +}; +static bool freed = false; +struct Freer { + void operator()(int* p) { + assert(*p == 123); + free(p); + freed = true; + } +}; + +int main(int argc, char* argv[]) { + // + // UniquePtr<T> tests... + // + + // Can we free a single object? + { + UniquePtr<C> c(new C); + assert(cCount == 1); + } + assert(cCount == 0); + // Does release work? + C* rawC; + { + UniquePtr<C> c(new C); + assert(cCount == 1); + rawC = c.release(); + } + assert(cCount == 1); + delete rawC; + // Does reset work? + { + UniquePtr<C> c(new C); + assert(cCount == 1); + c.reset(new C); + assert(cCount == 1); + } + assert(cCount == 0); + + // + // UniquePtr<T[]> tests... + // + + // Can we free an array? + { + UniquePtr<C[]> cs(new C[4]); + assert(cCount == 4); + } + assert(cCount == 0); + // Does release work? + { + UniquePtr<C[]> c(new C[4]); + assert(cCount == 4); + rawC = c.release(); + } + assert(cCount == 4); + delete[] rawC; + // Does reset work? + { + UniquePtr<C[]> c(new C[4]); + assert(cCount == 4); + c.reset(new C[2]); + assert(cCount == 2); + } + assert(cCount == 0); + + // + // Custom deleter tests... + // + assert(!freed); + { + UniquePtr<int, Freer> i(reinterpret_cast<int*>(malloc(sizeof(int)))); + *i = 123; + } + assert(freed); + return 0; +} +#endif + +#endif // UNIQUE_PTR_H_included diff --git a/json/src/test/java/org/json/AllTests.java b/json/src/test/java/org/json/AllTests.java new file mode 100644 index 0000000..8261a4d --- /dev/null +++ b/json/src/test/java/org/json/AllTests.java @@ -0,0 +1,30 @@ +/* + * 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 org.json; + +import junit.framework.Test; +import junit.framework.TestSuite; + +public class AllTests { + public static Test suite() { + TestSuite suite = tests.TestSuiteFactory.createTestSuite(); + suite.addTestSuite(JSONArrayTest.class); + suite.addTestSuite(JSONStringerTest.class); + suite.addTestSuite(JSONStringerTest.class); + return suite; + } +} diff --git a/json/src/test/java/org/json/JSONArrayTest.java b/json/src/test/java/org/json/JSONArrayTest.java new file mode 100644 index 0000000..34e5ff6 --- /dev/null +++ b/json/src/test/java/org/json/JSONArrayTest.java @@ -0,0 +1,339 @@ +/** + * 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 org.json; + +import junit.framework.TestCase; + +import java.util.Arrays; + +/** + * This black box test was written without inspecting the non-free org.json sourcecode. + */ +public class JSONArrayTest extends TestCase { + + public void testEmptyArray() throws JSONException { + JSONArray array = new JSONArray(); + assertEquals(0, array.length()); + assertEquals("", array.join(" AND ")); + try { + array.get(0); + fail(); + } catch (JSONException e) { + } + try { + array.getBoolean(0); + fail(); + } catch (JSONException e) { + } + + assertEquals("[]", array.toString()); + assertEquals("[]", array.toString(4)); + + // out of bounds is co-opted with defaulting + assertTrue(array.isNull(0)); + assertNull(array.opt(0)); + assertFalse(array.optBoolean(0)); + assertTrue(array.optBoolean(0, true)); + + // bogus (but documented) behaviour: returns null rather than an empty object + assertNull(array.toJSONObject(new JSONArray())); + } + + public void testEqualsAndHashCode() throws JSONException { + JSONArray a = new JSONArray(); + JSONArray b = new JSONArray(); + assertTrue(a.equals(b)); + // bogus behavior: JSONArray overrides equals() but not hashCode(). + assertEquals(a.hashCode(), b.hashCode()); + + a.put(true); + a.put(false); + b.put(true); + b.put(false); + assertTrue(a.equals(b)); + assertEquals(a.hashCode(), b.hashCode()); + + b.put(true); + assertFalse(a.equals(b)); + assertTrue(a.hashCode() != b.hashCode()); + } + + public void testBooleans() throws JSONException { + JSONArray array = new JSONArray(); + array.put(true); + array.put(false); + array.put(2, false); + array.put(3, false); + array.put(2, true); + assertEquals("[true,false,true,false]", array.toString()); + assertEquals(4, array.length()); + assertEquals(Boolean.TRUE, array.get(0)); + assertEquals(Boolean.FALSE, array.get(1)); + assertEquals(Boolean.TRUE, array.get(2)); + assertEquals(Boolean.FALSE, array.get(3)); + assertFalse(array.isNull(0)); + assertFalse(array.isNull(1)); + assertFalse(array.isNull(2)); + assertFalse(array.isNull(3)); + assertEquals(true, array.optBoolean(0)); + assertEquals(false, array.optBoolean(1, true)); + assertEquals(true, array.optBoolean(2, false)); + assertEquals(false, array.optBoolean(3)); + assertEquals("true", array.getString(0)); + assertEquals("false", array.getString(1)); + assertEquals("true", array.optString(2)); + assertEquals("false", array.optString(3, "x")); + assertEquals("[\n true,\n false,\n true,\n false\n]", array.toString(5)); + + JSONArray other = new JSONArray(); + other.put(true); + other.put(false); + other.put(true); + other.put(false); + assertTrue(array.equals(other)); + other.put(true); + assertFalse(array.equals(other)); + + other = new JSONArray(); + other.put("true"); + other.put("false"); + other.put("truE"); + other.put("FALSE"); + assertFalse(array.equals(other)); + assertFalse(other.equals(array)); + assertEquals(true, other.getBoolean(0)); + assertEquals(false, other.optBoolean(1, true)); + assertEquals(true, other.optBoolean(2)); + assertEquals(false, other.getBoolean(3)); + } + + public void testNulls() throws JSONException { + JSONArray array = new JSONArray(); + array.put(3, null); + array.put(0, JSONObject.NULL); + assertEquals(4, array.length()); + assertEquals("[null,null,null,null]", array.toString()); + + // bogus behaviour: there's 2 ways to represent null; each behaves differently! + assertEquals(JSONObject.NULL, array.get(0)); + try { + assertEquals(null, array.get(1)); + fail(); + } catch (JSONException e) { + } + try { + assertEquals(null, array.get(2)); + fail(); + } catch (JSONException e) { + } + try { + assertEquals(null, array.get(3)); + fail(); + } catch (JSONException e) { + } + assertEquals(JSONObject.NULL, array.opt(0)); + assertEquals(null, array.opt(1)); + assertEquals(null, array.opt(2)); + assertEquals(null, array.opt(3)); + assertTrue(array.isNull(0)); + assertTrue(array.isNull(1)); + assertTrue(array.isNull(2)); + assertTrue(array.isNull(3)); + assertEquals("null", array.optString(0)); + assertEquals("", array.optString(1)); + assertEquals("", array.optString(2)); + assertEquals("", array.optString(3)); + } + + public void testNumbers() throws JSONException { + JSONArray array = new JSONArray(); + array.put(Double.MIN_VALUE); + array.put(9223372036854775806L); + array.put(Double.MAX_VALUE); + array.put(-0d); + assertEquals(4, array.length()); + + // bogus behaviour: toString() and getString(int) return different values for -0d + assertEquals("[4.9E-324,9223372036854775806,1.7976931348623157E308,-0]", array.toString()); + + assertEquals(Double.MIN_VALUE, array.get(0)); + assertEquals(9223372036854775806L, array.get(1)); + assertEquals(Double.MAX_VALUE, array.get(2)); + assertEquals(-0d, array.get(3)); + assertEquals(Double.MIN_VALUE, array.getDouble(0)); + assertEquals(9.223372036854776E18, array.getDouble(1)); + assertEquals(Double.MAX_VALUE, array.getDouble(2)); + assertEquals(-0d, array.getDouble(3)); + assertEquals(0, array.getLong(0)); + assertEquals(9223372036854775806L, array.getLong(1)); + assertEquals(Long.MAX_VALUE, array.getLong(2)); + assertEquals(0, array.getLong(3)); + assertEquals(0, array.getInt(0)); + assertEquals(-2, array.getInt(1)); + assertEquals(Integer.MAX_VALUE, array.getInt(2)); + assertEquals(0, array.getInt(3)); + assertEquals(Double.MIN_VALUE, array.opt(0)); + assertEquals(Double.MIN_VALUE, array.optDouble(0)); + assertEquals(0, array.optLong(0, 1L)); + assertEquals(0, array.optInt(0, 1)); + assertEquals("4.9E-324", array.getString(0)); + assertEquals("9223372036854775806", array.getString(1)); + assertEquals("1.7976931348623157E308", array.getString(2)); + assertEquals("-0.0", array.getString(3)); + + JSONArray other = new JSONArray(); + other.put(Double.MIN_VALUE); + other.put(9223372036854775806L); + other.put(Double.MAX_VALUE); + other.put(-0d); + assertTrue(array.equals(other)); + other.put(0, 0L); + assertFalse(array.equals(other)); + } + + public void testStrings() throws JSONException { + JSONArray array = new JSONArray(); + array.put("true"); + array.put("5.5"); + array.put("9223372036854775806"); + array.put("null"); + array.put("5\"8' tall"); + assertEquals(5, array.length()); + assertEquals("[\"true\",\"5.5\",\"9223372036854775806\",\"null\",\"5\\\"8' tall\"]", + array.toString()); + + // although the documentation doesn't mention it, join() escapes text and wraps + // strings in quotes + assertEquals("\"true\" \"5.5\" \"9223372036854775806\" \"null\" \"5\\\"8' tall\"", + array.join(" ")); + + assertEquals("true", array.get(0)); + assertEquals("null", array.getString(3)); + assertEquals("5\"8' tall", array.getString(4)); + assertEquals("true", array.opt(0)); + assertEquals("5.5", array.optString(1)); + assertEquals("9223372036854775806", array.optString(2, null)); + assertEquals("null", array.optString(3, "-1")); + assertFalse(array.isNull(0)); + assertFalse(array.isNull(3)); + + assertEquals(true, array.getBoolean(0)); + assertEquals(true, array.optBoolean(0)); + assertEquals(true, array.optBoolean(0, false)); + assertEquals(0, array.optInt(0)); + assertEquals(-2, array.optInt(0, -2)); + + assertEquals(5.5d, array.getDouble(1)); + assertEquals(5, array.getLong(1)); + assertEquals(5, array.getInt(1)); + assertEquals(5, array.optInt(1, 3)); + + // The last digit of the string is a 6 but getLong returns a 7. It's probably parsing as a + // double and then converting that to a long. This is consistent with JavaScript. + assertEquals(9223372036854775807L, array.getLong(2)); + assertEquals(9.223372036854776E18, array.getDouble(2)); + assertEquals(Integer.MAX_VALUE, array.getInt(2)); + + assertFalse(array.isNull(3)); + try { + array.getDouble(3); + fail(); + } catch (JSONException e) { + } + assertEquals(Double.NaN, array.optDouble(3)); + assertEquals(-1.0d, array.optDouble(3, -1.0d)); + } + + public void testToJSONObject() throws JSONException { + JSONArray keys = new JSONArray(); + keys.put("a"); + keys.put("b"); + + JSONArray values = new JSONArray(); + values.put(5.5d); + values.put(false); + + JSONObject object = values.toJSONObject(keys); + assertEquals(5.5d, object.get("a")); + assertEquals(false, object.get("b")); + + keys.put(0, "a"); + values.put(0, 11.0d); + assertEquals(5.5d, object.get("a")); + } + + public void testToJSONObjectWithNulls() throws JSONException { + JSONArray keys = new JSONArray(); + keys.put("a"); + keys.put("b"); + + JSONArray values = new JSONArray(); + values.put(5.5d); + values.put(null); + + // bogus behaviour: null values are stripped + JSONObject object = values.toJSONObject(keys); + assertEquals(1, object.length()); + assertFalse(object.has("b")); + assertEquals("{\"a\":5.5}", object.toString()); + } + + public void testPutUnsupportedNumbers() throws JSONException { + JSONArray array = new JSONArray(); + + try { + array.put(Double.NaN); + fail(); + } catch (JSONException e) { + } + try { + array.put(0, Double.NEGATIVE_INFINITY); + fail(); + } catch (JSONException e) { + } + try { + array.put(0, Double.POSITIVE_INFINITY); + fail(); + } catch (JSONException e) { + } + } + + public void testCreateWithUnsupportedNumbers() throws JSONException { + JSONArray array = new JSONArray(Arrays.asList(5.5, Double.NaN)); + assertEquals(2, array.length()); + assertEquals(5.5, array.getDouble(0)); + assertEquals(Double.NaN, array.getDouble(1)); + } + + public void testToStringWithUnsupportedNumbers() throws JSONException { + // bogus behaviour: when the array contains an unsupported number, toString returns null + JSONArray array = new JSONArray(Arrays.asList(5.5, Double.NaN)); + assertNull(array.toString()); + } + + public void testCreate() throws JSONException { + JSONArray array = new JSONArray(Arrays.asList(5.5, true)); + assertEquals(2, array.length()); + assertEquals(5.5, array.getDouble(0)); + assertEquals(true, array.get(1)); + assertEquals("[5.5,true]", array.toString()); + } + + public void testParsingConstructor() { + fail("TODO"); + } +} diff --git a/json/src/test/java/org/json/JSONStringerTest.java b/json/src/test/java/org/json/JSONStringerTest.java new file mode 100644 index 0000000..a30df9e --- /dev/null +++ b/json/src/test/java/org/json/JSONStringerTest.java @@ -0,0 +1,356 @@ +/** + * 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 org.json; + +import junit.framework.TestCase; + +/** + * This black box test was written without inspecting the non-free org.json sourcecode. + */ +public class JSONStringerTest extends TestCase { + + public void testEmptyStringer() { + // bogus behaviour: why isn't this the empty string? + assertNull(new JSONStringer().toString()); + } + + public void testValueJSONNull() throws JSONException { + JSONStringer stringer = new JSONStringer(); + stringer.array(); + stringer.value(JSONObject.NULL); + stringer.endArray(); + assertEquals("[null]", stringer.toString()); + } + + public void testEmptyObject() throws JSONException { + JSONStringer stringer = new JSONStringer(); + stringer.object(); + stringer.endObject(); + assertEquals("{}", stringer.toString()); + } + + public void testEmptyArray() throws JSONException { + JSONStringer stringer = new JSONStringer(); + stringer.array(); + stringer.endArray(); + assertEquals("[]", stringer.toString()); + } + + public void testArray() throws JSONException { + JSONStringer stringer = new JSONStringer(); + stringer.array(); + stringer.value(false); + stringer.value(5.0); + stringer.value(5L); + stringer.value("five"); + stringer.value(null); + stringer.endArray(); + assertEquals("[false,5,5,\"five\",null]", stringer.toString()); + } + + public void testKeyValue() throws JSONException { + JSONStringer stringer = new JSONStringer(); + stringer.object(); + stringer.key("a").value(false); + stringer.key("b").value(5.0); + stringer.key("c").value(5L); + stringer.key("d").value("five"); + stringer.key("e").value(null); + stringer.endObject(); + assertEquals("{\"a\":false," + + "\"b\":5," + + "\"c\":5," + + "\"d\":\"five\"," + + "\"e\":null}", stringer.toString()); + } + + /** + * Test what happens when extreme values are emitted. Such values are likely + * to be rounded during parsing. + */ + public void testNumericRepresentations() throws JSONException { + JSONStringer stringer = new JSONStringer(); + stringer.array(); + stringer.value(Long.MAX_VALUE); + stringer.value(Double.MIN_VALUE); + stringer.endArray(); + assertEquals("[9223372036854775807,4.9E-324]", stringer.toString()); + } + + public void testWeirdNumbers() throws JSONException { + try { + new JSONStringer().array().value(Double.NaN); + fail(); + } catch (JSONException e) { + } + try { + new JSONStringer().array().value(Double.NEGATIVE_INFINITY); + fail(); + } catch (JSONException e) { + } + try { + new JSONStringer().array().value(Double.POSITIVE_INFINITY); + fail(); + } catch (JSONException e) { + } + + JSONStringer stringer = new JSONStringer(); + stringer.array(); + stringer.value(-0.0d); + stringer.value(0.0d); + stringer.endArray(); + assertEquals("[-0,0]", stringer.toString()); + } + + public void testMismatchedScopes() { + try { + new JSONStringer().key("a"); + fail(); + } catch (JSONException e) { + } + try { + new JSONStringer().value("a"); + fail(); + } catch (JSONException e) { + } + try { + new JSONStringer().endObject(); + fail(); + } catch (JSONException e) { + } + try { + new JSONStringer().endArray(); + fail(); + } catch (JSONException e) { + } + try { + new JSONStringer().array().endObject(); + fail(); + } catch (JSONException e) { + } + try { + new JSONStringer().object().endArray(); + fail(); + } catch (JSONException e) { + } + try { + new JSONStringer().object().key("a").key("a"); + fail(); + } catch (JSONException e) { + } + try { + new JSONStringer().object().value(false); + fail(); + } catch (JSONException e) { + } + } + + public void testNullKey() { + try { + new JSONStringer().object().key(null); + fail(); + } catch (JSONException e) { + } + } + + public void testRepeatedKey() throws JSONException { + JSONStringer stringer = new JSONStringer(); + stringer.object(); + stringer.key("a").value(true); + stringer.key("a").value(false); + stringer.endObject(); + // JSONStringer doesn't attempt to detect duplicates + assertEquals("{\"a\":true,\"a\":false}", stringer.toString()); + } + + public void testEmptyKey() throws JSONException { + JSONStringer stringer = new JSONStringer(); + stringer.object(); + stringer.key("").value(false); + stringer.endObject(); + assertEquals("{\"\":false}", stringer.toString()); // legit behaviour! + } + + public void testEscaping() throws JSONException { + assertEscapedAllWays("a", "a"); + assertEscapedAllWays("a\"", "a\\\""); + assertEscapedAllWays("\"", "\\\""); + assertEscapedAllWays(":", ":"); + assertEscapedAllWays(",", ","); + assertEscapedAllWays("\n", "\\n"); + assertEscapedAllWays("\t", "\\t"); + assertEscapedAllWays(" ", " "); + assertEscapedAllWays("\\", "\\\\"); + assertEscapedAllWays("{", "{"); + assertEscapedAllWays("}", "}"); + assertEscapedAllWays("[", "["); + assertEscapedAllWays("]", "]"); + + // how does it decide which characters to escape? + assertEscapedAllWays("\0", "\\u0000"); + assertEscapedAllWays("\u0019", "\\u0019"); + assertEscapedAllWays("\u0020", " "); + } + + private void assertEscapedAllWays(String original, String escaped) throws JSONException { + assertEquals("{\"" + escaped + "\":false}", + new JSONStringer().object().key(original).value(false).endObject().toString()); + assertEquals("{\"a\":\"" + escaped + "\"}", + new JSONStringer().object().key("a").value(original).endObject().toString()); + assertEquals("[\"" + escaped + "\"]", + new JSONStringer().array().value(original).endArray().toString()); + } + + public void testJSONArrayAsValue() throws JSONException { + JSONArray array = new JSONArray(); + array.put(false); + JSONStringer stringer = new JSONStringer(); + stringer.array(); + stringer.value(array); + stringer.endArray(); + assertEquals("[[false]]", stringer.toString()); + } + + public void testJSONObjectAsValue() throws JSONException { + JSONObject object = new JSONObject(); + object.put("a", false); + JSONStringer stringer = new JSONStringer(); + stringer.object(); + stringer.key("b").value(object); + stringer.endObject(); + assertEquals("{\"b\":{\"a\":false}}", stringer.toString()); + } + + public void testArrayNestingMaxDepthIs20() throws JSONException { + JSONStringer stringer = new JSONStringer(); + for (int i = 0; i < 20; i++) { + stringer.array(); + } + for (int i = 0; i < 20; i++) { + stringer.endArray(); + } + assertEquals("[[[[[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]]]]", stringer.toString()); + + stringer = new JSONStringer(); + for (int i = 0; i < 20; i++) { + stringer.array(); + } + try { + stringer.array(); + fail(); + } catch (JSONException e) { + } + } + + public void testObjectNestingMaxDepthIs20() throws JSONException { + JSONStringer stringer = new JSONStringer(); + for (int i = 0; i < 20; i++) { + stringer.object(); + stringer.key("a"); + } + stringer.value(false); + for (int i = 0; i < 20; i++) { + stringer.endObject(); + } + assertEquals("{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":" + + "{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":false" + + "}}}}}}}}}}}}}}}}}}}}", stringer.toString()); + + stringer = new JSONStringer(); + for (int i = 0; i < 20; i++) { + stringer.object(); + stringer.key("a"); + } + try { + stringer.object(); + fail(); + } catch (JSONException e) { + } + } + + public void testMixedMaxDepth() throws JSONException { + JSONStringer stringer = new JSONStringer(); + for (int i = 0; i < 20; i+=2) { + stringer.array(); + stringer.object(); + stringer.key("a"); + } + stringer.value(false); + for (int i = 0; i < 20; i+=2) { + stringer.endObject(); + stringer.endArray(); + } + assertEquals("[{\"a\":[{\"a\":[{\"a\":[{\"a\":[{\"a\":" + + "[{\"a\":[{\"a\":[{\"a\":[{\"a\":[{\"a\":false" + + "}]}]}]}]}]}]}]}]}]}]", stringer.toString()); + + stringer = new JSONStringer(); + for (int i = 0; i < 20; i+=2) { + stringer.array(); + stringer.object(); + stringer.key("a"); + } + try { + stringer.array(); + fail(); + } catch (JSONException e) { + } + } + + public void testMaxDepthWithArrayValue() throws JSONException { + JSONArray array = new JSONArray(); + array.put(false); + + JSONStringer stringer = new JSONStringer(); + for (int i = 0; i < 20; i++) { + stringer.array(); + } + stringer.value(array); + for (int i = 0; i < 20; i++) { + stringer.endArray(); + } + assertEquals("[[[[[[[[[[[[[[[[[[[[[false]]]]]]]]]]]]]]]]]]]]]", stringer.toString()); + } + + public void testMaxDepthWithObjectValue() throws JSONException { + JSONObject object = new JSONObject(); + object.put("a", false); + JSONStringer stringer = new JSONStringer(); + for (int i = 0; i < 20; i++) { + stringer.object(); + stringer.key("b"); + } + stringer.value(object); + for (int i = 0; i < 20; i++) { + stringer.endObject(); + } + assertEquals("{\"b\":{\"b\":{\"b\":{\"b\":{\"b\":{\"b\":{\"b\":{\"b\":{\"b\":{\"b\":" + + "{\"b\":{\"b\":{\"b\":{\"b\":{\"b\":{\"b\":{\"b\":{\"b\":{\"b\":{\"b\":" + + "{\"a\":false}}}}}}}}}}}}}}}}}}}}}", stringer.toString()); + } + + public void testMultipleRoots() throws JSONException { + JSONStringer stringer = new JSONStringer(); + stringer.array(); + stringer.endArray(); + try { + stringer.object(); + fail(); + } catch (JSONException e) { + } + } +} diff --git a/json/src/test/java/org/json/JSONTokenerTest.java b/json/src/test/java/org/json/JSONTokenerTest.java new file mode 100644 index 0000000..1409a3b --- /dev/null +++ b/json/src/test/java/org/json/JSONTokenerTest.java @@ -0,0 +1,565 @@ +/** + * 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 org.json; + +import junit.framework.TestCase; + +/** + * This black box test was written without inspecting the non-free org.json sourcecode. + */ +public class JSONTokenerTest extends TestCase { + + public void testNulls() throws JSONException { + // bogus behaviour: JSONTokener accepts null, only to fail later on almost all APIs. + new JSONTokener(null).back(); + + try { + new JSONTokener(null).more(); + fail(); + } catch (NullPointerException e) { + } + + try { + new JSONTokener(null).next(); + fail(); + } catch (NullPointerException e) { + } + + try { + new JSONTokener(null).next(3); + fail(); + } catch (NullPointerException e) { + } + + try { + new JSONTokener(null).next('A'); + fail(); + } catch (NullPointerException e) { + } + + try { + new JSONTokener(null).nextClean(); + fail(); + } catch (NullPointerException e) { + } + + try { + new JSONTokener(null).nextString('"'); + fail(); + } catch (NullPointerException e) { + } + + try { + new JSONTokener(null).nextTo('A'); + fail(); + } catch (NullPointerException e) { + } + + try { + new JSONTokener(null).nextTo("ABC"); + fail(); + } catch (NullPointerException e) { + } + + try { + new JSONTokener(null).nextValue(); + fail(); + } catch (NullPointerException e) { + } + + try { + new JSONTokener(null).skipPast("ABC"); + fail(); + } catch (NullPointerException e) { + } + + try { + new JSONTokener(null).skipTo('A'); + fail(); + } catch (NullPointerException e) { + } + + assertEquals("foo! at character 0 of null", + new JSONTokener(null).syntaxError("foo!").getMessage()); + + assertEquals(" at character 0 of null", new JSONTokener(null).toString()); + } + + public void testEmptyString() throws JSONException { + JSONTokener backTokener = new JSONTokener(""); + backTokener.back(); + assertEquals(" at character 0 of ", backTokener.toString()); + assertFalse(new JSONTokener("").more()); + assertEquals('\0', new JSONTokener("").next()); + try { + new JSONTokener("").next(3); + fail(); + } catch (JSONException expected) { + } + try { + new JSONTokener("").next('A'); + fail(); + } catch (JSONException e) { + } + assertEquals('\0', new JSONTokener("").nextClean()); + try { + new JSONTokener("").nextString('"'); + fail(); + } catch (JSONException e) { + } + assertEquals("", new JSONTokener("").nextTo('A')); + assertEquals("", new JSONTokener("").nextTo("ABC")); + try { + new JSONTokener("").nextValue(); + fail(); + } catch (JSONException e) { + } + new JSONTokener("").skipPast("ABC"); + assertEquals('\0', new JSONTokener("").skipTo('A')); + assertEquals("foo! at character 0 of ", + new JSONTokener("").syntaxError("foo!").getMessage()); + assertEquals(" at character 0 of ", new JSONTokener("").toString()); + } + + public void testCharacterNavigation() throws JSONException { + JSONTokener abcdeTokener = new JSONTokener("ABCDE"); + assertEquals('A', abcdeTokener.next()); + assertEquals('B', abcdeTokener.next('B')); + assertEquals("CD", abcdeTokener.next(2)); + try { + abcdeTokener.next(2); + fail(); + } catch (JSONException e) { + } + assertEquals('E', abcdeTokener.nextClean()); + assertEquals('\0', abcdeTokener.next()); + try { + // bogus behaviour: returning an empty string should be valid + abcdeTokener.next(0); + fail(); + } catch (JSONException e) { + } + assertFalse(abcdeTokener.more()); + abcdeTokener.back(); + assertTrue(abcdeTokener.more()); + assertEquals('E', abcdeTokener.next()); + } + + public void testBackNextAndMore() throws JSONException { + JSONTokener abcTokener = new JSONTokener("ABC"); + assertTrue(abcTokener.more()); + abcTokener.next(); + abcTokener.next(); + assertTrue(abcTokener.more()); + abcTokener.next(); + assertFalse(abcTokener.more()); + abcTokener.back(); + assertTrue(abcTokener.more()); + abcTokener.next(); + assertFalse(abcTokener.more()); + abcTokener.back(); + abcTokener.back(); + abcTokener.back(); + abcTokener.back(); // bogus behaviour: you can back up before the beginning of a String + assertEquals('A', abcTokener.next()); + } + + public void testNextMatching() throws JSONException { + JSONTokener abcdTokener = new JSONTokener("ABCD"); + assertEquals('A', abcdTokener.next('A')); + try { + abcdTokener.next('C'); // although it failed, this op consumes a character of input + fail(); + } catch (JSONException e) { + } + assertEquals('C', abcdTokener.next('C')); + assertEquals('D', abcdTokener.next('D')); + try { + abcdTokener.next('E'); + fail(); + } catch (JSONException e) { + } + } + + public void testNextN() throws JSONException { + JSONTokener abcdeTokener = new JSONTokener("ABCDEF"); + assertEquals("", abcdeTokener.next(0)); + try { + abcdeTokener.next(7); + fail(); + } catch (JSONException e) { + } + assertEquals("ABC", abcdeTokener.next(3)); + try { + abcdeTokener.next(4); + fail(); + } catch (JSONException e) { + } + try { + // bogus behaviour: there should be 3 characters left, but there must be an off-by-one + // error in the implementation. + assertEquals("DEF", abcdeTokener.next(3)); + fail(); + } catch (JSONException e) { + } + assertEquals("DE", abcdeTokener.next(2)); + assertEquals('F', abcdeTokener.next()); + try { + // bogus behaviour: returning an empty string should be valid + abcdeTokener.next(0); + fail(); + } catch (JSONException e) { + } + abcdeTokener.back(); + abcdeTokener.back(); + abcdeTokener.back(); + assertEquals("DE", abcdeTokener.next(2)); + assertEquals('F', abcdeTokener.next()); + } + + public void testNextCleanComments() throws JSONException { + JSONTokener tokener = new JSONTokener( + " A /*XX*/B/*XX//XX\n//XX\nXX*/C//X//X//X\nD/*X*///X\n"); + assertEquals('A', tokener.nextClean()); + assertEquals('B', tokener.nextClean()); + assertEquals('C', tokener.nextClean()); + assertEquals('D', tokener.nextClean()); + assertEquals('\0', tokener.nextClean()); + } + + public void testNextCleanTrailingOpenComment() throws JSONException { + try { + new JSONTokener(" /* ").nextClean(); + fail(); + } catch (JSONException e) { + } + assertEquals('\0', new JSONTokener(" // ").nextClean()); + } + + public void testNextCleanNewlineDelimiters() throws JSONException { + assertEquals('B', new JSONTokener(" // \r\n B ").nextClean()); + assertEquals('B', new JSONTokener(" // \n B ").nextClean()); + assertEquals('B', new JSONTokener(" // \r B ").nextClean()); + } + + /** + * Tests which characters tokener treats as ignorable whitespace. See Kevin Bourrillion's + * <a href="https://spreadsheets.google.com/pub?key=pd8dAQyHbdewRsnE5x5GzKQ">list + * of whitespace characters</a>. + */ + public void testNextCleanWhitespace() throws JSONException { + // This behaviour contradicts the JSON spec. It claims the only space + // characters are space, tab, newline and carriage return. But it treats + // many characters like whitespace! These are the same whitespace + // characters used by String.trim(), with the exception of '\0'. + assertEquals("character tabulation", 'A', new JSONTokener("\u0009A").nextClean()); + assertEquals("line feed", 'A', new JSONTokener("\nA").nextClean()); + assertEquals("line tabulation", 'A', new JSONTokener("\u000bA").nextClean()); + assertEquals("form feed", 'A', new JSONTokener("\u000cA").nextClean()); + assertEquals("carriage return", 'A', new JSONTokener("\rA").nextClean()); + assertEquals("information separator 4", 'A', new JSONTokener("\u001cA").nextClean()); + assertEquals("information separator 3", 'A', new JSONTokener("\u001dA").nextClean()); + assertEquals("information separator 2", 'A', new JSONTokener("\u001eA").nextClean()); + assertEquals("information separator 1", 'A', new JSONTokener("\u001fA").nextClean()); + assertEquals("space", 'A', new JSONTokener("\u0020A").nextClean()); + for (char c = '\u0002'; c < ' '; c++) { + assertEquals('A', new JSONTokener(new String(new char[] { ' ', c, 'A' })).nextClean()); + } + + // These characters are neither whitespace in the JSON spec nor the implementation + assertEquals("null", '\u0000', new JSONTokener("\u0000A").nextClean()); + assertEquals("next line", '\u0085', new JSONTokener("\u0085A").nextClean()); + assertEquals("non-breaking space", '\u00a0', new JSONTokener("\u00a0A").nextClean()); + assertEquals("ogham space mark", '\u1680', new JSONTokener("\u1680A").nextClean()); + assertEquals("mongolian vowel separator", '\u180e', new JSONTokener("\u180eA").nextClean()); + assertEquals("en quad", '\u2000', new JSONTokener("\u2000A").nextClean()); + assertEquals("em quad", '\u2001', new JSONTokener("\u2001A").nextClean()); + assertEquals("en space", '\u2002', new JSONTokener("\u2002A").nextClean()); + assertEquals("em space", '\u2003', new JSONTokener("\u2003A").nextClean()); + assertEquals("three-per-em space", '\u2004', new JSONTokener("\u2004A").nextClean()); + assertEquals("four-per-em space", '\u2005', new JSONTokener("\u2005A").nextClean()); + assertEquals("six-per-em space", '\u2006', new JSONTokener("\u2006A").nextClean()); + assertEquals("figure space", '\u2007', new JSONTokener("\u2007A").nextClean()); + assertEquals("punctuation space", '\u2008', new JSONTokener("\u2008A").nextClean()); + assertEquals("thin space", '\u2009', new JSONTokener("\u2009A").nextClean()); + assertEquals("hair space", '\u200a', new JSONTokener("\u200aA").nextClean()); + assertEquals("zero-width space", '\u200b', new JSONTokener("\u200bA").nextClean()); + assertEquals("left-to-right mark", '\u200e', new JSONTokener("\u200eA").nextClean()); + assertEquals("right-to-left mark", '\u200f', new JSONTokener("\u200fA").nextClean()); + assertEquals("line separator", '\u2028', new JSONTokener("\u2028A").nextClean()); + assertEquals("paragraph separator", '\u2029', new JSONTokener("\u2029A").nextClean()); + assertEquals("narrow non-breaking space", '\u202f', new JSONTokener("\u202fA").nextClean()); + assertEquals("medium mathematical space", '\u205f', new JSONTokener("\u205fA").nextClean()); + assertEquals("ideographic space", '\u3000', new JSONTokener("\u3000A").nextClean()); + } + + public void testNextString() throws JSONException { + assertEquals("", new JSONTokener("'").nextString('\'')); + assertEquals("", new JSONTokener("\"").nextString('\"')); + assertEquals("ABC", new JSONTokener("ABC'DEF").nextString('\'')); + assertEquals("ABC", new JSONTokener("ABC'''DEF").nextString('\'')); + + // nextString permits slash-escaping of arbitrary characters! + assertEquals("ABC", new JSONTokener("A\\B\\C'DEF").nextString('\'')); + + JSONTokener tokener = new JSONTokener(" 'abc' 'def' \"ghi\""); + tokener.next(); + assertEquals('\'', tokener.next()); + assertEquals("abc", tokener.nextString('\'')); + tokener.next(); + assertEquals('\'', tokener.next()); + assertEquals("def", tokener.nextString('\'')); + tokener.next(); + assertEquals('"', tokener.next()); + assertEquals("ghi", tokener.nextString('\"')); + assertFalse(tokener.more()); + } + + public void testNextStringNoDelimiter() throws JSONException { + try { + new JSONTokener("").nextString('\''); + fail(); + } catch (JSONException e) { + } + + JSONTokener tokener = new JSONTokener(" 'abc"); + tokener.next(); + tokener.next(); + try { + tokener.next('\''); + fail(); + } catch (JSONException e) { + } + } + + public void testNextStringEscapedQuote() throws JSONException { + try { + new JSONTokener("abc\\").nextString('"'); + fail(); + } catch (JSONException e) { + } + + // we're mixing Java escaping like \" and JavaScript escaping like \\\" + // which makes these tests extra tricky to read! + assertEquals("abc\"def", new JSONTokener("abc\\\"def\"ghi").nextString('"')); + assertEquals("abc\\def", new JSONTokener("abc\\\\def\"ghi").nextString('"')); + assertEquals("abc/def", new JSONTokener("abc\\/def\"ghi").nextString('"')); + assertEquals("abc\bdef", new JSONTokener("abc\\bdef\"ghi").nextString('"')); + assertEquals("abc\fdef", new JSONTokener("abc\\fdef\"ghi").nextString('"')); + assertEquals("abc\ndef", new JSONTokener("abc\\ndef\"ghi").nextString('"')); + assertEquals("abc\rdef", new JSONTokener("abc\\rdef\"ghi").nextString('"')); + assertEquals("abc\tdef", new JSONTokener("abc\\tdef\"ghi").nextString('"')); + } + + public void testNextStringUnicodeEscaped() throws JSONException { + // we're mixing Java escaping like \\ and JavaScript escaping like \\u + assertEquals("abc def", new JSONTokener("abc\\u0020def\"ghi").nextString('"')); + assertEquals("abcU0020def", new JSONTokener("abc\\U0020def\"ghi").nextString('"')); + + // JSON requires 4 hex characters after a unicode escape + try { + new JSONTokener("abc\\u002\"").nextString('"'); + fail(); + } catch (JSONException e) { + } + try { + new JSONTokener("abc\\u").nextString('"'); + fail(); + } catch (JSONException e) { + } + try { + new JSONTokener("abc\\u \"").nextString('"'); + fail(); + } catch (NumberFormatException e) { + } + assertEquals("abc\"def", new JSONTokener("abc\\u0022def\"ghi").nextString('"')); + try { + new JSONTokener("abc\\u000G\"").nextString('"'); + fail(); + } catch (NumberFormatException e) { + } + } + + public void testNextStringNonQuote() throws JSONException { + assertEquals("AB", new JSONTokener("ABC").nextString('C')); + assertEquals("ABCD", new JSONTokener("AB\\CDC").nextString('C')); + assertEquals("AB\nC", new JSONTokener("AB\\nCn").nextString('n')); + } + + public void testNextTo() throws JSONException { + assertEquals("ABC", new JSONTokener("ABCDEFG").nextTo("DHI")); + assertEquals("ABCDEF", new JSONTokener("ABCDEF").nextTo("")); + + JSONTokener tokener = new JSONTokener("ABC\rDEF\nGHI\r\nJKL"); + assertEquals("ABC", tokener.nextTo("M")); + assertEquals('\r', tokener.next()); + assertEquals("DEF", tokener.nextTo("M")); + assertEquals('\n', tokener.next()); + assertEquals("GHI", tokener.nextTo("M")); + assertEquals('\r', tokener.next()); + assertEquals('\n', tokener.next()); + assertEquals("JKL", tokener.nextTo("M")); + + tokener = new JSONTokener("ABCDEFGHI"); + assertEquals("ABC", tokener.nextTo("DEF")); + assertEquals("", tokener.nextTo("DEF")); + assertEquals('D', tokener.next()); + assertEquals("", tokener.nextTo("DEF")); + assertEquals('E', tokener.next()); + assertEquals("", tokener.nextTo("DEF")); + assertEquals('F', tokener.next()); + assertEquals("GHI", tokener.nextTo("DEF")); + assertEquals("", tokener.nextTo("DEF")); + + tokener = new JSONTokener(" \t \fABC \t DEF"); + assertEquals("ABC", tokener.nextTo("DEF")); + assertEquals('D', tokener.next()); + + tokener = new JSONTokener(" \t \fABC \n DEF"); + assertEquals("ABC", tokener.nextTo("\n")); + assertEquals("", tokener.nextTo("\n")); + + // Bogus behaviour: the tokener stops after \0 always + tokener = new JSONTokener(" \0\t \fABC \n DEF"); + assertEquals("", tokener.nextTo("D")); + assertEquals('\t', tokener.next()); + assertEquals("ABC", tokener.nextTo("D")); + tokener = new JSONTokener("ABC\0DEF"); + assertEquals("ABC", tokener.nextTo("\0")); + assertEquals("DEF", tokener.nextTo("\0")); + + tokener = new JSONTokener(""); + try { + tokener.nextTo(null); + fail(); + } catch (NullPointerException e) { + } + } + + public void testSkipPast() { + JSONTokener tokener = new JSONTokener("ABCDEF"); + tokener.skipPast("ABC"); + assertEquals('D', tokener.next()); + tokener.skipPast("EF"); + assertEquals('\0', tokener.next()); + + tokener = new JSONTokener("ABCDEF"); + tokener.skipPast("ABCDEF"); + assertEquals('\0', tokener.next()); + + tokener = new JSONTokener("ABCDEF"); + tokener.skipPast("G"); + assertEquals('\0', tokener.next()); + + tokener = new JSONTokener("ABC\0ABC"); + tokener.skipPast("ABC"); + assertEquals('\0', tokener.next()); + assertEquals('A', tokener.next()); + + tokener = new JSONTokener("\0ABC"); + tokener.skipPast("ABC"); + assertEquals('\0', tokener.next()); + + tokener = new JSONTokener("ABC\nDEF"); + tokener.skipPast("DEF"); + assertEquals('\0', tokener.next()); + + tokener = new JSONTokener("ABC"); + tokener.skipPast("ABCDEF"); + assertEquals('\0', tokener.next()); + + tokener = new JSONTokener("ABCDABCDABCD"); + tokener.skipPast("ABC"); + assertEquals('D', tokener.next()); + tokener.skipPast("ABC"); + assertEquals('D', tokener.next()); + tokener.skipPast("ABC"); + assertEquals('D', tokener.next()); + + tokener = new JSONTokener(""); + try { + tokener.skipPast(null); + fail(); + } catch (NullPointerException e) { + } + } + + public void testSkipTo() { + JSONTokener tokener = new JSONTokener("ABCDEF"); + tokener.skipTo('A'); + assertEquals('A', tokener.next()); + tokener.skipTo('D'); + assertEquals('D', tokener.next()); + tokener.skipTo('G'); + assertEquals('E', tokener.next()); + tokener.skipTo('A'); + assertEquals('F', tokener.next()); + + tokener = new JSONTokener("ABC\0DEF"); + tokener.skipTo('F'); + // bogus behaviour: skipTo gives up when it sees '\0' + assertEquals('A', tokener.next()); + + tokener = new JSONTokener("ABC\nDEF"); + tokener.skipTo('F'); + assertEquals('F', tokener.next()); + + tokener = new JSONTokener("ABCfDEF"); + tokener.skipTo('F'); + assertEquals('F', tokener.next()); + + tokener = new JSONTokener("ABC/* DEF */"); + tokener.skipTo('D'); + assertEquals('D', tokener.next()); + } + + public void testDehexchar() { + assertEquals( 0, JSONTokener.dehexchar('0')); + assertEquals( 1, JSONTokener.dehexchar('1')); + assertEquals( 2, JSONTokener.dehexchar('2')); + assertEquals( 3, JSONTokener.dehexchar('3')); + assertEquals( 4, JSONTokener.dehexchar('4')); + assertEquals( 5, JSONTokener.dehexchar('5')); + assertEquals( 6, JSONTokener.dehexchar('6')); + assertEquals( 7, JSONTokener.dehexchar('7')); + assertEquals( 8, JSONTokener.dehexchar('8')); + assertEquals( 9, JSONTokener.dehexchar('9')); + assertEquals(10, JSONTokener.dehexchar('A')); + assertEquals(11, JSONTokener.dehexchar('B')); + assertEquals(12, JSONTokener.dehexchar('C')); + assertEquals(13, JSONTokener.dehexchar('D')); + assertEquals(14, JSONTokener.dehexchar('E')); + assertEquals(15, JSONTokener.dehexchar('F')); + assertEquals(10, JSONTokener.dehexchar('a')); + assertEquals(11, JSONTokener.dehexchar('b')); + assertEquals(12, JSONTokener.dehexchar('c')); + assertEquals(13, JSONTokener.dehexchar('d')); + assertEquals(14, JSONTokener.dehexchar('e')); + assertEquals(15, JSONTokener.dehexchar('f')); + + for (int c = 0; c <= 0xFFFF; c++) { + if ((c >= '0' && c <= '9') || (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f')) { + continue; + } + assertEquals("dehexchar " + c, -1, JSONTokener.dehexchar((char) c)); + } + } + + public void testNextValue() { + fail("TODO"); + } +} diff --git a/luni/src/test/java/tests/AllTests.java b/luni/src/test/java/tests/AllTests.java index 0eab77e..3110026 100644 --- a/luni/src/test/java/tests/AllTests.java +++ b/luni/src/test/java/tests/AllTests.java @@ -66,6 +66,7 @@ public class AllTests suite.addTest(java.util.AllTests.suite()); suite.addTest(javax.xml.parsers.AllTests.suite()); suite.addTest(org.apache.harmony.luni.platform.AllTests.suite()); + suite.addTest(org.json.AllTests.suite()); suite.addTest(tests.api.org.apache.harmony.kernel.dalvik.AllTests.suite()); return suite; diff --git a/math/src/main/java/java/math/BigInt.java b/math/src/main/java/java/math/BigInt.java index 4d1e18e..6a1cb97 100644 --- a/math/src/main/java/java/math/BigInt.java +++ b/math/src/main/java/java/math/BigInt.java @@ -82,7 +82,6 @@ class BigInt public static int consumeErrors(StringBuilder sb) { int cnt = 0; int e, reason; - boolean first = true; while ((e = NativeBN.ERR_get_error()) != 0) { reason = e & 255; if (reason == 103) { @@ -96,7 +95,6 @@ class BigInt if (reason == 65) { throw new OutOfMemoryError(); } - if (!first) { sb.append(" *** "); first = false; } sb.append(e).append(": "); String s = NativeBN.ERR_error_string(e); sb.append(s); diff --git a/security/src/main/files/cacerts.bks b/security/src/main/files/cacerts.bks Binary files differindex 4f6fad4..ca45764 100644 --- a/security/src/main/files/cacerts.bks +++ b/security/src/main/files/cacerts.bks diff --git a/security/src/main/files/cacerts/2afc57aa.0 b/security/src/main/files/cacerts/2afc57aa.0 new file mode 100644 index 0000000..31b8092 --- /dev/null +++ b/security/src/main/files/cacerts/2afc57aa.0 @@ -0,0 +1,88 @@ +Certificate: + Data: + Version: 3 (0x2) + Serial Number: + 2e:6a:00:01:00:02:1f:d7:52:21:2c:11:5c:3b + Signature Algorithm: sha1WithRSAEncryption + Issuer: C=DE, O=TC TrustCenter GmbH, OU=TC TrustCenter Class 2 CA, CN=TC TrustCenter Class 2 CA II + Validity + Not Before: Jan 12 14:38:43 2006 GMT + Not After : Dec 31 22:59:59 2025 GMT + Subject: C=DE, O=TC TrustCenter GmbH, OU=TC TrustCenter Class 2 CA, CN=TC TrustCenter Class 2 CA II + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + RSA Public Key: (2048 bit) + Modulus (2048 bit): + 00:ab:80:87:9b:8e:f0:c3:7c:87:d7:e8:24:82:11: + b3:3c:dd:43:62:ee:f8:c3:45:da:e8:e1:a0:5f:d1: + 2a:b2:ea:93:68:df:b4:c8:d6:43:e9:c4:75:59:7f: + fc:e1:1d:f8:31:70:23:1b:88:9e:27:b9:7b:fd:3a: + d2:c9:a9:e9:14:2f:90:be:03:52:c1:49:cd:f6:fd: + e4:08:66:0b:57:8a:a2:42:a0:b8:d5:7f:69:5c:90: + 32:b2:97:0d:ca:4a:dc:46:3e:02:55:89:53:e3:1a: + 5a:cb:36:c6:07:56:f7:8c:cf:11:f4:4c:bb:30:70: + 04:95:a5:f6:39:8c:fd:73:81:08:7d:89:5e:32:1e: + 22:a9:22:45:4b:b0:66:2e:30:cc:9f:65:fd:fc:cb: + 81:a9:f1:e0:3b:af:a3:86:d1:89:ea:c4:45:79:50: + 5d:ae:e9:21:74:92:4d:8b:59:82:8f:94:e3:e9:4a: + f1:e7:49:b0:14:e3:f5:62:cb:d5:72:bd:1f:b9:d2: + 9f:a0:cd:a8:fa:01:c8:d9:0d:df:da:fc:47:9d:b3: + c8:54:df:49:4a:f1:21:a9:fe:18:4e:ee:48:d4:19: + bb:ef:7d:e4:e2:9d:cb:5b:b6:6e:ff:e3:cd:5a:e7: + 74:82:05:ba:80:25:38:cb:e4:69:9e:af:41:aa:1a: + 84:f5 + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Basic Constraints: critical + CA:TRUE + X509v3 Key Usage: critical + Certificate Sign, CRL Sign + X509v3 Subject Key Identifier: + E3:AB:54:4C:80:A1:DB:56:43:B7:91:4A:CB:F3:82:7A:13:5C:08:AB + X509v3 CRL Distribution Points: + URI:http://www.trustcenter.de/crl/v2/tc_class_2_ca_II.crl + URI:ldap://www.trustcenter.de/CN=TC%20TrustCenter%20Class%202%20CA%20II,O=TC%20TrustCenter%20GmbH,OU=rootcerts,DC=trustcenter,DC=de?certificateRevocationList?base? + + Signature Algorithm: sha1WithRSAEncryption + 8c:d7:df:7e:ee:1b:80:10:b3:83:f5:db:11:ea:6b:4b:a8:92: + 18:d9:f7:07:39:f5:2c:be:06:75:7a:68:53:15:1c:ea:4a:ed: + 5e:fc:23:b2:13:a0:d3:09:ff:f6:f6:2e:6b:41:71:79:cd:e2: + 6d:fd:ae:59:6b:85:1d:b8:4e:22:9a:ed:66:39:6e:4b:94:e6: + 55:fc:0b:1b:8b:77:c1:53:13:66:89:d9:28:d6:8b:f3:45:4a: + 63:b7:fd:7b:0b:61:5d:b8:6d:be:c3:dc:5b:79:d2:ed:86:e5: + a2:4d:be:5e:74:7c:6a:ed:16:38:1f:7f:58:81:5a:1a:eb:32: + 88:2d:b2:f3:39:77:80:af:5e:b6:61:75:29:db:23:4d:88:ca: + 50:28:cb:85:d2:d3:10:a2:59:6e:d3:93:54:00:7a:a2:46:95: + 86:05:9c:a9:19:98:e5:31:72:0c:00:e2:67:d9:40:e0:24:33: + 7b:6f:2c:b9:5c:ab:65:9d:2c:ac:76:ea:35:99:f5:97:b9:0f: + 24:ec:c7:76:21:28:65:ae:57:e8:07:88:75:4a:56:a0:d2:05: + 3a:a4:e6:8d:92:88:2c:f3:f2:e1:c1:c6:61:db:41:c5:c7:9b: + f7:0e:1a:51:45:c2:61:6b:dc:64:27:17:8c:5a:b7:da:74:28: + cd:97:e4:bd +-----BEGIN CERTIFICATE----- +MIIEqjCCA5KgAwIBAgIOLmoAAQACH9dSISwRXDswDQYJKoZIhvcNAQEFBQAwdjEL +MAkGA1UEBhMCREUxHDAaBgNVBAoTE1RDIFRydXN0Q2VudGVyIEdtYkgxIjAgBgNV +BAsTGVRDIFRydXN0Q2VudGVyIENsYXNzIDIgQ0ExJTAjBgNVBAMTHFRDIFRydXN0 +Q2VudGVyIENsYXNzIDIgQ0EgSUkwHhcNMDYwMTEyMTQzODQzWhcNMjUxMjMxMjI1 +OTU5WjB2MQswCQYDVQQGEwJERTEcMBoGA1UEChMTVEMgVHJ1c3RDZW50ZXIgR21i +SDEiMCAGA1UECxMZVEMgVHJ1c3RDZW50ZXIgQ2xhc3MgMiBDQTElMCMGA1UEAxMc +VEMgVHJ1c3RDZW50ZXIgQ2xhc3MgMiBDQSBJSTCCASIwDQYJKoZIhvcNAQEBBQAD +ggEPADCCAQoCggEBAKuAh5uO8MN8h9foJIIRszzdQ2Lu+MNF2ujhoF/RKrLqk2jf +tMjWQ+nEdVl//OEd+DFwIxuInie5e/060smp6RQvkL4DUsFJzfb95AhmC1eKokKg +uNV/aVyQMrKXDcpK3EY+AlWJU+MaWss2xgdW94zPEfRMuzBwBJWl9jmM/XOBCH2J +XjIeIqkiRUuwZi4wzJ9l/fzLganx4Duvo4bRierERXlQXa7pIXSSTYtZgo+U4+lK +8edJsBTj9WLL1XK9H7nSn6DNqPoByNkN39r8R52zyFTfSUrxIan+GE7uSNQZu+99 +5OKdy1u2bv/jzVrndIIFuoAlOMvkaZ6vQaoahPUCAwEAAaOCATQwggEwMA8GA1Ud +EwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBTjq1RMgKHbVkO3 +kUrL84J6E1wIqzCB7QYDVR0fBIHlMIHiMIHfoIHcoIHZhjVodHRwOi8vd3d3LnRy +dXN0Y2VudGVyLmRlL2NybC92Mi90Y19jbGFzc18yX2NhX0lJLmNybIaBn2xkYXA6 +Ly93d3cudHJ1c3RjZW50ZXIuZGUvQ049VEMlMjBUcnVzdENlbnRlciUyMENsYXNz +JTIwMiUyMENBJTIwSUksTz1UQyUyMFRydXN0Q2VudGVyJTIwR21iSCxPVT1yb290 +Y2VydHMsREM9dHJ1c3RjZW50ZXIsREM9ZGU/Y2VydGlmaWNhdGVSZXZvY2F0aW9u +TGlzdD9iYXNlPzANBgkqhkiG9w0BAQUFAAOCAQEAjNfffu4bgBCzg/XbEeprS6iS +GNn3Bzn1LL4GdXpoUxUc6krtXvwjshOg0wn/9vYua0Fxec3ibf2uWWuFHbhOIprt +ZjluS5TmVfwLG4t3wVMTZonZKNaL80VKY7f9ewthXbhtvsPcW3nS7Yblok2+XnR8 +au0WOB9/WIFaGusyiC2y8zl3gK9etmF1KdsjTYjKUCjLhdLTEKJZbtOTVAB6okaV +hgWcqRmY5TFyDADiZ9lA4CQze28suVyrZZ0srHbqNZn1l7kPJOzHdiEoZa5X6AeI +dUpWoNIFOqTmjZKILPPy4cHGYdtBxceb9w4aUUXCYWvcZCcXjFq32nQozZfkvQ== +-----END CERTIFICATE----- diff --git a/security/src/main/files/cacerts/5021a0a2.0 b/security/src/main/files/cacerts/5021a0a2.0 new file mode 100644 index 0000000..ae6c41b --- /dev/null +++ b/security/src/main/files/cacerts/5021a0a2.0 @@ -0,0 +1,83 @@ +Certificate: + Data: + Version: 3 (0x2) + Serial Number: + 1d:a2:00:01:00:02:ec:b7:60:80:78:8d:b6:06 + Signature Algorithm: sha1WithRSAEncryption + Issuer: C=DE, O=TC TrustCenter GmbH, OU=TC TrustCenter Universal CA, CN=TC TrustCenter Universal CA I + Validity + Not Before: Mar 22 15:54:28 2006 GMT + Not After : Dec 31 22:59:59 2025 GMT + Subject: C=DE, O=TC TrustCenter GmbH, OU=TC TrustCenter Universal CA, CN=TC TrustCenter Universal CA I + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + RSA Public Key: (2048 bit) + Modulus (2048 bit): + 00:a4:77:23:96:44:af:90:f4:31:a7:10:f4:26:87: + 9c:f3:38:d9:0f:5e:de:cf:41:e8:31:ad:c6:74:91: + 24:96:78:1e:09:a0:9b:9a:95:4a:4a:f5:62:7c:02: + a8:ca:ac:fb:5a:04:76:39:de:5f:f1:f9:b3:bf:f3: + 03:58:55:d2:aa:b7:e3:04:22:d1:f8:94:da:22:08: + 00:8d:d3:7c:26:5d:cc:77:79:e7:2c:78:39:a8:26: + 73:0e:a2:5d:25:69:85:4f:55:0e:9a:ef:c6:b9:44: + e1:57:3d:df:1f:54:22:e5:6f:65:aa:33:84:3a:f3: + ce:7a:be:55:97:ae:8d:12:0f:14:33:e2:50:70:c3: + 49:87:13:bc:51:de:d7:98:12:5a:ef:3a:83:33:92: + 06:75:8b:92:7c:12:68:7b:70:6a:0f:b5:9b:b6:77: + 5b:48:59:9d:e4:ef:5a:ad:f3:c1:9e:d4:d7:45:4e: + ca:56:34:21:bc:3e:17:5b:6f:77:0c:48:01:43:29: + b0:dd:3f:96:6e:e6:95:aa:0c:c0:20:b6:fd:3e:36: + 27:9c:e3:5c:cf:4e:81:dc:19:bb:91:90:7d:ec:e6: + 97:04:1e:93:cc:22:49:d7:97:86:b6:13:0a:3c:43: + 23:77:7e:f0:dc:e6:cd:24:1f:3b:83:9b:34:3a:83: + 34:e3 + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Authority Key Identifier: + keyid:92:A4:75:2C:A4:9E:BE:81:44:EB:79:FC:8A:C5:95:A5:EB:10:75:73 + + X509v3 Basic Constraints: critical + CA:TRUE + X509v3 Key Usage: critical + Digital Signature, Certificate Sign, CRL Sign + X509v3 Subject Key Identifier: + 92:A4:75:2C:A4:9E:BE:81:44:EB:79:FC:8A:C5:95:A5:EB:10:75:73 + Signature Algorithm: sha1WithRSAEncryption + 28:d2:e0:86:d5:e6:f8:7b:f0:97:dc:22:6b:3b:95:14:56:0f: + 11:30:a5:9a:4f:3a:b0:3a:e0:06:cb:65:f5:ed:c6:97:27:fe: + 25:f2:57:e6:5e:95:8c:3e:64:60:15:5a:7f:2f:0d:01:c5:b1: + 60:fd:45:35:cf:f0:b2:bf:06:d9:ef:5a:be:b3:62:21:b4:d7: + ab:35:7c:53:3e:a6:27:f1:a1:2d:da:1a:23:9d:cc:dd:ec:3c: + 2d:9e:27:34:5d:0f:c2:36:79:bc:c9:4a:62:2d:ed:6b:d9:7d: + 41:43:7c:b6:aa:ca:ed:61:b1:37:82:15:09:1a:8a:16:30:d8: + ec:c9:d6:47:72:78:4b:10:46:14:8e:5f:0e:af:ec:c7:2f:ab: + 10:d7:b6:f1:6e:ec:86:b2:c2:e8:0d:92:73:dc:a2:f4:0f:3a: + bf:61:23:10:89:9c:48:40:6e:70:00:b3:d3:ba:37:44:58:11: + 7a:02:6a:88:f0:37:34:f0:19:e9:ac:d4:65:73:f6:69:8c:64: + 94:3a:79:85:29:b0:16:2b:0c:82:3f:06:9c:c7:fd:10:2b:9e: + 0f:2c:b6:9e:e3:15:bf:d9:36:1c:ba:25:1a:52:3d:1a:ec:22: + 0c:1c:e0:a4:a2:3d:f0:e8:39:cf:81:c0:7b:ed:5d:1f:6f:c5: + d0:0b:d7:98 +-----BEGIN CERTIFICATE----- +MIID3TCCAsWgAwIBAgIOHaIAAQAC7LdggHiNtgYwDQYJKoZIhvcNAQEFBQAweTEL +MAkGA1UEBhMCREUxHDAaBgNVBAoTE1RDIFRydXN0Q2VudGVyIEdtYkgxJDAiBgNV +BAsTG1RDIFRydXN0Q2VudGVyIFVuaXZlcnNhbCBDQTEmMCQGA1UEAxMdVEMgVHJ1 +c3RDZW50ZXIgVW5pdmVyc2FsIENBIEkwHhcNMDYwMzIyMTU1NDI4WhcNMjUxMjMx +MjI1OTU5WjB5MQswCQYDVQQGEwJERTEcMBoGA1UEChMTVEMgVHJ1c3RDZW50ZXIg +R21iSDEkMCIGA1UECxMbVEMgVHJ1c3RDZW50ZXIgVW5pdmVyc2FsIENBMSYwJAYD +VQQDEx1UQyBUcnVzdENlbnRlciBVbml2ZXJzYWwgQ0EgSTCCASIwDQYJKoZIhvcN +AQEBBQADggEPADCCAQoCggEBAKR3I5ZEr5D0MacQ9CaHnPM42Q9e3s9B6DGtxnSR +JJZ4Hgmgm5qVSkr1YnwCqMqs+1oEdjneX/H5s7/zA1hV0qq34wQi0fiU2iIIAI3T +fCZdzHd55yx4Oagmcw6iXSVphU9VDprvxrlE4Vc93x9UIuVvZaozhDrzznq+VZeu +jRIPFDPiUHDDSYcTvFHe15gSWu86gzOSBnWLknwSaHtwag+1m7Z3W0hZneTvWq3z +wZ7U10VOylY0Ibw+F1tvdwxIAUMpsN0/lm7mlaoMwCC2/T42J5zjXM9OgdwZu5GQ +fezmlwQek8wiSdeXhrYTCjxDI3d+8NzmzSQfO4ObNDqDNOMCAwEAAaNjMGEwHwYD +VR0jBBgwFoAUkqR1LKSevoFE63n8isWVpesQdXMwDwYDVR0TAQH/BAUwAwEB/zAO +BgNVHQ8BAf8EBAMCAYYwHQYDVR0OBBYEFJKkdSyknr6BROt5/IrFlaXrEHVzMA0G +CSqGSIb3DQEBBQUAA4IBAQAo0uCG1eb4e/CX3CJrO5UUVg8RMKWaTzqwOuAGy2X1 +7caXJ/4l8lfmXpWMPmRgFVp/Lw0BxbFg/UU1z/CyvwbZ71q+s2IhtNerNXxTPqYn +8aEt2hojnczd7Dwtnic0XQ/CNnm8yUpiLe1r2X1BQ3y2qsrtYbE3ghUJGooWMNjs +ydZHcnhLEEYUjl8Or+zHL6sQ17bxbuyGssLoDZJz3KL0Dzq/YSMQiZxIQG5wALPT +ujdEWBF6AmqI8Dc08BnprNRlc/ZpjGSUOnmFKbAWKwyCPwacx/0QK54PLLae4xW/ +2TYcuiUaUj0a7CIMHOCkoj3w6DnPgcB77V0fb8XQC9eY +-----END CERTIFICATE----- diff --git a/support/src/test/java/tests/support/resource/Support_Resources.java b/support/src/test/java/tests/support/resource/Support_Resources.java index 53a8925..67b6001 100644 --- a/support/src/test/java/tests/support/resource/Support_Resources.java +++ b/support/src/test/java/tests/support/resource/Support_Resources.java @@ -205,4 +205,20 @@ public class Support_Resources { throw new RuntimeException("Failed to load resource: " + name); } } + + public static File resourceToTempFile(String path) throws IOException { + File f = File.createTempFile("out", ".xml"); + f.deleteOnExit(); + FileOutputStream out = new FileOutputStream(f); + + InputStream xml = Support_Resources.class.getResourceAsStream(path); + int b; + while ((b = xml.read()) != -1) { + out.write(b); + } + out.flush(); + out.close(); + xml.close(); + return f; + } } diff --git a/tools/runner/Android.mk b/tools/runner/Android.mk index 851214b..b208219 100644 --- a/tools/runner/Android.mk +++ b/tools/runner/Android.mk @@ -1,57 +1,22 @@ LOCAL_PATH:= $(call my-dir) +# build DalvikRunner from the source under java/. include $(CLEAR_VARS) - -ext_dirs := \ - ../../../../external/jsr305/ri/src/main/java \ - ../../../../external/guava/src \ - ../../../../external/caliper/src - -ext_src_files := $(call all-java-files-under,$(ext_dirs)) - -LOCAL_SRC_FILES := \ - $(ext_src_files) \ - java/dalvik/runner/Adb.java \ - java/dalvik/runner/CaliperFinder.java \ - java/dalvik/runner/CaliperRunner.java \ - java/dalvik/runner/Classpath.java \ - java/dalvik/runner/CodeFinder.java \ - java/dalvik/runner/Command.java \ - java/dalvik/runner/CommandFailedException.java \ - java/dalvik/runner/DalvikRunner.java \ - java/dalvik/runner/DeviceDalvikVm.java \ - java/dalvik/runner/Driver.java \ - java/dalvik/runner/Dx.java \ - java/dalvik/runner/ExpectedResult.java \ - java/dalvik/runner/JUnitFinder.java \ - java/dalvik/runner/JUnitRunner.java \ - java/dalvik/runner/JavaVm.java \ - java/dalvik/runner/Javac.java \ - java/dalvik/runner/JtregFinder.java \ - java/dalvik/runner/JtregRunner.java \ - java/dalvik/runner/MainFinder.java \ - java/dalvik/runner/MainRunner.java \ - java/dalvik/runner/NamingPatternCodeFinder.java \ - java/dalvik/runner/Option.java \ - java/dalvik/runner/OptionParser.java \ - java/dalvik/runner/Result.java \ - java/dalvik/runner/Strings.java \ - java/dalvik/runner/TestRun.java \ - java/dalvik/runner/TestRunner.java \ - java/dalvik/runner/Threads.java \ - java/dalvik/runner/Vm.java \ - java/dalvik/runner/XmlReportPrinter.java \ - +LOCAL_SRC_FILES := $(call all-java-files-under,java) LOCAL_MODULE:= dalvik_runner -LOCAL_STATIC_JAVA_LIBRARIES := javatest jh jtreg kxml2-2.3.0 - +LOCAL_STATIC_JAVA_LIBRARIES := caliper javatest jh jtreg kxml2-2.3.0 # TODO this only works when junit is already built... LOCAL_JAVA_LIBRARIES := junit - +LOCAL_JAVACFLAGS := -Werror -Xlint:unchecked include $(BUILD_HOST_JAVA_LIBRARY) include $(call all-subdir-makefiles) +# prebuilt caliper.jar +include $(CLEAR_VARS) +LOCAL_PREBUILT_JAVA_LIBRARIES := caliper:lib/caliper.jar +include $(BUILD_HOST_PREBUILT) + # prebuilt javatest.jar include $(CLEAR_VARS) LOCAL_PREBUILT_JAVA_LIBRARIES := javatest:lib/javatest.jar diff --git a/tools/runner/java/dalvik/runner/Aapt.java b/tools/runner/java/dalvik/runner/Aapt.java new file mode 100644 index 0000000..4d1a873 --- /dev/null +++ b/tools/runner/java/dalvik/runner/Aapt.java @@ -0,0 +1,49 @@ +/* + * 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 dalvik.runner; + +import java.io.File; + +/** + * An aapt (Android Asset Packaging Tool) command. + */ +final class Aapt { + + public void apk(File apk, File manifest) { + + // TODO: we should be able to work with a shipping SDK, not depend on out/... + new Command.Builder() + .args("aapt") + .args("package") + .args("-F") + .args(apk) + .args("-M") + .args(manifest) + .args("-I") + .args("out/target/common/obj/APPS/framework-res_intermediates/package-export.apk") + .execute(); + } + public void add(File apk, File dex) { + new Command.Builder() + .args("aapt") + .args("add") + .args("-k") + .args(apk) + .args(dex) + .execute(); + } +} diff --git a/tools/runner/java/dalvik/runner/ActivityMode.java b/tools/runner/java/dalvik/runner/ActivityMode.java new file mode 100644 index 0000000..b2b4a3b --- /dev/null +++ b/tools/runner/java/dalvik/runner/ActivityMode.java @@ -0,0 +1,256 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dalvik.runner; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Properties; +import java.util.Set; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; +import java.util.logging.Logger; + +/** + * Runs a test in the context of an android.app.Activity on a device + */ +final class ActivityMode extends Mode { + + private static final Logger logger = Logger.getLogger(ActivityMode.class.getName()); + + private static final String TEST_ACTIVITY_CLASS = "dalvik.runner.TestActivity"; + + ActivityMode(Integer debugPort, long timeoutSeconds, File sdkJar, File localTemp, + boolean cleanBefore, boolean cleanAfter, File deviceRunnerDir) { + super(new EnvironmentDevice(cleanBefore, cleanAfter, + debugPort, localTemp, deviceRunnerDir), + timeoutSeconds, sdkJar); + } + + private EnvironmentDevice getEnvironmentDevice() { + return (EnvironmentDevice) environment; + } + + @Override protected void prepare(Set<File> testRunnerJava, Classpath testRunnerClasspath) { + testRunnerJava.add(new File("dalvik/libcore/tools/runner/lib/TestActivity.java")); + super.prepare(testRunnerJava, testRunnerClasspath); + } + + @Override protected void postCompileTestRunner() { + } + + @Override protected void postCompileTest(TestRun testRun) { + logger.fine("aapt and push " + testRun.getQualifiedName()); + + // Some things of note: + // 1. we can't put multiple dex files in one apk + // 2. we can't just give dex multiple jars with conflicting class names + // 3. dex is slow if we give it too much to chew on + // 4. dex can run out of memory if given too much to chew on + + // With that in mind, the APK packaging strategy is as follows: + // 1. make an empty classes temporary directory + // 2. add test runner classes + // 3. find original jar test came from, add contents to classes + // 4. add supported runner classes specified by finder + // 5. add latest test classes to output + // 6. dx to create a dex + // 7. aapt the dex to create apk + // 8. sign the apk + // 9. install the apk + File packagingDir = makePackagingDirectory(testRun); + addTestRunnerClasses(packagingDir); + List<File> found = new ArrayList<File>(); + File originalTestJar = findOriginalTestJar(testRun); + if (originalTestJar != null) { + found.add(originalTestJar); + } + found.addAll(testRun.getRunnerClasspath().getElements()); + extractJars(packagingDir, found); + addTestClasses(testRun, packagingDir); + File dex = createDex(testRun, packagingDir); + File apkUnsigned = createApk(testRun, dex); + File apkSigned = signApk(testRun, apkUnsigned); + installApk(testRun, apkSigned); + } + + private File makePackagingDirectory(TestRun testRun) { + File packagingDir = new File(environment.testCompilationDir(testRun), "packaging"); + new Rm().directoryTree(packagingDir); + new Mkdir().mkdirs(packagingDir); + return packagingDir; + } + + private void addTestRunnerClasses(File packagingDir) { + new Command("rsync", "-a", + environment.testRunnerClassesDir() + "/", + packagingDir + "/").execute(); + } + + private File findOriginalTestJar(TestRun testRun) { + String testClass = testRun.getTestClass(); + String testFile = testClass.replace('.', '/') + ".class"; + for (File element : testClasspath.getElements()) { + try { + JarFile jar = new JarFile(element); + JarEntry jarEntry = jar.getJarEntry(testFile); + if (jarEntry != null) { + return element; + } + } catch (IOException e) { + throw new RuntimeException( + "Could not find element " + element + + " of test class path " + testClasspath, e); + } + } + return null; + } + + private static void extractJars(File packagingDir, List<File> jars) { + for (File jar : jars) { + new Command.Builder() + .args("unzip") + .args("-q") + .args("-o") + .args(jar) + .args("-d") + .args(packagingDir).execute(); + } + new Rm().directoryTree(new File(packagingDir, "META-INF")); + } + + private void addTestClasses(TestRun testRun, File packagingDir) { + File testClassesDir = environment.testClassesDir(testRun); + new Command("rsync", "-a", + testClassesDir + "/", + packagingDir + "/").execute(); + } + private File createDex (TestRun testRun, File packagingDir) { + File testClassesDir = environment.testClassesDir(testRun); + File dex = new File(testClassesDir + ".dex"); + new Dx().dex(dex, Classpath.of(packagingDir)); + return dex; + } + + private File createApk (TestRun testRun, File dex) { + String androidManifest = + "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" + + "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" + + " package=\"" + testRun.getQualifiedName() + "\">\n" + + " <uses-permission android:name=\"android.permission.INTERNET\" />\n" + + " <application>\n" + + " <activity android:name=\"" + TEST_ACTIVITY_CLASS + "\">\n" + + " <intent-filter>\n" + + " <action android:name=\"android.intent.action.MAIN\" />\n" + + " <category android:name=\"android.intent.category.LAUNCHER\" />\n" + + " </intent-filter>\n" + + " </activity>\n" + + " </application>\n" + + "</manifest>\n"; + File androidManifestFile = + new File(environment.testCompilationDir(testRun), + "AndroidManifest.xml"); + try { + FileOutputStream androidManifestOut = + new FileOutputStream(androidManifestFile); + androidManifestOut.write(androidManifest.getBytes("UTF-8")); + androidManifestOut.close(); + } catch (IOException e) { + throw new RuntimeException("Problem writing " + androidManifestFile, e); + } + + File testClassesDir = environment.testClassesDir(testRun); + File apkUnsigned = new File(testClassesDir + ".apk.unsigned"); + new Aapt().apk(apkUnsigned, androidManifestFile); + new Aapt().add(apkUnsigned, dex); + new Aapt().add(apkUnsigned, new File(testClassesDir, TestProperties.FILE)); + return apkUnsigned; + } + + private File signApk(TestRun testRun, File apkUnsigned) { + File testClassesDir = environment.testClassesDir(testRun); + File apkSigned = new File(testClassesDir, testRun.getQualifiedName() + ".apk"); + // TODO: we should be able to work with a shipping SDK, not depend on out/... + // TODO: we should be able to work without hardwired keys, not depend on build/... + new Command.Builder() + .args("java") + .args("-jar") + .args("out/host/linux-x86/framework/signapk.jar") + .args("build/target/product/security/testkey.x509.pem") + .args("build/target/product/security/testkey.pk8") + .args(apkUnsigned) + .args(apkSigned).execute(); + new Rm().file(apkUnsigned); + return apkSigned; + } + + private void installApk(TestRun testRun, File apkSigned) { + // install the local apk ona the device + getEnvironmentDevice().adb.uninstall(testRun.getQualifiedName()); + getEnvironmentDevice().adb.install(apkSigned); + } + + @Override protected void fillInProperties(Properties properties, TestRun testRun) { + super.fillInProperties(properties, testRun); + properties.setProperty(TestProperties.DEVICE_RUNNER_DIR, getEnvironmentDevice().runnerDir.getPath()); + } + + @Override protected List<Command> buildCommands(TestRun testRun) { + List<Command> commands = new ArrayList<Command>(); + commands.add(new Command.Builder() + .args("adb") + .args("shell") + .args("am") + .args("start") + .args("-a") + .args("android.intent.action.MAIN") + .args("-n") + .args(testRun.getQualifiedName() + "/" + TEST_ACTIVITY_CLASS).build()); + + File resultDir = new File(getEnvironmentDevice().runnerDir, testRun.getQualifiedName()); + File resultFile = new File(resultDir, TestProperties.RESULT_FILE); + /* + * The follow bash script waits for the result file to + * exist. It polls once a second to see if it is there with + * "adb shell ls". The "tr" is to remove the carriage return + * and newline from the adb output. When it does exist, we + * "adb shell cat" it so we can see the SUCCESS/FAILURE + * results that are expected by Mode.runTest. + */ + // TODO: move loop to Java + commands.add(new Command.Builder() + .args("bash") + .args("-c") + .args( + "while [ ! \"`adb shell ls " + resultFile + " | tr -d '\\r\\n'`\" = " + + " \"" + resultFile + "\" ] ; do " + + " sleep 1; " + + "done; " + + "adb shell cat " + resultFile).build()); + + return commands; + } + + @Override void cleanup(TestRun testRun) { + super.cleanup(testRun); + if (environment.cleanAfter) { + getEnvironmentDevice().adb.uninstall(testRun.getQualifiedName()); + } + } +} diff --git a/tools/runner/java/dalvik/runner/Adb.java b/tools/runner/java/dalvik/runner/Adb.java index d265167..075ca5f 100644 --- a/tools/runner/java/dalvik/runner/Adb.java +++ b/tools/runner/java/dalvik/runner/Adb.java @@ -17,6 +17,8 @@ package dalvik.runner; import java.io.File; +import java.util.List; +import java.util.concurrent.TimeoutException; /** * An adb command. @@ -24,15 +26,25 @@ import java.io.File; final class Adb { public void mkdir(File name) { - new Command("adb", "shell", "mkdir", name.toString()).execute(); + new Command("adb", "shell", "mkdir", name.getPath()).execute(); } public void rm(File name) { - new Command("adb", "shell", "rm", "-r", name.toString()).execute(); + new Command("adb", "shell", "rm", "-r", name.getPath()).execute(); } public void push(File local, File remote) { - new Command("adb", "push", local.toString(), remote.toString()) + new Command("adb", "push", local.getPath(), remote.getPath()) + .execute(); + } + + public void install(File apk) { + new Command("adb", "install", "-r", apk.getPath()) + .execute(); + } + + public void uninstall(String packageName) { + new Command("adb", "uninstall", packageName) .execute(); } @@ -40,4 +52,39 @@ final class Adb { new Command("adb", "forward", "tcp:" + localPort, "tcp:" + devicePort) .execute(); } -}
\ No newline at end of file + + public void waitForDevice() { + new Command("adb", "wait-for-device").execute(); + } + + /** + * Loop until we see a non-empty directory on the device. For + * example, wait until /sdcard is mounted. + */ + public void waitForNonEmptyDirectory(File path, int timeoutSeconds) { + final int millisPerSecond = 1000; + final long start = System.currentTimeMillis(); + final long deadline = start + (millisPerSecond * timeoutSeconds); + + while (true) { + final long remainingSeconds = ((deadline - System.currentTimeMillis()) + / millisPerSecond); + Command command = new Command("adb", "shell", "ls", path.getPath()); + List<String> output; + try { + output = command.executeWithTimeout(remainingSeconds); + } catch (TimeoutException e) { + throw new RuntimeException("Timed out after " + timeoutSeconds + + " seconds waiting for file " + path, e); + } + try { + Thread.sleep(millisPerSecond); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + if (!output.isEmpty()) { + return; + } + } + } +} diff --git a/tools/runner/java/dalvik/runner/CaliperFinder.java b/tools/runner/java/dalvik/runner/CaliperFinder.java index 77d85dc..3609471 100644 --- a/tools/runner/java/dalvik/runner/CaliperFinder.java +++ b/tools/runner/java/dalvik/runner/CaliperFinder.java @@ -32,7 +32,15 @@ class CaliperFinder extends NamingPatternCodeFinder { return "caliper"; } - @Override protected Class<? extends TestRunner> runnerClass() { + public Class<? extends Runner> getRunnerClass() { return CaliperRunner.class; } + + public File getRunnerJava() { + return new File(DalvikRunner.HOME_JAVA, "dalvik/runner/CaliperRunner.java"); + } + + public Classpath getRunnerClasspath() { + return new Classpath(); + } } diff --git a/tools/runner/java/dalvik/runner/CaliperRunner.java b/tools/runner/java/dalvik/runner/CaliperRunner.java index 514a208..b30644b 100644 --- a/tools/runner/java/dalvik/runner/CaliperRunner.java +++ b/tools/runner/java/dalvik/runner/CaliperRunner.java @@ -16,23 +16,22 @@ package dalvik.runner; +import com.google.caliper.Benchmark; import com.google.caliper.Runner; /** * Runs a <a href="http://code.google.com/p/caliper/">Caliper</a> benchmark. */ -public final class CaliperRunner extends TestRunner { +public final class CaliperRunner implements dalvik.runner.Runner { - @Override public boolean test() { + public void prepareTest(Class<?> testClass) {} + + public boolean test(Class<?> testClass) { try { - Runner.main(className); + Runner.main(testClass.asSubclass(Benchmark.class), new String[0]); } catch (Exception ex) { ex.printStackTrace(); } return false; // always print benchmarking results } - - public static void main(String[] args) throws Exception { - new CaliperRunner().run(); - } } diff --git a/tools/runner/java/dalvik/runner/Classpath.java b/tools/runner/java/dalvik/runner/Classpath.java index 86000cb..607615a 100644 --- a/tools/runner/java/dalvik/runner/Classpath.java +++ b/tools/runner/java/dalvik/runner/Classpath.java @@ -30,13 +30,21 @@ final class Classpath { private final List<File> elements = new ArrayList<File>(); public static Classpath of(File... files) { + return of(Arrays.asList(files)); + } + + public static Classpath of(Collection<File> files) { Classpath result = new Classpath(); - result.elements.addAll(Arrays.asList(files)); + result.elements.addAll(files); return result; } public void addAll(File... elements) { - this.elements.addAll(Arrays.asList(elements)); + addAll(Arrays.asList(elements)); + } + + public void addAll(Collection<File> elements) { + this.elements.addAll(elements); } public void addAll(Classpath anotherClasspath) { diff --git a/tools/runner/java/dalvik/runner/CodeFinder.java b/tools/runner/java/dalvik/runner/CodeFinder.java index 1d4a95a..f770fa4 100644 --- a/tools/runner/java/dalvik/runner/CodeFinder.java +++ b/tools/runner/java/dalvik/runner/CodeFinder.java @@ -29,4 +29,19 @@ public interface CodeFinder { * is empty, no executable code of this kind were found. */ public Set<TestRun> findTests(File file); + + /** + * Return the class for the TestRunner + */ + public Class<? extends Runner> getRunnerClass(); + + /** + * Return the Java file for the TestRunner + */ + public File getRunnerJava(); + + /** + * Return the compile classpath for the TestRunner + */ + public Classpath getRunnerClasspath(); } diff --git a/tools/runner/java/dalvik/runner/Command.java b/tools/runner/java/dalvik/runner/Command.java index 8904711..4319cf9 100644 --- a/tools/runner/java/dalvik/runner/Command.java +++ b/tools/runner/java/dalvik/runner/Command.java @@ -25,6 +25,13 @@ import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.List; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; import java.util.logging.Logger; /** @@ -124,11 +131,50 @@ final class Command { } } + /** + * Executes a command with a specified timeout. Output is returned + * if the command succeeds. If Otherwise null is returned if the + * command timed out. + */ + public List<String> executeWithTimeout(long timeoutSeconds) + throws TimeoutException { + ExecutorService outputReader + = Executors.newFixedThreadPool(1, Threads.daemonThreadFactory()); + try { + start(); + // run on a different thread to allow a timeout + Future<List<String>> future = outputReader.submit(new Callable<List<String>>() { + public List<String> call() throws Exception { + return gatherOutput(); + } + }); + return future.get(timeoutSeconds, TimeUnit.SECONDS); + } catch (IOException e) { + throw new RuntimeException("Failed to execute process: " + args, e); + } catch (InterruptedException e) { + throw new RuntimeException("Interrupted while executing process: " + args, e); + } catch (ExecutionException e) { + throw new RuntimeException(e); + } finally { + if (isStarted()) { + getProcess().destroy(); // to release the output reader + } + outputReader.shutdown(); + } + } + static class Builder { private final List<String> args = new ArrayList<String>(); private File workingDirectory; private boolean permitNonZeroExitStatus = false; + public Builder args(Object... objects) { + for (Object object : objects) { + args(object.toString()); + } + return this; + } + public Builder args(String... args) { return args(Arrays.asList(args)); } diff --git a/tools/runner/java/dalvik/runner/DalvikRunner.java b/tools/runner/java/dalvik/runner/DalvikRunner.java index 20e722c..015fb9a 100644 --- a/tools/runner/java/dalvik/runner/DalvikRunner.java +++ b/tools/runner/java/dalvik/runner/DalvikRunner.java @@ -35,6 +35,9 @@ import java.util.logging.Logger; */ public final class DalvikRunner { + static final File HOME = new File("dalvik/libcore/tools/runner"); + static final File HOME_JAVA = new File(HOME, "java"); + private static class Options { private final List<File> testFiles = new ArrayList<File>(); @@ -54,6 +57,12 @@ public final class DalvikRunner { @Option(names = { "--timeout" }) private long timeoutSeconds = 10 * 60; // default is ten minutes; + @Option(names = { "--clean-before" }) + private boolean cleanBefore = true; + + @Option(names = { "--clean-after" }) + private boolean cleanAfter = true; + @Option(names = { "--clean" }) private boolean clean = true; @@ -96,9 +105,16 @@ public final class DalvikRunner { System.out.println(" device within an android.app.Activity."); System.out.println(" Default is: " + mode); System.out.println(); - System.out.println(" --clean: remove temporary files (default). Disable with --no-clean"); - System.out.println(" and use with --verbose if you'd like to manually re-run"); - System.out.println(" commands afterwards."); + System.out.println(" --clean-before: remove working directories before building and"); + System.out.println(" running (default). Disable with --no-clean-before if you are"); + System.out.println(" using interactively with your own temporary input files."); + System.out.println(); + System.out.println(" --clean-after: remove temporary files after running (default)."); + System.out.println(" Disable with --no-clean-after and use with --verbose if"); + System.out.println(" you'd like to manually re-run commands afterwards."); + System.out.println(); + System.out.println(" --clean: synonym for --clean-before and --clean-after (default)."); + System.out.println(" Disable with --no-clean if you want no files removed."); System.out.println(); System.out.println(" --timeout-seconds <seconds>: maximum execution time of each"); System.out.println(" test before the runner aborts it."); @@ -181,10 +197,6 @@ public final class DalvikRunner { System.out.println("Invalid java home: " + javaHome); return false; } - if (debugPort != null) { - System.out.println("debug port " + debugPort + " should not be specified for mode " + mode); - return false; - } } // check vm option consistency @@ -210,6 +222,11 @@ public final class DalvikRunner { return false; } + if (!clean) { + cleanBefore = false; + cleanAfter = false; + } + // // Post-processing arguments // @@ -228,7 +245,7 @@ public final class DalvikRunner { } private final Options options = new Options(); - private final File localTemp = new File("/tmp/" + UUID.randomUUID()); + private final File localTemp = new File("/tmp/dalvikrunner/" + UUID.randomUUID()); private DalvikRunner() {} @@ -246,29 +263,36 @@ public final class DalvikRunner { } private void run() { - Vm vm; + Mode mode; if (options.mode.equals(Options.MODE_DEVICE)) { - vm = new DeviceDalvikVm( + mode = new DeviceDalvikVm( options.debugPort, options.timeoutSeconds, options.sdkJar, localTemp, options.vmArgs, - options.clean, + options.cleanBefore, + options.cleanAfter, options.deviceRunnerDir); } else if (options.mode.equals(Options.MODE_HOST)) { - vm = new JavaVm( + mode = new JavaVm( options.debugPort, options.timeoutSeconds, options.sdkJar, localTemp, options.javaHome, options.vmArgs, - options.clean); + options.cleanBefore, + options.cleanAfter); } else if (options.mode.equals(Options.MODE_ACTIVITY)) { - vm = null; - System.out.println("Mode " + options.mode + " not currently supported."); - return; + mode = new ActivityMode( + options.debugPort, + options.timeoutSeconds, + options.sdkJar, + localTemp, + options.cleanBefore, + options.cleanAfter, + options.deviceRunnerDir); } else { System.out.println("Unknown mode mode " + options.mode + "."); return; @@ -281,7 +305,7 @@ public final class DalvikRunner { new MainFinder()); Driver driver = new Driver( localTemp, - vm, + mode, options.expectationFiles, options.xmlReportsDirectory, codeFinders); @@ -293,7 +317,7 @@ public final class DalvikRunner { } driver.buildAndRunAllTests(options.testFiles); - vm.shutdown(); + mode.shutdown(); } public static void main(String[] args) { diff --git a/tools/runner/java/dalvik/runner/DeviceDalvikVm.java b/tools/runner/java/dalvik/runner/DeviceDalvikVm.java index 4388565..7bdf482 100644 --- a/tools/runner/java/dalvik/runner/DeviceDalvikVm.java +++ b/tools/runner/java/dalvik/runner/DeviceDalvikVm.java @@ -24,91 +24,84 @@ import java.util.logging.Logger; * Execute tests on a Dalvik VM using an Android device or emulator. */ final class DeviceDalvikVm extends Vm { - - private static final Classpath RUNTIME_SUPPORT_CLASSPATH = Classpath.of( - new File("/system/framework/core-tests.jar"), - new File("/system/framework/caliper.jar"), - new File("/system/framework/guava.jar"), - new File("/system/framework/jsr305.jar")); - private static final Logger logger = Logger.getLogger(DeviceDalvikVm.class.getName()); - private final File runnerDir; - private final File testTemp; - - private final Adb adb = new Adb(); DeviceDalvikVm(Integer debugPort, long timeoutSeconds, File sdkJar, - File localTemp, List<String> additionalVmArgs, boolean clean, File runnerDir) { - super(debugPort, timeoutSeconds, sdkJar, localTemp, additionalVmArgs, clean); + File localTemp, List<String> additionalVmArgs, + boolean cleanBefore, boolean cleanAfter, File runnerDir) { + super(new EnvironmentDevice(cleanBefore, cleanAfter, debugPort, localTemp, runnerDir), + timeoutSeconds, sdkJar, additionalVmArgs); + } - this.runnerDir = runnerDir; - this.testTemp = new File(this.runnerDir, "/tests.tmp"); + private EnvironmentDevice getEnvironmentDevice() { + return (EnvironmentDevice) environment; } - @Override public void prepare() { - adb.rm(runnerDir); - adb.mkdir(testTemp); - if (debugPort != null) { - adb.forwardTcp(debugPort, debugPort); + @Override protected void postCompileTestRunner() { + // TODO: does this really need to be a special case? + postCompile("testrunner", environment.testRunnerClassesDir()); + + // dex everything on the classpath and push it to the device. + for (File classpathElement : testClasspath.getElements()) { + String name = basenameOfJar(classpathElement); + logger.fine("dex and push " + name); + // make the local dex (inside a jar) + // TODO: this is *really* expensive. we need a cache! + File outputFile = getEnvironmentDevice().testDir(name + ".jar"); + new Dx().dex(outputFile, Classpath.of(classpathElement)); + // push the local dex to the device + getEnvironmentDevice().adb.push(outputFile, deviceDexFile(name)); } - super.prepare(); } - @Override protected Classpath postCompile(String name, Classpath targetClasses) { - logger.fine("dex and push " + name); - - // make the local dex - File localDex = new File(localTemp, name + ".jar"); - new Dx().dex(localDex.toString(), targetClasses); - - // post the local dex to the device - File deviceDex = new File(runnerDir, localDex.getName()); - adb.push(localDex, deviceDex); - - return Classpath.of(deviceDex); + private String basenameOfJar(File jarFile) { + return jarFile.getName().replaceAll("\\.jar$", ""); } - @Override public void shutdown() { - super.shutdown(); - - if (clean) { - adb.rm(runnerDir); - } + @Override protected void postCompileTest(TestRun testRun) { + postCompile(testRun.getQualifiedName(), environment.testClassesDir(testRun)); } - @Override protected void prepareUserDir(TestRun testRun) { - File testClassesDirOnDevice = testClassesDirOnDevice(testRun); - adb.mkdir(testClassesDirOnDevice); - adb.push(testRun.getTestDirectory(), testClassesDirOnDevice); - testRun.setUserDir(testClassesDirOnDevice); - } + private void postCompile(String name, File dir) { + logger.fine("dex and push " + name); - @Override public void cleanup(TestRun testRun) { - super.cleanup(testRun); + // make the local dex (inside a jar) + File localDex = new File(dir.getPath() + ".jar"); + new Dx().dex(localDex, Classpath.of(dir)); - if (clean) { - adb.rm(testClassesDirOnDevice(testRun)); - } + // post the local dex to the device + File deviceDex = deviceDexFile(name); + getEnvironmentDevice().adb.push(localDex, deviceDex); } - private File testClassesDirOnDevice(TestRun testRun) { - return new File(runnerDir, testRun.getQualifiedName()); + private File deviceDexFile(String name) { + return new File(getEnvironmentDevice().runnerDir, name + ".jar"); } @Override protected VmCommandBuilder newVmCommandBuilder( File workingDirectory) { // ignore the working directory; it's device-local and we can't easily // set the working directory for commands run via adb shell. + // TODO: we only *need* to set ANDROID_DATA on production devices. + // We set "user.home" to /sdcard because code might reasonably assume it can write to + // that directory. return new VmCommandBuilder() - .vmCommand("adb", "shell", "dalvikvm") + .vmCommand("adb", "shell", "ANDROID_DATA=/sdcard", "dalvikvm") + .vmArgs("-Duser.home=/sdcard") .vmArgs("-Duser.name=root") .vmArgs("-Duser.language=en") .vmArgs("-Duser.region=US") .vmArgs("-Djavax.net.ssl.trustStore=/system/etc/security/cacerts.bks") - .temp(testTemp); + .temp(getEnvironmentDevice().testTemp); } - @Override protected Classpath getRuntimeSupportClasspath() { - return RUNTIME_SUPPORT_CLASSPATH; + @Override protected Classpath getRuntimeSupportClasspath(TestRun testRun) { + Classpath classpath = new Classpath(); + classpath.addAll(deviceDexFile(testRun.getQualifiedName())); + classpath.addAll(deviceDexFile("testrunner")); + for (File testClasspathElement : testClasspath.getElements()) { + classpath.addAll(deviceDexFile(basenameOfJar(testClasspathElement))); + } + return classpath; } } diff --git a/tools/runner/java/dalvik/runner/Driver.java b/tools/runner/java/dalvik/runner/Driver.java index 81c7d6b..574c8cb 100644 --- a/tools/runner/java/dalvik/runner/Driver.java +++ b/tools/runner/java/dalvik/runner/Driver.java @@ -22,6 +22,7 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; +import java.util.HashSet; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; @@ -42,7 +43,7 @@ final class Driver { private final File localTemp; private final Set<File> expectationFiles; private final List<CodeFinder> codeFinders; - private final Vm vm; + private final Mode mode; private final File xmlReportsDirectory; private final Map<String, ExpectedResult> expectedResults = new HashMap<String, ExpectedResult>(); @@ -52,11 +53,11 @@ final class Driver { */ private int unsupportedTests = 0; - public Driver(File localTemp, Vm vm, Set<File> expectationFiles, + public Driver(File localTemp, Mode mode, Set<File> expectationFiles, File xmlReportsDirectory, List<CodeFinder> codeFinders) { this.localTemp = localTemp; this.expectationFiles = expectationFiles; - this.vm = vm; + this.mode = mode; this.xmlReportsDirectory = xmlReportsDirectory; this.codeFinders = codeFinders; } @@ -73,9 +74,7 @@ final class Driver { * Builds and executes all tests in the test directory. */ public void buildAndRunAllTests(Collection<File> testFiles) { - localTemp.mkdirs(); - - final BlockingQueue<TestRun> readyToRun = new ArrayBlockingQueue<TestRun>(4); + new Mkdir().mkdirs(localTemp); Set<TestRun> tests = new LinkedHashSet<TestRun>(); for (File testFile : testFiles) { @@ -94,10 +93,25 @@ final class Driver { tests.addAll(testsForFile); } + // compute TestRunner java and classpath to pass to mode.prepare + Set<File> testRunnerJava = new HashSet<File>(); + Classpath testRunnerClasspath = new Classpath(); + for (final TestRun testRun : tests) { + testRunnerJava.add(testRun.getRunnerJava()); + testRunnerClasspath.addAll(testRun.getRunnerClasspath()); + } + + // mode.prepare before mode.buildAndInstall to ensure test + // runner is built. packaging of activity APK files needs the + // test runner along with the test specific files. + mode.prepare(testRunnerJava, testRunnerClasspath); + logger.info("Running " + tests.size() + " tests."); // build and install tests in a background thread. Using lots of // threads helps for packages that contain many unsupported tests + final BlockingQueue<TestRun> readyToRun = new ArrayBlockingQueue<TestRun>(4); + ExecutorService builders = Threads.threadPerCpuExecutor(); int t = 0; for (final TestRun testRun : tests) { @@ -114,7 +128,7 @@ final class Driver { + " because the expectations file says it is unsupported."); } else { - vm.buildAndInstall(testRun); + mode.buildAndInstall(testRun); logger.fine("installed test " + runIndex + "; " + readyToRun.size() + " are ready to run"); } @@ -128,8 +142,6 @@ final class Driver { } builders.shutdown(); - vm.prepare(); - List<TestRun> runs = new ArrayList<TestRun>(tests.size()); for (int i = 0; i < tests.size(); i++) { logger.fine("executing test " + i + "; " @@ -149,7 +161,7 @@ final class Driver { runs.add(testRun); execute(testRun); - vm.cleanup(testRun); + mode.cleanup(testRun); } if (unsupportedTests > 0) { @@ -197,7 +209,7 @@ final class Driver { } if (testRun.isRunnable()) { - vm.runTest(testRun); + mode.runTest(testRun); } printResult(testRun); diff --git a/tools/runner/java/dalvik/runner/Dx.java b/tools/runner/java/dalvik/runner/Dx.java index d36a99d..393b70d 100644 --- a/tools/runner/java/dalvik/runner/Dx.java +++ b/tools/runner/java/dalvik/runner/Dx.java @@ -16,22 +16,46 @@ package dalvik.runner; +import java.io.File; +import java.util.logging.Logger; + /** * A dx command. */ final class Dx { + private static final Logger logger = Logger.getLogger(Dx.class.getName()); + private static final Md5Cache DEX_CACHE = new Md5Cache("dex"); - public void dex(String output, Classpath classpath) { - // We pass --core-library so that we can write tests in the same package they're testing, - // even when that's a core library package. If you're actually just using this tool to - // execute arbitrary code, this has the unfortunate side-effect of preventing "dx" from - // protecting you from yourself. + /** + * Converts all the .class files on 'classpath' into a dex file written to 'output'. + */ + public void dex(File output, Classpath classpath) { + File key = DEX_CACHE.makeKey(classpath); + if (key != null && key.exists()) { + logger.fine("dex cache hit for " + classpath); + new Command.Builder().args("cp", key, output).execute(); + return; + } + /* + * We pass --core-library so that we can write tests in the + * same package they're testing, even when that's a core + * library package. If you're actually just using this tool to + * execute arbitrary code, this has the unfortunate + * side-effect of preventing "dx" from protecting you from + * yourself. + * + * Memory options pulled from build/core/definitions.mk to + * handle large dx input when building dex for APK. + */ new Command.Builder() .args("dx") - .args("--core-library") + .args("-JXms16M") + .args("-JXmx1536M") .args("--dex") .args("--output=" + output) + .args("--core-library") .args(Strings.objectsToStrings(classpath.getElements())) .execute(); + DEX_CACHE.insert(key, output); } } diff --git a/tools/runner/java/dalvik/runner/Environment.java b/tools/runner/java/dalvik/runner/Environment.java new file mode 100644 index 0000000..c284a37 --- /dev/null +++ b/tools/runner/java/dalvik/runner/Environment.java @@ -0,0 +1,91 @@ +/* + * 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 dalvik.runner; + +import java.io.File; +import java.util.logging.Logger; + +/** + * A target runtime environment such as a remote device or the local host + */ +abstract class Environment { + private static final Logger logger = Logger.getLogger(Environment.class.getName()); + + final boolean cleanBefore; + final boolean cleanAfter; + final Integer debugPort; + private final File localTemp; + + Environment (boolean cleanBefore, boolean cleanAfter, Integer debugPort, File localTemp) { + this.cleanBefore = cleanBefore; + this.cleanAfter = cleanAfter; + this.debugPort = debugPort; + this.localTemp = localTemp; + } + + /** + * Initializes the temporary directories and test harness necessary to run + * tests. + */ + abstract void prepare(); + + /** + * Prepares the directory from which the test will be executed. Some tests + * expect to read data files from the current working directory; this step + * should ensure such files are available. + */ + abstract void prepareUserDir(TestRun testRun); + + /** + * Deletes files and releases any resources required for the execution of + * the given test. + */ + void cleanup(TestRun testRun) { + if (cleanAfter) { + logger.fine("clean " + testRun.getQualifiedName()); + new Rm().directoryTree(testCompilationDir(testRun)); + new Rm().directoryTree(testUserDir(testRun)); + } + } + + final File testDir(String name) { + return new File(localTemp, name); + } + + final File testRunnerDir(String name) { + return new File(testDir("testrunner"), name); + } + + final File testRunnerClassesDir() { + return testRunnerDir("classes"); + } + + final File testCompilationDir(TestRun testRun) { + return new File(localTemp, testRun.getQualifiedName()); + } + + final File testClassesDir(TestRun testRun) { + return new File(testCompilationDir(testRun), "classes"); + } + + final File testUserDir(TestRun testRun) { + File testTemp = new File(localTemp, "userDir"); + return new File(testTemp, testRun.getQualifiedName()); + } + + abstract void shutdown(); +} diff --git a/tools/runner/java/dalvik/runner/EnvironmentDevice.java b/tools/runner/java/dalvik/runner/EnvironmentDevice.java new file mode 100644 index 0000000..9ac1c64 --- /dev/null +++ b/tools/runner/java/dalvik/runner/EnvironmentDevice.java @@ -0,0 +1,73 @@ +/* + * 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 dalvik.runner; + +import java.io.File; +import java.util.logging.Logger; + +class EnvironmentDevice extends Environment { + private static final Logger logger = Logger.getLogger(EnvironmentDevice.class.getName()); + + final Adb adb = new Adb(); + final File runnerDir; + final File testTemp; + + EnvironmentDevice (boolean cleanBefore, boolean cleanAfter, + Integer debugPort, File localTemp, File runnerDir) { + super(cleanBefore, cleanAfter, debugPort, localTemp); + this.runnerDir = runnerDir; + this.testTemp = new File(runnerDir, "/tests.tmp"); + } + + @Override void prepare() { + adb.waitForDevice(); + adb.waitForNonEmptyDirectory(runnerDir.getParentFile(), 5 * 60); + if (cleanBefore) { + adb.rm(runnerDir); + } + adb.mkdir(runnerDir); + adb.mkdir(testTemp); + adb.mkdir(new File("/sdcard/dalvik-cache")); // TODO: only necessary on production devices. + if (debugPort != null) { + adb.forwardTcp(debugPort, debugPort); + } + } + + @Override protected void prepareUserDir(TestRun testRun) { + File testClassesDirOnDevice = testClassesDirOnDevice(testRun); + adb.mkdir(testClassesDirOnDevice); + adb.push(testRun.getTestDirectory(), testClassesDirOnDevice); + testRun.setUserDir(testClassesDirOnDevice); + } + + private File testClassesDirOnDevice(TestRun testRun) { + return new File(runnerDir, testRun.getQualifiedName()); + } + + @Override void cleanup(TestRun testRun) { + super.cleanup(testRun); + if (cleanAfter) { + adb.rm(testClassesDirOnDevice(testRun)); + } + } + + @Override void shutdown() { + if (cleanAfter) { + adb.rm(runnerDir); + } + } +} diff --git a/tools/runner/java/dalvik/runner/EnvironmentHost.java b/tools/runner/java/dalvik/runner/EnvironmentHost.java new file mode 100644 index 0000000..d02ea55 --- /dev/null +++ b/tools/runner/java/dalvik/runner/EnvironmentHost.java @@ -0,0 +1,45 @@ +/* + * 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 dalvik.runner; + +import java.io.File; + +class EnvironmentHost extends Environment { + + EnvironmentHost(boolean cleanBefore, boolean cleanAfter, + Integer debugPort, File localTemp) { + super(cleanBefore, cleanAfter, debugPort, localTemp); + } + + @Override void prepare() {} + + @Override protected void prepareUserDir(TestRun testRun) { + File testUserDir = testUserDir(testRun); + + // if the user dir exists, cp would copy the files to the wrong place + if (testUserDir.exists()) { + throw new IllegalStateException(); + } + + new Mkdir().mkdirs(testUserDir.getParentFile()); + new Command("cp", "-r", testRun.getTestDirectory().toString(), + testUserDir.toString()).execute(); + testRun.setUserDir(testUserDir); + } + + @Override void shutdown() {} +} diff --git a/tools/runner/java/dalvik/runner/JUnitFinder.java b/tools/runner/java/dalvik/runner/JUnitFinder.java index b446a39..131a8cf 100644 --- a/tools/runner/java/dalvik/runner/JUnitFinder.java +++ b/tools/runner/java/dalvik/runner/JUnitFinder.java @@ -32,7 +32,16 @@ class JUnitFinder extends NamingPatternCodeFinder { return "junit"; } - @Override protected Class<? extends TestRunner> runnerClass() { + public Class<? extends Runner> getRunnerClass() { return JUnitRunner.class; } + + public File getRunnerJava() { + return new File(DalvikRunner.HOME_JAVA, "dalvik/runner/JUnitRunner.java"); + } + + public Classpath getRunnerClasspath() { + // TODO: we should be able to work with a shipping SDK, not depend on out/... + return Classpath.of(new File("out/host/common/obj/JAVA_LIBRARIES/junit_intermediates/javalib.jar").getAbsoluteFile()); + } } diff --git a/tools/runner/java/dalvik/runner/JUnitRunner.java b/tools/runner/java/dalvik/runner/JUnitRunner.java index de5aacd..4891448 100644 --- a/tools/runner/java/dalvik/runner/JUnitRunner.java +++ b/tools/runner/java/dalvik/runner/JUnitRunner.java @@ -23,7 +23,7 @@ import junit.runner.TestSuiteLoader; /** * Runs a JUnit test. */ -public final class JUnitRunner extends TestRunner { +public final class JUnitRunner implements Runner { private final junit.textui.TestRunner testRunner; private Test junitTest; @@ -46,16 +46,12 @@ public final class JUnitRunner extends TestRunner { }; } - @Override public void prepareTest() { - junitTest = testRunner.getTest(className); + public void prepareTest(Class<?> testClass) { + junitTest = testRunner.getTest(testClass.getName()); } - @Override public boolean test() { + public boolean test(Class<?> testClass) { TestResult result = testRunner.doRun(junitTest); return result.wasSuccessful(); } - - public static void main(String[] args) throws Exception { - new JUnitRunner().run(); - } -}
\ No newline at end of file +} diff --git a/tools/runner/java/dalvik/runner/JavaVm.java b/tools/runner/java/dalvik/runner/JavaVm.java index e29b36b..2dfc3e7 100644 --- a/tools/runner/java/dalvik/runner/JavaVm.java +++ b/tools/runner/java/dalvik/runner/JavaVm.java @@ -18,6 +18,7 @@ package dalvik.runner; import java.io.File; import java.util.List; +import java.util.Set; /** * A local Java virtual machine like Harmony or the RI. @@ -27,11 +28,19 @@ final class JavaVm extends Vm { private final File javaHome; JavaVm(Integer debugPort, long timeoutSeconds, File sdkJar, File localTemp, - File javaHome, List<String> additionalVmArgs, boolean clean) { - super(debugPort, timeoutSeconds, sdkJar, localTemp, additionalVmArgs, clean); + File javaHome, List<String> additionalVmArgs, + boolean cleanBefore, boolean cleanAfter) { + super(new EnvironmentHost(cleanBefore, cleanAfter, debugPort, localTemp), + timeoutSeconds, sdkJar, additionalVmArgs); this.javaHome = javaHome; } + @Override protected void postCompileTestRunner() { + } + + @Override protected void postCompileTest(TestRun testRun) { + } + @Override protected VmCommandBuilder newVmCommandBuilder( File workingDirectory) { String java = javaHome == null ? "java" : new File(javaHome, "bin/java").getPath(); @@ -39,4 +48,12 @@ final class JavaVm extends Vm { .vmCommand(java) .workingDir(workingDirectory); } + @Override protected Classpath getRuntimeSupportClasspath(TestRun testRun) { + Classpath classpath = new Classpath(); + classpath.addAll(environment.testClassesDir(testRun)); + classpath.addAll(testClasspath); + classpath.addAll(environment.testRunnerClassesDir()); + classpath.addAll(testRunnerClasspath); + return classpath; + } } diff --git a/tools/runner/java/dalvik/runner/JtregFinder.java b/tools/runner/java/dalvik/runner/JtregFinder.java index c4e865c..d846ae2 100644 --- a/tools/runner/java/dalvik/runner/JtregFinder.java +++ b/tools/runner/java/dalvik/runner/JtregFinder.java @@ -65,7 +65,7 @@ class JtregFinder implements CodeFinder { try { logger.fine("scanning " + directoryToScan + " for jtreg tests"); File workDirectory = new File(localTemp, "JTwork"); - workDirectory.mkdirs(); + new Mkdir().mkdirs(workDirectory); /* * This code is capable of extracting test descriptions using jtreg 4.0 @@ -87,7 +87,8 @@ class JtregFinder implements CodeFinder { String testClass = description.getName(); result.add(new TestRun(description.getDir(), description.getFile(), testClass, suiteName, testName, qualifiedName, - description.getTitle(), JtregRunner.class)); + description.getTitle(), + getRunnerClass(), getRunnerJava(), getRunnerClasspath())); } return result; } catch (Exception jtregFailure) { @@ -123,4 +124,16 @@ class JtregFinder implements CodeFinder { private String escape(String s) { return s.replace('/', '.'); } + + public Class<? extends Runner> getRunnerClass() { + return JtregRunner.class; + } + + public File getRunnerJava() { + return new File(DalvikRunner.HOME_JAVA, "dalvik/runner/JtregRunner.java"); + } + + public Classpath getRunnerClasspath() { + return new Classpath(); + } } diff --git a/tools/runner/java/dalvik/runner/JtregRunner.java b/tools/runner/java/dalvik/runner/JtregRunner.java index 450c058..633a529 100644 --- a/tools/runner/java/dalvik/runner/JtregRunner.java +++ b/tools/runner/java/dalvik/runner/JtregRunner.java @@ -21,20 +21,19 @@ import java.lang.reflect.Method; /** * Runs a jtreg test. */ -public final class JtregRunner extends TestRunner { +public final class JtregRunner implements Runner { private Method main; - @Override public void prepareTest() { + public void prepareTest(Class<?> testClass) { try { - Class<?> testClass = Class.forName(className); main = testClass.getMethod("main", String[].class); } catch (Exception e) { throw new RuntimeException(e); } } - @Override public boolean test() { + public boolean test(Class<?> testClass) { try { main.invoke(null, new Object[] { new String[0] }); return true; @@ -43,8 +42,4 @@ public final class JtregRunner extends TestRunner { return false; } } - - public static void main(String[] args) { - new JtregRunner().run(); - } -}
\ No newline at end of file +} diff --git a/tools/runner/java/dalvik/runner/MainFinder.java b/tools/runner/java/dalvik/runner/MainFinder.java index 0ebcb43..282969f 100644 --- a/tools/runner/java/dalvik/runner/MainFinder.java +++ b/tools/runner/java/dalvik/runner/MainFinder.java @@ -31,7 +31,15 @@ class MainFinder extends NamingPatternCodeFinder { return "main"; } - @Override protected Class<? extends TestRunner> runnerClass() { + public Class<? extends Runner> getRunnerClass() { return MainRunner.class; } + + public File getRunnerJava() { + return new File(DalvikRunner.HOME_JAVA, "dalvik/runner/MainRunner.java"); + } + + public Classpath getRunnerClasspath() { + return new Classpath(); + } } diff --git a/tools/runner/java/dalvik/runner/MainRunner.java b/tools/runner/java/dalvik/runner/MainRunner.java index 6e993fe..34a4a47 100644 --- a/tools/runner/java/dalvik/runner/MainRunner.java +++ b/tools/runner/java/dalvik/runner/MainRunner.java @@ -21,20 +21,24 @@ import java.lang.reflect.Method; /** * Runs a Java class with a main method. */ -public final class MainRunner extends TestRunner { +public final class MainRunner implements Runner { - @Override public boolean test() { + private Method main; + + public void prepareTest(Class<?> testClass) { try { - Method mainMethod = Class.forName(className) - .getDeclaredMethod("main", String[].class); - mainMethod.invoke(null, new Object[] { new String[0] }); - } catch (Exception ex) { - ex.printStackTrace(); + main = testClass.getMethod("main", String[].class); + } catch (Exception e) { + throw new RuntimeException(e); } - return false; // always print main method output } - public static void main(String[] args) throws Exception { - new MainRunner().run(); + public boolean test(Class<?> testClass) { + try { + main.invoke(null, new Object[] { new String[0] }); + } catch (Throwable ex) { + ex.printStackTrace(); + } + return false; // always print main method output } } diff --git a/tools/runner/java/dalvik/runner/Md5Cache.java b/tools/runner/java/dalvik/runner/Md5Cache.java new file mode 100644 index 0000000..f6ba85d --- /dev/null +++ b/tools/runner/java/dalvik/runner/Md5Cache.java @@ -0,0 +1,115 @@ +/* + * 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 dalvik.runner; + +import java.io.File; +import java.io.FileInputStream; +import java.security.MessageDigest; +import java.util.logging.Logger; + +/** + * Caches content by MD5. + */ +public final class Md5Cache { + private static final Logger logger = Logger.getLogger(Md5Cache.class.getName()); + private static final File CACHE_ROOT = new File("/tmp/vogar-md5-cache/"); + + private final String keyPrefix; + + /** + * Creates a new cache accessor. There's only one directory on disk, so 'keyPrefix' is really + * just a convenience for humans inspecting the cache. + */ + public Md5Cache(String keyPrefix) { + this.keyPrefix = keyPrefix; + } + + /** + * Returns an ASCII hex representation of the MD5 of the content of 'file'. + */ + private static String md5(File file) { + byte[] digest = null; + try { + MessageDigest digester = MessageDigest.getInstance("MD5"); + byte[] bytes = new byte[8192]; + FileInputStream in = new FileInputStream(file); + try { + int byteCount; + while ((byteCount = in.read(bytes)) > 0) { + digester.update(bytes, 0, byteCount); + } + digest = digester.digest(); + } finally { + in.close(); + } + } catch (Exception cause) { + throw new RuntimeException("Unable to compute MD5 of \"" + file + "\"", cause); + } + return (digest == null) ? null : byteArrayToHexString(digest); + } + + private static String byteArrayToHexString(byte[] bytes) { + StringBuilder result = new StringBuilder(); + for (byte b : bytes) { + result.append(Integer.toHexString((b >> 4) & 0xf)); + result.append(Integer.toHexString(b & 0xf)); + } + return result.toString(); + } + + /** + * Returns the appropriate key for a dex file corresponding to the contents of 'classpath'. + * Returns null if we don't think it's possible to cache the given classpath. + */ + public File makeKey(Classpath classpath) { + // Do we have it in cache? + String key = keyPrefix; + for (File element : classpath.getElements()) { + // We only cache dexed .jar files, not directories. + if (!element.toString().endsWith(".jar")) { + return null; + } + key += "-" + md5(element); + } + return new File(CACHE_ROOT, key); + } + + /** + * Copy the file 'content' into the cache with the given 'key'. + * This method assumes you're using the appropriate key for the content (and has no way to + * check because the key is a function of the inputs that made the content, not the content + * itself). + * We accept a null so the caller doesn't have to pay attention to whether we think we can + * cache the content or not. + */ + public void insert(File key, File content) { + if (key == null) { + return; + } + logger.fine("inserting " + key); + if (!key.toString().startsWith(CACHE_ROOT.toString())) { + throw new IllegalArgumentException("key '" + key + "' not a valid cache key"); + } + // Make sure the cache exists first. + new Mkdir().mkdirs(CACHE_ROOT); + // Copy it onto the same file system first, then atomically move it into place. + // That way, if we fail, we don't leave anything dangerous lying around. + File temporary = new File(key + ".tmp"); + new Command.Builder().args("cp", content, temporary).execute(); + new Command.Builder().args("mv", temporary, key).execute(); + } +} diff --git a/tools/runner/java/dalvik/runner/Mkdir.java b/tools/runner/java/dalvik/runner/Mkdir.java new file mode 100644 index 0000000..46dcf08 --- /dev/null +++ b/tools/runner/java/dalvik/runner/Mkdir.java @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dalvik.runner; + +import java.io.File; + +/** + * A mkdir command. + */ +final class Mkdir { + + public void mkdirs(File directory) { + new Command("mkdir", "-p", directory.getPath()).execute(); + } +} diff --git a/tools/runner/java/dalvik/runner/Mode.java b/tools/runner/java/dalvik/runner/Mode.java new file mode 100644 index 0000000..c2d6b14 --- /dev/null +++ b/tools/runner/java/dalvik/runner/Mode.java @@ -0,0 +1,268 @@ +/* + * 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 dalvik.runner; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.FilenameFilter; +import java.io.IOException; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Properties; +import java.util.Set; +import java.util.concurrent.TimeoutException; +import java.util.logging.Logger; +import java.util.regex.Pattern; + +/** + * A Mode for running tests. Examples including running in a virtual + * machine either on the host or a device or within a specific context + * such as within an Activity. + */ +abstract class Mode { + + private static final Pattern JAVA_TEST_PATTERN = Pattern.compile("\\/(\\w)+\\.java$"); + + private static final Logger logger = Logger.getLogger(Mode.class.getName()); + + protected final Environment environment; + protected final long timeoutSeconds; + protected final File sdkJar; + + /** + * Set of Java files needed to built to tun the currently selected + * set of tests. We build a subset rather than all the files all + * the time to reduce dex packaging costs in the activity mode + * case. + */ + protected final Set<File> testRunnerJava = new HashSet<File>(); + + /** + * Classpath of testRunner on the host side including any + * supporting libraries for testRunnerJava. Useful for compiling + * testRunnerJava as well as executing it on the host. Execution + * on the device requires further packaging typically done by + * postCompileTestRunner. + */ + protected final Classpath testRunnerClasspath = new Classpath(); + + // TODO: this should be an immutable collection. + protected final Classpath testClasspath = Classpath.of( + new File("dalvik/libcore/tools/runner/lib/jsr305.jar"), + new File("dalvik/libcore/tools/runner/lib/guava.jar"), + new File("dalvik/libcore/tools/runner/lib/caliper.jar"), + // TODO: we should be able to work with a shipping SDK, not depend on out/... + // dalvik/libcore/**/test/ for junit + // TODO: jar up just the junit classes and drop the jar in our lib/ directory. + new File("out/target/common/obj/JAVA_LIBRARIES/core-tests_intermediates/classes.jar").getAbsoluteFile()); + + Mode(Environment environment, long timeoutSeconds, File sdkJar) { + this.environment = environment; + this.timeoutSeconds = timeoutSeconds; + this.sdkJar = sdkJar; + } + + /** + * Initializes the temporary directories and test harness necessary to run + * tests. + */ + protected void prepare(Set<File> testRunnerJava, Classpath testRunnerClasspath) { + this.testRunnerJava.add(new File(DalvikRunner.HOME_JAVA, "dalvik/runner/TestRunner.java")); + this.testRunnerJava.addAll(dalvikAnnotationSourceFiles()); + this.testRunnerJava.addAll(testRunnerJava); + this.testRunnerClasspath.addAll(testRunnerClasspath); + environment.prepare(); + compileTestRunner(); + } + + private List<File> dalvikAnnotationSourceFiles() { + // Hopefully one day we'll strip the dalvik annotations out, but until then we need to make + // them available to javac(1). + File sourceDir = new File("dalvik/libcore/dalvik/src/main/java/dalvik/annotation"); + File[] javaSourceFiles = sourceDir.listFiles(new FilenameFilter() { + public boolean accept(File dir, String filename) { + return filename.endsWith(".java"); + } + }); + return Arrays.asList(javaSourceFiles); + } + + private void compileTestRunner() { + logger.fine("build testrunner"); + + Classpath classpath = new Classpath(); + classpath.addAll(testClasspath); + classpath.addAll(testRunnerClasspath); + + File base = environment.testRunnerClassesDir(); + new Mkdir().mkdirs(base); + new Javac() + .bootClasspath(sdkJar) + .classpath(classpath) + .sourcepath(DalvikRunner.HOME_JAVA) + .destination(base) + .compile(testRunnerJava); + postCompileTestRunner(); + } + + /** + * Hook method called after TestRunner compilation. + */ + abstract protected void postCompileTestRunner(); + + /** + * Compiles classes for the given test and makes them ready for execution. + * If the test could not be compiled successfully, it will be updated with + * the appropriate test result. + */ + public void buildAndInstall(TestRun testRun) { + logger.fine("build " + testRun.getQualifiedName()); + + boolean testCompiled; + try { + testCompiled = compileTest(testRun); + if (!testCompiled) { + testRun.setResult(Result.UNSUPPORTED, Collections.<String>emptyList()); + return; + } + } catch (CommandFailedException e) { + testRun.setResult(Result.COMPILE_FAILED, e.getOutputLines()); + return; + } catch (IOException e) { + testRun.setResult(Result.ERROR, e); + return; + } + testRun.setTestCompiled(testCompiled); + environment.prepareUserDir(testRun); + } + + /** + * Compiles the classes for the described test. + * + * @return the path to the compiled classes (directory or jar), or {@code + * null} if the test could not be compiled. + * @throws CommandFailedException if javac fails + */ + private boolean compileTest(TestRun testRun) throws IOException { + if (!JAVA_TEST_PATTERN.matcher(testRun.getTestJava().toString()).find()) { + return false; + } + + String qualifiedName = testRun.getQualifiedName(); + File testClassesDir = environment.testClassesDir(testRun); + new Mkdir().mkdirs(testClassesDir); + FileOutputStream propertiesOut = new FileOutputStream( + new File(testClassesDir, TestProperties.FILE)); + Properties properties = new Properties(); + fillInProperties(properties, testRun); + properties.store(propertiesOut, "generated by " + Mode.class.getName()); + propertiesOut.close(); + + Classpath classpath = new Classpath(); + classpath.addAll(testClasspath); + classpath.addAll(testRun.getRunnerClasspath()); + + Set<File> sourceFiles = new HashSet<File>(); + sourceFiles.add(testRun.getTestJava()); + sourceFiles.addAll(dalvikAnnotationSourceFiles()); + + // compile the test case + new Javac() + .bootClasspath(sdkJar) + .classpath(classpath) + .sourcepath(testRun.getTestDirectory()) + .destination(testClassesDir) + .compile(sourceFiles); + postCompileTest(testRun); + return true; + } + + /** + * Hook method called after test compilation. + * + * @param testRun The test being compiled + */ + abstract protected void postCompileTest(TestRun testRun); + + + /** + * Fill in properties for running in this mode + */ + protected void fillInProperties(Properties properties, TestRun testRun) { + properties.setProperty(TestProperties.TEST_CLASS, testRun.getTestClass()); + properties.setProperty(TestProperties.QUALIFIED_NAME, testRun.getQualifiedName()); + properties.setProperty(TestProperties.RUNNER_CLASS, testRun.getRunnerClass().getName()); + } + + /** + * Runs the test, and updates its test result. + */ + void runTest(TestRun testRun) { + if (!testRun.isRunnable()) { + throw new IllegalArgumentException(); + } + + final List<Command> commands = buildCommands(testRun); + + List<String> output = null; + for (final Command command : commands) { + try { + output = command.executeWithTimeout(timeoutSeconds); + } catch (TimeoutException e) { + testRun.setResult(Result.EXEC_TIMEOUT, + Collections.singletonList("Exceeded timeout! (" + timeoutSeconds + "s)")); + return; + } catch (Exception e) { + testRun.setResult(Result.ERROR, e); + return; + } + } + // we only look at the output of the last command + if (output.isEmpty()) { + testRun.setResult(Result.ERROR, + Collections.singletonList("No output returned!")); + return; + } + + Result result = TestProperties.RESULT_SUCCESS.equals(output.get(output.size() - 1)) + ? Result.SUCCESS + : Result.EXEC_FAILED; + testRun.setResult(result, output.subList(0, output.size() - 1)); + } + + /** + * Returns commands for test execution. + */ + protected abstract List<Command> buildCommands(TestRun testRun); + + /** + * Deletes files and releases any resources required for the execution of + * the given test. + */ + void cleanup(TestRun testRun) { + environment.cleanup(testRun); + } + + /** + * Cleans up after all test runs have completed. + */ + void shutdown() { + environment.shutdown(); + } +} diff --git a/tools/runner/java/dalvik/runner/NamingPatternCodeFinder.java b/tools/runner/java/dalvik/runner/NamingPatternCodeFinder.java index d0b6459..19c9df2 100644 --- a/tools/runner/java/dalvik/runner/NamingPatternCodeFinder.java +++ b/tools/runner/java/dalvik/runner/NamingPatternCodeFinder.java @@ -32,7 +32,7 @@ abstract class NamingPatternCodeFinder implements CodeFinder { private final String PACKAGE_PATTERN = "(?m)^\\s*package\\s+(\\S+)\\s*;"; private final String TYPE_DECLARATION_PATTERN - = "(?m)\\b(?:public|private)\\s+(?:interface|class|enum)\\b"; + = "(?m)\\b(?:public|private)\\s+(?:final\\s+)?(?:interface|class|enum)\\b"; public Set<TestRun> findTests(File testDirectory) { Set<TestRun> result = new LinkedHashSet<TestRun>(); @@ -49,8 +49,6 @@ abstract class NamingPatternCodeFinder implements CodeFinder { protected abstract String testName(File file); - protected abstract Class<? extends TestRunner> runnerClass(); - private void findTestsRecursive(Set<TestRun> sink, File file) { if (file.isDirectory()) { for (File child : file.listFiles()) { @@ -68,7 +66,8 @@ abstract class NamingPatternCodeFinder implements CodeFinder { String testName = testName(file); String testDescription = null; sink.add(new TestRun(testDirectory, file, className, className, - testName, className, testDescription, runnerClass())); + testName, className, testDescription, + getRunnerClass(), getRunnerJava(), getRunnerClasspath())); } /** @@ -100,7 +99,7 @@ abstract class NamingPatternCodeFinder implements CodeFinder { if (Pattern.compile(TYPE_DECLARATION_PATTERN).matcher(content).find()) { return className; } - throw new IllegalArgumentException("Not a .java file: '" + file + "'\n"+content); + throw new IllegalArgumentException("Not a .java file: '" + file + "'\n" + content); } String packageName = packageMatcher.group(1); return packageName + "." + className; diff --git a/tools/runner/java/dalvik/runner/OptionParser.java b/tools/runner/java/dalvik/runner/OptionParser.java index 64af51c..3516264 100644 --- a/tools/runner/java/dalvik/runner/OptionParser.java +++ b/tools/runner/java/dalvik/runner/OptionParser.java @@ -288,6 +288,7 @@ public class OptionParser { } } + @SuppressWarnings("unchecked") private static void setValue(Object object, Field field, String arg, Handler handler, String valueText) { Object value = handler.translate(valueText); @@ -298,7 +299,7 @@ public class OptionParser { try { field.setAccessible(true); if (Collection.class.isAssignableFrom(field.getType())) { - Collection collection = (Collection)field.get(object); + Collection collection = (Collection) field.get(object); collection.add(value); } else { field.set(object, value); diff --git a/luni/src/test/java/org/apache/harmony/luni/tests/java/util/NullBenchmarkSuite.java b/tools/runner/java/dalvik/runner/Rm.java index 52f383c..1fc11d9 100644 --- a/luni/src/test/java/org/apache/harmony/luni/tests/java/util/NullBenchmarkSuite.java +++ b/tools/runner/java/dalvik/runner/Rm.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009 The Android Open Source Project + * 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. @@ -14,20 +14,28 @@ * limitations under the License. */ -package org.apache.harmony.luni.tests.java.util; +package dalvik.runner; -import com.google.caliper.Benchmark; -import com.google.caliper.SimpleBenchmark; +import java.io.File; /** - * This class exists only to force a dependency from our libraries on Caliper, - * our micro benchmarking framework. + * A rm command. */ -public class NullBenchmarkSuite extends SimpleBenchmark { +final class Rm { - public void timeNullBenchmark(int trials) throws Exception { - for (int i = 0; i < trials; i++) { - // code under test goes here! - } + public void file(File file) { + new Command.Builder() + .args("rm") + .args("-f") + .args(file) + .execute(); + } + + public void directoryTree(File directory) { + new Command.Builder() + .args("rm") + .args("-rf") + .args(directory) + .execute(); } } diff --git a/tools/runner/java/dalvik/runner/Runner.java b/tools/runner/java/dalvik/runner/Runner.java new file mode 100644 index 0000000..7d7b0ee --- /dev/null +++ b/tools/runner/java/dalvik/runner/Runner.java @@ -0,0 +1,28 @@ +/* + * 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 dalvik.runner; + +/** + * Interface between the generic TestRunner and the more specific + * backend implementations that know about specific types of tests. + */ +public interface Runner { + + public void prepareTest(Class<?> testClass); + + public boolean test(Class<?> testClass); +} diff --git a/tools/runner/java/dalvik/runner/TestProperties.java b/tools/runner/java/dalvik/runner/TestProperties.java new file mode 100644 index 0000000..1e90799 --- /dev/null +++ b/tools/runner/java/dalvik/runner/TestProperties.java @@ -0,0 +1,79 @@ +/* + * 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 dalvik.runner; + +/** + * TestProperties is a common class of constants shared between the + * DalvikRunner on the host and TestRunner classes potentially running + * on other devices. + */ +final public class TestProperties { + + /** + * The name of the test properties file within the {@code .jar} file. + */ + public static final String FILE = "test.properties"; + + /** + * Name of the property giving the test's main class name. This class should + * have a {@code public static void main(String[] args)} method. + */ + public static final String TEST_CLASS = "testClass"; + + /** + * Name of the property giving the test's name, such as {@code + * java.math.BigDecimal.PowTests}. + */ + public static final String QUALIFIED_NAME = "qualifiedName"; + + /** + * Name of the property used by TestRunner to determine which + * class to use as the Runner name. This class should implement + * Runner. + */ + public static final String RUNNER_CLASS = "runnerClass"; + + /** + * Name of the property used by TestActivity to the test directory. + */ + public static final String DEVICE_RUNNER_DIR = "deviceRunnerDir"; + + + /** + * The output file written by TestActivity + */ + public static final String RESULT_FILE = "result.txt"; + + /** + * Result value for successful test + */ + public static final String RESULT_SUCCESS = "SUCCESS"; + + /** + * Result value for failed test + */ + public static final String RESULT_FAILURE = "FAILURE"; + + public static String result(boolean success) { + return success ? RESULT_SUCCESS : RESULT_FAILURE; + } + + /** + * This class should not be instantiated + */ + private TestProperties() {} +} diff --git a/tools/runner/java/dalvik/runner/TestRun.java b/tools/runner/java/dalvik/runner/TestRun.java index 3e85d92..c610b25 100644 --- a/tools/runner/java/dalvik/runner/TestRun.java +++ b/tools/runner/java/dalvik/runner/TestRun.java @@ -35,33 +35,38 @@ import java.util.List; public final class TestRun { private final File testDirectory; - private final File javaFile; + private final File testJava; private final String testClass; - private final Class<? extends TestRunner> testRunner; + private final Class<? extends Runner> runnerClass; + private final File runnerJava; + private final Classpath runnerClasspath; private final String suiteName; private final String testName; private final String qualifiedName; private final String description; - private Classpath testClasspath; + private boolean testCompiled; private File userDir = new File(System.getProperty("user.dir")); private ExpectedResult expectedResult = ExpectedResult.SUCCESS; private Result result; private List<String> outputLines; - public TestRun(File testDirectory, File javaFile, String testClass, + public TestRun(File testDirectory, File testJava, String testClass, String suiteName, String testName, String qualifiedName, - String description, Class<? extends TestRunner> testRunner) { + String description, Class<? extends Runner> runnerClass, + File runnerJava, Classpath runnerClasspath) { this.qualifiedName = qualifiedName; this.suiteName = suiteName; this.testName = testName; this.testDirectory = testDirectory; - this.javaFile = javaFile; + this.testJava = testJava; this.description = description; this.testClass = testClass; - this.testRunner = testRunner; + this.runnerClass = runnerClass; + this.runnerJava = runnerJava; + this.runnerClasspath = runnerClasspath; } /** @@ -71,8 +76,8 @@ public final class TestRun { return testDirectory; } - public File getJavaFile() { - return javaFile; + public File getTestJava() { + return testJava; } /** @@ -118,15 +123,14 @@ public final class TestRun { } /** - * Initializes the path to the jar file or directory containing test - * classes. + * Set when the test is successfully compiled. */ - public void setTestClasspath(Classpath classpath) { - this.testClasspath = classpath; + public void setTestCompiled(boolean testCompiled) { + this.testCompiled = testCompiled; } - public Classpath getTestClasspath() { - return testClasspath; + public boolean getTestCompiled() { + return testCompiled; } /** @@ -145,7 +149,7 @@ public final class TestRun { * classpath prepared and have not yet been assigned a result. */ public boolean isRunnable() { - return testClasspath != null && result == null; + return testCompiled && result == null; } public void setResult(Result result, Throwable e) { @@ -154,7 +158,7 @@ public final class TestRun { public void setResult(Result result, List<String> outputLines) { if (this.result != null) { - throw new IllegalStateException(); + throw new IllegalStateException("result already set"); } this.result = result; @@ -176,8 +180,16 @@ public final class TestRun { return outputLines; } - public Class<? extends TestRunner> getTestRunner() { - return testRunner; + public Class<? extends Runner> getRunnerClass() { + return runnerClass; + } + + public File getRunnerJava() { + return runnerJava; + } + + public Classpath getRunnerClasspath() { + return runnerClasspath; } /** diff --git a/tools/runner/java/dalvik/runner/TestRunner.java b/tools/runner/java/dalvik/runner/TestRunner.java index af811d0..a706d40 100644 --- a/tools/runner/java/dalvik/runner/TestRunner.java +++ b/tools/runner/java/dalvik/runner/TestRunner.java @@ -23,57 +23,68 @@ import java.util.Properties; /** * Runs a test. */ -public abstract class TestRunner { +public class TestRunner { - /** - * The name of the test properties file within the {@code .jar} file. - */ - static final String TEST_PROPERTIES_FILE = "test.properties"; + protected final Properties properties; - /** - * Property identifier for the test's main class name. This class should - * have a {@code public static void main(String[] args)} method. - */ - static final String CLASS_NAME = "className"; + protected final String qualifiedName; + protected final Class<?> testClass; + protected final Class<?> runnerClass; - /** - * Property identifier for the test's name, such as {@code - * java.math.BigDecimal.PowTests}. - */ - static final String QUALIFIED_NAME = "qualifiedName"; - - protected String className; - protected String qualifiedName; + protected TestRunner () { + properties = loadProperties(); + qualifiedName = properties.getProperty(TestProperties.QUALIFIED_NAME); + testClass = classProperty(TestProperties.TEST_CLASS, Object.class); + runnerClass = classProperty(TestProperties.RUNNER_CLASS, Runner.class); + } - protected Properties loadProperties() { + protected static Properties loadProperties() { Properties properties = new Properties(); try { InputStream propertiesStream = TestRunner.class.getResourceAsStream( - "/" + TEST_PROPERTIES_FILE); + "/" + TestProperties.FILE); if (propertiesStream == null) { - throw new RuntimeException(TEST_PROPERTIES_FILE + " missing!"); + throw new RuntimeException(TestProperties.FILE + " missing!"); } - properties.load(propertiesStream); + return properties; } catch (IOException e) { throw new RuntimeException(e); } - - className = properties.getProperty(CLASS_NAME); - qualifiedName = properties.getProperty(QUALIFIED_NAME); - return properties; } - public void prepareTest() {} - - public abstract boolean test(); + private Class<?> classProperty(String propertyName, Class<?> superClass) { + String className = properties.getProperty(propertyName); + if (className == null) { + throw new IllegalArgumentException("Could not find property for " + + propertyName); + } + try { + Class<?> klass = Class.forName(className); + if (!superClass.isAssignableFrom(klass)) { + throw new IllegalArgumentException( + className + " can not be assigned to " + Runner.class); + } + return klass; + } catch (ClassNotFoundException e) { + throw new RuntimeException(e); + } + } - public void run() { - loadProperties(); - prepareTest(); + public boolean run() { + Runner runner; + try { + runner = (Runner) runnerClass.newInstance(); + } catch (InstantiationException e) { + throw new RuntimeException(e); + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } + runner.prepareTest(testClass); + return runner.test(testClass); + } - System.out.println("Executing " + qualifiedName); - boolean success = test(); - System.out.println(success ? "SUCCESS" : "FAILURE"); + public static void main(String[] args) { + System.out.println(TestProperties.result(new TestRunner().run())); } } diff --git a/tools/runner/java/dalvik/runner/Vm.java b/tools/runner/java/dalvik/runner/Vm.java index 3afd7ae..9f96ec5 100644 --- a/tools/runner/java/dalvik/runner/Vm.java +++ b/tools/runner/java/dalvik/runner/Vm.java @@ -27,249 +27,34 @@ import java.util.HashSet; import java.util.List; import java.util.Properties; import java.util.Set; -import java.util.concurrent.Callable; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; import java.util.logging.Logger; -import java.util.regex.Pattern; /** * A Java-like virtual machine for compiling and running tests. */ -public abstract class Vm { - - static final String DALVIK_RUNNER_HOME = "dalvik/libcore/tools/runner"; - - static final Set<File> TEST_RUNNER_JAVA_FILES = new HashSet<File>(Arrays.asList( - new File(DALVIK_RUNNER_HOME + "/java/dalvik/runner/CaliperRunner.java"), - new File(DALVIK_RUNNER_HOME + "/java/dalvik/runner/JUnitRunner.java"), - new File(DALVIK_RUNNER_HOME + "/java/dalvik/runner/JtregRunner.java"), - new File(DALVIK_RUNNER_HOME + "/java/dalvik/runner/MainRunner.java"), - new File(DALVIK_RUNNER_HOME + "/java/dalvik/runner/TestRunner.java"))); - - private final Pattern JAVA_TEST_PATTERN = Pattern.compile("\\/(\\w)+\\.java$"); - static final Classpath COMPILATION_CLASSPATH = Classpath.of( - new File("out/target/common/obj/JAVA_LIBRARIES/core_intermediates/classes.jar").getAbsoluteFile(), - new File("out/target/common/obj/JAVA_LIBRARIES/core-tests_intermediates/classes.jar").getAbsoluteFile(), - new File("out/target/common/obj/JAVA_LIBRARIES/jsr305_intermediates/classes.jar").getAbsoluteFile(), - new File("out/target/common/obj/JAVA_LIBRARIES/guava_intermediates/classes.jar").getAbsoluteFile(), - new File("out/target/common/obj/JAVA_LIBRARIES/caliper_intermediates/classes.jar").getAbsoluteFile()); +public abstract class Vm extends Mode { private static final Logger logger = Logger.getLogger(Vm.class.getName()); - protected final ExecutorService outputReaders - = Executors.newFixedThreadPool(1, Threads.daemonThreadFactory()); - - protected final Integer debugPort; protected final List<String> additionalVmArgs; - protected final long timeoutSeconds; - protected final File sdkJar; - protected final File localTemp; - protected final boolean clean; - - /** The path of the test runner's compiled classes */ - private Classpath testRunnerClasses; - Vm(Integer debugPort, long timeoutSeconds, File sdkJar, File localTemp, - List<String> additionalVmArgs, boolean clean) { - this.debugPort = debugPort; - this.timeoutSeconds = timeoutSeconds; - this.sdkJar = sdkJar; - this.localTemp = localTemp; + Vm(Environment environment, long timeoutSeconds, File sdkJar, + List<String> additionalVmArgs) { + super(environment, timeoutSeconds, sdkJar); this.additionalVmArgs = additionalVmArgs; - this.clean = clean; - } - - /** - * Initializes the temporary directories and test harness necessary to run - * tests. - */ - public void prepare() { - testRunnerClasses = compileTestRunner(); - } - - private Classpath compileTestRunner() { - logger.fine("build testrunner"); - - File base = new File(localTemp, "testrunner"); - base.mkdirs(); - new Javac() - .classpath(COMPILATION_CLASSPATH) - .destination(base) - .compile(TEST_RUNNER_JAVA_FILES); - - return postCompile("testrunner", Classpath.of(base)); } /** - * Cleans up after all test runs have completed. - */ - public void shutdown() { - outputReaders.shutdown(); - } - - /** - * Compiles classes for the given test and makes them ready for execution. - * If the test could not be compiled successfully, it will be updated with - * the appropriate test result. - */ - public void buildAndInstall(TestRun testRun) { - logger.fine("build " + testRun.getQualifiedName()); - - Classpath testClasses; - try { - testClasses = compileTest(testRun); - if (testClasses == null) { - testRun.setResult(Result.UNSUPPORTED, Collections.<String>emptyList()); - return; - } - } catch (CommandFailedException e) { - testRun.setResult(Result.COMPILE_FAILED, e.getOutputLines()); - return; - } catch (IOException e) { - testRun.setResult(Result.ERROR, e); - return; - } - testRun.setTestClasspath(testClasses); - prepareUserDir(testRun); - } - - /** - * Prepares the directory from which the test will be executed. Some tests - * expect to read data files from the current working directory; this step - * should ensure such files are available. - */ - protected void prepareUserDir(TestRun testRun) { - File testUserDir = testUserDir(testRun); - - // if the user dir exists, cp would copy the files to the wrong place - if (testUserDir.exists()) { - throw new IllegalStateException(); - } - - testUserDir.getParentFile().mkdirs(); - new Command("cp", "-r", testRun.getTestDirectory().toString(), - testUserDir.toString()).execute(); - testRun.setUserDir(testUserDir); - } - - /** - * Deletes files and releases any resources required for the execution of - * the given test. - */ - public void cleanup(TestRun testRun) { - if (clean) { - logger.fine("clean " + testRun.getQualifiedName()); - - new Command.Builder().args("rm", "-rf", testClassesDir(testRun).getPath()) - .execute(); - new Command.Builder().args("rm", "-rf", testUserDir(testRun).getPath()) - .execute(); - } - } - - /** - * Compiles the classes for the described test. - * - * @return the path to the compiled classes (directory or jar), or {@code - * null} if the test could not be compiled. - * @throws CommandFailedException if javac fails - */ - private Classpath compileTest(TestRun testRun) throws IOException { - if (!JAVA_TEST_PATTERN.matcher(testRun.getJavaFile().toString()).find()) { - return null; - } - - String qualifiedName = testRun.getQualifiedName(); - File testClassesDir = testClassesDir(testRun); - testClassesDir.mkdirs(); - - FileOutputStream propertiesOut = new FileOutputStream( - new File(testClassesDir, TestRunner.TEST_PROPERTIES_FILE)); - toProperties(testRun).store(propertiesOut, "generated by " + getClass().getName()); - propertiesOut.close(); - - // write a test descriptor - new Javac() - .bootClasspath(sdkJar) - .classpath(COMPILATION_CLASSPATH) - .sourcepath(testRun.getTestDirectory()) - .destination(testClassesDir) - .compile(testRun.getJavaFile()); - return postCompile(qualifiedName, Classpath.of(testClassesDir)); - } - - private File testClassesDir(TestRun testRun) { - return new File(localTemp, testRun.getQualifiedName()); - } - - private File testUserDir(TestRun testRun) { - File testTemp = new File(localTemp, "userDir"); - return new File(testTemp, testRun.getQualifiedName()); - } - - /** - * Returns a properties object for the given test description. - */ - static Properties toProperties(TestRun testRun) { - Properties result = new Properties(); - result.setProperty(TestRunner.CLASS_NAME, testRun.getTestClass()); - result.setProperty(TestRunner.QUALIFIED_NAME, testRun.getQualifiedName()); - return result; - } - - /** - * Runs the test, and updates its test result. + * Returns a VM for test execution. */ - public void runTest(TestRun testRun) { - if (!testRun.isRunnable()) { - throw new IllegalArgumentException(); - } - - final Command command = newVmCommandBuilder(testRun.getUserDir()) - .classpath(testRun.getTestClasspath()) - .classpath(testRunnerClasses) - .classpath(getRuntimeSupportClasspath()) + @Override protected List<Command> buildCommands(TestRun testRun) { + return Collections.singletonList(newVmCommandBuilder(testRun.getUserDir()) + .classpath(getRuntimeSupportClasspath(testRun)) .userDir(testRun.getUserDir()) - .debugPort(debugPort) + .debugPort(environment.debugPort) .vmArgs(additionalVmArgs) - .mainClass(testRun.getTestRunner().getName()) - .build(); - - logger.fine("executing " + command.getArgs()); - - try { - command.start(); - - // run on a different thread to allow a timeout - List<String> output = outputReaders.submit(new Callable<List<String>>() { - public List<String> call() throws Exception { - return command.gatherOutput(); - } - }).get(timeoutSeconds, TimeUnit.SECONDS); - - if (output.isEmpty()) { - testRun.setResult(Result.ERROR, - Collections.singletonList("No output returned!")); - return; - } - - Result result = "SUCCESS".equals(output.get(output.size() - 1)) - ? Result.SUCCESS - : Result.EXEC_FAILED; - testRun.setResult(result, output.subList(0, output.size() - 1)); - } catch (TimeoutException e) { - testRun.setResult(Result.EXEC_TIMEOUT, - Collections.singletonList("Exceeded timeout! (" + timeoutSeconds + "s)")); - } catch (Exception e) { - testRun.setResult(Result.ERROR, e); - } finally { - if (command.isStarted()) { - command.getProcess().destroy(); // to release the output reader - } - } + .mainClass(TestRunner.class.getName()) + .build()); } /** @@ -281,23 +66,7 @@ public abstract class Vm { * Returns the classpath containing JUnit and the dalvik annotations * required for test execution. */ - protected Classpath getRuntimeSupportClasspath() { - return COMPILATION_CLASSPATH; - } - - /** - * Hook method called after each compilation. - * - * @param name the name of this compilation unit. Usually a qualified test - * name like java.lang.Math.PowTests. - * @param targetClasses the full set of classes that make up this target. - * This will include the newly compiled classes, plus optional other - * classes that complete the target (such as library jars). - * @return the new result file. - */ - protected Classpath postCompile(String name, Classpath targetClasses) { - return targetClasses; - } + protected abstract Classpath getRuntimeSupportClasspath(TestRun testRun); /** * Builds a virtual machine command. diff --git a/tools/runner/lib/TestActivity.java b/tools/runner/lib/TestActivity.java new file mode 100644 index 0000000..15206f8 --- /dev/null +++ b/tools/runner/lib/TestActivity.java @@ -0,0 +1,105 @@ +/* + * 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 dalvik.runner; + +import android.app.Activity; +import android.os.Bundle; +import android.util.Log; +import android.widget.TextView; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.lang.reflect.Field; +import java.lang.reflect.Method; + +/** + * Runs a user-supplied {@code main(String[] args)} method + * in the context of an Android activity. The result of the method + * (success or exception) is reported to a file where Dalvik + * Runner can pick it up. + */ +public class TestActivity extends Activity { + + private final static String TAG = "TestActivity"; + + private TextView view; + + @Override public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + this.view = new TextView(this); + log("TestActivity starting..."); + setContentView(view); + ActivityRunner activityRunner = new ActivityRunner(); + activityRunner.run(); + } + + private void log(String message, Throwable ex) { + log(message + "\n" + Log.getStackTraceString(ex)); + } + private void log(String message) { + Log.i(TAG, message); + view.append(message + "\n"); + } + + class ActivityRunner extends TestRunner { + + private final File runnerDir; + private final Thread shutdownHook = new Thread(new ShutdownHook()); + + ActivityRunner() { + runnerDir = new File(properties.getProperty(TestProperties.DEVICE_RUNNER_DIR)); + } + + @Override public boolean run() { + log("Using " + runnerClass + " to run " + qualifiedName); + Runtime.getRuntime().addShutdownHook(shutdownHook); + boolean success = super.run(); + Runtime.getRuntime().removeShutdownHook(shutdownHook); + writeResultFile(success); + return success; + } + + private void writeResultFile (boolean success) { + String result = TestProperties.result(success); + File resultDir = new File(runnerDir, qualifiedName); + File resultTemp = new File(resultDir, TestProperties.RESULT_FILE + ".temp"); + File resultFile = new File(resultDir, TestProperties.RESULT_FILE); + log("TestActivity " + result + " " + resultFile); + try { + FileOutputStream resultOut = new FileOutputStream(resultTemp); + resultOut.write(result.getBytes("UTF-8")); + resultOut.close(); + // atomically rename since DalvikRunner will be polling for this + resultTemp.renameTo(resultFile); + } catch (IOException e) { + log("TestActivity could not create result file", e); + } + } + + /** + * Used to trap tests that try to exit on the their own. We + * treat this as a failure since they usually are calling + * System.exit with a non-zero value. + */ + class ShutdownHook implements Runnable { + public void run() { + writeResultFile(false); + } + } + } +} diff --git a/tools/runner/lib/caliper.jar b/tools/runner/lib/caliper.jar Binary files differnew file mode 100644 index 0000000..63a156a --- /dev/null +++ b/tools/runner/lib/caliper.jar diff --git a/tools/runner/lib/caliper.jar.txt b/tools/runner/lib/caliper.jar.txt new file mode 100644 index 0000000..d645695 --- /dev/null +++ b/tools/runner/lib/caliper.jar.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. diff --git a/tools/runner/lib/guava.jar b/tools/runner/lib/guava.jar Binary files differnew file mode 100644 index 0000000..39adc7f --- /dev/null +++ b/tools/runner/lib/guava.jar diff --git a/tools/runner/lib/guava.jar.txt b/tools/runner/lib/guava.jar.txt new file mode 100644 index 0000000..d645695 --- /dev/null +++ b/tools/runner/lib/guava.jar.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. diff --git a/tools/runner/lib/jsr305.jar b/tools/runner/lib/jsr305.jar Binary files differnew file mode 100644 index 0000000..57a62c1 --- /dev/null +++ b/tools/runner/lib/jsr305.jar diff --git a/tools/runner/lib/jsr305.jar.txt b/tools/runner/lib/jsr305.jar.txt new file mode 100644 index 0000000..6736681 --- /dev/null +++ b/tools/runner/lib/jsr305.jar.txt @@ -0,0 +1,28 @@ +Copyright (c) 2007-2009, JSR305 expert group +All rights reserved. + +http://www.opensource.org/licenses/bsd-license.php + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * Neither the name of the JSR305 expert group nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. diff --git a/tools/runner/test-dalvik-runner.sh b/tools/runner/test-dalvik-runner.sh new file mode 100755 index 0000000..1b9c35d --- /dev/null +++ b/tools/runner/test-dalvik-runner.sh @@ -0,0 +1,53 @@ +#!/bin/bash +# +# 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. +# + +# Test Dalvik Runner by exercising the various modes and various types of tests +# +# You can run this as follows + +# $ANDROID_BUILD_TOP/dalvik/libcore/tools/runner/test-dalvik-runner.sh + +modes="host device activity" + +# TODO: include dummy examples of each kind of 'runnable' we support, +# for test purposes instead of relying on external paths. +test_jtreg=/home/dalvik-prebuild/openjdk-6/jdk/test/java/util/HashMap/ +test_junit=dalvik/libcore/logging/src/test/java/org/apache/harmony/logging/tests/java/util/logging/FilterTest.java +test_caliper=/home/bdc/benchmarks/caliper/caliper-read-only/src/examples/ArraySortBenchmark.java +test_main=external/junit/src/junit/textui/TestRunner.java +tests="$test_jtreg $test_junit $test_caliper $test_main" + +cd $ANDROID_BUILD_TOP +. ./build/envsetup.sh +m core-tests junit caliper snod && adb reboot bootloader && fastboot flashall && adb wait-for-device +# when the device first comes up /sdcard is not mounted +while [ -z "`adb shell ls /sdcard | tr -d '\r\n'`" ] ; do sleep 1; done +mmm dalvik/libcore/tools/runner + +#verbose=--verbose +#clean=--no-clean-after +extras="$verbose $clean" + +dalvik_runner="java -cp out/host/linux-x86/framework/dalvik_runner.jar dalvik.runner.DalvikRunner" + +for mode in $modes; do + for test in $tests; do + command="$dalvik_runner --mode $mode $extras $test" + echo RUNNING $command + $command + done +done diff --git a/tools/runner/vogar b/tools/runner/vogar new file mode 100755 index 0000000..e5a6ad0 --- /dev/null +++ b/tools/runner/vogar @@ -0,0 +1,21 @@ +#!/bin/bash +# +# 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. + +# m core-tests junit caliper snod && adb reboot bootloader && fastboot flashall && adb wait-for-device +# mmm dalvik/libcore/tools/runner + +classpath=`dirname $0`/../../../../out/host/linux-x86/framework/dalvik_runner.jar +exec java -cp $classpath dalvik.runner.DalvikRunner "$@" diff --git a/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLSocketImpl.java b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLSocketImpl.java index d287bbd..8006757 100644 --- a/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLSocketImpl.java +++ b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLSocketImpl.java @@ -68,6 +68,9 @@ public class OpenSSLSocketImpl extends javax.net.ssl.SSLSocket { private ArrayList<HandshakeCompletedListener> listeners; private long ssl_op_no = 0x00000000L; private int timeout = 0; + // BEGIN android-added + private int handshakeTimeout = -1; // -1 = same as timeout; 0 = infinite + // END android-added private InetSocketAddress address; private static final String[] supportedProtocols = new String[] { @@ -298,6 +301,14 @@ public class OpenSSLSocketImpl extends javax.net.ssl.SSLSocket { if (session == null && !sslParameters.getEnableSessionCreation()) { throw new SSLHandshakeException("SSL Session may not be created"); } else { + // BEGIN android-added + // Temporarily use a different timeout for the handshake process + int savedTimeout = timeout; + if (handshakeTimeout >= 0) { + setSoTimeout(handshakeTimeout); + } + // END android-added + Socket socket = this.socket != null ? this.socket : this; int sessionId = session != null ? session.session : 0; boolean reusedSession; @@ -360,6 +371,13 @@ public class OpenSSLSocketImpl extends javax.net.ssl.SSLSocket { throw new SSLException("Not trusted server certificate", e); } } + + // BEGIN android-added + // Restore the original timeout now that the handshake is complete + if (handshakeTimeout >= 0) { + setSoTimeout(savedTimeout); + } + // END android-added } if (listeners != null) { @@ -882,6 +900,18 @@ public class OpenSSLSocketImpl extends javax.net.ssl.SSLSocket { this.timeout = timeout; } + // BEGIN android-added + /** + * Set the handshake timeout on this socket. This timeout is specified in + * milliseconds and will be used only during the handshake process. + * + * @param timeout the handshake timeout value + */ + public synchronized void setHandshakeTimeout(int timeout) throws SocketException { + this.handshakeTimeout = timeout; + } + // END android-added + private native void nativeinterrupt() throws IOException; private native void nativeclose() throws IOException; diff --git a/xml/src/main/java/org/apache/harmony/xml/dom/AttrImpl.java b/xml/src/main/java/org/apache/harmony/xml/dom/AttrImpl.java index 9c19835..4e689fb 100644 --- a/xml/src/main/java/org/apache/harmony/xml/dom/AttrImpl.java +++ b/xml/src/main/java/org/apache/harmony/xml/dom/AttrImpl.java @@ -73,7 +73,7 @@ public class AttrImpl extends NodeImpl implements Attr { throw new DOMException(DOMException.NAMESPACE_ERR, localName); } - if (!document.isXMLIdentifier(localName)) { + if (!DocumentImpl.isXMLIdentifier(localName)) { throw new DOMException(DOMException.INVALID_CHARACTER_ERR, localName); } @@ -90,11 +90,11 @@ public class AttrImpl extends NodeImpl implements Attr { String prefix = name.substring(0, prefixSeparator); String localName = name.substring(prefixSeparator + 1); - if (!document.isXMLIdentifier(prefix) || !document.isXMLIdentifier(localName)) { + if (!DocumentImpl.isXMLIdentifier(prefix) || !DocumentImpl.isXMLIdentifier(localName)) { throw new DOMException(DOMException.INVALID_CHARACTER_ERR, name); } } else { - if (!document.isXMLIdentifier(name)) { + if (!DocumentImpl.isXMLIdentifier(name)) { throw new DOMException(DOMException.INVALID_CHARACTER_ERR, name); } } @@ -108,7 +108,9 @@ public class AttrImpl extends NodeImpl implements Attr { } public String getName() { - return (prefix != null ? prefix + ":" : "") + localName; + return prefix != null + ? prefix + ":" + localName + : localName; } @Override @@ -148,27 +150,8 @@ public class AttrImpl extends NodeImpl implements Attr { } @Override - public void setNodeValue(String value) throws DOMException { - setValue(value); - } - - @Override public void setPrefix(String prefix) { - if (!namespaceAware) { - throw new DOMException(DOMException.NAMESPACE_ERR, prefix); - } - - if (prefix != null) { - if (namespaceURI == null || !document.isXMLIdentifier(prefix) || "xmlns".equals(prefix)) { - throw new DOMException(DOMException.NAMESPACE_ERR, prefix); - } - - if ("xml".equals(prefix) && !"http://www.w3.org/XML/1998/namespace".equals(namespaceURI)) { - throw new DOMException(DOMException.NAMESPACE_ERR, prefix); - } - } - - this.prefix = prefix; + this.prefix = validatePrefix(prefix, namespaceAware, namespaceURI); } public void setValue(String value) throws DOMException { diff --git a/xml/src/main/java/org/apache/harmony/xml/dom/DOMImplementationImpl.java b/xml/src/main/java/org/apache/harmony/xml/dom/DOMImplementationImpl.java index 861f0a3..834cc47 100644 --- a/xml/src/main/java/org/apache/harmony/xml/dom/DOMImplementationImpl.java +++ b/xml/src/main/java/org/apache/harmony/xml/dom/DOMImplementationImpl.java @@ -50,17 +50,24 @@ public class DOMImplementationImpl implements DOMImplementation { } public boolean hasFeature(String feature, String version) { - // We claim to support DOM Core Level 1 & 2, nothing else. + boolean anyVersion = version == null || version.length() == 0; + if (feature.startsWith("+")) { + feature = feature.substring(1); + } - // TODO + // TODO: fully implement these APIs: + // "LS" (org.w3c.dom.ls) versions "3.0" + // "ElementTraversal" (org.w3c.dom.traversal) versions "1.0" - if ("Core".equalsIgnoreCase(feature) || "XML".equalsIgnoreCase(feature)) { - if (version == null || "".equals(version) || "1.0".equals(version) || "2.0".equals(version)) { - return true; - } + if (feature.equalsIgnoreCase("Core")) { + return anyVersion || version.equals("1.0") || version.equals("2.0") || version.equals("3.0"); + } else if (feature.equalsIgnoreCase("XML")) { + return anyVersion || version.equals("1.0") || version.equals("2.0") || version.equals("3.0"); + } else if (feature.equalsIgnoreCase("XMLVersion")) { + return anyVersion || version.equals("1.0") || version.equals("1.1"); + } else { + return false; } - - return false; } /** diff --git a/xml/src/main/java/org/apache/harmony/xml/dom/DocumentImpl.java b/xml/src/main/java/org/apache/harmony/xml/dom/DocumentImpl.java index 499f518..c8819cb 100644 --- a/xml/src/main/java/org/apache/harmony/xml/dom/DocumentImpl.java +++ b/xml/src/main/java/org/apache/harmony/xml/dom/DocumentImpl.java @@ -100,6 +100,8 @@ public class DocumentImpl extends InnerNodeImpl implements Document { * @return The new node. */ Node cloneNode(Node node, boolean deep) throws DOMException { + // TODO: callback the UserDataHandler with a NODE_CLONED event + Node target; switch (node.getNodeType()) { @@ -279,6 +281,7 @@ public class DocumentImpl extends InnerNodeImpl implements Document { } public Node importNode(Node importedNode, boolean deep) throws DOMException { + // TODO: callback the UserDataHandler with a NODE_IMPORTED event return cloneNode(importedNode, deep); } @@ -296,6 +299,10 @@ public class DocumentImpl extends InnerNodeImpl implements Document { return super.insertChildAt(newChild, index); } + @Override public String getTextContent() throws DOMException { + return null; + } + public String getInputEncoding() { throw new UnsupportedOperationException(); // TODO } @@ -337,6 +344,7 @@ public class DocumentImpl extends InnerNodeImpl implements Document { } public Node adoptNode(Node source) throws DOMException { + // TODO: callback the UserDataHandler with a NODE_ADOPTED event throw new UnsupportedOperationException(); // TODO } @@ -350,6 +358,7 @@ public class DocumentImpl extends InnerNodeImpl implements Document { public Node renameNode(Node n, String namespaceURI, String qualifiedName) throws DOMException { + // TODO: callback the UserDataHandler with a NODE_RENAMED event throw new UnsupportedOperationException(); // TODO } } diff --git a/xml/src/main/java/org/apache/harmony/xml/dom/DocumentTypeImpl.java b/xml/src/main/java/org/apache/harmony/xml/dom/DocumentTypeImpl.java index df40d4b..67947b7 100644 --- a/xml/src/main/java/org/apache/harmony/xml/dom/DocumentTypeImpl.java +++ b/xml/src/main/java/org/apache/harmony/xml/dom/DocumentTypeImpl.java @@ -107,4 +107,7 @@ public class DocumentTypeImpl extends LeafNodeImpl implements DocumentType { return systemId; } + @Override public String getTextContent() throws DOMException { + return null; + } } diff --git a/xml/src/main/java/org/apache/harmony/xml/dom/ElementImpl.java b/xml/src/main/java/org/apache/harmony/xml/dom/ElementImpl.java index 230e444..df1383d 100644 --- a/xml/src/main/java/org/apache/harmony/xml/dom/ElementImpl.java +++ b/xml/src/main/java/org/apache/harmony/xml/dom/ElementImpl.java @@ -65,7 +65,7 @@ public class ElementImpl extends InnerNodeImpl implements Element { qualifiedName = qualifiedName.substring(p + 1); } - if (!document.isXMLIdentifier(qualifiedName)) { + if (!DocumentImpl.isXMLIdentifier(qualifiedName)) { throw new DOMException(DOMException.INVALID_CHARACTER_ERR, qualifiedName); } @@ -82,11 +82,11 @@ public class ElementImpl extends InnerNodeImpl implements Element { String prefix = name.substring(0, p); String localName = name.substring(p + 1); - if (!document.isXMLIdentifier(prefix) || !document.isXMLIdentifier(localName)) { + if (!DocumentImpl.isXMLIdentifier(prefix) || !DocumentImpl.isXMLIdentifier(localName)) { throw new DOMException(DOMException.INVALID_CHARACTER_ERR, name); } } else { - if (!document.isXMLIdentifier(name)) { + if (!DocumentImpl.isXMLIdentifier(name)) { throw new DOMException(DOMException.INVALID_CHARACTER_ERR, name); } } @@ -241,7 +241,9 @@ public class ElementImpl extends InnerNodeImpl implements Element { } public String getTagName() { - return (prefix != null ? prefix + ":" : "") + localName; + return prefix != null + ? prefix + ":" + localName + : localName; } public boolean hasAttribute(String name) { @@ -281,7 +283,7 @@ public class ElementImpl extends InnerNodeImpl implements Element { throw new DOMException(DOMException.NOT_FOUND_ERR, null); } - attributes.remove(oldAttr); + attributes.remove(oldAttrImpl); oldAttrImpl.ownerElement = null; return oldAttrImpl; @@ -362,21 +364,7 @@ public class ElementImpl extends InnerNodeImpl implements Element { @Override public void setPrefix(String prefix) { - if (!namespaceAware) { - throw new DOMException(DOMException.NAMESPACE_ERR, prefix); - } - - if (prefix != null) { - if (namespaceURI == null || !document.isXMLIdentifier(prefix)) { - throw new DOMException(DOMException.NAMESPACE_ERR, prefix); - } - - if ("xml".equals(prefix) && !"http://www.w3.org/XML/1998/namespace".equals(namespaceURI)) { - throw new DOMException(DOMException.NAMESPACE_ERR, prefix); - } - } - - this.prefix = prefix; + this.prefix = validatePrefix(prefix, namespaceAware, namespaceURI); } public class ElementAttrNamedNodeMapImpl implements NamedNodeMap { diff --git a/xml/src/main/java/org/apache/harmony/xml/dom/InnerNodeImpl.java b/xml/src/main/java/org/apache/harmony/xml/dom/InnerNodeImpl.java index f8ed85e..275bbf3 100644 --- a/xml/src/main/java/org/apache/harmony/xml/dom/InnerNodeImpl.java +++ b/xml/src/main/java/org/apache/harmony/xml/dom/InnerNodeImpl.java @@ -28,14 +28,16 @@ import java.util.List; * Provides a straightforward implementation of the corresponding W3C DOM * interface. The class is used internally only, thus only notable members that * are not in the original interface are documented (the W3C docs are quite - * extensive). Hope that's ok. - * <p> - * Some of the fields may have package visibility, so other classes belonging to - * the DOM implementation can easily access them while maintaining the DOM tree - * structure. - * <p> - * This class represents a Node that has a parent Node as well as (potentially) - * a number of children. + * extensive). + * + * <p>Some of the fields may have package visibility, so other classes belonging + * to the DOM implementation can easily access them while maintaining the DOM + * tree structure. + * + * <p>This class represents a Node that has a parent Node as well as + * (potentially) a number of children. + * + * <p>Some code was adapted from Apache Xerces. */ public abstract class InnerNodeImpl extends LeafNodeImpl { @@ -218,4 +220,35 @@ public abstract class InnerNodeImpl extends LeafNodeImpl { return oldChildImpl; } + public String getTextContent() throws DOMException { + Node child = getFirstChild(); + if (child == null) { + return ""; + } + + Node next = child.getNextSibling(); + if (next == null) { + return hasTextContent(child) ? child.getTextContent() : ""; + } + + StringBuilder buf = new StringBuilder(); + getTextContent(buf); + return buf.toString(); + } + + void getTextContent(StringBuilder buf) throws DOMException { + Node child = getFirstChild(); + while (child != null) { + if (hasTextContent(child)) { + ((NodeImpl) child).getTextContent(buf); + } + child = child.getNextSibling(); + } + } + + final boolean hasTextContent(Node child) { + // TODO: skip text nodes with ignorable whitespace? + return child.getNodeType() != Node.COMMENT_NODE + && child.getNodeType() != Node.PROCESSING_INSTRUCTION_NODE; + } } diff --git a/xml/src/main/java/org/apache/harmony/xml/dom/NodeImpl.java b/xml/src/main/java/org/apache/harmony/xml/dom/NodeImpl.java index bf4d791..ebfdd52 100644 --- a/xml/src/main/java/org/apache/harmony/xml/dom/NodeImpl.java +++ b/xml/src/main/java/org/apache/harmony/xml/dom/NodeImpl.java @@ -16,24 +16,30 @@ package org.apache.harmony.xml.dom; +import org.w3c.dom.Attr; +import org.w3c.dom.CharacterData; import org.w3c.dom.DOMException; import org.w3c.dom.Document; +import org.w3c.dom.Element; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; import org.w3c.dom.NodeList; +import org.w3c.dom.ProcessingInstruction; import org.w3c.dom.UserDataHandler; +import java.util.ArrayList; +import java.util.List; + /** - * Provides a straightforward implementation of the corresponding W3C DOM - * interface. The class is used internally only, thus only notable members that - * are not in the original interface are documented (the W3C docs are quite - * extensive). Hope that's ok. - * <p> - * Some of the fields may have package visibility, so other classes belonging to - * the DOM implementation can easily access them while maintaining the DOM tree - * structure. - * <p> - * This class represents a Node that has neither a parent nor children. + * A straightforward implementation of the corresponding W3C DOM node. + * + * <p>Some fields have package visibility so other classes can access them while + * maintaining the DOM structure. + * + * <p>This class represents a Node that has neither a parent nor children. + * Subclasses may have either. + * + * <p>Some code was adapted from Apache Xerces. */ public abstract class NodeImpl implements Node { @@ -135,13 +141,64 @@ public abstract class NodeImpl implements Node { throw new DOMException(DOMException.HIERARCHY_REQUEST_ERR, null); } - public void setNodeValue(String nodeValue) throws DOMException { + public final void setNodeValue(String nodeValue) throws DOMException { + switch (getNodeType()) { + case CDATA_SECTION_NODE: + case COMMENT_NODE: + case TEXT_NODE: + ((CharacterData) this).setData(nodeValue); + return; + + case PROCESSING_INSTRUCTION_NODE: + ((ProcessingInstruction) this).setData(nodeValue); + return; + + case ATTRIBUTE_NODE: + ((Attr) this).setValue(nodeValue); + return; + + case ELEMENT_NODE: + case ENTITY_REFERENCE_NODE: + case ENTITY_NODE: + case DOCUMENT_NODE: + case DOCUMENT_TYPE_NODE: + case DOCUMENT_FRAGMENT_NODE: + case NOTATION_NODE: + return; // do nothing! + + default: + throw new DOMException(DOMException.NOT_SUPPORTED_ERR, + "Unsupported node type " + getNodeType()); + } } public void setPrefix(String prefix) throws DOMException { } /** + * Validates the element or attribute namespace prefix on this node. + * + * @param namespaceAware whether this node is namespace aware + * @param namespaceURI this node's namespace URI + */ + protected String validatePrefix(String prefix, boolean namespaceAware, String namespaceURI) { + if (!namespaceAware) { + throw new DOMException(DOMException.NAMESPACE_ERR, prefix); + } + + if (prefix != null) { + if (namespaceURI == null + || !DocumentImpl.isXMLIdentifier(prefix) + || "xml".equals(prefix) && !"http://www.w3.org/XML/1998/namespace".equals(namespaceURI) + || "xmlns".equals(prefix) && !"http://www.w3.org/2000/xmlns/".equals(namespaceURI)) { + throw new DOMException(DOMException.NAMESPACE_ERR, prefix); + } + } + + return prefix; + } + + /** * Checks whether a required string matches an actual string. This utility * method is used for comparing namespaces and such. It takes into account * null arguments and the "*" special case. @@ -190,7 +247,34 @@ public abstract class NodeImpl implements Node { } public String getBaseURI() { - throw new UnsupportedOperationException(); // TODO + /* + * TODO: implement. For reference, here's Xerces' behaviour: + * + * In all cases, the returned URI should be sanitized before it is + * returned. If the URI is malformed, null should be returned instead. + * + * For document nodes, this should return a member field that's + * initialized by the parser. + * + * For element nodes, this should first look for the xml:base attribute. + * if that exists and is absolute, it should be returned. + * if that exists and is relative, it should be resolved to the parent's base URI + * if it doesn't exist, the parent's baseURI should be returned + * + * For entity nodes, if a base URI exists that should be returned. + * Otherwise the document's base URI should be returned + * + * For entity references, if a base URI exists that should be returned + * otherwise it dereferences the entity (via the document) and uses the + * entity's base URI. + * + * For notations, it returns the base URI field. + * + * For processing instructions, it returns the parent's base URI. + * + * For all other node types, it returns null. + */ + return null; } public short compareDocumentPosition(Node other) @@ -199,35 +283,308 @@ public abstract class NodeImpl implements Node { } public String getTextContent() throws DOMException { - throw new UnsupportedOperationException(); // TODO + return getNodeValue(); } - public void setTextContent(String textContent) throws DOMException { - throw new UnsupportedOperationException(); // TODO + void getTextContent(StringBuilder buf) throws DOMException { + String content = getNodeValue(); + if (content != null) { + buf.append(content); + } + } + + public final void setTextContent(String textContent) throws DOMException { + switch (getNodeType()) { + case DOCUMENT_TYPE_NODE: + case DOCUMENT_NODE: + return; // do nothing! + + case ELEMENT_NODE: + case ENTITY_NODE: + case ENTITY_REFERENCE_NODE: + case DOCUMENT_FRAGMENT_NODE: + // remove all existing children + Node child; + while ((child = getFirstChild()) != null) { + removeChild(child); + } + // create a text node to hold the given content + if (textContent != null && textContent.length() != 0){ + appendChild(getOwnerDocument().createTextNode(textContent)); + } + return; + + case ATTRIBUTE_NODE: + case TEXT_NODE: + case CDATA_SECTION_NODE: + case PROCESSING_INSTRUCTION_NODE: + case COMMENT_NODE: + case NOTATION_NODE: + setNodeValue(textContent); + return; + + default: + throw new DOMException(DOMException.NOT_SUPPORTED_ERR, + "Unsupported node type " + getNodeType()); + } } public boolean isSameNode(Node other) { - throw new UnsupportedOperationException(); // TODO + return this == other; } - public String lookupPrefix(String namespaceURI) { - throw new UnsupportedOperationException(); // TODO + /** + * Returns the element whose namespace definitions apply to this node. Use + * this element when mapping prefixes to URIs and vice versa. + */ + private NodeImpl getNamespacingElement() { + switch (this.getNodeType()) { + case ELEMENT_NODE: + return this; + + case DOCUMENT_NODE: + return (NodeImpl) ((Document) this).getDocumentElement(); + + case ENTITY_NODE: + case NOTATION_NODE: + case DOCUMENT_FRAGMENT_NODE: + case DOCUMENT_TYPE_NODE: + return null; + + case ATTRIBUTE_NODE: + return (NodeImpl) ((Attr) this).getOwnerElement(); + + case TEXT_NODE: + case CDATA_SECTION_NODE: + case ENTITY_REFERENCE_NODE: + case PROCESSING_INSTRUCTION_NODE: + case COMMENT_NODE: + return getContainingElement(); + + default: + throw new DOMException(DOMException.NOT_SUPPORTED_ERR, + "Unsupported node type " + getNodeType()); + } } - public boolean isDefaultNamespace(String namespaceURI) { - throw new UnsupportedOperationException(); // TODO + /** + * Returns the nearest ancestor element that contains this node. + */ + private NodeImpl getContainingElement() { + for (Node p = getParentNode(); p != null; p = p.getParentNode()) { + if (p.getNodeType() == ELEMENT_NODE) { + return (NodeImpl) p; + } + } + return null; } - public String lookupNamespaceURI(String prefix) { - throw new UnsupportedOperationException(); // TODO + public final String lookupPrefix(String namespaceURI) { + if (namespaceURI == null) { + return null; + } + + // the XML specs define some prefixes (like "xml" and "xmlns") but this + // API is explicitly defined to ignore those. + + NodeImpl target = getNamespacingElement(); + for (NodeImpl node = target; node != null; node = node.getContainingElement()) { + // check this element's namespace first + if (namespaceURI.equals(node.getNamespaceURI()) + && target.isPrefixMappedToUri(node.getPrefix(), namespaceURI)) { + return node.getPrefix(); + } + + // search this element for an attribute of this form: + // xmlns:foo="http://namespaceURI" + if (!node.hasAttributes()) { + continue; + } + NamedNodeMap attributes = node.getAttributes(); + for (int i = 0, length = attributes.getLength(); i < length; i++) { + Node attr = attributes.item(i); + if (!"http://www.w3.org/2000/xmlns/".equals(attr.getNamespaceURI()) + || !"xmlns".equals(attr.getPrefix()) + || !namespaceURI.equals(attr.getNodeValue())) { + continue; + } + if (target.isPrefixMappedToUri(attr.getLocalName(), namespaceURI)) { + return attr.getLocalName(); + } + } + } + + return null; } - public boolean isEqualNode(Node arg) { - throw new UnsupportedOperationException(); // TODO + /** + * Returns true if the given prefix is mapped to the given URI on this + * element. Since child elements can redefine prefixes, this check is + * necessary: {@code + * <foo xmlns:a="http://good"> + * <bar xmlns:a="http://evil"> + * <a:baz /> + * </bar> + * </foo>} + * + * @param prefix the prefix to find. Nullable. + * @param uri the URI to match. Non-null. + */ + boolean isPrefixMappedToUri(String prefix, String uri) { + if (prefix == null) { + return false; + } + + String actual = lookupNamespaceURI(prefix); + return uri.equals(actual); + } + + public final boolean isDefaultNamespace(String namespaceURI) { + String actual = lookupNamespaceURI(null); // null yields the default namespace + return namespaceURI == null + ? actual == null + : namespaceURI.equals(actual); + } + + public final String lookupNamespaceURI(String prefix) { + NodeImpl target = getNamespacingElement(); + for (NodeImpl node = target; node != null; node = node.getContainingElement()) { + // check this element's namespace first + String nodePrefix = node.getPrefix(); + if (node.getNamespaceURI() != null) { + if (prefix == null // null => default prefix + ? nodePrefix == null + : prefix.equals(nodePrefix)) { + return node.getNamespaceURI(); + } + } + + // search this element for an attribute of the appropriate form. + // default namespace: xmlns="http://resultUri" + // non default: xmlns:specifiedPrefix="http://resultUri" + if (!node.hasAttributes()) { + continue; + } + NamedNodeMap attributes = node.getAttributes(); + for (int i = 0, length = attributes.getLength(); i < length; i++) { + Node attr = attributes.item(i); + if (!"http://www.w3.org/2000/xmlns/".equals(attr.getNamespaceURI())) { + continue; + } + if (prefix == null // null => default prefix + ? "xmlns".equals(attr.getNodeName()) + : "xmlns".equals(attr.getPrefix()) && prefix.equals(attr.getLocalName())) { + String value = attr.getNodeValue(); + return value.length() > 0 ? value : null; + } + } + } + + return null; } - public Object getFeature(String feature, String version) { - throw new UnsupportedOperationException(); // TODO + /** + * Returns a list of objects such that two nodes are equal if their lists + * are equal. Be careful: the lists may contain NamedNodeMaps and Nodes, + * neither of which override Object.equals(). Such values must be compared + * manually. + */ + private static List<Object> createEqualityKey(Node node) { + List<Object> values = new ArrayList<Object>(); + values.add(node.getNodeType()); + values.add(node.getNodeName()); + values.add(node.getLocalName()); + values.add(node.getNamespaceURI()); + values.add(node.getPrefix()); + values.add(node.getNodeValue()); + for (Node child = node.getFirstChild(); child != null; child = child.getNextSibling()) { + values.add(child); + } + + switch (node.getNodeType()) { + case DOCUMENT_TYPE_NODE: + DocumentTypeImpl doctype = (DocumentTypeImpl) node; + values.add(doctype.getPublicId()); + values.add(doctype.getSystemId()); + values.add(doctype.getInternalSubset()); + values.add(doctype.getEntities()); + values.add(doctype.getNotations()); + break; + + case ELEMENT_NODE: + Element element = (Element) node; + values.add(element.getAttributes()); + break; + } + + return values; + } + + public final boolean isEqualNode(Node arg) { + if (arg == this) { + return true; + } + + List<Object> listA = createEqualityKey(this); + List<Object> listB = createEqualityKey(arg); + + if (listA.size() != listB.size()) { + return false; + } + + for (int i = 0; i < listA.size(); i++) { + Object a = listA.get(i); + Object b = listB.get(i); + + if (a == b) { + continue; + + } else if (a == null || b == null) { + return false; + + } else if (a instanceof String || a instanceof Short) { + if (!a.equals(b)) { + return false; + } + + } else if (a instanceof NamedNodeMap) { + if (!(b instanceof NamedNodeMap) + || !namedNodeMapsEqual((NamedNodeMap) a, (NamedNodeMap) b)) { + return false; + } + + } else if (a instanceof Node) { + if (!(b instanceof Node) + || !((Node) a).isEqualNode((Node) b)) { + return false; + } + + } else { + throw new AssertionError(); // unexpected type + } + } + + return true; + } + + private boolean namedNodeMapsEqual(NamedNodeMap a, NamedNodeMap b) { + if (a.getLength() != b.getLength()) { + return false; + } + for (int i = 0; i < a.getLength(); i++) { + Node aNode = a.item(i); + Node bNode = aNode.getLocalName() == null + ? b.getNamedItem(aNode.getNodeName()) + : b.getNamedItemNS(aNode.getNamespaceURI(), aNode.getLocalName()); + if (bNode == null || !aNode.isEqualNode(bNode)) { + return false; + } + } + return true; + } + + public final Object getFeature(String feature, String version) { + return isSupported(feature, version) ? this : null; } public Object setUserData(String key, Object data, diff --git a/xml/src/main/java/org/apache/harmony/xml/dom/TextImpl.java b/xml/src/main/java/org/apache/harmony/xml/dom/TextImpl.java index 3905865..5c9d123 100644 --- a/xml/src/main/java/org/apache/harmony/xml/dom/TextImpl.java +++ b/xml/src/main/java/org/apache/harmony/xml/dom/TextImpl.java @@ -46,11 +46,6 @@ public class TextImpl extends CharacterDataImpl implements Text { return Node.TEXT_NODE; } - @Override - public String getNodeValue() { - return getData(); - } - public Text splitText(int offset) throws DOMException { Text newText = getOwnerDocument().createTextNode( substringData(offset, getLength() - offset)); diff --git a/xml/src/main/java/org/apache/harmony/xml/parsers/DocumentBuilderImpl.java b/xml/src/main/java/org/apache/harmony/xml/parsers/DocumentBuilderImpl.java index 5a3c48c..52240aa 100644 --- a/xml/src/main/java/org/apache/harmony/xml/parsers/DocumentBuilderImpl.java +++ b/xml/src/main/java/org/apache/harmony/xml/parsers/DocumentBuilderImpl.java @@ -115,8 +115,8 @@ class DocumentBuilderImpl extends DocumentBuilder { Document document = newDocument(); try { - XmlPullParser parser = new KXmlParser(); - + KXmlParser parser = new KXmlParser(); + parser.keepNamespaceAttributes(); parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, namespaceAware); diff --git a/xml/src/main/java/org/apache/xalan/processor/TransformerFactoryImpl.java b/xml/src/main/java/org/apache/xalan/processor/TransformerFactoryImpl.java index 78d0cc5..618b412 100644 --- a/xml/src/main/java/org/apache/xalan/processor/TransformerFactoryImpl.java +++ b/xml/src/main/java/org/apache/xalan/processor/TransformerFactoryImpl.java @@ -792,7 +792,7 @@ public class TransformerFactoryImpl extends SAXTransformerFactory try { m_errorListener.fatalError( ex ); - return null; + return null; // TODO: but the API promises to never return null... } catch( TransformerConfigurationException ex1 ) { diff --git a/xml/src/main/java/org/apache/xml/utils/DOMHelper.java b/xml/src/main/java/org/apache/xml/utils/DOMHelper.java index 76721d0..53d0adc 100644 --- a/xml/src/main/java/org/apache/xml/utils/DOMHelper.java +++ b/xml/src/main/java/org/apache/xml/utils/DOMHelper.java @@ -85,16 +85,23 @@ public class DOMHelper DocumentBuilderFactory dfactory = DocumentBuilderFactory.newInstance(); dfactory.setNamespaceAware(true); - dfactory.setValidating(true); - - if (isSecureProcessing) - { - try - { - dfactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true); - } - catch (ParserConfigurationException pce) {} - } + // BEGIN android-removed + // If set, DocumentBuilderFactoryImpl.newDocumentBuilder() fails + // because we haven't implemented validation + // dfactory.setValidating(true); + // BEGIN android-removed + + // BEGIN android-removed + // We haven't implemented secure processing + // if (isSecureProcessing) + // { + // try + // { + // dfactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true); + // } + // catch (ParserConfigurationException pce) {} + // } + // END android-removed DocumentBuilder docBuilder = dfactory.newDocumentBuilder(); Document outNode = docBuilder.newDocument(); diff --git a/xml/src/main/java/org/kxml2/io/KXmlParser.java b/xml/src/main/java/org/kxml2/io/KXmlParser.java index c4d8f3d..99eb03b 100644 --- a/xml/src/main/java/org/kxml2/io/KXmlParser.java +++ b/xml/src/main/java/org/kxml2/io/KXmlParser.java @@ -45,6 +45,7 @@ public class KXmlParser implements XmlPullParser { private boolean processNsp; private boolean relaxed; + private boolean keepNamespaceAttributes; // android-added private Hashtable entityMap; private int depth; private String[] elementStack = new String[16]; @@ -80,6 +81,14 @@ public class KXmlParser implements XmlPullParser { private boolean degenerated; private int attributeCount; + + /** + * The current element's attributes arranged in groups of 4: + * i + 0 = attribute namespace URI + * i + 1 = attribute namespace prefix + * i + 2 = attribute qualified name (may contain ":", as in "html:h1") + * i + 3 = attribute value + */ private String[] attributes = new String[16]; // private int stackMismatch = 0; private String error; @@ -100,6 +109,19 @@ public class KXmlParser implements XmlPullParser { new char[Runtime.getRuntime().freeMemory() >= 1048576 ? 8192 : 128]; } + // BEGIN android-added + /** + * Retains namespace attributes like {@code xmlns="http://foo"} or {@code + * xmlns:foo="http:foo"} in pulled elements. Most applications will only be + * interested in the effective namespaces of their elements, so these + * attributes aren't useful. But for structure preserving wrappers like DOM, + * it is necessary to keep the namespace data around. + */ + public void keepNamespaceAttributes() { + this.keepNamespaceAttributes = true; + } + // END android-added + private final boolean isProp(String n1, boolean prop, String n2) { if (!n1.startsWith("http://xmlpull.org/v1/doc/")) return false; @@ -148,14 +170,23 @@ public class KXmlParser implements XmlPullParser { //System.out.println (prefixMap); - System.arraycopy( - attributes, - i + 4, - attributes, - i, - ((--attributeCount) << 2) - i); - - i -= 4; + // BEGIN android-changed + if (keepNamespaceAttributes) { + // explicitly set the namespace for unprefixed attributes + // such as xmlns="http://foo" + attributes[i] = "http://www.w3.org/2000/xmlns/"; + any = true; + } else { + System.arraycopy( + attributes, + i + 4, + attributes, + i, + ((--attributeCount) << 2) - i); + + i -= 4; + } + // END android-changed } } diff --git a/xml/src/main/java/org/w3c/dom/Attr.java b/xml/src/main/java/org/w3c/dom/Attr.java index d9ed6ff..bd7267b 100644 --- a/xml/src/main/java/org/w3c/dom/Attr.java +++ b/xml/src/main/java/org/w3c/dom/Attr.java @@ -176,7 +176,7 @@ public interface Attr extends Node { /** * On retrieval, the value of the attribute is returned as a string. * Character and general entity references are replaced with their - * values. See also the method <code>getAttribute</code> on the + * values. See also the method <code>getAttribute</code> on the * <code>Element</code> interface. * <br>On setting, this creates a <code>Text</code> node with the unparsed * contents of the string, i.e. any characters that an XML processor diff --git a/xml/src/main/java/org/w3c/dom/events/DocumentEvent.java b/xml/src/main/java/org/w3c/dom/events/DocumentEvent.java deleted file mode 100644 index 76644bc..0000000 --- a/xml/src/main/java/org/w3c/dom/events/DocumentEvent.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (c) 2000 World Wide Web Consortium, - * (Massachusetts Institute of Technology, Institut National de - * Recherche en Informatique et en Automatique, Keio University). All - * Rights Reserved. This program is distributed under the W3C's Software - * Intellectual Property License. This program is distributed in the - * hope that it will be useful, but WITHOUT ANY WARRANTY; without even - * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. - * See W3C License http://www.w3.org/Consortium/Legal/ for more details. - */ - -package org.w3c.dom.events; - -import org.w3c.dom.DOMException; - -/** - * The <code>DocumentEvent</code> interface provides a mechanism by which the - * user can create an Event of a type supported by the implementation. It is - * expected that the <code>DocumentEvent</code> interface will be - * implemented on the same object which implements the <code>Document</code> - * interface in an implementation which supports the Event model. - * <p>See also the <a href='http://www.w3.org/TR/2000/REC-DOM-Level-2-Events-20001113'>Document Object Model (DOM) Level 2 Events Specification</a>. - * @since DOM Level 2 - */ -public interface DocumentEvent { - /** - * - * @param eventType The <code>eventType</code> parameter specifies the - * type of <code>Event</code> interface to be created. If the - * <code>Event</code> interface specified is supported by the - * implementation this method will return a new <code>Event</code> of - * the interface type requested. If the <code>Event</code> is to be - * dispatched via the <code>dispatchEvent</code> method the - * appropriate event init method must be called after creation in - * order to initialize the <code>Event</code>'s values. As an example, - * a user wishing to synthesize some kind of <code>UIEvent</code> - * would call <code>createEvent</code> with the parameter "UIEvents". - * The <code>initUIEvent</code> method could then be called on the - * newly created <code>UIEvent</code> to set the specific type of - * UIEvent to be dispatched and set its context information.The - * <code>createEvent</code> method is used in creating - * <code>Event</code>s when it is either inconvenient or unnecessary - * for the user to create an <code>Event</code> themselves. In cases - * where the implementation provided <code>Event</code> is - * insufficient, users may supply their own <code>Event</code> - * implementations for use with the <code>dispatchEvent</code> method. - * @return The newly created <code>Event</code> - * @exception DOMException - * NOT_SUPPORTED_ERR: Raised if the implementation does not support the - * type of <code>Event</code> interface requested - */ - public Event createEvent(String eventType) - throws DOMException; - -} diff --git a/xml/src/main/java/org/w3c/dom/events/Event.java b/xml/src/main/java/org/w3c/dom/events/Event.java deleted file mode 100644 index 14a9239..0000000 --- a/xml/src/main/java/org/w3c/dom/events/Event.java +++ /dev/null @@ -1,141 +0,0 @@ -/* - * Copyright (c) 2000 World Wide Web Consortium, - * (Massachusetts Institute of Technology, Institut National de - * Recherche en Informatique et en Automatique, Keio University). All - * Rights Reserved. This program is distributed under the W3C's Software - * Intellectual Property License. This program is distributed in the - * hope that it will be useful, but WITHOUT ANY WARRANTY; without even - * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. - * See W3C License http://www.w3.org/Consortium/Legal/ for more details. - */ - -package org.w3c.dom.events; - -/** - * The <code>Event</code> interface is used to provide contextual information - * about an event to the handler processing the event. An object which - * implements the <code>Event</code> interface is generally passed as the - * first parameter to an event handler. More specific context information is - * passed to event handlers by deriving additional interfaces from - * <code>Event</code> which contain information directly relating to the - * type of event they accompany. These derived interfaces are also - * implemented by the object passed to the event listener. - * <p>See also the <a href='http://www.w3.org/TR/2000/REC-DOM-Level-2-Events-20001113'>Document Object Model (DOM) Level 2 Events Specification</a>. - * @since DOM Level 2 - */ -public interface Event { - // PhaseType - /** - * The current event phase is the capturing phase. - */ - public static final short CAPTURING_PHASE = 1; - /** - * The event is currently being evaluated at the target - * <code>EventTarget</code>. - */ - public static final short AT_TARGET = 2; - /** - * The current event phase is the bubbling phase. - */ - public static final short BUBBLING_PHASE = 3; - - /** - * The name of the event (case-insensitive). The name must be an XML name. - */ - public String getType(); - - /** - * Used to indicate the <code>EventTarget</code> to which the event was - * originally dispatched. - */ - public EventTarget getTarget(); - - /** - * Used to indicate the <code>EventTarget</code> whose - * <code>EventListeners</code> are currently being processed. This is - * particularly useful during capturing and bubbling. - */ - public EventTarget getCurrentTarget(); - - /** - * Used to indicate which phase of event flow is currently being - * evaluated. - */ - public short getEventPhase(); - - /** - * Used to indicate whether or not an event is a bubbling event. If the - * event can bubble the value is true, else the value is false. - */ - public boolean getBubbles(); - - /** - * Used to indicate whether or not an event can have its default action - * prevented. If the default action can be prevented the value is true, - * else the value is false. - */ - public boolean getCancelable(); - - /** - * Used to specify the time (in milliseconds relative to the epoch) at - * which the event was created. Due to the fact that some systems may - * not provide this information the value of <code>timeStamp</code> may - * be not available for all events. When not available, a value of 0 - * will be returned. Examples of epoch time are the time of the system - * start or 0:0:0 UTC 1st January 1970. - */ - public long getTimeStamp(); - - /** - * The <code>stopPropagation</code> method is used prevent further - * propagation of an event during event flow. If this method is called - * by any <code>EventListener</code> the event will cease propagating - * through the tree. The event will complete dispatch to all listeners - * on the current <code>EventTarget</code> before event flow stops. This - * method may be used during any stage of event flow. - */ - public void stopPropagation(); - - /** - * If an event is cancelable, the <code>preventDefault</code> method is - * used to signify that the event is to be canceled, meaning any default - * action normally taken by the implementation as a result of the event - * will not occur. If, during any stage of event flow, the - * <code>preventDefault</code> method is called the event is canceled. - * Any default action associated with the event will not occur. Calling - * this method for a non-cancelable event has no effect. Once - * <code>preventDefault</code> has been called it will remain in effect - * throughout the remainder of the event's propagation. This method may - * be used during any stage of event flow. - */ - public void preventDefault(); - - /** - * The <code>initEvent</code> method is used to initialize the value of an - * <code>Event</code> created through the <code>DocumentEvent</code> - * interface. This method may only be called before the - * <code>Event</code> has been dispatched via the - * <code>dispatchEvent</code> method, though it may be called multiple - * times during that phase if necessary. If called multiple times the - * final invocation takes precedence. If called from a subclass of - * <code>Event</code> interface only the values specified in the - * <code>initEvent</code> method are modified, all other attributes are - * left unchanged. - * @param eventTypeArg Specifies the event type. This type may be any - * event type currently defined in this specification or a new event - * type.. The string must be an XML name. Any new event type must not - * begin with any upper, lower, or mixed case version of the string - * "DOM". This prefix is reserved for future DOM event sets. It is - * also strongly recommended that third parties adding their own - * events use their own prefix to avoid confusion and lessen the - * probability of conflicts with other new events. - * @param canBubbleArg Specifies whether or not the event can bubble. - * @param cancelableArg Specifies whether or not the event's default - * action can be prevented. - */ - public void initEvent(String eventTypeArg, - boolean canBubbleArg, - boolean cancelableArg); - -} diff --git a/xml/src/main/java/org/w3c/dom/events/EventException.java b/xml/src/main/java/org/w3c/dom/events/EventException.java deleted file mode 100644 index 7a6ff26..0000000 --- a/xml/src/main/java/org/w3c/dom/events/EventException.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (c) 2000 World Wide Web Consortium, - * (Massachusetts Institute of Technology, Institut National de - * Recherche en Informatique et en Automatique, Keio University). All - * Rights Reserved. This program is distributed under the W3C's Software - * Intellectual Property License. This program is distributed in the - * hope that it will be useful, but WITHOUT ANY WARRANTY; without even - * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. - * See W3C License http://www.w3.org/Consortium/Legal/ for more details. - */ - -package org.w3c.dom.events; - -/** - * Event operations may throw an <code>EventException</code> as specified in - * their method descriptions. - * <p>See also the <a href='http://www.w3.org/TR/2000/REC-DOM-Level-2-Events-20001113'>Document Object Model (DOM) Level 2 Events Specification</a>. - * @since DOM Level 2 - */ -public class EventException extends RuntimeException { - public EventException(short code, String message) { - super(message); - this.code = code; - } - public short code; - // EventExceptionCode - /** - * If the <code>Event</code>'s type was not specified by initializing the - * event before the method was called. Specification of the Event's type - * as <code>null</code> or an empty string will also trigger this - * exception. - */ - public static final short UNSPECIFIED_EVENT_TYPE_ERR = 0; - -} diff --git a/xml/src/main/java/org/w3c/dom/events/EventListener.java b/xml/src/main/java/org/w3c/dom/events/EventListener.java deleted file mode 100644 index 1df8020..0000000 --- a/xml/src/main/java/org/w3c/dom/events/EventListener.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (c) 2000 World Wide Web Consortium, - * (Massachusetts Institute of Technology, Institut National de - * Recherche en Informatique et en Automatique, Keio University). All - * Rights Reserved. This program is distributed under the W3C's Software - * Intellectual Property License. This program is distributed in the - * hope that it will be useful, but WITHOUT ANY WARRANTY; without even - * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. - * See W3C License http://www.w3.org/Consortium/Legal/ for more details. - */ - -package org.w3c.dom.events; - -/** - * The <code>EventListener</code> interface is the primary method for - * handling events. Users implement the <code>EventListener</code> interface - * and register their listener on an <code>EventTarget</code> using the - * <code>AddEventListener</code> method. The users should also remove their - * <code>EventListener</code> from its <code>EventTarget</code> after they - * have completed using the listener. - * <p> When a <code>Node</code> is copied using the <code>cloneNode</code> - * method the <code>EventListener</code>s attached to the source - * <code>Node</code> are not attached to the copied <code>Node</code>. If - * the user wishes the same <code>EventListener</code>s to be added to the - * newly created copy the user must add them manually. - * <p>See also the <a href='http://www.w3.org/TR/2000/REC-DOM-Level-2-Events-20001113'>Document Object Model (DOM) Level 2 Events Specification</a>. - * @since DOM Level 2 - */ -public interface EventListener { - /** - * This method is called whenever an event occurs of the type for which - * the <code> EventListener</code> interface was registered. - * @param evt The <code>Event</code> contains contextual information - * about the event. It also contains the <code>stopPropagation</code> - * and <code>preventDefault</code> methods which are used in - * determining the event's flow and default action. - */ - public void handleEvent(Event evt); - -} diff --git a/xml/src/main/java/org/w3c/dom/events/EventTarget.java b/xml/src/main/java/org/w3c/dom/events/EventTarget.java deleted file mode 100644 index f076636..0000000 --- a/xml/src/main/java/org/w3c/dom/events/EventTarget.java +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright (c) 2000 World Wide Web Consortium, - * (Massachusetts Institute of Technology, Institut National de - * Recherche en Informatique et en Automatique, Keio University). All - * Rights Reserved. This program is distributed under the W3C's Software - * Intellectual Property License. This program is distributed in the - * hope that it will be useful, but WITHOUT ANY WARRANTY; without even - * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. - * See W3C License http://www.w3.org/Consortium/Legal/ for more details. - */ - -package org.w3c.dom.events; - -/** - * The <code>EventTarget</code> interface is implemented by all - * <code>Nodes</code> in an implementation which supports the DOM Event - * Model. Therefore, this interface can be obtained by using - * binding-specific casting methods on an instance of the <code>Node</code> - * interface. The interface allows registration and removal of - * <code>EventListeners</code> on an <code>EventTarget</code> and dispatch - * of events to that <code>EventTarget</code>. - * <p>See also the <a href='http://www.w3.org/TR/2000/REC-DOM-Level-2-Events-20001113'>Document Object Model (DOM) Level 2 Events Specification</a>. - * @since DOM Level 2 - */ -public interface EventTarget { - /** - * This method allows the registration of event listeners on the event - * target. If an <code>EventListener</code> is added to an - * <code>EventTarget</code> while it is processing an event, it will not - * be triggered by the current actions but may be triggered during a - * later stage of event flow, such as the bubbling phase. - * <br> If multiple identical <code>EventListener</code>s are registered - * on the same <code>EventTarget</code> with the same parameters the - * duplicate instances are discarded. They do not cause the - * <code>EventListener</code> to be called twice and since they are - * discarded they do not need to be removed with the - * <code>removeEventListener</code> method. - * @param type The event type for which the user is registering - * @param listener The <code>listener</code> parameter takes an interface - * implemented by the user which contains the methods to be called - * when the event occurs. - * @param useCapture If true, <code>useCapture</code> indicates that the - * user wishes to initiate capture. After initiating capture, all - * events of the specified type will be dispatched to the registered - * <code>EventListener</code> before being dispatched to any - * <code>EventTargets</code> beneath them in the tree. Events which - * are bubbling upward through the tree will not trigger an - * <code>EventListener</code> designated to use capture. - */ - public void addEventListener(String type, - EventListener listener, - boolean useCapture); - - /** - * This method allows the removal of event listeners from the event - * target. If an <code>EventListener</code> is removed from an - * <code>EventTarget</code> while it is processing an event, it will not - * be triggered by the current actions. <code>EventListener</code>s can - * never be invoked after being removed. - * <br>Calling <code>removeEventListener</code> with arguments which do - * not identify any currently registered <code>EventListener</code> on - * the <code>EventTarget</code> has no effect. - * @param type Specifies the event type of the <code>EventListener</code> - * being removed. - * @param listener The <code>EventListener</code> parameter indicates the - * <code>EventListener </code> to be removed. - * @param useCapture Specifies whether the <code>EventListener</code> - * being removed was registered as a capturing listener or not. If a - * listener was registered twice, one with capture and one without, - * each must be removed separately. Removal of a capturing listener - * does not affect a non-capturing version of the same listener, and - * vice versa. - */ - public void removeEventListener(String type, - EventListener listener, - boolean useCapture); - - /** - * This method allows the dispatch of events into the implementations - * event model. Events dispatched in this manner will have the same - * capturing and bubbling behavior as events dispatched directly by the - * implementation. The target of the event is the - * <code> EventTarget</code> on which <code>dispatchEvent</code> is - * called. - * @param evt Specifies the event type, behavior, and contextual - * information to be used in processing the event. - * @return The return value of <code>dispatchEvent</code> indicates - * whether any of the listeners which handled the event called - * <code>preventDefault</code>. If <code>preventDefault</code> was - * called the value is false, else the value is true. - * @exception EventException - * UNSPECIFIED_EVENT_TYPE_ERR: Raised if the <code>Event</code>'s type - * was not specified by initializing the event before - * <code>dispatchEvent</code> was called. Specification of the - * <code>Event</code>'s type as <code>null</code> or an empty string - * will also trigger this exception. - */ - public boolean dispatchEvent(Event evt) - throws EventException; - -} diff --git a/xml/src/main/java/org/w3c/dom/events/MouseEvent.java b/xml/src/main/java/org/w3c/dom/events/MouseEvent.java deleted file mode 100644 index be78035..0000000 --- a/xml/src/main/java/org/w3c/dom/events/MouseEvent.java +++ /dev/null @@ -1,156 +0,0 @@ -/* - * Copyright (c) 2000 World Wide Web Consortium, - * (Massachusetts Institute of Technology, Institut National de - * Recherche en Informatique et en Automatique, Keio University). All - * Rights Reserved. This program is distributed under the W3C's Software - * Intellectual Property License. This program is distributed in the - * hope that it will be useful, but WITHOUT ANY WARRANTY; without even - * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. - * See W3C License http://www.w3.org/Consortium/Legal/ for more details. - */ - -package org.w3c.dom.events; - -import org.w3c.dom.views.AbstractView; - -/** - * The <code>MouseEvent</code> interface provides specific contextual - * information associated with Mouse events. - * <p>The <code>detail</code> attribute inherited from <code>UIEvent</code> - * indicates the number of times a mouse button has been pressed and - * released over the same screen location during a user action. The - * attribute value is 1 when the user begins this action and increments by 1 - * for each full sequence of pressing and releasing. If the user moves the - * mouse between the mousedown and mouseup the value will be set to 0, - * indicating that no click is occurring. - * <p>In the case of nested elements mouse events are always targeted at the - * most deeply nested element. Ancestors of the targeted element may use - * bubbling to obtain notification of mouse events which occur within its - * descendent elements. - * <p>See also the <a href='http://www.w3.org/TR/2000/REC-DOM-Level-2-Events-20001113'>Document Object Model (DOM) Level 2 Events Specification</a>. - * @since DOM Level 2 - */ -public interface MouseEvent extends UIEvent { - /** - * The horizontal coordinate at which the event occurred relative to the - * origin of the screen coordinate system. - */ - public int getScreenX(); - - /** - * The vertical coordinate at which the event occurred relative to the - * origin of the screen coordinate system. - */ - public int getScreenY(); - - /** - * The horizontal coordinate at which the event occurred relative to the - * DOM implementation's client area. - */ - public int getClientX(); - - /** - * The vertical coordinate at which the event occurred relative to the DOM - * implementation's client area. - */ - public int getClientY(); - - /** - * Used to indicate whether the 'ctrl' key was depressed during the firing - * of the event. - */ - public boolean getCtrlKey(); - - /** - * Used to indicate whether the 'shift' key was depressed during the - * firing of the event. - */ - public boolean getShiftKey(); - - /** - * Used to indicate whether the 'alt' key was depressed during the firing - * of the event. On some platforms this key may map to an alternative - * key name. - */ - public boolean getAltKey(); - - /** - * Used to indicate whether the 'meta' key was depressed during the firing - * of the event. On some platforms this key may map to an alternative - * key name. - */ - public boolean getMetaKey(); - - /** - * During mouse events caused by the depression or release of a mouse - * button, <code>button</code> is used to indicate which mouse button - * changed state. The values for <code>button</code> range from zero to - * indicate the left button of the mouse, one to indicate the middle - * button if present, and two to indicate the right button. For mice - * configured for left handed use in which the button actions are - * reversed the values are instead read from right to left. - */ - public short getButton(); - - /** - * Used to identify a secondary <code>EventTarget</code> related to a UI - * event. Currently this attribute is used with the mouseover event to - * indicate the <code>EventTarget</code> which the pointing device - * exited and with the mouseout event to indicate the - * <code>EventTarget</code> which the pointing device entered. - */ - public EventTarget getRelatedTarget(); - - /** - * The <code>initMouseEvent</code> method is used to initialize the value - * of a <code>MouseEvent</code> created through the - * <code>DocumentEvent</code> interface. This method may only be called - * before the <code>MouseEvent</code> has been dispatched via the - * <code>dispatchEvent</code> method, though it may be called multiple - * times during that phase if necessary. If called multiple times, the - * final invocation takes precedence. - * @param typeArg Specifies the event type. - * @param canBubbleArg Specifies whether or not the event can bubble. - * @param cancelableArg Specifies whether or not the event's default - * action can be prevented. - * @param viewArg Specifies the <code>Event</code>'s - * <code>AbstractView</code>. - * @param detailArg Specifies the <code>Event</code>'s mouse click count. - * @param screenXArg Specifies the <code>Event</code>'s screen x - * coordinate - * @param screenYArg Specifies the <code>Event</code>'s screen y - * coordinate - * @param clientXArg Specifies the <code>Event</code>'s client x - * coordinate - * @param clientYArg Specifies the <code>Event</code>'s client y - * coordinate - * @param ctrlKeyArg Specifies whether or not control key was depressed - * during the <code>Event</code>. - * @param altKeyArg Specifies whether or not alt key was depressed during - * the <code>Event</code>. - * @param shiftKeyArg Specifies whether or not shift key was depressed - * during the <code>Event</code>. - * @param metaKeyArg Specifies whether or not meta key was depressed - * during the <code>Event</code>. - * @param buttonArg Specifies the <code>Event</code>'s mouse button. - * @param relatedTargetArg Specifies the <code>Event</code>'s related - * <code>EventTarget</code>. - */ - public void initMouseEvent(String typeArg, - boolean canBubbleArg, - boolean cancelableArg, - AbstractView viewArg, - int detailArg, - int screenXArg, - int screenYArg, - int clientXArg, - int clientYArg, - boolean ctrlKeyArg, - boolean altKeyArg, - boolean shiftKeyArg, - boolean metaKeyArg, - short buttonArg, - EventTarget relatedTargetArg); - -} diff --git a/xml/src/main/java/org/w3c/dom/events/MutationEvent.java b/xml/src/main/java/org/w3c/dom/events/MutationEvent.java deleted file mode 100644 index 3db4003..0000000 --- a/xml/src/main/java/org/w3c/dom/events/MutationEvent.java +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Copyright (c) 2000 World Wide Web Consortium, - * (Massachusetts Institute of Technology, Institut National de - * Recherche en Informatique et en Automatique, Keio University). All - * Rights Reserved. This program is distributed under the W3C's Software - * Intellectual Property License. This program is distributed in the - * hope that it will be useful, but WITHOUT ANY WARRANTY; without even - * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. - * See W3C License http://www.w3.org/Consortium/Legal/ for more details. - */ - -package org.w3c.dom.events; - -import org.w3c.dom.Node; - -/** - * The <code>MutationEvent</code> interface provides specific contextual - * information associated with Mutation events. - * <p>See also the <a href='http://www.w3.org/TR/2000/REC-DOM-Level-2-Events-20001113'>Document Object Model (DOM) Level 2 Events Specification</a>. - * @since DOM Level 2 - */ -public interface MutationEvent extends Event { - // attrChangeType - /** - * The <code>Attr</code> was modified in place. - */ - public static final short MODIFICATION = 1; - /** - * The <code>Attr</code> was just added. - */ - public static final short ADDITION = 2; - /** - * The <code>Attr</code> was just removed. - */ - public static final short REMOVAL = 3; - - /** - * <code>relatedNode</code> is used to identify a secondary node related - * to a mutation event. For example, if a mutation event is dispatched - * to a node indicating that its parent has changed, the - * <code>relatedNode</code> is the changed parent. If an event is - * instead dispatched to a subtree indicating a node was changed within - * it, the <code>relatedNode</code> is the changed node. In the case of - * the DOMAttrModified event it indicates the <code>Attr</code> node - * which was modified, added, or removed. - */ - public Node getRelatedNode(); - - /** - * <code>prevValue</code> indicates the previous value of the - * <code>Attr</code> node in DOMAttrModified events, and of the - * <code>CharacterData</code> node in DOMCharacterDataModified events. - */ - public String getPrevValue(); - - /** - * <code>newValue</code> indicates the new value of the <code>Attr</code> - * node in DOMAttrModified events, and of the <code>CharacterData</code> - * node in DOMCharacterDataModified events. - */ - public String getNewValue(); - - /** - * <code>attrName</code> indicates the name of the changed - * <code>Attr</code> node in a DOMAttrModified event. - */ - public String getAttrName(); - - /** - * <code>attrChange</code> indicates the type of change which triggered - * the DOMAttrModified event. The values can be <code>MODIFICATION</code> - * , <code>ADDITION</code>, or <code>REMOVAL</code>. - */ - public short getAttrChange(); - - /** - * The <code>initMutationEvent</code> method is used to initialize the - * value of a <code>MutationEvent</code> created through the - * <code>DocumentEvent</code> interface. This method may only be called - * before the <code>MutationEvent</code> has been dispatched via the - * <code>dispatchEvent</code> method, though it may be called multiple - * times during that phase if necessary. If called multiple times, the - * final invocation takes precedence. - * @param typeArg Specifies the event type. - * @param canBubbleArg Specifies whether or not the event can bubble. - * @param cancelableArg Specifies whether or not the event's default - * action can be prevented. - * @param relatedNodeArg Specifies the <code>Event</code>'s related Node. - * @param prevValueArg Specifies the <code>Event</code>'s - * <code>prevValue</code> attribute. This value may be null. - * @param newValueArg Specifies the <code>Event</code>'s - * <code>newValue</code> attribute. This value may be null. - * @param attrNameArg Specifies the <code>Event</code>'s - * <code>attrName</code> attribute. This value may be null. - * @param attrChangeArg Specifies the <code>Event</code>'s - * <code>attrChange</code> attribute - */ - public void initMutationEvent(String typeArg, - boolean canBubbleArg, - boolean cancelableArg, - Node relatedNodeArg, - String prevValueArg, - String newValueArg, - String attrNameArg, - short attrChangeArg); - -} diff --git a/xml/src/main/java/org/w3c/dom/events/UIEvent.java b/xml/src/main/java/org/w3c/dom/events/UIEvent.java deleted file mode 100644 index 15affe8..0000000 --- a/xml/src/main/java/org/w3c/dom/events/UIEvent.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright (c) 2000 World Wide Web Consortium, - * (Massachusetts Institute of Technology, Institut National de - * Recherche en Informatique et en Automatique, Keio University). All - * Rights Reserved. This program is distributed under the W3C's Software - * Intellectual Property License. This program is distributed in the - * hope that it will be useful, but WITHOUT ANY WARRANTY; without even - * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. - * See W3C License http://www.w3.org/Consortium/Legal/ for more details. - */ - -package org.w3c.dom.events; - -import org.w3c.dom.views.AbstractView; - -/** - * The <code>UIEvent</code> interface provides specific contextual information - * associated with User Interface events. - * <p>See also the <a href='http://www.w3.org/TR/2000/REC-DOM-Level-2-Events-20001113'>Document Object Model (DOM) Level 2 Events Specification</a>. - * @since DOM Level 2 - */ -public interface UIEvent extends Event { - /** - * The <code>view</code> attribute identifies the <code>AbstractView</code> - * from which the event was generated. - */ - public AbstractView getView(); - - /** - * Specifies some detail information about the <code>Event</code>, - * depending on the type of event. - */ - public int getDetail(); - - /** - * The <code>initUIEvent</code> method is used to initialize the value of - * a <code>UIEvent</code> created through the <code>DocumentEvent</code> - * interface. This method may only be called before the - * <code>UIEvent</code> has been dispatched via the - * <code>dispatchEvent</code> method, though it may be called multiple - * times during that phase if necessary. If called multiple times, the - * final invocation takes precedence. - * @param typeArg Specifies the event type. - * @param canBubbleArg Specifies whether or not the event can bubble. - * @param cancelableArg Specifies whether or not the event's default - * action can be prevented. - * @param viewArg Specifies the <code>Event</code>'s - * <code>AbstractView</code>. - * @param detailArg Specifies the <code>Event</code>'s detail. - */ - public void initUIEvent(String typeArg, - boolean canBubbleArg, - boolean cancelableArg, - AbstractView viewArg, - int detailArg); - -} diff --git a/xml/src/main/java/org/w3c/dom/ls/LSLoadEvent.java b/xml/src/main/java/org/w3c/dom/ls/LSLoadEvent.java deleted file mode 100644 index 601a5be..0000000 --- a/xml/src/main/java/org/w3c/dom/ls/LSLoadEvent.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (c) 2004 World Wide Web Consortium, - * - * (Massachusetts Institute of Technology, European Research Consortium for - * Informatics and Mathematics, Keio University). All Rights Reserved. This - * work is distributed under the W3C(r) Software License [1] in the hope that - * it will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * - * [1] http://www.w3.org/Consortium/Legal/2002/copyright-software-20021231 - */ - -package org.w3c.dom.ls; - -import org.w3c.dom.Document; -import org.w3c.dom.events.Event; - -/** - * This interface represents a load event object that signals the completion - * of a document load. - * <p>See also the <a href='http://www.w3.org/TR/2004/REC-DOM-Level-3-LS-20040407'>Document Object Model (DOM) Level 3 Load -and Save Specification</a>. - */ -public interface LSLoadEvent extends Event { - /** - * The document that finished loading. - */ - public Document getNewDocument(); - - /** - * The input source that was parsed. - */ - public LSInput getInput(); - -} diff --git a/xml/src/main/java/org/w3c/dom/ls/LSProgressEvent.java b/xml/src/main/java/org/w3c/dom/ls/LSProgressEvent.java deleted file mode 100644 index da98e14..0000000 --- a/xml/src/main/java/org/w3c/dom/ls/LSProgressEvent.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (c) 2004 World Wide Web Consortium, - * - * (Massachusetts Institute of Technology, European Research Consortium for - * Informatics and Mathematics, Keio University). All Rights Reserved. This - * work is distributed under the W3C(r) Software License [1] in the hope that - * it will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * - * [1] http://www.w3.org/Consortium/Legal/2002/copyright-software-20021231 - */ - -package org.w3c.dom.ls; - -import org.w3c.dom.events.Event; - -/** - * This interface represents a progress event object that notifies the - * application about progress as a document is parsed. It extends the - * <code>Event</code> interface defined in [<a href='http://www.w3.org/TR/2003/NOTE-DOM-Level-3-Events-20031107'>DOM Level 3 Events</a>] - * . - * <p> The units used for the attributes <code>position</code> and - * <code>totalSize</code> are not specified and can be implementation and - * input dependent. - * <p>See also the <a href='http://www.w3.org/TR/2004/REC-DOM-Level-3-LS-20040407'>Document Object Model (DOM) Level 3 Load -and Save Specification</a>. - */ -public interface LSProgressEvent extends Event { - /** - * The input source that is being parsed. - */ - public LSInput getInput(); - - /** - * The current position in the input source, including all external - * entities and other resources that have been read. - */ - public int getPosition(); - - /** - * The total size of the document including all external resources, this - * number might change as a document is being parsed if references to - * more external resources are seen. A value of <code>0</code> is - * returned if the total size cannot be determined or estimated. - */ - public int getTotalSize(); - -} diff --git a/xml/src/main/java/org/w3c/dom/ls/LSSerializer.java b/xml/src/main/java/org/w3c/dom/ls/LSSerializer.java index e7b6350..33b094a 100644 --- a/xml/src/main/java/org/w3c/dom/ls/LSSerializer.java +++ b/xml/src/main/java/org/w3c/dom/ls/LSSerializer.java @@ -330,7 +330,9 @@ public interface LSSerializer { * <br> The filter is invoked after the operations requested by the * <code>DOMConfiguration</code> parameters have been applied. For * example, CDATA sections won't be passed to the filter if "<a href='http://www.w3.org/TR/DOM-Level-3-Core/core.html#parameter-cdata-sections'> - * cdata-sections</a>" is set to <code>false</code>. + * cdata-sections</a>" is set to <code>false</code>. + * + * @hide */ public LSSerializerFilter getFilter(); /** @@ -341,7 +343,9 @@ public interface LSSerializer { * <br> The filter is invoked after the operations requested by the * <code>DOMConfiguration</code> parameters have been applied. For * example, CDATA sections won't be passed to the filter if "<a href='http://www.w3.org/TR/DOM-Level-3-Core/core.html#parameter-cdata-sections'> - * cdata-sections</a>" is set to <code>false</code>. + * cdata-sections</a>" is set to <code>false</code>. + * + * @hide */ public void setFilter(LSSerializerFilter filter); diff --git a/xml/src/main/java/org/w3c/dom/ls/LSSerializerFilter.java b/xml/src/main/java/org/w3c/dom/ls/LSSerializerFilter.java index 049459c..9e76b37 100644 --- a/xml/src/main/java/org/w3c/dom/ls/LSSerializerFilter.java +++ b/xml/src/main/java/org/w3c/dom/ls/LSSerializerFilter.java @@ -41,6 +41,8 @@ import org.w3c.dom.traversal.NodeFilter; * document. * <p>See also the <a href='http://www.w3.org/TR/2004/REC-DOM-Level-3-LS-20040407'>Document Object Model (DOM) Level 3 Load and Save Specification</a>. + * + * @hide */ public interface LSSerializerFilter extends NodeFilter { /** diff --git a/xml/src/main/java/org/w3c/dom/traversal/DocumentTraversal.java b/xml/src/main/java/org/w3c/dom/traversal/DocumentTraversal.java deleted file mode 100644 index bc45ad9..0000000 --- a/xml/src/main/java/org/w3c/dom/traversal/DocumentTraversal.java +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright (c) 2000 World Wide Web Consortium, - * (Massachusetts Institute of Technology, Institut National de - * Recherche en Informatique et en Automatique, Keio University). All - * Rights Reserved. This program is distributed under the W3C's Software - * Intellectual Property License. This program is distributed in the - * hope that it will be useful, but WITHOUT ANY WARRANTY; without even - * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. - * See W3C License http://www.w3.org/Consortium/Legal/ for more details. - */ - -package org.w3c.dom.traversal; - -import org.w3c.dom.Node; -import org.w3c.dom.DOMException; - -/** - * <code>DocumentTraversal</code> contains methods that create - * <code>NodeIterators</code> and <code>TreeWalkers</code> to traverse a - * node and its children in document order (depth first, pre-order - * traversal, which is equivalent to the order in which the start tags occur - * in the text representation of the document). In DOMs which support the - * Traversal feature, <code>DocumentTraversal</code> will be implemented by - * the same objects that implement the Document interface. - * <p>See also the <a href='http://www.w3.org/TR/2000/REC-DOM-Level-2-Traversal-Range-20001113'>Document Object Model (DOM) Level 2 Traversal and Range Specification</a>. - * @since DOM Level 2 - */ -public interface DocumentTraversal { - /** - * Create a new <code>NodeIterator</code> over the subtree rooted at the - * specified node. - * @param root The node which will be iterated together with its - * children. The <code>NodeIterator</code> is initially positioned - * just before this node. The <code>whatToShow</code> flags and the - * filter, if any, are not considered when setting this position. The - * root must not be <code>null</code>. - * @param whatToShow This flag specifies which node types may appear in - * the logical view of the tree presented by the - * <code>NodeIterator</code>. See the description of - * <code>NodeFilter</code> for the set of possible <code>SHOW_</code> - * values.These flags can be combined using <code>OR</code>. - * @param filter The <code>NodeFilter</code> to be used with this - * <code>NodeIterator</code>, or <code>null</code> to indicate no - * filter. - * @param entityReferenceExpansion The value of this flag determines - * whether entity reference nodes are expanded. - * @return The newly created <code>NodeIterator</code>. - * @exception DOMException - * NOT_SUPPORTED_ERR: Raised if the specified <code>root</code> is - * <code>null</code>. - */ - public NodeIterator createNodeIterator(Node root, - int whatToShow, - NodeFilter filter, - boolean entityReferenceExpansion) - throws DOMException; - - /** - * Create a new <code>TreeWalker</code> over the subtree rooted at the - * specified node. - * @param root The node which will serve as the <code>root</code> for the - * <code>TreeWalker</code>. The <code>whatToShow</code> flags and the - * <code>NodeFilter</code> are not considered when setting this value; - * any node type will be accepted as the <code>root</code>. The - * <code>currentNode</code> of the <code>TreeWalker</code> is - * initialized to this node, whether or not it is visible. The - * <code>root</code> functions as a stopping point for traversal - * methods that look upward in the document structure, such as - * <code>parentNode</code> and nextNode. The <code>root</code> must - * not be <code>null</code>. - * @param whatToShow This flag specifies which node types may appear in - * the logical view of the tree presented by the - * <code>TreeWalker</code>. See the description of - * <code>NodeFilter</code> for the set of possible <code>SHOW_</code> - * values.These flags can be combined using <code>OR</code>. - * @param filter The <code>NodeFilter</code> to be used with this - * <code>TreeWalker</code>, or <code>null</code> to indicate no filter. - * @param entityReferenceExpansion If this flag is false, the contents of - * <code>EntityReference</code> nodes are not presented in the logical - * view. - * @return The newly created <code>TreeWalker</code>. - * @exception DOMException - * NOT_SUPPORTED_ERR: Raised if the specified <code>root</code> is - * <code>null</code>. - */ - public TreeWalker createTreeWalker(Node root, - int whatToShow, - NodeFilter filter, - boolean entityReferenceExpansion) - throws DOMException; - -} diff --git a/xml/src/main/java/org/w3c/dom/traversal/NodeFilter.java b/xml/src/main/java/org/w3c/dom/traversal/NodeFilter.java index b9beac4..4d179e7 100644 --- a/xml/src/main/java/org/w3c/dom/traversal/NodeFilter.java +++ b/xml/src/main/java/org/w3c/dom/traversal/NodeFilter.java @@ -31,6 +31,8 @@ import org.w3c.dom.Node; * encouraging code reuse. * <p>See also the <a href='http://www.w3.org/TR/2000/REC-DOM-Level-2-Traversal-Range-20001113'>Document Object Model (DOM) Level 2 Traversal and Range Specification</a>. * @since DOM Level 2 + * + * @hide */ public interface NodeFilter { // Constants returned by acceptNode diff --git a/xml/src/main/java/org/w3c/dom/traversal/NodeIterator.java b/xml/src/main/java/org/w3c/dom/traversal/NodeIterator.java index d1f0d08..e55cd9f 100644 --- a/xml/src/main/java/org/w3c/dom/traversal/NodeIterator.java +++ b/xml/src/main/java/org/w3c/dom/traversal/NodeIterator.java @@ -27,6 +27,8 @@ import org.w3c.dom.DOMException; * <code>DocumentTraversal</code><code>.createNodeIterator()</code>. * <p>See also the <a href='http://www.w3.org/TR/2000/REC-DOM-Level-2-Traversal-Range-20001113'>Document Object Model (DOM) Level 2 Traversal and Range Specification</a>. * @since DOM Level 2 + * + * @hide */ public interface NodeIterator { /** diff --git a/xml/src/main/java/org/w3c/dom/traversal/TreeWalker.java b/xml/src/main/java/org/w3c/dom/traversal/TreeWalker.java deleted file mode 100644 index f5fff86..0000000 --- a/xml/src/main/java/org/w3c/dom/traversal/TreeWalker.java +++ /dev/null @@ -1,179 +0,0 @@ -/* - * Copyright (c) 2000 World Wide Web Consortium, - * (Massachusetts Institute of Technology, Institut National de - * Recherche en Informatique et en Automatique, Keio University). All - * Rights Reserved. This program is distributed under the W3C's Software - * Intellectual Property License. This program is distributed in the - * hope that it will be useful, but WITHOUT ANY WARRANTY; without even - * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. - * See W3C License http://www.w3.org/Consortium/Legal/ for more details. - */ - -package org.w3c.dom.traversal; - -import org.w3c.dom.Node; -import org.w3c.dom.DOMException; - -/** - * <code>TreeWalker</code> objects are used to navigate a document tree or - * subtree using the view of the document defined by their - * <code>whatToShow</code> flags and filter (if any). Any function which - * performs navigation using a <code>TreeWalker</code> will automatically - * support any view defined by a <code>TreeWalker</code>. - * <p>Omitting nodes from the logical view of a subtree can result in a - * structure that is substantially different from the same subtree in the - * complete, unfiltered document. Nodes that are siblings in the - * <code>TreeWalker</code> view may be children of different, widely - * separated nodes in the original view. For instance, consider a - * <code>NodeFilter</code> that skips all nodes except for Text nodes and - * the root node of a document. In the logical view that results, all text - * nodes will be siblings and appear as direct children of the root node, no - * matter how deeply nested the structure of the original document. - * <p>See also the <a href='http://www.w3.org/TR/2000/REC-DOM-Level-2-Traversal-Range-20001113'>Document Object Model (DOM) Level 2 Traversal and Range Specification</a>. - * @since DOM Level 2 - */ -public interface TreeWalker { - /** - * The <code>root</code> node of the <code>TreeWalker</code>, as specified - * when it was created. - */ - public Node getRoot(); - - /** - * This attribute determines which node types are presented via the - * <code>TreeWalker</code>. The available set of constants is defined in - * the <code>NodeFilter</code> interface. Nodes not accepted by - * <code>whatToShow</code> will be skipped, but their children may still - * be considered. Note that this skip takes precedence over the filter, - * if any. - */ - public int getWhatToShow(); - - /** - * The filter used to screen nodes. - */ - public NodeFilter getFilter(); - - /** - * The value of this flag determines whether the children of entity - * reference nodes are visible to the <code>TreeWalker</code>. If false, - * these children and their descendants will be rejected. Note that - * this rejection takes precedence over <code>whatToShow</code> and the - * filter, if any. - * <br> To produce a view of the document that has entity references - * expanded and does not expose the entity reference node itself, use - * the <code>whatToShow</code> flags to hide the entity reference node - * and set <code>expandEntityReferences</code> to true when creating the - * <code>TreeWalker</code>. To produce a view of the document that has - * entity reference nodes but no entity expansion, use the - * <code>whatToShow</code> flags to show the entity reference node and - * set <code>expandEntityReferences</code> to false. - */ - public boolean getExpandEntityReferences(); - - /** - * The node at which the <code>TreeWalker</code> is currently positioned. - * <br>Alterations to the DOM tree may cause the current node to no longer - * be accepted by the <code>TreeWalker</code>'s associated filter. - * <code>currentNode</code> may also be explicitly set to any node, - * whether or not it is within the subtree specified by the - * <code>root</code> node or would be accepted by the filter and - * <code>whatToShow</code> flags. Further traversal occurs relative to - * <code>currentNode</code> even if it is not part of the current view, - * by applying the filters in the requested direction; if no traversal - * is possible, <code>currentNode</code> is not changed. - */ - public Node getCurrentNode(); - /** - * The node at which the <code>TreeWalker</code> is currently positioned. - * <br>Alterations to the DOM tree may cause the current node to no longer - * be accepted by the <code>TreeWalker</code>'s associated filter. - * <code>currentNode</code> may also be explicitly set to any node, - * whether or not it is within the subtree specified by the - * <code>root</code> node or would be accepted by the filter and - * <code>whatToShow</code> flags. Further traversal occurs relative to - * <code>currentNode</code> even if it is not part of the current view, - * by applying the filters in the requested direction; if no traversal - * is possible, <code>currentNode</code> is not changed. - * @exception DOMException - * NOT_SUPPORTED_ERR: Raised if an attempt is made to set - * <code>currentNode</code> to <code>null</code>. - */ - public void setCurrentNode(Node currentNode) - throws DOMException; - - /** - * Moves to and returns the closest visible ancestor node of the current - * node. If the search for <code>parentNode</code> attempts to step - * upward from the <code>TreeWalker</code>'s <code>root</code> node, or - * if it fails to find a visible ancestor node, this method retains the - * current position and returns <code>null</code>. - * @return The new parent node, or <code>null</code> if the current node - * has no parent in the <code>TreeWalker</code>'s logical view. - */ - public Node parentNode(); - - /** - * Moves the <code>TreeWalker</code> to the first visible child of the - * current node, and returns the new node. If the current node has no - * visible children, returns <code>null</code>, and retains the current - * node. - * @return The new node, or <code>null</code> if the current node has no - * visible children in the <code>TreeWalker</code>'s logical view. - */ - public Node firstChild(); - - /** - * Moves the <code>TreeWalker</code> to the last visible child of the - * current node, and returns the new node. If the current node has no - * visible children, returns <code>null</code>, and retains the current - * node. - * @return The new node, or <code>null</code> if the current node has no - * children in the <code>TreeWalker</code>'s logical view. - */ - public Node lastChild(); - - /** - * Moves the <code>TreeWalker</code> to the previous sibling of the - * current node, and returns the new node. If the current node has no - * visible previous sibling, returns <code>null</code>, and retains the - * current node. - * @return The new node, or <code>null</code> if the current node has no - * previous sibling. in the <code>TreeWalker</code>'s logical view. - */ - public Node previousSibling(); - - /** - * Moves the <code>TreeWalker</code> to the next sibling of the current - * node, and returns the new node. If the current node has no visible - * next sibling, returns <code>null</code>, and retains the current node. - * @return The new node, or <code>null</code> if the current node has no - * next sibling. in the <code>TreeWalker</code>'s logical view. - */ - public Node nextSibling(); - - /** - * Moves the <code>TreeWalker</code> to the previous visible node in - * document order relative to the current node, and returns the new - * node. If the current node has no previous node, or if the search for - * <code>previousNode</code> attempts to step upward from the - * <code>TreeWalker</code>'s <code>root</code> node, returns - * <code>null</code>, and retains the current node. - * @return The new node, or <code>null</code> if the current node has no - * previous node in the <code>TreeWalker</code>'s logical view. - */ - public Node previousNode(); - - /** - * Moves the <code>TreeWalker</code> to the next visible node in document - * order relative to the current node, and returns the new node. If the - * current node has no next node, or if the search for nextNode attempts - * to step upward from the <code>TreeWalker</code>'s <code>root</code> - * node, returns <code>null</code>, and retains the current node. - * @return The new node, or <code>null</code> if the current node has no - * next node in the <code>TreeWalker</code>'s logical view. - */ - public Node nextNode(); - -} diff --git a/xml/src/main/java/org/w3c/dom/views/AbstractView.java b/xml/src/main/java/org/w3c/dom/views/AbstractView.java deleted file mode 100644 index 97e8f0e..0000000 --- a/xml/src/main/java/org/w3c/dom/views/AbstractView.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright (c) 2000 World Wide Web Consortium, - * (Massachusetts Institute of Technology, Institut National de - * Recherche en Informatique et en Automatique, Keio University). All - * Rights Reserved. This program is distributed under the W3C's Software - * Intellectual Property License. This program is distributed in the - * hope that it will be useful, but WITHOUT ANY WARRANTY; without even - * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. - * See W3C License http://www.w3.org/Consortium/Legal/ for more details. - */ - -package org.w3c.dom.views; - -/** - * A base interface that all views shall derive from. - * <p>See also the <a href='http://www.w3.org/TR/2000/REC-DOM-Level-2-Views-20001113'>Document Object Model (DOM) Level 2 Views Specification</a>. - * @since DOM Level 2 - */ -public interface AbstractView { - /** - * The source <code>DocumentView</code> of which this is an - * <code>AbstractView</code>. - */ - public DocumentView getDocument(); - -} diff --git a/xml/src/main/java/org/w3c/dom/views/DocumentView.java b/xml/src/main/java/org/w3c/dom/views/DocumentView.java deleted file mode 100644 index 2cb9eeb..0000000 --- a/xml/src/main/java/org/w3c/dom/views/DocumentView.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (c) 2000 World Wide Web Consortium, - * (Massachusetts Institute of Technology, Institut National de - * Recherche en Informatique et en Automatique, Keio University). All - * Rights Reserved. This program is distributed under the W3C's Software - * Intellectual Property License. This program is distributed in the - * hope that it will be useful, but WITHOUT ANY WARRANTY; without even - * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. - * See W3C License http://www.w3.org/Consortium/Legal/ for more details. - */ - -package org.w3c.dom.views; - -/** - * The <code>DocumentView</code> interface is implemented by - * <code>Document</code> objects in DOM implementations supporting DOM - * Views. It provides an attribute to retrieve the default view of a - * document. - * <p>See also the <a href='http://www.w3.org/TR/2000/REC-DOM-Level-2-Views-20001113'>Document Object Model (DOM) Level 2 Views Specification</a>. - * @since DOM Level 2 - */ -public interface DocumentView { - /** - * The default <code>AbstractView</code> for this <code>Document</code>, - * or <code>null</code> if none available. - */ - public AbstractView getDefaultView(); - -} diff --git a/xml/src/test/java/org/apache/harmony/xml/XsltXPathConformanceTestSuite.java b/xml/src/test/java/org/apache/harmony/xml/XsltXPathConformanceTestSuite.java index da37c45..3f0d2cb 100644 --- a/xml/src/test/java/org/apache/harmony/xml/XsltXPathConformanceTestSuite.java +++ b/xml/src/test/java/org/apache/harmony/xml/XsltXPathConformanceTestSuite.java @@ -22,15 +22,16 @@ import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestSuite; import org.w3c.dom.Attr; -import org.w3c.dom.Comment; import org.w3c.dom.Document; import org.w3c.dom.Element; +import org.w3c.dom.EntityReference; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.w3c.dom.ProcessingInstruction; -import org.w3c.dom.Text; +import org.xml.sax.InputSource; import org.xml.sax.SAXException; +import org.xml.sax.SAXParseException; import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlPullParserFactory; import org.xmlpull.v1.XmlSerializer; @@ -38,17 +39,25 @@ import org.xmlpull.v1.XmlSerializer; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; +import javax.xml.transform.ErrorListener; import javax.xml.transform.Result; import javax.xml.transform.Source; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerConfigurationException; +import javax.xml.transform.TransformerException; import javax.xml.transform.TransformerFactory; import javax.xml.transform.dom.DOMResult; +import javax.xml.transform.stream.StreamResult; import javax.xml.transform.stream.StreamSource; +import java.io.BufferedInputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.io.StringReader; import java.io.StringWriter; import java.util.ArrayList; import java.util.Collections; @@ -60,13 +69,23 @@ import java.util.List; * XSLT conformance test suite</a>, adapted for use by JUnit. To run these tests * on a device: * <ul> - * <li>Obtain the <a href="http://www.oasis-open.org/committees/download.php/12171/XSLT-testsuite-04.ZIP">test - * suite zip file from the OASIS project site.</li> - * <li>Unzip. - * <li>Copy the files to a device: <code>adb shell mkdir /data/oasis ; - * adb push ./XSLT-Conformance-TC /data/oasis</code>. - * <li>Invoke this class' main method, passing the on-device path to the test - * suite's <code>catalog.xml</code> file as an argument. + * <li>Obtain the <a href="http://www.oasis-open.org/committees/download.php/12171/XSLT-testsuite-04.ZIP">test + * suite zip file from the OASIS project site.</li> + * <li>Unzip. + * <li>Copy the files to a device: <code>adb shell mkdir /data/oasis ; + * adb push ./XSLT-Conformance-TC /data/oasis</code>. + * <li>Invoke this class' main method, passing the on-device path to the test + * suite's <code>catalog.xml</code> file as an argument. + * </ul> + * + * <p>Unfortunately, some of the tests in the OASIS suite will fail when + * executed outside of their original development environment: + * <ul> + * <li>The tests assume case insensitive filesystems. Some will fail with + * "Couldn't open file" errors due to a mismatch in file name casing. + * <li>The tests assume certain network hosts will exist and serve + * stylesheet files. In particular, "http://webxtest/" isn't generally + * available. * </ul> */ public class XsltXPathConformanceTestSuite { @@ -77,7 +96,7 @@ public class XsltXPathConformanceTestSuite { /** Orders element attributes by optional URI and name. */ private static final Comparator<Attr> orderByName = new Comparator<Attr>() { public int compare(Attr a, Attr b) { - int result = compareNullsFirst(a.getBaseURI(), b.getBaseURI()); + int result = compareNullsFirst(a.getNamespaceURI(), b.getNamespaceURI()); return result == 0 ? result : compareNullsFirst(a.getName(), b.getName()); } @@ -163,7 +182,7 @@ public class XsltXPathConformanceTestSuite { /** * Returns a JUnit test for the test described by the given element. */ - private Test create(File base, Element testCaseElement) { + private TestCase create(File base, Element testCaseElement) { /* * Extract the XSLT test from a DOM entity with the following structure: @@ -290,7 +309,6 @@ public class XsltXPathConformanceTestSuite { * the result to an expected output file. */ public class XsltTest extends TestCase { - // TODO: include these in toString private final String category; private final String id; private final String purpose; @@ -303,7 +321,10 @@ public class XsltXPathConformanceTestSuite { /** either "standard" or "execution-error" */ private final String operation; - /** the syntax to compare the output file using, such as "XML" or "HTML" */ + /** + * The syntax to compare the output file using, such as "XML", "HTML", + * "manual", or null for expected execution errors. + */ private final String compareAs; XsltTest(String category, String id, String purpose, String spec, @@ -321,6 +342,11 @@ public class XsltXPathConformanceTestSuite { this.compareAs = compareAs; } + XsltTest(File principalData, File principalStylesheet, File principal) { + this("standalone", "test", "", "", + principalData, principalStylesheet, principal, "standard", "XML"); + } + public void test() throws Exception { if (purpose != null) { System.out.println("Purpose: " + purpose); @@ -329,37 +355,50 @@ public class XsltXPathConformanceTestSuite { System.out.println("Spec: " + spec); } - Source xslt = new StreamSource(principalStylesheet); - Source in = new StreamSource(principalData); + Result result; + if ("XML".equals(compareAs)) { + DOMResult domResult = new DOMResult(); + domResult.setNode(documentBuilder.newDocument().createElementNS("", "result")); + result = domResult; + } else { + result = new StreamResult(new StringWriter()); + } + + ErrorRecorder errorRecorder = new ErrorRecorder(); + transformerFactory.setErrorListener(errorRecorder); Transformer transformer; try { + Source xslt = new StreamSource(principalStylesheet); transformer = transformerFactory.newTransformer(xslt); - assertEquals("Expected transformer creation to fail", - "standard", operation); + if (errorRecorder.error == null) { + transformer.setErrorListener(errorRecorder); + transformer.transform(new StreamSource(principalData), result); + } } catch (TransformerConfigurationException e) { - if (operation.equals("execution-error")) { - return; // expected, such as in XSLT-Result-Tree.Attributes__78369 + errorRecorder.fatalError(e); + } + + if (operation.equals("standard")) { + if (errorRecorder.error != null) { + throw errorRecorder.error; } - AssertionFailedError failure = new AssertionFailedError(); - failure.initCause(e); - throw failure; + } else if (operation.equals("execution-error")) { + if (errorRecorder.error != null) { + return; + } + fail("Expected " + operation + ", but transform completed normally." + + " (Warning=" + errorRecorder.warning + ")"); + } else { + throw new UnsupportedOperationException("Unexpected operation: " + operation); } - Result result; - if (compareAs.equals("XML")) { - result = new DOMResult(); + if ("XML".equals(compareAs)) { + assertNodesAreEquivalent(principal, ((DOMResult) result).getNode()); } else { // TODO: implement support for comparing HTML etc. throw new UnsupportedOperationException("Cannot compare as " + compareAs); } - - transformer.transform(in, result); - - if (compareAs.equals("XML")) { - DOMResult domResult = (DOMResult) result; - assertNodesAreEquivalent(principal, domResult.getNode()); - } } @Override public String getName() { @@ -370,19 +409,65 @@ public class XsltXPathConformanceTestSuite { /** * Ensures both XML documents represent the same semantic data. Non-semantic * data such as namespace prefixes, comments, and whitespace is ignored. + * + * @param actual an XML document whose root is a {@code <result>} element. + * @param expected a file containing an XML document fragment. */ private void assertNodesAreEquivalent(File expected, Node actual) throws ParserConfigurationException, IOException, SAXException, XmlPullParserException { - Document expectedDocument = documentBuilder.parse(new FileInputStream(expected)); - String expectedString = nodeToNormalizedString(expectedDocument); + Node expectedNode = fileToResultNode(expected); + String expectedString = nodeToNormalizedString(expectedNode); String actualString = nodeToNormalizedString(actual); Assert.assertEquals("Expected XML to match file " + expected, expectedString, actualString); } + /** + * Returns the given file's XML fragment as a single node, wrapped in + * {@code <result>} tags. This takes care of normalizing the following + * conditions: + * + * <ul> + * <li>Files containing XML document fragments with multiple elements: + * {@code <SPAN style="color=blue">Smurfs!</SPAN><br />} + * + * <li>Files containing XML document fragments with no elements: + * {@code Smurfs!} + * + * <li>Files containing proper XML documents with a single element and an + * XML declaration: + * {@code <?xml version="1.0"?><doc />} + * + * <li>Files prefixed with a byte order mark header, such as 0xEFBBBF. + * </ul> + */ + private Node fileToResultNode(File file) throws IOException, SAXException { + String rawContents = fileToString(file); + String fragment = rawContents; + + // If the file had an XML declaration, strip that. Otherwise wrapping + // it in <result> tags would result in a malformed XML document. + if (fragment.startsWith("<?xml")) { + int declarationEnd = fragment.indexOf("?>"); + fragment = fragment.substring(declarationEnd + 2); + } + + // Parse it as document fragment wrapped in <result> tags. + try { + fragment = "<result>" + fragment + "</result>"; + return documentBuilder.parse(new InputSource(new StringReader(fragment))) + .getDocumentElement(); + } catch (SAXParseException e) { + Error error = new AssertionFailedError( + "Failed to parse XML: " + file + "\n" + rawContents); + error.initCause(e); + throw error; + } + } + private String nodeToNormalizedString(Node node) throws XmlPullParserException, IOException { StringWriter writer = new StringWriter(); @@ -395,14 +480,18 @@ public class XsltXPathConformanceTestSuite { } private void emitNode(XmlSerializer serializer, Node node) throws IOException { - if (node instanceof Element) { + if (node == null) { + throw new UnsupportedOperationException("Cannot emit null nodes"); + + } else if (node.getNodeType() == Node.ELEMENT_NODE) { Element element = (Element) node; - serializer.startTag(element.getBaseURI(), element.getLocalName()); + serializer.startTag(element.getNamespaceURI(), element.getLocalName()); emitAttributes(serializer, element); emitChildren(serializer, element); - serializer.endTag(element.getBaseURI(), element.getLocalName()); + serializer.endTag(element.getNamespaceURI(), element.getLocalName()); - } else if (node instanceof Text) { + } else if (node.getNodeType() == Node.TEXT_NODE + || node.getNodeType() == Node.CDATA_SECTION_NODE) { // TODO: is it okay to trim whitespace in general? This may cause // false positives for elements like HTML's <pre> tag String trimmed = node.getTextContent().trim(); @@ -410,25 +499,28 @@ public class XsltXPathConformanceTestSuite { serializer.text(trimmed); } - } else if (node instanceof Document) { + } else if (node.getNodeType() == Node.DOCUMENT_NODE) { Document document = (Document) node; serializer.startDocument("UTF-8", true); emitNode(serializer, document.getDocumentElement()); serializer.endDocument(); - } else if (node instanceof ProcessingInstruction) { + } else if (node.getNodeType() == Node.PROCESSING_INSTRUCTION_NODE) { ProcessingInstruction processingInstruction = (ProcessingInstruction) node; String data = processingInstruction.getData(); String target = processingInstruction.getTarget(); serializer.processingInstruction(target + " " + data); - } else if (node instanceof Comment) { + } else if (node.getNodeType() == Node.COMMENT_NODE) { // ignore! + } else if (node.getNodeType() == Node.ENTITY_REFERENCE_NODE) { + EntityReference entityReference = (EntityReference) node; + serializer.entityRef(entityReference.getNodeName()); + } else { - Object nodeClass = node != null ? node.getClass() : null; throw new UnsupportedOperationException( - "Cannot serialize nodes of type " + nodeClass); + "Cannot emit " + node + " of type " + node.getNodeType()); } } @@ -457,7 +549,7 @@ public class XsltXPathConformanceTestSuite { * generate one for us, using a predictable pattern. */ } else { - serializer.attribute(attr.getBaseURI(), attr.getLocalName(), attr.getValue()); + serializer.attribute(attr.getNamespaceURI(), attr.getLocalName(), attr.getValue()); } } } @@ -480,4 +572,64 @@ public class XsltXPathConformanceTestSuite { } return result; } + + /** + * Reads the given file into a string. If the file contains a byte order + * mark, the corresponding character set will be used. Otherwise the system + * default charset will be used. + */ + private String fileToString(File file) throws IOException { + InputStream in = new BufferedInputStream(new FileInputStream(file), 1024); + + // Read the byte order mark to determine the charset. + // TODO: use a built-in API for this... + Reader reader; + in.mark(3); + int byte1 = in.read(); + int byte2 = in.read(); + if (byte1 == 0xFF && byte2 == 0xFE) { + reader = new InputStreamReader(in, "UTF-16LE"); + } else if (byte1 == 0xFF && byte2 == 0xFF) { + reader = new InputStreamReader(in, "UTF-16BE"); + } else { + int byte3 = in.read(); + if (byte1 == 0xEF && byte2 == 0xBB && byte3 == 0xBF) { + reader = new InputStreamReader(in, "UTF-8"); + } else { + in.reset(); + reader = new InputStreamReader(in); + } + } + + StringWriter out = new StringWriter(); + char[] buffer = new char[1024]; + int count; + while ((count = reader.read(buffer)) != -1) { + out.write(buffer, 0, count); + } + return out.toString(); + } + + static class ErrorRecorder implements ErrorListener { + Exception warning; + Exception error; + + public void warning(TransformerException exception) { + if (this.warning == null) { + this.warning = exception; + } + } + + public void error(TransformerException exception) { + if (this.error == null) { + this.error = exception; + } + } + + public void fatalError(TransformerException exception) { + if (this.error == null) { + this.error = exception; + } + } + } } diff --git a/xml/src/test/java/tests/api/javax/xml/parsers/DocumentBuilderTest.java b/xml/src/test/java/tests/api/javax/xml/parsers/DocumentBuilderTest.java index 7318ad0..cfa62dc 100644 --- a/xml/src/test/java/tests/api/javax/xml/parsers/DocumentBuilderTest.java +++ b/xml/src/test/java/tests/api/javax/xml/parsers/DocumentBuilderTest.java @@ -34,6 +34,7 @@ import org.xml.sax.SAXParseException; import tests.api.org.xml.sax.support.MethodLogger; import tests.api.org.xml.sax.support.MockHandler; import tests.api.org.xml.sax.support.MockResolver; +import tests.support.resource.Support_Resources; import tests.util.TestEnvironment; import javax.xml.parsers.DocumentBuilder; @@ -41,8 +42,6 @@ import javax.xml.parsers.DocumentBuilderFactory; import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; @@ -271,7 +270,7 @@ public class DocumentBuilderTest extends TestCase { args = {java.io.File.class} ) public void testGetBaseURI() throws IOException, SAXException { - File f = resourceToTmpFile("/simple.xml"); + File f = Support_Resources.resourceToTempFile("/simple.xml"); Document d = db.parse(f); assertTrue(d.getDocumentElement().getBaseURI().startsWith("file://")); } @@ -290,7 +289,7 @@ public class DocumentBuilderTest extends TestCase { args = {java.io.File.class} ) public void test_parseLjava_io_File() throws IOException { - File f = resourceToTmpFile("/simple.xml"); + File f = Support_Resources.resourceToTempFile("/simple.xml"); // case 1: Trivial use. try { @@ -332,7 +331,7 @@ public class DocumentBuilderTest extends TestCase { } // case 4: Try to parse incorrect xml file - f = resourceToTmpFile("/wrong.xml"); + f = Support_Resources.resourceToTempFile("/wrong.xml"); try { db.parse(f); fail("Expected SAXException was not thrown"); @@ -343,22 +342,6 @@ public class DocumentBuilderTest extends TestCase { } } - private File resourceToTmpFile(String path) throws IOException, - FileNotFoundException { - File f = File.createTempFile("out", ".xml"); - f.deleteOnExit(); - FileOutputStream out = new FileOutputStream(f); - - InputStream xml = getClass().getResourceAsStream(path); - while (xml.available() > 0) { - out.write(xml.read()); - } - out.flush(); - out.close(); - xml.close(); - return f; - } - /** * @tests javax.xml.parsers.DocumentBuilder#parse(java.io.InputStream) * Case 1: Try to parse correct xml document. diff --git a/xml/src/test/java/tests/xml/AllTests.java b/xml/src/test/java/tests/xml/AllTests.java index 8a4b4a9..0042967 100644 --- a/xml/src/test/java/tests/xml/AllTests.java +++ b/xml/src/test/java/tests/xml/AllTests.java @@ -26,7 +26,7 @@ public class AllTests { suite.addTestSuite(SimpleParserTest.class); suite.addTestSuite(SimpleBuilderTest.class); - suite.addTestSuite(NodeTests.class); + suite.addTestSuite(NodeTest.class); //suite.addTest(tests.org.w3c.dom.AllTests.suite()); suite.addTest(tests.api.javax.xml.parsers.AllTests.suite()); diff --git a/xml/src/test/java/tests/xml/DomTest.java b/xml/src/test/java/tests/xml/DomTest.java new file mode 100644 index 0000000..5f0a19a --- /dev/null +++ b/xml/src/test/java/tests/xml/DomTest.java @@ -0,0 +1,623 @@ +/* + * 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 tests.xml; + +import junit.framework.TestCase; +import org.w3c.dom.Attr; +import org.w3c.dom.CDATASection; +import org.w3c.dom.Comment; +import org.w3c.dom.DOMException; +import org.w3c.dom.DOMImplementation; +import org.w3c.dom.Document; +import org.w3c.dom.DocumentType; +import org.w3c.dom.Element; +import org.w3c.dom.Entity; +import org.w3c.dom.EntityReference; +import org.w3c.dom.Node; +import org.w3c.dom.Notation; +import org.w3c.dom.ProcessingInstruction; +import org.w3c.dom.Text; +import org.xml.sax.InputSource; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerException; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.dom.DOMSource; +import javax.xml.transform.stream.StreamResult; +import java.io.StringReader; +import java.io.StringWriter; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * Construct a DOM and then interrogate it. + */ +public class DomTest extends TestCase { + + private Transformer transformer; + private DocumentBuilder builder; + private DOMImplementation domImplementation; + + private final String xml + = "<!DOCTYPE menu [" + + " <!ENTITY sp \"Maple Syrup\">" + + " <!NOTATION png SYSTEM \"image/png\">" + + "]>" + + "<menu>\n" + + " <item xmlns=\"http://food\" xmlns:a=\"http://addons\">\n" + + " <name a:standard=\"strawberry\" deluxe=\"&sp;\">Waffles</name>\n" + + " <description xmlns=\"http://marketing\">Belgian<![CDATA[ waffles & strawberries (< 5g ]]>of fat)</description>\n" + + " <a:option>Whipped Cream</a:option>\n" + + " <a:option>&sp;</a:option>\n" + + " <?wafflemaker square shape?>\n" + + " <nutrition>\n" + + " <a:vitamins xmlns:a=\"http://usda\">\n" + + " <!-- add other vitamins? --> \n" + + " <a:vitaminc>60%</a:vitaminc>\n" + + " </a:vitamins>\n" + + " </nutrition>\n" + + " </item>\n" + + "</menu>"; + + private Document document; + private DocumentType doctype; + private Entity sp; + private Notation png; + private Element menu; + private Element item; + private Attr itemXmlns; + private Attr itemXmlnsA; + private Element name; + private Attr standard; + private Attr deluxe; + private Element description; + private Text descriptionText1; + private CDATASection descriptionText2; + private Text descriptionText3; + private Element option1; + private Element option2; + private Node option2Reference; // resolved to Text on RI, an EntityReference on Dalvik + private ProcessingInstruction wafflemaker; + private Element nutrition; + private Element vitamins; + private Attr vitaminsXmlnsA; + private Comment comment; + private Element vitaminc; + private Text vitamincText; + private List<Node> allNodes; + + @Override protected void setUp() throws Exception { + transformer = TransformerFactory.newInstance().newTransformer(); + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + factory.setNamespaceAware(true); + builder = factory.newDocumentBuilder(); + domImplementation = builder.getDOMImplementation(); + + document = builder.parse(new InputSource(new StringReader(xml))); + + // doctype nodes + doctype = document.getDoctype(); + if (doctype.getEntities() != null) { + sp = (Entity) doctype.getEntities().item(0); + } + if (doctype.getNotations() != null) { + png = (Notation) doctype.getNotations().item(0); + } + + // document nodes + menu = document.getDocumentElement(); + item = (Element) menu.getChildNodes().item(1); + itemXmlns = item.getAttributeNode("xmlns"); + itemXmlnsA = item.getAttributeNode("xmlns:a"); + name = (Element) item.getChildNodes().item(1); + standard = name.getAttributeNode("a:standard"); + deluxe = name.getAttributeNode("deluxe"); + description = (Element) item.getChildNodes().item(3); + descriptionText1 = (Text) description.getChildNodes().item(0); + descriptionText2 = (CDATASection) description.getChildNodes().item(1); + descriptionText3 = (Text) description.getChildNodes().item(2); + option1 = (Element) item.getChildNodes().item(5); + option2 = (Element) item.getChildNodes().item(7); + option2Reference = option2.getChildNodes().item(0); + wafflemaker = (ProcessingInstruction) item.getChildNodes().item(9); + nutrition = (Element) item.getChildNodes().item(11); + vitamins = (Element) nutrition.getChildNodes().item(1); + vitaminsXmlnsA = vitamins.getAttributeNode("xmlns:a"); + comment = (Comment) vitamins.getChildNodes().item(1); + vitaminc = (Element) vitamins.getChildNodes().item(3); + vitamincText = (Text) vitaminc.getChildNodes().item(0); + + allNodes = new ArrayList<Node>(); + + if (sp != null) { + allNodes.add(sp); + } + if (png != null) { + allNodes.add(png); + } + + allNodes.addAll(Arrays.asList(document, doctype, menu, item, itemXmlns, + itemXmlnsA, name, standard, deluxe, description, + descriptionText1, descriptionText2, descriptionText3, option1, + option2, option2Reference, wafflemaker, nutrition, vitamins, + vitaminsXmlnsA, comment, vitaminc, vitamincText)); + } + + /** + * Android's parsed DOM doesn't include entity declarations. These nodes will + * only be tested for implementations that support them. + */ + public void testEntityDeclarations() { + assertNotNull("This implementation does not parse entity declarations", sp); + } + + /** + * Android's parsed DOM doesn't include notations. These nodes will only be + * tested for implementations that support them. + */ + public void testNotations() { + assertNotNull("This implementation does not parse notations", png); + } + + public void testLookupNamespaceURIByPrefix() { + assertEquals(null, doctype.lookupNamespaceURI("a")); + if (sp != null) { + assertEquals(null, sp.lookupNamespaceURI("a")); + } + if (png != null) { + assertEquals(null, png.lookupNamespaceURI("a")); + } + assertEquals(null, document.lookupNamespaceURI("a")); + assertEquals(null, menu.lookupNamespaceURI("a")); + assertEquals("http://addons", item.lookupNamespaceURI("a")); + assertEquals("http://addons", itemXmlns.lookupNamespaceURI("a")); + assertEquals("http://addons", itemXmlnsA.lookupNamespaceURI("a")); + assertEquals("http://addons", name.lookupNamespaceURI("a")); + assertEquals("http://addons", standard.lookupNamespaceURI("a")); + assertEquals("http://addons", deluxe.lookupNamespaceURI("a")); + assertEquals("http://addons", description.lookupNamespaceURI("a")); + assertEquals("http://addons", descriptionText1.lookupNamespaceURI("a")); + assertEquals("http://addons", descriptionText2.lookupNamespaceURI("a")); + assertEquals("http://addons", descriptionText3.lookupNamespaceURI("a")); + assertEquals("http://addons", option1.lookupNamespaceURI("a")); + assertEquals("http://addons", option2.lookupNamespaceURI("a")); + assertEquals("http://addons", option2Reference.lookupNamespaceURI("a")); + assertEquals("http://addons", wafflemaker.lookupNamespaceURI("a")); + assertEquals("http://addons", nutrition.lookupNamespaceURI("a")); + assertEquals("http://usda", vitamins.lookupNamespaceURI("a")); + assertEquals("http://usda", vitaminsXmlnsA.lookupNamespaceURI("a")); + assertEquals("http://usda", comment.lookupNamespaceURI("a")); + assertEquals("http://usda", vitaminc.lookupNamespaceURI("a")); + assertEquals("http://usda", vitamincText.lookupNamespaceURI("a")); + } + + public void testLookupNamespaceURIWithNullPrefix() { + assertEquals(null, document.lookupNamespaceURI(null)); + assertEquals(null, doctype.lookupNamespaceURI(null)); + if (sp != null) { + assertEquals(null, sp.lookupNamespaceURI(null)); + } + if (png != null) { + assertEquals(null, png.lookupNamespaceURI(null)); + } + assertEquals(null, menu.lookupNamespaceURI(null)); + assertEquals("http://food", item.lookupNamespaceURI(null)); + assertEquals("http://food", itemXmlns.lookupNamespaceURI(null)); + assertEquals("http://food", itemXmlnsA.lookupNamespaceURI(null)); + assertEquals("http://food", name.lookupNamespaceURI(null)); + assertEquals("http://food", standard.lookupNamespaceURI(null)); + assertEquals("http://food", deluxe.lookupNamespaceURI(null)); + assertEquals("http://marketing", description.lookupNamespaceURI(null)); + assertEquals("http://marketing", descriptionText1.lookupNamespaceURI(null)); + assertEquals("http://marketing", descriptionText2.lookupNamespaceURI(null)); + assertEquals("http://marketing", descriptionText3.lookupNamespaceURI(null)); + assertEquals("http://food", option1.lookupNamespaceURI(null)); + assertEquals("http://food", option2.lookupNamespaceURI(null)); + assertEquals("http://food", option2Reference.lookupNamespaceURI(null)); + assertEquals("http://food", wafflemaker.lookupNamespaceURI(null)); + assertEquals("http://food", nutrition.lookupNamespaceURI(null)); + assertEquals("http://food", vitamins.lookupNamespaceURI(null)); + assertEquals("http://food", vitaminsXmlnsA.lookupNamespaceURI(null)); + assertEquals("http://food", comment.lookupNamespaceURI(null)); + assertEquals("http://food", vitaminc.lookupNamespaceURI(null)); + assertEquals("http://food", vitamincText.lookupNamespaceURI(null)); + } + + public void testLookupNamespaceURIWithXmlnsPrefix() { + for (Node node : allNodes) { + assertEquals(null, node.lookupNamespaceURI("xmlns")); + } + } + + public void testLookupPrefixWithShadowedUri() { + assertEquals(null, document.lookupPrefix("http://addons")); + assertEquals(null, doctype.lookupPrefix("http://addons")); + if (sp != null) { + assertEquals(null, sp.lookupPrefix("http://addons")); + } + if (png != null) { + assertEquals(null, png.lookupPrefix("http://addons")); + } + assertEquals(null, menu.lookupPrefix("http://addons")); + assertEquals("a", item.lookupPrefix("http://addons")); + assertEquals("a", itemXmlns.lookupPrefix("http://addons")); + assertEquals("a", itemXmlnsA.lookupPrefix("http://addons")); + assertEquals("a", name.lookupPrefix("http://addons")); + assertEquals("a", standard.lookupPrefix("http://addons")); + assertEquals("a", deluxe.lookupPrefix("http://addons")); + assertEquals("a", description.lookupPrefix("http://addons")); + assertEquals("a", descriptionText1.lookupPrefix("http://addons")); + assertEquals("a", descriptionText2.lookupPrefix("http://addons")); + assertEquals("a", descriptionText3.lookupPrefix("http://addons")); + assertEquals("a", option1.lookupPrefix("http://addons")); + assertEquals("a", option2.lookupPrefix("http://addons")); + assertEquals("a", option2Reference.lookupPrefix("http://addons")); + assertEquals("a", wafflemaker.lookupPrefix("http://addons")); + assertEquals("a", nutrition.lookupPrefix("http://addons")); + assertEquals(null, vitamins.lookupPrefix("http://addons")); + assertEquals(null, vitaminsXmlnsA.lookupPrefix("http://addons")); + assertEquals(null, comment.lookupPrefix("http://addons")); + assertEquals(null, vitaminc.lookupPrefix("http://addons")); + assertEquals(null, vitamincText.lookupPrefix("http://addons")); + } + + public void testLookupPrefixWithUnusedUri() { + for (Node node : allNodes) { + assertEquals(null, node.lookupPrefix("http://unused")); + } + } + + public void testLookupPrefixWithNullUri() { + for (Node node : allNodes) { + assertEquals(null, node.lookupPrefix(null)); + } + } + + public void testLookupPrefixWithShadowingUri() { + assertEquals(null, document.lookupPrefix("http://usda")); + assertEquals(null, doctype.lookupPrefix("http://usda")); + if (sp != null) { + assertEquals(null, sp.lookupPrefix("http://usda")); + } + if (png != null) { + assertEquals(null, png.lookupPrefix("http://usda")); + } + assertEquals(null, menu.lookupPrefix("http://usda")); + assertEquals(null, item.lookupPrefix("http://usda")); + assertEquals(null, itemXmlns.lookupPrefix("http://usda")); + assertEquals(null, itemXmlnsA.lookupPrefix("http://usda")); + assertEquals(null, name.lookupPrefix("http://usda")); + assertEquals(null, standard.lookupPrefix("http://usda")); + assertEquals(null, deluxe.lookupPrefix("http://usda")); + assertEquals(null, description.lookupPrefix("http://usda")); + assertEquals(null, descriptionText1.lookupPrefix("http://usda")); + assertEquals(null, descriptionText2.lookupPrefix("http://usda")); + assertEquals(null, descriptionText3.lookupPrefix("http://usda")); + assertEquals(null, option1.lookupPrefix("http://usda")); + assertEquals(null, option2.lookupPrefix("http://usda")); + assertEquals(null, option2Reference.lookupPrefix("http://usda")); + assertEquals(null, wafflemaker.lookupPrefix("http://usda")); + assertEquals(null, nutrition.lookupPrefix("http://usda")); + assertEquals("a", vitamins.lookupPrefix("http://usda")); + assertEquals("a", vitaminsXmlnsA.lookupPrefix("http://usda")); + assertEquals("a", comment.lookupPrefix("http://usda")); + assertEquals("a", vitaminc.lookupPrefix("http://usda")); + assertEquals("a", vitamincText.lookupPrefix("http://usda")); + } + + public void testIsDefaultNamespace() { + assertFalse(document.isDefaultNamespace("http://food")); + assertFalse(doctype.isDefaultNamespace("http://food")); + if (sp != null) { + assertFalse(sp.isDefaultNamespace("http://food")); + } + if (png != null) { + assertFalse(png.isDefaultNamespace("http://food")); + } + assertFalse(menu.isDefaultNamespace("http://food")); + assertTrue(item.isDefaultNamespace("http://food")); + assertTrue(itemXmlns.isDefaultNamespace("http://food")); + assertTrue(itemXmlnsA.isDefaultNamespace("http://food")); + assertTrue(name.isDefaultNamespace("http://food")); + assertTrue(standard.isDefaultNamespace("http://food")); + assertTrue(deluxe.isDefaultNamespace("http://food")); + assertFalse(description.isDefaultNamespace("http://food")); + assertFalse(descriptionText1.isDefaultNamespace("http://food")); + assertFalse(descriptionText2.isDefaultNamespace("http://food")); + assertFalse(descriptionText3.isDefaultNamespace("http://food")); + assertTrue(option1.isDefaultNamespace("http://food")); + assertTrue(option2.isDefaultNamespace("http://food")); + assertTrue(option2Reference.isDefaultNamespace("http://food")); + assertTrue(wafflemaker.isDefaultNamespace("http://food")); + assertTrue(nutrition.isDefaultNamespace("http://food")); + assertTrue(vitamins.isDefaultNamespace("http://food")); + assertTrue(vitaminsXmlnsA.isDefaultNamespace("http://food")); + assertTrue(comment.isDefaultNamespace("http://food")); + assertTrue(vitaminc.isDefaultNamespace("http://food")); + assertTrue(vitamincText.isDefaultNamespace("http://food")); + } + + /** + * Xerces fails this test. It returns false always for entity, notation, + * document fragment and document type nodes. This contradicts its own + * behaviour on lookupNamespaceURI(null). + */ + public void testIsDefaultNamespaceNull_XercesBugs() { + String message = "isDefaultNamespace() should be consistent with lookupNamespaceURI(null)"; + assertTrue(message, doctype.isDefaultNamespace(null)); + if (sp != null) { + assertTrue(message, sp.isDefaultNamespace(null)); + } + if (png != null) { + assertTrue(message, png.isDefaultNamespace(null)); + } + } + + public void testIsDefaultNamespaceNull() { + assertTrue(document.isDefaultNamespace(null)); + assertTrue(menu.isDefaultNamespace(null)); + assertFalse(item.isDefaultNamespace(null)); + assertFalse(itemXmlns.isDefaultNamespace(null)); + assertFalse(itemXmlnsA.isDefaultNamespace(null)); + assertFalse(name.isDefaultNamespace(null)); + assertFalse(standard.isDefaultNamespace(null)); + assertFalse(deluxe.isDefaultNamespace(null)); + assertFalse(description.isDefaultNamespace(null)); + assertFalse(descriptionText1.isDefaultNamespace(null)); + assertFalse(descriptionText2.isDefaultNamespace(null)); + assertFalse(descriptionText3.isDefaultNamespace(null)); + assertFalse(option1.isDefaultNamespace(null)); + assertFalse(option2.isDefaultNamespace(null)); + assertFalse(option2Reference.isDefaultNamespace(null)); + assertFalse(wafflemaker.isDefaultNamespace(null)); + assertFalse(nutrition.isDefaultNamespace(null)); + assertFalse(vitamins.isDefaultNamespace(null)); + assertFalse(vitaminsXmlnsA.isDefaultNamespace(null)); + assertFalse(comment.isDefaultNamespace(null)); + assertFalse(vitaminc.isDefaultNamespace(null)); + assertFalse(vitamincText.isDefaultNamespace(null)); + } + + public void testDoctypeSetTextContent() throws TransformerException { + String original = domToString(document); + doctype.setTextContent("foobar"); // strangely, this is specified to no-op + assertEquals(original, domToString(document)); + } + + public void testDocumentSetTextContent() throws TransformerException { + String original = domToString(document); + document.setTextContent("foobar"); // strangely, this is specified to no-op + assertEquals(original, domToString(document)); + } + + public void testElementSetTextContent() throws TransformerException { + String original = domToString(document); + nutrition.setTextContent("foobar"); + String expected = original.replaceFirst( + "(?s)<nutrition>.*</nutrition>", "<nutrition>foobar</nutrition>"); + assertEquals(expected, domToString(document)); + } + + public void testEntitySetTextContent() throws TransformerException { + if (sp == null) { + return; + } + try { + sp.setTextContent("foobar"); + fail(); // is this implementation-specific behaviour? + } catch (DOMException e) { + } + } + + public void testNotationSetTextContent() throws TransformerException { + if (png == null) { + return; + } + String original = domToString(document); + png.setTextContent("foobar"); + String expected = original.replace("image/png", "foobar"); + assertEquals(expected, domToString(document)); + } + + /** + * Tests setTextContent on entity references. Although the other tests can + * act on a parsed DOM, this needs to use a programmatically constructed DOM + * because the parser may have replaced the entity reference with the + * corresponding text. + */ + public void testEntityReferenceSetTextContent() throws TransformerException { + document = builder.newDocument(); + Element root = document.createElement("menu"); + document.appendChild(root); + + EntityReference entityReference = document.createEntityReference("sp"); + entityReference.setNodeValue("Maple Syrup"); + root.appendChild(entityReference); + + try { + entityReference.setTextContent("Lite Syrup"); + fail(); + } catch (DOMException e) { + } + } + + public void testAttributeSetTextContent() throws TransformerException { + String original = domToString(document); + standard.setTextContent("foobar"); + String expected = original.replaceFirst( + "standard=\"strawberry\"", "standard=\"foobar\""); + assertEquals(expected, domToString(document)); + } + + public void testTextSetTextContent() throws TransformerException { + String original = domToString(document); + descriptionText1.setTextContent("foobar"); + String expected = original.replace(">Belgian<!", ">foobar<!"); + assertEquals(expected, domToString(document)); + } + + public void testCdataSetTextContent() throws TransformerException { + String original = domToString(document); + descriptionText2.setTextContent("foobar"); + String expected = original.replace( + " waffles & strawberries (< 5g ", "foobar"); + assertEquals(expected, domToString(document)); + } + + public void testProcessingInstructionSetTextContent() throws TransformerException { + String original = domToString(document); + wafflemaker.setTextContent("foobar"); + String expected = original.replace(" square shape?>", " foobar?>"); + assertEquals(expected, domToString(document)); + } + + public void testCommentSetTextContent() throws TransformerException { + String original = domToString(document); + comment.setTextContent("foobar"); + String expected = original.replace("-- add other vitamins? --", "--foobar--"); + assertEquals(expected, domToString(document)); + } + + public void testCoreFeature() { + assertTrue(domImplementation.hasFeature("Core", null)); + assertTrue(domImplementation.hasFeature("Core", "")); + assertTrue(domImplementation.hasFeature("Core", "1.0")); + assertTrue(domImplementation.hasFeature("Core", "2.0")); + assertTrue(domImplementation.hasFeature("Core", "3.0")); + assertTrue(domImplementation.hasFeature("CORE", "3.0")); + assertTrue(domImplementation.hasFeature("+Core", "3.0")); + assertFalse(domImplementation.hasFeature("Core", "4.0")); + } + + public void testXmlFeature() { + assertTrue(domImplementation.hasFeature("XML", null)); + assertTrue(domImplementation.hasFeature("XML", "")); + assertTrue(domImplementation.hasFeature("XML", "1.0")); + assertTrue(domImplementation.hasFeature("XML", "2.0")); + assertTrue(domImplementation.hasFeature("XML", "3.0")); + assertTrue(domImplementation.hasFeature("Xml", "3.0")); + assertTrue(domImplementation.hasFeature("+XML", "3.0")); + assertFalse(domImplementation.hasFeature("XML", "4.0")); + } + + /** + * The RI fails this test. + * http://www.w3.org/TR/2004/REC-DOM-Level-3-Core-20040407/core.html#Document3-version + */ + public void testXmlVersionFeature() { + String message = "This implementation does not support the XMLVersion feature"; + assertTrue(message, domImplementation.hasFeature("XMLVersion", null)); + assertTrue(message, domImplementation.hasFeature("XMLVersion", "")); + assertTrue(message, domImplementation.hasFeature("XMLVersion", "1.0")); + assertTrue(message, domImplementation.hasFeature("XMLVersion", "1.1")); + assertTrue(message, domImplementation.hasFeature("XMLVERSION", "1.1")); + assertTrue(message, domImplementation.hasFeature("+XMLVersion", "1.1")); + assertFalse(domImplementation.hasFeature("XMLVersion", "1.2")); + assertFalse(domImplementation.hasFeature("XMLVersion", "2.0")); + assertFalse(domImplementation.hasFeature("XMLVersion", "2.0")); + } + + public void testLsFeature() { + assertTrue("This implementation does not support the LS feature", + domImplementation.hasFeature("LS", "3.0")); + } + + public void testElementTraversalFeature() { + assertTrue("This implementation does not support the ElementTraversal feature", + domImplementation.hasFeature("ElementTraversal", "1.0")); + } + + public void testIsSupported() { + // we don't independently test the features; instead just assume the + // implementation calls through to hasFeature (as tested above) + for (Node node : allNodes) { + assertTrue(node.isSupported("XML", null)); + assertTrue(node.isSupported("XML", "3.0")); + assertFalse(node.isSupported("foo", null)); + assertFalse(node.isSupported("foo", "bar")); + } + } + + public void testGetFeature() { + // we don't independently test the features; instead just assume the + // implementation calls through to hasFeature (as tested above) + for (Node node : allNodes) { + assertSame(node, node.getFeature("XML", null)); + assertSame(node, node.getFeature("XML", "3.0")); + assertNull(node.getFeature("foo", null)); + assertNull(node.getFeature("foo", "bar")); + } + } + + public void testNodeEqualsPositive() throws Exception { + DomTest copy = new DomTest(); + copy.setUp(); + + for (int i = 0; i < allNodes.size(); i++) { + Node a = allNodes.get(i); + Node b = copy.allNodes.get(i); + assertTrue(a.isEqualNode(b)); + } + } + + public void testNodeEqualsNegative() throws Exception { + for (Node a : allNodes) { + for (Node b : allNodes) { + assertEquals(a == b, a.isEqualNode(b)); + } + } + } + + public void testNodeEqualsNegativeRecursive() throws Exception { + DomTest copy = new DomTest(); + copy.setUp(); + copy.vitaminc.setTextContent("55%"); + + // changing anything about a node should break equality for all parents + assertFalse(document.isEqualNode(copy.document)); + assertFalse(menu.isEqualNode(copy.menu)); + assertFalse(item.isEqualNode(copy.item)); + assertFalse(nutrition.isEqualNode(copy.nutrition)); + assertFalse(vitamins.isEqualNode(copy.vitamins)); + assertFalse(vitaminc.isEqualNode(copy.vitaminc)); + + // but not siblings + assertTrue(doctype.isEqualNode(copy.doctype)); + assertTrue(description.isEqualNode(copy.description)); + assertTrue(option1.isEqualNode(copy.option1)); + } + + public void testNodeEqualsNull() { + for (Node node : allNodes) { + try { + node.isEqualNode(null); + fail(); + } catch (NullPointerException e) { + } + } + } + + private String domToString(Document document) + throws TransformerException { + StringWriter writer = new StringWriter(); + transformer.transform(new DOMSource(document), new StreamResult(writer)); + return writer.toString(); + } +} diff --git a/xml/src/test/java/tests/xml/NodeTest.java b/xml/src/test/java/tests/xml/NodeTest.java new file mode 100644 index 0000000..dc3a333 --- /dev/null +++ b/xml/src/test/java/tests/xml/NodeTest.java @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package tests.xml; + +import dalvik.annotation.TestTargetClass; +import junit.framework.TestCase; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; +import tests.support.resource.Support_Resources; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import java.io.ByteArrayInputStream; +import java.io.File; +import java.util.ArrayList; +import java.util.List; + +@TestTargetClass(Node.class) +public class NodeTest extends TestCase { + + /** + * For bug 779: Node#getNextSibling throws IndexOutOfBoundsException. + */ + public void test_getNextSibling() throws Exception { + // Calling getNextSibling when there is no next sibling should return null. + // From http://code.google.com/p/android/issues/detail?id=779. + ByteArrayInputStream bis = new ByteArrayInputStream("<root/>".getBytes()); + Document document = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(bis); + Node root = document.getDocumentElement(); + assertNull(root.getNextSibling()); + } + + public void testGetBaseUri() throws Exception { + DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder(); + File file = Support_Resources.resourceToTempFile("/simple.xml"); + Document document = builder.parse(file); + + String baseUri = "file:" + file.getPath(); + assertEquals(baseUri, document.getBaseURI()); + + Element documentElement = document.getDocumentElement(); + for (Node node : flattenSubtree(documentElement)) { + if (node.getNodeType() == Node.ELEMENT_NODE + || node.getNodeType() == Node.DOCUMENT_NODE) { + assertEquals("Unexpected base URI for " + node, baseUri, node.getBaseURI()); + } else { + assertNull("Unexpected base URI for " + node, node.getBaseURI()); + } + } + + // TODO: test other node types + // TODO: test resolution of relative paths + // TODO: test URI santization + } + + private List<Node> flattenSubtree(Node subtree) { + List<Node> result = new ArrayList<Node>(); + traverse(subtree, result); + return result; + } + + private void traverse(Node node, List<Node> sink) { + sink.add(node); + + NodeList children = node.getChildNodes(); + for (int i = 0; i < children.getLength(); i++) { + traverse(children.item(i), sink); + } + } +} diff --git a/xml/src/test/java/tests/xml/NodeTests.java b/xml/src/test/java/tests/xml/NodeTests.java deleted file mode 100644 index e46e216..0000000 --- a/xml/src/test/java/tests/xml/NodeTests.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (C) 2009 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package tests.xml; - -import dalvik.annotation.TestLevel; -import dalvik.annotation.TestTargetNew; -import dalvik.annotation.TestTargetClass; - -import junit.framework.TestCase; - -import org.w3c.dom.Document; -import org.w3c.dom.Node; - -import java.io.ByteArrayInputStream; -import javax.xml.parsers.DocumentBuilderFactory; - -@TestTargetClass(Node.class) -public class NodeTests extends TestCase { - @TestTargetNew( - level = TestLevel.PARTIAL, - notes = "Issue #779: org.w3c.dom.Node#getNextSibling throws IndexOutOfBoundsException.", - method = "getNextSibling", - args = {} - ) - public void test_getNextSibling() throws Exception { - // Calling getNextSibling when there is no next sibling should return null. - // From http://code.google.com/p/android/issues/detail?id=779. - ByteArrayInputStream bis = new ByteArrayInputStream("<root/>".getBytes()); - Document document = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(bis); - Node root = document.getDocumentElement(); - assertNull(root.getNextSibling()); - } -} |