package org.molgenis.xgap.test; import java.io.BufferedReader; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.net.URL; import java.net.URLConnection; import java.sql.Connection; import java.sql.SQLException; import java.util.ArrayList; import java.util.List; import javax.sql.DataSource; import matrix.DataMatrixInstance; import matrix.general.DataMatrixHandler; import org.apache.commons.dbcp.BasicDataSource; import org.molgenis.auth.MolgenisPermission; import org.molgenis.data.Data; import org.molgenis.framework.db.Database; import org.molgenis.framework.db.DatabaseException; import org.molgenis.framework.db.QueryRule; import org.molgenis.framework.db.QueryRule.Operator; import org.molgenis.pheno.Individual; import org.molgenis.util.CsvWriter; import org.molgenis.util.DetectOS; import org.molgenis.util.TupleWriter; import org.molgenis.xgap.Marker; import org.molgenis.xgap.xqtlworkbench.ResetXgapDb; import org.testng.Assert; import org.testng.annotations.AfterClass; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; import regressiontest.cluster.DataLoader; import app.DatabaseFactory; import boot.Helper; import boot.RunStandalone; import filehandling.storage.StorageHandler; /** * Database/webserver stress test */ public class Stress_XqtlTestNG_IGNORED { // the two key parameters: number of simultaneous threads, // and number of iterations per thread private int test_number_of_threads = 100; private int test_iterations_per_thread = 100; // other constants private Database db; private int webserverport; @BeforeClass public void setup() throws Exception { // cleanup before we start XqtlSeleniumTest.deleteDatabase(); db = DatabaseFactory.create(); // setup database tables String report = ResetXgapDb.reset(db, true); Assert.assertTrue(report.endsWith("SUCCESS")); StorageHandler sh = new StorageHandler(db); Assert.assertFalse(sh.hasFileStorage(false, db)); // setup file storage sh.setFileStorage(storagePath(), db); sh.validateFileStorage(db); Assert.assertTrue(sh.hasValidFileStorage(db)); // start webserver webserverport = Helper.getAvailablePort(11040, 10); new RunStandalone(webserverport); } @AfterClass(alwaysRun = true) public void cleanupAfterClass() throws InterruptedException, Exception { db.close(); XqtlSeleniumTest.deleteDatabase(); } @Test public void importExampleData() throws Exception { // load example data ArrayList<String> result = DataLoader.load(db, false); Assert.assertTrue(result.get(result.size() - 2).equals("Complete success")); checkIfExampleDataIsOK(); // give permissions for anonymous to query markers // this saves us the trouble of working with sessions MolgenisPermission mp = new MolgenisPermission(); mp.setEntity_ClassName("org.molgenis.xgap.Marker"); mp.setRole_Name("anonymous"); mp.setPermission("read"); db.add(mp); } /** * Webserver and database stress test */ @Test(dependsOnMethods = "importExampleData") public void stressTest() throws Exception { // create X amount of threats with N iterations in each thread List<downloadMarkers> tests = new ArrayList<downloadMarkers>(); for (int i = 0; i < test_number_of_threads; i++) { tests.add(new downloadMarkers(test_iterations_per_thread, webserverport)); } // keep track if one of the threads threw an error (failed assert or IO // error) final Bool testCompletedSuccessfully = new Bool(true); // total assert fails final Int assertFails = new Int(0); // total IO errors final Int ioErrors = new Int(0); // start all threads List<Thread> threads = new ArrayList<Thread>(); for (final downloadMarkers t : tests) { Runnable runnable = new Runnable() { public void run() { try { t.downloadAndAssert(); } catch (IOException ioe) { ioe.printStackTrace(); testCompletedSuccessfully.setVal(false); ioErrors.setVal(ioErrors.getVal() + 1); } catch (AssertionError ae) { ae.printStackTrace(); testCompletedSuccessfully.setVal(false); assertFails.setVal(assertFails.getVal() + 1); } } }; Thread thread = new Thread(runnable); thread.start(); threads.add(thread); } // wait for all threads to complete for (Thread thread : threads) { thread.join(); } System.out.println("** TOTAL ASSERT FAILS: " + assertFails.getVal() + " **"); System.out.println("** TOTAL IO ERRORS: " + ioErrors.getVal() + " **"); // check if one (or more) threads failed Assert.assertTrue(testCompletedSuccessfully.getVal()); } /** * Database-only stress test */ @Test(dependsOnMethods = "importExampleData") public void stressDatabaseTest() throws Exception { DataSource ds = createDataSource(); // create X amount of threats with N iterations in each thread List<queryMarkers> tests = new ArrayList<queryMarkers>(); for (int i = 0; i < test_number_of_threads; i++) { tests.add(new queryMarkers(test_iterations_per_thread, ds)); } // keep track if one of the threads threw an error (failed assert or IO // error) final Bool testCompletedSuccessfully = new Bool(true); // total assert fails final Int assertFails = new Int(0); // total db errors final Int dbErrors = new Int(0); // total sql errors final Int sqlErrors = new Int(0); // start all threads List<Thread> threads = new ArrayList<Thread>(); for (final queryMarkers t : tests) { Runnable runnable = new Runnable() { public void run() { try { t.queryAndAssert(); } catch (DatabaseException dbe) { dbe.printStackTrace(); testCompletedSuccessfully.setVal(false); dbErrors.setVal(dbErrors.getVal() + 1); } catch (SQLException se) { se.printStackTrace(); testCompletedSuccessfully.setVal(false); sqlErrors.setVal(sqlErrors.getVal() + 1); } catch (AssertionError ae) { ae.printStackTrace(); testCompletedSuccessfully.setVal(false); assertFails.setVal(assertFails.getVal() + 1); } catch (IOException ie) { ie.printStackTrace(); testCompletedSuccessfully.setVal(false); assertFails.setVal(assertFails.getVal() + 1); } } }; Thread thread = new Thread(runnable); thread.start(); threads.add(thread); } // wait for all threads to complete for (Thread thread : threads) { thread.join(); } System.out.println("** TOTAL ASSERT FAILS: " + assertFails.getVal() + " **"); System.out.println("** TOTAL DB ERRORS: " + dbErrors.getVal() + " **"); System.out.println("** TOTAL SQL ERRORS: " + sqlErrors.getVal() + " **"); // check if one (or more) threads failed Assert.assertTrue(testCompletedSuccessfully.getVal()); } /** * Helper function, use to see if the example data is all there. */ private void checkIfExampleDataIsOK() throws Exception { Assert.assertTrue(db.find(Marker.class).size() > 0); Assert.assertTrue(db.find(Individual.class).size() > 0); Assert.assertTrue(db.find(Data.class).size() > 0); Data geno = db.find(Data.class, new QueryRule(Data.NAME, Operator.EQUALS, "genotypes")).get(0); DataMatrixInstance dm = new DataMatrixHandler(db).createInstance(geno, db); Assert.assertTrue(dm.getElement(0, 0).equals("A")); Assert.assertTrue(dm.getElement(1, 2).equals("B")); } /** * Helper function. Get the storage path to use in test. */ public String storagePath() { String storagePath = new File(".").getAbsolutePath() + File.separator + "tmp_archiver_test_data"; if (DetectOS.getOS().startsWith("windows")) { return storagePath.replace("\\", "/"); } else { return storagePath; } } // manually create a connection pool for the database-only stress test // NOTE: we also start a RunStandalone server with FrontController, // which also creates 1 datasource.. problem? private DataSource createDataSource() { // NOTE: these are custom settings for high performance BasicDataSource data_src = new BasicDataSource(); data_src.setDriverClassName("org.hsqldb.jdbcDriver"); data_src.setUsername("sa"); data_src.setPassword(""); data_src.setUrl("jdbc:hsqldb:file:hsqldb/molgenisdb;shutdown=true"); // a // path // within // the // src // folder? data_src.setMaxIdle(10); data_src.setMaxWait(1000); data_src.setMaxActive(-1); return (DataSource) data_src; } } class queryMarkers { int iterations; DataSource ds; public queryMarkers(int iterations, DataSource ds) { this.iterations = iterations; this.ds = ds; } public void queryAndAssert() throws SQLException, DatabaseException, AssertionError, IOException { // get a fresh database connection for each batch of queries Database db = getDatabase(); for (int i = 0; i < iterations; i++) { ByteArrayOutputStream out = new ByteArrayOutputStream(); TupleWriter writer = new CsvWriter(out); db.find(Marker.class, writer, new QueryRule[] {}); int lines = countLines(new ByteArrayInputStream(out.toByteArray())); Assert.assertEquals(lines, 118); // 117 markers + 1 for col.header } db.close(); // not needed } private int countLines(InputStream is) { String content = convertStreamToString(is); String[] lines = content.split("\r\n|\r|\n"); return lines.length; } public String convertStreamToString(java.io.InputStream is) { try { return new java.util.Scanner(is).useDelimiter("\\A").next(); } catch (java.util.NoSuchElementException e) { return ""; } } private Database getDatabase() throws SQLException, DatabaseException { Connection conn = ds.getConnection(); Database db = DatabaseFactory.create(conn); return db; } } class downloadMarkers { int webserverport; int iterations; public downloadMarkers(int iterations, int webserverport) { this.webserverport = webserverport; this.iterations = iterations; } private ArrayList<String> getUrl(String path) throws IOException { ArrayList<String> res = new ArrayList<String>(); URL xqtl = new URL("http://localhost:" + webserverport + path); URLConnection xc = xqtl.openConnection(); BufferedReader in = new BufferedReader(new InputStreamReader(xc.getInputStream())); String inputLine; while ((inputLine = in.readLine()) != null) { res.add(inputLine); } in.close(); return res; } public void downloadAndAssert() throws AssertionError, IOException { for (int i = 0; i < iterations; i++) { String req = "/xqtl/api/find/Marker"; ArrayList<String> res = getUrl(req); Assert.assertEquals(res.size(), 118); // 117 markers + 1 for // col.header } } } class Bool { private boolean val; public Bool(boolean val) { this.val = val; } public boolean getVal() { return val; } public void setVal(boolean val) { this.val = val; } } class Int { private int val; public Int(int val) { this.val = val; } public int getVal() { return val; } public void setVal(int val) { this.val = val; } }