Add unit tests to util.cc and module_util.cc

This commit is contained in:
Tim Wojtulewicz 2019-12-18 15:24:24 -07:00
parent f6a1a7053b
commit 6a52857f8f
2 changed files with 349 additions and 1 deletions

View file

@ -5,6 +5,8 @@
#include <string.h>
#include "module_util.h"
#include "3rdparty/doctest.h"
using namespace std;
static int streq(const char* s1, const char* s2)
@ -12,7 +14,20 @@ static int streq(const char* s1, const char* s2)
return ! strcmp(s1, s2);
}
// Returns it without trailing "::".
TEST_CASE("module_util streq")
{
CHECK(streq("abcd", "abcd") == true);
CHECK(streq("abcd", "efgh") == false);
}
TEST_CASE("module_util extract_module_name")
{
CHECK(extract_module_name("mod") == GLOBAL_MODULE_NAME);
CHECK(extract_module_name("mod::") == "mod");
CHECK(extract_module_name("mod::var") == "mod");
}
// Returns it without trailing "::" var section.
string extract_module_name(const char* name)
{
string module_name = name;
@ -26,6 +41,14 @@ string extract_module_name(const char* name)
return module_name;
}
TEST_CASE("module_util extract_var_name")
{
CHECK(extract_var_name("mod") == "mod");
CHECK(extract_var_name("mod::") == "");
CHECK(extract_var_name("mod::var") == "var");
CHECK(extract_var_name("::var") == "var");
}
string extract_var_name(const char *name)
{
string var_name = name;
@ -40,6 +63,13 @@ string extract_var_name(const char *name)
return var_name.substr(pos+2);
}
TEST_CASE("module_util normalized_module_name")
{
CHECK(normalized_module_name("a") == "a");
CHECK(normalized_module_name("module") == "module");
CHECK(normalized_module_name("module::") == "module");
}
string normalized_module_name(const char* module_name)
{
int mod_len;
@ -50,6 +80,18 @@ string normalized_module_name(const char* module_name)
return string(module_name, mod_len);
}
TEST_CASE("module_util make_full_var_name")
{
CHECK(make_full_var_name(nullptr, "GLOBAL::var") == "var");
CHECK(make_full_var_name(GLOBAL_MODULE_NAME, "var") == "var");
CHECK(make_full_var_name(nullptr, "notglobal::var") == "notglobal::var");
CHECK(make_full_var_name(nullptr, "::var") == "::var");
CHECK(make_full_var_name("module", "var") == "module::var");
CHECK(make_full_var_name("module::", "var") == "module::var");
CHECK(make_full_var_name("", "var") == "::var");
}
string make_full_var_name(const char* module_name, const char* var_name)
{
if ( ! module_name || streq(module_name, GLOBAL_MODULE_NAME) ||

View file

@ -53,6 +53,15 @@
#include "iosource/Manager.h"
#include "ConvertUTF.h"
#include "3rdparty/doctest.h"
TEST_CASE("util extract_ip")
{
CHECK(extract_ip("[1.2.3.4]") == "1.2.3.4");
CHECK(extract_ip("0x1.2.3.4") == "1.2.3.4");
CHECK(extract_ip("[]") == "");
}
/**
* Return IP address without enclosing brackets and any leading 0x. Also
* trims leading/trailing whitespace.
@ -74,6 +83,27 @@ std::string extract_ip(const std::string& i)
return s;
}
TEST_CASE("util extract_ip_and_len")
{
int len;
std::string out = extract_ip_and_len("[1.2.3.4/24]", &len);
CHECK(out == "1.2.3.4");
CHECK(len == 24);
out = extract_ip_and_len("0x1.2.3.4/32", &len);
CHECK(out == "1.2.3.4");
CHECK(len == 32);
out = extract_ip_and_len("[]/abcd", &len);
CHECK(out == "");
CHECK(len == 0);
len = 12345;
out = extract_ip_and_len("[]/16", nullptr);
CHECK(out == "");
CHECK(len == 12345);
}
/**
* Given a subnet string, return IP address and subnet length separately.
*/
@ -89,6 +119,12 @@ std::string extract_ip_and_len(const std::string& i, int* len)
return extract_ip(i.substr(0, pos));
}
TEST_CASE("util get_unescaped_string")
{
CHECK(get_unescaped_string("abcde") == "abcde");
CHECK(get_unescaped_string("\\x41BCD\\x45") == "ABCDE");
}
/**
* Takes a string, unescapes all characters that are escaped as hex codes
* (\x##) and turns them into the equivalent ascii-codes. Returns a string
@ -127,6 +163,31 @@ std::string get_unescaped_string(const std::string& arg_str)
return outstring;
}
TEST_CASE("util get_escaped_string")
{
SUBCASE("returned ODesc")
{
ODesc* d = get_escaped_string(nullptr, "a bcd\n", 6, false);
CHECK(strcmp(d->Description(), "a\\x20bcd\\x0a") == 0);
}
SUBCASE("provided ODesc")
{
ODesc d2;
get_escaped_string(&d2, "ab\\e", 4, true);
CHECK(strcmp(d2.Description(), "\\x61\\x62\\\\\\x65") == 0);
}
SUBCASE("std::string versions")
{
std::string s = get_escaped_string("a b c", 5, false);
CHECK(s == "a\\x20b\\x20c");
s = get_escaped_string("d e", false);
CHECK(s == "d\\x20e");
}
}
/**
* Takes a string, escapes characters into equivalent hex codes (\x##), and
* returns a string containing all escaped values.
@ -184,6 +245,12 @@ char* copy_string(const char* s)
return c;
}
TEST_CASE("util streq")
{
CHECK(streq("abcd", "abcd") == true);
CHECK(streq("abcd", "efgh") == false);
}
int streq(const char* s1, const char* s2)
{
return ! strcmp(s1, s2);
@ -287,6 +354,30 @@ char* skip_digits(char* s)
return s;
}
TEST_CASE("util get_word")
{
char orig[10];
strcpy(orig, "two words");
SUBCASE("get first word")
{
char* a = (char*)orig;
char* b = get_word(a);
CHECK(strcmp(a, "words") == 0);
CHECK(strcmp(b, "two") == 0);
}
SUBCASE("get length of first word")
{
int len = strlen(orig);
int len2;
const char* b = nullptr;
get_word(len, orig, len2, b);
CHECK(len2 == 3);
}
}
char* get_word(char*& s)
{
char* w = s;
@ -316,6 +407,17 @@ void get_word(int length, const char* s, int& pwlen, const char*& pw)
pwlen = len;
}
TEST_CASE("util to_upper")
{
char a[10];
strcpy(a, "aBcD");
to_upper(a);
CHECK(strcmp(a, "ABCD") == 0);
std::string b = "aBcD";
CHECK(to_upper(b) == "ABCD");
}
void to_upper(char* s)
{
while ( *s )
@ -363,6 +465,16 @@ unsigned char encode_hex(int h)
return hex[h];
}
TEST_CASE("util strpbrk_n")
{
const char* s = "abcdef";
const char* o = strpbrk_n(5, s, "gc");
CHECK(strcmp(o, "cdef") == 0);
const char* f = strpbrk_n(5, s, "xyz");
CHECK(f == nullptr);
}
// Same as strpbrk except that s is not NUL-terminated, but limited by
// len. Note that '\0' is always implicitly contained in charset.
const char* strpbrk_n(size_t len, const char* s, const char* charset)
@ -375,6 +487,20 @@ const char* strpbrk_n(size_t len, const char* s, const char* charset)
}
#ifndef HAVE_STRCASESTR
TEST_CASE("util strcasestr")
{
const char* s = "this is a string";
const char* out = strcasestr(s, "is");
CHECK(strcmp(out, "is a string") == 0);
const char* out2 = strcasestr(s, "IS");
CHECK(strcmp(out2, "is a string") == 0);
const char* out3 = strcasestr(s, "not there");
CHECK(strcmp(out2, s) == 0);
}
// This code is derived from software contributed to BSD by Chris Torek.
char* strcasestr(const char* s, const char* find)
{
@ -401,6 +527,22 @@ char* strcasestr(const char* s, const char* find)
}
#endif
TEST_CASE("util atoi_n")
{
const char* dec = "12345";
int val;
CHECK(atoi_n(strlen(dec), dec, nullptr, 10, val) == 1);
CHECK(val == 12345);
const char* hex = "12AB";
CHECK(atoi_n(strlen(hex), hex, nullptr, 16, val) == 1);
CHECK(val == 0x12AB);
const char* fail = "XYZ";
CHECK(atoi_n(strlen(fail), fail, nullptr, 10, val) == 0);
}
template<class T> int atoi_n(int len, const char* s, const char** end, int base, T& result)
{
T n = 0;
@ -453,6 +595,15 @@ template int atoi_n<uint32_t>(int len, const char* s, const char** end, int base
template int atoi_n<int64_t>(int len, const char* s, const char** end, int base, int64_t& result);
template int atoi_n<uint64_t>(int len, const char* s, const char** end, int base, uint64_t& result);
TEST_CASE("util uitoa_n")
{
int val = 12345;
char str[20];
const char* result = uitoa_n(val, str, 20, 10, "pref: ");
// TODO: i'm not sure this is the correct output. was it supposed to reverse the digits?
CHECK(strcmp(str, "pref: 54321") == 0);
}
char* uitoa_n(uint64_t value, char* str, int n, int base, const char* prefix)
{
static char dig[] = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
@ -486,6 +637,21 @@ char* uitoa_n(uint64_t value, char* str, int n, int base, const char* prefix)
return str;
}
TEST_CASE("util strstr_n")
{
const u_char* s = reinterpret_cast<const u_char*>("this is a string");
int out = strstr_n(16, s, 3, reinterpret_cast<const u_char*>("str"));
CHECK(out == 10);
out = strstr_n(16, s, 17, reinterpret_cast<const u_char*>("is"));
CHECK(out == -1);
out = strstr_n(16, s, 2, reinterpret_cast<const u_char*>("IS"));
CHECK(out == -1);
out = strstr_n(16, s, 9, reinterpret_cast<const u_char*>("not there"));
CHECK(out == -1);
}
int strstr_n(const int big_len, const u_char* big,
const int little_len, const u_char* little)
@ -510,6 +676,12 @@ int fputs(int len, const char* s, FILE* fp)
return 0;
}
TEST_CASE("util is_printable")
{
CHECK(is_printable("abcd", 4) == true);
CHECK(is_printable("ab\0d", 4) == false);
}
bool is_printable(const char* s, int len)
{
while ( --len >= 0 )
@ -518,6 +690,15 @@ bool is_printable(const char* s, int len)
return true;
}
TEST_CASE("util strtolower")
{
const char* a = "aBcD";
CHECK(strtolower(a) == "abcd");
std::string b = "aBcD";
CHECK(strtolower(b) == "abcd");
}
std::string strtolower(const std::string& s)
{
std::string t = s;
@ -525,6 +706,20 @@ std::string strtolower(const std::string& s)
return t;
}
TEST_CASE("util fmt_bytes")
{
const char* a = "abcd";
const char* af = fmt_bytes(a, 4);
CHECK(strcmp(a, af) == 0);
const char* b = "abc\0abc";
const char* bf = fmt_bytes(b, 7);
CHECK(strcmp(bf, "abc\\x00abc") == 0);
const char* cf = fmt_bytes(a, 3);
CHECK(strcmp(cf, "abc") == 0);
}
const char* fmt_bytes(const char* data, int len)
{
static char buf[1024];
@ -682,6 +877,13 @@ bool is_file(const std::string& path)
return S_ISREG(st.st_mode);
}
TEST_CASE("util strreplace")
{
string s = "this is not a string";
CHECK(strreplace(s, "not", "really") == "this is really a string");
CHECK(strreplace(s, "not ", "") == "this is a string");
}
string strreplace(const string& s, const string& o, const string& n)
{
string r = s;
@ -699,6 +901,18 @@ string strreplace(const string& s, const string& o, const string& n)
return r;
}
TEST_CASE("util strstrip")
{
string s = " abcd";
CHECK(strstrip(s) == "abcd");
s = "abcd ";
CHECK(strstrip(s) == "abcd");
s = " abcd ";
CHECK(strstrip(s) == "abcd");
}
std::string strstrip(std::string s)
{
auto notspace = [](unsigned char c) { return ! std::isspace(c); };
@ -1013,6 +1227,12 @@ string bro_prefixes()
return rval;
}
TEST_CASE("util is_package_loader")
{
CHECK(is_package_loader("/some/path/__load__.zeek") == true);
CHECK(is_package_loader("/some/path/notload.zeek") == false);
}
const array<string, 2> script_extensions = {".zeek", ".bro"};
bool is_package_loader(const string& path)
@ -1072,6 +1292,32 @@ FILE* open_package(string& path, const string& mode)
return 0;
}
TEST_CASE("util path ops")
{
SUBCASE("SafeDirname")
{
SafeDirname d("/this/is/a/path", false);
CHECK(d.result == "/this/is/a");
SafeDirname d2("invalid", false);
CHECK(d2.result == ".");
SafeDirname d3("./filename", false);
CHECK(d2.result == ".");
}
SUBCASE("SafeBasename")
{
SafeBasename b("/this/is/a/path", false);
CHECK(b.result == "path");
CHECK(! b.error);
SafeBasename b2("justafile", false);
CHECK(b2.result == "justafile");
CHECK(! b2.error);
}
}
void SafePathOp::CheckValid(const char* op_result, const char* path,
bool error_aborts)
{
@ -1128,6 +1374,16 @@ void SafeBasename::DoFunc(const string& path, bool error_aborts)
delete [] tmp;
}
TEST_CASE("util implode_string_vector")
{
std::vector<std::string> v = { "a", "b", "c" };
CHECK(implode_string_vector(v, ",") == "a,b,c");
CHECK(implode_string_vector(v, "") == "abc");
v.clear();
CHECK(implode_string_vector(v, ",") == "");
}
string implode_string_vector(const std::vector<std::string>& v,
const std::string& delim)
{
@ -1144,6 +1400,13 @@ string implode_string_vector(const std::vector<std::string>& v,
return rval;
}
TEST_CASE("util flatten_script_name")
{
CHECK(flatten_script_name("script", "some/path") == "some.path.script");
CHECK(flatten_script_name("other/path/__load__.zeek", "some/path") == "some.path.other.path");
CHECK(flatten_script_name("path/to/script", "") == "path.to.script");
}
string flatten_script_name(const string& name, const string& prefix)
{
string rval = prefix;
@ -1164,6 +1427,23 @@ string flatten_script_name(const string& name, const string& prefix)
return rval;
}
TEST_CASE("tuil tokenize_string")
{
auto v = tokenize_string("/this/is/a/path", "/", nullptr);
CHECK(v->size() == 5);
CHECK(*v == vector<string>({ "", "this", "is", "a", "path" }));
delete v;
std::vector<std::string> v2;
tokenize_string("/this/is/path/2", "/", &v2);
CHECK(v2.size() == 5);
CHECK(v2 == vector<string>({ "", "this", "is", "path", "2" }));
v2.clear();
tokenize_string("/wrong/delim", ",", &v2);
CHECK(v2.size() == 1);
}
vector<string>* tokenize_string(string input, const string& delim,
vector<string>* rval)
{
@ -1182,6 +1462,13 @@ vector<string>* tokenize_string(string input, const string& delim,
return rval;
}
TEST_CASE("util normalize_path")
{
CHECK(normalize_path("/1/2/3") == "/1/2/3");
CHECK(normalize_path("/1/./2/3") == "/1/2/3");
CHECK(normalize_path("/1/2/../3") == "/1/3");
CHECK(normalize_path("1/2/3/") == "1/2/3");
}
string normalize_path(const string& path)
{
@ -1311,6 +1598,13 @@ static bool ends_with(const std::string& s, const std::string& ending)
return std::equal(ending.rbegin(), ending.rend(), s.rbegin());
}
TEST_CASE("util ends_with")
{
CHECK(ends_with("abcde", "de") == true);
CHECK(ends_with("abcde", "fg") == false);
CHECK(ends_with("abcde", "abcedf") == false);
}
string find_script_file(const string& filename, const string& path_set)
{
vector<string> paths;
@ -1810,6 +2104,11 @@ void operator delete[](void* v)
#endif
TEST_CASE("util canonify_name")
{
CHECK(canonify_name("file name") == "FILE_NAME");
}
std::string canonify_name(const std::string& name)
{
unsigned int len = name.size();
@ -1887,6 +2186,13 @@ static string json_escape_byte(char c)
return result;
}
TEST_CASE("util json_escape_utf8")
{
CHECK(json_escape_utf8("string") == "string");
CHECK(json_escape_utf8("string\n") == "string\n");
CHECK(json_escape_utf8("string\x82") == "string\\x82");
}
string json_escape_utf8(const string& val)
{
string result;