Skip to content
Merged
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
54 changes: 54 additions & 0 deletions AudioQualityEnhancer.Tests/AudioInfoTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
using System.Globalization;
using AudioQualityEnhancer.Models;
using AudioQualityEnhancer.Services;

namespace AudioQualityEnhancer.Tests;

public sealed class AudioInfoTests
{
/// <summary>
/// AudioInfo subscribes to the localization service in its constructor so its display
/// strings follow a language change. That service lives for the whole session, so an
/// instance that is never disposed stays reachable through it for just as long. This
/// pins the contract that makes disposal matter.
/// </summary>
[Fact]
public void Dispose_StopsListeningForLanguageChanges()
{
var info = new AudioInfo { Codec = "mp3" };
var notifications = 0;
var original = LocalizationService.Instance.Culture;

try
{
// Start from a known culture. Another test class may have left the process on
// English, and assigning the same culture again raises no change at all.
LocalizationService.Instance.Culture = CultureInfo.GetCultureInfo("de");
info.PropertyChanged += (_, _) => notifications++;

LocalizationService.Instance.Culture = CultureInfo.GetCultureInfo("en");
var whileSubscribed = notifications;
Assert.True(whileSubscribed > 0, "A language change has to reach a live instance.");

info.Dispose();
LocalizationService.Instance.Culture = CultureInfo.GetCultureInfo("de");

Assert.Equal(whileSubscribed, notifications);
}
finally
{
LocalizationService.Instance.Culture = original;
info.Dispose();
}
}

[Fact]
public void WithSelectedAudioStream_ReturnsTheSameInstanceWithoutStreams()
{
using var info = new AudioInfo { Codec = "mp3" };

// Returning this is what keeps BatchProcessingItem.SelectAudioStream from handing
// an instance to SetAudioInfo that would then be disposed and stored at once.
Assert.Same(info, info.WithSelectedAudioStream(null));
}
}
30 changes: 22 additions & 8 deletions Services/AudioProcessingService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -85,18 +85,32 @@ public async Task<Result<ProcessResult>> ProcessAsync(
_fileNameService.CleanupTemporaryOutputFiles(options.OutputDirectory, TimeSpan.FromDays(2));

var sourceInfo = options.SourceInfo;
if (sourceInfo is null)
if (sourceInfo is not null)
{
Report(progress, 8, LocalizationService.Instance["Phase_AnalyzingSource"]);
var analysis = await _ffprobeService.AnalyzeAsync(options.InputPath, _logService.Info, cancellationToken);
if (analysis.IsFailure || analysis.Value is null)
{
return Result<ProcessResult>.Failure(analysis.ErrorMessage ?? LocalizationService.Instance["Error_SourceAnalysisFailed"], analysis.Exception);
}
// Supplied by the caller, so it belongs to the queue item and is disposed with it.
return await RenderAsync(options, sourceInfo, progress, cancellationToken);
}

sourceInfo = analysis.Value;
Report(progress, 8, LocalizationService.Instance["Phase_AnalyzingSource"]);
var analysis = await _ffprobeService.AnalyzeAsync(options.InputPath, _logService.Info, cancellationToken);
if (analysis.IsFailure || analysis.Value is null)
{
return Result<ProcessResult>.Failure(analysis.ErrorMessage ?? LocalizationService.Instance["Error_SourceAnalysisFailed"], analysis.Exception);
}

// Created here, so disposed here. AudioInfo subscribes to the localization
// service in its constructor, and that service outlives the run, so an
// undisposed instance would stay reachable for the rest of the session.
using var ownedSourceInfo = analysis.Value;
return await RenderAsync(options, ownedSourceInfo, progress, cancellationToken);
}

private async Task<Result<ProcessResult>> RenderAsync(
ProcessingOptions options,
AudioInfo sourceInfo,
IProgress<ProcessingProgress>? progress,
CancellationToken cancellationToken)
{
var diskSpaceCheck = EnsureSufficientDiskSpace(options.OutputDirectory, EstimateOutputSizeBytes(options, sourceInfo));
if (diskSpaceCheck.IsFailure)
{
Expand Down