/* ChibiOS/RT - Copyright (C) 2006-2013 Giovanni Di Sirio Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ /** * @file test.c * @brief Tests support code. * * @addtogroup test * @{ */ #include "ch.h" #include "hal.h" #include "test.h" #include "testthd.h" #include "testsem.h" #include "testmtx.h" #include "testmsg.h" #include "testmbox.h" #include "testevt.h" #include "testheap.h" #include "testpools.h" #include "testdyn.h" #include "testqueues.h" #include "testbmk.h" /* * Array of all the test patterns. */ static ROMCONST struct testcase * ROMCONST *patterns[] = { patternthd, patternsem, patternmtx, patternmsg, patternmbox, patternevt, patternheap, patternpools, patterndyn, patternqueues, patternbmk, NULL }; static bool local_fail, global_fail; static unsigned failpoint; static char tokens_buffer[MAX_TOKENS]; static char *tokp; /* * Static working areas, the following areas can be used for threads or * used as temporary buffers. */ union test_buffers test; /* * Pointers to the spawned threads. */ thread_t *threads[MAX_THREADS]; /* * Pointers to the working areas. */ void * ROMCONST wa[5] = {test.wa.T0, test.wa.T1, test.wa.T2, test.wa.T3, test.wa.T4}; /* * Console output. */ static BaseSequentialStream *chp; /** * @brief Prints a decimal unsigned number. * * @param[in] n the number to be printed */ void test_printn(uint32_t n) { char buf[16], *p; if (!n) chSequentialStreamPut(chp, '0'); else { p = buf; while (n) *p++ = (n % 10) + '0', n /= 10; while (p > buf) chSequentialStreamPut(chp, *--p); } } /** * @brief Prints a line without final end-of-line. * * @param[in] msgp the message */ void test_print(const char *msgp) { while (*msgp) chSequentialStreamPut(chp, *msgp++); } /** * @brief Prints a line. * * @param[in] msgp the message */ void test_println(const char *msgp) { test_print(msgp); chSequentialStreamWrite(chp, (const uint8_t *)"\r\n", 2); } /* * Tokens. */ static void clear_tokens(void) { tokp = tokens_buffer; } static void print_tokens(void) { char *cp = tokens_buffer; while (cp < tokp) chSequentialStreamPut(chp, *cp++); } /** * @brief Emits a token into the tokens buffer. * * @param[in] token the token as a char */ void test_emit_token(char token) { chSysLock(); *tokp++ = token; chSysUnlock(); } /* * Assertions. */ bool _test_fail(unsigned point) { local_fail = TRUE; global_fail = TRUE; failpoint = point; return TRUE; } bool _test_assert(unsigned point, bool condition) { if (!condition) return _test_fail(point); return FALSE; } bool _test_assert_sequence(unsigned point, char *expected) { char *cp = tokens_buffer; while (cp < tokp) { if (*cp++ != *expected++) return _test_fail(point); } if (*expected) return _test_fail(point); clear_tokens(); return FALSE; } bool _test_assert_time_window(unsigned point, systime_t start, systime_t end) { return _test_assert(point, chVTIsSystemTimeWithin(start, end)); } /* * Threads utils. */ /** * @brief Sets a termination request in all the test-spawned threads. */ void test_terminate_threads(void) { int i; for (i = 0; i < MAX_THREADS; i++) if (threads[i]) chThdTerminate(threads[i]); } /** * @brief Waits for the completion of all the test-spawned threads. */ void test_wait_threads(void) { int i; for (i = 0; i < MAX_THREADS; i++) if (threads[i] != NULL) { chThdWait(threads[i]); threads[i] = NULL; } } #if CH_DBG_THREADS_PROFILING /** * @brief CPU pulse. * @note The current implementation is not totally reliable. * * @param[in] duration CPU pulse duration in milliseconds */ void test_cpu_pulse(unsigned duration) { systime_t start, end, now; start = chThdGetTicksX(chThdGetSelfX()); end = start + MS2ST(duration); do { now = chThdGetTicksX(chThdGetSelfX()); #if defined(SIMULATOR) _sim_check_for_interrupts(); #endif } while (end > start ? (now >= start) && (now < end) : (now >= start) || (now < end)); } #endif /** * @brief Delays execution until next system time tick. * * @return The system time. */ systime_t test_wait_tick(void) { chThdSleep(1); return chVTGetSystemTime(); } /* * Timer utils. */ /** * @brief Set to @p TRUE when the test timer reaches its deadline. */ bool test_timer_done; static virtual_timer_t vt; static void tmr(void *p) { (void)p; test_timer_done = TRUE; } /** * @brief Starts the test timer. * * @param[in] ms time in milliseconds */ void test_start_timer(unsigned ms) { systime_t duration = MS2ST(ms); test_timer_done = FALSE; chVTSet(&vt, duration, tmr, NULL); } /* * Test suite execution. */ static void execute_test(const struct testcase *tcp) { int i; /* Initialization */ clear_tokens(); local_fail = FALSE; for (i = 0; i < MAX_THREADS; i++) threads[i] = NULL; if (tcp->setup != NULL) tcp->setup(); tcp->execute(); if (tcp->teardown != NULL) tcp->teardown(); test_wait_threads(); } static void print_line(void) { unsigned i; for (i = 0; i < 76; i++) chSequentialStreamPut(chp, '-'); chSequentialStreamWrite(chp, (const uint8_t *)"\r\n", 2); } /** * @brief Test execution thread function. * * @param[in] p pointer to a @p BaseChannel object for test output * @return A failure boolean value. */ msg_t TestThread(void *p) { int i, j; chp = p; test_println(""); test_println("*** ChibiOS/RT test suite"); test_println("***"); test_print("*** Kernel: "); test_println(CH_KERNEL_VERSION); test_print("*** Compiled: "); test_println(__DATE__ " - " __TIME__); #ifdef PORT_COMPILER_NAME test_print("*** Compiler: "); test_println(PORT_COMPILER_NAME); #endif test_print("*** Architecture: "); test_println(PORT_ARCHITECTURE_NAME); #ifdef PORT_CORE_VARIANT_NAME test_print("*** Core Variant: "); test_println(PORT_CORE_VARIANT_NAME); #endif #ifdef PORT_INFO test_print("*** Port Info: "); test_println(PORT_INFO); #endif #ifdef PLATFORM_NAME test_print("*** Platform: "); test_println(PLATFORM_NAME); #endif #ifdef BOARD_NAME test_print("*** Test Board: "); test_println(BOARD_NAME); #endif test_println(""); global_fail = FALSE; i = 0; while (patterns[i]) { j = 0; while (patterns[i][j]) { print_line(); test_print("--- Test Case "); test_printn(i + 1); test_print("."); test_printn(j + 1); test_print(" ("); test_print(patterns[i][j]->name); test_println(")"); #if DELAY_BETWEEN_TESTS > 0 chThdSleepMilliseconds(DELAY_BETWEEN_TESTS); #endif execute_test(patterns[i][j]); if (local_fail) { test_print("--- Result: FAILURE (#"); test_printn(failpoint); test_print(" ["); print_tokens(); test_println("])"); } else test_println("--- Result: SUCCESS"); j++; } i++; } print_line(); test_println(""); test_print("Final result: "); if (global_fail) test_println("FAILURE"); else test_println("SUCCESS"); return (msg_t)global_fail; } /** @} */