Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ODE-1042 Automated integration test framework (prototype) #297

Merged
merged 2 commits into from
Jan 23, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions test-harness/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
FROM python:3.7.2-alpine

RUN pip3 install requests
RUN pip3 install kafka-python

WORKDIR /home/test-harness
COPY . /home/test-harness

CMD python main.py
106 changes: 106 additions & 0 deletions test-harness/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
import threading
import requests
import time
import queue
import json
import os
from kafka import KafkaConsumer

DOCKER_HOST_IP=os.getenv('DOCKER_HOST_IP')
assert DOCKER_HOST_IP != None, "Failed to get DOCKER_HOST_IP from environment variable"

class TestCase:
def __init__(self, input, expectedOutput):
self.input = input
self.expectedOutput = expectedOutput

mcin = str(input['tim']['msgCnt'])
mcout = str(expectedOutput['payload']['data']['MessageFrame']['value']['TravelerInformation']['msgCnt'])
assert mcin == mcout, "Message count input and output do not match: input=%s, output=%s" % (mcin, mcout)
self.msgCnt = mcin

def compare(actualOutput):
assert str(self.expectedOutput) == str(actualOutput), "Input file does not match expected output: expected <%s> but got <%s>" % (str(self.expectedOutput), str(actualOutput))

def sendRESTRequest(body):
response=requests.post('https://' + DOCKER_HOST_IP + ':8080/tim', data = body)
print("Response code: " + str(response.status_code))

def listenToKafkaTopic(topic, msgQueue, expectedMsgCount):
print("Listening on topic: " + topic)
consumer=KafkaConsumer(topic, bootstrap_servers=DOCKER_HOST_IP+':9092')
msgsReceived=0
for msg in consumer:
msgsReceived += 1
print(str(msgsReceived))
msgQueue.put(msg)
if msgsReceived >= expectedMsgCount:
return

def createOutputList(fileDict):
outputList=[]
for infile, outfile in fileDict.items():
outfileData=json.load(open(outfile, 'r').read())
outputList.append(outfileData)
return outputList

def findOutputFile(msgCnt, fileDict):
for outfile in fileDict.queue:
if json.loads(outfile.value)['payload']['data']['MessageFrame']['value']['TravelerInformation']['msgCnt'] == msgCnt:
return outfile
return None

def validate(testCaseList, actualMsgQueue):
assert len(testCaseList) == actualMsgQueue.qsize(), "Input list length does not match output list length, input: %d, output: %d" % (len(testCaseList), actualMsgQueue.qsize())
for msg in actualMsgQueue.queue:
# Find corresponding output file
msgCnt=json.loads(msg.value)['payload']['data']['MessageFrame']['value']['TravelerInformation']['msgCnt']
expectedOutputFile=findOutputFile(msgCnt, actualMsgQueue)
assert expectedOutputFile != None, "Unable to find matching output file for input file msgCnt=%s" % str(msgCnt)
assert msg.value == expectedOutputFile.value, "Input file does not match expected output: expected <%s> but got <%s>" % (msg.value, expectedOutputFile.value)

def main():
print("Test routine started...")
testFileDict={
"tim1_input.json": "tim1_output.json"
}
timBroadcastTopic='topic.J2735TimBroadcastJson'
msgQueue=queue.Queue()

# Create a kafka consumer and wait for it to connect
kafkaListenerThread=threading.Thread(target=listenToKafkaTopic,args=(timBroadcastTopic,msgQueue, len(testFileDict)),)
kafkaListenerThread.start()
time.sleep(5)

# Create test cases
print("Creating test cases...")
tcList = []
for inputFilename, expectedOutputFilename in testFileDict.items():
inputData = json.loads(open(inputFilename, 'r').read())
expectedOutputData = json.loads(open(expectedOutputFilename, 'r').read())
tc = TestCase(inputData, expectedOutputData)
tcList.append(tc)

print("Test cases created.")

# Send test data
for index, testCase in enumerate(tcList, start=1):
print("Sending REST request for test case %d/%d" % (index, len(tcList)))
sendRESTRequest(json.dumps(testCase.input))

# Perform validation
print("Request sending complete.")

print("Waiting for all messages to be received...")
kafkaListenerThread.join()
print("All messages received.")

print("Validating output...")
validate(tcList, msgQueue)
print("Output validated.")

print("All tests have passed")
print("End.")

if __name__ == "__main__":
main()
7 changes: 7 additions & 0 deletions test-harness/run.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#!/bin/sh
if [[ -z "$DOCKER_HOST_IP" ]]; then
echo "ERROR! Required environment variable DOCKER_HOST_IP not set"
exit 1
fi
docker build -t ode-test-harness .
docker run --rm -e DOCKER_HOST_IP=$DOCKER_HOST_IP ode-test-harness:latest
1 change: 1 addition & 0 deletions test-harness/tim1_input.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"request": {"rsus": [{"rsuIndex": "9","rsuTarget": "127.0.0.1","rsuUsername": "uuuuuuuu","rsuPassword": "pppppppp","rsuRetries": "1","rsuTimeout": "2500"}],"snmp": {"rsuid": "00000083","msgid": "31","mode": "1","channel": "178","interval": "2","deliverystart": "2018-06-01T17:47:11-05:00","deliverystop": "2019-01-01T17:47:11-05:15","enable": "1","status": "4"}},"tim": {"msgCnt": "1","timeStamp": "2017-03-13T01:07:11-05:00","packetID": "EC9C236B0000000000","urlB": "null","dataframes": [{"sspTimRights": "0","frameType": "advisory","msgId": {"roadSignID": {"position": {"latitude": "42.678473","longitude": "-102.782775","elevation": "912.1432"},"viewAngle": "1010101010101010","mutcdCode": "warning","crc": "F1A7"}},"startDateTime": "2017-12-01T17:47:11-05:00","durationTime": "22","priority": "0","sspLocationRights": "3","regions": [{"name": "bob","regulatorID": "23","segmentID": "33","anchorPosition": {"latitude": "42.278473","longitude": "-102.782775","elevation": "912.1432"},"laneWidth": "7","directionality": "3","closedPath": "false","direction": "1010101010101010","description": "geometry","geometry": {"direction": "1010101010101010","extent": "1","laneWidth": "33","circle": {"position": {"latitude": "42.678473","longitude": "-102.782775","elevation": "912.1432"},"radius": "15","units": "7"}}}],"sspMsgTypes": "2","sspMsgContent": "3","content": "Advisory","items": ["125","some text","250","'98765"],"url": "null"}]}}
1 change: 1 addition & 0 deletions test-harness/tim1_output.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"metadata":{"request":{"ode":{"verb":"POST","version":3},"rsus":{"rsus":{"rsuTarget":"127.0.0.1","rsuUsername":"uuuuuuuu","rsuRetries":1,"rsuTimeout":2500,"rsuPassword":"*","rsuIndex":9}},"snmp":{"mode":1,"deliverystop":"2019-01-01T17:47:11-05:15","rsuid":"00000083","deliverystart":"2018-06-01T17:47:11-05:00","enable":1,"channel":178,"msgid":31,"interval":2,"status":4}},"recordGeneratedBy":"TMC","schemaVersion":6,"payloadType":"us.dot.its.jpo.ode.model.OdeTimPayload","serialId":{"recordId":0,"serialNumber":42,"streamId":"03965238-88a3-4f2b-a6d9-27501936514f","bundleSize":1,"bundleId":42},"sanitized":false,"recordGeneratedAt":"2017-03-13T06:07:11Z","odeReceivedAt":"2019-01-18T17:12:30.511Z"},"payload":{"data":{"MessageFrame":{"messageId":31,"value":{"TravelerInformation":{"timeStamp":102607,"packetID":"EC9C236B0000000000","urlB":null,"dataFrames":{"TravelerDataFrame":{"regions":{"GeographicalPath":{"closedPath":{"false":""},"anchor":{"elevation":9121,"lat":422784730,"long":-1027827750},"name":"bob","laneWidth":700,"directionality":{"both":""},"description":{"geometry":{"extent":1,"laneWidth":3300,"circle":{"center":{"elevation":9121,"lat":426784730,"long":-1027827750},"units":{"mile":""},"radius":15},"direction":1010101010101010}},"id":{"id":33,"region":23},"direction":1010101010101010}},"duratonTime":22,"sspMsgRights1":2,"sspMsgRights2":3,"startYear":2017,"msgId":{"roadSignID":{"crc":"F1A7","viewAngle":1010101010101010,"mutcdCode":{"warning":""},"position":{"elevation":9121,"lat":426784730,"long":-1027827750}}},"priority":0,"content":{"advisory":{"SEQUENCE":[{"item":{"itis":125}},{"item":{"text":"some text"}},{"item":{"itis":250}},{"item":{"text":98765}}]}},"url":null,"sspTimRights":0,"sspLocationRights":3,"frameType":{"advisory":""},"startTime":482327}},"msgCnt":1}}}},"dataType":"TravelerInformation"}}