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.11.0 2020-05-31 12:35:45$
 *
 * SARL is an general-purpose agent programming language.
 * More details on http://www.sarl.io
 *
 * Copyright (C) 2014-2020 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;
import java.net.Proxy.Type;
import java.net.ProxySelector;
import java.net.SocketAddress;
import java.net.SocketTimeoutException;
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.arakhne.afc.vmutil.FileSystem;
import org.eclipse.emf.common.util.URI;
import org.eclipse.xtext.generator.trace.ILocationData;
import org.eclipse.xtext.generator.trace.LocationData;
import org.eclipse.xtext.generator.trace.TraceRegionSerializer;
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.jupiter.api.Assertions;
import org.junit.jupiter.api.Assumptions;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;
import org.opentest4j.AssertionFailedError;
import org.opentest4j.TestAbortedException;

import io.sarl.lang.SARLVersion;
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.11.0 2020-05-31 12:35:45
 * @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;

	private TraceRegionSerializer traceSerializer;

	/** Replies the trace serializer.
	 *
	 * @return the trace serializer.
	 */
	protected TraceRegionSerializer getTraceSerializer() {
		if (this.traceSerializer == null) {
			this.traceSerializer = this.injector.getInstance(TraceRegionSerializer.class);
		}
		return this.traceSerializer;
	}

	@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(Messages.GenerateTestsMojo_11);
		final File output = FileSystem.convertStringToFile(this.testSourceDirectory);
		final String msg = internalExecute(files, output);
		if (!Strings.isEmpty(msg)) {
			return msg;
		}

		try {
			generateAbstractTest(output);
		} catch (IOException exception) {
			final String message = Throwables.getRootCause(exception).getLocalizedMessage();
			getLog().error(message);
			getLog().debug(exception);
			return message;
		}

		return null;
	}

	@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 test classes.
		final String basicTestName = toTestName(inputFile);
		final String generalTestName = basicTestName + "Test"; //$NON-NLS-1$

		final ImportManager importManager = new ImportManager();
		final TraceableTreeAppendable it = new TraceableTreeAppendable(importManager);

		final String displayName = toClassDisplayName(relativeInputFile, basicTestName, generalTestName);
		
		it.append("@").append(DisplayName.class).append("(\"").append(displayName).append("\")").newLine();
		it.append("public class ").append(generalTestName).append(" extends ")
			.append(BASE_PACKAGE).append(".AbstractBaseTest").append(" {").increaseIndentation().newLine();
		
		generateTestsForSuccessCode(it, importManager, relativeInputFile, successCompilationComponents);
		generateTestsForFailureCode(it, importManager, relativeInputFile, failureCompilationComponents);
		generateTestsForFacts(it, importManager, relativeInputFile, factualComponents);
		generateDynamicTests(it, importManager, relativeInputFile, specificComponents);

		it.decreaseIndentation().newLine().append("}").newLine();

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

		//generateTraceFile(outputFolder, packageName, generalTestName, it.getTraceRegions());
	}

	private void generateTestsForSuccessCode(ITreeAppendable parent, ImportManager importManager,
			File inputFile, List successCompilationComponents) {
		int i = 0;
		for (final ValidationComponent component : successCompilationComponents) {
			getLog().debug(MessageFormat.format(Messages.GenerateTestsMojo_1,
					inputFile.getName(), component.getLinenoInSourceFile(), component.getCode()));
			final String actionName = toActionName("success", component, i); //$NON-NLS-1$
			final String displayName = toTestDisplayName(Messages.GenerateTestsMojo_7, i, component);
			final ILocationData location = new LocationData(
					// Offset
					component.getOffsetInSourceFile(),
					// Length
					component.getLengthInSourceFile(),
					// Line number
					component.getLinenoInSourceFile(),
					// End line number
					component.getEndLinenoInSourceFile(),
					// Source URI
					null);
			final ITreeAppendable it = parent.trace(location);
			//
			it.append("@").append(Test.class).newLine();
			it.append("@").append(DisplayName.class).append("(\"").append(displayName).append("\")").newLine();
			it.append("@").append(Tag.class).append("(\"success\")").newLine();
			it.append("public void ").append(actionName).append("() throws ")
				.append(Exception.class).append(" {").increaseIndentation().newLine();
			it.append(List.class).append(" issues = getScriptExecutor().compile(")
				.append(str(component.getLinenoInSourceFile())).append(", \"")
				.append(str(component.getCode())).append("\");").newLine();
			it.append("assertNoIssue(").append(str(component.getLinenoInSourceFile()))
				.append(", issues);").decreaseIndentation().newLine();
			it.append("}").newLine();
			//
			++i;
		}
	}

	private void generateTestsForFailureCode(ITreeAppendable parent, ImportManager importManager,
			File inputFile, List failureCompilationComponents) {
		int i = 0;
		for (final ValidationComponent component : failureCompilationComponents) {
			getLog().debug(MessageFormat.format(Messages.GenerateTestsMojo_2,
					inputFile.getName(), component.getLinenoInSourceFile(), component.getCode()));
			final String actionName = toActionName("failure", component, i); //$NON-NLS-1$
			final String displayName = toTestDisplayName(Messages.GenerateTestsMojo_8, i, component);
			final ILocationData location = new LocationData(
					// Offset
					component.getOffsetInSourceFile(),
					// Length
					component.getLengthInSourceFile(),
					// Line number
					component.getLinenoInSourceFile(),
					// End line number
					component.getEndLinenoInSourceFile(),
					// Source URI
					null);
			final ITreeAppendable it = parent.trace(location);
			//
			it.append("@").append(Test.class).newLine();
			it.append("@").append(DisplayName.class).append("(\"").append(displayName).append("\")").newLine();
			it.append("@").append(Tag.class).append("(\"failure\")").newLine();
			it.append("public void ").append(actionName).append("() throws ")
				.append(Exception.class).append(" {").increaseIndentation().newLine();
			it.append(List.class).append(" issues = getScriptExecutor().compile(")
				.append(str(component.getLinenoInSourceFile())).append(", \"")
				.append(str(component.getCode())).append("\");").newLine();
			it.append("assertIssues(").append(str(component.getLinenoInSourceFile()))
				.append(", issues);").decreaseIndentation().newLine();
			it.append("}").newLine();
			//
			++i;
		}
	}

	private void generateTestsForFacts(ITreeAppendable parent, ImportManager importManager,
			File inputFile, List factualComponents) {
		int i = 0;
		for (final ValidationComponent component : factualComponents) {
			getLog().debug(MessageFormat.format(Messages.GenerateTestsMojo_3,
					inputFile.getName(), component.getLinenoInSourceFile(), component.getCode()));
			final String actionName = toActionName("fact", component, i); //$NON-NLS-1$
			final String displayName = toTestDisplayName(Messages.GenerateTestsMojo_9, i, component);
			final ILocationData location = new LocationData(
					// Offset
					component.getOffsetInSourceFile(),
					// Length
					component.getLengthInSourceFile(),
					// Line number
					component.getLinenoInSourceFile(),
					// End line number
					component.getEndLinenoInSourceFile(),
					// Source URI
					null);
			final ITreeAppendable it = parent.trace(location);
			//
			it.append("@").append(Test.class).newLine();
			it.append("@").append(DisplayName.class).append("(\"").append(displayName).append("\")").newLine();
			it.append("@").append(Tag.class).append("(\"fact\")").newLine();
			it.append("public void ").append(actionName).append("() throws ")
				.append(Exception.class).append(" {").increaseIndentation().newLine();
			it.append("final String expected = ").append(Utils.class).append(".dump(")
				.append(Boolean.class).append(".TRUE, false) + \"\\nOR\\nObject {\\n}\\n\";").newLine();
			it.append("Object result;").newLine();
			it.append("try {").increaseIndentation().newLine();
			it.append("result = getScriptExecutor().execute(").append(str(component.getLinenoInSourceFile()))
				.append(", \"").append(str(component.getCode())).append("\");").decreaseIndentation().newLine();
			it.append("} catch (").append(Throwable.class).append(" exception) {").increaseIndentation().newLine();
			it.append("throw new ").append(AssertionFailedError.class)
				.append("(exception.getLocalizedMessage() + \" [line: ")
				.append(str(component.getLinenoInSourceFile())).append("]\", expected, ")
				.append(Throwables.class).append(".getStackTraceAsString(exception));").decreaseIndentation().newLine();
			it.append("}").newLine();
			it.append("if (result instanceof ").append(Boolean.class).append(") {").increaseIndentation().newLine();
			it.append("boolean boolResult = ((").append(Boolean.class).append(") result).booleanValue();").newLine();
			it.append("if (!boolResult) {").increaseIndentation().newLine();
			it.append("throw new ").append(AssertionFailedError.class)
				.append("(\"Invalid expression result [line: ").append(str(component.getLinenoInSourceFile()))
				.append("]\", expected, ").append(Utils.class).append(".dump(result, false));").decreaseIndentation().newLine();
			it.append("}").decreaseIndentation().newLine();
			it.append("} else if (result == null || result instanceof ").append(Exception.class)
				.append(") {").increaseIndentation().newLine();
			it.append("throw new ").append(AssertionFailedError.class)
				.append("(\"Invalid expression result [line: ").append(str(component.getLinenoInSourceFile()))
				.append("]\", expected, ").append(Utils.class).append(".dump(result, false));").decreaseIndentation().newLine();
			it.append("}").decreaseIndentation().newLine();
			it.append("}").newLine();
			//
			++i;
		}
	}

	private void generateDynamicTests(ITreeAppendable parent, ImportManager importManager,
			File inputFile, List specificComponents) {
		int i = 0;
		for (final DynamicValidationComponent component : specificComponents) {
			getLog().debug(MessageFormat.format(Messages.GenerateTestsMojo_4,
					inputFile.getName(), component.functionName() + i));
			final String actionName = toActionName("dyn_" + component.functionName(), component, i);
			final String displayName = toTestDisplayName(Messages.GenerateTestsMojo_10, i, component);
			final ILocationData location = new LocationData(
					// Offset
					component.getOffsetInSourceFile(),
					// Length
					component.getLengthInSourceFile(),
					// Line number
					component.getLinenoInSourceFile(),
					// End line number
					component.getEndLinenoInSourceFile(),
					// Source URI
					null);
			final ITreeAppendable it = parent.trace(location);
			//
			it.append("@").append(Test.class).newLine();
			it.append("@").append(DisplayName.class).append("(\"").append(displayName).append("\")").newLine();
			it.append("@").append(Tag.class).append("(\"other\")").newLine();
			it.append("public void ").append(actionName).append("() throws ")
				.append(Exception.class).append(" {").increaseIndentation().newLine();
			component.generateValidationCode(it);
			it.decreaseIndentation().newLine();
			it.append("}").newLine();
			//
			++i;
		}
	}

	/*private void generateTraceFile(File root, String packageName, String typeName, Collection regions) throws IOException {
		if (!regions.isEmpty()) {
			File folderFile = getOutputFolder(root, packageName);
			File outputFile = getOutputTraceFilename(folderFile, typeName);
			getLog().debug(MessageFormat.format(Messages.GenerateTestsMojo_6, outputFile.getName()));
			folderFile.mkdirs();
			try (final FileOutputStream os = new FileOutputStream(outputFile)) {
				for (final AbstractTraceRegion region : regions) {
					getTraceSerializer().writeTraceRegionTo(region, os);
				}
			}
		}
	}*/

	@SuppressWarnings({"checkstyle:methodlength", "checkstyle:npathcomplexity"})
	private void generateAbstractTest(File outputFolder) throws IOException {
		getLog().debug(Messages.GenerateTestsMojo_5); //$NON-NLS-1$
		final ImportManager importManager = new ImportManager();
		final ITreeAppendable it = new FakeTreeAppendable(importManager);

		it.append("@").append(SuppressWarnings.class).append("(\"all\")").newLine();
		it.append("public class AbstractBaseTest {").increaseIndentation().newLine();
		it.append("protected static String STR_SUCCESS = \"success\";").newLine();
		it.append("protected static String STR_FAILURE = \"failure\";").newLine();
		it.append("protected static String STR_FACT = \"fact\";").newLine();
		it.append("private static ").append(Injector.class).append(" injector = ")
		.append(DocumentationSetup.class).append(".doSetup();").newLine();
		it.append("private ").append(ScriptExecutor.class).append(" scriptExecutor;").newLine();

		it.append("protected ").append(ScriptExecutor.class).append(" getScriptExecutor() {").increaseIndentation().newLine();
		it.append("if (this.scriptExecutor == null) {").increaseIndentation().newLine();
		it.append("this.scriptExecutor = this.injector.getInstance(").append(ScriptExecutor.class).append(".class);").newLine();

		final StringBuilder cp = new StringBuilder();
		for (final File cpElement : getClassPath()) {
			if (cp.length() > 0) {
				cp.append(File.pathSeparator);
			}
			cp.append(cpElement.getAbsolutePath());
		}
		it.append("scriptExecutor.setClassPath(\"").append(str(cp)).append("\");").newLine();

		final String bootPath = getBootClassPath();
		if (!Strings.isEmpty(bootPath)) {
			it.append("scriptExecutor.setBootClassPath(\"") //$NON-NLS-1$
				.append(str(bootPath)).append("\");") //$NON-NLS-1$
				.newLine();
		}
		JavaVersion version = null;
		if (!Strings.isEmpty(this.source)) {
			try {
				version = JavaVersion.fromQualifier(this.source);
			} catch (Throwable exception) {
				version = null;
			}
		}
		if (version == null) {
			try {
				version = JavaVersion.fromQualifier(SARLVersion.MINIMAL_JDK_VERSION_FOR_SARL_COMPILATION_ENVIRONMENT);
			} catch (Throwable exception) {
				version = null;
			}
		}
		if (version == null) {
			throw new IllegalStateException("unknown Java version"); //$NON-NLS-1$
		}
		it.append("scriptExecutor.setJavaSourceVersion(\"").append(version.getQualifier()).append("\");").newLine();

		it.append("scriptExecutor.setTempFolder(new ").append(File.class).append("(\"")
		.append(str(this.tempDirectory.getAbsolutePath())).append("\"));")
		.decreaseIndentation().newLine();

		it.append("}").newLine();
		it.append("return this.scriptExecutor;").decreaseIndentation().newLine();
		it.append("}").newLine();

		it.append("public void assertNoIssue(int lineno, ").append(List.class).append(" issues) {")
		.increaseIndentation().newLine();
		it.append("if (issues != null && !issues.isEmpty()) {").increaseIndentation().newLine();
		it.append(StringBuilder.class).append(" msg = new ").append(StringBuilder.class).append("();").newLine();
		it.append("for (String message : issues) {").increaseIndentation().newLine();
		it.append("msg.append(message).append(\"\\n\");").decreaseIndentation().newLine();
		it.append("}").newLine();
		it.append("throw new ").append(AssertionFailedError.class)
		.append("(\"Expecting no issue but find one [line:\" + lineno + \"]\", \"\", msg.toString());")
		.decreaseIndentation().newLine();
		it.append("}").decreaseIndentation().newLine();
		it.append("}").newLine();

		it.append("public void assertIssues(int lineno, ").append(List.class).append(" issues) {")
		.increaseIndentation().newLine();
		it.append("if (issues == null || issues.isEmpty()) {").increaseIndentation().newLine();
		it.append(Assertions.class).append(".fail(\"Expecting issues but did not find one [line:\" + lineno + \"]\");")
		.decreaseIndentation().newLine();
		it.append("}").decreaseIndentation().newLine();
		it.append("}").newLine();

		it.append("public String computeHeaderIdWithSectionNumber(String header) {").increaseIndentation().newLine();
		it.append("String id = header.replaceAll(\"[^a-zA-Z0-9]+\", \"-\");").newLine();
		it.append("id = id.toLowerCase();").newLine();
		it.append("id = id.replaceFirst(\"^[^a-zA-Z0-9]+\", \"\");").newLine();
		it.append("id = id.replaceFirst(\"[^a-zA-Z0-9]+$\", \"\");").newLine();
		it.append("if (").append(Strings.class).append(".isEmpty(id)) {").increaseIndentation().newLine();
		it.append("return \"section\";").decreaseIndentation().newLine();
		it.append("}").newLine();
		it.append("return id;").decreaseIndentation().newLine();
		it.append("}").newLine();

		it.append("public String computeHeaderIdWithoutSectionNumber(String header) {").increaseIndentation().newLine();
		it.append("String id = computeHeaderIdWithSectionNumber(header);").newLine();
		it.append("id = id.replaceFirst(\"^[0-9.\\\\-]+\", \"\");").newLine();
		it.append("return id;").decreaseIndentation().newLine();
		it.append("}").newLine();

		it.append("public static String getHttpCodeExplanation(int code) {").increaseIndentation().newLine();
		it.append("switch (code) {").increaseIndentation().newLine();
		it.append("case ").append(HttpURLConnection.class).append(".HTTP_BAD_METHOD: return \"Method Not Allowed\";").newLine();
		it.append("case ").append(HttpURLConnection.class).append(".HTTP_CREATED: return \"Created\";").newLine();
		it.append("case ").append(HttpURLConnection.class).append(".HTTP_ACCEPTED: return \"Accepted\";").newLine();
		it.append("case ").append(HttpURLConnection.class).append(".HTTP_NOT_AUTHORITATIVE: return \"Non-Authoritative Information\";").newLine();
		it.append("case ").append(HttpURLConnection.class).append(".HTTP_NO_CONTENT: return \"No Content\";").newLine();
		it.append("case ").append(HttpURLConnection.class).append(".HTTP_RESET: return \"Reset Content\";").newLine();
		it.append("case ").append(HttpURLConnection.class).append(".HTTP_PARTIAL: return \"Partial Content\";").newLine();
		it.append("case ").append(HttpURLConnection.class).append(".HTTP_MULT_CHOICE: return \"Multiple Choices\";").newLine();
		it.append("case ").append(HttpURLConnection.class).append(".HTTP_MOVED_PERM: return \"Moved Permanently\";").newLine();
		it.append("case ").append(HttpURLConnection.class).append(".HTTP_MOVED_TEMP: return \"Temporary Redirect\";").newLine();
		it.append("case ").append(HttpURLConnection.class).append(".HTTP_SEE_OTHER: return \"See Other\";").newLine();
		it.append("case ").append(HttpURLConnection.class).append(".HTTP_NOT_MODIFIED: return \"Not Modified\";").newLine();
		it.append("case ").append(HttpURLConnection.class).append(".HTTP_USE_PROXY: return \"Use Proxy\";").newLine();
		it.append("case ").append(HttpURLConnection.class).append(".HTTP_BAD_REQUEST: return \"Bad Request\";").newLine();
		it.append("case ").append(HttpURLConnection.class).append(".HTTP_UNAUTHORIZED: return \"Unauthorized\";").newLine();
		it.append("case ").append(HttpURLConnection.class).append(".HTTP_PAYMENT_REQUIRED: return \"Payment Required\";").newLine();
		it.append("case ").append(HttpURLConnection.class).append(".HTTP_FORBIDDEN: return \"Forbidden\";").newLine();
		it.append("case ").append(HttpURLConnection.class).append(".HTTP_NOT_FOUND: return \"Not Found\";").newLine();
		it.append("case ").append(HttpURLConnection.class).append(".HTTP_NOT_ACCEPTABLE: return \"Not Acceptable\";").newLine();
		it.append("case ").append(HttpURLConnection.class).append(".HTTP_PROXY_AUTH: return \"Proxy Authentication Required\";").newLine();
		it.append("case ").append(HttpURLConnection.class).append(".HTTP_CLIENT_TIMEOUT: return \"Request Time-Out\";").newLine();
		it.append("case ").append(HttpURLConnection.class).append(".HTTP_CONFLICT: return \"Conflict\";").newLine();
		it.append("case ").append(HttpURLConnection.class).append(".HTTP_GONE: return \"Gone\";").newLine();
		it.append("case ").append(HttpURLConnection.class).append(".HTTP_LENGTH_REQUIRED: return \"Length Required\";").newLine();
		it.append("case ").append(HttpURLConnection.class).append(".HTTP_PRECON_FAILED: return \"Precondition Failed\";").newLine();
		it.append("case ").append(HttpURLConnection.class).append(".HTTP_ENTITY_TOO_LARGE: return \"Request Entity Too Large\";").newLine();
		it.append("case ").append(HttpURLConnection.class).append(".HTTP_REQ_TOO_LONG: return \"Request-URI Too Large\";").newLine();
		it.append("case ").append(HttpURLConnection.class).append(".HTTP_UNSUPPORTED_TYPE: return \"Unsupported Media Type\";").newLine();
		it.append("case ").append(HttpURLConnection.class).append(".HTTP_INTERNAL_ERROR: return \"Internal Server Error\";").newLine();
		it.append("case ").append(HttpURLConnection.class).append(".HTTP_NOT_IMPLEMENTED: return \"Not Implemented\";").newLine();
		it.append("case ").append(HttpURLConnection.class).append(".HTTP_BAD_GATEWAY: return \"Bad Gateway\";").newLine();
		it.append("case ").append(HttpURLConnection.class).append(".HTTP_UNAVAILABLE: return \"Service Unavailable\";").newLine();
		it.append("case ").append(HttpURLConnection.class).append(".HTTP_GATEWAY_TIMEOUT: return \"Gateway Timeout\";").newLine();
		it.append("case ").append(HttpURLConnection.class).append(".HTTP_VERSION: return \"HTTP Version Not Supported\";").newLine();
		it.append("default: return null;").decreaseIndentation().newLine();
		it.append("}").decreaseIndentation().newLine();
		it.append("}").newLine();

		it.append("public static boolean isAcceptableHttpCode(int code) {").increaseIndentation().newLine();
		it.append("return code == ").append(HttpURLConnection.class)
		.append(".HTTP_OK || code == ").append(HttpURLConnection.class).append(".HTTP_MOVED_TEMP;")
		.decreaseIndentation().newLine();
		it.append("}").newLine();

		it.append("public static void assertURLAccessibility(int lineno, ").append(URL.class)
		.append(" url) throws ").append(Exception.class).append(" {").increaseIndentation().newLine();
		it.append("int code;").newLine();
		it.append(HttpURLConnection.class).append(".setFollowRedirects(false);").newLine();
		it.append(URLConnection.class).append(" connection = url.openConnection();").newLine();
		it.append(Assumptions.class).append(".assumeTrue(connection instanceof ")
		.append(HttpURLConnection.class).append(", \"Not an UTL with http[s] protocol\");").newLine();
		it.append(HttpURLConnection.class).append(" httpConnection = (").append(HttpURLConnection.class)
		.append(") connection;").newLine();
		it.append("try {").increaseIndentation().newLine();
		it.append("httpConnection.setInstanceFollowRedirects(false);").newLine();
		it.append("httpConnection.setConnectTimeout(").append(str(this.remoteLinkTimeOut)).append(");").newLine();
		it.append("httpConnection.setReadTimeout(").append(str(this.remoteLinkTimeOut)).append(");").newLine();
		it.append("httpConnection.setAllowUserInteraction(false);").newLine();
		it.append("httpConnection.setRequestMethod(\"HEAD\");").newLine();
		it.append("httpConnection.connect();").newLine();
		it.append("code = httpConnection.getResponseCode();").decreaseIndentation().newLine();
		it.append("} catch (").append(IOException.class).append(" exception) {").increaseIndentation().newLine();
		it.append(Throwable.class).append(" rootCause = ").append(Throwables.class).append(".getRootCause(exception);").newLine();
		if (this.ignoreRemoteLinkTimeOut) {
			it.append("if (rootCause instanceof ").append(SocketTimeoutException.class).append(") {").increaseIndentation().newLine();
			it.append("throw new ").append(TestAbortedException.class)
			.append("(\"Connection time-out at line \" + lineno + \" when connecting to: \" + url.toExternalForm());")
			.decreaseIndentation().newLine();
			it.append("}").newLine();
		}
		it.append("throw new ").append(RuntimeException.class)
		.append("(\"Error at line \" + lineno + \" when connecting to: \" + url.toExternalForm(), rootCause);")
		.decreaseIndentation().newLine();
		it.append("} finally {").increaseIndentation().newLine();
		it.append("httpConnection.disconnect();").decreaseIndentation().newLine();
		it.append("}").newLine();
		it.append("if (isAcceptableHttpCode(code)) {").increaseIndentation().newLine();
		it.append("return;").decreaseIndentation().newLine();
		it.append("}").newLine();
		it.append("String explanation = getHttpCodeExplanation(code);").newLine();
		it.append("String codeMsg = !").append(Strings.class).append(".isEmpty(explanation) ? code + \"\\\"\" + explanation + \"\\\"\" : Integer.toString(code);")
		.newLine();
		it.append(Assertions.class).append(".fail(\"Invalid response code \" + codeMsg + \" at line \" + lineno + \" when connecting to: \" + url.toExternalForm());")
		.decreaseIndentation().newLine();
		it.append("}").newLine();

		if (!this.session.isOffline() && !this.session.getRequest().getProxies().isEmpty()) {

			it.append("private static boolean proxyNameMatches(String pattern, String name) {").increaseIndentation().newLine();
			it.append(Pattern.class).append(" pat = ").append(Pattern.class)
			.append(".compile(pattern, ").append(Pattern.class).append(".CASE_INSENSITIVE);").newLine();
			it.append(Matcher.class).append(" mat = pat.matcher(name);").newLine();
			it.append("return mat.matches();").decreaseIndentation().newLine();
			it.append("}").newLine();

			it.append("static {").increaseIndentation().newLine();
			it.append("final ").append(ProxySelector.class).append(" defaultSelector = ")
			.append(ProxySelector.class).append(".getDefault();").newLine();
			it.append(ProxySelector.class).append(" newSelector = new ")
			.append(ProxySelector.class).append("() {").increaseIndentation().newLine();
			it.append("public ").append(List.class).append("<").append(Proxy.class)
			.append("> select(").append(URI.class).append(" uri) {").increaseIndentation().newLine();
			it.append(List.class).append("<").append(Proxy.class).append("> proxies = new ")
			.append(ArrayList.class).append("(defaultSelector.select(uri));").newLine();

			for (final org.apache.maven.settings.Proxy proxy : this.session.getRequest().getProxies()) {
				it.append("if (\"").append(str(proxy.getProtocol())).append(".equals(uri.getScheme())) {").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(Proxy.class).append("(")
				.append(Type.class).append(".HTTP, new ").append(InetSocketAddress.class)
				.append("(\"").append(str(proxy.getHost())).append("\", ")
				.append(str(proxy.getPort())).append(")));");

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

			}

			it.newLine().append("return ").append(Collections.class).append(".unmodifiableList(proxies);")
			.decreaseIndentation().newLine();
			it.append("}").newLine();
			it.append("public void connectFailed(").append(URI.class).append(" uri, ")
			.append(SocketAddress.class).append(" sa, ").append(IOException.class).append(" ioe) {")
			.increaseIndentation().newLine();
			it.append("throw new ").append(RuntimeException.class).append("(ioe);").decreaseIndentation().newLine();
			it.append("}").decreaseIndentation().newLine();
			it.append("};").newLine();
			it.append(ProxySelector.class).append(".setDefault(newSelector);").decreaseIndentation().newLine();
			it.append("}");
		}

		it.decreaseIndentation().newLine().append("}").newLine();

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

	/** Create the name for the output filder.
	 *
	 * @param root the root folder.
	 * @param packageName the name of the package.
	 * @return the output folder.
	 */
	private static File getOutputFolder(File root, String packageName) {
		final File relativeFolder = toPackageFolder(packageName);
		return FileSystem.join(root, relativeFolder);
	}

	/** Create the name for the Java file.
	 *
	 * @param folderName the folder name.
	 * @param typeName the type name.
	 * @return the Java filename.
	 */
	protected static File getOutputJavaFilename(File folderName, String typeName) {
		return new File(folderName, typeName + ".java"); //$NON-NLS-1$
	}

	/** Create the name for the trace file.
	 *
	 * @param folderName the folder name.
	 * @param typeName the type name.
	 * @return the trace filename.
	 */
	protected static File getOutputTraceFilename(File folderName, String typeName) {
		return new File(folderName, typeName + ".java._trace"); //$NON-NLS-1$
	}

	private static void write(File root, String packageName, String typeName, ImportManager importManager,
			ITreeAppendable it) throws IOException {
		File outputFile = getOutputFolder(root, packageName);
		outputFile.mkdirs();
		outputFile = getOutputJavaFilename(outputFile, typeName);
		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$
	}

	private static String toActionName(String name, ValidationComponent component, int index) {
		final StringBuilder fullName = new StringBuilder();
		fullName.append(name).append("_");
		fullName.append(component.getLinenoInSourceFile()).append("_to_");
		fullName.append(component.getEndLinenoInSourceFile()).append("_");
		fullName.append(index);
		return fullName.toString();
	}

	private static String toTestDisplayName(String name, int index, ValidationComponent component) {
		final String nm;
		if (Strings.isEmpty(name)) {
			if (component instanceof DynamicValidationComponent) {
				nm = ((DynamicValidationComponent) component).functionName();
			} else {
				nm = component.getSourceFile().getName();
			}
		} else {
			nm = name;
		}
		return MessageFormat.format(nm, index, component.getLinenoInSourceFile());
	}

	private static String toClassDisplayName(File inputFile, String basicTestName, String generalTestName) {
		return MessageFormat.format(Messages.GenerateTestsMojo_12, inputFile.getName(), basicTestName, generalTestName,
				inputFile.getParentFile().getPath());
	}

	private static String str(Object obj) {
		if (obj == null) {
			return ""; //$NON-NLS-1$
		}
		return Strings.convertToJavaString(obj.toString());
	}

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy