Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Dependents: TYBLE16_simple_data_logger TYBLE16_MP3_Air
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
Generated on Tue Jul 12 2022 13:54:24 by
