cmake-server: Add unit test
This commit is contained in:
parent
d341d077c5
commit
b63c1f6ce7
|
@ -2722,6 +2722,15 @@ ${CMake_BINARY_DIR}/bin/cmake -DDIR=dev -P ${CMake_SOURCE_DIR}/Utilities/Release
|
||||||
ADD_TEST_MACRO(CMakeCommands.target_compile_definitions target_compile_definitions)
|
ADD_TEST_MACRO(CMakeCommands.target_compile_definitions target_compile_definitions)
|
||||||
ADD_TEST_MACRO(CMakeCommands.target_compile_options target_compile_options)
|
ADD_TEST_MACRO(CMakeCommands.target_compile_options target_compile_options)
|
||||||
|
|
||||||
|
if(CMake_HAVE_SERVER_MODE)
|
||||||
|
# The cmake server-mode test requires python for a simple client.
|
||||||
|
find_package(PythonInterp QUIET)
|
||||||
|
if(PYTHON_EXECUTABLE)
|
||||||
|
set(Server_BUILD_OPTIONS -DPYTHON_EXECUTABLE:FILEPATH=${PYTHON_EXECUTABLE})
|
||||||
|
ADD_TEST_MACRO(Server Server)
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
configure_file(
|
configure_file(
|
||||||
"${CMake_SOURCE_DIR}/Tests/CTestTestCrash/test.cmake.in"
|
"${CMake_SOURCE_DIR}/Tests/CTestTestCrash/test.cmake.in"
|
||||||
"${CMake_BINARY_DIR}/Tests/CTestTestCrash/test.cmake"
|
"${CMake_BINARY_DIR}/Tests/CTestTestCrash/test.cmake"
|
||||||
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
cmake_minimum_required(VERSION 3.4)
|
||||||
|
project(Server CXX)
|
||||||
|
|
||||||
|
find_package(PythonInterp REQUIRED)
|
||||||
|
|
||||||
|
macro(do_test bsname file)
|
||||||
|
execute_process(COMMAND ${PYTHON_EXECUTABLE}
|
||||||
|
"${CMAKE_SOURCE_DIR}/server-test.py"
|
||||||
|
"${CMAKE_COMMAND}"
|
||||||
|
"${CMAKE_SOURCE_DIR}/${file}"
|
||||||
|
"${CMAKE_SOURCE_DIR}"
|
||||||
|
"${CMAKE_BINARY_DIR}"
|
||||||
|
RESULT_VARIABLE test_result
|
||||||
|
)
|
||||||
|
|
||||||
|
if (NOT test_result EQUAL 0)
|
||||||
|
message(SEND_ERROR "TEST FAILED")
|
||||||
|
endif()
|
||||||
|
endmacro()
|
||||||
|
|
||||||
|
do_test("test_handshake" "tc_handshake.json")
|
||||||
|
|
||||||
|
add_executable(Server empty.cpp)
|
|
@ -0,0 +1,126 @@
|
||||||
|
import sys, subprocess, json
|
||||||
|
|
||||||
|
termwidth = 150
|
||||||
|
|
||||||
|
print_communication = True
|
||||||
|
|
||||||
|
def ordered(obj):
|
||||||
|
if isinstance(obj, dict):
|
||||||
|
return sorted((k, ordered(v)) for k, v in obj.items())
|
||||||
|
if isinstance(obj, list):
|
||||||
|
return sorted(ordered(x) for x in obj)
|
||||||
|
else:
|
||||||
|
return obj
|
||||||
|
|
||||||
|
def col_print(title, array):
|
||||||
|
print
|
||||||
|
print
|
||||||
|
print(title)
|
||||||
|
|
||||||
|
indentwidth = 4
|
||||||
|
indent = " " * indentwidth
|
||||||
|
|
||||||
|
if not array:
|
||||||
|
print(indent + "<None>")
|
||||||
|
return
|
||||||
|
|
||||||
|
padwidth = 2
|
||||||
|
|
||||||
|
maxitemwidth = len(max(array, key=len))
|
||||||
|
|
||||||
|
numCols = max(1, int((termwidth - indentwidth + padwidth) / (maxitemwidth + padwidth)))
|
||||||
|
|
||||||
|
numRows = len(array) // numCols + 1
|
||||||
|
|
||||||
|
pad = " " * padwidth
|
||||||
|
|
||||||
|
for index in range(numRows):
|
||||||
|
print(indent + pad.join(item.ljust(maxitemwidth) for item in array[index::numRows]))
|
||||||
|
|
||||||
|
def waitForRawMessage(cmakeCommand):
|
||||||
|
stdoutdata = ""
|
||||||
|
payload = ""
|
||||||
|
while not cmakeCommand.poll():
|
||||||
|
stdoutdataLine = cmakeCommand.stdout.readline()
|
||||||
|
if stdoutdataLine:
|
||||||
|
stdoutdata += stdoutdataLine.decode('utf-8')
|
||||||
|
else:
|
||||||
|
break
|
||||||
|
begin = stdoutdata.find("[== CMake Server ==[\n")
|
||||||
|
end = stdoutdata.find("]== CMake Server ==]")
|
||||||
|
|
||||||
|
if (begin != -1 and end != -1):
|
||||||
|
begin += len("[== CMake Server ==[\n")
|
||||||
|
payload = stdoutdata[begin:end]
|
||||||
|
if print_communication:
|
||||||
|
print("\nSERVER>", json.loads(payload), "\n")
|
||||||
|
return json.loads(payload)
|
||||||
|
|
||||||
|
def writeRawData(cmakeCommand, content):
|
||||||
|
writeRawData.counter += 1
|
||||||
|
payload = """
|
||||||
|
[== CMake Server ==[
|
||||||
|
%s
|
||||||
|
]== CMake Server ==]
|
||||||
|
""" % content
|
||||||
|
|
||||||
|
rn = ( writeRawData.counter % 2 ) == 0
|
||||||
|
|
||||||
|
if rn:
|
||||||
|
payload = payload.replace('\n', '\r\n')
|
||||||
|
|
||||||
|
if print_communication:
|
||||||
|
print("\nCLIENT>", content, "(Use \\r\\n:", rn, ")\n")
|
||||||
|
cmakeCommand.stdin.write(payload.encode('utf-8'))
|
||||||
|
cmakeCommand.stdin.flush()
|
||||||
|
writeRawData.counter = 0
|
||||||
|
|
||||||
|
def writePayload(cmakeCommand, obj):
|
||||||
|
writeRawData(cmakeCommand, json.dumps(obj))
|
||||||
|
|
||||||
|
def initProc(cmakeCommand):
|
||||||
|
cmakeCommand = subprocess.Popen([cmakeCommand, "-E", "server"],
|
||||||
|
stdin=subprocess.PIPE,
|
||||||
|
stdout=subprocess.PIPE)
|
||||||
|
|
||||||
|
packet = waitForRawMessage(cmakeCommand)
|
||||||
|
if packet == None:
|
||||||
|
print("Not in server mode")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
if packet['type'] != 'hello':
|
||||||
|
print("No hello message")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
return cmakeCommand
|
||||||
|
|
||||||
|
def waitForMessage(cmakeCommand, expected):
|
||||||
|
data = ordered(expected)
|
||||||
|
packet = ordered(waitForRawMessage(cmakeCommand))
|
||||||
|
|
||||||
|
if packet != data:
|
||||||
|
sys.exit(-1)
|
||||||
|
return packet
|
||||||
|
|
||||||
|
def waitForReply(cmakeCommand, originalType, cookie):
|
||||||
|
packet = waitForRawMessage(cmakeCommand)
|
||||||
|
if packet['cookie'] != cookie or packet['type'] != 'reply' or packet['inReplyTo'] != originalType:
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
def waitForError(cmakeCommand, originalType, cookie, message):
|
||||||
|
packet = waitForRawMessage(cmakeCommand)
|
||||||
|
if packet['cookie'] != cookie or packet['type'] != 'error' or packet['inReplyTo'] != originalType or packet['errorMessage'] != message:
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
def waitForProgress(cmakeCommand, originalType, cookie, current, message):
|
||||||
|
packet = waitForRawMessage(cmakeCommand)
|
||||||
|
if packet['cookie'] != cookie or packet['type'] != 'progress' or packet['inReplyTo'] != originalType or packet['progressCurrent'] != current or packet['progressMessage'] != message:
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
def handshake(cmakeCommand, major, minor):
|
||||||
|
version = { 'major': major }
|
||||||
|
if minor >= 0:
|
||||||
|
version['minor'] = minor
|
||||||
|
|
||||||
|
writePayload(cmakeCommand, { 'type': 'handshake', 'protocolVersion': version, 'cookie': 'TEST_HANDSHAKE' })
|
||||||
|
waitForReply(cmakeCommand, 'handshake', 'TEST_HANDSHAKE')
|
|
@ -0,0 +1,5 @@
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -0,0 +1,82 @@
|
||||||
|
import sys, cmakelib, json
|
||||||
|
|
||||||
|
debug = True
|
||||||
|
|
||||||
|
cmakeCommand = sys.argv[1]
|
||||||
|
testFile = sys.argv[2]
|
||||||
|
sourceDir = sys.argv[3]
|
||||||
|
buildDir = sys.argv[4]
|
||||||
|
|
||||||
|
print("SourceDir: ", sourceDir, " -- BuildDir: ", buildDir)
|
||||||
|
|
||||||
|
proc = cmakelib.initProc(cmakeCommand)
|
||||||
|
|
||||||
|
with open(testFile) as f:
|
||||||
|
testText = f.read()
|
||||||
|
testText = testText.replace('%BUILDDIR%', buildDir)
|
||||||
|
testText = testText.replace('%SOURCEDIR%', sourceDir)
|
||||||
|
testData = json.loads(testText)
|
||||||
|
|
||||||
|
buildDir = sys.argv[3]
|
||||||
|
sourceDir = sys.argv[4]
|
||||||
|
|
||||||
|
for obj in testData:
|
||||||
|
if 'sendRaw' in obj:
|
||||||
|
data = obj['sendRaw']
|
||||||
|
if debug: print("Sending raw:", data)
|
||||||
|
cmakelib.writeRawData(proc, data)
|
||||||
|
elif 'send' in obj:
|
||||||
|
data = obj['send']
|
||||||
|
if debug: print("Sending:", json.dumps(data))
|
||||||
|
cmakelib.writePayload(proc, data)
|
||||||
|
elif 'recv' in obj:
|
||||||
|
data = obj['recv']
|
||||||
|
if debug: print("Waiting for:", json.dumps(data))
|
||||||
|
cmakelib.waitForMessage(proc, data)
|
||||||
|
elif 'reply' in obj:
|
||||||
|
data = obj['reply']
|
||||||
|
if debug: print("Waiting for reply:", json.dumps(data))
|
||||||
|
originalType = ""
|
||||||
|
cookie = ""
|
||||||
|
if 'cookie' in data: cookie = data['cookie']
|
||||||
|
if 'type' in data: originalType = data['type']
|
||||||
|
cmakelib.waitForReply(proc, originalType, cookie)
|
||||||
|
elif 'error' in obj:
|
||||||
|
data = obj['error']
|
||||||
|
if debug: print("Waiting for error:", json.dumps(data))
|
||||||
|
originalType = ""
|
||||||
|
cookie = ""
|
||||||
|
message = ""
|
||||||
|
if 'cookie' in data: cookie = data['cookie']
|
||||||
|
if 'type' in data: originalType = data['type']
|
||||||
|
if 'message' in data: message = data['message']
|
||||||
|
cmakelib.waitForError(proc, originalType, cookie, message)
|
||||||
|
elif 'progress' in obj:
|
||||||
|
data = obj['progress']
|
||||||
|
if debug: print("Waiting for progress:", json.dumps(data))
|
||||||
|
originalType = ''
|
||||||
|
cookie = ""
|
||||||
|
current = 0
|
||||||
|
message = ""
|
||||||
|
if 'cookie' in data: cookie = data['cookie']
|
||||||
|
if 'type' in data: originalType = data['type']
|
||||||
|
if 'current' in data: current = data['current']
|
||||||
|
if 'message' in data: message = data['message']
|
||||||
|
cmakelib.waitForProgress(proc, originalType, cookie, current, message)
|
||||||
|
elif 'handshake' in obj:
|
||||||
|
data = obj['handshake']
|
||||||
|
if debug: print("Doing handshake:", json.dumps(data))
|
||||||
|
major = -1
|
||||||
|
minor = -1
|
||||||
|
if 'major' in data: major = data['major']
|
||||||
|
if 'minor' in data: minor = data['minor']
|
||||||
|
cmakelib.handshake(proc, major, minor)
|
||||||
|
elif 'message' in obj:
|
||||||
|
print("MESSAGE:", obj["message"])
|
||||||
|
else:
|
||||||
|
print("Unknown command:", json.dumps(obj))
|
||||||
|
sys.exit(2)
|
||||||
|
|
||||||
|
print("Completed")
|
||||||
|
|
||||||
|
sys.exit(0)
|
|
@ -0,0 +1,71 @@
|
||||||
|
[
|
||||||
|
{ "message": "Testing basic message handling:" },
|
||||||
|
|
||||||
|
{ "sendRaw": "Sometext"},
|
||||||
|
{ "recv": {"cookie":"","errorMessage":"Failed to parse JSON input.","inReplyTo":"","type":"error"} },
|
||||||
|
|
||||||
|
{ "message": "Testing invalid json input"},
|
||||||
|
{ "send": { "test": "sometext" } },
|
||||||
|
{ "recv": {"cookie":"","errorMessage":"No type given in request.","inReplyTo":"","type":"error"} },
|
||||||
|
|
||||||
|
{ "send": {"test": "sometext","cookie":"monster"} },
|
||||||
|
{ "recv": {"cookie":"monster","errorMessage":"No type given in request.","inReplyTo":"","type":"error"} },
|
||||||
|
|
||||||
|
{ "message": "Testing handshake" },
|
||||||
|
{ "send": {"type": "sometype","cookie":"monster2"} },
|
||||||
|
{ "recv": {"cookie":"monster2","errorMessage":"Waiting for type \"handshake\".","inReplyTo":"sometype","type":"error"} },
|
||||||
|
|
||||||
|
{ "send": {"type": "handshake"} },
|
||||||
|
{ "recv": {"cookie":"","errorMessage":"\"protocolVersion\" is required for \"handshake\".","inReplyTo":"handshake","type":"error"} },
|
||||||
|
|
||||||
|
{ "send": {"type": "handshake","foo":"bar"} },
|
||||||
|
{ "recv": {"cookie":"","errorMessage":"\"protocolVersion\" is required for \"handshake\".","inReplyTo":"handshake","type":"error"} },
|
||||||
|
|
||||||
|
{ "send": {"type": "handshake","protocolVersion":"bar"} },
|
||||||
|
{ "recv": {"cookie":"","errorMessage":"\"protocolVersion\" must be a JSON object.","inReplyTo":"handshake","type":"error"} },
|
||||||
|
|
||||||
|
{ "send": {"type": "handshake","protocolVersion":{}} },
|
||||||
|
{ "recv": {"cookie":"","errorMessage":"\"major\" must be set and an integer.","inReplyTo":"handshake","type":"error"} },
|
||||||
|
|
||||||
|
{ "send": {"type": "handshake","protocolVersion":{"major":"foo"}} },
|
||||||
|
{ "recv": {"cookie":"","errorMessage":"\"major\" must be set and an integer.","inReplyTo":"handshake","type":"error"} },
|
||||||
|
|
||||||
|
{ "send": {"type": "handshake","protocolVersion":{"major":1, "minor":"foo"}} },
|
||||||
|
{ "recv": {"cookie":"","errorMessage":"\"minor\" must be unset or an integer.","inReplyTo":"handshake","type":"error"} },
|
||||||
|
|
||||||
|
{ "send": {"type": "handshake","protocolVersion":{"major":-1, "minor":-1}} },
|
||||||
|
{ "recv": {"cookie":"","errorMessage":"\"major\" must be >= 0.","inReplyTo":"handshake","type":"error"} },
|
||||||
|
|
||||||
|
{ "send": {"type": "handshake","protocolVersion":{"major":10, "minor":-1}} },
|
||||||
|
{ "recv": {"cookie":"","errorMessage":"\"minor\" must be >= 0 when set.","inReplyTo":"handshake","type":"error"} },
|
||||||
|
|
||||||
|
{ "send": {"type": "handshake","protocolVersion":{"major":10000}} },
|
||||||
|
{ "recv": {"cookie":"","errorMessage":"Protocol version not supported.","inReplyTo":"handshake","type":"error"} },
|
||||||
|
|
||||||
|
{ "send": {"type": "handshake","protocolVersion":{"major":1, "minor":10000}} },
|
||||||
|
{ "recv": {"cookie":"","errorMessage":"Protocol version not supported.","inReplyTo":"handshake","type":"error"} },
|
||||||
|
|
||||||
|
{ "send": {"cookie":"zimtstern","type": "handshake","protocolVersion":{"major":1}} },
|
||||||
|
{ "recv": {"cookie":"zimtstern","inReplyTo":"handshake","type":"error","errorMessage":"Failed to activate protocol version: \"buildDirectory\" is missing."} },
|
||||||
|
|
||||||
|
{ "message": "Testing protocol version specific options (1.0):" },
|
||||||
|
{ "send": {"cookie":"zimtstern","type": "handshake","protocolVersion":{"major":1},"sourceDirectory":"/tmp/src"} },
|
||||||
|
{ "recv": {"cookie":"zimtstern","inReplyTo":"handshake","type":"error","errorMessage":"Failed to activate protocol version: \"buildDirectory\" is missing."} },
|
||||||
|
|
||||||
|
{ "send": {"cookie":"zimtstern","type": "handshake","protocolVersion":{"major":1},"sourceDirectory":"/tmp/src","buildDirectory":"/tmp/build"} },
|
||||||
|
{ "recv": {"cookie":"zimtstern","inReplyTo":"handshake","type":"error","errorMessage":"Failed to activate protocol version: \"sourceDirectory\" is not a directory."} },
|
||||||
|
|
||||||
|
{ "send": {"cookie":"zimtstern","type": "handshake","protocolVersion":{"major":1},"sourceDirectory":".","buildDirectory":"/tmp/build","extraGenerator":"CodeBlocks"} },
|
||||||
|
{ "recv": {"cookie":"zimtstern","inReplyTo":"handshake","type":"error","errorMessage":"Failed to activate protocol version: \"generator\" is unset but required."} },
|
||||||
|
|
||||||
|
{ "send": {"cookie":"zimtstern","type": "handshake","protocolVersion":{"major":1},"sourceDirectory":".","buildDirectory":"/tmp/build","generator":"XXXX","extraGenerator":"CodeBlocks"} },
|
||||||
|
{ "recv": {"cookie":"zimtstern","inReplyTo":"handshake","type":"error","errorMessage":"Failed to activate protocol version: Could not set up the requested combination of \"generator\" and \"extraGenerator\""} },
|
||||||
|
|
||||||
|
{ "send": {"cookie":"zimtstern","type": "handshake","protocolVersion":{"major":1},"sourceDirectory":".","buildDirectory":"/tmp/build","generator":"Ninja","extraGenerator":"XXXX"} },
|
||||||
|
{ "recv": {"cookie":"zimtstern","inReplyTo":"handshake","type":"error","errorMessage":"Failed to activate protocol version: Could not set up the requested combination of \"generator\" and \"extraGenerator\""} },
|
||||||
|
|
||||||
|
{ "send": {"cookie":"zimtstern","type": "handshake","protocolVersion":{"major":1},"sourceDirectory":".","buildDirectory":"/tmp/build","generator":"Ninja","extraGenerator":"CodeBlocks"} },
|
||||||
|
{ "recv": {"cookie":"zimtstern","inReplyTo":"handshake","type":"reply"} },
|
||||||
|
|
||||||
|
{ "message": "Everything ok." }
|
||||||
|
]
|
Loading…
Reference in New Issue