Skip to content

Commit

Permalink
Merge pull request #840 from onkelandy/blockly
Browse files Browse the repository at this point in the history
blockly: update to newest blockly, improve code and user doc
  • Loading branch information
psilo909 authored Nov 28, 2023
2 parents 62cd27d + e1bd8bc commit 494c819
Show file tree
Hide file tree
Showing 201 changed files with 13,841 additions and 2,493 deletions.
419 changes: 9 additions & 410 deletions blockly/__init__.py

Large diffs are not rendered by default.

File renamed without changes.
528 changes: 528 additions & 0 deletions blockly/_pv_1_4_0/__init__.py

Large diffs are not rendered by default.

File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
45 changes: 45 additions & 0 deletions blockly/_pv_1_4_0/plugin.yaml
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.
102 changes: 102 additions & 0 deletions blockly/_pv_1_4_0/tests/cptestcase.py
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
116 changes: 116 additions & 0 deletions blockly/_pv_1_4_0/tests/test_backend_blocklylogics.py
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()
Loading

0 comments on commit 494c819

Please sign in to comment.