Improve diff algorithm

This commit is contained in:
2025-03-28 03:49:00 +03:00
parent 30faa32792
commit bb5b00da11
4 changed files with 59 additions and 58 deletions

View File

@@ -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();

View File

@@ -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();
}
}

View File

@@ -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<String> diff) {
public static Diff diff(Reader readerA, Reader readerB) throws IOException {
List<String> 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<String> a, List<String> 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<String>() {
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<String> 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<String> readAllLines(BufferedReader reader) throws IOException {
var lines = new ArrayList<String>();
String line;
while ((line = reader.readLine()) != null) lines.add(line);
return lines;
}
/**
* Pads string to center.
*

View File

@@ -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