Skip to content

Commit 2cf32fa

Browse files
committed
Merge pull request #31 from Overdrivr/improve-cli-testing
Improved cli testing
2 parents 89e3bca + 681cb2f commit 2cf32fa

9 files changed

Lines changed: 454 additions & 205 deletions

File tree

.travis.yml

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
language: python
2+
matrix:
3+
include:
4+
- os: linux
5+
python: '3.3'
6+
- os: linux
7+
python: '3.4'
8+
- os: linux
9+
python: '3.5'
10+
# command to install dependencies
11+
install:
12+
- sudo apt-get update
13+
# We do this conditionally because it saves us some downloading if the
14+
# version is the same.
15+
- if [[ "$TRAVIS_PYTHON_VERSION" == "2.7" ]]; then
16+
wget https://repo.continuum.io/miniconda/Miniconda-latest-Linux-x86_64.sh -O miniconda.sh;
17+
else
18+
wget https://repo.continuum.io/miniconda/Miniconda3-latest-Linux-x86_64.sh -O miniconda.sh;
19+
fi
20+
- bash miniconda.sh -b -p $HOME/miniconda
21+
- export PATH="$HOME/miniconda/bin:$PATH"
22+
- hash -r
23+
- conda config --set always_yes yes --set changeps1 no
24+
- conda update -q conda
25+
# Useful for debugging any issues with conda
26+
- conda info -a
27+
28+
# Replace dep1 dep2 ... with your dependencies
29+
- conda install numpy -y -q
30+
- conda install pyqt -y -q
31+
- conda install pip -y -q
32+
- pip install pytelemetry docopt sortedcontainers pyqtgraph pytest-cov
33+
- export PYTHONPATH="$PYTHONPATH:$TRAVIS_BUILD_DIR"
34+
35+
# command to run tests
36+
script:
37+
- py.test --cov

appveyor.yml

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
version: 1.1.{build}-{branch}
2+
3+
build: false
4+
5+
environment:
6+
matrix:
7+
- MINICONDA: "C:\\Miniconda3"
8+
PYTHON_ARCH: "32"
9+
platform: x86
10+
11+
- MINICONDA: "C:\\Miniconda3"
12+
PYTHON_ARCH: "32"
13+
platform: x64
14+
15+
- MINICONDA: "C:\\Miniconda3"
16+
PYTHON_ARCH: "64"
17+
platform: x64
18+
19+
- MINICONDA: "C:\\Miniconda35"
20+
PYTHON_ARCH: "32"
21+
platform: x86
22+
23+
- MINICONDA: "C:\\Miniconda35"
24+
PYTHON_ARCH: "32"
25+
platform: x64
26+
27+
- MINICONDA: "C:\\Miniconda35"
28+
PYTHON_ARCH: "64"
29+
platform: x64
30+
31+
init:
32+
- "ECHO %PYTHON_ARCH%"
33+
- "ECHO %PYTHON_VERSION%"
34+
- "ECHO %MINICONDA%"
35+
- "ECHO %APPVEYOR_BUILD_FOLDER%"
36+
- "ECHO %PYTHONPATH%"
37+
38+
install:
39+
- "%MINICONDA%\\Scripts\\conda install numpy -y -q"
40+
- "%MINICONDA%\\Scripts\\conda install pyqt -y -q"
41+
- "%MINICONDA%\\Scripts\\conda install pip -y -q"
42+
- "%MINICONDA%\\Scripts\\pip.exe install pytelemetry docopt sortedcontainers pyqtgraph pytest-cov"
43+
- "cd %APPVEYOR_BUILD_FOLDER%"
44+
- "SET PYTHONPATH=%PYTHONPATH%;%APPVEYOR_BUILD_FOLDER%"
45+
test_script:
46+
- "%MINICONDA%\\Scripts\\py.test --cov"

dev-requirements.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
pytelemetry>1.1.0
2+
docopt
3+
sortedcontainers
4+
pyqtgraph

pytelemetrycli/cli.py

Lines changed: 37 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -43,16 +43,19 @@ def fn(self, arg):
4343
return fn
4444

4545
class Application (cmd.Cmd):
46-
def __init__(self):
46+
def __init__(self, transport=None, stdout=None):
4747
# cmd Initialization and configuration
48-
cmd.Cmd.__init__(self)
48+
cmd.Cmd.__init__(self,stdout=stdout)
4949
self.intro = 'pytelemetry terminal started.' \
5050
+ ' (type help for a list of commands.)'
5151
self.prompt = ':> '
5252
self.file = None
5353

5454
# pytelemetry setup
55-
self.transport = transports.SerialTransport()
55+
if not transport:
56+
self.transport = transports.SerialTransport()
57+
else:
58+
self.transport = transport
5659
self.telemetry = Pytelemetry(self.transport)
5760
self.topics = Topics()
5861
self.plots = []
@@ -101,15 +104,17 @@ def do_serial(self, arg):
101104
b = int(arg['--bauds'])
102105
self.runner.connect(arg['<port>'],b)
103106
except IOError as e:
104-
print("Failed to connect to {0} at {1} (bauds)."
107+
self.stdout.write("Failed to connect to {0} at {1} (bauds).\n"
105108
.format(arg['<port>'],b))
106109

107-
logger.warn("Failed to connect to {0} at {1} (bauds). E : "
110+
logger.warn("Failed to connect to {0} at {1} (bauds). E : \n"
108111
.format(arg['<port>'],b,e))
109112
else:
110-
s = "Connected to {0} at {1} (bauds).".format(arg['<port>'],b)
111-
print(s)
113+
s = "Connected to {0} at {1} (bauds).\n".format(arg['<port>'],b)
114+
self.stdout.write(s)
112115
logger.info(s)
116+
self.topics.clear()
117+
logger.info("Cleared all topics for new session.")
113118

114119
@docopt_cmd
115120
def do_print(self, arg):
@@ -124,26 +129,26 @@ def do_print(self, arg):
124129
"""
125130
topic = arg['<topic>']
126131
if not self.topics.exists(topic):
127-
s = "Topic '{0}' unknown. Type 'ls' to list all available topics.".format(topic)
128-
print(s)
132+
s = "Topic '{0}' unknown. Type 'ls' to list all available topics.\n".format(topic)
133+
self.stdout.write(s)
129134
logger.warn(s)
130135
return
131136

132137
try:
133138
amount = int(arg['--amount'])
134139
except:
135-
s = "Could not cast --amount = '{0}' to integer. Using 1.".format(amount)
136-
print(s)
140+
s = "Could not cast --amount = '{0}' to integer. Using 1.\n".format(arg['--amount'])
141+
self.stdout.write(s)
137142
logger.warn(s)
138143
amount = 1
139144

140145
s = self.topics.samples(topic,amount)
141146

142147
if s is not None:
143148
for i in s:
144-
print(i)
149+
self.stdout.write("%i\n" % i)
145150
else:
146-
logger.error("Could not retrieve {0} sample(s) under topic '{1}'.".format(amount,topic))
151+
logger.error("Could not retrieve {0} sample(s) under topic '{1}'.\n".format(amount,topic))
147152

148153
@docopt_cmd
149154
def do_ls(self, arg):
@@ -158,12 +163,14 @@ def do_ls(self, arg):
158163
159164
"""
160165
if arg['--serial']:
161-
print("Available COM ports:")
166+
self.stdout.write("Available COM ports:\n")
162167
for port,desc,hid in list_ports.comports():
163-
print(port,'\t',desc)
168+
self.stdout.write("%s \t %s\n" % (port,desc))
164169
else:
170+
165171
for topic in self.topics.ls():
166-
print(topic)
172+
self.stdout.write("%s\n" % topic)
173+
167174

168175
@docopt_cmd
169176
def do_plot(self, arg):
@@ -176,14 +183,14 @@ def do_plot(self, arg):
176183
topic = arg['<topic>']
177184

178185
if not self.topics.exists(topic):
179-
s = "Topic '{0}' unknown. Type 'ls' to list all available topics.".format(topic)
180-
print(s)
186+
s = "Topic '{0}' unknown. Type 'ls' to list all available topics.\n".format(topic)
187+
self.stdout.write(s)
181188
logger.warn(s)
182189
return
183190

184191
if self.topics.intransfer(topic):
185-
s = "Topic '{0}' already plotting.".format(topic)
186-
print(s)
192+
s = "Topic '{0}' already plotting.\n".format(topic)
193+
self.stdout.write(s)
187194
logger.warn(s)
188195
return
189196

@@ -210,9 +217,9 @@ def do_plot(self, arg):
210217

211218
self.topics.transfer(topic,q)
212219

213-
s = "Plotting '{0}' in mode [{1}].".format(topic,plotTypeFlag)
220+
s = "Plotting '{0}' in mode [{1}].\n".format(topic,plotTypeFlag)
214221
logger.info(s)
215-
print(s)
222+
self.stdout.write(s)
216223

217224
@docopt_cmd
218225
def do_pub(self, arg):
@@ -242,8 +249,8 @@ def do_pub(self, arg):
242249

243250
self.telemetry.publish(arg['<topic>'],arg['<value>'],valtype)
244251

245-
s = "Published on topic '{0}' : {1} [{2}]".format(arg['<topic>'], arg['<value>'],valtype)
246-
print(s)
252+
s = "Published on topic '{0}' : {1} [{2}]\n".format(arg['<topic>'], arg['<value>'],valtype)
253+
self.stdout.write(s)
247254
logger.info(s)
248255

249256
@docopt_cmd
@@ -254,7 +261,7 @@ def do_count(self, arg):
254261
Usage: count
255262
"""
256263
for topic in self.topics.ls():
257-
print("{0} : {1}".format(topic, self.topics.count(topic)))
264+
self.stdout.write("{0} : {1}\n".format(topic, self.topics.count(topic)))
258265

259266
@docopt_cmd
260267
def do_disconnect(self, arg):
@@ -265,7 +272,7 @@ def do_disconnect(self, arg):
265272
"""
266273
try:
267274
self.runner.disconnect()
268-
print("Disconnected.")
275+
self.stdout.write("Disconnected.\n")
269276
logger.info("Disconnected.")
270277
except:
271278
logger.warn("Already disconnected. Continuing happily.")
@@ -277,11 +284,11 @@ def do_info(self, arg):
277284
278285
Usage: info
279286
"""
280-
print("- CLI path : %s" % os.path.dirname(os.path.realpath(__file__)))
287+
self.stdout.write("- CLI path : %s\n" % os.path.dirname(os.path.realpath(__file__)))
281288
try:
282-
print("- version : %s" % __version__)
289+
self.stdout.write("- version : %s\n" % __version__)
283290
except:
284-
print("- version : not found.")
291+
self.stdout.write("- version : not found.\n")
285292

286293
def do_quit(self, arg):
287294
"""
@@ -290,7 +297,7 @@ def do_quit(self, arg):
290297
Usage: quit
291298
"""
292299
self.runner.terminate()
293-
print("Good Bye!")
300+
self.stdout.write("Good Bye!\n")
294301
logger.info("Application quit.")
295302
exit()
296303

pytelemetrycli/runner.py

Lines changed: 26 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -40,31 +40,34 @@ def terminate(self):
4040
except:
4141
pass # Already disconnected
4242

43-
def run(self):
44-
while self.running.is_set():
45-
if self.connected.is_set():
46-
# Update protocol decoding
47-
self.telemetryWrapper.update()
43+
def update(self):
44+
# Update protocol decoding
45+
self.telemetryWrapper.update()
46+
47+
# Protect the self.plots data structure from
48+
# being modified from the main thread
49+
self.plotsLock.acquire()
4850

49-
# Protect the self.plots data structure from
50-
# being modified from the main thread
51-
self.plotsLock.acquire()
52-
53-
# Poll each poll pipe to see if user closed them
54-
plotToDelete = None
55-
for p, i in zip(self.plots,range(len(self.plots))):
56-
if p['ctrl'].poll():
57-
if p['ctrl'].recv() == "closing":
58-
plotToDelete = i
59-
break
51+
# Poll each poll pipe to see if user closed them
52+
plotToDelete = None
53+
for p, i in zip(self.plots,range(len(self.plots))):
54+
if p['ctrl'].poll():
55+
if p['ctrl'].recv() == "closing":
56+
plotToDelete = i
57+
break
6058

61-
# Delete a plot if needed
62-
if plotToDelete is not None:
63-
self.plots[plotToDelete]['ctrl'].close()
64-
topic = self.plots[plotToDelete]['topic']
65-
self.topics.untransfer(topic)
66-
self.plots.pop(plotToDelete)
59+
# Delete a plot if needed
60+
if plotToDelete is not None:
61+
self.plots[plotToDelete]['ctrl'].close()
62+
topic = self.plots[plotToDelete]['topic']
63+
self.topics.untransfer(topic)
64+
self.plots.pop(plotToDelete)
6765

68-
self.plotsLock.release()
66+
self.plotsLock.release()
67+
68+
def run(self):
69+
while self.running.is_set():
70+
if self.connected.is_set():
71+
self.update()
6972
else:
7073
time.sleep(0.5)

0 commit comments

Comments
 (0)