updated
This commit is contained in:
@@ -47,6 +47,12 @@ class RegressionTest {
|
||||
Path.of("../state_machines/inheritance_state_machine"),
|
||||
Path.of("src/test/resources/golden/OneStateMachineConfiguration"),
|
||||
"OneStateMachineConfiguration"
|
||||
),
|
||||
new TestScenario(
|
||||
"Two State Machine (Extra Functions)",
|
||||
Path.of("../state_machines/inheritance_extra_functions_state_machine"),
|
||||
Path.of("src/test/resources/golden/TwoStateMachineExtraFunctionsConfiguration"),
|
||||
"TwoStateMachineConfiguration"
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -468,6 +468,25 @@ class AstTransitionParserTest {
|
||||
assertThat(transition.getIsLambdaActions()).containsExactly(true);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldParseTransitionsWithVariableDeclarations() {
|
||||
String source = """
|
||||
public class TestClass {
|
||||
public void configure(StateMachineTransitionConfigurer<String, String> transitions) throws Exception {
|
||||
ExternalTransitionConfigurer<String, String> t1 = transitions.withExternal().source("S1").target("S2").event("E1");
|
||||
t1.and().withExternal().source("S2").target("S3").event("E2");
|
||||
}
|
||||
}
|
||||
""";
|
||||
MethodDeclaration method = createMethodDeclaration(source);
|
||||
|
||||
List<Transition> transitions = AstTransitionParser.parseTransitions(method);
|
||||
|
||||
assertThat(transitions).hasSize(2);
|
||||
assertThat(transitions.get(0).getEvent()).isEqualTo("E1");
|
||||
assertThat(transitions.get(1).getEvent()).isEqualTo("E2");
|
||||
}
|
||||
|
||||
private MethodDeclaration createMethodDeclaration(String source) {
|
||||
ASTParser parser = ASTParser.newParser(AST.getJLSLatest());
|
||||
parser.setKind(ASTParser.K_COMPILATION_UNIT);
|
||||
@@ -483,4 +502,4 @@ class AstTransitionParserTest {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,282 @@
|
||||
package click.kamil.springstatemachineexporter.ast.app;
|
||||
|
||||
import click.kamil.springstatemachineexporter.ast.app.domain.Transition;
|
||||
import click.kamil.springstatemachineexporter.ast.common.CodebaseContext;
|
||||
import org.eclipse.jdt.core.dom.*;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.io.TempDir;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
class StateMachineAggregatorTest {
|
||||
|
||||
@TempDir
|
||||
Path tempDir;
|
||||
|
||||
private CodebaseContext context;
|
||||
private StateMachineAggregator aggregator;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
context = new CodebaseContext();
|
||||
aggregator = new StateMachineAggregator(context);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldAggregateTransitionsFromInheritance() throws IOException {
|
||||
String baseSource = """
|
||||
package com.example;
|
||||
public abstract class BaseConfig extends StateMachineConfigurerAdapter {
|
||||
public void configure(StateMachineTransitionConfigurer transitions) throws Exception {
|
||||
transitions.withExternal().source("BASE_S1").target("BASE_S2").event("BASE_E1");
|
||||
}
|
||||
}
|
||||
""";
|
||||
String childSource = """
|
||||
package com.example;
|
||||
public class ChildConfig extends BaseConfig {
|
||||
@Override
|
||||
public void configure(StateMachineTransitionConfigurer transitions) throws Exception {
|
||||
super.configure(transitions);
|
||||
transitions.withExternal().source("CHILD_S1").target("CHILD_S2").event("CHILD_E1");
|
||||
}
|
||||
}
|
||||
""";
|
||||
|
||||
writeClass("BaseConfig", baseSource);
|
||||
writeClass("ChildConfig", childSource);
|
||||
context.scan(tempDir);
|
||||
|
||||
TypeDeclaration childTd = context.getTypeDeclaration("ChildConfig");
|
||||
List<Transition> transitions = aggregator.aggregateTransitions(childTd);
|
||||
|
||||
assertThat(transitions).hasSize(2);
|
||||
assertThat(transitions).anyMatch(t -> t.getEvent().equals("BASE_E1"));
|
||||
assertThat(transitions).anyMatch(t -> t.getEvent().equals("CHILD_E1"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldFollowMethodCallsAcrossHierarchy() throws IOException {
|
||||
String baseSource = """
|
||||
package com.example;
|
||||
public abstract class BaseConfig extends StateMachineConfigurerAdapter {
|
||||
public void configure(StateMachineTransitionConfigurer transitions) throws Exception {
|
||||
addBaseTransitions(transitions);
|
||||
}
|
||||
protected void addBaseTransitions(StateMachineTransitionConfigurer transitions) {
|
||||
transitions.withExternal().source("BASE_S1").target("BASE_S2").event("BASE_E1");
|
||||
}
|
||||
}
|
||||
""";
|
||||
String childSource = """
|
||||
package com.example;
|
||||
public class ChildConfig extends BaseConfig {
|
||||
@Override
|
||||
protected void addBaseTransitions(StateMachineTransitionConfigurer transitions) {
|
||||
transitions.withExternal().source("OVERRIDDEN_S1").target("OVERRIDDEN_S2").event("OVERRIDDEN_E1");
|
||||
}
|
||||
}
|
||||
""";
|
||||
|
||||
writeClass("BaseConfig", baseSource);
|
||||
writeClass("ChildConfig", childSource);
|
||||
context.scan(tempDir);
|
||||
|
||||
TypeDeclaration childTd = context.getTypeDeclaration("ChildConfig");
|
||||
List<Transition> transitions = aggregator.aggregateTransitions(childTd);
|
||||
|
||||
assertThat(transitions).hasSize(1);
|
||||
assertThat(transitions.get(0).getEvent()).isEqualTo("OVERRIDDEN_E1");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldResolveParametersInMethodCalls() throws IOException {
|
||||
String source = """
|
||||
package com.example;
|
||||
public class ParamConfig extends StateMachineConfigurerAdapter {
|
||||
public void configure(StateMachineTransitionConfigurer transitions) throws Exception {
|
||||
addCustomTransition(transitions, "START", "END", "GO");
|
||||
}
|
||||
private void addCustomTransition(StateMachineTransitionConfigurer t, String src, String tgt, String evt) {
|
||||
t.withExternal().source(src).target(tgt).event(evt);
|
||||
}
|
||||
}
|
||||
""";
|
||||
|
||||
writeClass("ParamConfig", source);
|
||||
context.scan(tempDir);
|
||||
|
||||
TypeDeclaration td = context.getTypeDeclaration("ParamConfig");
|
||||
List<Transition> transitions = aggregator.aggregateTransitions(td);
|
||||
|
||||
assertThat(transitions).hasSize(1);
|
||||
assertThat(transitions.get(0).getSourceStates()).containsExactly("START");
|
||||
assertThat(transitions.get(0).getTargetStates()).containsExactly("END");
|
||||
assertThat(transitions.get(0).getEvent()).isEqualTo("GO");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldParseStreamOfForEachTransitions() throws IOException {
|
||||
String source = """
|
||||
package com.example;
|
||||
import java.util.stream.Stream;
|
||||
public class StreamConfig extends StateMachineConfigurerAdapter {
|
||||
public void configure(StateMachineTransitionConfigurer transitions) throws Exception {
|
||||
Stream.of("E1", "E2").forEach(e -> {
|
||||
transitions.withExternal().source("S1").target("S2").event(e);
|
||||
});
|
||||
}
|
||||
}
|
||||
""";
|
||||
|
||||
writeClass("StreamConfig", source);
|
||||
context.scan(tempDir);
|
||||
|
||||
TypeDeclaration td = context.getTypeDeclaration("StreamConfig");
|
||||
List<Transition> transitions = aggregator.aggregateTransitions(td);
|
||||
|
||||
assertThat(transitions).hasSize(2);
|
||||
assertThat(transitions).anyMatch(t -> t.getEvent().equals("E1"));
|
||||
assertThat(transitions).anyMatch(t -> t.getEvent().equals("E2"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldAggregateStatesAcrossInheritance() throws IOException {
|
||||
String baseSource = """
|
||||
package com.example;
|
||||
public abstract class BaseConfig extends StateMachineConfigurerAdapter {
|
||||
public void configure(StateMachineStateConfigurer states) throws Exception {
|
||||
states.withStates().initial("BASE_START");
|
||||
}
|
||||
}
|
||||
""";
|
||||
String childSource = """
|
||||
package com.example;
|
||||
public class ChildConfig extends BaseConfig {
|
||||
public void configure(StateMachineStateConfigurer states) throws Exception {
|
||||
super.configure(states);
|
||||
states.withStates().state("CHILD_STATE").end("CHILD_END");
|
||||
}
|
||||
}
|
||||
""";
|
||||
|
||||
writeClass("BaseConfig", baseSource);
|
||||
writeClass("ChildConfig", childSource);
|
||||
context.scan(tempDir);
|
||||
|
||||
TypeDeclaration childTd = context.getTypeDeclaration("ChildConfig");
|
||||
Set<String> initialStates = new HashSet<>();
|
||||
Set<String> endStates = new HashSet<>();
|
||||
aggregator.aggregateStates(childTd, initialStates, endStates);
|
||||
|
||||
assertThat(initialStates).containsExactly("BASE_START");
|
||||
assertThat(endStates).containsExactly("CHILD_END");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldParseStreamOfForEachStates() throws IOException {
|
||||
String source = """
|
||||
package com.example;
|
||||
import java.util.stream.Stream;
|
||||
public class StreamStateConfig extends StateMachineConfigurerAdapter {
|
||||
public void configure(StateMachineStateConfigurer states) throws Exception {
|
||||
Stream.of("S1", "S2").forEach(s -> {
|
||||
states.withStates().initial(s);
|
||||
});
|
||||
}
|
||||
}
|
||||
""";
|
||||
|
||||
writeClass("StreamStateConfig", source);
|
||||
context.scan(tempDir);
|
||||
|
||||
TypeDeclaration td = context.getTypeDeclaration("StreamStateConfig");
|
||||
Set<String> initialStates = new HashSet<>();
|
||||
Set<String> endStates = new HashSet<>();
|
||||
aggregator.aggregateStates(td, initialStates, endStates);
|
||||
|
||||
assertThat(initialStates).containsExactlyInAnyOrder("S1", "S2");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldAggregateTransitionsFromDeepInheritance() throws IOException {
|
||||
String grandparentSource = """
|
||||
package com.example;
|
||||
public abstract class GrandparentConfig extends StateMachineConfigurerAdapter {
|
||||
public void configure(StateMachineTransitionConfigurer transitions) throws Exception {
|
||||
transitions.withExternal().source("GP_S1").target("GP_S2").event("GP_E1");
|
||||
}
|
||||
}
|
||||
""";
|
||||
String parentSource = """
|
||||
package com.example;
|
||||
public abstract class ParentConfig extends GrandparentConfig {
|
||||
@Override
|
||||
public void configure(StateMachineTransitionConfigurer transitions) throws Exception {
|
||||
super.configure(transitions);
|
||||
transitions.withExternal().source("P_S1").target("P_S2").event("P_E1");
|
||||
}
|
||||
}
|
||||
""";
|
||||
String childSource = """
|
||||
package com.example;
|
||||
public class ChildConfig extends ParentConfig {
|
||||
@Override
|
||||
public void configure(StateMachineTransitionConfigurer transitions) throws Exception {
|
||||
super.configure(transitions);
|
||||
transitions.withExternal().source("C_S1").target("C_S2").event("C_E1");
|
||||
}
|
||||
}
|
||||
""";
|
||||
|
||||
writeClass("GrandparentConfig", grandparentSource);
|
||||
writeClass("ParentConfig", parentSource);
|
||||
writeClass("ChildConfig", childSource);
|
||||
context.scan(tempDir);
|
||||
|
||||
TypeDeclaration childTd = context.getTypeDeclaration("ChildConfig");
|
||||
List<Transition> transitions = aggregator.aggregateTransitions(childTd);
|
||||
|
||||
assertThat(transitions).hasSize(3);
|
||||
assertThat(transitions).anyMatch(t -> t.getEvent().equals("GP_E1"));
|
||||
assertThat(transitions).anyMatch(t -> t.getEvent().equals("P_E1"));
|
||||
assertThat(transitions).anyMatch(t -> t.getEvent().equals("C_E1"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldParseEnhancedForLoopTransitions() throws IOException {
|
||||
String source = """
|
||||
package com.example;
|
||||
import java.util.List;
|
||||
public class LoopConfig extends StateMachineConfigurerAdapter {
|
||||
public void configure(StateMachineTransitionConfigurer transitions) throws Exception {
|
||||
for (String e : List.of("E1", "E2")) {
|
||||
transitions.withExternal().source("S1").target("S2").event(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
""";
|
||||
|
||||
writeClass("LoopConfig", source);
|
||||
context.scan(tempDir);
|
||||
|
||||
TypeDeclaration td = context.getTypeDeclaration("LoopConfig");
|
||||
List<Transition> transitions = aggregator.aggregateTransitions(td);
|
||||
|
||||
assertThat(transitions).hasSize(2);
|
||||
assertThat(transitions).anyMatch(t -> t.getEvent().equals("E1"));
|
||||
assertThat(transitions).anyMatch(t -> t.getEvent().equals("E2"));
|
||||
}
|
||||
|
||||
private void writeClass(String name, String source) throws IOException {
|
||||
Files.writeString(tempDir.resolve(name + ".java"), source);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user