Java tutorial
/* This file is part of VoltDB. * Copyright (C) 2008-2015 VoltDB Inc. * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ package org.voltdb.regressionsuites; import java.io.IOException; import java.math.BigDecimal; import java.math.MathContext; import java.math.RoundingMode; import java.net.ConnectException; import java.nio.channels.SocketChannel; import java.util.ArrayList; import java.util.List; import java.util.Random; import java.util.regex.Pattern; import junit.framework.TestCase; import org.apache.commons.lang3.StringUtils; import org.voltdb.VoltDB; import org.voltdb.VoltTable; import org.voltdb.VoltType; import org.voltdb.client.Client; import org.voltdb.client.ClientAuthHashScheme; import org.voltdb.client.ClientConfig; import org.voltdb.client.ClientConfigForTest; import org.voltdb.client.ClientFactory; import org.voltdb.client.ConnectionUtil; import org.voltdb.client.NoConnectionsException; import org.voltdb.client.ProcCallException; import org.voltdb.common.Constants; import org.voltdb.types.VoltDecimalHelper; import org.voltdb.utils.Encoder; import com.google_voltpatches.common.net.HostAndPort; /** * Base class for a set of JUnit tests that perform regression tests * on a running VoltDB server. It is assumed that all tests will access * a particular VoltDB server to do their work and check the output of * any procedures called. The main feature of this class is that the * backend instance of VoltDB is very flexible and the tests can be run * on multiple instances of VoltDB to test different VoltDB topologies. * */ public class RegressionSuite extends TestCase { protected static int m_verboseDiagnosticRowCap = 40; protected VoltServerConfig m_config; protected String m_username = "default"; protected String m_password = "password"; private final ArrayList<Client> m_clients = new ArrayList<Client>(); private final ArrayList<SocketChannel> m_clientChannels = new ArrayList<SocketChannel>(); protected final String m_methodName; /** * Trivial constructor that passes parameter on to superclass. * @param name The name of the method to run as a test. (JUnit magic) */ public RegressionSuite(final String name) { super(name); m_methodName = name; } /** * JUnit special method called to setup the test. This instance will start * the VoltDB server using the VoltServerConfig instance provided. */ @Override public void setUp() throws Exception { //New tests means a new server thread that hasn't done a restore m_config.setCallingMethodName(m_methodName); m_config.startUp(true); } /** * JUnit special method called to shutdown the test. This instance will * stop the VoltDB server using the VoltServerConfig instance provided. */ @Override public void tearDown() throws Exception { m_config.shutDown(); for (final Client c : m_clients) { c.close(); } synchronized (m_clientChannels) { for (final SocketChannel sc : m_clientChannels) { try { ConnectionUtil.closeConnection(sc); } catch (final IOException e) { e.printStackTrace(); } } m_clientChannels.clear(); } m_clients.clear(); } /** * @return Is the underlying instance of VoltDB running HSQL? */ public boolean isHSQL() { return m_config.isHSQL(); } /** * @return The number of logical partitions in this configuration */ public int getLogicalPartitionCount() { return m_config.getLogicalPartitionCount(); } /** * @return Is the underlying instance of VoltDB running Valgrind with the IPC client? */ public boolean isValgrind() { return m_config.isValgrind(); } public boolean isLocalCluster() { return m_config instanceof LocalCluster; } public boolean isDebug() { return m_config.isDebug(); } /** * @return a reference to the associated VoltServerConfig */ public final VoltServerConfig getServerConfig() { return m_config; } public Client getAdminClient() throws IOException { return getClient(1000 * 60 * 10, ClientAuthHashScheme.HASH_SHA256, true); // 10 minute default } public Client getClient() throws IOException { return getClient(1000 * 60 * 10, ClientAuthHashScheme.HASH_SHA256); // 10 minute default } public Client getClient(ClientAuthHashScheme scheme) throws IOException { return getClient(1000 * 60 * 10, scheme); // 10 minute default } public Client getClientToHostId(int hostId) throws IOException { return getClientToHostId(hostId, 1000 * 60 * 10); // 10 minute default } public Client getFullyConnectedClient() throws IOException { return getFullyConnectedClient(1000 * 60 * 10); // 10 minute default } /** * Get a VoltClient instance connected to the server driven by the * VoltServerConfig instance. Just pick from the list of listeners * randomly. * * Only uses the time * * @return A VoltClient instance connected to the server driven by the * VoltServerConfig instance. */ public Client getClient(long timeout) throws IOException { return getClient(timeout, ClientAuthHashScheme.HASH_SHA256); } /** * Get a VoltClient instance connected to the server driven by the * VoltServerConfig instance. Just pick from the list of listeners * randomly. * * Only uses the time * * @return A VoltClient instance connected to the server driven by the * VoltServerConfig instance. */ public Client getClient(long timeout, ClientAuthHashScheme scheme) throws IOException { return getClient(timeout, scheme, false); } public Client getClient(long timeout, ClientAuthHashScheme scheme, boolean useAdmin) throws IOException { final Random r = new Random(); String listener = null; if (useAdmin) { listener = m_config.getAdminAddress(r.nextInt(m_config.getListenerCount())); } else { listener = m_config.getListenerAddress(r.nextInt(m_config.getListenerCount())); } ClientConfig config = new ClientConfigForTest(m_username, m_password, scheme); config.setConnectionResponseTimeout(timeout); config.setProcedureCallTimeout(timeout); final Client client = ClientFactory.createClient(config); // Use the port generated by LocalCluster if applicable try { client.createConnection(listener); } // retry once catch (ConnectException e) { if (useAdmin) { listener = m_config.getAdminAddress(r.nextInt(m_config.getListenerCount())); } else { listener = m_config.getListenerAddress(r.nextInt(m_config.getListenerCount())); } client.createConnection(listener); } m_clients.add(client); return client; } /** * Get a VoltClient instance connected to the server driven by the * VoltServerConfig instance. Just pick from the list of listeners * randomly. * * Only uses the time * * @return A VoltClient instance connected to the server driven by the * VoltServerConfig instance. */ public Client getClientSha1(long timeout) throws IOException { final List<String> listeners = m_config.getListenerAddresses(); final Random r = new Random(); String listener = listeners.get(r.nextInt(listeners.size())); ClientConfig config = new ClientConfigForTest(m_username, m_password, ClientAuthHashScheme.HASH_SHA1); config.setConnectionResponseTimeout(timeout); config.setProcedureCallTimeout(timeout); final Client client = ClientFactory.createClient(config); // Use the port generated by LocalCluster if applicable try { client.createConnection(listener); } // retry once catch (ConnectException e) { listener = listeners.get(r.nextInt(listeners.size())); client.createConnection(listener); } m_clients.add(client); return client; } /** * Get a VoltClient instance connected to a specific server driven by the * VoltServerConfig instance. Find the server by the config's HostId. * * @return A VoltClient instance connected to the server driven by the * VoltServerConfig instance. */ public Client getClientToHostId(int hostId, long timeout) throws IOException { final String listener = m_config.getListenerAddress(hostId); ClientConfig config = new ClientConfigForTest(m_username, m_password); config.setConnectionResponseTimeout(timeout); config.setProcedureCallTimeout(timeout); final Client client = ClientFactory.createClient(config); try { client.createConnection(listener); } // retry once catch (ConnectException e) { client.createConnection(listener); } m_clients.add(client); return client; } public Client getFullyConnectedClient(long timeout) throws IOException { final List<String> listeners = m_config.getListenerAddresses(); final Random r = new Random(); ClientConfig config = new ClientConfigForTest(m_username, m_password); config.setConnectionResponseTimeout(timeout); config.setProcedureCallTimeout(timeout); final Client client = ClientFactory.createClient(config); for (String listener : listeners) { // Use the port generated by LocalCluster if applicable try { client.createConnection(listener); } // retry once catch (ConnectException e) { listener = listeners.get(r.nextInt(listeners.size())); client.createConnection(listener); } } m_clients.add(client); return client; } /** * Release a client instance and any resources associated with it */ public void releaseClient(Client c) throws IOException, InterruptedException { boolean removed = m_clients.remove(c); assert (removed); c.close(); } /** * Get a SocketChannel that is an authenticated connection to a server driven by the * VoltServerConfig instance. Just pick from the list of listeners * randomly. * * @return A SocketChannel that is already authenticated with the server */ public SocketChannel getClientChannel() throws IOException { return getClientChannel(false); } public SocketChannel getClientChannel(final boolean noTearDown) throws IOException { final List<String> listeners = m_config.getListenerAddresses(); final Random r = new Random(); final String listener = listeners.get(r.nextInt(listeners.size())); byte[] hashedPassword = ConnectionUtil.getHashedPassword(m_password); HostAndPort hNp = HostAndPort.fromString(listener); int port = Constants.DEFAULT_PORT; if (hNp.hasPort()) { port = hNp.getPort(); } final SocketChannel channel = (SocketChannel) ConnectionUtil.getAuthenticatedConnection(hNp.getHostText(), m_username, hashedPassword, port, null, ClientAuthHashScheme.getByUnencodedLength(hashedPassword.length))[0]; channel.configureBlocking(true); if (!noTearDown) { synchronized (m_clientChannels) { m_clientChannels.add(channel); } } return channel; } /** * Protected method used by MultiConfigSuiteBuilder to set the VoltServerConfig * instance a particular test will run with. * * @param config An instance of VoltServerConfig to run tests with. */ void setConfig(final VoltServerConfig config) { m_config = config; } @Override public String getName() { // munge the test name with the VoltServerConfig instance name return super.getName() + "-" + m_config.getName(); } /** * Return appropriate port for hostId. Deal with LocalCluster providing non-default ports. * @param hostId zero-based host id * @return port number */ public int port(int hostId) { return isLocalCluster() ? ((LocalCluster) m_config).port(hostId) : VoltDB.DEFAULT_PORT + hostId; } /** * Return appropriate admin port for hostId. Deal with LocalCluster providing non-default ports. * @param hostId zero-based host id * @return admin port number */ public int adminPort(int hostId) { return isLocalCluster() ? ((LocalCluster) m_config).adminPort(hostId) : VoltDB.DEFAULT_ADMIN_PORT + hostId; } /** * Return appropriate internal port for hostId. Deal with LocalCluster providing non-default ports. * @param hostId zero-based host id * @return internal port number */ public int internalPort(int hostId) { return isLocalCluster() ? ((LocalCluster) m_config).internalPort(hostId) : VoltDB.DEFAULT_INTERNAL_PORT + hostId; } static protected void validateTableOfLongs(Client c, String sql, long[][] expected) throws NoConnectionsException, IOException, ProcCallException { VoltTable vt = c.callProcedure("@AdHoc", sql).getResults()[0]; validateTableOfLongs(sql, vt, expected); } static protected void validateTableOfScalarLongs(VoltTable vt, long[] expected) { assertNotNull(expected); assertEquals("Different number of rows! ", expected.length, vt.getRowCount()); int len = expected.length; for (int i = 0; i < len; i++) { validateRowOfLongs(vt, new long[] { expected[i] }); } } static protected void validateTableOfScalarLongs(Client client, String sql, long[] expected) throws NoConnectionsException, IOException, ProcCallException { assertNotNull(expected); VoltTable vt = client.callProcedure("@AdHoc", sql).getResults()[0]; validateTableOfScalarLongs(vt, expected); } private static void dumpExpectedLongs(long[][] expected) { System.out.println("row count:" + expected.length); for (long[] row : expected) { String prefix = "{ "; for (long value : row) { System.out.print(prefix + value); prefix = ", "; } System.out.println(" }"); } } static private void validateTableOfLongs(String messagePrefix, VoltTable vt, long[][] expected) { assertNotNull(expected); if (expected.length != vt.getRowCount()) { if (vt.getRowCount() < m_verboseDiagnosticRowCap) { System.out.println("Diagnostic dump of unexpected result for " + messagePrefix + " : " + vt); System.out.println("VS. expected : "); dumpExpectedLongs(expected); } //* enable and set breakpoint to debug multiple row count mismatches and continue */ return; } assertEquals(messagePrefix + " returned wrong number of rows. ", expected.length, vt.getRowCount()); int len = expected.length; for (int i = 0; i < len; i++) { validateRowOfLongs(messagePrefix + " at row " + i + ", ", vt, expected[i]); } } public static void validateTableOfLongs(VoltTable vt, long[][] expected) { assertNotNull(expected); assertEquals("Wrong number of rows in table. ", expected.length, vt.getRowCount()); int len = expected.length; for (int i = 0; i < len; i++) { validateRowOfLongs("at row " + i + ", ", vt, expected[i]); } } static protected void validateRowOfLongs(String messagePrefix, VoltTable vt, long[] expected) { int len = expected.length; assertTrue(vt.advanceRow()); for (int i = 0; i < len; i++) { String message = messagePrefix + "at column " + i + ", "; long actual = -10000000; // ENG-4295: hsql bug: HSQLBackend sometimes returns wrong column type. try { actual = vt.getLong(i); } catch (IllegalArgumentException ex) { try { actual = (long) vt.getDouble(i); } catch (IllegalArgumentException newEx) { try { actual = vt.getTimestampAsLong(i); } catch (IllegalArgumentException exTm) { try { actual = vt.getDecimalAsBigDecimal(i).longValueExact(); } catch (IllegalArgumentException newerEx) { newerEx.printStackTrace(); fail(message); } } catch (ArithmeticException newestEx) { newestEx.printStackTrace(); fail(message); } } } // Long.MIN_VALUE is like a NULL if (expected[i] != Long.MIN_VALUE) { assertEquals(message, expected[i], actual); } else { VoltType type = vt.getColumnType(i); assertEquals(message + "expected null: ", Long.parseLong(type.getNullValue().toString()), actual); } } } static protected void validateRowOfLongs(VoltTable vt, long[] expected) { validateRowOfLongs("", vt, expected); } static protected void validateTableColumnOfScalarLong(VoltTable vt, int col, long[] expected) { assertNotNull(expected); assertEquals(expected.length, vt.getRowCount()); int len = expected.length; for (int i = 0; i < len; i++) { assertTrue(vt.advanceRow()); long actual = vt.getLong(col); if (expected[i] == Long.MIN_VALUE) { assertTrue(vt.wasNull()); assertEquals(null, actual); } else { assertEquals(expected[i], actual); } } } static protected void validateTableColumnOfScalarVarchar(Client client, String sql, String[] expected) throws NoConnectionsException, IOException, ProcCallException { VoltTable vt = client.callProcedure("@AdHoc", sql).getResults()[0]; validateTableColumnOfScalarVarchar(vt, 0, expected); } static protected void validateTableColumnOfScalarVarchar(VoltTable vt, String[] expected) { validateTableColumnOfScalarVarchar(vt, 0, expected); } static protected void validateTableColumnOfScalarVarchar(VoltTable vt, int col, String[] expected) { assertNotNull(expected); assertEquals(expected.length, vt.getRowCount()); int len = expected.length; for (int i = 0; i < len; i++) { assertTrue(vt.advanceRow()); if (expected[i] == null) { String actual = vt.getString(col); assertTrue(vt.wasNull()); assertEquals(null, actual); } else { assertEquals(expected[i], vt.getString(col)); } } } static protected void validateTableColumnOfScalarVarbinary(Client client, String sql, String[] expected) throws NoConnectionsException, IOException, ProcCallException { VoltTable vt = client.callProcedure("@AdHoc", sql).getResults()[0]; validateTableColumnOfScalarVarbinary(vt, 0, expected); } static private void validateTableColumnOfScalarVarbinary(VoltTable vt, int col, String[] expected) { assertNotNull(expected); assertEquals(expected.length, vt.getRowCount()); int len = expected.length; for (int i = 0; i < len; i++) { assertTrue(vt.advanceRow()); byte[] actual = vt.getVarbinary(col); if (expected[i] == null) { assertTrue(vt.wasNull()); assertEquals(null, actual); } else { assertEquals(expected[i], Encoder.hexEncode(actual)); } } } static protected void validateTableColumnOfScalarFloat(Client client, String sql, double[] expected) throws NoConnectionsException, IOException, ProcCallException { VoltTable vt = client.callProcedure("@AdHoc", sql).getResults()[0]; validateTableColumnOfScalarFloat(vt, 0, expected); } static protected void validateTableColumnOfScalarFloat(VoltTable vt, int col, double[] expected) { assertNotNull(expected); assertEquals(expected.length, vt.getRowCount()); int len = expected.length; for (int i = 0; i < len; i++) { assertTrue(vt.advanceRow()); double actual = vt.getDouble(col); if (expected[i] == Double.MIN_VALUE) { assertTrue(vt.wasNull()); assertEquals(null, actual); } else { assertEquals(expected[i], actual, 0.00001); } } } private void validateRowOfDecimal(VoltTable vt, BigDecimal[] expected) { int len = expected.length; assertTrue(vt.advanceRow()); for (int i = 0; i < len; i++) { BigDecimal actual = null; try { actual = vt.getDecimalAsBigDecimal(i); } catch (IllegalArgumentException ex) { ex.printStackTrace(); fail(); } if (expected[i] != null) { assertNotSame(null, actual); assertEquals(expected[i], actual); } else { if (isHSQL()) { // We don't actually use this with // HSQL. So, just assert failure here. fail("HSQL is not used to test the Volt DECIMAL type."); } else { assertTrue(vt.wasNull()); } } } } protected void validateTableOfDecimal(VoltTable vt, BigDecimal[][] expected) { assertNotNull(expected); assertEquals(expected.length, vt.getRowCount()); int len = expected.length; for (int i = 0; i < len; i++) { validateRowOfDecimal(vt, expected[i]); } } protected static int m_defaultScale = 12; protected static String m_roundingEnabledProperty = "BIGDECIMAL_ROUND"; protected static String m_roundingModeProperty = "BIGDECIMAL_ROUND_POLICY"; protected static String m_defaultRoundingEnablement = "true"; protected static String m_defaultRoundingMode = "HALF_UP"; protected static String getRoundingString(String label) { return String.format("%sRounding %senabled, mode is %s", label == null ? (label + ": ") : "", VoltDecimalHelper.isRoundingEnabled() ? "is " : "is *NOT* ", VoltDecimalHelper.getRoundingMode().toString()); } /* * This little helper function converts a string to * a decimal, and, maybe, rounds it to the Volt default scale * using the given mode. If roundingEnabled is false, no * rounding is done. */ protected static final BigDecimal roundDecimalValue(String decimalValueString, boolean roundingEnabled, RoundingMode mode) { BigDecimal bd = new BigDecimal(decimalValueString); if (!roundingEnabled) { return bd; } int precision = bd.precision(); int scale = bd.scale(); int lostScale = scale - m_defaultScale; if (lostScale <= 0) { return bd; } int newPrecision = precision - lostScale; MathContext mc = new MathContext(newPrecision, mode); BigDecimal nbd = bd.round(mc); assertTrue(nbd.scale() <= m_defaultScale); if (nbd.scale() != m_defaultScale) { nbd = nbd.setScale(m_defaultScale); } assertEquals(getRoundingString("Decimal Scale setting failure"), m_defaultScale, nbd.scale()); return nbd; } protected void validateTableOfDecimal(Client c, String sql, BigDecimal[][] expected) throws Exception, IOException, ProcCallException { assertNotNull(expected); VoltTable vt = c.callProcedure("@AdHoc", sql).getResults()[0]; validateTableOfDecimal(vt, expected); } static protected void validateTableColumnOfScalarDecimal(Client client, String sql, BigDecimal[] expected) throws NoConnectionsException, IOException, ProcCallException { VoltTable vt = client.callProcedure("@AdHoc", sql).getResults()[0]; validateTableColumnOfScalarDecimal(vt, 0, expected); } static protected void validateTableColumnOfScalarDecimal(VoltTable vt, int col, BigDecimal[] expected) { assertNotNull(expected); assertEquals(expected.length, vt.getRowCount()); int len = expected.length; for (int i = 0; i < len; i++) { assertTrue(vt.advanceRow()); BigDecimal actual = vt.getDecimalAsBigDecimal(col); if (expected[i] == null) { assertTrue(vt.wasNull()); assertEquals(null, actual); } else { BigDecimal rounded = expected[i].setScale(m_defaultScale, RoundingMode.valueOf(m_defaultRoundingMode)); assertEquals(rounded, actual); } } } protected void assertTablesAreEqual(String prefix, VoltTable expectedRows, VoltTable actualRows) { assertEquals(prefix + "column count mismatch. Expected: " + expectedRows.getColumnCount() + " actual: " + actualRows.getColumnCount(), expectedRows.getColumnCount(), actualRows.getColumnCount()); int i = 0; while (expectedRows.advanceRow()) { assertTrue(prefix + "too few actual rows; expected more than " + (i + 1), actualRows.advanceRow()); for (int j = 0; j < actualRows.getColumnCount(); j++) { String columnName = actualRows.getColumnName(j); String colPrefix = prefix + "row " + i + ": column: " + columnName + ": "; VoltType actualTy = actualRows.getColumnType(j); VoltType expectedTy = expectedRows.getColumnType(j); assertEquals(colPrefix + "type mismatch", expectedTy, actualTy); Object expectedObj = expectedRows.get(j, expectedTy); Object actualObj = expectedRows.get(j, actualTy); assertEquals(colPrefix + "values not equal: expected: " + expectedObj + ", actual: " + actualObj, expectedObj, actualObj); } i++; } assertFalse(prefix + "too many actual rows; expected only " + i, actualRows.advanceRow()); } static protected void verifyStmtFails(Client client, String stmt, String expectedPattern) throws IOException { verifyProcFails(client, expectedPattern, "@AdHoc", stmt); } static protected void verifyAdHocFails(Client client, String expectedPattern, Object... args) throws IOException { verifyProcFails(client, expectedPattern, "@AdHoc", args); } static protected void verifyProcFails(Client client, String expectedPattern, String storedProc, Object... args) throws IOException { String what; if (storedProc.compareTo("@AdHoc") == 0) { what = "the statement \"" + args[0] + "\""; } else { what = "the stored procedure \"" + storedProc + "\""; } try { client.callProcedure(storedProc, args); } catch (ProcCallException pce) { String msg = pce.getMessage(); String diagnostic = "Expected " + what + " to throw an exception matching the pattern \"" + expectedPattern + "\", but instead it threw an exception containing \"" + msg + "\"."; Pattern pattern = Pattern.compile(expectedPattern, Pattern.MULTILINE); assertTrue(diagnostic, pattern.matcher(msg).find()); return; } String diagnostic = "Expected " + what + " to throw an exception matching the pattern \"" + expectedPattern + "\", but instead it threw nothing."; fail(diagnostic); } // ALL OF THE VALIDATION SCHEMAS IN THIS TEST ARE BASED OFF OF // THE VOLTDB DOCS, RATHER THAN REUSING THE CODE THAT GENERATES THEM. // IN SOME MAGICAL FUTURE MAYBE THEY ALL CAN BE GENERATED FROM THE // SAME METADATA. static protected void validateSchema(VoltTable result, VoltTable expected) { assertEquals(expected.getColumnCount(), result.getColumnCount()); for (int i = 0; i < result.getColumnCount(); i++) { assertEquals("Failed name column: " + i, expected.getColumnName(i), result.getColumnName(i)); assertEquals("Failed type column: " + i, expected.getColumnType(i), result.getColumnType(i)); } } static protected void validStatisticsForTableLimit(Client client, String tableName, long limit) throws Exception { validStatisticsForTableLimitAndPercentage(client, tableName, limit, -1); } static protected void validStatisticsForTableLimitAndPercentage(Client client, String tableName, long limit, long percentage) throws Exception { long start = System.currentTimeMillis(); while (true) { long lastLimit = -1, lastPercentage = -1; Thread.sleep(1000); if (System.currentTimeMillis() - start > 10000) { String percentageStr = ""; if (percentage >= 0) { percentageStr = ", last seen percentage: " + lastPercentage; } fail("Took too long or have wrong answers: last seen limit: " + lastLimit + percentageStr); } VoltTable[] results = client.callProcedure("@Statistics", "TABLE", 0).getResults(); for (VoltTable t : results) { System.out.println(t.toString()); } if (results[0].getRowCount() == 0) continue; boolean foundTargetTuple = false; boolean limitExpected = false; boolean percentageExpected = percentage < 0 ? true : false; for (VoltTable vt : results) { while (vt.advanceRow()) { String name = vt.getString("TABLE_NAME"); if (tableName.equals(name)) { foundTargetTuple = true; lastLimit = vt.getLong("TUPLE_LIMIT"); if (limit == lastLimit) { limitExpected = true; } if (percentageExpected || percentage == (lastPercentage = vt.getLong("PERCENT_FULL"))) { percentageExpected = true; } if (limitExpected && percentageExpected) return; break; } } if (foundTargetTuple) break; } } } static protected void checkDeploymentPropertyValue(Client client, String key, String value) throws IOException, ProcCallException, InterruptedException { boolean found = false; VoltTable result = client.callProcedure("@SystemInformation", "DEPLOYMENT").getResults()[0]; while (result.advanceRow()) { if (result.getString("PROPERTY").equalsIgnoreCase(key)) { found = true; assertEquals(value, result.getString("VALUE")); break; } } assertTrue(found); } static protected void checkQueryPlan(Client client, String query, String... patterns) throws NoConnectionsException, IOException, ProcCallException { VoltTable vt; assert (patterns.length >= 1); vt = client.callProcedure("@Explain", query).getResults()[0]; String vtStr = vt.toString(); for (String pattern : patterns) { if (!vtStr.contains(pattern)) { fail("The explain plan \n" + vtStr + "\n is expected to contain pattern: " + pattern); } } } /** * Utility function to run queries and dump results to stdout. * @param client * @param queries one or more query strings to send in a batch * @throws IOException * @throws NoConnectionsException * @throws ProcCallException */ protected static void dumpQueryResults(Client client, String... queries) throws IOException, NoConnectionsException, ProcCallException { VoltTable vts[] = client.callProcedure("@AdHoc", StringUtils.join(queries, '\n')).getResults(); int ii = 0; for (VoltTable vtn : vts) { System.out.println("DEBUG: result for " + queries[ii] + "\n" + vtn + "\n"); ++ii; } } /** * Utility function to explain queries and dump results to stdout. * @param client * @param queries one or more query strings to send in a batch to @Explain. * @throws IOException * @throws NoConnectionsException * @throws ProcCallException */ protected static void dumpQueryPlans(Client client, String... queries) throws IOException, NoConnectionsException, ProcCallException { VoltTable vts[] = client.callProcedure("@Explain", StringUtils.join(queries, '\n')).getResults(); int ii = 0; for (VoltTable vtn : vts) { System.out.println("DEBUG: plan for " + queries[ii] + "\n" + vtn + "\n"); ++ii; } } protected static void truncateTables(Client client, String[] tables) throws IOException, ProcCallException { for (String tb : tables) { truncateTables(client, tb); } } protected static void truncateTables(Client client, String tb) throws IOException, ProcCallException { client.callProcedure("@AdHoc", "Truncate table " + tb); validateTableOfScalarLongs(client, "select count(*) from " + tb, new long[] { 0 }); } }