Skip to content

Commit d959431

Browse files
Peter Markirobertsipka
authored andcommitted
Use interactive shell for all ssh devices (emulated and non-emulated) (#220)
JSRemoteTest-DCO-1.0-Signed-off-by: Peter Marki [email protected]
1 parent 46cdb3f commit d959431

File tree

8 files changed

+61
-38
lines changed

8 files changed

+61
-38
lines changed

jstest/__main__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -157,8 +157,8 @@ def adjust_options(options):
157157
options.ip = '127.0.0.1'
158158
options.port = 2022
159159
options.remote_workdir = 'emulated_workdir'
160-
options.sshclient_no_exec_command = True
161-
twisted_server.run()
160+
161+
twisted_server.run(options.device)
162162
atexit.register(twisted_server.stop)
163163

164164
else:

jstest/emulate/twisted_server.py

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,15 @@
3131
class SimpleSession(SSHChannel):
3232
name = 'session'
3333

34-
# pylint: disable=invalid-name
34+
@staticmethod
35+
def set_prompt(prompt):
36+
SimpleSession.prompt = prompt
37+
38+
#pylint: disable=invalid-name,unused-argument
39+
def channelOpen(self, specificData):
40+
self.write(SimpleSession.prompt)
41+
#pylint: enable=unused-argument
42+
3543
def dataReceived(self, data):
3644
cmd = str(data)
3745

@@ -53,6 +61,7 @@ def dataReceived(self, data):
5361
}
5462

5563
self.write(json.dumps(result) + '\r\n')
64+
self.write(SimpleSession.prompt)
5665

5766
# pylint: enable=invalid-name
5867
# pylint: disable=unused-argument
@@ -70,18 +79,22 @@ def request_pty_req(data):
7079
# pylint: enable=unused-argument
7180

7281
class SimpleRealm(object):
73-
@staticmethod
7482
# pylint: disable=invalid-name,unused-argument
83+
@staticmethod
7584
def requestAvatar(avatar_id, mind, *interfaces):
7685
user = ConchUser()
7786
user.channelLookup['session'] = SimpleSession
7887
return IConchUser, user, lambda: None
7988
# pylint: enable=invalid-name,unused-argument
8089

81-
def run():
90+
def run(device):
8291
'''
8392
Run a threaded server.
8493
'''
94+
prompts = {
95+
'rpi2': ':~$',
96+
'artik530': ':~>'
97+
}
8598
this_dir = os.path.dirname(__file__)
8699
with open(os.path.join(this_dir, 'private.key')) as private_blob_file:
87100
private_blob = private_blob_file.read()
@@ -95,6 +108,7 @@ def run():
95108
factory.privateKeys = {'ssh-rsa': private_key}
96109
factory.publicKeys = {'ssh-rsa': public_key}
97110

111+
SimpleSession.set_prompt(prompts[device])
98112
factory.portal = Portal(SimpleRealm())
99113
factory.portal.registerChecker(FilePasswordDB(os.path.join(this_dir, 'ssh-passwords')))
100114

jstest/resources/etc/tester.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,9 @@ def run_iotjs(options):
176176
stack_peak = 'n/a'
177177
malloc_peak = 'n/a'
178178

179+
if options.iotjs_build_info:
180+
return json.loads(output.split('\n')[0])
181+
179182
if output.find('Heap stats:') != -1:
180183
# Process jerry-memstat output.
181184
match = re.search(r'Peak allocated = (\d+) bytes', str(output))
@@ -250,6 +253,10 @@ def parse_arguments():
250253
action='store_true', default=False,
251254
help='do not measure memory statistics (default: %(default)s)')
252255

256+
parser.add_argument('--iotjs-build-info',
257+
action='store_true', default=False,
258+
help='Run the buildinfo script for iotjs')
259+
253260
return parser.parse_args()
254261

255262

jstest/testrunner/devices/artik530.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,4 +19,5 @@ class ARTIK530Device(SSHDevice):
1919
Device of the ARTIK530 target.
2020
'''
2121
def __init__(self, env):
22-
SSHDevice.__init__(self, env, 'tizen')
22+
# Note: the PS1 prompt on the device has to have this ending.
23+
SSHDevice.__init__(self, env, 'tizen', ':~>')

jstest/testrunner/devices/connections/sshcom.py

Lines changed: 18 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,7 @@ def __init__(self, device_info):
2828
self.ip = device_info['ip']
2929
self.port = device_info['port']
3030
self.timeout = device_info['timeout']
31-
# FIXME: the exec_command method of paramiko.client.SSHClient cannot be used with the
32-
# emulated ssh server.
33-
self._no_exec_command = device_info['_no_exec_command']
31+
self.prompt = device_info['prompt']
3432

3533
# Note: add your SSH key to the known host file
3634
# to avoid getting password.
@@ -46,9 +44,8 @@ def open(self):
4644
self.ssh.connect(hostname=self.ip, port=self.port, username=self.username,
4745
password=self.password, look_for_keys=not bool(self.password))
4846

49-
if self._no_exec_command:
50-
self.chan = self.ssh.invoke_shell()
51-
self.chan_file = self.chan.makefile('r')
47+
self.chan = self.ssh.invoke_shell()
48+
self.read_until(self.prompt)
5249

5350
def close(self):
5451
'''
@@ -60,32 +57,32 @@ def exec_command(self, cmd):
6057
'''
6158
Send command over the serial port.
6259
'''
60+
self.chan.settimeout(self.timeout)
6361
try:
64-
if self._no_exec_command:
65-
self.send(cmd)
66-
response = self.receive()
67-
68-
else:
69-
_, stdout, _ = self.ssh.exec_command(cmd, timeout=self.timeout)
70-
71-
response = stdout.readline()
72-
62+
self.send(cmd)
63+
data = self.read_until(self.prompt)
7364
except socket.timeout:
7465
raise TimeoutException
7566

76-
return response
67+
return data
7768

7869
def send(self, cmd):
7970
'''
8071
Send data over the ssh channel.
8172
'''
8273
self.chan.send(cmd + '\n')
8374

84-
def receive(self):
75+
def read_until(self, expected):
8576
'''
86-
Receive data from the ssh channel.
77+
Receive data from the server until we get the expected pattern.
8778
'''
79+
temp = ''
80+
while expected not in temp:
81+
temp += self.chan.recv(1)
82+
83+
temp = temp.split('\r\n')
8884
try:
89-
return self.chan_file.readline().strip('\r\n')
90-
except socket.timeout:
91-
raise TimeoutException
85+
temp.pop()
86+
return temp[-1]
87+
except IndexError:
88+
pass

jstest/testrunner/devices/device_base.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -66,9 +66,11 @@ def iotjs_build_info(self):
6666
return set(), set(), 'stable'
6767

6868
if self.device in ['rpi2', 'artik530']:
69+
tester_py = 'python %s/tester.py ' % self.workdir
6970
iotjs = '%s/iotjs' % self.workdir
7071
buildinfo = '%s/tests/tools/iotjs_build_info.js' % self.workdir
71-
command = '%s %s' % (iotjs, buildinfo)
72+
template = '%s --cwd %s --cmd %s --testfile %s --iotjs-build-info'
73+
command = template % (tester_py, self.workdir, iotjs, buildinfo)
7274

7375
elif self.device in ['artik053', 'stm32f4dis']:
7476
buildinfo = '/test/tools/iotjs_build_info.js'
@@ -92,6 +94,4 @@ def iotjs_build_info(self):
9294
features = set(info['features'])
9395
stability = info['stability']
9496

95-
self.logout()
96-
9797
return builtins, features, stability

jstest/testrunner/devices/rpi2.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,4 +19,5 @@ class RPi2Device(SSHDevice):
1919
Device of the Raspberry Pi 2 target.
2020
'''
2121
def __init__(self, env):
22-
SSHDevice.__init__(self, env, 'linux')
22+
# Note: the PS1 prompt on the device has to have this ending.
23+
SSHDevice.__init__(self, env, 'linux', ':~$')

jstest/testrunner/devices/ssh_device.py

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ class SSHDevice(RemoteDevice):
2525
'''
2626
Common super class for ssh devices.
2727
'''
28-
def __init__(self, env, os):
28+
def __init__(self, env, os, prompt):
2929
self.user = env.options.username
3030
self.ip = env.options.ip
3131
self.port = env.options.port
@@ -37,12 +37,20 @@ def __init__(self, env, os):
3737
'ip': self.ip,
3838
'port': self.port,
3939
'timeout': env.options.timeout,
40-
'_no_exec_command': hasattr(env.options, 'sshclient_no_exec_command')
40+
'prompt': prompt
4141
}
4242

4343
self.channel = SSHConnection(data)
4444
self.workdir = env.options.remote_workdir
4545

46+
self.login()
47+
48+
def __del__(self):
49+
'''
50+
Destructor function.
51+
'''
52+
self.logout()
53+
4654
def check_args(self):
4755
'''
4856
Check that all the arguments are established.
@@ -64,8 +72,6 @@ def execute(self, testset, test):
6472
FIXME: Remove the external tester.py to eliminate code duplications.
6573
Instead, this function should send commands to the device.
6674
'''
67-
self.login()
68-
6975
template = 'python %s/tester.py --cwd %s --cmd %s --testfile %s'
7076
# Absolute path to the test folder.
7177
testdir = '%s/tests' % self.workdir
@@ -97,12 +103,9 @@ def execute(self, testset, test):
97103
client_thread.start()
98104

99105
stdout = self.channel.exec_command(command)
100-
101106
# Since the stdout is a JSON text, parse it.
102107
result = json.loads(stdout)
103108
# Make HTML friendly stdout.
104109
result['output'] = result['output'].rstrip('\n').replace('\n', '<br>')
105110

106-
self.logout()
107-
108111
return result

0 commit comments

Comments
 (0)