Exemple #1
0
class TestsLayout(unittest.TestCase):
    def setUp(self):
        self.old_value = settings['activate_inotify']
        settings['activate_inotify'] = False

        self.cwd = settings["lupulo_cwd"]
        test = "tests/backend/layouts/complete.json"
        self.fp = open(os.path.join(self.cwd, test), "r")
        schema_manager = MagicMock()
        schema_manager.get_events = MagicMock(return_value=["distances",
                                              "something_else"])

        self.layout_manager = LayoutManager(self.fp, schema_manager)
        self.raw = self.layout_manager.raw

        self.contexts = self.layout_manager.contexts
        self.contexts["global"] = self.raw["global"]
        self.contexts["distances"] = self.raw["distances"]

    def tearDown(self):
        self.fp.close()
        settings['activate_inotify'] = self.old_value

    def invalid(self, filepath):
        layout_path = "tests/backend/layouts/" + filepath
        ifp = open(os.path.join(self.cwd, layout_path), "r")
        schema_manager = MagicMock()
        schema_manager.get_events = MagicMock(return_value=["something"])
        self.layout_manager = LayoutManager(ifp, schema_manager)
        self.layout_manager.compile()
        self.assertEqual(len(self.layout_manager.layouts), 0)
        ifp.close()

    def test_invalid_event(self):
        self.invalid("invalid_event.json")

    def test_invalid_size(self):
        self.invalid("invalid_size.json")

    def test_invalid_accessors(self):
        self.invalid("invalid_accessors.json")

    def test_default_accessor(self):
        layout_path = "tests/backend/layouts/" + "default_accessor.json"
        ifp = open(os.path.join(self.cwd, layout_path), "r")
        schema_manager = MagicMock()
        schema_manager.get_events = MagicMock(return_value=["something"])
        self.layout_manager = LayoutManager(ifp, schema_manager)
        self.layout_manager.compile()
        self.assertEqual(len(self.layout_manager.layouts), 1)
        accessor = self.layout_manager.layouts['battery']["accessors"][0]
        self.assertEqual(accessor["event"], "something")
        ifp.close()

    def test_accessor_with_event(self):
        layout_path = "tests/backend/layouts/" + "accessor_with_event.json"
        ifp = open(os.path.join(self.cwd, layout_path), "r")
        schema_manager = MagicMock()
        schema_manager.get_events = MagicMock(return_value=["something"])
        self.layout_manager = LayoutManager(ifp, schema_manager)
        self.layout_manager.compile()
        self.assertEqual(len(self.layout_manager.layouts), 1)
        ifp.close()

    def test_accessor_chaining_invalid(self):
        self.invalid("chaining_invalid.json")

    def test_accessor_chaining_valid(self):
        layout_path = "tests/backend/layouts/" + "chaining_valid.json"
        ifp = open(os.path.join(self.cwd, layout_path), "r")
        schema_manager = MagicMock()
        schema_manager.get_events = MagicMock(return_value=["something"])
        self.layout_manager = LayoutManager(ifp, schema_manager)
        self.layout_manager.compile()
        self.assertEqual(len(self.layout_manager.layouts), 1)
        ifp.close()

    def test_accessor_object(self):
        layout_path = "tests/backend/layouts/" + "accessor_object.json"
        ifp = open(os.path.join(self.cwd, layout_path), "r")
        schema_manager = MagicMock()
        schema_manager.get_events = MagicMock(return_value=["something"])
        self.layout_manager = LayoutManager(ifp, schema_manager)
        self.layout_manager.compile()
        self.assertEqual(len(self.layout_manager.layouts), 1)
        accessor = self.layout_manager.layouts['battery']['accessors']
        self.assertEqual(accessor['battery']['event'], 'something')
        ifp.close()

    def test_missing_attributes(self):
        self.invalid("missing_attributes.json")

    def test_one_level_inheritance(self):
        layout = self.raw["distances"]
        obj = self.layout_manager.inherit(layout)
        attributes = ["anchor", "overwritten", "abstract",
                      "parent", "range", "seconds", "size", "margin"]
        self.assertEqual(set(obj.keys()), set(attributes))

    def test_two_levels_inheritance(self):
        layout = self.raw["distances-center"]
        obj = self.layout_manager.inherit(layout)
        attributes = ["anchor", "overwritten", "parent", "range",
                      "seconds", "event_names", "type", "size", "margin"]
        self.assertEqual(set(obj.keys()), set(attributes))

    def test_overwritten(self):
        layout = self.raw["overwritten"]
        obj = self.layout_manager.inherit(layout)
        self.assertEqual(obj["overwritten"], True)

    def test_invalid_parent(self):
        self.invalid("invalid_parent.json")

    def test_bug_events_accessors(self):
        layout_path = "tests/backend/layouts/" + "bug_events_accessor.json"
        ifp = open(os.path.join(self.cwd, layout_path), "r")
        schema_manager = MagicMock()
        schema_manager.get_events = MagicMock(return_value=["battery", "date"])
        self.layout_manager = LayoutManager(ifp, schema_manager)
        self.layout_manager.compile()
        self.assertEqual(set(self.layout_manager.layouts.keys()),
                         set(['battery-widget', 'device_time']))
        self.assertEqual(self.layout_manager.layouts['battery-widget']['accessors'][0]['event'], 'battery')

    def test_compile_correct(self):
        self.layout_manager.compile()
        layouts = self.layout_manager.layouts
        self.assertEqual(set(layouts.keys()),
                         set(["simple", "distances-center", "overwritten"]))
        self.assertEqual(set(layouts["simple"].keys()),
                         set(["name", "type", "event_names", "anchor",
                              "size", "margin"]))
        self.assertEqual(layouts["simple"]["type"], 1)
        self.assertEqual(layouts["simple"]["event_names"], ["something_else"])
        attributes = ["anchor", "name", "type", "event_names",
                      "range", "overwritten", "seconds", "size", "margin"]
        self.assertEqual(set(layouts["distances-center"].keys()),
                         set(attributes))
        self.assertEqual(layouts["distances-center"]["overwritten"], False)
        self.assertEqual(set(layouts["overwritten"].keys()),
                         set(attributes))
        self.assertEqual(layouts["overwritten"]["overwritten"], True)

    def difference(self, filepath, result):
        self.layout_manager.compile()

        layout_path = "tests/backend/layouts/" + filepath
        ifp = open(os.path.join(self.cwd, layout_path), "r")

        self.layout_manager.fp = ifp
        jdata = {}
        self.layout_manager.inotify_callback(jdata)
        self.assertEqual(jdata, result)

        ifp.close()

    def test_inotify_difference_added(self):
        jdata = {}
        jdata['removed'] = []
        jdata['changed'] = {}
        jdata['added'] = {
            'distances2': {
                'overwritten': False,
                'name': 'distances2',
                'seconds': 100,
                'range': [0, 4],
                'type': 'multiple_line',
                'anchor': 0,
                'event_names': ['distances'],
                'size': {'width': 800, 'height': 600},
                "margin": {"top": 0, "bottom": 0, "right": 0, "left": 0}
            }
        }
        self.difference('difference_added.json', jdata)

    def test_inotify_difference_removed(self):
        jdata = {}
        jdata['removed'] = ['distances-center']
        jdata['changed'] = {}
        jdata['added'] = {}
        self.difference('difference_removed.json', jdata)

    def test_inotify_difference_changed(self):
        jdata = {}
        jdata['removed'] = []
        jdata['added'] = {}
        jdata['changed'] = {
            'distances-center': {
                'overwritten': False,
                'name': 'distances-center',
                'seconds': 100,
                'range': [0, 4],
                'type': 'awesomeness',
                'anchor': 0,
                'event_names': ['distances'],
                'size': {'width': 800, 'height': 600},
                "margin": {"top": 0, "bottom": 0, "right": 0, "left": 0}
            }
        }
        self.difference('difference_changed.json', jdata)

    def test_inotify_not_difference(self):
        self.layout_manager.compile()
        jdata = {}
        self.layout_manager.inotify_callback(jdata)
        self.assertEqual(jdata, {'added': {}, 'changed': {}, 'removed': []})
Exemple #2
0
class SSEResource(resource.Resource):
    """
        Twisted web resource that will work as the SSE server.
    """
    isLeaf = True

    def __init__(self):
        """
            @prop subscribers are the requests which should be updated
            when new information is published to the sse_resource.
        """
        self.subscribers = set()
        # The device ids who have sent a message through this sse resource
        self.ids = []

        fp = open(settings["data_schema"], "r")
        self.data_schema_manager = DataSchemaManager(fp)
        self.data_schema_manager.register_inotify_callback(self.schema_changed)

        fp = open(settings["layout"], "r")
        self.layout_manager = LayoutManager(fp, self.data_schema_manager)
        self.layout_manager.register_inotify_callback(self.layout_changed)
        self.layout_manager.compile()

        if settings['activate_mongo']:
            self.mongo_client = MongoClient(settings['mongo_host'])
            self.db = self.mongo_client[settings['mongo_db']]

        reactor.addSystemEventTrigger('after', 'shutdown', self.clean_up)

    def clean_up(self):
        log.msg("SSEResource cleanup.")
        self.data_schema_manager.fp.close()
        self.layout_manager.fp.close()

    def removeSubscriber(self, subscriber):
        """
            When the request is finished for some reason, the request is
            finished and the subscriber is removed from the set.
        """
        if subscriber in self.subscribers:
            subscriber.finish()
            self.subscribers.remove(subscriber)

    def render_GET(self, request):
        """
            Called when twisted wants to render the page, this method is
            asynchronous and therefore returns NOT_DONE_YET.
        """
        def wrap(x):
            return '"' + str(x) + '"'

        request.setHeader('Content-Type', 'text/event-stream; charset=utf-8')
        request.setResponseCode(200)

        self.subscribers.add(request)
        d = request.notifyFinish()
        d.addBoth(self.removeSubscriber)

        msg = []
        if len(self.ids) > 0:
            msg.append('event: new_devices\n')
            msg.append('data: [%s]\n\n' % ",".join(map(wrap, self.ids)))
            request.write("".join(msg))

        msg = []
        widgets = self.layout_manager.get_widgets()
        msg.append('event: new_widgets\n')
        msg.append('data: %s\n\n' % widgets)
        request.write("".join(msg))

        msg = []
        events = self.data_schema_manager.get_events()
        jdata = json.dumps({'added': events, 'removed': []})
        msg.append('event: new_event_sources\n')
        msg.append('data: %s\n\n' % jdata)
        request.write("".join(msg))

        log.msg("SSE connection made by %s" % request.getClientIP())
        return server.NOT_DONE_YET

    def publish(self, data):
        """
            When data arrives it is written to every request which is in the
            subscribers set.
        """
        if self.data_schema_manager.validate(data):
            jdata = json.loads(data)
            iid = jdata["id"]
            iid_encoded = iid.encode('ascii', 'ignore')

            if settings["activate_mongo"]:
                self.store(data)

            msg = []
            if iid not in self.ids:
                log.msg("New connection from device %s" % iid)
                self.ids.append(iid)
                msg.append('event: new_devices\n')
                msg.append('data: ["%s"]\n\n' % iid_encoded)
            for event, data in jdata.items():
                if event in ["id"]:
                    continue
                event_name = event.encode('ascii', 'ignore')
                msg.append("event: id%s-%s\n" % (iid_encoded, event_name))
                msg.append("data: %s\n\n" % json.dumps(data))

            for subscriber in self.subscribers:
                subscriber.write("".join(msg))

    def store(self, data):
        """
            Store the data in the data collection.
            A string is passed as attribute to avoid pollution of the argument.
        """
        jdata = json.loads(data)
        jdata['timestamp'] = datetime.utcnow()
        self.db.data.insert(jdata)

    def broadcast(self, event_source, data):
        msg = []
        jdata = json.dumps(data)
        msg.append('event: %s\n' % event_source)
        msg.append('data: %s\n\n' % jdata)

        for subscriber in self.subscribers:
            subscriber.write("".join(msg))

    def layout_changed(self, data):
        self.broadcast('new_widgets', data)

    def schema_changed(self, data):
        self.broadcast('new_event_sources', data)