diff --git a/pages/docs/inner-workings.md b/pages/docs/inner-workings.md index bd9986a9c7c81c034e30e57e364d243393a8e39a..1cb22a597804d2a67a6f05c0181f4b7c359644ee 100644 --- a/pages/docs/inner-workings.md +++ b/pages/docs/inner-workings.md @@ -1,4 +1,4 @@ -# Inner workings of `RagConnect` +# Inner Workings of `RagConnect` Please see [API documentation](ragdoc/index.html) for more details. @@ -20,11 +20,11 @@ The other main aspect (which is currently not really used) is `IntermediateToYAM This is used to generate a YAML file containing the data used by mustache. It can be used by the default mustache implementation together with the templates. -# Implementation details +# Implementation Details In the following, details for special implementation topics are discussed. -## forwarding +## Forwarding When a nonterminal is used in a send endpoints, it needs an implicit forwarding attribute to work, because only _computed elements_ can be sent. Since the nonterminal itself should be sent, the generated attribute simply returns this nonterminal. @@ -33,3 +33,29 @@ However, changing any token within the whole subtree or changing the structure o This way, the dependency tracking registers a dependency between structure and tokens to the attribute. The attribute (as well as any other generated element) is prefixed with `_ragconnect_` to avoid potential name conflicts with user-specified elements. + +# Implementation Hints + +## Debugging Tests and Finding Bugs + +To help with finding errors/bugs when tests fail, there are several things to find the correct spot. + +- **Look closely**. Analyze the error message closely, and possible any previous error message(s) that could have caused the test to fail. +- **Focus on single error** + - To only inspect one test, mark them with `@Tag("New")` and use the gradle task "newTests". + - Use `Assumptions.assumeTrue(false);` to abort unneeded test cases early. + - When editing RagConnect itself and force recreating source for the affected test, e.g., `compileForwardingIncremental.outputs.upToDateWhen { false }` + - _Remember to undo all changes, once the bug is fixed._ +- **Activate logs**. Add the following to the `ragconnect` specification of the compile-task of the affected test: + ``` + logReads = true + logWrites = true + logIncremental = true + ``` + _Remember to remove those lines, once the bug is fixed._ +- **Trace incremental events**. Add the following right after create the root node (named `model` here): + ```java + model.trace().setReceiver(TestUtils::logEvent); + ``` + This will output every event fired by the incremental evaluation engine. _Remember to remove this line, once the bug is fixed._ +- **Add log statements**. As there will be quite some log output, add some identifying log statement (i.e., using `logger.fatal("---")`) right before the suspicious statement to inspect only the relevant log message after that. diff --git a/ragconnect.base/src/main/resources/ragconnect.mustache b/ragconnect.base/src/main/resources/ragconnect.mustache index 088d2d3504d90f73e46fe976727d68d2bbae33e0..dc6b839a87050d3993cd1bf6f19ffe3694d16242 100644 --- a/ragconnect.base/src/main/resources/ragconnect.mustache +++ b/ragconnect.base/src/main/resources/ragconnect.mustache @@ -191,7 +191,7 @@ aspect RagConnectObserver { oldReceiver.accept(event, node, attribute, params, value); {{#configExperimentalJastAdd329}} // react to INC_FLUSH_START and remember entry - if (event == ASTState.Trace.Event.INC_FLUSH_START && startEntries.isEmpty()) { + if (event == ASTState.Trace.Event.INC_FLUSH_START) { {{#configLoggingEnabledForIncremental}} System.out.println("** observer start: " + node + " on " + attribute); {{/configLoggingEnabledForIncremental}} @@ -207,13 +207,14 @@ aspect RagConnectObserver { {{/configLoggingEnabledForIncremental}} return; } - RagConnectObserverStartEntry startEntry = startEntries.removeFirst(); + RagConnectObserverStartEntry startEntry = startEntries.peekFirst(); if (node == startEntry.node && attribute == startEntry.attributeString && value == startEntry.flushIncToken) { // create a copy of the queue to avoid entering this again causing an endless recursion RagConnectObserverEntry[] entriesToProcess = entryQueue.toArray(new RagConnectObserverEntry[entryQueue.size()]); entryQueue.clear(); + startEntries.removeFirst(); {{#configLoggingEnabledForIncremental}} System.out.println("** observer process (entries: " + entriesToProcess.length + "): " + node + " on " + attribute); {{/configLoggingEnabledForIncremental}} diff --git a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/AttributeTest.java b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/AttributeTest.java index 54f0bcafbbce3bff91d9939d7dd58f23460188f0..2e7e0c7d79bd7d20e938eff6257008379306bc8d 100644 --- a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/AttributeTest.java +++ b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/AttributeTest.java @@ -1,7 +1,6 @@ package org.jastadd.ragconnect.tests; import attributeInc.ast.*; -import org.awaitility.core.ConditionFactory; import org.junit.jupiter.api.Tag; import java.io.IOException; @@ -12,9 +11,7 @@ import java.util.function.Predicate; import java.util.function.Supplier; import static java.util.function.Predicate.isEqual; -import static org.awaitility.Awaitility.await; -import static org.jastadd.ragconnect.tests.TestUtils.mqttUri; -import static org.jastadd.ragconnect.tests.TestUtils.waitForMqtt; +import static org.jastadd.ragconnect.tests.TestUtils.*; import static org.junit.jupiter.api.Assertions.assertTrue; /** @@ -23,7 +20,6 @@ import static org.junit.jupiter.api.Assertions.assertTrue; * @author rschoene - Initial contribution */ @Tag("Incremental") -@Tag("New") public class AttributeTest extends AbstractMqttTest { private static final String TOPIC_WILDCARD = "attr/#"; @@ -105,13 +101,13 @@ public class AttributeTest extends AbstractMqttTest { private <T> void waitForValue(T expectedValue, Callable<T> callable) { if (isWriteCurrentValue()) { - await().until(callable, isEqual(expectedValue)); + awaitMqtt().until(callable, isEqual(expectedValue)); } } private <T> void waitForNonNull(Callable<T> callable) { if (isWriteCurrentValue()) { - await().until(callable, Predicate.not(isEqual(null))); + awaitMqtt().until(callable, Predicate.not(isEqual(null))); } } @@ -227,11 +223,11 @@ public class AttributeTest extends AbstractMqttTest { } private void awaitNull(Supplier<A> actual, String alias) { - internalAwait(alias).until(() -> actual.get() == null); + awaitMqtt().alias(alias).until(() -> actual.get() == null); } private <T> void awaitEquals(T expected, Callable<T> actual, String alias) { - internalAwait(alias).until(actual, isEqual(expected)); + awaitMqtt().alias(alias).until(actual, isEqual(expected)); } private void awaitA(String expectedValue, String expectedInner, A actual, String message) { @@ -239,10 +235,6 @@ public class AttributeTest extends AbstractMqttTest { awaitEquals(expectedInner, actual.getInner()::getInnerValue, message + " inner"); } - private ConditionFactory internalAwait(String alias) { - return await(alias).atMost(1500, TimeUnit.MILLISECONDS); - } - @Override protected void closeConnections() { if (handler != null) { diff --git a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/ForwardingTest.java b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/ForwardingTest.java index 64430eb40800d7bea28e38e63d0cb73728f5856f..e73f76981cd82034e2fd36d21225b539e8d00e74 100644 --- a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/ForwardingTest.java +++ b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/ForwardingTest.java @@ -1,8 +1,6 @@ package org.jastadd.ragconnect.tests; import forwardingInc.ast.*; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; import org.assertj.core.groups.Tuple; import org.junit.jupiter.api.Tag; @@ -14,9 +12,7 @@ import java.util.concurrent.atomic.AtomicBoolean; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.groups.Tuple.tuple; -import static org.awaitility.Awaitility.await; -import static org.jastadd.ragconnect.tests.TestUtils.mqttUri; -import static org.jastadd.ragconnect.tests.TestUtils.waitForMqtt; +import static org.jastadd.ragconnect.tests.TestUtils.*; import static org.junit.jupiter.api.Assertions.*; /** @@ -55,7 +51,7 @@ public class ForwardingTest extends AbstractMqttTest { @Override protected void createModel() { model = new Root(); -// model.trace().setReceiver(TestUtils::logEvent); + model.trace().setReceiver(TestUtils::logEvent); senderRoot = new SenderRoot(); model.setSenderRoot(senderRoot); @@ -183,7 +179,7 @@ public class ForwardingTest extends AbstractMqttTest { private void waitForValue() { if (isWriteCurrentValue()) { - await().until(() -> data.valueSentSinceLastCheck.getAndSet(false)); + awaitMqtt().until(() -> data.valueSentSinceLastCheck.getAndSet(false)); } } @@ -524,9 +520,7 @@ public class ForwardingTest extends AbstractMqttTest { if (model != null) { model.ragconnectCloseConnections(); } - if (observer != null) { - observer.init(); - } + observer.init(); } static class Values<T> { diff --git a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/IndexedSendTest.java b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/IndexedSendTest.java index a4575feafc716fe6dbd95f43f2b2212436a843d0..75d7d16b18c64f7b5fd39c71acba81f92223a2af 100644 --- a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/IndexedSendTest.java +++ b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/IndexedSendTest.java @@ -11,9 +11,7 @@ import java.util.concurrent.TimeUnit; import java.util.function.Predicate; import static org.assertj.core.groups.Tuple.tuple; -import static org.awaitility.Awaitility.await; -import static org.jastadd.ragconnect.tests.TestUtils.mqttUri; -import static org.jastadd.ragconnect.tests.TestUtils.waitForMqtt; +import static org.jastadd.ragconnect.tests.TestUtils.*; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -99,7 +97,7 @@ public class IndexedSendTest extends AbstractMqttTest { private void waitForValue(Callable<Integer> callable, int expectedValue) { if (isWriteCurrentValue()) { - await().until(callable, Predicate.isEqual(expectedValue)); + awaitMqtt().until(callable, Predicate.isEqual(expectedValue)); } } diff --git a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/TestUtils.java b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/TestUtils.java index 937c930d4b4a9e75003dbf74cdc42f12bc85ca0e..9a6c22bd962940b5d0dc0e942dcc91a3ca1fbf19 100644 --- a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/TestUtils.java +++ b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/TestUtils.java @@ -5,6 +5,8 @@ import com.fasterxml.jackson.core.JsonFactory; import com.fasterxml.jackson.core.JsonGenerator; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.awaitility.Awaitility; +import org.awaitility.core.ConditionFactory; import org.jastadd.ragconnect.compiler.Compiler; import org.junit.jupiter.api.Assertions; @@ -24,7 +26,6 @@ import java.util.stream.Collectors; import static java.util.Collections.addAll; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.util.Lists.newArrayList; -import static org.awaitility.Awaitility.await; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; @@ -145,6 +146,10 @@ public class TestUtils { TimeUnit.MILLISECONDS.sleep(1500); } + public static ConditionFactory awaitMqtt() { + return Awaitility.await().atMost(1500, TimeUnit.MILLISECONDS); + } + static <T_Event, T_ASTNode> void logEvent(T_Event event, T_ASTNode node, String attribute, Object params, Object value) { logger.info("event: {}, node: {}, attribute: {}, params: {}, value: {}", event, node, attribute, params, value); @@ -388,7 +393,7 @@ public class TestUtils { } void awaitChange() { - await().until(hasChanged); + awaitMqtt().until(hasChanged); updatePrevious(); } diff --git a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/singleList/AbstractSingleListTest.java b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/singleList/AbstractSingleListTest.java index 832ba76d7df935164b7ebd5ac90af1bbb3e854e9..cdcb41c72e1af5b9b53f9afba3fd78eefbe06bd6 100644 --- a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/singleList/AbstractSingleListTest.java +++ b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/singleList/AbstractSingleListTest.java @@ -14,10 +14,8 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.Function; -import static org.awaitility.Awaitility.await; +import static org.jastadd.ragconnect.tests.TestUtils.*; import static org.jastadd.ragconnect.tests.TestUtils.IntList.list; -import static org.jastadd.ragconnect.tests.TestUtils.mqttUri; -import static org.jastadd.ragconnect.tests.TestUtils.testJaddContainReferenceToJackson; import static org.junit.jupiter.api.Assertions.*; /** @@ -167,7 +165,7 @@ public abstract class AbstractSingleListTest extends AbstractMqttTest { private void waitForValue() { if (isWriteCurrentValue()) { - await().until(() -> data.valueSentSinceLastCheck.getAndSet(false)); + awaitMqtt().until(() -> data.valueSentSinceLastCheck.getAndSet(false)); } }