/* Copyright (C) 2021 Wildfire Games. * This file is part of 0 A.D. * * 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 * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * 0 A.D. is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with 0 A.D. If not, see . */ #include "lib/self_test.h" #include "ps/Future.h" #include #include class TestFuture : public CxxTest::TestSuite { public: void test_future_basic() { int counter = 0; { Future noret; std::function task = noret.Wrap([&counter]() mutable { counter++; }); task(); TS_ASSERT_EQUALS(counter, 1); } { Future noret; { std::function task = noret.Wrap([&counter]() mutable { counter++; }); // Auto-cancels the task. } } TS_ASSERT_EQUALS(counter, 1); } void test_future_return() { { Future future; std::function task = future.Wrap([]() { return 1; }); task(); TS_ASSERT_EQUALS(future.Get(), 1); } // Convertible type. { Future future; std::function task = future.Wrap([]() -> u8 { return 1; }); task(); TS_ASSERT_EQUALS(future.Get(), 1); } static int destroyed = 0; // No trivial constructor or destructor. Also not copiable. struct NonDef { NonDef() = delete; NonDef(int input) : value(input) {}; NonDef(const NonDef&) = delete; NonDef(NonDef&& o) { value = o.value; o.value = 0; } ~NonDef() { if (value != 0) destroyed++; } int value = 0; }; TS_ASSERT_EQUALS(destroyed, 0); { Future future; std::function task = future.Wrap([]() { return 1; }); task(); TS_ASSERT_EQUALS(future.Get().value, 1); } TS_ASSERT_EQUALS(destroyed, 1); { Future future; std::function task = future.Wrap([]() { return 1; }); } TS_ASSERT_EQUALS(destroyed, 1); /** * TODO: find a way to test this { Future future; std::function task = future.Wrap([]() { return 1; }); future.CancelOrWait(); TS_ASSERT_THROWS(future.Get(), const Future::BadFutureAccess&); } */ TS_ASSERT_EQUALS(destroyed, 1); } void test_future_moving() { Future future; std::function function; // Set things up so all temporaries passed into the futures will be reset to obviously invalid memory. std::aligned_storage_t), alignof(Future)> futureStorage; std::aligned_storage_t), alignof(std::function)> functionStorage; Future* f = &future; // CppCheck doesn't read placement new correctly, do this to silence errors. std::function* c = &function; f = new (&futureStorage) Future{}; c = new (&functionStorage) std::function{}; *c = []() { return 7; }; std::function task = f->Wrap(std::move(*c)); future = std::move(*f); function = std::move(*c); // Destroy and clear the memory f->~Future(); c->~function(); memset(&futureStorage, 0xFF, sizeof(decltype(futureStorage))); memset(&functionStorage, 0xFF, sizeof(decltype(functionStorage))); // Let's move the packaged task while at it. std::function task2 = std::move(task); task2(); TS_ASSERT_EQUALS(future.Get(), 7); } };