// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Reactive.Disposables;
using System.Reactive.Linq;
#if NUNIT
using NUnit.Framework;
#elif WINDOWS8
using Microsoft.VisualStudio.TestPlatform.UnitTestFramework;
#else
using Microsoft.VisualStudio.TestTools.UnitTesting;
#endif
namespace Microsoft.Reactive.Testing
{
///
/// Helper class to write asserts in unit tests for applications and libraries built using Reactive Extensions.
///
public static class ReactiveAssert
{
static string Message(IEnumerable actual, IEnumerable expected)
{
var sb = new StringBuilder();
sb.AppendLine();
sb.Append("Expected: [");
sb.Append(string.Join(", ", expected.Select(x => x.ToString()).ToArray()));
sb.Append("]");
sb.AppendLine();
sb.Append("Actual..: [");
sb.Append(string.Join(", ", actual.Select(x => x.ToString()).ToArray()));
sb.Append("]");
sb.AppendLine();
return sb.ToString();
}
///
/// Asserts that both enumerable sequences have equal length and equal elements.
///
/// The type of the elements in the sequence.
/// Expected sequence.
/// Actual sequence to compare against the expected one.
/// or is null.
public static void AreElementsEqual(IEnumerable expected, IEnumerable actual)
{
if (expected == null)
throw new ArgumentNullException("expected");
if (actual == null)
throw new ArgumentNullException("actual");
if (!expected.SequenceEqual(actual))
Assert.Fail(Message(actual, expected));
}
///
/// Asserts that both enumerable sequences have equal length and equal elements.
///
/// The type of the elements in the sequence.
/// Expected sequence.
/// Actual sequence to compare against the expected one.
/// Error message for assert failure.
/// or is null.
public static void AreElementsEqual(IEnumerable expected, IEnumerable actual, string message)
{
if (expected == null)
throw new ArgumentNullException("expected");
if (actual == null)
throw new ArgumentNullException("actual");
if (!expected.SequenceEqual(actual))
Assert.Fail(message);
}
///
/// Asserts that both observable sequences have equal length and equal notifications.
///
/// The type of the elements in the sequence.
/// Expected sequence.
/// Actual sequence to compare against the expected one.
/// or is null.
public static void AreElementsEqual(IObservable expected, IObservable actual)
{
if (expected == null)
throw new ArgumentNullException("expected");
if (actual == null)
throw new ArgumentNullException("actual");
AreElementsEqual(expected.Materialize().ToEnumerable(), actual.Materialize().ToEnumerable());
}
///
/// Asserts that both observable sequences have equal length and equal elements.
///
/// The type of the elements in the sequence.
/// Expected sequence.
/// Actual sequence to compare against the expected one.
/// Error message for assert failure.
/// or is null.
public static void AreElementsEqual(IObservable expected, IObservable actual, string message)
{
if (expected == null)
throw new ArgumentNullException("expected");
if (actual == null)
throw new ArgumentNullException("actual");
AreElementsEqual(expected.Materialize().ToEnumerable(), actual.Materialize().ToEnumerable(), message);
}
///
/// Asserts that the given action throws an exception of the type specified in the generic parameter, or a subtype thereof.
///
/// Type of the exception to check for.
/// Action to run.
/// is null.
public static void Throws(Action action) where TException : Exception
{
if (action == null)
throw new ArgumentNullException("action");
var failed = false;
try
{
action();
failed = true;
}
catch (TException)
{
}
catch (Exception ex)
{
Assert.Fail(string.Format(CultureInfo.CurrentCulture, "Expected {0} threw {1}.\r\n\r\nStack trace:\r\n{2}", typeof(TException).Name, ex.GetType().Name, ex.StackTrace));
}
if (failed)
Assert.Fail(string.Format(CultureInfo.CurrentCulture, "Expected {0}.", typeof(TException).Name));
}
///
/// Asserts that the given action throws an exception of the type specified in the generic parameter, or a subtype thereof.
///
/// Type of the exception to check for.
/// Action to run.
/// Error message for assert failure.
/// is null.
public static void Throws(Action action, string message) where TException : Exception
{
if (action == null)
throw new ArgumentNullException("action");
var failed = false;
try
{
action();
failed = true;
}
catch (TException)
{
}
catch
{
Assert.Fail(message);
}
if (failed)
Assert.Fail(message);
}
///
/// Asserts that the given action throws the specified exception.
///
/// Type of the exception to check for.
/// Exception to assert being thrown.
/// Action to run.
/// is null.
public static void Throws(TException exception, Action action) where TException : Exception
{
if (action == null)
throw new ArgumentNullException("action");
var failed = false;
try
{
action();
failed = true;
}
catch (TException ex)
{
Assert.AreSame(exception, ex);
}
catch (Exception ex)
{
Assert.Fail(string.Format(CultureInfo.CurrentCulture, "Expected {0} threw {1}.\r\n\r\nStack trace:\r\n{2}", typeof(TException).Name, ex.GetType().Name, ex.StackTrace));
}
if (failed)
Assert.Fail(string.Format(CultureInfo.CurrentCulture, "Expected {0}.", typeof(TException).Name));
}
///
/// Asserts that the given action throws the specified exception.
///
/// Type of the exception to check for.
/// Exception to assert being thrown.
/// Action to run.
/// Error message for assert failure.
/// is null.
public static void Throws(TException exception, Action action, string message) where TException : Exception
{
if (action == null)
throw new ArgumentNullException("action");
var failed = false;
try
{
action();
failed = true;
}
catch (TException ex)
{
Assert.AreSame(exception, ex);
}
catch
{
Assert.Fail(message);
}
if (failed)
Assert.Fail(message);
}
///
/// Asserts that both enumerable sequences have equal length and equal elements.
///
/// The type of the elements in the sequence.
/// Actual sequence to compare against the expected one.
/// Expected sequence.
/// or is null.
public static void AssertEqual(this IEnumerable actual, IEnumerable expected)
{
if (actual == null)
throw new ArgumentNullException("actual");
if (expected == null)
throw new ArgumentNullException("expected");
ReactiveAssert.AreElementsEqual(expected, actual);
}
///
/// Asserts the enumerable sequence has the expected elements.
///
/// The type of the elements in the sequence.
/// Actual sequence to compare against the expected elements.
/// Expected elements.
/// or is null.
public static void AssertEqual(this IEnumerable actual, params T[] expected)
{
if (actual == null)
throw new ArgumentNullException("actual");
if (expected == null)
throw new ArgumentNullException("expected");
ReactiveAssert.AreElementsEqual(expected, actual);
}
///
/// Asserts that both observable sequences have equal length and equal notifications.
///
/// The type of the elements in the sequence.
/// Actual sequence to compare against the expected one.
/// Expected sequence.
/// or is null.
public static void AssertEqual(this IObservable actual, IObservable expected)
{
if (actual == null)
throw new ArgumentNullException("actual");
if (expected == null)
throw new ArgumentNullException("expected");
ReactiveAssert.AreElementsEqual(expected, actual);
}
}
}