-
Notifications
You must be signed in to change notification settings - Fork 103
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #840 from onkelandy/blockly
blockly: update to newest blockly, improve code and user doc
- Loading branch information
Showing
201 changed files
with
13,841 additions
and
2,493 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
File renamed without changes.
File renamed without changes.
Large diffs are not rendered by default.
Oops, something went wrong.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
# Metadata for the Smart-Plugin | ||
plugin: | ||
# Global plugin attributes | ||
type: system # plugin type (gateway, interface, protocol, system, web) | ||
# subtype: core # plugin subtype (if applicable) | ||
description: # Alternative: description in multiple languages | ||
de: 'Blockly - graphischer Editor für Logiken - Noch in der Entwicklung, nicht für die Nutzung gedacht' | ||
en: 'Blockly - graphical editor for logics - Still in development, not for use' | ||
maintainer: msinn, psilo909 | ||
tester: '?' | ||
state: develop | ||
# keywords: iot xyz | ||
# documentation: https://github.com/smarthomeNG/plugins/blob/develop/mqtt/README.md # url of documentation (wiki) page | ||
# support: https://knx-user-forum.de/forum/supportforen/smarthome-py/959964-support-thread-für-das-backend-plugin | ||
|
||
version: 1.4.0 # Plugin version | ||
sh_minversion: 1.4 # minimum shNG version to use this plugin | ||
# sh_maxversion: # maximum shNG version to use this plugin (leave empty if latest) | ||
multi_instance: False # plugin supports multi instance | ||
restartable: unknown | ||
classname: Blockly # class containing the plugin | ||
|
||
|
||
parameters: | ||
# Definition of parameters to be configured in etc/plugin.yaml | ||
section_prefix: | ||
type: str | ||
default: blockly_ | ||
description: | ||
de: 'Prefix, der dem Sektionsnamen in /etc/logic.yaml vorangstellt wird' | ||
en: 'Prefix that is added to the beginning of the section name in /etc/logic.yaml' | ||
|
||
|
||
item_attributes: NONE | ||
# Definition of item attributes defined by this plugin | ||
|
||
item_structs: NONE | ||
# Definition of item-structure templates for this plugin | ||
|
||
logic_parameters: NONE | ||
# Definition of logic parameters defined by this plugin | ||
|
||
plugin_functions: NONE | ||
# Definition of plugin functions defined by this plugin | ||
|
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,102 @@ | ||
# -*- coding: utf-8 -*- | ||
from io import BytesIO | ||
import unittest | ||
import urllib.request, urllib.parse, urllib.error | ||
|
||
import cherrypy | ||
|
||
# Not strictly speaking mandatory but just makes sense | ||
cherrypy.config.update({'environment': "test_suite"}) | ||
|
||
# This is mandatory so that the HTTP server isn't started | ||
# if you need to actually start (why would you?), simply | ||
# subscribe it back. | ||
cherrypy.server.unsubscribe() | ||
|
||
# simulate fake socket address... they are irrelevant in our context | ||
local = cherrypy.lib.httputil.Host('127.0.0.1', 50000, "") | ||
remote = cherrypy.lib.httputil.Host('127.0.0.1', 50001, "") | ||
|
||
class BaseCherryPyTestCase(unittest.TestCase): | ||
def request(self, path='/', method='GET', app_path='', scheme='http', | ||
proto='HTTP/1.1', data=None, headers=None, **kwargs): | ||
""" | ||
CherryPy does not have a facility for serverless unit testing. | ||
However this recipe demonstrates a way of doing it by | ||
calling its internal API to simulate an incoming request. | ||
This will exercise the whole stack from there. | ||
Remember a couple of things: | ||
* CherryPy is multithreaded. The response you will get | ||
from this method is a thread-data object attached to | ||
the current thread. Unless you use many threads from | ||
within a unit test, you can mostly forget | ||
about the thread data aspect of the response. | ||
* Responses are dispatched to a mounted application's | ||
page handler, if found. This is the reason why you | ||
must indicate which app you are targetting with | ||
this request by specifying its mount point. | ||
You can simulate various request settings by setting | ||
the `headers` parameter to a dictionary of headers, | ||
the request's `scheme` or `protocol`. | ||
.. seealso: http://docs.cherrypy.org/stable/refman/_cprequest.html#cherrypy._cprequest.Response | ||
""" | ||
# This is a required header when running HTTP/1.1 | ||
h = {'Host': '127.0.0.1'} | ||
|
||
if headers is not None: | ||
h.update(headers) | ||
|
||
# If we have a POST/PUT request but no data | ||
# we urlencode the named arguments in **kwargs | ||
# and set the content-type header | ||
if method in ('POST', 'PUT') and not data: | ||
data = urllib.parse.urlencode(kwargs) | ||
kwargs = None | ||
h['content-type'] = 'application/x-www-form-urlencoded' | ||
|
||
# If we did have named arguments, let's | ||
# urlencode them and use them as a querystring | ||
qs = None | ||
if kwargs: | ||
qs = urllib.parse.urlencode(kwargs) | ||
|
||
# if we had some data passed as the request entity | ||
# let's make sure we have the content-length set | ||
fd = None | ||
if data is not None: | ||
h['content-length'] = '%d' % len(data) | ||
#fd = StringIO(data) | ||
fd = BytesIO(data.encode()) | ||
|
||
# Get our application and run the request against it | ||
app = cherrypy.tree.apps.get(app_path) | ||
if not app: | ||
# XXX: perhaps not the best exception to raise? | ||
raise AssertionError("No application mounted at '%s'" % app_path) | ||
|
||
# Cleanup any previous returned response | ||
# between calls to this method | ||
app.release_serving() | ||
|
||
# Let's fake the local and remote addresses | ||
request, response = app.get_serving(local, remote, scheme, proto) | ||
try: | ||
h = [(k, v) for k, v in h.items()] | ||
response = request.run(method, path, qs, proto, h, fd) | ||
finally: | ||
if fd: | ||
fd.close() | ||
fd = None | ||
|
||
if response.output_status.startswith(b'500'): | ||
print(response.body) | ||
raise AssertionError("Unexpected error") | ||
|
||
# collapse the response into a bytestring | ||
response.collapse_body() | ||
return response |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,116 @@ | ||
# -*- coding: utf-8 -*- | ||
import sys | ||
print(f"sys.path={sys.path}") | ||
|
||
from tests import common | ||
import cherrypy | ||
from bs4 import BeautifulSoup | ||
|
||
import lib.item | ||
|
||
#from plugins.backend import WebInterface as Root | ||
#from plugins.backend.tests.cptestcase import BaseCherryPyTestCase | ||
from .cptestcase import BaseCherryPyTestCase | ||
from tests.mock.core import MockSmartHome | ||
|
||
|
||
def setUpModule(): | ||
# bs = MockBackendServer() | ||
# sh = bs._sh | ||
# cherrypy.tree.mount(Root(backendserver=bs,developer_mode=True), '/') | ||
# cherrypy.engine.start() | ||
pass | ||
setup_module = setUpModule | ||
|
||
|
||
def tearDownModule(): | ||
# cherrypy.engine.exit() | ||
pass | ||
teardown_module = tearDownModule | ||
|
||
|
||
class TestCherryPyApp(BaseCherryPyTestCase): | ||
def test_blockly(self): | ||
pass | ||
# dummy, because tests are from the tightly coupled 1. try do integrate blockly | ||
# (before it became a seperate plugin) | ||
|
||
# def test_backendIntegration(self): | ||
# response = self.request('index') | ||
# self.assertEqual(response.output_status, b'200 OK') | ||
# body = BeautifulSoup(response.body[0]) | ||
# self.assertEqual( str(body.find("a", href="logics.html"))[:2], '<a' ) | ||
#self.assertEqual( str(body.find("a", href="logics_blockly.html"))[:2], '<a' ) | ||
|
||
# def test_logics_blockly_html(self): | ||
# response = self.request('logics_blockly_html') | ||
# self.assertEqual(response.output_status, b'200 OK') | ||
# resp_body = str(response.body[0],'utf-8') | ||
# self.assertRegex(resp_body, 'xml id="toolbox"') | ||
# self.assertRegex(resp_body, 'div id="content_blocks"') | ||
# self.assertRegex(resp_body, '<category name="Trigger">') | ||
# # self.assertEqual(response.body, ['hello world']) | ||
|
||
# def test_DynToolbox(self): | ||
# response = self.request('logics_blockly_html') | ||
# #resp_body = str(response.body[0],'utf-8') | ||
# bs_body = BeautifulSoup(response.body[0]) | ||
# #items = bs_body.find("category", name="SmartHome Items") | ||
# shItemsCat = bs_body.xml.find_all(attrs={'name': 'SmartHome Items'})[0] | ||
# # print(shItemsCat) | ||
# # print("categories: {}".format(len(list(shItemsCat.find_all("category")))) ) | ||
# # print(" blocks: {}".format(len(shItemsCat.find_all("block", type="sh_item_obj") )) ) | ||
# self.assertEqual(len(list(shItemsCat.find_all("block", type="sh_item_obj") )), 9 ) | ||
# self.assertEqual(len(list(shItemsCat.find_all("category") )), 6 ) | ||
|
||
# def test_logics_blockly_load(self): | ||
# response = self.request('logics_blockly_load') | ||
# self.assertEqual(response.output_status, b'200 OK') | ||
# resp_xml = str(response.body[0],'utf-8') | ||
# #print(resp_xml) | ||
# self.assertRegex(resp_xml, '<field name="N">Unit Test</field>') | ||
# self.assertRegex(resp_xml, '<field name="P">testen.unit.test</field>') | ||
# self.assertRegex(resp_xml, '<field name="T">bool</field>') | ||
|
||
|
||
|
||
# def test_logics_blockly_load(self): | ||
# with open(fn_py, 'w') as fpy: | ||
# with open(fn_xml, 'w') as fxml: | ||
# fpy.write(py) | ||
# fxml.write(xml) | ||
|
||
# def test_echo(self): | ||
# response = self.request('/echo', msg="hey there") | ||
# self.assertEqual(response.output_status, '200 OK') | ||
# self.assertEqual(response.body, ["hey there"]) | ||
# | ||
# response = self.request('/echo', method='POST', msg="back from the future") | ||
# self.assertEqual(response.output_status, '200 OK') | ||
# self.assertEqual(response.body, ["back from the future"]) | ||
# | ||
|
||
|
||
class MockBackendServer(): | ||
import os | ||
cwd = os.getcwd() | ||
print(f"blockly cwd={cwd}") | ||
os.chdir('..') | ||
cwd = os.getcwd() | ||
print(f"blockly new cwd={cwd}") | ||
|
||
_sh = MockSmartHome() | ||
print(f"blockly etc_dir = {_sh.get_etcdir()}") | ||
|
||
def __init__(self): | ||
self._sh.with_items_from(common.BASE + "/tests/resources/blockly_items.conf") | ||
|
||
# HACK: Make tests work! Backend accesses private field _logic_dir | ||
# directly instead of using a method (the field was remove in the | ||
# meantime). Setting this just to make it work again. | ||
self._sh._logic_dir = common.BASE + "/tests/resources/" | ||
|
||
|
||
if __name__ == '__main__': | ||
import unittest | ||
unittest.main() |
Oops, something went wrong.