diff options
author | Brooks Davis <brooks@FreeBSD.org> | 2020-03-17 16:56:50 +0000 |
---|---|---|
committer | Brooks Davis <brooks@FreeBSD.org> | 2020-03-17 16:56:50 +0000 |
commit | 08334c51dbb99d9ecd2bb86a2d94ed06da9e167a (patch) | |
tree | c43eb24d59bd5c963583a5190caef80fc8387322 /utils/format |
Notes
Diffstat (limited to 'utils/format')
-rw-r--r-- | utils/format/Kyuafile | 7 | ||||
-rw-r--r-- | utils/format/Makefile.am.inc | 59 | ||||
-rw-r--r-- | utils/format/containers.hpp | 66 | ||||
-rw-r--r-- | utils/format/containers.ipp | 138 | ||||
-rw-r--r-- | utils/format/containers_test.cpp | 190 | ||||
-rw-r--r-- | utils/format/exceptions.cpp | 110 | ||||
-rw-r--r-- | utils/format/exceptions.hpp | 84 | ||||
-rw-r--r-- | utils/format/exceptions_test.cpp | 74 | ||||
-rw-r--r-- | utils/format/formatter.cpp | 293 | ||||
-rw-r--r-- | utils/format/formatter.hpp | 123 | ||||
-rw-r--r-- | utils/format/formatter.ipp | 76 | ||||
-rw-r--r-- | utils/format/formatter_fwd.hpp | 45 | ||||
-rw-r--r-- | utils/format/formatter_test.cpp | 265 | ||||
-rw-r--r-- | utils/format/macros.hpp | 58 |
14 files changed, 1588 insertions, 0 deletions
diff --git a/utils/format/Kyuafile b/utils/format/Kyuafile new file mode 100644 index 000000000000..344ae455422c --- /dev/null +++ b/utils/format/Kyuafile @@ -0,0 +1,7 @@ +syntax(2) + +test_suite("kyua") + +atf_test_program{name="containers_test"} +atf_test_program{name="exceptions_test"} +atf_test_program{name="formatter_test"} diff --git a/utils/format/Makefile.am.inc b/utils/format/Makefile.am.inc new file mode 100644 index 000000000000..a37fc4057079 --- /dev/null +++ b/utils/format/Makefile.am.inc @@ -0,0 +1,59 @@ +# Copyright 2010 The Kyua Authors. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of Google Inc. nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +libutils_a_SOURCES += utils/format/containers.hpp +libutils_a_SOURCES += utils/format/containers.ipp +libutils_a_SOURCES += utils/format/exceptions.cpp +libutils_a_SOURCES += utils/format/exceptions.hpp +libutils_a_SOURCES += utils/format/formatter.cpp +libutils_a_SOURCES += utils/format/formatter.hpp +libutils_a_SOURCES += utils/format/formatter_fwd.hpp +libutils_a_SOURCES += utils/format/formatter.ipp +libutils_a_SOURCES += utils/format/macros.hpp + +if WITH_ATF +tests_utils_formatdir = $(pkgtestsdir)/utils/format + +tests_utils_format_DATA = utils/format/Kyuafile +EXTRA_DIST += $(tests_utils_format_DATA) + +tests_utils_format_PROGRAMS = utils/format/containers_test +utils_format_containers_test_SOURCES = utils/format/containers_test.cpp +utils_format_containers_test_CXXFLAGS = $(UTILS_CFLAGS) $(ATF_CXX_CFLAGS) +utils_format_containers_test_LDADD = $(UTILS_LIBS) $(ATF_CXX_LIBS) + +tests_utils_format_PROGRAMS += utils/format/exceptions_test +utils_format_exceptions_test_SOURCES = utils/format/exceptions_test.cpp +utils_format_exceptions_test_CXXFLAGS = $(UTILS_CFLAGS) $(ATF_CXX_CFLAGS) +utils_format_exceptions_test_LDADD = $(UTILS_LIBS) $(ATF_CXX_LIBS) + +tests_utils_format_PROGRAMS += utils/format/formatter_test +utils_format_formatter_test_SOURCES = utils/format/formatter_test.cpp +utils_format_formatter_test_CXXFLAGS = $(UTILS_CFLAGS) $(ATF_CXX_CFLAGS) +utils_format_formatter_test_LDADD = $(UTILS_LIBS) $(ATF_CXX_LIBS) +endif diff --git a/utils/format/containers.hpp b/utils/format/containers.hpp new file mode 100644 index 000000000000..7334c250de4e --- /dev/null +++ b/utils/format/containers.hpp @@ -0,0 +1,66 @@ +// Copyright 2014 The Kyua Authors. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of Google Inc. nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +/// \file utils/format/containers.hpp +/// Overloads to support formatting various base container types. + +#if !defined(UTILS_FORMAT_CONTAINERS_HPP) +#define UTILS_FORMAT_CONTAINERS_HPP + +#include <map> +#include <memory> +#include <ostream> +#include <set> +#include <utility> +#include <vector> + + +// This is ugly but necessary for C++ name resolution. Unsure if we'd do it +// differently... +namespace std { + + +template< typename K, typename V > +std::ostream& operator<<(std::ostream&, const std::map< K, V >&); + +template< typename T1, typename T2 > +std::ostream& operator<<(std::ostream&, const std::pair< T1, T2 >&); + +template< typename T > +std::ostream& operator<<(std::ostream&, const std::shared_ptr< T >); + +template< typename T > +std::ostream& operator<<(std::ostream&, const std::set< T >&); + +template< typename T > +std::ostream& operator<<(std::ostream&, const std::vector< T >&); + + +} // namespace std + +#endif // !defined(UTILS_FORMAT_CONTAINERS_HPP) diff --git a/utils/format/containers.ipp b/utils/format/containers.ipp new file mode 100644 index 000000000000..11d8e2914149 --- /dev/null +++ b/utils/format/containers.ipp @@ -0,0 +1,138 @@ +// Copyright 2014 The Kyua Authors. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of Google Inc. nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#if !defined(UTILS_FORMAT_CONTAINERS_IPP) +#define UTILS_FORMAT_CONTAINERS_IPP + +#include "utils/format/containers.hpp" + +#include <ostream> + + +/// Injects the object into a stream. +/// +/// \param output The stream into which to inject the object. +/// \param object The object to format. +/// +/// \return The output stream. +template< typename K, typename V > +std::ostream& +std::operator<<(std::ostream& output, const std::map< K, V >& object) +{ + output << "map("; + typename std::map< K, V >::size_type counter = 0; + for (typename std::map< K, V >::const_iterator iter = object.begin(); + iter != object.end(); ++iter, ++counter) { + if (counter != 0) + output << ", "; + output << (*iter).first << "=" << (*iter).second; + } + output << ")"; + return output; +} + + +/// Injects the object into a stream. +/// +/// \param output The stream into which to inject the object. +/// \param object The object to format. +/// +/// \return The output stream. +template< typename T1, typename T2 > +std::ostream& +std::operator<<(std::ostream& output, const std::pair< T1, T2 >& object) +{ + output << "pair(" << object.first << ", " << object.second << ")"; + return output; +} + + +/// Injects the object into a stream. +/// +/// \param output The stream into which to inject the object. +/// \param object The object to format. +/// +/// \return The output stream. +template< typename T > +std::ostream& +std::operator<<(std::ostream& output, const std::shared_ptr< T > object) +{ + if (object.get() == NULL) { + output << "<NULL>"; + } else { + output << *object; + } + return output; +} + + +/// Injects the object into a stream. +/// +/// \param output The stream into which to inject the object. +/// \param object The object to format. +/// +/// \return The output stream. +template< typename T > +std::ostream& +std::operator<<(std::ostream& output, const std::set< T >& object) +{ + output << "set("; + typename std::set< T >::size_type counter = 0; + for (typename std::set< T >::const_iterator iter = object.begin(); + iter != object.end(); ++iter, ++counter) { + if (counter != 0) + output << ", "; + output << (*iter); + } + output << ")"; + return output; +} + + +/// Injects the object into a stream. +/// +/// \param output The stream into which to inject the object. +/// \param object The object to format. +/// +/// \return The output stream. +template< typename T > +std::ostream& +std::operator<<(std::ostream& output, const std::vector< T >& object) +{ + output << "["; + for (typename std::vector< T >::size_type i = 0; i < object.size(); ++i) { + if (i != 0) + output << ", "; + output << object[i]; + } + output << "]"; + return output; +} + + +#endif // !defined(UTILS_FORMAT_CONTAINERS_IPP) diff --git a/utils/format/containers_test.cpp b/utils/format/containers_test.cpp new file mode 100644 index 000000000000..e1c452da2df6 --- /dev/null +++ b/utils/format/containers_test.cpp @@ -0,0 +1,190 @@ +// Copyright 2014 The Kyua Authors. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of Google Inc. nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "utils/format/containers.ipp" + +#include <memory> +#include <ostream> +#include <set> +#include <string> +#include <utility> +#include <vector> + +#include <atf-c++.hpp> + + + +namespace { + + +/// Formats a value and compares it to an expected string. +/// +/// \tparam T The type of the value to format. +/// \param expected Expected formatted text. +/// \param actual The value to format. +/// +/// \post Fails the test case if the formatted actual value does not match +/// the provided expected string. +template< typename T > +static void +do_check(const char* expected, const T& actual) +{ + std::ostringstream str; + str << actual; + ATF_REQUIRE_EQ(expected, str.str()); +} + + +} // anonymous namespace + + +ATF_TEST_CASE_WITHOUT_HEAD(std_map__empty); +ATF_TEST_CASE_BODY(std_map__empty) +{ + do_check("map()", std::map< char, char >()); + do_check("map()", std::map< int, long >()); +} + + +ATF_TEST_CASE_WITHOUT_HEAD(std_map__some); +ATF_TEST_CASE_BODY(std_map__some) +{ + { + std::map< char, int > v; + v['b'] = 123; + v['z'] = 321; + do_check("map(b=123, z=321)", v); + } + + { + std::map< int, std::string > v; + v[5] = "first"; + v[2] = "second"; + v[8] = "third"; + do_check("map(2=second, 5=first, 8=third)", v); + } +} + + +ATF_TEST_CASE_WITHOUT_HEAD(std_pair); +ATF_TEST_CASE_BODY(std_pair) +{ + do_check("pair(5, b)", std::pair< int, char >(5, 'b')); + do_check("pair(foo bar, baz)", + std::pair< std::string, std::string >("foo bar", "baz")); +} + + +ATF_TEST_CASE_WITHOUT_HEAD(std_shared_ptr__null); +ATF_TEST_CASE_BODY(std_shared_ptr__null) +{ + do_check("<NULL>", std::shared_ptr< char >()); + do_check("<NULL>", std::shared_ptr< int >()); +} + + +ATF_TEST_CASE_WITHOUT_HEAD(std_shared_ptr__not_null); +ATF_TEST_CASE_BODY(std_shared_ptr__not_null) +{ + do_check("f", std::shared_ptr< char >(new char('f'))); + do_check("8", std::shared_ptr< int >(new int(8))); +} + + +ATF_TEST_CASE_WITHOUT_HEAD(std_set__empty); +ATF_TEST_CASE_BODY(std_set__empty) +{ + do_check("set()", std::set< char >()); + do_check("set()", std::set< int >()); +} + + +ATF_TEST_CASE_WITHOUT_HEAD(std_set__some); +ATF_TEST_CASE_BODY(std_set__some) +{ + { + std::set< char > v; + v.insert('b'); + v.insert('z'); + do_check("set(b, z)", v); + } + + { + std::set< int > v; + v.insert(5); + v.insert(2); + v.insert(8); + do_check("set(2, 5, 8)", v); + } +} + + +ATF_TEST_CASE_WITHOUT_HEAD(std_vector__empty); +ATF_TEST_CASE_BODY(std_vector__empty) +{ + do_check("[]", std::vector< char >()); + do_check("[]", std::vector< int >()); +} + + +ATF_TEST_CASE_WITHOUT_HEAD(std_vector__some); +ATF_TEST_CASE_BODY(std_vector__some) +{ + { + std::vector< char > v; + v.push_back('b'); + v.push_back('z'); + do_check("[b, z]", v); + } + + { + std::vector< int > v; + v.push_back(5); + v.push_back(2); + v.push_back(8); + do_check("[5, 2, 8]", v); + } +} + + +ATF_INIT_TEST_CASES(tcs) +{ + ATF_ADD_TEST_CASE(tcs, std_map__empty); + ATF_ADD_TEST_CASE(tcs, std_map__some); + + ATF_ADD_TEST_CASE(tcs, std_pair); + + ATF_ADD_TEST_CASE(tcs, std_shared_ptr__null); + ATF_ADD_TEST_CASE(tcs, std_shared_ptr__not_null); + + ATF_ADD_TEST_CASE(tcs, std_set__empty); + ATF_ADD_TEST_CASE(tcs, std_set__some); + + ATF_ADD_TEST_CASE(tcs, std_vector__empty); + ATF_ADD_TEST_CASE(tcs, std_vector__some); +} diff --git a/utils/format/exceptions.cpp b/utils/format/exceptions.cpp new file mode 100644 index 000000000000..299b1d23cd8d --- /dev/null +++ b/utils/format/exceptions.cpp @@ -0,0 +1,110 @@ +// Copyright 2010 The Kyua Authors. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of Google Inc. nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "utils/format/exceptions.hpp" + +using utils::format::bad_format_error; +using utils::format::error; +using utils::format::extra_args_error; + + +/// Constructs a new error with a plain-text message. +/// +/// \param message The plain-text error message. +error::error(const std::string& message) : + std::runtime_error(message) +{ +} + + +/// Destructor for the error. +error::~error(void) throw() +{ +} + + +/// Constructs a new bad_format_error. +/// +/// \param format_ The invalid format string. +/// \param message Description of the error in the format string. +bad_format_error::bad_format_error(const std::string& format_, + const std::string& message) : + error("Invalid formatting string '" + format_ + "': " + message), + _format(format_) +{ +} + + +/// Destructor for the error. +bad_format_error::~bad_format_error(void) throw() +{ +} + + +/// \return The format string that caused the error. +const std::string& +bad_format_error::format(void) const +{ + return _format; +} + + +/// Constructs a new extra_args_error. +/// +/// \param format_ The format string. +/// \param arg_ The first extra argument passed to the format string. +extra_args_error::extra_args_error(const std::string& format_, + const std::string& arg_) : + error("Not enough fields in formatting string '" + format_ + "' to place " + "argument '" + arg_ + "'"), + _format(format_), + _arg(arg_) +{ +} + + +/// Destructor for the error. +extra_args_error::~extra_args_error(void) throw() +{ +} + + +/// \return The format string that was passed too many arguments. +const std::string& +extra_args_error::format(void) const +{ + return _format; +} + + +/// \return The first argument that caused the error. +const std::string& +extra_args_error::arg(void) const +{ + return _arg; +} diff --git a/utils/format/exceptions.hpp b/utils/format/exceptions.hpp new file mode 100644 index 000000000000..a28376df9c08 --- /dev/null +++ b/utils/format/exceptions.hpp @@ -0,0 +1,84 @@ +// Copyright 2010 The Kyua Authors. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of Google Inc. nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +/// \file utils/format/exceptions.hpp +/// Exception types raised by the format module. + +#if !defined(UTILS_FORMAT_EXCEPTIONS_HPP) +#define UTILS_FORMAT_EXCEPTIONS_HPP + +#include <stdexcept> +#include <string> + +namespace utils { +namespace format { + + +/// Base exception for format errors. +class error : public std::runtime_error { +public: + explicit error(const std::string&); + virtual ~error(void) throw(); +}; + + +/// Error denoting a bad format string. +class bad_format_error : public error { + /// The format string that caused the error. + std::string _format; + +public: + explicit bad_format_error(const std::string&, const std::string&); + virtual ~bad_format_error(void) throw(); + + const std::string& format(void) const; +}; + + +/// Error denoting too many arguments for the format string. +class extra_args_error : public error { + /// The format string that was passed too many arguments. + std::string _format; + + /// The first argument that caused the error. + std::string _arg; + +public: + explicit extra_args_error(const std::string&, const std::string&); + virtual ~extra_args_error(void) throw(); + + const std::string& format(void) const; + const std::string& arg(void) const; +}; + + +} // namespace format +} // namespace utils + + +#endif // !defined(UTILS_FORMAT_EXCEPTIONS_HPP) diff --git a/utils/format/exceptions_test.cpp b/utils/format/exceptions_test.cpp new file mode 100644 index 000000000000..28d401e57dad --- /dev/null +++ b/utils/format/exceptions_test.cpp @@ -0,0 +1,74 @@ +// Copyright 2010 The Kyua Authors. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of Google Inc. nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "utils/format/exceptions.hpp" + +#include <cstring> + +#include <atf-c++.hpp> + +using utils::format::bad_format_error; +using utils::format::error; +using utils::format::extra_args_error; + + +ATF_TEST_CASE_WITHOUT_HEAD(error); +ATF_TEST_CASE_BODY(error) +{ + const error e("Some text"); + ATF_REQUIRE(std::strcmp("Some text", e.what()) == 0); +} + + +ATF_TEST_CASE_WITHOUT_HEAD(bad_format_error); +ATF_TEST_CASE_BODY(bad_format_error) +{ + const bad_format_error e("format-string", "the-error"); + ATF_REQUIRE(std::strcmp("Invalid formatting string 'format-string': " + "the-error", e.what()) == 0); + ATF_REQUIRE_EQ("format-string", e.format()); +} + + +ATF_TEST_CASE_WITHOUT_HEAD(extra_args_error); +ATF_TEST_CASE_BODY(extra_args_error) +{ + const extra_args_error e("fmt", "extra"); + ATF_REQUIRE(std::strcmp("Not enough fields in formatting string 'fmt' to " + "place argument 'extra'", e.what()) == 0); + ATF_REQUIRE_EQ("fmt", e.format()); + ATF_REQUIRE_EQ("extra", e.arg()); +} + + +ATF_INIT_TEST_CASES(tcs) +{ + ATF_ADD_TEST_CASE(tcs, error); + ATF_ADD_TEST_CASE(tcs, bad_format_error); + ATF_ADD_TEST_CASE(tcs, extra_args_error); +} diff --git a/utils/format/formatter.cpp b/utils/format/formatter.cpp new file mode 100644 index 000000000000..99cfd40f03ab --- /dev/null +++ b/utils/format/formatter.cpp @@ -0,0 +1,293 @@ +// Copyright 2010 The Kyua Authors. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of Google Inc. nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "utils/format/formatter.hpp" + +#include <memory> +#include <string> +#include <utility> + +#include "utils/format/exceptions.hpp" +#include "utils/sanity.hpp" +#include "utils/text/exceptions.hpp" +#include "utils/text/operations.ipp" + +namespace format = utils::format; +namespace text = utils::text; + + +namespace { + + +/// Finds the next placeholder in a string. +/// +/// \param format The original format string provided by the user; needed for +/// error reporting purposes only. +/// \param expansion The string containing the placeholder to look for. Any +/// '%%' in the string will be skipped, and they must be stripped later by +/// strip_double_percent(). +/// \param begin The position from which to start looking for the next +/// placeholder. +/// +/// \return The position in the string in which the placeholder is located and +/// the placeholder itself. If there are no placeholders left, this returns +/// the length of the string and an empty string. +/// +/// \throw bad_format_error If the input string contains a trailing formatting +/// character. We cannot detect any other kind of invalid formatter because +/// we do not implement a full parser for them. +static std::pair< std::string::size_type, std::string > +find_next_placeholder(const std::string& format, + const std::string& expansion, + std::string::size_type begin) +{ + begin = expansion.find('%', begin); + while (begin != std::string::npos && expansion[begin + 1] == '%') + begin = expansion.find('%', begin + 2); + if (begin == std::string::npos) + return std::make_pair(expansion.length(), ""); + if (begin == expansion.length() - 1) + throw format::bad_format_error(format, "Trailing %"); + + std::string::size_type end = begin + 1; + while (end < expansion.length() && expansion[end] != 's') + end++; + const std::string placeholder = expansion.substr(begin, end - begin + 1); + if (end == expansion.length() || + placeholder.find('%', 1) != std::string::npos) + throw format::bad_format_error(format, "Unterminated placeholder '" + + placeholder + "'"); + return std::make_pair(begin, placeholder); +} + + +/// Converts a string to an integer. +/// +/// \param format The format string; for error reporting purposes only. +/// \param str The string to conver. +/// \param what The name of the field this integer belongs to; for error +/// reporting purposes only. +/// +/// \return An integer representing the input string. +inline int +to_int(const std::string& format, const std::string& str, const char* what) +{ + try { + return text::to_type< int >(str); + } catch (const text::value_error& e) { + throw format::bad_format_error(format, "Invalid " + std::string(what) + + "specifier"); + } +} + + +/// Constructs an std::ostringstream based on a formatting placeholder. +/// +/// \param format The format placeholder; may be empty. +/// +/// \return A new std::ostringstream that is prepared to format a single +/// object in the manner specified by the format placeholder. +/// +/// \throw bad_format_error If the format string is bad. We do minimal +/// validation on this string though. +static std::ostringstream* +new_ostringstream(const std::string& format) +{ + std::auto_ptr< std::ostringstream > output(new std::ostringstream()); + + if (format.length() <= 2) { + // If the format is empty, we create a new stream so that we don't have + // to check for NULLs later on. We rarely should hit this condition + // (and when we do it's a bug in the caller), so this is not a big deal. + // + // Otherwise, if the format is a regular '%s', then we don't have to do + // any processing for additional formatters. So this is just a "fast + // path". + } else { + std::string partial = format.substr(1, format.length() - 2); + if (partial[0] == '0') { + output->fill('0'); + partial.erase(0, 1); + } + if (!partial.empty()) { + const std::string::size_type dot = partial.find('.'); + if (dot != 0) + output->width(to_int(format, partial.substr(0, dot), "width")); + if (dot != std::string::npos) { + output->setf(std::ios::fixed, std::ios::floatfield); + output->precision(to_int(format, partial.substr(dot + 1), + "precision")); + } + } + } + + return output.release(); +} + + +/// Replaces '%%' by '%' in a given string range. +/// +/// \param in The input string to be rewritten. +/// \param begin The position at which to start the replacement. +/// \param end The position at which to end the replacement. +/// +/// \return The modified string and the amount of characters removed. +static std::pair< std::string, int > +strip_double_percent(const std::string& in, const std::string::size_type begin, + std::string::size_type end) +{ + std::string part = in.substr(begin, end - begin); + + int removed = 0; + std::string::size_type pos = part.find("%%"); + while (pos != std::string::npos) { + part.erase(pos, 1); + ++removed; + pos = part.find("%%", pos + 1); + } + + return std::make_pair(in.substr(0, begin) + part + in.substr(end), removed); +} + + +} // anonymous namespace + + +/// Performs internal initialization of the formatter. +/// +/// This is separate from the constructor just because it is shared by different +/// overloaded constructors. +void +format::formatter::init(void) +{ + const std::pair< std::string::size_type, std::string > placeholder = + find_next_placeholder(_format, _expansion, _last_pos); + const std::pair< std::string, int > no_percents = + strip_double_percent(_expansion, _last_pos, placeholder.first); + + _oss = new_ostringstream(placeholder.second); + + _expansion = no_percents.first; + _placeholder_pos = placeholder.first - no_percents.second; + _placeholder = placeholder.second; +} + + +/// Constructs a new formatter object (internal). +/// +/// \param format The format string. +/// \param expansion The format string with any replacements performed so far. +/// \param last_pos The position from which to start looking for formatting +/// placeholders. This must be maintained in case one of the replacements +/// introduced a new placeholder, which must be ignored. Think, for +/// example, replacing a "%s" string with "foo %s". +format::formatter::formatter(const std::string& format, + const std::string& expansion, + const std::string::size_type last_pos) : + _format(format), + _expansion(expansion), + _last_pos(last_pos), + _oss(NULL) +{ + init(); +} + + +/// Constructs a new formatter object. +/// +/// \param format The format string. The formatters in the string are not +/// validated during construction, but will cause errors when used later if +/// they are invalid. +format::formatter::formatter(const std::string& format) : + _format(format), + _expansion(format), + _last_pos(0), + _oss(NULL) +{ + init(); +} + + +format::formatter::~formatter(void) +{ + delete _oss; +} + + +/// Returns the formatted string. +/// +/// \return A string representation of the formatted string. +const std::string& +format::formatter::str(void) const +{ + return _expansion; +} + + +/// Automatic conversion of formatter objects to strings. +/// +/// This is provided to allow painless injection of formatter objects into +/// streams, without having to manually call the str() method. +format::formatter::operator const std::string&(void) const +{ + return _expansion; +} + + +/// Specialization of operator% for booleans. +/// +/// \param value The boolean to inject into the format string. +/// +/// \return A new formatter that has one less format placeholder. +format::formatter +format::formatter::operator%(const bool& value) const +{ + (*_oss) << (value ? "true" : "false"); + return replace(_oss->str()); +} + + +/// Replaces the first formatting placeholder with a value. +/// +/// \param arg The replacement string. +/// +/// \return A new formatter in which the first formatting placeholder has been +/// replaced by arg and is ready to replace the next item. +/// +/// \throw utils::format::extra_args_error If there are no more formatting +/// placeholders in the input string, or if the placeholder is invalid. +format::formatter +format::formatter::replace(const std::string& arg) const +{ + if (_placeholder_pos == _expansion.length()) + throw format::extra_args_error(_format, arg); + + const std::string expansion = _expansion.substr(0, _placeholder_pos) + + arg + _expansion.substr(_placeholder_pos + _placeholder.length()); + return formatter(_format, expansion, _placeholder_pos + arg.length()); +} diff --git a/utils/format/formatter.hpp b/utils/format/formatter.hpp new file mode 100644 index 000000000000..8c6188745a2e --- /dev/null +++ b/utils/format/formatter.hpp @@ -0,0 +1,123 @@ +// Copyright 2010 The Kyua Authors. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of Google Inc. nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +/// \file utils/format/formatter.hpp +/// Provides the definition of the utils::format::formatter class. +/// +/// The utils::format::formatter class is a poor man's replacement for the +/// Boost.Format library, as it is much simpler and has less dependencies. +/// +/// Be aware that the formatting supported by this module is NOT compatible +/// with printf(3) nor with Boost.Format. The general syntax for a +/// placeholder in a formatting string is: +/// +/// %[0][width][.precision]s +/// +/// In particular, note that the only valid formatting specifier is %s: the +/// library deduces what to print based on the type of the variable passed +/// in, not based on what the format string says. Also, note that the only +/// valid padding character is 0. + +#if !defined(UTILS_FORMAT_FORMATTER_HPP) +#define UTILS_FORMAT_FORMATTER_HPP + +#include "utils/format/formatter_fwd.hpp" + +#include <sstream> +#include <string> + +namespace utils { +namespace format { + + +/// Mechanism to format strings similar to printf. +/// +/// A formatter always maintains the original format string but also holds a +/// partial expansion. The partial expansion is immutable in the context of a +/// formatter instance, but calls to operator% return new formatter objects with +/// one less formatting placeholder. +/// +/// In general, one can format a string in the following manner: +/// +/// \code +/// const std::string s = (formatter("%s %s") % "foo" % 5).str(); +/// \endcode +/// +/// which, following the explanation above, would correspond to: +/// +/// \code +/// const formatter f1("%s %s"); +/// const formatter f2 = f1 % "foo"; +/// const formatter f3 = f2 % 5; +/// const std::string s = f3.str(); +/// \endcode +class formatter { + /// The original format string provided by the user. + std::string _format; + + /// The current "expansion" of the format string. + /// + /// This field gets updated on every call to operator%() to have one less + /// formatting placeholder. + std::string _expansion; + + /// The position of _expansion from which to scan for placeholders. + std::string::size_type _last_pos; + + /// The position of the first placeholder in the current expansion. + std::string::size_type _placeholder_pos; + + /// The first placeholder in the current expansion. + std::string _placeholder; + + /// Stream used to format any possible argument supplied by operator%(). + std::ostringstream* _oss; + + formatter replace(const std::string&) const; + + void init(void); + formatter(const std::string&, const std::string&, + const std::string::size_type); + +public: + explicit formatter(const std::string&); + ~formatter(void); + + const std::string& str(void) const; + operator const std::string&(void) const; + + template< typename Type > formatter operator%(const Type&) const; + formatter operator%(const bool&) const; +}; + + +} // namespace format +} // namespace utils + + +#endif // !defined(UTILS_FORMAT_FORMATTER_HPP) diff --git a/utils/format/formatter.ipp b/utils/format/formatter.ipp new file mode 100644 index 000000000000..6fad024b704f --- /dev/null +++ b/utils/format/formatter.ipp @@ -0,0 +1,76 @@ +// Copyright 2010 The Kyua Authors. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of Google Inc. nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#if !defined(UTILS_FORMAT_FORMATTER_IPP) +#define UTILS_FORMAT_FORMATTER_IPP + +#include <ostream> + +#include "utils/format/formatter.hpp" + +namespace utils { +namespace format { + + +/// Replaces the first format placeholder in a formatter. +/// +/// Constructs a new formatter object that has one less formatting placeholder, +/// as this has been replaced by the provided argument. Calling this operator +/// N times, where N is the number of formatting placeholders, effectively +/// formats the string. +/// +/// \param arg The argument to use as replacement for the format placeholder. +/// +/// \return A new formatter that has one less format placeholder. +template< typename Type > +inline formatter +formatter::operator%(const Type& arg) const +{ + (*_oss) << arg; + return replace(_oss->str()); +} + + +/// Inserts a formatter string into a stream. +/// +/// \param os The output stream. +/// \param f The formatter to process and inject into the stream. +/// +/// \return The output stream os. +inline std::ostream& +operator<<(std::ostream& os, const formatter& f) +{ + return (os << f.str()); +} + + +} // namespace format +} // namespace utils + + +#endif // !defined(UTILS_FORMAT_FORMATTER_IPP) diff --git a/utils/format/formatter_fwd.hpp b/utils/format/formatter_fwd.hpp new file mode 100644 index 000000000000..72c9e5ebf196 --- /dev/null +++ b/utils/format/formatter_fwd.hpp @@ -0,0 +1,45 @@ +// Copyright 2015 The Kyua Authors. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of Google Inc. nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +/// \file utils/format/formatter_fwd.hpp +/// Forward declarations for utils/format/formatter.hpp + +#if !defined(UTILS_FORMAT_FORMATTER_FWD_HPP) +#define UTILS_FORMAT_FORMATTER_FWD_HPP + +namespace utils { +namespace format { + + +class formatter; + + +} // namespace format +} // namespace utils + +#endif // !defined(UTILS_FORMAT_FORMATTER_FWD_HPP) diff --git a/utils/format/formatter_test.cpp b/utils/format/formatter_test.cpp new file mode 100644 index 000000000000..fdae785b1db7 --- /dev/null +++ b/utils/format/formatter_test.cpp @@ -0,0 +1,265 @@ +// Copyright 2010 The Kyua Authors. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of Google Inc. nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "utils/format/formatter.hpp" + +#include <ostream> + +#include <atf-c++.hpp> + +#include "utils/format/exceptions.hpp" +#include "utils/format/macros.hpp" + +namespace format = utils::format; + + +namespace { + + +/// Wraps an integer in a C++ class. +/// +/// This custom type exists to ensure that we can feed arbitrary objects that +/// support operator<< to the formatter; +class int_wrapper { + /// The wrapped integer. + int _value; + +public: + /// Constructs a new wrapper. + /// + /// \param value_ The value to wrap. + int_wrapper(const int value_) : _value(value_) + { + } + + /// Returns the wrapped value. + /// + /// \return An integer. + int + value(void) const + { + return _value; + } +}; + + +/// Writes a wrapped integer into an output stream. +/// +/// \param output The output stream into which to place the integer. +/// \param wrapper The wrapped integer. +/// +/// \return The output stream. +std::ostream& +operator<<(std::ostream& output, const int_wrapper& wrapper) +{ + return (output << wrapper.value()); +} + + +} // anonymous namespace + + +/// Calls ATF_REQUIRE_EQ on an expected string and a formatter. +/// +/// This is pure syntactic sugar to avoid calling the str() method on all the +/// individual tests below, which results in very long lines that require +/// wrapping and clutter readability. +/// +/// \param expected The expected string generated by the formatter. +/// \param formatter The formatter to test. +#define EQ(expected, formatter) ATF_REQUIRE_EQ(expected, (formatter).str()) + + +ATF_TEST_CASE_WITHOUT_HEAD(no_fields); +ATF_TEST_CASE_BODY(no_fields) +{ + EQ("Plain string", F("Plain string")); +} + + +ATF_TEST_CASE_WITHOUT_HEAD(one_field); +ATF_TEST_CASE_BODY(one_field) +{ + EQ("foo", F("%sfoo") % ""); + EQ(" foo", F("%sfoo") % " "); + EQ("foo ", F("foo %s") % ""); + EQ("foo bar", F("foo %s") % "bar"); + EQ("foo bar baz", F("foo %s baz") % "bar"); + EQ("foo %s %s", F("foo %s %s") % "%s" % "%s"); +} + + +ATF_TEST_CASE_WITHOUT_HEAD(many_fields); +ATF_TEST_CASE_BODY(many_fields) +{ + EQ("", F("%s%s") % "" % ""); + EQ("foo", F("%s%s%s") % "" % "foo" % ""); + EQ("some 5 text", F("%s %s %s") % "some" % 5 % "text"); + EQ("f%s 5 text", F("%s %s %s") % "f%s" % 5 % "text"); +} + + +ATF_TEST_CASE_WITHOUT_HEAD(escape); +ATF_TEST_CASE_BODY(escape) +{ + EQ("%", F("%%")); + EQ("% %", F("%% %%")); + EQ("%% %%", F("%%%% %%%%")); + + EQ("foo %", F("foo %%")); + EQ("foo bar %", F("foo %s %%") % "bar"); + EQ("foo % bar", F("foo %% %s") % "bar"); + + EQ("foo %%", F("foo %s") % "%%"); + EQ("foo a%%b", F("foo a%sb") % "%%"); + EQ("foo a%%b", F("foo %s") % "a%%b"); + + EQ("foo % bar %%", F("foo %% %s %%%%") % "bar"); +} + + +ATF_TEST_CASE_WITHOUT_HEAD(extra_args_error); +ATF_TEST_CASE_BODY(extra_args_error) +{ + using format::extra_args_error; + + ATF_REQUIRE_THROW(extra_args_error, F("foo") % "bar"); + ATF_REQUIRE_THROW(extra_args_error, F("foo %%") % "bar"); + ATF_REQUIRE_THROW(extra_args_error, F("foo %s") % "bar" % "baz"); + ATF_REQUIRE_THROW(extra_args_error, F("foo %s") % "%s" % "bar"); + ATF_REQUIRE_THROW(extra_args_error, F("%s foo %s") % "bar" % "baz" % "foo"); + + try { + F("foo %s %s") % "bar" % "baz" % "something extra"; + fail("extra_args_error not raised"); + } catch (const extra_args_error& e) { + ATF_REQUIRE_EQ("foo %s %s", e.format()); + ATF_REQUIRE_EQ("something extra", e.arg()); + } +} + + +ATF_TEST_CASE_WITHOUT_HEAD(format__class); +ATF_TEST_CASE_BODY(format__class) +{ + EQ("foo bar", F("%s") % std::string("foo bar")); + EQ("3", F("%s") % int_wrapper(3)); +} + + +ATF_TEST_CASE_WITHOUT_HEAD(format__pointer); +ATF_TEST_CASE_BODY(format__pointer) +{ + EQ("0xcafebabe", F("%s") % reinterpret_cast< void* >(0xcafebabe)); + EQ("foo bar", F("%s") % "foo bar"); +} + + +ATF_TEST_CASE_WITHOUT_HEAD(format__bool); +ATF_TEST_CASE_BODY(format__bool) +{ + EQ("true", F("%s") % true); + EQ("false", F("%s") % false); +} + + +ATF_TEST_CASE_WITHOUT_HEAD(format__char); +ATF_TEST_CASE_BODY(format__char) +{ + EQ("Z", F("%s") % 'Z'); +} + + +ATF_TEST_CASE_WITHOUT_HEAD(format__float); +ATF_TEST_CASE_BODY(format__float) +{ + EQ("3", F("%s") % 3.0); + EQ("3.0", F("%.1s") % 3.0); + EQ("3.0", F("%0.1s") % 3.0); + EQ(" 15.600", F("%8.3s") % 15.6); + EQ("01.52", F("%05.2s") % 1.52); +} + + +ATF_TEST_CASE_WITHOUT_HEAD(format__int); +ATF_TEST_CASE_BODY(format__int) +{ + EQ("3", F("%s") % 3); + EQ("3", F("%0s") % 3); + EQ(" -123", F("%5s") % -123); + EQ("00078", F("%05s") % 78); +} + + +ATF_TEST_CASE_WITHOUT_HEAD(format__error); +ATF_TEST_CASE_BODY(format__error) +{ + using format::bad_format_error; + + ATF_REQUIRE_THROW_RE(bad_format_error, "Trailing %", F("%")); + ATF_REQUIRE_THROW_RE(bad_format_error, "Trailing %", F("f%")); + ATF_REQUIRE_THROW_RE(bad_format_error, "Trailing %", F("f%%%")); + ATF_REQUIRE_THROW_RE(bad_format_error, "Trailing %", F("ab %s cd%") % "cd"); + + ATF_REQUIRE_THROW_RE(bad_format_error, "Invalid width", F("%1bs")); + + ATF_REQUIRE_THROW_RE(bad_format_error, "Invalid precision", F("%.s")); + ATF_REQUIRE_THROW_RE(bad_format_error, "Invalid precision", F("%0.s")); + ATF_REQUIRE_THROW_RE(bad_format_error, "Invalid precision", F("%123.s")); + ATF_REQUIRE_THROW_RE(bad_format_error, "Invalid precision", F("%.12bs")); + + ATF_REQUIRE_THROW_RE(bad_format_error, "Unterminated", F("%c") % 'Z'); + ATF_REQUIRE_THROW_RE(bad_format_error, "Unterminated", F("%d") % 5); + ATF_REQUIRE_THROW_RE(bad_format_error, "Unterminated", F("%.1f") % 3); + ATF_REQUIRE_THROW_RE(bad_format_error, "Unterminated", F("%d%s") % 3 % "a"); + + try { + F("foo %s%") % "bar"; + fail("bad_format_error not raised"); + } catch (const bad_format_error& e) { + ATF_REQUIRE_EQ("foo %s%", e.format()); + } +} + + +ATF_INIT_TEST_CASES(tcs) +{ + ATF_ADD_TEST_CASE(tcs, no_fields); + ATF_ADD_TEST_CASE(tcs, one_field); + ATF_ADD_TEST_CASE(tcs, many_fields); + ATF_ADD_TEST_CASE(tcs, escape); + ATF_ADD_TEST_CASE(tcs, extra_args_error); + + ATF_ADD_TEST_CASE(tcs, format__class); + ATF_ADD_TEST_CASE(tcs, format__pointer); + ATF_ADD_TEST_CASE(tcs, format__bool); + ATF_ADD_TEST_CASE(tcs, format__char); + ATF_ADD_TEST_CASE(tcs, format__float); + ATF_ADD_TEST_CASE(tcs, format__int); + ATF_ADD_TEST_CASE(tcs, format__error); +} diff --git a/utils/format/macros.hpp b/utils/format/macros.hpp new file mode 100644 index 000000000000..09ef14ea485e --- /dev/null +++ b/utils/format/macros.hpp @@ -0,0 +1,58 @@ +// Copyright 2010 The Kyua Authors. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of Google Inc. nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +/// \file utils/format/macros.hpp +/// Convenience macros to simplify usage of the format library. +/// +/// This file <em>must not be included from other header files</em>. + +#if !defined(UTILS_FORMAT_MACROS_HPP) +#define UTILS_FORMAT_MACROS_HPP + +// We include the .ipp file instead of .hpp because, after all, macros.hpp +// is provided purely for convenience and must not be included from other +// header files. Henceforth, we make things easier to the callers. +#include "utils/format/formatter.ipp" + + +/// Constructs a utils::format::formatter object with the given format string. +/// +/// This macro is just a wrapper to make the construction of +/// utils::format::formatter objects shorter, and thus to allow inlining these +/// calls right in where formatted strings are required. A typical usage would +/// look like: +/// +/// \code +/// std::cout << F("%s %d\n") % my_str % my_int; +/// \endcode +/// +/// \param fmt The format string. +#define F(fmt) utils::format::formatter(fmt) + + +#endif // !defined(UTILS_FORMAT_MACROS_HPP) |