Files
JIMRI/java/test/jmri/NonnullBeforeVoidTest.java
T
2026-06-17 14:00:51 +02:00

128 lines
5.4 KiB
Java

package jmri;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import jmri.util.JUnitUtil;
import org.junit.jupiter.api.*;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
/**
* Check that there is no @Nonnull annotation before a void declaration.
* Also checks that "static" comes after and not before public/protected/private.
* Also checks that "final" comes after and not before public/protected/private/static.
*
* @author Daniel Bergqvist (C) 2026
*/
public class NonnullBeforeVoidTest {
private static final Pattern PATTERN_NULL_BEFORE_VOID = Pattern.compile(
".*" // Beginning of the file
+ "\\@Nonnull" // @Nonnull
+ "\\s+" // One or more whitespace characters
+ "(\\@\\w+" // Zero or more other annotations
+ "(\\(.*?\\))\\s+)*" // Possible parameters to the annotation
+ "(|public\\s+|protected\\s+|private\\s+)" // Optional public/protected/private/static declaration
+ "("
+ "void|" // void
+ "byte[^\\[]\\s+|" // byte but not byte array
+ "short[^\\[]\\s+|" // short but not short array
+ "int[^\\[]\\s+|" // int but not int array
+ "long[^\\[]\\s+" // long but not long array
+ ")"
+ ".*", // End of file
Pattern.MULTILINE | Pattern.DOTALL); // Check the entire file at once
// Check that static doesn't come before public/protected/private.
private static final Pattern PATTERN_STATIC_BEFORE_PUBLIC = Pattern.compile(".*static\\s+(public|protected|private).*", Pattern.MULTILINE | Pattern.DOTALL);
// Check that final doesn't come before public/protected/private/static.
private static final Pattern PATTERN_FINAL_BEFORE_PUBLIC_OR_STATIC = Pattern.compile(".*final\\s+(public|protected|private|static).*", Pattern.MULTILINE | Pattern.DOTALL);
public static Stream<Arguments> data() {
// Exclude java/graalvm/ since we don't have control over them
File[] directories = {new File("java/src/"), new File("java/test/")};
boolean recurse = true;
boolean pass = true;
return getFiles(directories, recurse, pass);
}
/**
* Get all Java files in a directory and validate them.
*
* @param directories the directories containing Java files
* @param recurse if true, will recurse into subdirectories
* @param pass if true, successful validation will pass; if false,
* successful validation will fail
* @return a stream of {@link Arguments}, where each Argument contains the
* {@link java.io.File} with a filename ending in {@literal .java} to
* validate and a boolean matching the pass parameter
*/
private static Stream<Arguments> getFiles(File[] directories, boolean recurse, boolean pass) {
ArrayList<Arguments> files = new ArrayList<>();
for (File directory : directories) {
if (directory.isDirectory()) {
for (File file : directory.listFiles()) {
if (file.isDirectory()) {
if (recurse) {
files.addAll(getFiles(new File[]{file}, recurse, pass).collect(Collectors.toList()));
}
} else {
files.addAll(getFiles(new File[]{file}, recurse, pass).collect(Collectors.toList()));
}
}
} else if (directory.getName().endsWith(".java")) {
files.add(Arguments.of(directory, pass));
}
}
files.sort((a,b) -> {
File aa = (File) a.get()[0];
File bb = (File) b.get()[0];
return aa.getAbsolutePath().compareTo(bb.getAbsolutePath());
});
return files.stream();
}
@ParameterizedTest(name = "{index}: {0} (pass={1})")
@MethodSource("data")
public void checkFilesTest(File file, boolean pass) throws IOException, JmriException, ParseException {
checkFile(file);
}
private void checkFile(File file) throws IOException {
String data = new String(Files.readAllBytes(file.toPath()));
if (PATTERN_NULL_BEFORE_VOID.matcher(data).matches()) {
Assertions.fail(String.format("File %s has @Nonnull annotation for a void declaration", file));
}
if (PATTERN_STATIC_BEFORE_PUBLIC.matcher(data).matches()) {
Assertions.fail(String.format("File %s has static before public/protected/private", file));
}
if (PATTERN_FINAL_BEFORE_PUBLIC_OR_STATIC.matcher(data).matches()) {
Assertions.fail(String.format("File %s has final before public/protected/private/static", file));
}
}
@BeforeEach
public void setUp() {
JUnitUtil.setUp();
}
@AfterEach
public void tearDown() {
JUnitUtil.tearDown();
}
// private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(NonnullBeforeVoidTest.class);
}