This commit is contained in:
Aria 2025-03-21 22:23:30 +11:00
commit 9c94d113d3
Signed by untrusted user who does not match committer: aria
GPG key ID: 19AB7AA462B8AB3B
10260 changed files with 1237388 additions and 0 deletions

View 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)

View 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;
}
}

View 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;
};
}

View 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);
}

View 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");
}

View 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);
}

View 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});
}

View 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
View 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);
}

View 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);
}

View 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());
}

View 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);
}

View 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}));
}

View 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;
}

View 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
View 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
}

View 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());
}

View 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]");
}

View 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);
}

View 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();
}

File diff suppressed because it is too large Load diff

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
View 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);
}

View 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
View 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
View 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
View 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);
}

View 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
View 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
View 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);
}

View 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);
}

File diff suppressed because it is too large Load diff

View 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}));
}

View 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}));
}

View 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
View 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
View 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
View 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));
}

View 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
View 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());
}

View 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);
}

View 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
View 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));
}
}

View 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, "");
}

View 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);
}

View 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
View 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"));
}

View 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
View 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));
}

View 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();
}

View 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());
}

View 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); });
}

View 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();
}

View 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
View 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);
}

View 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);
}

View 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);
}