diff --git a/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/BooleanValueExpressionAdapter.java b/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/BooleanValueExpressionAdapter.java index e7986715e24..89035a3d480 100644 --- a/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/BooleanValueExpressionAdapter.java +++ b/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/BooleanValueExpressionAdapter.java @@ -3,6 +3,7 @@ import com.datadog.debugger.el.expressions.BooleanExpression; import com.datadog.debugger.el.expressions.ValueExpression; import com.datadog.debugger.el.values.BooleanValue; +import java.util.Objects; public class BooleanValueExpressionAdapter implements ValueExpression { @@ -26,4 +27,18 @@ public BooleanValue evaluate(EvalContext evalContext) { public R accept(Visitor visitor) { return visitor.visit(booleanExpression); } + + @Generated + @Override + public boolean equals(Object o) { + if (o == null || getClass() != o.getClass()) return false; + BooleanValueExpressionAdapter that = (BooleanValueExpressionAdapter) o; + return Objects.equals(booleanExpression, that.booleanExpression); + } + + @Generated + @Override + public int hashCode() { + return Objects.hashCode(booleanExpression); + } } diff --git a/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/expressions/BinaryExpression.java b/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/expressions/BinaryExpression.java index 3c955d21b74..3a3afe6b19d 100644 --- a/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/expressions/BinaryExpression.java +++ b/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/expressions/BinaryExpression.java @@ -1,7 +1,9 @@ package com.datadog.debugger.el.expressions; import com.datadog.debugger.el.EvalContext; +import com.datadog.debugger.el.Generated; import com.datadog.debugger.el.Visitor; +import java.util.Objects; /** * Takes two {@linkplain BooleanExpression} instances and combines them with the given {@link @@ -52,4 +54,20 @@ public BooleanExpression getRight() { public BinaryOperator getOperator() { return operator; } + + @Generated + @Override + public boolean equals(Object o) { + if (o == null || getClass() != o.getClass()) return false; + BinaryExpression that = (BinaryExpression) o; + return Objects.equals(left, that.left) + && Objects.equals(right, that.right) + && operator == that.operator; + } + + @Generated + @Override + public int hashCode() { + return Objects.hash(left, right, operator); + } } diff --git a/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/expressions/ComparisonExpression.java b/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/expressions/ComparisonExpression.java index e14c47a67be..5579faaf409 100644 --- a/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/expressions/ComparisonExpression.java +++ b/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/expressions/ComparisonExpression.java @@ -5,9 +5,11 @@ import com.datadog.debugger.el.EvalContext; import com.datadog.debugger.el.EvaluationException; import com.datadog.debugger.el.EvaluationTimeOutException; +import com.datadog.debugger.el.Generated; import com.datadog.debugger.el.PrettyPrintVisitor; import com.datadog.debugger.el.Value; import com.datadog.debugger.el.Visitor; +import java.util.Objects; /** * Takes two {@linkplain ValueExpression} instances and compares them using the given {@link @@ -62,4 +64,20 @@ public ValueExpression getRight() { public ComparisonOperator getOperator() { return operator; } + + @Generated + @Override + public boolean equals(Object o) { + if (o == null || getClass() != o.getClass()) return false; + ComparisonExpression that = (ComparisonExpression) o; + return Objects.equals(left, that.left) + && Objects.equals(right, that.right) + && operator == that.operator; + } + + @Generated + @Override + public int hashCode() { + return Objects.hash(left, right, operator); + } } diff --git a/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/expressions/ContainsExpression.java b/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/expressions/ContainsExpression.java index bde8654b551..c7ad7dbe23f 100644 --- a/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/expressions/ContainsExpression.java +++ b/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/expressions/ContainsExpression.java @@ -5,10 +5,12 @@ import com.datadog.debugger.el.EvalContext; import com.datadog.debugger.el.EvaluationException; +import com.datadog.debugger.el.Generated; import com.datadog.debugger.el.PrettyPrintVisitor; import com.datadog.debugger.el.Value; import com.datadog.debugger.el.Visitor; import com.datadog.debugger.el.values.CollectionValue; +import java.util.Objects; public class ContainsExpression implements BooleanExpression { private final ValueExpression target; @@ -71,4 +73,18 @@ public ValueExpression getTarget() { public ValueExpression getValue() { return value; } + + @Generated + @Override + public boolean equals(Object o) { + if (o == null || getClass() != o.getClass()) return false; + ContainsExpression that = (ContainsExpression) o; + return Objects.equals(target, that.target) && Objects.equals(value, that.value); + } + + @Generated + @Override + public int hashCode() { + return Objects.hash(target, value); + } } diff --git a/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/expressions/FilterCollectionExpression.java b/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/expressions/FilterCollectionExpression.java index cd4c8ef1c89..3a0eff4ae13 100644 --- a/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/expressions/FilterCollectionExpression.java +++ b/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/expressions/FilterCollectionExpression.java @@ -9,6 +9,7 @@ import com.datadog.debugger.el.EvalContext; import com.datadog.debugger.el.EvaluationException; +import com.datadog.debugger.el.Generated; import com.datadog.debugger.el.Value; import com.datadog.debugger.el.Visitor; import com.datadog.debugger.el.values.CollectionValue; @@ -23,6 +24,7 @@ import java.util.HashMap; import java.util.HashSet; import java.util.Map; +import java.util.Objects; import java.util.Set; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -124,4 +126,19 @@ public ValueExpression getSource() { public BooleanExpression getFilterExpression() { return filterExpression; } + + @Generated + @Override + public boolean equals(Object o) { + if (o == null || getClass() != o.getClass()) return false; + FilterCollectionExpression that = (FilterCollectionExpression) o; + return Objects.equals(source, that.source) + && Objects.equals(filterExpression, that.filterExpression); + } + + @Generated + @Override + public int hashCode() { + return Objects.hash(source, filterExpression); + } } diff --git a/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/expressions/IndexExpression.java b/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/expressions/IndexExpression.java index 8c718eb282d..e26d52aee31 100644 --- a/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/expressions/IndexExpression.java +++ b/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/expressions/IndexExpression.java @@ -6,12 +6,14 @@ import com.datadog.debugger.el.EvalContext; import com.datadog.debugger.el.EvaluationException; +import com.datadog.debugger.el.Generated; import com.datadog.debugger.el.PrettyPrintVisitor; import com.datadog.debugger.el.Value; import com.datadog.debugger.el.Visitor; import com.datadog.debugger.el.values.ListValue; import com.datadog.debugger.el.values.MapValue; import datadog.trace.bootstrap.debugger.util.Redaction; +import java.util.Objects; public class IndexExpression implements ValueExpression> { @@ -81,4 +83,18 @@ public ValueExpression getTarget() { public ValueExpression getKey() { return key; } + + @Generated + @Override + public boolean equals(Object o) { + if (o == null || getClass() != o.getClass()) return false; + IndexExpression that = (IndexExpression) o; + return Objects.equals(target, that.target) && Objects.equals(key, that.key); + } + + @Generated + @Override + public int hashCode() { + return Objects.hash(target, key); + } } diff --git a/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/expressions/IsDefinedExpression.java b/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/expressions/IsDefinedExpression.java index 24ac98be2d8..b949f1fdc32 100644 --- a/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/expressions/IsDefinedExpression.java +++ b/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/expressions/IsDefinedExpression.java @@ -5,8 +5,10 @@ import com.datadog.debugger.el.EvalContext; import com.datadog.debugger.el.EvaluationException; import com.datadog.debugger.el.EvaluationTimeOutException; +import com.datadog.debugger.el.Generated; import com.datadog.debugger.el.Value; import com.datadog.debugger.el.Visitor; +import java.util.Objects; /** Check whether a {@linkplain Value} was resolved correctly or symbol exists.
*/ public class IsDefinedExpression implements BooleanExpression { @@ -41,4 +43,18 @@ public R accept(Visitor visitor) { public ValueExpression getValueExpression() { return valueExpression; } + + @Generated + @Override + public boolean equals(Object o) { + if (o == null || getClass() != o.getClass()) return false; + IsDefinedExpression that = (IsDefinedExpression) o; + return Objects.equals(valueExpression, that.valueExpression); + } + + @Generated + @Override + public int hashCode() { + return Objects.hashCode(valueExpression); + } } diff --git a/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/expressions/IsEmptyExpression.java b/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/expressions/IsEmptyExpression.java index 1c4b43eef51..4b502d33c76 100644 --- a/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/expressions/IsEmptyExpression.java +++ b/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/expressions/IsEmptyExpression.java @@ -4,11 +4,13 @@ import com.datadog.debugger.el.EvalContext; import com.datadog.debugger.el.EvaluationException; +import com.datadog.debugger.el.Generated; import com.datadog.debugger.el.PrettyPrintVisitor; import com.datadog.debugger.el.Value; import com.datadog.debugger.el.Visitor; import com.datadog.debugger.el.values.CollectionValue; import com.datadog.debugger.el.values.StringValue; +import java.util.Objects; /** * Checks whether a {@linkplain Value} is empty.
@@ -50,4 +52,18 @@ public R accept(Visitor visitor) { public ValueExpression getValueExpression() { return valueExpression; } + + @Generated + @Override + public boolean equals(Object o) { + if (o == null || getClass() != o.getClass()) return false; + IsEmptyExpression that = (IsEmptyExpression) o; + return Objects.equals(valueExpression, that.valueExpression); + } + + @Generated + @Override + public int hashCode() { + return Objects.hashCode(valueExpression); + } } diff --git a/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/expressions/LenExpression.java b/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/expressions/LenExpression.java index 3033d693cf1..24581d784fb 100644 --- a/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/expressions/LenExpression.java +++ b/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/expressions/LenExpression.java @@ -4,6 +4,7 @@ import com.datadog.debugger.el.EvalContext; import com.datadog.debugger.el.EvaluationException; +import com.datadog.debugger.el.Generated; import com.datadog.debugger.el.PrettyPrintVisitor; import com.datadog.debugger.el.Value; import com.datadog.debugger.el.ValueType; @@ -11,6 +12,7 @@ import com.datadog.debugger.el.values.CollectionValue; import com.datadog.debugger.el.values.NumericValue; import com.datadog.debugger.el.values.StringValue; +import java.util.Objects; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -61,4 +63,18 @@ public ValueExpression getSource() { public R accept(Visitor visitor) { return visitor.visit(this); } + + @Generated + @Override + public boolean equals(Object o) { + if (o == null || getClass() != o.getClass()) return false; + LenExpression that = (LenExpression) o; + return Objects.equals(source, that.source); + } + + @Generated + @Override + public int hashCode() { + return Objects.hashCode(source); + } } diff --git a/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/expressions/MatchingExpression.java b/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/expressions/MatchingExpression.java index 9c0138617c4..bb8731075f9 100644 --- a/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/expressions/MatchingExpression.java +++ b/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/expressions/MatchingExpression.java @@ -1,5 +1,8 @@ package com.datadog.debugger.el.expressions; +import com.datadog.debugger.el.Generated; +import java.util.Objects; + /** Abstract super class for expressions performing matching of a value against a predicate. */ abstract class MatchingExpression implements BooleanExpression { protected final ValueExpression valueExpression; @@ -19,4 +22,19 @@ public ValueExpression getValueExpression() { public BooleanExpression getFilterPredicateExpression() { return filterPredicateExpression; } + + @Generated + @Override + public boolean equals(Object o) { + if (o == null || getClass() != o.getClass()) return false; + MatchingExpression that = (MatchingExpression) o; + return Objects.equals(valueExpression, that.valueExpression) + && Objects.equals(filterPredicateExpression, that.filterPredicateExpression); + } + + @Generated + @Override + public int hashCode() { + return Objects.hash(valueExpression, filterPredicateExpression); + } } diff --git a/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/expressions/NotExpression.java b/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/expressions/NotExpression.java index 5cf8fad7607..92a30c0f170 100644 --- a/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/expressions/NotExpression.java +++ b/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/expressions/NotExpression.java @@ -3,7 +3,9 @@ import static com.datadog.debugger.el.expressions.ExpressionHelper.checkTimeout; import com.datadog.debugger.el.EvalContext; +import com.datadog.debugger.el.Generated; import com.datadog.debugger.el.Visitor; +import java.util.Objects; /** Will negate the resolved {@linkplain BooleanExpression} */ public final class NotExpression implements BooleanExpression { @@ -28,4 +30,18 @@ public R accept(Visitor visitor) { public BooleanExpression getPredicate() { return predicate; } + + @Generated + @Override + public boolean equals(Object o) { + if (o == null || getClass() != o.getClass()) return false; + NotExpression that = (NotExpression) o; + return Objects.equals(predicate, that.predicate); + } + + @Generated + @Override + public int hashCode() { + return Objects.hashCode(predicate); + } } diff --git a/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/expressions/StringPredicateExpression.java b/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/expressions/StringPredicateExpression.java index efb1a4f3269..36e880de564 100644 --- a/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/expressions/StringPredicateExpression.java +++ b/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/expressions/StringPredicateExpression.java @@ -2,9 +2,11 @@ import com.datadog.debugger.el.EvalContext; import com.datadog.debugger.el.EvaluationException; +import com.datadog.debugger.el.Generated; import com.datadog.debugger.el.PrettyPrintVisitor; import com.datadog.debugger.el.Value; import com.datadog.debugger.el.values.StringValue; +import java.util.Objects; import java.util.function.BiPredicate; public class StringPredicateExpression implements BooleanExpression { @@ -60,4 +62,21 @@ public BiPredicate getPredicate() { public String getName() { return name; } + + @Generated + @Override + public boolean equals(Object o) { + if (o == null || getClass() != o.getClass()) return false; + StringPredicateExpression that = (StringPredicateExpression) o; + // predicate is excluded: lambda equality is identity-based, the class and name identify it + return Objects.equals(sourceString, that.sourceString) + && Objects.equals(str, that.str) + && Objects.equals(name, that.name); + } + + @Generated + @Override + public int hashCode() { + return Objects.hash(sourceString, str, name); + } } diff --git a/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/expressions/SubStringExpression.java b/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/expressions/SubStringExpression.java index 40e7d258e33..fe326c535ff 100644 --- a/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/expressions/SubStringExpression.java +++ b/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/expressions/SubStringExpression.java @@ -4,10 +4,12 @@ import com.datadog.debugger.el.EvalContext; import com.datadog.debugger.el.EvaluationException; +import com.datadog.debugger.el.Generated; import com.datadog.debugger.el.PrettyPrintVisitor; import com.datadog.debugger.el.Value; import com.datadog.debugger.el.ValueType; import com.datadog.debugger.el.Visitor; +import java.util.Objects; public class SubStringExpression implements ValueExpression> { private final ValueExpression source; @@ -64,4 +66,20 @@ public int getStartIndex() { public int getEndIndex() { return endIndex; } + + @Generated + @Override + public boolean equals(Object o) { + if (o == null || getClass() != o.getClass()) return false; + SubStringExpression that = (SubStringExpression) o; + return startIndex == that.startIndex + && endIndex == that.endIndex + && Objects.equals(source, that.source); + } + + @Generated + @Override + public int hashCode() { + return Objects.hash(source, startIndex, endIndex); + } } diff --git a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/probe/LogProbe.java b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/probe/LogProbe.java index 16025bb7e1f..548086744b2 100644 --- a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/probe/LogProbe.java +++ b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/probe/LogProbe.java @@ -304,6 +304,23 @@ public Capture getCapture() { return capture; } + @Generated + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + CaptureExpression that = (CaptureExpression) o; + return Objects.equals(name, that.name) + && Objects.equals(expr, that.expr) + && Objects.equals(capture, that.capture); + } + + @Generated + @Override + public int hashCode() { + return Objects.hash(name, expr, capture); + } + @Generated @Override public String toString() { @@ -1181,6 +1198,7 @@ public boolean equals(Object o) { && Objects.equals(template, that.template) && Objects.equals(segments, that.segments) && Objects.equals(captureSnapshot, that.captureSnapshot) + && Objects.equals(captureExpressions, that.captureExpressions) && Objects.equals(probeCondition, that.probeCondition) && Objects.equals(capture, that.capture) && Objects.equals(sampling, that.sampling); @@ -1200,6 +1218,7 @@ public int hashCode() { template, segments, captureSnapshot, + captureExpressions, probeCondition, capture, sampling); diff --git a/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/probe/LogProbeTest.java b/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/probe/LogProbeTest.java index b67ec331ed6..38fb79e6985 100644 --- a/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/probe/LogProbeTest.java +++ b/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/probe/LogProbeTest.java @@ -1,6 +1,29 @@ package com.datadog.debugger.probe; import static com.datadog.debugger.agent.CapturingTestBase.getConfig; +import static com.datadog.debugger.el.DSL.and; +import static com.datadog.debugger.el.DSL.bool; +import static com.datadog.debugger.el.DSL.contains; +import static com.datadog.debugger.el.DSL.endsWith; +import static com.datadog.debugger.el.DSL.eq; +import static com.datadog.debugger.el.DSL.filter; +import static com.datadog.debugger.el.DSL.ge; +import static com.datadog.debugger.el.DSL.getMember; +import static com.datadog.debugger.el.DSL.gt; +import static com.datadog.debugger.el.DSL.index; +import static com.datadog.debugger.el.DSL.instanceOf; +import static com.datadog.debugger.el.DSL.isDefined; +import static com.datadog.debugger.el.DSL.isEmpty; +import static com.datadog.debugger.el.DSL.le; +import static com.datadog.debugger.el.DSL.len; +import static com.datadog.debugger.el.DSL.lt; +import static com.datadog.debugger.el.DSL.matches; +import static com.datadog.debugger.el.DSL.not; +import static com.datadog.debugger.el.DSL.or; +import static com.datadog.debugger.el.DSL.ref; +import static com.datadog.debugger.el.DSL.startsWith; +import static com.datadog.debugger.el.DSL.subString; +import static com.datadog.debugger.el.DSL.value; import static com.datadog.debugger.util.LogProbeTestHelper.parseTemplate; import static datadog.trace.api.debugger.DebuggerMetricCollector.SkippedReason.EVALUATION_TIME_OUT; import static datadog.trace.api.debugger.DebuggerMetricCollector.SkippedReason.RATE_LIMIT; @@ -9,6 +32,7 @@ import static java.util.Collections.emptyList; import static java.util.Collections.singletonList; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.any; @@ -25,6 +49,8 @@ import com.datadog.debugger.el.EvaluationTimeOutException; import com.datadog.debugger.el.ProbeCondition; import com.datadog.debugger.el.ValueScript; +import com.datadog.debugger.el.expressions.ValueExpression; +import com.datadog.debugger.el.values.StringValue; import com.datadog.debugger.probe.LogProbe.Builder; import com.datadog.debugger.probe.LogProbe.LogStatus; import com.datadog.debugger.sink.DebuggerSink; @@ -44,6 +70,11 @@ import datadog.trace.bootstrap.instrumentation.api.AgentTracer.TracerAPI; import datadog.trace.bootstrap.instrumentation.api.Tags; import datadog.trace.core.CoreTracer; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.List; +import java.util.function.Supplier; import java.util.stream.IntStream; import java.util.stream.Stream; import javax.annotation.Nonnull; @@ -76,6 +107,256 @@ public void testSampling() { assertEquals(0.25, snapshotProbe.getSampling().getEventsPerSecond(), 0.01); } + @ParameterizedTest(name = "{0}") + @MethodSource("valueExpressions") + public void equalsSegment(String dsl, Supplier> exprSupplier) { + LogProbe.Segment segment1 = new LogProbe.Segment(new ValueScript(exprSupplier.get(), dsl)); + LogProbe.Segment segment2 = new LogProbe.Segment(new ValueScript(exprSupplier.get(), dsl)); + assertEquals(segment1, segment2); + } + + private static Stream valueExpressions() { + return Stream.of( + valueExpression("field", () -> ref("field")), + valueExpression("obj.field", () -> getMember(ref("obj"), "field")), + valueExpression("list[0]", () -> index(ref("list"), value(0))), + valueExpression("map['key']", () -> index(ref("map"), value("key"))), + valueExpression("len(list)", () -> len(ref("list"))), + valueExpression("substring(str, 0, 2)", () -> subString(ref("str"), 0, 2)), + valueExpression( + "filter(list, {len(@it) > 0})", + () -> filter(ref("list"), gt(len(ref("@it")), value(0)))), + valueExpression( + "any(list, {@it == 1})", () -> bool(DSL.any(ref("list"), eq(ref("@it"), value(1))))), + valueExpression( + "all(list, {@it == 1})", () -> bool(DSL.all(ref("list"), eq(ref("@it"), value(1))))), + valueExpression("isEmpty(list)", () -> bool(isEmpty(ref("list")))), + valueExpression("isDefined(field)", () -> bool(isDefined(ref("field")))), + valueExpression( + "startsWith(str, 'foo')", () -> bool(startsWith(ref("str"), new StringValue("foo")))), + valueExpression( + "endsWith(str, 'foo')", () -> bool(endsWith(ref("str"), new StringValue("foo")))), + valueExpression( + "matches(str, '[a-z]+')", () -> bool(matches(ref("str"), new StringValue("[a-z]+")))), + valueExpression("contains(str, 'foo')", () -> bool(contains(ref("str"), value("foo")))), + valueExpression( + "instanceof(obj, 'java.lang.String')", + () -> bool(instanceOf(ref("obj"), value("java.lang.String")))), + valueExpression("field == 1", () -> bool(eq(ref("field"), value(1)))), + valueExpression("field > 1", () -> bool(gt(ref("field"), value(1)))), + valueExpression("field >= 1", () -> bool(ge(ref("field"), value(1)))), + valueExpression("field < 1", () -> bool(lt(ref("field"), value(1)))), + valueExpression("field <= 1", () -> bool(le(ref("field"), value(1)))), + valueExpression("not(isEmpty(list))", () -> bool(not(isEmpty(ref("list"))))), + valueExpression( + "isEmpty(list) && isDefined(field)", + () -> bool(and(isEmpty(ref("list")), isDefined(ref("field"))))), + valueExpression( + "isEmpty(list) || isDefined(field)", + () -> bool(or(isEmpty(ref("list")), isDefined(ref("field")))))); + } + + private static Arguments valueExpression(String dsl, Supplier> expr) { + return Arguments.of(dsl, expr); + } + + @ParameterizedTest(name = "{0}") + @MethodSource("valueExpressions") + public void hashCodeSegment(String dsl, Supplier> exprSupplier) { + LogProbe.Segment segment1 = new LogProbe.Segment(new ValueScript(exprSupplier.get(), dsl)); + LogProbe.Segment segment2 = new LogProbe.Segment(new ValueScript(exprSupplier.get(), dsl)); + assertEquals(segment1.hashCode(), segment2.hashCode()); + } + + @ParameterizedTest(name = "{0}") + @MethodSource("differentValueExpressions") + public void notEqualsSegment(String name, ValueExpression expr1, ValueExpression expr2) { + // same dsl string on both sides so that only the expression differs + LogProbe.Segment segment1 = new LogProbe.Segment(new ValueScript(expr1, "dsl")); + LogProbe.Segment segment2 = new LogProbe.Segment(new ValueScript(expr2, "dsl")); + assertNotEquals(segment1, segment2); + } + + private static Stream differentValueExpressions() { + return Stream.of( + Arguments.of("ref", ref("field1"), ref("field2")), + Arguments.of("getMember", getMember(ref("obj"), "field1"), getMember(ref("obj"), "field2")), + Arguments.of("index key", index(ref("list"), value(0)), index(ref("list"), value(1))), + Arguments.of("index target", index(ref("list1"), value(0)), index(ref("list2"), value(0))), + Arguments.of("len", len(ref("list1")), len(ref("list2"))), + Arguments.of("substring start", subString(ref("str"), 0, 2), subString(ref("str"), 1, 2)), + Arguments.of("substring end", subString(ref("str"), 0, 2), subString(ref("str"), 0, 3)), + Arguments.of( + "substring source", subString(ref("str1"), 0, 2), subString(ref("str2"), 0, 2)), + Arguments.of( + "filter predicate", + filter(ref("list"), gt(len(ref("@it")), value(0))), + filter(ref("list"), gt(len(ref("@it")), value(1)))), + Arguments.of( + "filter source", + filter(ref("list1"), gt(len(ref("@it")), value(0))), + filter(ref("list2"), gt(len(ref("@it")), value(0)))), + Arguments.of( + "any vs all", + bool(DSL.any(ref("list"), eq(ref("@it"), value(1)))), + bool(DSL.all(ref("list"), eq(ref("@it"), value(1))))), + Arguments.of( + "any predicate", + bool(DSL.any(ref("list"), eq(ref("@it"), value(1)))), + bool(DSL.any(ref("list"), eq(ref("@it"), value(2))))), + Arguments.of("isEmpty", bool(isEmpty(ref("list1"))), bool(isEmpty(ref("list2")))), + Arguments.of("isDefined", bool(isDefined(ref("field1"))), bool(isDefined(ref("field2")))), + Arguments.of( + "startsWith vs endsWith", + bool(startsWith(ref("str"), new StringValue("foo"))), + bool(endsWith(ref("str"), new StringValue("foo")))), + Arguments.of( + "startsWith str", + bool(startsWith(ref("str"), new StringValue("foo"))), + bool(startsWith(ref("str"), new StringValue("bar")))), + Arguments.of( + "matches pattern", + bool(matches(ref("str"), new StringValue("[a-z]+"))), + bool(matches(ref("str"), new StringValue("[0-9]+")))), + Arguments.of( + "contains value", + bool(contains(ref("str"), value("foo"))), + bool(contains(ref("str"), value("bar")))), + Arguments.of( + "instanceof", + bool(instanceOf(ref("obj"), value("java.lang.String"))), + bool(instanceOf(ref("obj"), value("java.lang.Integer")))), + Arguments.of( + "comparison operator", + bool(gt(ref("field"), value(1))), + bool(ge(ref("field"), value(1)))), + Arguments.of( + "comparison left", + bool(eq(ref("field1"), value(1))), + bool(eq(ref("field2"), value(1)))), + Arguments.of( + "comparison right", bool(eq(ref("field"), value(1))), bool(eq(ref("field"), value(2)))), + Arguments.of("not", bool(not(isEmpty(ref("list1")))), bool(not(isEmpty(ref("list2"))))), + Arguments.of( + "and vs or", + bool(and(isEmpty(ref("list")), isDefined(ref("field")))), + bool(or(isEmpty(ref("list")), isDefined(ref("field"))))), + Arguments.of( + "binary left", + bool(and(isEmpty(ref("list1")), isDefined(ref("field")))), + bool(and(isEmpty(ref("list2")), isDefined(ref("field"))))), + Arguments.of( + "binary right", + bool(and(isEmpty(ref("list")), isDefined(ref("field1")))), + bool(and(isEmpty(ref("list")), isDefined(ref("field2")))))); + } + + @Test + public void notEqualsCaptureExpression() { + LogProbe.CaptureExpression base = + new LogProbe.CaptureExpression( + "expr", new ValueScript(ref("field"), "field"), new LogProbe.Capture(1, 2, 3, 4)); + assertNotEquals( + base, + new LogProbe.CaptureExpression( + "other", new ValueScript(ref("field"), "field"), new LogProbe.Capture(1, 2, 3, 4))); + assertNotEquals( + base, + new LogProbe.CaptureExpression( + "expr", new ValueScript(ref("other"), "field"), new LogProbe.Capture(1, 2, 3, 4))); + assertNotEquals( + base, + new LogProbe.CaptureExpression( + "expr", new ValueScript(ref("field"), "field"), new LogProbe.Capture(5, 2, 3, 4))); + } + + @ParameterizedTest(name = "{0}") + @MethodSource("valueExpressions") + public void equalsCaptureExpression(String dsl, Supplier> exprSupplier) { + LogProbe.CaptureExpression captureExpression1 = createCaptureExpression(dsl, exprSupplier); + LogProbe.CaptureExpression captureExpression2 = createCaptureExpression(dsl, exprSupplier); + assertEquals(captureExpression1, captureExpression2); + } + + @ParameterizedTest(name = "{0}") + @MethodSource("valueExpressions") + public void hashCodeCaptureExpression(String dsl, Supplier> exprSupplier) { + LogProbe.CaptureExpression captureExpression1 = createCaptureExpression(dsl, exprSupplier); + LogProbe.CaptureExpression captureExpression2 = createCaptureExpression(dsl, exprSupplier); + assertEquals(captureExpression1.hashCode(), captureExpression2.hashCode()); + } + + private static LogProbe.CaptureExpression createCaptureExpression( + String dsl, Supplier> exprSupplier) { + return new LogProbe.CaptureExpression( + "expr", new ValueScript(exprSupplier.get(), dsl), new LogProbe.Capture(1, 2, 3, 4)); + } + + @Test + public void equalsCoversAllNonTransientFields() throws Exception { + assertEquals(createFullProbe1(), createFullProbe1()); + LogProbe other = createFullProbe2(); + List uncoveredFields = new ArrayList<>(); + for (Class clazz = LogProbe.class; clazz != Object.class; clazz = clazz.getSuperclass()) { + for (Field field : clazz.getDeclaredFields()) { + int modifiers = field.getModifiers(); + if (Modifier.isStatic(modifiers) + || Modifier.isTransient(modifiers) + || field.isSynthetic()) { + continue; + } + field.setAccessible(true); + LogProbe probe = createFullProbe1(); + Object otherValue = field.get(other); + assertNotEquals( + field.get(probe), otherValue, "field " + field.getName() + " must differ in test data"); + field.set(probe, otherValue); + if (probe.equals(createFullProbe1())) { + uncoveredFields.add(clazz.getSimpleName() + "." + field.getName()); + } + } + } + assertTrue(uncoveredFields.isEmpty(), "equals does not cover fields: " + uncoveredFields); + } + + private static LogProbe createFullProbe1() { + return LogProbe.builder() + .language(LANGUAGE) + .probeId("probe-id-1", 1) + .tags("tag1:value1") + .where("Class1", "method1") + .evaluateAt(MethodLocation.ENTRY) + .template("{arg1}", parseTemplate("{arg1}")) + .captureSnapshot(true) + .when(new ProbeCondition(DSL.when(DSL.TRUE), "true")) + .capture(1, 2, 3, 4) + .sampling(1.0) + .captureExpressions( + singletonList( + new LogProbe.CaptureExpression( + "expr1", new ValueScript(DSL.ref("arg1"), "arg1"), null))) + .build(); + } + + private static LogProbe createFullProbe2() { + return LogProbe.builder() + .language("other") + .probeId("probe-id-2", 2) + .tags("tag2:value2") + .where("Class2", "method2") + .evaluateAt(MethodLocation.EXIT) + .template("{arg2}", parseTemplate("{arg2}")) + .captureSnapshot(false) + .when(new ProbeCondition(DSL.when(DSL.FALSE), "false")) + .capture(5, 6, 7, 8) + .sampling(2.0) + .captureExpressions( + singletonList( + new LogProbe.CaptureExpression( + "expr2", new ValueScript(DSL.ref("arg2"), "arg2"), null))) + .build(); + } + @Test public void coordinatedSamplingEmitsProbeOnceConcurrently() { LogProbe.CoordinatedSamplingState state =