/* * Copyright (C) 2009, 2010, 2011 Nicolas Bonnefon and other contributors * * This file is part of glogg. * * glogg 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 3 of the License, or * (at your option) any later version. * * glogg 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 glogg. If not, see . */ #include #include #include #include "testlogfiltereddata.h" #include "logdata.h" #include "logfiltereddata.h" #if !defined( TMPDIR ) #define TMPDIR "/tmp" #endif static const qint64 ML_NB_LINES = 15000LL; static const char* ml_format="LOGDATA is a part of glogg, we are going to test it thoroughly, this is line\t\t%06d\n"; static const int ML_VISIBLE_LINE_LENGTH = (76+8+4+6); // Without the final '\n' ! static const qint64 SL_NB_LINES = 2000LL; static const char* sl_format="LOGDATA is a part of glogg, we are going to test it thoroughly, this is line %06d\n"; static const char* partial_line_begin = "123... beginning of line."; static const char* partial_line_end = " end of line 123.\n"; static const char* partial_nonmatching_line_begin = "Beginning of line."; TestLogFilteredData::TestLogFilteredData() : QObject(), loadingFinishedMutex_(), searchProgressedMutex_(), loadingFinishedCondition_(), searchProgressedCondition_() { loadingFinished_received_ = false; loadingFinished_read_ = false; searchProgressed_received_ = false; searchProgressed_read_ = false; } void TestLogFilteredData::initTestCase() { QVERIFY( generateDataFiles() ); } void TestLogFilteredData::simpleSearch() { logData_ = new LogData(); // Register for notification file is loaded connect( logData_, SIGNAL( loadingFinished( bool ) ), this, SLOT( loadingFinished() ) ); filteredData_ = logData_->getNewFilteredData(); connect( filteredData_, SIGNAL( searchProgressed( int, int ) ), this, SLOT( searchProgressed( int, int ) ) ); QFuture future = QtConcurrent::run(this, &TestLogFilteredData::simpleSearchTest); QApplication::exec(); disconnect( filteredData_, 0 ); disconnect( logData_, 0 ); delete filteredData_; delete logData_; } void TestLogFilteredData::simpleSearchTest() { // First load the tests file logData_->attachFile( TMPDIR "/mediumlog.txt" ); // Wait for the loading to be done waitLoadingFinished(); QCOMPARE( logData_->getNbLine(), ML_NB_LINES ); signalLoadingFinishedRead(); // Now perform a simple search qint64 matches[] = { 0, 15, 20, 135 }; QBENCHMARK { // Start the search filteredData_->runSearch( QRegExp( "123" ) ); // And check we receive data in 4 chunks (the first being empty) for ( int i = 0; i < 4; i++ ) { std::pair progress = waitSearchProgressed(); // FIXME: The test for this is unfortunately not reliable // (race conditions) // QCOMPARE( (qint64) progress.first, matches[i] ); signalSearchProgressedRead(); } } QCOMPARE( filteredData_->getNbLine(), matches[3] ); // Check the search QCOMPARE( filteredData_->isLineInMatchingList( 123 ), true ); QCOMPARE( filteredData_->isLineInMatchingList( 124 ), false ); QCOMPARE( filteredData_->getMaxLength(), ML_VISIBLE_LINE_LENGTH ); QCOMPARE( filteredData_->getLineLength( 12 ), ML_VISIBLE_LINE_LENGTH ); QCOMPARE( filteredData_->getNbLine(), 135LL ); // Line beyond limit QCOMPARE( filteredData_->isLineInMatchingList( 60000 ), false ); QCOMPARE( filteredData_->getMatchingLineNumber( 0 ), 123LL ); // Now let's try interrupting a search filteredData_->runSearch( QRegExp( "123" ) ); // ... wait for two chunks. waitSearchProgressed(); signalSearchProgressedRead(); // and interrupt! filteredData_->interruptSearch(); { std::pair progress; do { progress = waitSearchProgressed(); signalSearchProgressedRead(); } while ( progress.second < 100 ); // (because there is no guarantee when the search is // interrupted, we are not sure how many chunk of result // we will get.) } QApplication::quit(); } void TestLogFilteredData::multipleSearch() { logData_ = new LogData(); // Register for notification file is loaded connect( logData_, SIGNAL( loadingFinished( bool ) ), this, SLOT( loadingFinished() ) ); filteredData_ = logData_->getNewFilteredData(); connect( filteredData_, SIGNAL( searchProgressed( int, int ) ), this, SLOT( searchProgressed( int, int ) ) ); QFuture future = QtConcurrent::run(this, &TestLogFilteredData::multipleSearchTest); QApplication::exec(); disconnect( filteredData_, 0 ); disconnect( logData_, 0 ); delete filteredData_; delete logData_; } void TestLogFilteredData::multipleSearchTest() { // First load the tests file logData_->attachFile( TMPDIR "/smalllog.txt" ); // Wait for the loading to be done waitLoadingFinished(); QCOMPARE( logData_->getNbLine(), SL_NB_LINES ); signalLoadingFinishedRead(); // Performs two searches in a row // Start the search, and immediately another one // (the second call should block until the first search is done) filteredData_->runSearch( QRegExp( "1234" ) ); filteredData_->runSearch( QRegExp( "123" ) ); for ( int i = 0; i < 3; i++ ) { waitSearchProgressed(); signalSearchProgressedRead(); } // We should have the result for the 2nd search after the last chunk waitSearchProgressed(); QCOMPARE( filteredData_->getNbLine(), 12LL ); signalSearchProgressedRead(); // Now a tricky one: we run a search and immediately attach a new file /* FIXME: sometimes we receive loadingFinished before searchProgressed * -> deadlock in the test. filteredData_->runSearch( QRegExp( "123" ) ); waitSearchProgressed(); signalSearchProgressedRead(); logData_->attachFile( TMPDIR "/mediumlog.txt" ); // We don't expect meaningful results but it should not crash! for ( int i = 0; i < 1; i++ ) { waitSearchProgressed(); signalSearchProgressedRead(); } */ sleep(10); QApplication::quit(); } void TestLogFilteredData::updateSearch() { logData_ = new LogData(); // Register for notification file is loaded connect( logData_, SIGNAL( loadingFinished( bool ) ), this, SLOT( loadingFinished() ) ); filteredData_ = logData_->getNewFilteredData(); connect( filteredData_, SIGNAL( searchProgressed( int, int ) ), this, SLOT( searchProgressed( int, int ) ) ); QFuture future = QtConcurrent::run(this, &TestLogFilteredData::updateSearchTest); QApplication::exec(); disconnect( filteredData_, 0 ); disconnect( logData_, 0 ); delete filteredData_; delete logData_; } void TestLogFilteredData::updateSearchTest() { // First load the tests file logData_->attachFile( TMPDIR "/smalllog.txt" ); // Wait for the loading to be done waitLoadingFinished(); QCOMPARE( logData_->getNbLine(), SL_NB_LINES ); signalLoadingFinishedRead(); // Perform a first search filteredData_->runSearch( QRegExp( "123" ) ); for ( int i = 0; i < 2; i++ ) { waitSearchProgressed(); signalSearchProgressedRead(); } // Check the result QCOMPARE( filteredData_->getNbLine(), 12LL ); sleep(1); QWARN("Starting stage 2"); // Add some data to the file char newLine[90]; QFile file( TMPDIR "/smalllog.txt" ); if ( file.open( QIODevice::Append ) ) { for (int i = 0; i < 3000; i++) { snprintf(newLine, 89, sl_format, i); file.write( newLine, qstrlen(newLine) ); } // To test the edge case when the final line is not complete and matching file.write( partial_line_begin, qstrlen( partial_line_begin ) ); } file.close(); // Let the system do the update (there might be several ones) do { waitLoadingFinished(); signalLoadingFinishedRead(); } while ( logData_->getNbLine() < 5001LL ); sleep(1); // Start an update search filteredData_->updateSearch(); for ( int i = 0; i < 2; i++ ) { waitSearchProgressed(); signalSearchProgressedRead(); } // Check the result QCOMPARE( logData_->getNbLine(), 5001LL ); QCOMPARE( filteredData_->getNbLine(), 26LL ); QWARN("Starting stage 3"); // Add a couple more lines, including the end of the unfinished one. if ( file.open( QIODevice::Append ) ) { file.write( partial_line_end, qstrlen( partial_line_end ) ); for (int i = 0; i < 20; i++) { snprintf(newLine, 89, sl_format, i); file.write( newLine, qstrlen(newLine) ); } // To test the edge case when the final line is not complete and not matching file.write( partial_nonmatching_line_begin, qstrlen( partial_nonmatching_line_begin ) ); } file.close(); // Let the system do the update waitLoadingFinished(); signalLoadingFinishedRead(); // Start an update search filteredData_->updateSearch(); for ( int i = 0; i < 2; i++ ) { waitSearchProgressed(); signalSearchProgressedRead(); } // Check the result QCOMPARE( logData_->getNbLine(), 5022LL ); QCOMPARE( filteredData_->getNbLine(), 26LL ); QWARN("Starting stage 4"); // Now test the case where a match is found at the end of an updated line. if ( file.open( QIODevice::Append ) ) { file.write( partial_line_end, qstrlen( partial_line_end ) ); for (int i = 0; i < 20; i++) { snprintf(newLine, 89, sl_format, i); file.write( newLine, qstrlen(newLine) ); } } file.close(); // Let the system do the update waitLoadingFinished(); signalLoadingFinishedRead(); // Start an update search filteredData_->updateSearch(); for ( int i = 0; i < 2; i++ ) { waitSearchProgressed(); signalSearchProgressedRead(); } // Check the result QCOMPARE( logData_->getNbLine(), 5042LL ); QCOMPARE( filteredData_->getNbLine(), 27LL ); QApplication::quit(); } void TestLogFilteredData::marks() { logData_ = new LogData(); // Register for notification file is loaded connect( logData_, SIGNAL( loadingFinished( bool ) ), this, SLOT( loadingFinished() ) ); filteredData_ = logData_->getNewFilteredData(); connect( filteredData_, SIGNAL( searchProgressed( int, int ) ), this, SLOT( searchProgressed( int, int ) ) ); QFuture future = QtConcurrent::run(this, &TestLogFilteredData::marksTest); QApplication::exec(); disconnect( filteredData_, 0 ); disconnect( logData_, 0 ); delete filteredData_; delete logData_; } void TestLogFilteredData::marksTest() { // First load the tests file logData_->attachFile( TMPDIR "/smalllog.txt" ); // Wait for the loading to be done waitLoadingFinished(); QCOMPARE( logData_->getNbLine(), SL_NB_LINES ); signalLoadingFinishedRead(); // First check no line is marked for ( int i = 0; i < SL_NB_LINES; i++ ) QVERIFY( filteredData_->isLineMarked( i ) == false ); // Try to create some "out of limit" marks filteredData_->addMark( -10 ); filteredData_->addMark( SL_NB_LINES + 25 ); // Check no line is marked still for ( int i = 0; i < SL_NB_LINES; i++ ) QVERIFY( filteredData_->isLineMarked( i ) == false ); // Create a couple of unnamed marks filteredData_->addMark( 10 ); filteredData_->addMark( 44 ); // This one will also be a match filteredData_->addMark( 25 ); // Check they are marked QVERIFY( filteredData_->isLineMarked( 10 ) ); QVERIFY( filteredData_->isLineMarked( 25 ) ); QVERIFY( filteredData_->isLineMarked( 44 ) ); // But others are not QVERIFY( filteredData_->isLineMarked( 15 ) == false ); QVERIFY( filteredData_->isLineMarked( 20 ) == false ); QCOMPARE( filteredData_->getNbLine(), 3LL ); // Performs a search QSignalSpy progressSpy( filteredData_, SIGNAL( searchProgressed( int, int ) ) ); filteredData_->runSearch( QRegExp( "0000.4" ) ); for ( int i = 0; i < 1; i++ ) { waitSearchProgressed(); signalSearchProgressedRead(); } // We should have the result of the search and the marks waitSearchProgressed(); QCOMPARE( filteredData_->getNbLine(), 12LL ); signalSearchProgressedRead(); QString startline = "LOGDATA is a part of glogg, we are going to test it thoroughly, this is line "; QCOMPARE( filteredData_->getLineString(0), startline + "000004" ); QCOMPARE( filteredData_->getLineString(1), startline + "000010" ); QCOMPARE( filteredData_->getLineString(2), startline + "000014" ); QCOMPARE( filteredData_->getLineString(3), startline + "000024" ); QCOMPARE( filteredData_->getLineString(4), startline + "000025" ); QCOMPARE( filteredData_->getLineString(5), startline + "000034" ); QCOMPARE( filteredData_->getLineString(6), startline + "000044" ); QCOMPARE( filteredData_->getLineString(7), startline + "000054" ); QCOMPARE( filteredData_->getLineString(8), startline + "000064" ); QCOMPARE( filteredData_->getLineString(9), startline + "000074" ); QCOMPARE( filteredData_->getLineString(10), startline + "000084" ); QCOMPARE( filteredData_->getLineString(11), startline + "000094" ); filteredData_->setVisibility( LogFilteredData::MatchesOnly ); QCOMPARE( filteredData_->getNbLine(), 10LL ); QCOMPARE( filteredData_->getLineString(0), startline + "000004" ); QCOMPARE( filteredData_->getLineString(1), startline + "000014" ); QCOMPARE( filteredData_->getLineString(2), startline + "000024" ); QCOMPARE( filteredData_->getLineString(3), startline + "000034" ); QCOMPARE( filteredData_->getLineString(4), startline + "000044" ); QCOMPARE( filteredData_->getLineString(5), startline + "000054" ); QCOMPARE( filteredData_->getLineString(6), startline + "000064" ); QCOMPARE( filteredData_->getLineString(7), startline + "000074" ); QCOMPARE( filteredData_->getLineString(8), startline + "000084" ); QCOMPARE( filteredData_->getLineString(9), startline + "000094" ); filteredData_->setVisibility( LogFilteredData::MarksOnly ); QCOMPARE( filteredData_->getNbLine(), 3LL ); QCOMPARE( filteredData_->getLineString(0), startline + "000010" ); QCOMPARE( filteredData_->getLineString(1), startline + "000025" ); QCOMPARE( filteredData_->getLineString(2), startline + "000044" ); // Another test with marks only filteredData_->clearSearch(); filteredData_->clearMarks(); filteredData_->setVisibility( LogFilteredData::MarksOnly ); filteredData_->addMark(18); filteredData_->addMark(19); filteredData_->addMark(20); QCOMPARE( filteredData_->getMatchingLineNumber(0), 18LL ); QCOMPARE( filteredData_->getMatchingLineNumber(1), 19LL ); QCOMPARE( filteredData_->getMatchingLineNumber(2), 20LL ); QApplication::quit(); } void TestLogFilteredData::lineLength() { logData_ = new LogData(); // Register for notification file is loaded connect( logData_, SIGNAL( loadingFinished( bool ) ), this, SLOT( loadingFinished() ) ); filteredData_ = logData_->getNewFilteredData(); connect( filteredData_, SIGNAL( searchProgressed( int, int ) ), this, SLOT( searchProgressed( int, int ) ) ); QFuture future = QtConcurrent::run(this, &TestLogFilteredData::lineLengthTest); QApplication::exec(); disconnect( filteredData_, 0 ); disconnect( logData_, 0 ); delete filteredData_; delete logData_; } void TestLogFilteredData::lineLengthTest() { // Line length tests logData_->attachFile( TMPDIR "/length_test.txt" ); // Wait for the loading to be done waitLoadingFinished(); QCOMPARE( logData_->getNbLine(), 4LL ); signalLoadingFinishedRead(); // Performs a search (the two middle lines matche) filteredData_->setVisibility( LogFilteredData::MatchesOnly ); filteredData_->runSearch( QRegExp( "longer" ) ); std::pair progress; do { progress = waitSearchProgressed(); signalSearchProgressedRead(); QWARN("progress"); } while ( progress.second < 100 ); filteredData_->addMark( 3 ); QCOMPARE( filteredData_->getNbLine(), 2LL ); QCOMPARE( filteredData_->getMaxLength(), 40 ); filteredData_->setVisibility( LogFilteredData::MarksAndMatches ); QCOMPARE( filteredData_->getNbLine(), 3LL ); QCOMPARE( filteredData_->getMaxLength(), 103 ); filteredData_->setVisibility( LogFilteredData::MarksOnly ); QCOMPARE( filteredData_->getNbLine(), 1LL ); QCOMPARE( filteredData_->getMaxLength(), 103 ); filteredData_->addMark( 0 ); QCOMPARE( filteredData_->getNbLine(), 2LL ); QCOMPARE( filteredData_->getMaxLength(), 103 ); filteredData_->deleteMark( 3 ); QCOMPARE( filteredData_->getNbLine(), 1LL ); QCOMPARE( filteredData_->getMaxLength(), 27 ); filteredData_->setVisibility( LogFilteredData::MarksAndMatches ); QCOMPARE( filteredData_->getMaxLength(), 40 ); QApplication::quit(); } // // Private functions // void TestLogFilteredData::loadingFinished() { QMutexLocker locker( &loadingFinishedMutex_ ); QWARN("loadingFinished"); loadingFinished_received_ = true; loadingFinished_read_ = false; loadingFinishedCondition_.wakeOne(); // Wait for the test thread to read the signal while ( ! loadingFinished_read_ ) loadingFinishedCondition_.wait( locker.mutex() ); } void TestLogFilteredData::searchProgressed( int nbMatches, int completion ) { QMutexLocker locker( &searchProgressedMutex_ ); QWARN("searchProgressed"); searchProgressed_received_ = true; searchProgressed_read_ = false; searchLastMatches_ = nbMatches; searchLastProgress_ = completion; searchProgressedCondition_.wakeOne(); // Wait for the test thread to read the signal while ( ! searchProgressed_read_ ) searchProgressedCondition_.wait( locker.mutex() ); } std::pair TestLogFilteredData::waitSearchProgressed() { QMutexLocker locker( &searchProgressedMutex_ ); while ( ! searchProgressed_received_ ) searchProgressedCondition_.wait( locker.mutex() ); QWARN("searchProgressed Received"); return std::pair(searchLastMatches_, searchLastProgress_); } void TestLogFilteredData::waitLoadingFinished() { QMutexLocker locker( &loadingFinishedMutex_ ); while ( ! loadingFinished_received_ ) loadingFinishedCondition_.wait( locker.mutex() ); QWARN("loadingFinished Received"); } void TestLogFilteredData::signalSearchProgressedRead() { QMutexLocker locker( &searchProgressedMutex_ ); searchProgressed_received_ = false; searchProgressed_read_ = true; searchProgressedCondition_.wakeOne(); } void TestLogFilteredData::signalLoadingFinishedRead() { QMutexLocker locker( &loadingFinishedMutex_ ); loadingFinished_received_ = false; loadingFinished_read_ = true; loadingFinishedCondition_.wakeOne(); } bool TestLogFilteredData::generateDataFiles() { char newLine[90]; QFile file( TMPDIR "/mediumlog.txt" ); if ( file.open( QIODevice::WriteOnly ) ) { for (int i = 0; i < ML_NB_LINES; i++) { snprintf(newLine, 89, ml_format, i); file.write( newLine, qstrlen(newLine) ); } } else { return false; } file.close(); file.setFileName( TMPDIR "/smalllog.txt" ); if ( file.open( QIODevice::WriteOnly ) ) { for (int i = 0; i < SL_NB_LINES; i++) { snprintf(newLine, 89, sl_format, i); file.write( newLine, qstrlen(newLine) ); } } else { return false; } file.close(); file.setFileName( TMPDIR "/length_test.txt" ); if ( file.open( QIODevice::WriteOnly ) ) { file.write( "This line is 27 characters.\n" ); file.write( "This line is longer: 36 characters.\n" ); file.write( "This line is even longer: 40 characters.\n" ); file.write( "This line is very long, it's actually hard to count but it is\ probably something around 103 characters.\n" ); } file.close(); return true; }