Bug 323738 - Minitest support
Initial support for minitest.
Recognize minitest, display in tests view and etc. All with
bugs/limitations but good enough for being able to run/view resulst
inside tests view. Aka good enough to base improvements on.
Change-Id: I83cd751e04f41587eee5c20e103b715971ad8ae3
Signed-off-by: Alexander Kurtakov <akurtako@redhat.com>
diff --git a/plugins/org.eclipse.dltk.ruby.testing/build.properties b/plugins/org.eclipse.dltk.ruby.testing/build.properties
index c35ac1e..a15eb58 100644
--- a/plugins/org.eclipse.dltk.ruby.testing/build.properties
+++ b/plugins/org.eclipse.dltk.ruby.testing/build.properties
@@ -7,4 +7,6 @@
icons/,\
testing/dltk-rspec-runner.rb,\
testing/dltk-testunit-runner.rb,\
- about.html
+ about.html,\
+ testing/minitest/dltk_plugin.rb,\
+ testing/dltk-minitest-runner.rb
diff --git a/plugins/org.eclipse.dltk.ruby.testing/plugin.xml b/plugins/org.eclipse.dltk.ruby.testing/plugin.xml
index f8707f0..a3cf698 100644
--- a/plugins/org.eclipse.dltk.ruby.testing/plugin.xml
+++ b/plugins/org.eclipse.dltk.ruby.testing/plugin.xml
@@ -2,6 +2,13 @@
<?eclipse version="3.2"?>
<plugin>
<extension point="org.eclipse.dltk.testing.engine">
+ <testingEngine
+ class="org.eclipse.dltk.ruby.testing.internal.miniunit.MiniTestingEngine"
+ id="org.eclipse.dltk.ruby.testing.minitest"
+ name="Minitest"
+ nature="org.eclipse.dltk.ruby.core.nature"
+ priority="30">
+ </testingEngine>
<testingEngine
class="org.eclipse.dltk.ruby.testing.internal.testunit.TestUnitTestingEngine"
id="org.eclipse.dltk.ruby.testing.testunit"
diff --git a/plugins/org.eclipse.dltk.ruby.testing/src/org/eclipse/dltk/ruby/testing/internal/Messages.java b/plugins/org.eclipse.dltk.ruby.testing/src/org/eclipse/dltk/ruby/testing/internal/Messages.java
index a22bd8b..9913143 100644
--- a/plugins/org.eclipse.dltk.ruby.testing/src/org/eclipse/dltk/ruby/testing/internal/Messages.java
+++ b/plugins/org.eclipse.dltk.ruby.testing/src/org/eclipse/dltk/ruby/testing/internal/Messages.java
@@ -34,6 +34,8 @@
public static String validate_probablyTestUnit;
public static String validate_notRSpec;
public static String validate_probablyRSpec;
+ public static String validate_notMinitest;
+ public static String validate_probablyMinitest;
public static String validate_runtimeError;
public static String validate_sourceErrors;
static {
diff --git a/plugins/org.eclipse.dltk.ruby.testing/src/org/eclipse/dltk/ruby/testing/internal/messages.properties b/plugins/org.eclipse.dltk.ruby.testing/src/org/eclipse/dltk/ruby/testing/internal/messages.properties
index 29535fc..2a608eb 100644
--- a/plugins/org.eclipse.dltk.ruby.testing/src/org/eclipse/dltk/ruby/testing/internal/messages.properties
+++ b/plugins/org.eclipse.dltk.ruby.testing/src/org/eclipse/dltk/ruby/testing/internal/messages.properties
@@ -7,6 +7,8 @@
validate_probablyTestUnit=This module could be Test::Unit test
validate_notRSpec=This module does not look like RSpec test
validate_probablyRSpec=This module could be RSpec test
+validate_notMinitest=This module does not look like Minitest test
+validate_probablyMinitest=This module could be Minitest test
validate_runtimeError=Error while evaluating: {0}
validate_sourceErrors=Could not evaluate due to errors in source code
RubyTestingLaunchShortcut_testLaunch=Test Launch
diff --git a/plugins/org.eclipse.dltk.ruby.testing/src/org/eclipse/dltk/ruby/testing/internal/miniunit/MiniTestRunnerUI.java b/plugins/org.eclipse.dltk.ruby.testing/src/org/eclipse/dltk/ruby/testing/internal/miniunit/MiniTestRunnerUI.java
new file mode 100644
index 0000000..90c0700
--- /dev/null
+++ b/plugins/org.eclipse.dltk.ruby.testing/src/org/eclipse/dltk/ruby/testing/internal/miniunit/MiniTestRunnerUI.java
@@ -0,0 +1,245 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Red Hat, Inc. and others
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Red Hat, Inc. - initial API and Implementation
+ *******************************************************************************/
+package org.eclipse.dltk.ruby.testing.internal.miniunit;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.dltk.core.IMethod;
+import org.eclipse.dltk.core.IProjectFragment;
+import org.eclipse.dltk.core.IScriptProject;
+import org.eclipse.dltk.core.IType;
+import org.eclipse.dltk.core.ModelException;
+import org.eclipse.dltk.core.environment.EnvironmentPathUtils;
+import org.eclipse.dltk.core.search.IDLTKSearchConstants;
+import org.eclipse.dltk.core.search.IDLTKSearchScope;
+import org.eclipse.dltk.core.search.SearchEngine;
+import org.eclipse.dltk.core.search.SearchMatch;
+import org.eclipse.dltk.core.search.SearchParticipant;
+import org.eclipse.dltk.core.search.SearchPattern;
+import org.eclipse.dltk.core.search.SearchRequestor;
+import org.eclipse.dltk.ruby.core.utils.RubySyntaxUtils;
+import org.eclipse.dltk.ruby.internal.debug.ui.console.RubyConsoleSourceModuleLookup;
+import org.eclipse.dltk.ruby.testing.internal.AbstractRubyTestRunnerUI;
+import org.eclipse.dltk.ruby.testing.internal.AbstractRubyTestingEngine;
+import org.eclipse.dltk.ruby.testing.internal.ResolverUtils;
+import org.eclipse.dltk.ruby.testing.internal.RubyTestingPlugin;
+import org.eclipse.dltk.testing.DLTKTestingMessages;
+import org.eclipse.dltk.testing.TestElementResolution;
+import org.eclipse.dltk.testing.model.ITestCaseElement;
+import org.eclipse.dltk.testing.model.ITestSuiteElement;
+import org.eclipse.osgi.util.NLS;
+
+public class MiniTestRunnerUI extends AbstractRubyTestRunnerUI {
+
+ private static final char CLASS_BEGIN = '(';
+ private static final char CLASS_END = ')';
+
+ /**
+ * @param testingEngine
+ */
+ public MiniTestRunnerUI(AbstractRubyTestingEngine testingEngine, IScriptProject project) {
+ super(testingEngine, project);
+ }
+
+ @Override
+ public String getTestCaseLabel(ITestCaseElement caseElement, boolean full) {
+ final String testName = caseElement.getTestName();
+ int index = testName.lastIndexOf(CLASS_BEGIN);
+ if (index > 0) {
+ final int braceIndex = index;
+ while (index > 0 && Character.isWhitespace(testName.charAt(index - 1))) {
+ --index;
+ }
+ if (full) {
+ int end = testName.length();
+ if (end > braceIndex + 1 && testName.charAt(end - 1) == CLASS_END) {
+ --end;
+ }
+ final String template = DLTKTestingMessages.TestSessionLabelProvider_testMethodName_className;
+ return NLS.bind(template, testName.substring(braceIndex + 1, end), testName.substring(0, index));
+ } else {
+ return testName.substring(0, index);
+ }
+ } else {
+ return testName;
+ }
+ }
+
+ @Override
+ public String getTestStartedMessage(ITestCaseElement caseElement) {
+ final String testName = caseElement.getTestName();
+ int index = testName.lastIndexOf(CLASS_BEGIN);
+ if (index > 0) {
+ int end = testName.length();
+ if (end > index && testName.charAt(end - 1) == CLASS_END) {
+ --end;
+ }
+ final String className = testName.substring(index + 1, end);
+ while (index > 0 && Character.isWhitespace(testName.charAt(index - 1))) {
+ --index;
+ }
+ final String method = testName.substring(0, index);
+ return NLS.bind(DLTKTestingMessages.TestRunnerViewPart_message_started, className, method);
+ } else {
+ return testName;
+ }
+ }
+
+ @Override
+ protected TestElementResolution resolveTestCase(ITestCaseElement testCase) {
+ final String testName = testCase.getTestName();
+ if (testName.length() == 0) {
+ return null;
+ }
+ final int pos = testName.lastIndexOf(CLASS_BEGIN);
+ if (!(pos > 0 && testName.charAt(testName.length() - 1) == CLASS_END)) {
+ return null;
+ }
+ final String className = testName.substring(pos + 1, testName.length() - 1);
+ if (!RubySyntaxUtils.isValidClass(className)) {
+ return null;
+ }
+ final String methodName = testName.substring(0, pos).trim();
+ if (RubySyntaxUtils.isRubyMethodName(methodName)) {
+ final IMethod method = findMethod(className, methodName);
+ if (method != null) {
+ return new TestElementResolution(method, ResolverUtils.getSourceRange(method));
+ }
+ }
+ final List<IType> types = findClasses(className);
+ if (types == null) {
+ return null;
+ }
+ return null;
+ }
+
+ @Override
+ protected TestElementResolution resolveTestSuite(ITestSuiteElement element) {
+ final String className = element.getSuiteTypeName();
+ if (RubySyntaxUtils.isValidClass(className)) {
+ final List<IType> types = findClasses(className);
+ if (types != null) {
+ final IType type = types.get(0);
+ return new TestElementResolution(type, ResolverUtils.getSourceRange(type));
+ }
+ }
+ return null;
+ }
+
+ private static final class TypeSearchRequestor extends SearchRequestor {
+ final List<IType> types = new ArrayList<IType>();
+
+ @Override
+ public void acceptSearchMatch(SearchMatch match) throws CoreException {
+ types.add((IType) match.getElement());
+ }
+ }
+
+ private static final class MethodRequestor extends SearchRequestor {
+ IMethod method = null;
+
+ @Override
+ public void acceptSearchMatch(SearchMatch match) throws CoreException {
+ method = (IMethod) match.getElement();
+ }
+ }
+
+ /**
+ * @param className
+ * @param methodName
+ * @return
+ */
+ private IMethod findMethod(String className, String methodName) {
+ final IDLTKSearchScope scope = getSearchScope();
+ final String sPattern = className + "::" + methodName; //$NON-NLS-1$
+ SearchPattern pattern = SearchPattern.createPattern(sPattern, IDLTKSearchConstants.METHOD,
+ IDLTKSearchConstants.DECLARATIONS, SearchPattern.R_EXACT_MATCH | SearchPattern.R_CASE_SENSITIVE,
+ scope.getLanguageToolkit());
+ try {
+ final MethodRequestor requestor = new MethodRequestor();
+ new SearchEngine().search(pattern, new SearchParticipant[] { SearchEngine.getDefaultSearchParticipant() },
+ scope, requestor, null);
+ return requestor.method;
+ } catch (CoreException e) {
+ final String msg = "Error in findMethod({0}::{1})"; //$NON-NLS-1$
+ RubyTestingPlugin.error(NLS.bind(msg, className, methodName), e);
+ }
+ return null;
+ }
+
+ /**
+ * @param className
+ */
+ private List<IType> findClasses(String className) {
+ final IDLTKSearchScope scope = getSearchScope();
+ SearchPattern pattern = SearchPattern.createPattern(className, IDLTKSearchConstants.TYPE,
+ IDLTKSearchConstants.DECLARATIONS, SearchPattern.R_EXACT_MATCH | SearchPattern.R_CASE_SENSITIVE,
+ scope.getLanguageToolkit());
+ try {
+ final TypeSearchRequestor requestor = new TypeSearchRequestor();
+ new SearchEngine().search(pattern, new SearchParticipant[] { SearchEngine.getDefaultSearchParticipant() },
+ scope, requestor, null);
+ if (!requestor.types.isEmpty()) {
+ return requestor.types;
+ }
+ } catch (CoreException e) {
+ final String msg = "Error in findClasses({0})"; //$NON-NLS-1$
+ RubyTestingPlugin.error(NLS.bind(msg, className), e);
+ }
+ return null;
+ }
+
+ private static final String[] TEST_UNIT = { "test", "unit" }; //$NON-NLS-1$ //$NON-NLS-2$
+
+ private boolean testFragmentPath(IPath fragmentPath, IPath path) {
+ if (pathEquality.isPrefixOf(fragmentPath, path)
+ && path.segmentCount() > fragmentPath.segmentCount() + TEST_UNIT.length) {
+ for (int j = 0; j < TEST_UNIT.length; ++j) {
+ if (!TEST_UNIT[j].equals(path.segment(fragmentPath.segmentCount() + j))) {
+ return false;
+ }
+ }
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ protected boolean selectLine(String line) {
+ final String filename = extractFileName(line);
+ if (filename == null) {
+ return true;
+ }
+ if (filename.endsWith(MiniTestingEngine.MINITEST_RUNNER)) {
+ return false;
+ }
+ final IPath path = new Path(filename);
+ try {
+ final IProjectFragment[] fragments = project.getProjectFragments();
+ for (int i = 0; i < fragments.length; ++i) {
+ final IProjectFragment fragment = fragments[i];
+ if (fragment.isExternal()
+ && testFragmentPath(EnvironmentPathUtils.getLocalPath(fragment.getPath()), path)
+ && RubyConsoleSourceModuleLookup.isIncluded(fragment, path)) {
+ return false;
+ }
+ }
+ } catch (ModelException e) {
+ return true;
+ }
+ return true;
+ }
+}
diff --git a/plugins/org.eclipse.dltk.ruby.testing/src/org/eclipse/dltk/ruby/testing/internal/miniunit/MiniTestingEngine.java b/plugins/org.eclipse.dltk.ruby.testing/src/org/eclipse/dltk/ruby/testing/internal/miniunit/MiniTestingEngine.java
new file mode 100644
index 0000000..10ff374
--- /dev/null
+++ b/plugins/org.eclipse.dltk.ruby.testing/src/org/eclipse/dltk/ruby/testing/internal/miniunit/MiniTestingEngine.java
@@ -0,0 +1,160 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Red Hat Inc. and others
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Red Hat, Inc. - initial API and Implementation
+ *******************************************************************************/
+package org.eclipse.dltk.ruby.testing.internal.miniunit;
+
+import java.io.File;
+
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.debug.core.ILaunch;
+import org.eclipse.debug.core.ILaunchConfiguration;
+import org.eclipse.dltk.ast.ASTNode;
+import org.eclipse.dltk.ast.declarations.MethodDeclaration;
+import org.eclipse.dltk.ast.declarations.ModuleDeclaration;
+import org.eclipse.dltk.ast.expressions.CallExpression;
+import org.eclipse.dltk.compiler.util.Util;
+import org.eclipse.dltk.core.DLTKCore;
+import org.eclipse.dltk.core.IModelElement;
+import org.eclipse.dltk.core.IScriptProject;
+import org.eclipse.dltk.core.ISourceModule;
+import org.eclipse.dltk.core.environment.IEnvironment;
+import org.eclipse.dltk.launching.InterpreterConfig;
+import org.eclipse.dltk.ruby.ast.RubyClassDeclaration;
+import org.eclipse.dltk.ruby.testing.internal.AbstractRubyTestingEngine;
+import org.eclipse.dltk.ruby.testing.internal.AbstractTestingEngineValidateVisitor;
+import org.eclipse.dltk.ruby.testing.internal.Messages;
+import org.eclipse.dltk.ruby.testing.internal.ResolverUtils;
+import org.eclipse.dltk.ruby.testing.internal.RubyTestingLaunchConfigurationDelegate;
+import org.eclipse.dltk.testing.DLTKTestingConstants;
+import org.eclipse.dltk.testing.ITestRunnerUI;
+import org.eclipse.osgi.util.NLS;
+
+public class MiniTestingEngine extends AbstractRubyTestingEngine {
+
+ static class TestUnitValidateVisitor extends AbstractTestingEngineValidateVisitor {
+
+ private static final String MINITEST_AUTORUN = "minitest/autorun"; //$NON-NLS-1$
+ private static final String MINITEST_TEST = "Minitest::Test"; //$NON-NLS-1$
+ private static final String TEST = "test"; //$NON-NLS-1$
+
+ private ISourceModule module;
+ private int testUnitWeight = 0;
+
+ static final int REQUIRE_WEIGHT = 10;
+ static final int TESTCASE_WEIGHT = 10;
+ static final int METHOD_WEIGHT = 1;
+
+ public TestUnitValidateVisitor(ISourceModule module) {
+ this.module = module;
+ }
+
+ @Override
+ public boolean visitGeneral(ASTNode node) throws Exception {
+ if (node instanceof CallExpression) {
+ final CallExpression call = (CallExpression) node;
+ if (isRequire(call, MINITEST_AUTORUN)) {
+ testUnitWeight += REQUIRE_WEIGHT;
+ }
+ } else if (node instanceof RubyClassDeclaration) {
+ if (isSuperClassOf(module, (RubyClassDeclaration) node, MINITEST_TEST)) {
+ testUnitWeight += TESTCASE_WEIGHT;
+ }
+ } else if (node instanceof MethodDeclaration) {
+ if (isNodeOnStack(RubyClassDeclaration.class) && isMethodPrefix((MethodDeclaration) node, TEST)) {
+ testUnitWeight += METHOD_WEIGHT;
+ }
+ }
+ return super.visitGeneral(node);
+ }
+
+ public IStatus getStatus() {
+ if (testUnitWeight >= Math.min(REQUIRE_WEIGHT, TESTCASE_WEIGHT)) {
+ return Status.OK_STATUS;
+ }
+ if (testUnitWeight >= METHOD_WEIGHT) {
+ return createStatus(IStatus.INFO, Messages.validate_probablyMinitest);
+ }
+ return createStatus(IStatus.WARNING, Messages.validate_notMinitest);
+ }
+ }
+
+ @Override
+ public IStatus validateSourceModule(ISourceModule module) {
+ final ModuleDeclaration declaration = ResolverUtils.parse(module);
+ if (declaration == null) {
+ return createStatus(IStatus.WARNING, Messages.validate_sourceErrors);
+ }
+ final TestUnitValidateVisitor visitor = new TestUnitValidateVisitor(module);
+ try {
+ declaration.traverse(visitor);
+ } catch (Exception e) {
+ return createStatus(IStatus.WARNING, NLS.bind(Messages.validate_runtimeError, e.getMessage()));
+ }
+ return visitor.getStatus();
+ }
+
+ static final String MINITEST_RUNNER = "dltk-minitest-runner.rb"; //$NON-NLS-1$
+
+ @Override
+ public void configureLaunch(InterpreterConfig config, ILaunchConfiguration configuration, ILaunch launch)
+ throws CoreException {
+ // select port number
+ final String strPort = String.valueOf(allocatePort());
+ launch.setAttribute(DLTKTestingConstants.ATTR_PORT, strPort);
+ config.addEnvVar(RUBY_TESTING_PORT, strPort);
+ // add runner
+ if (!RubyTestingLaunchConfigurationDelegate.isContainerMode(configuration)) {
+ if (config.getEnvironment().isLocal()) {
+ final String runnerName = MINITEST_RUNNER;
+ if (!isDevelopmentMode(config, runnerName)) {
+ final File runnerFile = getRunnerFile(getBundle(), RUNNER_PATH, runnerName);
+ config.addInterpreterArg("-r"); //$NON-NLS-1$
+ config.addInterpreterArg(runnerFile.getPath());
+ }
+ }
+ } else {
+ final String containerHandle = configuration.getAttribute(DLTKTestingConstants.ATTR_TEST_CONTAINER,
+ Util.EMPTY_STRING);
+ Assert.isLegal(containerHandle.length() != 0);
+ IModelElement element = DLTKCore.create(containerHandle);
+ Assert.isNotNull(element);
+ IResource resource = element.getUnderlyingResource();
+ Assert.isNotNull(resource);
+ final IPath path = resource.getProjectRelativePath();
+ if (path.isEmpty()) {
+ config.addEnvVar(RUBY_TESTING_PATH, "."); //$NON-NLS-1$
+ } else {
+ config.addEnvVar(RUBY_TESTING_PATH, path.toOSString());
+ }
+ }
+ }
+
+ @Override
+ public String getMainScriptPath(ILaunchConfiguration configuration, IEnvironment scriptEnvironment)
+ throws CoreException {
+ if (RubyTestingLaunchConfigurationDelegate.isContainerMode(configuration)) {
+ return getRunnerFile(getBundle(), RUNNER_PATH, MINITEST_RUNNER).getPath();
+ } else {
+ return null;
+ }
+ }
+
+ @Override
+ public ITestRunnerUI getTestRunnerUI(IScriptProject project, ILaunchConfiguration configuration) {
+ return new MiniTestRunnerUI(this, project);
+ }
+
+}
diff --git a/plugins/org.eclipse.dltk.ruby.testing/testing/dltk-minitest-runner.rb b/plugins/org.eclipse.dltk.ruby.testing/testing/dltk-minitest-runner.rb
new file mode 100644
index 0000000..641b3ef
--- /dev/null
+++ b/plugins/org.eclipse.dltk.ruby.testing/testing/dltk-minitest-runner.rb
@@ -0,0 +1,13 @@
+#
+# Copyright (c) 2016 Red Hat Inc. and others
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Eclipse Public License v1.0
+# which accompanies this distribution, and is available at
+# http://www.eclipse.org/legal/epl-v10.html
+#
+# Contributors:
+# Red Hat Inc. - initial API and Implementation
+#
+
+$:.unshift File.dirname(__FILE__)
\ No newline at end of file
diff --git a/plugins/org.eclipse.dltk.ruby.testing/testing/minitest/dltk_plugin.rb b/plugins/org.eclipse.dltk.ruby.testing/testing/minitest/dltk_plugin.rb
new file mode 100644
index 0000000..ac48f03
--- /dev/null
+++ b/plugins/org.eclipse.dltk.ruby.testing/testing/minitest/dltk_plugin.rb
@@ -0,0 +1,172 @@
+#
+# Copyright (c) 2016 Red Hat Inc. and others
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Eclipse Public License v1.0
+# which accompanies this distribution, and is available at
+# http://www.eclipse.org/legal/epl-v10.html
+#
+# Contributors:
+# Red Hat Inc. - initial API and Implementation
+#
+
+require 'minitest/autorun'
+require 'minitest/spec'
+require 'socket'
+
+module Minitest
+ module EnvVars
+ # environment variable name to pass communication port number
+ # to the launched script
+ PORT = "RUBY_TESTING_PORT"
+ PATH = "RUBY_TESTING_PATH"
+ end
+
+ module MessageIds
+ # Notification that a test run has started.
+ # MessageIds.TEST_RUN_START + testCount.toString + " " + version
+ TEST_RUN_START = "%TESTC "
+
+ # Notification that a test run has ended.
+ # TEST_RUN_END + elapsedTime.toString().
+ TEST_RUN_END = "%RUNTIME"
+
+ # Notification about a test inside the test suite.
+ # TEST_TREE + testId + "," + testName + "," + isSuite + "," + testcount
+ # isSuite = "true" or "false"
+ TEST_TREE = "%TSTTREE"
+
+ #Notification that a test has started.
+ # MessageIds.TEST_START + testID + "," + testName
+ TEST_START = "%TESTS "
+
+ # Notification that a test has ended.
+ # TEST_END + testID + "," + testName
+ TEST_END = "%TESTE "
+
+ # Notification that a test had a error.
+ # TEST_ERROR + testID + "," + testName.
+ # After the notification follows the stack trace.
+ TEST_ERROR = "%ERROR "
+
+ # Notification that a test had a failure.
+ # TEST_FAILED + testID + "," + testName.
+ # After the notification follows the stack trace.
+ TEST_FAILED = "%FAILED "
+
+ # Notification that a test trace has started.
+ # The end of the trace is signaled by a TRACE_END
+ # message. In between the TRACE_START and TRACE_END
+ # the stack trace is submitted as multiple lines.
+ TRACE_START = "%TRACES "
+
+ # Notification that a trace ends.
+ TRACE_END = "%TRACEE "
+
+ # Notification that the expected result has started.
+ # The end of the expected result is signaled by a EXPECTED_END.
+ EXPECTED_START = "%EXPECTS"
+
+ # Notification that an expected result ends.
+ EXPECTED_END = "%EXPECTE"
+
+ # Notification that the actual result has started.
+ # The end of the actual result is signaled by a ACTUAL_END.
+ ACTUAL_START = "%ACTUALS"
+
+ # Notification that an actual result ends.
+ ACTUAL_END = "%ACTUALE"
+
+ #Test identifier prefix for ignored tests.
+ IGNORED_TEST_PREFIX = "@Ignore: "
+
+ end # of MessageIds
+
+ def self.plugin_dltk_init(options)
+ Minitest.reporter.reporters.clear
+ self.reporter << DLTKReporter.new(options[:io], options)
+ end
+
+ class DLTKReporter < Minitest::StatisticsReporter
+ def start
+ connectSocket ENV[EnvVars::PORT].to_i
+ @testsByName = {}
+ DLTKReporter.sendMessage MessageIds::TEST_RUN_START + 0.to_s + " v2"
+ super
+ end
+
+ def record(result)
+ notifyTestError result if result.error?
+ notifyTestFailure result if result.failure and !result.error?
+ DLTKReporter.sendMessage MessageIds::TEST_END + result.class.name+result.name + "," + result.name
+ end
+
+ def report
+ super
+ DLTKReporter.sendMessage MessageIds::TEST_RUN_END + (@total_time.to_i * 1000).to_s
+ end
+
+ def connectSocket(port)
+ return false unless port > 0
+ #debug "Opening socket on #{port}"
+ for i in 1..10
+ #debug "Iteration #{i}"
+ begin
+ @@socket = TCPSocket.new('localhost', port)
+ #debug "Socket opened"
+ return true
+ rescue
+ #debug $!.to_s
+ end
+ sleep 1
+ end
+ false
+ end
+
+ def disconnect(result)
+ if @@socket
+ #debug "Closing socket"
+ begin
+ @@socket.close
+ rescue
+ debug $!.to_s
+ end
+ @@socket = nil
+ #debug "Socket closed"
+ end
+ end
+
+ def notifyTestFailure(result)
+ DLTKReporter.sendMessage MessageIds::TEST_FAILED + result.class.name+result.name + "," + result.name
+ DLTKReporter.sendMessage MessageIds::TRACE_START
+ DLTKReporter.sendMessage result.failure.to_s
+ DLTKReporter.sendMessage result.failure.location
+ DLTKReporter.sendMessage MessageIds::TRACE_END
+ end
+
+ def notifyTestError(result)
+ location, line = result.method(result.name).source_location
+ DLTKReporter.sendMessage MessageIds::TEST_ERROR + result.class.name+result.name + "," + result.name
+ DLTKReporter.sendMessage MessageIds::TRACE_START
+ DLTKReporter.sendMessage result.failure.to_s
+ DLTKReporter.sendMessage result.failure.location
+ DLTKReporter.sendMessage MessageIds::TRACE_END
+ end
+
+ def self.sendMessage(message)
+ #debug message
+ if @@socket
+ @@socket.puts message
+ end
+ end
+
+ end
+
+ class Runnable
+ def self.run_one_method klass, method_name, reporter
+ DLTKReporter.sendMessage MessageIds::TEST_START + klass.name+method_name + ',' + method_name
+ reporter.record Minitest.run_one_method(klass, method_name)
+ end
+ end
+
+end
\ No newline at end of file