2024-03-15 14:18:56 +01:00
// Copyright (C) 2024, The Duplicati Team
// https://duplicati.com, hello@duplicati.com
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
2024-02-28 15:45:30 +01:00
2013-02-12 21:43:14 +00:00
using System ;
using System.Collections.Generic ;
using System.Text ;
using Duplicati.Library.Interface ;
2015-07-01 09:39:26 +02:00
using System.Linq ;
2018-12-11 21:07:30 -08:00
using System.Globalization ;
2019-02-22 21:58:40 -06:00
using System.Threading ;
2013-02-12 21:43:14 +00:00
namespace Duplicati.CommandLine.BackendTester
{
2021-04-03 13:57:02 +02:00
public class Program
2013-02-12 21:43:14 +00:00
{
2018-09-29 13:20:16 -07:00
/// <summary>
/// Used to maintain a reference to initialized system settings.
/// </summary>
2018-09-29 13:32:40 -07:00
#pragma warning disable CS0414 // The private field `Duplicati.CommandLine.BackendTester.Program.SystemSettings' is assigned but its value is never used
2018-09-29 13:20:16 -07:00
private static IDisposable SystemSettings ;
2018-09-29 13:32:40 -07:00
#pragma warning restore CS0414 // The private field `Duplicati.CommandLine.BackendTester.Program.SystemSettings' is assigned but its value is never used
2018-09-29 13:20:16 -07:00
2013-02-12 21:43:14 +00:00
class TempFile
{
public readonly string remotefilename ;
public readonly string localfilename ;
public readonly byte [] hash ;
public readonly long length ;
public bool found = false ;
public TempFile ( string remotefilename , string localfilename , byte [] hash , long length )
{
this . remotefilename = remotefilename ;
this . localfilename = localfilename ;
this . hash = hash ;
this . length = length ;
}
}
private const string ValidFilenameChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ123456789" ;
private const string ExtendedChars = "-_',=)(&%$#@! +" ;
2014-06-26 22:37:58 +02:00
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
2024-03-15 14:18:56 +01:00
public static int Main ( string [] _args )
2013-02-12 21:43:14 +00:00
{
try
{
2015-07-01 09:39:26 +02:00
if ( _args . Length == 1 )
{
try
{
2018-09-22 19:51:30 -07:00
var p = Environment . ExpandEnvironmentVariables ( _args [ 0 ]);
2015-07-01 09:39:26 +02:00
if ( System . IO . File . Exists ( p ))
_args = ( from x in System . IO . File . ReadLines ( p )
2017-11-26 10:53:14 -08:00
where ! string . IsNullOrWhiteSpace ( x ) && ! x . Trim (). StartsWith ( "#" , StringComparison . Ordinal )
2015-07-01 09:39:26 +02:00
select x . Trim ()
). ToArray ();
}
catch
{
}
}
2013-02-12 21:43:14 +00:00
List < string > args = new List < string >( _args );
Dictionary < string , string > options = Library . Utility . CommandLineParser . ExtractOptions ( args );
2018-09-19 14:29:40 -07:00
if ( args . Count != 1 || String . Equals ( args [ 0 ], "help" , StringComparison . OrdinalIgnoreCase ) || args [ 0 ] == "?" )
2013-02-12 21:43:14 +00:00
{
Console . WriteLine ( "Usage: <protocol>://<username>:<password>@<path>" );
Console . WriteLine ( "Example: ftp://user:pass@server/folder" );
Console . WriteLine ();
Console . WriteLine ( "Supported backends: " + string . Join ( "," , Duplicati . Library . DynamicLoader . BackendLoader . Keys ));
Console . WriteLine ();
List < string > lines = new List < string >();
foreach ( Library . Interface . ICommandLineArgument arg in SupportedCommands )
Library . Interface . CommandLineArgument . PrintArgument ( lines , arg );
foreach ( string s in lines )
Console . WriteLine ( s );
2024-03-15 14:18:56 +01:00
return 0 ;
2013-02-12 21:43:14 +00:00
}
2018-05-23 19:37:49 +02:00
if ( options . ContainsKey ( "tempdir" ) && ! string . IsNullOrEmpty ( options [ "tempdir" ]))
Library . Utility . SystemContextSettings . DefaultTempPath = options [ "tempdir" ];
2018-09-29 13:20:16 -07:00
SystemSettings = Duplicati . Library . Utility . SystemContextSettings . StartSession ();
2018-05-23 19:37:49 +02:00
if (! options . ContainsKey ( "auth_password" ) && ! string . IsNullOrEmpty ( System . Environment . GetEnvironmentVariable ( "AUTH_PASSWORD" )))
options [ "auth_password" ] = System . Environment . GetEnvironmentVariable ( "AUTH_PASSWORD" );
if (! options . ContainsKey ( "auth_username" ) && ! string . IsNullOrEmpty ( System . Environment . GetEnvironmentVariable ( "AUTH_USERNAME" )))
options [ "auth_username" ] = System . Environment . GetEnvironmentVariable ( "AUTH_USERNAME" );
2013-02-12 21:43:14 +00:00
int reruns = 5 ;
if ( options . ContainsKey ( "reruns" ))
reruns = int . Parse ( options [ "reruns" ]);
for ( int i = 0 ; i < reruns ; i ++)
{
Console . WriteLine ( "Starting run no {0}" , i );
if (! Run ( args , options , i == 0 ))
2024-03-15 14:18:56 +01:00
return 1 ;
2013-02-12 21:43:14 +00:00
}
Console . WriteLine ( "Unittest complete!" );
2024-03-15 14:18:56 +01:00
return 0 ;
2013-02-12 21:43:14 +00:00
}
catch ( Exception ex )
{
2017-11-26 15:24:53 -08:00
Console . WriteLine ( "Unittest failed: " + ex );
2013-02-12 21:43:14 +00:00
}
2024-03-15 14:18:56 +01:00
return 1 ;
2013-02-12 21:43:14 +00:00
}
static bool Run ( List < string > args , Dictionary < string , string > options , bool first )
{
Library . Interface . IBackend backend = Library . DynamicLoader . BackendLoader . GetBackend ( args [ 0 ], options );
if ( backend == null )
{
Console . WriteLine ( "Unsupported backend" );
Console . WriteLine ();
Console . WriteLine ( "Supported backends: " + string . Join ( "," , Duplicati . Library . DynamicLoader . BackendLoader . Keys ));
return false ;
}
2018-06-29 19:46:33 +02:00
string allowedChars = ValidFilenameChars ;
2020-01-25 17:07:02 -08:00
if ( options . ContainsKey ( "extended-chars" ))
{
allowedChars += String . IsNullOrEmpty ( options [ "extended-chars" ]) ? ExtendedChars : options [ "extended-chars" ];
}
2018-06-29 19:46:33 +02:00
bool autoCreateFolders = Library . Utility . Utility . ParseBoolOption ( options , "auto-create-folder" );
2013-02-12 21:43:14 +00:00
string disabledModulesValue ;
string enabledModulesValue ;
options . TryGetValue ( "enable-module" , out enabledModulesValue );
options . TryGetValue ( "disable-module" , out disabledModulesValue );
2018-12-11 21:07:30 -08:00
string [] enabledModules = enabledModulesValue == null ? new string [ 0 ] : enabledModulesValue . Trim (). ToLower ( CultureInfo . InvariantCulture ). Split ( ',' );
string [] disabledModules = disabledModulesValue == null ? new string [ 0 ] : disabledModulesValue . Trim (). ToLower ( CultureInfo . InvariantCulture ). Split ( ',' );
2013-02-12 21:43:14 +00:00
List < Library . Interface . IGenericModule > loadedModules = new List < IGenericModule >();
foreach ( Library . Interface . IGenericModule m in Library . DynamicLoader . GenericLoader . Modules )
2018-09-19 14:29:40 -07:00
if (! disabledModules . Contains ( m . Key , StringComparer . OrdinalIgnoreCase ) && ( m . LoadAsDefault || enabledModules . Contains ( m . Key , StringComparer . OrdinalIgnoreCase )))
2013-02-12 21:43:14 +00:00
{
m . Configure ( options );
loadedModules . Add ( m );
}
try
{
2017-09-25 23:17:45 -06:00
IEnumerable < Library . Interface . IFileEntry > curlist = null ;
2013-02-12 21:43:14 +00:00
try
{
2018-03-20 13:46:49 -06:00
backend . Test ();
2013-02-12 21:43:14 +00:00
curlist = backend . List ();
}
2018-12-31 19:31:03 -08:00
catch ( FolderMissingException )
2013-02-12 21:43:14 +00:00
{
if ( autoCreateFolders )
{
try
{
2013-05-05 17:07:24 +02:00
backend . CreateFolder ();
2013-02-12 21:43:14 +00:00
curlist = backend . List ();
}
catch ( Exception ex )
{
Console . WriteLine ( "Autocreate folder failed with message: " + ex . Message );
}
}
if ( curlist == null )
2018-12-31 14:52:11 -08:00
throw ;
2013-02-12 21:43:14 +00:00
}
foreach ( Library . Interface . IFileEntry fe in curlist )
if (! fe . IsFolder )
{
if ( Library . Utility . Utility . ParseBoolOption ( options , "auto-clean" ) && first )
if ( Library . Utility . Utility . ParseBoolOption ( options , "force" ))
{
Console . WriteLine ( "Auto clean, removing file: {0}" , fe . Name );
backend . Delete ( fe . Name );
continue ;
}
else
Console . WriteLine ( "Specify the --force flag to actually delete files" );
Console . WriteLine ( "*** Remote folder is not empty, aborting" );
return false ;
}
int number_of_files = 10 ;
int min_file_size = 1024 ;
int max_file_size = 1024 * 1024 * 50 ;
int min_filename_size = 5 ;
int max_filename_size = 80 ;
bool disableStreaming = Library . Utility . Utility . ParseBoolOption ( options , "disable-streaming-transfers" );
bool skipOverwriteTest = Library . Utility . Utility . ParseBoolOption ( options , "skip-overwrite-test" );
2018-03-20 13:48:21 -06:00
bool trimFilenameSpaces = Library . Utility . Utility . ParseBoolOption ( options , "trim-filename-spaces" );
2013-02-12 21:43:14 +00:00
2021-03-22 09:57:56 -06:00
long throttleUpload = 0 ;
if ( options . TryGetValue ( "throttle-upload" , out string throttleUploadString ))
{
if (!( backend is IStreamingBackend ) || disableStreaming )
{
Console . WriteLine ( "Warning: Throttling is only supported in this tool on streaming backends" );
}
2021-03-22 10:47:44 -06:00
throttleUpload = Duplicati . Library . Utility . Sizeparser . ParseSize ( throttleUploadString , "kb" );
2021-03-22 09:57:56 -06:00
}
long throttleDownload = 0 ;
2021-03-23 17:40:34 -06:00
if ( options . TryGetValue ( "throttle-download" , out string throttleDownloadString ))
2021-03-22 09:57:56 -06:00
{
if (!( backend is IStreamingBackend ) || disableStreaming )
{
Console . WriteLine ( "Warning: Throttling is only supported in this tool on streaming backends" );
}
2021-03-22 10:47:44 -06:00
throttleDownload = Duplicati . Library . Utility . Sizeparser . ParseSize ( throttleDownloadString , "kb" );
2021-03-22 09:57:56 -06:00
}
2013-02-12 21:43:14 +00:00
if ( options . ContainsKey ( "number-of-files" ))
number_of_files = int . Parse ( options [ "number-of-files" ]);
if ( options . ContainsKey ( "min-file-size" ))
min_file_size = ( int ) Duplicati . Library . Utility . Sizeparser . ParseSize ( options [ "min-file-size" ], "mb" );
if ( options . ContainsKey ( "max-file-size" ))
2013-08-17 13:30:55 +02:00
max_file_size = ( int ) Duplicati . Library . Utility . Sizeparser . ParseSize ( options [ "max-file-size" ], "mb" );
2013-02-12 21:43:14 +00:00
if ( options . ContainsKey ( "min-filename-length" ))
min_filename_size = int . Parse ( options [ "min-filename-length" ]);
if ( options . ContainsKey ( "max-filename-length" ))
max_filename_size = int . Parse ( options [ "max-filename-length" ]);
Random rnd = new Random ();
System . Security . Cryptography . SHA256 sha = System . Security . Cryptography . SHA256 . Create ();
//Create random files
using ( Library . Utility . TempFolder tf = new Duplicati . Library . Utility . TempFolder ())
{
List < TempFile > files = new List < TempFile >();
for ( int i = 0 ; i < number_of_files ; i ++)
{
2018-03-20 13:48:21 -06:00
string filename = CreateRandomRemoteFileName ( min_filename_size , max_filename_size , allowedChars , trimFilenameSpaces , rnd );
2013-02-12 21:43:14 +00:00
string localfilename = CreateRandomFile ( tf , i , min_file_size , max_file_size , rnd );
//Calculate local hash and length
using ( System . IO . FileStream fs = new System . IO . FileStream ( localfilename , System . IO . FileMode . Open , System . IO . FileAccess . Read ))
2018-03-20 13:48:21 -06:00
files . Add ( new TempFile ( filename , localfilename , sha . ComputeHash ( fs ), fs . Length ));
2013-02-12 21:43:14 +00:00
}
byte [] dummyFileHash = null ;
if (! skipOverwriteTest )
{
Console . WriteLine ( "Uploading wrong files ..." );
2013-04-16 22:36:19 +02:00
using ( Library . Utility . TempFile dummy = Library . Utility . TempFile . WrapExistingFile ( CreateRandomFile ( tf , files . Count , 1024 , 2048 , rnd )))
2013-02-12 21:43:14 +00:00
{
using ( System . IO . FileStream fs = new System . IO . FileStream ( dummy , System . IO . FileMode . Open , System . IO . FileAccess . Read ))
dummyFileHash = sha . ComputeHash ( fs );
//Upload a dummy file for entry 0 and the last one, they will be replaced by the real files afterwards
//We upload entry 0 twice just to try to freak any internal cache list
2021-03-22 09:57:56 -06:00
Uploadfile ( dummy , 0 , files [ 0 ]. remotefilename , backend , disableStreaming , throttleUpload );
Uploadfile ( dummy , 0 , files [ 0 ]. remotefilename , backend , disableStreaming , throttleUpload );
Uploadfile ( dummy , files . Count - 1 , files [ files . Count - 1 ]. remotefilename , backend , disableStreaming , throttleUpload );
2013-02-12 21:43:14 +00:00
}
}
Console . WriteLine ( "Uploading files ..." );
for ( int i = 0 ; i < files . Count ; i ++)
2021-03-22 09:57:56 -06:00
Uploadfile ( files [ i ]. localfilename , i , files [ i ]. remotefilename , backend , disableStreaming , throttleUpload );
2013-02-12 21:43:14 +00:00
2018-03-20 13:50:06 -06:00
TempFile originalRenamedFile = null ;
string renamedFileNewName = null ;
IRenameEnabledBackend renameEnabledBackend = backend as IRenameEnabledBackend ;
if ( renameEnabledBackend != null )
{
// Rename the second file in the list, if there are more than one. If not, just do the first one.
int renameIndex = files . Count > 1 ? 1 : 0 ;
originalRenamedFile = files [ renameIndex ];
renamedFileNewName = CreateRandomRemoteFileName ( min_filename_size , max_filename_size , allowedChars , trimFilenameSpaces , rnd );
Console . WriteLine ( "Renaming file {0} from {1} to {2}" , renameIndex , originalRenamedFile . remotefilename , renamedFileNewName );
renameEnabledBackend . Rename ( originalRenamedFile . remotefilename , renamedFileNewName );
files [ renameIndex ] = new TempFile ( renamedFileNewName , originalRenamedFile . localfilename , originalRenamedFile . hash , originalRenamedFile . length );
}
2013-02-12 21:43:14 +00:00
Console . WriteLine ( "Verifying file list ..." );
curlist = backend . List ();
foreach ( Library . Interface . IFileEntry fe in curlist )
if (! fe . IsFolder )
{
bool found = false ;
foreach ( TempFile tx in files )
if ( tx . remotefilename == fe . Name )
{
if ( tx . found )
Console . WriteLine ( "*** File with name {0} was found more than once" , tx . remotefilename );
found = true ;
tx . found = true ;
if ( fe . Size > 0 && tx . length != fe . Size )
Console . WriteLine ( "*** File with name {0} has size {1} but the size was reported as {2}" , tx . remotefilename , tx . length , fe . Size );
break ;
}
if (! found )
2018-03-20 13:50:06 -06:00
if ( originalRenamedFile != null && renamedFileNewName != null && originalRenamedFile . remotefilename == fe . Name )
{
Console . WriteLine ( "*** File with name {0} was found on server but was supposed to have been renamed to {1}!" , fe . Name , renamedFileNewName );
}
else
{
Console . WriteLine ( "*** File with name {0} was found on server but not uploaded!" , fe . Name );
}
2013-02-12 21:43:14 +00:00
}
foreach ( TempFile tx in files )
if (! tx . found )
Console . WriteLine ( "*** File with name {0} was uploaded but not found afterwards" , tx . remotefilename );
Console . WriteLine ( "Downloading files" );
for ( int i = 0 ; i < files . Count ; i ++)
{
using ( Duplicati . Library . Utility . TempFile cf = new Duplicati . Library . Utility . TempFile ())
{
Exception e = null ;
Console . Write ( "Downloading file {0} ... " , i );
try
{
2019-09-29 20:16:28 -07:00
if ( backend is IStreamingBackend streamingBackend && ! disableStreaming )
2013-02-12 21:43:14 +00:00
{
using ( System . IO . FileStream fs = new System . IO . FileStream ( cf , System . IO . FileMode . Create , System . IO . FileAccess . Write , System . IO . FileShare . None ))
2021-03-22 09:57:56 -06:00
using ( Library . Utility . ThrottledStream ts = new Library . Utility . ThrottledStream ( fs , throttleDownload , throttleDownload ))
using ( NonSeekableStream nss = new NonSeekableStream ( ts ))
2019-09-29 20:16:28 -07:00
streamingBackend . Get ( files [ i ]. remotefilename , nss );
2013-02-12 21:43:14 +00:00
}
else
backend . Get ( files [ i ]. remotefilename , cf );
e = null ;
}
catch ( Exception ex )
{
e = ex ;
}
if ( e != null )
2017-11-26 15:24:53 -08:00
Console . WriteLine ( "failed\n*** Error: {0}" , e );
2013-02-12 21:43:14 +00:00
else
Console . WriteLine ( "done" );
Console . Write ( "Checking hash ... " );
using ( System . IO . FileStream fs = new System . IO . FileStream ( cf , System . IO . FileMode . Open , System . IO . FileAccess . Read ))
if ( Convert . ToBase64String ( sha . ComputeHash ( fs )) != Convert . ToBase64String ( files [ i ]. hash ))
{
if ( dummyFileHash != null && Convert . ToBase64String ( sha . ComputeHash ( fs )) == Convert . ToBase64String ( dummyFileHash ))
Console . WriteLine ( "failed\n*** Downloaded file was the dummy file" );
else
Console . WriteLine ( "failed\n*** Downloaded file was corrupt" );
}
else
Console . WriteLine ( "done" );
}
}
Console . WriteLine ( "Deleting files..." );
foreach ( TempFile tx in files )
try { backend . Delete ( tx . remotefilename ); }
catch ( Exception ex )
{
2017-11-26 15:24:53 -08:00
Console . WriteLine ( "*** Failed to delete file {0}, message: {1}" , tx . remotefilename , ex );
2013-02-12 21:43:14 +00:00
}
curlist = backend . List ();
foreach ( Library . Interface . IFileEntry fe in curlist )
if (! fe . IsFolder )
{
Console . WriteLine ( "*** Remote folder contains {0} after cleanup" , fe . Name );
}
2020-03-23 14:10:40 -06:00
// Test some error cases
Console . WriteLine ( "Checking retrieval of non-existent file..." );
bool caughtExpectedException = false ;
try
{
using ( Duplicati . Library . Utility . TempFile tempFile = new Duplicati . Library . Utility . TempFile ())
{
backend . Get ( string . Format ( "NonExistentFile-{0}" , Guid . NewGuid ()), tempFile . Name );
}
}
2020-03-29 19:17:37 -07:00
catch ( FileMissingException )
2020-03-23 14:10:40 -06:00
{
Console . WriteLine ( "Caught expected FileMissingException" );
caughtExpectedException = true ;
}
catch ( Exception ex )
{
Console . WriteLine ( "*** Retrieval of non-existent file failed: {0}" , ex );
}
if (! caughtExpectedException )
{
Console . WriteLine ( "*** Retrieval of non-existent file should have failed with FileMissingException" );
}
2013-02-12 21:43:14 +00:00
}
2018-03-20 13:53:32 -06:00
// Test quota retrieval
IQuotaEnabledBackend quotaEnabledBackend = backend as IQuotaEnabledBackend ;
if ( quotaEnabledBackend != null )
{
Console . WriteLine ( "Checking quota..." );
IQuotaInfo quota = null ;
bool noException ;
try
{
quota = quotaEnabledBackend . Quota ;
noException = true ;
}
catch ( Exception ex )
{
Console . WriteLine ( "*** Checking quota information failed: {0}" , ex );
noException = false ;
}
if ( noException )
{
if ( quota != null )
{
Console . WriteLine ( "Free Space: {0}" , Library . Utility . Utility . FormatSizeString ( quota . FreeQuotaSpace ));
Console . WriteLine ( "Total Space: {0}" , Library . Utility . Utility . FormatSizeString ( quota . TotalQuotaSpace ));
}
else
{
Console . WriteLine ( "Unable to retrieve quota information" );
}
}
}
2018-03-20 15:26:17 -06:00
// Test DNSName lookup
Console . WriteLine ( "Checking DNS names used by this backend..." );
try
{
string [] dnsNames = backend . DNSName ;
if ( dnsNames != null )
{
foreach ( string dnsName in dnsNames )
{
Console . WriteLine ( dnsName );
}
}
else
{
Console . WriteLine ( "No DNS names reported" );
}
}
catch ( Exception ex )
{
Console . WriteLine ( "*** Checking DNSName failed: {0}" , ex );
}
2013-02-12 21:43:14 +00:00
}
finally
{
foreach ( Library . Interface . IGenericModule m in loadedModules )
2019-09-29 20:16:28 -07:00
if ( m is IDisposable disposable )
disposable . Dispose ();
2013-02-12 21:43:14 +00:00
}
return true ;
}
2021-03-22 09:57:56 -06:00
private static void Uploadfile ( string localfilename , int i , string remotefilename , IBackend backend , bool disableStreaming , long throttle )
2013-02-12 21:43:14 +00:00
{
Console . Write ( "Uploading file {0}, {1} ... " , i , Duplicati . Library . Utility . Utility . FormatSizeString ( new System . IO . FileInfo ( localfilename ). Length ));
Exception e = null ;
try
{
2019-09-29 20:16:28 -07:00
if ( backend is IStreamingBackend streamingBackend && ! disableStreaming )
2013-02-12 21:43:14 +00:00
{
using ( System . IO . FileStream fs = new System . IO . FileStream ( localfilename , System . IO . FileMode . Open , System . IO . FileAccess . Read , System . IO . FileShare . Read ))
2021-03-22 09:57:56 -06:00
using ( Library . Utility . ThrottledStream ts = new Library . Utility . ThrottledStream ( fs , throttle , throttle ))
using ( NonSeekableStream nss = new NonSeekableStream ( ts ))
2019-09-29 20:16:28 -07:00
streamingBackend . PutAsync ( remotefilename , nss , CancellationToken . None ). Wait ();
2013-02-12 21:43:14 +00:00
}
else
2019-03-17 18:20:14 -05:00
backend . PutAsync ( remotefilename , localfilename , CancellationToken . None ). Wait ();
2013-02-12 21:43:14 +00:00
e = null ;
}
catch ( Exception ex )
{
e = ex ;
}
if ( e != null )
{
2017-11-26 15:24:53 -08:00
Console . WriteLine ( "Failed to upload file {0}, error message: {1}, remote name: {2}" , i , e , remotefilename );
2013-02-12 21:43:14 +00:00
while ( e . InnerException != null )
{
e = e . InnerException ;
2020-01-25 17:07:02 -08:00
Console . WriteLine ( " Inner exception: {0}" , e );
2013-02-12 21:43:14 +00:00
}
}
else
{
Console . WriteLine ( " done!" );
}
}
2018-03-20 13:48:21 -06:00
private static string CreateRandomRemoteFileName ( int min_filename_size , int max_filename_size , string allowedChars , bool trimFilenameSpaces , Random rnd )
{
StringBuilder filenameBuilder = new StringBuilder ();
int filenamelen = rnd . Next ( min_filename_size , max_filename_size );
for ( int j = 0 ; j < filenamelen ; j ++)
filenameBuilder . Append ( allowedChars [ rnd . Next ( 0 , allowedChars . Length )]);
string filename = filenameBuilder . ToString ();
if ( trimFilenameSpaces )
filename = filename . Trim ();
return filename ;
}
2013-02-12 21:43:14 +00:00
private static string CreateRandomFile ( Library . Utility . TempFolder tf , int i , int min_file_size , int max_file_size , Random rnd )
{
Console . Write ( "Generating file {0}" , i );
string filename = System . IO . Path . Combine ( tf , i . ToString ());
using ( System . IO . FileStream fs = new System . IO . FileStream ( filename , System . IO . FileMode . CreateNew , System . IO . FileAccess . Write ))
{
//Random size
byte [] buf = new byte [ 1024 ];
int size = rnd . Next ( min_file_size , max_file_size );
Console . WriteLine ( " ({0})" , Duplicati . Library . Utility . Utility . FormatSizeString ( size ));
while ( size > 0 )
{
rnd . NextBytes ( buf );
fs . Write ( buf , 0 , Math . Min ( buf . Length , size ));
size -= buf . Length ;
}
}
return filename ;
}
public static IList < ICommandLineArgument > SupportedCommands
{
get
{
return new List < ICommandLineArgument >( new ICommandLineArgument [] {
new CommandLineArgument ( "reruns" , CommandLineArgument . ArgumentType . Integer , "The number of test runs to perform" , "A number that describes how many times the test is performed" , "5" ),
new CommandLineArgument ( "tempdir" , CommandLineArgument . ArgumentType . Path , "The path used to store temporary files" , "The backend tester will use the system default temp path. You can set this option to choose another path." ),
new CommandLineArgument ( "extended-chars" , CommandLineArgument . ArgumentType . String , "A list of allowed extended filename chars" , "A list of characters besides {a-z, A-Z, 0-9} to use when generating filenames" , ExtendedChars ),
new CommandLineArgument ( "number-of-files" , CommandLineArgument . ArgumentType . Integer , "The number of files to test with" , "An integer describing how many files to upload during a test run" , "10" ),
2017-01-23 22:12:24 +01:00
new CommandLineArgument ( "min-file-size" , CommandLineArgument . ArgumentType . Size , "The minimum allowed file size" , "File sizes are chosen at random, this value is the lower bound" , "1kb" ),
new CommandLineArgument ( "max-file-size" , CommandLineArgument . ArgumentType . Size , "The maximum allowed file size" , "File sizes are chosen at random, this value is the upper bound" , "50mb" ),
new CommandLineArgument ( "min-filename-length" , CommandLineArgument . ArgumentType . Integer , "The minimum allowed filename length" , "File name lengths are chosen at random, this value is the lower bound" , "5" ),
new CommandLineArgument ( "max-filename-length" , CommandLineArgument . ArgumentType . Integer , "The minimum allowed filename length" , "File name lengths are chosen at random, this value is the upper bound" , "80" ),
2018-03-20 13:48:21 -06:00
new CommandLineArgument ( "trim-filename-spaces" , CommandLineArgument . ArgumentType . Boolean , "Trims whitespace from filenames" , "A value that indicates if whitespace should be trimmed from the ends of randomly generated filenames" , "false" ),
2013-02-12 21:43:14 +00:00
new CommandLineArgument ( "auto-create-folder" , CommandLineArgument . ArgumentType . Boolean , "Allows automatic folder creation" , "A value that indicates if missing folders are created automatically" , "false" ),
new CommandLineArgument ( "skip-overwrite-test" , CommandLineArgument . ArgumentType . Boolean , "Bypasses the overwrite test" , "A value that indicates if dummy files should be uploaded prior to uploading the real files" , "false" ),
new CommandLineArgument ( "auto-clean" , CommandLineArgument . ArgumentType . Boolean , "Removes any files found in target folder" , "A value that indicates if all files in the target folder should be deleted before starting the first test" , "false" ),
2014-06-30 11:31:06 +02:00
new CommandLineArgument ( "force" , CommandLineArgument . ArgumentType . Boolean , "Activates file deletion" , "A value that indicates if existing files should really be deleted when using auto-clean" , "false" ),
2013-02-12 21:43:14 +00:00
});
}
}
}
}