added test-framework
This commit is contained in:
parent
5997d9b4a1
commit
49d3b8c14f
|
@ -0,0 +1,11 @@
|
||||||
|
#!/bin/sh
|
||||||
|
make clean -C src/
|
||||||
|
make analysis -C src/
|
||||||
|
|
||||||
|
echo "\n\n"
|
||||||
|
|
||||||
|
./bin/analysis "SELECT * FROM t1 UNION SELECT abc AS t FROM t2 ORDER BY col3 LIMIT 10;"
|
||||||
|
./bin/analysis "INSERT INTO students (name, city, age) VALUES ('Max', 'Musterhausen', 5);"
|
||||||
|
./bin/analysis "INSERT INTO students (name, city) SELECT * FROM employees;"
|
||||||
|
|
||||||
|
echo "\n\n"
|
27
run_tests.sh
27
run_tests.sh
|
@ -1,23 +1,6 @@
|
||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
|
echo "Compiling..."
|
||||||
|
make clean -C src/ >/dev/null
|
||||||
make clean -C src/
|
make tests -C src/ >/dev/null
|
||||||
|
echo "Running tests:"
|
||||||
# make tests
|
./bin/tests
|
||||||
make analysis -C src/
|
|
||||||
make grammar_test -C src/
|
|
||||||
|
|
||||||
echo "\n\n"
|
|
||||||
|
|
||||||
|
|
||||||
# ./bin/analysis "SELECT t1.a AS id, t1.b, t2.c FROM \"tbl\" AS t1 JOIN (SELECT * FROM foo JOIN bar ON foo.id = bar.id) t2 ON t1.a = t2.b WHERE (t1.b OR NOT t1.a) AND t2.c = 12.5"
|
|
||||||
# ./bin/analysis "CREATE TABLE \"table\" FROM TBL FILE 'students.tbl'"
|
|
||||||
./bin/analysis "SELECT * FROM t1 UNION SELECT abc AS t FROM t2 ORDER BY col3 LIMIT 10;"
|
|
||||||
./bin/analysis "INSERT INTO students (name, city, age) VALUES ('Max', 'Musterhausen', 5);"
|
|
||||||
./bin/analysis "INSERT INTO students (name, city) SELECT * FROM employees;"
|
|
||||||
|
|
||||||
echo "\n\n"
|
|
||||||
|
|
||||||
./bin/grammar_test -f "test/valid_queries.sql"
|
|
||||||
|
|
||||||
echo "\n\n"
|
|
|
@ -33,6 +33,6 @@ parser/bison_parser.cpp:
|
||||||
|
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -f *.o *~ $(BIN_DIR)/analysis $(TESTS_BIN) $(BIN_DIR)/grammar_test
|
rm -f *.o *~ $(BIN_DIR)/analysis $(TESTS_BIN) $(BIN_DIR)/grammar_test $(BIN_DIR)/tests
|
||||||
rm -rf $(BUILD_DIR)
|
rm -rf $(BUILD_DIR)
|
||||||
make clean -C parser/
|
make clean -C parser/
|
||||||
|
|
|
@ -86,9 +86,9 @@ int main(int argc, char *argv[]) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (num_failed == 0) {
|
if (num_failed == 0) {
|
||||||
printf("All %lu grammar tests completed successfully!", queries.size());
|
printf("All %lu grammar tests completed successfully!\n", queries.size());
|
||||||
} else {
|
} else {
|
||||||
fprintf(stderr, "Some grammar tests failed! %d out of %lu tests failed!", num_failed, queries.size());
|
fprintf(stderr, "Some grammar tests failed! %d out of %lu tests failed!\n", num_failed, queries.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -3,159 +3,36 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "SQLParser.h"
|
#include "SQLParser.h"
|
||||||
#include <stdio.h>
|
#include "tests/test.h"
|
||||||
#include <string>
|
|
||||||
#include <cassert>
|
|
||||||
#include <thread>
|
|
||||||
#include <time.h>
|
|
||||||
|
|
||||||
#define STREQUALS(str1, str2) std::string(str1).compare(std::string(str2)) == 0
|
|
||||||
|
|
||||||
#define ASSERT(cond) if (!(cond)) { fprintf(stderr, "failed! Assertion (" #cond ")\n"); return; }
|
|
||||||
#define ASSERT_STR(STR1, STR2) ASSERT(STREQUALS(STR1, STR2));
|
|
||||||
|
|
||||||
|
|
||||||
void SelectTest1() {
|
using namespace hsql;
|
||||||
printf("Test: SelectTest1... "); fflush(stdout);
|
|
||||||
|
|
||||||
const char* sql = "SELECT age, (name), address from table WHERE age < 12.5;";
|
|
||||||
Statement* sqlStatement = SQLParser::parseSQLString(sql);
|
|
||||||
ASSERT(sqlStatement != NULL);
|
|
||||||
ASSERT(sqlStatement->type == eSelect);
|
|
||||||
|
|
||||||
SelectStatement* stmt = (SelectStatement*) sqlStatement;
|
|
||||||
|
|
||||||
ASSERT(stmt->select_list->size() == 3);
|
|
||||||
ASSERT_STR(stmt->select_list->at(0)->name, "age");
|
|
||||||
ASSERT_STR(stmt->select_list->at(1)->name, "name");
|
|
||||||
ASSERT_STR(stmt->select_list->at(2)->name, "address");
|
|
||||||
|
|
||||||
ASSERT(stmt->from_table != NULL);
|
|
||||||
ASSERT(stmt->from_table->type == eTableName);
|
|
||||||
ASSERT_STR(stmt->from_table->name, "table");
|
|
||||||
|
|
||||||
// WHERE
|
|
||||||
ASSERT(stmt->where_clause != NULL);
|
|
||||||
ASSERT(stmt->where_clause->expr->type == eExprColumnRef);
|
|
||||||
ASSERT_STR(stmt->where_clause->expr->name, "age");
|
|
||||||
ASSERT(stmt->where_clause->pred_type == SQL_LESS);
|
|
||||||
ASSERT(stmt->where_clause->expr2->type == eExprLiteralFloat);
|
|
||||||
ASSERT(stmt->where_clause->expr2->float_literal == 12.5);
|
|
||||||
|
|
||||||
printf("passed!\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
void SelectTest2() {
|
|
||||||
printf("Test: SelectTest2... "); fflush(stdout);
|
|
||||||
|
|
||||||
const char* sql = "SELECT * FROM (SELECT age+zipcode FROM table, table2);";
|
|
||||||
Statement* stmt = SQLParser::parseSQLString(sql);
|
|
||||||
ASSERT(stmt != NULL);
|
|
||||||
ASSERT(stmt->type == eSelect);
|
|
||||||
|
|
||||||
SelectStatement* select = (SelectStatement*) stmt;
|
|
||||||
|
|
||||||
ASSERT(select->select_list->size() == 1);
|
|
||||||
ASSERT(select->select_list->at(0)->type == eExprStar);
|
|
||||||
|
|
||||||
ASSERT(select->from_table != NULL);
|
|
||||||
ASSERT(select->from_table->type == eTableSelect);
|
|
||||||
ASSERT(select->from_table->stmt != NULL);
|
|
||||||
ASSERT(select->from_table->stmt->select_list->size() == 1);
|
|
||||||
ASSERT(select->from_table->stmt->from_table->type == eTableCrossProduct);
|
|
||||||
// ASSERT_STR(select->from_table->stmt->from_table->table_names->at(0), "table");
|
|
||||||
// ASSERT_STR(select->from_table->stmt->from_table->table_names->at(1), "table2");
|
|
||||||
|
|
||||||
printf("passed!\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
uint parse_count = 0;
|
|
||||||
uint conflicts = 0;
|
|
||||||
void SelectTest3(bool print) {
|
|
||||||
if (print) { printf("Test: SelectTest3... "); fflush(stdout); }
|
|
||||||
|
|
||||||
const char* sql = "SELECT name, AVG(age) FROM table GROUP BY name";
|
|
||||||
parse_count++;
|
|
||||||
|
|
||||||
Statement* stmt = SQLParser::parseSQLString(sql);
|
|
||||||
|
|
||||||
if (parse_count != 1) conflicts++;
|
|
||||||
parse_count--;
|
|
||||||
|
|
||||||
ASSERT(stmt != NULL);
|
|
||||||
ASSERT(stmt->type == eSelect);
|
|
||||||
|
|
||||||
SelectStatement* select = (SelectStatement*) stmt;
|
|
||||||
|
|
||||||
ASSERT(select->select_list->size() == 2);
|
|
||||||
|
|
||||||
ASSERT(select->select_list->at(0)->type == eExprColumnRef);
|
|
||||||
ASSERT(select->select_list->at(1)->type == eExprFunctionRef);
|
|
||||||
ASSERT_STR("name", select->select_list->at(0)->name);
|
|
||||||
|
|
||||||
|
|
||||||
ASSERT(select->group_by != NULL);
|
|
||||||
ASSERT(select->group_by->size() == 1);
|
|
||||||
ASSERT_STR("name", select->group_by->at(0)->name);
|
|
||||||
|
|
||||||
if (print) printf("passed!\n");
|
TEST(SelectTest) {
|
||||||
|
StatementList* stmt_list = SQLParser::parseSQLString("SELECT * FROM students;");
|
||||||
|
ASSERT(stmt_list->isValid);
|
||||||
|
ASSERT(stmt_list->size() == 1);
|
||||||
|
ASSERT(stmt_list->at(0)->type == kStmtSelect);
|
||||||
|
|
||||||
|
SelectStatement* stmt = (SelectStatement*) stmt_list->at(0);
|
||||||
|
ASSERT(stmt->where_clause == NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/** Multithread Test **/
|
TEST(DeleteTest) {
|
||||||
void multithreadTest(int numberOfRuns, int id) {
|
StatementList* stmt_list = SQLParser::parseSQLString("DELETE FROM students WHERE grade > 2.0;");
|
||||||
for (int n = 0; n < numberOfRuns; ++n) {
|
ASSERT(stmt_list->isValid);
|
||||||
SelectTest3(false);
|
ASSERT(stmt_list->size() == 1);
|
||||||
}
|
ASSERT(stmt_list->at(0)->type == kStmtDelete);
|
||||||
}
|
|
||||||
void ThreadSafeTest(uint numThreads, uint runsPerThread) {
|
DeleteStatement* stmt = (DeleteStatement*) stmt_list->at(0);
|
||||||
printf("Multithread-Test... ");
|
ASSERT_STREQ(stmt->table_name, "students");
|
||||||
conflicts = 0;
|
ASSERT(stmt->expr != NULL);
|
||||||
std::thread* threads = new std::thread[numThreads];
|
ASSERT(stmt->expr->isType(kExprOperator));
|
||||||
for (int n = 0; n < numThreads; ++n) {
|
ASSERT_STREQ(stmt->expr->expr->name, "grade");
|
||||||
threads[n] = std::thread(multithreadTest, runsPerThread, n);
|
ASSERT_EQ(stmt->expr->expr2->fval, 2.0);
|
||||||
}
|
|
||||||
for (int n = 0; n < numThreads; ++n) {
|
|
||||||
threads[n].join();
|
|
||||||
}
|
|
||||||
printf("there were %u concurrent parses... ", conflicts);
|
|
||||||
printf("finished!\n");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/** Performance Test **/
|
|
||||||
void Benchmark1(uint numRuns) {
|
|
||||||
printf("Benchmarking... ");
|
|
||||||
|
|
||||||
clock_t start, end;
|
|
||||||
const char* sql = "SELECT SUM(age), name, address FROM (SELECT age FROM (SELECT age FROM (SELECT age FROM table, table2))) WHERE income > 10 GROUP BY age;";
|
|
||||||
|
|
||||||
start = clock();
|
|
||||||
for (uint n = 0; n < numRuns; ++n) {
|
|
||||||
Statement* stmt = SQLParser::parseSQLString(sql);
|
|
||||||
}
|
|
||||||
end = clock();
|
|
||||||
|
|
||||||
long diff = end-start;
|
|
||||||
printf("Total Time: %ld ticks (~%.4fms)\n", diff, 1000.0*diff/CLOCKS_PER_SEC);
|
|
||||||
printf("Time per exec: ~%.2f ticks (~%.4fms)\n", (double)diff/numRuns, (1000.0*diff/numRuns)/CLOCKS_PER_SEC);
|
|
||||||
printf("Benchmarking done!\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
|
||||||
|
|
||||||
printf("\n######################################\n");
|
|
||||||
printf("## Running all tests...\n\n");
|
|
||||||
|
|
||||||
SelectTest1();
|
|
||||||
SelectTest2();
|
|
||||||
SelectTest3(true);
|
|
||||||
ThreadSafeTest(10, 1000);
|
|
||||||
Benchmark1(10000);
|
|
||||||
|
|
||||||
printf("\n## Finished running all tests...\n");
|
|
||||||
printf("######################################\n");
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
#ifndef __HELPER_H__
|
||||||
|
#define __HELPER_H__
|
||||||
|
|
||||||
|
|
||||||
|
std::vector<std::string> readlines(std::string path) {
|
||||||
|
std::ifstream infile(path);
|
||||||
|
std::vector<std::string> lines;
|
||||||
|
std::string line;
|
||||||
|
while (std::getline(infile, line)) {
|
||||||
|
std::istringstream iss(line);
|
||||||
|
|
||||||
|
// Skip comments
|
||||||
|
if (line[0] != '#') {
|
||||||
|
lines.push_back(line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return lines;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,69 @@
|
||||||
|
#ifndef __TEST_H__
|
||||||
|
#define __TEST_H__
|
||||||
|
|
||||||
|
#define TEST(name) \
|
||||||
|
void name(); \
|
||||||
|
namespace g_dummy { int _##name = AddTest(name, #name); } \
|
||||||
|
void name()
|
||||||
|
|
||||||
|
#define ASSERT(cond) if (!(cond)) throw AssertionFailedException(#cond);
|
||||||
|
#define ASSERT_TRUE(cond) ASSERT(cond);
|
||||||
|
#define ASSERT_FALSE(cond) if (cond) throw AssertionFailedException(#cond);
|
||||||
|
#define ASSERT_STREQ(a, b) \
|
||||||
|
if (std::string(a).compare(std::string(b)) != 0) throw AssertionFailedException(#a " == " #b)
|
||||||
|
#define ASSERT_EQ(a, b) \
|
||||||
|
ASSERT(a == b);
|
||||||
|
|
||||||
|
class AssertionFailedException: public std::exception {
|
||||||
|
public:
|
||||||
|
AssertionFailedException(std::string msg) :
|
||||||
|
std::exception(),
|
||||||
|
_msg(msg) {};
|
||||||
|
|
||||||
|
virtual const char* what() const throw() {
|
||||||
|
return _msg.c_str();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
std::string _msg;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
std::vector<std::string> g_test_names;
|
||||||
|
std::vector<void (*)(void)> g_tests;
|
||||||
|
|
||||||
|
int AddTest(void (*foo)(void), std::string name) {
|
||||||
|
g_tests.push_back(foo);
|
||||||
|
g_test_names.push_back(name);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void RunTests() {
|
||||||
|
size_t num_failed = 0;
|
||||||
|
for (size_t i = 0; i < g_tests.size(); ++i) {
|
||||||
|
printf("\033[0;32m{ running}\033[0m %s\n", g_test_names[i].c_str());
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Run test
|
||||||
|
(*g_tests[i])();
|
||||||
|
printf("\033[0;32m{ ok}\033[0m %s\n", g_test_names[i].c_str());
|
||||||
|
|
||||||
|
} catch (AssertionFailedException& e) {
|
||||||
|
printf("\033[1;31m{ failed} %s\n", g_test_names[i].c_str());
|
||||||
|
printf("\tAssertion failed: %s\n\033[0m", e.what());
|
||||||
|
num_failed++;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
RunTests();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,26 @@
|
||||||
|
# SELECT statement
|
||||||
|
SELECT * FROM orders;
|
||||||
|
SELECT a FROM foo WHERE a > 12 OR b > 3 AND NOT c LIMIT 10
|
||||||
|
SELECT col1 AS myname, col2, 'test' FROM "table", foo AS t WHERE age > 12 AND zipcode = 12345 GROUP BY col1;
|
||||||
|
SELECT * from "table" JOIN table2 ON a = b WHERE (b OR NOT a) AND a = 12.5
|
||||||
|
(SELECT a FROM foo WHERE a > 12 OR b > 3 AND c NOT LIKE 's%' LIMIT 10);
|
||||||
|
SELECT * FROM "table" LIMIT 10 OFFSET 10;
|
||||||
|
SELECT * FROM t1 UNION SELECT * FROM t2 ORDER BY col1;
|
||||||
|
# SELECT * FROM t1 UNION (SELECT * FROM t2 UNION SELECT * FROM t3) ORDER BY col1;
|
||||||
|
# JOIN
|
||||||
|
SELECT t1.a, t1.b, t2.c FROM "table" AS t1 JOIN (SELECT * FROM foo JOIN bar ON foo.id = bar.id) t2 ON t1.a = t2.b WHERE (t1.b OR NOT t1.a) AND t2.c = 12.5
|
||||||
|
SELECT * FROM t1 JOIN t2 ON c1 = c2;
|
||||||
|
# CREATE statement
|
||||||
|
CREATE TABLE "table" FROM TBL FILE 'students.tbl'
|
||||||
|
CREATE TABLE IF NOT EXISTS "table" FROM TBL FILE 'students.tbl'
|
||||||
|
CREATE TABLE students (name TEXT, student_number INTEGER, city TEXT, grade DOUBLE)
|
||||||
|
# Multiple statements
|
||||||
|
CREATE TABLE "table" FROM TBL FILE 'students.tbl'; SELECT * FROM "table";
|
||||||
|
# INSERT
|
||||||
|
INSERT INTO test_table VALUES (1, 2, 'test');
|
||||||
|
INSERT INTO test_table (id, value, name) VALUES (1, 2, 'test');
|
||||||
|
INSERT INTO test_table SELECT * FROM students;
|
||||||
|
# DELETE
|
||||||
|
DELETE FROM students WHERE grade > 3.0
|
||||||
|
DELETE FROM students
|
||||||
|
TRUNCATE students
|
Loading…
Reference in New Issue