/*
* Copyright 2004-2011 H2 Group. Multiple-Licensed under the H2 License,
* Version 1.0, and under the Eclipse Public License, Version 1.0
* (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.test.synth.sql;
import java.util.ArrayList;
import org.h2.test.TestAll;
import org.h2.test.TestBase;
import org.h2.util.MathUtils;
import org.h2.util.New;
/**
* A test that generates random SQL statements against a number of databases
* and compares the results.
*/
public class TestSynth extends TestBase {
// TODO hsqldb: call 1||null should return 1 but returns null
// TODO hsqldb: call mod(1) should return invalid parameter count
/**
* A H2 database connection.
*/
static final int H2 = 0;
/**
* An in-memory H2 database connection.
*/
static final int H2_MEM = 1;
/**
* An HSQLDB database connection.
*/
static final int HSQLDB = 2;
/**
* A MySQL database connection.
*/
static final int MYSQL = 3;
/**
* A PostgreSQL database connection.
*/
static final int POSTGRESQL = 4;
private DbState dbState = new DbState(this);
private ArrayList<DbInterface> databases;
private ArrayList<Command> commands;
private RandomGen random = new RandomGen();
private boolean showError, showLog;
private boolean stopImmediately;
private int mode;
/**
* Run just this test.
*
* @param a ignored
*/
public static void main(String... a) throws Exception {
TestBase.createCaller().init().test();
}
/**
* Check whether this database is of the specified type.
*
* @param isType the database type
* @return true if it is
*/
boolean is(int isType) {
return mode == isType;
}
/**
* Get the random number generator.
*
* @return the random number generator
*/
RandomGen random() {
return random;
}
/**
* Get a random identifier.
*
* @return the random identifier
*/
String randomIdentifier() {
int len = random.getLog(8) + 2;
while (true) {
return random.randomString(len);
}
}
private void add(Command command) throws Exception {
command.run(dbState);
commands.add(command);
}
private void addRandomCommands() throws Exception {
switch (random.getInt(20)) {
case 0: {
add(Command.getDisconnect(this));
add(Command.getConnect(this));
break;
}
case 1: {
Table table = Table.newRandomTable(this);
add(Command.getCreateTable(this, table));
break;
}
case 2: {
Table table = randomTable();
add(Command.getCreateIndex(this, table.newRandomIndex()));
break;
}
case 3:
case 4:
case 5: {
Table table = randomTable();
add(Command.getRandomInsert(this, table));
break;
}
case 6:
case 7:
case 8: {
Table table = randomTable();
add(Command.getRandomUpdate(this, table));
break;
}
case 9:
case 10: {
Table table = randomTable();
add(Command.getRandomDelete(this, table));
break;
}
default: {
Table table = randomTable();
add(Command.getRandomSelect(this, table));
}
}
}
private void testRun(int seed) throws Exception {
random.setSeed(seed);
commands = New.arrayList();
add(Command.getConnect(this));
add(Command.getReset(this));
for (int i = 0; i < 1; i++) {
Table table = Table.newRandomTable(this);
add(Command.getCreateTable(this, table));
add(Command.getCreateIndex(this, table.newRandomIndex()));
}
for (int i = 0; i < 2000; i++) {
addRandomCommands();
}
// for (int i = 0; i < 20; i++) {
// Table table = randomTable();
// add(Command.getRandomInsert(this, table));
// }
// for (int i = 0; i < 100; i++) {
// Table table = randomTable();
// add(Command.getRandomSelect(this, table));
// }
// for (int i = 0; i < 10; i++) {
// Table table = randomTable();
// add(Command.getRandomUpdate(this, table));
// }
// for (int i = 0; i < 30; i++) {
// Table table = randomTable();
// add(Command.getRandomSelect(this, table));
// }
// for (int i = 0; i < 50; i++) {
// Table table = randomTable();
// add(Command.getRandomDelete(this, table));
// }
// for (int i = 0; i < 10; i++) {
// Table table = randomTable();
// add(Command.getRandomSelect(this, table));
// }
// while(true) {
// Table table = randomTable();
// if(table == null) {
// break;
// }
// add(Command.getDropTable(this, table));
// }
add(Command.getDisconnect(this));
add(Command.getEnd(this));
for (int i = 0; i < commands.size(); i++) {
Command command = commands.get(i);
boolean stop = process(seed, i, command);
if (stop) {
break;
}
}
}
private boolean process(int seed, int id, Command command) throws Exception {
try {
ArrayList<Result> results = New.arrayList();
for (int i = 0; i < databases.size(); i++) {
DbInterface db = databases.get(i);
Result result = command.run(db);
results.add(result);
if (showError && i == 0) {
// result.log();
}
}
compareResults(results);
} catch (Error e) {
if (showError) {
TestBase.logError("synth", e);
}
System.out.println("new TestSynth().init(test).testCase(" + seed + "); // id=" + id + " " + e.toString());
if (stopImmediately) {
System.exit(0);
}
return true;
}
return false;
}
private void compareResults(ArrayList<Result> results) {
Result original = results.get(0);
for (int i = 1; i < results.size(); i++) {
Result copy = results.get(i);
if (original.compareTo(copy) != 0) {
if (showError) {
throw new AssertionError("Results don't match: original (0): \r\n" + original + "\r\n" + "other:\r\n" + copy);
}
throw new AssertionError("Results don't match");
}
}
}
/**
* Get a random table.
*
* @return the table
*/
Table randomTable() {
return dbState.randomTable();
}
/**
* Print this message if the log is enabled.
*
* @param id the id
* @param s the message
*/
void log(int id, String s) {
if (showLog && id == 0) {
System.out.println(s);
}
}
int getMode() {
return mode;
}
private void addDatabase(String className, String url, String user, String password, boolean useSentinel) {
DbConnection db = new DbConnection(this, className, url, user, password, databases.size(), useSentinel);
databases.add(db);
}
public TestBase init(TestAll conf) throws Exception {
super.init(conf);
deleteDb("synth/synth");
databases = New.arrayList();
// mode = HSQLDB;
// addDatabase("org.hsqldb.jdbcDriver", "jdbc:hsqldb:test", "sa", "" );
// addDatabase("org.h2.Driver", "jdbc:h2:synth;mode=hsqldb", "sa", "");
// mode = POSTGRESQL;
// addDatabase("org.postgresql.Driver", "jdbc:postgresql:test", "sa",
// "sa");
// addDatabase("org.h2.Driver", "jdbc:h2:synth;mode=postgresql", "sa",
// "");
mode = H2_MEM;
org.h2.Driver.load();
addDatabase("org.h2.Driver", "jdbc:h2:mem:synth", "sa", "", true);
addDatabase("org.h2.Driver", "jdbc:h2:" + getBaseDir() + "/synth/synth", "sa", "", false);
// addDatabase("com.mysql.jdbc.Driver", "jdbc:mysql://localhost/test",
// "sa", "");
// addDatabase("org.h2.Driver", "jdbc:h2:synth;mode=mysql", "sa", "");
// addDatabase("com.mysql.jdbc.Driver", "jdbc:mysql://localhost/test",
// "sa", "");
// addDatabase("org.ldbc.jdbc.jdbcDriver",
// "jdbc:ldbc:mysql://localhost/test", "sa", "");
// addDatabase("org.h2.Driver", "jdbc:h2:memFS:synth", "sa", "");
// MySQL: NOT is bound to column: NOT ID = 1 means (NOT ID) = 1 instead
// of NOT (ID=1)
for (int i = 0; i < databases.size(); i++) {
DbConnection conn = (DbConnection) databases.get(i);
System.out.println(i + " = " + conn.toString());
}
showError = true;
showLog = false;
// stopImmediately = true;
// showLog = true;
// testRun(110600); // id=27 java.lang.Error: results don't match:
// original (0):
// System.exit(0);
return this;
}
public void testCase(int seed) throws Exception {
deleteDb("synth/synth");
try {
printTime("TestSynth " + seed);
testRun(seed);
} catch (Error e) {
TestBase.logError("error", e);
System.exit(0);
}
}
public void test() throws Exception {
while (true) {
int seed = MathUtils.randomInt(Integer.MAX_VALUE);
testCase(seed);
}
}
}