view tests/OnClientTester.java @ 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
children b4c7816e3592
line wrap: on
line source
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);
	}


}