diff --git a/src/Peachpied.PhpUnit.TestAdapter/PhpUnitHelper.cs b/src/Peachpied.PhpUnit.TestAdapter/PhpUnitHelper.cs index 75fb492..0500b0b 100644 --- a/src/Peachpied.PhpUnit.TestAdapter/PhpUnitHelper.cs +++ b/src/Peachpied.PhpUnit.TestAdapter/PhpUnitHelper.cs @@ -19,7 +19,7 @@ namespace Peachpied.PhpUnit.TestAdapter Context.AddScriptReference(typeof(TestCase).Assembly); } - public static void Launch(string cwd, string testedAssembly, params string[] args) + public static void Launch(string cwd, string testedAssembly, string[] args, Action ctxPreparer = null) { // Load assembly with tests (if not loaded yet) Context.AddScriptReference(Assembly.LoadFrom(testedAssembly)); @@ -32,6 +32,9 @@ namespace Peachpied.PhpUnit.TestAdapter // (there is the condition for execution __FILE__ === realpath($_SERVER['SCRIPT_NAME']) in PHPUnit PHAR entry file) ctx.Server[CommonPhpArrayKeys.SCRIPT_NAME] = "__DUMMY_INVALID_FILE"; + // Perform any custom operations on the context + ctxPreparer?.Invoke(ctx); + // Run the PHAR entry point so that all the classes are included var pharLoader = Context.TryGetDeclaredScript(PharName); RunScript(ctx, () => pharLoader.Evaluate(ctx, PhpArray.NewEmpty(), null).ToInt()); @@ -59,6 +62,9 @@ namespace Peachpied.PhpUnit.TestAdapter } } + public static string GetTestNameFromPhp(string fullTestName) => + fullTestName.Replace('\\', '.').Replace("::", "."); + public static string GetTestNameFromPhp(string className, string methodName) => className.Replace('\\', '.') + "." + methodName; } diff --git a/src/Peachpied.PhpUnit.TestAdapter/PhpUnitTestDiscoverer.cs b/src/Peachpied.PhpUnit.TestAdapter/PhpUnitTestDiscoverer.cs index ce735b9..2f4fe39 100644 --- a/src/Peachpied.PhpUnit.TestAdapter/PhpUnitTestDiscoverer.cs +++ b/src/Peachpied.PhpUnit.TestAdapter/PhpUnitTestDiscoverer.cs @@ -36,7 +36,7 @@ namespace Peachpied.PhpUnit.TestAdapter { string projectDir = EnvironmentHelper.TryFindProjectDirectory(Path.GetDirectoryName(source)); tempTestsXml = Path.GetTempFileName(); - PhpUnitHelper.Launch(projectDir, source, "--teamcity", "--list-tests-xml", tempTestsXml); // TODO: Remove --teamcity switch when it no longer causes crash + PhpUnitHelper.Launch(projectDir, source, new[] { "--teamcity", "--list-tests-xml", tempTestsXml }); // TODO: Remove --teamcity switch when it no longer causes crash ProcessTestsXml(source, tempTestsXml, discoverySink); diff --git a/src/Peachpied.PhpUnit.TestAdapter/PhpUnitTestExecutor.cs b/src/Peachpied.PhpUnit.TestAdapter/PhpUnitTestExecutor.cs index 0c4d634..ba87f43 100644 --- a/src/Peachpied.PhpUnit.TestAdapter/PhpUnitTestExecutor.cs +++ b/src/Peachpied.PhpUnit.TestAdapter/PhpUnitTestExecutor.cs @@ -1,10 +1,12 @@ using System; using System.Collections.Generic; using System.Collections.Immutable; +using System.IO; using System.Linq; using System.Text; using Microsoft.VisualStudio.TestPlatform.ObjectModel; using Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter; +using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging; namespace Peachpied.PhpUnit.TestAdapter { @@ -16,10 +18,26 @@ namespace Peachpied.PhpUnit.TestAdapter public void RunTests(IEnumerable sources, IRunContext runContext, IFrameworkHandle frameworkHandle) { - var ctx = new PhpUnitContext(sources); - var tests = ctx.FindTestCases(); - - ctx.RunTests(tests, frameworkHandle); + foreach (var source in sources) + { + try + { + string projectDir = EnvironmentHelper.TryFindProjectDirectory(Path.GetDirectoryName(source)); + + PhpUnitHelper.Launch(projectDir, source, new[] { "--teamcity", "--extensions", TestReporterExtension.PhpName }, + ctx => + { + ctx.DeclareType(); + + var testRunCtx = new TestRunContext(source, frameworkHandle); + ctx.SetProperty(testRunCtx); + }); + } + catch (Exception e) + { + frameworkHandle.SendMessage(TestMessageLevel.Error, e.Message + "\n" + e.StackTrace); + } + } } public void RunTests(IEnumerable tests, IRunContext runContext, IFrameworkHandle frameworkHandle) diff --git a/src/Peachpied.PhpUnit.TestAdapter/TestReporterExtension.cs b/src/Peachpied.PhpUnit.TestAdapter/TestReporterExtension.cs new file mode 100644 index 0000000..326dda1 --- /dev/null +++ b/src/Peachpied.PhpUnit.TestAdapter/TestReporterExtension.cs @@ -0,0 +1,54 @@ +using Microsoft.VisualStudio.TestPlatform.ObjectModel; +using Pchp.Core; +using Pchp.Core.Reflection; +using PHPUnit.Runner; +using System; +using System.Collections.Generic; +using System.Text; + +namespace Peachpied.PhpUnit.TestAdapter +{ + internal class TestReporterExtension : BeforeTestHook, AfterSuccessfulTestHook, AfterTestErrorHook, AfterTestFailureHook, AfterSkippedTestHook + { + public readonly static string PhpName = typeof(TestReporterExtension).GetPhpTypeInfo().Name; + + private TestRunContext _testRunContext; + + public TestReporterExtension(Context ctx) + { + _testRunContext = ctx.TryGetProperty() ?? throw new InvalidOperationException(); + } + + public void executeBeforeTest([NotNull] string test) => + _testRunContext.FrameworkHandle.RecordStart(GetTestCase(test)); + + public void executeAfterSuccessfulTest([NotNull] string test, double time) => + ReportOutcome(test, TestOutcome.Passed); + + public void executeAfterTestError([NotNull] string test, [NotNull] string message, double time) => + ReportOutcome(test, TestOutcome.Failed); + + public void executeAfterTestFailure([NotNull] string test, [NotNull] string message, double time) => + ReportOutcome(test, TestOutcome.Failed); + + public void executeAfterSkippedTest([NotNull] string test, [NotNull] string message, double time) => + ReportOutcome(test, TestOutcome.Skipped); + + private void ReportOutcome(string phpTestName, TestOutcome outcome) + { + var testCase = GetTestCase(phpTestName); + var testResult = new TestResult(testCase) + { + Outcome = outcome + }; + + _testRunContext.FrameworkHandle.RecordResult(testResult); + } + + private TestCase GetTestCase(string phpTestName) + { + string vsTestName = PhpUnitHelper.GetTestNameFromPhp(phpTestName); + return new TestCase(vsTestName, PhpUnitTestExecutor.ExecutorUri, _testRunContext.Source); + } + } +} diff --git a/src/Peachpied.PhpUnit.TestAdapter/TestRunContext.cs b/src/Peachpied.PhpUnit.TestAdapter/TestRunContext.cs new file mode 100644 index 0000000..08a2876 --- /dev/null +++ b/src/Peachpied.PhpUnit.TestAdapter/TestRunContext.cs @@ -0,0 +1,20 @@ +using Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter; +using System; +using System.Collections.Generic; +using System.Text; + +namespace Peachpied.PhpUnit.TestAdapter +{ + internal class TestRunContext + { + public string Source { get; } + + public IFrameworkHandle FrameworkHandle { get; } + + public TestRunContext(string source, IFrameworkHandle frameworkHandle) + { + this.Source = source; + this.FrameworkHandle = frameworkHandle; + } + } +}