using System.Threading.Tasks; using Microsoft.CodeAnalysis.CSharp.Testing; using Microsoft.CodeAnalysis.Testing; using Microsoft.CodeAnalysis.Testing.Verifiers; using SqrtSpace.SpaceTime.Analyzers; using Xunit; namespace SqrtSpace.SpaceTime.Tests.Analyzers; public class LargeAllocationCodeFixTests { private static async Task VerifyCodeFixAsync(string source, string fixedSource) { var test = new CSharpCodeFixTest { TestCode = source, FixedCode = fixedSource, ReferenceAssemblies = ReferenceAssemblies.Net.Net60 }; // Add reference to SpaceTime libraries test.TestState.AdditionalReferences.Add(typeof(SqrtSpace.SpaceTime.Linq.SpaceTimeEnumerable).Assembly); test.TestState.AdditionalReferences.Add(typeof(SqrtSpace.SpaceTime.Collections.AdaptiveList<>).Assembly); await test.RunAsync(); } [Fact] public async Task ToList_FixesToCheckpointedListAsync() { const string source = @" using System.Linq; using System.Collections.Generic; using System.Threading.Tasks; class TestClass { async Task TestMethod() { var context = new TestContext(); var list = context.LargeCollection.{|ST001:ToList|}(); } } class TestContext { public IQueryable LargeCollection { get; set; } }"; const string fixedSource = @" using System.Linq; using System.Collections.Generic; using System.Threading.Tasks; using SqrtSpace.SpaceTime.Linq; class TestClass { async Task TestMethod() { var context = new TestContext(); var list = await context.LargeCollection.ToCheckpointedListAsync(); } } class TestContext { public IQueryable LargeCollection { get; set; } }"; await VerifyCodeFixAsync(source, fixedSource); } [Fact] public async Task OrderBy_FixesToOrderByExternal() { const string source = @" using System.Linq; class TestClass { void TestMethod() { var context = new AppContext(); var sorted = context.Users.{|ST001:OrderBy|}(u => u.Name).ToList(); } } class AppContext { public IQueryable Users { get; set; } } class User { public string Name { get; set; } }"; const string fixedSource = @" using System.Linq; using SqrtSpace.SpaceTime.Linq; class TestClass { void TestMethod() { var context = new AppContext(); var sorted = context.Users.OrderByExternal(u => u.Name).ToList(); } } class AppContext { public IQueryable Users { get; set; } } class User { public string Name { get; set; } }"; await VerifyCodeFixAsync(source, fixedSource); } [Fact] public async Task OrderByDescending_FixesToOrderByDescendingExternal() { const string source = @" using System.Linq; class TestClass { void TestMethod() { var context = new DataContext(); var sorted = context.Items.{|ST001:OrderByDescending|}(i => i.Value); } } class DataContext { public IQueryable Items { get; set; } } class Item { public int Value { get; set; } }"; const string fixedSource = @" using System.Linq; using SqrtSpace.SpaceTime.Linq; class TestClass { void TestMethod() { var context = new DataContext(); var sorted = context.Items.OrderByDescendingExternal(i => i.Value); } } class DataContext { public IQueryable Items { get; set; } } class Item { public int Value { get; set; } }"; await VerifyCodeFixAsync(source, fixedSource); } [Fact] public async Task GroupBy_FixesToGroupByExternal() { const string source = @" using System.Linq; class TestClass { void TestMethod() { var context = new OrderContext(); var grouped = context.Orders.{|ST001:GroupBy|}(o => o.Category); } } class OrderContext { public IQueryable Orders { get; set; } } class Order { public string Category { get; set; } }"; const string fixedSource = @" using System.Linq; using SqrtSpace.SpaceTime.Linq; class TestClass { void TestMethod() { var context = new OrderContext(); var grouped = context.Orders.GroupByExternal(o => o.Category); } } class OrderContext { public IQueryable Orders { get; set; } } class Order { public string Category { get; set; } }"; await VerifyCodeFixAsync(source, fixedSource); } [Fact] public async Task LargeList_FixesToAdaptiveList() { const string source = @" using System.Collections.Generic; class TestClass { void TestMethod() { var list = {|ST001:new List(100000)|}; } }"; const string fixedSource = @" using System.Collections.Generic; using SqrtSpace.SpaceTime.Collections; class TestClass { void TestMethod() { var list = new AdaptiveList(); } }"; await VerifyCodeFixAsync(source, fixedSource); } [Fact] public async Task ToList_InNonAsyncMethod_MakesMethodAsync() { const string source = @" using System.Linq; using System.Collections.Generic; class TestClass { List TestMethod() { var context = new TestContext(); return context.LargeCollection.{|ST001:ToList|}(); } } class TestContext { public IQueryable LargeCollection { get; set; } }"; const string fixedSource = @" using System.Linq; using System.Collections.Generic; using SqrtSpace.SpaceTime.Linq; class TestClass { async Task> TestMethod() { var context = new TestContext(); return await context.LargeCollection.ToCheckpointedListAsync(); } } class TestContext { public IQueryable LargeCollection { get; set; } }"; await VerifyCodeFixAsync(source, fixedSource); } [Fact] public async Task ComplexLinqChain_FixesMultipleOperations() { const string source = @" using System.Linq; class TestClass { void TestMethod() { var context = new BigDataContext(); var result = context.Transactions .Where(t => t.Amount > 100) .{|ST001:OrderBy|}(t => t.Date) .{|ST002:GroupBy|}(t => t.Category) .Select(g => new { Category = g.Key, Count = g.Count() }) .{|ST003:ToList|}(); } } class BigDataContext { public IQueryable Transactions { get; set; } } class Transaction { public DateTime Date { get; set; } public string Category { get; set; } public decimal Amount { get; set; } }"; // Note: In practice, multiple code fixes would be applied separately // This test shows the first fix (OrderBy -> OrderByExternal) const string fixedSource = @" using System.Linq; using SqrtSpace.SpaceTime.Linq; class TestClass { void TestMethod() { var context = new BigDataContext(); var result = context.Transactions .Where(t => t.Amount > 100) .OrderByExternal(t => t.Date) .{|ST002:GroupBy|}(t => t.Category) .Select(g => new { Category = g.Key, Count = g.Count() }) .{|ST003:ToList|}(); } } class BigDataContext { public IQueryable Transactions { get; set; } } class Transaction { public DateTime Date { get; set; } public string Category { get; set; } public decimal Amount { get; set; } }"; await VerifyCodeFixAsync(source, fixedSource); } [Fact] public async Task PreservesFormatting_AndComments() { const string source = @" using System.Linq; class TestClass { void TestMethod() { var context = new AppContext(); // Get all users sorted by name var sorted = context.Users .Where(u => u.IsActive) .{|ST001:OrderBy|}(u => u.Name) // Sort by name .ToList(); } } class AppContext { public IQueryable Users { get; set; } } class User { public string Name { get; set; } public bool IsActive { get; set; } }"; const string fixedSource = @" using System.Linq; using SqrtSpace.SpaceTime.Linq; class TestClass { void TestMethod() { var context = new AppContext(); // Get all users sorted by name var sorted = context.Users .Where(u => u.IsActive) .OrderByExternal(u => u.Name) // Sort by name .ToList(); } } class AppContext { public IQueryable Users { get; set; } } class User { public string Name { get; set; } public bool IsActive { get; set; } }"; await VerifyCodeFixAsync(source, fixedSource); } }