Merge remote-tracking branch 'origin/topic/neverlord/doctest'

- Minor whitespace tweaks
- Add line to build summary output for whether unit tests are enabled

* origin/topic/neverlord/doctest:
  Add doctest license and copyright
  Integrate review feedback
  Fix submodule reference for doctest
  Add initial scaffold for unit testing via doctest
This commit is contained in:
Jon Siwek 2019-11-14 19:16:31 -08:00
commit 17fd371eae
10 changed files with 164 additions and 17 deletions

View file

@ -1,4 +1,8 @@
3.1.0-dev.271 | 2019-11-14 19:16:31 -0800
* Add initial scaffold for unit testing via doctest (Dominik Charousset, Corelight)
3.1.0-dev.266 | 2019-11-14 17:29:00 -0800 3.1.0-dev.266 | 2019-11-14 17:29:00 -0800
* Add hint to run `make distclean` if configure fails (Simon Hardy-Francis, Corelight) * Add hint to run `make distclean` if configure fails (Simon Hardy-Francis, Corelight)

View file

@ -19,6 +19,14 @@ include(cmake/FindClangTidy.cmake)
######################################################################## ########################################################################
## Project/Build Configuration ## Project/Build Configuration
if (ENABLE_ZEEK_UNIT_TESTS)
enable_testing()
add_definitions(-DDOCTEST_CONFIG_SUPER_FAST_ASSERTS)
else ()
add_definitions(-DDOCTEST_CONFIG_DISABLE)
endif ()
if ( ENABLE_CCACHE ) if ( ENABLE_CCACHE )
find_program(CCACHE_PROGRAM ccache) find_program(CCACHE_PROGRAM ccache)
@ -421,6 +429,7 @@ message(
"\nInstall prefix: ${CMAKE_INSTALL_PREFIX}" "\nInstall prefix: ${CMAKE_INSTALL_PREFIX}"
"\nZeek Script Path: ${ZEEK_SCRIPT_INSTALL_PATH}" "\nZeek Script Path: ${ZEEK_SCRIPT_INSTALL_PATH}"
"\nDebug mode: ${ENABLE_DEBUG}" "\nDebug mode: ${ENABLE_DEBUG}"
"\nUnit tests: ${ENABLE_ZEEK_UNIT_TESTS}"
"\n" "\n"
"\nCC: ${CMAKE_C_COMPILER}" "\nCC: ${CMAKE_C_COMPILER}"
"\nCFLAGS: ${CMAKE_C_FLAGS} ${CMAKE_C_FLAGS_${BuildType}}" "\nCFLAGS: ${CMAKE_C_FLAGS} ${CMAKE_C_FLAGS_${BuildType}}"

View file

@ -583,3 +583,29 @@ The original source file comes with this licensing statement:
This Source Code Form is subject to the terms of the Mozilla Public This Source Code Form is subject to the terms of the Mozilla Public
License, v. 2.0. If a copy of the MPL was not distributed with this License, v. 2.0. If a copy of the MPL was not distributed with this
file, You can obtain one at http://mozilla.org/MPL/2.0/. file, You can obtain one at http://mozilla.org/MPL/2.0/.
==============================================================================
%%% src/3rdparty/doctest.h
==============================================================================
Copyright (c) 2016-2019 Viktor Kirilov
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View file

@ -1 +1 @@
3.1.0-dev.266 3.1.0-dev.271

@ -1 +1 @@
Subproject commit 3cd85db7697584c9f6f0687cc9bdd1a64b4b74f8 Subproject commit 4bda55bcaa606bb2feb30ce93a0700e483208e2a

4
configure vendored
View file

@ -52,6 +52,7 @@ Usage: $0 [OPTION]... [VAR=VALUE]...
--enable-jemalloc link against jemalloc --enable-jemalloc link against jemalloc
--enable-static-broker build Broker statically (ignored if --with-broker is specified) --enable-static-broker build Broker statically (ignored if --with-broker is specified)
--enable-static-binpac build binpac statically (ignored if --with-binpac is specified) --enable-static-binpac build binpac statically (ignored if --with-binpac is specified)
--enable-cpp-tests build Zeek's C++ unit tests
--disable-zeekctl don't install ZeekControl --disable-zeekctl don't install ZeekControl
--disable-auxtools don't build or install auxiliary tools --disable-auxtools don't build or install auxiliary tools
--disable-python don't try to build python bindings for Broker --disable-python don't try to build python bindings for Broker
@ -250,6 +251,9 @@ while [ $# -ne 0 ]; do
--enable-static-binpac) --enable-static-binpac)
append_cache_entry BUILD_STATIC_BINPAC BOOL true append_cache_entry BUILD_STATIC_BINPAC BOOL true
;; ;;
--enable-cpp-tests)
append_cache_entry ENABLE_ZEEK_UNIT_TESTS BOOL true
;;
--disable-zeekctl) --disable-zeekctl)
append_cache_entry INSTALL_ZEEKCTL BOOL false append_cache_entry INSTALL_ZEEKCTL BOOL false
;; ;;

@ -1 +1 @@
Subproject commit ceaa6346378cbeda8ce4e2be95f75b60865d113e Subproject commit 2b3206b7add3472ea0736f2841473e11d506a85e

View file

@ -433,3 +433,25 @@ add_clang_tidy_files(${MAIN_SRCS})
# (*.pac.cc) files, and most of the generated code for BIFs (not including # (*.pac.cc) files, and most of the generated code for BIFs (not including
# *.bif.register.cc) # *.bif.register.cc)
create_clang_tidy_target() create_clang_tidy_target()
########################################################################
## CTest setup.
# Scan all .cc files for TEST_CASE macros and generate CTest targets.
if (ENABLE_ZEEK_UNIT_TESTS)
file(GLOB_RECURSE all_cc_files "*.cc")
set(test_cases "")
foreach (cc_file ${all_cc_files})
file (STRINGS ${cc_file} test_case_lines REGEX "TEST_CASE")
foreach (line ${test_case_lines})
string(REGEX REPLACE "TEST_CASE\\(\"(.+)\"\\)" "\\1" test_case "${line}")
list(APPEND test_cases "${test_case}")
endforeach ()
endforeach ()
list(LENGTH test_cases num_test_cases)
MESSAGE(STATUS "-- Found ${num_test_cases} test cases for CTest")
foreach (test_case ${test_cases})
add_test(NAME "\"${test_case}\""
COMMAND zeek --test "--test-case=${test_case}")
endforeach ()
endif ()

View file

@ -1,5 +1,6 @@
#include "Data.h" #include "Data.h"
#include "File.h" #include "File.h"
#include "3rdparty/doctest.h"
#include "broker/data.bif.h" #include "broker/data.bif.h"
#include <broker/error.hh> #include <broker/error.hh>
@ -35,6 +36,16 @@ static broker::port::protocol to_broker_port_proto(TransportProto tp)
} }
} }
TEST_CASE("converting Zeek to Broker protocol constants")
{
CHECK_EQ(to_broker_port_proto(TRANSPORT_TCP), broker::port::protocol::tcp);
CHECK_EQ(to_broker_port_proto(TRANSPORT_UDP), broker::port::protocol::udp);
CHECK_EQ(to_broker_port_proto(TRANSPORT_ICMP),
broker::port::protocol::icmp);
CHECK_EQ(to_broker_port_proto(TRANSPORT_UNKNOWN),
broker::port::protocol::unknown);
}
TransportProto bro_broker::to_bro_port_proto(broker::port::protocol tp) TransportProto bro_broker::to_bro_port_proto(broker::port::protocol tp)
{ {
switch ( tp ) { switch ( tp ) {
@ -50,6 +61,16 @@ TransportProto bro_broker::to_bro_port_proto(broker::port::protocol tp)
} }
} }
TEST_CASE("converting Broker to Zeek protocol constants")
{
using bro_broker::to_bro_port_proto;
CHECK_EQ(to_bro_port_proto(broker::port::protocol::tcp), TRANSPORT_TCP);
CHECK_EQ(to_bro_port_proto(broker::port::protocol::udp), TRANSPORT_UDP);
CHECK_EQ(to_bro_port_proto(broker::port::protocol::icmp), TRANSPORT_ICMP);
CHECK_EQ(to_bro_port_proto(broker::port::protocol::unknown),
TRANSPORT_UNKNOWN);
}
struct val_converter { struct val_converter {
using result_type = Val*; using result_type = Val*;

View file

@ -60,6 +60,9 @@ extern "C" {
#include "3rdparty/sqlite3.h" #include "3rdparty/sqlite3.h"
#define DOCTEST_CONFIG_IMPLEMENT
#include "3rdparty/doctest.h"
Brofiler brofiler; Brofiler brofiler;
#ifndef HAVE_STRSEP #ifndef HAVE_STRSEP
@ -150,7 +153,10 @@ bool bro_dns_fake()
void usage(int code = 1) void usage(int code = 1)
{ {
fprintf(stderr, "zeek version %s\n", zeek_version()); fprintf(stderr, "zeek version %s\n", zeek_version());
fprintf(stderr, "usage: %s [options] [file ...]\n", prog); fprintf(stderr, "usage: %s [options] [file ...]\n", prog);
fprintf(stderr, "usage: %s --test [doctest-options] -- [options] [file ...]\n", prog);
fprintf(stderr, " <file> | policy file, or read stdin\n"); fprintf(stderr, " <file> | policy file, or read stdin\n");
fprintf(stderr, " -a|--parse-only | exit immediately after parsing scripts\n"); fprintf(stderr, " -a|--parse-only | exit immediately after parsing scripts\n");
fprintf(stderr, " -b|--bare-mode | don't load scripts from the base/ directory\n"); fprintf(stderr, " -b|--bare-mode | don't load scripts from the base/ directory\n");
@ -192,6 +198,7 @@ void usage(int code = 1)
fprintf(stderr, " -n|--idmef-dtd <idmef-msg.dtd> | specify path to IDMEF DTD file\n"); fprintf(stderr, " -n|--idmef-dtd <idmef-msg.dtd> | specify path to IDMEF DTD file\n");
#endif #endif
fprintf(stderr, " --test | run unit tests ('--test -h' for help, only when compiling with ENABLE_ZEEK_UNIT_TESTS)\n");
fprintf(stderr, " $ZEEKPATH | file search path (%s)\n", bro_path().c_str()); fprintf(stderr, " $ZEEKPATH | file search path (%s)\n", bro_path().c_str());
fprintf(stderr, " $ZEEK_PLUGIN_PATH | plugin search path (%s)\n", bro_plugin_path()); fprintf(stderr, " $ZEEK_PLUGIN_PATH | plugin search path (%s)\n", bro_plugin_path());
fprintf(stderr, " $ZEEK_PLUGIN_ACTIVATE | plugins to always activate (%s)\n", bro_plugin_activate()); fprintf(stderr, " $ZEEK_PLUGIN_ACTIVATE | plugins to always activate (%s)\n", bro_plugin_activate());
@ -401,15 +408,63 @@ int main(int argc, char** argv)
{ {
std::set_new_handler(bro_new_handler); std::set_new_handler(bro_new_handler);
double time_start = current_time(true); // When running unit tests, the first argument on the command line must be
// --test, followed by doctest options. Optionally, users can use "--" as
// separator to pass Zeek options afterwards:
//
// zeek --test [doctest-options] -- [zeek-options]
brofiler.ReadStats(); if ( argc > 1 && strcmp(argv[1], "--test") == 0 )
{
// Deal with CLI arguments (copy Zeek part over to bro_argv).
auto is_separator = [](const char* cstr)
{
return strcmp(cstr, "--") == 0;
};
auto first = argv;
auto last = argv + argc;
auto separator = std::find_if(first, last, is_separator);
if ( separator == last )
{
bro_argc = 1;
bro_argv = new char*[1];
bro_argv[0] = copy_string(argv[0]);
}
else
{
auto first_zeek_arg = std::next(separator);
bro_argc = 1 + std::distance(first_zeek_arg, last);
bro_argv = new char*[bro_argc];
bro_argv[0] = copy_string(argv[0]);
auto bro_argv_iter = std::next(bro_argv);
for ( auto i = first_zeek_arg; i != last; ++i )
*bro_argv_iter++ = copy_string(*i);
}
#ifdef DOCTEST_CONFIG_DISABLE
fprintf(stderr, "ERROR: C++ unit tests are disabled for this build.\n"
" Please re-compile with ENABLE_ZEEK_UNIT_TESTS "
"to run the C++ unit tests.\n");
return EXIT_FAILURE;
#else
doctest::Context context;
context.applyCommandLine(std::distance(first, separator), argv);
return context.run();
#endif
}
else
{
bro_argc = argc; bro_argc = argc;
bro_argv = new char* [argc]; bro_argv = new char* [argc];
for ( int i = 0; i < argc; i++ ) for ( int i = 0; i < argc; i++ )
bro_argv[i] = copy_string(argv[i]); bro_argv[i] = copy_string(argv[i]);
}
double time_start = current_time(true);
brofiler.ReadStats();
name_list interfaces; name_list interfaces;
name_list read_files; name_list read_files;
@ -469,6 +524,7 @@ int main(int argc, char** argv)
#endif #endif
{"pseudo-realtime", optional_argument, 0, 'E'}, {"pseudo-realtime", optional_argument, 0, 'E'},
{"test", no_argument, 0, '#'},
{0, 0, 0, 0}, {0, 0, 0, 0},
}; };
@ -509,7 +565,7 @@ int main(int argc, char** argv)
#endif #endif
int op; int op;
while ( (op = getopt_long(argc, argv, opts, long_opts, &long_optsind)) != EOF ) while ( (op = getopt_long(bro_argc, bro_argv, opts, long_opts, &long_optsind)) != EOF )
switch ( op ) { switch ( op ) {
case 'a': case 'a':
parse_only = true; parse_only = true;
@ -649,6 +705,11 @@ int main(int argc, char** argv)
break; break;
#endif #endif
case '#':
fprintf(stderr, "ERROR: --test only allowed as first argument.\n");
usage(1);
break;
case 0: case 0:
// This happens for long options that don't have // This happens for long options that don't have
// a short-option equivalent. // a short-option equivalent.
@ -721,7 +782,7 @@ int main(int argc, char** argv)
plugin_mgr->SearchDynamicPlugins(bro_plugin_path()); plugin_mgr->SearchDynamicPlugins(bro_plugin_path());
if ( optind == argc && if ( optind == bro_argc &&
read_files.length() == 0 && read_files.length() == 0 &&
interfaces.length() == 0 && interfaces.length() == 0 &&
! id_name && ! command_line_policy && ! print_plugins ) ! id_name && ! command_line_policy && ! print_plugins )
@ -730,14 +791,14 @@ int main(int argc, char** argv)
// Process remaining arguments. X=Y arguments indicate script // Process remaining arguments. X=Y arguments indicate script
// variable/parameter assignments. X::Y arguments indicate plugins to // variable/parameter assignments. X::Y arguments indicate plugins to
// activate/query. The remainder are treated as scripts to load. // activate/query. The remainder are treated as scripts to load.
while ( optind < argc ) while ( optind < bro_argc )
{ {
if ( strchr(argv[optind], '=') ) if ( strchr(bro_argv[optind], '=') )
params.push_back(argv[optind++]); params.push_back(bro_argv[optind++]);
else if ( strstr(argv[optind], "::") ) else if ( strstr(bro_argv[optind], "::") )
requested_plugins.insert(argv[optind++]); requested_plugins.insert(bro_argv[optind++]);
else else
add_input_file(argv[optind++]); add_input_file(bro_argv[optind++]);
} }
push_scope(nullptr, nullptr); push_scope(nullptr, nullptr);