Commit 7c0e66d3 authored by Simon Morlat's avatar Simon Morlat

Add minimal testsuite

parent ca17fe16
......@@ -29,6 +29,7 @@ option(ENABLE_SHARED "Build shared library." ON)
option(ENABLE_STATIC "Build static library." ON)
option(ENABLE_STRICT "Build with strict compile options." YES)
option(ENABLE_TOOLS "Turn on or off compilation of tools." YES)
option(ENABLE_UNIT_TESTS "Enable compilation of unit tests." YES)
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
......@@ -50,7 +51,7 @@ if(LINPHONE_BUILDER_GROUP_EXTERNAL_SOURCE_PATH_BUILDERS)
set(BcToolbox_FIND_COMPONENTS tester)
include("${EP_bctoolbox_CONFIG_DIR}/BcToolboxConfig.cmake")
else()
find_package(BcToolbox 0.0.3 REQUIRED)
find_package(BcToolbox 0.0.3 REQUIRED OPTIONAL_COMPONENTS tester)
endif()
if(UNIX AND NOT APPLE)
......@@ -120,6 +121,9 @@ endif()
add_subdirectory(include)
add_subdirectory(src)
if(ENABLE_UNIT_TESTS AND BCTOOLBOX_TESTER_FOUND)
add_subdirectory(tester)
endif()
if(ENABLE_TOOLS)
add_subdirectory(tools)
endif()
......
......@@ -262,9 +262,11 @@ public:
DebugElement(const std::string &rulename, const std::string &value);
static std::shared_ptr<DebugElement> create(const std::string &rulename, const std::string &value);
void addChild(const std::shared_ptr<DebugElement> &e);
void findChildren(const std::string &rulename, std::list<std::shared_ptr<DebugElement>> &retlist)const;
BELR_PUBLIC std::ostream &tostream(int level, std::ostream &str)const;
const std::string &getValue()const;
private:
std::string mRulename;
std::string mValue;
std::list<std::shared_ptr<DebugElement>> mChildren;
......@@ -487,6 +489,9 @@ void ParserContext<_parserElementT>::_beginParse(ParserLocalContext & lctx, cons
ctx=h->createContext();
mHandlerStack.push_back(std::static_pointer_cast<HandlerContext<_parserElementT>>(ctx));
}
if (mHandlerStack.empty()){
belr_fatal("Cannot parse when mHandlerStack is empty. You must define a top-level rule handler.");
}
lctx.set(ctx,rec,mHandlerStack.back()->getLastIterator());
}
......
......@@ -42,6 +42,20 @@ void DebugElement::addChild(const shared_ptr< DebugElement >& e){
mChildren.push_back(e);
}
const string &DebugElement::getValue()const{
return mValue;
}
void DebugElement::findChildren(const std::string &rulename, std::list<std::shared_ptr<DebugElement>> &retlist)const{
for (std::list<std::shared_ptr<DebugElement>>::const_iterator it = mChildren.begin(); it != mChildren.end() ; ++it){
if ((*it)->mRulename == rulename){
retlist.push_back(*it);
}else{
findChildren(rulename, retlist);
}
}
}
ostream& DebugElement::tostream(int level, ostream& str) const{
int i;
for(i=0;i<level;i++) str<<'\t';
......@@ -83,4 +97,5 @@ void DebugParser::setObservedRules(const list< string >& rules){
}
}//end of namespace
############################################################################
# CMakeLists.txt
# Copyright (C) 2015 Belledonne Communications, Grenoble France
#
############################################################################
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
############################################################################
if(ENABLE_SHARED)
set(BELR_LIBRARIES_FOR_TESTER belr)
else()
set(BELR_LIBRARIES_FOR_TESTER belr-static)
endif()
set(GRAMMAR_FILES
res/basicgrammar.txt
res/vcardgrammar.txt
res/sipgrammar.txt
res/sipmessage.txt
)
set(HEADER_FILES_CXX belr-tester.h)
set(SOURCE_FILES_CXX
belr-tester.cpp
grammar-tester.cpp
)
bc_apply_compile_flags(SOURCE_FILES_CXX STRICT_OPTIONS_CPP STRICT_OPTIONS_CXX)
add_executable(belr_tester ${SOURCE_FILES_CXX} ${HEADER_FILES_CXX})
set_target_properties(belr_tester PROPERTIES LINKER_LANGUAGE CXX)
target_include_directories(belr_tester PUBLIC ${BCTOOLBOX_TESTER_INCLUDE_DIRS})
target_link_libraries(belr_tester ${BELR_LIBRARIES_FOR_TESTER} ${BCTOOLBOX_TESTER_LIBRARIES})
if(APPLE)
set_target_properties(belr_tester PROPERTIES LINK_FLAGS "-stdlib=libc++")
endif()
if(NOT IOS)
install(TARGETS belr_tester
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE
)
endif()
install(FILES ${GRAMMAR_FILES} DESTINATION "${CMAKE_INSTALL_DATADIR}/belr-tester/res")
/*
belcard-tester.cpp
Copyright (C) 2015 Belledonne Communications SARL
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "belr-tester.h"
#include "bctoolbox/logging.h"
std::string bcTesterFile(const std::string &name){
char *file = bc_tester_file(name.c_str());
std::string ret(file);
bc_free(file);
return ret;
}
std::string bcTesterRes(const std::string &name){
char *file = bc_tester_res(name.c_str());
std::string ret(file);
bc_free(file);
return ret;
}
int main(int argc, char *argv[]) {
int i;
int ret;
belr_tester_init(NULL);
if (strstr(argv[0], ".libs")) {
int prefix_length = (int)(strstr(argv[0], ".libs") - argv[0]) + 1;
char prefix[200];
sprintf(prefix, "%s%.*s", argv[0][0] == '/' ? "" : "./", prefix_length, argv[0]);
bc_tester_set_resource_dir_prefix(prefix);
bc_tester_set_writable_dir_prefix(prefix);
}
for(i = 1; i < argc; ++i) {
int ret = bc_tester_parse_args(argc, argv, i);
if (ret>0) {
i += ret - 1;
continue;
} else if (ret<0) {
bc_tester_helper(argv[0], "");
}
return ret;
}
ret = bc_tester_start(argv[0]);
belr_tester_uninit();
return ret;
}
static void log_handler(int lev, const char *fmt, va_list args) {
#ifdef _WIN32
vfprintf(lev == BCTBX_LOG_ERROR ? stderr : stdout, fmt, args);
fprintf(lev == BCTBX_LOG_ERROR ? stderr : stdout, "\n");
#else
va_list cap;
va_copy(cap,args);
/* Otherwise, we must use stdio to avoid log formatting (for autocompletion etc.) */
vfprintf(lev == BCTBX_LOG_ERROR ? stderr : stdout, fmt, cap);
fprintf(lev == BCTBX_LOG_ERROR ? stderr : stdout, "\n");
va_end(cap);
#endif
}
void belr_tester_init(void(*ftester_printf)(int level, const char *fmt, va_list args)) {
if (ftester_printf == NULL) ftester_printf = log_handler;
bc_tester_init(ftester_printf, BCTBX_LOG_MESSAGE, BCTBX_LOG_ERROR, ".");
bc_tester_add_suite(&grammar_suite);
}
void belr_tester_uninit(void) {
bc_tester_uninit();
}
/*
belr-tester.hpp
Copyright (C) 2015 Belledonne Communications SARL
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef belr_tester_hpp
#define belr_tester_hpp
#include <bctoolbox/tester.h>
#include "belr/grammarbuilder.h"
#include "belr/abnf.h"
#include "belr/belr.h"
#include <fstream>
#include <string>
#include <memory>
#include <sstream>
#include <iostream>
#include <vector>
#include <chrono>
#ifdef __cplusplus
extern "C" {
#endif
extern test_suite_t grammar_suite;
std::string bcTesterFile(const std::string &name);
std::string bcTesterRes(const std::string &name);
void belr_tester_init(void(*ftester_printf)(int level, const char *fmt, va_list args));
void belr_tester_uninit(void);
#ifdef __cplusplus
};
#endif
#endif
/*
belr-sipgrammar-tester.cpp
Copyright (C) 2015 Belledonne Communications SARL
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "belr-tester.h"
#include <cstdio>
using namespace::std;
using namespace::belr;
static string openFile(const string &name) {
ifstream istr(name, std::ios::binary);
if (!istr.is_open()) {
BC_FAIL(name);
}
stringstream tmpStream;
tmpStream << istr.rdbuf();
string tmp = tmpStream.str();
return tmp;
}
static bool parseMessage(shared_ptr<Grammar> grammar, const string &message) {
shared_ptr<DebugParser> parser = make_shared<DebugParser>(grammar);
parser->setObservedRules({"sip-message"});
size_t pos = 0;
shared_ptr<DebugElement> elem = parser->parseInput("sip-message", message, &pos);
BC_ASSERT_TRUE(elem != nullptr);
if (!elem) return FALSE;
BC_ASSERT_EQUAL(pos, message.size(), int, "%i");
BC_ASSERT_TRUE(message == elem->getValue());
return message.size() == pos && message == elem->getValue();
}
static void sipgrammar_save_and_load(void) {
string grammarToParse = bcTesterRes("res/sipgrammar.txt");
string grammarDump = bcTesterFile("grammarDump.bin");
string sipmessage = openFile(bcTesterRes("res/register.txt"));
remove(grammarDump.c_str());
BC_ASSERT_TRUE(sipmessage.size() > 0);
ABNFGrammarBuilder builder;
//Read grammar put it in object grammar
auto start = std::chrono::high_resolution_clock::now();
shared_ptr<Grammar> grammar=builder.createFromAbnfFile(grammarToParse, make_shared<CoreRules>());
auto finish = std::chrono::high_resolution_clock::now();
std::chrono::duration<double> elapsedFirst = finish - start;
BC_ASSERT_FALSE(!grammar);
if (!grammar) return;
// make sure we're able to parse a SIP message with the created grammar
BC_ASSERT_TRUE(parseMessage(grammar, sipmessage));
//Save grammar
grammar->save(grammarDump);
//Load grammar
start = std::chrono::high_resolution_clock::now();
shared_ptr<Grammar> loadedGram = make_shared<Grammar>("loaded");
BC_ASSERT_TRUE(loadedGram->load(grammarDump) == 0);
finish = std::chrono::high_resolution_clock::now();
std::chrono::duration<double> elapsedSecond = finish - start;
// Make sure the loaded from binary file grammar is still able to parse the sip message:
BC_ASSERT_TRUE(parseMessage(loadedGram, sipmessage));
BC_ASSERT_TRUE(elapsedFirst.count() > 2*elapsedSecond.count());
}
/**
* In SIP grammar, we have:
* header-name = token
* This kind of rules were causing issues in previous version of belr because "header-name" rule could not be attached
* with a parser handler, as the recognizer behind it was named "token".
*/
static void aliases_rules(void) {
string grammarToParse = bcTesterRes("res/sipgrammar.txt");
string sipmessage = openFile(bcTesterRes("res/response.txt"));
BC_ASSERT_TRUE(sipmessage.size() > 0);
ABNFGrammarBuilder builder;
//Read grammar put it in object grammar
auto start = std::chrono::high_resolution_clock::now();
shared_ptr<Grammar> grammar=builder.createFromAbnfFile(grammarToParse, make_shared<CoreRules>());
auto finish = std::chrono::high_resolution_clock::now();
std::chrono::duration<double> elapsedFirst = finish - start;
BC_ASSERT_FALSE(!grammar);
if (!grammar) return;
shared_ptr<DebugParser> parser = make_shared<DebugParser>(grammar);
parser->setObservedRules({"sip-message", "header-name"});
size_t pos = 0;
shared_ptr<DebugElement> elem = parser->parseInput("sip-message", sipmessage, &pos);
BC_ASSERT_TRUE(elem != nullptr);
if (!elem) return;
BC_ASSERT_EQUAL(pos, sipmessage.size(), int, "%i");
BC_ASSERT_TRUE(sipmessage == elem->getValue());
list<shared_ptr<DebugElement>> headerNames;
elem->findChildren("header-name", headerNames);
BC_ASSERT_TRUE(headerNames.size() == 1);
if (!headerNames.empty()) {
BC_ASSERT_TRUE(headerNames.front()->getValue() == "Custom-header");
}
}
static test_t tests[] = {
TEST_NO_TAG("SIP grammar save and load", sipgrammar_save_and_load),
TEST_NO_TAG("SIP grammar with aliases rules", aliases_rules)
};
test_suite_t grammar_suite = {
"Grammar",
NULL,
NULL,
NULL,
NULL,
sizeof(tests) / sizeof(tests[0]),
tests
};
mark = ALPHA
alphanum = DIGIT
escaped = "%" HEXDIG HEXDIG
separators = "(" / "(" / "<" / ">" / "@" / "," / ";" / ":"
unknown = 1*(mark)
multipke = mark alphanum
alphamine = escaped [unknown]
scheme = *("+")
kalimoon = *( "(" "^" "$" "``")
INVITEm = %x49.4E.56.49.54.45 ; INVITE in caps
ACKm = %x41.43.4B ; ACK in caps
OPTIONSm = %x4F.50.54.49.4F.4E.53 ; OPTIONS in caps
BYEm = %x42.59.45 ; BYE in caps
CANCELm = %x43.41.4E.43.45.4C ; CANCEL in caps
REGISTERm = %x52.45.47.49.53.54.45.52 ; REGISTER in caps
Method = INVITEm / ACKm / OPTIONSm / BYEm / CANCELm / REGISTERm
......@@ -5,5 +5,6 @@ To: <sip:smorlat2@siptest.linphone.org>;tag=yDXec8ej8Hrea
Call-ID: s69J2OQ4h4
CSeq: 21 REGISTER
Server: Flexisip/0.8.0 (sofia-sip-nta/2.0)
Custom-header: some-value
Content-Length: 0
This diff is collapsed.
vcard-entity = 1*vcard
vcard = "BEGIN:VCARD" CRLF
"VERSION:4.0" CRLF
1*contentline
"END:VCARD" CRLF
; A vCard object MUST include the VERSION and FN properties.
; VERSION MUST come immediately after BEGIN:VCARD.
contentline = [group "."] name *(";" param) ":" value CRLF
; When parsing a content line, folded lines must first
; be unfolded according to the unfolding procedure
; described in Section 3.2.
; When generating a content line, lines longer than 75
; characters SHOULD be folded according to the folding
; procedure described in Section 3.2.
group = 1*(ALPHA / DIGIT / "-")
name = "SOURCE" / "KIND" / "FN" / "N" / "NICKNAME"
/ "PHOTO" / "BDAY" / "ANNIVERSARY" / "GENDER" / "ADR" / "TEL"
/ "EMAIL" / "IMPP" / "LANG" / "TZ" / "GEO" / "TITLE" / "ROLE"
/ "LOGO" / "ORG" / "MEMBER" / "RELATED" / "CATEGORIES"
/ "NOTE" / "PRODID" / "REV" / "SOUND" / "UID" / "CLIENTPIDMAP"
/ "URL" / "KEY" / "FBURL" / "CALADRURI" / "CALURI" / "XML"
/ iana-token / x-name
; Parsing of the param and value is based on the "name" as
; defined in ABNF sections below.
; Group and name are case-insensitive.
iana-token = 1*(ALPHA / DIGIT / "-")
; identifier registered with IANA
x-name = "x-" 1*(ALPHA / DIGIT / "-")
; Names that begin with "x-" or "X-" are
; reserved for experimental use, not intended for released
; products, or for use in bilateral agreements.
param = any-param
; Allowed parameters depend on property name.
param-value = *SAFE-CHAR / DQUOTE *QSAFE-CHAR DQUOTE
any-param = (iana-token / x-name) "=" param-value *("," param-value)
value = text
/ text-list
/ date-list
/ time-list
/ date-time-list
/ date-and-or-time-list
/ timestamp-list
/ boolean
/ integer-list
/ float-list
;/ URI ; from Section 3 of [RFC3986]
/ utc-offset
;/ Language-Tag
;/ iana-valuespec
text = *TEXT-CHAR
TEXT-CHAR = "\\" / "\," / "\n" / WSP / NON-ASCII / %x21-2B / %x2D-5B / %x5D-7E
; Backslashes, commas, and newlines must be encoded.
text-list = text *("," text)
date-list = date *("," date)
time-list = time *("," time)
date-time-list = date-time *("," date-time)
date-and-or-time-list = date-and-or-time *("," date-and-or-time)
timestamp-list = timestamp *("," timestamp)
integer-list = integer *("," integer)
float-list = float *("," float)
boolean = "TRUE" / "FALSE"
integer = [sign] 1*DIGIT
float = [sign] 1*DIGIT ["." 1*DIGIT]
sign = "+" / "-"
year = 4DIGIT ; 0000-9999
month = 2DIGIT ; 01-12
day = 2DIGIT ; 01-28/29/30/31 depending on month and leap year
hour = 2DIGIT ; 00-23
minute = 2DIGIT ; 00-59
second = 2DIGIT ; 00-58/59/60 depending on leap second
zone = utc-designator / utc-offset
utc-designator = %x5A ; uppercase "Z"
date = year [month day]
/ year "-" month
/ "--" month [day]
/ "--" "-" day
date-noreduc = year month day
/ "--" month day
/ "--" "-" day
date-complete = year month day
time = hour [minute [second]] [zone]
/ "-" minute [second] [zone]
/ "-" "-" second [zone]
time-notrunc = hour [minute [second]] [zone]
time-complete = hour minute second [zone]
time-designator = %x54 ; uppercase "T"
date-time = date-noreduc time-designator time-notrunc
timestamp = date-complete time-designator time-complete
date-and-or-time = date-time / date / time-designator time
utc-offset = sign hour [minute]
NON-ASCII = UTF8-2 / UTF8-3 / UTF8-4
; UTF8-{2,3,4} are defined in [RFC3629]
QSAFE-CHAR = WSP / "!" / %x23-7E / NON-ASCII
; Any character except CTLs, DQUOTE
SAFE-CHAR = WSP / "!" / %x23-39 / %x3C-7E / NON-ASCII
; Any character except CTLs, DQUOTE, ";", ":"
VALUE-CHAR = WSP / VCHAR / NON-ASCII
; Any textual character
UTF8-1 = %x00-7F
UTF8-2 = %xC2-DF UTF8-tail
UTF8-3 = %xE0 %xA0-BF UTF8-tail / %xE1-EC 2( UTF8-tail ) / %xED %x80-9F UTF8-tail / %xEE-EF 2( UTF8-tail )
UTF8-4 = %xF0 %x90-BF 2( UTF8-tail ) / %xF1-F3 3( UTF8-tail ) / %xF4 %x80-8F 2( UTF8-tail )
UTF8-tail = %x80-BF
;Language-Tag = <Language-Tag, defined in [RFC5646], Section 2.1>
;iana-valuespec = <value-spec, see Section 12>
; a publicly defined valuetype format, registered
; with IANA, as defined in Section 12 of this
; document.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment