mirror of
https://github.com/espressif/esp-idf.git
synced 2026-04-27 19:13:21 +00:00
style: format python files with isort and double-quote-string-fixer
This commit is contained in:
@@ -1,8 +1,8 @@
|
||||
import argparse
|
||||
import os
|
||||
import re
|
||||
import argparse
|
||||
import tempfile
|
||||
import tarfile
|
||||
import tempfile
|
||||
import zipfile
|
||||
from functools import wraps
|
||||
|
||||
@@ -10,21 +10,21 @@ import gitlab
|
||||
|
||||
|
||||
class Gitlab(object):
|
||||
JOB_NAME_PATTERN = re.compile(r"(\w+)(\s+(\d+)/(\d+))?")
|
||||
JOB_NAME_PATTERN = re.compile(r'(\w+)(\s+(\d+)/(\d+))?')
|
||||
|
||||
DOWNLOAD_ERROR_MAX_RETRIES = 3
|
||||
|
||||
def __init__(self, project_id=None):
|
||||
config_data_from_env = os.getenv("PYTHON_GITLAB_CONFIG")
|
||||
config_data_from_env = os.getenv('PYTHON_GITLAB_CONFIG')
|
||||
if config_data_from_env:
|
||||
# prefer to load config from env variable
|
||||
with tempfile.NamedTemporaryFile("w", delete=False) as temp_file:
|
||||
with tempfile.NamedTemporaryFile('w', delete=False) as temp_file:
|
||||
temp_file.write(config_data_from_env)
|
||||
config_files = [temp_file.name]
|
||||
else:
|
||||
# otherwise try to use config file at local filesystem
|
||||
config_files = None
|
||||
gitlab_id = os.getenv("LOCAL_GITLAB_HTTPS_HOST") # if None, will use the default gitlab server
|
||||
gitlab_id = os.getenv('LOCAL_GITLAB_HTTPS_HOST') # if None, will use the default gitlab server
|
||||
self.gitlab_inst = gitlab.Gitlab.from_config(gitlab_id=gitlab_id, config_files=config_files)
|
||||
self.gitlab_inst.auth()
|
||||
if project_id:
|
||||
@@ -46,7 +46,7 @@ class Gitlab(object):
|
||||
if len(projects) == 1:
|
||||
project_id = project.id
|
||||
break
|
||||
if project.namespace["path"] == namespace:
|
||||
if project.namespace['path'] == namespace:
|
||||
project_id = project.id
|
||||
break
|
||||
else:
|
||||
@@ -65,7 +65,7 @@ class Gitlab(object):
|
||||
with tempfile.NamedTemporaryFile(delete=False) as temp_file:
|
||||
job.artifacts(streamed=True, action=temp_file.write)
|
||||
|
||||
with zipfile.ZipFile(temp_file.name, "r") as archive_file:
|
||||
with zipfile.ZipFile(temp_file.name, 'r') as archive_file:
|
||||
archive_file.extractall(destination)
|
||||
|
||||
def retry_download(func):
|
||||
@@ -120,12 +120,12 @@ class Gitlab(object):
|
||||
except OSError:
|
||||
# already exists
|
||||
pass
|
||||
with open(file_path, "wb") as f:
|
||||
with open(file_path, 'wb') as f:
|
||||
f.write(data)
|
||||
|
||||
return raw_data_list
|
||||
|
||||
def find_job_id(self, job_name, pipeline_id=None, job_status="success"):
|
||||
def find_job_id(self, job_name, pipeline_id=None, job_status='success'):
|
||||
"""
|
||||
Get Job ID from job name of specific pipeline
|
||||
|
||||
@@ -137,14 +137,14 @@ class Gitlab(object):
|
||||
"""
|
||||
job_id_list = []
|
||||
if pipeline_id is None:
|
||||
pipeline_id = os.getenv("CI_PIPELINE_ID")
|
||||
pipeline_id = os.getenv('CI_PIPELINE_ID')
|
||||
pipeline = self.project.pipelines.get(pipeline_id)
|
||||
jobs = pipeline.jobs.list(all=True)
|
||||
for job in jobs:
|
||||
match = self.JOB_NAME_PATTERN.match(job.name)
|
||||
if match:
|
||||
if match.group(1) == job_name and job.status == job_status:
|
||||
job_id_list.append({"id": job.id, "parallel_num": match.group(3)})
|
||||
job_id_list.append({'id': job.id, 'parallel_num': match.group(3)})
|
||||
return job_id_list
|
||||
|
||||
@retry_download
|
||||
@@ -166,12 +166,12 @@ class Gitlab(object):
|
||||
try:
|
||||
project.repository_archive(sha=ref, streamed=True, action=temp_file.write)
|
||||
except gitlab.GitlabGetError as e:
|
||||
print("Failed to archive from project {}".format(project_id))
|
||||
print('Failed to archive from project {}'.format(project_id))
|
||||
raise e
|
||||
|
||||
print("archive size: {:.03f}MB".format(float(os.path.getsize(temp_file.name)) / (1024 * 1024)))
|
||||
print('archive size: {:.03f}MB'.format(float(os.path.getsize(temp_file.name)) / (1024 * 1024)))
|
||||
|
||||
with tarfile.open(temp_file.name, "r") as archive_file:
|
||||
with tarfile.open(temp_file.name, 'r') as archive_file:
|
||||
root_name = archive_file.getnames()[0]
|
||||
archive_file.extractall(destination)
|
||||
|
||||
@@ -180,27 +180,27 @@ class Gitlab(object):
|
||||
|
||||
if __name__ == '__main__':
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("action")
|
||||
parser.add_argument("project_id", type=int)
|
||||
parser.add_argument("--pipeline_id", "-i", type=int, default=None)
|
||||
parser.add_argument("--ref", "-r", default="master")
|
||||
parser.add_argument("--job_id", "-j", type=int, default=None)
|
||||
parser.add_argument("--job_name", "-n", default=None)
|
||||
parser.add_argument("--project_name", "-m", default=None)
|
||||
parser.add_argument("--destination", "-d", default=None)
|
||||
parser.add_argument("--artifact_path", "-a", nargs="*", default=None)
|
||||
parser.add_argument('action')
|
||||
parser.add_argument('project_id', type=int)
|
||||
parser.add_argument('--pipeline_id', '-i', type=int, default=None)
|
||||
parser.add_argument('--ref', '-r', default='master')
|
||||
parser.add_argument('--job_id', '-j', type=int, default=None)
|
||||
parser.add_argument('--job_name', '-n', default=None)
|
||||
parser.add_argument('--project_name', '-m', default=None)
|
||||
parser.add_argument('--destination', '-d', default=None)
|
||||
parser.add_argument('--artifact_path', '-a', nargs='*', default=None)
|
||||
args = parser.parse_args()
|
||||
|
||||
gitlab_inst = Gitlab(args.project_id)
|
||||
if args.action == "download_artifacts":
|
||||
if args.action == 'download_artifacts':
|
||||
gitlab_inst.download_artifacts(args.job_id, args.destination)
|
||||
if args.action == "download_artifact":
|
||||
if args.action == 'download_artifact':
|
||||
gitlab_inst.download_artifact(args.job_id, args.artifact_path, args.destination)
|
||||
elif args.action == "find_job_id":
|
||||
elif args.action == 'find_job_id':
|
||||
job_ids = gitlab_inst.find_job_id(args.job_name, args.pipeline_id)
|
||||
print(";".join([",".join([str(j["id"]), j["parallel_num"]]) for j in job_ids]))
|
||||
elif args.action == "download_archive":
|
||||
print(';'.join([','.join([str(j['id']), j['parallel_num']]) for j in job_ids]))
|
||||
elif args.action == 'download_archive':
|
||||
gitlab_inst.download_archive(args.ref, args.destination)
|
||||
elif args.action == "get_project_id":
|
||||
elif args.action == 'get_project_id':
|
||||
ret = gitlab_inst.get_project_id(args.project_name)
|
||||
print("project id: {}".format(ret))
|
||||
print('project id: {}'.format(ret))
|
||||
|
||||
@@ -14,12 +14,11 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from __future__ import print_function
|
||||
from __future__ import unicode_literals
|
||||
from builtins import str
|
||||
from builtins import range
|
||||
import http.client
|
||||
from __future__ import print_function, unicode_literals
|
||||
|
||||
import argparse
|
||||
import http.client
|
||||
from builtins import range, str
|
||||
|
||||
from tiny_test_fw import Utility
|
||||
|
||||
@@ -33,41 +32,41 @@ def end_session(conn):
|
||||
|
||||
|
||||
def getreq(conn, path, verbose=False):
|
||||
conn.request("GET", path)
|
||||
conn.request('GET', path)
|
||||
resp = conn.getresponse()
|
||||
data = resp.read()
|
||||
if verbose:
|
||||
Utility.console_log("GET : " + path)
|
||||
Utility.console_log("Status : " + resp.status)
|
||||
Utility.console_log("Reason : " + resp.reason)
|
||||
Utility.console_log("Data length : " + str(len(data)))
|
||||
Utility.console_log("Data content : " + data)
|
||||
Utility.console_log('GET : ' + path)
|
||||
Utility.console_log('Status : ' + resp.status)
|
||||
Utility.console_log('Reason : ' + resp.reason)
|
||||
Utility.console_log('Data length : ' + str(len(data)))
|
||||
Utility.console_log('Data content : ' + data)
|
||||
return data
|
||||
|
||||
|
||||
def postreq(conn, path, data, verbose=False):
|
||||
conn.request("POST", path, data)
|
||||
conn.request('POST', path, data)
|
||||
resp = conn.getresponse()
|
||||
data = resp.read()
|
||||
if verbose:
|
||||
Utility.console_log("POST : " + data)
|
||||
Utility.console_log("Status : " + resp.status)
|
||||
Utility.console_log("Reason : " + resp.reason)
|
||||
Utility.console_log("Data length : " + str(len(data)))
|
||||
Utility.console_log("Data content : " + data)
|
||||
Utility.console_log('POST : ' + data)
|
||||
Utility.console_log('Status : ' + resp.status)
|
||||
Utility.console_log('Reason : ' + resp.reason)
|
||||
Utility.console_log('Data length : ' + str(len(data)))
|
||||
Utility.console_log('Data content : ' + data)
|
||||
return data
|
||||
|
||||
|
||||
def putreq(conn, path, body, verbose=False):
|
||||
conn.request("PUT", path, body)
|
||||
conn.request('PUT', path, body)
|
||||
resp = conn.getresponse()
|
||||
data = resp.read()
|
||||
if verbose:
|
||||
Utility.console_log("PUT : " + path, body)
|
||||
Utility.console_log("Status : " + resp.status)
|
||||
Utility.console_log("Reason : " + resp.reason)
|
||||
Utility.console_log("Data length : " + str(len(data)))
|
||||
Utility.console_log("Data content : " + data)
|
||||
Utility.console_log('PUT : ' + path, body)
|
||||
Utility.console_log('Status : ' + resp.status)
|
||||
Utility.console_log('Reason : ' + resp.reason)
|
||||
Utility.console_log('Data length : ' + str(len(data)))
|
||||
Utility.console_log('Data content : ' + data)
|
||||
return data
|
||||
|
||||
|
||||
@@ -85,22 +84,22 @@ if __name__ == '__main__':
|
||||
N = args['N']
|
||||
|
||||
# Establish HTTP connection
|
||||
Utility.console_log("Connecting to => " + ip + ":" + port)
|
||||
Utility.console_log('Connecting to => ' + ip + ':' + port)
|
||||
conn = start_session(ip, port)
|
||||
|
||||
# Reset adder context to specified value(0)
|
||||
# -- Not needed as new connection will always
|
||||
# -- have zero value of the accumulator
|
||||
Utility.console_log("Reset the accumulator to 0")
|
||||
putreq(conn, "/adder", str(0))
|
||||
Utility.console_log('Reset the accumulator to 0')
|
||||
putreq(conn, '/adder', str(0))
|
||||
|
||||
# Sum numbers from 1 to specified value(N)
|
||||
Utility.console_log("Summing numbers from 1 to " + str(N))
|
||||
Utility.console_log('Summing numbers from 1 to ' + str(N))
|
||||
for i in range(1, N + 1):
|
||||
postreq(conn, "/adder", str(i))
|
||||
postreq(conn, '/adder', str(i))
|
||||
|
||||
# Fetch the result
|
||||
Utility.console_log("Result :" + getreq(conn, "/adder"))
|
||||
Utility.console_log('Result :' + getreq(conn, '/adder'))
|
||||
|
||||
# Close HTTP connection
|
||||
end_session(conn)
|
||||
|
||||
@@ -14,12 +14,11 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from __future__ import print_function
|
||||
from __future__ import unicode_literals
|
||||
from builtins import str
|
||||
import http.client
|
||||
import argparse
|
||||
from __future__ import print_function, unicode_literals
|
||||
|
||||
import argparse
|
||||
import http.client
|
||||
from builtins import str
|
||||
|
||||
from tiny_test_fw import Utility
|
||||
|
||||
@@ -31,28 +30,28 @@ def verbose_print(verbosity, *args):
|
||||
|
||||
def test_val(text, expected, received):
|
||||
if expected != received:
|
||||
Utility.console_log(" Fail!")
|
||||
Utility.console_log(" [reason] " + text + ":")
|
||||
Utility.console_log(" expected: " + str(expected))
|
||||
Utility.console_log(" received: " + str(received))
|
||||
Utility.console_log(' Fail!')
|
||||
Utility.console_log(' [reason] ' + text + ':')
|
||||
Utility.console_log(' expected: ' + str(expected))
|
||||
Utility.console_log(' received: ' + str(received))
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def test_get_handler(ip, port, verbosity=False):
|
||||
verbose_print(verbosity, "======== GET HANDLER TEST =============")
|
||||
verbose_print(verbosity, '======== GET HANDLER TEST =============')
|
||||
# Establish HTTP connection
|
||||
verbose_print(verbosity, "Connecting to => " + ip + ":" + port)
|
||||
sess = http.client.HTTPConnection(ip + ":" + port, timeout=15)
|
||||
verbose_print(verbosity, 'Connecting to => ' + ip + ':' + port)
|
||||
sess = http.client.HTTPConnection(ip + ':' + port, timeout=15)
|
||||
|
||||
uri = "/hello?query1=value1&query2=value2&query3=value3"
|
||||
uri = '/hello?query1=value1&query2=value2&query3=value3'
|
||||
# GET hello response
|
||||
test_headers = {"Test-Header-1":"Test-Value-1", "Test-Header-2":"Test-Value-2"}
|
||||
verbose_print(verbosity, "Sending GET to URI : ", uri)
|
||||
verbose_print(verbosity, "Sending additional headers : ")
|
||||
test_headers = {'Test-Header-1':'Test-Value-1', 'Test-Header-2':'Test-Value-2'}
|
||||
verbose_print(verbosity, 'Sending GET to URI : ', uri)
|
||||
verbose_print(verbosity, 'Sending additional headers : ')
|
||||
for k, v in test_headers.items():
|
||||
verbose_print(verbosity, "\t", k, ": ", v)
|
||||
sess.request("GET", url=uri, headers=test_headers)
|
||||
verbose_print(verbosity, '\t', k, ': ', v)
|
||||
sess.request('GET', url=uri, headers=test_headers)
|
||||
resp = sess.getresponse()
|
||||
resp_hdrs = resp.getheaders()
|
||||
resp_data = resp.read().decode()
|
||||
@@ -60,100 +59,100 @@ def test_get_handler(ip, port, verbosity=False):
|
||||
sess.close()
|
||||
|
||||
if not (
|
||||
test_val("Status code mismatch", 200, resp.status) and
|
||||
test_val("Response mismatch", "Custom-Value-1", resp.getheader("Custom-Header-1")) and
|
||||
test_val("Response mismatch", "Custom-Value-2", resp.getheader("Custom-Header-2")) and
|
||||
test_val("Response mismatch", "Hello World!", resp_data)
|
||||
test_val('Status code mismatch', 200, resp.status) and
|
||||
test_val('Response mismatch', 'Custom-Value-1', resp.getheader('Custom-Header-1')) and
|
||||
test_val('Response mismatch', 'Custom-Value-2', resp.getheader('Custom-Header-2')) and
|
||||
test_val('Response mismatch', 'Hello World!', resp_data)
|
||||
):
|
||||
return False
|
||||
|
||||
verbose_print(verbosity, "vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv")
|
||||
verbose_print(verbosity, "Server response to GET /hello")
|
||||
verbose_print(verbosity, "Response Headers : ")
|
||||
verbose_print(verbosity, 'vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv')
|
||||
verbose_print(verbosity, 'Server response to GET /hello')
|
||||
verbose_print(verbosity, 'Response Headers : ')
|
||||
for k, v in resp_hdrs:
|
||||
verbose_print(verbosity, "\t", k, ": ", v)
|
||||
verbose_print(verbosity, "Response Data : " + resp_data)
|
||||
verbose_print(verbosity, "========================================\n")
|
||||
verbose_print(verbosity, '\t', k, ': ', v)
|
||||
verbose_print(verbosity, 'Response Data : ' + resp_data)
|
||||
verbose_print(verbosity, '========================================\n')
|
||||
return True
|
||||
|
||||
|
||||
def test_post_handler(ip, port, msg, verbosity=False):
|
||||
verbose_print(verbosity, "======== POST HANDLER TEST ============")
|
||||
verbose_print(verbosity, '======== POST HANDLER TEST ============')
|
||||
# Establish HTTP connection
|
||||
verbose_print(verbosity, "Connecting to => " + ip + ":" + port)
|
||||
sess = http.client.HTTPConnection(ip + ":" + port, timeout=15)
|
||||
verbose_print(verbosity, 'Connecting to => ' + ip + ':' + port)
|
||||
sess = http.client.HTTPConnection(ip + ':' + port, timeout=15)
|
||||
|
||||
# POST message to /echo and get back response
|
||||
sess.request("POST", url="/echo", body=msg)
|
||||
sess.request('POST', url='/echo', body=msg)
|
||||
resp = sess.getresponse()
|
||||
resp_data = resp.read().decode()
|
||||
verbose_print(verbosity, "Server response to POST /echo (" + msg + ")")
|
||||
verbose_print(verbosity, "vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv")
|
||||
verbose_print(verbosity, 'Server response to POST /echo (' + msg + ')')
|
||||
verbose_print(verbosity, 'vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv')
|
||||
verbose_print(verbosity, resp_data)
|
||||
verbose_print(verbosity, "========================================\n")
|
||||
verbose_print(verbosity, '========================================\n')
|
||||
|
||||
# Close HTTP connection
|
||||
sess.close()
|
||||
return test_val("Response mismatch", msg, resp_data)
|
||||
return test_val('Response mismatch', msg, resp_data)
|
||||
|
||||
|
||||
def test_put_handler(ip, port, verbosity=False):
|
||||
verbose_print(verbosity, "======== PUT HANDLER TEST =============")
|
||||
verbose_print(verbosity, '======== PUT HANDLER TEST =============')
|
||||
# Establish HTTP connection
|
||||
verbose_print(verbosity, "Connecting to => " + ip + ":" + port)
|
||||
sess = http.client.HTTPConnection(ip + ":" + port, timeout=15)
|
||||
verbose_print(verbosity, 'Connecting to => ' + ip + ':' + port)
|
||||
sess = http.client.HTTPConnection(ip + ':' + port, timeout=15)
|
||||
|
||||
# PUT message to /ctrl to disable /hello and /echo URI handlers
|
||||
# and set 404 error handler to custom http_404_error_handler()
|
||||
verbose_print(verbosity, "Disabling /hello and /echo handlers")
|
||||
sess.request("PUT", url="/ctrl", body="0")
|
||||
verbose_print(verbosity, 'Disabling /hello and /echo handlers')
|
||||
sess.request('PUT', url='/ctrl', body='0')
|
||||
resp = sess.getresponse()
|
||||
resp.read()
|
||||
|
||||
try:
|
||||
# Send HTTP request to /hello URI
|
||||
sess.request("GET", url="/hello")
|
||||
sess.request('GET', url='/hello')
|
||||
resp = sess.getresponse()
|
||||
resp_data = resp.read().decode()
|
||||
|
||||
# 404 Error must be returned from server as URI /hello is no longer available.
|
||||
# But the custom error handler http_404_error_handler() will not close the
|
||||
# session if the requested URI is /hello
|
||||
if not test_val("Status code mismatch", 404, resp.status):
|
||||
if not test_val('Status code mismatch', 404, resp.status):
|
||||
raise AssertionError
|
||||
|
||||
# Compare error response string with expectation
|
||||
verbose_print(verbosity, "Response on GET /hello : " + resp_data)
|
||||
if not test_val("Response mismatch", "/hello URI is not available", resp_data):
|
||||
verbose_print(verbosity, 'Response on GET /hello : ' + resp_data)
|
||||
if not test_val('Response mismatch', '/hello URI is not available', resp_data):
|
||||
raise AssertionError
|
||||
|
||||
# Using same session for sending an HTTP request to /echo, as it is expected
|
||||
# that the custom error handler http_404_error_handler() would not have closed
|
||||
# the session
|
||||
sess.request("POST", url="/echo", body="Some content")
|
||||
sess.request('POST', url='/echo', body='Some content')
|
||||
resp = sess.getresponse()
|
||||
resp_data = resp.read().decode()
|
||||
|
||||
# 404 Error must be returned from server as URI /hello is no longer available.
|
||||
# The custom error handler http_404_error_handler() will close the session
|
||||
# this time as the requested URI is /echo
|
||||
if not test_val("Status code mismatch", 404, resp.status):
|
||||
if not test_val('Status code mismatch', 404, resp.status):
|
||||
raise AssertionError
|
||||
|
||||
# Compare error response string with expectation
|
||||
verbose_print(verbosity, "Response on POST /echo : " + resp_data)
|
||||
if not test_val("Response mismatch", "/echo URI is not available", resp_data):
|
||||
verbose_print(verbosity, 'Response on POST /echo : ' + resp_data)
|
||||
if not test_val('Response mismatch', '/echo URI is not available', resp_data):
|
||||
raise AssertionError
|
||||
|
||||
try:
|
||||
# Using same session should fail as by now the session would have closed
|
||||
sess.request("POST", url="/hello", body="Some content")
|
||||
sess.request('POST', url='/hello', body='Some content')
|
||||
resp = sess.getresponse()
|
||||
resp.read().decode()
|
||||
|
||||
# If control reaches this point then the socket was not closed.
|
||||
# This is not expected
|
||||
verbose_print(verbosity, "Socket not closed by server")
|
||||
verbose_print(verbosity, 'Socket not closed by server')
|
||||
raise AssertionError
|
||||
|
||||
except http.client.HTTPException:
|
||||
@@ -161,7 +160,7 @@ def test_put_handler(ip, port, verbosity=False):
|
||||
pass
|
||||
|
||||
except http.client.HTTPException:
|
||||
verbose_print(verbosity, "Socket closed by server")
|
||||
verbose_print(verbosity, 'Socket closed by server')
|
||||
return False
|
||||
|
||||
except AssertionError:
|
||||
@@ -171,47 +170,47 @@ def test_put_handler(ip, port, verbosity=False):
|
||||
# Close HTTP connection
|
||||
sess.close()
|
||||
|
||||
verbose_print(verbosity, "Enabling /hello handler")
|
||||
verbose_print(verbosity, 'Enabling /hello handler')
|
||||
# Create new connection
|
||||
sess = http.client.HTTPConnection(ip + ":" + port, timeout=15)
|
||||
sess = http.client.HTTPConnection(ip + ':' + port, timeout=15)
|
||||
# PUT message to /ctrl to enable /hello URI handler
|
||||
# and restore 404 error handler to default
|
||||
sess.request("PUT", url="/ctrl", body="1")
|
||||
sess.request('PUT', url='/ctrl', body='1')
|
||||
resp = sess.getresponse()
|
||||
resp.read()
|
||||
# Close HTTP connection
|
||||
sess.close()
|
||||
|
||||
# Create new connection
|
||||
sess = http.client.HTTPConnection(ip + ":" + port, timeout=15)
|
||||
sess = http.client.HTTPConnection(ip + ':' + port, timeout=15)
|
||||
|
||||
try:
|
||||
# Sending HTTP request to /hello should work now
|
||||
sess.request("GET", url="/hello")
|
||||
sess.request('GET', url='/hello')
|
||||
resp = sess.getresponse()
|
||||
resp_data = resp.read().decode()
|
||||
|
||||
if not test_val("Status code mismatch", 200, resp.status):
|
||||
if not test_val('Status code mismatch', 200, resp.status):
|
||||
raise AssertionError
|
||||
|
||||
verbose_print(verbosity, "Response on GET /hello : " + resp_data)
|
||||
if not test_val("Response mismatch", "Hello World!", resp_data):
|
||||
verbose_print(verbosity, 'Response on GET /hello : ' + resp_data)
|
||||
if not test_val('Response mismatch', 'Hello World!', resp_data):
|
||||
raise AssertionError
|
||||
|
||||
# 404 Error handler should have been restored to default
|
||||
sess.request("GET", url="/invalid")
|
||||
sess.request('GET', url='/invalid')
|
||||
resp = sess.getresponse()
|
||||
resp_data = resp.read().decode()
|
||||
|
||||
if not test_val("Status code mismatch", 404, resp.status):
|
||||
if not test_val('Status code mismatch', 404, resp.status):
|
||||
raise AssertionError
|
||||
|
||||
verbose_print(verbosity, "Response on GET /invalid : " + resp_data)
|
||||
if not test_val("Response mismatch", "This URI does not exist", resp_data):
|
||||
verbose_print(verbosity, 'Response on GET /invalid : ' + resp_data)
|
||||
if not test_val('Response mismatch', 'This URI does not exist', resp_data):
|
||||
raise AssertionError
|
||||
|
||||
except http.client.HTTPException:
|
||||
verbose_print(verbosity, "Socket closed by server")
|
||||
verbose_print(verbosity, 'Socket closed by server')
|
||||
return False
|
||||
|
||||
except AssertionError:
|
||||
@@ -225,26 +224,26 @@ def test_put_handler(ip, port, verbosity=False):
|
||||
|
||||
|
||||
def test_custom_uri_query(ip, port, query, verbosity=False):
|
||||
verbose_print(verbosity, "======== GET HANDLER TEST =============")
|
||||
verbose_print(verbosity, '======== GET HANDLER TEST =============')
|
||||
# Establish HTTP connection
|
||||
verbose_print(verbosity, "Connecting to => " + ip + ":" + port)
|
||||
sess = http.client.HTTPConnection(ip + ":" + port, timeout=15)
|
||||
verbose_print(verbosity, 'Connecting to => ' + ip + ':' + port)
|
||||
sess = http.client.HTTPConnection(ip + ':' + port, timeout=15)
|
||||
|
||||
uri = "/hello?" + query
|
||||
uri = '/hello?' + query
|
||||
# GET hello response
|
||||
verbose_print(verbosity, "Sending GET to URI : ", uri)
|
||||
sess.request("GET", url=uri, headers={})
|
||||
verbose_print(verbosity, 'Sending GET to URI : ', uri)
|
||||
sess.request('GET', url=uri, headers={})
|
||||
resp = sess.getresponse()
|
||||
resp_data = resp.read().decode()
|
||||
|
||||
verbose_print(verbosity, "vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv")
|
||||
verbose_print(verbosity, "Server response to GET /hello")
|
||||
verbose_print(verbosity, "Response Data : " + resp_data)
|
||||
verbose_print(verbosity, "========================================\n")
|
||||
verbose_print(verbosity, 'vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv')
|
||||
verbose_print(verbosity, 'Server response to GET /hello')
|
||||
verbose_print(verbosity, 'Response Data : ' + resp_data)
|
||||
verbose_print(verbosity, '========================================\n')
|
||||
|
||||
# Close HTTP connection
|
||||
sess.close()
|
||||
return "Hello World!" == resp_data
|
||||
return 'Hello World!' == resp_data
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
@@ -265,4 +264,4 @@ if __name__ == '__main__':
|
||||
test_put_handler(ip, port, True) and
|
||||
test_post_handler(ip, port, msg, True)
|
||||
):
|
||||
Utility.console_log("Failed!")
|
||||
Utility.console_log('Failed!')
|
||||
|
||||
@@ -129,19 +129,17 @@
|
||||
# - Simple GET on /hello/restart_results (returns the leak results)
|
||||
|
||||
|
||||
from __future__ import division
|
||||
from __future__ import print_function
|
||||
from builtins import str
|
||||
from builtins import range
|
||||
from builtins import object
|
||||
import threading
|
||||
import socket
|
||||
import time
|
||||
from __future__ import division, print_function
|
||||
|
||||
import argparse
|
||||
import http.client
|
||||
import sys
|
||||
import string
|
||||
import random
|
||||
import socket
|
||||
import string
|
||||
import sys
|
||||
import threading
|
||||
import time
|
||||
from builtins import object, range, str
|
||||
|
||||
from tiny_test_fw import Utility
|
||||
|
||||
@@ -165,32 +163,32 @@ class Session(object):
|
||||
self.client.sendall(data.encode())
|
||||
except socket.error as err:
|
||||
self.client.close()
|
||||
Utility.console_log("Socket Error in send :", err)
|
||||
Utility.console_log('Socket Error in send :', err)
|
||||
rval = False
|
||||
return rval
|
||||
|
||||
def send_get(self, path, headers=None):
|
||||
request = "GET " + path + " HTTP/1.1\r\nHost: " + self.target
|
||||
request = 'GET ' + path + ' HTTP/1.1\r\nHost: ' + self.target
|
||||
if headers:
|
||||
for field, value in headers.items():
|
||||
request += "\r\n" + field + ": " + value
|
||||
request += "\r\n\r\n"
|
||||
request += '\r\n' + field + ': ' + value
|
||||
request += '\r\n\r\n'
|
||||
return self.send_err_check(request)
|
||||
|
||||
def send_put(self, path, data, headers=None):
|
||||
request = "PUT " + path + " HTTP/1.1\r\nHost: " + self.target
|
||||
request = 'PUT ' + path + ' HTTP/1.1\r\nHost: ' + self.target
|
||||
if headers:
|
||||
for field, value in headers.items():
|
||||
request += "\r\n" + field + ": " + value
|
||||
request += "\r\nContent-Length: " + str(len(data)) + "\r\n\r\n"
|
||||
request += '\r\n' + field + ': ' + value
|
||||
request += '\r\nContent-Length: ' + str(len(data)) + '\r\n\r\n'
|
||||
return self.send_err_check(request, data)
|
||||
|
||||
def send_post(self, path, data, headers=None):
|
||||
request = "POST " + path + " HTTP/1.1\r\nHost: " + self.target
|
||||
request = 'POST ' + path + ' HTTP/1.1\r\nHost: ' + self.target
|
||||
if headers:
|
||||
for field, value in headers.items():
|
||||
request += "\r\n" + field + ": " + value
|
||||
request += "\r\nContent-Length: " + str(len(data)) + "\r\n\r\n"
|
||||
request += '\r\n' + field + ': ' + value
|
||||
request += '\r\nContent-Length: ' + str(len(data)) + '\r\n\r\n'
|
||||
return self.send_err_check(request, data)
|
||||
|
||||
def read_resp_hdrs(self):
|
||||
@@ -234,7 +232,7 @@ class Session(object):
|
||||
return headers
|
||||
except socket.error as err:
|
||||
self.client.close()
|
||||
Utility.console_log("Socket Error in recv :", err)
|
||||
Utility.console_log('Socket Error in recv :', err)
|
||||
return None
|
||||
|
||||
def read_resp_data(self):
|
||||
@@ -263,9 +261,9 @@ class Session(object):
|
||||
rem_len -= len(new_data)
|
||||
chunk_data_buf = ''
|
||||
# Fetch remaining CRLF
|
||||
if self.client.recv(2) != "\r\n":
|
||||
if self.client.recv(2) != '\r\n':
|
||||
# Error in packet
|
||||
Utility.console_log("Error in chunked data")
|
||||
Utility.console_log('Error in chunked data')
|
||||
return None
|
||||
if not chunk_len:
|
||||
# If last chunk
|
||||
@@ -278,7 +276,7 @@ class Session(object):
|
||||
return read_data
|
||||
except socket.error as err:
|
||||
self.client.close()
|
||||
Utility.console_log("Socket Error in recv :", err)
|
||||
Utility.console_log('Socket Error in recv :', err)
|
||||
return None
|
||||
|
||||
def close(self):
|
||||
@@ -287,10 +285,10 @@ class Session(object):
|
||||
|
||||
def test_val(text, expected, received):
|
||||
if expected != received:
|
||||
Utility.console_log(" Fail!")
|
||||
Utility.console_log(" [reason] " + text + ":")
|
||||
Utility.console_log(" expected: " + str(expected))
|
||||
Utility.console_log(" received: " + str(received))
|
||||
Utility.console_log(' Fail!')
|
||||
Utility.console_log(' [reason] ' + text + ':')
|
||||
Utility.console_log(' expected: ' + str(expected))
|
||||
Utility.console_log(' received: ' + str(received))
|
||||
return False
|
||||
return True
|
||||
|
||||
@@ -308,7 +306,7 @@ class adder_thread (threading.Thread):
|
||||
|
||||
# Pipeline 3 requests
|
||||
if (_verbose_):
|
||||
Utility.console_log(" Thread: Using adder start " + str(self.id))
|
||||
Utility.console_log(' Thread: Using adder start ' + str(self.id))
|
||||
|
||||
for _ in range(self.depth):
|
||||
self.session.send_post('/adder', str(self.id))
|
||||
@@ -320,10 +318,10 @@ class adder_thread (threading.Thread):
|
||||
|
||||
def adder_result(self):
|
||||
if len(self.response) != self.depth:
|
||||
Utility.console_log("Error : missing response packets")
|
||||
Utility.console_log('Error : missing response packets')
|
||||
return False
|
||||
for i in range(len(self.response)):
|
||||
if not test_val("Thread" + str(self.id) + " response[" + str(i) + "]",
|
||||
if not test_val('Thread' + str(self.id) + ' response[' + str(i) + ']',
|
||||
str(self.id * (i + 1)), str(self.response[i])):
|
||||
return False
|
||||
return True
|
||||
@@ -336,177 +334,177 @@ def get_hello(dut, port):
|
||||
# GET /hello should return 'Hello World!'
|
||||
Utility.console_log("[test] GET /hello returns 'Hello World!' =>", end=' ')
|
||||
conn = http.client.HTTPConnection(dut, int(port), timeout=15)
|
||||
conn.request("GET", "/hello")
|
||||
conn.request('GET', '/hello')
|
||||
resp = conn.getresponse()
|
||||
if not test_val("status_code", 200, resp.status):
|
||||
if not test_val('status_code', 200, resp.status):
|
||||
conn.close()
|
||||
return False
|
||||
if not test_val("data", "Hello World!", resp.read().decode()):
|
||||
if not test_val('data', 'Hello World!', resp.read().decode()):
|
||||
conn.close()
|
||||
return False
|
||||
if not test_val("data", "text/html", resp.getheader('Content-Type')):
|
||||
if not test_val('data', 'text/html', resp.getheader('Content-Type')):
|
||||
conn.close()
|
||||
return False
|
||||
Utility.console_log("Success")
|
||||
Utility.console_log('Success')
|
||||
conn.close()
|
||||
return True
|
||||
|
||||
|
||||
def put_hello(dut, port):
|
||||
# PUT /hello returns 405'
|
||||
Utility.console_log("[test] PUT /hello returns 405 =>", end=' ')
|
||||
Utility.console_log('[test] PUT /hello returns 405 =>', end=' ')
|
||||
conn = http.client.HTTPConnection(dut, int(port), timeout=15)
|
||||
conn.request("PUT", "/hello", "Hello")
|
||||
conn.request('PUT', '/hello', 'Hello')
|
||||
resp = conn.getresponse()
|
||||
if not test_val("status_code", 405, resp.status):
|
||||
if not test_val('status_code', 405, resp.status):
|
||||
conn.close()
|
||||
return False
|
||||
Utility.console_log("Success")
|
||||
Utility.console_log('Success')
|
||||
conn.close()
|
||||
return True
|
||||
|
||||
|
||||
def post_hello(dut, port):
|
||||
# POST /hello returns 405'
|
||||
Utility.console_log("[test] POST /hello returns 405 =>", end=' ')
|
||||
Utility.console_log('[test] POST /hello returns 405 =>', end=' ')
|
||||
conn = http.client.HTTPConnection(dut, int(port), timeout=15)
|
||||
conn.request("POST", "/hello", "Hello")
|
||||
conn.request('POST', '/hello', 'Hello')
|
||||
resp = conn.getresponse()
|
||||
if not test_val("status_code", 405, resp.status):
|
||||
if not test_val('status_code', 405, resp.status):
|
||||
conn.close()
|
||||
return False
|
||||
Utility.console_log("Success")
|
||||
Utility.console_log('Success')
|
||||
conn.close()
|
||||
return True
|
||||
|
||||
|
||||
def post_echo(dut, port):
|
||||
# POST /echo echoes data'
|
||||
Utility.console_log("[test] POST /echo echoes data =>", end=' ')
|
||||
Utility.console_log('[test] POST /echo echoes data =>', end=' ')
|
||||
conn = http.client.HTTPConnection(dut, int(port), timeout=15)
|
||||
conn.request("POST", "/echo", "Hello")
|
||||
conn.request('POST', '/echo', 'Hello')
|
||||
resp = conn.getresponse()
|
||||
if not test_val("status_code", 200, resp.status):
|
||||
if not test_val('status_code', 200, resp.status):
|
||||
conn.close()
|
||||
return False
|
||||
if not test_val("data", "Hello", resp.read().decode()):
|
||||
if not test_val('data', 'Hello', resp.read().decode()):
|
||||
conn.close()
|
||||
return False
|
||||
Utility.console_log("Success")
|
||||
Utility.console_log('Success')
|
||||
conn.close()
|
||||
return True
|
||||
|
||||
|
||||
def put_echo(dut, port):
|
||||
# PUT /echo echoes data'
|
||||
Utility.console_log("[test] PUT /echo echoes data =>", end=' ')
|
||||
Utility.console_log('[test] PUT /echo echoes data =>', end=' ')
|
||||
conn = http.client.HTTPConnection(dut, int(port), timeout=15)
|
||||
conn.request("PUT", "/echo", "Hello")
|
||||
conn.request('PUT', '/echo', 'Hello')
|
||||
resp = conn.getresponse()
|
||||
if not test_val("status_code", 200, resp.status):
|
||||
if not test_val('status_code', 200, resp.status):
|
||||
conn.close()
|
||||
return False
|
||||
if not test_val("data", "Hello", resp.read().decode()):
|
||||
if not test_val('data', 'Hello', resp.read().decode()):
|
||||
conn.close()
|
||||
return False
|
||||
Utility.console_log("Success")
|
||||
Utility.console_log('Success')
|
||||
conn.close()
|
||||
return True
|
||||
|
||||
|
||||
def get_echo(dut, port):
|
||||
# GET /echo returns 404'
|
||||
Utility.console_log("[test] GET /echo returns 405 =>", end=' ')
|
||||
Utility.console_log('[test] GET /echo returns 405 =>', end=' ')
|
||||
conn = http.client.HTTPConnection(dut, int(port), timeout=15)
|
||||
conn.request("GET", "/echo")
|
||||
conn.request('GET', '/echo')
|
||||
resp = conn.getresponse()
|
||||
if not test_val("status_code", 405, resp.status):
|
||||
if not test_val('status_code', 405, resp.status):
|
||||
conn.close()
|
||||
return False
|
||||
Utility.console_log("Success")
|
||||
Utility.console_log('Success')
|
||||
conn.close()
|
||||
return True
|
||||
|
||||
|
||||
def get_test_headers(dut, port):
|
||||
# GET /test_header returns data of Header2'
|
||||
Utility.console_log("[test] GET /test_header =>", end=' ')
|
||||
Utility.console_log('[test] GET /test_header =>', end=' ')
|
||||
conn = http.client.HTTPConnection(dut, int(port), timeout=15)
|
||||
custom_header = {"Header1": "Value1", "Header3": "Value3"}
|
||||
header2_values = ["", " ", "Value2", " Value2", "Value2 ", " Value2 "]
|
||||
custom_header = {'Header1': 'Value1', 'Header3': 'Value3'}
|
||||
header2_values = ['', ' ', 'Value2', ' Value2', 'Value2 ', ' Value2 ']
|
||||
for val in header2_values:
|
||||
custom_header["Header2"] = val
|
||||
conn.request("GET", "/test_header", headers=custom_header)
|
||||
custom_header['Header2'] = val
|
||||
conn.request('GET', '/test_header', headers=custom_header)
|
||||
resp = conn.getresponse()
|
||||
if not test_val("status_code", 200, resp.status):
|
||||
if not test_val('status_code', 200, resp.status):
|
||||
conn.close()
|
||||
return False
|
||||
hdr_val_start_idx = val.find("Value2")
|
||||
hdr_val_start_idx = val.find('Value2')
|
||||
if hdr_val_start_idx == -1:
|
||||
if not test_val("header: Header2", "", resp.read().decode()):
|
||||
if not test_val('header: Header2', '', resp.read().decode()):
|
||||
conn.close()
|
||||
return False
|
||||
else:
|
||||
if not test_val("header: Header2", val[hdr_val_start_idx:], resp.read().decode()):
|
||||
if not test_val('header: Header2', val[hdr_val_start_idx:], resp.read().decode()):
|
||||
conn.close()
|
||||
return False
|
||||
resp.read()
|
||||
Utility.console_log("Success")
|
||||
Utility.console_log('Success')
|
||||
conn.close()
|
||||
return True
|
||||
|
||||
|
||||
def get_hello_type(dut, port):
|
||||
# GET /hello/type_html returns text/html as Content-Type'
|
||||
Utility.console_log("[test] GET /hello/type_html has Content-Type of text/html =>", end=' ')
|
||||
Utility.console_log('[test] GET /hello/type_html has Content-Type of text/html =>', end=' ')
|
||||
conn = http.client.HTTPConnection(dut, int(port), timeout=15)
|
||||
conn.request("GET", "/hello/type_html")
|
||||
conn.request('GET', '/hello/type_html')
|
||||
resp = conn.getresponse()
|
||||
if not test_val("status_code", 200, resp.status):
|
||||
if not test_val('status_code', 200, resp.status):
|
||||
conn.close()
|
||||
return False
|
||||
if not test_val("data", "Hello World!", resp.read().decode()):
|
||||
if not test_val('data', 'Hello World!', resp.read().decode()):
|
||||
conn.close()
|
||||
return False
|
||||
if not test_val("data", "text/html", resp.getheader('Content-Type')):
|
||||
if not test_val('data', 'text/html', resp.getheader('Content-Type')):
|
||||
conn.close()
|
||||
return False
|
||||
Utility.console_log("Success")
|
||||
Utility.console_log('Success')
|
||||
conn.close()
|
||||
return True
|
||||
|
||||
|
||||
def get_hello_status(dut, port):
|
||||
# GET /hello/status_500 returns status 500'
|
||||
Utility.console_log("[test] GET /hello/status_500 returns status 500 =>", end=' ')
|
||||
Utility.console_log('[test] GET /hello/status_500 returns status 500 =>', end=' ')
|
||||
conn = http.client.HTTPConnection(dut, int(port), timeout=15)
|
||||
conn.request("GET", "/hello/status_500")
|
||||
conn.request('GET', '/hello/status_500')
|
||||
resp = conn.getresponse()
|
||||
if not test_val("status_code", 500, resp.status):
|
||||
if not test_val('status_code', 500, resp.status):
|
||||
conn.close()
|
||||
return False
|
||||
Utility.console_log("Success")
|
||||
Utility.console_log('Success')
|
||||
conn.close()
|
||||
return True
|
||||
|
||||
|
||||
def get_false_uri(dut, port):
|
||||
# GET /false_uri returns status 404'
|
||||
Utility.console_log("[test] GET /false_uri returns status 404 =>", end=' ')
|
||||
Utility.console_log('[test] GET /false_uri returns status 404 =>', end=' ')
|
||||
conn = http.client.HTTPConnection(dut, int(port), timeout=15)
|
||||
conn.request("GET", "/false_uri")
|
||||
conn.request('GET', '/false_uri')
|
||||
resp = conn.getresponse()
|
||||
if not test_val("status_code", 404, resp.status):
|
||||
if not test_val('status_code', 404, resp.status):
|
||||
conn.close()
|
||||
return False
|
||||
Utility.console_log("Success")
|
||||
Utility.console_log('Success')
|
||||
conn.close()
|
||||
return True
|
||||
|
||||
|
||||
def parallel_sessions_adder(dut, port, max_sessions):
|
||||
# POSTs on /adder in parallel sessions
|
||||
Utility.console_log("[test] POST {pipelined} on /adder in " + str(max_sessions) + " sessions =>", end=' ')
|
||||
Utility.console_log('[test] POST {pipelined} on /adder in ' + str(max_sessions) + ' sessions =>', end=' ')
|
||||
t = []
|
||||
# Create all sessions
|
||||
for i in range(max_sessions):
|
||||
@@ -520,90 +518,90 @@ def parallel_sessions_adder(dut, port, max_sessions):
|
||||
|
||||
res = True
|
||||
for i in range(len(t)):
|
||||
if not test_val("Thread" + str(i) + " Failed", t[i].adder_result(), True):
|
||||
if not test_val('Thread' + str(i) + ' Failed', t[i].adder_result(), True):
|
||||
res = False
|
||||
t[i].close()
|
||||
if (res):
|
||||
Utility.console_log("Success")
|
||||
Utility.console_log('Success')
|
||||
return res
|
||||
|
||||
|
||||
def async_response_test(dut, port):
|
||||
# Test that an asynchronous work is executed in the HTTPD's context
|
||||
# This is tested by reading two responses over the same session
|
||||
Utility.console_log("[test] Test HTTPD Work Queue (Async response) =>", end=' ')
|
||||
Utility.console_log('[test] Test HTTPD Work Queue (Async response) =>', end=' ')
|
||||
s = Session(dut, port)
|
||||
|
||||
s.send_get('/async_data')
|
||||
s.read_resp_hdrs()
|
||||
if not test_val("First Response", "Hello World!", s.read_resp_data()):
|
||||
if not test_val('First Response', 'Hello World!', s.read_resp_data()):
|
||||
s.close()
|
||||
return False
|
||||
s.read_resp_hdrs()
|
||||
if not test_val("Second Response", "Hello Double World!", s.read_resp_data()):
|
||||
if not test_val('Second Response', 'Hello Double World!', s.read_resp_data()):
|
||||
s.close()
|
||||
return False
|
||||
s.close()
|
||||
Utility.console_log("Success")
|
||||
Utility.console_log('Success')
|
||||
return True
|
||||
|
||||
|
||||
def leftover_data_test(dut, port):
|
||||
# Leftover data in POST is purged (valid and invalid URIs)
|
||||
Utility.console_log("[test] Leftover data in POST is purged (valid and invalid URIs) =>", end=' ')
|
||||
s = http.client.HTTPConnection(dut + ":" + port, timeout=15)
|
||||
Utility.console_log('[test] Leftover data in POST is purged (valid and invalid URIs) =>', end=' ')
|
||||
s = http.client.HTTPConnection(dut + ':' + port, timeout=15)
|
||||
|
||||
s.request("POST", url='/leftover_data', body="abcdefghijklmnopqrstuvwxyz\r\nabcdefghijklmnopqrstuvwxyz")
|
||||
s.request('POST', url='/leftover_data', body='abcdefghijklmnopqrstuvwxyz\r\nabcdefghijklmnopqrstuvwxyz')
|
||||
resp = s.getresponse()
|
||||
if not test_val("Partial data", "abcdefghij", resp.read().decode()):
|
||||
if not test_val('Partial data', 'abcdefghij', resp.read().decode()):
|
||||
s.close()
|
||||
return False
|
||||
|
||||
s.request("GET", url='/hello')
|
||||
s.request('GET', url='/hello')
|
||||
resp = s.getresponse()
|
||||
if not test_val("Hello World Data", "Hello World!", resp.read().decode()):
|
||||
if not test_val('Hello World Data', 'Hello World!', resp.read().decode()):
|
||||
s.close()
|
||||
return False
|
||||
|
||||
s.request("POST", url='/false_uri', body="abcdefghijklmnopqrstuvwxyz\r\nabcdefghijklmnopqrstuvwxyz")
|
||||
s.request('POST', url='/false_uri', body='abcdefghijklmnopqrstuvwxyz\r\nabcdefghijklmnopqrstuvwxyz')
|
||||
resp = s.getresponse()
|
||||
if not test_val("False URI Status", str(404), str(resp.status)):
|
||||
if not test_val('False URI Status', str(404), str(resp.status)):
|
||||
s.close()
|
||||
return False
|
||||
# socket would have been closed by server due to error
|
||||
s.close()
|
||||
|
||||
s = http.client.HTTPConnection(dut + ":" + port, timeout=15)
|
||||
s.request("GET", url='/hello')
|
||||
s = http.client.HTTPConnection(dut + ':' + port, timeout=15)
|
||||
s.request('GET', url='/hello')
|
||||
resp = s.getresponse()
|
||||
if not test_val("Hello World Data", "Hello World!", resp.read().decode()):
|
||||
if not test_val('Hello World Data', 'Hello World!', resp.read().decode()):
|
||||
s.close()
|
||||
return False
|
||||
|
||||
s.close()
|
||||
Utility.console_log("Success")
|
||||
Utility.console_log('Success')
|
||||
return True
|
||||
|
||||
|
||||
def spillover_session(dut, port, max_sess):
|
||||
# Session max_sess_sessions + 1 is rejected
|
||||
Utility.console_log("[test] Session max_sess_sessions (" + str(max_sess) + ") + 1 is rejected =>", end=' ')
|
||||
Utility.console_log('[test] Session max_sess_sessions (' + str(max_sess) + ') + 1 is rejected =>', end=' ')
|
||||
s = []
|
||||
_verbose_ = True
|
||||
for i in range(max_sess + 1):
|
||||
if (_verbose_):
|
||||
Utility.console_log("Executing " + str(i))
|
||||
Utility.console_log('Executing ' + str(i))
|
||||
try:
|
||||
a = http.client.HTTPConnection(dut + ":" + port, timeout=15)
|
||||
a.request("GET", url='/hello')
|
||||
a = http.client.HTTPConnection(dut + ':' + port, timeout=15)
|
||||
a.request('GET', url='/hello')
|
||||
resp = a.getresponse()
|
||||
if not test_val("Connection " + str(i), "Hello World!", resp.read().decode()):
|
||||
if not test_val('Connection ' + str(i), 'Hello World!', resp.read().decode()):
|
||||
a.close()
|
||||
break
|
||||
s.append(a)
|
||||
except Exception:
|
||||
if (_verbose_):
|
||||
Utility.console_log("Connection " + str(i) + " rejected")
|
||||
Utility.console_log('Connection ' + str(i) + ' rejected')
|
||||
a.close()
|
||||
break
|
||||
|
||||
@@ -612,134 +610,134 @@ def spillover_session(dut, port, max_sess):
|
||||
a.close()
|
||||
|
||||
# Check if number of connections is equal to max_sess
|
||||
Utility.console_log(["Fail","Success"][len(s) == max_sess])
|
||||
Utility.console_log(['Fail','Success'][len(s) == max_sess])
|
||||
return (len(s) == max_sess)
|
||||
|
||||
|
||||
def recv_timeout_test(dut, port):
|
||||
Utility.console_log("[test] Timeout occurs if partial packet sent =>", end=' ')
|
||||
Utility.console_log('[test] Timeout occurs if partial packet sent =>', end=' ')
|
||||
s = Session(dut, port)
|
||||
s.client.sendall(b"GE")
|
||||
s.client.sendall(b'GE')
|
||||
s.read_resp_hdrs()
|
||||
resp = s.read_resp_data()
|
||||
if not test_val("Request Timeout", "Server closed this connection", resp):
|
||||
if not test_val('Request Timeout', 'Server closed this connection', resp):
|
||||
s.close()
|
||||
return False
|
||||
s.close()
|
||||
Utility.console_log("Success")
|
||||
Utility.console_log('Success')
|
||||
return True
|
||||
|
||||
|
||||
def packet_size_limit_test(dut, port, test_size):
|
||||
Utility.console_log("[test] send size limit test =>", end=' ')
|
||||
Utility.console_log('[test] send size limit test =>', end=' ')
|
||||
retry = 5
|
||||
while (retry):
|
||||
retry -= 1
|
||||
Utility.console_log("data size = ", test_size)
|
||||
s = http.client.HTTPConnection(dut + ":" + port, timeout=15)
|
||||
Utility.console_log('data size = ', test_size)
|
||||
s = http.client.HTTPConnection(dut + ':' + port, timeout=15)
|
||||
random_data = ''.join(string.printable[random.randint(0,len(string.printable)) - 1] for _ in list(range(test_size)))
|
||||
path = "/echo"
|
||||
s.request("POST", url=path, body=random_data)
|
||||
path = '/echo'
|
||||
s.request('POST', url=path, body=random_data)
|
||||
resp = s.getresponse()
|
||||
if not test_val("Error", "200", str(resp.status)):
|
||||
if test_val("Error", "500", str(resp.status)):
|
||||
Utility.console_log("Data too large to be allocated")
|
||||
if not test_val('Error', '200', str(resp.status)):
|
||||
if test_val('Error', '500', str(resp.status)):
|
||||
Utility.console_log('Data too large to be allocated')
|
||||
test_size = test_size // 10
|
||||
else:
|
||||
Utility.console_log("Unexpected error")
|
||||
Utility.console_log('Unexpected error')
|
||||
s.close()
|
||||
Utility.console_log("Retry...")
|
||||
Utility.console_log('Retry...')
|
||||
continue
|
||||
resp = resp.read().decode()
|
||||
result = (resp == random_data)
|
||||
if not result:
|
||||
test_val("Data size", str(len(random_data)), str(len(resp)))
|
||||
test_val('Data size', str(len(random_data)), str(len(resp)))
|
||||
s.close()
|
||||
Utility.console_log("Retry...")
|
||||
Utility.console_log('Retry...')
|
||||
continue
|
||||
s.close()
|
||||
Utility.console_log("Success")
|
||||
Utility.console_log('Success')
|
||||
return True
|
||||
Utility.console_log("Failed")
|
||||
Utility.console_log('Failed')
|
||||
return False
|
||||
|
||||
|
||||
def arbitrary_termination_test(dut, port):
|
||||
Utility.console_log("[test] Arbitrary termination test =>", end=' ')
|
||||
Utility.console_log('[test] Arbitrary termination test =>', end=' ')
|
||||
cases = [
|
||||
{
|
||||
"request": "POST /echo HTTP/1.1\r\nHost: " + dut + "\r\nCustom: SomeValue\r\n\r\n",
|
||||
"code": "200",
|
||||
"header": "SomeValue"
|
||||
'request': 'POST /echo HTTP/1.1\r\nHost: ' + dut + '\r\nCustom: SomeValue\r\n\r\n',
|
||||
'code': '200',
|
||||
'header': 'SomeValue'
|
||||
},
|
||||
{
|
||||
"request": "POST /echo HTTP/1.1\nHost: " + dut + "\r\nCustom: SomeValue\r\n\r\n",
|
||||
"code": "200",
|
||||
"header": "SomeValue"
|
||||
'request': 'POST /echo HTTP/1.1\nHost: ' + dut + '\r\nCustom: SomeValue\r\n\r\n',
|
||||
'code': '200',
|
||||
'header': 'SomeValue'
|
||||
},
|
||||
{
|
||||
"request": "POST /echo HTTP/1.1\r\nHost: " + dut + "\nCustom: SomeValue\r\n\r\n",
|
||||
"code": "200",
|
||||
"header": "SomeValue"
|
||||
'request': 'POST /echo HTTP/1.1\r\nHost: ' + dut + '\nCustom: SomeValue\r\n\r\n',
|
||||
'code': '200',
|
||||
'header': 'SomeValue'
|
||||
},
|
||||
{
|
||||
"request": "POST /echo HTTP/1.1\r\nHost: " + dut + "\r\nCustom: SomeValue\n\r\n",
|
||||
"code": "200",
|
||||
"header": "SomeValue"
|
||||
'request': 'POST /echo HTTP/1.1\r\nHost: ' + dut + '\r\nCustom: SomeValue\n\r\n',
|
||||
'code': '200',
|
||||
'header': 'SomeValue'
|
||||
},
|
||||
{
|
||||
"request": "POST /echo HTTP/1.1\r\nHost: " + dut + "\r\nCustom: SomeValue\r\n\n",
|
||||
"code": "200",
|
||||
"header": "SomeValue"
|
||||
'request': 'POST /echo HTTP/1.1\r\nHost: ' + dut + '\r\nCustom: SomeValue\r\n\n',
|
||||
'code': '200',
|
||||
'header': 'SomeValue'
|
||||
},
|
||||
{
|
||||
"request": "POST /echo HTTP/1.1\nHost: " + dut + "\nCustom: SomeValue\n\n",
|
||||
"code": "200",
|
||||
"header": "SomeValue"
|
||||
'request': 'POST /echo HTTP/1.1\nHost: ' + dut + '\nCustom: SomeValue\n\n',
|
||||
'code': '200',
|
||||
'header': 'SomeValue'
|
||||
},
|
||||
{
|
||||
"request": "POST /echo HTTP/1.1\r\nHost: " + dut + "\r\nContent-Length: 5\n\r\nABCDE",
|
||||
"code": "200",
|
||||
"body": "ABCDE"
|
||||
'request': 'POST /echo HTTP/1.1\r\nHost: ' + dut + '\r\nContent-Length: 5\n\r\nABCDE',
|
||||
'code': '200',
|
||||
'body': 'ABCDE'
|
||||
},
|
||||
{
|
||||
"request": "POST /echo HTTP/1.1\r\nHost: " + dut + "\r\nContent-Length: 5\r\n\nABCDE",
|
||||
"code": "200",
|
||||
"body": "ABCDE"
|
||||
'request': 'POST /echo HTTP/1.1\r\nHost: ' + dut + '\r\nContent-Length: 5\r\n\nABCDE',
|
||||
'code': '200',
|
||||
'body': 'ABCDE'
|
||||
},
|
||||
{
|
||||
"request": "POST /echo HTTP/1.1\r\nHost: " + dut + "\r\nContent-Length: 5\n\nABCDE",
|
||||
"code": "200",
|
||||
"body": "ABCDE"
|
||||
'request': 'POST /echo HTTP/1.1\r\nHost: ' + dut + '\r\nContent-Length: 5\n\nABCDE',
|
||||
'code': '200',
|
||||
'body': 'ABCDE'
|
||||
},
|
||||
{
|
||||
"request": "POST /echo HTTP/1.1\r\nHost: " + dut + "\r\nContent-Length: 5\n\n\rABCD",
|
||||
"code": "200",
|
||||
"body": "\rABCD"
|
||||
'request': 'POST /echo HTTP/1.1\r\nHost: ' + dut + '\r\nContent-Length: 5\n\n\rABCD',
|
||||
'code': '200',
|
||||
'body': '\rABCD'
|
||||
},
|
||||
{
|
||||
"request": "POST /echo HTTP/1.1\r\nHost: " + dut + "\r\r\nCustom: SomeValue\r\r\n\r\r\n",
|
||||
"code": "400"
|
||||
'request': 'POST /echo HTTP/1.1\r\nHost: ' + dut + '\r\r\nCustom: SomeValue\r\r\n\r\r\n',
|
||||
'code': '400'
|
||||
},
|
||||
{
|
||||
"request": "POST /echo HTTP/1.1\r\r\nHost: " + dut + "\r\n\r\n",
|
||||
"code": "400"
|
||||
'request': 'POST /echo HTTP/1.1\r\r\nHost: ' + dut + '\r\n\r\n',
|
||||
'code': '400'
|
||||
},
|
||||
{
|
||||
"request": "POST /echo HTTP/1.1\r\n\rHost: " + dut + "\r\n\r\n",
|
||||
"code": "400"
|
||||
'request': 'POST /echo HTTP/1.1\r\n\rHost: ' + dut + '\r\n\r\n',
|
||||
'code': '400'
|
||||
},
|
||||
{
|
||||
"request": "POST /echo HTTP/1.1\r\nHost: " + dut + "\rCustom: SomeValue\r\n",
|
||||
"code": "400"
|
||||
'request': 'POST /echo HTTP/1.1\r\nHost: ' + dut + '\rCustom: SomeValue\r\n',
|
||||
'code': '400'
|
||||
},
|
||||
{
|
||||
"request": "POST /echo HTTP/1.1\r\nHost: " + dut + "\r\nCustom: Some\rValue\r\n",
|
||||
"code": "400"
|
||||
'request': 'POST /echo HTTP/1.1\r\nHost: ' + dut + '\r\nCustom: Some\rValue\r\n',
|
||||
'code': '400'
|
||||
},
|
||||
{
|
||||
"request": "POST /echo HTTP/1.1\r\nHost: " + dut + "\r\nCustom- SomeValue\r\n\r\n",
|
||||
"code": "400"
|
||||
'request': 'POST /echo HTTP/1.1\r\nHost: ' + dut + '\r\nCustom- SomeValue\r\n\r\n',
|
||||
'code': '400'
|
||||
}
|
||||
]
|
||||
for case in cases:
|
||||
@@ -748,159 +746,159 @@ def arbitrary_termination_test(dut, port):
|
||||
resp_hdrs = s.read_resp_hdrs()
|
||||
resp_body = s.read_resp_data()
|
||||
s.close()
|
||||
if not test_val("Response Code", case["code"], s.status):
|
||||
if not test_val('Response Code', case['code'], s.status):
|
||||
return False
|
||||
if "header" in case.keys():
|
||||
if 'header' in case.keys():
|
||||
resp_hdr_val = None
|
||||
if "Custom" in resp_hdrs.keys():
|
||||
resp_hdr_val = resp_hdrs["Custom"]
|
||||
if not test_val("Response Header", case["header"], resp_hdr_val):
|
||||
if 'Custom' in resp_hdrs.keys():
|
||||
resp_hdr_val = resp_hdrs['Custom']
|
||||
if not test_val('Response Header', case['header'], resp_hdr_val):
|
||||
return False
|
||||
if "body" in case.keys():
|
||||
if not test_val("Response Body", case["body"], resp_body):
|
||||
if 'body' in case.keys():
|
||||
if not test_val('Response Body', case['body'], resp_body):
|
||||
return False
|
||||
Utility.console_log("Success")
|
||||
Utility.console_log('Success')
|
||||
return True
|
||||
|
||||
|
||||
def code_500_server_error_test(dut, port):
|
||||
Utility.console_log("[test] 500 Server Error test =>", end=' ')
|
||||
Utility.console_log('[test] 500 Server Error test =>', end=' ')
|
||||
s = Session(dut, port)
|
||||
# Sending a very large content length will cause malloc to fail
|
||||
content_len = 2**30
|
||||
s.client.sendall(("POST /echo HTTP/1.1\r\nHost: " + dut + "\r\nContent-Length: " + str(content_len) + "\r\n\r\nABCD").encode())
|
||||
s.client.sendall(('POST /echo HTTP/1.1\r\nHost: ' + dut + '\r\nContent-Length: ' + str(content_len) + '\r\n\r\nABCD').encode())
|
||||
s.read_resp_hdrs()
|
||||
s.read_resp_data()
|
||||
if not test_val("Server Error", "500", s.status):
|
||||
if not test_val('Server Error', '500', s.status):
|
||||
s.close()
|
||||
return False
|
||||
s.close()
|
||||
Utility.console_log("Success")
|
||||
Utility.console_log('Success')
|
||||
return True
|
||||
|
||||
|
||||
def code_501_method_not_impl(dut, port):
|
||||
Utility.console_log("[test] 501 Method Not Implemented =>", end=' ')
|
||||
Utility.console_log('[test] 501 Method Not Implemented =>', end=' ')
|
||||
s = Session(dut, port)
|
||||
path = "/hello"
|
||||
s.client.sendall(("ABC " + path + " HTTP/1.1\r\nHost: " + dut + "\r\n\r\n").encode())
|
||||
path = '/hello'
|
||||
s.client.sendall(('ABC ' + path + ' HTTP/1.1\r\nHost: ' + dut + '\r\n\r\n').encode())
|
||||
s.read_resp_hdrs()
|
||||
s.read_resp_data()
|
||||
# Presently server sends back 400 Bad Request
|
||||
# if not test_val("Server Error", "501", s.status):
|
||||
# s.close()
|
||||
# return False
|
||||
if not test_val("Server Error", "400", s.status):
|
||||
if not test_val('Server Error', '400', s.status):
|
||||
s.close()
|
||||
return False
|
||||
s.close()
|
||||
Utility.console_log("Success")
|
||||
Utility.console_log('Success')
|
||||
return True
|
||||
|
||||
|
||||
def code_505_version_not_supported(dut, port):
|
||||
Utility.console_log("[test] 505 Version Not Supported =>", end=' ')
|
||||
Utility.console_log('[test] 505 Version Not Supported =>', end=' ')
|
||||
s = Session(dut, port)
|
||||
path = "/hello"
|
||||
s.client.sendall(("GET " + path + " HTTP/2.0\r\nHost: " + dut + "\r\n\r\n").encode())
|
||||
path = '/hello'
|
||||
s.client.sendall(('GET ' + path + ' HTTP/2.0\r\nHost: ' + dut + '\r\n\r\n').encode())
|
||||
s.read_resp_hdrs()
|
||||
s.read_resp_data()
|
||||
if not test_val("Server Error", "505", s.status):
|
||||
if not test_val('Server Error', '505', s.status):
|
||||
s.close()
|
||||
return False
|
||||
s.close()
|
||||
Utility.console_log("Success")
|
||||
Utility.console_log('Success')
|
||||
return True
|
||||
|
||||
|
||||
def code_400_bad_request(dut, port):
|
||||
Utility.console_log("[test] 400 Bad Request =>", end=' ')
|
||||
Utility.console_log('[test] 400 Bad Request =>', end=' ')
|
||||
s = Session(dut, port)
|
||||
path = "/hello"
|
||||
s.client.sendall(("XYZ " + path + " HTTP/1.1\r\nHost: " + dut + "\r\n\r\n").encode())
|
||||
path = '/hello'
|
||||
s.client.sendall(('XYZ ' + path + ' HTTP/1.1\r\nHost: ' + dut + '\r\n\r\n').encode())
|
||||
s.read_resp_hdrs()
|
||||
s.read_resp_data()
|
||||
if not test_val("Client Error", "400", s.status):
|
||||
if not test_val('Client Error', '400', s.status):
|
||||
s.close()
|
||||
return False
|
||||
s.close()
|
||||
Utility.console_log("Success")
|
||||
Utility.console_log('Success')
|
||||
return True
|
||||
|
||||
|
||||
def code_404_not_found(dut, port):
|
||||
Utility.console_log("[test] 404 Not Found =>", end=' ')
|
||||
Utility.console_log('[test] 404 Not Found =>', end=' ')
|
||||
s = Session(dut, port)
|
||||
path = "/dummy"
|
||||
s.client.sendall(("GET " + path + " HTTP/1.1\r\nHost: " + dut + "\r\n\r\n").encode())
|
||||
path = '/dummy'
|
||||
s.client.sendall(('GET ' + path + ' HTTP/1.1\r\nHost: ' + dut + '\r\n\r\n').encode())
|
||||
s.read_resp_hdrs()
|
||||
s.read_resp_data()
|
||||
if not test_val("Client Error", "404", s.status):
|
||||
if not test_val('Client Error', '404', s.status):
|
||||
s.close()
|
||||
return False
|
||||
s.close()
|
||||
Utility.console_log("Success")
|
||||
Utility.console_log('Success')
|
||||
return True
|
||||
|
||||
|
||||
def code_405_method_not_allowed(dut, port):
|
||||
Utility.console_log("[test] 405 Method Not Allowed =>", end=' ')
|
||||
Utility.console_log('[test] 405 Method Not Allowed =>', end=' ')
|
||||
s = Session(dut, port)
|
||||
path = "/hello"
|
||||
s.client.sendall(("POST " + path + " HTTP/1.1\r\nHost: " + dut + "\r\n\r\n").encode())
|
||||
path = '/hello'
|
||||
s.client.sendall(('POST ' + path + ' HTTP/1.1\r\nHost: ' + dut + '\r\n\r\n').encode())
|
||||
s.read_resp_hdrs()
|
||||
s.read_resp_data()
|
||||
if not test_val("Client Error", "405", s.status):
|
||||
if not test_val('Client Error', '405', s.status):
|
||||
s.close()
|
||||
return False
|
||||
s.close()
|
||||
Utility.console_log("Success")
|
||||
Utility.console_log('Success')
|
||||
return True
|
||||
|
||||
|
||||
def code_408_req_timeout(dut, port):
|
||||
Utility.console_log("[test] 408 Request Timeout =>", end=' ')
|
||||
Utility.console_log('[test] 408 Request Timeout =>', end=' ')
|
||||
s = Session(dut, port)
|
||||
s.client.sendall(("POST /echo HTTP/1.1\r\nHost: " + dut + "\r\nContent-Length: 10\r\n\r\nABCD").encode())
|
||||
s.client.sendall(('POST /echo HTTP/1.1\r\nHost: ' + dut + '\r\nContent-Length: 10\r\n\r\nABCD').encode())
|
||||
s.read_resp_hdrs()
|
||||
s.read_resp_data()
|
||||
if not test_val("Client Error", "408", s.status):
|
||||
if not test_val('Client Error', '408', s.status):
|
||||
s.close()
|
||||
return False
|
||||
s.close()
|
||||
Utility.console_log("Success")
|
||||
Utility.console_log('Success')
|
||||
return True
|
||||
|
||||
|
||||
def code_411_length_required(dut, port):
|
||||
Utility.console_log("[test] 411 Length Required =>", end=' ')
|
||||
Utility.console_log('[test] 411 Length Required =>', end=' ')
|
||||
s = Session(dut, port)
|
||||
path = "/echo"
|
||||
s.client.sendall(("POST " + path + " HTTP/1.1\r\nHost: " + dut + "\r\nContent-Type: text/plain\r\nTransfer-Encoding: chunked\r\n\r\n").encode())
|
||||
path = '/echo'
|
||||
s.client.sendall(('POST ' + path + ' HTTP/1.1\r\nHost: ' + dut + '\r\nContent-Type: text/plain\r\nTransfer-Encoding: chunked\r\n\r\n').encode())
|
||||
s.read_resp_hdrs()
|
||||
s.read_resp_data()
|
||||
# Presently server sends back 400 Bad Request
|
||||
# if not test_val("Client Error", "411", s.status):
|
||||
# s.close()
|
||||
# return False
|
||||
if not test_val("Client Error", "400", s.status):
|
||||
if not test_val('Client Error', '400', s.status):
|
||||
s.close()
|
||||
return False
|
||||
s.close()
|
||||
Utility.console_log("Success")
|
||||
Utility.console_log('Success')
|
||||
return True
|
||||
|
||||
|
||||
def send_getx_uri_len(dut, port, length):
|
||||
s = Session(dut, port)
|
||||
method = "GET "
|
||||
version = " HTTP/1.1\r\n"
|
||||
path = "/" + "x" * (length - len(method) - len(version) - len("/"))
|
||||
method = 'GET '
|
||||
version = ' HTTP/1.1\r\n'
|
||||
path = '/' + 'x' * (length - len(method) - len(version) - len('/'))
|
||||
s.client.sendall(method.encode())
|
||||
time.sleep(1)
|
||||
s.client.sendall(path.encode())
|
||||
time.sleep(1)
|
||||
s.client.sendall((version + "Host: " + dut + "\r\n\r\n").encode())
|
||||
s.client.sendall((version + 'Host: ' + dut + '\r\n\r\n').encode())
|
||||
s.read_resp_hdrs()
|
||||
s.read_resp_data()
|
||||
s.close()
|
||||
@@ -908,59 +906,59 @@ def send_getx_uri_len(dut, port, length):
|
||||
|
||||
|
||||
def code_414_uri_too_long(dut, port, max_uri_len):
|
||||
Utility.console_log("[test] 414 URI Too Long =>", end=' ')
|
||||
Utility.console_log('[test] 414 URI Too Long =>', end=' ')
|
||||
status = send_getx_uri_len(dut, port, max_uri_len)
|
||||
if not test_val("Client Error", "404", status):
|
||||
if not test_val('Client Error', '404', status):
|
||||
return False
|
||||
status = send_getx_uri_len(dut, port, max_uri_len + 1)
|
||||
if not test_val("Client Error", "414", status):
|
||||
if not test_val('Client Error', '414', status):
|
||||
return False
|
||||
Utility.console_log("Success")
|
||||
Utility.console_log('Success')
|
||||
return True
|
||||
|
||||
|
||||
def send_postx_hdr_len(dut, port, length):
|
||||
s = Session(dut, port)
|
||||
path = "/echo"
|
||||
host = "Host: " + dut
|
||||
custom_hdr_field = "\r\nCustom: "
|
||||
custom_hdr_val = "x" * (length - len(host) - len(custom_hdr_field) - len("\r\n\r\n") + len("0"))
|
||||
request = ("POST " + path + " HTTP/1.1\r\n" + host + custom_hdr_field + custom_hdr_val + "\r\n\r\n").encode()
|
||||
path = '/echo'
|
||||
host = 'Host: ' + dut
|
||||
custom_hdr_field = '\r\nCustom: '
|
||||
custom_hdr_val = 'x' * (length - len(host) - len(custom_hdr_field) - len('\r\n\r\n') + len('0'))
|
||||
request = ('POST ' + path + ' HTTP/1.1\r\n' + host + custom_hdr_field + custom_hdr_val + '\r\n\r\n').encode()
|
||||
s.client.sendall(request[:length // 2])
|
||||
time.sleep(1)
|
||||
s.client.sendall(request[length // 2:])
|
||||
hdr = s.read_resp_hdrs()
|
||||
resp = s.read_resp_data()
|
||||
s.close()
|
||||
if hdr and ("Custom" in hdr):
|
||||
return (hdr["Custom"] == custom_hdr_val), resp
|
||||
if hdr and ('Custom' in hdr):
|
||||
return (hdr['Custom'] == custom_hdr_val), resp
|
||||
return False, s.status
|
||||
|
||||
|
||||
def code_431_hdr_too_long(dut, port, max_hdr_len):
|
||||
Utility.console_log("[test] 431 Header Too Long =>", end=' ')
|
||||
Utility.console_log('[test] 431 Header Too Long =>', end=' ')
|
||||
res, status = send_postx_hdr_len(dut, port, max_hdr_len)
|
||||
if not res:
|
||||
return False
|
||||
res, status = send_postx_hdr_len(dut, port, max_hdr_len + 1)
|
||||
if not test_val("Client Error", "431", status):
|
||||
if not test_val('Client Error', '431', status):
|
||||
return False
|
||||
Utility.console_log("Success")
|
||||
Utility.console_log('Success')
|
||||
return True
|
||||
|
||||
|
||||
def test_upgrade_not_supported(dut, port):
|
||||
Utility.console_log("[test] Upgrade Not Supported =>", end=' ')
|
||||
Utility.console_log('[test] Upgrade Not Supported =>', end=' ')
|
||||
s = Session(dut, port)
|
||||
# path = "/hello"
|
||||
s.client.sendall(("OPTIONS * HTTP/1.1\r\nHost:" + dut + "\r\nUpgrade: TLS/1.0\r\nConnection: Upgrade\r\n\r\n").encode())
|
||||
s.client.sendall(('OPTIONS * HTTP/1.1\r\nHost:' + dut + '\r\nUpgrade: TLS/1.0\r\nConnection: Upgrade\r\n\r\n').encode())
|
||||
s.read_resp_hdrs()
|
||||
s.read_resp_data()
|
||||
if not test_val("Client Error", "400", s.status):
|
||||
if not test_val('Client Error', '400', s.status):
|
||||
s.close()
|
||||
return False
|
||||
s.close()
|
||||
Utility.console_log("Success")
|
||||
Utility.console_log('Success')
|
||||
return True
|
||||
|
||||
|
||||
@@ -985,7 +983,7 @@ if __name__ == '__main__':
|
||||
|
||||
_verbose_ = True
|
||||
|
||||
Utility.console_log("### Basic HTTP Client Tests")
|
||||
Utility.console_log('### Basic HTTP Client Tests')
|
||||
get_hello(dut, port)
|
||||
post_hello(dut, port)
|
||||
put_hello(dut, port)
|
||||
@@ -997,7 +995,7 @@ if __name__ == '__main__':
|
||||
get_false_uri(dut, port)
|
||||
get_test_headers(dut, port)
|
||||
|
||||
Utility.console_log("### Error code tests")
|
||||
Utility.console_log('### Error code tests')
|
||||
code_500_server_error_test(dut, port)
|
||||
code_501_method_not_impl(dut, port)
|
||||
code_505_version_not_supported(dut, port)
|
||||
@@ -1012,7 +1010,7 @@ if __name__ == '__main__':
|
||||
# Not supported yet (Error on chunked request)
|
||||
# code_411_length_required(dut, port)
|
||||
|
||||
Utility.console_log("### Sessions and Context Tests")
|
||||
Utility.console_log('### Sessions and Context Tests')
|
||||
parallel_sessions_adder(dut, port, max_sessions)
|
||||
leftover_data_test(dut, port)
|
||||
async_response_test(dut, port)
|
||||
|
||||
@@ -18,9 +18,10 @@ Internal use only.
|
||||
This file provide method to control programmable attenuator.
|
||||
"""
|
||||
|
||||
import time
|
||||
import serial
|
||||
import codecs
|
||||
import time
|
||||
|
||||
import serial
|
||||
|
||||
|
||||
def set_att(port, att, att_fix=False):
|
||||
@@ -47,16 +48,16 @@ def set_att(port, att, att_fix=False):
|
||||
|
||||
serial_port = serial.Serial(port, baudrate=9600, rtscts=False, timeout=0.1)
|
||||
if serial_port.isOpen() is False:
|
||||
raise IOError("attenuator control, failed to open att port")
|
||||
raise IOError('attenuator control, failed to open att port')
|
||||
|
||||
cmd_hex = "7e7e10{:02x}{:x}".format(att_t, 0x10 + att_t)
|
||||
exp_res_hex = "7e7e20{:02x}00{:x}".format(att_t, 0x20 + att_t)
|
||||
cmd_hex = '7e7e10{:02x}{:x}'.format(att_t, 0x10 + att_t)
|
||||
exp_res_hex = '7e7e20{:02x}00{:x}'.format(att_t, 0x20 + att_t)
|
||||
|
||||
cmd = codecs.decode(cmd_hex, "hex")
|
||||
exp_res = codecs.decode(exp_res_hex, "hex")
|
||||
cmd = codecs.decode(cmd_hex, 'hex')
|
||||
exp_res = codecs.decode(exp_res_hex, 'hex')
|
||||
|
||||
serial_port.write(cmd)
|
||||
res = b""
|
||||
res = b''
|
||||
|
||||
for i in range(5):
|
||||
res += serial_port.read(20)
|
||||
|
||||
@@ -40,18 +40,18 @@ def draw_line_chart(file_name, title, x_label, y_label, data_series, range_list)
|
||||
_data[str(key)] = data_series[item][key]
|
||||
_data = list(_data.values())
|
||||
try:
|
||||
legend = item + " (max: {:.02f})".format(max([x for x in _data if x]))
|
||||
legend = item + ' (max: {:.02f})'.format(max([x for x in _data if x]))
|
||||
except TypeError:
|
||||
legend = item
|
||||
line.add_yaxis(legend, _data, is_smooth=True, is_connect_nones=True,
|
||||
label_opts=opts.LabelOpts(is_show=False))
|
||||
line.set_global_opts(
|
||||
datazoom_opts=opts.DataZoomOpts(range_start=0, range_end=100),
|
||||
title_opts=opts.TitleOpts(title=title, pos_left="center"),
|
||||
legend_opts=opts.LegendOpts(pos_top="10%", pos_left="right", orient="vertical"),
|
||||
tooltip_opts=opts.TooltipOpts(trigger="axis"),
|
||||
xaxis_opts=opts.AxisOpts(type_="category", name=x_label, splitline_opts=opts.SplitLineOpts(is_show=True)),
|
||||
yaxis_opts=opts.AxisOpts(type_="value", name=y_label,
|
||||
title_opts=opts.TitleOpts(title=title, pos_left='center'),
|
||||
legend_opts=opts.LegendOpts(pos_top='10%', pos_left='right', orient='vertical'),
|
||||
tooltip_opts=opts.TooltipOpts(trigger='axis'),
|
||||
xaxis_opts=opts.AxisOpts(type_='category', name=x_label, splitline_opts=opts.SplitLineOpts(is_show=True)),
|
||||
yaxis_opts=opts.AxisOpts(type_='value', name=y_label,
|
||||
axistick_opts=opts.AxisTickOpts(is_show=True),
|
||||
splitline_opts=opts.SplitLineOpts(is_show=True)),
|
||||
)
|
||||
|
||||
@@ -27,15 +27,15 @@ class Control(object):
|
||||
@classmethod
|
||||
def apc_telnet_make_choice(cls, telnet, choice):
|
||||
""" select a choice """
|
||||
telnet.read_until(b"Event Log")
|
||||
telnet.read_until(b">")
|
||||
telnet.write(choice.encode() + b"\r\n")
|
||||
telnet.read_until(b'Event Log')
|
||||
telnet.read_until(b'>')
|
||||
telnet.write(choice.encode() + b'\r\n')
|
||||
|
||||
@classmethod
|
||||
def apc_telnet_common_action(cls, telnet, check_str, action):
|
||||
""" wait until a pattern and then write a line """
|
||||
telnet.read_until(check_str.encode())
|
||||
telnet.write(action.encode() + b"\r\n")
|
||||
telnet.write(action.encode() + b'\r\n')
|
||||
|
||||
@classmethod
|
||||
def control(cls, apc_ip, control_dict):
|
||||
@@ -48,45 +48,45 @@ class Control(object):
|
||||
|
||||
for _outlet in control_dict:
|
||||
assert 0 < _outlet < 9
|
||||
assert control_dict[_outlet] in ["ON", "OFF"]
|
||||
assert control_dict[_outlet] in ['ON', 'OFF']
|
||||
|
||||
# telnet
|
||||
# set timeout as 2s so that it won't waste time even can't access APC
|
||||
tn = telnetlib.Telnet(host=apc_ip, timeout=5)
|
||||
# log on
|
||||
cls.apc_telnet_common_action(tn, "User Name :", "apc")
|
||||
cls.apc_telnet_common_action(tn, "Password :", "apc")
|
||||
cls.apc_telnet_common_action(tn, 'User Name :', 'apc')
|
||||
cls.apc_telnet_common_action(tn, 'Password :', 'apc')
|
||||
# go to Device Manager
|
||||
cls.apc_telnet_make_choice(tn, "1")
|
||||
cls.apc_telnet_make_choice(tn, '1')
|
||||
# go to Outlet Management
|
||||
cls.apc_telnet_make_choice(tn, "2")
|
||||
cls.apc_telnet_make_choice(tn, '2')
|
||||
# go to Outlet Control/Configuration
|
||||
cls.apc_telnet_make_choice(tn, "1")
|
||||
cls.apc_telnet_make_choice(tn, '1')
|
||||
|
||||
# do select Outlet and control
|
||||
for _outlet in control_dict:
|
||||
# choose Outlet
|
||||
cls.apc_telnet_make_choice(tn, str(_outlet))
|
||||
# choose Control Outlet
|
||||
cls.apc_telnet_make_choice(tn, "1")
|
||||
cls.apc_telnet_make_choice(tn, '1')
|
||||
# choose action
|
||||
_action = control_dict[_outlet]
|
||||
if "ON" in _action:
|
||||
cls.apc_telnet_make_choice(tn, "1")
|
||||
if 'ON' in _action:
|
||||
cls.apc_telnet_make_choice(tn, '1')
|
||||
else:
|
||||
cls.apc_telnet_make_choice(tn, "2")
|
||||
cls.apc_telnet_make_choice(tn, '2')
|
||||
# do confirm
|
||||
cls.apc_telnet_common_action(tn, "cancel :", "YES")
|
||||
cls.apc_telnet_common_action(tn, "continue...", "")
|
||||
cls.apc_telnet_common_action(tn, 'cancel :', 'YES')
|
||||
cls.apc_telnet_common_action(tn, 'continue...', '')
|
||||
# return to Outlet Control/Configuration
|
||||
cls.apc_telnet_make_choice(tn, "\033")
|
||||
cls.apc_telnet_make_choice(tn, "\033")
|
||||
cls.apc_telnet_make_choice(tn, '\033')
|
||||
cls.apc_telnet_make_choice(tn, '\033')
|
||||
|
||||
# exit to main menu and logout
|
||||
tn.write(b"\033\r\n")
|
||||
tn.write(b"\033\r\n")
|
||||
tn.write(b"\033\r\n")
|
||||
tn.write(b"4\r\n")
|
||||
tn.write(b'\033\r\n')
|
||||
tn.write(b'\033\r\n')
|
||||
tn.write(b'\033\r\n')
|
||||
tn.write(b'4\r\n')
|
||||
|
||||
@classmethod
|
||||
def control_rest(cls, apc_ip, outlet, action):
|
||||
|
||||
@@ -10,9 +10,9 @@ import os
|
||||
|
||||
|
||||
class ThroughputForConfigsReport(object):
|
||||
THROUGHPUT_TYPES = ["tcp_tx", "tcp_rx", "udp_tx", "udp_rx"]
|
||||
THROUGHPUT_TYPES = ['tcp_tx', 'tcp_rx', 'udp_tx', 'udp_rx']
|
||||
|
||||
REPORT_FILE_NAME = "ThroughputForConfigs.md"
|
||||
REPORT_FILE_NAME = 'ThroughputForConfigs.md'
|
||||
|
||||
def __init__(self, output_path, ap_ssid, throughput_results, sdkconfig_files):
|
||||
"""
|
||||
@@ -42,14 +42,14 @@ class ThroughputForConfigsReport(object):
|
||||
@staticmethod
|
||||
def _parse_config_file(config_file_path):
|
||||
sdkconfig = {}
|
||||
with open(config_file_path, "r") as f:
|
||||
with open(config_file_path, 'r') as f:
|
||||
for line in f:
|
||||
if not line.isspace():
|
||||
if line[0] == "#":
|
||||
if line[0] == '#':
|
||||
continue
|
||||
name, value = line.split("=")
|
||||
value = value.strip("\r\n")
|
||||
sdkconfig[name] = value if value else "n"
|
||||
name, value = line.split('=')
|
||||
value = value.strip('\r\n')
|
||||
sdkconfig[name] = value if value else 'n'
|
||||
return sdkconfig
|
||||
|
||||
def _generate_the_difference_between_configs(self):
|
||||
@@ -65,7 +65,7 @@ class ThroughputForConfigsReport(object):
|
||||
|
||||
"""
|
||||
|
||||
data = "## Config Definition:\r\n\r\n"
|
||||
data = '## Config Definition:\r\n\r\n'
|
||||
|
||||
def find_difference(base, new):
|
||||
_difference = {}
|
||||
@@ -75,13 +75,13 @@ class ThroughputForConfigsReport(object):
|
||||
try:
|
||||
_base_value = base[_config]
|
||||
except KeyError:
|
||||
_base_value = "null"
|
||||
_base_value = 'null'
|
||||
try:
|
||||
_new_value = new[_config]
|
||||
except KeyError:
|
||||
_new_value = "null"
|
||||
_new_value = 'null'
|
||||
if _base_value != _new_value:
|
||||
_difference[_config] = "{} -> {}".format(_base_value, _new_value)
|
||||
_difference[_config] = '{} -> {}'.format(_base_value, _new_value)
|
||||
return _difference
|
||||
|
||||
for i, _config_name in enumerate(self.sort_order):
|
||||
@@ -96,9 +96,9 @@ class ThroughputForConfigsReport(object):
|
||||
if previous_config:
|
||||
# log the difference
|
||||
difference = find_difference(previous_config, current_config)
|
||||
data += "* {} (compared to {}):\r\n".format(_config_name, previous_config_name)
|
||||
data += '* {} (compared to {}):\r\n'.format(_config_name, previous_config_name)
|
||||
for diff_name in difference:
|
||||
data += " * `{}`: {}\r\n".format(diff_name, difference[diff_name])
|
||||
data += ' * `{}`: {}\r\n'.format(diff_name, difference[diff_name])
|
||||
return data
|
||||
|
||||
def _generate_report_for_one_type(self, throughput_type):
|
||||
@@ -115,39 +115,39 @@ class ThroughputForConfigsReport(object):
|
||||
"""
|
||||
empty = True
|
||||
|
||||
ret = "\r\n### {} {}\r\n\r\n".format(*throughput_type.split("_"))
|
||||
ret += "| config name | throughput (Mbps) | free heap size (bytes) |\r\n"
|
||||
ret += "|-------------|-------------------|------------------------|\r\n"
|
||||
ret = '\r\n### {} {}\r\n\r\n'.format(*throughput_type.split('_'))
|
||||
ret += '| config name | throughput (Mbps) | free heap size (bytes) |\r\n'
|
||||
ret += '|-------------|-------------------|------------------------|\r\n'
|
||||
for config in self.sort_order:
|
||||
try:
|
||||
result = self.results[config][throughput_type]
|
||||
throughput = "{:.02f}".format(max(result.throughput_by_att[self.ap_ssid].values()))
|
||||
throughput = '{:.02f}'.format(max(result.throughput_by_att[self.ap_ssid].values()))
|
||||
heap_size = str(result.heap_size)
|
||||
# although markdown table will do alignment
|
||||
# do align here for better text editor presentation
|
||||
ret += "| {:<12}| {:<18}| {:<23}|\r\n".format(config, throughput, heap_size)
|
||||
ret += '| {:<12}| {:<18}| {:<23}|\r\n'.format(config, throughput, heap_size)
|
||||
empty = False
|
||||
except KeyError:
|
||||
pass
|
||||
return ret if not empty else ""
|
||||
return ret if not empty else ''
|
||||
|
||||
def generate_report(self):
|
||||
data = "# Throughput for different configs\r\n"
|
||||
data += "\r\nAP: {}\r\n".format(self.ap_ssid)
|
||||
data = '# Throughput for different configs\r\n'
|
||||
data += '\r\nAP: {}\r\n'.format(self.ap_ssid)
|
||||
|
||||
for throughput_type in self.THROUGHPUT_TYPES:
|
||||
data += self._generate_report_for_one_type(throughput_type)
|
||||
data += "\r\n------\r\n"
|
||||
data += '\r\n------\r\n'
|
||||
|
||||
data += self._generate_the_difference_between_configs()
|
||||
|
||||
with open(os.path.join(self.output_path, self.REPORT_FILE_NAME), "w") as f:
|
||||
with open(os.path.join(self.output_path, self.REPORT_FILE_NAME), 'w') as f:
|
||||
f.write(data)
|
||||
|
||||
|
||||
class ThroughputVsRssiReport(object):
|
||||
|
||||
REPORT_FILE_NAME = "ThroughputVsRssi.md"
|
||||
REPORT_FILE_NAME = 'ThroughputVsRssi.md'
|
||||
|
||||
def __init__(self, output_path, throughput_results):
|
||||
"""
|
||||
@@ -160,7 +160,7 @@ class ThroughputVsRssiReport(object):
|
||||
}
|
||||
"""
|
||||
self.output_path = output_path
|
||||
self.raw_data_path = os.path.join(output_path, "raw_data")
|
||||
self.raw_data_path = os.path.join(output_path, 'raw_data')
|
||||
self.results = throughput_results
|
||||
self.throughput_types = list(self.results.keys())
|
||||
self.throughput_types.sort()
|
||||
@@ -179,20 +179,20 @@ class ThroughputVsRssiReport(object):
|
||||
| udp rx | Failed | 55.44 |
|
||||
"""
|
||||
|
||||
ret = "\r\n### Summary\r\n\r\n"
|
||||
ret += "| item | curve analysis | max throughput (Mbps) |\r\n"
|
||||
ret += "|---------|----------------|-----------------------|\r\n"
|
||||
ret = '\r\n### Summary\r\n\r\n'
|
||||
ret += '| item | curve analysis | max throughput (Mbps) |\r\n'
|
||||
ret += '|---------|----------------|-----------------------|\r\n'
|
||||
|
||||
for _type in self.throughput_types:
|
||||
result = self.results[_type]
|
||||
max_throughput = 0.0
|
||||
curve_analysis = "Failed" if result.error_list else "Success"
|
||||
curve_analysis = 'Failed' if result.error_list else 'Success'
|
||||
for ap_ssid in result.throughput_by_att:
|
||||
_max_for_ap = max(result.throughput_by_rssi[ap_ssid].values())
|
||||
if _max_for_ap > max_throughput:
|
||||
max_throughput = _max_for_ap
|
||||
max_throughput = "{:.02f}".format(max_throughput)
|
||||
ret += "| {:<8}| {:<15}| {:<22}|\r\n".format("{}_{}".format(result.proto, result.direction),
|
||||
max_throughput = '{:.02f}'.format(max_throughput)
|
||||
ret += '| {:<8}| {:<15}| {:<22}|\r\n'.format('{}_{}'.format(result.proto, result.direction),
|
||||
curve_analysis, max_throughput)
|
||||
return ret
|
||||
|
||||
@@ -217,29 +217,29 @@ class ThroughputVsRssiReport(object):
|
||||
|
||||
"""
|
||||
result.post_analysis()
|
||||
ret = "\r\n### {} {}\r\n".format(result.proto, result.direction)
|
||||
ret = '\r\n### {} {}\r\n'.format(result.proto, result.direction)
|
||||
if result.error_list:
|
||||
ret += "\r\nErrors:\r\n\r\n"
|
||||
ret += '\r\nErrors:\r\n\r\n'
|
||||
for error in result.error_list:
|
||||
ret += "* " + error + "\r\n"
|
||||
ret += '* ' + error + '\r\n'
|
||||
for ap_ssid in result.throughput_by_rssi:
|
||||
ret += "\r\nAP: {}\r\n".format(ap_ssid)
|
||||
ret += '\r\nAP: {}\r\n'.format(ap_ssid)
|
||||
# draw figure
|
||||
file_name = result.draw_throughput_figure(self.raw_data_path, ap_ssid, "rssi")
|
||||
result.draw_throughput_figure(self.raw_data_path, ap_ssid, "att")
|
||||
file_name = result.draw_throughput_figure(self.raw_data_path, ap_ssid, 'rssi')
|
||||
result.draw_throughput_figure(self.raw_data_path, ap_ssid, 'att')
|
||||
result.draw_rssi_vs_att_figure(self.raw_data_path, ap_ssid)
|
||||
|
||||
ret += "\r\n[throughput Vs RSSI]({})\r\n".format(os.path.join("raw_data", file_name))
|
||||
ret += '\r\n[throughput Vs RSSI]({})\r\n'.format(os.path.join('raw_data', file_name))
|
||||
|
||||
return ret
|
||||
|
||||
def generate_report(self):
|
||||
data = "# Throughput Vs RSSI\r\n"
|
||||
data = '# Throughput Vs RSSI\r\n'
|
||||
|
||||
data += self._generate_summary()
|
||||
|
||||
for _type in self.throughput_types:
|
||||
data += self._generate_report_for_one_type(self.results[_type])
|
||||
|
||||
with open(os.path.join(self.output_path, self.REPORT_FILE_NAME), "w") as f:
|
||||
with open(os.path.join(self.output_path, self.REPORT_FILE_NAME), 'w') as f:
|
||||
f.write(data)
|
||||
|
||||
@@ -80,9 +80,9 @@ class BaseApp(object):
|
||||
if not test_suite_name:
|
||||
test_suite_name = os.path.splitext(os.path.basename(sys.modules['__main__'].__file__))[0]
|
||||
sdk_path = cls.get_sdk_path()
|
||||
log_folder = os.path.join(sdk_path, "TEST_LOGS",
|
||||
log_folder = os.path.join(sdk_path, 'TEST_LOGS',
|
||||
test_suite_name +
|
||||
time.strftime("_%m%d_%H_%M_%S", time.localtime(LOG_FOLDER_TIMESTAMP)))
|
||||
time.strftime('_%m%d_%H_%M_%S', time.localtime(LOG_FOLDER_TIMESTAMP)))
|
||||
if not os.path.exists(log_folder):
|
||||
os.makedirs(log_folder)
|
||||
return log_folder
|
||||
|
||||
@@ -38,12 +38,13 @@ If they using different port then need to implement their DUTPort class as well.
|
||||
"""
|
||||
|
||||
from __future__ import print_function
|
||||
import time
|
||||
|
||||
import copy
|
||||
import functools
|
||||
import re
|
||||
import sys
|
||||
import threading
|
||||
import copy
|
||||
import functools
|
||||
import time
|
||||
|
||||
# python2 and python3 queue package name is different
|
||||
try:
|
||||
@@ -82,15 +83,15 @@ def _decode_data(data):
|
||||
# convert bytes to string. This is a bit of a hack, we know that we want to log this
|
||||
# later so encode to the stdout encoding with backslash escapes for anything non-encodable
|
||||
try:
|
||||
return data.decode(sys.stdout.encoding, "backslashreplace")
|
||||
return data.decode(sys.stdout.encoding, 'backslashreplace')
|
||||
except UnicodeDecodeError: # Python <3.5 doesn't support backslashreplace
|
||||
return data.decode(sys.stdout.encoding, "replace")
|
||||
return data.decode(sys.stdout.encoding, 'replace')
|
||||
return data
|
||||
|
||||
|
||||
def _pattern_to_string(pattern):
|
||||
try:
|
||||
ret = "RegEx: " + pattern.pattern
|
||||
ret = 'RegEx: ' + pattern.pattern
|
||||
except AttributeError:
|
||||
ret = pattern
|
||||
return ret
|
||||
@@ -167,7 +168,7 @@ class _LogThread(threading.Thread, _queue.Queue):
|
||||
Then data will be passed to ``expect`` as soon as received.
|
||||
"""
|
||||
def __init__(self):
|
||||
threading.Thread.__init__(self, name="LogThread")
|
||||
threading.Thread.__init__(self, name='LogThread')
|
||||
_queue.Queue.__init__(self, maxsize=0)
|
||||
self.setDaemon(True)
|
||||
self.flush_lock = threading.Lock()
|
||||
@@ -177,7 +178,7 @@ class _LogThread(threading.Thread, _queue.Queue):
|
||||
:param filename: log file name
|
||||
:param data: log data. Must be ``bytes``.
|
||||
"""
|
||||
self.put({"filename": filename, "data": data})
|
||||
self.put({'filename': filename, 'data': data})
|
||||
|
||||
def flush_data(self):
|
||||
with self.flush_lock:
|
||||
@@ -187,14 +188,14 @@ class _LogThread(threading.Thread, _queue.Queue):
|
||||
try:
|
||||
log = self.get_nowait()
|
||||
try:
|
||||
data_cache[log["filename"]] += log["data"]
|
||||
data_cache[log['filename']] += log['data']
|
||||
except KeyError:
|
||||
data_cache[log["filename"]] = log["data"]
|
||||
data_cache[log['filename']] = log['data']
|
||||
except _queue.Empty:
|
||||
break
|
||||
# flush data
|
||||
for filename in data_cache:
|
||||
with open(filename, "ab+") as f:
|
||||
with open(filename, 'ab+') as f:
|
||||
f.write(data_cache[filename])
|
||||
|
||||
def run(self):
|
||||
@@ -231,7 +232,7 @@ class RecvThread(threading.Thread):
|
||||
lines = decoded_data.splitlines(True)
|
||||
last_line = lines[-1]
|
||||
|
||||
if last_line[-1] != "\n":
|
||||
if last_line[-1] != '\n':
|
||||
if len(lines) == 1:
|
||||
# only one line and the line is not finished, then append this to cache
|
||||
self._line_cache += lines[-1]
|
||||
@@ -239,7 +240,7 @@ class RecvThread(threading.Thread):
|
||||
else:
|
||||
# more than one line and not finished, replace line cache
|
||||
self._line_cache = lines[-1]
|
||||
ret += "".join(lines[:-1])
|
||||
ret += ''.join(lines[:-1])
|
||||
else:
|
||||
# line finishes, flush cache
|
||||
self._line_cache = str()
|
||||
@@ -302,7 +303,7 @@ class BaseDUT(object):
|
||||
self.start_receive()
|
||||
|
||||
def __str__(self):
|
||||
return "DUT({}: {})".format(self.name, str(self.port))
|
||||
return 'DUT({}: {})'.format(self.name, str(self.port))
|
||||
|
||||
def _save_expect_failure(self, pattern, data, start_time):
|
||||
"""
|
||||
@@ -311,8 +312,8 @@ class BaseDUT(object):
|
||||
The expect failures could be false alarm, and test case might generate a lot of such failures.
|
||||
Therefore, we don't print the failure immediately and limit the max size of failure list.
|
||||
"""
|
||||
self.expect_failures.insert(0, {"pattern": pattern, "data": data,
|
||||
"start": start_time, "end": time.time()})
|
||||
self.expect_failures.insert(0, {'pattern': pattern, 'data': data,
|
||||
'start': start_time, 'end': time.time()})
|
||||
self.expect_failures = self.expect_failures[:self.MAX_EXPECT_FAILURES_TO_SAVED]
|
||||
|
||||
def _save_dut_log(self, data):
|
||||
@@ -444,7 +445,7 @@ class BaseDUT(object):
|
||||
raise e
|
||||
return data
|
||||
|
||||
def write(self, data, eol="\r\n", flush=True):
|
||||
def write(self, data, eol='\r\n', flush=True):
|
||||
"""
|
||||
:param data: data
|
||||
:param eol: end of line pattern.
|
||||
@@ -474,7 +475,7 @@ class BaseDUT(object):
|
||||
self.data_cache.flush(size)
|
||||
return data
|
||||
|
||||
def start_capture_raw_data(self, capture_id="default"):
|
||||
def start_capture_raw_data(self, capture_id='default'):
|
||||
"""
|
||||
Sometime application want to get DUT raw data and use ``expect`` method at the same time.
|
||||
Capture methods provides a way to get raw data without affecting ``expect`` or ``read`` method.
|
||||
@@ -491,7 +492,7 @@ class BaseDUT(object):
|
||||
# otherwise, create new data cache
|
||||
self.recorded_data[capture_id] = _DataCache()
|
||||
|
||||
def stop_capture_raw_data(self, capture_id="default"):
|
||||
def stop_capture_raw_data(self, capture_id='default'):
|
||||
"""
|
||||
Stop capture and get raw data.
|
||||
This method should be used after ``start_capture_raw_data`` on the same capture ID.
|
||||
@@ -504,9 +505,9 @@ class BaseDUT(object):
|
||||
ret = self.recorded_data[capture_id].get_data()
|
||||
self.recorded_data.pop(capture_id)
|
||||
except KeyError as e:
|
||||
e.message = "capture_id does not exist. " \
|
||||
"You should call start_capture_raw_data with same ID " \
|
||||
"before calling stop_capture_raw_data"
|
||||
e.message = 'capture_id does not exist. ' \
|
||||
'You should call start_capture_raw_data with same ID ' \
|
||||
'before calling stop_capture_raw_data'
|
||||
raise e
|
||||
return ret
|
||||
|
||||
@@ -552,9 +553,9 @@ class BaseDUT(object):
|
||||
return ret, index
|
||||
|
||||
EXPECT_METHOD = [
|
||||
[type(re.compile("")), "_expect_re"],
|
||||
[type(b''), "_expect_str"], # Python 2 & 3 hook to work without 'from builtins import str' from future
|
||||
[type(u''), "_expect_str"],
|
||||
[type(re.compile('')), '_expect_re'],
|
||||
[type(b''), '_expect_str'], # Python 2 & 3 hook to work without 'from builtins import str' from future
|
||||
[type(u''), '_expect_str'],
|
||||
]
|
||||
|
||||
def _get_expect_method(self, pattern):
|
||||
@@ -607,7 +608,7 @@ class BaseDUT(object):
|
||||
if ret is None:
|
||||
pattern = _pattern_to_string(pattern)
|
||||
self._save_expect_failure(pattern, data, start_time)
|
||||
raise ExpectTimeout(self.name + ": " + pattern)
|
||||
raise ExpectTimeout(self.name + ': ' + pattern)
|
||||
return stdout if full_stdout else ret
|
||||
|
||||
def _expect_multi(self, expect_all, expect_item_list, timeout):
|
||||
@@ -622,12 +623,12 @@ class BaseDUT(object):
|
||||
def process_expected_item(item_raw):
|
||||
# convert item raw data to standard dict
|
||||
item = {
|
||||
"pattern": item_raw[0] if isinstance(item_raw, tuple) else item_raw,
|
||||
"method": self._get_expect_method(item_raw[0] if isinstance(item_raw, tuple)
|
||||
'pattern': item_raw[0] if isinstance(item_raw, tuple) else item_raw,
|
||||
'method': self._get_expect_method(item_raw[0] if isinstance(item_raw, tuple)
|
||||
else item_raw),
|
||||
"callback": item_raw[1] if isinstance(item_raw, tuple) else None,
|
||||
"index": -1,
|
||||
"ret": None,
|
||||
'callback': item_raw[1] if isinstance(item_raw, tuple) else None,
|
||||
'index': -1,
|
||||
'ret': None,
|
||||
}
|
||||
return item
|
||||
|
||||
@@ -642,9 +643,9 @@ class BaseDUT(object):
|
||||
for expect_item in expect_items:
|
||||
if expect_item not in matched_expect_items:
|
||||
# exclude those already matched
|
||||
expect_item["ret"], expect_item["index"] = \
|
||||
expect_item["method"](data, expect_item["pattern"])
|
||||
if expect_item["ret"] is not None:
|
||||
expect_item['ret'], expect_item['index'] = \
|
||||
expect_item['method'](data, expect_item['pattern'])
|
||||
if expect_item['ret'] is not None:
|
||||
# match succeed for one item
|
||||
matched_expect_items.append(expect_item)
|
||||
|
||||
@@ -664,20 +665,20 @@ class BaseDUT(object):
|
||||
if match_succeed:
|
||||
# sort matched items according to order of appearance in the input data,
|
||||
# so that the callbacks are invoked in correct order
|
||||
matched_expect_items = sorted(matched_expect_items, key=lambda it: it["index"])
|
||||
matched_expect_items = sorted(matched_expect_items, key=lambda it: it['index'])
|
||||
# invoke callbacks and flush matched data cache
|
||||
slice_index = -1
|
||||
for expect_item in matched_expect_items:
|
||||
# trigger callback
|
||||
if expect_item["callback"]:
|
||||
expect_item["callback"](expect_item["ret"])
|
||||
slice_index = max(slice_index, expect_item["index"])
|
||||
if expect_item['callback']:
|
||||
expect_item['callback'](expect_item['ret'])
|
||||
slice_index = max(slice_index, expect_item['index'])
|
||||
# flush already matched data
|
||||
self.data_cache.flush(slice_index)
|
||||
else:
|
||||
pattern = str([_pattern_to_string(x["pattern"]) for x in expect_items])
|
||||
pattern = str([_pattern_to_string(x['pattern']) for x in expect_items])
|
||||
self._save_expect_failure(pattern, data, start_time)
|
||||
raise ExpectTimeout(self.name + ": " + pattern)
|
||||
raise ExpectTimeout(self.name + ': ' + pattern)
|
||||
|
||||
@_expect_lock
|
||||
def expect_any(self, *expect_items, **timeout):
|
||||
@@ -697,8 +698,8 @@ class BaseDUT(object):
|
||||
"""
|
||||
# to be compatible with python2
|
||||
# in python3 we can write f(self, *expect_items, timeout=DEFAULT_TIMEOUT)
|
||||
if "timeout" not in timeout:
|
||||
timeout["timeout"] = self.DEFAULT_EXPECT_TIMEOUT
|
||||
if 'timeout' not in timeout:
|
||||
timeout['timeout'] = self.DEFAULT_EXPECT_TIMEOUT
|
||||
return self._expect_multi(False, expect_items, **timeout)
|
||||
|
||||
@_expect_lock
|
||||
@@ -719,38 +720,38 @@ class BaseDUT(object):
|
||||
"""
|
||||
# to be compatible with python2
|
||||
# in python3 we can write f(self, *expect_items, timeout=DEFAULT_TIMEOUT)
|
||||
if "timeout" not in timeout:
|
||||
timeout["timeout"] = self.DEFAULT_EXPECT_TIMEOUT
|
||||
if 'timeout' not in timeout:
|
||||
timeout['timeout'] = self.DEFAULT_EXPECT_TIMEOUT
|
||||
return self._expect_multi(True, expect_items, **timeout)
|
||||
|
||||
@staticmethod
|
||||
def _format_ts(ts):
|
||||
return "{}:{}".format(time.strftime("%m-%d %H:%M:%S", time.localtime(ts)), str(ts % 1)[2:5])
|
||||
return '{}:{}'.format(time.strftime('%m-%d %H:%M:%S', time.localtime(ts)), str(ts % 1)[2:5])
|
||||
|
||||
def print_debug_info(self):
|
||||
"""
|
||||
Print debug info of current DUT. Currently we will print debug info for expect failures.
|
||||
"""
|
||||
Utility.console_log("DUT debug info for DUT: {}:".format(self.name), color="orange")
|
||||
Utility.console_log('DUT debug info for DUT: {}:'.format(self.name), color='orange')
|
||||
|
||||
for failure in self.expect_failures:
|
||||
Utility.console_log(u"\t[pattern]: {}\r\n\t[data]: {}\r\n\t[time]: {} - {}\r\n"
|
||||
.format(failure["pattern"], failure["data"],
|
||||
self._format_ts(failure["start"]), self._format_ts(failure["end"])),
|
||||
color="orange")
|
||||
Utility.console_log(u'\t[pattern]: {}\r\n\t[data]: {}\r\n\t[time]: {} - {}\r\n'
|
||||
.format(failure['pattern'], failure['data'],
|
||||
self._format_ts(failure['start']), self._format_ts(failure['end'])),
|
||||
color='orange')
|
||||
|
||||
|
||||
class SerialDUT(BaseDUT):
|
||||
""" serial with logging received data feature """
|
||||
|
||||
DEFAULT_UART_CONFIG = {
|
||||
"baudrate": 115200,
|
||||
"bytesize": serial.EIGHTBITS,
|
||||
"parity": serial.PARITY_NONE,
|
||||
"stopbits": serial.STOPBITS_ONE,
|
||||
"timeout": 0.05,
|
||||
"xonxoff": False,
|
||||
"rtscts": False,
|
||||
'baudrate': 115200,
|
||||
'bytesize': serial.EIGHTBITS,
|
||||
'parity': serial.PARITY_NONE,
|
||||
'stopbits': serial.STOPBITS_ONE,
|
||||
'timeout': 0.05,
|
||||
'xonxoff': False,
|
||||
'rtscts': False,
|
||||
}
|
||||
|
||||
def __init__(self, name, port, log_file, app, **kwargs):
|
||||
@@ -768,8 +769,8 @@ class SerialDUT(BaseDUT):
|
||||
:param data: raw data from read
|
||||
:return: formatted data (str)
|
||||
"""
|
||||
timestamp = "[{}]".format(self._format_ts(time.time()))
|
||||
formatted_data = timestamp.encode() + b"\r\n" + data + b"\r\n"
|
||||
timestamp = '[{}]'.format(self._format_ts(time.time()))
|
||||
formatted_data = timestamp.encode() + b'\r\n' + data + b'\r\n'
|
||||
return formatted_data
|
||||
|
||||
def _port_open(self):
|
||||
|
||||
@@ -13,12 +13,12 @@
|
||||
# limitations under the License.
|
||||
|
||||
""" Test Env, manages DUT, App and EnvConfig, interface for test cases to access these components """
|
||||
import functools
|
||||
import os
|
||||
import threading
|
||||
import functools
|
||||
import traceback
|
||||
|
||||
import netifaces
|
||||
import traceback
|
||||
|
||||
from . import EnvConfig
|
||||
|
||||
@@ -44,7 +44,7 @@ class Env(object):
|
||||
:keyword env_config_file: test env config file path
|
||||
:keyword test_name: test suite name, used when generate log folder name
|
||||
"""
|
||||
CURRENT_LOG_FOLDER = ""
|
||||
CURRENT_LOG_FOLDER = ''
|
||||
|
||||
def __init__(self,
|
||||
app=None,
|
||||
@@ -79,7 +79,7 @@ class Env(object):
|
||||
:return: dut instance
|
||||
"""
|
||||
if dut_name in self.allocated_duts:
|
||||
dut = self.allocated_duts[dut_name]["dut"]
|
||||
dut = self.allocated_duts[dut_name]['dut']
|
||||
else:
|
||||
if dut_class is None:
|
||||
dut_class = self.default_dut_cls
|
||||
@@ -95,7 +95,7 @@ class Env(object):
|
||||
result, detected_target = dut_class.confirm_dut(port)
|
||||
except ValueError:
|
||||
# try to auto detect ports
|
||||
allocated_ports = [self.allocated_duts[x]["port"] for x in self.allocated_duts]
|
||||
allocated_ports = [self.allocated_duts[x]['port'] for x in self.allocated_duts]
|
||||
available_ports = dut_class.list_available_ports()
|
||||
for port in available_ports:
|
||||
if port not in allocated_ports:
|
||||
@@ -113,17 +113,17 @@ class Env(object):
|
||||
|
||||
if port:
|
||||
try:
|
||||
dut_config = self.get_variable(dut_name + "_port_config")
|
||||
dut_config = self.get_variable(dut_name + '_port_config')
|
||||
except ValueError:
|
||||
dut_config = dict()
|
||||
dut_config.update(dut_init_args)
|
||||
dut = dut_class(dut_name, port,
|
||||
os.path.join(self.log_path, dut_name + ".log"),
|
||||
os.path.join(self.log_path, dut_name + '.log'),
|
||||
app_inst,
|
||||
**dut_config)
|
||||
self.allocated_duts[dut_name] = {"port": port, "dut": dut}
|
||||
self.allocated_duts[dut_name] = {'port': port, 'dut': dut}
|
||||
else:
|
||||
raise ValueError("Failed to get DUT")
|
||||
raise ValueError('Failed to get DUT')
|
||||
return dut
|
||||
|
||||
@_synced
|
||||
@@ -136,7 +136,7 @@ class Env(object):
|
||||
:return: None
|
||||
"""
|
||||
try:
|
||||
dut = self.allocated_duts.pop(dut_name)["dut"]
|
||||
dut = self.allocated_duts.pop(dut_name)['dut']
|
||||
dut.close()
|
||||
except KeyError:
|
||||
pass
|
||||
@@ -153,13 +153,13 @@ class Env(object):
|
||||
return self.config.get_variable(variable_name)
|
||||
|
||||
PROTO_MAP = {
|
||||
"ipv4": netifaces.AF_INET,
|
||||
"ipv6": netifaces.AF_INET6,
|
||||
"mac": netifaces.AF_LINK,
|
||||
'ipv4': netifaces.AF_INET,
|
||||
'ipv6': netifaces.AF_INET6,
|
||||
'mac': netifaces.AF_LINK,
|
||||
}
|
||||
|
||||
@_synced
|
||||
def get_pc_nic_info(self, nic_name="pc_nic", proto="ipv4"):
|
||||
def get_pc_nic_info(self, nic_name='pc_nic', proto='ipv4'):
|
||||
"""
|
||||
get_pc_nic_info(nic_name="pc_nic")
|
||||
try to get info of a specified NIC and protocol.
|
||||
@@ -192,7 +192,7 @@ class Env(object):
|
||||
"""
|
||||
dut_close_errors = []
|
||||
for dut_name in self.allocated_duts:
|
||||
dut = self.allocated_duts[dut_name]["dut"]
|
||||
dut = self.allocated_duts[dut_name]['dut']
|
||||
try:
|
||||
if dut_debug:
|
||||
dut.print_debug_info()
|
||||
|
||||
@@ -79,5 +79,5 @@ class Config(object):
|
||||
# TODO: to support auto get variable here
|
||||
value = None
|
||||
if value is None:
|
||||
raise ValueError("Failed to get variable")
|
||||
raise ValueError('Failed to get variable')
|
||||
return value
|
||||
|
||||
@@ -13,18 +13,15 @@
|
||||
# limitations under the License.
|
||||
|
||||
""" Interface for test cases. """
|
||||
import os
|
||||
import time
|
||||
import functools
|
||||
import os
|
||||
import socket
|
||||
import time
|
||||
from datetime import datetime
|
||||
|
||||
import junit_xml
|
||||
|
||||
from . import Env
|
||||
from . import DUT
|
||||
from . import App
|
||||
from . import Utility
|
||||
from . import DUT, App, Env, Utility
|
||||
|
||||
|
||||
class TestCaseFailed(AssertionError):
|
||||
@@ -37,7 +34,7 @@ class TestCaseFailed(AssertionError):
|
||||
|
||||
'cases' argument is the names of one or more test cases
|
||||
"""
|
||||
message = "Test case{} failed: {}".format("s" if len(cases) > 1 else "", ", ".join(str(c) for c in cases))
|
||||
message = 'Test case{} failed: {}'.format('s' if len(cases) > 1 else '', ', '.join(str(c) for c in cases))
|
||||
super(TestCaseFailed, self).__init__(self, message)
|
||||
|
||||
|
||||
@@ -50,11 +47,11 @@ class DefaultEnvConfig(object):
|
||||
3. default env config get from this class
|
||||
"""
|
||||
DEFAULT_CONFIG = {
|
||||
"app": App.BaseApp,
|
||||
"dut": DUT.BaseDUT,
|
||||
"env_tag": "default",
|
||||
"env_config_file": None,
|
||||
"test_suite_name": None,
|
||||
'app': App.BaseApp,
|
||||
'dut': DUT.BaseDUT,
|
||||
'env_tag': 'default',
|
||||
'env_config_file': None,
|
||||
'test_suite_name': None,
|
||||
}
|
||||
|
||||
@classmethod
|
||||
@@ -78,10 +75,10 @@ get_default_config = DefaultEnvConfig.get_default_config
|
||||
|
||||
|
||||
MANDATORY_INFO = {
|
||||
"execution_time": 1,
|
||||
"env_tag": "default",
|
||||
"category": "function",
|
||||
"ignore": False,
|
||||
'execution_time': 1,
|
||||
'env_tag': 'default',
|
||||
'category': 'function',
|
||||
'ignore': False,
|
||||
}
|
||||
|
||||
|
||||
@@ -89,8 +86,8 @@ class JunitReport(object):
|
||||
# wrapper for junit test report
|
||||
# TODO: JunitReport methods are not thread safe (although not likely to be used this way).
|
||||
|
||||
JUNIT_FILE_NAME = "XUNIT_RESULT.xml"
|
||||
JUNIT_DEFAULT_TEST_SUITE = "test-suite"
|
||||
JUNIT_FILE_NAME = 'XUNIT_RESULT.xml'
|
||||
JUNIT_DEFAULT_TEST_SUITE = 'test-suite'
|
||||
JUNIT_TEST_SUITE = junit_xml.TestSuite(JUNIT_DEFAULT_TEST_SUITE,
|
||||
hostname=socket.gethostname(),
|
||||
timestamp=datetime.utcnow().isoformat())
|
||||
@@ -100,7 +97,7 @@ class JunitReport(object):
|
||||
@classmethod
|
||||
def output_report(cls, junit_file_path):
|
||||
""" Output current test result to file. """
|
||||
with open(os.path.join(junit_file_path, cls.JUNIT_FILE_NAME), "w") as f:
|
||||
with open(os.path.join(junit_file_path, cls.JUNIT_FILE_NAME), 'w') as f:
|
||||
cls.JUNIT_TEST_SUITE.to_file(f, [cls.JUNIT_TEST_SUITE], prettyprint=False)
|
||||
|
||||
@classmethod
|
||||
@@ -136,7 +133,7 @@ class JunitReport(object):
|
||||
"""
|
||||
# set stdout to empty string, so we can always append string to stdout.
|
||||
# It won't affect output logic. If stdout is empty, it won't be put to report.
|
||||
test_case = junit_xml.TestCase(name, stdout="")
|
||||
test_case = junit_xml.TestCase(name, stdout='')
|
||||
cls.JUNIT_CURRENT_TEST_CASE = test_case
|
||||
cls._TEST_CASE_CREATED_TS = time.time()
|
||||
return test_case
|
||||
@@ -151,7 +148,7 @@ class JunitReport(object):
|
||||
assert cls.JUNIT_CURRENT_TEST_CASE
|
||||
|
||||
for item in performance_items:
|
||||
cls.JUNIT_CURRENT_TEST_CASE.stdout += "[{}]: {}\n".format(item[0], item[1])
|
||||
cls.JUNIT_CURRENT_TEST_CASE.stdout += '[{}]: {}\n'.format(item[0], item[1])
|
||||
|
||||
|
||||
def test_method(**kwargs):
|
||||
@@ -174,8 +171,8 @@ def test_method(**kwargs):
|
||||
def test(test_func):
|
||||
|
||||
case_info = MANDATORY_INFO.copy()
|
||||
case_info["name"] = case_info["ID"] = test_func.__name__
|
||||
case_info["junit_report_by_case"] = False
|
||||
case_info['name'] = case_info['ID'] = test_func.__name__
|
||||
case_info['junit_report_by_case'] = False
|
||||
case_info.update(kwargs)
|
||||
|
||||
@functools.wraps(test_func)
|
||||
@@ -197,12 +194,12 @@ def test_method(**kwargs):
|
||||
env_inst = Env.Env(**env_config)
|
||||
|
||||
# prepare for xunit test results
|
||||
junit_file_path = env_inst.app_cls.get_log_folder(env_config["test_suite_name"])
|
||||
junit_test_case = JunitReport.create_test_case(case_info["ID"])
|
||||
junit_file_path = env_inst.app_cls.get_log_folder(env_config['test_suite_name'])
|
||||
junit_test_case = JunitReport.create_test_case(case_info['ID'])
|
||||
result = False
|
||||
|
||||
try:
|
||||
Utility.console_log("starting running test: " + test_func.__name__, color="green")
|
||||
Utility.console_log('starting running test: ' + test_func.__name__, color='green')
|
||||
# execute test function
|
||||
test_func(env_inst, extra_data)
|
||||
# if finish without exception, test result is True
|
||||
@@ -224,16 +221,16 @@ def test_method(**kwargs):
|
||||
for error in close_errors:
|
||||
junit_test_case.add_failure_info(str(error))
|
||||
result = False
|
||||
if not case_info["junit_report_by_case"]:
|
||||
if not case_info['junit_report_by_case']:
|
||||
JunitReport.test_case_finish(junit_test_case)
|
||||
|
||||
# end case and output result
|
||||
JunitReport.output_report(junit_file_path)
|
||||
|
||||
if result:
|
||||
Utility.console_log("Test Succeed: " + test_func.__name__, color="green")
|
||||
Utility.console_log('Test Succeed: ' + test_func.__name__, color='green')
|
||||
else:
|
||||
Utility.console_log(("Test Fail: " + test_func.__name__), color="red")
|
||||
Utility.console_log(('Test Fail: ' + test_func.__name__), color='red')
|
||||
return result
|
||||
|
||||
handle_test.case_info = case_info
|
||||
|
||||
@@ -39,9 +39,9 @@ The Basic logic to assign test cases is as follow:
|
||||
|
||||
"""
|
||||
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import json
|
||||
|
||||
import yaml
|
||||
|
||||
@@ -50,13 +50,13 @@ try:
|
||||
except ImportError:
|
||||
from yaml import Loader as Loader
|
||||
|
||||
from . import (CaseConfig, SearchCases, GitlabCIJob, console_log)
|
||||
from . import CaseConfig, GitlabCIJob, SearchCases, console_log
|
||||
|
||||
|
||||
class Group(object):
|
||||
MAX_EXECUTION_TIME = 30
|
||||
MAX_CASE = 15
|
||||
SORT_KEYS = ["env_tag"]
|
||||
SORT_KEYS = ['env_tag']
|
||||
# Matching CI job rules could be different from the way we want to group test cases.
|
||||
# For example, when assign unit test cases, different test cases need to use different test functions.
|
||||
# We need to put them into different groups.
|
||||
@@ -92,7 +92,7 @@ class Group(object):
|
||||
|
||||
:return: True or False
|
||||
"""
|
||||
max_time = (sum([self._get_case_attr(x, "execution_time") for x in self.case_list])
|
||||
max_time = (sum([self._get_case_attr(x, 'execution_time') for x in self.case_list])
|
||||
< self.MAX_EXECUTION_TIME)
|
||||
max_case = (len(self.case_list) < self.MAX_CASE)
|
||||
return max_time and max_case
|
||||
@@ -135,8 +135,8 @@ class Group(object):
|
||||
:return: {"Filter": case filter, "CaseConfig": list of case configs for cases in this group}
|
||||
"""
|
||||
output_data = {
|
||||
"Filter": self.filters,
|
||||
"CaseConfig": [{"name": self._get_case_attr(x, "name")} for x in self.case_list],
|
||||
'Filter': self.filters,
|
||||
'CaseConfig': [{'name': self._get_case_attr(x, 'name')} for x in self.case_list],
|
||||
}
|
||||
return output_data
|
||||
|
||||
@@ -149,12 +149,12 @@ class AssignTest(object):
|
||||
:param ci_config_file: path of ``.gitlab-ci.yml``
|
||||
"""
|
||||
# subclass need to rewrite CI test job pattern, to filter all test jobs
|
||||
CI_TEST_JOB_PATTERN = re.compile(r"^test_.+")
|
||||
CI_TEST_JOB_PATTERN = re.compile(r'^test_.+')
|
||||
# by default we only run function in CI, as other tests could take long time
|
||||
DEFAULT_FILTER = {
|
||||
"category": "function",
|
||||
"ignore": False,
|
||||
"supported_in_ci": True,
|
||||
'category': 'function',
|
||||
'ignore': False,
|
||||
'supported_in_ci': True,
|
||||
}
|
||||
|
||||
def __init__(self, test_case_paths, ci_config_file, case_group=Group):
|
||||
@@ -168,25 +168,25 @@ class AssignTest(object):
|
||||
def _handle_parallel_attribute(job_name, job):
|
||||
jobs_out = []
|
||||
try:
|
||||
for i in range(job["parallel"]):
|
||||
jobs_out.append(GitlabCIJob.Job(job, job_name + "_{}".format(i + 1)))
|
||||
for i in range(job['parallel']):
|
||||
jobs_out.append(GitlabCIJob.Job(job, job_name + '_{}'.format(i + 1)))
|
||||
except KeyError:
|
||||
# Gitlab don't allow to set parallel to 1.
|
||||
# to make test job name same ($CI_JOB_NAME_$CI_NODE_INDEX),
|
||||
# we append "_" to jobs don't have parallel attribute
|
||||
jobs_out.append(GitlabCIJob.Job(job, job_name + "_"))
|
||||
jobs_out.append(GitlabCIJob.Job(job, job_name + '_'))
|
||||
return jobs_out
|
||||
|
||||
def _parse_gitlab_ci_config(self, ci_config_file):
|
||||
|
||||
with open(ci_config_file, "r") as f:
|
||||
with open(ci_config_file, 'r') as f:
|
||||
ci_config = yaml.load(f, Loader=Loader)
|
||||
|
||||
job_list = list()
|
||||
for job_name in ci_config:
|
||||
if self.CI_TEST_JOB_PATTERN.search(job_name) is not None:
|
||||
job_list.extend(self._handle_parallel_attribute(job_name, ci_config[job_name]))
|
||||
job_list.sort(key=lambda x: x["name"])
|
||||
job_list.sort(key=lambda x: x['name'])
|
||||
return job_list
|
||||
|
||||
def search_cases(self, case_filter=None):
|
||||
@@ -256,7 +256,7 @@ class AssignTest(object):
|
||||
Bot could also pass test count.
|
||||
If filtered cases need to be tested for several times, then we do duplicate them here.
|
||||
"""
|
||||
test_count = os.getenv("BOT_TEST_COUNT")
|
||||
test_count = os.getenv('BOT_TEST_COUNT')
|
||||
if test_count:
|
||||
test_count = int(test_count)
|
||||
self.test_cases *= test_count
|
||||
@@ -269,7 +269,7 @@ class AssignTest(object):
|
||||
"""
|
||||
group_count = dict()
|
||||
for group in test_groups:
|
||||
key = ",".join(group.ci_job_match_keys)
|
||||
key = ','.join(group.ci_job_match_keys)
|
||||
try:
|
||||
group_count[key] += 1
|
||||
except KeyError:
|
||||
@@ -305,26 +305,26 @@ class AssignTest(object):
|
||||
# print debug info
|
||||
# total requirement of current pipeline
|
||||
required_group_count = self._count_groups_by_keys(test_groups)
|
||||
console_log("Required job count by tags:")
|
||||
console_log('Required job count by tags:')
|
||||
for tags in required_group_count:
|
||||
console_log("\t{}: {}".format(tags, required_group_count[tags]))
|
||||
console_log('\t{}: {}'.format(tags, required_group_count[tags]))
|
||||
|
||||
# number of unused jobs
|
||||
not_used_jobs = [job for job in self.jobs if "case group" not in job]
|
||||
not_used_jobs = [job for job in self.jobs if 'case group' not in job]
|
||||
if not_used_jobs:
|
||||
console_log("{} jobs not used. Please check if you define too much jobs".format(len(not_used_jobs)), "O")
|
||||
console_log('{} jobs not used. Please check if you define too much jobs'.format(len(not_used_jobs)), 'O')
|
||||
for job in not_used_jobs:
|
||||
console_log("\t{}".format(job["name"]), "O")
|
||||
console_log('\t{}'.format(job['name']), 'O')
|
||||
|
||||
# failures
|
||||
if failed_to_assign:
|
||||
console_log("Too many test cases vs jobs to run. "
|
||||
"Please increase parallel count in tools/ci/config/target-test.yml "
|
||||
"for jobs with specific tags:", "R")
|
||||
console_log('Too many test cases vs jobs to run. '
|
||||
'Please increase parallel count in tools/ci/config/target-test.yml '
|
||||
'for jobs with specific tags:', 'R')
|
||||
failed_group_count = self._count_groups_by_keys(failed_to_assign)
|
||||
for tags in failed_group_count:
|
||||
console_log("\t{}: {}".format(tags, failed_group_count[tags]), "R")
|
||||
raise RuntimeError("Failed to assign test case to CI jobs")
|
||||
console_log('\t{}: {}'.format(tags, failed_group_count[tags]), 'R')
|
||||
raise RuntimeError('Failed to assign test case to CI jobs')
|
||||
|
||||
def output_configs(self, output_path):
|
||||
"""
|
||||
|
||||
@@ -141,9 +141,9 @@ def filter_test_cases(test_methods, case_filter):
|
||||
|
||||
class Parser(object):
|
||||
DEFAULT_CONFIG = {
|
||||
"TestConfig": dict(),
|
||||
"Filter": dict(),
|
||||
"CaseConfig": [{"extra_data": None}],
|
||||
'TestConfig': dict(),
|
||||
'Filter': dict(),
|
||||
'CaseConfig': [{'extra_data': None}],
|
||||
}
|
||||
|
||||
@classmethod
|
||||
@@ -156,7 +156,7 @@ class Parser(object):
|
||||
"""
|
||||
configs = cls.DEFAULT_CONFIG.copy()
|
||||
if config_file:
|
||||
with open(config_file, "r") as f:
|
||||
with open(config_file, 'r') as f:
|
||||
configs.update(yaml.load(f, Loader=Loader))
|
||||
return configs
|
||||
|
||||
@@ -170,8 +170,8 @@ class Parser(object):
|
||||
"""
|
||||
output = dict()
|
||||
for key in overwrite:
|
||||
module = importlib.import_module(overwrite[key]["package"])
|
||||
output[key] = module.__getattribute__(overwrite[key]["class"])
|
||||
module = importlib.import_module(overwrite[key]['package'])
|
||||
output[key] = module.__getattribute__(overwrite[key]['class'])
|
||||
return output
|
||||
|
||||
@classmethod
|
||||
@@ -185,10 +185,10 @@ class Parser(object):
|
||||
"""
|
||||
configs = cls.parse_config_file(config_file)
|
||||
test_case_list = []
|
||||
for _config in configs["CaseConfig"]:
|
||||
_filter = configs["Filter"].copy()
|
||||
_overwrite = cls.handle_overwrite_args(_config.pop("overwrite", dict()))
|
||||
_extra_data = _config.pop("extra_data", None)
|
||||
for _config in configs['CaseConfig']:
|
||||
_filter = configs['Filter'].copy()
|
||||
_overwrite = cls.handle_overwrite_args(_config.pop('overwrite', dict()))
|
||||
_extra_data = _config.pop('extra_data', None)
|
||||
_filter.update(_config)
|
||||
|
||||
# Try get target from yml
|
||||
@@ -222,8 +222,8 @@ class Generator(object):
|
||||
|
||||
def __init__(self):
|
||||
self.default_config = {
|
||||
"TestConfig": dict(),
|
||||
"Filter": dict(),
|
||||
'TestConfig': dict(),
|
||||
'Filter': dict(),
|
||||
}
|
||||
|
||||
def set_default_configs(self, test_config, case_filter):
|
||||
@@ -232,7 +232,7 @@ class Generator(object):
|
||||
:param case_filter: "Filter" value
|
||||
:return: None
|
||||
"""
|
||||
self.default_config = {"TestConfig": test_config, "Filter": case_filter}
|
||||
self.default_config = {'TestConfig': test_config, 'Filter': case_filter}
|
||||
|
||||
def generate_config(self, case_configs, output_file):
|
||||
"""
|
||||
@@ -241,6 +241,6 @@ class Generator(object):
|
||||
:return: None
|
||||
"""
|
||||
config = self.default_config.copy()
|
||||
config.update({"CaseConfig": case_configs})
|
||||
with open(output_file, "w") as f:
|
||||
config.update({'CaseConfig': case_configs})
|
||||
with open(output_file, 'w') as f:
|
||||
yaml.dump(config, f)
|
||||
|
||||
@@ -26,8 +26,8 @@ class Job(dict):
|
||||
"""
|
||||
def __init__(self, job, job_name):
|
||||
super(Job, self).__init__(job)
|
||||
self["name"] = job_name
|
||||
self.tags = set(self["tags"])
|
||||
self['name'] = job_name
|
||||
self.tags = set(self['tags'])
|
||||
|
||||
def match_group(self, group):
|
||||
"""
|
||||
@@ -38,7 +38,7 @@ class Job(dict):
|
||||
:return: True or False
|
||||
"""
|
||||
match_result = False
|
||||
if "case group" not in self and group.ci_job_match_keys == self.tags:
|
||||
if 'case group' not in self and group.ci_job_match_keys == self.tags:
|
||||
# group not assigned and all tags match
|
||||
match_result = True
|
||||
return match_result
|
||||
@@ -49,7 +49,7 @@ class Job(dict):
|
||||
|
||||
:param group: the case group to assign
|
||||
"""
|
||||
self["case group"] = group
|
||||
self['case group'] = group
|
||||
|
||||
def output_config(self, file_path):
|
||||
"""
|
||||
@@ -59,7 +59,7 @@ class Job(dict):
|
||||
:param file_path: output file path
|
||||
:return: None
|
||||
"""
|
||||
file_name = os.path.join(file_path, self["name"] + ".yml")
|
||||
if "case group" in self:
|
||||
with open(file_name, "w") as f:
|
||||
yaml.safe_dump(self["case group"].output(), f, encoding='utf-8', default_flow_style=False)
|
||||
file_name = os.path.join(file_path, self['name'] + '.yml')
|
||||
if 'case group' in self:
|
||||
with open(file_name, 'w') as f:
|
||||
yaml.safe_dump(self['case group'].output(), f, encoding='utf-8', default_flow_style=False)
|
||||
|
||||
@@ -13,23 +13,23 @@
|
||||
# limitations under the License.
|
||||
|
||||
""" search test cases from a given file or path """
|
||||
import os
|
||||
import fnmatch
|
||||
import types
|
||||
import copy
|
||||
import fnmatch
|
||||
import os
|
||||
import types
|
||||
|
||||
from . import load_source
|
||||
|
||||
|
||||
class Search(object):
|
||||
TEST_CASE_FILE_PATTERN = "*_test.py"
|
||||
TEST_CASE_FILE_PATTERN = '*_test.py'
|
||||
SUPPORT_REPLICATE_CASES_KEY = ['target']
|
||||
|
||||
@classmethod
|
||||
def _search_cases_from_file(cls, file_name):
|
||||
""" get test cases from test case .py file """
|
||||
|
||||
print("Try to get cases from: " + file_name)
|
||||
print('Try to get cases from: ' + file_name)
|
||||
test_functions = []
|
||||
try:
|
||||
mod = load_source(file_name)
|
||||
@@ -42,14 +42,14 @@ class Search(object):
|
||||
except AttributeError:
|
||||
continue
|
||||
except ImportError as e:
|
||||
print("ImportError: \r\n\tFile:" + file_name + "\r\n\tError:" + str(e))
|
||||
print('ImportError: \r\n\tFile:' + file_name + '\r\n\tError:' + str(e))
|
||||
|
||||
test_functions_out = []
|
||||
for case in test_functions:
|
||||
test_functions_out += cls.replicate_case(case)
|
||||
|
||||
for i, test_function in enumerate(test_functions_out):
|
||||
print("\t{}. {} <{}>".format(i + 1, test_function.case_info["name"], test_function.case_info["target"]))
|
||||
print('\t{}. {} <{}>'.format(i + 1, test_function.case_info['name'], test_function.case_info['target']))
|
||||
test_function.case_info['app_dir'] = os.path.dirname(file_name)
|
||||
return test_functions_out
|
||||
|
||||
@@ -58,7 +58,7 @@ class Search(object):
|
||||
""" search all test case files recursively of a path """
|
||||
|
||||
if not os.path.exists(test_case):
|
||||
raise OSError("test case path not exist")
|
||||
raise OSError('test case path not exist')
|
||||
if os.path.isdir(test_case):
|
||||
test_case_files = []
|
||||
for root, _, file_names in os.walk(test_case):
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
from __future__ import print_function
|
||||
|
||||
import os.path
|
||||
import sys
|
||||
import time
|
||||
@@ -7,35 +8,35 @@ import traceback
|
||||
from .. import Env
|
||||
|
||||
_COLOR_CODES = {
|
||||
"white": u'\033[0m',
|
||||
"red": u'\033[31m',
|
||||
"green": u'\033[32m',
|
||||
"orange": u'\033[33m',
|
||||
"blue": u'\033[34m',
|
||||
"purple": u'\033[35m',
|
||||
"W": u'\033[0m',
|
||||
"R": u'\033[31m',
|
||||
"G": u'\033[32m',
|
||||
"O": u'\033[33m',
|
||||
"B": u'\033[34m',
|
||||
"P": u'\033[35m'
|
||||
'white': u'\033[0m',
|
||||
'red': u'\033[31m',
|
||||
'green': u'\033[32m',
|
||||
'orange': u'\033[33m',
|
||||
'blue': u'\033[34m',
|
||||
'purple': u'\033[35m',
|
||||
'W': u'\033[0m',
|
||||
'R': u'\033[31m',
|
||||
'G': u'\033[32m',
|
||||
'O': u'\033[33m',
|
||||
'B': u'\033[34m',
|
||||
'P': u'\033[35m'
|
||||
}
|
||||
|
||||
|
||||
def _get_log_file_name():
|
||||
if Env.Env.CURRENT_LOG_FOLDER:
|
||||
file_name = os.path.join(Env.Env.CURRENT_LOG_FOLDER, "console.log")
|
||||
file_name = os.path.join(Env.Env.CURRENT_LOG_FOLDER, 'console.log')
|
||||
else:
|
||||
raise OSError("env log folder does not exist, will not save to log file")
|
||||
raise OSError('env log folder does not exist, will not save to log file')
|
||||
return file_name
|
||||
|
||||
|
||||
def format_timestamp():
|
||||
ts = time.time()
|
||||
return "{}:{}".format(time.strftime("%m-%d %H:%M:%S", time.localtime(ts)), str(ts % 1)[2:5])
|
||||
return '{}:{}'.format(time.strftime('%m-%d %H:%M:%S', time.localtime(ts)), str(ts % 1)[2:5])
|
||||
|
||||
|
||||
def console_log(data, color="white", end="\n"):
|
||||
def console_log(data, color='white', end='\n'):
|
||||
"""
|
||||
log data to console.
|
||||
(if not flush console log, Gitlab-CI won't update logs during job execution)
|
||||
@@ -44,19 +45,19 @@ def console_log(data, color="white", end="\n"):
|
||||
:param color: color
|
||||
"""
|
||||
if color not in _COLOR_CODES:
|
||||
color = "white"
|
||||
color = 'white'
|
||||
color_codes = _COLOR_CODES[color]
|
||||
if isinstance(data, type(b'')):
|
||||
data = data.decode('utf-8', 'replace')
|
||||
print(color_codes + data, end=end)
|
||||
if color not in ["white", "W"]:
|
||||
if color not in ['white', 'W']:
|
||||
# reset color to white for later logs
|
||||
print(_COLOR_CODES["white"] + u"\r")
|
||||
print(_COLOR_CODES['white'] + u'\r')
|
||||
sys.stdout.flush()
|
||||
log_data = "[{}] ".format(format_timestamp()) + data
|
||||
log_data = '[{}] '.format(format_timestamp()) + data
|
||||
try:
|
||||
log_file = _get_log_file_name()
|
||||
with open(log_file, "a+") as f:
|
||||
with open(log_file, 'a+') as f:
|
||||
f.write(log_data + end)
|
||||
except OSError:
|
||||
pass
|
||||
@@ -108,4 +109,4 @@ def handle_unexpected_exception(junit_test_case, exception):
|
||||
traceback.print_exc()
|
||||
# AssertionError caused by an 'assert' statement has an empty string as its 'str' form
|
||||
e_str = str(exception) if str(exception) else repr(exception)
|
||||
junit_test_case.add_failure_info("Unexpected exception: {}\n{}".format(e_str, traceback.format_exc()))
|
||||
junit_test_case.add_failure_info('Unexpected exception: {}\n{}'.format(e_str, traceback.format_exc()))
|
||||
|
||||
@@ -21,13 +21,13 @@ Command line interface to run test cases from a given path.
|
||||
Use ``python Runner.py test_case_path -c config_file -e env_config_file`` to run test cases.
|
||||
|
||||
"""
|
||||
import argparse
|
||||
import os
|
||||
import sys
|
||||
import argparse
|
||||
import threading
|
||||
|
||||
from tiny_test_fw import TinyFW
|
||||
from tiny_test_fw.Utility import SearchCases, CaseConfig
|
||||
from tiny_test_fw.Utility import CaseConfig, SearchCases
|
||||
|
||||
|
||||
class Runner(threading.Thread):
|
||||
@@ -43,7 +43,7 @@ class Runner(threading.Thread):
|
||||
if case_config:
|
||||
test_suite_name = os.path.splitext(os.path.basename(case_config))[0]
|
||||
else:
|
||||
test_suite_name = "TestRunner"
|
||||
test_suite_name = 'TestRunner'
|
||||
TinyFW.set_default_config(env_config_file=env_config_file, test_suite_name=test_suite_name)
|
||||
test_methods = SearchCases.Search.search_test_cases(test_case_paths)
|
||||
self.test_cases = CaseConfig.Parser.apply_config(test_methods, case_config)
|
||||
@@ -60,12 +60,12 @@ class Runner(threading.Thread):
|
||||
|
||||
if __name__ == '__main__':
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("test_cases", nargs='+',
|
||||
help="test case folders or files")
|
||||
parser.add_argument("--case_config", "-c", default=None,
|
||||
help="case filter/config file")
|
||||
parser.add_argument("--env_config_file", "-e", default=None,
|
||||
help="test env config file")
|
||||
parser.add_argument('test_cases', nargs='+',
|
||||
help='test case folders or files')
|
||||
parser.add_argument('--case_config', '-c', default=None,
|
||||
help='case filter/config file')
|
||||
parser.add_argument('--env_config_file', '-e', default=None,
|
||||
help='test env config file')
|
||||
args = parser.parse_args()
|
||||
|
||||
test_cases = [os.path.join(os.getenv('IDF_PATH'), path) if not os.path.isabs(path) else path for path in args.test_cases]
|
||||
@@ -78,7 +78,7 @@ if __name__ == '__main__':
|
||||
if not runner.is_alive():
|
||||
break
|
||||
except KeyboardInterrupt:
|
||||
print("exit by Ctrl-C")
|
||||
print('exit by Ctrl-C')
|
||||
break
|
||||
if not runner.get_test_result():
|
||||
sys.exit(1)
|
||||
|
||||
@@ -19,7 +19,7 @@ import ttfw_idf
|
||||
from tiny_test_fw import TinyFW
|
||||
|
||||
|
||||
@ttfw_idf.idf_example_test(env_tag="Example_WIFI")
|
||||
@ttfw_idf.idf_example_test(env_tag='Example_WIFI')
|
||||
def test_examples_protocol_https_request(env, extra_data):
|
||||
"""
|
||||
steps: |
|
||||
@@ -27,17 +27,17 @@ def test_examples_protocol_https_request(env, extra_data):
|
||||
2. connect to www.howsmyssl.com:443
|
||||
3. send http request
|
||||
"""
|
||||
dut1 = env.get_dut("https_request", "examples/protocols/https_request", dut_class=ttfw_idf.ESP32DUT)
|
||||
dut1 = env.get_dut('https_request', 'examples/protocols/https_request', dut_class=ttfw_idf.ESP32DUT)
|
||||
dut1.start_app()
|
||||
dut1.expect(re.compile(r"Connecting to www.howsmyssl.com:443"), timeout=30)
|
||||
dut1.expect("Performing the SSL/TLS handshake")
|
||||
dut1.expect("Certificate verified.", timeout=15)
|
||||
dut1.expect_all(re.compile(r"Cipher suite is TLS-ECDHE-RSA-WITH-AES-128-GCM-SHA256"),
|
||||
"Reading HTTP response",
|
||||
dut1.expect(re.compile(r'Connecting to www.howsmyssl.com:443'), timeout=30)
|
||||
dut1.expect('Performing the SSL/TLS handshake')
|
||||
dut1.expect('Certificate verified.', timeout=15)
|
||||
dut1.expect_all(re.compile(r'Cipher suite is TLS-ECDHE-RSA-WITH-AES-128-GCM-SHA256'),
|
||||
'Reading HTTP response',
|
||||
timeout=20)
|
||||
dut1.expect(re.compile(r"Completed (\d) requests"))
|
||||
dut1.expect(re.compile(r'Completed (\d) requests'))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
TinyFW.set_default_config(env_config_file="EnvConfigTemplate.yml", dut=ttfw_idf.IDFDUT)
|
||||
TinyFW.set_default_config(env_config_file='EnvConfigTemplate.yml', dut=ttfw_idf.IDFDUT)
|
||||
test_examples_protocol_https_request()
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#
|
||||
import os
|
||||
import sys
|
||||
|
||||
sys.path.insert(0, os.path.abspath('..'))
|
||||
|
||||
# import sphinx_rtd_theme
|
||||
|
||||
@@ -7,9 +7,9 @@ from collections import defaultdict
|
||||
from copy import deepcopy
|
||||
|
||||
from find_apps import find_apps
|
||||
from find_build_apps import BUILD_SYSTEMS, BUILD_SYSTEM_CMAKE
|
||||
from find_build_apps import BUILD_SYSTEM_CMAKE, BUILD_SYSTEMS
|
||||
from idf_py_actions.constants import PREVIEW_TARGETS, SUPPORTED_TARGETS
|
||||
from ttfw_idf.IDFAssignTest import ExampleAssignTest, TestAppsAssignTest
|
||||
from idf_py_actions.constants import SUPPORTED_TARGETS, PREVIEW_TARGETS
|
||||
|
||||
TEST_LABELS = {
|
||||
'example_test': 'BOT_LABEL_EXAMPLE_TEST',
|
||||
@@ -73,15 +73,15 @@ def main():
|
||||
default=BUILD_SYSTEM_CMAKE)
|
||||
parser.add_argument('-c', '--ci-config-file',
|
||||
required=True,
|
||||
help="gitlab ci config target-test file")
|
||||
help='gitlab ci config target-test file')
|
||||
parser.add_argument('-o', '--output-path',
|
||||
required=True,
|
||||
help="output path of the scan result")
|
||||
parser.add_argument("--exclude", nargs="*",
|
||||
help='output path of the scan result')
|
||||
parser.add_argument('--exclude', nargs='*',
|
||||
help='Ignore specified directory. Can be used multiple times.')
|
||||
parser.add_argument('--preserve', action="store_true",
|
||||
parser.add_argument('--preserve', action='store_true',
|
||||
help='add this flag to preserve artifacts for all apps')
|
||||
parser.add_argument('--build-all', action="store_true",
|
||||
parser.add_argument('--build-all', action='store_true',
|
||||
help='add this flag to build all apps')
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
@@ -13,12 +13,12 @@
|
||||
# limitations under the License.
|
||||
|
||||
from __future__ import unicode_literals
|
||||
from io import open
|
||||
|
||||
import logging
|
||||
from io import open
|
||||
|
||||
import pexpect
|
||||
import pygdbmi.gdbcontroller
|
||||
|
||||
from tiny_test_fw import Utility
|
||||
|
||||
try:
|
||||
|
||||
@@ -22,7 +22,8 @@ import sys
|
||||
from abc import abstractmethod
|
||||
|
||||
from tiny_test_fw import App
|
||||
from .IDFAssignTest import ExampleGroup, TestAppsGroup, UnitTestGroup, IDFCaseGroup, ComponentUTGroup
|
||||
|
||||
from .IDFAssignTest import ComponentUTGroup, ExampleGroup, IDFCaseGroup, TestAppsGroup, UnitTestGroup
|
||||
|
||||
try:
|
||||
import gitlab_api
|
||||
@@ -36,8 +37,8 @@ def parse_encrypted_flag(args, offs, binary):
|
||||
# If the current entry is a partition, we have to check whether it is
|
||||
# the one we are looking for or not
|
||||
try:
|
||||
if (entry["offset"], entry["file"]) == (offs, binary):
|
||||
return entry["encrypted"] == "true"
|
||||
if (entry['offset'], entry['file']) == (offs, binary):
|
||||
return entry['encrypted'] == 'true'
|
||||
except (TypeError, KeyError):
|
||||
# TypeError occurs if the entry is a list, which is possible in JSON
|
||||
# data structure.
|
||||
@@ -58,12 +59,12 @@ def parse_flash_settings(path, default_encryption=False):
|
||||
# The following list only contains the files that need encryption
|
||||
encrypt_files = []
|
||||
|
||||
if file_name == "flasher_args.json":
|
||||
if file_name == 'flasher_args.json':
|
||||
# CMake version using build metadata file
|
||||
with open(path, "r") as f:
|
||||
with open(path, 'r') as f:
|
||||
args = json.load(f)
|
||||
|
||||
for (offs, binary) in args["flash_files"].items():
|
||||
for (offs, binary) in args['flash_files'].items():
|
||||
if offs:
|
||||
flash_files.append((offs, binary))
|
||||
encrypted = parse_encrypted_flag(args, offs, binary)
|
||||
@@ -73,15 +74,15 @@ def parse_flash_settings(path, default_encryption=False):
|
||||
if (encrypted is None and default_encryption) or encrypted:
|
||||
encrypt_files.append((offs, binary))
|
||||
|
||||
flash_settings = args["flash_settings"]
|
||||
app_name = os.path.splitext(args["app"]["file"])[0]
|
||||
flash_settings = args['flash_settings']
|
||||
app_name = os.path.splitext(args['app']['file'])[0]
|
||||
else:
|
||||
# GNU Make version uses download.config arguments file
|
||||
with open(path, "r") as f:
|
||||
args = f.readlines()[-1].split(" ")
|
||||
with open(path, 'r') as f:
|
||||
args = f.readlines()[-1].split(' ')
|
||||
flash_settings = {}
|
||||
for idx in range(0, len(args), 2): # process arguments in pairs
|
||||
if args[idx].startswith("--"):
|
||||
if args[idx].startswith('--'):
|
||||
# strip the -- from the command line argument
|
||||
flash_settings[args[idx][2:]] = args[idx + 1]
|
||||
else:
|
||||
@@ -92,7 +93,7 @@ def parse_flash_settings(path, default_encryption=False):
|
||||
encrypt_files = flash_files
|
||||
# we can only guess app name in download.config.
|
||||
for p in flash_files:
|
||||
if not os.path.dirname(p[1]) and "partition" not in p[1]:
|
||||
if not os.path.dirname(p[1]) and 'partition' not in p[1]:
|
||||
# app bin usually in the same dir with download.config and it's not partition table
|
||||
app_name = os.path.splitext(p[1])[0]
|
||||
break
|
||||
@@ -107,9 +108,9 @@ class Artifacts(object):
|
||||
# at least one of app_path or config_name is not None. otherwise we can't match artifact
|
||||
assert app_path or config_name
|
||||
assert os.path.exists(artifact_index_file)
|
||||
self.gitlab_inst = gitlab_api.Gitlab(os.getenv("CI_PROJECT_ID"))
|
||||
self.gitlab_inst = gitlab_api.Gitlab(os.getenv('CI_PROJECT_ID'))
|
||||
self.dest_root_path = dest_root_path
|
||||
with open(artifact_index_file, "r") as f:
|
||||
with open(artifact_index_file, 'r') as f:
|
||||
artifact_index = json.load(f)
|
||||
self.artifact_info = self._find_artifact(artifact_index, app_path, config_name, target)
|
||||
|
||||
@@ -120,11 +121,11 @@ class Artifacts(object):
|
||||
if app_path:
|
||||
# We use endswith here to avoid issue like:
|
||||
# examples_protocols_mqtt_ws but return a examples_protocols_mqtt_wss failure
|
||||
match_result = artifact_info["app_dir"].endswith(app_path)
|
||||
match_result = artifact_info['app_dir'].endswith(app_path)
|
||||
if config_name:
|
||||
match_result = match_result and config_name == artifact_info["config"]
|
||||
match_result = match_result and config_name == artifact_info['config']
|
||||
if target:
|
||||
match_result = match_result and target == artifact_info["target"]
|
||||
match_result = match_result and target == artifact_info['target']
|
||||
if match_result:
|
||||
ret = artifact_info
|
||||
break
|
||||
@@ -134,15 +135,15 @@ class Artifacts(object):
|
||||
|
||||
def _get_app_base_path(self):
|
||||
if self.artifact_info:
|
||||
return os.path.join(self.artifact_info["work_dir"], self.artifact_info["build_dir"])
|
||||
return os.path.join(self.artifact_info['work_dir'], self.artifact_info['build_dir'])
|
||||
else:
|
||||
return None
|
||||
|
||||
def _get_flash_arg_file(self, base_path, job_id):
|
||||
if self.artifact_info["build_system"] == "cmake":
|
||||
flash_arg_file = os.path.join(base_path, "flasher_args.json")
|
||||
if self.artifact_info['build_system'] == 'cmake':
|
||||
flash_arg_file = os.path.join(base_path, 'flasher_args.json')
|
||||
else:
|
||||
flash_arg_file = os.path.join(base_path, "download.config")
|
||||
flash_arg_file = os.path.join(base_path, 'download.config')
|
||||
|
||||
self.gitlab_inst.download_artifact(job_id, [flash_arg_file], self.dest_root_path)
|
||||
return flash_arg_file
|
||||
@@ -152,19 +153,19 @@ class Artifacts(object):
|
||||
# files also appear in the first list
|
||||
flash_files, _, _, app_name = parse_flash_settings(os.path.join(self.dest_root_path, flash_arg_file))
|
||||
artifact_files = [os.path.join(base_path, p[1]) for p in flash_files]
|
||||
artifact_files.append(os.path.join(base_path, app_name + ".elf"))
|
||||
artifact_files.append(os.path.join(base_path, app_name + '.elf'))
|
||||
|
||||
self.gitlab_inst.download_artifact(job_id, artifact_files, self.dest_root_path)
|
||||
|
||||
def _download_sdkconfig_file(self, base_path, job_id):
|
||||
self.gitlab_inst.download_artifact(job_id, [os.path.join(os.path.dirname(base_path), "sdkconfig")],
|
||||
self.gitlab_inst.download_artifact(job_id, [os.path.join(os.path.dirname(base_path), 'sdkconfig')],
|
||||
self.dest_root_path)
|
||||
|
||||
def download_artifacts(self):
|
||||
if not self.artifact_info:
|
||||
return None
|
||||
base_path = self._get_app_base_path()
|
||||
job_id = self.artifact_info["ci_job_id"]
|
||||
job_id = self.artifact_info['ci_job_id']
|
||||
# 1. download flash args file
|
||||
flash_arg_file = self._get_flash_arg_file(base_path, job_id)
|
||||
|
||||
@@ -177,15 +178,15 @@ class Artifacts(object):
|
||||
|
||||
def download_artifact_files(self, file_names):
|
||||
if self.artifact_info:
|
||||
base_path = os.path.join(self.artifact_info["work_dir"], self.artifact_info["build_dir"])
|
||||
job_id = self.artifact_info["ci_job_id"]
|
||||
base_path = os.path.join(self.artifact_info['work_dir'], self.artifact_info['build_dir'])
|
||||
job_id = self.artifact_info['ci_job_id']
|
||||
|
||||
# download all binary files
|
||||
artifact_files = [os.path.join(base_path, fn) for fn in file_names]
|
||||
self.gitlab_inst.download_artifact(job_id, artifact_files, self.dest_root_path)
|
||||
|
||||
# download sdkconfig file
|
||||
self.gitlab_inst.download_artifact(job_id, [os.path.join(os.path.dirname(base_path), "sdkconfig")],
|
||||
self.gitlab_inst.download_artifact(job_id, [os.path.join(os.path.dirname(base_path), 'sdkconfig')],
|
||||
self.dest_root_path)
|
||||
else:
|
||||
base_path = None
|
||||
@@ -197,13 +198,13 @@ class UnitTestArtifacts(Artifacts):
|
||||
|
||||
def _get_app_base_path(self):
|
||||
if self.artifact_info:
|
||||
output_dir = self.BUILDS_DIR_RE.sub('output/', self.artifact_info["build_dir"])
|
||||
return os.path.join(self.artifact_info["app_dir"], output_dir)
|
||||
output_dir = self.BUILDS_DIR_RE.sub('output/', self.artifact_info['build_dir'])
|
||||
return os.path.join(self.artifact_info['app_dir'], output_dir)
|
||||
else:
|
||||
return None
|
||||
|
||||
def _download_sdkconfig_file(self, base_path, job_id):
|
||||
self.gitlab_inst.download_artifact(job_id, [os.path.join(base_path, "sdkconfig")], self.dest_root_path)
|
||||
self.gitlab_inst.download_artifact(job_id, [os.path.join(base_path, 'sdkconfig')], self.dest_root_path)
|
||||
|
||||
|
||||
class IDFApp(App.BaseApp):
|
||||
@@ -212,8 +213,8 @@ class IDFApp(App.BaseApp):
|
||||
idf applications should inherent from this class and overwrite method get_binary_path.
|
||||
"""
|
||||
|
||||
IDF_DOWNLOAD_CONFIG_FILE = "download.config"
|
||||
IDF_FLASH_ARGS_FILE = "flasher_args.json"
|
||||
IDF_DOWNLOAD_CONFIG_FILE = 'download.config'
|
||||
IDF_FLASH_ARGS_FILE = 'flasher_args.json'
|
||||
|
||||
def __init__(self, app_path, config_name=None, target=None, case_group=IDFCaseGroup, artifact_cls=Artifacts):
|
||||
super(IDFApp, self).__init__(app_path)
|
||||
@@ -229,11 +230,11 @@ class IDFApp(App.BaseApp):
|
||||
assert os.path.exists(self.binary_path)
|
||||
if self.IDF_DOWNLOAD_CONFIG_FILE not in os.listdir(self.binary_path):
|
||||
if self.IDF_FLASH_ARGS_FILE not in os.listdir(self.binary_path):
|
||||
msg = ("Neither {} nor {} exists. "
|
||||
msg = ('Neither {} nor {} exists. '
|
||||
"Try to run 'make print_flash_cmd | tail -n 1 > {}/{}' "
|
||||
"or 'idf.py build' "
|
||||
"for resolving the issue."
|
||||
"").format(self.IDF_DOWNLOAD_CONFIG_FILE, self.IDF_FLASH_ARGS_FILE,
|
||||
'for resolving the issue.'
|
||||
'').format(self.IDF_DOWNLOAD_CONFIG_FILE, self.IDF_FLASH_ARGS_FILE,
|
||||
self.binary_path, self.IDF_DOWNLOAD_CONFIG_FILE)
|
||||
raise AssertionError(msg)
|
||||
|
||||
@@ -252,7 +253,7 @@ class IDFApp(App.BaseApp):
|
||||
|
||||
@classmethod
|
||||
def get_sdk_path(cls): # type: () -> str
|
||||
idf_path = os.getenv("IDF_PATH")
|
||||
idf_path = os.getenv('IDF_PATH')
|
||||
assert idf_path
|
||||
assert os.path.exists(idf_path)
|
||||
return idf_path
|
||||
@@ -263,7 +264,7 @@ class IDFApp(App.BaseApp):
|
||||
|
||||
Note: could be overwritten by a derived class to provide other locations or order
|
||||
"""
|
||||
return [os.path.join(self.binary_path, "sdkconfig"), os.path.join(self.binary_path, "..", "sdkconfig")]
|
||||
return [os.path.join(self.binary_path, 'sdkconfig'), os.path.join(self.binary_path, '..', 'sdkconfig')]
|
||||
|
||||
def get_sdkconfig(self):
|
||||
"""
|
||||
@@ -306,13 +307,13 @@ class IDFApp(App.BaseApp):
|
||||
if path:
|
||||
return os.path.join(self.idf_path, path)
|
||||
else:
|
||||
raise OSError("Failed to get binary for {}".format(self))
|
||||
raise OSError('Failed to get binary for {}'.format(self))
|
||||
|
||||
def _get_elf_file_path(self):
|
||||
ret = ""
|
||||
ret = ''
|
||||
file_names = os.listdir(self.binary_path)
|
||||
for fn in file_names:
|
||||
if os.path.splitext(fn)[1] == ".elf":
|
||||
if os.path.splitext(fn)[1] == '.elf':
|
||||
ret = os.path.join(self.binary_path, fn)
|
||||
return ret
|
||||
|
||||
@@ -343,14 +344,14 @@ class IDFApp(App.BaseApp):
|
||||
# a default encrpytion flag: the macro
|
||||
# CONFIG_SECURE_FLASH_ENCRYPTION_MODE_DEVELOPMENT
|
||||
sdkconfig_dict = self.get_sdkconfig()
|
||||
default_encryption = "CONFIG_SECURE_FLASH_ENCRYPTION_MODE_DEVELOPMENT" in sdkconfig_dict
|
||||
default_encryption = 'CONFIG_SECURE_FLASH_ENCRYPTION_MODE_DEVELOPMENT' in sdkconfig_dict
|
||||
|
||||
flash_files, encrypt_files, flash_settings, _ = parse_flash_settings(path, default_encryption)
|
||||
|
||||
# Flash setting "encrypt" only and only if all the files to flash
|
||||
# must be encrypted. Else, this parameter should be False.
|
||||
# All files must be encrypted is both file lists are the same
|
||||
flash_settings["encrypt"] = sorted(flash_files) == sorted(encrypt_files)
|
||||
flash_settings['encrypt'] = sorted(flash_files) == sorted(encrypt_files)
|
||||
|
||||
return self._int_offs_abs_paths(flash_files), self._int_offs_abs_paths(encrypt_files), flash_settings
|
||||
|
||||
@@ -363,9 +364,9 @@ class IDFApp(App.BaseApp):
|
||||
(Called from constructor)
|
||||
"""
|
||||
partition_tool = os.path.join(self.idf_path,
|
||||
"components",
|
||||
"partition_table",
|
||||
"gen_esp32part.py")
|
||||
'components',
|
||||
'partition_table',
|
||||
'gen_esp32part.py')
|
||||
assert os.path.exists(partition_tool)
|
||||
|
||||
errors = []
|
||||
@@ -393,18 +394,18 @@ class IDFApp(App.BaseApp):
|
||||
p,
|
||||
os.linesep,
|
||||
msg) for p, msg in errors])
|
||||
raise ValueError("No partition table found for IDF binary path: {}{}{}".format(self.binary_path,
|
||||
raise ValueError('No partition table found for IDF binary path: {}{}{}'.format(self.binary_path,
|
||||
os.linesep,
|
||||
traceback_msg))
|
||||
|
||||
partition_table = dict()
|
||||
for line in raw_data.splitlines():
|
||||
if line[0] != "#":
|
||||
if line[0] != '#':
|
||||
try:
|
||||
_name, _type, _subtype, _offset, _size, _flags = line.split(",")
|
||||
if _size[-1] == "K":
|
||||
_name, _type, _subtype, _offset, _size, _flags = line.split(',')
|
||||
if _size[-1] == 'K':
|
||||
_size = int(_size[:-1]) * 1024
|
||||
elif _size[-1] == "M":
|
||||
elif _size[-1] == 'M':
|
||||
_size = int(_size[:-1]) * 1024 * 1024
|
||||
else:
|
||||
_size = int(_size)
|
||||
@@ -412,11 +413,11 @@ class IDFApp(App.BaseApp):
|
||||
except ValueError:
|
||||
continue
|
||||
partition_table[_name] = {
|
||||
"type": _type,
|
||||
"subtype": _subtype,
|
||||
"offset": _offset,
|
||||
"size": _size,
|
||||
"flags": _flags
|
||||
'type': _type,
|
||||
'subtype': _subtype,
|
||||
'offset': _offset,
|
||||
'size': _size,
|
||||
'flags': _flags
|
||||
}
|
||||
|
||||
return partition_table
|
||||
@@ -444,11 +445,11 @@ class Example(IDFApp):
|
||||
"""
|
||||
overrides the parent method to provide exact path of sdkconfig for example tests
|
||||
"""
|
||||
return [os.path.join(self.binary_path, "..", "sdkconfig")]
|
||||
return [os.path.join(self.binary_path, '..', 'sdkconfig')]
|
||||
|
||||
def _try_get_binary_from_local_fs(self):
|
||||
# build folder of example path
|
||||
path = os.path.join(self.idf_path, self.app_path, "build")
|
||||
path = os.path.join(self.idf_path, self.app_path, 'build')
|
||||
if os.path.exists(path):
|
||||
return path
|
||||
|
||||
@@ -456,11 +457,11 @@ class Example(IDFApp):
|
||||
# Path format: $IDF_PATH/build_examples/app_path_with_underscores/config/target
|
||||
# (see tools/ci/build_examples.sh)
|
||||
# For example: $IDF_PATH/build_examples/examples_get-started_blink/default/esp32
|
||||
app_path_underscored = self.app_path.replace(os.path.sep, "_")
|
||||
app_path_underscored = self.app_path.replace(os.path.sep, '_')
|
||||
example_path = os.path.join(self.idf_path, self.case_group.LOCAL_BUILD_DIR)
|
||||
for dirpath in os.listdir(example_path):
|
||||
if os.path.basename(dirpath) == app_path_underscored:
|
||||
path = os.path.join(example_path, dirpath, self.config_name, self.target, "build")
|
||||
path = os.path.join(example_path, dirpath, self.config_name, self.target, 'build')
|
||||
if os.path.exists(path):
|
||||
return path
|
||||
else:
|
||||
@@ -476,18 +477,18 @@ class UT(IDFApp):
|
||||
super(UT, self).__init__(app_path, config_name, target, case_group, artifacts_cls)
|
||||
|
||||
def _try_get_binary_from_local_fs(self):
|
||||
path = os.path.join(self.idf_path, self.app_path, "build")
|
||||
path = os.path.join(self.idf_path, self.app_path, 'build')
|
||||
if os.path.exists(path):
|
||||
return path
|
||||
|
||||
# first try to get from build folder of unit-test-app
|
||||
path = os.path.join(self.idf_path, "tools", "unit-test-app", "build")
|
||||
path = os.path.join(self.idf_path, 'tools', 'unit-test-app', 'build')
|
||||
if os.path.exists(path):
|
||||
# found, use bin in build path
|
||||
return path
|
||||
|
||||
# ``build_unit_test.sh`` will copy binary to output folder
|
||||
path = os.path.join(self.idf_path, "tools", "unit-test-app", "output", self.target, self.config_name)
|
||||
path = os.path.join(self.idf_path, 'tools', 'unit-test-app', 'output', self.target, self.config_name)
|
||||
if os.path.exists(path):
|
||||
return path
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ import gitlab_api
|
||||
from tiny_test_fw.Utility import CIAssignTest
|
||||
|
||||
try:
|
||||
from idf_py_actions.constants import SUPPORTED_TARGETS, PREVIEW_TARGETS
|
||||
from idf_py_actions.constants import PREVIEW_TARGETS, SUPPORTED_TARGETS
|
||||
except ImportError:
|
||||
SUPPORTED_TARGETS = []
|
||||
PREVIEW_TARGETS = []
|
||||
|
||||
@@ -13,14 +13,15 @@
|
||||
# limitations under the License.
|
||||
|
||||
""" DUT for IDF applications """
|
||||
import functools
|
||||
import os
|
||||
import os.path
|
||||
import sys
|
||||
import re
|
||||
import functools
|
||||
import tempfile
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
import time
|
||||
|
||||
import pexpect
|
||||
|
||||
# python2 and python3 queue package name is different
|
||||
@@ -29,18 +30,16 @@ try:
|
||||
except ImportError:
|
||||
import queue as _queue
|
||||
|
||||
|
||||
from serial.tools import list_ports
|
||||
|
||||
from tiny_test_fw import DUT, Utility
|
||||
|
||||
try:
|
||||
import esptool
|
||||
except ImportError: # cheat and use IDF's copy of esptool if available
|
||||
idf_path = os.getenv("IDF_PATH")
|
||||
idf_path = os.getenv('IDF_PATH')
|
||||
if not idf_path or not os.path.exists(idf_path):
|
||||
raise
|
||||
sys.path.insert(0, os.path.join(idf_path, "components", "esptool_py", "esptool"))
|
||||
sys.path.insert(0, os.path.join(idf_path, 'components', 'esptool_py', 'esptool'))
|
||||
import esptool
|
||||
|
||||
|
||||
@@ -54,14 +53,14 @@ class IDFDUTException(RuntimeError):
|
||||
|
||||
class IDFRecvThread(DUT.RecvThread):
|
||||
|
||||
PERFORMANCE_PATTERN = re.compile(r"\[Performance]\[(\w+)]: ([^\r\n]+)\r?\n")
|
||||
PERFORMANCE_PATTERN = re.compile(r'\[Performance]\[(\w+)]: ([^\r\n]+)\r?\n')
|
||||
EXCEPTION_PATTERNS = [
|
||||
re.compile(r"(Guru Meditation Error: Core\s+\d panic'ed \([\w].*?\))"),
|
||||
re.compile(r"(abort\(\) was called at PC 0x[a-fA-F\d]{8} on core \d)"),
|
||||
re.compile(r"(rst 0x\d+ \(TG\dWDT_SYS_RESET|TGWDT_CPU_RESET\))")
|
||||
re.compile(r'(abort\(\) was called at PC 0x[a-fA-F\d]{8} on core \d)'),
|
||||
re.compile(r'(rst 0x\d+ \(TG\dWDT_SYS_RESET|TGWDT_CPU_RESET\))')
|
||||
]
|
||||
BACKTRACE_PATTERN = re.compile(r"Backtrace:((\s(0x[0-9a-f]{8}):0x[0-9a-f]{8})+)")
|
||||
BACKTRACE_ADDRESS_PATTERN = re.compile(r"(0x[0-9a-f]{8}):0x[0-9a-f]{8}")
|
||||
BACKTRACE_PATTERN = re.compile(r'Backtrace:((\s(0x[0-9a-f]{8}):0x[0-9a-f]{8})+)')
|
||||
BACKTRACE_ADDRESS_PATTERN = re.compile(r'(0x[0-9a-f]{8}):0x[0-9a-f]{8}')
|
||||
|
||||
def __init__(self, read, dut):
|
||||
super(IDFRecvThread, self).__init__(read, dut)
|
||||
@@ -71,8 +70,8 @@ class IDFRecvThread(DUT.RecvThread):
|
||||
def collect_performance(self, comp_data):
|
||||
matches = self.PERFORMANCE_PATTERN.findall(comp_data)
|
||||
for match in matches:
|
||||
Utility.console_log("[Performance][{}]: {}".format(match[0], match[1]),
|
||||
color="orange")
|
||||
Utility.console_log('[Performance][{}]: {}'.format(match[0], match[1]),
|
||||
color='orange')
|
||||
self.performance_items.put((match[0], match[1]))
|
||||
|
||||
def detect_exception(self, comp_data):
|
||||
@@ -83,7 +82,7 @@ class IDFRecvThread(DUT.RecvThread):
|
||||
if match:
|
||||
start = match.end()
|
||||
self.exceptions.put(match.group(0))
|
||||
Utility.console_log("[Exception]: {}".format(match.group(0)), color="red")
|
||||
Utility.console_log('[Exception]: {}'.format(match.group(0)), color='red')
|
||||
else:
|
||||
break
|
||||
|
||||
@@ -93,18 +92,18 @@ class IDFRecvThread(DUT.RecvThread):
|
||||
match = self.BACKTRACE_PATTERN.search(comp_data, pos=start)
|
||||
if match:
|
||||
start = match.end()
|
||||
Utility.console_log("[Backtrace]:{}".format(match.group(1)), color="red")
|
||||
Utility.console_log('[Backtrace]:{}'.format(match.group(1)), color='red')
|
||||
# translate backtrace
|
||||
addresses = self.BACKTRACE_ADDRESS_PATTERN.findall(match.group(1))
|
||||
translated_backtrace = ""
|
||||
translated_backtrace = ''
|
||||
for addr in addresses:
|
||||
ret = self.dut.lookup_pc_address(addr)
|
||||
if ret:
|
||||
translated_backtrace += ret + "\n"
|
||||
translated_backtrace += ret + '\n'
|
||||
if translated_backtrace:
|
||||
Utility.console_log("Translated backtrace\n:" + translated_backtrace, color="yellow")
|
||||
Utility.console_log('Translated backtrace\n:' + translated_backtrace, color='yellow')
|
||||
else:
|
||||
Utility.console_log("Failed to translate backtrace", color="yellow")
|
||||
Utility.console_log('Failed to translate backtrace', color='yellow')
|
||||
else:
|
||||
break
|
||||
|
||||
@@ -149,7 +148,7 @@ class IDFDUT(DUT.SerialDUT):
|
||||
|
||||
# /dev/ttyAMA0 port is listed in Raspberry Pi
|
||||
# /dev/tty.Bluetooth-Incoming-Port port is listed in Mac
|
||||
INVALID_PORT_PATTERN = re.compile(r"AMA|Bluetooth")
|
||||
INVALID_PORT_PATTERN = re.compile(r'AMA|Bluetooth')
|
||||
# if need to erase NVS partition in start app
|
||||
ERASE_NVS = True
|
||||
RECV_THREAD_CLS = IDFRecvThread
|
||||
@@ -163,7 +162,7 @@ class IDFDUT(DUT.SerialDUT):
|
||||
|
||||
@classmethod
|
||||
def _get_rom(cls):
|
||||
raise NotImplementedError("This is an abstraction class, method not defined.")
|
||||
raise NotImplementedError('This is an abstraction class, method not defined.')
|
||||
|
||||
@classmethod
|
||||
def get_mac(cls, app, port):
|
||||
@@ -200,7 +199,7 @@ class IDFDUT(DUT.SerialDUT):
|
||||
# Otherwise overwrite it in ESP8266DUT
|
||||
inst = esptool.ESPLoader.detect_chip(port)
|
||||
if expected_rom_class and type(inst) != expected_rom_class:
|
||||
raise RuntimeError("Target not expected")
|
||||
raise RuntimeError('Target not expected')
|
||||
return inst.read_mac() is not None, get_target_by_rom_class(type(inst))
|
||||
except(esptool.FatalError, RuntimeError):
|
||||
return False, None
|
||||
@@ -227,7 +226,7 @@ class IDFDUT(DUT.SerialDUT):
|
||||
# and encrypt_files contains the ones to flash encrypted.
|
||||
flash_files = self.app.flash_files
|
||||
encrypt_files = self.app.encrypt_files
|
||||
encrypt = self.app.flash_settings.get("encrypt", False)
|
||||
encrypt = self.app.flash_settings.get('encrypt', False)
|
||||
if encrypt:
|
||||
flash_files = encrypt_files
|
||||
encrypt_files = []
|
||||
@@ -236,12 +235,12 @@ class IDFDUT(DUT.SerialDUT):
|
||||
for entry in flash_files
|
||||
if entry not in encrypt_files]
|
||||
|
||||
flash_files = [(offs, open(path, "rb")) for (offs, path) in flash_files]
|
||||
encrypt_files = [(offs, open(path, "rb")) for (offs, path) in encrypt_files]
|
||||
flash_files = [(offs, open(path, 'rb')) for (offs, path) in flash_files]
|
||||
encrypt_files = [(offs, open(path, 'rb')) for (offs, path) in encrypt_files]
|
||||
|
||||
if erase_nvs:
|
||||
address = self.app.partition_table["nvs"]["offset"]
|
||||
size = self.app.partition_table["nvs"]["size"]
|
||||
address = self.app.partition_table['nvs']['offset']
|
||||
size = self.app.partition_table['nvs']['size']
|
||||
nvs_file = tempfile.TemporaryFile()
|
||||
nvs_file.write(b'\xff' * size)
|
||||
nvs_file.seek(0)
|
||||
@@ -252,7 +251,7 @@ class IDFDUT(DUT.SerialDUT):
|
||||
# Get the CONFIG_SECURE_FLASH_ENCRYPTION_MODE_DEVELOPMENT macro
|
||||
# value. If it is set to True, then NVS is always encrypted.
|
||||
sdkconfig_dict = self.app.get_sdkconfig()
|
||||
macro_encryption = "CONFIG_SECURE_FLASH_ENCRYPTION_MODE_DEVELOPMENT" in sdkconfig_dict
|
||||
macro_encryption = 'CONFIG_SECURE_FLASH_ENCRYPTION_MODE_DEVELOPMENT' in sdkconfig_dict
|
||||
# If the macro is not enabled (plain text flash) or all files
|
||||
# must be encrypted, add NVS to flash_files.
|
||||
if not macro_encryption or encrypt:
|
||||
@@ -270,9 +269,9 @@ class IDFDUT(DUT.SerialDUT):
|
||||
# write_flash expects the parameter encrypt_files to be None and not
|
||||
# an empty list, so perform the check here
|
||||
flash_args = FlashArgs({
|
||||
'flash_size': self.app.flash_settings["flash_size"],
|
||||
'flash_mode': self.app.flash_settings["flash_mode"],
|
||||
'flash_freq': self.app.flash_settings["flash_freq"],
|
||||
'flash_size': self.app.flash_settings['flash_size'],
|
||||
'flash_mode': self.app.flash_settings['flash_mode'],
|
||||
'flash_freq': self.app.flash_settings['flash_freq'],
|
||||
'addr_filename': flash_files,
|
||||
'encrypt_files': encrypt_files or None,
|
||||
'no_stub': False,
|
||||
@@ -328,9 +327,9 @@ class IDFDUT(DUT.SerialDUT):
|
||||
"""
|
||||
raise NotImplementedError() # TODO: implement this
|
||||
# address = self.app.partition_table[partition]["offset"]
|
||||
size = self.app.partition_table[partition]["size"]
|
||||
size = self.app.partition_table[partition]['size']
|
||||
# TODO can use esp.erase_region() instead of this, I think
|
||||
with open(".erase_partition.tmp", "wb") as f:
|
||||
with open('.erase_partition.tmp', 'wb') as f:
|
||||
f.write(chr(0xFF) * size)
|
||||
|
||||
@_uses_esptool
|
||||
@@ -356,18 +355,18 @@ class IDFDUT(DUT.SerialDUT):
|
||||
"""
|
||||
if os.path.isabs(output_file) is False:
|
||||
output_file = os.path.relpath(output_file, self.app.get_log_folder())
|
||||
if "partition" in kwargs:
|
||||
partition = self.app.partition_table[kwargs["partition"]]
|
||||
_address = partition["offset"]
|
||||
_size = partition["size"]
|
||||
elif "address" in kwargs and "size" in kwargs:
|
||||
_address = kwargs["address"]
|
||||
_size = kwargs["size"]
|
||||
if 'partition' in kwargs:
|
||||
partition = self.app.partition_table[kwargs['partition']]
|
||||
_address = partition['offset']
|
||||
_size = partition['size']
|
||||
elif 'address' in kwargs and 'size' in kwargs:
|
||||
_address = kwargs['address']
|
||||
_size = kwargs['size']
|
||||
else:
|
||||
raise IDFToolError("You must specify 'partition' or ('address' and 'size') to dump flash")
|
||||
|
||||
content = esp.read_flash(_address, _size)
|
||||
with open(output_file, "wb") as f:
|
||||
with open(output_file, 'wb') as f:
|
||||
f.write(content)
|
||||
|
||||
@classmethod
|
||||
@@ -405,9 +404,9 @@ class IDFDUT(DUT.SerialDUT):
|
||||
return ports
|
||||
|
||||
def lookup_pc_address(self, pc_addr):
|
||||
cmd = ["%saddr2line" % self.TOOLCHAIN_PREFIX,
|
||||
"-pfiaC", "-e", self.app.elf_file, pc_addr]
|
||||
ret = ""
|
||||
cmd = ['%saddr2line' % self.TOOLCHAIN_PREFIX,
|
||||
'-pfiaC', '-e', self.app.elf_file, pc_addr]
|
||||
ret = ''
|
||||
try:
|
||||
translation = subprocess.check_output(cmd)
|
||||
ret = translation.decode()
|
||||
@@ -439,7 +438,7 @@ class IDFDUT(DUT.SerialDUT):
|
||||
|
||||
def stop_receive(self):
|
||||
if self.receive_thread:
|
||||
for name in ["performance_items", "exceptions"]:
|
||||
for name in ['performance_items', 'exceptions']:
|
||||
source_queue = getattr(self.receive_thread, name)
|
||||
dest_queue = getattr(self, name)
|
||||
self._queue_copy(source_queue, dest_queue)
|
||||
@@ -447,7 +446,7 @@ class IDFDUT(DUT.SerialDUT):
|
||||
|
||||
def get_exceptions(self):
|
||||
""" Get exceptions detected by DUT receive thread. """
|
||||
return self._get_from_queue("exceptions")
|
||||
return self._get_from_queue('exceptions')
|
||||
|
||||
def get_performance_items(self):
|
||||
"""
|
||||
@@ -456,18 +455,18 @@ class IDFDUT(DUT.SerialDUT):
|
||||
|
||||
:return: a list of performance items.
|
||||
"""
|
||||
return self._get_from_queue("performance_items")
|
||||
return self._get_from_queue('performance_items')
|
||||
|
||||
def close(self):
|
||||
super(IDFDUT, self).close()
|
||||
if not self.allow_dut_exception and self.get_exceptions():
|
||||
Utility.console_log("DUT exception detected on {}".format(self), color="red")
|
||||
Utility.console_log('DUT exception detected on {}'.format(self), color='red')
|
||||
raise IDFDUTException()
|
||||
|
||||
|
||||
class ESP32DUT(IDFDUT):
|
||||
TARGET = "esp32"
|
||||
TOOLCHAIN_PREFIX = "xtensa-esp32-elf-"
|
||||
TARGET = 'esp32'
|
||||
TOOLCHAIN_PREFIX = 'xtensa-esp32-elf-'
|
||||
|
||||
@classmethod
|
||||
def _get_rom(cls):
|
||||
@@ -478,8 +477,8 @@ class ESP32DUT(IDFDUT):
|
||||
|
||||
|
||||
class ESP32S2DUT(IDFDUT):
|
||||
TARGET = "esp32s2"
|
||||
TOOLCHAIN_PREFIX = "xtensa-esp32s2-elf-"
|
||||
TARGET = 'esp32s2'
|
||||
TOOLCHAIN_PREFIX = 'xtensa-esp32s2-elf-'
|
||||
|
||||
@classmethod
|
||||
def _get_rom(cls):
|
||||
@@ -490,8 +489,8 @@ class ESP32S2DUT(IDFDUT):
|
||||
|
||||
|
||||
class ESP32C3DUT(IDFDUT):
|
||||
TARGET = "esp32c3"
|
||||
TOOLCHAIN_PREFIX = "riscv32-esp-elf-"
|
||||
TARGET = 'esp32c3'
|
||||
TOOLCHAIN_PREFIX = 'riscv32-esp-elf-'
|
||||
|
||||
@classmethod
|
||||
def _get_rom(cls):
|
||||
@@ -502,8 +501,8 @@ class ESP32C3DUT(IDFDUT):
|
||||
|
||||
|
||||
class ESP8266DUT(IDFDUT):
|
||||
TARGET = "esp8266"
|
||||
TOOLCHAIN_PREFIX = "xtensa-lx106-elf-"
|
||||
TARGET = 'esp8266'
|
||||
TOOLCHAIN_PREFIX = 'xtensa-lx106-elf-'
|
||||
|
||||
@classmethod
|
||||
def _get_rom(cls):
|
||||
@@ -528,34 +527,34 @@ class IDFQEMUDUT(IDFDUT):
|
||||
QEMU_SERIAL_PORT = 3334
|
||||
|
||||
def __init__(self, name, port, log_file, app, allow_dut_exception=False, **kwargs):
|
||||
self.flash_image = tempfile.NamedTemporaryFile('rb+', suffix=".bin", prefix="qemu_flash_img")
|
||||
self.flash_image = tempfile.NamedTemporaryFile('rb+', suffix='.bin', prefix='qemu_flash_img')
|
||||
self.app = app
|
||||
self.flash_size = 4 * 1024 * 1024
|
||||
self._write_flash_img()
|
||||
|
||||
args = [
|
||||
"qemu-system-xtensa",
|
||||
"-nographic",
|
||||
"-machine", self.TARGET,
|
||||
"-drive", "file={},if=mtd,format=raw".format(self.flash_image.name),
|
||||
"-nic", "user,model=open_eth",
|
||||
"-serial", "tcp::{},server,nowait".format(self.QEMU_SERIAL_PORT),
|
||||
"-S",
|
||||
"-global driver=timer.esp32.timg,property=wdt_disable,value=true"]
|
||||
'qemu-system-xtensa',
|
||||
'-nographic',
|
||||
'-machine', self.TARGET,
|
||||
'-drive', 'file={},if=mtd,format=raw'.format(self.flash_image.name),
|
||||
'-nic', 'user,model=open_eth',
|
||||
'-serial', 'tcp::{},server,nowait'.format(self.QEMU_SERIAL_PORT),
|
||||
'-S',
|
||||
'-global driver=timer.esp32.timg,property=wdt_disable,value=true']
|
||||
# TODO(IDF-1242): generate a temporary efuse binary, pass it to QEMU
|
||||
|
||||
if "QEMU_BIOS_PATH" in os.environ:
|
||||
args += ["-L", os.environ["QEMU_BIOS_PATH"]]
|
||||
if 'QEMU_BIOS_PATH' in os.environ:
|
||||
args += ['-L', os.environ['QEMU_BIOS_PATH']]
|
||||
|
||||
self.qemu = pexpect.spawn(" ".join(args), timeout=self.DEFAULT_EXPECT_TIMEOUT)
|
||||
self.qemu.expect_exact(b"(qemu)")
|
||||
self.qemu = pexpect.spawn(' '.join(args), timeout=self.DEFAULT_EXPECT_TIMEOUT)
|
||||
self.qemu.expect_exact(b'(qemu)')
|
||||
super(IDFQEMUDUT, self).__init__(name, port, log_file, app, allow_dut_exception=allow_dut_exception, **kwargs)
|
||||
|
||||
def _write_flash_img(self):
|
||||
self.flash_image.seek(0)
|
||||
self.flash_image.write(b'\x00' * self.flash_size)
|
||||
for offs, path in self.app.flash_files:
|
||||
with open(path, "rb") as flash_file:
|
||||
with open(path, 'rb') as flash_file:
|
||||
contents = flash_file.read()
|
||||
self.flash_image.seek(offs)
|
||||
self.flash_image.write(contents)
|
||||
@@ -568,7 +567,7 @@ class IDFQEMUDUT(IDFDUT):
|
||||
@classmethod
|
||||
def get_mac(cls, app, port):
|
||||
# TODO(IDF-1242): get this from QEMU/efuse binary
|
||||
return "11:22:33:44:55:66"
|
||||
return '11:22:33:44:55:66'
|
||||
|
||||
@classmethod
|
||||
def confirm_dut(cls, port, **kwargs):
|
||||
@@ -577,30 +576,30 @@ class IDFQEMUDUT(IDFDUT):
|
||||
def start_app(self, erase_nvs=ERASE_NVS):
|
||||
# TODO: implement erase_nvs
|
||||
# since the flash image is generated every time in the constructor, maybe this isn't needed...
|
||||
self.qemu.sendline(b"cont\n")
|
||||
self.qemu.expect_exact(b"(qemu)")
|
||||
self.qemu.sendline(b'cont\n')
|
||||
self.qemu.expect_exact(b'(qemu)')
|
||||
|
||||
def reset(self):
|
||||
self.qemu.sendline(b"system_reset\n")
|
||||
self.qemu.expect_exact(b"(qemu)")
|
||||
self.qemu.sendline(b'system_reset\n')
|
||||
self.qemu.expect_exact(b'(qemu)')
|
||||
|
||||
def erase_partition(self, partition):
|
||||
raise NotImplementedError("method erase_partition not implemented")
|
||||
raise NotImplementedError('method erase_partition not implemented')
|
||||
|
||||
def erase_flash(self):
|
||||
raise NotImplementedError("method erase_flash not implemented")
|
||||
raise NotImplementedError('method erase_flash not implemented')
|
||||
|
||||
def dump_flash(self, output_file, **kwargs):
|
||||
raise NotImplementedError("method dump_flash not implemented")
|
||||
raise NotImplementedError('method dump_flash not implemented')
|
||||
|
||||
@classmethod
|
||||
def list_available_ports(cls):
|
||||
return ["socket://localhost:{}".format(cls.QEMU_SERIAL_PORT)]
|
||||
return ['socket://localhost:{}'.format(cls.QEMU_SERIAL_PORT)]
|
||||
|
||||
def close(self):
|
||||
super(IDFQEMUDUT, self).close()
|
||||
self.qemu.sendline(b"q\n")
|
||||
self.qemu.expect_exact(b"(qemu)")
|
||||
self.qemu.sendline(b'q\n')
|
||||
self.qemu.expect_exact(b'(qemu)')
|
||||
for _ in range(self.DEFAULT_EXPECT_TIMEOUT):
|
||||
if not self.qemu.isalive():
|
||||
break
|
||||
@@ -610,5 +609,5 @@ class IDFQEMUDUT(IDFDUT):
|
||||
|
||||
|
||||
class ESP32QEMUDUT(IDFQEMUDUT):
|
||||
TARGET = "esp32"
|
||||
TOOLCHAIN_PREFIX = "xtensa-esp32-elf-"
|
||||
TARGET = 'esp32'
|
||||
TOOLCHAIN_PREFIX = 'xtensa-esp32-elf-'
|
||||
|
||||
@@ -19,12 +19,12 @@ import re
|
||||
from copy import deepcopy
|
||||
|
||||
import junit_xml
|
||||
|
||||
from tiny_test_fw import TinyFW, Utility
|
||||
from .DebugUtils import OCDBackend, GDBBackend, CustomProcess # noqa: export DebugUtils for users
|
||||
from .IDFApp import IDFApp, Example, LoadableElfTestApp, UT, TestApp, ComponentUTApp # noqa: export all Apps for users
|
||||
from .IDFDUT import IDFDUT, ESP32DUT, ESP32S2DUT, ESP32C3DUT, ESP8266DUT, ESP32QEMUDUT # noqa: export DUTs for users
|
||||
from .unity_test_parser import TestResults, TestFormat
|
||||
|
||||
from .DebugUtils import CustomProcess, GDBBackend, OCDBackend # noqa: export DebugUtils for users
|
||||
from .IDFApp import UT, ComponentUTApp, Example, IDFApp, LoadableElfTestApp, TestApp # noqa: export all Apps for users
|
||||
from .IDFDUT import ESP32C3DUT, ESP32DUT, ESP32QEMUDUT, ESP32S2DUT, ESP8266DUT, IDFDUT # noqa: export DUTs for users
|
||||
from .unity_test_parser import TestFormat, TestResults
|
||||
|
||||
# pass TARGET_DUT_CLS_DICT to Env.py to avoid circular dependency issue.
|
||||
TARGET_DUT_CLS_DICT = {
|
||||
@@ -35,7 +35,7 @@ TARGET_DUT_CLS_DICT = {
|
||||
|
||||
|
||||
def format_case_id(target, case_name):
|
||||
return "{}.{}".format(target, case_name)
|
||||
return '{}.{}'.format(target, case_name)
|
||||
|
||||
|
||||
try:
|
||||
@@ -128,13 +128,13 @@ def test_func_generator(func, app, target, ci_target, module, execution_time, le
|
||||
dut_dict=dut_classes, **kwargs
|
||||
)
|
||||
test_func = original_method(func)
|
||||
test_func.case_info["ID"] = format_case_id(target, test_func.case_info["name"])
|
||||
test_func.case_info['ID'] = format_case_id(target, test_func.case_info['name'])
|
||||
return test_func
|
||||
|
||||
|
||||
@ci_target_check
|
||||
def idf_example_test(app=Example, target="ESP32", ci_target=None, module="examples", execution_time=1,
|
||||
level="example", erase_nvs=True, config_name=None, **kwargs):
|
||||
def idf_example_test(app=Example, target='ESP32', ci_target=None, module='examples', execution_time=1,
|
||||
level='example', erase_nvs=True, config_name=None, **kwargs):
|
||||
"""
|
||||
decorator for testing idf examples (with default values for some keyword args).
|
||||
|
||||
@@ -155,8 +155,8 @@ def idf_example_test(app=Example, target="ESP32", ci_target=None, module="exampl
|
||||
|
||||
|
||||
@ci_target_check
|
||||
def idf_unit_test(app=UT, target="ESP32", ci_target=None, module="unit-test", execution_time=1,
|
||||
level="unit", erase_nvs=True, **kwargs):
|
||||
def idf_unit_test(app=UT, target='ESP32', ci_target=None, module='unit-test', execution_time=1,
|
||||
level='unit', erase_nvs=True, **kwargs):
|
||||
"""
|
||||
decorator for testing idf unit tests (with default values for some keyword args).
|
||||
|
||||
@@ -176,8 +176,8 @@ def idf_unit_test(app=UT, target="ESP32", ci_target=None, module="unit-test", ex
|
||||
|
||||
|
||||
@ci_target_check
|
||||
def idf_custom_test(app=TestApp, target="ESP32", ci_target=None, module="misc", execution_time=1,
|
||||
level="integration", erase_nvs=True, config_name=None, **kwargs):
|
||||
def idf_custom_test(app=TestApp, target='ESP32', ci_target=None, module='misc', execution_time=1,
|
||||
level='integration', erase_nvs=True, config_name=None, **kwargs):
|
||||
"""
|
||||
decorator for idf custom tests (with default values for some keyword args).
|
||||
|
||||
@@ -198,8 +198,8 @@ def idf_custom_test(app=TestApp, target="ESP32", ci_target=None, module="misc",
|
||||
|
||||
|
||||
@ci_target_check
|
||||
def idf_component_unit_test(app=ComponentUTApp, target="ESP32", ci_target=None, module="misc", execution_time=1,
|
||||
level="integration", erase_nvs=True, config_name=None, **kwargs):
|
||||
def idf_component_unit_test(app=ComponentUTApp, target='ESP32', ci_target=None, module='misc', execution_time=1,
|
||||
level='integration', erase_nvs=True, config_name=None, **kwargs):
|
||||
"""
|
||||
decorator for idf custom tests (with default values for some keyword args).
|
||||
|
||||
@@ -253,11 +253,11 @@ def log_performance(item, value):
|
||||
:param item: performance item name
|
||||
:param value: performance value
|
||||
"""
|
||||
performance_msg = "[Performance][{}]: {}".format(item, value)
|
||||
Utility.console_log(performance_msg, "orange")
|
||||
performance_msg = '[Performance][{}]: {}'.format(item, value)
|
||||
Utility.console_log(performance_msg, 'orange')
|
||||
# update to junit test report
|
||||
current_junit_case = TinyFW.JunitReport.get_current_test_case()
|
||||
current_junit_case.stdout += performance_msg + "\r\n"
|
||||
current_junit_case.stdout += performance_msg + '\r\n'
|
||||
|
||||
|
||||
def check_performance(item, value, target):
|
||||
@@ -300,7 +300,7 @@ def check_performance(item, value, target):
|
||||
# if no exception was thrown then the performance is met and no need to continue
|
||||
break
|
||||
else:
|
||||
raise AssertionError("Failed to get performance standard for {}".format(item))
|
||||
raise AssertionError('Failed to get performance standard for {}'.format(item))
|
||||
|
||||
|
||||
MINIMUM_FREE_HEAP_SIZE_RE = re.compile(r'Minimum free heap size: (\d+) bytes')
|
||||
|
||||
@@ -13,13 +13,13 @@ import re
|
||||
|
||||
import junit_xml
|
||||
|
||||
_NORMAL_TEST_REGEX = re.compile(r"(?P<file>.+):(?P<line>\d+):(?P<test_name>[^\s:]+):(?P<result>PASS|FAIL|IGNORE)(?:: (?P<message>.+))?")
|
||||
_UNITY_FIXTURE_VERBOSE_PREFIX_REGEX = re.compile(r"(?P<prefix>TEST\((?P<test_group>[^\s,]+), (?P<test_name>[^\s\)]+)\))(?P<remainder>.+)?$")
|
||||
_UNITY_FIXTURE_REMAINDER_REGEX = re.compile(r"^(?P<file>.+):(?P<line>\d+)::(?P<result>PASS|FAIL|IGNORE)(?:: (?P<message>.+))?")
|
||||
_NORMAL_TEST_REGEX = re.compile(r'(?P<file>.+):(?P<line>\d+):(?P<test_name>[^\s:]+):(?P<result>PASS|FAIL|IGNORE)(?:: (?P<message>.+))?')
|
||||
_UNITY_FIXTURE_VERBOSE_PREFIX_REGEX = re.compile(r'(?P<prefix>TEST\((?P<test_group>[^\s,]+), (?P<test_name>[^\s\)]+)\))(?P<remainder>.+)?$')
|
||||
_UNITY_FIXTURE_REMAINDER_REGEX = re.compile(r'^(?P<file>.+):(?P<line>\d+)::(?P<result>PASS|FAIL|IGNORE)(?:: (?P<message>.+))?')
|
||||
_TEST_SUMMARY_BLOCK_REGEX = re.compile(
|
||||
r"^(?P<num_tests>\d+) Tests (?P<num_failures>\d+) Failures (?P<num_ignored>\d+) Ignored\s*\r?\n(?P<overall_result>OK|FAIL)(?:ED)?", re.MULTILINE
|
||||
r'^(?P<num_tests>\d+) Tests (?P<num_failures>\d+) Failures (?P<num_ignored>\d+) Ignored\s*\r?\n(?P<overall_result>OK|FAIL)(?:ED)?', re.MULTILINE
|
||||
)
|
||||
_TEST_RESULT_ENUM = ["PASS", "FAIL", "IGNORE"]
|
||||
_TEST_RESULT_ENUM = ['PASS', 'FAIL', 'IGNORE']
|
||||
|
||||
|
||||
class TestFormat(enum.Enum):
|
||||
@@ -63,14 +63,14 @@ class TestResult:
|
||||
self,
|
||||
test_name,
|
||||
result,
|
||||
group="default",
|
||||
file="",
|
||||
group='default',
|
||||
file='',
|
||||
line=0,
|
||||
message="",
|
||||
full_line="",
|
||||
message='',
|
||||
full_line='',
|
||||
):
|
||||
if result not in _TEST_RESULT_ENUM:
|
||||
raise ValueError("result must be one of {}.".format(_TEST_RESULT_ENUM))
|
||||
raise ValueError('result must be one of {}.'.format(_TEST_RESULT_ENUM))
|
||||
|
||||
self._test_name = test_name
|
||||
self._result = result
|
||||
@@ -78,11 +78,11 @@ class TestResult:
|
||||
self._message = message
|
||||
self._full_line = full_line
|
||||
|
||||
if result != "PASS":
|
||||
if result != 'PASS':
|
||||
self._file = file
|
||||
self._line = line
|
||||
else:
|
||||
self._file = ""
|
||||
self._file = ''
|
||||
self._line = 0
|
||||
|
||||
def file(self):
|
||||
@@ -150,7 +150,7 @@ class TestResults:
|
||||
self._parse_unity_fixture_verbose(test_output)
|
||||
else:
|
||||
raise ValueError(
|
||||
"test_format must be one of UNITY_BASIC or UNITY_FIXTURE_VERBOSE."
|
||||
'test_format must be one of UNITY_BASIC or UNITY_FIXTURE_VERBOSE.'
|
||||
)
|
||||
|
||||
def num_tests(self):
|
||||
@@ -185,7 +185,7 @@ class TestResults:
|
||||
return self._tests
|
||||
|
||||
def to_junit(
|
||||
self, suite_name="all_tests",
|
||||
self, suite_name='all_tests',
|
||||
):
|
||||
"""
|
||||
Convert the tests to JUnit XML.
|
||||
@@ -207,7 +207,7 @@ class TestResults:
|
||||
test_case_list = []
|
||||
|
||||
for test in self._tests:
|
||||
if test.result() == "PASS":
|
||||
if test.result() == 'PASS':
|
||||
test_case_list.append(
|
||||
junit_xml.TestCase(name=test.name(), classname=test.group())
|
||||
)
|
||||
@@ -218,11 +218,11 @@ class TestResults:
|
||||
file=test.file(),
|
||||
line=test.line(),
|
||||
)
|
||||
if test.result() == "FAIL":
|
||||
if test.result() == 'FAIL':
|
||||
junit_tc.add_failure_info(
|
||||
message=test.message(), output=test.full_line()
|
||||
)
|
||||
elif test.result() == "IGNORE":
|
||||
elif test.result() == 'IGNORE':
|
||||
junit_tc.add_skipped_info(
|
||||
message=test.message(), output=test.full_line()
|
||||
)
|
||||
@@ -245,17 +245,17 @@ class TestResults:
|
||||
"""
|
||||
match = _TEST_SUMMARY_BLOCK_REGEX.search(unity_output)
|
||||
if not match:
|
||||
raise ValueError("A Unity test summary block was not found.")
|
||||
raise ValueError('A Unity test summary block was not found.')
|
||||
|
||||
try:
|
||||
stats = TestStats()
|
||||
stats.total = int(match.group("num_tests"))
|
||||
stats.failed = int(match.group("num_failures"))
|
||||
stats.ignored = int(match.group("num_ignored"))
|
||||
stats.total = int(match.group('num_tests'))
|
||||
stats.failed = int(match.group('num_failures'))
|
||||
stats.ignored = int(match.group('num_ignored'))
|
||||
stats.passed = stats.total - stats.failed - stats.ignored
|
||||
return stats
|
||||
except ValueError:
|
||||
raise ValueError("The Unity test summary block was not valid.")
|
||||
raise ValueError('The Unity test summary block was not valid.')
|
||||
|
||||
def _parse_unity_basic(self, unity_output):
|
||||
"""
|
||||
@@ -268,13 +268,13 @@ class TestResults:
|
||||
for test in _NORMAL_TEST_REGEX.finditer(unity_output):
|
||||
try:
|
||||
new_test = TestResult(
|
||||
test.group("test_name"),
|
||||
test.group("result"),
|
||||
file=test.group("file"),
|
||||
line=int(test.group("line")),
|
||||
message=test.group("message")
|
||||
if test.group("message") is not None
|
||||
else "",
|
||||
test.group('test_name'),
|
||||
test.group('result'),
|
||||
file=test.group('file'),
|
||||
line=int(test.group('line')),
|
||||
message=test.group('message')
|
||||
if test.group('message') is not None
|
||||
else '',
|
||||
full_line=test.group(0),
|
||||
)
|
||||
except ValueError:
|
||||
@@ -283,10 +283,10 @@ class TestResults:
|
||||
self._add_new_test(new_test, found_test_stats)
|
||||
|
||||
if len(self._tests) == 0:
|
||||
raise ValueError("No tests were found.")
|
||||
raise ValueError('No tests were found.')
|
||||
|
||||
if found_test_stats != self._test_stats:
|
||||
raise ValueError("Test output does not match summary block.")
|
||||
raise ValueError('Test output does not match summary block.')
|
||||
|
||||
def _parse_unity_fixture_verbose(self, unity_output):
|
||||
"""
|
||||
@@ -309,7 +309,7 @@ class TestResults:
|
||||
if prefix_match:
|
||||
# Handle the remaining portion of a test case line after the unity_fixture
|
||||
# prefix.
|
||||
remainder = prefix_match.group("remainder")
|
||||
remainder = prefix_match.group('remainder')
|
||||
if remainder:
|
||||
self._parse_unity_fixture_remainder(
|
||||
prefix_match, remainder, found_test_stats
|
||||
@@ -324,10 +324,10 @@ class TestResults:
|
||||
pass
|
||||
|
||||
if len(self._tests) == 0:
|
||||
raise ValueError("No tests were found.")
|
||||
raise ValueError('No tests were found.')
|
||||
|
||||
if found_test_stats != self._test_stats:
|
||||
raise ValueError("Test output does not match summary block.")
|
||||
raise ValueError('Test output does not match summary block.')
|
||||
|
||||
def _parse_unity_fixture_remainder(self, prefix_match, remainder, test_stats):
|
||||
"""
|
||||
@@ -337,26 +337,26 @@ class TestResults:
|
||||
"""
|
||||
new_test = None
|
||||
|
||||
if remainder == " PASS":
|
||||
if remainder == ' PASS':
|
||||
new_test = TestResult(
|
||||
prefix_match.group("test_name"),
|
||||
"PASS",
|
||||
group=prefix_match.group("test_group"),
|
||||
prefix_match.group('test_name'),
|
||||
'PASS',
|
||||
group=prefix_match.group('test_group'),
|
||||
full_line=prefix_match.group(0),
|
||||
)
|
||||
else:
|
||||
remainder_match = _UNITY_FIXTURE_REMAINDER_REGEX.match(remainder)
|
||||
if remainder_match:
|
||||
new_test = TestResult(
|
||||
prefix_match.group("test_name"),
|
||||
remainder_match.group("result"),
|
||||
group=prefix_match.group("test_group"),
|
||||
file=remainder_match.group("file"),
|
||||
line=int(remainder_match.group("line")),
|
||||
message=remainder_match.group("message")
|
||||
if remainder_match.group("message") is not None
|
||||
else "",
|
||||
full_line=prefix_match.group("prefix") + remainder_match.group(0),
|
||||
prefix_match.group('test_name'),
|
||||
remainder_match.group('result'),
|
||||
group=prefix_match.group('test_group'),
|
||||
file=remainder_match.group('file'),
|
||||
line=int(remainder_match.group('line')),
|
||||
message=remainder_match.group('message')
|
||||
if remainder_match.group('message') is not None
|
||||
else '',
|
||||
full_line=prefix_match.group('prefix') + remainder_match.group(0),
|
||||
)
|
||||
|
||||
if new_test is not None:
|
||||
@@ -365,9 +365,9 @@ class TestResults:
|
||||
def _add_new_test(self, new_test, test_stats):
|
||||
"""Add a new test and increment the proper members of test_stats."""
|
||||
test_stats.total += 1
|
||||
if new_test.result() == "PASS":
|
||||
if new_test.result() == 'PASS':
|
||||
test_stats.passed += 1
|
||||
elif new_test.result() == "FAIL":
|
||||
elif new_test.result() == 'FAIL':
|
||||
test_stats.failed += 1
|
||||
else:
|
||||
test_stats.ignored += 1
|
||||
|
||||
@@ -13,10 +13,11 @@
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
import time
|
||||
|
||||
import dbus
|
||||
import dbus.mainloop.glib
|
||||
import netifaces
|
||||
import time
|
||||
|
||||
|
||||
def get_wiface_name():
|
||||
@@ -46,17 +47,17 @@ class wpa_cli:
|
||||
dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
|
||||
bus = dbus.SystemBus()
|
||||
|
||||
service = dbus.Interface(bus.get_object("fi.w1.wpa_supplicant1", "/fi/w1/wpa_supplicant1"),
|
||||
"fi.w1.wpa_supplicant1")
|
||||
service = dbus.Interface(bus.get_object('fi.w1.wpa_supplicant1', '/fi/w1/wpa_supplicant1'),
|
||||
'fi.w1.wpa_supplicant1')
|
||||
iface_path = service.GetInterface(self.iface_name)
|
||||
self.iface_obj = bus.get_object("fi.w1.wpa_supplicant1", iface_path)
|
||||
self.iface_ifc = dbus.Interface(self.iface_obj, "fi.w1.wpa_supplicant1.Interface")
|
||||
self.iface_obj = bus.get_object('fi.w1.wpa_supplicant1', iface_path)
|
||||
self.iface_ifc = dbus.Interface(self.iface_obj, 'fi.w1.wpa_supplicant1.Interface')
|
||||
self.iface_props = dbus.Interface(self.iface_obj, 'org.freedesktop.DBus.Properties')
|
||||
if self.iface_ifc is None:
|
||||
raise RuntimeError('supplicant : Failed to fetch interface')
|
||||
|
||||
self.old_network = self._get_iface_property("CurrentNetwork")
|
||||
print("Old network is %s" % self.old_network)
|
||||
self.old_network = self._get_iface_property('CurrentNetwork')
|
||||
print('Old network is %s' % self.old_network)
|
||||
|
||||
if self.old_network == '/':
|
||||
self.old_network = None
|
||||
@@ -69,7 +70,7 @@ class wpa_cli:
|
||||
Note: The result is a dbus wrapped type, so should usually convert it to the corresponding native
|
||||
Python type
|
||||
"""
|
||||
return self.iface_props.Get("fi.w1.wpa_supplicant1.Interface", name)
|
||||
return self.iface_props.Get('fi.w1.wpa_supplicant1.Interface', name)
|
||||
|
||||
def connect(self, ssid, password):
|
||||
if self.connected is True:
|
||||
@@ -79,9 +80,9 @@ class wpa_cli:
|
||||
if self.new_network is not None:
|
||||
self.iface_ifc.RemoveNetwork(self.new_network)
|
||||
|
||||
print("Pre-connect state is %s, IP is %s" % (self._get_iface_property("State"), get_wiface_IPv4(self.iface_name)))
|
||||
print('Pre-connect state is %s, IP is %s' % (self._get_iface_property('State'), get_wiface_IPv4(self.iface_name)))
|
||||
|
||||
self.new_network = self.iface_ifc.AddNetwork({"ssid": ssid, "psk": password})
|
||||
self.new_network = self.iface_ifc.AddNetwork({'ssid': ssid, 'psk': password})
|
||||
self.iface_ifc.SelectNetwork(self.new_network)
|
||||
time.sleep(10)
|
||||
|
||||
@@ -89,12 +90,12 @@ class wpa_cli:
|
||||
retry = 10
|
||||
while retry > 0:
|
||||
time.sleep(5)
|
||||
state = str(self._get_iface_property("State"))
|
||||
print("wpa iface state %s (scanning %s)" % (state, bool(self._get_iface_property("Scanning"))))
|
||||
if state in ["disconnected", "inactive"]:
|
||||
state = str(self._get_iface_property('State'))
|
||||
print('wpa iface state %s (scanning %s)' % (state, bool(self._get_iface_property('Scanning'))))
|
||||
if state in ['disconnected', 'inactive']:
|
||||
self.iface_ifc.Reconnect()
|
||||
ip = get_wiface_IPv4(self.iface_name)
|
||||
print("wpa iface %s IP %s" % (self.iface_name, ip))
|
||||
print('wpa iface %s IP %s' % (self.iface_name, ip))
|
||||
if ip is not None:
|
||||
self.connected = True
|
||||
return ip
|
||||
|
||||
Reference in New Issue
Block a user