mirror of
https://github.com/MaskRay/ccls.git
synced 2024-11-25 09:05:10 +00:00
WIP but basic test e2e test running
This commit is contained in:
parent
d049cc23c6
commit
1b4f377682
1
.gitignore
vendored
1
.gitignore
vendored
@ -9,6 +9,7 @@ waf-*
|
||||
.waf*
|
||||
*.swp
|
||||
*.sln
|
||||
.vscode/.ropeproject
|
||||
*.vcxproj
|
||||
*.vcxproj.user
|
||||
*.vcxproj.filters
|
||||
|
@ -791,6 +791,7 @@ void RegisterMessageTypes() {
|
||||
MessageRegistry::instance()->Register<Ipc_CancelRequest>();
|
||||
MessageRegistry::instance()->Register<Ipc_InitializeRequest>();
|
||||
MessageRegistry::instance()->Register<Ipc_InitializedNotification>();
|
||||
MessageRegistry::instance()->Register<Ipc_Exit>();
|
||||
MessageRegistry::instance()->Register<Ipc_TextDocumentDidOpen>();
|
||||
MessageRegistry::instance()->Register<Ipc_TextDocumentDidChange>();
|
||||
MessageRegistry::instance()->Register<Ipc_TextDocumentDidClose>();
|
||||
@ -1330,6 +1331,11 @@ bool QueryDbMainLoop(
|
||||
break;
|
||||
}
|
||||
|
||||
case IpcId::Exit: {
|
||||
exit(0);
|
||||
break;
|
||||
}
|
||||
|
||||
case IpcId::CqueryFreshenIndex: {
|
||||
std::cerr << "Freshening " << project->entries.size() << " files" << std::endl;
|
||||
project->ForAllFilteredFiles(config, [&](int i, const Project::Entry& entry) {
|
||||
@ -1974,6 +1980,7 @@ void LanguageServerStdinLoop(IndexerConfig* config, std::unordered_map<IpcId, Ti
|
||||
}
|
||||
|
||||
case IpcId::Initialize:
|
||||
case IpcId::Exit:
|
||||
case IpcId::TextDocumentDidOpen:
|
||||
case IpcId::TextDocumentDidChange:
|
||||
case IpcId::TextDocumentDidClose:
|
||||
|
@ -10,6 +10,8 @@ const char* IpcIdToString(IpcId id) {
|
||||
return "initialize";
|
||||
case IpcId::Initialized:
|
||||
return "initialized";
|
||||
case IpcId::Exit:
|
||||
return "exit";
|
||||
case IpcId::TextDocumentDidOpen:
|
||||
return "textDocument/didOpen";
|
||||
case IpcId::TextDocumentDidChange:
|
||||
|
@ -10,6 +10,7 @@ enum class IpcId : int {
|
||||
CancelRequest = 0,
|
||||
Initialize,
|
||||
Initialized,
|
||||
Exit,
|
||||
TextDocumentDidOpen,
|
||||
TextDocumentDidChange,
|
||||
TextDocumentDidClose,
|
||||
|
@ -33,6 +33,11 @@ std::unique_ptr<BaseIpcMessage> MessageRegistry::ReadMessageFromStdin() {
|
||||
|
||||
std::string line;
|
||||
std::getline(std::cin, line);
|
||||
|
||||
// No content; end of stdin.
|
||||
if (line.empty())
|
||||
return nullptr;
|
||||
|
||||
// std::cin >> line;
|
||||
// std::cerr << "Read line " << line;
|
||||
|
||||
@ -70,9 +75,10 @@ std::unique_ptr<BaseIpcMessage> MessageRegistry::ReadMessageFromStdin() {
|
||||
}
|
||||
|
||||
std::unique_ptr<BaseIpcMessage> MessageRegistry::Parse(Reader& visitor) {
|
||||
std::string jsonrpc = visitor["jsonrpc"].GetString();
|
||||
if (jsonrpc != "2.0")
|
||||
if (!visitor.HasMember("jsonrpc") || std::string(visitor["jsonrpc"].GetString()) != "2.0") {
|
||||
std::cerr << "Bad or missing jsonrpc version" << std::endl;
|
||||
exit(1);
|
||||
}
|
||||
|
||||
std::string method;
|
||||
ReflectMember(visitor, "method", method);
|
||||
|
@ -1052,7 +1052,10 @@ MAKE_REFLECT_STRUCT(Ipc_InitializedNotification, id);
|
||||
|
||||
|
||||
|
||||
|
||||
struct Ipc_Exit : public IpcMessage<Ipc_Exit> {
|
||||
static const IpcId kIpcId = IpcId::Exit;
|
||||
};
|
||||
MAKE_REFLECT_EMPTY_STRUCT(Ipc_Exit);
|
||||
|
||||
|
||||
|
||||
|
@ -1,25 +1,7 @@
|
||||
import json
|
||||
import shlex
|
||||
from subprocess import Popen, PIPE
|
||||
|
||||
# We write test files in python. The test runner collects all python files in
|
||||
# the directory and executes them. The test function just creates a test object
|
||||
# which specifies expected stdin/stdout.
|
||||
#
|
||||
# Test functions are automatically discovered; they just need to be in the
|
||||
# global environment and start with `Test_`.
|
||||
|
||||
class TestBuilder:
|
||||
def WithFile(self, filename, contents):
|
||||
"""
|
||||
Writes the file contents to disk so that the language server can access it.
|
||||
"""
|
||||
pass
|
||||
|
||||
def Send(self, stdin):
|
||||
"""
|
||||
Send the given message to the language server.
|
||||
"""
|
||||
|
||||
# Content-Length: ...\r\n
|
||||
# \r\n
|
||||
# {
|
||||
@ -31,21 +13,138 @@ class TestBuilder:
|
||||
# }
|
||||
# }
|
||||
|
||||
# We write test files in python. The test runner collects all python files in
|
||||
# the directory and executes them. The test function just creates a test object
|
||||
# which specifies expected stdin/stdout.
|
||||
#
|
||||
# Test functions are automatically discovered; they just need to be in the
|
||||
# global environment and start with `Test_`.
|
||||
|
||||
class TestBuilder:
|
||||
def __init__(self):
|
||||
self.files = []
|
||||
self.sent = []
|
||||
self.received = []
|
||||
|
||||
def WithFile(self, filename, contents):
|
||||
"""
|
||||
Writes the file contents to disk so that the language server can access it.
|
||||
"""
|
||||
self.files.append((filename, contents))
|
||||
return self
|
||||
|
||||
def Send(self, stdin):
|
||||
"""
|
||||
Send the given message to the language server.
|
||||
"""
|
||||
stdin['jsonrpc'] = '2.0'
|
||||
self.sent.append(stdin)
|
||||
return self
|
||||
|
||||
def Expect(self, stdout):
|
||||
"""
|
||||
Expect a message from the language server.
|
||||
"""
|
||||
pass
|
||||
self.received.append(stdout)
|
||||
return self
|
||||
|
||||
def SetupCommonInit():
|
||||
def SetupCommonInit(self):
|
||||
"""
|
||||
Add initialize/initialized messages.
|
||||
"""
|
||||
pass
|
||||
self.Send({
|
||||
'id': 0,
|
||||
'method': 'initialize',
|
||||
'params': {
|
||||
'processId': 123,
|
||||
'rootPath': 'cquery',
|
||||
'capabilities': {},
|
||||
'trace': 'off'
|
||||
}
|
||||
})
|
||||
self.Expect({
|
||||
'id': 0,
|
||||
'method': 'initialized',
|
||||
'result': {}
|
||||
})
|
||||
return self
|
||||
|
||||
def _ExecuteTest(name, func):
|
||||
"""
|
||||
Executes a specific test.
|
||||
|
||||
|func| must return a TestBuilder object.
|
||||
"""
|
||||
test_builder = func()
|
||||
if not isinstance(test_builder, TestBuilder):
|
||||
raise Exception('%s does not return a TestBuilder instance' % name)
|
||||
|
||||
test_builder.Send({ 'method': 'exit' })
|
||||
|
||||
# Possible test runner implementation
|
||||
cmd = "x64/Debug/indexer.exe --language-server"
|
||||
process = Popen(shlex.split(cmd), stdin=PIPE, stdout=PIPE, stderr=PIPE, universal_newlines=True)
|
||||
|
||||
stdin = ''
|
||||
for message in test_builder.sent:
|
||||
payload = json.dumps(message)
|
||||
wrapped = 'Content-Length: %s\r\n\r\n%s' % (len(payload), payload)
|
||||
stdin += wrapped
|
||||
|
||||
print('## %s ##' % name)
|
||||
print('== STDIN ==')
|
||||
print(stdin)
|
||||
(stdout, stderr) = process.communicate(stdin)
|
||||
if stdout:
|
||||
print('== STDOUT ==')
|
||||
print(stdout)
|
||||
if stderr:
|
||||
print('== STDERR ==')
|
||||
print(stderr)
|
||||
|
||||
# TODO: Actually verify stdout.
|
||||
|
||||
exit_code = process.wait()
|
||||
|
||||
def _DiscoverTests():
|
||||
"""
|
||||
Discover and return all tests.
|
||||
"""
|
||||
for name, value in globals().items():
|
||||
if not callable(value):
|
||||
continue
|
||||
if not name.startswith('Test_'):
|
||||
continue
|
||||
yield (name, value)
|
||||
|
||||
def _RunTests():
|
||||
"""
|
||||
Executes all tests.
|
||||
"""
|
||||
for name, func in _DiscoverTests():
|
||||
print('Running test function %s' % name)
|
||||
_ExecuteTest(name, func)
|
||||
|
||||
|
||||
def Test_Outline():
|
||||
return TestBuilder()
|
||||
|
||||
|
||||
class lsSymbolKind:
|
||||
Function = 1
|
||||
|
||||
def lsSymbolInfo(name, position, kind):
|
||||
return {
|
||||
'name': name,
|
||||
'position': position,
|
||||
'kind': kind
|
||||
}
|
||||
|
||||
def Test_Init():
|
||||
return (TestBuilder()
|
||||
.SetupCommonInit()
|
||||
)
|
||||
|
||||
def _Test_Outline():
|
||||
return (TestBuilder()
|
||||
.SetupCommonInit()
|
||||
.WithFile("foo.cc",
|
||||
"""
|
||||
@ -58,15 +157,12 @@ def Test_Outline():
|
||||
'params': {}
|
||||
})
|
||||
.Expect({
|
||||
'id': 1
|
||||
'id': 1,
|
||||
'result': [
|
||||
lsSymbolInfo('void main()', (1, 1), lsSymbolKind.Function)
|
||||
]
|
||||
})
|
||||
}))
|
||||
|
||||
|
||||
# Possible test runner implementation
|
||||
# cmd = "x64/Release/indexer.exe --language-server"
|
||||
# process = Popen(shlex.split(cmd), stdin=PIPE, stdout=PIPE)
|
||||
# process.communicate('{}')
|
||||
# exit_code = process.wait()
|
||||
if __name__ == '__main__':
|
||||
_RunTests()
|
Loading…
Reference in New Issue
Block a user