diff --git a/src/core/support/Amarok.cpp b/src/core/support/Amarok.cpp index 791cff0819..513db86729 100644 --- a/src/core/support/Amarok.cpp +++ b/src/core/support/Amarok.cpp @@ -1,428 +1,440 @@ /**************************************************************************************** * Copyright (c) 2002 Mark Kretschmann * * * * This program is free software; you can redistribute it and/or modify it under * * the terms of the GNU General Public License as published by the Free Software * * Foundation; either version 2 of the License, or (at your option) any later * * version. * * * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * * PARTICULAR PURPOSE. See the GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License along with * * this program. If not, see . * ****************************************************************************************/ #include "core/support/Amarok.h" #include "core/meta/Meta.h" #include "core/meta/support/MetaUtility.h" #include "core/capabilities/SourceInfoCapability.h" #include "core/playlists/PlaylistFormat.h" #include #include #include #include #include #include #include #include #include #include #include QWeakPointer Amarok::actionCollectionObject; QMutex Amarok::globalDirsMutex; namespace Amarok { // TODO: sometimes we have a playcount but no valid datetime. // in such a case we should maybe display "Unknown" and not "Never" QString verboseTimeSince( const QDateTime &datetime ) { if( datetime.isNull() || !datetime.toTime_t() ) return i18nc( "The amount of time since last played", "Never" ); const QDateTime now = QDateTime::currentDateTime(); const int datediff = datetime.daysTo( now ); // HACK: Fix 203522. Arithmetic overflow? // Getting weird values from Plasma::DataEngine (LAST_PLAYED field). if( datediff < 0 ) return i18nc( "When this track was last played", "Unknown" ); if( datediff >= 6*7 /*six weeks*/ ) { // return absolute month/year const KCalendarSystem *cal = KGlobal::locale()->calendar(); const QDate date = datetime.date(); return i18nc( "monthname year", "%1 %2", cal->monthName(date), cal->formatDate( date, KLocale::Year, KLocale::LongNumber ) ); } //TODO "last week" = maybe within 7 days, but prolly before last Sunday if( datediff >= 7 ) // return difference in weeks return i18np( "One week ago", "%1 weeks ago", (datediff+3)/7 ); const int timediff = datetime.secsTo( now ); if( timediff >= 24*60*60 /*24 hours*/ ) // return difference in days return datediff == 1 ? i18n( "Yesterday" ) : i18np( "One day ago", "%1 days ago", (timediff+12*60*60)/(24*60*60) ); if( timediff >= 90*60 /*90 minutes*/ ) // return difference in hours return i18np( "One hour ago", "%1 hours ago", (timediff+30*60)/(60*60) ); //TODO are we too specific here? Be more fuzzy? ie, use units of 5 minutes, or "Recently" if( timediff >= 0 ) // return difference in minutes return timediff/60 ? i18np( "One minute ago", "%1 minutes ago", (timediff+30)/60 ) : i18n( "Within the last minute" ); return i18n( "The future" ); } QString verboseTimeSince( uint time_t ) { if( !time_t ) return i18nc( "The amount of time since last played", "Never" ); QDateTime dt; dt.setTime_t( time_t ); return verboseTimeSince( dt ); } QString conciseTimeSince( uint time_t ) { if( !time_t ) return i18nc( "The amount of time since last played", "0" ); QDateTime datetime; datetime.setTime_t( time_t ); const QDateTime now = QDateTime::currentDateTime(); const int datediff = datetime.daysTo( now ); if( datediff >= 6*7 /*six weeks*/ ) { // return difference in months return i18nc( "number of months ago", "%1M", datediff/7/4 ); } if( datediff >= 7 ) // return difference in weeks return i18nc( "w for weeks", "%1w", (datediff+3)/7 ); if( datediff == -1 ) return i18nc( "When this track was last played", "Tomorrow" ); const int timediff = datetime.secsTo( now ); if( timediff >= 24*60*60 /*24 hours*/ ) // return difference in days // xgettext: no-c-format return i18nc( "d for days", "%1d", (timediff+12*60*60)/(24*60*60) ); if( timediff >= 90*60 /*90 minutes*/ ) // return difference in hours return i18nc( "h for hours", "%1h", (timediff+30*60)/(60*60) ); //TODO are we too specific here? Be more fuzzy? ie, use units of 5 minutes, or "Recently" if( timediff >= 60 ) // return difference in minutes return QString("%1'").arg( ( timediff + 30 )/60 ); if( timediff >= 0 ) // return difference in seconds return QString("%1\"").arg( ( timediff + 1 )/60 ); return i18n( "0" ); } void manipulateThe( QString &str, bool reverse ) { if( reverse ) { if( !str.startsWith( "the ", Qt::CaseInsensitive ) ) return; QString begin = str.left( 3 ); str = str.append( ", %1" ).arg( begin ); str = str.mid( 4 ); return; } if( !str.endsWith( ", the", Qt::CaseInsensitive ) ) return; QString end = str.right( 3 ); str = str.prepend( "%1 " ).arg( end ); uint newLen = str.length() - end.length() - 2; str.truncate( newLen ); } QString generatePlaylistName( const Meta::TrackList tracks ) { QString datePart = KGlobal::locale()->formatDateTime( QDateTime::currentDateTime(), KLocale::ShortDate, true ); if( tracks.count() == 0 ) { return i18nc( "A saved playlist with the current time (KLocale::Shortdate) added between \ the parentheses", "Empty Playlist (%1)", datePart ); } bool singleArtist = true; bool singleAlbum = true; Meta::ArtistPtr artist = tracks.first()->artist(); Meta::AlbumPtr album = tracks.first()->album(); QString artistPart; QString albumPart; foreach( const Meta::TrackPtr track, tracks ) { if( artist != track->artist() ) singleArtist = false; if( album != track->album() ) singleAlbum = false; if ( !singleArtist && !singleAlbum ) break; } if( ( !singleArtist && !singleAlbum ) || ( !artist && !album ) ) return i18nc( "A saved playlist with the current time (KLocale::Shortdate) added between \ the parentheses", "Various Tracks (%1)", datePart ); if( singleArtist ) { if( artist ) artistPart = artist->prettyName(); else artistPart = i18n( "Unknown Artist(s)" ); } else if( album && album->hasAlbumArtist() && singleAlbum ) { artistPart = album->albumArtist()->prettyName(); } else { artistPart = i18n( "Various Artists" ); } if( singleAlbum ) { if( album ) albumPart = album->prettyName(); else albumPart = i18n( "Unknown Album(s)" ); } else { albumPart = i18n( "Various Albums" ); } return i18nc( "A saved playlist titled - ", "%1 - %2", artistPart, albumPart ); } KActionCollection* actionCollection() // TODO: constify? { if( !actionCollectionObject ) { actionCollectionObject = new KActionCollection( kapp ); actionCollectionObject.data()->setObjectName( "Amarok-KActionCollection" ); } return actionCollectionObject.data(); } KConfigGroup config( const QString &group ) { //Slightly more useful config() that allows setting the group simultaneously return KGlobal::config()->group( group ); } namespace ColorScheme { QColor Base; QColor Text; QColor Background; QColor Foreground; QColor AltBase; } OverrideCursor::OverrideCursor( Qt::CursorShape cursor ) { QApplication::setOverrideCursor( cursor == Qt::WaitCursor ? Qt::WaitCursor : Qt::BusyCursor ); } OverrideCursor::~OverrideCursor() { QApplication::restoreOverrideCursor(); } QString saveLocation( const QString &directory ) { globalDirsMutex.lock(); QString result = KGlobal::dirs()->saveLocation( "data", QString("amarok/") + directory, true ); globalDirsMutex.unlock(); return result; } QString defaultPlaylistPath() { return Amarok::saveLocation() + QLatin1String("current.xspf"); } QString cleanPath( const QString &path ) { /* Unicode uses combining characters to form accented versions of other characters. * (Exception: Latin-1 table for compatibility with ASCII.) * Those can be found in the Unicode tables listed at: * http://en.wikipedia.org/w/index.php?title=Combining_character&oldid=255990982 * Removing those characters removes accents. :) */ QString result = path; // German umlauts result.replace( QChar(0x00e4), "ae" ).replace( QChar(0x00c4), "Ae" ); result.replace( QChar(0x00f6), "oe" ).replace( QChar(0x00d6), "Oe" ); result.replace( QChar(0x00fc), "ue" ).replace( QChar(0x00dc), "Ue" ); result.replace( QChar(0x00df), "ss" ); // other special cases result.replace( QChar(0x00C6), "AE" ); result.replace( QChar(0x00E6), "ae" ); result.replace( QChar(0x00D8), "OE" ); result.replace( QChar(0x00F8), "oe" ); // normalize in a form where accents are separate characters result = result.normalized( QString::NormalizationForm_D ); // remove accents from table "Combining Diacritical Marks" for( int i = 0x0300; i <= 0x036F; i++ ) { result.remove( QChar( i ) ); } return result; } QString asciiPath( const QString &path ) { QString result = path; for( int i = 0; i < result.length(); i++ ) { QChar c = result[ i ]; if( c > QChar(0x7f) || c == QChar(0) ) { c = '_'; } result[ i ] = c; } return result; } QString vfatPath( const QString &path, PathSeparatorBehaviour behaviour ) { QString s = path; - if( behaviour == AutoBehaviour ) - behaviour = ( QDir::separator() == '/' ) ? UnixBehaviour : WindowsBehaviour; + QChar separator = ( behaviour == AutoBehaviour ) ? QDir::separator() : ( behaviour == UnixBehaviour ) ? '/' : '\\'; if( behaviour == UnixBehaviour ) // we are on *nix, \ is a valid character in file or directory names, NOT the dir separator s.replace( '\\', '_' ); else s.replace( '/', '_' ); // on windows we have to replace / instead int start = 0; #ifdef Q_OS_WIN // exclude the leading "C:/" from special character replecement in the loop below // bug 279560, bug 302251 if( QDir::isAbsolutePath( s ) ) start = 3; #endif for( int i = start; i < s.length(); i++ ) { QChar c = s[ i ]; if( c < QChar(0x20) || c == QChar(0x7F) // 0x7F = 127 = DEL control character || c=='*' || c=='?' || c=='<' || c=='>' || c=='|' || c=='"' || c==':' ) c = '_'; else if( c == '[' ) c = '('; else if ( c == ']' ) c = ')'; s[ i ] = c; } /* beware of reserved device names */ uint len = s.length(); if( len == 3 || (len > 3 && s[3] == '.') ) { QString l = s.left(3).toLower(); if( l=="aux" || l=="con" || l=="nul" || l=="prn" ) s = '_' + s; } else if( len == 4 || (len > 4 && s[4] == '.') ) { QString l = s.left(3).toLower(); QString d = s.mid(3,1); if( (l=="com" || l=="lpt") && (d=="0" || d=="1" || d=="2" || d=="3" || d=="4" || d=="5" || d=="6" || d=="7" || d=="8" || d=="9") ) s = '_' + s; } // "clock$" is only allowed WITH extension, according to: // http://en.wikipedia.org/w/index.php?title=Filename&oldid=303934888#Comparison_of_file_name_limitations if( QString::compare( s, "clock$", Qt::CaseInsensitive ) == 0 ) s = '_' + s; /* max path length of Windows API */ s = s.left(255); - /* whitespace at the end of folder/file names or extensions are bad */ + /* whitespace or dot at the end of folder/file names or extensions are bad */ len = s.length(); - if( s[len-1] == ' ' ) - s[len-1] = '_'; - - int extensionIndex = s.lastIndexOf( '.' ); // correct trailing spaces in file name itself - if( ( s.length() > 1 ) && ( extensionIndex > 0 ) ) - if( s.at( extensionIndex - 1 ) == ' ' ) - s[extensionIndex - 1] = '_'; + if( s.at(len - 1) == ' ' || s.at(len - 1) == '.' ) + s[len - 1] = '_'; for( int i = 1; i < s.length(); i++ ) // correct trailing whitespace in folder names { - if( ( s.at( i ) == QDir::separator() ) && ( s.at( i - 1 ) == ' ' ) ) + if( s.at(i) == separator && s.at(i - 1) == ' ' ) + s[i - 1] = '_'; + } + + for( int i = 1; i < s.length(); i++ ) // correct trailing dot in folder names, excluding . and .. + { + if( s.at(i) == separator + && s.at(i - 1) == '.' + && !( i == 1 // ./any + || ( i == 2 && s.at(i - 2) == '.' ) // ../any + || ( i >= 2 && s.at(i - 2) == separator ) // any/./any + || ( i >= 3 && s.at(i - 3) == separator && s.at(i - 2) == '.' ) // any/../any + ) ) s[i - 1] = '_'; } + /* correct trailing spaces in file name itself, not needed for dots */ + int extensionIndex = s.lastIndexOf( '.' ); + if( ( s.length() > 1 ) && ( extensionIndex > 0 ) ) + if( s.at(extensionIndex - 1) == ' ' ) + s[extensionIndex - 1] = '_'; + return s; } QPixmap semiTransparentLogo( int dim ) { QPixmap logo; #define AMAROK_LOGO_CACHE_KEY QLatin1String("AmarokSemiTransparentLogo")+QString::number(dim) if( !QPixmapCache::find( AMAROK_LOGO_CACHE_KEY, &logo ) ) { QImage amarokIcon = KIcon( QLatin1String("amarok") ).pixmap( dim, dim ).toImage(); KIconEffect::toGray( amarokIcon, 1 ); KIconEffect::semiTransparent( amarokIcon ); logo = QPixmap::fromImage( amarokIcon ); QPixmapCache::insert( AMAROK_LOGO_CACHE_KEY, logo ); } #undef AMAROK_LOGO_CACHE_KEY return logo; } } // End namespace Amarok diff --git a/tests/TestAmarok.cpp b/tests/TestAmarok.cpp index e9c351996a..03d2c74f1e 100644 --- a/tests/TestAmarok.cpp +++ b/tests/TestAmarok.cpp @@ -1,341 +1,373 @@ /*************************************************************************** * Copyright (c) 2009 Sven Krohlas * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ***************************************************************************/ #include "TestAmarok.h" #include "core/support/Amarok.h" #include "config-amarok-test.h" #include #include #include #include +#include QTEST_KDEMAIN_CORE( TestAmarok ) TestAmarok::TestAmarok() {} QString TestAmarok::dataPath( const QString &relPath ) { return QDir::toNativeSeparators( QString( AMAROK_TEST_DIR ) + '/' + relPath ); } void TestAmarok::testAsciiPath() { QCOMPARE( Amarok::asciiPath( "" ), QString( "" ) ); QCOMPARE( Amarok::asciiPath( "/home/sven" ), QString( "/home/sven" ) ); QCOMPARE( Amarok::asciiPath( QString::fromUtf8( "/äöü" ) ), QString( "/___" ) ); QCOMPARE( Amarok::asciiPath( QString::fromUtf8( "/äöütest" ) ), QString( "/___test" ) ); QCOMPARE( Amarok::asciiPath( QString::fromUtf8( "/äöü/test" ) ), QString( "/___/test" ) ); QCOMPARE( Amarok::asciiPath( QString::fromUtf8( "/123/" ) ), QString( "/123/" ) ); QCOMPARE( Amarok::asciiPath( QString::fromUtf8( "/.hidden" ) ), QString( "/.hidden" ) ); QCOMPARE( Amarok::asciiPath( QString::fromUtf8( "/here be dragons" ) ), QString( "/here be dragons" ) ); QCOMPARE( Amarok::asciiPath( QString::fromUtf8( "/!important/some%20stuff/what's this?" ) ), QString( "/!important/some%20stuff/what's this?" ) ); /* 0x7F = 127 = DEL control character, explicitly ok on *nix file systems */ QCOMPARE( Amarok::asciiPath( QString( "/abc" ) + QChar( 0x7F ) + ".1" ), QString( QString( "/abc" ) + QChar( 0x7F ) + ".1" ) ); /* random control character: ok */ QCOMPARE( Amarok::asciiPath( QString( "/abc" ) + QChar( 0x07 ) + ".1" ), QString( QString( "/abc" ) + QChar( 0x07 ) + ".1" ) ); /* null byte is not ok */ QCOMPARE( Amarok::asciiPath( QString( "/abc" ) + QChar( 0x00 ) + ".1" ), QString( "/abc_.1" ) ); } void TestAmarok::testCleanPath() { /* no changes expected here */ QCOMPARE( Amarok::cleanPath( QString( "" ) ), QString( "" ) ); QCOMPARE( Amarok::cleanPath( QString( "abcdefghijklmnopqrstuvwxyz" ) ), QString( "abcdefghijklmnopqrstuvwxyz" ) ); QCOMPARE( Amarok::cleanPath( QString( "ABCDEFGHIJKLMNOPQRSTUVWXYZ" ) ), QString( "ABCDEFGHIJKLMNOPQRSTUVWXYZ" ) ); QCOMPARE( Amarok::cleanPath( QString( "/\\.,-+" ) ), QString( "/\\.,-+" ) ); /* German */ QCOMPARE( Amarok::cleanPath( QString::fromUtf8( "äöüß" ) ), QString( "aeoeuess" ) ); QCOMPARE( Amarok::cleanPath( QString::fromUtf8( "ÄÖÜß" ) ), QString( "AeOeUess" ) ); // capital ß only exists in theory in the German language, but had been defined some time ago, iirc /* French */ QCOMPARE( Amarok::cleanPath( QString::fromUtf8( "áàéèêô" ) ), QString( "aaeeeo" ) ); QCOMPARE( Amarok::cleanPath( QString::fromUtf8( "ÁÀÉÈÊÔ" ) ), QString( "AAEEEO" ) ); QCOMPARE( Amarok::cleanPath( QString::fromUtf8( "æ" ) ), QString( "ae" ) ); QCOMPARE( Amarok::cleanPath( QString::fromUtf8( "Æ" ) ), QString( "AE" ) ); /* Czech and other east European languages */ QCOMPARE( Amarok::cleanPath( QString::fromUtf8( "çńǹýỳź" ) ), QString( "cnnyyz" ) ); QCOMPARE( Amarok::cleanPath( QString::fromUtf8( "ÇŃǸÝỲŹ" ) ), QString( "CNNYYZ" ) ); QCOMPARE( Amarok::cleanPath( QString::fromUtf8( "ěĺľôŕřůž" ) ), QString( "ellorruz" ) ); QCOMPARE( Amarok::cleanPath( QString::fromUtf8( "ÁČĎÉĚÍŇÓŘŠŤÚŮÝŽ" ) ), QString( "ACDEEINORSTUUYZ" ) ); /* Skandinavian languages */ QCOMPARE( Amarok::cleanPath( QString::fromUtf8( "åø" ) ), QString( "aoe" ) ); QCOMPARE( Amarok::cleanPath( QString::fromUtf8( "ÅØ" ) ), QString( "AOE" ) ); /* Spanish */ QCOMPARE( Amarok::cleanPath( QString::fromUtf8( "ñóÿ" ) ), QString( "noy" ) ); QCOMPARE( Amarok::cleanPath( QString::fromUtf8( "ÑÓŸ" ) ), QString( "NOY" ) ); /* add missing ones here */ } void TestAmarok::testComputeScore() { QVERIFY( 50 < Amarok::computeScore( 50, 1, 1 ) ); // greater score if played completely QVERIFY( 0 < Amarok::computeScore( 0, 1, 1 ) ); // handle 0 score QVERIFY( 50 > Amarok::computeScore( 50, 1, 0.1 ) ); // lower score if aborted early QVERIFY( 50 > Amarok::computeScore( 50, 1, 0 ) ); // handle 0% played fraction QVERIFY( 50 > Amarok::computeScore( 50, 0, 0 ) ); // handle 0 playcount } void TestAmarok::testConciseTimeSince() { QCOMPARE( Amarok::conciseTimeSince( 0 ).isEmpty(), false ); QCOMPARE( Amarok::conciseTimeSince( 10 ).isEmpty(), false ); QCOMPARE( Amarok::conciseTimeSince( 100 ).isEmpty(), false ); QCOMPARE( Amarok::conciseTimeSince( 1000 ).isEmpty(), false ); /* any other good ideas what to test here? */ } void TestAmarok::testExtension() { QCOMPARE( Amarok::extension( "" ), QString( "" ) ); QCOMPARE( Amarok::extension( "..." ), QString( "" ) ); QCOMPARE( Amarok::extension( "test" ), QString( "" ) ); QCOMPARE( Amarok::extension( "test." ), QString( "" ) ); QCOMPARE( Amarok::extension( "test.mp3" ), QString( "mp3" ) ); QCOMPARE( Amarok::extension( "test.mP3" ), QString( "mp3" ) ); QCOMPARE( Amarok::extension( "test.MP3" ), QString( "mp3" ) ); QCOMPARE( Amarok::extension( "test.longextension" ), QString( "longextension" ) ); QCOMPARE( Amarok::extension( "test.long.extension" ), QString( "extension" ) ); QCOMPARE( Amarok::extension( "test.m" ), QString( "m" ) ); QCOMPARE( Amarok::extension( QString::fromUtf8( "test.äöü" ) ), QString::fromUtf8( "äöü" ) ); QCOMPARE( Amarok::extension( QString::fromUtf8( "test.ÄÖÜ" ) ), QString::fromUtf8( "äöü" ) ); QCOMPARE( Amarok::extension( "..test.mp3" ), QString( "mp3" ) ); QCOMPARE( Amarok::extension( "..te st.mp3" ), QString( "mp3" ) ); QCOMPARE( Amarok::extension( "..te st.m p3" ), QString( "m p3" ) ); } void TestAmarok::testManipulateThe() { QString teststring; Amarok::manipulateThe( teststring = "", true ); QCOMPARE( teststring, QString( "" ) ); Amarok::manipulateThe( teststring = "", false ); QCOMPARE( teststring, QString( "" ) ); Amarok::manipulateThe( teststring = 'A', true ); QCOMPARE( teststring, QString( "A" ) ); Amarok::manipulateThe( teststring = 'A', false ); QCOMPARE( teststring, QString( "A" ) ); Amarok::manipulateThe( teststring = "ABC", true ); QCOMPARE( teststring, QString( "ABC" ) ); Amarok::manipulateThe( teststring = "ABC", false ); QCOMPARE( teststring, QString( "ABC" ) ); Amarok::manipulateThe( teststring = "The Eagles", true ); QCOMPARE( teststring, QString( "Eagles, The" ) ); Amarok::manipulateThe( teststring = "Eagles, The", false ); QCOMPARE( teststring, QString( "The Eagles" ) ); Amarok::manipulateThe( teststring = "The The", true ); QCOMPARE( teststring, QString( "The, The" ) ); Amarok::manipulateThe( teststring = "The, The", false ); QCOMPARE( teststring, QString( "The The" ) ); Amarok::manipulateThe( teststring = "Something else", true ); QCOMPARE( teststring, QString( "Something else" ) ); Amarok::manipulateThe( teststring = "The Äöü", true ); QCOMPARE( teststring, QString( "Äöü, The" ) ); Amarok::manipulateThe( teststring = QString::fromUtf8( "Äöü, The" ), false ); QCOMPARE( teststring, QString::fromUtf8( "The Äöü" ) ); } void TestAmarok::testSaveLocation() { QString saveLocation = Amarok::saveLocation(); QDir saveLocationDir( saveLocation ); QCOMPARE( saveLocationDir.exists(), true ); QCOMPARE( QDir::isAbsolutePath( saveLocation ), true ); QCOMPARE( saveLocationDir.isReadable(), true ); /* any other good ideas what to test here? */ } void TestAmarok::testVerboseTimeSince() { /* There are two overloaded variants of this function */ QCOMPARE( Amarok::verboseTimeSince( 0 ).isEmpty(), false ); QCOMPARE( Amarok::verboseTimeSince( QDateTime::fromTime_t( 0 ) ).isEmpty(), false ); QCOMPARE( Amarok::verboseTimeSince( 10 ).isEmpty(), false ); QCOMPARE( Amarok::verboseTimeSince( QDateTime::fromTime_t( 10 ) ).isEmpty(), false ); QCOMPARE( Amarok::verboseTimeSince( 100 ).isEmpty(), false ); QCOMPARE( Amarok::verboseTimeSince( QDateTime::fromTime_t( 100 ) ).isEmpty(), false ); QCOMPARE( Amarok::verboseTimeSince( 1000 ).isEmpty(), false ); QCOMPARE( Amarok::verboseTimeSince( QDateTime::fromTime_t( 1000 ) ).isEmpty(), false ); /* any other good ideas what to test here? */ } void TestAmarok::testVfatPath() { QCOMPARE( Amarok::vfatPath( "" ), QString( "" ) ); /* allowed characters */ QCOMPARE( Amarok::vfatPath( "abcdefghijklmnopqrstuvwxyz" ), QString( "abcdefghijklmnopqrstuvwxyz" ) ); QCOMPARE( Amarok::vfatPath( "ABCDEFGHIJKLMNOPQRSTUVWXYZ" ), QString( "ABCDEFGHIJKLMNOPQRSTUVWXYZ" ) ); QCOMPARE( Amarok::vfatPath( "0123456789" ), QString( "0123456789" ) ); QCOMPARE( Amarok::vfatPath( "! # $ % & ' ( ) - @ ^ _ ` { } ~" ), QString( "! # $ % & ' ( ) - @ ^ _ ` { } ~" ) ); /* only allowed in long file names */ QCOMPARE( Amarok::vfatPath( "+,.;=[]" ), QString( "+,.;=()" ) ); /* illegal characters, without / and \ (see later tests) */ QCOMPARE( Amarok::vfatPath( "\"_*_:_<_>_?_|" ), QString( "_____________" ) ); /* illegal control characters: 0-31, 127 */ QCOMPARE( Amarok::vfatPath( QString( "abc" ) + QChar( 0x00 ) + QChar( 0x01 ) + QChar( 0x02 ) + ".1" ), QString( "abc___.1" ) ); QCOMPARE( Amarok::vfatPath( QString( "abc" ) + QChar( 0x03 ) + QChar( 0x04 ) + QChar( 0x05 ) + ".1" ), QString( "abc___.1" ) ); QCOMPARE( Amarok::vfatPath( QString( "abc" ) + QChar( 0x06 ) + QChar( 0x07 ) + QChar( 0x08 ) + ".1" ), QString( "abc___.1" ) ); QCOMPARE( Amarok::vfatPath( QString( "abc" ) + QChar( 0x09 ) + QChar( 0x0A ) + QChar( 0x0B ) + ".1" ), QString( "abc___.1" ) ); QCOMPARE( Amarok::vfatPath( QString( "abc" ) + QChar( 0x0C ) + QChar( 0x0D ) + QChar( 0x0E ) + ".1" ), QString( "abc___.1" ) ); QCOMPARE( Amarok::vfatPath( QString( "abc" ) + QChar( 0x0F ) + QChar( 0x10 ) + QChar( 0x11 ) + ".1" ), QString( "abc___.1" ) ); QCOMPARE( Amarok::vfatPath( QString( "abc" ) + QChar( 0x12 ) + QChar( 0x13 ) + QChar( 0x14 ) + ".1" ), QString( "abc___.1" ) ); QCOMPARE( Amarok::vfatPath( QString( "abc" ) + QChar( 0x15 ) + QChar( 0x16 ) + QChar( 0x17 ) + ".1" ), QString( "abc___.1" ) ); QCOMPARE( Amarok::vfatPath( QString( "abc" ) + QChar( 0x18 ) + QChar( 0x19 ) + QChar( 0x1A ) + ".1" ), QString( "abc___.1" ) ); QCOMPARE( Amarok::vfatPath( QString( "abc" ) + QChar( 0x1B ) + QChar( 0x1C ) + QChar( 0x1D ) + ".1" ), QString( "abc___.1" ) ); QCOMPARE( Amarok::vfatPath( QString( "abc" ) + QChar( 0x1E ) + QChar( 0x7F ) + ".1" ), QString( "abc__.1" ) ); // 0x7F = 127 = DEL control character /* trailing spaces in extension, directory and file names are being ignored (!) */ QCOMPARE( Amarok::vfatPath( "test " ), QString( "test _" ) ); QCOMPARE( Amarok::vfatPath( " test " ), QString( " test _" ) ); QCOMPARE( Amarok::vfatPath( "test.ext " ), QString( "test.ext _" ) ); QCOMPARE( Amarok::vfatPath( "test .ext " ), QString( "test _.ext _" ) ); QCOMPARE( Amarok::vfatPath( " test .ext " ), QString( " test _.ext _" ) ); // yes, really! -#ifdef Q_WS_WIN // interpret / as part of the name, not directory separator - QCOMPARE( Amarok::vfatPath( "\\some\\folder \\" ), QString( "\\some\\folder _\\" ) ); - QCOMPARE( Amarok::vfatPath( "\\some \\folder \\" ), QString( "\\some _\\folder _\\" ) ); - QCOMPARE( Amarok::vfatPath( "\\...some \\ev il \\folders...\\" ), QString( "\\...some _\\ev il _\\folders...\\" ) ); - QCOMPARE( Amarok::vfatPath( "\\some\\fol/der \\" ), QString( "\\some\\fol_der _\\" ) ); -#else // interpret \ as part of the name, not directory separator - QCOMPARE( Amarok::vfatPath( "/some/folder /" ), QString( "/some/folder _/" ) ); - QCOMPARE( Amarok::vfatPath( "/some /folder /" ), QString( "/some _/folder _/" ) ); - QCOMPARE( Amarok::vfatPath( "/...some /ev il /folders.../" ), QString( "/...some _/ev il _/folders.../" ) ); - QCOMPARE( Amarok::vfatPath( "/some/fol\\der /" ), QString( "/some/fol_der _/" ) ); -#endif + /* trailing dot in directory and file names are unsupported are being ignored (!) */ + QCOMPARE( Amarok::vfatPath( "test..." ), QString( "test.._" ) ); + QCOMPARE( Amarok::vfatPath( "...test..." ), QString( "...test.._" ) ); + QCOMPARE( Amarok::vfatPath( "test.ext..." ), QString( "test.ext.._" ) ); + QCOMPARE( Amarok::vfatPath( "test....ext..." ), QString( "test....ext.._" ) ); + QCOMPARE( Amarok::vfatPath( "...test....ext..." ), QString( "...test....ext.._" ) ); + + /* more tests of trailing spaces and dot in directory names for Windows */ + QCOMPARE( Amarok::vfatPath( "\\some\\folder \\", Amarok::WindowsBehaviour ), QString( "\\some\\folder _\\" ) ); + QCOMPARE( Amarok::vfatPath( "\\some \\folder \\", Amarok::WindowsBehaviour ), QString( "\\some _\\folder _\\" ) ); + QCOMPARE( Amarok::vfatPath( "\\...some \\ev il \\folders...\\", Amarok::WindowsBehaviour ), QString( "\\...some _\\ev il _\\folders.._\\" ) ); + QCOMPARE( Amarok::vfatPath( "\\some\\fol/der \\", Amarok::WindowsBehaviour ), QString( "\\some\\fol_der _\\" ) ); + QCOMPARE( Amarok::vfatPath( "\\some...\\folder...\\", Amarok::WindowsBehaviour ), QString( "\\some.._\\folder.._\\" ) ); + QCOMPARE( Amarok::vfatPath( "\\some\\fol/der...\\", Amarok::WindowsBehaviour ), QString( "\\some\\fol_der.._\\" ) ); + QCOMPARE( Amarok::vfatPath( "\\so..me.\\folder .\\", Amarok::WindowsBehaviour ), QString( "\\so..me_\\folder _\\" ) ); + QCOMPARE( Amarok::vfatPath( ".\\any", Amarok::WindowsBehaviour ), QString( ".\\any" ) ); + QCOMPARE( Amarok::vfatPath( "..\\any", Amarok::WindowsBehaviour ), QString( "..\\any" ) ); + QCOMPARE( Amarok::vfatPath( "...\\any", Amarok::WindowsBehaviour ), QString( ".._\\any" ) ); + QCOMPARE( Amarok::vfatPath( "a..\\any", Amarok::WindowsBehaviour ), QString( "a._\\any" ) ); + QCOMPARE( Amarok::vfatPath( "any\\.\\any.", Amarok::WindowsBehaviour ), QString( "any\\.\\any_" ) ); + QCOMPARE( Amarok::vfatPath( "any\\..\\any ", Amarok::WindowsBehaviour ), QString( "any\\..\\any_" ) ); + QCOMPARE( Amarok::vfatPath( "any.\\...\\any", Amarok::WindowsBehaviour ), QString( "any_\\.._\\any" ) ); + QCOMPARE( Amarok::vfatPath( "any \\a..\\any", Amarok::WindowsBehaviour ), QString( "any_\\a._\\any" ) ); + QCOMPARE( Amarok::vfatPath( "Music\\R.E.M.\\Automatic for the people", Amarok::WindowsBehaviour ), QString( "Music\\R.E.M_\\Automatic for the people" ) ); + + /* more tests of trailing spaces and dot in directory names for Unix */ + QCOMPARE( Amarok::vfatPath( "/some/folder /", Amarok::UnixBehaviour ), QString( "/some/folder _/" ) ); + QCOMPARE( Amarok::vfatPath( "/some /folder /", Amarok::UnixBehaviour ), QString( "/some _/folder _/" ) ); + QCOMPARE( Amarok::vfatPath( "/...some /ev il /folders.../", Amarok::UnixBehaviour ), QString( "/...some _/ev il _/folders.._/" ) ); + QCOMPARE( Amarok::vfatPath( "/some/fol\\der /", Amarok::UnixBehaviour ), QString( "/some/fol_der _/" ) ); + QCOMPARE( Amarok::vfatPath( "/some.../folder.../", Amarok::UnixBehaviour ), QString( "/some.._/folder.._/" ) ); + QCOMPARE( Amarok::vfatPath( "/some/fol\\der.../", Amarok::UnixBehaviour ), QString( "/some/fol_der.._/" ) ); + QCOMPARE( Amarok::vfatPath( "/so..me./folder ./", Amarok::UnixBehaviour ), QString( "/so..me_/folder _/" ) ); + QCOMPARE( Amarok::vfatPath( "./any", Amarok::UnixBehaviour ), QString( "./any" ) ); + QCOMPARE( Amarok::vfatPath( "../any", Amarok::UnixBehaviour ), QString( "../any" ) ); + QCOMPARE( Amarok::vfatPath( ".../any", Amarok::UnixBehaviour ), QString( ".._/any" ) ); + QCOMPARE( Amarok::vfatPath( "a../any", Amarok::UnixBehaviour ), QString( "a._/any" ) ); + QCOMPARE( Amarok::vfatPath( "any/./any.", Amarok::UnixBehaviour ), QString( "any/./any_" ) ); + QCOMPARE( Amarok::vfatPath( "any/../any ", Amarok::UnixBehaviour ), QString( "any/../any_" ) ); + QCOMPARE( Amarok::vfatPath( "any./.../any", Amarok::UnixBehaviour ), QString( "any_/.._/any" ) ); + QCOMPARE( Amarok::vfatPath( "any /a../any", Amarok::UnixBehaviour ), QString( "any_/a._/any" ) ); + QCOMPARE( Amarok::vfatPath( "Music/R.E.M./Automatic for the people", Amarok::UnixBehaviour ), QString( "Music/R.E.M_/Automatic for the people" ) ); /* Stepping deeper into M$ hell: reserved device names * See http://msdn.microsoft.com/en-us/library/aa365247(VS.85).aspx */ QCOMPARE( Amarok::vfatPath( "CLOCK$" ), QString( "_CLOCK$" ) ); /* this one IS allowed according to * http://en.wikipedia.org/w/index.php?title=Filename&oldid=303934888#Comparison_of_file_name_limitations */ QCOMPARE( Amarok::vfatPath( "CLOCK$.ext" ), QString( "CLOCK$.ext" ) ); QCOMPARE( Amarok::vfatPath( "CON" ), QString( "_CON" ) ); QCOMPARE( Amarok::vfatPath( "CON.ext" ), QString( "_CON.ext" ) ); QCOMPARE( Amarok::vfatPath( "PRN" ), QString( "_PRN" ) ); QCOMPARE( Amarok::vfatPath( "PRN.ext" ), QString( "_PRN.ext" ) ); QCOMPARE( Amarok::vfatPath( "AUX" ), QString( "_AUX" ) ); QCOMPARE( Amarok::vfatPath( "AUX.ext" ), QString( "_AUX.ext" ) ); QCOMPARE( Amarok::vfatPath( "NUL" ), QString( "_NUL" ) ); QCOMPARE( Amarok::vfatPath( "NUL.ext" ), QString( "_NUL.ext" ) ); QCOMPARE( Amarok::vfatPath( "COM1" ), QString( "_COM1" ) ); QCOMPARE( Amarok::vfatPath( "COM1.ext" ), QString( "_COM1.ext" ) ); QCOMPARE( Amarok::vfatPath( "COM2" ), QString( "_COM2" ) ); QCOMPARE( Amarok::vfatPath( "COM2.ext" ), QString( "_COM2.ext" ) ); QCOMPARE( Amarok::vfatPath( "COM3" ), QString( "_COM3" ) ); QCOMPARE( Amarok::vfatPath( "COM3.ext" ), QString( "_COM3.ext" ) ); QCOMPARE( Amarok::vfatPath( "COM4" ), QString( "_COM4" ) ); QCOMPARE( Amarok::vfatPath( "COM4.ext" ), QString( "_COM4.ext" ) ); QCOMPARE( Amarok::vfatPath( "COM5" ), QString( "_COM5" ) ); QCOMPARE( Amarok::vfatPath( "COM5.ext" ), QString( "_COM5.ext" ) ); QCOMPARE( Amarok::vfatPath( "COM6" ), QString( "_COM6" ) ); QCOMPARE( Amarok::vfatPath( "COM6.ext" ), QString( "_COM6.ext" ) ); QCOMPARE( Amarok::vfatPath( "COM7" ), QString( "_COM7" ) ); QCOMPARE( Amarok::vfatPath( "COM7.ext" ), QString( "_COM7.ext" ) ); QCOMPARE( Amarok::vfatPath( "COM8" ), QString( "_COM8" ) ); QCOMPARE( Amarok::vfatPath( "COM8.ext" ), QString( "_COM8.ext" ) ); QCOMPARE( Amarok::vfatPath( "COM9" ), QString( "_COM9" ) ); QCOMPARE( Amarok::vfatPath( "COM9.ext" ), QString( "_COM9.ext" ) ); QCOMPARE( Amarok::vfatPath( "LPT1" ), QString( "_LPT1" ) ); QCOMPARE( Amarok::vfatPath( "LPT1.ext" ), QString( "_LPT1.ext" ) ); QCOMPARE( Amarok::vfatPath( "LPT2" ), QString( "_LPT2" ) ); QCOMPARE( Amarok::vfatPath( "LPT2.ext" ), QString( "_LPT2.ext" ) ); QCOMPARE( Amarok::vfatPath( "LPT3" ), QString( "_LPT3" ) ); QCOMPARE( Amarok::vfatPath( "LPT3.ext" ), QString( "_LPT3.ext" ) ); QCOMPARE( Amarok::vfatPath( "LPT4" ), QString( "_LPT4" ) ); QCOMPARE( Amarok::vfatPath( "LPT4.ext" ), QString( "_LPT4.ext" ) ); QCOMPARE( Amarok::vfatPath( "LPT5" ), QString( "_LPT5" ) ); QCOMPARE( Amarok::vfatPath( "LPT5.ext" ), QString( "_LPT5.ext" ) ); QCOMPARE( Amarok::vfatPath( "LPT6" ), QString( "_LPT6" ) ); QCOMPARE( Amarok::vfatPath( "LPT6.ext" ), QString( "_LPT6.ext" ) ); QCOMPARE( Amarok::vfatPath( "LPT7" ), QString( "_LPT7" ) ); QCOMPARE( Amarok::vfatPath( "LPT7.ext" ), QString( "_LPT7.ext" ) ); QCOMPARE( Amarok::vfatPath( "LPT8" ), QString( "_LPT8" ) ); QCOMPARE( Amarok::vfatPath( "LPT8.ext" ), QString( "_LPT8.ext" ) ); QCOMPARE( Amarok::vfatPath( "LPT9" ), QString( "_LPT9" ) ); QCOMPARE( Amarok::vfatPath( "LPT9.ext" ), QString( "_LPT9.ext" ) ); /* Device names in different cases: */ QCOMPARE( Amarok::vfatPath( "con" ), QString( "_con" ) ); QCOMPARE( Amarok::vfatPath( "con.ext" ), QString( "_con.ext" ) ); QCOMPARE( Amarok::vfatPath( "cON" ), QString( "_cON" ) ); QCOMPARE( Amarok::vfatPath( "cON.ext" ), QString( "_cON.ext" ) ); /* This one is ok :) */ QCOMPARE( Amarok::vfatPath( "CONCERT" ), QString( "CONCERT" ) ); QCOMPARE( Amarok::vfatPath( "CONCERT.ext" ), QString( "CONCERT.ext" ) ); }