From 8316c8117bf4322ad7e8ae1c0e6b63059f860199 Mon Sep 17 00:00:00 2001 From: Charlie Poole Date: Fri, 2 Apr 2021 09:47:45 -0700 Subject: [PATCH 01/24] Rename folder to match namespace --- .../Transports/ITestAgentTransport.cs | 0 .../{Communications => Communication}/Transports/ITransport.cs | 0 .../Transports/Remoting/TestAgentRemotingTransport.cs | 0 .../Transports/ITestAgencyTransport.cs | 0 .../Transports/Remoting/TestAgencyRemotingTransport.cs | 0 .../Transports/Remoting/TestAgentRemotingProxy.cs | 0 6 files changed, 0 insertions(+), 0 deletions(-) rename src/NUnitEngine/nunit.engine.core/{Communications => Communication}/Transports/ITestAgentTransport.cs (100%) rename src/NUnitEngine/nunit.engine.core/{Communications => Communication}/Transports/ITransport.cs (100%) rename src/NUnitEngine/nunit.engine.core/{Communications => Communication}/Transports/Remoting/TestAgentRemotingTransport.cs (100%) rename src/NUnitEngine/nunit.engine/{Communications => Communication}/Transports/ITestAgencyTransport.cs (100%) rename src/NUnitEngine/nunit.engine/{Communications => Communication}/Transports/Remoting/TestAgencyRemotingTransport.cs (100%) rename src/NUnitEngine/nunit.engine/{Communications => Communication}/Transports/Remoting/TestAgentRemotingProxy.cs (100%) diff --git a/src/NUnitEngine/nunit.engine.core/Communications/Transports/ITestAgentTransport.cs b/src/NUnitEngine/nunit.engine.core/Communication/Transports/ITestAgentTransport.cs similarity index 100% rename from src/NUnitEngine/nunit.engine.core/Communications/Transports/ITestAgentTransport.cs rename to src/NUnitEngine/nunit.engine.core/Communication/Transports/ITestAgentTransport.cs diff --git a/src/NUnitEngine/nunit.engine.core/Communications/Transports/ITransport.cs b/src/NUnitEngine/nunit.engine.core/Communication/Transports/ITransport.cs similarity index 100% rename from src/NUnitEngine/nunit.engine.core/Communications/Transports/ITransport.cs rename to src/NUnitEngine/nunit.engine.core/Communication/Transports/ITransport.cs diff --git a/src/NUnitEngine/nunit.engine.core/Communications/Transports/Remoting/TestAgentRemotingTransport.cs b/src/NUnitEngine/nunit.engine.core/Communication/Transports/Remoting/TestAgentRemotingTransport.cs similarity index 100% rename from src/NUnitEngine/nunit.engine.core/Communications/Transports/Remoting/TestAgentRemotingTransport.cs rename to src/NUnitEngine/nunit.engine.core/Communication/Transports/Remoting/TestAgentRemotingTransport.cs diff --git a/src/NUnitEngine/nunit.engine/Communications/Transports/ITestAgencyTransport.cs b/src/NUnitEngine/nunit.engine/Communication/Transports/ITestAgencyTransport.cs similarity index 100% rename from src/NUnitEngine/nunit.engine/Communications/Transports/ITestAgencyTransport.cs rename to src/NUnitEngine/nunit.engine/Communication/Transports/ITestAgencyTransport.cs diff --git a/src/NUnitEngine/nunit.engine/Communications/Transports/Remoting/TestAgencyRemotingTransport.cs b/src/NUnitEngine/nunit.engine/Communication/Transports/Remoting/TestAgencyRemotingTransport.cs similarity index 100% rename from src/NUnitEngine/nunit.engine/Communications/Transports/Remoting/TestAgencyRemotingTransport.cs rename to src/NUnitEngine/nunit.engine/Communication/Transports/Remoting/TestAgencyRemotingTransport.cs diff --git a/src/NUnitEngine/nunit.engine/Communications/Transports/Remoting/TestAgentRemotingProxy.cs b/src/NUnitEngine/nunit.engine/Communication/Transports/Remoting/TestAgentRemotingProxy.cs similarity index 100% rename from src/NUnitEngine/nunit.engine/Communications/Transports/Remoting/TestAgentRemotingProxy.cs rename to src/NUnitEngine/nunit.engine/Communication/Transports/Remoting/TestAgentRemotingProxy.cs From e5d1cf8db0f42ba6692eb74e0b5feb0657a20a62 Mon Sep 17 00:00:00 2001 From: Charlie Poole Date: Fri, 2 Apr 2021 09:54:53 -0700 Subject: [PATCH 02/24] Descriptions of each EngineStatus value --- .../nunit.engine/Services/AgentStatus.cs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/NUnitEngine/nunit.engine/Services/AgentStatus.cs b/src/NUnitEngine/nunit.engine/Services/AgentStatus.cs index c1be9f7fb..8a600d641 100644 --- a/src/NUnitEngine/nunit.engine/Services/AgentStatus.cs +++ b/src/NUnitEngine/nunit.engine/Services/AgentStatus.cs @@ -8,8 +8,23 @@ namespace NUnit.Engine.Services /// public enum AgentStatus { + /// + /// Agent is in the process of starting and we are waiting for it to be ready. + /// This is generally used for process agents launched by the TestAgency, + /// which must call back using the agency's Register method. + /// Starting, + + /// + /// Agent is ready to receive commands. Once in this state, it remains there until + /// it is told to terminate using the Stop method. + /// Ready, + + /// + /// The agent has stopped completely. In the case of a process agent, this means + /// that the process has terminated. + /// Terminated } } From f6b92a347cd2afc029ea9a1e78bf49c2d77b74f6 Mon Sep 17 00:00:00 2001 From: Charlie Poole Date: Fri, 2 Apr 2021 10:26:25 -0700 Subject: [PATCH 03/24] Add TCP to engine core --- .../Agents/RemoteTestAgent.cs | 6 - .../Communication/Messages/CommandMessage.cs | 22 ++ .../Messages/CommandReturnMessage.cs | 19 ++ .../Communication/Messages/ProgressMessage.cs | 19 ++ .../Messages/TestEngineMessage.cs | 13 + .../Protocols/BinarySerializationProtocol.cs | 249 ++++++++++++++++++ .../Protocols/ISerializationProtocol.cs | 27 ++ .../Transports/Tcp/SocketReader.cs | 66 +++++ .../Transports/Tcp/TestAgentTcpTransport.cs | 134 ++++++++++ 9 files changed, 549 insertions(+), 6 deletions(-) create mode 100644 src/NUnitEngine/nunit.engine.core/Communication/Messages/CommandMessage.cs create mode 100644 src/NUnitEngine/nunit.engine.core/Communication/Messages/CommandReturnMessage.cs create mode 100644 src/NUnitEngine/nunit.engine.core/Communication/Messages/ProgressMessage.cs create mode 100644 src/NUnitEngine/nunit.engine.core/Communication/Messages/TestEngineMessage.cs create mode 100644 src/NUnitEngine/nunit.engine.core/Communication/Protocols/BinarySerializationProtocol.cs create mode 100644 src/NUnitEngine/nunit.engine.core/Communication/Protocols/ISerializationProtocol.cs create mode 100644 src/NUnitEngine/nunit.engine.core/Communication/Transports/Tcp/SocketReader.cs create mode 100644 src/NUnitEngine/nunit.engine.core/Communication/Transports/Tcp/TestAgentTcpTransport.cs diff --git a/src/NUnitEngine/nunit.engine.core/Agents/RemoteTestAgent.cs b/src/NUnitEngine/nunit.engine.core/Agents/RemoteTestAgent.cs index 1faf87646..a038d8cc1 100644 --- a/src/NUnitEngine/nunit.engine.core/Agents/RemoteTestAgent.cs +++ b/src/NUnitEngine/nunit.engine.core/Agents/RemoteTestAgent.cs @@ -1,13 +1,8 @@ // Copyright (c) Charlie Poole, Rob Prouse and Contributors. MIT License - see LICENSE.txt -#if NETFRAMEWORK using System; -using System.Runtime.Remoting.Channels; -using System.Runtime.Remoting.Channels.Tcp; -using System.Threading; using NUnit.Common; using NUnit.Engine.Communication.Transports; -using NUnit.Engine.Internal; namespace NUnit.Engine.Agents { @@ -47,4 +42,3 @@ public override ITestEngineRunner CreateRunner(TestPackage package) } } } -#endif \ No newline at end of file diff --git a/src/NUnitEngine/nunit.engine.core/Communication/Messages/CommandMessage.cs b/src/NUnitEngine/nunit.engine.core/Communication/Messages/CommandMessage.cs new file mode 100644 index 000000000..80b5256e8 --- /dev/null +++ b/src/NUnitEngine/nunit.engine.core/Communication/Messages/CommandMessage.cs @@ -0,0 +1,22 @@ +// Copyright (c) Charlie Poole, Rob Prouse and Contributors. MIT License - see LICENSE.txt + +using System; + +namespace NUnit.Engine.Communication.Messages +{ +#if !NETSTANDARD1_6 + [Serializable] +#endif + public class CommandMessage : TestEngineMessage + { + public CommandMessage(string commandName, params object[] arguments) + { + CommandName = commandName; + Arguments = arguments; + } + + public string CommandName { get; } + + public object[] Arguments { get; } + } +} diff --git a/src/NUnitEngine/nunit.engine.core/Communication/Messages/CommandReturnMessage.cs b/src/NUnitEngine/nunit.engine.core/Communication/Messages/CommandReturnMessage.cs new file mode 100644 index 000000000..89198630d --- /dev/null +++ b/src/NUnitEngine/nunit.engine.core/Communication/Messages/CommandReturnMessage.cs @@ -0,0 +1,19 @@ +// Copyright (c) Charlie Poole, Rob Prouse and Contributors. MIT License - see LICENSE.txt + +using System; + +namespace NUnit.Engine.Communication.Messages +{ +#if !NETSTANDARD1_6 + [Serializable] +#endif + public class CommandReturnMessage : TestEngineMessage + { + public CommandReturnMessage(object returnValue) + { + ReturnValue = returnValue; + } + + public object ReturnValue { get; } + } +} diff --git a/src/NUnitEngine/nunit.engine.core/Communication/Messages/ProgressMessage.cs b/src/NUnitEngine/nunit.engine.core/Communication/Messages/ProgressMessage.cs new file mode 100644 index 000000000..23253b1f5 --- /dev/null +++ b/src/NUnitEngine/nunit.engine.core/Communication/Messages/ProgressMessage.cs @@ -0,0 +1,19 @@ +// Copyright (c) Charlie Poole, Rob Prouse and Contributors. MIT License - see LICENSE.txt + +using System; + +namespace NUnit.Engine.Communication.Messages +{ +#if !NETSTANDARD1_6 + [Serializable] +#endif + public class ProgressMessage : TestEngineMessage + { + public ProgressMessage(string report) + { + Report = report; + } + + public string Report { get; } + } +} diff --git a/src/NUnitEngine/nunit.engine.core/Communication/Messages/TestEngineMessage.cs b/src/NUnitEngine/nunit.engine.core/Communication/Messages/TestEngineMessage.cs new file mode 100644 index 000000000..7b720ec65 --- /dev/null +++ b/src/NUnitEngine/nunit.engine.core/Communication/Messages/TestEngineMessage.cs @@ -0,0 +1,13 @@ +// Copyright (c) Charlie Poole, Rob Prouse and Contributors. MIT License - see LICENSE.txt + +using System; + +namespace NUnit.Engine.Communication.Messages +{ +#if !NETSTANDARD1_6 + [Serializable] +#endif + public abstract class TestEngineMessage + { + } +} diff --git a/src/NUnitEngine/nunit.engine.core/Communication/Protocols/BinarySerializationProtocol.cs b/src/NUnitEngine/nunit.engine.core/Communication/Protocols/BinarySerializationProtocol.cs new file mode 100644 index 000000000..76505ea0a --- /dev/null +++ b/src/NUnitEngine/nunit.engine.core/Communication/Protocols/BinarySerializationProtocol.cs @@ -0,0 +1,249 @@ +// Copyright (c) Charlie Poole, Rob Prouse and Contributors. MIT License - see LICENSE.txt + +#if !NETSTANDARD1_6 +using System; +using System.Collections.Generic; +using System.IO; +using System.Runtime.Serialization; +using System.Runtime.Serialization.Formatters.Binary; +using NUnit.Engine.Communication.Messages; + +namespace NUnit.Engine.Communication.Protocols +{ + /// + /// BinarySerializationProtocol serializes messages in the following format: + /// + /// [Message Length (4 bytes)][Serialized Message Content] + /// + /// The message content is in binary form as produced by the .NET BinaryFormatter. + /// Each message of length n is serialized as n + 4 bytes. + /// + public class BinarySerializationProtocol : ISerializationProtocol + { + /// + /// Maximum length of a message. + /// + private const int MAX_MESSAGE_LENGTH = 128 * 1024 * 1024; //128 Megabytes. + + /// + /// This MemoryStream object is used to collect receiving bytes to build messages. + /// + private MemoryStream _receiveMemoryStream = new MemoryStream(); + + /// + /// Serializes a message to a byte array to send to remote application. + /// + /// Message to be serialized + /// A byte[] containing the message, serialized as per the protocol. + public byte[] Encode(object message) + { + //Serialize the message to a byte array + var serializedMessage = SerializeMessage(message); + + //Check for message length + var messageLength = serializedMessage.Length; + if (messageLength > MAX_MESSAGE_LENGTH) + { + throw new Exception("Message is too big (" + messageLength + " bytes). Max allowed length is " + MAX_MESSAGE_LENGTH + " bytes."); + } + + //Create a byte array including the length of the message (4 bytes) and serialized message content + var bytes = new byte[messageLength + 4]; + WriteInt32(bytes, 0, messageLength); + Array.Copy(serializedMessage, 0, bytes, 4, messageLength); + + //Return serialized message by this protocol + return bytes; + } + + /// + /// Accept an array of bytes and deserialize the messages that are found. + /// A single call may provide no messages, part of a message, a single + /// message or multiple messages. Unused bytes are saved for use as + /// further calls are made. + /// + /// The byte array to be deserialized + /// An enumeration of TestEngineMessages + public IEnumerable Decode(byte[] receivedBytes) + { + // Write all received bytes to the _receiveMemoryStream, which may + // already contain part of a message from a previous call. + _receiveMemoryStream.Write(receivedBytes, 0, receivedBytes.Length); + + //Create a list to collect messages + var messages = new List(); + + //Read all available messages and add to messages collection + while (ReadSingleMessage(messages)) { } + + return messages; + } + + public void Reset() + { + if (_receiveMemoryStream.Length > 0) + { + _receiveMemoryStream = new MemoryStream(); + } + } + + /// + /// Serializes a message to a byte array to send to remote application. + /// + /// Message to be serialized + /// + /// A byte[] containing the message itself, without a prefixed + /// length, serialized according to the protocol. + /// + internal byte[] SerializeMessage(object message) + { + using (var memoryStream = new MemoryStream()) + { + new BinaryFormatter().Serialize(memoryStream, message); + return memoryStream.ToArray(); + } + } + + /// + /// Deserializes a message contained in a byte array. + /// + /// A byte[] containing just the message, without a length prefix + /// An object representing the message encoded in the byte array + internal object DeserializeMessage(byte[] bytes) + { + using (var memoryStream = new MemoryStream(bytes)) + { + try + { + return new BinaryFormatter().Deserialize(memoryStream); + } + catch (Exception exception) + { + Reset(); // reset the received memory stream before the exception is rethrown - otherwise the same erroneous message is received again and again + throw new SerializationException("error while deserializing message", exception); + } + } + } + + /// + /// This method tries to read a single message and add to the messages collection. + /// + /// Messages collection to collect messages + /// + /// Returns a boolean value indicates that if there is a need to re-call this method. + /// + /// Throws CommunicationException if message is bigger than maximum allowed message length. + private bool ReadSingleMessage(ICollection messages) + { + //Go to the begining of the stream + _receiveMemoryStream.Position = 0; + + //If stream has less than 4 bytes, that means we can not even read length of the message + //So, return false to wait more for bytes from remorte application. + if (_receiveMemoryStream.Length < 4) + { + return false; + } + + //Read length of the message + var messageLength = ReadInt32(_receiveMemoryStream); + if (messageLength > MAX_MESSAGE_LENGTH) + { + throw new Exception("Message is too big (" + messageLength + " bytes). Max allowed length is " + MAX_MESSAGE_LENGTH + " bytes."); + } + + //If message is zero-length (It must not be but good approach to check it) + if (messageLength == 0) + { + //if no more bytes, return immediately + if (_receiveMemoryStream.Length == 4) + { + _receiveMemoryStream = new MemoryStream(); //Clear the stream + return false; + } + + //Create a new memory stream from current except first 4-bytes. + var bytes = _receiveMemoryStream.ToArray(); + _receiveMemoryStream = new MemoryStream(); + _receiveMemoryStream.Write(bytes, 4, bytes.Length - 4); + return true; + } + + //If all bytes of the message is not received yet, return to wait more bytes + if (_receiveMemoryStream.Length < (4 + messageLength)) + { + _receiveMemoryStream.Position = _receiveMemoryStream.Length; + return false; + } + + //Read bytes of serialized message and deserialize it + var serializedMessageBytes = ReadByteArray(_receiveMemoryStream, messageLength); + + messages.Add((TestEngineMessage)DeserializeMessage(serializedMessageBytes)); + + //Read remaining bytes to an array + var remainingBytes = ReadByteArray(_receiveMemoryStream, (int)(_receiveMemoryStream.Length - (4 + messageLength))); + + //Re-create the receive memory stream and write remaining bytes + _receiveMemoryStream = new MemoryStream(); + _receiveMemoryStream.Write(remainingBytes, 0, remainingBytes.Length); + + //Return true to re-call this method to try to read next message + return (remainingBytes.Length > 4); + } + + /// + /// Writes a int value to a byte array from a starting index. + /// + /// Byte array to write int value + /// Start index of byte array to write + /// An integer value to write + private static void WriteInt32(byte[] buffer, int startIndex, int number) + { + buffer[startIndex] = (byte)((number >> 24) & 0xFF); + buffer[startIndex + 1] = (byte)((number >> 16) & 0xFF); + buffer[startIndex + 2] = (byte)((number >> 8) & 0xFF); + buffer[startIndex + 3] = (byte)((number) & 0xFF); + } + + /// + /// Deserializes and returns a serialized integer. + /// + /// Deserialized integer + private static int ReadInt32(Stream stream) + { + var buffer = ReadByteArray(stream, 4); + return ((buffer[0] << 24) | + (buffer[1] << 16) | + (buffer[2] << 8) | + (buffer[3]) + ); + } + + /// + /// Reads a byte array with specified length. + /// + /// Stream to read from + /// Length of the byte array to read + /// Read byte array + /// Throws EndOfStreamException if can not read from stream. + private static byte[] ReadByteArray(Stream stream, int length) + { + var buffer = new byte[length]; + var totalRead = 0; + while (totalRead < length) + { + var read = stream.Read(buffer, totalRead, length - totalRead); + if (read <= 0) + { + throw new EndOfStreamException("Can not read from stream! Input stream is closed."); + } + + totalRead += read; + } + + return buffer; + } + } +} +#endif diff --git a/src/NUnitEngine/nunit.engine.core/Communication/Protocols/ISerializationProtocol.cs b/src/NUnitEngine/nunit.engine.core/Communication/Protocols/ISerializationProtocol.cs new file mode 100644 index 000000000..89b0511dc --- /dev/null +++ b/src/NUnitEngine/nunit.engine.core/Communication/Protocols/ISerializationProtocol.cs @@ -0,0 +1,27 @@ +// Copyright (c) Charlie Poole, Rob Prouse and Contributors. MIT License - see LICENSE.txt + +using System.Collections.Generic; +using NUnit.Engine.Communication.Messages; + +namespace NUnit.Engine.Communication.Protocols +{ + public interface ISerializationProtocol + { + /// + /// Serializes a message to a byte array to send to remote application. + /// + /// Message to be serialized + /// A byte[] containing the message, serialized as per the protocol. + byte[] Encode(object message); + + /// + /// Accept an array of bytes and deserialize the messages that are found. + /// A single call may provide no messages, part of a message, a single + /// message or multiple messages. Unused bytes are saved for use as + /// further calls are made. + /// + /// The byte array to be deserialized + /// An enumeration of TestEngineMessages + IEnumerable Decode(byte[] receivedBytes); + } +} diff --git a/src/NUnitEngine/nunit.engine.core/Communication/Transports/Tcp/SocketReader.cs b/src/NUnitEngine/nunit.engine.core/Communication/Transports/Tcp/SocketReader.cs new file mode 100644 index 000000000..a34230429 --- /dev/null +++ b/src/NUnitEngine/nunit.engine.core/Communication/Transports/Tcp/SocketReader.cs @@ -0,0 +1,66 @@ +// Copyright (c) Charlie Poole, Rob Prouse and Contributors. MIT License - see LICENSE.txt + +using System; +using System.Collections.Generic; +using System.Net.Sockets; +using NUnit.Engine.Communication.Messages; +using NUnit.Engine.Communication.Protocols; + +namespace NUnit.Engine.Communication.Transports.Tcp +{ + public class SocketReader + { + private const int BUFFER_SIZE = 1024; + + private Socket _socket; + private ISerializationProtocol _wireProtocol; + + private Queue _msgQueue; + private byte[] _buffer; + + public SocketReader(Socket socket, ISerializationProtocol protocol) + { + _socket = socket; + _wireProtocol = protocol; + + _msgQueue = new Queue(); + _buffer = new byte[BUFFER_SIZE]; + } + + /// + /// Get the next TestEngineMessage to arrive + /// + /// The message + public TestEngineMessage GetNextMessage() + { + while (_msgQueue.Count == 0) + { + int n = _socket.Receive(_buffer); + var bytes = new byte[n]; + Array.Copy(_buffer, 0, bytes, 0, n); + foreach (var message in _wireProtocol.Decode(bytes)) + _msgQueue.Enqueue(message); + } + + return _msgQueue.Dequeue(); + } + + /// + /// Get the next message to arrive, which must be of the + /// specified message type. + /// + /// The expected message type + /// A message of type TMessage + /// A message of a different type was received + public TMessage GetNextMessage() where TMessage : TestEngineMessage + { + var receivedMessage = GetNextMessage(); + var expectedMessage = receivedMessage as TMessage; + + if (expectedMessage == null) + throw new InvalidOperationException($"Expected a {typeof(TMessage)} but received a {receivedMessage.GetType()}"); + + return expectedMessage; + } + } +} diff --git a/src/NUnitEngine/nunit.engine.core/Communication/Transports/Tcp/TestAgentTcpTransport.cs b/src/NUnitEngine/nunit.engine.core/Communication/Transports/Tcp/TestAgentTcpTransport.cs new file mode 100644 index 000000000..8eed0738e --- /dev/null +++ b/src/NUnitEngine/nunit.engine.core/Communication/Transports/Tcp/TestAgentTcpTransport.cs @@ -0,0 +1,134 @@ +// Copyright (c) Charlie Poole, Rob Prouse and Contributors. MIT License - see LICENSE.txt + +#if !NETSTANDARD1_6 +using System.Net; +using System.Net.Sockets; +using System.Threading; +using NUnit.Common; +using NUnit.Engine.Agents; +using NUnit.Engine.Internal; +using NUnit.Engine.Communication.Messages; +using NUnit.Engine.Communication.Protocols; + +namespace NUnit.Engine.Communication.Transports.Tcp +{ + public class TestAgentTcpTransport : ITestAgentTransport, ITestEventListener + { + private static readonly Logger log = InternalTrace.GetLogger(typeof(TestAgentTcpTransport)); + + private string _agencyUrl; + private Socket _clientSocket; + private ITestEngineRunner _runner; + + public TestAgentTcpTransport(RemoteTestAgent agent, string serverUrl) + { + Guard.ArgumentNotNull(agent, nameof(agent)); + Agent = agent; + + Guard.ArgumentNotNullOrEmpty(serverUrl, nameof(serverUrl)); + _agencyUrl = serverUrl; + + var parts = serverUrl.Split(new char[] { ':' }); + Guard.ArgumentValid(parts.Length == 2, "Invalid server address specified. Must be a valid endpoint including the port number", nameof(serverUrl)); + ServerEndPoint = new IPEndPoint(IPAddress.Parse(parts[0]), int.Parse(parts[1])); + } + + public TestAgent Agent { get; } + + public IPEndPoint ServerEndPoint { get; } + + public bool Start() + { + log.Info("Connecting to TestAgency at {0}", _agencyUrl); + + // Connect to the server + _clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); + _clientSocket.Connect(ServerEndPoint); + + // Immediately upon connection send the agent Id as a raw byte array + _clientSocket.Send(Agent.Id.ToByteArray()); + + // Start the loop that reads and executes commands + Thread commandLoop = new Thread(CommandLoop); + commandLoop.Start(); + + return true; + } + + public void Stop() + { + Agent.StopSignal.Set(); + } + + public ITestEngineRunner CreateRunner(TestPackage package) + { + return Agent.CreateRunner(package); + } + + private void CommandLoop() + { + bool keepRunning = true; + var socketReader = new SocketReader(_clientSocket, new BinarySerializationProtocol()); + + while (keepRunning) + { + var command = socketReader.GetNextMessage(); + + switch (command.CommandName) + { + case "CreateRunner": + var package = (TestPackage)command.Arguments[0]; + _runner = CreateRunner(package); + break; + case "Load": + SendResult(_runner.Load()); + break; + case "Reload": + SendResult(_runner.Reload()); + break; + case "Unload": + _runner.Unload(); + break; + case "Explore": + var filter = (TestFilter)command.Arguments[0]; + SendResult(_runner.Explore(filter)); + break; + case "CountTestCases": + filter = (TestFilter)command.Arguments[0]; + SendResult(_runner.CountTestCases(filter)); + break; + case "Run": + filter = (TestFilter)command.Arguments[0]; + SendResult(_runner.Run(this, filter)); + break; + + case "RunAsync": + filter = (TestFilter)command.Arguments[0]; + _runner.RunAsync(this, filter); + break; + + case "Stop": + keepRunning = false; + break; + } + } + + Stop(); + } + + private void SendResult(object result) + { + var resultMessage = new CommandReturnMessage(result); + var bytes = new BinarySerializationProtocol().Encode(resultMessage); + _clientSocket.Send(bytes); + } + + public void OnTestEvent(string report) + { + var progressMessage = new ProgressMessage(report); + var bytes = new BinarySerializationProtocol().Encode(progressMessage); + _clientSocket.Send(bytes); + } + } +} +#endif From 741d46366e35abb5f8b4216f2496316975114e8a Mon Sep 17 00:00:00 2001 From: Charlie Poole Date: Fri, 2 Apr 2021 11:32:28 -0700 Subject: [PATCH 04/24] Add TCP Transport to nunit.engine --- .../Communication/Transports/Tcp/TcpServer.cs | 112 +++++++++++++++ .../Transports/Tcp/TestAgencyTcpTransport.cs | 80 +++++++++++ .../Transports/Tcp/TestAgentTcpProxy.cs | 132 ++++++++++++++++++ 3 files changed, 324 insertions(+) create mode 100644 src/NUnitEngine/nunit.engine/Communication/Transports/Tcp/TcpServer.cs create mode 100644 src/NUnitEngine/nunit.engine/Communication/Transports/Tcp/TestAgencyTcpTransport.cs create mode 100644 src/NUnitEngine/nunit.engine/Communication/Transports/Tcp/TestAgentTcpProxy.cs diff --git a/src/NUnitEngine/nunit.engine/Communication/Transports/Tcp/TcpServer.cs b/src/NUnitEngine/nunit.engine/Communication/Transports/Tcp/TcpServer.cs new file mode 100644 index 000000000..1b8ae21bc --- /dev/null +++ b/src/NUnitEngine/nunit.engine/Communication/Transports/Tcp/TcpServer.cs @@ -0,0 +1,112 @@ +// Copyright (c) Charlie Poole, Rob Prouse and Contributors. MIT License - see LICENSE.txt + +using System; +using System.Net; +using System.Net.Sockets; +using System.Threading; +using NUnit.Engine.Internal; + +namespace NUnit.Engine.Communication.Transports.Tcp +{ + public class TcpServer + { + private static readonly Logger log = InternalTrace.GetLogger(typeof(TestAgentTcpTransport)); + + private const int GUID_BUFFER_SIZE = 16; + + TcpListener _listenerSocket; + Thread _listenerThread; + volatile bool _running; + + public delegate void ConnectionEventHandler(Socket clientSocket, Guid id); + + public event ConnectionEventHandler ClientConnected; + + public TcpServer(int port = 0) + { + _listenerSocket = new TcpListener(IPAddress.Loopback, port); + } + + public IPEndPoint EndPoint => (IPEndPoint)_listenerSocket.LocalEndpoint; + + public void Start() + { + _listenerSocket.Start(); + _running = true; + + _listenerThread = new Thread(WaitForClientConnections); + _listenerThread.Start(); + } + + public void Stop() + { + try + { + _running = false; + _listenerSocket.Stop(); + } + catch (Exception exception) + { + log.Error($"Failed to stop listener socket: {exception}"); + } + } + + private void WaitForClientConnections() + { + while (_running) + { + try + { + var clientSocket = _listenerSocket.AcceptSocket(); + if (clientSocket.Connected) + { + // Upon connection, remote agent must immediately send its Id as identification. + // Guid is sent as a raw byte array, without any preceding length specified. + byte[] bytes = ReadBytes(clientSocket, GUID_BUFFER_SIZE); + + Guid id = new Guid(bytes); + ClientConnected?.Invoke(clientSocket, id); + } + } + catch + { + // Two possibilities: + // 1. We were trying to stop the socket + // 2. The connection was dropped due to some external event + // In either case, we stop the socket and wait a while + _listenerSocket.Stop(); + + // If we were trying to stop, that's all + if (!_running) + return; + + // Otherwise, wait and try to restart it. An exception here is simply logged + Thread.Sleep(500); + try + { + _listenerSocket.Start(); + } + catch (Exception exception) + { + log.Error($"Unable to restart listener socket: {exception}"); + } + } + } + } + + private static byte[] ReadBytes(Socket clientSocket, int numBytes) + { + var buf = new byte[numBytes]; + var bytes = new byte[numBytes]; + int count = 0; + while (count < numBytes) + { + int n = clientSocket.Receive(buf); + Array.Copy(buf, 0, bytes, count, n); + count += n; + } + + return bytes; + } + } +} diff --git a/src/NUnitEngine/nunit.engine/Communication/Transports/Tcp/TestAgencyTcpTransport.cs b/src/NUnitEngine/nunit.engine/Communication/Transports/Tcp/TestAgencyTcpTransport.cs new file mode 100644 index 000000000..31fb06758 --- /dev/null +++ b/src/NUnitEngine/nunit.engine/Communication/Transports/Tcp/TestAgencyTcpTransport.cs @@ -0,0 +1,80 @@ +// Copyright (c) Charlie Poole, Rob Prouse and Contributors. MIT License - see LICENSE.txt + +using System; +using System.Net; +using System.Threading; +using NUnit.Common; +using NUnit.Engine.Internal; + +namespace NUnit.Engine.Communication.Transports.Tcp +{ + /// + /// Summary description for TestAgencyTcpTransport. + /// + public class TestAgencyTcpTransport : ITestAgencyTransport, ITestAgency, IDisposable + { + private static readonly Logger log = InternalTrace.GetLogger(typeof(TestAgencyTcpTransport)); + + private ITestAgency _agency; + private TcpServer _server; + + private object _theLock = new object(); + + public TestAgencyTcpTransport(ITestAgency agency, int port=0) + { + Guard.ArgumentNotNull(agency, nameof(agency)); + Guard.ArgumentValid(port >= IPEndPoint.MinPort && port <= IPEndPoint.MaxPort, + $"Port number {port} is invalid. Must be a positive integer less than or equal to {IPEndPoint.MaxPort}", nameof(port)); + + _agency = agency; + _server = new TcpServer(port); + } + + public string ServerUrl => _server.EndPoint.ToString(); + + public bool Start() + { + _server.ClientConnected += (socket, id) => _agency.Register(new TestAgentTcpProxy(socket, id)); + _server.Start(); + + return true; + } + + public void Stop() + { + _server.Stop(); + } + + public void Register(ITestAgent agent) + { + _agency.Register(agent); + } + + public void WaitForStop() + { + lock( _theLock ) + { + Monitor.Wait( _theLock ); + } + } + + public void Dispose() + { + GC.SuppressFinalize(this); + Dispose(true); + } + + private bool _disposed = false; + + protected virtual void Dispose(bool disposing) + { + if (!_disposed) + { + if (disposing) + Stop(); + + _disposed = true; + } + } + } +} diff --git a/src/NUnitEngine/nunit.engine/Communication/Transports/Tcp/TestAgentTcpProxy.cs b/src/NUnitEngine/nunit.engine/Communication/Transports/Tcp/TestAgentTcpProxy.cs new file mode 100644 index 000000000..c4c12c286 --- /dev/null +++ b/src/NUnitEngine/nunit.engine/Communication/Transports/Tcp/TestAgentTcpProxy.cs @@ -0,0 +1,132 @@ +// Copyright (c) Charlie Poole, Rob Prouse and Contributors. MIT License - see LICENSE.txt + +using System; +using System.Net.Sockets; +using NUnit.Engine.Communication.Messages; +using NUnit.Engine.Communication.Protocols; + +namespace NUnit.Engine.Communication.Transports.Tcp +{ + /// + /// TestAgentTcpProxy wraps a RemoteTestAgent so that certain + /// of its properties may be accessed directly. + /// + internal class TestAgentTcpProxy : ITestAgent, ITestEngineRunner + { + private Socket _socket; + private BinarySerializationProtocol _wireProtocol = new BinarySerializationProtocol(); + + public TestAgentTcpProxy(Socket socket, Guid id) + { + _socket = socket; + Id = id; + } + + public Guid Id { get; } + + public ITestEngineRunner CreateRunner(TestPackage package) + { + SendCommandMessage("CreateRunner", package); + + // Agent also functions as the runner + return this; + } + + public bool Start() + { + SendCommandMessage("Start"); + return CommandResult(); + } + + public void Stop() + { + SendCommandMessage("Stop"); + } + + public TestEngineResult Load() + { + SendCommandMessage("Load"); + return CommandResult(); + } + + public void Unload() + { + SendCommandMessage("Unload"); + } + + public TestEngineResult Reload() + { + SendCommandMessage("Reload"); + return CommandResult(); + } + + public int CountTestCases(TestFilter filter) + { + SendCommandMessage("CountTestCases", filter); + return CommandResult(); + } + + public TestEngineResult Run(ITestEventListener listener, TestFilter filter) + { + SendCommandMessage("Run", filter); + + return TestRunResult(listener); + } + + public AsyncTestEngineResult RunAsync(ITestEventListener listener, TestFilter filter) + { + SendCommandMessage("RunAsync", filter); + // TODO: Should we get the async result from the agent or just use our own? + return CommandResult(); + //return new AsyncTestEngineResult(); + } + + public void StopRun(bool force) + { + SendCommandMessage("StopRun", force); + } + + public TestEngineResult Explore(TestFilter filter) + { + SendCommandMessage("Explore", filter); + return CommandResult(); + } + + public void Dispose() + { + throw new NotImplementedException(); + } + + private void SendCommandMessage(string command, params object[] arguments) + { + _socket.Send(_wireProtocol.Encode(new CommandMessage(command, arguments))); + } + + private T CommandResult() + { + return (T)new SocketReader(_socket, _wireProtocol).GetNextMessage().ReturnValue; + } + + // Return the result of a test run as a TestEngineResult. ProgressMessages + // preceding the final CommandReturnMessage are handled as well. + private TestEngineResult TestRunResult(ITestEventListener listener) + { + var rdr = new SocketReader(_socket, _wireProtocol); + while (true) + { + var receivedMessage = rdr.GetNextMessage(); + var receivedType = receivedMessage.GetType(); + + var returnMessage = receivedMessage as CommandReturnMessage; + if (returnMessage != null) + return (TestEngineResult)returnMessage.ReturnValue; + + var progressMessage = receivedMessage as ProgressMessage; + if (progressMessage == null) + throw new InvalidOperationException($"Expected either a ProgressMessage or a CommandReturnMessage but received a {receivedType}"); + + listener.OnTestEvent(progressMessage.Report); + } + } + } +} From 08777aa9c1cb055fcf2c98acc0946d5d5f2de478 Mon Sep 17 00:00:00 2001 From: Charlie Poole Date: Fri, 2 Apr 2021 11:54:46 -0700 Subject: [PATCH 05/24] Connect TestAgency to both transports --- src/NUnitEngine/nunit.engine/Services/TestAgency.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/NUnitEngine/nunit.engine/Services/TestAgency.cs b/src/NUnitEngine/nunit.engine/Services/TestAgency.cs index 19a7f65bf..0c50bb0b5 100644 --- a/src/NUnitEngine/nunit.engine/Services/TestAgency.cs +++ b/src/NUnitEngine/nunit.engine/Services/TestAgency.cs @@ -7,7 +7,7 @@ using NUnit.Common; using NUnit.Engine.Internal; using NUnit.Engine.Communication.Transports.Remoting; -//using NUnit.Engine.Communication.Transports.Tcp; +using NUnit.Engine.Communication.Transports.Tcp; namespace NUnit.Engine.Services { @@ -32,17 +32,17 @@ public partial class TestAgency : ITestAgency, IService // Transports used for various target runtimes private TestAgencyRemotingTransport _remotingTransport; // .NET Framework - //private TestAgencyTcpTransport _tcpTransport; // .NET Standard 2.0 + private TestAgencyTcpTransport _tcpTransport; // .NET Standard 2.0 internal virtual string RemotingUrl => _remotingTransport.ServerUrl; - //internal virtual string TcpEndPoint => _tcpTransport.ServerUrl; + internal virtual string TcpEndPoint => _tcpTransport.ServerUrl; public TestAgency() : this( "TestAgency", 0 ) { } public TestAgency( string uri, int port ) { _remotingTransport = new TestAgencyRemotingTransport(this, uri, port); - //_tcpTransport = new TestAgencyTcpTransport(this, port); + _tcpTransport = new TestAgencyTcpTransport(this, port); } public void Register(ITestAgent agent) @@ -164,7 +164,7 @@ public void StopService() try { _remotingTransport.Stop(); - //_tcpTransport.Stop(); + _tcpTransport.Stop(); } finally { @@ -181,7 +181,7 @@ public void StartService() try { _remotingTransport.Start(); - //_tcpTransport.Start(); + _tcpTransport.Start(); Status = ServiceStatus.Started; } catch From f94d3607869c72e0d54e236e77c213318106cea1 Mon Sep 17 00:00:00 2001 From: Charlie Poole Date: Fri, 2 Apr 2021 18:23:10 -0700 Subject: [PATCH 06/24] Build agent for .NET Core 3.1 --- src/NUnitEngine/nunit-agent/Program.cs | 2 + .../nunit-agent/nunit-agent-x86.csproj | 2 +- .../nunit-agent/nunit-agent.csproj | 2 +- .../nunit.engine.core/RuntimeFramework.cs | 50 +++++++++++-------- .../nunit.engine.core.csproj | 1 + 5 files changed, 34 insertions(+), 23 deletions(-) diff --git a/src/NUnitEngine/nunit-agent/Program.cs b/src/NUnitEngine/nunit-agent/Program.cs index 2b635a93d..f2fb48fee 100644 --- a/src/NUnitEngine/nunit-agent/Program.cs +++ b/src/NUnitEngine/nunit-agent/Program.cs @@ -70,9 +70,11 @@ public static void Main(string[] args) log.Info("Agent process {0} starting", pid); +#if NETFRAMEWORK log.Info("Running under version {0}, {1}", Environment.Version, RuntimeFramework.CurrentFramework.DisplayName); +#endif // Create CoreEngine var engine = new CoreEngine diff --git a/src/NUnitEngine/nunit-agent/nunit-agent-x86.csproj b/src/NUnitEngine/nunit-agent/nunit-agent-x86.csproj index 2a3003887..3e63c083e 100644 --- a/src/NUnitEngine/nunit-agent/nunit-agent-x86.csproj +++ b/src/NUnitEngine/nunit-agent/nunit-agent-x86.csproj @@ -2,7 +2,7 @@ Exe nunit.agent - net20;net40 + net20;net40;netcoreapp3.1 app.manifest ..\..\..\nunit.ico x86 diff --git a/src/NUnitEngine/nunit-agent/nunit-agent.csproj b/src/NUnitEngine/nunit-agent/nunit-agent.csproj index 9829f8d80..5e7aa9b0e 100644 --- a/src/NUnitEngine/nunit-agent/nunit-agent.csproj +++ b/src/NUnitEngine/nunit-agent/nunit-agent.csproj @@ -2,7 +2,7 @@ Exe nunit.agent - net20;net40 + net20;net40;netcoreapp3.1 app.manifest ..\..\..\nunit.ico false diff --git a/src/NUnitEngine/nunit.engine.core/RuntimeFramework.cs b/src/NUnitEngine/nunit.engine.core/RuntimeFramework.cs index 26b44ef67..6c89f2a32 100644 --- a/src/NUnitEngine/nunit.engine.core/RuntimeFramework.cs +++ b/src/NUnitEngine/nunit.engine.core/RuntimeFramework.cs @@ -1,13 +1,14 @@ // Copyright (c) Charlie Poole, Rob Prouse and Contributors. MIT License - see LICENSE.txt -#if NETFRAMEWORK using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Reflection; using Microsoft.Win32; +#if NETFRAMEWORK using NUnit.Engine.Internal.RuntimeFrameworks; +#endif namespace NUnit.Engine { @@ -45,7 +46,9 @@ public sealed class RuntimeFramework : IRuntimeFramework public static readonly Version DefaultVersion = new Version(0, 0); private static RuntimeFramework _currentFramework; +#if NETFRAMEWORK private static List _availableFrameworks; +#endif private static readonly string DEFAULT_WINDOWS_MONO_DIR = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), "Mono"); @@ -241,6 +244,7 @@ public static RuntimeFramework CurrentFramework } } +#if NETFRAMEWORK /// /// Gets an array of all available frameworks /// @@ -254,6 +258,7 @@ public static RuntimeFramework[] AvailableFrameworks return _availableFrameworks.ToArray(); } } +#endif /// /// The version of Mono in use or null if no Mono runtime @@ -305,6 +310,7 @@ public string Id } } +#if NETFRAMEWORK /// /// Returns true if the current RuntimeFramework is available. /// In the current implementation, only Mono and Microsoft .NET @@ -322,6 +328,7 @@ public bool IsAvailable return false; } } +#endif /// /// The type of this runtime framework @@ -494,6 +501,25 @@ private static bool VersionsMatch(Version v1, Version v2) (v1.Revision < 0 || v2.Revision < 0 || v1.Revision == v2.Revision); } + private static string GetMonoPrefixFromAssembly(Assembly assembly) + { + string prefix = assembly.Location; + + // In all normal mono installations, there will be sufficient + // levels to complete the four iterations. But just in case + // files have been copied to some non-standard place, we check. + for (int i = 0; i < 4; i++) + { + string dir = Path.GetDirectoryName(prefix); + if (string.IsNullOrEmpty(dir)) break; + + prefix = dir; + } + + return prefix; + } + +#if NETFRAMEWORK private static void FindAvailableFrameworks() { _availableFrameworks = new List(); @@ -525,24 +551,6 @@ private static void UseCurrentMonoFramework() _availableFrameworks.Add(RuntimeFramework.CurrentFramework); } - private static string GetMonoPrefixFromAssembly(Assembly assembly) - { - string prefix = assembly.Location; - - // In all normal mono installations, there will be sufficient - // levels to complete the four iterations. But just in case - // files have been copied to some non-standard place, we check. - for (int i = 0; i < 4; i++) - { - string dir = Path.GetDirectoryName(prefix); - if (string.IsNullOrEmpty(dir)) break; - - prefix = dir; - } - - return prefix; - } - private static void FindBestMonoFrameworkOnWindows() { // First, look for recent frameworks that use the Software\Mono Key @@ -641,6 +649,6 @@ private static void AddMonoFramework(Version frameworkVersion, string profile) _availableFrameworks.Add(framework); } - } +#endif + } } -#endif \ No newline at end of file diff --git a/src/NUnitEngine/nunit.engine.core/nunit.engine.core.csproj b/src/NUnitEngine/nunit.engine.core/nunit.engine.core.csproj index 2f691979e..2b22f1fb1 100644 --- a/src/NUnitEngine/nunit.engine.core/nunit.engine.core.csproj +++ b/src/NUnitEngine/nunit.engine.core/nunit.engine.core.csproj @@ -10,6 +10,7 @@ + From 6b108de0dbbe5733300b757d12eb14f4befd36af Mon Sep 17 00:00:00 2001 From: Charlie Poole Date: Sun, 4 Apr 2021 19:06:51 -0700 Subject: [PATCH 07/24] Modify process runner to use .NET Core 3.1 agent --- build.cake | 11 + nuget/engine/nunit.engine.nuspec | 8 + .../Compatibility/FrameworkName.cs | 232 ++++++++++++++++++ .../nunit.engine.core/RuntimeFramework.cs | 84 ++++++- .../nunit.engine.core/RuntimeType.cs | 4 +- .../Services/DriverService.cs | 4 + .../nunit.engine/Services/AgentProcess.cs | 24 +- .../Services/RuntimeFrameworkService.cs | 49 +++- .../nunit.engine/Services/TestAgency.cs | 4 +- 9 files changed, 401 insertions(+), 19 deletions(-) create mode 100644 src/NUnitEngine/nunit.engine.core/Compatibility/FrameworkName.cs diff --git a/build.cake b/build.cake index c518dbe7c..7ab267fb7 100644 --- a/build.cake +++ b/build.cake @@ -457,6 +457,7 @@ Task("BuildChocolateyPackages") new ChocolateyNuSpecContent { Source = CHOCO_DIR + "nunit.choco.addins", Target = "tools" }, new ChocolateyNuSpecContent { Source = CHOCO_DIR + "nunit.agent.addins", Target = "tools/agents/net20" }, new ChocolateyNuSpecContent { Source = CHOCO_DIR + "nunit.agent.addins", Target = "tools/agents/net40" }, + new ChocolateyNuSpecContent { Source = CHOCO_DIR + "nunit.agent.addins", Target = "tools/agents/netcoreapp3.1" }, new ChocolateyNuSpecContent { Source = CURRENT_IMG_DIR + "bin/agents/net20/nunit-agent.exe", Target="tools/agents/net20" }, new ChocolateyNuSpecContent { Source = CURRENT_IMG_DIR + "bin/agents/net20/nunit-agent.exe.config", Target="tools/agents/net20" }, new ChocolateyNuSpecContent { Source = CHOCO_DIR + "nunit-agent.exe.ignore", Target="tools/agents/net20" }, @@ -477,6 +478,16 @@ Task("BuildChocolateyPackages") new ChocolateyNuSpecContent { Source = CURRENT_IMG_DIR + "bin/agents/net40/nunit.engine.api.xml", Target="tools/agents/net40" }, new ChocolateyNuSpecContent { Source = CURRENT_IMG_DIR + "bin/agents/net40/nunit.engine.core.dll", Target="tools/agents/net40" }, new ChocolateyNuSpecContent { Source = CURRENT_IMG_DIR + "bin/agents/net40/testcentric.engine.metadata.dll", Target="tools/agents/net40" }, + new ChocolateyNuSpecContent { Source = CURRENT_IMG_DIR + "bin/agents/netcoreapp3.1/nunit-agent.exe", Target="tools/agents/netcoreapp3.1" }, + //new ChocolateyNuSpecContent { Source = CURRENT_IMG_DIR + "bin/agents/netcoreapp3.1/nunit-agent.exe.config", Target="tools/agents/netcoreapp3.1" }, + new ChocolateyNuSpecContent { Source = CHOCO_DIR + "nunit-agent.exe.ignore", Target="tools/agents/netcoreapp3.1" }, + new ChocolateyNuSpecContent { Source = CURRENT_IMG_DIR + "bin/agents/netcoreapp3.1/nunit-agent-x86.exe", Target="tools/agents/netcoreapp3.1" }, + // new ChocolateyNuSpecContent { Source = CURRENT_IMG_DIR + "bin/agents/netcoreapp3.1/nunit-agent-x86.exe.config", Target="tools/agents/netcoreapp3.1" }, + new ChocolateyNuSpecContent { Source = CHOCO_DIR + "nunit-agent-x86.exe.ignore", Target="tools/agents/netcoreapp3.1" }, + new ChocolateyNuSpecContent { Source = CURRENT_IMG_DIR + "bin/agents/netcoreapp3.1/nunit.engine.api.dll", Target="tools/agents/netcoreapp3.1" }, + new ChocolateyNuSpecContent { Source = CURRENT_IMG_DIR + "bin/agents/netcoreapp3.1/nunit.engine.api.xml", Target="tools/agents/netcoreapp3.1" }, + new ChocolateyNuSpecContent { Source = CURRENT_IMG_DIR + "bin/agents/netcoreapp3.1/nunit.engine.core.dll", Target="tools/agents/netcoreapp3.1" }, + new ChocolateyNuSpecContent { Source = CURRENT_IMG_DIR + "bin/agents/netcoreapp3.1/testcentric.engine.metadata.dll", Target="tools/agents/netcoreapp3.1" }, new ChocolateyNuSpecContent { Source = CURRENT_IMG_NET20_BIN_DIR + "nunit3-console.exe", Target="tools" }, new ChocolateyNuSpecContent { Source = CURRENT_IMG_NET20_BIN_DIR + "nunit3-console.exe.config", Target="tools" }, new ChocolateyNuSpecContent { Source = CURRENT_IMG_NET20_BIN_DIR + "nunit.engine.api.dll", Target="tools" }, diff --git a/nuget/engine/nunit.engine.nuspec b/nuget/engine/nunit.engine.nuspec index f7778ff94..cfe1f1473 100644 --- a/nuget/engine/nunit.engine.nuspec +++ b/nuget/engine/nunit.engine.nuspec @@ -60,9 +60,17 @@ + + + + + + + + diff --git a/src/NUnitEngine/nunit.engine.core/Compatibility/FrameworkName.cs b/src/NUnitEngine/nunit.engine.core/Compatibility/FrameworkName.cs new file mode 100644 index 000000000..ecd2238b3 --- /dev/null +++ b/src/NUnitEngine/nunit.engine.core/Compatibility/FrameworkName.cs @@ -0,0 +1,232 @@ +// Copyright (c) Charlie Poole, Rob Prouse and Contributors. MIT License - see LICENSE.txt + +#if NET20 || NET35 +using System; +using System.Diagnostics; +using NUnit.Common; + +// NOTE: Since the .NET 4.5 engine refers to this assembly, we can't define +// FrameworkName in the System.Runtime.Versioning namespace. +namespace NUnit.Engine.Compatibility +{ + /// + /// Compatible implementation of FrameworkName, based on the corefx implementation + /// + public sealed class FrameworkName : IEquatable + { + private const string FRAMEWORK_NAME_INVALID = "Invalid FrameworkName"; + private const string FRAMEWORK_NAME_VERSION_REQUIRED = "FramweworkName must include Version"; + private const string FRAMEWORK_NAME_VERSION_INVALID = "The specified Version is invalid"; + private const string FRAMEWORK_NAME_COMPONENT_COUNT = "FrameworkName must specify either two or three components"; + + private readonly string _identifier; + private readonly Version _version = null; + private readonly string _profile; + private string _fullName; + + private const char COMPONENT_SEPARATOR = ','; + private const char KEY_VALUE_SEPARATOR = '='; + private const char VERSION_PREFIX = 'v'; + private const string VERSION_KEY = "Version"; + private const string PROFILE_KEY = "Profile"; + + private static readonly char[] COMPONENT_SPLIT_SEPARATOR = { COMPONENT_SEPARATOR }; + + public string Identifier + { + get + { + Debug.Assert(_identifier != null); + return _identifier; + } + } + + public Version Version + { + get + { + Debug.Assert(_version != null); + return _version; + } + } + + public string Profile + { + get + { + Debug.Assert(_profile != null); + return _profile; + } + } + + public string FullName + { + get + { + if (_fullName == null) + { + if (string.IsNullOrEmpty(Profile)) + { + _fullName = + Identifier + + COMPONENT_SEPARATOR + VERSION_KEY + KEY_VALUE_SEPARATOR + VERSION_PREFIX + + Version.ToString(); + } + else + { + _fullName = + Identifier + + COMPONENT_SEPARATOR + VERSION_KEY + KEY_VALUE_SEPARATOR + VERSION_PREFIX + + Version.ToString() + + COMPONENT_SEPARATOR + PROFILE_KEY + KEY_VALUE_SEPARATOR + + Profile; + } + } + + Debug.Assert(_fullName != null); + return _fullName; + } + } + + public override bool Equals(object obj) + { + return Equals(obj as FrameworkName); + } + + public bool Equals(FrameworkName other) + { + if (other is null) + { + return false; + } + + return Identifier == other.Identifier && + Version == other.Version && + Profile == other.Profile; + } + + public override int GetHashCode() + { + return Identifier.GetHashCode() ^ Version.GetHashCode() ^ Profile.GetHashCode(); + } + + public override string ToString() + { + return FullName; + } + + public FrameworkName(string identifier, Version version) + : this(identifier, version, null) + { + } + + public FrameworkName(string identifier, Version version, string profile=null) + { + Guard.ArgumentNotNull(identifier, nameof(identifier)); + Guard.ArgumentNotNull(version, nameof(version)); + + identifier = identifier.Trim(); + Guard.ArgumentNotNullOrEmpty(identifier, nameof(identifier)); + + _identifier = identifier; + _version = version; + _profile = (profile == null) ? string.Empty : profile.Trim(); + } + + // Parses strings in the following format: ", Version=[v|V], Profile=" + // - The identifier and version is required, profile is optional + // - Only three components are allowed. + // - The version string must be in the System.Version format; an optional "v" or "V" prefix is allowed + public FrameworkName(string frameworkName) + { + Guard.ArgumentNotNullOrEmpty(frameworkName, nameof(frameworkName)); + + string[] components = frameworkName.Split(COMPONENT_SPLIT_SEPARATOR); + + // Identifier and Version are required, Profile is optional. + Guard.ArgumentValid(components.Length == 2 || components.Length == 3, + FRAMEWORK_NAME_COMPONENT_COUNT, nameof(frameworkName)); + + // + // 1) Parse the "Identifier", which must come first. Trim any whitespace + // + _identifier = components[0].Trim(); + + Guard.ArgumentValid(_identifier.Length > 0, FRAMEWORK_NAME_INVALID, nameof(frameworkName)); + + bool versionFound = false; + _profile = string.Empty; + + // + // The required "Version" and optional "Profile" component can be in any order + // + for (int i = 1; i < components.Length; i++) + { + // Get the key/value pair separated by '=' + string component = components[i]; + int separatorIndex = component.IndexOf(KEY_VALUE_SEPARATOR); + + Guard.ArgumentValid(separatorIndex >= 0 && separatorIndex == component.LastIndexOf(KEY_VALUE_SEPARATOR), + FRAMEWORK_NAME_INVALID, nameof(frameworkName)); + + // Get the key and value, trimming any whitespace + string key = component.Substring(0, separatorIndex).Trim(); + string value = component.Substring(separatorIndex + 1).Trim(); + + // + // 2) Parse the required "Version" key value + // + if (key.Equals(VERSION_KEY, StringComparison.OrdinalIgnoreCase)) + { + versionFound = true; + + // Allow the version to include a 'v' or 'V' prefix... + if (value.Length > 0 && (value[0] == VERSION_PREFIX || value[0] == 'V')) + value = value.Substring(1); + + try + { + _version = new Version(value); + } + catch (Exception e) + { + throw new ArgumentException(FRAMEWORK_NAME_VERSION_INVALID, nameof(frameworkName), e); + } + } + // + // 3) Parse the optional "Profile" key value + // + else if (key.Equals(PROFILE_KEY, StringComparison.OrdinalIgnoreCase)) + { + if (value.Length > 0) + { + _profile = value.ToString(); + } + } + else + { + throw new ArgumentException(FRAMEWORK_NAME_INVALID, nameof(frameworkName)); + } + } + + if (!versionFound) + throw new ArgumentException(FRAMEWORK_NAME_VERSION_REQUIRED, nameof(frameworkName)); + } + + public static bool operator ==(FrameworkName left, FrameworkName right) + { + if (left is null) + { + return right is null; + } + + return left.Equals(right); + } + + public static bool operator !=(FrameworkName left, FrameworkName right) + { + return !(left == right); + } + } +} +#endif diff --git a/src/NUnitEngine/nunit.engine.core/RuntimeFramework.cs b/src/NUnitEngine/nunit.engine.core/RuntimeFramework.cs index 6c89f2a32..8c60fc8fb 100644 --- a/src/NUnitEngine/nunit.engine.core/RuntimeFramework.cs +++ b/src/NUnitEngine/nunit.engine.core/RuntimeFramework.cs @@ -138,6 +138,18 @@ private Version GetClrVersionForFramework(Version frameworkVersion) return new Version(4, 0, 30319); } break; + case RuntimeType.NetCore: + switch (frameworkVersion.Major) + { + case 1: + case 2: + return new Version(4, 0, 30319); + case 3: + return new Version(3, 1, 10); + case 5: + return new Version(5, 0, 1); + } + break; } throw new ArgumentException("Unknown framework version " + frameworkVersion.ToString(), "version"); @@ -488,6 +500,8 @@ private static string GetRuntimeDisplayName(RuntimeType runtime) { case RuntimeType.Net: return ".NET"; + case RuntimeType.NetCore: + return ".NETCore"; default: return runtime.ToString(); } @@ -528,6 +542,7 @@ private static void FindAvailableFrameworks() _availableFrameworks.AddRange(DotNetFrameworkLocator.FindDotNetFrameworks()); FindDefaultMonoFramework(); + FindDotNetCoreFrameworks(); } private static void FindDefaultMonoFramework() @@ -649,6 +664,73 @@ private static void AddMonoFramework(Version frameworkVersion, string profile) _availableFrameworks.Add(framework); } -#endif + + private static void FindDotNetCoreFrameworks() + { + const string WINDOWS_INSTALL_DIR = "C:\\Program Files\\dotnet\\"; + const string LINUX_INSTALL_DIR = "/usr/shared/dotnet/"; + string INSTALL_DIR = Path.DirectorySeparatorChar == '\\' + ? WINDOWS_INSTALL_DIR + : LINUX_INSTALL_DIR; + + if (!Directory.Exists(INSTALL_DIR)) + return; + if (!File.Exists(Path.Combine(INSTALL_DIR, "dotnet.exe"))) + return; + + string runtimeDir = Path.Combine(INSTALL_DIR, "shared\\Microsoft.NETCore.App"); + if (!Directory.Exists(runtimeDir)) + return; + + var dirList = new DirectoryInfo(runtimeDir).GetDirectories(); + var dirNames = new List(); + foreach (var dir in dirList) + dirNames.Add(dir.Name); + var runtimes = GetNetCoreRuntimesFromDirectoryNames(dirNames); + + _availableFrameworks.AddRange(runtimes); } + + // Internal for testing + internal static IList GetNetCoreRuntimesFromDirectoryNames(IEnumerable dirNames) + { + const string VERSION_CHARS = ".0123456789"; + var runtimes = new List(); + + foreach (string dirName in dirNames) + { + int len = 0; + foreach (char c in dirName) + { + if (VERSION_CHARS.IndexOf(c) >= 0) + len++; + else + break; + } + + if (len == 0) + continue; + + Version fullVersion = null; + try + { + fullVersion = new Version(dirName.Substring(0, len)); + } + catch + { + continue; + } + + var newVersion = new Version(fullVersion.Major, fullVersion.Minor); + int count = runtimes.Count; + if (count > 0 && runtimes[count - 1].FrameworkVersion == newVersion) + continue; + + runtimes.Add(new RuntimeFramework(RuntimeType.NetCore, newVersion)); + } + + return runtimes; + } +#endif + } } diff --git a/src/NUnitEngine/nunit.engine.core/RuntimeType.cs b/src/NUnitEngine/nunit.engine.core/RuntimeType.cs index c7a91d04d..0594b6641 100644 --- a/src/NUnitEngine/nunit.engine.core/RuntimeType.cs +++ b/src/NUnitEngine/nunit.engine.core/RuntimeType.cs @@ -13,6 +13,8 @@ public enum RuntimeType /// Microsoft .NET Framework Net, /// Mono - Mono + Mono, + /// .NET Core + NetCore } } diff --git a/src/NUnitEngine/nunit.engine.core/Services/DriverService.cs b/src/NUnitEngine/nunit.engine.core/Services/DriverService.cs index 52391262a..c1a0ac140 100644 --- a/src/NUnitEngine/nunit.engine.core/Services/DriverService.cs +++ b/src/NUnitEngine/nunit.engine.core/Services/DriverService.cs @@ -18,6 +18,8 @@ namespace NUnit.Engine.Services /// public class DriverService : Service, IDriverService { + static ILogger log = InternalTrace.GetLogger("DriverService"); + readonly IList _factories = new List(); /// @@ -67,6 +69,8 @@ public IFrameworkDriver GetDriver(AppDomain domain, string assemblyPath, string foreach (var factory in _factories) { + log.Debug($"Trying {factory.GetType().Name}"); + foreach (var reference in references) { if (factory.IsSupportedTestFramework(reference)) diff --git a/src/NUnitEngine/nunit.engine/Services/AgentProcess.cs b/src/NUnitEngine/nunit.engine/Services/AgentProcess.cs index 62e43339d..98081e566 100644 --- a/src/NUnitEngine/nunit.engine/Services/AgentProcess.cs +++ b/src/NUnitEngine/nunit.engine/Services/AgentProcess.cs @@ -17,6 +17,7 @@ public AgentProcess(TestAgency agency, TestPackage package, Guid agentId) { // Get target runtime string runtimeSetting = package.GetSetting(EnginePackageSettings.TargetRuntimeFramework, ""); + log.Debug($"TargetRuntime = {runtimeSetting}"); TargetRuntime = RuntimeFramework.Parse(runtimeSetting); // Access other package settings @@ -26,7 +27,8 @@ public AgentProcess(TestAgency agency, TestPackage package, Guid agentId) bool loadUserProfile = package.GetSetting(EnginePackageSettings.LoadUserProfile, false); string workDirectory = package.GetSetting(EnginePackageSettings.WorkDirectory, string.Empty); - AgentArgs = new StringBuilder($"{agentId} {agency.RemotingUrl} --pid={Process.GetCurrentProcess().Id}"); + string agencyUrl = TargetRuntime.Runtime == RuntimeType.NetCore ? agency.TcpEndPoint : agency.RemotingUrl; + AgentArgs = new StringBuilder($"{agentId} {agencyUrl} --pid={Process.GetCurrentProcess().Id}"); // Set options that need to be in effect before the package // is loaded by using the command line. @@ -74,12 +76,12 @@ public AgentProcess(TestAgency agency, TestPackage package, Guid agentId) public static string GetTestAgentExePath(RuntimeFramework targetRuntime, bool requires32Bit) { + log.Debug($"GetTestAgentExePath({targetRuntime}, {requires32Bit})"); string engineDir = NUnitConfiguration.EngineDirectory; if (engineDir == null) return null; // If running out of a package "agents" is a subdirectory string agentsDir = Path.Combine(engineDir, "agents"); - log.Debug($"Checking for agents at {agentsDir}"); if (!Directory.Exists(agentsDir)) { @@ -88,15 +90,29 @@ public static string GetTestAgentExePath(RuntimeFramework targetRuntime, bool re // bit of a kluge, but it's necessary unless we change the binary // output directory to match the distribution structure. agentsDir = Path.Combine(Path.GetDirectoryName(engineDir), "agents"); - log.Debug($"Directory not found! Using {agentsDir}"); + log.Debug("Assuming test run in project output directory"); } - string runtimeDir = targetRuntime.FrameworkVersion.Major >= 4 ? "net40" : "net20"; + log.Debug($"Checking for agents at {agentsDir}"); string agentName = requires32Bit ? "nunit-agent-x86.exe" : "nunit-agent.exe"; + string runtimeDir; + switch (targetRuntime.Runtime) + { + case RuntimeType.Net: + case RuntimeType.Mono: + runtimeDir = targetRuntime.FrameworkVersion.Major >= 4 ? "net40" : "net20"; + break; + case RuntimeType.NetCore: + runtimeDir = "netcoreapp3.1"; + break; + default: + return null; + } + return Path.Combine(Path.Combine(agentsDir, runtimeDir), agentName); } } diff --git a/src/NUnitEngine/nunit.engine/Services/RuntimeFrameworkService.cs b/src/NUnitEngine/nunit.engine/Services/RuntimeFrameworkService.cs index 0683aa24b..58b4d09a3 100644 --- a/src/NUnitEngine/nunit.engine/Services/RuntimeFrameworkService.cs +++ b/src/NUnitEngine/nunit.engine/Services/RuntimeFrameworkService.cs @@ -7,6 +7,9 @@ using Mono.Cecil; using NUnit.Common; using NUnit.Engine.Internal; +#if NET20 +using FrameworkName = NUnit.Engine.Compatibility.FrameworkName; +#endif namespace NUnit.Engine.Services { @@ -89,7 +92,7 @@ public string SelectRuntimeFramework(TestPackage package) return targetFramework.ToString(); } - private static RuntimeFramework SelectRuntimeFrameworkInner(TestPackage package) + private RuntimeFramework SelectRuntimeFrameworkInner(TestPackage package) { foreach (var subPackage in package.SubPackages) { @@ -109,21 +112,45 @@ private static RuntimeFramework SelectRuntimeFrameworkInner(TestPackage package) throw new NUnitEngineException("Invalid or unknown framework requested: " + frameworkSetting); log.Debug($"Requested framework for {package.Name} is {requestedFramework}"); + + if (!IsAvailable(frameworkSetting)) + throw new NUnitEngineException("Requested framework is not available: " + frameworkSetting); + + package.Settings[EnginePackageSettings.TargetRuntimeFramework] = frameworkSetting; + + return requestedFramework; } - else + + log.Debug($"No specific framework requested for {package.Name}"); + + string imageTargetFrameworkNameSetting = package.GetSetting(InternalEnginePackageSettings.ImageTargetFrameworkName, ""); + RuntimeType targetRuntime; + Version targetVersion; + + if (string.IsNullOrEmpty(imageTargetFrameworkNameSetting)) { - requestedFramework = new RuntimeFramework(RuntimeType.Any, RuntimeFramework.DefaultVersion); - log.Debug($"No specific framework requested for {package.Name}"); + // Assume .NET Framework + targetRuntime = RuntimeType.Net; + targetVersion = package.GetSetting(InternalEnginePackageSettings.ImageRuntimeVersion, new Version(2, 0)); } + else + { + FrameworkName frameworkName = new FrameworkName(imageTargetFrameworkNameSetting); - RuntimeType targetRuntime = requestedFramework.Runtime; - Version targetVersion = requestedFramework.FrameworkVersion; - - if (targetRuntime == RuntimeType.Any) - targetRuntime = currentFramework.Runtime; + switch (frameworkName.Identifier) + { + case ".NETFramework": + targetRuntime = RuntimeType.Net; + break; + case ".NETCoreApp": + targetRuntime = RuntimeType.NetCore; + break; + default: + throw new NUnitEngineException("Unsupported Target Framework: " + imageTargetFrameworkNameSetting); + } - if (targetVersion == RuntimeFramework.DefaultVersion) - targetVersion = package.GetSetting(InternalEnginePackageSettings.ImageRuntimeVersion, currentFramework.FrameworkVersion); + targetVersion = frameworkName.Version; + } if (!new RuntimeFramework(targetRuntime, targetVersion).IsAvailable) { diff --git a/src/NUnitEngine/nunit.engine/Services/TestAgency.cs b/src/NUnitEngine/nunit.engine/Services/TestAgency.cs index 0c50bb0b5..7231be280 100644 --- a/src/NUnitEngine/nunit.engine/Services/TestAgency.cs +++ b/src/NUnitEngine/nunit.engine/Services/TestAgency.cs @@ -181,8 +181,8 @@ public void StartService() try { _remotingTransport.Start(); - _tcpTransport.Start(); - Status = ServiceStatus.Started; + _tcpTransport.Start(); + Status = ServiceStatus.Started; } catch { From 381f1ae07cc8b9c0672e34ca9f9d876376153d72 Mon Sep 17 00:00:00 2001 From: Charlie Poole Date: Sun, 11 Apr 2021 10:26:44 -0700 Subject: [PATCH 08/24] Update package content --- build.cake | 25 ++++++++++++------- nuget/engine/nunit.engine.nuspec | 16 +++++++++--- nuget/runners/nunit.console-runner.nuspec | 12 +++++++++ .../nunit-agent/nunit-agent-x86.csproj | 4 ++- .../nunit-agent/nunit-agent.csproj | 4 ++- 5 files changed, 46 insertions(+), 15 deletions(-) diff --git a/build.cake b/build.cake index 7ab267fb7..492cd1e2f 100644 --- a/build.cake +++ b/build.cake @@ -51,6 +51,8 @@ var ZIP_IMG = PROJECT_DIR + "zip-image/"; var SOLUTION_FILE = PROJECT_DIR + "NUnitConsole.sln"; var ENGINE_CSPROJ = PROJECT_DIR + "src/NUnitEngine/nunit.engine/nunit.engine.csproj"; +var AGENT_CSPROJ = PROJECT_DIR + "src/NUnitEngine/nunit-agent/nunit-agent.csproj"; + // Agent X86 is in the same directory, so we don't need to do anything with it var ENGINE_API_CSPROJ = PROJECT_DIR + "src/NUnitEngine/nunit.engine.api/nunit.engine.api.csproj"; var ENGINE_TESTS_CSPROJ = PROJECT_DIR + "src/NUnitEngine/nunit.engine.tests/nunit.engine.tests.csproj"; var CONSOLE_CSPROJ = PROJECT_DIR + "src/NUnitConsole/nunit3-console/nunit3-console.csproj"; @@ -209,12 +211,17 @@ Task("Build") .WithProperty("TargetFramework", framework) .WithProperty("PublishDir", BIN_DIR + framework)); - foreach (var framework in new [] { "netstandard2.0" }) - MSBuild(ENGINE_API_CSPROJ, CreateMSBuildSettings("Publish") - .WithProperty("TargetFramework", framework) - .WithProperty("PublishDir", BIN_DIR + framework)); + foreach (var framework in new[] { "netcoreapp3.1" }) + MSBuild(AGENT_CSPROJ, CreateMSBuildSettings("Publish") + .WithProperty("TargetFramework", framework) + .WithProperty("PublishDir", BIN_DIR + "agents/" + framework)); + + foreach (var framework in new[] { "netstandard2.0" }) + MSBuild(ENGINE_API_CSPROJ, CreateMSBuildSettings("Publish") + .WithProperty("TargetFramework", framework) + .WithProperty("PublishDir", BIN_DIR + framework)); - foreach(var framework in new [] { "netcoreapp2.1", "netcoreapp3.1" }) + foreach (var framework in new [] { "netcoreapp2.1", "netcoreapp3.1" }) MSBuild(ENGINE_TESTS_CSPROJ, CreateMSBuildSettings("Publish") .WithProperty("TargetFramework", framework) .WithProperty("PublishDir", BIN_DIR + framework)); @@ -478,11 +485,11 @@ Task("BuildChocolateyPackages") new ChocolateyNuSpecContent { Source = CURRENT_IMG_DIR + "bin/agents/net40/nunit.engine.api.xml", Target="tools/agents/net40" }, new ChocolateyNuSpecContent { Source = CURRENT_IMG_DIR + "bin/agents/net40/nunit.engine.core.dll", Target="tools/agents/net40" }, new ChocolateyNuSpecContent { Source = CURRENT_IMG_DIR + "bin/agents/net40/testcentric.engine.metadata.dll", Target="tools/agents/net40" }, - new ChocolateyNuSpecContent { Source = CURRENT_IMG_DIR + "bin/agents/netcoreapp3.1/nunit-agent.exe", Target="tools/agents/netcoreapp3.1" }, - //new ChocolateyNuSpecContent { Source = CURRENT_IMG_DIR + "bin/agents/netcoreapp3.1/nunit-agent.exe.config", Target="tools/agents/netcoreapp3.1" }, + new ChocolateyNuSpecContent { Source = CURRENT_IMG_DIR + "bin/agents/netcoreapp3.1/nunit-agent.dll", Target="tools/agents/netcoreapp3.1" }, + new ChocolateyNuSpecContent { Source = CURRENT_IMG_DIR + "bin/agents/netcoreapp3.1/nunit-agent.dll.config", Target="tools/agents/netcoreapp3.1" }, new ChocolateyNuSpecContent { Source = CHOCO_DIR + "nunit-agent.exe.ignore", Target="tools/agents/netcoreapp3.1" }, - new ChocolateyNuSpecContent { Source = CURRENT_IMG_DIR + "bin/agents/netcoreapp3.1/nunit-agent-x86.exe", Target="tools/agents/netcoreapp3.1" }, - // new ChocolateyNuSpecContent { Source = CURRENT_IMG_DIR + "bin/agents/netcoreapp3.1/nunit-agent-x86.exe.config", Target="tools/agents/netcoreapp3.1" }, + new ChocolateyNuSpecContent { Source = CURRENT_IMG_DIR + "bin/agents/netcoreapp3.1/nunit-agent-x86.dll", Target="tools/agents/netcoreapp3.1" }, + new ChocolateyNuSpecContent { Source = CURRENT_IMG_DIR + "bin/agents/netcoreapp3.1/nunit-agent-x86.dll.config", Target="tools/agents/netcoreapp3.1" }, new ChocolateyNuSpecContent { Source = CHOCO_DIR + "nunit-agent-x86.exe.ignore", Target="tools/agents/netcoreapp3.1" }, new ChocolateyNuSpecContent { Source = CURRENT_IMG_DIR + "bin/agents/netcoreapp3.1/nunit.engine.api.dll", Target="tools/agents/netcoreapp3.1" }, new ChocolateyNuSpecContent { Source = CURRENT_IMG_DIR + "bin/agents/netcoreapp3.1/nunit.engine.api.xml", Target="tools/agents/netcoreapp3.1" }, diff --git a/nuget/engine/nunit.engine.nuspec b/nuget/engine/nunit.engine.nuspec index cfe1f1473..93bea794b 100644 --- a/nuget/engine/nunit.engine.nuspec +++ b/nuget/engine/nunit.engine.nuspec @@ -42,10 +42,12 @@ + + @@ -53,6 +55,7 @@ + @@ -60,27 +63,32 @@ - - - - + + + + + + + + + diff --git a/nuget/runners/nunit.console-runner.nuspec b/nuget/runners/nunit.console-runner.nuspec index 9d303660f..f3bdd4c89 100644 --- a/nuget/runners/nunit.console-runner.nuspec +++ b/nuget/runners/nunit.console-runner.nuspec @@ -35,6 +35,7 @@ + @@ -44,6 +45,17 @@ + + + + + + + + + + + diff --git a/src/NUnitEngine/nunit-agent/nunit-agent-x86.csproj b/src/NUnitEngine/nunit-agent/nunit-agent-x86.csproj index 3e63c083e..2a7242876 100644 --- a/src/NUnitEngine/nunit-agent/nunit-agent-x86.csproj +++ b/src/NUnitEngine/nunit-agent/nunit-agent-x86.csproj @@ -1,6 +1,5 @@  - Exe nunit.agent net20;net40;netcoreapp3.1 app.manifest @@ -10,6 +9,9 @@ ..\..\..\bin\$(Configuration)\agents\ False + + Exe + diff --git a/src/NUnitEngine/nunit-agent/nunit-agent.csproj b/src/NUnitEngine/nunit-agent/nunit-agent.csproj index 5e7aa9b0e..d9ed7def0 100644 --- a/src/NUnitEngine/nunit-agent/nunit-agent.csproj +++ b/src/NUnitEngine/nunit-agent/nunit-agent.csproj @@ -1,6 +1,5 @@  - Exe nunit.agent net20;net40;netcoreapp3.1 app.manifest @@ -8,6 +7,9 @@ false ..\..\..\bin\$(Configuration)\agents\ + + Exe + From 82ca51df65fea4134057394ee0c99aa8496347e4 Mon Sep 17 00:00:00 2001 From: Charlie Poole Date: Tue, 13 Apr 2021 05:05:41 -0700 Subject: [PATCH 09/24] Add nunit3-console package test under .NET Core 3.1 --- NUnitConsole.sln | 3 ++ build.cake | 1 + nuget/engine/nunit.engine.nuspec | 1 + nuget/runners/nunit.console-runner.nuspec | 20 +++---- package-tests.cake | 52 ++++++++++++++++--- src/NUnitEngine/nunit-agent/Program.cs | 5 +- .../nunit-agent/nunit-agent-x86.csproj | 4 +- .../nunit-agent/nunit-agent.csproj | 4 +- .../nunit.engine/Services/AgentProcess.cs | 18 +++++-- test-results.cake | 3 +- 10 files changed, 81 insertions(+), 30 deletions(-) diff --git a/NUnitConsole.sln b/NUnitConsole.sln index 04fd75174..b0224796f 100644 --- a/NUnitConsole.sln +++ b/NUnitConsole.sln @@ -14,8 +14,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution build.cake = build.cake BUILDING.md = BUILDING.md CHANGES.txt = CHANGES.txt + ci.cake = ci.cake CONTRIBUTING.md = CONTRIBUTING.md src\Directory.Build.props = src\Directory.Build.props + header-check.cake = header-check.cake LICENSE.txt = LICENSE.txt NetFXTests.nunit = NetFXTests.nunit NOTICES.txt = NOTICES.txt @@ -24,6 +26,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution package-checks.cake = package-checks.cake package-tests.cake = package-tests.cake README.md = README.md + test-results.cake = test-results.cake EndProjectSection EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "nuget", "nuget", "{A972031D-2F61-4183-AF75-99EE1A9F6B32}" diff --git a/build.cake b/build.cake index 492cd1e2f..3fd77b1af 100644 --- a/build.cake +++ b/build.cake @@ -487,6 +487,7 @@ Task("BuildChocolateyPackages") new ChocolateyNuSpecContent { Source = CURRENT_IMG_DIR + "bin/agents/net40/testcentric.engine.metadata.dll", Target="tools/agents/net40" }, new ChocolateyNuSpecContent { Source = CURRENT_IMG_DIR + "bin/agents/netcoreapp3.1/nunit-agent.dll", Target="tools/agents/netcoreapp3.1" }, new ChocolateyNuSpecContent { Source = CURRENT_IMG_DIR + "bin/agents/netcoreapp3.1/nunit-agent.dll.config", Target="tools/agents/netcoreapp3.1" }, + new ChocolateyNuSpecContent { Source = CURRENT_IMG_DIR + "bin/agents/netcoreapp3.1/nunit-agent.runtimeconfig.json", Target="tools/agents/netcoreapp3.1" }, new ChocolateyNuSpecContent { Source = CHOCO_DIR + "nunit-agent.exe.ignore", Target="tools/agents/netcoreapp3.1" }, new ChocolateyNuSpecContent { Source = CURRENT_IMG_DIR + "bin/agents/netcoreapp3.1/nunit-agent-x86.dll", Target="tools/agents/netcoreapp3.1" }, new ChocolateyNuSpecContent { Source = CURRENT_IMG_DIR + "bin/agents/netcoreapp3.1/nunit-agent-x86.dll.config", Target="tools/agents/netcoreapp3.1" }, diff --git a/nuget/engine/nunit.engine.nuspec b/nuget/engine/nunit.engine.nuspec index 93bea794b..053e7e2fb 100644 --- a/nuget/engine/nunit.engine.nuspec +++ b/nuget/engine/nunit.engine.nuspec @@ -66,6 +66,7 @@ + diff --git a/nuget/runners/nunit.console-runner.nuspec b/nuget/runners/nunit.console-runner.nuspec index f3bdd4c89..9a02b199a 100644 --- a/nuget/runners/nunit.console-runner.nuspec +++ b/nuget/runners/nunit.console-runner.nuspec @@ -46,15 +46,17 @@ - - - - - - - - - + + + + + + + + + + + diff --git a/package-tests.cake b/package-tests.cake index b65dffae1..96fb644e0 100644 --- a/package-tests.cake +++ b/package-tests.cake @@ -1,12 +1,14 @@ // Representation of a single test to be run against a pre-built package. public struct PackageTest { + public string Name; public string Description; public string Arguments; public ExpectedResult ExpectedResult; - public PackageTest(string description, string arguments, ExpectedResult expectedResult) + public PackageTest(string name, string description, string arguments, ExpectedResult expectedResult) { + Name = name; Description = description; Arguments = arguments; ExpectedResult = expectedResult; @@ -38,6 +40,7 @@ public abstract class PackageTester protected abstract string PackageName { get; } protected abstract string PackageInstallDirectory { get; } + protected abstract string PackageResultDirectory { get; } protected abstract string PackageBinDir { get; } protected string PackageUnderTest => _packageDir + PackageName; @@ -64,14 +67,12 @@ public abstract class PackageTester { var reporter = new ResultReporter(PackageName); + _context.CleanDirectory(PackageResultDirectory); + foreach (var packageTest in PackageTests) { - var resultFile = _outputDir + "TestResult.xml"; - // Delete result file ahead of time so we don't mistakenly - // read a left-over file from another test run. Leave the - // file after the run in case we need it to debug a failure. - if (_context.FileExists(resultFile)) - _context.DeleteFile(resultFile); + var resultDir = PackageResultDirectory + packageTest.Name + "/"; + var resultFile = resultDir + "TestResult.xml"; DisplayBanner(packageTest.Description); @@ -79,7 +80,7 @@ public abstract class PackageTester PackageBinDir + "nunit3-console.exe", new ProcessSettings() { - Arguments = packageTest.Arguments, + Arguments = $"{packageTest.Arguments} --work={resultDir}", WorkingDirectory = _outputDir }); @@ -116,6 +117,8 @@ public abstract class PackageTester } } +// These are tests using the .NET Framework build of the console runner. +// However, they now include running tests under .Net Core 3.1. public abstract class NetFXPackageTester : PackageTester { public NetFXPackageTester(ICakeContext context, string packageVersion) @@ -123,6 +126,7 @@ public abstract class NetFXPackageTester : PackageTester { // Add common tests for running under .NET Framework PackageTests.Add(new PackageTest( + "net35", "Run mock-assembly.dll under .NET 3.5", "net35/mock-assembly.dll", new ExpectedResult("Failed") @@ -136,6 +140,7 @@ public abstract class NetFXPackageTester : PackageTester })); PackageTests.Add(new PackageTest( + "net40", "Run mock-assembly.dll under .NET 4.x", "net40/mock-assembly.dll", new ExpectedResult("Failed") @@ -149,6 +154,7 @@ public abstract class NetFXPackageTester : PackageTester })); PackageTests.Add(new PackageTest( + "net35_plus_net40", "Run both copies of mock-assembly together", "net35/mock-assembly.dll net40/mock-assembly.dll", new ExpectedResult("Failed") @@ -160,6 +166,23 @@ public abstract class NetFXPackageTester : PackageTester Inconclusive = 2 * 1, Skipped = 2 * 7 })); + + // TODO: Remove this check when msi package is + // updated to include .net core assemblies. + if (GetType().Name != "MsiPackageTester") + PackageTests.Add(new PackageTest( + "netcoreapp3.1", + "Run mock-assembly.dll under .NET Core 3.1", + "netcoreapp3.1/mock-assembly.dll --trace:Debug", + new ExpectedResult("Failed") + { + Total = 37, + Passed = 23, + Failed = 5, + Warnings = 0, + Inconclusive = 1, + Skipped = 7 + })); } } @@ -170,6 +193,7 @@ public abstract class NetCorePackageTester : PackageTester { // Add common tests for running under .NET Core (2.1 or higher) PackageTests.Add(new PackageTest( + "netcoreapp2.1", "Run mock-assembly.dll targeting .NET Core 2.1", "netcoreapp2.1/mock-assembly.dll", new ExpectedResult("Failed") @@ -183,6 +207,7 @@ public abstract class NetCorePackageTester : PackageTester })); PackageTests.Add(new PackageTest( + "netcoreapp3.1", "Run mock-assembly targeting .NET Core 3.1", "netcoreapp3.1/mock-assembly.dll", new ExpectedResult("Failed") @@ -196,6 +221,7 @@ public abstract class NetCorePackageTester : PackageTester })); PackageTests.Add(new PackageTest( + "netcoreapp2.1_plus_netcoreapp3.1", "Run both copies of mock-assembly together", "netcoreapp2.1/mock-assembly.dll netcoreapp3.1/mock-assembly.dll", new ExpectedResult("Failed") @@ -218,6 +244,7 @@ public class NuGetNetFXPackageTester : NetFXPackageTester protected override string PackageName => $"NUnit.ConsoleRunner.{_packageVersion}.nupkg"; protected override string PackageInstallDirectory => _packageDir + "test/nuget-netfx/"; protected override string PackageBinDir => PackageInstallDirectory + "tools/"; + protected override string PackageResultDirectory => _packageDir + "test-results/nuget-netfx/"; } public class NuGetNetCorePackageTester : NetCorePackageTester @@ -228,6 +255,7 @@ public class NuGetNetCorePackageTester : NetCorePackageTester protected override string PackageName => $"NUnit.ConsoleRunner.NetCore.{_packageVersion}.nupkg"; protected override string PackageInstallDirectory => _packageDir + "test/nuget-netcore/"; protected override string PackageBinDir => PackageInstallDirectory + "tools/netcoreapp3.1/any/"; + protected override string PackageResultDirectory => _packageDir + "test-results/nuget-netcore/"; } public class ChocolateyPackageTester : NetFXPackageTester @@ -238,6 +266,7 @@ public class ChocolateyPackageTester : NetFXPackageTester protected override string PackageName => $"nunit-console-runner.{_packageVersion}.nupkg"; protected override string PackageInstallDirectory => _packageDir + "test/choco/"; protected override string PackageBinDir => PackageInstallDirectory + "tools/"; + protected override string PackageResultDirectory => _packageDir + "test-results/choco/"; } public class MsiPackageTester : NetFXPackageTester @@ -247,6 +276,7 @@ public class MsiPackageTester : NetFXPackageTester { // Add tests specific to the msi package PackageTests.Add(new PackageTest( + "net35_plus_net40_project", "Run project with both copies of mock-assembly", $"../../NetFXTests.nunit --config={_config}", new ExpectedResult("Failed") @@ -263,6 +293,7 @@ public class MsiPackageTester : NetFXPackageTester protected override string PackageName => $"NUnit.Console-{_packageVersion}.msi"; protected override string PackageInstallDirectory => _packageDir + "test/msi/"; protected override string PackageBinDir => PackageInstallDirectory + "NUnit.org/nunit-console/"; + protected override string PackageResultDirectory => _packageDir + "test-results/msi/"; protected override void CreatePackageInstallDirectory() { @@ -284,6 +315,9 @@ public class MsiPackageTester : NetFXPackageTester _context.CopyFiles( PackageBinDir + "*.dll", PackageBinDir + "agents/net40/"); + _context.CopyFiles( + PackageBinDir + "*.dll", + PackageBinDir + "agents/netcoreapp3.1"); } } } @@ -295,6 +329,7 @@ public class ZipPackageTester : NetFXPackageTester { // Add tests specific to the zip package PackageTests.Add(new PackageTest( + "net35_plus_net40_project", "Run project with both copies of mock-assembly", $"../../NetFXTests.nunit --config={_config}", new ExpectedResult("Failed") @@ -311,4 +346,5 @@ public class ZipPackageTester : NetFXPackageTester protected override string PackageName => $"NUnit.Console-{_packageVersion}.zip"; protected override string PackageInstallDirectory => _packageDir + "test/zip/"; protected override string PackageBinDir => PackageInstallDirectory + "bin/net20/"; + protected override string PackageResultDirectory => _packageDir + "test-results/zip/"; } diff --git a/src/NUnitEngine/nunit-agent/Program.cs b/src/NUnitEngine/nunit-agent/Program.cs index f2fb48fee..00e301e0b 100644 --- a/src/NUnitEngine/nunit-agent/Program.cs +++ b/src/NUnitEngine/nunit-agent/Program.cs @@ -26,6 +26,7 @@ public class NUnitTestAgent [STAThread] public static void Main(string[] args) { + Console.WriteLine("Agent Process Starting"); AgentId = new Guid(args[0]); AgencyUrl = args[1]; @@ -63,13 +64,13 @@ public static void Main(string[] args) InternalTrace.Initialize(Path.Combine(workDirectory, logName), traceLevel); log = InternalTrace.GetLogger(typeof(NUnitTestAgent)); + log.Info("Agent process {0} starting", pid); + if (debugArgPassed) TryLaunchDebugger(); LocateAgencyProcess(agencyPid); - log.Info("Agent process {0} starting", pid); - #if NETFRAMEWORK log.Info("Running under version {0}, {1}", Environment.Version, diff --git a/src/NUnitEngine/nunit-agent/nunit-agent-x86.csproj b/src/NUnitEngine/nunit-agent/nunit-agent-x86.csproj index 2a7242876..3e63c083e 100644 --- a/src/NUnitEngine/nunit-agent/nunit-agent-x86.csproj +++ b/src/NUnitEngine/nunit-agent/nunit-agent-x86.csproj @@ -1,5 +1,6 @@  + Exe nunit.agent net20;net40;netcoreapp3.1 app.manifest @@ -9,9 +10,6 @@ ..\..\..\bin\$(Configuration)\agents\ False - - Exe - diff --git a/src/NUnitEngine/nunit-agent/nunit-agent.csproj b/src/NUnitEngine/nunit-agent/nunit-agent.csproj index d9ed7def0..5e7aa9b0e 100644 --- a/src/NUnitEngine/nunit-agent/nunit-agent.csproj +++ b/src/NUnitEngine/nunit-agent/nunit-agent.csproj @@ -1,5 +1,6 @@  + Exe nunit.agent net20;net40;netcoreapp3.1 app.manifest @@ -7,9 +8,6 @@ false ..\..\..\bin\$(Configuration)\agents\ - - Exe - diff --git a/src/NUnitEngine/nunit.engine/Services/AgentProcess.cs b/src/NUnitEngine/nunit.engine/Services/AgentProcess.cs index 98081e566..acf9b5936 100644 --- a/src/NUnitEngine/nunit.engine/Services/AgentProcess.cs +++ b/src/NUnitEngine/nunit.engine/Services/AgentProcess.cs @@ -61,6 +61,12 @@ public AgentProcess(TestAgency agency, TestPackage package, Guid agentId) StartInfo.Arguments = AgentArgs.ToString(); StartInfo.LoadUserProfile = loadUserProfile; } + else if (TargetRuntime.Runtime == RuntimeType.NetCore) + { + StartInfo.FileName = "dotnet"; + StartInfo.Arguments = $"{AgentExePath} {AgentArgs}"; + StartInfo.LoadUserProfile = loadUserProfile; + } else { StartInfo.FileName = AgentExePath; @@ -70,7 +76,7 @@ public AgentProcess(TestAgency agency, TestPackage package, Guid agentId) // Internal properties exposed for testing - internal RuntimeFramework TargetRuntime { get; } + internal RuntimeFramework TargetRuntime { get; } internal string AgentExePath { get; } internal StringBuilder AgentArgs { get; } @@ -96,24 +102,28 @@ public static string GetTestAgentExePath(RuntimeFramework targetRuntime, bool re log.Debug($"Checking for agents at {agentsDir}"); string agentName = requires32Bit - ? "nunit-agent-x86.exe" - : "nunit-agent.exe"; + ? "nunit-agent-x86" + : "nunit-agent"; string runtimeDir; + string agentExtension; switch (targetRuntime.Runtime) { case RuntimeType.Net: case RuntimeType.Mono: runtimeDir = targetRuntime.FrameworkVersion.Major >= 4 ? "net40" : "net20"; + agentExtension = ".exe"; break; case RuntimeType.NetCore: runtimeDir = "netcoreapp3.1"; + agentExtension = ".dll"; break; default: + log.Error($"Unknown runtime type: {targetRuntime.Runtime}"); return null; } - return Path.Combine(Path.Combine(agentsDir, runtimeDir), agentName); + return Path.Combine(Path.Combine(agentsDir, runtimeDir), agentName + agentExtension); } } } diff --git a/test-results.cake b/test-results.cake index 2588bfac9..33791b863 100644 --- a/test-results.cake +++ b/test-results.cake @@ -130,10 +130,11 @@ public class TestReport foreach (XmlNode suite in suites) { // Narrow down to the specific failures we want + string runState = GetAttribute(suite, "runstate"); string suiteResult = GetAttribute(suite, "result"); string label = GetAttribute(suite, "label"); string site = suite.Attributes["site"]?.Value ?? "Test"; - if (suiteResult == "Failed" && site == "Test" && label == "Invalid") + if (runState == "NotRunnable" || suiteResult == "Failed" && site == "Test" && (label == "Invalid" || label=="Error")) { string message = suite.SelectSingleNode("reason/message")?.InnerText; Errors.Add($" {message}"); From c8f6fc0aa0b88e7839e591d86e8ed05b6eb7992f Mon Sep 17 00:00:00 2001 From: Charlie Poole Date: Tue, 13 Apr 2021 08:38:30 -0700 Subject: [PATCH 10/24] Fix error in MSI package tests --- package-tests.cake | 3 --- 1 file changed, 3 deletions(-) diff --git a/package-tests.cake b/package-tests.cake index 96fb644e0..50b6cd975 100644 --- a/package-tests.cake +++ b/package-tests.cake @@ -315,9 +315,6 @@ public class MsiPackageTester : NetFXPackageTester _context.CopyFiles( PackageBinDir + "*.dll", PackageBinDir + "agents/net40/"); - _context.CopyFiles( - PackageBinDir + "*.dll", - PackageBinDir + "agents/netcoreapp3.1"); } } } From c3556d0f11d1ac2eb802e59a3611ea4541175edb Mon Sep 17 00:00:00 2001 From: Charlie Poole Date: Wed, 14 Apr 2021 16:43:10 -0700 Subject: [PATCH 11/24] Remove 32-bit build of .net core agents --- build.cake | 3 --- nuget/engine/nunit.engine.nuspec | 2 -- nuget/runners/nunit.console-runner.nuspec | 3 --- src/NUnitEngine/nunit-agent/nunit-agent-x86.csproj | 2 +- 4 files changed, 1 insertion(+), 9 deletions(-) diff --git a/build.cake b/build.cake index 3fd77b1af..e0e36518b 100644 --- a/build.cake +++ b/build.cake @@ -489,9 +489,6 @@ Task("BuildChocolateyPackages") new ChocolateyNuSpecContent { Source = CURRENT_IMG_DIR + "bin/agents/netcoreapp3.1/nunit-agent.dll.config", Target="tools/agents/netcoreapp3.1" }, new ChocolateyNuSpecContent { Source = CURRENT_IMG_DIR + "bin/agents/netcoreapp3.1/nunit-agent.runtimeconfig.json", Target="tools/agents/netcoreapp3.1" }, new ChocolateyNuSpecContent { Source = CHOCO_DIR + "nunit-agent.exe.ignore", Target="tools/agents/netcoreapp3.1" }, - new ChocolateyNuSpecContent { Source = CURRENT_IMG_DIR + "bin/agents/netcoreapp3.1/nunit-agent-x86.dll", Target="tools/agents/netcoreapp3.1" }, - new ChocolateyNuSpecContent { Source = CURRENT_IMG_DIR + "bin/agents/netcoreapp3.1/nunit-agent-x86.dll.config", Target="tools/agents/netcoreapp3.1" }, - new ChocolateyNuSpecContent { Source = CHOCO_DIR + "nunit-agent-x86.exe.ignore", Target="tools/agents/netcoreapp3.1" }, new ChocolateyNuSpecContent { Source = CURRENT_IMG_DIR + "bin/agents/netcoreapp3.1/nunit.engine.api.dll", Target="tools/agents/netcoreapp3.1" }, new ChocolateyNuSpecContent { Source = CURRENT_IMG_DIR + "bin/agents/netcoreapp3.1/nunit.engine.api.xml", Target="tools/agents/netcoreapp3.1" }, new ChocolateyNuSpecContent { Source = CURRENT_IMG_DIR + "bin/agents/netcoreapp3.1/nunit.engine.core.dll", Target="tools/agents/netcoreapp3.1" }, diff --git a/nuget/engine/nunit.engine.nuspec b/nuget/engine/nunit.engine.nuspec index 0f9e00bd8..2efdcb48a 100644 --- a/nuget/engine/nunit.engine.nuspec +++ b/nuget/engine/nunit.engine.nuspec @@ -59,8 +59,6 @@ - - diff --git a/nuget/runners/nunit.console-runner.nuspec b/nuget/runners/nunit.console-runner.nuspec index 9a02b199a..361e514ec 100644 --- a/nuget/runners/nunit.console-runner.nuspec +++ b/nuget/runners/nunit.console-runner.nuspec @@ -49,9 +49,6 @@ - - - diff --git a/src/NUnitEngine/nunit-agent/nunit-agent-x86.csproj b/src/NUnitEngine/nunit-agent/nunit-agent-x86.csproj index 3e63c083e..2a3003887 100644 --- a/src/NUnitEngine/nunit-agent/nunit-agent-x86.csproj +++ b/src/NUnitEngine/nunit-agent/nunit-agent-x86.csproj @@ -2,7 +2,7 @@ Exe nunit.agent - net20;net40;netcoreapp3.1 + net20;net40 app.manifest ..\..\..\nunit.ico x86 From a6d3e9e6218522c3371c4553c0ec2d44e3d354f1 Mon Sep 17 00:00:00 2001 From: Charlie Poole Date: Thu, 15 Apr 2021 08:06:26 -0700 Subject: [PATCH 12/24] Move nunit-agent-x86 to a separate folder --- NUnitConsole.sln | 16 +- build.cake | 1 - .../agents/nunit-agent-x86/AgentExitCodes.cs | 16 ++ .../agents/nunit-agent-x86/Program.cs | 186 ++++++++++++++++++ .../Properties/AssemblyInfo.cs | 17 ++ .../agents/nunit-agent-x86/app.config | 41 ++++ .../agents/nunit-agent-x86/app.manifest | 41 ++++ .../nunit-agent-x86/nunit-agent-x86.csproj | 28 +++ .../agents/nunit-agent/AgentExitCodes.cs | 16 ++ src/NUnitEngine/agents/nunit-agent/Program.cs | 186 ++++++++++++++++++ .../nunit-agent/Properties/AssemblyInfo.cs | 17 ++ src/NUnitEngine/agents/nunit-agent/app.config | 41 ++++ .../agents/nunit-agent/app.manifest | 41 ++++ .../agents/nunit-agent/nunit-agent.csproj | 27 +++ .../Properties/AssemblyInfo.cs | 17 ++ src/NUnitEngine/nunit-agent-x86/app.config | 41 ++++ src/NUnitEngine/nunit-agent-x86/app.manifest | 41 ++++ .../nunit-agent-x86.csproj | 3 +- 18 files changed, 765 insertions(+), 11 deletions(-) create mode 100644 src/NUnitEngine/agents/nunit-agent-x86/AgentExitCodes.cs create mode 100644 src/NUnitEngine/agents/nunit-agent-x86/Program.cs create mode 100644 src/NUnitEngine/agents/nunit-agent-x86/Properties/AssemblyInfo.cs create mode 100644 src/NUnitEngine/agents/nunit-agent-x86/app.config create mode 100644 src/NUnitEngine/agents/nunit-agent-x86/app.manifest create mode 100644 src/NUnitEngine/agents/nunit-agent-x86/nunit-agent-x86.csproj create mode 100644 src/NUnitEngine/agents/nunit-agent/AgentExitCodes.cs create mode 100644 src/NUnitEngine/agents/nunit-agent/Program.cs create mode 100644 src/NUnitEngine/agents/nunit-agent/Properties/AssemblyInfo.cs create mode 100644 src/NUnitEngine/agents/nunit-agent/app.config create mode 100644 src/NUnitEngine/agents/nunit-agent/app.manifest create mode 100644 src/NUnitEngine/agents/nunit-agent/nunit-agent.csproj create mode 100644 src/NUnitEngine/nunit-agent-x86/Properties/AssemblyInfo.cs create mode 100644 src/NUnitEngine/nunit-agent-x86/app.config create mode 100644 src/NUnitEngine/nunit-agent-x86/app.manifest rename src/NUnitEngine/{nunit-agent => nunit-agent-x86}/nunit-agent-x86.csproj (88%) diff --git a/NUnitConsole.sln b/NUnitConsole.sln index b0224796f..d18fc5037 100644 --- a/NUnitConsole.sln +++ b/NUnitConsole.sln @@ -41,7 +41,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "nunit.engine.tests", "src\N ProjectSection(ProjectDependencies) = postProject {B1D90742-39BD-429C-8E87-C5CD2991DF27} = {B1D90742-39BD-429C-8E87-C5CD2991DF27} {C2A8FC7A-FA64-46EA-AF6D-73D6B371DBF8} = {C2A8FC7A-FA64-46EA-AF6D-73D6B371DBF8} - {28B605B2-E2E9-417E-8369-49E263F1F31B} = {28B605B2-E2E9-417E-8369-49E263F1F31B} {0DE218CA-AFB8-423A-9CD2-E22DEAC55C46} = {0DE218CA-AFB8-423A-9CD2-E22DEAC55C46} EndProjectSection EndProject @@ -56,11 +55,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "nunit3-console.tests", "src {B1D90742-39BD-429C-8E87-C5CD2991DF27} = {B1D90742-39BD-429C-8E87-C5CD2991DF27} {D694CB69-6CFB-4762-86C2-EB27B808B282} = {D694CB69-6CFB-4762-86C2-EB27B808B282} {C2A8FC7A-FA64-46EA-AF6D-73D6B371DBF8} = {C2A8FC7A-FA64-46EA-AF6D-73D6B371DBF8} - {28B605B2-E2E9-417E-8369-49E263F1F31B} = {28B605B2-E2E9-417E-8369-49E263F1F31B} EndProjectSection EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "nunit-agent-x86", "src\NUnitEngine\nunit-agent\nunit-agent-x86.csproj", "{28B605B2-E2E9-417E-8369-49E263F1F31B}" -EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "engine", "engine", "{43A219A8-2995-4884-806F-FDB9CD25D403}" ProjectSection(SolutionItems) = preProject nuget\engine\nunit.agent.addins = nuget\engine\nunit.agent.addins @@ -101,6 +97,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "deprecated", "deprecated", EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "nunit.engine.core", "src\NUnitEngine\nunit.engine.core\nunit.engine.core.csproj", "{A19C026B-1C0F-4AA3-AC49-7D8B4C7231CF}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "nunit-agent-x86", "src\NUnitEngine\nunit-agent-x86\nunit-agent-x86.csproj", "{333D2FBC-CCA7-46AF-9453-C310671A67B0}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -131,10 +129,6 @@ Global {B310A760-8AE1-41CA-81F8-03B12E2FCE30}.Debug|Any CPU.Build.0 = Debug|Any CPU {B310A760-8AE1-41CA-81F8-03B12E2FCE30}.Release|Any CPU.ActiveCfg = Release|Any CPU {B310A760-8AE1-41CA-81F8-03B12E2FCE30}.Release|Any CPU.Build.0 = Release|Any CPU - {28B605B2-E2E9-417E-8369-49E263F1F31B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {28B605B2-E2E9-417E-8369-49E263F1F31B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {28B605B2-E2E9-417E-8369-49E263F1F31B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {28B605B2-E2E9-417E-8369-49E263F1F31B}.Release|Any CPU.Build.0 = Release|Any CPU {D2C80E4B-1117-4F02-AB02-E453BDA0C58E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {D2C80E4B-1117-4F02-AB02-E453BDA0C58E}.Debug|Any CPU.Build.0 = Debug|Any CPU {D2C80E4B-1117-4F02-AB02-E453BDA0C58E}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -147,6 +141,10 @@ Global {A19C026B-1C0F-4AA3-AC49-7D8B4C7231CF}.Debug|Any CPU.Build.0 = Debug|Any CPU {A19C026B-1C0F-4AA3-AC49-7D8B4C7231CF}.Release|Any CPU.ActiveCfg = Release|Any CPU {A19C026B-1C0F-4AA3-AC49-7D8B4C7231CF}.Release|Any CPU.Build.0 = Release|Any CPU + {333D2FBC-CCA7-46AF-9453-C310671A67B0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {333D2FBC-CCA7-46AF-9453-C310671A67B0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {333D2FBC-CCA7-46AF-9453-C310671A67B0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {333D2FBC-CCA7-46AF-9453-C310671A67B0}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -159,7 +157,6 @@ Global {C2A8FC7A-FA64-46EA-AF6D-73D6B371DBF8} = {31B45C4C-206F-4F31-9CC6-33BF11DFEE39} {0DE218CA-AFB8-423A-9CD2-E22DEAC55C46} = {576DB1E6-C5EC-4FEF-A826-EC19D8BEE572} {B310A760-8AE1-41CA-81F8-03B12E2FCE30} = {576DB1E6-C5EC-4FEF-A826-EC19D8BEE572} - {28B605B2-E2E9-417E-8369-49E263F1F31B} = {31B45C4C-206F-4F31-9CC6-33BF11DFEE39} {43A219A8-2995-4884-806F-FDB9CD25D403} = {A972031D-2F61-4183-AF75-99EE1A9F6B32} {F3E87D0F-6F06-4C0B-AE06-42C0834C3C6E} = {A972031D-2F61-4183-AF75-99EE1A9F6B32} {D2C80E4B-1117-4F02-AB02-E453BDA0C58E} = {31B45C4C-206F-4F31-9CC6-33BF11DFEE39} @@ -167,6 +164,7 @@ Global {4FDF7BFA-A337-41D3-898D-C6A98278E6AD} = {49D441DF-39FD-4F4D-AECA-86CF8EFE23AF} {9A7C8370-ED1F-486F-A8F5-C5BF4221464E} = {A972031D-2F61-4183-AF75-99EE1A9F6B32} {A19C026B-1C0F-4AA3-AC49-7D8B4C7231CF} = {31B45C4C-206F-4F31-9CC6-33BF11DFEE39} + {333D2FBC-CCA7-46AF-9453-C310671A67B0} = {31B45C4C-206F-4F31-9CC6-33BF11DFEE39} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {D8E4FC26-5422-4C51-8BBC-D1AC0A578711} diff --git a/build.cake b/build.cake index e0e36518b..c4270ec6e 100644 --- a/build.cake +++ b/build.cake @@ -52,7 +52,6 @@ var ZIP_IMG = PROJECT_DIR + "zip-image/"; var SOLUTION_FILE = PROJECT_DIR + "NUnitConsole.sln"; var ENGINE_CSPROJ = PROJECT_DIR + "src/NUnitEngine/nunit.engine/nunit.engine.csproj"; var AGENT_CSPROJ = PROJECT_DIR + "src/NUnitEngine/nunit-agent/nunit-agent.csproj"; - // Agent X86 is in the same directory, so we don't need to do anything with it var ENGINE_API_CSPROJ = PROJECT_DIR + "src/NUnitEngine/nunit.engine.api/nunit.engine.api.csproj"; var ENGINE_TESTS_CSPROJ = PROJECT_DIR + "src/NUnitEngine/nunit.engine.tests/nunit.engine.tests.csproj"; var CONSOLE_CSPROJ = PROJECT_DIR + "src/NUnitConsole/nunit3-console/nunit3-console.csproj"; diff --git a/src/NUnitEngine/agents/nunit-agent-x86/AgentExitCodes.cs b/src/NUnitEngine/agents/nunit-agent-x86/AgentExitCodes.cs new file mode 100644 index 000000000..c1dfc0bfb --- /dev/null +++ b/src/NUnitEngine/agents/nunit-agent-x86/AgentExitCodes.cs @@ -0,0 +1,16 @@ +// Copyright (c) Charlie Poole, Rob Prouse and Contributors. MIT License - see LICENSE.txt + +namespace NUnit.Common +{ + internal static class AgentExitCodes + { + public const int OK = 0; + public const int PARENT_PROCESS_TERMINATED = -1; + public const int FAILED_TO_START_REMOTE_AGENT = -2; + public const int DEBUGGER_SECURITY_VIOLATION = -3; + public const int DEBUGGER_NOT_IMPLEMENTED = -4; + public const int UNABLE_TO_LOCATE_AGENCY = -5; + public const int UNEXPECTED_EXCEPTION = -100; + public const int STACK_OVERFLOW_EXCEPTION = -1073741571; + } +} diff --git a/src/NUnitEngine/agents/nunit-agent-x86/Program.cs b/src/NUnitEngine/agents/nunit-agent-x86/Program.cs new file mode 100644 index 000000000..00e301e0b --- /dev/null +++ b/src/NUnitEngine/agents/nunit-agent-x86/Program.cs @@ -0,0 +1,186 @@ +// Copyright (c) Charlie Poole, Rob Prouse and Contributors. MIT License - see LICENSE.txt + +using System; +using System.Diagnostics; +using System.IO; +using System.Security; +using NUnit.Common; +using NUnit.Engine; +using NUnit.Engine.Agents; +using NUnit.Engine.Internal; +using NUnit.Engine.Services; + +namespace NUnit.Agent +{ + public class NUnitTestAgent + { + static Guid AgentId; + static string AgencyUrl; + static Process AgencyProcess; + static RemoteTestAgent Agent; + private static Logger log; + + /// + /// The main entry point for the application. + /// + [STAThread] + public static void Main(string[] args) + { + Console.WriteLine("Agent Process Starting"); + AgentId = new Guid(args[0]); + AgencyUrl = args[1]; + + var traceLevel = InternalTraceLevel.Off; + var pid = Process.GetCurrentProcess().Id; + var debugArgPassed = false; + var workDirectory = string.Empty; + var agencyPid = string.Empty; + + for (int i = 2; i < args.Length; i++) + { + string arg = args[i]; + + // NOTE: we can test these strings exactly since + // they originate from the engine itself. + if (arg == "--debug-agent") + { + debugArgPassed = true; + } + else if (arg.StartsWith("--trace=")) + { + traceLevel = (InternalTraceLevel)Enum.Parse(typeof(InternalTraceLevel), arg.Substring(8)); + } + else if (arg.StartsWith("--pid=")) + { + agencyPid = arg.Substring(6); + } + else if (arg.StartsWith("--work=")) + { + workDirectory = arg.Substring(7); + } + } + + var logName = $"nunit-agent_{pid}.log"; + InternalTrace.Initialize(Path.Combine(workDirectory, logName), traceLevel); + log = InternalTrace.GetLogger(typeof(NUnitTestAgent)); + + log.Info("Agent process {0} starting", pid); + + if (debugArgPassed) + TryLaunchDebugger(); + + LocateAgencyProcess(agencyPid); + +#if NETFRAMEWORK + log.Info("Running under version {0}, {1}", + Environment.Version, + RuntimeFramework.CurrentFramework.DisplayName); +#endif + + // Create CoreEngine + var engine = new CoreEngine + { + WorkDirectory = workDirectory, + InternalTraceLevel = traceLevel + }; + + // Custom Service Initialization + engine.Services.Add(new ExtensionService(isRunningOnAgent: true)); +#if !NETCOREAPP + engine.Services.Add(new DomainManager()); +#endif + engine.Services.Add(new InProcessTestRunnerFactory()); + engine.Services.Add(new DriverService()); + + // Initialize Services + log.Info("Initializing Services"); + engine.InitializeServices(); + + log.Info("Starting RemoteTestAgent"); + Agent = new RemoteTestAgent(AgentId, engine.Services); + Agent.Transport = +#if NETFRAMEWORK + new Engine.Communication.Transports.Remoting.TestAgentRemotingTransport(Agent, AgencyUrl); +#else + new Engine.Communication.Transports.Tcp.TestAgentTcpTransport(Agent, AgencyUrl); +#endif + + try + { + if (Agent.Start()) + WaitForStop(); + else + { + log.Error("Failed to start RemoteTestAgent"); + Environment.Exit(AgentExitCodes.FAILED_TO_START_REMOTE_AGENT); + } + } + catch (Exception ex) + { + log.Error("Exception in RemoteTestAgent. {0}", ExceptionHelper.BuildMessageAndStackTrace(ex)); + Environment.Exit(AgentExitCodes.UNEXPECTED_EXCEPTION); + } + log.Info("Agent process {0} exiting cleanly", pid); + + Environment.Exit(AgentExitCodes.OK); + } + + private static void LocateAgencyProcess(string agencyPid) + { + var agencyProcessId = int.Parse(agencyPid); + try + { + AgencyProcess = Process.GetProcessById(agencyProcessId); + } + catch (Exception e) + { + log.Error($"Unable to connect to agency process with PID: {agencyProcessId}"); + log.Error($"Failed with exception: {e.Message} {e.StackTrace}"); + Environment.Exit(AgentExitCodes.UNABLE_TO_LOCATE_AGENCY); + } + } + + private static void WaitForStop() + { + log.Debug("Waiting for stopSignal"); + + while (!Agent.WaitForStop(500)) + { + if (AgencyProcess.HasExited) + { + log.Error("Parent process has been terminated."); + Environment.Exit(AgentExitCodes.PARENT_PROCESS_TERMINATED); + } + } + + log.Debug("Stop signal received"); + } + + private static void TryLaunchDebugger() + { + if (Debugger.IsAttached) + return; + + try + { + Debugger.Launch(); + } + catch (SecurityException se) + { + if (InternalTrace.Initialized) + { + log.Error($"System.Security.Permissions.UIPermission is not set to start the debugger. {se} {se.StackTrace}"); + } + Environment.Exit(AgentExitCodes.DEBUGGER_SECURITY_VIOLATION); + } + catch (NotImplementedException nie) //Debugger is not implemented on mono + { + if (InternalTrace.Initialized) + { + log.Error($"Debugger is not available on all platforms. {nie} {nie.StackTrace}"); + } + Environment.Exit(AgentExitCodes.DEBUGGER_NOT_IMPLEMENTED); + } + } + } +} diff --git a/src/NUnitEngine/agents/nunit-agent-x86/Properties/AssemblyInfo.cs b/src/NUnitEngine/agents/nunit-agent-x86/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..0b3a9e8c1 --- /dev/null +++ b/src/NUnitEngine/agents/nunit-agent-x86/Properties/AssemblyInfo.cs @@ -0,0 +1,17 @@ +using System.Reflection; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("NUnit Agent")] +[assembly: AssemblyDescription("Runs tests in a separate process when necessary")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("59b4d53d-99d7-44f2-8c6e-b83d41718dd6")] diff --git a/src/NUnitEngine/agents/nunit-agent-x86/app.config b/src/NUnitEngine/agents/nunit-agent-x86/app.config new file mode 100644 index 000000000..f72511441 --- /dev/null +++ b/src/NUnitEngine/agents/nunit-agent-x86/app.config @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/NUnitEngine/agents/nunit-agent-x86/app.manifest b/src/NUnitEngine/agents/nunit-agent-x86/app.manifest new file mode 100644 index 000000000..589e43719 --- /dev/null +++ b/src/NUnitEngine/agents/nunit-agent-x86/app.manifest @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/NUnitEngine/agents/nunit-agent-x86/nunit-agent-x86.csproj b/src/NUnitEngine/agents/nunit-agent-x86/nunit-agent-x86.csproj new file mode 100644 index 000000000..9deda8001 --- /dev/null +++ b/src/NUnitEngine/agents/nunit-agent-x86/nunit-agent-x86.csproj @@ -0,0 +1,28 @@ + + + Exe + nunit.agent + net20;net40 + app.manifest + ..\..\..\..\nunit.ico + x86 + ..\..\..\..\bin\$(Configuration)\agents\ + False + + + + + + + + + + + nunit.ico + + + + + + + \ No newline at end of file diff --git a/src/NUnitEngine/agents/nunit-agent/AgentExitCodes.cs b/src/NUnitEngine/agents/nunit-agent/AgentExitCodes.cs new file mode 100644 index 000000000..c1dfc0bfb --- /dev/null +++ b/src/NUnitEngine/agents/nunit-agent/AgentExitCodes.cs @@ -0,0 +1,16 @@ +// Copyright (c) Charlie Poole, Rob Prouse and Contributors. MIT License - see LICENSE.txt + +namespace NUnit.Common +{ + internal static class AgentExitCodes + { + public const int OK = 0; + public const int PARENT_PROCESS_TERMINATED = -1; + public const int FAILED_TO_START_REMOTE_AGENT = -2; + public const int DEBUGGER_SECURITY_VIOLATION = -3; + public const int DEBUGGER_NOT_IMPLEMENTED = -4; + public const int UNABLE_TO_LOCATE_AGENCY = -5; + public const int UNEXPECTED_EXCEPTION = -100; + public const int STACK_OVERFLOW_EXCEPTION = -1073741571; + } +} diff --git a/src/NUnitEngine/agents/nunit-agent/Program.cs b/src/NUnitEngine/agents/nunit-agent/Program.cs new file mode 100644 index 000000000..00e301e0b --- /dev/null +++ b/src/NUnitEngine/agents/nunit-agent/Program.cs @@ -0,0 +1,186 @@ +// Copyright (c) Charlie Poole, Rob Prouse and Contributors. MIT License - see LICENSE.txt + +using System; +using System.Diagnostics; +using System.IO; +using System.Security; +using NUnit.Common; +using NUnit.Engine; +using NUnit.Engine.Agents; +using NUnit.Engine.Internal; +using NUnit.Engine.Services; + +namespace NUnit.Agent +{ + public class NUnitTestAgent + { + static Guid AgentId; + static string AgencyUrl; + static Process AgencyProcess; + static RemoteTestAgent Agent; + private static Logger log; + + /// + /// The main entry point for the application. + /// + [STAThread] + public static void Main(string[] args) + { + Console.WriteLine("Agent Process Starting"); + AgentId = new Guid(args[0]); + AgencyUrl = args[1]; + + var traceLevel = InternalTraceLevel.Off; + var pid = Process.GetCurrentProcess().Id; + var debugArgPassed = false; + var workDirectory = string.Empty; + var agencyPid = string.Empty; + + for (int i = 2; i < args.Length; i++) + { + string arg = args[i]; + + // NOTE: we can test these strings exactly since + // they originate from the engine itself. + if (arg == "--debug-agent") + { + debugArgPassed = true; + } + else if (arg.StartsWith("--trace=")) + { + traceLevel = (InternalTraceLevel)Enum.Parse(typeof(InternalTraceLevel), arg.Substring(8)); + } + else if (arg.StartsWith("--pid=")) + { + agencyPid = arg.Substring(6); + } + else if (arg.StartsWith("--work=")) + { + workDirectory = arg.Substring(7); + } + } + + var logName = $"nunit-agent_{pid}.log"; + InternalTrace.Initialize(Path.Combine(workDirectory, logName), traceLevel); + log = InternalTrace.GetLogger(typeof(NUnitTestAgent)); + + log.Info("Agent process {0} starting", pid); + + if (debugArgPassed) + TryLaunchDebugger(); + + LocateAgencyProcess(agencyPid); + +#if NETFRAMEWORK + log.Info("Running under version {0}, {1}", + Environment.Version, + RuntimeFramework.CurrentFramework.DisplayName); +#endif + + // Create CoreEngine + var engine = new CoreEngine + { + WorkDirectory = workDirectory, + InternalTraceLevel = traceLevel + }; + + // Custom Service Initialization + engine.Services.Add(new ExtensionService(isRunningOnAgent: true)); +#if !NETCOREAPP + engine.Services.Add(new DomainManager()); +#endif + engine.Services.Add(new InProcessTestRunnerFactory()); + engine.Services.Add(new DriverService()); + + // Initialize Services + log.Info("Initializing Services"); + engine.InitializeServices(); + + log.Info("Starting RemoteTestAgent"); + Agent = new RemoteTestAgent(AgentId, engine.Services); + Agent.Transport = +#if NETFRAMEWORK + new Engine.Communication.Transports.Remoting.TestAgentRemotingTransport(Agent, AgencyUrl); +#else + new Engine.Communication.Transports.Tcp.TestAgentTcpTransport(Agent, AgencyUrl); +#endif + + try + { + if (Agent.Start()) + WaitForStop(); + else + { + log.Error("Failed to start RemoteTestAgent"); + Environment.Exit(AgentExitCodes.FAILED_TO_START_REMOTE_AGENT); + } + } + catch (Exception ex) + { + log.Error("Exception in RemoteTestAgent. {0}", ExceptionHelper.BuildMessageAndStackTrace(ex)); + Environment.Exit(AgentExitCodes.UNEXPECTED_EXCEPTION); + } + log.Info("Agent process {0} exiting cleanly", pid); + + Environment.Exit(AgentExitCodes.OK); + } + + private static void LocateAgencyProcess(string agencyPid) + { + var agencyProcessId = int.Parse(agencyPid); + try + { + AgencyProcess = Process.GetProcessById(agencyProcessId); + } + catch (Exception e) + { + log.Error($"Unable to connect to agency process with PID: {agencyProcessId}"); + log.Error($"Failed with exception: {e.Message} {e.StackTrace}"); + Environment.Exit(AgentExitCodes.UNABLE_TO_LOCATE_AGENCY); + } + } + + private static void WaitForStop() + { + log.Debug("Waiting for stopSignal"); + + while (!Agent.WaitForStop(500)) + { + if (AgencyProcess.HasExited) + { + log.Error("Parent process has been terminated."); + Environment.Exit(AgentExitCodes.PARENT_PROCESS_TERMINATED); + } + } + + log.Debug("Stop signal received"); + } + + private static void TryLaunchDebugger() + { + if (Debugger.IsAttached) + return; + + try + { + Debugger.Launch(); + } + catch (SecurityException se) + { + if (InternalTrace.Initialized) + { + log.Error($"System.Security.Permissions.UIPermission is not set to start the debugger. {se} {se.StackTrace}"); + } + Environment.Exit(AgentExitCodes.DEBUGGER_SECURITY_VIOLATION); + } + catch (NotImplementedException nie) //Debugger is not implemented on mono + { + if (InternalTrace.Initialized) + { + log.Error($"Debugger is not available on all platforms. {nie} {nie.StackTrace}"); + } + Environment.Exit(AgentExitCodes.DEBUGGER_NOT_IMPLEMENTED); + } + } + } +} diff --git a/src/NUnitEngine/agents/nunit-agent/Properties/AssemblyInfo.cs b/src/NUnitEngine/agents/nunit-agent/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..0b3a9e8c1 --- /dev/null +++ b/src/NUnitEngine/agents/nunit-agent/Properties/AssemblyInfo.cs @@ -0,0 +1,17 @@ +using System.Reflection; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("NUnit Agent")] +[assembly: AssemblyDescription("Runs tests in a separate process when necessary")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("59b4d53d-99d7-44f2-8c6e-b83d41718dd6")] diff --git a/src/NUnitEngine/agents/nunit-agent/app.config b/src/NUnitEngine/agents/nunit-agent/app.config new file mode 100644 index 000000000..f72511441 --- /dev/null +++ b/src/NUnitEngine/agents/nunit-agent/app.config @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/NUnitEngine/agents/nunit-agent/app.manifest b/src/NUnitEngine/agents/nunit-agent/app.manifest new file mode 100644 index 000000000..589e43719 --- /dev/null +++ b/src/NUnitEngine/agents/nunit-agent/app.manifest @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/NUnitEngine/agents/nunit-agent/nunit-agent.csproj b/src/NUnitEngine/agents/nunit-agent/nunit-agent.csproj new file mode 100644 index 000000000..05903d399 --- /dev/null +++ b/src/NUnitEngine/agents/nunit-agent/nunit-agent.csproj @@ -0,0 +1,27 @@ + + + Exe + nunit.agent + net20;net40;netcoreapp3.1 + app.manifest + ..\..\..\..\nunit.ico + false + ..\..\..\..\bin\$(Configuration)\agents\ + + + + + + + + + + + nunit.ico + + + + + + + \ No newline at end of file diff --git a/src/NUnitEngine/nunit-agent-x86/Properties/AssemblyInfo.cs b/src/NUnitEngine/nunit-agent-x86/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..a23e55768 --- /dev/null +++ b/src/NUnitEngine/nunit-agent-x86/Properties/AssemblyInfo.cs @@ -0,0 +1,17 @@ +using System.Reflection; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("NUnit Agent (X86)")] +[assembly: AssemblyDescription("Runs tests in a separate process when necessary")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("59b4d53d-99d7-44f2-8c6e-b83d41718dd6")] diff --git a/src/NUnitEngine/nunit-agent-x86/app.config b/src/NUnitEngine/nunit-agent-x86/app.config new file mode 100644 index 000000000..f72511441 --- /dev/null +++ b/src/NUnitEngine/nunit-agent-x86/app.config @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/NUnitEngine/nunit-agent-x86/app.manifest b/src/NUnitEngine/nunit-agent-x86/app.manifest new file mode 100644 index 000000000..589e43719 --- /dev/null +++ b/src/NUnitEngine/nunit-agent-x86/app.manifest @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/NUnitEngine/nunit-agent/nunit-agent-x86.csproj b/src/NUnitEngine/nunit-agent-x86/nunit-agent-x86.csproj similarity index 88% rename from src/NUnitEngine/nunit-agent/nunit-agent-x86.csproj rename to src/NUnitEngine/nunit-agent-x86/nunit-agent-x86.csproj index 2a3003887..82687de16 100644 --- a/src/NUnitEngine/nunit-agent/nunit-agent-x86.csproj +++ b/src/NUnitEngine/nunit-agent-x86/nunit-agent-x86.csproj @@ -6,7 +6,6 @@ app.manifest ..\..\..\nunit.ico x86 - obj\$(Configuration)\x86\ ..\..\..\bin\$(Configuration)\agents\ False @@ -15,6 +14,8 @@ + + From eda68127ca98c2e7f5447adc48bd71e109360b0e Mon Sep 17 00:00:00 2001 From: Charlie Poole Date: Thu, 15 Apr 2021 18:00:46 -0700 Subject: [PATCH 13/24] Recognize that mono can run .Net FX assemblies --- .../nunit.engine.core/RuntimeFramework.cs | 32 +++++++++++++++---- .../RuntimeFrameworkTests.cs | 14 +++++++- .../Services/RuntimeFrameworkService.cs | 19 ++++++++++- .../nunit.engine/Services/TestAgency.cs | 12 ++++--- 4 files changed, 65 insertions(+), 12 deletions(-) diff --git a/src/NUnitEngine/nunit.engine.core/RuntimeFramework.cs b/src/NUnitEngine/nunit.engine.core/RuntimeFramework.cs index 8c60fc8fb..41137aa4c 100644 --- a/src/NUnitEngine/nunit.engine.core/RuntimeFramework.cs +++ b/src/NUnitEngine/nunit.engine.core/RuntimeFramework.cs @@ -440,9 +440,14 @@ public override string ToString() /// /// Returns true if the current framework matches the - /// one supplied as an argument. Two frameworks match - /// if their runtime types are the same or either one - /// is RuntimeType.Any and all specified version components + /// one supplied as an argument. Both the RuntimeType + /// and the version must match. + /// + /// Two RuntimeTypes match if they are equal, if either one + /// is RuntimeType.Any or if one is RuntimeType.Net and + /// the other is RuntimeType.Mono. + /// + /// Two versions match if all specified version components /// are equal. Negative (i.e. unspecified) version /// components are ignored. /// @@ -450,9 +455,7 @@ public override string ToString() /// true on match, otherwise false public bool Supports(RuntimeFramework target) { - if (this.Runtime != RuntimeType.Any - && target.Runtime != RuntimeType.Any - && this.Runtime != target.Runtime) + if (!this.Supports(target.Runtime)) return false; if (this.AllowAnyVersion || target.AllowAnyVersion) @@ -463,6 +466,23 @@ public bool Supports(RuntimeFramework target) && this.FrameworkVersion.Minor >= target.FrameworkVersion.Minor; } + private bool Supports(RuntimeType targetRuntime) + { + if (this.Runtime == targetRuntime) + return true; + + if (this.Runtime == RuntimeType.Any || targetRuntime == RuntimeType.Any) + return true; + + if (this.Runtime == RuntimeType.Net && targetRuntime == RuntimeType.Mono) + return true; + + if (this.Runtime == RuntimeType.Mono && targetRuntime == RuntimeType.Net) + return true; + + return false; + } + public bool CanLoad(IRuntimeFramework requested) { return FrameworkVersion >= requested.FrameworkVersion; diff --git a/src/NUnitEngine/nunit.engine.tests/RuntimeFrameworkTests.cs b/src/NUnitEngine/nunit.engine.tests/RuntimeFrameworkTests.cs index fd8622165..3d1b650d5 100644 --- a/src/NUnitEngine/nunit.engine.tests/RuntimeFrameworkTests.cs +++ b/src/NUnitEngine/nunit.engine.tests/RuntimeFrameworkTests.cs @@ -150,7 +150,19 @@ public bool CanLoad(RuntimeFramework f1, RuntimeFramework f2) new TestCaseData( new RuntimeFramework(RuntimeType.Net, new Version(2,0)), new RuntimeFramework(RuntimeType.Mono, new Version(2,0))) - .Returns(false), + .Returns(true), + new TestCaseData( + new RuntimeFramework(RuntimeType.Mono, new Version(2,0)), + new RuntimeFramework(RuntimeType.Net, new Version(2,0))) + .Returns(true), + new TestCaseData( + new RuntimeFramework(RuntimeType.Net, new Version(4,0)), + new RuntimeFramework(RuntimeType.Mono, new Version(4,0))) + .Returns(true), + new TestCaseData( + new RuntimeFramework(RuntimeType.Mono, new Version(4,0)), + new RuntimeFramework(RuntimeType.Net, new Version(4,0))) + .Returns(true), new TestCaseData( new RuntimeFramework(RuntimeType.Net, new Version(2,0)), new RuntimeFramework(RuntimeType.Net, new Version(1,1))) diff --git a/src/NUnitEngine/nunit.engine/Services/RuntimeFrameworkService.cs b/src/NUnitEngine/nunit.engine/Services/RuntimeFrameworkService.cs index 58b4d09a3..145590701 100644 --- a/src/NUnitEngine/nunit.engine/Services/RuntimeFrameworkService.cs +++ b/src/NUnitEngine/nunit.engine/Services/RuntimeFrameworkService.cs @@ -58,7 +58,7 @@ private static bool FrameworksMatch(RuntimeFramework f1, RuntimeFramework f2) var rt1 = f1.Runtime; var rt2 = f2.Runtime; - if (rt1 != RuntimeType.Any && rt2 != RuntimeType.Any && rt1 != rt2) + if (!RuntimesMatch(rt1, rt2)) return false; var v1 = f1.ClrVersion; @@ -75,6 +75,23 @@ private static bool FrameworksMatch(RuntimeFramework f1, RuntimeFramework f2) f1.FrameworkVersion.Minor == f2.FrameworkVersion.Minor; } + private static bool RuntimesMatch(RuntimeType rt1, RuntimeType rt2) + { + if (rt1 == rt2) + return true; + + if (rt1 == RuntimeType.Any || rt2 == RuntimeType.Any) + return true; + + if (rt1 == RuntimeType.Net && rt2 == RuntimeType.Mono) + return true; + + if (rt1 == RuntimeType.Mono && rt2 == RuntimeType.Net) + return true; + + return false; + } + /// /// Selects a target runtime framework for a TestPackage based on /// the settings in the package and the assemblies themselves. diff --git a/src/NUnitEngine/nunit.engine/Services/TestAgency.cs b/src/NUnitEngine/nunit.engine/Services/TestAgency.cs index 7231be280..88f066dfb 100644 --- a/src/NUnitEngine/nunit.engine/Services/TestAgency.cs +++ b/src/NUnitEngine/nunit.engine/Services/TestAgency.cs @@ -56,12 +56,16 @@ public ITestAgent GetAgent(TestPackage package) string runtimeSetting = package.GetSetting(EnginePackageSettings.TargetRuntimeFramework, ""); Guard.OperationValid(runtimeSetting.Length > 0, "LaunchAgentProcess called with no runtime specified"); - // If target runtime is not available, something went wrong earlier + // If target runtime is not available, something went wrong earlier. + // We list all available frameworks to use in debugging. var targetRuntime = RuntimeFramework.Parse(runtimeSetting); if (!_runtimeService.IsAvailable(targetRuntime.Id)) - throw new ArgumentException( - string.Format("The {0} framework is not available", targetRuntime), - "framework"); + { + string msg = $"The {targetRuntime} framework is not available.\r\nAvailable frameworks:"; + foreach (var runtime in RuntimeFramework.AvailableFrameworks) + msg += $" {runtime}"; + throw new ArgumentException(msg); + } var agentId = Guid.NewGuid(); var agentProcess = new AgentProcess(this, package, agentId); From dbcf2f2e0703ca73b1d9bf67640ad6878e8af5cb Mon Sep 17 00:00:00 2001 From: Charlie Poole Date: Fri, 16 Apr 2021 02:30:54 -0700 Subject: [PATCH 14/24] Fix tests to run under Mono --- .../Services/RuntimeFrameworkServiceTests.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/NUnitEngine/nunit.engine.tests/Services/RuntimeFrameworkServiceTests.cs b/src/NUnitEngine/nunit.engine.tests/Services/RuntimeFrameworkServiceTests.cs index 7ed50aa04..659c378af 100644 --- a/src/NUnitEngine/nunit.engine.tests/Services/RuntimeFrameworkServiceTests.cs +++ b/src/NUnitEngine/nunit.engine.tests/Services/RuntimeFrameworkServiceTests.cs @@ -57,9 +57,9 @@ public void AvailableFrameworks() Console.WriteLine("Available: {0}", framework.DisplayName); } - [TestCase("mono", 4, 5, "net-4.5")] - [TestCase("net", 4, 0, "net-4.5")] - [TestCase("net", 4, 5, "net-4.5")] + [TestCase("mono", 2, 0, "net-4.0")] + [TestCase("net", 2, 0, "net-4.0")] + [TestCase("net", 3, 5, "net-4.0")] public void EngineOptionPreferredOverImageTarget(string framework, int majorVersion, int minorVersion, string requested) { From 924d0cfd58c2e5846ef19662e1231460016b75e6 Mon Sep 17 00:00:00 2001 From: Charlie Poole Date: Fri, 16 Apr 2021 12:47:55 -0700 Subject: [PATCH 15/24] Delete extra copies of agent files --- .../agents/nunit-agent-x86/AgentExitCodes.cs | 16 -- .../agents/nunit-agent-x86/Program.cs | 186 ------------------ .../Properties/AssemblyInfo.cs | 17 -- .../agents/nunit-agent-x86/app.config | 41 ---- .../agents/nunit-agent-x86/app.manifest | 41 ---- .../nunit-agent-x86/nunit-agent-x86.csproj | 28 --- .../agents/nunit-agent/AgentExitCodes.cs | 16 -- src/NUnitEngine/agents/nunit-agent/Program.cs | 186 ------------------ .../nunit-agent/Properties/AssemblyInfo.cs | 17 -- src/NUnitEngine/agents/nunit-agent/app.config | 41 ---- .../agents/nunit-agent/app.manifest | 41 ---- .../agents/nunit-agent/nunit-agent.csproj | 27 --- 12 files changed, 657 deletions(-) delete mode 100644 src/NUnitEngine/agents/nunit-agent-x86/AgentExitCodes.cs delete mode 100644 src/NUnitEngine/agents/nunit-agent-x86/Program.cs delete mode 100644 src/NUnitEngine/agents/nunit-agent-x86/Properties/AssemblyInfo.cs delete mode 100644 src/NUnitEngine/agents/nunit-agent-x86/app.config delete mode 100644 src/NUnitEngine/agents/nunit-agent-x86/app.manifest delete mode 100644 src/NUnitEngine/agents/nunit-agent-x86/nunit-agent-x86.csproj delete mode 100644 src/NUnitEngine/agents/nunit-agent/AgentExitCodes.cs delete mode 100644 src/NUnitEngine/agents/nunit-agent/Program.cs delete mode 100644 src/NUnitEngine/agents/nunit-agent/Properties/AssemblyInfo.cs delete mode 100644 src/NUnitEngine/agents/nunit-agent/app.config delete mode 100644 src/NUnitEngine/agents/nunit-agent/app.manifest delete mode 100644 src/NUnitEngine/agents/nunit-agent/nunit-agent.csproj diff --git a/src/NUnitEngine/agents/nunit-agent-x86/AgentExitCodes.cs b/src/NUnitEngine/agents/nunit-agent-x86/AgentExitCodes.cs deleted file mode 100644 index c1dfc0bfb..000000000 --- a/src/NUnitEngine/agents/nunit-agent-x86/AgentExitCodes.cs +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright (c) Charlie Poole, Rob Prouse and Contributors. MIT License - see LICENSE.txt - -namespace NUnit.Common -{ - internal static class AgentExitCodes - { - public const int OK = 0; - public const int PARENT_PROCESS_TERMINATED = -1; - public const int FAILED_TO_START_REMOTE_AGENT = -2; - public const int DEBUGGER_SECURITY_VIOLATION = -3; - public const int DEBUGGER_NOT_IMPLEMENTED = -4; - public const int UNABLE_TO_LOCATE_AGENCY = -5; - public const int UNEXPECTED_EXCEPTION = -100; - public const int STACK_OVERFLOW_EXCEPTION = -1073741571; - } -} diff --git a/src/NUnitEngine/agents/nunit-agent-x86/Program.cs b/src/NUnitEngine/agents/nunit-agent-x86/Program.cs deleted file mode 100644 index 00e301e0b..000000000 --- a/src/NUnitEngine/agents/nunit-agent-x86/Program.cs +++ /dev/null @@ -1,186 +0,0 @@ -// Copyright (c) Charlie Poole, Rob Prouse and Contributors. MIT License - see LICENSE.txt - -using System; -using System.Diagnostics; -using System.IO; -using System.Security; -using NUnit.Common; -using NUnit.Engine; -using NUnit.Engine.Agents; -using NUnit.Engine.Internal; -using NUnit.Engine.Services; - -namespace NUnit.Agent -{ - public class NUnitTestAgent - { - static Guid AgentId; - static string AgencyUrl; - static Process AgencyProcess; - static RemoteTestAgent Agent; - private static Logger log; - - /// - /// The main entry point for the application. - /// - [STAThread] - public static void Main(string[] args) - { - Console.WriteLine("Agent Process Starting"); - AgentId = new Guid(args[0]); - AgencyUrl = args[1]; - - var traceLevel = InternalTraceLevel.Off; - var pid = Process.GetCurrentProcess().Id; - var debugArgPassed = false; - var workDirectory = string.Empty; - var agencyPid = string.Empty; - - for (int i = 2; i < args.Length; i++) - { - string arg = args[i]; - - // NOTE: we can test these strings exactly since - // they originate from the engine itself. - if (arg == "--debug-agent") - { - debugArgPassed = true; - } - else if (arg.StartsWith("--trace=")) - { - traceLevel = (InternalTraceLevel)Enum.Parse(typeof(InternalTraceLevel), arg.Substring(8)); - } - else if (arg.StartsWith("--pid=")) - { - agencyPid = arg.Substring(6); - } - else if (arg.StartsWith("--work=")) - { - workDirectory = arg.Substring(7); - } - } - - var logName = $"nunit-agent_{pid}.log"; - InternalTrace.Initialize(Path.Combine(workDirectory, logName), traceLevel); - log = InternalTrace.GetLogger(typeof(NUnitTestAgent)); - - log.Info("Agent process {0} starting", pid); - - if (debugArgPassed) - TryLaunchDebugger(); - - LocateAgencyProcess(agencyPid); - -#if NETFRAMEWORK - log.Info("Running under version {0}, {1}", - Environment.Version, - RuntimeFramework.CurrentFramework.DisplayName); -#endif - - // Create CoreEngine - var engine = new CoreEngine - { - WorkDirectory = workDirectory, - InternalTraceLevel = traceLevel - }; - - // Custom Service Initialization - engine.Services.Add(new ExtensionService(isRunningOnAgent: true)); -#if !NETCOREAPP - engine.Services.Add(new DomainManager()); -#endif - engine.Services.Add(new InProcessTestRunnerFactory()); - engine.Services.Add(new DriverService()); - - // Initialize Services - log.Info("Initializing Services"); - engine.InitializeServices(); - - log.Info("Starting RemoteTestAgent"); - Agent = new RemoteTestAgent(AgentId, engine.Services); - Agent.Transport = -#if NETFRAMEWORK - new Engine.Communication.Transports.Remoting.TestAgentRemotingTransport(Agent, AgencyUrl); -#else - new Engine.Communication.Transports.Tcp.TestAgentTcpTransport(Agent, AgencyUrl); -#endif - - try - { - if (Agent.Start()) - WaitForStop(); - else - { - log.Error("Failed to start RemoteTestAgent"); - Environment.Exit(AgentExitCodes.FAILED_TO_START_REMOTE_AGENT); - } - } - catch (Exception ex) - { - log.Error("Exception in RemoteTestAgent. {0}", ExceptionHelper.BuildMessageAndStackTrace(ex)); - Environment.Exit(AgentExitCodes.UNEXPECTED_EXCEPTION); - } - log.Info("Agent process {0} exiting cleanly", pid); - - Environment.Exit(AgentExitCodes.OK); - } - - private static void LocateAgencyProcess(string agencyPid) - { - var agencyProcessId = int.Parse(agencyPid); - try - { - AgencyProcess = Process.GetProcessById(agencyProcessId); - } - catch (Exception e) - { - log.Error($"Unable to connect to agency process with PID: {agencyProcessId}"); - log.Error($"Failed with exception: {e.Message} {e.StackTrace}"); - Environment.Exit(AgentExitCodes.UNABLE_TO_LOCATE_AGENCY); - } - } - - private static void WaitForStop() - { - log.Debug("Waiting for stopSignal"); - - while (!Agent.WaitForStop(500)) - { - if (AgencyProcess.HasExited) - { - log.Error("Parent process has been terminated."); - Environment.Exit(AgentExitCodes.PARENT_PROCESS_TERMINATED); - } - } - - log.Debug("Stop signal received"); - } - - private static void TryLaunchDebugger() - { - if (Debugger.IsAttached) - return; - - try - { - Debugger.Launch(); - } - catch (SecurityException se) - { - if (InternalTrace.Initialized) - { - log.Error($"System.Security.Permissions.UIPermission is not set to start the debugger. {se} {se.StackTrace}"); - } - Environment.Exit(AgentExitCodes.DEBUGGER_SECURITY_VIOLATION); - } - catch (NotImplementedException nie) //Debugger is not implemented on mono - { - if (InternalTrace.Initialized) - { - log.Error($"Debugger is not available on all platforms. {nie} {nie.StackTrace}"); - } - Environment.Exit(AgentExitCodes.DEBUGGER_NOT_IMPLEMENTED); - } - } - } -} diff --git a/src/NUnitEngine/agents/nunit-agent-x86/Properties/AssemblyInfo.cs b/src/NUnitEngine/agents/nunit-agent-x86/Properties/AssemblyInfo.cs deleted file mode 100644 index 0b3a9e8c1..000000000 --- a/src/NUnitEngine/agents/nunit-agent-x86/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System.Reflection; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("NUnit Agent")] -[assembly: AssemblyDescription("Runs tests in a separate process when necessary")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("59b4d53d-99d7-44f2-8c6e-b83d41718dd6")] diff --git a/src/NUnitEngine/agents/nunit-agent-x86/app.config b/src/NUnitEngine/agents/nunit-agent-x86/app.config deleted file mode 100644 index f72511441..000000000 --- a/src/NUnitEngine/agents/nunit-agent-x86/app.config +++ /dev/null @@ -1,41 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/NUnitEngine/agents/nunit-agent-x86/app.manifest b/src/NUnitEngine/agents/nunit-agent-x86/app.manifest deleted file mode 100644 index 589e43719..000000000 --- a/src/NUnitEngine/agents/nunit-agent-x86/app.manifest +++ /dev/null @@ -1,41 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/NUnitEngine/agents/nunit-agent-x86/nunit-agent-x86.csproj b/src/NUnitEngine/agents/nunit-agent-x86/nunit-agent-x86.csproj deleted file mode 100644 index 9deda8001..000000000 --- a/src/NUnitEngine/agents/nunit-agent-x86/nunit-agent-x86.csproj +++ /dev/null @@ -1,28 +0,0 @@ - - - Exe - nunit.agent - net20;net40 - app.manifest - ..\..\..\..\nunit.ico - x86 - ..\..\..\..\bin\$(Configuration)\agents\ - False - - - - - - - - - - - nunit.ico - - - - - - - \ No newline at end of file diff --git a/src/NUnitEngine/agents/nunit-agent/AgentExitCodes.cs b/src/NUnitEngine/agents/nunit-agent/AgentExitCodes.cs deleted file mode 100644 index c1dfc0bfb..000000000 --- a/src/NUnitEngine/agents/nunit-agent/AgentExitCodes.cs +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright (c) Charlie Poole, Rob Prouse and Contributors. MIT License - see LICENSE.txt - -namespace NUnit.Common -{ - internal static class AgentExitCodes - { - public const int OK = 0; - public const int PARENT_PROCESS_TERMINATED = -1; - public const int FAILED_TO_START_REMOTE_AGENT = -2; - public const int DEBUGGER_SECURITY_VIOLATION = -3; - public const int DEBUGGER_NOT_IMPLEMENTED = -4; - public const int UNABLE_TO_LOCATE_AGENCY = -5; - public const int UNEXPECTED_EXCEPTION = -100; - public const int STACK_OVERFLOW_EXCEPTION = -1073741571; - } -} diff --git a/src/NUnitEngine/agents/nunit-agent/Program.cs b/src/NUnitEngine/agents/nunit-agent/Program.cs deleted file mode 100644 index 00e301e0b..000000000 --- a/src/NUnitEngine/agents/nunit-agent/Program.cs +++ /dev/null @@ -1,186 +0,0 @@ -// Copyright (c) Charlie Poole, Rob Prouse and Contributors. MIT License - see LICENSE.txt - -using System; -using System.Diagnostics; -using System.IO; -using System.Security; -using NUnit.Common; -using NUnit.Engine; -using NUnit.Engine.Agents; -using NUnit.Engine.Internal; -using NUnit.Engine.Services; - -namespace NUnit.Agent -{ - public class NUnitTestAgent - { - static Guid AgentId; - static string AgencyUrl; - static Process AgencyProcess; - static RemoteTestAgent Agent; - private static Logger log; - - /// - /// The main entry point for the application. - /// - [STAThread] - public static void Main(string[] args) - { - Console.WriteLine("Agent Process Starting"); - AgentId = new Guid(args[0]); - AgencyUrl = args[1]; - - var traceLevel = InternalTraceLevel.Off; - var pid = Process.GetCurrentProcess().Id; - var debugArgPassed = false; - var workDirectory = string.Empty; - var agencyPid = string.Empty; - - for (int i = 2; i < args.Length; i++) - { - string arg = args[i]; - - // NOTE: we can test these strings exactly since - // they originate from the engine itself. - if (arg == "--debug-agent") - { - debugArgPassed = true; - } - else if (arg.StartsWith("--trace=")) - { - traceLevel = (InternalTraceLevel)Enum.Parse(typeof(InternalTraceLevel), arg.Substring(8)); - } - else if (arg.StartsWith("--pid=")) - { - agencyPid = arg.Substring(6); - } - else if (arg.StartsWith("--work=")) - { - workDirectory = arg.Substring(7); - } - } - - var logName = $"nunit-agent_{pid}.log"; - InternalTrace.Initialize(Path.Combine(workDirectory, logName), traceLevel); - log = InternalTrace.GetLogger(typeof(NUnitTestAgent)); - - log.Info("Agent process {0} starting", pid); - - if (debugArgPassed) - TryLaunchDebugger(); - - LocateAgencyProcess(agencyPid); - -#if NETFRAMEWORK - log.Info("Running under version {0}, {1}", - Environment.Version, - RuntimeFramework.CurrentFramework.DisplayName); -#endif - - // Create CoreEngine - var engine = new CoreEngine - { - WorkDirectory = workDirectory, - InternalTraceLevel = traceLevel - }; - - // Custom Service Initialization - engine.Services.Add(new ExtensionService(isRunningOnAgent: true)); -#if !NETCOREAPP - engine.Services.Add(new DomainManager()); -#endif - engine.Services.Add(new InProcessTestRunnerFactory()); - engine.Services.Add(new DriverService()); - - // Initialize Services - log.Info("Initializing Services"); - engine.InitializeServices(); - - log.Info("Starting RemoteTestAgent"); - Agent = new RemoteTestAgent(AgentId, engine.Services); - Agent.Transport = -#if NETFRAMEWORK - new Engine.Communication.Transports.Remoting.TestAgentRemotingTransport(Agent, AgencyUrl); -#else - new Engine.Communication.Transports.Tcp.TestAgentTcpTransport(Agent, AgencyUrl); -#endif - - try - { - if (Agent.Start()) - WaitForStop(); - else - { - log.Error("Failed to start RemoteTestAgent"); - Environment.Exit(AgentExitCodes.FAILED_TO_START_REMOTE_AGENT); - } - } - catch (Exception ex) - { - log.Error("Exception in RemoteTestAgent. {0}", ExceptionHelper.BuildMessageAndStackTrace(ex)); - Environment.Exit(AgentExitCodes.UNEXPECTED_EXCEPTION); - } - log.Info("Agent process {0} exiting cleanly", pid); - - Environment.Exit(AgentExitCodes.OK); - } - - private static void LocateAgencyProcess(string agencyPid) - { - var agencyProcessId = int.Parse(agencyPid); - try - { - AgencyProcess = Process.GetProcessById(agencyProcessId); - } - catch (Exception e) - { - log.Error($"Unable to connect to agency process with PID: {agencyProcessId}"); - log.Error($"Failed with exception: {e.Message} {e.StackTrace}"); - Environment.Exit(AgentExitCodes.UNABLE_TO_LOCATE_AGENCY); - } - } - - private static void WaitForStop() - { - log.Debug("Waiting for stopSignal"); - - while (!Agent.WaitForStop(500)) - { - if (AgencyProcess.HasExited) - { - log.Error("Parent process has been terminated."); - Environment.Exit(AgentExitCodes.PARENT_PROCESS_TERMINATED); - } - } - - log.Debug("Stop signal received"); - } - - private static void TryLaunchDebugger() - { - if (Debugger.IsAttached) - return; - - try - { - Debugger.Launch(); - } - catch (SecurityException se) - { - if (InternalTrace.Initialized) - { - log.Error($"System.Security.Permissions.UIPermission is not set to start the debugger. {se} {se.StackTrace}"); - } - Environment.Exit(AgentExitCodes.DEBUGGER_SECURITY_VIOLATION); - } - catch (NotImplementedException nie) //Debugger is not implemented on mono - { - if (InternalTrace.Initialized) - { - log.Error($"Debugger is not available on all platforms. {nie} {nie.StackTrace}"); - } - Environment.Exit(AgentExitCodes.DEBUGGER_NOT_IMPLEMENTED); - } - } - } -} diff --git a/src/NUnitEngine/agents/nunit-agent/Properties/AssemblyInfo.cs b/src/NUnitEngine/agents/nunit-agent/Properties/AssemblyInfo.cs deleted file mode 100644 index 0b3a9e8c1..000000000 --- a/src/NUnitEngine/agents/nunit-agent/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System.Reflection; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("NUnit Agent")] -[assembly: AssemblyDescription("Runs tests in a separate process when necessary")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("59b4d53d-99d7-44f2-8c6e-b83d41718dd6")] diff --git a/src/NUnitEngine/agents/nunit-agent/app.config b/src/NUnitEngine/agents/nunit-agent/app.config deleted file mode 100644 index f72511441..000000000 --- a/src/NUnitEngine/agents/nunit-agent/app.config +++ /dev/null @@ -1,41 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/NUnitEngine/agents/nunit-agent/app.manifest b/src/NUnitEngine/agents/nunit-agent/app.manifest deleted file mode 100644 index 589e43719..000000000 --- a/src/NUnitEngine/agents/nunit-agent/app.manifest +++ /dev/null @@ -1,41 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/NUnitEngine/agents/nunit-agent/nunit-agent.csproj b/src/NUnitEngine/agents/nunit-agent/nunit-agent.csproj deleted file mode 100644 index 05903d399..000000000 --- a/src/NUnitEngine/agents/nunit-agent/nunit-agent.csproj +++ /dev/null @@ -1,27 +0,0 @@ - - - Exe - nunit.agent - net20;net40;netcoreapp3.1 - app.manifest - ..\..\..\..\nunit.ico - false - ..\..\..\..\bin\$(Configuration)\agents\ - - - - - - - - - - - nunit.ico - - - - - - - \ No newline at end of file From 71cca2391dcec4db4c852e84820510e55608021b Mon Sep 17 00:00:00 2001 From: Charlie Poole Date: Sat, 24 Apr 2021 17:11:38 -0700 Subject: [PATCH 16/24] Fixes from code review --- package-tests.cake | 2 +- .../Properties/AssemblyInfo.cs | 2 +- src/NUnitEngine/nunit-agent/Program.cs | 12 ++++--- .../nunit-agent/Properties/AssemblyInfo.cs | 2 +- .../Communication/Messages/CommandMessage.cs | 2 -- .../Messages/CommandReturnMessage.cs | 2 -- .../Communication/Messages/ProgressMessage.cs | 2 -- .../Messages/TestEngineMessage.cs | 2 -- .../Protocols/BinarySerializationProtocol.cs | 2 -- .../Transports/ITestAgentTransport.cs | 4 +++ .../Communication/Transports/ITransport.cs | 4 +++ .../Remoting/TestAgentRemotingTransport.cs | 5 +++ .../Transports/Tcp/SocketReader.cs | 4 +++ .../Transports/Tcp/TestAgentTcpTransport.cs | 7 ++-- .../nunit.engine.core/RuntimeFramework.cs | 5 +-- .../Transports/ITestAgencyTransport.cs | 4 +++ .../Remoting/TestAgencyRemotingTransport.cs | 4 +++ .../Communication/Transports/Tcp/TcpServer.cs | 2 +- .../Transports/Tcp/TestAgencyTcpTransport.cs | 3 +- .../Services/RuntimeFrameworkService.cs | 36 ++++++++----------- 20 files changed, 61 insertions(+), 45 deletions(-) diff --git a/package-tests.cake b/package-tests.cake index 50b6cd975..383393e60 100644 --- a/package-tests.cake +++ b/package-tests.cake @@ -173,7 +173,7 @@ public abstract class NetFXPackageTester : PackageTester PackageTests.Add(new PackageTest( "netcoreapp3.1", "Run mock-assembly.dll under .NET Core 3.1", - "netcoreapp3.1/mock-assembly.dll --trace:Debug", + "netcoreapp3.1/mock-assembly.dll", new ExpectedResult("Failed") { Total = 37, diff --git a/src/NUnitEngine/nunit-agent-x86/Properties/AssemblyInfo.cs b/src/NUnitEngine/nunit-agent-x86/Properties/AssemblyInfo.cs index a23e55768..1e4e4f78d 100644 --- a/src/NUnitEngine/nunit-agent-x86/Properties/AssemblyInfo.cs +++ b/src/NUnitEngine/nunit-agent-x86/Properties/AssemblyInfo.cs @@ -5,7 +5,7 @@ // set of attributes. Change these attribute values to modify the information // associated with an assembly. [assembly: AssemblyTitle("NUnit Agent (X86)")] -[assembly: AssemblyDescription("Runs tests in a separate process when necessary")] +[assembly: AssemblyDescription("An agent to run tests in a child process created by the engine")] [assembly: AssemblyCulture("")] // Setting ComVisible to false makes the types in this assembly not visible diff --git a/src/NUnitEngine/nunit-agent/Program.cs b/src/NUnitEngine/nunit-agent/Program.cs index 00e301e0b..a1b6930b4 100644 --- a/src/NUnitEngine/nunit-agent/Program.cs +++ b/src/NUnitEngine/nunit-agent/Program.cs @@ -3,6 +3,7 @@ using System; using System.Diagnostics; using System.IO; +using System.Runtime.InteropServices; using System.Security; using NUnit.Common; using NUnit.Engine; @@ -26,7 +27,6 @@ public class NUnitTestAgent [STAThread] public static void Main(string[] args) { - Console.WriteLine("Agent Process Starting"); AgentId = new Guid(args[0]); AgencyUrl = args[1]; @@ -71,10 +71,12 @@ public static void Main(string[] args) LocateAgencyProcess(agencyPid); -#if NETFRAMEWORK - log.Info("Running under version {0}, {1}", - Environment.Version, - RuntimeFramework.CurrentFramework.DisplayName); +#if NETCOREAPP3_1 + log.Info($"Running .NET Core 3.1 agent under {RuntimeInformation.FrameworkDescription}"); +#elif NET40 + log.Info($"Running .NET 4.0 agent under {RuntimeFramework.CurrentFramework.DisplayName}"); +#elif NET20 + log.Info($"Running .NET 2.0 agent under {RuntimeFramework.CurrentFramework.DisplayName}"); #endif // Create CoreEngine diff --git a/src/NUnitEngine/nunit-agent/Properties/AssemblyInfo.cs b/src/NUnitEngine/nunit-agent/Properties/AssemblyInfo.cs index 0b3a9e8c1..40193e32c 100644 --- a/src/NUnitEngine/nunit-agent/Properties/AssemblyInfo.cs +++ b/src/NUnitEngine/nunit-agent/Properties/AssemblyInfo.cs @@ -5,7 +5,7 @@ // set of attributes. Change these attribute values to modify the information // associated with an assembly. [assembly: AssemblyTitle("NUnit Agent")] -[assembly: AssemblyDescription("Runs tests in a separate process when necessary")] +[assembly: AssemblyDescription("An agent to run tests in a child process created by the engine")] [assembly: AssemblyCulture("")] // Setting ComVisible to false makes the types in this assembly not visible diff --git a/src/NUnitEngine/nunit.engine.core/Communication/Messages/CommandMessage.cs b/src/NUnitEngine/nunit.engine.core/Communication/Messages/CommandMessage.cs index 80b5256e8..3cfd3a3ed 100644 --- a/src/NUnitEngine/nunit.engine.core/Communication/Messages/CommandMessage.cs +++ b/src/NUnitEngine/nunit.engine.core/Communication/Messages/CommandMessage.cs @@ -4,9 +4,7 @@ namespace NUnit.Engine.Communication.Messages { -#if !NETSTANDARD1_6 [Serializable] -#endif public class CommandMessage : TestEngineMessage { public CommandMessage(string commandName, params object[] arguments) diff --git a/src/NUnitEngine/nunit.engine.core/Communication/Messages/CommandReturnMessage.cs b/src/NUnitEngine/nunit.engine.core/Communication/Messages/CommandReturnMessage.cs index 89198630d..808b6400b 100644 --- a/src/NUnitEngine/nunit.engine.core/Communication/Messages/CommandReturnMessage.cs +++ b/src/NUnitEngine/nunit.engine.core/Communication/Messages/CommandReturnMessage.cs @@ -4,9 +4,7 @@ namespace NUnit.Engine.Communication.Messages { -#if !NETSTANDARD1_6 [Serializable] -#endif public class CommandReturnMessage : TestEngineMessage { public CommandReturnMessage(object returnValue) diff --git a/src/NUnitEngine/nunit.engine.core/Communication/Messages/ProgressMessage.cs b/src/NUnitEngine/nunit.engine.core/Communication/Messages/ProgressMessage.cs index 23253b1f5..6038aa792 100644 --- a/src/NUnitEngine/nunit.engine.core/Communication/Messages/ProgressMessage.cs +++ b/src/NUnitEngine/nunit.engine.core/Communication/Messages/ProgressMessage.cs @@ -4,9 +4,7 @@ namespace NUnit.Engine.Communication.Messages { -#if !NETSTANDARD1_6 [Serializable] -#endif public class ProgressMessage : TestEngineMessage { public ProgressMessage(string report) diff --git a/src/NUnitEngine/nunit.engine.core/Communication/Messages/TestEngineMessage.cs b/src/NUnitEngine/nunit.engine.core/Communication/Messages/TestEngineMessage.cs index 7b720ec65..16478eab1 100644 --- a/src/NUnitEngine/nunit.engine.core/Communication/Messages/TestEngineMessage.cs +++ b/src/NUnitEngine/nunit.engine.core/Communication/Messages/TestEngineMessage.cs @@ -4,9 +4,7 @@ namespace NUnit.Engine.Communication.Messages { -#if !NETSTANDARD1_6 [Serializable] -#endif public abstract class TestEngineMessage { } diff --git a/src/NUnitEngine/nunit.engine.core/Communication/Protocols/BinarySerializationProtocol.cs b/src/NUnitEngine/nunit.engine.core/Communication/Protocols/BinarySerializationProtocol.cs index 76505ea0a..5995853af 100644 --- a/src/NUnitEngine/nunit.engine.core/Communication/Protocols/BinarySerializationProtocol.cs +++ b/src/NUnitEngine/nunit.engine.core/Communication/Protocols/BinarySerializationProtocol.cs @@ -1,6 +1,5 @@ // Copyright (c) Charlie Poole, Rob Prouse and Contributors. MIT License - see LICENSE.txt -#if !NETSTANDARD1_6 using System; using System.Collections.Generic; using System.IO; @@ -246,4 +245,3 @@ private static byte[] ReadByteArray(Stream stream, int length) } } } -#endif diff --git a/src/NUnitEngine/nunit.engine.core/Communication/Transports/ITestAgentTransport.cs b/src/NUnitEngine/nunit.engine.core/Communication/Transports/ITestAgentTransport.cs index 31c6ee976..78d409fac 100644 --- a/src/NUnitEngine/nunit.engine.core/Communication/Transports/ITestAgentTransport.cs +++ b/src/NUnitEngine/nunit.engine.core/Communication/Transports/ITestAgentTransport.cs @@ -4,6 +4,10 @@ namespace NUnit.Engine.Communication.Transports { + /// + /// The ITestAgentTransport interface is implemented by a + /// class providing communication for a TestAgent. + /// public interface ITestAgentTransport : ITransport { TestAgent Agent { get; } diff --git a/src/NUnitEngine/nunit.engine.core/Communication/Transports/ITransport.cs b/src/NUnitEngine/nunit.engine.core/Communication/Transports/ITransport.cs index aebc1dac0..498fa582d 100644 --- a/src/NUnitEngine/nunit.engine.core/Communication/Transports/ITransport.cs +++ b/src/NUnitEngine/nunit.engine.core/Communication/Transports/ITransport.cs @@ -2,6 +2,10 @@ namespace NUnit.Engine.Communication.Transports { + /// + /// The ITransport interface is implemented by a class + /// providing a communication interface for another class. + /// public interface ITransport { bool Start(); diff --git a/src/NUnitEngine/nunit.engine.core/Communication/Transports/Remoting/TestAgentRemotingTransport.cs b/src/NUnitEngine/nunit.engine.core/Communication/Transports/Remoting/TestAgentRemotingTransport.cs index a609353f4..ac8486355 100644 --- a/src/NUnitEngine/nunit.engine.core/Communication/Transports/Remoting/TestAgentRemotingTransport.cs +++ b/src/NUnitEngine/nunit.engine.core/Communication/Transports/Remoting/TestAgentRemotingTransport.cs @@ -12,6 +12,11 @@ namespace NUnit.Engine.Communication.Transports.Remoting { + /// + /// TestAgentRemotingTransport uses remoting to support + /// a TestAgent in communicating with a TestAgency and + /// with the runners that make use of it. + /// public class TestAgentRemotingTransport : MarshalByRefObject, ITestAgentTransport, ITestAgent, ITestEngineRunner { private static readonly Logger log = InternalTrace.GetLogger(typeof(TestAgentRemotingTransport)); diff --git a/src/NUnitEngine/nunit.engine.core/Communication/Transports/Tcp/SocketReader.cs b/src/NUnitEngine/nunit.engine.core/Communication/Transports/Tcp/SocketReader.cs index a34230429..60223956e 100644 --- a/src/NUnitEngine/nunit.engine.core/Communication/Transports/Tcp/SocketReader.cs +++ b/src/NUnitEngine/nunit.engine.core/Communication/Transports/Tcp/SocketReader.cs @@ -8,6 +8,10 @@ namespace NUnit.Engine.Communication.Transports.Tcp { + /// + /// SocketReader reads a socket and composes Messages + /// for consumption using a specific wire protocol. + /// public class SocketReader { private const int BUFFER_SIZE = 1024; diff --git a/src/NUnitEngine/nunit.engine.core/Communication/Transports/Tcp/TestAgentTcpTransport.cs b/src/NUnitEngine/nunit.engine.core/Communication/Transports/Tcp/TestAgentTcpTransport.cs index 8eed0738e..bd8f913e3 100644 --- a/src/NUnitEngine/nunit.engine.core/Communication/Transports/Tcp/TestAgentTcpTransport.cs +++ b/src/NUnitEngine/nunit.engine.core/Communication/Transports/Tcp/TestAgentTcpTransport.cs @@ -1,6 +1,5 @@ // Copyright (c) Charlie Poole, Rob Prouse and Contributors. MIT License - see LICENSE.txt -#if !NETSTANDARD1_6 using System.Net; using System.Net.Sockets; using System.Threading; @@ -12,6 +11,11 @@ namespace NUnit.Engine.Communication.Transports.Tcp { + /// + /// TestAgentRemotingTransport uses TCP to support + /// a TestAgent in communicating with a TestAgency and + /// with the runners that make use of it. + /// public class TestAgentTcpTransport : ITestAgentTransport, ITestEventListener { private static readonly Logger log = InternalTrace.GetLogger(typeof(TestAgentTcpTransport)); @@ -131,4 +135,3 @@ public void OnTestEvent(string report) } } } -#endif diff --git a/src/NUnitEngine/nunit.engine.core/RuntimeFramework.cs b/src/NUnitEngine/nunit.engine.core/RuntimeFramework.cs index 41137aa4c..413d478da 100644 --- a/src/NUnitEngine/nunit.engine.core/RuntimeFramework.cs +++ b/src/NUnitEngine/nunit.engine.core/RuntimeFramework.cs @@ -143,6 +143,7 @@ private Version GetClrVersionForFramework(Version frameworkVersion) { case 1: case 2: + // For pre-3.0 versions of .NET Core, Environment.Version returns 4.0.30319.42000 return new Version(4, 0, 30319); case 3: return new Version(3, 1, 10); @@ -698,7 +699,7 @@ private static void FindDotNetCoreFrameworks() if (!File.Exists(Path.Combine(INSTALL_DIR, "dotnet.exe"))) return; - string runtimeDir = Path.Combine(INSTALL_DIR, "shared\\Microsoft.NETCore.App"); + string runtimeDir = Path.Combine(INSTALL_DIR, Path.Combine("shared", "Microsoft.NETCore.App")); if (!Directory.Exists(runtimeDir)) return; @@ -711,7 +712,7 @@ private static void FindDotNetCoreFrameworks() _availableFrameworks.AddRange(runtimes); } - // Internal for testing + // Deal with oddly named directories, which may sometimes appear when previews are installed internal static IList GetNetCoreRuntimesFromDirectoryNames(IEnumerable dirNames) { const string VERSION_CHARS = ".0123456789"; diff --git a/src/NUnitEngine/nunit.engine/Communication/Transports/ITestAgencyTransport.cs b/src/NUnitEngine/nunit.engine/Communication/Transports/ITestAgencyTransport.cs index 44072c9b9..5903f79ba 100644 --- a/src/NUnitEngine/nunit.engine/Communication/Transports/ITestAgencyTransport.cs +++ b/src/NUnitEngine/nunit.engine/Communication/Transports/ITestAgencyTransport.cs @@ -2,6 +2,10 @@ namespace NUnit.Engine.Communication.Transports { + /// + /// The ITestAgencyTransport interface is implemented by a + /// class providing communication for a TestAgency. + /// public interface ITestAgencyTransport : ITransport { string ServerUrl { get; } diff --git a/src/NUnitEngine/nunit.engine/Communication/Transports/Remoting/TestAgencyRemotingTransport.cs b/src/NUnitEngine/nunit.engine/Communication/Transports/Remoting/TestAgencyRemotingTransport.cs index e326c9bb2..c9d24b877 100644 --- a/src/NUnitEngine/nunit.engine/Communication/Transports/Remoting/TestAgencyRemotingTransport.cs +++ b/src/NUnitEngine/nunit.engine/Communication/Transports/Remoting/TestAgencyRemotingTransport.cs @@ -15,6 +15,10 @@ namespace NUnit.Engine.Communication.Transports.Remoting { + /// + /// TestAgencyRemotingTransport uses the remoting to connect a + /// TestAgency with its agents. + /// public class TestAgencyRemotingTransport : MarshalByRefObject, ITestAgencyTransport, ITestAgency, IDisposable { private static readonly Logger log = InternalTrace.GetLogger(typeof(TestAgencyRemotingTransport)); diff --git a/src/NUnitEngine/nunit.engine/Communication/Transports/Tcp/TcpServer.cs b/src/NUnitEngine/nunit.engine/Communication/Transports/Tcp/TcpServer.cs index 1b8ae21bc..359704296 100644 --- a/src/NUnitEngine/nunit.engine/Communication/Transports/Tcp/TcpServer.cs +++ b/src/NUnitEngine/nunit.engine/Communication/Transports/Tcp/TcpServer.cs @@ -10,7 +10,7 @@ namespace NUnit.Engine.Communication.Transports.Tcp { public class TcpServer { - private static readonly Logger log = InternalTrace.GetLogger(typeof(TestAgentTcpTransport)); + private static readonly Logger log = InternalTrace.GetLogger(typeof(TcpServer)); private const int GUID_BUFFER_SIZE = 16; diff --git a/src/NUnitEngine/nunit.engine/Communication/Transports/Tcp/TestAgencyTcpTransport.cs b/src/NUnitEngine/nunit.engine/Communication/Transports/Tcp/TestAgencyTcpTransport.cs index 31fb06758..acf808975 100644 --- a/src/NUnitEngine/nunit.engine/Communication/Transports/Tcp/TestAgencyTcpTransport.cs +++ b/src/NUnitEngine/nunit.engine/Communication/Transports/Tcp/TestAgencyTcpTransport.cs @@ -9,7 +9,8 @@ namespace NUnit.Engine.Communication.Transports.Tcp { /// - /// Summary description for TestAgencyTcpTransport. + /// TestAgencyTcpTransport uses the TCP protocol to connect a + /// TestAgency with its agents. /// public class TestAgencyTcpTransport : ITestAgencyTransport, ITestAgency, IDisposable { diff --git a/src/NUnitEngine/nunit.engine/Services/RuntimeFrameworkService.cs b/src/NUnitEngine/nunit.engine/Services/RuntimeFrameworkService.cs index 145590701..0dea80d11 100644 --- a/src/NUnitEngine/nunit.engine/Services/RuntimeFrameworkService.cs +++ b/src/NUnitEngine/nunit.engine/Services/RuntimeFrameworkService.cs @@ -53,40 +53,34 @@ public bool IsAvailable(string name) private static readonly Version AnyVersion = new Version(0, 0); - private static bool FrameworksMatch(RuntimeFramework f1, RuntimeFramework f2) + private static bool FrameworksMatch(RuntimeFramework requested, RuntimeFramework available) { - var rt1 = f1.Runtime; - var rt2 = f2.Runtime; - - if (!RuntimesMatch(rt1, rt2)) + if (!RuntimesMatch(requested.Runtime, available.Runtime)) return false; - var v1 = f1.ClrVersion; - var v2 = f2.ClrVersion; + var requestedVersion = requested.FrameworkVersion; + var availableVersion = available.FrameworkVersion; - if (v1 == AnyVersion || v2 == AnyVersion) + if (requestedVersion == AnyVersion) return true; - return v1.Major == v2.Major && - v1.Minor == v2.Minor && - (v1.Build < 0 || v2.Build < 0 || v1.Build == v2.Build) && - (v1.Revision < 0 || v2.Revision < 0 || v1.Revision == v2.Revision) && - f1.FrameworkVersion.Major == f2.FrameworkVersion.Major && - f1.FrameworkVersion.Minor == f2.FrameworkVersion.Minor; + return requestedVersion.Major == availableVersion.Major && + requestedVersion.Minor == availableVersion.Minor && + (requestedVersion.Build < 0 || availableVersion.Build < 0 || requestedVersion.Build == availableVersion.Build) && + (requestedVersion.Revision < 0 || availableVersion.Revision < 0 || requestedVersion.Revision == availableVersion.Revision) && + requestedVersion.Major == availableVersion.Major && + requestedVersion.Minor == availableVersion.Minor; } - private static bool RuntimesMatch(RuntimeType rt1, RuntimeType rt2) + private static bool RuntimesMatch(RuntimeType requested, RuntimeType available) { - if (rt1 == rt2) - return true; - - if (rt1 == RuntimeType.Any || rt2 == RuntimeType.Any) + if (requested == available || requested == RuntimeType.Any) return true; - if (rt1 == RuntimeType.Net && rt2 == RuntimeType.Mono) + if (requested == RuntimeType.Net && available == RuntimeType.Mono) return true; - if (rt1 == RuntimeType.Mono && rt2 == RuntimeType.Net) + if (requested == RuntimeType.Mono && available == RuntimeType.Net) return true; return false; From ebf731c25d1395fb684bf1725fb55714d6709a7c Mon Sep 17 00:00:00 2001 From: Charlie Poole Date: Thu, 29 Apr 2021 07:05:44 -0700 Subject: [PATCH 17/24] Fix TcpAgentTcpProxy.Dispose() method --- .../Communication/Transports/Tcp/TestAgentTcpProxy.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/NUnitEngine/nunit.engine/Communication/Transports/Tcp/TestAgentTcpProxy.cs b/src/NUnitEngine/nunit.engine/Communication/Transports/Tcp/TestAgentTcpProxy.cs index c4c12c286..efb1862d8 100644 --- a/src/NUnitEngine/nunit.engine/Communication/Transports/Tcp/TestAgentTcpProxy.cs +++ b/src/NUnitEngine/nunit.engine/Communication/Transports/Tcp/TestAgentTcpProxy.cs @@ -94,7 +94,6 @@ public TestEngineResult Explore(TestFilter filter) public void Dispose() { - throw new NotImplementedException(); } private void SendCommandMessage(string command, params object[] arguments) From 15ccfd47266f79993c11ff45199a2ca098d8cb71 Mon Sep 17 00:00:00 2001 From: Charlie Poole Date: Thu, 29 Apr 2021 11:31:18 -0700 Subject: [PATCH 18/24] Add mock-assembly-x86 assembly and package tests to verify running it in netfx --- NUnitConsole.sln | 7 +++++ package-tests.cake | 30 ++++++++++++++++++- .../Properties/AssemblyInfo.cs | 10 +++++++ .../mock-assembly-x86.csproj | 21 +++++++++++++ 4 files changed, 67 insertions(+), 1 deletion(-) create mode 100644 src/NUnitEngine/mock-assembly-x86/Properties/AssemblyInfo.cs create mode 100644 src/NUnitEngine/mock-assembly-x86/mock-assembly-x86.csproj diff --git a/NUnitConsole.sln b/NUnitConsole.sln index d18fc5037..55e99ba8e 100644 --- a/NUnitConsole.sln +++ b/NUnitConsole.sln @@ -99,6 +99,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "nunit.engine.core", "src\NU EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "nunit-agent-x86", "src\NUnitEngine\nunit-agent-x86\nunit-agent-x86.csproj", "{333D2FBC-CCA7-46AF-9453-C310671A67B0}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "mock-assembly-x86", "src\NUnitEngine\mock-assembly-x86\mock-assembly-x86.csproj", "{9D3015EE-5B84-41B3-A1D3-1A439370C392}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -145,6 +147,10 @@ Global {333D2FBC-CCA7-46AF-9453-C310671A67B0}.Debug|Any CPU.Build.0 = Debug|Any CPU {333D2FBC-CCA7-46AF-9453-C310671A67B0}.Release|Any CPU.ActiveCfg = Release|Any CPU {333D2FBC-CCA7-46AF-9453-C310671A67B0}.Release|Any CPU.Build.0 = Release|Any CPU + {9D3015EE-5B84-41B3-A1D3-1A439370C392}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9D3015EE-5B84-41B3-A1D3-1A439370C392}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9D3015EE-5B84-41B3-A1D3-1A439370C392}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9D3015EE-5B84-41B3-A1D3-1A439370C392}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -165,6 +171,7 @@ Global {9A7C8370-ED1F-486F-A8F5-C5BF4221464E} = {A972031D-2F61-4183-AF75-99EE1A9F6B32} {A19C026B-1C0F-4AA3-AC49-7D8B4C7231CF} = {31B45C4C-206F-4F31-9CC6-33BF11DFEE39} {333D2FBC-CCA7-46AF-9453-C310671A67B0} = {31B45C4C-206F-4F31-9CC6-33BF11DFEE39} + {9D3015EE-5B84-41B3-A1D3-1A439370C392} = {31B45C4C-206F-4F31-9CC6-33BF11DFEE39} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {D8E4FC26-5422-4C51-8BBC-D1AC0A578711} diff --git a/package-tests.cake b/package-tests.cake index 383393e60..712cc701a 100644 --- a/package-tests.cake +++ b/package-tests.cake @@ -118,7 +118,7 @@ public abstract class PackageTester } // These are tests using the .NET Framework build of the console runner. -// However, they now include running tests under .Net Core 3.1. +// However, they now include running tests under .Net Core. public abstract class NetFXPackageTester : PackageTester { public NetFXPackageTester(ICakeContext context, string packageVersion) @@ -139,6 +139,20 @@ public abstract class NetFXPackageTester : PackageTester Skipped = 7 })); + PackageTests.Add(new PackageTest( + "net35-x86", + "Run mock-assembly-x86.dll under .NET 3.5", + "net35/mock-assembly-x86.dll", + new ExpectedResult("Failed") + { + Total = 37, + Passed = 23, + Failed = 5, + Warnings = 0, + Inconclusive = 1, + Skipped = 7 + })); + PackageTests.Add(new PackageTest( "net40", "Run mock-assembly.dll under .NET 4.x", @@ -153,6 +167,20 @@ public abstract class NetFXPackageTester : PackageTester Skipped = 7 })); + PackageTests.Add(new PackageTest( + "net40-x86", + "Run mock-assembly-x86.dll under .NET 4.x", + "net40/mock-assembly-x86.dll", + new ExpectedResult("Failed") + { + Total = 37, + Passed = 23, + Failed = 5, + Warnings = 0, + Inconclusive = 1, + Skipped = 7 + })); + PackageTests.Add(new PackageTest( "net35_plus_net40", "Run both copies of mock-assembly together", diff --git a/src/NUnitEngine/mock-assembly-x86/Properties/AssemblyInfo.cs b/src/NUnitEngine/mock-assembly-x86/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..530475516 --- /dev/null +++ b/src/NUnitEngine/mock-assembly-x86/Properties/AssemblyInfo.cs @@ -0,0 +1,10 @@ +// Copyright (c) Charlie Poole, Rob Prouse and Contributors. MIT License - see LICENSE.txt + +using System.Reflection; + +// Information about this assembly is defined by the following attributes. +// Change them to the _values specific to your project. + +[assembly: AssemblyTitle("mock-assembly-x86")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyCulture("")] \ No newline at end of file diff --git a/src/NUnitEngine/mock-assembly-x86/mock-assembly-x86.csproj b/src/NUnitEngine/mock-assembly-x86/mock-assembly-x86.csproj new file mode 100644 index 000000000..cd1b1d801 --- /dev/null +++ b/src/NUnitEngine/mock-assembly-x86/mock-assembly-x86.csproj @@ -0,0 +1,21 @@ + + + NUnit.Tests + net35;net40;netcoreapp2.1;netcoreapp3.1 + true + ..\..\nunit.snk + x86 + + + + + + + + + + + + + + \ No newline at end of file From 714ada11990330d4d54e3fdcae38cd4e7b8b3480 Mon Sep 17 00:00:00 2001 From: Charlie Poole Date: Thu, 29 Apr 2021 17:31:38 -0700 Subject: [PATCH 19/24] Make mock-assembly-x86.dll run under .NET Core --- package-tests.cake | 42 +++++++++++++------ .../nunit.engine/Services/AgentProcess.cs | 11 ++--- 2 files changed, 35 insertions(+), 18 deletions(-) diff --git a/package-tests.cake b/package-tests.cake index 712cc701a..cea44ba37 100644 --- a/package-tests.cake +++ b/package-tests.cake @@ -198,19 +198,35 @@ public abstract class NetFXPackageTester : PackageTester // TODO: Remove this check when msi package is // updated to include .net core assemblies. if (GetType().Name != "MsiPackageTester") - PackageTests.Add(new PackageTest( - "netcoreapp3.1", - "Run mock-assembly.dll under .NET Core 3.1", - "netcoreapp3.1/mock-assembly.dll", - new ExpectedResult("Failed") - { - Total = 37, - Passed = 23, - Failed = 5, - Warnings = 0, - Inconclusive = 1, - Skipped = 7 - })); + { + PackageTests.Add(new PackageTest( + "netcoreapp3.1", + "Run mock-assembly.dll under .NET Core 3.1", + "netcoreapp3.1/mock-assembly.dll", + new ExpectedResult("Failed") + { + Total = 37, + Passed = 23, + Failed = 5, + Warnings = 0, + Inconclusive = 1, + Skipped = 7 + })); + + PackageTests.Add(new PackageTest( + "netcoreapp3.1-x86", + "Run mock-assembly-x86.dll under .NET Core 3.1", + "netcoreapp3.1/mock-assembly-x86.dll", + new ExpectedResult("Failed") + { + Total = 37, + Passed = 23, + Failed = 5, + Warnings = 0, + Inconclusive = 1, + Skipped = 7 + })); + } } } diff --git a/src/NUnitEngine/nunit.engine/Services/AgentProcess.cs b/src/NUnitEngine/nunit.engine/Services/AgentProcess.cs index acf9b5936..3f10c44f2 100644 --- a/src/NUnitEngine/nunit.engine/Services/AgentProcess.cs +++ b/src/NUnitEngine/nunit.engine/Services/AgentProcess.cs @@ -63,7 +63,9 @@ public AgentProcess(TestAgency agency, TestPackage package, Guid agentId) } else if (TargetRuntime.Runtime == RuntimeType.NetCore) { - StartInfo.FileName = "dotnet"; + StartInfo.FileName = runAsX86 + ? @"C:\Program Files (x86)\dotnet\dotnet.exe" + : "dotnet"; StartInfo.Arguments = $"{AgentExePath} {AgentArgs}"; StartInfo.LoadUserProfile = loadUserProfile; } @@ -101,21 +103,20 @@ public static string GetTestAgentExePath(RuntimeFramework targetRuntime, bool re log.Debug($"Checking for agents at {agentsDir}"); - string agentName = requires32Bit - ? "nunit-agent-x86" - : "nunit-agent"; - string runtimeDir; + string agentName; string agentExtension; switch (targetRuntime.Runtime) { case RuntimeType.Net: case RuntimeType.Mono: runtimeDir = targetRuntime.FrameworkVersion.Major >= 4 ? "net40" : "net20"; + agentName = requires32Bit ? "nunit-agent-x86" : "nunit-agent"; agentExtension = ".exe"; break; case RuntimeType.NetCore: runtimeDir = "netcoreapp3.1"; + agentName = "nunit-agent"; agentExtension = ".dll"; break; default: From 3e3b43d9827d33e7d076fb6e6a42b1ae26501343 Mon Sep 17 00:00:00 2001 From: Charlie Poole Date: Thu, 29 Apr 2021 21:34:13 -0700 Subject: [PATCH 20/24] Give error if x86 version of dotnet is needed but not available --- .../nunit.engine/Services/AgentProcess.cs | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/src/NUnitEngine/nunit.engine/Services/AgentProcess.cs b/src/NUnitEngine/nunit.engine/Services/AgentProcess.cs index 3f10c44f2..0ee7e8e3a 100644 --- a/src/NUnitEngine/nunit.engine/Services/AgentProcess.cs +++ b/src/NUnitEngine/nunit.engine/Services/AgentProcess.cs @@ -63,11 +63,22 @@ public AgentProcess(TestAgency agency, TestPackage package, Guid agentId) } else if (TargetRuntime.Runtime == RuntimeType.NetCore) { - StartInfo.FileName = runAsX86 - ? @"C:\Program Files (x86)\dotnet\dotnet.exe" - : "dotnet"; + StartInfo.FileName = "dotnet"; StartInfo.Arguments = $"{AgentExePath} {AgentArgs}"; StartInfo.LoadUserProfile = loadUserProfile; + + // TODO: Remove the windows limitation and the use of a hard-coded path. + if (runAsX86) + { + if (Path.DirectorySeparatorChar != '\\') + throw new Exception("Running .NET Core as X86 is currently only supported on Windows"); + + var x86_dotnet_exe = @"C:\Program Files (x86)\dotnet\dotnet.exe"; + if (!File.Exists(x86_dotnet_exe)) + throw new Exception("The X86 version of dotnet.exe is not installed"); + + StartInfo.FileName = x86_dotnet_exe; + } } else { @@ -78,7 +89,7 @@ public AgentProcess(TestAgency agency, TestPackage package, Guid agentId) // Internal properties exposed for testing - internal RuntimeFramework TargetRuntime { get; } + internal RuntimeFramework TargetRuntime { get; } internal string AgentExePath { get; } internal StringBuilder AgentArgs { get; } From 7963ff27db2d30b88526f83383103b86c953a985 Mon Sep 17 00:00:00 2001 From: Charlie Poole Date: Thu, 29 Apr 2021 22:15:41 -0700 Subject: [PATCH 21/24] Skip running X86 .NET Core tests where not installed --- package-tests.cake | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/package-tests.cake b/package-tests.cake index cea44ba37..284064e0f 100644 --- a/package-tests.cake +++ b/package-tests.cake @@ -121,9 +121,13 @@ public abstract class PackageTester // However, they now include running tests under .Net Core. public abstract class NetFXPackageTester : PackageTester { + static readonly string DOTNET_EXE_X86 = @"C:\Program Files (x86)\dotnet\dotnet.exe"; + public NetFXPackageTester(ICakeContext context, string packageVersion) : base(context, packageVersion) { + bool dotnetX86Available = _context.IsRunningOnWindows() && System.IO.File.Exists(DOTNET_EXE_X86); + // Add common tests for running under .NET Framework PackageTests.Add(new PackageTest( "net35", @@ -213,6 +217,7 @@ public abstract class NetFXPackageTester : PackageTester Skipped = 7 })); + if (dotnetX86Available) PackageTests.Add(new PackageTest( "netcoreapp3.1-x86", "Run mock-assembly-x86.dll under .NET Core 3.1", From 154866e1db065af3b1ffff38353f10c98dddfc3c Mon Sep 17 00:00:00 2001 From: Charlie Poole Date: Fri, 30 Apr 2021 21:51:04 -0700 Subject: [PATCH 22/24] Add .NET Core 3.1 agent to msi --- msi/nunit/engine-files.wxi | 30 +++++++++++++++++ msi/nunit/runner-directories.wxi | 1 + package-tests.cake | 33 ++++++++----------- .../nunit3-console.tests/App.config | 6 ++++ .../nunit.engine.tests.csproj | 3 ++ 5 files changed, 54 insertions(+), 19 deletions(-) create mode 100644 src/NUnitConsole/nunit3-console.tests/App.config diff --git a/msi/nunit/engine-files.wxi b/msi/nunit/engine-files.wxi index a75fc15fd..5ff52e16b 100644 --- a/msi/nunit/engine-files.wxi +++ b/msi/nunit/engine-files.wxi @@ -46,6 +46,7 @@ + @@ -87,5 +88,34 @@ + + + + + + + + + + + + + + + + + + + diff --git a/msi/nunit/runner-directories.wxi b/msi/nunit/runner-directories.wxi index ef157572b..605ed50a2 100644 --- a/msi/nunit/runner-directories.wxi +++ b/msi/nunit/runner-directories.wxi @@ -13,6 +13,7 @@ + diff --git a/package-tests.cake b/package-tests.cake index 284064e0f..402efd331 100644 --- a/package-tests.cake +++ b/package-tests.cake @@ -199,25 +199,21 @@ public abstract class NetFXPackageTester : PackageTester Skipped = 2 * 7 })); - // TODO: Remove this check when msi package is - // updated to include .net core assemblies. - if (GetType().Name != "MsiPackageTester") - { - PackageTests.Add(new PackageTest( - "netcoreapp3.1", - "Run mock-assembly.dll under .NET Core 3.1", - "netcoreapp3.1/mock-assembly.dll", - new ExpectedResult("Failed") - { - Total = 37, - Passed = 23, - Failed = 5, - Warnings = 0, - Inconclusive = 1, - Skipped = 7 - })); + PackageTests.Add(new PackageTest( + "netcoreapp3.1", + "Run mock-assembly.dll under .NET Core 3.1", + "netcoreapp3.1/mock-assembly.dll", + new ExpectedResult("Failed") + { + Total = 37, + Passed = 23, + Failed = 5, + Warnings = 0, + Inconclusive = 1, + Skipped = 7 + })); - if (dotnetX86Available) + if (dotnetX86Available) PackageTests.Add(new PackageTest( "netcoreapp3.1-x86", "Run mock-assembly-x86.dll under .NET Core 3.1", @@ -231,7 +227,6 @@ public abstract class NetFXPackageTester : PackageTester Inconclusive = 1, Skipped = 7 })); - } } } diff --git a/src/NUnitConsole/nunit3-console.tests/App.config b/src/NUnitConsole/nunit3-console.tests/App.config new file mode 100644 index 000000000..1f86e4c2b --- /dev/null +++ b/src/NUnitConsole/nunit3-console.tests/App.config @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/NUnitEngine/nunit.engine.tests/nunit.engine.tests.csproj b/src/NUnitEngine/nunit.engine.tests/nunit.engine.tests.csproj index 39fa15f1c..faef8b0fb 100644 --- a/src/NUnitEngine/nunit.engine.tests/nunit.engine.tests.csproj +++ b/src/NUnitEngine/nunit.engine.tests/nunit.engine.tests.csproj @@ -14,6 +14,9 @@ + + + From 116d081a114c96f47a1ad94361569c54aafc1e71 Mon Sep 17 00:00:00 2001 From: Charlie Poole Date: Tue, 4 May 2021 09:08:59 -0700 Subject: [PATCH 23/24] Changes arising from last review --- build.cake | 1 + msi/nunit/engine-files.wxi | 5 ++--- nuget/engine/nunit.engine.nuspec | 6 ++---- nuget/runners/nunit.console-runner.nuspec | 2 +- package-checks.cake | 2 -- src/NUnitEngine/mock-assembly-x86/mock-assembly-x86.csproj | 3 +-- src/NUnitEngine/nunit.engine.core/nunit.engine.core.csproj | 4 ---- .../nunit.engine.tests/nunit.engine.tests.csproj | 3 --- .../nunit.engine/Services/RuntimeFrameworkService.cs | 6 ++---- 9 files changed, 9 insertions(+), 23 deletions(-) diff --git a/build.cake b/build.cake index c4270ec6e..ac843c9a7 100644 --- a/build.cake +++ b/build.cake @@ -486,6 +486,7 @@ Task("BuildChocolateyPackages") new ChocolateyNuSpecContent { Source = CURRENT_IMG_DIR + "bin/agents/net40/testcentric.engine.metadata.dll", Target="tools/agents/net40" }, new ChocolateyNuSpecContent { Source = CURRENT_IMG_DIR + "bin/agents/netcoreapp3.1/nunit-agent.dll", Target="tools/agents/netcoreapp3.1" }, new ChocolateyNuSpecContent { Source = CURRENT_IMG_DIR + "bin/agents/netcoreapp3.1/nunit-agent.dll.config", Target="tools/agents/netcoreapp3.1" }, + new ChocolateyNuSpecContent { Source = CURRENT_IMG_DIR + "bin/agents/netcoreapp3.1/nunit-agent.runtimeconfig.dev.json", Target="tools/agents/netcoreapp3.1" }, new ChocolateyNuSpecContent { Source = CURRENT_IMG_DIR + "bin/agents/netcoreapp3.1/nunit-agent.runtimeconfig.json", Target="tools/agents/netcoreapp3.1" }, new ChocolateyNuSpecContent { Source = CHOCO_DIR + "nunit-agent.exe.ignore", Target="tools/agents/netcoreapp3.1" }, new ChocolateyNuSpecContent { Source = CURRENT_IMG_DIR + "bin/agents/netcoreapp3.1/nunit.engine.api.dll", Target="tools/agents/netcoreapp3.1" }, diff --git a/msi/nunit/engine-files.wxi b/msi/nunit/engine-files.wxi index 5ff52e16b..72b0d45b8 100644 --- a/msi/nunit/engine-files.wxi +++ b/msi/nunit/engine-files.wxi @@ -93,6 +93,8 @@ + @@ -113,9 +115,6 @@ ProcessorArchitecture="msil" Source="$(var.InstallImage)bin/netcoreapp3.1/testcentric.engine.metadata.dll" /> - - - diff --git a/nuget/engine/nunit.engine.nuspec b/nuget/engine/nunit.engine.nuspec index 2efdcb48a..60ea6d54a 100644 --- a/nuget/engine/nunit.engine.nuspec +++ b/nuget/engine/nunit.engine.nuspec @@ -58,7 +58,8 @@ - + + @@ -66,19 +67,16 @@ - - - diff --git a/nuget/runners/nunit.console-runner.nuspec b/nuget/runners/nunit.console-runner.nuspec index 361e514ec..007829f8e 100644 --- a/nuget/runners/nunit.console-runner.nuspec +++ b/nuget/runners/nunit.console-runner.nuspec @@ -48,12 +48,12 @@ + - diff --git a/package-checks.cake b/package-checks.cake index ff531792d..049b5a151 100644 --- a/package-checks.cake +++ b/package-checks.cake @@ -35,8 +35,6 @@ public void CheckAllPackages() HasDirectory("lib/netstandard2.0").WithFiles(ENGINE_FILES), HasDirectory("lib/netcoreapp3.1").WithFiles(ENGINE_FILES), HasDirectory("contentFiles/any/lib/net20").WithFile("nunit.engine.nuget.addins"), - HasDirectory("contentFiles/any/lib/netstandard2.0").WithFile("nunit.engine.nuget.addins"), - HasDirectory("contentFiles/any/lib/netcoreapp3.1").WithFile("nunit.engine.nuget.addins"), HasDirectory("contentFiles/any/agents/net20").WithFiles(AGENT_FILES).AndFile("nunit.agent.addins"), HasDirectory("contentFiles/any/agents/net40").WithFiles(AGENT_FILES).AndFile("nunit.agent.addins")) & CheckNuGetPackage( diff --git a/src/NUnitEngine/mock-assembly-x86/mock-assembly-x86.csproj b/src/NUnitEngine/mock-assembly-x86/mock-assembly-x86.csproj index cd1b1d801..e69fbc2d1 100644 --- a/src/NUnitEngine/mock-assembly-x86/mock-assembly-x86.csproj +++ b/src/NUnitEngine/mock-assembly-x86/mock-assembly-x86.csproj @@ -11,8 +11,7 @@ - - + diff --git a/src/NUnitEngine/nunit.engine.core/nunit.engine.core.csproj b/src/NUnitEngine/nunit.engine.core/nunit.engine.core.csproj index 2b22f1fb1..c031e32f2 100644 --- a/src/NUnitEngine/nunit.engine.core/nunit.engine.core.csproj +++ b/src/NUnitEngine/nunit.engine.core/nunit.engine.core.csproj @@ -11,10 +11,6 @@ - - - - diff --git a/src/NUnitEngine/nunit.engine.tests/nunit.engine.tests.csproj b/src/NUnitEngine/nunit.engine.tests/nunit.engine.tests.csproj index faef8b0fb..39fa15f1c 100644 --- a/src/NUnitEngine/nunit.engine.tests/nunit.engine.tests.csproj +++ b/src/NUnitEngine/nunit.engine.tests/nunit.engine.tests.csproj @@ -14,9 +14,6 @@ - - - diff --git a/src/NUnitEngine/nunit.engine/Services/RuntimeFrameworkService.cs b/src/NUnitEngine/nunit.engine/Services/RuntimeFrameworkService.cs index 0dea80d11..362213e33 100644 --- a/src/NUnitEngine/nunit.engine/Services/RuntimeFrameworkService.cs +++ b/src/NUnitEngine/nunit.engine/Services/RuntimeFrameworkService.cs @@ -67,9 +67,7 @@ private static bool FrameworksMatch(RuntimeFramework requested, RuntimeFramework return requestedVersion.Major == availableVersion.Major && requestedVersion.Minor == availableVersion.Minor && (requestedVersion.Build < 0 || availableVersion.Build < 0 || requestedVersion.Build == availableVersion.Build) && - (requestedVersion.Revision < 0 || availableVersion.Revision < 0 || requestedVersion.Revision == availableVersion.Revision) && - requestedVersion.Major == availableVersion.Major && - requestedVersion.Minor == availableVersion.Minor; + (requestedVersion.Revision < 0 || availableVersion.Revision < 0 || requestedVersion.Revision == availableVersion.Revision); } private static bool RuntimesMatch(RuntimeType requested, RuntimeType available) @@ -141,7 +139,7 @@ private RuntimeFramework SelectRuntimeFrameworkInner(TestPackage package) if (string.IsNullOrEmpty(imageTargetFrameworkNameSetting)) { // Assume .NET Framework - targetRuntime = RuntimeType.Net; + targetRuntime = currentFramework.Runtime; targetVersion = package.GetSetting(InternalEnginePackageSettings.ImageRuntimeVersion, new Version(2, 0)); } else From d81b3bae47ddf0e8046deb38fc27898da00c82d9 Mon Sep 17 00:00:00 2001 From: Charlie Poole Date: Wed, 5 May 2021 17:38:04 -0700 Subject: [PATCH 24/24] Final fixes --- build.cake | 2 +- msi/nunit/engine-files.wxi | 4 ++-- nuget/engine/nunit.engine.nuspec | 11 +++++++---- nuget/runners/nunit.console-runner.nuspec | 5 +++-- package-checks.cake | 2 ++ 5 files changed, 15 insertions(+), 9 deletions(-) diff --git a/build.cake b/build.cake index ac843c9a7..72421c24a 100644 --- a/build.cake +++ b/build.cake @@ -486,7 +486,7 @@ Task("BuildChocolateyPackages") new ChocolateyNuSpecContent { Source = CURRENT_IMG_DIR + "bin/agents/net40/testcentric.engine.metadata.dll", Target="tools/agents/net40" }, new ChocolateyNuSpecContent { Source = CURRENT_IMG_DIR + "bin/agents/netcoreapp3.1/nunit-agent.dll", Target="tools/agents/netcoreapp3.1" }, new ChocolateyNuSpecContent { Source = CURRENT_IMG_DIR + "bin/agents/netcoreapp3.1/nunit-agent.dll.config", Target="tools/agents/netcoreapp3.1" }, - new ChocolateyNuSpecContent { Source = CURRENT_IMG_DIR + "bin/agents/netcoreapp3.1/nunit-agent.runtimeconfig.dev.json", Target="tools/agents/netcoreapp3.1" }, + new ChocolateyNuSpecContent { Source = CURRENT_IMG_DIR + "bin/agents/netcoreapp3.1/nunit-agent.deps.json", Target="tools/agents/netcoreapp3.1" }, new ChocolateyNuSpecContent { Source = CURRENT_IMG_DIR + "bin/agents/netcoreapp3.1/nunit-agent.runtimeconfig.json", Target="tools/agents/netcoreapp3.1" }, new ChocolateyNuSpecContent { Source = CHOCO_DIR + "nunit-agent.exe.ignore", Target="tools/agents/netcoreapp3.1" }, new ChocolateyNuSpecContent { Source = CURRENT_IMG_DIR + "bin/agents/netcoreapp3.1/nunit.engine.api.dll", Target="tools/agents/netcoreapp3.1" }, diff --git a/msi/nunit/engine-files.wxi b/msi/nunit/engine-files.wxi index 72b0d45b8..20248109c 100644 --- a/msi/nunit/engine-files.wxi +++ b/msi/nunit/engine-files.wxi @@ -93,8 +93,8 @@ - + diff --git a/nuget/engine/nunit.engine.nuspec b/nuget/engine/nunit.engine.nuspec index 60ea6d54a..8c20a410c 100644 --- a/nuget/engine/nunit.engine.nuspec +++ b/nuget/engine/nunit.engine.nuspec @@ -58,7 +58,7 @@ - + @@ -67,17 +67,20 @@ - + + - + + - + + diff --git a/nuget/runners/nunit.console-runner.nuspec b/nuget/runners/nunit.console-runner.nuspec index 007829f8e..35ff5c9f0 100644 --- a/nuget/runners/nunit.console-runner.nuspec +++ b/nuget/runners/nunit.console-runner.nuspec @@ -48,13 +48,14 @@ - + - + + diff --git a/package-checks.cake b/package-checks.cake index 049b5a151..ff531792d 100644 --- a/package-checks.cake +++ b/package-checks.cake @@ -35,6 +35,8 @@ public void CheckAllPackages() HasDirectory("lib/netstandard2.0").WithFiles(ENGINE_FILES), HasDirectory("lib/netcoreapp3.1").WithFiles(ENGINE_FILES), HasDirectory("contentFiles/any/lib/net20").WithFile("nunit.engine.nuget.addins"), + HasDirectory("contentFiles/any/lib/netstandard2.0").WithFile("nunit.engine.nuget.addins"), + HasDirectory("contentFiles/any/lib/netcoreapp3.1").WithFile("nunit.engine.nuget.addins"), HasDirectory("contentFiles/any/agents/net20").WithFiles(AGENT_FILES).AndFile("nunit.agent.addins"), HasDirectory("contentFiles/any/agents/net40").WithFiles(AGENT_FILES).AndFile("nunit.agent.addins")) & CheckNuGetPackage(