Kenji Arai / mbed-os_TYBLE16

Dependents:   TYBLE16_simple_data_logger TYBLE16_MP3_Air

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers greentea_test_env.cpp Source File

greentea_test_env.cpp

00001 /*
00002  * Copyright (c) 2013-2016, ARM Limited, All Rights Reserved
00003  * SPDX-License-Identifier: Apache-2.0
00004  *
00005  * Licensed under the Apache License, Version 2.0 (the "License"); you may
00006  * not use this file except in compliance with the License.
00007  * You may obtain a copy of the License at
00008  *
00009  * http://www.apache.org/licenses/LICENSE-2.0
00010  *
00011  * Unless required by applicable law or agreed to in writing, software
00012  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
00013  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00014  * See the License for the specific language governing permissions and
00015  * limitations under the License.
00016  */
00017 
00018 #if DEVICE_SERIAL
00019 
00020 #include <ctype.h>
00021 #include <cstdio>
00022 #include <string.h>
00023 #include "greentea-client/test_env.h"
00024 #include "greentea-client/greentea_serial.h"
00025 #include "greentea-client/greentea_metrics.h"
00026 #include "mbed_trace.h"
00027 
00028 /**
00029  *   Generic test suite transport protocol keys
00030  */
00031 const char* GREENTEA_TEST_ENV_END = "end";
00032 const char* GREENTEA_TEST_ENV_EXIT = "__exit";
00033 const char* GREENTEA_TEST_ENV_SYNC = "__sync";
00034 const char* GREENTEA_TEST_ENV_TIMEOUT = "__timeout";
00035 const char* GREENTEA_TEST_ENV_HOST_TEST_NAME = "__host_test_name";
00036 const char* GREENTEA_TEST_ENV_HOST_TEST_VERSION = "__version";
00037 
00038 /**
00039  *   Test suite success code strings
00040  */
00041 const char* GREENTEA_TEST_ENV_SUCCESS = "success";
00042 const char* GREENTEA_TEST_ENV_FAILURE = "failure";
00043 
00044 /**
00045  *   Test case transport protocol start/finish keys
00046  */
00047 const char* GREENTEA_TEST_ENV_TESTCASE_NAME = "__testcase_name";
00048 const char* GREENTEA_TEST_ENV_TESTCASE_COUNT = "__testcase_count";
00049 const char* GREENTEA_TEST_ENV_TESTCASE_START = "__testcase_start";
00050 const char* GREENTEA_TEST_ENV_TESTCASE_FINISH = "__testcase_finish";
00051 const char* GREENTEA_TEST_ENV_TESTCASE_SUMMARY = "__testcase_summary";
00052 // Code Coverage (LCOV)  transport protocol keys
00053 const char* GREENTEA_TEST_ENV_LCOV_START = "__coverage_start";
00054 
00055 /**
00056  *   Auxilary functions
00057  */
00058 static void greentea_notify_timeout(const int);
00059 static void greentea_notify_hosttest(const char *);
00060 static void greentea_notify_completion(const int);
00061 static void greentea_notify_version();
00062 static void greentea_write_string(const char *str);
00063 
00064 /** \brief Handle the handshake with the host
00065  *  \details This is contains the shared handhshake functionality that is used between
00066  *           GREENTEA_SETUP and GREENTEA_SETUP_UUID.
00067  *           This function is blocking.
00068  */
00069 void _GREENTEA_SETUP_COMMON(const int timeout, const char *host_test_name, char *buffer, size_t size) {
00070     greentea_metrics_setup();
00071     // Key-value protocol handshake function. Waits for {{__sync;...}} message
00072     // Sync preamble: "{{__sync;0dad4a9d-59a3-4aec-810d-d5fb09d852c1}}"
00073     // Example value of sync_uuid == "0dad4a9d-59a3-4aec-810d-d5fb09d852c1"
00074 
00075     char _key[8] = {0};
00076 
00077     while (1) {
00078         greentea_parse_kv(_key, buffer, sizeof(_key), size);
00079         greentea_write_string("mbedmbedmbedmbedmbedmbedmbedmbed\r\n");
00080         if (strcmp(_key, GREENTEA_TEST_ENV_SYNC) == 0) {
00081             // Found correct __sync message
00082             greentea_send_kv(_key, buffer);
00083             break;
00084         }
00085     }
00086 
00087 #ifdef MBED_CONF_MBED_TRACE_ENABLE
00088     mbed_trace_init();
00089 #endif
00090 
00091     greentea_notify_version();
00092     greentea_notify_timeout(timeout);
00093     greentea_notify_hosttest(host_test_name);
00094 }
00095 
00096 /** \brief Handshake with host and send setup data (timeout and host test name)
00097  *  \details This function will send preamble to master.
00098  *           After host test name is received master will invoke host test script
00099  *           and add host test's callback handlers to main event loop
00100  *           This function is blocking.
00101  */
00102 extern "C" void GREENTEA_SETUP(const int timeout, const char *host_test_name) {
00103 #if ! defined(NO_GREENTEA)
00104     char _value[GREENTEA_UUID_LENGTH] = {0};
00105     _GREENTEA_SETUP_COMMON(timeout, host_test_name, _value, GREENTEA_UUID_LENGTH);
00106 #endif
00107 }
00108 
00109 /** \brief Handshake with host and send setup data (timeout and host test name). Allows you to preserve sync UUID.
00110  *  \details This function will send preamble to master.
00111  *           After host test name is received master will invoke host test script
00112  *           and add host test's callback handlers to main event loop
00113  *           This function is blocking.
00114  *           This function differs from GREENTEA_SETUP because it allows you to
00115  *           preserve the UUID sent during the sync process.
00116  */
00117 void GREENTEA_SETUP_UUID(const int timeout, const char *host_test_name, char *buffer, size_t size) {
00118     _GREENTEA_SETUP_COMMON(timeout, host_test_name, buffer, size);
00119 }
00120 
00121 /** \brief Notify host (__exit message) side that test suite execution was complete
00122  *  \result Test suite result
00123  *  \details If __exit is not received by host side we will assume TIMEOUT
00124  */
00125 void GREENTEA_TESTSUITE_RESULT(const int result) {
00126     greentea_notify_completion(result);
00127 }
00128 
00129 /**
00130  *  Test Case support
00131  */
00132 
00133 /** \brief Notify host side that test case started
00134  *  \details test_case_name Test case name
00135  */
00136 void GREENTEA_TESTCASE_START(const char *test_case_name) {
00137     greentea_send_kv(GREENTEA_TEST_ENV_TESTCASE_START, test_case_name);
00138 }
00139 
00140 /** \brief Notify host side that test case finished
00141  *  \details test_case_name Test case name
00142  *  \details result Test case result (0 -OK, non zero...)
00143  */
00144 void GREENTEA_TESTCASE_FINISH(const char *test_case_name, const size_t passes, const size_t failed) {
00145     greentea_send_kv(GREENTEA_TEST_ENV_TESTCASE_FINISH, test_case_name, passes, failed);
00146 }
00147 
00148 /**
00149  *****************************************************************************
00150  *  Auxilary functions and key-value protocol support
00151  *****************************************************************************
00152  */
00153 
00154 
00155 /**
00156  *****************************************************************************
00157  *  LCOV support
00158  *****************************************************************************
00159  */
00160 #ifdef MBED_CFG_DEBUG_OPTIONS_COVERAGE
00161 extern "C" void __gcov_flush(void);
00162 extern bool coverage_report;
00163 
00164 /**
00165  * \brief Send code coverage (gcov/LCOV) notification to master
00166  *
00167  *        Generates preamble of message sent to notify host about code coverage data dump.
00168  *
00169  *        This function is used by Mbed OS
00170  *        (see: mbed-os/platform/mbed_retarget.cpp) to generate code coverage
00171  *        messages to host. When code coverage feature is turned on slave will
00172  *        print-out code coverage data in form of key-value protocol.
00173  *        Message with code coverage data will contain message name, path to code
00174  *        coverage output file host will touch and fill with code coverage binary
00175  *        payload. Coverage payload is encoded as stream of ASCII coded bytes ("%02X").
00176  *
00177  * \param path to file with code coverage payload (set by gcov instrumentation)
00178  *
00179  */
00180 void greentea_notify_coverage_start(const char *path) {
00181     printf("{{%s;%s;", GREENTEA_TEST_ENV_LCOV_START, path);
00182 }
00183 
00184 /**
00185  *  \brief Sufix for code coverage message to master (closing statement)
00186  *
00187  *         This function is used by Mbed OS
00188  *         (see: mbed-os/platform/mbed_retarget.cpp) to generate code coverage
00189  *         messages to host. When code coverage feature is turned on slave will
00190  *         print-out code coverage data in form of key-value protocol.
00191  *         Message with code coverage data will contain message name, path to code
00192  *         coverage output file host will touch and fill with code coverage binary
00193  *         payload. Coverage payload is encoded as stream of ASCII coded bytes ("%02X").
00194  *
00195  *         Companion function greentea_notify_coverage_start() defines code coverage message structure
00196  *
00197  */
00198 void greentea_notify_coverage_end() {
00199     printf("}}" NL);
00200 }
00201 
00202 #endif
00203 
00204 /**
00205  *****************************************************************************
00206  *  Key-value protocol support
00207  *****************************************************************************
00208  */
00209 
00210 /**
00211  * \brief Write the preamble characters to the serial port
00212  *
00213  *        This function writes the preamble "{{" which is required
00214  *        for key-value comunication between the target and the host.
00215  *        This uses a Rawserial object, greentea_serial, which provides
00216  *        a direct interface to the USBTX and USBRX serial pins and allows
00217  *        the direct writing of characters using the putc() method.
00218  *        This suite of functions are provided to allow for serial communication
00219  *        to the host from within a thread/ISR.
00220  *
00221  */
00222 inline void greentea_write_preamble()
00223 {
00224     greentea_serial->putc('{');
00225     greentea_serial->putc('{');
00226 }
00227 
00228 /**
00229  * \brief Write the postamble characters to the serial port
00230  *
00231  *        This function writes the postamble "{{\n" which is required
00232  *        for key-value comunication between the target and the host.
00233  *        This uses a Rawserial object, greentea_serial, which provides
00234  *        a direct interface to the USBTX and USBRX serial pins and allows
00235  *        the direct writing of characters using the putc() method.
00236  *        This suite of functions are provided to allow for serial communication
00237  *        to the host from within a thread/ISR.
00238  *
00239  */
00240 inline void greentea_write_postamble()
00241 {
00242     greentea_serial->putc('}');
00243     greentea_serial->putc('}');
00244     greentea_serial->putc('\r');
00245     greentea_serial->putc('\n');
00246 }
00247 
00248 /**
00249  * \brief Write a string to the serial port
00250  *
00251  *        This function writes a '\0' terminated string from the target
00252  *        to the host. It writes directly to the serial port using the
00253  *        greentea_serial, Rawserial object.
00254  *
00255  * \param str - string value
00256  *
00257  */
00258 inline void greentea_write_string(const char *str)
00259 {
00260     while (*str != '\0') {
00261         greentea_serial->putc(*str);
00262         str ++;
00263     }
00264 }
00265 
00266 
00267 /**
00268  * \brief Write an int to the serial port
00269  *
00270  *        This function writes an integer value from the target
00271  *        to the host. The integer value is converted to a string and
00272  *        and then written character by character directly to the serial
00273  *        port using the greentea_serial, Rawserial object.
00274  *        sprintf() is used to convert the int to a string. Sprintf if
00275  *        inherently thread safe so can be used.
00276  *
00277  * \param val - integer value
00278  *
00279  */
00280 #define MAX_INT_STRING_LEN 15
00281 inline void greentea_write_int(const int val)
00282 {
00283     char intval[MAX_INT_STRING_LEN];
00284     unsigned int i = 0;
00285     sprintf(intval, "%d", val);
00286     while (intval[i] != '\0') {
00287         greentea_serial->putc(intval[i]);
00288         i++;
00289     }
00290 }
00291 
00292 /**
00293  * \brief Encapsulate and send key-value message from DUT to host
00294  *
00295  *        This function uses underlying functions to write directly
00296  *        to the serial port, (USBTX). This allows KVs to be used
00297  *        from within interrupt context.
00298  *
00299  * \param key Message key (message/event name)
00300  * \param value Message payload, string value
00301  *
00302  */
00303 extern "C" void greentea_send_kv(const char *key, const char *val) {
00304     if (key && val) {
00305         greentea_write_preamble();
00306         greentea_write_string(key);
00307         greentea_serial->putc(';');
00308         greentea_write_string(val);
00309         greentea_write_postamble();
00310     }
00311 }
00312 
00313 /**
00314  * \brief Encapsulate and send key-value message from DUT to host
00315  *
00316  *        This function uses underlying functions to write directly
00317  *        to the serial port, (USBTX). This allows KVs to be used
00318  *        from within interrupt context.
00319  *        Last value is an integer to avoid integer to string conversion
00320  *        made by the user.
00321  *
00322  * \param key Message key (message/event name)
00323  * \param value Message payload, integer value
00324  *
00325  */
00326 void greentea_send_kv(const char *key, const int val) {
00327     if (key) {
00328         greentea_write_preamble();
00329         greentea_write_string(key);
00330         greentea_serial->putc(';');
00331         greentea_write_int(val);
00332         greentea_write_postamble();
00333     }
00334 }
00335 
00336 /**
00337  * \brief Encapsulate and send key-value-value message from DUT to host
00338  *
00339  *        This function uses underlying functions to write directly
00340  *        to the serial port, (USBTX). This allows KVs to be used
00341  *        from within interrupt context.
00342  *        Last value is an integer to avoid integer to string conversion
00343  *        made by the user.
00344  *
00345  * \param key Message key (message/event name)
00346  * \param value Message payload, string value
00347  * \param result Send additional integer formatted data
00348  *
00349  */
00350 void greentea_send_kv(const char *key, const char *val, const int result) {
00351     if (key) {
00352         greentea_write_preamble();
00353         greentea_write_string(key);
00354         greentea_serial->putc(';');
00355         greentea_write_string(val);
00356         greentea_serial->putc(';');
00357         greentea_write_int(result);
00358         greentea_write_postamble();
00359 
00360     }
00361 }
00362 
00363 /**
00364  * \brief Encapsulate and send key-value-value-value message from DUT to host
00365  *
00366  *        This function uses underlying functions to write directly
00367  *        to the serial port, (USBTX). This allows KVs to be used
00368  *        from within interrupt context.
00369  *        Last 2 values are integers to avoid integer to string conversion
00370  *        made by the user.
00371  *
00372  *        Names of the parameters: this function is used to send test case
00373  *        name with number of passes and failures to host. But it can be used
00374  *        to send any key-value-value-value (string-string-integer-integer)
00375  *        set to host.
00376  *
00377  * \param key Message key (message/event name)
00378  * \param value Message payload, string value
00379  * \param passes Send additional integer formatted data
00380  * \param failures Send additional integer formatted data
00381  *
00382  */
00383 void greentea_send_kv(const char *key, const char *val, const int passes, const int failures) {
00384     if (key) {
00385         greentea_write_preamble();
00386         greentea_write_string(key);
00387         greentea_serial->putc(';');
00388         greentea_write_string(val);
00389         greentea_serial->putc(';');
00390         greentea_write_int(passes);
00391         greentea_serial->putc(';');
00392         greentea_write_int(failures);
00393         greentea_write_postamble();
00394     }
00395 }
00396 
00397 /**
00398  * \brief Encapsulate and send key-value-value message from DUT to host
00399  *
00400  *        This function uses underlying functions to write directly
00401  *        to the serial port, (USBTX). This allows key-value-value to be used
00402  *        from within interrupt context.
00403  *        Both values are integers to avoid integer to string conversion
00404  *        made by the user.
00405  *
00406  *        Names of the parameters: this function is used to send number
00407  *        of passes and failures to host. But it can be used to send any
00408  *        key-value-value (string-integer-integer) message to host.
00409  *
00410  * \param key Message key (message/event name)
00411  * \param value Message payload, integer value
00412  * \param passes Send additional integer formatted data
00413  * \param failures Send additional integer formatted data
00414  *
00415  */
00416 void greentea_send_kv(const char *key, const int passes, const int failures) {
00417     if (key) {
00418         greentea_write_preamble();
00419         greentea_write_string(key);
00420         greentea_serial->putc(';');
00421         greentea_write_int(passes);
00422         greentea_serial->putc(';');
00423         greentea_write_int(failures);
00424         greentea_write_postamble();
00425     }
00426 }
00427 
00428 /**
00429  * \brief Send message with timeout to master in seconds
00430  *
00431  *        GREENTEA_TEST_ENV_TIMEOUT message is part of preamble
00432  *        sent from DUT to host during synchronisation (beginning of test
00433  *        suite execution).
00434  *
00435  *        Notification about total test suite timeout. Timeout is measured
00436  *        from the moment of GREENTEA_TEST_ENV_TIMEOUT reception by host.
00437  *        If timeout is reached host (and host test) will be stopped and
00438  *        control will return to Greentea.
00439  *
00440  * \param timeout Test suite timeout in seconds
00441  *
00442  */
00443 static void greentea_notify_timeout(const int timeout) {
00444     greentea_send_kv(GREENTEA_TEST_ENV_TIMEOUT, timeout);
00445 }
00446 
00447 /**
00448  * \brief Send host test name to master
00449  *
00450  *        GREENTEA_TEST_ENV_HOST_TEST_NAME message is part of preamble
00451  *        sent from DUT to host during synchronisation (beginning of test
00452  *        suite execution).
00453  *
00454  *        Host test Python script implements host side callbacks
00455  *        for key-value events sent from DUT to host. Host test's
00456  *        callbacks are registered after GREENTEA_TEST_ENV_HOST_TEST_NAME
00457  *        message reaches host.
00458  *
00459  * \param host_test_name Host test name, host test will be loaded by mbedhtrun
00460  */
00461 static void greentea_notify_hosttest(const char *host_test_name) {
00462     greentea_send_kv(GREENTEA_TEST_ENV_HOST_TEST_NAME, host_test_name);
00463 }
00464 
00465 /**
00466  * \brief Send to master information that test suite finished its execution
00467  *
00468  *        GREENTEA_TEST_ENV_END and GREENTEA_TEST_ENV_EXIT messages
00469  *        are sent just before test suite execution finishes (noting
00470  *        else to do). You can place it just before you return from your
00471  *        main() function.
00472  *
00473  *        Code coverage: If MEBD_CFG_DEBUG_OPTIONS_COVERAGE is set in the
00474  *        project via build configuration function will output series
00475  *        of code coverage messages GREENTEA_TEST_ENV_LCOV_START with code
00476  *        coverage binary data. This data is captured by Greentea and can
00477  *        be used to generate LCOV reports.
00478  *
00479  * \param result Test suite result from DUT (0 - FAIl, !0 - SUCCESS)
00480  *
00481  */
00482 static void greentea_notify_completion(const int result) {
00483     const char *val = result ? GREENTEA_TEST_ENV_SUCCESS : GREENTEA_TEST_ENV_FAILURE;
00484 #ifdef MBED_CFG_DEBUG_OPTIONS_COVERAGE
00485     coverage_report = true;
00486     __gcov_flush();
00487     coverage_report = false;
00488 #endif
00489     greentea_metrics_report();
00490     greentea_send_kv(GREENTEA_TEST_ENV_END, val);
00491     greentea_send_kv(GREENTEA_TEST_ENV_EXIT, 0);
00492 }
00493 
00494 /**
00495  * \brief Send to master greentea-client version
00496  */
00497 static void greentea_notify_version() {
00498     greentea_send_kv(GREENTEA_TEST_ENV_HOST_TEST_VERSION, MBED_GREENTEA_CLIENT_VERSION_STRING);
00499 }
00500 
00501 /**
00502  *****************************************************************************
00503  *  Parse engine for KV values which replaces scanf
00504  *****************************************************************************
00505  *
00506  *  Example usage:
00507  *
00508  *  char key[10];
00509  *  char value[48];
00510  *
00511  *  greentea_parse_kv(key, value, 10, 48);
00512  *  greentea_parse_kv(key, value, 10, 48);
00513  *
00514  */
00515 
00516 
00517 static int gettok(char *, const int);
00518 static int getNextToken(char *, const int);
00519 static int HandleKV(char *,  char *,  const int,  const int);
00520 static int isstring(int);
00521 
00522 /**
00523  *  \brief Current token of key-value protocol's tokenizer
00524  */
00525 static int CurTok = 0;
00526 
00527 /**
00528  *  \enum Token enumeration for key-value protocol tokenizer
00529  *
00530  *        This enum is used by key-value protocol tokenizer
00531  *        to detect parts of protocol in stream.
00532  *
00533  *        tok_eof       ::= EOF (end of file)
00534  *        tok_open      ::= "{{"
00535  *        tok_close     ::= "}}"
00536  *        tok_semicolon ::= ";"
00537  *        tok_string    ::= [a-zA-Z0-9_-!@#$%^&*()]+    // See isstring() function
00538  *
00539  */
00540 enum Token {
00541     tok_eof = -1,
00542     tok_open = -2,
00543     tok_close = -3,
00544     tok_semicolon = -4,
00545     tok_string = -5
00546 };
00547 
00548 /**
00549  * \brief Read character from stream of data
00550  *
00551  *        Closure for default "get character" function.
00552  *        This function is used to read characters from the stream
00553  *        (default is serial port RX). Key-value protocol tokenizer
00554  *        will build stream of tokes used by key-value protocol to
00555  *        detect valid messages.
00556  *
00557  *        If EOF is received parser finishes parsing and stops. In
00558  *        situation where we have serial port stream of data parsing
00559  *        goes forever.
00560  *
00561  * \return Next character from the stream or EOF if stream has ended.
00562  *
00563  */
00564 extern "C" int greentea_getc() {
00565     return greentea_serial->getc();
00566 }
00567 
00568 /**
00569  * \brief parse input string for key-value pairs: {{key;value}}
00570  *        This function should replace scanf() used to
00571  *        check for incoming messages from master. All data
00572  *        parsed and rejected is discarded.
00573  *
00574  * \param out_key Ouput data with key
00575  * \param out_value Ouput data with value
00576  * \param out_key_size out_key total size
00577  * \param out_value_size out_value total data
00578  *
00579  * success != 0 when key-value pair was found
00580  * success == 0 when end of the stream was found
00581  *
00582  */
00583 extern "C" int greentea_parse_kv(char *out_key,
00584                       char *out_value,
00585                       const int out_key_size,
00586                       const int out_value_size) {
00587     getNextToken(0, 0);
00588     while (1) {
00589         switch (CurTok) {
00590         case tok_eof:
00591             return 0;
00592 
00593         case tok_open:
00594             if (HandleKV(out_key, out_value, out_key_size, out_value_size)) {
00595                 // We've found {{ KEY ; VALUE }} expression
00596                 return 1;
00597             }
00598             break;
00599 
00600         default:
00601             // Load next token and pray...
00602             getNextToken(0, 0);
00603             break;
00604         }
00605     }
00606 }
00607 
00608 /**
00609  *  \brief Get next token from stream
00610  *
00611  *         Key-value TOKENIZER feature
00612  *
00613  *         This function is used by key-value parser determine
00614  *         if key-value message is embedded in stream data.
00615  *
00616  *  \param str Output parameters to store token string value
00617  *  \param str_size Size of 'str' parameter in bytes (characters)
00618  *
00619  */
00620 static int getNextToken(char *str, const int str_size) {
00621     return CurTok = gettok(str, str_size);
00622 }
00623 
00624 /**
00625  *  \brief Check if character is punctuation character
00626  *
00627  *          Auxilary key-value TOKENIZER function
00628  *
00629  *          Defines if character is in subset of allowed punctuation
00630  *          characters which can be part of a key or value string.
00631  *          Not allowed characters are: ";{}"
00632  *
00633  *  \param c Input character to check
00634  *  \return Return 1 if character is allowed punctuation character, otherwise return false
00635  *
00636  */
00637 static int ispunctuation(int c) {
00638     static const char punctuation[] = "_-!@#$%^&*()=+:<>,./?\\\"'";  // No ";{}"
00639     for (size_t i=0; i< sizeof(punctuation); ++i) {
00640         if (c == punctuation[i]) {
00641             return 1;
00642         }
00643     }
00644     return 0;
00645 }
00646 
00647 /**
00648  *  \brief Check if character is string token character
00649  *
00650  *          Auxilary key-value TOKENIZER function
00651  *
00652  *          Defines if character is in subset of allowed string
00653  *          token characters.
00654  *          String defines set of characters which can be a key or value string.
00655  *
00656  *          Allowed subset includes:
00657  *          - Alphanumerical characters
00658  *          - Digits
00659  *          - White spaces and
00660  *          - subset of punctuation characters.
00661  *
00662  *  \param c Input character to check
00663  *  \return Return 1 if character is allowed punctuation character, otherwise return false
00664  *
00665  */
00666 static int isstring(int c) {
00667     return (isalpha(c) ||
00668             isdigit(c) ||
00669             isspace(c) ||
00670             ispunctuation(c));
00671 }
00672 
00673 /**
00674  *  \brief TOKENIZER of key-value protocol
00675  *
00676  *         Actual key-value TOKENIZER engine
00677  *
00678  *         TOKENIZER defines #Token enum to map recognized tokens to integer values.
00679  *
00680  *         <TOK_EOF>       ::= EOF (end of file)
00681  *         <TOK_OPEN>      ::= "{{"
00682  *         <TOK_CLOSE>     ::= "}}"
00683  *         <TOK_SEMICOLON> ::= ";"
00684  *         <TOK_STRING>    ::= [a-zA-Z0-9_-!@#$%^&*()]+    // See isstring() function *
00685  *
00686  *  \param out_str Output string with parsed token (string)
00687  *  \param str_size Size of str buffer we can use
00688  *
00689  *  \return Return #Token enum value used by parser to check for key-value occurrences
00690  *
00691  */
00692 static int gettok(char *out_str, const int str_size) {
00693     static int LastChar = '!';
00694     static int str_idx = 0;
00695 
00696     // whitespace ::=
00697     while (isspace(LastChar)) {
00698         LastChar = greentea_getc();
00699     }
00700 
00701     // string ::= [a-zA-Z0-9_-!@#$%^&*()]+
00702     if (isstring(LastChar)) {
00703         str_idx = 0;
00704         if (out_str && str_idx < str_size - 1) {
00705             out_str[str_idx++] = LastChar;
00706         }
00707 
00708         while (isstring((LastChar = greentea_getc())))
00709             if (out_str && str_idx < str_size - 1) {
00710                 out_str[str_idx++] = LastChar;
00711             }
00712         if (out_str && str_idx < str_size) {
00713             out_str[str_idx] = '\0';
00714         }
00715 
00716         return tok_string;
00717     }
00718 
00719     // semicolon ::= ';'
00720     if (LastChar == ';') {
00721         LastChar = greentea_getc();
00722         return tok_semicolon;
00723     }
00724 
00725     // open ::= '{{'
00726     if (LastChar == '{') {
00727         LastChar = greentea_getc();
00728         if (LastChar == '{') {
00729             LastChar = greentea_getc();
00730             return tok_open;
00731         }
00732     }
00733 
00734     // close ::= '}'
00735     if (LastChar == '}') {
00736         LastChar = greentea_getc();
00737         if (LastChar == '}') {
00738             LastChar = '!';
00739             return tok_close;
00740         }
00741     }
00742 
00743     if (LastChar == EOF)
00744         return tok_eof;
00745 
00746     // Otherwise, just return the character as its ascii value.
00747     int ThisChar = LastChar;
00748     LastChar = greentea_getc();
00749     return ThisChar;
00750 }
00751 
00752 /**
00753  *  \brief Key-value parser
00754  *
00755  *         Key-value message grammar
00756  *
00757  *         <MESSAGE>: <TOK_OPEN> <TOK_STRING> <TOK_SEMICOLON> <TOK_STRING> <TOK_CLOSE>
00758  *
00759  *         Examples:
00760  *         message:     "{{__timeout; 1000}}"
00761  *                      "{{__sync; 12345678-1234-5678-1234-567812345678}}"
00762  *
00763  *  \param out_key Output buffer to store key string value
00764  *  \param out_value Output buffer to store value string value
00765  *  \param out_key_size Buffer 'out_key' buffer size
00766  *  \param out_value_size Buffer 'out_value_size' buffer size
00767  *  \return Returns 1 if key-value message was parsed successfully in stream of tokens from tokenizer
00768  *
00769  */
00770 static int HandleKV(char *out_key,
00771                     char *out_value,
00772                     const int out_key_size,
00773                     const int out_value_size) {
00774     // We already started with <open>
00775     if (getNextToken(out_key, out_key_size) == tok_string) {
00776         if (getNextToken(0, 0) == tok_semicolon) {
00777             if (getNextToken(out_value, out_value_size) == tok_string) {
00778                 if (getNextToken(0, 0) == tok_close) {
00779                     // <open> <string> <semicolon> <string> <close>
00780                     // Found "{{KEY;VALUE}}" expression
00781                     return 1;
00782                 }
00783             }
00784         }
00785     }
00786     getNextToken(0, 0);
00787     return 0;
00788 }
00789 
00790 #endif