-
-
Notifications
You must be signed in to change notification settings - Fork 50
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
39599cb
commit d64acaf
Showing
3 changed files
with
192 additions
and
114 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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) | ||
|
@@ -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(); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
} | ||
} |