synth/arm/async_test.cc
Paul Mathieu f274749050 arm: add host uart driver tests
All host tests currently pass
Some async refactors may not work well on device, will try later
2023-06-02 23:33:01 -07:00

281 lines
6.1 KiB
C++

#include "async.h"
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <array>
#include <atomic>
#include <chrono>
#include <cstdio>
#include <mutex>
#include <semaphore>
#include <thread>
#include "trace.h"
using namespace ::testing;
using async::AwaitableType;
using namespace std::literals::chrono_literals;
int uptime() {
static std::chrono::system_clock::time_point start =
std::chrono::system_clock::now();
return std::chrono::duration_cast<std::chrono::microseconds>(
std::chrono::system_clock::now() - start)
.count();
}
template <typename... Args>
void log(int i, const char* fmt, Args... args) {
if (i != 3) {
return;
}
printf("[%10i] ", uptime());
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wformat-security"
printf(fmt, args...);
#pragma clang diagnostic pop
printf("\n");
}
namespace tracing {
void trace(TraceEvent) {}
} // namespace tracing
struct SimpleAsyncTest : public Test {
SimpleAsyncTest() : done(0) {}
std::binary_semaphore done;
async::task<> test_task() {
co_await async::await(AwaitableType::kUartRx);
done.release();
}
void SetUp() { terminate = false; }
static std::atomic<bool> terminate;
};
std::atomic<bool> SimpleAsyncTest::terminate;
TEST_F(SimpleAsyncTest, EnqueueResume) {
async::schedule(test_task().h);
std::thread t(
[]() { async::main_loop([]() -> bool { return terminate; }); });
async::resume(AwaitableType::kUartRx);
EXPECT_TRUE(done.try_acquire_for(1s));
terminate = true;
t.join();
}
struct LoopAsyncTest : public Test {
int in_isr = 0;
std::mutex m;
std::counting_semaphore<123456> processed;
LoopAsyncTest() : processed(0) {}
async::task<> loop_task() {
while (!terminate) {
co_await async::await(AwaitableType::kUartRx);
{
std::scoped_lock lock(m);
while (in_isr > 0) {
processed.release();
log(1, "processed.release() in_isr: %i", in_isr);
in_isr--;
}
}
}
}
void SetUp() { terminate = false; }
static std::atomic<bool> terminate;
};
std::atomic<bool> LoopAsyncTest::terminate;
TEST_F(LoopAsyncTest, ManyEnqueueResume) {
constexpr int kLoops = 100;
async::schedule(loop_task().h);
std::thread t(
[]() { async::main_loop([]() -> bool { return terminate; }); });
for (int i = 0; i < kLoops; i++) {
std::scoped_lock lock(m);
in_isr++;
async::resume(AwaitableType::kUartRx);
log(1, "async::resume() in_isr: %i", in_isr);
}
int done = 0;
for (int i = 0; i < kLoops; i++) {
if (processed.try_acquire_for(2s)) {
{
std::scoped_lock lock(m);
log(1, "processed acquired");
}
done++;
} else {
log(1, "skipping %i", i);
break;
}
}
EXPECT_EQ(done, kLoops);
log(3, "got all of em, done");
terminate = true;
t.join();
}
struct NestedAsync : SimpleAsyncTest {
async::task<int> interior() { co_return 42; }
async::task<int> interior_yield() {
co_await async::delay(0);
co_return 42;
}
async::task<int> generator() {
int i = 41;
while (true) {
co_yield i++;
}
}
std::atomic<bool> voiddone = false;
std::atomic<int> result = 0;
async::task<> voidstuff() {
voiddone = true;
log(3, "voidstuff()");
co_return;
}
async::task<> nested() {
log(3, "nested()");
result = co_await interior();
log(3, "got result");
co_await voidstuff();
log(3, "voidstuff returned");
done.release();
}
async::task<> othernested() {
log(3, "othernested()");
result = co_await interior_yield();
log(3, "got result");
co_await voidstuff();
log(3, "voidstuff returned");
done.release();
}
async::task<> call_generator() {
log(4, "call_generator()");
auto g = generator();
log(4, "generator()");
int a0 = co_await g;
log(4, "a0: %d", a0);
int a1 = co_await g;
log(4, "a1: %d", a1);
result = a1;
g.h.destroy();
done.release();
}
static std::binary_semaphore next;
static std::binary_semaphore ready;
};
std::binary_semaphore NestedAsync::next(0);
std::binary_semaphore NestedAsync::ready(0);
TEST_F(NestedAsync, SimpleNestedTest) {
while (next.try_acquire());
while (ready.try_acquire());
async::schedule(nested().h);
std::thread t([]() {
async::main_loop([]() -> bool {
// for some reason the bare `acquire` fails sometimes :/
while (!next.try_acquire_for(10ms));
return terminate;
});
});
ASSERT_FALSE(voiddone);
ASSERT_EQ(result, 0);
next.release();
EXPECT_TRUE(done.try_acquire_for(1s));
ASSERT_TRUE(voiddone);
ASSERT_EQ(result, 42);
terminate = true;
next.release();
t.join();
}
TEST_F(NestedAsync, LessSimpleNestedTest) {
async::schedule(othernested().h);
ASSERT_FALSE(voiddone);
ASSERT_EQ(result, 0);
log(3, "step");
async::step(); // should proceed until yield
ASSERT_FALSE(voiddone);
ASSERT_EQ(result, 0);
log(3, "step");
async::step(); // should proceed until end
EXPECT_EQ(voiddone, true);
ASSERT_EQ(result, 42);
EXPECT_TRUE(done.try_acquire_for(1s));
}
TEST_F(NestedAsync, GeneratorTest) {
async::schedule(call_generator().h);
std::thread t([]() {
async::main_loop([]() -> bool {
ready.release();
log(3, "loop ready");
next.acquire();
return terminate;
});
});
ASSERT_EQ(result, 0);
log(4, "starting stuff");
ASSERT_TRUE(ready.try_acquire_for(1s));
next.release();
ASSERT_TRUE(ready.try_acquire_for(1s));
ASSERT_EQ(result, 42);
EXPECT_TRUE(done.try_acquire_for(1s));
terminate = true;
next.release();
t.join();
}