Loading plugins/org.etsi.mts.tdl.execution.java.codegen/src/org/etsi/mts/tdl/execution/java/codegen/RuntimeApi.java 0 → 100644 +110 −0 Original line number Diff line number Diff line package org.etsi.mts.tdl.execution.java.codegen; /** * Names of the runtime members referenced <b>by name</b> from generated test * code, i.e. the dependency of this generator on the runtime that the compiler * cannot check. Every member listed here carries the {@code GeneratedCodeApi} * annotation in the runtime; keep the two in sync when renaming. */ final class RuntimeApi { private RuntimeApi() { } // ---- TestControl (the "tc" field of generated tests) static final String SYSTEM_ADAPTER = "systemAdapter"; static final String VALIDATOR = "validator"; static final String REPORTER = "reporter"; static final String FUNCTIONS = "functions"; static final String GET_TESTER_COMPONENT = "getTesterComponent"; static final String VALUE = "value"; static final String TERMINATE = "terminate"; static final String STOP = "stop"; static final String TIMEOUT = "timeout"; static final String TIME_CONSTRAINT_DEADLINE = "timeConstraintDeadline"; static final String TIME = "time"; static final String SLEEP = "sleep"; static final String SHUTDOWN = "shutdown"; static final String RESUME_RECEIVING = "resumeReceiving"; static final String REMOVE_EXCEPTIONAL_BEHAVIOUR = "removeExceptionalBehaviour"; static final String NO_INPUT = "noInput"; static final String NEXT = "next"; static final String GET_GATE_REFERENCE = "getGateReference"; static final String GET_CONNECTION = "getConnection"; static final String ENABLE_EXCEPTIONAL_BEHAVIOURS = "enableExceptionalBehaviours"; static final String DISABLE_ACTIVE_EXCEPTIONAL_BEHAVIOURS = "disableActiveExceptionalBehaviours"; static final String CONFIGURE = "configure"; static final String CLEANUP_BATCH = "cleanupBatch"; static final String CHECK_TIME_CONSTRAINT = "checkTimeConstraint"; static final String CALL = "call"; static final String BREAK_BLOCK = "breakBlock"; static final String AWAIT_TIME_CONSTRAINT = "awaitTimeConstraint"; static final String ADD_EXCEPTIONAL_BEHAVIOUR = "addExceptionalBehaviour"; static final String EXECUTE_ALTERNATIVES_AND_EXCEPTIONALS = "executeAlternativesAndExceptionals"; static final String RECEIVE = "receive"; static final String TRIGGER = "trigger"; static final String IS_TRUE = "isTrue"; static final String INTEGER = "integer"; static final String MILLIS = "millis"; static final String GET_EXCEPTIONAL_BEHAVIOUR = "getExceptionalBehaviour"; // ---- Other runtime classes (owner#member) static final String EXECUTE = "execute"; // ExecutionCallable#execute static final String GET_TIMESTAMP = "getTimestamp"; // ExecutionResult#getTimestamp static final String DATA = "data"; // InteractionResult#data static final String ALTERNATIVES = "alternatives"; // BatchedFutures#alternatives static final String EXCEPTIONALS = "exceptionals"; // BatchedFutures#exceptionals static final String ADD_TEST = "addTest"; // ComponentExecutor#addTest static final String BEHAVIOUR = "behaviour"; // ExceptionalBehaviour#behaviour static final String IS_INTERRUPT = "isInterrupt"; // ExceptionalBehaviour#isInterrupt static final String SET_CALLABLE = "setCallable"; // ExceptionalBehaviour#setCallable static final String GET_VERDICT = "getVerdict"; // StopExceptionImpl#getVerdict static final String GET_BLOCK_NAME = "getBlockName"; // BreakException#getBlockName static final String SET_TEMPLATE = "setTemplate"; // ValueImpl#setTemplate static final String SET_IS_COLLECTION = "setIsCollection"; // ValueImpl#setIsCollection static final String SET_IS_STRUCTURE = "setIsStructure"; // ValueImpl#setIsStructure static final String SET_MAPPING = "setMapping"; // ValueImpl#setMapping static final String SET_VALUE = "setValue"; // ValueImpl#setValue static final String SET_PARAMETER = "setParameter"; // ValueImpl#setParameter static final String ADD_ITEM = "addItem"; // ValueImpl#addItem static final String SET_NAME = "setName"; // ValueImpl#setName static final String ADD_ANNOTATION = "addAnnotation"; // ValueImpl#addAnnotation static final String AS_DATA = "asData"; // ValueImpl#asData static final String SET_ITEM_TYPE = "setItemType"; // TypeImpl#setItemType static final String SET_PARENT = "setParent"; // MappingImpl#setParent static final String SET_IS_RESOURCE = "setIsResource"; // MappingImpl#setIsResource static final String SET_IS_PARAMETER = "setIsParameter"; // MappingImpl#setIsParameter static final String SET_RESOURCE = "setResource"; // MappingImpl#setResource static final String NONE = "NONE"; // TemplateImpl#NONE static final String MARK = "mark"; // TemplateImpl#mark static final String MEMBER = "member"; // TemplateImpl#member static final String ITEM = "item"; // TemplateImpl#item static final String SET_UNASSIGNED_MEMBER_TREATMENT = "setUnassignedMemberTreatment"; // TemplateImpl#setUnassignedMemberTreatment static final String EVALUATE = "evaluate"; // Constraint#evaluate static final String HOLDS = "holds"; // Evaluation#holds static final String OPERANDS = "operands"; // Evaluation#operands static final String OF = "of"; // BehaviourProperties#of static final String PROPERTY_SOURCE = "SOURCE"; // BehaviourProperties#SOURCE static final String PROPERTY_RESULT = "RESULT"; // BehaviourProperties#RESULT static final String PROPERTY_OPERATOR = "OPERATOR"; // BehaviourProperties#OPERATOR static final String PROPERTY_OPERANDS = "OPERANDS"; // BehaviourProperties#OPERANDS static final String PROPERTY_VALUE = "VALUE"; // BehaviourProperties#VALUE static final String PROPERTY_TIME_CONSTRAINT_RESULT = "TIME_CONSTRAINT_RESULT"; // BehaviourProperties#TIME_CONSTRAINT_RESULT static final String PROPERTY_TIME_CONSTRAINT_OPERATOR = "TIME_CONSTRAINT_OPERATOR"; // BehaviourProperties#TIME_CONSTRAINT_OPERATOR static final String PROPERTY_TIME_CONSTRAINT_OPERANDS = "TIME_CONSTRAINT_OPERANDS"; // BehaviourProperties#TIME_CONSTRAINT_OPERANDS static final String TIMESTAMP = "timestamp"; // TimeLabel#timestamp static final String LAST = "last"; // TimeLabel#last static final String FIRST = "first"; // TimeLabel#first static final String PREVIOUS = "previous"; // TimeLabel#previous static final String FIRST_OR = "firstOr"; // TimeLabel#firstOr static final String NOW_MILLIS = "nowMillis"; // TimeLabel#nowMillis static final String TIMER_START = "start"; // Timer#start static final String TIMER_STOP = "stop"; // Timer#stop static final String FAIL = "fail"; // VerdictImpl#fail static final String PASS = "pass"; // VerdictImpl#pass (verdict instances are referenced by their TDL names) static final String INCONCLUSIVE = "inconclusive"; // VerdictImpl#inconclusive static final String TO_MILLIS = "toMillis"; // TimeUnit#toMillis } plugins/org.etsi.mts.tdl.execution.java.runtime/pom.xml +12 −1 Original line number Diff line number Diff line Loading @@ -7,6 +7,7 @@ <version>1.0.0</version> <build> <sourceDirectory>src</sourceDirectory> <testSourceDirectory>test</testSourceDirectory> <plugins> <plugin> <artifactId>maven-compiler-plugin</artifactId> Loading @@ -15,6 +16,10 @@ <release>11</release> </configuration> </plugin> <plugin> <artifactId>maven-surefire-plugin</artifactId> <version>3.2.5</version> </plugin> <plugin> <artifactId>maven-assembly-plugin</artifactId> <configuration> Loading Loading @@ -65,7 +70,13 @@ <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter-api</artifactId> <version>5.9.2</version> <version>5.10.0</version> </dependency> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter-engine</artifactId> <version>5.10.0</version> <scope>test</scope> </dependency> <dependency> <groupId>org.assertj</groupId> Loading plugins/org.etsi.mts.tdl.execution.java.runtime/src/org/etsi/mts/tdl/execution/java/rt/core/GeneratedCodeApi.java 0 → 100644 +21 −0 Original line number Diff line number Diff line package org.etsi.mts.tdl.execution.java.rt.core; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * Marks runtime members that generated test code references <b>by name</b> * (the code generator emits them as strings, so the compiler cannot enforce the * dependency). Renaming or changing the signature of an annotated member * requires the corresponding change in the code generator, which lists these * names in its {@code RuntimeApi} class. Members without this annotation are * free to change as long as the runtime compiles. */ @Documented @Retention(RetentionPolicy.RUNTIME) @Target({ ElementType.METHOD, ElementType.FIELD, ElementType.CONSTRUCTOR, ElementType.TYPE }) public @interface GeneratedCodeApi { } plugins/org.etsi.mts.tdl.execution.java.runtime/test/org/etsi/mts/tdl/execution/java/rt/core/GeneratedCodeApiTest.java 0 → 100644 +121 −0 Original line number Diff line number Diff line package org.etsi.mts.tdl.execution.java.rt.core; import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.IOException; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; import java.util.stream.Stream; import org.junit.jupiter.api.Test; /** * Cross-checks the contract between the code generator and the runtime: every * runtime member the generator references by name (listed in the generator's * {@code RuntimeApi} class) must exist and carry {@link GeneratedCodeApi}, and * every annotated method or field must be listed there. The generator source is * read as a file, as the two modules share no compile-time dependency. */ class GeneratedCodeApiTest { /** * Location of the generator's RuntimeApi source, relative to this module; * overridable with the system property {@code tdl.runtimeApi}. */ private static final Path RUNTIME_API = Paths.get(System.getProperty("tdl.runtimeApi", "../org.etsi.mts.tdl.execution.java.codegen/src/org/etsi/mts/tdl/execution/java/codegen/RuntimeApi.java")); private static final Path CORE_SOURCES = Paths.get("src/org/etsi/mts/tdl/execution/java/rt/core"); private static final Pattern CONSTANT = Pattern .compile("static final String (\\w+) = \"(\\w+)\";(?:\\s*//\\s*(\\w+)#(\\w+))?"); /** owner simple name -> referenced member names */ private static Map<String, Set<String>> referencedMembers() throws IOException { Map<String, Set<String>> refs = new HashMap<>(); for (String line : Files.readAllLines(RUNTIME_API)) { Matcher m = CONSTANT.matcher(line); if (!m.find()) continue; String owner = m.group(3) != null ? m.group(3) : "TestControl"; refs.computeIfAbsent(owner, k -> new HashSet<>()).add(m.group(2)); } assertTrue(!refs.isEmpty(), "No constants found in " + RUNTIME_API.toAbsolutePath()); return refs; } /** All classes of the runtime core package, nested classes included. */ private static List<Class<?>> coreClasses() throws IOException, ClassNotFoundException { List<Class<?>> classes = new ArrayList<>(); try (Stream<Path> files = Files.list(CORE_SOURCES)) { for (Path f : files.filter(p -> p.toString().endsWith(".java")).collect(Collectors.toList())) { String name = f.getFileName().toString().replace(".java", ""); collect(Class.forName(GeneratedCodeApi.class.getPackageName() + "." + name), classes); } } return classes; } private static void collect(Class<?> c, List<Class<?>> into) { into.add(c); for (Class<?> nested : c.getDeclaredClasses()) collect(nested, into); } private static Set<String> annotatedMembers(Class<?> c) { Set<String> names = new HashSet<>(); for (Method m : c.getDeclaredMethods()) if (m.isAnnotationPresent(GeneratedCodeApi.class)) names.add(m.getName()); for (Field f : c.getDeclaredFields()) if (f.isAnnotationPresent(GeneratedCodeApi.class)) names.add(f.getName()); return names; } @Test void everyGeneratorReferenceIsAnAnnotatedRuntimeMember() throws Exception { Map<String, Set<String>> refs = referencedMembers(); Map<String, Class<?>> bySimpleName = new HashMap<>(); for (Class<?> c : coreClasses()) bySimpleName.put(c.getSimpleName(), c); List<String> problems = new ArrayList<>(); refs.forEach((owner, members) -> { Class<?> c = bySimpleName.get(owner); if (c == null) { problems.add(owner + ": no such runtime class"); return; } Set<String> annotated = annotatedMembers(c); for (String member : members) if (!annotated.contains(member)) problems.add(owner + "#" + member + ": not an @GeneratedCodeApi member"); }); assertTrue(problems.isEmpty(), "RuntimeApi references without annotated runtime member:\n" + String.join("\n", problems)); } @Test void everyAnnotatedMemberIsReferencedByTheGenerator() throws Exception { Set<String> referenced = referencedMembers().values().stream().flatMap(Set::stream).collect(Collectors.toSet()); List<String> problems = new ArrayList<>(); for (Class<?> c : coreClasses()) for (String member : annotatedMembers(c)) if (!referenced.contains(member)) problems.add(c.getSimpleName() + "#" + member); assertTrue(problems.isEmpty(), "@GeneratedCodeApi members not listed in RuntimeApi:\n" + String.join("\n", problems)); } } Loading
plugins/org.etsi.mts.tdl.execution.java.codegen/src/org/etsi/mts/tdl/execution/java/codegen/RuntimeApi.java 0 → 100644 +110 −0 Original line number Diff line number Diff line package org.etsi.mts.tdl.execution.java.codegen; /** * Names of the runtime members referenced <b>by name</b> from generated test * code, i.e. the dependency of this generator on the runtime that the compiler * cannot check. Every member listed here carries the {@code GeneratedCodeApi} * annotation in the runtime; keep the two in sync when renaming. */ final class RuntimeApi { private RuntimeApi() { } // ---- TestControl (the "tc" field of generated tests) static final String SYSTEM_ADAPTER = "systemAdapter"; static final String VALIDATOR = "validator"; static final String REPORTER = "reporter"; static final String FUNCTIONS = "functions"; static final String GET_TESTER_COMPONENT = "getTesterComponent"; static final String VALUE = "value"; static final String TERMINATE = "terminate"; static final String STOP = "stop"; static final String TIMEOUT = "timeout"; static final String TIME_CONSTRAINT_DEADLINE = "timeConstraintDeadline"; static final String TIME = "time"; static final String SLEEP = "sleep"; static final String SHUTDOWN = "shutdown"; static final String RESUME_RECEIVING = "resumeReceiving"; static final String REMOVE_EXCEPTIONAL_BEHAVIOUR = "removeExceptionalBehaviour"; static final String NO_INPUT = "noInput"; static final String NEXT = "next"; static final String GET_GATE_REFERENCE = "getGateReference"; static final String GET_CONNECTION = "getConnection"; static final String ENABLE_EXCEPTIONAL_BEHAVIOURS = "enableExceptionalBehaviours"; static final String DISABLE_ACTIVE_EXCEPTIONAL_BEHAVIOURS = "disableActiveExceptionalBehaviours"; static final String CONFIGURE = "configure"; static final String CLEANUP_BATCH = "cleanupBatch"; static final String CHECK_TIME_CONSTRAINT = "checkTimeConstraint"; static final String CALL = "call"; static final String BREAK_BLOCK = "breakBlock"; static final String AWAIT_TIME_CONSTRAINT = "awaitTimeConstraint"; static final String ADD_EXCEPTIONAL_BEHAVIOUR = "addExceptionalBehaviour"; static final String EXECUTE_ALTERNATIVES_AND_EXCEPTIONALS = "executeAlternativesAndExceptionals"; static final String RECEIVE = "receive"; static final String TRIGGER = "trigger"; static final String IS_TRUE = "isTrue"; static final String INTEGER = "integer"; static final String MILLIS = "millis"; static final String GET_EXCEPTIONAL_BEHAVIOUR = "getExceptionalBehaviour"; // ---- Other runtime classes (owner#member) static final String EXECUTE = "execute"; // ExecutionCallable#execute static final String GET_TIMESTAMP = "getTimestamp"; // ExecutionResult#getTimestamp static final String DATA = "data"; // InteractionResult#data static final String ALTERNATIVES = "alternatives"; // BatchedFutures#alternatives static final String EXCEPTIONALS = "exceptionals"; // BatchedFutures#exceptionals static final String ADD_TEST = "addTest"; // ComponentExecutor#addTest static final String BEHAVIOUR = "behaviour"; // ExceptionalBehaviour#behaviour static final String IS_INTERRUPT = "isInterrupt"; // ExceptionalBehaviour#isInterrupt static final String SET_CALLABLE = "setCallable"; // ExceptionalBehaviour#setCallable static final String GET_VERDICT = "getVerdict"; // StopExceptionImpl#getVerdict static final String GET_BLOCK_NAME = "getBlockName"; // BreakException#getBlockName static final String SET_TEMPLATE = "setTemplate"; // ValueImpl#setTemplate static final String SET_IS_COLLECTION = "setIsCollection"; // ValueImpl#setIsCollection static final String SET_IS_STRUCTURE = "setIsStructure"; // ValueImpl#setIsStructure static final String SET_MAPPING = "setMapping"; // ValueImpl#setMapping static final String SET_VALUE = "setValue"; // ValueImpl#setValue static final String SET_PARAMETER = "setParameter"; // ValueImpl#setParameter static final String ADD_ITEM = "addItem"; // ValueImpl#addItem static final String SET_NAME = "setName"; // ValueImpl#setName static final String ADD_ANNOTATION = "addAnnotation"; // ValueImpl#addAnnotation static final String AS_DATA = "asData"; // ValueImpl#asData static final String SET_ITEM_TYPE = "setItemType"; // TypeImpl#setItemType static final String SET_PARENT = "setParent"; // MappingImpl#setParent static final String SET_IS_RESOURCE = "setIsResource"; // MappingImpl#setIsResource static final String SET_IS_PARAMETER = "setIsParameter"; // MappingImpl#setIsParameter static final String SET_RESOURCE = "setResource"; // MappingImpl#setResource static final String NONE = "NONE"; // TemplateImpl#NONE static final String MARK = "mark"; // TemplateImpl#mark static final String MEMBER = "member"; // TemplateImpl#member static final String ITEM = "item"; // TemplateImpl#item static final String SET_UNASSIGNED_MEMBER_TREATMENT = "setUnassignedMemberTreatment"; // TemplateImpl#setUnassignedMemberTreatment static final String EVALUATE = "evaluate"; // Constraint#evaluate static final String HOLDS = "holds"; // Evaluation#holds static final String OPERANDS = "operands"; // Evaluation#operands static final String OF = "of"; // BehaviourProperties#of static final String PROPERTY_SOURCE = "SOURCE"; // BehaviourProperties#SOURCE static final String PROPERTY_RESULT = "RESULT"; // BehaviourProperties#RESULT static final String PROPERTY_OPERATOR = "OPERATOR"; // BehaviourProperties#OPERATOR static final String PROPERTY_OPERANDS = "OPERANDS"; // BehaviourProperties#OPERANDS static final String PROPERTY_VALUE = "VALUE"; // BehaviourProperties#VALUE static final String PROPERTY_TIME_CONSTRAINT_RESULT = "TIME_CONSTRAINT_RESULT"; // BehaviourProperties#TIME_CONSTRAINT_RESULT static final String PROPERTY_TIME_CONSTRAINT_OPERATOR = "TIME_CONSTRAINT_OPERATOR"; // BehaviourProperties#TIME_CONSTRAINT_OPERATOR static final String PROPERTY_TIME_CONSTRAINT_OPERANDS = "TIME_CONSTRAINT_OPERANDS"; // BehaviourProperties#TIME_CONSTRAINT_OPERANDS static final String TIMESTAMP = "timestamp"; // TimeLabel#timestamp static final String LAST = "last"; // TimeLabel#last static final String FIRST = "first"; // TimeLabel#first static final String PREVIOUS = "previous"; // TimeLabel#previous static final String FIRST_OR = "firstOr"; // TimeLabel#firstOr static final String NOW_MILLIS = "nowMillis"; // TimeLabel#nowMillis static final String TIMER_START = "start"; // Timer#start static final String TIMER_STOP = "stop"; // Timer#stop static final String FAIL = "fail"; // VerdictImpl#fail static final String PASS = "pass"; // VerdictImpl#pass (verdict instances are referenced by their TDL names) static final String INCONCLUSIVE = "inconclusive"; // VerdictImpl#inconclusive static final String TO_MILLIS = "toMillis"; // TimeUnit#toMillis }
plugins/org.etsi.mts.tdl.execution.java.runtime/pom.xml +12 −1 Original line number Diff line number Diff line Loading @@ -7,6 +7,7 @@ <version>1.0.0</version> <build> <sourceDirectory>src</sourceDirectory> <testSourceDirectory>test</testSourceDirectory> <plugins> <plugin> <artifactId>maven-compiler-plugin</artifactId> Loading @@ -15,6 +16,10 @@ <release>11</release> </configuration> </plugin> <plugin> <artifactId>maven-surefire-plugin</artifactId> <version>3.2.5</version> </plugin> <plugin> <artifactId>maven-assembly-plugin</artifactId> <configuration> Loading Loading @@ -65,7 +70,13 @@ <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter-api</artifactId> <version>5.9.2</version> <version>5.10.0</version> </dependency> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter-engine</artifactId> <version>5.10.0</version> <scope>test</scope> </dependency> <dependency> <groupId>org.assertj</groupId> Loading
plugins/org.etsi.mts.tdl.execution.java.runtime/src/org/etsi/mts/tdl/execution/java/rt/core/GeneratedCodeApi.java 0 → 100644 +21 −0 Original line number Diff line number Diff line package org.etsi.mts.tdl.execution.java.rt.core; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * Marks runtime members that generated test code references <b>by name</b> * (the code generator emits them as strings, so the compiler cannot enforce the * dependency). Renaming or changing the signature of an annotated member * requires the corresponding change in the code generator, which lists these * names in its {@code RuntimeApi} class. Members without this annotation are * free to change as long as the runtime compiles. */ @Documented @Retention(RetentionPolicy.RUNTIME) @Target({ ElementType.METHOD, ElementType.FIELD, ElementType.CONSTRUCTOR, ElementType.TYPE }) public @interface GeneratedCodeApi { }
plugins/org.etsi.mts.tdl.execution.java.runtime/test/org/etsi/mts/tdl/execution/java/rt/core/GeneratedCodeApiTest.java 0 → 100644 +121 −0 Original line number Diff line number Diff line package org.etsi.mts.tdl.execution.java.rt.core; import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.IOException; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; import java.util.stream.Stream; import org.junit.jupiter.api.Test; /** * Cross-checks the contract between the code generator and the runtime: every * runtime member the generator references by name (listed in the generator's * {@code RuntimeApi} class) must exist and carry {@link GeneratedCodeApi}, and * every annotated method or field must be listed there. The generator source is * read as a file, as the two modules share no compile-time dependency. */ class GeneratedCodeApiTest { /** * Location of the generator's RuntimeApi source, relative to this module; * overridable with the system property {@code tdl.runtimeApi}. */ private static final Path RUNTIME_API = Paths.get(System.getProperty("tdl.runtimeApi", "../org.etsi.mts.tdl.execution.java.codegen/src/org/etsi/mts/tdl/execution/java/codegen/RuntimeApi.java")); private static final Path CORE_SOURCES = Paths.get("src/org/etsi/mts/tdl/execution/java/rt/core"); private static final Pattern CONSTANT = Pattern .compile("static final String (\\w+) = \"(\\w+)\";(?:\\s*//\\s*(\\w+)#(\\w+))?"); /** owner simple name -> referenced member names */ private static Map<String, Set<String>> referencedMembers() throws IOException { Map<String, Set<String>> refs = new HashMap<>(); for (String line : Files.readAllLines(RUNTIME_API)) { Matcher m = CONSTANT.matcher(line); if (!m.find()) continue; String owner = m.group(3) != null ? m.group(3) : "TestControl"; refs.computeIfAbsent(owner, k -> new HashSet<>()).add(m.group(2)); } assertTrue(!refs.isEmpty(), "No constants found in " + RUNTIME_API.toAbsolutePath()); return refs; } /** All classes of the runtime core package, nested classes included. */ private static List<Class<?>> coreClasses() throws IOException, ClassNotFoundException { List<Class<?>> classes = new ArrayList<>(); try (Stream<Path> files = Files.list(CORE_SOURCES)) { for (Path f : files.filter(p -> p.toString().endsWith(".java")).collect(Collectors.toList())) { String name = f.getFileName().toString().replace(".java", ""); collect(Class.forName(GeneratedCodeApi.class.getPackageName() + "." + name), classes); } } return classes; } private static void collect(Class<?> c, List<Class<?>> into) { into.add(c); for (Class<?> nested : c.getDeclaredClasses()) collect(nested, into); } private static Set<String> annotatedMembers(Class<?> c) { Set<String> names = new HashSet<>(); for (Method m : c.getDeclaredMethods()) if (m.isAnnotationPresent(GeneratedCodeApi.class)) names.add(m.getName()); for (Field f : c.getDeclaredFields()) if (f.isAnnotationPresent(GeneratedCodeApi.class)) names.add(f.getName()); return names; } @Test void everyGeneratorReferenceIsAnAnnotatedRuntimeMember() throws Exception { Map<String, Set<String>> refs = referencedMembers(); Map<String, Class<?>> bySimpleName = new HashMap<>(); for (Class<?> c : coreClasses()) bySimpleName.put(c.getSimpleName(), c); List<String> problems = new ArrayList<>(); refs.forEach((owner, members) -> { Class<?> c = bySimpleName.get(owner); if (c == null) { problems.add(owner + ": no such runtime class"); return; } Set<String> annotated = annotatedMembers(c); for (String member : members) if (!annotated.contains(member)) problems.add(owner + "#" + member + ": not an @GeneratedCodeApi member"); }); assertTrue(problems.isEmpty(), "RuntimeApi references without annotated runtime member:\n" + String.join("\n", problems)); } @Test void everyAnnotatedMemberIsReferencedByTheGenerator() throws Exception { Set<String> referenced = referencedMembers().values().stream().flatMap(Set::stream).collect(Collectors.toSet()); List<String> problems = new ArrayList<>(); for (Class<?> c : coreClasses()) for (String member : annotatedMembers(c)) if (!referenced.contains(member)) problems.add(c.getSimpleName() + "#" + member); assertTrue(problems.isEmpty(), "@GeneratedCodeApi members not listed in RuntimeApi:\n" + String.join("\n", problems)); } }