From bb5b00da11cbc024a6edfea3d7f6443b41d3ec64 Mon Sep 17 00:00:00 2001 From: Naofal Date: Fri, 28 Mar 2025 03:49:00 +0300 Subject: [PATCH] Improve diff algorithm --- nobuild/NoBuild.java | 6 +- src/test/java/TestJtags.java | 16 ++-- src/test/java/notest/Test.java | 93 ++++++++++--------- .../snapshots/BasicExample.basicExample.1 | 2 +- 4 files changed, 59 insertions(+), 58 deletions(-) diff --git a/nobuild/NoBuild.java b/nobuild/NoBuild.java index 6f43103..f335aa7 100644 --- a/nobuild/NoBuild.java +++ b/nobuild/NoBuild.java @@ -364,11 +364,7 @@ public class NoBuild { * @throws Exception */ public static int commandThrows(String... command) throws IOException { - ProcessBuilder pb = - new ProcessBuilder(command) - .redirectInput(Redirect.INHERIT) - .redirectOutput(Redirect.INHERIT) - .redirectError(Redirect.INHERIT); + ProcessBuilder pb = new ProcessBuilder(command).inheritIO(); Process process; try { process = pb.start(); diff --git a/src/test/java/TestJtags.java b/src/test/java/TestJtags.java index ce11cd1..837d121 100644 --- a/src/test/java/TestJtags.java +++ b/src/test/java/TestJtags.java @@ -1,8 +1,8 @@ import static notest.Test.Util.*; +import java.io.BufferedReader; import java.io.FileReader; import java.io.IOException; -import java.nio.file.Files; import java.nio.file.Path; import notest.Test; @@ -15,21 +15,21 @@ class TestJtags { @Test static void basicExample() throws IOException { - Path file = Files.createTempFile("jtags", null); - var tags = + var process = runJava( new String[] {"-Dlogger.level=CONFIG"}, Jtags, "-o", "-", - "src/test/java/examples/BasicExample.java") - .inputReader(); + "src/test/java/examples/BasicExample.java"); + + var tags = readAllLines(process.inputReader()); Diff.diff( tags, - new FileReader( + readAllLines(new BufferedReader(new FileReader( Path.of("src", "test", "java", "snapshots", "BasicExample.basicExample.1") - .toFile())) - .assertEquals(); + .toFile())))) + .assertEqual(); } } diff --git a/src/test/java/notest/Test.java b/src/test/java/notest/Test.java index 57a7d40..a6ee9a0 100644 --- a/src/test/java/notest/Test.java +++ b/src/test/java/notest/Test.java @@ -28,7 +28,6 @@ package notest; import java.io.BufferedReader; import java.io.File; import java.io.IOException; -import java.io.Reader; import java.lang.ProcessBuilder.Redirect; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; @@ -42,7 +41,6 @@ import java.nio.file.PathMatcher; import java.nio.file.Paths; import java.util.ArrayList; import java.util.Arrays; -import java.util.Comparator; import java.util.IntSummaryStatistics; import java.util.List; import java.util.Optional; @@ -84,13 +82,13 @@ public @interface Test { System.err.println(ex.toString()); System.err.println(); return 0; - } catch (InvocationTargetException ex) { - System.err.println( - boxMiddle(ex.getTargetException().getClass().getSimpleName(), 50)); - System.err.println( - boxLeft( - Optional.ofNullable(ex.getTargetException().getMessage()) - .orElse(""))); + } catch (InvocationTargetException invocationException) { + var ex = invocationException.getTargetException(); + System.err.println(boxMiddle(ex.getClass().getSimpleName(), 50)); + System.err.println(boxLeft(Optional.ofNullable(ex.getMessage()).orElse(""))); + if (!(ex instanceof AssertionError)) { + ex.printStackTrace(); + } System.err.println(boxBottom(it.getName() + ": FAIL", 50)); System.err.println(); return 0; @@ -170,51 +168,50 @@ public @interface Test { } } + /** Representation of changes between two string sequences */ public static record Diff(List diff) { - public static Diff diff(Reader readerA, Reader readerB) throws IOException { - List diff = new ArrayList<>(); - - BufferedReader - bufReaderA = readerA instanceof BufferedReader ra ? ra : new BufferedReader(readerA), - bufReaderB = readerB instanceof BufferedReader rb ? rb : new BufferedReader(readerB); - - String a = bufReaderA.readLine(), b = bufReaderB.readLine(); - boolean different = false; - while (a != null || b != null) { - if (a == null || b == null || (a != null && !a.equals(b))) { - different = true; - if (a != null) { - diff.add("-" + a); + /** Creates a {@code Diff} representing changes from {@code a} to {@code b} */ + public static Diff diff(List a, List b) { + int diffSize = 0; + // longest common subsequence algorithm + int[][] dp = new int[a.size() + 1][b.size() + 1]; + for (int i = 1; i <= a.size(); i++) { + for (int j = 1; j <= b.size(); j++) { + if (a.get(i - 1).equals(b.get(j - 1))) { + dp[i][j] = dp[i - 1][j - 1] + 1; + } else { + dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]); } - if (b != null) { - diff.add("+" + b); - } - } else { - diff.add(" " + a); } - - a = bufReaderA.readLine(); - b = bufReaderB.readLine(); } - if (different) { - diff.sort( - new Comparator() { - public int compare(String s1, String s2) { - char a = s1.charAt(0), b = s2.charAt(0); - if (a == ' ' || b == ' ') return 0; - return b - a; - } - }); - } else { - diff = List.of(); + int lcs = dp[a.size()][b.size()]; + if (lcs == a.size()) return new Diff(List.of()); + + List diff = new ArrayList<>(a.size()); + int i = a.size(), j = b.size(); + while (true) { + if (i > 0 && j > 0 && a.get(i - 1).equals(b.get(j - 1))) { + diff.add(" " + a.get(i - 1)); + i--; + j--; + } else if (j > 0 && (i == 0 || dp[i][j - 1] >= dp[i - 1][j])) { + if (j > 0) diff.add("+" + b.get(j - 1)); + j--; + } else if (i > 0 && (j == 0 || dp[i][j - 1] < dp[i - 1][j])) { + if (i > 0) diff.add("-" + a.get(i - 1)); + i--; + } else { + break; + } } - return new Diff(diff); + return new Diff(diff.reversed()); } - public void assertEquals() { + /** Asserts that the two sequences are the same */ + public void assertEqual() { if (diff.isEmpty()) return; throw new AssertionError( @@ -246,6 +243,14 @@ public @interface Test { } } + /** Reads all lines from {@code reader} */ + public static List readAllLines(BufferedReader reader) throws IOException { + var lines = new ArrayList(); + String line; + while ((line = reader.readLine()) != null) lines.add(line); + return lines; + } + /** * Pads string to center. * diff --git a/src/test/java/snapshots/BasicExample.basicExample.1 b/src/test/java/snapshots/BasicExample.basicExample.1 index 44fcb6e..81781cf 100644 --- a/src/test/java/snapshots/BasicExample.basicExample.1 +++ b/src/test/java/snapshots/BasicExample.basicExample.1 @@ -1,3 +1,4 @@ +ABC !_TAG_FILE_ENCODING utf-8 !_TAG_FILE_SORTED 2 /0=unsorted, 1=sorted, 2=foldcase/ BasicExample src/test/java/examples/BasicExample.java /^public class BasicExample {$/;" Cls package:examples @@ -5,5 +6,4 @@ InnerClass src/test/java/examples/BasicExample.java /^ class InnerClass {$/;" method1 src/test/java/examples/BasicExample.java /^ private boolean method1() {$/;" mthd class:BasicExample ABC lorem ipsum dolor method2 src/test/java/examples/BasicExample.java /^ public static void method2($/;" mthd file: class:BasicExample -method3 src/test/java/examples/BasicExample.java /^ void method3() {}$/;" mthd class:InnerClass method4 src/test/java/examples/BasicExample.java /^ private void method4() {}$/;" mthd class:InnerClass