diff --git a/src/ByteSync.Client/Assets/Resources/Resources.fr.resx b/src/ByteSync.Client/Assets/Resources/Resources.fr.resx
index 1f53c5f1d..bb2186922 100644
--- a/src/ByteSync.Client/Assets/Resources/Resources.fr.resx
+++ b/src/ByteSync.Client/Assets/Resources/Resources.fr.resx
@@ -397,6 +397,9 @@ Voulez-vous continuer ?
Erreurs d'identification :
+
+ Entrées ignorées :
+
Démarrage :
@@ -670,6 +673,9 @@ Voulez-vous continuer ?
Total des erreurs de calcul :
+
+ Total des entrées ignorées :
+
Total erreurs d'identification :
diff --git a/src/ByteSync.Client/Assets/Resources/Resources.resx b/src/ByteSync.Client/Assets/Resources/Resources.resx
index 395492cad..69dc16029 100644
--- a/src/ByteSync.Client/Assets/Resources/Resources.resx
+++ b/src/ByteSync.Client/Assets/Resources/Resources.resx
@@ -397,6 +397,9 @@ Would you like to continue ?
Identification Errors:
+
+ Skipped Entries:
+
Start:
@@ -679,6 +682,9 @@ Would you like to continue ?
Total Calculation Errors:
+
+ Total Skipped Entries:
+
Total Identification Errors:
diff --git a/src/ByteSync.Client/Business/Inventories/InventoryMonitorData.cs b/src/ByteSync.Client/Business/Inventories/InventoryMonitorData.cs
index 7224fe229..88cdc0d9f 100644
--- a/src/ByteSync.Client/Business/Inventories/InventoryMonitorData.cs
+++ b/src/ByteSync.Client/Business/Inventories/InventoryMonitorData.cs
@@ -23,6 +23,8 @@ public record InventoryMonitorData
public long UploadTotalVolume { get; set; }
public long UploadedVolume { get; set; }
+
+ public int SkippedEntriesCount { get; set; }
public bool HasNonZeroProperty()
{
@@ -36,6 +38,7 @@ public bool HasNonZeroProperty()
|| AnalyzableVolume != 0
|| IdentifiedVolume != 0
|| UploadTotalVolume != 0
- || UploadedVolume != 0;
+ || UploadedVolume != 0
+ || SkippedEntriesCount != 0;
}
}
diff --git a/src/ByteSync.Client/Business/Inventories/InventoryProcessData.cs b/src/ByteSync.Client/Business/Inventories/InventoryProcessData.cs
index db3317777..7ffdf0a0a 100644
--- a/src/ByteSync.Client/Business/Inventories/InventoryProcessData.cs
+++ b/src/ByteSync.Client/Business/Inventories/InventoryProcessData.cs
@@ -147,6 +147,7 @@ public void RecordSkippedEntry(SkippedEntry entry)
_skippedEntries.Enqueue(entry);
_skippedCountsByReason.AddOrUpdate(entry.Reason, 1, (_, currentCount) => currentCount + 1);
Interlocked.Increment(ref _skippedCount);
+ UpdateMonitorData(m => { m.SkippedEntriesCount += 1; });
}
// should be used during issue 268 implementation
@@ -182,4 +183,4 @@ private void ClearSkippedEntries()
_skippedCountsByReason.Clear();
Interlocked.Exchange(ref _skippedCount, 0);
}
-}
\ No newline at end of file
+}
diff --git a/src/ByteSync.Client/Business/Inventories/InventoryStatistics.cs b/src/ByteSync.Client/Business/Inventories/InventoryStatistics.cs
index 673158891..dcc0a69d0 100644
--- a/src/ByteSync.Client/Business/Inventories/InventoryStatistics.cs
+++ b/src/ByteSync.Client/Business/Inventories/InventoryStatistics.cs
@@ -11,4 +11,6 @@ public record InventoryStatistics
public int AnalyzeErrors { get; init; }
public int IdentificationErrors { get; init; }
+
+ public int TotalSkippedEntries { get; init; }
}
diff --git a/src/ByteSync.Client/Services/Inventories/InventoryStatisticsService.cs b/src/ByteSync.Client/Services/Inventories/InventoryStatisticsService.cs
index bf6c58e46..271a4aae9 100644
--- a/src/ByteSync.Client/Services/Inventories/InventoryStatisticsService.cs
+++ b/src/ByteSync.Client/Services/Inventories/InventoryStatisticsService.cs
@@ -66,7 +66,8 @@ private void DoCompute()
ProcessedVolume = statsCollector.ProcessedSize,
AnalyzeSuccess = statsCollector.Success,
AnalyzeErrors = statsCollector.Errors,
- IdentificationErrors = statsCollector.IdentificationErrors
+ IdentificationErrors = statsCollector.IdentificationErrors,
+ TotalSkippedEntries = statsCollector.TotalSkippedEntries
};
_statisticsSubject.OnNext(stats);
@@ -81,6 +82,8 @@ private void ProcessInventoryFile(InventoryFile inventoryFile, StatisticsCollect
foreach (var part in inventory.InventoryParts)
{
+ collector.TotalSkippedEntries += part.SkippedCount;
+
foreach (var dir in part.DirectoryDescriptions)
{
if (!dir.IsAccessible)
@@ -142,6 +145,8 @@ private class StatisticsCollector
public int Errors { get; set; }
public int IdentificationErrors { get; set; }
+
+ public int TotalSkippedEntries { get; set; }
public long ProcessedSize { get; set; }
}
diff --git a/src/ByteSync.Client/ViewModels/Sessions/Inventories/InventoryGlobalStatusViewModel.cs b/src/ByteSync.Client/ViewModels/Sessions/Inventories/InventoryGlobalStatusViewModel.cs
index 3a6a72d6f..eea8f3fc0 100644
--- a/src/ByteSync.Client/ViewModels/Sessions/Inventories/InventoryGlobalStatusViewModel.cs
+++ b/src/ByteSync.Client/ViewModels/Sessions/Inventories/InventoryGlobalStatusViewModel.cs
@@ -79,10 +79,15 @@ public InventoryGlobalStatusViewModel(IInventoryService inventoryService, ISessi
[Reactive]
public int? GlobalIdentificationErrors { get; set; }
+
+ [Reactive]
+ public int? GlobalSkippedEntries { get; set; }
public extern bool HasErrors { [ObservableAsProperty] get; }
public extern bool HasIdentificationErrors { [ObservableAsProperty] get; }
+
+ public extern bool ShowGlobalSkippedEntries { [ObservableAsProperty] get; }
[Reactive]
public string GlobalMainIcon { get; set; } = "None";
@@ -112,6 +117,11 @@ private void SetupBasicProperties(CompositeDisposable disposables)
.Select(e => (e ?? 0) > 0)
.ToPropertyEx(this, x => x.HasIdentificationErrors)
.DisposeWith(disposables);
+
+ this.WhenAnyValue(x => x.GlobalSkippedEntries)
+ .Select(e => (e ?? 0) > 0)
+ .ToPropertyEx(this, x => x.ShowGlobalSkippedEntries)
+ .DisposeWith(disposables);
}
private ReactiveStreams CreateStreams(IInventoryStatisticsService inventoryStatisticsService, CompositeDisposable disposables)
@@ -270,6 +280,7 @@ private void UpdateStatisticsValues(InventoryStatistics? stats)
GlobalAnalyzeSuccess = stats?.AnalyzeSuccess;
GlobalAnalyzeErrors = stats?.AnalyzeErrors;
GlobalIdentificationErrors = stats?.IdentificationErrors;
+ GlobalSkippedEntries = stats?.TotalSkippedEntries;
}
private void ApplySuccessState(int? errors, int? identificationErrors = null)
@@ -332,6 +343,7 @@ private void ResetStatistics()
GlobalAnalyzeSuccess = null;
GlobalAnalyzeErrors = null;
GlobalIdentificationErrors = null;
+ GlobalSkippedEntries = null;
GlobalMainIcon = "None";
GlobalMainStatusText = string.Empty;
GlobalMainIconBrush = null;
diff --git a/src/ByteSync.Client/ViewModels/Sessions/Inventories/InventoryLocalIdentificationViewModel.cs b/src/ByteSync.Client/ViewModels/Sessions/Inventories/InventoryLocalIdentificationViewModel.cs
index bebb7a953..2d9a0490f 100644
--- a/src/ByteSync.Client/ViewModels/Sessions/Inventories/InventoryLocalIdentificationViewModel.cs
+++ b/src/ByteSync.Client/ViewModels/Sessions/Inventories/InventoryLocalIdentificationViewModel.cs
@@ -52,6 +52,7 @@ private void HandleActivation(CompositeDisposable disposables)
IdentifiedDirectories = m.IdentifiedDirectories;
IdentifiedVolume = m.IdentifiedVolume;
IdentificationErrors = m.IdentificationErrors;
+ SkippedEntriesCount = m.SkippedEntriesCount;
})
.DisposeWith(disposables);
@@ -59,6 +60,11 @@ private void HandleActivation(CompositeDisposable disposables)
.Select(v => v > 0)
.ToPropertyEx(this, x => x.HasIdentificationErrors)
.DisposeWith(disposables);
+
+ this.WhenAnyValue(x => x.SkippedEntriesCount)
+ .Select(v => v > 0)
+ .ToPropertyEx(this, x => x.ShowSkippedEntriesCount)
+ .DisposeWith(disposables);
_inventoryService.InventoryProcessData.IdentificationStatus
.ObserveOn(RxApp.MainThreadScheduler)
@@ -133,8 +139,13 @@ private void HandleActivation(CompositeDisposable disposables)
[Reactive]
public int IdentificationErrors { get; set; }
-
+
public extern bool HasIdentificationErrors { [ObservableAsProperty] get; }
+
+ [Reactive]
+ public int SkippedEntriesCount { get; set; }
+
+ public extern bool ShowSkippedEntriesCount { [ObservableAsProperty] get; }
[Reactive]
public string IdentificationIcon { get; set; } = "None";
diff --git a/src/ByteSync.Client/Views/Sessions/Inventories/InventoryGlobalStatusView.axaml b/src/ByteSync.Client/Views/Sessions/Inventories/InventoryGlobalStatusView.axaml
index b3b428de2..cadf3c2d0 100644
--- a/src/ByteSync.Client/Views/Sessions/Inventories/InventoryGlobalStatusView.axaml
+++ b/src/ByteSync.Client/Views/Sessions/Inventories/InventoryGlobalStatusView.axaml
@@ -57,6 +57,7 @@
+
+
+
+
+
+
diff --git a/src/ByteSync.Client/Views/Sessions/Inventories/InventoryLocalIdentificationView.axaml b/src/ByteSync.Client/Views/Sessions/Inventories/InventoryLocalIdentificationView.axaml
index 4c5262a1f..926dc39ed 100644
--- a/src/ByteSync.Client/Views/Sessions/Inventories/InventoryLocalIdentificationView.axaml
+++ b/src/ByteSync.Client/Views/Sessions/Inventories/InventoryLocalIdentificationView.axaml
@@ -42,6 +42,7 @@
+
+
+
+
+
+
diff --git a/tests/ByteSync.Client.UnitTests/Business/Inventories/InventoryMonitorDataTests.cs b/tests/ByteSync.Client.UnitTests/Business/Inventories/InventoryMonitorDataTests.cs
index 6a45fd0b8..0c395b535 100644
--- a/tests/ByteSync.Client.UnitTests/Business/Inventories/InventoryMonitorDataTests.cs
+++ b/tests/ByteSync.Client.UnitTests/Business/Inventories/InventoryMonitorDataTests.cs
@@ -149,6 +149,19 @@ public void HasNonZeroProperty_WithUploadedVolume_ShouldReturnTrue()
// Assert
result.Should().BeTrue();
}
+
+ [Test]
+ public void HasNonZeroProperty_WithSkippedEntriesCount_ShouldReturnTrue()
+ {
+ // Arrange
+ var data = new InventoryMonitorData { SkippedEntriesCount = 2 };
+
+ // Act
+ var result = data.HasNonZeroProperty();
+
+ // Assert
+ result.Should().BeTrue();
+ }
[Test]
public void HasNonZeroProperty_WithMultipleNonZeroProperties_ShouldReturnTrue()
@@ -201,4 +214,4 @@ public void UploadTotalVolume_ShouldBeSettableAndGettable()
// Assert
data.UploadTotalVolume.Should().Be(10240);
}
-}
\ No newline at end of file
+}
diff --git a/tests/ByteSync.Client.UnitTests/Business/Inventories/InventoryProcessDataTests.cs b/tests/ByteSync.Client.UnitTests/Business/Inventories/InventoryProcessDataTests.cs
index b15cd755e..da296adc9 100644
--- a/tests/ByteSync.Client.UnitTests/Business/Inventories/InventoryProcessDataTests.cs
+++ b/tests/ByteSync.Client.UnitTests/Business/Inventories/InventoryProcessDataTests.cs
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
+using System.Reactive.Linq;
using ByteSync.Business.Inventories;
using ByteSync.Models.Inventories;
using FluentAssertions;
@@ -44,6 +45,21 @@ public void RecordSkippedEntry_ShouldUpdateGlobalAndReasonCounters()
data.GetSkippedCountByReason(SkipReason.NoiseEntry).Should().Be(1);
data.GetSkippedCountByReason(SkipReason.Offline).Should().Be(0);
}
+
+ [Test]
+ public async Task RecordSkippedEntry_ShouldUpdateMonitorSkippedEntriesCount()
+ {
+ // Arrange
+ var data = new InventoryProcessData();
+
+ // Act
+ data.RecordSkippedEntry(new SkippedEntry { Reason = SkipReason.Hidden });
+ data.RecordSkippedEntry(new SkippedEntry { Reason = SkipReason.NoiseEntry });
+ var monitor = await data.InventoryMonitorObservable.FirstAsync();
+
+ // Assert
+ monitor.SkippedEntriesCount.Should().Be(2);
+ }
[Test]
public void Reset_ShouldClearSkippedEntriesAndCounters()
diff --git a/tests/ByteSync.Client.UnitTests/Services/Inventories/InventoryStatisticsServiceTests.cs b/tests/ByteSync.Client.UnitTests/Services/Inventories/InventoryStatisticsServiceTests.cs
index 1ceac833d..9c6b2625f 100644
--- a/tests/ByteSync.Client.UnitTests/Services/Inventories/InventoryStatisticsServiceTests.cs
+++ b/tests/ByteSync.Client.UnitTests/Services/Inventories/InventoryStatisticsServiceTests.cs
@@ -41,7 +41,7 @@ private static string CreateInventoryZip(Inventory inventory)
}
private static Inventory BuildInventoryWithFiles(int successCount, int errorCount, int neutralCount,
- int inaccessibleFiles = 0, int inaccessibleDirectories = 0)
+ int inaccessibleFiles = 0, int inaccessibleDirectories = 0, int skippedHidden = 0, int skippedNoise = 0)
{
var inv = new Inventory
{
@@ -122,6 +122,16 @@ private static Inventory BuildInventoryWithFiles(int successCount, int errorCoun
};
part.DirectoryDescriptions.Add(dir);
}
+
+ for (var i = 0; i < skippedHidden; i++)
+ {
+ part.RecordSkippedEntry(SkipReason.Hidden);
+ }
+
+ for (var i = 0; i < skippedNoise; i++)
+ {
+ part.RecordSkippedEntry(SkipReason.NoiseEntry);
+ }
return inv;
}
@@ -167,6 +177,7 @@ public async Task Compute_WithSingleInventory_ComputesExpectedTotals()
stats.AnalyzeSuccess.Should().Be(3);
stats.AnalyzeErrors.Should().Be(2);
stats.IdentificationErrors.Should().Be(0);
+ stats.TotalSkippedEntries.Should().Be(0);
}
finally
{
@@ -229,6 +240,7 @@ public async Task Compute_WithMultipleInventories_AggregatesAll()
stats.AnalyzeSuccess.Should().Be(1 + 2);
stats.AnalyzeErrors.Should().Be(1 + 0);
stats.IdentificationErrors.Should().Be(0);
+ stats.TotalSkippedEntries.Should().Be(0);
}
finally
{
@@ -286,6 +298,55 @@ public async Task Compute_CountsIdentificationErrors()
stats.AnalyzeSuccess.Should().Be(0);
stats.AnalyzeErrors.Should().Be(0);
stats.IdentificationErrors.Should().Be(3);
+ stats.TotalSkippedEntries.Should().Be(0);
+ }
+ finally
+ {
+ if (File.Exists(zip))
+ {
+ File.Delete(zip);
+ }
+ }
+ }
+
+ [Test]
+ public async Task Compute_CountsSkippedEntriesFromInventoryParts()
+ {
+ var inv = BuildInventoryWithFiles(successCount: 0, errorCount: 0, neutralCount: 0, skippedHidden: 2, skippedNoise: 1);
+ var zip = CreateInventoryZip(inv);
+
+ try
+ {
+ var sfd = new SharedFileDefinition
+ {
+ SessionId = "S",
+ ClientInstanceId = inv.Endpoint.ClientInstanceId,
+ SharedFileType = SharedFileTypes.FullInventory,
+ AdditionalName = inv.CodeAndId,
+ IV = new byte[16]
+ };
+ var inventoryFile = new InventoryFile(sfd, zip);
+
+ var repo = new Mock();
+ repo.Setup(r => r.GetAllInventoriesFiles(LocalInventoryModes.Full))
+ .Returns([inventoryFile]);
+
+ var ipd = new InventoryProcessData();
+ var invService = new Mock();
+ invService.SetupGet(s => s.InventoryProcessData).Returns(ipd);
+
+ var logger = new Mock>();
+
+ var service = new InventoryStatisticsService(invService.Object, repo.Object, logger.Object);
+
+ var tcs = new TaskCompletionSource();
+ using var sub = service.Statistics.Where(s => s != null).Take(1).Select(s => s!).Subscribe(s => tcs.TrySetResult(s));
+
+ ipd.AreFullInventoriesComplete.OnNext(true);
+
+ var stats = await tcs.Task.WaitAsync(TimeSpan.FromSeconds(5));
+
+ stats.TotalSkippedEntries.Should().Be(3);
}
finally
{
diff --git a/tests/ByteSync.Client.UnitTests/ViewModels/Sessions/Inventories/InventoryGlobalStatusViewModelTests.cs b/tests/ByteSync.Client.UnitTests/ViewModels/Sessions/Inventories/InventoryGlobalStatusViewModelTests.cs
index 45ae63da2..db77f3f14 100644
--- a/tests/ByteSync.Client.UnitTests/ViewModels/Sessions/Inventories/InventoryGlobalStatusViewModelTests.cs
+++ b/tests/ByteSync.Client.UnitTests/ViewModels/Sessions/Inventories/InventoryGlobalStatusViewModelTests.cs
@@ -262,6 +262,20 @@ public void SuccessWithIdentificationErrors_RendersErrorState()
vm.HasIdentificationErrors.Should().BeTrue();
vm.GlobalMainIcon.Should().Be("RegularError");
}
+
+ [Test]
+ public void ShowGlobalSkippedEntries_ComputedFromStats()
+ {
+ var vm = CreateVm();
+
+ _statsSubject.OnNext(new InventoryStatistics { TotalSkippedEntries = 4 });
+ vm.ShowGlobalSkippedEntries.Should().BeTrue();
+ vm.GlobalSkippedEntries.Should().Be(4);
+
+ _statsSubject.OnNext(new InventoryStatistics { TotalSkippedEntries = 0 });
+ vm.ShowGlobalSkippedEntries.Should().BeFalse();
+ vm.GlobalSkippedEntries.Should().Be(0);
+ }
[Test]
public async Task AbortCommand_UserConfirms_RequestsAbort()
diff --git a/tests/ByteSync.Client.UnitTests/ViewModels/Sessions/Inventories/InventoryLocalIdentificationViewModelTests.cs b/tests/ByteSync.Client.UnitTests/ViewModels/Sessions/Inventories/InventoryLocalIdentificationViewModelTests.cs
index 67c45ff15..ff4573ffd 100644
--- a/tests/ByteSync.Client.UnitTests/ViewModels/Sessions/Inventories/InventoryLocalIdentificationViewModelTests.cs
+++ b/tests/ByteSync.Client.UnitTests/ViewModels/Sessions/Inventories/InventoryLocalIdentificationViewModelTests.cs
@@ -84,6 +84,24 @@ public void StatusTransitions_UpdateIconsBrushesAndText()
vm.IdentificationStatusText.Should().NotBeNullOrWhiteSpace();
vm.IdentificationStatusText.Should().NotBe(successText);
}
+
+ [Test]
+ public void MonitorUpdates_ShouldUpdateSkippedEntriesAndVisibility()
+ {
+ var vm = CreateVm();
+
+ vm.SkippedEntriesCount.Should().Be(0);
+ vm.ShowSkippedEntriesCount.Should().BeFalse();
+
+ _processData.UpdateMonitorData(m => { m.SkippedEntriesCount = 3; });
+
+ vm.ShouldEventuallyBe(x => x.SkippedEntriesCount, 3);
+ vm.ShouldEventuallyBe(x => x.ShowSkippedEntriesCount, true);
+
+ _processData.UpdateMonitorData(m => { m.SkippedEntriesCount = 0; });
+
+ vm.ShouldEventuallyBe(x => x.ShowSkippedEntriesCount, false);
+ }
private InventoryLocalIdentificationViewModel CreateVm()
{
@@ -92,4 +110,4 @@ private InventoryLocalIdentificationViewModel CreateVm()
return vm;
}
-}
\ No newline at end of file
+}