Package org.voltdb.regressionsuites

Source Code of org.voltdb.regressionsuites.RegressionSuite

/* This file is part of VoltDB.
* Copyright (C) 2008-2014 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.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.voltdb.VoltDB;
import org.voltdb.VoltTable;
import org.voltdb.VoltType;
import org.voltdb.client.Client;
import org.voltdb.client.ClientConfig;
import org.voltdb.client.ClientConfigForTest;
import org.voltdb.client.ClientFactory;
import org.voltdb.client.ConnectionUtil;
import org.voltdb.client.ProcCallException;
import org.voltdb.common.Constants;

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 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 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;
    }

    /**
     * @return a reference to the associated VoltServerConfig
     */
    public final VoltServerConfig getServerConfig() {
        return m_config;
    }

    public Client getClient() throws IOException {
        return getClient(1000 * 60 * 10); // 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 {
        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);
        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)[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;
    }

    public void validateTableOfLongs(Client c, String sql, long[][] expected)
            throws Exception, IOException, ProcCallException {
        assertNotNull(expected);
        VoltTable vt = c.callProcedure("@AdHoc", sql).getResults()[0];
        validateTableOfLongs(vt, expected);
    }

    public void validateTableOfScalarLongs(VoltTable vt, long[] expected) {
        assertNotNull(expected);
        assertEquals(expected.length, vt.getRowCount());
        int len = expected.length;
        for (int i=0; i < len; i++) {
            validateRowOfLongs(vt, new long[] {expected[i]});
        }
    }

    public 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(vt, expected[i]);
        }
    }

    public void validateRowOfLongs(VoltTable vt, long [] expected) {
        int len = expected.length;
        assertTrue(vt.advanceRow());
        for (int i=0; i < len; 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();
                        }
                    } catch (ArithmeticException newestEx) {
                        newestEx.printStackTrace();
                        fail();
                    }
                }
            }
            // Long.MIN_VALUE is like a NULL
            if (expected[i] != Long.MIN_VALUE) {
                assertEquals(expected[i], actual);
            } else {
                if (isHSQL()) {
                    // Hsql return 0 for NULL
                    assertEquals(0, actual);
                } else {
                    VoltType type = vt.getColumnType(i);
                    assertEquals(Long.parseLong(type.getNullValue().toString()), actual);
                }
            }
        }
    }

    static public void validateTableColumnOfScalarVarchar(VoltTable vt, String[] expected) {
        validateTableColumnOfScalarVarchar(vt, 0, expected);
    }

    static public 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());
            assertEquals(expected[i], vt.getString(col));
        }
    }

    public 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 public void verifyStmtFails(Client client, String stmt, String expectedPattern) throws IOException {
        verifyProcFails(client, expectedPattern, "@AdHoc", stmt);
    }

    static public 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 public 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 public void validStatisticsForTableLimit(Client client, String tableName, long limit) throws Exception {
        validStatisticsForTableLimitAndPercentage(client, tableName, limit, -1);
    }

    static public 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 public 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);
    }
}
TOP

Related Classes of org.voltdb.regressionsuites.RegressionSuite

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.