diff options
Diffstat (limited to 'testsrunner/org.eclipse.cdt.testsrunner.gtest')
14 files changed, 1094 insertions, 0 deletions
diff --git a/testsrunner/org.eclipse.cdt.testsrunner.gtest/.classpath b/testsrunner/org.eclipse.cdt.testsrunner.gtest/.classpath new file mode 100644 index 00000000000..deb673668e9 --- /dev/null +++ b/testsrunner/org.eclipse.cdt.testsrunner.gtest/.classpath @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="UTF-8"?> +<classpath> + <classpathentry kind="src" path="src"/> + <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/> + <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"/> + <classpathentry kind="output" path="bin"/> +</classpath> diff --git a/testsrunner/org.eclipse.cdt.testsrunner.gtest/.project b/testsrunner/org.eclipse.cdt.testsrunner.gtest/.project new file mode 100644 index 00000000000..174ece0c727 --- /dev/null +++ b/testsrunner/org.eclipse.cdt.testsrunner.gtest/.project @@ -0,0 +1,34 @@ +<?xml version="1.0" encoding="UTF-8"?> +<projectDescription> + <name>org.eclipse.cdt.testsrunner.gtest</name> + <comment></comment> + <projects> + </projects> + <buildSpec> + <buildCommand> + <name>org.eclipse.jdt.core.javabuilder</name> + <arguments> + </arguments> + </buildCommand> + <buildCommand> + <name>org.eclipse.pde.ManifestBuilder</name> + <arguments> + </arguments> + </buildCommand> + <buildCommand> + <name>org.eclipse.pde.SchemaBuilder</name> + <arguments> + </arguments> + </buildCommand> + <buildCommand> + <name>org.eclipse.pde.api.tools.apiAnalysisBuilder</name> + <arguments> + </arguments> + </buildCommand> + </buildSpec> + <natures> + <nature>org.eclipse.jdt.core.javanature</nature> + <nature>org.eclipse.pde.PluginNature</nature> + <nature>org.eclipse.pde.api.tools.apiAnalysisNature</nature> + </natures> +</projectDescription> diff --git a/testsrunner/org.eclipse.cdt.testsrunner.gtest/.settings/org.eclipse.jdt.core.prefs b/testsrunner/org.eclipse.cdt.testsrunner.gtest/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 00000000000..0e860bfc279 --- /dev/null +++ b/testsrunner/org.eclipse.cdt.testsrunner.gtest/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,82 @@ +#Mon Apr 16 13:01:24 EEST 2012 +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6 +org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve +org.eclipse.jdt.core.compiler.compliance=1.6 +org.eclipse.jdt.core.compiler.debug.lineNumber=generate +org.eclipse.jdt.core.compiler.debug.localVariable=generate +org.eclipse.jdt.core.compiler.debug.sourceFile=generate +org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=warning +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.autoboxing=ignore +org.eclipse.jdt.core.compiler.problem.comparingIdentical=warning +org.eclipse.jdt.core.compiler.problem.deadCode=warning +org.eclipse.jdt.core.compiler.problem.deprecation=warning +org.eclipse.jdt.core.compiler.problem.deprecationInDeprecatedCode=disabled +org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=disabled +org.eclipse.jdt.core.compiler.problem.discouragedReference=warning +org.eclipse.jdt.core.compiler.problem.emptyStatement=warning +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.problem.fallthroughCase=warning +org.eclipse.jdt.core.compiler.problem.fatalOptionalError=enabled +org.eclipse.jdt.core.compiler.problem.fieldHiding=warning +org.eclipse.jdt.core.compiler.problem.finalParameterBound=warning +org.eclipse.jdt.core.compiler.problem.finallyBlockNotCompletingNormally=warning +org.eclipse.jdt.core.compiler.problem.forbiddenReference=error +org.eclipse.jdt.core.compiler.problem.hiddenCatchBlock=warning +org.eclipse.jdt.core.compiler.problem.includeNullInfoFromAsserts=disabled +org.eclipse.jdt.core.compiler.problem.incompatibleNonInheritedInterfaceMethod=warning +org.eclipse.jdt.core.compiler.problem.incompleteEnumSwitch=warning +org.eclipse.jdt.core.compiler.problem.indirectStaticAccess=ignore +org.eclipse.jdt.core.compiler.problem.localVariableHiding=ignore +org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=error +org.eclipse.jdt.core.compiler.problem.missingDeprecatedAnnotation=warning +org.eclipse.jdt.core.compiler.problem.missingHashCodeMethod=ignore +org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=warning +org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotationForInterfaceMethodImplementation=enabled +org.eclipse.jdt.core.compiler.problem.missingSerialVersion=ignore +org.eclipse.jdt.core.compiler.problem.missingSynchronizedOnInheritedMethod=ignore +org.eclipse.jdt.core.compiler.problem.noEffectAssignment=warning +org.eclipse.jdt.core.compiler.problem.noImplicitStringConversion=warning +org.eclipse.jdt.core.compiler.problem.nonExternalizedStringLiteral=warning +org.eclipse.jdt.core.compiler.problem.nullReference=error +org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod=error +org.eclipse.jdt.core.compiler.problem.parameterAssignment=ignore +org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment=warning +org.eclipse.jdt.core.compiler.problem.potentialNullReference=warning +org.eclipse.jdt.core.compiler.problem.rawTypeReference=warning +org.eclipse.jdt.core.compiler.problem.redundantNullCheck=warning +org.eclipse.jdt.core.compiler.problem.redundantSpecificationOfTypeArguments=ignore +org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=warning +org.eclipse.jdt.core.compiler.problem.reportMethodCanBePotentiallyStatic=ignore +org.eclipse.jdt.core.compiler.problem.reportMethodCanBeStatic=ignore +org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled +org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=warning +org.eclipse.jdt.core.compiler.problem.suppressOptionalErrors=disabled +org.eclipse.jdt.core.compiler.problem.suppressWarnings=enabled +org.eclipse.jdt.core.compiler.problem.syntheticAccessEmulation=ignore +org.eclipse.jdt.core.compiler.problem.typeParameterHiding=warning +org.eclipse.jdt.core.compiler.problem.unavoidableGenericTypeProblems=enabled +org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=warning +org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=ignore +org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=warning +org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore +org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=warning +org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore +org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=ignore +org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionExemptExceptionAndThrowable=enabled +org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionIncludeDocCommentReference=enabled +org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding=disabled +org.eclipse.jdt.core.compiler.problem.unusedImport=warning +org.eclipse.jdt.core.compiler.problem.unusedLabel=warning +org.eclipse.jdt.core.compiler.problem.unusedLocal=warning +org.eclipse.jdt.core.compiler.problem.unusedObjectAllocation=ignore +org.eclipse.jdt.core.compiler.problem.unusedParameter=ignore +org.eclipse.jdt.core.compiler.problem.unusedParameterIncludeDocCommentReference=enabled +org.eclipse.jdt.core.compiler.problem.unusedParameterWhenImplementingAbstract=disabled +org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=disabled +org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=warning +org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning +org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning +org.eclipse.jdt.core.compiler.source=1.6 diff --git a/testsrunner/org.eclipse.cdt.testsrunner.gtest/META-INF/MANIFEST.MF b/testsrunner/org.eclipse.cdt.testsrunner.gtest/META-INF/MANIFEST.MF new file mode 100644 index 00000000000..6138224787a --- /dev/null +++ b/testsrunner/org.eclipse.cdt.testsrunner.gtest/META-INF/MANIFEST.MF @@ -0,0 +1,13 @@ +Manifest-Version: 1.0 +Bundle-ManifestVersion: 2 +Bundle-Name: %pluginName +Bundle-SymbolicName: org.eclipse.cdt.testsrunner.gtest;singleton:=true +Bundle-Version: 7.0.0.qualifier +Bundle-Activator: org.eclipse.cdt.testsrunner.internal.gtest.GoogleTestsRunnerPlugin +Bundle-Vendor: %providerName +Bundle-Localization: plugin +Bundle-ActivationPolicy: lazy +Bundle-RequiredExecutionEnvironment: JavaSE-1.6 +Require-Bundle: org.eclipse.core.runtime;bundle-version="3.5.0", + org.eclipse.cdt.testsrunner;bundle-version="3.5.0" +Export-Package: org.eclipse.cdt.testsrunner.internal.gtest;x-friends:="org.eclipse.cdt.testsrunner.test" diff --git a/testsrunner/org.eclipse.cdt.testsrunner.gtest/about.html b/testsrunner/org.eclipse.cdt.testsrunner.gtest/about.html new file mode 100644 index 00000000000..d7c511887d6 --- /dev/null +++ b/testsrunner/org.eclipse.cdt.testsrunner.gtest/about.html @@ -0,0 +1,24 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"><head> +<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"><title>About</title></head> + +<body lang="EN-US"> +<h2>About This Content</h2> + +<p>June 22, 2007</p> +<h3>License</h3> + +<p>The Eclipse Foundation makes available all content in this plug-in ("Content"). Unless otherwise +indicated below, the Content is provided to you under the terms and conditions of the +Eclipse Public License Version 1.0 ("EPL"). A copy of the EPL is available +at <a href="http://www.eclipse.org/legal/epl-v10.html">http://www.eclipse.org/legal/epl-v10.html</a>. +For purposes of the EPL, "Program" will mean the Content.</p> + +<p>If you did not receive this Content directly from the Eclipse Foundation, the Content is +being redistributed by another party ("Redistributor") and different terms and conditions may +apply to your use of any object code in the Content. Check the Redistributor's license that was +provided with the Content. If no such license exists, contact the Redistributor. Unless otherwise +indicated below, the terms and conditions of the EPL still apply to any source code in the Content +and such source code may be obtained at <a href="http://www.eclipse.org/">http://www.eclipse.org</a>.</p> + +</body></html>
\ No newline at end of file diff --git a/testsrunner/org.eclipse.cdt.testsrunner.gtest/build.properties b/testsrunner/org.eclipse.cdt.testsrunner.gtest/build.properties new file mode 100644 index 00000000000..55d9be83320 --- /dev/null +++ b/testsrunner/org.eclipse.cdt.testsrunner.gtest/build.properties @@ -0,0 +1,21 @@ +############################################################################### +# Copyright (c) 2011 Anton Gorenkov +# 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: +# Anton Gorenkov - Initial implementation +############################################################################### + +bin.includes = plugin.xml,\ + plugin.properties,\ + about.html,\ + .,\ + META-INF/ +javadoc.packages = org.eclipse.cdt.launch.ui.*,\ + org.eclipse.cdt.launch.sourcelookup*,\ + org.eclipse.cdt.launch.* +source.. = src/ +src.includes = about.html diff --git a/testsrunner/org.eclipse.cdt.testsrunner.gtest/plugin.properties b/testsrunner/org.eclipse.cdt.testsrunner.gtest/plugin.properties new file mode 100644 index 00000000000..f96761da73b --- /dev/null +++ b/testsrunner/org.eclipse.cdt.testsrunner.gtest/plugin.properties @@ -0,0 +1,15 @@ +############################################################################### +# Copyright (c) 2011 Anton Gorenkov +# 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: +# Anton Gorenkov - Initial implementation +############################################################################### +pluginName=C/C++ Development Tools Google Tests Runner Support +providerName=Eclipse CDT + +GoogleTestsRunner.name=Google Tests Runner +GoogleTestsRunner.description=Tests runner for a test module based on Google C++ Testing Framework diff --git a/testsrunner/org.eclipse.cdt.testsrunner.gtest/plugin.xml b/testsrunner/org.eclipse.cdt.testsrunner.gtest/plugin.xml new file mode 100644 index 00000000000..152f28a03e2 --- /dev/null +++ b/testsrunner/org.eclipse.cdt.testsrunner.gtest/plugin.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8"?> +<?eclipse version="3.0"?> +<plugin> + <extension + point="org.eclipse.cdt.testsrunner.TestsRunner"> + <runner + class="org.eclipse.cdt.testsrunner.internal.gtest.GoogleTestsRunnerProvider" + description="%GoogleTestsRunner.description" + id="org.eclipse.cdt.testsrunner.gtest" + name="%GoogleTestsRunner.name"> + <features + multipleTestFilter="true"> + </features> + </runner> + </extension> +</plugin> diff --git a/testsrunner/org.eclipse.cdt.testsrunner.gtest/pom.xml b/testsrunner/org.eclipse.cdt.testsrunner.gtest/pom.xml new file mode 100644 index 00000000000..50dd36fb314 --- /dev/null +++ b/testsrunner/org.eclipse.cdt.testsrunner.gtest/pom.xml @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" + xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> + <modelVersion>4.0.0</modelVersion> + + <parent> + <groupId>org.eclipse.cdt</groupId> + <artifactId>cdt-parent</artifactId> + <version>8.1.0-SNAPSHOT</version> + <relativePath>../../pom.xml</relativePath> + </parent> + + <version>7.0.0-SNAPSHOT</version> + <artifactId>org.eclipse.cdt.testsrunner.gtest</artifactId> + <packaging>eclipse-plugin</packaging> +</project> diff --git a/testsrunner/org.eclipse.cdt.testsrunner.gtest/src/org/eclipse/cdt/testsrunner/internal/gtest/GoogleTestsRunnerMessages.java b/testsrunner/org.eclipse.cdt.testsrunner.gtest/src/org/eclipse/cdt/testsrunner/internal/gtest/GoogleTestsRunnerMessages.java new file mode 100644 index 00000000000..6aa1056514d --- /dev/null +++ b/testsrunner/org.eclipse.cdt.testsrunner.gtest/src/org/eclipse/cdt/testsrunner/internal/gtest/GoogleTestsRunnerMessages.java @@ -0,0 +1,35 @@ +/******************************************************************************* + * Copyright (c) 2011 Anton Gorenkov. + * 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: + * Anton Gorenkov - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.testsrunner.internal.gtest; + +import org.eclipse.osgi.util.NLS; + +public class GoogleTestsRunnerMessages extends NLS { + private static final String BUNDLE_NAME = "org.eclipse.cdt.testsrunner.internal.gtest.GoogleTestsRunnerMessages"; //$NON-NLS-1$ + public static String GoogleTestsRunner_error_format; + public static String GoogleTestsRunner_io_error_prefix; + public static String OutputHandler_getparam_message; + public static String OutputHandler_unexpected_case_end; + public static String OutputHandler_unexpected_output; + public static String OutputHandler_unexpected_suite_end; + public static String OutputHandler_unknown_error_prefix; + public static String OutputHandler_unknown_location_format; + public static String OutputHandler_unknown_test_status; + public static String OutputHandler_wrong_groups_count; + public static String OutputHandler_wrong_suite_name; + static { + // initialize resource bundle + NLS.initializeMessages(BUNDLE_NAME, GoogleTestsRunnerMessages.class); + } + + private GoogleTestsRunnerMessages() { + } +} diff --git a/testsrunner/org.eclipse.cdt.testsrunner.gtest/src/org/eclipse/cdt/testsrunner/internal/gtest/GoogleTestsRunnerMessages.properties b/testsrunner/org.eclipse.cdt.testsrunner.gtest/src/org/eclipse/cdt/testsrunner/internal/gtest/GoogleTestsRunnerMessages.properties new file mode 100644 index 00000000000..e329ed2d6e0 --- /dev/null +++ b/testsrunner/org.eclipse.cdt.testsrunner.gtest/src/org/eclipse/cdt/testsrunner/internal/gtest/GoogleTestsRunnerMessages.properties @@ -0,0 +1,21 @@ +############################################################################### +# Copyright (c) 2011 Anton Gorenkov +# 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: +# Anton Gorenkov - Initial implementation +############################################################################### +GoogleTestsRunner_error_format={0}: {1} +GoogleTestsRunner_io_error_prefix=I/O Error: +OutputHandler_getparam_message=Instantiated with GetParam() = {0} +OutputHandler_unexpected_case_end=End of test case "{0}" is not expected, because the last started case is "{1}". +OutputHandler_unexpected_output=Unexpected test module output. +OutputHandler_unexpected_suite_end=End of test suite "{0}" is not expected, because the last started suite is "{1}". +OutputHandler_unknown_error_prefix=Unknown error during parsing Google Test module output: +OutputHandler_unknown_location_format=Unknown location format. +OutputHandler_unknown_test_status=Test status value "{0}" is unknown. +OutputHandler_wrong_groups_count=State with pattern "{0}" should has {1} groups but has {2}. +OutputHandler_wrong_suite_name=A test case "{0}" belongs to test suite "{1}", but the last started suite is "{2}". diff --git a/testsrunner/org.eclipse.cdt.testsrunner.gtest/src/org/eclipse/cdt/testsrunner/internal/gtest/GoogleTestsRunnerPlugin.java b/testsrunner/org.eclipse.cdt.testsrunner.gtest/src/org/eclipse/cdt/testsrunner/internal/gtest/GoogleTestsRunnerPlugin.java new file mode 100644 index 00000000000..1ddb7430e81 --- /dev/null +++ b/testsrunner/org.eclipse.cdt.testsrunner.gtest/src/org/eclipse/cdt/testsrunner/internal/gtest/GoogleTestsRunnerPlugin.java @@ -0,0 +1,67 @@ +/******************************************************************************* + * Copyright (c) 2011 Anton Gorenkov + * 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: + * Anton Gorenkov - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.testsrunner.internal.gtest; + +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Plugin; +import org.eclipse.core.runtime.Status; + + +/** + * The activator class controls the plug-in life cycle + */ +public class GoogleTestsRunnerPlugin extends Plugin { + + /** The plug-in ID .*/ + public static final String PLUGIN_ID = "org.eclipse.cdt.testsrunner.gtest"; //$NON-NLS-1$ + + /** Plug-in instance. */ + private static GoogleTestsRunnerPlugin plugin; + + + public GoogleTestsRunnerPlugin() { + super(); + plugin = this; + } + + /** + * Returns the Boost Tests Runner provider plug-in instance. + * + * @return the plug-in instance + */ + public static GoogleTestsRunnerPlugin getDefault() { + return plugin; + } + + /** Convenience method which returns the unique identifier of this plugin. */ + public static String getUniqueIdentifier() { + return PLUGIN_ID; + } + + /** + * Logs the specified status with this plug-in's log. + * + * @param status status to log + */ + public static void log(IStatus status) { + getDefault().getLog().log(status); + } + + /** + * Logs an internal error with the specified throwable + * + * @param e the exception to be logged + */ + public static void log(Throwable e) { + log(new Status(IStatus.ERROR, getUniqueIdentifier(), IStatus.ERROR, e.getMessage(), e)); + } + +} diff --git a/testsrunner/org.eclipse.cdt.testsrunner.gtest/src/org/eclipse/cdt/testsrunner/internal/gtest/GoogleTestsRunnerProvider.java b/testsrunner/org.eclipse.cdt.testsrunner.gtest/src/org/eclipse/cdt/testsrunner/internal/gtest/GoogleTestsRunnerProvider.java new file mode 100644 index 00000000000..17ca3bd321e --- /dev/null +++ b/testsrunner/org.eclipse.cdt.testsrunner.gtest/src/org/eclipse/cdt/testsrunner/internal/gtest/GoogleTestsRunnerProvider.java @@ -0,0 +1,97 @@ +/******************************************************************************* + * Copyright (c) 2011 Anton Gorenkov + * 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: + * Anton Gorenkov - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.testsrunner.internal.gtest; + +import java.io.IOException; +import java.io.InputStream; +import java.text.MessageFormat; + +import org.eclipse.cdt.testsrunner.launcher.ITestsRunnerProvider; +import org.eclipse.cdt.testsrunner.model.ITestModelUpdater; +import org.eclipse.cdt.testsrunner.model.TestingException; + + +/** + * The Tests Runner provider plug-in to run tests with Google Testing framework. + * + * Parses the text test module to output and provides the data for the Tests + * Runner Plug-In. + */ +public class GoogleTestsRunnerProvider implements ITestsRunnerProvider { + + private static final String TEST_PATHS_DELIMITED = ":"; //$NON-NLS-1$ + private static final String TEST_PATH_PARTS_DELIMITED = "."; //$NON-NLS-1$ + private static final String ALL_TESTS= ".*"; //$NON-NLS-1$ + + + @Override + public String[] getAdditionalLaunchParameters(String[][] testPaths) { + final String[] gtestParameters = { + "--gtest_repeat=1", //$NON-NLS-1$ + "--gtest_print_time=1", //$NON-NLS-1$ + "--gtest_color=no", //$NON-NLS-1$ + }; + String[] result = gtestParameters; + + // Build tests filter + if (testPaths != null && testPaths.length >= 1) { + StringBuilder sb = new StringBuilder("--gtest_filter="); //$NON-NLS-1$ + boolean needTestPathDelimiter = false; + for (String[] testPath : testPaths) { + if (needTestPathDelimiter) { + sb.append(TEST_PATHS_DELIMITED); + } else { + needTestPathDelimiter = true; + } + boolean needTestPathPartDelimiter = false; + for (String testPathPart : testPath) { + if (needTestPathPartDelimiter) { + sb.append(TEST_PATH_PARTS_DELIMITED); + } else { + needTestPathPartDelimiter = true; + } + sb.append(testPathPart); + } + // If it is a test suite + if (testPath.length <= 1) { + sb.append(ALL_TESTS); + } + } + result = new String[gtestParameters.length + 1]; + System.arraycopy(gtestParameters, 0, result, 0, gtestParameters.length); + result[gtestParameters.length] = sb.toString(); + } + return result; + } + + /** + * Construct the error message from prefix and detailed description. + * + * @param prefix prefix + * @param description detailed description + * @return the full message + */ + private String getErrorText(String prefix, String description) { + return MessageFormat.format(GoogleTestsRunnerMessages.GoogleTestsRunner_error_format, prefix, description); + } + + @Override + public void run(ITestModelUpdater modelUpdater, InputStream inputStream) throws TestingException { + + try { + OutputHandler ouputHandler = new OutputHandler(modelUpdater); + ouputHandler.run(inputStream); + } catch (IOException e) { + throw new TestingException(getErrorText(GoogleTestsRunnerMessages.GoogleTestsRunner_io_error_prefix, e.getLocalizedMessage())); + } + } + +} diff --git a/testsrunner/org.eclipse.cdt.testsrunner.gtest/src/org/eclipse/cdt/testsrunner/internal/gtest/OutputHandler.java b/testsrunner/org.eclipse.cdt.testsrunner.gtest/src/org/eclipse/cdt/testsrunner/internal/gtest/OutputHandler.java new file mode 100644 index 00000000000..3a58563d5be --- /dev/null +++ b/testsrunner/org.eclipse.cdt.testsrunner.gtest/src/org/eclipse/cdt/testsrunner/internal/gtest/OutputHandler.java @@ -0,0 +1,645 @@ +/******************************************************************************* + * Copyright (c) 2011 Anton Gorenkov + * 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: + * Anton Gorenkov - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.testsrunner.internal.gtest; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.text.MessageFormat; +import java.util.HashMap; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.eclipse.cdt.testsrunner.model.ITestModelUpdater; +import org.eclipse.cdt.testsrunner.model.ITestItem; +import org.eclipse.cdt.testsrunner.model.ITestMessage; +import org.eclipse.cdt.testsrunner.model.TestingException; +import org.xml.sax.SAXException; + + +/** + * <p> + * Parses the output of Google Testing Framework and notifies the Tests Runner + * Core about how the testing process is going. + * </p> + * <p> + * Unfortunately, gtest does not provide a realtime XML output (yes, it has XML + * output, but it is generated after testing process is done), so we have to + * parse its output that is less reliable. + * </p> + * <p> + * The parsing is done with a simple FSM (Final State Machine). There is an + * internal state that changes when input tokens (gtest output lines) come. + * There is a transitions table that is used to determine what is the next state + * depending on the current one and the input token. The state may define + * onEnter and onExit actions to do the useful job. + * </p> + */ +public class OutputHandler { + + /** + * Base class for the FSM internal state. + */ + class State { + + /** Stores the regular expression by which the state should be entered. */ + private Pattern enterPattern; + + /** The regular expression matcher. */ + private Matcher matcher; + + /** Groups count in a regular expression. */ + private int groupCount; + + /** + * The constructor. + * + * @param enterRegex the regular expression by which the state should be + * entered + */ + State(String enterRegex) { + this(enterRegex, -1); + } + + /** + * The constructor. + * + * @param enterRegex the regular expression by which the state should be + * entered + * @param groupCount groups count in a regular expression. It is used + * just to make debug easier and the parser more reliable. + */ + State(String enterRegex, int groupCount) { + enterPattern = Pattern.compile(enterRegex); + this.groupCount = groupCount; + } + + /** + * Checks whether the specified string matches the enter pattern + * (regular expression). If it is so the state should be entered. + * + * @param line input line (token) + * @return true if matches and false otherwise + * @throws TestingException if groups count does not match the defined + * in constructor number. + */ + public boolean match(String line) throws TestingException { + matcher = enterPattern.matcher(line); + boolean groupsCountOk = groupCount == -1 || matcher.groupCount() == groupCount; + if (!groupsCountOk) { + generateInternalError( + MessageFormat.format( + GoogleTestsRunnerMessages.OutputHandler_wrong_groups_count, + enterPattern.pattern(), matcher.groupCount(), groupCount + ) + ); + } + boolean matches = matcher.matches(); + if (!matches || !groupsCountOk) { + // Do not keep the reference - it will be unnecessary anyway + matcher = null; + } + return matches; + } + + /** + * Returns the matched group value by index. + * + * @param groupNumber group index + * @return group value + */ + protected String group(int groupNumber) { + return matcher.group(groupNumber); + } + + /** + * Action that triggers on state enter. + * + * @param previousState previous state + * @throws TestingException if testing error is detected + */ + public void onEnter(State previousState) throws TestingException {} + + /** + * Action that triggers on state exit. + * + * @param previousState next state + * @throws TestingException if testing error is detected + */ + public void onExit(State nextState) {} + + /** + * Common routine that constructs full test suite name by name and type + * parameter. + * + * @param name test suite name + * @param typeParameter type parameter + * @return full test suite name + */ + protected String getTestSuiteName(String name, String typeParameter) { + return (typeParameter != null) ? MessageFormat.format("{0}({1})", name, typeParameter.trim()) : name; //$NON-NLS-1$ + } + } + + + /** + * The state is activated when a new test suite is started. + */ + class TestSuiteStart extends State { + + /** Stores the matched type parameter. */ + private String typeParameter; + + TestSuiteStart(String enterRegex, int groupCount) { + super(enterRegex, groupCount); + } + + /** + * Stores type parameter and notify Tests Runner Core about test suite + * start. + */ + @Override + public void onEnter(State previousState) { + typeParameter = group(3); + modelUpdater.enterTestSuite(getTestSuiteName(group(1), typeParameter)); + } + + /** + * Provides access to the matched type parameter. + * + * @return type parameter value + */ + public String getTypeParameter() { + return typeParameter; + } + } + + + /** + * The state is activated when a new test case is started. + */ + class TestCaseStart extends State { + + TestCaseStart(String enterRegex, int groupCount) { + super(enterRegex, groupCount); + } + + /** + * Extract current test case and test suite names and notify Tests + * Runner Core about test case start. + * + * @throws TestingException if extracted test suite name does not match + * last entered test suite name. + */ + @Override + public void onEnter(State previousState) throws TestingException { + String testCaseName = group(2); + String lastTestSuiteName = modelUpdater.currentTestSuite().getName(); + String currTestSuiteName = getTestSuiteName(group(1), stateTestSuiteStart.getTypeParameter()); + if (!lastTestSuiteName.equals(currTestSuiteName)) { + generateInternalError( + MessageFormat.format( + GoogleTestsRunnerMessages.OutputHandler_wrong_suite_name, + testCaseName, currTestSuiteName, lastTestSuiteName + ) + ); + } + modelUpdater.enterTestCase(testCaseName); + } + } + + + /** + * The state is activated when an error message's location is started. + */ + class ErrorMessageLocation extends State { + + /** Stores the message location file name. */ + private String messageFileName; + + /** Stores the message location line number. */ + private int messageLineNumber; + + /** Stores the first part of the message. */ + private String messagePart; + + ErrorMessageLocation(String enterRegex, int groupCount) { + super(enterRegex, groupCount); + } + + /** + * Extract the data for the message location (file name, line number). + * The data may be provided in a common style ("/path/file:line" with + * the message text starting on the next line) or Visual Studio style + * ("/path/file(line):" with the message text continuing on the same + * line). It is also possible not to specify line number at all + * ("/path/file:"). + * + * @throws TestingException if location format cannot be recognized. + */ + @Override + public void onEnter(State previousState) throws TestingException { + String fileNameIfLinePresent = group(2); + String fileNameIfLineAbsent = group(6); + String lineNumberCommon = group(4); + String lineNumberVS = group(5); + if (fileNameIfLinePresent != null) { + if (lineNumberCommon != null) { + messageFileName = fileNameIfLinePresent; + messageLineNumber = Integer.parseInt(lineNumberCommon.trim()); + } else if (lineNumberVS != null) { + messageFileName = fileNameIfLinePresent; + messageLineNumber = Integer.parseInt(lineNumberVS.trim()); + } else { + if (!modelUpdater.currentTestSuite().getName().equals(group(1))) { + generateInternalError(GoogleTestsRunnerMessages.OutputHandler_unknown_location_format); + } + } + } else if (fileNameIfLineAbsent != null) { + if (lineNumberCommon == null && lineNumberVS == null) { + messageFileName = fileNameIfLineAbsent; + messageLineNumber = DEFAULT_LOCATION_LINE; + } else { + generateInternalError(GoogleTestsRunnerMessages.OutputHandler_unknown_location_format); + } + } + // Check special case when file is not known - reset location + if (messageFileName.equals("unknown file")) { //$NON-NLS-1$ + messageFileName = DEFAULT_LOCATION_FILE; + } + // NOTE: For Visual Studio style there is also first part of the message at this line + messagePart = group(8); + } + + /** + * Provides access to the message location file name. + * + * @return file name + */ + public String getMessageFileName() { + return messageFileName; + } + + /** + * Provides access to the message location line number. + * + * @return line number + */ + public int getMessageLineNumber() { + return messageLineNumber; + } + + /** + * Provides access to the first part of the message. + * + * @return message part + */ + public String getMessagePart() { + return messagePart; + } + } + + + /** + * The state is activated when an error message text is started or continued. + */ + class ErrorMessage extends State { + + /** Stores the error message text that was already read. */ + private StringBuilder messagePart = new StringBuilder(); + + ErrorMessage(String enterRegex, int groupCount) { + super(enterRegex, groupCount); + } + + /** + * Collects the error message parts into internal buffer. If the + * previous state is not the same (it should be + * stateErrorMessageLocation) - get the message part from it. + */ + @Override + public void onEnter(State previousState) { + boolean needEndOfLine = (this == previousState); + if (this != previousState) { + String firstMessagePart = stateErrorMessageLocation.getMessagePart(); + if (firstMessagePart != null) { + messagePart.append(firstMessagePart); + needEndOfLine = true; + } + } + if (needEndOfLine) { + messagePart.append(System.getProperty("line.separator")); //$NON-NLS-1$ + } + messagePart.append(group(1)); + } + + /** + * Notifies the Tests Runner Core about new test message. + */ + @Override + public void onExit(State nextState) { + if (this != nextState) { + modelUpdater.addTestMessage( + stateErrorMessageLocation.getMessageFileName(), + stateErrorMessageLocation.getMessageLineNumber(), + ITestMessage.Level.Error, + messagePart.toString() + ); + messagePart.setLength(0); + } + } + } + + + /** + * The state is activated when a test trace is started or continued. + */ + class TestTrace extends ErrorMessageLocation { + + TestTrace(String enterRegex, int groupCount) { + super(enterRegex, groupCount); + } + + /** + * Notifies the Tests Runner Core about new test message with test trace + * info. + */ + @Override + public void onEnter(State previousState) throws TestingException { + super.onEnter(previousState); + modelUpdater.addTestMessage( + getMessageFileName(), + getMessageLineNumber(), + ITestMessage.Level.Info, + getMessagePart() + ); + } + } + + + /** + * The state is activated when a test case is finished. + */ + class TestCaseEnd extends State { + + TestCaseEnd(String enterRegex, int groupCount) { + super(enterRegex, groupCount); + } + + /** + * Sets the test case execution time, status and notify Tests Runner + * Core about test case end. + * + * @throws TestingException if current test suite or case name does not + * match last entered test suite or case name or if test status is not + * known. + */ + @Override + public void onEnter(State previousState) throws TestingException { + String lastTestSuiteName = modelUpdater.currentTestSuite().getName(); + String explicitTypeParameter = group(5); + String typeParameter = explicitTypeParameter != null ? explicitTypeParameter : stateTestSuiteStart.getTypeParameter(); + String currTestSuiteName = getTestSuiteName(group(2), typeParameter); + if (!lastTestSuiteName.equals(currTestSuiteName)) { + generateInternalError( + MessageFormat.format( + GoogleTestsRunnerMessages.OutputHandler_wrong_suite_name, + group(2), currTestSuiteName, lastTestSuiteName + ) + ); + } + String lastTestCaseName = modelUpdater.currentTestCase().getName(); + if (!lastTestCaseName.equals(group(3))) { + generateInternalError( + MessageFormat.format( + GoogleTestsRunnerMessages.OutputHandler_unexpected_case_end, + group(3), lastTestCaseName + ) + ); + } + String testStatusStr = group(1); + ITestItem.Status testStatus = ITestItem.Status.Skipped; + if (testStatusStr.equals(testStatusOk)) { + testStatus = ITestItem.Status.Passed; + } else if (testStatusStr.equals(testStatusFailed)) { + testStatus = ITestItem.Status.Failed; + } else { + generateInternalError(MessageFormat.format(GoogleTestsRunnerMessages.OutputHandler_unknown_test_status, testStatusStr)); + } + String getParamValue = group(7); + if (getParamValue != null) { + modelUpdater.addTestMessage( + DEFAULT_LOCATION_FILE, + DEFAULT_LOCATION_LINE, + ITestMessage.Level.Info, + MessageFormat.format(GoogleTestsRunnerMessages.OutputHandler_getparam_message, getParamValue) + ); + + } + modelUpdater.setTestingTime(Integer.parseInt(group(8))); + modelUpdater.setTestStatus(testStatus); + modelUpdater.exitTestCase(); + } + } + + + /** + * The state is activated when a test suite is finished. + */ + class TestSuiteEnd extends State { + + TestSuiteEnd(String enterRegex, int groupCount) { + super(enterRegex, groupCount); + } + + /** + * Notify Tests Runner Core about test suite end. + * + * @throws TestingException if current test suite name does not match + * last entered test suite name. + */ + @Override + public void onEnter(State previousState) throws TestingException { + String lastTestSuiteName = modelUpdater.currentTestSuite().getName(); + String currTestSuiteName = getTestSuiteName(group(1), stateTestSuiteStart.getTypeParameter()); + if (!lastTestSuiteName.equals(currTestSuiteName)) { + generateInternalError( + MessageFormat.format( + GoogleTestsRunnerMessages.OutputHandler_unexpected_suite_end, + currTestSuiteName, lastTestSuiteName + ) + ); + } + modelUpdater.exitTestSuite(); + } + } + + + /** The default file name for test message location. */ + private static final String DEFAULT_LOCATION_FILE = null; + + /** The default line number for test message location. */ + private static final int DEFAULT_LOCATION_LINE = 1; + + // Common regular expression parts + static private String regexTestSuiteName = "([^,]+)"; //$NON-NLS-1$ + static private String regexParameterInstantiation = "(\\s*,\\s+where\\s+TypeParam\\s*=([^,(]+))?"; //$NON-NLS-1$ + static private String regexTestName = regexTestSuiteName+"\\.([^,]+)"; //$NON-NLS-1$ + static private String regexTestCount = "\\d+\\s+tests?"; //$NON-NLS-1$ + static private String regexTestTime = "(\\d+)\\s+ms"; //$NON-NLS-1$ + /* Matches location in the following formats: + * - /file:line: + * - /file(line): + * - /file: (with no line number specified) + * Groups: + * 1 - all except ":" + * 2 - file name (if line present) * + * 3 - line number with delimiters + * 4 - line number (common style) * + * 5 - line number (Visual Studio style) * + * 6 - file name (if no line number specified) * + * Using: + * - group 2 with 4 or 5 (if line number was specified) + * - group 6 (if filename only was specified) + */ + static private String regexLocation = "((.*)(:(\\d+)|\\((\\d+)\\))|(.*[^):])):"; //$NON-NLS-1$ + + // Test statuses representation + static private String testStatusOk = "OK"; //$NON-NLS-1$ + static private String testStatusFailed = "FAILED"; //$NON-NLS-1$ + + + // All available states in FSM + private State stateInitial = new State(""); //$NON-NLS-1$ + private State stateInitialized = new State(".*Global test environment set-up.*"); //$NON-NLS-1$ + private TestSuiteStart stateTestSuiteStart = new TestSuiteStart("\\[-*\\]\\s+"+regexTestCount+"\\s+from\\s+"+regexTestSuiteName+regexParameterInstantiation, 3); //$NON-NLS-1$ //$NON-NLS-2$ + private State stateTestCaseStart = new TestCaseStart("\\[\\s*RUN\\s*\\]\\s+"+regexTestName, 2); //$NON-NLS-1$ + private ErrorMessageLocation stateErrorMessageLocation = new ErrorMessageLocation(regexLocation+"\\s+(Failure|error: (.*))", 8); //$NON-NLS-1$ + private State stateErrorMessage = new ErrorMessage("(.*)", 1); //$NON-NLS-1$ + private State stateTestTraceStart = new State(".*Google Test trace.*"); //$NON-NLS-1$ + // NOTE: Use 8 groups instead of 7 cause we need to be consistent with ErrorMessageLocation (as we subclass it) + private State stateTestTrace = new TestTrace(regexLocation+"\\s+((.*))", 8); //$NON-NLS-1$ + private State stateTestCaseEnd = new TestCaseEnd("\\[\\s*("+testStatusOk+"|"+testStatusFailed+")\\s*\\]\\s+"+regexTestName+regexParameterInstantiation+"(\\s*,\\s+where\\s+GetParam\\s*\\(\\s*\\)\\s*=\\s*(.+))?\\s+\\("+regexTestTime+"\\)", 8); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ + private State stateTestSuiteEnd = new TestSuiteEnd("\\[-*\\]\\s+"+regexTestCount+"\\s+from\\s+"+regexTestSuiteName+"\\s+\\("+regexTestTime+"\\s+total\\)", 2); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ + private State stateFinal = new State(".*Global test environment tear-down.*"); //$NON-NLS-1$ + // NOTE: This state is a special workaround for empty test modules (they haven't got global test environment set-up/tear-down). They should be always passed. + private State stateEmptyTestModuleFinal = new State(".*\\[\\s*PASSED\\s*\\]\\s+0\\s+tests.*"); //$NON-NLS-1$ + + // Transitions table + private Map<State, State[] > transitions = new HashMap<State, State[]>(); + { + // NOTE: Next states order is important! + transitions.put( from(stateInitial), to(stateInitialized, stateEmptyTestModuleFinal) ); + transitions.put( from(stateInitialized), to(stateTestSuiteStart) ); + transitions.put( from(stateTestSuiteStart), to(stateTestCaseStart) ); + transitions.put( from(stateTestCaseStart), to(stateTestCaseEnd, stateErrorMessageLocation) ); + transitions.put( from(stateErrorMessageLocation), to(stateTestTraceStart, stateTestCaseEnd, stateErrorMessageLocation, stateErrorMessage) ); + transitions.put( from(stateErrorMessage), to(stateTestTraceStart, stateTestCaseEnd, stateErrorMessageLocation, stateErrorMessage) ); + transitions.put( from(stateTestTraceStart), to(stateTestTrace) ); + transitions.put( from(stateTestTrace), to(stateTestCaseEnd, stateErrorMessageLocation, stateTestTrace) ); + transitions.put( from(stateTestCaseEnd), to(stateTestCaseStart, stateTestSuiteEnd) ); + transitions.put( from(stateTestSuiteEnd), to(stateTestSuiteStart, stateFinal) ); + } + + /** Current FSM state. */ + private State currentState; + + /** The interface to notify the Tests Runner Core */ + private ITestModelUpdater modelUpdater; + + + OutputHandler(ITestModelUpdater modelUpdater) { + this.modelUpdater = modelUpdater; + } + + /** + * Runs the parsing process. Initializes the FSM, selects new states with + * transitions table and checks whether the parsing completes successfully. + * + * @param inputStream gtest test module output stream + * @throws IOException if stream reading error happens + * @throws TestingException if testing error happens + */ + public void run(InputStream inputStream) throws IOException, TestingException { + // Initialize input stream reader + InputStreamReader streamReader = new InputStreamReader(inputStream); + BufferedReader reader = new BufferedReader(streamReader); + String line; + boolean finalizedProperly = false; + + // Initialize internal state + currentState = stateInitial; + while ( ( line = reader.readLine() ) != null ) { + // Search for the next possible state + State[] possibleNextStates = transitions.get(currentState); + if (possibleNextStates == null) { + // Final state, stop running + finalizedProperly = true; + break; + } + for (State nextState : possibleNextStates) { + if (nextState.match(line)) { + // Next state found - send notifications to the states + currentState.onExit(nextState); + State previousState = currentState; + currentState = nextState; + nextState.onEnter(previousState); + break; + } + } + // NOTE: We cannot be sure that we cover all the output of gtest with our regular expressions + // (e.g. some framework notes or warnings may be uncovered etc.), so we just skip unmatched + // lines without an error + } + // Check whether the last line leads to the final state + if (transitions.get(currentState) == null) { + finalizedProperly = true; + } + if (!finalizedProperly) { + generateInternalError(GoogleTestsRunnerMessages.OutputHandler_unexpected_output); + } + } + + /** + * Throws the testing exception with unknown internal error prefix and the specified description. + * + * @param additionalInfo additional description of what happens + * @throws SAXException the exception that will be thrown + */ + private void generateInternalError(String additionalInfo) throws TestingException { + TestingException e = new TestingException(GoogleTestsRunnerMessages.OutputHandler_unknown_error_prefix+additionalInfo); + GoogleTestsRunnerPlugin.log(e); + throw e; + } + + /** + * Helper functions to make code more readable. + * + * @param fromState state to return + * @return passed state + */ + private State from(State fromState) { + return fromState; + } + + /** + * Helper functions to make code more readable. + * + * @param toStates states array to return + * @return passed states array + */ + private State[] to(State... toStates) { + return toStates; + } + +} |