/*
* 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.ReproduceErrorMessageBuilder;
import org.apache.logging.log4j.Logger;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.logging.Loggers;
import org.elasticsearch.test.ESIntegTestCase;
import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.test.rest.yaml.ESClientYamlSuiteTestCase;
import org.junit.internal.AssumptionViolatedException;
import org.junit.runner.Description;
import org.junit.runner.notification.Failure;
import org.junit.runner.notification.RunListener;
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;
import static org.elasticsearch.test.rest.yaml.ESClientYamlSuiteTestCase.REST_TESTS_BLACKLIST;
import static org.elasticsearch.test.rest.yaml.ESClientYamlSuiteTestCase.REST_TESTS_SUITE;
/**
* A {@link RunListener} that emits a command you can use to re-run a failing test with the failing random seed to
* {@link System#err}.
*/
public class ReproduceInfoPrinter extends RunListener {
protected final Logger 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());
}
/**
* Are we in the integ test phase?
*/
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("REPRODUCE WITH: gradle ");
String task = System.getProperty("tests.task");
// TODO: enforce (intellij still runs the runner?) or use default "test" but that won't work for integ
b.append(task);
GradleMessageBuilder gradleMessageBuilder = new GradleMessageBuilder(b);
gradleMessageBuilder.appendAllOpts(failure.getDescription());
// Client yaml suite tests are a special case as they allow for additional parameters
if (ESClientYamlSuiteTestCase.class.isAssignableFrom(failure.getDescription().getTestClass())) {
gradleMessageBuilder.appendClientYamlSuiteProperties();
}
System.err.println(b.toString());
}
protected static class GradleMessageBuilder extends ReproduceErrorMessageBuilder {
public GradleMessageBuilder(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() + "\"");
}
return appendESProperties();
}
@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)) {
return super.appendOpt(sysPropName, value);
}
return this;
}
public ReproduceErrorMessageBuilder appendESProperties() {
appendProperties("tests.es.logger.level");
if (inVerifyPhase()) {
// these properties only make sense for integration tests
appendProperties(ESIntegTestCase.TESTS_ENABLE_MOCK_MODULES);
}
appendProperties("tests.assertion.disabled", "tests.security.manager", "tests.nightly", "tests.jvms",
"tests.client.ratio", "tests.heap.size", "tests.bwc", "tests.bwc.version");
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;
}
public ReproduceErrorMessageBuilder appendClientYamlSuiteProperties() {
return appendProperties(REST_TESTS_SUITE, REST_TESTS_BLACKLIST);
}
protected ReproduceErrorMessageBuilder appendProperties(String... properties) {
for (String sysPropName : properties) {
if (Strings.hasLength(System.getProperty(sysPropName))) {
appendOpt(sysPropName, System.getProperty(sysPropName));
}
}
return this;
}
}
}