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

Implement dotnet test help option #41142

Merged
merged 57 commits into from
Jun 17, 2024
Merged
Show file tree
Hide file tree
Changes from 31 commits
Commits
Show all changes
57 commits
Select commit Hold shift + click to select a range
8f7b82c
implement dotnet test help option
mariam-abdulla May 24, 2024
19393e0
delete DebuggerUtility.cs
mariam-abdulla May 24, 2024
8d61d89
refactor
mariam-abdulla May 27, 2024
20f5b1a
refactor
mariam-abdulla May 28, 2024
9f6f951
refactor TestingPlatformCommand.Help.cs
mariam-abdulla May 28, 2024
1c93e3b
move Microsoft.NET.Testing.Tasks project into tasks folder
mariam-abdulla May 29, 2024
1b8db4b
Remove IDisposable from IClient and IServer
mariam-abdulla May 29, 2024
1c38cc1
Add comment to ObjectFieldIds
mariam-abdulla May 29, 2024
7c04a05
Revert "Add comment to ObjectFieldIds"
mariam-abdulla May 29, 2024
b5e8c18
Add comment to ObjectFieldIds
mariam-abdulla May 29, 2024
23bb5ac
remove pragma warning from VoidResponseSerializer
mariam-abdulla May 29, 2024
f6b3cd6
Expose ErrorEvent in TestApplication.cs
mariam-abdulla May 29, 2024
b1a64fd
rename env var to DOTNET_CLI_TESTINGPLATFORM_ENABLE
mariam-abdulla May 29, 2024
698c7e8
revert dotnet/Program.cs change
mariam-abdulla May 29, 2024
ab171f5
reverse logic for containsNoBuild in TestingPlatformCommand
mariam-abdulla May 29, 2024
7c14c93
remove MSBuildExeName from TestApplication.cs
mariam-abdulla May 29, 2024
6fdd73d
Update namespaces
mariam-abdulla May 29, 2024
b7070a0
move TestingPlatformCommand constructor to top
mariam-abdulla May 29, 2024
f4cb57e
refactor code
mariam-abdulla Jun 3, 2024
2cf9cb2
refactor code & add constants
mariam-abdulla Jun 3, 2024
46f7200
remove DebuggerUtility
mariam-abdulla Jun 3, 2024
ca41b0d
remove empty line
mariam-abdulla Jun 4, 2024
097d9ba
Merge branch 'main' into dev/mabdullah/ImplementDotnetTestHelp
mariam-abdulla Jun 4, 2024
4801c6e
Merge branch 'main' into dev/mabdullah/ImplementDotnetTestHelp
mariam-abdulla Jun 4, 2024
dfc7cb7
delete PathDotnetTest.ps1
mariam-abdulla Jun 5, 2024
89b8f76
Merge branch 'dev/mabdullah/ImplementDotnetTestHelp' of https://githu…
mariam-abdulla Jun 5, 2024
81a3e90
rename constant
mariam-abdulla Jun 5, 2024
15c652e
Move GetTestsProject to Microsoft.NET.Build.Tasks
mariam-abdulla Jun 6, 2024
4af142c
Support _GetTestsProject target with multiple target frameworks
mariam-abdulla Jun 7, 2024
1f69b2d
Display dotnet test help options differently
mariam-abdulla Jun 10, 2024
5e4347a
refactor
mariam-abdulla Jun 10, 2024
ff1880c
remove TryGetMSBuildArgs
mariam-abdulla Jun 10, 2024
6c55c56
Update src/Cli/dotnet/commands/dotnet-test/IPC/Models/CommandLineOpti…
mariam-abdulla Jun 10, 2024
208ae67
Update src/Cli/dotnet/commands/dotnet-test/IPC/NamedPipeClient.cs
mariam-abdulla Jun 10, 2024
da9ffba
Update src/Cli/dotnet/commands/dotnet-test/IPC/NamedPipeServer.cs
mariam-abdulla Jun 10, 2024
87074d7
apply comments
mariam-abdulla Jun 10, 2024
2aa8506
Merge branch 'dev/mabdullah/ImplementDotnetTestHelp' of https://githu…
mariam-abdulla Jun 10, 2024
fae7748
Update src/Cli/dotnet/commands/dotnet-test/IPC/NamedPipeServer.cs
mariam-abdulla Jun 10, 2024
9138671
refactor code
mariam-abdulla Jun 10, 2024
a8fba78
Merge branch 'dev/mabdullah/ImplementDotnetTestHelp' of https://githu…
mariam-abdulla Jun 10, 2024
cdc6d94
refactor code
mariam-abdulla Jun 10, 2024
46cb20d
change exception in GetSerializer() in NamedPipeBase
mariam-abdulla Jun 10, 2024
3d37391
clean warnings from IClient.cs and IServer.cs
mariam-abdulla Jun 11, 2024
88e5dc9
Merge branch 'main' into dev/mabdullah/ImplementDotnetTestHelp
mariam-abdulla Jun 11, 2024
d0b41f0
Send Project full path in GetTestsProject task
mariam-abdulla Jun 11, 2024
e96af9c
Merge branch 'dev/mabdullah/ImplementDotnetTestHelp' of https://githu…
mariam-abdulla Jun 11, 2024
9f7d0ab
Remove DebuggerUtility class
mariam-abdulla Jun 11, 2024
b1d85db
Merge branch 'main' into dev/mabdullah/ImplementDotnetTestHelp
mariam-abdulla Jun 11, 2024
de47bb4
Merge branch 'main' into dev/mabdullah/ImplementDotnetTestHelp
mariam-abdulla Jun 11, 2024
2c2501e
Use MSBuildThisFileDirectory instead of MSBuildProjectFullPath in Mic…
mariam-abdulla Jun 12, 2024
e4271e9
Merge branch 'main' into dev/mabdullah/ImplementDotnetTestHelp
mariam-abdulla Jun 12, 2024
07d37fd
Merge branch 'dev/mabdullah/ImplementDotnetTestHelp' of https://githu…
mariam-abdulla Jun 12, 2024
d9c9839
Merge branch 'main' into dev/mabdullah/ImplementDotnetTestHelp
mariam-abdulla Jun 12, 2024
5484b06
Fix replacing MicrosoftNETBuildTasksAssembly
mariam-abdulla Jun 12, 2024
79ed573
Merge branch 'dev/mabdullah/ImplementDotnetTestHelp' of https://githu…
mariam-abdulla Jun 12, 2024
98be087
Merge branch 'main' into dev/mabdullah/ImplementDotnetTestHelp
mariam-abdulla Jun 12, 2024
9f2acd1
Merge branch 'main' into dev/mabdullah/ImplementDotnetTestHelp
mariam-abdulla Jun 17, 2024
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
19 changes: 19 additions & 0 deletions src/Cli/dotnet/commands/dotnet-test/CliConstants.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

namespace Microsoft.DotNet.Cli
{
internal static class CliConstants
{
public const string HelpOptionKey = "--help";
public const string MSBuildOptionKey = "--additional-msbuild-params";
public const string NoBuildOptionKey = "--no-build";
public const string ServerOptionKey = "--server";
public const string DotNetTestPipeOptionKey = "--dotnet-test-pipe";

public const string ServerOptionValue = "dotnettestcli";

public const string MSBuildExeName = "MSBuild.dll";

}
}
17 changes: 17 additions & 0 deletions src/Cli/dotnet/commands/dotnet-test/CustomEventArgs.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using Microsoft.DotNet.Tools.Test;

namespace Microsoft.DotNet.Cli
{
internal class ErrorEventArgs : EventArgs
{
public string ErrorMessage { get; set; }
}

internal class HelpEventArgs : EventArgs
{
public CommandLineOptionMessages CommandLineOptionMessages { get; set; }
}
}
20 changes: 20 additions & 0 deletions src/Cli/dotnet/commands/dotnet-test/IPC/IClient.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

namespace Microsoft.DotNet.Tools.Test;

internal interface IClient : IDisposable
MarcoRossignoli marked this conversation as resolved.
Show resolved Hide resolved
#if NETCOREAPP
#pragma warning disable SA1001 // Commas should be spaced correctly
, IAsyncDisposable
Copy link
Member

Choose a reason for hiding this comment

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

I think you won't have to disable the warnings if you reverse the order of these interfaces, i.e.,
IClient :
#if NETCOREAPP
IAsyncDisposable,
#endif
IDisposable

Copy link
Member

Choose a reason for hiding this comment

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

@mariam-abdulla pls give it a try looks a good idea! thanks

#pragma warning restore SA1001 // Commas should be spaced correctly
#endif
{
bool IsConnected { get; }

Task ConnectAsync(CancellationToken cancellationToken);

Task<TResponse> RequestReplyAsync<TRequest, TResponse>(TRequest request, CancellationToken cancellationToken)
where TRequest : IRequest
where TResponse : IResponse;
}
9 changes: 9 additions & 0 deletions src/Cli/dotnet/commands/dotnet-test/IPC/INamedPipeIC.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

namespace Microsoft.DotNet.Tools.Test;

internal interface INamedPipeBase
mariam-abdulla marked this conversation as resolved.
Show resolved Hide resolved
{
void RegisterSerializer<T>(INamedPipeSerializer namedPipeSerializer);
mariam-abdulla marked this conversation as resolved.
Show resolved Hide resolved
}
13 changes: 13 additions & 0 deletions src/Cli/dotnet/commands/dotnet-test/IPC/INamedPipeSerializer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

namespace Microsoft.DotNet.Tools.Test;

internal interface INamedPipeSerializer
{
int Id { get; }

void Serialize(object objectToSerialize, Stream stream);

object Deserialize(Stream stream);
}
8 changes: 8 additions & 0 deletions src/Cli/dotnet/commands/dotnet-test/IPC/IRequest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

namespace Microsoft.DotNet.Tools.Test;

internal interface IRequest
{
}
8 changes: 8 additions & 0 deletions src/Cli/dotnet/commands/dotnet-test/IPC/IResponse.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

namespace Microsoft.DotNet.Tools.Test;

internal interface IResponse
{
}
16 changes: 16 additions & 0 deletions src/Cli/dotnet/commands/dotnet-test/IPC/IServer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

namespace Microsoft.DotNet.Tools.Test;

internal interface IServer : INamedPipeBase, IDisposable
#if NETCOREAPP
#pragma warning disable SA1001 // Commas should be spaced correctly
, IAsyncDisposable
#pragma warning restore SA1001 // Commas should be spaced correctly
#endif
{
PipeNameDescription PipeName { get; }

Task WaitConnectionAsync(CancellationToken cancellationToken);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

namespace Microsoft.DotNet.Tools.Test
{
internal sealed record class CommandLineOptionMessage(string Name, string Description, bool IsHidden, bool IsBuiltIn) : IRequest;

internal sealed record class CommandLineOptionMessages(string ModuleName, CommandLineOptionMessage[] CommandLineOptionMessageList) : IRequest;
mariam-abdulla marked this conversation as resolved.
Show resolved Hide resolved
}
6 changes: 6 additions & 0 deletions src/Cli/dotnet/commands/dotnet-test/IPC/Models/Module.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

namespace Microsoft.DotNet.Tools.Test;

internal sealed record class Module(string Name) : IRequest;
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

namespace Microsoft.DotNet.Tools.Test;

internal sealed class VoidResponse : IResponse
{
public static readonly VoidResponse CachedInstance = new();
}
38 changes: 38 additions & 0 deletions src/Cli/dotnet/commands/dotnet-test/IPC/NamedPipeBase.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

#pragma warning disable IDE0240 // Remove redundant nullable directive
#nullable disable
#pragma warning restore IDE0240 // Remove redundant nullable directive

using System.Globalization;

namespace Microsoft.DotNet.Tools.Test;

internal abstract class NamedPipeBase
{
private readonly Dictionary<Type, object> _typeSerializer = [];
private readonly Dictionary<int, object> _idSerializer = [];

public void RegisterSerializer<T>(INamedPipeSerializer namedPipeSerializer)
mariam-abdulla marked this conversation as resolved.
Show resolved Hide resolved
{
_typeSerializer.Add(typeof(T), namedPipeSerializer);
_idSerializer.Add(namedPipeSerializer.Id, namedPipeSerializer);
}

protected INamedPipeSerializer GetSerializer(int id)
=> _idSerializer.TryGetValue(id, out object serializer)
? (INamedPipeSerializer)serializer
: throw new InvalidOperationException(string.Format(
mariam-abdulla marked this conversation as resolved.
Show resolved Hide resolved
CultureInfo.InvariantCulture,
"No serializer registered with id '{0}'",
mariam-abdulla marked this conversation as resolved.
Show resolved Hide resolved
mariam-abdulla marked this conversation as resolved.
Show resolved Hide resolved
id));

protected INamedPipeSerializer GetSerializer(Type type)
=> _typeSerializer.TryGetValue(type, out object serializer)
? (INamedPipeSerializer)serializer
: throw new InvalidOperationException(string.Format(
CultureInfo.InvariantCulture,
"No serializer registered with type '{0}'",
type));
}
210 changes: 210 additions & 0 deletions src/Cli/dotnet/commands/dotnet-test/IPC/NamedPipeClient.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,210 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Buffers;
using System.IO.Pipes;

namespace Microsoft.DotNet.Tools.Test;

internal sealed class NamedPipeClient : NamedPipeBase, IClient
{
private readonly NamedPipeClientStream _namedPipeClientStream;
private readonly SemaphoreSlim _lock = new(1, 1);

private readonly MemoryStream _serializationBuffer = new();
private readonly MemoryStream _messageBuffer = new();
private readonly byte[] _readBuffer = new byte[250000];

private bool _disposed;

public NamedPipeClient(string name)
{
_namedPipeClientStream = new(".", name, PipeDirection.InOut);
PipeName = name;
}

public string PipeName { get; }

public bool IsConnected => _namedPipeClientStream.IsConnected;

public async Task ConnectAsync(CancellationToken cancellationToken)
=> await _namedPipeClientStream.ConnectAsync(cancellationToken);

public async Task<TResponse> RequestReplyAsync<TRequest, TResponse>(TRequest request, CancellationToken cancellationToken)
where TRequest : IRequest
where TResponse : IResponse
{
await _lock.WaitAsync(cancellationToken);
try
{
INamedPipeSerializer requestNamedPipeSerializer = GetSerializer(typeof(TRequest));

// Ask to serialize the body
_serializationBuffer.Position = 0;
requestNamedPipeSerializer.Serialize(request, _serializationBuffer);

// Write the message size
_messageBuffer.Position = 0;

// The length of the message is the size of the message plus one byte to store the serializer id
// Space for the message
int sizeOfTheWholeMessage = (int)_serializationBuffer.Position;

// Space for the serializer id
sizeOfTheWholeMessage += sizeof(int);

// Write the message size
#if NETCOREAPP
mariam-abdulla marked this conversation as resolved.
Show resolved Hide resolved
byte[] bytes = ArrayPool<byte>.Shared.Rent(sizeof(int));
try
{
BitConverter.TryWriteBytes(bytes, sizeOfTheWholeMessage);
await _messageBuffer.WriteAsync(bytes.AsMemory(0, sizeof(int)), cancellationToken);
}
finally
{
ArrayPool<byte>.Shared.Return(bytes);
}
#else
await _messageBuffer.WriteAsync(BitConverter.GetBytes(sizeOfTheWholeMessage), 0, sizeof(int), cancellationToken);
#endif

// Write the serializer id
#if NETCOREAPP
bytes = ArrayPool<byte>.Shared.Rent(sizeof(int));
try
{
BitConverter.TryWriteBytes(bytes, requestNamedPipeSerializer.Id);
await _messageBuffer.WriteAsync(bytes.AsMemory(0, sizeof(int)), cancellationToken);
}
finally
{
ArrayPool<byte>.Shared.Return(bytes);
}
#else
await _messageBuffer.WriteAsync(BitConverter.GetBytes(requestNamedPipeSerializer.Id), 0, sizeof(int), cancellationToken);
#endif

try
{
// Write the message
#if NETCOREAPP
await _messageBuffer.WriteAsync(_serializationBuffer.GetBuffer().AsMemory(0, (int)_serializationBuffer.Position), cancellationToken);
#else
await _messageBuffer.WriteAsync(_serializationBuffer.GetBuffer(), 0, (int)_serializationBuffer.Position, cancellationToken);
#endif
}
finally
{
// Reset the serialization buffer
_serializationBuffer.Position = 0;
}

// Send the message
try
{
#if NETCOREAPP
await _namedPipeClientStream.WriteAsync(_messageBuffer.GetBuffer().AsMemory(0, (int)_messageBuffer.Position), cancellationToken);
#else
await _namedPipeClientStream.WriteAsync(_messageBuffer.GetBuffer(), 0, (int)_messageBuffer.Position, cancellationToken);
#endif
await _namedPipeClientStream.FlushAsync(cancellationToken);
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
_namedPipeClientStream.WaitForPipeDrain();
}
}
finally
{
// Reset the buffers
_messageBuffer.Position = 0;
_serializationBuffer.Position = 0;
}

// Read the response
int currentMessageSize = 0;
int missingBytesToReadOfWholeMessage = 0;
while (true)
{
int missingBytesToReadOfCurrentChunk = 0;
int currentReadIndex = 0;
#if NETCOREAPP
int currentReadBytes = await _namedPipeClientStream.ReadAsync(_readBuffer.AsMemory(currentReadIndex, _readBuffer.Length), cancellationToken);
#else
int currentReadBytes = await _namedPipeClientStream.ReadAsync(_readBuffer, currentReadIndex, _readBuffer.Length, cancellationToken);
#endif
// Reset the current chunk size
missingBytesToReadOfCurrentChunk = currentReadBytes;

// If currentRequestSize is 0, we need to read the message size
if (currentMessageSize == 0)
{
// We need to read the message size, first 4 bytes
currentMessageSize = BitConverter.ToInt32(_readBuffer, 0);
missingBytesToReadOfCurrentChunk = currentReadBytes - sizeof(int);
missingBytesToReadOfWholeMessage = currentMessageSize;
currentReadIndex = sizeof(int);
}

if (missingBytesToReadOfCurrentChunk > 0)
{
// We need to read the rest of the message
#if NETCOREAPP
await _messageBuffer.WriteAsync(_readBuffer.AsMemory(currentReadIndex, missingBytesToReadOfCurrentChunk), cancellationToken);
#else
await _messageBuffer.WriteAsync(_readBuffer, currentReadIndex, missingBytesToReadOfCurrentChunk, cancellationToken);
#endif
missingBytesToReadOfWholeMessage -= missingBytesToReadOfCurrentChunk;
}

// If we have read all the message, we can deserialize it
if (missingBytesToReadOfWholeMessage == 0)
{
// Deserialize the message
_messageBuffer.Position = 0;

// Get the serializer id
int serializerId = BitConverter.ToInt32(_messageBuffer.GetBuffer(), 0);

// Get the serializer
_messageBuffer.Position += sizeof(int); // Skip the serializer id
INamedPipeSerializer responseNamedPipeSerializer = GetSerializer(serializerId);

// Deserialize the message
try
{
return (TResponse)responseNamedPipeSerializer.Deserialize(_messageBuffer);
}
finally
{
// Reset the message buffer
_messageBuffer.Position = 0;
}
}
}
}
finally
{
_lock.Release();
}
}
public void Dispose()
mariam-abdulla marked this conversation as resolved.
Show resolved Hide resolved
{
if (!_disposed)
{
_namedPipeClientStream.Dispose();
_disposed = true;
}
}

#if NETCOREAPP
public async ValueTask DisposeAsync()
{
if (!_disposed)
{
await _namedPipeClientStream.DisposeAsync();
_disposed = true;
}
}
#endif
}
Loading
Loading