lmi-commits
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[lmi-commits] [lmi] master 440c67d 2/6: Add a null_stream unit test


From: Greg Chicares
Subject: [lmi-commits] [lmi] master 440c67d 2/6: Add a null_stream unit test
Date: Thu, 5 Aug 2021 17:09:59 -0400 (EDT)

branch: master
commit 440c67d12164bedc1f1c4603246178c99cd068cd
Author: Gregory W. Chicares <gchicares@sbcglobal.net>
Commit: Gregory W. Chicares <gchicares@sbcglobal.net>

    Add a null_stream unit test
    
    Using a std::stringstream with badbit set, or a std::fstream with no
    associated file (whether or not badbit is set), is fast and simple.
    Techniques that set badbit aren't appropriate for code that tests
    operations to make sure they succeed, as they will instead fail.
    Another problem with all these techniques is that they might not
    work: Kühl noted in 1999 that "some implementations have minor bugs
    and do not always consider the flags correctly", and there were still
    defects many years later--see, e.g., the comment to this article:
      https://stackoverflow.com/a/8244052
    that refers to this defect addressed only in 2017:
      https://gcc.gnu.org/bugzilla/show_bug.cgi?id=53984
    
    The null-stream technique, derived from Dietmar Kühl's work, is much
    slower because of its construction overhead; but it's as fast as the
    others if it's constructed OAOO. (The OAOO approach currently used by
    lmi happens to set badbit, which therefore seems not to be a problem
    in practice after all.)
    
    The /dev/null technique is too slow.
---
 Makefile.am          |   4 ++
 null_stream_test.cpp | 172 +++++++++++++++++++++++++++++++++++++++++++++++++++
 objects.make         |   7 +++
 3 files changed, 183 insertions(+)

diff --git a/Makefile.am b/Makefile.am
index cc24612..24c87a1 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -140,6 +140,7 @@ TESTS = \
     mortality_rates_test \
     name_value_pairs_test \
     ncnnnpnn_test \
+    null_stream_test \
     numeric_io_test \
     path_utility_test \
     premium_tax_test \
@@ -941,6 +942,9 @@ name_value_pairs_test_LDADD = \
 ncnnnpnn_test_LDADD = \
   libtest_common.la
 
+null_stream_test_LDADD = \
+  libtest_common.la
+
 numeric_io_test_LDADD = \
   libtest_common.la \
   $(BOOST_LIBS)
diff --git a/null_stream_test.cpp b/null_stream_test.cpp
new file mode 100644
index 0000000..37cbb80
--- /dev/null
+++ b/null_stream_test.cpp
@@ -0,0 +1,172 @@
+// Stream and stream buffer that discard output--unit test.
+//
+// Copyright (C) 2021 Gregory W. Chicares.
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License version 2 as
+// published by the Free Software Foundation.
+//
+// This program 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, write to the Free Software Foundation,
+// Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+//
+// https://savannah.nongnu.org/projects/lmi
+// email: <gchicares@sbcglobal.net>
+// snail: Chicares, 186 Belle Woods Drive, Glastonbury CT 06033, USA
+
+#include "pchfile.hpp"
+
+#include "null_stream.hpp"
+
+#include "miscellany.hpp"               // ios_out_app_binary()
+#include "test_tools.hpp"
+#include "timer.hpp"
+
+#include <fstream>
+#include <iomanip>
+#include <sstream>
+
+void emit_text_to_stream(std::ostream& os)
+{
+    for(int i = 0; i < 10; ++i)
+        {
+        os
+            << "The Beaver had counted with scrupulous care,\n"
+            << "  Attending to every word:\n"
+            << "But it fairly lost heart, and outgrabe in despair,\n"
+            << "  When the third repetition occurred.\n"
+            << std::flush
+            ;
+        os
+            << std::setprecision(21)
+            << std::setw(12) << 3.14159
+            << std::fixed
+            << std::hex
+            << std::hexfloat
+            << std::setw(12) << 3.14159
+            << std::endl
+            ;
+        }
+}
+
+/// Test writing to '/dev/null'.
+///
+/// Not every OS has '/dev/null', but for msw, using 'NUL' doesn't
+/// make this any faster.
+
+void mete_dev_null()
+{
+    for(int i = 0; i < 1e4; ++i)
+        {
+        std::ofstream ofs("/dev/null", ios_out_app_binary());
+        emit_text_to_stream(ofs);
+        }
+}
+
+// Test writing to null stream derived from Dietmar Kühl's work.
+
+void mete_kuehl()
+{
+    for(int i = 0; i < 1e4; ++i)
+        {
+        std::ostream os(&null_streambuf());
+        emit_text_to_stream(os);
+        }
+}
+
+// Test writing to static null stream.
+
+void mete_kuehl_static()
+{
+    for(int i = 0; i < 1e4; ++i)
+        {
+        emit_text_to_stream(null_stream());
+        }
+}
+
+/// Test writing to an unopened ofstream.
+
+void mete_unopened_fstream()
+{
+    for(int i = 0; i < 1e4; ++i)
+        {
+        std::ofstream ofs;
+        emit_text_to_stream(ofs);
+        }
+}
+
+/// Test writing to an ofstream with 'badbit' set.
+
+void mete_badbit_fstream()
+{
+    for(int i = 0; i < 1e4; ++i)
+        {
+        std::ofstream ofs;
+        ofs.setstate(std::ios_base::badbit);
+        emit_text_to_stream(ofs);
+        }
+}
+
+/// Test writing to an ofstream with 'badbit' set.
+
+void mete_badbit_sstream()
+{
+    for(int i = 0; i < 1e4; ++i)
+        {
+        std::ostringstream oss;
+        oss.setstate(std::ios_base::badbit);
+        emit_text_to_stream(oss);
+        }
+}
+
+void test_fundamentals()
+{
+    // This shouldn't appear anywhere. Of course, there's no way to
+    // verify its absence everywhere.
+    std::ostream& os0 = null_stream();
+    os0
+        << "'But oh, beamish nephew, beware of the day,\n"
+        << "  If your Snark be a Boojum! For then\n"
+        << "You will softly and suddenly vanish away,\n"
+        << "  and never be met with again!'\n"
+        << std::flush
+        ;
+
+    // This alternative explicitly constructs a std::ostream each time
+    // it's used, which is costlier.
+    std::ostream os1(&null_streambuf());
+    os1
+        << "But if ever I meet with a Boojum, that day,\n"
+        << "  In a moment (of this I am sure),\n"
+        << "I shall softly and suddenly vanish away--\n"
+        << "  And the notion I cannot endure!\n"
+        << std::flush
+        ;
+}
+
+void assay_speed()
+{
+    std::cout
+        << "\n  Speed tests..."
+        << "\n  /dev/null        : " << TimeAnAliquot(mete_dev_null)
+        << "\n  Kühl             : " << TimeAnAliquot(mete_kuehl)
+        << "\n  Kühl, static     : " << TimeAnAliquot(mete_kuehl_static)
+        << "\n  unopened fstream : " << TimeAnAliquot(mete_unopened_fstream)
+        << "\n  fstream, badbit  : " << TimeAnAliquot(mete_badbit_fstream)
+        << "\n  sstream, badbit  : " << TimeAnAliquot(mete_badbit_sstream)
+        << std::endl
+        ;
+}
+
+int test_main(int, char*[])
+{
+    test_fundamentals();
+    assay_speed();
+
+    return 0;
+}
diff --git a/objects.make b/objects.make
index 2621716..6f5bd85 100644
--- a/objects.make
+++ b/objects.make
@@ -433,6 +433,7 @@ unit_test_targets := \
   mortality_rates_test \
   name_value_pairs_test \
   ncnnnpnn_test \
+  null_stream_test \
   numeric_io_test \
   path_utility_test \
   premium_tax_test \
@@ -861,6 +862,12 @@ ncnnnpnn_test$(EXEEXT): \
   $(common_test_objects) \
   ncnnnpnn_test.o \
 
+null_stream_test$(EXEEXT): \
+  $(common_test_objects) \
+  null_stream.o \
+  null_stream_test.o \
+  timer.o \
+
 numeric_io_test$(EXEEXT): \
   $(common_test_objects) \
   calendar_date.o \



reply via email to

[Prev in Thread] Current Thread [Next in Thread]