提交 bf0e5b35 authored 作者: Thomas Mueller's avatar Thomas Mueller

Simplified test cases using assertThrows(...).

上级 c5c09177
...@@ -18,7 +18,13 @@ Change Log ...@@ -18,7 +18,13 @@ Change Log
<h1>Change Log</h1> <h1>Change Log</h1>
<h2>Next Version (unreleased)</h2> <h2>Next Version (unreleased)</h2>
<ul><li>String.toUpperCase() was used a few places, which is problematic when using the Turkish locale. <ul><li>Simplified test cases using assertThrows(...).
</li><li>A Java function that returns a result set that was generated using the provided connection
could cause an internal error in some cases. Now such result sets are copied early on.
</li><li>Data type RESULT_SET: information_schema.function_aliases returned type 0; now type -10 is returned (the same as Oracle: OracleTypes.CURSOR = -10).
</li><li>Data type RESULT_SET: tables with columns of type RESULT_SET could be created, but no data could be persisted in this column.
</li><li>H2 Console: improved system tray icon for Mac OS X (transparent background).
</li><li>String.toUpperCase() was used a few places, which is problematic when using the Turkish locale.
The method has been replaced with toUpperCase(Locale.ENGLISH) to solve such problems. The method has been replaced with toUpperCase(Locale.ENGLISH) to solve such problems.
</li><li>Shell tool: the built-in command "distinct" has been removed </li><li>Shell tool: the built-in command "distinct" has been removed
(use the SQL statements "show tables" / "show columns from tableName" instead). (use the SQL statements "show tables" / "show columns from tableName" instead).
......
...@@ -531,6 +531,7 @@ See also <a href="build.html#providing_patches">Providing Patches</a>. ...@@ -531,6 +531,7 @@ See also <a href="build.html#providing_patches">Providing Patches</a>.
(compatibility with MySQL, PostgreSQL, HSQLDB; not Derby). (compatibility with MySQL, PostgreSQL, HSQLDB; not Derby).
</li><li>ARRAY data type: support Integer[] and so on in Java functions (currently only Object[] is supported). </li><li>ARRAY data type: support Integer[] and so on in Java functions (currently only Object[] is supported).
</li><li>MySQL compatibility: LOCK TABLES a READ, b READ - see also http://dev.mysql.com/doc/refman/5.0/en/lock-tables.html </li><li>MySQL compatibility: LOCK TABLES a READ, b READ - see also http://dev.mysql.com/doc/refman/5.0/en/lock-tables.html
</li><li>Oracle compatibility: convert empty strings to null.
</li><li>The HTML to PDF converter should use http://code.google.com/p/wkhtmltopdf/ </li><li>The HTML to PDF converter should use http://code.google.com/p/wkhtmltopdf/
</li><li>Issue 303: automatically convert "X NOT IN(SELECT...)" to "NOT EXISTS(...)". </li><li>Issue 303: automatically convert "X NOT IN(SELECT...)" to "NOT EXISTS(...)".
</li><li>MySQL compatibility: update test1 t1, test2 t2 set t1.name=t2.name where t1.id=t2.id. </li><li>MySQL compatibility: update test1 t1, test2 t2 set t1.name=t2.name where t1.id=t2.id.
...@@ -548,10 +549,10 @@ See also <a href="build.html#providing_patches">Providing Patches</a>. ...@@ -548,10 +549,10 @@ See also <a href="build.html#providing_patches">Providing Patches</a>.
</li><li>Support SELECT ... FOR UPDATE OF [table-list] (supported by PostgreSQL, HSQLDB, Sybase). </li><li>Support SELECT ... FOR UPDATE OF [table-list] (supported by PostgreSQL, HSQLDB, Sybase).
</li><li>TRANSACTION_ID() for in-memory databases. </li><li>TRANSACTION_ID() for in-memory databases.
</li><li>TRANSACTION_ID() should be long (same as HSQLDB and PostgreSQL). </li><li>TRANSACTION_ID() should be long (same as HSQLDB and PostgreSQL).
</li><li>Oracle compatibility: convert empty strings to null.
</li><li>Support [INNER | OUTER] JOIN USING(column [,...]). </li><li>Support [INNER | OUTER] JOIN USING(column [,...]).
</li><li>Support NATURAL [ { LEFT | RIGHT } [ OUTER ] | INNER ] JOIN (Derby, Oracle) </li><li>Support NATURAL [ { LEFT | RIGHT } [ OUTER ] | INNER ] JOIN (Derby, Oracle)
</li><li>GROUP BY columnNumber (similar to ORDER BY columnNumber) (MySQL, PostgreSQL, SQLite; not by HSQLDB and Derby). </li><li>GROUP BY columnNumber (similar to ORDER BY columnNumber) (MySQL, PostgreSQL, SQLite; not by HSQLDB and Derby).
</li><li>Sybase / MS SQL Server compatiblitiy: CONVERT(..) parameters are swapped.
</li></ul> </li></ul>
<h2>Not Planned</h2> <h2>Not Planned</h2>
......
...@@ -69,7 +69,7 @@ public class SourceCompiler { ...@@ -69,7 +69,7 @@ public class SourceCompiler {
* @param packageAndClassName the class name * @param packageAndClassName the class name
* @return the class * @return the class
*/ */
private Class<?> getClass(String packageAndClassName) throws ClassNotFoundException { public Class<?> getClass(String packageAndClassName) throws ClassNotFoundException {
Class<?> compiledClass = compiled.get(packageAndClassName); Class<?> compiledClass = compiled.get(packageAndClassName);
if (compiledClass != null) { if (compiledClass != null) {
...@@ -150,6 +150,9 @@ public class SourceCompiler { ...@@ -150,6 +150,9 @@ public class SourceCompiler {
OutputStream f = IOUtils.openFileOutputStream(javaFile.getAbsolutePath(), false); OutputStream f = IOUtils.openFileOutputStream(javaFile.getAbsolutePath(), false);
PrintWriter out = new PrintWriter(IOUtils.getBufferedWriter(f)); PrintWriter out = new PrintWriter(IOUtils.getBufferedWriter(f));
classFile.delete(); classFile.delete();
if (source.startsWith("package ")) {
out.println(source);
} else {
int endImport = source.indexOf("@CODE"); int endImport = source.indexOf("@CODE");
String importCode = "import java.util.*;\n" + String importCode = "import java.util.*;\n" +
"import java.math.*;\n" + "import java.math.*;\n" +
...@@ -166,6 +169,7 @@ public class SourceCompiler { ...@@ -166,6 +169,7 @@ public class SourceCompiler {
" public static " + " public static " +
source + "\n" + source + "\n" +
"}\n"); "}\n");
}
out.close(); out.close();
if (JAVAC_SUN != null) { if (JAVAC_SUN != null) {
javacSun(javaFile); javacSun(javaFile);
...@@ -209,7 +213,9 @@ public class SourceCompiler { ...@@ -209,7 +213,9 @@ public class SourceCompiler {
private void throwSyntaxError(ByteArrayOutputStream out) { private void throwSyntaxError(ByteArrayOutputStream out) {
String err = StringUtils.utf8Decode(out.toByteArray()); String err = StringUtils.utf8Decode(out.toByteArray());
if (err.length() > 0) { if (err.startsWith("Note:")) {
// unchecked or unsafe operations - just a warning
} else if (err.length() > 0) {
err = StringUtils.replaceAll(err, compileDir, ""); err = StringUtils.replaceAll(err, compileDir, "");
throw DbException.get(ErrorCode.SYNTAX_ERROR_1, err); throw DbException.get(ErrorCode.SYNTAX_ERROR_1, err);
} }
......
...@@ -12,6 +12,7 @@ import java.io.IOException; ...@@ -12,6 +12,7 @@ import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.PrintWriter; import java.io.PrintWriter;
import java.io.Reader; import java.io.Reader;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler; import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method; import java.lang.reflect.Method;
...@@ -30,6 +31,7 @@ import org.h2.jdbc.JdbcConnection; ...@@ -30,6 +31,7 @@ import org.h2.jdbc.JdbcConnection;
import org.h2.message.TraceSystem; import org.h2.message.TraceSystem;
import org.h2.store.FileLock; import org.h2.store.FileLock;
import org.h2.store.fs.FileSystemSplit; import org.h2.store.fs.FileSystemSplit;
import org.h2.test.utils.ProxyCodeGenerator;
import org.h2.test.utils.RecordingFileSystem; import org.h2.test.utils.RecordingFileSystem;
import org.h2.tools.DeleteDbFiles; import org.h2.tools.DeleteDbFiles;
import org.h2.util.IOUtils; import org.h2.util.IOUtils;
...@@ -1302,11 +1304,9 @@ public abstract class TestBase { ...@@ -1302,11 +1304,9 @@ public abstract class TestBase {
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
protected protected <T> T assertThrows(final Thread.UncaughtExceptionHandler handler, final String expected, final T obj) {
<T> T assertThrows(final Thread.UncaughtExceptionHandler handler, final String expected, final T obj) {
Class<?> c = obj.getClass(); Class<?> c = obj.getClass();
return (T) Proxy.newProxyInstance(c.getClassLoader(), InvocationHandler ih = new InvocationHandler() {
c.getInterfaces(), new InvocationHandler() {
private Exception called = new Exception("No method called"); private Exception called = new Exception("No method called");
public void finalize() { public void finalize() {
if (called != null) { if (called != null) {
...@@ -1347,7 +1347,19 @@ public abstract class TestBase { ...@@ -1347,7 +1347,19 @@ public abstract class TestBase {
return null; return null;
} }
} }
}); };
if (obj == this) {
// class proxies
try {
Class<?> pc = ProxyCodeGenerator.getClassProxy(TestBase.class);
Constructor cons = pc.getConstructor(new Class<?>[] { InvocationHandler.class });
return (T) cons.newInstance(new Object[] { ih });
} catch (Exception e) {
throw new RuntimeException(e);
}
}
return (T) Proxy.newProxyInstance(c.getClassLoader(),
c.getInterfaces(), ih);
} }
} }
...@@ -54,15 +54,9 @@ public class TestOpenClose extends TestBase implements DatabaseEventListener { ...@@ -54,15 +54,9 @@ public class TestOpenClose extends TestBase implements DatabaseEventListener {
} }
deleteDb("openClose"); deleteDb("openClose");
Connection conn; Connection conn;
conn = DriverManager.getConnection( conn = getConnection("jdbc:h2:" + getBaseDir() + "/openClose;FILE_LOCK=FS");
"jdbc:h2:" + getBaseDir() + "/openClose;FILE_LOCK=FS"); assertThrows(ErrorCode.DATABASE_ALREADY_OPEN_1, (TestBase) this).
try { getConnection("jdbc:h2:" + getBaseDir() + "/openClose;FILE_LOCK=FS;OPEN_NEW=TRUE");
DriverManager.getConnection(
"jdbc:h2:" + getBaseDir() + "/openClose;FILE_LOCK=FS;OPEN_NEW=TRUE");
fail();
} catch (SQLException e) {
assertEquals(ErrorCode.DATABASE_ALREADY_OPEN_1, e.getErrorCode());
}
conn.close(); conn.close();
} }
...@@ -78,12 +72,8 @@ public class TestOpenClose extends TestBase implements DatabaseEventListener { ...@@ -78,12 +72,8 @@ public class TestOpenClose extends TestBase implements DatabaseEventListener {
FileObject f = FileSystem.getInstance(getBaseDir()).openFileObject(getBaseDir() + "/openClose2.h2.db.1.part", "rw"); FileObject f = FileSystem.getInstance(getBaseDir()).openFileObject(getBaseDir() + "/openClose2.h2.db.1.part", "rw");
f.setFileLength(f.length() * 2); f.setFileLength(f.length() * 2);
f.close(); f.close();
try { assertThrows(ErrorCode.IO_EXCEPTION_2, (TestBase) this).
DriverManager.getConnection("jdbc:h2:split:18:" + getBaseDir() + "/openClose2"); getConnection("jdbc:h2:split:18:" + getBaseDir() + "/openClose2");
fail();
} catch (SQLException e) {
assertEquals(ErrorCode.IO_EXCEPTION_2, e.getErrorCode());
}
FileSystem.getInstance("split:").delete("split:" + getBaseDir() + "/openClose2.h2.db"); FileSystem.getInstance("split:").delete("split:" + getBaseDir() + "/openClose2.h2.db");
} }
......
/*
* Copyright 2004-2011 H2 Group. Multiple-Licensed under the H2 License,
* Version 1.0, and under the Eclipse Public License, Version 1.0
* (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.test.utils;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.TreeSet;
import org.h2.test.TestBase;
import org.h2.util.New;
import org.h2.util.SourceCompiler;
/**
* A code generator for class proxies.
*/
public class ProxyCodeGenerator {
static SourceCompiler compiler = new SourceCompiler();
static HashMap<Class<?>, Class<?>> proxyMap = New.hashMap();
private TreeSet<String> imports = new TreeSet<String>();
private ArrayList<Method> methods = new ArrayList<Method>();
private String packageName;
private String className;
private Class<?> extendsClass;
public static Class<?> getClassProxy(Class<?> c) throws ClassNotFoundException {
Class<?> p = proxyMap.get(c);
if (p != null) {
return p;
}
ProxyCodeGenerator cg = new ProxyCodeGenerator();
cg.setPackageName("bytecode");
cg.generateClassProxy(TestBase.class);
StringWriter sw = new StringWriter();
cg.write(new PrintWriter(sw));
String code = sw.toString();
String proxy = "bytecode."+ c.getSimpleName() + "Proxy";
compiler.setSource(proxy, code);
Class<?> px = compiler.getClass(proxy);
proxyMap.put(c, px);
return px;
}
void setPackageName(String packageName) {
this.packageName = packageName;
}
void generateStaticProxy(Class<?> clazz) {
imports.clear();
addImport(InvocationHandler.class);
addImport(Method.class);
addImport(clazz);
className = getClassName(clazz) + "Proxy";
for (Method m : clazz.getDeclaredMethods()) {
if (Modifier.isStatic(m.getModifiers())) {
if (!Modifier.isPrivate(m.getModifiers())) {
addMethod(m);
}
}
}
}
void generateClassProxy(Class<?> clazz) {
imports.clear();
addImport(InvocationHandler.class);
addImport(Method.class);
addImport(clazz);
className = getClassName(clazz) + "Proxy";
extendsClass = clazz;
for (Method m : clazz.getDeclaredMethods()) {
if (!Modifier.isStatic(m.getModifiers())) {
if (!Modifier.isPrivate(m.getModifiers())) {
addMethod(m);
}
}
}
}
void addMethod(Method m) {
addImport(m.getReturnType());
for (Class<?> c : m.getParameterTypes()) {
addImport(c);
}
for (Class<?> c : m.getExceptionTypes()) {
addImport(c);
}
methods.add(m);
}
void addImport(Class<?> c) {
while (c.isArray()) {
c = c.getComponentType();
}
if (!c.isPrimitive()) {
if (!"java.lang".equals(c.getPackage().getName())) {
imports.add(c.getName());
}
}
}
private static String getClassName(Class<?> c) {
String s = c.getSimpleName();
while (true) {
c = c.getEnclosingClass();
if (c == null) {
break;
}
s = c.getSimpleName() + "." + s;
}
return s;
}
void write(PrintWriter writer) {
if (packageName != null) {
writer.println("package " + packageName + ";");
}
for (String imp : imports) {
writer.println("import " + imp + ";");
}
writer.print("public class " + className);
if (extendsClass != null) {
writer.print(" extends " + getClassName(extendsClass));
}
writer.println(" {");
writer.println(" private final InvocationHandler ih;");
writer.println(" public " + className + "() {");
writer.println(" this(new InvocationHandler() {");
writer.println(" public Object invoke(Object proxy,");
writer.println(" Method method, Object[] args) throws Throwable {");
writer.println(" return method.invoke(proxy, args);");
writer.println(" }});");
writer.println(" }");
writer.println(" public " + className + "(InvocationHandler ih) {");
writer.println(" this.ih = ih;");
writer.println(" }");
writer.println(" @SuppressWarnings(\"unchecked\")");
writer.println(" private static <T extends RuntimeException> T convertException(Throwable e) {");
writer.println(" return (T) e;");
writer.println(" }");
for (Method m : methods) {
Class<?> retClass = m.getReturnType();
writer.print(" public " + getClassName(retClass) +
" " + m.getName() + "(");
int i = 0;
for (Class<?> p : m.getParameterTypes()) {
if (i > 0) {
writer.print(", ");
}
writer.print(getClassName(p) + " p" + i);
i++;
}
writer.print(")");
Class<?>[] ec = m.getExceptionTypes();
if (ec.length > 0) {
writer.print(" throws ");
int ic = 0;
for (Class<?> e : ec) {
if (ic > 0) {
writer.print(", ");
}
writer.print(getClassName(e));
ic++;
}
}
writer.println(" {");
writer.println(" try {");
writer.print(" ");
if (retClass != void.class) {
writer.print("return (");
if (retClass == boolean.class) {
writer.print("Boolean");
} else if (retClass == byte.class) {
writer.print("Byte");
} else if (retClass == char.class) {
writer.print("Char");
} else if (retClass == short.class) {
writer.print("Short");
} else if (retClass == int.class) {
writer.print("Integer");
} else if (retClass == long.class) {
writer.print("Long");
} else if (retClass == float.class) {
writer.print("Float");
} else if (retClass == double.class) {
writer.print("Double");
} else {
writer.print(getClassName(retClass));
}
writer.print(") ");
}
writer.print("ih.invoke(this, ");
writer.println(getClassName(m.getDeclaringClass()) +
".class.getMethod(\"" + m.getName() +
"\",");
writer.print(" new Class[] {");
i = 0;
for (Class<?> p : m.getParameterTypes()) {
if (i > 0) {
writer.print(", ");
}
writer.print(getClassName(p) + ".class");
i++;
}
writer.println("}),");
writer.print(" new Object[] {");
for (i = 0; i < m.getParameterTypes().length; i++) {
if (i > 0) {
writer.print(", ");
}
writer.print("p" + i);
}
writer.println("});");
writer.println(" } catch (Throwable e) {");
writer.println(" throw convertException(e);");
writer.println(" }");
writer.println(" }");
}
writer.println("}");
writer.flush();
}
}
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论