# add boatload of self-tests
(these are the old self-tests ripped out as well as new ones) note: have not run yet; several would surely fail if they run (can't anyway until cxxtest is installed) This was SVN commit r3912.
This commit is contained in:
parent
1ead202b24
commit
e0dfbe719d
105
source/lib/res/file/tests/test_archive_builder.h
Normal file
105
source/lib/res/file/tests/test_archive_builder.h
Normal file
@ -0,0 +1,105 @@
|
||||
#include <cxxtest/TestSuite.h>
|
||||
|
||||
#include "lib/res/file/archive_builder.h"
|
||||
|
||||
class TestArchiveBuilder : public CxxTest::TestSuite
|
||||
{
|
||||
const char* const archive_fn;
|
||||
const size_t NUM_FILES;
|
||||
const size_t MAX_FILE_SIZE;
|
||||
|
||||
std::set<const char*> existing_names;
|
||||
const char* gen_random_name()
|
||||
{
|
||||
// 10 chars is enough for (10-1)*5 bits = 45 bits > u32
|
||||
char name_tmp[10];
|
||||
|
||||
for(;;)
|
||||
{
|
||||
u32 rand_num = rand(0, 100000);
|
||||
base32(4, (u8*)&rand_num, name_tmp);
|
||||
|
||||
// store filename in atom pool
|
||||
const char* atom_fn = file_make_unique_fn_copy(name_tmp);
|
||||
// done if the filename is unique (not been generated yet)
|
||||
if(existing_names.find(atom_fn) == existing_names.end())
|
||||
{
|
||||
existing_names.insert(atom_fn);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct TestFile
|
||||
{
|
||||
off_t size;
|
||||
u8* data; // must be delete[]-ed after comparing
|
||||
};
|
||||
// (must be separate array and end with NULL entry (see Filenames))
|
||||
const char* filenames[NUM_FILES+1];
|
||||
|
||||
void generate_random_files()
|
||||
{
|
||||
TestFile files[NUM_FILES];
|
||||
for(size_t i = 0; i < NUM_FILES; i++)
|
||||
{
|
||||
const size_t size = rand(0, MAX_FILE_SIZE);
|
||||
u8* data = new u8[size];
|
||||
|
||||
// random data won't compress at all, and we want to exercise
|
||||
// the uncompressed codepath as well => make some of the files
|
||||
// easily compressible (much less values).
|
||||
const bool make_easily_compressible = (rand(0, 100) > 50);
|
||||
if(make_easily_compressible)
|
||||
{
|
||||
for(size_t i = 0; i < size; i++)
|
||||
data[i] = rand() & 0x0F;
|
||||
}
|
||||
else
|
||||
{
|
||||
for(size_t i = 0; i < size; i++)
|
||||
data[i] = rand() & 0xFF;
|
||||
}
|
||||
|
||||
filenames[i] = gen_random_name();
|
||||
files[i].size = size;
|
||||
files[i].data = data;
|
||||
|
||||
TS_ASSERT_OK(vfs_store(filenames[i], data, size, FILE_NO_AIO));
|
||||
}
|
||||
|
||||
filenames[NUM_FILES] = NULL;
|
||||
}
|
||||
|
||||
public:
|
||||
TestArchiveBuilder()
|
||||
: archive_fn("test_archive_random_data.zip"),
|
||||
NUM_FILES(300), MAX_FILE_SIZE(20000) {}
|
||||
|
||||
void test()
|
||||
{
|
||||
generate_random_files();
|
||||
|
||||
// build and open archive
|
||||
TS_ASSERT_OK(archive_build(archive_fn, filenames));
|
||||
Handle ha = archive_open(archive_fn);
|
||||
TS_ASSERT(ha > 0);
|
||||
|
||||
// read in each file and compare file contents
|
||||
for(size_t i = 0; i < num_files; i++)
|
||||
{
|
||||
File f;
|
||||
TS_ASSERT_OK(afile_open(ha, filenames[i], 0, 0, &f));
|
||||
FileIOBuf buf = FILE_BUF_ALLOC;
|
||||
ssize_t bytes_read = afile_read(&f, 0, files[i].size, &buf);
|
||||
TS_ASSERT_EQUAL(bytes_read, files[i].size);
|
||||
|
||||
TS_ASSERT_SAME_DATA(buf, files[i].data);
|
||||
|
||||
TS_ASSERT_OK(file_buf_free(buf));
|
||||
SAFE_ARRAY_DELETE(files[i].data);
|
||||
}
|
||||
|
||||
TS_ASSERT_OK(archive_close(ha));
|
||||
}
|
||||
};
|
47
source/lib/res/file/tests/test_compression.h
Normal file
47
source/lib/res/file/tests/test_compression.h
Normal file
@ -0,0 +1,47 @@
|
||||
#include <cxxtest/TestSuite.h>
|
||||
|
||||
#include "lib/res/file/compression.h"
|
||||
|
||||
class TestCompression : public CxxTest::TestSuite
|
||||
{
|
||||
public:
|
||||
void test()
|
||||
{
|
||||
// generate random input data
|
||||
const size_t data_size = 10000;
|
||||
u8 data[data_size];
|
||||
for(size_t i = 0; i < data_size; i++)
|
||||
data[i] = rand() & 0xFF;
|
||||
|
||||
void* cdata; size_t csize;
|
||||
u8 ucdata[data_size];
|
||||
|
||||
// compress
|
||||
uintptr_t c = comp_alloc(CT_COMPRESSION, CM_DEFLATE);
|
||||
{
|
||||
TS_ASSERT(c != 0);
|
||||
TS_ASSERT_OK(comp_alloc_output(c, in_size));
|
||||
const ssize_t cdata_produced = comp_feed(c, in, in_size);
|
||||
TS_ASSERT(cdata_produced > 0);
|
||||
TS_ASSERT_OK(comp_finish(c, &cdata, &csize));
|
||||
TS_ASSERT(cdata_produced <= csize); // can't have produced more than total
|
||||
}
|
||||
|
||||
// decompress
|
||||
uintptr_t d = comp_alloc(CT_DECOMPRESSION, CM_DEFLATE);
|
||||
{
|
||||
TS_ASSERT(d != 0);
|
||||
TS_ASSERT_OK(comp_set_output(ucdata, data_size));
|
||||
const ssize_t ucdata_produced = comp_feed(c, cdata, cdata_size);
|
||||
TS_ASSERT(ucdata_produced > 0);
|
||||
void* ucdata_final; size_t ucsize_final;
|
||||
TS_ASSERT_OK(comp_finish(c, &ucdata_final, &ucsize_final));
|
||||
TS_ASSERT(ucdata_produced <= ucsize_final); // can't have produced more than total
|
||||
TS_ASSERT_EQUAL(ucdata_final, ucdata); // output buffer address is same
|
||||
TS_ASSERT_EQUAL(ucsize_final, data_size); // correct amount of output
|
||||
}
|
||||
|
||||
// verify data survived intact
|
||||
TS_ASSERT_SAME_DATA(data, ucdata, data_size);
|
||||
}
|
||||
};
|
63
source/lib/res/file/tests/test_file_cache.h
Normal file
63
source/lib/res/file/tests/test_file_cache.h
Normal file
@ -0,0 +1,63 @@
|
||||
#include <cxxtest/TestSuite.h>
|
||||
|
||||
#include "lib/res/file/file_cache.h"
|
||||
|
||||
class TestFileCache : public CxxTest::TestSuite
|
||||
{
|
||||
public:
|
||||
void test_cache_allocator()
|
||||
{
|
||||
// allocated address -> its size
|
||||
typedef std::map<void*, size_t> AllocMap;
|
||||
AllocMap allocations;
|
||||
|
||||
// put allocator through its paces by allocating several times
|
||||
// its capacity (this ensures memory is reused)
|
||||
srand(1);
|
||||
size_t total_size_used = 0;
|
||||
while(total_size_used < 4*MAX_CACHE_SIZE)
|
||||
{
|
||||
size_t size = rand(1, MAX_CACHE_SIZE/4);
|
||||
total_size_used += size;
|
||||
void* p;
|
||||
// until successful alloc:
|
||||
for(;;)
|
||||
{
|
||||
p = cache_allocator.alloc(size);
|
||||
if(p)
|
||||
break;
|
||||
// out of room - remove a previous allocation
|
||||
// .. choose one at random
|
||||
size_t chosen_idx = (size_t)rand(0, (uint)allocations.size());
|
||||
AllocMap::iterator it = allocations.begin();
|
||||
for(; chosen_idx != 0; chosen_idx--)
|
||||
++it;
|
||||
cache_allocator.dealloc((u8*)it->first, it->second);
|
||||
allocations.erase(it);
|
||||
}
|
||||
|
||||
// must not already have been allocated
|
||||
TS_ASSERT_EQUAL(allocations.find(p), allocations.end());
|
||||
allocations[p] = size;
|
||||
}
|
||||
|
||||
// reset to virginal state
|
||||
cache_allocator.reset();
|
||||
}
|
||||
|
||||
void test_file_cache()
|
||||
{
|
||||
// we need a unique address for file_cache_add, but don't want to
|
||||
// actually put it in the atom_fn storage (permanently clutters it).
|
||||
// just increment this pointer (evil but works since it's not used).
|
||||
// const char* atom_fn = (const char*)1;
|
||||
// give to file_cache
|
||||
// file_cache_add((FileIOBuf)p, size, atom_fn++);
|
||||
|
||||
file_cache_reset();
|
||||
TS_ASSERT(file_cache.empty());
|
||||
|
||||
// note: even though everything has now been freed,
|
||||
// the freelists may be a bit scattered already.
|
||||
}
|
||||
};
|
64
source/lib/res/file/tests/test_path.h
Normal file
64
source/lib/res/file/tests/test_path.h
Normal file
@ -0,0 +1,64 @@
|
||||
#include <cxxtest/TestSuite.h>
|
||||
|
||||
#include "lib/res/file/path.h"
|
||||
|
||||
class TestPath : public CxxTest::TestSuite
|
||||
{
|
||||
public:
|
||||
void test_conversion()
|
||||
{
|
||||
char N_path[PATH_MAX] = {0};
|
||||
TS_ASSERT_OK(file_make_native_path("a/b/c", N_path));
|
||||
#if OS_WIN
|
||||
TS_ASSERT_STR_EQUAL(N_path, "a\\b\\c");
|
||||
#else
|
||||
TS_ASSERT_STR_EQUAL(N_path, "a/b/c");
|
||||
#endif
|
||||
|
||||
char P_path[PATH_MAX] = {0};
|
||||
TS_ASSERT_OK(file_make_portable_path("a\\b\\c", P_path));
|
||||
#if OS_WIN
|
||||
TS_ASSERT_STR_EQUAL(P_path, "a/b/c"));
|
||||
#else
|
||||
// sounds strange, but correct: on non-Windows, \\ didn't
|
||||
// get recognized as separators and weren't converted.
|
||||
TS_ASSERT_STR_EQUAL(P_path, "a\\b\\c"));
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
// file_make_full_*_path is left untested (hard to do so)
|
||||
|
||||
void test_atom()
|
||||
{
|
||||
path_init();
|
||||
|
||||
// file_make_unique_fn_copy
|
||||
|
||||
// .. return same address for same string?
|
||||
const char* atom1 = file_make_unique_fn_copy("a/bc/def");
|
||||
const char* atom2 = file_make_unique_fn_copy("a/bc/def");
|
||||
TS_ASSERT_EQUAL(atom1, atom2);
|
||||
|
||||
// .. early out (already in pool) check works?
|
||||
const char* atom3 = file_make_unique_fn_copy(atom1);
|
||||
TS_ASSERT_EQUAL(atom3, atom1);
|
||||
|
||||
|
||||
// path_is_atom_fn
|
||||
// is it reported as in pool?
|
||||
TS_ASSERT(path_is_atom_fn(atom1));
|
||||
|
||||
// file_get_random_name
|
||||
// see if the atom added above eventually comes out when a
|
||||
// random one is returned from the pool.
|
||||
int tries_left;
|
||||
for(tries_left = 1000; tries != 0; tries--)
|
||||
{
|
||||
const char* random_name = file_get_random_name();
|
||||
if(random_name == atom1)
|
||||
break;
|
||||
}
|
||||
TS_ASSERT(tries_left != 0);
|
||||
}
|
||||
};
|
23
source/lib/res/file/tests/test_zip.h
Normal file
23
source/lib/res/file/tests/test_zip.h
Normal file
@ -0,0 +1,23 @@
|
||||
#include <cxxtest/TestSuite.h>
|
||||
|
||||
#include "lib/res/file/zip.h"
|
||||
|
||||
class TestZip : public CxxTest::TestSuite
|
||||
{
|
||||
public:
|
||||
void test_fat_timedate_conversion()
|
||||
{
|
||||
// note: FAT time stores second/2, which means converting may
|
||||
// end up off by 1 second.
|
||||
|
||||
time_t t, converted_t;
|
||||
|
||||
t = time(0);
|
||||
converted_t = time_t_from_FAT(FAT_from_time_t(t));
|
||||
TS_ASSERT_DELTA(t, converted_t, 2);
|
||||
|
||||
t++;
|
||||
converted_t = time_t_from_FAT(FAT_from_time_t(t));
|
||||
TS_ASSERT_DELTA(t, converted_t, 2);
|
||||
}
|
||||
};
|
130
source/lib/res/graphics/tests/test_tex.h
Normal file
130
source/lib/res/graphics/tests/test_tex.h
Normal file
@ -0,0 +1,130 @@
|
||||
#include <cxxtest/TestSuite.h>
|
||||
|
||||
#include "lib/self_test.h"
|
||||
#include "lib/res/graphics/tex.h"
|
||||
|
||||
class TestTex : public CxxTest::TestSuite
|
||||
{
|
||||
void generate_encode_decode_compare(size_t w, size_t h, uint flags, uint bpp,
|
||||
const char* filename)
|
||||
{
|
||||
// generate test data
|
||||
const size_t size = w*h*bpp/8;
|
||||
u8* img = new u8[size];
|
||||
for(size_t i = 0; i < size; i++)
|
||||
img[i] = rand() & 0xFF;
|
||||
|
||||
// wrap in Tex
|
||||
Tex t;
|
||||
TS_ASSERT_OK(tex_wrap(w, h, bpp, flags, img, &t));
|
||||
|
||||
// encode to file format
|
||||
DynArray da;
|
||||
TS_ASSERT_OK(tex_encode(&t, filename, &da));
|
||||
memset(&t, 0, sizeof(t));
|
||||
|
||||
// decode from file format
|
||||
MEM_DTOR dtor = 0; // we'll free da manually
|
||||
TS_ASSERT_OK(tex_decode(da.base, da.cur_size, dtor, &t));
|
||||
|
||||
// make sure pixel format gets converted completely to plain
|
||||
TS_ASSERT_OK(tex_transform_to(&t, 0));
|
||||
|
||||
// compare img
|
||||
TS_ASSERT_SAME_DATA(tex_get_data(&t), img);
|
||||
|
||||
// cleanup
|
||||
TS_ASSERT_OK(tex_free(&t));
|
||||
TS_ASSERT_OK(da_free(&da));
|
||||
delete[] img;
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
// this also covers BGR and orientation transforms.
|
||||
void test_encode_decode()
|
||||
{
|
||||
// for each codec
|
||||
TexCodecVTbl* c = 0;
|
||||
for(;;)
|
||||
{
|
||||
c = tex_codec_next(c);
|
||||
if(!c)
|
||||
break;
|
||||
|
||||
// get an extension that this codec will support
|
||||
// (so that tex_encode uses this codec)
|
||||
char extension[30] = {'.'};
|
||||
strcpy_s(extension+1, 29, c->name);
|
||||
// .. make sure the c->name hack worked
|
||||
TexCodecVTbl* correct_c;
|
||||
TS_ASSERT_OK(tex_codec_for_filename(extension, &correct_c));
|
||||
TS_ASSERT_EQUAL(c, correct_c);
|
||||
|
||||
// for each test width/height combination
|
||||
const uint widths [] = { 4, 5, 4, 256, 384 };
|
||||
const uint heights[] = { 4, 4, 5, 256, 256 };
|
||||
for(size_t i = 0; i < ARRAY_SIZE(widths); i++)
|
||||
{
|
||||
// for each bit depth
|
||||
for(uint bpp = 8; bpp <= 32; bpp += 8)
|
||||
{
|
||||
uint flags = 0;
|
||||
if(!strcmp(extension, ".dds")
|
||||
flags |= (TEX_DXT&3); // DXT3
|
||||
if(bpp == 8)
|
||||
flags |= TEX_GRAY;
|
||||
else if(bpp == 32)
|
||||
flags |= TEX_ALPHA;
|
||||
|
||||
// normal
|
||||
generate_encode_decode_compare(widths[i], heights[i], flags, bpp, extension);
|
||||
// top-down
|
||||
flags &= ~TEX_ORIENTATION; flags |= TEX_TOP_DOWN;
|
||||
generate_encode_decode_compare(widths[i], heights[i], flags, bpp, extension);
|
||||
// bottom up
|
||||
flags &= ~TEX_ORIENTATION; flags |= TEX_BOTTOM_UP;
|
||||
generate_encode_decode_compare(widths[i], heights[i], flags, bpp, extension);
|
||||
|
||||
flags &= ~TEX_ORIENTATION;
|
||||
flags |= TEX_BGR;
|
||||
|
||||
// bgr, normal
|
||||
generate_encode_decode_compare(widths[i], heights[i], flags, bpp, extension);
|
||||
// bgr, top-down
|
||||
flags ~= TEX_ORIENTATION; flags |= TEX_TOP_DOWN;
|
||||
generate_encode_decode_compare(widths[i], heights[i], flags, bpp, extension);
|
||||
// bgr, bottom up
|
||||
flags ~= TEX_ORIENTATION; flags |= TEX_BOTTOM_UP;
|
||||
generate_encode_decode_compare(widths[i], heights[i], flags, bpp, extension);
|
||||
} // for bpp
|
||||
} // for width/height
|
||||
} // foreach codec
|
||||
}
|
||||
|
||||
// have mipmaps be created for a test image; check resulting size and pixels
|
||||
void test_mipmap_create()
|
||||
{
|
||||
const u8 img[] = { 0x10,0x20,0x30, 0x40,0x60,0x80, 0xA0,0xA4,0xA8, 0xC0,0xC1,0xC2 };
|
||||
// assumes 2x2 box filter algorithm with rounding
|
||||
const u8 mipmap[] = { 0x70,0x79,0x87 };
|
||||
Tex t;
|
||||
TS_ASSERT_OK(tex_wrap(2, 2, 24, 0, img, &t));
|
||||
TS_ASSERT_OK(tex_transform_to(&t, TEX_MIPMAPS));
|
||||
const u8* const out_img = tex_get_data(&t);
|
||||
TS_ASSERT_EQUAL(tex_img_size(&t, 12+3));
|
||||
TS_ASSERT_SAME_DATA(out_img, img, 12);
|
||||
TS_ASSERT_SAME_DATA(out_img+12, mipmap, 3);
|
||||
}
|
||||
|
||||
void test_img_size()
|
||||
{
|
||||
Tex t;
|
||||
TS_ASSERT_OK(tex_wrap(100, 100, 32, TEX_ALPHA, 0, &t));
|
||||
TS_ASSERT_EQUAL(tex_img_size(&t), 40000);
|
||||
|
||||
// DXT rounds up to 4x4 blocks; DXT1a is 4bpp
|
||||
TS_ASSERT_OK(tex_wrap(97, 97, 32, DXT1A, 0, &t));
|
||||
TS_ASSERT_EQUAL(tex_img_size(&t), 5000);
|
||||
}
|
||||
};
|
54
source/lib/sysdep/tests/test_sysdep.h
Normal file
54
source/lib/sysdep/tests/test_sysdep.h
Normal file
@ -0,0 +1,54 @@
|
||||
#include <cxxtest/TestSuite.h>
|
||||
|
||||
#include "lib/sysdep/sysdep.h"
|
||||
#include "lib/posix.h" // fminf etc.
|
||||
|
||||
class TestSysdep : public CxxTest::TestSuite
|
||||
{
|
||||
public:
|
||||
void test_float_int()
|
||||
{
|
||||
TS_ASSERT_EQUAL(i32_from_float(0.99999f), 0);
|
||||
TS_ASSERT_EQUAL(i32_from_float(1.0f), 1);
|
||||
TS_ASSERT_EQUAL(i32_from_float(1.01f), 1);
|
||||
TS_ASSERT_EQUAL(i32_from_float(5.6f), 5);
|
||||
|
||||
TS_ASSERT_EQUAL(i32_from_double(0.99999), 0);
|
||||
TS_ASSERT_EQUAL(i32_from_double(1.0), 1);
|
||||
TS_ASSERT_EQUAL(i32_from_double(1.01), 1);
|
||||
TS_ASSERT_EQUAL(i32_from_double(5.6), 5);
|
||||
|
||||
TS_ASSERT_EQUAL(i64_from_double(0.99999), 0LL);
|
||||
TS_ASSERT_EQUAL(i64_from_double(1.0), 1LL);
|
||||
TS_ASSERT_EQUAL(i64_from_double(1.01), 1LL);
|
||||
TS_ASSERT_EQUAL(i64_from_double(5.6), 5LL);
|
||||
}
|
||||
|
||||
void test_round()
|
||||
{
|
||||
TS_ASSERT_EQUAL(rintf(0.99999f), 1.0f);
|
||||
TS_ASSERT_EQUAL(rintf(1.0f), 1.0f);
|
||||
TS_ASSERT_EQUAL(rintf(1.01f), 1.0f);
|
||||
TS_ASSERT_EQUAL(rintf(5.6f), 5.0f);
|
||||
|
||||
TS_ASSERT_EQUAL(rint(0.99999), 1.0);
|
||||
TS_ASSERT_EQUAL(rint(1.0), 1.0);
|
||||
TS_ASSERT_EQUAL(rint(1.01), 1.0);
|
||||
TS_ASSERT_EQUAL(rint(5.6), 5.0);
|
||||
}
|
||||
|
||||
void test_min_max()
|
||||
{
|
||||
TS_ASSERT_EQUAL(fminf(0.0f, 10000.0f), 0.0f);
|
||||
TS_ASSERT_EQUAL(fminf(100.0f, 10000.0f), 100.0f);
|
||||
TS_ASSERT_EQUAL(fminf(-1.0f, 2.0f), -1.0f);
|
||||
TS_ASSERT_EQUAL(fminf(-2.0f, 1.0f), -2.0f);
|
||||
TS_ASSERT_EQUAL(fminf(0.001f, 0.00001f), 0.00001f);
|
||||
|
||||
TS_ASSERT_EQUAL(fmaxf(0.0f, 10000.0f), 10000.0f);
|
||||
TS_ASSERT_EQUAL(fmaxf(100.0f, 10000.0f), 10000.0f);
|
||||
TS_ASSERT_EQUAL(fmaxf(-1.0f, 2.0f), 2.0f);
|
||||
TS_ASSERT_EQUAL(fmaxf(-2.0f, 1.0f), 1.0f);
|
||||
TS_ASSERT_EQUAL(fmaxf(0.001f, 0.00001f), 0.001f);
|
||||
}
|
||||
};
|
28
source/lib/sysdep/win/tests/test_ia32.h
Normal file
28
source/lib/sysdep/win/tests/test_ia32.h
Normal file
@ -0,0 +1,28 @@
|
||||
#include <cxxtest/TestSuite.h>
|
||||
|
||||
#include "lib/sysdep/ia32.h"
|
||||
|
||||
// note: ia32_i??_from_*, ia32_rint*, ia32_fm??f are all tested within
|
||||
// sysdep to avoid test duplication (both the ia32 versions and
|
||||
// the portable fallback must behave the same).
|
||||
|
||||
class TestIA32: public CxxTest::TestSuite
|
||||
{
|
||||
public:
|
||||
void test_rdtsc()
|
||||
{
|
||||
// must increase monotonously
|
||||
const u64 c1 = ia32_rdtsc();
|
||||
const u64 c2 = ia32_rdtsc();
|
||||
const u64 c3 = ia32_rdtsc();
|
||||
TS_ASSERT(c1 < c2 && c2 < c3);
|
||||
}
|
||||
|
||||
void test_ia32_cap()
|
||||
{
|
||||
// make sure the really common/basic caps end up reported as true
|
||||
TS_ASSERT(ia32_cap(IA32_CAP_FPU));
|
||||
TS_ASSERT(ia32_cap(IA32_CAP_TSC));
|
||||
TS_ASSERT(ia32_cap(IA32_CAP_MMX));
|
||||
}
|
||||
};
|
236
source/lib/sysdep/win/tests/test_wdbg_sym.h
Normal file
236
source/lib/sysdep/win/tests/test_wdbg_sym.h
Normal file
@ -0,0 +1,236 @@
|
||||
// note: this is more of an on-demand display of the stack trace than
|
||||
// self-test of it.
|
||||
// TODO: compare against known-good result?
|
||||
// problem: results may differ by compiler (e.g. due to differing STL)
|
||||
|
||||
#include <cxxtest/TestSuite.h>
|
||||
|
||||
#include "lib/debug.h" // no wdbg_sym interface needed
|
||||
|
||||
class TestWdbgSym : public CxxTest::TestSuite
|
||||
{
|
||||
#pragma optimize("", off)
|
||||
|
||||
static void test_array()
|
||||
{
|
||||
struct Small
|
||||
{
|
||||
int i1;
|
||||
int i2;
|
||||
};
|
||||
|
||||
struct Large
|
||||
{
|
||||
double d1;
|
||||
double d2;
|
||||
double d3;
|
||||
double d4;
|
||||
};
|
||||
|
||||
Large large_array_of_large_structs[8] = { { 0.0,0.0,0.0,0.0 } }; UNUSED2(large_array_of_large_structs);
|
||||
Large small_array_of_large_structs[2] = { { 0.0,0.0,0.0,0.0 } }; UNUSED2(small_array_of_large_structs);
|
||||
Small large_array_of_small_structs[8] = { { 1,2 } }; UNUSED2(large_array_of_small_structs);
|
||||
Small small_array_of_small_structs[2] = { { 1,2 } }; UNUSED2(small_array_of_small_structs);
|
||||
|
||||
int ints[] = { 1,2,3,4,5 }; UNUSED2(ints);
|
||||
wchar_t chars[] = { 'w','c','h','a','r','s',0 }; UNUSED2(chars);
|
||||
|
||||
// note: prefer simple error (which also generates stack trace) to
|
||||
// exception, because it is guaranteed to work (no issues with the
|
||||
// debugger swallowing exceptions).
|
||||
DISPLAY_ERROR(L"wdbg_sym self test: check if stack trace below is ok.");
|
||||
//RaiseException(0xf001,0,0,0);
|
||||
}
|
||||
|
||||
// also used by test_stl as an element type
|
||||
struct Nested
|
||||
{
|
||||
int nested_member;
|
||||
struct Nested* self_ptr;
|
||||
};
|
||||
|
||||
static void test_udt()
|
||||
{
|
||||
Nested nested = { 123 }; nested.self_ptr = &nested;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
u8 s1;
|
||||
u8 s2;
|
||||
char s3;
|
||||
}
|
||||
Small;
|
||||
Small small__ = { 0x55, 0xaa, -1 }; UNUSED2(small__);
|
||||
|
||||
struct Large
|
||||
{
|
||||
u8 large_member_u8;
|
||||
std::string large_member_string;
|
||||
double large_member_double;
|
||||
}
|
||||
large = { 0xff, "large struct string", 123456.0 }; UNUSED2(large);
|
||||
|
||||
|
||||
class Base
|
||||
{
|
||||
int base_int;
|
||||
std::wstring base_wstring;
|
||||
public:
|
||||
Base()
|
||||
: base_int(123), base_wstring(L"base wstring")
|
||||
{
|
||||
}
|
||||
};
|
||||
class Derived : private Base
|
||||
{
|
||||
double derived_double;
|
||||
public:
|
||||
Derived()
|
||||
: derived_double(-1.0)
|
||||
{
|
||||
}
|
||||
}
|
||||
derived;
|
||||
|
||||
test_array();
|
||||
}
|
||||
|
||||
// STL containers and their contents
|
||||
static void test_stl()
|
||||
{
|
||||
std::vector<std::wstring> v_wstring;
|
||||
v_wstring.push_back(L"ws1"); v_wstring.push_back(L"ws2");
|
||||
|
||||
std::deque<int> d_int;
|
||||
d_int.push_back(1); d_int.push_back(2); d_int.push_back(3);
|
||||
std::deque<std::string> d_string;
|
||||
d_string.push_back("a"); d_string.push_back("b"); d_string.push_back("c");
|
||||
|
||||
std::list<float> l_float;
|
||||
l_float.push_back(0.1f); l_float.push_back(0.2f); l_float.push_back(0.3f); l_float.push_back(0.4f);
|
||||
|
||||
std::map<std::string, int> m_string_int;
|
||||
m_string_int.insert(std::make_pair<std::string,int>("s5", 5));
|
||||
m_string_int.insert(std::make_pair<std::string,int>("s6", 6));
|
||||
m_string_int.insert(std::make_pair<std::string,int>("s7", 7));
|
||||
std::map<int, std::string> m_int_string;
|
||||
m_int_string.insert(std::make_pair<int,std::string>(1, "s1"));
|
||||
m_int_string.insert(std::make_pair<int,std::string>(2, "s2"));
|
||||
m_int_string.insert(std::make_pair<int,std::string>(3, "s3"));
|
||||
std::map<int, int> m_int_int;
|
||||
m_int_int.insert(std::make_pair<int,int>(1, 1));
|
||||
m_int_int.insert(std::make_pair<int,int>(2, 2));
|
||||
m_int_int.insert(std::make_pair<int,int>(3, 3));
|
||||
|
||||
STL_HASH_MAP<std::string, int> hm_string_int;
|
||||
hm_string_int.insert(std::make_pair<std::string,int>("s5", 5));
|
||||
hm_string_int.insert(std::make_pair<std::string,int>("s6", 6));
|
||||
hm_string_int.insert(std::make_pair<std::string,int>("s7", 7));
|
||||
STL_HASH_MAP<int, std::string> hm_int_string;
|
||||
hm_int_string.insert(std::make_pair<int,std::string>(1, "s1"));
|
||||
hm_int_string.insert(std::make_pair<int,std::string>(2, "s2"));
|
||||
hm_int_string.insert(std::make_pair<int,std::string>(3, "s3"));
|
||||
STL_HASH_MAP<int, int> hm_int_int;
|
||||
hm_int_int.insert(std::make_pair<int,int>(1, 1));
|
||||
hm_int_int.insert(std::make_pair<int,int>(2, 2));
|
||||
hm_int_int.insert(std::make_pair<int,int>(3, 3));
|
||||
|
||||
|
||||
std::set<uintptr_t> s_uintptr;
|
||||
s_uintptr.insert(0x123); s_uintptr.insert(0x456);
|
||||
|
||||
// empty
|
||||
std::deque<u8> d_u8_empty;
|
||||
std::list<Nested> l_nested_empty;
|
||||
std::map<double,double> m_double_empty;
|
||||
std::multimap<int,u8> mm_int_empty;
|
||||
std::set<uint> s_uint_empty;
|
||||
std::multiset<char> ms_char_empty;
|
||||
std::vector<double> v_double_empty;
|
||||
std::queue<double> q_double_empty;
|
||||
std::stack<double> st_double_empty;
|
||||
#if HAVE_STL_HASH
|
||||
STL_HASH_MAP<double,double> hm_double_empty;
|
||||
STL_HASH_MULTIMAP<double,std::wstring> hmm_double_empty;
|
||||
STL_HASH_SET<double> hs_double_empty;
|
||||
STL_HASH_MULTISET<double> hms_double_empty;
|
||||
#endif
|
||||
#if HAVE_STL_SLIST
|
||||
STL_SLIST<double> sl_double_empty;
|
||||
#endif
|
||||
std::string str_empty;
|
||||
std::wstring wstr_empty;
|
||||
|
||||
test_udt();
|
||||
|
||||
// uninitialized
|
||||
std::deque<u8> d_u8_uninit;
|
||||
std::list<Nested> l_nested_uninit;
|
||||
std::map<double,double> m_double_uninit;
|
||||
std::multimap<int,u8> mm_int_uninit;
|
||||
std::set<uint> s_uint_uninit;
|
||||
std::multiset<char> ms_char_uninit;
|
||||
std::vector<double> v_double_uninit;
|
||||
std::queue<double> q_double_uninit;
|
||||
std::stack<double> st_double_uninit;
|
||||
#if HAVE_STL_HASH
|
||||
STL_HASH_MAP<double,double> hm_double_uninit;
|
||||
STL_HASH_MULTIMAP<double,std::wstring> hmm_double_uninit;
|
||||
STL_HASH_SET<double> hs_double_uninit;
|
||||
STL_HASH_MULTISET<double> hms_double_uninit;
|
||||
#endif
|
||||
#if HAVE_STL_SLIST
|
||||
STL_SLIST<double> sl_double_uninit;
|
||||
#endif
|
||||
std::string str_uninit;
|
||||
std::wstring wstr_uninit;
|
||||
}
|
||||
|
||||
|
||||
// also exercises all basic types because we need to display some values
|
||||
// anyway (to see at a glance whether symbol engine addrs are correct)
|
||||
static void test_addrs(int p_int, double p_double, char* p_pchar, uintptr_t p_uintptr)
|
||||
{
|
||||
debug_printf("\nTEST_ADDRS\n");
|
||||
|
||||
uint l_uint = 0x1234;
|
||||
bool l_bool = true; UNUSED2(l_bool);
|
||||
wchar_t l_wchars[] = L"wchar string";
|
||||
enum TestEnum { VAL1=1, VAL2=2 } l_enum = VAL1;
|
||||
u8 l_u8s[] = { 1,2,3,4 };
|
||||
void (*l_funcptr)(void) = test_stl;
|
||||
|
||||
static double s_double = -2.718;
|
||||
static char s_chars[] = {'c','h','a','r','s',0};
|
||||
static void (*s_funcptr)(int, double, char*, uintptr_t) = test_addrs;
|
||||
static void* s_ptr = (void*)(uintptr_t)0x87654321;
|
||||
static HDC s_hdc = (HDC)0xff0;
|
||||
|
||||
debug_printf("p_int addr=%p val=%d\n", &p_int, p_int);
|
||||
debug_printf("p_double addr=%p val=%g\n", &p_double, p_double);
|
||||
debug_printf("p_pchar addr=%p val=%s\n", &p_pchar, p_pchar);
|
||||
debug_printf("p_uintptr addr=%p val=%lu\n", &p_uintptr, p_uintptr);
|
||||
|
||||
debug_printf("l_uint addr=%p val=%u\n", &l_uint, l_uint);
|
||||
debug_printf("l_wchars addr=%p val=%ws\n", &l_wchars, l_wchars);
|
||||
debug_printf("l_enum addr=%p val=%d\n", &l_enum, l_enum);
|
||||
debug_printf("l_u8s addr=%p val=%d\n", &l_u8s, l_u8s);
|
||||
debug_printf("l_funcptr addr=%p val=%p\n", &l_funcptr, l_funcptr);
|
||||
|
||||
test_stl();
|
||||
|
||||
int uninit_int; UNUSED2(uninit_int);
|
||||
float uninit_float; UNUSED2(uninit_float);
|
||||
double uninit_double; UNUSED2(uninit_double);
|
||||
bool uninit_bool; UNUSED2(uninit_bool);
|
||||
HWND uninit_hwnd; UNUSED2(uninit_hwnd);
|
||||
}
|
||||
|
||||
#pragma optimize("", on)
|
||||
|
||||
public:
|
||||
void test()
|
||||
{
|
||||
test_addrs(123, 3.1415926535897932384626, "pchar string", 0xf00d);
|
||||
}
|
||||
};
|
178
source/lib/tests/test_adts.h
Normal file
178
source/lib/tests/test_adts.h
Normal file
@ -0,0 +1,178 @@
|
||||
#include <cxxtest/TestSuite.h>
|
||||
|
||||
class TestRingbuf : public CxxTest::TestSuite
|
||||
{
|
||||
const size_t N = 49; // RingBuf capacity
|
||||
const int S = 100; // number of test items
|
||||
public:
|
||||
void test_insert_remove()
|
||||
{
|
||||
RingBuf<int, N> buf;
|
||||
for(int i = 1; i < S; i++)
|
||||
{
|
||||
buf.push_back(i);
|
||||
TS_ASSERT_EQUAL(buf.front(), i);
|
||||
buf.pop_front();
|
||||
}
|
||||
TS_ASSERT(buf.size() == 0 && buf.empty());
|
||||
}
|
||||
|
||||
void test_fill_overwrite_old()
|
||||
{
|
||||
RingBuf<int, N> buf;
|
||||
for(int i = 1; i < S; i++)
|
||||
buf.push_back(i);
|
||||
TS_ASSERT_EQUAL(buf.size(), N);
|
||||
int first = buf.front();
|
||||
TS_ASSERT_EQUAL(first, (int)(S-1 -N +1));
|
||||
for(size_t i = 0; i < N; i++)
|
||||
{
|
||||
TS_ASSERT_EQUAL(buf.front(), first);
|
||||
first++;
|
||||
buf.pop_front();
|
||||
}
|
||||
TS_ASSERT(buf.size() == 0 && buf.empty());
|
||||
}
|
||||
|
||||
void test_randomized_insert_remove()
|
||||
{
|
||||
srand(1);
|
||||
RingBuf<int, N> buf;
|
||||
std::deque<int> deq;
|
||||
for(uint rep = 0; rep < 1000; rep++)
|
||||
{
|
||||
uint rnd_op = rand(0, 10);
|
||||
// 70% - insert
|
||||
if(rnd_op >= 3)
|
||||
{
|
||||
int item = rand();
|
||||
buf.push_back(item);
|
||||
|
||||
deq.push_back(item);
|
||||
int excess_items = (int)deq.size() - N;
|
||||
if(excess_items > 0)
|
||||
{
|
||||
for(int i = 0; i < excess_items; i++)
|
||||
{
|
||||
deq.pop_front();
|
||||
}
|
||||
}
|
||||
}
|
||||
// 30% - pop front (only if not empty)
|
||||
else if(!deq.empty())
|
||||
{
|
||||
buf.pop_front();
|
||||
deq.pop_front();
|
||||
}
|
||||
}
|
||||
|
||||
TS_ASSERT_EQUAL(buf.size(), deq.size());
|
||||
RingBuf<int, N>::iterator begin = buf.begin(), end = buf.end();
|
||||
TS_ASSERT(equal(begin, end, deq.begin()));
|
||||
}
|
||||
};
|
||||
|
||||
class TestCache: public CxxTest::TestSuite
|
||||
{
|
||||
public:
|
||||
void test_cache_perf()
|
||||
{
|
||||
Cache<int, int, Landlord_Naive> c1;
|
||||
Cache<int, int, Landlord_Naive, Divider_Recip> c1r;
|
||||
Cache<int, int, Landlord_Cached> c2;
|
||||
Cache<int, int, Landlord_Cached, Divider_Recip> c2r;
|
||||
Cache<int, int, Landlord_Lazy> c3;
|
||||
Cache<int, int, Landlord_Lazy, Divider_Recip> c3r;
|
||||
|
||||
#if defined(ENABLE_CACHE_POLICY_BENCHMARK) || 0
|
||||
// set max priority, to reduce interference while measuring.
|
||||
int old_policy; static sched_param old_param; // (static => 0-init)
|
||||
pthread_getschedparam(pthread_self(), &old_policy, &old_param);
|
||||
static sched_param max_param;
|
||||
max_param.sched_priority = sched_get_priority_max(SCHED_FIFO);
|
||||
pthread_setschedparam(pthread_self(), SCHED_FIFO, &max_param);
|
||||
|
||||
#define MEASURE(c, desc)\
|
||||
{\
|
||||
srand(1);\
|
||||
int cnt = 1;\
|
||||
TIMER_BEGIN(desc);\
|
||||
for(int i = 0; i < 30000; i++)\
|
||||
{\
|
||||
/* 70% add (random objects) */\
|
||||
bool add = rand(1,10) < 7;\
|
||||
if(add)\
|
||||
{\
|
||||
int key = cnt++;\
|
||||
int val = cnt++;\
|
||||
size_t size = (size_t)rand(1,100);\
|
||||
uint cost = (uint)rand(1,100);\
|
||||
c.add(key, val, size, cost);\
|
||||
}\
|
||||
else\
|
||||
{\
|
||||
size_t size;\
|
||||
int value;\
|
||||
c.remove_least_valuable(&value, &size);\
|
||||
}\
|
||||
}\
|
||||
TIMER_END(desc);\
|
||||
}
|
||||
MEASURE(c1, "naive")
|
||||
MEASURE(c1r, "naiverecip")
|
||||
MEASURE(c2, "cached")
|
||||
MEASURE(c2r, "cachedrecip")
|
||||
MEASURE(c3, "lazy")
|
||||
MEASURE(c3r, "lazyrecip")
|
||||
|
||||
// restore previous policy and priority.
|
||||
pthread_setschedparam(pthread_self(), old_policy, &old_param);
|
||||
exit(1134);
|
||||
#endif
|
||||
}
|
||||
|
||||
// ensures all 3 variants of Landlord<> behave the same
|
||||
void test_cache_policies()
|
||||
{
|
||||
Cache<int, int, Landlord_Naive > c1;
|
||||
Cache<int, int, Landlord_Cached> c2;
|
||||
Cache<int, int, Landlord_Lazy > c3;
|
||||
|
||||
srand(1);
|
||||
int cnt = 1;
|
||||
for(int i = 0; i < 1000; i++)
|
||||
{
|
||||
// 70% add (random objects)
|
||||
bool add = rand(1,10) < 7;
|
||||
if(add)
|
||||
{
|
||||
int key = cnt++;
|
||||
int val = cnt++;
|
||||
size_t size = (size_t)rand(1,100);
|
||||
uint cost = (uint)rand(1,100);
|
||||
c1.add(key, val, size, cost);
|
||||
c2.add(key, val, size, cost);
|
||||
c3.add(key, val, size, cost);
|
||||
}
|
||||
// 30% delete - make sure "least valuable" was same for all
|
||||
else
|
||||
{
|
||||
size_t size1, size2, size3;
|
||||
int value1, value2, value3;
|
||||
bool removed1, removed2, removed3;
|
||||
removed1 = c1.remove_least_valuable(&value1, &size1);
|
||||
removed2 = c2.remove_least_valuable(&value2, &size2);
|
||||
removed3 = c3.remove_least_valuable(&value3, &size3);
|
||||
TS_ASSERT_EQUAL(removed1, removed2);
|
||||
TS_ASSERT_EQUAL(removed2, removed3);
|
||||
if (removed1)
|
||||
{
|
||||
TS_ASSERT_EQUAL(size1, size2);
|
||||
TS_ASSERT_EQUAL(value1, value2);
|
||||
TS_ASSERT_EQUAL(size2, size3);
|
||||
TS_ASSERT_EQUAL(value2, value3);
|
||||
}
|
||||
} // else
|
||||
} // for i
|
||||
}
|
||||
};
|
44
source/lib/tests/test_allocators.h
Normal file
44
source/lib/tests/test_allocators.h
Normal file
@ -0,0 +1,44 @@
|
||||
#include <cxxtest/TestSuite.h>
|
||||
|
||||
class TestAllocators : public CxxTest::TestSuite
|
||||
{
|
||||
public:
|
||||
void test_da()
|
||||
{
|
||||
DynArray da;
|
||||
|
||||
// basic test of functionality (not really meaningful)
|
||||
TS_ASSERT_OK(da_alloc(&da, 1000));
|
||||
TS_ASSERT_OK(da_set_size(&da, 1000));
|
||||
TS_ASSERT_OK(da_set_prot(&da, PROT_NONE));
|
||||
TS_ASSERT_OK(da_free(&da));
|
||||
|
||||
// test wrapping existing mem blocks for use with da_read
|
||||
u8 data[4] = { 0x12, 0x34, 0x56, 0x78 };
|
||||
TS_ASSERT_OK(da_wrap_fixed(&da, data, sizeof(data)));
|
||||
u8 buf[4];
|
||||
TS_ASSERT_OK(da_read(&da, buf, 4));
|
||||
TS_ASSERT_EQUAL(read_le32(buf), 0x78563412); // read correct value
|
||||
TS_ASSERT(da_read(&da, buf, 1) < 0); // no more data left
|
||||
TS_ASSERT_OK(da_free(&da));
|
||||
}
|
||||
|
||||
void test_expand()
|
||||
{
|
||||
}
|
||||
|
||||
void test_matrix()
|
||||
{
|
||||
// not much we can do here; allocate a matrix, write to it and
|
||||
// make sure it can be freed.
|
||||
// (note: can't check memory layout because "matrix" is int** -
|
||||
// array of pointers. the matrix interface doesn't guarantee
|
||||
// that data comes in row-major order after the row pointers)
|
||||
int** m = (int**)matrix_alloc(3, 3, sizeof(int));
|
||||
m[0][0] = 1;
|
||||
m[0][1] = 2;
|
||||
m[1][0] = 3;
|
||||
m[2][2] = 4;
|
||||
matrix_free((void**)m);
|
||||
}
|
||||
};
|
38
source/lib/tests/test_byte_order.h
Normal file
38
source/lib/tests/test_byte_order.h
Normal file
@ -0,0 +1,38 @@
|
||||
#include <cxxtest/TestSuite.h>
|
||||
|
||||
class TestByteOrder : public CxxTest::TestSuite
|
||||
{
|
||||
public:
|
||||
void test()
|
||||
{
|
||||
const u32 x = 0x01234567u;
|
||||
const u8 LS_byte = *(u8*)&x;
|
||||
// little endian
|
||||
if(LS_byte, 0x67)
|
||||
{
|
||||
TS_ASSERT_EQUAL(to_le16(0x0123u), 0x0123u);
|
||||
TS_ASSERT_EQUAL(to_le32(0x01234567u), 0x01234567u);
|
||||
TS_ASSERT_EQUAL(to_le64(0x0123456789ABCDEFu), 0x0123456789ABCDEFu);
|
||||
|
||||
TS_ASSERT_EQUAL(to_be16(0x0123u), 0x2301u);
|
||||
TS_ASSERT_EQUAL(to_be32(0x01234567u), 0x67452301u);
|
||||
TS_ASSERT_EQUAL(to_be64(0x0123456789ABCDEFu), 0xEFCDAB8967452301u);
|
||||
}
|
||||
// big endian
|
||||
else if(LS_byte, 0x01)
|
||||
{
|
||||
TS_ASSERT_EQUAL(to_le16(0x0123u), 0x2301u);
|
||||
TS_ASSERT_EQUAL(to_le32(0x01234567u), 0x67452301u);
|
||||
TS_ASSERT_EQUAL(to_le64(0x0123456789ABCDEFu), 0xEFCDAB8967452301u);
|
||||
|
||||
TS_ASSERT_EQUAL(to_be16(0x0123u), 0x0123u);
|
||||
TS_ASSERT_EQUAL(to_be32(0x01234567u), 0x01234567u);
|
||||
TS_ASSERT_EQUAL(to_be64(0x0123456789ABCDEFu), 0x0123456789ABCDEFu);
|
||||
}
|
||||
else
|
||||
TS_FAIL("endian determination failed");
|
||||
|
||||
// note: no need to test read_?e* / write_?e* - they are
|
||||
// trivial wrappers on top of to_?e*.
|
||||
}
|
||||
};
|
231
source/lib/tests/test_lib.h
Normal file
231
source/lib/tests/test_lib.h
Normal file
@ -0,0 +1,231 @@
|
||||
#include <cxxtest/TestSuite.h>
|
||||
|
||||
#include "lib/self_test.h"
|
||||
#include "lib/lib.h"
|
||||
|
||||
class TestLib : public CxxTest::TestSuite
|
||||
{
|
||||
public:
|
||||
void test_fnv_hash()
|
||||
{
|
||||
TS_ASSERT_EQUAL(fnv_hash(""), 0x811C9DC5u); // verify initial value
|
||||
const u32 h1 = fnv_hash("abcdef");
|
||||
TS_ASSERT_EQUAL(h1, 0xFF478A2A); // verify value for simple string
|
||||
TS_ASSERT_EQUAL(fnv_hash ("abcdef", 6), h1); // same result if hashing buffer
|
||||
TS_ASSERT_EQUAL(fnv_lc_hash("ABcDeF", 6), h1); // same result if case differs
|
||||
|
||||
TS_ASSERT_EQUAL(fnv_hash64(""), 0xCBF29CE484222325ull); // verify initial value
|
||||
const u64 h2 = fnv_hash("abcdef");
|
||||
TS_ASSERT_EQUAL(h2, 0xD80BDA3FBE244A0Aull); // verify value for simple string
|
||||
TS_ASSERT_EQUAL(fnv_hash64("abcdef", 6), h2); // same result if hashing buffer
|
||||
}
|
||||
|
||||
void test_is_pow2()
|
||||
{
|
||||
TS_ASSERT_EQUAL(is_pow2(0u), false);
|
||||
TS_ASSERT_EQUAL(is_pow2(~0u), false);
|
||||
TS_ASSERT_EQUAL(is_pow2(0x80000001), false);
|
||||
TS_ASSERT_EQUAL(is_pow2(1), true);
|
||||
TS_ASSERT_EQUAL(is_pow2(1u << 31), true);
|
||||
}
|
||||
|
||||
void test_ilog2()
|
||||
{
|
||||
TS_ASSERT_EQUAL(ilog2(0u), -1);
|
||||
TS_ASSERT_EQUAL(ilog2(3u), -1);
|
||||
TS_ASSERT_EQUAL(ilog2(0xffffffffu), -1);
|
||||
TS_ASSERT_EQUAL(ilog2(1u), 0);
|
||||
TS_ASSERT_EQUAL(ilog2(256u), 8);
|
||||
TS_ASSERT_EQUAL(ilog2(0x80000000u), 31);
|
||||
}
|
||||
|
||||
void test_log2()
|
||||
{
|
||||
TS_ASSERT_EQUAL(log2(0u), 1u);
|
||||
TS_ASSERT_EQUAL(log2(3u), 2u);
|
||||
TS_ASSERT_EQUAL(log2(0xffffffffu), 32u);
|
||||
TS_ASSERT_EQUAL(log2(1u), 0u);
|
||||
TS_ASSERT_EQUAL(log2(256u), 8u);
|
||||
TS_ASSERT_EQUAL(log2(0x80000000u), 31u);
|
||||
}
|
||||
|
||||
void test_ilog2f()
|
||||
{
|
||||
TS_ASSERT_EQUAL(ilog2(0.f), 0);
|
||||
TS_ASSERT_EQUAL(ilog2(1.f), 0);
|
||||
TS_ASSERT_EQUAL(ilog2(3.f), 1);
|
||||
TS_ASSERT_EQUAL(ilog2(256.f), 8);
|
||||
}
|
||||
|
||||
void test_round_next_pow2()
|
||||
{
|
||||
TS_ASSERT_EQUAL(round_up_to_pow2(0u), 1u);
|
||||
TS_ASSERT_EQUAL(round_up_to_pow2(1u), 2u);
|
||||
TS_ASSERT_EQUAL(round_up_to_pow2(127u), 128u);
|
||||
TS_ASSERT_EQUAL(round_up_to_pow2(128u), 128u);
|
||||
TS_ASSERT_EQUAL(round_up_to_pow2(129u), 256u);
|
||||
}
|
||||
|
||||
void test_round_up()
|
||||
{
|
||||
TS_ASSERT_EQUAL(round_up( 0u, 16u), 0u);
|
||||
TS_ASSERT_EQUAL(round_up( 4u, 16u), 16u);
|
||||
TS_ASSERT_EQUAL(round_up(15u, 16u), 16u);
|
||||
TS_ASSERT_EQUAL(round_up(20u, 32u), 32u);
|
||||
TS_ASSERT_EQUAL(round_up(29u, 32u), 32u);
|
||||
TS_ASSERT_EQUAL(round_up(0x1000u, 0x1000u), 0x1000u);
|
||||
TS_ASSERT_EQUAL(round_up(0x1001u, 0x1000u), 0x2000u);
|
||||
TS_ASSERT_EQUAL(round_up(0x1900u, 0x1000u), 0x2000u);
|
||||
}
|
||||
|
||||
void test_round_down()
|
||||
{
|
||||
TS_ASSERT_EQUAL(round_down( 0u, 16u), 0u);
|
||||
TS_ASSERT_EQUAL(round_down( 4u, 16u), 0u);
|
||||
TS_ASSERT_EQUAL(round_down(15u, 16u), 0u);
|
||||
TS_ASSERT_EQUAL(round_down(20u, 16u), 16u);
|
||||
TS_ASSERT_EQUAL(round_down(29u, 16u), 16u);
|
||||
TS_ASSERT_EQUAL(round_down(0x1900u, 0x1000u), 0x1000u);
|
||||
TS_ASSERT_EQUAL(round_down(0x2001u, 0x2000u), 0x2000u);
|
||||
}
|
||||
|
||||
// 16-bit saturating arithmetic
|
||||
void test_addusw()
|
||||
{
|
||||
TS_ASSERT_EQUAL(addusw(4u, 0x100u), 0x0104u);
|
||||
TS_ASSERT_EQUAL(addusw(0u, 0xFFFFu), 0xFFFFu);
|
||||
TS_ASSERT_EQUAL(addusw(0x8000u, 0x8000u), 0xFFFFu);
|
||||
TS_ASSERT_EQUAL(addusw(0xFFF0u, 0x0004u), 0xFFF4u);
|
||||
TS_ASSERT_EQUAL(addusw(0xFFFFu, 0xFFFFu), 0xFFFFu);
|
||||
}
|
||||
|
||||
void test_subusw()
|
||||
{
|
||||
TS_ASSERT_EQUAL(subusw(4u, 0x100u), 0u);
|
||||
TS_ASSERT_EQUAL(subusw(100u, 90u), 10u);
|
||||
TS_ASSERT_EQUAL(subusw(0x8000u, 0x8000u), 0u);
|
||||
TS_ASSERT_EQUAL(subusw(0x0FFFu, 0xFFFFu), 0u);
|
||||
TS_ASSERT_EQUAL(subusw(0xFFFFu, 0x0FFFu), 0xF000u);
|
||||
}
|
||||
|
||||
void test_movzx()
|
||||
{
|
||||
const char d1[] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08 };
|
||||
const char d2[] = { 0x43, 0x12, 0x23, 0xA4 };
|
||||
TS_ASSERT_EQUAL(movzx_64le(d1, 1), 0x01ull);
|
||||
TS_ASSERT_EQUAL(movzx_64le(d1, 2), 0x0201ull);
|
||||
TS_ASSERT_EQUAL(movzx_64le(d1, 8), 0x0807060504030201ull);
|
||||
TS_ASSERT_EQUAL(movzx_64le(d2, 4), 0xA4231243ull);
|
||||
TS_ASSERT_EQUAL(movzx_64le(d2+3, 1), 0xA4ull);
|
||||
}
|
||||
|
||||
void test_movsx()
|
||||
{
|
||||
const char d1[] = { 0x09, 0xFE };
|
||||
const char d2[] = { 0xD9, 0x2C, 0xDD, 0x8F };
|
||||
const char d3[] = { 0x92, 0x26, 0x88, 0xF1, 0x35, 0xAC, 0x01, 0x83 };
|
||||
TS_ASSERT_EQUAL(movsx_64le(d1, 1), 0x09ull);
|
||||
TS_ASSERT_EQUAL(movsx_64le(d1, 2), 0xFFFFFFFFFFFFFE09ull);
|
||||
TS_ASSERT_EQUAL(movsx_64le(d2, 4), 0xFFFFFFFF8FDD2CD9ull);
|
||||
TS_ASSERT_EQUAL(movsx_64le(d3, 8), 0x8301AC35F1882692ull);
|
||||
}
|
||||
|
||||
void test_hi_lo()
|
||||
{
|
||||
TS_ASSERT_EQUAL(u64_hi(0x0123456789ABCDEFull), 0x01234567u);
|
||||
TS_ASSERT_EQUAL(u64_hi(0x0000000100000002ull), 0x00000001u);
|
||||
|
||||
TS_ASSERT_EQUAL(u64_lo(0x0123456789ABCDEFull), 0x89ABCDEFu);
|
||||
TS_ASSERT_EQUAL(u64_lo(0x0000000100000002ull), 0x00000002u);
|
||||
|
||||
TS_ASSERT_EQUAL(u32_hi(0x01234567u), 0x0123u);
|
||||
TS_ASSERT_EQUAL(u32_hi(0x00000001u), 0x0000u);
|
||||
|
||||
TS_ASSERT_EQUAL(u32_lo(0x01234567u), 0x4567u);
|
||||
TS_ASSERT_EQUAL(u32_lo(0x00000001u), 0x0001u);
|
||||
|
||||
TS_ASSERT_EQUAL(u64_from_u32(0xFFFFFFFFu, 0x80000008u), 0xFFFFFFFF80000008ull);
|
||||
TS_ASSERT_EQUAL(u32_from_u16(0x8000u, 0xFFFFu), 0x8000FFFFu);
|
||||
}
|
||||
|
||||
// fp_to_u?? already validate the result.
|
||||
|
||||
void test_wildcard()
|
||||
{
|
||||
TS_ASSERT_EQUAL(match_wildcard("", ""), 1);
|
||||
TS_ASSERT_EQUAL(match_wildcard("a", 0), 1); // NULL matches everything
|
||||
|
||||
TS_ASSERT_EQUAL(match_wildcard("abc", "abc") , 1); // direct match
|
||||
TS_ASSERT_EQUAL(match_wildcard("abc", "???") , 1); // only ?
|
||||
TS_ASSERT_EQUAL(match_wildcard("abc", "*" ) , 1); // only *
|
||||
|
||||
TS_ASSERT_EQUAL(match_wildcard("ab" , "a?" ) , 1); // trailing ?
|
||||
TS_ASSERT_EQUAL(match_wildcard("abc", "a?c") , 1); // middle ?
|
||||
TS_ASSERT_EQUAL(match_wildcard("abc", "?bc") , 1); // leading ?
|
||||
|
||||
TS_ASSERT_EQUAL(match_wildcard("abc", "a*" ) , 1); // trailing *
|
||||
TS_ASSERT_EQUAL(match_wildcard("abcdef", "ab*ef"), 1); // middle *
|
||||
TS_ASSERT_EQUAL(match_wildcard("abcdef", "*f" ), 1); // leading *
|
||||
|
||||
TS_ASSERT_EQUAL(match_wildcard("abcdef", "a?cd*"), 1); // ? and *
|
||||
TS_ASSERT_EQUAL(match_wildcard("abcdef", "a*d?f"), 1); // * and ?
|
||||
TS_ASSERT_EQUAL(match_wildcard("abcdef", "a*d*" ), 1); // multiple *
|
||||
|
||||
// unicode test pasted from the above; keep in sync!
|
||||
|
||||
TS_ASSERT_EQUAL(match_wildcardw(L"", L""), 1);
|
||||
TS_ASSERT_EQUAL(match_wildcardw(L"a", 0), 1); // NULL matches everything
|
||||
|
||||
TS_ASSERT_EQUAL(match_wildcardw(L"abc", L"abc") , 1); // direct match
|
||||
TS_ASSERT_EQUAL(match_wildcardw(L"abc", L"???") , 1); // only ?
|
||||
TS_ASSERT_EQUAL(match_wildcardw(L"abc", L"*" ) , 1); // only *
|
||||
|
||||
TS_ASSERT_EQUAL(match_wildcardw(L"ab" , L"a?" ) , 1); // trailing ?
|
||||
TS_ASSERT_EQUAL(match_wildcardw(L"abc", L"a?c") , 1); // middle ?
|
||||
TS_ASSERT_EQUAL(match_wildcardw(L"abc", L"?bc") , 1); // leading ?
|
||||
|
||||
TS_ASSERT_EQUAL(match_wildcardw(L"abc", L"a*" ) , 1); // trailing *
|
||||
TS_ASSERT_EQUAL(match_wildcardw(L"abcdef", L"ab*ef"), 1); // middle *
|
||||
TS_ASSERT_EQUAL(match_wildcardw(L"abcdef", L"*f" ), 1); // leading *
|
||||
|
||||
TS_ASSERT_EQUAL(match_wildcardw(L"abcdef", L"a?cd*"), 1); // ? and *
|
||||
TS_ASSERT_EQUAL(match_wildcardw(L"abcdef", L"a*d?f"), 1); // * and ?
|
||||
TS_ASSERT_EQUAL(match_wildcardw(L"abcdef", L"a*d*" ), 1); // multiple *
|
||||
}
|
||||
|
||||
void test_base32()
|
||||
{
|
||||
// compare against previous output (generated via this base32() call)
|
||||
const u8 in[] = { 0x12, 0x57, 0x85, 0xA2, 0xF9, 0x41, 0xCD, 0x57, 0xF3 };
|
||||
u8 out[20] = {0};
|
||||
base32(ARRAY_SIZE(in), in, out);
|
||||
const u8 correct_out[] = "CJLYLIXZI";
|
||||
TS_ASSERT_SAME_DATA(out, correct_out, ARRAY_SIZE(correct_out));
|
||||
}
|
||||
|
||||
void test_rand()
|
||||
{
|
||||
// complain if huge interval or min > max
|
||||
TS_ASSERT_EQUAL(rand(1, 0), 0);
|
||||
TS_ASSERT_EQUAL(rand(2, ~0u), 0);
|
||||
|
||||
// returned number must be in [min, max)
|
||||
for(int i = 0; i < 100; i++)
|
||||
{
|
||||
uint min = rand(), max = min+rand();
|
||||
uint x = rand(min, max);
|
||||
TS_ASSERT(min <= x && x < max);
|
||||
}
|
||||
|
||||
// make sure both possible values are hit
|
||||
uint ones = 0, twos = 0;
|
||||
for(int i = 0; i < 100; i++)
|
||||
{
|
||||
uint x = rand(1, 3);
|
||||
// paranoia: don't use array (x might not be 1 or 2 - checked below)
|
||||
if(x, 1) ones++; if(x, 2) twos++;
|
||||
}
|
||||
TS_ASSERT_EQUAL(ones+twos, 100);
|
||||
TS_ASSERT(ones > 10 && twos > 10);
|
||||
}
|
||||
};
|
211
source/lib/tests/test_lockfree.h
Normal file
211
source/lib/tests/test_lockfree.h
Normal file
@ -0,0 +1,211 @@
|
||||
#include <cxxtest/TestSuite.h>
|
||||
|
||||
#include "lib/self_test.h"
|
||||
#include "lib/posix.h"
|
||||
#include "lib/lockfree.h"
|
||||
|
||||
// make sure the data structures work at all; doesn't test thread-safety.
|
||||
class TestLockfreeBasic : public CxxTest::TestSuite
|
||||
{
|
||||
public:
|
||||
void test_basic_single_threaded()
|
||||
{
|
||||
void* user_data;
|
||||
|
||||
const uint ENTRIES = 50;
|
||||
// should be more than max # retired nodes to test release..() code
|
||||
uintptr_t key = 0x1000;
|
||||
uint sig = 10;
|
||||
|
||||
LFList list;
|
||||
TS_ASSERT_OK(lfl_init(&list));
|
||||
|
||||
LFHash hash;
|
||||
TS_ASSERT_OK(lfh_init(&hash, 8));
|
||||
|
||||
// add some entries; store "signatures" (ascending int values)
|
||||
for(uint i = 0; i < ENTRIES; i++)
|
||||
{
|
||||
int was_inserted;
|
||||
|
||||
user_data = lfl_insert(&list, key+i, sizeof(int), &was_inserted);
|
||||
TS_ASSERT(user_data != 0 && was_inserted);
|
||||
*(uint*)user_data = sig+i;
|
||||
|
||||
user_data = lfh_insert(&hash, key+i, sizeof(int), &was_inserted);
|
||||
TS_ASSERT(user_data != 0 && was_inserted);
|
||||
*(uint*)user_data = sig+i;
|
||||
}
|
||||
|
||||
// make sure all "signatures" are present in list
|
||||
for(uint i = 0; i < ENTRIES; i++)
|
||||
{
|
||||
user_data = lfl_find(&list, key+i);
|
||||
TS_ASSERT(user_data != 0);
|
||||
TS_ASSERT_EQUAL(*(uint*)user_data, sig+i);
|
||||
|
||||
user_data = lfh_find(&hash, key+i);
|
||||
TS_ASSERT(user_data != 0);
|
||||
TS_ASSERT_EQUAL(*(uint*)user_data, sig+i);
|
||||
}
|
||||
|
||||
lfl_free(&list);
|
||||
lfh_free(&hash);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// known to fail on P4 due to mem reordering and lack of membars.
|
||||
class TestMultithread : public CxxTest::TestSuite
|
||||
{
|
||||
// poor man's synchronization "barrier"
|
||||
bool is_complete;
|
||||
intptr_t num_active_threads;
|
||||
|
||||
LFList list;
|
||||
LFHash hash;
|
||||
|
||||
typedef std::set<uintptr_t> KeySet;
|
||||
typedef KeySet::const_iterator KeySetIt;
|
||||
KeySet keys;
|
||||
pthread_mutex_t mutex; // protects <keys>
|
||||
|
||||
static void* thread_func(void* arg)
|
||||
{
|
||||
debug_set_thread_name("LF_test");
|
||||
|
||||
const uintptr_t thread_number = (uintptr_t)arg;
|
||||
|
||||
atomic_add(&num_active_threads, 1);
|
||||
|
||||
// chosen randomly every iteration (int_value % 4)
|
||||
enum TestAction
|
||||
{
|
||||
TA_FIND = 0,
|
||||
TA_INSERT = 1,
|
||||
TA_ERASE = 2,
|
||||
TA_SLEEP = 3
|
||||
};
|
||||
static const char* const action_strings[] =
|
||||
{
|
||||
"find", "insert", "erase", "sleep"
|
||||
};
|
||||
|
||||
while(!is_complete)
|
||||
{
|
||||
void* user_data;
|
||||
|
||||
const int action = rand(0, 4);
|
||||
const uintptr_t key = rand(0, 100);
|
||||
const int sleep_duration_ms = rand(0, 100);
|
||||
debug_printf("thread %d: %s\n", thread_number, action_strings[action]);
|
||||
|
||||
//
|
||||
pthread_mutex_lock(&mutex);
|
||||
const bool was_in_set = keys.find(key) != keys.end();
|
||||
if(action == TA_INSERT)
|
||||
keys.insert(key);
|
||||
else if(action == TA_ERASE)
|
||||
keys.erase(key);
|
||||
pthread_mutex_unlock(&mutex);
|
||||
|
||||
switch(action)
|
||||
{
|
||||
case TA_FIND:
|
||||
{
|
||||
user_data = lfl_find(&list, key);
|
||||
TS_ASSERT(was_in_set == (user_data != 0));
|
||||
if(user_data)
|
||||
TS_ASSERT_EQUAL(*(uintptr_t*)user_data, ~key);
|
||||
|
||||
user_data = lfh_find(&hash, key);
|
||||
// typical failure site if lockfree data structure has bugs.
|
||||
TS_ASSERT(was_in_set == (user_data != 0));
|
||||
if(user_data)
|
||||
TS_ASSERT_EQUAL(*(uintptr_t*)user_data, ~key);
|
||||
}
|
||||
break;
|
||||
|
||||
case TA_INSERT:
|
||||
{
|
||||
int was_inserted;
|
||||
|
||||
user_data = lfl_insert(&list, key, sizeof(uintptr_t), &was_inserted);
|
||||
TS_ASSERT(user_data != 0); // only triggers if out of memory
|
||||
*(uintptr_t*)user_data = ~key; // checked above
|
||||
TS_ASSERT(was_in_set == !was_inserted);
|
||||
|
||||
user_data = lfh_insert(&hash, key, sizeof(uintptr_t), &was_inserted);
|
||||
TS_ASSERT(user_data != 0); // only triggers if out of memory
|
||||
*(uintptr_t*)user_data = ~key; // checked above
|
||||
TS_ASSERT(was_in_set == !was_inserted);
|
||||
}
|
||||
break;
|
||||
|
||||
case TA_ERASE:
|
||||
{
|
||||
int err;
|
||||
|
||||
err = lfl_erase(&list, key);
|
||||
TS_ASSERT(was_in_set == (err == ERR_OK));
|
||||
|
||||
err = lfh_erase(&hash, key);
|
||||
TS_ASSERT(was_in_set == (err == ERR_OK));
|
||||
}
|
||||
break;
|
||||
|
||||
case TA_SLEEP:
|
||||
usleep(sleep_duration_ms*1000);
|
||||
break;
|
||||
|
||||
default:
|
||||
TS_FAIL(L"invalid TA_* action");
|
||||
break;
|
||||
} // switch
|
||||
} // while !is_complete
|
||||
|
||||
atomic_add(&num_active_threads, -1);
|
||||
TS_ASSERT(num_active_threads >= 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public:
|
||||
TestMultithread()
|
||||
: is_complete(false), num_active_threads(0),
|
||||
list(), hash(),
|
||||
mutex(0) {}
|
||||
|
||||
void test_multithread()
|
||||
{
|
||||
// this test is randomized; we need deterministic results.
|
||||
srand(1);
|
||||
|
||||
static const double TEST_LENGTH = 30.; // [seconds]
|
||||
const double end_time = get_time() + TEST_LENGTH;
|
||||
is_complete = false;
|
||||
|
||||
TS_ASSERT_OK(lfl_init(&list));
|
||||
TS_ASSERT_OK(lfh_init(&hash, 128));
|
||||
TS_ASSERT_OK(pthread_mutex_init(&mutex, 0));
|
||||
|
||||
// spin off test threads (many, to force preemption)
|
||||
const uint NUM_THREADS = 16;
|
||||
for(uintptr_t i = 0; i < NUM_THREADS; i++)
|
||||
pthread_create(0, 0, thread_func, (void*)i);
|
||||
|
||||
// wait until time interval elapsed (if we get that far, all is well).
|
||||
while(get_time() < end_time)
|
||||
usleep(10*1000);
|
||||
|
||||
// signal and wait for all threads to complete (poor man's barrier -
|
||||
// those aren't currently implemented in wpthread).
|
||||
is_complete = true;
|
||||
while(num_active_threads > 0)
|
||||
usleep(5*1000);
|
||||
|
||||
lfl_free(&list);
|
||||
lfh_free(&hash);
|
||||
TS_ASSERT_OK(pthread_mutex_destroy(&mutex));
|
||||
}
|
||||
};
|
215
source/lib/tests/test_path_util.h
Normal file
215
source/lib/tests/test_path_util.h
Normal file
@ -0,0 +1,215 @@
|
||||
#include <cxxtest/TestSuite.h>
|
||||
|
||||
#include "lib/self_test.h"
|
||||
#include "lib/path_util.h"
|
||||
|
||||
class TestPathUtil : public CxxTest::TestSuite
|
||||
{
|
||||
void TEST_APPEND(const char* path1, const char* path2, uint flags, const char* correct_result)
|
||||
{
|
||||
char dst[PATH_MAX] = {0};
|
||||
TS_ASSERT_OK(path_append(dst, path1, path2, flags));
|
||||
TS_ASSERT_STR_EQUAL(dst, correct_result);
|
||||
}
|
||||
|
||||
// if correct_ret is ERR_FAIL, ignore correct_result.
|
||||
void TEST_REPLACE(const char* src, const char* remove, const char* replace,
|
||||
LibError correct_ret, const char* correct_result)
|
||||
{
|
||||
char dst[PATH_MAX] = {0};
|
||||
TS_ASSERT_EQUAL(path_replace(dst, src, remove, replace), correct_ret);
|
||||
if(correct_ret != ERR_FAIL)
|
||||
TS_ASSERT_STR_EQUAL(dst, correct_result);
|
||||
}
|
||||
|
||||
void TEST_NAME_ONLY(const char* path, const char* correct_result)
|
||||
{
|
||||
const char* result = path_name_only(path);
|
||||
TS_ASSERT_STR_EQUAL(result, correct_result);
|
||||
}
|
||||
|
||||
void TEST_LAST_COMPONENT(const char* path, const char* correct_result)
|
||||
{
|
||||
const char* result = path_last_component(path);
|
||||
TS_ASSERT_STR_EQUAL(result, correct_result);
|
||||
}
|
||||
|
||||
void TEST_STRIP_FN(const char* path_readonly, const char* correct_result)
|
||||
{
|
||||
char path[PATH_MAX];
|
||||
path_copy(path, path_readonly);
|
||||
path_strip_fn(path);
|
||||
TS_ASSERT_STR_EQUAL(path, correct_result);
|
||||
}
|
||||
|
||||
void TEST_PATH_EXT(const char* path, const char* correct_result)
|
||||
{
|
||||
const char* result = path_extension(path);
|
||||
TS_ASSERT_STR_EQUAL(result, correct_result);
|
||||
}
|
||||
|
||||
void TEST_PATH_PACKAGE(const char* path, const char* fn,
|
||||
const char* correct_result)
|
||||
{
|
||||
PathPackage pp;
|
||||
TS_ASSERT_OK(path_package_set_dir(&pp, path));
|
||||
TS_ASSERT_OK(path_package_append_file(&pp, fn));
|
||||
TS_ASSERT_STR_EQUAL(pp.path, correct_result);
|
||||
}
|
||||
|
||||
|
||||
public:
|
||||
|
||||
void test_subpath()
|
||||
{
|
||||
// obvious true
|
||||
TS_ASSERT(path_is_subpath("abc/def/", "abc/def/") == true); // same
|
||||
TS_ASSERT(path_is_subpath("abc/def/", "abc/") == true); // 2 is subpath
|
||||
TS_ASSERT(path_is_subpath("abc/", "abc/def/") == true); // 1 is subpath
|
||||
|
||||
// nonobvious true
|
||||
TS_ASSERT(path_is_subpath("", "") == true);
|
||||
TS_ASSERT(path_is_subpath("abc/def/", "abc/def") == true); // no '/' !
|
||||
|
||||
// obvious false
|
||||
TS_ASSERT(path_is_subpath("abc", "def") == false); // different, no path
|
||||
|
||||
// nonobvious false
|
||||
TS_ASSERT(path_is_subpath("abc", "") == false); // empty comparand
|
||||
// .. different but followed by common subdir
|
||||
TS_ASSERT(path_is_subpath("abc/def/", "ghi/def/") == false);
|
||||
TS_ASSERT(path_is_subpath("abc/def/", "abc/ghi") == false);
|
||||
}
|
||||
|
||||
// TODO: can't test path validate yet without suppress-error-dialog
|
||||
|
||||
void test_append()
|
||||
{
|
||||
// simplest case
|
||||
TEST_APPEND("abc", "def", 0, "abc/def");
|
||||
// trailing slash
|
||||
TEST_APPEND("abc", "def", PATH_APPEND_SLASH, "abc/def/");
|
||||
// intervening slash
|
||||
TEST_APPEND("abc/", "def", 0, "abc/def");
|
||||
// nonportable intervening slash
|
||||
TEST_APPEND("abc\\", "def", 0, "abc\\def");
|
||||
// mixed path slashes
|
||||
TEST_APPEND("abc", "def/ghi\\jkl", 0, "abc/def/ghi\\jkl");
|
||||
// path1 empty
|
||||
TEST_APPEND("", "abc/def/", 0, "abc/def/");
|
||||
// path2 empty, no trailing slash
|
||||
TEST_APPEND("abc/def", "", 0, "abc/def");
|
||||
// path2 empty, require trailing slash
|
||||
TEST_APPEND("abc/def", "", PATH_APPEND_SLASH, "abc/def/");
|
||||
// require trailing slash, already exists
|
||||
TEST_APPEND("abc/", "def/", PATH_APPEND_SLASH, "abc/def/");
|
||||
}
|
||||
|
||||
void test_replace()
|
||||
{
|
||||
// no match
|
||||
TEST_REPLACE("abc/def", "/def", "xx", ERR_FAIL, 0);
|
||||
// normal case: match and remove
|
||||
TEST_REPLACE("abc/def", "abc", "ok", ERR_OK, "ok/def");
|
||||
// caller also stripping /
|
||||
TEST_REPLACE("abc/def", "abc/", "ok", ERR_OK, "ok/def");
|
||||
// empty remove
|
||||
TEST_REPLACE("abc/def", "", "ok", ERR_OK, "ok/abc/def");
|
||||
// empty replace
|
||||
TEST_REPLACE("abc/def", "abc", "", ERR_OK, "def");
|
||||
// remove entire string
|
||||
TEST_REPLACE("abc/def", "abc/def", "", ERR_OK, "");
|
||||
}
|
||||
|
||||
void test_name_only()
|
||||
{
|
||||
// path with filename
|
||||
TEST_NAME_ONLY("abc/def", "def");
|
||||
// nonportable path with filename
|
||||
TEST_NAME_ONLY("abc\\def\\ghi", "ghi");
|
||||
// mixed path with filename
|
||||
TEST_NAME_ONLY("abc/def\\ghi", "ghi");
|
||||
// mixed path with filename (2)
|
||||
TEST_NAME_ONLY("abc\\def/ghi", "ghi");
|
||||
// filename only
|
||||
TEST_NAME_ONLY("abc", "abc");
|
||||
// empty
|
||||
TEST_NAME_ONLY("", "");
|
||||
}
|
||||
|
||||
void test_last_component()
|
||||
{
|
||||
// path with filename
|
||||
TEST_LAST_COMPONENT("abc/def", "def");
|
||||
// nonportable path with filename
|
||||
TEST_LAST_COMPONENT("abc\\def\\ghi", "ghi");
|
||||
// mixed path with filename
|
||||
TEST_LAST_COMPONENT("abc/def\\ghi", "ghi");
|
||||
// mixed path with filename (2)
|
||||
TEST_LAST_COMPONENT("abc\\def/ghi", "ghi");
|
||||
// filename only
|
||||
TEST_LAST_COMPONENT("abc", "abc");
|
||||
// empty
|
||||
TEST_LAST_COMPONENT("", "");
|
||||
|
||||
// now paths (mostly copied from above test series)
|
||||
|
||||
// path
|
||||
TEST_LAST_COMPONENT("abc/def/", "def");
|
||||
// nonportable path
|
||||
TEST_LAST_COMPONENT("abc\\def\\ghi\\", "ghi");
|
||||
// mixed path
|
||||
TEST_LAST_COMPONENT("abc/def\\ghi/", "ghi");
|
||||
// mixed path (2)
|
||||
TEST_LAST_COMPONENT("abc\\def/ghi\\", "ghi");
|
||||
}
|
||||
|
||||
void test_strip_fn()
|
||||
{
|
||||
// path with filename
|
||||
TEST_STRIP_FN("abc/def", "abc/");
|
||||
// nonportable path with filename
|
||||
TEST_STRIP_FN("abc\\def\\ghi", "abc\\def\\");
|
||||
// mixed path with filename
|
||||
TEST_STRIP_FN("abc/def\\ghi", "abc/def\\");
|
||||
// mixed path with filename (2)
|
||||
TEST_STRIP_FN("abc\\def/ghi", "abc\\def/");
|
||||
// filename only
|
||||
TEST_STRIP_FN("abc", "");
|
||||
// empty
|
||||
TEST_STRIP_FN("", "");
|
||||
// path
|
||||
TEST_LAST_COMPONENT("abc/def/", "");
|
||||
// nonportable path
|
||||
TEST_LAST_COMPONENT("abc\\def\\ghi\\", "");
|
||||
}
|
||||
|
||||
// note: no need to test path_dir_only - it is implemented exactly as
|
||||
// done in TEST_STRIP_FN.
|
||||
|
||||
void test_path_ext()
|
||||
{
|
||||
TEST_PATH_EXT("a/b/c.bmp", "bmp");
|
||||
TEST_PATH_EXT("a.BmP", "BmP"); // case sensitive
|
||||
TEST_PATH_EXT("c", ""); // no extension
|
||||
TEST_PATH_EXT("", ""); // empty
|
||||
}
|
||||
|
||||
// testing path_foreach_component is difficult; currently skipped.
|
||||
|
||||
void test_path_package()
|
||||
{
|
||||
// normal
|
||||
TEST_PATH_PACKAGE("a/b", "c", "a/b/c");
|
||||
// nonportable slash
|
||||
TEST_PATH_PACKAGE("a\\b", "c", "a\\b/c");
|
||||
// slash already present
|
||||
TEST_PATH_PACKAGE("a/b/", "c", "a/b/c");
|
||||
// nonportable slash already present
|
||||
TEST_PATH_PACKAGE("a\\b\\", "c", "a\\b\\c");
|
||||
// mixed slashes
|
||||
TEST_PATH_PACKAGE("a/b\\c", "d", "a/b\\c/d");
|
||||
// mixed slashes (2)
|
||||
TEST_PATH_PACKAGE("a\\b/c", "d", "a\\b/c/d");
|
||||
}
|
||||
};
|
196
source/lib/tests/test_string_s.h
Normal file
196
source/lib/tests/test_string_s.h
Normal file
@ -0,0 +1,196 @@
|
||||
#include <cxxtest/TestSuite.h>
|
||||
|
||||
#include "lib/string_s.h"
|
||||
|
||||
class TestString_s : public CxxTest::TestSuite
|
||||
{
|
||||
// note: avoid 4-byte strings - they would trigger WARN_IF_PTR_LEN.
|
||||
|
||||
const tchar* const s0;
|
||||
const tchar* const s1;
|
||||
const tchar* const s5;
|
||||
const tchar* const s10;
|
||||
|
||||
tchar d1[1];
|
||||
tchar d2[2];
|
||||
tchar d3[3];
|
||||
tchar d5[5];
|
||||
tchar d6[6];
|
||||
tchar d10[10];
|
||||
tchar d11[11];
|
||||
|
||||
tchar no_null[7];
|
||||
|
||||
|
||||
static void TEST_LEN(const char* string, size_t limit, size_t expected)
|
||||
{
|
||||
TS_ASSERT_EQUAL(tnlen((string), (limit)), (expected));
|
||||
}
|
||||
|
||||
static void TEST_CPY(char* dst, size_t dst_max, const char* src,
|
||||
int expected_ret, const char* expected_dst)
|
||||
{
|
||||
int ret = tcpy_s((dst), dst_max, (src));
|
||||
TS_ASSERT_EQUAL(ret, expected_ret);
|
||||
if(dst != 0)
|
||||
TS_ASSERT(!tcmp(dst, T(expected_dst)));
|
||||
}
|
||||
|
||||
static void TEST_CPY2(char* dst, const char* src,
|
||||
int expected_ret, const char* expected_dst)
|
||||
{
|
||||
int ret = tcpy_s((dst), ARRAY_SIZE(dst), (src));
|
||||
TS_ASSERT_EQUAL(ret, expected_ret);
|
||||
if(dst != 0)
|
||||
TS_ASSERT(!tcmp(dst, T(expected_dst)));
|
||||
}
|
||||
|
||||
static void TEST_NCPY(char* dst, const char* src, size_t max_src_chars,
|
||||
int expected_ret, const char* expected_dst)
|
||||
{
|
||||
int ret = tncpy_s((dst), ARRAY_SIZE(dst), (src), (max_src_chars));
|
||||
TS_ASSERT_EQUAL(ret, expected_ret);
|
||||
if(dst != 0)
|
||||
TS_ASSERT(!tcmp(dst, T(expected_dst)));
|
||||
}
|
||||
|
||||
static void TEST_CAT(char* dst, size_t dst_max, const char* src,
|
||||
int expected_ret, const char expected_dst)
|
||||
{
|
||||
int ret = tcat_s((dst), dst_max, (src));
|
||||
TS_ASSERT_EQUAL(ret, expected_ret);
|
||||
if(dst != 0)
|
||||
TS_ASSERT(!tcmp(dst, T(expected_dst)));
|
||||
}
|
||||
|
||||
static void TEST_CAT2(char* dst, const char* dst_val, const char* src,
|
||||
int expected_ret, const char* expected_dst)
|
||||
{
|
||||
tcpy(dst, T(dst_val));
|
||||
int ret = tcat_s((dst), ARRAY_SIZE(dst), (src));
|
||||
TS_ASSERT_EQUAL(ret, expected_ret);
|
||||
if(dst != 0)
|
||||
TS_ASSERT(!tcmp(dst, T(expected_dst)));
|
||||
}
|
||||
|
||||
static void TEST_NCAT(char* dst, const char* dst_val,
|
||||
const char* src, size_t max_src_chars,
|
||||
int expected_ret, const char* expected_dst)
|
||||
{
|
||||
tcpy(dst, T(dst_val));
|
||||
int ret = tncat_s((dst), ARRAY_SIZE(dst), (src), (max_src_chars));
|
||||
TS_ASSERT_EQUAL(ret, expected_ret);
|
||||
if(dst != 0)
|
||||
TS_ASSERT(!tcmp(dst, T(expected_dst)));
|
||||
}
|
||||
|
||||
public:
|
||||
TestString_s()
|
||||
: s0(T("")), s1(T("a")), s5(T("abcde")), s10(T("abcdefghij"))
|
||||
{
|
||||
const tchar no_null_[] = { '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) do not.
|
||||
// since we have no control over their error reporting (which ends up taking
|
||||
// down the program), we must skip this part of the test if using them.
|
||||
// this is still preferable to completely disabling the self-test.
|
||||
void test_param_validation()
|
||||
{
|
||||
#if !HAVE_STRING_S
|
||||
TEST_CPY(0 ,0,0 , EINVAL,""); // all invalid
|
||||
TEST_CPY(0 ,0,s1, EINVAL,""); // dst = 0, max = 0
|
||||
TEST_CPY(0 ,1,s1, EINVAL,""); // dst = 0, max > 0
|
||||
TEST_CPY(d1,1,0 , EINVAL,""); // src = 0
|
||||
TEST_CPY(d1,0,s1, ERANGE,""); // max_dst_chars = 0
|
||||
|
||||
TEST_CPY2(d1 ,s1, ERANGE,"");
|
||||
TEST_CPY2(d1 ,s5, ERANGE,"");
|
||||
TEST_CPY2(d5 ,s5, ERANGE,"");
|
||||
|
||||
TEST_NCPY(d1 ,s1,1, ERANGE,"");
|
||||
TEST_NCPY(d1 ,s5,1, ERANGE,"");
|
||||
TEST_NCPY(d5 ,s5,5, ERANGE,"");
|
||||
|
||||
TEST_CAT(0 ,0,0 , EINVAL,""); // all invalid
|
||||
TEST_CAT(0 ,0,s1, EINVAL,""); // dst = 0, max = 0
|
||||
TEST_CAT(0 ,1,s1, EINVAL,""); // dst = 0, max > 0
|
||||
TEST_CAT(d1,1,0 , EINVAL,""); // src = 0
|
||||
TEST_CAT(d1,0,s1, ERANGE,""); // max_dst_chars = 0
|
||||
TEST_CAT(no_null,5,s1, ERANGE,""); // dst not terminated
|
||||
|
||||
TEST_CAT2(d1 ,"" ,s1, ERANGE,"");
|
||||
TEST_CAT2(d1 ,"" ,s5, ERANGE,"");
|
||||
TEST_CAT2(d10,"" ,s10, ERANGE,""); // empty, total overflow
|
||||
TEST_CAT2(d10,"12345",s5 , ERANGE,""); // not empty, overflow
|
||||
TEST_CAT2(d10,"12345",s10, ERANGE,""); // not empty, total overflow
|
||||
|
||||
TEST_NCAT(d1 ,"" ,s1,1, ERANGE,"");
|
||||
TEST_NCAT(d1 ,"" ,s5,5, ERANGE,"");
|
||||
TEST_NCAT(d10,"" ,s10,10, ERANGE,""); // empty, total overflow
|
||||
TEST_NCAT(d10,"12345",s5 ,5 , ERANGE,""); // not empty, overflow
|
||||
TEST_NCAT(d10,"12345",s10,10, ERANGE,""); // not empty, total overflow
|
||||
#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 ,s1, 0,"a");
|
||||
TEST_CPY2(d6 ,s5, 0,"abcde");
|
||||
TEST_CPY2(d11,s5, 0,"abcde");
|
||||
|
||||
TEST_NCPY(d2 ,s1,1, 0,"a");
|
||||
TEST_NCPY(d6 ,s5,5, 0,"abcde");
|
||||
TEST_NCPY(d11,s5,5, 0,"abcde");
|
||||
|
||||
tcpy(d5, T("----"));
|
||||
TEST_NCPY(d5,s5,0 , 0,""); // specified behavior! see 3.6.2.1.1 #4
|
||||
TEST_NCPY(d5,s5,1 , 0,"a");
|
||||
TEST_NCPY(d5,s5,4 , 0,"abcd");
|
||||
TEST_NCPY(d6,s5,5 , 0,"abcde");
|
||||
TEST_NCPY(d6,s5,10, 0,"abcde");
|
||||
}
|
||||
|
||||
|
||||
void test_concatenate()
|
||||
{
|
||||
TEST_CAT2(d3 ,"1",s1, 0,"1a");
|
||||
TEST_CAT2(d5 ,"1",s1, 0,"1a");
|
||||
TEST_CAT2(d6 ,"" ,s5, 0,"abcde");
|
||||
TEST_CAT2(d10,"" ,s5, 0,"abcde");
|
||||
TEST_CAT2(d10,"1234" ,s5 , 0,"1234abcde");
|
||||
|
||||
TEST_NCAT(d3 ,"1",s1,1, 0,"1a");
|
||||
TEST_NCAT(d5 ,"1",s1,1, 0,"1a");
|
||||
TEST_NCAT(d6 ,"" ,s5,5, 0,"abcde");
|
||||
TEST_NCAT(d10,"" ,s5,5, 0,"abcde");
|
||||
TEST_NCAT(d10,"1234" ,s5 ,5 , 0,"1234abcde");
|
||||
|
||||
TEST_NCAT(d5,"----",s5,0 , 0,"----");
|
||||
TEST_NCAT(d5,"",s5,1 , 0,"a");
|
||||
TEST_NCAT(d5,"",s5,4 , 0,"abcd");
|
||||
TEST_NCAT(d5,"12",s5,2 , 0,"12ab");
|
||||
TEST_NCAT(d6,"",s5,10, 0,"abcde");
|
||||
}
|
||||
};
|
64
source/maths/tests/test_Matrix3d.h
Normal file
64
source/maths/tests/test_Matrix3d.h
Normal file
@ -0,0 +1,64 @@
|
||||
#include <cxxtest/TestSuite.h>
|
||||
|
||||
#include "Maths/Matrix3D.h"
|
||||
|
||||
class TestMatrix : public CxxTest::TestSuite
|
||||
{
|
||||
public:
|
||||
void test_inverse()
|
||||
{
|
||||
CMatrix3D m;
|
||||
srand(0);
|
||||
for (int i = 0; i < 4; ++i)
|
||||
{
|
||||
for (int j = 0; j < 16; ++j)
|
||||
{
|
||||
m._data[j] = -1.0f + 2.0f*(rand()/(float)RAND_MAX);
|
||||
}
|
||||
CMatrix3D n;
|
||||
m.GetInverse(n);
|
||||
m *= n;
|
||||
// verify identity has 1s on diagonal and 0 otherwise
|
||||
for (int x = 0; x < 4; ++x)
|
||||
{
|
||||
for (int y = 0; y < 4; ++y)
|
||||
{
|
||||
const float expected = (x==y)? 1.0f : 0.0f;
|
||||
TS_ASSERT_DELTA(m(x,y), expected, 0.0001f);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void test_quats()
|
||||
{
|
||||
srand(0);
|
||||
for (int i = 0; i < 4; ++i)
|
||||
{
|
||||
CQuaternion q;
|
||||
q.FromEulerAngles(
|
||||
-6.28f + 12.56f*(rand()/(float)RAND_MAX),
|
||||
-6.28f + 12.56f*(rand()/(float)RAND_MAX),
|
||||
-6.28f + 12.56f*(rand()/(float)RAND_MAX)
|
||||
);
|
||||
CMatrix3D m;
|
||||
q.ToMatrix(m);
|
||||
CQuaternion q2 = m.GetRotation();
|
||||
|
||||
// I hope there's a good reason why they're sometimes negated, and
|
||||
// it's not just a bug...
|
||||
const bool ok_oneway =
|
||||
feq(q2.m_W, q.m_W) &&
|
||||
feq(q2.m_V.X, q.m_V.X) &&
|
||||
feq(q2.m_V.Y, q.m_V.Y) &&
|
||||
feq(q2.m_V.Z, q.m_V.Z);
|
||||
const bool ok_otherway =
|
||||
feq(q2.m_W, -q.m_W) &&
|
||||
feq(q2.m_V.X, -q.m_V.X) &&
|
||||
feq(q2.m_V.Y, -q.m_V.Y) &&
|
||||
feq(q2.m_V.Z, -q.m_V.Z);
|
||||
TS_ASSERT(ok_oneway ^ ok_otherway);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
56
source/ps/XML/tests/test_XMLWriter.h
Normal file
56
source/ps/XML/tests/test_XMLWriter.h
Normal file
@ -0,0 +1,56 @@
|
||||
#include <cxxtest/TestSuite.h>
|
||||
|
||||
#include "ps/xml/XML.h"
|
||||
|
||||
class TestXmlWriter : public CxxTest::TestSuite
|
||||
{
|
||||
public:
|
||||
void test1()
|
||||
{
|
||||
XML_Start("utf-8");
|
||||
XML_Doctype("Scenario", "/maps/scenario.dtd");
|
||||
|
||||
{
|
||||
XML_Element("Scenario");
|
||||
{
|
||||
XML_Comment("Comment test.");
|
||||
XML_Comment("Comment test again.");
|
||||
{
|
||||
XML_Element("a");
|
||||
XML_Attribute("one", 1);
|
||||
XML_Attribute("two", "TWO");
|
||||
XML_Text("b");
|
||||
XML_Text(" (etc)");
|
||||
}
|
||||
{
|
||||
XML_Element("c");
|
||||
XML_Text("d");
|
||||
}
|
||||
XML_Setting("c2", "d2");
|
||||
{
|
||||
XML_Element("e");
|
||||
{
|
||||
{
|
||||
XML_Element("f");
|
||||
XML_Text("g");
|
||||
}
|
||||
{
|
||||
XML_Element("h");
|
||||
}
|
||||
{
|
||||
XML_Element("i");
|
||||
XML_Attribute("j", 1.23);
|
||||
{
|
||||
XML_Element("k");
|
||||
XML_Attribute("l", 2.34);
|
||||
XML_Text("m");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// For this test to be useful, it should actually test something.
|
||||
}
|
||||
};
|
18
source/ps/tests/test_CStr.h
Normal file
18
source/ps/tests/test_CStr.h
Normal file
@ -0,0 +1,18 @@
|
||||
#include <cxxtest/TestSuite.h>
|
||||
|
||||
#include "ps/CStr.h"
|
||||
|
||||
class TestCStr : public CxxTest::TestSuite
|
||||
{
|
||||
public:
|
||||
void test_utf8_utf16_conversion()
|
||||
{
|
||||
const wchar_t chr_utf16[] = { 0x12, 0xff, 0x1234, 0x3456, 0x5678, 0x7890, 0x9abc, 0xbcde, 0xfffe };
|
||||
const unsigned char chr_utf8[] = { 0x12, 0xc3, 0xbf, 0xe1, 0x88, 0xb4, 0xe3, 0x91, 0x96, 0xe5, 0x99, 0xb8, 0xe7, 0xa2, 0x90, 0xe9, 0xaa, 0xbc, 0xeb, 0xb3, 0x9e, 0xef, 0xbf, 0xbe };
|
||||
CStrW str_utf16 (chr_utf16, sizeof(chr_utf16)/sizeof(wchar_t));
|
||||
CStr8 str_utf8 = str_utf16.ToUTF8();
|
||||
TS_ASSERT_EQUAL(str_utf8.length(), sizeof(chr_utf8));
|
||||
TS_ASSERT_SAME_DATA(str_utf8.data(), chr_utf8, sizeof(chr_utf8));
|
||||
TS_ASSERT_EQUAL(str_utf8.FromUTF8(), str_utf16);
|
||||
}
|
||||
};
|
93
source/ps/tests/test_Parser.h
Normal file
93
source/ps/tests/test_Parser.h
Normal file
@ -0,0 +1,93 @@
|
||||
#include <cxxtest/TestSuite.h>
|
||||
|
||||
class TestParser : public CxxTest::TestSuite
|
||||
{
|
||||
public:
|
||||
void test1()
|
||||
{
|
||||
CParser Parser;
|
||||
Parser.InputTaskType("test", "_$ident_=_$value_");
|
||||
|
||||
std::string str;
|
||||
int i;
|
||||
|
||||
CParserLine Line;
|
||||
|
||||
TS_ASSERT(Line.ParseString(Parser, "value=23"));
|
||||
|
||||
TS_ASSERT(Line.GetArgString(0, str) && str == "value");
|
||||
TS_ASSERT(Line.GetArgInt(1, i) && i == 23);
|
||||
}
|
||||
|
||||
|
||||
void test2()
|
||||
{
|
||||
CParser Parser;
|
||||
Parser.InputTaskType("test", "_$value_[$value]_");
|
||||
|
||||
std::string str;
|
||||
|
||||
CParserLine Line;
|
||||
|
||||
TS_ASSERT(Line.ParseString(Parser, "12 34"));
|
||||
TS_ASSERT_EQUAL(Line.GetArgCount(), 2);
|
||||
TS_ASSERT(Line.GetArgString(0, str) && str == "12");
|
||||
TS_ASSERT(Line.GetArgString(1, str) && str == "34");
|
||||
|
||||
TS_ASSERT(Line.ParseString(Parser, "56"));
|
||||
TS_ASSERT_EQUAL(Line.GetArgCount(), 1);
|
||||
TS_ASSERT(Line.GetArgString(0, str) && str == "56");
|
||||
|
||||
TS_ASSERT(! Line.ParseString(Parser, " "));
|
||||
}
|
||||
|
||||
|
||||
void test3()
|
||||
{
|
||||
CParser Parser;
|
||||
Parser.InputTaskType("test", "_[$value]_[$value]_[$value]_");
|
||||
|
||||
std::string str;
|
||||
|
||||
CParserLine Line;
|
||||
|
||||
TS_ASSERT(Line.ParseString(Parser, "12 34 56"));
|
||||
TS_ASSERT_EQUAL(Line.GetArgCount(), 3);
|
||||
TS_ASSERT(Line.GetArgString(0, str) && str == "12");
|
||||
TS_ASSERT(Line.GetArgString(1, str) && str == "34");
|
||||
TS_ASSERT(Line.GetArgString(2, str) && str == "56");
|
||||
|
||||
TS_ASSERT(Line.ParseString(Parser, "78 90"));
|
||||
TS_ASSERT_EQUAL(Line.GetArgCount(), 2);
|
||||
TS_ASSERT(Line.GetArgString(0, str) && str == "78");
|
||||
TS_ASSERT(Line.GetArgString(1, str) && str == "90");
|
||||
|
||||
TS_ASSERT(Line.ParseString(Parser, "ab"));
|
||||
TS_ASSERT_EQUAL(Line.GetArgCount(), 1);
|
||||
TS_ASSERT(Line.GetArgString(0, str) && str == "ab");
|
||||
|
||||
TS_ASSERT(Line.ParseString(Parser, " "));
|
||||
TS_ASSERT_EQUAL(Line.GetArgCount(), 0);
|
||||
}
|
||||
|
||||
|
||||
void test4()
|
||||
{
|
||||
CParser Parser;
|
||||
Parser.InputTaskType("test", "<[_a_][_b_]_x_>");
|
||||
|
||||
std::string str;
|
||||
|
||||
CParserLine Line;
|
||||
TS_ASSERT(Line.ParseString(Parser, "a b x a b x"));
|
||||
TS_ASSERT(Line.ParseString(Parser, "a x b x"));
|
||||
TS_ASSERT(Line.ParseString(Parser, "a x"));
|
||||
TS_ASSERT(Line.ParseString(Parser, "b x"));
|
||||
TS_ASSERT(Line.ParseString(Parser, "x"));
|
||||
TS_ASSERT(! Line.ParseString(Parser, "a x c x"));
|
||||
TS_ASSERT(! Line.ParseString(Parser, "a b a x"));
|
||||
TS_ASSERT(! Line.ParseString(Parser, "a"));
|
||||
TS_ASSERT(! Line.ParseString(Parser, "a a x"));
|
||||
TS_ASSERT(Line.ParseString(Parser, "a x a b x a x b x b x b x b x a x a x a b x a b x b x a x"));
|
||||
}
|
||||
};
|
Loading…
Reference in New Issue
Block a user