diff --git a/.gitignore b/.gitignore index 406551f..93f7e94 100644 --- a/.gitignore +++ b/.gitignore @@ -10,7 +10,7 @@ _ReSharper.* *.bak install/xunitcontrib-* install/UnblockZoneIdentifier.exe -nuget/*.nupkg +resharper/nuget/*.nupkg nuget/nuget.config packages diff --git a/resharper/nuget/xunitcontrib.2.0.0-alpha-20140502.nupkg b/resharper/nuget/xunitcontrib.2.0.0-alpha-20140502.nupkg deleted file mode 100644 index cb15e3a..0000000 Binary files a/resharper/nuget/xunitcontrib.2.0.0-alpha-20140502.nupkg and /dev/null differ diff --git a/resharper/nuget/xunitcontrib.nuspec b/resharper/nuget/xunitcontrib.nuspec index 058c7c5..763d362 100644 --- a/resharper/nuget/xunitcontrib.nuspec +++ b/resharper/nuget/xunitcontrib.nuspec @@ -3,15 +3,23 @@ xunitcontrib xUnit.net Test Support - 2.0.0-alpha-20140502 + 2.0.0-alpha-20140718 Matt Ellis Matt Ellis A unit test provider for xUnit.net. Discovers and runs xUnit.net tests. Includes annotations to aid ReSharper inspections and Live Templates to speed up inserting test methods and asserts A unit test provider for xUnit.net - • Initial support for xUnit.net 2 + Support for xUnit.net 2.0 +• Beta 3 support (build 2700) +• Shadow copy cache clean up on abort + +Known issues: +• Parallelisation temporarily disabled to prevent issues with error handling +• Runs all tests in a class, not just requested methods, due to xunit1 RunWith compatibility +• External annotations not yet implemented for xunit2 +• Live Templates for Theory use xunit1 namespace https://github.com/xunit/resharper-xunit https://raw.githubusercontent.com/xunit/resharper-xunit/xunit2/license.txt - http://download-codeplex.sec.s-msft.com/Download?ProjectName=xunit&DownloadId=662625 + https://raw.githubusercontent.com/xunit/media/master/logo-512-transparent.png Copyright 2014 Matt Ellis false diff --git a/resharper/src/xunitcontrib.runner.resharper.provider/packages.xunitcontrib.runner.resharper.provider.8.2.config b/resharper/src/xunitcontrib.runner.resharper.provider/packages.xunitcontrib.runner.resharper.provider.8.2.config index c0849ff..ab9dd52 100644 --- a/resharper/src/xunitcontrib.runner.resharper.provider/packages.xunitcontrib.runner.resharper.provider.8.2.config +++ b/resharper/src/xunitcontrib.runner.resharper.provider/packages.xunitcontrib.runner.resharper.provider.8.2.config @@ -1,5 +1,5 @@  - + \ No newline at end of file diff --git a/resharper/src/xunitcontrib.runner.resharper.provider/xunitcontrib.runner.resharper.provider.8.2.csproj b/resharper/src/xunitcontrib.runner.resharper.provider/xunitcontrib.runner.resharper.provider.8.2.csproj index ed7ced5..b0075a7 100644 --- a/resharper/src/xunitcontrib.runner.resharper.provider/xunitcontrib.runner.resharper.provider.8.2.csproj +++ b/resharper/src/xunitcontrib.runner.resharper.provider/xunitcontrib.runner.resharper.provider.8.2.csproj @@ -48,7 +48,7 @@ False - ..\..\packages\xunit.abstractions.2.0.0-beta-build2700\lib\net35\xunit.abstractions.dll + ..\..\packages\xunit.abstractions.2.0.0-beta-build2716\lib\net35\xunit.abstractions.dll diff --git a/resharper/src/xunitcontrib.runner.resharper.runner/ReSharperRunnerLogger.cs b/resharper/src/xunitcontrib.runner.resharper.runner/ReSharperRunnerLogger.cs index 975f012..60e461d 100644 --- a/resharper/src/xunitcontrib.runner.resharper.runner/ReSharperRunnerLogger.cs +++ b/resharper/src/xunitcontrib.runner.resharper.runner/ReSharperRunnerLogger.cs @@ -11,6 +11,7 @@ namespace XunitContrib.Runner.ReSharper.RemoteRunner public class ReSharperRunnerLogger : TestMessageVisitor { private const string OneOrMoreChildTestsFailedMessage = "One or more child tests failed"; + private readonly RemoteTaskServer server; private readonly TaskProvider taskProvider; @@ -20,23 +21,6 @@ public ReSharperRunnerLogger(RemoteTaskServer server, TaskProvider taskProvider) this.taskProvider = taskProvider; } - private readonly Stack nastySharedState = new Stack(); - - private void NastySharedState_PushCurrent(TaskInfo task) - { - nastySharedState.Push(task); - } - - private void NastySharedState_PopCurrent(TaskInfo expectedTaskInfo) - { - var poppedState = nastySharedState.Pop(); - if (!Equals(poppedState.RemoteTask, expectedTaskInfo.RemoteTask)) - { - server.TaskFinished(expectedTaskInfo.RemoteTask, "Internal (xunit runner): Shared state out of sync!?", TaskResult.Error, - TimeSpan.Zero); - } - } - protected override bool Visit(ITestClassStarting testClassStarting) { var classTaskInfo = taskProvider.GetClassTask(testClassStarting.TestClass.Class.Name); @@ -44,36 +28,31 @@ protected override bool Visit(ITestClassStarting testClassStarting) return server.ShouldContinue; } - // Called when a class failure is encountered (i.e., when a fixture from IUseFixture throws an - // exception during construction or System.IDisposable.Dispose) - // If the exception happens in the class (fixture) construtor, the child tests are not run, so - // we need to mark them all as having failed - // xunit2: Seems to be when a class or collection fixture fails - - // Called for catastrophic errors, e.g. ambiguously named methods in xunit1 + // Called for xunit1: + // 1. Catastrophic error, i.e. the test runner throws. The only expected exception + // is for ambiguous method names. Test run is aborted + // 2. class failure, i.e. fixture's ctor or dispose throws. Test methods are not run + // if the fixture's ctor throws. Not called for exceptions in class ctor/dispose + // (these are reported as failing test methods, as the class is created for each + // test method) + // Not called for xunit2 protected override bool Visit(IErrorMessage error) { - // TODO: This is very nasty - // OK. I know this will only get called in these scenarios: - // 1. Collection fixtures Dispose methods throw exceptions - // Potentially called multiple times, class and methods are still run - // 2. Class fixtures Dispose methods throw exceptions - // Potentially called multiple times, methods are still run - // 3. xunit1 class fixture ctor or dispose - // Called once, per class. Methods not run - // 4. xunit1 catastrophic error, e.g. ambiguous methods - // Class and methods not run - var taskInfo = nastySharedState.Peek(); + // TODO: This is assuming a lot... + var testClasses = from testCase in error.TestCases + select testCase.TestMethod.TestClass.Class.Name; + + var taskInfo = taskProvider.GetClassTask(testClasses.First()); string message; var exceptions = ConvertExceptions(error, out message); if (taskInfo.RemoteTask is XunitTestClassTask) { - var classTaskInfo = (ClassTaskInfo) taskInfo; + var classTaskInfo = taskInfo; var methodMessage = string.Format("Class failed in {0}", classTaskInfo.ClassTask.TypeName); - var methodExceptions = new[] {new TaskException(null, methodMessage, null)}; + var methodExceptions = new[] { new TaskException(null, methodMessage, null) }; foreach (var task in taskProvider.GetDescendants(classTaskInfo)) { server.TaskException(task.RemoteTask, methodExceptions); @@ -86,6 +65,132 @@ protected override bool Visit(IErrorMessage error) server.TaskException(taskInfo.RemoteTask, exceptions); + TaskFinished(taskInfo, taskInfo.Message, taskInfo.Result, 0); + + return server.ShouldContinue; + } + + protected override bool Visit(ITestAssemblyCleanupFailure testAssemblyCleanupFailure) + { + Console.WriteLine("ITestAssemblyCleanupFailure"); + + // TODO: We can do nothing about this. Report on the root node? + return server.ShouldContinue; + } + + protected override bool Visit(ITestCollectionCleanupFailure testCollectionCleanupFailure) + { + // TODO: Add a collection node? + var methodMessage = string.Format("Collection cleanup failed in {0}", + testCollectionCleanupFailure.TestCollection.DisplayName); + HandleCleanupFailure(testCollectionCleanupFailure, testCollectionCleanupFailure.TestCases, methodMessage); + return server.ShouldContinue; + } + + protected override bool Visit(ITestClassCleanupFailure testClassCleanupFailure) + { + var methodMessage = string.Format("Class cleanup failed in {0}", testClassCleanupFailure.TestClass.Class.Name); + HandleCleanupFailure(testClassCleanupFailure, testClassCleanupFailure.TestCases, methodMessage); + return server.ShouldContinue; + } + + private void HandleCleanupFailure(IFailureInformation failureInformation, IEnumerable testCases, string methodMessage) + { + var testClasses = from testCase in testCases + select testCase.TestMethod.TestClass.Class.Name; + + string classMessage; + var methodExceptions = new[] { new TaskException(null, methodMessage, null) }; + var classExceptions = ConvertExceptions(failureInformation, out classMessage); + + foreach (var typeName in testClasses.Distinct()) + { + var taskInfo = taskProvider.GetClassTask(typeName); + + // TODO: Should fail all test cases rather than have to find descendants + foreach (var task in taskProvider.GetDescendants(taskInfo)) + { + server.TaskException(task.RemoteTask, methodExceptions); + server.TaskFinished(task.RemoteTask, methodMessage, TaskResult.Error, 0); + } + + server.TaskException(taskInfo.RemoteTask, classExceptions); + + taskInfo.Message = classMessage; + taskInfo.Result = TaskResult.Exception; + + if (taskInfo.Finished) + { + taskInfo.Finished = false; + TaskFinished(taskInfo, taskInfo.Message, TaskResult.Exception, 0); + } + } + + } + + protected override bool Visit(ITestMethodCleanupFailure testMethodCleanupFailure) + { + Console.WriteLine("ITestMethodCleanupFailure"); + + // TODO: What causes this? Need to create a test + +#if false + + var taskInfo = taskProvider.GetMethodTask(testMethodCleanupFailure.TestClass.Class.Name, + testMethodCleanupFailure.TestMethod.Method.Name); + + string message; + var exceptions = ConvertExceptions(testMethodCleanupFailure, out message); + + taskInfo.Result = TaskResult.Exception; + taskInfo.Message = message; + + server.TaskException(taskInfo.RemoteTask, exceptions); + +#endif + + return server.ShouldContinue; + } + + protected override bool Visit(ITestCaseCleanupFailure testCaseCleanupFailure) + { + Console.WriteLine("ITestCaseCleanupFailure"); + +#if false + // TODO: Perhaps look for theory task? + var taskInfo = taskProvider.GetMethodTask(testCaseCleanupFailure.TestClass.Class.Name, + testCaseCleanupFailure.TestMethod.Method.Name); + + string message; + var exceptions = ConvertExceptions(testCaseCleanupFailure, out message); + + taskInfo.Result = TaskResult.Exception; + taskInfo.Message = message; + + server.TaskException(taskInfo.RemoteTask, exceptions); +#endif + + return server.ShouldContinue; + } + + protected override bool Visit(ITestCleanupFailure testCleanupFailure) + { + Console.WriteLine("ITestCleanupFailure"); + +#if false + // TODO: Perhaps look for theory task? + var taskInfo = taskProvider.GetMethodTask(testCleanupFailure.TestClass.Class.Name, + testCleanupFailure.TestMethod.Method.Name); + + string message; + var exceptions = ConvertExceptions(testCleanupFailure, out message); + + taskInfo.Result = TaskResult.Exception; + taskInfo.Message = message; + + server.TaskException(taskInfo.RemoteTask, exceptions); +#endif + return server.ShouldContinue; } @@ -106,8 +211,6 @@ protected override bool Visit(ITestMethodStarting testMethodStarting) var taskInfo = taskProvider.GetMethodTask(testMethodStarting.TestClass.Class.Name, testMethodStarting.TestMethod.Method.Name); TaskStarting(taskInfo); - // BUG: beta1+2 will report overloaded methods starting/finished twice - // Since the method is only keyed on name, it s I'll submit a PR to fix taskInfo.Finished = false; taskInfo.Start(); @@ -223,13 +326,12 @@ private void TaskStarting(TaskInfo taskInfo) { server.TaskStarting(taskInfo.RemoteTask); taskInfo.Started = true; - - NastySharedState_PushCurrent(taskInfo); } private void TaskFinished(TaskInfo taskInfo, string message, TaskResult taskResult, decimal executionTime) { - NastySharedState_PopCurrent(taskInfo); + if (taskInfo.Finished) + return; if (taskInfo.Result != TaskResult.Inconclusive) taskResult = taskInfo.Result; @@ -249,9 +351,9 @@ private TaskException[] ConvertExceptions(IFailureInformation failure, out strin { // Strip out the xunit assert methods from the stack traces by taking // out anything in the Xunit.Assert namespace - var stackTraces = failure.StackTraces[i] + var stackTraces = failure.StackTraces[i] != null ? failure.StackTraces[i] .Split(new[] {Environment.NewLine}, StringSplitOptions.RemoveEmptyEntries) - .Where(s => !s.Contains("Xunit.Assert")).Join(Environment.NewLine); + .Where(s => !s.Contains("Xunit.Assert")).Join(Environment.NewLine) : string.Empty; exceptions.Add(new TaskException(failure.ExceptionTypes[i], failure.Messages[i], stackTraces)); } diff --git a/resharper/src/xunitcontrib.runner.resharper.runner/RemoteTaskServer.cs b/resharper/src/xunitcontrib.runner.resharper.runner/RemoteTaskServer.cs index be18b19..184f1de 100644 --- a/resharper/src/xunitcontrib.runner.resharper.runner/RemoteTaskServer.cs +++ b/resharper/src/xunitcontrib.runner.resharper.runner/RemoteTaskServer.cs @@ -56,7 +56,8 @@ public void TaskOutput(RemoteTask remoteTask, string text, TaskOutputType output public void TaskFinished(RemoteTask remoteTask, string message, TaskResult result, decimal durationInSeconds) { - TaskFinished(remoteTask, message, result, TimeSpan.FromSeconds((double) durationInSeconds)); + var timeSpan = durationInSeconds != 0 ? TimeSpan.FromSeconds((double) durationInSeconds) : TimeSpan.Zero; + TaskFinished(remoteTask, message, result, timeSpan); } public void TaskFinished(RemoteTask remoteTask, string message, TaskResult result, TimeSpan duration) diff --git a/resharper/src/xunitcontrib.runner.resharper.runner/TestDiscoveryVisitor.cs b/resharper/src/xunitcontrib.runner.resharper.runner/TestDiscoveryVisitor.cs index f8bce53..38c815b 100644 --- a/resharper/src/xunitcontrib.runner.resharper.runner/TestDiscoveryVisitor.cs +++ b/resharper/src/xunitcontrib.runner.resharper.runner/TestDiscoveryVisitor.cs @@ -18,12 +18,5 @@ protected override bool Visit(ITestCaseDiscoveryMessage discovery) TestCases.Add(discovery.TestCase); return true; } - - public override void Dispose() - { - foreach (var testCase in TestCases) - testCase.Dispose(); - base.Dispose(); - } } } \ No newline at end of file diff --git a/resharper/src/xunitcontrib.runner.resharper.runner/XunitTestRun.cs b/resharper/src/xunitcontrib.runner.resharper.runner/XunitTestRun.cs index d502a83..ddce930 100644 --- a/resharper/src/xunitcontrib.runner.resharper.runner/XunitTestRun.cs +++ b/resharper/src/xunitcontrib.runner.resharper.runner/XunitTestRun.cs @@ -23,10 +23,7 @@ public void RunTests(IEnumerable testCases) var logger = new ReSharperRunnerLogger(server, taskProvider); // TODO: Set any execution options? // Hmm. testCases is serialised, so can't be an arbitrary IEnumerable<> - executor.RunTests(testCases.ToList(), logger, new XunitExecutionOptions - { - DisableParallelization = true - }); + executor.RunTests(testCases.ToList(), logger, new XunitExecutionOptions()); logger.Finished.WaitOne(); } } diff --git a/resharper/src/xunitcontrib.runner.resharper.runner/packages.xunitcontrib.runner.resharper.runner.8.2.config b/resharper/src/xunitcontrib.runner.resharper.runner/packages.xunitcontrib.runner.resharper.runner.8.2.config index 53913bb..7d285a7 100644 --- a/resharper/src/xunitcontrib.runner.resharper.runner/packages.xunitcontrib.runner.resharper.runner.8.2.config +++ b/resharper/src/xunitcontrib.runner.resharper.runner/packages.xunitcontrib.runner.resharper.runner.8.2.config @@ -1,6 +1,6 @@  - - + + \ No newline at end of file diff --git a/resharper/src/xunitcontrib.runner.resharper.runner/xunitcontrib.runner.resharper.runner.8.2.csproj b/resharper/src/xunitcontrib.runner.resharper.runner/xunitcontrib.runner.resharper.runner.8.2.csproj index 0e7fe9c..a0e89d0 100644 --- a/resharper/src/xunitcontrib.runner.resharper.runner/xunitcontrib.runner.resharper.runner.8.2.csproj +++ b/resharper/src/xunitcontrib.runner.resharper.runner/xunitcontrib.runner.resharper.runner.8.2.csproj @@ -40,11 +40,11 @@ False - ..\..\packages\xunit.abstractions.2.0.0-beta-build2700\lib\net35\xunit.abstractions.dll + ..\..\packages\xunit.abstractions.2.0.0-beta-build2716\lib\net35\xunit.abstractions.dll - + False - ..\..\packages\xunit.runner.utility.2.0.0-beta-build2700\lib\net35\xunit.runner.utility.dll + ..\..\packages\xunit.runner.utility.2.0.0-beta-build2716\lib\net35\xunit.runner.utility.dll diff --git a/resharper/test/data/Runner/ClassFixtureThrowsInConstructor.cs b/resharper/test/data/Runner/ClassFixtureThrowsInConstructor.cs new file mode 100644 index 0000000..e1978b1 --- /dev/null +++ b/resharper/test/data/Runner/ClassFixtureThrowsInConstructor.cs @@ -0,0 +1,38 @@ +using System; +using Xunit; + +namespace Foo +{ + public class Fixture + { + public Fixture() + { + throw new InvalidOperationException("Thrown in fixture Constructor"); + } + } + + public class FixtureThrowsInConstructor : IClassFixture + { + public FixtureThrowsInConstructor(Fixture fixture) + { + } + + [Fact] + public void TestMethod1() + { + } + + [Fact] + public void TestMethod2() + { + } + } + + public class Class2 + { + [Fact] + public void TestMethod() + { + } + } +} diff --git a/resharper/test/data/Runner/ClassFixtureThrowsInDispose.cs b/resharper/test/data/Runner/ClassFixtureThrowsInDispose.cs new file mode 100644 index 0000000..027c251 --- /dev/null +++ b/resharper/test/data/Runner/ClassFixtureThrowsInDispose.cs @@ -0,0 +1,38 @@ +using System; +using Xunit; + +namespace Foo +{ + public class Fixture : IDisposable + { + public void Dispose() + { + throw new InvalidOperationException("Thrown in fixture Dispose"); + } + } + + public class FixtureThrowsInDispose : IClassFixture + { + public FixtureThrowsInDispose(Fixture fixture) + { + } + + [Fact] + public void TestMethod1() + { + } + + [Fact] + public void TestMethod2() + { + } + } + + public class Class2 + { + [Fact] + public void TestMethod() + { + } + } +} diff --git a/resharper/test/data/Runner/CollectionFixtureThrowsInConstructor.cs b/resharper/test/data/Runner/CollectionFixtureThrowsInConstructor.cs new file mode 100644 index 0000000..a47db9d --- /dev/null +++ b/resharper/test/data/Runner/CollectionFixtureThrowsInConstructor.cs @@ -0,0 +1,45 @@ +using System; +using Xunit; + +namespace Foo +{ + public class Fixture + { + public Fixture() + { + throw new InvalidOperationException("Thrown in fixture Constructor"); + } + } + + [CollectionDefinition("My collection")] + public class Collection : ICollectionFixture + { + } + + [Collection("My collection")] + public class FixtureThrowsInConstructor + { + public FixtureThrowsInConstructor(Fixture fixture) + { + } + + [Fact] + public void TestMethod1() + { + } + + [Fact] + public void TestMethod2() + { + } + } + + [Collection("Other collection")] + public class Class2 + { + [Fact] + public void TestMethod() + { + } + } +} diff --git a/resharper/test/data/Runner/CollectionFixtureThrowsInDispose.cs b/resharper/test/data/Runner/CollectionFixtureThrowsInDispose.cs new file mode 100644 index 0000000..cccf83e --- /dev/null +++ b/resharper/test/data/Runner/CollectionFixtureThrowsInDispose.cs @@ -0,0 +1,45 @@ +using System; +using Xunit; + +namespace Foo +{ + public class Fixture : IDisposable + { + public void Dispose() + { + throw new InvalidOperationException("Thrown in fixture Dispose"); + } + } + + [CollectionDefinition("My collection")] + public class Collection : ICollectionFixture + { + } + + [Collection("My collection")] + public class FixtureThrowsInDispose + { + public FixtureThrowsInDispose(Fixture fixture) + { + } + + [Fact] + public void TestMethod1() + { + } + + [Fact] + public void TestMethod2() + { + } + } + + [Collection("Other collection")] + public class Class2 + { + [Fact] + public void TestMethod() + { + } + } +} diff --git a/resharper/test/data/Runner/FixtureThrowsInConstructor.cs b/resharper/test/data/Runner/FixtureThrowsInConstructor.xunit1.cs similarity index 100% rename from resharper/test/data/Runner/FixtureThrowsInConstructor.cs rename to resharper/test/data/Runner/FixtureThrowsInConstructor.xunit1.cs diff --git a/resharper/test/data/Runner/FixtureThrowsInDispose.cs b/resharper/test/data/Runner/FixtureThrowsInDispose.xunit1.cs similarity index 100% rename from resharper/test/data/Runner/FixtureThrowsInDispose.cs rename to resharper/test/data/Runner/FixtureThrowsInDispose.xunit1.cs diff --git a/resharper/test/data/Runner/HasRunWith.cs b/resharper/test/data/Runner/HasRunWith.xunit1.cs similarity index 100% rename from resharper/test/data/Runner/HasRunWith.cs rename to resharper/test/data/Runner/HasRunWith.xunit1.cs diff --git a/resharper/test/data/Runner/HasRunWithKnownMethodTask.cs b/resharper/test/data/Runner/HasRunWithKnownMethodTask.xunit1.cs similarity index 100% rename from resharper/test/data/Runner/HasRunWithKnownMethodTask.cs rename to resharper/test/data/Runner/HasRunWithKnownMethodTask.xunit1.cs diff --git a/resharper/test/src/tests/AcceptanceTests/Runner/When_class_fixture_throws_in_constructor.cs b/resharper/test/src/tests/AcceptanceTests/Runner/When_class_fixture_throws_in_constructor.cs new file mode 100644 index 0000000..5b8e68b --- /dev/null +++ b/resharper/test/src/tests/AcceptanceTests/Runner/When_class_fixture_throws_in_constructor.cs @@ -0,0 +1,82 @@ +using NUnit.Framework; + +namespace XunitContrib.Runner.ReSharper.Tests.AcceptanceTests.Runner +{ + [Category("xunit2")] + public class When_class_fixture_throws_in_constructor : XunitTaskRunnerOutputTestBase + { + private const string ExceptionType = "System.InvalidOperationException"; + private const string ExceptionMessage = "Thrown in fixture Constructor"; + private const string StackTrace = "at Foo.Fixture..ctor()"; + + public When_class_fixture_throws_in_constructor() + : base("xunit2") + { + } + + protected override string GetTestName() + { + return "ClassFixtureThrowsInConstructor"; + } + + private TaskId ClassTaskId + { + get { return ForTaskOnly("Foo.FixtureThrowsInConstructor"); } + } + + private TaskId Class2TaskId + { + get { return ForTaskOnly("Foo.Class2"); } + } + + private TaskId Method1TaskId + { + get { return ForTaskOnly("Foo.FixtureThrowsInConstructor", "TestMethod1"); } + } + + private TaskId Method2TaskId + { + get { return ForTaskOnly("Foo.FixtureThrowsInConstructor", "TestMethod2"); } + } + + [Test] + public void Should_notify_class_start() + { + AssertContainsStart(ClassTaskId); + } + + [Test] + public void Should_not_notify_class_exception() + { + // Because the failure is due to the methods failing, not the class itself + // (Error in ctor is reported to method. Error in dispose to class) + AssertDoesNotContain(ClassTaskId, TaskAction.Exception); + } + + [Test] + public void Should_notify_class_failed() + { + AssertContainsFinish(ClassTaskId, TaskResult.Exception, "One or more child tests failed"); + } + + [Test] + public void Should_notify_all_methods_as_finished_and_failed() + { + // TODO: Not sure if we should strip out AggregateException + // Tempted to leave it, because more than one error did occur + // i.e. unable to create fixture, and unable to create class (due to fixture) + const string message = "System.AggregateException: One or more errors occurred."; + + AssertContainsException(Method1TaskId, ExceptionType, ExceptionMessage, StackTrace); + AssertContainsFinishFinal(Method1TaskId, TaskResult.Exception, message); + AssertContainsException(Method2TaskId, ExceptionType, ExceptionMessage, StackTrace); + AssertContainsFinishFinal(Method2TaskId, TaskResult.Exception, message); + } + + [Test] + public void Should_continue_running_other_classes() + { + AssertContainsStart(Class2TaskId); + } + } +} \ No newline at end of file diff --git a/resharper/test/src/tests/AcceptanceTests/Runner/When_class_fixture_throws_in_dispose.cs b/resharper/test/src/tests/AcceptanceTests/Runner/When_class_fixture_throws_in_dispose.cs new file mode 100644 index 0000000..8f3ba77 --- /dev/null +++ b/resharper/test/src/tests/AcceptanceTests/Runner/When_class_fixture_throws_in_dispose.cs @@ -0,0 +1,77 @@ +using NUnit.Framework; + +namespace XunitContrib.Runner.ReSharper.Tests.AcceptanceTests.Runner +{ + [Category("xunit2")] + public class When_class_fixture_throws_in_dispose : XunitTaskRunnerOutputTestBase + { + private const string ExceptionType = "System.InvalidOperationException"; + private const string ExceptionMessage = "Thrown in fixture Dispose"; + private const string StackTrace = "at Foo.Fixture.Dispose()"; + + public When_class_fixture_throws_in_dispose() + : base("xunit2") + { + } + + protected override string GetTestName() + { + return "ClassFixtureThrowsInDispose"; + } + + private TaskId ClassTaskId + { + get { return ForTaskOnly("Foo.FixtureThrowsInDispose"); } + } + + private TaskId Class2TaskId + { + get { return ForTaskOnly("Foo.Class2"); } + } + + private TaskId Method1TaskId + { + get { return ForTaskOnly("Foo.FixtureThrowsInDispose", "TestMethod1"); } + } + + private TaskId Method2TaskId + { + get { return ForTaskOnly("Foo.FixtureThrowsInDispose", "TestMethod2"); } + } + + [Test] + public void Should_notify_class_start() + { + AssertContainsStart(ClassTaskId); + } + + [Test] + public void Should_notify_class_exception() + { + AssertContainsException(ClassTaskId, ExceptionType, ExceptionMessage, StackTrace); + } + + [Test] + public void Should_notify_class_failed() + { + AssertContainsFinish(ClassTaskId, TaskResult.Exception, ExceptionType + ": " + ExceptionMessage); + } + + [Test] + public void Should_notify_all_methods_as_finished_and_failed() + { + const string message = "Class cleanup failed in Foo.FixtureThrowsInDispose"; + + AssertContainsException(Method1TaskId, string.Empty, message, string.Empty); + AssertContainsErrorFinal(Method1TaskId, message); + AssertContainsException(Method2TaskId, string.Empty, message, string.Empty); + AssertContainsErrorFinal(Method2TaskId, message); + } + + [Test] + public void Should_continue_running_other_classes() + { + AssertContainsStart(Class2TaskId); + } + } +} \ No newline at end of file diff --git a/resharper/test/src/tests/AcceptanceTests/Runner/When_class_has_runwith_attribute.cs b/resharper/test/src/tests/AcceptanceTests/Runner/When_class_has_runwith_attribute_xunit1.cs similarity index 85% rename from resharper/test/src/tests/AcceptanceTests/Runner/When_class_has_runwith_attribute.cs rename to resharper/test/src/tests/AcceptanceTests/Runner/When_class_has_runwith_attribute_xunit1.cs index 64a7dae..e7da193 100644 --- a/resharper/test/src/tests/AcceptanceTests/Runner/When_class_has_runwith_attribute.cs +++ b/resharper/test/src/tests/AcceptanceTests/Runner/When_class_has_runwith_attribute_xunit1.cs @@ -3,17 +3,17 @@ namespace XunitContrib.Runner.ReSharper.Tests.AcceptanceTests.Runner { [Category("xunit1")] - public class When_class_has_runwith_attribute : XunitTaskRunnerOutputTestBase + public class When_class_has_runwith_attribute_xunit1 : XunitTaskRunnerOutputTestBase { // RunWith is not a part of xunit2 - public When_class_has_runwith_attribute() + public When_class_has_runwith_attribute_xunit1() : base("xunit1") { } protected override string GetTestName() { - return "HasRunWith"; + return "HasRunWith.xunit1"; } private TaskId TestMethodOnlyTaskId diff --git a/resharper/test/src/tests/AcceptanceTests/Runner/When_class_with_runwith_attribute_runs_again.cs b/resharper/test/src/tests/AcceptanceTests/Runner/When_class_with_runwith_attribute_runs_again_xunit1.cs similarity index 88% rename from resharper/test/src/tests/AcceptanceTests/Runner/When_class_with_runwith_attribute_runs_again.cs rename to resharper/test/src/tests/AcceptanceTests/Runner/When_class_with_runwith_attribute_runs_again_xunit1.cs index cc4dd2e..976cc08 100644 --- a/resharper/test/src/tests/AcceptanceTests/Runner/When_class_with_runwith_attribute_runs_again.cs +++ b/resharper/test/src/tests/AcceptanceTests/Runner/When_class_with_runwith_attribute_runs_again_xunit1.cs @@ -9,17 +9,17 @@ namespace XunitContrib.Runner.ReSharper.Tests.AcceptanceTests.Runner { [Category("xunit1")] - public class When_class_with_runwith_attribute_runs_again : XunitTaskRunnerOutputTestBase + public class When_class_with_runwith_attribute_runs_again_xunit1 : XunitTaskRunnerOutputTestBase { // xunit2 doesn't support RunWith - public When_class_with_runwith_attribute_runs_again() + public When_class_with_runwith_attribute_runs_again_xunit1() : base("xunit1") { } protected override string GetTestName() { - return "HasRunWithKnownMethodTask"; + return "HasRunWithKnownMethodTask.xunit1"; } protected override ICollection GetUnitTestElements(IProject testProject, string assemblyLocation) diff --git a/resharper/test/src/tests/AcceptanceTests/Runner/When_collection_fixture_throws_in_constructor.cs b/resharper/test/src/tests/AcceptanceTests/Runner/When_collection_fixture_throws_in_constructor.cs new file mode 100644 index 0000000..0893783 --- /dev/null +++ b/resharper/test/src/tests/AcceptanceTests/Runner/When_collection_fixture_throws_in_constructor.cs @@ -0,0 +1,82 @@ +using NUnit.Framework; + +namespace XunitContrib.Runner.ReSharper.Tests.AcceptanceTests.Runner +{ + [Category("xunit2")] + public class When_collection_fixture_throws_in_constructor : XunitTaskRunnerOutputTestBase + { + private const string ExceptionType = "System.InvalidOperationException"; + private const string ExceptionMessage = "Thrown in fixture Constructor"; + private const string StackTrace = "at Foo.Fixture..ctor()"; + + public When_collection_fixture_throws_in_constructor() + : base("xunit2") + { + } + + protected override string GetTestName() + { + return "CollectionFixtureThrowsInConstructor"; + } + + private TaskId ClassTaskId + { + get { return ForTaskOnly("Foo.FixtureThrowsInConstructor"); } + } + + private TaskId Class2TaskId + { + get { return ForTaskOnly("Foo.Class2"); } + } + + private TaskId Method1TaskId + { + get { return ForTaskOnly("Foo.FixtureThrowsInConstructor", "TestMethod1"); } + } + + private TaskId Method2TaskId + { + get { return ForTaskOnly("Foo.FixtureThrowsInConstructor", "TestMethod2"); } + } + + [Test] + public void Should_notify_class_start() + { + AssertContainsStart(ClassTaskId); + } + + [Test] + public void Should_not_notify_class_exception() + { + // Because the failure is due to the methods failing, not the class itself + // (Error in ctor is reported to method. Error in dispose to class) + AssertDoesNotContain(ClassTaskId, TaskAction.Exception); + } + + [Test] + public void Should_notify_class_failed() + { + AssertContainsFinish(ClassTaskId, TaskResult.Exception, "One or more child tests failed"); + } + + [Test] + public void Should_notify_all_methods_as_finished_and_failed() + { + // TODO: Not sure if we should strip out AggregateException + // Tempted to leave it, because more than one error did occur + // i.e. unable to create fixture, and unable to create class (due to fixture) + const string message = "System.AggregateException: One or more errors occurred."; + + AssertContainsException(Method1TaskId, ExceptionType, ExceptionMessage, StackTrace); + AssertContainsFinishFinal(Method1TaskId, TaskResult.Exception, message); + AssertContainsException(Method2TaskId, ExceptionType, ExceptionMessage, StackTrace); + AssertContainsFinishFinal(Method2TaskId, TaskResult.Exception, message); + } + + [Test] + public void Should_continue_running_other_collections() + { + AssertContainsStart(Class2TaskId); + } + } +} \ No newline at end of file diff --git a/resharper/test/src/tests/AcceptanceTests/Runner/When_collection_fixture_throws_in_dispose.cs b/resharper/test/src/tests/AcceptanceTests/Runner/When_collection_fixture_throws_in_dispose.cs new file mode 100644 index 0000000..59cbf30 --- /dev/null +++ b/resharper/test/src/tests/AcceptanceTests/Runner/When_collection_fixture_throws_in_dispose.cs @@ -0,0 +1,77 @@ +using NUnit.Framework; + +namespace XunitContrib.Runner.ReSharper.Tests.AcceptanceTests.Runner +{ + [Category("xunit2")] + public class When_collection_fixture_throws_in_dispose : XunitTaskRunnerOutputTestBase + { + private const string ExceptionType = "System.InvalidOperationException"; + private const string ExceptionMessage = "Thrown in fixture Dispose"; + private const string StackTrace = "at Foo.Fixture.Dispose()"; + + public When_collection_fixture_throws_in_dispose() + : base("xunit2") + { + } + + protected override string GetTestName() + { + return "CollectionFixtureThrowsInDispose"; + } + + private TaskId ClassTaskId + { + get { return ForTaskOnly("Foo.FixtureThrowsInDispose"); } + } + + private TaskId Class2TaskId + { + get { return ForTaskOnly("Foo.Class2"); } + } + + private TaskId Method1TaskId + { + get { return ForTaskOnly("Foo.FixtureThrowsInDispose", "TestMethod1"); } + } + + private TaskId Method2TaskId + { + get { return ForTaskOnly("Foo.FixtureThrowsInDispose", "TestMethod2"); } + } + + [Test] + public void Should_notify_class_start() + { + AssertContainsStart(ClassTaskId); + } + + [Test] + public void Should_notify_class_exception() + { + AssertContainsException(ClassTaskId, ExceptionType, ExceptionMessage, StackTrace); + } + + [Test] + public void Should_notify_class_failed() + { + AssertContainsFinishFinal(ClassTaskId, TaskResult.Exception, ExceptionType + ": " + ExceptionMessage); + } + + [Test] + public void Should_notify_all_methods_as_finished_and_failed() + { + const string message = "Collection cleanup failed in My collection"; + + AssertContainsException(Method1TaskId, string.Empty, message, string.Empty); + AssertContainsErrorFinal(Method1TaskId, message); + AssertContainsException(Method2TaskId, string.Empty, message, string.Empty); + AssertContainsErrorFinal(Method2TaskId, message); + } + + [Test] + public void Should_continue_running_other_collections() + { + AssertContainsStart(Class2TaskId); + } + } +} \ No newline at end of file diff --git a/resharper/test/src/tests/AcceptanceTests/Runner/When_fixture_throws_in_constructor_xunit1.cs b/resharper/test/src/tests/AcceptanceTests/Runner/When_fixture_throws_in_constructor_xunit1.cs index 93bb7c5..959ab47 100644 --- a/resharper/test/src/tests/AcceptanceTests/Runner/When_fixture_throws_in_constructor_xunit1.cs +++ b/resharper/test/src/tests/AcceptanceTests/Runner/When_fixture_throws_in_constructor_xunit1.cs @@ -16,7 +16,7 @@ public When_fixture_throws_in_constructor_xunit1() protected override string GetTestName() { - return "FixtureThrowsInConstructor"; + return "FixtureThrowsInConstructor.xunit1"; } private TaskId ClassTaskId diff --git a/resharper/test/src/tests/AcceptanceTests/Runner/When_fixture_throws_in_dispose_xunit1.cs b/resharper/test/src/tests/AcceptanceTests/Runner/When_fixture_throws_in_dispose_xunit1.cs index 8324c08..5494dbe 100644 --- a/resharper/test/src/tests/AcceptanceTests/Runner/When_fixture_throws_in_dispose_xunit1.cs +++ b/resharper/test/src/tests/AcceptanceTests/Runner/When_fixture_throws_in_dispose_xunit1.cs @@ -16,7 +16,7 @@ public When_fixture_throws_in_dispose_xunit1() protected override string GetTestName() { - return "FixtureThrowsInDispose"; + return "FixtureThrowsInDispose.xunit1"; } private TaskId ClassTaskId diff --git a/resharper/test/src/tests/AcceptanceTests/XunitTaskRunnerOutputTestBase.cs b/resharper/test/src/tests/AcceptanceTests/XunitTaskRunnerOutputTestBase.cs index 910ff8b..3a0c1db 100644 --- a/resharper/test/src/tests/AcceptanceTests/XunitTaskRunnerOutputTestBase.cs +++ b/resharper/test/src/tests/AcceptanceTests/XunitTaskRunnerOutputTestBase.cs @@ -3,6 +3,8 @@ using System.Linq; using System.Text; using System.Xml.Linq; +using System.Xml.Schema; +using JetBrains.Util; using NUnit.Framework; namespace XunitContrib.Runner.ReSharper.Tests.AcceptanceTests @@ -93,18 +95,15 @@ protected void AssertContainsErrorFinal(TaskId task, string expectedMessage) protected void AssertContainsException(TaskId task, string expectedType, string expectedExceptionMessage, string expectedStackTrace) { + var expectedString = string.Format("{0}\n{1}\n{2}", expectedType, expectedExceptionMessage, + expectedStackTrace); var messages = from e in messageElements where e.Name == TaskAction.Exception && task.MatchesTaskElement(e) - select new - { - Type = e.Attribute("type").Value, - Message = GetElementValue(e, "message"), - StackTrace = GetElementValue(e, "stack-trace") - }; - var output = messages.Single(); - Assert.AreEqual(expectedType, output.Type); - Assert.AreEqual(expectedExceptionMessage, output.Message.Replace("\n", Environment.NewLine)); - Assert.AreEqual(expectedStackTrace, output.StackTrace.Trim()); + select string.Format("{0}\n{1}\n{2}", e.Attribute("type").Value, + GetElementValue(e, "message").Replace("\n", Environment.NewLine), + GetElementValue(e, "stack-trace").Trim()); + var output = messages.Join("\n---\n"); + StringAssert.Contains(expectedString, output); } protected void AssertContainsStart(TaskId task) @@ -133,6 +132,21 @@ protected void AssertContainsFinish(TaskId task, string expectedTaskResult, stri Assert.AreEqual(expectedMessage, result.Message); } + protected void AssertContainsFinishFinal(TaskId task, string expectedTaskResult, string expectedMessage = null) + { + var messages = from e in messageElements + where e.Name == TaskAction.Finish && task.MatchesTaskElement(e) + select new + { + Result = e.Attribute("result").Value, + Message = GetElementValue(e, "message") + }; + var result = messages.Last(); + Assert.AreEqual(expectedTaskResult, result.Result); + if (!string.IsNullOrEmpty(expectedMessage)) + Assert.AreEqual(expectedMessage, result.Message); + } + protected void AssertContainsCreate(TaskId taskId) { AssertSingle(taskId, TaskAction.Create); diff --git a/resharper/test/src/tests/packages.config b/resharper/test/src/tests/packages.config index ca8bf14..3182d80 100644 --- a/resharper/test/src/tests/packages.config +++ b/resharper/test/src/tests/packages.config @@ -2,7 +2,5 @@ - - - + \ No newline at end of file diff --git a/resharper/test/src/tests/tests-rs82.csproj b/resharper/test/src/tests/tests-rs82.csproj index d80f954..c28997e 100644 --- a/resharper/test/src/tests/tests-rs82.csproj +++ b/resharper/test/src/tests/tests-rs82.csproj @@ -1,11 +1,10 @@  - Tests - 88cb9066 + 7bf2378a Debug @@ -49,15 +48,7 @@ False - ..\..\..\packages\xunit.abstractions.2.0.0-beta-build2700\lib\net35\xunit.abstractions.dll - - - False - ..\..\..\packages\xunit.core.2.0.0-beta-build2700\lib\portable-net45+win+wpa81+wp80+monotouch+monoandroid\xunit.core.dll - - - False - ..\..\..\packages\xunit.execution.2.0.0-beta-build2700\lib\net45\xunit.execution.dll + ..\..\..\packages\xunit.abstractions.2.0.0-beta-build2716\lib\net35\xunit.abstractions.dll @@ -81,10 +72,14 @@ - + + + - + + + @@ -122,7 +117,6 @@ - diff --git a/resharper/tests.manual/tests.xunit/TestsCanHaveTheSameName.cs b/resharper/tests.manual/tests.xunit/TestsCanHaveTheSameName.cs new file mode 100644 index 0000000..aee0939 --- /dev/null +++ b/resharper/tests.manual/tests.xunit/TestsCanHaveTheSameName.cs @@ -0,0 +1,34 @@ +using Xunit; + +namespace tests.xunit.passing +{ + namespace TestsWithSameName + { + // xunit2 allows tests to have the same name. v1 treats this + // as an ambiguous name exception and is a catastrophic error + public class TestsCanHaveSameMethodName + { + [Fact] + public void TheTest() + { + Assert.Equal(12, 23); + } + + [Theory] + [InlineData(42)] + public void TheTest(int x) + { + } + + [Theory] + [InlineData("Hello, world")] + public void TheTest(string x) + { + } + + private void TheTest(string x, int y) + { + } + } + } +} \ No newline at end of file diff --git a/resharper/tests.manual/tests.xunit/tests.xunit.eyeball.csproj b/resharper/tests.manual/tests.xunit/tests.xunit.eyeball.csproj index 53e3317..d3344db 100644 --- a/resharper/tests.manual/tests.xunit/tests.xunit.eyeball.csproj +++ b/resharper/tests.manual/tests.xunit/tests.xunit.eyeball.csproj @@ -14,7 +14,7 @@ v4.5 512 - f4c2830f + ca77c53d true @@ -47,7 +47,7 @@ False ..\..\packages\xunit.abstractions.2.0.0-beta-build2700\lib\net35\xunit.abstractions.dll - + False ..\..\packages\xunit.assert.2.0.0-beta-build2700\lib\portable-net45+win+wpa81+wp80+monoandroid+monotouch10\xunit.assert.dll diff --git a/resharper/tests.manual/tests.xunit/tests.xunit.passing.csproj b/resharper/tests.manual/tests.xunit/tests.xunit.passing.csproj index 38fbd42..6bc111a 100644 --- a/resharper/tests.manual/tests.xunit/tests.xunit.passing.csproj +++ b/resharper/tests.manual/tests.xunit/tests.xunit.passing.csproj @@ -66,6 +66,7 @@ +