mbed-os/TESTS/mbed_platform/Stream/main.cpp

233 lines
6.4 KiB
C++

/* mbed Microcontroller Library
* Copyright (c) 2018 ARM Limited
* SPDX-License-Identifier: Apache-2.0
*
* 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.
*/
#include "greentea-client/test_env.h"
#include "utest/utest.h"
#include "unity/unity.h"
#include "mbed.h"
#include "Stream.h"
/* This test suite verifies that write/read/write/read sequence can be
* successfully executed on the Stream objects.
*
* A qute from C99 standard, paragraph 7.19.5.3, point 6:
*
* When a file is opened with update mode ('+' as the second or third character in the
* above list of mode argument values), both input and output may be performed on the
* associated stream. However, output shall not be directly followed by input without an
* intervening call to the fflush function or to a file positioning function (fseek,
* fsetpos, or rewind), and input shall not be directly followed by output without an
* intervening call to a file positioning function, unless the input operation encounters end-
* of-file.
*/
using utest::v1::Case;
const char FMT[] = "Foo%02ibar.";
const size_t FORMATTED_STR_SIZE = 3 + 2 + 4 + 1;
// The test Stream instance has to be able to store two printf() output strings.
const size_t LOOPBACK_BUFF_SIZE = 2 * FORMATTED_STR_SIZE;
class Loopback : public Stream {
public:
Loopback(const char *name = NULL) : Stream(name)
{
// The `fgets()` stops reading after a newline or EOF.
// Fill the buffer with newlines to simplify fgets() usage in this test.
memset(_buff, '\n', LOOPBACK_BUFF_SIZE);
_p_index = 0;
_g_index = 0;
}
virtual ~Loopback()
{
}
int test_vprintf(const char *fmt, ...)
{
int rc = -1;
std::va_list args;
va_start(args, fmt);
rc = vprintf(fmt, args);
va_end(args);
return rc;
}
int test_vscanf(const char *fmt, ...)
{
int rc = EOF;
std::va_list args;
va_start(args, fmt);
rc = vscanf(fmt, args);
va_end(args);
return rc;
}
protected:
virtual int _putc(int c)
{
if (_p_index >= LOOPBACK_BUFF_SIZE) {
return -1;
}
_buff[_p_index++] = (int8_t)c;
return c;
}
virtual int _getc()
{
if (_g_index >= LOOPBACK_BUFF_SIZE) {
return -1;
}
return _buff[_g_index++];
}
private:
int8_t _buff[LOOPBACK_BUFF_SIZE];
size_t _p_index;
size_t _g_index;
};
/* Test intermixed Stream::putc() / Stream::getc().
*
* Given a Stream object,
* when a write/read/write/read sequence is executed
* with the use of Stream::putc() and Stream::getc() methods,
* then all operations succeed.
*/
void test_putc_getc()
{
char char_buff[2] = {'a', 'b'};
Loopback loop("loopback");
int ret;
ret = loop.putc(char_buff[0]);
TEST_ASSERT_EQUAL_INT(char_buff[0], ret);
ret = loop.getc();
TEST_ASSERT_EQUAL_INT(char_buff[0], ret);
ret = loop.putc(char_buff[1]);
TEST_ASSERT_EQUAL_INT(char_buff[1], ret);
ret = loop.getc();
TEST_ASSERT_EQUAL_INT(char_buff[1], ret);
}
/* Test intermixed Stream::puts() / Stream::gets().
*
* Given a Stream object,
* when a write/read/write/read sequence is executed,
* with the use of Stream::puts() and Stream::gets() methods,
* then all operations succeed.
*/
void test_puts_gets()
{
const size_t STR_LEN = 3;
const size_t STR_SIZE = STR_LEN + 1; // +1 for '\0'
char strings[2][STR_SIZE] = {"Foo", "Bar"};
const size_t GETS_BUFF_SIZE = STR_LEN + 2; // +1 for '\n' (gets() stops AFTER a '\n'), +1 for '\0'
char g_buff[GETS_BUFF_SIZE] = {};
Loopback loop("loopback");
int p_rc;
char *g_rc;
p_rc = loop.puts(strings[0]);
TEST_ASSERT(p_rc >= 0);
g_rc = loop.gets(g_buff, GETS_BUFF_SIZE);
TEST_ASSERT_EQUAL_PTR(g_buff, g_rc);
p_rc = loop.puts(strings[1]);
TEST_ASSERT(p_rc >= 0);
g_rc = loop.gets(g_buff, GETS_BUFF_SIZE);
TEST_ASSERT_EQUAL_PTR(g_buff, g_rc);
}
/* Test intermixed Stream::printf() / Stream::scanf().
*
* Given a Stream object,
* when a write/read/write/read sequence is executed,
* with the use of Stream::printf() and Stream::scanf() methods,
* then all operations succeed.
*/
void test_printf_scanf()
{
Loopback loop("loopback");
int p_val, g_val, rc;
p_val = 42;
g_val = p_val + 1;
rc = loop.printf(FMT, p_val);
TEST_ASSERT(rc > 0);
rc = loop.scanf(FMT, &g_val);
TEST_ASSERT(rc == 1);
TEST_ASSERT_EQUAL_INT(p_val, g_val);
p_val += 5;
g_val = p_val + 1;
rc = loop.printf(FMT, p_val);
TEST_ASSERT(rc > 0);
rc = loop.scanf(FMT, &g_val);
TEST_ASSERT(rc == 1);
TEST_ASSERT_EQUAL_INT(p_val, g_val);
}
/* Test intermixed Stream::vprintf() / Stream::vscanf().
*
* Given a Stream object,
* when a write/read/write/read sequence is executed,
* with the use of Stream::vprintf() and Stream::vscanf() methods,
* then all operations succeed.
*/
void test_vprintf_vscanf()
{
Loopback loop("loopback");
int p_val, g_val, rc;
p_val = 42;
g_val = p_val + 1;
rc = loop.test_vprintf(FMT, p_val);
TEST_ASSERT(rc > 0);
rc = loop.test_vscanf(FMT, &g_val);
TEST_ASSERT(rc == 1);
TEST_ASSERT_EQUAL_INT(p_val, g_val);
p_val += 5;
g_val = p_val + 1;
rc = loop.test_vprintf(FMT, p_val);
TEST_ASSERT(rc > 0);
rc = loop.test_vscanf(FMT, &g_val);
TEST_ASSERT(rc == 1);
TEST_ASSERT_EQUAL_INT(p_val, g_val);
}
utest::v1::status_t test_setup(const size_t number_of_cases)
{
GREENTEA_SETUP(10, "default_auto");
return utest::v1::verbose_test_setup_handler(number_of_cases);
}
Case cases[] = {
Case("Test putc/getc", test_putc_getc),
Case("Test puts/gets", test_puts_gets),
Case("Test printf/scanf", test_printf_scanf),
Case("Test vprintf/vscanf", test_vprintf_vscanf)
};
utest::v1::Specification specification(test_setup, cases);
int main()
{
return !utest::v1::Harness::run(specification);
}