refactored test framework
This commit is contained in:
parent
4d33ea21bf
commit
48b473e7c5
|
@ -14,6 +14,10 @@ To create the full parser code run `make build`. The parser library code is crea
|
||||||
|
|
||||||
To use the SQL Parser in your own code, you only need to include `SQLParser.h` and build+link all the source files from the parser with your project. See `hyrise/src/lib/access/sql/SQLQueryParser.cpp` for how it's used in Hyrise.
|
To use the SQL Parser in your own code, you only need to include `SQLParser.h` and build+link all the source files from the parser with your project. See `hyrise/src/lib/access/sql/SQLQueryParser.cpp` for how it's used in Hyrise.
|
||||||
|
|
||||||
|
### Run tests
|
||||||
|
|
||||||
|
To execute all tests run: `./run_tests.sh`.
|
||||||
|
|
||||||
### Update in Hyrise
|
### Update in Hyrise
|
||||||
|
|
||||||
Run `./deploy_to_hyris.sh path/to/hyrise` to update the SQL parser within Hyrise.
|
Run `./deploy_to_hyris.sh path/to/hyrise` to update the SQL parser within Hyrise.
|
||||||
|
|
|
@ -177,7 +177,7 @@ $(function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
addBuggyQuery('GROUP', 'SELECT AVG(grade) FROM (SELECT city, AVG(grade) FROM students GROUP BY city) t1');
|
addBuggyQuery('GROUP', 'SELECT AVG(grade) FROM (SELECT city, AVG(grade) FROM students GROUP BY city) t1');
|
||||||
addBuggyQuery('UNION', 'SELECT name FROM students WHERE grade > 2.0 UNION SELECT name FROM students');
|
addBuggyQuery('UNION (kills hyrise)', 'SELECT name FROM students WHERE grade > 2.0 UNION SELECT name FROM students');
|
||||||
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////
|
//////////////////////////////////////////////////
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
# Load Tables
|
# Load Tables
|
||||||
CREATE TABLE IF NOT EXISTS students FROM TBL FILE 'test/students.tbl';
|
CREATE TABLE IF NOT EXISTS students FROM TBL FILE 'test/students.tbl';
|
||||||
CREATE TABLE IF NOT EXISTS test FROM TBL FILE 'test/lin_xxs.tbl';
|
CREATE TABLE IF NOT EXISTS test_big FROM TBL FILE 'test/lin_xxs.tbl';
|
||||||
CREATE TABLE IF NOT EXISTS companies FROM TBL FILE 'test/tables/companies.tbl';
|
CREATE TABLE IF NOT EXISTS companies FROM TBL FILE 'test/tables/companies.tbl';
|
||||||
CREATE TABLE IF NOT EXISTS employees FROM TBL FILE 'test/tables/employees.tbl';
|
CREATE TABLE IF NOT EXISTS employees FROM TBL FILE 'test/tables/employees.tbl';
|
||||||
# SELECT
|
# SELECT
|
||||||
|
@ -14,4 +14,15 @@ SELECT * FROM students WHERE grade = 1.3 UNION SELECT * FROM students WHERE grad
|
||||||
# JOIN
|
# JOIN
|
||||||
SELECT * FROM companies JOIN employees ON company_id = employee_company_id;
|
SELECT * FROM companies JOIN employees ON company_id = employee_company_id;
|
||||||
# INSERT
|
# INSERT
|
||||||
INSERT INTO students VALUES ('Max', 10101, 'Musterhausen', 3.3);
|
INSERT INTO students VALUES ('Max Mustermann', 10101, 'Musterhausen', 1.7);
|
||||||
|
# CREATE
|
||||||
|
CREATE TABLE IF NOT EXISTS test (v1 INTEGER, v2 INTEGER, v3 INTEGER);
|
||||||
|
# INSERT/SELECT
|
||||||
|
INSERT INTO test VALUES (1, 23, 45);|
|
||||||
|
INSERT INTO test VALUES (1, 23, 45);
|
||||||
|
INSERT INTO test VALUES (1, 23, 45);
|
||||||
|
SELECT * FROM test;
|
||||||
|
# CREATE/INSERT/SELECT
|
||||||
|
CREATE TABLE IF NOT EXISTS test (v1 INTEGER, v2 INTEGER, v3 INTEGER);
|
||||||
|
INSERT INTO test VALUES (1, 12, 43);
|
||||||
|
SELECT * FROM test;
|
|
@ -1,8 +1,9 @@
|
||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
echo "Compiling..."
|
echo "Compiling..."
|
||||||
make clean -C src/ >/dev/null
|
make clean -C src/ >/dev/null || exit 1
|
||||||
make tests -C src/ >/dev/null
|
make tests -C src/ >/dev/null || exit 1
|
||||||
make grammar_test -C src/ >/dev/null
|
make grammar_test -C src/ >/dev/null || exit 1
|
||||||
|
|
||||||
echo "Running tests:"
|
echo "Running tests:"
|
||||||
./bin/grammar_test -f "test/valid_queries.sql"
|
./bin/grammar_test -f "test/valid_queries.sql"
|
||||||
./bin/tests
|
./bin/tests
|
||||||
|
|
22
src/Makefile
22
src/Makefile
|
@ -1,10 +1,14 @@
|
||||||
|
|
||||||
|
|
||||||
|
BUILD_DIR = ../build/
|
||||||
|
BIN_DIR = ../bin
|
||||||
|
|
||||||
CC = g++
|
CC = g++
|
||||||
CFLAGS = -O3 -Ilib/ -I./ -Iparser/ -std=c++11 -pthread -Wall -g
|
CFLAGS = -O3 -Ilib/ -I./ -Iparser/ -std=c++11 -pthread -Wall -g
|
||||||
SOURCES = $(shell find lib/ -name '*.cpp') parser/bison_parser.cpp parser/flex_lexer.cpp parser/SQLParser.cpp
|
|
||||||
BUILD_DIR = ../build/
|
LIB_SOURCES = $(shell find lib/ -name '*.cpp') parser/bison_parser.cpp parser/flex_lexer.cpp parser/SQLParser.cpp
|
||||||
BIN_DIR = ../bin
|
TEST_SOURCES = $(shell find tests/ -name '*.cpp')
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# release build is always using bison
|
# release build is always using bison
|
||||||
|
@ -16,16 +20,16 @@ build: clean
|
||||||
cp parser/*.cpp $(BUILD_DIR)
|
cp parser/*.cpp $(BUILD_DIR)
|
||||||
|
|
||||||
|
|
||||||
analysis: $(SOURCES) sql_analysis.cpp
|
analysis: $(LIB_SOURCES) sql_analysis.cpp
|
||||||
$(CC) $(CFLAGS) $(SOURCES) sql_analysis.cpp -o $(BIN_DIR)/analysis
|
$(CC) $(CFLAGS) $(LIB_SOURCES) sql_analysis.cpp -o $(BIN_DIR)/analysis
|
||||||
|
|
||||||
|
|
||||||
grammar_test: $(SOURCES) sql_grammar_test.cpp
|
grammar_test: $(LIB_SOURCES) sql_grammar_test.cpp
|
||||||
$(CC) $(CFLAGS) $(SOURCES) sql_grammar_test.cpp -o $(BIN_DIR)/grammar_test
|
$(CC) $(CFLAGS) $(LIB_SOURCES) sql_grammar_test.cpp -o $(BIN_DIR)/grammar_test
|
||||||
|
|
||||||
|
|
||||||
tests: $(SOURCES) $(TESTS_MAIN)
|
tests: $(LIB_SOURCES) $(TEST_SOURCES) sql_tests.cpp
|
||||||
$(CC) $(CFLAGS) $(SOURCES) sql_tests.cpp -o $(BIN_DIR)/tests
|
$(CC) $(CFLAGS) $(LIB_SOURCES) $(TEST_SOURCES) sql_tests.cpp -o $(BIN_DIR)/tests
|
||||||
|
|
||||||
|
|
||||||
parser/bison_parser.cpp:
|
parser/bison_parser.cpp:
|
||||||
|
|
|
@ -108,8 +108,8 @@ typedef void* yyscan_t;
|
||||||
** Token Definition
|
** Token Definition
|
||||||
*********************************/
|
*********************************/
|
||||||
%token <sval> IDENTIFIER STRING
|
%token <sval> IDENTIFIER STRING
|
||||||
%token <fval> FLOAT
|
%token <fval> FLOATVAL
|
||||||
%token <ival> INT
|
%token <ival> INTVAL
|
||||||
%token <uval> NOTEQUALS LESSEQ GREATEREQ
|
%token <uval> NOTEQUALS LESSEQ GREATEREQ
|
||||||
|
|
||||||
/* SQL Keywords */
|
/* SQL Keywords */
|
||||||
|
@ -124,7 +124,7 @@ typedef void* yyscan_t;
|
||||||
%token LIMIT LOCAL MERGE MINUS ORDER OUTER RIGHT TABLE UNION
|
%token LIMIT LOCAL MERGE MINUS ORDER OUTER RIGHT TABLE UNION
|
||||||
%token USING WHERE CALL DATE DESC DROP FILE FROM FULL HASH
|
%token USING WHERE CALL DATE DESC DROP FILE FROM FULL HASH
|
||||||
%token INTO JOIN LEFT LIKE LOAD NULL PART PLAN SHOW TEXT
|
%token INTO JOIN LEFT LIKE LOAD NULL PART PLAN SHOW TEXT
|
||||||
%token TIME VIEW WITH ADD ALL AND ASC CSV FOR KEY NOT OFF
|
%token TIME VIEW WITH ADD ALL AND ASC CSV FOR INT KEY NOT OFF
|
||||||
%token SET TBL TOP AS BY IF IN IS OF ON OR TO
|
%token SET TBL TOP AS BY IF IN IS OF ON OR TO
|
||||||
|
|
||||||
|
|
||||||
|
@ -268,8 +268,10 @@ column_def:
|
||||||
}
|
}
|
||||||
;
|
;
|
||||||
|
|
||||||
|
|
||||||
column_type:
|
column_type:
|
||||||
INTEGER { $$ = ColumnDefinition::INT; }
|
INT { $$ = ColumnDefinition::INT; }
|
||||||
|
| INTEGER { $$ = ColumnDefinition::INT; }
|
||||||
| DOUBLE { $$ = ColumnDefinition::DOUBLE; }
|
| DOUBLE { $$ = ColumnDefinition::DOUBLE; }
|
||||||
| TEXT { $$ = ColumnDefinition::TEXT; }
|
| TEXT { $$ = ColumnDefinition::TEXT; }
|
||||||
;
|
;
|
||||||
|
@ -522,12 +524,12 @@ string_literal:
|
||||||
|
|
||||||
|
|
||||||
num_literal:
|
num_literal:
|
||||||
FLOAT { $$ = Expr::makeLiteral($1); }
|
FLOATVAL { $$ = Expr::makeLiteral($1); }
|
||||||
| int_literal
|
| int_literal
|
||||||
;
|
;
|
||||||
|
|
||||||
int_literal:
|
int_literal:
|
||||||
INT { $$ = Expr::makeLiteral($1); }
|
INTVAL { $$ = Expr::makeLiteral($1); }
|
||||||
;
|
;
|
||||||
|
|
||||||
star_expr:
|
star_expr:
|
||||||
|
|
|
@ -149,6 +149,7 @@ AND TOKEN(AND)
|
||||||
ASC TOKEN(ASC)
|
ASC TOKEN(ASC)
|
||||||
CSV TOKEN(CSV)
|
CSV TOKEN(CSV)
|
||||||
FOR TOKEN(FOR)
|
FOR TOKEN(FOR)
|
||||||
|
INT TOKEN(INT)
|
||||||
KEY TOKEN(KEY)
|
KEY TOKEN(KEY)
|
||||||
NOT TOKEN(NOT)
|
NOT TOKEN(NOT)
|
||||||
OFF TOKEN(OFF)
|
OFF TOKEN(OFF)
|
||||||
|
@ -177,12 +178,12 @@ TO TOKEN(TO)
|
||||||
[0-9]+"."[0-9]* |
|
[0-9]+"."[0-9]* |
|
||||||
"."[0-9]* {
|
"."[0-9]* {
|
||||||
yylval->fval = atof(yytext);
|
yylval->fval = atof(yytext);
|
||||||
return SQL_FLOAT;
|
return SQL_FLOATVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
[0-9]+ {
|
[0-9]+ {
|
||||||
yylval->ival = atol(yytext);
|
yylval->ival = atol(yytext);
|
||||||
return SQL_INT;
|
return SQL_INTVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
\"[^\"\n]+\" {
|
\"[^\"\n]+\" {
|
||||||
|
|
|
@ -147,6 +147,7 @@ DATE
|
||||||
TIME
|
TIME
|
||||||
TIMESTAMP
|
TIMESTAMP
|
||||||
INTEGER
|
INTEGER
|
||||||
|
INT
|
||||||
DOUBLE
|
DOUBLE
|
||||||
NVARCHAR
|
NVARCHAR
|
||||||
TEXT
|
TEXT
|
||||||
|
|
|
@ -2,27 +2,12 @@
|
||||||
* sql_tests.cpp
|
* sql_tests.cpp
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include "tests/test.h"
|
||||||
#include "SQLParser.h"
|
#include "SQLParser.h"
|
||||||
#include "sqlhelper.h"
|
#include "sqlhelper.h"
|
||||||
#include "tests/test.h"
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
using namespace hsql;
|
using namespace hsql;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
TEST(Select) {
|
|
||||||
StatementList* stmt_list = SQLParser::parseSQLString("SELECT * FROM students;");
|
|
||||||
ASSERT(stmt_list->isValid);
|
|
||||||
ASSERT_EQ(stmt_list->size(), 1);
|
|
||||||
ASSERT(stmt_list->at(0)->type == kStmtSelect);
|
|
||||||
|
|
||||||
SelectStatement* stmt = (SelectStatement*) stmt_list->at(0);
|
|
||||||
ASSERT_NULL(stmt->where_clause);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
TEST(Delete) {
|
TEST(Delete) {
|
||||||
StatementList* stmt_list = SQLParser::parseSQLString("DELETE FROM students WHERE grade > 2.0;");
|
StatementList* stmt_list = SQLParser::parseSQLString("DELETE FROM students WHERE grade > 2.0;");
|
||||||
ASSERT(stmt_list->isValid);
|
ASSERT(stmt_list->isValid);
|
||||||
|
@ -38,7 +23,7 @@ TEST(Delete) {
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(Create) {
|
TEST(Create) {
|
||||||
StatementList* stmt_list = SQLParser::parseSQLString("CREATE TABLE students (name TEXT, student_number INTEGER, city TEXT, grade DOUBLE)");
|
StatementList* stmt_list = SQLParser::parseSQLString("CREATE TABLE students (name TEXT, student_number INT, city INTEGER, grade DOUBLE)");
|
||||||
ASSERT(stmt_list->isValid);
|
ASSERT(stmt_list->isValid);
|
||||||
ASSERT_EQ(stmt_list->size(), 1);
|
ASSERT_EQ(stmt_list->size(), 1);
|
||||||
ASSERT_EQ(stmt_list->at(0)->type, kStmtCreate);
|
ASSERT_EQ(stmt_list->at(0)->type, kStmtCreate);
|
||||||
|
@ -49,8 +34,12 @@ TEST(Create) {
|
||||||
ASSERT_NOTNULL(stmt->columns);
|
ASSERT_NOTNULL(stmt->columns);
|
||||||
ASSERT_EQ(stmt->columns->size(), 4);
|
ASSERT_EQ(stmt->columns->size(), 4);
|
||||||
ASSERT_STREQ(stmt->columns->at(0)->name, "name");
|
ASSERT_STREQ(stmt->columns->at(0)->name, "name");
|
||||||
ASSERT_EQ(stmt->columns->at(0)->type, ColumnDefinition::TEXT);
|
ASSERT_STREQ(stmt->columns->at(1)->name, "student_number");
|
||||||
|
ASSERT_STREQ(stmt->columns->at(2)->name, "city");
|
||||||
ASSERT_STREQ(stmt->columns->at(3)->name, "grade");
|
ASSERT_STREQ(stmt->columns->at(3)->name, "grade");
|
||||||
|
ASSERT_EQ(stmt->columns->at(0)->type, ColumnDefinition::TEXT);
|
||||||
|
ASSERT_EQ(stmt->columns->at(1)->type, ColumnDefinition::INT);
|
||||||
|
ASSERT_EQ(stmt->columns->at(2)->type, ColumnDefinition::INT);
|
||||||
ASSERT_EQ(stmt->columns->at(3)->type, ColumnDefinition::DOUBLE);
|
ASSERT_EQ(stmt->columns->at(3)->type, ColumnDefinition::DOUBLE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
|
||||||
|
#include "test.h"
|
||||||
|
#include "SQLParser.h"
|
||||||
|
|
||||||
|
using namespace hsql;
|
||||||
|
|
||||||
|
TEST(Select) {
|
||||||
|
StatementList* stmt_list = SQLParser::parseSQLString("SELECT * FROM students;");
|
||||||
|
ASSERT(stmt_list->isValid);
|
||||||
|
ASSERT_EQ(stmt_list->size(), 1);
|
||||||
|
ASSERT(stmt_list->at(0)->type == kStmtSelect);
|
||||||
|
|
||||||
|
SelectStatement* stmt = (SelectStatement*) stmt_list->at(0);
|
||||||
|
ASSERT_NULL(stmt->where_clause);
|
||||||
|
}
|
|
@ -0,0 +1,55 @@
|
||||||
|
|
||||||
|
#include "test.h"
|
||||||
|
|
||||||
|
|
||||||
|
class TestsManager {
|
||||||
|
// Note: static initialization fiasco
|
||||||
|
// http://www.parashift.com/c++-faq-lite/static-init-order.html
|
||||||
|
// http://www.parashift.com/c++-faq-lite/static-init-order-on-first-use.html
|
||||||
|
public:
|
||||||
|
static std::vector<std::string>& test_names() {
|
||||||
|
static std::vector<std::string>* test_names = new std::vector<std::string>;
|
||||||
|
return *test_names;
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::vector<void (*)(void)>& tests() {
|
||||||
|
static std::vector<void (*)(void)>* tests = new std::vector<void (*)(void)>;
|
||||||
|
return *tests;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
int AddTest(void (*foo)(void), std::string name) {
|
||||||
|
TestsManager::tests().push_back(foo);
|
||||||
|
TestsManager::test_names().push_back(name);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void RunTests() {
|
||||||
|
size_t num_failed = 0;
|
||||||
|
for (size_t i = 0; i < TestsManager::tests().size(); ++i) {
|
||||||
|
printf("\033[0;32m{ running}\033[0m %s\n", TestsManager::test_names()[i].c_str());
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Run test
|
||||||
|
(*TestsManager::tests()[i])();
|
||||||
|
printf("\033[0;32m{ ok}\033[0m %s\n", TestsManager::test_names()[i].c_str());
|
||||||
|
|
||||||
|
} catch (AssertionFailedException& e) {
|
||||||
|
printf("\033[1;31m{ failed} %s\n", TestsManager::test_names()[i].c_str());
|
||||||
|
printf("\tAssertion failed: %s\n\033[0m", e.what());
|
||||||
|
num_failed++;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
RunTests();
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -1,7 +1,8 @@
|
||||||
#ifndef __TEST_H__
|
#ifndef __TEST_H__
|
||||||
#define __TEST_H__
|
#define __TEST_H__
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
|
|
||||||
|
@ -44,40 +45,9 @@ protected:
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
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() {
|
int AddTest(void (*foo)(void), std::string name);
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue