changeset 519:04a72c5bde80 onclient

Add OnclientTester.java
author Joeri van Ruth <joeri.van.ruth@monetdbsolutions.com>
date Wed, 25 Aug 2021 15:42:22 +0200 (2021-08-25)
parents e84b3cbd8b57
children b4c7816e3592
files tests/OnClientTester.java tests/build.xml
diffstat 2 files changed, 311 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
new file mode 100644
--- /dev/null
+++ b/tests/OnClientTester.java
@@ -0,0 +1,304 @@
+import org.monetdb.jdbc.MonetConnection;
+import org.monetdb.jdbc.MonetUploadHandler;
+
+import java.io.IOException;
+import java.io.PrintStream;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.sql.*;
+
+public final class OnClientTester {
+	private String jdbcUrl;
+	boolean verbose = false;
+	boolean succeeded = true;
+	private MonetConnection conn;
+	private PrintWriter out;
+	private Statement stmt;
+	private StringWriter outBuffer;
+
+	public static void main(String[] args) throws SQLException, NoSuchMethodException {
+		String jdbcUrl;
+		String specificTest = null;
+		switch (args.length) {
+			case 2:
+				specificTest = args[1];
+				/* fallthrough */
+			case 1:
+				jdbcUrl = args[0];
+				break;
+			default:
+				throw new IllegalArgumentException("Usage: OnClientTester JDBC_URL [TESTNAME]");
+		}
+
+		OnClientTester tester = new OnClientTester(jdbcUrl);
+		boolean succeeded;
+		if (specificTest != null)
+			succeeded = tester.runTest(specificTest);
+		else
+			succeeded = tester.runTests();
+
+		if (tester.verbosity >= VERBOSITY_ON || tester.failureCount > 0) {
+			System.out.println();
+			System.out.println("Ran " + tester.testCount + " tests, " + tester.failureCount + " failed");
+		}
+		if (tester.failureCount > 0) {
+			System.exit(1);
+		}
+	}
+
+	public OnClientTester(String jdbcUrl) {
+		this.jdbcUrl = jdbcUrl;
+	}
+
+	private boolean runTests() throws SQLException, NoSuchMethodException {
+		for (Method method: this.getClass().getDeclaredMethods()) {
+			String methodName = method.getName();
+			if (methodName.startsWith("test_") && method.getParameterCount() == 0) {
+				String testName = methodName.substring(5);
+				runTest(testName, method);
+			}
+		}
+
+		return succeeded;
+	}
+
+	private boolean runTest(String testName) throws SQLException, NoSuchMethodException {
+		String methodName = "test_" + testName;
+		Method method = this.getClass().getDeclaredMethod(methodName);
+
+		return runTest(testName, method);
+	}
+
+	private synchronized boolean runTest(String testName, Method method) throws SQLException {
+		outBuffer = new StringWriter();
+		out = new PrintWriter(outBuffer);
+
+		Connection genericConnection = DriverManager.getConnection(jdbcUrl);
+		conn = genericConnection.unwrap(MonetConnection.class);
+		stmt = conn.createStatement();
+
+		System.out.println();
+
+		try {
+			try {
+				method.invoke(this);
+			} catch (InvocationTargetException e) {
+				Throwable cause = e.getCause();
+				if (cause instanceof  Failure)
+					throw (Failure)cause;
+				else if (cause instanceof Exception) {
+					throw (Exception)cause;
+				} else {
+					throw e;
+				}
+			}
+
+			System.out.println("Test " + testName + " succeeded");
+			if (verbose)
+				dumpOutput();
+		} catch (Failure e) {
+			succeeded = false;
+			System.out.println("Test " + testName + " failed");
+			dumpOutput();
+		} catch (Exception e) {
+			succeeded = false;
+			System.out.println("Test " + testName + " failed:");
+			e.printStackTrace();
+			System.out.println();
+			dumpOutput();
+			// Show the inner bits of the exception again, they may have scrolled off screen
+			Throwable t = e;
+			while (t.getCause() != null) {
+				t = t.getCause();
+			}
+			System.out.println();
+			System.out.println("Innermost cause was " + t);
+			if (t.getStackTrace().length > 0) {
+				System.out.println("                 at " + t.getStackTrace()[0]);
+			}
+		} finally {
+			stmt.close();
+			conn.close();
+		}
+
+		return succeeded;
+	}
+
+	private void dumpOutput() {
+		String output = outBuffer.getBuffer().toString();
+		if (output.isEmpty()) {
+			System.out.println("(Test did not produce any output)");
+		} else {
+			System.out.println("------ Accumulated output:");
+			boolean terminated = output.endsWith(System.lineSeparator());
+			if (terminated) {
+				System.out.print(output);
+			} else {
+				System.out.println(output);
+			}
+			System.out.println("------ End of accumulated output" + (terminated ? "" : " (no trailing newline)"));
+		}
+	}
+
+	private void fail(String message) throws Failure {
+		out.println("FAILURE: " + message);
+		throw new Failure(message);
+	}
+
+	protected boolean execute(String query) throws SQLException {
+		out.println("EXEC " + query);
+		boolean result;
+		result = stmt.execute(query);
+		if (result) {
+			out.println("  OK");
+		} else {
+			out.println("  OK, updated " + stmt.getUpdateCount() + " rows");
+		}
+		return result;
+	}
+
+	protected void update(String query, int expectedUpdateCount) throws SQLException, Failure {
+		execute(query);
+		int updateCount = stmt.getUpdateCount();
+		if (updateCount != expectedUpdateCount) {
+			fail("Query updated " + updateCount + "rows, expected " + expectedUpdateCount);
+		}
+	}
+
+	protected void expectError(String query, String expectedError) throws SQLException, Failure {
+		try {
+			execute(query);
+		} catch (SQLException e) {
+			if (e.getMessage().contains(expectedError)) {
+				out.println("  GOT EXPECTED EXCEPTION: " + e.getMessage());
+			} else {
+				throw e;
+			}
+		}
+	}
+
+	protected void queryInt(String query, int expected) throws SQLException, Failure {
+		if (execute(query) == false) {
+			fail("Query does not return a result set");
+		}
+		ResultSet rs = stmt.getResultSet();
+		ResultSetMetaData metaData = rs.getMetaData();
+		if (metaData.getColumnCount() != 1) {
+			fail("Result should have exactly one column");
+		}
+		if (!rs.next()) {
+			fail("Result set is empty");
+		}
+		int result = rs.getInt(1);
+		if (rs.next()) {
+			String message = "Result set has more than one row";
+			fail(message);
+		}
+		rs.close();
+		if (result != expected)
+			fail("Query returned " + result + ", expected " + expected);
+	}
+
+	protected void prepare() throws SQLException {
+		execute("DROP TABLE IF EXISTS foo");
+		execute("CREATE TABLE foo (i INT, t TEXT)");
+	}
+
+	static class MyUploadHandler implements MonetUploadHandler {
+
+		private final int rows;
+		private final int errorAt;
+		private final String errorMessage;
+		MyUploadHandler(int rows, int errorAt, String errorMessage) {
+			this.rows = rows;
+			this.errorAt = errorAt;
+			this.errorMessage = errorMessage;
+		}
+
+		MyUploadHandler(int rows) {
+			this(rows, -1, null);
+		}
+
+		MyUploadHandler(String errorMessage) {
+			this(0, -1, errorMessage);
+		}
+
+
+		@Override
+		public void handleUpload(MonetConnection.Upload handle, String name, boolean textMode, int offset) throws IOException {
+			int toSkip = offset > 0 ? offset - 1 : 0;
+			if (errorAt == -1 && errorMessage != null) {
+				handle.sendError(errorMessage);
+				return;
+			}
+			PrintStream stream = handle.getStream();
+			for (int i = toSkip; i < rows; i++) {
+				if (i == errorAt) {
+					throw new IOException(errorMessage);
+				}
+				stream.printf("%d|%d%n", i + 1, i + 1);
+			}
+		}
+
+	}
+	static class Failure extends Exception {
+
+		public Failure(String message) {
+			super(message);
+		}
+		public Failure(String message, Throwable cause) {
+			super(message, cause);
+		}
+
+	}
+
+	private void test_Upload() throws Exception {
+		prepare();
+		conn.setUploadHandler(new MyUploadHandler(100));
+		update("COPY INTO foo FROM 'banana' ON CLIENT", 100);
+		queryInt("SELECT COUNT(*) FROM foo", 100);
+	}
+
+	private void test_ImmediateError() throws Exception {
+		prepare();
+		conn.setUploadHandler(new MyUploadHandler("immediate error"));
+		expectError("COPY INTO foo FROM 'banana' ON CLIENT", "immediate error");
+		queryInt("SELECT COUNT(*) FROM foo", 0);
+	}
+
+	private void test_Offset0() throws SQLException, Failure {
+		prepare();
+		conn.setUploadHandler(new MyUploadHandler(100));
+		update("COPY OFFSET 0 INTO foo FROM 'banana' ON CLIENT", 100);
+		queryInt("SELECT MIN(i) FROM foo", 1);
+		queryInt("SELECT MAX(i) FROM foo", 100);
+	}
+
+	private void test_Offset1() throws SQLException, Failure {
+		prepare();
+		conn.setUploadHandler(new MyUploadHandler(100));
+		update("COPY OFFSET 1 INTO foo FROM 'banana' ON CLIENT", 100);
+		queryInt("SELECT MIN(i) FROM foo", 1);
+		queryInt("SELECT MAX(i) FROM foo", 100);
+	}
+
+	private void test_Offset5() throws SQLException, Failure {
+		prepare();
+		conn.setUploadHandler(new MyUploadHandler(100));
+		update("COPY OFFSET 5 INTO foo FROM 'banana' ON CLIENT", 96);
+		queryInt("SELECT MIN(i) FROM foo", 5);
+		queryInt("SELECT MAX(i) FROM foo", 100);
+	}
+
+	private void test_ServerCancels() throws SQLException, Failure {
+		prepare();
+		conn.setUploadHandler(new MyUploadHandler(100));
+		update("COPY 10 RECORDS INTO foo FROM 'banana' ON CLIENT", 96);
+		queryInt("SELECT MIN(i) FROM foo", 5);
+		queryInt("SELECT MAX(i) FROM foo", 100);
+	}
+
+
+}
\ No newline at end of file
--- a/tests/build.xml
+++ b/tests/build.xml
@@ -72,6 +72,7 @@ Copyright 1997 - July 2008 CWI, August 2
     <jar jarfile="${jdbctests-jar}">
       <fileset dir="${builddir}">
         <include name="JDBC_API_Tester.class" />
+        <include name="OnClientTester.class" />
       </fileset>
     </jar>
   </target>
@@ -122,4 +123,10 @@ Copyright 1997 - July 2008 CWI, August 2
     </antcall>
   </target>
 
+  <target name="OnClientTester">
+    <antcall target="test_class">
+      <param name="test.class" value="OnClientTester" />
+    </antcall>
+  </target>
+
 </project>