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 QWARN("Starting stage 2"); 214 215 // Add some data to the file 216 char newLine[90]; 217 QFile file( TMPDIR "/smalllog.txt" ); 218 if ( file.open( QIODevice::Append ) ) { 219 for (int i = 0; i < 3000; i++) { 220 snprintf(newLine, 89, sl_format, i); 221 file.write( newLine, qstrlen(newLine) ); 222 } 223 // To test the edge case when the final line is not complete and matching 224 file.write( partial_line_begin, qstrlen( partial_line_begin ) ); 225 } 226 file.close(); 227 228 // Let the system do the update 229 QApplication::exec(); 230 231 // Start an update search 232 filteredData->updateSearch(); 233 234 for ( int i = 0; i < 2; i++ ) 235 QApplication::exec(); 236 237 // Check the result 238 QCOMPARE( logData.getNbLine(), 5001LL ); 239 QCOMPARE( filteredData->getNbLine(), 26LL ); 240 QCOMPARE( progressSpy.count(), 4 ); 241 242 QWARN("Starting stage 3"); 243 244 // Add a couple more lines, including the end of the unfinished one. 245 if ( file.open( QIODevice::Append ) ) { 246 file.write( partial_line_end, qstrlen( partial_line_end ) ); 247 for (int i = 0; i < 20; i++) { 248 snprintf(newLine, 89, sl_format, i); 249 file.write( newLine, qstrlen(newLine) ); 250 } 251 // To test the edge case when the final line is not complete and not matching 252 file.write( partial_nonmatching_line_begin, 253 qstrlen( partial_nonmatching_line_begin ) ); 254 } 255 file.close(); 256 257 // Start an update search 258 filteredData->updateSearch(); 259 260 for ( int i = 0; i < 3; i++ ) 261 QApplication::exec(); 262 263 // Check the result 264 QCOMPARE( logData.getNbLine(), 5022LL ); 265 QCOMPARE( filteredData->getNbLine(), 26LL ); 266 QCOMPARE( progressSpy.count(), 6 ); 267 268 QWARN("Starting stage 4"); 269 270 // Now test the case where a match is found at the end of an updated line. 271 if ( file.open( QIODevice::Append ) ) { 272 file.write( partial_line_end, qstrlen( partial_line_end ) ); 273 for (int i = 0; i < 20; i++) { 274 snprintf(newLine, 89, sl_format, i); 275 file.write( newLine, qstrlen(newLine) ); 276 } 277 } 278 file.close(); 279 280 // Start an update search 281 filteredData->updateSearch(); 282 283 for ( int i = 0; i < 3; i++ ) 284 QApplication::exec(); 285 286 // Check the result 287 QCOMPARE( logData.getNbLine(), 5042LL ); 288 QCOMPARE( filteredData->getNbLine(), 27LL ); 289 QCOMPARE( progressSpy.count(), 8 ); 290 } 291 292 // 293 // Private functions 294 // 295 void TestLogFilteredData::loadingFinished() 296 { 297 QApplication::quit(); 298 } 299 300 void TestLogFilteredData::searchProgressed( int nbMatches, int completion ) 301 { 302 QApplication::quit(); 303 } 304 305 bool TestLogFilteredData::generateDataFiles() 306 { 307 char newLine[90]; 308 309 QFile file( TMPDIR "/mediumlog.txt" ); 310 if ( file.open( QIODevice::WriteOnly ) ) { 311 for (int i = 0; i < ML_NB_LINES; i++) { 312 snprintf(newLine, 89, ml_format, i); 313 file.write( newLine, qstrlen(newLine) ); 314 } 315 } 316 else { 317 return false; 318 } 319 file.close(); 320 321 file.setFileName( TMPDIR "/smalllog.txt" ); 322 if ( file.open( QIODevice::WriteOnly ) ) { 323 for (int i = 0; i < SL_NB_LINES; i++) { 324 snprintf(newLine, 89, sl_format, i); 325 file.write( newLine, qstrlen(newLine) ); 326 } 327 } 328 else { 329 return false; 330 } 331 file.close(); 332 333 return true; 334 } 335