From 0b6077fcd74759b097964d49ccb57e36bd86f9bb Mon Sep 17 00:00:00 2001 From: Tommy Carpenter Date: Thu, 22 Aug 2019 09:34:57 -0400 Subject: [PATCH] Greatly improve test coverage Change-Id: Iaddc5fb3a365b34b91c3ae15649b4bb43f7f69a7 Signed-off-by: Tommy Carpenter --- src/bindings/rmr-python/docs/Changelog.rst | 9 ++ src/bindings/rmr-python/docs/source/index.rst | 15 ++-- src/bindings/rmr-python/rmr/rmr.py | 4 + src/bindings/rmr-python/rmr/rmr_mocks/rmr_mocks.py | 4 +- src/bindings/rmr-python/setup.py | 2 +- src/bindings/rmr-python/tests/conftest.py | 91 +++++++++++---------- .../rmr-python/tests/fixtures/test_local.rt | 3 + src/bindings/rmr-python/tests/test_rmr.py | 95 ++++++++++++++++------ src/bindings/rmr-python/tox.ini | 8 +- 9 files changed, 151 insertions(+), 80 deletions(-) create mode 100644 src/bindings/rmr-python/tests/fixtures/test_local.rt diff --git a/src/bindings/rmr-python/docs/Changelog.rst b/src/bindings/rmr-python/docs/Changelog.rst index 894ce3d..e758354 100644 --- a/src/bindings/rmr-python/docs/Changelog.rst +++ b/src/bindings/rmr-python/docs/Changelog.rst @@ -7,6 +7,15 @@ The format is based on `Keep a Changelog `__ and this project adheres to `Semantic Versioning `__. +[0.11.0] - 8/21/2019 +-------------------- + +:: + * Overhaul unit tests to remove mocking from the rmr tests, which gives much greater confidence in changing the code. More is still needed however, specifically test sends and test receives. + * Adds an alias rmr_set_meid to rmr_bytes2meid for naming consistency. + * Found a possible inconsistency/bug that requires further investigation later; setting meid takes bytes, but getting it returns a string. + + [0.10.8] - 8/20/2019 -------------------- diff --git a/src/bindings/rmr-python/docs/source/index.rst b/src/bindings/rmr-python/docs/source/index.rst index 29a6c94..8b354da 100644 --- a/src/bindings/rmr-python/docs/source/index.rst +++ b/src/bindings/rmr-python/docs/source/index.rst @@ -40,18 +40,23 @@ are not yet implemented in this library (do we need them?): Unit Testing ============ -You can unit test outside of docker, or in docker. Unit testing outside of docker currently requires rmr to be installed. +You can unit test in docker or outside of docker. +The preferred method (by far) is to use Docker, because it encapsulates rmr, as well as ensuring that ports that rmr opens do not conflict with the host machine :: - tox - open htmlcov/index.html + docker build -t rmrunittestt:latest -f Dockerfile-Unit-Test . + +A coverage report will be shown in stdout. -Or, use the provided Dockerfile (building is enough): +It is possible to run tox locally provided that rmr is intalled, and you are prepared to have the test open ports (4562) as ``rmr_init`` must succeed for the tests to proceed. :: - docker build -t rmrunittestt:latest -f Dockerfile-Unit-Test . + tox + open htmlcov/index.html + +The added benefit of the local option is that the coverage report can be viewed in html, which I find easier to read than the term coverage reort the docker build will print. Installation ============ diff --git a/src/bindings/rmr-python/rmr/rmr.py b/src/bindings/rmr-python/rmr/rmr.py index 9d21b88..7e5d493 100644 --- a/src/bindings/rmr-python/rmr/rmr.py +++ b/src/bindings/rmr-python/rmr/rmr.py @@ -309,6 +309,10 @@ def rmr_bytes2meid(ptr_mbuf, src, length): return _rmr_bytes2meid(ptr_mbuf, src, length) +# this is an alias to rmr_bytes2meid using familiar set/get terminoloigy +rmr_set_meid = rmr_bytes2meid + + # CAUTION: Some of the C functions expect a mutable buffer to copy the bytes into; # if there is a get_* function below, use it to set up and return the # buffer properly. diff --git a/src/bindings/rmr-python/rmr/rmr_mocks/rmr_mocks.py b/src/bindings/rmr-python/rmr/rmr_mocks/rmr_mocks.py index 6037e1f..239ac2b 100644 --- a/src/bindings/rmr-python/rmr/rmr_mocks/rmr_mocks.py +++ b/src/bindings/rmr-python/rmr/rmr_mocks/rmr_mocks.py @@ -34,7 +34,7 @@ def rcv_mock_generator(msg_payload, msg_type, msg_state, jsonb, timeout=0): sbuf.contents.payload = payload sbuf.contents.len = len(payload) sbuf.contents.state = msg_state - if msg_state != 0: # set something in transport state if 'error' + if msg_state != 0: # set something in transport state if 'error' sbuf.contents.tp_state = 99 else: sbuf.contents.tp_state = 0 @@ -52,7 +52,7 @@ def send_mock_generator(msg_state): def f(_unused, sbuf): sbuf.contents.state = msg_state - if msg_state != 0: # set something in transport state if 'error' + if msg_state != 0: # set something in transport state if 'error' sbuf.contents.tp_state = 99 else: sbuf.contents.tp_state = 0 diff --git a/src/bindings/rmr-python/setup.py b/src/bindings/rmr-python/setup.py index 06f4c14..ebdc471 100644 --- a/src/bindings/rmr-python/setup.py +++ b/src/bindings/rmr-python/setup.py @@ -32,7 +32,7 @@ def _long_descr(): setup( name="rmr", - version="0.10.8", + version="0.11.0", packages=find_packages(), author="Tommy Carpenter, E. Scott Daniels", description="Python wrapper for RIC RMR", diff --git a/src/bindings/rmr-python/tests/conftest.py b/src/bindings/rmr-python/tests/conftest.py index 51ddda9..3019352 100644 --- a/src/bindings/rmr-python/tests/conftest.py +++ b/src/bindings/rmr-python/tests/conftest.py @@ -17,51 +17,56 @@ import pytest +# These are here just to reduce the size of the code in test_rmr so those (important) tests are more readable; in theory these dicts could be large @pytest.fixture -def fake_consts(): - return {"RMR_MAX_XID": 32, - "RMR_MAX_SID": 32, - "RMR_MAX_MEID": 32, - "RMR_MAX_SRC": 64, - "RMR_MAX_RCV_BYTES": 4096, - "RMRFL_NONE": 0, - "RMRFL_AUTO_ALLOC": 1, - "RMR_DEF_SIZE": 0, - "RMR_VOID_MSGTYPE": -1, - "RMR_VOID_SUBID": -1, - "RMR_OK": 0, - "RMR_ERR_BADARG": 1, - "RMR_ERR_NOENDPT": 2, - "RMR_ERR_EMPTY": 3, - "RMR_ERR_NOHDR": 4, - "RMR_ERR_SENDFAILED": 5, - "RMR_ERR_CALLFAILED": 6, - "RMR_ERR_NOWHOPEN": 7, - "RMR_ERR_WHID": 8, - "RMR_ERR_OVERFLOW": 9, - "RMR_ERR_RETRY": 10, - "RMR_ERR_RCVFAILED": 11, - "RMR_ERR_TIMEOUT": 12, - "RMR_ERR_UNSET": 13, - "RMR_ERR_TRUNC": 14, - "RMR_ERR_INITFAILED": 15} +def expected_constants(): + return { + "RMR_MAX_XID": 32, + "RMR_MAX_SID": 32, + "RMR_MAX_MEID": 32, + "RMR_MAX_SRC": 64, + "RMR_MAX_RCV_BYTES": 4096, + "RMRFL_NONE": 0, + "RMRFL_AUTO_ALLOC": 3, + "RMR_DEF_SIZE": 0, + "RMR_VOID_MSGTYPE": -1, + "RMR_VOID_SUBID": -1, + "RMR_OK": 0, + "RMR_ERR_BADARG": 1, + "RMR_ERR_NOENDPT": 2, + "RMR_ERR_EMPTY": 3, + "RMR_ERR_NOHDR": 4, + "RMR_ERR_SENDFAILED": 5, + "RMR_ERR_CALLFAILED": 6, + "RMR_ERR_NOWHOPEN": 7, + "RMR_ERR_WHID": 8, + "RMR_ERR_OVERFLOW": 9, + "RMR_ERR_RETRY": 10, + "RMR_ERR_RCVFAILED": 11, + "RMR_ERR_TIMEOUT": 12, + "RMR_ERR_UNSET": 13, + "RMR_ERR_TRUNC": 14, + "RMR_ERR_INITFAILED": 15, + } @pytest.fixture def expected_states(): - return {0: "RMR_OK", - 1: "RMR_ERR_BADARG", - 2: "RMR_ERR_NOENDPT", - 3: "RMR_ERR_EMPTY", - 4: "RMR_ERR_NOHDR", - 5: "RMR_ERR_SENDFAILED", - 6: "RMR_ERR_CALLFAILED", - 7: "RMR_ERR_NOWHOPEN", - 8: "RMR_ERR_WHID", - 9: "RMR_ERR_OVERFLOW", - 10: "RMR_ERR_RETRY", - 11: "RMR_ERR_RCVFAILED", - 12: "RMR_ERR_TIMEOUT", - 13: "RMR_ERR_UNSET", - 14: "RMR_ERR_TRUNC", - 15: "RMR_ERR_INITFAILED"} + return { + 0: "RMR_OK", + 1: "RMR_ERR_BADARG", + 2: "RMR_ERR_NOENDPT", + 3: "RMR_ERR_EMPTY", + 4: "RMR_ERR_NOHDR", + 5: "RMR_ERR_SENDFAILED", + 6: "RMR_ERR_CALLFAILED", + 7: "RMR_ERR_NOWHOPEN", + 8: "RMR_ERR_WHID", + 9: "RMR_ERR_OVERFLOW", + 10: "RMR_ERR_RETRY", + 11: "RMR_ERR_RCVFAILED", + 12: "RMR_ERR_TIMEOUT", + 13: "RMR_ERR_UNSET", + 14: "RMR_ERR_TRUNC", + 15: "RMR_ERR_INITFAILED", + } diff --git a/src/bindings/rmr-python/tests/fixtures/test_local.rt b/src/bindings/rmr-python/tests/fixtures/test_local.rt new file mode 100644 index 0000000..4f09782 --- /dev/null +++ b/src/bindings/rmr-python/tests/fixtures/test_local.rt @@ -0,0 +1,3 @@ +newrt|start +rte|10001|localhost:4562 +newrt|end diff --git a/src/bindings/rmr-python/tests/test_rmr.py b/src/bindings/rmr-python/tests/test_rmr.py index fdc21ed..f3d0b75 100644 --- a/src/bindings/rmr-python/tests/test_rmr.py +++ b/src/bindings/rmr-python/tests/test_rmr.py @@ -14,54 +14,95 @@ # See the License for the specific language governing permissions and # limitations under the License. # ================================================================================== +import time import pytest -import json from rmr import rmr -from rmr.rmr_mocks import rmr_mocks -MRC = None SIZE = 256 +MRC = None + + +def setup_module(): + global MRC + MRC = rmr.rmr_init(b"4562", rmr.RMR_MAX_RCV_BYTES, 0x00) + while rmr.rmr_ready(MRC) == 0: + time.sleep(1) -def test_get_mapping_dict(monkeypatch, fake_consts, expected_states): +def teardown_module(): + rmr.rmr_close(MRC) + + +def _assert_new_sbuf(sbuf): """ - test getting mapping string + verify the initial state of an alloced message is what we expect """ + summary = rmr.message_summary(sbuf) + assert summary["payload"] == b"" + assert summary["payload length"] == 0 + assert summary["transaction id"] == b"" + assert summary["message state"] == 0 + assert summary["message status"] == "RMR_OK" + assert summary["meid"] is None + assert summary["errno"] == 0 - def fake_rmr_get_consts(): - return json.dumps(fake_consts).encode("utf-8") - monkeypatch.setattr("rmr.rmr._rmr_const", fake_rmr_get_consts) - assert rmr._get_mapping_dict() == expected_states - # do again, trigger cache line coverage - assert rmr._get_mapping_dict() == expected_states +def test_get_constants(expected_constants): + """ + test getting constants + """ + assert rmr._get_constants() == expected_constants + +def test_get_mapping_dict(expected_states): + """ + test getting mapping string + """ + assert rmr._get_mapping_dict() == expected_states assert rmr._state_to_status(0) == "RMR_OK" assert rmr._state_to_status(12) == "RMR_ERR_TIMEOUT" assert rmr._state_to_status(666) == "UNKNOWN STATE" -def test_meid_prettify(monkeypatch): - rmr_mocks.patch_rmr(monkeypatch) - - # here we re-monkey get_meid - monkeypatch.setattr("rmr.rmr.get_meid", lambda _: "yoooo") +def test_meid_prettify(): + """ + test the printing of meid based on it's value + """ + # TODO?? weirdness: setting it takes bytes, but getting it returns a string. This does NOT happen for payload; bytes in, bytes come out. sbuf = rmr.rmr_alloc_msg(MRC, SIZE) + rmr.rmr_set_meid(sbuf, b"\x00" * 32, 32) summary = rmr.message_summary(sbuf) - assert summary["meid"] == "yoooo" + assert summary["meid"] is None # summary does a pretty print" of 32 null bytes + assert rmr.get_meid(sbuf) == "\x00" * 32 # real underlying value + - # test bytes - monkeypatch.setattr("rmr.rmr.get_meid", lambda _: b"\x01\x00f\x80") +def test_rmr_set_get(): + """ + test set functions + """ sbuf = rmr.rmr_alloc_msg(MRC, SIZE) + _assert_new_sbuf(sbuf) + + # test payload + pay = b"\x01\x00\x80" + rmr.set_payload_and_length(pay, sbuf) summary = rmr.message_summary(sbuf) - assert summary["meid"] == b"\x01\x00f\x80" + assert summary["payload"] == pay + assert summary["payload length"] == 3 - # test the cleanup of null bytes - monkeypatch.setattr( - "rmr.rmr.get_meid", - lambda _: "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", - ) - sbuf = rmr.rmr_alloc_msg(MRC, SIZE) + # test transid (note we cant test payload because it's randomly gen) + assert summary["transaction id"] == b"" + assert len(summary["transaction id"]) == 0 + rmr.generate_and_set_transaction_id(sbuf) + summary = rmr.message_summary(sbuf) + assert summary["transaction id"] != b"" + assert len(summary["transaction id"]) == 32 + + # test meid + assert rmr.get_meid(sbuf) == "\x00" * 32 + assert summary["meid"] is None # the summary printing function shows the above horridness as None + rmr.rmr_set_meid(sbuf, b"666", 3) summary = rmr.message_summary(sbuf) - assert summary["meid"] == None + # TODO?? weirdness: setting it takes bytes, but getting it returns a string. This does NOT happen for payload; bytes in, bytes come out. + assert rmr.get_meid(sbuf) == summary["meid"] == "666" + "\x00" * 29 diff --git a/src/bindings/rmr-python/tox.ini b/src/bindings/rmr-python/tox.ini index cb7cf77..f67ca14 100644 --- a/src/bindings/rmr-python/tox.ini +++ b/src/bindings/rmr-python/tox.ini @@ -22,8 +22,12 @@ deps= pytest coverage pytest-cov -setenv = LD_LIBRARY_PATH = /usr/local/lib/:/usr/local/lib64 -commands=pytest --verbose --cov {envsitepackagesdir}/rmr --cov-report term-missing +setenv = + LD_LIBRARY_PATH = /usr/local/lib/:/usr/local/lib64 + RMR_SEED_RT = tests/fixtures/test_local.rt + + +commands=pytest --verbose --cov {envsitepackagesdir}/rmr --cov-report term-missing --cov-report html [testenv:flake8] basepython = python3.7 -- 2.16.6