diff --git a/.coveragerc b/.coveragerc
new file mode 100644
index 0000000000000000000000000000000000000000..449e17b8dd577683bf0f5604aab71219ce4ff180_LmNvdmVyYWdlcmM=
--- /dev/null
+++ b/.coveragerc
@@ -0,0 +1,2 @@
+[run]
+omit = */CMQ*.py
diff --git a/.github/workflows/install-tests.yml b/.github/workflows/install-tests.yml
new file mode 100644
index 0000000000000000000000000000000000000000..449e17b8dd577683bf0f5604aab71219ce4ff180_LmdpdGh1Yi93b3JrZmxvd3MvaW5zdGFsbC10ZXN0cy55bWw=
--- /dev/null
+++ b/.github/workflows/install-tests.yml
@@ -0,0 +1,46 @@
+name: install-tests
+on:
+  push:
+    branches:
+      - master
+      - gh159-actions-unit-testing
+  pull_request:
+
+jobs:
+  pymqi_test_job:
+    strategy:
+      matrix:
+        environment: ['macos-latest', 'windows-latest', 'ubuntu-latest']
+        python-version: [2.7, 3.5, 3.6, 3.7, 3.8]
+        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
+    runs-on: ${{ matrix.environment}}
+    steps:
+      - name: Checkout source
+        uses: actions/checkout@v2
+
+      - name: Cache MQ Client
+        uses: actions/cache@v1
+        with:
+          path: ${{ github.workspace }}/setup-mqclient
+          key: mqclient-${{ runner.os }}-${{ matrix.mq-client-version }}
+
+      - name: Install MQ Client
+        id: setup-mqclient
+        uses: SeyfSV/setup-mqclient@v0.1.3
+        with:
+          mq-client-version: ${{ matrix.mq-client-version }}
+
+      - name: Setup python ${{ matrix.python-version }}
+        uses: actions/setup-python@v1
+        with:
+          python-version: ${{ matrix.python-version }}
+
+      - name: Install pymqi
+        env:
+          MQ_FILE_PATH: ${{ steps.setup-mqclient.outputs.mq-file-path }}
+        run: |
+          python setup.py install --verbose
diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml
new file mode 100644
index 0000000000000000000000000000000000000000..449e17b8dd577683bf0f5604aab71219ce4ff180_LmdpdGh1Yi93b3JrZmxvd3MvdW5pdC10ZXN0cy55bWw=
--- /dev/null
+++ b/.github/workflows/unit-tests.yml
@@ -0,0 +1,121 @@
+name: unit-tests
+on:
+  push:
+    branches:
+      - master
+      - gh159-actions-unit-testing
+  pull_request:
+
+jobs:
+  pymqi_test_job:
+    strategy:
+      max-parallel: 3
+      matrix:
+        environment: ['ubuntu-latest']
+        # windows-latest: runner does allow to run ibmcom/mq linux container
+        # macos-latest: runner does not have docker
+        # macos-latest: has some issues in SeyfSV/setup-mqclient@master action
+        #environment: ['ubuntu-latest', 'macos-latest', 'windows-latest']
+        python-version: [2.7, 3.5, 3.6, 3.7, 3.8]
+        mq-client-version: [9.1.4.0]
+
+    services:
+      mq:
+        image: ibmcom/mq
+        env:
+          LICENSE: accept
+          MQ_QMGR_NAME: MQTEST
+        ports:
+          - 8886:1414
+        volumes:
+          - mqm_volume:/mnt/mqm
+          - pki_volume:/etc/mqm/pki
+        options: --name mq --health-cmd "dspmq -m MQTEST" --health-interval 5s --health-timeout 5s --health-retries 10
+
+    runs-on: ${{ matrix.environment}}
+    steps:
+      - name: Extract pki_volume path
+        id: pki_volume_path
+        run: |
+          echo "::set-output name=volume_path::$(docker volume inspect --format '{{ .Mountpoint }}' pki_volume)"
+
+      - name: Extract mqm_volume path
+        id: mqm_volume_path
+        run: |
+          echo "::set-output name=volume_path::$(docker volume inspect --format '{{ .Mountpoint }}' mqm_volume)"
+
+      - name: Checkout source
+        uses: actions/checkout@v2
+
+      - name: Cache MQ Client
+        uses: actions/cache@v1
+        with:
+          path: ${{ github.workspace }}/setup-mqclient
+          key: mqclient-${{ runner.os }}-${{ matrix.mq-client-version }}
+
+      - name: Install MQ Client
+        id: setup-mqclient
+        uses: SeyfSV/setup-mqclient@v0.1.3
+        with:
+          mq-client-version: ${{ matrix.mq-client-version }}
+
+      - name: Setup python ${{ matrix.python-version }}
+        uses: actions/setup-python@v1
+        with:
+          python-version: ${{ matrix.python-version }}
+
+      - name: Install pymqi dependencies
+        run: |
+          pip install tox
+
+      - name: Prepare to TLS test
+        run: |
+          mkdir ./keys
+          runmqakm -keydb -create -db ./keys/mqtest.kdb -pw Secret13 -stash
+          runmqakm -cert -create -db ./keys/mqtest.kdb -type kdb -pw Secret13 \
+          -label mqtest -dn CN=mqtest -size 2048 -x509version 3 -expire 365 -fips -sig_alg SHA256WithRSA
+          runmqakm -keydb -create -db ./keys/client.kdb -pw Secret13 -stash
+          runmqakm -cert -create -db ./keys/client.kdb -type kdb -pw Secret13 \
+          -label client -dn CN=client -size 2048 -x509version 3 -expire 365 -fips -sig_alg SHA256WithRSA
+          runmqakm -cert -extract -db ./keys/mqtest.kdb -pw Secret13 \
+          -label mqtest -target ./keys/mqtest.pem
+          runmqakm -cert -add -db ./keys/client.kdb -pw Secret13 \
+          -label mqtest -file ./keys/mqtest.pem
+          runmqakm -cert -extract -db ./keys/client.kdb -pw Secret13 \
+          -label client -target ./keys/client.pem
+          runmqakm -cert -add -db ./keys/mqtest.kdb -pw Secret13 \
+          -label client -file ./keys/client.pem
+          sudo cp -r ./keys ${{ steps.pki_volume_path.outputs['volume_path'] }}
+          sudo chown -R `id -u`:1001 ${{ steps.pki_volume_path.outputs['volume_path'] }}/keys
+          sudo chmod -R g+r ${{ steps.pki_volume_path.outputs['volume_path'] }}/keys
+
+      - name: Test pymqi with tox
+        id: test-tox
+        env:
+          PYMQI_TEST_TLS_SKIP: 0
+          PYMQI_TEST_TLS_KEY_REPO_LOCATION_QMGR: /etc/mqm/pki/keys
+          PYMQI_TEST_TLS_KEY_REPO_LOCATION_CLIENT: ./keys
+        run: |
+          tox -e docker
+
+      - name: Copy MQ Server logs
+        if: failure()
+        run: |
+          sudo cp -r ${{ steps.mqm_volume_path.outputs['volume_path'] }}/data/qmgrs/MQTEST/errors ./errors
+          sudo chmod o+x ./errors
+          sudo chmod -R o+r ./errors
+
+      - name: Upload MQ Server logs
+        if: failure()
+        uses: actions/upload-artifact@v1
+        with:
+          name: mq-server-logs
+          path: ./errors
+
+      - name: Upload MQ Client logs
+        if: failure()
+        uses: actions/upload-artifact@v1
+        with:
+          name: mq-client-logs
+          path: ${{ steps.setup-mqclient.outputs.mq-file-path }}/errors
+
diff --git a/.gitignore b/.gitignore
index 1ba572cd9ec5f51b4c469bf2a4436100113dfe3a_LmdpdGlnbm9yZQ==..449e17b8dd577683bf0f5604aab71219ce4ff180_LmdpdGlnbm9yZQ== 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,6 +1,6 @@
 .idea
 .idea/*
-code/build/*
+build/*
 code/dist
 code/develop-eggs/*
 code/MANIFEST
diff --git a/code/README b/code/README
index 1ba572cd9ec5f51b4c469bf2a4436100113dfe3a_Y29kZS9SRUFETUU=..449e17b8dd577683bf0f5604aab71219ce4ff180_Y29kZS9SRUFETUU= 100644
--- a/code/README
+++ b/code/README
@@ -1,3 +1,6 @@
+
+![unit-tests](https://github.com/dsuch/pymqi/workflows/unit-tests/badge.svg)
+![install-tests](https://github.com/dsuch/pymqi/workflows/install-tests/badge.svg)
 
 PyMQI - Python interface to IBM MQ (WebSphere MQ, MQSeries)
 ----------------------------------------------------------
diff --git a/code/setup.py b/code/setup.py
deleted file mode 100644
index 1ba572cd9ec5f51b4c469bf2a4436100113dfe3a_Y29kZS9zZXR1cC5weQ==..0000000000000000000000000000000000000000
--- a/code/setup.py
+++ /dev/null
@@ -1,198 +0,0 @@
-# Setup script usable for distutils.
-#
-# Original Author: Maas-Maarten Zeeman
-#
-# Linux build is re-entrant/multithreaded.
-
-# stdlib
-import os
-import sys
-try:
-    from setuptools import setup
-except ImportError:
-    from distutils.core import setup
-
-from distutils.core import Extension
-from distutils import spawn
-from struct import calcsize
-
-version = '1.11.0'
-
-# Build either in bindings or client mode.
-bindings_mode = 0
-if sys.argv[-1] in ('bindings', 'server'):
-    bindings_mode = 1
-    sys.argv = sys.argv[:-1]
-if sys.argv[-1] == 'client':
-    bindings_mode = 0
-    sys.argv = sys.argv[:-1]
-
-# Are we running 64bits?
-if calcsize('P') == 8:
-    bits = 64
-else:
-    bits = 32
-
-def get_windows_settings():
-    """ Windows settings.
-    """
-    if bits == 64:
-        library_dirs = [r'c:\Program Files (x86)\IBM\WebSphere MQ\tools\Lib64']
-        include_dirs = [r'c:\Program Files (x86)\IBM\WebSphere MQ\tools\c\include']
-    else:
-        library_dirs = [r'c:\Program Files\IBM\WebSphere MQ\Tools\Lib']
-        include_dirs = [r'c:\Program Files\IBM\WebSphere MQ\tools\c\include']
-
-    if bindings_mode:
-        libraries = ['mqm']
-    else:
-        if bits == 64:
-            libraries = ['mqic']
-        else:
-            libraries = ['mqic32']
-
-    return library_dirs, include_dirs, libraries
-
-def get_sunos_zlinux_settings():
-    """ SunOS and z/Linux settings.
-    """
-    if bits == 64:
-        library_dirs = ['/opt/mqm/lib64']
-    else:
-        library_dirs = ['/opt/mqm/lib']
-
-    include_dirs = ['/opt/mqm/inc']
-
-    if bindings_mode:
-        libraries = ['mqm','mqmcs','mqmzse']
-    else:
-        libraries = ['mqic']
-
-    return library_dirs, include_dirs, libraries
-
-def get_aix_settings():
-    """ AIX settings.
-    """
-    if bits == 64:
-        library_dirs = ['/usr/mqm/lib64']
-    else:
-        library_dirs = ['/usr/mqm/lib']
-
-    include_dirs = ['/usr/mqm/inc']
-
-    if bindings_mode:
-        libraries = ['mqm_r']
-    else:
-        libraries = ['mqic_r']
-
-    return library_dirs, include_dirs, libraries
-
-def get_generic_unix_settings():
-    """ Generic UNIX, including Linux, settings.
-    """
-    if bits == 64:
-        library_dirs = ['/opt/mqm/lib64']
-    else:
-        library_dirs = ['/opt/mqm/lib']
-
-    include_dirs = ['/opt/mqm/inc']
-
-    if bindings_mode:
-        libraries = ['mqm_r']
-    else:
-        libraries = ['mqic_r']
-
-    return library_dirs, include_dirs, libraries
-
-def get_locations_by_command_path(command_path):
-    """ Extracts directory locations by the path to one of MQ commands, such as dspmqver.
-    """
-    command_dir = os.path.dirname(command_path)
-    mq_installation_path = os.path.abspath(os.path.join(command_dir, '..'))
-
-    if bits == 64:
-        library_dirs = ['{}/lib64'.format(mq_installation_path)]
-    else:
-        library_dirs = ['{}/lib'.format(mq_installation_path)]
-
-    include_dirs = ['{}/inc'.format(mq_installation_path)]
-
-    if bindings_mode:
-        libraries = ['mqm_r']
-    else:
-        libraries = ['mqic_r']
-
-    return library_dirs, include_dirs, libraries
-
-# Windows
-if sys.platform == 'win32':
-    library_dirs, include_dirs, libraries = get_windows_settings()
-
-# SunOS and z/Linux
-elif sys.platform == 'sunos5' or sys.platform == 'linux-s390':
-    library_dirs, include_dirs, libraries = get_sunos_zlinux_settings()
-
-# AIX
-elif sys.platform.startswith('aix'):
-    library_dirs, include_dirs, libraries = get_aix_settings()
-
-# At this point, to preserve backward-compatibility we try out generic
-# UNIX settings first, i.e. libraries and include files in well-known locations.
-# Otherwise, to support Mac, we look up dspmqver in $PATH.
-else:
-
-    has_generic_lib = os.path.exists('/opt/mqm/lib64') if bits == 64 else os.path.exists('/opt/mqm/lib')
-
-    if has_generic_lib:
-        library_dirs, include_dirs, libraries = get_generic_unix_settings()
-
-    else:
-
-        # On Mac, users can install MQ to any location so we look up
-        # the path that dspmqver is installed to and find the rest
-        # of the information needed in relation to that base directory.
-        dspmqver_path = spawn.find_executable('dspmqver')
-
-        # We have found the command so we will be able to extract the relevant directories now
-        if dspmqver_path:
-            library_dirs, include_dirs, libraries = get_locations_by_command_path(dspmqver_path)
-
-        else:
-            raise Exception('MQ libraries could not be found')
-
-if bindings_mode:
-    print('Building PyMQI bindings mode %sbits' % bits)
-else:
-    print('Building PyMQI client mode %sbits' % bits)
-
-print('Using library_dirs:`%s`, include:`%s`, libraries:`%s`' % (library_dirs, include_dirs, libraries))
-
-setup(name = 'pymqi',
-    version = version,
-    description = 'Python IBM MQI Extension for IBM MQ (formerly WebSphere MQ and MQSeries).',
-    long_description= 'PyMQI is a Python library for working with IBM MQ (formerly WebSphere MQ and MQSeries) implementing MQI and PCF protocols.',
-    author='Dariusz Suchojad',
-    author_email='pymqi@m.zato.io',
-    url='https://dsuch.github.io/pymqi/',
-    download_url='https://pypi.python.org/pypi/pymqi',
-    platforms='OS Independent',
-    packages = ['pymqi'],
-    license='Python Software Foundation License',
-    keywords=('pymqi IBM MQ WebSphere WMQ MQSeries IBM middleware messaging queueing asynchronous SOA EAI ESB integration'),
-
-    classifiers = [
-        'Development Status :: 5 - Production/Stable',
-        'License :: OSI Approved :: Python Software Foundation License',
-        'Intended Audience :: Developers',
-        'Natural Language :: English',
-        'Operating System :: OS Independent',
-        'Programming Language :: C',
-        'Programming Language :: Python',
-        'Topic :: Software Development :: Libraries :: Python Modules',
-        'Topic :: Software Development :: Object Brokering',
-        ],
-    py_modules = ['pymqi.CMQC', 'pymqi.CMQCFC', 'pymqi.CMQXC', 'pymqi.CMQZC'],
-    ext_modules = [Extension('pymqi.pymqe',['pymqi/pymqe.c'], define_macros=[('PYQMI_BINDINGS_MODE_BUILD', bindings_mode)],
-        library_dirs = library_dirs,
-        include_dirs = include_dirs,
-        libraries = libraries)])
diff --git a/code/tests/README b/code/tests/README
index 1ba572cd9ec5f51b4c469bf2a4436100113dfe3a_Y29kZS90ZXN0cy9SRUFETUU=..449e17b8dd577683bf0f5604aab71219ce4ff180_Y29kZS90ZXN0cy9SRUFETUU= 100644
--- a/code/tests/README
+++ b/code/tests/README
@@ -5,7 +5,8 @@
 * The test suite itself requires at least Python 2.5 because of its reliance
   on the 'uuid' module which has been introduced in 2.5
 
-* Install nose and testfixtures
-  $ sudo pip install nose
-  $ sudo pip install testfixtures
+* Install tox
+  ```
+  $ sudo pip install tox
+  ```
 
@@ -11,4 +12,4 @@
 
-* Set a proper configuration by modifying the defaults in config.py or override
+* Set a proper configuration by modifying the defaults in config.py or override 
   these properties with environment variables, see config.py, if necessary.
 
@@ -13,6 +14,16 @@
   these properties with environment variables, see config.py, if necessary.
 
-* Prepare the test environment using create_mq_objects.py (needs a local MQ
-  server installation to access the MQ command executables) or create the 
-  objects by hand, consistent with the configuration. 
+* Prepare the test environment:
+  * Using create_mq_objects.py (needs a local MQ
+  server installation to access the MQ command executables) or create the
+  objects by hand, consistent with the configuration.
+  * Using Docker image (ibmcom/mq) provided by IBM. 
+  For minimal setup this command can be used:
+    ```
+    $ docker run \
+      --env LICENSE=accept \
+      --env MQ_QMGR_NAME=MQTEST \
+      --publish 8886:1414 \
+      ibmcom/mq
+    ```
 
@@ -18,3 +29,13 @@
 
-* Use nose to execute the tests:
+* Use tox to execute the tests:
+ * With local MQ:
+   ```
+   $ tox -e local
+   ```
+ * With Docker image:
+   ```
+   $ tox -e docker
+   ```
+
+* Use py.test to execute the tests:
  * Directly:
@@ -20,4 +41,6 @@
  * Directly:
-   $ nosetests
+    ```
+   $ py.test
+   ```
    Note that you need an inplace build of pymqe.so if you want to run the tests
    on the checkout rather than e.g. a test installation
@@ -22,9 +45,6 @@
    Note that you need an inplace build of pymqe.so if you want to run the tests
    on the checkout rather than e.g. a test installation
-   (python setup.py build_ext --inplace). 
- * Through setup.py:
-   $ python setup.py nosetests 
-   (this issues an inplace-build automatically)
+   (`python setup.py build_ext --inplace`).
 
 * [Delete the test environment with delete_mq_objects.py]
 
diff --git a/code/tests/create_mq_objects.py b/code/tests/create_mq_objects.py
index 1ba572cd9ec5f51b4c469bf2a4436100113dfe3a_Y29kZS90ZXN0cy9jcmVhdGVfbXFfb2JqZWN0cy5weQ==..449e17b8dd577683bf0f5604aab71219ce4ff180_Y29kZS90ZXN0cy9jcmVhdGVfbXFfb2JqZWN0cy5weQ== 100644
--- a/code/tests/create_mq_objects.py
+++ b/code/tests/create_mq_objects.py
@@ -150,8 +150,8 @@
                  errormsg="MQ queue manager connection authentication setup not successful.")
     else:
         print("Connection authentication not applicable for pre-8.0 MQ.")
-        
-    
+
+
 print("Disabling MQ queue manager channel authentication records feature.")
 run_mqsc(mqsc_script_channel_auth,
          errormsg="Disabling MQ queue manager channel authentication records not successful.")
diff --git a/code/tests/env.py b/code/tests/env.py
deleted file mode 100644
index 1ba572cd9ec5f51b4c469bf2a4436100113dfe3a_Y29kZS90ZXN0cy9lbnYucHk=..0000000000000000000000000000000000000000
--- a/code/tests/env.py
+++ /dev/null
@@ -1,22 +0,0 @@
-# test environment settings
-
-import sys
-import os
-
-
-def add_syspath(p):
-    """Add path p to sys.path without defeating PYTHONPATH.
-
-    Appends to sys.path if PYTHONPATH is set, otherwise put p at sys.path index
-    1.
-    """
-    if p not in sys.path:
-        if "PYTHONPATH" in os.environ:
-            sys.path.append(p)
-        else:
-            sys.path.insert(1, p)
-
-
-# Add pymqi code directory to sys.path for import from checkout
-# (needs in-place build of pymqe.so)
-add_syspath(os.path.normpath(os.path.join(os.path.dirname(__file__), '..')))
diff --git a/code/tests/requirements.txt b/code/tests/requirements.txt
index 1ba572cd9ec5f51b4c469bf2a4436100113dfe3a_Y29kZS90ZXN0cy9yZXF1aXJlbWVudHMudHh0..449e17b8dd577683bf0f5604aab71219ce4ff180_Y29kZS90ZXN0cy9yZXF1aXJlbWVudHMudHh0 100644
--- a/code/tests/requirements.txt
+++ b/code/tests/requirements.txt
@@ -1,4 +1,5 @@
 ddt
 nose
 pytest
+pytest-cov
 testfixtures
@@ -4,1 +5,2 @@
 testfixtures
+tox
diff --git a/code/tests/run_pymqi_test_suite.py b/code/tests/run_pymqi_test_suite.py
deleted file mode 100644
index 1ba572cd9ec5f51b4c469bf2a4436100113dfe3a_Y29kZS90ZXN0cy9ydW5fcHltcWlfdGVzdF9zdWl0ZS5weQ==..0000000000000000000000000000000000000000
--- a/code/tests/run_pymqi_test_suite.py
+++ /dev/null
@@ -1,27 +0,0 @@
-'''
-Created on 17 Nov 2010
-
-@author: hannes
-'''
-
-import unittest
-
-import test_rfh2
-import test_h2py
-import test_rfh2_put_get
-
-h2py_suite =  unittest.TestLoader().loadTestsFromTestCase(test_h2py.Testh2py)
-
-rfh2_suite = unittest.TestLoader().loadTestsFromTestCase(test_rfh2.TestRFH2)
-rfh2_put_get_suite = unittest.TestLoader().loadTestsFromTestCase(test_rfh2_put_get.TestRFH2PutGet)
-
-all_suite = unittest.TestSuite([h2py_suite, rfh2_suite])
-
-mq_not_required_tests = [h2py_suite, rfh2_suite]
-mq_required_tests = [rfh2_put_get_suite]
-
-mq_not_required_suite = unittest.TestSuite(mq_not_required_tests)
-mq_required_suite = unittest.TestSuite(mq_required_tests)
-
-unittest.TextTestRunner(verbosity=2).run(mq_not_required_suite)
-unittest.TextTestRunner(verbosity=2).run(mq_required_suite)
diff --git a/code/tests/test_api_transition_pep8.py b/code/tests/test_api_transition_pep8.py
index 1ba572cd9ec5f51b4c469bf2a4436100113dfe3a_Y29kZS90ZXN0cy90ZXN0X2FwaV90cmFuc2l0aW9uX3BlcDgucHk=..449e17b8dd577683bf0f5604aab71219ce4ff180_Y29kZS90ZXN0cy90ZXN0X2FwaV90cmFuc2l0aW9uX3BlcDgucHk= 100644
--- a/code/tests/test_api_transition_pep8.py
+++ b/code/tests/test_api_transition_pep8.py
@@ -1,13 +1,7 @@
 """ All sorts of tests related to making the API PEP-8 compliant.
 """
-
-# stdlib
-import sys
-
-sys.path.insert(0, "..")
-
-from nose.tools import eq_
+import unittest
 
 # PyMQI
 import pymqi
 
@@ -10,18 +4,24 @@
 
 # PyMQI
 import pymqi
 
-def test_backward_compatibility():
-    """ Makes sure all the relevant classes and methods have backward-compatible
-    replacements.
-    """
-    eq_(pymqi.gmo, pymqi.GMO)
-    eq_(pymqi.pmo, pymqi.PMO)
-    eq_(pymqi.od, pymqi.OD)
-    eq_(pymqi.md, pymqi.MD)
-    eq_(pymqi.cd, pymqi.CD)
-    eq_(pymqi.sco, pymqi.SCO)
-    eq_(pymqi.QueueManager.connectWithOptions, pymqi.QueueManager.connect_with_options)
-    eq_(pymqi.QueueManager.connectTCPClient, pymqi.QueueManager.connect_tcp_client)
-    eq_(pymqi.QueueManager.getHandle, pymqi.QueueManager.get_handle)
-    eq_(pymqi.PCFExecute.stringifyKeys, pymqi.PCFExecute.stringify_keys)
+
+class TestApiTransitionPEP8(unittest.TestCase):
+    """All sorts of tests related to making the API PEP-8 compliant."""
+
+    def test_backward_compatibility(self):
+        """Test backward-compatible.
+
+        Makes sure all the relevant classes and methods have
+        backward-compatible replacements.
+        """
+        self.assertEqual(pymqi.gmo, pymqi.GMO)
+        self.assertEqual(pymqi.pmo, pymqi.PMO)
+        self.assertEqual(pymqi.od, pymqi.OD)
+        self.assertEqual(pymqi.md, pymqi.MD)
+        self.assertEqual(pymqi.cd, pymqi.CD)
+        self.assertEqual(pymqi.sco, pymqi.SCO)
+        self.assertEqual(pymqi.QueueManager.connectWithOptions, pymqi.QueueManager.connect_with_options)
+        self.assertEqual(pymqi.QueueManager.connectTCPClient, pymqi.QueueManager.connect_tcp_client)
+        self.assertEqual(pymqi.QueueManager.getHandle, pymqi.QueueManager.get_handle)
+        self.assertEqual(pymqi.PCFExecute.stringifyKeys, pymqi.PCFExecute.stringify_keys)
diff --git a/code/tests/test_base_api.py b/code/tests/test_base_api.py
index 1ba572cd9ec5f51b4c469bf2a4436100113dfe3a_Y29kZS90ZXN0cy90ZXN0X2Jhc2VfYXBpLnB5..449e17b8dd577683bf0f5604aab71219ce4ff180_Y29kZS90ZXN0cy90ZXN0X2Jhc2VfYXBpLnB5 100644
--- a/code/tests/test_base_api.py
+++ b/code/tests/test_base_api.py
@@ -323,5 +323,21 @@
         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)
+        # now get the message from the queue
+        queue = pymqi.Queue(self.qmgr, self.queue_name)
+        result_msg = queue.get()
+        self.assertEqual(input_msg, result_msg)
+
+    def test_inquire(self):
+        attribute = pymqi.CMQC.MQCA_Q_MGR_NAME
+        expected_value = utils.py3str2bytes(self.queue_manager)
+        attribute_value = self.qmgr.inquire(attribute)
+        self.assertEqual(len(attribute_value), pymqi.CMQC.MQ_Q_MGR_NAME_LENGTH)
+        self.assertEqual(attribute_value.strip(), expected_value)
+
+
 if __name__ == "__main__":
     unittest.main()
diff --git a/code/tests/test_message_property.py b/code/tests/test_message_property.py
index 1ba572cd9ec5f51b4c469bf2a4436100113dfe3a_Y29kZS90ZXN0cy90ZXN0X21lc3NhZ2VfcHJvcGVydHkucHk=..449e17b8dd577683bf0f5604aab71219ce4ff180_Y29kZS90ZXN0cy90ZXN0X21lc3NhZ2VfcHJvcGVydHkucHk= 100644
--- a/code/tests/test_message_property.py
+++ b/code/tests/test_message_property.py
@@ -3,7 +3,6 @@
 import unittest
 
 import config
-import env
 import utils
 
 import pymqi
@@ -56,9 +55,9 @@
                 pymqi.CMQC.MQIA_Q_TYPE: queue_type,
                 pymqi.CMQC.MQIA_MAX_Q_DEPTH: max_depth,
                 pymqi.CMQCFC.MQIACF_REPLACE: pymqi.CMQCFC.MQRP_YES}
-        pcf = pymqi.PCFExecute(self.qmgr)
+        pcf = pymqi.PCFExecute(self.qmgr, response_wait_interval=120000)
         pcf.MQCMD_CREATE_Q(args)
         pcf.disconnect
 
     def delete_queue(self, queue_name):
 
@@ -60,9 +59,9 @@
         pcf.MQCMD_CREATE_Q(args)
         pcf.disconnect
 
     def delete_queue(self, queue_name):
 
-        pcf = pymqi.PCFExecute(self.qmgr)
+        pcf = pymqi.PCFExecute(self.qmgr, response_wait_interval=120000)
         args = {pymqi.CMQC.MQCA_Q_NAME: utils.py3str2bytes(queue_name),
                 pymqi.CMQCFC.MQIACF_PURGE: pymqi.CMQCFC.MQPO_YES}
         pcf.MQCMD_DELETE_Q(args)
diff --git a/code/tests/test_mq80.py b/code/tests/test_mq80.py
index 1ba572cd9ec5f51b4c469bf2a4436100113dfe3a_Y29kZS90ZXN0cy90ZXN0X21xODAucHk=..449e17b8dd577683bf0f5604aab71219ce4ff180_Y29kZS90ZXN0cy90ZXN0X21xODAucHk= 100644
--- a/code/tests/test_mq80.py
+++ b/code/tests/test_mq80.py
@@ -19,7 +19,6 @@
 
 # test config & env
 import config
-import env
 
 # PyMQI
 import pymqi
@@ -46,4 +45,5 @@
         conn = self.get_conn()
         pcf = pymqi.PCFExecute(conn)
         command_level = pcf.MQCMD_INQUIRE_Q_MGR()[0][CMQC.MQIA_COMMAND_LEVEL]
+        conn.disconnect()
         self.assertGreaterEqual(command_level, 800)
@@ -49,5 +49,4 @@
         self.assertGreaterEqual(command_level, 800)
-        conn.disconnect()
 
 
     def test_connect_with_credentials(self):
diff --git a/code/tests/test_pcf.py b/code/tests/test_pcf.py
index 1ba572cd9ec5f51b4c469bf2a4436100113dfe3a_Y29kZS90ZXN0cy90ZXN0X3BjZi5weQ==..449e17b8dd577683bf0f5604aab71219ce4ff180_Y29kZS90ZXN0cy90ZXN0X3BjZi5weQ== 100644
--- a/code/tests/test_pcf.py
+++ b/code/tests/test_pcf.py
@@ -1,5 +1,6 @@
 """Test PCF usage."""
 import os
 from unittest import skip
+from unittest import skipIf
 from ddt import data
 from ddt import ddt
@@ -4,5 +5,6 @@
 from ddt import data
 from ddt import ddt
+from sys import version_info as sys_version_info
 
 from test_setup import Tests  # noqa
 from test_setup import main  # noqa
@@ -23,8 +25,7 @@
 
         # max length of queue names is 48 characters
         cls.queue_name = "{prefix}PCF.QUEUE".format(prefix=cls.prefix)
-        cls.pcf = pymqi.PCFExecute(cls.qmgr, response_wait_interval=600)
 
     @classmethod
     def tearDownClass(cls):
         """Tear down test environment."""
@@ -27,9 +28,7 @@
 
     @classmethod
     def tearDownClass(cls):
         """Tear down test environment."""
-        cls.pcf.disconnect()
-
         super(TestPCF, cls).tearDownClass()
 
     def setUp(self):
@@ -274,6 +273,7 @@
         self.assertEqual(item[pymqi.CMQC.MQCA_Q_NAME].strip(), b'SYSTEM.ADMIN.COMMAND.QUEUE')
         self.assertEqual(item[pymqi.CMQCFC.MQIAMO_PUTS], [14, 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 = {
diff --git a/code/tests/test_queue_manager.py b/code/tests/test_queue_manager.py
index 1ba572cd9ec5f51b4c469bf2a4436100113dfe3a_Y29kZS90ZXN0cy90ZXN0X3F1ZXVlX21hbmFnZXIucHk=..449e17b8dd577683bf0f5604aab71219ce4ff180_Y29kZS90ZXN0cy90ZXN0X3F1ZXVlX21hbmFnZXIucHk= 100644
--- a/code/tests/test_queue_manager.py
+++ b/code/tests/test_queue_manager.py
@@ -5,7 +5,6 @@
 import unittest
 from uuid import uuid4
 
-from nose.tools import eq_
 from testfixtures import Replacer
 
 import config  # noqa
@@ -128,18 +127,6 @@
     def test_backout(self):
         pass
 
-    def test_put1(self):
-        qmgr = pymqi.QueueManager(None)
-        qmgr.connect_tcp_client(
-            self.qm_name, pymqi.cd(), self.channel, self.conn_info, user=self.user,
-            password=self.password)
-        input_msg = b'Hello world!'
-        qmgr.put1(self.queue_name, input_msg)
-        # now get the message from the queue
-        queue = pymqi.Queue(qmgr, self.queue_name)
-        result_msg = queue.get()
-        self.assertEqual(input_msg, result_msg)
-
     def test_inquire(self):
         qmgr = pymqi.QueueManager(None)
         qmgr.connect_tcp_client(
@@ -151,50 +138,5 @@
         self.assertEqual(len(attribute_value), pymqi.CMQC.MQ_Q_MGR_NAME_LENGTH)
         self.assertEqual(attribute_value.strip(), expected_value)
 
-    def test_is_connected(self):
-        """Makes sure the QueueManager's 'is_connected' property works as
-        expected.
-        """
-        # uses a mock so no real connection to a queue manager will be
-        # established - the parameters below are basically moot
-        with Replacer() as r:
-            queue_manager = uuid4().hex
-            channel = uuid4().hex
-            host = uuid4().hex
-            port = "1431"
-            conn_info = "%s(%s)" % (host, port)
-            user = "myuser"
-            password = "mypass"
-
-            for expected in(True, False):
-
-                # noinspection PyUnusedLocal
-                def _connect_tcp_client(*ignored_args, **ignored_kwargs):
-                    pass
-
-                # noinspection PyUnusedLocal
-                def _getattr(self2, name):
-                    if expected:
-                        class _DummyMethod(object):
-                            pass
-                        # The mere fact of not raising an exception will suffice
-                        # for QueueManager._is_connected to understand it as an
-                        # all's OK condition.
-                        return _DummyMethod
-                    else:
-                        raise Exception()
-
-                r.replace('pymqi.QueueManager.connect_tcp_client',
-                          _connect_tcp_client)
-                r.replace('pymqi.PCFExecute.__getattr__', _getattr)
-
-                qmgr = pymqi.QueueManager(None)
-                qmgr.connect_tcp_client(
-                    queue_manager, pymqi.cd(), channel, conn_info, user,
-                    password)
-
-                eq_(qmgr.is_connected, expected)
-
-
 if __name__ == '__main__':
     unittest.main()
diff --git a/code/tests/test_rfh2.py b/code/tests/test_rfh2.py
index 1ba572cd9ec5f51b4c469bf2a4436100113dfe3a_Y29kZS90ZXN0cy90ZXN0X3JmaDIucHk=..449e17b8dd577683bf0f5604aab71219ce4ff180_Y29kZS90ZXN0cy90ZXN0X3JmaDIucHk= 100644
--- a/code/tests/test_rfh2.py
+++ b/code/tests/test_rfh2.py
@@ -10,6 +10,7 @@
 from pymqi import CMQC
 
 
+
 class TestRFH2(unittest.TestCase):
     """This test case tests the RFH2 class and it's methods.
     """
@@ -222,8 +223,8 @@
             rfh2.unpack(self.single_rfh2_message[0:32])
         except pymqi.PYIFError as e:
             self.assertEqual(str(e),
-                             "PYMQI Error: RFH2 - Buffer too short. Should be 36 bytes or longer.  Buffer Length: 32",
-                             "Not Buffer to short exception?")
+                             'PYMQI Error: RFH2 - Buffer too short. Should be 36+ bytes instead of 32',
+                             'Not Buffer to short exception?')
 
     def test_buffer_too_short_for_complete_rfh2_exception(self):
         """Test exception occurs when buffer is too short for complete RFH2.
diff --git a/code/tests/test_rfh2_put_get.py b/code/tests/test_rfh2_put_get.py
index 1ba572cd9ec5f51b4c469bf2a4436100113dfe3a_Y29kZS90ZXN0cy90ZXN0X3JmaDJfcHV0X2dldC5weQ==..449e17b8dd577683bf0f5604aab71219ce4ff180_Y29kZS90ZXN0cy90ZXN0X3JmaDJfcHV0X2dldC5weQ== 100644
--- a/code/tests/test_rfh2_put_get.py
+++ b/code/tests/test_rfh2_put_get.py
@@ -10,4 +10,5 @@
 import pymqi
 from pymqi import CMQC
 
+import test_setup
 
@@ -13,5 +14,6 @@
 
-class TestRFH2PutGet(unittest.TestCase):
+
+class TestRFH2PutGet(test_setup.Tests):
     """This test case tests the RFH2 class and it's methods.
     """
 
@@ -22,6 +24,7 @@
         Must be run as a user that has 'mqm' access.
 
         """
+        super(TestRFH2PutGet, self).setUp()
 
         self.single_rfh2_message = open(
             os.path.join(self.messages_dir, "single_rfh2.dat"), "rb").read()
@@ -33,8 +36,5 @@
         self.multiple_rfh2_message_not_well_formed = \
             self.multiple_rfh2_message[0:117] + self.multiple_rfh2_message[121:]
 
-        queue_manager = config.MQ.QM.NAME
-        channel = config.MQ.QM.CHANNEL
-        conn_info = "%s(%s)" % (config.MQ.QM.HOST, config.MQ.QM.PORT)
-        queue_name = config.MQ.QUEUE.QUEUE_NAMES['TestRFH2PutGet']
+        self.create_queue(self.queue_name)
 
@@ -40,14 +40,6 @@
 
-        self.qmgr = None
-        if pymqi.__mqbuild__ == 'server':
-            self.qmgr = pymqi.QueueManager(queue_manager)
-        else:
-            self.qmgr = pymqi.QueueManager(None)
-            self.qmgr.connect_tcp_client(
-                queue_manager, pymqi.cd(), channel, conn_info,
-                user=config.MQ.QM.USER, password=config.MQ.QM.PASSWORD)
-        self.put_queue = pymqi.Queue(self.qmgr, queue_name)
-        self.get_queue = pymqi.Queue(self.qmgr, queue_name)
+        self.put_queue = pymqi.Queue(self.qmgr, self.queue_name)
+        self.get_queue = pymqi.Queue(self.qmgr, self.queue_name)
         self.clear_queue(self.get_queue)
 
     def tearDown(self):
@@ -56,7 +48,8 @@
         """
         self.put_queue.close()
         self.get_queue.close()
-        self.qmgr.disconnect()
+        self.delete_queue(self.queue_name)
+        super(TestRFH2PutGet, self).tearDown()
 
     @staticmethod
     def clear_queue(queue):
diff --git a/code/tests/test_setup.py b/code/tests/test_setup.py
index 1ba572cd9ec5f51b4c469bf2a4436100113dfe3a_Y29kZS90ZXN0cy90ZXN0X3NldHVwLnB5..449e17b8dd577683bf0f5604aab71219ce4ff180_Y29kZS90ZXN0cy90ZXN0X3NldHVwLnB5 100644
--- a/code/tests/test_setup.py
+++ b/code/tests/test_setup.py
@@ -42,7 +42,9 @@
 
         cls.conn_info = '{0}({1})'.format(cls.host, cls.port)
 
-        cls.qmgr = pymqi.QueueManager(None)
-        try:
+        if pymqi.__mqbuild__ == 'server':
+            cls.qmgr = pymqi.QueueManager(cls.queue_manager)
+        else:
+            cls.qmgr = pymqi.QueueManager(None)
             cls.qmgr.connectTCPClient(cls.queue_manager, pymqi.CD(), cls.channel,
                                       cls.conn_info, cls.user, cls.password)
@@ -47,11 +49,10 @@
             cls.qmgr.connectTCPClient(cls.queue_manager, pymqi.CD(), cls.channel,
                                       cls.conn_info, cls.user, cls.password)
-        except pymqi.MQMIError as ex:
-            if ex.comp == pymqi.CMQC.MQCC_FAILED:
-                raise ex
+
+        cls.pcf = pymqi.PCFExecute(cls.qmgr, response_wait_interval=5000)
 
         cls.version = cls.inquire_qmgr_version().decode()
 
     @classmethod
     def tearDownClass(cls):
         """Clear test environment."""
@@ -52,9 +53,10 @@
 
         cls.version = cls.inquire_qmgr_version().decode()
 
     @classmethod
     def tearDownClass(cls):
         """Clear test environment."""
+        cls.pcf.disconnect()
         cls.qmgr.disconnect()
 
     def setUp(self):
@@ -81,8 +83,7 @@
             attrs.append(pymqi.CFIN(Parameter=pymqi.CMQCFC.MQIACF_REPLACE,
                                     Value=pymqi.CMQCFC.MQRP_YES))
 
-        pcf = pymqi.PCFExecute(self.qmgr)
-        pcf.MQCMD_CREATE_Q(attrs)
+        self.pcf.MQCMD_CREATE_Q(attrs)
 
     def delete_queue(self, queue_name):
         """Delete queue."""
@@ -86,9 +87,8 @@
 
     def delete_queue(self, queue_name):
         """Delete queue."""
-        pcf = pymqi.PCFExecute(self.qmgr)
         attrs = []
         attrs.append(pymqi.CFST(Parameter=pymqi.CMQC.MQCA_Q_NAME,
                                 String=utils.py3str2bytes(queue_name)))
         attrs.append(pymqi.CFIN(Parameter=pymqi.CMQCFC.MQIACF_PURGE,
                                 Value=pymqi.CMQCFC.MQPO_YES))
@@ -90,9 +90,9 @@
         attrs = []
         attrs.append(pymqi.CFST(Parameter=pymqi.CMQC.MQCA_Q_NAME,
                                 String=utils.py3str2bytes(queue_name)))
         attrs.append(pymqi.CFIN(Parameter=pymqi.CMQCFC.MQIACF_PURGE,
                                 Value=pymqi.CMQCFC.MQPO_YES))
-        pcf.MQCMD_DELETE_Q(attrs)
+        self.pcf.MQCMD_DELETE_Q(attrs)
 
     def create_channel(self, channel_name, attrs=None):
         """Create channle."""
@@ -104,8 +104,7 @@
                                     Value=pymqi.CMQC.MQCHT_SVRCONN))
             attrs.append(pymqi.CFIN(Parameter=pymqi.CMQCFC.MQIACF_REPLACE,
                                     Value=pymqi.CMQCFC.MQRP_YES))
-        pcf = pymqi.PCFExecute(self.qmgr)
-        pcf.MQCMD_CREATE_CHANNEL(attrs)
+        self.pcf.MQCMD_CREATE_CHANNEL(attrs)
 
     def delete_channel(self, channel_name):
         """Delete channel."""
@@ -109,7 +108,6 @@
 
     def delete_channel(self, channel_name):
         """Delete channel."""
-        pcf = pymqi.PCFExecute(self.qmgr)
         attrs = []
         attrs.append(pymqi.CFST(Parameter=pymqi.CMQCFC.MQCACH_CHANNEL_NAME,
                                 String=utils.py3str2bytes(channel_name)))
@@ -113,7 +111,7 @@
         attrs = []
         attrs.append(pymqi.CFST(Parameter=pymqi.CMQCFC.MQCACH_CHANNEL_NAME,
                                 String=utils.py3str2bytes(channel_name)))
-        pcf.MQCMD_DELETE_CHANNEL(attrs)
+        self.pcf.MQCMD_DELETE_CHANNEL(attrs)
 
     def create_auth_rec(self, attrs):
         """Create authentication recoed."""
@@ -117,8 +115,7 @@
 
     def create_auth_rec(self, attrs):
         """Create authentication recoed."""
-        pcf = pymqi.PCFExecute(self.qmgr)
-        pcf.MQCMD_SET_CHLAUTH_REC(attrs)
+        self.pcf.MQCMD_SET_CHLAUTH_REC(attrs)
 
     def delete_auth_rec(self, attrs):
         """Delete authentication recoed."""
@@ -122,9 +119,8 @@
 
     def delete_auth_rec(self, attrs):
         """Delete authentication recoed."""
-        pcf = pymqi.PCFExecute(self.qmgr)
-        pcf.MQCMD_SET_CHLAUTH_REC(attrs)
+        self.pcf.MQCMD_SET_CHLAUTH_REC(attrs)
 
     @classmethod
     def edit_qmgr(cls, attrs):
         """Edit connected Queue Manager."""
@@ -127,6 +123,5 @@
 
     @classmethod
     def edit_qmgr(cls, attrs):
         """Edit connected Queue Manager."""
-        pcf = pymqi.PCFExecute(cls.qmgr)
-        pcf.MQCMD_CHANGE_Q_MGR(attrs)
+        cls.pcf.MQCMD_CHANGE_Q_MGR(attrs)
diff --git a/code/tests/test_tls.py b/code/tests/test_tls.py
index 1ba572cd9ec5f51b4c469bf2a4436100113dfe3a_Y29kZS90ZXN0cy90ZXN0X3Rscy5weQ==..449e17b8dd577683bf0f5604aab71219ce4ff180_Y29kZS90ZXN0cy90ZXN0X3Rscy5weQ== 100644
--- a/code/tests/test_tls.py
+++ b/code/tests/test_tls.py
@@ -147,7 +147,7 @@
         attrs.append(pymqi.CFIN(Parameter=pymqi.CMQCFC.MQIACF_ACTION,
                                 Value=pymqi.CMQCFC.MQACT_REPLACE))
         attrs.append(pymqi.CFST(Parameter=pymqi.CMQCFC.MQCACH_CLIENT_USER_ID,
-                                String=self.user))
+                                String=utils.py3str2bytes(self.user)))
         attrs.append(pymqi.CFIN(Parameter=pymqi.CMQC.MQIA_CHECK_CLIENT_BINDING,
                                 Value=pymqi.CMQCFC.MQCHK_REQUIRED_ADMIN))
         attrs.append(pymqi.CFIN(Parameter=pymqi.CMQCFC.MQIACH_USER_SOURCE,
@@ -151,7 +151,10 @@
         attrs.append(pymqi.CFIN(Parameter=pymqi.CMQC.MQIA_CHECK_CLIENT_BINDING,
                                 Value=pymqi.CMQCFC.MQCHK_REQUIRED_ADMIN))
         attrs.append(pymqi.CFIN(Parameter=pymqi.CMQCFC.MQIACH_USER_SOURCE,
-                                Value=pymqi.CMQC.MQUSRC_CHANNEL))
+                                Value=pymqi.CMQC.MQUSRC_MAP))
+        attrs.append(pymqi.CFST(Parameter=pymqi.CMQCFC.MQCACH_MCA_USER_ID,
+                                String=b'mqm'))
+
         self.create_auth_rec(attrs)
 
         attrs = []
@@ -160,7 +163,7 @@
         attrs.append(pymqi.CFIN(Parameter=pymqi.CMQCFC.MQIACF_CHLAUTH_TYPE,
                                 Value=pymqi.CMQCFC.MQCAUT_BLOCKUSER))
         attrs.append(pymqi.CFST(Parameter=pymqi.CMQCFC.MQCACH_MCA_USER_ID_LIST,
-                                String='nobody'))
+                                String=b'nobody'))
         attrs.append(pymqi.CFIN(Parameter=pymqi.CMQCFC.MQIACH_WARNING,
                                 Value=pymqi.CMQC.MQWARN_NO))
         attrs.append(pymqi.CFIN(Parameter=pymqi.CMQCFC.MQIACF_ACTION,
@@ -224,4 +227,4 @@
         self.assertTrue(is_connected)
 
 if __name__ == '__main__':
-    main(module='test_pcf')
+    main(module='test_tls')
diff --git a/setup.py b/setup.py
new file mode 100644
index 0000000000000000000000000000000000000000..449e17b8dd577683bf0f5604aab71219ce4ff180_c2V0dXAucHk=
--- /dev/null
+++ b/setup.py
@@ -0,0 +1,202 @@
+# Setup script usable for distutils.
+#
+# Original Author: Maas-Maarten Zeeman
+#
+# Linux build is re-entrant/multithreaded.
+
+# stdlib
+import os
+import sys
+try:
+    from setuptools import setup
+except ImportError:
+    from distutils.core import setup
+
+from distutils.core import Extension
+from distutils import spawn
+from struct import calcsize
+
+version = '1.11.1'
+
+# Build either in bindings or client mode.
+bindings_mode = 0
+if sys.argv[-1] in ('bindings', 'server'):
+    bindings_mode = 1
+    sys.argv = sys.argv[:-1]
+if sys.argv[-1] == 'client':
+    bindings_mode = 0
+    sys.argv = sys.argv[:-1]
+
+# Are we running 64bits?
+if calcsize('P') == 8:
+    bits = 64
+else:
+    bits = 32
+
+def get_windows_settings():
+    """ Windows settings.
+    """
+    if bits == 64:
+        library_dirs = [r'c:\Program Files (x86)\IBM\WebSphere MQ\tools\Lib64',
+                        r'{}\tools\Lib64'.format(os.environ['MQ_FILE_PATH'])]
+        include_dirs = [r'c:\Program Files (x86)\IBM\WebSphere MQ\tools\c\include',
+                        r'{}\tools\c\include'.format(os.environ['MQ_FILE_PATH'])]
+    else:
+        library_dirs = [r'c:\Program Files\IBM\WebSphere MQ\Tools\Lib',
+                        r'{}\tools\Lib'.format(os.environ['MQ_FILE_PATH'])]
+        include_dirs = [r'c:\Program Files\IBM\WebSphere MQ\tools\c\include',
+                        r'{}\tools\c\include'.format(os.environ['MQ_FILE_PATH'])]
+
+    if bindings_mode:
+        libraries = ['mqm']
+    else:
+        if bits == 64:
+            libraries = ['mqic']
+        else:
+            libraries = ['mqic32']
+
+    return library_dirs, include_dirs, libraries
+
+def get_sunos_zlinux_settings():
+    """ SunOS and z/Linux settings.
+    """
+    if bits == 64:
+        library_dirs = ['/opt/mqm/lib64']
+    else:
+        library_dirs = ['/opt/mqm/lib']
+
+    include_dirs = ['/opt/mqm/inc']
+
+    if bindings_mode:
+        libraries = ['mqm','mqmcs','mqmzse']
+    else:
+        libraries = ['mqic']
+
+    return library_dirs, include_dirs, libraries
+
+def get_aix_settings():
+    """ AIX settings.
+    """
+    if bits == 64:
+        library_dirs = ['/usr/mqm/lib64']
+    else:
+        library_dirs = ['/usr/mqm/lib']
+
+    include_dirs = ['/usr/mqm/inc']
+
+    if bindings_mode:
+        libraries = ['mqm_r']
+    else:
+        libraries = ['mqic_r']
+
+    return library_dirs, include_dirs, libraries
+
+def get_generic_unix_settings():
+    """ Generic UNIX, including Linux, settings.
+    """
+    if bits == 64:
+        library_dirs = ['/opt/mqm/lib64']
+    else:
+        library_dirs = ['/opt/mqm/lib']
+
+    include_dirs = ['/opt/mqm/inc']
+
+    if bindings_mode:
+        libraries = ['mqm_r']
+    else:
+        libraries = ['mqic_r']
+
+    return library_dirs, include_dirs, libraries
+
+def get_locations_by_command_path(command_path):
+    """ Extracts directory locations by the path to one of MQ commands, such as dspmqver.
+    """
+    command_dir = os.path.dirname(command_path)
+    mq_installation_path = os.path.abspath(os.path.join(command_dir, '..'))
+
+    if bits == 64:
+        library_dirs = ['{}/lib64'.format(mq_installation_path)]
+    else:
+        library_dirs = ['{}/lib'.format(mq_installation_path)]
+
+    include_dirs = ['{}/inc'.format(mq_installation_path)]
+
+    if bindings_mode:
+        libraries = ['mqm_r']
+    else:
+        libraries = ['mqic_r']
+
+    return library_dirs, include_dirs, libraries
+
+# Windows
+if sys.platform == 'win32':
+    library_dirs, include_dirs, libraries = get_windows_settings()
+
+# SunOS and z/Linux
+elif sys.platform == 'sunos5' or sys.platform == 'linux-s390':
+    library_dirs, include_dirs, libraries = get_sunos_zlinux_settings()
+
+# AIX
+elif sys.platform.startswith('aix'):
+    library_dirs, include_dirs, libraries = get_aix_settings()
+
+# At this point, to preserve backward-compatibility we try out generic
+# UNIX settings first, i.e. libraries and include files in well-known locations.
+# Otherwise, to support Mac, we look up dspmqver in $PATH.
+else:
+
+    has_generic_lib = os.path.exists('/opt/mqm/lib64') if bits == 64 else os.path.exists('/opt/mqm/lib')
+
+    if has_generic_lib:
+        library_dirs, include_dirs, libraries = get_generic_unix_settings()
+
+    else:
+
+        # On Mac, users can install MQ to any location so we look up
+        # the path that dspmqver is installed to and find the rest
+        # of the information needed in relation to that base directory.
+        dspmqver_path = spawn.find_executable('dspmqver')
+
+        # We have found the command so we will be able to extract the relevant directories now
+        if dspmqver_path:
+            library_dirs, include_dirs, libraries = get_locations_by_command_path(dspmqver_path)
+
+        else:
+            raise Exception('MQ libraries could not be found')
+
+if bindings_mode:
+    print('Building PyMQI bindings mode %sbits' % bits)
+else:
+    print('Building PyMQI client mode %sbits' % bits)
+
+print('Using library_dirs:`%s`, include:`%s`, libraries:`%s`' % (library_dirs, include_dirs, libraries))
+
+setup(name = 'pymqi',
+    version = version,
+    description = 'Python IBM MQI Extension for IBM MQ (formerly WebSphere MQ and MQSeries).',
+    long_description= 'PyMQI is a Python library for working with IBM MQ (formerly WebSphere MQ and MQSeries) implementing MQI and PCF protocols.',
+    author='Dariusz Suchojad',
+    author_email='pymqi@m.zato.io',
+    url='https://dsuch.github.io/pymqi/',
+    download_url='https://pypi.python.org/pypi/pymqi',
+    platforms='OS Independent',
+    package_dir = {'': 'code'},
+    packages = ['pymqi'],
+    license='Python Software Foundation License',
+    keywords=('pymqi IBM MQ WebSphere WMQ MQSeries IBM middleware messaging queueing asynchronous SOA EAI ESB integration'),
+    classifiers = [
+        'Development Status :: 5 - Production/Stable',
+        'License :: OSI Approved :: Python Software Foundation License',
+        'Intended Audience :: Developers',
+        'Natural Language :: English',
+        'Operating System :: OS Independent',
+        'Programming Language :: C',
+        'Programming Language :: Python',
+        'Topic :: Software Development :: Libraries :: Python Modules',
+        'Topic :: Software Development :: Object Brokering',
+        ],
+    py_modules = ['pymqi.CMQC', 'pymqi.CMQCFC', 'pymqi.CMQXC', 'pymqi.CMQZC'],
+    ext_modules = [Extension('pymqi.pymqe',['code/pymqi/pymqe.c'], define_macros=[('PYQMI_BINDINGS_MODE_BUILD', bindings_mode)],
+        library_dirs = library_dirs,
+        include_dirs = include_dirs,
+        libraries = libraries)])
diff --git a/tox.ini b/tox.ini
index 1ba572cd9ec5f51b4c469bf2a4436100113dfe3a_dG94LmluaQ==..449e17b8dd577683bf0f5604aab71219ce4ff180_dG94LmluaQ== 100644
--- a/tox.ini
+++ b/tox.ini
@@ -24,3 +24,41 @@
 # W291 trailing whitespace
 # W293 blank line contains whitespace
 ignore=E112,E121,E122,E123,E124,E125,E126,E127,E128,E129,E131,E225,E226,E231,E251,E261,E302,E401,E713,F403,W291,W293
+
+
+[testenv]
+deps =
+    -rcode/tests/requirements.txt
+    pytest-cov
+
+commands =
+    py.test -x --cov=pymqi --cov-config=.coveragerc
+    ; flake8 code/pymqi code/tests
+
+setenv =
+    PYMQI_TEST_QM_NAME = MQTEST
+    PYMQI_TEST_QM_HOST = localhost
+    PYMQI_TEST_QM_TRANSPORT = TCP
+
+[testenv:local]
+setenv =
+    PYMQI_TEST_QM_PORT = 8887
+    PYMQI_TEST_QM_CHANNEL = CH1
+    PYMQI_TEST_QM_USER =
+    PYMQI_TEST_QM_PASSWORD =
+    PYMQI_TEST_QM_CONN_AUTH_SUPPORTED = 0
+
+[testenv:docker]
+# Environment for testing with
+# Docker image ibmcom/mq used as MQ Server
+setenv = PYMQI_TEST_QM_PORT = 8886
+    PYMQI_TEST_QM_CHANNEL = DEV.ADMIN.SVRCONN
+    PYMQI_TEST_QM_USER = admin
+    PYMQI_TEST_QM_PASSWORD = passw0rd
+    PYMQI_TEST_QM_CONN_AUTH_USE_PW = REQUIRED
+    # Without HOME error AMQ6235E occurs
+    HOME = /home/runner
+passenv =
+    PYMQI_TEST_TLS_SKIP
+    PYMQI_TEST_TLS_KEY_REPO_LOCATION_QMGR
+    PYMQI_TEST_TLS_KEY_REPO_LOCATION_CLIENT