All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.sarl.maven.docs.GenerateTestsMojo Maven / Gradle / Ivy

There is a newer version: 0.12.0
Show newest version
/*
 * $Id: io/sarl/maven/docs/GenerateTestsMojo.java v0.10.0 2019-10-26 17:20:53$
 *
 * SARL is an general-purpose agent programming language.
 * More details on http://www.sarl.io
 *
 * Copyright (C) 2014-2019 the original authors or authors.
 *
 * Licensed 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 io.sarl.maven.docs;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.InetSocketAddress;
import java.net.Proxy.Type;
import java.net.ProxySelector;
import java.net.SocketAddress;
import java.net.SocketTimeoutException;
import java.net.URI;
import java.net.URL;
import java.net.URLConnection;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import com.google.common.base.Throwables;
import com.google.common.collect.Lists;
import com.google.inject.Injector;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugins.annotations.LifecyclePhase;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.plugins.annotations.ResolutionScope;
import org.apache.maven.settings.Proxy;
import org.arakhne.afc.util.ListUtil;
import org.arakhne.afc.vmutil.FileSystem;
import org.eclipse.xtext.util.JavaVersion;
import org.eclipse.xtext.util.Strings;
import org.eclipse.xtext.xbase.compiler.ImportManager;
import org.eclipse.xtext.xbase.compiler.output.FakeTreeAppendable;
import org.eclipse.xtext.xbase.compiler.output.ITreeAppendable;
import org.junit.Assert;
import org.junit.Assume;
import org.junit.ComparisonFailure;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.BlockJUnit4ClassRunner;
import org.junit.runners.model.FrameworkMethod;
import org.junit.runners.model.InitializationError;

import io.sarl.lang.util.Utils;
import io.sarl.maven.docs.markdown.MarkdownParser;
import io.sarl.maven.docs.parser.AbstractMarkerLanguageParser;
import io.sarl.maven.docs.parser.DynamicValidationComponent;
import io.sarl.maven.docs.parser.DynamicValidationContext;
import io.sarl.maven.docs.parser.ValidationComponent;
import io.sarl.maven.docs.testing.DocumentationSetup;
import io.sarl.maven.docs.testing.ScriptExecutor;

/** Maven MOJO that is generating the documentation tests for the SARL project.
 *
 * @author Stéphane Galland
 * @version 0.10.0 2019-10-26 17:20:53
 * @mavengroupid io.sarl.maven
 * @mavenartifactid io.sarl.maven.docs.generator
 * @since 0.6
 */
@Mojo(name = "generatetests", defaultPhase = LifecyclePhase.GENERATE_TEST_SOURCES,
	requiresDependencyResolution = ResolutionScope.TEST)
public class GenerateTestsMojo extends AbstractDocumentationMojo {

	private static final String BASE_PACKAGE = "io.sarl.maven.docs"; //$NON-NLS-1$

	/**
	 * Indicates if the references to the local files should be validated.
	 */
	@Parameter(defaultValue = "true", required = false)
	protected boolean localLinkValidation;

	/**
	 * Indicates if the references to the remote Internet pages should be validated.
	 */
	@Parameter(defaultValue = "true", required = false)
	protected boolean remoteLinkValidation;

	/**
	 * Indicates if the timeout to use for connecting to remote hosts.
	 * This timeout value is expressed in milliseconds.
	 */
	@Parameter(defaultValue = "5000", required = false)
	protected int remoteLinkTimeOut;

	/**
	 * Indicates if a timeout for connecting to remote hosts should be ignored.
	 * If the timeout is ignore, the corresponding test will be marked as "ignored".
	 *
	 * @see Assume
	 */
	@Parameter(defaultValue = "true", required = false)
	protected boolean ignoreRemoteLinkTimeOut;

	/**
	 * Indicates if the references to the local files should be validated.
	 */
	@Parameter(defaultValue = "true", required = false)
	protected boolean localImageValidation;

	@Override
	protected String getSkippingMessage() {
		final String[] variableNames = {
			"maven.test.skip", //$NON-NLS-1$
			"skipTests", //$NON-NLS-1$
			"io.sarl.docs.tests.skip", //$NON-NLS-1$
		};
		String value = null;
		int i = 0;
		while (Strings.isEmpty(value) && i < variableNames.length) {
			value = System.getProperty(variableNames[i]);
			++i;
		}
		i = 0;
		while (Strings.isEmpty(value) && i < variableNames.length) {
			value = System.getenv(variableNames[i]);
			++i;
		}
		if (Boolean.valueOf(value)) {
			return "Tests are skipped."; //$NON-NLS-1$
		}
		return null;
	}

	@Override
	protected AbstractMarkerLanguageParser createLanguageParser(File inputFile) throws MojoExecutionException, IOException {
		final AbstractMarkerLanguageParser parser = super.createLanguageParser(inputFile);
		if (parser instanceof MarkdownParser) {
			final MarkdownParser mdParser = (MarkdownParser) parser;
			mdParser.setLocalImageReferenceValidation(this.localImageValidation);
			mdParser.setLocalFileReferenceValidation(this.localLinkValidation);
			mdParser.setRemoteReferenceValidation(
					!this.session.isOffline() && this.remoteLinkValidation);
		}
		return parser;
	}

	@Override
	protected String internalExecute(Map files) {
		getLog().info("Generating the testing resources"); //$NON-NLS-1$
		final File output = FileSystem.convertStringToFile(this.testSourceDirectory);
		final String msg = internalExecute(files, output);
		if (!Strings.isEmpty(msg)) {
			return msg;
		}
		try {
			generateRunner(output);
			generateAbstractTest(output);
			return null;
		} catch (IOException exception) {
			final String message = Throwables.getRootCause(exception).getLocalizedMessage();
			getLog().error(message);
			getLog().debug(exception);
			return message;
		}

	}

	@Override
	protected String internalExecute(Map files, File outputFolder) {
		try {
			FileSystem.delete(outputFolder);
		} catch (IOException exception) {
			final String message = formatErrorMessage(outputFolder, exception);
			getLog().error(message);
			getLog().debug(exception);
			return message;
		}
		return super.internalExecute(files, outputFolder);
	}

	@SuppressWarnings({"checkstyle:npathcomplexity", "checkstyle:cyclomaticcomplexity", "all"})
	@Override
	protected void internalExecute(File sourceFolder, File inputFile, File relativeInputFile, File outputFolder,
			AbstractMarkerLanguageParser parser) throws IOException {
		getLog().debug(MessageFormat.format(Messages.GenerateTestsMojo_0, inputFile.getName()));
		final List successCompilationComponents = new ArrayList<>();
		final List failureCompilationComponents = new ArrayList<>();
		final List factualComponents = new ArrayList<>();
		for (final ValidationComponent component : parser.getStandardValidationComponents(inputFile)) {
			if (component.isCompilable()) {
				if (component.isExecutable()) {
					factualComponents.add(component);
				} else {
					successCompilationComponents.add(component);
				}
			} else {
				failureCompilationComponents.add(component);
			}
		}
		final DynamicValidationContext validationContext = new DynamicValidationContext();
		validationContext.setSourceRoots(this.session.getCurrentProject().getCompileSourceRoots());
		validationContext.setResourceRoots(Lists.transform(this.session.getCurrentProject().getResources(),
				it -> it.getDirectory()));
		validationContext.setDestinationRoots(
				Collections.singletonList(this.session.getCurrentProject().getBuild().getOutputDirectory()));
		final List specificComponents = parser.getMarkerSpecificValidationComponents(
				inputFile, sourceFolder, validationContext);
		if (successCompilationComponents.isEmpty() && failureCompilationComponents.isEmpty()
				&& factualComponents.isEmpty() && specificComponents.isEmpty()) {
			return;
		}

		// Do not change the "Test" postfix because it is used by Surefire for detecting tests.
		final String generalTestName = toTestName(inputFile) + "Test"; //$NON-NLS-1$

		final ImportManager importManager = new ImportManager();
		final ITreeAppendable it = new FakeTreeAppendable(importManager);

		it.append("@").append(SuppressWarnings.class).append("(\"all\")"); //$NON-NLS-1$//$NON-NLS-2$
		it.newLine();
		it.append("@").append(RunWith.class).append("("); //$NON-NLS-1$ //$NON-NLS-2$
		it.append(BASE_PACKAGE).append(".DocumentationTestRunner.class)"); //$NON-NLS-1$
		it.newLine();
		it.append("public class ").append(generalTestName).append(" extends ");  //$NON-NLS-1$//$NON-NLS-2$
		it.append(BASE_PACKAGE).append(".AbstractBaseTest {"); //$NON-NLS-1$
		it.increaseIndentation();

		int i = 0;
		for (final ValidationComponent component : successCompilationComponents) {
			getLog().debug(MessageFormat.format(Messages.GenerateTestsMojo_1,
					inputFile.getName(), component.getLineno(), component.getCode()));
			final String actionName = "success_" + component.getLineno() + "_" + i; //$NON-NLS-1$ //$NON-NLS-2$
			it.newLine();
			it.append("@").append(Test.class); //$NON-NLS-1$
			it.newLine();
			it.append("public void ").append(actionName).append("() throws Exception {"); //$NON-NLS-1$ //$NON-NLS-2$
			it.increaseIndentation().newLine();
			it.append(List.class).append(" issues = getScriptExecutor().compile("); //$NON-NLS-1$
			it.append(Integer.toString(component.getLineno())).append(", \""); //$NON-NLS-1$
			it.append(Strings.convertToJavaString(component.getCode()));
			it.append("\");"); //$NON-NLS-1$
			it.newLine();
			it.append("assertNoIssue(" + component.getLineno() + ", issues);"); //$NON-NLS-1$ //$NON-NLS-2$
			it.decreaseIndentation().newLine();
			it.append("}"); //$NON-NLS-1$
			++i;
		}

		i = 0;
		for (final ValidationComponent component : failureCompilationComponents) {
			getLog().debug(MessageFormat.format(Messages.GenerateTestsMojo_2,
					inputFile.getName(), component.getLineno(), component.getCode()));
			final String actionName = "failure_" + component.getLineno() + "_" + i; //$NON-NLS-1$ //$NON-NLS-2$
			it.newLine();
			it.append("@").append(Test.class); //$NON-NLS-1$
			it.newLine();
			it.append("public void ").append(actionName).append("() throws Exception {"); //$NON-NLS-1$ //$NON-NLS-2$
			it.increaseIndentation().newLine();
			it.append(List.class).append(" issues = getScriptExecutor().compile("); //$NON-NLS-1$
			it.append(Integer.toString(component.getLineno())).append(", \""); //$NON-NLS-1$
			it.append(Strings.convertToJavaString(component.getCode()));
			it.append("\");"); //$NON-NLS-1$
			it.newLine();
			it.append("assertIssues(" + component.getLineno() + ", issues);"); //$NON-NLS-1$ //$NON-NLS-2$
			it.decreaseIndentation().newLine();
			it.append("}"); //$NON-NLS-1$
			++i;
		}

		i = 0;
		for (final ValidationComponent component : factualComponents) {
			getLog().debug(MessageFormat.format(Messages.GenerateTestsMojo_3,
					inputFile.getName(), component.getLineno(), component.getCode()));
			final String actionName = "fact_" + component.getLineno() + "_" + i; //$NON-NLS-1$ //$NON-NLS-2$
			it.newLine();
			it.append("@").append(Test.class); //$NON-NLS-1$
			it.newLine();
			it.append("public void ").append(actionName).append("() throws Exception {"); //$NON-NLS-1$ //$NON-NLS-2$
			it.increaseIndentation().newLine();
			it.append("final String expected = ").append(Utils.class).append(".dump(Boolean.TRUE, false) + "); //$NON-NLS-1$ //$NON-NLS-2$
			it.append("\"\\nOR\\nObject {\\n}\\n\";"); //$NON-NLS-1$
			it.newLine();
			it.append("Object result;"); //$NON-NLS-1$
			it.newLine();
			it.append("try {"); //$NON-NLS-1$
			it.increaseIndentation().newLine();
			it.append("result = getScriptExecutor().execute(").append(Integer.toString(component.getLineno())); //$NON-NLS-1$
			it.append(", \"").append(Strings.convertToJavaString(component.getCode())); //$NON-NLS-1$
			it.append("\");"); //$NON-NLS-1$
			it.decreaseIndentation().newLine();
			it.append("} catch (Throwable exception) {"); //$NON-NLS-1$
			it.increaseIndentation().newLine();
			it.append("throw new ").append(ComparisonFailure.class); //$NON-NLS-1$
			it.append("(exception.getLocalizedMessage() + \" [line: ").append(Integer.toString(component.getLineno())); //$NON-NLS-1$
			it.append("]\", expected, ").append(Throwables.class); //$NON-NLS-1$
			it.append(".getStackTraceAsString(exception));"); //$NON-NLS-1$
			it.decreaseIndentation().newLine();
			it.append("}"); //$NON-NLS-1$
			it.newLine();
			it.append("if (result instanceof Boolean) {"); //$NON-NLS-1$
			it.increaseIndentation().newLine();
			it.append("boolean boolResult = ((Boolean) result).booleanValue();"); //$NON-NLS-1$
			it.newLine();
			it.append("if (!boolResult) {"); //$NON-NLS-1$
			it.increaseIndentation().newLine();
			it.append("throw new ").append(ComparisonFailure.class); //$NON-NLS-1$
			it.append("(\"Invalid expression result [line: ").append(Integer.toString(component.getLineno())); //$NON-NLS-1$
			it.append("]\", expected, ").append(Utils.class); //$NON-NLS-1$
			it.append(".dump(result, false));"); //$NON-NLS-1$
			it.decreaseIndentation().newLine();
			it.append("}"); //$NON-NLS-1$
			it.decreaseIndentation().newLine();
			it.append("} else if (result == null || result instanceof Exception) {"); //$NON-NLS-1$
			it.increaseIndentation().newLine();
			it.append("throw new ").append(ComparisonFailure.class); //$NON-NLS-1$
			it.append("(\"Invalid expression result [line: ").append(Integer.toString(component.getLineno())); //$NON-NLS-1$
			it.append("]\", expected, ").append(Utils.class); //$NON-NLS-1$
			it.append(".dump(result, false));"); //$NON-NLS-1$
			it.decreaseIndentation().newLine();
			it.append("}"); //$NON-NLS-1$
			it.decreaseIndentation().newLine();
			it.append("}"); //$NON-NLS-1$
			++i;
		}

		i = 0;
		for (final DynamicValidationComponent component : specificComponents) {
			getLog().debug(MessageFormat.format(Messages.GenerateTestsMojo_4,
					inputFile.getName(), component.functionName() + i));
			final String actionName = component.functionName() + i;
			it.newLine();
			it.append("@").append(Test.class); //$NON-NLS-1$
			it.newLine();
			it.append("public void ").append(actionName).append("() throws Exception {"); //$NON-NLS-1$ //$NON-NLS-2$
			it.increaseIndentation().newLine();
			component.generateValidationCode(it);
			it.decreaseIndentation().newLine();
			it.append("}"); //$NON-NLS-1$
			++i;
		}

		it.decreaseIndentation().newLine();
		it.append("}"); //$NON-NLS-1$
		it.newLine();

		final File packagePath = relativeInputFile.getParentFile();
		final String packageName = toPackageName("docs", packagePath); //$NON-NLS-1$
		write(outputFolder, packageName, generalTestName, importManager, it);
	}

	@SuppressWarnings({"checkstyle:methodlength", "checkstyle:npathcomplexity"})
	private void generateAbstractTest(File outputFolder) throws IOException {
		getLog().debug("Generating abstract test"); //$NON-NLS-1$
		final ImportManager importManager = new ImportManager();
		final ITreeAppendable it = new FakeTreeAppendable(importManager);
		it.append("@").append(SuppressWarnings.class).append("(\"all\")"); //$NON-NLS-1$ //$NON-NLS-2$
		it.newLine();
		it.append("public class AbstractBaseTest {"); //$NON-NLS-1$
		it.increaseIndentation().newLine();
		it.append("protected static String STR_SUCCESS = \"success\";"); //$NON-NLS-1$
		it.newLine();
		it.append("protected static String STR_FAILURE = \"failure\";"); //$NON-NLS-1$
		it.newLine();
		it.append("protected static String STR_FACT = \"fact\";"); //$NON-NLS-1$
		it.newLine().newLine();
		it.append("private static ").append(Injector.class).append(" injector = "); //$NON-NLS-1$ //$NON-NLS-2$
		it.append(DocumentationSetup.class).append(".doSetup();"); //$NON-NLS-1$
		it.newLine().newLine();
		it.append("private ").append(ScriptExecutor.class).append(" scriptExecutor;"); //$NON-NLS-1$ //$NON-NLS-2$
		it.newLine().newLine();
		it.append("protected ").append(ScriptExecutor.class).append(" getScriptExecutor() {"); //$NON-NLS-1$ //$NON-NLS-2$
		it.increaseIndentation().newLine();
		it.append("if (this.scriptExecutor == null) {"); //$NON-NLS-1$
		it.increaseIndentation().newLine();
		it.append("this.scriptExecutor = this.injector.getInstance(").append(ScriptExecutor.class).append(".class);"); //$NON-NLS-1$ //$NON-NLS-2$
		it.newLine();
		final StringBuilder cp = new StringBuilder();
		for (final File cpElement : getClassPath()) {
			if (cp.length() > 0) {
				cp.append(":"); //$NON-NLS-1$
			}
			cp.append(cpElement.getAbsolutePath());
		}
		it.append("scriptExecutor.setClassPath(\""); //$NON-NLS-1$
		it.append(Strings.convertToJavaString(cp.toString()));
		it.append("\");"); //$NON-NLS-1$
		it.newLine();
		final String bootPath = getBootClassPath();
		if (!Strings.isEmpty(bootPath)) {
			it.append("scriptExecutor.setBootClassPath(\""); //$NON-NLS-1$
			it.append(Strings.convertToJavaString(bootPath));
			it.append("\");"); //$NON-NLS-1$
			it.newLine();
		}
		JavaVersion version = null;
		if (!Strings.isEmpty(this.source)) {
			version = JavaVersion.fromQualifier(this.source);
		}
		if (version == null) {
			version = JavaVersion.JAVA8;
		}
		it.append("scriptExecutor.setJavaSourceVersion(\""); //$NON-NLS-1$
		it.append(Strings.convertToJavaString(version.getQualifier()));
		it.append("\");"); //$NON-NLS-1$
		it.newLine();
		it.append("scriptExecutor.setTempFolder(").append(FileSystem.class); //$NON-NLS-1$
		it.append(".convertStringToFile(\""); //$NON-NLS-1$
		it.append(Strings.convertToJavaString(this.tempDirectory.getAbsolutePath()));
		it.append("\"));"); //$NON-NLS-1$
		it.decreaseIndentation().newLine();
		it.append("}"); //$NON-NLS-1$
		it.decreaseIndentation().newLine();
		it.append("return this.scriptExecutor;"); //$NON-NLS-1$
		it.newLine();
		it.append("}"); //$NON-NLS-1$
		it.newLine().newLine();
		it.append("public void assertNoIssue(int lineno, ").append(List.class).append(" issues) {"); //$NON-NLS-1$ //$NON-NLS-2$
		it.increaseIndentation().newLine();
		it.append("if (issues != null && !issues.isEmpty()) {"); //$NON-NLS-1$
		it.increaseIndentation().newLine();
		it.append(StringBuilder.class).append(" msg = new ").append(StringBuilder.class).append("();"); //$NON-NLS-1$ //$NON-NLS-2$
		it.newLine();
		it.append("for (String message : issues) {"); //$NON-NLS-1$
		it.increaseIndentation().newLine();
		it.append("msg.append(message).append(\"\\n\");"); //$NON-NLS-1$
		it.decreaseIndentation().newLine();
		it.append("}"); //$NON-NLS-1$
		it.newLine();
		it.append("throw new ").append(ComparisonFailure.class).append(//$NON-NLS-1$
				"(\"Expecting no issue but find one [line:\" + lineno + \"]\", \"\", msg.toString());"); //$NON-NLS-1$
		it.decreaseIndentation().newLine();
		it.append("}"); //$NON-NLS-1$
		it.decreaseIndentation().newLine();
		it.append("}"); //$NON-NLS-1$
		it.newLine().newLine();
		it.append("public void assertIssues(int lineno, ").append(List.class).append(" issues) {"); //$NON-NLS-1$ //$NON-NLS-2$
		it.increaseIndentation().newLine();
		it.append("if (issues == null || issues.isEmpty()) {"); //$NON-NLS-1$
		it.increaseIndentation().newLine();
		it.append(Assert.class).append(".fail(\"Expecting issues but did not find one [line:\" + lineno + \"]\");"); //$NON-NLS-1$
		it.decreaseIndentation().newLine();
		it.append("}"); //$NON-NLS-1$
		it.decreaseIndentation().newLine();
		it.append("}"); //$NON-NLS-1$
		it.newLine().newLine();
		it.append("public String computeHeaderIdWithSectionNumber(String header) {"); //$NON-NLS-1$
		it.increaseIndentation().newLine();
		it.append("String id = header.replaceAll(\"[^a-zA-Z0-9]+\", \"-\");").newLine(); //$NON-NLS-1$
		it.append("id = id.toLowerCase();").newLine(); //$NON-NLS-1$
		it.append("id = id.replaceFirst(\"^[^a-zA-Z0-9]+\", \"\");").newLine(); //$NON-NLS-1$
		it.append("id = id.replaceFirst(\"[^a-zA-Z0-9]+$\", \"\");").newLine(); //$NON-NLS-1$
		it.append("if (Strings.isEmpty(id)) {"); //$NON-NLS-1$
		it.increaseIndentation().newLine();
		it.append("return \"section\";"); //$NON-NLS-1$
		it.decreaseIndentation().newLine();
		it.append("}").newLine(); //$NON-NLS-1$
		it.append("return id;"); //$NON-NLS-1$
		it.decreaseIndentation().newLine();
		it.append("}"); //$NON-NLS-1$
		it.newLine().newLine();
		it.append("public String computeHeaderIdWithoutSectionNumber(String header) {"); //$NON-NLS-1$
		it.increaseIndentation().newLine();
		it.append("String id = computeHeaderIdWithSectionNumber(header);").newLine(); //$NON-NLS-1$
		it.append("id = id.replaceFirst(\"^[0-9.\\\\-]+\", \"\");").newLine(); //$NON-NLS-1$
		it.append("return id;"); //$NON-NLS-1$
		it.decreaseIndentation().newLine();
		it.append("}"); //$NON-NLS-1$
		it.newLine().newLine();
		it.append("public static String getHttpCodeExplanation(int code) {"); //$NON-NLS-1$
		it.increaseIndentation().newLine();
		it.append("switch (code) {"); //$NON-NLS-1$
		it.newLine();
		it.append("case ").append(HttpURLConnection.class).append(".HTTP_BAD_METHOD: return \"Method Not Allowed\";"); //$NON-NLS-1$ //$NON-NLS-2$
		it.newLine();
		it.append("case ").append(HttpURLConnection.class).append(".HTTP_CREATED: return \"Created\";"); //$NON-NLS-1$ //$NON-NLS-2$
		it.newLine();
		it.append("case ").append(HttpURLConnection.class).append(".HTTP_ACCEPTED: return \"Accepted\";"); //$NON-NLS-1$ //$NON-NLS-2$
		it.newLine();
		it.append("case ").append(HttpURLConnection.class).append(".HTTP_NOT_AUTHORITATIVE: return "); //$NON-NLS-1$ //$NON-NLS-2$
		it.append("\"Non-Authoritative Information\";"); //$NON-NLS-1$
		it.newLine();
		it.append("case ").append(HttpURLConnection.class).append(".HTTP_NO_CONTENT: return \"No Content\";"); //$NON-NLS-1$ //$NON-NLS-2$
		it.newLine();
		it.append("case ").append(HttpURLConnection.class).append(".HTTP_RESET: return \"Reset Content\";"); //$NON-NLS-1$ //$NON-NLS-2$
		it.newLine();
		it.append("case ").append(HttpURLConnection.class).append(".HTTP_PARTIAL: return \"Partial Content\";"); //$NON-NLS-1$ //$NON-NLS-2$
		it.newLine();
		it.append("case ").append(HttpURLConnection.class).append(".HTTP_MULT_CHOICE: return \"Multiple Choices\";"); //$NON-NLS-1$ //$NON-NLS-2$
		it.newLine();
		it.append("case ").append(HttpURLConnection.class).append(".HTTP_MOVED_PERM: return \"Moved Permanently\";"); //$NON-NLS-1$ //$NON-NLS-2$
		it.newLine();
		it.append("case ").append(HttpURLConnection.class).append(".HTTP_MOVED_TEMP: return \"Temporary Redirect\";"); //$NON-NLS-1$ //$NON-NLS-2$
		it.newLine();
		it.append("case ").append(HttpURLConnection.class).append(".HTTP_SEE_OTHER: return \"See Other\";"); //$NON-NLS-1$ //$NON-NLS-2$
		it.newLine();
		it.append("case ").append(HttpURLConnection.class).append(".HTTP_NOT_MODIFIED: return \"Not Modified\";"); //$NON-NLS-1$ //$NON-NLS-2$
		it.newLine();
		it.append("case ").append(HttpURLConnection.class).append(".HTTP_USE_PROXY: return \"Use Proxy\";"); //$NON-NLS-1$ //$NON-NLS-2$
		it.newLine();
		it.append("case ").append(HttpURLConnection.class).append(".HTTP_BAD_REQUEST: return \"Bad Request\";"); //$NON-NLS-1$ //$NON-NLS-2$
		it.newLine();
		it.append("case ").append(HttpURLConnection.class).append(".HTTP_UNAUTHORIZED: return \"Unauthorized\";"); //$NON-NLS-1$ //$NON-NLS-2$
		it.newLine();
		it.append("case ").append(HttpURLConnection.class).append(".HTTP_PAYMENT_REQUIRED: return \"Payment Required\";"); //$NON-NLS-1$ //$NON-NLS-2$
		it.newLine();
		it.append("case ").append(HttpURLConnection.class).append(".HTTP_FORBIDDEN: return \"Forbidden\";"); //$NON-NLS-1$ //$NON-NLS-2$
		it.newLine();
		it.append("case ").append(HttpURLConnection.class).append(".HTTP_NOT_FOUND: return \"Not Found\";"); //$NON-NLS-1$ //$NON-NLS-2$
		it.newLine();
		it.append("case ").append(HttpURLConnection.class).append(".HTTP_NOT_ACCEPTABLE: return \"Not Acceptable\";"); //$NON-NLS-1$ //$NON-NLS-2$
		it.newLine();
		it.append("case ").append(HttpURLConnection.class).append(".HTTP_PROXY_AUTH: return "); //$NON-NLS-1$ //$NON-NLS-2$
		it.append("\"Proxy Authentication Required\";"); //$NON-NLS-1$
		it.newLine();
		it.append("case ").append(HttpURLConnection.class).append(".HTTP_CLIENT_TIMEOUT: return \"Request Time-Out\";"); //$NON-NLS-1$ //$NON-NLS-2$
		it.newLine();
		it.append("case ").append(HttpURLConnection.class).append(".HTTP_CONFLICT: return \"Conflict\";"); //$NON-NLS-1$ //$NON-NLS-2$
		it.newLine();
		it.append("case ").append(HttpURLConnection.class).append(".HTTP_GONE: return \"Gone\";"); //$NON-NLS-1$ //$NON-NLS-2$
		it.newLine();
		it.append("case ").append(HttpURLConnection.class).append(".HTTP_LENGTH_REQUIRED: return \"Length Required\";"); //$NON-NLS-1$ //$NON-NLS-2$
		it.newLine();
		it.append("case ").append(HttpURLConnection.class).append(".HTTP_PRECON_FAILED: return \"Precondition Failed\";"); //$NON-NLS-1$ //$NON-NLS-2$
		it.newLine();
		it.append("case ").append(HttpURLConnection.class).append(".HTTP_ENTITY_TOO_LARGE: return "); //$NON-NLS-1$ //$NON-NLS-2$
		it.append("\"Request Entity Too Large\";"); //$NON-NLS-1$
		it.newLine();
		it.append("case ").append(HttpURLConnection.class).append(".HTTP_REQ_TOO_LONG: return "); //$NON-NLS-1$ //$NON-NLS-2$
		it.append("\"Request-URI Too Large\";"); //$NON-NLS-1$
		it.newLine();
		it.append("case ").append(HttpURLConnection.class).append(".HTTP_UNSUPPORTED_TYPE: return "); //$NON-NLS-1$ //$NON-NLS-2$
		it.append("\"Unsupported Media Type\";"); //$NON-NLS-1$
		it.newLine();
		it.append("case ").append(HttpURLConnection.class).append(".HTTP_INTERNAL_ERROR: return "); //$NON-NLS-1$ //$NON-NLS-2$
		it.append("\"Internal Server Error\";"); //$NON-NLS-1$
		it.newLine();
		it.append("case ").append(HttpURLConnection.class).append(".HTTP_NOT_IMPLEMENTED: return \"Not Implemented\";"); //$NON-NLS-1$ //$NON-NLS-2$
		it.newLine();
		it.append("case ").append(HttpURLConnection.class).append(".HTTP_BAD_GATEWAY: return \"Bad Gateway\";"); //$NON-NLS-1$ //$NON-NLS-2$
		it.newLine();
		it.append("case ").append(HttpURLConnection.class).append(".HTTP_UNAVAILABLE: return \"Service Unavailable\";"); //$NON-NLS-1$ //$NON-NLS-2$
		it.newLine();
		it.append("case ").append(HttpURLConnection.class).append(".HTTP_GATEWAY_TIMEOUT: return \"Gateway Timeout\";"); //$NON-NLS-1$ //$NON-NLS-2$
		it.newLine();
		it.append("case ").append(HttpURLConnection.class).append(".HTTP_VERSION: return "); //$NON-NLS-1$ //$NON-NLS-2$
		it.append("\"HTTP Version Not Supported\";"); //$NON-NLS-1$
		it.newLine();
		it.append("default: return null;"); //$NON-NLS-1$
		it.newLine();
		it.append("}"); //$NON-NLS-1$
		it.decreaseIndentation().newLine();
		it.append("}"); //$NON-NLS-1$
		it.newLine().newLine();
		it.append("public static boolean isAcceptableHttpCode(int code) {"); //$NON-NLS-1$
		it.increaseIndentation().newLine();
		it.append("return code == ").append(HttpURLConnection.class).append(".HTTP_OK"); //$NON-NLS-1$ //$NON-NLS-2$
		it.increaseIndentation().newLine();
		it.append("|| code == ").append(HttpURLConnection.class).append(".HTTP_MOVED_TEMP"); //$NON-NLS-1$ //$NON-NLS-2$
		it.decreaseIndentation().newLine();
		it.append(";"); //$NON-NLS-1$
		it.decreaseIndentation().newLine();
		it.append("}"); //$NON-NLS-1$
		it.newLine().newLine();
		it.append("public static void assertURLAccessibility(int lineno, ").append(URL.class); //$NON-NLS-1$
		it.append(" url) throws ").append(Exception.class).append(" {");  //$NON-NLS-1$//$NON-NLS-2$
		it.increaseIndentation().newLine();
		it.append("int code;"); //$NON-NLS-1$
		it.newLine();
		it.append(HttpURLConnection.class).append(".setFollowRedirects(false);"); //$NON-NLS-1$
		it.newLine();
		it.append(URLConnection.class).append(" connection = url.openConnection();"); //$NON-NLS-1$
		it.newLine();
		it.append(Assume.class).append(".assumeTrue(\"Not an UTL with http[s] protocol\", connection instanceof "); //$NON-NLS-1$
		it.append(HttpURLConnection.class).append(");"); //$NON-NLS-1$
		it.newLine();
		it.append(HttpURLConnection.class).append(" httpConnection = ("); //$NON-NLS-1$
		it.append(HttpURLConnection.class).append(") connection;"); //$NON-NLS-1$
		it.newLine();
		it.append("try {"); //$NON-NLS-1$
		it.increaseIndentation().newLine();
		it.append("httpConnection.setInstanceFollowRedirects(false);"); //$NON-NLS-1$
		it.newLine();
		it.append("httpConnection.setConnectTimeout(").append(Integer.toString(this.remoteLinkTimeOut)); //$NON-NLS-1$
		it.append(");"); //$NON-NLS-1$
		it.newLine();
		it.append("httpConnection.setReadTimeout(").append(Integer.toString(this.remoteLinkTimeOut)); //$NON-NLS-1$
		it.append(");"); //$NON-NLS-1$
		it.newLine();
		it.append("httpConnection.setAllowUserInteraction(false);"); //$NON-NLS-1$
		it.newLine();
		it.append("httpConnection.setRequestMethod(\"HEAD\");"); //$NON-NLS-1$
		it.newLine();
		it.append("httpConnection.connect();"); //$NON-NLS-1$
		it.newLine();
		it.append("code = httpConnection.getResponseCode();"); //$NON-NLS-1$
		it.decreaseIndentation().newLine();
		it.append("} catch (").append(IOException.class).append(" exception) {"); //$NON-NLS-1$ //$NON-NLS-2$
		it.increaseIndentation().newLine();
		it.append("Throwable rootCause = ").append(Throwables.class).append(".getRootCause(exception);"); //$NON-NLS-1$ //$NON-NLS-2$
		it.newLine();
		if (this.ignoreRemoteLinkTimeOut) {
			it.append("if (rootCause instanceof ").append(SocketTimeoutException.class).append(") {"); //$NON-NLS-1$ //$NON-NLS-2$
			it.increaseIndentation().newLine();
			it.append(Assume.class).append(".assumeNoException(\""); //$NON-NLS-1$
			it.append("Connection time-out at line \" + lineno + \" when connecting to: \" + url.toExternalForm()"); //$NON-NLS-1$
			it.append(", rootCause);"); //$NON-NLS-1$
			it.decreaseIndentation().newLine();
			it.append("}"); //$NON-NLS-1$
			it.newLine();
		}
		it.append("throw new ").append(RuntimeException.class); //$NON-NLS-1$
		it.append("(\"Error at line \" + lineno + \" when connecting to: \" + url.toExternalForm(), rootCause);"); //$NON-NLS-1$
		it.decreaseIndentation().newLine();
		it.append("} finally {"); //$NON-NLS-1$
		it.increaseIndentation().newLine();
		it.append("httpConnection.disconnect();"); //$NON-NLS-1$
		it.decreaseIndentation().newLine();
		it.append("}"); //$NON-NLS-1$
		it.newLine();
		it.append("if (isAcceptableHttpCode(code)) {"); //$NON-NLS-1$
		it.increaseIndentation().newLine();
		it.append("return;"); //$NON-NLS-1$
		it.decreaseIndentation().newLine();
		it.append("}"); //$NON-NLS-1$
		it.newLine();
		it.append("String explanation = getHttpCodeExplanation(code);"); //$NON-NLS-1$
		it.newLine();
		it.append("String codeMsg = !").append(Strings.class); //$NON-NLS-1$
		it.append(".isEmpty(explanation) ? code + \"/\\\"\" + explanation + \"\\\"\" : Integer.toString(code);"); //$NON-NLS-1$
		it.newLine();
		it.append(Assert.class).append(".fail(\"Invalid response code \" + codeMsg + \" at line \" + lineno "); //$NON-NLS-1$
		it.append("+ \" when connecting to: \" + url.toExternalForm());"); //$NON-NLS-1$
		it.decreaseIndentation().newLine();
		it.append("}"); //$NON-NLS-1$

		if (!this.session.isOffline() && !this.session.getRequest().getProxies().isEmpty()) {
			it.newLine().newLine();
			it.append("private static boolean proxyNameMatches(String pattern, String name) {"); //$NON-NLS-1$
			it.increaseIndentation().newLine();
			it.append(Pattern.class).append(" pat = "); //$NON-NLS-1$
            it.append(Pattern.class).append(".compile(pattern, "); //$NON-NLS-1$
            it.append(Pattern.class).append(".CASE_INSENSITIVE);"); //$NON-NLS-1$
			it.newLine();
            it.append(Matcher.class).append(" mat = pat.matcher(name);"); //$NON-NLS-1$
			it.newLine();
			it.append("return mat.matches();"); //$NON-NLS-1$
			it.decreaseIndentation().newLine();
			it.append("}"); //$NON-NLS-1$
			it.newLine().newLine();
			it.append("static {"); //$NON-NLS-1$
			it.increaseIndentation().newLine();
			it.append("final ").append(ProxySelector.class).append(" defaultSelector = "); //$NON-NLS-1$ //$NON-NLS-2$
			it.append(ProxySelector.class).append(".getDefault();"); //$NON-NLS-1$
			it.newLine();
			it.append(ProxySelector.class).append(" newSelector = new ").append(ProxySelector.class); //$NON-NLS-1$
			it.append("() {"); //$NON-NLS-1$
			it.increaseIndentation().newLine();
			it.append("public ").append(List.class); //$NON-NLS-1$
			it.append("<").append(java.net.Proxy.class).append("> select(final "); //$NON-NLS-1$ //$NON-NLS-2$
			it.append(URI.class).append(" uri) {"); //$NON-NLS-1$
			it.increaseIndentation().newLine();
			it.append(List.class).append("<").append(java.net.Proxy.class).append("> proxies = new "); //$NON-NLS-1$ //$NON-NLS-2$
			it.append(ArrayList.class).append("(defaultSelector.select(uri));"); //$NON-NLS-1$
			it.newLine();
			for (final Proxy proxy : this.session.getRequest().getProxies()) {
				it.append("if (\"").append(Strings.convertToJavaString(proxy.getProtocol())); //$NON-NLS-1$
				it.append("\".equals(uri.getScheme())) {"); //$NON-NLS-1$
				it.increaseIndentation().newLine();
				final String nonProxyHosts = proxy.getNonProxyHosts();
				boolean hasProxy = false;
				if (!Strings.isEmpty(nonProxyHosts)) {
		            if (nonProxyHosts != null) {
		            	hasProxy = true;
		            	it.append("if ("); //$NON-NLS-1$
		            	final StringTokenizer tokenizer = new StringTokenizer(nonProxyHosts, "|"); //$NON-NLS-1$
		            	boolean first = true;
		                while (tokenizer.hasMoreTokens()) {
		                    String pattern = tokenizer.nextToken();
		                    pattern = pattern.replace(".", "\\.").replace("*", ".*"); //$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$//$NON-NLS-4$
		                    if (first) {
		                    	first = false;
		                    } else {
		                    	it.newLine().append(" && "); //$NON-NLS-1$
		                    }
		                    it.append("!proxyNameMatches(\"^").append(Strings.convertToJavaString(pattern)); //$NON-NLS-1$
		                    it.append("$\", uri.getHost())"); //$NON-NLS-1$
		                }
		                it.append(") {"); //$NON-NLS-1$
		                it.increaseIndentation().newLine();
		            }
				}

				it.append("proxies.add(new ").append(java.net.Proxy.class); //$NON-NLS-1$
				it.append("(").append(Type.class).append(".HTTP, new "); //$NON-NLS-1$ //$NON-NLS-2$
				it.append(InetSocketAddress.class).append("(\""); //$NON-NLS-1$
				it.append(Strings.convertToJavaString(proxy.getHost()));
				it.append("\", ").append(Integer.toString(proxy.getPort())); //$NON-NLS-1$
				it.append(")));"); //$NON-NLS-1$
				if (hasProxy) {
					it.decreaseIndentation().newLine();
					it.append("}"); //$NON-NLS-1$
				}
				it.decreaseIndentation().newLine();
				it.append("}"); //$NON-NLS-1$
				it.newLine();
			}
			it.append("return ").append(Collections.class).append(".unmodifiableList(proxies);"); //$NON-NLS-1$ //$NON-NLS-2$
			it.decreaseIndentation().newLine();
			it.append("}"); //$NON-NLS-1$
			it.newLine();
			it.append("public void connectFailed(").append(URI.class); //$NON-NLS-1$
			it.append(" uri, ").append(SocketAddress.class); //$NON-NLS-1$
			it.append(" sa, ").append(IOException.class); //$NON-NLS-1$
			it.append(" ioe) {"); //$NON-NLS-1$
			it.increaseIndentation().newLine();
			it.append("throw new ").append(RuntimeException.class); //$NON-NLS-1$
			it.append("(ioe);"); //$NON-NLS-1$
			it.decreaseIndentation().newLine();
			it.append("}"); //$NON-NLS-1$
			it.decreaseIndentation().newLine();
			it.append("};"); //$NON-NLS-1$
			it.newLine();
			it.append(ProxySelector.class).append(".setDefault(newSelector);"); //$NON-NLS-1$
			it.decreaseIndentation().newLine();
			it.append("}"); //$NON-NLS-1$
		}

		it.decreaseIndentation().newLine();
		it.append("}"); //$NON-NLS-1$
		it.newLine();

		write(outputFolder,
				BASE_PACKAGE, "AbstractBaseTest", //$NON-NLS-1$
				importManager, it);
	}

	private void generateRunner(File outputFolder) throws IOException {
		getLog().debug("Generating test runner"); //$NON-NLS-1$
		final ImportManager importManager = new ImportManager();
		final ITreeAppendable it = new FakeTreeAppendable(importManager);
		it.append("@").append(SuppressWarnings.class).append("(\"all\")"); //$NON-NLS-1$ //$NON-NLS-2$
		it.newLine();
		it.append("public class DocumentationTestRunner extends ").append(BlockJUnit4ClassRunner.class).append(" {"); //$NON-NLS-1$//$NON-NLS-2$
		it.increaseIndentation().newLine();
		it.append("protected ").append(Pattern.class).append(" pattern = "); //$NON-NLS-1$ //$NON-NLS-2$
		it.append(Pattern.class).append(".compile(\"^((?:success)|(?:failure)|(?:fact))\\\\_([0-9]+)\\\\_\");"); //$NON-NLS-1$
		it.newLine();
		it.append("protected String testName(").append(FrameworkMethod.class).append(" method) {"); //$NON-NLS-1$ //$NON-NLS-2$
		it.increaseIndentation().newLine();
		it.append("String methodName = method.getMethod().getName();"); //$NON-NLS-1$
		it.newLine();
		it.append(Matcher.class).append(" matcher = pattern.matcher(methodName);"); //$NON-NLS-1$
		it.newLine();
		it.append("if (matcher.find()) {"); //$NON-NLS-1$
		it.increaseIndentation().newLine();
		it.append("String typeName = matcher.group(1);"); //$NON-NLS-1$
		it.newLine();
		it.append("int lineno = Integer.valueOf(matcher.group(2));"); //$NON-NLS-1$
		it.newLine();
		it.append("if (\"success\".equals(typeName)) {"); //$NON-NLS-1$
		it.increaseIndentation().newLine();
		it.append("return \"Success block #\" + lineno;"); //$NON-NLS-1$
		it.decreaseIndentation().newLine();
		it.append("}"); //$NON-NLS-1$
		it.append("if (\"failure\".equals(typeName)) {"); //$NON-NLS-1$
		it.increaseIndentation().newLine();
		it.append("return \"Failing block #\" + lineno;"); //$NON-NLS-1$
		it.decreaseIndentation().newLine();
		it.append("}"); //$NON-NLS-1$
		it.append("if (\"fact\".equals(typeName)) {"); //$NON-NLS-1$
		it.increaseIndentation().newLine();
		it.append("return \"Factual expression #\" + lineno;"); //$NON-NLS-1$
		it.decreaseIndentation().newLine();
		it.append("}"); //$NON-NLS-1$
		it.decreaseIndentation().newLine();
		it.append("}"); //$NON-NLS-1$
		it.append("return methodName.replaceAll(\"[_]+\", \" \");"); //$NON-NLS-1$
		it.decreaseIndentation().newLine();
		it.append("}"); //$NON-NLS-1$
		it.newLine().newLine();
		it.append("protected ").append(List.class).append("<"); //$NON-NLS-1$ //$NON-NLS-2$
		it.append(FrameworkMethod.class).append("> getChildren() {"); //$NON-NLS-1$
		it.increaseIndentation().newLine();
		it.append(List.class).append("<").append(FrameworkMethod.class); //$NON-NLS-1$
		it.append("> sortedList = new ").append(ArrayList.class).append("<>();"); //$NON-NLS-1$ //$NON-NLS-2$
		it.newLine();
		it.append("for (").append(FrameworkMethod.class).append(" method : computeTestMethods()) {"); //$NON-NLS-1$ //$NON-NLS-2$
		it.increaseIndentation().newLine();
		it.append(ListUtil.class).append(".add(sortedList, (c1, c2) -> {"); //$NON-NLS-1$
		it.increaseIndentation().newLine();
		it.append(Matcher.class).append(" matcher1 = pattern.matcher(c1.getName());"); //$NON-NLS-1$
		it.newLine();
		it.append("if (!matcher1.find()) return c1.getName().compareTo(c2.getName());"); //$NON-NLS-1$
		it.newLine();
		it.append(Matcher.class).append(" matcher2 = pattern.matcher(c2.getName());"); //$NON-NLS-1$
		it.newLine();
		it.append("if (!matcher2.find()) return c1.getName().compareTo(c2.getName());"); //$NON-NLS-1$
		it.newLine();
		it.append("int cmp = Integer.compare(Integer.valueOf(matcher1.group(2)), Integer.valueOf(matcher1.group(2)));"); //$NON-NLS-1$
		it.newLine();
		it.append("if (cmp != 0) return cmp;"); //$NON-NLS-1$
		it.newLine();
		it.append("return c1.getName().compareTo(c2.getName());"); //$NON-NLS-1$
		it.decreaseIndentation().newLine();
		it.append("}, method, true, false);"); //$NON-NLS-1$
		it.decreaseIndentation().newLine();
		it.append("}"); //$NON-NLS-1$
		it.newLine();
		it.append("return sortedList;"); //$NON-NLS-1$
		it.decreaseIndentation().newLine();
		it.append("}"); //$NON-NLS-1$
		it.newLine().newLine();
		it.append("public DocumentationTestRunner(Class clazz) throws "); //$NON-NLS-1$
		it.append(InitializationError.class).append(" {"); //$NON-NLS-1$
		it.increaseIndentation().newLine();
		it.append("super(clazz);"); //$NON-NLS-1$
		it.decreaseIndentation().newLine();
		it.append("}"); //$NON-NLS-1$
		it.decreaseIndentation().newLine();
		it.append("}"); //$NON-NLS-1$
		it.newLine();

		write(outputFolder,
				BASE_PACKAGE, "DocumentationTestRunner", //$NON-NLS-1$
				importManager, it);
	}

	private static void write(File root, String packageName, String typeName, ImportManager importManager,
			ITreeAppendable it) throws IOException {
		final File relativeFolder = toPackageFolder(packageName);
		File outputFile = FileSystem.join(root, relativeFolder);
		outputFile.mkdirs();
		outputFile = new File(outputFile, typeName + ".java"); //$NON-NLS-1$
		try (FileWriter writer = new FileWriter(outputFile)) {
			writer.write("/* This file was automatically generated. Do not change its content. */\n\n"); //$NON-NLS-1$
			writer.write("package "); //$NON-NLS-1$
			writer.write(packageName);
			writer.write(";\n"); //$NON-NLS-1$
			for (final String importedType : importManager.getImports()) {
				writer.write("import "); //$NON-NLS-1$
				writer.write(importedType);
				writer.write(";\n"); //$NON-NLS-1$
			}
			writer.write(it.getContent());
			writer.flush();
		}
	}

	private static String toTestName(File inputFile) {
		return Strings.toFirstUpper(FileSystem.shortBasename(inputFile).replaceAll("[^a-zA-Z0-9]+", "")); //$NON-NLS-1$ //$NON-NLS-2$
	}

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy