more tests
This commit is contained in:
@@ -0,0 +1,78 @@
|
|||||||
|
package click.kamil.springstatemachineexporter.analysis.service;
|
||||||
|
|
||||||
|
import click.kamil.springstatemachineexporter.analysis.model.CallChain;
|
||||||
|
import click.kamil.springstatemachineexporter.analysis.model.EntryPoint;
|
||||||
|
import click.kamil.springstatemachineexporter.analysis.model.TriggerPoint;
|
||||||
|
import click.kamil.springstatemachineexporter.ast.common.CodebaseContext;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
public class AnonymousClassBugTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldResolveEventThroughAnonymousClass() throws IOException {
|
||||||
|
String source = """
|
||||||
|
package com.example;
|
||||||
|
|
||||||
|
public class MyController {
|
||||||
|
private StateMachine machine;
|
||||||
|
|
||||||
|
public void process() {
|
||||||
|
// Supplying the event via an anonymous class
|
||||||
|
fireEvent(new EventProvider() {
|
||||||
|
@Override
|
||||||
|
public TransitionEnum getEvent() {
|
||||||
|
return TransitionEnum.STATE_A;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void fireEvent(EventProvider provider) {
|
||||||
|
machine.fire(provider.getEvent());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface EventProvider {
|
||||||
|
TransitionEnum getEvent();
|
||||||
|
}
|
||||||
|
|
||||||
|
class StateMachine {
|
||||||
|
public void fire(TransitionEnum event) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum TransitionEnum { STATE_A, STATE_B }
|
||||||
|
""";
|
||||||
|
|
||||||
|
Path tempDir = Files.createTempDirectory("callgraph_test_anonymous");
|
||||||
|
Files.writeString(tempDir.resolve("Config.java"), source);
|
||||||
|
|
||||||
|
CodebaseContext context = new CodebaseContext();
|
||||||
|
context.scan(tempDir);
|
||||||
|
|
||||||
|
HeuristicCallGraphEngine engine = new HeuristicCallGraphEngine(context);
|
||||||
|
|
||||||
|
EntryPoint entryPoint = EntryPoint.builder()
|
||||||
|
.className("com.example.MyController")
|
||||||
|
.methodName("process")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
TriggerPoint trigger = TriggerPoint.builder()
|
||||||
|
.className("com.example.StateMachine")
|
||||||
|
.methodName("fire")
|
||||||
|
.event("event")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
List<CallChain> chains = engine.findChains(List.of(entryPoint), List.of(trigger));
|
||||||
|
assertThat(chains).hasSize(1);
|
||||||
|
System.out.println("Poly events from anonymous class: " + chains.get(0).getTriggerPoint().getPolymorphicEvents());
|
||||||
|
|
||||||
|
// It SHOULD resolve to STATE_A by tracking the anonymous class implementation
|
||||||
|
assertThat(chains.get(0).getTriggerPoint().getPolymorphicEvents()).containsExactly("TransitionEnum.STATE_A");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
package click.kamil.springstatemachineexporter.analysis.service;
|
||||||
|
|
||||||
|
import click.kamil.springstatemachineexporter.analysis.model.CallChain;
|
||||||
|
import click.kamil.springstatemachineexporter.analysis.model.EntryPoint;
|
||||||
|
import click.kamil.springstatemachineexporter.analysis.model.TriggerPoint;
|
||||||
|
import click.kamil.springstatemachineexporter.ast.common.CodebaseContext;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
public class InheritedMethodBugTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldResolveEventFromInheritedProtectedMethodWithoutThis() throws IOException {
|
||||||
|
String source = """
|
||||||
|
package com.example;
|
||||||
|
|
||||||
|
public class ChildController extends ParentController {
|
||||||
|
private StateMachine machine;
|
||||||
|
|
||||||
|
public void process() {
|
||||||
|
// Calling inherited protected method WITHOUT 'this.' or 'super.'
|
||||||
|
machine.fire(getInheritedEvent());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class ParentController {
|
||||||
|
protected TransitionEnum getInheritedEvent() {
|
||||||
|
return TransitionEnum.STATE_P; // Protected method returns STATE_P
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class StateMachine {
|
||||||
|
public void fire(TransitionEnum event) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum TransitionEnum { STATE_P, STATE_Q }
|
||||||
|
""";
|
||||||
|
|
||||||
|
Path tempDir = Files.createTempDirectory("callgraph_test_inherited");
|
||||||
|
Files.writeString(tempDir.resolve("Config.java"), source);
|
||||||
|
|
||||||
|
CodebaseContext context = new CodebaseContext();
|
||||||
|
context.scan(tempDir);
|
||||||
|
|
||||||
|
HeuristicCallGraphEngine engine = new HeuristicCallGraphEngine(context);
|
||||||
|
|
||||||
|
EntryPoint entryPoint = EntryPoint.builder()
|
||||||
|
.className("com.example.ChildController")
|
||||||
|
.methodName("process")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
TriggerPoint trigger = TriggerPoint.builder()
|
||||||
|
.className("com.example.StateMachine")
|
||||||
|
.methodName("fire")
|
||||||
|
.event("event")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
List<CallChain> chains = engine.findChains(List.of(entryPoint), List.of(trigger));
|
||||||
|
assertThat(chains).hasSize(1);
|
||||||
|
System.out.println("Poly events from inherited method: " + chains.get(0).getTriggerPoint().getPolymorphicEvents());
|
||||||
|
|
||||||
|
// It SHOULD resolve to STATE_P by finding getInheritedEvent() in ParentController
|
||||||
|
assertThat(chains.get(0).getTriggerPoint().getPolymorphicEvents()).containsExactly("TransitionEnum.STATE_P");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
package click.kamil.springstatemachineexporter.analysis.service;
|
||||||
|
|
||||||
|
import click.kamil.springstatemachineexporter.analysis.model.CallChain;
|
||||||
|
import click.kamil.springstatemachineexporter.analysis.model.EntryPoint;
|
||||||
|
import click.kamil.springstatemachineexporter.analysis.model.TriggerPoint;
|
||||||
|
import click.kamil.springstatemachineexporter.ast.common.CodebaseContext;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
public class LambdaBugTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldResolveEventThroughLambda() throws IOException {
|
||||||
|
String source = """
|
||||||
|
package com.example;
|
||||||
|
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
public class MyController {
|
||||||
|
private StateMachine machine;
|
||||||
|
|
||||||
|
public void process() {
|
||||||
|
// Supplying the event via a lambda
|
||||||
|
fireEvent(() -> TransitionEnum.STATE_L);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void fireEvent(Supplier<TransitionEnum> eventSupplier) {
|
||||||
|
machine.fire(eventSupplier.get());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class StateMachine {
|
||||||
|
public void fire(TransitionEnum event) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum TransitionEnum { STATE_L, STATE_M }
|
||||||
|
""";
|
||||||
|
|
||||||
|
Path tempDir = Files.createTempDirectory("callgraph_test_lambda");
|
||||||
|
Files.writeString(tempDir.resolve("Config.java"), source);
|
||||||
|
|
||||||
|
CodebaseContext context = new CodebaseContext();
|
||||||
|
context.scan(tempDir);
|
||||||
|
|
||||||
|
HeuristicCallGraphEngine engine = new HeuristicCallGraphEngine(context);
|
||||||
|
|
||||||
|
EntryPoint entryPoint = EntryPoint.builder()
|
||||||
|
.className("com.example.MyController")
|
||||||
|
.methodName("process")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
TriggerPoint trigger = TriggerPoint.builder()
|
||||||
|
.className("com.example.StateMachine")
|
||||||
|
.methodName("fire")
|
||||||
|
.event("event")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
List<CallChain> chains = engine.findChains(List.of(entryPoint), List.of(trigger));
|
||||||
|
assertThat(chains).hasSize(1);
|
||||||
|
System.out.println("Poly events from lambda: " + chains.get(0).getTriggerPoint().getPolymorphicEvents());
|
||||||
|
|
||||||
|
// It SHOULD resolve to STATE_L by tracking the lambda supplier
|
||||||
|
assertThat(chains.get(0).getTriggerPoint().getPolymorphicEvents()).containsExactly("TransitionEnum.STATE_L");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,86 @@
|
|||||||
|
package click.kamil.springstatemachineexporter.analysis.service;
|
||||||
|
|
||||||
|
import click.kamil.springstatemachineexporter.analysis.model.CallChain;
|
||||||
|
import click.kamil.springstatemachineexporter.analysis.model.EntryPoint;
|
||||||
|
import click.kamil.springstatemachineexporter.analysis.model.TriggerPoint;
|
||||||
|
import click.kamil.springstatemachineexporter.ast.common.CodebaseContext;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
public class OverloadedConstructorBugTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldResolveEventThroughOverloadedConstructors() throws IOException {
|
||||||
|
String source = """
|
||||||
|
package com.example;
|
||||||
|
|
||||||
|
public class MyController {
|
||||||
|
private StateMachine machine;
|
||||||
|
|
||||||
|
public void process() {
|
||||||
|
// Creating command via 1-arg constructor
|
||||||
|
MyCommand command = new MyCommand(TransitionEnum.STATE_Z);
|
||||||
|
machine.fire(command.getEvent());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class MyCommand {
|
||||||
|
private final TransitionEnum event;
|
||||||
|
private final String metadata;
|
||||||
|
|
||||||
|
// 1-arg constructor calls 2-arg constructor using this(...)
|
||||||
|
public MyCommand(TransitionEnum event) {
|
||||||
|
this(event, "default_metadata");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2-arg constructor assigns the field
|
||||||
|
public MyCommand(TransitionEnum event, String metadata) {
|
||||||
|
this.event = event;
|
||||||
|
this.metadata = metadata;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TransitionEnum getEvent() {
|
||||||
|
return event;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class StateMachine {
|
||||||
|
public void fire(TransitionEnum event) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum TransitionEnum { STATE_X, STATE_Y, STATE_Z }
|
||||||
|
""";
|
||||||
|
|
||||||
|
Path tempDir = Files.createTempDirectory("callgraph_test_constructor");
|
||||||
|
Files.writeString(tempDir.resolve("Config.java"), source);
|
||||||
|
|
||||||
|
CodebaseContext context = new CodebaseContext();
|
||||||
|
context.scan(tempDir);
|
||||||
|
|
||||||
|
HeuristicCallGraphEngine engine = new HeuristicCallGraphEngine(context);
|
||||||
|
|
||||||
|
EntryPoint entryPoint = EntryPoint.builder()
|
||||||
|
.className("com.example.MyController")
|
||||||
|
.methodName("process")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
TriggerPoint trigger = TriggerPoint.builder()
|
||||||
|
.className("com.example.StateMachine")
|
||||||
|
.methodName("fire")
|
||||||
|
.event("event")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
List<CallChain> chains = engine.findChains(List.of(entryPoint), List.of(trigger));
|
||||||
|
assertThat(chains).hasSize(1);
|
||||||
|
System.out.println("Poly events from overloaded constructor: " + chains.get(0).getTriggerPoint().getPolymorphicEvents());
|
||||||
|
|
||||||
|
// It SHOULD resolve to STATE_Z by following the 'this(...)' call in the constructor
|
||||||
|
assertThat(chains.get(0).getTriggerPoint().getPolymorphicEvents()).containsExactly("TransitionEnum.STATE_Z");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,84 @@
|
|||||||
|
package click.kamil.springstatemachineexporter.analysis.service;
|
||||||
|
|
||||||
|
import click.kamil.springstatemachineexporter.analysis.model.CallChain;
|
||||||
|
import click.kamil.springstatemachineexporter.analysis.model.EntryPoint;
|
||||||
|
import click.kamil.springstatemachineexporter.analysis.model.TriggerPoint;
|
||||||
|
import click.kamil.springstatemachineexporter.ast.common.CodebaseContext;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
public class StaticFactoryBugTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldResolveEventThroughStaticFactory() throws IOException {
|
||||||
|
String source = """
|
||||||
|
package com.example;
|
||||||
|
|
||||||
|
public class MyController {
|
||||||
|
private StateMachine machine;
|
||||||
|
|
||||||
|
public void process() {
|
||||||
|
// Creating command via static factory method
|
||||||
|
MyCommand command = MyCommand.of(TransitionEnum.STATE_W);
|
||||||
|
machine.fire(command.getEvent());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class MyCommand {
|
||||||
|
private final TransitionEnum event;
|
||||||
|
|
||||||
|
// Private constructor
|
||||||
|
private MyCommand(TransitionEnum event) {
|
||||||
|
this.event = event;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Static factory method
|
||||||
|
public static MyCommand of(TransitionEnum event) {
|
||||||
|
return new MyCommand(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
public TransitionEnum getEvent() {
|
||||||
|
return event;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class StateMachine {
|
||||||
|
public void fire(TransitionEnum event) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum TransitionEnum { STATE_W, STATE_X, STATE_Y, STATE_Z }
|
||||||
|
""";
|
||||||
|
|
||||||
|
Path tempDir = Files.createTempDirectory("callgraph_test_static_factory");
|
||||||
|
Files.writeString(tempDir.resolve("Config.java"), source);
|
||||||
|
|
||||||
|
CodebaseContext context = new CodebaseContext();
|
||||||
|
context.scan(tempDir);
|
||||||
|
|
||||||
|
HeuristicCallGraphEngine engine = new HeuristicCallGraphEngine(context);
|
||||||
|
|
||||||
|
EntryPoint entryPoint = EntryPoint.builder()
|
||||||
|
.className("com.example.MyController")
|
||||||
|
.methodName("process")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
TriggerPoint trigger = TriggerPoint.builder()
|
||||||
|
.className("com.example.StateMachine")
|
||||||
|
.methodName("fire")
|
||||||
|
.event("event")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
List<CallChain> chains = engine.findChains(List.of(entryPoint), List.of(trigger));
|
||||||
|
assertThat(chains).hasSize(1);
|
||||||
|
System.out.println("Poly events from static factory: " + chains.get(0).getTriggerPoint().getPolymorphicEvents());
|
||||||
|
|
||||||
|
// It SHOULD resolve to STATE_W by following the static factory and its internal instantiation
|
||||||
|
assertThat(chains.get(0).getTriggerPoint().getPolymorphicEvents()).containsExactly("TransitionEnum.STATE_W");
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user