/* * Licensed to Elasticsearch under one or more contributor * license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright * ownership. Elasticsearch 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.elasticsearch.test.junit.listeners; import com.carrotsearch.randomizedtesting.RandomizedContext; import com.carrotsearch.randomizedtesting.ReproduceErrorMessageBuilder; import com.carrotsearch.randomizedtesting.TraceFormatting; import org.elasticsearch.common.Strings; import org.elasticsearch.common.logging.ESLogger; import org.elasticsearch.common.logging.Loggers; import org.elasticsearch.test.ESTestCase; import org.junit.internal.AssumptionViolatedException; import org.junit.runner.Description; import org.junit.runner.notification.Failure; import org.junit.runner.notification.RunListener; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import java.util.ArrayList; import java.util.List; import java.util.Locale; import java.util.TimeZone; import static com.carrotsearch.randomizedtesting.SysGlobals.SYSPROP_ITERATIONS; import static com.carrotsearch.randomizedtesting.SysGlobals.SYSPROP_PREFIX; import static com.carrotsearch.randomizedtesting.SysGlobals.SYSPROP_TESTMETHOD; /** * A {@link RunListener} that emits to {@link System#err} a string with command * line parameters allowing quick test re-run under MVN command line. */ public class ReproduceInfoPrinter extends RunListener { protected final ESLogger logger = Loggers.getLogger(ESTestCase.class); @Override public void testStarted(Description description) throws Exception { logger.trace("Test {} started", description.getDisplayName()); } @Override public void testFinished(Description description) throws Exception { logger.trace("Test {} finished", description.getDisplayName()); } /** * true if we are running maven integration tests (mvn verify) */ static boolean inVerifyPhase() { return Boolean.parseBoolean(System.getProperty("tests.verify.phase")); } @Override public void testFailure(Failure failure) throws Exception { // Ignore assumptions. if (failure.getException() instanceof AssumptionViolatedException) { return; } final StringBuilder b = new StringBuilder(); if (inVerifyPhase()) { b.append("REPRODUCE WITH: mvn verify -Pdev -Dskip.unit.tests" ); } else { b.append("REPRODUCE WITH: mvn test -Pdev"); } String project = System.getProperty("tests.project"); if (project != null) { b.append(" -pl " + project); } MavenMessageBuilder mavenMessageBuilder = new MavenMessageBuilder(b); mavenMessageBuilder.appendAllOpts(failure.getDescription()); System.err.println(b.toString()); } /** * Declared on test classes to add extra properties to the reproduction * info. Note that this is scanned from all superclasses. */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) public static @interface Properties { String[] value(); } protected TraceFormatting traces() { TraceFormatting traces = new TraceFormatting(); try { traces = RandomizedContext.current().getRunner().getTraceFormatting(); } catch (IllegalStateException e) { // Ignore if no context. } return traces; } protected static class MavenMessageBuilder extends ReproduceErrorMessageBuilder { public MavenMessageBuilder(StringBuilder b) { super(b); } @Override public ReproduceErrorMessageBuilder appendAllOpts(Description description) { super.appendAllOpts(description); if (description.getMethodName() != null) { //prints out the raw method description instead of methodName(description) which filters out the parameters super.appendOpt(SYSPROP_TESTMETHOD(), "\"" + description.getMethodName() + "\""); } List<String> properties = new ArrayList<>(); scanProperties(description.getTestClass(), properties); appendProperties(properties.toArray(new String[properties.size()])); return appendESProperties(); } /** * Scans c and its superclasses for the {@linkplain Properties} * annotations, copying all the listed properties in order from * superclass to subclass. */ private void scanProperties(Class<?> c, List<String> properties) { if (Object.class.equals(c) == false) { scanProperties(c.getSuperclass(), properties); } Properties extraParameterAnnocation = c.getAnnotation(Properties.class); if (extraParameterAnnocation != null) { for (String property : extraParameterAnnocation.value()) { properties.add(property); } } } @Override public ReproduceErrorMessageBuilder appendEnvironmentSettings() { // we handle our own environment settings return this; } /** * Append a single VM option. */ @Override public ReproduceErrorMessageBuilder appendOpt(String sysPropName, String value) { if (sysPropName.equals(SYSPROP_ITERATIONS())) { // we don't want the iters to be in there! return this; } if (sysPropName.equals(SYSPROP_TESTMETHOD())) { //don't print out the test method, we print it ourselves in appendAllOpts //without filtering out the parameters (needed for REST tests) return this; } if (sysPropName.equals(SYSPROP_PREFIX())) { // we always use the default prefix return this; } if (Strings.hasLength(value)) { if (value.indexOf(' ') >= 0) { return super.appendOpt(sysPropName, '"' + value + '"'); } return super.appendOpt(sysPropName, value); } return this; } public ReproduceErrorMessageBuilder appendESProperties() { if (System.getProperty("tests.jvm.argline") != null && !System.getProperty("tests.jvm.argline").isEmpty()) { appendOpt("tests.jvm.argline", System.getProperty("tests.jvm.argline")); } appendOpt("tests.locale", Locale.getDefault().toLanguageTag()); appendOpt("tests.timezone", TimeZone.getDefault().getID()); return this; } protected ReproduceErrorMessageBuilder appendProperties(String... properties) { for (String sysPropName : properties) { if (Strings.hasLength(System.getProperty(sysPropName))) { appendOpt(sysPropName, System.getProperty(sysPropName)); } } return this; } } }