216 lines
6.3 KiB
C++
216 lines
6.3 KiB
C++
#include "zeroerr/assert.h"
|
|
#include "zeroerr/unittest.h"
|
|
|
|
#include "common/Module.h"
|
|
#include "platform_event/PlatformEvent.h"
|
|
#include "scripts.h"
|
|
#include "thread/Thread.h"
|
|
#include "timer/Timer.h"
|
|
|
|
#include <simplesquirrel/simplesquirrel.hpp>
|
|
|
|
#include <memory>
|
|
#include <string>
|
|
|
|
namespace {
|
|
|
|
void exposeCore(ssq::VM& vm) { eve::ModuleManager::expose(vm); }
|
|
|
|
std::string runAsyncSnippet(const std::string& body) {
|
|
ssq::VM vm(1024, ssq::Libs::ALL);
|
|
exposeCore(vm);
|
|
{
|
|
ssq::Table eve(vm.find("eve"));
|
|
eve.set("asyncScript", std::string(eve::async_content ? eve::async_content : ""));
|
|
}
|
|
|
|
// Minimal globals expected by async.nut / load prelude.
|
|
std::string prelude = R"(
|
|
timer <- eve.Timer();
|
|
thread <- eve.Thread();
|
|
event <- eve.PlatformEvent();
|
|
if ("asyncScript" in eve && eve.asyncScript != "")
|
|
compilestring(eve.asyncScript)();
|
|
)";
|
|
|
|
vm.run(vm.compileSource((prelude + "\n" + body).c_str()));
|
|
// Return a global `result` string if set.
|
|
try {
|
|
return vm.find("result").toString();
|
|
} catch (...) {
|
|
return {};
|
|
}
|
|
}
|
|
|
|
} // namespace
|
|
|
|
TEST_CASE("async.promise.resolveThen") {
|
|
std::string out = runAsyncSnippet(R"(
|
|
result <- "fail";
|
|
local p = Promise.resolve(42);
|
|
p.then(function(v) { result = "" + v; });
|
|
async_pump();
|
|
async_pump();
|
|
)");
|
|
CHECK_EQ(out, std::string("42"));
|
|
}
|
|
|
|
TEST_CASE("async.setTimeout") {
|
|
std::string out = runAsyncSnippet(R"(
|
|
result <- "pending";
|
|
setTimeout(function() { result = "fired"; }, 5);
|
|
local t0 = timer.getTime();
|
|
while (timer.getTime() - t0 < 0.05) {
|
|
// busy wait wall time; timer uses SDL performance counter
|
|
}
|
|
// Advance timer.step so getTime keeps moving; fire timers.
|
|
for (local i = 0; i < 5; i++) {
|
|
timer.step();
|
|
async_pump();
|
|
}
|
|
// If still pending, force due by pumping after sleep via thread
|
|
if (result == "pending") {
|
|
local pool = thread.getPool();
|
|
local task = pool.submitSleep(20);
|
|
task.wait();
|
|
timer.step();
|
|
async_pump();
|
|
async_pump();
|
|
}
|
|
)");
|
|
CHECK_EQ(out, std::string("fired"));
|
|
}
|
|
|
|
TEST_CASE("async.asyncSleep") {
|
|
std::string out = runAsyncSnippet(R"(
|
|
result <- "pending";
|
|
asyncSleep(1).then(function(v) { result = "ok"; });
|
|
local pool = thread.getPool();
|
|
local task = pool.submitSleep(15);
|
|
task.wait();
|
|
for (local i = 0; i < 8; i++) {
|
|
timer.step();
|
|
async_pump();
|
|
}
|
|
)");
|
|
CHECK_EQ(out, std::string("ok"));
|
|
}
|
|
|
|
TEST_CASE("async.asyncDelay.worker") {
|
|
std::string out = runAsyncSnippet(R"(
|
|
result <- "pending";
|
|
asyncDelay(5, "hello").then(function(v) { result = v; });
|
|
local pool = thread.getPool();
|
|
local task = pool.submitSleep(30);
|
|
task.wait();
|
|
for (local i = 0; i < 10; i++) {
|
|
async_pump();
|
|
}
|
|
)");
|
|
CHECK_EQ(out, std::string("hello"));
|
|
}
|
|
|
|
TEST_CASE("async.asyncPost.sameNameCorrelatedByTask") {
|
|
std::string out = runAsyncSnippet(R"(
|
|
result <- "pending";
|
|
local slow = "";
|
|
local fast = "";
|
|
asyncPost("same-name-correlation", "slow", 25).then(function(v) { slow = v; });
|
|
asyncPost("same-name-correlation", "fast", 1).then(function(v) { fast = v; });
|
|
thread.getPool().waitAll();
|
|
|
|
local name = event.poll();
|
|
while (name != "") {
|
|
async_dispatch_event(name, event.getLastData());
|
|
name = event.poll();
|
|
}
|
|
for (local i = 0; i < 6; i++)
|
|
async_pump();
|
|
result = slow + "," + fast;
|
|
)");
|
|
CHECK_EQ(out, std::string("slow,fast"));
|
|
}
|
|
|
|
TEST_CASE("async.postMain.event") {
|
|
auto* th = eve::thread::Thread::create();
|
|
auto* ev = eve::platform_event::PlatformEvent::create();
|
|
th->postMain("asynctest", "payload");
|
|
CHECK_EQ(ev->pollName(), std::string("asynctest"));
|
|
CHECK_EQ(ev->getLastData(), std::string("payload"));
|
|
}
|
|
|
|
TEST_CASE("async.Promise.all") {
|
|
std::string out = runAsyncSnippet(R"(
|
|
result <- "fail";
|
|
Promise.all([Promise.resolve(1), Promise.resolve(2)]).then(function(arr) {
|
|
result = "" + arr[0] + "," + arr[1];
|
|
});
|
|
async_pump();
|
|
async_pump();
|
|
async_pump();
|
|
)");
|
|
CHECK_EQ(out, std::string("1,2"));
|
|
}
|
|
|
|
TEST_CASE("async.asyncSeq") {
|
|
std::string out = runAsyncSnippet(R"(
|
|
result <- "fail";
|
|
asyncSeq([
|
|
function() { return Promise.resolve(1); },
|
|
function(v) { return Promise.resolve(v + 2); },
|
|
function(v) { return v * 10; },
|
|
]).then(function(v) { result = "" + v; });
|
|
async_pump();
|
|
async_pump();
|
|
async_pump();
|
|
async_pump();
|
|
async_pump();
|
|
)");
|
|
CHECK_EQ(out, std::string("30"));
|
|
}
|
|
|
|
TEST_CASE("async.eveScriptAwaitStateMachine") {
|
|
std::string out = runAsyncSnippet(R"(
|
|
result <- "pending";
|
|
class Counter {
|
|
offset = 4;
|
|
async function compute(limit: int) -> int {
|
|
local total = this.offset;
|
|
for (local i = 0; i < limit; ++i)
|
|
total += await Promise.resolve(i);
|
|
try {
|
|
await Promise.reject("expected");
|
|
} catch (e) {
|
|
total += 10;
|
|
}
|
|
return total;
|
|
}
|
|
}
|
|
Counter().compute(4).then(
|
|
function(v) { result = "" + v; },
|
|
function(e) { result = "error:" + e; }
|
|
);
|
|
for (local i = 0; i < 32; ++i)
|
|
async_pump();
|
|
)");
|
|
CHECK_EQ(out, std::string("20"));
|
|
}
|
|
|
|
TEST_CASE("async.hotReloadCancelsOldContinuations") {
|
|
std::string out = runAsyncSnippet(R"(
|
|
result <- "pending";
|
|
async function wait_forever() {
|
|
await Promise(function(resolve, reject) {});
|
|
return "unexpected";
|
|
}
|
|
wait_forever().then(
|
|
function(v) { result = v; },
|
|
function(e) { result = e; }
|
|
);
|
|
async_cancel_continuations("test reload");
|
|
async_pump();
|
|
async_pump();
|
|
)");
|
|
CHECK(out.find("async continuation cancelled: test reload") != std::string::npos);
|
|
}
|