From af4e630a29e8ddcb11f377d3b0e6f6d580040313 Mon Sep 17 00:00:00 2001 From: Jan Breuer <jan.breuer@jaybee.cz> Date: 周五, 12 12月 2014 17:50:29 +0800 Subject: [PATCH] Parser refactoring --- libscpi/Makefile | 2 libscpi/src/parser.c | 84 +++++++ Makefile | 3 libscpi/test/test_lexer_parser.c | 4 libscpi/src/error.c | 5 libscpi/src/fifo.c | 26 ++ libscpi/src/lexer.c | 198 +++++++++++++++++ libscpi/inc/scpi/error.h | 1 libscpi/inc/scpi/types.h | 9 libscpi/test/test_scpi_utils.c | 12 + libscpi/inc/scpi/parser.h | 2 libscpi/test/test_parser.c | 252 ++++++++++++++++++++++ libscpi/src/utils.c | 6 13 files changed, 582 insertions(+), 22 deletions(-) diff --git a/Makefile b/Makefile index 14ff52a..ec20bb2 100644 --- a/Makefile +++ b/Makefile @@ -5,3 +5,6 @@ clean: $(MAKE) clean -C libscpi $(MAKE) clean -C examples + +build-tests: + $(MAKE) test -C libscpi \ No newline at end of file diff --git a/libscpi/Makefile b/libscpi/Makefile index 8e57c36..90988ea 100644 --- a/libscpi/Makefile +++ b/libscpi/Makefile @@ -39,7 +39,7 @@ TESTS = $(addprefix test/, \ - test_fifo.c test_scpi_utils.c test_lexer_parser.c\ + test_fifo.c test_scpi_utils.c test_lexer_parser.c test_parser.c\ ) TESTS_OBJS = $(TESTS:.c=.o) diff --git a/libscpi/inc/scpi/error.h b/libscpi/inc/scpi/error.h index 27ad31b..0e21a96 100644 --- a/libscpi/inc/scpi/error.h +++ b/libscpi/inc/scpi/error.h @@ -62,6 +62,7 @@ X(SCPI_ERROR_INVALID_SUFFIX, -131, "Invalid suffix") \ X(SCPI_ERROR_SUFFIX_NOT_ALLOWED, -138, "Suffix not allowed") \ X(SCPI_ERROR_INVALID_STRING_DATA, -151, "Invalid string data") \ + X(SCPI_ERROR_INVALID_BLOCK_DATA, -161, "Invalid block data") \ X(SCPI_ERROR_EXECUTION_ERROR, -200, "Execution error") \ X(SCPI_ERROR_ILLEGAL_PARAMETER_VALUE,-224,"Illegal parameter value") \ X(SCPI_ERROR_SYSTEM_ERROR, -310, "System error") \ diff --git a/libscpi/inc/scpi/parser.h b/libscpi/inc/scpi/parser.h index 47a38ec..834a12f 100644 --- a/libscpi/inc/scpi/parser.h +++ b/libscpi/inc/scpi/parser.h @@ -68,7 +68,7 @@ // scpi_bool_t SCPI_ParamString(scpi_t * context, const char ** value, size_t * len, scpi_bool_t mandatory); scpi_bool_t SCPI_ParamCharacters(scpi_t * context, const char ** value, size_t * len, scpi_bool_t mandatory); #define SCPI_ParamArbitraryBlock SCPI_ParamCharacters - scpi_bool_t SCPI_ParamText(scpi_t * context, const char ** value, size_t * len, int * type, scpi_bool_t mandatory); +// scpi_bool_t SCPI_ParamText(scpi_t * context, const char ** value, size_t * len, int * type, scpi_bool_t mandatory); scpi_bool_t SCPI_ParamBool(scpi_t * context, scpi_bool_t * value, scpi_bool_t mandatory); scpi_bool_t SCPI_ParamChoice(scpi_t * context, const char * options[], int32_t * value, scpi_bool_t mandatory); diff --git a/libscpi/inc/scpi/types.h b/libscpi/inc/scpi/types.h index 29d8bf4..52ee8f2 100644 --- a/libscpi/inc/scpi/types.h +++ b/libscpi/inc/scpi/types.h @@ -117,6 +117,13 @@ char * data; }; typedef struct _scpi_buffer_t scpi_buffer_t; + + struct _scpi_const_buffer_t { + size_t length; + size_t position; + const char * data; + }; + typedef struct _scpi_const_buffer_t scpi_const_buffer_t; typedef size_t(*scpi_write_t)(scpi_t * context, const char * data, size_t len); typedef scpi_result_t(*scpi_write_control_t)(scpi_t * context, scpi_ctrl_name_t ctrl, scpi_reg_val_t val); @@ -231,7 +238,7 @@ struct _scpi_param_list_t { const scpi_command_t * cmd; lex_state_t lex_state; - scpi_buffer_t cmd_raw; + scpi_const_buffer_t cmd_raw; }; typedef struct _scpi_param_list_t scpi_param_list_t; diff --git a/libscpi/src/error.c b/libscpi/src/error.c index 6dd3e6d..da0398e 100644 --- a/libscpi/src/error.c +++ b/libscpi/src/error.c @@ -45,7 +45,10 @@ static scpi_fifo_t local_error_queue; - +/** + * Initialize error queue + * @param context - scpi context + */ void SCPI_ErrorInit(scpi_t * context) { /* * // FreeRTOS diff --git a/libscpi/src/fifo.c b/libscpi/src/fifo.c index 7011030..341bc8e 100644 --- a/libscpi/src/fifo.c +++ b/libscpi/src/fifo.c @@ -1,17 +1,31 @@ #include "fifo_private.h" +/** + * Initialize fifo + * @param fifo + */ void fifo_init(scpi_fifo_t * fifo) { fifo->wr = 0; fifo->rd = 0; fifo->size = FIFO_SIZE; } +/** + * Empty fifo + * @param fifo + */ void fifo_clear(scpi_fifo_t * fifo) { fifo->wr = 0; fifo->rd = 0; } +/** + * Add element to fifo. If fifo is full, remove last element. + * @param fifo + * @param value + * @return + */ scpi_bool_t fifo_add(scpi_fifo_t * fifo, int16_t value) { /* FIFO full? */ if (fifo->wr == ((fifo->rd + fifo->size) % (fifo->size + 1))) { @@ -24,6 +38,12 @@ return TRUE; } +/** + * Remove element form fifo + * @param fifo + * @param value + * @return FALSE - fifo is empty + */ scpi_bool_t fifo_remove(scpi_fifo_t * fifo, int16_t * value) { /* FIFO empty? */ if (fifo->wr == fifo->rd) { @@ -39,6 +59,12 @@ return TRUE; } +/** + * Retrive number of elements in fifo + * @param fifo + * @param value + * @return + */ scpi_bool_t fifo_count(scpi_fifo_t * fifo, int16_t * value) { *value = fifo->wr - fifo->rd; if (*value < 0) { diff --git a/libscpi/src/lexer.c b/libscpi/src/lexer.c index 9060d2f..a1be5e4 100644 --- a/libscpi/src/lexer.c +++ b/libscpi/src/lexer.c @@ -39,8 +39,13 @@ #include <string.h> #include "lexer_private.h" +#include "scpi/error.h" -/* identify character */ +/** + * Is white space + * @param c + * @return + */ static int isws(int c) { if ((c == ' ') || (c == '\t')) { return 1; @@ -48,6 +53,11 @@ return 0; } +/** + * Is binary digit + * @param c + * @return + */ static int isbdigit(int c) { if ((c == '0') || (c == '1')) { return 1; @@ -55,6 +65,11 @@ return 0; } +/** + * Is hexadecimal digit + * @param c + * @return + */ static int isqdigit(int c) { if ((c == '0') || (c == '1') || (c == '2') || (c == '3') || (c == '4') || (c == '5') || (c == '6') || (c == '7')) { return 1; @@ -62,6 +77,11 @@ return 0; } +/** + * Is end of string + * @param state + * @return + */ static int iseos(lex_state_t * state) { if ((state->buffer + state->len) <= (state->pos)) { return 1; @@ -70,30 +90,66 @@ } } +/** + * Private export of iseos + * @param state + * @return + */ int scpiLex_IsEos(lex_state_t * state) { return iseos(state); } +/** + * Test current character + * @param state + * @param chr + * @return + */ static int ischr(lex_state_t * state, char chr) { return (state->pos[0] == chr); } +/** + * Is plus or minus + * @param c + * @return + */ static int isplusmn(int c) { return c == '+' || c == '-'; } +/** + * Is letter H + * @param c + * @return + */ static int isH(int c) { return c == 'h' || c == 'H'; } +/** + * Is letter B + * @param c + * @return + */ static int isB(int c) { return c == 'b' || c == 'B'; } +/** + * Is letter Q + * @param c + * @return + */ static int isQ(int c) { return c == 'q' || c == 'Q'; } +/** + * Is letter E + * @param c + * @return + */ static int isE(int c) { return c == 'e' || c == 'E'; } @@ -106,6 +162,11 @@ /* 7.4.1 <PROGRAM MESSAGE UNIT SEPARATOR>*/ // TODO: static int skipProgramMessageUnitSeparator(lex_state_t * state) +/** + * Skip all whitespaces + * @param state + * @return + */ static int skipWs(lex_state_t * state) { int someSpace = 0; while (!iseos(state) && isws(state->pos[0])) { @@ -122,6 +183,11 @@ /* 7.5.2 <PROGRAM MESSAGE TERMINATOR> */ // static int skipProgramMessageTerminator(lex_state_t * state) +/** + * Skip decimal digit + * @param state + * @return + */ static int skipDigit(lex_state_t * state) { if (!iseos(state) && isdigit(state->pos[0])) { state->pos++; @@ -131,6 +197,11 @@ } } +/** + * Skip multiple decimal digits + * @param state + * @return + */ static int skipNumbers(lex_state_t * state) { int someNumbers = 0; while (!iseos(state) && isdigit(state->pos[0])) { @@ -140,6 +211,11 @@ return someNumbers; } +/** + * Skip plus or minus + * @param state + * @return + */ static int skipPlusmn(lex_state_t * state) { if (!iseos(state) && isplusmn(state->pos[0])) { state->pos++; @@ -149,6 +225,11 @@ } } +/** + * Skip any character from 'a'-'Z' + * @param state + * @return + */ static int skipAlpha(lex_state_t * state) { int someLetters = 0; while (!iseos(state) && isalpha(state->pos[0])) { @@ -158,6 +239,12 @@ return someLetters; } +/** + * Skip exact character chr or nothing + * @param state + * @param chr + * @return + */ static int skipChr(lex_state_t * state, int chr) { if (!iseos(state) && ischr(state, chr)) { state->pos++; @@ -167,6 +254,11 @@ } } +/** + * Skip slash or dot + * @param state + * @return + */ static int skipSlashDot(lex_state_t * state) { if (!iseos(state) && (ischr(state, '/') | ischr(state, '.'))) { state->pos++; @@ -176,6 +268,11 @@ } } +/** + * Skip star + * @param state + * @return + */ static int skipStar(lex_state_t * state) { if (!iseos(state) && ischr(state, '*')) { state->pos++; @@ -185,6 +282,11 @@ } } +/** + * Skip colon + * @param state + * @return + */ static int skipColon(lex_state_t * state) { if (!iseos(state) && ischr(state, ':')) { state->pos++; @@ -195,6 +297,11 @@ } /* 7.6.1.2 <COMMAND PROGRAM HEADER> */ +/** + * Skip program mnemonic [a-z][a-z0-9_]* + * @param state + * @return + */ static int skipProgramMnemonic(lex_state_t * state) { const char * startPos = state->pos; if (!iseos(state) && isalpha(state->pos[0])) { @@ -212,6 +319,12 @@ } /* tokens */ +/** + * Detect token white space + * @param state + * @param token + * @return + */ int scpiLex_WhiteSpace(lex_state_t * state, scpi_token_t * token) { token->ptr = state->pos; @@ -229,6 +342,11 @@ } /* 7.6.1 <COMMAND PROGRAM HEADER> */ +/** + * Skip command program header \*<PROGRAM MNEMONIC> + * @param state + * @return + */ static int skipCommonProgramHeader(lex_state_t * state) { int res; if (skipStar(state)) { @@ -246,6 +364,11 @@ return SKIP_NONE; } +/** + * Skip compound program header :<PROGRAM MNEMONIC>:<PROGRAM MNEMONIC>... + * @param state + * @return + */ static int skipCompoundProgramHeader(lex_state_t * state) { int res; int firstColon = skipColon(state); @@ -270,6 +393,12 @@ } } +/** + * Detect token command or compound program header + * @param state + * @param token + * @return + */ int scpiLex_ProgramHeader(lex_state_t * state, scpi_token_t * token) { int res; token->ptr = state->pos; @@ -309,6 +438,12 @@ } /* 7.7.1 <CHARACTER PROGRAM DATA> */ +/** + * Detect token "Character program data" + * @param state + * @param token + * @return + */ int scpiLex_CharacterProgramData(lex_state_t * state, scpi_token_t * token) { token->ptr = state->pos; @@ -360,6 +495,12 @@ return someNumbers; } +/** + * Detect token Decimal number + * @param state + * @param token + * @return + */ int scpiLex_DecimalNumericProgramData(lex_state_t * state, scpi_token_t * token) { const char * rollback; token->ptr = state->pos; @@ -442,6 +583,12 @@ return someNumbers; } +/** + * Detect token nondecimal number + * @param state + * @param token + * @return + */ int scpiLex_NondecimalNumericData(lex_state_t * state, scpi_token_t * token) { token->ptr = state->pos; int someNumbers = 0; @@ -503,6 +650,12 @@ skipQuoteProgramData(state, '\''); } +/** + * Detect token String data + * @param state + * @param token + * @return + */ int scpiLex_StringProgramData(lex_state_t * state, scpi_token_t * token) { token->ptr = state->pos; @@ -549,9 +702,15 @@ return isdigit(c) && (c != '0'); } +/** + * Detect token Block Data + * @param state + * @param token + * @return + */ int scpiLex_ArbitraryBlockProgramData(lex_state_t * state, scpi_token_t * token) { int i; - int j = 0; + int arbitraryBlockLength = 0; const char * ptr = state->pos; token->ptr = state->pos; @@ -563,8 +722,8 @@ for (; i > 0; i--) { if (!iseos(state) && isdigit(state->pos[0])) { - j *= 10; - j += (state->pos[0] - '0'); + arbitraryBlockLength *= 10; + arbitraryBlockLength += (state->pos[0] - '0'); state->pos++; } else { break; @@ -572,12 +731,12 @@ } if (i == 0) { - state->pos += j; + state->pos += arbitraryBlockLength; if ((state->buffer + state->len) < (state->pos)) { token->len = 0; } else { - token->ptr = state->pos - j; - token->len = j; + token->ptr = state->pos - arbitraryBlockLength; + token->len = arbitraryBlockLength; } } else { token->len = 0; @@ -621,7 +780,12 @@ } // TODO: 7.7.7.2-2 recursive - any program data - +/** + * Detect token Expression + * @param state + * @param token + * @return + */ int scpiLex_ProgramExpression(lex_state_t * state, scpi_token_t * token) { token->ptr = state->pos; @@ -648,6 +812,12 @@ return token->len; } +/** + * Detect token comma + * @param state + * @param token + * @return + */ int scpiLex_Comma(lex_state_t * state, scpi_token_t * token) { token->ptr = state->pos; @@ -662,6 +832,12 @@ return token->len; } +/** + * Detect token semicolon + * @param state + * @param token + * @return + */ int scpiLex_Semicolon(lex_state_t * state, scpi_token_t * token) { token->ptr = state->pos; @@ -676,6 +852,12 @@ return token->len; } +/** + * Detect token New line + * @param state + * @param token + * @return + */ int scpiLex_NewLine(lex_state_t * state, scpi_token_t * token) { token->ptr = state->pos; diff --git a/libscpi/src/parser.c b/libscpi/src/parser.c index 2f95fe7..7f8f6b6 100644 --- a/libscpi/src/parser.c +++ b/libscpi/src/parser.c @@ -299,6 +299,11 @@ return SCPI_ResultIntBase(context, val, 10); } +/** + * Return prefix of nondecimal base + * @param base + * @return + */ static const char * getBasePrefix(int8_t base) { switch (base) { case 2: return "#B"; @@ -308,6 +313,13 @@ } } +/** + * Write integer value in specific base to the result + * @param context + * @param val + * @param base + * @return + */ size_t SCPI_ResultIntBase(scpi_t * context, int32_t val, int8_t base) { char buffer[33]; const char * basePrefix; @@ -353,6 +365,7 @@ size_t result = 0; result += writeDelimiter(context); result += writeData(context, "\"", 1); + // TODO: convert " to "" result += writeData(context, data, strlen(data)); result += writeData(context, "\"", 1); context->output_count++; @@ -371,7 +384,13 @@ /* parsing parameters */ - +/** + * Get one parameter from command line + * @param context + * @param parameter + * @param mandatory + * @return + */ scpi_bool_t SCPI_Parameter(scpi_t * context, scpi_parameter_t * parameter, scpi_bool_t mandatory) { scpi_token_t token; lex_state_t * state; @@ -455,6 +474,12 @@ } } +/** + * Detect if parameter is number + * @param parameter + * @param suffixAllowed + * @return + */ scpi_bool_t SCPI_ParamIsNumber(scpi_parameter_t * parameter, scpi_bool_t suffixAllowed) { switch (parameter->type) { case TokHexnum: @@ -469,6 +494,13 @@ } } +/** + * Read floating point parameter + * @param context + * @param value + * @param mandatory + * @return + */ scpi_bool_t SCPI_ParamDouble(scpi_t * context, double * value, scpi_bool_t mandatory) { scpi_bool_t result; @@ -494,6 +526,13 @@ return result; } +/** + * Read integer parameter + * @param context + * @param value + * @param mandatory + * @return + */ scpi_bool_t SCPI_ParamInt(scpi_t * context, int32_t * value, scpi_bool_t mandatory) { // TODO: remove dependency on double @@ -513,6 +552,14 @@ return result; } +/** + * Read character parameter + * @param context + * @param value + * @param len + * @param mandatory + * @return + */ scpi_bool_t SCPI_ParamCharacters(scpi_t * context, const char ** value, size_t * len, scpi_bool_t mandatory) { scpi_bool_t result; @@ -534,6 +581,13 @@ return result; } +/** + * Read BOOL parameter (0,1,ON,OFF) + * @param context + * @param value + * @param mandatory + * @return + */ scpi_bool_t SCPI_ParamBool(scpi_t * context, scpi_bool_t * value, scpi_bool_t mandatory) { scpi_bool_t result; @@ -570,6 +624,14 @@ return result; } +/** + * Read value from list of options + * @param context + * @param options + * @param value + * @param mandatory + * @return + */ scpi_bool_t SCPI_ParamChoice(scpi_t * context, const char * options[], int32_t * value, scpi_bool_t mandatory) { size_t res; @@ -604,6 +666,12 @@ return result; } +/** + * Parse one parameter and detect type + * @param state + * @param token + * @return + */ int scpiParser_parseProgramData(lex_state_t * state, scpi_token_t * token) { scpi_token_t tmp; int result = 0; @@ -636,6 +704,13 @@ return result + realLen; } +/** + * Skip all parameters to correctly detect end of command line. + * @param state + * @param token + * @param numberOfParameters + * @return + */ int scpiParser_parseAllProgramData(lex_state_t * state, scpi_token_t * token, int * numberOfParameters) { int result; @@ -685,6 +760,13 @@ token->type = TokUnknown; } +/** + * Skip complete command line - program header and parameters + * @param state + * @param buffer + * @param len + * @return + */ int scpiParser_detectProgramMessageUnit(scpi_parser_state_t * state, const char * buffer, int len) { lex_state_t lex_state; scpi_token_t tmp; diff --git a/libscpi/src/utils.c b/libscpi/src/utils.c index 922b870..0d0a274 100644 --- a/libscpi/src/utils.c +++ b/libscpi/src/utils.c @@ -74,8 +74,6 @@ * @param len string buffer length * @return number of bytes written to str (without '\0') */ -// TODO: add support for other bases - size_t longToStr(int32_t val, char * str, size_t len, int8_t base) { uint32_t x = 1000000000L; int_fast8_t digit; @@ -90,14 +88,14 @@ } while ((val / x) == 0) { - x /= 10; + x /= base; } do { digit = (uint8_t) (val / x); if (pos < len) str[pos++] = digit + '0'; val -= digit * x; - x /= 10; + x /= base; } while (x && (pos < len)); } diff --git a/libscpi/test/test_lexer_parser.c b/libscpi/test/test_lexer_parser.c index c3bb4f8..559cdb4 100644 --- a/libscpi/test/test_lexer_parser.c +++ b/libscpi/test/test_lexer_parser.c @@ -271,11 +271,7 @@ TEST_TOKEN(" 0", scpiParser_parseProgramData, 1, 1, TokDecimalNumericProgramData); TEST_TOKEN(" ON", scpiParser_parseProgramData, 1, 2, TokProgramMnemonic); TEST_TOKEN("OFF ", scpiParser_parseProgramData, 0, 3, TokProgramMnemonic); - - // TODO: finish bool test } - -// TODO: SCPI_Parameter test int main() { CU_pSuite pSuite = NULL; diff --git a/libscpi/test/test_parser.c b/libscpi/test/test_parser.c new file mode 100644 index 0000000..b243e56 --- /dev/null +++ b/libscpi/test/test_parser.c @@ -0,0 +1,252 @@ +/* + * File: test_parser.c + * Author: Jan Breuer + */ + +#include <stdio.h> +#include <stdlib.h> +#include "CUnit/Basic.h" + +#include "scpi/scpi.h" + +/* + * CUnit Test Suite + */ + +static const scpi_command_t scpi_commands[] = { + /* IEEE Mandated Commands (SCPI std V1999.0 4.1.1) */ +// { .pattern = "*CLS", .callback = SCPI_CoreCls,}, +// { .pattern = "*ESE", .callback = SCPI_CoreEse,}, +// { .pattern = "*ESE?", .callback = SCPI_CoreEseQ,}, +// { .pattern = "*ESR?", .callback = SCPI_CoreEsrQ,}, +// { .pattern = "*IDN?", .callback = SCPI_CoreIdnQ,}, +// { .pattern = "*OPC", .callback = SCPI_CoreOpc,}, +// { .pattern = "*OPC?", .callback = SCPI_CoreOpcQ,}, +// { .pattern = "*RST", .callback = SCPI_CoreRst,}, +// { .pattern = "*SRE", .callback = SCPI_CoreSre,}, +// { .pattern = "*SRE?", .callback = SCPI_CoreSreQ,}, +// { .pattern = "*STB?", .callback = SCPI_CoreStbQ,}, +// { .pattern = "*TST?", .callback = SCPI_CoreTstQ,}, +// { .pattern = "*WAI", .callback = SCPI_CoreWai,}, + + /* Required SCPI commands (SCPI std V1999.0 4.2.1) */ +// {.pattern = "SYSTem:ERRor[:NEXT]?", .callback = SCPI_SystemErrorNextQ,}, +// {.pattern = "SYSTem:ERRor:COUNt?", .callback = SCPI_SystemErrorCountQ,}, +// {.pattern = "SYSTem:VERSion?", .callback = SCPI_SystemVersionQ,}, + +// {.pattern = "STATus:QUEStionable[:EVENt]?", .callback = SCPI_StatusQuestionableEventQ,}, +// {.pattern = "STATus:QUEStionable:ENABle", .callback = SCPI_StatusQuestionableEnable,}, +// {.pattern = "STATus:QUEStionable:ENABle?", .callback = SCPI_StatusQuestionableEnableQ,}, + +// {.pattern = "STATus:PRESet", .callback = SCPI_StatusPreset,}, + + SCPI_CMD_LIST_END +}; + +size_t SCPI_Write(scpi_t * context, const char * data, size_t len) +{ + (void)context; + (void)data; + (void)len; +} + +int SCPI_Error(scpi_t * context, int_fast16_t err) +{ + (void)context; + (void)err; +} + +scpi_result_t SCPI_Control(scpi_t * context, scpi_ctrl_name_t ctrl, scpi_reg_val_t val) +{ + (void)context; + (void)ctrl; + (void)val; +} + +scpi_result_t SCPI_Reset(scpi_t * context) +{ + (void)context; +} + +scpi_result_t SCPI_Test(scpi_t * context) +{ + (void)context; +} + +scpi_result_t SCPI_Flush(scpi_t * context) +{ + (void)context; +} + + +static scpi_interface_t scpi_interface = { + .error = SCPI_Error, + .write = SCPI_Write, + .control = SCPI_Control, + .flush = SCPI_Flush, + .reset = SCPI_Reset, + .test = SCPI_Test, +}; + +#define SCPI_INPUT_BUFFER_LENGTH 256 +static char scpi_input_buffer[SCPI_INPUT_BUFFER_LENGTH]; + +static scpi_reg_val_t scpi_regs[SCPI_REG_COUNT]; + + +scpi_t scpi_context = { + .cmdlist = scpi_commands, + .buffer = { + .length = SCPI_INPUT_BUFFER_LENGTH, + .data = scpi_input_buffer, + }, + .interface = &scpi_interface, + .registers = scpi_regs, + .units = scpi_units_def, + .special_numbers = scpi_special_numbers_def, + .idn = {"MANUFACTURE", "INSTR2013", NULL, "01-02"}, +}; + +int init_suite(void) { + SCPI_Init(&scpi_context); + + return 0; +} + +int clean_suite(void) { + return 0; +} + + +#define TEST_ParamInt(data, mandatory, expected_value, expected_result, expected_error_code) \ +{ \ + int32_t value; \ + scpi_bool_t result; \ + int16_t errCode; \ + \ + SCPI_CoreCls(&scpi_context); \ + scpi_context.input_count = 0; \ + scpi_context.param_list.lex_state.buffer = data; \ + scpi_context.param_list.lex_state.len = strlen(scpi_context.param_list.lex_state.buffer);\ + scpi_context.param_list.lex_state.pos = scpi_context.param_list.lex_state.buffer; \ + result = SCPI_ParamInt(&scpi_context, &value, mandatory); \ + \ + errCode = SCPI_ErrorPop(&scpi_context); \ + CU_ASSERT_EQUAL(result, expected_result); \ + if (expected_result) { \ + CU_ASSERT_EQUAL(value, expected_value); \ + } \ + CU_ASSERT_EQUAL(errCode, expected_error_code); \ +} + +void testSCPI_ParamInt(void) { + TEST_ParamInt("10", TRUE, 10, TRUE, 0); + TEST_ParamInt("", FALSE, 0, FALSE, 0); + TEST_ParamInt("10.5", TRUE, 10, TRUE, 0); // TODO: should be FALSE, -104 + TEST_ParamInt("#B101010", TRUE, 42, TRUE, 0); + TEST_ParamInt("#H101010", TRUE, 1052688, TRUE, 0); + TEST_ParamInt("#Q10", TRUE, 8, TRUE, 0); + + TEST_ParamInt("", TRUE, 0, FALSE, -109); // missing parameter + TEST_ParamInt("abcd", TRUE, 0, FALSE, -104); // Data type error + TEST_ParamInt("10.5V", TRUE, 0, FALSE, -138); + TEST_ParamInt("10V", TRUE, 0, FALSE, -138); +} + +#define TEST_ParamDouble(data, mandatory, expected_value, expected_result, expected_error_code) \ +{ \ + double value; \ + scpi_bool_t result; \ + int16_t errCode; \ + \ + SCPI_CoreCls(&scpi_context); \ + scpi_context.input_count = 0; \ + scpi_context.param_list.lex_state.buffer = data; \ + scpi_context.param_list.lex_state.len = strlen(scpi_context.param_list.lex_state.buffer);\ + scpi_context.param_list.lex_state.pos = scpi_context.param_list.lex_state.buffer; \ + result = SCPI_ParamDouble(&scpi_context, &value, mandatory); \ + \ + errCode = SCPI_ErrorPop(&scpi_context); \ + CU_ASSERT_EQUAL(result, expected_result); \ + if (expected_result) { \ + CU_ASSERT_DOUBLE_EQUAL(value, expected_value, 0.000001); \ + } \ + CU_ASSERT_EQUAL(errCode, expected_error_code); \ +} + +void testSCPI_ParamDouble(void) { + TEST_ParamDouble("10", TRUE, 10, TRUE, 0); + TEST_ParamDouble("", FALSE, 0, FALSE, 0); + TEST_ParamDouble("10.5", TRUE, 10.5, TRUE, 0); + TEST_ParamDouble("#B101010", TRUE, 42, TRUE, 0); + TEST_ParamDouble("#H101010", TRUE, 1052688, TRUE, 0); + TEST_ParamDouble("#Q10", TRUE, 8, TRUE, 0); + + TEST_ParamDouble("", TRUE, 0, FALSE, -109); // missing parameter + TEST_ParamDouble("abcd", TRUE, 0, FALSE, -104); // Data type error + TEST_ParamDouble("10.5V", TRUE, 0, FALSE, -138); + TEST_ParamDouble("10V", TRUE, 0, FALSE, -138); +} + +#define TEST_ParamCharacters(data, mandatory, expected_value, expected_result, expected_error_code) \ +{ \ + const char * value; \ + size_t value_len; \ + scpi_bool_t result; \ + int16_t errCode; \ + \ + SCPI_CoreCls(&scpi_context); \ + scpi_context.input_count = 0; \ + scpi_context.param_list.lex_state.buffer = data; \ + scpi_context.param_list.lex_state.len = strlen(scpi_context.param_list.lex_state.buffer);\ + scpi_context.param_list.lex_state.pos = scpi_context.param_list.lex_state.buffer; \ + result = SCPI_ParamCharacters(&scpi_context, &value, &value_len, mandatory); \ + /*printf("%.*s\r\n", (int)value_len, value);*/ \ + errCode = SCPI_ErrorPop(&scpi_context); \ + CU_ASSERT_EQUAL(result, expected_result); \ + if (expected_result) { \ + CU_ASSERT_NSTRING_EQUAL(value, expected_value, value_len); \ + } \ + CU_ASSERT_EQUAL(errCode, expected_error_code); \ +} + +void testSCPI_ParamCharacters(void) { + TEST_ParamCharacters("10", TRUE, "10", TRUE, 0); + TEST_ParamCharacters(" ABCD", TRUE, "ABCD", TRUE, 0); // TokProgramMnemonic + TEST_ParamCharacters("\"ABCD\"", TRUE, "ABCD", TRUE, 0); // TokDoubleQuoteProgramData + TEST_ParamCharacters("\'ABCD\'", TRUE, "ABCD", TRUE, 0); // TokSingleQuoteProgramData + TEST_ParamCharacters("#204ABCD", TRUE, "ABCD", TRUE, 0); // TokArbitraryBlockProgramData + TEST_ParamCharacters("#210ABCD", TRUE, "", FALSE, -151); // invalid Block Data +} + + +int main() { + CU_pSuite pSuite = NULL; + + /* Initialize the CUnit test registry */ + if (CUE_SUCCESS != CU_initialize_registry()) + return CU_get_error(); + + /* Add a suite to the registry */ + pSuite = CU_add_suite("Parser", init_suite, clean_suite); + if (NULL == pSuite) { + CU_cleanup_registry(); + return CU_get_error(); + } + + /* Add the tests to the suite */ + if ((NULL == CU_add_test(pSuite, "SCPI_ParamInt", testSCPI_ParamInt)) + || (NULL == CU_add_test(pSuite, "SCPI_ParamDouble", testSCPI_ParamDouble)) + || (NULL == CU_add_test(pSuite, "SCPI_ParamCharacters", testSCPI_ParamCharacters)) + ) { + CU_cleanup_registry(); + return CU_get_error(); + } + + /* Run all tests using the CUnit Basic interface */ + CU_basic_set_mode(CU_BRM_VERBOSE); + CU_basic_run_tests(); + CU_cleanup_registry(); + return CU_get_error(); +} + diff --git a/libscpi/test/test_scpi_utils.c b/libscpi/test/test_scpi_utils.c index f1b7363..19e7282 100644 --- a/libscpi/test/test_scpi_utils.c +++ b/libscpi/test/test_scpi_utils.c @@ -63,15 +63,25 @@ CU_ASSERT(strnpbrk(str, 4, "xo") == (str + 2)); } + + void test_longToStr() { char str[32]; size_t len; len = longToStr(10, str, 32, 10); CU_ASSERT(len == 2); + CU_ASSERT_STRING_EQUAL(str, "10"); + CU_ASSERT(str[len] == '\0'); + + len = longToStr(10, str, 32, 2); + CU_ASSERT(len == 4); CU_ASSERT(str[0] == '1'); CU_ASSERT(str[1] == '0'); - CU_ASSERT(str[2] == '\0'); + CU_ASSERT(str[2] == '1'); + CU_ASSERT(str[3] == '0'); + CU_ASSERT(str[4] == '\0'); + } void test_doubleToStr() { -- Gitblit v1.9.1