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);
}