Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Convert fallback path of GetCommandLineArgs to managed #70608

Merged
merged 10 commits into from
Jun 20, 2022
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Diagnostics;
using System.IO;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
Expand Down Expand Up @@ -184,10 +185,7 @@ public static unsafe long WorkingSet
private static unsafe string[] GetCommandLineArgsNative()
{
char* lpCmdLine = Interop.Kernel32.GetCommandLine();
jkotas marked this conversation as resolved.
Show resolved Hide resolved
if (lpCmdLine == null)
{
ThrowHelper.ThrowOutOfMemoryException();
}
Debug.Assert(lpCmdLine != null);

int numArgs = 0;
char** argvW = Interop.Shell32.CommandLineToArgv(lpCmdLine, &numArgs);
Expand All @@ -196,14 +194,19 @@ private static unsafe string[] GetCommandLineArgsNative()
ThrowHelper.ThrowOutOfMemoryException();
}

string[] result = new string[numArgs];
for (int i = 0; i < result.Length; i++)
try
{
string[] result = new string[numArgs];
for (int i = 0; i < result.Length; i++)
{
result[i] = new string(*(argvW + i));
}
return result;
}
finally
{
result[i] = new string(*(argvW + i));
Interop.Kernel32.LocalFree((nint)argvW);
jkotas marked this conversation as resolved.
Show resolved Hide resolved
}

Interop.Kernel32.LocalFree((nint)argvW);
return result;
}
}
}
34 changes: 34 additions & 0 deletions src/tests/Interop/CommandLine/CommandLineTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
using System;
jkotas marked this conversation as resolved.
Show resolved Hide resolved
using System.IO;
using System.Reflection;
using TestLibrary;
using Xunit;

namespace CommandLineTest
{
class Program
Copy link
Member

Choose a reason for hiding this comment

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

I would prefer this to follow the [Fact] pattern we are moving towards.

Removing the namespace, making the class public, renaming to CommandLineTest and then renaming int Main() as follows should be all that is needed. Also, removing the return as this will all be generated for you.

[Fact]
public void Validate_CommandLineArgsSet()

Copy link
Member

Choose a reason for hiding this comment

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

This test does not really need to be under src/tests. It can be under libraries (and using remote executor). It is a test for Environment.GetCommandLineArgs() library API after all.

Copy link
Member Author

Choose a reason for hiding this comment

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

I considered that under xunit we may have much less control of the actual command line of the process. Can xunit be run in-process by Visual Studio?

Copy link
Member Author

Choose a reason for hiding this comment

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

@AaronRobinsonMSFT What's your thought about the expected value here? I'm afraid the only thing we can test is not empty, which looks not enough.

Copy link
Member

@AaronRobinsonMSFT AaronRobinsonMSFT Jun 18, 2022

Choose a reason for hiding this comment

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

@AaronRobinsonMSFT What's your thought about the expected value here? I'm afraid the only thing we can test is not empty, which looks not enough.

In general, I agree. However, the libraries teams have traditionally made an effort to call every API even if it is only to verify it always throws or returns nothing. It does make for some odd tests, but is helpful in ensuring the API works enough to invoke. More to the point, I think it is already done https://github.com/dotnet/runtime/blob/main/src/libraries/System.Runtime.Extensions/tests/System/Environment.GetCommandLineArgs.cs.

Once I update CoreShim to be more useful we can create the more robust suite I think we all want.

{
int Main()
Copy link
Member

Choose a reason for hiding this comment

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

This needs to be marked static, but I'd prefer making it a Fact.

{
// Currently native command line is only implemented on CoreCLR Windows
if (!TestLibrary.Utilities.IsMonoRuntime && !TestLibrary.Utilities.IsNativeAot)
{
// Clear the command line args set for managed entry point
var field = typeof(Environment).GetField("s_commandLineArgs", BindingFlags.Static | BindingFlags.NonPublic);
Assert.NotNull(field);
field.SetValue(null, null);

string[] args = Environment.GetCommandLineArgs();
if (OperatingSystem.IsWindows())
{
// The command line should be "corerun assemblyname.dll" for coreclr test
Assert.Equal(2, args.Length);
Assert.Equal("corerun", Path.GetFileNameWithoutExtension(args[0]));
Assert.Equal(typeof(Program).Assembly.GetName().Name, Path.GetFileNameWithoutExtension(args[1]));
}
}

return 100;
}
}
}
12 changes: 12 additions & 0 deletions src/tests/Interop/CommandLine/CommandLineTest.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<ItemGroup>
<Compile Include="CommandLineTest.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="$(TestSourceDir)Common/CoreCLRTestLibrary/CoreCLRTestLibrary.csproj" />
</ItemGroup>
</Project>