Skip to content

Commit 1f87bdd

Browse files
author
brentru
committed
add test file and modified unittest
1 parent d67fcc8 commit 1f87bdd

File tree

2 files changed

+375
-0
lines changed

2 files changed

+375
-0
lines changed
Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
"""
2+
CircuitPython_AdafruitIO IO_HTTP Tester
3+
--------------------------------------------------
4+
5+
Tests Adafruit IO CircuitPython HTTP method
6+
coverage with a WiFi CircuitPython device.
7+
8+
* Author(s): Brent Rubell for Adafruit Industries
9+
"""
10+
from random import randint, uniform
11+
import time
12+
import board
13+
import busio
14+
from digitalio import DigitalInOut
15+
from adafruit_esp32spi import adafruit_esp32spi, adafruit_esp32spi_wifimanager
16+
import neopixel
17+
from adafruit_io.adafruit_io import IO_HTTP, AdafruitIO_RequestError
18+
19+
# REQUIRES MicroPython's UnitTest
20+
# https://github.com/micropython/micropython-lib/tree/master/unittest
21+
import unittest
22+
23+
# Get wifi details and more from a secrets.py file
24+
try:
25+
from secrets import secrets
26+
except ImportError:
27+
print("WiFi secrets are kept in secrets.py, please add them there!")
28+
raise
29+
30+
# ESP32 Setup
31+
esp32_cs = DigitalInOut(board.ESP_CS)
32+
esp32_ready = DigitalInOut(board.ESP_BUSY)
33+
esp32_reset = DigitalInOut(board.ESP_RESET)
34+
spi = busio.SPI(board.SCK, board.MOSI, board.MISO)
35+
esp = adafruit_esp32spi.ESP_SPIcontrol(spi, esp32_cs, esp32_ready, esp32_reset)
36+
status_light = neopixel.NeoPixel(board.NEOPIXEL, 1, brightness=0.2)
37+
wifi = adafruit_esp32spi_wifimanager.ESPSPI_WiFiManager(esp, secrets, status_light)
38+
39+
# Set your Adafruit IO Username and Key in secrets.py
40+
# (visit io.adafruit.com if you need to create an account,
41+
# or if you need your Adafruit IO key.)
42+
aio_username = secrets["aio_user"]
43+
aio_key = secrets["aio_password"]
44+
45+
46+
class Test_IO_HTTP(unittest.TestCase):
47+
48+
# Tests for Adafruit IO Authentication
49+
def test_set_user_key(self):
50+
"""__init__ constructor
51+
correctly exposes provided credentials.
52+
"""
53+
username = "adabot"
54+
key = "mho"
55+
io = IO_HTTP(username, key, wifi)
56+
self.assertEqual(username, io.username)
57+
self.assertEqual(key, io.key)
58+
59+
def test_incorrect_user_pass_action(self):
60+
"""Incorrect credentials provided to __init__
61+
should raise a RequestError.
62+
"""
63+
username = "adabot"
64+
key = "mho"
65+
io = IO_HTTP(username, key, wifi)
66+
with self.assertRaises(AdafruitIO_RequestError):
67+
test_feed = io.get_feed("errorfeed")
68+
pass
69+
70+
# Tests for Adafruit IO Data Methods
71+
def test_txrx(self):
72+
"""Sends a random integer value to a feed and receives it back.
73+
"""
74+
# Create an Adafruit IO HTTP Client
75+
io = IO_HTTP(aio_username, aio_key, wifi)
76+
try:
77+
test_feed = io.get_feed("testfeed")
78+
except AdafruitIO_RequestError:
79+
test_feed = io.create_new_feed("testfeed")
80+
tx_data = randint(1, 100)
81+
# send the value
82+
io.send_data(test_feed["key"], tx_data)
83+
# and get it back...
84+
rx_data = io.receive_data(test_feed["key"])
85+
self.assertEqual(int(rx_data["value"]), tx_data)
86+
87+
def test_send_location_data(self):
88+
"""Sets location metadata.
89+
send_data
90+
"""
91+
# Create an Adafruit IO HTTP Client
92+
io = IO_HTTP(aio_username, aio_key, wifi)
93+
io.delete_feed('testfeed')
94+
test_feed = io.create_new_feed('testfeed')
95+
# value
96+
value = randint(1, 100)
97+
# Set up metadata associated with value
98+
metadata = {'lat': uniform(1, 100),
99+
'lon': uniform(1, 100),
100+
'ele': 10,
101+
'created_at': None}
102+
io.send_data(test_feed['key'], value, metadata)
103+
rx_data = io.receive_data(test_feed['key'])
104+
self.assertEqual(int(rx_data['value']), value)
105+
self.assertAlmostEqual(float(rx_data['lat']), metadata['lat'])
106+
self.assertAlmostEqual(float(rx_data['lon']), metadata['lon'])
107+
self.assertAlmostEqual(float(rx_data['ele']), metadata['ele'])
108+
109+
# Test for Adafruit IO Feed Methods
110+
def test_create_feed(self):
111+
"""Test creating a new feed.
112+
"""
113+
# Create an Adafruit IO HTTP Client
114+
io = IO_HTTP(aio_username, aio_key, wifi)
115+
io.delete_feed('testfeed')
116+
test_feed = io.create_new_feed('testfeed')
117+
self.assertEqual(test_feed['name'], 'testfeed')
118+
119+
def test_delete_feed(self):
120+
"""delete_feed by feed key
121+
"""
122+
# Create an Adafruit IO HTTP Client
123+
io = IO_HTTP(aio_username, aio_key, wifi)
124+
io.delete_feed('testfeed')
125+
with self.assertRaises(AdafruitIO_RequestError):
126+
io.receive_data('testfeed'['key'])
127+
pass
128+
129+
def test_delete_nonexistent_feed(self):
130+
"""delete nonexistent feed by feed key
131+
"""
132+
# Create an Adafruit IO HTTP Client
133+
io = IO_HTTP(aio_username, aio_key, wifi)
134+
io.delete_feed('testfeed')
135+
with self.assertRaises(AdafruitIO_RequestError):
136+
io.delete_feed['testfeed']
137+
138+
139+
if __name__ == "__main__":
140+
# Pass the NetworkManager Object to UnitTest.py
141+
unittest.get_wifi(wifi)
142+
unittest.main()

examples/tests/unittest.py

Lines changed: 233 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,233 @@
1+
# UnitTest.py
2+
# https://github.com/micropython/micropython-lib/blob/master/unittest/unittest.py
3+
# Modified for handling ESP32SPI module connectivity
4+
5+
class SkipTest(Exception):
6+
pass
7+
8+
9+
class AssertRaisesContext:
10+
11+
def __init__(self, exc):
12+
self.expected = exc
13+
14+
def __enter__(self):
15+
return self
16+
17+
def __exit__(self, exc_type, exc_value, tb):
18+
if exc_type is None:
19+
assert False, "%r not raised" % self.expected
20+
if issubclass(exc_type, self.expected):
21+
return True
22+
return False
23+
24+
25+
class TestCase:
26+
27+
def fail(self, msg=''):
28+
assert False, msg
29+
30+
def assertEqual(self, x, y, msg=''):
31+
if not msg:
32+
msg = "%r vs (expected) %r" % (x, y)
33+
assert x == y, msg
34+
35+
def assertNotEqual(self, x, y, msg=''):
36+
if not msg:
37+
msg = "%r not expected to be equal %r" % (x, y)
38+
assert x != y, msg
39+
40+
def assertAlmostEqual(self, x, y, places=None, msg='', delta=None):
41+
if x == y:
42+
return
43+
if delta is not None and places is not None:
44+
raise TypeError("specify delta or places not both")
45+
46+
if delta is not None:
47+
if abs(x - y) <= delta:
48+
return
49+
if not msg:
50+
msg = '%r != %r within %r delta' % (x, y, delta)
51+
else:
52+
if places is None:
53+
places = 7
54+
if round(abs(y-x), places) == 0:
55+
return
56+
if not msg:
57+
msg = '%r != %r within %r places' % (x, y, places)
58+
59+
assert False, msg
60+
61+
def assertNotAlmostEqual(self, x, y, places=None, msg='', delta=None):
62+
if delta is not None and places is not None:
63+
raise TypeError("specify delta or places not both")
64+
65+
if delta is not None:
66+
if not (x == y) and abs(x - y) > delta:
67+
return
68+
if not msg:
69+
msg = '%r == %r within %r delta' % (x, y, delta)
70+
else:
71+
if places is None:
72+
places = 7
73+
if not (x == y) and round(abs(y-x), places) != 0:
74+
return
75+
if not msg:
76+
msg = '%r == %r within %r places' % (x, y, places)
77+
78+
assert False, msg
79+
80+
def assertIs(self, x, y, msg=''):
81+
if not msg:
82+
msg = "%r is not %r" % (x, y)
83+
assert x is y, msg
84+
85+
def assertIsNot(self, x, y, msg=''):
86+
if not msg:
87+
msg = "%r is %r" % (x, y)
88+
assert x is not y, msg
89+
90+
def assertIsNone(self, x, msg=''):
91+
if not msg:
92+
msg = "%r is not None" % x
93+
assert x is None, msg
94+
95+
def assertIsNotNone(self, x, msg=''):
96+
if not msg:
97+
msg = "%r is None" % x
98+
assert x is not None, msg
99+
100+
def assertTrue(self, x, msg=''):
101+
if not msg:
102+
msg = "Expected %r to be True" % x
103+
assert x, msg
104+
105+
def assertFalse(self, x, msg=''):
106+
if not msg:
107+
msg = "Expected %r to be False" % x
108+
assert not x, msg
109+
110+
def assertIn(self, x, y, msg=''):
111+
if not msg:
112+
msg = "Expected %r to be in %r" % (x, y)
113+
assert x in y, msg
114+
115+
def assertIsInstance(self, x, y, msg=''):
116+
assert isinstance(x, y), msg
117+
118+
def assertRaises(self, exc, func=None, *args, **kwargs):
119+
if func is None:
120+
return AssertRaisesContext(exc)
121+
122+
try:
123+
func(*args, **kwargs)
124+
assert False, "%r not raised" % exc
125+
except Exception as e:
126+
if isinstance(e, exc):
127+
return
128+
raise
129+
130+
131+
132+
def skip(msg):
133+
def _decor(fun):
134+
# We just replace original fun with _inner
135+
def _inner(self):
136+
raise SkipTest(msg)
137+
return _inner
138+
return _decor
139+
140+
def skipIf(cond, msg):
141+
if not cond:
142+
return lambda x: x
143+
return skip(msg)
144+
145+
def skipUnless(cond, msg):
146+
if cond:
147+
return lambda x: x
148+
return skip(msg)
149+
150+
151+
class TestSuite:
152+
def __init__(self):
153+
self.tests = []
154+
def addTest(self, cls):
155+
self.tests.append(cls)
156+
157+
class TestRunner:
158+
def run(self, suite):
159+
res = TestResult()
160+
for c in suite.tests:
161+
run_class(c, res)
162+
163+
print("Ran %d tests\n" % res.testsRun)
164+
if res.failuresNum > 0 or res.errorsNum > 0:
165+
print("FAILED (failures=%d, errors=%d)" % (res.failuresNum, res.errorsNum))
166+
else:
167+
msg = "OK"
168+
if res.skippedNum > 0:
169+
msg += " (%d skipped)" % res.skippedNum
170+
print(msg)
171+
172+
return res
173+
174+
class TestResult:
175+
def __init__(self):
176+
self.errorsNum = 0
177+
self.failuresNum = 0
178+
self.skippedNum = 0
179+
self.testsRun = 0
180+
181+
def wasSuccessful(self):
182+
return self.errorsNum == 0 and self.failuresNum == 0
183+
184+
# TODO: Uncompliant
185+
def run_class(c, test_result):
186+
o = c()
187+
set_up = getattr(o, "setUp", lambda: None)
188+
tear_down = getattr(o, "tearDown", lambda: None)
189+
for name in dir(o):
190+
if name.startswith("test"):
191+
print("%s (%s) ..." % (name, c.__qualname__), end="")
192+
m = getattr(o, name)
193+
set_up()
194+
try:
195+
test_result.testsRun += 1
196+
m()
197+
print(" ok")
198+
except SkipTest as e:
199+
print(" skipped:", e.args[0])
200+
test_result.skippedNum += 1
201+
except (RuntimeError, ValueError) as e:
202+
print('Failed to get data from ESP32, retrying...\n')
203+
wifi.reset()
204+
pass
205+
except:
206+
print(" FAIL")
207+
test_result.failuresNum += 1
208+
# Uncomment to investigate failure in detail
209+
raise
210+
continue
211+
finally:
212+
tear_down()
213+
214+
def get_wifi(wifi_module):
215+
wifi = wifi_module
216+
return wifi
217+
218+
def main(module="__main__"):
219+
def test_cases(m):
220+
for tn in dir(m):
221+
c = getattr(m, tn)
222+
if isinstance(c, object) and isinstance(c, type) and issubclass(c, TestCase):
223+
yield c
224+
225+
m = __import__(module)
226+
suite = TestSuite()
227+
for c in test_cases(m):
228+
suite.addTest(c)
229+
runner = TestRunner()
230+
result = runner.run(suite)
231+
# Terminate with non zero return code in case of failures
232+
if result.failuresNum > 0:
233+
raise ValueError('Failures: ', result.failuresNum)

0 commit comments

Comments
 (0)