Ejemplo n.º 1
0
def test_validate_err():
    """An erroneous Thing Description raises error on validation."""

    update_funcs = [
        lambda x: x.update({"properties": [1, 2, 3]}) or x,
        lambda x: x.update({"actions": "hello-interactions"}) or x,
        lambda x: x.update({"events": {
            "overheating": {
                "forms": 0.5
            }
        }}) or x, lambda x: x.update({"events": {
            "Invalid Name": {}
        }}) or x,
        lambda x: x.update({"events": {
            100: {
                "label": "Invalid Name"
            }
        }}) or x
    ]

    for update_func in update_funcs:
        td_err = update_func(copy.deepcopy(TD_EXAMPLE))

        with pytest.raises(InvalidDescription):
            ThingDescription.validate(doc=td_err)
Ejemplo n.º 2
0
def all_protocols_servient():
    """Returns a Servient configured to use all available protocol bindings."""

    servient = Servient(catalogue_port=None)

    http_port = find_free_port()
    http_server = HTTPServer(port=http_port)
    servient.add_server(http_server)

    ws_port = find_free_port()
    ws_server = WebsocketServer(port=ws_port)
    servient.add_server(ws_server)

    if is_coap_supported():
        from wotpy.protocols.coap.server import CoAPServer
        coap_port = find_free_port()
        coap_server = CoAPServer(port=coap_port)
        servient.add_server(coap_server)

    if is_mqtt_supported():
        from wotpy.protocols.mqtt.server import MQTTServer
        from tests.protocols.mqtt.broker import get_test_broker_url, is_test_broker_online
        if is_test_broker_online():
            mqtt_server = MQTTServer(broker_url=get_test_broker_url())
            servient.add_server(mqtt_server)

    @tornado.gen.coroutine
    def start():
        raise tornado.gen.Return((yield servient.start()))

    wot = tornado.ioloop.IOLoop.current().run_sync(start)

    td_dict = {
        "id": uuid.uuid4().urn,
        "title": uuid.uuid4().hex,
        "properties": {
            uuid.uuid4().hex: {
                "observable": True,
                "type": "string"
            }
        }
    }

    td = ThingDescription(td_dict)

    exposed_thing = wot.produce(td.to_str())
    exposed_thing.expose()

    yield servient

    @tornado.gen.coroutine
    def shutdown():
        yield servient.shutdown()

    tornado.ioloop.IOLoop.current().run_sync(shutdown)
Ejemplo n.º 3
0
    def fetch(cls, url, timeout_secs=None):
        """Accepts an url argument and returns a Future
        that resolves with a Thing Description string."""

        timeout_secs = timeout_secs or DEFAULT_FETCH_TIMEOUT_SECS

        http_client = AsyncHTTPClient()
        http_request = HTTPRequest(url, request_timeout=timeout_secs)

        http_response = yield http_client.fetch(http_request)

        td_doc = json.loads(http_response.body)
        td = ThingDescription(td_doc)

        raise tornado.gen.Return(td.to_str())
Ejemplo n.º 4
0
def client_test_invoke_action_error(servient, protocol_client_cls):
    """Helper function to test Action invocations that raise errors on bindings clients."""

    exposed_thing = next(servient.exposed_things)

    action_name = uuid.uuid4().hex

    err_message = Faker().sentence()

    # noinspection PyUnusedLocal
    def action_handler(parameters):
        raise ValueError(err_message)

    exposed_thing.add_action(action_name, ActionFragmentDict({
        "input": {"type": "number"},
        "output": {"type": "string"}
    }), action_handler)

    servient.refresh_forms()

    td = ThingDescription.from_thing(exposed_thing.thing)

    @tornado.gen.coroutine
    def test_coroutine():
        protocol_client = protocol_client_cls()

        try:
            yield protocol_client.invoke_action(td, action_name, Faker().pyint())
            raise AssertionError("Did not raise Exception")
        except Exception as ex:
            assert err_message in str(ex)

    run_test_coroutine(test_coroutine)
Ejemplo n.º 5
0
def client_test_invoke_action(servient, protocol_client_cls, timeout=None):
    """Helper function to test Action invocations on bindings clients."""

    exposed_thing = next(servient.exposed_things)

    action_name = uuid.uuid4().hex

    @tornado.gen.coroutine
    def action_handler(parameters):
        input_value = parameters.get("input")
        yield tornado.gen.sleep(random.random() * 0.1)
        raise tornado.gen.Return("{:f}".format(input_value))

    exposed_thing.add_action(action_name, ActionFragmentDict({
        "input": {"type": "number"},
        "output": {"type": "string"}
    }), action_handler)

    servient.refresh_forms()

    td = ThingDescription.from_thing(exposed_thing.thing)

    @tornado.gen.coroutine
    def test_coroutine():
        protocol_client = protocol_client_cls()

        input_value = Faker().pyint()

        result = yield protocol_client.invoke_action(td, action_name, input_value, timeout=timeout)
        result_expected = yield action_handler({"input": input_value})

        assert result == result_expected

    run_test_coroutine(test_coroutine)
Ejemplo n.º 6
0
def client_test_write_property(servient, protocol_client_cls, timeout=None):
    """Helper function to test Property writes on bindings clients."""

    exposed_thing = next(servient.exposed_things)

    prop_name = uuid.uuid4().hex

    exposed_thing.add_property(prop_name, PropertyFragmentDict({
        "type": "string",
        "observable": True
    }), value=Faker().sentence())

    servient.refresh_forms()

    td = ThingDescription.from_thing(exposed_thing.thing)

    @tornado.gen.coroutine
    def test_coroutine():
        protocol_client = protocol_client_cls()
        prop_value = Faker().sentence()

        prev_value = yield exposed_thing.properties[prop_name].read()
        assert prev_value != prop_value

        yield protocol_client.write_property(td, prop_name, prop_value, timeout=timeout)

        curr_value = yield exposed_thing.properties[prop_name].read()
        assert curr_value == prop_value

    run_test_coroutine(test_coroutine)
Ejemplo n.º 7
0
    def consume(self, td_str):
        """Accepts a thing description string argument and returns a
        ConsumedThing object instantiated based on that description."""

        td = ThingDescription(td_str)

        return ConsumedThing(servient=self._servient, td=td)
Ejemplo n.º 8
0
    def test_coroutine():
        td = ThingDescription.from_thing(exposed_thing.thing)
        event_name = next(six.iterkeys(td.events))

        future_conn = Future()
        future_event = Future()

        payload = Faker().sentence()

        def on_next(ev):
            if not future_conn.done():
                future_conn.set_result(True)
                return

            if ev.data == payload:
                future_event.set_result(True)

        subscription = subscribe_func(event_name, on_next)

        while not future_conn.done():
            yield tornado.gen.sleep(0)
            exposed_thing.emit_event(event_name, Faker().sentence())

        exposed_thing.emit_event(event_name, payload)

        yield future_event

        assert future_event.result()

        subscription.dispose()
Ejemplo n.º 9
0
async def expose_proxy(wot, consumed_thing):
    """Takes a Consumed Thing and exposes an Exposed Thing that acts as a proxy."""

    description = {
        "id": THING_ID,
        "name": "Thing Proxy: {}".format(consumed_thing.name)
    }

    td_dict = consumed_thing.td.to_dict()

    for intrct_key in ['properties', 'actions', 'events']:
        description.update({intrct_key: td_dict.get(intrct_key, {})})

    exposed_thing = wot.produce(json.dumps(description))

    for name in six.iterkeys(description.get('properties')):
        exposed_thing.set_property_read_handler(
            name, build_prop_read_proxy(consumed_thing, name))
        exposed_thing.set_property_write_handler(
            name, build_prop_write_proxy(consumed_thing, name))

    for name in six.iterkeys(description.get('actions')):
        exposed_thing.set_action_handler(
            name, build_action_invoke_proxy(consumed_thing, name))

    for name in six.iterkeys(description.get('events')):
        subscribe_event(consumed_thing, exposed_thing, name)

    exposed_thing.expose()

    logger.info("Exposed Thing proxy TD:\n{}".format(
        pprint.pformat(
            ThingDescription.from_thing(exposed_thing.thing).to_dict())))

    return exposed_thing
Ejemplo n.º 10
0
def test_build_thing():
    """Thing objects can be built from ThingDescription objects."""

    json_td = ThingDescription(TD_EXAMPLE)
    thing = json_td.build_thing()
    td_dict = json_td.to_dict()

    def assert_same_keys(dict_a, dict_b):
        assert sorted(list(dict_a.keys())) == sorted(list(dict_b.keys()))

    assert thing.id == td_dict.get("id")
    assert thing.title == td_dict.get("title")
    assert thing.description == td_dict.get("description")
    assert_same_keys(thing.properties, td_dict.get("properties", {}))
    assert_same_keys(thing.actions, td_dict.get("actions", {}))
    assert_same_keys(thing.events, td_dict.get("events", {}))
Ejemplo n.º 11
0
    def test_coroutine():
        td = ThingDescription.from_thing(exposed_thing.thing)
        prop_name = next(six.iterkeys(td.properties))

        future_conn = Future()
        future_change = Future()

        prop_value = Faker().sentence()

        def on_next(ev):
            if not future_conn.done():
                future_conn.set_result(True)
                return

            if ev.data.value == prop_value:
                future_change.set_result(True)

        subscription = subscribe_func(prop_name, on_next)

        while not future_conn.done():
            yield tornado.gen.sleep(0)
            yield exposed_thing.write_property(prop_name, Faker().sentence())

        yield exposed_thing.write_property(prop_name, prop_value)

        yield future_change

        assert future_change.result()

        subscription.dispose()
Ejemplo n.º 12
0
def consumed_exposed_pair():
    """Returns a dict with two keys:
    * consumed_thing: A ConsumedThing instance. The Servient instance that contains this
    ConsumedThing has been patched to use the ExposedThingProxyClient Protocol Binding client.
    * exposed_thing: The ExposedThing behind the previous ConsumedThing (for assertion purposes)."""

    servient = Servient()

    exp_thing = ExposedThing(servient=servient,
                             thing=Thing(id=uuid.uuid4().urn))

    servient.select_client = MagicMock(
        return_value=ExposedThingProxyClient(exp_thing))

    @tornado.gen.coroutine
    def lower(parameters):
        input_value = parameters.get("input")
        yield tornado.gen.sleep(0)
        raise tornado.gen.Return(str(input_value).lower())

    exp_thing.add_property(uuid.uuid4().hex, _build_property_fragment())
    exp_thing.add_action(uuid.uuid4().hex, _build_action_fragment(), lower)
    exp_thing.add_event(uuid.uuid4().hex, _build_event_fragment())

    td = ThingDescription.from_thing(exp_thing.thing)

    return {
        "consumed_thing": ConsumedThing(servient=servient, td=td),
        "exposed_thing": exp_thing
    }
Ejemplo n.º 13
0
    def test_coroutine():
        wot_01 = yield servient_01.start()
        wot_02 = yield servient_02.start()

        wot_01.produce(ThingFragment(TD_DICT_01)).expose()
        wot_01.produce(ThingFragment(TD_DICT_02)).expose()

        thing_filter = ThingFilterDict(method=DiscoveryMethod.MULTICAST)

        observable = wot_02.discover(thing_filter,
                                     dnssd_find_kwargs={
                                         "min_results": 1,
                                         "timeout": 5
                                     })

        subscription = observable.subscribe(
            on_next=lambda td_str: found.append(ThingDescription(td_str)
                                                ) or resolve())

        yield future_done

        assert_equal_td_sequences(found, [TD_DICT_01, TD_DICT_02])

        subscription.dispose()

        yield servient_01.shutdown()
        yield servient_02.shutdown()
Ejemplo n.º 14
0
def test_from_dict():
    """ThingDescription objects can be built from TD documents in dict format."""

    td = ThingDescription(TD_EXAMPLE)

    assert td.id == TD_EXAMPLE.get("id")
    assert td.title == TD_EXAMPLE.get("title")
    assert td.description == TD_EXAMPLE.get("description")
Ejemplo n.º 15
0
    def get_property(prop_name):
        """Gets the given property using the WS Link contained in the thing description."""

        td = ThingDescription.from_thing(exposed_thing.thing)
        consumed_thing = ConsumedThing(servient, td=td)

        value = yield consumed_thing.read_property(prop_name)

        raise tornado.gen.Return(value)
Ejemplo n.º 16
0
    def _is_fragment_match(cls, item, thing_filter):
        """Returns True if the given item (an ExposedThing, Thing or TD)
        matches the fragment in the given Thing filter."""

        td = None

        if isinstance(item, ExposedThing):
            td = ThingDescription.from_thing(item.thing)
        elif isinstance(item, Thing):
            td = ThingDescription.from_thing(item)
        elif isinstance(item, ThingDescription):
            td = item

        assert td

        fragment_dict = thing_filter.fragment if thing_filter.fragment else {}

        return all(item in six.iteritems(td.to_dict())
                   for item in six.iteritems(fragment_dict))
Ejemplo n.º 17
0
    def get(self, thing_url_name):
        exp_thing = self.servient.exposed_thing_set.find_by_thing_id(
            thing_url_name)

        td_doc = ThingDescription.from_thing(exp_thing.thing).to_dict()
        base_url = self.servient.get_thing_base_url(exp_thing)

        if base_url:
            td_doc.update({"base": base_url})

        self.write(td_doc)
Ejemplo n.º 18
0
    def _build_local_discover_observable(self, thing_filter):
        """Builds an Observable to discover Things using the local method."""

        found_tds = [
            ThingDescription.from_thing(exposed_thing.thing).to_str()
            for exposed_thing in self._servient.exposed_things
            if self._is_fragment_match(exposed_thing, thing_filter)
        ]

        # noinspection PyUnresolvedReferences
        return Observable.of(*found_tds)
Ejemplo n.º 19
0
def test_clients_subset():
    """Although all clients are enabled by default, the user may only enable a subset."""

    ws_client = WebsocketClient()
    servient_01 = Servient()
    servient_02 = Servient(clients=[ws_client])
    td = ThingDescription(TD_DICT_01)
    prop_name = next(six.iterkeys(TD_DICT_01["properties"]))

    assert servient_01.select_client(td, prop_name) is not ws_client
    assert servient_02.select_client(td, prop_name) is ws_client
Ejemplo n.º 20
0
def test_all_protocols_combined(all_protocols_servient):
    """Protocol bindings work as expected when multiple
    servers are combined within the same Servient."""

    exposed_thing = next(all_protocols_servient.exposed_things)
    td = ThingDescription.from_thing(exposed_thing.thing)

    clients = [WebsocketClient(), HTTPClient()]

    if is_coap_supported():
        from wotpy.protocols.coap.client import CoAPClient
        clients.append(CoAPClient())

    if is_mqtt_supported():
        from tests.protocols.mqtt.broker import is_test_broker_online
        from wotpy.protocols.mqtt.client import MQTTClient
        if is_test_broker_online():
            clients.append(MQTTClient())

    prop_name = next(six.iterkeys(td.properties))

    @tornado.gen.coroutine
    def read_property(the_client):
        prop_value = Faker().sentence()

        curr_value = yield the_client.read_property(td, prop_name)
        assert curr_value != prop_value

        yield exposed_thing.properties[prop_name].write(prop_value)

        curr_value = yield the_client.read_property(td, prop_name)
        assert curr_value == prop_value

    @tornado.gen.coroutine
    def write_property(the_client):
        updated_value = Faker().sentence()

        curr_value = yield exposed_thing.properties[prop_name].read()
        assert curr_value != updated_value

        yield the_client.write_property(td, prop_name, updated_value)

        curr_value = yield exposed_thing.properties[prop_name].read()
        assert curr_value == updated_value

    @tornado.gen.coroutine
    def test_coroutine():
        for client in clients:
            yield read_property(client)
            yield write_property(client)

    run_test_coroutine(test_coroutine)
Ejemplo n.º 21
0
    def test_coroutine():
        thing_filter = ThingFilterDict(method=DiscoveryMethod.LOCAL)
        observable = wot.discover(thing_filter)

        subscription = observable.subscribe(
            on_next=lambda td_str: found.append(ThingDescription(td_str)
                                                ) or resolve())

        yield future_done

        assert_equal_td_sequences(found, [TD_DICT_01, TD_DICT_02])

        subscription.dispose()
Ejemplo n.º 22
0
def assert_equal_tds(one, other):
    """Asserts that both TDs are equal."""

    one = ThingDescription(one) if not isinstance(one,
                                                  ThingDescription) else one
    other = ThingDescription(other) if not isinstance(
        other, ThingDescription) else other
    assert one.to_dict() == other.to_dict()
Ejemplo n.º 23
0
            def callback():
                address_port_pairs = yield self._servient.dnssd.find(
                    **dnssd_find_kwargs)

                def build_pair_url(idx, path=None):
                    addr, port = address_port_pairs[idx]
                    base = "http://{}:{}".format(addr, port)
                    path = path if path else ''
                    return "{}/{}".format(base, path.strip("/"))

                http_client = AsyncHTTPClient()

                catalogue_resps = [
                    http_client.fetch(build_pair_url(idx))
                    for idx in range(len(address_port_pairs))
                ]

                wait_iter = tornado.gen.WaitIterator(*catalogue_resps)

                while not wait_iter.done() and not state["stop"]:
                    try:
                        catalogue_resp = yield wait_iter.next()
                    except Exception as ex:
                        self._logr.warning(
                            "Exception on HTTP request to TD catalogue: {}".
                            format(ex))
                    else:
                        catalogue = json.loads(catalogue_resp.body)

                        if state["stop"]:
                            return

                        td_resps = yield [
                            http_client.fetch(
                                build_pair_url(wait_iter.current_index,
                                               path=path))
                            for thing_id, path in six.iteritems(catalogue)
                        ]

                        tds = [
                            ThingDescription(td_resp.body)
                            for td_resp in td_resps
                        ]

                        tds_filtered = [
                            td for td in tds
                            if self._is_fragment_match(td, thing_filter)
                        ]

                        [observer.on_next(td.to_str()) for td in tds_filtered]
Ejemplo n.º 24
0
def test_read_property_unknown(websocket_servient):
    """The Websockets client raises an error when attempting to read an unknown property."""

    exposed_thing = next(websocket_servient.exposed_things)
    td = ThingDescription.from_thing(exposed_thing.thing)

    @tornado.gen.coroutine
    def test_coroutine():
        ws_client = WebsocketClient()

        with pytest.raises(ProtocolClientException):
            yield ws_client.read_property(td, uuid.uuid4().hex)

    run_test_coroutine(test_coroutine)
Ejemplo n.º 25
0
        def discover_first():
            observable = wot.discover(thing_filter)

            subscription = observable.subscribe(
                on_next=lambda td_str: found.append(ThingDescription(td_str)
                                                    ) or resolve())

            yield future_done

            subscription.dispose()

            assert len(found)

            raise tornado.gen.Return(found[0])
Ejemplo n.º 26
0
def client_test_on_property_change(servient, protocol_client_cls):
    """Helper function to test observation of Property updates on bindings clients."""

    exposed_thing = next(servient.exposed_things)

    prop_name = uuid.uuid4().hex

    exposed_thing.add_property(prop_name, PropertyFragmentDict({
        "type": "string",
        "observable": True
    }), value=Faker().sentence())

    servient.refresh_forms()

    td = ThingDescription.from_thing(exposed_thing.thing)

    @tornado.gen.coroutine
    def test_coroutine():
        protocol_client = protocol_client_cls()

        values = [Faker().sentence() for _ in range(10)]
        values_observed = {value: tornado.concurrent.Future() for value in values}

        @tornado.gen.coroutine
        def write_next():
            try:
                next_value = next(val for val, fut in six.iteritems(values_observed) if not fut.done())
                yield exposed_thing.properties[prop_name].write(next_value)
            except StopIteration:
                pass

        def on_next(ev):
            prop_value = ev.data.value
            if prop_value in values_observed and not values_observed[prop_value].done():
                values_observed[prop_value].set_result(True)

        observable = protocol_client.on_property_change(td, prop_name)

        subscription = observable.subscribe_on(IOLoopScheduler()).subscribe(on_next)

        periodic_emit = tornado.ioloop.PeriodicCallback(write_next, 10)
        periodic_emit.start()

        yield list(values_observed.values())

        periodic_emit.stop()
        subscription.dispose()

    run_test_coroutine(test_coroutine)
Ejemplo n.º 27
0
def client_test_on_property_change_error(servient, protocol_client_cls):
    """Helper function to test propagation of errors raised
    during observation of Property updates on bindings clients."""

    exposed_thing = next(servient.exposed_things)

    prop_name = uuid.uuid4().hex

    exposed_thing.add_property(prop_name, PropertyFragmentDict({
        "type": "string",
        "observable": True
    }), value=Faker().sentence())

    servient.refresh_forms()

    td = ThingDescription.from_thing(exposed_thing.thing)

    @tornado.gen.coroutine
    def test_coroutine():
        protocol_client = protocol_client_cls()

        yield servient.shutdown()

        future_err = tornado.concurrent.Future()

        # noinspection PyUnusedLocal
        def on_next(item):
            future_err.set_exception(Exception("Should not have emitted any items"))

        def on_error(err):
            future_err.set_result(err)

        observable = protocol_client.on_property_change(td, prop_name)

        subscribe_kwargs = {
            "on_next": on_next,
            "on_error": on_error
        }

        subscription = observable.subscribe_on(IOLoopScheduler()).subscribe(**subscribe_kwargs)

        observe_err = yield future_err

        assert isinstance(observe_err, Exception)

        subscription.dispose()

    run_test_coroutine(test_coroutine)
Ejemplo n.º 28
0
    def get(self):
        response = {}

        for exp_thing in self.servient.enabled_exposed_things:
            thing_id = exp_thing.thing.id

            if self.get_argument("expanded", False):
                val = ThingDescription.from_thing(exp_thing.thing).to_dict()
                val.update(
                    {"base": self.servient.get_thing_base_url(exp_thing)})
            else:
                val = "/{}".format(exp_thing.thing.url_name)

            response[thing_id] = val

        self.write(response)
Ejemplo n.º 29
0
    def thing_from_model(cls, model):
        """Takes a ThingModel and builds a Thing. 
        Raises if the model has an unexpected type."""

        expected_types = (six.string_types, ThingFragment, ConsumedThing)

        if not isinstance(model, expected_types):
            raise ValueError("Expected one of: {}".format(expected_types))

        if isinstance(model, six.string_types):
            thing = ThingDescription(doc=model).build_thing()
        elif isinstance(model, ThingFragment):
            thing = Thing(thing_fragment=model)
        else:
            thing = model.td.build_thing()

        return thing
Ejemplo n.º 30
0
def test_from_thing():
    """ThingDescription objects can be built from Thing objects."""

    fake = Faker()

    thing_id = uuid.uuid4().urn
    action_id = uuid.uuid4().hex
    prop_id = uuid.uuid4().hex
    event_id = uuid.uuid4().hex
    action_form_href = fake.url()
    prop_form_href = fake.url()

    thing = Thing(id=thing_id)

    action = Action(thing=thing, name=action_id)
    action_form = Form(interaction=action,
                       protocol=Protocols.HTTP,
                       href=action_form_href)
    action.add_form(action_form)
    thing.add_interaction(action)

    prop = Property(thing=thing, name=prop_id, type="string")
    prop_form = Form(interaction=prop,
                     protocol=Protocols.HTTP,
                     href=prop_form_href)
    prop.add_form(prop_form)
    thing.add_interaction(prop)

    event = Event(thing=thing, name=event_id)
    thing.add_interaction(event)

    json_td = ThingDescription.from_thing(thing)
    td_dict = json_td.to_dict()

    assert td_dict["id"] == thing.id
    assert td_dict["title"] == thing.title
    assert len(td_dict["properties"]) == 1
    assert len(td_dict["actions"]) == 1
    assert len(td_dict["events"]) == 1
    assert len(td_dict["actions"][action_id]["forms"]) == 1
    assert len(td_dict["properties"][prop_id]["forms"]) == 1
    assert td_dict["actions"][action_id]["forms"][0][
        "href"] == action_form_href
    assert td_dict["properties"][prop_id]["forms"][0]["href"] == prop_form_href