package org.voltdb.regressionsuites; import junit.framework.Test; import org.voltdb.BackendTarget; import org.voltdb.CatalogContext; import org.voltdb.VoltSystemProcedure; import org.voltdb.VoltTable; import org.voltdb.catalog.Table; import org.voltdb.client.Client; import org.voltdb.client.ClientResponse; import org.voltdb.client.ProcCallException; import org.voltdb.sysprocs.AdHoc; import org.voltdb.utils.VoltTableUtil; import edu.brown.benchmark.smallbank.SmallBankConstants; import edu.brown.benchmark.smallbank.SmallBankLoader; import edu.brown.benchmark.smallbank.SmallBankProjectBuilder; import edu.brown.benchmark.smallbank.procedures.SendPayment; import edu.brown.hstore.Hstoreservice.Status; /** * Simple test suite for the SmallBank benchmark * @author pavlo */ public class TestSmallBankSuite extends RegressionSuite { private static final String PREFIX = "smallbank"; private static final double SCALEFACTOR = 0.0001; /** * Constructor needed for JUnit. Should just pass on parameters to superclass. * @param name The name of the method to test. This is just passed to the superclass. */ public TestSmallBankSuite(String name) { super(name); } /** * testSendPayment */ public void testSendPayment() throws Exception { CatalogContext catalogContext = this.getCatalogContext(); Client client = this.getClient(); TestSmallBankSuite.initializeSmallBankDatabase(catalogContext, client); long acctIds[] = { 1l, 2l }; double balances[] = { 100d, 0d }; ClientResponse cresponse; VoltTable results[]; Table catalog_tbl = catalogContext.getTableByName(SmallBankConstants.TABLENAME_ACCOUNTS); long num_rows = RegressionSuiteUtil.getRowCount(client, catalog_tbl); assert(num_rows > acctIds[acctIds.length-1]); // System.err.println("# of Rows: " + num_rows); for (int i = 0; i < acctIds.length; i++) { updateBalance(client, acctIds[i], balances[i]); checkBalance(client, acctIds[i], balances[i]); } // FOR // Run the SendPayment txn to send all the money from the first // account to the second account. cresponse = client.callProcedure(SendPayment.class.getSimpleName(), acctIds[0], acctIds[1], balances[0]); assertEquals(Status.OK, cresponse.getStatus()); results = cresponse.getResults(); assertEquals(2, results.length); for (int i = 0; i < results.length; i++) { assertEquals(1l, results[i].asScalarLong()); // System.err.println(VoltTableUtil.format(results[i])); } // FOR // Make sure the account balances have switched for (int i = 0; i < acctIds.length; i++) { checkBalance(client, acctIds[i], balances[i == 0 ? 1 : 0]); } // FOR } /** * testSendPaymentInsufficientFunds */ public void testSendPaymentInsufficientFunds() throws Exception { CatalogContext catalogContext = this.getCatalogContext(); Client client = this.getClient(); TestSmallBankSuite.initializeSmallBankDatabase(catalogContext, client); long acctIds[] = { 1l, 2l }; double balances[] = { 0d, 100d }; ClientResponse cresponse; for (int i = 0; i < acctIds.length; i++) { updateBalance(client, acctIds[i], balances[i]); checkBalance(client, acctIds[i], balances[i]); } // FOR // Run the SendPayment txn that tries to send to money from the first account, // but it should fail because the balance is zero try { cresponse = client.callProcedure(SendPayment.class.getSimpleName(), acctIds[0], acctIds[1], balances[1]); } catch (ProcCallException ex) { cresponse = ex.getClientResponse(); } assertEquals(Status.ABORT_USER, cresponse.getStatus()); // Make sure the account balances are still the same for (int i = 0; i < acctIds.length; i++) { checkBalance(client, acctIds[i], balances[i]); } // FOR } // -------------------------------------------------------------------------------------------- // UTILITY METHODS // -------------------------------------------------------------------------------------------- public static final void initializeSmallBankDatabase(final CatalogContext catalogContext, final Client client) throws Exception { String args[] = { "NOCONNECTIONS=true", "CLIENT.SCALEFACTOR=" + SCALEFACTOR, }; SmallBankLoader loader = new SmallBankLoader(args) { { this.setCatalogContext(catalogContext); this.setClientHandle(client); } @Override public CatalogContext getCatalogContext() { return (catalogContext); } }; loader.load(); } public static void checkBalance(Client client, long acctId, double expected) throws Exception { // Make sure that we set it correctly String query = String.format("SELECT * FROM %s WHERE custid = %d", SmallBankConstants.TABLENAME_CHECKING, acctId); ClientResponse cresponse = client.callProcedure("@AdHoc", query); assertEquals(Status.OK, cresponse.getStatus()); VoltTable results[] = cresponse.getResults(); assertEquals(1, results.length); if (results[0].getRowCount() == 0) { System.err.println(VoltTableUtil.format(results[0])); } assertEquals("No rows for acctId "+acctId, 1, results[0].getRowCount()); assertTrue(results[0].advanceRow()); assertEquals("Mismatched balance for acctId "+acctId, expected, results[0].getDouble("bal")); } public static void updateBalance(Client client, long acctId, double balance) throws Exception { // Prime the customer's balance String query = String.format("UPDATE %s SET bal = %f WHERE custid = %d", SmallBankConstants.TABLENAME_CHECKING, balance, acctId); String procName = VoltSystemProcedure.procCallName(AdHoc.class); ClientResponse cresponse = client.callProcedure(procName, query); assertEquals(Status.OK, cresponse.getStatus()); VoltTable results[] = cresponse.getResults(); assertEquals(1, results.length); assertTrue(results[0].advanceRow()); } public static Test suite() { // the suite made here will all be using the tests from this class MultiConfigSuiteBuilder builder = new MultiConfigSuiteBuilder(TestSmallBankSuite.class); SmallBankProjectBuilder project = new SmallBankProjectBuilder(); project.addAllDefaults(); boolean success; VoltServerConfig config; ///////////////////////////////////////////////////////////// // CONFIG #1: 1 Local Site/Partition running on JNI backend ///////////////////////////////////////////////////////////// config = new LocalSingleProcessServer(PREFIX+"-1part.jar", 1, BackendTarget.NATIVE_EE_JNI); success = config.compile(project); assert(success); builder.addServerConfig(config); ///////////////////////////////////////////////////////////// // CONFIG #2: 1 Local Site with 2 Partitions running on JNI backend ///////////////////////////////////////////////////////////// config = new LocalSingleProcessServer(PREFIX+"-2part.jar", 2, BackendTarget.NATIVE_EE_JNI); success = config.compile(project); assert(success); builder.addServerConfig(config); //////////////////////////////////////////////////////////// // CONFIG #3: cluster of 2 nodes running 2 site each, one replica //////////////////////////////////////////////////////////// config = new LocalCluster(PREFIX+"-cluster.jar", 2, 2, 1, BackendTarget.NATIVE_EE_JNI); success = config.compile(project); assert(success); builder.addServerConfig(config); return builder; } }