v1.4.4
This commit is contained in:
commit
9c94d113d3
10260 changed files with 1237388 additions and 0 deletions
85
source/test/CMakeLists.txt
Normal file
85
source/test/CMakeLists.txt
Normal file
|
@ -0,0 +1,85 @@
|
|||
INCLUDE_DIRECTORIES (
|
||||
${STAR_EXTERN_INCLUDES}
|
||||
${STAR_CORE_INCLUDES}
|
||||
${STAR_BASE_INCLUDES}
|
||||
${STAR_PLATFORM_INCLUDES}
|
||||
${STAR_GAME_INCLUDES}
|
||||
)
|
||||
|
||||
# Add top-level test directory so gtest-all.cc can find its own include file
|
||||
INCLUDE_DIRECTORIES (.)
|
||||
|
||||
SET (star_core_tests_SOURCES
|
||||
gtest/gtest-all.cc
|
||||
|
||||
core_tests_main.cpp
|
||||
|
||||
algorithm_test.cpp
|
||||
block_allocator_test.cpp
|
||||
blocks_along_line_test.cpp
|
||||
btree_database_test.cpp
|
||||
btree_test.cpp
|
||||
byte_array_test.cpp
|
||||
clock_test.cpp
|
||||
color_test.cpp
|
||||
container_test.cpp
|
||||
encode_test.cpp
|
||||
file_test.cpp
|
||||
hash_test.cpp
|
||||
host_address_test.cpp
|
||||
ref_ptr_test.cpp
|
||||
json_test.cpp
|
||||
flat_hash_test.cpp
|
||||
formatted_json_test.cpp
|
||||
line_test.cpp
|
||||
lua_test.cpp
|
||||
lua_json_test.cpp
|
||||
math_test.cpp
|
||||
multi_table_test.cpp
|
||||
net_states_test.cpp
|
||||
ordered_map_test.cpp
|
||||
ordered_set_test.cpp
|
||||
periodic_test.cpp
|
||||
poly_test.cpp
|
||||
random_test.cpp
|
||||
rect_test.cpp
|
||||
serialization_test.cpp
|
||||
static_vector_test.cpp
|
||||
small_vector_test.cpp
|
||||
sha_test.cpp
|
||||
shell_parse.cpp
|
||||
string_test.cpp
|
||||
strong_typedef_test.cpp
|
||||
thread_test.cpp
|
||||
worker_pool_test.cpp
|
||||
variant_test.cpp
|
||||
vlq_test.cpp
|
||||
)
|
||||
ADD_EXECUTABLE (core_tests
|
||||
$<TARGET_OBJECTS:star_extern> $<TARGET_OBJECTS:star_core>
|
||||
${star_core_tests_SOURCES})
|
||||
TARGET_LINK_LIBRARIES (core_tests ${STAR_EXT_LIBS})
|
||||
ADD_TEST (NAME core_tests WORKING_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} COMMAND ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/core_tests)
|
||||
|
||||
SET (star_game_tests_SOURCES
|
||||
gtest/gtest-all.cc
|
||||
|
||||
game_tests_main.cpp
|
||||
|
||||
StarTestUniverse.cpp
|
||||
assets_test.cpp
|
||||
function_test.cpp
|
||||
item_test.cpp
|
||||
root_test.cpp
|
||||
server_test.cpp
|
||||
spawn_test.cpp
|
||||
stat_test.cpp
|
||||
tile_array_test.cpp
|
||||
world_geometry_test.cpp
|
||||
universe_connection_test.cpp
|
||||
)
|
||||
ADD_EXECUTABLE (game_tests
|
||||
$<TARGET_OBJECTS:star_extern> $<TARGET_OBJECTS:star_core> $<TARGET_OBJECTS:star_base> $<TARGET_OBJECTS:star_game>
|
||||
${star_game_tests_SOURCES})
|
||||
TARGET_LINK_LIBRARIES (game_tests ${STAR_EXT_LIBS})
|
||||
ADD_TEST (NAME game_tests WORKING_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} COMMAND ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/game_tests)
|
76
source/test/StarTestUniverse.cpp
Normal file
76
source/test/StarTestUniverse.cpp
Normal file
|
@ -0,0 +1,76 @@
|
|||
#include "StarTestUniverse.hpp"
|
||||
#include "StarFile.hpp"
|
||||
#include "StarQuests.hpp"
|
||||
#include "StarPlayerFactory.hpp"
|
||||
#include "StarPlayerStorage.hpp"
|
||||
#include "StarStatistics.hpp"
|
||||
#include "StarStatisticsService.hpp"
|
||||
#include "StarPlayer.hpp"
|
||||
#include "StarAssets.hpp"
|
||||
#include "StarWorldClient.hpp"
|
||||
|
||||
namespace Star {
|
||||
|
||||
TestUniverse::TestUniverse(Vec2U clientWindowSize) {
|
||||
auto& root = Root::singleton();
|
||||
|
||||
m_clientWindowSize = clientWindowSize;
|
||||
|
||||
m_storagePath = File::temporaryDirectory();
|
||||
auto playerStorage = make_shared<PlayerStorage>(File::relativeTo(m_storagePath, "player"));
|
||||
auto statistics = make_shared<Statistics>(File::relativeTo(m_storagePath, "statistics"));
|
||||
m_server = make_shared<UniverseServer>(File::relativeTo(m_storagePath, "universe"));
|
||||
m_client = make_shared<UniverseClient>(playerStorage, statistics);
|
||||
|
||||
m_server->start();
|
||||
|
||||
m_mainPlayer = root.playerFactory()->create();
|
||||
m_mainPlayer->finalizeCreation();
|
||||
m_mainPlayer->setAdmin(true);
|
||||
m_mainPlayer->setModeType(PlayerMode::Survival);
|
||||
m_client->setMainPlayer(m_mainPlayer);
|
||||
m_client->connect(m_server->addLocalClient(), "test", "");
|
||||
}
|
||||
|
||||
TestUniverse::~TestUniverse() {
|
||||
m_client = {};
|
||||
m_server = {};
|
||||
m_mainPlayer = {};
|
||||
File::removeDirectoryRecursive(m_storagePath);
|
||||
}
|
||||
|
||||
void TestUniverse::warpPlayer(WorldId worldId) {
|
||||
m_client->warpPlayer(WarpToWorld(worldId), true);
|
||||
while (m_mainPlayer->isTeleporting() || m_client->playerWorld().empty()) {
|
||||
m_client->update();
|
||||
Thread::sleep(16);
|
||||
}
|
||||
}
|
||||
|
||||
WorldId TestUniverse::currentPlayerWorld() const {
|
||||
return m_client->clientContext()->playerWorldId();
|
||||
}
|
||||
|
||||
void TestUniverse::update(unsigned times) {
|
||||
for (unsigned i = 0; i < times; ++i) {
|
||||
m_client->update();
|
||||
Thread::sleep(16);
|
||||
}
|
||||
}
|
||||
|
||||
List<Drawable> TestUniverse::currentClientDrawables() {
|
||||
WorldRenderData renderData;
|
||||
auto worldClient = m_client->worldClient();
|
||||
worldClient->centerClientWindowOnPlayer(m_clientWindowSize);
|
||||
worldClient->render(renderData, 0);
|
||||
|
||||
List<Drawable> drawables;
|
||||
for (auto& ed : renderData.entityDrawables) {
|
||||
for (auto& p : ed.layers)
|
||||
drawables.appendAll(move(p.second));
|
||||
}
|
||||
|
||||
return drawables;
|
||||
}
|
||||
|
||||
}
|
28
source/test/StarTestUniverse.hpp
Normal file
28
source/test/StarTestUniverse.hpp
Normal file
|
@ -0,0 +1,28 @@
|
|||
#include "StarPlayer.hpp"
|
||||
#include "StarRoot.hpp"
|
||||
#include "StarUniverseClient.hpp"
|
||||
#include "StarUniverseServer.hpp"
|
||||
|
||||
namespace Star {
|
||||
|
||||
class TestUniverse {
|
||||
public:
|
||||
TestUniverse(Vec2U clientWindowSize);
|
||||
~TestUniverse();
|
||||
|
||||
void warpPlayer(WorldId worldId);
|
||||
WorldId currentPlayerWorld() const;
|
||||
|
||||
void update(unsigned times = 1);
|
||||
|
||||
List<Drawable> currentClientDrawables();
|
||||
|
||||
private:
|
||||
Vec2U m_clientWindowSize;
|
||||
String m_storagePath;
|
||||
UniverseServerPtr m_server;
|
||||
UniverseClientPtr m_client;
|
||||
PlayerPtr m_mainPlayer;
|
||||
};
|
||||
|
||||
}
|
135
source/test/algorithm_test.cpp
Normal file
135
source/test/algorithm_test.cpp
Normal file
|
@ -0,0 +1,135 @@
|
|||
#include "StarList.hpp"
|
||||
#include "StarMultiArray.hpp"
|
||||
|
||||
#include <list>
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
using namespace Star;
|
||||
|
||||
TEST(any, allTests) {
|
||||
int a = 60;
|
||||
int asdf[] = {1, 2, 3, 4, 5, 6};
|
||||
|
||||
EXPECT_TRUE(any(asdf, [&](int b) { return b < a; }));
|
||||
EXPECT_FALSE(any(asdf, [&](int b) { return b > a; }));
|
||||
EXPECT_TRUE(any(asdf, [&](int b) { return a % b == 0; }));
|
||||
|
||||
bool b[] = {false, false, false, true};
|
||||
bool c[] = {false, false, false, false};
|
||||
bool d[] = {false, false, true, true};
|
||||
bool e[] = {true, true, true, true};
|
||||
int f[] = {0, 1, 0, 0, 0, 3};
|
||||
|
||||
EXPECT_TRUE(any(b));
|
||||
EXPECT_FALSE(any(c));
|
||||
EXPECT_TRUE(any(d));
|
||||
EXPECT_TRUE(any(e));
|
||||
EXPECT_TRUE(any(f));
|
||||
}
|
||||
|
||||
TEST(all, allTests) {
|
||||
int a = 60;
|
||||
int asdf[] = {1, 2, 3, 4, 5, 6};
|
||||
|
||||
EXPECT_TRUE(all(asdf, [&](int b) { return b < a; }));
|
||||
EXPECT_FALSE(all(asdf, [&](int b) { return b > a; }));
|
||||
EXPECT_TRUE(all(asdf, [&](int b) { return a % b == 0; }));
|
||||
|
||||
bool b[] = {false, false, false, true};
|
||||
bool c[] = {false, false, false, false};
|
||||
bool d[] = {false, false, true, true};
|
||||
bool e[] = {true, true, true, true};
|
||||
int f[] = {0, 1, 0, 0, 0, 3};
|
||||
|
||||
EXPECT_FALSE(all(b));
|
||||
EXPECT_FALSE(all(c));
|
||||
EXPECT_FALSE(all(d));
|
||||
EXPECT_TRUE(all(e));
|
||||
EXPECT_FALSE(all(f));
|
||||
}
|
||||
|
||||
TEST(ContainerOperators, allTests) {
|
||||
List<bool> a{false, false, true, false};
|
||||
List<int> b{1, 1, 0, 1};
|
||||
List<int> c = a.transformed([](bool a) { return a ? 0 : 1; });
|
||||
List<int> d{1, 2, 3, 5};
|
||||
List<int> e{1, 3, 5};
|
||||
List<int> f = d.filtered([](int i) { return i % 2 == 1; });
|
||||
|
||||
EXPECT_TRUE(a.any());
|
||||
EXPECT_FALSE(a.all());
|
||||
EXPECT_EQ(b, c);
|
||||
EXPECT_EQ(e, f);
|
||||
}
|
||||
|
||||
template <int Amount>
|
||||
struct Times {
|
||||
template <typename T>
|
||||
T operator()(T t) {
|
||||
return t * Amount;
|
||||
}
|
||||
};
|
||||
|
||||
template <int Amount>
|
||||
struct Add {
|
||||
template <typename T>
|
||||
T operator()(T t) {
|
||||
return t + Amount;
|
||||
}
|
||||
};
|
||||
|
||||
struct AddTogether {
|
||||
int operator()(int8_t& a, int64_t& b) {
|
||||
return a + b;
|
||||
}
|
||||
};
|
||||
|
||||
TEST(TupleOperators, allTests) {
|
||||
tuple<int8_t, int64_t> t1{3, 5};
|
||||
tuple<int8_t, int64_t> t2{6, 10};
|
||||
|
||||
auto t3 = tupleApplyFunction(Times<2>(), t1);
|
||||
EXPECT_EQ(t2, t3);
|
||||
|
||||
int r = tupleUnpackFunction(AddTogether(), t2);
|
||||
EXPECT_EQ(r, 16);
|
||||
|
||||
auto f = compose(Times<2>(), Add<1>(), Times<2>(), Add<3>());
|
||||
EXPECT_EQ(f(5), 34);
|
||||
}
|
||||
|
||||
TEST(ZipTest, All) {
|
||||
List<int> a{1, 2, 3};
|
||||
std::vector<uint64_t> b{5, 4, 3, 2, 1};
|
||||
std::deque<int64_t> c{3, 2, 2};
|
||||
std::list<unsigned> d{0, 0, 0, 0, 4, 8};
|
||||
|
||||
auto zipResult = zip(a, b, c, d);
|
||||
|
||||
EXPECT_EQ(zipResult.size(), 3u);
|
||||
EXPECT_EQ(get<0>(zipResult[0]), 1);
|
||||
EXPECT_EQ(get<1>(zipResult[0]), 5u);
|
||||
EXPECT_EQ(get<2>(zipResult[0]), 3);
|
||||
EXPECT_EQ(get<3>(zipResult[0]), 0u);
|
||||
EXPECT_EQ(get<0>(zipResult[2]), 3);
|
||||
EXPECT_EQ(get<1>(zipResult[2]), 3u);
|
||||
EXPECT_EQ(get<2>(zipResult[2]), 2);
|
||||
EXPECT_EQ(get<3>(zipResult[2]), 0u);
|
||||
}
|
||||
|
||||
TEST(ZipWith, All) {
|
||||
List<int> a{1, 1, 2, 3, 5, 8};
|
||||
List<int> b{5, 4, 3, 2, 1, 0};
|
||||
List<int> c{6, 5, 5, 5, 6, 8};
|
||||
List<int> d = zipWith<List<int>>(std::plus<int>(), a, b);
|
||||
EXPECT_EQ(c, d);
|
||||
}
|
||||
|
||||
TEST(TupleFunctions, All) {
|
||||
std::vector<int> a;
|
||||
std::vector<int> b = {1, 2, 3, 4, 5, 6, 7, 8};
|
||||
tupleCallFunction(make_tuple(1, 2, 3, 4, 5, 6, 7, 8), [&a](int i) { a.push_back(i); });
|
||||
|
||||
EXPECT_EQ(a, b);
|
||||
}
|
24
source/test/assets_test.cpp
Normal file
24
source/test/assets_test.cpp
Normal file
|
@ -0,0 +1,24 @@
|
|||
#include "StarAssets.hpp"
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
using namespace Star;
|
||||
|
||||
TEST(AssetsTest, All) {
|
||||
EXPECT_EQ(AssetPath::removeDirectives("/foo/bar/baz??::?:??:asdfasdf??D?"), "/foo/bar/baz");
|
||||
EXPECT_EQ(AssetPath::directory("/foo/bar/baz"), "/foo/bar/");
|
||||
EXPECT_EQ(AssetPath::directory("foo/bar/baz"), "foo/bar/");
|
||||
EXPECT_EQ(AssetPath::directory("foo"), "");
|
||||
EXPECT_EQ(AssetPath::directory("/foo"), "/");
|
||||
EXPECT_EQ(AssetPath::filename(""), "");
|
||||
EXPECT_EQ(AssetPath::filename("foo"), "foo");
|
||||
EXPECT_EQ(AssetPath::filename("/foo"), "foo");
|
||||
EXPECT_EQ(AssetPath::filename("/foo/"), "");
|
||||
EXPECT_EQ(AssetPath::filename("/foo/bar"), "bar");
|
||||
|
||||
AssetPath compare = AssetPath{"/foo/bar/baz", String("baf"), {"whoa", "there"}};
|
||||
EXPECT_EQ(AssetPath::split("/foo/bar/baz:baf?whoa?there"), compare);
|
||||
|
||||
EXPECT_EQ(
|
||||
AssetPath::relativeTo("/foo/bar/baz:baf?whoa?there", "thing:sub?directive"), "/foo/bar/thing:sub?directive");
|
||||
}
|
16
source/test/block_allocator_test.cpp
Normal file
16
source/test/block_allocator_test.cpp
Normal file
|
@ -0,0 +1,16 @@
|
|||
#include "StarBlockAllocator.hpp"
|
||||
#include "StarMap.hpp"
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
using namespace Star;
|
||||
|
||||
TEST(BlockAllocatorTest, All) {
|
||||
HashMap<int, int, std::hash<int>, std::equal_to<int>, BlockAllocator<pair<int const, int>, 4096>> testMap;
|
||||
testMap.reserve(32768);
|
||||
for (size_t i = 0; i < 65536; ++i)
|
||||
testMap[i] = i;
|
||||
|
||||
for (auto const& p : testMap)
|
||||
EXPECT_EQ(p.first, p.second);
|
||||
}
|
45
source/test/blocks_along_line_test.cpp
Normal file
45
source/test/blocks_along_line_test.cpp
Normal file
|
@ -0,0 +1,45 @@
|
|||
#include "StarBlocksAlongLine.hpp"
|
||||
#include "StarList.hpp"
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
using namespace Star;
|
||||
|
||||
void testLine(Vec2D a, Vec2D b, Vec2I offset, List<Vec2I> comp) {
|
||||
List<Vec2I> res;
|
||||
forBlocksAlongLine(a + Vec2D(offset),
|
||||
b - a,
|
||||
[&res](int x, int y) {
|
||||
res.append({x, y});
|
||||
return true;
|
||||
});
|
||||
for (auto& c : comp)
|
||||
c += offset;
|
||||
EXPECT_TRUE(comp == res);
|
||||
}
|
||||
|
||||
void testGroup(Vec2I offset) {
|
||||
testLine(Vec2D(0.5, 0.5), Vec2D(0.5, 0.5), offset, {{0, 0}});
|
||||
testLine(Vec2D(-0.5, -0.5), Vec2D(-0.5, -0.5), offset, {{-1, -1}});
|
||||
|
||||
testLine(Vec2D(-0.5, -0.5), Vec2D(0.5, 0.5), offset, {{-1, -1}, {0, 0}});
|
||||
testLine(Vec2D(0.5, 0.5), Vec2D(-0.5, -0.5), offset, {{0, 0}, {-1, -1}});
|
||||
testLine(Vec2D(-0.5, 0.5), Vec2D(0.5, -0.5), offset, {{-1, 0}, {0, -1}});
|
||||
testLine(Vec2D(0.5, -0.5), Vec2D(-0.5, 0.5), offset, {{0, -1}, {-1, 0}});
|
||||
|
||||
testLine(Vec2D(0.5, -0.5), Vec2D(0.5, 0.5), offset, {{0, -1}, {0, 0}});
|
||||
testLine(Vec2D(-0.5, 0.5), Vec2D(0.5, 0.5), offset, {{-1, 0}, {0, 0}});
|
||||
|
||||
testLine(Vec2D(0.0, 0.5), Vec2D(0.0, -0.5), offset, {{0, 0}, {0, -1}});
|
||||
testLine(Vec2D(0.5, 0.0), Vec2D(-0.5, 0.0), offset, {{0, 0}, {-1, 0}});
|
||||
}
|
||||
|
||||
TEST(BlocksAlongLine, All) {
|
||||
testGroup({0, 0});
|
||||
testGroup({50, 50});
|
||||
testGroup({-5, -50});
|
||||
testGroup({50, -5});
|
||||
testGroup({100, 10});
|
||||
testGroup({-10, -100});
|
||||
testGroup({-10, 10});
|
||||
}
|
256
source/test/btree_database_test.cpp
Normal file
256
source/test/btree_database_test.cpp
Normal file
|
@ -0,0 +1,256 @@
|
|||
#include "StarBTreeDatabase.hpp"
|
||||
#include "StarFile.hpp"
|
||||
#include "StarRandom.hpp"
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
using namespace Star;
|
||||
|
||||
namespace {
|
||||
size_t const RandFactor = 0xd5a2f037;
|
||||
size_t const MaxSize = 400;
|
||||
uint32_t const MaxKey = 100000;
|
||||
|
||||
ByteArray toByteArray(uint32_t k) {
|
||||
k = toBigEndian(k);
|
||||
return ByteArray((char*)(&k), sizeof(k));
|
||||
}
|
||||
|
||||
ByteArray genBlock(uint32_t k) {
|
||||
// Make sure not empty, because we test for existence with empty()
|
||||
size_t size = (RandFactor * k) % (MaxSize - 1) + 1;
|
||||
uint8_t val = (uint8_t)(k % 256);
|
||||
|
||||
ByteArray b(size, 0);
|
||||
|
||||
for (size_t i = 0; i < size; ++i) {
|
||||
b[i] = val;
|
||||
val += 1;
|
||||
}
|
||||
|
||||
return b;
|
||||
}
|
||||
|
||||
bool checkBlock(uint32_t k, ByteArray b) {
|
||||
return genBlock(k) == b;
|
||||
}
|
||||
|
||||
void putAll(BTreeDatabase& db, const List<uint32_t>& keys) {
|
||||
for (uint32_t k : keys) {
|
||||
ByteArray val = genBlock(k);
|
||||
db.insert(toByteArray(k), val);
|
||||
|
||||
int i = Random::randi32();
|
||||
if (i % 23 == 0)
|
||||
db.commit();
|
||||
}
|
||||
}
|
||||
|
||||
void checkAll(BTreeDatabase& db, const List<uint32_t>& keys) {
|
||||
for (uint32_t k : keys) {
|
||||
auto res = db.find(toByteArray(k));
|
||||
EXPECT_TRUE((bool)res);
|
||||
EXPECT_TRUE(checkBlock(k, *res));
|
||||
}
|
||||
|
||||
// Also check that forAll works.
|
||||
|
||||
Set<ByteArray> keySet;
|
||||
for (uint32_t k : keys)
|
||||
keySet.add(toByteArray(k));
|
||||
|
||||
db.forAll([&keySet](ByteArray const& key, ByteArray const&) { EXPECT_TRUE(keySet.remove(key)); });
|
||||
|
||||
EXPECT_TRUE(keySet.empty());
|
||||
}
|
||||
|
||||
size_t removeAll(BTreeDatabase& db, const List<uint32_t>& keys) {
|
||||
size_t totalRemoved = 0;
|
||||
for (uint32_t k : keys) {
|
||||
auto old = db.find(toByteArray(k));
|
||||
EXPECT_TRUE(!old || *old == genBlock(k));
|
||||
|
||||
if (db.remove(toByteArray(k))) {
|
||||
EXPECT_FALSE((bool)db.find(toByteArray(k)));
|
||||
++totalRemoved;
|
||||
}
|
||||
}
|
||||
return totalRemoved;
|
||||
}
|
||||
|
||||
void testBTreeDatabase(size_t testCount, size_t writeRepeat, size_t randCount, size_t rollbackCount, size_t blockSize) {
|
||||
auto tmpFile = File::temporaryFile();
|
||||
auto finallyGuard = finally([&tmpFile]() { tmpFile->remove(); });
|
||||
|
||||
Set<uint32_t> keySet;
|
||||
BTreeDatabase db("TestDB", 4);
|
||||
db.setAutoCommit(false);
|
||||
|
||||
while (keySet.size() < testCount)
|
||||
keySet.add(Random::randUInt(0, MaxKey));
|
||||
|
||||
List<uint32_t> keys;
|
||||
for (uint32_t k : keySet) {
|
||||
for (uint32_t j = 0; j < writeRepeat; ++j)
|
||||
keys.append(k);
|
||||
}
|
||||
|
||||
db.setIndexCacheSize(0);
|
||||
db.setBlockSize(blockSize);
|
||||
db.setIODevice(tmpFile);
|
||||
db.open();
|
||||
|
||||
// record writes/reads repeated writeRepeat times randomly each cycle
|
||||
std::random_shuffle(keys.begin(), keys.end());
|
||||
putAll(db, keys);
|
||||
|
||||
EXPECT_EQ(db.recordCount(), testCount);
|
||||
|
||||
std::random_shuffle(keys.begin(), keys.end());
|
||||
checkAll(db, keys);
|
||||
|
||||
// Random reads/writes with randCount cycles...
|
||||
for (uint32_t i = 0; i < randCount; ++i) {
|
||||
List<uint32_t> keysTemp(keys.begin(), keys.begin() + keys.size() / 2);
|
||||
|
||||
std::random_shuffle(keysTemp.begin(), keysTemp.end());
|
||||
removeAll(db, keysTemp);
|
||||
|
||||
std::random_shuffle(keysTemp.begin(), keysTemp.end());
|
||||
putAll(db, keysTemp);
|
||||
|
||||
std::random_shuffle(keys.begin(), keys.end());
|
||||
checkAll(db, keys);
|
||||
}
|
||||
|
||||
db.commit();
|
||||
|
||||
// Random reads/writes/rollbacks with rollbackCount cycles...
|
||||
for (uint32_t i = 0; i < rollbackCount ; ++i) {
|
||||
List<uint32_t> keysTemp(keys.begin(), keys.begin() + keys.size() / 2);
|
||||
std::random_shuffle(keysTemp.begin(), keysTemp.end());
|
||||
|
||||
removeAll(db, keysTemp);
|
||||
db.rollback();
|
||||
|
||||
checkAll(db, keys);
|
||||
}
|
||||
|
||||
EXPECT_EQ(db.totalBlockCount(), db.freeBlockCount() + db.indexBlockCount() + db.leafBlockCount());
|
||||
|
||||
// Now testing closing and reading
|
||||
|
||||
db.close();
|
||||
|
||||
// Set the wrong value, should be set to correct value in open()
|
||||
db.setBlockSize(blockSize + 512);
|
||||
|
||||
db.open();
|
||||
|
||||
// Checking values...
|
||||
|
||||
checkAll(db, keys);
|
||||
|
||||
EXPECT_EQ(db.totalBlockCount(), db.freeBlockCount() + db.indexBlockCount() + db.leafBlockCount());
|
||||
|
||||
// Removing all records...
|
||||
|
||||
size_t totalRemoved = removeAll(db, keys);
|
||||
|
||||
EXPECT_EQ(totalRemoved, testCount);
|
||||
|
||||
EXPECT_EQ(db.totalBlockCount(), db.freeBlockCount() + db.indexBlockCount() + db.leafBlockCount());
|
||||
|
||||
db.close();
|
||||
}
|
||||
}
|
||||
|
||||
TEST(BTreeDatabaseTest, Consistency) {
|
||||
testBTreeDatabase(500, 3, 5, 5, 512);
|
||||
|
||||
// Test a range of block sizes to make sure there are not off by one errors
|
||||
// in maximum index / leaf size calculations.
|
||||
for (size_t i = 0; i < 16; ++i)
|
||||
testBTreeDatabase(30, 2, 2, 2, 200 + i);
|
||||
}
|
||||
|
||||
TEST(BTreeDatabaseTest, Threading) {
|
||||
auto tmpFile = File::temporaryFile();
|
||||
auto finallyGuard = finally([&tmpFile]() { tmpFile->remove(); });
|
||||
|
||||
BTreeDatabase db("TestDB", 4);
|
||||
db.setAutoCommit(false);
|
||||
db.setBlockSize(256);
|
||||
db.setIODevice(tmpFile);
|
||||
db.open();
|
||||
|
||||
List<uint32_t> writeKeySet;
|
||||
List<uint32_t> deleteKeySet;
|
||||
|
||||
while (writeKeySet.size() < 5000) {
|
||||
uint32_t key = Random::randu32();
|
||||
writeKeySet.append(key);
|
||||
if (Random::randf() > 0.3)
|
||||
deleteKeySet.append(key);
|
||||
}
|
||||
std::random_shuffle(writeKeySet.begin(), writeKeySet.end());
|
||||
|
||||
{
|
||||
auto writer = Thread::invoke("databaseTestWriter",
|
||||
[&db, &writeKeySet]() {
|
||||
try {
|
||||
for (uint32_t k : writeKeySet) {
|
||||
ByteArray val = genBlock(k);
|
||||
db.insert(toByteArray(k), val);
|
||||
if (Random::randi32() % 23 == 0)
|
||||
db.commit();
|
||||
}
|
||||
} catch (std::exception const& e) {
|
||||
SCOPED_TRACE(outputException(e, true));
|
||||
FAIL();
|
||||
}
|
||||
});
|
||||
|
||||
auto deleter = Thread::invoke("databaseTestDeleter",
|
||||
[&db, &deleteKeySet]() {
|
||||
try {
|
||||
for (uint32_t k : deleteKeySet) {
|
||||
db.remove(toByteArray(k));
|
||||
if (Random::randi32() % 23 == 0)
|
||||
db.commit();
|
||||
}
|
||||
} catch (std::exception const& e) {
|
||||
SCOPED_TRACE(outputException(e, true));
|
||||
FAIL();
|
||||
}
|
||||
});
|
||||
|
||||
writer.finish();
|
||||
deleter.finish();
|
||||
|
||||
db.close(false);
|
||||
tmpFile->open(IOMode::Read);
|
||||
db.open();
|
||||
|
||||
EXPECT_EQ(db.totalBlockCount(), db.freeBlockCount() + db.indexBlockCount() + db.leafBlockCount());
|
||||
|
||||
List<ThreadFunction<void>> readers;
|
||||
for (size_t i = 0; i < 5; ++i) {
|
||||
readers.append(Thread::invoke("databaseTestReader",
|
||||
[&db, &writeKeySet, &deleteKeySet]() {
|
||||
try {
|
||||
for (uint32_t k : writeKeySet) {
|
||||
if (auto res = db.find(toByteArray(k)))
|
||||
EXPECT_TRUE(checkBlock(k, *res));
|
||||
else
|
||||
EXPECT_TRUE(deleteKeySet.contains(k));
|
||||
}
|
||||
} catch (std::exception const& e) {
|
||||
SCOPED_TRACE(outputException(e, true));
|
||||
FAIL();
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
646
source/test/btree_test.cpp
Normal file
646
source/test/btree_test.cpp
Normal file
|
@ -0,0 +1,646 @@
|
|||
#include "StarBTree.hpp"
|
||||
#include "StarString.hpp"
|
||||
#include "StarMap.hpp"
|
||||
#include "StarSet.hpp"
|
||||
#include "StarLexicalCast.hpp"
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
using namespace Star;
|
||||
using namespace std;
|
||||
|
||||
template <typename Key, typename Pointer>
|
||||
struct SimpleBTreeIndex {
|
||||
size_t pointerCount() const;
|
||||
Pointer pointer(size_t i) const;
|
||||
void updatePointer(size_t i, Pointer p);
|
||||
|
||||
Key const& keyBefore(size_t i) const;
|
||||
void updateKeyBefore(size_t i, Key k);
|
||||
|
||||
void removeBefore(size_t i);
|
||||
void insertAfter(size_t i, Key k, Pointer p);
|
||||
|
||||
size_t indexLevel() const;
|
||||
void setIndexLevel(size_t indexLevel);
|
||||
|
||||
// count is number of elements to shift left *including* right's beginPointer
|
||||
void shiftLeft(Key const& mid, SimpleBTreeIndex& right, size_t count);
|
||||
|
||||
// count is number of elements to shift right
|
||||
void shiftRight(Key const& mid, SimpleBTreeIndex& left, size_t count);
|
||||
|
||||
// i should be index of pointer that will be the new beginPointer of right
|
||||
// node (cannot be 0).
|
||||
Key split(SimpleBTreeIndex& right, size_t i);
|
||||
|
||||
struct Element {
|
||||
Key key;
|
||||
Pointer pointer;
|
||||
};
|
||||
typedef List<Element> ElementList;
|
||||
|
||||
Pointer self;
|
||||
size_t level;
|
||||
Maybe<Pointer> beginPointer;
|
||||
ElementList pointers;
|
||||
};
|
||||
|
||||
template <typename Key, typename Data, typename Pointer>
|
||||
struct SimpleBTreeLeaf {
|
||||
size_t count() const;
|
||||
Key const& key(size_t i) const;
|
||||
Data const& data(size_t i) const;
|
||||
|
||||
void insert(size_t i, Key k, Data d);
|
||||
void remove(size_t i);
|
||||
|
||||
Maybe<Pointer> nextLeaf() const;
|
||||
void setNextLeaf(Maybe<Pointer> n);
|
||||
|
||||
// count is number of elements to shift left
|
||||
void shiftLeft(SimpleBTreeLeaf& right, size_t count);
|
||||
|
||||
// count is number of elements to shift right
|
||||
void shiftRight(SimpleBTreeLeaf& left, size_t count);
|
||||
|
||||
// i should be index of element that will be the new start of right node.
|
||||
// Returns right index node.
|
||||
void split(SimpleBTreeLeaf& right, size_t i);
|
||||
|
||||
struct Element {
|
||||
Key key;
|
||||
Data data;
|
||||
};
|
||||
typedef List<Element> ElementList;
|
||||
|
||||
Maybe<Pointer> next;
|
||||
Pointer self;
|
||||
ElementList elements;
|
||||
};
|
||||
|
||||
template <typename Key, typename Pointer>
|
||||
size_t SimpleBTreeIndex<Key, Pointer>::pointerCount() const {
|
||||
// If no begin pointer is set then the index is simply uninitialized.
|
||||
if (!beginPointer)
|
||||
return 0;
|
||||
else
|
||||
return pointers.size() + 1;
|
||||
}
|
||||
|
||||
template <typename Key, typename Pointer>
|
||||
Pointer SimpleBTreeIndex<Key, Pointer>::pointer(size_t i) const {
|
||||
if (i == 0)
|
||||
return *beginPointer;
|
||||
else
|
||||
return pointers.at(i - 1).pointer;
|
||||
}
|
||||
|
||||
template <typename Key, typename Pointer>
|
||||
void SimpleBTreeIndex<Key, Pointer>::updatePointer(size_t i, Pointer p) {
|
||||
if (i == 0)
|
||||
*beginPointer = p;
|
||||
else
|
||||
pointers.at(i - 1).pointer = p;
|
||||
}
|
||||
|
||||
template <typename Key, typename Pointer>
|
||||
Key const& SimpleBTreeIndex<Key, Pointer>::keyBefore(size_t i) const {
|
||||
return pointers.at(i - 1).key;
|
||||
}
|
||||
|
||||
template <typename Key, typename Pointer>
|
||||
void SimpleBTreeIndex<Key, Pointer>::updateKeyBefore(size_t i, Key k) {
|
||||
pointers.at(i - 1).key = k;
|
||||
}
|
||||
|
||||
template <typename Key, typename Pointer>
|
||||
void SimpleBTreeIndex<Key, Pointer>::removeBefore(size_t i) {
|
||||
if (i == 0) {
|
||||
beginPointer = pointers.at(0).pointer;
|
||||
pointers.eraseAt(0);
|
||||
} else {
|
||||
pointers.eraseAt(i - 1);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Key, typename Pointer>
|
||||
void SimpleBTreeIndex<Key, Pointer>::insertAfter(size_t i, Key k, Pointer p) {
|
||||
pointers.insertAt(i, Element{k, p});
|
||||
}
|
||||
|
||||
template <typename Key, typename Pointer>
|
||||
size_t SimpleBTreeIndex<Key, Pointer>::indexLevel() const {
|
||||
return level;
|
||||
}
|
||||
|
||||
template <typename Key, typename Pointer>
|
||||
void SimpleBTreeIndex<Key, Pointer>::setIndexLevel(size_t indexLevel) {
|
||||
level = indexLevel;
|
||||
}
|
||||
|
||||
template <typename Key, typename Pointer>
|
||||
void SimpleBTreeIndex<Key, Pointer>::shiftLeft(Key const& mid, SimpleBTreeIndex& right, size_t count) {
|
||||
count = std::min(right.pointerCount(), count);
|
||||
|
||||
if (count == 0)
|
||||
return;
|
||||
|
||||
pointers.append(Element{mid, *right.beginPointer});
|
||||
|
||||
typename ElementList::iterator s = right.pointers.begin();
|
||||
std::advance(s, count - 1);
|
||||
pointers.insert(pointers.end(), right.pointers.begin(), s);
|
||||
|
||||
right.pointers.erase(right.pointers.begin(), s);
|
||||
if (right.pointers.size() != 0) {
|
||||
right.beginPointer = right.pointers.at(0).pointer;
|
||||
right.pointers.eraseAt(0);
|
||||
} else {
|
||||
right.beginPointer.reset();
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Key, typename Pointer>
|
||||
void SimpleBTreeIndex<Key, Pointer>::shiftRight(Key const& mid, SimpleBTreeIndex& left, size_t count) {
|
||||
count = std::min(left.pointerCount(), count);
|
||||
|
||||
if (count == 0)
|
||||
return;
|
||||
--count;
|
||||
|
||||
pointers.insert(pointers.begin(), Element{mid, *beginPointer});
|
||||
|
||||
typename ElementList::iterator s = left.pointers.begin();
|
||||
std::advance(s, left.pointers.size() - count);
|
||||
pointers.insert(pointers.begin(), s, left.pointers.end());
|
||||
|
||||
left.pointers.erase(s, left.pointers.end());
|
||||
if (left.pointers.size() != 0) {
|
||||
beginPointer = left.pointers.at(left.pointers.size() - 1).pointer;
|
||||
left.pointers.eraseAt(left.pointers.size() - 1);
|
||||
} else {
|
||||
beginPointer = left.beginPointer.take();
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Key, typename Pointer>
|
||||
Key SimpleBTreeIndex<Key, Pointer>::split(SimpleBTreeIndex& right, size_t i) {
|
||||
typename ElementList::iterator s = pointers.begin();
|
||||
std::advance(s, i - 1);
|
||||
|
||||
right.beginPointer = s->pointer;
|
||||
Key midKey = s->key;
|
||||
right.level = level;
|
||||
++s;
|
||||
|
||||
right.pointers.insert(right.pointers.begin(), s, pointers.end());
|
||||
--s;
|
||||
|
||||
pointers.erase(s, pointers.end());
|
||||
|
||||
return midKey;
|
||||
}
|
||||
|
||||
template <typename Key, typename Data, typename Pointer>
|
||||
size_t SimpleBTreeLeaf<Key, Data, Pointer>::count() const {
|
||||
return elements.size();
|
||||
}
|
||||
|
||||
template <typename Key, typename Data, typename Pointer>
|
||||
Key const& SimpleBTreeLeaf<Key, Data, Pointer>::key(size_t i) const {
|
||||
return elements.at(i).key;
|
||||
}
|
||||
|
||||
template <typename Key, typename Data, typename Pointer>
|
||||
Data const& SimpleBTreeLeaf<Key, Data, Pointer>::data(size_t i) const {
|
||||
return elements.at(i).data;
|
||||
}
|
||||
|
||||
template <typename Key, typename Data, typename Pointer>
|
||||
void SimpleBTreeLeaf<Key, Data, Pointer>::insert(size_t i, Key k, Data d) {
|
||||
elements.insertAt(i, Element{move(k), move(d)});
|
||||
}
|
||||
|
||||
template <typename Key, typename Data, typename Pointer>
|
||||
void SimpleBTreeLeaf<Key, Data, Pointer>::remove(size_t i) {
|
||||
elements.eraseAt(i);
|
||||
}
|
||||
|
||||
template <typename Key, typename Data, typename Pointer>
|
||||
void SimpleBTreeLeaf<Key, Data, Pointer>::shiftLeft(SimpleBTreeLeaf& right, size_t count) {
|
||||
count = std::min(right.count(), count);
|
||||
|
||||
if (count == 0)
|
||||
return;
|
||||
|
||||
typename ElementList::iterator s = right.elements.begin();
|
||||
std::advance(s, count);
|
||||
|
||||
elements.insert(elements.end(), right.elements.begin(), s);
|
||||
right.elements.erase(right.elements.begin(), s);
|
||||
}
|
||||
|
||||
template <typename Key, typename Data, typename Pointer>
|
||||
void SimpleBTreeLeaf<Key, Data, Pointer>::shiftRight(SimpleBTreeLeaf& left, size_t count) {
|
||||
count = std::min(left.count(), count);
|
||||
|
||||
if (count == 0)
|
||||
return;
|
||||
|
||||
typename ElementList::iterator s = left.elements.begin();
|
||||
std::advance(s, left.elements.size() - count);
|
||||
|
||||
elements.insert(elements.begin(), s, left.elements.end());
|
||||
left.elements.erase(s, left.elements.end());
|
||||
}
|
||||
|
||||
template <typename Key, typename Data, typename Pointer>
|
||||
void SimpleBTreeLeaf<Key, Data, Pointer>::split(SimpleBTreeLeaf& right, size_t i) {
|
||||
typename ElementList::iterator s = elements.begin();
|
||||
std::advance(s, i);
|
||||
|
||||
right.elements.insert(right.elements.begin(), s, elements.end());
|
||||
elements.erase(s, elements.end());
|
||||
}
|
||||
|
||||
template <typename Key, typename Data, typename Pointer>
|
||||
Maybe<Pointer> SimpleBTreeLeaf<Key, Data, Pointer>::nextLeaf() const {
|
||||
return next;
|
||||
}
|
||||
|
||||
template <typename Key, typename Data, typename Pointer>
|
||||
void SimpleBTreeLeaf<Key, Data, Pointer>::setNextLeaf(Maybe<Pointer> n) {
|
||||
next = move(n);
|
||||
}
|
||||
|
||||
// Testing BTree class that simulates storage by storing in-memory copies of
|
||||
// nodes. Used to test BTree algorithm.
|
||||
|
||||
struct SimpleBTreeBase {
|
||||
typedef int Key;
|
||||
typedef String Data;
|
||||
typedef int Pointer;
|
||||
|
||||
typedef SimpleBTreeIndex<int, int> Index;
|
||||
typedef SimpleBTreeLeaf<int, String, int> Leaf;
|
||||
|
||||
Pointer rootPointer() {
|
||||
return root;
|
||||
}
|
||||
|
||||
bool rootIsLeaf() {
|
||||
return rootleaf;
|
||||
}
|
||||
|
||||
void setNewRoot(Pointer pointer, bool isLeaf) {
|
||||
root = pointer;
|
||||
rootleaf = isLeaf;
|
||||
|
||||
for (int i : deletedLeaves)
|
||||
leaves.remove(i);
|
||||
|
||||
for (int i : deletedIndexes)
|
||||
indexes.remove(i);
|
||||
|
||||
deletedLeaves.clear();
|
||||
deletedIndexes.clear();
|
||||
}
|
||||
|
||||
// Should create new empty leaf.
|
||||
Leaf createLeaf() {
|
||||
Leaf leaf;
|
||||
leaf.self = -1;
|
||||
return leaf;
|
||||
}
|
||||
|
||||
Leaf loadLeaf(Pointer const& pointer) {
|
||||
// To make sure to accurately test storage, always *copy* in and out
|
||||
return leaves.get(pointer);
|
||||
}
|
||||
|
||||
bool leafNeedsShift(Leaf const& leaf) {
|
||||
return leaf.count() < (maxLeafSize + 1) / 2;
|
||||
}
|
||||
|
||||
bool shouldAppendNewLeaf(Leaf const& leaf) {
|
||||
return maxLeafSize == 2 && leaf.count() == 2;
|
||||
}
|
||||
|
||||
bool leafShift(Leaf& left, Leaf& right) {
|
||||
if (left.count() + right.count() <= maxLeafSize) {
|
||||
left.shiftLeft(right, right.count());
|
||||
return true;
|
||||
} else {
|
||||
if (leafNeedsShift(right)) {
|
||||
right.shiftRight(left, 1);
|
||||
return true;
|
||||
} else if (leafNeedsShift(left)) {
|
||||
left.shiftLeft(right, 1);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Maybe<Leaf> leafSplit(Leaf& leaf) {
|
||||
if (leaf.count() <= maxLeafSize) {
|
||||
return {};
|
||||
} else {
|
||||
Leaf right;
|
||||
right.self = -1;
|
||||
|
||||
leaf.split(right, (leaf.count() + 1) / 2);
|
||||
|
||||
return right;
|
||||
}
|
||||
}
|
||||
|
||||
Pointer storeLeaf(Leaf leaf) {
|
||||
if (leaf.self != -1)
|
||||
deleteLeaf(leaf);
|
||||
|
||||
while (leaves.contains(leafId))
|
||||
++leafId;
|
||||
leaf.self = leafId;
|
||||
|
||||
// To make sure to accurately test storage, always *copy* in and out
|
||||
leaves[leafId] = leaf;
|
||||
|
||||
return leaf.self;
|
||||
}
|
||||
|
||||
void deleteLeaf(Leaf const& leaf) {
|
||||
deletedLeaves.append(leaf.self);
|
||||
}
|
||||
|
||||
// Should create new index with two pointers and one mid key.
|
||||
Index createIndex(Pointer beginPointer) {
|
||||
Index indexNode;
|
||||
indexNode.self = -1;
|
||||
indexNode.level = 0;
|
||||
indexNode.beginPointer = beginPointer;
|
||||
return indexNode;
|
||||
}
|
||||
|
||||
Index loadIndex(Pointer const& pointer) {
|
||||
return indexes.get(pointer);
|
||||
}
|
||||
|
||||
bool indexNeedsShift(Index const& index) {
|
||||
return index.pointerCount() < (maxIndexSize + 1) / 2;
|
||||
}
|
||||
|
||||
bool indexShift(Index& left, Key const& mid, Index& right) {
|
||||
if (left.pointerCount() + right.pointerCount() <= maxIndexSize) {
|
||||
left.shiftLeft(mid, right, right.pointerCount());
|
||||
return true;
|
||||
} else {
|
||||
if (indexNeedsShift(right)) {
|
||||
right.shiftRight(mid, left, 1);
|
||||
return true;
|
||||
} else if (indexNeedsShift(left)) {
|
||||
left.shiftLeft(mid, right, 1);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Maybe<pair<Key, Index>> indexSplit(Index& index) {
|
||||
if (index.pointerCount() <= maxIndexSize) {
|
||||
return {};
|
||||
} else {
|
||||
Index right;
|
||||
right.self = -1;
|
||||
|
||||
Key mid = index.split(right, (index.pointerCount() + 1) / 2);
|
||||
|
||||
return make_pair(mid, right);
|
||||
}
|
||||
}
|
||||
|
||||
Pointer storeIndex(Index index) {
|
||||
if (index.self != -1)
|
||||
deleteIndex(index);
|
||||
|
||||
while (indexes.contains(indexId))
|
||||
++indexId;
|
||||
index.self = indexId;
|
||||
|
||||
indexes[indexId] = index;
|
||||
|
||||
return index.self;
|
||||
}
|
||||
|
||||
void deleteIndex(Index const& index) {
|
||||
deletedIndexes.append(index.self);
|
||||
}
|
||||
|
||||
size_t indexPointerCount(Index const& index) {
|
||||
return index.pointerCount();
|
||||
}
|
||||
|
||||
Pointer indexPointer(Index const& index, size_t i) {
|
||||
return index.pointer(i);
|
||||
}
|
||||
|
||||
void indexUpdatePointer(Index& index, size_t i, Pointer p) {
|
||||
index.updatePointer(i, p);
|
||||
}
|
||||
|
||||
Key indexKeyBefore(Index const& index, size_t i) {
|
||||
return index.keyBefore(i);
|
||||
}
|
||||
|
||||
void indexUpdateKeyBefore(Index& index, size_t i, Key k) {
|
||||
index.updateKeyBefore(i, k);
|
||||
}
|
||||
|
||||
void indexRemoveBefore(Index& index, size_t i) {
|
||||
index.removeBefore(i);
|
||||
}
|
||||
|
||||
void indexInsertAfter(Index& index, size_t i, Key k, Pointer p) {
|
||||
index.insertAfter(i, k, p);
|
||||
}
|
||||
|
||||
size_t indexLevel(Index const& index) {
|
||||
return index.indexLevel();
|
||||
}
|
||||
|
||||
void setIndexLevel(Index& index, size_t indexLevel) {
|
||||
index.setIndexLevel(indexLevel);
|
||||
}
|
||||
|
||||
size_t leafElementCount(Leaf const& leaf) {
|
||||
return leaf.count();
|
||||
}
|
||||
|
||||
Key leafKey(Leaf const& leaf, size_t i) {
|
||||
return leaf.key(i);
|
||||
}
|
||||
|
||||
Data leafData(Leaf const& leaf, size_t i) {
|
||||
return leaf.data(i);
|
||||
}
|
||||
|
||||
void leafInsert(Leaf& leaf, size_t i, Key k, Data d) {
|
||||
return leaf.insert(i, k, d);
|
||||
}
|
||||
|
||||
void leafRemove(Leaf& leaf, size_t i) {
|
||||
return leaf.remove(i);
|
||||
}
|
||||
|
||||
Maybe<Pointer> nextLeaf(Leaf const& leaf) {
|
||||
return leaf.nextLeaf();
|
||||
}
|
||||
|
||||
void setNextLeaf(Leaf& leaf, Maybe<Pointer> n) {
|
||||
leaf.setNextLeaf(n);
|
||||
}
|
||||
|
||||
int root;
|
||||
bool rootleaf;
|
||||
|
||||
size_t maxIndexSize;
|
||||
size_t maxLeafSize;
|
||||
|
||||
int indexId;
|
||||
int leafId;
|
||||
|
||||
Map<int, Index> indexes;
|
||||
Map<int, Leaf> leaves;
|
||||
|
||||
List<int> deletedLeaves;
|
||||
List<int> deletedIndexes;
|
||||
};
|
||||
|
||||
struct SimpleBTree : public BTreeMixin<SimpleBTreeBase> {
|
||||
SimpleBTree(size_t maxisize, size_t maxlsize) {
|
||||
maxIndexSize = maxisize;
|
||||
maxLeafSize = maxlsize;
|
||||
|
||||
leafId = 0;
|
||||
indexId = 0;
|
||||
|
||||
createNewRoot();
|
||||
}
|
||||
|
||||
void print() {
|
||||
forAllNodes(Printer());
|
||||
cout << endl;
|
||||
}
|
||||
|
||||
struct Printer {
|
||||
bool operator()(Index const& index) {
|
||||
cout << "[" << index.level << ":" << index.self << "]"
|
||||
<< " " << index.beginPointer << " ";
|
||||
for (Index::Element e : index.pointers) {
|
||||
cout << "(" << e.key << ")"
|
||||
<< " " << e.pointer << " ";
|
||||
}
|
||||
cout << endl;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool operator()(Leaf const& leaf) {
|
||||
cout << "[" << leaf.self << "]"
|
||||
<< " ";
|
||||
for (Leaf::Element e : leaf.elements) {
|
||||
cout << "(" << e.key << ")"
|
||||
<< " " << e.data << " ";
|
||||
}
|
||||
cout << endl;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
const int RandFactor = 0xd5a2f037;
|
||||
const size_t TestCount = 500;
|
||||
const size_t WriteRepeat = 3;
|
||||
const size_t ShrinkCount = 5;
|
||||
|
||||
String genValue(int k) {
|
||||
return toString(k * RandFactor);
|
||||
}
|
||||
|
||||
bool checkValue(int k, String v) {
|
||||
return genValue(k) == v;
|
||||
}
|
||||
|
||||
void putAll(SimpleBTree& db, List<int> keys) {
|
||||
for (int k : keys)
|
||||
db.insert(k, genValue(k));
|
||||
}
|
||||
|
||||
void checkAll(SimpleBTree& db, List<int> keys) {
|
||||
for (int k : keys) {
|
||||
auto v = db.find(k);
|
||||
EXPECT_TRUE(checkValue(k, *v));
|
||||
}
|
||||
}
|
||||
|
||||
size_t removeAll(SimpleBTree& db, List<int> keys) {
|
||||
size_t totalRemoved = 0;
|
||||
Set<int> removed;
|
||||
for (int k : keys) {
|
||||
if (db.remove(k)) {
|
||||
EXPECT_FALSE(removed.contains(k));
|
||||
removed.add(k);
|
||||
++totalRemoved;
|
||||
}
|
||||
}
|
||||
return totalRemoved;
|
||||
}
|
||||
|
||||
void testBTree(size_t maxIndexSize, size_t maxLeafSize) {
|
||||
srand(time(0));
|
||||
|
||||
SimpleBTree db(maxIndexSize, maxLeafSize);
|
||||
|
||||
Set<int> keySet;
|
||||
while (keySet.size() < TestCount)
|
||||
keySet.add(rand());
|
||||
|
||||
List<int> keys;
|
||||
for (int k : keySet) {
|
||||
for (size_t j = 0; j < WriteRepeat; ++j)
|
||||
keys.append(k);
|
||||
}
|
||||
|
||||
// record writes/reads repeated WriteRepeat times randomly each cycle
|
||||
std::random_shuffle(keys.begin(), keys.end());
|
||||
putAll(db, keys);
|
||||
|
||||
EXPECT_EQ(db.recordCount(), TestCount);
|
||||
|
||||
std::random_shuffle(keys.begin(), keys.end());
|
||||
checkAll(db, keys);
|
||||
|
||||
// Random reads/writes with ShrinkCount cycles...
|
||||
for (size_t i = 0; i < ShrinkCount; ++i) {
|
||||
std::random_shuffle(keys.begin(), keys.end());
|
||||
List<int> keysTemp = keys.slice(0, keys.size() / 2);
|
||||
|
||||
removeAll(db, keysTemp);
|
||||
|
||||
std::random_shuffle(keysTemp.begin(), keysTemp.end());
|
||||
putAll(db, keysTemp);
|
||||
|
||||
std::random_shuffle(keysTemp.begin(), keysTemp.end());
|
||||
checkAll(db, keys);
|
||||
}
|
||||
|
||||
size_t totalRemoved = removeAll(db, keys);
|
||||
EXPECT_EQ(totalRemoved, TestCount);
|
||||
}
|
||||
|
||||
TEST(BTreeTest, All) {
|
||||
testBTree(3, 2);
|
||||
testBTree(6, 6);
|
||||
}
|
33
source/test/byte_array_test.cpp
Normal file
33
source/test/byte_array_test.cpp
Normal file
|
@ -0,0 +1,33 @@
|
|||
#include "StarByteArray.hpp"
|
||||
#include "StarEncode.hpp"
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
using namespace Star;
|
||||
|
||||
TEST(ByteArrayTest, All) {
|
||||
auto res = ByteArray::fromCString("foobar");
|
||||
res.insert(2, 'a');
|
||||
res.insert(6, 'b');
|
||||
res.push_back('c');
|
||||
res.insert(9, 'd');
|
||||
EXPECT_EQ(res, ByteArray::fromCString("foaobabrcd"));
|
||||
|
||||
auto a = hexDecode("0a0a0a");
|
||||
auto b = hexDecode("a0a0a0");
|
||||
auto c = hexDecode("818181");
|
||||
auto d = hexDecode("aaaaaa");
|
||||
auto e = hexDecode("000000");
|
||||
auto f = hexDecode("212121");
|
||||
|
||||
auto g = hexDecode("a0a0a0");
|
||||
auto h = hexDecode("8181818181");
|
||||
auto i = hexDecode("2121218181");
|
||||
|
||||
EXPECT_EQ(a.andWith(b), e);
|
||||
EXPECT_EQ(a.orWith(b), d);
|
||||
EXPECT_EQ(b.xorWith(c), f);
|
||||
EXPECT_EQ(g.xorWith(h), f);
|
||||
EXPECT_EQ(g.xorWith(h, true), i);
|
||||
EXPECT_EQ(h.xorWith(g, true), i);
|
||||
}
|
29
source/test/clock_test.cpp
Normal file
29
source/test/clock_test.cpp
Normal file
|
@ -0,0 +1,29 @@
|
|||
#include "StarTime.hpp"
|
||||
#include "StarThread.hpp"
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
using namespace Star;
|
||||
|
||||
TEST(ClockTest, All) {
|
||||
Clock clock;
|
||||
|
||||
Thread::sleepPrecise(1000);
|
||||
|
||||
// Pick wide range in case the system is acting iffy, it's just to check that
|
||||
// the clock is progressing properly.
|
||||
EXPECT_GT(clock.time(), 0.8);
|
||||
EXPECT_LT(clock.time(), 8.0);
|
||||
|
||||
double time = clock.time();
|
||||
clock.stop();
|
||||
Thread::sleepPrecise(1000);
|
||||
EXPECT_EQ(clock.time(), time);
|
||||
|
||||
clock.reset();
|
||||
EXPECT_EQ(clock.time(), 0.0);
|
||||
|
||||
Timer nullTimer;
|
||||
EXPECT_TRUE(nullTimer.timeUp());
|
||||
EXPECT_FALSE(nullTimer.running());
|
||||
}
|
37
source/test/color_test.cpp
Normal file
37
source/test/color_test.cpp
Normal file
|
@ -0,0 +1,37 @@
|
|||
#include "StarColor.hpp"
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
using namespace Star;
|
||||
|
||||
TEST(ColorTest, ColorTemperature) {
|
||||
Color temp1000 = Color::temperature(1000);
|
||||
Color temp3000 = Color::temperature(3000);
|
||||
Color temp5000 = Color::temperature(5000);
|
||||
Color temp6000 = Color::temperature(6000);
|
||||
Color temp7000 = Color::temperature(7000);
|
||||
Color temp10000 = Color::temperature(10000);
|
||||
Color temp20000 = Color::temperature(20000);
|
||||
|
||||
EXPECT_EQ(temp1000.red(), 255);
|
||||
EXPECT_EQ(temp1000.green(), 68);
|
||||
EXPECT_EQ(temp1000.blue(), 0);
|
||||
EXPECT_EQ(temp3000.red(), 255);
|
||||
EXPECT_EQ(temp3000.green(), 177);
|
||||
EXPECT_EQ(temp3000.blue(), 110);
|
||||
EXPECT_EQ(temp5000.red(), 255);
|
||||
EXPECT_EQ(temp5000.green(), 228);
|
||||
EXPECT_EQ(temp5000.blue(), 206);
|
||||
EXPECT_EQ(temp6000.red(), 255);
|
||||
EXPECT_EQ(temp6000.green(), 246);
|
||||
EXPECT_EQ(temp6000.blue(), 237);
|
||||
EXPECT_EQ(temp7000.red(), 243);
|
||||
EXPECT_EQ(temp7000.green(), 242);
|
||||
EXPECT_EQ(temp7000.blue(), 255);
|
||||
EXPECT_EQ(temp10000.red(), 202);
|
||||
EXPECT_EQ(temp10000.green(), 218);
|
||||
EXPECT_EQ(temp10000.blue(), 255);
|
||||
EXPECT_EQ(temp20000.red(), 171);
|
||||
EXPECT_EQ(temp20000.green(), 198);
|
||||
EXPECT_EQ(temp20000.blue(), 255);
|
||||
}
|
13
source/test/container_test.cpp
Normal file
13
source/test/container_test.cpp
Normal file
|
@ -0,0 +1,13 @@
|
|||
#include "StarSet.hpp"
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
using namespace Star;
|
||||
|
||||
TEST(SetTest, All) {
|
||||
Set<int> a = {1, 2, 3, 4};
|
||||
Set<int> b = {2, 4, 5};
|
||||
EXPECT_EQ(a.difference(b), Set<int>({1, 3}));
|
||||
EXPECT_EQ(a.intersection(b), Set<int>({2, 4}));
|
||||
EXPECT_EQ(a.combination(b), Set<int>({1, 2, 3, 4, 5}));
|
||||
}
|
7
source/test/core_tests_main.cpp
Normal file
7
source/test/core_tests_main.cpp
Normal file
|
@ -0,0 +1,7 @@
|
|||
#include "gtest/gtest.h"
|
||||
|
||||
GTEST_API_ int main(int argc, char** argv) {
|
||||
testing::InitGoogleTest(&argc, argv);
|
||||
int res = RUN_ALL_TESTS();
|
||||
return res;
|
||||
}
|
36
source/test/encode_test.cpp
Normal file
36
source/test/encode_test.cpp
Normal file
|
@ -0,0 +1,36 @@
|
|||
#include "StarEncode.hpp"
|
||||
#include "StarSha256.hpp"
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
using namespace Star;
|
||||
|
||||
TEST(EncodeTest, Base64) {
|
||||
char const* data =
|
||||
"Man is distinguished, not only by his reason, but by this singular passion from other animals, which is a lust "
|
||||
"of the mind, that by a perseverance of delight in the continued and indefatigable generation of knowledge, "
|
||||
"exceeds the short vehemence of any carnal pleasure.";
|
||||
ByteArray testSource = ByteArray(data, strlen(data));
|
||||
String testEncoded =
|
||||
"TWFuIGlzIGRpc3Rpbmd1aXNoZWQsIG5vdCBvbmx5IGJ5IGhpcyByZWFzb24sIGJ1dCBieSB0aGlzIHNpbmd1bGFyIHBhc3Npb24gZnJvbSBvdGhl"
|
||||
"ciBhbmltYWxzLCB3aGljaCBpcyBhIGx1c3Qgb2YgdGhlIG1pbmQsIHRoYXQgYnkgYSBwZXJzZXZlcmFuY2Ugb2YgZGVsaWdodCBpbiB0aGUgY29u"
|
||||
"dGludWVkIGFuZCBpbmRlZmF0aWdhYmxlIGdlbmVyYXRpb24gb2Yga25vd2xlZGdlLCBleGNlZWRzIHRoZSBzaG9ydCB2ZWhlbWVuY2Ugb2YgYW55"
|
||||
"IGNhcm5hbCBwbGVhc3VyZS4=";
|
||||
|
||||
String encoded = base64Encode(testSource);
|
||||
ByteArray decoded = base64Decode(encoded);
|
||||
|
||||
EXPECT_EQ(encoded, testEncoded);
|
||||
EXPECT_EQ(decoded, testSource);
|
||||
}
|
||||
|
||||
TEST(EncodeTest, SHA256) {
|
||||
char const* data =
|
||||
"Man is distinguished, not only by his reason, but by this singular passion from other animals, which is a lust "
|
||||
"of the mind, that by a perseverance of delight in the continued and indefatigable generation of knowledge, "
|
||||
"exceeds the short vehemence of any carnal pleasure.";
|
||||
ByteArray testSource = ByteArray(data, strlen(data));
|
||||
ByteArray testHash = hexDecode("78fe75026c4390ceccc4e9e6a9428ba8ae5968b458e60b5eebecd682cf24bbf2");
|
||||
|
||||
EXPECT_EQ(sha256(testSource), testHash);
|
||||
}
|
51
source/test/file_test.cpp
Normal file
51
source/test/file_test.cpp
Normal file
|
@ -0,0 +1,51 @@
|
|||
#include "StarFile.hpp"
|
||||
#include "StarString.hpp"
|
||||
#include "StarFormat.hpp"
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
using namespace Star;
|
||||
|
||||
TEST(FileTest, All) {
|
||||
auto file = File::ephemeralFile();
|
||||
file->resize(1000);
|
||||
file->resize(0);
|
||||
file->resize(500);
|
||||
EXPECT_EQ(file->size(), 500);
|
||||
|
||||
auto dir = File::temporaryDirectory();
|
||||
File::makeDirectory(File::relativeTo(dir, "inner"));
|
||||
EXPECT_TRUE(File::isDirectory(File::relativeTo(dir, "inner") + "/"));
|
||||
File::removeDirectoryRecursive(dir);
|
||||
|
||||
#ifdef STAR_SYSTEM_FAMILY_WINDOWS
|
||||
EXPECT_EQ(File::baseName("/foo/bar"), "bar");
|
||||
EXPECT_EQ(File::baseName("\\foo\\bar\\"), "bar");
|
||||
EXPECT_EQ(File::baseName("/foo/bar/baz"), "baz");
|
||||
EXPECT_EQ(File::dirName("\\foo\\bar"), "\\foo");
|
||||
EXPECT_EQ(File::dirName("/foo\\bar/"), "/foo");
|
||||
EXPECT_EQ(File::dirName("/foo/bar\\baz"), "/foo/bar");
|
||||
EXPECT_EQ(File::dirName("foo/bar/baz"), "foo/bar");
|
||||
|
||||
EXPECT_EQ(File::relativeTo("c:\\foo\\", "bar"), "c:\\foo\\bar");
|
||||
EXPECT_EQ(File::relativeTo("c:\\foo", "bar"), "c:\\foo\\bar");
|
||||
EXPECT_EQ(File::relativeTo("c:\\foo\\", "\\bar"), "\\bar");
|
||||
EXPECT_EQ(File::relativeTo("c:\\foo\\", ".\\bar"), "c:\\foo\\bar");
|
||||
EXPECT_EQ(File::relativeTo("c:\\foo\\.", ".\\bar"), "c:\\foo\\bar");
|
||||
EXPECT_EQ(File::relativeTo("c:\\foo\\.", "c:\\bar"), "c:\\bar");
|
||||
EXPECT_EQ(File::relativeTo("c:\\foo\\.", "c:bar\\"), "c:bar\\");
|
||||
EXPECT_EQ(File::relativeTo("c:\\foo.", "bar"), "c:\\foo.\\bar");
|
||||
#else
|
||||
EXPECT_EQ(File::baseName("/foo/bar"), "bar");
|
||||
EXPECT_EQ(File::baseName("/foo/bar/"), "bar");
|
||||
EXPECT_EQ(File::baseName("/foo/bar/baz"), "baz");
|
||||
EXPECT_EQ(File::dirName("/foo/bar"), "/foo");
|
||||
EXPECT_EQ(File::dirName("/foo/bar/"), "/foo");
|
||||
EXPECT_EQ(File::dirName("/foo/bar/baz"), "/foo/bar");
|
||||
EXPECT_EQ(File::dirName("foo/bar/baz"), "foo/bar");
|
||||
|
||||
EXPECT_EQ(File::relativeTo("/foo", "bar"), "/foo/bar");
|
||||
EXPECT_EQ(File::relativeTo("/foo", "bar/"), "/foo/bar/");
|
||||
EXPECT_EQ(File::relativeTo("/foo", "/bar/"), "/bar/");
|
||||
#endif
|
||||
}
|
220
source/test/flat_hash_test.cpp
Normal file
220
source/test/flat_hash_test.cpp
Normal file
|
@ -0,0 +1,220 @@
|
|||
#include "StarFlatHashSet.hpp"
|
||||
#include "StarFlatHashMap.hpp"
|
||||
#include "StarRandom.hpp"
|
||||
#include "StarVector.hpp"
|
||||
#include "StarIterator.hpp"
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
using namespace Star;
|
||||
|
||||
TEST(FlatHashSet, Preset) {
|
||||
FlatHashSet<int> testSet = {42, 63};
|
||||
ASSERT_EQ(testSet.find(41), testSet.end());
|
||||
ASSERT_EQ(*testSet.find(42), 42);
|
||||
ASSERT_EQ(*testSet.find(63), 63);
|
||||
ASSERT_EQ(testSet.find(64), testSet.end());
|
||||
ASSERT_EQ(testSet.size(), 2u);
|
||||
|
||||
testSet.erase(testSet.find(42));
|
||||
ASSERT_EQ(testSet.find(42), testSet.end());
|
||||
ASSERT_EQ(*testSet.find(63), 63);
|
||||
ASSERT_EQ(testSet.size(), 1u);
|
||||
|
||||
testSet.erase(testSet.find(63));
|
||||
ASSERT_EQ(testSet.find(42), testSet.end());
|
||||
ASSERT_EQ(testSet.find(63), testSet.end());
|
||||
ASSERT_EQ(testSet.size(), 0u);
|
||||
|
||||
testSet.insert(12);
|
||||
testSet.insert(24);
|
||||
ASSERT_EQ(*testSet.find(12), 12);
|
||||
ASSERT_EQ(testSet.size(), 2u);
|
||||
testSet.clear();
|
||||
|
||||
ASSERT_EQ(testSet.find(12), testSet.end());
|
||||
ASSERT_EQ(testSet.size(), 0u);
|
||||
|
||||
EXPECT_TRUE(testSet.insert(7).second);
|
||||
EXPECT_TRUE(testSet.insert(11).second);
|
||||
EXPECT_FALSE(testSet.insert(7).second);
|
||||
|
||||
ASSERT_EQ(testSet.size(), 2u);
|
||||
|
||||
FlatHashSet<int> testSet2(testSet.begin(), testSet.end());
|
||||
ASSERT_EQ(testSet, testSet2);
|
||||
|
||||
ASSERT_EQ(testSet.erase(testSet.begin(), testSet.end()), testSet.end());
|
||||
ASSERT_EQ(testSet.size(), 0u);
|
||||
|
||||
ASSERT_NE(testSet, testSet2);
|
||||
|
||||
FlatHashSet<int> testSet3(testSet.begin(), testSet.end());
|
||||
ASSERT_EQ(testSet3.size(), 0u);
|
||||
|
||||
testSet2 = testSet;
|
||||
ASSERT_EQ(testSet, testSet2);
|
||||
}
|
||||
|
||||
TEST(FlatHashSet, Random) {
|
||||
List<Vec2I> keys;
|
||||
for (unsigned i = 0; i < 100000; ++i)
|
||||
keys.append(Vec2I(i * 743202097, i * 205495087));
|
||||
Random::shuffle(keys);
|
||||
|
||||
FlatHashSet<Vec2I> testSet;
|
||||
for (size_t i = 0; i < keys.size(); ++i)
|
||||
testSet.insert(keys[i]);
|
||||
|
||||
Random::shuffle(keys);
|
||||
for (size_t i = 0; i < keys.size() / 2; ++i)
|
||||
testSet.erase(keys[i]);
|
||||
|
||||
Random::shuffle(keys);
|
||||
for (size_t i = 0; i < keys.size() / 3; ++i)
|
||||
testSet.insert(keys[i]);
|
||||
|
||||
Random::shuffle(keys);
|
||||
for (size_t i = 0; i < keys.size() / 2; ++i)
|
||||
testSet.erase(keys[i]);
|
||||
|
||||
Random::shuffle(keys);
|
||||
for (auto k : keys) {
|
||||
auto i = testSet.find(k);
|
||||
if (i != testSet.end())
|
||||
ASSERT_TRUE(*i == k);
|
||||
}
|
||||
|
||||
Random::shuffle(keys);
|
||||
for (auto k : keys) {
|
||||
testSet.insert(k);
|
||||
ASSERT_TRUE(testSet.find(k) != testSet.end());
|
||||
}
|
||||
|
||||
List<Vec2I> cmp;
|
||||
for (auto const& k : testSet)
|
||||
cmp.append(k);
|
||||
cmp.sort();
|
||||
keys.sort();
|
||||
ASSERT_TRUE(cmp == keys);
|
||||
|
||||
Random::shuffle(keys);
|
||||
for (auto k : keys) {
|
||||
testSet.erase(k);
|
||||
ASSERT_TRUE(testSet.find(k) == testSet.end());
|
||||
}
|
||||
|
||||
ASSERT_TRUE(testSet.empty());
|
||||
}
|
||||
|
||||
TEST(FlatHashMap, Preset) {
|
||||
FlatHashMap<int, int> testMap = {{42, 42}, {63, 63}};
|
||||
ASSERT_EQ(testMap.find(41), testMap.end());
|
||||
ASSERT_EQ(testMap.find(42)->second, 42);
|
||||
ASSERT_EQ(testMap.find(63)->second, 63);
|
||||
ASSERT_EQ(testMap.find(64), testMap.end());
|
||||
ASSERT_EQ(testMap.size(), 2u);
|
||||
|
||||
testMap.erase(testMap.find(42));
|
||||
ASSERT_EQ(testMap.find(42), testMap.end());
|
||||
ASSERT_EQ(testMap.find(63)->second, 63);
|
||||
ASSERT_EQ(testMap.size(), 1u);
|
||||
|
||||
testMap.erase(testMap.find(63));
|
||||
ASSERT_EQ(testMap.find(42), testMap.end());
|
||||
ASSERT_EQ(testMap.find(63), testMap.end());
|
||||
ASSERT_EQ(testMap.size(), 0u);
|
||||
|
||||
testMap.insert({12, 12});
|
||||
testMap.insert({24, 24});
|
||||
ASSERT_EQ(testMap.find(12)->second, 12);
|
||||
ASSERT_EQ(testMap.size(), 2u);
|
||||
testMap.clear();
|
||||
|
||||
ASSERT_EQ(testMap.find(12), testMap.end());
|
||||
ASSERT_EQ(testMap.size(), 0u);
|
||||
|
||||
EXPECT_TRUE(testMap.insert({7, 7}).second);
|
||||
EXPECT_TRUE(testMap.insert({11, 11}).second);
|
||||
EXPECT_FALSE(testMap.insert({7, 7}).second);
|
||||
|
||||
ASSERT_EQ(testMap.size(), 2u);
|
||||
|
||||
FlatHashMap<int, int> testMap2(testMap.begin(), testMap.end());
|
||||
ASSERT_EQ(testMap, testMap2);
|
||||
|
||||
ASSERT_EQ(testMap.erase(testMap.begin(), testMap.end()), testMap.end());
|
||||
ASSERT_EQ(testMap.size(), 0u);
|
||||
|
||||
ASSERT_NE(testMap, testMap2);
|
||||
|
||||
FlatHashMap<int, int> testMap3(testMap.begin(), testMap.end());
|
||||
ASSERT_EQ(testMap3.size(), 0u);
|
||||
|
||||
testMap2 = testMap;
|
||||
ASSERT_EQ(testMap, testMap2);
|
||||
}
|
||||
|
||||
TEST(FlatHashMap, Random) {
|
||||
List<pair<Vec2I, int>> values;
|
||||
for (unsigned i = 0; i < 100000; ++i)
|
||||
values.append({Vec2I(i * 743202097, i * 205495087), i});
|
||||
Random::shuffle(values);
|
||||
|
||||
FlatHashMap<Vec2I, int> testMap;
|
||||
for (auto v : values)
|
||||
testMap.insert(v);
|
||||
|
||||
Random::shuffle(values);
|
||||
for (size_t i = 0; i < values.size() / 2; ++i)
|
||||
testMap.erase(values[i].first);
|
||||
|
||||
Random::shuffle(values);
|
||||
for (size_t i = 0; i < values.size() / 3; ++i)
|
||||
testMap.insert(values[i]);
|
||||
|
||||
Random::shuffle(values);
|
||||
for (size_t i = 0; i < values.size() / 2; ++i)
|
||||
testMap.erase(values[i].first);
|
||||
|
||||
Random::shuffle(values);
|
||||
for (auto v : values) {
|
||||
auto i = testMap.find(v.first);
|
||||
if (i != testMap.end())
|
||||
ASSERT_TRUE(i->second == v.second);
|
||||
}
|
||||
|
||||
Random::shuffle(values);
|
||||
for (auto v : values) {
|
||||
ASSERT_TRUE(testMap.insert(v).first->second == v.second);
|
||||
ASSERT_TRUE(testMap.at(v.first) == v.second);
|
||||
}
|
||||
|
||||
Random::shuffle(values);
|
||||
for (auto v : values) {
|
||||
ASSERT_EQ(testMap.erase(v.first), 1u);
|
||||
ASSERT_TRUE(testMap.find(v.first) == testMap.end());
|
||||
}
|
||||
|
||||
ASSERT_TRUE(testMap.empty());
|
||||
}
|
||||
|
||||
TEST(FlatHashMap, Iterator) {
|
||||
List<pair<Vec2I, int>> values;
|
||||
for (unsigned i = 0; i < 100000; ++i)
|
||||
values.append({Vec2I(i * 743202097, i * 205495087), i});
|
||||
|
||||
FlatHashMap<Vec2I, int> testMap;
|
||||
for (auto v : values)
|
||||
testMap.insert(v);
|
||||
|
||||
auto it = makeSMutableMapIterator(testMap);
|
||||
while (it.hasNext()) {
|
||||
if (it.next().second % 3 == 0)
|
||||
it.remove();
|
||||
}
|
||||
|
||||
auto i = values.begin();
|
||||
std::advance(i, values.size());
|
||||
ASSERT_EQ(i, values.end());
|
||||
}
|
371
source/test/formatted_json_test.cpp
Normal file
371
source/test/formatted_json_test.cpp
Normal file
|
@ -0,0 +1,371 @@
|
|||
#include "StarFormattedJson.hpp"
|
||||
#include "StarJsonPath.hpp"
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
using namespace Star;
|
||||
|
||||
TEST(FormattedJsonTest, JsonInterop) {
|
||||
Json array1 = JsonArray{1, 2, 3, 4};
|
||||
Json array2 = JsonArray{4, 3, 2, 1};
|
||||
FormattedJson farray1 = array1;
|
||||
FormattedJson farray2 = array2;
|
||||
|
||||
EXPECT_EQ(farray1.toJson(), array1);
|
||||
EXPECT_EQ(farray2.toJson(), array2);
|
||||
EXPECT_NE(farray1.toJson(), array2);
|
||||
EXPECT_NE(farray2.toJson(), array1);
|
||||
}
|
||||
|
||||
TEST(FormattedJsonTest, Parsing) {
|
||||
FormattedJson json1 = FormattedJson::parse(R"JSON(
|
||||
{
|
||||
"foo": "bar",
|
||||
"hello" : "world",
|
||||
"abc" :123
|
||||
// Comment
|
||||
,"wat": {
|
||||
"thing": [
|
||||
49,
|
||||
27]
|
||||
}
|
||||
}
|
||||
)JSON");
|
||||
FormattedJson::ElementList expectedElements = {
|
||||
WhitespaceElement{"\n "},
|
||||
ObjectKeyElement{"foo"},
|
||||
ColonElement{},
|
||||
WhitespaceElement{" "},
|
||||
ValueElement{Json{"bar"}},
|
||||
CommaElement{},
|
||||
WhitespaceElement{"\n "},
|
||||
ObjectKeyElement{"hello"},
|
||||
WhitespaceElement{" "},
|
||||
ColonElement{},
|
||||
WhitespaceElement{" "},
|
||||
ValueElement{Json{"world"}},
|
||||
CommaElement{},
|
||||
WhitespaceElement{"\n "},
|
||||
ObjectKeyElement{"abc"},
|
||||
WhitespaceElement{" "},
|
||||
ColonElement{},
|
||||
ValueElement{Json{123}},
|
||||
WhitespaceElement{"\n // Comment\n "},
|
||||
CommaElement{},
|
||||
ObjectKeyElement{"wat"},
|
||||
ColonElement{},
|
||||
WhitespaceElement{" "},
|
||||
ValueElement{Json{JsonObject{{"thing", JsonArray{49, 27}}}}},
|
||||
WhitespaceElement{"\n "}
|
||||
};
|
||||
EXPECT_EQ(json1.elements(), expectedElements);
|
||||
EXPECT_EQ(json1.get("foo"), FormattedJson{"bar"});
|
||||
EXPECT_EQ(json1.get("abc"), FormattedJson{123});
|
||||
EXPECT_EQ(json1.get("wat").get("thing").get(1), FormattedJson{27});
|
||||
EXPECT_NE(json1.get("wat").get("thing").get(0), FormattedJson{66});
|
||||
|
||||
ASSERT_THROW(FormattedJson::parse(" "), JsonParsingException);
|
||||
ASSERT_THROW(FormattedJson::parse("/* */"), JsonParsingException);
|
||||
ASSERT_THROW(FormattedJson::parse("x"), JsonParsingException);
|
||||
ASSERT_THROW(FormattedJson::parseJson("123"), JsonParsingException);
|
||||
ASSERT_THROW(FormattedJson::parseJson("\"foo\""), JsonParsingException);
|
||||
EXPECT_TRUE(FormattedJson::parse("123").isType(Json::Type::Int));
|
||||
EXPECT_TRUE(FormattedJson::parse("\"foo\"").isType(Json::Type::String));
|
||||
}
|
||||
|
||||
List<String> keyOrder(FormattedJson const& json) {
|
||||
List<String> keys;
|
||||
for (JsonElement const& elem : json.elements()) {
|
||||
if (elem.is<ObjectKeyElement>())
|
||||
keys.append(elem.get<ObjectKeyElement>().key);
|
||||
}
|
||||
return keys;
|
||||
}
|
||||
|
||||
TEST(FormattedJsonTest, ObjectInsertion) {
|
||||
FormattedJson json = FormattedJson::ofType(Json::Type::Object);
|
||||
List<String> expectedKeys;
|
||||
|
||||
EXPECT_EQ(keyOrder(json), expectedKeys);
|
||||
|
||||
json = json.set("foo", Json{"bar"});
|
||||
expectedKeys.append("foo");
|
||||
EXPECT_EQ(keyOrder(json), expectedKeys);
|
||||
|
||||
json = json.set("baz", Json{"..."});
|
||||
expectedKeys.append("baz");
|
||||
EXPECT_EQ(keyOrder(json), expectedKeys);
|
||||
|
||||
json = json.prepend("hello", Json{"world"});
|
||||
expectedKeys.insertAt(0, "hello");
|
||||
EXPECT_EQ(keyOrder(json), expectedKeys);
|
||||
|
||||
json = json.insertBefore("lala", Json{"alal"}, "foo");
|
||||
expectedKeys.insertAt(1, "lala");
|
||||
EXPECT_EQ(keyOrder(json), expectedKeys);
|
||||
|
||||
json = json.insertAfter("lorem", Json{"ipsum"}, "foo");
|
||||
expectedKeys.insertAt(3, "lorem");
|
||||
EXPECT_EQ(keyOrder(json), expectedKeys);
|
||||
|
||||
json = json.append("dolor", Json{"sit amet"});
|
||||
expectedKeys.append("dolor");
|
||||
EXPECT_EQ(keyOrder(json), expectedKeys);
|
||||
|
||||
// If the key already exists, the key order doesn't change.
|
||||
json = json.set("foo", Json{123}).append("hello", Json{123}).insertAfter("dolor", Json{123}, "baz");
|
||||
EXPECT_EQ(keyOrder(json), expectedKeys);
|
||||
|
||||
FormattedJson::ElementList expectedElements = {ObjectKeyElement{"hello"},
|
||||
ColonElement{},
|
||||
ValueElement{Json{123}},
|
||||
CommaElement{},
|
||||
ObjectKeyElement{"lala"},
|
||||
ColonElement{},
|
||||
ValueElement{Json{"alal"}},
|
||||
CommaElement{},
|
||||
ObjectKeyElement{"foo"},
|
||||
ColonElement{},
|
||||
ValueElement{Json{123}},
|
||||
CommaElement{},
|
||||
ObjectKeyElement{"lorem"},
|
||||
ColonElement{},
|
||||
ValueElement{Json{"ipsum"}},
|
||||
CommaElement{},
|
||||
ObjectKeyElement{"baz"},
|
||||
ColonElement{},
|
||||
ValueElement{Json{"..."}},
|
||||
CommaElement{},
|
||||
ObjectKeyElement{"dolor"},
|
||||
ColonElement{},
|
||||
ValueElement{Json{123}}};
|
||||
EXPECT_EQ(json.elements(), expectedElements);
|
||||
|
||||
FormattedJson emptyObject = FormattedJson::ofType(Json::Type::Object);
|
||||
ASSERT_THROW(emptyObject.insertBefore("foo", Json{}, "bar"), JsonException);
|
||||
ASSERT_THROW(emptyObject.insertAfter("foo", Json{}, "bar"), JsonException);
|
||||
ASSERT_THROW(FormattedJson::ofType(Json::Type::Array).set("foo", Json{}), JsonException);
|
||||
}
|
||||
|
||||
TEST(FormattedJsonTest, ObjectInsertionWithWhitespace) {
|
||||
FormattedJson json = FormattedJson::parse(" { \"foo\": 123 } ");
|
||||
json = json.append("hello", Json{"world"});
|
||||
json = json.prepend("lorem", Json{"ipsum"});
|
||||
EXPECT_EQ(json.repr(),
|
||||
R"JSON({ "lorem": "ipsum", "foo": 123, "hello": "world" })JSON");
|
||||
}
|
||||
|
||||
TEST(FormattedJsonTest, ArrayInsertion) {
|
||||
FormattedJson json = FormattedJson::parse(R"JSON([
|
||||
12,
|
||||
34
|
||||
])JSON");
|
||||
json = json.insert(1, Json{23});
|
||||
json = json.append(Json{45});
|
||||
json = json.set(0, Json{"01"});
|
||||
json = json.insert(0, Json{0});
|
||||
char const* expected = R"JSON([
|
||||
0,
|
||||
"01",
|
||||
23,
|
||||
34,
|
||||
45
|
||||
])JSON";
|
||||
EXPECT_EQ(json.repr(), expected);
|
||||
|
||||
FormattedJson emptyArray = FormattedJson::ofType(Json::Type::Array);
|
||||
EXPECT_EQ(emptyArray.insert(0, Json{}).size(), 1u);
|
||||
ASSERT_THROW(emptyArray.insert(1, Json{}), JsonException);
|
||||
ASSERT_THROW(FormattedJson::ofType(Json::Type::Object).insert(0, Json{}), JsonException);
|
||||
}
|
||||
|
||||
TEST(FormattedJsonTest, ObjectErase) {
|
||||
FormattedJson json = FormattedJson::parse(R"JSON({
|
||||
"zzz": 123,
|
||||
"mmm": 456,
|
||||
"aaa": 789
|
||||
})JSON");
|
||||
json = json.eraseKey("mmm");
|
||||
char const* expected = R"JSON({
|
||||
"zzz": 123,
|
||||
"aaa": 789
|
||||
})JSON";
|
||||
EXPECT_EQ(json.repr(), expected);
|
||||
|
||||
FormattedJson jsonNoZ = json.eraseKey("zzz");
|
||||
expected = R"JSON({
|
||||
"aaa": 789
|
||||
})JSON";
|
||||
EXPECT_EQ(jsonNoZ.repr(), expected);
|
||||
|
||||
FormattedJson jsonNoA = json.eraseKey("aaa");
|
||||
expected = R"JSON({
|
||||
"zzz": 123
|
||||
})JSON";
|
||||
EXPECT_EQ(jsonNoA.repr(), expected);
|
||||
|
||||
ASSERT_EQ(json.eraseKey("bbb"), json);
|
||||
ASSERT_THROW(FormattedJson::ofType(Json::Type::Array).eraseKey("foo"), JsonException);
|
||||
}
|
||||
|
||||
TEST(FormattedJsonTest, ArrayErase) {
|
||||
FormattedJson json = FormattedJson::parse("[123, 456, 789]");
|
||||
EXPECT_EQ(json.eraseIndex(0).repr(), "[456, 789]");
|
||||
EXPECT_EQ(json.eraseIndex(1).repr(), "[123, 789]");
|
||||
EXPECT_EQ(json.eraseIndex(2).repr(), "[123, 456]");
|
||||
EXPECT_EQ(json.eraseIndex(0).eraseIndex(0).repr(), "[789]");
|
||||
EXPECT_EQ(json.eraseIndex(0).eraseIndex(0).eraseIndex(0).repr(), "[]");
|
||||
|
||||
ASSERT_THROW(FormattedJson::ofType(Json::Type::Object).eraseIndex(0), JsonException);
|
||||
}
|
||||
|
||||
TEST(FormattedJsonTest, CommentPreservation) {
|
||||
FormattedJson json = FormattedJson::parse(R"JSON({
|
||||
// This is a comment
|
||||
"hello": 1,
|
||||
"world": 2
|
||||
})JSON");
|
||||
json = json.insertBefore("goodbye", Json{1}, "world");
|
||||
json = json.eraseKey("hello");
|
||||
char const* expected = R"JSON({
|
||||
// This is a comment
|
||||
"goodbye": 1,
|
||||
"world": 2
|
||||
})JSON";
|
||||
EXPECT_EQ(json.repr(), expected);
|
||||
}
|
||||
|
||||
TEST(FormattedJsonTest, StylePreservation) {
|
||||
FormattedJson json = FormattedJson::parse(R"JSON({
|
||||
|
||||
"hello" : 1234
|
||||
|
||||
})JSON");
|
||||
json = json.append("world", Json{5678});
|
||||
char const* expected = R"JSON({
|
||||
|
||||
"hello" : 1234,
|
||||
|
||||
"world" : 5678
|
||||
|
||||
})JSON";
|
||||
EXPECT_EQ(json.repr(), expected);
|
||||
}
|
||||
|
||||
TEST(FormattedJsonTest, Queries) {
|
||||
FormattedJson json0 = FormattedJson::parse("[]");
|
||||
FormattedJson json1 = FormattedJson::parse("{\"a\":1}");
|
||||
FormattedJson json2 = FormattedJson::parse("[1,2]");
|
||||
FormattedJson json3 = FormattedJson::parse(R"({"a":1,"b":2,"c":3})");
|
||||
|
||||
EXPECT_EQ(json0.size(), 0u);
|
||||
EXPECT_EQ(json1.size(), 1u);
|
||||
EXPECT_EQ(json2.size(), 2u);
|
||||
EXPECT_EQ(json3.size(), 3u);
|
||||
|
||||
EXPECT_TRUE(json1.contains("a"));
|
||||
EXPECT_FALSE(json1.contains("b"));
|
||||
EXPECT_TRUE(json3.contains("c"));
|
||||
EXPECT_TRUE(json3.contains("b"));
|
||||
|
||||
ASSERT_THROW(FormattedJson::parse("123").size(), JsonException);
|
||||
ASSERT_THROW(json2.contains("1"), JsonException);
|
||||
}
|
||||
|
||||
TEST(FormattedJsonTest, Types) {
|
||||
FormattedJson jsonNull = Json{};
|
||||
FormattedJson jsonBool = Json{true};
|
||||
FormattedJson jsonInt = Json{1};
|
||||
FormattedJson jsonFloat = Json{2.0};
|
||||
FormattedJson jsonString = Json{"foo"};
|
||||
FormattedJson jsonArray = Json::ofType(Json::Type::Array);
|
||||
FormattedJson jsonObject = Json::ofType(Json::Type::Object);
|
||||
|
||||
EXPECT_EQ(jsonNull.type(), Json::Type::Null);
|
||||
EXPECT_TRUE(jsonNull.isType(Json::Type::Null));
|
||||
EXPECT_EQ(jsonNull.typeName(), "null");
|
||||
|
||||
EXPECT_EQ(jsonBool.type(), Json::Type::Bool);
|
||||
EXPECT_TRUE(jsonBool.isType(Json::Type::Bool));
|
||||
EXPECT_EQ(jsonBool.typeName(), "bool");
|
||||
|
||||
EXPECT_EQ(jsonInt.type(), Json::Type::Int);
|
||||
EXPECT_TRUE(jsonInt.isType(Json::Type::Int));
|
||||
EXPECT_EQ(jsonInt.typeName(), "int");
|
||||
|
||||
EXPECT_EQ(jsonFloat.type(), Json::Type::Float);
|
||||
EXPECT_TRUE(jsonFloat.isType(Json::Type::Float));
|
||||
EXPECT_EQ(jsonFloat.typeName(), "float");
|
||||
|
||||
EXPECT_EQ(jsonString.type(), Json::Type::String);
|
||||
EXPECT_TRUE(jsonString.isType(Json::Type::String));
|
||||
EXPECT_EQ(jsonString.typeName(), "string");
|
||||
|
||||
EXPECT_EQ(jsonArray.type(), Json::Type::Array);
|
||||
EXPECT_TRUE(jsonArray.isType(Json::Type::Array));
|
||||
EXPECT_EQ(jsonArray.typeName(), "array");
|
||||
|
||||
EXPECT_EQ(jsonObject.type(), Json::Type::Object);
|
||||
EXPECT_TRUE(jsonObject.isType(Json::Type::Object));
|
||||
EXPECT_EQ(jsonObject.typeName(), "object");
|
||||
}
|
||||
|
||||
TEST(FormattedJsonTest, JsonPath) {
|
||||
FormattedJson json = FormattedJson::parse(R"JSON({
|
||||
"foo": {
|
||||
"bar": [
|
||||
12,
|
||||
{
|
||||
"hello": "world"
|
||||
},
|
||||
45
|
||||
]
|
||||
},
|
||||
"baz": [{"a":1}, {"a":2}, {"a":3}]
|
||||
})JSON");
|
||||
|
||||
FormattedJson expectedHelloWorld = Json{JsonObject{{"hello", "world"}}};
|
||||
EXPECT_EQ(JsonPath::Pointer("/foo/bar/1").get(json), expectedHelloWorld);
|
||||
EXPECT_EQ(JsonPath::QueryPath("foo.bar[1]").get(json), expectedHelloWorld);
|
||||
EXPECT_EQ(JsonPath::Pointer("/baz/0/a").get(json), FormattedJson{Json{1}});
|
||||
EXPECT_EQ(JsonPath::QueryPath("baz[0].a").get(json), FormattedJson{Json{1}});
|
||||
|
||||
json = JsonPath::Pointer("/baz/0/a").set(json, FormattedJson{Json{0}});
|
||||
json = JsonPath::QueryPath("baz[1].a").set(json, FormattedJson{Json{4}});
|
||||
|
||||
json = JsonPath::Pointer("/baz/1").add(json, FormattedJson::parse("{\"b\":1}"));
|
||||
json = JsonPath::QueryPath("baz[1]").add(json, FormattedJson::parse("{\"c\":0.5}"));
|
||||
|
||||
EXPECT_EQ(json.get("baz").repr(),
|
||||
R"([{"a":0},{"c":0.5},{"b":1}, {"a":4}, {"a":3}])");
|
||||
|
||||
json = JsonPath::Pointer("/plz").set(json, FormattedJson{JsonArray{}});
|
||||
json = JsonPath::Pointer("/plz/-").set(json, FormattedJson{Json{"thx"}});
|
||||
json = JsonPath::Pointer("/plz/-").add(json, FormattedJson{Json{"bye"}});
|
||||
FormattedJson expectedThxBye = Json{JsonArray{"thx", "bye"}};
|
||||
EXPECT_EQ(json.get("plz"), expectedThxBye);
|
||||
|
||||
// Set and add are almost the same, but:
|
||||
// Set 0 => replaces the first array element
|
||||
json = JsonPath::Pointer("/plz/0").set(json, FormattedJson{Json{"kthx"}});
|
||||
// Add 0 => inserts a new element at the beginning
|
||||
json = JsonPath::Pointer("/plz/0").add(json, FormattedJson{Json{"kbye"}});
|
||||
FormattedJson expectedKByeKThxBye = Json{JsonArray{"kbye", "kthx", "bye"}};
|
||||
EXPECT_EQ(json.get("plz"), expectedKByeKThxBye);
|
||||
|
||||
json = JsonPath::Pointer("/foo/bar/1").remove(json);
|
||||
FormattedJson expectedBar = Json{JsonArray{12, 45}};
|
||||
EXPECT_EQ(JsonPath::Pointer("/foo/bar").get(json), expectedBar);
|
||||
json = JsonPath::QueryPath("foo.bar[1]").remove(json);
|
||||
EXPECT_EQ(JsonPath::QueryPath("foo.bar").get(json).toJson(), JsonArray{12});
|
||||
}
|
||||
|
||||
TEST(FormattedJsonTest, NumberFormatPreservation) {
|
||||
EXPECT_EQ(FormattedJson::parse("1.0").repr(), "1.0");
|
||||
EXPECT_EQ(FormattedJson::parse("1").repr(), "1");
|
||||
EXPECT_EQ(FormattedJson::parse("-0").repr(), "-0");
|
||||
EXPECT_EQ(FormattedJson::parse("0").repr(), "0");
|
||||
|
||||
EXPECT_EQ(FormattedJson::parseJson("[-0.0,1.0,-0]").repr(), "[-0.0,1.0,-0]");
|
||||
EXPECT_EQ(FormattedJson::parseJson("[1,0]").repr(), "[1,0]");
|
||||
}
|
57
source/test/function_test.cpp
Normal file
57
source/test/function_test.cpp
Normal file
|
@ -0,0 +1,57 @@
|
|||
#include "StarStoredFunctions.hpp"
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
using namespace Star;
|
||||
|
||||
TEST(StoredFunctionTest, All) {
|
||||
List<pair<double, double>> values = {{0.0f, 0.0f}, {1.0f, 1.0f}, {2.0f, 4.0f}, {3.0f, 9.0f}, {4.0f, 16.0f}};
|
||||
ParametricFunction<double, double> function(values, InterpolationMode::Linear, BoundMode::Clamp);
|
||||
StoredFunction levelingFunction(function);
|
||||
|
||||
EXPECT_LT(fabs(function.interpolate(2.5f) - 6.5f), 0.001f);
|
||||
|
||||
auto result = levelingFunction.search(16.0f);
|
||||
EXPECT_TRUE(result.found);
|
||||
EXPECT_LT(fabs(result.value - 16.0f), 0.001f);
|
||||
EXPECT_LT(fabs(result.solution - 4.0f), 0.001f);
|
||||
|
||||
result = levelingFunction.search(0.0f);
|
||||
EXPECT_TRUE(result.found);
|
||||
EXPECT_LT(fabs(result.value - 0.0f), 0.001f);
|
||||
EXPECT_LT(fabs(result.solution - 0.0f), 0.001f);
|
||||
|
||||
result = levelingFunction.search(6.5f);
|
||||
EXPECT_TRUE(result.found);
|
||||
EXPECT_LT(fabs(result.value - 6.5f), 0.001f);
|
||||
EXPECT_LT(fabs(result.solution - 2.5f), 0.001f);
|
||||
|
||||
List<pair<double, double>> swordsValues = {{0.0f, 0.0f}, {1.0f, 10.0f}, {100.0f, 500.0f}, {9999.0f, 9999999.0f}};
|
||||
ParametricFunction<double, double> swordsFunction(swordsValues, InterpolationMode::Linear, BoundMode::Clamp);
|
||||
StoredFunction swordsLevelingFunction(swordsFunction);
|
||||
|
||||
result = swordsLevelingFunction.search(0.0f);
|
||||
EXPECT_TRUE(result.found);
|
||||
EXPECT_LT(fabs(result.value - 0.0f), 0.001f);
|
||||
EXPECT_LT(fabs(result.solution - 0.0f), 0.001f);
|
||||
|
||||
result = swordsLevelingFunction.search(10.0f);
|
||||
EXPECT_TRUE(result.found);
|
||||
EXPECT_LT(fabs(result.value - 10.0f), 0.001f);
|
||||
EXPECT_LT(fabs(result.solution - 1.0f), 0.001f);
|
||||
|
||||
result = swordsLevelingFunction.search(500.0f);
|
||||
EXPECT_TRUE(result.found);
|
||||
EXPECT_LT(fabs(result.value - 500.0f), 0.001f);
|
||||
EXPECT_LT(fabs(result.solution - 100.0f), 0.001f);
|
||||
|
||||
result = swordsLevelingFunction.search(501.0f);
|
||||
EXPECT_TRUE(result.found);
|
||||
EXPECT_LT(fabs(result.value - 501.0f), 0.001f);
|
||||
EXPECT_LT(fabs(result.solution - 100.0f), 0.001f);
|
||||
|
||||
result = swordsLevelingFunction.search(500.01f);
|
||||
EXPECT_TRUE(result.found);
|
||||
EXPECT_LT(fabs(result.value - 500.01f), 0.001f);
|
||||
EXPECT_LT(fabs(result.solution - 100.0f), 0.001f);
|
||||
}
|
43
source/test/game_tests_main.cpp
Normal file
43
source/test/game_tests_main.cpp
Normal file
|
@ -0,0 +1,43 @@
|
|||
#include "StarLogging.hpp"
|
||||
#include "StarFile.hpp"
|
||||
#include "StarRootLoader.hpp"
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
using namespace Star;
|
||||
|
||||
struct ErrorLogSink : public LogSink {
|
||||
ErrorLogSink() {
|
||||
setLevel(LogLevel::Error);
|
||||
}
|
||||
|
||||
void log(char const* msg, LogLevel) override {
|
||||
ADD_FAILURE() << "Error was logged: " << msg;
|
||||
}
|
||||
};
|
||||
|
||||
class TestEnvironment : public testing::Environment {
|
||||
public:
|
||||
unique_ptr<Root> root;
|
||||
Root::Settings settings;
|
||||
|
||||
TestEnvironment(Root::Settings settings)
|
||||
: settings(move(settings)) {}
|
||||
|
||||
virtual void SetUp() {
|
||||
Logger::addSink(make_shared<ErrorLogSink>());
|
||||
root = make_unique<Root>(settings);
|
||||
root->configuration()->set("clearUniverseFiles", true);
|
||||
root->configuration()->set("clearPlayerFiles", true);
|
||||
}
|
||||
|
||||
virtual void TearDown() {
|
||||
root.reset();
|
||||
}
|
||||
};
|
||||
|
||||
GTEST_API_ int main(int argc, char** argv) {
|
||||
testing::InitGoogleTest(&argc, argv);
|
||||
testing::AddGlobalTestEnvironment(new TestEnvironment(RootLoader({{}, {}, {}, LogLevel::Error, true, {}}).commandParseOrDie(argc, argv).first));
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
9592
source/test/gtest/gtest-all.cc
Normal file
9592
source/test/gtest/gtest-all.cc
Normal file
File diff suppressed because it is too large
Load diff
20061
source/test/gtest/gtest.h
Normal file
20061
source/test/gtest/gtest.h
Normal file
File diff suppressed because it is too large
Load diff
15
source/test/hash_test.cpp
Normal file
15
source/test/hash_test.cpp
Normal file
|
@ -0,0 +1,15 @@
|
|||
#include "StarHash.hpp"
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
TEST(HashTest, All) {
|
||||
enum SomeEnum { Foo, Bar };
|
||||
|
||||
std::tuple<int, int, bool> testTuple(1, 2, false);
|
||||
std::pair<SomeEnum, int> testPair(SomeEnum::Bar, 10);
|
||||
|
||||
// Yeah yeah, I know that it's technically possible for the hash to be zero,
|
||||
// but it's not!
|
||||
EXPECT_NE(Star::hash<decltype(testTuple)>()(testTuple), 0u);
|
||||
EXPECT_NE(Star::hash<decltype(testPair)>()(testPair), 0u);
|
||||
}
|
24
source/test/host_address_test.cpp
Normal file
24
source/test/host_address_test.cpp
Normal file
|
@ -0,0 +1,24 @@
|
|||
#include "StarHostAddress.hpp"
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
using namespace Star;
|
||||
|
||||
TEST(HostAddress, All) {
|
||||
EXPECT_TRUE(HostAddress::localhost(NetworkMode::IPv4).isLocalHost());
|
||||
EXPECT_TRUE(HostAddress::localhost(NetworkMode::IPv6).isLocalHost());
|
||||
EXPECT_TRUE(HostAddress("*").isZero());
|
||||
EXPECT_TRUE(HostAddress("::").isZero());
|
||||
EXPECT_TRUE(HostAddress("127.0.0.1").isLocalHost());
|
||||
EXPECT_TRUE(HostAddress("::1").isLocalHost());
|
||||
EXPECT_EQ(HostAddress("*").mode(), NetworkMode::IPv4);
|
||||
EXPECT_EQ(HostAddress("::").mode(), NetworkMode::IPv6);
|
||||
}
|
||||
|
||||
TEST(HostAddressWithPort, All) {
|
||||
EXPECT_EQ(HostAddressWithPort("*:80").port(), 80);
|
||||
EXPECT_EQ(HostAddressWithPort(":::80").port(), 80);
|
||||
EXPECT_EQ(HostAddressWithPort("[::]:80").port(), 80);
|
||||
EXPECT_TRUE(HostAddressWithPort("[::]:80").address().isZero());
|
||||
EXPECT_TRUE(HostAddressWithPort("[::1]:80").address().isLocalHost());
|
||||
}
|
119
source/test/item_test.cpp
Normal file
119
source/test/item_test.cpp
Normal file
|
@ -0,0 +1,119 @@
|
|||
#include "StarItemDatabase.hpp"
|
||||
|
||||
#include <list>
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
using namespace Star;
|
||||
|
||||
TEST(ItemTest, ItemDescriptorConstruction) {
|
||||
ItemDescriptor testItemDescriptor;
|
||||
|
||||
testItemDescriptor = ItemDescriptor();
|
||||
|
||||
testItemDescriptor = ItemDescriptor(Json());
|
||||
|
||||
String nameOnly = "perfectlygenericitem";
|
||||
testItemDescriptor = ItemDescriptor(nameOnly);
|
||||
|
||||
List<JsonArray> arrayFormats = List<JsonArray>{
|
||||
JsonArray{"perfectlygenericitem"},
|
||||
JsonArray{"perfectlygenericitem", 1},
|
||||
JsonArray{"perfectlygenericitem", 1, JsonObject()},
|
||||
JsonArray{"perfectlygenericitem", 1, JsonObject{{"testParameter", "testValue"}}}
|
||||
};
|
||||
|
||||
for (auto arrayFormat : arrayFormats)
|
||||
testItemDescriptor = ItemDescriptor(arrayFormat);
|
||||
|
||||
List<JsonObject> objectFormats = List<JsonObject>{
|
||||
JsonObject{{"name", "perfectlygenericitem"}},
|
||||
JsonObject{{"item", "perfectlygenericitem"}},
|
||||
JsonObject{{"name", "perfectlygenericitem"}, {"count", 1}},
|
||||
JsonObject{{"name", "perfectlygenericitem"}, {"count", 1}, {"parameters", JsonObject()}},
|
||||
JsonObject{{"name", "perfectlygenericitem"}, {"count", 1}, {"parameters", JsonObject{{"testParameter", "testValue"}}}}
|
||||
};
|
||||
|
||||
for (auto objectFormat : objectFormats)
|
||||
testItemDescriptor = ItemDescriptor(objectFormat);
|
||||
|
||||
testItemDescriptor = ItemDescriptor("perfectlygenericitem", 1);
|
||||
testItemDescriptor = ItemDescriptor("perfectlygenericitem", 1, JsonObject{{"testParameter", "testValue"}});
|
||||
}
|
||||
|
||||
TEST(ItemTest, ItemComparison) {
|
||||
auto itemDatabase = Root::singleton().itemDatabase();
|
||||
ItemPtr testItem = itemDatabase->item(ItemDescriptor("perfectlygenericitem", 1));
|
||||
ItemPtr testItemParams = itemDatabase->item(ItemDescriptor("perfectlygenericitem", 1, JsonObject{{"testParameter", "testValue"}}));
|
||||
|
||||
List<ItemDescriptor> testItemDescriptors = List<ItemDescriptor>{
|
||||
ItemDescriptor("perfectlygenericitem", 1),
|
||||
ItemDescriptor(JsonArray{"perfectlygenericitem"}),
|
||||
ItemDescriptor(JsonArray{"perfectlygenericitem", 1}),
|
||||
ItemDescriptor(JsonArray{"perfectlygenericitem", 1, JsonObject()}),
|
||||
ItemDescriptor(JsonObject{{"name", "perfectlygenericitem"}}),
|
||||
ItemDescriptor(JsonObject{{"item", "perfectlygenericitem"}}),
|
||||
ItemDescriptor(JsonObject{{"name", "perfectlygenericitem"}, {"count", 1}}),
|
||||
ItemDescriptor(JsonObject{{"name", "perfectlygenericitem"}, {"count", 1}, {"parameters", JsonObject()}})
|
||||
};
|
||||
|
||||
List<ItemDescriptor> testItemDescriptorsParams = List<ItemDescriptor>{
|
||||
ItemDescriptor(JsonArray{"perfectlygenericitem", 1, JsonObject{{"testParameter", "testValue"}}}),
|
||||
ItemDescriptor(JsonObject{{"name", "perfectlygenericitem"}, {"count", 1}, {"parameters", JsonObject{{"testParameter", "testValue"}}}})
|
||||
};
|
||||
|
||||
// comparisons WITHOUT exactMatch
|
||||
for (ItemDescriptor const& id : testItemDescriptors) {
|
||||
EXPECT_TRUE(testItem->matches(id));
|
||||
EXPECT_TRUE(testItemParams->matches(id));
|
||||
EXPECT_TRUE(id.matches(testItem));
|
||||
EXPECT_TRUE(id.matches(testItemParams));
|
||||
for (ItemDescriptor const& id2 : testItemDescriptors)
|
||||
EXPECT_TRUE(id.matches(id2));
|
||||
for (ItemDescriptor const& id2 : testItemDescriptorsParams)
|
||||
EXPECT_TRUE(id.matches(id2));
|
||||
}
|
||||
for (ItemDescriptor const& id : testItemDescriptorsParams) {
|
||||
EXPECT_TRUE(testItem->matches(id));
|
||||
EXPECT_TRUE(testItemParams->matches(id));
|
||||
EXPECT_TRUE(id.matches(testItem));
|
||||
EXPECT_TRUE(id.matches(testItemParams));
|
||||
for (ItemDescriptor const& id2 : testItemDescriptors)
|
||||
EXPECT_TRUE(id.matches(id2));
|
||||
for (ItemDescriptor const& id2 : testItemDescriptorsParams)
|
||||
EXPECT_TRUE(id.matches(id2));
|
||||
}
|
||||
EXPECT_TRUE(testItem->matches(testItemParams));
|
||||
EXPECT_TRUE(testItemParams->matches(testItem));
|
||||
|
||||
// comparisons WITH exactMatch
|
||||
for (ItemDescriptor const& id : testItemDescriptors) {
|
||||
EXPECT_TRUE(testItem->matches(id, true));
|
||||
EXPECT_FALSE(testItemParams->matches(id, true));
|
||||
EXPECT_TRUE(id.matches(testItem, true));
|
||||
EXPECT_FALSE(id.matches(testItemParams, true));
|
||||
for (ItemDescriptor const& id2 : testItemDescriptors)
|
||||
EXPECT_TRUE(id.matches(id2, true));
|
||||
for (ItemDescriptor const& id2 : testItemDescriptorsParams)
|
||||
EXPECT_FALSE(id.matches(id2, true));
|
||||
}
|
||||
for (ItemDescriptor const& id : testItemDescriptorsParams) {
|
||||
EXPECT_FALSE(testItem->matches(id, true));
|
||||
EXPECT_TRUE(testItemParams->matches(id, true));
|
||||
EXPECT_FALSE(id.matches(testItem, true));
|
||||
EXPECT_TRUE(id.matches(testItemParams, true));
|
||||
for (ItemDescriptor const& id2 : testItemDescriptors)
|
||||
EXPECT_FALSE(id.matches(id2, true));
|
||||
for (ItemDescriptor const& id2 : testItemDescriptorsParams)
|
||||
EXPECT_TRUE(id.matches(id2, true));
|
||||
}
|
||||
EXPECT_FALSE(testItem->matches(testItemParams, true));
|
||||
EXPECT_FALSE(testItemParams->matches(testItem, true));
|
||||
}
|
||||
|
||||
TEST(ItemTest, ConstructItems) {
|
||||
auto itemDatabase = Root::singleton().itemDatabase();
|
||||
|
||||
for (auto itemName : itemDatabase->allItems())
|
||||
ItemPtr item = itemDatabase->item(ItemDescriptor(itemName, 1));
|
||||
}
|
703
source/test/json_test.cpp
Normal file
703
source/test/json_test.cpp
Normal file
|
@ -0,0 +1,703 @@
|
|||
#include "StarJson.hpp"
|
||||
#include "StarFile.hpp"
|
||||
#include "StarJsonPatch.hpp"
|
||||
#include "StarJsonPath.hpp"
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
using namespace Star;
|
||||
|
||||
TEST(JsonTest, ImplicitSharing) {
|
||||
Json map1 = JsonObject{{"foo", 1}, {"bar", 10}};
|
||||
|
||||
Json map2 = JsonObject{{"foo", 5}, {"bar", 50}};
|
||||
|
||||
std::swap(map1, map2);
|
||||
Json map3 = map1;
|
||||
map1 = map2;
|
||||
map2 = map3;
|
||||
|
||||
EXPECT_EQ(map1.get("foo"), 1);
|
||||
EXPECT_EQ(map2.get("bar"), 50);
|
||||
}
|
||||
|
||||
TEST(JsonTest, Defaults) {
|
||||
Json obj = JsonObject{{"null", Json()}};
|
||||
Json arr = JsonArray{"array", JsonArray{Json(), Json()}};
|
||||
|
||||
EXPECT_EQ(obj.getInt("null", 5), 5);
|
||||
EXPECT_EQ(arr.getInt(2, 5), 5);
|
||||
EXPECT_EQ(arr.getInt(3, 5), 5);
|
||||
EXPECT_THROW(arr.getInt(2), JsonException);
|
||||
}
|
||||
|
||||
TEST(JsonTest, Merging) {
|
||||
JsonObject a{{"I", "feel"}, {"friendly", "now"}};
|
||||
JsonObject b{{"hello", "there"}, {"leg", "friend"}};
|
||||
JsonObject c{{"hello", "you"}, {"leg", "fiend"}};
|
||||
JsonObject d{{"goodbye", "you"}, {"friendly", "leg"}};
|
||||
|
||||
Json merged = jsonMerge(a, b, c, d);
|
||||
|
||||
EXPECT_EQ(merged.get("I"), "feel");
|
||||
EXPECT_EQ(merged.get("hello"), "you");
|
||||
EXPECT_EQ(merged.get("friendly"), "leg");
|
||||
EXPECT_EQ(merged.get("leg"), "fiend");
|
||||
|
||||
Json e = JsonObject();
|
||||
e = e.set("1", 2);
|
||||
e = e.setAll({{"a", "b"}, {"c", "d"}});
|
||||
Json f = JsonObject{{"1", 2}, {"a", "b"}, {"c", "d"}};
|
||||
|
||||
EXPECT_EQ(e, f);
|
||||
|
||||
Json g = JsonObject{{"a", "a"}, {"sub", JsonObject()}};
|
||||
g = g.setPath("sub.field", 1);
|
||||
g = g.setPath("sub.field2", 2);
|
||||
g = g.erasePath("sub.field2");
|
||||
Json h = JsonObject{{"a", "a"}, {"sub", JsonObject{{"field", 1}}}};
|
||||
EXPECT_EQ(g, h);
|
||||
}
|
||||
|
||||
TEST(JsonTest, Unicode) {
|
||||
Json v = Json::parse("{ \"first\" : \"日本語\", \"second\" : \"foobar\\u0019\" }");
|
||||
EXPECT_EQ(v.getString("first"), String("日本語"));
|
||||
EXPECT_EQ(v.get("second").repr(), String("\"foobar\\u0019\""));
|
||||
|
||||
String json = v.printJson();
|
||||
Json v2 = Json::parseJson(json);
|
||||
EXPECT_EQ(v2.getString("first"), String("日本語"));
|
||||
|
||||
EXPECT_EQ(v, v2);
|
||||
|
||||
EXPECT_EQ(Json("😀"), Json::parse("\"\\ud83d\\ude00\""));
|
||||
EXPECT_EQ(Json::parse("\"\\ud83d\\ude00\"").toString().size(), 1u);
|
||||
}
|
||||
|
||||
TEST(JsonTest, UnicodeFile) {
|
||||
Json v = Json::parse("{ \"first\" : \"日本語\", \"second\" : \"foobar\\u0019\" }");
|
||||
EXPECT_EQ(v.getString("first"), String("日本語"));
|
||||
EXPECT_EQ(v.get("second").repr(), String("\"foobar\\u0019\""));
|
||||
|
||||
String file = File::temporaryFileName();
|
||||
auto finallyGuard = finally([&file]() { File::remove(file); });
|
||||
|
||||
File::writeFile(v.printJson(), file);
|
||||
Json v2 = Json::parseJson(File::readFileString(file));
|
||||
EXPECT_EQ(v2.getString("first"), "日本語");
|
||||
|
||||
EXPECT_EQ(v, v2);
|
||||
}
|
||||
|
||||
TEST(JsonTest, JsonParsingEdge) {
|
||||
auto isValidFragment = [](String const& json) -> bool {
|
||||
try {
|
||||
Json::parse(json);
|
||||
return true;
|
||||
} catch (JsonParsingException const&) {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
auto isValidJson = [](String const& json) -> bool {
|
||||
try {
|
||||
Json::parseJson(json);
|
||||
return true;
|
||||
} catch (JsonParsingException const&) {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
EXPECT_TRUE(isValidFragment(" \t 0.0 "));
|
||||
EXPECT_TRUE(isValidFragment("-0.0\t "));
|
||||
EXPECT_FALSE(isValidFragment("-.0"));
|
||||
EXPECT_FALSE(isValidFragment("00.0"));
|
||||
|
||||
EXPECT_FALSE(isValidJson(" 0.0"));
|
||||
EXPECT_FALSE(isValidJson("true"));
|
||||
EXPECT_TRUE(isValidJson("\t[]"));
|
||||
EXPECT_TRUE(isValidJson(" {} "));
|
||||
}
|
||||
|
||||
TEST(JsonTest, Types) {
|
||||
Json v;
|
||||
EXPECT_EQ(v.type(), Json::Type::Null);
|
||||
v = 0;
|
||||
EXPECT_EQ(v.type(), Json::Type::Int);
|
||||
v = 0.0;
|
||||
EXPECT_EQ(v.type(), Json::Type::Float);
|
||||
v = true;
|
||||
EXPECT_EQ(v.type(), Json::Type::Bool);
|
||||
v = "";
|
||||
EXPECT_EQ(v.type(), Json::Type::String);
|
||||
v = JsonArray();
|
||||
EXPECT_EQ(v.type(), Json::Type::Array);
|
||||
v = JsonObject();
|
||||
EXPECT_EQ(v.type(), Json::Type::Object);
|
||||
}
|
||||
|
||||
TEST(JsonTest, Query) {
|
||||
Json v = Json::parse(R"JSON(
|
||||
{
|
||||
"foo" : "bar",
|
||||
"baz" : {
|
||||
"baf" : [1, 2],
|
||||
"bal" : 2
|
||||
},
|
||||
|
||||
"baf" : null
|
||||
}
|
||||
)JSON");
|
||||
|
||||
EXPECT_EQ(v.query("foo"), Json("bar"));
|
||||
EXPECT_EQ(v.query("baz.baf[1]"), Json(2));
|
||||
EXPECT_EQ(v.query("baz.bal"), Json(2));
|
||||
EXPECT_EQ(v.query("blargh", Json("default")), Json("default"));
|
||||
EXPECT_EQ(v.query("blargh", Json("default")), Json("default"));
|
||||
EXPECT_EQ(v.query("baz.baf[3]", Json("default")), Json("default"));
|
||||
EXPECT_EQ(v.query("baz.bal[0]", Json("default")), Json("default"));
|
||||
EXPECT_EQ(v.query("baz[1]", Json("default")), Json("default"));
|
||||
EXPECT_EQ(v.query("baz.bal.a", Json("default")), Json("default"));
|
||||
EXPECT_EQ(v.query("baz[0]", Json("default")), Json("default"));
|
||||
EXPECT_EQ(v.query("baz.baf.a", Json("default")), Json("default"));
|
||||
EXPECT_THROW(v.query("blargh"), JsonPath::TraversalException);
|
||||
EXPECT_THROW(v.query("baz.funk"), JsonPath::TraversalException);
|
||||
EXPECT_THROW(v.query("baz.baf[3]"), JsonPath::TraversalException);
|
||||
EXPECT_THROW(v.query("baz.baf[whee]", Json()), JsonPath::ParsingException);
|
||||
EXPECT_THROW(v.query("baz.baf[[]", Json()), JsonPath::ParsingException);
|
||||
EXPECT_THROW(v.query("baz..baf", Json()), JsonPath::ParsingException);
|
||||
EXPECT_THROW(v.query("baf.nothing"), JsonException);
|
||||
}
|
||||
|
||||
TEST(JsonTest, PatchingAdd) {
|
||||
Json before = Json::parse(R"JSON(
|
||||
{
|
||||
"foo" : "bar",
|
||||
"baz" : {
|
||||
"baf" : 1,
|
||||
"bal" : 2
|
||||
},
|
||||
"rab" : [0, 1, 2, "foo", false]
|
||||
}
|
||||
)JSON");
|
||||
|
||||
Json after = Json::parse(R"JSON(
|
||||
{
|
||||
"foo" : "xyzzy",
|
||||
"bar" : "foo",
|
||||
"baz" : {
|
||||
"baf" : 1,
|
||||
"bal" : 2,
|
||||
"0" : "derp",
|
||||
"rebar" : {
|
||||
"after" : "party"
|
||||
}
|
||||
},
|
||||
"rab" : [0, 0, 1, 2, "foo", false, true, { "baz" : "bar"} ]
|
||||
}
|
||||
)JSON");
|
||||
|
||||
Json patch = Json::parse(R"JSON(
|
||||
[
|
||||
{"op" : "add", "path" : "/foo", "value" : "xyzzy"},
|
||||
{"op" : "add", "path" : "/bar", "value" : "foo"},
|
||||
{"op" : "add", "path" : "/baz/rebar", "value" : {}},
|
||||
{"op" : "add", "path" : "/baz/rebar/after", "value" : "party"},
|
||||
{"op" : "add", "path" : "/baz/0", "value" : "derp"},
|
||||
{"op" : "add", "path" : "/rab/0", "value" : 0},
|
||||
{"op" : "add", "path" : "/rab/6", "value" : true},
|
||||
{"op" : "add", "path" : "/rab/-", "value" : {"baz" : "bar"} }
|
||||
]
|
||||
)JSON");
|
||||
|
||||
// Past end of list
|
||||
Json badPatch1 = Json::parse(R"JSON(
|
||||
[
|
||||
{"op" : "add", "path" : "/rab/6", "value" : {"baz" : "bar"} }
|
||||
]
|
||||
)JSON");
|
||||
|
||||
// Parent does not exist, map
|
||||
Json badPatch2 = Json::parse(R"JSON(
|
||||
[
|
||||
{"op" : "add", "path" : "/bar/baz", "value" : {"baz" : "bar"} }
|
||||
]
|
||||
)JSON");
|
||||
|
||||
// Parent does not exist, list
|
||||
Json badPatch3 = Json::parse(R"JSON(
|
||||
[
|
||||
{"op" : "add", "path" : "/bar/0", "value" : {"baz" : "bar"} }
|
||||
]
|
||||
)JSON");
|
||||
|
||||
EXPECT_EQ(jsonPatch(before, patch.toArray()), after);
|
||||
ASSERT_THROW(jsonPatch(before, badPatch1.toArray()), JsonPatchException);
|
||||
ASSERT_THROW(jsonPatch(before, badPatch2.toArray()), JsonPatchException);
|
||||
ASSERT_THROW(jsonPatch(before, badPatch3.toArray()), JsonPatchException);
|
||||
}
|
||||
|
||||
TEST(JsonTest, PatchingRemove) {
|
||||
Json before = Json::parse(R"JSON(
|
||||
{
|
||||
"foo" : "xyzzy",
|
||||
"bar" : "foo",
|
||||
"baz" : {
|
||||
"baf" : 1,
|
||||
"bal" : 2,
|
||||
"rebar" : true
|
||||
},
|
||||
"rab" : [0, 0, 1, 2, "foo", false, {"baz" : "bar"} ]
|
||||
}
|
||||
)JSON");
|
||||
|
||||
Json after = Json::parse(R"JSON(
|
||||
{
|
||||
"bar" : "foo",
|
||||
"baz" : {
|
||||
"baf" : 1,
|
||||
"bal" : 2
|
||||
},
|
||||
"rab" : [0, 1, 2, "foo", false]
|
||||
}
|
||||
)JSON");
|
||||
|
||||
Json patch = Json::parse(R"JSON(
|
||||
[
|
||||
{"op" : "remove", "path" : "/foo"},
|
||||
{"op" : "remove", "path" : "/baz/rebar"},
|
||||
{"op" : "remove", "path" : "/rab/0"},
|
||||
{"op" : "remove", "path" : "/rab/5"}
|
||||
]
|
||||
)JSON");
|
||||
|
||||
// Removing end of list
|
||||
Json badPatch1 = Json::parse(R"JSON(
|
||||
[
|
||||
{"op" : "remove", "path" : "/rab/-"}
|
||||
]
|
||||
)JSON");
|
||||
|
||||
// Removing past end of list
|
||||
Json badPatch2 = Json::parse(R"JSON(
|
||||
[
|
||||
{"op" : "add", "path" : "/rab/7"}
|
||||
]
|
||||
)JSON");
|
||||
|
||||
// Path wrong type
|
||||
Json badPatch3 = Json::parse(R"JSON(
|
||||
[
|
||||
{"op" : "remove", "path" : "/bar/baz"}
|
||||
]
|
||||
)JSON");
|
||||
|
||||
EXPECT_EQ(jsonPatch(before, patch.toArray()), after);
|
||||
ASSERT_THROW(jsonPatch(before, badPatch1.toArray()), JsonPatchException);
|
||||
ASSERT_THROW(jsonPatch(before, badPatch2.toArray()), JsonPatchException);
|
||||
ASSERT_THROW(jsonPatch(before, badPatch3.toArray()), JsonPatchException);
|
||||
}
|
||||
|
||||
TEST(JsonTest, PatchingReplace) {
|
||||
Json before = Json::parse(R"JSON(
|
||||
{
|
||||
"foo" : "bar",
|
||||
"bar" : {
|
||||
"baf" : 1,
|
||||
"bal" : 2
|
||||
},
|
||||
"baz" : {
|
||||
"baf" : 1,
|
||||
"bal" : 2
|
||||
},
|
||||
"rab" : [0, 1, 2, "foo", false],
|
||||
"rabby" : [0, 1, 2, "foo", false]
|
||||
}
|
||||
)JSON");
|
||||
|
||||
Json after = Json::parse(R"JSON(
|
||||
{
|
||||
"foo" : "xyzzy",
|
||||
"bar" : [3, 2, 1, "contact"],
|
||||
"baz" : {
|
||||
"baf" : 1,
|
||||
"bal" : "touched"
|
||||
},
|
||||
"rab" : [{"omg" : "no"}, 1, 2, "foo", false],
|
||||
"rabby" : false
|
||||
}
|
||||
)JSON");
|
||||
|
||||
Json patch = Json::parse(R"JSON(
|
||||
[
|
||||
{"op" : "replace", "path" : "/foo", "value" : "xyzzy"},
|
||||
{"op" : "replace", "path" : "/bar", "value" : [3, 2, 1, "contact"]},
|
||||
{"op" : "replace", "path" : "/baz/bal", "value" : "touched"},
|
||||
{"op" : "replace", "path" : "/rab/0", "value" : {"omg" : "yes"}},
|
||||
{"op" : "replace", "path" : "/rab/0/omg", "value" : "no"},
|
||||
{"op" : "replace", "path" : "/rab/2", "value" : 2},
|
||||
{"op" : "replace", "path" : "/rabby", "value" : false}
|
||||
]
|
||||
)JSON");
|
||||
|
||||
// End of list
|
||||
Json badPatch1 = Json::parse(R"JSON(
|
||||
[
|
||||
{"op" : "replace", "path" : "/rab/-", "value" : {"baz" : "bar"} }
|
||||
]
|
||||
)JSON");
|
||||
|
||||
// Past end of list
|
||||
Json badPatch2 = Json::parse(R"JSON(
|
||||
[
|
||||
{"op" : "replace", "path" : "/rab/5", "value" : {"baz" : "bar"} }
|
||||
]
|
||||
)JSON");
|
||||
|
||||
// Key does not exist
|
||||
Json badPatch3 = Json::parse(R"JSON(
|
||||
[
|
||||
{"op" : "replace", "path" : "/bar/baz", "value" : {"baz" : "bar"} }
|
||||
]
|
||||
)JSON");
|
||||
|
||||
EXPECT_EQ(jsonPatch(before, patch.toArray()), after);
|
||||
ASSERT_THROW(jsonPatch(before, badPatch1.toArray()), JsonPatchException);
|
||||
ASSERT_THROW(jsonPatch(before, badPatch2.toArray()), JsonPatchException);
|
||||
ASSERT_THROW(jsonPatch(before, badPatch3.toArray()), JsonPatchException);
|
||||
}
|
||||
|
||||
TEST(JsonTest, PatchingMove) {
|
||||
Json before = Json::parse(R"JSON(
|
||||
{
|
||||
"foo" : "bar",
|
||||
"bar" : [1, 2, 3, "contact"],
|
||||
"baz" : {
|
||||
"baf" : 1,
|
||||
"bar" : 2
|
||||
},
|
||||
"rab" : [0, 1, 2, "foo", false],
|
||||
"rabby" : [0, 1, 2, "foo", true]
|
||||
}
|
||||
)JSON");
|
||||
|
||||
Json after = Json::parse(R"JSON(
|
||||
{
|
||||
"foot" : "bar",
|
||||
"baz" : {
|
||||
"baf" : 1,
|
||||
"bar" : [3, 2, 1, "contact"]
|
||||
},
|
||||
"bar" : 2,
|
||||
"rab" : [0, 1, 2, true, "foo"]
|
||||
}
|
||||
)JSON");
|
||||
|
||||
Json patch = Json::parse(R"JSON(
|
||||
[
|
||||
{"op" : "move", "from" : "/foo", "path" : "/foot"},
|
||||
{"op" : "move", "from" : "/bar", "path" : "/baz/bal"},
|
||||
{"op" : "move", "from" : "/baz/bar", "path" : "/bar"},
|
||||
{"op" : "move", "from" : "/baz/bal", "path" : "/baz/bar"},
|
||||
{"op" : "move", "from" : "/baz/bar/0", "path" : "/baz/bar/1"},
|
||||
{"op" : "move", "from" : "/baz/bar/2", "path" : "/baz/bar/0"},
|
||||
{"op" : "move", "from" : "/rabby", "path" : "/rab"},
|
||||
{"op" : "move", "from" : "/rab/3", "path" : "/rab/-"}
|
||||
]
|
||||
)JSON");
|
||||
|
||||
// From end of list
|
||||
Json badPatch1 = Json::parse(R"JSON(
|
||||
[
|
||||
{"op" : "move", "from" : "/rab/-", "path" : "/doesnotmatter"}
|
||||
]
|
||||
)JSON");
|
||||
|
||||
// From past end of list
|
||||
Json badPatch2 = Json::parse(R"JSON(
|
||||
[
|
||||
{"op" : "move", "from" : "/rab/5", "path" : "/doesnotmatter"}
|
||||
]
|
||||
)JSON");
|
||||
|
||||
// To past end of list
|
||||
Json badPatch3 = Json::parse(R"JSON(
|
||||
[
|
||||
{"op" : "move", "from" : "/rab/0", "path" : "/rab/5"}
|
||||
]
|
||||
)JSON");
|
||||
|
||||
// Source path does not exist
|
||||
Json badPatch4 = Json::parse(R"JSON(
|
||||
[
|
||||
{"op" : "move", "from" : "/omgomg", "path" : "/doesntmatter"}
|
||||
]
|
||||
)JSON");
|
||||
|
||||
// Dest path wrong type
|
||||
Json badPatch5 = Json::parse(R"JSON(
|
||||
[
|
||||
{"op" : "move", "from" : "/baz/bar", "path" : "/rabby/bar"}
|
||||
]
|
||||
)JSON");
|
||||
|
||||
EXPECT_EQ(jsonPatch(before, patch.toArray()), after);
|
||||
ASSERT_THROW(jsonPatch(before, badPatch1.toArray()), JsonPatchException);
|
||||
ASSERT_THROW(jsonPatch(before, badPatch2.toArray()), JsonPatchException);
|
||||
ASSERT_THROW(jsonPatch(before, badPatch3.toArray()), JsonPatchException);
|
||||
ASSERT_THROW(jsonPatch(before, badPatch4.toArray()), JsonPatchException);
|
||||
ASSERT_THROW(jsonPatch(before, badPatch5.toArray()), JsonPatchException);
|
||||
}
|
||||
|
||||
TEST(JsonTest, PatchingCopy) {
|
||||
Json before = Json::parse(R"JSON(
|
||||
{
|
||||
"foo" : "bar",
|
||||
"foot" : "bar",
|
||||
"bar" : [1, 2, 3, "contact"],
|
||||
"baz" : {
|
||||
"baf" : 1,
|
||||
"bar" : 2
|
||||
},
|
||||
"rab" : [0, 1, 2, "foo", false],
|
||||
"rabby" : [0, 1, 2, "foo", true]
|
||||
}
|
||||
)JSON");
|
||||
|
||||
Json after = Json::parse(R"JSON(
|
||||
{
|
||||
"foo" : "bar",
|
||||
"foot" : "bar",
|
||||
"baz" : {
|
||||
"baf" : 1,
|
||||
"bar" : [2, 1, 1, 2, 3, "contact"],
|
||||
"bal" : [1, 2, 3, "contact"]
|
||||
},
|
||||
"bar" : 2,
|
||||
"rab" : [0, 1, 2, "foo", true, "foo"],
|
||||
"rabby" : [0, 1, 2, "foo", true]
|
||||
}
|
||||
)JSON");
|
||||
|
||||
Json patch = Json::parse(R"JSON(
|
||||
[
|
||||
{"op" : "copy", "from" : "/foo", "path" : "/foot"},
|
||||
{"op" : "copy", "from" : "/bar", "path" : "/baz/bal"},
|
||||
{"op" : "copy", "from" : "/baz/bar", "path" : "/bar"},
|
||||
{"op" : "copy", "from" : "/baz/bal", "path" : "/baz/bar"},
|
||||
{"op" : "copy", "from" : "/baz/bar/0", "path" : "/baz/bar/1"},
|
||||
{"op" : "copy", "from" : "/baz/bar/2", "path" : "/baz/bar/0"},
|
||||
{"op" : "copy", "from" : "/rabby", "path" : "/rab"},
|
||||
{"op" : "copy", "from" : "/rab/3", "path" : "/rab/-"}
|
||||
]
|
||||
)JSON");
|
||||
|
||||
// From end of list
|
||||
Json badPatch1 = Json::parse(R"JSON(
|
||||
[
|
||||
{"op" : "copy", "from" : "/rab/-", "path" : "/doesnotmatter"}
|
||||
]
|
||||
)JSON");
|
||||
|
||||
// From past end of list
|
||||
Json badPatch2 = Json::parse(R"JSON(
|
||||
[
|
||||
{"op" : "copy", "from" : "/rab/5", "path" : "/doesnotmatter"}
|
||||
]
|
||||
)JSON");
|
||||
|
||||
// To past end of list
|
||||
Json badPatch3 = Json::parse(R"JSON(
|
||||
[
|
||||
{"op" : "copy", "from" : "/rab/0", "path" : "/rab/6"}
|
||||
]
|
||||
)JSON");
|
||||
|
||||
// Source path does not exist
|
||||
Json badPatch4 = Json::parse(R"JSON(
|
||||
[
|
||||
{"op" : "copy", "from" : "/omgomg", "path" : "/doesntmatter"}
|
||||
]
|
||||
)JSON");
|
||||
|
||||
// Dest path wrong type
|
||||
Json badPatch5 = Json::parse(R"JSON(
|
||||
[
|
||||
{"op" : "copy", "from" : "/baz/bar", "path" : "/rabby/bar"}
|
||||
]
|
||||
)JSON");
|
||||
|
||||
EXPECT_EQ(jsonPatch(before, patch.toArray()), after);
|
||||
ASSERT_THROW(jsonPatch(before, badPatch1.toArray()), JsonPatchException);
|
||||
ASSERT_THROW(jsonPatch(before, badPatch2.toArray()), JsonPatchException);
|
||||
ASSERT_THROW(jsonPatch(before, badPatch3.toArray()), JsonPatchException);
|
||||
ASSERT_THROW(jsonPatch(before, badPatch4.toArray()), JsonPatchException);
|
||||
ASSERT_THROW(jsonPatch(before, badPatch5.toArray()), JsonPatchException);
|
||||
}
|
||||
|
||||
TEST(JsonTest, PatchingTest) {
|
||||
Json base = Json::parse(R"JSON(
|
||||
{
|
||||
"foo" : "bar",
|
||||
"foot" : "bart",
|
||||
"bar" : [1, 2, 3, "contact"],
|
||||
"baz" : {
|
||||
"baf" : 1,
|
||||
"bar" : 2,
|
||||
"0" : 3
|
||||
}
|
||||
}
|
||||
)JSON");
|
||||
|
||||
Json goodTest = Json::parse(R"JSON(
|
||||
[
|
||||
{"op" : "test", "path" : "/foo", "value" : "bar"},
|
||||
{"op" : "test", "path" : "/foo", "value" : "bark", "inverse" : true},
|
||||
{"op" : "test", "path" : "/foot", "value" : "bart"},
|
||||
{"op" : "test", "path" : "/bar", "value" : [1, 2, 3, "contact"]},
|
||||
{"op" : "test", "path" : "/bar/0", "value" : 1},
|
||||
{"op" : "test", "path" : "/bar/1", "value" : 2},
|
||||
{"op" : "test", "path" : "/bar/2", "value" : 3},
|
||||
{"op" : "test", "path" : "/bar/3", "value" : "contact"},
|
||||
{"op" : "test", "path" : "/baz", "value" : {"0" : 3, "baf" : 1, "bar" : 2}},
|
||||
{"op" : "test", "path" : "/baz/baf", "value" : 1},
|
||||
{"op" : "test", "path" : "/baz/bar", "value" : 2},
|
||||
{"op" : "test", "path" : "/baz/0", "value" : 3},
|
||||
{"op" : "test", "path" : "/nothere", "inverse" : true},
|
||||
{"op" : "test", "path" : "/foo" }
|
||||
]
|
||||
)JSON");
|
||||
|
||||
Json failTest1 = Json::parse(R"JSON(
|
||||
[
|
||||
{"op" : "test", "path" : "/bar", "value" : [1, 3, 2, "contact"]}
|
||||
]
|
||||
)JSON");
|
||||
|
||||
Json failTest2 = Json::parse(R"JSON(
|
||||
[
|
||||
{"op" : "test", "path" : "/bar/-", "value" : "contact"}
|
||||
]
|
||||
)JSON");
|
||||
|
||||
Json failTest3 = Json::parse(R"JSON(
|
||||
[
|
||||
{"op" : "test", "path" : "/xyzzy", "value" : null}
|
||||
]
|
||||
)JSON");
|
||||
|
||||
Json failTest4 = Json::parse(R"JSON(
|
||||
[
|
||||
{"op" : "test", "path" : "/xyzzy/zop", "value" : null}
|
||||
]
|
||||
)JSON");
|
||||
|
||||
Json failTest5 = Json::parse(R"JSON(
|
||||
[
|
||||
{"op" : "test", "path" : "/nothere" }
|
||||
]
|
||||
)JSON");
|
||||
|
||||
Json failTest6 = Json::parse(R"JSON(
|
||||
[
|
||||
{"op" : "test", "path" : "/bar", "inverse" : true }
|
||||
]
|
||||
)JSON");
|
||||
|
||||
Json failTest7 = Json::parse(R"JSON(
|
||||
[
|
||||
{"op" : "test", "path" : "/foo", "value" : "bar", "inverse" : true }
|
||||
]
|
||||
)JSON");
|
||||
|
||||
jsonPatch(base, goodTest.toArray());
|
||||
|
||||
ASSERT_THROW(jsonPatch(base, failTest1.toArray()), JsonPatchTestFail);
|
||||
ASSERT_THROW(jsonPatch(base, failTest2.toArray()), JsonPatchTestFail);
|
||||
ASSERT_THROW(jsonPatch(base, failTest3.toArray()), JsonPatchTestFail);
|
||||
ASSERT_THROW(jsonPatch(base, failTest4.toArray()), JsonPatchTestFail);
|
||||
ASSERT_THROW(jsonPatch(base, failTest5.toArray()), JsonPatchTestFail);
|
||||
ASSERT_THROW(jsonPatch(base, failTest6.toArray()), JsonPatchTestFail);
|
||||
ASSERT_THROW(jsonPatch(base, failTest7.toArray()), JsonPatchTestFail);
|
||||
}
|
||||
|
||||
TEST(JsonTest, PatchingEscaping) {
|
||||
Json base1 = Json::parse(R"JSON(
|
||||
{
|
||||
"~" : true,
|
||||
"/" : false,
|
||||
"~~0" : "foo",
|
||||
"~~1" : "bar",
|
||||
"~~0~1/~0~" : "ugh"
|
||||
}
|
||||
)JSON");
|
||||
|
||||
Json test1 = Json::parse(R"JSON(
|
||||
[
|
||||
{"op" : "test", "path" : "/~0", "value" : true},
|
||||
{"op" : "test", "path" : "/~1", "value" : false},
|
||||
{"op" : "test", "path" : "/~0~00", "value" : "foo"},
|
||||
{"op" : "test", "path" : "/~0~01", "value" : "bar"},
|
||||
{"op" : "test", "path" : "/~0~00~01~1~00~0", "value" : "ugh"}
|
||||
]
|
||||
)JSON");
|
||||
|
||||
jsonPatch(base1, test1.toArray());
|
||||
}
|
||||
|
||||
TEST(JsonTest, MergeQuery) {
|
||||
Json json1 = Json::parse(R"JSON(
|
||||
{
|
||||
"foo" : "foo1",
|
||||
"bar" : "bar1",
|
||||
"baz" : {
|
||||
"1" : "1"
|
||||
},
|
||||
"fob" : {},
|
||||
"fizz" : 4
|
||||
}
|
||||
)JSON");
|
||||
Json json2 = Json::parse(R"JSON(
|
||||
{
|
||||
"foo" : "foo2",
|
||||
"bar" : "bar2",
|
||||
"baz" : null,
|
||||
"baf" : {
|
||||
"2" : "2"
|
||||
},
|
||||
"fob" : 2
|
||||
}
|
||||
)JSON");
|
||||
Json json3 = Json::parse(R"JSON(
|
||||
{
|
||||
"baz" : {
|
||||
"3" : "3"
|
||||
},
|
||||
"baf" : {
|
||||
"3" : "3"
|
||||
},
|
||||
"fizz" : {
|
||||
}
|
||||
}
|
||||
)JSON");
|
||||
|
||||
auto testIdentical = [&](String const& key) {
|
||||
EXPECT_EQ(jsonMergeQuery(key, json1, json2, json3), jsonMerge(json1, json2, json3).query(key, {}));
|
||||
};
|
||||
|
||||
testIdentical("foo");
|
||||
testIdentical("bar");
|
||||
testIdentical("baz");
|
||||
testIdentical("baf");
|
||||
testIdentical("baz.1");
|
||||
testIdentical("baz.2");
|
||||
testIdentical("baz.3");
|
||||
testIdentical("baf.0");
|
||||
testIdentical("baf.2");
|
||||
testIdentical("baf.3");
|
||||
testIdentical("baz.blip");
|
||||
testIdentical("boo.blip");
|
||||
testIdentical("fob");
|
||||
testIdentical("fiz");
|
||||
testIdentical("nothing");
|
||||
}
|
270
source/test/line_test.cpp
Normal file
270
source/test/line_test.cpp
Normal file
|
@ -0,0 +1,270 @@
|
|||
#include "StarLine.hpp"
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
using namespace Star;
|
||||
|
||||
TEST(LineTest, IntersectionEndpoint) {
|
||||
Line2F a({0, 0}, {10, 10});
|
||||
Line2F b({10, -10}, {0, 0});
|
||||
|
||||
auto intersection1 = a.intersection(b);
|
||||
auto intersection2 = b.intersection(a);
|
||||
|
||||
EXPECT_TRUE(intersection1.intersects);
|
||||
EXPECT_TRUE(intersection2.intersects);
|
||||
|
||||
EXPECT_TRUE(vmag(intersection1.point - Vec2F(0, 0)) < 0.0001f);
|
||||
EXPECT_TRUE(vmag(intersection2.point - Vec2F(0, 0)) < 0.0001f);
|
||||
|
||||
EXPECT_TRUE(intersection1.glances);
|
||||
EXPECT_TRUE(intersection2.glances);
|
||||
|
||||
EXPECT_FALSE(intersection1.coincides);
|
||||
EXPECT_FALSE(intersection2.coincides);
|
||||
}
|
||||
|
||||
TEST(LineTest, IntersectionMiddle) {
|
||||
Line2F a({-5, 0}, {5, 0});
|
||||
Line2F b({0, -2}, {0, 8});
|
||||
|
||||
auto intersection1 = a.intersection(b);
|
||||
EXPECT_TRUE(intersection1.intersects);
|
||||
EXPECT_FALSE(intersection1.glances);
|
||||
EXPECT_FALSE(intersection1.coincides);
|
||||
EXPECT_TRUE(vmag(intersection1.point - Vec2F(0, 0)) < 0.0001f);
|
||||
EXPECT_TRUE(fabs(intersection1.t - 0.5f) < 0.0001f);
|
||||
}
|
||||
|
||||
TEST(LineTest, IntersectionOneEndpoint) {
|
||||
Line2F a({0, 0}, {0, 5});
|
||||
Line2F b({-1, 5}, {1, 5});
|
||||
|
||||
auto intersection1 = a.intersection(b);
|
||||
auto intersection2 = b.intersection(a);
|
||||
EXPECT_TRUE(intersection1.glances);
|
||||
EXPECT_TRUE(intersection2.glances);
|
||||
EXPECT_FALSE(intersection1.coincides);
|
||||
EXPECT_FALSE(intersection2.coincides);
|
||||
|
||||
EXPECT_TRUE(fabs(intersection1.t - 1.0f) < 0.0001f);
|
||||
EXPECT_TRUE(fabs(intersection2.t - 0.5f) < 0.0001f);
|
||||
}
|
||||
|
||||
TEST(LineTest, IntersectionOneVertical) {
|
||||
Line2F a({0, 3}, {8, 5});
|
||||
Line2F b({4, 0}, {4, 8});
|
||||
|
||||
auto intersection1 = a.intersection(b);
|
||||
auto intersection2 = b.intersection(a);
|
||||
EXPECT_FALSE(intersection1.glances);
|
||||
EXPECT_FALSE(intersection2.glances);
|
||||
EXPECT_FALSE(intersection1.coincides);
|
||||
EXPECT_FALSE(intersection2.coincides);
|
||||
EXPECT_TRUE(intersection1.intersects);
|
||||
EXPECT_TRUE(intersection2.intersects);
|
||||
|
||||
EXPECT_TRUE(fabs(intersection1.t - 0.5f) < 0.0001f);
|
||||
EXPECT_TRUE(fabs(intersection2.t - 0.5f) < 0.0001f);
|
||||
|
||||
EXPECT_TRUE(vmag(intersection1.point - Vec2F(4, 4)) < 0.0001f);
|
||||
EXPECT_TRUE(vmag(intersection2.point - Vec2F(4, 4)) < 0.0001f);
|
||||
}
|
||||
|
||||
TEST(LineTest, NoIntersection) {
|
||||
Line2F a({1, 1}, {2, 2});
|
||||
Line2F b({-1, 1}, {0, 0});
|
||||
|
||||
auto intersection1 = a.intersection(b);
|
||||
auto intersection1inf = a.intersection(b, true);
|
||||
|
||||
EXPECT_FALSE(intersection1.intersects);
|
||||
EXPECT_TRUE(intersection1inf.intersects);
|
||||
}
|
||||
|
||||
TEST(LineTest, ParallelHorizontal) {
|
||||
Line2F a({9, 12}, {10, 12});
|
||||
Line2F b({10, 20}, {20, 20});
|
||||
|
||||
auto intersection1 = a.intersection(b);
|
||||
auto intersection2 = b.intersection(a);
|
||||
|
||||
EXPECT_FALSE(intersection1.intersects);
|
||||
EXPECT_FALSE(intersection2.intersects);
|
||||
|
||||
EXPECT_FALSE(intersection1.glances);
|
||||
EXPECT_FALSE(intersection2.glances);
|
||||
|
||||
EXPECT_FALSE(intersection1.coincides);
|
||||
EXPECT_FALSE(intersection2.coincides);
|
||||
}
|
||||
|
||||
TEST(LineTest, ParallelVertical) {
|
||||
Line2F a({12, 12}, {12, 14});
|
||||
Line2F b({20, 10}, {20, 20});
|
||||
|
||||
auto intersection1 = a.intersection(b);
|
||||
auto intersection2 = b.intersection(a);
|
||||
|
||||
EXPECT_FALSE(intersection1.intersects);
|
||||
EXPECT_FALSE(intersection2.intersects);
|
||||
|
||||
EXPECT_FALSE(intersection1.glances);
|
||||
EXPECT_FALSE(intersection2.glances);
|
||||
|
||||
EXPECT_FALSE(intersection1.coincides);
|
||||
EXPECT_FALSE(intersection2.coincides);
|
||||
}
|
||||
|
||||
TEST(LineTest, ParallelOther) {
|
||||
Line2F a({3, 3}, {4, 4});
|
||||
Line2F b({5, 6}, {7, 8});
|
||||
|
||||
auto intersection1 = a.intersection(b);
|
||||
auto intersection2 = b.intersection(a);
|
||||
|
||||
EXPECT_FALSE(intersection1.intersects);
|
||||
EXPECT_FALSE(intersection2.intersects);
|
||||
|
||||
EXPECT_FALSE(intersection1.glances);
|
||||
EXPECT_FALSE(intersection2.glances);
|
||||
|
||||
EXPECT_FALSE(intersection1.coincides);
|
||||
EXPECT_FALSE(intersection2.coincides);
|
||||
}
|
||||
|
||||
TEST(LineTest, CoincidesVertical) {
|
||||
Line2F a({3, 3}, {3, 4});
|
||||
Line2F b({3, 5}, {3, 7});
|
||||
|
||||
auto intersection1 = a.intersection(b);
|
||||
auto intersection2 = b.intersection(a);
|
||||
|
||||
auto intersection1inf = a.intersection(b, true);
|
||||
|
||||
EXPECT_FALSE(intersection1.intersects);
|
||||
EXPECT_FALSE(intersection2.intersects);
|
||||
EXPECT_TRUE(intersection1inf.intersects);
|
||||
|
||||
EXPECT_FALSE(intersection1.glances);
|
||||
EXPECT_FALSE(intersection2.glances);
|
||||
EXPECT_TRUE(intersection1inf.glances);
|
||||
|
||||
EXPECT_TRUE(intersection1.coincides);
|
||||
EXPECT_TRUE(intersection2.coincides);
|
||||
EXPECT_TRUE(intersection1inf.coincides);
|
||||
|
||||
EXPECT_TRUE(fabs(intersection1.t - 2.0f) < 0.0001f);
|
||||
EXPECT_TRUE(fabs(intersection2.t + 0.5f) < 0.0001f);
|
||||
}
|
||||
|
||||
TEST(LineTest, CoincidesHorizontal) {
|
||||
Line2F a({3, 3}, {4, 3});
|
||||
Line2F b({5, 3}, {7, 3});
|
||||
|
||||
auto intersection1 = a.intersection(b);
|
||||
auto intersection2 = b.intersection(a);
|
||||
|
||||
auto intersection1inf = a.intersection(b, true);
|
||||
|
||||
EXPECT_FALSE(intersection1.intersects);
|
||||
EXPECT_FALSE(intersection2.intersects);
|
||||
EXPECT_TRUE(intersection1inf.intersects);
|
||||
|
||||
EXPECT_FALSE(intersection1.glances);
|
||||
EXPECT_FALSE(intersection2.glances);
|
||||
EXPECT_TRUE(intersection1inf.glances);
|
||||
|
||||
EXPECT_TRUE(intersection1.coincides);
|
||||
EXPECT_TRUE(intersection2.coincides);
|
||||
EXPECT_TRUE(intersection1inf.coincides);
|
||||
|
||||
EXPECT_TRUE(fabs(intersection1.t - 2.0f) < 0.0001f);
|
||||
EXPECT_TRUE(fabs(intersection2.t + 0.5f) < 0.0001f);
|
||||
}
|
||||
|
||||
TEST(LineTest, CoincidesOther) {
|
||||
Line2F a({3, 3}, {4, 4});
|
||||
Line2F b({5, 5}, {7, 7});
|
||||
|
||||
auto intersection1 = a.intersection(b);
|
||||
auto intersection2 = b.intersection(a);
|
||||
|
||||
auto intersection1inf = a.intersection(b, true);
|
||||
|
||||
EXPECT_FALSE(intersection1.intersects);
|
||||
EXPECT_FALSE(intersection2.intersects);
|
||||
EXPECT_TRUE(intersection1inf.intersects);
|
||||
|
||||
EXPECT_FALSE(intersection1.glances);
|
||||
EXPECT_FALSE(intersection2.glances);
|
||||
EXPECT_TRUE(intersection1inf.glances);
|
||||
|
||||
EXPECT_TRUE(intersection1.coincides);
|
||||
EXPECT_TRUE(intersection2.coincides);
|
||||
EXPECT_TRUE(intersection1inf.coincides);
|
||||
|
||||
EXPECT_TRUE(fabs(intersection1.t - 2.0f) < 0.0001f);
|
||||
EXPECT_TRUE(fabs(intersection2.t + 0.5f) < 0.0001f);
|
||||
}
|
||||
|
||||
TEST(LineTest, IntersectCoincides) {
|
||||
Line2F a({3, 3}, {5, 5});
|
||||
Line2F b({4, 4}, {6, 6});
|
||||
|
||||
auto intersection1 = a.intersection(b);
|
||||
auto intersection2 = b.intersection(a);
|
||||
|
||||
EXPECT_TRUE(intersection1.intersects);
|
||||
EXPECT_TRUE(intersection2.intersects);
|
||||
|
||||
EXPECT_TRUE(intersection1.glances);
|
||||
EXPECT_TRUE(intersection2.glances);
|
||||
|
||||
EXPECT_TRUE(intersection1.coincides);
|
||||
EXPECT_TRUE(intersection2.coincides);
|
||||
|
||||
EXPECT_TRUE(vmag(intersection1.point - Vec2F(4, 4)) < 0.0001f);
|
||||
EXPECT_TRUE(vmag(intersection2.point - Vec2F(4, 4)) < 0.0001f);
|
||||
|
||||
EXPECT_TRUE(fabs(intersection1.t - 0.5f) < 0.0001f);
|
||||
EXPECT_TRUE(fabs(intersection2.t) < 0.0001f);
|
||||
}
|
||||
|
||||
TEST(LineTest, Closest) {
|
||||
Line2F a({0, 0}, {10, 0});
|
||||
|
||||
EXPECT_TRUE(fabs(a.distanceTo(Vec2F(-1, 5), true) - 5.0f) < 0.0001f);
|
||||
EXPECT_TRUE(fabs(a.distanceTo(Vec2F(-3, 4), false) - 5.0f) < 0.0001f);
|
||||
}
|
||||
|
||||
TEST(LineTest, MakePositive) {
|
||||
Line2F a({0, 0}, {10, 0});
|
||||
Line2F aorig = a;
|
||||
Line2F b({10, 0}, {0, 0});
|
||||
Line2F borig = b;
|
||||
Line2F c({10, 0}, {10, 1});
|
||||
Line2F corig = c;
|
||||
Line2F d({10, 1}, {10, 0});
|
||||
Line2F dorig = d;
|
||||
Line<float, 3> e({10, 0, 0}, {10, 0, 1});
|
||||
Line<float, 3> eorig = e;
|
||||
Line<float, 3> f({10, 0, 1}, {10, 0, 0});
|
||||
Line<float, 3> forig = f;
|
||||
|
||||
a.makePositive();
|
||||
EXPECT_TRUE(a == aorig);
|
||||
b.makePositive();
|
||||
EXPECT_TRUE(b == aorig);
|
||||
EXPECT_FALSE(b == borig);
|
||||
c.makePositive();
|
||||
EXPECT_TRUE(c == corig);
|
||||
d.makePositive();
|
||||
EXPECT_TRUE(d == corig);
|
||||
EXPECT_FALSE(d == dorig);
|
||||
e.makePositive();
|
||||
EXPECT_TRUE(e == eorig);
|
||||
f.makePositive();
|
||||
EXPECT_TRUE(f == eorig);
|
||||
EXPECT_FALSE(f == forig);
|
||||
}
|
425
source/test/lua_json_test.cpp
Normal file
425
source/test/lua_json_test.cpp
Normal file
|
@ -0,0 +1,425 @@
|
|||
#include "StarLua.hpp"
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
using namespace Star;
|
||||
|
||||
TEST(LuaJsonTest, Scope) {
|
||||
auto engine = LuaEngine::create();
|
||||
auto script1 = engine->compile(
|
||||
R"SCRIPT(
|
||||
function increment()
|
||||
self.called = self.called + 1
|
||||
return self.called
|
||||
end
|
||||
)SCRIPT");
|
||||
|
||||
auto script2 = engine->compile(
|
||||
R"SCRIPT(
|
||||
global = 42
|
||||
|
||||
function increment()
|
||||
self.called = self.called + 1
|
||||
return self.called
|
||||
end
|
||||
)SCRIPT");
|
||||
|
||||
auto context1 = engine->createContext();
|
||||
auto context2 = engine->createContext();
|
||||
|
||||
context1.load(script1);
|
||||
context2.load(script2);
|
||||
|
||||
context1.setPath("self", JsonObject{{"called", 0}});
|
||||
context2.setPath("self", JsonObject{{"called", 0}});
|
||||
|
||||
EXPECT_TRUE(context1.contains("self"));
|
||||
EXPECT_TRUE(context1.contains("increment"));
|
||||
|
||||
EXPECT_EQ(context1.invokePath<Json>("increment"), 1);
|
||||
EXPECT_EQ(context1.invokePath<Json>("increment"), 2);
|
||||
EXPECT_EQ(context1.invokePath<Json>("increment"), 3);
|
||||
EXPECT_EQ(context2.invokePath<Json>("increment"), 1);
|
||||
EXPECT_EQ(context1.invokePath<Json>("increment"), 4);
|
||||
EXPECT_EQ(context2.invokePath<Json>("increment"), 2);
|
||||
|
||||
auto context3 = engine->createContext();
|
||||
context3.load(script2);
|
||||
context3.setPath("self", JsonObject{{"called", 0}});
|
||||
|
||||
EXPECT_EQ(context2.invokePath<Json>("increment"), 3);
|
||||
EXPECT_EQ(context3.invokePath<Json>("increment"), 1);
|
||||
EXPECT_EQ(context1.invokePath<Json>("increment"), 5);
|
||||
|
||||
EXPECT_FALSE(context1.contains("global"));
|
||||
EXPECT_TRUE(context3.contains("global"));
|
||||
}
|
||||
|
||||
TEST(LuaJsonTest, FunkyRecursion) {
|
||||
auto engine = LuaEngine::create();
|
||||
|
||||
auto context1 = engine->createContext();
|
||||
context1.load(
|
||||
R"SCRIPT(
|
||||
mine = 1
|
||||
|
||||
function util()
|
||||
mine = 1
|
||||
return mine
|
||||
end
|
||||
|
||||
function util3()
|
||||
mine = 1
|
||||
return callbacks.util2()
|
||||
end
|
||||
)SCRIPT");
|
||||
|
||||
auto context2 = engine->createContext();
|
||||
context2.load(
|
||||
R"SCRIPT(
|
||||
mine = 2
|
||||
|
||||
function util2()
|
||||
return 4
|
||||
end
|
||||
|
||||
function entry()
|
||||
local other = callbacks.util()
|
||||
return {other, mine}
|
||||
end
|
||||
|
||||
function entry2()
|
||||
local other = callbacks.util2()
|
||||
return {other, mine}
|
||||
end
|
||||
|
||||
function entry3()
|
||||
local other = callbacks.util3()
|
||||
return {other, mine}
|
||||
end
|
||||
)SCRIPT");
|
||||
|
||||
LuaCallbacks callbacks;
|
||||
callbacks.registerCallback("util", [&context1]() -> Json { return context1.invokePath<Json>("util"); });
|
||||
callbacks.registerCallback("util2", [&context2]() -> Json { return context2.invokePath<Json>("util2"); });
|
||||
callbacks.registerCallback("util3", [&context1]() -> Json { return context1.invokePath<Json>("util3"); });
|
||||
|
||||
context1.setCallbacks("callbacks", callbacks);
|
||||
context2.setCallbacks("callbacks", callbacks);
|
||||
|
||||
auto res = context2.invokePath<Json>("entry");
|
||||
EXPECT_EQ(res.get(0), 1);
|
||||
EXPECT_EQ(res.get(1), 2);
|
||||
|
||||
auto res2 = context2.invokePath<Json>("entry2");
|
||||
EXPECT_EQ(res2.get(0), 4);
|
||||
EXPECT_EQ(res2.get(1), 2);
|
||||
|
||||
auto res3 = context2.invokePath<Json>("entry3");
|
||||
EXPECT_EQ(res3.get(0), 4);
|
||||
EXPECT_EQ(res3.get(1), 2);
|
||||
}
|
||||
|
||||
TEST(LuaJsonTest, TypeConversion) {
|
||||
auto engine = LuaEngine::create();
|
||||
auto context = engine->createContext();
|
||||
context.load(
|
||||
R"SCRIPT(
|
||||
var1 = "1"
|
||||
var2 = {}
|
||||
)SCRIPT");
|
||||
|
||||
EXPECT_EQ(context.getPath<String>("var1"), "1");
|
||||
EXPECT_EQ(context.getPath<double>("var1"), 1.0);
|
||||
EXPECT_EQ(context.getPath<JsonArray>("var2"), JsonArray());
|
||||
EXPECT_EQ(context.getPath<JsonObject>("var2"), JsonObject());
|
||||
}
|
||||
|
||||
TEST(LuaJsonTest, ChunkBoundaries) {
|
||||
auto engine = LuaEngine::create();
|
||||
auto context = engine->createContext();
|
||||
|
||||
context.load(
|
||||
R"SCRIPT(
|
||||
function func()
|
||||
return env.thing()
|
||||
end
|
||||
)SCRIPT");
|
||||
|
||||
context.load(
|
||||
R"SCRIPT(
|
||||
env = {}
|
||||
|
||||
function env.thing()
|
||||
local temp = {
|
||||
foo = extern.var
|
||||
}
|
||||
|
||||
return temp.foo
|
||||
end
|
||||
)SCRIPT");
|
||||
|
||||
context.setPath("extern", JsonObject{{"var", 1}});
|
||||
|
||||
EXPECT_EQ(context.invokePath<int>("func"), 1);
|
||||
}
|
||||
|
||||
TEST(LuaJsonTest, CustomObjectType) {
|
||||
auto engine = LuaEngine::create();
|
||||
auto context = engine->createContext();
|
||||
|
||||
context.load(
|
||||
R"SCRIPT(
|
||||
function createObject()
|
||||
map = jobject()
|
||||
map.foo = 'hello'
|
||||
map.bar = nil
|
||||
map.baz = nil
|
||||
return map
|
||||
end
|
||||
|
||||
function handleObject(arg)
|
||||
arg.bar = 'noodles'
|
||||
arg.test1 = jarray()
|
||||
arg.test2 = jobject()
|
||||
return arg
|
||||
end
|
||||
|
||||
function iteratePairs()
|
||||
local map = jobject()
|
||||
map.foo = 1
|
||||
map.foo = nil
|
||||
map.bar = 1
|
||||
|
||||
local keys = {}
|
||||
for key, val in pairs(map) do
|
||||
table.insert(keys, key)
|
||||
end
|
||||
return keys
|
||||
end
|
||||
|
||||
function nilsRemoved()
|
||||
local map = jobject()
|
||||
map.foo = 1
|
||||
map.foo = nil
|
||||
return rawget(map, "foo")
|
||||
end
|
||||
|
||||
function removeObject(arg, key)
|
||||
jremove(arg, key)
|
||||
return arg
|
||||
end
|
||||
)SCRIPT");
|
||||
|
||||
JsonObject test = context.invokePath<JsonObject>("createObject");
|
||||
JsonObject comp = {{"foo", "hello"}, {"bar", Json()}, {"baz", Json()}};
|
||||
EXPECT_EQ(test, comp);
|
||||
|
||||
test = context.invokePath<JsonObject>("handleObject", JsonObject{
|
||||
{"foo", JsonArray()},
|
||||
{"bar", Json()},
|
||||
{"baz", "hunky dory"},
|
||||
{"baf", Json()}
|
||||
});
|
||||
comp = {
|
||||
{"foo", JsonArray()},
|
||||
{"bar", "noodles"},
|
||||
{"baz", "hunky dory"},
|
||||
{"baf", Json()},
|
||||
{"test1", JsonArray()},
|
||||
{"test2", JsonObject()}
|
||||
};
|
||||
EXPECT_EQ(test, comp);
|
||||
|
||||
JsonArray testArray = context.invokePath<JsonArray>("iteratePairs");
|
||||
JsonArray compArray = {"bar"};
|
||||
EXPECT_EQ(testArray, compArray);
|
||||
|
||||
Json testValue = context.invokePath<Json>("nilsRemoved");
|
||||
EXPECT_EQ(testValue, Json());
|
||||
|
||||
Json testValue2 =
|
||||
context.invokePath<Json>("removeObject", JsonObject{{"foo", 1}, {"bar", Json()}, {"baz", Json()}}, "bar");
|
||||
Json compValue2 = JsonObject{{"foo", 1}, {"baz", Json()}};
|
||||
EXPECT_EQ(testValue2, compValue2);
|
||||
}
|
||||
|
||||
TEST(LuaJsonTest, CustomArrayType) {
|
||||
auto engine = LuaEngine::create();
|
||||
auto context = engine->createContext();
|
||||
|
||||
context.load(
|
||||
R"SCRIPT(
|
||||
function createArray()
|
||||
list = jarray()
|
||||
list[1] = 1
|
||||
list[2] = 1
|
||||
list[3] = 1
|
||||
list[7] = 1
|
||||
list[12] = nil
|
||||
return list
|
||||
end
|
||||
|
||||
function handleArray(arg)
|
||||
arg[1] = 'noodles'
|
||||
arg[4] = jarray()
|
||||
arg[9] = jobject()
|
||||
arg[10] = nil
|
||||
return arg
|
||||
end
|
||||
|
||||
function iteratePairs()
|
||||
local list = jarray()
|
||||
list[1] = 1
|
||||
list[5] = nil
|
||||
list[4] = 1
|
||||
list[9] = nil
|
||||
|
||||
local keys = {}
|
||||
for key, val in pairs(list) do
|
||||
table.insert(keys, key)
|
||||
end
|
||||
return keys
|
||||
end
|
||||
|
||||
function resizeArray(arg, size)
|
||||
jresize(arg, size)
|
||||
return arg
|
||||
end
|
||||
|
||||
function listSize(list)
|
||||
return jsize(list)
|
||||
end
|
||||
|
||||
function listSize2()
|
||||
return jsize({1, 1, 1, 1, 1})
|
||||
end
|
||||
)SCRIPT");
|
||||
|
||||
JsonArray test = context.invokePath<JsonArray>("createArray");
|
||||
JsonArray comp = {
|
||||
1, 1, 1, {}, {}, {}, 1, {}, {}, {}, {}, {},
|
||||
};
|
||||
EXPECT_EQ(test, comp);
|
||||
|
||||
test = context.invokePath<JsonArray>("handleArray",
|
||||
JsonArray{
|
||||
2, {}, 5, 6, {}, {}, "omg",
|
||||
});
|
||||
comp = {
|
||||
"noodles", {}, 5, JsonArray{}, {}, {}, "omg", {}, JsonObject{}, {},
|
||||
};
|
||||
EXPECT_EQ(test, comp);
|
||||
|
||||
JsonArray testArray = context.invokePath<JsonArray>("iteratePairs");
|
||||
JsonArray compArray = {1, 4};
|
||||
EXPECT_EQ(testArray, compArray);
|
||||
|
||||
JsonArray testArray2 = context.invokePath<JsonArray>("resizeArray", JsonArray{1, 2, 3, 4, 5, Json(), Json(), 8}, 4);
|
||||
JsonArray compArray2 = {1, 2, 3, 4};
|
||||
EXPECT_EQ(testArray2, compArray2);
|
||||
|
||||
JsonArray testArray3 = context.invokePath<JsonArray>("resizeArray", JsonArray{1, 2, 3, 4}, 6);
|
||||
JsonArray compArray3 = {1, 2, 3, 4, Json(), Json()};
|
||||
EXPECT_EQ(testArray3, compArray3);
|
||||
|
||||
Json test4 = context.invokePath<Json>("listSize", JsonArray{1, 2, 3, 4, Json(), Json(), Json()});
|
||||
EXPECT_EQ(test4, 7);
|
||||
|
||||
EXPECT_EQ(context.invokePath<Json>("listSize2"), Json(5));
|
||||
}
|
||||
|
||||
TEST(LuaJsonTest, CustomArrayType2) {
|
||||
auto engine = LuaEngine::create();
|
||||
auto context = engine->createContext();
|
||||
|
||||
context.load(
|
||||
R"SCRIPT(
|
||||
function doTest()
|
||||
sampleTable = jobject()
|
||||
sampleTable[18] = 0
|
||||
sampleTable[37] = 0
|
||||
|
||||
targetTable = jobject()
|
||||
|
||||
for k, v in pairs(sampleTable) do
|
||||
targetTable[k] = v
|
||||
end
|
||||
|
||||
return targetTable
|
||||
end
|
||||
|
||||
function arrayLogic1()
|
||||
l = {}
|
||||
l[1] = "foo"
|
||||
l[2] = "bar"
|
||||
return l
|
||||
end
|
||||
|
||||
function arrayLogic2()
|
||||
l = {}
|
||||
l["1"] = "foo"
|
||||
l["2"] = "bar"
|
||||
return l
|
||||
end
|
||||
|
||||
function arrayLogic3()
|
||||
l = {}
|
||||
l["1"] = "foo"
|
||||
l["2.1"] = "bar"
|
||||
return l
|
||||
end
|
||||
|
||||
function arrayLogic4()
|
||||
l = jarray()
|
||||
l["1"] = "foo"
|
||||
l["2"] = "bar"
|
||||
return l
|
||||
end
|
||||
)SCRIPT");
|
||||
|
||||
JsonObject test = context.invokePath<JsonObject>("doTest");
|
||||
JsonObject comp = {{"18", 0}, {"37", 0}};
|
||||
EXPECT_EQ(test, comp);
|
||||
|
||||
Json arrayTest1 = context.invokePath<Json>("arrayLogic1");
|
||||
Json arrayComp1 = JsonArray{"foo", "bar"};
|
||||
EXPECT_EQ(arrayTest1, arrayComp1);
|
||||
|
||||
Json arrayTest2 = context.invokePath<Json>("arrayLogic2");
|
||||
Json arrayComp2 = JsonArray{"foo", "bar"};
|
||||
EXPECT_EQ(arrayTest2, arrayComp2);
|
||||
|
||||
Json arrayTest3 = context.invokePath<Json>("arrayLogic3");
|
||||
Json arrayComp3 = JsonObject{{"1", "foo"}, {"2.1", "bar"}};
|
||||
EXPECT_EQ(arrayTest2, arrayComp2);
|
||||
|
||||
Json arrayTest4 = context.invokePath<Json>("arrayLogic4");
|
||||
Json arrayComp4 = JsonArray{"foo", "bar"};
|
||||
EXPECT_EQ(arrayTest3, arrayComp3);
|
||||
}
|
||||
|
||||
TEST(LuaJsonTest, IntFloat) {
|
||||
auto engine = LuaEngine::create();
|
||||
auto context = engine->createContext();
|
||||
|
||||
context.load(
|
||||
R"SCRIPT(
|
||||
function returnFloat()
|
||||
return 1.0
|
||||
end
|
||||
|
||||
function returnInt()
|
||||
return 1
|
||||
end
|
||||
|
||||
function printNumber(n)
|
||||
return tostring(n)
|
||||
end
|
||||
)SCRIPT");
|
||||
|
||||
EXPECT_TRUE(context.invokePath<Json>("returnFloat").isType(Json::Type::Float));
|
||||
EXPECT_TRUE(context.invokePath<Json>("returnInt").isType(Json::Type::Int));
|
||||
EXPECT_EQ(context.invokePath<String>("printNumber", 1.0), "1.0");
|
||||
EXPECT_EQ(context.invokePath<String>("printNumber", 1), "1");
|
||||
}
|
838
source/test/lua_test.cpp
Normal file
838
source/test/lua_test.cpp
Normal file
|
@ -0,0 +1,838 @@
|
|||
#include "StarLua.hpp"
|
||||
#include "StarLuaConverters.hpp"
|
||||
#include "StarLexicalCast.hpp"
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
using namespace Star;
|
||||
|
||||
TEST(LuaTest, BasicGetSet) {
|
||||
auto luaEngine = LuaEngine::create();
|
||||
LuaContext luaContext = luaEngine->createContext();
|
||||
luaContext.load(R"SCRIPT(
|
||||
data1 = 1.0
|
||||
data2 = 3.0 > 2.0
|
||||
data3 = "hello"
|
||||
)SCRIPT");
|
||||
|
||||
luaContext.set("data4", 4.0);
|
||||
|
||||
EXPECT_EQ(luaContext.get<double>("data1"), 1.0);
|
||||
EXPECT_EQ(luaContext.get<bool>("data2"), true);
|
||||
EXPECT_EQ(luaContext.get<String>("data3"), "hello");
|
||||
EXPECT_EQ(luaContext.get<double>("data4"), 4.0);
|
||||
}
|
||||
|
||||
TEST(LuaTest, TableReferences) {
|
||||
auto luaEngine = LuaEngine::create();
|
||||
LuaContext luaContext = luaEngine->createContext();
|
||||
luaContext.load(R"SCRIPT(
|
||||
table = {foo=1, bar=2}
|
||||
tableRef = table
|
||||
)SCRIPT");
|
||||
|
||||
auto table = luaContext.get<LuaTable>("table");
|
||||
auto tableRef1 = luaContext.get<LuaTable>("tableRef");
|
||||
auto tableRef2 = table;
|
||||
|
||||
EXPECT_EQ(table.get<double>("foo"), 1.0);
|
||||
EXPECT_EQ(table.get<double>("bar"), 2.0);
|
||||
|
||||
table.set("baz", 3.0);
|
||||
EXPECT_EQ(tableRef1.get<double>("baz"), 3.0);
|
||||
|
||||
tableRef1.set("baf", 4.0);
|
||||
EXPECT_EQ(table.get<double>("baf"), 4.0);
|
||||
EXPECT_EQ(tableRef2.get<double>("baf"), 4.0);
|
||||
}
|
||||
|
||||
TEST(LuaTest, FunctionCallTest) {
|
||||
weak_ptr<int> destructionObserver;
|
||||
|
||||
{
|
||||
auto luaEngine = LuaEngine::create();
|
||||
LuaContext luaContext = luaEngine->createContext();
|
||||
luaContext.load(R"SCRIPT(
|
||||
function testFunc(arg1, arg2)
|
||||
return callback(3) + arg1 + arg2
|
||||
end
|
||||
|
||||
function testEmpty()
|
||||
return emptyCallback()
|
||||
end
|
||||
)SCRIPT");
|
||||
|
||||
auto toDestruct = make_shared<int>();
|
||||
destructionObserver = toDestruct;
|
||||
luaContext.set("callback", luaEngine->createFunction([toDestruct](double n) { return n * 2; }));
|
||||
|
||||
luaContext.set("emptyCallback", luaEngine->createFunction([]() { return "heyooo"; }));
|
||||
|
||||
EXPECT_EQ(luaContext.invokePath<double>("testFunc", 5.0, 10.0), 21.0);
|
||||
EXPECT_EQ(luaContext.invokePath<String>("emptyCallback"), "heyooo");
|
||||
}
|
||||
|
||||
EXPECT_TRUE(destructionObserver.expired());
|
||||
}
|
||||
|
||||
TEST(LuaTest, CoroutineTest) {
|
||||
auto luaEngine = LuaEngine::create();
|
||||
LuaContext luaContext = luaEngine->createContext();
|
||||
|
||||
luaContext.load(R"SCRIPT(
|
||||
function accumulate(sum)
|
||||
return sum + callback(coroutine.yield(sum))
|
||||
end
|
||||
|
||||
function run()
|
||||
local sum = 0
|
||||
for i=1,4 do
|
||||
sum = accumulate(sum)
|
||||
end
|
||||
return sum
|
||||
end
|
||||
|
||||
co = coroutine.create(run)
|
||||
)SCRIPT");
|
||||
|
||||
luaContext.set("callback", luaEngine->createFunction([](double num) { return num * 2; }));
|
||||
|
||||
LuaThread thread = luaEngine->createThread();
|
||||
EXPECT_EQ(thread.status(), LuaThread::Status::Dead);
|
||||
LuaFunction func = luaContext.get<LuaFunction>("run");
|
||||
thread.pushFunction(func);
|
||||
EXPECT_EQ(thread.status(), LuaThread::Status::Active);
|
||||
EXPECT_EQ(thread.resume<double>(), 0.0);
|
||||
EXPECT_EQ(thread.resume<double>(1.0), 2.0);
|
||||
EXPECT_EQ(thread.resume<double>(3.0), 8.0);
|
||||
EXPECT_EQ(thread.resume<double>(5.0), 18.0);
|
||||
EXPECT_EQ(thread.resume<double>(7.0), 32.0);
|
||||
// manually created threads are empty after execution is finished
|
||||
EXPECT_EQ(thread.status(), LuaThread::Status::Dead);
|
||||
|
||||
thread.pushFunction(func);
|
||||
EXPECT_EQ(thread.resume<double>(), 0.0);
|
||||
EXPECT_EQ(thread.resume<double>(1.0), 2.0);
|
||||
EXPECT_THROW(thread.pushFunction(func), LuaException); // pushing function to suspended or errored thread
|
||||
|
||||
auto coroutine = luaContext.get<LuaThread>("co");
|
||||
EXPECT_EQ(coroutine.status(), LuaThread::Status::Active);
|
||||
EXPECT_EQ(coroutine.resume<double>(), 0.0);
|
||||
EXPECT_EQ(coroutine.resume<double>(1.0), 2.0);
|
||||
EXPECT_EQ(coroutine.resume<double>(3.0), 8.0);
|
||||
EXPECT_EQ(coroutine.resume<double>(5.0), 18.0);
|
||||
EXPECT_EQ(coroutine.resume<double>(7.0), 32.0);
|
||||
EXPECT_EQ(coroutine.status(), LuaThread::Status::Dead);
|
||||
EXPECT_THROW(coroutine.resume(), LuaException);
|
||||
EXPECT_EQ(coroutine.status(), LuaThread::Status::Dead);
|
||||
|
||||
LuaThread thread2 = luaEngine->createThread();
|
||||
EXPECT_EQ(thread2.status(), LuaThread::Status::Dead);
|
||||
thread2.pushFunction(func);
|
||||
EXPECT_EQ(thread2.status(), LuaThread::Status::Active);
|
||||
EXPECT_EQ(thread2.resume<double>(), 0.0);
|
||||
EXPECT_EQ(thread2.resume<double>(1.0), 2.0);
|
||||
EXPECT_THROW(thread2.resume<String>("not_a_number"), LuaException);
|
||||
EXPECT_EQ(thread2.status(), LuaThread::Status::Error);
|
||||
EXPECT_THROW(thread2.resume(), LuaException);
|
||||
EXPECT_EQ(thread2.status(), LuaThread::Status::Error);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool roundTripEqual(LuaContext const& context, T t) {
|
||||
return context.invokePath<T>("roundTrip", t) == t;
|
||||
}
|
||||
|
||||
TEST(LuaTest, Converters) {
|
||||
auto luaEngine = LuaEngine::create();
|
||||
LuaContext luaContext = luaEngine->createContext();
|
||||
|
||||
luaContext.load( R"SCRIPT(
|
||||
function makeVec()
|
||||
return {1, 2}
|
||||
end
|
||||
|
||||
function makePoly()
|
||||
return {{1, 2}, {3, 4}, {5, 6}}
|
||||
end
|
||||
|
||||
function roundTrip(ret)
|
||||
return ret
|
||||
end
|
||||
)SCRIPT");
|
||||
|
||||
auto vecCompare = Vec2F(1.0f, 2.0f);
|
||||
auto polyCompare = PolyF({Vec2F(1.0f, 2.0f), Vec2F(3.0f, 4.0f), Vec2F(5.0f, 6.0f)});
|
||||
|
||||
EXPECT_TRUE(luaContext.invokePath<Vec2F>("makeVec") == vecCompare);
|
||||
EXPECT_TRUE(luaContext.invokePath<PolyF>("makePoly") == polyCompare);
|
||||
EXPECT_TRUE(luaContext.invokePath<Vec2F>("roundTrip", vecCompare) == vecCompare);
|
||||
EXPECT_TRUE(luaContext.invokePath<PolyF>("roundTrip", polyCompare) == polyCompare);
|
||||
|
||||
EXPECT_TRUE(roundTripEqual(luaContext, PolyF()));
|
||||
EXPECT_TRUE(roundTripEqual(luaContext, List<int>{1, 2, 3, 4}));
|
||||
EXPECT_TRUE(roundTripEqual(luaContext, List<PolyF>{PolyF(), PolyF()}));
|
||||
EXPECT_TRUE(roundTripEqual(luaContext, Maybe<int>(1)));
|
||||
EXPECT_TRUE(roundTripEqual(luaContext, Maybe<int>()));
|
||||
|
||||
auto listCompare = List<int>{1, 2, 3, 4};
|
||||
EXPECT_TRUE(luaContext.invokePath<List<int>>("roundTrip", JsonArray{1, 2, 3, 4}) == listCompare);
|
||||
auto mapCompare = StringMap<String>{{"one", "two"}, {"three", "four"}};
|
||||
EXPECT_TRUE(luaContext.invokePath<StringMap<String>>("roundTrip", JsonObject{{"one", "two"}, {"three", "four"}}) == mapCompare);
|
||||
}
|
||||
|
||||
struct TestUserData1 {
|
||||
int field;
|
||||
};
|
||||
|
||||
struct TestUserData2 {
|
||||
int field;
|
||||
};
|
||||
|
||||
namespace Star {
|
||||
template <>
|
||||
struct LuaConverter<TestUserData1> : LuaUserDataConverter<TestUserData1> {};
|
||||
|
||||
template <>
|
||||
struct LuaConverter<TestUserData2> : LuaUserDataConverter<TestUserData2> {};
|
||||
}
|
||||
|
||||
TEST(LuaTest, UserDataTest) {
|
||||
auto luaEngine = LuaEngine::create();
|
||||
|
||||
LuaContext luaContext = luaEngine->createContext();
|
||||
luaContext.load(
|
||||
R"SCRIPT(
|
||||
function doit(ref)
|
||||
global = ref
|
||||
end
|
||||
)SCRIPT");
|
||||
|
||||
auto userdata1 = luaEngine->createUserData(TestUserData1{1});
|
||||
auto userdata2 = luaEngine->createUserData(TestUserData2{2});
|
||||
|
||||
luaContext.invokePath("doit", userdata1);
|
||||
auto userdata3 = luaContext.get<LuaUserData>("global");
|
||||
|
||||
EXPECT_TRUE(userdata2.is<TestUserData2>());
|
||||
EXPECT_FALSE(userdata2.is<TestUserData1>());
|
||||
|
||||
EXPECT_TRUE(userdata1.is<TestUserData1>());
|
||||
EXPECT_FALSE(userdata1.is<TestUserData2>());
|
||||
|
||||
EXPECT_TRUE(userdata3.is<TestUserData1>());
|
||||
EXPECT_FALSE(userdata3.is<TestUserData2>());
|
||||
|
||||
EXPECT_EQ(userdata1.get<TestUserData1>().field, 1);
|
||||
EXPECT_EQ(userdata2.get<TestUserData2>().field, 2);
|
||||
EXPECT_EQ(userdata3.get<TestUserData1>().field, 1);
|
||||
|
||||
userdata1.get<TestUserData1>().field = 3;
|
||||
EXPECT_EQ(userdata1.get<TestUserData1>().field, 3);
|
||||
EXPECT_EQ(userdata3.get<TestUserData1>().field, 3);
|
||||
|
||||
luaContext.invokePath("doit", TestUserData1());
|
||||
auto userdata4 = luaContext.get("global");
|
||||
EXPECT_TRUE(luaEngine->luaMaybeTo<TestUserData1>(userdata4).isValid());
|
||||
|
||||
luaContext.invokePath("doit", "notuserdata");
|
||||
auto notuserdata = luaContext.get("global");
|
||||
EXPECT_FALSE(luaEngine->luaMaybeTo<TestUserData1>(notuserdata).isValid());
|
||||
}
|
||||
|
||||
namespace Star {
|
||||
template <>
|
||||
struct LuaConverter<Vec3F> : LuaUserDataConverter<Vec3F> {};
|
||||
|
||||
template <>
|
||||
struct LuaUserDataMethods<Vec3F> {
|
||||
static LuaMethods<Vec3F> make() {
|
||||
LuaMethods<Vec3F> methods;
|
||||
methods.registerMethodWithSignature<float, Vec3F const&>("magnitude", mem_fn(&Vec3F::magnitude));
|
||||
return methods;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
TEST(LuaTest, UserMethodTest) {
|
||||
auto luaEngine = LuaEngine::create();
|
||||
luaEngine->setGlobal("vec3", luaEngine->createFunctionWithSignature<Vec3F, float, float, float>(construct<Vec3F>()));
|
||||
|
||||
LuaContext luaContext = luaEngine->createContext();
|
||||
luaContext.load(R"SCRIPT(
|
||||
v = vec3(3, 2, 1)
|
||||
function testMagnitude(v2)
|
||||
return v:magnitude() + v2:magnitude()
|
||||
end
|
||||
)SCRIPT");
|
||||
|
||||
float magnitude = luaContext.invokePath<float>("testMagnitude", Vec3F(5, 5, 5));
|
||||
EXPECT_TRUE(fabs(magnitude - (Vec3F(3, 2, 1).magnitude() + Vec3F(5, 5, 5).magnitude())) < 0.00001f);
|
||||
}
|
||||
|
||||
TEST(LuaTest, GlobalTest) {
|
||||
auto luaEngine = LuaEngine::create();
|
||||
luaEngine->setGlobal("globalfoo", LuaInt(42));
|
||||
EXPECT_EQ(luaEngine->getGlobal("globalfoo"), LuaInt(42));
|
||||
|
||||
LuaContext luaContext = luaEngine->createContext();
|
||||
luaContext.load(R"SCRIPT(
|
||||
function test()
|
||||
return globalfoo
|
||||
end
|
||||
)SCRIPT");
|
||||
|
||||
EXPECT_EQ(luaContext.invokePath("test"), LuaInt(42));
|
||||
}
|
||||
|
||||
TEST(LuaTest, ArgTest) {
|
||||
auto luaEngine = LuaEngine::create();
|
||||
|
||||
LuaContext luaContext = luaEngine->createContext();
|
||||
luaContext.load(R"SCRIPT(
|
||||
function test()
|
||||
callback("2", 3, nil)
|
||||
end
|
||||
)SCRIPT");
|
||||
|
||||
luaContext.set("callback",
|
||||
luaEngine->createFunction([](LuaFloat n, LuaString s, LuaBoolean b, LuaValue o) {
|
||||
EXPECT_EQ(n, 2.0);
|
||||
EXPECT_EQ(s, String("3"));
|
||||
EXPECT_EQ(b, false);
|
||||
EXPECT_EQ(o, LuaNil);
|
||||
}));
|
||||
|
||||
luaContext.invokePath("test");
|
||||
}
|
||||
|
||||
TEST(LuaTest, ArrayTest) {
|
||||
auto luaEngine = LuaEngine::create();
|
||||
LuaContext luaContext = luaEngine->createContext();
|
||||
luaContext.load(R"SCRIPT(
|
||||
function test()
|
||||
return {2, 4, 6, 8, 10}
|
||||
end
|
||||
)SCRIPT");
|
||||
|
||||
auto arrayTable = luaContext.invokePath("test").get<LuaTable>();
|
||||
|
||||
EXPECT_EQ(arrayTable.length(), 5);
|
||||
EXPECT_EQ(arrayTable.get(2), LuaInt(4));
|
||||
EXPECT_EQ(arrayTable.get(5), LuaInt(10));
|
||||
|
||||
List<pair<int, int>> values;
|
||||
arrayTable.iterate([&values](LuaValue const& key, LuaValue const& value) {
|
||||
auto ikey = key.get<LuaInt>();
|
||||
auto ivalue = value.get<LuaInt>();
|
||||
values.append({ikey, ivalue});
|
||||
});
|
||||
List<pair<int, int>> compare = {{1, 2}, {2, 4}, {3, 6}, {4, 8}, {5, 10}};
|
||||
EXPECT_EQ(values, compare);
|
||||
}
|
||||
|
||||
TEST(LuaTest, PathTest) {
|
||||
auto luaEngine = LuaEngine::create();
|
||||
LuaContext luaContext = luaEngine->createContext();
|
||||
luaContext.load(R"SCRIPT(
|
||||
foo = {
|
||||
bar = {
|
||||
baz = 1
|
||||
}
|
||||
}
|
||||
|
||||
function test()
|
||||
return foo.bar.baf
|
||||
end
|
||||
)SCRIPT");
|
||||
|
||||
EXPECT_EQ(luaContext.containsPath("foo.bar.baz"), true);
|
||||
EXPECT_EQ(luaContext.getPath("foo.bar.baz"), LuaInt(1));
|
||||
EXPECT_EQ(luaContext.containsPath("foo.nothing.at.all"), false);
|
||||
|
||||
luaContext.setPath("foo.bar.baf", LuaInt(5));
|
||||
EXPECT_EQ(luaContext.invokePath("test"), LuaInt(5));
|
||||
|
||||
luaContext.setPath("new.table.value", LuaInt(5));
|
||||
EXPECT_EQ(luaContext.getPath("new.table.value"), LuaInt(5));
|
||||
}
|
||||
|
||||
TEST(LuaTest, CallbackTest) {
|
||||
auto luaEngine = LuaEngine::create();
|
||||
|
||||
LuaCallbacks callbacks;
|
||||
callbacks.registerCallback("add", [](LuaInt a, LuaInt b) { return a + b; });
|
||||
callbacks.registerCallbackWithSignature<LuaInt, LuaInt, LuaInt>("subtract", [](int a, int b) { return a - b; });
|
||||
callbacks.registerCallbackWithSignature<LuaInt, LuaInt>("multiply2", bind([](int a, int b) { return a * b; }, 2, _1));
|
||||
callbacks.registerCallbackWithSignature<void, LuaValue>("nothing", [](LuaValue v) { return v; });
|
||||
|
||||
LuaContext luaContext = luaEngine->createContext();
|
||||
luaContext.setCallbacks("callbacks", callbacks);
|
||||
luaContext.load(R"SCRIPT(
|
||||
function test1()
|
||||
return callbacks.multiply2(callbacks.add(5, 10) + callbacks.subtract(3, 10))
|
||||
end
|
||||
|
||||
function test2()
|
||||
return callbacks.nothing(1)
|
||||
end
|
||||
)SCRIPT");
|
||||
|
||||
EXPECT_EQ(luaContext.invokePath("test1").get<LuaInt>(), 16);
|
||||
EXPECT_EQ(luaContext.invokePath("test2"), LuaNil);
|
||||
}
|
||||
|
||||
TEST(LuaTest, VariableParameters) {
|
||||
auto luaEngine = LuaEngine::create();
|
||||
auto context1 = luaEngine->createContext();
|
||||
|
||||
context1.load(R"SCRIPT(
|
||||
function variableArgsCount(...)
|
||||
local arg = {...}
|
||||
return #arg
|
||||
end
|
||||
)SCRIPT");
|
||||
|
||||
auto res1 = context1.invokePath<int>("variableArgsCount");
|
||||
auto res2 = context1.invokePath<int>("variableArgsCount", 1);
|
||||
auto res3 = context1.invokePath<int>("variableArgsCount", 1, 1);
|
||||
auto res4 = context1.invokePath<int>("variableArgsCount", 1, 1, 1);
|
||||
|
||||
EXPECT_EQ(res1, 0);
|
||||
EXPECT_EQ(res2, 1);
|
||||
EXPECT_EQ(res3, 2);
|
||||
EXPECT_EQ(res4, 3);
|
||||
}
|
||||
|
||||
TEST(LuaTest, Scope) {
|
||||
auto luaEngine = LuaEngine::create();
|
||||
auto script1 = luaEngine->compile(R"SCRIPT(
|
||||
function create(param)
|
||||
local self = {}
|
||||
local foo = param
|
||||
|
||||
local getValue = function()
|
||||
return foo
|
||||
end
|
||||
|
||||
function self.get()
|
||||
return getValue()
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
)SCRIPT");
|
||||
|
||||
auto script2 = luaEngine->compile(R"SCRIPT(
|
||||
function init()
|
||||
obj = create(param)
|
||||
end
|
||||
|
||||
function produce()
|
||||
return obj.get()
|
||||
end
|
||||
)SCRIPT");
|
||||
|
||||
auto context1 = luaEngine->createContext();
|
||||
context1.load(script1);
|
||||
context1.load(script2);
|
||||
|
||||
auto context2 = luaEngine->createContext();
|
||||
context2.load(script1);
|
||||
context2.load(script2);
|
||||
|
||||
context1.setPath("param", 1);
|
||||
context1.invokePath("init");
|
||||
|
||||
context2.setPath("param", 2);
|
||||
context2.invokePath("init");
|
||||
|
||||
EXPECT_EQ(context1.invokePath<int>("produce"), 1);
|
||||
EXPECT_EQ(context2.invokePath<int>("produce"), 2);
|
||||
|
||||
context1.setPath("param", 2);
|
||||
context1.invokePath("init");
|
||||
|
||||
context2.setPath("param", 1);
|
||||
context2.invokePath("init");
|
||||
|
||||
EXPECT_EQ(context1.invokePath<int>("produce"), 2);
|
||||
EXPECT_EQ(context2.invokePath<int>("produce"), 1);
|
||||
}
|
||||
|
||||
TEST(LuaTest, Scope2) {
|
||||
auto luaEngine = LuaEngine::create();
|
||||
|
||||
auto context1 = luaEngine->createContext();
|
||||
context1.load(R"SCRIPT(
|
||||
function init1()
|
||||
global = {}
|
||||
global.val = 10
|
||||
end
|
||||
)SCRIPT");
|
||||
|
||||
auto context2 = luaEngine->createContext();
|
||||
context2.load(R"SCRIPT(
|
||||
function init2()
|
||||
global = {}
|
||||
global.val = 20
|
||||
end
|
||||
)SCRIPT");
|
||||
|
||||
EXPECT_TRUE(context1.contains("init1"));
|
||||
EXPECT_TRUE(context2.contains("init2"));
|
||||
EXPECT_FALSE(context1.contains("init2"));
|
||||
EXPECT_FALSE(context2.contains("init1"));
|
||||
|
||||
context1.invokePath("init1");
|
||||
EXPECT_EQ(context1.getPath<int>("global.val"), 10);
|
||||
|
||||
EXPECT_TRUE(context2.getPath("global") == LuaNil);
|
||||
|
||||
context2.invokePath("init2");
|
||||
EXPECT_EQ(context2.getPath<int>("global.val"), 20);
|
||||
|
||||
EXPECT_EQ(context1.getPath<int>("global.val"), 10);
|
||||
}
|
||||
|
||||
TEST(LuaTest, MetaTable) {
|
||||
auto luaEngine = LuaEngine::create();
|
||||
|
||||
auto context = luaEngine->createContext();
|
||||
context.load(R"SCRIPT(
|
||||
function add(a, b)
|
||||
return a + b
|
||||
end
|
||||
)SCRIPT");
|
||||
|
||||
auto mt = luaEngine->createTable();
|
||||
mt.set("__add",
|
||||
luaEngine->createFunction([](LuaEngine& engine, LuaTable const& a, LuaTable const& b) {
|
||||
return engine.createArrayTable(
|
||||
initializer_list<double>{a.get<double>(1) + b.get<double>(1), a.get<double>(2) + b.get<double>(2)});
|
||||
}));
|
||||
mt.set("test", "hello");
|
||||
|
||||
auto t1 = luaEngine->createArrayTable(initializer_list<double>{1, 2});
|
||||
t1.setMetatable(mt);
|
||||
|
||||
auto t2 = luaEngine->createArrayTable(initializer_list<double>{5, 6});
|
||||
t2.setMetatable(mt);
|
||||
|
||||
auto tr = context.invokePath<LuaTable>("add", t1, t2);
|
||||
EXPECT_EQ(tr.get<double>(1), 6);
|
||||
EXPECT_EQ(tr.get<double>(2), 8);
|
||||
EXPECT_EQ(t1.getMetatable()->get<String>("test"), "hello");
|
||||
EXPECT_EQ(t2.getMetatable()->get<String>("test"), "hello");
|
||||
}
|
||||
|
||||
TEST(LuaTest, Integers) {
|
||||
auto luaEngine = LuaEngine::create();
|
||||
auto context = luaEngine->createContext();
|
||||
context.load(R"SCRIPT(
|
||||
n1 = 0
|
||||
n2 = 1
|
||||
n3 = 1.0
|
||||
n4 = 1.1
|
||||
n5 = 5.0
|
||||
n6 = 5
|
||||
)SCRIPT");
|
||||
|
||||
EXPECT_EQ(context.get("n1"), LuaInt(0));
|
||||
EXPECT_EQ(context.get("n2"), LuaInt(1));
|
||||
EXPECT_EQ(context.get("n3"), LuaFloat(1.0));
|
||||
EXPECT_EQ(context.get("n4"), LuaFloat(1.1));
|
||||
EXPECT_EQ(context.get("n5"), LuaFloat(5.0));
|
||||
EXPECT_EQ(context.get("n6"), LuaInt(5));
|
||||
}
|
||||
|
||||
TEST(LuaTest, Require) {
|
||||
auto luaEngine = LuaEngine::create();
|
||||
auto context = luaEngine->createContext();
|
||||
context.setRequireFunction([](LuaContext& context, LuaString const& arg) {
|
||||
context.set(arg, context.createFunction([arg]() {
|
||||
return arg;
|
||||
}));
|
||||
});
|
||||
|
||||
context.load(R"SCRIPT(
|
||||
require "a"
|
||||
require "b"
|
||||
require "c"
|
||||
|
||||
function res()
|
||||
return a() .. b() .. c()
|
||||
end
|
||||
)SCRIPT");
|
||||
|
||||
EXPECT_EQ(context.invokePath<LuaString>("res"), String("abc"));
|
||||
}
|
||||
|
||||
TEST(LuaTest, Eval) {
|
||||
auto luaEngine = LuaEngine::create();
|
||||
auto context = luaEngine->createContext();
|
||||
|
||||
context.eval("i = 3");
|
||||
// Make sure statements and expressions both work in eval.
|
||||
EXPECT_EQ(context.eval<int>("i + 1"), 4);
|
||||
EXPECT_EQ(context.eval<int>("return i + 1"), 4);
|
||||
}
|
||||
|
||||
TEST(LuaTest, Multi) {
|
||||
auto luaEngine = LuaEngine::create();
|
||||
auto script = luaEngine->compile(R"SCRIPT(
|
||||
function entry()
|
||||
return callbacks.func(2, 4)
|
||||
end
|
||||
|
||||
function sum(...)
|
||||
local sum = 0
|
||||
for i,v in ipairs(arg) do
|
||||
sum = sum + v
|
||||
end
|
||||
return sum
|
||||
end
|
||||
)SCRIPT");
|
||||
|
||||
auto context1 = luaEngine->createContext();
|
||||
auto context2 = luaEngine->createContext();
|
||||
auto context3 = luaEngine->createContext();
|
||||
|
||||
context1.load(script);
|
||||
context2.load(script);
|
||||
context3.load(script);
|
||||
|
||||
LuaCallbacks addCallbacks;
|
||||
addCallbacks.registerCallback("func",
|
||||
[](LuaVariadic<int> const& args) -> int {
|
||||
int sum = 0.0;
|
||||
for (auto arg : args)
|
||||
sum += arg;
|
||||
return sum;
|
||||
});
|
||||
|
||||
LuaCallbacks multCallbacks;
|
||||
multCallbacks.registerCallback("func",
|
||||
[](LuaVariadic<int> const& args) -> int {
|
||||
int mult = 1.0;
|
||||
for (auto arg : args)
|
||||
mult *= arg;
|
||||
return mult;
|
||||
});
|
||||
|
||||
context1.setCallbacks("callbacks", addCallbacks);
|
||||
context2.setCallbacks("callbacks", multCallbacks);
|
||||
context3.setCallbacks("callbacks", addCallbacks);
|
||||
|
||||
EXPECT_EQ(context1.invokePath<int>("entry"), 6);
|
||||
EXPECT_EQ(context2.invokePath<int>("entry"), 8);
|
||||
EXPECT_EQ(context3.invokePath<int>("entry"), 6);
|
||||
EXPECT_EQ(context1.invokePath<int>("entry"), 6);
|
||||
EXPECT_EQ(context2.invokePath<int>("entry"), 8);
|
||||
EXPECT_EQ(context3.invokePath<int>("entry"), 6);
|
||||
EXPECT_EQ(context1.invokePath<int>("entry"), 6);
|
||||
|
||||
auto context4 = luaEngine->createContext();
|
||||
context4.load(R"SCRIPT(
|
||||
function sum(...)
|
||||
local args = {...}
|
||||
local sum = 0
|
||||
for i = 1, #args do
|
||||
sum = sum + args[i]
|
||||
end
|
||||
return sum
|
||||
end
|
||||
|
||||
function mreturn(...)
|
||||
return ...
|
||||
end
|
||||
|
||||
function callbacktest(...)
|
||||
local x, y = callback()
|
||||
return x, y
|
||||
end
|
||||
|
||||
function emptycallbacktest(...)
|
||||
return emptycallback()
|
||||
end
|
||||
)SCRIPT");
|
||||
EXPECT_EQ(context4.invokePath<int>("sum", LuaVariadic<int>{1, 2, 3}), 6);
|
||||
EXPECT_EQ(context4.invokePath<int>("sum", 5, LuaVariadic<int>{1, 2, 3}, 10), 21);
|
||||
EXPECT_EQ(context4.invokePath<LuaVariadic<int>>("mreturn", 1, 2, 3), LuaVariadic<int>({1, 2, 3}));
|
||||
|
||||
int a;
|
||||
float b;
|
||||
String c;
|
||||
luaTie(a, b, c) = context4.invokePath<LuaTupleReturn<int, float, String>>("mreturn", 1, 2.0f, "foo");
|
||||
EXPECT_EQ(a, 1);
|
||||
EXPECT_EQ(b, 2.0f);
|
||||
EXPECT_EQ(c, "foo");
|
||||
|
||||
context4.set("callback", context4.createFunction([]() { return luaTupleReturn(5, 10); }));
|
||||
|
||||
context4.set("emptycallback", context4.createFunction([]() { return luaTupleReturn(); }));
|
||||
|
||||
int d;
|
||||
int e;
|
||||
luaTie(d, e) = context4.invokePath<LuaTupleReturn<int, int>>("callbacktest");
|
||||
EXPECT_EQ(d, 5);
|
||||
EXPECT_EQ(e, 10);
|
||||
|
||||
EXPECT_EQ(context4.invokePath("emptycallbacktest"), LuaNil);
|
||||
}
|
||||
|
||||
TEST(LuaTest, Limits) {
|
||||
auto luaEngine = LuaEngine::create();
|
||||
luaEngine->setInstructionLimit(500000);
|
||||
luaEngine->setRecursionLimit(64);
|
||||
auto context = luaEngine->createContext();
|
||||
context.load(R"SCRIPT(
|
||||
function toinfinityandbeyond()
|
||||
while true do
|
||||
end
|
||||
end
|
||||
|
||||
function toabignumberandthenstop()
|
||||
for i = 0, 50000 do
|
||||
end
|
||||
end
|
||||
)SCRIPT");
|
||||
|
||||
// Make sure infinite loops trigger the instruction limit.
|
||||
EXPECT_THROW(context.invokePath("toinfinityandbeyond"), LuaInstructionLimitReached);
|
||||
|
||||
// Make sure the instruction count is reset after each call.
|
||||
context.invokePath("toabignumberandthenstop");
|
||||
|
||||
auto infLoop = R"SCRIPT(
|
||||
while true do
|
||||
end
|
||||
)SCRIPT";
|
||||
|
||||
// Make sure loading code into context with infinite loops in their
|
||||
// evaluation triggers instruction limit.
|
||||
EXPECT_THROW(context.load(infLoop), LuaInstructionLimitReached);
|
||||
|
||||
// And the same for eval
|
||||
EXPECT_THROW(context.eval(infLoop), LuaInstructionLimitReached);
|
||||
|
||||
auto call1 = [&context]() { context.invokePath("call2"); };
|
||||
|
||||
auto call2 = [&context]() { context.invokePath("call1"); };
|
||||
|
||||
context.set("call1", context.createFunction(call1));
|
||||
context.set("call2", context.createFunction(call2));
|
||||
|
||||
EXPECT_THROW(context.invokePath("call1"), LuaRecursionLimitReached);
|
||||
|
||||
// Make sure the context still functions properly after these previous
|
||||
// errors.
|
||||
EXPECT_EQ(context.eval<int>("1 + 1"), 2);
|
||||
}
|
||||
|
||||
TEST(LuaTest, Errors) {
|
||||
auto luaEngine = LuaEngine::create();
|
||||
auto context = luaEngine->createContext();
|
||||
|
||||
EXPECT_THROW(context.eval("while true do"), LuaIncompleteStatementException);
|
||||
context.setPath("val", 1.0);
|
||||
EXPECT_THROW(context.getPath<Vec2D>("val"), LuaConversionException);
|
||||
EXPECT_EQ(luaEngine->luaMaybeTo<RectF>(context.get("val")), Maybe<RectF>());
|
||||
|
||||
context.set("throwException", luaEngine->createFunction([]() {
|
||||
throw StarException("lua caught the exception!");
|
||||
}));
|
||||
|
||||
context.load(R"SCRIPT(
|
||||
function throwError()
|
||||
return throwException()
|
||||
end
|
||||
function catchError()
|
||||
return pcall(throwException)
|
||||
end
|
||||
)SCRIPT");
|
||||
|
||||
EXPECT_THROW(context.invokePath("throwError"), LuaException);
|
||||
|
||||
bool status;
|
||||
LuaValue error;
|
||||
luaTie(status, error) = context.invokePath<LuaTupleReturn<bool, LuaValue>>("catchError");
|
||||
EXPECT_EQ(status, false);
|
||||
EXPECT_TRUE(String(toString(error)).contains("lua caught the exception"));
|
||||
}
|
||||
|
||||
TEST(LuaTest, GarbageCollection) {
|
||||
auto engine = LuaEngine::create();
|
||||
auto context = engine->createContext();
|
||||
|
||||
auto ptr = make_shared<int>(5);
|
||||
engine->setAutoGarbageCollection(false);
|
||||
context.setPath("ref", context.createUserData(ptr));
|
||||
EXPECT_EQ(ptr.use_count(), 2);
|
||||
context.setPath("ref", LuaNil);
|
||||
EXPECT_EQ(ptr.use_count(), 2);
|
||||
engine->collectGarbage();
|
||||
EXPECT_EQ(ptr.use_count(), 1);
|
||||
}
|
||||
|
||||
TEST(LuaTest, IntTest) {
|
||||
auto engine = LuaEngine::create();
|
||||
auto context = engine->createContext();
|
||||
context.setPath("test", (uint64_t)-1);
|
||||
EXPECT_EQ(context.getPath<uint64_t>("test"), (uint64_t)-1);
|
||||
}
|
||||
|
||||
TEST(LuaTest, VariantTest) {
|
||||
auto engine = LuaEngine::create();
|
||||
auto context = engine->createContext();
|
||||
|
||||
typedef Variant<int, String> IntOrString;
|
||||
|
||||
EXPECT_EQ(context.eval<IntOrString>("'foo'"), IntOrString(String("foo")));
|
||||
EXPECT_EQ(context.eval<IntOrString>("'1'"), IntOrString(1));
|
||||
|
||||
typedef MVariant<Maybe<int>, String> MIntOrString;
|
||||
|
||||
EXPECT_EQ(context.eval<MIntOrString>("'foo'"), MIntOrString(String("foo")));
|
||||
EXPECT_EQ(context.eval<MIntOrString>("'1'"), MIntOrString(Maybe<int>(1)));
|
||||
EXPECT_EQ(context.eval<MIntOrString>("nil"), MIntOrString());
|
||||
}
|
||||
|
||||
TEST(LuaTest, ProfilingTest) {
|
||||
auto luaEngine = LuaEngine::create();
|
||||
luaEngine->setProfilingEnabled(true);
|
||||
luaEngine->setInstructionMeasureInterval(1000);
|
||||
|
||||
auto context = luaEngine->createContext();
|
||||
context.eval(R"SCRIPT(
|
||||
function function1()
|
||||
for i = 1, 1000 do
|
||||
end
|
||||
end
|
||||
|
||||
function function2()
|
||||
for i = 1, 1000 do
|
||||
end
|
||||
end
|
||||
|
||||
function function3()
|
||||
for i = 1, 1000 do
|
||||
end
|
||||
end
|
||||
|
||||
for i = 1, 10000 do
|
||||
function1()
|
||||
function2()
|
||||
function3()
|
||||
end
|
||||
)SCRIPT");
|
||||
|
||||
StringSet names;
|
||||
List<LuaProfileEntry> profile = luaEngine->getProfile();
|
||||
for (auto const& p : profile[0].calls)
|
||||
names.add(p.second->name.value());
|
||||
|
||||
EXPECT_TRUE(names.contains("function1"));
|
||||
EXPECT_TRUE(names.contains("function2"));
|
||||
EXPECT_TRUE(names.contains("function3"));
|
||||
}
|
28
source/test/math_test.cpp
Normal file
28
source/test/math_test.cpp
Normal file
|
@ -0,0 +1,28 @@
|
|||
#include "StarMathCommon.hpp"
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
using namespace Star;
|
||||
|
||||
TEST(MathTest, All) {
|
||||
EXPECT_EQ(countSetBits<uint32_t>(7), 3u);
|
||||
EXPECT_EQ(countSetBits<uint32_t>(15), 4u);
|
||||
EXPECT_EQ(countSetBits<uint32_t>(-1), 32u);
|
||||
}
|
||||
|
||||
TEST(Math, CycleIncrement) {
|
||||
int a = 0;
|
||||
a = cycleIncrement(a, 10, 13);
|
||||
ASSERT_EQ(a, 10);
|
||||
a = cycleIncrement(a, 10, 13);
|
||||
ASSERT_EQ(a, 11);
|
||||
a = cycleIncrement(a, 10, 13);
|
||||
ASSERT_EQ(a, 12);
|
||||
a = cycleIncrement(a, 10, 13);
|
||||
ASSERT_EQ(a, 13);
|
||||
a = cycleIncrement(a, 10, 13);
|
||||
ASSERT_EQ(a, 10);
|
||||
int b = 14;
|
||||
b = cycleIncrement(b, 10, 13);
|
||||
ASSERT_EQ(b, 10);
|
||||
}
|
61
source/test/multi_table_test.cpp
Normal file
61
source/test/multi_table_test.cpp
Normal file
|
@ -0,0 +1,61 @@
|
|||
#include "StarMultiTable.hpp"
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
using namespace Star;
|
||||
|
||||
TEST(MultiArrayTest, All) {
|
||||
MultiArray<int, 2> table(10, 12);
|
||||
table.forEach([](Array2S const& index, int& val) {
|
||||
val = (index[0] + 1) * index[1];
|
||||
});
|
||||
|
||||
EXPECT_EQ(table(3, 4), 16);
|
||||
EXPECT_EQ(table(5, 2), 12);
|
||||
EXPECT_EQ(table(0, 9), 9);
|
||||
EXPECT_EQ(table(8, 1), 9);
|
||||
EXPECT_EQ(table(0, 1), 1);
|
||||
EXPECT_EQ(table(8, 9), 81);
|
||||
|
||||
MultiArray<int, 3> table3(5, 6, 7);
|
||||
table3.forEach([](Array3S const& index, int& val) {
|
||||
val = index[0] + index[1] + index[2];
|
||||
});
|
||||
|
||||
EXPECT_EQ(table3(0, 0, 0), 0);
|
||||
EXPECT_EQ(table3(1, 1, 0), 2);
|
||||
EXPECT_EQ(table3(2, 0, 2), 4);
|
||||
EXPECT_EQ(table3(1, 1, 1), 3);
|
||||
EXPECT_EQ(table3(0, 1, 0), 1);
|
||||
EXPECT_EQ(table3(2, 2, 2), 6);
|
||||
EXPECT_EQ(table3(3, 3, 3), 9);
|
||||
EXPECT_EQ(table3(4, 4, 4), 12);
|
||||
|
||||
table3.forEach({3, 3, 3}, {2, 2, 2}, [](Array3S const&, int& val) {
|
||||
val = 42;
|
||||
});
|
||||
|
||||
EXPECT_EQ(table3(2, 2, 2), 6);
|
||||
EXPECT_EQ(table3(3, 3, 4), 42);
|
||||
EXPECT_EQ(table3(4, 4, 4), 42);
|
||||
}
|
||||
|
||||
TEST(MultiTableTest, All) {
|
||||
MultiTable2F table;
|
||||
table.setRanges({MultiTable2F::Range{0, 2, 4, 6, 8, 10}, MultiTable2F::Range{0, 5, 10}});
|
||||
table.setInterpolationMode(InterpolationMode::Linear);
|
||||
table.setBoundMode(BoundMode::Clamp);
|
||||
table.eval([](Array2F const& index) {
|
||||
return index[0] * index[1];
|
||||
});
|
||||
|
||||
EXPECT_LT(fabs(table.interpolate({1.0f, 1.0f}) - 1.0f), 0.001f);
|
||||
EXPECT_LT(fabs(table.interpolate({9.0f, 9.0f}) - 81.0f), 0.001f);
|
||||
EXPECT_LT(fabs(table.interpolate({6.0f, 10.0f}) - 60.0f), 0.001f);
|
||||
EXPECT_LT(fabs(table.interpolate({6.0f, 11.0f}) - 60.0f), 0.001f);
|
||||
EXPECT_LT(fabs(table.get({1, 1}) - 10.0f), 0.001f);
|
||||
|
||||
table.setInterpolationMode(InterpolationMode::HalfStep);
|
||||
EXPECT_LT(fabs(table.interpolate({0.5f, 0.5f}) - 0.0f), 0.001f);
|
||||
EXPECT_LT(fabs(table.interpolate({4.0, 4.0}) - 20.0f), 0.001f);
|
||||
}
|
1058
source/test/net_states_test.cpp
Normal file
1058
source/test/net_states_test.cpp
Normal file
File diff suppressed because it is too large
Load diff
93
source/test/ordered_map_test.cpp
Normal file
93
source/test/ordered_map_test.cpp
Normal file
|
@ -0,0 +1,93 @@
|
|||
#include "StarOrderedMap.hpp"
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
using namespace Star;
|
||||
|
||||
TEST(OrderedMap, Insert) {
|
||||
OrderedMap<int, int> map;
|
||||
map.insert({3, 3});
|
||||
map.insert({2, 2});
|
||||
map.insert({1, 1});
|
||||
|
||||
{
|
||||
List<int> comp = {3, 2, 1};
|
||||
EXPECT_EQ(comp, map.keys());
|
||||
}
|
||||
|
||||
{
|
||||
List<int> comp = {3, 2, 1};
|
||||
List<int> keys;
|
||||
for (auto i = map.begin(); i != map.end(); ++i)
|
||||
keys.append(i->first);
|
||||
EXPECT_EQ(comp, keys);
|
||||
}
|
||||
|
||||
{
|
||||
List<int> comp = {1, 2, 3};
|
||||
List<int> keys;
|
||||
for (auto i = map.rbegin(); i != map.rend(); ++i)
|
||||
keys.append(i->first);
|
||||
EXPECT_EQ(comp, keys);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(OrderedMap, Getters) {
|
||||
OrderedMap<int, int> map;
|
||||
map[1] = 1;
|
||||
map[2] = 2;
|
||||
map[3] = 3;
|
||||
|
||||
EXPECT_EQ(map.get(1), 1);
|
||||
EXPECT_EQ(map.get(2), 2);
|
||||
EXPECT_EQ(map.get(3), 3);
|
||||
|
||||
EXPECT_EQ(map.ptr(3), &map.get(3));
|
||||
}
|
||||
|
||||
TEST(OrderedMap, ConstGetters) {
|
||||
OrderedHashMap<int, int> const map{
|
||||
{1, 1},
|
||||
{2, 2},
|
||||
{3, 3}
|
||||
};
|
||||
|
||||
EXPECT_EQ(map.get(1), 1);
|
||||
EXPECT_EQ(map.get(2), 2);
|
||||
EXPECT_EQ(map.get(3), 3);
|
||||
|
||||
EXPECT_EQ(map.ptr(3), &map.get(3));
|
||||
|
||||
EXPECT_EQ(map.value(4, 4), 4);
|
||||
EXPECT_EQ(map.maybe(5), Maybe<int>());
|
||||
}
|
||||
|
||||
TEST(OrderedMap, Sorting) {
|
||||
OrderedMap<int, int> map{
|
||||
{1, 5},
|
||||
{3, 3},
|
||||
{2, 4},
|
||||
{5, 1},
|
||||
{4, 2}
|
||||
};
|
||||
|
||||
EXPECT_EQ(map.keys(), List<int>({1, 3, 2, 5, 4}));
|
||||
map.sortByKey();
|
||||
EXPECT_EQ(map.keys(), List<int>({1, 2, 3, 4, 5}));
|
||||
map.sortByValue();
|
||||
EXPECT_EQ(map.keys(), List<int>({5, 4, 3, 2, 1}));
|
||||
}
|
||||
|
||||
TEST(OrderedMap, Removing) {
|
||||
OrderedHashMap<int, int> map{
|
||||
{5, 5},
|
||||
{4, 4},
|
||||
{3, 3},
|
||||
{2, 2},
|
||||
{1, 1}
|
||||
};
|
||||
|
||||
map.remove(3);
|
||||
map.remove(1);
|
||||
EXPECT_EQ(map.keys(), List<int>({5, 4, 2}));
|
||||
}
|
47
source/test/ordered_set_test.cpp
Normal file
47
source/test/ordered_set_test.cpp
Normal file
|
@ -0,0 +1,47 @@
|
|||
#include "StarOrderedSet.hpp"
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
using namespace Star;
|
||||
|
||||
TEST(OrderedSet, Insert) {
|
||||
OrderedSet<int> map{5, 4};
|
||||
map.insert(3);
|
||||
map.insert(2);
|
||||
map.insert(1);
|
||||
|
||||
EXPECT_EQ(map.values(), List<int>({5, 4, 3, 2, 1}));
|
||||
}
|
||||
|
||||
TEST(OrderedSet, AssignClear) {
|
||||
OrderedHashSet<int> map1{1, 2, 3};
|
||||
OrderedHashSet<int> map2 = map1;
|
||||
map1.clear();
|
||||
EXPECT_EQ(map1.values(), List<int>());
|
||||
EXPECT_EQ(map2.values(), List<int>({1, 2, 3}));
|
||||
}
|
||||
|
||||
TEST(OrderedSet, AddReplace) {
|
||||
OrderedHashSet<int> map;
|
||||
EXPECT_TRUE(map.add(4));
|
||||
EXPECT_TRUE(map.add(6));
|
||||
EXPECT_TRUE(map.add(1));
|
||||
EXPECT_FALSE(map.add(1));
|
||||
EXPECT_TRUE(map.add(2));
|
||||
EXPECT_TRUE(map.add(3));
|
||||
EXPECT_FALSE(map.add(2));
|
||||
EXPECT_TRUE(map.replace(4));
|
||||
EXPECT_FALSE(map.replace(5));
|
||||
EXPECT_FALSE(map.addBack(6));
|
||||
|
||||
EXPECT_EQ(map.values(), List<int>({1, 2, 3, 4, 5, 6}));
|
||||
}
|
||||
|
||||
TEST(OrderedSet, ReverseIterate) {
|
||||
OrderedHashSet<int> map{5, 4, 3, 2, 1};
|
||||
|
||||
List<int> vals;
|
||||
for (auto i = map.rbegin(); i != map.rend(); ++i)
|
||||
vals.append(*i);
|
||||
EXPECT_EQ(vals, List<int>({1, 2, 3, 4, 5}));
|
||||
}
|
30
source/test/periodic_test.cpp
Normal file
30
source/test/periodic_test.cpp
Normal file
|
@ -0,0 +1,30 @@
|
|||
#include "StarPeriodic.hpp"
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
using namespace Star;
|
||||
|
||||
TEST(PeriodicTest, All) {
|
||||
Periodic periodic(2);
|
||||
|
||||
EXPECT_TRUE(periodic.ready());
|
||||
EXPECT_TRUE(periodic.tick());
|
||||
EXPECT_FALSE(periodic.ready());
|
||||
EXPECT_FALSE(periodic.tick());
|
||||
EXPECT_TRUE(periodic.ready());
|
||||
EXPECT_TRUE(periodic.tick());
|
||||
|
||||
periodic = Periodic(0);
|
||||
EXPECT_FALSE(periodic.tick());
|
||||
EXPECT_FALSE(periodic.tick());
|
||||
|
||||
periodic = Periodic(3);
|
||||
EXPECT_TRUE(periodic.ready());
|
||||
EXPECT_TRUE(periodic.tick());
|
||||
EXPECT_FALSE(periodic.ready());
|
||||
EXPECT_FALSE(periodic.tick());
|
||||
EXPECT_FALSE(periodic.tick());
|
||||
EXPECT_TRUE(periodic.ready());
|
||||
EXPECT_TRUE(periodic.tick());
|
||||
EXPECT_FALSE(periodic.ready());
|
||||
}
|
51
source/test/poly_test.cpp
Normal file
51
source/test/poly_test.cpp
Normal file
|
@ -0,0 +1,51 @@
|
|||
#include "StarPoly.hpp"
|
||||
#include "StarRandom.hpp"
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
using namespace Star;
|
||||
|
||||
TEST(PolyTest, ConvexHull) {
|
||||
PolyF::VertexList inputVertexes;
|
||||
for (unsigned i = 0; i < 1000; ++i) {
|
||||
float angle = Random::randf() * 2 * Constants::pi;
|
||||
inputVertexes.append({sin(angle), cos(angle)});
|
||||
}
|
||||
|
||||
PolyF convex = PolyF::convexHull(inputVertexes);
|
||||
|
||||
PolyF::VertexList testVertexes;
|
||||
for (unsigned i = 0; i < 1000; ++i) {
|
||||
float angle = Random::randf() * 2 * Constants::pi;
|
||||
testVertexes.append({sin(angle) * 0.75f, cos(angle) * 0.75f});
|
||||
}
|
||||
|
||||
for (auto const& vertex : testVertexes)
|
||||
EXPECT_TRUE(convex.contains(vertex * 0.75f));
|
||||
}
|
||||
|
||||
TEST(PolyTest, Distance) {
|
||||
PolyF square = {{-1.0f, -1.0f}, {1.0f, -1.0f}, {1.0f, 1.0f}, {-1.0f, 1.0f}};
|
||||
|
||||
EXPECT_TRUE(fabs(square.distance({-2.0f, -2.0f}) - Constants::sqrt2) < 0.00001f);
|
||||
}
|
||||
|
||||
TEST(PolyTest, LineCollision) {
|
||||
PolyF square = {{-1.0f, -1.0f}, {1.0f, -1.0f}, {1.0f, 1.0f}, {-1.0f, 1.0f}};
|
||||
EXPECT_TRUE(vmag(Vec2F(-1, 0) - square.lineIntersection(Line2F({-2, 0}, {2, 0}))->point) < 0.0001f);
|
||||
EXPECT_TRUE(vmag(Vec2F(1, 0) - square.lineIntersection(Line2F({2, 0}, {-2, 0}))->point) < 0.0001f);
|
||||
}
|
||||
|
||||
TEST(PolyTest, ConvexArea) {
|
||||
PolyF triangle = {{-1.0f, -1.0f}, {1.0f, -1.0f}, {0.0f, 1.0f}};
|
||||
EXPECT_TRUE(fabs(triangle.convexArea() - 2.0f) < 0.0001f);
|
||||
}
|
||||
|
||||
TEST(PolyTest, Clipping) {
|
||||
PolyF triangle1 = {{-2.0f, -1.0f}, {2.0f, -1.0f}, {0.0f, 1.0f}};
|
||||
PolyF triangle2 = {{2.0f, 1.0f}, {-2.0f, 1.0f}, {0.0f, -1.0f}};
|
||||
PolyF overlap = PolyF::clip(triangle1, triangle2);
|
||||
overlap.deduplicateVertexes(0.0001f);
|
||||
EXPECT_TRUE(overlap.sides() == 4);
|
||||
EXPECT_TRUE(overlap.convexArea() - 2 < 0.0001f);
|
||||
}
|
174
source/test/random_test.cpp
Normal file
174
source/test/random_test.cpp
Normal file
|
@ -0,0 +1,174 @@
|
|||
#include "StarRandom.hpp"
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
using namespace Star;
|
||||
|
||||
TEST(RandTest, All) {
|
||||
RandomSource rand(31415926);
|
||||
EXPECT_EQ(rand.randu32(), 2950892229u);
|
||||
EXPECT_EQ(rand.randu32(), 1418047276u);
|
||||
EXPECT_EQ(rand.randu32(), 3790079132u);
|
||||
EXPECT_EQ(rand.randu32(), 445970691u);
|
||||
EXPECT_EQ(rand.randu32(), 2728181679u);
|
||||
|
||||
rand.addEntropy(27182818);
|
||||
EXPECT_EQ(rand.randu32(), 3255590103u);
|
||||
EXPECT_EQ(rand.randu32(), 678168874u);
|
||||
EXPECT_EQ(rand.randu32(), 3814633989u);
|
||||
EXPECT_EQ(rand.randu32(), 4070190392u);
|
||||
EXPECT_EQ(rand.randu32(), 265077625u);
|
||||
|
||||
for (auto i = 0; i < 1e5; ++i)
|
||||
rand.randu32();
|
||||
|
||||
EXPECT_EQ(rand.randu32(), 724230938u);
|
||||
|
||||
EXPECT_EQ(rand.randf(), 0.6708741188049316f);
|
||||
EXPECT_EQ(rand.randf(), 0.3297619521617889f);
|
||||
EXPECT_EQ(rand.randf(), 0.2407863438129425f);
|
||||
EXPECT_EQ(rand.randf(), 0.2388365715742111f);
|
||||
EXPECT_EQ(rand.randf(), 0.8430468440055847f);
|
||||
EXPECT_EQ(rand.randf(), 0.5036200881004333f);
|
||||
EXPECT_EQ(rand.randf(), 0.2279680222272873f);
|
||||
|
||||
EXPECT_EQ(rand.randd(), 0.0993789769271370693193290435374);
|
||||
EXPECT_EQ(rand.randd(), 0.489891395527775608265130813379);
|
||||
EXPECT_EQ(rand.randd(), 0.609412270506578757078841590555);
|
||||
EXPECT_EQ(rand.randd(), 0.838596715529411951273175418464);
|
||||
EXPECT_EQ(rand.randd(), 0.556277078882413622551439402741);
|
||||
EXPECT_EQ(rand.randd(), 0.575903901290708120086492272094);
|
||||
EXPECT_EQ(rand.randd(), 0.721894899474715856513284961693);
|
||||
|
||||
EXPECT_EQ(rand.randu32(), 2870093081u);
|
||||
EXPECT_EQ(rand.randu64(), 16492986915519838998u);
|
||||
EXPECT_EQ(rand.randu32(), 1029635267u);
|
||||
EXPECT_EQ(rand.randu32(), 1469630330u);
|
||||
EXPECT_EQ(rand.randu32(), 2017291831u);
|
||||
EXPECT_EQ(rand.randu32(), 2167938696u);
|
||||
EXPECT_EQ(rand.randu64(), 7889337349893562706u);
|
||||
EXPECT_EQ(rand.randu64(), 11595813817497350001u);
|
||||
EXPECT_EQ(rand.randu64(), 14292979134113073402u);
|
||||
EXPECT_EQ(rand.randu32(), 119058573u);
|
||||
|
||||
EXPECT_EQ(rand.randi32(), -1995152573);
|
||||
EXPECT_EQ(rand.randi32(), 1717688829);
|
||||
EXPECT_EQ(rand.randi64(), -4500435351487619671);
|
||||
EXPECT_EQ(rand.randi32(), 644788487);
|
||||
EXPECT_EQ(rand.randi64(), 2370131680533925071);
|
||||
EXPECT_EQ(rand.randi64(), -7391462988205297660);
|
||||
EXPECT_EQ(rand.randi32(), 817418170);
|
||||
EXPECT_EQ(rand.randi64(), -3754584120231434991);
|
||||
EXPECT_EQ(rand.randi64(), -2585223751692222899);
|
||||
|
||||
EXPECT_EQ(rand.randInt(34), 20);
|
||||
EXPECT_EQ(rand.randInt(483), 49);
|
||||
EXPECT_EQ(rand.randInt(2), 1);
|
||||
EXPECT_EQ(rand.randInt(49382), 12751);
|
||||
EXPECT_EQ(rand.randInt(1291), 872);
|
||||
EXPECT_EQ(rand.randInt(rand.randu32()), 306693728);
|
||||
EXPECT_EQ(rand.randInt(rand.randu32()), 332940738);
|
||||
EXPECT_EQ(rand.randInt(rand.randu32()), 94215324);
|
||||
EXPECT_EQ(rand.randInt(rand.randu32()), 43770718);
|
||||
EXPECT_EQ(rand.randInt(2939), 2938u);
|
||||
EXPECT_EQ(rand.randUInt(rand.randu32()), 179327438u);
|
||||
EXPECT_EQ(rand.randUInt(rand.randu32()), 1761816964u);
|
||||
EXPECT_EQ(rand.randUInt(rand.randu32()), 68031400u);
|
||||
EXPECT_EQ(rand.randUInt(3972097), 2100462u);
|
||||
EXPECT_EQ(rand.randUInt(878), 839u);
|
||||
EXPECT_EQ(rand.randUInt(rand.randu32()), 1499799820u);
|
||||
EXPECT_EQ(rand.randUInt(rand.randu32()), 1807471845u);
|
||||
|
||||
EXPECT_EQ(rand.randInt(83, 198207), 90862);
|
||||
EXPECT_EQ(rand.randInt(-98982, -989), -23203);
|
||||
EXPECT_EQ(rand.randInt(0, 1), 1);
|
||||
EXPECT_EQ(rand.randInt(-8279, rand.randu32()), 20616743);
|
||||
EXPECT_EQ(rand.randInt(87297, 298398), 142455);
|
||||
EXPECT_EQ(rand.randInt(-93792, rand.randu32()), 734418822);
|
||||
EXPECT_EQ(rand.randInt(2, 5), 3);
|
||||
EXPECT_EQ(rand.randInt(2938, 2940), 2939);
|
||||
EXPECT_EQ(rand.randUInt(9802, 87297), 47048u);
|
||||
EXPECT_EQ(rand.randUInt(9809802, 372987297), 150191254u);
|
||||
EXPECT_EQ(rand.randUInt(9809809, 272987297), 263742306u);
|
||||
EXPECT_EQ(rand.randUInt(4, (uint64_t)(-1)), 7288528389985641665u);
|
||||
EXPECT_EQ(rand.randUInt(rand.randu32(), (uint64_t)(-1)), 1748024317879856867u);
|
||||
EXPECT_EQ(rand.randUInt(2, rand.randu32()), 558624029u);
|
||||
EXPECT_EQ(rand.randUInt(9382, 888888), 212491u);
|
||||
|
||||
EXPECT_FLOAT_EQ(rand.randf(4.3f, 4.4f), 4.395795345306396f);
|
||||
EXPECT_FLOAT_EQ(rand.randf(rand.randf(), 5.0f), 4.580977439880371f);
|
||||
EXPECT_FLOAT_EQ(rand.randf(387.f, 3920.f), 3740.644775390625f);
|
||||
EXPECT_FLOAT_EQ(rand.randf(rand.randf(), 1.0f), 0.9794777631759644f);
|
||||
EXPECT_FLOAT_EQ(rand.randf(-392.f, rand.randf()), -276.0828552246094f);
|
||||
EXPECT_FLOAT_EQ(rand.randf(-2.f, rand.randf()), 0.1681497097015381f);
|
||||
EXPECT_DOUBLE_EQ(rand.randd(rand.randd(), 1.0), 0.942969795571236168996165361023);
|
||||
EXPECT_DOUBLE_EQ(rand.randd(rand.randd(), 1.1), 0.751293963391068353452340033982);
|
||||
EXPECT_DOUBLE_EQ(rand.randd(rand.randd(), 83), 9.31872432254218274749746342422);
|
||||
EXPECT_DOUBLE_EQ(rand.randd(-2, rand.randd()), 0.361844402875284743004158372059);
|
||||
EXPECT_DOUBLE_EQ(rand.randd(-2.3, rand.randd()), 0.580774591935332651360113231931);
|
||||
EXPECT_DOUBLE_EQ(rand.randd(-303, rand.randd()), -110.181037142766882652722415514);
|
||||
|
||||
EXPECT_EQ(rand.randb(), false);
|
||||
EXPECT_EQ(rand.randb(), true);
|
||||
EXPECT_EQ(rand.randb(), false);
|
||||
EXPECT_EQ(rand.randb(), true);
|
||||
EXPECT_EQ(rand.randb(), false);
|
||||
EXPECT_EQ(rand.randb(), false);
|
||||
|
||||
EXPECT_FLOAT_EQ(rand.nrandf(1.0f, 0.0f), -0.46687412f);
|
||||
EXPECT_FLOAT_EQ(rand.nrandf(1.5f, 4.0f), 4.5038204f);
|
||||
EXPECT_FLOAT_EQ(rand.nrandf(0.1f, 3.0f), 2.8866563f);
|
||||
EXPECT_FLOAT_EQ(rand.nrandf(5.0f, -10.0f), -7.4856615f);
|
||||
EXPECT_FLOAT_EQ(rand.nrandf(rand.randf(), 0.0f), 0.21202649f);
|
||||
EXPECT_FLOAT_EQ(rand.nrandf(rand.randf(), 0.0f), -0.18832046f);
|
||||
EXPECT_FLOAT_EQ(rand.nrandf(rand.randf(), 0.0f), -0.8733508f);
|
||||
EXPECT_DOUBLE_EQ(rand.nrandd(1.0, 0.0), -1.6134212525108711);
|
||||
EXPECT_DOUBLE_EQ(rand.nrandd(1.5, 4.0), 4.1692477323762258);
|
||||
EXPECT_DOUBLE_EQ(rand.nrandd(0.1, 3.0), 2.8561578555964706);
|
||||
EXPECT_DOUBLE_EQ(rand.nrandd(5.0, -10.0), -15.805748549670087);
|
||||
EXPECT_DOUBLE_EQ(rand.nrandd(rand.randd(), 0.0), -0.3154774175319317);
|
||||
EXPECT_DOUBLE_EQ(rand.nrandd(rand.randd(), 0.0), 0.074425803794012854);
|
||||
EXPECT_DOUBLE_EQ(rand.nrandd(rand.randd(), 0.0), 0.45895995279014684);
|
||||
|
||||
EXPECT_EQ(rand.stochasticRound(0.7), 1);
|
||||
EXPECT_EQ(rand.stochasticRound(0.7), 1);
|
||||
EXPECT_EQ(rand.stochasticRound(0.7), 1);
|
||||
EXPECT_EQ(rand.stochasticRound(0.7), 1);
|
||||
EXPECT_EQ(rand.stochasticRound(0.7), 0);
|
||||
EXPECT_EQ(rand.stochasticRound(0.7), 1);
|
||||
EXPECT_EQ(rand.stochasticRound(0.7), 1);
|
||||
EXPECT_EQ(rand.stochasticRound(0.7), 0);
|
||||
EXPECT_EQ(rand.stochasticRound(0.7), 1);
|
||||
EXPECT_EQ(rand.stochasticRound(0.1), 0);
|
||||
EXPECT_EQ(rand.stochasticRound(0.1), 0);
|
||||
EXPECT_EQ(rand.stochasticRound(0.1), 0);
|
||||
EXPECT_EQ(rand.stochasticRound(0.1), 0);
|
||||
EXPECT_EQ(rand.stochasticRound(0.1), 0);
|
||||
EXPECT_EQ(rand.stochasticRound(0.1), 0);
|
||||
EXPECT_EQ(rand.stochasticRound(0.1), 0);
|
||||
EXPECT_EQ(rand.stochasticRound(0.1), 0);
|
||||
EXPECT_EQ(rand.stochasticRound(0.1), 0);
|
||||
EXPECT_EQ(rand.stochasticRound(0.1), 0);
|
||||
EXPECT_EQ(rand.stochasticRound(0.1), 0);
|
||||
EXPECT_EQ(rand.stochasticRound(0.1), 1);
|
||||
EXPECT_EQ(rand.stochasticRound(0.1), 0);
|
||||
EXPECT_EQ(rand.stochasticRound(0.1), 0);
|
||||
}
|
||||
|
||||
TEST(StaticRandomTest, All) {
|
||||
EXPECT_EQ(staticRandomU64("test1", 999, "test2"), 17057684957748924255u);
|
||||
EXPECT_EQ(staticRandomU64("test1", 1000, "test2"), 17136762056491983648u);
|
||||
EXPECT_EQ(staticRandomU64("test1", 1001, "test2"), 10826209999926048792u);
|
||||
EXPECT_EQ(staticRandomU64("test1", 1002, "test2"), 10190371075442159783u);
|
||||
EXPECT_EQ(staticRandomU64("test1", 1003, "test2"), 16325723287291511625u);
|
||||
EXPECT_EQ(staticRandomU64("test1", 1004, "test2"), 6061201707788279217u);
|
||||
EXPECT_EQ(staticRandomU64("test1", 1005, "test2"), 13034875300321135291u);
|
||||
|
||||
EXPECT_EQ(staticRandomU32("test1", 999, "test2"), 3893169212u);
|
||||
EXPECT_EQ(staticRandomU32("test1", 1000, "test2"), 1330274955u);
|
||||
EXPECT_EQ(staticRandomU32("test1", 1001, "test2"), 2268597334u);
|
||||
EXPECT_EQ(staticRandomU32("test1", 1002, "test2"), 1221477368u);
|
||||
EXPECT_EQ(staticRandomU32("test1", 1003, "test2"), 271894555u);
|
||||
EXPECT_EQ(staticRandomU32("test1", 1004, "test2"), 2464836468u);
|
||||
EXPECT_EQ(staticRandomU32("test1", 1005, "test2"), 3808877030u);
|
||||
}
|
265
source/test/rect_test.cpp
Normal file
265
source/test/rect_test.cpp
Normal file
|
@ -0,0 +1,265 @@
|
|||
#include "StarRect.hpp"
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
using namespace Star;
|
||||
|
||||
TEST(RectTest, TranslateToInclude) {
|
||||
RectF rect;
|
||||
rect = {0, 0, 10, 10};
|
||||
rect.translateToInclude(20, 20, 2, 2);
|
||||
EXPECT_TRUE(rect.xMax() < 22.01 && rect.xMax() > 21.99);
|
||||
EXPECT_TRUE(rect.yMax() < 22.01 && rect.yMax() > 21.99);
|
||||
|
||||
rect = {0, 0, 10, 10};
|
||||
rect.translateToInclude(-20, -20, 2, 2);
|
||||
EXPECT_TRUE(rect.xMin() > -22.01 && rect.xMin() < -21.99);
|
||||
EXPECT_TRUE(rect.yMin() > -22.01 && rect.yMin() < -21.99);
|
||||
|
||||
rect = {0, 0, 10, 10};
|
||||
rect.translateToInclude(5, 5, 3, 3);
|
||||
EXPECT_TRUE(rect.xMin() < 0.01 && rect.xMin() > -0.01);
|
||||
EXPECT_TRUE(rect.yMin() < 0.01 && rect.yMin() > -0.01);
|
||||
}
|
||||
|
||||
TEST(RectTest, BoxGlanceCorner) {
|
||||
RectF rect1 = {0, 0, 10, 10};
|
||||
RectF rect2 = {-10, -10, 0, 0};
|
||||
auto res = rect1.intersection(rect2);
|
||||
|
||||
EXPECT_TRUE(!res.intersects);
|
||||
EXPECT_TRUE(res.glances);
|
||||
EXPECT_TRUE(vmag(res.overlap) < 0.0001f);
|
||||
}
|
||||
|
||||
TEST(RectTest, BoxGlanceEdge) {
|
||||
RectF rect1 = {0, 0, 10, 10};
|
||||
RectF rect2 = {-10, 0, 0, 10};
|
||||
auto res = rect1.intersection(rect2);
|
||||
|
||||
EXPECT_TRUE(!res.intersects);
|
||||
EXPECT_TRUE(res.glances);
|
||||
EXPECT_TRUE(vmag(res.overlap) < 0.0001f);
|
||||
}
|
||||
|
||||
TEST(RectTest, BoxIntersectionNone) {
|
||||
RectF rect1 = {0, 0, 10, 10};
|
||||
RectF rect2 = {-10, 0, -1, 10};
|
||||
auto res = rect1.intersection(rect2);
|
||||
|
||||
EXPECT_TRUE(!res.intersects);
|
||||
EXPECT_TRUE(!res.glances);
|
||||
}
|
||||
|
||||
TEST(RectTest, BoxIntersectionOverlapX) {
|
||||
RectF rect1 = {0, 0, 10, 10};
|
||||
RectF rect2 = {7, 6, 10, 10};
|
||||
auto res = rect1.intersection(rect2);
|
||||
|
||||
EXPECT_TRUE(res.intersects);
|
||||
EXPECT_TRUE(!res.glances);
|
||||
EXPECT_TRUE(fabs(res.overlap[0] + 3) < 0.0001f);
|
||||
EXPECT_TRUE(fabs(res.overlap[1]) < 0.0001f);
|
||||
}
|
||||
|
||||
TEST(RectTest, BoxIntersectionOverlapY) {
|
||||
RectF rect1 = {0, 0, 10, 10};
|
||||
RectF rect2 = {5, 6, 10, 10};
|
||||
auto res = rect1.intersection(rect2);
|
||||
|
||||
EXPECT_TRUE(res.intersects);
|
||||
EXPECT_TRUE(!res.glances);
|
||||
EXPECT_TRUE(fabs(res.overlap[0]) < 0.0001f);
|
||||
EXPECT_TRUE(fabs(res.overlap[1] + 4) < 0.0001f);
|
||||
}
|
||||
|
||||
TEST(RectTest, ContainsPoint) {
|
||||
Box<float, 4> rect1 = {{0, 0, 0, 0}, {10, 10, 10, 10}};
|
||||
Vec4F point1 = {5, 5, 5, 10};
|
||||
Vec4F point2 = {-10, 0, 0, 0};
|
||||
Vec4F point3 = {5, 4, 3, 2};
|
||||
Vec4F point4 = {5, 4, 3, -2};
|
||||
|
||||
EXPECT_TRUE(rect1.contains(point1));
|
||||
EXPECT_FALSE(rect1.contains(point1, false));
|
||||
EXPECT_FALSE(rect1.contains(point2));
|
||||
EXPECT_FALSE(rect1.contains(point2, false));
|
||||
EXPECT_TRUE(rect1.contains(point3));
|
||||
EXPECT_TRUE(rect1.contains(point3, false));
|
||||
EXPECT_FALSE(rect1.contains(point4));
|
||||
EXPECT_FALSE(rect1.contains(point4, false));
|
||||
}
|
||||
|
||||
TEST(RectTest, EdgeIntersection) {
|
||||
RectF rect1 = {10, 10, 20, 20};
|
||||
|
||||
Line2F line0_1 = {{3, 3}, {4, 4}};
|
||||
auto res = rect1.edgeIntersection(line0_1);
|
||||
EXPECT_FALSE(res.intersects);
|
||||
EXPECT_FALSE(res.coincides);
|
||||
EXPECT_FALSE(res.glances);
|
||||
|
||||
Line2F line1_1 = {{9, 12}, {10, 12}};
|
||||
res = rect1.edgeIntersection(line1_1);
|
||||
EXPECT_TRUE(res.intersects);
|
||||
EXPECT_FALSE(res.coincides);
|
||||
EXPECT_TRUE(res.glances);
|
||||
EXPECT_TRUE(fabs(res.point[0] - 10) < 0.0001f);
|
||||
EXPECT_TRUE(fabs(res.point[1] - 12) < 0.0001f);
|
||||
EXPECT_TRUE(fabs(res.t - 1.0f) < 0.0001f);
|
||||
|
||||
Line2F line1_2 = {{9, 12}, {11, 12}};
|
||||
res = rect1.edgeIntersection(line1_2);
|
||||
EXPECT_TRUE(res.intersects);
|
||||
EXPECT_FALSE(res.coincides);
|
||||
EXPECT_FALSE(res.glances);
|
||||
EXPECT_TRUE(fabs(res.point[0] - 10) < 0.0001f);
|
||||
EXPECT_TRUE(fabs(res.point[1] - 12) < 0.0001f);
|
||||
EXPECT_TRUE(fabs(res.t - 0.5f) < 0.0001f);
|
||||
|
||||
Line2F line1_3 = {{10, 12}, {11, 12}};
|
||||
res = rect1.edgeIntersection(line1_3);
|
||||
EXPECT_TRUE(res.intersects);
|
||||
EXPECT_FALSE(res.coincides);
|
||||
EXPECT_FALSE(res.glances);
|
||||
EXPECT_TRUE(fabs(res.point[0] - 10) < 0.0001f);
|
||||
EXPECT_TRUE(fabs(res.point[1] - 12) < 0.0001f);
|
||||
EXPECT_TRUE(fabs(res.t) < 0.0001f);
|
||||
|
||||
Line2F line1_4 = {{10, 12}, {10, 13}};
|
||||
res = rect1.edgeIntersection(line1_4);
|
||||
EXPECT_TRUE(res.intersects);
|
||||
EXPECT_TRUE(res.coincides);
|
||||
EXPECT_TRUE(res.glances);
|
||||
EXPECT_TRUE(fabs(res.point[0] - 10) < 0.0001f);
|
||||
EXPECT_TRUE(fabs(res.point[1] - 12) < 0.0001f);
|
||||
|
||||
Line2F line2_1 = {{10, 10}, {11, 10}};
|
||||
res = rect1.edgeIntersection(line2_1);
|
||||
EXPECT_TRUE(res.intersects);
|
||||
EXPECT_TRUE(res.coincides);
|
||||
EXPECT_TRUE(res.glances);
|
||||
EXPECT_TRUE(fabs(res.point[0] - 10) < 0.0001f);
|
||||
EXPECT_TRUE(fabs(res.point[1] - 10) < 0.0001f);
|
||||
|
||||
Line2F line2_2 = {{15, 9}, {20, 15}};
|
||||
res = rect1.edgeIntersection(line2_2);
|
||||
EXPECT_TRUE(res.intersects);
|
||||
EXPECT_FALSE(res.coincides);
|
||||
EXPECT_FALSE(res.glances);
|
||||
EXPECT_TRUE(fabs(res.point[1] - 10) < 0.0001f);
|
||||
|
||||
Line2F line2_3 = {{15, 9}, {21, 15}};
|
||||
res = rect1.edgeIntersection(line2_3);
|
||||
EXPECT_TRUE(res.intersects);
|
||||
EXPECT_FALSE(res.coincides);
|
||||
EXPECT_FALSE(res.glances);
|
||||
EXPECT_TRUE(fabs(res.point[1] - 10) < 0.0001f);
|
||||
|
||||
Line2F line2_4 = {{15, 10}, {21, 15}};
|
||||
res = rect1.edgeIntersection(line2_4);
|
||||
EXPECT_TRUE(res.intersects);
|
||||
EXPECT_FALSE(res.coincides);
|
||||
EXPECT_FALSE(res.glances);
|
||||
EXPECT_TRUE(fabs(res.point[1] - 10) < 0.0001f);
|
||||
|
||||
Line2F line2_5 = {{15, 11}, {20, 15}};
|
||||
res = rect1.edgeIntersection(line2_5);
|
||||
EXPECT_TRUE(res.intersects);
|
||||
EXPECT_FALSE(res.coincides);
|
||||
EXPECT_FALSE(res.glances);
|
||||
EXPECT_TRUE(fabs(res.point[0] - 20) < 0.0001f);
|
||||
|
||||
Line2F line2_6 = {{9, 9}, {11, 11}};
|
||||
res = rect1.edgeIntersection(line2_6);
|
||||
EXPECT_TRUE(res.intersects);
|
||||
EXPECT_FALSE(res.coincides);
|
||||
EXPECT_FALSE(res.glances);
|
||||
EXPECT_TRUE(fabs(res.point[0] - 10) < 0.0001f);
|
||||
EXPECT_TRUE(fabs(res.point[1] - 10) < 0.0001f);
|
||||
|
||||
Line2F line2_7 = {{9, 11}, {11, 9}};
|
||||
res = rect1.edgeIntersection(line2_7);
|
||||
EXPECT_TRUE(res.intersects);
|
||||
EXPECT_FALSE(res.coincides);
|
||||
EXPECT_TRUE(res.glances);
|
||||
EXPECT_TRUE(fabs(res.point[0] - 10) < 0.0001f);
|
||||
EXPECT_TRUE(fabs(res.point[1] - 10) < 0.0001f);
|
||||
|
||||
Line2F line2_8 = {{10, 10.5}, {10, 10}};
|
||||
res = rect1.edgeIntersection(line2_8);
|
||||
EXPECT_TRUE(res.intersects);
|
||||
EXPECT_TRUE(res.coincides);
|
||||
EXPECT_TRUE(res.glances);
|
||||
EXPECT_TRUE(fabs(res.point[0] - 10) < 0.0001f);
|
||||
EXPECT_TRUE(fabs(res.point[1] - 10.5) < 0.0001f);
|
||||
|
||||
Line2F line2_9 = {{10, 10}, {10, 10.5}};
|
||||
res = rect1.edgeIntersection(line2_9);
|
||||
EXPECT_TRUE(res.intersects);
|
||||
EXPECT_TRUE(res.coincides);
|
||||
EXPECT_TRUE(res.glances);
|
||||
EXPECT_TRUE(fabs(res.point[0] - 10) < 0.0001f);
|
||||
EXPECT_TRUE(fabs(res.point[1] - 10) < 0.0001f);
|
||||
|
||||
Line2F line3_1 = {{10, 10}, {20, 10}};
|
||||
res = rect1.edgeIntersection(line3_1);
|
||||
EXPECT_TRUE(res.intersects);
|
||||
EXPECT_TRUE(res.coincides);
|
||||
EXPECT_TRUE(res.glances);
|
||||
EXPECT_TRUE(fabs(res.point[0] - 10) < 0.0001f);
|
||||
EXPECT_TRUE(fabs(res.point[1] - 10) < 0.0001f);
|
||||
|
||||
Line2F line3_2 = {{9, 10}, {21, 10}};
|
||||
res = rect1.edgeIntersection(line3_2);
|
||||
EXPECT_TRUE(res.intersects);
|
||||
EXPECT_TRUE(res.coincides);
|
||||
EXPECT_TRUE(res.glances);
|
||||
EXPECT_TRUE(fabs(res.point[0] - 10) < 0.0001f);
|
||||
EXPECT_TRUE(fabs(res.point[1] - 10) < 0.0001f);
|
||||
|
||||
Line2F line3_3 = {{9, 8}, {15, 20}};
|
||||
res = rect1.edgeIntersection(line3_3);
|
||||
EXPECT_TRUE(res.intersects);
|
||||
EXPECT_FALSE(res.coincides);
|
||||
EXPECT_FALSE(res.glances);
|
||||
EXPECT_TRUE(fabs(res.point[0] - 10) < 0.0001f);
|
||||
EXPECT_TRUE(fabs(res.point[1] - 10) < 0.0001f);
|
||||
|
||||
Line2F line3_4 = {{9, 8}, {16, 22}};
|
||||
res = rect1.edgeIntersection(line3_4);
|
||||
EXPECT_TRUE(res.intersects);
|
||||
EXPECT_FALSE(res.coincides);
|
||||
EXPECT_FALSE(res.glances);
|
||||
EXPECT_TRUE(fabs(res.point[0] - 10) < 0.0001f);
|
||||
EXPECT_TRUE(fabs(res.point[1] - 10) < 0.0001f);
|
||||
|
||||
Line2F line4_1 = {{9, 9}, {21, 21}};
|
||||
res = rect1.edgeIntersection(line4_1);
|
||||
EXPECT_TRUE(res.intersects);
|
||||
EXPECT_FALSE(res.coincides);
|
||||
EXPECT_FALSE(res.glances);
|
||||
EXPECT_TRUE(fabs(res.point[0] - 10) < 0.0001f);
|
||||
EXPECT_TRUE(fabs(res.point[1] - 10) < 0.0001f);
|
||||
|
||||
Line2F line4_2 = {{9, 21}, {21, 9}};
|
||||
res = rect1.edgeIntersection(line4_2);
|
||||
EXPECT_TRUE(res.intersects);
|
||||
EXPECT_FALSE(res.coincides);
|
||||
EXPECT_FALSE(res.glances);
|
||||
EXPECT_TRUE(fabs(res.point[0] - 10) < 0.0001f);
|
||||
EXPECT_TRUE(fabs(res.point[1] - 20) < 0.0001f);
|
||||
}
|
||||
|
||||
TEST(RectTest, Center) {
|
||||
RectU a(0, 0, 10, 10);
|
||||
|
||||
a.setCenter(Vec2U(5, 5));
|
||||
EXPECT_EQ(a, RectU(0, 0, 10, 10));
|
||||
|
||||
a.setCenter(Vec2U(10, 10));
|
||||
EXPECT_EQ(a, RectU(5, 5, 15, 15));
|
||||
|
||||
a.setCenter(Vec2U(5, 5));
|
||||
EXPECT_EQ(a, RectU(0, 0, 10, 10));
|
||||
}
|
83
source/test/ref_ptr_test.cpp
Normal file
83
source/test/ref_ptr_test.cpp
Normal file
|
@ -0,0 +1,83 @@
|
|||
#include "StarRefPtr.hpp"
|
||||
#include "StarCasting.hpp"
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
using namespace Star;
|
||||
|
||||
int g_test1Count = 0;
|
||||
int g_test2Count = 0;
|
||||
|
||||
struct Base : RefCounter {};
|
||||
|
||||
struct Test1 : Base {
|
||||
Test1() {
|
||||
++g_test1Count;
|
||||
}
|
||||
|
||||
~Test1() {
|
||||
--g_test1Count;
|
||||
}
|
||||
};
|
||||
|
||||
struct Test2 : Base {
|
||||
Test2() {
|
||||
++g_test2Count;
|
||||
}
|
||||
|
||||
~Test2() {
|
||||
--g_test2Count;
|
||||
}
|
||||
};
|
||||
|
||||
TEST(IntrusivePtr, All) {
|
||||
{
|
||||
RefPtr<Base> p1 = make_ref<Test1>();
|
||||
RefPtr<Base> p2 = make_ref<Test2>();
|
||||
|
||||
EXPECT_TRUE(is<Test1>(p1));
|
||||
EXPECT_FALSE(is<Test2>(p1));
|
||||
EXPECT_TRUE(is<Test2>(p2));
|
||||
EXPECT_FALSE(is<Test1>(p2));
|
||||
|
||||
RefPtr<Base> p3 = p1;
|
||||
RefPtr<Base> p4 = p2;
|
||||
|
||||
p3 = p3;
|
||||
swap(p3, p4);
|
||||
|
||||
EXPECT_TRUE(is<Test1>(p4));
|
||||
EXPECT_FALSE(is<Test2>(p4));
|
||||
EXPECT_TRUE(is<Test2>(p3));
|
||||
EXPECT_FALSE(is<Test1>(p3));
|
||||
|
||||
EXPECT_EQ(p3, p2);
|
||||
EXPECT_EQ(p4, p1);
|
||||
|
||||
swap(p3, p4);
|
||||
|
||||
EXPECT_EQ(p3, p1);
|
||||
EXPECT_EQ(p4, p2);
|
||||
|
||||
RefPtr<Base> p5;
|
||||
EXPECT_FALSE(p5);
|
||||
|
||||
EXPECT_NE(p4, p1);
|
||||
EXPECT_NE(p3, p2);
|
||||
EXPECT_NE(p3, p5);
|
||||
|
||||
p5 = p2;
|
||||
p2 = move(p5);
|
||||
EXPECT_TRUE(is<Test2>(p2));
|
||||
|
||||
RefPtr<Test1> p6 = as<Test1>(p1);
|
||||
RefPtr<Test2> p7 = as<Test2>(p2);
|
||||
RefPtr<Test2> p8 = as<Test2>(p1);
|
||||
EXPECT_TRUE((bool)p6);
|
||||
EXPECT_TRUE((bool)p7);
|
||||
EXPECT_FALSE((bool)p8);
|
||||
}
|
||||
|
||||
EXPECT_EQ(0, g_test1Count);
|
||||
EXPECT_EQ(0, g_test2Count);
|
||||
}
|
82
source/test/root_test.cpp
Normal file
82
source/test/root_test.cpp
Normal file
|
@ -0,0 +1,82 @@
|
|||
#include "StarRoot.hpp"
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
using namespace Star;
|
||||
|
||||
TEST(RootTest, All) {
|
||||
auto root = Root::singletonPtr();
|
||||
EXPECT_TRUE(root);
|
||||
|
||||
EXPECT_TRUE((bool)root->assets());
|
||||
EXPECT_TRUE((bool)root->objectDatabase());
|
||||
EXPECT_TRUE((bool)root->plantDatabase());
|
||||
EXPECT_TRUE((bool)root->projectileDatabase());
|
||||
EXPECT_TRUE((bool)root->monsterDatabase());
|
||||
EXPECT_TRUE((bool)root->npcDatabase());
|
||||
EXPECT_TRUE((bool)root->playerFactory());
|
||||
EXPECT_TRUE((bool)root->entityFactory());
|
||||
EXPECT_TRUE((bool)root->nameGenerator());
|
||||
EXPECT_TRUE((bool)root->itemDatabase());
|
||||
EXPECT_TRUE((bool)root->materialDatabase());
|
||||
EXPECT_TRUE((bool)root->terrainDatabase());
|
||||
EXPECT_TRUE((bool)root->biomeDatabase());
|
||||
EXPECT_TRUE((bool)root->liquidsDatabase());
|
||||
EXPECT_TRUE((bool)root->statusEffectDatabase());
|
||||
EXPECT_TRUE((bool)root->damageDatabase());
|
||||
EXPECT_TRUE((bool)root->particleDatabase());
|
||||
EXPECT_TRUE((bool)root->effectSourceDatabase());
|
||||
EXPECT_TRUE((bool)root->functionDatabase());
|
||||
EXPECT_TRUE((bool)root->treasureDatabase());
|
||||
EXPECT_TRUE((bool)root->dungeonDefinitions());
|
||||
EXPECT_TRUE((bool)root->emoteProcessor());
|
||||
EXPECT_TRUE((bool)root->speciesDatabase());
|
||||
EXPECT_TRUE((bool)root->imageMetadataDatabase());
|
||||
EXPECT_TRUE((bool)root->versioningDatabase());
|
||||
EXPECT_TRUE((bool)root->questTemplateDatabase());
|
||||
EXPECT_TRUE((bool)root->aiDatabase());
|
||||
EXPECT_TRUE((bool)root->techDatabase());
|
||||
EXPECT_TRUE((bool)root->codexDatabase());
|
||||
EXPECT_TRUE((bool)root->stagehandDatabase());
|
||||
EXPECT_TRUE((bool)root->behaviorDatabase());
|
||||
EXPECT_TRUE((bool)root->tenantDatabase());
|
||||
EXPECT_TRUE((bool)root->danceDatabase());
|
||||
EXPECT_TRUE((bool)root->spawnTypeDatabase());
|
||||
|
||||
root->reload();
|
||||
|
||||
EXPECT_TRUE((bool)root->assets());
|
||||
EXPECT_TRUE((bool)root->objectDatabase());
|
||||
EXPECT_TRUE((bool)root->plantDatabase());
|
||||
EXPECT_TRUE((bool)root->projectileDatabase());
|
||||
EXPECT_TRUE((bool)root->monsterDatabase());
|
||||
EXPECT_TRUE((bool)root->npcDatabase());
|
||||
EXPECT_TRUE((bool)root->playerFactory());
|
||||
EXPECT_TRUE((bool)root->entityFactory());
|
||||
EXPECT_TRUE((bool)root->nameGenerator());
|
||||
EXPECT_TRUE((bool)root->itemDatabase());
|
||||
EXPECT_TRUE((bool)root->materialDatabase());
|
||||
EXPECT_TRUE((bool)root->terrainDatabase());
|
||||
EXPECT_TRUE((bool)root->biomeDatabase());
|
||||
EXPECT_TRUE((bool)root->liquidsDatabase());
|
||||
EXPECT_TRUE((bool)root->statusEffectDatabase());
|
||||
EXPECT_TRUE((bool)root->damageDatabase());
|
||||
EXPECT_TRUE((bool)root->particleDatabase());
|
||||
EXPECT_TRUE((bool)root->effectSourceDatabase());
|
||||
EXPECT_TRUE((bool)root->functionDatabase());
|
||||
EXPECT_TRUE((bool)root->treasureDatabase());
|
||||
EXPECT_TRUE((bool)root->dungeonDefinitions());
|
||||
EXPECT_TRUE((bool)root->emoteProcessor());
|
||||
EXPECT_TRUE((bool)root->speciesDatabase());
|
||||
EXPECT_TRUE((bool)root->imageMetadataDatabase());
|
||||
EXPECT_TRUE((bool)root->versioningDatabase());
|
||||
EXPECT_TRUE((bool)root->questTemplateDatabase());
|
||||
EXPECT_TRUE((bool)root->aiDatabase());
|
||||
EXPECT_TRUE((bool)root->techDatabase());
|
||||
EXPECT_TRUE((bool)root->codexDatabase());
|
||||
EXPECT_TRUE((bool)root->stagehandDatabase());
|
||||
EXPECT_TRUE((bool)root->behaviorDatabase());
|
||||
EXPECT_TRUE((bool)root->tenantDatabase());
|
||||
EXPECT_TRUE((bool)root->danceDatabase());
|
||||
EXPECT_TRUE((bool)root->spawnTypeDatabase());
|
||||
}
|
28
source/test/serialization_test.cpp
Normal file
28
source/test/serialization_test.cpp
Normal file
|
@ -0,0 +1,28 @@
|
|||
#include "StarDataStreamDevices.hpp"
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
using namespace Star;
|
||||
|
||||
template <typename T>
|
||||
void testMap(T const& map) {
|
||||
auto byteArray = DataStreamBuffer::serializeMapContainer(map);
|
||||
auto mapOut = DataStreamBuffer::deserializeMapContainer<T>(byteArray);
|
||||
EXPECT_EQ(map, mapOut);
|
||||
}
|
||||
|
||||
TEST(DataStreamTest, All) {
|
||||
Map<int, int> map1 = {
|
||||
{1, 2}, {3, 4}, {5, 6},
|
||||
};
|
||||
|
||||
Map<String, int> map2 = {
|
||||
{"asdf", 1}, {"asdf1", 2}, {"omg", 2},
|
||||
};
|
||||
|
||||
Map<String, int> map3 = {};
|
||||
|
||||
testMap(map1);
|
||||
testMap(map2);
|
||||
testMap(map3);
|
||||
}
|
13
source/test/server_test.cpp
Normal file
13
source/test/server_test.cpp
Normal file
|
@ -0,0 +1,13 @@
|
|||
#include "StarUniverseServer.hpp"
|
||||
#include "StarRoot.hpp"
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
using namespace Star;
|
||||
|
||||
TEST(ServerTest, Run) {
|
||||
UniverseServer server(Root::singleton().toStoragePath("universe"));
|
||||
server.start();
|
||||
server.stop();
|
||||
server.join();
|
||||
}
|
146
source/test/sha_test.cpp
Normal file
146
source/test/sha_test.cpp
Normal file
|
@ -0,0 +1,146 @@
|
|||
#include "StarSha256.hpp"
|
||||
#include "StarEncode.hpp"
|
||||
#include "StarPythonic.hpp"
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
using namespace Star;
|
||||
|
||||
TEST(ShaTest, All) {
|
||||
Sha256Hasher hasher;
|
||||
List<ByteArray> computed;
|
||||
List<ByteArray> expected;
|
||||
|
||||
computed.append(sha256("/animations/muzzleflash/bulletmuzzle1/bulletmuzzle1.png"));
|
||||
expected.append(hexDecode("7e4a2cbe3826945d5f6593b347b49285b00ba8471cb4f6edc9b045c2cb3b6ea6"));
|
||||
computed.append(sha256(""));
|
||||
expected.append(hexDecode("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"));
|
||||
computed.append(sha256("The quick brown fox jumped over the lazy dog."));
|
||||
expected.append(hexDecode("68b1282b91de2c054c36629cb8dd447f12f096d3e3c587978dc2248444633483"));
|
||||
computed.append(sha256("The quick brown fox jumped over the lazy dog"));
|
||||
expected.append(hexDecode("7d38b5cd25a2baf85ad3bb5b9311383e671a8a142eb302b324d4a5fba8748c69"));
|
||||
computed.append(sha256("1"));
|
||||
expected.append(hexDecode("6b86b273ff34fce19d6b804eff5a3f5747ada4eaa22f1d49c01e52ddb7875b4b"));
|
||||
computed.append(sha256("22"));
|
||||
expected.append(hexDecode("785f3ec7eb32f30b90cd0fcf3657d388b5ff4297f2f9716ff66e9b69c05ddd09"));
|
||||
computed.append(sha256("333"));
|
||||
expected.append(hexDecode("556d7dc3a115356350f1f9910b1af1ab0e312d4b3e4fc788d2da63668f36d017"));
|
||||
computed.append(sha256("four"));
|
||||
expected.append(hexDecode("04efaf080f5a3e74e1c29d1ca6a48569382cbbcd324e8d59d2b83ef21c039f00"));
|
||||
computed.append(sha256("five5"));
|
||||
expected.append(hexDecode("9caa1a017da4f2a1b836d0b4826549f651f3318bca4569af1d12549a92a3e0b0"));
|
||||
computed.append(sha256("sixsix"));
|
||||
expected.append(hexDecode("aec04bf8cf83291ceed3d47e8601e1989dc29ce76a6399264132de2731e69e55"));
|
||||
computed.append(sha256("seven77"));
|
||||
expected.append(hexDecode("363025b80362011c5f647eb99f029fc95f7df417a1aec169227a4fbb72f17f0f"));
|
||||
computed.append(sha256("eight888"));
|
||||
expected.append(hexDecode("ed13590c2bf3fc9df3a2f8631f117a70b67f6776480b9dd70125d1a4990e39f1"));
|
||||
computed.append(sha256("nine char"));
|
||||
expected.append(hexDecode("eb9d9e1b52a1e31daa806fbaf6db9b81ba1509cb933858af7ac73ba56e7d4c7f"));
|
||||
computed.append(sha256("1010101010"));
|
||||
expected.append(hexDecode("179b1a3e9e319cff0ca6671e07a62dad7d087b0f2a89eef0202612c3cb46baf9"));
|
||||
computed.append(sha256("eleven char"));
|
||||
expected.append(hexDecode("9734f6b41c8669fd1c6b5ad5ed8be0f733571933c7ec1bfe6061cc17944eeb48"));
|
||||
computed.append(sha256("tweleve char"));
|
||||
expected.append(hexDecode("219d8903ddaf708532901ac17f730b1f10a8a2aeb790ceb1e6883a8fa6960fc5"));
|
||||
computed.append(sha256("thirteen char"));
|
||||
expected.append(hexDecode("a7979658ac2830a4b39910c5eb8420b28dbf5a594210b102ff8faf76220c3895"));
|
||||
computed.append(sha256("fourteen chars"));
|
||||
expected.append(hexDecode("6099743911ee19e952125d8534ac0248bc1f83c87303ceef79e7c80e417c248a"));
|
||||
computed.append(sha256("15 char long :)"));
|
||||
expected.append(hexDecode("cc5b9b929c4e57a18c6e35191361a7d7eb825da773c13a006f71c4180acba379"));
|
||||
computed.append(sha256("16 chars long :)"));
|
||||
expected.append(hexDecode("5fb4f6a8d4a7f1918a94d8cf88e9efe893b7ce5dca92c6ff9c531eeb34192911"));
|
||||
computed.append(sha256("seventeenseventee"));
|
||||
expected.append(hexDecode("c86aa346db174a97bc41140be8778a17c305224a279c18c518c1213b5072d5ef"));
|
||||
computed.append(sha256("eighteeneighteen:)"));
|
||||
expected.append(hexDecode("686a19974520da4cde42bdfb930d63a147325c5461573c91adcd20aca3209b35"));
|
||||
computed.append(sha256("19 char winner here"));
|
||||
expected.append(hexDecode("f889777c4c1a74030e7db49e92f1d531d85638c04886c1605c90839b671a1f9e"));
|
||||
computed.append(sha256("this is 20 char long"));
|
||||
expected.append(hexDecode("95e85585cf3ebb0a8adb32f8a73663dc29bbe0e683bc44725af0f37b743f0afc"));
|
||||
computed.append(sha256("this is 21 chars long"));
|
||||
expected.append(hexDecode("64ce72b12e8e9dd32b5263d3ee69b3fb3122a040430b1626143a8ca14541cafc"));
|
||||
computed.append(sha256("22 chars right here..."));
|
||||
expected.append(hexDecode("a3799668feaef5fbaf693d6ae8202499397bd76ff1d0cf128f016bf9de1ab38c"));
|
||||
computed.append(sha256("23 right here right now"));
|
||||
expected.append(hexDecode("86ee2ce2e66d5a58a24a7dc2467d3f06abbceae0e047d8c62915c066c13de7e4"));
|
||||
computed.append(sha256("24 characters right here"));
|
||||
expected.append(hexDecode("b459283fd97ae84d988924773b366e813265adcc7c17cdbef0636f63ced73b22"));
|
||||
computed.append(sha256("count to 25 anyone? noone"));
|
||||
expected.append(hexDecode("abcbd8876f1e988a810c5a85db5643eeecd896f3cace9a5cd5e60af7ec8da858"));
|
||||
computed.append(sha256("26 chars in this string :)"));
|
||||
expected.append(hexDecode("86cbb112bd10aa23349bc49a9debeb91e9797da03937fd3d0ce4999ea3054bed"));
|
||||
computed.append(sha256("give me 27 characters or :("));
|
||||
expected.append(hexDecode("dab1030a321027fdc38df8c7110255c8be4b1477cc5d298de4e26d76b1af5697"));
|
||||
computed.append(sha256("this string is 28 characters"));
|
||||
expected.append(hexDecode("e073ef666b321eb5e8330ba852043896acc33d358a0efe1aac23417c1ad21662"));
|
||||
computed.append(sha256("29 character strings are best"));
|
||||
expected.append(hexDecode("56baa88cdbdc29ac09455f9a6bb318b3e5a6eed338fb54640ffa84e697a22e91"));
|
||||
computed.append(sha256("everyone loves 30 char strings"));
|
||||
expected.append(hexDecode("3459e7ebe0a2ef0be70d4097e4b63f7d7780abd02965faa0786a8b5bae49462e"));
|
||||
computed.append(sha256("31 char strings are cool though"));
|
||||
expected.append(hexDecode("062ce79b19e7cd44e0fe07a9183a123bcbb15740a31236c8bf14376df1ce4951"));
|
||||
computed.append(sha256("I prefer 32 character strings :)"));
|
||||
expected.append(hexDecode("937a2fda340bab7bc6789aebd467f77c22c3e970ba578882c390c77050253774"));
|
||||
computed.append(sha256("33 characters for the win? maybe!"));
|
||||
expected.append(hexDecode("4f7a34b15d5aae90309a9cc25e9f931b5de26834c3f6f69089561610802fc552"));
|
||||
computed.append(sha256("Running out of ideas 34 chars here"));
|
||||
expected.append(hexDecode("f5f4eca8faa433a4b98cc497e406f91e3b6e86d3024ca5a82e5f344db71e9966"));
|
||||
computed.append(sha256("What's 7 * 5? Did I hear 35 chars??"));
|
||||
expected.append(hexDecode("a914e66807993f19fea27b718c78ff28216b4851e79d3375c00e9103d6f7ee29"));
|
||||
computed.append(sha256("36 is a perfect square of a perf num"));
|
||||
expected.append(hexDecode("ebe580788c7c0c8f34f4e098ab4175b17603ee2f8a6a5e082bdb6e89c71ba977"));
|
||||
computed.append(sha256("This is getting a touch tedious... 37"));
|
||||
expected.append(hexDecode("2d596213f63a7a93237e59f692546a3c6f8b4e9f52a282bfb35828817372b3a2"));
|
||||
computed.append(sha256("Coming up with these isn't really hard"));
|
||||
expected.append(hexDecode("9462d6ca0d859423aa262e8e032635f16999cbd78c3d1aa7a7fd5a0247149e13"));
|
||||
computed.append(sha256("I mean, 39 chars isn't too bad to do..."));
|
||||
expected.append(hexDecode("d9166eba746094a585185a0a40ee7f684e5ffa7c1e3c30ee3b4d8a0cbfa2bfb0"));
|
||||
computed.append(sha256("You think it would be based on the limit"));
|
||||
expected.append(hexDecode("079874dc87365598b5feea42b9523d17fd75d791b07e187bbd7c9a4b335680dd"));
|
||||
computed.append(sha256("I mean, they just happen to come up right"));
|
||||
expected.append(hexDecode("6cc9f9a5f4e12aa57ef6ce736634f6ee93aac682e0d3fbb7a55574e06ece0e4f"));
|
||||
computed.append(sha256("I just can't explain that. Like the tides."));
|
||||
expected.append(hexDecode("21a55b18cc186e7c288bbba642d29be75bac0d08c58b2ec9d4956e11099ada66"));
|
||||
computed.append(sha256("I need 43 characters? I get 43 characters."));
|
||||
expected.append(hexDecode("004bae6a27b46c06d64b1df29d2f53f3b409552ec031d961e8886a748b831d06"));
|
||||
computed.append(sha256("Simple as that. Don't know why, just is!! :)"));
|
||||
expected.append(hexDecode("66f03337b1b0d702904bebbfafc1bf6fbcb7e97e8284c3371eeb2d7538e89225"));
|
||||
computed.append(sha256("So I guess even though it's boring, it's fun."));
|
||||
expected.append(hexDecode("7a4036fafac9c46f0373471d50e3ff0ff05ea545060ca43919b726350eb32c45"));
|
||||
computed.append(sha256("I'll stop complaining about my problems though"));
|
||||
expected.append(hexDecode("0803b1553945c6158dee5e4b0a630e29c8c924403ebb03a8f4da34967d4c15ab"));
|
||||
computed.append(sha256("I sound so entitled, whaaa I'm typing words. :("));
|
||||
expected.append(hexDecode("b4df42295e968cd5ad71773fb9a813e8e4e66a41c4c53a16d29e746b879adaed"));
|
||||
computed.append(sha256("It's not really fair for people with real issues"));
|
||||
expected.append(hexDecode("dbed0bcca6b0dae74e78c5106de42936db7d2a4c318b04f233c9e48423e9e691"));
|
||||
computed.append(sha256("49 characters? How am I going to fill that up? :("));
|
||||
expected.append(hexDecode("9bffff6740825a4891584f5dad4d8725cd4918c80d4a9ab6471f6d6fec56ab51"));
|
||||
computed.append(sha256("That is not a real problem. Just me whining. Pish!"));
|
||||
expected.append(hexDecode("ce99e892a27c3e6a99f880003759bf16a7191f3acdbb7d542cd50013b1303ba4"));
|
||||
computed.append(sha256("So 51 characters long string huh? Who knew? Not me"));
|
||||
expected.append(hexDecode("3fc667f8c419beba75f9ec99b342984bb1bda584c463b95dc9d0f006badd8f5e"));
|
||||
computed.append(sha256("And this one is 52. I guess it's alright. I guess..."));
|
||||
expected.append(hexDecode("6ee879d588e96ee70c71c98002913d3a8ecd21c07c57aea48a578e22e8366758"));
|
||||
computed.append(sha256("Someone order a 53 character long string? Anyone? No?"));
|
||||
expected.append(hexDecode("597a49f9a193db96804984b3ca4228f3e267f65f864ade5906949e14ff0f12b7"));
|
||||
computed.append(sha256("Well that's good! All I have is this 54 character one."));
|
||||
expected.append(hexDecode("ec6fde57b976adee10014c58f87403731b4b79f8442eeb606f9697c0deae9b11"));
|
||||
computed.append(sha256("This is the one we were having problems with: 55 chars."));
|
||||
expected.append(hexDecode("6bb7bf41f5f6e625d39e54d58fcc4e458a3af2e30d85eb90199de31fd668a16a"));
|
||||
computed.append(sha256("This is a 56 char string. Just making sure it's good too"));
|
||||
expected.append(hexDecode("446867368ec996d69a822e63454917372c0d0a23d46e6727730b975fcb7a8f25"));
|
||||
computed.append(sha256(
|
||||
"And this is a really long string that I'm testing to make sure that long strings do well in this function "
|
||||
"because who can know if the change I made fucked anything up! Not me says I! I have to test it first before I "
|
||||
"know if it actually works or not. So I'm sitting here typing up an extremely long string just so I know if my "
|
||||
"changed royally cocked anything up beyond recognition. So where are we at now? Only about 4 or 5 blocks? Not "
|
||||
"enough says I! Keep going, ya bildge lickers. Type your fingers off! Bored now reading your mind. Ha! That "
|
||||
"thing was a girl. Did you just read my mind? Yup! How?! Muffin button."));
|
||||
expected.append(hexDecode("9253b707d18e956cde492ae999a67957a48a6720ad76a6cfa2046f697ebeafd3"));
|
||||
|
||||
for (auto i : zip(computed, expected)) {
|
||||
EXPECT_TRUE(std::get<0>(i) == std::get<1>(i));
|
||||
}
|
||||
}
|
85
source/test/shell_parse.cpp
Normal file
85
source/test/shell_parse.cpp
Normal file
|
@ -0,0 +1,85 @@
|
|||
#include "StarShellParser.hpp"
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
TEST(ShellParser, Simple) {
|
||||
Star::ShellParser parser;
|
||||
auto tokenSet1 = parser.tokenize("The first test.");
|
||||
|
||||
EXPECT_EQ(tokenSet1.size(), 3u);
|
||||
EXPECT_EQ(tokenSet1.at(0).token, "The");
|
||||
EXPECT_EQ(tokenSet1.at(1).token, "first");
|
||||
EXPECT_EQ(tokenSet1.at(2).token, "test.");
|
||||
}
|
||||
|
||||
TEST(ShellParser, DirectUnicode) {
|
||||
Star::ShellParser parser;
|
||||
auto tokenSet2 = parser.tokenize("Unicode Symbols: ❤ ☀ ☆ ☂ ☻ ♞ ☯ ☭ ☢ € → ☎ ❄ ♫ ✂ ▷ ✇ ♎ ⇧ ☮ ♻ ⌘ ⌛ ☘");
|
||||
|
||||
EXPECT_EQ(tokenSet2.size(), 26u);
|
||||
EXPECT_EQ(tokenSet2.at(0).token, "Unicode");
|
||||
EXPECT_EQ(tokenSet2.at(1).token, "Symbols:");
|
||||
EXPECT_EQ(tokenSet2.at(10).token, "☢");
|
||||
}
|
||||
|
||||
TEST(ShellParser, SimpleQuotes) {
|
||||
Star::ShellParser parser;
|
||||
auto tokenSet3 = parser.tokenize("\"This is a test\" 'This is another test'");
|
||||
|
||||
EXPECT_EQ(tokenSet3.size(), 2u);
|
||||
EXPECT_EQ(tokenSet3.at(0).token, "This is a test");
|
||||
EXPECT_EQ(tokenSet3.at(1).token, "This is another test");
|
||||
}
|
||||
|
||||
TEST(ShellParser, ComplexQuotes) {
|
||||
Star::ShellParser parser;
|
||||
auto tokenSet4 = parser.tokenize("\"'asdf' 'asdf asdf'\" '\"omg\" omg omg'");
|
||||
|
||||
EXPECT_EQ(tokenSet4.size(), 2u);
|
||||
EXPECT_EQ(tokenSet4.at(0).token, "'asdf' 'asdf asdf'");
|
||||
EXPECT_EQ(tokenSet4.at(1).token, "\"omg\" omg omg");
|
||||
}
|
||||
|
||||
TEST(ShellParser, SpacelessQuotes) {
|
||||
Star::ShellParser parser;
|
||||
auto tokenSet5 = parser.tokenize("\"asdf\"asdf asdf");
|
||||
|
||||
EXPECT_EQ(tokenSet5.size(), 2u);
|
||||
EXPECT_EQ(tokenSet5.at(0).token, "asdfasdf");
|
||||
EXPECT_EQ(tokenSet5.at(1).token, "asdf");
|
||||
|
||||
auto tokenSet6 = parser.tokenize("'asdf'asdf asdf");
|
||||
|
||||
EXPECT_EQ(tokenSet6.size(), 2u);
|
||||
EXPECT_EQ(tokenSet6.at(0).token, "asdfasdf");
|
||||
EXPECT_EQ(tokenSet6.at(1).token, "asdf");
|
||||
}
|
||||
|
||||
TEST(ShellParser, EscapedSpaces) {
|
||||
Star::ShellParser parser;
|
||||
auto tokenSet7 = parser.tokenize("This\\ is\\ a test");
|
||||
|
||||
EXPECT_EQ(tokenSet7.size(), 2u);
|
||||
EXPECT_EQ(tokenSet7.at(0).token, "This is a");
|
||||
EXPECT_EQ(tokenSet7.at(1).token, "test");
|
||||
}
|
||||
|
||||
TEST(ShellParser, EscapedUnicode) {
|
||||
Star::ShellParser parser;
|
||||
auto tokenSet8 = parser.tokenize("This is a unicode codepoint: \\u2603");
|
||||
|
||||
EXPECT_EQ(tokenSet8.size(), 6u);
|
||||
EXPECT_EQ(tokenSet8.at(0).token, "This");
|
||||
EXPECT_EQ(tokenSet8.at(5).token, "☃");
|
||||
|
||||
auto tokenSet9 = parser.tokenize("\\u");
|
||||
|
||||
EXPECT_EQ(tokenSet9.size(), 1u);
|
||||
EXPECT_EQ(tokenSet9.at(0).token, "u");
|
||||
|
||||
auto tokenSet10 = parser.tokenize("\\u2603\\u2603\\u2603 \\u2603");
|
||||
|
||||
EXPECT_EQ(tokenSet10.size(), 2u);
|
||||
EXPECT_EQ(tokenSet10.at(0).token, "☃☃☃");
|
||||
EXPECT_EQ(tokenSet10.at(1).token, "☃");
|
||||
}
|
59
source/test/small_vector_test.cpp
Normal file
59
source/test/small_vector_test.cpp
Normal file
|
@ -0,0 +1,59 @@
|
|||
#include "StarSmallVector.hpp"
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
using namespace Star;
|
||||
|
||||
TEST(SmallVectorTest, InsertErase) {
|
||||
typedef SmallVector<int, 2> SV;
|
||||
SV a = {1, 2, 3, 4};
|
||||
EXPECT_EQ(a.size(), 4u);
|
||||
EXPECT_EQ(a, SV({1, 2, 3, 4}));
|
||||
EXPECT_NE(a, SV({1, 2, 3}));
|
||||
a.insert(a.begin(), 0);
|
||||
a.insert(a.begin(), -1);
|
||||
EXPECT_EQ(a, SV({-1, 0, 1, 2, 3, 4}));
|
||||
a.insert(a.begin(), {-3, -2});
|
||||
EXPECT_EQ(a, SV({-3, -2, -1, 0, 1, 2, 3, 4}));
|
||||
a.erase(a.begin() + 1);
|
||||
EXPECT_EQ(a, SV({-3, -1, 0, 1, 2, 3, 4}));
|
||||
a.erase(a.begin(), a.begin() + 3);
|
||||
EXPECT_EQ(a, SV({1, 2, 3, 4}));
|
||||
a.insert(a.end(), {5, 6, 7, 8});
|
||||
EXPECT_EQ(a, SV({1, 2, 3, 4, 5, 6, 7, 8}));
|
||||
a.erase(a.begin() + 2, a.end() - 2);
|
||||
EXPECT_EQ(a, SV({1, 2, 7, 8}));
|
||||
a.insert(a.begin() + 2, 6);
|
||||
a.insert(a.begin() + 2, 5);
|
||||
a.insert(a.begin() + 2, 4);
|
||||
a.insert(a.begin() + 2, 3);
|
||||
EXPECT_EQ(a, SV({1, 2, 3, 4, 5, 6, 7, 8}));
|
||||
|
||||
SV b(a.begin(), a.end());
|
||||
EXPECT_EQ(b, SV({1, 2, 3, 4, 5, 6, 7, 8}));
|
||||
}
|
||||
|
||||
TEST(SmallVectorTest, Comparators) {
|
||||
typedef SmallVector<int, 3> SV;
|
||||
|
||||
EXPECT_TRUE(SV({1, 2, 3, 4}) < SV({1, 2, 3, 5}));
|
||||
EXPECT_FALSE(SV({1, 2, 3, 4}) < SV({1, 2, 3, 4}));
|
||||
EXPECT_FALSE(SV({1, 2, 3, 4}) < SV({1, 2, 3, 3}));
|
||||
EXPECT_TRUE(SV({1, 2, 3}) < SV({1, 2, 3, 4}));
|
||||
EXPECT_FALSE(SV({1, 2, 3, 4, 5}) < SV({1, 2, 3, 4}));
|
||||
}
|
||||
|
||||
TEST(SmallVectorTest, Destructors) {
|
||||
auto i = make_shared<int>(0);
|
||||
SmallVector<shared_ptr<int>, 1> v;
|
||||
v.push_back(i);
|
||||
v.push_back(i);
|
||||
v.push_back(i);
|
||||
EXPECT_EQ(i.use_count(), 4);
|
||||
v.pop_back();
|
||||
EXPECT_EQ(i.use_count(), 3);
|
||||
v.pop_back();
|
||||
EXPECT_EQ(i.use_count(), 2);
|
||||
v.clear();
|
||||
EXPECT_EQ(i.use_count(), 1);
|
||||
}
|
48
source/test/spawn_test.cpp
Normal file
48
source/test/spawn_test.cpp
Normal file
|
@ -0,0 +1,48 @@
|
|||
#include "StarAssets.hpp"
|
||||
#include "StarCelestialDatabase.hpp"
|
||||
#include "StarRoot.hpp"
|
||||
|
||||
#include "StarTestUniverse.hpp"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
using namespace Star;
|
||||
|
||||
void validateWorld(TestUniverse& testUniverse) {
|
||||
testUniverse.update(100);
|
||||
|
||||
// Just make sure the test world draws something for now, this will grow to
|
||||
// include more than this.
|
||||
EXPECT_GE(testUniverse.currentClientDrawables().size(), 1u) << strf("world: %s", testUniverse.currentPlayerWorld());
|
||||
|
||||
auto assets = Root::singleton().assets();
|
||||
for (auto const& drawable : testUniverse.currentClientDrawables()) {
|
||||
if (drawable.isImage())
|
||||
assets->image(drawable.imagePart().image);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(SpawnTest, RandomCelestialWorld) {
|
||||
CelestialMasterDatabase celestialDatabase;
|
||||
Maybe<CelestialCoordinate> celestialWorld = celestialDatabase.findRandomWorld(10, 50, [&](CelestialCoordinate const& coord) {
|
||||
return celestialDatabase.parameters(coord)->isVisitable();
|
||||
});
|
||||
ASSERT_TRUE((bool)celestialWorld);
|
||||
|
||||
TestUniverse testUniverse(Vec2U(100, 100));
|
||||
WorldId worldId = CelestialWorldId(*celestialWorld);
|
||||
testUniverse.warpPlayer(worldId);
|
||||
EXPECT_EQ(testUniverse.currentPlayerWorld(), worldId);
|
||||
validateWorld(testUniverse);
|
||||
}
|
||||
|
||||
TEST(SpawnTest, RandomInstanceWorld) {
|
||||
auto& root = Root::singleton();
|
||||
StringList instanceWorlds = root.assets()->json("/instance_worlds.config").toObject().keys();
|
||||
ASSERT_GT(instanceWorlds.size(), 0u);
|
||||
WorldId instanceWorld = InstanceWorldId(Random::randFrom(instanceWorlds));
|
||||
|
||||
TestUniverse testUniverse(Vec2U(100, 100));
|
||||
testUniverse.warpPlayer(instanceWorld);
|
||||
EXPECT_EQ(testUniverse.currentPlayerWorld(), instanceWorld);
|
||||
validateWorld(testUniverse);
|
||||
}
|
83
source/test/stat_test.cpp
Normal file
83
source/test/stat_test.cpp
Normal file
|
@ -0,0 +1,83 @@
|
|||
#include "StarStatCollection.hpp"
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
using namespace Star;
|
||||
|
||||
bool withinAmount(float value, float target, float amount) {
|
||||
return fabs(value - target) <= amount;
|
||||
}
|
||||
|
||||
TEST(StatTest, Set) {
|
||||
StatSet stats;
|
||||
|
||||
stats.addStat("MaxHealth", 100.0f);
|
||||
stats.addStat("HealthRegen", 0.0f);
|
||||
stats.addStat("MaxEnergy", 100.0f);
|
||||
stats.addStat("EnergyRegen", 0.0f);
|
||||
|
||||
EXPECT_EQ(stats.statBaseValue("MaxHealth"), 100.0f);
|
||||
EXPECT_EQ(stats.statEffectiveValue("MaxHealth"), 100.0f);
|
||||
EXPECT_EQ(stats.statBaseValue("MaxEnergy"), 100.0f);
|
||||
EXPECT_EQ(stats.statEffectiveValue("MaxEnergy"), 100.0f);
|
||||
|
||||
stats.addResource("Health", String("MaxHealth"), String("HealthRegen"));
|
||||
stats.setResourceValue("Health", 110.0f);
|
||||
stats.addResource("Energy", String("MaxEnergy"), 0.0f);
|
||||
stats.setResourceValue("Energy", 112.0f);
|
||||
stats.addResource("Experience");
|
||||
|
||||
EXPECT_EQ(stats.resourceValue("Health"), 100.0f);
|
||||
EXPECT_EQ(stats.resourceValue("Energy"), 100.0f);
|
||||
EXPECT_EQ(stats.resourceValue("Experience"), 0.0f);
|
||||
|
||||
stats.setStatBaseValue("MaxHealth", 200.0f);
|
||||
EXPECT_TRUE(withinAmount(stats.resourceValue("Health"), 200.0f, 0.0001f));
|
||||
|
||||
stats.modifyResourceValue("Health", -100.0f);
|
||||
EXPECT_TRUE(withinAmount(stats.resourceValue("Health"), 100.0f, 0.0001f));
|
||||
|
||||
stats.modifyResourceValue("Experience", -100.0f);
|
||||
EXPECT_EQ(stats.resourceValue("Experience"), 0.0f);
|
||||
stats.modifyResourceValue("Experience", 1000.0f);
|
||||
EXPECT_TRUE(withinAmount(stats.resourceValue("Experience"), 1000.0f, 0.0001f));
|
||||
|
||||
stats.setStatBaseValue("HealthRegen", 100.0f);
|
||||
stats.update(0.01f);
|
||||
EXPECT_TRUE(withinAmount(stats.resourceValue("Health"), 101.0f, 0.0001f));
|
||||
stats.update(2.0f);
|
||||
EXPECT_TRUE(withinAmount(stats.resourceValue("Health"), 200.0f, 0.0001f));
|
||||
stats.setStatBaseValue("HealthRegen", 0.0f);
|
||||
|
||||
auto id = stats.addStatModifierGroup({StatValueModifier{"HealthRegen", -50.0f}});
|
||||
stats.update(1.0f);
|
||||
EXPECT_TRUE(withinAmount(stats.resourceValue("Health"), 150.0f, 0.0001f));
|
||||
stats.removeStatModifierGroup(id);
|
||||
stats.update(1.0f);
|
||||
EXPECT_TRUE(withinAmount(stats.resourceValue("Health"), 150.0f, 0.0001f));
|
||||
|
||||
id = stats.addStatModifierGroup({StatBaseMultiplier{"MaxHealth", 1.1f},
|
||||
StatEffectiveMultiplier{"MaxHealth", 1.1f},
|
||||
StatEffectiveMultiplier{"MaxHealth", 1.2f},
|
||||
StatValueModifier{"MaxHealth", 50.0}});
|
||||
// 200 (base) + 20 (base perc mod) + 50 (value mod) = 270 ...
|
||||
// * 1.1 (eff perc mod) * 1.2 (eff perc mod) = 356.4
|
||||
EXPECT_TRUE(withinAmount(stats.statEffectiveValue("MaxHealth"), 356.4f, 0.0001f));
|
||||
stats.removeStatModifierGroup(id);
|
||||
|
||||
id = stats.addStatModifierGroup({StatBaseMultiplier{"MaxHealth", 1.5f}, StatBaseMultiplier{"MaxHealth", 1.5f}});
|
||||
// 200 (base) + 100 (base perc mod) + 100 (base perc mod) -- make sure base
|
||||
// perc mods do NOT stack
|
||||
// with each other
|
||||
EXPECT_TRUE(withinAmount(stats.statEffectiveValue("MaxHealth"), 400.0f, 0.0001f));
|
||||
stats.removeStatModifierGroup(id);
|
||||
|
||||
EXPECT_FALSE(stats.isEffectiveStat("TempStat"));
|
||||
EXPECT_TRUE(withinAmount(stats.statEffectiveValue("TempStat"), 0.0f, 0.0001f));
|
||||
auto nextId = stats.addStatModifierGroup({StatValueModifier{"TempStat", 20}});
|
||||
EXPECT_TRUE(withinAmount(stats.statEffectiveValue("TempStat"), 20.0f, 0.0001f));
|
||||
EXPECT_TRUE(stats.isEffectiveStat("TempStat"));
|
||||
stats.removeStatModifierGroup(nextId);
|
||||
EXPECT_TRUE(withinAmount(stats.statEffectiveValue("TempStat"), 0.0f, 0.0001f));
|
||||
EXPECT_FALSE(stats.isEffectiveStat("TempStat"));
|
||||
}
|
56
source/test/static_vector_test.cpp
Normal file
56
source/test/static_vector_test.cpp
Normal file
|
@ -0,0 +1,56 @@
|
|||
#include "StarStaticVector.hpp"
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
using namespace Star;
|
||||
|
||||
TEST(StaticVectorTest, InsertErase) {
|
||||
typedef StaticVector<int, 64> SV;
|
||||
SV a = {1, 2, 3, 4};
|
||||
EXPECT_EQ(a.size(), 4u);
|
||||
EXPECT_EQ(a, SV({1, 2, 3, 4}));
|
||||
EXPECT_NE(a, SV({1, 2, 3}));
|
||||
a.insert(a.begin(), 0);
|
||||
a.insert(a.begin(), -1);
|
||||
EXPECT_EQ(a, SV({-1, 0, 1, 2, 3, 4}));
|
||||
a.insert(a.begin(), {-3, -2});
|
||||
EXPECT_EQ(a, SV({-3, -2, -1, 0, 1, 2, 3, 4}));
|
||||
a.erase(a.begin() + 1);
|
||||
EXPECT_EQ(a, SV({-3, -1, 0, 1, 2, 3, 4}));
|
||||
a.erase(a.begin(), a.begin() + 3);
|
||||
EXPECT_EQ(a, SV({1, 2, 3, 4}));
|
||||
a.insert(a.end(), {5, 6, 7, 8});
|
||||
EXPECT_EQ(a, SV({1, 2, 3, 4, 5, 6, 7, 8}));
|
||||
a.erase(a.begin() + 2, a.end() - 2);
|
||||
EXPECT_EQ(a, SV({1, 2, 7, 8}));
|
||||
a.insert(a.begin() + 2, 6);
|
||||
a.insert(a.begin() + 2, 5);
|
||||
a.insert(a.begin() + 2, 4);
|
||||
a.insert(a.begin() + 2, 3);
|
||||
EXPECT_EQ(a, SV({1, 2, 3, 4, 5, 6, 7, 8}));
|
||||
|
||||
SV b(a.begin(), a.end());
|
||||
EXPECT_EQ(b, SV({1, 2, 3, 4, 5, 6, 7, 8}));
|
||||
}
|
||||
|
||||
TEST(StaticVectorTest, Comparators) {
|
||||
typedef StaticVector<int, 64> SV;
|
||||
|
||||
EXPECT_TRUE(SV({1, 2, 3, 4}) < SV({1, 2, 3, 5}));
|
||||
EXPECT_FALSE(SV({1, 2, 3, 4}) < SV({1, 2, 3, 4}));
|
||||
EXPECT_FALSE(SV({1, 2, 3, 4}) < SV({1, 2, 3, 3}));
|
||||
EXPECT_TRUE(SV({1, 2, 3}) < SV({1, 2, 3, 4}));
|
||||
EXPECT_FALSE(SV({1, 2, 3, 4, 5}) < SV({1, 2, 3, 4}));
|
||||
}
|
||||
|
||||
TEST(StaticVectorTest, SizeLimits) {
|
||||
StaticVector<int, 0> a;
|
||||
StaticVector<int, 1> b;
|
||||
b.push_back(0);
|
||||
StaticVector<int, 2> c;
|
||||
c.resize(2, 0);
|
||||
|
||||
EXPECT_THROW(a.push_back(0), StaticVectorSizeException);
|
||||
EXPECT_THROW(b.push_back(0), StaticVectorSizeException);
|
||||
EXPECT_THROW(c.push_back(0), StaticVectorSizeException);
|
||||
}
|
235
source/test/string_test.cpp
Normal file
235
source/test/string_test.cpp
Normal file
|
@ -0,0 +1,235 @@
|
|||
#include "StarString.hpp"
|
||||
#include "StarFormat.hpp"
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
using namespace Star;
|
||||
|
||||
TEST(StringTest, substr) {
|
||||
EXPECT_EQ(String("barbazbaffoo").substr(4, 4), String("azba"));
|
||||
EXPECT_EQ(String("\0asdf", 5).substr(1, 2), String("as"));
|
||||
}
|
||||
|
||||
TEST(StringTest, find) {
|
||||
EXPECT_EQ(String("xxFooxx").find("Foo"), 2u);
|
||||
EXPECT_EQ(String("xxFooxx").find("foo"), NPos);
|
||||
EXPECT_EQ(String("xxFooxx").find("foo", 0, String::CaseInsensitive), 2u);
|
||||
EXPECT_EQ(String("xxFooxx").find("bar", 0, String::CaseInsensitive), NPos);
|
||||
EXPECT_EQ(String("BAR baz baf BAR").find("bar", 1, String::CaseInsensitive), 12u);
|
||||
EXPECT_EQ(String("\0asdf", 5).find("df"), 3u);
|
||||
}
|
||||
|
||||
TEST(StringTest, split_join) {
|
||||
EXPECT_EQ(String("語語日語語日語語").split("日").join("_"), "語語_語語_語語");
|
||||
EXPECT_EQ(String("日語語日語語日語語日").split("日").join("_"), "_語語_語語_語語_");
|
||||
EXPECT_EQ(String("aabaabaa").split('b').join("_"), "aa_aa_aa");
|
||||
EXPECT_EQ(String("baabaabaab").split('b').join("_"), "_aa_aa_aa_");
|
||||
EXPECT_EQ(String("a").split("bcd"), StringList{"a"});
|
||||
EXPECT_EQ(String("").split("bcd"), StringList{""});
|
||||
EXPECT_EQ(String("\n\raa\n\raa\r\n\r\naa\r\n\r\n\r\n").splitAny("\r\n"), StringList(3, "aa"));
|
||||
EXPECT_EQ(String("\n\r\n\r\r\n\r\n\r\n\r\n\r\n").splitAny("\r\n"), StringList());
|
||||
EXPECT_EQ(String("").splitAny("\r\n"), StringList());
|
||||
EXPECT_EQ(String("xyxFoo").splitAny("x", 1).join("_"), "y_Foo");
|
||||
EXPECT_EQ(String("xyxFoo").rsplitAny("x", 1).join("_"), "xy_Foo");
|
||||
EXPECT_EQ(String("xyxFooxFoox").rsplitAny("x", 1).join("_"), "xyxFoo_Foo");
|
||||
EXPECT_EQ(String("x").rsplitAny("x"), StringList());
|
||||
EXPECT_EQ(String("x").splitAny("x", 1), StringList());
|
||||
EXPECT_EQ(String("").splitAny("x", 1), StringList());
|
||||
EXPECT_EQ(String("asdf\0asdf", 9).splitAny(String("\0", 1)), StringList(2, "asdf"));
|
||||
EXPECT_EQ(String("asdf\0asdf", 9).splitAny("a"), StringList({String("sdf\0", 4), String("sdf")}));
|
||||
}
|
||||
|
||||
TEST(StringTest, replace) {
|
||||
EXPECT_EQ(String("x").replace("cdc", "foo"), "x");
|
||||
EXPECT_EQ(String("cdcdcdc").replace("cdc", "foo"), "foodfoo");
|
||||
EXPECT_EQ(String("").replace("cdc", "foo"), String());
|
||||
EXPECT_EQ(String("xxx").replace("x", "xx"), "xxxxxx");
|
||||
EXPECT_EQ(String("/bin/bash\0aaa:123123:123", 24).replace(String("\0", 1), ""), String("/bin/bashaaa:123123:123"));
|
||||
EXPECT_EQ(String("/bin/bash\0aaa:123123:123", 24).replace(String(), ""), String("/bin/bash\0aaa:123123:123", 24));
|
||||
}
|
||||
|
||||
TEST(StringTest, endsWith) {
|
||||
EXPECT_EQ(String("something.com").endsWith(".com"), true);
|
||||
EXPECT_EQ(String("something.com").endsWith("fsomething.com"), false);
|
||||
EXPECT_EQ(String("something.com").endsWith(""), true);
|
||||
EXPECT_EQ(String("something.com").endsWith("SOMETHING.COMF", String::CaseInsensitive), false);
|
||||
EXPECT_EQ(String("something.com").endsWith('M', String::CaseInsensitive), true);
|
||||
EXPECT_EQ(String("something.com").endsWith('F', String::CaseInsensitive), false);
|
||||
EXPECT_EQ(String("").endsWith('f'), false);
|
||||
EXPECT_EQ(String("something.com\0", 14).endsWith("m"), false);
|
||||
EXPECT_EQ(String("s\0omething.com", 14).endsWith("m"), true);
|
||||
EXPECT_EQ(String("s\0omething.com", 14).endsWith("s"), false);
|
||||
}
|
||||
|
||||
TEST(StringTest, beginsWith) {
|
||||
EXPECT_EQ(String("something.com").beginsWith("something"), true);
|
||||
EXPECT_EQ(String("something.com").beginsWith("something.comf"), false);
|
||||
EXPECT_EQ(String("something.com").beginsWith(""), true);
|
||||
EXPECT_EQ(String("something.com").beginsWith("FSOMETHING.COM", String::CaseInsensitive), false);
|
||||
EXPECT_EQ(String("something.com").beginsWith('S', String::CaseInsensitive), true);
|
||||
EXPECT_EQ(String("something.com").beginsWith('F', String::CaseInsensitive), false);
|
||||
EXPECT_EQ(String("").beginsWith('s'), false);
|
||||
EXPECT_EQ(String("\0something.com", 14).beginsWith(String("\0", 1)), true);
|
||||
}
|
||||
|
||||
TEST(StringTest, trim) {
|
||||
EXPECT_EQ(String("").trim(), String());
|
||||
EXPECT_EQ(String(" ").trim(), String());
|
||||
EXPECT_EQ(String(" \t ").trim(), String());
|
||||
EXPECT_EQ(String(" something ").trim(), "something");
|
||||
EXPECT_EQ(String("something").trim(), "something");
|
||||
EXPECT_EQ(String("\tsomething\t\t \t").trim(), "something");
|
||||
|
||||
EXPECT_EQ(String("thththsomethingthththt").trim("th"), "something");
|
||||
EXPECT_EQ(String("mmmmmmsomethingmmmmmmm").trim("m"), "something");
|
||||
EXPECT_EQ(String("\tsomething\t\t\t").trim("\t"), "something");
|
||||
EXPECT_EQ(String("\0something\0\0\0", 13).trim(String("\0", 1)), "something");
|
||||
}
|
||||
|
||||
TEST(StringTest, extract) {
|
||||
String test("xxxfooxxxfooxxxfooxxxbarxxx");
|
||||
EXPECT_EQ(test.rextract("x"), "bar");
|
||||
EXPECT_EQ(test, "xxxfooxxxfooxxxfoo");
|
||||
EXPECT_EQ(test.rextract("x"), "foo");
|
||||
EXPECT_EQ(test, "xxxfooxxxfoo");
|
||||
EXPECT_EQ(test.rextract("x"), "foo");
|
||||
EXPECT_EQ(test, "xxxfoo");
|
||||
EXPECT_EQ(test.rextract("x"), "foo");
|
||||
EXPECT_EQ(test, "");
|
||||
}
|
||||
|
||||
TEST(StringTest, reverse) {
|
||||
EXPECT_EQ(String("FooBar").reverse(), "raBooF");
|
||||
EXPECT_EQ(String("").reverse(), "");
|
||||
}
|
||||
|
||||
TEST(StringTest, contains) {
|
||||
EXPECT_EQ(String("Foo Bar Foo").contains("foo", String::CaseInsensitive), true);
|
||||
EXPECT_EQ(String("Foo Bar Foo").contains("bar foo", String::CaseInsensitive), true);
|
||||
EXPECT_EQ(String("Foo Bar Foo").contains("foo"), false);
|
||||
EXPECT_EQ(String("Foo Bar Foo").toLower(), String("foo bar foo"));
|
||||
EXPECT_EQ(String("Foo Bar Foo").toUpper(), String("FOO BAR FOO"));
|
||||
}
|
||||
|
||||
TEST(StringTest, format) {
|
||||
EXPECT_EQ(strf("(%s, %s, %s)", 1, "foo", 3.2), "(1, foo, 3.2)");
|
||||
EXPECT_EQ(strf("%s (%s, %s, %s)", String("asdf\0", 5), 1, "foo", 3.2), String("asdf\0 (1, foo, 3.2)", 19));
|
||||
}
|
||||
|
||||
TEST(StringTest, append) {
|
||||
String s = "foo";
|
||||
s.append(String("bar"));
|
||||
EXPECT_EQ(s, "foobar");
|
||||
|
||||
s = "foo";
|
||||
s.append("bar");
|
||||
EXPECT_EQ(s, "foobar");
|
||||
|
||||
s = "foo";
|
||||
s.append('b');
|
||||
EXPECT_EQ(s, "foob");
|
||||
}
|
||||
|
||||
TEST(StringTest, prepend) {
|
||||
String s = "foo";
|
||||
s.prepend(String("bar"));
|
||||
EXPECT_EQ(s, "barfoo");
|
||||
|
||||
s = "foo";
|
||||
s.prepend("bar");
|
||||
EXPECT_EQ(s, "barfoo");
|
||||
|
||||
s = "foo";
|
||||
s.prepend('b');
|
||||
EXPECT_EQ(s, "bfoo");
|
||||
}
|
||||
|
||||
TEST(StringTest, utf8) {
|
||||
char const* utf8String = "This is a [日本語] Unicode String. (日本語)";
|
||||
EXPECT_EQ(utf8Length(utf8String, strlen(utf8String)), 37u);
|
||||
|
||||
String s1 = utf8String;
|
||||
EXPECT_EQ(s1.utf8(), std::string(utf8String));
|
||||
EXPECT_EQ(s1, utf8String);
|
||||
EXPECT_EQ(s1, "This is a [日本語] Unicode String. (日本語)");
|
||||
EXPECT_EQ("This is a [日本語] Unicode String. (日本語)", s1);
|
||||
EXPECT_EQ(s1.size(), 37u);
|
||||
EXPECT_EQ(s1.utf8().size(), 49u);
|
||||
EXPECT_EQ(String(s1.utf8Ptr()), String(utf8String));
|
||||
|
||||
EXPECT_EQ(String("abcdefghijkl").slice(1, 6, 2), String("bdf"));
|
||||
EXPECT_EQ(String("aa").compare("aaaa") < 0, true);
|
||||
EXPECT_EQ(String("bb").compare("aaaa") > 0, true);
|
||||
EXPECT_EQ(String("[日本語]").compare("[日本語]") == 0, true);
|
||||
EXPECT_EQ(String("Aa").compare("aAaA", String::CaseInsensitive) < 0, true);
|
||||
EXPECT_EQ(String("bB").compare("AaAa", String::CaseInsensitive) > 0, true);
|
||||
EXPECT_EQ(String("[日本語]").compare("[日本語]", String::CaseInsensitive) == 0, true);
|
||||
EXPECT_EQ(String("[日本語]").find(']', 1), 4u);
|
||||
|
||||
EXPECT_EQ(String("日本語日本語日本語日本語").substr(6, 3), String("日本語"));
|
||||
String s2 = "日本語日本語日本語日本語";
|
||||
s2.erase(6, 3);
|
||||
EXPECT_EQ(s2, String("日本語日本語日本語"));
|
||||
EXPECT_EQ(String("日本語日本語日本語").reverse(), String("語本日語本日語本日"));
|
||||
|
||||
EXPECT_EQ(String("foo_bar_baz_baf").regexMatch("foo.*baf"), true);
|
||||
EXPECT_EQ(String("日本語日本語日本語").regexMatch("日.*本語"), true);
|
||||
EXPECT_EQ(String("12345678").regexMatch("[[:digit:]]{1,8}"), true);
|
||||
EXPECT_EQ(String("81234567").regexMatch("[[:digit:]]{1,7}"), false);
|
||||
EXPECT_EQ(String("12345678").regexMatch("[[:digit:]]{1,7}"), false);
|
||||
EXPECT_EQ(String("12345678").regexMatch("[[:digit:]]{1,8}", false), true);
|
||||
EXPECT_EQ(String("81234567").regexMatch("[[:digit:]]{1,7}", false), true);
|
||||
EXPECT_EQ(String("12345678").regexMatch("[[:digit:]]{1,7}", false), true);
|
||||
|
||||
EXPECT_EQ(String("𠜎𠜱𠝹𠱓𠱸𠲖𠳏𠳕𠴕𠵼𠵿𠸎𠸏𠹷𠺝𠺢𠻗𠻹𠻺𠼭𠼮𠽌𠾴𠾼𠿪𡁜𡁯𡁵𡁶𡁻𡃁𡃉𡇙𢃇𢞵𢫕𢭃𢯊𢱑𢱕𢳂𢴈𢵌𢵧𢺳𣲷𤓓𤶸𤷪"
|
||||
"𥄫𦉘𦟌𦧲𦧺𧨾𨅝𨈇𨋢𨳊𨳍𨳒𩶘")
|
||||
.size(),
|
||||
62u);
|
||||
}
|
||||
|
||||
TEST(StringTest, tags) {
|
||||
String testString = "<foo>:<bar>";
|
||||
StringMap<String> tags = {{"foo", "hello"}, {"bar", "there"}};
|
||||
|
||||
EXPECT_EQ(testString.replaceTags(tags), "hello:there");
|
||||
}
|
||||
|
||||
TEST(StringTest, CaseInsensitive) {
|
||||
EXPECT_TRUE(CaseInsensitiveStringCompare()("foo", "FOO"));
|
||||
EXPECT_FALSE(CaseInsensitiveStringCompare()("FOO", "foo "));
|
||||
EXPECT_FALSE(CaseInsensitiveStringCompare()("foo ", "FOO"));
|
||||
EXPECT_TRUE(CaseInsensitiveStringCompare()("FOO ", "foo "));
|
||||
|
||||
EXPECT_EQ(CaseInsensitiveStringHash()("foo"), CaseInsensitiveStringHash()("FOO"));
|
||||
EXPECT_NE(CaseInsensitiveStringHash()("FOO"), CaseInsensitiveStringHash()("foo "));
|
||||
EXPECT_NE(CaseInsensitiveStringHash()("foo "), CaseInsensitiveStringHash()("FOO"));
|
||||
EXPECT_EQ(CaseInsensitiveStringHash()("FOO "), CaseInsensitiveStringHash()("foo "));
|
||||
|
||||
// Should only be 2 string allocations, dunno how to test it
|
||||
StringMap<int, CaseInsensitiveStringHash, CaseInsensitiveStringCompare> map;
|
||||
map["One"] = 1;
|
||||
map["Three"] = 3;
|
||||
map["OnE"] = 2;
|
||||
|
||||
EXPECT_TRUE(map.contains("one"));
|
||||
EXPECT_TRUE(map.contains("three"));
|
||||
EXPECT_FALSE(map.contains("two"));
|
||||
|
||||
StringSet keys;
|
||||
for (auto const& p : map)
|
||||
keys.add(p.first);
|
||||
|
||||
StringSet keyCmp = {"One", "Three"};
|
||||
EXPECT_EQ(keys, keyCmp);
|
||||
}
|
||||
|
||||
TEST(StringTest, RegexSearch) {
|
||||
EXPECT_TRUE(String("foo").regexMatch("foo", true, true));
|
||||
EXPECT_FALSE(String("foo bar").regexMatch("foo", true, true));
|
||||
EXPECT_TRUE(String("foo bar").regexMatch("foo", false, true));
|
||||
EXPECT_TRUE(String("foo bar").regexMatch("FOO", false, false));
|
||||
EXPECT_FALSE(String("foo bar").regexMatch("FOO", false, true));
|
||||
EXPECT_TRUE(String("foo bar").regexMatch("^fo*", false, true));
|
||||
EXPECT_FALSE(String("foo bar").regexMatch("^fo*", true, true));
|
||||
EXPECT_TRUE(String("0123456").regexMatch("\\d{0,9}", true, true));
|
||||
}
|
26
source/test/strong_typedef_test.cpp
Normal file
26
source/test/strong_typedef_test.cpp
Normal file
|
@ -0,0 +1,26 @@
|
|||
#include "StarStrongTypedef.hpp"
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
struct BaseType {};
|
||||
strong_typedef(BaseType, DerivedType1);
|
||||
strong_typedef(BaseType, DerivedType2);
|
||||
void func(DerivedType1) {}
|
||||
|
||||
strong_typedef_builtin(int, AlsoInt);
|
||||
|
||||
TEST(StrongTypedefTest, All) {
|
||||
AlsoInt i = AlsoInt(0);
|
||||
++i;
|
||||
i -= 5;
|
||||
EXPECT_EQ(i, -4);
|
||||
|
||||
func((DerivedType1)BaseType());
|
||||
func(DerivedType1());
|
||||
|
||||
// Shouldn't compile! Can't test this automatically!
|
||||
// func(BaseType());
|
||||
// func(DerivedType2());
|
||||
// DerivedType1 dt1 = Basetype();
|
||||
// DerivedType2 dt2 = DerivedType1();
|
||||
}
|
36
source/test/thread_test.cpp
Normal file
36
source/test/thread_test.cpp
Normal file
|
@ -0,0 +1,36 @@
|
|||
#include "StarThread.hpp"
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
using namespace Star;
|
||||
|
||||
TEST(Thread, InvokeErrors) {
|
||||
struct TestException {};
|
||||
|
||||
auto function = Thread::invoke("test", []() {
|
||||
throw TestException();
|
||||
});
|
||||
|
||||
EXPECT_THROW(function.finish(), TestException);
|
||||
}
|
||||
|
||||
TEST(Thread, InvokeReturn) {
|
||||
auto functionRet = Thread::invoke("test", []() {
|
||||
return String("TestValue");
|
||||
});
|
||||
|
||||
EXPECT_EQ(functionRet.finish(), String("TestValue"));
|
||||
EXPECT_THROW(functionRet.finish(), InvalidMaybeAccessException);
|
||||
}
|
||||
|
||||
TEST(Thread, ReadersWriterMutex) {
|
||||
ReadersWriterMutex mutex;
|
||||
ReadLocker rl1(mutex);
|
||||
ReadLocker rl2(mutex);
|
||||
WriteLocker wl(mutex, false);
|
||||
EXPECT_FALSE(wl.tryLock());
|
||||
rl1.unlock();
|
||||
EXPECT_FALSE(wl.tryLock());
|
||||
rl2.unlock();
|
||||
EXPECT_TRUE(wl.tryLock());
|
||||
}
|
99
source/test/tile_array_test.cpp
Normal file
99
source/test/tile_array_test.cpp
Normal file
|
@ -0,0 +1,99 @@
|
|||
#include "StarTileSectorArray.hpp"
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
using namespace Star;
|
||||
|
||||
TEST(TileSectorArrayTest, All) {
|
||||
typedef TileSectorArray<int, 32> TileArray;
|
||||
TileArray tileSectorArray({100, 100}, -1);
|
||||
|
||||
EXPECT_TRUE(tileSectorArray.sectorValid(TileArray::Sector(1, 1)));
|
||||
EXPECT_TRUE(tileSectorArray.sectorValid(TileArray::Sector(3, 3)));
|
||||
EXPECT_FALSE(tileSectorArray.sectorValid(TileArray::Sector(4, 4)));
|
||||
|
||||
EXPECT_TRUE(List<TileArray::Sector>({{0, 0}, {1, 0}}) == tileSectorArray.validSectorsFor(RectI(0, -32, 64, 32)));
|
||||
|
||||
EXPECT_TRUE(TileArray::Sector(0, 0) == tileSectorArray.sectorFor({0, 0}));
|
||||
EXPECT_TRUE(TileArray::Sector(3, 1) == tileSectorArray.sectorFor({-1, 33}));
|
||||
EXPECT_TRUE(-1 == tileSectorArray.tile({-1, -1}));
|
||||
EXPECT_TRUE(nullptr == tileSectorArray.modifyTile({-1, -1}));
|
||||
EXPECT_TRUE(RectI(32, 32, 64, 64) == tileSectorArray.sectorRegion({1, 1}));
|
||||
EXPECT_TRUE(TileArray::Sector(0, 3) == tileSectorArray.adjacentSector({3, 3}, {1, 0}));
|
||||
|
||||
tileSectorArray.loadSector({0, 0}, make_unique<TileArray::Array>(1));
|
||||
tileSectorArray.loadSector({1, 0}, make_unique<TileArray::Array>(1));
|
||||
tileSectorArray.loadSector({2, 0}, make_unique<TileArray::Array>(1));
|
||||
tileSectorArray.loadSector({3, 0}, make_unique<TileArray::Array>(1));
|
||||
tileSectorArray.loadSector({0, 1}, make_unique<TileArray::Array>(2));
|
||||
tileSectorArray.loadSector({1, 1}, make_unique<TileArray::Array>(2));
|
||||
tileSectorArray.loadSector({2, 1}, make_unique<TileArray::Array>(2));
|
||||
tileSectorArray.loadSector({3, 1}, make_unique<TileArray::Array>(2));
|
||||
|
||||
Set<Vec2I> found;
|
||||
tileSectorArray.tileEach(RectI(-2, 0, 3, 1),
|
||||
[&found](Vec2I const& pos, int tile) {
|
||||
found.add(pos);
|
||||
EXPECT_TRUE(pos[0] >= -2 && pos[0] < 3);
|
||||
EXPECT_TRUE(pos[1] == 0);
|
||||
EXPECT_EQ(1, tile);
|
||||
});
|
||||
EXPECT_TRUE(found.contains(Vec2I(0, 0)));
|
||||
EXPECT_TRUE(found.contains(Vec2I(-1, 0)));
|
||||
EXPECT_TRUE(found.contains(Vec2I(-2, 0)));
|
||||
EXPECT_TRUE(found.contains(Vec2I(1, 0)));
|
||||
|
||||
tileSectorArray.tileEach(RectI(-10, 0, -1, 1),
|
||||
[](Vec2I const& pos, int tile) {
|
||||
EXPECT_TRUE(pos[0] >= -10 && pos[0] < -1);
|
||||
EXPECT_EQ(1, tile);
|
||||
});
|
||||
|
||||
tileSectorArray.tileEach(RectI(-10, -1, -1, 0),
|
||||
[](Vec2I const& pos, int tile) {
|
||||
EXPECT_TRUE(pos[0] >= -10 && pos[0] < -1);
|
||||
EXPECT_TRUE(pos[1] == -1);
|
||||
EXPECT_EQ(-1, tile);
|
||||
});
|
||||
|
||||
found.clear();
|
||||
tileSectorArray.tileEach(RectI(110, 101, 120, 102),
|
||||
[&found](Vec2I const& pos, int tile) {
|
||||
found.add(pos);
|
||||
EXPECT_TRUE(pos[0] >= 110 && pos[0] < 120);
|
||||
EXPECT_TRUE(pos[1] == 101);
|
||||
EXPECT_EQ(-1, tile);
|
||||
});
|
||||
EXPECT_TRUE(found.contains(Vec2I(110, 101)));
|
||||
EXPECT_TRUE(found.contains(Vec2I(119, 101)));
|
||||
|
||||
auto res1 = tileSectorArray.tileEachResult(RectI(110, 110, 120, 120),
|
||||
[](Vec2I const& pos, int tile) -> int {
|
||||
return (pos[0] >= 110 && pos[0] < 120 && pos[1] >= 110 && pos[1] < 120 && tile == -1) ? 1 : 0;
|
||||
});
|
||||
MultiArray<int, 2> res1comp({10, 10}, 1);
|
||||
|
||||
EXPECT_TRUE(res1.size() == res1comp.size());
|
||||
res1.forEach([](Array2S const&, int elem) { EXPECT_TRUE(elem == 1); });
|
||||
|
||||
auto res2 = tileSectorArray.tileEachResult(RectI(32, 32, 64, 64),
|
||||
[](Vec2I const& pos, int tile) -> int {
|
||||
return (pos[0] >= 32 && pos[0] < 64 && pos[1] >= 32 && pos[1] < 64 && tile == 2) ? 1 : 0;
|
||||
});
|
||||
MultiArray<int, 2> res2comp({32, 32}, 1);
|
||||
|
||||
EXPECT_TRUE(res2.size() == res2comp.size());
|
||||
res2.forEach([](Array2S const&, int elem) { EXPECT_TRUE(elem == 1); });
|
||||
|
||||
auto res3 = tileSectorArray.tileEachResult(RectI(-10, -10, 1, 1),
|
||||
[](Vec2I const& pos, int tile) -> int {
|
||||
if (pos[1] < 0)
|
||||
return tile == -1;
|
||||
else
|
||||
return tile == 1;
|
||||
});
|
||||
MultiArray<int, 2> res3comp({11, 11}, 1);
|
||||
|
||||
EXPECT_TRUE(res3.size() == res3comp.size());
|
||||
res3.forEach([](Array2S const&, int elem) { EXPECT_TRUE(elem == 1); });
|
||||
}
|
152
source/test/universe_connection_test.cpp
Normal file
152
source/test/universe_connection_test.cpp
Normal file
|
@ -0,0 +1,152 @@
|
|||
#include "StarUniverseConnection.hpp"
|
||||
#include "StarTcp.hpp"
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
using namespace Star;
|
||||
|
||||
unsigned const PacketCount = 20;
|
||||
uint16_t const ServerPort = 55555;
|
||||
|
||||
unsigned const NumLocalASyncConnections = 5;
|
||||
unsigned const NumRemoteASyncConnections = 5;
|
||||
unsigned const ASyncSleepMillis = 5;
|
||||
|
||||
unsigned const NumLocalSyncConnections = 5;
|
||||
unsigned const NumRemoteSyncConnections = 5;
|
||||
unsigned const SyncWaitMillis = 10000;
|
||||
|
||||
class ASyncClientThread : public Thread {
|
||||
public:
|
||||
ASyncClientThread(UniverseConnection conn)
|
||||
: Thread("UniverseConnectionTestClientThread"), m_connection(move(conn)) {
|
||||
start();
|
||||
}
|
||||
|
||||
virtual void run() {
|
||||
try {
|
||||
unsigned read = 0;
|
||||
unsigned written = 0;
|
||||
while (read < PacketCount || written < PacketCount) {
|
||||
m_connection.receive();
|
||||
if (read < PacketCount) {
|
||||
if (auto packet = m_connection.pullSingle()) {
|
||||
EXPECT_TRUE(convert<ProtocolRequestPacket>(packet)->requestProtocolVersion == read);
|
||||
++read;
|
||||
}
|
||||
}
|
||||
|
||||
if (written < PacketCount) {
|
||||
m_connection.push({make_shared<ProtocolRequestPacket>(written)});
|
||||
++written;
|
||||
}
|
||||
m_connection.send();
|
||||
|
||||
Thread::sleep(ASyncSleepMillis);
|
||||
|
||||
if (!m_connection.isOpen())
|
||||
break;
|
||||
}
|
||||
|
||||
EXPECT_EQ(PacketCount, read);
|
||||
EXPECT_EQ(PacketCount, written);
|
||||
m_connection.close();
|
||||
EXPECT_TRUE(m_connection.pull().empty());
|
||||
} catch (std::exception const& e) {
|
||||
ADD_FAILURE() << "Exception: " << outputException(e, true);
|
||||
} catch (...) {
|
||||
ADD_FAILURE();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
UniverseConnection m_connection;
|
||||
};
|
||||
|
||||
class SyncClientThread : public Thread {
|
||||
public:
|
||||
SyncClientThread(UniverseConnection conn)
|
||||
: Thread("UniverseConnectionTestClientThread"), m_connection(move(conn)) {
|
||||
start();
|
||||
}
|
||||
|
||||
virtual void run() {
|
||||
try {
|
||||
for (unsigned i = 0; i < PacketCount; ++i) {
|
||||
m_connection.pushSingle(make_shared<ProtocolRequestPacket>(i));
|
||||
EXPECT_TRUE(m_connection.sendAll(SyncWaitMillis));
|
||||
EXPECT_TRUE(m_connection.receiveAny(SyncWaitMillis));
|
||||
EXPECT_EQ(convert<ProtocolRequestPacket>(m_connection.pullSingle())->requestProtocolVersion, i);
|
||||
|
||||
if (!m_connection.isOpen())
|
||||
break;
|
||||
}
|
||||
|
||||
m_connection.close();
|
||||
EXPECT_TRUE(m_connection.pull().empty());
|
||||
} catch (std::exception const& e) {
|
||||
ADD_FAILURE() << "Exception: " << outputException(e, true);
|
||||
} catch (...) {
|
||||
ADD_FAILURE();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
UniverseConnection m_connection;
|
||||
};
|
||||
|
||||
TEST(UniverseConnections, All) {
|
||||
UniverseConnectionServer server([](UniverseConnectionServer* server, ConnectionId clientId, List<PacketPtr> packets) {
|
||||
server->sendPackets(clientId, packets);
|
||||
});
|
||||
|
||||
ConnectionId clientId = ServerConnectionId;
|
||||
TcpServer tcpServer(HostAddressWithPort(HostAddress::localhost(), ServerPort));
|
||||
tcpServer.setAcceptCallback([&server, &clientId](TcpSocketPtr socket) {
|
||||
socket->setNonBlocking(true);
|
||||
auto conn = UniverseConnection(TcpPacketSocket::open(move(socket)));
|
||||
server.addConnection(++clientId, move(conn));
|
||||
});
|
||||
|
||||
LinkedList<ASyncClientThread> localASyncClients;
|
||||
for (unsigned i = 0; i < NumLocalASyncConnections; ++i) {
|
||||
auto pair = LocalPacketSocket::openPair();
|
||||
server.addConnection(++clientId, UniverseConnection(move(pair.first)));
|
||||
localASyncClients.emplaceAppend(UniverseConnection(move(pair.second)));
|
||||
}
|
||||
|
||||
LinkedList<SyncClientThread> localSyncClients;
|
||||
for (unsigned i = 0; i < NumLocalSyncConnections; ++i) {
|
||||
auto pair = LocalPacketSocket::openPair();
|
||||
server.addConnection(++clientId, UniverseConnection(move(pair.first)));
|
||||
localSyncClients.emplaceAppend(UniverseConnection(move(pair.second)));
|
||||
}
|
||||
|
||||
LinkedList<ASyncClientThread> remoteASyncClients;
|
||||
for (unsigned i = 0; i < NumRemoteASyncConnections; ++i) {
|
||||
auto socket = TcpSocket::connectTo({HostAddress::localhost(), ServerPort});
|
||||
socket->setNonBlocking(true);
|
||||
remoteASyncClients.emplaceAppend(UniverseConnection(TcpPacketSocket::open(move(socket))));
|
||||
}
|
||||
|
||||
LinkedList<SyncClientThread> remoteSyncClients;
|
||||
for (unsigned i = 0; i < NumRemoteSyncConnections; ++i) {
|
||||
auto socket = TcpSocket::connectTo({HostAddress::localhost(), ServerPort});
|
||||
socket->setNonBlocking(true);
|
||||
remoteSyncClients.emplaceAppend(UniverseConnection(TcpPacketSocket::open(move(socket))));
|
||||
}
|
||||
|
||||
for (auto& c : localASyncClients)
|
||||
c.join();
|
||||
|
||||
for (auto& c : remoteASyncClients)
|
||||
c.join();
|
||||
|
||||
for (auto& c : localSyncClients)
|
||||
c.join();
|
||||
|
||||
for (auto& c : remoteSyncClients)
|
||||
c.join();
|
||||
|
||||
server.removeAllConnections();
|
||||
}
|
68
source/test/variant_test.cpp
Normal file
68
source/test/variant_test.cpp
Normal file
|
@ -0,0 +1,68 @@
|
|||
#include "StarVariant.hpp"
|
||||
#include "StarMaybe.hpp"
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
using namespace Star;
|
||||
|
||||
TEST(VariantTest, All) {
|
||||
struct VariantTester {
|
||||
shared_ptr<int> intptr;
|
||||
};
|
||||
|
||||
MVariant<int, double, char, VariantTester> a, b;
|
||||
EXPECT_EQ(a.typeIndex(), 0);
|
||||
a = 'c';
|
||||
EXPECT_EQ(a.typeIndex(), 3u);
|
||||
EXPECT_TRUE(a.is<char>());
|
||||
a.makeType(1);
|
||||
EXPECT_EQ(a.get<int>(), 0);
|
||||
EXPECT_TRUE(a.is<int>());
|
||||
a = b;
|
||||
EXPECT_TRUE(a.empty());
|
||||
|
||||
shared_ptr<int> intptr = make_shared<int>(42);
|
||||
a = VariantTester{intptr};
|
||||
b = VariantTester{intptr};
|
||||
a = b;
|
||||
a = a;
|
||||
b = move(a);
|
||||
a = move(b);
|
||||
EXPECT_EQ(intptr.use_count(), 2);
|
||||
a.reset();
|
||||
EXPECT_EQ(intptr.use_count(), 1);
|
||||
|
||||
Variant<int, double, char> v(1.0);
|
||||
MVariant<int, double, char> mv(v);
|
||||
EXPECT_EQ(mv, 1.0);
|
||||
v = 2;
|
||||
mv = v;
|
||||
EXPECT_EQ(mv, 2);
|
||||
mv = '3';
|
||||
v = mv.takeValue();
|
||||
EXPECT_EQ(v, '3');
|
||||
EXPECT_TRUE(mv.empty());
|
||||
}
|
||||
|
||||
TEST(MaybeTest, All) {
|
||||
struct MaybeTester {
|
||||
shared_ptr<int> intptr;
|
||||
};
|
||||
|
||||
Maybe<MaybeTester> a, b;
|
||||
|
||||
EXPECT_FALSE(a.isValid());
|
||||
|
||||
shared_ptr<int> intptr = make_shared<int>(42);
|
||||
a = MaybeTester{intptr};
|
||||
b = MaybeTester{intptr};
|
||||
EXPECT_TRUE(a.isValid());
|
||||
a = b;
|
||||
a = a;
|
||||
b = move(a);
|
||||
a = move(b);
|
||||
EXPECT_EQ(intptr.use_count(), 2);
|
||||
a = {};
|
||||
EXPECT_FALSE(a.isValid());
|
||||
EXPECT_EQ(intptr.use_count(), 1);
|
||||
}
|
30
source/test/vlq_test.cpp
Normal file
30
source/test/vlq_test.cpp
Normal file
|
@ -0,0 +1,30 @@
|
|||
#include "StarVlqEncoding.hpp"
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
using namespace Star;
|
||||
|
||||
TEST(VlqTest, All) {
|
||||
char buffer[10];
|
||||
int64_t result;
|
||||
|
||||
EXPECT_EQ(writeVlqI(-1, buffer), 1u);
|
||||
EXPECT_EQ(readVlqI(result, buffer), 1u);
|
||||
EXPECT_EQ(result, -1);
|
||||
|
||||
EXPECT_EQ(writeVlqI(-65, buffer), 2u);
|
||||
EXPECT_EQ(readVlqI(result, buffer), 2u);
|
||||
EXPECT_EQ(result, -65);
|
||||
|
||||
EXPECT_EQ(writeVlqI(-64, buffer), 1u);
|
||||
EXPECT_EQ(readVlqI(result, buffer), 1u);
|
||||
EXPECT_EQ(result, -64);
|
||||
|
||||
EXPECT_EQ(writeVlqI((int64_t)1 << 63, buffer), 10u);
|
||||
EXPECT_EQ(readVlqI(result, buffer), 10u);
|
||||
EXPECT_EQ(result, (int64_t)1 << 63);
|
||||
|
||||
EXPECT_EQ(writeVlqI(0, buffer), 1u);
|
||||
EXPECT_EQ(readVlqI(result, buffer), 1u);
|
||||
EXPECT_EQ(result, 0);
|
||||
}
|
34
source/test/worker_pool_test.cpp
Normal file
34
source/test/worker_pool_test.cpp
Normal file
|
@ -0,0 +1,34 @@
|
|||
#include "StarWorkerPool.hpp"
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
using namespace Star;
|
||||
|
||||
TEST(WorkerPoolTest, All) {
|
||||
int counter = 0;
|
||||
Mutex counterMutex;
|
||||
|
||||
auto incCounter = [&counter, &counterMutex]() {
|
||||
Thread::sleep(100);
|
||||
MutexLocker locker(counterMutex);
|
||||
counter += 1;
|
||||
};
|
||||
|
||||
Deque<WorkerPoolHandle> handles;
|
||||
|
||||
WorkerPool workerPool("WorkerPoolTest");
|
||||
for (size_t i = 0; i < 10; ++i)
|
||||
handles.append(workerPool.addWork(incCounter));
|
||||
|
||||
workerPool.start(10);
|
||||
|
||||
for (size_t i = 0; i < 90; ++i)
|
||||
handles.append(workerPool.addWork(incCounter));
|
||||
|
||||
while (handles.size() > 20)
|
||||
handles.takeFirst().finish();
|
||||
|
||||
workerPool.finish();
|
||||
|
||||
EXPECT_EQ(counter, 100);
|
||||
}
|
44
source/test/world_geometry_test.cpp
Normal file
44
source/test/world_geometry_test.cpp
Normal file
|
@ -0,0 +1,44 @@
|
|||
#include "StarPoly.hpp"
|
||||
#include "StarWorldGeometry.hpp"
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
using namespace Star;
|
||||
|
||||
TEST(WorldGeometryTest, All) {
|
||||
WorldGeometry geom = WorldGeometry(Vec2U{8192, 3072});
|
||||
|
||||
PolyF shapeA = {
|
||||
{19.9954f, 1603.62f}, {21.9954f, 1602.62f}, {21.9954f, 1607.62f}, {19.9954f, 1606.62f}, {19.9954f, 1605.12f}};
|
||||
Line2F lineA = {{20.9575f, 1605.13f}, {21.7853f, 1605.03f}};
|
||||
|
||||
EXPECT_TRUE(geom.lineIntersectsPoly(lineA, shapeA));
|
||||
|
||||
PolyF shapeA2 = {{0.9954f, 1.62f}, {2.9954f, 0.62f}, {2.9954f, 5.62f}, {0.9954f, 4.62f}, {0.9954f, 3.12f}};
|
||||
Line2F lineA2 = {{1.9575f, 3.13f}, {2.7853f, 3.03f}};
|
||||
|
||||
EXPECT_TRUE(geom.lineIntersectsPoly(lineA2, shapeA2));
|
||||
|
||||
PolyF shapeA3 = {{-10.0f, -10.0f}, {10, -10.0f}, {10.0f, 10.0f}, {-10.0f, 10.0f}}; //, {-10, 3.12};
|
||||
Line2F lineA3 = {{1.9575f, 3.13f}, {2.7853f, 3.03f}};
|
||||
|
||||
EXPECT_TRUE(geom.lineIntersectsPoly(lineA3, shapeA3));
|
||||
|
||||
RectF shapeA3R = RectF::withSize({-10, -10}, {20, 20});
|
||||
|
||||
EXPECT_TRUE(geom.lineIntersectsRect(lineA3, shapeA3R));
|
||||
|
||||
PolyF shapeB = {
|
||||
{20.5608f, 1603.62f}, {22.5608f, 1602.62f}, {22.5608f, 1607.62f}, {20.5608f, 1606.62f}, {20.5608f, 1605.12f}};
|
||||
Line2F lineB = {{21.7893f, 1605.07f}, {22.6173f, 1604.98f}};
|
||||
|
||||
EXPECT_TRUE(geom.lineIntersectsPoly(lineB, shapeB));
|
||||
|
||||
PolyF shapeC = {{-10.0f, -10.0f}, {10.0f, -10.0f}, {10.0f, 10.0f}, {-10.0f, 10.0f}};
|
||||
PolyF shapeC2 = {{-1.0f, -1.0f}, {1.0f, -1.0f}, {1.0f, 1.0f}, {-1.0f, 1.0f}};
|
||||
|
||||
EXPECT_TRUE(geom.polyIntersectsPoly(shapeC2, shapeC));
|
||||
EXPECT_TRUE(geom.polyIntersectsPoly(shapeC, shapeC2));
|
||||
EXPECT_TRUE(geom.polyContains(shapeC, Vec2F(0, 0)));
|
||||
EXPECT_TRUE(fabs(geom.polyOverlapArea(shapeC, shapeC2) - 4) < 0.0001f);
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue