Skip to content

Commit

Permalink
Merge branch 'develop'
Browse files Browse the repository at this point in the history
  • Loading branch information
olivier-spinelli committed Dec 14, 2017
2 parents 8558148 + 6b85ea3 commit b1c0526
Show file tree
Hide file tree
Showing 7 changed files with 115 additions and 118 deletions.
62 changes: 56 additions & 6 deletions CK.Core/FileUtil.cs
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,7 @@ public static FileStream CreateAndOpenUniqueTimedFile( string pathPrefix, string
{
if( access == FileAccess.Read ) throw new ArgumentException( Impl.CoreResources.FileUtilNoReadOnlyWhenCreateFile, "access" );
FileStream f = null;
FindUniqueTimedFile( pathPrefix, fileSuffix, time, maxTryBeforeGuid, p => TryCreateNew( p, access, share, bufferSize, options, out f ) );
FindUniqueTimedFileOrFolder( pathPrefix, fileSuffix, time, maxTryBeforeGuid, p => TryCreateNew( p, access, share, bufferSize, options, out f ) );
return f;
}

Expand Down Expand Up @@ -230,7 +230,7 @@ public static string MoveToUniqueTimedFile( string sourceFilePath, string pathPr
{
if( sourceFilePath == null ) throw new ArgumentNullException( "sourceFilePath" );
if( !File.Exists( sourceFilePath ) ) throw new FileNotFoundException( Impl.CoreResources.FileMustExist, sourceFilePath );
return FindUniqueTimedFile( pathPrefix, fileSuffix, time, maxTryBeforeGuid, p => TryMoveTo( sourceFilePath, p ) );
return FindUniqueTimedFileOrFolder( pathPrefix, fileSuffix, time, maxTryBeforeGuid, p => TryMoveTo( sourceFilePath, p ) );
}

/// <summary>
Expand All @@ -241,12 +241,12 @@ public static string MoveToUniqueTimedFile( string sourceFilePath, string pathPr
/// <param name="fileSuffix">Suffix for the file name. Must not be null. Typically an extension (like ".txt").</param>
/// <param name="time">The time that will be used to create the file name. It must be an UTC time.</param>
/// <param name="maxTryBeforeGuid">
/// Maximum value for short hexadecimal uniquifier before using a base 64 guid suffix. Must greater than 0.
/// Maximum value for short hexadecimal uniquifier before using a base 64 guid suffix. Must be greater than 0.
/// </param>
/// <returns>A string to a necessarily unique named file path.</returns>
public static string EnsureUniqueTimedFile( string pathPrefix, string fileSuffix, DateTime time, int maxTryBeforeGuid = 512 )
{
return FindUniqueTimedFile( pathPrefix, fileSuffix, time, maxTryBeforeGuid, p => TryCreateFile( p ) );
return FindUniqueTimedFileOrFolder( pathPrefix, fileSuffix, time, maxTryBeforeGuid, TryCreateFile );
}

static bool TryCreateFile( string path )
Expand All @@ -264,6 +264,56 @@ static bool TryCreateFile( string path )
return false;
}

/// <summary>
/// Gets a path to a necessarily unique time-based named folder.
/// The folder name is based on a <see cref="DateTime"/>, with an eventual uniquifier if a folder already exists with the same name.
/// </summary>
/// <param name="pathPrefix">The path prefix. Must not be null. Must be a valid path and may ends with a prefix for the file name itself.</param>
/// <param name="folderSuffix">Suffix for the folder name. Can be null or empty.</param>
/// <param name="time">The time that will be used to create the file name. It must be an UTC time.</param>
/// <param name="maxTryBeforeGuid">
/// Maximum value for short hexadecimal uniquifier before using a base 64 guid suffix. Must be greater than 0.
/// </param>
/// <returns>The path to a necessarily unique folder.</returns>
public static string CreateUniqueTimedFolder( string pathPrefix, string folderSuffix, DateTime time, int maxTryBeforeGuid = 512 )
{
if( folderSuffix == null ) folderSuffix = String.Empty;
return FindUniqueTimedFileOrFolder( pathPrefix, folderSuffix, time, maxTryBeforeGuid, TryCreateFolder );
}

static bool TryCreateFolder( string path )
{
string origin = null;
try
{
if( Directory.Exists( path ) ) return false;
// Directory.CreateDirectory can not be used here.
// The trick n°1 is moving an empty folder: it fails if the destination already exists.
// The second trick is to always create the parent folder:
// - Move requires it to exist...
// - ... but since we WILL succeed to create the unique folder, we can do it safely.
origin = Path.GetTempPath() + Guid.NewGuid().ToString( "N" );
Directory.CreateDirectory( origin );
Directory.CreateDirectory( Path.GetDirectoryName( path ) );
Directory.Move( origin, path );
return true;
}
catch( IOException ex )
{
if( ex is PathTooLongException ) throw;
try
{
if( origin != null && Directory.Exists( origin ) ) Directory.Delete( origin );
}
catch
{
// Forget the temp folder suppression.
}
}
return false;
}


static bool TryMoveTo( string sourceFilePath, string timedPath )
{
try
Expand All @@ -279,7 +329,7 @@ static bool TryMoveTo( string sourceFilePath, string timedPath )
return false;
}

static string FindUniqueTimedFile( string pathPrefix, string fileSuffix, DateTime time, int maxTryBeforeGuid, Func<string, bool> tester )
static string FindUniqueTimedFileOrFolder( string pathPrefix, string fileSuffix, DateTime time, int maxTryBeforeGuid, Func<string, bool> tester )
{
if( pathPrefix == null ) throw new ArgumentNullException( "pathPrefix" );
if( fileSuffix == null ) throw new ArgumentNullException( "fileSuffix" );
Expand All @@ -298,7 +348,7 @@ static string FindUniqueTimedFile( string pathPrefix, string fileSuffix, DateTim
}
else
{
if( counter == maxTryBeforeGuid + 1 ) throw new Exception( Impl.CoreResources.FileUtilUnableToCreateUniqueTimedFile );
if( counter == maxTryBeforeGuid + 1 ) throw new Exception( Impl.CoreResources.FileUtilUnableToCreateUniqueTimedFileOrFolder );
if( counter == maxTryBeforeGuid )
{
result = pathPrefix + FormatTimedUniqueFilePart( time ) + fileSuffix;
Expand Down
4 changes: 2 additions & 2 deletions CK.Core/Impl/CoreResources.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using System;
using System;

namespace CK.Core.Impl
{
Expand Down Expand Up @@ -43,7 +43,7 @@ public class CoreResources

public static readonly string FileUtilNoReadOnlyWhenCreateFile = "Access set to FileAccess.Read is stupid when creating a file.";

public static readonly string FileUtilUnableToCreateUniqueTimedFile = "Unable to create a unique timed file.";
public static readonly string FileUtilUnableToCreateUniqueTimedFileOrFolder = "Unable to create a unique timed file or folder.";

public static readonly string InnerExceptionMustBeTheFirstAggregatedException = "The InnerException must be the first AggregatedExceptions.";

Expand Down
97 changes: 0 additions & 97 deletions CK.Core/WeakAssemblyNameResolver.cs

This file was deleted.

2 changes: 1 addition & 1 deletion CodeCakeBuilder/Build.cs
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ public Build()
|| gitInfo.PreReleaseName == "prerelease"
|| gitInfo.PreReleaseName == "rc" )
{
PushNuGetPackages( "MYGET_RELEASE_API_KEY", "https://www.myget.org/F/invenietis-preview/api/v2/package", nugetPackages );
PushNuGetPackages( "MYGET_RELEASE_API_KEY", "https://www.myget.org/F/invenietis-release/api/v2/package", nugetPackages );
}
else
{
Expand Down
13 changes: 13 additions & 0 deletions Common/Shared.props
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,19 @@
<GenerateDocumentationFile>true</GenerateDocumentationFile>
</PropertyGroup>

<!-- Reproducible builds. See: http://blog.paranoidcoding.com/2016/04/05/deterministic-builds-in-roslyn.html
On appveyor, the clone folder MUST be set to the solution path in the CK-World.
For this solution, it is:
clone_folder: C:\CK-World\CK-Core-Projects\CK-Core
This must be adapted for each solution/repository.
-->
<PropertyGroup Condition=" '$(CakeBuild)' == 'true' ">
<Deterministic>true</Deterministic>
<CKWorldPath>$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), CK-World))</CKWorldPath>
<PathMap Condition=" '$(CKWorldPath)' != '' ">$(CKWorldPath)=C:\CK-World</PathMap>
</PropertyGroup>

<!--
When building from the CI (ie. from Cake, hence directly with the msbuild tool, SolutionDir is not defined),
Expand Down
54 changes: 42 additions & 12 deletions Tests/CK.Core.Tests/FileUtilTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
using NUnit.Framework;
using System.Globalization;
using FluentAssertions;
using System.Collections.Concurrent;

namespace CK.Core.Tests
{
Expand Down Expand Up @@ -52,7 +53,7 @@ public void WriteUniqueTimedFile()
}

[Test]
public void UniqueTimedFile_is_28_characters_long_as_a_string()
public void UniqueTimedFile_is_27_characters_long_as_a_string_or_50_with_the_GUID_uniquifier()
{
DateTime.UtcNow.ToString( FileUtil.FileNameUniqueTimeUtcFormat, CultureInfo.InvariantCulture ).Length
.Should().Be( 27, "FileNameUniqueTimeUtcFormat => 27 characters long." );
Expand Down Expand Up @@ -98,19 +99,48 @@ public void WriteUniqueTimedFile_clash_never_happen()
TestHelper.CleanupTestFolder();
DateTime now = DateTime.UtcNow;
string prefix = Path.Combine( TestHelper.TestFolder, "Clash " );
List<string> files = new List<string>();
for( int i = 0; i < 10; ++i )
var files = new string[100];
Parallel.ForEach( Enumerable.Range( 0, 100 ), i =>
{
files.Add( FileUtil.WriteUniqueTimedFile( prefix, String.Empty, now, null, false, 0 ) );
}
files.Count.Should().Be( 10 );
files.All( f => f.StartsWith( prefix ) );
files.All( f => File.Exists( f ) );
for( int i = 1; i < 10; ++i )
files[i] = FileUtil.WriteUniqueTimedFile( prefix, String.Empty, now, null, false, 0 );
} );
files.Should().NotContainNulls();
files.Should().OnlyContain( f => f.StartsWith( prefix ) );
files.Should().OnlyContain( f => File.Exists( f ) );
var winner = files.MaxBy( f => -f.Length );
files.Where( f => f.Length == winner.Length + 1 + 22 ).Should().HaveCount( 99, "Ends with Url compliant Base64 GUID." );
}

[Test]
public void CreateUniqueTimedFolder_simple_test()
{
TestHelper.CleanupTestFolder();
DateTime now = DateTime.UtcNow;
var prefix = Path.Combine( TestHelper.TestFolder,"F/Simple/F-" );
var f1 = FileUtil.CreateUniqueTimedFolder( prefix, String.Empty, now );
var f2 = FileUtil.CreateUniqueTimedFolder( prefix, String.Empty, now );
f1.Should().NotBe( f2 );
Directory.Exists( f1 ).Should().BeTrue();
Directory.Exists( f2 ).Should().BeTrue();
}

[Test]
public void CreateUniqueTimedFolder_clash_never_happen()
{
TestHelper.CleanupTestFolder();
DateTime now = DateTime.UtcNow;
var prefixes = new[] {
Path.Combine( TestHelper.TestFolder, "F-Clash/FA" ),
Path.Combine( TestHelper.TestFolder, "F-Clash/FB" ),
Path.Combine( TestHelper.TestFolder, "F-Clash/FA/F1" ) };
var folders = new string[100];
Parallel.ForEach( Enumerable.Range( 0, 100 ), i =>
{
files[i].Length.Should().Be( files[0].Length + 1 + 22, "Ends with Url compliant Base64 GUID." );
}
files.SequenceEqual( files.Distinct() ).Should().BeTrue();
folders[i] = FileUtil.CreateUniqueTimedFolder( prefixes[i % 3], String.Empty, now );
} );
folders.Should().NotContainNulls();
folders.Should().OnlyContain( f => f.StartsWith( prefixes[0] ) || f.StartsWith( prefixes[1] ) || f.StartsWith( prefixes[2] ) );
folders.Should().OnlyContain( f => Directory.Exists( f ) );
}

[Test]
Expand Down
1 change: 1 addition & 0 deletions appveyor.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Basic appveyor configuration : CodeCakeBuilder does the job.
version: build{build}
image: Visual Studio 2017
clone_folder: C:\CK-World\CK-Core-Projects\CK-Core
branches:
only:
- master
Expand Down

0 comments on commit b1c0526

Please sign in to comment.