Skip to content

Commit 8ba1dc0

Browse files
author
Roland Takacs
committed
Testrunner refactor to support multiple targets
IoT.js-DCO-1.0-Signed-off-by: Roland Takacs [email protected]
1 parent fbb371f commit 8ba1dc0

File tree

14 files changed

+786
-313
lines changed

14 files changed

+786
-313
lines changed
File renamed without changes.

tools/common_py/path.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,9 @@
5252
# Root directory for http-parser submodule.
5353
HTTPPARSER_ROOT = fs.join(DEPS_ROOT, 'http-parser')
5454

55+
# Root directory for stlink.
56+
STLINK_ROOT = fs.join(DEPS_ROOT, 'stlink')
57+
5558
# checktest
5659
CHECKTEST_PATH = fs.join(TOOLS_ROOT, 'check_test.js')
5760

tools/common_py/system/executor.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,10 @@
1717
import subprocess
1818

1919

20+
class TimeoutException(Exception):
21+
pass
22+
23+
2024
class Executor(object):
2125
_TERM_RED = "\033[1;31m"
2226
_TERM_YELLOW = "\033[1;33m"
@@ -44,9 +48,15 @@ def fail(msg):
4448
@staticmethod
4549
def run_cmd(cmd, args=[], quiet=False):
4650
if not quiet:
51+
stdout = None
52+
stderr = None
4753
Executor.print_cmd_line(cmd, args)
54+
else:
55+
stdout = subprocess.PIPE
56+
stderr = subprocess.STDOUT
57+
4858
try:
49-
return subprocess.call([cmd] + args)
59+
return subprocess.call([cmd] + args, stdout=stdout, stderr=stderr)
5060
except OSError as e:
5161
Executor.fail("[Failed - %s] %s" % (cmd, e.strerror))
5262

tools/test_runner.py

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
#!/usr/bin/env python
2+
3+
# Copyright 2016-present Samsung Electronics Co., Ltd. and other contributors
4+
#
5+
# Licensed under the Apache License, Version 2.0 (the "License");
6+
# you may not use this file except in compliance with the License.
7+
# You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing, software
12+
# distributed under the License is distributed on an "AS IS" BASIS
13+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
# See the License for the specific language governing permissions and
15+
# limitations under the License.
16+
17+
import argparse
18+
19+
from testdriver.testrunner import TestRunner
20+
from testdriver.target import DEVICES
21+
22+
23+
def parse_options():
24+
"""
25+
Parse the given options.
26+
"""
27+
parser = argparse.ArgumentParser()
28+
29+
parser.add_argument("--build", default=False, action="store_true",
30+
help="create build for the selected target")
31+
parser.add_argument("--quiet", default=False, action="store_true",
32+
help="hide the output of the test execution")
33+
parser.add_argument("--skip-modules", metavar="LIST", default="",
34+
help="""specify the built-in modules that sholuld be
35+
skipped (e.g. fs,net,process)""")
36+
parser.add_argument("--target", default="host", choices=DEVICES.keys(),
37+
help="""define the target where the testing happens
38+
(default: %(default)s)""")
39+
parser.add_argument("--timeout", metavar="SEC", default=300, type=int,
40+
help="default timeout in sec (default: %(default)s)")
41+
42+
group = parser.add_argument_group("Local testing")
43+
44+
group.add_argument("--bin-path", metavar="PATH",
45+
help="path to the iotjs binary file")
46+
group.add_argument("--coverage", default=False, action="store_true",
47+
help="""measure JavaScript coverage
48+
(only for the meausre_coverage.sh script)""")
49+
group.add_argument("--valgrind", action="store_true", default=False,
50+
help="check memory management by Valgrind")
51+
52+
group = parser.add_argument_group("Remote testing (SSH communication)")
53+
54+
group.add_argument("--address", metavar="IPADDR",
55+
help="IP address of the device")
56+
group.add_argument("--remote-path", metavar="PATH",
57+
help="""define the folder (absolute path) on the device
58+
where iotjs and tests are located""")
59+
group.add_argument("--username", metavar="USER",
60+
help="username to login")
61+
62+
group = parser.add_argument_group("Remote testing (Serial communication)")
63+
64+
group.add_argument("--baud", default=115200, type=int,
65+
help="baud rate (default: %(default)s)")
66+
group.add_argument("--port",
67+
help="serial port name (e.g. /dev/ttyACM0)")
68+
69+
return parser.parse_args()
70+
71+
72+
def main():
73+
options = parse_options()
74+
75+
testrunner = TestRunner(options)
76+
testrunner.run()
77+
78+
79+
if __name__ == '__main__':
80+
main()

tools/testdriver/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
# Required for Python to search this directory for module files

tools/testdriver/reporter.py

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
#!/usr/bin/env python
2+
3+
# Copyright 2017-present Samsung Electronics Co., Ltd. and other contributors
4+
#
5+
# Licensed under the Apache License, Version 2.0 (the "License");
6+
# you may not use this file except in compliance with the License.
7+
# You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing, software
12+
# distributed under the License is distributed on an "AS IS" BASIS
13+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
# See the License for the specific language governing permissions and
15+
# limitations under the License.
16+
17+
from __future__ import print_function
18+
19+
from common_py.system.executor import Executor as ex
20+
21+
22+
class Reporter(object):
23+
@staticmethod
24+
def message(msg="", color=ex._TERM_EMPTY):
25+
print("%s%s%s" % (color, msg, ex._TERM_EMPTY))
26+
27+
@staticmethod
28+
def report_testset(testset):
29+
Reporter.message()
30+
Reporter.message("Testset: %s" % testset, ex._TERM_BLUE)
31+
32+
@staticmethod
33+
def report_pass(test, time):
34+
Reporter.message(" PASS: %s (%ss)" % (test, time), ex._TERM_GREEN)
35+
36+
@staticmethod
37+
def report_fail(test, time):
38+
Reporter.message(" FAIL: %s (%ss)" % (test, time), ex._TERM_RED)
39+
40+
@staticmethod
41+
def report_timeout(test):
42+
Reporter.message(" TIMEOUT: %s" % test, ex._TERM_RED)
43+
44+
@staticmethod
45+
def report_skip(test, reason):
46+
skip_message = " SKIP: %s" % test
47+
48+
if reason:
49+
skip_message += " (Reason: %s)" % reason
50+
51+
Reporter.message(skip_message, ex._TERM_YELLOW)
52+
53+
@staticmethod
54+
def report_configuration(options):
55+
Reporter.message()
56+
Reporter.message("Test configuration:")
57+
Reporter.message(" target: %s" % options.target)
58+
59+
if options.target == "host":
60+
Reporter.message(" bin path: %s" % options.bin_path)
61+
Reporter.message(" coverage: %s" % options.coverage)
62+
Reporter.message(" valgrind: %s" % options.valgrind)
63+
elif options.target == "rpi2":
64+
Reporter.message(" address: %s" % options.address)
65+
Reporter.message(" username: %s" % options.username)
66+
Reporter.message(" remote path: %s" % options.remote_path)
67+
elif options.target in ["stm32f4dis", "artik053"]:
68+
Reporter.message(" port: %s" % options.port)
69+
Reporter.message(" baud: %d" % options.baud)
70+
71+
Reporter.message(" quiet: %s" % options.quiet)
72+
Reporter.message(" timeout: %s" % options.timeout)
73+
Reporter.message(" skip-modules: %s" % repr(options.skip_modules))
74+
75+
@staticmethod
76+
def report_final(results):
77+
Reporter.message()
78+
Reporter.message("Finished with all tests:", ex._TERM_BLUE)
79+
Reporter.message(" PASS: %d" % results["pass"], ex._TERM_GREEN)
80+
Reporter.message(" FAIL: %d" % results["fail"], ex._TERM_RED)
81+
Reporter.message(" TIMEOUT: %d" % results["timeout"], ex._TERM_RED)
82+
Reporter.message(" SKIP: %d" % results["skip"], ex._TERM_YELLOW)

tools/testdriver/target/__init__.py

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
# Copyright 2017-present Samsung Electronics Co., Ltd. and other contributors
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
import host
16+
import stm32f4dis
17+
18+
from common_py import path
19+
from common_py.system.filesystem import FileSystem as fs
20+
from common_py.system.executor import Executor as ex
21+
from common_py.system.platform import Platform
22+
23+
24+
DEVICES = {
25+
"host": host.Device,
26+
"stm32f4dis": stm32f4dis.Device
27+
}
28+
29+
30+
def create_build(options):
31+
if options.target == "host":
32+
ex.run_cmd(fs.join(path.TOOLS_ROOT, "build.py"), ["--no-check-test"])
33+
34+
# Append the build path to the options.
35+
target = "%s-%s" % (Platform().arch(), Platform().os())
36+
options.bin_path = fs.join(path.BUILD_ROOT, target, "debug/bin/iotjs")
37+
38+
elif options.target == "stm32f4dis":
39+
build_options = [
40+
"--test=nuttx",
41+
"--buildtype=release",
42+
"--buildoptions=--jerry-memstat",
43+
"--enable-testsuite",
44+
"--flash"
45+
]
46+
47+
ex.run_cmd(fs.join(path.TOOLS_ROOT, "precommit.py"), build_options)
48+
49+
else:
50+
ex.fail("Unimplemented case for building iotjs to the target.")
51+
52+
53+
def create_device(options):
54+
if options.build:
55+
create_build(options)
56+
57+
device_class = DEVICES[options.target]
58+
59+
return device_class(options)
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
# Required for Python to search this directory for module files
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
# Copyright 2017-present Samsung Electronics Co., Ltd. and other contributors
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
import serial
16+
import time
17+
import xmodem
18+
19+
from common_py.system.executor import TimeoutException
20+
21+
22+
class Connection(object):
23+
"""
24+
The serial communication wrapper.
25+
"""
26+
def __init__(self, port, baud, timeout, prompt):
27+
self.port = port
28+
self.baud = baud
29+
self.timeout = timeout
30+
31+
# Defines the end of the stdout.
32+
self.prompt = prompt
33+
34+
def get_prompt(self):
35+
"""
36+
Get the prompt.
37+
"""
38+
return self.prompt
39+
40+
def open(self):
41+
"""
42+
Open the serial port.
43+
"""
44+
self.serial = serial.Serial(self.port, self.baud, timeout=self.timeout)
45+
self.xmodem = xmodem.XMODEM1k(self.getc, self.putc)
46+
47+
def close(self):
48+
"""
49+
Close the serial port.
50+
"""
51+
self.serial.close()
52+
53+
def getc(self, size, timeout=1):
54+
"""
55+
Recevice data from the serial port.
56+
"""
57+
time.sleep(2)
58+
59+
return self.serial.read(size) or None
60+
61+
def putc(self, data, timeout=1):
62+
"""
63+
Send data to the serial port.
64+
"""
65+
return self.serial.write(data)
66+
67+
def readline(self):
68+
"""
69+
Read line from the serial port.
70+
"""
71+
return self.serial.readline()
72+
73+
def exec_command(self, cmd):
74+
"""
75+
Send command over the serial port.
76+
"""
77+
self.serial.write(cmd + "\n")
78+
79+
receive = self.serial.read_until(self.prompt)
80+
81+
# Throw exception when timeout happens.
82+
if self.prompt not in receive:
83+
raise TimeoutException
84+
85+
# Note: since the received data format is
86+
#
87+
# [command][CRLF][stdout][CRFL][prompt],
88+
#
89+
# we should process the output for the stdout.
90+
return "\n".join(receive.split("\r\n")[1:-1])
91+
92+
def read_until(self, *args):
93+
"""
94+
Read data until it contains args.
95+
"""
96+
line = bytearray()
97+
while True:
98+
c = self.serial.read(1)
99+
if c:
100+
line += c
101+
for stdout in args:
102+
if line[-len(stdout):] == stdout:
103+
return stdout, bytes(line)
104+
else:
105+
# raise utils.TimeoutException
106+
raise Exception("use TimeoutException")
107+
108+
return False, False
109+
110+
def send_file(self, lpath, rpath):
111+
"""
112+
Send file over the serial port.
113+
Note: `lrzsz` package should be installed on the device.
114+
"""
115+
self.serial.write("rm " + rpath + "\n")
116+
self.serial.write("rx " + rpath + "\n")
117+
118+
# Receive all the data from the device except the last
119+
# \x15 (NAK) byte that is needed by the xmodem protocol.
120+
self.serial.read(self.serial.in_waiting - 1)
121+
122+
with open(lpath, "rb") as file:
123+
self.xmodem.send(file)

0 commit comments

Comments
 (0)