def test_EntryNewRemote(nt_live, server_cb):
    nt_server, nt_client = nt_live
    nt_server_api = nt_server._api
    nt_client_api = nt_client._api

    nt_server_api.addEntryListenerById(nt_server_api.getEntryId("/foo"),
                                       server_cb, NT_NOTIFY_NEW)

    # Trigger an event
    nt_client_api.setEntryValueById(nt_client_api.getEntryId("/foo/bar"),
                                    Value.makeDouble(2.0))
    nt_client_api.setEntryValueById(nt_client_api.getEntryId("/foo"),
                                    Value.makeDouble(1.0))

    nt_client_api.flush()

    assert nt_server_api.waitForEntryListenerQueue(1.0)

    # Check the event
    events = server_cb.wait(1)

    # assert events[0].listener == handle
    assert events[0].local_id == nt_server_api.getEntryId("/foo")
    assert events[0].name == "/foo"
    assert events[0].value == Value.makeDouble(1.0)
    assert events[0].flags == NT_NOTIFY_NEW
示例#2
0
    def set_safe(self, name: str, value: NT_TYPES) -> None:
        entry = self._get_entry(name)

        # Calculated using Python type (bool, str, float)
        try:
            create_value_func: Callable[..., Value] = Value.getFactory(value)
        except ValueError as e:  # getFactory raises ValueError when it should be raising TypeError
            raise TypeError(*e.args)

        if entry is not None:
            # Calculated using NT type (NT_BOOLEAN, NT_STRING, NT_DOUBLE)
            if Value.getFactoryByType(entry.type) != create_value_func:
                # The factories don't match, which means the types don't match
                # Do not allow implicit type conversion
                raise TypeError("Existing type {} does not match: {}".format(
                    entry.type, type(value)))

        # Convert Python type into NT type
        nt_value: Value = create_value_func(value)

        # Returns False on error (type mismatch)
        successful = self.api.setEntryValue(self._get_path(name), nt_value)

        if not successful:
            raise TypeError("Existing type {} does not match: {}".format(
                entry.type, type(value)))
示例#3
0
def test_Raw():
    v = Value.makeRaw(b"hello")
    assert NT_RAW == v.type
    assert b"hello" == v.value

    v = Value.makeRaw(b"goodbye")
    assert NT_RAW == v.type
    assert b"goodbye" == v.value
示例#4
0
def test_String():
    v = Value.makeString("hello")
    assert NT_STRING == v.type
    assert "hello" == v.value

    v = Value.makeString("goodbye")
    assert NT_STRING == v.type
    assert "goodbye" == v.value
示例#5
0
def test_Boolean():
    v = Value.makeBoolean(False)
    assert NT_BOOLEAN == v.type
    assert not v.value

    v = Value.makeBoolean(True)
    assert NT_BOOLEAN == v.type
    assert v.value
示例#6
0
def test_Double():
    v = Value.makeDouble(0.5)
    assert NT_DOUBLE == v.type
    assert 0.5 == v.value

    v = Value.makeDouble(0.25)
    assert NT_DOUBLE == v.type
    assert 0.25 == v.value
示例#7
0
def test_StringComparison():
    v1 = Value.makeString("hello")
    v2 = Value.makeString("hello")
    assert v1 == v2
    v2 = Value.makeString("world")
    # different contents
    assert v1 != v2
    v2 = Value.makeString("goodbye")
    # different size
    assert v1 != v2
示例#8
0
def test_DoubleArrayComparison():
    v1 = Value.makeDoubleArray([0.5, 0.25, 0.5])
    v2 = Value.makeDoubleArray((0.5, 0.25, 0.5))
    assert v1 == v2

    # different contents
    v2 = Value.makeDoubleArray([0.5, 0.5, 0.5])
    assert v1 != v2

    # different size
    v2 = Value.makeDoubleArray([0.5, 0.25])
    assert v1 != v2
示例#9
0
def test_BooleanArrayComparison():
    v1 = Value.makeBooleanArray([1, 0, 1])
    v2 = Value.makeBooleanArray((1, 0, 1))
    assert v1 == v2

    # different contents
    v2 = Value.makeBooleanArray([1, 1, 1])
    assert v1 != v2

    # different size
    v2 = Value.makeBooleanArray([True, False])
    assert v1 != v2
示例#10
0
def test_DeleteEntryExist(storage_populated, dispatcher, entry_notifier,
                          is_server):
    storage = storage_populated

    storage.deleteEntry("foo2")

    entry = storage.m_entries.get("foo2")
    assert entry is not None
    assert entry.value is None
    assert entry.id == 0xFFFF
    assert entry.local_write == False

    # client shouldn't send an update as id not assigned yet
    if is_server:
        # id assigned as this is the server
        dispatcher._queueOutgoing.assert_has_calls(
            [call(Message.entryDelete(1), None, None)])
    else:
        # shouldn't send an update id not assigned yet
        assert dispatcher._queueOutgoing.call_count == 0

    entry_notifier.notifyEntry.assert_has_calls([
        call(1, "foo2", Value.makeDouble(0.0),
             NT_NOTIFY_DELETE | NT_NOTIFY_LOCAL)
    ])

    if is_server:
        assert len(storage.m_idmap) >= 2
        assert not storage.m_idmap[1]
def generateNotifications(notifier):
    # All flags combos that can be generated by Storage
    flags = [
        # "normal" notifications
        NT_NOTIFY_NEW,
        NT_NOTIFY_DELETE,
        NT_NOTIFY_UPDATE,
        NT_NOTIFY_FLAGS,
        NT_NOTIFY_UPDATE | NT_NOTIFY_FLAGS,
        # immediate notifications are always "new"
        NT_NOTIFY_IMMEDIATE | NT_NOTIFY_NEW,
        # local notifications can be of any flag combo
        NT_NOTIFY_LOCAL | NT_NOTIFY_NEW,
        NT_NOTIFY_LOCAL | NT_NOTIFY_DELETE,
        NT_NOTIFY_LOCAL | NT_NOTIFY_UPDATE,
        NT_NOTIFY_LOCAL | NT_NOTIFY_FLAGS,
        NT_NOTIFY_LOCAL | NT_NOTIFY_UPDATE | NT_NOTIFY_FLAGS,
    ]

    # Generate across keys
    keys = ["/foo/bar", "/baz", "/boo"]

    val = Value.makeDouble(1)

    # Provide unique key indexes for each key
    keyindex = 5
    for key in keys:
        for flag in flags:
            notifier.notifyEntry(keyindex, key, val, flag)

        keyindex += 1
示例#12
0
def test_StringArrayComparison():
    v1 = Value.makeStringArray(["hello", "goodbye", "string"])
    v2 = Value.makeStringArray(("hello", "goodbye", "string"))
    assert v1 == v2

    # different contents
    v2 = Value.makeStringArray(["hello", "goodby2", "string"])
    assert v1 != v2

    # different sized contents
    v2 = Value.makeStringArray(["hello", "goodbye2", "string"])
    assert v1 != v2

    # different size
    v2 = Value.makeStringArray(["hello", "goodbye"])
    assert v1 != v2
示例#13
0
def storage_populated(storage_empty, dispatcher, entry_notifier):
    storage = storage_empty

    entry_notifier.m_local_notifiers = False

    entry_notifier.m_local_notifiers = False
    storage.setEntryTypeValue("foo", Value.makeBoolean(True))
    storage.setEntryTypeValue("foo2", Value.makeDouble(0.0))
    storage.setEntryTypeValue("bar", Value.makeDouble(1.0))
    storage.setEntryTypeValue("bar2", Value.makeBoolean(False))

    dispatcher.reset_mock()
    entry_notifier.reset_mock()
    entry_notifier.m_local_notifiers = True

    return storage
示例#14
0
def test_LoadPersistentUpdateValueFlags(storage_populated, dispatcher,
                                        entry_notifier, is_server):
    storage = storage_populated

    fp = StringIO('[NetworkTables Storage 3.0]\ndouble "foo2"=1.0\n')
    assert storage.loadPersistent(fp=fp) is None

    entry = storage.m_entries.get("foo2")
    assert Value.makeDouble(1.0) == entry.value
    assert NT_PERSISTENT == entry.flags
    assert entry.isPersistent

    # client shouldn't send an update as id not assigned yet
    if is_server:
        # id assigned as this is the server; seq_num incremented
        dispatcher._queueOutgoing.assert_has_calls([
            call(Message.entryUpdate(1, 2, entry.value), None, None),
            call(Message.flagsUpdate(1, NT_PERSISTENT), None, None),
        ])
    else:
        assert dispatcher._queueOutgoing.call_count == 0

    entry_notifier.notifyEntry.assert_has_calls([
        call(
            1,
            "foo2",
            entry.value,
            NT_NOTIFY_FLAGS | NT_NOTIFY_UPDATE | NT_NOTIFY_LOCAL,
        )
    ])

    if not is_server:
        assert 2 == storage.m_entries.get(
            "foo2").seq_num  # still should be incremented
示例#15
0
def test_SetEntryTypeValueAssignNew(storage_empty, dispatcher, entry_notifier,
                                    is_server):
    storage = storage_empty

    # brand new entry
    value = Value.makeBoolean(True)
    storage.setEntryTypeValue("foo", value)
    assert value == storage.m_entries.get("foo").value

    dispatcher._queueOutgoing.assert_has_calls([
        call(
            Message.entryAssign("foo", 0 if is_server else 0xFFFF, 1, value,
                                0),
            None,
            None,
        )
    ])
    entry_notifier.notifyEntry.assert_has_calls(
        [call(0, "foo", value, NT_NOTIFY_NEW | NT_NOTIFY_LOCAL)])

    if is_server:
        assert 1 == len(storage.m_idmap)
        assert value == storage.m_idmap[0].value
    else:
        assert len(storage.m_idmap) == 0
示例#16
0
def test_GetEntryValueExist(storage_empty, dispatcher, entry_notifier):
    storage = storage_empty

    value = Value.makeBoolean(True)
    storage.setEntryTypeValue("foo", value)
    assert dispatcher._queueOutgoing.call_count == 1
    assert entry_notifier.notifyEntry.call_count == 1

    assert value == storage.getEntryValue("foo")
示例#17
0
def test_ProcessIncomingEntryAssignIgnore(storage_populate_one, dispatcher,
                                          entry_notifier, is_server, conn):
    storage = storage_populate_one
    value = Value.makeDouble(1.0)

    storage.processIncoming(Message.entryAssign("foo", 0xFFFF, 1, value, 0),
                            conn)

    assert dispatcher._queueOutgoing.call_count == 0
    assert entry_notifier.notifyEntry.call_count == 0
示例#18
0
    def set(self, name: str, value: NT_TYPES) -> None:
        # Calculated using Python type (bool, str, float)
        try:
            create_value_func: Callable[..., Value] = Value.getFactory(value)
        except ValueError as e:  # getFactory raises ValueError when it should be raising TypeError
            raise TypeError(*e.args)

        nt_value: Value = create_value_func(value)

        self.api.setEntryTypeValue(self._get_path(name), nt_value)
示例#19
0
def test_DeleteCheckHandle(storage_populate_one, dispatcher, entry_notifier,
                           is_server):
    storage = storage_populate_one

    handle = storage.getEntryId("foo")
    storage.deleteEntry("foo")
    storage.setEntryTypeValue("foo", Value.makeBoolean(True))

    handle2 = storage.getEntryId("foo")
    assert handle == handle2
示例#20
0
def test_SetEntryValueEmptyName(storage_empty, dispatcher, entry_notifier):
    storage = storage_empty

    value = Value.makeBoolean(True)
    assert storage.setEntryValue("", value)
    assert len(storage.m_entries) == 0
    assert len(storage.m_idmap) == 0

    assert dispatcher._queueOutgoing.call_count == 0
    assert entry_notifier.notifyEntry.call_count == 0
示例#21
0
def test_SetDefaultEntryExistsSameType(storage_populate_one, dispatcher,
                                       entry_notifier):
    storage = storage_populate_one

    # existing entry
    value = Value.makeBoolean(False)
    ret_val = storage.setDefaultEntryValue("foo", value)
    assert ret_val
    assert value != storage.m_entries.get("foo").value

    assert dispatcher._queueOutgoing.call_count == 0
    assert entry_notifier.notifyEntry.call_count == 0
示例#22
0
def test_SetEntryValueAssignTypeChange(storage_populate_one, dispatcher,
                                       entry_notifier):
    storage = storage_populate_one

    # update with different type results in error and no message
    value = Value.makeDouble(0.0)
    assert not storage.setEntryValue("foo", value)
    entry = storage.m_entries.get("foo")
    assert value != entry.value

    assert dispatcher._queueOutgoing.call_count == 0
    assert entry_notifier.notifyEntry.call_count == 0
示例#23
0
def test_SetEntryTypeValueEqualValue(storage_populate_one, dispatcher,
                                     entry_notifier):
    storage = storage_populate_one

    # update with same type and same value: change value contents but no update
    # message is issued (minimizing bandwidth usage)
    value = Value.makeBoolean(True)
    storage.setEntryTypeValue("foo", value)
    assert value == storage.m_entries.get("foo").value

    assert dispatcher._queueOutgoing.call_count == 0
    assert entry_notifier.notifyEntry.call_count == 0
示例#24
0
def storage_populate_one(storage_empty, dispatcher, entry_notifier):
    storage = storage_empty

    entry_notifier.m_local_notifiers = False

    storage.setEntryTypeValue("foo", Value.makeBoolean(True))

    dispatcher.reset_mock()
    entry_notifier.reset_mock()
    entry_notifier.m_local_notifiers = True

    return storage
示例#25
0
def test_SetDefaultEntryExistsDifferentType(storage_populate_one, dispatcher,
                                            entry_notifier):
    storage = storage_populate_one

    # existing entry is boolean
    value = Value.makeDouble(2.0)
    ret_val = storage.setDefaultEntryValue("foo", value)
    assert not ret_val
    # should not have updated value in table if it already existed.
    assert value != storage.m_entries.get("foo").value

    assert dispatcher._queueOutgoing.call_count == 0
    assert entry_notifier.notifyEntry.call_count == 0
def test_PrefixNewLocal(nt_live, server_cb):
    nt_server, nt_client = nt_live
    nt_server_api = nt_server._api

    nt_server_api.addEntryListener("/foo", server_cb,
                                   NT_NOTIFY_NEW | NT_NOTIFY_LOCAL)

    # Trigger an event
    nt_server_api.setEntryValueById(nt_server_api.getEntryId("/foo/bar"),
                                    Value.makeDouble(1.0))
    nt_server_api.setEntryValueById(nt_server_api.getEntryId("/baz"),
                                    Value.makeDouble(1.0))

    assert nt_server_api.waitForEntryListenerQueue(1.0)

    events = server_cb.wait(1)

    # assert events[0].listener == handle
    assert events[0].local_id == nt_server_api.getEntryId("/foo/bar")
    assert events[0].name == "/foo/bar"
    assert events[0].value == Value.makeDouble(1.0)
    assert events[0].flags == NT_NOTIFY_NEW | NT_NOTIFY_LOCAL
示例#27
0
def test_empty_SetDefaultEntryEmptyName(storage_empty, dispatcher,
                                        entry_notifier):
    storage = storage_empty

    value = Value.makeBoolean(True)
    ret_val = storage.setDefaultEntryValue("", value)
    assert not ret_val
    assert "foo" not in storage.m_entries

    assert len(storage.m_entries) == 0
    assert len(storage.m_idmap) == 0

    assert dispatcher._queueOutgoing.call_count == 0
    assert entry_notifier.notifyEntry.call_count == 0
def test_PollPrefixBasic(notifier):
    poller = notifier.createPoller()
    g1 = notifier.addPolled(poller, "/foo", NT_NOTIFY_NEW)
    g2 = notifier.addPolled(poller, "/foo", NT_NOTIFY_DELETE)
    g3 = notifier.addPolled(poller, "/foo", NT_NOTIFY_UPDATE)
    g4 = notifier.addPolled(poller, "/foo", NT_NOTIFY_FLAGS)
    notifier.addPolled(poller, "/bar", NT_NOTIFY_NEW)
    notifier.addPolled(poller, "/bar", NT_NOTIFY_DELETE)
    notifier.addPolled(poller, "/bar", NT_NOTIFY_UPDATE)
    notifier.addPolled(poller, "/bar", NT_NOTIFY_FLAGS)

    assert not notifier.m_local_notifiers

    generateNotifications(notifier)

    assert notifier.waitForQueue(1.0)

    results, timed_out = notifier.poll(poller, 0)
    assert not timed_out

    g1count = 0
    g2count = 0
    g3count = 0
    g4count = 0
    for h, result in results:
        print(result)
        assert result.name.startswith("/foo")
        assert result.value == Value.makeDouble(1)

        if h == g1:
            g1count += 1
            assert (result.flags & NT_NOTIFY_NEW) != 0
        elif h == g2:
            g2count += 1
            assert (result.flags & NT_NOTIFY_DELETE) != 0
        elif h == g3:
            g3count += 1
            assert (result.flags & NT_NOTIFY_UPDATE) != 0
        elif h == g4:
            g4count += 1
            assert (result.flags & NT_NOTIFY_FLAGS) != 0
        else:
            assert False, "unknown listener index"

    assert g1count == 2
    assert g2count == 1  # NT_NOTIFY_DELETE
    assert g3count == 2
    assert g4count == 2
示例#29
0
def test_ProcessIncomingEntryAssign1(storage_populate_one, dispatcher,
                                     entry_notifier, is_server, conn):
    storage = storage_populate_one
    value = Value.makeDouble(1.0)

    storage.processIncoming(Message.entryAssign("foo", 0, 1, value, 0), conn)

    # EXPECT_CALL(*conn, proto_rev()).WillRepeatedly(Return(0x0300u))
    if is_server:
        # server broadcasts new value to all *other* connections
        dispatcher._queueOutgoing.assert_has_calls(
            [call(Message.entryAssign("foo", 0, 1, value, 0), None, conn)])
    else:
        assert dispatcher._queueOutgoing.call_count == 0

    entry_notifier.notifyEntry.assert_has_calls(
        [call(0, "foo", value, NT_NOTIFY_UPDATE)])
示例#30
0
def test_populated_SetDefaultEntryEmptyName(storage_populated, dispatcher,
                                            entry_notifier, is_server):
    storage = storage_populated

    value = Value.makeBoolean(True)
    ret_val = storage.setDefaultEntryValue("", value)
    assert not ret_val
    # assert that no entries get added
    assert 4 == len(storage.m_entries)
    if is_server:
        assert 4 == len(storage.m_idmap)

    else:
        assert 0 == len(storage.m_idmap)

    assert dispatcher._queueOutgoing.call_count == 0
    assert entry_notifier.notifyEntry.call_count == 0