From 3c591f146e4794ada40bdc4c0bdf75cd194ad966 Mon Sep 17 00:00:00 2001 From: Edwin Eefting Date: Fri, 13 Oct 2017 01:19:35 +0200 Subject: [PATCH] refactored and added automated test stuff --- test/config.py | 8 ++-- test/espeasy.py | 31 +++++++++++++++ test/esptest | 44 ++++++++++++++++------ test/{esp.py => node.py} | 81 ++++++++++++++++++++++++++++------------ test/util.py | 22 +++++++++++ 5 files changed, 148 insertions(+), 38 deletions(-) create mode 100644 test/espeasy.py rename test/{esp.py => node.py} (70%) create mode 100644 test/util.py diff --git a/test/config.py b/test/config.py index bddeac769..52373acaa 100644 --- a/test/config.py +++ b/test/config.py @@ -1,7 +1,7 @@ -units=[ +nodes=[ { 'type' : 'wemos d1 mini v2.2.0', - 'port' : '/dev/ttyUSB0', + 'port' : '/dev/serial/by-path/pci-0000:00:14.0-usb-0:3.1:1.0-port0', 'ip' : '192.168.13.91', 'flash_cmd' : 'esptool.py --port {port} -b 1500000 write_flash 0x0 .pioenvs/dev_4096/firmware.bin --flash_size=32m -p', 'build_cmd' : 'platformio run --environment dev_4096' @@ -9,7 +9,7 @@ units=[ { 'type' : 'nodemcu geekcreit ESP12E devkit v2', - 'port' : '/dev/serial/by-path/pci-0000:00:14.0-usb-0:3.4.3:1.0-port0', + 'port' : '/dev/serial/by-path/pci-0000:00:14.0-usb-0:3.2:1.0-port0', 'ip' : '192.168.13.92', 'flash_cmd' : 'esptool.py --port {port} -b 1500000 write_flash 0x0 .pioenvs/dev_4096/firmware.bin --flash_size=32m -p', 'build_cmd' : 'platformio run --environment dev_4096' @@ -17,6 +17,8 @@ units=[ ] +mqtt_broker="192.168.13.236" + # TYPE2="geekcreit ESP12E devkit v2" # SERIAL2=/dev/serial/by-path/pci-0000:00:14.0-usb-0:3.4.3:1.0-port0 # IP2=192.168.13.92 diff --git a/test/espeasy.py b/test/espeasy.py new file mode 100644 index 000000000..cd3b35009 --- /dev/null +++ b/test/espeasy.py @@ -0,0 +1,31 @@ +### High level ESPEasy config. +### Things like configging a controller or device via http +### I've created it in way that you can just copy/past form parameters from Chromium's header view + + +class EspEasy: + + def __init__(self, node): + self._node=node + + + def controller_domoticz_mqtt(self, **kwargs): + self._node.http_post( + page="controllers", + + params=""" + index: {index} + """.format(**kwargs), + + data=""" + protocol:2 + usedns:0 + controllerip:{mqtt_broker} + controllerport:1883 + controlleruser: + controllerpassword: http://urltje + controllersubscribe:domoticz/out + controllerpublish:domoticz/in + controllerenabled:on + """.format(**kwargs) + ) diff --git a/test/esptest b/test/esptest index c113bb27a..c7f409d7c 100755 --- a/test/esptest +++ b/test/esptest @@ -4,10 +4,15 @@ import argparse import json import sys import config -import esp +import node +import espeasy import pydoc +import util -parser = argparse.ArgumentParser(description='ESPEasy testing framework', epilog=pydoc.render_doc(esp.Esp), formatter_class=argparse.RawDescriptionHelpFormatter) +def get_epilog(): + return(pydoc.render_doc(node.Node) + pydoc.render_doc(espeasy.EspEasy)) + +parser = argparse.ArgumentParser(description='ESPEasy testing framework', epilog=get_epilog(), formatter_class=argparse.RawDescriptionHelpFormatter) # parser.add_argument('--url', default='http://localhost:8080/rpc', help='url of rpc server to connect to. default: %(default)s') # parser.add_argument('--user', default=None, help='user to login.') @@ -18,21 +23,30 @@ parser = argparse.ArgumentParser(description='ESPEasy testing framework', epilog # parser.add_argument('--short', action='store_true', help='return short single-line JSON output (for scripting)') # parser.add_argument('--verbose', action='store_true', help='request verbose mode (module help output)') # parser.add_argument('--insecure', action='store_true', help='dont verify SSL certificates. (use with self-signed certificates)') -parser.add_argument('command', nargs=1, help='Function from the Esp class to run (help below)') -parser.add_argument('unit', type=int,nargs=1, help='Unit number (from config)') +parser.add_argument('--debug', action='store_true', help='enable http debugging output') + +parser.add_argument('module', nargs=1, help='Module to use (node or espeasy)') +parser.add_argument('node', type=int,nargs=1, help='Node number (from config)') +parser.add_argument('command', nargs=1, help='Function from the module (help below)') parser.add_argument('params', default=[], nargs='*', help='command parameters. in foo=bar form ') args = parser.parse_args() try: - if args.unit[0]<1: - raise(Exception("unit number should be 1 or higher")) + if args.node[0]<1: + raise(Exception("node number should be 1 or higher")) + + if args.debug: + util.enable_http_debug() - unit=args.unit[0]-1 + node_index=args.node[0]-1 + + config.nodes[node_index]['node']=args.node[0] + + #we always need a node instance + node_instance=node.Node(config.nodes[node_index]) - config.units[unit]['unit']=args.unit[0] - e=esp.Esp(config.units[unit]) #determine rpc parameters params={} @@ -40,8 +54,16 @@ try: (key,value)=param.split("=") params[key]=value - command = getattr(e, args.command[0]) - command(**params) + if args.module[0]=='node': + command = getattr(node_instance, args.command[0]) + command(**params) + + elif args.module[0]=='espeasy': + espeasy_instance=espeasy.EspEasy(node_instance) + command = getattr(espeasy_instance, args.command[0]) + command(**params) + else: + raise(Exception("Unknown module:"+args.module[0])) sys.exit(0) diff --git a/test/esp.py b/test/node.py similarity index 70% rename from test/esp.py rename to test/node.py index f016fce95..0755bd75f 100644 --- a/test/esp.py +++ b/test/node.py @@ -1,3 +1,6 @@ +### Generic lowlevel ESP and espeasy per-node stuff. +### Used for things like flashing, serial communication, resetting, wificonfig, http communication + import serial import sys import time @@ -5,16 +8,16 @@ import subprocess import wificonfig import requests import serial.tools.miniterm - +import re def log(txt): print(txt, end="", flush=True) -class Esp(): +class Node(): def __init__(self, config): - print("Using unit {unit} ({type}) with ip {ip}".format(**config)) + print("Using node {node} ({type}) with ip {ip}".format(**config)) self._config=config self._url="http://{ip}/".format(**self._config) self._serial_initialized=False @@ -126,28 +129,58 @@ class Esp(): self.serial() - def config_device(self): + + def http_post(self, page, params, data): + """http post to espeasy webinterface""" + + # transform easy copy/pastable chromium data into a dict + + params_dict={} + for line in params.split("\n"): + m=re.match(" *(.*?):(.*)",line) + if (m): + params_dict[m.group(1)]=m.group(2) + + data_dict={} + for line in data.split("\n"): + m=re.match(" *(.*?):(.*)",line) + if (m): + data_dict[m.group(1)]=m.group(2) + + r=requests.post( - self._url+"devices", - params={ - 'index':1, - 'page':1 - }, - data={ - 'TDNUM':1, - 'TDN': "", - 'TDE': 'on', - 'taskdevicepin1': 12, - 'plugin_001_type':1, - 'plugin_001_button':0, - 'TDT':0, - 'TDSD1':'on', - 'TDID1':1, - 'TDVN1':'Switch', - 'edit':1, - 'page':1 - } + self._url+page, + params=params_dict, + data=data_dict ) + r.raise_for_status() + # + # return(r) + # + # + # def config_device(self): + # + # r=requests.post( + # self._url+"devices", + # params={ + # 'index':1, + # 'page':1 + # }, + # data={ + # 'TDNUM':1, + # 'TDN': "", + # 'TDE': 'on', + # 'taskdevicepin1': 12, + # 'plugin_001_type':1, + # 'plugin_001_button':0, + # 'TDT':0, + # 'TDSD1':'on', + # 'TDID1':1, + # 'TDVN1':'Switch', + # 'edit':1, + # 'page':1 + # } + # ) - print(r.url) + # print(r.url) diff --git a/test/util.py b/test/util.py new file mode 100644 index 000000000..c0aa820af --- /dev/null +++ b/test/util.py @@ -0,0 +1,22 @@ + +def enable_http_debug(): + + import requests + import logging + + # These two lines enable debugging at httplib level (requests->urllib3->http.client) + # You will see the REQUEST, including HEADERS and DATA, and RESPONSE with HEADERS but without DATA. + # The only thing missing will be the response.body which is not logged. + try: + import http.client as http_client + except ImportError: + # Python 2 + import httplib as http_client + http_client.HTTPConnection.debuglevel = 1 + + # You must initialize logging, otherwise you'll not see debug output. + logging.basicConfig() + logging.getLogger().setLevel(logging.DEBUG) + requests_log = logging.getLogger("requests.packages.urllib3") + requests_log.setLevel(logging.DEBUG) + requests_log.propagate = True