hideakitai / MsgPacketizer

msgpack based serializer / deserializer + packetize for Arduino, ROS, and more
MIT License
79 stars 10 forks source link

How to write Unit tests #2

Closed dwjbosman closed 4 years ago

dwjbosman commented 4 years ago

I would like to write unit tests. The idea is that I inject a buffer containing packetized and encoded data setup by the test. Then I call the "parse" method and test if my callback functions correctly:

static int8_t global_received_val = 0; // setup a callback MsgPacketizer::subscribe(Serial, [&](const uint8_t index, MsgPack::Unpacker& unpacker) { // store the received value in a place accessible to the test unpacker.unpackInt8(global_received_val); } auto &pm = MsgPacketizer::PackerManager::getInstance(); auto &packer = pm.getPacker(); packer.clear(); int8_t expected = 100; packer.serialize(expected); // serialize some data // simulate reception via Serial by injecting the packed and encoded buffer auto unpacker = MsgPacketizer::UnpackerManager::getInstance().getUnpackerRef(Serial); unpacker->clear(); unpacker->feed(packer.data(), packer.size()); MsgPacketizer::parse(); // test if the callback fired and correctly handled the message assertEqual(global_received_val, expected);

This code doesn't work however.

  1. The parse function checks the stream if data is available and as there is no data available (it is already stored in the unpacker) the parse function returns.
  2. I think my use of Packer - Unpacker skips the Encoder/Decoder. I would like to take these into account also during the test.

Can you show how to perform the test?

hideakitai commented 4 years ago

Hi, parse() works as follows:

  1. decode COBS + CRC ( -> MessagePack)
  2. decode MessagePack

And Packetizer is used in first step. https://github.com/hideakitai/MsgPacketizer/blob/be4922cfb7310ce4755e52c93f97ea23c48dab98/MsgPacketizer.h#L464

So you should feed data to Packetizer.

const size_t size;
uint8_t data[size];

auto cobs_decoder = Packetizer::getDecoderRef(Serial);
cobs_decoder->feed(data_ptr, size);
// if data is correct, callback will be called here
dwjbosman commented 4 years ago

I have been able to get it working:

static auto &pm = MsgPacketizer::PackerManager::getInstance(); static auto &packer = pm.getPacker(); packer.clear(); packer.serialize(header); packer.serialize(MSG_ID_SET_STATUS); packer.packBool(true); auto &packet = Packetizer::encode(MSG_DST_RTD_PLUGIN, packer.data(), packer.size()); auto decoder = Packetizer::DecodeManager::getInstance().getDecoderRef(Serial); decoder->reset(); // the callback is called when the feed operation is performed decoder->feed(packet.data.data(), packet.data.size());

I am using this on Arduino. I had some memory issues. With the code above the MsgPacketizer library performs (with the default settings) a number of dynamic allocations of at least 142, 112, 4, 109, 43 = 410 bytes in total, which is quite a lot if only 2048 bytes are available.

Is this memory usage typical (using the default settings)?

hideakitai commented 4 years ago

@dwjbosman Maybe this library is too rich for AVR boards like Arduino Uno, because many features (STL) are added to enable easy data manipulation which is generally not included in Uno. Simple examples in MsgPacketizer, MsgPack and Packetizer uses about 500-600 bytes by default setting for easier usage than plain Arduino.

There are several ways to reduce heap memory size, so I can let you know how to reduce it if you have the simplified ino file to describe usage you want to.

For example, you just need MsgPack encoding or you just need cobs + crc + index packet verifications, or both.

Or I recommend to use cheap but powerful boards like ESP32...

hideakitai commented 4 years ago

@dwjbosman Is there something else? Please re-open the issue if you have any question about testing. Thanks.