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_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(
|
||||
"${CMake_SOURCE_DIR}/Tests/CTestTestCrash/test.cmake.in"
|
||||
"${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