Commit b63c1f6c authored by Tobias Hunger's avatar Tobias Hunger Committed by Brad King
Browse files

cmake-server: Add unit test

parent d341d077
Pipeline #26668 passed with stage
......@@ -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"
......
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)
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')
int main()
{
return 0;
}
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)
[
{ "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." }
]
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment