diff options
Diffstat (limited to 'src/unittest')
-rw-r--r-- | src/unittest/CMakeLists.txt | 1 | ||||
-rw-r--r-- | src/unittest/test.cpp | 18 | ||||
-rw-r--r-- | src/unittest/test.h | 91 | ||||
-rw-r--r-- | src/unittest/test_areastore.cpp | 69 | ||||
-rw-r--r-- | src/unittest/test_collision.cpp | 32 | ||||
-rw-r--r-- | src/unittest/test_serialization.cpp | 263 | ||||
-rw-r--r-- | src/unittest/test_threading.cpp | 182 | ||||
-rw-r--r-- | src/unittest/test_utilities.cpp | 35 |
8 files changed, 601 insertions, 90 deletions
diff --git a/src/unittest/CMakeLists.txt b/src/unittest/CMakeLists.txt index bdff14f05..a07ed8ba5 100644 --- a/src/unittest/CMakeLists.txt +++ b/src/unittest/CMakeLists.txt @@ -17,6 +17,7 @@ set (UNITTEST_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/test_serialization.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_settings.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_socket.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/test_threading.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_utilities.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_voxelalgorithms.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_voxelmanipulator.cpp diff --git a/src/unittest/test.cpp b/src/unittest/test.cpp index d0ffb423f..41ccf0d2d 100644 --- a/src/unittest/test.cpp +++ b/src/unittest/test.cpp @@ -19,7 +19,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "test.h" -#include "debug.h" #include "log.h" #include "nodedef.h" #include "itemdef.h" @@ -216,14 +215,14 @@ void TestGameDef::defineSomeNodes() //// run_tests //// -void run_tests() +bool run_tests() { - DSTACK(__FUNCTION_NAME); + DSTACK(FUNCTION_NAME); u32 t1 = porting::getTime(PRECISION_MILLI); TestGameDef gamedef; - log_set_lev_silence(LMT_ERROR, true); + g_logger.setLevelSilenced(LL_ERROR, true); u32 num_modules_failed = 0; u32 num_total_tests_failed = 0; @@ -239,11 +238,11 @@ void run_tests() u32 tdiff = porting::getTime(PRECISION_MILLI) - t1; - log_set_lev_silence(LMT_ERROR, false); + g_logger.setLevelSilenced(LL_ERROR, false); const char *overall_status = (num_modules_failed == 0) ? "PASSED" : "FAILED"; - dstream + rawstream << "++++++++++++++++++++++++++++++++++++++++" << "++++++++++++++++++++++++++++++++++++++++" << std::endl << "Unit Test Results: " << overall_status << std::endl @@ -254,8 +253,7 @@ void run_tests() << "++++++++++++++++++++++++++++++++++++++++" << "++++++++++++++++++++++++++++++++++++++++" << std::endl; - if (num_modules_failed) - abort(); + return num_modules_failed; } //// @@ -264,14 +262,14 @@ void run_tests() bool TestBase::testModule(IGameDef *gamedef) { - dstream << "======== Testing module " << getName() << std::endl; + rawstream << "======== Testing module " << getName() << std::endl; u32 t1 = porting::getTime(PRECISION_MILLI); runTests(gamedef); u32 tdiff = porting::getTime(PRECISION_MILLI) - t1; - dstream << "======== Module " << getName() << " " + rawstream << "======== Module " << getName() << " " << (num_tests_failed ? "failed" : "passed") << " (" << num_tests_failed << " failures / " << num_tests_run << " tests) - " << tdiff << "ms" << std::endl; diff --git a/src/unittest/test.h b/src/unittest/test.h index 47a441e02..e60e657cc 100644 --- a/src/unittest/test.h +++ b/src/unittest/test.h @@ -32,60 +32,61 @@ class TestFailedException : public std::exception { }; // Runs a unit test and reports results -#define TEST(fxn, ...) do { \ - u32 t1 = porting::getTime(PRECISION_MILLI); \ - try { \ - fxn(__VA_ARGS__); \ - dstream << "[PASS] "; \ - } catch (TestFailedException &e) { \ - dstream << "[FAIL] "; \ - num_tests_failed++; \ - } catch (std::exception &e) { \ - dstream << "Caught unhandled exception: " << e.what() << std::endl; \ - dstream << "[FAIL] "; \ - num_tests_failed++; \ - } \ - num_tests_run++; \ - u32 tdiff = porting::getTime(PRECISION_MILLI) - t1; \ - dstream << #fxn << " - " << tdiff << "ms" << std::endl; \ +#define TEST(fxn, ...) do { \ + u32 t1 = porting::getTime(PRECISION_MILLI); \ + try { \ + fxn(__VA_ARGS__); \ + rawstream << "[PASS] "; \ + } catch (TestFailedException &e) { \ + rawstream << "[FAIL] "; \ + num_tests_failed++; \ + } catch (std::exception &e) { \ + rawstream << "Caught unhandled exception: " << e.what() << std::endl; \ + rawstream << "[FAIL] "; \ + num_tests_failed++; \ + } \ + num_tests_run++; \ + u32 tdiff = porting::getTime(PRECISION_MILLI) - t1; \ + rawstream << #fxn << " - " << tdiff << "ms" << std::endl; \ } while (0) // Asserts the specified condition is true, or fails the current unit test -#define UASSERT(x) do { \ - if (!(x)) { \ - dstream << "Test assertion failed: " #x << std::endl \ - << " at " << fs::GetFilenameFromPath(__FILE__) \ - << ":" << __LINE__ << std::endl; \ - throw TestFailedException(); \ - } \ +#define UASSERT(x) do { \ + if (!(x)) { \ + rawstream << "Test assertion failed: " #x << std::endl \ + << " at " << fs::GetFilenameFromPath(__FILE__) \ + << ":" << __LINE__ << std::endl; \ + throw TestFailedException(); \ + } \ } while (0) // Asserts the specified condition is true, or fails the current unit test // and prints the format specifier fmt -#define UTEST(x, fmt, ...) do { \ - if (!(x)) { \ - char utest_buf[1024]; \ - snprintf(utest_buf, sizeof(utest_buf), fmt, __VA_ARGS__); \ - dstream << "Test assertion failed: " << utest_buf << std::endl \ - << " at " << fs::GetFilenameFromPath(__FILE__) \ - << ":" << __LINE__ << std::endl; \ - throw TestFailedException(); \ - } \ +#define UTEST(x, fmt, ...) do { \ + if (!(x)) { \ + char utest_buf[1024]; \ + snprintf(utest_buf, sizeof(utest_buf), fmt, __VA_ARGS__); \ + rawstream << "Test assertion failed: " << utest_buf << std::endl \ + << " at " << fs::GetFilenameFromPath(__FILE__) \ + << ":" << __LINE__ << std::endl; \ + throw TestFailedException(); \ + } \ } while (0) // Asserts the comparison specified by CMP is true, or fails the current unit test -#define UASSERTCMP(T, CMP, actual, expected) do { \ - T a = (actual); \ - T e = (expected); \ - if (!(a CMP e)) { \ - dstream << "Test assertion failed: " << #actual << " " << #CMP << " " \ - << #expected << std::endl \ - << " at " << fs::GetFilenameFromPath(__FILE__) << ":" \ - << __LINE__ << std::endl \ - << " actual: " << a << std::endl << " expected: " \ - << e << std::endl; \ - throw TestFailedException(); \ - } \ +#define UASSERTCMP(T, CMP, actual, expected) do { \ + T a = (actual); \ + T e = (expected); \ + if (!(a CMP e)) { \ + rawstream \ + << "Test assertion failed: " << #actual << " " << #CMP << " " \ + << #expected << std::endl \ + << " at " << fs::GetFilenameFromPath(__FILE__) << ":" \ + << __LINE__ << std::endl \ + << " actual: " << a << std::endl << " expected: " \ + << e << std::endl; \ + throw TestFailedException(); \ + } \ } while (0) #define UASSERTEQ(T, actual, expected) UASSERTCMP(T, ==, actual, expected) @@ -141,6 +142,6 @@ extern content_t t_CONTENT_WATER; extern content_t t_CONTENT_LAVA; extern content_t t_CONTENT_BRICK; -void run_tests(); +bool run_tests(); #endif diff --git a/src/unittest/test_areastore.cpp b/src/unittest/test_areastore.cpp index a0dcada94..62d446f5c 100644 --- a/src/unittest/test_areastore.cpp +++ b/src/unittest/test_areastore.cpp @@ -19,7 +19,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "test.h" -#include "areastore.h" +#include "util/areastore.h" class TestAreaStore : public TestBase { public: @@ -31,6 +31,7 @@ public: void genericStoreTest(AreaStore *store); void testVectorStore(); void testSpatialStore(); + void testSerialization(); }; static TestAreaStore g_test_instance; @@ -41,6 +42,7 @@ void TestAreaStore::runTests(IGameDef *gamedef) #if USE_SPATIAL TEST(testSpatialStore); #endif + TEST(testSerialization); } //////////////////////////////////////////////////////////////////////////////// @@ -62,18 +64,15 @@ void TestAreaStore::testSpatialStore() void TestAreaStore::genericStoreTest(AreaStore *store) { Area a(v3s16(-10, -3, 5), v3s16(0, 29, 7)); - a.id = 1; Area b(v3s16(-5, -2, 5), v3s16(0, 28, 6)); - b.id = 2; Area c(v3s16(-7, -3, 6), v3s16(-1, 27, 7)); - c.id = 3; std::vector<Area *> res; UASSERTEQ(size_t, store->size(), 0); store->reserve(2); // sic - store->insertArea(a); - store->insertArea(b); - store->insertArea(c); + store->insertArea(&a); + store->insertArea(&b); + store->insertArea(&c); UASSERTEQ(size_t, store->size(), 3); store->getAreasForPos(&res, v3s16(-1, 0, 6)); @@ -81,20 +80,18 @@ void TestAreaStore::genericStoreTest(AreaStore *store) res.clear(); store->getAreasForPos(&res, v3s16(0, 0, 7)); UASSERTEQ(size_t, res.size(), 1); - UASSERTEQ(u32, res[0]->id, 1); res.clear(); - store->removeArea(1); + store->removeArea(a.id); store->getAreasForPos(&res, v3s16(0, 0, 7)); UASSERTEQ(size_t, res.size(), 0); res.clear(); - store->insertArea(a); + store->insertArea(&a); store->getAreasForPos(&res, v3s16(0, 0, 7)); UASSERTEQ(size_t, res.size(), 1); - UASSERTEQ(u32, res[0]->id, 1); res.clear(); store->getAreasInArea(&res, v3s16(-10, -3, 5), v3s16(0, 29, 7), false); @@ -109,21 +106,57 @@ void TestAreaStore::genericStoreTest(AreaStore *store) UASSERTEQ(size_t, res.size(), 3); res.clear(); - store->removeArea(1); - store->removeArea(2); - store->removeArea(3); + store->removeArea(a.id); + store->removeArea(b.id); + store->removeArea(c.id); Area d(v3s16(-100, -300, -200), v3s16(-50, -200, -100)); - d.id = 4; d.data = "Hi!"; - store->insertArea(d); + store->insertArea(&d); store->getAreasForPos(&res, v3s16(-75, -250, -150)); UASSERTEQ(size_t, res.size(), 1); - UASSERTEQ(u32, res[0]->id, 4); UASSERTEQ(u16, res[0]->data.size(), 3); UASSERT(strncmp(res[0]->data.c_str(), "Hi!", 3) == 0); res.clear(); - store->removeArea(4); + store->removeArea(d.id); } + +void TestAreaStore::testSerialization() +{ + VectorAreaStore store; + + Area a(v3s16(-1, 0, 1), v3s16(0, 1, 2)); + a.data = "Area A"; + store.insertArea(&a); + + Area b(v3s16(123, 456, 789), v3s16(32000, 100, 10)); + b.data = "Area B"; + store.insertArea(&b); + + std::ostringstream os; + store.serialize(os); + std::string str = os.str(); + + std::string str_wanted("\x00" // Version + "\x00\x02" // Count + "\xFF\xFF\x00\x00\x00\x01" // Area A min edge + "\x00\x00\x00\x01\x00\x02" // Area A max edge + "\x00\x06" // Area A data length + "Area A" // Area A data + "\x00\x7B\x00\x64\x00\x0A" // Area B min edge (last two swapped with max edge for sorting) + "\x7D\x00\x01\xC8\x03\x15" // Area B max edge (^) + "\x00\x06" // Area B data length + "Area B", // Area B data + 1 + 2 + + 6 + 6 + 2 + 6 + + 6 + 6 + 2 + 6); + UASSERTEQ(std::string, str, str_wanted); + + std::istringstream is(str); + store.deserialize(is); + + UASSERTEQ(size_t, store.size(), 4); // deserialize() doesn't clear the store +} + diff --git a/src/unittest/test_collision.cpp b/src/unittest/test_collision.cpp index e505de450..332d3fa13 100644 --- a/src/unittest/test_collision.cpp +++ b/src/unittest/test_collision.cpp @@ -51,7 +51,7 @@ void TestCollision::testAxisAlignedCollision() aabb3f m(bx-2, by, bz, bx-1, by+1, bz+1); v3f v(1, 0, 0); f32 dtime = 0; - UASSERT(axisAlignedCollision(s, m, v, 0, dtime) == 0); + UASSERT(axisAlignedCollision(s, m, v, 0, &dtime) == 0); UASSERT(fabs(dtime - 1.000) < 0.001); } { @@ -59,21 +59,21 @@ void TestCollision::testAxisAlignedCollision() aabb3f m(bx-2, by, bz, bx-1, by+1, bz+1); v3f v(-1, 0, 0); f32 dtime = 0; - UASSERT(axisAlignedCollision(s, m, v, 0, dtime) == -1); + UASSERT(axisAlignedCollision(s, m, v, 0, &dtime) == -1); } { aabb3f s(bx, by, bz, bx+1, by+1, bz+1); aabb3f m(bx-2, by+1.5, bz, bx-1, by+2.5, bz-1); v3f v(1, 0, 0); f32 dtime; - UASSERT(axisAlignedCollision(s, m, v, 0, dtime) == -1); + UASSERT(axisAlignedCollision(s, m, v, 0, &dtime) == -1); } { aabb3f s(bx, by, bz, bx+1, by+1, bz+1); aabb3f m(bx-2, by-1.5, bz, bx-1.5, by+0.5, bz+1); v3f v(0.5, 0.1, 0); f32 dtime; - UASSERT(axisAlignedCollision(s, m, v, 0, dtime) == 0); + UASSERT(axisAlignedCollision(s, m, v, 0, &dtime) == 0); UASSERT(fabs(dtime - 3.000) < 0.001); } { @@ -81,7 +81,7 @@ void TestCollision::testAxisAlignedCollision() aabb3f m(bx-2, by-1.5, bz, bx-1.5, by+0.5, bz+1); v3f v(0.5, 0.1, 0); f32 dtime; - UASSERT(axisAlignedCollision(s, m, v, 0, dtime) == 0); + UASSERT(axisAlignedCollision(s, m, v, 0, &dtime) == 0); UASSERT(fabs(dtime - 3.000) < 0.001); } @@ -91,7 +91,7 @@ void TestCollision::testAxisAlignedCollision() aabb3f m(bx+2, by, bz, bx+3, by+1, bz+1); v3f v(-1, 0, 0); f32 dtime; - UASSERT(axisAlignedCollision(s, m, v, 0, dtime) == 0); + UASSERT(axisAlignedCollision(s, m, v, 0, &dtime) == 0); UASSERT(fabs(dtime - 1.000) < 0.001); } { @@ -99,21 +99,21 @@ void TestCollision::testAxisAlignedCollision() aabb3f m(bx+2, by, bz, bx+3, by+1, bz+1); v3f v(1, 0, 0); f32 dtime; - UASSERT(axisAlignedCollision(s, m, v, 0, dtime) == -1); + UASSERT(axisAlignedCollision(s, m, v, 0, &dtime) == -1); } { aabb3f s(bx, by, bz, bx+1, by+1, bz+1); aabb3f m(bx+2, by, bz+1.5, bx+3, by+1, bz+3.5); v3f v(-1, 0, 0); f32 dtime; - UASSERT(axisAlignedCollision(s, m, v, 0, dtime) == -1); + UASSERT(axisAlignedCollision(s, m, v, 0, &dtime) == -1); } { aabb3f s(bx, by, bz, bx+1, by+1, bz+1); aabb3f m(bx+2, by-1.5, bz, bx+2.5, by-0.5, bz+1); v3f v(-0.5, 0.2, 0); f32 dtime; - UASSERT(axisAlignedCollision(s, m, v, 0, dtime) == 1); // Y, not X! + UASSERT(axisAlignedCollision(s, m, v, 0, &dtime) == 1); // Y, not X! UASSERT(fabs(dtime - 2.500) < 0.001); } { @@ -121,7 +121,7 @@ void TestCollision::testAxisAlignedCollision() aabb3f m(bx+2, by-1.5, bz, bx+2.5, by-0.5, bz+1); v3f v(-0.5, 0.3, 0); f32 dtime; - UASSERT(axisAlignedCollision(s, m, v, 0, dtime) == 0); + UASSERT(axisAlignedCollision(s, m, v, 0, &dtime) == 0); UASSERT(fabs(dtime - 2.000) < 0.001); } @@ -133,7 +133,7 @@ void TestCollision::testAxisAlignedCollision() aabb3f m(bx+2.3, by+2.29, bz+2.29, bx+4.2, by+4.2, bz+4.2); v3f v(-1./3, -1./3, -1./3); f32 dtime; - UASSERT(axisAlignedCollision(s, m, v, 0, dtime) == 0); + UASSERT(axisAlignedCollision(s, m, v, 0, &dtime) == 0); UASSERT(fabs(dtime - 0.9) < 0.001); } { @@ -141,7 +141,7 @@ void TestCollision::testAxisAlignedCollision() aabb3f m(bx+2.29, by+2.3, bz+2.29, bx+4.2, by+4.2, bz+4.2); v3f v(-1./3, -1./3, -1./3); f32 dtime; - UASSERT(axisAlignedCollision(s, m, v, 0, dtime) == 1); + UASSERT(axisAlignedCollision(s, m, v, 0, &dtime) == 1); UASSERT(fabs(dtime - 0.9) < 0.001); } { @@ -149,7 +149,7 @@ void TestCollision::testAxisAlignedCollision() aabb3f m(bx+2.29, by+2.29, bz+2.3, bx+4.2, by+4.2, bz+4.2); v3f v(-1./3, -1./3, -1./3); f32 dtime; - UASSERT(axisAlignedCollision(s, m, v, 0, dtime) == 2); + UASSERT(axisAlignedCollision(s, m, v, 0, &dtime) == 2); UASSERT(fabs(dtime - 0.9) < 0.001); } { @@ -157,7 +157,7 @@ void TestCollision::testAxisAlignedCollision() aabb3f m(bx-4.2, by-4.2, bz-4.2, bx-2.3, by-2.29, bz-2.29); v3f v(1./7, 1./7, 1./7); f32 dtime; - UASSERT(axisAlignedCollision(s, m, v, 0, dtime) == 0); + UASSERT(axisAlignedCollision(s, m, v, 0, &dtime) == 0); UASSERT(fabs(dtime - 16.1) < 0.001); } { @@ -165,7 +165,7 @@ void TestCollision::testAxisAlignedCollision() aabb3f m(bx-4.2, by-4.2, bz-4.2, bx-2.29, by-2.3, bz-2.29); v3f v(1./7, 1./7, 1./7); f32 dtime; - UASSERT(axisAlignedCollision(s, m, v, 0, dtime) == 1); + UASSERT(axisAlignedCollision(s, m, v, 0, &dtime) == 1); UASSERT(fabs(dtime - 16.1) < 0.001); } { @@ -173,7 +173,7 @@ void TestCollision::testAxisAlignedCollision() aabb3f m(bx-4.2, by-4.2, bz-4.2, bx-2.29, by-2.29, bz-2.3); v3f v(1./7, 1./7, 1./7); f32 dtime; - UASSERT(axisAlignedCollision(s, m, v, 0, dtime) == 2); + UASSERT(axisAlignedCollision(s, m, v, 0, &dtime) == 2); UASSERT(fabs(dtime - 16.1) < 0.001); } } diff --git a/src/unittest/test_serialization.cpp b/src/unittest/test_serialization.cpp index 49f348e9c..4cbc999ea 100644 --- a/src/unittest/test_serialization.cpp +++ b/src/unittest/test_serialization.cpp @@ -40,6 +40,9 @@ public: void testDeSerializeLongString(); void testStreamRead(); void testStreamWrite(); + void testVecPut(); + void testStringLengthLimits(); + void testBufReader(); std::string teststring2; std::wstring teststring2_w; @@ -64,6 +67,9 @@ void TestSerialization::runTests(IGameDef *gamedef) TEST(testSerializeHex); TEST(testStreamRead); TEST(testStreamWrite); + TEST(testVecPut); + TEST(testStringLengthLimits); + TEST(testBufReader); } //////////////////////////////////////////////////////////////////////////////// @@ -369,6 +375,263 @@ void TestSerialization::testStreamWrite() } +void TestSerialization::testVecPut() +{ + std::vector<u8> buf; + + putU8(&buf, 0x11); + putU16(&buf, 0x2233); + putU32(&buf, 0x44556677); + putU64(&buf, 0x8899AABBCCDDEEFF); + + putS8(&buf, -128); + putS16(&buf, 30000); + putS32(&buf, -6); + putS64(&buf, -43); + + putF1000(&buf, 53.53467f); + putF1000(&buf, -300000.32f); + putF1000(&buf, F1000_MIN); + putF1000(&buf, F1000_MAX); + + putString(&buf, "foobar!"); + + putV2S16(&buf, v2s16(500, 500)); + putV3S16(&buf, v3s16(4207, 604, -30)); + putV2S32(&buf, v2s32(1920, 1080)); + putV3S32(&buf, v3s32(-400, 6400054, 290549855)); + putV2F1000(&buf, v2f(500.65661f, 350.34567f)); + + putWideString(&buf, L"\x02~woof~\x5455"); + + putV3F1000(&buf, v3f(500, 10024.2f, -192.54f)); + putARGB8(&buf, video::SColor(255, 128, 50, 128)); + + putLongString(&buf, "some longer string here"); + + putU16(&buf, 0xF00D); + + UASSERT(buf.size() == sizeof(test_serialized_data)); + UASSERT(!memcmp(&buf[0], test_serialized_data, sizeof(test_serialized_data))); +} + + +void TestSerialization::testStringLengthLimits() +{ + std::vector<u8> buf; + std::string too_long(STRING_MAX_LEN + 1, 'A'); + std::string way_too_large(LONG_STRING_MAX_LEN + 1, 'B'); + std::wstring too_long_wide(WIDE_STRING_MAX_LEN + 1, L'C'); + + EXCEPTION_CHECK(SerializationError, putString(&buf, too_long)); + + putLongString(&buf, too_long); + too_long.resize(too_long.size() - 1); + putString(&buf, too_long); + + EXCEPTION_CHECK(SerializationError, putWideString(&buf, too_long_wide)); + too_long_wide.resize(too_long_wide.size() - 1); + putWideString(&buf, too_long_wide); +} + + +void TestSerialization::testBufReader() +{ + u8 u8_data; + u16 u16_data; + u32 u32_data; + u64 u64_data; + s8 s8_data; + s16 s16_data; + s32 s32_data; + s64 s64_data; + f32 f32_data, f32_data2, f32_data3, f32_data4; + video::SColor scolor_data; + v2s16 v2s16_data; + v3s16 v3s16_data; + v2s32 v2s32_data; + v3s32 v3s32_data; + v2f v2f_data; + v3f v3f_data; + std::string string_data; + std::wstring widestring_data; + std::string longstring_data; + u8 raw_data[10] = {0}; + + BufReader buf(test_serialized_data, sizeof(test_serialized_data)); + + // Try reading data like normal + UASSERT(buf.getU8() == 0x11); + UASSERT(buf.getU16() == 0x2233); + UASSERT(buf.getU32() == 0x44556677); + UASSERT(buf.getU64() == 0x8899AABBCCDDEEFF); + UASSERT(buf.getS8() == -128); + UASSERT(buf.getS16() == 30000); + UASSERT(buf.getS32() == -6); + UASSERT(buf.getS64() == -43); + UASSERT(buf.getF1000() == 53.534f); + UASSERT(buf.getF1000() == -300000.32f); + UASSERT(buf.getF1000() == F1000_MIN); + UASSERT(buf.getF1000() == F1000_MAX); + UASSERT(buf.getString() == "foobar!"); + UASSERT(buf.getV2S16() == v2s16(500, 500)); + UASSERT(buf.getV3S16() == v3s16(4207, 604, -30)); + UASSERT(buf.getV2S32() == v2s32(1920, 1080)); + UASSERT(buf.getV3S32() == v3s32(-400, 6400054, 290549855)); + UASSERT(buf.getV2F1000() == v2f(500.656f, 350.345f)); + UASSERT(buf.getWideString() == L"\x02~woof~\x5455"); + UASSERT(buf.getV3F1000() == v3f(500, 10024.2f, -192.54f)); + UASSERT(buf.getARGB8() == video::SColor(255, 128, 50, 128)); + UASSERT(buf.getLongString() == "some longer string here"); + + // Verify the offset and data is unchanged after a failed read + size_t orig_pos = buf.pos; + u32_data = 0; + UASSERT(buf.getU32NoEx(&u32_data) == false); + UASSERT(buf.pos == orig_pos); + UASSERT(u32_data == 0); + + // Now try the same for a failed string read + UASSERT(buf.getStringNoEx(&string_data) == false); + UASSERT(buf.pos == orig_pos); + UASSERT(string_data == ""); + + // Now try the same for a failed string read + UASSERT(buf.getWideStringNoEx(&widestring_data) == false); + UASSERT(buf.pos == orig_pos); + UASSERT(widestring_data == L""); + + UASSERT(buf.getU16() == 0xF00D); + + UASSERT(buf.remaining() == 0); + + // Check to make sure these each blow exceptions as they're supposed to + EXCEPTION_CHECK(SerializationError, buf.getU8()); + EXCEPTION_CHECK(SerializationError, buf.getU16()); + EXCEPTION_CHECK(SerializationError, buf.getU32()); + EXCEPTION_CHECK(SerializationError, buf.getU64()); + + EXCEPTION_CHECK(SerializationError, buf.getS8()); + EXCEPTION_CHECK(SerializationError, buf.getS16()); + EXCEPTION_CHECK(SerializationError, buf.getS32()); + EXCEPTION_CHECK(SerializationError, buf.getS64()); + + EXCEPTION_CHECK(SerializationError, buf.getF1000()); + EXCEPTION_CHECK(SerializationError, buf.getARGB8()); + + EXCEPTION_CHECK(SerializationError, buf.getV2S16()); + EXCEPTION_CHECK(SerializationError, buf.getV3S16()); + EXCEPTION_CHECK(SerializationError, buf.getV2S32()); + EXCEPTION_CHECK(SerializationError, buf.getV3S32()); + EXCEPTION_CHECK(SerializationError, buf.getV2F1000()); + EXCEPTION_CHECK(SerializationError, buf.getV3F1000()); + + EXCEPTION_CHECK(SerializationError, buf.getString()); + EXCEPTION_CHECK(SerializationError, buf.getWideString()); + EXCEPTION_CHECK(SerializationError, buf.getLongString()); + EXCEPTION_CHECK(SerializationError, + buf.getRawData(raw_data, sizeof(raw_data))); + + // See if we can skip backwards + buf.pos = 5; + UASSERT(buf.getRawDataNoEx(raw_data, 3) == true); + UASSERT(raw_data[0] == 0x66); + UASSERT(raw_data[1] == 0x77); + UASSERT(raw_data[2] == 0x88); + + UASSERT(buf.getU32() == 0x99AABBCC); + UASSERT(buf.pos == 12); + + // Now let's try it all over again using the NoEx variants + buf.pos = 0; + + UASSERT(buf.getU8NoEx(&u8_data)); + UASSERT(buf.getU16NoEx(&u16_data)); + UASSERT(buf.getU32NoEx(&u32_data)); + UASSERT(buf.getU64NoEx(&u64_data)); + + UASSERT(buf.getS8NoEx(&s8_data)); + UASSERT(buf.getS16NoEx(&s16_data)); + UASSERT(buf.getS32NoEx(&s32_data)); + UASSERT(buf.getS64NoEx(&s64_data)); + + UASSERT(buf.getF1000NoEx(&f32_data)); + UASSERT(buf.getF1000NoEx(&f32_data2)); + UASSERT(buf.getF1000NoEx(&f32_data3)); + UASSERT(buf.getF1000NoEx(&f32_data4)); + + UASSERT(buf.getStringNoEx(&string_data)); + UASSERT(buf.getV2S16NoEx(&v2s16_data)); + UASSERT(buf.getV3S16NoEx(&v3s16_data)); + UASSERT(buf.getV2S32NoEx(&v2s32_data)); + UASSERT(buf.getV3S32NoEx(&v3s32_data)); + UASSERT(buf.getV2F1000NoEx(&v2f_data)); + UASSERT(buf.getWideStringNoEx(&widestring_data)); + UASSERT(buf.getV3F1000NoEx(&v3f_data)); + UASSERT(buf.getARGB8NoEx(&scolor_data)); + + UASSERT(buf.getLongStringNoEx(&longstring_data)); + + // and make sure we got the correct data + UASSERT(u8_data == 0x11); + UASSERT(u16_data == 0x2233); + UASSERT(u32_data == 0x44556677); + UASSERT(u64_data == 0x8899AABBCCDDEEFF); + UASSERT(s8_data == -128); + UASSERT(s16_data == 30000); + UASSERT(s32_data == -6); + UASSERT(s64_data == -43); + UASSERT(f32_data == 53.534f); + UASSERT(f32_data2 == -300000.32f); + UASSERT(f32_data3 == F1000_MIN); + UASSERT(f32_data4 == F1000_MAX); + UASSERT(string_data == "foobar!"); + UASSERT(v2s16_data == v2s16(500, 500)); + UASSERT(v3s16_data == v3s16(4207, 604, -30)); + UASSERT(v2s32_data == v2s32(1920, 1080)); + UASSERT(v3s32_data == v3s32(-400, 6400054, 290549855)); + UASSERT(v2f_data == v2f(500.656f, 350.345f)); + UASSERT(widestring_data == L"\x02~woof~\x5455"); + UASSERT(v3f_data == v3f(500, 10024.2f, -192.54f)); + UASSERT(scolor_data == video::SColor(255, 128, 50, 128)); + UASSERT(longstring_data == "some longer string here"); + + UASSERT(buf.remaining() == 2); + UASSERT(buf.getRawDataNoEx(raw_data, 3) == false); + UASSERT(buf.remaining() == 2); + UASSERT(buf.getRawDataNoEx(raw_data, 2) == true); + UASSERT(raw_data[0] == 0xF0); + UASSERT(raw_data[1] == 0x0D); + UASSERT(buf.remaining() == 0); + + // Make sure no more available data causes a failure + UASSERT(!buf.getU8NoEx(&u8_data)); + UASSERT(!buf.getU16NoEx(&u16_data)); + UASSERT(!buf.getU32NoEx(&u32_data)); + UASSERT(!buf.getU64NoEx(&u64_data)); + + UASSERT(!buf.getS8NoEx(&s8_data)); + UASSERT(!buf.getS16NoEx(&s16_data)); + UASSERT(!buf.getS32NoEx(&s32_data)); + UASSERT(!buf.getS64NoEx(&s64_data)); + + UASSERT(!buf.getF1000NoEx(&f32_data)); + UASSERT(!buf.getARGB8NoEx(&scolor_data)); + + UASSERT(!buf.getV2S16NoEx(&v2s16_data)); + UASSERT(!buf.getV3S16NoEx(&v3s16_data)); + UASSERT(!buf.getV2S32NoEx(&v2s32_data)); + UASSERT(!buf.getV3S32NoEx(&v3s32_data)); + UASSERT(!buf.getV2F1000NoEx(&v2f_data)); + UASSERT(!buf.getV3F1000NoEx(&v3f_data)); + + UASSERT(!buf.getStringNoEx(&string_data)); + UASSERT(!buf.getWideStringNoEx(&widestring_data)); + UASSERT(!buf.getLongStringNoEx(&longstring_data)); + UASSERT(!buf.getRawDataNoEx(raw_data, sizeof(raw_data))); +} + + const u8 TestSerialization::test_serialized_data[12 * 13] = { 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, 0x80, 0x75, 0x30, 0xff, 0xff, 0xff, 0xfa, 0xff, 0xff, diff --git a/src/unittest/test_threading.cpp b/src/unittest/test_threading.cpp new file mode 100644 index 000000000..f0df85b2d --- /dev/null +++ b/src/unittest/test_threading.cpp @@ -0,0 +1,182 @@ +/* +Minetest +Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com> + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "test.h" + +#include "threading/atomic.h" +#include "threading/semaphore.h" +#include "threading/thread.h" + + +class TestThreading : public TestBase { +public: + TestThreading() { TestManager::registerTestModule(this); } + const char *getName() { return "TestThreading"; } + void runTests(IGameDef *gamedef); + + void testStartStopWait(); + void testThreadKill(); + void testAtomicSemaphoreThread(); +}; + +static TestThreading g_test_instance; + +void TestThreading::runTests(IGameDef *gamedef) +{ + TEST(testStartStopWait); + TEST(testThreadKill); + TEST(testAtomicSemaphoreThread); +} + +class SimpleTestThread : public Thread { +public: + SimpleTestThread(unsigned int interval) : + Thread("SimpleTest"), + m_interval(interval) + { + } + +private: + void *run() + { + void *retval = this; + + if (isCurrentThread() == false) + retval = (void *)0xBAD; + + while (!stopRequested()) + sleep_ms(m_interval); + + return retval; + } + + unsigned int m_interval; +}; + +void TestThreading::testStartStopWait() +{ + void *thread_retval; + SimpleTestThread *thread = new SimpleTestThread(25); + + // Try this a couple times, since a Thread should be reusable after waiting + for (size_t i = 0; i != 5; i++) { + // Can't wait() on a joined, stopped thread + UASSERT(thread->wait() == false); + + // start() should work the first time, but not the second. + UASSERT(thread->start() == true); + UASSERT(thread->start() == false); + + UASSERT(thread->isRunning() == true); + UASSERT(thread->isCurrentThread() == false); + + // Let it loop a few times... + sleep_ms(70); + + // It's still running, so the return value shouldn't be available to us. + UASSERT(thread->getReturnValue(&thread_retval) == false); + + // stop() should always succeed + UASSERT(thread->stop() == true); + + // wait() only needs to wait the first time - the other two are no-ops. + UASSERT(thread->wait() == true); + UASSERT(thread->wait() == false); + UASSERT(thread->wait() == false); + + // Now that the thread is stopped, we should be able to get the + // return value, and it should be the object itself. + thread_retval = NULL; + UASSERT(thread->getReturnValue(&thread_retval) == true); + UASSERT(thread_retval == thread); + } + + delete thread; +} + + +void TestThreading::testThreadKill() +{ + SimpleTestThread *thread = new SimpleTestThread(300); + + UASSERT(thread->start() == true); + + // kill()ing is quite violent, so let's make sure our victim is sleeping + // before we do this... so we don't corrupt the rest of the program's state + sleep_ms(100); + UASSERT(thread->kill() == true); + + // The state of the thread object should be reset if all went well + UASSERT(thread->isRunning() == false); + UASSERT(thread->start() == true); + UASSERT(thread->stop() == true); + UASSERT(thread->wait() == true); + + // kill() after already waiting should fail. + UASSERT(thread->kill() == false); + + delete thread; +} + + +class AtomicTestThread : public Thread { +public: + AtomicTestThread(Atomic<u32> &v, Semaphore &trigger) : + Thread("AtomicTest"), + val(v), + trigger(trigger) + { + } + +private: + void *run() + { + trigger.wait(); + for (u32 i = 0; i < 0x10000; ++i) + ++val; + return NULL; + } + + Atomic<u32> &val; + Semaphore &trigger; +}; + + +void TestThreading::testAtomicSemaphoreThread() +{ + Atomic<u32> val; + Semaphore trigger; + static const u8 num_threads = 4; + + AtomicTestThread *threads[num_threads]; + for (u8 i = 0; i < num_threads; ++i) { + threads[i] = new AtomicTestThread(val, trigger); + UASSERT(threads[i]->start()); + } + + trigger.post(num_threads); + + for (u8 i = 0; i < num_threads; ++i) { + threads[i]->wait(); + delete threads[i]; + } + + UASSERT(val == num_threads * 0x10000); +} + diff --git a/src/unittest/test_utilities.cpp b/src/unittest/test_utilities.cpp index df90d37bd..d73975b9f 100644 --- a/src/unittest/test_utilities.cpp +++ b/src/unittest/test_utilities.cpp @@ -43,7 +43,9 @@ public: void testStrToIntConversion(); void testStringReplace(); void testStringAllowed(); + void testAsciiPrintableHelper(); void testUTF8(); + void testRemoveEscapes(); void testWrapRows(); void testIsNumber(); void testIsPowerOfTwo(); @@ -68,7 +70,9 @@ void TestUtilities::runTests(IGameDef *gamedef) TEST(testStrToIntConversion); TEST(testStringReplace); TEST(testStringAllowed); + TEST(testAsciiPrintableHelper); TEST(testUTF8); + TEST(testRemoveEscapes); TEST(testWrapRows); TEST(testIsNumber); TEST(testIsPowerOfTwo); @@ -232,6 +236,18 @@ void TestUtilities::testStringAllowed() UASSERT(string_allowed_blacklist("hello123", "123") == false); } +void TestUtilities::testAsciiPrintableHelper() +{ + UASSERT(IS_ASCII_PRINTABLE_CHAR('e') == true); + UASSERT(IS_ASCII_PRINTABLE_CHAR('\0') == false); + + // Ensures that there is no cutting off going on... + // If there were, 331 would be cut to 75 in this example + // and 73 is a valid ASCII char. + int ch = 331; + UASSERT(IS_ASCII_PRINTABLE_CHAR(ch) == false); +} + void TestUtilities::testUTF8() { UASSERT(wide_to_utf8(utf8_to_wide("")) == ""); @@ -239,6 +255,23 @@ void TestUtilities::testUTF8() == "the shovel dug a crumbly node!"); } +void TestUtilities::testRemoveEscapes() +{ + UASSERT(unescape_enriched<wchar_t>( + L"abc\x1bXdef") == L"abcdef"); + UASSERT(unescape_enriched<wchar_t>( + L"abc\x1b(escaped)def") == L"abcdef"); + UASSERT(unescape_enriched<wchar_t>( + L"abc\x1b((escaped with parenthesis\\))def") == L"abcdef"); + UASSERT(unescape_enriched<wchar_t>( + L"abc\x1b(incomplete") == L"abc"); + UASSERT(unescape_enriched<wchar_t>( + L"escape at the end\x1b") == L"escape at the end"); + // Nested escapes not supported + UASSERT(unescape_enriched<wchar_t>( + L"abc\x1b(outer \x1b(inner escape)escape)def") == L"abcescape)def"); +} + void TestUtilities::testWrapRows() { UASSERT(wrap_rows("12345678",4) == "1234\n5678"); @@ -282,7 +315,7 @@ void TestUtilities::testIsPowerOfTwo() UASSERT(is_power_of_two((1 << exponent)) == true); UASSERT(is_power_of_two((1 << exponent) + 1) == false); } - UASSERT(is_power_of_two((u32)-1) == false); + UASSERT(is_power_of_two(U32_MAX) == false); } void TestUtilities::testMyround() |