Skip to content

Michailf netstandard2.0 for atomic file tests#6102

Closed
snakefoot wants to merge 7 commits intoNLog:devfrom
snakefoot:michailf-netstandard2.0_for_AtomicFile-tests
Closed

Michailf netstandard2.0 for atomic file tests#6102
snakefoot wants to merge 7 commits intoNLog:devfrom
snakefoot:michailf-netstandard2.0_for_AtomicFile-tests

Conversation

@snakefoot
Copy link
Contributor

No description provided.

@coderabbitai
Copy link

coderabbitai bot commented Feb 28, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f676cac and 833a70d.

📒 Files selected for processing (2)
  • src/NLog.Targets.AtomicFile/AtomicFileTarget.cs
  • src/NLog.Targets.AtomicFile/Internal/NativeMethods.cs
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/NLog.Targets.AtomicFile/Internal/NativeMethods.cs

Walkthrough

Adds .NET Standard 2.0 support to NLog.Targets.AtomicFile by introducing a NETSTANDARD Windows CreateFile-based stream path with P/Invoke bindings, updating project target frameworks and test project references, and adding a test invocation for netstandard2.0 in the Windows test script.

Changes

Cohort / File(s) Summary
Build & Test Script
run-tests.ps1
Adds Windows test invocation for netstandard2.0 (dotnet test ./tests/NLog.Targets.AtomicFile.Tests/ --configuration release /p:TestTargetFramework=netstandard2.0) and an exit-code check.
Target implementation & interop
src/NLog.Targets.AtomicFile/AtomicFileTarget.cs, src/NLog.Targets.AtomicFile/Internal/NativeMethods.cs
Adds NETSTANDARD-specific Windows code path: new CreateWindowsStream helper that uses Win32 flags and wraps a SafeFileHandle in a FileStream; adds NativeMethods P/Invoke (CreateFile) and Win32 enums; uses conditional compilation to preserve existing paths.
Project configuration
src/NLog.Targets.AtomicFile/NLog.Targets.AtomicFile.csproj
Adds netstandard2.0 to TargetFrameworks, removes net45 block and adjusts dependency/ItemGroup conditions (Mono.Posix.NETStandard).
Test project reference
tests/NLog.Targets.AtomicFile.Tests/NLog.Targets.AtomicFile.Tests.csproj
Adds conditional Reference to the built NLog.Targets.AtomicFile assembly when TargetFramework != net8.0 and TestTargetFramework is set.

Sequence Diagram(s)

sequenceDiagram
    participant AtomicTarget as AtomicFileTarget
    participant CreateWinStream as CreateWindowsStream
    participant NativeMethods as NativeMethods (P/Invoke)
    participant Kernel32 as Kernel32
    participant FileStream as FileStream

    AtomicTarget->>CreateWinStream: Request stream(path, mode)
    CreateWinStream->>NativeMethods: CreateFile(path, access, share, ...)
    NativeMethods->>Kernel32: P/Invoke CreateFile
    Kernel32-->>NativeMethods: SafeFileHandle
    NativeMethods-->>CreateWinStream: SafeFileHandle
    CreateWinStream->>FileStream: Wrap handle into FileStream
    FileStream-->>AtomicTarget: Return FileStream
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐇 I dug a standard path so neat,
P/Invoke paws made handles meet,
Atomic nibs write soft and fast,
Cross-platform hopping built to last. ✨

🚥 Pre-merge checks | ✅ 1 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Description check ❓ Inconclusive No pull request description was provided by the author, making it impossible to assess whether the description relates to the changeset. Add a description explaining the purpose of adding .NET Standard 2.0 support, the implementation approach, and any testing considerations.
✅ Passed checks (1 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly references the main change: adding .NET Standard 2.0 support for atomic file tests, which aligns with the changeset that adds netstandard2.0 to target frameworks and implements platform-specific code paths.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (1)
src/NLog.Targets.AtomicFile/Internal/NativeMethods.cs (1)

82-84: Enum member naming is misleading (non-blocking).

FILE_GENERIC_READ (0x80000000) and FILE_GENERIC_WRITE (0x40000000) are actually GENERIC_READ and GENERIC_WRITE Windows constants. The FILE_GENERIC_* names typically refer to different composite values. This works correctly for the current usage but could cause confusion if the enum is extended.

Optional: Rename for clarity
-            FILE_GENERIC_READ = 0x80000000,
-            FILE_GENERIC_WRITE = 0x40000000,
-            FILE_GENERIC_EXECUTE = 0x20000000,
+            GENERIC_READ = 0x80000000,
+            GENERIC_WRITE = 0x40000000,
+            GENERIC_EXECUTE = 0x20000000,

This would require updating references in AtomicFileTarget.cs accordingly.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/NLog.Targets.AtomicFile/Internal/NativeMethods.cs` around lines 82 - 84,
The enum member names FILE_GENERIC_READ/FILE_GENERIC_WRITE/FILE_GENERIC_EXECUTE
are misleading; rename them to GENERIC_READ, GENERIC_WRITE, GENERIC_EXECUTE in
the enum definition to match Windows constants and update all usages (e.g.,
references in AtomicFileTarget.cs) to the new identifiers; ensure any
XML-doc/comments are adjusted and run a build to catch any remaining references.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/NLog.Targets.AtomicFile/AtomicFileTarget.cs`:
- Around line 152-155: The condition testing whether the incoming FileMode is
Append is incorrect because FileMode is not a [Flags] enum; replace the bitwise
check "(fileMode & FileMode.Append) != 0" with an equality comparison "fileMode
== FileMode.Append" (or an explicit set of allowed modes if multiple modes
should be treated as append) in the method using the local variable fileMode so
that the branch that adds NativeMethods.Win32FileAccess.FILE_APPEND_DATA only
runs for the actual FileMode.Append case.
- Around line 198-199: CreateUnixStream is declared static but calls the
instance member CreateDirs (inherited from FileTarget), which causes a compile
error; fix by making CreateUnixStream an instance method (remove the static
modifier) or change its signature to accept the AtomicFileTarget/FileTarget
instance and update all callers accordingly so the method can call CreateDirs;
update the declaration of CreateUnixStream and any invocations to match the
chosen approach.

In `@tests/NLog.Targets.AtomicFile.Tests/NLog.Targets.AtomicFile.Tests.csproj`:
- Around line 32-40: The ProjectReference and the added Reference can both be
active when TargetFramework != 'net8.0' and TestTargetFramework is set, causing
duplicate assembly references; update the ProjectReference ItemGroup (the
ProjectReference element) to include a complementary Condition so it is skipped
when the Reference is used (i.e., when TestTargetFramework != '' and
TargetFramework != 'net8.0'), ensuring only one of Reference or ProjectReference
is included; modify the Condition on the ProjectReference ItemGroup to be the
inverse of the new Reference's Condition so duplicates are avoided.

---

Nitpick comments:
In `@src/NLog.Targets.AtomicFile/Internal/NativeMethods.cs`:
- Around line 82-84: The enum member names
FILE_GENERIC_READ/FILE_GENERIC_WRITE/FILE_GENERIC_EXECUTE are misleading; rename
them to GENERIC_READ, GENERIC_WRITE, GENERIC_EXECUTE in the enum definition to
match Windows constants and update all usages (e.g., references in
AtomicFileTarget.cs) to the new identifiers; ensure any XML-doc/comments are
adjusted and run a build to catch any remaining references.

ℹ️ Review info

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1594d92 and aaacc86.

📒 Files selected for processing (5)
  • run-tests.ps1
  • src/NLog.Targets.AtomicFile/AtomicFileTarget.cs
  • src/NLog.Targets.AtomicFile/Internal/NativeMethods.cs
  • src/NLog.Targets.AtomicFile/NLog.Targets.AtomicFile.csproj
  • tests/NLog.Targets.AtomicFile.Tests/NLog.Targets.AtomicFile.Tests.csproj

Comment on lines +152 to +155
if ((fileMode & FileMode.Append) != 0)
{
dwDesiredAccess |= NativeMethods.Win32FileAccess.FILE_APPEND_DATA;
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Incorrect use of bitwise AND on non-flags enum.

FileMode is not a [Flags] enum, so using bitwise & can produce incorrect results. For example, FileMode.Create (2) & FileMode.Append (6) = 2, which would incorrectly pass this check.

Use equality comparison instead:

Proposed fix
-                if ((fileMode & FileMode.Append) != 0)
+                if (fileMode == FileMode.Append)
                 {
                     dwDesiredAccess |= NativeMethods.Win32FileAccess.FILE_APPEND_DATA;
                 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if ((fileMode & FileMode.Append) != 0)
{
dwDesiredAccess |= NativeMethods.Win32FileAccess.FILE_APPEND_DATA;
}
if (fileMode == FileMode.Append)
{
dwDesiredAccess |= NativeMethods.Win32FileAccess.FILE_APPEND_DATA;
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/NLog.Targets.AtomicFile/AtomicFileTarget.cs` around lines 152 - 155, The
condition testing whether the incoming FileMode is Append is incorrect because
FileMode is not a [Flags] enum; replace the bitwise check "(fileMode &
FileMode.Append) != 0" with an equality comparison "fileMode == FileMode.Append"
(or an explicit set of allowed modes if multiple modes should be treated as
append) in the method using the local variable fileMode so that the branch that
adds NativeMethods.Win32FileAccess.FILE_APPEND_DATA only runs for the actual
FileMode.Append case.

Comment on lines +198 to +199
#if !NETFRAMEWORK && !WINDOWS
private Stream CreateUnixStream(string filePath)
private static Stream CreateUnixStream(string filePath)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# First, find the CreateUnixStream method and see how it uses CreateDirs
echo "=== CreateUnixStream method context (lines 198-220) ==="
sed -n '198,220p' src/NLog.Targets.AtomicFile/AtomicFileTarget.cs

echo ""
echo "=== Search for CreateDirs property definition ==="
rg -n "CreateDirs" src/NLog.Targets.AtomicFile/ -A 2 -B 2

echo ""
echo "=== Search for CreateDirs in NLog.Targets (FileTarget base class) ==="
rg -n "CreateDirs\s*\{|CreateDirs\s*=>" --type cs | head -20

Repository: NLog/NLog

Length of output: 1937


🏁 Script executed:

#!/bin/bash
# Verify AtomicFileTarget inheritance and check if there's a way it could work
echo "=== Check AtomicFileTarget class definition ==="
head -50 src/NLog.Targets.AtomicFile/AtomicFileTarget.cs | grep -A 5 "class AtomicFileTarget"

echo ""
echo "=== Check if CreateUnixStream is called from instance context ==="
rg -n "CreateUnixStream" src/NLog.Targets.AtomicFile/ -B 3 -A 3

Repository: NLog/NLog

Length of output: 1425


🏁 Script executed:

#!/bin/bash
# Check the method containing the CreateUnixStream call at line 115
echo "=== Method containing CreateUnixStream call (lines 105-125) ==="
sed -n '105,125p' src/NLog.Targets.AtomicFile/AtomicFileTarget.cs

echo ""
echo "=== Verify CreateDirs property in FileTarget ==="
sed -n '95,105p' src/NLog/Targets/FileTarget.cs

Repository: NLog/NLog

Length of output: 1650


🏁 Script executed:

#!/bin/bash
# Check the full method context where CreateUnixStream is called (lines 80-125)
echo "=== Full method containing CreateUnixStream call ==="
sed -n '80,125p' src/NLog.Targets.AtomicFile/AtomicFileTarget.cs | head -60

Repository: NLog/NLog

Length of output: 2176


Remove static keyword or pass this instance to CreateUnixStream.

CreateUnixStream is declared static but references CreateDirs at line 206—an instance property inherited from FileTarget. Static methods cannot access instance members. This will cause a compilation error. Either remove the static modifier or refactor to pass the instance as a parameter.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/NLog.Targets.AtomicFile/AtomicFileTarget.cs` around lines 198 - 199,
CreateUnixStream is declared static but calls the instance member CreateDirs
(inherited from FileTarget), which causes a compile error; fix by making
CreateUnixStream an instance method (remove the static modifier) or change its
signature to accept the AtomicFileTarget/FileTarget instance and update all
callers accordingly so the method can call CreateDirs; update the declaration of
CreateUnixStream and any invocations to match the chosen approach.

Comment on lines +32 to 40
<ItemGroup Condition=" '$(TargetFramework)' != 'net8.0' AND '$(TestTargetFramework)' != '' ">
<Reference Include="NLog.Targets.AtomicFile">
<HintPath>../../src/NLog.Targets.AtomicFile/bin/$(Configuration)/$(TestTargetFramework)/NLog.Targets.AtomicFile.dll</HintPath>
<Private>True</Private>
</Reference>
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\..\src\NLog.Targets.AtomicFile\NLog.Targets.AtomicFile.csproj" />
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Potential duplicate reference when TestTargetFramework is set.

The new Reference (lines 33-36) and the existing ProjectReference (line 40) can both be active when TargetFramework != 'net8.0' and TestTargetFramework != ''. This may cause duplicate assembly warnings or unexpected behavior.

Consider adding a complementary condition to the ProjectReference to exclude it when the direct reference is used:

Proposed fix
-  <ItemGroup>
+  <ItemGroup Condition=" '$(TargetFramework)' == 'net8.0' OR '$(TestTargetFramework)' == '' ">
     <ProjectReference Include="..\..\src\NLog.Targets.AtomicFile\NLog.Targets.AtomicFile.csproj" />
   </ItemGroup>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/NLog.Targets.AtomicFile.Tests/NLog.Targets.AtomicFile.Tests.csproj`
around lines 32 - 40, The ProjectReference and the added Reference can both be
active when TargetFramework != 'net8.0' and TestTargetFramework is set, causing
duplicate assembly references; update the ProjectReference ItemGroup (the
ProjectReference element) to include a complementary Condition so it is skipped
when the Reference is used (i.e., when TestTargetFramework != '' and
TargetFramework != 'net8.0'), ensuring only one of Reference or ProjectReference
is included; modify the Condition on the ProjectReference ItemGroup to be the
inverse of the new Reference's Condition so duplicates are avoided.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
src/NLog.Targets.AtomicFile/Internal/NativeMethods.cs (1)

82-84: Optional: Enum values are GENERIC_, not FILE_GENERIC_.

The values 0x80000000, 0x40000000, 0x20000000 are GENERIC_READ, GENERIC_WRITE, GENERIC_EXECUTE respectively. The actual FILE_GENERIC_* constants are different (e.g., FILE_GENERIC_READ = 0x00120089).

Both work with CreateFile since generic access rights get mapped to specific rights, so this is functionally correct. Consider renaming to GENERIC_READ, etc., for accuracy, or adding comments clarifying this.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/NLog.Targets.AtomicFile/Internal/NativeMethods.cs` around lines 82 - 84,
The enum in NativeMethods.cs defines FILE_GENERIC_READ/WRITE/EXECUTE with values
that are actually the GENERIC_* access masks; update the identifiers to
GENERIC_READ, GENERIC_WRITE, GENERIC_EXECUTE (or add a clarifying comment) so
the names accurately reflect the constants used; locate the enum entries
FILE_GENERIC_READ, FILE_GENERIC_WRITE, FILE_GENERIC_EXECUTE in the NativeMethods
class and either rename them to GENERIC_READ/GENERIC_WRITE/GENERIC_EXECUTE
(adjust any references) or keep the names but add a comment stating these are
generic access masks mapped by CreateFile.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@src/NLog.Targets.AtomicFile/Internal/NativeMethods.cs`:
- Around line 82-84: The enum in NativeMethods.cs defines
FILE_GENERIC_READ/WRITE/EXECUTE with values that are actually the GENERIC_*
access masks; update the identifiers to GENERIC_READ, GENERIC_WRITE,
GENERIC_EXECUTE (or add a clarifying comment) so the names accurately reflect
the constants used; locate the enum entries FILE_GENERIC_READ,
FILE_GENERIC_WRITE, FILE_GENERIC_EXECUTE in the NativeMethods class and either
rename them to GENERIC_READ/GENERIC_WRITE/GENERIC_EXECUTE (adjust any
references) or keep the names but add a comment stating these are generic access
masks mapped by CreateFile.

ℹ️ Review info

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between aaacc86 and f676cac.

📒 Files selected for processing (2)
  • src/NLog.Targets.AtomicFile/AtomicFileTarget.cs
  • src/NLog.Targets.AtomicFile/Internal/NativeMethods.cs

@snakefoot snakefoot force-pushed the michailf-netstandard2.0_for_AtomicFile-tests branch 2 times, most recently from b51271a to 833a70d Compare February 28, 2026 10:16
@sonarqubecloud
Copy link

@sonarqubecloud
Copy link

@snakefoot
Copy link
Contributor Author

Superseeded by #6100

@snakefoot snakefoot closed this Feb 28, 2026
@snakefoot snakefoot linked an issue Feb 28, 2026 that may be closed by this pull request
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add .NET Standard 2.0 support to AtomicFile package

2 participants