Skip to content

Commit

Permalink
refactor tc_as_a_exe test
Browse files Browse the repository at this point in the history
  • Loading branch information
WebFreak001 committed May 31, 2024
1 parent 39599cb commit d64acaf
Show file tree
Hide file tree
Showing 3 changed files with 192 additions and 114 deletions.
121 changes: 7 additions & 114 deletions test/tc_as_a_exe/source/app.d
Original file line number Diff line number Diff line change
Expand Up @@ -14,40 +14,19 @@ import core.thread;
import core.thread.fiber;

import served.lsp.filereader;
import served.lsp.jsonops;
import served.lsp.jsonrpc;
import served.lsp.protocol;
import served.lsp.uri;
import served.lsp.jsonops;

import tests._basic;

version (assert)
{
}
else
static assert(false, "Compile with asserts.");

__gshared RPCProcessor rpc;
__gshared string cwd;

// https://forum.dlang.org/post/[email protected]
void copyDir(string inDir, string outDir)
{
if (!exists(outDir))
mkdir(outDir);
else
if (!isDir(outDir))
throw new FileException(format("Destination path %s is not a folder.", outDir));

foreach (entry; dirEntries(inDir.idup, SpanMode.shallow))
{
auto fileName = baseName(entry.name);
auto destName = buildPath(outDir, fileName);
if (entry.isDir())
copyDir(entry.name, destName);
else
copy(entry.name, destName);
}
}

void main()
{
version (Windows)
Expand All @@ -57,94 +36,8 @@ void main()

globalLogLevel = LogLevel.all;

cwd = buildNormalizedPath(tempDir, randomUUID.toString);
copyDir("template", cwd);
info("temporary CWD: ", cwd);
scope (failure)
info("Keeping temporary directory for debugging purposes");
scope (success)
rmdirRecurse(cwd);

auto proc = pipeProcess([exe, "--loglevel=all"], Redirect.stdin | Redirect.stdout);

auto reader = newFileReader(proc.stdout);
reader.start();
scope (exit)
reader.stop();
rpc = new RPCProcessor(reader, proc.stdin);
auto testMethod = new Fiber(&doTests);
do
{
rpc.call();
if (testMethod.state == Fiber.State.TERM) {
if (rpc.state == Fiber.State.TERM)
break;
assert(false, "doTests exitted too early ");
}
testMethod.call();
Thread.sleep(1.msecs);
}
while (rpc.state != Fiber.State.TERM);
assert(testMethod.state == Fiber.State.TERM);
auto exitCode = proc.pid.wait;
assert(exitCode == 0, "serve-d failed with exit code " ~ exitCode.to!string);
return;
}

void delegate(RequestMessageRaw msg) gotRequest;
void delegate(RequestMessageRaw msg) gotNotify;

shared static this()
{
gotRequest = toDelegate(&defaultRequestHandler);
gotNotify = toDelegate(&defaultNotifyHandler);
}

void defaultRequestHandler(RequestMessageRaw msg)
{
assert(false, "Unexpected request " ~ msg.toString);
}

void defaultNotifyHandler(RequestMessageRaw msg)
{
info("Ignoring notification " ~ msg.toString);
}

void pumpEvents()
{
while (rpc.hasData)
{
auto msg = rpc.poll;
if (!msg.id.isNone)
gotRequest(msg);
else
gotNotify(msg);
}
}

void doTests()
{
WorkspaceClientCapabilities workspace = {
configuration: opt(true)
};
InitializeParams init = {
processId: thisProcessID,
rootUri: uriFromFile(cwd),
capabilities: {
workspace: opt(workspace)
}
};
auto msg = rpc.sendRequest("initialize", init, 10.seconds);
info("Response: ", msg.resultJson);

// TODO: do actual tests here

info("Shutting down...");
rpc.sendRequest("shutdown", init);
pumpEvents();
Fiber.yield();
rpc.notifyMethod("exit");
pumpEvents();
Thread.sleep(2.seconds); // give serve-d a chance to clean up
Fiber.yield();
foreach (test; [
new BasicTests(exe)
])
test.run();
}
38 changes: 38 additions & 0 deletions test/tc_as_a_exe/source/tests/_basic.d
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
module tests._basic;

import tests;

class BasicTests : ServedInstancedTest
{
this(string servedExe)
{
super(servedExe, buildPath(__FILE_FULL_PATH__, "../.."));
}

override void runImpl()
{
// dfmt off
WorkspaceClientCapabilities workspace = {
configuration: opt(true)
};
InitializeParams init = InitializeParams(
processId: typeof(InitializeParams.processId)(thisProcessID),
rootUri: uriFromFile(cwd),
capabilities: ClientCapabilities(
workspace: opt(workspace)
)
);
// dfmt on
auto msg = rpc.sendRequest("initialize", init, 10.seconds);
info("Response: ", msg.resultJson);

info("Shutting down...");
rpc.sendRequest("shutdown", init);
pumpEvents();
Fiber.yield();
rpc.notifyMethod("exit");
pumpEvents();
Thread.sleep(2.seconds); // give serve-d a chance to clean up
Fiber.yield();
}
}
147 changes: 147 additions & 0 deletions test/tc_as_a_exe/source/tests/package.d
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
module tests;

public import core.thread : Fiber, Thread;
public import core.time;
public import served.lsp.jsonrpc;
public import served.lsp.protocol;
public import served.lsp.uri;
public import std.conv;
public import std.experimental.logger;
public import std.path;
public import std.process : thisProcessID;
import std.functional : toDelegate;

abstract class ServedTest
{
this(string servedExe)
{
gotRequest = toDelegate(&defaultRequestHandler);
gotNotify = toDelegate(&defaultNotifyHandler);
this.servedExe = servedExe;
}

abstract void run();

void tick()
{
pumpEvents();
Fiber.yield();
}

void processResponse(void delegate(RequestMessageRaw msg) cb)
{
bool called;
gotRequest = (msg) { called = true; cb(msg); };
tick();
assert(called, "no response received!");
gotRequest = null;
}

void pumpEvents()
{
while (rpc.hasData)
{
auto msg = rpc.poll;
if (!msg.id.isNone)
gotRequest(msg);
else
gotNotify(msg);
}
}

protected string servedExe;

RPCProcessor rpc;

void delegate(RequestMessageRaw msg) gotRequest;
void delegate(RequestMessageRaw msg) gotNotify;
}

void defaultRequestHandler(RequestMessageRaw msg)
{
assert(false, "Unexpected request " ~ msg.toString);
}

void defaultNotifyHandler(RequestMessageRaw msg)
{
info("Ignoring notification " ~ msg.toString);
}

abstract class ServedInstancedTest : ServedTest
{
import std.file;
import std.process;

string templateDir;
string cwd;

this(string servedExe, string templateDir)
{
super(servedExe);
this.templateDir = templateDir;
}

override void run()
{
import served.lsp.filereader;
import std.uuid;

cwd = buildNormalizedPath(tempDir, randomUUID.toString);
copyDir("template", cwd);
info("temporary CWD: ", cwd);
scope (failure)
info("Keeping temporary directory for debugging purposes");
scope (success)
rmdirRecurse(cwd);

auto proc = pipeProcess([servedExe, "--loglevel=all"], Redirect.stdin |
Redirect.stdout);

auto reader = newFileReader(proc.stdout);
reader.start();
scope (exit)
reader.stop();
rpc = new RPCProcessor(reader, proc.stdin);
auto testMethod = new Fiber(&runImpl);
do
{
rpc.call();
if (testMethod.state == Fiber.State.TERM)
{
if (rpc.state == Fiber.State.TERM)
break;
assert(false, "doTests exitted too early ");
}
testMethod.call();
Thread.sleep(1.msecs);
}
while (rpc.state != Fiber.State.TERM);
assert(testMethod.state == Fiber.State.TERM);
auto exitCode = proc.pid.wait;
assert(exitCode == 0, "serve-d failed with exit code " ~ exitCode.to!string);
}

abstract void runImpl();
}

// https://forum.dlang.org/post/[email protected]
void copyDir(string inDir, string outDir)
{
import std.file;
import std.format;

if (!exists(outDir))
mkdir(outDir);
else if (!isDir(outDir))
throw new FileException(format("Destination path %s is not a folder.", outDir));

foreach (entry; dirEntries(inDir.idup, SpanMode.shallow))
{
auto fileName = baseName(entry.name);
auto destName = buildPath(outDir, fileName);
if (entry.isDir())
copyDir(entry.name, destName);
else
copy(entry.name, destName);
}
}

0 comments on commit d64acaf

Please sign in to comment.