Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
13 changes: 13 additions & 0 deletions Source/Testably.Abstractions.Migration.Analyzers/Patterns.cs
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,19 @@ public static class Patterns
/// </summary>
public const string MockFileSystemAddDrive = "MockFileSystem.AddDrive";

/// <summary>
/// <c>fs.MockTime(Func&lt;DateTime&gt;)</c>. Manual review (Phase 5.3): TestableIO
/// calls the supplied delegate on every timestamp request, while Testably installs
/// a fixed-then-mutable <c>MockTimeSystem</c> at construction. The two have no
/// observably-equivalent automatic rewrite for arbitrary delegates, and the
/// equivalent surface (<c>o =&gt; o.UseTimeSystem(...)</c>) lives in the
/// <c>MockFileSystemOptions</c> lambda — a cross-statement fold that conflicts
/// with the parameterless / options-ctor fixes when both touch the construction.
/// A future sub-phase may add an opt-in fix for the narrow constant-DateTime
/// lambda shape with a custom <c>FixAllProvider</c>.
/// </summary>
public const string MockFileSystemMockTime = "MockFileSystem.MockTime";

// ── Enumeration properties (Phase 5.1) ────────────────────────────────
// These IMockFileDataAccessor properties enumerate the whole mocked file
// system. Testably has no direct equivalent — the natural replacements
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -314,6 +314,7 @@ private static bool IsMockFileSystemOptions(IParameterSymbol parameter)
"MoveDirectory" => Patterns.AccessorMoveDirectory,
"FileExists" => Patterns.AccessorFileExists,
"AddDrive" => Patterns.MockFileSystemAddDrive,
"MockTime" => Patterns.MockFileSystemMockTime,
_ => null,
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,21 @@
await That(clone.TextContents).IsEqualTo("hello");
}

[Fact]
public async Task MockFileSystem_MockTime_ReturnsSelfForFluentChaining()
{
// TestableIO calls the supplied delegate every time it needs a timestamp.
// Testably installs a fixed-then-mutable MockTimeSystem at construction with
// no equivalent post-construction fluent API, so this site is reported with
// pattern id `MockFileSystem.MockTime` and left for manual migration. The
// playground only needs to keep the call shape compiling; the timestamp
// semantics of MockTime are out of scope for the parity baseline.
MockFileSystem fs = new();

Check warning on line 96 in Tests/Testably.Abstractions.Migration.SystemIOAbstractionsPlayground/ManualReviewTests.cs

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

System.IO.Abstractions MockFileSystem should be migrated to Testably.Abstractions.

See more on https://sonarcloud.io/project/issues?id=Testably_Testably.Abstractions.Migration&issues=AZ4qoD86G33uax-c_Apq&open=AZ4qoD86G33uax-c_Apq&pullRequest=15
MockFileSystem chained = fs.MockTime(() => DateTime.UnixEpoch);

Check warning on line 97 in Tests/Testably.Abstractions.Migration.SystemIOAbstractionsPlayground/ManualReviewTests.cs

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

System.IO.Abstractions MockFileSystem should be migrated to Testably.Abstractions.

See more on https://sonarcloud.io/project/issues?id=Testably_Testably.Abstractions.Migration&issues=AZ4qoD86G33uax-c_Apr&open=AZ4qoD86G33uax-c_Apr&pullRequest=15

await That(chained).IsSameAs(fs);
}

private sealed class MyMockFs : MockFileSystem
{
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,25 @@ await Verifier.VerifyAnalyzerAsync(
Verifier.Diagnostic(Rules.SystemIOAbstractionsRule).WithLocation(0));
}

[Fact]
public async Task MockTime_ShouldBeFlagged()
{
const string source = """
using System;
using System.IO.Abstractions.TestingHelpers;

public class C
{
public void Run(MockFileSystem fs)
=> {|#0:fs.MockTime(() => DateTime.UnixEpoch)|};
}
""";

await Verifier.VerifyAnalyzerAsync(
source,
Verifier.Diagnostic(Rules.SystemIOAbstractionsRule).WithLocation(0));
}

[Theory]
[InlineData("AllPaths")]
[InlineData("AllFiles")]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -705,5 +705,38 @@ await Verifier.VerifyCodeFixAsync(
Verifier.Diagnostic(Rules.SystemIOAbstractionsRule).WithLocation(0),
source);
}

[Theory]
[InlineData("() => System.DateTime.UnixEpoch")]
[InlineData("() => System.DateTime.Now")]
[InlineData("() => System.DateTime.UtcNow")]
[InlineData("dateTimeProvider")]
public async Task MockTime_HasNoFix(string argument)
{
// Phase 5.3 ships MockTime as manual review only. TestableIO calls the
// supplied delegate every time it needs a timestamp; Testably installs a
// fixed-then-mutable MockTimeSystem at construction. The two have no
// observably-equivalent automatic rewrite for arbitrary delegates, and the
// equivalent surface (`o => o.UseTimeSystem(...)`) lives inside the
// MockFileSystemOptions lambda — a cross-statement fold that conflicts
// with the parameterless / options-ctor fixes when both touch the same
// construction. A future sub-phase may opt-in fix the narrow constant-
// DateTime lambda shape with a custom FixAllProvider.
string source = $$"""
using System;
using System.IO.Abstractions.TestingHelpers;

public class C
{
public void Run(MockFileSystem fs, Func<DateTime> dateTimeProvider)
=> {|#0:fs.MockTime({{argument}})|};
}
""";

await Verifier.VerifyCodeFixAsync(
source,
Verifier.Diagnostic(Rules.SystemIOAbstractionsRule).WithLocation(0),
source);
}
}
}
Loading