From b21ee06230adf8b9f4090eef7b46c179f8e26d42 Mon Sep 17 00:00:00 2001 From: Mike Fleetwood Date: Fri, 12 May 2017 22:00:08 +0100 Subject: [PATCH] Add initial unit tests for PipeCapture (#777973) So far just tests sending 0 bytes and a few ASCII bytes into a pipe and that they are read correctly by the PipeCapture class. Bug 777973 - Segmentation fault on bad disk --- .gitignore | 1 + tests/Makefile.am | 4 +- tests/test_PipeCapture.cc | 161 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 165 insertions(+), 1 deletion(-) create mode 100644 tests/test_PipeCapture.cc diff --git a/.gitignore b/.gitignore index 0b30ece4..a8e339b9 100644 --- a/.gitignore +++ b/.gitignore @@ -50,5 +50,6 @@ tests/*.log tests/*.trs tests/test-suite.log tests/test_BlockSpecial +tests/test_PipeCapture tests/test_dummy xmldocs.make diff --git a/tests/Makefile.am b/tests/Makefile.am index 07b01997..8fb91f3b 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -12,10 +12,12 @@ LDADD = \ # Programs to be built by "make check" check_PROGRAMS = \ test_dummy \ - test_BlockSpecial + test_BlockSpecial \ + test_PipeCapture # Test cases to be run by "make check" TESTS = $(check_PROGRAMS) test_dummy_SOURCES = test_dummy.cc test_BlockSpecial_SOURCES = test_BlockSpecial.cc ../src/BlockSpecial.cc +test_PipeCapture_SOURCES = test_PipeCapture.cc ../src/PipeCapture.cc diff --git a/tests/test_PipeCapture.cc b/tests/test_PipeCapture.cc new file mode 100644 index 00000000..b77369ca --- /dev/null +++ b/tests/test_PipeCapture.cc @@ -0,0 +1,161 @@ +/* Copyright (C) 2017 Mike Fleetwood + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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, see . + */ + +/* Test PipeCapture + * + * All the tests work by creating a pipe(3) and using a separate thread to write data into + * the pipe with PipeCapture running in the initial thread. Captured data is then checked + * that it matches the input. + */ + +#include "PipeCapture.h" +#include "gtest/gtest.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace GParted +{ + +// Explicit test fixture class with common variables and methods used in each test. +// Reference: +// Google Test, Primer, Test Fixtures: Using the Same Data Configuration for Multiple Tests +class PipeCaptureTest : public ::testing::Test +{ +protected: + PipeCaptureTest() : eof_signalled( false ) {}; + + virtual void SetUp(); + virtual void TearDown(); + + static gboolean main_loop_quit( gpointer data ); + void writer_thread( const std::string & str ); + void run_writer_thread(); + + static const size_t ReaderFD = 0; + static const size_t WriterFD = 1; + + std::string inputstr; + Glib::ustring capturedstr; + bool eof_signalled; + int pipefds[2]; + Glib::RefPtr glib_main_loop; + +public: + void eof_callback() { eof_signalled = true; }; +}; + +// Further setup PipeCaptureTest fixture before running each test. Create pipe and Glib +// main loop object. +void PipeCaptureTest::SetUp() +{ + ASSERT_TRUE( pipe( pipefds ) == 0 ) << "Failed to create pipe. errno=" + << errno << "," << strerror( errno ); + glib_main_loop = Glib::MainLoop::create(); +} + +// Tear down fixture after running each test. Close reading end of the pipe. Also +// re-closed the writing end of the pipe, just in case something went wrong in the test. +void PipeCaptureTest::TearDown() +{ + ASSERT_TRUE( close( pipefds[ReaderFD] ) == 0 ) << "Failed to close reading end of pipe. errno=" + << errno << "," << strerror( errno ); + close( pipefds[WriterFD] ); +} + +// Callback used to end the currently running Glib main loop. +gboolean PipeCaptureTest::main_loop_quit( gpointer data ) +{ + static_cast( data )->glib_main_loop->quit(); + return false; // One shot g_idle_add() callback +} + +// Write the string into the pipe and close the pipe for writing. Registers callback to +// end the currently running Glib main loop. +void PipeCaptureTest::writer_thread( const std::string & str ) +{ + const size_t BlockSize = 4096; + const char * writebuf = str.data(); + size_t remaining_size = str.length(); + while ( remaining_size > 0 ) + { + size_t write_size = ( remaining_size > BlockSize ) ? BlockSize : remaining_size; + ssize_t written = write( pipefds[WriterFD], writebuf, write_size ); + if ( written <= 0 ) + { + ADD_FAILURE() << __func__ << "(): Failed to write to pipe. errno=" + << errno << "," << strerror( errno ); + break; + } + remaining_size -= written; + writebuf += written; + } + ASSERT_TRUE( close( pipefds[WriterFD] ) == 0 ) << "Failed to close writing end of pipe. errno=" + << errno << "," << strerror( errno ); + g_idle_add( main_loop_quit, this ); +} + +// Create writer thread and run the Glib main loop. +void PipeCaptureTest::run_writer_thread() +{ + Glib::Thread::create( sigc::bind( sigc::mem_fun( *this, &PipeCaptureTest::writer_thread ), + inputstr ), + false ); + glib_main_loop->run(); +} + +TEST_F( PipeCaptureTest, EmptyPipe ) +{ + // Test capturing 0 bytes with no on EOF callback registered. + inputstr = ""; + PipeCapture pc( pipefds[ReaderFD], capturedstr ); + pc.connect_signal(); + run_writer_thread(); + EXPECT_STREQ( inputstr.c_str(), capturedstr.c_str() ); + EXPECT_FALSE( eof_signalled ); +} + +TEST_F( PipeCaptureTest, EmptyPipeWithEOF ) +{ + // Test capturing 0 bytes and registered on EOF callback occurs. + inputstr = ""; + PipeCapture pc( pipefds[ReaderFD], capturedstr ); + pc.signal_eof.connect( sigc::mem_fun( *this, &PipeCaptureTest::eof_callback ) ); + pc.connect_signal(); + run_writer_thread(); + EXPECT_STREQ( inputstr.c_str(), capturedstr.c_str() ); + EXPECT_TRUE( eof_signalled ); +} + +TEST_F( PipeCaptureTest, ShortASCIIText ) +{ + // Test capturing small amount of ASCII text. + inputstr = "The quick brown fox jumps over the lazy dog"; + PipeCapture pc( pipefds[ReaderFD], capturedstr ); + pc.signal_eof.connect( sigc::mem_fun( *this, &PipeCaptureTest::eof_callback ) ); + pc.connect_signal(); + run_writer_thread(); + EXPECT_STREQ( inputstr.c_str(), capturedstr.c_str() ); + EXPECT_TRUE( eof_signalled ); +} + +} // namespace GParted