ExecUtils.java
/*
* Copyright 2019, Gerwin Klein, R��gis D��camps, Steve Rowe
* SPDX-License-Identifier: BSD-3-Clause
*/
package jflex.maven.plugin.testsuite;
import com.google.common.base.Joiner;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.PrintStream;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import jflex.core.OptionUtils;
import jflex.exceptions.GeneratorException;
import jflex.exceptions.SilentExit;
import jflex.logging.Out;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.taskdefs.Javac;
import org.apache.tools.ant.types.Path;
public class ExecUtils {
private ExecUtils() {}
private static final String JAVA_VERSION = "1.7";
public static final String NL = System.getProperty("line.separator");
/**
* Convert two Lists with String elements into one array containing all elements.
*
* @param a a List with String elements
* @param b a List with String elements
* @return an array containing all elements of a then b
*/
private static String[] toArray(List<String> a, List<String> b) {
if (a == null) a = new ArrayList<>();
if (b == null) b = new ArrayList<>();
String[] cmdline = new String[a.size() + b.size()];
int n = 0;
for (String aElem : a) cmdline[n++] = aElem;
for (String bElem : b) cmdline[n++] = bElem;
return cmdline;
}
/**
* Call javac on javaSourceFiles in input dir. If javaSourceFiles is {@code null}, all {@code
* *.java} files in the directory will be compiled.
*
* @param javaSourceFiles A list of files to compile, or {@code null}
* @param dir Source directory.
*/
public static TestResult execJavac(
List<String> javaSourceFiles, File dir, String additionalJars, String encoding)
throws FileNotFoundException {
// javac fails if an input file doesn't exist
checkFilesExist(javaSourceFiles, dir);
Project p = new Project();
Path path = new Path(p, dir.toString());
Javac javac = new Javac();
javac.setProject(p);
javac.setSrcdir(path);
javac.setDestdir(dir);
javac.setTarget(JAVA_VERSION);
javac.setSource(JAVA_VERSION);
javac.setSourcepath(new Path(p, "")); // Only compile explicitly specified source files
if (javaSourceFiles != null) {
javac.setIncludes(Joiner.on(' ').join(javaSourceFiles));
}
javac.setEncoding(encoding);
Path classPath = javac.createClasspath();
// Locate the jflex jar in the user's Maven local repository
classPath.setPath(additionalJars);
ByteArrayOutputStream out = new ByteArrayOutputStream();
PrintStream outSafe = System.err;
System.setErr(new PrintStream(out));
try {
javac.execute();
return new TestResult(out.toString(), true);
} catch (BuildException e) {
return new TestResult(e + NL + out.toString() + NL + "classpath: " + classPath, false);
} finally {
System.setErr(outSafe);
}
}
/** Call jflex with command line and input files. */
public static TestResult execJFlex(List<String> cmdline, List<String> files) {
String[] cmd = toArray(cmdline, files);
ByteArrayOutputStream out = new ByteArrayOutputStream();
try {
OptionUtils.setDefaultOptions();
Out.setOutputStream(out);
jflex.Main.generate(cmd);
return new TestResult(out.toString(), true);
} catch (GeneratorException e) {
return new TestResult(out.toString(), false);
} catch (SilentExit e) {
return new TestResult(out.toString(), false);
}
}
/**
* Call main method of specified class with command line and input files.
*
* @param path the directory in which to search for the class
* @param additionalJars
*/
public static TestResult execClass(
String theClass,
String path,
List<String> files,
List<File> additionalJars,
String outputFileEncoding,
List<String> cmdline)
throws UnsupportedEncodingException {
String[] cmd = toArray(cmdline, files);
Class<?> c;
Method main;
Class<?>[] sig = {String[].class};
// System.out.println("exec class "+theClass);
// System.out.println("cmdline "+cmdline+"\nfiles: "+files);
CustomClassLoader l = new CustomClassLoader(path);
// Locate the shaded jar in the lib directory
// TODO(regisd) Alternatively, we could load JFlex and its dependency graph.
try {
for (File jar : additionalJars) {
// We are in $basedir/testsuite/testcases
l.addPath(jar);
}
} catch (FileNotFoundException e) {
throw new RuntimeException(e);
}
try {
c = l.loadClass(theClass, true);
} catch (ClassNotFoundException e) {
System.out.println("no such class: " + e);
return null;
}
try {
main = c.getMethod("main", sig);
} catch (NoSuchMethodException e) {
System.out.println("no main: " + e);
return null;
}
ByteArrayOutputStream out = new ByteArrayOutputStream();
boolean success = true;
// System.out.println("loaded class "+theClass);
PrintStream stdOut = System.out;
System.setOut(new PrintStream(out, true));
try {
Object[] params = {cmd};
main.invoke(null, params);
System.setOut(stdOut);
} catch (IllegalAccessException e) {
System.setOut(stdOut);
System.out.println("main not public :" + e + main);
return null;
} catch (InvocationTargetException e) {
System.setOut(stdOut);
System.out.println("test subject threw exception :" + e);
success = false;
}
// System.out.println("finished exec class "+theClass);
return new TestResult(out.toString(outputFileEncoding), success);
}
/**
* Checks that all files exist in the given directory.
*
* @throws FileNotFoundException when a file doesn't exist.
*/
private static void checkFilesExist(List<String> files, File dir) throws FileNotFoundException {
if (files != null) {
for (String src : files) {
File f = new File(dir, src);
if (!f.isFile()) {
throw new FileNotFoundException(f.getPath());
}
}
}
}
}