xref: /glogg/tests/testlogfiltereddata.cpp (revision 7ab70cd2ae046c1ee4ded510a91a615878aff320) !
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