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>
27 #include <kio/deletejob.h>
28 #include <kio/netaccess.h>
29 #include <kprotocolinfo.h>
34 #include <kconfiggroup.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"; }
52 static QString
srcLink() { return homeTmpDir() + "symlink"; }
53 static QString
destLink() { return destDir() + "symlink"; }
56 static QString
srcSubDir() { return homeTmpDir() + "subdir"; }
57 static QString
destSubDir() { return destDir() + "subdir"; }
59 static KUrl::List
sourceList()
62 lst
<< KUrl( srcFile() );
64 lst
<< KUrl( srcLink() );
69 static void createTestFile( const QString
& path
, const char* contents
)
72 if ( !f
.open( QIODevice::WriteOnly
) )
73 kFatal() << "Can't create " << path
;
74 f
.write( QByteArray( contents
) );
78 static void createTestSymlink( const QString
& path
)
80 // Create symlink if it doesn't exist yet
82 if ( KDE_lstat( QFile::encodeName( path
), &buf
) != 0 ) {
83 bool ok
= symlink( "/IDontExist", QFile::encodeName( path
) ) == 0; // broken symlink
85 kFatal() << "couldn't create symlink: " << strerror( errno
) ;
86 QVERIFY( KDE_lstat( QFile::encodeName( path
), &buf
) == 0 );
87 QVERIFY( S_ISLNK( buf
.st_mode
) );
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() );
100 QVERIFY( QFileInfo( path
+ "/testlink" ).isSymLink() );
102 QVERIFY( QFileInfo( path
+ "/dirindir" ).isDir() );
103 QVERIFY( QFileInfo( path
+ "/dirindir/nested" ).isFile() );
106 static void createTestDirectory( const QString
& path
)
109 bool ok
= dir
.mkdir( path
);
111 kFatal() << "couldn't create " << path
;
112 createTestFile( path
+ "/fileindir", "File in dir" );
114 createTestSymlink( path
+ "/testlink" );
116 ok
= dir
.mkdir( path
+ "/dirindir" );
118 kFatal() << "couldn't create " << path
;
119 createTestFile( path
+ "/dirindir/nested", "Nested" );
120 checkTestDirectory( path
);
123 class TestUiInterface
: public KonqFileUndoManager::UiInterface
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
) {
136 Q_UNUSED( destTime
);
139 virtual bool confirmDeletion( const KUrl::List
& 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
; }
153 bool m_nextReplyToConfirmDeletion
;
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
168 QDir dir
; // TT: why not a static method?
169 if ( !QFile::exists( homeTmpDir() ) ) {
170 bool ok
= dir
.mkdir( homeTmpDir() );
172 kFatal() << "Couldn't create " << homeTmpDir() ;
175 createTestFile( srcFile(), "Hello world" );
177 createTestSymlink( srcLink() );
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() ) );
202 KonqFileUndoManager::self()->undo();
203 eventLoop
.exec(QEventLoop::ExcludeUserInputEvents
); // wait for undo job to finish
206 void KonqFileUndoManagerTest::testCopyFiles()
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 );
225 QVERIFY( QFile::exists( destFile() ) );
227 // Don't use QFile::exists, it's a broken symlink...
228 QVERIFY( QFileInfo( destLink() ).isSymLink() );
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
245 m_uiInterface
->clear();
246 m_uiInterface
->setNextReplyToConfirmDeletion( true );
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() ) );
258 QVERIFY( !QFile::exists( destLink() ) );
259 QVERIFY( !QFileInfo( destLink() ).isSymLink() );
263 void KonqFileUndoManagerTest::testMoveFiles()
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 );
276 QVERIFY( !QFile::exists( srcFile() ) ); // the source moved
277 QVERIFY( QFile::exists( destFile() ) );
279 QVERIFY( !QFileInfo( srcLink() ).isSymLink() );
280 // Don't use QFile::exists, it's a broken symlink...
281 QVERIFY( QFileInfo( destLink() ).isSymLink() );
286 QVERIFY( QFile::exists( srcFile() ) ); // the source is back
287 QVERIFY( !QFile::exists( destFile() ) );
289 QVERIFY( QFileInfo( srcLink() ).isSymLink() );
290 QVERIFY( !QFileInfo( destLink() ).isSymLink() );
294 // Testing for overwrite isn't possible, because non-interactive jobs never overwrite.
295 // And nothing different happens anyway, the dest is removed...
297 void KonqFileUndoManagerTest::testCopyFilesOverwrite()
300 // Create a different file in the destdir
301 createTestFile( destFile(), "An old file already in the destdir" );
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 );
319 checkTestDirectory( srcSubDir() ); // src untouched
320 checkTestDirectory( destSubDir() );
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 );
340 QVERIFY( !QFile::exists( srcSubDir() ) );
341 checkTestDirectory( destSubDir() );
345 checkTestDirectory( srcSubDir() );
346 QVERIFY( !QFile::exists( destSubDir() ) );
349 void KonqFileUndoManagerTest::testRenameFile()
351 const KUrl
oldUrl( srcFile() );
352 const KUrl
newUrl( srcFile() + ".new" );
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 );
362 QVERIFY( !QFile::exists( srcFile() ) );
363 QVERIFY( QFileInfo( newUrl
.path() ).isFile() );
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" );
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 );
384 QVERIFY( !QFile::exists( srcSubDir() ) );
385 QVERIFY( QFileInfo( newUrl
.path() ).isDir() );
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 );
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
415 m_uiInterface
->clear();
416 m_uiInterface
->setNextReplyToConfirmDeletion( true );
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 );
437 // Check that things got removed
438 QVERIFY( !QFile::exists( srcFile() ) );
440 QVERIFY( !QFileInfo( srcLink() ).isSymLink() );
442 QVERIFY( !QFile::exists( srcSubDir() ) );
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 );
452 QVERIFY( QFile::exists( srcFile() ) );
454 QVERIFY( QFileInfo( srcLink() ).isSymLink() );
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
)
464 // Put timestamp in the past so that we can check that the
465 // copy actually preserves it.
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
) );
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 );
489 checkTestDirectory( srcSubDir() ); // src untouched
490 checkTestDirectory( destSubDir() );
491 const QString destFile
= destSubDir() + "/fileindir";
492 setTimeStamp( destFile
); // simulate a modification of the file
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