Mercurial > hg > monetdb-java
changeset 574:3370027aeb7f onclient
Test more systematically that the connection is still alive after file transfer
Some MonetDB versions have a bug where the connection is closed by the server
when the client refuses to download.
The tests look at the the server version and skip the
lifeness check if the server is too old.
author | Joeri van Ruth <joeri.van.ruth@monetdbsolutions.com> |
---|---|
date | Wed, 06 Oct 2021 11:34:18 +0200 (2021-10-06) |
parents | 0fbf1cbde56c |
children | 08c9918177b2 |
files | tests/OnClientTester.java tests/TestRunner.java |
diffstat | 2 files changed, 147 insertions(+), 23 deletions(-) [+] |
line wrap: on
line diff
--- a/tests/OnClientTester.java +++ b/tests/OnClientTester.java @@ -59,27 +59,117 @@ public final class OnClientTester extend System.exit(1); } + /// Some tests have to work limitations of the protocol or bugs in the server. + /// This Enum is used to indicate the possibilities. + public enum BugFixLevel { + /// Only those tests that work with older MonetDB versions + Baseline(0, 0, 0), + + /// Connection keeps working after download request has been refused by client + CanRefuseDownload(11, 41, 12), + + ; + + private final int major; + private final int minor; + private final int micro; + + BugFixLevel(int major, int minor, int micro) { + this.major = major; + this.minor = minor; + this.micro = micro; + } + + boolean includesVersion(int major, int minor, int micro) { + if (major > this.major) + return true; + if (major < this.major) + return false; + if (minor > this.minor) + return true; + if (minor < this.minor) + return false; + return micro >= this.micro; + } + + static BugFixLevel forVersion(String version) { + String[] parts = version.split("[.]", 3); + assert parts.length == 3; + int major = Integer.parseInt(parts[0]); + int minor = Integer.parseInt(parts[1]); + int micro = Integer.parseInt(parts[2]); + + return BugFixLevel.forVersion(major, minor, micro); + } + + static BugFixLevel forVersion(int major, int minor, int micro) { + BugFixLevel lastValid = Baseline; + for (BugFixLevel level: BugFixLevel.values()) { + if (level.includesVersion(major, minor, micro)) + lastValid = level; + else + break; + } + return lastValid; + } + } + void prepare() throws SQLException { execute("DROP TABLE IF EXISTS foo"); execute("CREATE TABLE foo (i INT, t TEXT)"); } - public void test_Upload() throws Exception { + private BugFixLevel getLevel() throws SQLException, Failure { + String version = queryString("SELECT value FROM environment WHERE name = 'monet_version'"); + BugFixLevel level = BugFixLevel.forVersion(version); + out.println(" NOTE: version " + version + " means level = " + level); + return level; + } + + public void test_BugFixLevel() throws Failure { + assertEq("Baseline includes 0.0.0", true, BugFixLevel.Baseline.includesVersion(0, 0, 0)); + assertEq("Baseline includes 11.41.11", true, BugFixLevel.Baseline.includesVersion(11, 41, 11)); + assertEq("Baseline includes 11.41.12", true, BugFixLevel.Baseline.includesVersion(11, 41, 12)); + + assertEq("CanRefuseDownload includes 0.0.0", false, BugFixLevel.CanRefuseDownload.includesVersion(0, 0, 0)); + + assertEq("CanRefuseDownload includes 11.0.0", false, BugFixLevel.CanRefuseDownload.includesVersion(11, 0, 0)); + assertEq("CanRefuseDownload includes 12.0.0", true, BugFixLevel.CanRefuseDownload.includesVersion(12, 0, 0)); + + assertEq("CanRefuseDownload includes 11.41.0", false, BugFixLevel.CanRefuseDownload.includesVersion(11, 41, 0)); + assertEq("CanRefuseDownload includes 11.42.0", true, BugFixLevel.CanRefuseDownload.includesVersion(11, 42, 0)); + + assertEq("CanRefuseDownload includes 11.41.11", false, BugFixLevel.CanRefuseDownload.includesVersion(11, 41, 11)); + assertEq("CanRefuseDownload includes 11.41.12", true, BugFixLevel.CanRefuseDownload.includesVersion(11, 41, 12)); + + assertEq("Level for 0.0.0", BugFixLevel.Baseline, BugFixLevel.forVersion(0, 0, 0)); + assertEq("Level for 11.0.0", BugFixLevel.Baseline, BugFixLevel.forVersion(11, 0, 0)); + assertEq("Level for 11.41.0", BugFixLevel.Baseline, BugFixLevel.forVersion(11, 41, 0)); + assertEq("Level for 11.41.11", BugFixLevel.Baseline, BugFixLevel.forVersion(11, 41, 11)); + assertEq("Level for 11.41.12", BugFixLevel.CanRefuseDownload, BugFixLevel.forVersion(11, 41, 12)); + assertEq("Level for 11.42.0", BugFixLevel.CanRefuseDownload, BugFixLevel.forVersion(11, 42, 0)); + assertEq("Level for 12.0.0", BugFixLevel.CanRefuseDownload, BugFixLevel.forVersion(12, 0, 0)); + + assertEq("Level for \"11.41.11\"", BugFixLevel.Baseline, BugFixLevel.forVersion("11.41.11")); + assertEq("Level for \"11.41.12\"", BugFixLevel.CanRefuseDownload, BugFixLevel.forVersion("11.41.12")); + } + + public void test_Upload() throws SQLException, Failure { prepare(); MyUploadHandler handler = new MyUploadHandler(100); conn.setUploadHandler(handler); update("COPY INTO foo FROM 'banana' ON CLIENT", 100); assertEq("cancellation callback called", false, handler.isCancelled()); - queryInt("SELECT COUNT(*) FROM foo", 100); + assertQueryInt("SELECT COUNT(*) FROM foo", 100); } - public void test_ClientRefusesUpload() throws Exception { + public void test_ClientRefusesUpload() throws SQLException, Failure { prepare(); MyUploadHandler handler = new MyUploadHandler("immediate error"); conn.setUploadHandler(handler); expectError("COPY INTO foo FROM 'banana' ON CLIENT", "immediate error"); assertEq("cancellation callback called", false, handler.isCancelled()); - queryInt("SELECT COUNT(*) FROM foo", 0); + assertQueryInt("SELECT COUNT(*) FROM foo", 0); } public void test_Offset0() throws SQLException, Failure { @@ -88,8 +178,8 @@ public final class OnClientTester extend conn.setUploadHandler(handler); update("COPY OFFSET 0 INTO foo FROM 'banana' ON CLIENT", 100); assertEq("cancellation callback called", false, handler.isCancelled()); - queryInt("SELECT MIN(i) FROM foo", 1); - queryInt("SELECT MAX(i) FROM foo", 100); + assertQueryInt("SELECT MIN(i) FROM foo", 1); + assertQueryInt("SELECT MAX(i) FROM foo", 100); } public void test_Offset1() throws SQLException, Failure { @@ -98,8 +188,8 @@ public final class OnClientTester extend conn.setUploadHandler(handler); update("COPY OFFSET 1 INTO foo FROM 'banana' ON CLIENT", 100); assertEq("cancellation callback called", false, handler.isCancelled()); - queryInt("SELECT MIN(i) FROM foo", 1); - queryInt("SELECT MAX(i) FROM foo", 100); + assertQueryInt("SELECT MIN(i) FROM foo", 1); + assertQueryInt("SELECT MAX(i) FROM foo", 100); } public void test_Offset5() throws SQLException, Failure { @@ -108,8 +198,8 @@ public final class OnClientTester extend conn.setUploadHandler(handler); update("COPY OFFSET 5 INTO foo FROM 'banana' ON CLIENT", 96); assertEq("cancellation callback called", false, handler.isCancelled()); - queryInt("SELECT MIN(i) FROM foo", 5); - queryInt("SELECT MAX(i) FROM foo", 100); + assertQueryInt("SELECT MIN(i) FROM foo", 5); + assertQueryInt("SELECT MAX(i) FROM foo", 100); } public void test_ServerStopsReading() throws SQLException, Failure { @@ -119,8 +209,8 @@ public final class OnClientTester extend update("COPY 10 RECORDS INTO foo FROM 'banana' ON CLIENT", 10); assertEq("cancellation callback called", true, handler.isCancelled()); assertEq("handler encountered write error", true, handler.encounteredWriteError()); - // Server stopped reading after 10 rows. Will we stay in sync? - queryInt("SELECT COUNT(i) FROM foo", 10); + // connection is still alive + assertQueryInt("SELECT COUNT(i) FROM foo", 10); } public void test_Download(int n) throws SQLException, Failure { @@ -132,6 +222,8 @@ public final class OnClientTester extend update("COPY (SELECT * FROM foo) INTO 'banana' ON CLIENT", -1); assertEq("download attempts", 1, handler.countAttempts()); assertEq("lines downloaded", n, handler.lineCount()); + // connection is still alive + assertQueryInt("SELECT COUNT(*) FROM foo", n); } public void test_Download() throws SQLException, Failure { @@ -140,12 +232,17 @@ public final class OnClientTester extend public void test_ClientRefusesDownload() throws SQLException, Failure { prepare(); + BugFixLevel level = getLevel(); MyDownloadHandler handler = new MyDownloadHandler("download refused"); conn.setDownloadHandler(handler); update("INSERT INTO foo SELECT value as i, 'number' || value AS t FROM sys.generate_series(0, 100)", 100); expectError("COPY (SELECT * FROM foo) INTO 'banana' ON CLIENT", "download refused"); // Wish it were different but the server closes the connection expectError("SELECT 42 -- check if the connection still works", "Connection to server lost!"); + if (level.compareTo(BugFixLevel.CanRefuseDownload) >= 0) { + // connection is still alive + assertQueryInt("SELECT COUNT(*) FROM foo", 100); + } } public void test_LargeUpload() throws SQLException, Failure { @@ -157,7 +254,8 @@ public final class OnClientTester extend handler.setChunkSize(1024 * 1024); update("COPY INTO foo FROM 'banana' ON CLIENT", n); assertEq("cancellation callback called", false, handler.isCancelled()); - queryInt("SELECT COUNT(DISTINCT i) FROM foo", n); + // connection is still alive + assertQueryInt("SELECT COUNT(DISTINCT i) FROM foo", n); } public void test_LargeDownload() throws SQLException, Failure { @@ -179,7 +277,8 @@ public final class OnClientTester extend }; conn.setUploadHandler(handler); update("COPY INTO foo FROM 'banana' ON CLIENT", 3); - queryInt("SELECT i FROM foo WHERE t = 'three'", 3); + // connection is still alive + assertQueryInt("SELECT i FROM foo WHERE t = 'three'", 3); } public void test_UploadFromReader() throws SQLException, Failure { @@ -196,7 +295,7 @@ public final class OnClientTester extend }; conn.setUploadHandler(handler); update("COPY INTO foo FROM 'banana' ON CLIENT", 3); - queryInt("SELECT i FROM foo WHERE t = 'three'", 3); + assertQueryInt("SELECT i FROM foo WHERE t = 'three'", 3); } public void test_UploadFromReaderOffset() throws SQLException, Failure { @@ -212,7 +311,7 @@ public final class OnClientTester extend }; conn.setUploadHandler(handler); update("COPY OFFSET 2 INTO foo FROM 'banana' ON CLIENT", 2); - queryInt("SELECT i FROM foo WHERE t = 'three'", 3); + assertQueryInt("SELECT i FROM foo WHERE t = 'three'", 3); } public void test_FailUploadLate() throws SQLException, Failure { @@ -246,7 +345,6 @@ public final class OnClientTester extend // Cannot check the server log, but at the time I checked, it said "prematurely stopped client", which is fine. } - // Disabled because it hangs, triggering the watchdog timer public void test_FailDownloadLate() throws SQLException, Failure { prepare(); MyDownloadHandler handler = new MyDownloadHandler(200, "download refused"); @@ -269,7 +367,7 @@ public final class OnClientTester extend ps.close(); conn.setUploadHandler(new FileTransferHandler(d, true)); update("COPY INTO foo FROM 'data.txt' ON CLIENT", 3); - queryInt("SELECT SUM(i) FROM foo", 6); + assertQueryInt("SELECT SUM(i) FROM foo", 6); } public void test_FileTransferHandlerUploadRefused() throws IOException, SQLException, Failure { @@ -288,7 +386,7 @@ public final class OnClientTester extend String quoted = f.toAbsolutePath().toString().replaceAll("'", "''"); expectError("COPY INTO foo FROM R'"+ quoted + "' ON CLIENT", "not in upload directory"); // connection is still alive - queryInt("SELECT SUM(i) FROM foo", 0); + assertQueryInt("SELECT SUM(i) FROM foo", 0); } public void test_FileTransferHandlerDownload() throws SQLException, Failure, IOException { @@ -300,17 +398,23 @@ public final class OnClientTester extend List<String> lines = Files.readAllLines(d.resolve("data.txt")); assertEq("lines written", lines.size(), 1); assertEq("line content", lines.get(0), "42|\"forty-two\""); + // connection is still alive + assertQueryInt("SELECT SUM(i) FROM foo", 42); } public void test_FileTransferHandlerDownloadRefused() throws SQLException, Failure, IOException { prepare(); + BugFixLevel level = getLevel(); update("INSERT INTO foo VALUES (42, 'forty-two')", 1); Path d = getTmpDir(currentTestName); Path d2 = getTmpDir(currentTestName + "2"); conn.setDownloadHandler(new FileTransferHandler(d2, false)); String quoted = d.resolve("data.txt").toAbsolutePath().toString().replaceAll("'", "''"); expectError("COPY SELECT * FROM foo INTO R'" + quoted + "' ON CLIENT", "not in download directory"); - + if (level.compareTo(BugFixLevel.CanRefuseDownload) >= 0) { + // connection is still alive + assertQueryInt("SELECT SUM(i) FROM foo", 42); + } } static class MyUploadHandler implements UploadHandler {
--- a/tests/TestRunner.java +++ b/tests/TestRunner.java @@ -30,9 +30,9 @@ public class TestRunner { protected String currentTestName; protected final WatchDog watchDog; protected MonetConnection conn; - private Statement stmt; + protected Statement stmt; private StringWriter outBuffer; - private PrintWriter out; + protected PrintWriter out; private Path tmpDir = null; public TestRunner(String jdbcUrl, int verbosity, boolean watchDogEnabled) { @@ -239,7 +239,7 @@ public class TestRunner { } } - protected void queryInt(String query, int expected) throws SQLException, Failure { + protected void assertQueryInt(String query, int expected) throws SQLException, Failure { if (execute(query) == false) { fail("Query does not return a result set"); } @@ -259,6 +259,26 @@ public class TestRunner { assertEq("query result", expected, result); } + protected String queryString(String query) throws SQLException, Failure { + if (execute(query) == false) { + fail("Query does not return a result set"); + } + ResultSet rs = stmt.getResultSet(); + ResultSetMetaData metaData = rs.getMetaData(); + assertEq("column count", 1, metaData.getColumnCount()); + if (!rs.next()) { + fail("Result set is empty"); + } + String result = rs.getString(1); + if (rs.next()) { + String message = "Result set has more than one row"; + fail(message); + } + rs.close(); + checked("row count", 1); + return result; + } + protected synchronized Path getTmpDir(String name) throws IOException { if (tmpDir == null) { tmpDir = Files.createTempDirectory("testMonetDB");