/*
    ChibiOS/RT - Copyright (C) 2006-2007 Giovanni Di Sirio.
    This file is part of ChibiOS/RT.
    ChibiOS/RT is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 3 of the License, or
    (at your option) any later version.
    ChibiOS/RT is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.
    You should have received a copy of the GNU General Public License
    along with this program.  If not, see .
*/
#include 
#include "test.h"
/**
 * @page test_serial Serial Drivers test
 *
 * Description
 * This module implements the test sequence for the @ref Serial subsystem.
 * The tests are performed on a loopback software serial driver where a
 * dedicated thread echoes back in the input queue the data read from the
 * output queue at a fixed rate. The test module also tests implicitly the
 * channels code.
 *
 * Objective
 * Objective of the test module is to cover 100% of the @ref Serial code
 * as a necessary step in order to assess its maturity level.
 * Note that the @ref Serial subsystem depends on the @ref Semaphores and
 * @ref Events subsystems that have to met their testing objectives as well.
 *
 * Preconditions
 * The module requires the following kernel options:
 * - @p CH_USE_SERIAL_FULLDUPLEX
 * .
 * In case some of the required options are not enabled then some or all tests
 * may be skipped.
 *
 *
 * Test Cases
 * - @subpage test_serial_001
 * - @subpage test_serial_002
 * .
 * @file testserial.c
 * @brief Serial Driver test source file
 * @file testserial.h
 * @brief Serial Driver test header file
 */
#if CH_USE_SERIAL_FULLDUPLEX
#define TEST_QUEUES_SIZE 8
static FullDuplexDriver fdd;
/* Loopback thread, it simulates a low level driver. The thread terminates by
   sending a zero through the loopback driver.*/
static msg_t thread1(void *p) {
  while (TRUE) {
    chEvtWaitAny(1);
    chSysLock();
    while (TRUE) {
      msg_t b = chFDDRequestDataI(&fdd);
      if (b < Q_OK)
        break;
      if (b == 0) {
        chSchRescheduleS();
        chSysUnlock();
        return 0;
      }
      chFDDIncomingDataI(&fdd, (uint8_t)b);
      chSchRescheduleS();
    }
    chSysUnlock();
  }
}
static void infy(void) {}
static void onfy(void) {
  chEvtSignalI(threads[0], 1);
  chSchRescheduleS();
}
/**
 * @page test_serial_001 Synchronous loopback
 *
 * Description
 * A sequence of characters are sent to the loopback driver and read back. The
 * test is performed twice using both the direct APIs and the channels API
 * implementations.
 * The test expects to read all the characters back and in the correct
 * sequence.
 */
static char *serial1_gettest(void) {
  return "Serial driver, synchronous";
}
static void serial1_setup(void) {
  /* Initializes the loopback driver.*/
  chFDDInit(&fdd, wa[3], 8, infy, wa[4], 8, onfy);
  /* Starts the loopback thread.*/
  threads[0] = chThdCreateStatic(wa[0], WA_SIZE, chThdGetPriority() + 1,
                                 thread1, 0);
}
static void serial1_teardown(void) {
  /* Terminates the loopback thread.*/
  chFDDPut(&fdd, 0);
}
static void serial1_execute(void) {
  unsigned i;
  msg_t b;
  /* Loopback test using the direct APIs.*/
  for (i = 0; i < 4; i++) {
    chFDDPut(&fdd, 'A' + i);
    b = chFDDGetTimeout(&fdd, S2ST(1));
    if (b < Q_OK)
      break;
    test_emit_token(b);
  }
  test_assert_sequence(1, "ABCD");
  test_assert(2, chFDDPutWouldBlock(&fdd) == FALSE, "output would block");
  test_assert(3, chFDDGetWouldBlock(&fdd) == TRUE, "input would not block");
  /* Loopback test using the channel APIs.*/
  for (i = 0; i < 4; i++) {
    chIOPut(&fdd, 'A' + i);
    b = chIOGetTimeout(&fdd, S2ST(1));
    if (b < Q_OK)
      break;
    test_emit_token(b);
  }
  test_assert_sequence(4, "ABCD");
  test_assert(5, chIOPutWouldBlock(&fdd) == FALSE, "output would block");
  test_assert(6, chIOGetWouldBlock(&fdd) == TRUE, "input would not block");
}
const struct testcase testserial1 = {
  serial1_gettest,
  serial1_setup,
  serial1_teardown,
  serial1_execute
};
/**
 * @page test_serial_002 Asynchronous loopback
 *
 * Description
 * A sequence of characters are sent to the loopback driver using the
 * asynchronous APIs and then read back. The test is performed twice using
 * both the direct APIs and the channels API. An input queue overflow test
 * is performed too.
 * The test expects that the queues are filled and emptied as expected and that
 * the overflow error condition is reported when expected.
 */
static char *serial2_gettest(void) {
  return "Serial driver, asynchronous";
}
static void serial2_setup(void) {
  /* Initializes the loopback driver.*/
  chFDDInit(&fdd, wa[3], 8, infy, wa[4], 8, onfy);
  /* Starts the loopback thread.*/
  threads[0] = chThdCreateStatic(wa[0], WA_SIZE, chThdGetPriority() + 1,
                                 thread1, 0);
}
static void serial2_teardown(void) {
  /* Terminates the loopback thread.*/
  chFDDPut(&fdd, 0);
}
static void serial2_execute(void) {
  size_t n;
  dflags_t flags;
  /* Asynchronous test using the direct APIs.*/
  n = chFDDWrite(&fdd, (uint8_t *)"ABCDEFGH", TEST_QUEUES_SIZE);
  test_assert(1, n == TEST_QUEUES_SIZE, "unexpected write condition");
  n = chFDDRead(&fdd, wa[1], TEST_QUEUES_SIZE);
  test_assert(2, n == TEST_QUEUES_SIZE, "unexpected read condition");
  test_assert(2, chFDDPutWouldBlock(&fdd) == FALSE, "output would block");
  test_assert(3, chFDDGetWouldBlock(&fdd) == TRUE, "input would not block");
  flags = chFDDGetAndClearFlags(&fdd);
  test_assert(4, flags == 0, "unexpected error condition");
  /* Input overflow testing.*/
  n = chFDDWrite(&fdd, (uint8_t *)"ABCDEFGH", TEST_QUEUES_SIZE);
  test_assert(5, n == TEST_QUEUES_SIZE, "unexpected write condition");
  /* The following operation will fail to loopback because the input queue
   * is full.*/
  chFDDPut(&fdd, 'Z');
  flags = chFDDGetAndClearFlags(&fdd);
  test_assert(6, flags == SD_OVERRUN_ERROR, "unexpected error condition");
  n = chFDDRead(&fdd, wa[1], TEST_QUEUES_SIZE);
  test_assert(7, n == TEST_QUEUES_SIZE, "unexpected read condition");
  /* Asynchronous test using the channel APIs.*/
  n = chIOWrite(&fdd, (uint8_t *)"ABCDEFGH", TEST_QUEUES_SIZE);
  test_assert(8, n == TEST_QUEUES_SIZE, "unexpected write condition");
  n = chIORead(&fdd, wa[1], TEST_QUEUES_SIZE);
  test_assert(9, n == TEST_QUEUES_SIZE, "unexpected read condition");
  test_assert(10, chIOPutWouldBlock(&fdd) == FALSE, "output would block");
  test_assert(11, chIOGetWouldBlock(&fdd) == TRUE, "input would not block");
}
const struct testcase testserial2 = {
  serial2_gettest,
  serial2_setup,
  serial2_teardown,
  serial2_execute
};
#endif /* CH_USE_SERIAL_FULLDUPLEX */
/*
 * Test sequence for queues pattern.
 */
const struct testcase * const patternserial[] = {
#if CH_USE_SERIAL_FULLDUPLEX
  &testserial1,
  &testserial2,
#endif
  NULL
};