diff --git a/.github/workflows/install-tests.yml b/.github/workflows/install-tests.yml index 660da4a8bd7cbe0622d3307a99f7de9a4ac7fb38_LmdpdGh1Yi93b3JrZmxvd3MvaW5zdGFsbC10ZXN0cy55bWw=..4db15d6e3210fb6ad82774413d3499ed5625f8d4_LmdpdGh1Yi93b3JrZmxvd3MvaW5zdGFsbC10ZXN0cy55bWw= 100644 --- a/.github/workflows/install-tests.yml +++ b/.github/workflows/install-tests.yml @@ -11,8 +11,9 @@ matrix: environment: ['macos-latest', 'windows-latest', 'ubuntu-latest'] python-version: [2.7, 3.5, 3.6, 3.7, 3.8] + python-architecture: ['x86', 'x64'] mq-client-version: [9.1.5.0] exclude: # Windows runner does not have libraries required for build with python2.7 - environment: windows-latest python-version: 2.7 @@ -14,8 +15,13 @@ mq-client-version: [9.1.5.0] exclude: # Windows runner does not have libraries required for build with python2.7 - environment: windows-latest python-version: 2.7 + # actions/setup-python does not have x86 versions for ubuntu and macos + - environment: ubuntu-latest + python-architecture: 'x86' + - environment: macos-latest + python-architecture: 'x86' runs-on: ${{ matrix.environment}} steps: - name: Checkout source @@ -34,6 +40,6 @@ mq-client-version: ${{ matrix.mq-client-version }} - name: Setup python ${{ matrix.python-version }} - uses: actions/setup-python@v1 + uses: actions/setup-python@v2 with: python-version: ${{ matrix.python-version }} @@ -38,5 +44,6 @@ with: python-version: ${{ matrix.python-version }} + architecture: ${{ matrix.python-architecture }} - name: Install pymqi env: diff --git a/code/README b/code/README index 660da4a8bd7cbe0622d3307a99f7de9a4ac7fb38_Y29kZS9SRUFETUU=..4db15d6e3210fb6ad82774413d3499ed5625f8d4_Y29kZS9SRUFETUU= 100644 --- a/code/README +++ b/code/README @@ -62,6 +62,9 @@ * As a prerequisite, you first need to install an IBM MQ client in the system that PyMQI is about to be installed; it is a free library offered by IBM on top of which higher-level ones, such as PyMQI, can connect to queue managers. IBM MQ clients can be downloaded from IBM's website. + +* Prerequisite for Windows: Microsoft Visual C++ compiler. + Each Python version uses a specific compiler version. So, you need to install the compiler version that corresponds to your Python version : https://wiki.python.org/moin/WindowsCompilers * Now you can use pip to install PyMQI itself: diff --git a/code/pymqi/__init__.py b/code/pymqi/__init__.py index 660da4a8bd7cbe0622d3307a99f7de9a4ac7fb38_Y29kZS9weW1xaS9fX2luaXRfXy5weQ==..4db15d6e3210fb6ad82774413d3499ed5625f8d4_Y29kZS9weW1xaS9fX2luaXRfXy5weQ== 100644 --- a/code/pymqi/__init__.py +++ b/code/pymqi/__init__.py @@ -2986,6 +2986,24 @@ parameter = CFBS(StringLength=parameter.StringLength) parameter.unpack(message[cursor:cursor + parameter.StrucLength]) value = parameter.String + elif parameter_type == CMQCFC.MQCFT_STRING_FILTER: + parameter = CFSF() + parameter.unpack(message[cursor:cursor + CMQCFC.MQCFSF_STRUC_LENGTH_FIXED]) + if parameter.FilterValueLength > 0: + parameter = CFSF(FilterValueLength=parameter.FilterValueLength) + parameter.unpack(message[cursor:cursor + CMQCFC.MQCFSF_STRUC_LENGTH_FIXED + parameter.FilterValueLength]) + value = (parameter.Operator, parameter.FilterValue) + elif parameter_type == CMQCFC.MQCFT_BYTE_STRING_FILTER: + parameter = CFBF() + parameter.unpack(message[cursor:cursor + CMQCFC.MQCFBF_STRUC_LENGTH_FIXED]) + if parameter.FilterValueLength > 0: + parameter = CFBF(FilterValueLength=parameter.FilterValueLength) + parameter.unpack(message[cursor:cursor + CMQCFC.MQCFBF_STRUC_LENGTH_FIXED + parameter.FilterValueLength]) + value = (parameter.Operator, parameter.FilterValue) + elif parameter_type == CMQCFC.MQCFT_INTEGER_FILTER: + parameter = CFIF() + parameter.unpack(message[cursor:cursor + CMQCFC.MQCFIF_STRUC_LENGTH]) + value = (parameter.Operator, parameter.FilterValue) else: pcf_type = struct.unpack(MQLONG_TYPE, message[cursor:cursor + 4]) raise NotImplementedError('Unpack for type ({}) not implemented'.format(pcf_type)) diff --git a/code/tests/messages/pcf_with_cfif.dat b/code/tests/messages/pcf_with_cfif.dat new file mode 100644 index 0000000000000000000000000000000000000000..3cb8714fa57da95f20cae85d74559e4c50d647f7 GIT binary patch literal 284 zc$|gLu?_)I6olb#U2Fu2LZP#TN+t0Ggi;~Afz}q@SR)dxS|TUAE8CgmH0RDgXKs!K z5-!N<f$M+{rkG$er>mL*k0I;C2<5FT8bwz4->J$9{|?XEzrx{o-k;7t<gc2{Ph#Ba kv~6{o;htCB<L)x+XAKwr*fnhIg9-I&_LZvt##`ik0maZ1K>z>% diff --git a/code/tests/messages/pcf_with_cfsf.dat b/code/tests/messages/pcf_with_cfsf.dat new file mode 100644 index 0000000000000000000000000000000000000000..287e01b35bcfc99ffba3affeebd56f5efcb0f2ca GIT binary patch literal 296 zc$|gLy=nqs5QX7q^Jy0G2MHGGv`A$ocm;}<78373>|!B$yMj#+Y^?<u_S;~W0|UeG z&Y3eMzL2m*kt_7m;td1zF&olQ)d;_Bril*rhyAElcm;n?l~?c!Tr)qxYQ0&0Z~j*P zRqgpmbYC*fUow5-QdZgH?zng_>b>A&*2~$?B_H|ECQiHKX`K7;xuxp&;KK6>5>Xfp diff --git a/code/tests/test_base_api.py b/code/tests/test_base_api.py index 660da4a8bd7cbe0622d3307a99f7de9a4ac7fb38_Y29kZS90ZXN0cy90ZXN0X2Jhc2VfYXBpLnB5..4db15d6e3210fb6ad82774413d3499ed5625f8d4_Y29kZS90ZXN0cy90ZXN0X2Jhc2VfYXBpLnB5 100644 --- a/code/tests/test_base_api.py +++ b/code/tests/test_base_api.py @@ -312,17 +312,6 @@ self.assertEqual(md.Format, pymqi.CMQC.MQFMT_STRING) self.assertEqual(md.CodedCharSetId, 1208) - def test_put1_bytes(self): - md = pymqi.MD() - self.qmgr.put1(self.queue_name, b'\xd1\x82\xd0\xb5\xd1\x81\xd1\x82', md) # Non-ascii characters - - gmo = pymqi.GMO() - gmo.Options = gmo.Options & ~ pymqi.CMQC.MQGMO_CONVERT - message = self.queue.get(None, md, gmo) - - self.assertEqual(message, b'\xd1\x82\xd0\xb5\xd1\x81\xd1\x82') - self.assertEqual(md.Format, pymqi.CMQC.MQFMT_NONE) - def test_put1(self): input_msg = b'Hello world!' self.qmgr.put1(self.queue_name, input_msg) diff --git a/code/tests/test_pcf.py b/code/tests/test_pcf.py index 660da4a8bd7cbe0622d3307a99f7de9a4ac7fb38_Y29kZS90ZXN0cy90ZXN0X3BjZi5weQ==..4db15d6e3210fb6ad82774413d3499ed5625f8d4_Y29kZS90ZXN0cy90ZXN0X3BjZi5weQ== 100644 --- a/code/tests/test_pcf.py +++ b/code/tests/test_pcf.py @@ -1,4 +1,6 @@ """Test PCF usage.""" import os +from sys import version_info as sys_version_info + from unittest import skip from unittest import skipIf @@ -3,4 +5,4 @@ from unittest import skip from unittest import skipIf -from ddt import data +from ddt import data # type: ignore from ddt import ddt @@ -6,5 +8,9 @@ from ddt import ddt -from sys import version_info as sys_version_info + +try: + from typing import List +except ImportError: + pass from test_setup import Tests # noqa from test_setup import main # noqa @@ -15,7 +21,6 @@ class TestPCF(Tests): """Class for MQ PCF testing.""" - pcf = None messages_dir = os.path.join(os.path.dirname(__file__), "messages") @classmethod @@ -44,7 +49,7 @@ super(TestPCF, self).tearDown() - @skip('Not implemented') + @skip('Test not implemented') def test_mqcfbf(self): """Test MQCFBF PCF byte string filter parameter.""" @@ -53,7 +58,7 @@ Also uses MQCFIN and MQCFIL as parameters """ - attrs = [] + attrs = [] # type: List[pymqi.MQOpts] attrs.append(pymqi.CFBS(Parameter=pymqi.CMQCFC.MQBACF_GENERIC_CONNECTION_ID, String=b'')) attrs.append(pymqi.CFIN(Parameter=pymqi.CMQCFC.MQIACF_CONN_INFO_TYPE, @@ -76,7 +81,7 @@ Also uses MQCFST, MQCFIN and MQCFIL as parameters """ - attrs = [] + attrs = [] # type: List[pymqi.MQOpts] attrs.append(pymqi.CFST(Parameter=pymqi.CMQC.MQCA_Q_NAME, String=b'*')) attrs.append(pymqi.CFIN(Parameter=pymqi.CMQC.MQIA_Q_TYPE, @@ -101,7 +106,7 @@ Also uses MQCFST, MQCFIN and MQCFIL as parameters """ - attrs = [] + attrs = [] # type: List[pymqi.MQOpts] attrs.append(pymqi.CFST(Parameter=pymqi.CMQC.MQCA_Q_NAME, String=b'*')) attrs.append(pymqi.CFIN(Parameter=pymqi.CMQC.MQIA_Q_TYPE, @@ -130,7 +135,7 @@ Also uses MQCFST and MQCFIN as parameters """ - attrs = [] + attrs = [] # type: List[pymqi.MQOpts] attrs.append(pymqi.CFST(Parameter=pymqi.CMQC.MQCA_NAMELIST_NAME, String='{}NAMELIST'.format(self.prefix).encode())) attrs.append(pymqi.CFSL(Parameter=pymqi.CMQC.MQCA_NAMES, @@ -187,5 +192,5 @@ get_md = pymqi.MD(MsgId=put_md.MsgId) # pylint: disable=no-member message = queue.get(None, get_md, get_opts) queue.close() - message = pymqi.PCFExecute.unpack(message) + unpacked_message = pymqi.PCFExecute.unpack(message) @@ -191,4 +196,4 @@ - self.assertTrue(isinstance(message[0][1], list), - 'Returned value is not list: {}'.format(type(message[0][1]))) + self.assertTrue(isinstance(unpacked_message[0][1], list), + 'Returned value is not list: {}'.format(type(unpacked_message[0][1]))) @@ -194,3 +199,3 @@ - self.assertTrue(len(message[0][1]) == len(value), 'List length is different!') + self.assertTrue(len(unpacked_message[0][1]) == len(value), 'List length is different!') @@ -196,5 +201,5 @@ - for item in message[0][1]: + for item in unpacked_message[0][1]: self.assertTrue(item in value, '{} value not in values list'.format(item)) value.remove(item) @@ -204,6 +209,6 @@ Type=pymqi.CMQCFC.MQCFT_USER, ParameterCount=4).pack() message += pymqi.CFST(Parameter=pymqi.CMQC.MQCA_Q_MGR_NAME, - String=b'QM1').pack() + String=b'QM1').pack() # group1 message += pymqi.CFGR(Parameter=pymqi.CMQCFC.MQGACF_Q_STATISTICS_DATA, @@ -208,4 +213,4 @@ # group1 message += pymqi.CFGR(Parameter=pymqi.CMQCFC.MQGACF_Q_STATISTICS_DATA, - ParameterCount=3).pack() + ParameterCount=3).pack() message += pymqi.CFST(Parameter=pymqi.CMQC.MQCA_Q_NAME, @@ -211,3 +216,3 @@ message += pymqi.CFST(Parameter=pymqi.CMQC.MQCA_Q_NAME, - String=b'SYSTEM.ADMIN.COMMAND.QUEUE').pack() + String=b'SYSTEM.ADMIN.COMMAND.QUEUE').pack() message += pymqi.CFIN64(Parameter=pymqi.CMQCFC.MQIAMO_Q_MIN_DEPTH, @@ -213,3 +218,3 @@ message += pymqi.CFIN64(Parameter=pymqi.CMQCFC.MQIAMO_Q_MIN_DEPTH, - Value=10).pack() + Value=10).pack() message += pymqi.CFIL64(Parameter=pymqi.CMQCFC.MQIAMO64_AVG_Q_TIME, @@ -215,4 +220,4 @@ message += pymqi.CFIL64(Parameter=pymqi.CMQCFC.MQIAMO64_AVG_Q_TIME, - Values=[1, 2, 3]).pack() + Values=[1, 2, 3]).pack() # group2 message += pymqi.CFGR(Parameter=pymqi.CMQCFC.MQGACF_Q_STATISTICS_DATA, @@ -217,4 +222,4 @@ # group2 message += pymqi.CFGR(Parameter=pymqi.CMQCFC.MQGACF_Q_STATISTICS_DATA, - ParameterCount=3).pack() + ParameterCount=3).pack() message += pymqi.CFST(Parameter=pymqi.CMQC.MQCA_Q_NAME, @@ -220,3 +225,3 @@ message += pymqi.CFST(Parameter=pymqi.CMQC.MQCA_Q_NAME, - String=b'SYSTEM.ADMIN.COMMAND.QUEUE2').pack() + String=b'SYSTEM.ADMIN.COMMAND.QUEUE2').pack() message += pymqi.CFIN64(Parameter=pymqi.CMQCFC.MQIAMO_Q_MIN_DEPTH, @@ -222,3 +227,3 @@ message += pymqi.CFIN64(Parameter=pymqi.CMQCFC.MQIAMO_Q_MIN_DEPTH, - Value=20).pack() + Value=20).pack() message += pymqi.CFIL64(Parameter=pymqi.CMQCFC.MQIAMO64_AVG_Q_TIME, @@ -224,5 +229,5 @@ message += pymqi.CFIL64(Parameter=pymqi.CMQCFC.MQIAMO64_AVG_Q_TIME, - Values=[111, 222]).pack() + Values=[111, 222]).pack() message += pymqi.CFST(Parameter=pymqi.CMQCFC.MQCAMO_START_TIME, String=b'10.41.58').pack() @@ -266,7 +271,7 @@ Command=pymqi.CMQCFC.MQCMD_STATISTICS_Q, ParameterCount=1).pack() message += pymqi.CFST(Parameter=pymqi.CMQC.MQCA_Q_MGR_NAME, - String=b'QM1').pack() + String=b'QM1').pack() queue = pymqi.Queue(self.qmgr, self.queue_name, pymqi.CMQC.MQOO_INPUT_AS_Q_DEF + pymqi.CMQC.MQOO_OUTPUT) @@ -283,8 +288,8 @@ queue.close() message, header = pymqi.PCFExecute.unpack(message) - self.assertEqual(header.Command, pymqi.CMQCFC.MQCMD_STATISTICS_Q) - self.assertEqual(header.Type, pymqi.CMQCFC.MQCFT_STATISTICS) + self.assertEqual(header.Command, pymqi.CMQCFC.MQCMD_STATISTICS_Q) # pylint: disable=no-member + self.assertEqual(header.Type, pymqi.CMQCFC.MQCFT_STATISTICS) # pylint: disable=no-member self.assertEqual({ pymqi.CMQC.MQCA_Q_MGR_NAME: b'QM1\x00', @@ -292,5 +297,14 @@ def test_unpack_group(self): """Test parameters group unpack.""" - binary_message = open(os.path.join(self.messages_dir, "statistics_q.dat"), "rb").read() + with open(os.path.join(self.messages_dir, "statistics_q.dat"), "rb") as file: + binary_message = file.read() + message, header = pymqi.PCFExecute.unpack(binary_message) + + self.assertEqual(header.Command, pymqi.CMQCFC.MQCMD_STATISTICS_Q) # pylint: disable=no-member + self.assertEqual(header.Type, pymqi.CMQCFC.MQCFT_STATISTICS) # pylint: disable=no-member + + self.assertEqual(message[pymqi.CMQC.MQCA_Q_MGR_NAME].strip(), b'mq_mgr1') + self.assertEqual(message[pymqi.CMQCFC.MQCAMO_START_DATE], b'2020-06-15\x00\x00') + self.assertEqual(len(message[pymqi.CMQCFC.MQGACF_Q_STATISTICS_DATA]), 16) @@ -296,3 +310,5 @@ - message, header = pymqi.PCFExecute.unpack(binary_message) + item = message[pymqi.CMQCFC.MQGACF_Q_STATISTICS_DATA][0] + self.assertEqual(item[pymqi.CMQC.MQCA_Q_NAME].strip(), b'SYSTEM.ADMIN.COMMAND.QUEUE') + self.assertEqual(item[pymqi.CMQCFC.MQIAMO_PUTS], [14, 0]) @@ -298,4 +314,8 @@ - self.assertEqual(header.Command, pymqi.CMQCFC.MQCMD_STATISTICS_Q) - self.assertEqual(header.Type, pymqi.CMQCFC.MQCFT_STATISTICS) + def test_unpack_cfsf(self): + """Test unpack of PCF message with MQCFSF structure.""" + with open(os.path.join(self.messages_dir, "pcf_with_cfsf.dat"), "rb") as file: + binary_message = file.read() + + message, _ = pymqi.PCFExecute.unpack(binary_message) @@ -301,5 +321,13 @@ - self.assertEqual(message[pymqi.CMQC.MQCA_Q_MGR_NAME].strip(), b'mq_mgr1') - self.assertEqual(message[pymqi.CMQCFC.MQCAMO_START_DATE], b'2020-06-15\x00\x00') - self.assertEqual(len(message[pymqi.CMQCFC.MQGACF_Q_STATISTICS_DATA]), 16) + self.assertEqual(message.get(pymqi.CMQCFC.MQGACF_COMMAND_DATA, [{}])[0].get(pymqi.CMQC.MQCA_Q_DESC)[0], + pymqi.CMQCFC.MQCFOP_LIKE) + + self.assertEqual(message.get(pymqi.CMQCFC.MQGACF_COMMAND_DATA, + [{}])[0].get(pymqi.CMQC.MQCA_Q_DESC)[1].rstrip(b'\x00'), + b'test*') + + @skip('Test not implemented') + def test_unpack_cfbf(self): + """Test unpack of PCF message with MQCFBF structure.""" + @@ -305,5 +333,6 @@ - item = message[pymqi.CMQCFC.MQGACF_Q_STATISTICS_DATA][0] - self.assertEqual(item[pymqi.CMQC.MQCA_Q_NAME].strip(), b'SYSTEM.ADMIN.COMMAND.QUEUE') - self.assertEqual(item[pymqi.CMQCFC.MQIAMO_PUTS], [14, 0]) + def test_unpack_cfif(self): + """Test unpack of PCF message with MQCFIF structure.""" + with open(os.path.join(self.messages_dir, "pcf_with_cfif.dat"), "rb") as file: + binary_message = file.read() @@ -309,5 +338,11 @@ - @skipIf(sys_version_info < (3, 7),'Python pre 3.7 issues: https://github.com/dsuch/pymqi/issues/207#issuecomment-645422229') + message, _ = pymqi.PCFExecute.unpack(binary_message) + + self.assertEqual(message.get(pymqi.CMQCFC.MQGACF_COMMAND_DATA, [{}])[0].get(pymqi.CMQC.MQIA_CURRENT_Q_DEPTH), + (pymqi.CMQCFC.MQCFOP_GREATER, 0)) + + @skipIf(sys_version_info < (3, 7), + 'Python pre 3.7 issues: https://github.com/dsuch/pymqi/issues/207#issuecomment-645422229') def test_mqcfbs_old(self): """Test byte string MQCFBS with old style.""" attrs = { @@ -317,7 +352,7 @@ } fltr = pymqi.Filter(pymqi.CMQC.MQIA_APPL_TYPE).equal(pymqi.CMQC.MQAT_USER) - results = self.pcf.MQCMD_INQUIRE_CONNECTION(attrs) #, [fltr]) + results = self.pcf.MQCMD_INQUIRE_CONNECTION(attrs, [fltr]) self.assertGreater(len(results), 0) diff --git a/code/tests/test_setup.py b/code/tests/test_setup.py index 660da4a8bd7cbe0622d3307a99f7de9a4ac7fb38_Y29kZS90ZXN0cy90ZXN0X3NldHVwLnB5..4db15d6e3210fb6ad82774413d3499ed5625f8d4_Y29kZS90ZXN0cy90ZXN0X3NldHVwLnB5 100644 --- a/code/tests/test_setup.py +++ b/code/tests/test_setup.py @@ -23,7 +23,8 @@ prefix = '' - qmgr = None + qmgr = None # type: pymqi.QueueManager + pcf = None # type: pymqi.PCFExecute @classmethod @@ -49,7 +50,7 @@ cls.qmgr.connectTCPClient(cls.queue_manager, pymqi.CD(), cls.channel, cls.conn_info, cls.user, cls.password) - cls.pcf = pymqi.PCFExecute(cls.qmgr, response_wait_interval=5000) + cls.pcf = pymqi.PCFExecute(cls.qmgr, response_wait_interval=15000) cls.version = cls.inquire_qmgr_version().decode()