def test_unsubscribe_options(self): """ Test a variety of valid and invalid options values. Invalid options should result in the client.unsubscribe(...) method throwing a TypeError. Note that this test just checks that the options parameter is only accepted when it is of the correct type. The actual validation of individual options will be in separate tests. """ test_is_done = threading.Event() func = self.func004 data = [ {'valid': False, 'pattern' : 'TestEmpty', 'options': ''}, {'valid': True, 'pattern' : 'TestNone', 'options': None}, {'valid': False, 'pattern' : 'TestFunc', 'options': func}, {'valid': False, 'pattern' : 'TestString', 'options': '1'}, {'valid': False, 'pattern' : 'TestNumber', 'options': 2}, {'valid': False, 'pattern' : 'TestBoolean', 'options': True}, {'valid': True, 'pattern' : 'TestEmptyList','options': {}}, {'valid': True, 'pattern' : 'TestList', 'options': {'a': 1}}, ] def started(client): """started listener""" try: for test in data: if test['valid']: test_options = test['options'] def on_subscribed(err,topic_pattern, share): client.unsubscribe(topic_pattern, 'share', options=test['options'], on_unsubscribed=func) sub_test_is_done.set() sub_test_is_done = threading.Event() client.subscribe( test['pattern'], 'share', on_subscribed=on_subscribed) sub_test_is_done.wait(self.TEST_TIMEOUT) else: with pytest.raises(TypeError): client.unsubscribe(test['pattern'], 'share', options=test['options'], on_unsubscribed=func) except Exception as exc: pytest.fail('Unexpected Exception ' + str(exc)) finally: client.stop() test_is_done.set() client = mqlight.Client( 'amqp://host', 'test_unsubscribe_options', on_started=started) test_is_done.wait(self.TEST_TIMEOUT) assert test_is_done.is_set()
def test_unsubscribe_ttl_validity(self): """ Test a variety of valid and invalid ttl options. Invalid ttl values should result in the client.unsubscribe(...) method throwing a TypeError. """ test_is_done = threading.Event() func = self.func004 data = [ {'valid': True, 'pattern': 'TTL0', 'ttl': 0}, {'valid': False, 'pattern': 'TTLNone', 'ttl': None}, {'valid': False, 'pattern': 'TTLfunc', 'ttl': func}, {'valid': False, 'pattern': 'TTLNegLarge','ttl': -9007199254740992}, {'valid': False, 'pattern': 'TTLNegNan', 'ttl': float('-nan')}, {'valid': False, 'pattern': 'TTLPosNan', 'ttl': float('nan')}, {'valid': False, 'pattern': 'TTLNegInf', 'ttl': float('-inf')}, {'valid': False, 'pattern': 'TTLPosInf', 'ttl': float('inf')}, {'valid': False, 'pattern': 'TTLNeg1', 'ttl': -1}, {'valid': False, 'pattern': 'TTLPos1', 'ttl': 1}, {'valid': False, 'pattern': 'TTLPosLarge','ttl': 9007199254740992}, {'valid': False, 'pattern': 'TTLEmpty', 'ttl': ''} ] def started(client): """started listener""" try: for test in data: test_opts = {'ttl': test['ttl']} if test['valid']: def on_subscribed(client, err, topic_pattern, share): client.unsubscribe(topic_pattern, 'share', test_opts, on_unsubscribed=func) sub_test_is_done.set() sub_test_is_done = threading.Event() client.subscribe( test['pattern'], 'share', on_subscribed=on_subscribed ) sub_test_is_done.wait(self.TEST_TIMEOUT) else: with pytest.raises(RangeError): client.unsubscribe( test['pattern'], options=test_opts) except Exception as exc: traceback.print_exc(exc) pytest.fail('Unexpected Exception ' + str(exc)) finally: client.stop() test_is_done.set() client = mqlight.Client( 'amqp://host', 'test_unsubscribe_ttl_validity', on_started=started) test_is_done.wait(self.TEST_TIMEOUT) assert test_is_done.is_set()
def test_id_types_values(self): """ Test a range of types / values for client IDs """ data = [ {'data': 1234, 'valid': True}, {'data': None, 'valid': True}, {'data': True, 'valid': True}, {'data': 'abc1234', 'valid': True}, {'data': ':1234', 'valid': False}, {'data': '1234:', 'valid': False}, {'data': '12:34', 'valid': False}, {'data': '%./_', 'valid': True}, {'data': '&.\\_', 'valid': False} ] def stop_client(client): client.stop() for i in data: client = None try: client = mqlight.Client('amqp://localhost:5672', i['data'], on_started=stop_client) except Exception as exc: if i['valid']: pytest.fail('Unexpected Exception ' + str(exc)) finally: if client: client.stop()
def test_subscribe_topics(self): """ Test a variety of valid and invalid patterns. Invalid patterns should result in the client.subscribe(...) method throwing a TypeError """ test_is_done = threading.Event() func = self.func003 data = [{ 'valid': False, 'pattern': '' }, { 'valid': False, 'pattern': None }, { 'valid': False, 'pattern': 1234 }, { 'valid': False, 'pattern': func }, { 'valid': True, 'pattern': 'kittens' }, { 'valid': True, 'pattern': '/kittens' }, { 'valid': True, 'pattern': '+' }, { 'valid': True, 'pattern': '#' }, { 'valid': True, 'pattern': '/#' }, { 'valid': True, 'pattern': '/+' }] def started(client): """started listener""" try: for test in data: if test['valid']: client.subscribe(test['pattern']) else: with pytest.raises(InvalidArgumentError): client.subscribe(test['pattern']) except Exception as exc: pytest.fail('Unexpected Exception ' + str(exc)) finally: client.stop() test_is_done.set() client = mqlight.Client('amqp://host', 'test_subscribe_topics', on_started=started) test_is_done.wait(self.TEST_TIMEOUT) assert test_is_done.is_set()
def test_create_client_must_have_a_value(self): """ Test that passing no value to create_client is not valid """ # pylint: disable=no-value-for-parameter with pytest.raises(TypeError): mqlight.Client()
def test_bad_ssl_options(self): """ Test that bad ssl options cause Client to fail """ data = [ {'ssl_trust_certificate': 1, 'ssl_verify_name': True}, {'ssl_trust_certificate': {'a': 1}, 'ssl_verify_name': True}, {'ssl_trust_certificate': True, 'ssl_verify_name': True}, { 'ssl_trust_certificate': 'ValidCertificate', 'ssl_verify_name': 'a' }, { 'ssl_trust_certificate': 'ValidCertificate', 'ssl_verify_name': '1' }, { 'ssl_trust_certificate': 'ValidCertificate', 'ssl_verify_name': {'a': 1} }, ] for i, option in enumerate(data): with pytest.raises(Exception) as err: service = 'amqp://host' client_id = 'test_bad_ssl_options_{0}'.format(i) security_options = option mqlight.Client(service, client_id, security_options) err_type = type(err.value) allowed = err_type in (TypeError, SecurityError, InvalidArgumentError) assert allowed, 'errtype is unexpectedly ' + str(err_type) if os.path.exists('dirCertificate'): os.rmdir('dirCertificate')
def test_service_not_a_string(self): """ Test that a service name must be a string """ with pytest.raises(InvalidArgumentError) as exc: mqlight.Client(1234) assert str(exc.value) == 'Service is an unsupported type','Error: {0}'.format(exc.value)
def test_start_retry(self): """ Tests that calling start on an endpoint that is currently down retries until successful. """ test_is_done = threading.Event() required_connect_status = 1 _MQLightMessenger.set_connect_status(required_connect_status) def state_changed(client, state, err): if state == mqlight.RETRYING: """error callback""" _MQLightMessenger.set_connect_status( _MQLightMessenger.get_connect_status() - 1) def started(client): """started listener""" assert _MQLightMessenger.get_connect_status() == 0 client.stop() test_is_done.set() client = mqlight.Client('amqp://host:1234', 'test_start_retry', on_started=started, on_state_changed=state_changed) test_is_done.wait(self.TEST_TIMEOUT) assert test_is_done.is_set()
def test_unsubscribe_when_not_subscribed(self): """ Test that trying to remove a subscription that does not exist throws an Error. """ test_is_done = threading.Event() def started(client): """started listener""" subscribe_event = threading.Event() client.subscribe( '/bar', on_subscribed=lambda _w, _x, _y, _z: subscribe_event.set()) subscribe_event.wait(2.0) assert subscribe_event.is_set() with pytest.raises(UnsubscribedError): client.unsubscribe('/foo') client.stop() test_is_done.set() client = mqlight.Client( 'amqp://host', 'test_unsubscribe_when_not_subscribed', on_started=started) test_is_done.wait(self.TEST_TIMEOUT) assert test_is_done.is_set()
def test_stop_when_already_stopped(self): """ Tests that calling stop on an already stopped client has no effect other than to callback any supplied callback function to indicate success. """ test_is_done = threading.Event() def second_callback(client, error): """second stopped callback""" assert client.get_state() == mqlight.STOPPED test_is_done.set() def first_callback(client, error): """first stopped callback""" assert client.get_state() == mqlight.STOPPED client.stop(second_callback) def started(client): """started listener""" assert client.get_state() == mqlight.STARTED client.stop(first_callback) client = mqlight.Client('amqp://host:1234', 'test_stop_when_already_stopped', on_started=started) test_is_done.wait(self.TEST_TIMEOUT) assert test_is_done.is_set()
def test_subscribe_options(self): """ Test a variety of valid and invalid options values. Invalid options should result in the client.subscribe(...) method throwing a ValueError. """ test_is_done = threading.Event() func = self.func003 data = [{ 'valid': False, 'options': '' }, { 'valid': True, 'options': None }, { 'valid': False, 'options': func }, { 'valid': False, 'options': '1' }, { 'valid': False, 'options': 2 }, { 'valid': False, 'options': True }, { 'valid': True, 'options': {} }, { 'valid': True, 'options': { 'a': 1 } }] def started(client): """started listener""" try: for i in range(len(data)): test = data[i] if test['valid']: client.subscribe('/foo' + str(i), 'share', test['options'], func) else: with pytest.raises(TypeError): client.subscribe('/foo' + str(i), 'share', test['options'], func) except Exception as exc: pytest.fail('Unexpected Exception ' + str(exc)) finally: client.stop() test_is_done.set() client = mqlight.Client('amqp://host', 'test_subscribe_options', on_started=started) test_is_done.wait(self.TEST_TIMEOUT) assert test_is_done.is_set()
def test_send_qos(self): """ Test a variety of valid and invalid QoS values. Invalid QoS values should result in the client.send(...) method throwing a ValueError. """ test_is_done = threading.Event() func = self.func005 data = [{ 'valid': False, 'qos': '' }, { 'valid': False, 'qos': None }, { 'valid': False, 'qos': func }, { 'valid': False, 'qos': '1' }, { 'valid': False, 'qos': 2 }, { 'valid': True, 'qos': 0 }, { 'valid': True, 'qos': 1 }, { 'valid': True, 'qos': 9 - 8 }, { 'valid': True, 'qos': mqlight.QOS_AT_MOST_ONCE }, { 'valid': True, 'qos': mqlight.QOS_AT_LEAST_ONCE }] def started(client): """started listener""" try: for test in data: opts = {'qos': test['qos']} if test['valid']: client.send('test', 'message', opts, func) else: with pytest.raises(Exception): client.send('test', 'message', opts, func) except Exception as exc: pytest.fail('Unexpected Exception \'' + str(exc) + '\' for qos ' + str(test['qos'])) client.stop() test_is_done.set() client = mqlight.Client('amqp://host', on_started=started) test_is_done.wait(self.TEST_TIMEOUT) assert test_is_done.is_set()
def test_subscribe_qos(self): """ Test a variety of valid and invalid QoS options. Invalid QoS values should result in the client.subscribe(...) method throwing a ValueError """ test_is_done = threading.Event() func = self.func003 data = [{ 'valid': False, 'qos': '' }, { 'valid': False, 'qos': None }, { 'valid': False, 'qos': func }, { 'valid': False, 'qos': '1' }, { 'valid': False, 'qos': 2 }, { 'valid': True, 'qos': 0 }, { 'valid': True, 'qos': 9 - 8 }, { 'valid': True, 'qos': mqlight.QOS_AT_MOST_ONCE }, { 'valid': True, 'qos': mqlight.QOS_AT_LEAST_ONCE }] def started(client): """started listener""" try: for i in range(len(data)): test = data[i] opts = {'qos': test['qos']} if test['valid']: client.subscribe('/foo' + str(i), options=opts) else: with pytest.raises(RangeError): client.subscribe('/foo' + str(i), options=opts) except Exception as exc: pytest.fail('Unexpected Exception ' + str(exc)) finally: client.stop() test_is_done.set() client = mqlight.Client('amqp://host', 'test_subscribe_qos', on_started=started) test_is_done.wait(self.TEST_TIMEOUT) assert test_is_done.is_set()
def test_create_client_must_have_service_value(self): """ Test that omitting the 'service' property from createClient causes an error """ # pylint: disable=no-value-for-parameter with pytest.raises(TypeError): mqlight.Client( client_id='test_create_client_must_have_service_value')
def __init__(self, broker='amqp://127.0.0.1:5672', cid=None, security_options=None): self.services = {} self.client = mqlight.Client(broker, client_id=cid, security_options=security_options) self.logger = logging.getLogger(__name__)
def test_send_options(self): """ Test a variety of valid and invalid options values. Invalid options should result in the client.send(...) method throwing a TypeError. Note that this test just checks that the options parameter is only accepted when it is of the correct type. The actual validation of individual options will be in separate tests """ test_is_done = threading.Event() func = self.func005 data = [{ 'valid': False, 'options': '' }, { 'valid': True, 'options': None }, { 'valid': False, 'options': func }, { 'valid': False, 'options': '1' }, { 'valid': False, 'options': 2 }, { 'valid': False, 'options': True }, { 'valid': True, 'options': {} }, { 'valid': True, 'options': { 'a': 1 } }] def started(client): """started listener""" try: for test in data: if test['valid']: client.send('test', 'message', test['options'], func) else: with pytest.raises(TypeError): client.send('test', 'message', test['options'], func) except Exception as exc: pytest.fail('Unexpected Exception ' + str(exc)) client.stop() test_is_done.set() client = mqlight.Client('amqp://host', on_started=started) test_is_done.wait(self.TEST_TIMEOUT) assert test_is_done.is_set()
def test_id_autogenerated(self): """ Test that if the 'id' property is omitted then the client id will be generated """ def stop_client(client): client.stop() client = mqlight.Client('amqp://localhost:5672', on_started=stop_client) assert re.search(r'^AUTO_[a-z0-9%/._]{7}$', client.get_id()) is not None
def run(self): """ Start the sample by performing a client connection. The associated callback will handle the subsequent stages of sending a message """ self._client = mqlight.Client(service=self._service, client_id=self._client_id, security_options=self._security_options, on_started=self._started, on_state_changed=self._state_changed, on_drain=self._drained)
def test_send_topics(self): """ Test a variety of valid and invalid topic names. Invalid topic names should result in the client.send(...) method throwing a TypeError. """ test_is_done = threading.Event() func = self.func001 data = [{ 'name': 'TopicEmptyStr', 'error': InvalidArgumentError, 'topic': '' }, { 'name': 'TopicNone', 'error': InvalidArgumentError, 'topic': None }, { 'name': 'Topic1234', 'error': InvalidArgumentError, 'topic': 1234 }, { 'name': 'TopicFunc', 'error': InvalidArgumentError, 'topic': func }, { 'name': 'TopicKittens', 'error': None, 'topic': 'kittens' }, { 'name': 'Topic/Kittens', 'error': None, 'topic': '/kittens' }] def started(client): """started listener""" try: for test in data: if test['error'] is None: client.send(test['topic'], 'message') else: with pytest.raises(test['error']): client.send(test['topic'], 'message') except Exception as exc: pytest.fail('Test {0} : Unexpected Exception {1}{2}'.format( test['name'], str(type(exc)), str(exc))) finally: client.stop() test_is_done.set() client = mqlight.Client('amqp://host', on_started=started) test_is_done.wait(self.TEST_TIMEOUT) assert test_is_done.is_set()
def __init__(self, broker='amqp://127.0.0.1:5672', cid=None, security_options=None): self.callbacks = {} sub_opt = {"auto_confirm": True} self.client = mqlight.Client(broker, client_id=cid, security_options=security_options) self.client.subscribe('response/' + self.client.get_id(), options=sub_opt, on_message=self.serve) self.logger = logging.getLogger(__name__)
def test_start_argument_is_function(self): """ Test that when an argument is specified to the client.start(...) function it must be a callback (e.g. of type function) """ def started(client): pytest.raises(TypeError, client.start, 1234) client.stop() test_is_done.set() test_is_done = threading.Event() client = mqlight.Client('amqp://host:1234', 'test_start_argument_is_function',on_started=started) test_is_done.wait(self.TEST_TIMEOUT) assert test_is_done.is_set()
def test_send_callback(self): """ Tests that, if a callback function is supplied to client.test(...) then the function is invoked when the send operation completes, and this references the client. """ test_is_done = threading.Event() data = [{ 'topic': 'topic1', 'data': 'last', 'options': { 'qos': 0, 'ttl': 1 } }, { 'topic': 'topic2', 'data': 'data2', 'options': { 'qos': 1, 'ttl': 10000 } }] def started(client): """started listener""" def send_callback(client, err, topic, d, options): """send callback""" opts = data.pop() assert err is None assert topic == opts['topic'] assert d == opts['data'] assert options == opts['options'] if d == 'last': client.stop() test_is_done.set() try: for test in reversed(data): client.send(test['topic'], test['data'], test['options'], send_callback) except Exception as exc: traceback.print_exc() pytest.fail('Unexpected Exception ' + str(exc)) client = mqlight.Client('amqp://host', client_id='test_send_callback', on_started=started) test_is_done.wait(self.TEST_TIMEOUT) assert test_is_done.is_set()
def test_start_method_returns_client(self): """ Test that the start(...) method returns the instance of the client that it is invoked on. """ def started(client): result = client.start() assert client == result client.stop() test_is_done.set() test_is_done = threading.Event() client = mqlight.Client('amqp://host:1234', 'test_start_method_returns_client', on_started=started) test_is_done.wait(self.TEST_TIMEOUT) assert test_is_done.is_set()
def test_password_hidden(self): """ Test that a clear text password isn't trivially recoverable from the client object """ def stop_client(client): client.stop() client = mqlight.Client('amqp://localhost:5672', 'test_password_hidden', {'user': '******', 'password': '******'}, on_started=stop_client) members = inspect.getmembers(client, lambda a: not inspect.isroutine(a)) assert re.search(r's3cret', str(members)) is None
def test_client_id_limit(self): """ Test that you can set the client ID to the maximum limit, but no longer than that """ test_is_done = threading.Event() client_id = "A"*256 service = 'amqp://host:1234' def started(client): """started listener""" client.stop() with pytest.raises(InvalidArgumentError): client = mqlight.Client( service=service, client_id=client_id+"A") client = mqlight.Client( service=service, client_id=client_id, on_started=started) test_is_done.set() test_is_done.wait(self.TEST_TIMEOUT) assert test_is_done.is_set()
def test_send_qos_function(self): """ Test that a function is required when QoS is 1. """ test_is_done = threading.Event() func = self.func005 data = [{ 'name': 'Qos1CallbackNone', 'valid': False, 'qos': 1, 'callback': None }, { 'name': 'Qos1CallbackFunc', 'valid': True, 'qos': 1, 'callback': func }, { 'name': 'Qos0CallbackNone', 'valid': True, 'qos': 0, 'callback': None }, { 'name': 'Qos0CallbackFunc', 'valid': True, 'qos': 0, 'callback': func }] def started(client): """started listener""" try: for test in data: opts = {'qos': test['qos']} if test['valid']: client.send('test', 'message', opts, test['callback']) else: with pytest.raises(InvalidArgumentError): client.send('test', 'message', opts, test['callback']) except Exception as exc: pytest.fail('Test {0} : Unexpected Exception {1}'.format( test['name'], str(exc))) client.stop() test_is_done.set() client = mqlight.Client('amqp://host', on_started=started) test_is_done.wait(self.TEST_TIMEOUT) assert test_is_done.is_set()
def test_unsubscribe_callback_must_be_function(self): """ Test that the callback argument to client.unsubscribe(...) must be a function """ test_is_done = threading.Event() def on_stopped(self, error): """client stop callback""" test_is_done.set() def started(client): """started listener""" COUNTER = self.Counter(3) def func(c, e, t, s): if COUNTER.decrementAndGet() <= 0: client.stop(on_stopped=on_stopped) def subscribed1(client, err, pattern, share): with pytest.raises(TypeError): client.unsubscribe('/foo1', 'share', {}, on_unsubscribed=7) client.subscribe('/foo1', 'share', on_subscribed=subscribed1) def subscribed2(client, err, pattern, share): assert err is None client.unsubscribe('/foo2', on_unsubscribed=func) client.subscribe('/foo2', on_subscribed=subscribed2) def subscribed3(client, err, pattern, share): assert err is None client.unsubscribe('/foo3', 'share', on_unsubscribed=func) client.subscribe('/foo3', 'share', on_subscribed=subscribed3) def subscribed4(client, err, pattern, share): assert err is None client.unsubscribe('/foo4', 'share', {}, on_unsubscribed=func) client.subscribe('/foo4', 'share', on_subscribed=subscribed4) client = mqlight.Client( 'amqp://host', 'test_unsubscribe_callback_must_be_function', on_started=started ) test_is_done.wait(self.TEST_TIMEOUT) assert test_is_done.is_set()
def test_unsubscribe_topics(self): """ Test a variety of valid and invalid patterns. Invalid patterns should result in the client.unsubscribe(...) method throwing a TypeError. """ test_is_done = threading.Event() data = [ {'name': 'EmptyStr', 'valid': False, 'pattern': ''}, {'name': 'None', 'valid': False, 'pattern': None}, {'name': 'Number', 'valid': False, 'pattern': 1234}, {'name': 'Func', 'valid': True, 'pattern': lambda *args: 'topic'}, {'name': 'Pat:kittens', 'valid': True, 'pattern': 'kittens'}, {'name': 'Pat:/kittens', 'valid': True, 'pattern': '/kittens'}, {'name': 'Pat:+', 'valid': True, 'pattern': '+'}, {'name': 'Pat:#', 'valid': True, 'pattern': '#'}, {'name': 'Pat:/#', 'valid': True, 'pattern': '/#'}, {'name': 'Pat:/+', 'valid': True, 'pattern': '/+'} ] def started(client): """started listener""" try: for test in data: if test['valid']: test_pattern = test['pattern'] client.subscribe( test['pattern'], on_subscribed=lambda pattern=test_pattern: client.unsubscribe(pattern)) else: with pytest.raises(TypeError): client.unsubscribe(test['pattern']) except InvalidArgumentError: pass except Exception as exc: pytest.fail('Test: {0} reports unexpected Exception {1}'.format(test['name'], exc)) finally: client.stop() test_is_done.set() client = mqlight.Client( 'amqp://host', 'test_unsubscribe_topics', on_started=started) test_is_done.wait(self.TEST_TIMEOUT) assert test_is_done.is_set()
def test_unsubscribe_share_names(self): """ Tests a variety of valid and invalid share names to check that they are accepted or rejected (by throwing an Error) as appropriate. """ test_is_done = threading.Event() data = [ {'valid': True, 'share': 'abc'}, {'valid': False, 'share': 7}, {'valid': False, 'share': ':'}, {'valid': False, 'share': 'a:'}, {'valid': False, 'share': ':a'} ] def started(client): """started listener""" try: for test in data: if test['valid']: test_share = test['share'] def on_unsubscribed(err, pattern, share): sub_test_is_done.set() sub_test_is_done = threading.Event() client.subscribe( '/foo', test_share, on_subscribed=lambda err=None, pattern=None, share=test_share: client.unsubscribe('/foo', share, on_unsubscribed=on_unsubscribed)) sub_test_is_done.wait(self.TEST_TIMEOUT) else: with pytest.raises(InvalidArgumentError): client.unsubscribe('/foo', test['share']) except UnsubscribedError: pass except Exception as exc: pytest.fail('Unexpected Exception ' + str(exc)) finally: client.stop() test_is_done.set() client = mqlight.Client( 'amqp://host', 'test_unsubscribe_share_names', on_started=started) test_is_done.wait(self.TEST_TIMEOUT) assert test_is_done.is_set()
def test_unsubscribe_when_stopped(self): """ Test that trying to remove a subscription, while the client is in stopped state, throws an Error. """ test_is_done = threading.Event() def started(client): def stopped(client, error): """stopped listener""" with pytest.raises(StoppedError): client.unsubscribe('/foo') test_is_done.set() client.stop(stopped) client = mqlight.Client('amqp://host', 'test_unsubscribe_when_stopped', on_started=started) test_is_done.wait(self.TEST_TIMEOUT) assert test_is_done.is_set()