diff --git a/fuzz/src/main/java/edu/berkeley/cs/jqf/fuzz/ei/ZestGuidance.java b/fuzz/src/main/java/edu/berkeley/cs/jqf/fuzz/ei/ZestGuidance.java index b9df383f5..38f352f47 100644 --- a/fuzz/src/main/java/edu/berkeley/cs/jqf/fuzz/ei/ZestGuidance.java +++ b/fuzz/src/main/java/edu/berkeley/cs/jqf/fuzz/ei/ZestGuidance.java @@ -43,6 +43,9 @@ import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.time.Duration; +import java.time.Instant; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Arrays; @@ -589,7 +592,8 @@ protected int getTargetChildrenForParent(Input parentInput) { protected void completeCycle() { // Increment cycle count cyclesCompleted++; - infoLog("\n# Cycle " + cyclesCompleted + " completed."); + final String now = DateTimeFormatter.ISO_DATE_TIME.format(LocalDateTime.now()); + infoLog("\n# Cycle " + cyclesCompleted + " completed at " + now); // Go over all inputs and do a sanity check (plus log) infoLog("Here is a list of favored inputs:"); diff --git a/fuzz/src/main/java/edu/berkeley/cs/jqf/fuzz/util/IOUtils.java b/fuzz/src/main/java/edu/berkeley/cs/jqf/fuzz/util/IOUtils.java index 59339deee..9f9987c6a 100644 --- a/fuzz/src/main/java/edu/berkeley/cs/jqf/fuzz/util/IOUtils.java +++ b/fuzz/src/main/java/edu/berkeley/cs/jqf/fuzz/util/IOUtils.java @@ -31,8 +31,13 @@ import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.Arrays; import java.util.Comparator; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; /** * Utility class containing static methods for common I/O operations @@ -53,13 +58,17 @@ public class IOUtils { * `null` if `file` is also `null` * @throws FileNotFoundException if {@code file} does not exist */ - public static File[] resolveInputFileOrDirectory(File file) throws FileNotFoundException { + public static File[] resolveInputFileOrDirectory(File file) throws IOException { if (file == null) { return null; } else if (file.isDirectory()) { - File[] files = file.listFiles(); - Arrays.sort(files, Comparator.comparing(File::getName)); - return files; + try (final Stream pathStream = Files.walk(file.toPath())) { + return pathStream + .map(Path::toFile) + .filter(File::isFile) + .sorted(Comparator.comparing(File::getName)) + .toArray(File[]::new); + } } else if (file.isFile()) { return new File[]{file}; } else { diff --git a/fuzz/src/test/java/edu/berkeley/cs/jqf/fuzz/ei/ZestGuidanceTest.java b/fuzz/src/test/java/edu/berkeley/cs/jqf/fuzz/ei/ZestGuidanceTest.java new file mode 100644 index 000000000..87b437f6b --- /dev/null +++ b/fuzz/src/test/java/edu/berkeley/cs/jqf/fuzz/ei/ZestGuidanceTest.java @@ -0,0 +1,54 @@ +package edu.berkeley.cs.jqf.fuzz.ei; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.time.Duration; +import java.util.List; +import java.util.Random; +import java.util.regex.Pattern; + +import org.junit.Assert; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import org.junit.rules.TestName; + +import edu.berkeley.cs.jqf.fuzz.guidance.Result; +import edu.berkeley.cs.jqf.instrument.tracing.events.BranchEvent; +import edu.berkeley.cs.jqf.instrument.tracing.events.TraceEvent; +import edu.berkeley.cs.jqf.instrument.tracing.events.TraceEventVisitor; + +public class ZestGuidanceTest { + @Rule + public TestName testName = new TestName(); + @Rule + public TemporaryFolder temp = new TemporaryFolder(); + + @Test + public void shouldPrintEndCycleStatistics() throws IOException { + // public ZestGuidance(String testName, Duration duration, Long trials, File outputDirectory, Random + // sourceOfRandomness) throws IOException { + final File output = temp.newFolder("output"); + ZestGuidance zg = new ZestGuidance( + testName.getMethodName(), Duration.ofMillis(1), 1L, output, new Random() + ); + for (int i = 0; i < 1000 + 1; i++) { + try (final InputStream input = zg.getInput()) { + input.read(); + zg.generateCallBack(null).accept(new BranchEvent(1, null, 0, -1)); + zg.handleResult(Result.SUCCESS, null); + } + } + zg.getInput(); + + final Path logFile = output.toPath().resolve("fuzz.log"); + Assert.assertTrue(logFile.toFile().exists()); + + final List logLines = Files.readAllLines(logFile); + Pattern p = Pattern.compile("# Cycle \\d completed at (\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}\\.\\d+)"); + Assert.assertTrue(logLines.stream().anyMatch(s -> p.matcher(s).matches())); + } +} diff --git a/fuzz/src/test/java/edu/berkeley/cs/jqf/fuzz/util/IOUtilsTest.java b/fuzz/src/test/java/edu/berkeley/cs/jqf/fuzz/util/IOUtilsTest.java new file mode 100644 index 000000000..385a27394 --- /dev/null +++ b/fuzz/src/test/java/edu/berkeley/cs/jqf/fuzz/util/IOUtilsTest.java @@ -0,0 +1,57 @@ +package edu.berkeley.cs.jqf.fuzz.util; + +import java.io.File; +import java.io.IOException; + +import org.junit.Assert; +import org.junit.Assume; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; + +public class IOUtilsTest { + @Rule + public TemporaryFolder temp = new TemporaryFolder(); + + @Test + public void shouldResolveSingleFile() throws IOException { + final File file = temp.newFile(); + final File[] resolved = IOUtils.resolveInputFileOrDirectory(file); + Assert.assertEquals(1, resolved.length); + Assert.assertEquals(file, resolved[0]); + } + + @Test + public void shouldResolveFilesInDirectory() throws IOException { + final File dir = temp.newFolder(); + final File a = dir.toPath().resolve("a").toFile(); + Assume.assumeTrue(a.createNewFile()); + final File b = dir.toPath().resolve("b").toFile(); + Assume.assumeTrue(b.createNewFile()); + final File[] resolved = IOUtils.resolveInputFileOrDirectory(dir); + Assert.assertEquals(2, resolved.length); + Assert.assertEquals(a, resolved[0]); + Assert.assertEquals(b, resolved[1]); + } + + @Test + public void shouldResolveFilesInSubdirectories() throws IOException { + final File dir = temp.newFolder(); + final File a = dir.toPath().resolve("a").toFile(); + Assume.assumeTrue(a.createNewFile()); + final File b = dir.toPath().resolve("b").toFile(); + Assume.assumeTrue(b.createNewFile()); + final File sub = dir.toPath().resolve("sub").toFile(); + Assume.assumeTrue(sub.mkdir()); + final File subA = dir.toPath().resolve("a").toFile(); + Assume.assumeTrue(subA.createNewFile()); + final File subB = dir.toPath().resolve("b").toFile(); + Assume.assumeTrue(subB.createNewFile()); + final File[] resolved = IOUtils.resolveInputFileOrDirectory(dir); + Assert.assertEquals(4, resolved.length); + Assert.assertEquals(a, resolved[0]); + Assert.assertEquals(b, resolved[1]); + Assert.assertEquals(subA, resolved[2]); + Assert.assertEquals(subB, resolved[3]); + } +}