aboutsummaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
authorShadowghost <Ghost_of_Stone@web.de>2026-09-05 07:08:14 +0200
committerShadowghost <Ghost_of_Stone@web.de>2026-09-05 07:09:29 +0200
commite5cd3381acefbe13aa36a26059c28629e23e1944 (patch)
treef6703d141322c2ce50812076427096745eeced44 /tests
parent46dd7d8e99ff7167d2d175878896294ed823927f (diff)
Limit cache size again
Co-Authored-By: Cody Robibero <cody@robibe.ro>
Diffstat (limited to 'tests')
-rw-r--r--tests/Jellyfin.Controller.Tests/DirectoryServiceTests.cs73
-rw-r--r--tests/Jellyfin.Controller.Tests/LibraryTaskScheduler/LimitedConcurrencyLibrarySchedulerTests.cs3
2 files changed, 76 insertions, 0 deletions
diff --git a/tests/Jellyfin.Controller.Tests/DirectoryServiceTests.cs b/tests/Jellyfin.Controller.Tests/DirectoryServiceTests.cs
index 7c275b78cc..e57fbfe473 100644
--- a/tests/Jellyfin.Controller.Tests/DirectoryServiceTests.cs
+++ b/tests/Jellyfin.Controller.Tests/DirectoryServiceTests.cs
@@ -1,3 +1,4 @@
+using System.Globalization;
using System.IO;
using System.Linq;
using MediaBrowser.Controller.Providers;
@@ -327,6 +328,78 @@ namespace Jellyfin.Controller.Tests
}
[Fact]
+ public void GetFileSystemEntries_MoreRecordsThanTheCeiling_DropsCache()
+ {
+ // Charged by the files in a listing, not the number of listings, so a few big folders
+ // reach the limit where a lot of small ones would not.
+ const int FolderCount = 60;
+ var bigListing = new FileSystemMetadata[5000];
+ for (var i = 0; i < bigListing.Length; i++)
+ {
+ bigListing[i] = new FileSystemMetadata
+ {
+ FullName = "/music/track" + i.ToString(CultureInfo.InvariantCulture),
+ IsDirectory = false
+ };
+ }
+
+ var fileSystemMock = new Mock<IFileSystem>();
+ fileSystemMock.Setup(f => f.GetFileSystemEntries(It.IsAny<string>()))
+ .Returns(bigListing);
+
+ var directoryService = new DirectoryService(fileSystemMock.Object);
+
+ const string FirstPath = "/music/artist0";
+ directoryService.GetFileSystemEntries(FirstPath);
+
+ for (var i = 1; i < FolderCount; i++)
+ {
+ directoryService.GetFileSystemEntries("/music/artist" + i.ToString(CultureInfo.InvariantCulture));
+ }
+
+ directoryService.GetFileSystemEntries(FirstPath);
+
+ fileSystemMock.Verify(f => f.GetFileSystemEntries(FirstPath), Times.Exactly(2));
+ }
+
+ [Fact]
+ public void GetFileSystemEntries_RepeatedlyInvalidatedFolder_KeepsUnrelatedEntriesCached()
+ {
+ // Invalidating gives the records back, so churning one folder must not add up to the
+ // ceiling and drop everything else with it.
+ const int ChurnCount = 50;
+ var bigListing = new FileSystemMetadata[5000];
+ for (var i = 0; i < bigListing.Length; i++)
+ {
+ bigListing[i] = new FileSystemMetadata
+ {
+ FullName = "/music/track" + i.ToString(CultureInfo.InvariantCulture),
+ IsDirectory = false
+ };
+ }
+
+ var fileSystemMock = new Mock<IFileSystem>();
+ fileSystemMock.Setup(f => f.GetFileSystemEntries(It.IsAny<string>()))
+ .Returns(bigListing);
+
+ var directoryService = new DirectoryService(fileSystemMock.Object);
+
+ const string ChurnedPath = "/music/watched";
+ const string StablePath = "/music/untouched";
+ directoryService.GetFileSystemEntries(StablePath);
+
+ for (var i = 0; i < ChurnCount; i++)
+ {
+ directoryService.GetFileSystemEntries(ChurnedPath);
+ directoryService.Invalidate(ChurnedPath);
+ }
+
+ directoryService.GetFileSystemEntries(StablePath);
+
+ fileSystemMock.Verify(f => f.GetFileSystemEntries(StablePath), Times.Once);
+ }
+
+ [Fact]
public void GetFileSystemEntry_MissingPath_IsNotRemembered()
{
const string MissingPath = "/music/not-here";
diff --git a/tests/Jellyfin.Controller.Tests/LibraryTaskScheduler/LimitedConcurrencyLibrarySchedulerTests.cs b/tests/Jellyfin.Controller.Tests/LibraryTaskScheduler/LimitedConcurrencyLibrarySchedulerTests.cs
index 21a719a78f..f776e893a0 100644
--- a/tests/Jellyfin.Controller.Tests/LibraryTaskScheduler/LimitedConcurrencyLibrarySchedulerTests.cs
+++ b/tests/Jellyfin.Controller.Tests/LibraryTaskScheduler/LimitedConcurrencyLibrarySchedulerTests.cs
@@ -65,6 +65,7 @@ namespace Jellyfin.Controller.Tests.LibraryTaskScheduler
/// so a shutdown has to reach them. It does not travel from the linked source back to the one
/// the cleanup cancels, which is what made them immortal.
/// </summary>
+ /// <returns><placeholder>A <see cref="Task"/> representing the asynchronous unit test.</placeholder></returns>
[Fact]
public async Task ApplicationStopping_RetiresRunners()
{
@@ -87,6 +88,7 @@ namespace Jellyfin.Controller.Tests.LibraryTaskScheduler
/// The cleanup used to be a one shot: it never released the scheduling slot it took, so
/// every runner spawned after the first pass stayed around for the lifetime of the server.
/// </summary>
+ /// <returns><placeholder>A <see cref="Task"/> representing the asynchronous unit test.</placeholder></returns>
[Fact]
public async Task Enqueue_RetiresIdleRunnersAfterEveryOperation()
{
@@ -108,6 +110,7 @@ namespace Jellyfin.Controller.Tests.LibraryTaskScheduler
/// Disposing used to sit out the rest of the cleanup grace period, holding up shutdown for
/// up to a minute.
/// </summary>
+ /// <returns><placeholder>A <see cref="Task"/> representing the asynchronous unit test.</placeholder></returns>
[Fact]
public async Task DisposeAsync_DoesNotWaitOutTheGracePeriod()
{