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

Simplified test cases using assertThrows(...).

上级 c5c09177
......@@ -18,7 +18,13 @@ Change Log
<h1>Change Log</h1>
<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.
</li><li>Shell tool: the built-in command "distinct" has been removed
(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>.
(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>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>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.
......@@ -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>TRANSACTION_ID() for in-memory databases.
</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 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>Sybase / MS SQL Server compatiblitiy: CONVERT(..) parameters are swapped.
</li></ul>
<h2>Not Planned</h2>
......
......@@ -69,7 +69,7 @@ public class SourceCompiler {
* @param packageAndClassName the class name
* @return the class
*/
private Class<?> getClass(String packageAndClassName) throws ClassNotFoundException {
public Class<?> getClass(String packageAndClassName) throws ClassNotFoundException {
Class<?> compiledClass = compiled.get(packageAndClassName);
if (compiledClass != null) {
......@@ -150,22 +150,26 @@ public class SourceCompiler {
OutputStream f = IOUtils.openFileOutputStream(javaFile.getAbsolutePath(), false);
PrintWriter out = new PrintWriter(IOUtils.getBufferedWriter(f));
classFile.delete();
int endImport = source.indexOf("@CODE");
String importCode = "import java.util.*;\n" +
"import java.math.*;\n" +
"import java.sql.*;\n";
if (endImport >= 0) {
importCode = source.substring(0, endImport);
source = source.substring("@CODE".length() + endImport);
}
if (packageName != null) {
out.println("package " + packageName + ";");
if (source.startsWith("package ")) {
out.println(source);
} else {
int endImport = source.indexOf("@CODE");
String importCode = "import java.util.*;\n" +
"import java.math.*;\n" +
"import java.sql.*;\n";
if (endImport >= 0) {
importCode = source.substring(0, endImport);
source = source.substring("@CODE".length() + endImport);
}
if (packageName != null) {
out.println("package " + packageName + ";");
}
out.println(importCode);
out.println("public class "+ className +" {\n" +
" public static " +
source + "\n" +
"}\n");
}
out.println(importCode);
out.println("public class "+ className +" {\n" +
" public static " +
source + "\n" +
"}\n");
out.close();
if (JAVAC_SUN != null) {
javacSun(javaFile);
......@@ -209,7 +213,9 @@ public class SourceCompiler {
private void throwSyntaxError(ByteArrayOutputStream out) {
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, "");
throw DbException.get(ErrorCode.SYNTAX_ERROR_1, err);
}
......
......@@ -12,6 +12,7 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.io.Reader;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
......@@ -30,6 +31,7 @@ import org.h2.jdbc.JdbcConnection;
import org.h2.message.TraceSystem;
import org.h2.store.FileLock;
import org.h2.store.fs.FileSystemSplit;
import org.h2.test.utils.ProxyCodeGenerator;
import org.h2.test.utils.RecordingFileSystem;
import org.h2.tools.DeleteDbFiles;
import org.h2.util.IOUtils;
......@@ -1302,11 +1304,9 @@ public abstract class TestBase {
}
@SuppressWarnings("unchecked")
protected
<T> T assertThrows(final Thread.UncaughtExceptionHandler handler, final String expected, final T obj) {
protected <T> T assertThrows(final Thread.UncaughtExceptionHandler handler, final String expected, final T obj) {
Class<?> c = obj.getClass();
return (T) Proxy.newProxyInstance(c.getClassLoader(),
c.getInterfaces(), new InvocationHandler() {
InvocationHandler ih = new InvocationHandler() {
private Exception called = new Exception("No method called");
public void finalize() {
if (called != null) {
......@@ -1347,7 +1347,19 @@ public abstract class TestBase {
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 {
}
deleteDb("openClose");
Connection conn;
conn = DriverManager.getConnection(
"jdbc:h2:" + getBaseDir() + "/openClose;FILE_LOCK=FS");
try {
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 = getConnection("jdbc:h2:" + getBaseDir() + "/openClose;FILE_LOCK=FS");
assertThrows(ErrorCode.DATABASE_ALREADY_OPEN_1, (TestBase) this).
getConnection("jdbc:h2:" + getBaseDir() + "/openClose;FILE_LOCK=FS;OPEN_NEW=TRUE");
conn.close();
}
......@@ -78,12 +72,8 @@ public class TestOpenClose extends TestBase implements DatabaseEventListener {
FileObject f = FileSystem.getInstance(getBaseDir()).openFileObject(getBaseDir() + "/openClose2.h2.db.1.part", "rw");
f.setFileLength(f.length() * 2);
f.close();
try {
DriverManager.getConnection("jdbc:h2:split:18:" + getBaseDir() + "/openClose2");
fail();
} catch (SQLException e) {
assertEquals(ErrorCode.IO_EXCEPTION_2, e.getErrorCode());
}
assertThrows(ErrorCode.IO_EXCEPTION_2, (TestBase) this).
getConnection("jdbc:h2:split:18:" + getBaseDir() + "/openClose2");
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 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论