unitTest.c - Implements unit tests for the microcontroller comm protocol¶
A very simple test runner, runAllTests(), executes the tests. ASSERT statements provide verification.
 
#include "dataXfer.h"
#include <string.h>
#include <stdio.h>
 
Tests for the command-finding state machine¶
 
Run all normal chars through the machine
void findChar() {
  char c_in, c_out;
  for (c_in = 0; c_in < ESCAPED_CMD; c_in++) {
    CMD_OUTPUT cmdOutput = stepCommandFindMachine(c_in, &c_out);
    ASSERT(cmdOutput == OUTPUT_CMD_CHAR);
    ASSERT(c_in == c_out);
  }
  for (c_in = ESCAPED_CMD + 1; c_in <= ((char) 0xFF); c_in++) {
    CMD_OUTPUT cmdOutput = stepCommandFindMachine(c_in, &c_out);
    ASSERT(cmdOutput == OUTPUT_CMD_CHAR);
    ASSERT(c_in == c_out);
  }
}
 
Run an escaped command through the machine.
void findEscapedCommandChar() {
  char c_out;
  CMD_OUTPUT cmdOutput = stepCommandFindMachine(CMD_TOKEN, &c_out);
  ASSERT(cmdOutput == OUTPUT_CMD_NONE);
  cmdOutput = stepCommandFindMachine(ESCAPED_CMD, &c_out);
  ASSERT(cmdOutput == OUTPUT_CMD_CHAR);
  ASSERT(c_out == CMD_TOKEN);
}
 
Look for all the normal (unescaped) commands.
void findCommand() {
  char c_in, c_out;
  for (c_in = 0; c_in < ESCAPED_CMD; c_in++) {
    CMD_OUTPUT cmdOutput = stepCommandFindMachine(CMD_TOKEN, &c_out);
    ASSERT(cmdOutput == OUTPUT_CMD_NONE);
    cmdOutput = stepCommandFindMachine(c_in, &c_out);
    ASSERT(cmdOutput == OUTPUT_CMD_CMD);
    ASSERT(c_in == c_out);
  }
  for (c_in = ESCAPED_CMD + 1; c_in <= ((char) 0xFF); c_in++) {
    CMD_OUTPUT cmdOutput = stepCommandFindMachine(CMD_TOKEN, &c_out);
    ASSERT(cmdOutput == OUTPUT_CMD_NONE);
    cmdOutput = stepCommandFindMachine(c_in, &c_out);
    ASSERT(cmdOutput == OUTPUT_CMD_CMD);
    ASSERT(c_in == c_out);
  }
}
 
Run an escaped command through the machine
void findEscapedCommand() {
  char c_out;
  CMD_OUTPUT cmdOutput = stepCommandFindMachine(CMD_TOKEN, &c_out);
  ASSERT(cmdOutput == OUTPUT_CMD_NONE);
  cmdOutput = stepCommandFindMachine(CMD_TOKEN, &c_out);
  ASSERT(cmdOutput == OUTPUT_CMD_NONE);
  cmdOutput = stepCommandFindMachine(ESCAPED_CMD, &c_out);
  ASSERT(cmdOutput == OUTPUT_CMD_CMD);
  ASSERT(c_out == CMD_TOKEN);
}
 
Verify that the sequence CMD_TOKEN CMD_TOKEN CMD_TOKEN is recognized as a repeated wait.
void findRepeatedWait() {
  char c_out;
  CMD_OUTPUT cmdOutput = stepCommandFindMachine(CMD_TOKEN, &c_out);
  ASSERT(cmdOutput == OUTPUT_CMD_NONE);
  cmdOutput = stepCommandFindMachine(CMD_TOKEN, &c_out);
  ASSERT(cmdOutput == OUTPUT_CMD_NONE);
  cmdOutput = stepCommandFindMachine(CMD_TOKEN, &c_out);
  ASSERT(cmdOutput == OUTPUT_CMD_REPEATED_WAIT);
}
 
todo: Cases still to unit test:
- (repeated wait)
- CMD_TOKEN CMD_TOKEN c (repeated command)
void findRepeatedCommand() {
  char c_out;
  CMD_OUTPUT cmdOutput = stepCommandFindMachine(CMD_TOKEN, &c_out);
  ASSERT(cmdOutput == OUTPUT_CMD_NONE);
  cmdOutput = stepCommandFindMachine(CMD_TOKEN, &c_out);
  ASSERT(cmdOutput == OUTPUT_CMD_NONE);
  cmdOutput = stepCommandFindMachine(0, &c_out);
  ASSERT(cmdOutput == OUTPUT_CMD_REPEATED_CMD);
  ASSERT(c_out == 0);
}
 
 
Tests for the receive state machine¶
 
void sendData(uint8_t* pu8_data, uint u_len);
 
 
Sending a normal char shold report that char received.
void sendOneNormalChar(
The character to send. This character will NOT be esacaped – an 0x55 will be sent as just an 0x55.
  char c_charToSend) {
  stepReceiveMachine(c_charToSend);
  ASSERT(isReceiveMachineChar());
  ASSERT(getReceiveMachineOutChar() == c_charToSend);
};
 
 
Sending a letter should report that letter received
void sendLetter() {
  sendOneNormalChar('c');
}
 
 
Sending the char 0x00 shold report that char receivd
void send0x00() {
  sendOneNormalChar(0x00);
}
 
 
Sending the char 0xFF shold report that char receivd
void send0xFF() {
  sendOneNormalChar((char) 0xFF);
}
 
 
Check sending an escaped command
void sendEscapedCommand() {
  uint8_t au8_data[] = { CMD_TOKEN, ESCAPED_CMD };
  sendData(au8_data, 2);
  ASSERT(isReceiveMachineChar());
  ASSERT(getReceiveMachineOutChar() == CMD_TOKEN);
}
 
Set up the xferData structure for receiving some data.
void setupXferData(
Index of data to be received
  uint u_index,
  // Length (in bytes) of data to be received
  uint u_len) {
 
Check params
  ASSERT(u_index < NUM_XFER_VARS);
  ASSERT(u_len <= 256);
 
Set up structure.
  xferVar[u_index].u8_size = u_len - 1;  // Value is length-1
#ifdef MICROCONTROLLER
On a microcontroller, provide a statically-allocated buffer to hold received data.
  static uint8_t au8_data[256];
  xferVar[u_index].pu8_data = au8_data;
#else
On the PC, dynamically allocate it; initDataXfer() will free this.
  xferVar[u_index].pu8_data = (uint8_t*) malloc(u_len*sizeof(uint8_t));
#endif
  assignBit(u_index, TRUE);
}
 
Check sending a one-byte piece of data 0x00 == 000000 00 : index 0, length 0 (1 byte)
void sendOneByteData() {
Set up index 0 for 1 byte of data
  setupXferData(0, 1);
  uint8_t au8_data[] = { CMD_TOKEN, 0x00, 0x12 };
  sendData(au8_data, 3);
  ASSERT(isReceiveMachineData());
  ASSERT(getReceiveMachineIndex() == 0);
  ASSERT(xferVar[0].pu8_data[0] == au8_data[2]);
}
 
 
Check sending a four-byte piece of data
void sendFourBytesData() {
  setupXferData(0, 4);
0x03: index 0, length 3 (4 bytes); following are data bytes
  uint8_t au8_data[] = { CMD_TOKEN, 0x03, 0x00, 0x01, 0x02, 0x03 };
  sendData(au8_data, 6);
  ASSERT(isReceiveMachineData());
  ASSERT(getReceiveMachineIndex() == 0);
  uint i;
  for (i = 0; i < 4; i++)
    ASSERT(xferVar[0].pu8_data[i] == i);
}
 
 
Check sending a four-byte piece of data which contains four CMD_TOKEN bytes. 0x00 == 000000 11 : index 0, length 3 (4 bytes)
void sendFourBytesCmdTokenData() {
  setupXferData(0, 4);
0x03: index 0, length 3; following are data bytes
  uint8_t au8_data[] = { CMD_TOKEN, 0x03, CMD_TOKEN, ESCAPED_CMD,
                         CMD_TOKEN, ESCAPED_CMD, CMD_TOKEN, ESCAPED_CMD, CMD_TOKEN, ESCAPED_CMD
                       };
  sendData(au8_data, 10);
  ASSERT(isReceiveMachineData());
  ASSERT(getReceiveMachineIndex() == 0);
  uint i;
  for (i = 0; i < 4; i++)
    ASSERT(xferVar[0].pu8_data[i] == ((uint8_t) CMD_TOKEN));
}
 
 
Send a repeated command and make sure both an error is reported and data can be received (error recovery works).
void sendRepeatedCommand() {
0x00: index 0, length 1.
  uint8_t au8_data[] = { CMD_TOKEN, CMD_TOKEN, 0x00, 0xFF };
Send first three bytes and check for repeated command.
  setupXferData(0, 1);
  sendData(au8_data, 2);
Send third byte manually, since it reports an error
  stepReceiveMachine(au8_data[2]);
  ASSERT(getReceiveMachineError() == ERR_REPEATED_CMD);
Send last byte
  sendData(&au8_data[3], 1);
  ASSERT(isReceiveMachineData());
  ASSERT(getReceiveMachineIndex() == 0);
  ASSERT(xferVar[0].pu8_data[0] == au8_data[3]);
}
 
 
Send a command of CMD_TOKEN
void sendCommandCmdToken() {
Set up and send command
  uint u_index = getVarIndex(CMD_TOKEN);
  uint u_len = getVarLength(CMD_TOKEN);
  setupXferData(u_index, u_len);
  uint8_t au8_data[7] = { CMD_TOKEN, CMD_TOKEN, ESCAPED_CMD };
  uint u;
  for (u = 0; u < u_len; u++)
    au8_data[u + 3] = u;
  sendData(au8_data, 3 + u_len);
 
Check received data
  ASSERT(isReceiveMachineData());
  ASSERT(getReceiveMachineIndex() == u_index);
  for (u = 0; u < u_len; u++)
    ASSERT(xferVar[u_index].pu8_data[u] == u);
}
 
 
Send a repeated command followed by a command of CMD_TOKEN
void sendRepeatedCommandCmdToken() {
Set up and send command
  uint u_index = getVarIndex(CMD_TOKEN);
  uint u_len = getVarLength(CMD_TOKEN);
  setupXferData(u_index, u_len);
  uint8_t au8_data[8] = { CMD_TOKEN, CMD_TOKEN, CMD_TOKEN, ESCAPED_CMD };
  uint u;
  for (u = 0; u < u_len; u++)
    au8_data[u + 4] = u;
Send first two bytes (no error yet)
  sendData(au8_data, 2);
Send 3rd command (should be a repeated command error)
  stepReceiveMachine(au8_data[2]);
  ASSERT(getReceiveMachineError() == ERR_REPEATED_CMD);
Send remaining data (ESCAPED_CMD followed by u_len data bytes)
  sendData(&au8_data[3], 1 + u_len);
 
Check received data
  ASSERT(isReceiveMachineData());
  ASSERT(getReceiveMachineIndex() == u_index);
  for (u = 0; u < u_len; u++)
    ASSERT(xferVar[u_index].pu8_data[u] == u);
}
 
Test timeout detection.
void sendWithTimeout() {
  setupXferData(0, 4);
0x03: index 0, length 3 (4 bytes); following are data bytes
  uint8_t au8_data[] = { CMD_TOKEN, 0x03, 0x00, 0x01, 0x02, 0x03 };
 
Create a timeout at each point in the transmission of the data
  uint u;
  for (u = 0; u < 6; u++) {
Start fresh each time
    resetReceiveMachine();
Send u-1 bytes with no timeout
    if (u > 0)
      sendData(au8_data, u);
Send the final byte with a timeout
    notifyOfTimeout();
    RECEIVE_ERROR re = stepReceiveMachine(au8_data[u]);
Except for the first byte, make sure a timeout is reported
    if (u > 0)
      ASSERT(re == ERR_TIMEOUT);
  }
}
 
Test sending a second command before the first completes.
void sendInterruptedCommand() {
  setupXferData(0, 4);
CMD_TOKEN, 0x03: index 0, length 3 (4 bytes)
  uint8_t au8_data[] = { CMD_TOKEN, 0x03, 0x00, CMD_TOKEN, 0x03, 0x00, 0x01, 0x02, 0x03 };
 
Send the first command then interrupt it
  sendData(au8_data, 4);
  RECEIVE_ERROR re = stepReceiveMachine(au8_data[4]);
  ASSERT(re == ERR_INTERRUPTED_CMD);
  clearReceiveMachineError();
 
Make sure the second command competes
  sendData(au8_data + 5, 4);
  ASSERT(isReceiveMachineData());
  ASSERT(getReceiveMachineIndex() == 0);
  uint i;
  for (i = 0; i < 4; i++)
    ASSERT(xferVar[0].pu8_data[i] == i);
}
 
Test sending data to an unspecified index
void sendToUnspecifiedIndex() {
CMD_TOKEN, 0x03: index 0, length 3 (4 bytes)
  uint8_t au8_data[] = { CMD_TOKEN, 0x03 };
  sendData(au8_data, 1);
  RECEIVE_ERROR re = stepReceiveMachine(au8_data[1]);
  ASSERT(re == ERR_UNSPECIFIED_INDEX);
}
 
Test sending data to an index beyond the end of the variable storage area
void sendToHighIndex() {
This test only works if NUM_XFER_VARS is less than the max; otherwise, there’s no “beyond” index to test.
  ASSERT(NUM_XFER_VARS < MAX_NUM_XFER_VARS);
 
CMD_TOKEN, 0x03: index NUM_XFER_VARS, length 3 (4 bytes)
  uint8_t au8_data[] = { CMD_TOKEN, (NUM_XFER_VARS << 2) | 0x03 };
  sendData(au8_data, 1);
  RECEIVE_ERROR re = stepReceiveMachine(au8_data[1]);
  ASSERT(re == ERR_INDEX_TOO_HIGH);
}
 
Test sending incorrectly-sized data to a variable.
void sendWithWrongSize() {
  setupXferData(0, 4);
CMD_TOKEN, 0x03: index 0, length 2 (3 bytes)
  uint8_t au8_data[] = { CMD_TOKEN, 0x02 };
  sendData(au8_data, 1);
  RECEIVE_ERROR re = stepReceiveMachine(au8_data[1]);
  ASSERT(re == ERR_VAR_SIZE_MISMATCH);
}
 
Test sending long data to an unspecified index
void sendLongToUnspecifiedIndex() {
CMD_TOKEN, CMD_LONG_VAR, 0x03: index 0, length 3 (4 bytes)
  uint8_t au8_data[] = { CMD_TOKEN, CMD_LONG_VAR, 0x03 };
  sendData(au8_data, 2);
  RECEIVE_ERROR re = stepReceiveMachine(au8_data[2]);
  ASSERT(re == ERR_UNSPECIFIED_INDEX);
}
 
Test sending long data to an index beyond the end of the variable storage area
void sendLongToHighIndex() {
This test only works if NUM_XFER_VARS is less than the max; otherwise, there’s no “beyond” index to test.
  ASSERT(NUM_XFER_VARS < MAX_NUM_XFER_VARS);
 
CMD_TOKEN, CMD_LONG_VAR, index NUM_XFER_VARS, length 3 (4 bytes)
  uint8_t au8_data[] = { CMD_TOKEN, CMD_LONG_VAR, NUM_XFER_VARS, 0x03 };
  sendData(au8_data, 2);
  RECEIVE_ERROR re = stepReceiveMachine(au8_data[2]);
  ASSERT(re == ERR_INDEX_TOO_HIGH);
}
 
Test sending incorrectly-sized data to a long variable.
void sendLongWithWrongSize() {
  setupXferData(0, 4);
CMD_TOKEN, CMD_LONG_BAR, index 0, length 2 (3 bytes)
  uint8_t au8_data[] = { CMD_TOKEN, CMD_LONG_VAR, 0, 0x02 };
  sendData(au8_data, 3);
  RECEIVE_ERROR re = stepReceiveMachine(au8_data[3]);
  ASSERT(re == ERR_VAR_SIZE_MISMATCH);
}
 
Test sending 256 bytes of data.
void sendLongData() {
  uint i;
  setupXferData(0, 256);
CMD_TOKEN, CMD_LONG_VAR, index 0, length 0xFF = 256 bytes
  uint8_t au8_data[256 + 5] = { CMD_TOKEN, CMD_LONG_VAR, 0, 0xFF };
Fill array with data, escaping the CMD_TOKEN. Do horrible casts (to 8 bit, then to uint) to avoid sign-extension problems that make this loop run too far (0xAA < 0, without a cast is sign-extended to 0xFFFFFFAA, which is a NOT 0x00AA).
  for (i = 0; i <= ((uint) ((uint8_t) CMD_TOKEN)); i++)
    au8_data[i + 4] = i;
  au8_data[i + 4] = ESCAPED_CMD;
  for (; i < 256; i++)
    au8_data[i + 5] = i;
Send it, then check the data received
  sendData(au8_data, 256 + 5);
  for (i = 0; i < 256; i++)
    ASSERT(xferVar[0].pu8_data[i] == i);
}
#ifdef MICROCONTROLLER
Test sending data to a read-only variable. Only applies to the microcontroller.
void sendReadOnly() {
Set up index 0 for 1 byte of data, read-only
  setupXferData(0, 1);
  assignBit(0, FALSE);
  uint8_t au8_data[] = { CMD_TOKEN, 0x00 };
  sendData(au8_data, 1);
  RECEIVE_ERROR re = stepReceiveMachine(au8_data[1]);
  ASSERT(re == ERR_READ_ONLY_VAR);
}
 
 
Test sending a var spec
void sendVarSpecMicrocontroller() {
Set up index 0 for 1 byte of data, read-only
  setupXferData(0, 1);
  uint8_t au8_data[] = { CMD_TOKEN, CMD_SEND_ONLY };
  sendData(au8_data, 1);
  RECEIVE_ERROR re = stepReceiveMachine(au8_data[1]);
  ASSERT(re == ERR_MICROCONTROLLER_VAR_SPEC);
}
#endif
#ifndef MICROCONTROLLER
Used to create strings with commands in them below.
#define CMD_TOKEN_STR "\xAA"
#define CMD_SEND_ONLY_STR "\xFE"
#define CMD_SEND_RECEIVE_VAR_STR "\xFF"
 
Test sending a variable specification
void sendVarSpec() {
index, length, size, format, name, description
  uint8_t au8_data[17] = CMD_TOKEN_STR CMD_SEND_ONLY_STR "\x00" "\x0C"  "\x03"  "%x\x00"  "test\x00"  "ing";
  sendData(au8_data, 17);
  ASSERT(isReceiveMachineSpec());
  ASSERT(xferVar[0].u8_size == 3);
  ASSERT(xferVar[0].pu8_data != NULL);   // Verify that some storage was allocated for this variable
  ASSERT(!isVarWriteable(0));
  ASSERT(strcmp(xferVar[0].psz_format, "%x") == 0);
  ASSERT(strcmp(xferVar[0].psz_name, "test") == 0);
  ASSERT(strcmp(xferVar[0].psz_desc, "ing") == 0);
}
 
Test sending a variable specification, then sending a different spec
void resendVarSpec() {
  sendVarSpec();
index, length, size, format, name, description
  uint8_t au8_data[17] = CMD_TOKEN_STR CMD_SEND_ONLY_STR "\x00" "\x0C"  "\xFF"  "%y\x00"  "book\x00"  "let";
  sendData(au8_data, 17);
  ASSERT(isReceiveMachineSpec());
  ASSERT(xferVar[0].u8_size == 255);
  ASSERT(xferVar[0].pu8_data != NULL);   // Verify that some storage was allocated for this variable
  ASSERT(!isVarWriteable(0));
  ASSERT(strcmp(xferVar[0].psz_format, "%y") == 0);
  ASSERT(strcmp(xferVar[0].psz_name, "book") == 0);
  ASSERT(strcmp(xferVar[0].psz_desc, "let") == 0);
}
 
Test sending a variable specification
void sendWriteableVarSpec() {
index, length, size, format, name, description
  uint8_t au8_data[17] = CMD_TOKEN_STR CMD_SEND_RECEIVE_VAR_STR "\x00" "\x0C"  "\x03"  "%x\x00"  "test\x00"  "ing";
  sendData(au8_data, 17);
  ASSERT(isReceiveMachineSpec());
  ASSERT(xferVar[0].u8_size == 3);
  ASSERT(xferVar[0].pu8_data != NULL);   // Verify that some storage was allocated for this variable
  ASSERT(isVarWriteable(0));
  ASSERT(strcmp(xferVar[0].psz_format, "%x") == 0);
  ASSERT(strcmp(xferVar[0].psz_name, "test") == 0);
  ASSERT(strcmp(xferVar[0].psz_desc, "ing") == 0);
}
 
Test sending a variable specification with no strings
void sendEmptyVarSpec() {
index, length, size
  uint8_t au8_data[6] = CMD_TOKEN_STR CMD_SEND_RECEIVE_VAR_STR "\x00" "\x00"  "\x03";
  sendData(au8_data, 5);
  ASSERT(isReceiveMachineSpec());
  ASSERT(xferVar[0].u8_size == 3);
  ASSERT(xferVar[0].pu8_data != NULL);   // Verify that some storage was allocated for this variable
  ASSERT(isVarWriteable(0));
  ASSERT(strcmp(xferVar[0].psz_format, "") == 0);
  ASSERT(strcmp(xferVar[0].psz_name, "") == 0);
  ASSERT(strcmp(xferVar[0].psz_desc, "") == 0);
}
 
Test sending a variable specification with only a format
void sendFormatOnlyVarSpec() {
index, length, size, format
  uint8_t au8_data[8] = CMD_TOKEN_STR CMD_SEND_RECEIVE_VAR_STR "\x00" "\x03"  "\x03" "%x";
  sendData(au8_data, 8);
  ASSERT(isReceiveMachineSpec());
  ASSERT(xferVar[0].u8_size == 3);
  ASSERT(xferVar[0].pu8_data != NULL);   // Verify that some storage was allocated for this variable
  ASSERT(isVarWriteable(0));
  ASSERT(strcmp(xferVar[0].psz_format, "%x") == 0);
  ASSERT(strcmp(xferVar[0].psz_name, "") == 0);
  ASSERT(strcmp(xferVar[0].psz_desc, "") == 0);
}
 
Test sending a variable specification with only a format
void sendNameOnlyVarSpec() {
index, length, size, format, name
  uint8_t au8_data[11] = CMD_TOKEN_STR CMD_SEND_RECEIVE_VAR_STR "\x00" "\x06" "\x03" "\x00"  "test";
  sendData(au8_data, 11);
  ASSERT(isReceiveMachineSpec());
  ASSERT(xferVar[0].u8_size == 3);
  ASSERT(xferVar[0].pu8_data != NULL);   // Verify that some storage was allocated for this variable
  ASSERT(isVarWriteable(0));
  ASSERT(strcmp(xferVar[0].psz_format, "") == 0);
  ASSERT(strcmp(xferVar[0].psz_name, "test") == 0);
  ASSERT(strcmp(xferVar[0].psz_desc, "") == 0);
}
 
Test sending a spec followed by actual data
void sendVarSpecAndData() {
  sendVarSpec();
 
0x03: index 0, length 3 (4 bytes); following are data bytes
  uint8_t au8_data[] = { CMD_TOKEN, 0x03, 0x00, 0x01, 0x02, 0x03 };
  sendData(au8_data, 6);
  ASSERT(isReceiveMachineData());
  ASSERT(getReceiveMachineIndex() == 0);
  for (uint i = 0; i < 4; i++)
    ASSERT(xferVar[0].pu8_data[i] == i);
}
#endif
 
Run a sequence of characters through the receive state machine, verifying that nothing is received until the final character and that no errors occurred.
void sendData(uint8_t* pu8_data, uint u_len) {
  while (u_len--) {
    RECEIVE_ERROR re = stepReceiveMachine(*pu8_data++);
    ASSERT(re == ERR_NONE);
    if (u_len)
      ASSERT(getReceiveMachineState() != STATE_RECV_START);
  }
}
//@}
 
name Tests for the specify and send functions
//@{
 
The length of an array of characters used to check OUT_CHAR’s usage.
static size_t st_outCharLen = 0;
 
An index into the array of check characters.
static size_t st_outCharIndex = 0;
 
A pointer to an array containing the expected characters to be output.
static uint8_t* au8_outCharData = NULL;
 
Reset all the OUT_CHAR associated data (the variables above).
void clearOutChar() {
  st_outCharLen = 0;
  st_outCharIndex = 0;
  au8_outCharData = NULL;
}
 
An outChar function which simply checks to see that the output character matches the expected string.
#ifdef __cplusplus
extern "C"
#endif
void testOutChar(uint8_t c) {
ASSERT(au8_outCharData != NULL);
ASSERT(st_outCharIndex < st_outCharLen);
ASSERT(au8_outCharData[st_outCharIndex++] == c);
}
/** Test support: ASSERT if an exception isn't thrown.
 *  \param code Code which when executed should cause a specific ASSERT.
 *  \param expectedMsg String the ASSERT should throw.
 *  This macro expects to test ASSERT statements of the form
 *  ASSERT("a string" && someCondition). The "a string" portion is always
 *  true, but also provides a hackish way to name an assert. To make
 *  testing easier, only the text inside the quotes is tested: the
 *  expectedString in this case is "a string".
 */
#ifdef __cplusplus
#define REQUIRE_ASSERT(code, expectedMsg)      \
  do {                          \
    BOOL didAssert = FALSE;   \
    try {                     \
      code;                 \
    } catch (char* psz_msg) { \
      didAssert = strncmp(psz_msg, expectedMsg, strlen(expectedMsg)) ? FALSE : TRUE;     \
    }                         \
    ASSERT(didAssert);        \
  } while (FALSE)
#else
Do nothing, since C doesn’t support exceptions
#define REQUIRE_ASSERT(code, expectedMsg) (void) 0
#endif
 
Send to a index that’s too high
void testSendIndexTooHigh() {
  REQUIRE_ASSERT(sendVar(NUM_XFER_VARS + 1), "sendVar:indexTooHigh");
}
 
Send to an unconfigured index
void testSendIndexUnspecificed() {
  REQUIRE_ASSERT(sendVar(0), "sendVar:indexNotSpecified");
}
 
Send to a read-only variable (PC only)
#ifndef MICROCONTROLLER
void testSendToReadOnly() {
Set up index 0 for 1 byte of data, read-only
  setupXferData(0, 1);
  assignBit(0, FALSE);
Expected transmission
  REQUIRE_ASSERT(sendVar(0), "sendVar:notWriteable");
}
#endif
 
A macro to send a variable and check the resulting output
void checkSendVar(uint8_t u8_index, uint u_len, uint8_t* au8_data) {
  au8_outCharData = au8_data;
  st_outCharLen = u_len;
  sendVar(u8_index);
  ASSERT(st_outCharIndex == st_outCharLen);
}
 
Send a one-byte variable
void testSendOneByteVar() {
Set up index 0 for 1 byte of data
  setupXferData(0, 1);
Assign data
  xferVar[0].pu8_data[0] = 0;
Expected transmission
  uint8_t au8_data[3] = { CMD_TOKEN, 0x00, 0x00 };
  checkSendVar(0, 3, au8_data);
}
 
Send a one-byte variable that needs to be escaped
void testSendOneEscapedByteVar() {
Set up index 0 for 1 byte of data
  setupXferData(0, 1);
Assign data
  xferVar[0].pu8_data[0] = CMD_TOKEN;
Expected transmission
  uint8_t au8_data[4] = { CMD_TOKEN, 0, CMD_TOKEN, ESCAPED_CMD };
  checkSendVar(0, 4, au8_data);
}
 
Send a four-byte variable
void testSendFourByteVar() {
Set up index 0 for 4 bytes of data
  setupXferData(0, 4);
Assign data
  uint u_i;
  for (u_i = 0; u_i < 4; u_i++)
    xferVar[0].pu8_data[u_i] = u_i;
Expected transmission
  uint8_t au8_data[6] = { CMD_TOKEN, 0x03, 0x00, 0x01, 0x02, 0x03 };
  checkSendVar(0, 6, au8_data);
}
 
Send a 256-byte variable
void testSend256ByteVar() {
Set up index 0 for 256 bytes of data
  setupXferData(0, 256);
Assign data
  uint u_i;
  for (u_i = 0; u_i < 256; u_i++)
    xferVar[0].pu8_data[u_i] = u_i;
Expected transmission
  uint8_t au8_data[261] = { CMD_TOKEN, CMD_LONG_VAR, 0x00, 0xFF };
Do horrible casts (to 8 bit, then to uint) to avoid sign-extension problems that make this loop run too far (0xAA < 0, without a cast is sign-extended to 0xFFFFFFAA, which is a NOT 0x00AA).
  for (u_i = 0; u_i <= ((uint) ((uint8_t) CMD_TOKEN)); u_i++)
    au8_data[u_i + 4] = u_i;
  au8_data[u_i + 4] = ESCAPED_CMD;
  for (; u_i < 256; u_i++)
    au8_data[u_i + 5] = u_i;
  checkSendVar(0, 261, au8_data);
}
 
Specify an index that’s too high
void testSpecifyIndexTooHigh() {
Dummy buffer to hold variable data. Initialize it to something to avoid “unreferenced local variable” warnings.
  uint8_t au8_buf[1] = {0};
  REQUIRE_ASSERT(specifyVar(NUM_XFER_VARS + 1, au8_buf, 1, TRUE, "", "", ""),
                 "specifyVar:indexTooHigh");
}
 
Specify with NULL data
void testSpecifyNullData() {
  REQUIRE_ASSERT(specifyVar(0, NULL, 1, TRUE, "", "", ""),
                 "specifyVar:nullData");
}
 
Specify with an invalid size
void testSpecifyInvalidSize() {
Dummy buffer to hold variable data. Initialize it to something to avoid “unreferenced local variable” warnings.
  uint8_t au8_buf[1] = {0};
  REQUIRE_ASSERT(specifyVar(0, au8_buf, 0, TRUE, "", "", ""),
                 "specifyVar:invalidSize");
  REQUIRE_ASSERT(specifyVar(0, au8_buf, 257, TRUE, "", "", ""),
                 "specifyVar:invalidSize");
}
 
Minimally specify a variable
void testSpecifyMinimalVar() {
Dummy buffer to hold variable data
  uint8_t au8_buf[1];
Expected transmission
  uint8_t au8_data[5 + 3] = { CMD_TOKEN, CMD_SEND_RECEIVE_VAR, 0 /* u_varIndex */,
                              3 /* length of rest - 1 */, /* var size - 1 */ 0, /* data */ 0, 0, 0
                            };
Test it out
  au8_outCharData = au8_data;
  st_outCharLen = 8;
  specifyVar(0 /* u_varIndex */, au8_buf, 1 /* u_size */,
             TRUE /* b_isWriteable */, "", "", "");
  ASSERT(st_outCharIndex == st_outCharLen);
Make sure data structure was updated
  ASSERT(xferVar[0].pu8_data == au8_buf);
  ASSERT(xferVar[0].u8_size == 0);
  ASSERT(isVarWriteable(0));
}
 
Test specifying a var with a format string which exceeds the max length. Also check a send-only variable.
void testSpecifyLongFormat() {
Dummy buffer to hold variable data
  uint8_t au8_buf[1];
Expected transmission
  uint8_t au8_data[5 + 255] = { CMD_TOKEN, CMD_SEND_ONLY, 0 /* u_varIndex */,
                                255 /* length of rest - 1 */, 0 /* var size - 1 */,
format string – filled in below
                              };
  uint u_i;
  for (u_i = 5; u_i < 5 + 255; u_i++)
    au8_data[u_i] = ' ';
 
Test it out
  au8_outCharData = au8_data;
  st_outCharLen = 4 + 256;
  specifyVar(0 /* u_varIndex */, au8_buf, 1 /* u_size */,
             FALSE /* b_isWriteable */, // a looong format string
             "                                                                                "
             "                                                                                "
             "                                                                                "
             "                                                                                "
             "                                                                                ",
             "" /* name */, "" /* description */);
  ASSERT(st_outCharIndex == st_outCharLen);
Make sure data structure was updated
  ASSERT(xferVar[0].pu8_data == au8_buf);
  ASSERT(xferVar[0].u8_size == 0);
  ASSERT(!isVarWriteable(0));
}
 
Test specifying a var with a name string which exceeds the max length. Also check a send-only variable.
void testSpecifyLongName() {
Dummy buffer to hold variable data
  uint8_t au8_buf[1];
Expected transmission
  uint8_t au8_data[6 + 254] = { CMD_TOKEN, CMD_SEND_ONLY, 0 /* u_varIndex */,
                                255 /* length of rest - 1 */, 0 /* var size - 1 */,
                                0 /* empty format string */,
name – filled in below
                              };
  uint u_i;
  for (u_i = 6; u_i < 6 + 254; u_i++)
    au8_data[u_i] = ' ';
 
Test it out
  au8_outCharData = au8_data;
  st_outCharLen = 4 + 256;
  specifyVar(0 /* u_varIndex */, au8_buf, 1 /* u_size */,
             FALSE /* b_isWriteable */, "" /* format */, // a looong name string
             "                                                                                "
             "                                                                                "
             "                                                                                "
             "                                                                                "
             "                                                                                ",
             "" /* description */);
  ASSERT(st_outCharIndex == st_outCharLen);
Make sure data structure was updated
  ASSERT(xferVar[0].pu8_data == au8_buf);
  ASSERT(xferVar[0].u8_size == 0);
  ASSERT(!isVarWriteable(0));
}
 
Test specifying a var with a description string which exceeds the max length. Also check a send-only variable.
void testSpecifyLongDesc() {
Dummy buffer to hold variable data
  uint8_t au8_buf[1];
Expected transmission
  uint8_t au8_data[7 + 253] = { CMD_TOKEN, CMD_SEND_ONLY, 0 /* u_varIndex */,
                                255 /* length of rest - 1 */, 0 /* var size - 1 */,
                                /* empty format string */ 0, /* empty name string */ 0,
description – filled in below
                              };
  uint u_i;
  for (u_i = 7; u_i < 7 + 253; u_i++)
    au8_data[u_i] = ' ';
 
Test it out
  au8_outCharData = au8_data;
  st_outCharLen = 4 + 256;
  specifyVar(0 /* u_varIndex */, au8_buf, 1 /* u_size */,
             FALSE /* b_isWriteable */, "" /* format */, "" /* name */,
a looong description string
             "                                                                                "
             "                                                                                "
             "                                                                                "
             "                                                                                "
             "                                                                                ");
  ASSERT(st_outCharIndex == st_outCharLen);
Make sure data structure was updated
  ASSERT(xferVar[0].pu8_data == au8_buf);
  ASSERT(xferVar[0].u8_size == 0);
  ASSERT(!isVarWriteable(0));
}
 
Send to a index that’s too high
void testFormatIndexTooHigh() {
Dummy buffer to hold variable data. Initialize it to something to avoid “unreferenced local variable” warnings.
  char psz_buf[200] = {0};
  REQUIRE_ASSERT(formatVar(NUM_XFER_VARS + 1, psz_buf), "formatVar:indexTooHigh");
}
 
Send to an unconfigured index
void testFormatIndexUnspecificed() {
Dummy buffer to hold variable data. Initialize it to something to avoid “unreferenced local variable” warnings.
  char psz_buf[200] = {0};
  REQUIRE_ASSERT(formatVar(0, psz_buf), "formatVar:indexNotSpecified");
}
 
 
A list of functions which comprise tests to be run, terminated with a NULL.
void (*afp_testList[])() = {
  findChar,
  findEscapedCommandChar,
  findCommand,
  findEscapedCommand,
  findRepeatedWait,
  findRepeatedCommand,
  sendLetter,
  send0x00,
  send0xFF,
  sendEscapedCommand,
  sendOneByteData,
  sendFourBytesData,
  sendFourBytesCmdTokenData,
  sendRepeatedCommand,
  sendCommandCmdToken,
  sendRepeatedCommandCmdToken,
  sendWithTimeout,
  sendInterruptedCommand,
  sendToUnspecifiedIndex,
  sendToHighIndex,
  sendWithWrongSize,
  sendLongToUnspecifiedIndex,
  sendLongToHighIndex,
  sendLongWithWrongSize,
  sendLongData,
#ifdef MICROCONTROLLER
  testSpecifyLongFormat,
  testSpecifyLongName,
  testSpecifyLongDesc,
  sendReadOnly,
  sendVarSpecMicrocontroller,
  testSpecifyMinimalVar,
#else
  sendVarSpec,
  sendWriteableVarSpec,
  resendVarSpec,
  sendEmptyVarSpec,
  sendFormatOnlyVarSpec,
  sendNameOnlyVarSpec,
  sendVarSpecAndData,
  testSendToReadOnly,
  testFormatIndexTooHigh,
  testFormatIndexUnspecificed,
#endif
  testSendIndexTooHigh,
  testSendIndexUnspecificed,
  testSendOneByteVar,
  testSendOneEscapedByteVar,
  testSendFourByteVar,
  testSend256ByteVar,
  testSpecifyIndexTooHigh,
  testSpecifyNullData,
  testSpecifyInvalidSize,
  NULL
};
 
 
Execute one test. This resets the state machines before a run to create a clean slate for every test.
void runTest(
Index of test to run. NO BOUNDS CHECKING is performed on this index. Be careful.
  uint u_index) {
  initDataXfer();
  clearOutChar();
  (afp_testList[u_index])();  // Execute the specified test
}
 
Run all the tests by executing everything in the list of tests.
void runAllTests() {
  uint u_index;
  for (u_index = 0; afp_testList[u_index] != NULL; u_index++) {
    printf("Running test %d...", u_index + 1);
    runTest(u_index);
    printf("success.\n");
  }
Free all memory used by the last test (for the PC; the microcontroller doesn’t dynamically allocate memory).
  initDataXfer();
  printf("All %d tests passed.\n", u_index);
}