Make a branch to make krunner Good Enough For Aaron™.
[kdebase/uwolfer.git] / apps / lib / konq / tests / konqfileundomanagertest.cpp
blob74700fd2fabf85f186f7479b664ac550e6d6d041
1 /* This file is part of KDE
2 Copyright (c) 2006 David Faure <faure@kde.org>
4 This library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Library General Public
6 License as published by the Free Software Foundation; either
7 version 2 of the License, or (at your option) any later version.
9 This library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Library General Public License for more details.
14 You should have received a copy of the GNU Library General Public License
15 along with this library; see the file COPYING.LIB. If not, write to
16 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 Boston, MA 02110-1301, USA.
20 #include <qtest_kde.h>
22 #include "konqfileundomanagertest.h"
23 #include <konq_fileundomanager.h>
25 #include <kio/copyjob.h>
26 #include <kio/job.h>
27 #include <kio/deletejob.h>
28 #include <kio/netaccess.h>
29 #include <kprotocolinfo.h>
31 #include <kde_file.h>
32 #include <kdebug.h>
33 #include <kconfig.h>
34 #include <kconfiggroup.h>
36 #include <errno.h>
37 #include <utime.h>
38 #include <time.h>
39 #include <sys/time.h>
41 #include "konqfileundomanagertest.moc"
43 QTEST_KDEMAIN( KonqFileUndoManagerTest, NoGUI )
45 static QString homeTmpDir() { return QFile::decodeName( getenv( "KDEHOME" ) ) + "/jobtest/"; }
46 static QString destDir() { return homeTmpDir() + "destdir/"; }
48 static QString srcFile() { return homeTmpDir() + "testfile"; }
49 static QString destFile() { return destDir() + "testfile"; }
51 #ifndef Q_WS_WIN
52 static QString srcLink() { return homeTmpDir() + "symlink"; }
53 static QString destLink() { return destDir() + "symlink"; }
54 #endif
56 static QString srcSubDir() { return homeTmpDir() + "subdir"; }
57 static QString destSubDir() { return destDir() + "subdir"; }
59 static KUrl::List sourceList()
61 KUrl::List lst;
62 lst << KUrl( srcFile() );
63 #ifndef Q_WS_WIN
64 lst << KUrl( srcLink() );
65 #endif
66 return lst;
69 static void createTestFile( const QString& path, const char* contents )
71 QFile f( path );
72 if ( !f.open( QIODevice::WriteOnly ) )
73 kFatal() << "Can't create " << path ;
74 f.write( QByteArray( contents ) );
75 f.close();
78 static void createTestSymlink( const QString& path )
80 // Create symlink if it doesn't exist yet
81 KDE_struct_stat buf;
82 if ( KDE_lstat( QFile::encodeName( path ), &buf ) != 0 ) {
83 bool ok = symlink( "/IDontExist", QFile::encodeName( path ) ) == 0; // broken symlink
84 if ( !ok )
85 kFatal() << "couldn't create symlink: " << strerror( errno ) ;
86 QVERIFY( KDE_lstat( QFile::encodeName( path ), &buf ) == 0 );
87 QVERIFY( S_ISLNK( buf.st_mode ) );
88 } else {
89 QVERIFY( S_ISLNK( buf.st_mode ) );
91 qDebug( "symlink %s created", qPrintable( path ) );
92 QVERIFY( QFileInfo( path ).isSymLink() );
95 static void checkTestDirectory( const QString& path )
97 QVERIFY( QFileInfo( path ).isDir() );
98 QVERIFY( QFileInfo( path + "/fileindir" ).isFile() );
99 #ifndef Q_WS_WIN
100 QVERIFY( QFileInfo( path + "/testlink" ).isSymLink() );
101 #endif
102 QVERIFY( QFileInfo( path + "/dirindir" ).isDir() );
103 QVERIFY( QFileInfo( path + "/dirindir/nested" ).isFile() );
106 static void createTestDirectory( const QString& path )
108 QDir dir;
109 bool ok = dir.mkdir( path );
110 if ( !ok )
111 kFatal() << "couldn't create " << path ;
112 createTestFile( path + "/fileindir", "File in dir" );
113 #ifndef Q_WS_WIN
114 createTestSymlink( path + "/testlink" );
115 #endif
116 ok = dir.mkdir( path + "/dirindir" );
117 if ( !ok )
118 kFatal() << "couldn't create " << path ;
119 createTestFile( path + "/dirindir/nested", "Nested" );
120 checkTestDirectory( path );
123 class TestUiInterface : public KonqFileUndoManager::UiInterface
125 public:
126 TestUiInterface() : KonqFileUndoManager::UiInterface(0), m_nextReplyToConfirmDeletion(true) {
127 setShowProgressInfo( false );
129 virtual void jobError( KIO::Job* job ) {
130 kFatal() << job->errorString() ;
132 virtual bool copiedFileWasModified( const KUrl& src, const KUrl& dest, time_t srcTime, time_t destTime ) {
133 Q_UNUSED( src );
134 m_dest = dest;
135 Q_UNUSED( srcTime );
136 Q_UNUSED( destTime );
137 return true;
139 virtual bool confirmDeletion( const KUrl::List& files ) {
140 m_files = files;
141 return m_nextReplyToConfirmDeletion;
143 void setNextReplyToConfirmDeletion( bool b ) {
144 m_nextReplyToConfirmDeletion = b;
146 KUrl::List files() const { return m_files; }
147 KUrl dest() const { return m_dest; }
148 void clear() {
149 m_dest = KUrl();
150 m_files.clear();
152 private:
153 bool m_nextReplyToConfirmDeletion;
154 KUrl m_dest;
155 KUrl::List m_files;
158 void KonqFileUndoManagerTest::initTestCase()
160 qDebug( "initTestCase" );
162 // Get kio_trash to share our environment so that it writes trashrc to the right kdehome
163 setenv( "KDE_FORK_SLAVES", "yes", true );
165 // Start with a clean base dir
166 cleanupTestCase();
168 QDir dir; // TT: why not a static method?
169 if ( !QFile::exists( homeTmpDir() ) ) {
170 bool ok = dir.mkdir( homeTmpDir() );
171 if ( !ok )
172 kFatal() << "Couldn't create " << homeTmpDir() ;
175 createTestFile( srcFile(), "Hello world" );
176 #ifndef Q_WS_WIN
177 createTestSymlink( srcLink() );
178 #endif
179 createTestDirectory( srcSubDir() );
181 QDir().mkdir( destDir() );
182 QVERIFY( QFileInfo( destDir() ).isDir() );
184 QVERIFY( !KonqFileUndoManager::self()->undoAvailable() );
185 m_uiInterface = new TestUiInterface; // owned by KonqFileUndoManager
186 KonqFileUndoManager::self()->setUiInterface( m_uiInterface );
189 void KonqFileUndoManagerTest::cleanupTestCase()
191 KIO::Job* job = KIO::del( KUrl::fromPath( homeTmpDir() ), KIO::HideProgressInfo );
192 KIO::NetAccess::synchronousRun( job, 0 );
195 void KonqFileUndoManagerTest::doUndo()
197 QEventLoop eventLoop;
198 bool ok = connect( KonqFileUndoManager::self(), SIGNAL( undoJobFinished() ),
199 &eventLoop, SLOT( quit() ) );
200 QVERIFY( ok );
202 KonqFileUndoManager::self()->undo();
203 eventLoop.exec(QEventLoop::ExcludeUserInputEvents); // wait for undo job to finish
206 void KonqFileUndoManagerTest::testCopyFiles()
208 kDebug() ;
209 // Initially inspired from JobTest::copyFileToSamePartition()
210 const QString destdir = destDir();
211 KUrl::List lst = sourceList();
212 const KUrl d( destdir );
213 KIO::CopyJob* job = KIO::copy( lst, d, KIO::HideProgressInfo );
214 job->setUiDelegate( 0 );
215 KonqFileUndoManager::self()->recordJob( KonqFileUndoManager::COPY, lst, d, job );
217 QSignalSpy spyUndoAvailable( KonqFileUndoManager::self(), SIGNAL(undoAvailable(bool)) );
218 QVERIFY( spyUndoAvailable.isValid() );
219 QSignalSpy spyTextChanged( KonqFileUndoManager::self(), SIGNAL(undoTextChanged(QString)) );
220 QVERIFY( spyTextChanged.isValid() );
222 bool ok = KIO::NetAccess::synchronousRun( job, 0 );
223 QVERIFY( ok );
225 QVERIFY( QFile::exists( destFile() ) );
226 #ifndef Q_WS_WIN
227 // Don't use QFile::exists, it's a broken symlink...
228 QVERIFY( QFileInfo( destLink() ).isSymLink() );
229 #endif
231 // might have to wait for dbus signal here... but this is currently disabled.
232 //QTest::qWait( 20 );
233 QVERIFY( KonqFileUndoManager::self()->undoAvailable() );
234 QCOMPARE( spyUndoAvailable.count(), 1 );
235 QCOMPARE( spyTextChanged.count(), 1 );
236 m_uiInterface->clear();
238 m_uiInterface->setNextReplyToConfirmDeletion( false ); // act like the user didn't confirm
239 KonqFileUndoManager::self()->undo();
240 QCOMPARE( m_uiInterface->files().count(), 1 ); // confirmDeletion was called
241 QCOMPARE( m_uiInterface->files()[0].url(), KUrl(destFile()).url() );
242 QVERIFY( QFile::exists( destFile() ) ); // nothing happened yet
244 // OK, now do it
245 m_uiInterface->clear();
246 m_uiInterface->setNextReplyToConfirmDeletion( true );
247 doUndo();
249 QVERIFY( !KonqFileUndoManager::self()->undoAvailable() );
250 QVERIFY( spyUndoAvailable.count() >= 2 ); // it's in fact 3, due to lock/unlock emitting it as well
251 QCOMPARE( spyTextChanged.count(), 2 );
252 QCOMPARE( m_uiInterface->files().count(), 1 ); // confirmDeletion was called
253 QCOMPARE( m_uiInterface->files()[0].url(), KUrl(destFile()).url() );
255 // Check that undo worked
256 QVERIFY( !QFile::exists( destFile() ) );
257 #ifndef Q_WS_WIN
258 QVERIFY( !QFile::exists( destLink() ) );
259 QVERIFY( !QFileInfo( destLink() ).isSymLink() );
260 #endif
263 void KonqFileUndoManagerTest::testMoveFiles()
265 kDebug() ;
266 const QString destdir = destDir();
267 KUrl::List lst = sourceList();
268 const KUrl d( destdir );
269 KIO::CopyJob* job = KIO::move( lst, d, KIO::HideProgressInfo );
270 job->setUiDelegate( 0 );
271 KonqFileUndoManager::self()->recordJob( KonqFileUndoManager::MOVE, lst, d, job );
273 bool ok = KIO::NetAccess::synchronousRun( job, 0 );
274 QVERIFY( ok );
276 QVERIFY( !QFile::exists( srcFile() ) ); // the source moved
277 QVERIFY( QFile::exists( destFile() ) );
278 #ifndef Q_WS_WIN
279 QVERIFY( !QFileInfo( srcLink() ).isSymLink() );
280 // Don't use QFile::exists, it's a broken symlink...
281 QVERIFY( QFileInfo( destLink() ).isSymLink() );
282 #endif
284 doUndo();
286 QVERIFY( QFile::exists( srcFile() ) ); // the source is back
287 QVERIFY( !QFile::exists( destFile() ) );
288 #ifndef Q_WS_WIN
289 QVERIFY( QFileInfo( srcLink() ).isSymLink() );
290 QVERIFY( !QFileInfo( destLink() ).isSymLink() );
291 #endif
294 // Testing for overwrite isn't possible, because non-interactive jobs never overwrite.
295 // And nothing different happens anyway, the dest is removed...
296 #if 0
297 void KonqFileUndoManagerTest::testCopyFilesOverwrite()
299 kDebug() ;
300 // Create a different file in the destdir
301 createTestFile( destFile(), "An old file already in the destdir" );
303 testCopyFiles();
305 #endif
307 void KonqFileUndoManagerTest::testCopyDirectory()
309 const QString destdir = destDir();
310 KUrl::List lst; lst << srcSubDir();
311 const KUrl d( destdir );
312 KIO::CopyJob* job = KIO::copy( lst, d, KIO::HideProgressInfo );
313 job->setUiDelegate( 0 );
314 KonqFileUndoManager::self()->recordJob( KonqFileUndoManager::COPY, lst, d, job );
316 bool ok = KIO::NetAccess::synchronousRun( job, 0 );
317 QVERIFY( ok );
319 checkTestDirectory( srcSubDir() ); // src untouched
320 checkTestDirectory( destSubDir() );
322 doUndo();
324 checkTestDirectory( srcSubDir() );
325 QVERIFY( !QFile::exists( destSubDir() ) );
328 void KonqFileUndoManagerTest::testMoveDirectory()
330 const QString destdir = destDir();
331 KUrl::List lst; lst << srcSubDir();
332 const KUrl d( destdir );
333 KIO::CopyJob* job = KIO::move( lst, d, KIO::HideProgressInfo );
334 job->setUiDelegate( 0 );
335 KonqFileUndoManager::self()->recordJob( KonqFileUndoManager::MOVE, lst, d, job );
337 bool ok = KIO::NetAccess::synchronousRun( job, 0 );
338 QVERIFY( ok );
340 QVERIFY( !QFile::exists( srcSubDir() ) );
341 checkTestDirectory( destSubDir() );
343 doUndo();
345 checkTestDirectory( srcSubDir() );
346 QVERIFY( !QFile::exists( destSubDir() ) );
349 void KonqFileUndoManagerTest::testRenameFile()
351 const KUrl oldUrl( srcFile() );
352 const KUrl newUrl( srcFile() + ".new" );
353 KUrl::List lst;
354 lst.append(oldUrl);
355 KIO::Job* job = KIO::moveAs( oldUrl, newUrl, KIO::HideProgressInfo );
356 job->setUiDelegate( 0 );
357 KonqFileUndoManager::self()->recordJob( KonqFileUndoManager::RENAME, lst, newUrl, job );
359 bool ok = KIO::NetAccess::synchronousRun( job, 0 );
360 QVERIFY( ok );
362 QVERIFY( !QFile::exists( srcFile() ) );
363 QVERIFY( QFileInfo( newUrl.path() ).isFile() );
365 doUndo();
367 QVERIFY( QFile::exists( srcFile() ) );
368 QVERIFY( !QFileInfo( newUrl.path() ).isFile() );
371 void KonqFileUndoManagerTest::testRenameDir()
373 const KUrl oldUrl( srcSubDir() );
374 const KUrl newUrl( srcSubDir() + ".new" );
375 KUrl::List lst;
376 lst.append(oldUrl);
377 KIO::Job* job = KIO::moveAs( oldUrl, newUrl, KIO::HideProgressInfo );
378 job->setUiDelegate( 0 );
379 KonqFileUndoManager::self()->recordJob( KonqFileUndoManager::RENAME, lst, newUrl, job );
381 bool ok = KIO::NetAccess::synchronousRun( job, 0 );
382 QVERIFY( ok );
384 QVERIFY( !QFile::exists( srcSubDir() ) );
385 QVERIFY( QFileInfo( newUrl.path() ).isDir() );
387 doUndo();
389 QVERIFY( QFile::exists( srcSubDir() ) );
390 QVERIFY( !QFileInfo( newUrl.path() ).isDir() );
393 void KonqFileUndoManagerTest::testCreateDir()
395 const KUrl url( srcSubDir() + ".mkdir" );
396 const QString path = url.path();
397 QVERIFY( !QFile::exists(path) );
399 KIO::SimpleJob* job = KIO::mkdir(url);
400 job->setUiDelegate( 0 );
401 KonqFileUndoManager::self()->recordJob( KonqFileUndoManager::MKDIR, KUrl(), url, job );
402 bool ok = KIO::NetAccess::synchronousRun( job, 0 );
403 QVERIFY( ok );
404 QVERIFY( QFile::exists(path) );
405 QVERIFY( QFileInfo(path).isDir() );
407 m_uiInterface->clear();
408 m_uiInterface->setNextReplyToConfirmDeletion( false ); // act like the user didn't confirm
409 KonqFileUndoManager::self()->undo();
410 QCOMPARE( m_uiInterface->files().count(), 1 ); // confirmDeletion was called
411 QCOMPARE( m_uiInterface->files()[0].url(), url.url() );
412 QVERIFY( QFile::exists(path) ); // nothing happened yet
414 // OK, now do it
415 m_uiInterface->clear();
416 m_uiInterface->setNextReplyToConfirmDeletion( true );
417 doUndo();
419 QVERIFY( !QFile::exists(path) );
422 void KonqFileUndoManagerTest::testTrashFiles()
424 if ( !KProtocolInfo::isKnownProtocol( "trash" ) )
425 QSKIP( "kio_trash not installed", SkipAll );
427 // Trash it all at once: the file, the symlink, the subdir.
428 KUrl::List lst = sourceList();
429 lst.append( srcSubDir() );
430 KIO::Job* job = KIO::trash( lst, KIO::HideProgressInfo );
431 job->setUiDelegate( 0 );
432 KonqFileUndoManager::self()->recordJob( KonqFileUndoManager::TRASH, lst, KUrl("trash:/"), job );
434 bool ok = KIO::NetAccess::synchronousRun( job, 0 );
435 QVERIFY( ok );
437 // Check that things got removed
438 QVERIFY( !QFile::exists( srcFile() ) );
439 #ifndef Q_WS_WIN
440 QVERIFY( !QFileInfo( srcLink() ).isSymLink() );
441 #endif
442 QVERIFY( !QFile::exists( srcSubDir() ) );
444 // check trash?
445 // Let's just check that it's not empty. kio_trash has its own unit tests anyway.
446 KConfig cfg( "trashrc", KConfig::SimpleConfig );
447 QVERIFY( cfg.hasGroup( "Status" ) );
448 QCOMPARE( cfg.group("Status").readEntry( "Empty", true ), false );
450 doUndo();
452 QVERIFY( QFile::exists( srcFile() ) );
453 #ifndef Q_WS_WIN
454 QVERIFY( QFileInfo( srcLink() ).isSymLink() );
455 #endif
456 QVERIFY( QFile::exists( srcSubDir() ) );
458 // We can't check that the trash is empty; other partitions might have their own trash
461 static void setTimeStamp( const QString& path )
463 #ifdef Q_OS_UNIX
464 // Put timestamp in the past so that we can check that the
465 // copy actually preserves it.
466 struct timeval tp;
467 gettimeofday( &tp, 0 );
468 struct utimbuf utbuf;
469 utbuf.actime = tp.tv_sec + 30; // 30 seconds in the future
470 utbuf.modtime = tp.tv_sec + 60; // 60 second in the future
471 utime( QFile::encodeName( path ), &utbuf );
472 qDebug( "Time changed for %s", qPrintable( path ) );
473 #endif
476 void KonqFileUndoManagerTest::testModifyFileBeforeUndo()
478 // based on testCopyDirectory (so that we check that it works for files in subdirs too)
479 const QString destdir = destDir();
480 KUrl::List lst; lst << srcSubDir();
481 const KUrl d( destdir );
482 KIO::CopyJob* job = KIO::copy( lst, d, KIO::HideProgressInfo );
483 job->setUiDelegate( 0 );
484 KonqFileUndoManager::self()->recordJob( KonqFileUndoManager::COPY, lst, d, job );
486 bool ok = KIO::NetAccess::synchronousRun( job, 0 );
487 QVERIFY( ok );
489 checkTestDirectory( srcSubDir() ); // src untouched
490 checkTestDirectory( destSubDir() );
491 const QString destFile = destSubDir() + "/fileindir";
492 setTimeStamp( destFile ); // simulate a modification of the file
494 doUndo();
496 // Check that TestUiInterface::copiedFileWasModified got called
497 QCOMPARE( m_uiInterface->dest().path(), destFile );
499 checkTestDirectory( srcSubDir() );
500 QVERIFY( !QFile::exists( destSubDir() ) );
504 // TODO: add test (and fix bug) for DND of remote urls / "Link here" (creates .desktop files) // Undo (doesn't do anything)
505 // TODO: add test for interrupting a moving operation and then using Undo - bug:91579