Mercurial > hg > monetdb-java
view tests/UrlTester.java @ 970:f90d811e97eb default tip
Adjust getTableTypes() test for new table type: LOCAL TEMPORARY VIEW, added in 11.53.4 (Mar2025-SP1)
author | Martin van Dinther <martin.van.dinther@monetdbsolutions.com> |
---|---|
date | Thu, 03 Apr 2025 15:01:33 +0200 (3 days ago) |
parents | d416e9b6b3d0 |
children |
line wrap: on
line source
/* * SPDX-License-Identifier: MPL-2.0 * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * Copyright 2024, 2025 MonetDB Foundation; * Copyright August 2008 - 2023 MonetDB B.V.; * Copyright 1997 - July 2008 CWI. */ import org.monetdb.mcl.net.*; import java.io.*; import java.net.URISyntaxException; import java.util.ArrayList; public final class UrlTester { final String filename; final int verbose; final BufferedReader reader; int lineno = 0; int testCount = 0; Target target = null; Target.Validated validated = null; public UrlTester(String filename, BufferedReader reader, int verbose) { this.filename = filename; this.verbose = verbose; this.reader = reader; } public UrlTester(String filename, int verbose) throws IOException { this.filename = filename; this.verbose = verbose; this.reader = new BufferedReader(new FileReader(filename)); } public static void main(String[] args) throws IOException { ArrayList<String> filenames = new ArrayList<>(); int verbose = 0; for (String arg : args) { switch (arg) { case "-vvv": verbose++; case "-vv": verbose++; case "-v": verbose++; break; case "-h": case "--help": exitUsage(null); break; default: if (!arg.startsWith("-")) { filenames.add(arg); } else { exitUsage("Unexpected argument: " + arg); } break; } } runUnitTests(); try { if (filenames.isEmpty()) { runAllTests(); } else { for (String filename : filenames) { new UrlTester(filename, verbose).run(); } } } catch (Failure e) { System.err.println("Test failed: " + e.getMessage()); System.exit(1); } } private static void exitUsage(String message) { if (message != null) { System.err.println(message); } System.err.println("Usage: UrlTester OPTIONS [FILENAME..]"); System.err.println("Options:"); System.err.println(" -v Be more verbose"); System.err.println(" -h --help Show this help"); int status = message == null ? 0 : 1; System.exit(status); } public static UrlTester forResource(String resourceName, int verbose) throws FileNotFoundException { InputStream stream = UrlTester.class.getResourceAsStream(resourceName); if (stream == null) { throw new FileNotFoundException("Resource " + resourceName); } BufferedReader reader = new BufferedReader(new InputStreamReader(stream)); return new UrlTester(resourceName, reader, verbose); } public static void runAllTests() throws IOException, Failure { runUnitTests(); UrlTester.forResource("/tests.md", 0).run(); UrlTester.forResource("/javaspecific.md", 0).run(); } public static void runUnitTests() { testDefaults(); testParameters(); } private static void testDefaults() { Target target = new Target(); for (Parameter parm : Parameter.values()) { Object expected = parm.getDefault(); if (expected == null) continue; Object actual = target.getObject(parm); if (!expected.equals(actual)) { throw new RuntimeException("Default for " + parm.name + " expected to be <" + expected + "> but is <" + actual + ">"); } } } private static void testParameters() { for (Parameter parm : Parameter.values()) { Parameter found = Parameter.forName(parm.name); if (parm != found) { String foundStr = found != null ? found.name : "null"; throw new RuntimeException("Looking up <" + parm.name + ">, found <" + foundStr); } } } public void run() throws Failure, IOException { try { processFile(); } catch (Failure e) { if (e.getFilename() == null) { e.setFilename(filename); e.setLineno(lineno); throw e; } } } private void processFile() throws IOException, Failure { while (true) { String line = reader.readLine(); if (line == null) break; lineno++; processLine(line); } if (verbose >= 1) { System.out.println(); System.out.println("Ran " + testCount + " tests in " + lineno + " lines"); } } private void processLine(String line) throws Failure { line = line.replaceFirst("\\s+$", ""); // remove trailing if (target == null && line.equals("```test")) { if (verbose >= 2) { if (testCount > 0) { System.out.println(); } System.out.println("\u25B6 " + filename + ":" + lineno); } target = new Target(); testCount++; return; } if (target != null) { if (line.equals("```")) { stopProcessing(); return; } handleCommand(line); } } private void stopProcessing() { target = null; validated = null; } private void handleCommand(String line) throws Failure { if (verbose >= 3) { System.out.println(line); } if (line.isEmpty()) return; String[] parts = line.split("\\s+", 2); String command = parts[0]; switch (command.toUpperCase()) { case "ONLY": handleOnly(true, parts[1]); return; case "NOT": handleOnly(false, parts[1]); return; case "PARSE": handleParse(parts[1], null); return; case "ACCEPT": handleParse(parts[1], true); return; case "REJECT": handleParse(parts[1], false); return; case "SET": handleSet(parts[1]); return; case "EXPECT": handleExpect(parts[1]); return; default: throw new Failure("Unexpected command: " + command); } } private void handleOnly(boolean mustBePresent, String rest) throws Failure { boolean found = false; for (String part : rest.split("\\s+")) { if (part.equals("jdbc")) { found = true; break; } } if (found != mustBePresent) { // do not further process this block stopProcessing(); } } private int findEqualSign(String rest) throws Failure { int index = rest.indexOf('='); if (index < -1) throw new Failure("Expected to find a '='"); return index; } private String splitKey(String rest) throws Failure { int index = findEqualSign(rest); return rest.substring(0, index); } private String splitValue(String rest) throws Failure { int index = findEqualSign(rest); return rest.substring(index + 1); } private void handleSet(String rest) throws Failure { validated = null; String key = splitKey(rest); String value = splitValue(rest); try { target.setString(key, value); } catch (ValidationError e) { throw new Failure(e.getMessage()); } } private void handleParse(String rest, Boolean shouldSucceed) throws Failure { URISyntaxException parseError = null; ValidationError validationError = null; validated = null; try { target.barrier(); MonetUrlParser.parse(target, rest); } catch (URISyntaxException e) { parseError = e; } catch (ValidationError e) { validationError = e; } if (parseError == null && validationError == null) { try { tryValidate(); } catch (ValidationError e) { validationError = e; } } if (shouldSucceed == Boolean.FALSE) { if (parseError != null || validationError != null) return; // happy else throw new Failure("URL unexpectedly parsed and validated"); } if (parseError != null) throw new Failure("Parse error: " + parseError); if (validationError != null && shouldSucceed == Boolean.TRUE) throw new Failure("Validation error: " + validationError); } private void handleExpect(String rest) throws Failure { String key = splitKey(rest); String expectedString = splitValue(rest); Object actual = null; try { actual = extract(key); } catch (ValidationError e) { throw new Failure(e.getMessage()); } Object expected; try { if (actual instanceof Boolean) expected = ParameterType.Bool.parse(key, expectedString); else if (actual instanceof Integer) expected = ParameterType.Int.parse(key, expectedString); else expected = expectedString; } catch (ValidationError e) { String typ = actual.getClass().getName(); throw new Failure("Cannot convert expected value <" + expectedString + "> to " + typ + ": " + e.getMessage()); } if (actual.equals(expected)) return; throw new Failure("Expected " + key + "=<" + expectedString + ">, found <" + actual + ">"); } private Target.Validated tryValidate() throws ValidationError { if (validated == null) validated = target.validate(); return validated; } private Object extract(String key) throws ValidationError, Failure { switch (key) { case "valid": try { tryValidate(); } catch (ValidationError e) { return Boolean.FALSE; } return Boolean.TRUE; case "connect_scan": return tryValidate().connectScan(); case "connect_port": return tryValidate().connectPort(); case "connect_unix": return tryValidate().connectUnix(); case "connect_tcp": return tryValidate().connectTcp(); case "connect_tls_verify": switch (tryValidate().connectVerify()) { case None: return ""; case Cert: return "cert"; case Hash: return "hash"; case System: return "system"; default: throw new IllegalStateException("unreachable"); } case "connect_certhash_digits": return tryValidate().connectCertHashDigits(); case "connect_binary": return tryValidate().connectBinary(); case "connect_clientkey": return tryValidate().connectClientKey(); case "connect_clientcert": return tryValidate().connectClientCert(); default: Parameter parm = Parameter.forName(key); if (parm != null) return target.getObject(parm); else throw new Failure("Unknown attribute: " + key); } } @SuppressWarnings("serial") public static class Failure extends Exception { private String filename = null; private int lineno = -1; public Failure(String message) { super(message); } @Override public String getMessage() { StringBuilder buffer = new StringBuilder(); if (filename != null) { buffer.append(filename).append(":"); if (lineno >= 0) buffer.append(lineno).append(":"); } buffer.append(super.getMessage()); return buffer.toString(); } public String getFilename() { return filename; } public void setFilename(String filename) { this.filename = filename; } public int getLineno() { return lineno; } public void setLineno(int lineno) { this.lineno = lineno; } } }