http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/116637b8/utils/test-support/src/main/java/org/apache/brooklyn/test/VerboseReporter.java ---------------------------------------------------------------------- diff --git a/utils/test-support/src/main/java/org/apache/brooklyn/test/VerboseReporter.java b/utils/test-support/src/main/java/org/apache/brooklyn/test/VerboseReporter.java deleted file mode 100644 index 0ec78e1..0000000 --- a/utils/test-support/src/main/java/org/apache/brooklyn/test/VerboseReporter.java +++ /dev/null @@ -1,343 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.brooklyn.test; - -import java.util.List; - -import org.testng.ITestContext; -import org.testng.ITestNGMethod; -import org.testng.ITestResult; -import org.testng.TestListenerAdapter; -import org.testng.internal.ConstructorOrMethod; -import org.testng.internal.Utils; - -/** - * Reporter printing out detailed messages about what TestNG - * is going to run and what is the status of what has been just run. - * - * To see messages from this reporter, either run Ant in verbose mode ('ant -v') - * or set verbose level to 5 or higher - * - * @author Lukas Jungmann - * @since 6.4 - * - * THIS IS AN EXACT COPY OF THE CLASS IN org.testng.reporters - * EXCEPT FOR THIS COMMENT AND log(String) CHANGED TO BE PROTECTED - */ -public class VerboseReporter extends TestListenerAdapter { - - /** - * Default prefix for messages printed out by this reporter - * - */ - public static final String LISTENER_PREFIX = "[VerboseTestNG] "; - private String suiteName; - private final String prefix; - - private enum Status { - - SUCCESS(ITestResult.SUCCESS), FAILURE(ITestResult.FAILURE), SKIP(ITestResult.SKIP), - SUCCESS_PERCENTAGE_FAILURE(ITestResult.SUCCESS_PERCENTAGE_FAILURE), STARTED(ITestResult.STARTED); - private final int code; - - private Status(int i) { - code = i; - } - - @SuppressWarnings("unused") - public int getCode() { - return code; - } - } - - /** - * Default constructor - */ - public VerboseReporter() { - this(LISTENER_PREFIX); - } - - /** - * Create VerboseReporter with custom prefix - * - * @param prefix prefix for messages printed out by this reporter - */ - public VerboseReporter(String prefix) { - this.prefix = prefix; - } - - @Override - public void beforeConfiguration(ITestResult tr) { - super.beforeConfiguration(tr); - logTestResult(Status.STARTED, tr, true); - } - - @Override - public void onConfigurationFailure(ITestResult tr) { - super.onConfigurationFailure(tr); - logTestResult(Status.FAILURE, tr, true); - } - - @Override - public void onConfigurationSkip(ITestResult tr) { - super.onConfigurationSkip(tr); - logTestResult(Status.SKIP, tr, true); - } - - @Override - public void onConfigurationSuccess(ITestResult tr) { - super.onConfigurationSuccess(tr); - logTestResult(Status.SUCCESS, tr, true); - } - - @Override - public void onTestStart(ITestResult tr) { - logTestResult(Status.STARTED, tr, false); - } - - @Override - public void onTestFailure(ITestResult tr) { - super.onTestFailure(tr); - logTestResult(Status.FAILURE, tr, false); - } - - @Override - public void onTestFailedButWithinSuccessPercentage(ITestResult tr) { - super.onTestFailedButWithinSuccessPercentage(tr); - logTestResult(Status.SUCCESS_PERCENTAGE_FAILURE, tr, false); - } - - @Override - public void onTestSkipped(ITestResult tr) { - super.onTestSkipped(tr); - logTestResult(Status.SKIP, tr, false); - } - - @Override - public void onTestSuccess(ITestResult tr) { - super.onTestSuccess(tr); - logTestResult(Status.SUCCESS, tr, false); - } - - @Override - public void onStart(ITestContext ctx) { - suiteName = ctx.getName();//ctx.getSuite().getXmlSuite().getFileName(); - log("RUNNING: Suite: \"" + suiteName + "\" containing \"" + ctx.getAllTestMethods().length + "\" Tests (config: " + ctx.getSuite().getXmlSuite().getFileName() + ")"); - } - - @Override - public void onFinish(ITestContext context) { - logResults(); - suiteName = null; - } - - private ITestNGMethod[] resultsToMethods(List<ITestResult> results) { - ITestNGMethod[] result = new ITestNGMethod[results.size()]; - int i = 0; - for (ITestResult tr : results) { - result[i++] = tr.getMethod(); - } - return result; - } - - /** - * Print out test summary - */ - private void logResults() { - // - // Log test summary - // - ITestNGMethod[] ft = resultsToMethods(getFailedTests()); - StringBuilder sb = new StringBuilder("\n===============================================\n"); - sb.append(" ").append(suiteName).append("\n"); - sb.append(" Tests run: ").append(Utils.calculateInvokedMethodCount(getAllTestMethods())); - sb.append(", Failures: ").append(Utils.calculateInvokedMethodCount(ft)); - sb.append(", Skips: ").append(Utils.calculateInvokedMethodCount(resultsToMethods(getSkippedTests()))); - int confFailures = getConfigurationFailures().size(); - int confSkips = getConfigurationSkips().size(); - if (confFailures > 0 || confSkips > 0) { - sb.append("\n").append(" Configuration Failures: ").append(confFailures); - sb.append(", Skips: ").append(confSkips); - } - sb.append("\n==============================================="); - log(sb.toString()); - } - - /** - * Log meaningful message for passed in arguments. - * Message itself is of form: - * $status: "$suiteName" - $methodDeclaration ($actualArguments) finished in $x ms ($run of $totalRuns) - * - * @param st status of passed in itr - * @param itr test result to be described - * @param isConfMethod is itr describing configuration method - */ - private void logTestResult(Status st, ITestResult itr, boolean isConfMethod) { - StringBuilder sb = new StringBuilder(); - String stackTrace = ""; - switch (st) { - case STARTED: - sb.append("INVOKING"); - break; - case SKIP: - sb.append("SKIPPED"); - stackTrace = itr.getThrowable() != null - ? Utils.stackTrace(itr.getThrowable(), false)[0] : ""; - break; - case FAILURE: - sb.append("FAILED"); - stackTrace = itr.getThrowable() != null - ? Utils.stackTrace(itr.getThrowable(), false)[0] : ""; - break; - case SUCCESS: - sb.append("PASSED"); - break; - case SUCCESS_PERCENTAGE_FAILURE: - sb.append("PASSED with failures"); - break; - default: - //not happen - throw new RuntimeException("Unsupported test status:" + itr.getStatus()); - } - if (isConfMethod) { - sb.append(" CONFIGURATION: "); - } else { - sb.append(": "); - } - ITestNGMethod tm = itr.getMethod(); - int identLevel = sb.length(); - sb.append(getMethodDeclaration(tm)); - Object[] params = itr.getParameters(); - Class<?>[] paramTypes = tm.getConstructorOrMethod().getParameterTypes(); - if (null != params && params.length > 0) { - // The error might be a data provider parameter mismatch, so make - // a special case here - if (params.length != paramTypes.length) { - sb.append("Wrong number of arguments were passed by the Data Provider: found "); - sb.append(params.length); - sb.append(" but expected "); - sb.append(paramTypes.length); - } else { - sb.append("(value(s): "); - for (int i = 0; i < params.length; i++) { - if (i > 0) { - sb.append(", "); - } - sb.append(Utils.toString(params[i], paramTypes[i])); - } - sb.append(")"); - - } - } - if (Status.STARTED != st) { - sb.append(" finished in "); - sb.append(itr.getEndMillis() - itr.getStartMillis()); - sb.append(" ms"); - if (!Utils.isStringEmpty(tm.getDescription())) { - sb.append("\n"); - for (int i = 0; i < identLevel; i++) { - sb.append(" "); - } - sb.append(tm.getDescription()); - } - if (tm.getInvocationCount() > 1) { - sb.append(" ("); - sb.append(tm.getCurrentInvocationCount()); - sb.append(" of "); - sb.append(tm.getInvocationCount()); - sb.append(")"); - } - if (!Utils.isStringEmpty(stackTrace)) { - sb.append("\n").append(stackTrace.substring(0, stackTrace.lastIndexOf(System.getProperty("line.separator")))); - } - } else { - if (!isConfMethod && tm.getInvocationCount() > 1) { - sb.append(" success: "); - sb.append(tm.getSuccessPercentage()); - sb.append("%"); - } - } - log(sb.toString()); - } - - protected void log(String message) { - //prefix all output lines - System.out.println(message.replaceAll("(?m)^", prefix)); - } - - /** - * - * @param method method to be described - * @return FQN of a class + method declaration for a method passed in - * ie. test.triangle.CheckCount.testCheckCount(java.lang.String) - */ - private String getMethodDeclaration(ITestNGMethod method) { - //see Utils.detailedMethodName - //perhaps should rather adopt the original method instead - ConstructorOrMethod m = method.getConstructorOrMethod(); - StringBuilder buf = new StringBuilder(); - buf.append("\""); - if (suiteName != null) { - buf.append(suiteName); - } else { - buf.append("UNKNOWN"); - } - buf.append("\""); - buf.append(" - "); - if (method.isBeforeSuiteConfiguration()) { - buf.append("@BeforeSuite "); - } else if (method.isBeforeTestConfiguration()) { - buf.append("@BeforeTest "); - } else if (method.isBeforeClassConfiguration()) { - buf.append("@BeforeClass "); - } else if (method.isBeforeGroupsConfiguration()) { - buf.append("@BeforeGroups "); - } else if (method.isBeforeMethodConfiguration()) { - buf.append("@BeforeMethod "); - } else if (method.isAfterMethodConfiguration()) { - buf.append("@AfterMethod "); - } else if (method.isAfterGroupsConfiguration()) { - buf.append("@AfterGroups "); - } else if (method.isAfterClassConfiguration()) { - buf.append("@AfterClass "); - } else if (method.isAfterTestConfiguration()) { - buf.append("@AfterTest "); - } else if (method.isAfterSuiteConfiguration()) { - buf.append("@AfterSuite "); - } - buf.append(m.getDeclaringClass().getName()); - buf.append("."); - buf.append(m.getName()); - buf.append("("); - int i = 0; - for (Class<?> p : m.getParameterTypes()) { - if (i++ > 0) { - buf.append(", "); - } - buf.append(p.getName()); - } - buf.append(")"); - return buf.toString(); - } - - @Override - public String toString() { - return "VerboseReporter{" + "suiteName=" + suiteName + '}'; - } -}
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/116637b8/utils/test-support/src/main/java/org/apache/brooklyn/test/support/BrooklynLeakListener.java ---------------------------------------------------------------------- diff --git a/utils/test-support/src/main/java/org/apache/brooklyn/test/support/BrooklynLeakListener.java b/utils/test-support/src/main/java/org/apache/brooklyn/test/support/BrooklynLeakListener.java new file mode 100644 index 0000000..6147d7c --- /dev/null +++ b/utils/test-support/src/main/java/org/apache/brooklyn/test/support/BrooklynLeakListener.java @@ -0,0 +1,89 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.brooklyn.test.support; + +import java.util.Arrays; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.testng.ITestContext; +import org.testng.TestListenerAdapter; + +public class BrooklynLeakListener extends TestListenerAdapter { + + private static final Logger TEST_RESOURCE_LOG = LoggerFactory.getLogger("test.resource.usage"); + + @Override + public void onStart(ITestContext testContext) { + super.onStart(testContext); + tryTerminateAll("BrooklynLeakListener.onStart", testContext); + } + + @Override + public void onFinish(ITestContext testContext) { + super.onFinish(testContext); + tryTerminateAll("BrooklynLeakListener.onFinish", testContext); + } + + /** + * Tries to reflectively invoke {@code LocalManagementContext.logAll(TEST_RESOURCE_LOG); LocalManagementContext.terminateAll()}. + * + * It does this reflectively because the listener is executed for all projects, included those that don't + * depend on brooklyn-core, so LocalManagementContext may not be on the classpath. + */ + private void tryTerminateAll(String context, ITestContext testContext) { + String clazzName = "org.apache.brooklyn.core.management.internal.LocalManagementContext"; + String message; + int level; + try { + Class<?> clazz = BrooklynLeakListener.class.getClassLoader().loadClass(clazzName); + clazz.getMethod("logAll", new Class[] {Logger.class}).invoke(null, TEST_RESOURCE_LOG); + Integer count = (Integer)clazz.getMethod("terminateAll").invoke(null); + if (count>0) { + level = 0; + message = ""+count+" ManagementContexts terminated"; + } else if (count<0) { + level = 1; + message = ""+(-count)+" ManagementContexts left dangling"; + } else { + level = -1; + message = ""+count+" ManagementContexts terminated"; + } + } catch (ClassNotFoundException e) { + TEST_RESOURCE_LOG.debug("Class {} not found in testng listener, so not attempting to terminate all extant ManagementContexts; continuing", clazzName); + level = 0; + message = "no "+clazzName+" available, so skipped"; + } catch (Throwable e) { + TEST_RESOURCE_LOG.error("ERROR in testng listener, attempting to terminate all extant ManagementContexts", e); + level = 1; + message = "ERROR: "+e; + } + + String logMessage = context+" attempting to terminate all extant ManagementContexts: " + + "name=" + testContext.getName() + + "; includedGroups="+Arrays.toString(testContext.getIncludedGroups()) + + "; excludedGroups="+Arrays.toString(testContext.getExcludedGroups()) + + "; suiteName="+testContext.getSuite().getName() + + "; outDir="+testContext.getOutputDirectory() + + ": "+message; + if (level<0) TEST_RESOURCE_LOG.debug(logMessage); + else if (level>0) TEST_RESOURCE_LOG.warn(logMessage); + else TEST_RESOURCE_LOG.info(logMessage); + } +} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/116637b8/utils/test-support/src/main/java/org/apache/brooklyn/test/support/LoggingVerboseReporter.java ---------------------------------------------------------------------- diff --git a/utils/test-support/src/main/java/org/apache/brooklyn/test/support/LoggingVerboseReporter.java b/utils/test-support/src/main/java/org/apache/brooklyn/test/support/LoggingVerboseReporter.java new file mode 100644 index 0000000..c81c6fd --- /dev/null +++ b/utils/test-support/src/main/java/org/apache/brooklyn/test/support/LoggingVerboseReporter.java @@ -0,0 +1,36 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.brooklyn.test.support; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class LoggingVerboseReporter extends VerboseReporter { + + private static final Logger log = LoggerFactory.getLogger(LoggingVerboseReporter.class); + + public LoggingVerboseReporter() { super(); } + public LoggingVerboseReporter(String prefix) { super(prefix); } + + @Override + protected void log(String message) { + log.info("TESTNG "+message); + } + +} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/116637b8/utils/test-support/src/main/java/org/apache/brooklyn/test/support/PlatformTestSelectorListener.java ---------------------------------------------------------------------- diff --git a/utils/test-support/src/main/java/org/apache/brooklyn/test/support/PlatformTestSelectorListener.java b/utils/test-support/src/main/java/org/apache/brooklyn/test/support/PlatformTestSelectorListener.java new file mode 100644 index 0000000..5ef7ba0 --- /dev/null +++ b/utils/test-support/src/main/java/org/apache/brooklyn/test/support/PlatformTestSelectorListener.java @@ -0,0 +1,57 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.brooklyn.test.support; + +import org.testng.IInvokedMethod; +import org.testng.IInvokedMethodListener; +import org.testng.ITestResult; +import org.testng.SkipException; + +public class PlatformTestSelectorListener implements IInvokedMethodListener { + private static final String GROUP_UNIX = "UNIX"; + private static final String GROUP_WINDOWS = "Windows"; + + public static boolean isWindows() { + return System.getProperty("os.name").startsWith("Windows"); + } + + @Override + public void beforeInvocation(IInvokedMethod method, ITestResult testResult) { + boolean isUnixTest = false; + boolean isWinTest = false; + + String[] groups = method.getTestMethod().getGroups(); + for (String group : groups) { + isUnixTest = isUnixTest || group.equalsIgnoreCase(GROUP_UNIX); + isWinTest = isWinTest || group.equalsIgnoreCase(GROUP_WINDOWS); + } + + boolean isWinPlatform = isWindows(); + if (isUnixTest || isWinTest) { + if (isWinPlatform && isUnixTest && !isWinTest) { + throw new SkipException("Skipping unix-specific test."); + } else if (!isWinPlatform && isWinTest && !isUnixTest) { + throw new SkipException("Skipping windows-specific test."); + } + } + } + + @Override + public void afterInvocation(IInvokedMethod method, ITestResult testResult) {} +} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/116637b8/utils/test-support/src/main/java/org/apache/brooklyn/test/support/StatusListener.java ---------------------------------------------------------------------- diff --git a/utils/test-support/src/main/java/org/apache/brooklyn/test/support/StatusListener.java b/utils/test-support/src/main/java/org/apache/brooklyn/test/support/StatusListener.java new file mode 100644 index 0000000..fcf7bb2 --- /dev/null +++ b/utils/test-support/src/main/java/org/apache/brooklyn/test/support/StatusListener.java @@ -0,0 +1,100 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.brooklyn.test.support; + +import java.util.concurrent.atomic.AtomicInteger; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.testng.IClass; +import org.testng.ITestContext; +import org.testng.ITestListener; +import org.testng.ITestResult; + +/** + * adapted from the following class: + * + * @see org.jclouds.test.testng.UnitTestStatusListener + * + * normally not used, preferring instead LoggingVerboseReporter which prints out config info + */ +public class StatusListener implements ITestListener { + + public static final Logger log = LoggerFactory.getLogger(StatusListener.class); + + /** + * Holds test classes actually running in all threads. + */ + private ThreadLocal<IClass> threadTestClass = new ThreadLocal<IClass>(); + private ThreadLocal<Long> threadTestStart = new ThreadLocal<Long>(); + + private AtomicInteger failed = new AtomicInteger(0); + private AtomicInteger succeded = new AtomicInteger(0); + private AtomicInteger skipped = new AtomicInteger(0); + + //TODO instead of system.out.println we should log -- *and* perhaps write to sysout if logger doesn't? + protected static void log(String msg) { + log.info(msg); + } + + public void onTestStart(ITestResult res) { + log("Starting test " + getTestDesc(res)); + threadTestClass.set(res.getTestClass()); + threadTestStart.set(System.currentTimeMillis()); + } + + synchronized public void onTestSuccess(ITestResult arg0) { + log(getThreadId() + " Test " + getTestDesc(arg0) + " succeeded: " + (System.currentTimeMillis() - threadTestStart.get()) + "ms"); + succeded.incrementAndGet(); + printStatus(); + } + + synchronized public void onTestFailure(ITestResult arg0) { + log(getThreadId() + " Test " + getTestDesc(arg0) + " failed: "+arg0.getThrowable()); + failed.incrementAndGet(); + printStatus(); + } + + synchronized public void onTestSkipped(ITestResult arg0) { + System.out.println(getThreadId() + " Test " + getTestDesc(arg0) + " skipped."); + skipped.incrementAndGet(); + printStatus(); + } + + public void onTestFailedButWithinSuccessPercentage(ITestResult arg0) { + } + + public void onStart(ITestContext arg0) { + } + + public void onFinish(ITestContext arg0) { + } + + private String getThreadId() { + return "[" + Thread.currentThread().getName() + "]"; + } + + private String getTestDesc(ITestResult res) { + return res.getMethod().getMethodName() + "(" + res.getTestClass().getName() + ")"; + } + + private void printStatus() { + log("Test suite progress: tests succeeded: " + succeded.get() + ", failed: " + failed.get() + ", skipped: " + skipped.get() + "."); + } +} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/116637b8/utils/test-support/src/main/java/org/apache/brooklyn/test/support/TestResourceUnavailableException.java ---------------------------------------------------------------------- diff --git a/utils/test-support/src/main/java/org/apache/brooklyn/test/support/TestResourceUnavailableException.java b/utils/test-support/src/main/java/org/apache/brooklyn/test/support/TestResourceUnavailableException.java new file mode 100644 index 0000000..1596801 --- /dev/null +++ b/utils/test-support/src/main/java/org/apache/brooklyn/test/support/TestResourceUnavailableException.java @@ -0,0 +1,140 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.brooklyn.test.support; + +import com.google.common.base.Throwables; +import org.testng.SkipException; + +import java.io.IOException; +import java.io.InputStream; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Indicates that a test cannot be run as a necessary resource is not available. + * + * <p>This exception should be used by tests that need a particular test resource (i.e., a file that is conventionally + * in the <code>src/test/resources</code> folder, and that available as a classpath resource at runtime) is not + * available. It will cause TestNG to mark the test as "skipped" with a suitable message.</p> + * + * <p>Some tests require binary files (such as OSGi bundles) which under Apache conventions are not able to be + * distributed in our source code release. This exception allows such tests to become "optional" so that they do not + * cause test failures when running the tests on a source distribution.</p> + * + * <p>Note that the single-string constructors expect the string to be the simple name of the classpath resource that + * is not available. The exception message is then derived from this. The two-string constructors take both the + * resource name and an explicit exception message.</p> + */ +@SuppressWarnings("UnusedDeclaration") +public class TestResourceUnavailableException extends SkipException { + + private final String resourceName; + + /** + * Asserts that a resource is available on the classpath; otherwise, throws {@link TestResourceUnavailableException} + * + * Note that this will use the same classloader that was used to load this class. + * + * @param resourceName the classpath resource name, e.g. + * <code>/brooklyn/osgi/brooklyn-test-osgi-entities.jar</code> + */ + public static void throwIfResourceUnavailable(Class<?> relativeToClass, String resourceName) { + checkNotNull(relativeToClass, relativeToClass); + checkNotNull(resourceName, "resourceName"); + checkArgument(!resourceName.isEmpty(), "resourceName must not be empty"); + InputStream resource = relativeToClass.getResourceAsStream(resourceName); + if (resource == null) + throw new TestResourceUnavailableException(resourceName); + + // just make sure we clean up the resource + try { + resource.close(); + } catch (IOException e) { + Throwables.propagate(e); + } + } + + /** + * Instantiate an exception, giving the name of the unavailable resource. + * + * @param resourceName the name of the resource on the classpath, e.g. + * <code>/brooklyn/osgi/brooklyn-test-osgi-entities.jar</code> + */ + public TestResourceUnavailableException(String resourceName) { + super(messageFromResourceName(resourceName)); + this.resourceName = resourceName; + } + + /** + * Instantiate an exception, giving the name of the unavailable resource. + * + * @param resourceName the name of the resource on the classpath, e.g. + * <code>/brooklyn/osgi/brooklyn-test-osgi-entities.jar</code> + * @param cause the underlying exception that caused this one + */ + public TestResourceUnavailableException(String resourceName, Throwable cause) { + super(messageFromResourceName(resourceName), cause); + this.resourceName = resourceName; + } + + /** + * Instantiate an exception, giving the name of the unavailable resource. + * + * @param resourceName the name of the resource on the classpath, e.g. + * <code>/brooklyn/osgi/brooklyn-test-osgi-entities.jar</code> + * @param skipMessage the message associated with the exception + */ + public TestResourceUnavailableException(String resourceName, String skipMessage) { + super(skipMessage); + this.resourceName = resourceName; + } + + /** + * Instantiate an exception, giving the name of the unavailable resource. + * + * @param resourceName the name of the resource on the classpath, e.g. + * <code>/brooklyn/osgi/brooklyn-test-osgi-entities.jar</code> + * @param skipMessage the message associated with the exception + * @param cause the underlying exception that caused this one + */ + public TestResourceUnavailableException(String resourceName, String skipMessage, Throwable cause) { + super(skipMessage, cause); + this.resourceName = resourceName; + } + + private static String messageFromResourceName(String resourceName) { + return String.format("Test resource '%s' not found; test skipped.", resourceName); + } + + /** + * Get the name of the classpath resource that could not be loaded. + * + * @return the name of the classpath resource whose absence caused this exception. + */ + public String getResourceName() { + return resourceName; + } + + @Override + public boolean isSkip() { + return true; + } + +} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/116637b8/utils/test-support/src/main/java/org/apache/brooklyn/test/support/VerboseReporter.java ---------------------------------------------------------------------- diff --git a/utils/test-support/src/main/java/org/apache/brooklyn/test/support/VerboseReporter.java b/utils/test-support/src/main/java/org/apache/brooklyn/test/support/VerboseReporter.java new file mode 100644 index 0000000..adcb529 --- /dev/null +++ b/utils/test-support/src/main/java/org/apache/brooklyn/test/support/VerboseReporter.java @@ -0,0 +1,343 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.brooklyn.test.support; + +import java.util.List; + +import org.testng.ITestContext; +import org.testng.ITestNGMethod; +import org.testng.ITestResult; +import org.testng.TestListenerAdapter; +import org.testng.internal.ConstructorOrMethod; +import org.testng.internal.Utils; + +/** + * Reporter printing out detailed messages about what TestNG + * is going to run and what is the status of what has been just run. + * + * To see messages from this reporter, either run Ant in verbose mode ('ant -v') + * or set verbose level to 5 or higher + * + * @author Lukas Jungmann + * @since 6.4 + * + * THIS IS AN EXACT COPY OF THE CLASS IN org.testng.reporters + * EXCEPT FOR THIS COMMENT AND log(String) CHANGED TO BE PROTECTED + */ +public class VerboseReporter extends TestListenerAdapter { + + /** + * Default prefix for messages printed out by this reporter + * + */ + public static final String LISTENER_PREFIX = "[VerboseTestNG] "; + private String suiteName; + private final String prefix; + + private enum Status { + + SUCCESS(ITestResult.SUCCESS), FAILURE(ITestResult.FAILURE), SKIP(ITestResult.SKIP), + SUCCESS_PERCENTAGE_FAILURE(ITestResult.SUCCESS_PERCENTAGE_FAILURE), STARTED(ITestResult.STARTED); + private final int code; + + private Status(int i) { + code = i; + } + + @SuppressWarnings("unused") + public int getCode() { + return code; + } + } + + /** + * Default constructor + */ + public VerboseReporter() { + this(LISTENER_PREFIX); + } + + /** + * Create VerboseReporter with custom prefix + * + * @param prefix prefix for messages printed out by this reporter + */ + public VerboseReporter(String prefix) { + this.prefix = prefix; + } + + @Override + public void beforeConfiguration(ITestResult tr) { + super.beforeConfiguration(tr); + logTestResult(Status.STARTED, tr, true); + } + + @Override + public void onConfigurationFailure(ITestResult tr) { + super.onConfigurationFailure(tr); + logTestResult(Status.FAILURE, tr, true); + } + + @Override + public void onConfigurationSkip(ITestResult tr) { + super.onConfigurationSkip(tr); + logTestResult(Status.SKIP, tr, true); + } + + @Override + public void onConfigurationSuccess(ITestResult tr) { + super.onConfigurationSuccess(tr); + logTestResult(Status.SUCCESS, tr, true); + } + + @Override + public void onTestStart(ITestResult tr) { + logTestResult(Status.STARTED, tr, false); + } + + @Override + public void onTestFailure(ITestResult tr) { + super.onTestFailure(tr); + logTestResult(Status.FAILURE, tr, false); + } + + @Override + public void onTestFailedButWithinSuccessPercentage(ITestResult tr) { + super.onTestFailedButWithinSuccessPercentage(tr); + logTestResult(Status.SUCCESS_PERCENTAGE_FAILURE, tr, false); + } + + @Override + public void onTestSkipped(ITestResult tr) { + super.onTestSkipped(tr); + logTestResult(Status.SKIP, tr, false); + } + + @Override + public void onTestSuccess(ITestResult tr) { + super.onTestSuccess(tr); + logTestResult(Status.SUCCESS, tr, false); + } + + @Override + public void onStart(ITestContext ctx) { + suiteName = ctx.getName();//ctx.getSuite().getXmlSuite().getFileName(); + log("RUNNING: Suite: \"" + suiteName + "\" containing \"" + ctx.getAllTestMethods().length + "\" Tests (config: " + ctx.getSuite().getXmlSuite().getFileName() + ")"); + } + + @Override + public void onFinish(ITestContext context) { + logResults(); + suiteName = null; + } + + private ITestNGMethod[] resultsToMethods(List<ITestResult> results) { + ITestNGMethod[] result = new ITestNGMethod[results.size()]; + int i = 0; + for (ITestResult tr : results) { + result[i++] = tr.getMethod(); + } + return result; + } + + /** + * Print out test summary + */ + private void logResults() { + // + // Log test summary + // + ITestNGMethod[] ft = resultsToMethods(getFailedTests()); + StringBuilder sb = new StringBuilder("\n===============================================\n"); + sb.append(" ").append(suiteName).append("\n"); + sb.append(" Tests run: ").append(Utils.calculateInvokedMethodCount(getAllTestMethods())); + sb.append(", Failures: ").append(Utils.calculateInvokedMethodCount(ft)); + sb.append(", Skips: ").append(Utils.calculateInvokedMethodCount(resultsToMethods(getSkippedTests()))); + int confFailures = getConfigurationFailures().size(); + int confSkips = getConfigurationSkips().size(); + if (confFailures > 0 || confSkips > 0) { + sb.append("\n").append(" Configuration Failures: ").append(confFailures); + sb.append(", Skips: ").append(confSkips); + } + sb.append("\n==============================================="); + log(sb.toString()); + } + + /** + * Log meaningful message for passed in arguments. + * Message itself is of form: + * $status: "$suiteName" - $methodDeclaration ($actualArguments) finished in $x ms ($run of $totalRuns) + * + * @param st status of passed in itr + * @param itr test result to be described + * @param isConfMethod is itr describing configuration method + */ + private void logTestResult(Status st, ITestResult itr, boolean isConfMethod) { + StringBuilder sb = new StringBuilder(); + String stackTrace = ""; + switch (st) { + case STARTED: + sb.append("INVOKING"); + break; + case SKIP: + sb.append("SKIPPED"); + stackTrace = itr.getThrowable() != null + ? Utils.stackTrace(itr.getThrowable(), false)[0] : ""; + break; + case FAILURE: + sb.append("FAILED"); + stackTrace = itr.getThrowable() != null + ? Utils.stackTrace(itr.getThrowable(), false)[0] : ""; + break; + case SUCCESS: + sb.append("PASSED"); + break; + case SUCCESS_PERCENTAGE_FAILURE: + sb.append("PASSED with failures"); + break; + default: + //not happen + throw new RuntimeException("Unsupported test status:" + itr.getStatus()); + } + if (isConfMethod) { + sb.append(" CONFIGURATION: "); + } else { + sb.append(": "); + } + ITestNGMethod tm = itr.getMethod(); + int identLevel = sb.length(); + sb.append(getMethodDeclaration(tm)); + Object[] params = itr.getParameters(); + Class<?>[] paramTypes = tm.getConstructorOrMethod().getParameterTypes(); + if (null != params && params.length > 0) { + // The error might be a data provider parameter mismatch, so make + // a special case here + if (params.length != paramTypes.length) { + sb.append("Wrong number of arguments were passed by the Data Provider: found "); + sb.append(params.length); + sb.append(" but expected "); + sb.append(paramTypes.length); + } else { + sb.append("(value(s): "); + for (int i = 0; i < params.length; i++) { + if (i > 0) { + sb.append(", "); + } + sb.append(Utils.toString(params[i], paramTypes[i])); + } + sb.append(")"); + + } + } + if (Status.STARTED != st) { + sb.append(" finished in "); + sb.append(itr.getEndMillis() - itr.getStartMillis()); + sb.append(" ms"); + if (!Utils.isStringEmpty(tm.getDescription())) { + sb.append("\n"); + for (int i = 0; i < identLevel; i++) { + sb.append(" "); + } + sb.append(tm.getDescription()); + } + if (tm.getInvocationCount() > 1) { + sb.append(" ("); + sb.append(tm.getCurrentInvocationCount()); + sb.append(" of "); + sb.append(tm.getInvocationCount()); + sb.append(")"); + } + if (!Utils.isStringEmpty(stackTrace)) { + sb.append("\n").append(stackTrace.substring(0, stackTrace.lastIndexOf(System.getProperty("line.separator")))); + } + } else { + if (!isConfMethod && tm.getInvocationCount() > 1) { + sb.append(" success: "); + sb.append(tm.getSuccessPercentage()); + sb.append("%"); + } + } + log(sb.toString()); + } + + protected void log(String message) { + //prefix all output lines + System.out.println(message.replaceAll("(?m)^", prefix)); + } + + /** + * + * @param method method to be described + * @return FQN of a class + method declaration for a method passed in + * ie. test.triangle.CheckCount.testCheckCount(java.lang.String) + */ + private String getMethodDeclaration(ITestNGMethod method) { + //see Utils.detailedMethodName + //perhaps should rather adopt the original method instead + ConstructorOrMethod m = method.getConstructorOrMethod(); + StringBuilder buf = new StringBuilder(); + buf.append("\""); + if (suiteName != null) { + buf.append(suiteName); + } else { + buf.append("UNKNOWN"); + } + buf.append("\""); + buf.append(" - "); + if (method.isBeforeSuiteConfiguration()) { + buf.append("@BeforeSuite "); + } else if (method.isBeforeTestConfiguration()) { + buf.append("@BeforeTest "); + } else if (method.isBeforeClassConfiguration()) { + buf.append("@BeforeClass "); + } else if (method.isBeforeGroupsConfiguration()) { + buf.append("@BeforeGroups "); + } else if (method.isBeforeMethodConfiguration()) { + buf.append("@BeforeMethod "); + } else if (method.isAfterMethodConfiguration()) { + buf.append("@AfterMethod "); + } else if (method.isAfterGroupsConfiguration()) { + buf.append("@AfterGroups "); + } else if (method.isAfterClassConfiguration()) { + buf.append("@AfterClass "); + } else if (method.isAfterTestConfiguration()) { + buf.append("@AfterTest "); + } else if (method.isAfterSuiteConfiguration()) { + buf.append("@AfterSuite "); + } + buf.append(m.getDeclaringClass().getName()); + buf.append("."); + buf.append(m.getName()); + buf.append("("); + int i = 0; + for (Class<?> p : m.getParameterTypes()) { + if (i++ > 0) { + buf.append(", "); + } + buf.append(p.getName()); + } + buf.append(")"); + return buf.toString(); + } + + @Override + public String toString() { + return "VerboseReporter{" + "suiteName=" + suiteName + '}'; + } +}
