0ad/source/lib/tests/test_secure_crt.h
2009-08-09 09:42:23 +00:00

317 lines
9.4 KiB
C++

/* Copyright (C) 2009 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. 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.
*
* 0 A.D. 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 0 A.D. If not, see <http://www.gnu.org/licenses/>.
*/
#include "lib/self_test.h"
#include "lib/secure_crt.h"
// note: we only test the char version. this avoids having to
// expose secure_crt.cpp's tchar / tcpy etc. macros in the header and/or
// writing a copy of this test for the unicode version.
// secure_crt.cpp's unicode functions are the same anyway
// (they're implemented via the abovementioned tcpy macro redirection).
#if OS_WIN
// helper class to disable CRT error dialogs
class SuppressErrors
{
public:
SuppressErrors()
{
// Redirect the assertion output to somewhere where it shouldn't be noticed
old_mode = _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_FILE);
old_file = _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);
// Replace the invalid parameter handler with one that ignores everything
old_handler = _set_invalid_parameter_handler(&invalid_parameter_handler);
}
~SuppressErrors()
{
_CrtSetReportMode(_CRT_ASSERT, old_mode);
_CrtSetReportFile(_CRT_ASSERT, old_file);
_set_invalid_parameter_handler(old_handler);
}
private:
int old_mode;
_HFILE old_file;
_invalid_parameter_handler old_handler;
static void invalid_parameter_handler(const wchar_t* UNUSED(expression), const wchar_t* UNUSED(function),
const wchar_t* UNUSED(file), unsigned int UNUSED(line), uintptr_t UNUSED(pReserved))
{
return;
}
};
#else
class SuppressErrors
{
};
#endif
class TestString_s : public CxxTest::TestSuite
{
// note: avoid 4-byte strings - they would trigger WARN::IF_PTR_LEN.
const char* const s0;
const char* const s1;
const char* const s5;
const char* const s10;
char d1[1];
char d2[2];
char d3[3];
char d5[5];
char d6[6];
char d10[10];
char d11[11];
char no_null[7];
static void TEST_LEN(const char* string, size_t limit,
size_t expected_len)
{
TS_ASSERT_EQUALS(int(strnlen((string), int(limit))), int(expected_len));
}
static void TEST_CPY(char* dst, size_t dst_max, const char* src,
int expected_ret, const char* expected_dst)
{
int ret = strcpy_s(dst, dst_max, src);
TS_ASSERT_EQUALS(ret, expected_ret);
if(dst != 0)
TS_ASSERT(!strcmp(dst, expected_dst));
}
static void TEST_CPY2(char* dst, size_t max_dst_chars, const char* src,
int expected_ret, const char* expected_dst)
{
int ret = strcpy_s((dst), max_dst_chars, (src));
TS_ASSERT_EQUALS(ret, expected_ret);
if(dst != 0)
TS_ASSERT(!strcmp(dst, expected_dst));
}
static void TEST_NCPY(char* dst, size_t max_dst_chars, const char* src, size_t max_src_chars,
int expected_ret, const char* expected_dst)
{
int ret = strncpy_s(dst, max_dst_chars, src, max_src_chars);
TS_ASSERT_EQUALS(ret, expected_ret);
if(dst != 0)
TS_ASSERT(!strcmp(dst, expected_dst));
}
static void TEST_CAT(char* dst, size_t max_dst_chars, const char* src,
int expected_ret, const char* expected_dst)
{
int ret = strcat_s(dst, max_dst_chars, src);
TS_ASSERT_EQUALS(ret, expected_ret);
if(dst != 0)
TS_ASSERT(!strcmp(dst, expected_dst));
}
static void TEST_CAT2(char* dst, size_t max_dst_chars, const char* src,
const char* dst_val, int expected_ret, const char* expected_dst)
{
strcpy(dst, dst_val);
int ret = strcat_s(dst, max_dst_chars, src);
TS_ASSERT_EQUALS(ret, expected_ret);
if(dst != 0)
TS_ASSERT(!strcmp(dst, expected_dst));
}
static void TEST_NCAT(char* dst, size_t max_dst_chars, const char* src, size_t max_src_chars,
const char* dst_val, int expected_ret, const char* expected_dst)
{
strcpy(dst, dst_val);
int ret = strncat_s(dst, max_dst_chars, src, (max_src_chars));
TS_ASSERT_EQUALS(ret, expected_ret);
if(dst != 0)
TS_ASSERT(!strcmp(dst, expected_dst));
}
public:
TestString_s()
: s0(""), s1("a"), s5("abcde"), s10("abcdefghij")
{
const char no_null_tmp[] = { 'n','o','_','n','u','l','l'};
memcpy(no_null, no_null_tmp, sizeof(no_null));
}
// contains all tests that verify correct behavior for bogus input.
// our implementation suppresses error dialogs while the self-test is active,
// but others (e.g. the functions shipped with VC8) need the code in
// SuppressErrors to disable the error dialogs.
void test_param_validation()
{
SuppressErrors suppress;
#if !EMULATE_SECURE_CRT
# define debug_SkipNextError(err) (void)0
#endif
debug_SkipNextError(ERR::INVALID_PARAM);
TEST_CPY(0 ,0,0 , EINVAL,""); // all invalid
debug_SkipNextError(ERR::INVALID_PARAM);
TEST_CPY(0 ,0,s1, EINVAL,""); // dst = 0, max = 0
debug_SkipNextError(ERR::INVALID_PARAM);
TEST_CPY(0 ,1,s1, EINVAL,""); // dst = 0, max > 0
debug_SkipNextError(ERR::INVALID_PARAM);
TEST_CPY(d1,1,0 , EINVAL,""); // src = 0
debug_SkipNextError(ERR::INVALID_PARAM);
TEST_CPY(d1,0,s1, EINVAL,""); // max_dst_chars = 0
debug_SkipNextError(ERR::BUF_SIZE);
TEST_CPY2(d1,1, s1, ERANGE,"");
debug_SkipNextError(ERR::BUF_SIZE);
TEST_CPY2(d1,1, s5, ERANGE,"");
debug_SkipNextError(ERR::BUF_SIZE);
TEST_CPY2(d5,5, s5, ERANGE,"");
debug_SkipNextError(ERR::BUF_SIZE);
TEST_NCPY(d1,1 ,s1,1, ERANGE,"");
debug_SkipNextError(ERR::BUF_SIZE);
TEST_NCPY(d1,1 ,s5,1, ERANGE,"");
debug_SkipNextError(ERR::BUF_SIZE);
TEST_NCPY(d5,5 ,s5,5, ERANGE,"");
debug_SkipNextError(ERR::INVALID_PARAM);
TEST_CAT(0 ,0,0 , EINVAL,""); // all invalid
debug_SkipNextError(ERR::INVALID_PARAM);
TEST_CAT(0 ,0,s1, EINVAL,""); // dst = 0, max = 0
debug_SkipNextError(ERR::INVALID_PARAM);
TEST_CAT(0 ,1,s1, EINVAL,""); // dst = 0, max > 0
debug_SkipNextError(ERR::INVALID_PARAM);
TEST_CAT(d1,1,0 , EINVAL,""); // src = 0
debug_SkipNextError(ERR::INVALID_PARAM);
TEST_CAT(d1,0,s1, EINVAL,""); // max_dst_chars = 0
debug_SkipNextError(ERR::STRING_NOT_TERMINATED);
TEST_CAT(no_null,5,s1, EINVAL,""); // dst not terminated
debug_SkipNextError(ERR::BUF_SIZE);
TEST_CAT2(d1,1, s1, "",ERANGE,"");
debug_SkipNextError(ERR::BUF_SIZE);
TEST_CAT2(d1,1, s5, "",ERANGE,"");
debug_SkipNextError(ERR::BUF_SIZE);
TEST_CAT2(d10,10, s10, "",ERANGE,""); // empty, total overflow
debug_SkipNextError(ERR::BUF_SIZE);
TEST_CAT2(d10,10, s5, "12345",ERANGE,""); // not empty, overflow
debug_SkipNextError(ERR::BUF_SIZE);
TEST_CAT2(d10,10, s10, "12345",ERANGE,""); // not empty, total overflow
debug_SkipNextError(ERR::BUF_SIZE);
TEST_NCAT(d1,1, s1,1, "",ERANGE,"");
debug_SkipNextError(ERR::BUF_SIZE);
TEST_NCAT(d1,1, s5,5, "",ERANGE,"");
debug_SkipNextError(ERR::BUF_SIZE);
TEST_NCAT(d10,10, s10,10, "",ERANGE,""); // empty, total overflow
debug_SkipNextError(ERR::BUF_SIZE);
TEST_NCAT(d10,10, s5,5, "12345",ERANGE,""); // not empty, overflow
debug_SkipNextError(ERR::BUF_SIZE);
TEST_NCAT(d10,10, s10,10, "12345",ERANGE,""); // not empty, total overflow
#if !EMULATE_SECURE_CRT
# undef debug_SkipNextError
#endif
}
void test_length()
{
TEST_LEN(s0, 0 , 0 );
TEST_LEN(s0, 1 , 0 );
TEST_LEN(s0, 50, 0 );
TEST_LEN(s1, 0 , 0 );
TEST_LEN(s1, 1 , 1 );
TEST_LEN(s1, 50, 1 );
TEST_LEN(s5, 0 , 0 );
TEST_LEN(s5, 1 , 1 );
TEST_LEN(s5, 50, 5 );
TEST_LEN(s10,9 , 9 );
TEST_LEN(s10,10, 10);
TEST_LEN(s10,11, 10);
}
void test_copy()
{
TEST_CPY2(d2,2 ,s1, 0,"a");
TEST_CPY2(d6,6 ,s5, 0,"abcde");
TEST_CPY2(d11,11, s5, 0,"abcde");
TEST_NCPY(d2,2 ,s1,1, 0,"a");
TEST_NCPY(d6,6 ,s5,5, 0,"abcde");
TEST_NCPY(d11,11, s5,5, 0,"abcde");
strcpy(d5, "----");
TEST_NCPY(d5,5, s5,0 , 0,""); // specified behavior! see 3.6.2.1.1 #4
TEST_NCPY(d5,5, s5,1 , 0,"a");
TEST_NCPY(d6,6, s5,5 , 0,"abcde");
TEST_NCPY(d6,6, s5,10, 0,"abcde");
}
void test_concatenate()
{
TEST_CAT2(d3,3, s1, "1",0,"1a");
TEST_CAT2(d5,5, s1, "1",0,"1a");
TEST_CAT2(d6,6, s5, "",0,"abcde");
TEST_CAT2(d10,10, s5, "",0,"abcde");
TEST_CAT2(d10,10, s5, "1234",0,"1234abcde");
TEST_NCAT(d3,3, s1,1, "1",0,"1a");
TEST_NCAT(d5,5, s1,1, "1",0,"1a");
TEST_NCAT(d6,6, s5,5, "",0,"abcde");
TEST_NCAT(d10,10, s5,5, "",0,"abcde");
TEST_NCAT(d10,10, s5,5, "1234",0,"1234abcde");
TEST_NCAT(d5,5, s5,0, "----",0,"----");
TEST_NCAT(d5,5, s5,1, "",0,"a");
TEST_NCAT(d5,5, s5,4, "",0,"abcd");
TEST_NCAT(d5,5, s5,2, "12",0,"12ab");
TEST_NCAT(d6,6, s5,10, "",0,"abcde");
}
static void TEST_PRINTF(char* dst, size_t max_dst_chars, const char* dst_val,
int expected_ret, const char* expected_dst, const char* fmt, ...)
{
if (dst) strcpy(dst, dst_val);
va_list ap;
va_start(ap, fmt);
int ret = vsprintf_s(dst, max_dst_chars, fmt, ap);
va_end(ap);
TS_ASSERT_EQUALS(ret, expected_ret);
if (dst) TS_ASSERT_STR_EQUALS(dst, expected_dst);
}
void test_printf()
{
TEST_PRINTF(d10,10, s10, 4, "1234", "%d", 1234);
TEST_PRINTF(d10,5, s10, 4, "1234", "%d", 1234);
SuppressErrors suppress;
TEST_PRINTF(d10,4, s10, -1, "", "%d", 1234);
TEST_PRINTF(d10,3, s10, -1, "", "%d", 1234);
TEST_PRINTF(d10,0, s10, -1, "abcdefghij", "%d", 1234);
TEST_PRINTF(NULL,0, NULL, -1, "", "%d", 1234);
TEST_PRINTF(d10,10, s10, -1, "abcdefghij", NULL);
}
};