2015-08-05 13:09:01 +02:00
// Copyright (C) 2015, The Duplicati Team
2015-04-10 22:29:52 +02:00
// http://www.duplicati.com, info@duplicati.com
//
// This library is free software; you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as
// published by the Free Software Foundation; either version 2.1 of the
// License, or (at your option) any later version.
//
// This library 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
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
2015-09-05 09:14:21 +02:00
using System ;
using System.IO ;
using System.Collections.Generic ;
using Duplicati.Library.Utility ;
using System.Linq ;
using Duplicati.Library.Logging ;
using System.Reflection ;
2019-08-29 17:23:19 -04:00
using System.Threading ;
using System.Threading.Tasks ;
2018-11-02 21:34:07 +01:00
using Duplicati.Library.Common.IO ;
2018-10-27 12:17:07 +02:00
2015-04-10 22:29:52 +02:00
namespace Duplicati.UnitTest
{
public static class TestUtils
{
2018-03-12 14:07:11 +01:00
/// <summary>
/// The log tag
/// </summary>
private static readonly string LOGTAG = Library . Logging . Log . LogTagFromType ( typeof ( TestUtils ));
2015-09-05 09:14:21 +02:00
public static Dictionary < string , string > DefaultOptions
{
get
{
var opts = new Dictionary < string , string >();
string auth_password = Path . Combine ( Path . GetDirectoryName ( Assembly . GetExecutingAssembly (). Location ), "unittest_authpassword.txt" );
if ( System . IO . File . Exists ( auth_password ))
opts [ "auth-password" ] = File . ReadAllText ( auth_password ). Trim ();
return opts ;
}
}
2019-08-29 17:23:19 -04:00
public static async Task GrowingFile ( string testFile , CancellationToken token )
{
try
{
var str = new string ( '*' , 50 );
while ( true )
{
if ( token . IsCancellationRequested )
{
continue ;
}
File . AppendAllText ( testFile , str );
await Task . Delay ( 18 , token ). ConfigureAwait ( false );
}
}
finally
{
if ( File . Exists ( testFile ))
{
File . Delete ( testFile );
}
}
}
2015-09-05 09:14:21 +02:00
public static string GetDefaultTarget ( string other = null )
{
string alttarget = System . IO . Path . Combine ( Path . GetDirectoryName ( Assembly . GetExecutingAssembly (). Location ), "unittest_target.txt" );
if ( File . Exists ( alttarget ))
return File . ReadAllText ( alttarget ). Trim ();
else if ( other != null )
return other ;
else
using ( var tf = new Library . Utility . TempFolder ())
{
tf . Protected = true ;
return "file://" + tf ;
}
}
/// <summary>
/// Recursively copy a directory to another location.
/// </summary>
/// <param name="sourcefolder">Source directory path</param>
/// <param name="targetfolder">Destination directory path</param>
public static void CopyDirectoryRecursive ( string sourcefolder , string targetfolder )
{
2018-10-27 12:17:07 +02:00
sourcefolder = Util . AppendDirSeparator ( sourcefolder );
2015-09-05 09:14:21 +02:00
var work = new Queue < string >();
work . Enqueue ( sourcefolder );
2016-01-26 22:48:59 +01:00
var timestampfailures = 0 ;
2015-09-05 09:14:21 +02:00
while ( work . Count > 0 )
{
var c = work . Dequeue ();
var t = Path . Combine ( targetfolder , c . Substring ( sourcefolder . Length ));
if (! Directory . Exists ( t ))
Directory . CreateDirectory ( t );
2016-01-26 22:48:59 +01:00
try { Directory . SetCreationTimeUtc ( t , Directory . GetCreationTimeUtc ( c )); }
catch ( Exception ex )
{
if ( timestampfailures ++ < 20 )
Console . WriteLine ( "Failed to set creation time on dir {0}: {1}" , t , ex . Message );
}
try { Directory . SetLastWriteTimeUtc ( t , Directory . GetLastWriteTimeUtc ( c )); }
catch ( Exception ex )
{
if ( timestampfailures ++ < 20 )
Console . WriteLine ( "Failed to set write time on dir {0}: {1}" , t , ex . Message );
}
2015-09-05 09:14:21 +02:00
foreach ( var n in Directory . EnumerateFiles ( c ))
{
var tf = Path . Combine ( t , Path . GetFileName ( n ));
File . Copy ( n , tf , true );
2016-01-26 22:48:59 +01:00
try { File . SetCreationTimeUtc ( tf , System . IO . File . GetCreationTimeUtc ( n )); }
catch ( Exception ex )
{
if ( timestampfailures ++ < 20 )
Console . WriteLine ( "Failed to set creation time on file {0}: {1}" , n , ex . Message );
}
try { File . SetLastWriteTimeUtc ( tf , System . IO . File . GetLastWriteTimeUtc ( n )); }
catch ( Exception ex )
{
if ( timestampfailures ++ < 20 )
Console . WriteLine ( "Failed to set write time on file {0}: {1}" , n , ex . Message );
}
2015-09-05 09:14:21 +02:00
}
foreach ( var n in Directory . EnumerateDirectories ( c ))
work . Enqueue ( n );
}
2016-01-26 22:48:59 +01:00
if ( timestampfailures > 20 )
Console . WriteLine ( "Encountered additional {0} timestamp errors!" , timestampfailures );
2015-09-05 09:14:21 +02:00
}
/// <summary>
/// Returns the index of a given string, using the file system case sensitivity
/// </summary>
/// <returns>The index of the entry or -1 if no entry was found</returns>
/// <param name='lst'>The list to search</param>
/// <param name='m'>The string to find</param>
private static int IndexOf ( List < string > lst , string m )
{
2018-05-16 19:40:51 +02:00
StringComparison sc = Duplicati . Library . Utility . Utility . ClientFilenameStringComparison ;
2015-09-05 09:14:21 +02:00
for ( int i = 0 ; i < lst . Count ; i ++)
if ( lst [ i ]. Equals ( m , sc ))
return i ;
return - 1 ;
}
/// <summary>
/// Verifies the existence of all files and folders, and ensures that all
/// files are binary equal.
/// </summary>
/// <param name="f1">One folder</param>
/// <param name="f2">Another folder</param>
public static void VerifyDir ( string f1 , string f2 , bool verifymetadata )
{
var anymissing = false ;
2018-10-27 12:17:07 +02:00
f1 = Util . AppendDirSeparator ( f1 );
f2 = Util . AppendDirSeparator ( f2 );
2015-09-05 09:14:21 +02:00
var folders1 = Utility . EnumerateFolders ( f1 );
var folders2 = Utility . EnumerateFolders ( f2 ). ToList ();
foreach ( string s in folders1 )
{
string relpath = s . Substring ( f1 . Length );
string target = System . IO . Path . Combine ( f2 , relpath );
int ix = IndexOf ( folders2 , target );
if ( ix < 0 )
{
2018-03-12 14:07:11 +01:00
Log . WriteErrorMessage ( LOGTAG , "MissingFolder" , null , "Missing folder: {0}" , relpath );
2015-09-05 09:14:21 +02:00
Console . WriteLine ( "Missing folder: " + relpath );
anymissing = true ;
}
else
folders2 . RemoveAt ( ix );
}
foreach ( string s in folders2 )
{
2018-03-12 14:07:11 +01:00
Log . WriteErrorMessage ( LOGTAG , "ExtraFolder" , null , "Extra folder: {0}" , s . Substring ( f2 . Length ));
2015-09-05 09:14:21 +02:00
Console . WriteLine ( "Extra folder: " + s . Substring ( f2 . Length ));
}
var files1 = Utility . EnumerateFiles ( f1 );
var files2 = Utility . EnumerateFiles ( f2 ). ToList ();
foreach ( string s in files1 )
{
string relpath = s . Substring ( f1 . Length );
string target = System . IO . Path . Combine ( f2 , relpath );
int ix = IndexOf ( files2 , target );
if ( ix < 0 )
{
2018-03-12 14:07:11 +01:00
Log . WriteErrorMessage ( LOGTAG , "MissingFile" , null , "Missing file: {0}" , relpath );
2015-09-05 09:14:21 +02:00
Console . WriteLine ( "Missing file: " + relpath );
anymissing = true ;
}
else
{
files2 . RemoveAt ( ix );
if (! CompareFiles ( s , target , relpath , verifymetadata ))
{
2018-03-12 14:07:11 +01:00
Log . WriteErrorMessage ( LOGTAG , "FileDiffers" , null , "File differs: {0}" , relpath );
2015-09-05 09:14:21 +02:00
Console . WriteLine ( "File differs: " + relpath );
}
}
}
foreach ( string s in files2 )
{
2018-03-12 14:07:11 +01:00
Log . WriteErrorMessage ( LOGTAG , "ExtraFile" , null , "Extra file: {0}" , s . Substring ( f2 . Length ));
2015-09-05 09:14:21 +02:00
Console . WriteLine ( "Extra file: " + s . Substring ( f2 . Length ));
}
if ( anymissing )
throw new Exception ( "Verify failed, some items are missing" );
}
/// <summary>
/// Compares two files by reading all bytes, and comparing one by one
/// </summary>
/// <param name="f1">One file</param>
/// <param name="f2">Another file</param>
/// <param name="display">File display name</param>
/// <returns>True if they are equal, false otherwise</returns>
public static bool CompareFiles ( string f1 , string f2 , string display , bool verifymetadata )
{
using ( System . IO . FileStream fs1 = System . IO . File . OpenRead ( f1 ))
using ( System . IO . FileStream fs2 = System . IO . File . OpenRead ( f2 ))
if ( fs1 . Length != fs2 . Length )
{
2018-03-12 14:07:11 +01:00
Log . WriteErrorMessage ( LOGTAG , "LengthsDiffer" , null , "Lengths differ: {0}, {1} vs {2}" , display , fs1 . Length . ToString (), fs2 . Length . ToString ());
2015-09-05 09:14:21 +02:00
Console . WriteLine ( "Lengths differ: " + display + ", " + fs1 . Length . ToString () + " vs. " + fs2 . Length . ToString ());
return false ;
}
else
{
2016-04-02 21:54:46 +02:00
// The byte-by-byte compare is dog-slow, so we use a fast(-er) check, and then report the first byte diff if required
if (! Library . Utility . Utility . CompareStreams ( fs1 , fs2 , true ))
{
fs1 . Position = 0 ;
fs2 . Position = 0 ;
long len = fs1 . Length ;
for ( long l = 0 ; l < len ; l ++)
if ( fs1 . ReadByte () != fs2 . ReadByte ())
{
2018-03-12 14:07:11 +01:00
Log . WriteErrorMessage ( LOGTAG , "MismatchInFile" , null , "Mismatch in byte {0} in file {1}" , l . ToString (), display );
2016-04-02 21:54:46 +02:00
Console . WriteLine ( "Mismatch in byte " + l . ToString () + " in file " + display );
return false ;
}
}
2015-09-05 09:14:21 +02:00
}
if ( verifymetadata )
{
if ( System . IO . File . GetLastWriteTime ( f1 ) != System . IO . File . GetLastWriteTime ( f2 ))
{
2018-03-12 14:07:11 +01:00
Log . WriteWarningMessage ( LOGTAG , "MismatchInLastModified" , null , "Mismatch in lastmodified for {0}, {1} vs {2}" , f2 , System . IO . File . GetLastWriteTimeUtc ( f1 ), System . IO . File . GetLastWriteTimeUtc ( f2 ));
2015-09-05 09:14:21 +02:00
Console . WriteLine ( "Mismatch in lastmodified for " + f2 + ", " + System . IO . File . GetLastWriteTimeUtc ( f1 ) + " vs. " + System . IO . File . GetLastWriteTimeUtc ( f2 ));
}
if ( System . IO . File . GetCreationTimeUtc ( f1 ) != System . IO . File . GetCreationTimeUtc ( f2 ))
{
2018-03-12 14:07:11 +01:00
Log . WriteWarningMessage ( LOGTAG , "MismatchInCreateTime" , null , "Mismatch in create-time for {0}, {1} vs {2}" , f2 , System . IO . File . GetCreationTimeUtc ( f1 ), System . IO . File . GetCreationTimeUtc ( f2 ));
2015-09-05 09:14:21 +02:00
Console . WriteLine ( "Mismatch in create-time for " + f2 + ", " + System . IO . File . GetCreationTimeUtc ( f1 ) + " vs. " + System . IO . File . GetCreationTimeUtc ( f2 ));
}
}
return true ;
}
public static Dictionary < string , string > Expand ( this Dictionary < string , string > self , object extra )
{
var res = new Dictionary < string , string >( self );
foreach ( var n in extra . GetType (). GetFields ())
{
var name = n . Name . Replace ( '_' , '-' );
var value = n . GetValue ( extra );
res [ name ] = value == null ? "" : value . ToString ();
}
foreach ( var n in extra . GetType (). GetProperties ())
{
var name = n . Name . Replace ( '_' , '-' );
var value = n . GetValue ( extra );
res [ name ] = value == null ? "" : value . ToString ();
}
return res ;
}
}
2015-04-10 22:29:52 +02:00
}