Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -255,7 +255,7 @@ using Azure.Storage.Files.Shares;
var shareClient = new ShareClient(...);

await using var directory = await TemporaryShareDirectory.CreateIfNotExistsAsync(
shareClient, "<directory-name", logger);
shareClient, "<directory-name", logger, cancellationToken);

// Interact with the directory during the lifetime of the fixture.
ShareDirectoryClient client = directory.Client;
Expand All @@ -271,7 +271,7 @@ await using TemporaryShareDirectory directory = ...
string fileName = "TestFile";
await using Stream fileContents = File.OpenRead(...);

await directory.UpsertFileAsync(fileName, fileContents);
await directory.UpsertFileAsync(fileName, fileContents, cancellationToken);
```

:::praise
Expand Down Expand Up @@ -335,14 +335,12 @@ The `TemporaryShareFile` provides a solution when the integration test requires
```csharp
using Arcus.Testing;

ShareDirectoryClient client = ...

string fileName = "TestFile";
ShareFileClient client = ...
await using Stream fileContents = File.OpenRead(...);

await using var file = await TemporaryShareFile.UpsertFileAsync(
client, fileName, fileContents, logger);
client, fileContents, logger, cancellationToken);
```

</TabItem>
</Tabs>
</Tabs>
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@
<AnalysisMode>All</AnalysisMode>
</PropertyGroup>

<ItemGroup>
<Compile Include="..\ObsoleteDefaults.cs" Link="ObsoleteDefaults.cs" />
</ItemGroup>

<ItemGroup>
<None Include="..\..\README.md" Pack="true" PackagePath="\" />
<None Include="..\..\LICENSE" Pack="true" PackagePath="\" />
Expand Down
112 changes: 91 additions & 21 deletions src/Arcus.Testing.Storage.File.Share/TemporaryShareDirectory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using Azure;
using Azure.Storage.Files.Shares;
Expand Down Expand Up @@ -83,7 +84,7 @@

/// <summary>
/// (default) Configures the <see cref="TemporaryShareDirectory"/> to clean only the items (both files and directories) in the directory share
/// that the test fixture was responsible for upserting (via <see cref="TemporaryShareDirectory.UpsertFileAsync"/>), upon the deletion of the fixture.
/// that the test fixture was responsible for upserting (via <see cref="TemporaryShareDirectory.UpsertFileAsync(string,Stream,CancellationToken)"/>), upon the deletion of the fixture.
/// </summary>
public OnTeardownTemporaryShareDirectoryOptions CleanUpsertedItems()
{
Expand Down Expand Up @@ -207,6 +208,7 @@
/// <param name="logger">The logger instance to write diagnostic traces during the lifetime of the fixture.</param>
/// <exception cref="ArgumentNullException">Thrown when the <paramref name="shareClient"/> is <c>null</c>.</exception>
/// <exception cref="ArgumentException">Thrown when the <paramref name="directoryName"/> is blank.</exception>
[Obsolete("Will be removed in v3, please use the " + nameof(CreateIfNotExistsAsync) + " overload instead that provides cancellation token support", DiagnosticId = ObsoleteDefaults.DiagnosticId)]

Check warning on line 211 in src/Arcus.Testing.Storage.File.Share/TemporaryShareDirectory.cs

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Do not forget to remove this deprecated code someday.

See more on https://sonarcloud.io/project/issues?id=arcus-azure_arcus.testing&issues=AZyZP2JJ23gWdVaFu2Lg&open=AZyZP2JJ23gWdVaFu2Lg&pullRequest=512
public static Task<TemporaryShareDirectory> CreateIfNotExistsAsync(ShareClient shareClient, string directoryName, ILogger logger)
{
return CreateIfNotExistsAsync(shareClient, directoryName, logger, configureOptions: null);
Expand All @@ -221,6 +223,7 @@
/// <param name="configureOptions">The additional options to manipulate the behavior of the test fixture during its lifetime.</param>
/// <exception cref="ArgumentNullException">Thrown when the <paramref name="shareClient"/> is <c>null</c>.</exception>
/// <exception cref="ArgumentException">Thrown when the <paramref name="directoryName"/> is blank.</exception>
[Obsolete("Will be removed in v3, please use the " + nameof(CreateIfNotExistsAsync) + " overload instead that provides cancellation token support", DiagnosticId = ObsoleteDefaults.DiagnosticId)]

Check warning on line 226 in src/Arcus.Testing.Storage.File.Share/TemporaryShareDirectory.cs

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Do not forget to remove this deprecated code someday.

See more on https://sonarcloud.io/project/issues?id=arcus-azure_arcus.testing&issues=AZyZP2JJ23gWdVaFu2Lj&open=AZyZP2JJ23gWdVaFu2Lj&pullRequest=512
public static Task<TemporaryShareDirectory> CreateIfNotExistsAsync(
ShareClient shareClient,
string directoryName,
Expand All @@ -240,6 +243,7 @@
/// <param name="directoryClient">The client to interact with the directory share in the Azure Files share resource.</param>
/// <param name="logger">The logger instance to write diagnostic traces during the lifetime of the fixture.</param>
/// <exception cref="ArgumentNullException">Thrown when the <paramref name="directoryClient"/> is <c>null</c>.</exception>
[Obsolete("Will be removed in v3, please use the " + nameof(CreateIfNotExistsAsync) + " overload instead that provides cancellation token support", DiagnosticId = ObsoleteDefaults.DiagnosticId)]

Check warning on line 246 in src/Arcus.Testing.Storage.File.Share/TemporaryShareDirectory.cs

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Do not forget to remove this deprecated code someday.

See more on https://sonarcloud.io/project/issues?id=arcus-azure_arcus.testing&issues=AZyZP2JJ23gWdVaFu2Lh&open=AZyZP2JJ23gWdVaFu2Lh&pullRequest=512
public static Task<TemporaryShareDirectory> CreateIfNotExistsAsync(ShareDirectoryClient directoryClient, ILogger logger)
{
return CreateIfNotExistsAsync(directoryClient, logger, configureOptions: null);
Expand All @@ -252,29 +256,65 @@
/// <param name="logger">The logger instance to write diagnostic traces during the lifetime of the fixture.</param>
/// <param name="configureOptions">The additional options to manipulate the behavior of the test fixture during its lifetime.</param>
/// <exception cref="ArgumentNullException">Thrown when the <paramref name="directoryClient"/> is <c>null</c>.</exception>
public static async Task<TemporaryShareDirectory> CreateIfNotExistsAsync(
[Obsolete("Will be removed in v3, please use the " + nameof(CreateIfNotExistsAsync) + " overload instead that provides cancellation token support", DiagnosticId = ObsoleteDefaults.DiagnosticId)]

Check warning on line 259 in src/Arcus.Testing.Storage.File.Share/TemporaryShareDirectory.cs

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Do not forget to remove this deprecated code someday.

See more on https://sonarcloud.io/project/issues?id=arcus-azure_arcus.testing&issues=AZyZP2JJ23gWdVaFu2Li&open=AZyZP2JJ23gWdVaFu2Li&pullRequest=512
public static Task<TemporaryShareDirectory> CreateIfNotExistsAsync(
ShareDirectoryClient directoryClient,
ILogger logger,
Action<TemporaryShareDirectoryOptions> configureOptions)
{
return CreateIfNotExistsAsync(directoryClient, logger, configureOptions, CancellationToken.None);
}

/// <summary>
/// Creates a temporary directory share on an Azure Files share resource.
/// </summary>
/// <param name="directoryClient">The client to interact with the directory share in the Azure Files share resource.</param>
/// <param name="logger">The logger instance to write diagnostic traces during the lifetime of the fixture.</param>
/// <param name="cancellationToken">The optional token to propagate notifications that the operation should be cancelled.</param>
/// <exception cref="ArgumentNullException">Thrown when the <paramref name="directoryClient"/> is <c>null</c>.</exception>
/// <exception cref="RequestFailedException">Thrown when the interaction with Azure failed.</exception>
public static Task<TemporaryShareDirectory> CreateIfNotExistsAsync(
ShareDirectoryClient directoryClient,
ILogger logger,
CancellationToken cancellationToken)
{
return CreateIfNotExistsAsync(directoryClient, logger, configureOptions: null, cancellationToken);
}

/// <summary>
/// Creates a temporary directory share on an Azure Files share resource.
/// </summary>
/// <param name="directoryClient">The client to interact with the directory share in the Azure Files share resource.</param>
/// <param name="logger">The logger instance to write diagnostic traces during the lifetime of the fixture.</param>
/// <param name="configureOptions">The additional options to manipulate the behavior of the test fixture during its lifetime.</param>
/// <param name="cancellationToken">The optional token to propagate notifications that the operation should be cancelled.</param>
/// <exception cref="ArgumentNullException">Thrown when the <paramref name="directoryClient"/> is <c>null</c>.</exception>
/// <exception cref="RequestFailedException">Thrown when the interaction with Azure failed.</exception>
public static async Task<TemporaryShareDirectory> CreateIfNotExistsAsync(
ShareDirectoryClient directoryClient,
ILogger logger,
Action<TemporaryShareDirectoryOptions> configureOptions,
CancellationToken cancellationToken)
{
ArgumentNullException.ThrowIfNull(directoryClient);
cancellationToken.ThrowIfCancellationRequested();
logger ??= NullLogger.Instance;

var options = new TemporaryShareDirectoryOptions();
configureOptions?.Invoke(options);

if (await directoryClient.ExistsAsync().ConfigureAwait(false))
if (await directoryClient.ExistsAsync(cancellationToken: cancellationToken).ConfigureAwait(false))
{
logger.LogSetupUseExistingDirectory(directoryClient.Name, directoryClient.AccountName, directoryClient.Path);
await CleanDirectoryOnSetupAsync(directoryClient, options, logger).ConfigureAwait(false);
await CleanDirectoryOnSetupAsync(directoryClient, options, logger, cancellationToken).ConfigureAwait(false);

return new TemporaryShareDirectory(directoryClient, createdByUs: false, options, logger);
}

try
{
logger.LogSetupCreateNewDirectory(directoryClient.Name, directoryClient.AccountName, directoryClient.Path);
await directoryClient.CreateAsync().ConfigureAwait(false);
await directoryClient.CreateAsync(cancellationToken: cancellationToken).ConfigureAwait(false);
}
catch (RequestFailedException exception) when (exception.ErrorCode == ShareErrorCode.ShareNotFound)
{
Expand All @@ -288,20 +328,25 @@
return new TemporaryShareDirectory(directoryClient, createdByUs: true, options, logger);
}

private static async Task CleanDirectoryOnSetupAsync(ShareDirectoryClient directoryClient, TemporaryShareDirectoryOptions options, ILogger logger)
private static async Task CleanDirectoryOnSetupAsync(
ShareDirectoryClient directoryClient,
TemporaryShareDirectoryOptions options,
ILogger logger,
CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
if (options.OnSetup.Items is OnSetupDirectoryShare.LeaveExisted)
{
return;
}

if (options.OnSetup.Items is OnSetupDirectoryShare.CleanIfExisted)
{
await DeleteAllDirectoryContentsAsync(directoryClient, TestFixture.Setup, logger).ConfigureAwait(false);
await DeleteAllDirectoryContentsAsync(directoryClient, TestFixture.Setup, logger, cancellationToken).ConfigureAwait(false);
}
else if (options.OnSetup.Items is OnSetupDirectoryShare.CleanIfMatched)
{
await DeleteDirectoryContentsAsync(directoryClient, options.OnSetup.IsMatch, TestFixture.Setup, logger).ConfigureAwait(false);
await DeleteDirectoryContentsAsync(directoryClient, options.OnSetup.IsMatch, TestFixture.Setup, logger, cancellationToken).ConfigureAwait(false);
}
}

Expand All @@ -316,10 +361,32 @@
/// <exception cref="ObjectDisposedException">Thrown when the test fixture was already teared down.</exception>
/// <exception cref="ArgumentException">Thrown when the <paramref name="fileName"/> is blank.</exception>
/// <exception cref="ArgumentNullException">Thrown when the <paramref name="fileContents"/> is <c>null</c>.</exception>
public async Task UpsertFileAsync(string fileName, Stream fileContents)
[Obsolete("Will be removed in v3, please use the " + nameof(UpsertFileAsync) + " overload instead that provides cancellation token support", DiagnosticId = ObsoleteDefaults.DiagnosticId)]

Check warning on line 364 in src/Arcus.Testing.Storage.File.Share/TemporaryShareDirectory.cs

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Do not forget to remove this deprecated code someday.

See more on https://sonarcloud.io/project/issues?id=arcus-azure_arcus.testing&issues=AZyZP2JJ23gWdVaFu2Lk&open=AZyZP2JJ23gWdVaFu2Lk&pullRequest=512
public Task UpsertFileAsync(string fileName, Stream fileContents)
{
return UpsertFileAsync(fileName, fileContents, CancellationToken.None);
}

/// <summary>
/// Creates a new or replaces an existing file in this directory share (a.k.a. UPSERT).
/// </summary>
/// <remarks>
/// Any files uploaded via this call will always be deleted (if new) or reverted (if existing) when this instance is disposed.
/// </remarks>
/// <param name="fileName">The name of the file to upload to the share directory.</param>
/// <param name="fileContents">The contents of the file to upload to the share directory.</param>
/// <param name="cancellationToken">The optional token to propagate notifications that the operation should be cancelled.</param>
/// <exception cref="ObjectDisposedException">Thrown when the test fixture was already teared down.</exception>
/// <exception cref="ArgumentException">Thrown when the <paramref name="fileName"/> is blank.</exception>
/// <exception cref="ArgumentNullException">Thrown when the <paramref name="fileContents"/> is <c>null</c>.</exception>
/// <exception cref="RequestFailedException">Thrown when the interaction with Azure failed.</exception>
public async Task UpsertFileAsync(string fileName, Stream fileContents, CancellationToken cancellationToken)
{
ObjectDisposedException.ThrowIf(_disposables.IsDisposed, this);
_files.Add(await TemporaryShareFile.UpsertFileAsync(Client, fileName, fileContents, _logger).ConfigureAwait(false));
cancellationToken.ThrowIfCancellationRequested();

ShareFileClient fileClient = Client.GetFileClient(fileName);
_files.Add(await TemporaryShareFile.UpsertFileAsync(fileClient, fileContents, _logger, cancellationToken).ConfigureAwait(false));
}

/// <summary>
Expand All @@ -339,7 +406,7 @@

if (_createdByUs)
{
await DeleteAllDirectoryContentsAsync(_client, TestFixture.Teardown, _logger).ConfigureAwait(false);
await DeleteAllDirectoryContentsAsync(_client, TestFixture.Teardown, _logger, CancellationToken.None).ConfigureAwait(false);

_disposables.Add(AsyncDisposable.Create(async () =>
{
Expand All @@ -365,7 +432,7 @@

if (_options.OnTeardown.Items is OnTeardownDirectoryShare.CleanAll)
{
await DeleteAllDirectoryContentsAsync(_client, TestFixture.Teardown, _logger).ConfigureAwait(false);
await DeleteAllDirectoryContentsAsync(_client, TestFixture.Teardown, _logger, CancellationToken.None).ConfigureAwait(false);
}
else if (_options.OnTeardown.Items is OnTeardownDirectoryShare.CleanIfMatched)
{
Expand All @@ -379,33 +446,35 @@
ShareDirectoryClient current,
Func<ShareFileItem, bool> shouldDeleteItem,
TestFixture state,
ILogger logger)
ILogger logger,
CancellationToken cancellationToken = default)
{
await current.ForceCloseAllHandlesAsync(recursive: true).ConfigureAwait(false);
cancellationToken.ThrowIfCancellationRequested();
await current.ForceCloseAllHandlesAsync(recursive: true, cancellationToken: cancellationToken).ConfigureAwait(false);

await foreach (ShareFileItem item in current.GetFilesAndDirectoriesAsync().ConfigureAwait(false))
await foreach (ShareFileItem item in current.GetFilesAndDirectoriesAsync(cancellationToken: cancellationToken).ConfigureAwait(false))
{
if (item.IsDirectory)
{
ShareDirectoryClient sub = current.GetSubdirectoryClient(item.Name);
if (shouldDeleteItem(item))
{
await DeleteAllDirectoryContentsAsync(sub, state, logger).ConfigureAwait(false);
await DeleteAllDirectoryContentsAsync(sub, state, logger, cancellationToken).ConfigureAwait(false);

LogDeleteDirectory(logger, state, sub.Name, sub.AccountName, sub.Path);
await sub.DeleteIfExistsAsync().ConfigureAwait(false);
await sub.DeleteIfExistsAsync(cancellationToken: cancellationToken).ConfigureAwait(false);
}
else
{
await DeleteDirectoryContentsAsync(sub, shouldDeleteItem, state, logger).ConfigureAwait(false);
await DeleteDirectoryContentsAsync(sub, shouldDeleteItem, state, logger, cancellationToken).ConfigureAwait(false);
}
}
else if (shouldDeleteItem(item))
{
ShareFileClient file = current.GetFileClient(item.Name);

LogDeleteFile(logger, state, file.Name, file.AccountName, file.Path);
await file.DeleteIfExistsAsync().ConfigureAwait(false);
await file.DeleteIfExistsAsync(cancellationToken: cancellationToken).ConfigureAwait(false);
}
}
}
Expand Down Expand Up @@ -438,9 +507,10 @@
}
}

private static async Task DeleteAllDirectoryContentsAsync(ShareDirectoryClient current, TestFixture state, ILogger logger)
private static async Task DeleteAllDirectoryContentsAsync(ShareDirectoryClient current, TestFixture state, ILogger logger, CancellationToken cancellationToken)
{
await DeleteDirectoryContentsAsync(current, shouldDeleteItem: _ => true, state, logger).ConfigureAwait(false);
cancellationToken.ThrowIfCancellationRequested();
await DeleteDirectoryContentsAsync(current, shouldDeleteItem: _ => true, state: state, logger: logger, cancellationToken).ConfigureAwait(false);
}
}

Expand Down
Loading
Loading