diff --git a/tests/device/Makefile b/tests/device/Makefile
index b34cb0dbbc..5062e4b2a2 100644
--- a/tests/device/Makefile
+++ b/tests/device/Makefile
@@ -11,9 +11,9 @@ UPLOAD_PORT ?= $(shell ls /dev/tty* | grep -m 1 -i USB)
UPLOAD_BAUD ?= 460800
UPLOAD_BOARD ?= nodemcu
BS_DIR ?= libraries/BSTest
-DEBUG_LEVEL ?= DebugLevel=None____
+DEBUG_LEVEL ?= lvl=None____
#FQBN ?= esp8266com:esp8266:generic:CpuFrequency=80,FlashFreq=40,FlashMode=dio,UploadSpeed=115200,FlashSize=4M1M,LwIPVariant=v2mss536,ResetMethod=none,Debug=Serial,$(DEBUG_LEVEL)
-FQBN ?= esp8266com:esp8266:generic:xtal=80,FlashFreq=40,FlashMode=dio,baud=115200,eesz=4M1M,ip=lm2f,ResetMethod=none,dbg=Serial,$(DEBUG_LEVEL)
+FQBN ?= esp8266com:esp8266:generic:xtal=160,FlashFreq=40,FlashMode=dio,baud=115200,eesz=4M1M,ip=lm2f,ResetMethod=none,dbg=Serial,$(DEBUG_LEVEL)
BUILD_TOOL := $(ARDUINO_IDE_PATH)/arduino-builder
TEST_CONFIG := test_env.cfg
TEST_REPORT_XML := test_report.xml
@@ -104,7 +104,7 @@ ifneq ("$(NO_RUN)","1")
endif
$(TEST_REPORT_XML): $(HARDWARE_DIR) virtualenv
- $(SILENT)$(BS_DIR)/virtualenv/bin/xunitmerge $(shell find $(BUILD_DIR) -name 'test_result.xml' | xargs echo) $(TEST_REPORT_XML)
+ $(SILENT)$(BS_DIR)/xunitmerge $(shell find $(BUILD_DIR) -name 'test_result.xml' | xargs echo) $(TEST_REPORT_XML)
$(TEST_REPORT_HTML): $(TEST_REPORT_XML) | virtualenv
$(SILENT)$(BS_DIR)/virtualenv/bin/junit2html $< $@
@@ -124,7 +124,8 @@ virtualenv:
clean:
rm -rf $(BUILD_DIR)
- rm -rf $(HARDWARE_DIR)
+ rm -rf $(HARDWARE_DIR)A
+ rm -rf $(BS_DIR)/virtualenv
rm -f $(TEST_REPORT_HTML) $(TEST_REPORT_XML)
distclean: clean
diff --git a/tests/device/libraries/BSTest/requirements.txt b/tests/device/libraries/BSTest/requirements.txt
index d31484d2af..a65d9b1705 100644
--- a/tests/device/libraries/BSTest/requirements.txt
+++ b/tests/device/libraries/BSTest/requirements.txt
@@ -3,6 +3,5 @@ junit-xml
MarkupSafe
pexpect
pyserial
-xunitmerge
junit2html
-poster
+poster3
diff --git a/tests/device/libraries/BSTest/runner.py b/tests/device/libraries/BSTest/runner.py
index 425ac2a422..97849a5e32 100644
--- a/tests/device/libraries/BSTest/runner.py
+++ b/tests/device/libraries/BSTest/runner.py
@@ -236,10 +236,10 @@ def request_env(self, key):
def spawn_port(port_name, baudrate=115200):
global ser
ser = serial.serial_for_url(port_name, baudrate=baudrate)
- return fdpexpect.fdspawn(ser, 'wb', timeout=0)
+ return fdpexpect.fdspawn(ser, 'wb', timeout=0, encoding='cp437')
def spawn_exec(name):
- return pexpect.spawn(name, timeout=0)
+ return pexpect.spawn(name, timeout=0, encoding='cp437')
def run_tests(spawn, name, mocks, env_vars):
tw = BSTestRunner(spawn, name, mocks, env_vars)
diff --git a/tests/device/libraries/BSTest/xmerge.py b/tests/device/libraries/BSTest/xmerge.py
new file mode 100644
index 0000000000..c10ca7297e
--- /dev/null
+++ b/tests/device/libraries/BSTest/xmerge.py
@@ -0,0 +1,154 @@
+# Cloned from https://github.com/miki725/xunitmerge
+# to fix a Python3 error.
+#
+# xunitmerge is MIT licensed by Miroslav Shubernetskiy https://github.com/miki725
+#
+# The MIT License (MIT)
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+
+from contextlib import contextmanager
+from xml.etree import ElementTree as etree
+from xml.sax.saxutils import quoteattr
+
+import six
+
+
+CNAME_TAGS = ('system-out', 'skipped', 'error', 'failure')
+CNAME_PATTERN = ''
+TAG_PATTERN = '<{tag}{attrs}>{text}{tag}>'
+
+
+@contextmanager
+def patch_etree_cname(etree):
+ """
+ Patch ElementTree's _serialize_xml function so that it will
+ write text as CDATA tag for tags tags defined in CNAME_TAGS.
+
+ >>> import re
+ >>> from xml.etree import ElementTree
+ >>> xml_string = '''
+ ...
+ ...
+ ... Some output here
+ ...
+ ...
+ ... Skipped
+ ...
+ ...
+ ... Error here
+ ...
+ ...
+ ... Failure here
+ ...
+ ...
+ ... '''
+ >>> tree = ElementTree.fromstring(xml_string)
+ >>> with patch_etree_cname(ElementTree):
+ ... saved = str(ElementTree.tostring(tree))
+ >>> systemout = re.findall(r'(.*?)', saved)[0]
+ >>> print(systemout)
+
+ >>> skipped = re.findall(r'()', saved)[0]
+ >>> print(skipped)
+
+ >>> error = re.findall(r'()', saved)[0]
+ >>> print(error)
+
+ >>> failure = re.findall(r'()', saved)[0]
+ >>> print(failure)
+
+ """
+ original_serialize = etree._serialize_xml
+
+ def _serialize_xml(write, elem, *args, **kwargs):
+ if elem.tag in CNAME_TAGS:
+ attrs = ' '.join(
+ ['{}={}'.format(k, quoteattr(v))
+ for k, v in sorted(elem.attrib.items())]
+ )
+ attrs = ' ' + attrs if attrs else ''
+ text = CNAME_PATTERN.format(elem.text)
+ write(TAG_PATTERN.format(
+ tag=elem.tag,
+ attrs=attrs,
+ text=text
+ ))
+ else:
+ original_serialize(write, elem, *args, **kwargs)
+
+ etree._serialize_xml = etree._serialize['xml'] = _serialize_xml
+
+ yield
+
+ etree._serialize_xml = etree._serialize['xml'] = original_serialize
+
+
+def merge_trees(*trees):
+ """
+ Merge all given XUnit ElementTrees into a single ElementTree.
+ This combines all of the children test-cases and also merges
+ all of the metadata of how many tests were executed, etc.
+ """
+ first_tree = trees[0]
+ first_root = first_tree.getroot()
+
+ if len(trees) == 0:
+ return first_tree
+
+ for tree in trees[1:]:
+ root = tree.getroot()
+
+ # append children elements (testcases)
+ first_root.extend(root.getchildren())
+
+ # combine root attributes which stores the number
+ # of executed tests, skipped tests, etc
+ for key, value in first_root.attrib.items():
+ if not value.isdigit():
+ continue
+ combined = six.text_type(int(value) + int(root.attrib.get(key, '0')))
+ first_root.set(key, combined)
+
+ return first_tree
+
+
+def merge_xunit(files, output, callback=None):
+ """
+ Merge the given xunit xml files into a single output xml file.
+
+ If callback is not None, it will be called with the merged ElementTree
+ before the output file is written (useful for applying other fixes to
+ the merged file). This can either modify the element tree in place (and
+ return None) or return a completely new ElementTree to be written.
+ """
+ trees = []
+
+ for f in files:
+ trees.append(etree.parse(f))
+
+ merged = merge_trees(*trees)
+
+ if callback is not None:
+ result = callback(merged)
+ if result is not None:
+ merged = result
+
+ with patch_etree_cname(etree):
+ merged.write(output, encoding='utf-8', xml_declaration=True)
diff --git a/tests/device/libraries/BSTest/xunitmerge b/tests/device/libraries/BSTest/xunitmerge
new file mode 100755
index 0000000000..61a69f6ec0
--- /dev/null
+++ b/tests/device/libraries/BSTest/xunitmerge
@@ -0,0 +1,50 @@
+#!/usr/bin/env python3
+
+# Cloned from https://github.com/miki725/xunitmerge
+# to fix a Python3 error.
+#
+# xunitmerge is MIT licensed by Miroslav Shubernetskiy https://github.com/miki725
+#
+# The MIT License (MIT)
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+
+import argparse
+from xmerge import merge_xunit
+
+
+parser = argparse.ArgumentParser(
+ description='Utility for merging multiple XUnit xml reports '
+ 'into a single xml report.',
+)
+parser.add_argument(
+ 'report',
+ nargs='+',
+ type=argparse.FileType('r'),
+ help='Path of XUnit xml report. Multiple can be provided.',
+)
+parser.add_argument(
+ 'output',
+ help='Path where merged of XUnit will be saved.',
+)
+
+
+if __name__ == '__main__':
+ args = parser.parse_args()
+ merge_xunit(args.report, args.output)
diff --git a/tests/device/test_ClientContext/test_ClientContext.py b/tests/device/test_ClientContext/test_ClientContext.py
index ae29bcd2fe..6650e1cfdb 100644
--- a/tests/device/test_ClientContext/test_ClientContext.py
+++ b/tests/device/test_ClientContext/test_ClientContext.py
@@ -1,3 +1,5 @@
+#!/usr/bin/env python3
+
from mock_decorators import setup, teardown
from flask import Flask, request
from threading import Thread
@@ -21,7 +23,7 @@ def run():
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
for port in range(8266, 8285 + 1):
try:
- print >>sys.stderr, 'trying port', port
+ print ('trying port %d' %port, file=sys.stderr)
server_address = ("0.0.0.0", port)
sock.bind(server_address)
sock.listen(1)
@@ -31,17 +33,17 @@ def run():
print >>sys.stderr, 'busy'
if not running:
return
- print >>sys.stderr, 'starting up on %s port %s' % server_address
- print >>sys.stderr, 'waiting for connections'
+ print ('starting up on %s port %s' % server_address, file=sys.stderr)
+ print ( 'waiting for connections', file=sys.stderr)
while running:
- print >>sys.stderr, 'loop'
+ print ('loop', file=sys.stderr)
readable, writable, errored = select.select([sock], [], [], 1.0)
if readable:
connection, client_address = sock.accept()
try:
- print >>sys.stderr, 'client connected:', client_address
+ print('client connected: %s' % str(client_address), file=sys.stderr)
finally:
- print >>sys.stderr, 'close'
+ print ('close', file=sys.stderr)
connection.shutdown(socket.SHUT_RDWR)
connection.close()
@@ -54,7 +56,7 @@ def teardown_tcpsrv(e):
global thread
global running
- print >>sys.stderr, 'closing'
+ print ('closing', file=sys.stderr)
running = False
thread.join()
return 0
diff --git a/tests/device/test_http_client/test_http_client.py b/tests/device/test_http_client/test_http_client.py
index d991ca985a..83bc4e8c17 100644
--- a/tests/device/test_http_client/test_http_client.py
+++ b/tests/device/test_http_client/test_http_client.py
@@ -1,7 +1,7 @@
from mock_decorators import setup, teardown
from flask import Flask, request, redirect
from threading import Thread
-import urllib2
+import urllib
import os
import ssl
import time
@@ -20,7 +20,7 @@ def shutdown():
return 'Server shutting down...'
@app.route("/", methods = ['GET', 'POST'])
def root():
- print('Got data: ' + request.data);
+ print('Got data: ' + request.data.decode());
return 'hello!!!'
@app.route("/data")
def get_data():
@@ -48,7 +48,7 @@ def flaskThread():
@teardown('HTTP GET & POST requests')
def teardown_http_get(e):
- response = urllib2.urlopen('http://localhost:8088/shutdown')
+ response = urllib.request.urlopen('http://localhost:8088/shutdown')
html = response.read()
time.sleep(1) # avoid address in use error on macOS
@@ -86,6 +86,6 @@ def teardown_http_get(e):
ctx.check_hostname = False
ctx.verify_mode = ssl.CERT_NONE
p = os.path.dirname(os.path.abspath(__file__))
- response = urllib2.urlopen('https://localhost:8088/shutdown', context=ctx)
+ response = urllib.request.urlopen('https://localhost:8088/shutdown', context=ctx)
html = response.read()
diff --git a/tests/device/test_http_server/test_http_server.py b/tests/device/test_http_server/test_http_server.py
index 319a8a7101..e184e367e6 100644
--- a/tests/device/test_http_server/test_http_server.py
+++ b/tests/device/test_http_server/test_http_server.py
@@ -1,10 +1,9 @@
from collections import OrderedDict
from mock_decorators import setup, teardown
from threading import Thread
-from poster.encode import MultipartParam
-from poster.encode import multipart_encode
-from poster.streaminghttp import register_openers
-import urllib2
+from poster3.encode import MultipartParam
+from poster3.encode import multipart_encode
+from poster3.streaminghttp import register_openers
import urllib
def http_test(res, url, get=None, post=None):
@@ -13,8 +12,8 @@ def http_test(res, url, get=None, post=None):
if get:
url += '?' + urllib.urlencode(get)
if post:
- post = urllib.urlencode(post)
- request = urllib2.urlopen(url, post, 2)
+ post = urllib.parse.quote(post)
+ request = urllib.request.urlopen(url, post, 2)
response = request.read()
except:
return 1
@@ -60,8 +59,8 @@ def testRun():
register_openers()
p = MultipartParam("file", "0123456789abcdef", "test.txt", "text/plain; charset=utf8")
datagen, headers = multipart_encode( [("var4", "val with spaces"), p] )
- request = urllib2.Request('http://etd.local/upload', datagen, headers)
- response = urllib2.urlopen(request, None, 2).read()
+ request = urllib.request('http://etd.local/upload', datagen, headers)
+ response = urllib.request.urlopen(request, None, 2).read()
except:
return 1
if response != 'test.txt:16\nvar4 = val with spaces':