arm: add async tests
This commit is contained in:
parent
b9a9c2be7b
commit
15488550c0
287
arm/async_test.cc
Normal file
287
arm/async_test.cc
Normal file
@ -0,0 +1,287 @@
|
||||
#include "async.h"
|
||||
|
||||
#include <gmock/gmock.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <cstdio>
|
||||
#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 != 4) {
|
||||
return;
|
||||
}
|
||||
printf("[%10i] ", uptime());
|
||||
printf(fmt, args...);
|
||||
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);
|
||||
|
||||
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) {
|
||||
async::schedule(nested().h);
|
||||
std::thread t([]() {
|
||||
async::main_loop([]() -> bool {
|
||||
next.acquire();
|
||||
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);
|
||||
std::thread t([]() {
|
||||
async::main_loop([]() -> bool {
|
||||
ready.release();
|
||||
log(3, "loop ready");
|
||||
next.acquire();
|
||||
return terminate;
|
||||
});
|
||||
});
|
||||
|
||||
ASSERT_FALSE(voiddone);
|
||||
ASSERT_EQ(result, 0);
|
||||
|
||||
log(3, "starting stuff");
|
||||
|
||||
ASSERT_TRUE(ready.try_acquire_for(1s));
|
||||
next.release();
|
||||
ASSERT_TRUE(ready.try_acquire_for(1s));
|
||||
|
||||
ASSERT_FALSE(voiddone);
|
||||
ASSERT_EQ(result, 0);
|
||||
|
||||
next.release();
|
||||
ASSERT_TRUE(ready.try_acquire_for(1s));
|
||||
|
||||
EXPECT_EQ(voiddone, true);
|
||||
ASSERT_EQ(result, 42);
|
||||
|
||||
EXPECT_TRUE(done.try_acquire_for(1s));
|
||||
|
||||
terminate = true;
|
||||
next.release();
|
||||
|
||||
t.join();
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
23
arm/filter.sh
Normal file
23
arm/filter.sh
Normal file
@ -0,0 +1,23 @@
|
||||
#!/bin/sh
|
||||
|
||||
dos2unix "$@"
|
||||
|
||||
sed \
|
||||
-e 's,02$,02 rx,' \
|
||||
-e 's,03$,03 tx,' \
|
||||
-e 's,04$,04 resume,' \
|
||||
-e 's,05$,05 enqueue,' \
|
||||
-e 's,06$,06 run task,' \
|
||||
-e 's,09$,09 schedule,' \
|
||||
-e 's,0a$,0a send,' \
|
||||
-e 's,0b$,0b recv,' \
|
||||
-e 's,0e$,0e task done,' \
|
||||
-e 's,10$,10 run parent,' \
|
||||
-e 's,11$,11 parent done,' \
|
||||
-e 's,12$,12 co_await task,' \
|
||||
-e 's,13$,13 suspend,' \
|
||||
-e 's,14$,14 UartWrite done,' \
|
||||
-e 's,15$,15 coroutine destroy,' \
|
||||
-e 's,16$,16 gimme waiting,' \
|
||||
-e 's,17$,17 gimme resume,' \
|
||||
-i '' "$@"
|
36
arm/makefile
36
arm/makefile
@ -46,14 +46,42 @@ deps = $(app_objects:.o=.d) $(bootloader_objects:.o=.d)
|
||||
clean:
|
||||
rm -rf *.elf *.bin $(tests) $(app_objects) $(bootloader_objects) $(deps)
|
||||
|
||||
HOSTCXX = clang++
|
||||
HOSTCXX = /usr/local/opt/llvm/bin/clang++
|
||||
HOSTLDFLAGS = -lgmock -lgtest -lgtest_main -L/usr/local/opt/llvm/lib -L/usr/local/lib
|
||||
HOSTCFLAGS = -std=c++20 -g\
|
||||
-I/usr/local/opt/llvm/include \
|
||||
-I/usr/local/include \
|
||||
-I/usr/local/include \
|
||||
-MP -MD
|
||||
|
||||
tests = ring_buffer_test
|
||||
TSAN_CFLAGS = $(HOSTCFLAGS) -fsanitize=thread
|
||||
ASAN_CFLAGS = $(HOSTCFLAGS) -fsanitize=address -fsanitize=leak
|
||||
|
||||
tests = ring_buffer_test async_test_asan async_test_tsan
|
||||
|
||||
.PRECIOUS: $(tests)
|
||||
|
||||
test: $(tests)
|
||||
|
||||
ring_buffer_test: ring_buffer_test.cc
|
||||
$(HOSTCXX) -std=c++2a -o $@ $< -lgmock -lgtest -lgtest_main
|
||||
./$@
|
||||
mkdir test
|
||||
$(HOSTCXX) $(HOSTCFLAGS) -o test/$@ $< $(HOSTLDFLAGS)
|
||||
./test/$@
|
||||
|
||||
%.host.o: %.cc
|
||||
$(HOSTCXX) $(HOSTCFLAGS) -c -o $@ $<
|
||||
|
||||
async_test_tsan: async_test.cc async.host.o
|
||||
mkdir -p test
|
||||
$(HOSTCXX) $(TSAN_CFLAGS) -o test/$@ $^ $(HOSTLDFLAGS)
|
||||
TSAN_OPTIONS='suppressions=tsan.suppressions' ./test/$@
|
||||
|
||||
async_test_asan: async_test.cc async.host.o
|
||||
mkdir -p test
|
||||
$(HOSTCXX) $(ASAN_CFLAGS) -o test/$@ $^ $(HOSTLDFLAGS)
|
||||
ASAN_OPTIONS=detect_leaks=1 ./test/$@
|
||||
|
||||
test_deps = async.host.d
|
||||
|
||||
-include $(deps)
|
||||
-include $(test_deps)
|
||||
|
18
arm/test.py
Normal file
18
arm/test.py
Normal file
@ -0,0 +1,18 @@
|
||||
import serial
|
||||
|
||||
s = serial.Serial('tty', 115200, timeout=6)
|
||||
length = 1
|
||||
|
||||
log = open('test.log', 'wb')
|
||||
|
||||
while True:
|
||||
print(f'Writing {length} bytes')
|
||||
s.write(b'b' * length);
|
||||
data = s.read(length)
|
||||
log.write(data)
|
||||
if not data:
|
||||
break
|
||||
|
||||
length += 1
|
||||
|
||||
print(f'Read back 0 bytes')
|
3
arm/tsan.suppressions
Normal file
3
arm/tsan.suppressions
Normal file
@ -0,0 +1,3 @@
|
||||
race:std::__1::basic_ios<char, std::__1::char_traits<char> >::fill()
|
||||
|
||||
race:std::__1::ios_base::width()
|
Loading…
Reference in New Issue
Block a user