From 5c9cc5b8f12528641f908c7968ea57903b717253 Mon Sep 17 00:00:00 2001 From: Filip Jagodzinski Date: Mon, 10 Dec 2018 12:09:22 +0100 Subject: [PATCH 1/3] Tests: Stream: Add more test cases --- TESTS/mbed_platform/Stream/main.cpp | 192 +++++++++++++++++++++++++--- 1 file changed, 175 insertions(+), 17 deletions(-) diff --git a/TESTS/mbed_platform/Stream/main.cpp b/TESTS/mbed_platform/Stream/main.cpp index e615e8adfc0..8c6d7b0a90c 100644 --- a/TESTS/mbed_platform/Stream/main.cpp +++ b/TESTS/mbed_platform/Stream/main.cpp @@ -18,43 +18,198 @@ #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 `man 3 fdopen`: + * + * Reads and writes may be intermixed on read/write streams in any order. Note + * that ANSI C requires that a file positioning function intervene between + * output and input, unless an input operation encounters end-of-file. (If + * this condition is not met, then a read is allowed to return the result + * of writes other than the most recent.) Therefore it is good practice (and + * indeed sometimes necessary under Linux) to put an fseek(3) or fgetpos(3) + * operation between write and read operations on such a stream. This operation + * may be an apparent no-op (as in fseek(..., 0L, SEEK_CUR) called for its + * synchronizing side effect). + */ 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) {} + 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; + } -protected: - virtual int _getc() + virtual ~Loopback() + { + } + + int test_vprintf(const char *fmt, ...) { - return _c; + 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) { - _c = 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: - char _c; + int8_t _buff[LOOPBACK_BUFF_SIZE]; + size_t _p_index; + size_t _g_index; }; -Loopback loop("loopback"); - +/* 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; - char char_buf[2] = {'a', 'b'}; - ret = loop.putc(char_buf[0]); - TEST_ASSERT_EQUAL_INT(char_buf[0], ret); + ret = loop.putc(char_buff[0]); + TEST_ASSERT_EQUAL_INT(char_buff[0], ret); ret = loop.getc(); - TEST_ASSERT_EQUAL_INT(char_buf[0], ret); - ret = loop.putc(char_buf[1]); - TEST_ASSERT_EQUAL_INT(char_buf[1], ret); + 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_buf[1], ret); - return; + 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) @@ -64,7 +219,10 @@ utest::v1::status_t test_setup(const size_t number_of_cases) } Case cases[] = { - Case("Test putc/getc", test_putc_getc) + 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); From 89a475bc4e3335cfc8e34d030da5b1ebbc3782f7 Mon Sep 17 00:00:00 2001 From: Filip Jagodzinski Date: Mon, 10 Dec 2018 12:13:55 +0100 Subject: [PATCH 2/3] Fix: Stream: Replace fflush() with fseek() This is a follow-up to PR #8331; fixing the Stream methods not covered there. --- platform/Stream.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/platform/Stream.cpp b/platform/Stream.cpp index 5c45c6c79f9..bdc7af91770 100644 --- a/platform/Stream.cpp +++ b/platform/Stream.cpp @@ -147,7 +147,7 @@ int Stream::printf(const char *format, ...) lock(); std::va_list arg; va_start(arg, format); - fflush(_file); + std::fseek(_file, 0, SEEK_CUR); int r = vfprintf(_file, format, arg); va_end(arg); unlock(); @@ -169,7 +169,7 @@ int Stream::scanf(const char *format, ...) int Stream::vprintf(const char *format, std::va_list args) { lock(); - fflush(_file); + std::fseek(_file, 0, SEEK_CUR); int r = vfprintf(_file, format, args); unlock(); return r; From 91e9010dd20c2aa87626d6da760bf56e53d46256 Mon Sep 17 00:00:00 2001 From: Filip Jagodzinski Date: Mon, 10 Dec 2018 16:30:16 +0100 Subject: [PATCH 3/3] Tests: Stream: Use a quote from C99 spec Use C99 instead of Linux man entry. --- TESTS/mbed_platform/Stream/main.cpp | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/TESTS/mbed_platform/Stream/main.cpp b/TESTS/mbed_platform/Stream/main.cpp index 8c6d7b0a90c..288be0aff00 100644 --- a/TESTS/mbed_platform/Stream/main.cpp +++ b/TESTS/mbed_platform/Stream/main.cpp @@ -23,17 +23,15 @@ /* This test suite verifies that write/read/write/read sequence can be * successfully executed on the Stream objects. * - * A qute from `man 3 fdopen`: + * A qute from C99 standard, paragraph 7.19.5.3, point 6: * - * Reads and writes may be intermixed on read/write streams in any order. Note - * that ANSI C requires that a file positioning function intervene between - * output and input, unless an input operation encounters end-of-file. (If - * this condition is not met, then a read is allowed to return the result - * of writes other than the most recent.) Therefore it is good practice (and - * indeed sometimes necessary under Linux) to put an fseek(3) or fgetpos(3) - * operation between write and read operations on such a stream. This operation - * may be an apparent no-op (as in fseek(..., 0L, SEEK_CUR) called for its - * synchronizing side effect). + * 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;