Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
e1933ef
Update SessionStateContainer.cs
NeoCoderMatrix86 Sep 30, 2026
2075633
Update ImportManager.cs
NeoCoderMatrix86 Oct 1, 2026
43bfd92
Update ImportManager.cs
NeoCoderMatrix86 Oct 1, 2026
a03251c
first step for importing audiofiles
NeoCoderMatrix86 Oct 5, 2026
adbefc3
Update AudioImportService.cs
NeoCoderMatrix86 Oct 5, 2026
31c3f9f
Update ImportAudiofilesDialog.razor
NeoCoderMatrix86 Oct 5, 2026
8136000
Update AudioImportService.cs
NeoCoderMatrix86 Oct 5, 2026
b4c7b86
fix title
NeoCoderMatrix86 Oct 5, 2026
d81c503
Update ImportAudiofilesDialog.razor
NeoCoderMatrix86 Oct 5, 2026
34a8807
Update SessionStateContainer.cs
NeoCoderMatrix86 Oct 5, 2026
ede483c
dialog for mapping of audiofiles
NeoCoderMatrix86 Oct 6, 2026
99bc0aa
Update ImportAudiofilesDialog.razor
NeoCoderMatrix86 Oct 6, 2026
8588321
disable ok while not all files are mapped
NeoCoderMatrix86 Oct 6, 2026
db35c40
display track count
NeoCoderMatrix86 Oct 6, 2026
bbe0f7a
add localization
NeoCoderMatrix86 Oct 6, 2026
5698eb7
Update AudioImportService.cs
NeoCoderMatrix86 Oct 6, 2026
66dce51
add and remove files during import
NeoCoderMatrix86 Oct 6, 2026
3f03cd3
Update FileInputManager.cs
NeoCoderMatrix86 Oct 6, 2026
6986f65
Update AudioImportService.cs
NeoCoderMatrix86 Oct 6, 2026
f8e3976
fix audio file upload bug
NeoCoderMatrix86 Oct 7, 2026
200eaa0
map result of dialog
NeoCoderMatrix86 Oct 7, 2026
573e1ff
revoke object urls if needed
NeoCoderMatrix86 Oct 8, 2026
018fc46
Update AudioImportService.cs
NeoCoderMatrix86 Oct 8, 2026
3dcbf7a
add automatic mapping
NeoCoderMatrix86 Oct 8, 2026
ec1753e
move dialog handling to DialogManager
NeoCoderMatrix86 Oct 8, 2026
2339ebe
Update AudioImportService.cs
NeoCoderMatrix86 Oct 8, 2026
c9f74cf
fix text import and upload of files
NeoCoderMatrix86 Oct 9, 2026
cdc5b06
revoke import audiofiles if aborted
NeoCoderMatrix86 Oct 9, 2026
182ba83
add AudioImportServiceTests
NeoCoderMatrix86 Oct 9, 2026
09bc650
fix for tracing import cuesheet
NeoCoderMatrix86 Oct 9, 2026
f8d3cf1
use detection for object urls
NeoCoderMatrix86 Oct 9, 2026
aeb1baa
fix bug for upload audiofile
NeoCoderMatrix86 Oct 12, 2026
f6de428
Update AudioImportService.cs
NeoCoderMatrix86 Oct 12, 2026
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 @@ -116,14 +116,13 @@ public async Task SetPropertiesAsync_WithBrowserFile_ShouldSetPropertiesAsync()
// Arrange
var objectUrl = "blob:123";
var filename = "song.mp3";
var inputId = "inputId";
var browserFile = new Mock<IBrowserFile>();
browserFile.SetupGet(b => b.ContentType).Returns("audio/mpeg");
browserFile.SetupGet(b => b.Name).Returns(filename);

var codec = Audiofile.AudioCodecs.First(x => x.FileExtension == ".mp3");
_fileInputManager.Setup(f => f.GetAudioCodec(filename, "audio/mpeg")).Returns(codec);
_fileInputManager.Setup(f => f.GetObjectUrlAsync(inputId)).ReturnsAsync(objectUrl);
_fileInputManager.Setup(f => f.GetObjectUrlAsync(browserFile.Object)).ReturnsAsync(objectUrl);
_jsRuntime.Setup(js => js.InvokeAsync<double>("getAudioDurationFromFile", It.IsAny<object?[]>())).Returns(new ValueTask<double>(90.0));

var audiofile = new Audiofile();
Expand All @@ -137,7 +136,7 @@ public async Task SetPropertiesAsync_WithBrowserFile_ShouldSetPropertiesAsync()
}
};
// Act
await _audiofileManager.SetPropertiesAsync(audiofile, browserFile.Object, inputId);
await _audiofileManager.SetPropertiesAsync(audiofile, browserFile.Object);

// Assert
Assert.AreEqual(codec, audiofile.AudioCodec);
Expand All @@ -146,7 +145,7 @@ public async Task SetPropertiesAsync_WithBrowserFile_ShouldSetPropertiesAsync()
Assert.AreEqual(TimeSpan.FromSeconds(90), audiofile.Duration);
Assert.AreEqual(1, audiofileChangedFired);
_fileInputManager.Verify(f => f.GetAudioCodec(filename, "audio/mpeg"), Times.Once);
_fileInputManager.Verify(f => f.GetObjectUrlAsync(inputId), Times.Once);
_fileInputManager.Verify(f => f.GetObjectUrlAsync(browserFile.Object), Times.Once);
_jsRuntime.Verify(js => js.InvokeAsync<double>("getAudioDurationFromFile", It.Is<object?[]>(o => o[0] as string == audiofile.ObjectURL)), Times.Once);
}

Expand All @@ -156,14 +155,13 @@ public async Task SetProperty_AudiofileWithDuration_SetsLastTrackEndAlsoAsync()
// Arrange
var objectUrl = "blob:123";
var filename = "song.mp3";
var inputId = "inputId";
var browserFile = new Mock<IBrowserFile>();
browserFile.SetupGet(b => b.ContentType).Returns("audio/mpeg");
browserFile.SetupGet(b => b.Name).Returns(filename);

var codec = Audiofile.AudioCodecs.First(x => x.FileExtension == ".mp3");
_fileInputManager.Setup(f => f.GetAudioCodec(filename, "audio/mpeg")).Returns(codec);
_fileInputManager.Setup(f => f.GetObjectUrlAsync(inputId)).ReturnsAsync(objectUrl);
_fileInputManager.Setup(f => f.GetObjectUrlAsync(browserFile.Object)).ReturnsAsync(objectUrl);
_jsRuntime.Setup(js => js.InvokeAsync<double>("getAudioDurationFromFile", It.IsAny<object?[]>())).Returns(new ValueTask<double>(90.0));
var track1 = new Track()
{
Expand All @@ -189,7 +187,7 @@ public async Task SetProperty_AudiofileWithDuration_SetsLastTrackEndAlsoAsync()
}
};
// Act
await _audiofileManager.SetPropertiesAsync(audiofile, browserFile.Object, inputId);
await _audiofileManager.SetPropertiesAsync(audiofile, browserFile.Object);

// Assert
Assert.AreEqual(codec, audiofile.AudioCodec);
Expand All @@ -199,7 +197,7 @@ public async Task SetProperty_AudiofileWithDuration_SetsLastTrackEndAlsoAsync()
Assert.AreEqual(TimeSpan.FromSeconds(90), track2.End);
Assert.AreEqual(1, audiofileChangedFired);
_fileInputManager.Verify(f => f.GetAudioCodec(filename, "audio/mpeg"), Times.Once);
_fileInputManager.Verify(f => f.GetObjectUrlAsync(inputId), Times.Once);
_fileInputManager.Verify(f => f.GetObjectUrlAsync(browserFile.Object), Times.Once);
_jsRuntime.Verify(js => js.InvokeAsync<double>("getAudioDurationFromFile", It.Is<object?[]>(o => o[0] as string == audiofile.ObjectURL)), Times.Once);
}

Expand All @@ -209,14 +207,13 @@ public async Task SetPropertiesAsync_PreviouslySetAudiofile_ShouldClearFirstAndS
// Arrange
var objectUrl = "blob:123";
var filename = "song.mp3";
var inputId = "inputId";
var browserFile = new Mock<IBrowserFile>();
browserFile.SetupGet(b => b.ContentType).Returns("audio/mpeg");
browserFile.SetupGet(b => b.Name).Returns(filename);

var codec = Audiofile.AudioCodecs.First(x => x.FileExtension == ".mp3");
_fileInputManager.Setup(f => f.GetAudioCodec(filename, "audio/mpeg")).Returns(codec);
_fileInputManager.Setup(f => f.GetObjectUrlAsync(inputId)).ReturnsAsync(objectUrl);
_fileInputManager.Setup(f => f.GetObjectUrlAsync(browserFile.Object)).ReturnsAsync(objectUrl);
_jsRuntime.Setup(js => js.InvokeAsync<double>("getAudioDurationFromFile", It.IsAny<object?[]>())).Returns(new ValueTask<double>(90.0));

var audiofile = new Audiofile
Expand All @@ -232,7 +229,7 @@ public async Task SetPropertiesAsync_PreviouslySetAudiofile_ShouldClearFirstAndS
}
};
// Act
await _audiofileManager.SetPropertiesAsync(audiofile, browserFile.Object, inputId);
await _audiofileManager.SetPropertiesAsync(audiofile, browserFile.Object);

// Assert
Assert.AreEqual(codec, audiofile.AudioCodec);
Expand All @@ -241,7 +238,7 @@ public async Task SetPropertiesAsync_PreviouslySetAudiofile_ShouldClearFirstAndS
Assert.AreEqual(TimeSpan.FromSeconds(90), audiofile.Duration);
Assert.AreEqual(1, audiofileChangedFired);
_fileInputManager.Verify(f => f.GetAudioCodec(filename, "audio/mpeg"), Times.Once);
_fileInputManager.Verify(f => f.GetObjectUrlAsync(inputId), Times.Once);
_fileInputManager.Verify(f => f.GetObjectUrlAsync(browserFile.Object), Times.Once);
_jsRuntime.Verify(js => js.InvokeAsync<double>("getAudioDurationFromFile", It.Is<object?[]>(o => o[0] as string == audiofile.ObjectURL)), Times.Once);
_traceChangeManager.VerifySet(t => t.BulkEdit = true, Times.Once);
_traceChangeManager.VerifySet(t => t.BulkEdit = false, Times.Once);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ public void SetProperty_NewValue_ChangesPropertyAndSetsTracedChange()
{
// Arrange
var cuesheet = new Cuesheet();
_sessionStateContainer.SetupGet(x => x.Cuesheet).Returns(cuesheet);
_sessionStateContainer.Setup(x => x.ActiveCuesheet).Returns(cuesheet);
// Act
_cuesheetManager.SetProperty(x => x.Artist, "Artist");
Expand Down Expand Up @@ -116,6 +117,21 @@ public void SetProperty_DeletedAudiofile_RevokesObjectURLs()
_jsRuntime.Verify(x => x.InvokeAsync<Microsoft.JSInterop.Infrastructure.IJSVoidResult>("revokeAudioObjectURL", It.Is<object[]>(args => args.Length == 1 && args[0].Equals(audiofile2.ObjectURL))), Times.Once());
}

[TestMethod]
public void SetProperty_OnImportCuesheet_DoesNotTraceChanges()
{
// Arrange
var importCuesheet = new Cuesheet();
var cuesheet = new Cuesheet();
_sessionStateContainer.SetupGet(x => x.ImportCuesheet).Returns(importCuesheet);
_sessionStateContainer.SetupGet(x => x.Cuesheet).Returns(cuesheet);
_sessionStateContainer.SetupGet(x => x.ActiveCuesheet).Returns(importCuesheet);
// Act
_cuesheetManager.SetProperty(x => x.Artist, nameof(SetProperty_OnImportCuesheet_DoesNotTraceChanges));
// Assert
_traceChangeManager.Verify(x => x.AddChange(It.Is<TracedChange>(y => y.TraceableObject == importCuesheet)), Times.Never);
}

[TestMethod]
public void IsRecordingPossible_WithoutTracks_ReturnsSuccess()
{
Expand Down
120 changes: 120 additions & 0 deletions AudioCuesheetEditor.Tests/Services/IO/AudioImportServiceTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
//This file is part of AudioCuesheetEditor.

//AudioCuesheetEditor is free software: you can redistribute it and/or modify
//it under the terms of the GNU General Public License as published by
//the Free Software Foundation, either version 3 of the License, or
//(at your option) any later version.

//AudioCuesheetEditor 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 General Public License for more details.

//You should have received a copy of the GNU General Public License
//along with Foobar. If not, see
//<http: //www.gnu.org/licenses />.
using AudioCuesheetEditor.Model.AudioCuesheet;
using AudioCuesheetEditor.Model.IO.Audio;
using AudioCuesheetEditor.Services.AudioCuesheet;
using AudioCuesheetEditor.Services.IO;
using AudioCuesheetEditor.Services.UI;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace AudioCuesheetEditor.Tests.Services.IO
{
[TestClass]
public class AudioImportServiceTests
{
private readonly AudioImportService _service;
private readonly Mock<IDialogManager> _dialogManager;
private readonly Mock<ISessionStateContainer> _sessionStateContainer;
private readonly Mock<ICuesheetManager> _cuesheetManager;

public AudioImportServiceTests()
{
_dialogManager = new();
_sessionStateContainer = new();
_cuesheetManager = new();
_service = new(_dialogManager.Object, _sessionStateContainer.Object, _cuesheetManager.Object);
}

[TestMethod]
public async Task MapAudioImportAsync_WhenMappingIsNotRequired_ReturnsTrueAsync()
{
// Arrange
var cuesheet = new Cuesheet();
var importAudiofileMapping = new Dictionary<Audiofile, Audiofile?>();
_dialogManager.Setup(x => x.ShowImportAudiofilesDialogAsync()).ReturnsAsync(true);
_sessionStateContainer.SetupGet(x => x.ImportAudiofileMapping).Returns(importAudiofileMapping);
_sessionStateContainer.SetupSet(x => x.ImportAudiofileMapping = It.IsAny<Dictionary<Audiofile, Audiofile?>>()).Callback<Dictionary<Audiofile, Audiofile?>>(cs => importAudiofileMapping = cs);
_sessionStateContainer.SetupGet(x => x.ImportAudiofiles).Returns([]);
_sessionStateContainer.SetupGet(x => x.ActiveCuesheet).Returns(cuesheet);
// Act
var result = await _service.MapAudioImportAsync();
// Assert
Assert.IsTrue(result);
}

[TestMethod]
public async Task MapAudioImportAsync_WhenMappingIsRequiredAndDoneAutomatically_ReturnsTrueAsync()
{
// Arrange
var cuesheet = new Cuesheet()
{
Audiofiles = [
new()
]
};
var importAudiofileMapping = new Dictionary<Audiofile, Audiofile?>
{
{ new(), null }
};
var importAudioFiles = new List<Audiofile>
{
new()
};
_dialogManager.Setup(x => x.ShowImportAudiofilesDialogAsync()).ReturnsAsync(true);
_sessionStateContainer.SetupGet(x => x.ImportAudiofileMapping).Returns(importAudiofileMapping);
_sessionStateContainer.SetupSet(x => x.ImportAudiofileMapping = It.IsAny<Dictionary<Audiofile, Audiofile?>>()).Callback<Dictionary<Audiofile, Audiofile?>>(cs => importAudiofileMapping = cs);
_sessionStateContainer.SetupGet(x => x.ImportAudiofiles).Returns(importAudioFiles);
_sessionStateContainer.SetupGet(x => x.ActiveCuesheet).Returns(cuesheet);
// Act
var result = await _service.MapAudioImportAsync();
// Assert
Assert.IsTrue(result);
}

[TestMethod]
public async Task MapAudioImportAsync_WhenMappingIsRequiredAndCannotBeDoneAutomatically_ReturnsDialogResultAsync()
{
// Arrange
var cuesheet = new Cuesheet()
{
Audiofiles = [
new()
]
};
var importAudiofileMapping = new Dictionary<Audiofile, Audiofile?>
{
{ new(), null }
};
var importAudioFiles = new List<Audiofile>
{
new(), new()
};
var dialogResultAborted = true;
_dialogManager.Setup(x => x.ShowImportAudiofilesDialogAsync()).ReturnsAsync(dialogResultAborted);
_sessionStateContainer.SetupGet(x => x.ImportAudiofileMapping).Returns(importAudiofileMapping);
_sessionStateContainer.SetupSet(x => x.ImportAudiofileMapping = It.IsAny<Dictionary<Audiofile, Audiofile?>>()).Callback<Dictionary<Audiofile, Audiofile?>>(cs => importAudiofileMapping = cs);
_sessionStateContainer.SetupGet(x => x.ImportAudiofiles).Returns(importAudioFiles);
_sessionStateContainer.SetupGet(x => x.ActiveCuesheet).Returns(cuesheet);
// Act
var result = await _service.MapAudioImportAsync();
// Assert
Assert.AreEqual(!dialogResultAborted, result);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -232,15 +232,14 @@ public async Task CreateFileUploadsAsync_ReturnsFileUploads_WhenFileHasTextConte
// Arrange
var firstFile = CreateBrowserFile("Test.txt", "text/plain", "Just a test!");
var secondFile = CreateBrowserFile("Test.mp3", "audio/mpeg");
var fileInputId = nameof(CreateFileUploadsAsync_ReturnsFileUploads_WhenFileHasTextContentAsync);
IReadOnlyList<IBrowserFile> browserfiles = [
firstFile,
secondFile
];
var objectUrl = "Some object url!";
_jsRuntimeMock.Setup(js => js.InvokeAsync<String>(It.IsAny<string>(), It.IsAny<object[]>())).ReturnsAsync(objectUrl);
// Act
var result = await _service.CreateFileUploadsAsync(browserfiles, fileInputId);
var result = await _service.CreateFileUploadsAsync(browserfiles);
// Assert
Assert.HasCount(2, result);
Assert.AreEqual(firstFile.Name, result.First().Name);
Expand All @@ -265,7 +264,7 @@ public async Task CreateFileUploadsAsync_ReturnsEmpty_WhenFilesHaveInvalidMimeTy
// Act
var result = await _service.CreateFileUploadsAsync(browserfiles);
// Assert
Assert.HasCount(0, result);
Assert.IsEmpty(result);
}

static IBrowserFile CreateBrowserFile(string name, string contentType, string? content = null)
Expand Down
8 changes: 5 additions & 3 deletions AudioCuesheetEditor.Tests/Services/IO/ImportManagerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ public class ImportManagerTests
private readonly Mock<IFileInputManager> _fileInputManagerMock;
private readonly Mock<ITextImportService> _textImportServiceMock;
private readonly Mock<ITrackManager> _trackManagerMock;
private readonly Mock<IAudioImportService> _audioImportService;
public ImportManagerTests()
{
_traceChangeManagerMock = new();
Expand Down Expand Up @@ -105,8 +106,9 @@ public ImportManagerTests()
_trackManagerMock.Object.CopyValues(track, clone, setLength: setLength);
return clone;
});
_audioImportService = new();
var loggerMock = new Mock<ILogger<ImportManager>>();
_service = new ImportManager(_sessionStateContainerMock.Object, _traceChangeManagerMock.Object, _fileInputManagerMock.Object, _textImportServiceMock.Object, _trackManagerMock.Object, loggerMock.Object);
_service = new ImportManager(_sessionStateContainerMock.Object, _traceChangeManagerMock.Object, _fileInputManagerMock.Object, _textImportServiceMock.Object, _trackManagerMock.Object, _audioImportService.Object, loggerMock.Object);
}

void TrackManager_SetValue<TProperty>(Track track, Expression<Func<Track, TProperty>> propertyExpression, TProperty value, Boolean signalTraceChangeManager = true)
Expand Down Expand Up @@ -385,7 +387,7 @@ public async Task UploadFilesAsync_WithAudiofile_ImportsCorrectly()
}

[TestMethod]
public void ImportCuesheet_WithImportCuesheetAvailable_ImportsCuesheetData()
public async Task ImportCuesheetAsync_WithImportCuesheetAvailable_ImportsCuesheetDataAsync()
{
// Arrange
var track1 = new Track()
Expand Down Expand Up @@ -425,7 +427,7 @@ public void ImportCuesheet_WithImportCuesheetAvailable_ImportsCuesheetData()
IList<Audiofile>? sessionStateContainerImportAudiofiles = [];
_sessionStateContainerMock.SetupGet(x => x.ImportAudiofiles).Returns(() => sessionStateContainerImportAudiofiles);
// Act
_service.ImportCuesheet();
await _service.ImportCuesheetAsync();
// Assert
Assert.IsNotNull(sessionStateContainerCuesheet);
Assert.AreEqual(sessionStateContainerImportCuesheet.Artist, sessionStateContainerCuesheet.Artist);
Expand Down
Loading
Loading