master - test: Implement journalling and --continue.
by Petr Rockai
Gitweb: http://git.fedorahosted.org/git/?p=lvm2.git;a=commitdiff;h=d2dbc65f00abe5...
Commit: d2dbc65f00abe5ccf44af3545a1ec1ddd5ebdad7
Parent: 9dfbce0aa72735048a788dd23f91f76fbc21dd23
Author: Petr Rockai <prockai(a)redhat.com>
AuthorDate: Thu Jun 26 01:02:13 2014 +0200
Committer: Petr Rockai <prockai(a)redhat.com>
CommitterDate: Thu Feb 5 13:47:16 2015 +0100
test: Implement journalling and --continue.
---
test/lib/journal.h | 65 +++++++++++++++++++++++++++++++++++++++++++++++++++
test/lib/runner.cpp | 36 +++++++++++++++++++++-------
2 files changed, 92 insertions(+), 9 deletions(-)
diff --git a/test/lib/journal.h b/test/lib/journal.h
index ff662f3..6d0e243 100644
--- a/test/lib/journal.h
+++ b/test/lib/journal.h
@@ -1,7 +1,10 @@
// -*- C++ -*-
#include <map>
+#include <string>
#include <iostream>
+#include <fstream>
+#include <iterator>
#ifndef RUNNER_JOURNAL_H
#define RUNNER_JOURNAL_H
@@ -9,6 +12,7 @@
struct Journal {
enum R {
STARTED,
+ RETRIED,
UNKNOWN,
FAILED,
INTERRUPTED,
@@ -32,9 +36,59 @@ struct Journal {
}
}
+ friend std::istream &operator>>( std::istream &i, R &r ) {
+ std::string x;
+ i >> x;
+
+ r = UNKNOWN;
+ if ( x == "started" ) r = STARTED;
+ if ( x == "retried" ) r = RETRIED;
+ if ( x == "failed" ) r = FAILED;
+ if ( x == "interrupted" ) r = INTERRUPTED;
+ if ( x == "passed" ) r = PASSED;
+ if ( x == "skipped" ) r = SKIPPED;
+ if ( x == "timeout" ) r = TIMEOUT;
+ if ( x == "warnings" ) r = WARNED;
+ return i;
+ }
+
+ template< typename S, typename T >
+ friend std::istream &operator>>( std::istream &i, std::pair< S, T > &r ) {
+ return i >> r.first >> r.second;
+ }
+
typedef std::map< std::string, R > Status;
Status status;
+ std::string location_tmp, location;
+
+ void sync() {
+ std::ofstream of( location_tmp.c_str() );
+ for ( Status::iterator i = status.begin(); i != status.end(); ++i )
+ of << i->first << " " << i->second << std::endl;
+ of.close();
+ rename( location_tmp.c_str(), location.c_str() );
+ }
+
+ void started( std::string n ) {
+ if ( status.count( n ) && status[ n ] == STARTED )
+ status[ n ] = RETRIED;
+ else
+ status[ n ] = STARTED;
+ sync();
+ }
+
+ void done( std::string n, R r ) {
+ status[ n ] = r;
+ sync();
+ }
+
+ bool done( std::string n ) {
+ if ( !status.count( n ) )
+ return false;
+ return status[ n ] != STARTED && status[ n ] != INTERRUPTED;
+ }
+
int count( R r ) {
int c = 0;
for ( Status::iterator i = status.begin(); i != status.end(); ++i )
@@ -53,6 +107,17 @@ struct Journal {
if ( i->second != PASSED )
std::cout << i->second << ": " << i->first << std::endl;
}
+
+ void read() {
+ std::ifstream ifs( location.c_str() );
+ typedef std::istream_iterator< std::pair< std::string, R > > It;
+ std::copy( It( ifs ), It(), std::inserter( status, status.begin() ) );
+ }
+
+ Journal( std::string dir )
+ : location( dir + "/journal" ),
+ location_tmp( dir + "/journal.tmp" )
+ {}
};
#endif
diff --git a/test/lib/runner.cpp b/test/lib/runner.cpp
index 4180de3..0875b02 100644
--- a/test/lib/runner.cpp
+++ b/test/lib/runner.cpp
@@ -55,11 +55,12 @@
pid_t kill_pid = 0;
bool fatal_signal = false;
+bool interrupt = false;
struct Options {
- bool verbose, quiet, interactive;
+ bool verbose, quiet, interactive, cont;
std::string testdir, outdir;
- Options() : verbose( false ), quiet( false ), interactive( false ) {}
+ Options() : verbose( false ), quiet( false ), interactive( false ), cont( false ) {}
};
struct TestProcess
@@ -115,6 +116,8 @@ struct TestCase {
time_t start, end;
Options options;
+ Journal *journal;
+
void pipe() {
int fds[2];
@@ -175,7 +178,7 @@ struct TestCase {
}
std::string tag( std::string n ) {
- int pad = (8 - n.length());
+ int pad = (12 - n.length());
return "### " + std::string( pad, ' ' ) + n + ": ";
}
@@ -217,7 +220,9 @@ struct TestCase {
r = Journal::SKIPPED;
else
r = Journal::FAILED;
- } else
+ } else if ( interrupt && WIFSIGNALED( status ) && WTERMSIG( status ) == SIGINT )
+ r = Journal::INTERRUPTED;
+ else
r = Journal::FAILED;
::close( io.fd );
@@ -228,6 +233,7 @@ struct TestCase {
close(fd_debuglog);
} */
+ journal->done( name, r );
progress( Last ) << tag( r ) << name << std::endl;
}
@@ -241,6 +247,7 @@ struct TestCase {
io.close();
child.exec();
} else {
+ journal->started( name );
progress( First ) << tag( "running" ) << name << std::flush;
if ( options.verbose || options.interactive )
progress() << std::endl;
@@ -249,8 +256,8 @@ struct TestCase {
}
}
- TestCase( Options opt, std::string path, std::string name )
- : timeout( false ), silent_ctr( 0 ), child( path ), name( name ), options( opt )
+ TestCase( Journal &j, Options opt, std::string path, std::string name )
+ : timeout( false ), silent_ctr( 0 ), child( path ), name( name ), options( opt ), journal( &j )
{
if ( opt.verbose )
io.sinks.push_back( new FdSink( 1 ) );
@@ -276,10 +283,12 @@ struct Main {
continue;
if ( i->substr( 0, 4 ) == "lib/" )
continue;
- cases.push_back( TestCase( options, options.testdir + *i, *i ) );
+ cases.push_back( TestCase( journal, options, options.testdir + *i, *i ) );
cases.back().options = options;
}
+ if ( options.cont )
+ journal.read();
}
void run() {
@@ -288,6 +297,10 @@ struct Main {
std::cerr << "running " << cases.size() << " tests" << std::endl;
for ( Cases::iterator i = cases.begin(); i != cases.end(); ++i ) {
+
+ if ( options.cont && journal.done( i->name ) )
+ continue;
+
i->run();
if ( time(0) - start > 3 * 3600 ) {
@@ -304,14 +317,16 @@ struct Main {
exit( 1 );
}
- Main( Options o ) : die( false ), options( o ) {}
+ Main( Options o ) : die( false ), options( o ), journal( o.outdir ) {}
};
static void handler( int sig ) {
- signal( sig, SIG_DFL );
+ signal( sig, SIG_DFL ); /* die right away next time */
if ( kill_pid > 0 )
kill( -kill_pid, sig );
fatal_signal = true;
+ if ( sig == SIGINT )
+ interrupt = true;
}
void setup_handlers() {
@@ -378,6 +393,9 @@ int main(int argc, char **argv)
Args args( argc, argv );
Options opt;
+ if ( args.has( "--continue" ) )
+ opt.cont = true;
+
if ( args.has( "--quiet" ) || getenv( "QUIET" ) ) {
opt.verbose = false;
opt.quiet = true;
9 years, 2 months
master - test: Split up the new runner into multiple files.
by Petr Rockai
Gitweb: http://git.fedorahosted.org/git/?p=lvm2.git;a=commitdiff;h=9dfbce0aa72735...
Commit: 9dfbce0aa72735048a788dd23f91f76fbc21dd23
Parent: b726d6688260e1e1e98500809eee014b7ceb92b5
Author: Petr Rockai <prockai(a)redhat.com>
AuthorDate: Wed Jun 25 21:34:34 2014 +0200
Committer: Petr Rockai <prockai(a)redhat.com>
CommitterDate: Thu Feb 5 13:47:16 2015 +0100
test: Split up the new runner into multiple files.
---
test/Makefile.in | 4 +-
test/lib/filesystem.h | 86 ++++++++++++++++
test/lib/io.h | 211 +++++++++++++++++++++++++++++++++++++++
test/lib/journal.h | 58 +++++++++++
test/lib/runner.cpp | 265 ++++++++-----------------------------------------
test/lib/util.h | 39 +++++++
6 files changed, 437 insertions(+), 226 deletions(-)
diff --git a/test/Makefile.in b/test/Makefile.in
index c88d07a..88644f3 100644
--- a/test/Makefile.in
+++ b/test/Makefile.in
@@ -38,7 +38,6 @@ export LVM_TEST_CACHE_RESTORE_CMD?=@CACHE_RESTORE_CMD@
SUBDIRS = api unit
SOURCES = lib/not.c lib/harness.c
CXXSOURCES = lib/runner.cpp
-CXXFLAGS = -std=c++11
include $(top_builddir)/make.tmpl
@@ -137,10 +136,11 @@ lib/invalid: lib/not
lib/fail: lib/not
ln -sf not lib/fail
-
lib/runner: lib/runner.o .lib-dir-stamp
$(CXX) $(LDFLAGS) -o $@ $<
+lib/runner.o: $(wildcard $(srcdir)/lib/*.h)
+
lib/%: lib/%.o .lib-dir-stamp
$(CC) $(LDFLAGS) -o $@ $<
diff --git a/test/lib/filesystem.h b/test/lib/filesystem.h
new file mode 100644
index 0000000..718c433
--- /dev/null
+++ b/test/lib/filesystem.h
@@ -0,0 +1,86 @@
+/* -*- C++ -*- copyright (c) 2014 Red Hat, Inc.
+ *
+ * This file is part of LVM2.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. 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.
+ *
+ * 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 HOLDER 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 "util.h"
+
+#include <vector>
+#include <string>
+
+#include <dirent.h>
+#include <sys/stat.h>
+
+#ifndef RUNNER_FILESYSTEM_H
+#define RUNNER_FILESYSTEM_H
+
+struct dir {
+ DIR *d;
+ dir( std::string p ) {
+ d = opendir( p.c_str() );
+ if ( !d )
+ throw syserr( "error opening directory", p );
+ }
+ ~dir() { closedir( d ); }
+};
+
+typedef std::vector< std::string > Listing;
+
+inline Listing listdir( std::string p, bool recurse = false, std::string prefix = "" )
+{
+ Listing r;
+
+ dir d( p );
+ struct dirent entry, *iter = 0;
+ int readerr;
+
+ while ( (readerr = readdir_r( d.d, &entry, &iter )) == 0 && iter ) {
+ std::string ename( entry.d_name );
+
+ if ( ename == "." || ename == ".." )
+ continue;
+
+ if ( recurse ) {
+ struct stat64 stat;
+ std::string s = p + "/" + ename;
+ if ( ::stat64( s.c_str(), &stat ) == -1 )
+ continue;
+ if ( S_ISDIR(stat.st_mode) ) {
+ Listing sl = listdir( s, true, prefix + ename + "/" );
+ for ( Listing::iterator i = sl.begin(); i != sl.end(); ++i )
+ r.push_back( prefix + *i );
+ } else
+ r.push_back( prefix + ename );
+ } else
+ r.push_back( ename );
+ };
+
+ if ( readerr != 0 )
+ throw syserr( "error reading directory", p );
+
+ return r;
+}
+
+#endif
diff --git a/test/lib/io.h b/test/lib/io.h
new file mode 100644
index 0000000..8ade281
--- /dev/null
+++ b/test/lib/io.h
@@ -0,0 +1,211 @@
+/* -*- C++ -*- copyright (c) 2014 Red Hat, Inc.
+ *
+ * This file is part of LVM2.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. 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.
+ *
+ * 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 HOLDER 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 "util.h"
+
+#include <deque>
+#include <map>
+#include <vector>
+#include <string>
+#include <cstdio>
+#include <cassert>
+#include <algorithm>
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include <sys/klog.h>
+
+#include <iostream>
+
+#ifndef RUNNER_IO_H
+#define RUNNER_IO_H
+
+#define SYSLOG_ACTION_READ_CLEAR 4
+#define SYSLOG_ACTION_CLEAR 5
+
+struct Sink {
+ virtual void outline( bool ) {}
+ virtual void push( std::string x ) = 0;
+ virtual void sync() {}
+};
+
+struct FdSink : Sink {
+ int fd;
+
+ typedef std::deque< char > Stream;
+ typedef std::map< std::string, std::string > Subst;
+
+ Stream stream;
+ Subst subst;
+ bool killed;
+
+ virtual void outline( bool force )
+ {
+ Stream::iterator nl = std::find( stream.begin(), stream.end(), '\n' );
+ if ( nl == stream.end() ) {
+ if ( !force )
+ return;
+ } else
+ force = false;
+
+ assert( nl != stream.end() || force );
+
+ std::string line( stream.begin(), nl );
+ stream.erase( stream.begin(), force ? nl : nl + 1 );
+
+ if ( std::string( line, 0, 9 ) == "@TESTDIR=" )
+ subst[ "@TESTDIR@" ] = std::string( line, 9, std::string::npos );
+ else if ( std::string( line, 0, 8 ) == "@PREFIX=" )
+ subst[ "@PREFIX@" ] = std::string( line, 8, std::string::npos );
+ else {
+ int off;
+ for ( Subst::iterator s = subst.begin(); s != subst.end(); ++s )
+ while ( (off = line.find( s->first )) != std::string::npos )
+ line.replace( off, s->first.length(), s->second );
+ write( fd, line.c_str(), line.length() );
+ if ( !force )
+ write( fd, "\n", 1 );
+ }
+ }
+
+ virtual void sync() {
+ while ( !stream.empty() )
+ outline( true );
+ }
+
+ virtual void push( std::string x ) {
+ if ( !killed )
+ std::copy( x.begin(), x.end(), std::back_inserter( stream ) );
+ }
+
+ FdSink( int _fd ) : fd( _fd ), killed( false ) {}
+};
+
+struct Observer : Sink {
+ Observer() {}
+ void push( std::string ) {}
+};
+
+struct KMsg {
+ int fd;
+ char buf[ 4096 ];
+
+ bool dev_kmsg() {
+ return fd >= 0;
+ }
+
+ void reset() {
+ int sz;
+
+ if ( dev_kmsg() ) {
+ if ( (fd = open("/dev/kmsg", O_RDONLY | O_NONBLOCK)) < 0 ) {
+ if (errno != ENOENT) /* Older kernels (<3.5) do not support /dev/kmsg */
+ perror("opening /dev/kmsg");
+ } else if (lseek(fd, 0L, SEEK_END) == (off_t) -1)
+ perror("lseek /dev/kmsg");
+ } else
+ klogctl( SYSLOG_ACTION_CLEAR, 0, 0 );
+ }
+
+ void read( Sink *s ) {
+ int sz;
+
+ if ( dev_kmsg() ) {
+ while ( (sz = ::read(fd, buf, sizeof(buf) - 1)) > 0 )
+ s->push( std::string( buf, sz ) );
+ if ( sz < 0 ) {
+ fd = -1;
+ read( s );
+ }
+ } else {
+ sz = klogctl( SYSLOG_ACTION_READ_CLEAR, buf, sizeof(buf) - 1 );
+ if ( sz > 0 )
+ s->push( std::string( buf, sz ) );
+ }
+ }
+
+ KMsg() : fd( -1 ) {}
+};
+
+struct IO : Sink {
+ typedef std::vector< Sink* > Sinks;
+ mutable Sinks sinks;
+ Observer *_observer;
+
+ KMsg kmsg;
+ int fd;
+ char buf[ 4096 ];
+
+ virtual void push( std::string x ) {
+ for ( Sinks::iterator i = sinks.begin(); i != sinks.end(); ++i )
+ (*i)->push( x );
+ }
+
+ void sync() {
+ ssize_t sz;
+ while ( (sz = read(fd, buf, sizeof(buf) - 1)) > 0 )
+ push( std::string( buf, sz ) );
+
+ if ( sz < 0 && errno != EAGAIN )
+ throw syserr( "reading pipe" );
+
+ kmsg.read( this );
+
+ for ( Sinks::iterator i = sinks.begin(); i != sinks.end(); ++i )
+ (*i)->sync();
+ }
+
+ void close() { ::close( fd ); }
+ Observer &observer() { return *_observer; }
+
+ IO() : fd( -1 ) {
+ sinks.push_back( _observer = new Observer );
+ }
+
+ IO( const IO &io ) {
+ fd = io.fd;
+ sinks = io.sinks;
+ io.sinks.clear();
+ }
+
+ IO &operator= ( const IO &io ) {
+ fd = io.fd;
+ sinks = io.sinks;
+ io.sinks.clear();
+ return *this;
+ }
+
+ ~IO() {
+ for ( Sinks::iterator i = sinks.begin(); i != sinks.end(); ++i )
+ delete *i;
+ }
+
+};
+
+#endif
diff --git a/test/lib/journal.h b/test/lib/journal.h
new file mode 100644
index 0000000..ff662f3
--- /dev/null
+++ b/test/lib/journal.h
@@ -0,0 +1,58 @@
+// -*- C++ -*-
+
+#include <map>
+#include <iostream>
+
+#ifndef RUNNER_JOURNAL_H
+#define RUNNER_JOURNAL_H
+
+struct Journal {
+ enum R {
+ STARTED,
+ UNKNOWN,
+ FAILED,
+ INTERRUPTED,
+ KNOWNFAIL,
+ PASSED,
+ SKIPPED,
+ TIMEOUT,
+ WARNED,
+ };
+
+ friend std::ostream &operator<<( std::ostream &o, R r ) {
+ switch ( r ) {
+ case STARTED: return o << "started";
+ case FAILED: return o << "failed";
+ case INTERRUPTED: return o << "interrupted";
+ case PASSED: return o << "passed";
+ case SKIPPED: return o << "skipped";
+ case TIMEOUT: return o << "timeout";
+ case WARNED: return o << "warnings";
+ default: return o << "unknown";
+ }
+ }
+
+ typedef std::map< std::string, R > Status;
+ Status status;
+
+ int count( R r ) {
+ int c = 0;
+ for ( Status::iterator i = status.begin(); i != status.end(); ++i )
+ if ( i->second == r )
+ ++ c;
+ return c;
+ }
+
+ void banner() {
+ std::cout << std::endl << "### " << status.size() << " tests: "
+ << count( PASSED ) << " passed" << std::endl;
+ }
+
+ void details() {
+ for ( Status::iterator i = status.begin(); i != status.end(); ++i )
+ if ( i->second != PASSED )
+ std::cout << i->second << ": " << i->first << std::endl;
+ }
+};
+
+#endif
diff --git a/test/lib/runner.cpp b/test/lib/runner.cpp
index bcc98ba..4180de3 100644
--- a/test/lib/runner.cpp
+++ b/test/lib/runner.cpp
@@ -1,24 +1,40 @@
-/*
- * Copyright (C) 2010-2014 Red Hat, Inc. All rights reserved.
+/* -*- C++ -*- copyright (c) 2014 Red Hat, Inc.
*
* This file is part of LVM2.
*
- * This copyrighted material is made available to anyone wishing to use,
- * modify, copy, or redistribute it subject to the terms and conditions
- * of the GNU General Public License v.2.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
*
- * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. 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.
+ *
+ * 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 HOLDER 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 "io.h"
+#include "journal.h"
+#include "filesystem.h"
+
#include <iostream>
#include <vector>
#include <deque>
#include <map>
#include <sstream>
#include <cassert>
-#include <system_error>
#include <algorithm>
#include <fcntl.h>
@@ -29,7 +45,6 @@
#include <sys/resource.h> /* rusage */
#include <sys/select.h>
#include <sys/socket.h>
-#include <sys/stat.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/wait.h>
@@ -37,226 +52,21 @@
#include <time.h>
#include <unistd.h>
#include <stdint.h>
-#include <dirent.h>
-
-#define SYSLOG_ACTION_READ_CLEAR 4
-#define SYSLOG_ACTION_CLEAR 5
pid_t kill_pid = 0;
bool fatal_signal = false;
-std::system_error syserr( std::string msg, std::string ctx = "" ) {
- return std::system_error( errno, std::generic_category(), msg + " " + ctx );
-}
-
-struct dir {
- DIR *d;
- dir( std::string p ) {
- d = opendir( p.c_str() );
- if ( !d )
- throw syserr( "error opening directory", p );
- }
- ~dir() { closedir( d ); }
-};
-
-typedef std::vector< std::string > Listing;
-
-Listing listdir( std::string p, bool recurse = false, std::string prefix = "" ) {
- Listing r;
-
- dir d( p );
- struct dirent entry, *iter = 0;
- int readerr;
-
- while ( (readerr = readdir_r( d.d, &entry, &iter )) == 0 && iter ) {
- std::string ename( entry.d_name );
-
- if ( ename == "." || ename == ".." )
- continue;
-
- if ( recurse ) {
- struct stat64 stat;
- std::string s = p + "/" + ename;
- if ( ::stat64( s.c_str(), &stat ) == -1 )
- throw syserr( "stat error", s );
- if ( S_ISDIR(stat.st_mode) ) {
- Listing sl = listdir( s, true, prefix + ename + "/" );
- for ( Listing::iterator i = sl.begin(); i != sl.end(); ++i )
- r.push_back( prefix + *i );
- } else
- r.push_back( prefix + ename );
- } else
- r.push_back( ename );
- };
-
- if ( readerr != 0 )
- throw syserr( "error reading directory", p );
-
- return r;
-}
-
struct Options {
bool verbose, quiet, interactive;
std::string testdir, outdir;
-};
-
-struct Journal {
- enum R {
- STARTED,
- UNKNOWN,
- FAILED,
- INTERRUPTED,
- KNOWNFAIL,
- PASSED,
- SKIPPED,
- TIMEOUT,
- WARNED,
- };
-
- friend std::ostream &operator<<( std::ostream &o, R r ) {
- switch ( r ) {
- case STARTED: return o << "started";
- case FAILED: return o << "failed";
- case INTERRUPTED: return o << "interrupted";
- case PASSED: return o << "passed";
- case SKIPPED: return o << "skipped";
- case TIMEOUT: return o << "timeout";
- case WARNED: return o << "warnings";
- default: return o << "unknown";
- }
- }
-
- typedef std::map< std::string, R > Status;
- Status status;
-
- int count( R r ) {
- int c = 0;
- for ( Status::iterator i = status.begin(); i != status.end(); ++i )
- if ( i->second == r )
- ++ c;
- return c;
- }
-
- void banner() {
- std::cout << std::endl << "### " << status.size() << " tests: "
- << count( PASSED ) << " passed" << std::endl;
- }
-
- void details() {
- for ( Status::iterator i = status.begin(); i != status.end(); ++i )
- if ( i->second != PASSED )
- std::cout << i->second << ": " << i->first << std::endl;
- }
-};
-
-struct Sink {
- int fd;
-
- typedef std::deque< char > Stream;
- typedef std::map< std::string, std::string > Subst;
-
- Stream stream;
- Subst subst;
- bool killed;
-
- virtual void outline( bool force )
- {
- Stream::iterator nl = std::find( stream.begin(), stream.end(), '\n' );
- if ( nl == stream.end() && !force )
- return;
-
- std::string line( stream.begin(), nl );
- stream.erase( stream.begin(), nl + 1 );
-
- if ( std::string( line, 0, 9 ) == "@TESTDIR=" )
- subst[ "@TESTDIR@" ] = std::string( line, 9, std::string::npos );
- else if ( std::string( line, 0, 8 ) == "@PREFIX=" )
- subst[ "@PREFIX@" ] = std::string( line, 8, std::string::npos );
- else {
- int off;
- for ( Subst::iterator s = subst.begin(); s != subst.end(); ++s )
- while ( (off = line.find( s->first )) != std::string::npos )
- line.replace( off, s->first.length(), s->second );
- write( fd, line.c_str(), line.length() );
- }
- }
-
- void sync() {
- while ( !stream.empty() )
- outline( true );
- }
-
- void push( std::string x ) {
- if ( !killed )
- std::copy( x.begin(), x.end(), std::back_inserter( stream ) );
- }
-
- Sink( int fd ) : fd( fd ), killed( false ) {}
-};
-
-struct Observer : Sink {
- Observer() : Sink( -1 ) {}
-};
-
-struct IO {
- typedef std::vector< Sink* > Sinks;
- mutable Sinks sinks;
- Observer *_observer;
-
- int fd;
- char buf[ 4097 ];
-
- void sink( std::string x ) {
- for ( Sinks::iterator i = sinks.begin(); i != sinks.end(); ++i )
- (*i)->push( x );
- }
-
- void sync() {
- ssize_t sz;
- while ((sz = read(fd, buf, sizeof(buf) - 1)) > 0)
- sink( std::string( buf, sz ) );
-
- if ( sz < 0 && errno != EAGAIN )
- throw syserr( "reading pipe" );
-
- /* get the kernel ring buffer too */
- sz = klogctl( SYSLOG_ACTION_READ_CLEAR, buf, sizeof(buf) - 1 );
- if ( sz > 0 )
- sink( std::string( buf, sz ) );
- }
-
- void close() { ::close( fd ); }
- Observer &observer() { return *_observer; }
-
- IO() : fd( -1 ) {
- sinks.push_back( _observer = new Observer );
- }
-
- IO( const IO &io ) {
- fd = io.fd;
- sinks = io.sinks;
- io.sinks.clear();
- }
-
- IO &operator= ( const IO &io ) {
- fd = io.fd;
- sinks = io.sinks;
- io.sinks.clear();
- return *this;
- }
-
- ~IO() {
- for ( Sinks::iterator i = sinks.begin(); i != sinks.end(); ++i )
- delete *i;
- }
-
+ Options() : verbose( false ), quiet( false ), interactive( false ) {}
};
struct TestProcess
{
- int fd;
- bool interactive;
std::string filename;
+ bool interactive;
+ int fd;
void exec() {
assert( fd >= 0 );
@@ -269,7 +79,6 @@ struct TestProcess
environment();
setpgid( 0, 0 );
- klogctl( SYSLOG_ACTION_CLEAR, 0, 0 );
execlp( "bash", "bash", "-noprofile", "-norc", filename.c_str(), NULL );
perror( "execlp" );
@@ -433,13 +242,19 @@ struct TestCase {
child.exec();
} else {
progress( First ) << tag( "running" ) << name << std::flush;
+ if ( options.verbose || options.interactive )
+ progress() << std::endl;
start = time( 0 );
parent();
}
}
- TestCase( std::string path, std::string name )
- : timeout( false ), silent_ctr( 0 ), child( path ), name( name ) {}
+ TestCase( Options opt, std::string path, std::string name )
+ : timeout( false ), silent_ctr( 0 ), child( path ), name( name ), options( opt )
+ {
+ if ( opt.verbose )
+ io.sinks.push_back( new FdSink( 1 ) );
+ }
};
struct Main {
@@ -461,7 +276,7 @@ struct Main {
continue;
if ( i->substr( 0, 4 ) == "lib/" )
continue;
- cases.push_back( TestCase( options.testdir + *i, *i ) );
+ cases.push_back( TestCase( options, options.testdir + *i, *i ) );
cases.back().options = options;
}
@@ -470,11 +285,13 @@ struct Main {
void run() {
setup();
start = time( 0 );
+ std::cerr << "running " << cases.size() << " tests" << std::endl;
+
for ( Cases::iterator i = cases.begin(); i != cases.end(); ++i ) {
i->run();
if ( time(0) - start > 3 * 3600 ) {
- printf("3 hours passed, giving up...\n");
+ std::cerr << "3 hours passed, giving up..." << std::endl;
die = 1;
}
@@ -487,7 +304,7 @@ struct Main {
exit( 1 );
}
- Main( Options o ) : options( o ) {}
+ Main( Options o ) : die( false ), options( o ) {}
};
static void handler( int sig ) {
diff --git a/test/lib/util.h b/test/lib/util.h
new file mode 100644
index 0000000..1450ab4
--- /dev/null
+++ b/test/lib/util.h
@@ -0,0 +1,39 @@
+/* -*- C++ -*- copyright (c) 2014 Red Hat, Inc.
+ *
+ * This file is part of LVM2.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. 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.
+ *
+ * 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 HOLDER 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 <stdexcept>
+#include <cstring>
+#include <errno.h>
+
+#ifndef RUNNER_UTIL_H
+#define RUNNER_UTIL_H
+
+inline std::runtime_error syserr( std::string msg, std::string ctx = "" ) {
+ return std::runtime_error( std::string( strerror( errno ) ) + " " + msg + " " + ctx );
+}
+
+#endif
9 years, 2 months
master - test: Use the new runner in make check.
by Petr Rockai
Gitweb: http://git.fedorahosted.org/git/?p=lvm2.git;a=commitdiff;h=b726d6688260e1...
Commit: b726d6688260e1e1e98500809eee014b7ceb92b5
Parent: 1ec2023cbfe40d285a9da55f029fcf173c763b88
Author: Petr Rockai <prockai(a)redhat.com>
AuthorDate: Wed Jun 25 21:35:11 2014 +0200
Committer: Petr Rockai <prockai(a)redhat.com>
CommitterDate: Thu Feb 5 13:47:16 2015 +0100
test: Use the new runner in make check.
---
test/Makefile.in | 7 +------
1 files changed, 1 insertions(+), 6 deletions(-)
diff --git a/test/Makefile.in b/test/Makefile.in
index e2f2cb8..c88d07a 100644
--- a/test/Makefile.in
+++ b/test/Makefile.in
@@ -101,12 +101,7 @@ help:
@echo " VERBOSE Verbose output (1), timing (2)."
check: .tests-stamp
- VERBOSE=$(VERBOSE) \
- cluster_LVM_TEST_LOCKING=3 \
- lvmetad_LVM_TEST_LVMETAD=1 \
- ./lib/harness $(patsubst %,normal:%,$(RUN_BASE)) \
- $(patsubst %,cluster:%,$(RUN_BASE)) \
- $(patsubst %,lvmetad:%,$(LVMETAD_RUN_BASE))
+ VERBOSE=$(VERBOSE) ./lib/runner --testdir shell --outdir results
check_system: .tests-stamp
VERBOSE=$(VERBOSE) \
9 years, 2 months
master - test: Build the new test runner.
by Petr Rockai
Gitweb: http://git.fedorahosted.org/git/?p=lvm2.git;a=commitdiff;h=1ec2023cbfe40d...
Commit: 1ec2023cbfe40d285a9da55f029fcf173c763b88
Parent: 9d6a92b4d08827c2d3e6c7f57570830ef0b44e8d
Author: Petr Rockai <prockai(a)redhat.com>
AuthorDate: Sun Jun 8 18:24:33 2014 +0200
Committer: Petr Rockai <prockai(a)redhat.com>
CommitterDate: Thu Feb 5 13:47:16 2015 +0100
test: Build the new test runner.
---
test/Makefile.in | 7 ++++++-
1 files changed, 6 insertions(+), 1 deletions(-)
diff --git a/test/Makefile.in b/test/Makefile.in
index 1aec88b..e2f2cb8 100644
--- a/test/Makefile.in
+++ b/test/Makefile.in
@@ -37,6 +37,8 @@ export LVM_TEST_CACHE_RESTORE_CMD?=@CACHE_RESTORE_CMD@
SUBDIRS = api unit
SOURCES = lib/not.c lib/harness.c
+CXXSOURCES = lib/runner.cpp
+CXXFLAGS = -std=c++11
include $(top_builddir)/make.tmpl
@@ -141,6 +143,9 @@ lib/fail: lib/not
ln -sf not lib/fail
+lib/runner: lib/runner.o .lib-dir-stamp
+ $(CXX) $(LDFLAGS) -o $@ $<
+
lib/%: lib/%.o .lib-dir-stamp
$(CC) $(LDFLAGS) -o $@ $<
@@ -174,7 +179,7 @@ lib/paths: $(srcdir)/Makefile.in .lib-dir-stamp Makefile
LIB = lib/not lib/invalid lib/fail lib/should lib/harness \
lib/check lib/aux lib/inittest lib/utils lib/get lib/lvm-wrapper \
- lib/paths
+ lib/paths lib/runner
CMDS = lvm $(shell cat $(top_builddir)/tools/.commands)
9 years, 2 months
master - configure: Add libexecdir/datadir to make.tmpl.in.
by Petr Rockai
Gitweb: http://git.fedorahosted.org/git/?p=lvm2.git;a=commitdiff;h=9d6a92b4d08827...
Commit: 9d6a92b4d08827c2d3e6c7f57570830ef0b44e8d
Parent: bf1f22cc997d3883f5ce481fb1ad1c6487fff9b7
Author: Petr Rockai <prockai(a)redhat.com>
AuthorDate: Fri Jun 27 01:24:16 2014 +0200
Committer: Petr Rockai <prockai(a)redhat.com>
CommitterDate: Thu Feb 5 13:47:16 2015 +0100
configure: Add libexecdir/datadir to make.tmpl.in.
---
make.tmpl.in | 2 ++
1 files changed, 2 insertions(+), 0 deletions(-)
diff --git a/make.tmpl.in b/make.tmpl.in
index 2a2107f..3a82731 100644
--- a/make.tmpl.in
+++ b/make.tmpl.in
@@ -73,10 +73,12 @@ bindir = $(DESTDIR)@bindir@
confdir = $(DESTDIR)@CONFDIR@/lvm
includedir = $(DESTDIR)@includedir@
libdir = $(DESTDIR)@libdir@
+libexecdir = $(DESTDIR)@libexecdir@
usrlibdir = $(DESTDIR)@usrlibdir@
sbindir = $(DESTDIR)@sbindir@
usrsbindir = $(DESTDIR)@usrsbindir@
datarootdir = @datarootdir@
+datadir = $(DESTDIR)/@datadir@
infodir = $(DESTDIR)@infodir@
mandir = $(DESTDIR)@mandir@
localedir = $(DESTDIR)@LOCALEDIR@
9 years, 2 months
master - configure: Allow C++ components.
by Petr Rockai
Gitweb: http://git.fedorahosted.org/git/?p=lvm2.git;a=commitdiff;h=bf1f22cc997d38...
Commit: bf1f22cc997d3883f5ce481fb1ad1c6487fff9b7
Parent: f476655fee6594b33b52ad5a90d5d442d155fbb9
Author: Petr Rockai <prockai(a)redhat.com>
AuthorDate: Sun Jun 8 18:23:47 2014 +0200
Committer: Petr Rockai <prockai(a)redhat.com>
CommitterDate: Tue Feb 3 08:49:35 2015 +0100
configure: Allow C++ components.
---
configure.in | 1 +
make.tmpl.in | 10 +++++++---
2 files changed, 8 insertions(+), 3 deletions(-)
diff --git a/configure.in b/configure.in
index 107bb29..add6653 100644
--- a/configure.in
+++ b/configure.in
@@ -69,6 +69,7 @@ dnl -- Checks for programs.
AC_PROG_SED
AC_PROG_AWK
AC_PROG_CC
+AC_PROG_CXX
dnl probably no longer needed in 2008, but...
AC_PROG_GCC_TRADITIONAL
diff --git a/make.tmpl.in b/make.tmpl.in
index 7efe46b..2a2107f 100644
--- a/make.tmpl.in
+++ b/make.tmpl.in
@@ -106,6 +106,7 @@ DEFAULT_MANGLING = @MANGLING@
# Setup vpath search paths for some suffixes
vpath %.c $(srcdir)
+vpath %.cpp $(srcdir)
vpath %.in $(srcdir)
vpath %.po $(srcdir)
vpath %.exported_symbols $(srcdir)
@@ -136,7 +137,7 @@ INSTALL_ROOT_DIR = $(INSTALL) -m 700 -d
INSTALL_ROOT_DATA = $(INSTALL) -m 600
INSTALL_SCRIPT = $(INSTALL) -p $(M_INSTALL_PROGRAM)
-.SUFFIXES: .c .d .o .so .a .po .pot .mo .dylib
+.SUFFIXES: .c .cpp .d .o .so .a .po .pot .mo .dylib
WFLAGS += -Wall -Wundef -Wshadow -Wcast-align -Wwrite-strings \
-Wmissing-prototypes -Wmissing-declarations -Wnested-externs \
@@ -239,7 +240,7 @@ INC_LNS = $(top_builddir)/include/.symlinks_created
DEPS = $(top_builddir)/make.tmpl $(top_srcdir)/VERSION \
$(top_builddir)/Makefile $(INC_LNS)
-OBJECTS = $(SOURCES:%.c=%.o)
+OBJECTS = $(SOURCES:%.c=%.o) $(CXXSOURCES:%.cpp=%.o)
POTFILES = $(SOURCES:%.c=%.pot)
.PHONY: all pofile distclean clean cleandir cflow device-mapper
@@ -366,6 +367,9 @@ $(TARGETS): $(OBJECTS)
%.o: %.c
$(CC) -c $(INCLUDES) $(DEFS) $(WFLAGS) $(CFLAGS) $(CFLAGS_$@) $< -o $@
+%.o: %.cpp
+ $(CXX) -c $(INCLUDES) $(DEFS) $(WFLAGS) $(CXXFLAGS) $(CXXFLAGS_$@) $< -o $@
+
%.pot: %.c Makefile
$(CC) -E $(INCLUDES) -include $(top_builddir)/include/pogen.h \
$(DEFS) $(WFLAGS) $(CFLAGS) $< > $@
@@ -454,7 +458,7 @@ distclean: cleandir $(SUBDIRS.distclean)
ifeq (,$(findstring $(MAKECMDGOALS),cscope.out cflow clean distclean lcov \
help check check_local check_cluster check_lvmetad))
ifdef SOURCES
- -include $(SOURCES:.c=.d)
+ -include $(SOURCES:.c=.d) $(CXXSOURCES:.cpp=.d)
endif
ifdef SOURCES2
-include $(SOURCES2:.c=.d)
9 years, 2 months
master - test: Import first draft of a new test runner.
by Petr Rockai
Gitweb: http://git.fedorahosted.org/git/?p=lvm2.git;a=commitdiff;h=f476655fee6594...
Commit: f476655fee6594b33b52ad5a90d5d442d155fbb9
Parent: f73526f58cd8e86a19b5cd8a26449ef40c9697ef
Author: Petr Rockai <prockai(a)redhat.com>
AuthorDate: Sun Jun 8 18:03:32 2014 +0200
Committer: Petr Rockai <prockai(a)redhat.com>
CommitterDate: Tue Feb 3 08:49:35 2015 +0100
test: Import first draft of a new test runner.
---
test/lib/runner.cpp | 594 +++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 594 insertions(+), 0 deletions(-)
diff --git a/test/lib/runner.cpp b/test/lib/runner.cpp
new file mode 100644
index 0000000..bcc98ba
--- /dev/null
+++ b/test/lib/runner.cpp
@@ -0,0 +1,594 @@
+/*
+ * Copyright (C) 2010-2014 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU General Public License v.2.
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <iostream>
+#include <vector>
+#include <deque>
+#include <map>
+#include <sstream>
+#include <cassert>
+#include <system_error>
+#include <algorithm>
+
+#include <fcntl.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/resource.h> /* rusage */
+#include <sys/select.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/klog.h>
+#include <time.h>
+#include <unistd.h>
+#include <stdint.h>
+#include <dirent.h>
+
+#define SYSLOG_ACTION_READ_CLEAR 4
+#define SYSLOG_ACTION_CLEAR 5
+
+pid_t kill_pid = 0;
+bool fatal_signal = false;
+
+std::system_error syserr( std::string msg, std::string ctx = "" ) {
+ return std::system_error( errno, std::generic_category(), msg + " " + ctx );
+}
+
+struct dir {
+ DIR *d;
+ dir( std::string p ) {
+ d = opendir( p.c_str() );
+ if ( !d )
+ throw syserr( "error opening directory", p );
+ }
+ ~dir() { closedir( d ); }
+};
+
+typedef std::vector< std::string > Listing;
+
+Listing listdir( std::string p, bool recurse = false, std::string prefix = "" ) {
+ Listing r;
+
+ dir d( p );
+ struct dirent entry, *iter = 0;
+ int readerr;
+
+ while ( (readerr = readdir_r( d.d, &entry, &iter )) == 0 && iter ) {
+ std::string ename( entry.d_name );
+
+ if ( ename == "." || ename == ".." )
+ continue;
+
+ if ( recurse ) {
+ struct stat64 stat;
+ std::string s = p + "/" + ename;
+ if ( ::stat64( s.c_str(), &stat ) == -1 )
+ throw syserr( "stat error", s );
+ if ( S_ISDIR(stat.st_mode) ) {
+ Listing sl = listdir( s, true, prefix + ename + "/" );
+ for ( Listing::iterator i = sl.begin(); i != sl.end(); ++i )
+ r.push_back( prefix + *i );
+ } else
+ r.push_back( prefix + ename );
+ } else
+ r.push_back( ename );
+ };
+
+ if ( readerr != 0 )
+ throw syserr( "error reading directory", p );
+
+ return r;
+}
+
+struct Options {
+ bool verbose, quiet, interactive;
+ std::string testdir, outdir;
+};
+
+struct Journal {
+ enum R {
+ STARTED,
+ UNKNOWN,
+ FAILED,
+ INTERRUPTED,
+ KNOWNFAIL,
+ PASSED,
+ SKIPPED,
+ TIMEOUT,
+ WARNED,
+ };
+
+ friend std::ostream &operator<<( std::ostream &o, R r ) {
+ switch ( r ) {
+ case STARTED: return o << "started";
+ case FAILED: return o << "failed";
+ case INTERRUPTED: return o << "interrupted";
+ case PASSED: return o << "passed";
+ case SKIPPED: return o << "skipped";
+ case TIMEOUT: return o << "timeout";
+ case WARNED: return o << "warnings";
+ default: return o << "unknown";
+ }
+ }
+
+ typedef std::map< std::string, R > Status;
+ Status status;
+
+ int count( R r ) {
+ int c = 0;
+ for ( Status::iterator i = status.begin(); i != status.end(); ++i )
+ if ( i->second == r )
+ ++ c;
+ return c;
+ }
+
+ void banner() {
+ std::cout << std::endl << "### " << status.size() << " tests: "
+ << count( PASSED ) << " passed" << std::endl;
+ }
+
+ void details() {
+ for ( Status::iterator i = status.begin(); i != status.end(); ++i )
+ if ( i->second != PASSED )
+ std::cout << i->second << ": " << i->first << std::endl;
+ }
+};
+
+struct Sink {
+ int fd;
+
+ typedef std::deque< char > Stream;
+ typedef std::map< std::string, std::string > Subst;
+
+ Stream stream;
+ Subst subst;
+ bool killed;
+
+ virtual void outline( bool force )
+ {
+ Stream::iterator nl = std::find( stream.begin(), stream.end(), '\n' );
+ if ( nl == stream.end() && !force )
+ return;
+
+ std::string line( stream.begin(), nl );
+ stream.erase( stream.begin(), nl + 1 );
+
+ if ( std::string( line, 0, 9 ) == "@TESTDIR=" )
+ subst[ "@TESTDIR@" ] = std::string( line, 9, std::string::npos );
+ else if ( std::string( line, 0, 8 ) == "@PREFIX=" )
+ subst[ "@PREFIX@" ] = std::string( line, 8, std::string::npos );
+ else {
+ int off;
+ for ( Subst::iterator s = subst.begin(); s != subst.end(); ++s )
+ while ( (off = line.find( s->first )) != std::string::npos )
+ line.replace( off, s->first.length(), s->second );
+ write( fd, line.c_str(), line.length() );
+ }
+ }
+
+ void sync() {
+ while ( !stream.empty() )
+ outline( true );
+ }
+
+ void push( std::string x ) {
+ if ( !killed )
+ std::copy( x.begin(), x.end(), std::back_inserter( stream ) );
+ }
+
+ Sink( int fd ) : fd( fd ), killed( false ) {}
+};
+
+struct Observer : Sink {
+ Observer() : Sink( -1 ) {}
+};
+
+struct IO {
+ typedef std::vector< Sink* > Sinks;
+ mutable Sinks sinks;
+ Observer *_observer;
+
+ int fd;
+ char buf[ 4097 ];
+
+ void sink( std::string x ) {
+ for ( Sinks::iterator i = sinks.begin(); i != sinks.end(); ++i )
+ (*i)->push( x );
+ }
+
+ void sync() {
+ ssize_t sz;
+ while ((sz = read(fd, buf, sizeof(buf) - 1)) > 0)
+ sink( std::string( buf, sz ) );
+
+ if ( sz < 0 && errno != EAGAIN )
+ throw syserr( "reading pipe" );
+
+ /* get the kernel ring buffer too */
+ sz = klogctl( SYSLOG_ACTION_READ_CLEAR, buf, sizeof(buf) - 1 );
+ if ( sz > 0 )
+ sink( std::string( buf, sz ) );
+ }
+
+ void close() { ::close( fd ); }
+ Observer &observer() { return *_observer; }
+
+ IO() : fd( -1 ) {
+ sinks.push_back( _observer = new Observer );
+ }
+
+ IO( const IO &io ) {
+ fd = io.fd;
+ sinks = io.sinks;
+ io.sinks.clear();
+ }
+
+ IO &operator= ( const IO &io ) {
+ fd = io.fd;
+ sinks = io.sinks;
+ io.sinks.clear();
+ return *this;
+ }
+
+ ~IO() {
+ for ( Sinks::iterator i = sinks.begin(); i != sinks.end(); ++i )
+ delete *i;
+ }
+
+};
+
+struct TestProcess
+{
+ int fd;
+ bool interactive;
+ std::string filename;
+
+ void exec() {
+ assert( fd >= 0 );
+ if ( !interactive ) {
+ close( STDIN_FILENO );
+ dup2( fd, STDOUT_FILENO );
+ dup2( fd, STDERR_FILENO );
+ }
+
+ environment();
+
+ setpgid( 0, 0 );
+ klogctl( SYSLOG_ACTION_CLEAR, 0, 0 );
+
+ execlp( "bash", "bash", "-noprofile", "-norc", filename.c_str(), NULL );
+ perror( "execlp" );
+ _exit( 202 );
+ }
+
+ void environment() {
+ /* if (strchr(f, ':')) {
+ strcpy(flavour, f);
+ *strchr(flavour, ':') = 0;
+ setenv("LVM_TEST_FLAVOUR", flavour, 1);
+ strcpy(script, strchr(f, ':') + 1);
+ } else {
+ strcpy(script, f);
+ } */
+ }
+
+ TestProcess( std::string file )
+ : filename( file ), interactive( false ), fd( -1 )
+ {}
+};
+
+struct TestCase {
+ TestProcess child;
+ std::string name;
+ IO io;
+
+ struct rusage usage;
+ int status;
+ bool timeout;
+ int silent_ctr;
+ pid_t pid;
+
+ time_t start, end;
+ Options options;
+
+ void pipe() {
+ int fds[2];
+
+ if (socketpair( PF_UNIX, SOCK_STREAM, 0, fds )) {
+ perror("socketpair");
+ exit(201);
+ }
+
+ if (fcntl( fds[0], F_SETFL, O_NONBLOCK ) == -1) {
+ perror("fcntl on socket");
+ exit(202);
+ }
+
+ io.fd = fds[0];
+ child.fd = fds[1];
+ child.interactive = options.interactive;
+ }
+
+ bool monitor() {
+ end = time( 0 );
+ if ( wait4(pid, &status, WNOHANG, &usage) != 0 )
+ return false;
+
+ /* kill off tests after a minute of silence */
+ if ( silent_ctr > 2 * 60 ) {
+ kill( pid, SIGINT );
+ sleep( 5 ); /* wait a bit for a reaction */
+ if ( waitpid( pid, &status, WNOHANG ) == 0 ) {
+ system( "echo t > /proc/sysrq-trigger" );
+ kill( -pid, SIGKILL );
+ waitpid( pid, &status, 0 );
+ }
+ timeout = true;
+ return false;
+ }
+
+ struct timeval wait;
+ fd_set set;
+
+ FD_ZERO( &set );
+ FD_SET( io.fd, &set );
+ wait.tv_sec = 0;
+ wait.tv_usec = 500000; /* timeout 0.5s */
+
+ if ( !options.verbose && !options.interactive )
+ progress( Update ) << tag( "running" ) << name << " " << end - start << std::flush;
+
+ if ( select( io.fd + 1, &set, NULL, NULL, &wait ) <= 0 )
+ {
+ silent_ctr++;
+ return true;
+ }
+
+ io.sync();
+ silent_ctr = 0;
+
+ return true;
+ }
+
+ std::string tag( std::string n ) {
+ int pad = (8 - n.length());
+ return "### " + std::string( pad, ' ' ) + n + ": ";
+ }
+
+ std::string tag( Journal::R r ) {
+ std::stringstream s;
+ s << r;
+ return tag( s.str() );
+ }
+
+ enum P { First, Update, Last };
+
+ std::ostream &progress( P p = Last )
+ {
+ static struct : std::streambuf {} buf;
+ static std::ostream null(&buf);
+
+ if ( isatty( STDOUT_FILENO ) ) {
+ if ( p != First )
+ return std::cout << "\r";
+ return std::cout;
+ }
+
+ if ( p == Last )
+ return std::cout;
+ return null;
+ }
+
+ void parent() {
+ while ( monitor() );
+
+ Journal::R r = Journal::UNKNOWN;
+
+ if ( timeout ) {
+ r = Journal::TIMEOUT;
+ } else if ( WIFEXITED( status ) ) {
+ if ( WEXITSTATUS( status ) == 0 )
+ r = Journal::PASSED;
+ else if ( WEXITSTATUS( status ) == 200 )
+ r = Journal::SKIPPED;
+ else
+ r = Journal::FAILED;
+ } else
+ r = Journal::FAILED;
+
+ ::close( io.fd );
+
+ /*
+ if ((fd_debuglog = open(testdirdebug, O_RDONLY)) != -1) {
+ drain(fd_debuglog, unlimited ? INT32_MAX : 4 * 1024 * 1024);
+ close(fd_debuglog);
+ } */
+
+ progress( Last ) << tag( r ) << name << std::endl;
+ }
+
+ void run() {
+ pipe();
+ pid = kill_pid = fork();
+ if (pid < 0) {
+ perror("Fork failed.");
+ exit(201);
+ } else if (pid == 0) {
+ io.close();
+ child.exec();
+ } else {
+ progress( First ) << tag( "running" ) << name << std::flush;
+ start = time( 0 );
+ parent();
+ }
+ }
+
+ TestCase( std::string path, std::string name )
+ : timeout( false ), silent_ctr( 0 ), child( path ), name( name ) {}
+};
+
+struct Main {
+ bool die;
+ time_t start;
+
+ typedef std::vector< TestCase > Cases;
+
+ Journal journal;
+ Options options;
+ Cases cases;
+
+ void setup() {
+ Listing l = listdir( options.testdir, true );
+ std::sort( l.begin(), l.end() );
+
+ for ( Listing::iterator i = l.begin(); i != l.end(); ++i ) {
+ if ( i->substr( i->length() - 3, i->length() ) != ".sh" )
+ continue;
+ if ( i->substr( 0, 4 ) == "lib/" )
+ continue;
+ cases.push_back( TestCase( options.testdir + *i, *i ) );
+ cases.back().options = options;
+ }
+
+ }
+
+ void run() {
+ setup();
+ start = time( 0 );
+ for ( Cases::iterator i = cases.begin(); i != cases.end(); ++i ) {
+ i->run();
+
+ if ( time(0) - start > 3 * 3600 ) {
+ printf("3 hours passed, giving up...\n");
+ die = 1;
+ }
+
+ if ( die || fatal_signal )
+ break;
+ }
+
+ journal.banner();
+ if ( die || fatal_signal )
+ exit( 1 );
+ }
+
+ Main( Options o ) : options( o ) {}
+};
+
+static void handler( int sig ) {
+ signal( sig, SIG_DFL );
+ if ( kill_pid > 0 )
+ kill( -kill_pid, sig );
+ fatal_signal = true;
+}
+
+void setup_handlers() {
+ /* set up signal handlers */
+ for ( int i = 0; i <= 32; ++i )
+ switch (i) {
+ case SIGCHLD: case SIGWINCH: case SIGURG:
+ case SIGKILL: case SIGSTOP: break;
+ default: signal(i, handler);
+ }
+}
+
+static int64_t get_time_us(void)
+{
+ struct timeval tv;
+
+ (void) gettimeofday(&tv, 0);
+ return (int64_t) tv.tv_sec * 1000000 + (int64_t) tv.tv_usec;
+}
+
+
+static const char *duration(time_t start, const struct rusage *usage)
+{
+ static char buf[100];
+ int t = (int)(time(NULL) - start);
+
+ int p = sprintf(buf, "%2d:%02d walltime", t / 60, t % 60);
+
+ if (usage)
+ sprintf(buf + p, " %2ld:%02ld.%03ld u, %ld:%02ld.%03ld s, %5ldk rss, %8ld/%ld IO",
+ usage->ru_utime.tv_sec / 60, usage->ru_utime.tv_sec % 60,
+ usage->ru_utime.tv_usec / 1000,
+ usage->ru_stime.tv_sec / 60, usage->ru_stime.tv_sec % 60,
+ usage->ru_stime.tv_usec / 1000,
+ usage->ru_maxrss / 1024,
+ usage->ru_inblock, usage->ru_oublock);
+
+ return buf;
+}
+
+struct Args {
+ typedef std::vector< std::string > V;
+ V args;
+
+ Args( int argc, char **argv ) {
+ for ( int i = 1; i < argc; ++ i )
+ args.push_back( argv[ i ] );
+ }
+
+ bool has( std::string fl ) {
+ return std::find( args.begin(), args.end(), fl ) != args.end();
+ }
+
+ std::string opt( std::string fl ) {
+ V::iterator i = std::find( args.begin(), args.end(), fl );
+ if ( i == args.end() || i + 1 == args.end() )
+ return "";
+ return *(i + 1);
+ }
+};
+
+int main(int argc, char **argv)
+{
+ Args args( argc, argv );
+ Options opt;
+
+ if ( args.has( "--quiet" ) || getenv( "QUIET" ) ) {
+ opt.verbose = false;
+ opt.quiet = true;
+ }
+
+ if ( args.has( "--verbose" ) || getenv( "VERBOSE" ) ) {
+ opt.quiet = false;
+ opt.verbose = true;
+ }
+
+ if ( args.has( "--interactive" ) || getenv( "INTERACTIVE" ) ) {
+ opt.verbose = false;
+ opt.quiet = false;
+ opt.interactive = true;
+ }
+
+ opt.outdir = args.opt( "--outdir" );
+ opt.testdir = args.opt( "--testdir" );
+
+ if ( opt.testdir.empty() )
+ opt.testdir = "/usr/share/lvm2-testsuite";
+
+ opt.testdir += "/";
+
+ setup_handlers();
+
+ Main main( opt );
+ main.run();
+
+}
+
9 years, 2 months
master - libdaemon: set CLOEXEC flag on systemd socket
by okozina
Gitweb: http://git.fedorahosted.org/git/?p=lvm2.git;a=commitdiff;h=f73526f58cd8e8...
Commit: f73526f58cd8e86a19b5cd8a26449ef40c9697ef
Parent: 9dd81df8b25a51c919679b5f1c4e5fea9269972b
Author: Ondrej Kozina <okozina(a)redhat.com>
AuthorDate: Fri Jan 30 15:15:24 2015 +0100
Committer: Ondrej Kozina <okozina(a)redhat.com>
CommitterDate: Mon Feb 2 10:20:35 2015 +0100
libdaemon: set CLOEXEC flag on systemd socket
all sockets opened by a daemon or handed over by systemd
have to have CLOEXEC flag set. Otherwise we get nasty
warnings about leaking descriptors in processes spawned by
daemon.
---
WHATS_NEW | 1 +
libdaemon/server/daemon-server.c | 8 +++++---
2 files changed, 6 insertions(+), 3 deletions(-)
diff --git a/WHATS_NEW b/WHATS_NEW
index 3555063..cd4abfe 100644
--- a/WHATS_NEW
+++ b/WHATS_NEW
@@ -1,5 +1,6 @@
Version 2.02.117 -
====================================
+ set CLOEXEC flag on file descriptors originating in libdaemon
Version 2.02.116 - 30th January 2015
====================================
diff --git a/libdaemon/server/daemon-server.c b/libdaemon/server/daemon-server.c
index 3dffdbb..5ccf4e9 100644
--- a/libdaemon/server/daemon-server.c
+++ b/libdaemon/server/daemon-server.c
@@ -221,9 +221,7 @@ static int _open_socket(daemon_state s)
goto error;
}
- /* Set Close-on-exec & non-blocking */
- if (fcntl(fd, F_SETFD, 1))
- fprintf(stderr, "setting CLOEXEC on socket fd %d failed: %s\n", fd, strerror(errno));
+ /* Set non-blocking */
if (fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_NONBLOCK))
fprintf(stderr, "setting O_NONBLOCK on socket fd %d failed: %s\n", fd, strerror(errno));
@@ -572,6 +570,10 @@ void daemon_start(daemon_state s)
failed = 1;
}
+ /* Set Close-on-exec */
+ if (fcntl(s.socket_fd, F_SETFD, 1))
+ fprintf(stderr, "setting CLOEXEC on socket fd %d failed: %s\n", s.socket_fd, strerror(errno));
+
/* Signal parent, letting them know we are ready to go. */
if (!s.foreground)
kill(getppid(), SIGTERM);
9 years, 2 months
master - libdaemon: set CLOEXEC on client socket fd
by okozina
Gitweb: http://git.fedorahosted.org/git/?p=lvm2.git;a=commitdiff;h=9dd81df8b25a51...
Commit: 9dd81df8b25a51c919679b5f1c4e5fea9269972b
Parent: 99c443facce33d2c666b0e043d9e8a6bd1cb40a2
Author: Ondrej Kozina <okozina(a)redhat.com>
AuthorDate: Wed Nov 12 09:50:59 2014 +0100
Committer: Ondrej Kozina <okozina(a)redhat.com>
CommitterDate: Mon Feb 2 10:15:52 2015 +0100
libdaemon: set CLOEXEC on client socket fd
---
libdaemon/server/daemon-server.c | 3 +++
1 files changed, 3 insertions(+), 0 deletions(-)
diff --git a/libdaemon/server/daemon-server.c b/libdaemon/server/daemon-server.c
index a8afcc1..3dffdbb 100644
--- a/libdaemon/server/daemon-server.c
+++ b/libdaemon/server/daemon-server.c
@@ -470,6 +470,9 @@ static int handle_connect(daemon_state s)
if (client.socket_fd < 0)
return 0;
+ if (fcntl(client.socket_fd, F_SETFD, FD_CLOEXEC))
+ WARN(&s, "setting CLOEXEC on client socket fd %d failed", client.socket_fd);
+
if (!(ts = dm_malloc(sizeof(thread_state)))) {
if (close(client.socket_fd))
perror("close");
9 years, 2 months