1 /* 2 * Copyright (C) 2009, 2010 Nicolas Bonnefon and other contributors 3 * 4 * This file is part of glogg. 5 * 6 * glogg is free software: you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License as published by 8 * the Free Software Foundation, either version 3 of the License, or 9 * (at your option) any later version. 10 * 11 * glogg is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 * 16 * You should have received a copy of the GNU General Public License 17 * along with glogg. If not, see <http://www.gnu.org/licenses/>. 18 */ 19 20 #include <QSignalSpy> 21 #include <QMutexLocker> 22 #include <QFile> 23 24 #include "testlogfiltereddata.h" 25 #include "logdata.h" 26 #include "logfiltereddata.h" 27 28 #if QT_VERSION < 0x040500 29 #define QBENCHMARK 30 #endif 31 32 #if !defined( TMPDIR ) 33 #define TMPDIR "/tmp" 34 #endif 35 36 static const qint64 ML_NB_LINES = 15000LL; 37 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"; 38 static const int ML_VISIBLE_LINE_LENGTH = (76+8+4+6); // Without the final '\n' ! 39 40 static const qint64 SL_NB_LINES = 2000LL; 41 static const char* sl_format="LOGDATA is a part of glogg, we are going to test it thoroughly, this is line %06d\n"; 42 43 static const char* partial_line_begin = "123... beginning of line."; 44 static const char* partial_line_end = " end of line 123.\n"; 45 46 static const char* partial_nonmatching_line_begin = "Beginning of line."; 47 48 void TestLogFilteredData::initTestCase() 49 { 50 QVERIFY( generateDataFiles() ); 51 } 52 53 void TestLogFilteredData::simpleSearch() 54 { 55 LogData logData; 56 57 // First load the tests file 58 // Register for notification file is loaded 59 connect( &logData, SIGNAL( loadingFinished( bool ) ), 60 this, SLOT( loadingFinished() ) ); 61 62 logData.attachFile( TMPDIR "/mediumlog.txt" ); 63 // Wait for the loading to be done 64 { 65 QApplication::exec(); 66 } 67 68 QCOMPARE( logData.getNbLine(), ML_NB_LINES ); 69 70 // Now perform a simple search 71 LogFilteredData* filteredData = logData.getNewFilteredData(); 72 connect( filteredData, SIGNAL( searchProgressed( int, int ) ), 73 this, SLOT( searchProgressed( int, int ) ) ); 74 75 QSignalSpy progressSpy( filteredData, SIGNAL( searchProgressed( int, int ) ) ); 76 77 qint64 matches[] = { 0, 15, 20, 135 }; 78 QBENCHMARK { 79 // Start the search 80 filteredData->runSearch( QRegExp( "123" ) ); 81 82 // And check we receive data in 4 chunks (the first being empty) 83 for ( int i = 0; i < 4; i++ ) { 84 QApplication::exec(); 85 QCOMPARE( filteredData->getNbLine(), matches[i] ); 86 } 87 } 88 89 QCOMPARE( progressSpy.count(), 4 ); 90 91 // Check the search 92 QCOMPARE( filteredData->isLineInMatchingList( 123 ), true ); 93 QCOMPARE( filteredData->isLineInMatchingList( 124 ), false ); 94 QCOMPARE( filteredData->getMaxLength(), ML_VISIBLE_LINE_LENGTH ); 95 QCOMPARE( filteredData->getLineLength( 12 ), ML_VISIBLE_LINE_LENGTH ); 96 QCOMPARE( filteredData->getNbLine(), 135LL ); 97 // Line beyond limit 98 QCOMPARE( filteredData->isLineInMatchingList( 60000 ), false ); 99 QCOMPARE( filteredData->getMatchingLineNumber( 0 ), 123LL ); 100 101 // Now let's try interrupting a search 102 filteredData->runSearch( QRegExp( "123" ) ); 103 // ... wait for two chunks. 104 QApplication::exec(); 105 QApplication::exec(); 106 // and interrupt! 107 filteredData->interruptSearch(); 108 QCOMPARE( filteredData->getNbLine(), matches[1] ); 109 QApplication::exec(); 110 // After interrupt: should be 100% and the same number of matches 111 QCOMPARE( filteredData->getNbLine(), matches[2] ); 112 113 // (because there is no guarantee when the search is 114 // interrupted, we are not sure how many chunk of result 115 // we will get.) 116 117 // Disconnect all signals 118 disconnect( &logData, 0 ); 119 120 // Destroy the filtered data 121 delete filteredData; 122 } 123 124 void TestLogFilteredData::multipleSearch() 125 { 126 LogData logData; 127 128 // First load the tests file 129 // Register for notification file is loaded 130 connect( &logData, SIGNAL( loadingFinished( bool ) ), 131 this, SLOT( loadingFinished() ) ); 132 133 logData.attachFile( TMPDIR "/smalllog.txt" ); 134 // Wait for the loading to be done 135 { 136 QApplication::exec(); 137 } 138 139 QCOMPARE( logData.getNbLine(), SL_NB_LINES ); 140 141 // Performs two searches in a row 142 LogFilteredData* filteredData = logData.getNewFilteredData(); 143 connect( filteredData, SIGNAL( searchProgressed( int, int ) ), 144 this, SLOT( searchProgressed( int, int ) ) ); 145 146 QSignalSpy progressSpy( filteredData, 147 SIGNAL( searchProgressed( int, int ) ) ); 148 149 // Start the search, and immediately another one 150 // (the second call should block until the first search is done) 151 filteredData->runSearch( QRegExp( "1234" ) ); 152 filteredData->runSearch( QRegExp( "123" ) ); 153 154 for ( int i = 0; i < 3; i++ ) 155 QApplication::exec(); 156 157 // We should have the result for the 2nd search 158 QCOMPARE( filteredData->getNbLine(), 12LL ); 159 160 QCOMPARE( progressSpy.count(), 4 ); 161 162 // Now a tricky one: we run a search and immediately attach a new file 163 filteredData->runSearch( QRegExp( "123" ) ); 164 QApplication::exec(); 165 logData.attachFile( TMPDIR "/mediumlog.txt" ); 166 167 // We don't expect meaningful results but it should not crash! 168 for ( int i = 0; i < 2; i++ ) 169 QApplication::exec(); 170 171 // Disconnect all signals 172 disconnect( &logData, 0 ); 173 174 // Destroy the filtered data 175 delete filteredData; 176 } 177 178 void TestLogFilteredData::updateSearch() 179 { 180 LogData logData; 181 182 // First load the tests file 183 // Register for notification file is loaded 184 connect( &logData, SIGNAL( loadingFinished( bool ) ), 185 this, SLOT( loadingFinished() ) ); 186 187 logData.attachFile( TMPDIR "/smalllog.txt" ); 188 // Wait for the loading to be done 189 { 190 QApplication::exec(); 191 } 192 193 QCOMPARE( logData.getNbLine(), SL_NB_LINES ); 194 195 // Perform a first search 196 LogFilteredData* filteredData = logData.getNewFilteredData(); 197 connect( filteredData, SIGNAL( searchProgressed( int, int ) ), 198 this, SLOT( searchProgressed( int, int ) ) ); 199 200 QSignalSpy progressSpy( filteredData, 201 SIGNAL( searchProgressed( int, int ) ) ); 202 203 // Start a search 204 filteredData->runSearch( QRegExp( "123" ) ); 205 206 for ( int i = 0; i < 2; i++ ) 207 QApplication::exec(); 208 209 // Check the result 210 QCOMPARE( filteredData->getNbLine(), 12LL ); 211 QCOMPARE( progressSpy.count(), 2 ); 212 213 // Add some data to the file 214 char newLine[90]; 215 QFile file( TMPDIR "/smalllog.txt" ); 216 if ( file.open( QIODevice::Append ) ) { 217 for (int i = 0; i < 3000; i++) { 218 snprintf(newLine, 89, sl_format, i); 219 file.write( newLine, qstrlen(newLine) ); 220 } 221 // To test the edge case when the final line is not complete and matching 222 file.write( partial_line_begin, qstrlen( partial_line_begin ) ); 223 } 224 file.close(); 225 226 // Let the system do the update 227 QApplication::exec(); 228 229 // Start an update search 230 filteredData->updateSearch(); 231 232 for ( int i = 0; i < 2; i++ ) 233 QApplication::exec(); 234 235 // Check the result 236 QCOMPARE( logData.getNbLine(), 5001LL ); 237 QCOMPARE( filteredData->getNbLine(), 26LL ); 238 QCOMPARE( progressSpy.count(), 4 ); 239 240 // Add a couple more lines, including the end of the unfinished one. 241 if ( file.open( QIODevice::Append ) ) { 242 file.write( partial_line_end, qstrlen( partial_line_end ) ); 243 for (int i = 0; i < 20; i++) { 244 snprintf(newLine, 89, sl_format, i); 245 file.write( newLine, qstrlen(newLine) ); 246 } 247 // To test the edge case when the final line is not complete and not matching 248 file.write( partial_nonmatching_line_begin, 249 qstrlen( partial_nonmatching_line_begin ) ); 250 } 251 file.close(); 252 253 // Start an update search 254 filteredData->updateSearch(); 255 256 for ( int i = 0; i < 3; i++ ) 257 QApplication::exec(); 258 259 // Check the result 260 QCOMPARE( logData.getNbLine(), 5022LL ); 261 QCOMPARE( filteredData->getNbLine(), 26LL ); 262 QCOMPARE( progressSpy.count(), 6 ); 263 264 // Now test the case where a match is found at the end of an updated line. 265 if ( file.open( QIODevice::Append ) ) { 266 file.write( partial_line_end, qstrlen( partial_line_end ) ); 267 for (int i = 0; i < 20; i++) { 268 snprintf(newLine, 89, sl_format, i); 269 file.write( newLine, qstrlen(newLine) ); 270 } 271 } 272 file.close(); 273 274 // Start an update search 275 filteredData->updateSearch(); 276 277 for ( int i = 0; i < 3; i++ ) 278 QApplication::exec(); 279 280 // Check the result 281 QCOMPARE( logData.getNbLine(), 5042LL ); 282 QCOMPARE( filteredData->getNbLine(), 27LL ); 283 QCOMPARE( progressSpy.count(), 8 ); 284 } 285 286 // 287 // Private functions 288 // 289 void TestLogFilteredData::loadingFinished() 290 { 291 QApplication::quit(); 292 } 293 294 void TestLogFilteredData::searchProgressed( int nbMatches, int completion ) 295 { 296 QApplication::quit(); 297 } 298 299 bool TestLogFilteredData::generateDataFiles() 300 { 301 char newLine[90]; 302 303 QFile file( TMPDIR "/mediumlog.txt" ); 304 if ( file.open( QIODevice::WriteOnly ) ) { 305 for (int i = 0; i < ML_NB_LINES; i++) { 306 snprintf(newLine, 89, ml_format, i); 307 file.write( newLine, qstrlen(newLine) ); 308 } 309 } 310 else { 311 return false; 312 } 313 file.close(); 314 315 file.setFileName( TMPDIR "/smalllog.txt" ); 316 if ( file.open( QIODevice::WriteOnly ) ) { 317 for (int i = 0; i < SL_NB_LINES; i++) { 318 snprintf(newLine, 89, sl_format, i); 319 file.write( newLine, qstrlen(newLine) ); 320 } 321 } 322 else { 323 return false; 324 } 325 file.close(); 326 327 return true; 328 } 329