def test_uid_conformance_false(self): """Test UID conformance with ENFORCE_UID_CONFORMANCE = False""" _config.ENFORCE_UID_CONFORMANCE = False primitive = A_ASSOCIATE() primitive.application_context_name = 'abc' assert primitive.application_context_name == 'abc'
def test_uid_conformance_true(self): """Test UID conformance with ENFORCE_UID_CONFORMANCE = True""" _config.ENFORCE_UID_CONFORMANCE = True primitive = A_ASSOCIATE() with pytest.raises(ValueError): primitive.application_context_name = 'abc'
def test_invalid_source_str(self, caplog): """Test an invalid source value gets logged and doesn't raise.""" pdu = A_ASSOCIATE() pdu.result_source = None with caplog.at_level(logging.WARNING, logger="pynetdicom"): assert pdu.source_str == "(no value available)" assert "Invalid A-ASSOCIATE 'Result Source' None" in caplog.text
def test_primitive_to_event(self): """Test that parameter returns expected results""" dul = DULServiceProvider(DummyAssociation()) p2e = dul._primitive_to_event primitive = A_ASSOCIATE() primitive.result = None assert p2e(primitive) == 'Evt1' primitive.result = 0 assert p2e(primitive) == 'Evt7' primitive.result = 1 assert p2e(primitive) == 'Evt8' primitive = A_RELEASE() primitive.result = None assert p2e(primitive) == 'Evt11' primitive.result = 'affirmative' assert p2e(primitive) == 'Evt14' primitive = A_ABORT() assert p2e(primitive) == 'Evt15' primitive = P_DATA() assert p2e(primitive) == 'Evt9' with pytest.raises(ValueError): p2e('TEST')
def test_invalid_result_str(self, caplog): """Test an invalid result value gets logged and doesn't raise.""" pdu = A_ASSOCIATE() pdu.result = None with caplog.at_level(logging.WARNING, logger='pynetdicom'): assert pdu.result_str == '(no value available)' assert "Invalid A-ASSOCIATE 'Result' None" in caplog.text
def test_invalid_reason_str(self, caplog): """Test an invalid diagnostic value gets logged and doesn't raise.""" pdu = A_ASSOCIATE() pdu.result = 1 pdu.result_source = 2 pdu.diagnostic = 7 with caplog.at_level(logging.WARNING, logger='pynetdicom'): assert pdu.reason_str == '(no value available)' assert ("Invalid A-ASSOCIATE 'Result Source' 2 and/or " "'Diagnostic' 7 values") in caplog.text
def send_accept(self): """Send an A-ASSOCIATE (accept) to the peer.""" # The following parameters must be set for an A-ASSOCIATE (accept) # primitive (* sent in A-ASSOCIATE-AC PDU): # Application Context Name* # Calling AE Title* (but not to be tested) # Called AE Title* (but not to be tested) # User Information # Maximum PDV Length* # Implementation Class UID* # Result # Result Source # Presentation Context Definition List Result* primitive = A_ASSOCIATE() primitive.application_context_name = APPLICATION_CONTEXT_NAME primitive.calling_ae_title = self.requestor.primitive.calling_ae_title primitive.called_ae_title = self.requestor.primitive.called_ae_title primitive.result = 0x00 primitive.result_source = 0x01 primitive.presentation_context_definition_results_list = ( self.assoc.accepted_contexts + self.assoc.rejected_contexts) ## User Information - PS3.7 Annex D.3.3 primitive.user_information = self.acceptor.user_information self.acceptor.primitive = primitive self.dul.send_pdu(primitive)
def send_request(assoc): """Send an A-ASSOCIATE (request) to the peer. Parameters ---------- assoc : pynetdicom.association.Association The association that is sending the A-ASSOCIATE (request). """ # The following parameters must be set for a request primitive # (* sent in A-ASSOCIATE-RQ PDU) # Application Context Name* # Calling AE Title* # Called AE Title* # UserInformation* # Maximum PDV Length* # Implementation Class UID* # Calling Presentation Address # Called Presentation Address # Presentation Context Definition List* primitive = A_ASSOCIATE() # DICOM Application Context Name, see PS3.7 Annex A.2.1 primitive.application_context_name = APPLICATION_CONTEXT_NAME # Calling AE Title is the source DICOM AE title primitive.calling_ae_title = assoc.requestor.ae_title # Called AE Title is the destination DICOM AE title primitive.called_ae_title = assoc.acceptor.ae_title # The TCP/IP address of the source, pynetdicom includes port too primitive.calling_presentation_address = ( assoc.requestor.address, assoc.requestor.port ) # The TCP/IP address of the destination, pynetdicom includes port too primitive.called_presentation_address = ( assoc.acceptor.address, assoc.acceptor.port ) # Proposed presentation contexts primitive.presentation_context_definition_list = ( assoc.requestor.requested_contexts ) ## User Information - PS3.7 Annex D.3.3 # Mandatory items: # Maximum Length Notification (1) # Implementation Class UID Notification (1) # Optional notification items: # Implementation Version Name Notification (0 or 1) # Optional negotiation items: # SCP/SCU Role Selection Negotiation (0 or N) # Asynchronous Operations Window Negotiation (0 or 1) # SOP Class Extended Negotiation (0 or N) # SOP Class Common Extended Negotiation (0 or N) # User Identity Negotiation (0 or 1) primitive.user_information = assoc.requestor.user_information # Save the request primitive assoc.requestor.primitive = primitive # Send the A-ASSOCIATE request primitive to the peer assoc.dul.send_pdu(primitive)
def test_recv_primitive(self): """Test processing received primitives""" dul = DULServiceProvider(DummyAssociation()) primitive = A_ASSOCIATE() primitive.result = None dul.to_provider_queue.put(primitive) dul._process_recv_primitive() assert dul.event_queue.get(False) == "Evt1" primitive.result = 0 dul._process_recv_primitive() assert dul.event_queue.get(False) == "Evt7" primitive.result = 1 dul._process_recv_primitive() assert dul.event_queue.get(False) == "Evt8" dul.to_provider_queue.get(False) primitive = A_RELEASE() primitive.result = None dul.to_provider_queue.put(primitive) dul._process_recv_primitive() assert dul.event_queue.get(False) == "Evt11" primitive.result = "affirmative" dul._process_recv_primitive() assert dul.event_queue.get(False) == "Evt14" dul.to_provider_queue.get(False) primitive = A_ABORT() dul.to_provider_queue.put(primitive) dul._process_recv_primitive() assert dul.event_queue.get(False) == "Evt15" dul.to_provider_queue.get(False) primitive = P_DATA() dul.to_provider_queue.put(primitive) dul._process_recv_primitive() assert dul.event_queue.get(False) == "Evt9" dul.to_provider_queue.get(False) msg = "Unknown primitive type 'str' received" with pytest.raises(ValueError, match=msg): dul.to_provider_queue.put("TEST") dul._process_recv_primitive()
def test_conversion(self): """ Check conversion to a PDU produces the correct output """ assoc = A_ASSOCIATE() assoc.application_context_name = "1.2.840.10008.3.1.1.1" assoc.calling_ae_title = 'ECHOSCU' assoc.called_ae_title = 'ANY-SCP' assoc.maximum_length_received = 16382 assoc.implementation_class_uid = '1.2.826.0.1.3680043.9.3811.0.9.0' imp_ver_name = ImplementationVersionNameNotification() imp_ver_name.implementation_version_name = 'PYNETDICOM_090' assoc.user_information.append(imp_ver_name) pc = PresentationContext() pc.context_id = 1 pc.abstract_syntax = '1.2.840.10008.1.1' pc.transfer_syntax = ['1.2.840.10008.1.2'] assoc.presentation_context_definition_list = [pc] pdu = A_ASSOCIATE_RQ() pdu.from_primitive(assoc) data = pdu.encode() assert data == ( b"\x01\x00\x00\x00\x00\xd1\x00\x01\x00\x00\x41\x4e\x59\x2d\x53\x43" b"\x50\x20\x20\x20\x20\x20\x20\x20\x20\x20\x45\x43\x48\x4f\x53\x43" b"\x55\x20\x20\x20\x20\x20\x20\x20\x20\x20\x00\x00\x00\x00\x00\x00" b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x10\x00\x00\x15\x31\x2e" b"\x32\x2e\x38\x34\x30\x2e\x31\x30\x30\x30\x38\x2e\x33\x2e\x31\x2e" b"\x31\x2e\x31\x20\x00\x00\x2e\x01\x00\x00\x00\x30\x00\x00\x11\x31" b"\x2e\x32\x2e\x38\x34\x30\x2e\x31\x30\x30\x30\x38\x2e\x31\x2e\x31" b"\x40\x00\x00\x11\x31\x2e\x32\x2e\x38\x34\x30\x2e\x31\x30\x30\x30" b"\x38\x2e\x31\x2e\x32\x50\x00\x00\x3e\x51\x00\x00\x04\x00\x00\x3f" b"\xfe\x52\x00\x00\x20\x31\x2e\x32\x2e\x38\x32\x36\x2e\x30\x2e\x31" b"\x2e\x33\x36\x38\x30\x30\x34\x33\x2e\x39\x2e\x33\x38\x31\x31\x2e" b"\x30\x2e\x39\x2e\x30\x55\x00\x00\x0e\x50\x59\x4e\x45\x54\x44\x49" b"\x43\x4f\x4d\x5f\x30\x39\x30" )
def test_exceptions(self): """ Check incorrect types/values for properties raise exceptions """ assoc = A_ASSOCIATE() # application_context_name with pytest.raises(TypeError): assoc.application_context_name = 10 with pytest.raises(TypeError): assoc.application_context_name = 45.2 # calling_ae_title with pytest.raises(TypeError): assoc.calling_ae_title = 45.2 with pytest.raises(TypeError): assoc.calling_ae_title = 100 with pytest.raises(ValueError): assoc.calling_ae_title = '' with pytest.raises(ValueError): assoc.calling_ae_title = ' ' # called_ae_title with pytest.raises(TypeError): assoc.called_ae_title = 45.2 with pytest.raises(TypeError): assoc.called_ae_title = 100 with pytest.raises(ValueError): assoc.called_ae_title = '' with pytest.raises(ValueError): assoc.called_ae_title = ' ' # user_information with pytest.raises(TypeError): assoc.user_information = 45.2 # result with pytest.raises(ValueError): assoc.result = -1 with pytest.raises(ValueError): assoc.result = 3 # result_source with pytest.raises(ValueError): assoc.result_source = 0 # result_source with pytest.raises(ValueError): assoc.result_source = 4 # diagnostic with pytest.raises(ValueError): assoc.diagnostic = 0 with pytest.raises(ValueError): assoc.diagnostic = 4 with pytest.raises(ValueError): assoc.diagnostic = 5 with pytest.raises(ValueError): assoc.diagnostic = 6 with pytest.raises(ValueError): assoc.diagnostic = 8 # calling_presentation_addresss with pytest.raises(TypeError): assoc.calling_presentation_address = ['10.40.94.43', 105] with pytest.raises(TypeError): assoc.calling_presentation_address = (105, '10.40.94.43') # called_presentation_addresss with pytest.raises(TypeError): assoc.called_presentation_address = ['10.40.94.43', 105] with pytest.raises(TypeError): assoc.called_presentation_address = (105, '10.40.94.43') # presentation_context_definition_list with pytest.raises(TypeError): assoc.presentation_context_definition_list = 45.2 # presentation_context_definition_results_list with pytest.raises(TypeError): assoc.presentation_context_definition_results_list = 45.2 # implementation_class_uid with pytest.raises(ValueError): x = assoc.implementation_class_uid imp_uid = ImplementationClassUIDNotification() assoc.user_information.append(imp_uid) with pytest.raises(ValueError): x = assoc.implementation_class_uid
def test_assignment(self): """ Check assignment works correctly """ assoc = A_ASSOCIATE() with pytest.raises(AttributeError): assoc.mode = "test value" with pytest.raises(AttributeError): assoc.presentation_requirements = "test value2" with pytest.raises(AttributeError): assoc.session_requirements = "test value3" assoc.application_context_name = "1.2.840.10008.3.1.1.1" assert assoc.application_context_name == UID('1.2.840.10008.3.1.1.1') assoc.application_context_name = b"1.2.840.10008.3.1.1.1" assert assoc.application_context_name == UID('1.2.840.10008.3.1.1.1') assoc.application_context_name = UID("1.2.840.10008.3.1.1.1") assert assoc.application_context_name == UID('1.2.840.10008.3.1.1.1') assoc.calling_ae_title = 'ABCD1234ABCD12345' assert assoc.calling_ae_title == b'ABCD1234ABCD1234' assoc.called_ae_title = 'ABCD1234ABCD12345' assert assoc.called_ae_title == b'ABCD1234ABCD1234' assert assoc.responding_ae_title == b'ABCD1234ABCD1234' max_length = MaximumLengthNotification() max_length.maximum_length_received = 31222 assoc.user_information.append(max_length) assert assoc.user_information[0].maximum_length_received == 31222 assoc.user_information = ['a', max_length] assert assoc.user_information == [max_length] assoc.result = 0 assert assoc.result == 0 assoc.result = 1 assert assoc.result == 1 assoc.result = 2 assert assoc.result == 2 assoc.result_source = 1 assert assoc.result_source == 1 assoc.result_source = 2 assert assoc.result_source == 2 assoc.result_source = 3 assert assoc.result_source == 3 assoc.diagnostic = 1 assert assoc.diagnostic == 1 assoc.diagnostic = 2 assert assoc.diagnostic == 2 assoc.diagnostic = 3 assert assoc.diagnostic == 3 assoc.diagnostic = 7 assert assoc.diagnostic == 7 assoc.calling_presentation_address = ('10.40.94.43', 105) assert assoc.calling_presentation_address == ('10.40.94.43', 105) assoc.called_presentation_address = ('10.40.94.44', 106) assert assoc.called_presentation_address == ('10.40.94.44', 106) pc = PresentationContext() pc.context_id = 1 assoc.presentation_context_definition_list = [pc] assert assoc.presentation_context_definition_list == [pc] assoc.presentation_context_definition_list = ['a', pc] assert assoc.presentation_context_definition_list == [pc] assoc.presentation_context_definition_results_list = [pc] assert assoc.presentation_context_definition_results_list == [pc] assoc.presentation_context_definition_results_list = ['a', pc] assert assoc.presentation_context_definition_results_list == [pc] assoc = A_ASSOCIATE() # No maximum_length_received set assert assoc.maximum_length_received is None # No MaximumLengthNotification present assoc.maximum_length_received = 31223 assert assoc.user_information[0].maximum_length_received == 31223 assert assoc.maximum_length_received == 31223 # MaximumLengthNotification already present assoc.maximum_length_received = 31224 assert assoc.maximum_length_received == 31224 # No ImplementationClassUIDNotification present assoc.implementation_class_uid = '1.1.2.3.4' assert assoc.user_information[1].implementation_class_uid == UID( '1.1.2.3.4') assert assoc.implementation_class_uid == UID('1.1.2.3.4') # ImplementationClassUIDNotification already present assoc.implementation_class_uid = '1.1.2.3.4' assert assoc.implementation_class_uid == UID('1.1.2.3.4')
def send_reject(self, result, source, diagnostic): """Send an A-ASSOCIATE (reject) to the peer. Parameters ---------- result : int The association rejection: - ``0x01`` - rejected permanent - ``0x02`` - rejected transient source : int The source of the rejection: - ``0x01`` - DUL service user - ``0x02`` - DUL service provider (ACSE related) - ``0x03`` - DUL service provider (presentation related) diagnostic : int The reason for the rejection, if the `source` is ``0x01``: - ``0x01`` - no reason given - ``0x02`` - application context name not supported - ``0x03`` - calling AE title not recognised - ``0x07`` - called AE title not recognised If the `source` is ``0x02``: - ``0x01`` - no reason given - ``0x02`` - protocol version not supported If the `source` is ``0x03``: - ``0x01`` - temporary congestion - ``0x02`` - local limit exceeded """ if result not in [0x01, 0x02]: raise ValueError("Invalid 'result' parameter value") _valid_reason_diagnostic = { 0x01: [0x01, 0x02, 0x03, 0x07], 0x02: [0x01, 0x02], 0x03: [0x01, 0x02], } try: if diagnostic not in _valid_reason_diagnostic[source]: raise ValueError("Invalid 'diagnostic' parameter value") except KeyError: raise ValueError("Invalid 'source' parameter value") # The following parameters must be set for an A-ASSOCIATE (reject) # primitive (* sent in A-ASSOCIATE-RJ PDU): # Result* # Result Source* # Diagnostic* primitive = A_ASSOCIATE() primitive.result = result primitive.result_source = source primitive.diagnostic = diagnostic self.acceptor.primitive = primitive self.dul.send_pdu(primitive) self.assoc.is_rejected = True self.assoc.is_established = False
def test_assignment(self): """Check assignment works correctly""" assoc = A_ASSOCIATE() with pytest.raises(AttributeError): assoc.mode = "test value" with pytest.raises(AttributeError): assoc.presentation_requirements = "test value2" with pytest.raises(AttributeError): assoc.session_requirements = "test value3" assoc.application_context_name = None assert assoc.application_context_name is None assoc.application_context_name = "1.2.840.10008.3.1.1.1" assert assoc.application_context_name == UID("1.2.840.10008.3.1.1.1") assoc.application_context_name = b"1.2.840.10008.3.1.1.1" assert assoc.application_context_name == UID("1.2.840.10008.3.1.1.1") assoc.application_context_name = UID("1.2.840.10008.3.1.1.1") assert assoc.application_context_name == UID("1.2.840.10008.3.1.1.1") msg = "'Calling AE Title' must be str, not 'NoneType'" with pytest.raises(TypeError, match=msg): assoc.calling_ae_title = None assoc.calling_ae_title = "ABCDEF1234567890" assert assoc.calling_ae_title == "ABCDEF1234567890" msg = "'Called AE Title' must be str, not 'NoneType'" with pytest.raises(TypeError, match=msg): assoc.called_ae_title = None assert assoc.responding_ae_title == assoc.called_ae_title assoc.called_ae_title = "1234567890ABCDEF" assert assoc.called_ae_title == "1234567890ABCDEF" assert assoc.responding_ae_title == assoc.called_ae_title max_length = MaximumLengthNotification() max_length.maximum_length_received = 31222 assoc.user_information.append(max_length) assert assoc.user_information[0].maximum_length_received == 31222 assoc.user_information = ["a", max_length] assert assoc.user_information == [max_length] assoc.result = None assert assoc.result is None assoc.result = 0 assoc.result = 1 assert assoc.result == 1 assoc.result = 2 assert assoc.result == 2 assoc.result_source = None assert assoc.result_source is None assoc.result_source = 1 assert assoc.result_source == 1 assoc.result_source = 2 assert assoc.result_source == 2 assoc.result_source = 3 assert assoc.result_source == 3 assoc.diagnostic = None assert assoc.diagnostic is None assoc.diagnostic = 1 assert assoc.diagnostic == 1 assoc.diagnostic = 2 assert assoc.diagnostic == 2 assoc.diagnostic = 3 assert assoc.diagnostic == 3 assoc.diagnostic = 7 assert assoc.diagnostic == 7 assoc.calling_presentation_address = None assert assoc.calling_presentation_address is None assoc.calling_presentation_address = ("10.40.94.43", 105) assert assoc.calling_presentation_address == ("10.40.94.43", 105) assoc.called_presentation_address = None assert assoc.called_presentation_address is None assoc.called_presentation_address = ("10.40.94.44", 106) assert assoc.called_presentation_address == ("10.40.94.44", 106) pc = PresentationContext() pc.context_id = 1 assoc.presentation_context_definition_list = [pc] assert assoc.presentation_context_definition_list == [pc] assoc.presentation_context_definition_list = ["a", pc] assert assoc.presentation_context_definition_list == [pc] assoc.presentation_context_definition_results_list = [pc] assert assoc.presentation_context_definition_results_list == [pc] assoc.presentation_context_definition_results_list = ["a", pc] assert assoc.presentation_context_definition_results_list == [pc] assoc = A_ASSOCIATE() # No maximum_length_received set assert assoc.maximum_length_received is None # No MaximumLengthNotification present assoc.maximum_length_received = 31223 assert assoc.user_information[0].maximum_length_received == 31223 assert assoc.maximum_length_received == 31223 # MaximumLengthNotification already present assoc.maximum_length_received = 31224 assert assoc.maximum_length_received == 31224 # No ImplementationClassUIDNotification present assoc.implementation_class_uid = "1.1.2.3.4" assert assoc.user_information[1].implementation_class_uid == UID("1.1.2.3.4") assert assoc.implementation_class_uid == UID("1.1.2.3.4") # ImplementationClassUIDNotification already present assoc.implementation_class_uid = "1.1.2.3.4" assert assoc.implementation_class_uid == UID("1.1.2.3.4")
def setup(self): """Setup each test.""" self.acse = ACSE(5) # A-ASSOCIATE (request) primitive = A_ASSOCIATE() primitive.application_context_name = APPLICATION_CONTEXT_NAME # Calling AE Title is the source DICOM AE title primitive.calling_ae_title = b'ABCDEFGHIJKLMNOP' # Called AE Title is the destination DICOM AE title primitive.called_ae_title = b'1234567890123456' # The TCP/IP address of the source, pynetdicom includes port too primitive.calling_presentation_address = ('127.127.127.127', 111112) # The TCP/IP address of the destination, pynetdicom includes port too primitive.called_presentation_address = ('0.0.0.0', 0) # Proposed presentation contexts contexts = [ build_context('1.2.3.4.5.6', JPEGBaseline), build_context('1.2.840.10008.1.1') ] for ii, cx in enumerate(contexts): cx.context_id = ii * 2 + 1 primitive.presentation_context_definition_list = contexts item = MaximumLengthNotification() item.maximum_length_received = 0 primitive.user_information.append(item) item = ImplementationClassUIDNotification() item.implementation_class_uid = generate_uid(entropy_srcs=['lorem']) primitive.user_information.append(item) self.associate_rq = primitive # A-ASSOCIATE (accept) primitive = A_ASSOCIATE() primitive.application_context_name = APPLICATION_CONTEXT_NAME # Calling AE Title is the source DICOM AE title primitive.calling_ae_title = b'ABCDEFGHIJKLMNOP' # Called AE Title is the destination DICOM AE title primitive.called_ae_title = b'1234567890123456' # The TCP/IP address of the source, pynetdicom includes port too primitive.calling_presentation_address = ('127.127.127.127', 111112) # The TCP/IP address of the destination, pynetdicom includes port too primitive.called_presentation_address = ('0.0.0.0', 0) # Proposed presentation contexts contexts = [ build_context('1.2.3.4.5.6', JPEGBaseline), build_context('1.2.840.10008.1.1'), build_context('1.2.840.10008.1.1'), build_context('1.2.840.10008.1.1'), build_context('1.2.840.10008.1.1'), ] for ii, cx in enumerate(contexts): cx.context_id = ii * 2 + 1 cx.result = ii primitive.presentation_context_definition_results_list = contexts primitive.result = 0x00 item = MaximumLengthNotification() item.maximum_length_received = 0 primitive.user_information.append(item) item = ImplementationClassUIDNotification() item.implementation_class_uid = generate_uid(entropy_srcs=['lorem']) primitive.user_information.append(item) self.associate_ac = primitive