Пример #1
0
 def test_validation_list_calls(self, EnumMock):
     test = "tests/backend/data_schemas/list.json"
     ifp = open(os.path.join(self.cwd, test), "r")
     dsd = DataSchemaManager(ifp)
     EnumMock.assert_called_once_with(values=["on", "off", "null"])
     data = '{"leds": ["on", "off", "null"], "id": 1}'
     validate = MagicMock(return_value=True)
     dsd.descriptors["leds"].delegate.validate = validate
     self.assertEqual(dsd.validate(data), True)
     self.assertEqual(validate.call_count, 3)
Пример #2
0
 def test_generate_nested_list_dict(self):
     test = "tests/backend/data_schemas/list_dict.json"
     ifp = open(os.path.join(self.cwd, test), "r")
     dsd = DataSchemaManager(ifp)
     data = dsd.generate(1)
     jdata = json.loads(data)
     self.assertEqual(set(jdata.keys()), set(["motor", "id"]))
     self.assertEqual(len(jdata["motor"]), 2)
     self.assertEqual(set(jdata["motor"][0].keys()),
                      set(["speed", "turn_radius"]))
     self.assertEqual(set(jdata["motor"][1].keys()),
                      set(["speed", "turn_radius"]))
Пример #3
0
 def test_validation_enum(self):
     test = "tests/backend/data_schemas/enum.json"
     ifp = open(os.path.join(self.cwd, test), "r")
     dsd = DataSchemaManager(ifp)
     data = '{"interesting_name": 1, "id": 1}'
     self.assertEqual(dsd.validate(data), True)
     data = '{"interesting_name": 2, "id": 1}'
     self.assertEqual(dsd.validate(data), True)
     data = '{"interesting_name": 3, "id": 1}'
     self.assertEqual(dsd.validate(data), True)
     data = '{"interesting_name": 4, "id": 1}'
     self.assertEqual(dsd.validate(data), False)
Пример #4
0
 def test_validation_dict_calls(self, NumberMock, EnumMock):
     test = "tests/backend/data_schemas/dict.json"
     ifp = open(os.path.join(self.cwd, test), "r")
     dsd = DataSchemaManager(ifp)
     EnumMock.assert_called_once_with(values=[0, 3], type="enum")
     NumberMock.assert_called_once_with(range=[0, 5], type="number")
     data = '{"motor": {"speed": 4, "turn_radius": 3}, "id": 1}'
     v_enum = MagicMock(return_value=True)
     v_number = MagicMock(return_value=True)
     dsd.descriptors["motor"].delegates["speed"].validate = v_number
     dsd.descriptors["motor"].delegates["turn_radius"].validate = v_enum
     self.assertEqual(dsd.validate(data), True)
     v_enum.assert_called_once_with(3)
     v_number.assert_called_once_with(4)
Пример #5
0
 def test_validaton_nested_list_dict(self, MockNumber):
     mock_validate = MagicMock(return_value=True)
     MockNumber().validate = mock_validate
     test = "tests/backend/data_schemas/list_dict.json"
     ifp = open(os.path.join(self.cwd, test), "r")
     dsd = DataSchemaManager(ifp)
     data = """
         {
          "id": 1,
          "motor": [{"speed": 4, "turn_radius": 3},
                    {"speed": 3, "turn_radius": 2}]
         }
     """
     dsd.validate(data)
     self.assertEqual(mock_validate.called, True)
Пример #6
0
 def setUp(self):
     self.cwd = settings["lupulo_cwd"]
     test = "tests/backend/data_schemas/complete.json"
     self.fp = open(os.path.join(self.cwd, test), "r")
     self.old_inotify = settings['activate_inotify']
     settings['activate_inotify'] = False
     self.valid_schema_desc = DataSchemaManager(self.fp)
Пример #7
0
    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)
Пример #8
0
class TestDataSchemaGenerations(unittest.TestCase):
    def setUp(self):
        self.cwd = settings["lupulo_cwd"]
        test = "tests/backend/data_schemas/complete.json"
        self.fp = open(os.path.join(self.cwd, test), "r")
        self.old_inotify = settings['activate_inotify']
        settings['activate_inotify'] = False
        self.valid_schema_desc = DataSchemaManager(self.fp)

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

    def test_generate_complete(self):
        data = self.valid_schema_desc.generate(1)
        jdata = json.loads(data)
        valid_keys = set(self.valid_schema_desc.descriptors.keys())
        valid_keys.add("id")
        self.assertEqual(set(jdata.keys()), valid_keys)

    def test_generate_partial(self):
        data = self.valid_schema_desc.generate(1, ["battery", "date"])
        jdata = json.loads(data)
        self.assertEqual(["battery", "date", "id"], jdata.keys())

    def test_generate_null(self):
        data = self.valid_schema_desc.generate([])
        jdata = json.loads(data)
        valid_keys = set(self.valid_schema_desc.descriptors.keys())
        valid_keys.add("id")
        self.assertEqual(set(jdata.keys()), valid_keys)
        data = self.valid_schema_desc.generate(1, ["nothing"])
        jdata = json.loads(data)
        self.assertEqual(jdata.keys(), ["id"])

    def test_generate_list(self):
        data = self.valid_schema_desc.generate(1, ["distances"])
        jdata = json.loads(data)
        self.assertEqual(set(["distances", "id"]), set(jdata.keys()))
        self.assertEqual(len(jdata["distances"]), 8)

    def test_generate_dict(self):
        data = self.valid_schema_desc.generate(1, ["motor"])
        jdata = json.loads(data)
        self.assertEqual(set(["motor", "id"]), set(jdata.keys()))
        self.assertEqual(type(jdata["motor"]), dict)
        self.assertEqual(set(["turn_radius", "speed"]),
                         set(jdata["motor"].keys()))
        self.assertEqual(type(jdata["motor"]["speed"]), float)
        self.assertEqual(type(jdata["motor"]["turn_radius"]), float)

    def test_generate_nested_list_dict(self):
        test = "tests/backend/data_schemas/list_dict.json"
        ifp = open(os.path.join(self.cwd, test), "r")
        dsd = DataSchemaManager(ifp)
        data = dsd.generate(1)
        jdata = json.loads(data)
        self.assertEqual(set(jdata.keys()), set(["motor", "id"]))
        self.assertEqual(len(jdata["motor"]), 2)
        self.assertEqual(set(jdata["motor"][0].keys()),
                         set(["speed", "turn_radius"]))
        self.assertEqual(set(jdata["motor"][1].keys()),
                         set(["speed", "turn_radius"]))
Пример #9
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)
Пример #10
0
class TestDataSchemaValidations(unittest.TestCase):
    def setUp(self):
        self.cwd = settings["lupulo_cwd"]
        test = "tests/backend/data_schemas/complete.json"
        self.fp = open(os.path.join(self.cwd, test), "r")
        self.old_inotify = settings["activate_inotify"]
        settings["activate_inotify"] = False
        self.valid_schema_desc = DataSchemaManager(self.fp)

    def tearDown(self):
        settings["activate_inotify"] = self.old_inotify
        self.fp.close()

    def test_validation_number(self):
        data = '{"rotation": 180, "id": 1}'
        self.assertEqual(self.valid_schema_desc.validate(data), True)
        data = '{"rotation": 400, "id": 1}'
        self.assertEqual(self.valid_schema_desc.validate(data), False)
        data = '{"direction": 180, "id": 1}'
        self.assertEqual(self.valid_schema_desc.validate(data), False)

    def test_validation_enum(self):
        test = "tests/backend/data_schemas/enum.json"
        ifp = open(os.path.join(self.cwd, test), "r")
        dsd = DataSchemaManager(ifp)
        data = '{"interesting_name": 1, "id": 1}'
        self.assertEqual(dsd.validate(data), True)
        data = '{"interesting_name": 2, "id": 1}'
        self.assertEqual(dsd.validate(data), True)
        data = '{"interesting_name": 3, "id": 1}'
        self.assertEqual(dsd.validate(data), True)
        data = '{"interesting_name": 4, "id": 1}'
        self.assertEqual(dsd.validate(data), False)

    def test_validation_list(self):
        data = """
            {
             "id": 1,
             "leds": ["on", "off", "null", "on",
                      "null", "off", "null", "on"]
            }
        """
        self.assertEqual(self.valid_schema_desc.validate(data), True)
        data = """
            {
             "id": 1,
             "leds": ["on", "off", "null", "on",
                      "null", "off", "null", "on",
                      "off"]
            }
        """
        self.assertEqual(self.valid_schema_desc.validate(data), False)
        data = """
            {
             "id": 1,
             "leds": ["shit", "off", "null", "on",
                      "null", "off", "null", "on"]
            }
        """
        self.assertEqual(self.valid_schema_desc.validate(data), False)
        data = '{"leds": ["off"], "id": 1}'
        self.assertEqual(self.valid_schema_desc.validate(data), False)

    @patch("lupulo.descriptors.enum.Enum")
    def test_validation_list_calls(self, EnumMock):
        test = "tests/backend/data_schemas/list.json"
        ifp = open(os.path.join(self.cwd, test), "r")
        dsd = DataSchemaManager(ifp)
        EnumMock.assert_called_once_with(values=["on", "off", "null"])
        data = '{"leds": ["on", "off", "null"], "id": 1}'
        validate = MagicMock(return_value=True)
        dsd.descriptors["leds"].delegate.validate = validate
        self.assertEqual(dsd.validate(data), True)
        self.assertEqual(validate.call_count, 3)

    def test_validation_dict(self):
        data = '{"motor": {"speed": 1.45, "turn_radius": 2.32}, "id": 1}'
        self.assertEqual(self.valid_schema_desc.validate(data), True)
        data = '{"motor": {"turn_radius": 2.32}, "id": 1}'
        self.assertEqual(self.valid_schema_desc.validate(data), False)
        data = """
            {
             "id": 1,
             "motor": {"speed": 1.45,
                       "turn_radius": 2.32,
                       "something": 5.55}
            }
        """
        self.assertEqual(self.valid_schema_desc.validate(data), False)
        data = '{"motor": {"speed": 1000, "turn_radius": 2.32}, "id": 1}'
        self.assertEqual(self.valid_schema_desc.validate(data), False)

    @patch("lupulo.descriptors.enum.Enum")
    @patch("lupulo.descriptors.number.Number")
    def test_validation_dict_calls(self, NumberMock, EnumMock):
        test = "tests/backend/data_schemas/dict.json"
        ifp = open(os.path.join(self.cwd, test), "r")
        dsd = DataSchemaManager(ifp)
        EnumMock.assert_called_once_with(values=[0, 3], type="enum")
        NumberMock.assert_called_once_with(range=[0, 5], type="number")
        data = '{"motor": {"speed": 4, "turn_radius": 3}, "id": 1}'
        v_enum = MagicMock(return_value=True)
        v_number = MagicMock(return_value=True)
        dsd.descriptors["motor"].delegates["speed"].validate = v_number
        dsd.descriptors["motor"].delegates["turn_radius"].validate = v_enum
        self.assertEqual(dsd.validate(data), True)
        v_enum.assert_called_once_with(3)
        v_number.assert_called_once_with(4)

    @patch("lupulo.descriptors.number.Number")
    def test_validaton_nested_list_dict(self, MockNumber):
        mock_validate = MagicMock(return_value=True)
        MockNumber().validate = mock_validate
        test = "tests/backend/data_schemas/list_dict.json"
        ifp = open(os.path.join(self.cwd, test), "r")
        dsd = DataSchemaManager(ifp)
        data = """
            {
             "id": 1,
             "motor": [{"speed": 4, "turn_radius": 3},
                       {"speed": 3, "turn_radius": 2}]
            }
        """
        dsd.validate(data)
        self.assertEqual(mock_validate.called, True)