package de.neitzel.fxdesigner.kit;
import static org.junit.jupiter.api.Assertions.*;
import de.neitzel.fxdesigner.contracts.*;
import de.neitzel.fxdesigner.host.HostServices.*;
import de.neitzel.fxdesigner.protocol.BindingMetadata.Problem;
import java.net.URI;
import java.util.*;
import org.junit.jupiter.api.Test;
class DiagnosticActionsTest {
private static final String XML =
"\r\n"
+ "";
private DocumentSnapshot snapshot(String text, boolean writable) {
return new DocumentSnapshot(URI.create("file:/View.fxml"), 7, text, writable, true);
}
private Diagnostic direction(DocumentSnapshot d) {
return FxmlDiagnostics.local(d).stream()
.filter(p -> p.message().startsWith("Invalid or missing direction:"))
.findFirst()
.orElseThrow();
}
private String apply(DocumentSnapshot d, QuickFix fix) {
return new EditBatch(d.revision(), fix.label(), fix.edits()).applyTo(d);
}
@Test
void directionChoicesAreExplicitAndChangeOnlyTheAttributeValue() {
var d = snapshot(XML, true);
var problem = direction(d);
assertEquals(
"bad",
XML.substring(problem.range().orElseThrow().start(), problem.range().orElseThrow().end()));
assertEquals(3, problem.fixes().size());
for (var fix : problem.fixes()) {
String value = fix.label().substring(fix.label().lastIndexOf(' ') + 1);
assertEquals(XML.replace("direction = 'bad'", "direction = '" + value + "'"), apply(d, fix));
}
}
@Test
void missingAndEmptyDirectionKeepSourceAndHighlightAnActualRange() {
for (String text :
List.of(XML.replace(" direction = 'bad'", ""), XML.replace("'bad' extra", "'' extra"))) {
var d = snapshot(text, true);
var p = direction(d);
assertTrue(p.range().orElseThrow().end() > p.range().orElseThrow().start());
String fixed = apply(d, p.fixes().getFirst());
assertTrue(fixed.contains(""));
assertTrue(fixed.contains("extra='keep'"));
assertFalse(
FxmlDiagnostics.local(snapshot(fixed, true)).stream()
.anyMatch(x -> x.message().startsWith("Invalid or missing direction:")));
}
}
@Test
void duplicateRemovalPreservesFirstDeclarationAndSurroundingComments() {
String declaration = "";
String text =
XML.replace("'bad' extra", "'view' extra")
.replace("", "" + declaration + "");
var d = snapshot(text, true);
var p =
FxmlDiagnostics.local(d).stream()
.filter(x -> x.message().startsWith("Duplicate binding target:"))
.findFirst()
.orElseThrow();
assertEquals(text.replace(declaration, ""), apply(d, p.fixes().getFirst()));
}
@Test
void targetFixesRequireKnownSimpleDeclarationsAndNeverRemoveControls() {
var p = new Problem("name", "text", "ERROR", "unknown path");
var d = snapshot(XML, true);
assertEquals(1, FxmlDiagnostics.target(d, List.of(p)).getFirst().fixes().size());
for (String text :
List.of(
XML.replace("extra='keep'", "fx:id='binding'"),
XML.replace(
"extra='keep'/>", "extra='keep'>app.C"),
XML.replace("DirectBinding", "app.UnknownBinding")))
assertTrue(
FxmlDiagnostics.target(snapshot(text, true), List.of(p)).getFirst().fixes().isEmpty());
assertTrue(
FxmlDiagnostics.target(d, List.of(new Problem("name", "text", "WARNING", "unverified")))
.getFirst()
.fixes()
.isEmpty());
}
@Test
void readOnlyAndStaleSourceCannotApplyFixes() {
assertTrue(direction(snapshot(XML, false)).fixes().isEmpty());
var d = snapshot(XML, true);
var fix = direction(d).fixes().getFirst();
var edit = new EditBatch(d.revision(), fix.label(), fix.edits());
assertThrows(
ConcurrentModificationException.class,
() -> edit.applyTo(new DocumentSnapshot(d.document(), 8, XML, true, true)));
assertThrows(IllegalStateException.class, () -> edit.applyTo(snapshot(XML, false)));
}
}