using System; using System.Collections.Generic; using System.Linq; using Xunit; namespace SharpCompress.Test; public class LazyReadOnlyCollectionTests { // Helper class to track how many times items are enumerated from the source private class TrackingEnumerable : IEnumerable { private readonly List _items; public int EnumerationCount { get; private set; } public int ItemsRequestedCount { get; private set; } public TrackingEnumerable(params T[] items) { _items = new List(items); } public IEnumerator GetEnumerator() { EnumerationCount++; return new TrackingEnumerator(this); } System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => GetEnumerator(); private class TrackingEnumerator : IEnumerator { private readonly TrackingEnumerable _parent; private int _index = -1; public TrackingEnumerator(TrackingEnumerable parent) { _parent = parent; } public T Current => _parent._items[_index]; object? System.Collections.IEnumerator.Current => Current; public bool MoveNext() { _index++; if (_index < _parent._items.Count) { _parent.ItemsRequestedCount++; return true; } return false; } public void Reset() => throw new NotSupportedException(); public void Dispose() { } } } [Fact] public void BasicEnumeration_IteratesThroughAllItems() { // Arrange var source = new TrackingEnumerable(1, 2, 3, 4, 5); var collection = new LazyReadOnlyCollection(source); // Act var results = new List(); foreach (var item in collection) { results.Add(item); } // Assert Assert.Equal(5, results.Count); Assert.Equal(new[] { 1, 2, 3, 4, 5 }, results); Assert.Equal(1, source.EnumerationCount); Assert.Equal(5, source.ItemsRequestedCount); } [Fact] public void MultipleEnumerations_UsesCachedBackingList() { // Arrange var source = new TrackingEnumerable("a", "b", "c"); var collection = new LazyReadOnlyCollection(source); // Act - First enumeration var firstResults = new List(); foreach (var item in collection) { firstResults.Add(item); } var itemsRequestedAfterFirst = source.ItemsRequestedCount; // Act - Second enumeration var secondResults = new List(); foreach (var item in collection) { secondResults.Add(item); } // Assert Assert.Equal(firstResults, secondResults); Assert.Equal(new[] { "a", "b", "c" }, secondResults); // Source should only be enumerated once Assert.Equal(1, source.EnumerationCount); // Items should only be requested from source during first enumeration Assert.Equal(itemsRequestedAfterFirst, source.ItemsRequestedCount); } [Fact] public void EnsureFullyLoaded_LoadsAllItemsIntoBackingList() { // Arrange var source = new TrackingEnumerable(10, 20, 30, 40); var collection = new LazyReadOnlyCollection(source); // Act collection.EnsureFullyLoaded(); var loaded = collection.GetLoaded().ToList(); // Assert Assert.Equal(4, loaded.Count); Assert.Equal(new[] { 10, 20, 30, 40 }, loaded); Assert.Equal(4, source.ItemsRequestedCount); } [Fact] public void GetLoaded_ReturnsOnlyLoadedItemsBeforeFullEnumeration() { // Arrange var source = new TrackingEnumerable(1, 2, 3, 4, 5); var collection = new LazyReadOnlyCollection(source); // Act - Partially enumerate (only first 2 items) using var enumerator = collection.GetEnumerator(); enumerator.MoveNext(); // Load item 1 enumerator.MoveNext(); // Load item 2 var loadedItems = collection.GetLoaded().ToList(); // Continue enumeration enumerator.MoveNext(); // Load item 3 var loadedItemsAfter = collection.GetLoaded().ToList(); // Assert Assert.Equal(2, loadedItems.Count); Assert.Equal(new[] { 1, 2 }, loadedItems); Assert.Equal(3, loadedItemsAfter.Count); Assert.Equal(new[] { 1, 2, 3 }, loadedItemsAfter); } [Fact] public void Count_TriggersFullLoad() { // Arrange var source = new TrackingEnumerable(1, 2, 3, 4, 5); var collection = new LazyReadOnlyCollection(source); // Act var count = collection.Count; // Assert Assert.Equal(5, count); Assert.Equal(5, source.ItemsRequestedCount); Assert.Equal(5, collection.GetLoaded().Count()); } [Fact] public void Contains_TriggersFullLoadAndSearches() { // Arrange var source = new TrackingEnumerable("apple", "banana", "cherry"); var collection = new LazyReadOnlyCollection(source); // Act var containsBanana = collection.Contains("banana"); var containsOrange = collection.Contains("orange"); // Assert Assert.True(containsBanana); Assert.False(containsOrange); Assert.Equal(3, source.ItemsRequestedCount); Assert.Equal(3, collection.GetLoaded().Count()); } [Fact] public void CopyTo_TriggersFullLoadAndCopiesArray() { // Arrange var source = new TrackingEnumerable(10, 20, 30); var collection = new LazyReadOnlyCollection(source); var array = new int[5]; // Act collection.CopyTo(array, 1); // Assert Assert.Equal(0, array[0]); Assert.Equal(10, array[1]); Assert.Equal(20, array[2]); Assert.Equal(30, array[3]); Assert.Equal(0, array[4]); Assert.Equal(3, source.ItemsRequestedCount); } [Fact] public void EmptySourceEnumerable_ReturnsNoItems() { // Arrange var source = new TrackingEnumerable(); var collection = new LazyReadOnlyCollection(source); // Act var results = new List(); foreach (var item in collection) { results.Add(item); } // Assert Assert.Empty(results); Assert.Equal(0, collection.Count); Assert.Equal(1, source.EnumerationCount); } [Fact] public void SingleItemSourceEnumerable_ReturnsSingleItem() { // Arrange var source = new TrackingEnumerable("only"); var collection = new LazyReadOnlyCollection(source); // Act var results = new List(); foreach (var item in collection) { results.Add(item); } // Assert Assert.Single(results); Assert.Equal("only", results[0]); Assert.Equal(1, collection.Count); } [Fact] public void PartialEnumeration_ThenGetLoaded_ReturnsOnlyEnumeratedItems() { // Arrange var source = new TrackingEnumerable(10, 20, 30, 40, 50); var collection = new LazyReadOnlyCollection(source); // Act - Enumerate only first 3 items using (var enumerator = collection.GetEnumerator()) { var hasMore = enumerator.MoveNext(); Assert.True(hasMore); hasMore = enumerator.MoveNext(); Assert.True(hasMore); hasMore = enumerator.MoveNext(); Assert.True(hasMore); } var loadedItems = collection.GetLoaded().ToList(); // Assert Assert.Equal(3, loadedItems.Count); Assert.Equal(new[] { 10, 20, 30 }, loadedItems); Assert.Equal(3, source.ItemsRequestedCount); } [Fact] public void Reset_ThrowsNotSupportedException() { // Arrange var source = new TrackingEnumerable(1, 2, 3); var collection = new LazyReadOnlyCollection(source); // Act & Assert using var enumerator = collection.GetEnumerator(); enumerator.MoveNext(); Assert.Throws(() => enumerator.Reset()); } [Fact] public void Dispose_OnEnumerator_CompletesSuccessfully() { // Arrange var source = new TrackingEnumerable(1, 2, 3); var collection = new LazyReadOnlyCollection(source); // Act using (var enumerator = collection.GetEnumerator()) { enumerator.MoveNext(); var firstItem = enumerator.Current; Assert.Equal(1, firstItem); // Dispose is called automatically by using } // Assert - should be able to enumerate again after disposal var results = new List(); foreach (var item in collection) { results.Add(item); } Assert.Equal(new[] { 1, 2, 3 }, results); } [Fact] public void IsReadOnly_ReturnsTrue() { // Arrange var source = new TrackingEnumerable(1, 2, 3); var collection = new LazyReadOnlyCollection(source); // Act & Assert Assert.True(collection.IsReadOnly); } [Fact] public void Add_ThrowsNotSupportedException() { // Arrange var source = new TrackingEnumerable(1, 2, 3); var collection = new LazyReadOnlyCollection(source); // Act & Assert Assert.Throws(() => collection.Add(4)); } [Fact] public void Clear_ThrowsNotSupportedException() { // Arrange var source = new TrackingEnumerable(1, 2, 3); var collection = new LazyReadOnlyCollection(source); // Act & Assert Assert.Throws(() => collection.Clear()); } [Fact] public void Remove_ThrowsNotSupportedException() { // Arrange var source = new TrackingEnumerable(1, 2, 3); var collection = new LazyReadOnlyCollection(source); // Act & Assert Assert.Throws(() => collection.Remove(1)); } [Fact] public void NonGenericEnumerator_WorksCorrectly() { // Arrange var source = new TrackingEnumerable(1, 2, 3); var collection = new LazyReadOnlyCollection(source); // Act - Use non-generic IEnumerator var results = new List(); var enumerable = (System.Collections.IEnumerable)collection; foreach (var item in enumerable) { results.Add((int)item); } // Assert Assert.Equal(new[] { 1, 2, 3 }, results); } }