186 lines
8.2 KiB
Java
186 lines
8.2 KiB
Java
package base;
|
|
|
|
import java.io.*;
|
|
import java.lang.reflect.InvocationTargetException;
|
|
import java.lang.reflect.Method;
|
|
import java.lang.reflect.Modifier;
|
|
import java.net.MalformedURLException;
|
|
import java.net.URL;
|
|
import java.net.URLClassLoader;
|
|
import java.nio.charset.StandardCharsets;
|
|
import java.nio.file.Files;
|
|
import java.nio.file.Path;
|
|
import java.util.ArrayList;
|
|
import java.util.List;
|
|
import java.util.stream.Collectors;
|
|
import java.util.stream.Stream;
|
|
|
|
/**
|
|
* @author Georgiy Korneev (kgeorgiy@kgeorgiy.info)
|
|
*/
|
|
@SuppressWarnings("unused")
|
|
@FunctionalInterface
|
|
public interface Runner {
|
|
List<String> run(final TestCounter counter, final List<String> input);
|
|
|
|
default List<String> run(final TestCounter counter, final String... input) {
|
|
return run(counter, List.of(input));
|
|
}
|
|
|
|
default void testEquals(final TestCounter counter, final List<String> input, final List<String> expected) {
|
|
counter.test(() -> {
|
|
final List<String> actual = run(counter, input);
|
|
for (int i = 0; i < Math.min(expected.size(), actual.size()); i++) {
|
|
final String exp = expected.get(i);
|
|
final String act = actual.get(i);
|
|
if (!exp.equalsIgnoreCase(act)) {
|
|
Asserts.assertEquals("Line " + (i + 1), exp, act);
|
|
return;
|
|
}
|
|
}
|
|
Asserts.assertEquals("Number of lines", expected.size(), actual.size());
|
|
});
|
|
}
|
|
|
|
static Packages packages(final String... packages) {
|
|
return new Packages(List.of(packages));
|
|
}
|
|
|
|
@FunctionalInterface
|
|
interface CommentRunner {
|
|
List<String> run(String comment, TestCounter counter, List<String> input);
|
|
}
|
|
|
|
final class Packages {
|
|
private final List<String> packages;
|
|
|
|
private Packages(final List<String> packages) {
|
|
this.packages = packages;
|
|
}
|
|
|
|
public Runner std(final String className) {
|
|
final CommentRunner main = main(className);
|
|
return (counter, input) -> counter.call("io", () -> {
|
|
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
|
try (final PrintWriter writer = new PrintWriter(baos, false, StandardCharsets.UTF_8)) {
|
|
input.forEach(writer::println);
|
|
}
|
|
|
|
final InputStream oldIn = System.in;
|
|
try {
|
|
System.setIn(new ByteArrayInputStream(baos.toByteArray()));
|
|
return main.run(String.format("[%d input lines]", input.size()), counter, List.of());
|
|
} finally {
|
|
System.setIn(oldIn);
|
|
}
|
|
});
|
|
}
|
|
|
|
@SuppressWarnings("ConfusingMainMethod")
|
|
public CommentRunner main(final String className) {
|
|
final Method method = findMain(className);
|
|
|
|
return (comment, counter, input) -> {
|
|
counter.format("Running test %02d: java %s %s%n", counter.getTestNo(), method.getDeclaringClass().getName(), comment);
|
|
final ByteArrayOutputStream out = new ByteArrayOutputStream();
|
|
@SuppressWarnings("UseOfSystemOutOrSystemErr") final PrintStream oldOut = System.out;
|
|
try {
|
|
System.setOut(new PrintStream(out, false, StandardCharsets.UTF_8));
|
|
method.invoke(null, new Object[]{input.toArray(String[]::new)});
|
|
final BufferedReader reader = new BufferedReader(new InputStreamReader(new ByteArrayInputStream(out.toByteArray()), StandardCharsets.UTF_8));
|
|
final List<String> result = new ArrayList<>();
|
|
while (true) {
|
|
final String line = reader.readLine();
|
|
if (line == null) {
|
|
if (result.isEmpty()) {
|
|
result.add("");
|
|
}
|
|
return result;
|
|
}
|
|
result.add(line.trim());
|
|
}
|
|
} catch (final InvocationTargetException e) {
|
|
final Throwable cause = e.getCause();
|
|
throw Asserts.error("main thrown exception %s: %s", cause.getClass().getSimpleName(), cause);
|
|
} catch (final Exception e) {
|
|
throw Asserts.error("Cannot invoke main: %s: %s", e.getClass().getSimpleName(), e.getMessage());
|
|
} finally {
|
|
System.setOut(oldOut);
|
|
}
|
|
};
|
|
}
|
|
|
|
private Method findMain(final String className) {
|
|
try {
|
|
final URL url = new File(".").toURI().toURL();
|
|
final List<String> candidates = packages.stream()
|
|
.flatMap(pkg -> {
|
|
final String prefix = pkg.isEmpty() ? pkg : pkg + ".";
|
|
return Stream.of(prefix + className, prefix + "$" + className);
|
|
})
|
|
.toList();
|
|
|
|
//noinspection ClassLoaderInstantiation,resource,IOResourceOpenedButNotSafelyClosed
|
|
final URLClassLoader classLoader = new URLClassLoader(new URL[]{url});
|
|
for (final String candidate : candidates) {
|
|
try {
|
|
final Class<?> loaded = classLoader.loadClass(candidate);
|
|
if (!Modifier.isPublic(loaded.getModifiers())) {
|
|
throw Asserts.error("Class %s is not public", candidate);
|
|
}
|
|
final Method main = loaded.getMethod("main", String[].class);
|
|
if (!Modifier.isPublic(main.getModifiers()) || !Modifier.isStatic(main.getModifiers())) {
|
|
throw Asserts.error("Method main of class %s should be public and static", candidate);
|
|
}
|
|
return main;
|
|
} catch (final ClassNotFoundException e) {
|
|
// Ignore
|
|
} catch (final NoSuchMethodException e) {
|
|
throw Asserts.error("Could not find method main(String[]) in class %s", candidate, e);
|
|
}
|
|
}
|
|
throw Asserts.error("Could not find neither of classes %s", candidates);
|
|
} catch (final MalformedURLException e) {
|
|
throw Asserts.error("Invalid path", e);
|
|
}
|
|
}
|
|
|
|
private static String getClassName(final String pkg, final String className) {
|
|
return pkg.isEmpty() ? className : pkg + "." + className;
|
|
}
|
|
|
|
public Runner args(final String className) {
|
|
final CommentRunner main = main(className);
|
|
// final AtomicReference<String> prev = new AtomicReference<>("");
|
|
return (counter, input) -> {
|
|
final int total = input.stream().mapToInt(String::length).sum() + input.size() * 3;
|
|
final String comment = total <= 300
|
|
? input.stream().collect(Collectors.joining("\" \"", "\"", "\""))
|
|
: input.size() <= 100
|
|
? String.format("[%d arguments, sizes: %s]", input.size(), input.stream()
|
|
.mapToInt(String::length)
|
|
.mapToObj(String::valueOf)
|
|
.collect(Collectors.joining(", ")))
|
|
: String.format("[%d arguments, total size: %d]", input.size(), total);
|
|
// assert comment.length() <= 5 || !prev.get().equals(comment) : "Duplicate tests " + comment;
|
|
// prev.set(comment);
|
|
return main.run(comment, counter, input);
|
|
};
|
|
}
|
|
|
|
public Runner files(final String className) {
|
|
final Runner args = args(className);
|
|
return (counter, input) -> counter.call("io", () -> {
|
|
final Path inf = counter.getFile("in");
|
|
final Path ouf = counter.getFile("out");
|
|
Files.write(inf, input);
|
|
args.run(counter, List.of(inf.toString(), ouf.toString()));
|
|
final List<String> output = Files.readAllLines(ouf);
|
|
Files.delete(inf);
|
|
Files.delete(ouf);
|
|
return output;
|
|
});
|
|
}
|
|
}
|
|
}
|