Changeset View
Changeset View
Standalone View
Standalone View
source/ps/tests/test_Future.h
/* Copyright (C) 2021 Wildfire Games. | /* Copyright (C) 2021 Wildfire Games. | ||||
Lint: Inaccurate Copyright Year: Inaccurate Copyright Year | |||||
* This file is part of 0 A.D. | * This file is part of 0 A.D. | ||||
* | * | ||||
* 0 A.D. is free software: you can redistribute it and/or modify | * 0 A.D. is free software: you can redistribute it and/or modify | ||||
* it under the terms of the GNU General Public License as published by | * it under the terms of the GNU General Public License as published by | ||||
* the Free Software Foundation, either version 2 of the License, or | * the Free Software Foundation, either version 2 of the License, or | ||||
* (at your option) any later version. | * (at your option) any later version. | ||||
* | * | ||||
* 0 A.D. is distributed in the hope that it will be useful, | * 0 A.D. is distributed in the hope that it will be useful, | ||||
Show All 14 Lines | |||||
class TestFuture : public CxxTest::TestSuite | class TestFuture : public CxxTest::TestSuite | ||||
{ | { | ||||
public: | public: | ||||
void test_future_basic() | void test_future_basic() | ||||
{ | { | ||||
int counter = 0; | int counter = 0; | ||||
{ | { | ||||
Future<void> noret; | Future noret([&counter]() { counter++; }); | ||||
std::function<void()> task = noret.Wrap([&counter]() mutable { counter++; }); | std::function<void()> task = noret.GetTask(); | ||||
task(); | task(); | ||||
TS_ASSERT_EQUALS(counter, 1); | TS_ASSERT_EQUALS(counter, 1); | ||||
} | } | ||||
{ | { | ||||
Future<void> noret; | Future noret([&counter]() { counter++; }); | ||||
{ | { | ||||
std::function<void()> task = noret.Wrap([&counter]() mutable { counter++; }); | std::function<void()> task = noret.GetTask(); | ||||
// Auto-cancels the task. | // Auto-cancels the task. | ||||
} | } | ||||
} | } | ||||
TS_ASSERT_EQUALS(counter, 1); | TS_ASSERT_EQUALS(counter, 1); | ||||
} | } | ||||
void test_future_return() | void test_future_return() | ||||
{ | { | ||||
{ | { | ||||
Future<int> future; | Future future([]() { return 1; }); | ||||
std::function<void()> task = future.Wrap([]() { return 1; }); | std::function<void()> task = future.GetTask(); | ||||
task(); | task(); | ||||
TS_ASSERT_EQUALS(future.Get(), 1); | TS_ASSERT_EQUALS(future.Get(), 1); | ||||
} | } | ||||
// Convertible type. | // Convertible type. | ||||
{ | { | ||||
Future<int> future; | Future future([]() -> u8 { return 1; }); | ||||
std::function<void()> task = future.Wrap([]() -> u8 { return 1; }); | std::function<void()> task = future.GetTask(); | ||||
task(); | task(); | ||||
TS_ASSERT_EQUALS(future.Get(), 1); | TS_ASSERT_EQUALS(future.Get(), 1); | ||||
} | } | ||||
static int destroyed = 0; | static int destroyed = 0; | ||||
// No trivial constructor or destructor. Also not copiable. | // No trivial constructor or destructor. Also not copiable. | ||||
struct NonDef | struct NonDef | ||||
{ | { | ||||
NonDef() = delete; | NonDef() = delete; | ||||
NonDef(int input) : value(input) {}; | NonDef(int input) : value(input) {}; | ||||
NonDef(const NonDef&) = delete; | NonDef(const NonDef&) = delete; | ||||
NonDef(NonDef&& o) | NonDef(NonDef&& o) | ||||
{ | { | ||||
value = o.value; | value = o.value; | ||||
o.value = 0; | o.value = 0; | ||||
} | } | ||||
~NonDef() { if (value != 0) destroyed++; } | ~NonDef() { if (value != 0) destroyed++; } | ||||
int value = 0; | int value = 0; | ||||
}; | }; | ||||
TS_ASSERT_EQUALS(destroyed, 0); | TS_ASSERT_EQUALS(destroyed, 0); | ||||
{ | { | ||||
Future<NonDef> future; | Future future([]() -> NonDef { return 1; }); | ||||
std::function<void()> task = future.Wrap([]() { return 1; }); | std::function<void()> task = future.GetTask(); | ||||
task(); | task(); | ||||
TS_ASSERT_EQUALS(future.Get().value, 1); | TS_ASSERT_EQUALS(future.Get().value, 1); | ||||
} | } | ||||
TS_ASSERT_EQUALS(destroyed, 1); | TS_ASSERT_EQUALS(destroyed, 1); | ||||
{ | { | ||||
Future<NonDef> future; | Future future([]() -> NonDef { return 1; }); | ||||
std::function<void()> task = future.Wrap([]() { return 1; }); | std::function<void()> task = future.GetTask(); | ||||
} | } | ||||
TS_ASSERT_EQUALS(destroyed, 1); | TS_ASSERT_EQUALS(destroyed, 1); | ||||
/** | /** | ||||
* TODO: find a way to test this | * TODO: find a way to test this | ||||
{ | { | ||||
Future<NonDef> future; | Future<NonDef> future; | ||||
std::function<void()> task = future.Wrap([]() { return 1; }); | std::function<void()> task = future.Wrap([]() { return 1; }); | ||||
future.Cancel(); | future.Cancel(); | ||||
future.Wait(); | future.Wait(); | ||||
TS_ASSERT_THROWS(future.Get(), const Future<NonDef>::BadFutureAccess&); | TS_ASSERT_THROWS(future.Get(), const Future<NonDef>::BadFutureAccess&); | ||||
} | } | ||||
*/ | */ | ||||
TS_ASSERT_EQUALS(destroyed, 1); | TS_ASSERT_EQUALS(destroyed, 1); | ||||
} | } | ||||
void test_future_moving() | void test_future_moving() | ||||
{ | { | ||||
Future<int> future; | using Function = int(*)(); | ||||
std::function<int()> function; | Future<int(*)()> future; | ||||
Function function = nullptr; | |||||
// Set things up so all temporaries passed into the futures will be reset to obviously invalid memory. | // Set things up so all temporaries passed into the futures will be reset to obviously invalid memory. | ||||
std::aligned_storage_t<sizeof(Future<int>), alignof(Future<int>)> futureStorage; | std::aligned_storage_t<sizeof(Future<int(*)()>), alignof(Future<int(*)()>)> futureStorage; | ||||
std::aligned_storage_t<sizeof(std::function<int()>), alignof(std::function<int()>)> functionStorage; | std::aligned_storage_t<sizeof(function), alignof(function)> functionStorage; | ||||
Future<int>* f = &future; // CppCheck doesn't read placement new correctly, do this to silence errors. | Future<int(*)()>* f = &future; // CppCheck doesn't read placement new correctly, do this to silence errors. | ||||
std::function<int()>* c = &function; | Function* c = &function; | ||||
f = new (&futureStorage) Future<int>{}; | f = new (&futureStorage) Future<int(*)()>{}; | ||||
c = new (&functionStorage) std::function<int()>{}; | c = new (&functionStorage) Function(nullptr); | ||||
*c = []() { return 7; }; | *c = []() { return 7; }; | ||||
std::function<void()> task = f->Wrap(std::move(*c)); | *f = std::move(*c); | ||||
std::function<void()> task = f->GetTask(); | |||||
future = std::move(*f); | future = std::move(*f); | ||||
function = std::move(*c); | function = std::move(*c); | ||||
// Destroy and clear the memory | // Destroy and clear the memory | ||||
f->~Future(); | f->~Future(); | ||||
c->~function(); | |||||
memset(&futureStorage, 0xFF, sizeof(decltype(futureStorage))); | memset(&futureStorage, 0xFF, sizeof(decltype(futureStorage))); | ||||
memset(&functionStorage, 0xFF, sizeof(decltype(functionStorage))); | memset(&functionStorage, 0xFF, sizeof(decltype(functionStorage))); | ||||
// Let's move the packaged task while at it. | // Let's move the packaged task while at it. | ||||
std::function<void()> task2 = std::move(task); | std::function<void()> task2 = std::move(task); | ||||
task2(); | task2(); | ||||
TS_ASSERT_EQUALS(future.Get(), 7); | TS_ASSERT_EQUALS(future.Get(), 7); | ||||
} | } | ||||
}; | }; |
Wildfire Games · Phabricator
Inaccurate Copyright Year