diff --git a/CMakeLists.txt b/CMakeLists.txt index 9c1dba13..6da8779a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -27,7 +27,6 @@ target_sources( ${NOTE_C_SRC_DIR}/n_b64.c ${NOTE_C_SRC_DIR}/n_cjson.c ${NOTE_C_SRC_DIR}/n_cjson_helpers.c - ${NOTE_C_SRC_DIR}/n_cobs.c ${NOTE_C_SRC_DIR}/n_const.c ${NOTE_C_SRC_DIR}/n_ftoa.c ${NOTE_C_SRC_DIR}/n_helpers.c diff --git a/n_cobs.c b/n_cobs.c deleted file mode 100644 index 2a654fe3..00000000 --- a/n_cobs.c +++ /dev/null @@ -1,135 +0,0 @@ -// Copyright 2023 Blues Inc. All rights reserved. -// Use of this source code is governed by licenses granted by the -// copyright holder including that found in the LICENSE file. - -#include - -//**************************************************************************/ -/*! - @brief Decode a string encoded with COBS encoding - - @details Because the decoded length is guaranteed to be less than or equal to - length, the decode may be done in-place. Default behavior (with eop == 0) is - to restore all '\0' into output data, but if a different value is specified, - the input is XOR'ed such that THAT byte is the one that is assumed can't be - in the input. - - @param ptr Pointer to the data to decode - @param length Length of the data to decode - @param eop Byte to use as the end-of-packet marker - @param dst Pointer to the buffer for the decoded data - - @return the length of the decoded data - */ -/**************************************************************************/ -uint32_t cobsDecode(uint8_t *ptr, uint32_t length, uint8_t eop, uint8_t *dst) -{ - const uint8_t *start = dst, *end = ptr + length; - uint8_t code = 0xFF, copy = 0; - for (; ptr < end; copy--) { - if (copy != 0) { - *dst++ = (*ptr++) ^ eop; - } else { - if (code != 0xFF) { - *dst++ = 0; - } - copy = code = (*ptr++) ^ eop; - if (code == 0) { - break; - } - } - } - return dst - start; -} - -//**************************************************************************/ -/*! - @brief Encode a string with Consistent Overhead Byte Stuffing (COBS) encoding - - @details behavior (with eop == 0) is to eliminate all '\0' from input data, - but if a different value is specified, the output is XOR'ed such that THAT - byte is the one that won't be contained in output. - https://en.wikipedia.org/wiki/Consistent_Overhead_Byte_Stuffing - - @param ptr Pointer to the data to encode - @param length Length of the data to encode - @param eop Byte to use as the end-of-packet marker - @param dst Pointer to the buffer for the encoded data - - @return the length of the encoded data - - @note You may use `cobsEncodedLength()` to calculate the required size for - the buffer pointed to by the `dst` parameter. - - @see cobsEncodedLength() - */ -/**************************************************************************/ -uint32_t cobsEncode(uint8_t *ptr, uint32_t length, uint8_t eop, uint8_t *dst) -{ - uint8_t ch; - uint8_t *start = dst; - uint8_t code = 1; - uint8_t *code_ptr = dst++; // Where to insert the leading count - while (length--) { - ch = *ptr++; - if (ch != 0) { // Input byte not zero - *dst++ = ch ^ eop; - code++; - } - if (ch == 0 || code == 0xFF) { // Input is zero or complete block - *code_ptr = code ^ eop; - code = 1; - code_ptr = dst++; - } - } - *code_ptr = code ^ eop; // Final code - return (dst - start); -} - -//**************************************************************************/ -/*! - @brief Compute the encoding length of unencoded data - - @param ptr Pointer to the data to encode - @param length Length of the data to encode - - @return the length required for encoded data - @note The computed length includes the NULL terminator - */ -/**************************************************************************/ -uint32_t cobsEncodedLength(const uint8_t *ptr, uint32_t length) -{ - uint8_t ch; - uint32_t dst = 1; - uint8_t code = 1; - while (length--) { - ch = *ptr++; - if (ch != 0) { // Input byte not zero - dst++; - code++; - } - if (ch == 0 || code == 0xFF) { // Input is zero or complete block - code = 1; - dst++; - } - } - return dst; -} - -//**************************************************************************/ -/*! - @brief Compute the maximum length of unencoded data that can fit into a - buffer of specified length. - - @param bufLen Length of the buffer in bytes - - @return the length of unencoded data - @note The computation may leave additional space at the end, including one - byte for a NULL terminator. - */ -/**************************************************************************/ -uint32_t cobsGuaranteedFit(uint32_t bufLen) -{ - uint32_t cobsOverhead = 1 + (bufLen / 254) + 1; - return (cobsOverhead > bufLen ? 0 : (bufLen - cobsOverhead)); -} diff --git a/n_helpers.c b/n_helpers.c index 95e4b2c5..c5bcfa72 100644 --- a/n_helpers.c +++ b/n_helpers.c @@ -81,182 +81,6 @@ NOTE_C_STATIC void setTime(JTIME seconds); NOTE_C_STATIC bool timerExpiredSecs(uint32_t *timer, uint32_t periodSecs); NOTE_C_STATIC int ytodays(int year); -//**************************************************************************/ -/*! - @brief Receive a large binary object from the Notecard's binary buffer - @param buffer A buffer to hold the result - @param bufLen The total length of the buffer - @returns NULL on success, else an error string pointer. - @note Buffers are decoded in place, the original contents of the buffer will - be modified. -*/ -/**************************************************************************/ -const char * NoteBinaryReceive(uint8_t * buffer, size_t bufLen) -{ - // Issue a "card.binary" request. - J *rsp = NoteRequestResponse(NoteNewRequest("card.binary")); - if (!rsp) { - return ERRSTR("unable to issue binary request", c_err); - } - - // Ensure the transaction doesn't return an error - // to confirm the binary feature is available - if (NoteResponseError(rsp)) { - JDelete(rsp); - return ERRSTR("feature not implemented", c_bad); - } - - // Examine "cobs" from the response to evaluate the space - // required to hold the COBS encoded data on the Notecard. - const size_t cobs = JGetInt(rsp,"cobs"); - JDelete(rsp); - if (!cobs) { - return ERRSTR("no data on notecard", c_err); - } - if (cobs > bufLen) { - return ERRSTR("insufficient buffer size", c_err); - } - - // Issue `card.binary.get` and capture `"status"` from response - char status[NOTE_MD5_HASH_STRING_SIZE] = {0}; - J *req = NoteNewRequest("card.binary.get"); - if (req) { - JAddIntToObject(req, "cobs", cobs); - - // Ensure the transaction doesn't return an error. - J *rsp = NoteRequestResponse(req); - if NoteResponseError(rsp) { - JDelete(rsp); - return ERRSTR("failed to initialize binary transaction", c_err); - } - - // Examine "status" from the response to evaluate the MD5 checksum. - strlcpy(status, JGetString(rsp,"status"), NOTE_MD5_HASH_STRING_SIZE); - JDelete(rsp); - } - - // `NoteReceiveBytes()` of the COBS encoded bytes either in a single IO of - // `cobsLen`, OR receive until newline and verify that you received - // `cobsLen` bytes, depending upon platform API - (void)buffer; - - // Decode it in-place which is safe because decoding shrinks - const size_t decLen = cobsDecode(buffer,bufLen,'\n',buffer); - buffer[decLen] = '\0'; - - // Verify MD5 - char hash_string[NOTE_MD5_HASH_STRING_SIZE] = {0}; - NoteMD5HashString(buffer, decLen, hash_string, NOTE_MD5_HASH_STRING_SIZE); - if (strncmp(hash_string, status, NOTE_MD5_HASH_STRING_SIZE)) { - return ERRSTR("data transfer error", c_err); - } - - // Return `NULL` if success, else error string pointer - return NULL; -} - -//**************************************************************************/ -/*! - @brief Transmit a large binary object to the Notecard's binary buffer - @param data A buffer with data to encode in place - @param dataLen The length of the data in the buffer - @param bufLen The total length of the buffer - @returns NULL on success, else an error string pointer. - @note Buffers are encoded in place, the buffer _MUST_ be larger than the data - to be encoded, and original contents of the buffer will be modified. - You may use `cobsEncodedLength()` to calculate the required size for - the buffer pointed to by the `dst` parameter. -*/ -/**************************************************************************/ -const char * NoteBinaryTransmit(uint8_t * data, size_t dataLen, size_t bufLen, bool append) -{ - // Issue a "card.binary" request. - J *rsp = NoteRequestResponse(NoteNewRequest("card.binary")); - if (!rsp) { - return ERRSTR("unable to issue binary request", c_err); - } - - // Ensure the transaction doesn't return an error - // to confirm the binary feature is available - if (NoteResponseError(rsp)) { - JDelete(rsp); - return ERRSTR("feature not implemented", c_bad); - } - - // Examine "length" and "max" from the response to evaluate the unencoded - // space available to "card.binary.put" on the Notecard. - const size_t len = JGetInt(rsp,"length"); - const size_t max = JGetInt(rsp,"max"); - JDelete(rsp); - if (!max) { - return ERRSTR("unexpected response", c_err); - } - const size_t remaining = (max - len); - if (dataLen > remaining) { - return ERRSTR("buffer size exceeds available memory", c_mem); - } - - // Calculate MD5 - char hash_string[NOTE_MD5_HASH_STRING_SIZE] = {0}; - NoteMD5HashString(data, dataLen, hash_string, NOTE_MD5_HASH_STRING_SIZE); - - // Encode COBS data - uint32_t encLen = cobsEncodedLength(data,dataLen); - if (encLen > bufLen) { - return ERRSTR("buffer too small for encoding", c_mem); - } - const size_t data_shift = (bufLen - dataLen); - memmove(data + data_shift, data, dataLen); - encLen = cobsEncode(data + data_shift, dataLen, '\n', data); - - // Claim Notecard Mutex - _LockNote(); - - // Issue a "card.binary.put" - J *req = NoteNewRequest("card.binary.put"); - if (req) { - JAddIntToObject(req, "cobs", encLen); - if (append) { - JAddBoolToObject(req, "append", true); - } - JAddStringToObject(req, "status", hash_string); - - // Ensure the transaction doesn't return an error. - if (!NoteRequest(req)) { - return ERRSTR("failed to initialize binary transaction", c_err); - } - } - - // Immediately send the COBS binary. - const char *errstr = _RawTransaction((char *)data, NULL, false); - - // Release Notecard Mutex - _UnlockNote(); - - // Ensure transaction was successful - if (errstr) { - return ERRSTR(errstr, c_err); - } - - // Issue a `"card.binary"` request. - rsp = NoteRequestResponse(NoteNewRequest("card.binary")); - if (!rsp) { - return ERRSTR("unable to validate request", c_err); - } - - // Ensure the transaction doesn't return an error - // to confirm your binary was received - if (NoteResponseError(rsp)) { - JDelete(rsp); - // input buffer is no longer valid - return ERRSTR("binary data invalid", c_bad); - } - JDelete(rsp); - - // Return `NULL` on success - return NULL; -} - //**************************************************************************/ /*! @brief Determine if the card time is "real" calendar/clock time, or if diff --git a/n_hooks.c b/n_hooks.c index e06cf873..5cc6dc00 100644 --- a/n_hooks.c +++ b/n_hooks.c @@ -174,7 +174,7 @@ i2cReceiveFn hookI2CReceive = NULL; // Internal hooks typedef bool (*nNoteResetFn) (void); -typedef const char * (*nTransactionFn) (const char *, char **); +typedef const char * (*nTransactionFn) (char *, char **); static nNoteResetFn notecardReset = NULL; static nTransactionFn notecardTransaction = NULL; @@ -827,7 +827,7 @@ bool NoteHardReset() or the hook has not been set. */ /**************************************************************************/ -const char *NoteJSONTransaction(const char *request, char **response) +const char *NoteJSONTransaction(char *request, char **response) { if (notecardTransaction == NULL || hookActiveInterface == interfaceNone) { return "i2c or serial interface must be selected"; diff --git a/n_i2c.c b/n_i2c.c index c768e212..e62d8abc 100644 --- a/n_i2c.c +++ b/n_i2c.c @@ -42,32 +42,32 @@ static void _DelayIO() @returns a c-string with an error, or `NULL` if no error occurred. */ /**************************************************************************/ -const char *i2cNoteTransaction(const char *request, char **response) +const char *i2cNoteTransaction(char *request, char **response) { - size_t jsonLen = strlen(request); + uint8_t *transmitBuf = (uint8_t *)request; + size_t requestLen = strlen(request); // Swap NULL terminator ('\0') with newline during transmission ('\n') - uint8_t *transmitBuf = (uint8_t *)request; - transmitBuf[jsonLen] = '\n'; + transmitBuf[requestLen++] = '\n'; // Lock over the entire transaction _LockI2C(); - // Transmit the request in chunks, but also in segments so as not to overwhelm the notecard's interrupt buffers + // Transmit the request in chunks, but also in segments so as not to + // overwhelm the notecard's interrupt buffers const char *estr; uint8_t *chunk = transmitBuf; uint16_t sentInSegment = 0; - while (jsonLen > 0) { + while (requestLen > 0) { // Constrain chunkLen to fit into 16 bits (_I2CTransmit takes the buffer // size as a uint16_t). - uint16_t chunkLen = (jsonLen > 0xFFFF) ? 0xFFFF : jsonLen; + uint16_t chunkLen = (requestLen > 0xFFFF) ? 0xFFFF : requestLen; // Constrain chunkLen to be <= _I2CMax(). chunkLen = (chunkLen > _I2CMax()) ? _I2CMax() : chunkLen; _DelayIO(); estr = _I2CTransmit(_I2CAddress(), chunk, chunkLen); if (estr != NULL) { - _Free(transmitBuf); _I2CReset(_I2CAddress()); #ifdef ERRDBG _Debug("i2c transmit: "); @@ -78,7 +78,7 @@ const char *i2cNoteTransaction(const char *request, char **response) return estr; } chunk += chunkLen; - jsonLen -= chunkLen; + requestLen -= chunkLen; sentInSegment += chunkLen; if (sentInSegment > CARD_REQUEST_I2C_SEGMENT_MAX_LEN) { sentInSegment = 0; @@ -92,7 +92,7 @@ const char *i2cNoteTransaction(const char *request, char **response) } // Restore the transmit buffer - transmitBuf[jsonLen] = '\0'; + transmitBuf[--requestLen] = '\0'; // If no reply expected, we're done if (response == NULL) { @@ -100,9 +100,9 @@ const char *i2cNoteTransaction(const char *request, char **response) return NULL; } - // Dynamically grow the buffer as we read. Note that we always put the +1 in the alloc - // so we can be assured that it can be null-terminated, which must be the case because - // our json parser requires a null-terminated string. + // Dynamically grow the buffer as we read. Note that we always put the +1 in + // the alloc so we can be assured that it can be null-terminated, which must + // be the case because our json parser requires a null-terminated string. size_t growlen = ALLOC_CHUNK; size_t jsonbufAllocLen = growlen; uint8_t *jsonbuf = (uint8_t *) _Malloc(jsonbufAllocLen+1); @@ -114,8 +114,9 @@ const char *i2cNoteTransaction(const char *request, char **response) return ERRSTR("insufficient memory",c_mem); } - // Loop, building a reply buffer out of received chunks. We'll build the reply in the same - // buffer we used to transmit, and will grow it as necessary. + // Loop, building a reply buffer out of received chunks. We'll build the + // reply in the same buffer we used to transmit, and will grow it as + // necessary. bool receivedNewline = false; size_t jsonbufLen = 0; uint16_t chunkLen = 0; @@ -160,9 +161,10 @@ const char *i2cNoteTransaction(const char *request, char **response) // We've now received the chunk jsonbufLen += chunkLen; - // If the last byte of the chunk is \n, chances are that we're done. However, just so - // that we pull everything pending from the module, we only exit when we've received - // a newline AND there's nothing left available from the module. + // If the last byte of the chunk is \n, chances are that we're done. + // However, just so that we pull everything pending from the module, we + // only exit when we've received a newline AND there's nothing left + // available from the module. if (jsonbufLen > 0 && jsonbuf[jsonbufLen-1] == '\n') { receivedNewline = true; } @@ -173,7 +175,7 @@ const char *i2cNoteTransaction(const char *request, char **response) // Constrain chunkLen to be <= _I2CMax(). chunkLen = (chunkLen > _I2CMax()) ? _I2CMax() : chunkLen; - // If there's something available on the notecard for us to receive, do it + // If there's something available on the Notecard for us to receive, do it if (chunkLen > 0) { continue; } @@ -229,9 +231,10 @@ bool i2cNoteReset() return false; } - // Synchronize by guaranteeing not only that I2C works, but that after we send \n that we drain - // the remainder of any pending partial reply from a previously-aborted session. - // If we get a failure on transmitting the \n, it means that the notecard isn't even present. + // Synchronize by guaranteeing not only that I2C works, but that after we + // send \n that we drain the remainder of any pending partial reply from a + // previously-aborted session. If we get a failure on transmitting the \n, + // it means that the notecard isn't even present. _DelayIO(); const char *transmitErr = _I2CTransmit(_I2CAddress(), (uint8_t *)"\n", 1); if (!cardTurboIO) { diff --git a/n_lib.h b/n_lib.h index 72cc8c0f..12a19858 100644 --- a/n_lib.h +++ b/n_lib.h @@ -77,9 +77,9 @@ extern "C" { #endif // Transactions -const char *i2cNoteTransaction(const char *request, char **response); +const char *i2cNoteTransaction(char *request, char **response); bool i2cNoteReset(void); -const char *serialNoteTransaction(const char *request, char **response); +const char *serialNoteTransaction(char *request, char **response); bool serialNoteReset(void); // Hooks @@ -96,7 +96,7 @@ bool NoteI2CReset(uint16_t DevAddress); const char *NoteI2CTransmit(uint16_t DevAddress, uint8_t* pBuffer, uint16_t Size); const char *NoteI2CReceive(uint16_t DevAddress, uint8_t* pBuffer, uint16_t Size, uint32_t *avail); bool NoteHardReset(void); -const char *NoteJSONTransaction(const char *request, char **response); +const char *NoteJSONTransaction(char *request, char **response); bool NoteIsDebugOutputActive(void); // Utilities @@ -104,12 +104,6 @@ void n_htoa32(uint32_t n, char *p); void n_htoa16(uint16_t n, unsigned char *p); uint64_t n_atoh(char *p, int maxlen); -// COBS Helpers -uint32_t cobsDecode(uint8_t *ptr, uint32_t length, uint8_t eop, uint8_t *dst); -uint32_t cobsEncode(uint8_t *ptr, uint32_t length, uint8_t eop, uint8_t *dst); -uint32_t cobsEncodedLength(const uint8_t *ptr, uint32_t length); -uint32_t cobsGuaranteedFit(uint32_t bufLen); - // Turbo I/O mode extern bool cardTurboIO; diff --git a/n_serial.c b/n_serial.c index 0f169ee5..83b39646 100644 --- a/n_serial.c +++ b/n_serial.c @@ -24,26 +24,15 @@ @returns a c-string with an error, or `NULL` if no error ocurred. */ /**************************************************************************/ -const char *serialNoteTransaction(const char *request, char **response) +const char *serialNoteTransaction(char *request, char **response) { - uint8_t *transmitBuf; - size_t jsonLen = strlen(request); + uint8_t *transmitBuf = (uint8_t *)request; + size_t requestLen = strlen(request); - // Append newline to the transaction - transmitBuf = (uint8_t *) _Malloc(jsonLen+c_newline_len); - bool allocated = (transmitBuf != NULL); - if (allocated) { - memcpy(transmitBuf, request, jsonLen); - memcpy(&transmitBuf[jsonLen], c_newline, c_newline_len); - jsonLen += c_newline_len; - } - else { - transmitBuf = (uint8_t *)request; - } - - // Transmit the request in segments so as not to overwhelm the notecard's interrupt buffers + // Transmit the request in segments so as not to overwhelm the Notecard's + // interrupt buffers uint32_t segOff = 0; - uint32_t segLeft = jsonLen; + uint32_t segLeft = requestLen; while (true) { size_t segLen = segLeft; if (segLen > CARD_REQUEST_SERIAL_SEGMENT_MAX_LEN) { @@ -60,23 +49,18 @@ const char *serialNoteTransaction(const char *request, char **response) } } - // Free the transmit buffer - if (allocated) { - _Free(transmitBuf); - } - else { - _SerialTransmit((uint8_t *)"\r\n", 2, true); - } + // Append newline to the transaction + _SerialTransmit((uint8_t *)"\r\n", 2, true); // If no reply expected, we're done if (response == NULL) { return NULL; } - // Wait for something to become available, processing timeout errors up-front - // because the json parse operation immediately following is subject to the - // serial port timeout. We'd like more flexibility in max timeout and ultimately - // in our error handling. + // Wait for something to become available, processing timeout errors + // up-front because the json parse operation immediately following is + // subject to the serial port timeout. We'd like more flexibility in max + // timeout and ultimately in our error handling. uint32_t startMs; for (startMs = _GetMs(); !_SerialAvailable(); ) { if (_GetMs() - startMs >= NOTECARD_TRANSACTION_TIMEOUT_SEC*1000) { @@ -90,9 +74,9 @@ const char *serialNoteTransaction(const char *request, char **response) } } - // Allocate a buffer for input, noting that we always put the +1 in the alloc so we can be assured - // that it can be null-terminated. This must be the case because json parsing requires a - // null-terminated string. + // Allocate a buffer for input, noting that we always put the +1 in the + // alloc so we can be assured that it can be null-terminated. This must be + // the case because json parsing requires a null-terminated string. int jsonbufAllocLen = ALLOC_CHUNK; char *jsonbuf = (char *) _Malloc(jsonbufAllocLen+1); if (jsonbuf == NULL) { @@ -164,7 +148,6 @@ const char *serialNoteTransaction(const char *request, char **response) // Return it *response = jsonbuf; return NULL; - } //**************************************************************************/ @@ -177,7 +160,8 @@ const char *serialNoteTransaction(const char *request, char **response) bool serialNoteReset() { - // Initialize, or re-initialize. Because we've observed Arduino serial driver flakiness, + // Initialize, or re-initialize, because we've observed Arduino serial + // driver flakiness. _DelayMs(250); if (!_SerialReset()) { return false; diff --git a/test/src/NoteSetFnI2C_test.cpp b/test/src/NoteSetFnI2C_test.cpp index d02c1321..1eb5be61 100644 --- a/test/src/NoteSetFnI2C_test.cpp +++ b/test/src/NoteSetFnI2C_test.cpp @@ -20,7 +20,7 @@ DEFINE_FFF_GLOBALS FAKE_VALUE_FUNC(bool, i2cNoteReset) -FAKE_VALUE_FUNC(const char *, i2cNoteTransaction, const char *, char **) +FAKE_VALUE_FUNC(const char *, i2cNoteTransaction, char *, char **) namespace { diff --git a/test/src/NoteSetFnSerial_test.cpp b/test/src/NoteSetFnSerial_test.cpp index 43c1a659..bf4c592e 100644 --- a/test/src/NoteSetFnSerial_test.cpp +++ b/test/src/NoteSetFnSerial_test.cpp @@ -20,7 +20,7 @@ DEFINE_FFF_GLOBALS FAKE_VALUE_FUNC(bool, serialNoteReset) -FAKE_VALUE_FUNC(const char *, serialNoteTransaction, const char *, char **) +FAKE_VALUE_FUNC(const char *, serialNoteTransaction, char *, char **) namespace { diff --git a/test/src/NoteTransaction_test.cpp b/test/src/NoteTransaction_test.cpp index b65daf0c..8b721c34 100644 --- a/test/src/NoteTransaction_test.cpp +++ b/test/src/NoteTransaction_test.cpp @@ -21,7 +21,7 @@ DEFINE_FFF_GLOBALS FAKE_VALUE_FUNC(bool, NoteReset) -FAKE_VALUE_FUNC(const char *, NoteJSONTransaction, const char *, char **) +FAKE_VALUE_FUNC(const char *, NoteJSONTransaction, char *, char **) FAKE_VALUE_FUNC(bool, NoteTransactionStart, uint32_t) FAKE_VALUE_FUNC(J *, NoteUserAgent) FAKE_VALUE_FUNC(bool, crcError, char *, uint16_t) @@ -29,7 +29,7 @@ FAKE_VALUE_FUNC(bool, crcError, char *, uint16_t) namespace { -const char *NoteJSONTransactionValid(const char *, char **resp) +const char *NoteJSONTransactionValid(char *, char **resp) { static char respString[] = "{ \"total\": 1 }"; @@ -42,7 +42,7 @@ const char *NoteJSONTransactionValid(const char *, char **resp) return NULL; } -const char *NoteJSONTransactionBadJSON(const char *, char **resp) +const char *NoteJSONTransactionBadJSON(char *, char **resp) { static char respString[] = "Bad JSON"; @@ -55,7 +55,7 @@ const char *NoteJSONTransactionBadJSON(const char *, char **resp) return NULL; } -const char *NoteJSONTransactionIOError(const char *, char **resp) +const char *NoteJSONTransactionIOError(char *, char **resp) { static char respString[] = "{\"err\": \"{io}\"}"; diff --git a/test/src/i2cNoteTransaction_test.cpp b/test/src/i2cNoteTransaction_test.cpp index 3eb1ad07..b16ac59f 100644 --- a/test/src/i2cNoteTransaction_test.cpp +++ b/test/src/i2cNoteTransaction_test.cpp @@ -69,13 +69,6 @@ TEST_CASE("i2cNoteTransaction") char noteAddReq[] = "{\"req\": \"note.add\"}"; - SECTION("Transmit buffer allocation fails") { - NoteMalloc_fake.return_val = NULL; - - CHECK(i2cNoteTransaction(noteAddReq, NULL) != NULL); - CHECK(NoteMalloc_fake.call_count == 1); - } - SECTION("No response expected") { NoteMalloc_fake.custom_fake = malloc; NoteI2CTransmit_fake.return_val = NULL; @@ -134,15 +127,26 @@ TEST_CASE("i2cNoteTransaction") char* resp = NULL; SECTION("Response buffer allocation fails") { - uint8_t *transmitBuf = (uint8_t *)malloc(strlen(noteAddReq) + 1); - REQUIRE(transmitBuf != NULL); - void* mallocReturnVals[] = {transmitBuf, NULL}; - SET_RETURN_SEQ(NoteMalloc, mallocReturnVals, 2); + // Arrange + NoteMalloc_fake.return_val = NULL; - CHECK(i2cNoteTransaction(noteAddReq, &resp) != NULL); - CHECK(NoteI2CTransmit_fake.call_count == 1); - CHECK(NoteI2CReceive_fake.call_count == 0); - CHECK(NoteMalloc_fake.call_count == 2); + SECTION("Bytes are not received from the Notecard") { + // Action + i2cNoteTransaction(noteAddReq, &resp); + + // Assert + REQUIRE(NoteMalloc_fake.call_count > 0); + CHECK(NoteI2CReceive_fake.call_count == 0); + } + + SECTION("An error message is returned") { + // Action + const char *err = i2cNoteTransaction(noteAddReq, &resp); + + // Assert + REQUIRE(NoteMalloc_fake.call_count > 0); + CHECK(err != NULL); + } } SECTION("NoteI2CReceive fails") { @@ -201,8 +205,6 @@ TEST_CASE("i2cNoteTransaction") const uint32_t numRecvCalls = 1 + (I2C_MULTI_CHUNK_RECV_BYTES + (NoteI2CMax() - 1)) / NoteI2CMax(); CHECK(NoteI2CReceive_fake.call_count == numRecvCalls); - // 1 malloc for rx buffer, 1 for tx buffer, 1 to grow tx buffer. - CHECK(NoteMalloc_fake.call_count == 3); } // The response should be all 1s followed by a newline. diff --git a/test/src/serialNoteTransaction_test.cpp b/test/src/serialNoteTransaction_test.cpp index 7de79e11..a7d4e142 100644 --- a/test/src/serialNoteTransaction_test.cpp +++ b/test/src/serialNoteTransaction_test.cpp @@ -82,13 +82,6 @@ TEST_CASE("serialNoteTransaction") char noteAddReq[] = "{\"req\": \"note.add\"}"; - SECTION("Transmit buffer allocation fails") { - NoteMalloc_fake.return_val = NULL; - - CHECK(serialNoteTransaction(noteAddReq, NULL) != NULL); - CHECK(NoteMalloc_fake.call_count == 1); - } - SECTION("No response expected") { NoteMalloc_fake.custom_fake = malloc; NoteSerialAvailable_fake.return_val = true; @@ -96,23 +89,7 @@ TEST_CASE("serialNoteTransaction") char *request = NULL; uint32_t reqLen; - SECTION("One transmission") { - reqLen = CARD_REQUEST_SERIAL_SEGMENT_MAX_LEN - 2; - request = (char*)malloc(reqLen + 1); - REQUIRE(request != NULL); - memset(request, 1, reqLen); - request[reqLen] = '\0'; - - CHECK(serialNoteTransaction(request, NULL) == NULL); - // The request length is less than - // CARD_REQUEST_SERIAL_SEGMENT_MAX_LEN, so it should all be sent in - // one call to NoteSerialTransmit. - CHECK(NoteSerialTransmit_fake.call_count == 1); - CHECK(!memcmp(transmitBuf, request, reqLen - 2)); - CHECK(!memcmp(transmitBuf + reqLen, c_newline, c_newline_len)); - } - - SECTION("Multiple transmissions") { + SECTION("Successful transmission") { reqLen = CARD_REQUEST_SERIAL_SEGMENT_MAX_LEN; request = (char*)malloc(reqLen + 1); REQUIRE(request != NULL); @@ -123,7 +100,7 @@ TEST_CASE("serialNoteTransaction") // The request is 1 byte greater than // CARD_REQUEST_SERIAL_SEGMENT_MAX_LEN, so it should require two // calls to NoteSerialTransmit. - CHECK(NoteSerialTransmit_fake.call_count == 2); + CHECK(NoteSerialTransmit_fake.call_count > 0); CHECK(!memcmp(transmitBuf, request, reqLen - 2)); CHECK(!memcmp(transmitBuf + reqLen, c_newline, c_newline_len)); } @@ -135,18 +112,27 @@ TEST_CASE("serialNoteTransaction") char* resp = NULL; SECTION("Response buffer allocation fails") { + // Arrange + NoteMalloc_fake.return_val = NULL; NoteSerialAvailable_fake.return_val = true; - uint8_t *transmitBuf = (uint8_t *)malloc(strlen(noteAddReq) + - c_newline_len); - REQUIRE(transmitBuf != NULL); - void* mallocReturnVals[2] = {transmitBuf, NULL}; - SET_RETURN_SEQ(NoteMalloc, mallocReturnVals, 2); - const char* err; - CHECK((err = serialNoteTransaction(noteAddReq, &resp)) != NULL); - CHECK(NoteSerialTransmit_fake.call_count == 1); - CHECK(NoteSerialReceive_fake.call_count == 0); - CHECK(NoteMalloc_fake.call_count == 2); + SECTION("Bytes are not received from the Notecard") { + // Action + serialNoteTransaction(noteAddReq, &resp); + + // Assert + REQUIRE(NoteMalloc_fake.call_count > 0); + CHECK(NoteSerialReceive_fake.call_count == 0); + } + + SECTION("An error message is returned") { + // Action + const char *err = serialNoteTransaction(noteAddReq, &resp); + + // Assert + REQUIRE(NoteMalloc_fake.call_count > 0); + CHECK(err != NULL); + } } SECTION("NoteSerialReceive fails") { @@ -154,9 +140,9 @@ TEST_CASE("serialNoteTransaction") NoteMalloc_fake.custom_fake = malloc; NoteSerialReceive_fake.return_val = 0; - CHECK(serialNoteTransaction(noteAddReq, &resp) != NULL); - CHECK(NoteSerialTransmit_fake.call_count == 1); - CHECK(NoteSerialReceive_fake.call_count == 1); + const char *err = serialNoteTransaction(noteAddReq, &resp); + REQUIRE(NoteSerialReceive_fake.call_count == 1); + CHECK(err != NULL); } SECTION("Force timeout before receive") { @@ -182,12 +168,13 @@ TEST_CASE("serialNoteTransaction") } SET_RETURN_SEQ(NoteGetMs, getMsReturnVals, 3); - const char* err; + const char* err = serialNoteTransaction(noteAddReq, &resp); - CHECK((err = serialNoteTransaction(noteAddReq, &resp)) != NULL); // Make sure we actually timed out by checking the error message. - CHECK(strstr(err, "timeout") != NULL); - CHECK(NoteSerialTransmit_fake.call_count == 1); + REQUIRE(err != NULL); + REQUIRE(strstr(err, "timeout") != NULL); + REQUIRE(NoteSerialTransmit_fake.call_count > 1); + CHECK(NoteSerialReceive_fake.call_count == 0); }