mirror of
https://github.com/MaskRay/ccls.git
synced 2024-11-22 07:35:08 +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*
|
.waf*
|
||||||
*.swp
|
*.swp
|
||||||
*.sln
|
*.sln
|
||||||
|
.vscode/.ropeproject
|
||||||
*.vcxproj
|
*.vcxproj
|
||||||
*.vcxproj.user
|
*.vcxproj.user
|
||||||
*.vcxproj.filters
|
*.vcxproj.filters
|
||||||
|
@ -791,6 +791,7 @@ void RegisterMessageTypes() {
|
|||||||
MessageRegistry::instance()->Register<Ipc_CancelRequest>();
|
MessageRegistry::instance()->Register<Ipc_CancelRequest>();
|
||||||
MessageRegistry::instance()->Register<Ipc_InitializeRequest>();
|
MessageRegistry::instance()->Register<Ipc_InitializeRequest>();
|
||||||
MessageRegistry::instance()->Register<Ipc_InitializedNotification>();
|
MessageRegistry::instance()->Register<Ipc_InitializedNotification>();
|
||||||
|
MessageRegistry::instance()->Register<Ipc_Exit>();
|
||||||
MessageRegistry::instance()->Register<Ipc_TextDocumentDidOpen>();
|
MessageRegistry::instance()->Register<Ipc_TextDocumentDidOpen>();
|
||||||
MessageRegistry::instance()->Register<Ipc_TextDocumentDidChange>();
|
MessageRegistry::instance()->Register<Ipc_TextDocumentDidChange>();
|
||||||
MessageRegistry::instance()->Register<Ipc_TextDocumentDidClose>();
|
MessageRegistry::instance()->Register<Ipc_TextDocumentDidClose>();
|
||||||
@ -1330,6 +1331,11 @@ bool QueryDbMainLoop(
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case IpcId::Exit: {
|
||||||
|
exit(0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case IpcId::CqueryFreshenIndex: {
|
case IpcId::CqueryFreshenIndex: {
|
||||||
std::cerr << "Freshening " << project->entries.size() << " files" << std::endl;
|
std::cerr << "Freshening " << project->entries.size() << " files" << std::endl;
|
||||||
project->ForAllFilteredFiles(config, [&](int i, const Project::Entry& entry) {
|
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::Initialize:
|
||||||
|
case IpcId::Exit:
|
||||||
case IpcId::TextDocumentDidOpen:
|
case IpcId::TextDocumentDidOpen:
|
||||||
case IpcId::TextDocumentDidChange:
|
case IpcId::TextDocumentDidChange:
|
||||||
case IpcId::TextDocumentDidClose:
|
case IpcId::TextDocumentDidClose:
|
||||||
|
@ -10,6 +10,8 @@ const char* IpcIdToString(IpcId id) {
|
|||||||
return "initialize";
|
return "initialize";
|
||||||
case IpcId::Initialized:
|
case IpcId::Initialized:
|
||||||
return "initialized";
|
return "initialized";
|
||||||
|
case IpcId::Exit:
|
||||||
|
return "exit";
|
||||||
case IpcId::TextDocumentDidOpen:
|
case IpcId::TextDocumentDidOpen:
|
||||||
return "textDocument/didOpen";
|
return "textDocument/didOpen";
|
||||||
case IpcId::TextDocumentDidChange:
|
case IpcId::TextDocumentDidChange:
|
||||||
|
@ -10,6 +10,7 @@ enum class IpcId : int {
|
|||||||
CancelRequest = 0,
|
CancelRequest = 0,
|
||||||
Initialize,
|
Initialize,
|
||||||
Initialized,
|
Initialized,
|
||||||
|
Exit,
|
||||||
TextDocumentDidOpen,
|
TextDocumentDidOpen,
|
||||||
TextDocumentDidChange,
|
TextDocumentDidChange,
|
||||||
TextDocumentDidClose,
|
TextDocumentDidClose,
|
||||||
|
@ -33,6 +33,11 @@ std::unique_ptr<BaseIpcMessage> MessageRegistry::ReadMessageFromStdin() {
|
|||||||
|
|
||||||
std::string line;
|
std::string line;
|
||||||
std::getline(std::cin, line);
|
std::getline(std::cin, line);
|
||||||
|
|
||||||
|
// No content; end of stdin.
|
||||||
|
if (line.empty())
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
// std::cin >> line;
|
// std::cin >> line;
|
||||||
// std::cerr << "Read line " << 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::unique_ptr<BaseIpcMessage> MessageRegistry::Parse(Reader& visitor) {
|
||||||
std::string jsonrpc = visitor["jsonrpc"].GetString();
|
if (!visitor.HasMember("jsonrpc") || std::string(visitor["jsonrpc"].GetString()) != "2.0") {
|
||||||
if (jsonrpc != "2.0")
|
std::cerr << "Bad or missing jsonrpc version" << std::endl;
|
||||||
exit(1);
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
std::string method;
|
std::string method;
|
||||||
ReflectMember(visitor, "method", 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
|
import shlex
|
||||||
from subprocess import Popen, PIPE
|
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
|
# Content-Length: ...\r\n
|
||||||
# \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):
|
def Expect(self, stdout):
|
||||||
"""
|
"""
|
||||||
Expect a message from the language server.
|
Expect a message from the language server.
|
||||||
"""
|
"""
|
||||||
pass
|
self.received.append(stdout)
|
||||||
|
return self
|
||||||
|
|
||||||
def SetupCommonInit():
|
def SetupCommonInit(self):
|
||||||
"""
|
"""
|
||||||
Add initialize/initialized messages.
|
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()
|
.SetupCommonInit()
|
||||||
.WithFile("foo.cc",
|
.WithFile("foo.cc",
|
||||||
"""
|
"""
|
||||||
@ -58,15 +157,12 @@ def Test_Outline():
|
|||||||
'params': {}
|
'params': {}
|
||||||
})
|
})
|
||||||
.Expect({
|
.Expect({
|
||||||
'id': 1
|
'id': 1,
|
||||||
'result': [
|
'result': [
|
||||||
lsSymbolInfo('void main()', (1, 1), lsSymbolKind.Function)
|
lsSymbolInfo('void main()', (1, 1), lsSymbolKind.Function)
|
||||||
]
|
]
|
||||||
})
|
}))
|
||||||
|
|
||||||
|
|
||||||
# Possible test runner implementation
|
if __name__ == '__main__':
|
||||||
# cmd = "x64/Release/indexer.exe --language-server"
|
_RunTests()
|
||||||
# process = Popen(shlex.split(cmd), stdin=PIPE, stdout=PIPE)
|
|
||||||
# process.communicate('{}')
|
|
||||||
# exit_code = process.wait()
|
|
Loading…
Reference in New Issue
Block a user