From f2918a113ed42ca3e58ed85fecb7ccbe1335c5f6 Mon Sep 17 00:00:00 2001 From: Andreas Waidler Date: Sat, 27 Feb 2010 01:17:14 +0100 Subject: [PATCH] Added utility functions and their tests. --- include/libsex/Makefile.am | 3 +- include/libsex/utility.hxx | 65 +++++++++++++++++ include/libsex/utility.ixx | 26 +++++++ src/Makefile.am | 2 +- src/utility.cxx | 57 +++++++++++++++ tests/Makefile.am | 4 +- tests/MockException.cxx | 8 +++ tests/MockException.hxx | 14 ++++ tests/TestUtility.cxx | 173 +++++++++++++++++++++++++++++++++++++++++++++ tests/TestUtility.hxx | 44 ++++++++++++ tests/testRegistry.cxx | 3 + 11 files changed, 396 insertions(+), 3 deletions(-) create mode 100644 include/libsex/utility.hxx create mode 100644 include/libsex/utility.ixx create mode 100644 src/utility.cxx create mode 100644 tests/MockException.cxx create mode 100644 tests/MockException.hxx create mode 100644 tests/TestUtility.cxx create mode 100644 tests/TestUtility.hxx diff --git a/include/libsex/Makefile.am b/include/libsex/Makefile.am index b84e390..8588d68 100644 --- a/include/libsex/Makefile.am +++ b/include/libsex/Makefile.am @@ -1,7 +1,8 @@ SUBDIRS = installdir = $(includedir)/$(PACKAGE) -install_HEADERS = Exception.hxx +install_HEADERS = Exception.hxx \ + utility.hxx utility.ixx uninstall-hook: rm -rf $(includedir)/$(PACKAGE) diff --git a/include/libsex/utility.hxx b/include/libsex/utility.hxx new file mode 100644 index 0000000..8218a85 --- /dev/null +++ b/include/libsex/utility.hxx @@ -0,0 +1,65 @@ +#ifndef UTILITY_HXX +#define UTILITY_HXX + +#include // variadic argument fun +#include // (v)snprintf() and size_t + +namespace libsex { + +/** + * @brief Formats a message and writes it into @a buffer. + * + * It is guaranteed that not more than @a length characters + * are written to @a buffer. + * + * @TODO what to do when buffer too small? + * + * @param buffer String to be filled. + * @param length Size of buffer. + * @param file Filename in which the error occured. + * @param line Line number in which the error occured. + * @param message printf formatstring template + * @param ap variadic argument list, holds parameters + * to be used when formatting (vsnprintf()). + */ +bool vformat( + char* const buffer, + size_t length, + const char* const file, + unsigned short line, + const char* const message, + va_list ap); + +/** + * @brief Same as the vformat() but uses "real" variadic + * arguments instead of va_list parameter. + */ +bool format( + char* const buffer, + size_t length, + const char* const file, + unsigned short line, + const char* const message, + ...); + +/** + * @brief Instanciates T with properly formatted message. + * + * T is supposed to have a static const char* + * MessageTemplate field, which will be used as + * formatstring for makeMessage(). + * + * @param file To be passed on to format(). + * @param line To be passed on to format(). + * @param ... Passed to format() (sprintf() like). + * + * @return Instance of T. + */ +template +T formatted(const char* const file, unsigned short line, ...); + +}; // namespace + +#include "utility.ixx" + +#endif diff --git a/include/libsex/utility.ixx b/include/libsex/utility.ixx new file mode 100644 index 0000000..6bee998 --- /dev/null +++ b/include/libsex/utility.ixx @@ -0,0 +1,26 @@ +#include // Exception::LENGTH + +namespace libsex { + +template +T formatted(const char* const file, + unsigned short line, + ...) +{ + // Temporary string should be initialized + // with 0 to not forget a trailing 0. + char buf[Exception::LENGTH + 1] = { 0 }; + + va_list ap; + va_start(ap, line); + // Fill buf with formatted T::TEMPLATE message. + vformat( + buf, Exception::LENGTH + 1, + file, line, + T::TEMPLATE, ap);\ + va_end(ap); + + return T(buf); +} + +}; // namespace diff --git a/src/Makefile.am b/src/Makefile.am index 1a3755c..602517d 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -4,4 +4,4 @@ lib_LTLIBRARIES = libsex.la libsex_la_CPPFLAGS = -I$(top_srcdir)/include libsex_la_CXXFLAGS = libsex_la_LIBADD = -libsex_la_SOURCES = Exception.cxx +libsex_la_SOURCES = Exception.cxx utility.cxx diff --git a/src/utility.cxx b/src/utility.cxx new file mode 100644 index 0000000..be38298 --- /dev/null +++ b/src/utility.cxx @@ -0,0 +1,57 @@ +#include +#include + +namespace libsex { + +bool vformat( + char* const buffer, + size_t length, + const char* const file, + unsigned short line, + const char* const message, + va_list ap) +{ + size_t chars + = snprintf( + buffer, length, + "%s:%d: ", file, line); + + // If buffer was too small, chars contains length + // of chars that would've been written. Chars must + // be lower than length or else we could not fit + // the message into buffer, too. + bool didFitIntoBuffer = chars < length; + + if (didFitIntoBuffer) { + size_t newLength = length - chars; + char* const newBuffer = buffer + chars; + chars = vsnprintf( + newBuffer, newLength, + message, ap); + // See comment above. + didFitIntoBuffer = chars <= newLength; + } + + return didFitIntoBuffer; + +} + +bool format( + char* const buffer, + size_t length, + const char* const file, + unsigned short line, + const char* const message, + ...) +{ + va_list ap; + va_start(ap, message); + + bool result = vformat( + buffer, length, file, line, message, ap); + + va_end(ap); + return result; +} + +}; // namespace diff --git a/tests/Makefile.am b/tests/Makefile.am index 436c370..d6e4530 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -9,9 +9,11 @@ tests_CPPFLAGS = -I$(top_srcdir)/include -I$(top_srcdir) $(CPPUNIT_CFLAGS) tests_CXXFLAGS = $(CPPUNIT_LIBS) tests_LDADD = $(top_srcdir)/src/$(PACKAGE).la tests_SOURCES = main.cxx testRegistry.cxx \ + MockException.cxx \ CountingException.cxx \ UncloneableException.cxx \ - TestException.cxx + TestException.cxx \ + TestUtility.cxx else diff --git a/tests/MockException.cxx b/tests/MockException.cxx new file mode 100644 index 0000000..30eec58 --- /dev/null +++ b/tests/MockException.cxx @@ -0,0 +1,8 @@ +#include + +const char* const MockException::TEMPLATE = "%s=%d"; + +MockException::MockException(const char* const message) throw() +: libsex::Exception(message) +{ +} diff --git a/tests/MockException.hxx b/tests/MockException.hxx new file mode 100644 index 0000000..c4dd985 --- /dev/null +++ b/tests/MockException.hxx @@ -0,0 +1,14 @@ +#ifndef MOCKEXCEPTION_HXX +#define MOCKEXCEPTION_HXX + +#include + +class MockException : public libsex::Exception +{ +public: + static const char* const TEMPLATE; + + MockException(const char* const message) throw(); +}; + +#endif diff --git a/tests/TestUtility.cxx b/tests/TestUtility.cxx new file mode 100644 index 0000000..377266d --- /dev/null +++ b/tests/TestUtility.cxx @@ -0,0 +1,173 @@ +#include +#include + +#include + +void TestUtility::setUp() {} +void TestUtility::tearDown() {} + +void TestUtility::testShortTemplateWithoutFormatting() +{ + char buf[100] = { 0 }; + + CPPUNIT_ASSERT( + libsex::format( + buf, 100, + "foobar.cxx", 42, + "Message template.")); + + std::string exp("foobar.cxx:42: Message template."); + CPPUNIT_ASSERT_EQUAL(exp, std::string(buf)); +} + +void TestUtility::testWhetherFalseIsReturnedWhenHeaderFillsBuffer() +{ + char buf[100] = { 0 }; + + CPPUNIT_ASSERT( + !libsex::format( + buf, 16, + "foobar.cxx", 42, + "Message template.")); + + std::string exp("foobar.cxx:42: "); + CPPUNIT_ASSERT_EQUAL(exp, std::string(buf)); +} + +void TestUtility::testWhetherTrueIsReturnedOnExactFit() +{ + char buf[100] = { 0 }; + + CPPUNIT_ASSERT( + libsex::format( + buf, 33, + "foobar.cxx", 42, + "Message template.")); + + std::string exp("foobar.cxx:42: Message template."); + CPPUNIT_ASSERT_EQUAL(exp, std::string(buf)); +} + +void TestUtility::testWhetherUnformattedOverflowsArePrevented() +{ + // Array should be large enough to not really get + // overwritten. Just adjust the parameter 'length' + // to libsex::format(). + char buf[100] = { 0 }; + + CPPUNIT_ASSERT( + !libsex::format( + buf, 10, + "foobar.cxx", 42, + "Message template.")); + + assertBoundary(buf, 10, 100); +} + +void TestUtility::testWhetherSimpleFormattingWorks() +{ + char buf[100] = { 0 }; + + CPPUNIT_ASSERT( + libsex::format( + buf, 100, + "foobar.cxx", 42, + "String: '%s'.", "STRING")); + + std::string exp("foobar.cxx:42: String: 'STRING'."); + CPPUNIT_ASSERT_EQUAL(exp, std::string(buf)); +} + +void TestUtility::testWhetherFormattedOverflowsArePreventedBeforeFormatstring() +{ + // Array should be large enough to not really get + // overwritten. Just adjust the parameter 'length' + // to libsex::format(). + char buf[100] = { 0 }; + + CPPUNIT_ASSERT( + !libsex::format( + buf, 10, + "foobar.cxx", 42, + "String: '%s'.", "STRING")); + + assertBoundary(buf, 10, 100); +} + +void TestUtility::testWhetherFormattedOverflowsArePreventedInFormatstring() +{ + // Array should be large enough to not really get + // overwritten. Just adjust the parameter 'length' + // to libsex::format(). + char buf[100] = { 0 }; + + CPPUNIT_ASSERT( + !libsex::format( + buf, 28, + "foobar.cxx", 42, + "String: '%s'.", "STRING")); + + assertBoundary(buf, 28, 100); +} + +void TestUtility::testMultipleFormatParameters() +{ + char buf[100] = { 0 }; + + CPPUNIT_ASSERT( + libsex::format( + buf, 100, + "foobar.cxx", 42, + "%s=%d", "var", 42)); + + std::string exp("foobar.cxx:42: var=42"); + CPPUNIT_ASSERT_EQUAL(exp, std::string(buf)); +} + +void TestUtility::testWhetherMultipleParametersDontOverflowBeforeFormatstring() +{ + char buf[100] = { 0 }; + + CPPUNIT_ASSERT( + !libsex::format( + buf, 10, + "foobar.cxx", 42, + "%s=%d", "var", 42)); + + assertBoundary(buf, 10, 100); +} + +void TestUtility::testWhetherMultipleParametersDontOverflowInFormatstring() +{ + char buf[100] = { 0 }; + + CPPUNIT_ASSERT( + !libsex::format( + buf, 18, + "foobar.cxx", 42, + "%s=%d", "var", 42)); + + assertBoundary(buf, 18, 100); +} + +void TestUtility::testTemplateFactory() +{ + MockException e + = libsex::formatted( + "foobar.cxx", 42, + "VAR", 42); + + CPPUNIT_ASSERT_EQUAL( + std::string("foobar.cxx:42: VAR=42"), + std::string(e.what())); +} + +void TestUtility::assertBoundary( + const char* const buffer, + size_t boundary, + size_t length) +{ + for (size_t i = boundary; i < length; ++i) { + CPPUNIT_ASSERT_EQUAL('\0', buffer[i]); + } +} diff --git a/tests/TestUtility.hxx b/tests/TestUtility.hxx new file mode 100644 index 0000000..8b097d4 --- /dev/null +++ b/tests/TestUtility.hxx @@ -0,0 +1,44 @@ +#ifndef TESTUTILITY_HXX +#define TESTUTILITY_HXX + +#include + +class TestUtility : public CppUnit::TestFixture +{ +CPPUNIT_TEST_SUITE(TestUtility); + CPPUNIT_TEST(testShortTemplateWithoutFormatting); + CPPUNIT_TEST(testWhetherFalseIsReturnedWhenHeaderFillsBuffer); + CPPUNIT_TEST(testWhetherTrueIsReturnedOnExactFit); + CPPUNIT_TEST(testWhetherUnformattedOverflowsArePrevented); + CPPUNIT_TEST(testWhetherSimpleFormattingWorks); + CPPUNIT_TEST(testWhetherFormattedOverflowsArePreventedBeforeFormatstring); + CPPUNIT_TEST(testWhetherFormattedOverflowsArePreventedInFormatstring); + CPPUNIT_TEST(testMultipleFormatParameters); + CPPUNIT_TEST(testWhetherMultipleParametersDontOverflowBeforeFormatstring); + CPPUNIT_TEST(testWhetherMultipleParametersDontOverflowInFormatstring); + CPPUNIT_TEST(testTemplateFactory); +CPPUNIT_TEST_SUITE_END(); + +public: + void setUp(); + void tearDown(); + + void testShortTemplateWithoutFormatting(); + void testWhetherFalseIsReturnedWhenHeaderFillsBuffer(); + void testWhetherTrueIsReturnedOnExactFit(); + void testWhetherUnformattedOverflowsArePrevented(); + void testWhetherSimpleFormattingWorks(); + void testWhetherFormattedOverflowsArePreventedBeforeFormatstring(); + void testWhetherFormattedOverflowsArePreventedInFormatstring(); + void testMultipleFormatParameters(); + void testWhetherMultipleParametersDontOverflowBeforeFormatstring(); + void testWhetherMultipleParametersDontOverflowInFormatstring(); + void testTemplateFactory(); + + void assertBoundary( + const char* const buffer, + size_t boundary, + size_t length); +}; + +#endif diff --git a/tests/testRegistry.cxx b/tests/testRegistry.cxx index 62a8c6e..99338a8 100644 --- a/tests/testRegistry.cxx +++ b/tests/testRegistry.cxx @@ -5,3 +5,6 @@ #include CPPUNIT_TEST_SUITE_REGISTRATION(TestException); + +#include +CPPUNIT_TEST_SUITE_REGISTRATION(TestUtility); -- 2.11.4.GIT