#develop (short for SharpDevelop) is a free IDE for .NET programming languages.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

237 lines
6.5 KiB

// Copyright (c) 2014 AlphaSierraPapa for the SharpDevelop Team
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using System.IO;
using System.IO.Pipes;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using ICSharpCode.Core;
using ICSharpCode.SharpDevelop;
namespace ICSharpCode.UnitTesting
{
public interface ITestResultsReader : IDisposable
{
event EventHandler<TestFinishedEventArgs> TestFinished;
string PipeName { get; }
void Start();
void Join();
}
public class TestResultsReader : ITestResultsReader
{
TextReader reader;
readonly NamedPipeServerStream namedPipe;
readonly string pipeName;
public TestResultsReader()
{
pipeName = Guid.NewGuid().ToString();
namedPipe = new NamedPipeServerStream(this.PipeName, PipeDirection.In, 1, PipeTransmissionMode.Byte);
}
/// <summary>
/// Creates a new TestResultsReader that reads from an existing text reader.
/// </summary>
public TestResultsReader(TextReader reader)
{
this.reader = reader;
}
public string PipeName {
get { return pipeName; }
}
public void Dispose()
{
if (reader != null)
reader.Dispose();
if (namedPipe != null)
namedPipe.Dispose();
}
public event EventHandler<TestFinishedEventArgs> TestFinished;
Thread thread;
/// <summary>
/// Runs the pipe reader on a background thread.
/// The returned task will be signalled as completed when the worker process closes the pipe, after
/// all contents have been read from the pipe.
/// </summary>
public void Start()
{
thread = new Thread(Run);
thread.Name = "UnitTesting Pipe Reader";
thread.Start();
}
public void Join()
{
SD.Log.Debug("Waiting for pipe reader to finish");
thread.Join();
SD.Log.Debug("Pipe reader has finished");
}
/// <summary>
/// Run the pipe reader on the current thread.
/// This method blocks until the worker process closes the pipe.
/// </summary>
public void Run()
{
if (reader == null) {
SD.Log.Debug("Waiting for connection to pipe");
namedPipe.WaitForConnection();
SD.Log.Debug("Start reading pipe");
reader = new StreamReader(namedPipe);
}
char[] buffer = new char[1024];
int read;
while ((read = reader.Read(buffer, 0, buffer.Length)) != 0) {
for (int i = 0; i < read; i++) {
if (ReadNameValuePair(buffer[i])) {
if (ReadTestResult()) {
if (TestFinished != null)
TestFinished(this, new TestFinishedEventArgs(result));
}
}
}
}
SD.Log.Debug("End of pipe");
}
StringBuilder nameBuilder = new StringBuilder();
StringBuilder valueBuilder = new StringBuilder();
bool firstNameChar = true;
TestResult result;
enum State {
WaitingForEndOfName = 0,
WaitingForStartOfValue = 1,
WaitingForEndOfValue = 2
}
State state = State.WaitingForEndOfName;
/// <summary>
/// Reads a name-value pair of the form:
///
/// Name: Value
///
/// A value can span multiple lines:
///
/// Name1: ValueLine1
/// {SP}ValueLine2
/// {SP}ValueLine3
/// Name1: Value2
///
/// Each continued line of the value must start with
/// a single space character to distinguish it from
/// the start of a new name-value pair.
/// </summary>
/// <returns>True if a name-value pair has been
/// successfully read in</returns>
bool ReadNameValuePair(char ch)
{
if (state == State.WaitingForEndOfName) {
ReadNameChar(ch);
} else if (state == State.WaitingForStartOfValue) {
// Makes sure first space is ignored.
state = State.WaitingForEndOfValue;
} else {
return ReadValueChar(ch);
}
return false;
}
void ReadNameChar(char ch)
{
if (ch == ':') {
state = State.WaitingForStartOfValue;
} else if (ch == '\r' || ch == '\n') {
nameBuilder = new StringBuilder();
} else if (ch == ' ' && firstNameChar) {
state = State.WaitingForEndOfValue;
valueBuilder.Append("\r\n");
} else if (firstNameChar) {
firstNameChar = false;
nameBuilder = new StringBuilder();
valueBuilder = new StringBuilder();
nameBuilder.Append(ch);
} else {
nameBuilder.Append(ch);
}
}
bool ReadValueChar(char ch)
{
if (ch == '\r') {
// Ignore.
} else if (ch == '\n') {
state = State.WaitingForEndOfName;
firstNameChar = true;
return true;
} else {
valueBuilder.Append(ch);
}
return false;
}
/// <summary>
/// Creates and updates a TestResult based on the
/// name-value pair read in,
/// </summary>
/// <returns>True if a TestResult is ready to be returned
/// to the caller.</returns>
/// <remarks>
/// The first name-value pair for a test result is the
/// test name. The last name-value pair is the result of
/// the test (Success, Failure or Ignored).</remarks>
bool ReadTestResult()
{
string name = nameBuilder.ToString();
if (name == "Name") {
result = new TestResult(valueBuilder.ToString());
} else if (result != null) {
if (name == "Message") {
result.Message = valueBuilder.ToString();
} else if (name == "StackTrace") {
result.StackTrace = valueBuilder.ToString();
} else if (name == "Result") {
UpdateTestSuccess();
return true;
}
}
return false;
}
void UpdateTestSuccess()
{
string value = valueBuilder.ToString();
if (value == "Success") {
result.ResultType = TestResultType.Success;
} else if (value == "Failure") {
result.ResultType = TestResultType.Failure;
} else {
result.ResultType = TestResultType.Ignored;
}
}
}
}