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