mbed-os
Dependents: cobaLCDJoyMotor_Thread odometry_omni_3roda_v3 odometry_omni_3roda_v1 odometry_omni_3roda_v2 ... more
features/frameworks/greentea-client/source/greentea_test_env.cpp@0:b74591d5ab33, 2017-12-11 (annotated)
- Committer:
- be_bryan
- Date:
- Mon Dec 11 17:54:04 2017 +0000
- Revision:
- 0:b74591d5ab33
motor ++
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
be_bryan | 0:b74591d5ab33 | 1 | /* |
be_bryan | 0:b74591d5ab33 | 2 | * Copyright (c) 2013-2016, ARM Limited, All Rights Reserved |
be_bryan | 0:b74591d5ab33 | 3 | * SPDX-License-Identifier: Apache-2.0 |
be_bryan | 0:b74591d5ab33 | 4 | * |
be_bryan | 0:b74591d5ab33 | 5 | * Licensed under the Apache License, Version 2.0 (the "License"); you may |
be_bryan | 0:b74591d5ab33 | 6 | * not use this file except in compliance with the License. |
be_bryan | 0:b74591d5ab33 | 7 | * You may obtain a copy of the License at |
be_bryan | 0:b74591d5ab33 | 8 | * |
be_bryan | 0:b74591d5ab33 | 9 | * http://www.apache.org/licenses/LICENSE-2.0 |
be_bryan | 0:b74591d5ab33 | 10 | * |
be_bryan | 0:b74591d5ab33 | 11 | * Unless required by applicable law or agreed to in writing, software |
be_bryan | 0:b74591d5ab33 | 12 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
be_bryan | 0:b74591d5ab33 | 13 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
be_bryan | 0:b74591d5ab33 | 14 | * See the License for the specific language governing permissions and |
be_bryan | 0:b74591d5ab33 | 15 | * limitations under the License. |
be_bryan | 0:b74591d5ab33 | 16 | */ |
be_bryan | 0:b74591d5ab33 | 17 | |
be_bryan | 0:b74591d5ab33 | 18 | #include <ctype.h> |
be_bryan | 0:b74591d5ab33 | 19 | #include <cstdio> |
be_bryan | 0:b74591d5ab33 | 20 | #include <string.h> |
be_bryan | 0:b74591d5ab33 | 21 | #include "mbed.h" |
be_bryan | 0:b74591d5ab33 | 22 | #include "greentea-client/test_env.h" |
be_bryan | 0:b74591d5ab33 | 23 | #include "greentea-client/greentea_serial.h" |
be_bryan | 0:b74591d5ab33 | 24 | #include "greentea-client/greentea_metrics.h" |
be_bryan | 0:b74591d5ab33 | 25 | |
be_bryan | 0:b74591d5ab33 | 26 | |
be_bryan | 0:b74591d5ab33 | 27 | /** |
be_bryan | 0:b74591d5ab33 | 28 | * Generic test suite transport protocol keys |
be_bryan | 0:b74591d5ab33 | 29 | */ |
be_bryan | 0:b74591d5ab33 | 30 | const char* GREENTEA_TEST_ENV_END = "end"; |
be_bryan | 0:b74591d5ab33 | 31 | const char* GREENTEA_TEST_ENV_EXIT = "__exit"; |
be_bryan | 0:b74591d5ab33 | 32 | const char* GREENTEA_TEST_ENV_SYNC = "__sync"; |
be_bryan | 0:b74591d5ab33 | 33 | const char* GREENTEA_TEST_ENV_TIMEOUT = "__timeout"; |
be_bryan | 0:b74591d5ab33 | 34 | const char* GREENTEA_TEST_ENV_HOST_TEST_NAME = "__host_test_name"; |
be_bryan | 0:b74591d5ab33 | 35 | const char* GREENTEA_TEST_ENV_HOST_TEST_VERSION = "__version"; |
be_bryan | 0:b74591d5ab33 | 36 | |
be_bryan | 0:b74591d5ab33 | 37 | /** |
be_bryan | 0:b74591d5ab33 | 38 | * Test suite success code strings |
be_bryan | 0:b74591d5ab33 | 39 | */ |
be_bryan | 0:b74591d5ab33 | 40 | const char* GREENTEA_TEST_ENV_SUCCESS = "success"; |
be_bryan | 0:b74591d5ab33 | 41 | const char* GREENTEA_TEST_ENV_FAILURE = "failure"; |
be_bryan | 0:b74591d5ab33 | 42 | |
be_bryan | 0:b74591d5ab33 | 43 | /** |
be_bryan | 0:b74591d5ab33 | 44 | * Test case transport protocol start/finish keys |
be_bryan | 0:b74591d5ab33 | 45 | */ |
be_bryan | 0:b74591d5ab33 | 46 | const char* GREENTEA_TEST_ENV_TESTCASE_NAME = "__testcase_name"; |
be_bryan | 0:b74591d5ab33 | 47 | const char* GREENTEA_TEST_ENV_TESTCASE_COUNT = "__testcase_count"; |
be_bryan | 0:b74591d5ab33 | 48 | const char* GREENTEA_TEST_ENV_TESTCASE_START = "__testcase_start"; |
be_bryan | 0:b74591d5ab33 | 49 | const char* GREENTEA_TEST_ENV_TESTCASE_FINISH = "__testcase_finish"; |
be_bryan | 0:b74591d5ab33 | 50 | const char* GREENTEA_TEST_ENV_TESTCASE_SUMMARY = "__testcase_summary"; |
be_bryan | 0:b74591d5ab33 | 51 | // Code Coverage (LCOV) transport protocol keys |
be_bryan | 0:b74591d5ab33 | 52 | const char* GREENTEA_TEST_ENV_LCOV_START = "__coverage_start"; |
be_bryan | 0:b74591d5ab33 | 53 | |
be_bryan | 0:b74591d5ab33 | 54 | /** |
be_bryan | 0:b74591d5ab33 | 55 | * Auxilary functions |
be_bryan | 0:b74591d5ab33 | 56 | */ |
be_bryan | 0:b74591d5ab33 | 57 | static void greentea_notify_timeout(const int); |
be_bryan | 0:b74591d5ab33 | 58 | static void greentea_notify_hosttest(const char *); |
be_bryan | 0:b74591d5ab33 | 59 | static void greentea_notify_completion(const int); |
be_bryan | 0:b74591d5ab33 | 60 | static void greentea_notify_version(); |
be_bryan | 0:b74591d5ab33 | 61 | static void greentea_write_string(const char *str); |
be_bryan | 0:b74591d5ab33 | 62 | |
be_bryan | 0:b74591d5ab33 | 63 | /** \brief Handle the handshake with the host |
be_bryan | 0:b74591d5ab33 | 64 | * \details This is contains the shared handhshake functionality that is used between |
be_bryan | 0:b74591d5ab33 | 65 | * GREENTEA_SETUP and GREENTEA_SETUP_UUID. |
be_bryan | 0:b74591d5ab33 | 66 | * This function is blocking. |
be_bryan | 0:b74591d5ab33 | 67 | */ |
be_bryan | 0:b74591d5ab33 | 68 | void _GREENTEA_SETUP_COMMON(const int timeout, const char *host_test_name, char *buffer, size_t size) { |
be_bryan | 0:b74591d5ab33 | 69 | greentea_metrics_setup(); |
be_bryan | 0:b74591d5ab33 | 70 | // Key-value protocol handshake function. Waits for {{__sync;...}} message |
be_bryan | 0:b74591d5ab33 | 71 | // Sync preamble: "{{__sync;0dad4a9d-59a3-4aec-810d-d5fb09d852c1}}" |
be_bryan | 0:b74591d5ab33 | 72 | // Example value of sync_uuid == "0dad4a9d-59a3-4aec-810d-d5fb09d852c1" |
be_bryan | 0:b74591d5ab33 | 73 | |
be_bryan | 0:b74591d5ab33 | 74 | char _key[8] = {0}; |
be_bryan | 0:b74591d5ab33 | 75 | |
be_bryan | 0:b74591d5ab33 | 76 | while (1) { |
be_bryan | 0:b74591d5ab33 | 77 | greentea_parse_kv(_key, buffer, sizeof(_key), size); |
be_bryan | 0:b74591d5ab33 | 78 | greentea_write_string("mbedmbedmbedmbedmbedmbedmbedmbed\r\n"); |
be_bryan | 0:b74591d5ab33 | 79 | if (strcmp(_key, GREENTEA_TEST_ENV_SYNC) == 0) { |
be_bryan | 0:b74591d5ab33 | 80 | // Found correct __sync message |
be_bryan | 0:b74591d5ab33 | 81 | greentea_send_kv(_key, buffer); |
be_bryan | 0:b74591d5ab33 | 82 | break; |
be_bryan | 0:b74591d5ab33 | 83 | } |
be_bryan | 0:b74591d5ab33 | 84 | } |
be_bryan | 0:b74591d5ab33 | 85 | |
be_bryan | 0:b74591d5ab33 | 86 | greentea_notify_version(); |
be_bryan | 0:b74591d5ab33 | 87 | greentea_notify_timeout(timeout); |
be_bryan | 0:b74591d5ab33 | 88 | greentea_notify_hosttest(host_test_name); |
be_bryan | 0:b74591d5ab33 | 89 | } |
be_bryan | 0:b74591d5ab33 | 90 | |
be_bryan | 0:b74591d5ab33 | 91 | /** \brief Handshake with host and send setup data (timeout and host test name) |
be_bryan | 0:b74591d5ab33 | 92 | * \details This function will send preamble to master. |
be_bryan | 0:b74591d5ab33 | 93 | * After host test name is received master will invoke host test script |
be_bryan | 0:b74591d5ab33 | 94 | * and add host test's callback handlers to main event loop |
be_bryan | 0:b74591d5ab33 | 95 | * This function is blocking. |
be_bryan | 0:b74591d5ab33 | 96 | */ |
be_bryan | 0:b74591d5ab33 | 97 | extern "C" void GREENTEA_SETUP(const int timeout, const char *host_test_name) { |
be_bryan | 0:b74591d5ab33 | 98 | char _value[GREENTEA_UUID_LENGTH] = {0}; |
be_bryan | 0:b74591d5ab33 | 99 | _GREENTEA_SETUP_COMMON(timeout, host_test_name, _value, GREENTEA_UUID_LENGTH); |
be_bryan | 0:b74591d5ab33 | 100 | } |
be_bryan | 0:b74591d5ab33 | 101 | |
be_bryan | 0:b74591d5ab33 | 102 | /** \brief Handshake with host and send setup data (timeout and host test name). Allows you to preserve sync UUID. |
be_bryan | 0:b74591d5ab33 | 103 | * \details This function will send preamble to master. |
be_bryan | 0:b74591d5ab33 | 104 | * After host test name is received master will invoke host test script |
be_bryan | 0:b74591d5ab33 | 105 | * and add host test's callback handlers to main event loop |
be_bryan | 0:b74591d5ab33 | 106 | * This function is blocking. |
be_bryan | 0:b74591d5ab33 | 107 | * This function differs from GREENTEA_SETUP because it allows you to |
be_bryan | 0:b74591d5ab33 | 108 | * preserve the UUID sent during the sync process. |
be_bryan | 0:b74591d5ab33 | 109 | */ |
be_bryan | 0:b74591d5ab33 | 110 | void GREENTEA_SETUP_UUID(const int timeout, const char *host_test_name, char *buffer, size_t size) { |
be_bryan | 0:b74591d5ab33 | 111 | _GREENTEA_SETUP_COMMON(timeout, host_test_name, buffer, size); |
be_bryan | 0:b74591d5ab33 | 112 | } |
be_bryan | 0:b74591d5ab33 | 113 | |
be_bryan | 0:b74591d5ab33 | 114 | /** \brief Notify host (__exit message) side that test suite execution was complete |
be_bryan | 0:b74591d5ab33 | 115 | * \result Test suite result |
be_bryan | 0:b74591d5ab33 | 116 | * \details If __exit is not received by host side we will assume TIMEOUT |
be_bryan | 0:b74591d5ab33 | 117 | */ |
be_bryan | 0:b74591d5ab33 | 118 | void GREENTEA_TESTSUITE_RESULT(const int result) { |
be_bryan | 0:b74591d5ab33 | 119 | greentea_notify_completion(result); |
be_bryan | 0:b74591d5ab33 | 120 | } |
be_bryan | 0:b74591d5ab33 | 121 | |
be_bryan | 0:b74591d5ab33 | 122 | /** |
be_bryan | 0:b74591d5ab33 | 123 | * Test Case support |
be_bryan | 0:b74591d5ab33 | 124 | */ |
be_bryan | 0:b74591d5ab33 | 125 | |
be_bryan | 0:b74591d5ab33 | 126 | /** \brief Notify host side that test case started |
be_bryan | 0:b74591d5ab33 | 127 | * \details test_case_name Test case name |
be_bryan | 0:b74591d5ab33 | 128 | */ |
be_bryan | 0:b74591d5ab33 | 129 | void GREENTEA_TESTCASE_START(const char *test_case_name) { |
be_bryan | 0:b74591d5ab33 | 130 | greentea_send_kv(GREENTEA_TEST_ENV_TESTCASE_START, test_case_name); |
be_bryan | 0:b74591d5ab33 | 131 | } |
be_bryan | 0:b74591d5ab33 | 132 | |
be_bryan | 0:b74591d5ab33 | 133 | /** \brief Notify host side that test case finished |
be_bryan | 0:b74591d5ab33 | 134 | * \details test_case_name Test case name |
be_bryan | 0:b74591d5ab33 | 135 | * \details result Test case result (0 -OK, non zero...) |
be_bryan | 0:b74591d5ab33 | 136 | */ |
be_bryan | 0:b74591d5ab33 | 137 | void GREENTEA_TESTCASE_FINISH(const char *test_case_name, const size_t passes, const size_t failed) { |
be_bryan | 0:b74591d5ab33 | 138 | greentea_send_kv(GREENTEA_TEST_ENV_TESTCASE_FINISH, test_case_name, passes, failed); |
be_bryan | 0:b74591d5ab33 | 139 | } |
be_bryan | 0:b74591d5ab33 | 140 | |
be_bryan | 0:b74591d5ab33 | 141 | /** |
be_bryan | 0:b74591d5ab33 | 142 | ***************************************************************************** |
be_bryan | 0:b74591d5ab33 | 143 | * Auxilary functions and key-value protocol support |
be_bryan | 0:b74591d5ab33 | 144 | ***************************************************************************** |
be_bryan | 0:b74591d5ab33 | 145 | */ |
be_bryan | 0:b74591d5ab33 | 146 | |
be_bryan | 0:b74591d5ab33 | 147 | |
be_bryan | 0:b74591d5ab33 | 148 | /** |
be_bryan | 0:b74591d5ab33 | 149 | ***************************************************************************** |
be_bryan | 0:b74591d5ab33 | 150 | * LCOV support |
be_bryan | 0:b74591d5ab33 | 151 | ***************************************************************************** |
be_bryan | 0:b74591d5ab33 | 152 | */ |
be_bryan | 0:b74591d5ab33 | 153 | #ifdef MBED_CFG_DEBUG_OPTIONS_COVERAGE |
be_bryan | 0:b74591d5ab33 | 154 | extern "C" void __gcov_flush(void); |
be_bryan | 0:b74591d5ab33 | 155 | extern bool coverage_report; |
be_bryan | 0:b74591d5ab33 | 156 | |
be_bryan | 0:b74591d5ab33 | 157 | /** |
be_bryan | 0:b74591d5ab33 | 158 | * \brief Send code coverage (gcov/LCOV) notification to master |
be_bryan | 0:b74591d5ab33 | 159 | * |
be_bryan | 0:b74591d5ab33 | 160 | * Generates preamble of message sent to notify host about code coverage data dump. |
be_bryan | 0:b74591d5ab33 | 161 | * |
be_bryan | 0:b74591d5ab33 | 162 | * This function is used by mbedOS software |
be_bryan | 0:b74591d5ab33 | 163 | * (see: mbed-drivers/source/retarget.cpp file) to generate code coverage |
be_bryan | 0:b74591d5ab33 | 164 | * messages to host. When code coverage feature is turned on slave will |
be_bryan | 0:b74591d5ab33 | 165 | * print-out code coverage data in form of key-value protocol. |
be_bryan | 0:b74591d5ab33 | 166 | * Message with code coverage data will contain message name, path to code |
be_bryan | 0:b74591d5ab33 | 167 | * coverage output file host will touch and fill with code coverage binary |
be_bryan | 0:b74591d5ab33 | 168 | * payload. Coverage payload is encoded as stream of ASCII coded bytes ("%02X"). |
be_bryan | 0:b74591d5ab33 | 169 | * |
be_bryan | 0:b74591d5ab33 | 170 | * \param path to file with code coverage payload (set by gcov instrumentation) |
be_bryan | 0:b74591d5ab33 | 171 | * |
be_bryan | 0:b74591d5ab33 | 172 | */ |
be_bryan | 0:b74591d5ab33 | 173 | void greentea_notify_coverage_start(const char *path) { |
be_bryan | 0:b74591d5ab33 | 174 | printf("{{%s;%s;", GREENTEA_TEST_ENV_LCOV_START, path); |
be_bryan | 0:b74591d5ab33 | 175 | } |
be_bryan | 0:b74591d5ab33 | 176 | |
be_bryan | 0:b74591d5ab33 | 177 | /** |
be_bryan | 0:b74591d5ab33 | 178 | * \brief Sufix for code coverage message to master (closing statement) |
be_bryan | 0:b74591d5ab33 | 179 | * |
be_bryan | 0:b74591d5ab33 | 180 | * This function is used by mbedOS software |
be_bryan | 0:b74591d5ab33 | 181 | * (see: mbed-drivers/source/retarget.cpp file) to generate code coverage |
be_bryan | 0:b74591d5ab33 | 182 | * messages to host. When code coverage feature is turned on slave will |
be_bryan | 0:b74591d5ab33 | 183 | * print-out code coverage data in form of key-value protocol. |
be_bryan | 0:b74591d5ab33 | 184 | * Message with code coverage data will contain message name, path to code |
be_bryan | 0:b74591d5ab33 | 185 | * coverage output file host will touch and fill with code coverage binary |
be_bryan | 0:b74591d5ab33 | 186 | * payload. Coverage payload is encoded as stream of ASCII coded bytes ("%02X"). |
be_bryan | 0:b74591d5ab33 | 187 | * |
be_bryan | 0:b74591d5ab33 | 188 | * Companion function greentea_notify_coverage_start() defines code coverage message structure |
be_bryan | 0:b74591d5ab33 | 189 | * |
be_bryan | 0:b74591d5ab33 | 190 | */ |
be_bryan | 0:b74591d5ab33 | 191 | void greentea_notify_coverage_end() { |
be_bryan | 0:b74591d5ab33 | 192 | printf("}}" NL); |
be_bryan | 0:b74591d5ab33 | 193 | } |
be_bryan | 0:b74591d5ab33 | 194 | |
be_bryan | 0:b74591d5ab33 | 195 | #endif |
be_bryan | 0:b74591d5ab33 | 196 | |
be_bryan | 0:b74591d5ab33 | 197 | /** |
be_bryan | 0:b74591d5ab33 | 198 | ***************************************************************************** |
be_bryan | 0:b74591d5ab33 | 199 | * Key-value protocol support |
be_bryan | 0:b74591d5ab33 | 200 | ***************************************************************************** |
be_bryan | 0:b74591d5ab33 | 201 | */ |
be_bryan | 0:b74591d5ab33 | 202 | |
be_bryan | 0:b74591d5ab33 | 203 | /** |
be_bryan | 0:b74591d5ab33 | 204 | * \brief Write the preamble characters to the serial port |
be_bryan | 0:b74591d5ab33 | 205 | * |
be_bryan | 0:b74591d5ab33 | 206 | * This function writes the preamble "{{" which is required |
be_bryan | 0:b74591d5ab33 | 207 | * for key-value comunication between the target and the host. |
be_bryan | 0:b74591d5ab33 | 208 | * This uses a Rawserial object, greentea_serial, which provides |
be_bryan | 0:b74591d5ab33 | 209 | * a direct interface to the USBTX and USBRX serial pins and allows |
be_bryan | 0:b74591d5ab33 | 210 | * the direct writing of characters using the putc() method. |
be_bryan | 0:b74591d5ab33 | 211 | * This suite of functions are provided to allow for serial communication |
be_bryan | 0:b74591d5ab33 | 212 | * to the host from within a thread/ISR. |
be_bryan | 0:b74591d5ab33 | 213 | * |
be_bryan | 0:b74591d5ab33 | 214 | */ |
be_bryan | 0:b74591d5ab33 | 215 | inline void greentea_write_preamble() |
be_bryan | 0:b74591d5ab33 | 216 | { |
be_bryan | 0:b74591d5ab33 | 217 | greentea_serial->putc('{'); |
be_bryan | 0:b74591d5ab33 | 218 | greentea_serial->putc('{'); |
be_bryan | 0:b74591d5ab33 | 219 | } |
be_bryan | 0:b74591d5ab33 | 220 | |
be_bryan | 0:b74591d5ab33 | 221 | /** |
be_bryan | 0:b74591d5ab33 | 222 | * \brief Write the postamble characters to the serial port |
be_bryan | 0:b74591d5ab33 | 223 | * |
be_bryan | 0:b74591d5ab33 | 224 | * This function writes the postamble "{{\n" which is required |
be_bryan | 0:b74591d5ab33 | 225 | * for key-value comunication between the target and the host. |
be_bryan | 0:b74591d5ab33 | 226 | * This uses a Rawserial object, greentea_serial, which provides |
be_bryan | 0:b74591d5ab33 | 227 | * a direct interface to the USBTX and USBRX serial pins and allows |
be_bryan | 0:b74591d5ab33 | 228 | * the direct writing of characters using the putc() method. |
be_bryan | 0:b74591d5ab33 | 229 | * This suite of functions are provided to allow for serial communication |
be_bryan | 0:b74591d5ab33 | 230 | * to the host from within a thread/ISR. |
be_bryan | 0:b74591d5ab33 | 231 | * |
be_bryan | 0:b74591d5ab33 | 232 | */ |
be_bryan | 0:b74591d5ab33 | 233 | inline void greentea_write_postamble() |
be_bryan | 0:b74591d5ab33 | 234 | { |
be_bryan | 0:b74591d5ab33 | 235 | greentea_serial->putc('}'); |
be_bryan | 0:b74591d5ab33 | 236 | greentea_serial->putc('}'); |
be_bryan | 0:b74591d5ab33 | 237 | greentea_serial->putc('\r'); |
be_bryan | 0:b74591d5ab33 | 238 | greentea_serial->putc('\n'); |
be_bryan | 0:b74591d5ab33 | 239 | } |
be_bryan | 0:b74591d5ab33 | 240 | |
be_bryan | 0:b74591d5ab33 | 241 | /** |
be_bryan | 0:b74591d5ab33 | 242 | * \brief Write a string to the serial port |
be_bryan | 0:b74591d5ab33 | 243 | * |
be_bryan | 0:b74591d5ab33 | 244 | * This function writes a '\0' terminated string from the target |
be_bryan | 0:b74591d5ab33 | 245 | * to the host. It writes directly to the serial port using the |
be_bryan | 0:b74591d5ab33 | 246 | * greentea_serial, Rawserial object. |
be_bryan | 0:b74591d5ab33 | 247 | * |
be_bryan | 0:b74591d5ab33 | 248 | * \param str - string value |
be_bryan | 0:b74591d5ab33 | 249 | * |
be_bryan | 0:b74591d5ab33 | 250 | */ |
be_bryan | 0:b74591d5ab33 | 251 | inline void greentea_write_string(const char *str) |
be_bryan | 0:b74591d5ab33 | 252 | { |
be_bryan | 0:b74591d5ab33 | 253 | while (*str != '\0') { |
be_bryan | 0:b74591d5ab33 | 254 | greentea_serial->putc(*str); |
be_bryan | 0:b74591d5ab33 | 255 | str ++; |
be_bryan | 0:b74591d5ab33 | 256 | } |
be_bryan | 0:b74591d5ab33 | 257 | } |
be_bryan | 0:b74591d5ab33 | 258 | |
be_bryan | 0:b74591d5ab33 | 259 | |
be_bryan | 0:b74591d5ab33 | 260 | /** |
be_bryan | 0:b74591d5ab33 | 261 | * \brief Write an int to the serial port |
be_bryan | 0:b74591d5ab33 | 262 | * |
be_bryan | 0:b74591d5ab33 | 263 | * This function writes an integer value from the target |
be_bryan | 0:b74591d5ab33 | 264 | * to the host. The integer value is converted to a string and |
be_bryan | 0:b74591d5ab33 | 265 | * and then written character by character directly to the serial |
be_bryan | 0:b74591d5ab33 | 266 | * port using the greentea_serial, Rawserial object. |
be_bryan | 0:b74591d5ab33 | 267 | * sprintf() is used to convert the int to a string. Sprintf if |
be_bryan | 0:b74591d5ab33 | 268 | * inherently thread safe so can be used. |
be_bryan | 0:b74591d5ab33 | 269 | * |
be_bryan | 0:b74591d5ab33 | 270 | * \param val - integer value |
be_bryan | 0:b74591d5ab33 | 271 | * |
be_bryan | 0:b74591d5ab33 | 272 | */ |
be_bryan | 0:b74591d5ab33 | 273 | #define MAX_INT_STRING_LEN 15 |
be_bryan | 0:b74591d5ab33 | 274 | inline void greentea_write_int(const int val) |
be_bryan | 0:b74591d5ab33 | 275 | { |
be_bryan | 0:b74591d5ab33 | 276 | char intval[MAX_INT_STRING_LEN]; |
be_bryan | 0:b74591d5ab33 | 277 | unsigned int i = 0; |
be_bryan | 0:b74591d5ab33 | 278 | sprintf(intval, "%d", val); |
be_bryan | 0:b74591d5ab33 | 279 | while (intval[i] != '\0') { |
be_bryan | 0:b74591d5ab33 | 280 | greentea_serial->putc(intval[i]); |
be_bryan | 0:b74591d5ab33 | 281 | i++; |
be_bryan | 0:b74591d5ab33 | 282 | } |
be_bryan | 0:b74591d5ab33 | 283 | } |
be_bryan | 0:b74591d5ab33 | 284 | |
be_bryan | 0:b74591d5ab33 | 285 | /** |
be_bryan | 0:b74591d5ab33 | 286 | * \brief Encapsulate and send key-value message from DUT to host |
be_bryan | 0:b74591d5ab33 | 287 | * |
be_bryan | 0:b74591d5ab33 | 288 | * This function uses underlying functions to write directly |
be_bryan | 0:b74591d5ab33 | 289 | * to the serial port, (USBTX). This allows KVs to be used |
be_bryan | 0:b74591d5ab33 | 290 | * from within interrupt context. |
be_bryan | 0:b74591d5ab33 | 291 | * |
be_bryan | 0:b74591d5ab33 | 292 | * \param key Message key (message/event name) |
be_bryan | 0:b74591d5ab33 | 293 | * \param value Message payload, string value |
be_bryan | 0:b74591d5ab33 | 294 | * |
be_bryan | 0:b74591d5ab33 | 295 | */ |
be_bryan | 0:b74591d5ab33 | 296 | extern "C" void greentea_send_kv(const char *key, const char *val) { |
be_bryan | 0:b74591d5ab33 | 297 | if (key && val) { |
be_bryan | 0:b74591d5ab33 | 298 | greentea_write_preamble(); |
be_bryan | 0:b74591d5ab33 | 299 | greentea_write_string(key); |
be_bryan | 0:b74591d5ab33 | 300 | greentea_serial->putc(';'); |
be_bryan | 0:b74591d5ab33 | 301 | greentea_write_string(val); |
be_bryan | 0:b74591d5ab33 | 302 | greentea_write_postamble(); |
be_bryan | 0:b74591d5ab33 | 303 | } |
be_bryan | 0:b74591d5ab33 | 304 | } |
be_bryan | 0:b74591d5ab33 | 305 | |
be_bryan | 0:b74591d5ab33 | 306 | /** |
be_bryan | 0:b74591d5ab33 | 307 | * \brief Encapsulate and send key-value message from DUT to host |
be_bryan | 0:b74591d5ab33 | 308 | * |
be_bryan | 0:b74591d5ab33 | 309 | * This function uses underlying functions to write directly |
be_bryan | 0:b74591d5ab33 | 310 | * to the serial port, (USBTX). This allows KVs to be used |
be_bryan | 0:b74591d5ab33 | 311 | * from within interrupt context. |
be_bryan | 0:b74591d5ab33 | 312 | * Last value is an integer to avoid integer to string conversion |
be_bryan | 0:b74591d5ab33 | 313 | * made by the user. |
be_bryan | 0:b74591d5ab33 | 314 | * |
be_bryan | 0:b74591d5ab33 | 315 | * \param key Message key (message/event name) |
be_bryan | 0:b74591d5ab33 | 316 | * \param value Message payload, integer value |
be_bryan | 0:b74591d5ab33 | 317 | * |
be_bryan | 0:b74591d5ab33 | 318 | */ |
be_bryan | 0:b74591d5ab33 | 319 | void greentea_send_kv(const char *key, const int val) { |
be_bryan | 0:b74591d5ab33 | 320 | if (key) { |
be_bryan | 0:b74591d5ab33 | 321 | greentea_write_preamble(); |
be_bryan | 0:b74591d5ab33 | 322 | greentea_write_string(key); |
be_bryan | 0:b74591d5ab33 | 323 | greentea_serial->putc(';'); |
be_bryan | 0:b74591d5ab33 | 324 | greentea_write_int(val); |
be_bryan | 0:b74591d5ab33 | 325 | greentea_write_postamble(); |
be_bryan | 0:b74591d5ab33 | 326 | } |
be_bryan | 0:b74591d5ab33 | 327 | } |
be_bryan | 0:b74591d5ab33 | 328 | |
be_bryan | 0:b74591d5ab33 | 329 | /** |
be_bryan | 0:b74591d5ab33 | 330 | * \brief Encapsulate and send key-value-value message from DUT to host |
be_bryan | 0:b74591d5ab33 | 331 | * |
be_bryan | 0:b74591d5ab33 | 332 | * This function uses underlying functions to write directly |
be_bryan | 0:b74591d5ab33 | 333 | * to the serial port, (USBTX). This allows KVs to be used |
be_bryan | 0:b74591d5ab33 | 334 | * from within interrupt context. |
be_bryan | 0:b74591d5ab33 | 335 | * Last value is an integer to avoid integer to string conversion |
be_bryan | 0:b74591d5ab33 | 336 | * made by the user. |
be_bryan | 0:b74591d5ab33 | 337 | * |
be_bryan | 0:b74591d5ab33 | 338 | * \param key Message key (message/event name) |
be_bryan | 0:b74591d5ab33 | 339 | * \param value Message payload, string value |
be_bryan | 0:b74591d5ab33 | 340 | * \param result Send additional integer formatted data |
be_bryan | 0:b74591d5ab33 | 341 | * |
be_bryan | 0:b74591d5ab33 | 342 | */ |
be_bryan | 0:b74591d5ab33 | 343 | void greentea_send_kv(const char *key, const char *val, const int result) { |
be_bryan | 0:b74591d5ab33 | 344 | if (key) { |
be_bryan | 0:b74591d5ab33 | 345 | greentea_write_preamble(); |
be_bryan | 0:b74591d5ab33 | 346 | greentea_write_string(key); |
be_bryan | 0:b74591d5ab33 | 347 | greentea_serial->putc(';'); |
be_bryan | 0:b74591d5ab33 | 348 | greentea_write_string(val); |
be_bryan | 0:b74591d5ab33 | 349 | greentea_serial->putc(';'); |
be_bryan | 0:b74591d5ab33 | 350 | greentea_write_int(result); |
be_bryan | 0:b74591d5ab33 | 351 | greentea_write_postamble(); |
be_bryan | 0:b74591d5ab33 | 352 | |
be_bryan | 0:b74591d5ab33 | 353 | } |
be_bryan | 0:b74591d5ab33 | 354 | } |
be_bryan | 0:b74591d5ab33 | 355 | |
be_bryan | 0:b74591d5ab33 | 356 | /** |
be_bryan | 0:b74591d5ab33 | 357 | * \brief Encapsulate and send key-value-value-value message from DUT to host |
be_bryan | 0:b74591d5ab33 | 358 | * |
be_bryan | 0:b74591d5ab33 | 359 | * This function uses underlying functions to write directly |
be_bryan | 0:b74591d5ab33 | 360 | * to the serial port, (USBTX). This allows KVs to be used |
be_bryan | 0:b74591d5ab33 | 361 | * from within interrupt context. |
be_bryan | 0:b74591d5ab33 | 362 | * Last 2 values are integers to avoid integer to string conversion |
be_bryan | 0:b74591d5ab33 | 363 | * made by the user. |
be_bryan | 0:b74591d5ab33 | 364 | * |
be_bryan | 0:b74591d5ab33 | 365 | * Names of the parameters: this function is used to send test case |
be_bryan | 0:b74591d5ab33 | 366 | * name with number of passes and failures to host. But it can be used |
be_bryan | 0:b74591d5ab33 | 367 | * to send any key-value-value-value (string-string-integer-integer) |
be_bryan | 0:b74591d5ab33 | 368 | * set to host. |
be_bryan | 0:b74591d5ab33 | 369 | * |
be_bryan | 0:b74591d5ab33 | 370 | * \param key Message key (message/event name) |
be_bryan | 0:b74591d5ab33 | 371 | * \param value Message payload, string value |
be_bryan | 0:b74591d5ab33 | 372 | * \param passes Send additional integer formatted data |
be_bryan | 0:b74591d5ab33 | 373 | * \param failures Send additional integer formatted data |
be_bryan | 0:b74591d5ab33 | 374 | * |
be_bryan | 0:b74591d5ab33 | 375 | */ |
be_bryan | 0:b74591d5ab33 | 376 | void greentea_send_kv(const char *key, const char *val, const int passes, const int failures) { |
be_bryan | 0:b74591d5ab33 | 377 | if (key) { |
be_bryan | 0:b74591d5ab33 | 378 | greentea_write_preamble(); |
be_bryan | 0:b74591d5ab33 | 379 | greentea_write_string(key); |
be_bryan | 0:b74591d5ab33 | 380 | greentea_serial->putc(';'); |
be_bryan | 0:b74591d5ab33 | 381 | greentea_write_string(val); |
be_bryan | 0:b74591d5ab33 | 382 | greentea_serial->putc(';'); |
be_bryan | 0:b74591d5ab33 | 383 | greentea_write_int(passes); |
be_bryan | 0:b74591d5ab33 | 384 | greentea_serial->putc(';'); |
be_bryan | 0:b74591d5ab33 | 385 | greentea_write_int(failures); |
be_bryan | 0:b74591d5ab33 | 386 | greentea_write_postamble(); |
be_bryan | 0:b74591d5ab33 | 387 | } |
be_bryan | 0:b74591d5ab33 | 388 | } |
be_bryan | 0:b74591d5ab33 | 389 | |
be_bryan | 0:b74591d5ab33 | 390 | /** |
be_bryan | 0:b74591d5ab33 | 391 | * \brief Encapsulate and send key-value-value message from DUT to host |
be_bryan | 0:b74591d5ab33 | 392 | * |
be_bryan | 0:b74591d5ab33 | 393 | * This function uses underlying functions to write directly |
be_bryan | 0:b74591d5ab33 | 394 | * to the serial port, (USBTX). This allows key-value-value to be used |
be_bryan | 0:b74591d5ab33 | 395 | * from within interrupt context. |
be_bryan | 0:b74591d5ab33 | 396 | * Both values are integers to avoid integer to string conversion |
be_bryan | 0:b74591d5ab33 | 397 | * made by the user. |
be_bryan | 0:b74591d5ab33 | 398 | * |
be_bryan | 0:b74591d5ab33 | 399 | * Names of the parameters: this function is used to send number |
be_bryan | 0:b74591d5ab33 | 400 | * of passes and failures to host. But it can be used to send any |
be_bryan | 0:b74591d5ab33 | 401 | * key-value-value (string-integer-integer) message to host. |
be_bryan | 0:b74591d5ab33 | 402 | * |
be_bryan | 0:b74591d5ab33 | 403 | * \param key Message key (message/event name) |
be_bryan | 0:b74591d5ab33 | 404 | * \param value Message payload, integer value |
be_bryan | 0:b74591d5ab33 | 405 | * \param passes Send additional integer formatted data |
be_bryan | 0:b74591d5ab33 | 406 | * \param failures Send additional integer formatted data |
be_bryan | 0:b74591d5ab33 | 407 | * |
be_bryan | 0:b74591d5ab33 | 408 | */ |
be_bryan | 0:b74591d5ab33 | 409 | void greentea_send_kv(const char *key, const int passes, const int failures) { |
be_bryan | 0:b74591d5ab33 | 410 | if (key) { |
be_bryan | 0:b74591d5ab33 | 411 | greentea_write_preamble(); |
be_bryan | 0:b74591d5ab33 | 412 | greentea_write_string(key); |
be_bryan | 0:b74591d5ab33 | 413 | greentea_serial->putc(';'); |
be_bryan | 0:b74591d5ab33 | 414 | greentea_write_int(passes); |
be_bryan | 0:b74591d5ab33 | 415 | greentea_serial->putc(';'); |
be_bryan | 0:b74591d5ab33 | 416 | greentea_write_int(failures); |
be_bryan | 0:b74591d5ab33 | 417 | greentea_write_postamble(); |
be_bryan | 0:b74591d5ab33 | 418 | } |
be_bryan | 0:b74591d5ab33 | 419 | } |
be_bryan | 0:b74591d5ab33 | 420 | |
be_bryan | 0:b74591d5ab33 | 421 | /** |
be_bryan | 0:b74591d5ab33 | 422 | * \brief Send message with timeout to master in seconds |
be_bryan | 0:b74591d5ab33 | 423 | * |
be_bryan | 0:b74591d5ab33 | 424 | * GREENTEA_TEST_ENV_TIMEOUT message is part of preamble |
be_bryan | 0:b74591d5ab33 | 425 | * sent from DUT to host during synchronisation (beginning of test |
be_bryan | 0:b74591d5ab33 | 426 | * suite execution). |
be_bryan | 0:b74591d5ab33 | 427 | * |
be_bryan | 0:b74591d5ab33 | 428 | * Notification about total test suite timeout. Timeout is measured |
be_bryan | 0:b74591d5ab33 | 429 | * from the moment of GREENTEA_TEST_ENV_TIMEOUT reception by host. |
be_bryan | 0:b74591d5ab33 | 430 | * If timeout is reached host (and host test) will be stopped and |
be_bryan | 0:b74591d5ab33 | 431 | * control will return to Greentea. |
be_bryan | 0:b74591d5ab33 | 432 | * |
be_bryan | 0:b74591d5ab33 | 433 | * \param timeout Test suite timeout in seconds |
be_bryan | 0:b74591d5ab33 | 434 | * |
be_bryan | 0:b74591d5ab33 | 435 | */ |
be_bryan | 0:b74591d5ab33 | 436 | static void greentea_notify_timeout(const int timeout) { |
be_bryan | 0:b74591d5ab33 | 437 | greentea_send_kv(GREENTEA_TEST_ENV_TIMEOUT, timeout); |
be_bryan | 0:b74591d5ab33 | 438 | } |
be_bryan | 0:b74591d5ab33 | 439 | |
be_bryan | 0:b74591d5ab33 | 440 | /** |
be_bryan | 0:b74591d5ab33 | 441 | * \brief Send host test name to master |
be_bryan | 0:b74591d5ab33 | 442 | * |
be_bryan | 0:b74591d5ab33 | 443 | * GREENTEA_TEST_ENV_HOST_TEST_NAME message is part of preamble |
be_bryan | 0:b74591d5ab33 | 444 | * sent from DUT to host during synchronisation (beginning of test |
be_bryan | 0:b74591d5ab33 | 445 | * suite execution). |
be_bryan | 0:b74591d5ab33 | 446 | * |
be_bryan | 0:b74591d5ab33 | 447 | * Host test Python script implements host side callbacks |
be_bryan | 0:b74591d5ab33 | 448 | * for key-value events sent from DUT to host. Host test's |
be_bryan | 0:b74591d5ab33 | 449 | * callbacks are registered after GREENTEA_TEST_ENV_HOST_TEST_NAME |
be_bryan | 0:b74591d5ab33 | 450 | * message reaches host. |
be_bryan | 0:b74591d5ab33 | 451 | * |
be_bryan | 0:b74591d5ab33 | 452 | * \param host_test_name Host test name, host test will be loaded by mbedhtrun |
be_bryan | 0:b74591d5ab33 | 453 | */ |
be_bryan | 0:b74591d5ab33 | 454 | static void greentea_notify_hosttest(const char *host_test_name) { |
be_bryan | 0:b74591d5ab33 | 455 | greentea_send_kv(GREENTEA_TEST_ENV_HOST_TEST_NAME, host_test_name); |
be_bryan | 0:b74591d5ab33 | 456 | } |
be_bryan | 0:b74591d5ab33 | 457 | |
be_bryan | 0:b74591d5ab33 | 458 | /** |
be_bryan | 0:b74591d5ab33 | 459 | * \brief Send to master information that test suite finished its execution |
be_bryan | 0:b74591d5ab33 | 460 | * |
be_bryan | 0:b74591d5ab33 | 461 | * GREENTEA_TEST_ENV_END and GREENTEA_TEST_ENV_EXIT messages |
be_bryan | 0:b74591d5ab33 | 462 | * are sent just before test suite execution finishes (noting |
be_bryan | 0:b74591d5ab33 | 463 | * else to do). You can place it just before you return from your |
be_bryan | 0:b74591d5ab33 | 464 | * main() function. |
be_bryan | 0:b74591d5ab33 | 465 | * |
be_bryan | 0:b74591d5ab33 | 466 | * Code coverage: If MEBD_CFG_DEBUG_OPTIONS_COVERAGE is set in the |
be_bryan | 0:b74591d5ab33 | 467 | * project via build configuration function will output series |
be_bryan | 0:b74591d5ab33 | 468 | * of code coverage messages GREENTEA_TEST_ENV_LCOV_START with code |
be_bryan | 0:b74591d5ab33 | 469 | * coverage binary data. This data is captured by Greentea and can |
be_bryan | 0:b74591d5ab33 | 470 | * be used to generate LCOV reports. |
be_bryan | 0:b74591d5ab33 | 471 | * |
be_bryan | 0:b74591d5ab33 | 472 | * \param result Test suite result from DUT (0 - FAIl, !0 - SUCCESS) |
be_bryan | 0:b74591d5ab33 | 473 | * |
be_bryan | 0:b74591d5ab33 | 474 | */ |
be_bryan | 0:b74591d5ab33 | 475 | static void greentea_notify_completion(const int result) { |
be_bryan | 0:b74591d5ab33 | 476 | const char *val = result ? GREENTEA_TEST_ENV_SUCCESS : GREENTEA_TEST_ENV_FAILURE; |
be_bryan | 0:b74591d5ab33 | 477 | #ifdef MBED_CFG_DEBUG_OPTIONS_COVERAGE |
be_bryan | 0:b74591d5ab33 | 478 | coverage_report = true; |
be_bryan | 0:b74591d5ab33 | 479 | __gcov_flush(); |
be_bryan | 0:b74591d5ab33 | 480 | coverage_report = false; |
be_bryan | 0:b74591d5ab33 | 481 | #endif |
be_bryan | 0:b74591d5ab33 | 482 | greentea_metrics_report(); |
be_bryan | 0:b74591d5ab33 | 483 | greentea_send_kv(GREENTEA_TEST_ENV_END, val); |
be_bryan | 0:b74591d5ab33 | 484 | greentea_send_kv(GREENTEA_TEST_ENV_EXIT, 0); |
be_bryan | 0:b74591d5ab33 | 485 | } |
be_bryan | 0:b74591d5ab33 | 486 | |
be_bryan | 0:b74591d5ab33 | 487 | /** |
be_bryan | 0:b74591d5ab33 | 488 | * \brief Send to master greentea-client version |
be_bryan | 0:b74591d5ab33 | 489 | */ |
be_bryan | 0:b74591d5ab33 | 490 | static void greentea_notify_version() { |
be_bryan | 0:b74591d5ab33 | 491 | greentea_send_kv(GREENTEA_TEST_ENV_HOST_TEST_VERSION, MBED_GREENTEA_CLIENT_VERSION_STRING); |
be_bryan | 0:b74591d5ab33 | 492 | } |
be_bryan | 0:b74591d5ab33 | 493 | |
be_bryan | 0:b74591d5ab33 | 494 | /** |
be_bryan | 0:b74591d5ab33 | 495 | ***************************************************************************** |
be_bryan | 0:b74591d5ab33 | 496 | * Parse engine for KV values which replaces scanf |
be_bryan | 0:b74591d5ab33 | 497 | ***************************************************************************** |
be_bryan | 0:b74591d5ab33 | 498 | * |
be_bryan | 0:b74591d5ab33 | 499 | * Example usage: |
be_bryan | 0:b74591d5ab33 | 500 | * |
be_bryan | 0:b74591d5ab33 | 501 | * char key[10]; |
be_bryan | 0:b74591d5ab33 | 502 | * char value[48]; |
be_bryan | 0:b74591d5ab33 | 503 | * |
be_bryan | 0:b74591d5ab33 | 504 | * greentea_parse_kv(key, value, 10, 48); |
be_bryan | 0:b74591d5ab33 | 505 | * greentea_parse_kv(key, value, 10, 48); |
be_bryan | 0:b74591d5ab33 | 506 | * |
be_bryan | 0:b74591d5ab33 | 507 | */ |
be_bryan | 0:b74591d5ab33 | 508 | |
be_bryan | 0:b74591d5ab33 | 509 | |
be_bryan | 0:b74591d5ab33 | 510 | static int gettok(char *, const int); |
be_bryan | 0:b74591d5ab33 | 511 | static int getNextToken(char *, const int); |
be_bryan | 0:b74591d5ab33 | 512 | static int HandleKV(char *, char *, const int, const int); |
be_bryan | 0:b74591d5ab33 | 513 | static int isstring(int); |
be_bryan | 0:b74591d5ab33 | 514 | |
be_bryan | 0:b74591d5ab33 | 515 | /** |
be_bryan | 0:b74591d5ab33 | 516 | * \brief Current token of key-value protocol's tokenizer |
be_bryan | 0:b74591d5ab33 | 517 | */ |
be_bryan | 0:b74591d5ab33 | 518 | static int CurTok = 0; |
be_bryan | 0:b74591d5ab33 | 519 | |
be_bryan | 0:b74591d5ab33 | 520 | /** |
be_bryan | 0:b74591d5ab33 | 521 | * \enum Token enumeration for key-value protocol tokenizer |
be_bryan | 0:b74591d5ab33 | 522 | * |
be_bryan | 0:b74591d5ab33 | 523 | * This enum is used by key-value protocol tokenizer |
be_bryan | 0:b74591d5ab33 | 524 | * to detect parts of protocol in stream. |
be_bryan | 0:b74591d5ab33 | 525 | * |
be_bryan | 0:b74591d5ab33 | 526 | * tok_eof ::= EOF (end of file) |
be_bryan | 0:b74591d5ab33 | 527 | * tok_open ::= "{{" |
be_bryan | 0:b74591d5ab33 | 528 | * tok_close ::= "}}" |
be_bryan | 0:b74591d5ab33 | 529 | * tok_semicolon ::= ";" |
be_bryan | 0:b74591d5ab33 | 530 | * tok_string ::= [a-zA-Z0-9_-!@#$%^&*()]+ // See isstring() function |
be_bryan | 0:b74591d5ab33 | 531 | * |
be_bryan | 0:b74591d5ab33 | 532 | */ |
be_bryan | 0:b74591d5ab33 | 533 | enum Token { |
be_bryan | 0:b74591d5ab33 | 534 | tok_eof = -1, |
be_bryan | 0:b74591d5ab33 | 535 | tok_open = -2, |
be_bryan | 0:b74591d5ab33 | 536 | tok_close = -3, |
be_bryan | 0:b74591d5ab33 | 537 | tok_semicolon = -4, |
be_bryan | 0:b74591d5ab33 | 538 | tok_string = -5 |
be_bryan | 0:b74591d5ab33 | 539 | }; |
be_bryan | 0:b74591d5ab33 | 540 | |
be_bryan | 0:b74591d5ab33 | 541 | /** |
be_bryan | 0:b74591d5ab33 | 542 | * \brief Read character from stream of data |
be_bryan | 0:b74591d5ab33 | 543 | * |
be_bryan | 0:b74591d5ab33 | 544 | * Closure for default "get character" function. |
be_bryan | 0:b74591d5ab33 | 545 | * This function is used to read characters from the stream |
be_bryan | 0:b74591d5ab33 | 546 | * (default is serial port RX). Key-value protocol tokenizer |
be_bryan | 0:b74591d5ab33 | 547 | * will build stream of tokes used by key-value protocol to |
be_bryan | 0:b74591d5ab33 | 548 | * detect valid messages. |
be_bryan | 0:b74591d5ab33 | 549 | * |
be_bryan | 0:b74591d5ab33 | 550 | * If EOF is received parser finishes parsing and stops. In |
be_bryan | 0:b74591d5ab33 | 551 | * situation where we have serial port stream of data parsing |
be_bryan | 0:b74591d5ab33 | 552 | * goes forever. |
be_bryan | 0:b74591d5ab33 | 553 | * |
be_bryan | 0:b74591d5ab33 | 554 | * \return Next character from the stream or EOF if stream has ended. |
be_bryan | 0:b74591d5ab33 | 555 | * |
be_bryan | 0:b74591d5ab33 | 556 | */ |
be_bryan | 0:b74591d5ab33 | 557 | extern "C" int greentea_getc() { |
be_bryan | 0:b74591d5ab33 | 558 | return greentea_serial->getc(); |
be_bryan | 0:b74591d5ab33 | 559 | } |
be_bryan | 0:b74591d5ab33 | 560 | |
be_bryan | 0:b74591d5ab33 | 561 | /** |
be_bryan | 0:b74591d5ab33 | 562 | * \brief parse input string for key-value pairs: {{key;value}} |
be_bryan | 0:b74591d5ab33 | 563 | * This function should replace scanf() used to |
be_bryan | 0:b74591d5ab33 | 564 | * check for incoming messages from master. All data |
be_bryan | 0:b74591d5ab33 | 565 | * parsed and rejected is discarded. |
be_bryan | 0:b74591d5ab33 | 566 | * |
be_bryan | 0:b74591d5ab33 | 567 | * \param out_key Ouput data with key |
be_bryan | 0:b74591d5ab33 | 568 | * \param out_value Ouput data with value |
be_bryan | 0:b74591d5ab33 | 569 | * \param out_key_size out_key total size |
be_bryan | 0:b74591d5ab33 | 570 | * \param out_value_size out_value total data |
be_bryan | 0:b74591d5ab33 | 571 | * |
be_bryan | 0:b74591d5ab33 | 572 | * success != 0 when key-value pair was found |
be_bryan | 0:b74591d5ab33 | 573 | * success == 0 when end of the stream was found |
be_bryan | 0:b74591d5ab33 | 574 | * |
be_bryan | 0:b74591d5ab33 | 575 | */ |
be_bryan | 0:b74591d5ab33 | 576 | extern "C" int greentea_parse_kv(char *out_key, |
be_bryan | 0:b74591d5ab33 | 577 | char *out_value, |
be_bryan | 0:b74591d5ab33 | 578 | const int out_key_size, |
be_bryan | 0:b74591d5ab33 | 579 | const int out_value_size) { |
be_bryan | 0:b74591d5ab33 | 580 | getNextToken(0, 0); |
be_bryan | 0:b74591d5ab33 | 581 | while (1) { |
be_bryan | 0:b74591d5ab33 | 582 | switch (CurTok) { |
be_bryan | 0:b74591d5ab33 | 583 | case tok_eof: |
be_bryan | 0:b74591d5ab33 | 584 | return 0; |
be_bryan | 0:b74591d5ab33 | 585 | |
be_bryan | 0:b74591d5ab33 | 586 | case tok_open: |
be_bryan | 0:b74591d5ab33 | 587 | if (HandleKV(out_key, out_value, out_key_size, out_value_size)) { |
be_bryan | 0:b74591d5ab33 | 588 | // We've found {{ KEY ; VALUE }} expression |
be_bryan | 0:b74591d5ab33 | 589 | return 1; |
be_bryan | 0:b74591d5ab33 | 590 | } |
be_bryan | 0:b74591d5ab33 | 591 | break; |
be_bryan | 0:b74591d5ab33 | 592 | |
be_bryan | 0:b74591d5ab33 | 593 | default: |
be_bryan | 0:b74591d5ab33 | 594 | // Load next token and pray... |
be_bryan | 0:b74591d5ab33 | 595 | getNextToken(0, 0); |
be_bryan | 0:b74591d5ab33 | 596 | break; |
be_bryan | 0:b74591d5ab33 | 597 | } |
be_bryan | 0:b74591d5ab33 | 598 | } |
be_bryan | 0:b74591d5ab33 | 599 | } |
be_bryan | 0:b74591d5ab33 | 600 | |
be_bryan | 0:b74591d5ab33 | 601 | /** |
be_bryan | 0:b74591d5ab33 | 602 | * \brief Get next token from stream |
be_bryan | 0:b74591d5ab33 | 603 | * |
be_bryan | 0:b74591d5ab33 | 604 | * Key-value TOKENIZER feature |
be_bryan | 0:b74591d5ab33 | 605 | * |
be_bryan | 0:b74591d5ab33 | 606 | * This function is used by key-value parser determine |
be_bryan | 0:b74591d5ab33 | 607 | * if key-value message is embedded in stream data. |
be_bryan | 0:b74591d5ab33 | 608 | * |
be_bryan | 0:b74591d5ab33 | 609 | * \param str Output parameters to store token string value |
be_bryan | 0:b74591d5ab33 | 610 | * \param str_size Size of 'str' parameter in bytes (characters) |
be_bryan | 0:b74591d5ab33 | 611 | * |
be_bryan | 0:b74591d5ab33 | 612 | */ |
be_bryan | 0:b74591d5ab33 | 613 | static int getNextToken(char *str, const int str_size) { |
be_bryan | 0:b74591d5ab33 | 614 | return CurTok = gettok(str, str_size); |
be_bryan | 0:b74591d5ab33 | 615 | } |
be_bryan | 0:b74591d5ab33 | 616 | |
be_bryan | 0:b74591d5ab33 | 617 | /** |
be_bryan | 0:b74591d5ab33 | 618 | * \brief Check if character is punctuation character |
be_bryan | 0:b74591d5ab33 | 619 | * |
be_bryan | 0:b74591d5ab33 | 620 | * Auxilary key-value TOKENIZER function |
be_bryan | 0:b74591d5ab33 | 621 | * |
be_bryan | 0:b74591d5ab33 | 622 | * Defines if character is in subset of allowed punctuation |
be_bryan | 0:b74591d5ab33 | 623 | * characters which can be part of a key or value string. |
be_bryan | 0:b74591d5ab33 | 624 | * Not allowed characters are: ";{}" |
be_bryan | 0:b74591d5ab33 | 625 | * |
be_bryan | 0:b74591d5ab33 | 626 | * \param c Input character to check |
be_bryan | 0:b74591d5ab33 | 627 | * \return Return 1 if character is allowed punctuation character, otherwise return false |
be_bryan | 0:b74591d5ab33 | 628 | * |
be_bryan | 0:b74591d5ab33 | 629 | */ |
be_bryan | 0:b74591d5ab33 | 630 | static int ispunctuation(int c) { |
be_bryan | 0:b74591d5ab33 | 631 | static const char punctuation[] = "_-!@#$%^&*()=+:<>,./?\\\"'"; // No ";{}" |
be_bryan | 0:b74591d5ab33 | 632 | for (size_t i=0; i< sizeof(punctuation); ++i) { |
be_bryan | 0:b74591d5ab33 | 633 | if (c == punctuation[i]) { |
be_bryan | 0:b74591d5ab33 | 634 | return 1; |
be_bryan | 0:b74591d5ab33 | 635 | } |
be_bryan | 0:b74591d5ab33 | 636 | } |
be_bryan | 0:b74591d5ab33 | 637 | return 0; |
be_bryan | 0:b74591d5ab33 | 638 | } |
be_bryan | 0:b74591d5ab33 | 639 | |
be_bryan | 0:b74591d5ab33 | 640 | /** |
be_bryan | 0:b74591d5ab33 | 641 | * \brief Check if character is string token character |
be_bryan | 0:b74591d5ab33 | 642 | * |
be_bryan | 0:b74591d5ab33 | 643 | * Auxilary key-value TOKENIZER function |
be_bryan | 0:b74591d5ab33 | 644 | * |
be_bryan | 0:b74591d5ab33 | 645 | * Defines if character is in subset of allowed string |
be_bryan | 0:b74591d5ab33 | 646 | * token characters. |
be_bryan | 0:b74591d5ab33 | 647 | * String defines set of characters which can be a key or value string. |
be_bryan | 0:b74591d5ab33 | 648 | * |
be_bryan | 0:b74591d5ab33 | 649 | * Allowed subset includes: |
be_bryan | 0:b74591d5ab33 | 650 | * - Alphanumerical characters |
be_bryan | 0:b74591d5ab33 | 651 | * - Digits |
be_bryan | 0:b74591d5ab33 | 652 | * - White spaces and |
be_bryan | 0:b74591d5ab33 | 653 | * - subset of punctuation characters. |
be_bryan | 0:b74591d5ab33 | 654 | * |
be_bryan | 0:b74591d5ab33 | 655 | * \param c Input character to check |
be_bryan | 0:b74591d5ab33 | 656 | * \return Return 1 if character is allowed punctuation character, otherwise return false |
be_bryan | 0:b74591d5ab33 | 657 | * |
be_bryan | 0:b74591d5ab33 | 658 | */ |
be_bryan | 0:b74591d5ab33 | 659 | static int isstring(int c) { |
be_bryan | 0:b74591d5ab33 | 660 | return (isalpha(c) || |
be_bryan | 0:b74591d5ab33 | 661 | isdigit(c) || |
be_bryan | 0:b74591d5ab33 | 662 | isspace(c) || |
be_bryan | 0:b74591d5ab33 | 663 | ispunctuation(c)); |
be_bryan | 0:b74591d5ab33 | 664 | } |
be_bryan | 0:b74591d5ab33 | 665 | |
be_bryan | 0:b74591d5ab33 | 666 | /** |
be_bryan | 0:b74591d5ab33 | 667 | * \brief TOKENIZER of key-value protocol |
be_bryan | 0:b74591d5ab33 | 668 | * |
be_bryan | 0:b74591d5ab33 | 669 | * Actual key-value TOKENIZER engine |
be_bryan | 0:b74591d5ab33 | 670 | * |
be_bryan | 0:b74591d5ab33 | 671 | * TOKENIZER defines #Token enum to map recognized tokens to integer values. |
be_bryan | 0:b74591d5ab33 | 672 | * |
be_bryan | 0:b74591d5ab33 | 673 | * <TOK_EOF> ::= EOF (end of file) |
be_bryan | 0:b74591d5ab33 | 674 | * <TOK_OPEN> ::= "{{" |
be_bryan | 0:b74591d5ab33 | 675 | * <TOK_CLOSE> ::= "}}" |
be_bryan | 0:b74591d5ab33 | 676 | * <TOK_SEMICOLON> ::= ";" |
be_bryan | 0:b74591d5ab33 | 677 | * <TOK_STRING> ::= [a-zA-Z0-9_-!@#$%^&*()]+ // See isstring() function * |
be_bryan | 0:b74591d5ab33 | 678 | * |
be_bryan | 0:b74591d5ab33 | 679 | * \param out_str Output string with parsed token (string) |
be_bryan | 0:b74591d5ab33 | 680 | * \param str_size Size of str buffer we can use |
be_bryan | 0:b74591d5ab33 | 681 | * |
be_bryan | 0:b74591d5ab33 | 682 | * \return Return #Token enum value used by parser to check for key-value occurrences |
be_bryan | 0:b74591d5ab33 | 683 | * |
be_bryan | 0:b74591d5ab33 | 684 | */ |
be_bryan | 0:b74591d5ab33 | 685 | static int gettok(char *out_str, const int str_size) { |
be_bryan | 0:b74591d5ab33 | 686 | static int LastChar = '!'; |
be_bryan | 0:b74591d5ab33 | 687 | static int str_idx = 0; |
be_bryan | 0:b74591d5ab33 | 688 | |
be_bryan | 0:b74591d5ab33 | 689 | // whitespace ::= |
be_bryan | 0:b74591d5ab33 | 690 | while (isspace(LastChar)) { |
be_bryan | 0:b74591d5ab33 | 691 | LastChar = greentea_getc(); |
be_bryan | 0:b74591d5ab33 | 692 | } |
be_bryan | 0:b74591d5ab33 | 693 | |
be_bryan | 0:b74591d5ab33 | 694 | // string ::= [a-zA-Z0-9_-!@#$%^&*()]+ |
be_bryan | 0:b74591d5ab33 | 695 | if (isstring(LastChar)) { |
be_bryan | 0:b74591d5ab33 | 696 | str_idx = 0; |
be_bryan | 0:b74591d5ab33 | 697 | if (out_str && str_idx < str_size - 1) { |
be_bryan | 0:b74591d5ab33 | 698 | out_str[str_idx++] = LastChar; |
be_bryan | 0:b74591d5ab33 | 699 | } |
be_bryan | 0:b74591d5ab33 | 700 | |
be_bryan | 0:b74591d5ab33 | 701 | while (isstring((LastChar = greentea_getc()))) |
be_bryan | 0:b74591d5ab33 | 702 | if (out_str && str_idx < str_size - 1) { |
be_bryan | 0:b74591d5ab33 | 703 | out_str[str_idx++] = LastChar; |
be_bryan | 0:b74591d5ab33 | 704 | } |
be_bryan | 0:b74591d5ab33 | 705 | if (out_str && str_idx < str_size) { |
be_bryan | 0:b74591d5ab33 | 706 | out_str[str_idx] = '\0'; |
be_bryan | 0:b74591d5ab33 | 707 | } |
be_bryan | 0:b74591d5ab33 | 708 | |
be_bryan | 0:b74591d5ab33 | 709 | return tok_string; |
be_bryan | 0:b74591d5ab33 | 710 | } |
be_bryan | 0:b74591d5ab33 | 711 | |
be_bryan | 0:b74591d5ab33 | 712 | // semicolon ::= ';' |
be_bryan | 0:b74591d5ab33 | 713 | if (LastChar == ';') { |
be_bryan | 0:b74591d5ab33 | 714 | LastChar = greentea_getc(); |
be_bryan | 0:b74591d5ab33 | 715 | return tok_semicolon; |
be_bryan | 0:b74591d5ab33 | 716 | } |
be_bryan | 0:b74591d5ab33 | 717 | |
be_bryan | 0:b74591d5ab33 | 718 | // open ::= '{{' |
be_bryan | 0:b74591d5ab33 | 719 | if (LastChar == '{') { |
be_bryan | 0:b74591d5ab33 | 720 | LastChar = greentea_getc(); |
be_bryan | 0:b74591d5ab33 | 721 | if (LastChar == '{') { |
be_bryan | 0:b74591d5ab33 | 722 | LastChar = greentea_getc(); |
be_bryan | 0:b74591d5ab33 | 723 | return tok_open; |
be_bryan | 0:b74591d5ab33 | 724 | } |
be_bryan | 0:b74591d5ab33 | 725 | } |
be_bryan | 0:b74591d5ab33 | 726 | |
be_bryan | 0:b74591d5ab33 | 727 | // close ::= '}' |
be_bryan | 0:b74591d5ab33 | 728 | if (LastChar == '}') { |
be_bryan | 0:b74591d5ab33 | 729 | LastChar = greentea_getc(); |
be_bryan | 0:b74591d5ab33 | 730 | if (LastChar == '}') { |
be_bryan | 0:b74591d5ab33 | 731 | return tok_close; |
be_bryan | 0:b74591d5ab33 | 732 | } |
be_bryan | 0:b74591d5ab33 | 733 | } |
be_bryan | 0:b74591d5ab33 | 734 | |
be_bryan | 0:b74591d5ab33 | 735 | if (LastChar == EOF) |
be_bryan | 0:b74591d5ab33 | 736 | return tok_eof; |
be_bryan | 0:b74591d5ab33 | 737 | |
be_bryan | 0:b74591d5ab33 | 738 | // Otherwise, just return the character as its ascii value. |
be_bryan | 0:b74591d5ab33 | 739 | int ThisChar = LastChar; |
be_bryan | 0:b74591d5ab33 | 740 | LastChar = greentea_getc(); |
be_bryan | 0:b74591d5ab33 | 741 | return ThisChar; |
be_bryan | 0:b74591d5ab33 | 742 | } |
be_bryan | 0:b74591d5ab33 | 743 | |
be_bryan | 0:b74591d5ab33 | 744 | /** |
be_bryan | 0:b74591d5ab33 | 745 | * \brief Key-value parser |
be_bryan | 0:b74591d5ab33 | 746 | * |
be_bryan | 0:b74591d5ab33 | 747 | * Key-value message grammar |
be_bryan | 0:b74591d5ab33 | 748 | * |
be_bryan | 0:b74591d5ab33 | 749 | * <MESSAGE>: <TOK_OPEN> <TOK_STRING> <TOK_SEMICOLON> <TOK_STRING> <TOK_CLOSE> |
be_bryan | 0:b74591d5ab33 | 750 | * |
be_bryan | 0:b74591d5ab33 | 751 | * Examples: |
be_bryan | 0:b74591d5ab33 | 752 | * message: "{{__timeout; 1000}}" |
be_bryan | 0:b74591d5ab33 | 753 | * "{{__sync; 12345678-1234-5678-1234-567812345678}}" |
be_bryan | 0:b74591d5ab33 | 754 | * |
be_bryan | 0:b74591d5ab33 | 755 | * \param out_key Output buffer to store key string value |
be_bryan | 0:b74591d5ab33 | 756 | * \param out_value Output buffer to store value string value |
be_bryan | 0:b74591d5ab33 | 757 | * \param out_key_size Buffer 'out_key' buffer size |
be_bryan | 0:b74591d5ab33 | 758 | * \param out_value_size Buffer 'out_value_size' buffer size |
be_bryan | 0:b74591d5ab33 | 759 | * \return Returns 1 if key-value message was parsed successfully in stream of tokens from tokenizer |
be_bryan | 0:b74591d5ab33 | 760 | * |
be_bryan | 0:b74591d5ab33 | 761 | */ |
be_bryan | 0:b74591d5ab33 | 762 | static int HandleKV(char *out_key, |
be_bryan | 0:b74591d5ab33 | 763 | char *out_value, |
be_bryan | 0:b74591d5ab33 | 764 | const int out_key_size, |
be_bryan | 0:b74591d5ab33 | 765 | const int out_value_size) { |
be_bryan | 0:b74591d5ab33 | 766 | // We already started with <open> |
be_bryan | 0:b74591d5ab33 | 767 | if (getNextToken(out_key, out_key_size) == tok_string) { |
be_bryan | 0:b74591d5ab33 | 768 | if (getNextToken(0, 0) == tok_semicolon) { |
be_bryan | 0:b74591d5ab33 | 769 | if (getNextToken(out_value, out_value_size) == tok_string) { |
be_bryan | 0:b74591d5ab33 | 770 | if (getNextToken(0, 0) == tok_close) { |
be_bryan | 0:b74591d5ab33 | 771 | // <open> <string> <semicolon> <string> <close> |
be_bryan | 0:b74591d5ab33 | 772 | // Found "{{KEY;VALUE}}" expression |
be_bryan | 0:b74591d5ab33 | 773 | return 1; |
be_bryan | 0:b74591d5ab33 | 774 | } |
be_bryan | 0:b74591d5ab33 | 775 | } |
be_bryan | 0:b74591d5ab33 | 776 | } |
be_bryan | 0:b74591d5ab33 | 777 | } |
be_bryan | 0:b74591d5ab33 | 778 | getNextToken(0, 0); |
be_bryan | 0:b74591d5ab33 | 779 | return 0; |
be_bryan | 0:b74591d5ab33 | 780 | } |