Migrate rxjava3 unit tests to Java (#7924)
This commit is contained in:
parent
999863d39b
commit
fd9ef741a0
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,951 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyNoInteractions;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import io.opentelemetry.api.common.AttributeKey;
|
||||
import io.opentelemetry.api.trace.Span;
|
||||
import io.opentelemetry.context.Context;
|
||||
import io.opentelemetry.instrumentation.api.annotation.support.async.AsyncOperationEndStrategy;
|
||||
import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter;
|
||||
import io.opentelemetry.instrumentation.rxjava.v3.common.RxJava3AsyncOperationEndStrategy;
|
||||
import io.reactivex.rxjava3.core.Completable;
|
||||
import io.reactivex.rxjava3.core.Flowable;
|
||||
import io.reactivex.rxjava3.core.Maybe;
|
||||
import io.reactivex.rxjava3.core.Observable;
|
||||
import io.reactivex.rxjava3.core.Single;
|
||||
import io.reactivex.rxjava3.observers.TestObserver;
|
||||
import io.reactivex.rxjava3.parallel.ParallelFlowable;
|
||||
import io.reactivex.rxjava3.processors.ReplayProcessor;
|
||||
import io.reactivex.rxjava3.processors.UnicastProcessor;
|
||||
import io.reactivex.rxjava3.subjects.CompletableSubject;
|
||||
import io.reactivex.rxjava3.subjects.MaybeSubject;
|
||||
import io.reactivex.rxjava3.subjects.ReplaySubject;
|
||||
import io.reactivex.rxjava3.subjects.SingleSubject;
|
||||
import io.reactivex.rxjava3.subjects.UnicastSubject;
|
||||
import io.reactivex.rxjava3.subscribers.TestSubscriber;
|
||||
import org.junit.jupiter.api.Nested;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import org.reactivestreams.Publisher;
|
||||
import org.reactivestreams.Subscriber;
|
||||
import org.reactivestreams.Subscription;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
public class RxJava3AsyncOperationEndStrategyTest {
|
||||
private static final AttributeKey<Boolean> CANCELED_ATTRIBUTE_KEY =
|
||||
AttributeKey.booleanKey("rxjava.canceled");
|
||||
@Mock Instrumenter<String, String> instrumenter;
|
||||
@Mock Span span;
|
||||
private final AsyncOperationEndStrategy underTest = RxJava3AsyncOperationEndStrategy.create();
|
||||
private final RxJava3AsyncOperationEndStrategy underTestWithExperimentalAttributes =
|
||||
RxJava3AsyncOperationEndStrategy.builder().setCaptureExperimentalSpanAttributes(true).build();
|
||||
|
||||
@Nested
|
||||
class CompletableTest {
|
||||
@Test
|
||||
void supported() {
|
||||
assertThat(underTest.supports(Completable.class)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void endsSpanOnAlreadyCompleted() {
|
||||
Completable result =
|
||||
(Completable)
|
||||
underTest.end(
|
||||
instrumenter, Context.root(), "request", Completable.complete(), String.class);
|
||||
TestObserver<Void> observer = result.test();
|
||||
|
||||
observer.assertComplete();
|
||||
verify(instrumenter).end(Context.root(), "request", null, null);
|
||||
}
|
||||
|
||||
@Test
|
||||
void endsSpanOnAlreadyErrored() {
|
||||
IllegalStateException exception = new IllegalStateException();
|
||||
|
||||
Completable result =
|
||||
(Completable)
|
||||
underTest.end(
|
||||
instrumenter,
|
||||
Context.root(),
|
||||
"request",
|
||||
Completable.error(exception),
|
||||
String.class);
|
||||
TestObserver<Void> observer = result.test();
|
||||
|
||||
observer.assertError(exception);
|
||||
verify(instrumenter).end(Context.root(), "request", null, exception);
|
||||
}
|
||||
|
||||
@Test
|
||||
void endsSpanWhenCompleted() {
|
||||
CompletableSubject source = CompletableSubject.create();
|
||||
|
||||
Completable result =
|
||||
(Completable)
|
||||
underTest.end(instrumenter, Context.root(), "request", source, String.class);
|
||||
TestObserver<Void> observer = result.test();
|
||||
|
||||
verifyNoInteractions(instrumenter);
|
||||
|
||||
source.onComplete();
|
||||
observer.assertComplete();
|
||||
verify(instrumenter).end(Context.root(), "request", null, null);
|
||||
}
|
||||
|
||||
@Test
|
||||
void endsSpanWhenErrored() {
|
||||
IllegalStateException exception = new IllegalStateException();
|
||||
CompletableSubject source = CompletableSubject.create();
|
||||
|
||||
Completable result =
|
||||
(Completable)
|
||||
underTest.end(instrumenter, Context.root(), "request", source, String.class);
|
||||
TestObserver<Void> observer = result.test();
|
||||
|
||||
verifyNoInteractions(instrumenter);
|
||||
|
||||
source.onError(exception);
|
||||
observer.assertError(exception);
|
||||
verify(instrumenter).end(Context.root(), "request", null, exception);
|
||||
}
|
||||
|
||||
@Test
|
||||
void endsWhenCancelled() {
|
||||
when(span.storeInContext(any())).thenCallRealMethod();
|
||||
CompletableSubject source = CompletableSubject.create();
|
||||
Context context = Context.root().with(span);
|
||||
|
||||
Completable result =
|
||||
(Completable) underTest.end(instrumenter, context, "request", source, String.class);
|
||||
TestObserver<Void> observer = result.test();
|
||||
|
||||
verifyNoInteractions(instrumenter);
|
||||
|
||||
observer.dispose();
|
||||
verify(instrumenter).end(context, "request", null, null);
|
||||
verify(span, never()).setAttribute(CANCELED_ATTRIBUTE_KEY, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
void endsSpanWhenCancelledExperimentalAttribute() {
|
||||
when(span.storeInContext(any())).thenCallRealMethod();
|
||||
CompletableSubject source = CompletableSubject.create();
|
||||
Context context = Context.root().with(span);
|
||||
|
||||
Completable result =
|
||||
(Completable)
|
||||
underTestWithExperimentalAttributes.end(
|
||||
instrumenter, context, "request", source, String.class);
|
||||
TestObserver<Void> observer = result.test();
|
||||
|
||||
verifyNoInteractions(instrumenter);
|
||||
verify(span, never()).setAttribute(CANCELED_ATTRIBUTE_KEY, true);
|
||||
|
||||
observer.dispose();
|
||||
|
||||
verify(instrumenter).end(context, "request", null, null);
|
||||
verify(span).setAttribute(CANCELED_ATTRIBUTE_KEY, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
void endsSpanOnceForMultipleSubscribers() {
|
||||
CompletableSubject source = CompletableSubject.create();
|
||||
TestObserver<String> observer1 = new TestObserver<>();
|
||||
TestObserver<String> observer2 = new TestObserver<>();
|
||||
TestObserver<String> observer3 = new TestObserver<>();
|
||||
|
||||
Completable result =
|
||||
(Completable)
|
||||
underTest.end(instrumenter, Context.root(), "request", source, String.class);
|
||||
result.subscribe(observer1);
|
||||
result.subscribe(observer2);
|
||||
result.subscribe(observer3);
|
||||
|
||||
verifyNoInteractions(instrumenter);
|
||||
|
||||
source.onComplete();
|
||||
observer1.assertComplete();
|
||||
observer2.assertComplete();
|
||||
observer3.assertComplete();
|
||||
verify(instrumenter).end(Context.root(), "request", null, null);
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
class MaybeTest {
|
||||
@Test
|
||||
void supported() {
|
||||
assertThat(underTest.supports(Maybe.class)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void endsSpanOnAlreadyCompleted() {
|
||||
Maybe<?> result =
|
||||
(Maybe<?>)
|
||||
underTest.end(
|
||||
instrumenter, Context.root(), "request", Maybe.just("response"), String.class);
|
||||
TestObserver<?> observer = result.test();
|
||||
|
||||
observer.assertComplete();
|
||||
verify(instrumenter).end(Context.root(), "request", "response", null);
|
||||
}
|
||||
|
||||
@Test
|
||||
void endsSpanOnAlreadyEmpty() {
|
||||
Maybe<?> result =
|
||||
(Maybe<?>)
|
||||
underTest.end(instrumenter, Context.root(), "request", Maybe.empty(), String.class);
|
||||
TestObserver<?> observer = result.test();
|
||||
|
||||
observer.assertComplete();
|
||||
verify(instrumenter).end(Context.root(), "request", null, null);
|
||||
}
|
||||
|
||||
@Test
|
||||
void endsSpanOnAlreadyErrored() {
|
||||
IllegalStateException exception = new IllegalStateException();
|
||||
|
||||
Maybe<?> result =
|
||||
(Maybe<?>)
|
||||
underTest.end(
|
||||
instrumenter, Context.root(), "request", Maybe.error(exception), String.class);
|
||||
TestObserver<?> observer = result.test();
|
||||
|
||||
observer.assertError(exception);
|
||||
verify(instrumenter).end(Context.root(), "request", null, exception);
|
||||
}
|
||||
|
||||
@Test
|
||||
void endsSpanWhenCompleted() {
|
||||
MaybeSubject<String> source = MaybeSubject.create();
|
||||
|
||||
Maybe<?> result =
|
||||
(Maybe<?>) underTest.end(instrumenter, Context.root(), "request", source, String.class);
|
||||
TestObserver<?> observer = result.test();
|
||||
|
||||
verifyNoInteractions(instrumenter);
|
||||
|
||||
source.onSuccess("response");
|
||||
observer.assertComplete();
|
||||
verify(instrumenter).end(Context.root(), "request", "response", null);
|
||||
}
|
||||
|
||||
@Test
|
||||
void endsSpanWhenEmpty() {
|
||||
MaybeSubject<String> source = MaybeSubject.create();
|
||||
|
||||
Maybe<?> result =
|
||||
(Maybe<?>) underTest.end(instrumenter, Context.root(), "request", source, String.class);
|
||||
TestObserver<?> observer = result.test();
|
||||
|
||||
verifyNoInteractions(instrumenter);
|
||||
|
||||
source.onComplete();
|
||||
observer.assertComplete();
|
||||
verify(instrumenter).end(Context.root(), "request", null, null);
|
||||
}
|
||||
|
||||
@Test
|
||||
void endsSpanWhenErrored() {
|
||||
IllegalStateException exception = new IllegalStateException();
|
||||
MaybeSubject<String> source = MaybeSubject.create();
|
||||
|
||||
Maybe<?> result =
|
||||
(Maybe<?>) underTest.end(instrumenter, Context.root(), "request", source, String.class);
|
||||
TestObserver<?> observer = result.test();
|
||||
|
||||
source.onError(exception);
|
||||
observer.assertError(exception);
|
||||
verify(instrumenter).end(Context.root(), "request", null, exception);
|
||||
}
|
||||
|
||||
@Test
|
||||
void endsSpanWhenCancelled() {
|
||||
when(span.storeInContext(any())).thenCallRealMethod();
|
||||
MaybeSubject<String> source = MaybeSubject.create();
|
||||
Context context = Context.root().with(span);
|
||||
|
||||
Maybe<?> result =
|
||||
(Maybe<?>) underTest.end(instrumenter, context, "request", source, String.class);
|
||||
TestObserver<?> observer = result.test();
|
||||
|
||||
verifyNoInteractions(instrumenter);
|
||||
|
||||
observer.dispose();
|
||||
|
||||
verify(instrumenter).end(context, "request", null, null);
|
||||
verify(span, never()).setAttribute(CANCELED_ATTRIBUTE_KEY, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
void endsSpanWhenCancelledExperimentalAttributes() {
|
||||
when(span.storeInContext(any())).thenCallRealMethod();
|
||||
MaybeSubject<String> source = MaybeSubject.create();
|
||||
Context context = Context.root().with(span);
|
||||
|
||||
Maybe<?> result =
|
||||
(Maybe<?>)
|
||||
underTestWithExperimentalAttributes.end(
|
||||
instrumenter, context, "request", source, String.class);
|
||||
TestObserver<?> observer = result.test();
|
||||
|
||||
verifyNoInteractions(instrumenter);
|
||||
verify(span, never()).setAttribute(CANCELED_ATTRIBUTE_KEY, true);
|
||||
|
||||
observer.dispose();
|
||||
|
||||
verify(instrumenter).end(context, "request", null, null);
|
||||
verify(span).setAttribute(CANCELED_ATTRIBUTE_KEY, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
void endsSpanOnceForMultipleSubscribers() {
|
||||
MaybeSubject<String> source = MaybeSubject.create();
|
||||
|
||||
Maybe<?> result =
|
||||
(Maybe<?>) underTest.end(instrumenter, Context.root(), "request", source, String.class);
|
||||
TestObserver<?> observer1 = result.test();
|
||||
TestObserver<?> observer2 = result.test();
|
||||
TestObserver<?> observer3 = result.test();
|
||||
|
||||
verifyNoInteractions(instrumenter);
|
||||
|
||||
source.onSuccess("response");
|
||||
|
||||
observer1.assertComplete();
|
||||
observer1.assertValue(value -> value.equals("response"));
|
||||
observer2.assertComplete();
|
||||
observer2.assertValue(value -> value.equals("response"));
|
||||
observer3.assertComplete();
|
||||
observer3.assertValue(value -> value.equals("response"));
|
||||
verify(instrumenter).end(Context.root(), "request", "response", null);
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
class SingleTest {
|
||||
@Test
|
||||
void supported() {
|
||||
assertThat(underTest.supports(Single.class)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void endsSpanOnAlreadyCompleted() {
|
||||
Single<?> result =
|
||||
(Single<?>)
|
||||
underTest.end(
|
||||
instrumenter, Context.root(), "request", Single.just("response"), String.class);
|
||||
TestObserver<?> observer = result.test();
|
||||
|
||||
observer.assertComplete();
|
||||
verify(instrumenter).end(Context.root(), "request", "response", null);
|
||||
}
|
||||
|
||||
@Test
|
||||
void endsSpanOnAlreadyErrored() {
|
||||
IllegalStateException exception = new IllegalStateException();
|
||||
|
||||
Single<?> result =
|
||||
(Single<?>)
|
||||
underTest.end(
|
||||
instrumenter, Context.root(), "request", Single.error(exception), String.class);
|
||||
TestObserver<?> observer = result.test();
|
||||
|
||||
observer.assertError(exception);
|
||||
verify(instrumenter).end(Context.root(), "request", null, exception);
|
||||
}
|
||||
|
||||
@Test
|
||||
void endsSpanWhenCompleted() {
|
||||
SingleSubject<String> source = SingleSubject.create();
|
||||
|
||||
Single<?> result =
|
||||
(Single<?>) underTest.end(instrumenter, Context.root(), "request", source, String.class);
|
||||
TestObserver<?> observer = result.test();
|
||||
|
||||
verifyNoInteractions(instrumenter);
|
||||
|
||||
source.onSuccess("response");
|
||||
|
||||
observer.assertComplete();
|
||||
verify(instrumenter).end(Context.root(), "request", "response", null);
|
||||
}
|
||||
|
||||
@Test
|
||||
void endsSpanWhenErrored() {
|
||||
IllegalStateException exception = new IllegalStateException();
|
||||
SingleSubject<String> source = SingleSubject.create();
|
||||
|
||||
Single<?> result =
|
||||
(Single<?>) underTest.end(instrumenter, Context.root(), "request", source, String.class);
|
||||
TestObserver<?> observer = result.test();
|
||||
|
||||
verifyNoInteractions(instrumenter);
|
||||
|
||||
source.onError(exception);
|
||||
|
||||
observer.assertError(exception);
|
||||
verify(instrumenter).end(Context.root(), "request", null, exception);
|
||||
}
|
||||
|
||||
@Test
|
||||
void endsSpanWhenCancelled() {
|
||||
when(span.storeInContext(any())).thenCallRealMethod();
|
||||
SingleSubject<String> source = SingleSubject.create();
|
||||
Context context = Context.root().with(span);
|
||||
|
||||
Single<?> result =
|
||||
(Single<?>) underTest.end(instrumenter, context, "request", source, String.class);
|
||||
TestObserver<?> observer = result.test();
|
||||
|
||||
verifyNoInteractions(instrumenter);
|
||||
|
||||
observer.dispose();
|
||||
verify(span, never()).setAttribute(CANCELED_ATTRIBUTE_KEY, true);
|
||||
verify(instrumenter).end(context, "request", null, null);
|
||||
}
|
||||
|
||||
@Test
|
||||
void endsSpanWhenCancelledExperimentalAttributes() {
|
||||
when(span.storeInContext(any())).thenCallRealMethod();
|
||||
SingleSubject<String> source = SingleSubject.create();
|
||||
Context context = Context.root().with(span);
|
||||
|
||||
Single<?> result =
|
||||
(Single<?>)
|
||||
underTestWithExperimentalAttributes.end(
|
||||
instrumenter, context, "request", source, String.class);
|
||||
TestObserver<?> observer = result.test();
|
||||
|
||||
verifyNoInteractions(instrumenter);
|
||||
verify(span, never()).setAttribute(CANCELED_ATTRIBUTE_KEY, true);
|
||||
|
||||
observer.dispose();
|
||||
|
||||
verify(instrumenter).end(context, "request", null, null);
|
||||
verify(span).setAttribute(CANCELED_ATTRIBUTE_KEY, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
void endsSpanOnceForMultipleSubscribers() {
|
||||
SingleSubject<String> source = SingleSubject.create();
|
||||
|
||||
Single<?> result =
|
||||
(Single<?>) underTest.end(instrumenter, Context.root(), "request", source, String.class);
|
||||
TestObserver<?> observer1 = result.test();
|
||||
TestObserver<?> observer2 = result.test();
|
||||
TestObserver<?> observer3 = result.test();
|
||||
|
||||
verifyNoInteractions(instrumenter);
|
||||
|
||||
source.onSuccess("response");
|
||||
|
||||
observer1.assertValue(value -> value.equals("response"));
|
||||
observer1.assertComplete();
|
||||
observer2.assertValue(value -> value.equals("response"));
|
||||
observer2.assertComplete();
|
||||
observer3.assertValue(value -> value.equals("response"));
|
||||
observer3.assertComplete();
|
||||
verify(instrumenter).end(Context.root(), "request", "response", null);
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
class ObservableTest {
|
||||
@Test
|
||||
void supported() {
|
||||
assertThat(underTest.supports(Observable.class)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void endsSpanOnAlreadyCompleted() {
|
||||
Observable<?> result =
|
||||
(Observable<?>)
|
||||
underTest.end(
|
||||
instrumenter,
|
||||
Context.root(),
|
||||
"request",
|
||||
Observable.just("response"),
|
||||
String.class);
|
||||
TestObserver<?> observer = result.test();
|
||||
|
||||
verify(instrumenter).end(Context.root(), "request", null, null);
|
||||
observer.assertComplete();
|
||||
}
|
||||
|
||||
@Test
|
||||
void endsSpanOnAlreadyErrored() {
|
||||
IllegalStateException exception = new IllegalStateException();
|
||||
|
||||
Observable<?> result =
|
||||
(Observable<?>)
|
||||
underTest.end(
|
||||
instrumenter,
|
||||
Context.root(),
|
||||
"request",
|
||||
Observable.error(exception),
|
||||
String.class);
|
||||
TestObserver<?> observer = result.test();
|
||||
|
||||
verify(instrumenter).end(Context.root(), "request", null, exception);
|
||||
observer.assertError(exception);
|
||||
}
|
||||
|
||||
@Test
|
||||
void endsSpanWhenCompleted() {
|
||||
UnicastSubject<String> source = UnicastSubject.create();
|
||||
|
||||
Observable<?> result =
|
||||
(Observable<?>)
|
||||
underTest.end(instrumenter, Context.root(), "request", source, String.class);
|
||||
TestObserver<?> observer = result.test();
|
||||
|
||||
verifyNoInteractions(instrumenter);
|
||||
|
||||
source.onComplete();
|
||||
|
||||
verify(instrumenter).end(Context.root(), "request", null, null);
|
||||
observer.assertComplete();
|
||||
}
|
||||
|
||||
@Test
|
||||
void endsSpanWhenErrored() {
|
||||
IllegalStateException exception = new IllegalStateException();
|
||||
UnicastSubject<String> source = UnicastSubject.create();
|
||||
|
||||
Observable<?> result =
|
||||
(Observable<?>)
|
||||
underTest.end(instrumenter, Context.root(), "request", source, String.class);
|
||||
TestObserver<?> observer = result.test();
|
||||
|
||||
verifyNoInteractions(instrumenter);
|
||||
|
||||
source.onError(exception);
|
||||
|
||||
verify(instrumenter).end(Context.root(), "request", null, exception);
|
||||
observer.assertError(exception);
|
||||
}
|
||||
|
||||
@Test
|
||||
void endsOnWhenCancelled() {
|
||||
when(span.storeInContext(any())).thenCallRealMethod();
|
||||
UnicastSubject<String> source = UnicastSubject.create();
|
||||
Context context = Context.root().with(span);
|
||||
|
||||
Observable<?> result =
|
||||
(Observable<?>) underTest.end(instrumenter, context, "request", source, String.class);
|
||||
TestObserver<?> observer = result.test();
|
||||
|
||||
verifyNoInteractions(instrumenter);
|
||||
|
||||
observer.dispose();
|
||||
|
||||
verify(instrumenter).end(context, "request", null, null);
|
||||
verify(span, never()).setAttribute(CANCELED_ATTRIBUTE_KEY, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
void endsSpanWhenCancelledExperimentalAttributes() {
|
||||
when(span.storeInContext(any())).thenCallRealMethod();
|
||||
UnicastSubject<String> source = UnicastSubject.create();
|
||||
Context context = Context.root().with(span);
|
||||
|
||||
Observable<?> result =
|
||||
(Observable<?>)
|
||||
underTestWithExperimentalAttributes.end(
|
||||
instrumenter, context, "request", source, String.class);
|
||||
TestObserver<?> observer = result.test();
|
||||
|
||||
verifyNoInteractions(instrumenter);
|
||||
verify(span, never()).setAttribute(CANCELED_ATTRIBUTE_KEY, true);
|
||||
|
||||
observer.dispose();
|
||||
|
||||
verify(instrumenter).end(context, "request", null, null);
|
||||
verify(span).setAttribute(CANCELED_ATTRIBUTE_KEY, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
void endsSpanOnceForMultipleSubscribers() {
|
||||
ReplaySubject<String> source = ReplaySubject.create();
|
||||
|
||||
Observable<?> result =
|
||||
(Observable<?>)
|
||||
underTest.end(instrumenter, Context.root(), "request", source, String.class);
|
||||
TestObserver<?> observer1 = result.test();
|
||||
TestObserver<?> observer2 = result.test();
|
||||
TestObserver<?> observer3 = result.test();
|
||||
|
||||
verifyNoInteractions(instrumenter);
|
||||
|
||||
source.onComplete();
|
||||
|
||||
observer1.assertComplete();
|
||||
observer2.assertComplete();
|
||||
observer3.assertComplete();
|
||||
verify(instrumenter).end(Context.root(), "request", null, null);
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
class FlowableTest {
|
||||
@Test
|
||||
void supported() {
|
||||
assertThat(underTest.supports(Flowable.class)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void endsSpanOnAlreadyCompleted() {
|
||||
Flowable<?> result =
|
||||
(Flowable<?>)
|
||||
underTest.end(
|
||||
instrumenter, Context.root(), "request", Flowable.just("response"), String.class);
|
||||
TestSubscriber<?> observer = result.test();
|
||||
|
||||
verify(instrumenter).end(Context.root(), "request", null, null);
|
||||
observer.assertComplete();
|
||||
}
|
||||
|
||||
@Test
|
||||
void endsOnAlreadyErrored() {
|
||||
IllegalStateException exception = new IllegalStateException();
|
||||
|
||||
Flowable<?> result =
|
||||
(Flowable<?>)
|
||||
underTest.end(
|
||||
instrumenter, Context.root(), "request", Flowable.error(exception), String.class);
|
||||
TestSubscriber<?> observer = result.test();
|
||||
|
||||
verify(instrumenter).end(Context.root(), "request", null, exception);
|
||||
observer.assertError(exception);
|
||||
}
|
||||
|
||||
@Test
|
||||
void endsSpanWhenCompleted() {
|
||||
UnicastProcessor<String> source = UnicastProcessor.create();
|
||||
|
||||
Flowable<?> result =
|
||||
(Flowable<?>)
|
||||
underTest.end(instrumenter, Context.root(), "request", source, String.class);
|
||||
TestSubscriber<?> observer = result.test();
|
||||
|
||||
verifyNoInteractions(instrumenter);
|
||||
|
||||
source.onComplete();
|
||||
observer.assertComplete();
|
||||
verify(instrumenter).end(Context.root(), "request", null, null);
|
||||
}
|
||||
|
||||
@Test
|
||||
void endsOnWhenErrored() {
|
||||
IllegalStateException exception = new IllegalStateException();
|
||||
UnicastProcessor<String> source = UnicastProcessor.create();
|
||||
|
||||
Flowable<?> result =
|
||||
(Flowable<?>)
|
||||
underTest.end(instrumenter, Context.root(), "request", source, String.class);
|
||||
TestSubscriber<?> observer = result.test();
|
||||
|
||||
verifyNoInteractions(instrumenter);
|
||||
|
||||
source.onError(exception);
|
||||
|
||||
observer.assertError(exception);
|
||||
verify(instrumenter).end(Context.root(), "request", null, exception);
|
||||
}
|
||||
|
||||
@Test
|
||||
void endsSpanWhenCancelled() {
|
||||
when(span.storeInContext(any())).thenCallRealMethod();
|
||||
UnicastProcessor<String> source = UnicastProcessor.create();
|
||||
Context context = Context.root().with(span);
|
||||
|
||||
Flowable<?> result =
|
||||
(Flowable<?>) underTest.end(instrumenter, context, "request", source, String.class);
|
||||
TestSubscriber<?> observer = result.test();
|
||||
|
||||
verifyNoInteractions(instrumenter);
|
||||
|
||||
observer.cancel();
|
||||
|
||||
verify(span, never()).setAttribute(CANCELED_ATTRIBUTE_KEY, true);
|
||||
verify(instrumenter).end(context, "request", null, null);
|
||||
}
|
||||
|
||||
@Test
|
||||
void endsSpanWhenCancelledExperimentalAttributes() {
|
||||
when(span.storeInContext(any())).thenCallRealMethod();
|
||||
UnicastProcessor<String> source = UnicastProcessor.create();
|
||||
Context context = Context.root().with(span);
|
||||
|
||||
Flowable<?> result =
|
||||
(Flowable<?>)
|
||||
underTestWithExperimentalAttributes.end(
|
||||
instrumenter, context, "request", source, String.class);
|
||||
TestSubscriber<?> observer = result.test();
|
||||
|
||||
verifyNoInteractions(instrumenter);
|
||||
verify(span, never()).setAttribute(CANCELED_ATTRIBUTE_KEY, true);
|
||||
|
||||
observer.cancel();
|
||||
|
||||
verify(span).setAttribute(CANCELED_ATTRIBUTE_KEY, true);
|
||||
verify(instrumenter).end(context, "request", null, null);
|
||||
}
|
||||
|
||||
@Test
|
||||
void endsSpanOnceForMultipleSubscribers() {
|
||||
ReplayProcessor<String> source = ReplayProcessor.create();
|
||||
|
||||
Flowable<?> result =
|
||||
(Flowable<?>)
|
||||
underTest.end(instrumenter, Context.root(), "request", source, String.class);
|
||||
TestSubscriber<?> observer1 = result.test();
|
||||
TestSubscriber<?> observer2 = result.test();
|
||||
TestSubscriber<?> observer3 = result.test();
|
||||
|
||||
verifyNoInteractions(instrumenter);
|
||||
|
||||
source.onComplete();
|
||||
observer1.assertComplete();
|
||||
observer2.assertComplete();
|
||||
observer3.assertComplete();
|
||||
verify(instrumenter).end(Context.root(), "request", null, null);
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
class ParallelFlowableTest {
|
||||
@Test
|
||||
void supported() {
|
||||
assertThat(underTest.supports(ParallelFlowable.class)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void endsSpanOnAlreadyCompleted() {
|
||||
ParallelFlowable<?> result =
|
||||
(ParallelFlowable<?>)
|
||||
underTest.end(
|
||||
instrumenter,
|
||||
Context.root(),
|
||||
"request",
|
||||
Flowable.just("response").parallel(),
|
||||
String.class);
|
||||
TestSubscriber<?> observer = result.sequential().test();
|
||||
|
||||
observer.assertComplete();
|
||||
verify(instrumenter).end(Context.root(), "request", null, null);
|
||||
}
|
||||
|
||||
@Test
|
||||
void endsSpanOnAlreadyErrored() {
|
||||
IllegalStateException exception = new IllegalStateException();
|
||||
|
||||
ParallelFlowable<?> result =
|
||||
(ParallelFlowable<?>)
|
||||
underTest.end(
|
||||
instrumenter,
|
||||
Context.root(),
|
||||
"request",
|
||||
Flowable.error(exception).parallel(),
|
||||
String.class);
|
||||
TestSubscriber<?> observer = result.sequential().test();
|
||||
|
||||
observer.assertError(exception);
|
||||
verify(instrumenter).end(Context.root(), "request", null, exception);
|
||||
}
|
||||
|
||||
@Test
|
||||
void endsSpanWhenCompleted() {
|
||||
UnicastProcessor<String> source = UnicastProcessor.create();
|
||||
|
||||
ParallelFlowable<?> result =
|
||||
(ParallelFlowable<?>)
|
||||
underTest.end(
|
||||
instrumenter, Context.root(), "request", source.parallel(), String.class);
|
||||
TestSubscriber<?> observer = result.sequential().test();
|
||||
|
||||
verifyNoInteractions(instrumenter);
|
||||
|
||||
source.onComplete();
|
||||
|
||||
observer.assertComplete();
|
||||
verify(instrumenter).end(Context.root(), "request", null, null);
|
||||
}
|
||||
|
||||
@Test
|
||||
void endsSpanWhenErrored() {
|
||||
IllegalStateException exception = new IllegalStateException();
|
||||
UnicastProcessor<String> source = UnicastProcessor.create();
|
||||
|
||||
ParallelFlowable<?> result =
|
||||
(ParallelFlowable<?>)
|
||||
underTest.end(
|
||||
instrumenter, Context.root(), "request", source.parallel(), String.class);
|
||||
TestSubscriber<?> observer = result.sequential().test();
|
||||
|
||||
verifyNoInteractions(instrumenter);
|
||||
|
||||
source.onError(exception);
|
||||
|
||||
observer.assertError(exception);
|
||||
verify(instrumenter).end(Context.root(), "request", null, exception);
|
||||
}
|
||||
|
||||
@Test
|
||||
void endsSpanWhenCancelled() {
|
||||
when(span.storeInContext(any())).thenCallRealMethod();
|
||||
UnicastProcessor<String> source = UnicastProcessor.create();
|
||||
Context context = Context.root().with(span);
|
||||
|
||||
ParallelFlowable<?> result =
|
||||
(ParallelFlowable<?>)
|
||||
underTest.end(instrumenter, context, "request", source.parallel(), String.class);
|
||||
TestSubscriber<?> observer = result.sequential().test();
|
||||
|
||||
verifyNoInteractions(instrumenter);
|
||||
|
||||
observer.cancel();
|
||||
|
||||
verify(span, never()).setAttribute(CANCELED_ATTRIBUTE_KEY, true);
|
||||
verify(instrumenter).end(context, "request", null, null);
|
||||
}
|
||||
|
||||
@Test
|
||||
void endsSpanWhenCancelledExperimentalAttributes() {
|
||||
when(span.storeInContext(any())).thenCallRealMethod();
|
||||
UnicastProcessor<String> source = UnicastProcessor.create();
|
||||
Context context = Context.root().with(span);
|
||||
|
||||
ParallelFlowable<?> result =
|
||||
(ParallelFlowable<?>)
|
||||
underTestWithExperimentalAttributes.end(
|
||||
instrumenter, context, "request", source.parallel(), String.class);
|
||||
TestSubscriber<?> observer = result.sequential().test();
|
||||
|
||||
verifyNoInteractions(instrumenter);
|
||||
verify(span, never()).setAttribute(CANCELED_ATTRIBUTE_KEY, true);
|
||||
|
||||
observer.cancel();
|
||||
verify(span).setAttribute(CANCELED_ATTRIBUTE_KEY, true);
|
||||
verify(instrumenter).end(context, "request", null, null);
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
class PublisherTest {
|
||||
@Test
|
||||
void supported() {
|
||||
assertThat(underTest.supports(Publisher.class)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void endsSpanWhenCompleted() {
|
||||
CustomPublisher source = new CustomPublisher();
|
||||
|
||||
Flowable<?> result =
|
||||
(Flowable<?>)
|
||||
underTest.end(instrumenter, Context.root(), "request", source, String.class);
|
||||
TestSubscriber<?> observer = result.test();
|
||||
|
||||
verifyNoInteractions(instrumenter);
|
||||
|
||||
source.onComplete();
|
||||
|
||||
observer.assertComplete();
|
||||
verify(instrumenter).end(Context.root(), "request", null, null);
|
||||
}
|
||||
|
||||
@Test
|
||||
void endsSpanWhenErrored() {
|
||||
IllegalStateException exception = new IllegalStateException();
|
||||
CustomPublisher source = new CustomPublisher();
|
||||
|
||||
Flowable<?> result =
|
||||
(Flowable<?>)
|
||||
underTest.end(instrumenter, Context.root(), "request", source, String.class);
|
||||
TestSubscriber<?> observer = result.test();
|
||||
|
||||
verifyNoInteractions(instrumenter);
|
||||
|
||||
source.onError(exception);
|
||||
|
||||
observer.assertError(exception);
|
||||
verify(instrumenter).end(Context.root(), "request", null, exception);
|
||||
}
|
||||
|
||||
@Test
|
||||
void endsSpanWhenCancelled() {
|
||||
when(span.storeInContext(any())).thenCallRealMethod();
|
||||
CustomPublisher source = new CustomPublisher();
|
||||
Context context = Context.root().with(span);
|
||||
|
||||
Flowable<?> result =
|
||||
(Flowable<?>) underTest.end(instrumenter, context, "request", source, String.class);
|
||||
TestSubscriber<?> observer = result.test();
|
||||
|
||||
verifyNoInteractions(instrumenter);
|
||||
|
||||
observer.cancel();
|
||||
|
||||
verify(span, never()).setAttribute(CANCELED_ATTRIBUTE_KEY, true);
|
||||
verify(instrumenter).end(context, "request", null, null);
|
||||
}
|
||||
|
||||
@Test
|
||||
void endsSpanWhenCancelledExperimentalAttributes() {
|
||||
when(span.storeInContext(any())).thenCallRealMethod();
|
||||
CustomPublisher source = new CustomPublisher();
|
||||
Context context = Context.root().with(span);
|
||||
|
||||
Flowable<?> result =
|
||||
(Flowable<?>)
|
||||
underTestWithExperimentalAttributes.end(
|
||||
instrumenter, context, "request", source, String.class);
|
||||
TestSubscriber<?> observer = result.test();
|
||||
|
||||
verifyNoInteractions(instrumenter);
|
||||
verify(span, never()).setAttribute(CANCELED_ATTRIBUTE_KEY, true);
|
||||
|
||||
observer.cancel();
|
||||
|
||||
verify(span).setAttribute(CANCELED_ATTRIBUTE_KEY, true);
|
||||
verify(instrumenter).end(context, "request", null, null);
|
||||
}
|
||||
|
||||
class CustomPublisher implements Publisher<String>, Subscription {
|
||||
Subscriber<? super String> subscriber;
|
||||
|
||||
@Override
|
||||
public void subscribe(Subscriber<? super String> subscriber) {
|
||||
this.subscriber = subscriber;
|
||||
subscriber.onSubscribe(this);
|
||||
}
|
||||
|
||||
public void onComplete() {
|
||||
this.subscriber.onComplete();
|
||||
}
|
||||
|
||||
public void onError(Throwable exception) {
|
||||
this.subscriber.onError(exception);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void request(long l) {}
|
||||
|
||||
@Override
|
||||
public void cancel() {}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,59 +0,0 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.instrumentation.rxjava.v3.common
|
||||
|
||||
import io.opentelemetry.api.GlobalOpenTelemetry
|
||||
import io.opentelemetry.api.trace.SpanKind
|
||||
import io.opentelemetry.instrumentation.test.InstrumentationSpecification
|
||||
import io.reactivex.rxjava3.core.Single
|
||||
import io.reactivex.rxjava3.functions.Consumer
|
||||
|
||||
import java.util.concurrent.CountDownLatch
|
||||
|
||||
abstract class AbstractRxJava3SubscriptionTest extends InstrumentationSpecification {
|
||||
|
||||
def "subscription test"() {
|
||||
when:
|
||||
CountDownLatch latch = new CountDownLatch(1)
|
||||
runWithSpan("parent") {
|
||||
Single<Connection> connection = Single.create {
|
||||
it.onSuccess(new Connection())
|
||||
}
|
||||
connection.subscribe(new Consumer<Connection>() {
|
||||
@Override
|
||||
void accept(Connection t) {
|
||||
t.query()
|
||||
latch.countDown()
|
||||
}
|
||||
})
|
||||
}
|
||||
latch.await()
|
||||
|
||||
then:
|
||||
assertTraces(1) {
|
||||
trace(0, 2) {
|
||||
span(0) {
|
||||
name "parent"
|
||||
kind SpanKind.INTERNAL
|
||||
hasNoParent()
|
||||
}
|
||||
span(1) {
|
||||
name "Connection.query"
|
||||
kind SpanKind.INTERNAL
|
||||
childOf span(0)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static class Connection {
|
||||
static int query() {
|
||||
def span = GlobalOpenTelemetry.getTracer("test").spanBuilder("Connection.query").startSpan()
|
||||
span.end()
|
||||
return new Random().nextInt()
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,491 +0,0 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.instrumentation.rxjava.v3.common
|
||||
|
||||
import com.google.common.collect.Lists
|
||||
import io.opentelemetry.api.common.AttributeKey
|
||||
import io.opentelemetry.api.trace.Span
|
||||
import io.opentelemetry.api.trace.SpanKind
|
||||
import io.opentelemetry.instrumentation.test.InstrumentationSpecification
|
||||
import io.reactivex.rxjava3.core.BackpressureStrategy
|
||||
import io.reactivex.rxjava3.core.Completable
|
||||
import io.reactivex.rxjava3.core.Flowable
|
||||
import io.reactivex.rxjava3.core.Maybe
|
||||
import io.reactivex.rxjava3.core.Observable
|
||||
import io.reactivex.rxjava3.core.Single
|
||||
import io.reactivex.rxjava3.internal.operators.flowable.FlowablePublish
|
||||
import io.reactivex.rxjava3.internal.operators.observable.ObservablePublish
|
||||
import io.reactivex.rxjava3.schedulers.Schedulers
|
||||
import org.reactivestreams.Subscriber
|
||||
import org.reactivestreams.Subscription
|
||||
import spock.lang.Shared
|
||||
import spock.lang.Unroll
|
||||
|
||||
import static java.util.concurrent.TimeUnit.MILLISECONDS
|
||||
|
||||
/**
|
||||
* <p>Tests in this class may seem not exhaustive due to the fact that some classes are converted
|
||||
* into others, ie. {@link Completable#toMaybe()}. Fortunately, RxJava3 uses helper classes like
|
||||
* {@link io.reactivex.rxjava3.internal.operators.maybe.MaybeFromCompletable} and as a result we
|
||||
* can test subscriptions and cancellations correctly.
|
||||
*/
|
||||
@Unroll
|
||||
abstract class AbstractRxJava3Test extends InstrumentationSpecification {
|
||||
|
||||
public static final String EXCEPTION_MESSAGE = "test exception"
|
||||
|
||||
@Shared
|
||||
def addOne = { i ->
|
||||
addOneFunc(i)
|
||||
}
|
||||
|
||||
@Shared
|
||||
def addTwo = { i ->
|
||||
addTwoFunc(i)
|
||||
}
|
||||
|
||||
@Shared
|
||||
def throwException = {
|
||||
throw new IllegalStateException(EXCEPTION_MESSAGE)
|
||||
}
|
||||
|
||||
def addOneFunc(int i) {
|
||||
runWithSpan("addOne") {
|
||||
return i + 1
|
||||
}
|
||||
}
|
||||
|
||||
def addTwoFunc(int i) {
|
||||
runWithSpan("addTwo") {
|
||||
return i + 2
|
||||
}
|
||||
}
|
||||
|
||||
def "Publisher '#testName' test"() {
|
||||
when:
|
||||
def result = assemblePublisherUnderTrace(publisherSupplier)
|
||||
|
||||
then:
|
||||
result == expected
|
||||
and:
|
||||
assertTraces(1) {
|
||||
sortSpansByStartTime()
|
||||
trace(0, workSpans + 1) {
|
||||
|
||||
span(0) {
|
||||
name "publisher-parent"
|
||||
kind SpanKind.INTERNAL
|
||||
hasNoParent()
|
||||
}
|
||||
for (int i = 1; i < workSpans + 1; ++i) {
|
||||
span(i) {
|
||||
name "addOne"
|
||||
kind SpanKind.INTERNAL
|
||||
childOf span(0)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
where:
|
||||
testName | expected | workSpans | publisherSupplier
|
||||
"basic maybe" | 2 | 1 | { -> Maybe.just(1).map(addOne) }
|
||||
"two operations maybe" | 4 | 2 | { -> Maybe.just(2).map(addOne).map(addOne) }
|
||||
"delayed maybe" | 4 | 1 | { ->
|
||||
Maybe.just(3).delay(100, MILLISECONDS).map(addOne)
|
||||
}
|
||||
"delayed twice maybe" | 6 | 2 | { ->
|
||||
Maybe.just(4).delay(100, MILLISECONDS).map(addOne).delay(100, MILLISECONDS).map(addOne)
|
||||
}
|
||||
"basic flowable" | [6, 7] | 2 | { ->
|
||||
Flowable.fromIterable([5, 6]).map(addOne)
|
||||
}
|
||||
"two operations flowable" | [8, 9] | 4 | { ->
|
||||
Flowable.fromIterable([6, 7]).map(addOne).map(addOne)
|
||||
}
|
||||
"delayed flowable" | [8, 9] | 2 | { ->
|
||||
Flowable.fromIterable([7, 8]).delay(100, MILLISECONDS).map(addOne)
|
||||
}
|
||||
"delayed twice flowable" | [10, 11] | 4 | { ->
|
||||
Flowable.fromIterable([8, 9]).delay(100, MILLISECONDS).map(addOne).delay(100, MILLISECONDS).map(addOne)
|
||||
}
|
||||
"maybe from callable" | 12 | 2 | { ->
|
||||
Maybe.fromCallable({ addOneFunc(10) }).map(addOne)
|
||||
}
|
||||
"basic single" | 1 | 1 | { -> Single.just(0).map(addOne) }
|
||||
"basic observable" | [1] | 1 | { -> Observable.just(0).map(addOne) }
|
||||
"connectable flowable" | [1] | 1 | { ->
|
||||
FlowablePublish.just(0).delay(100, MILLISECONDS).map(addOne)
|
||||
}
|
||||
"connectable observable" | [1] | 1 | { ->
|
||||
ObservablePublish.just(0).delay(100, MILLISECONDS).map(addOne)
|
||||
}
|
||||
}
|
||||
|
||||
def "Publisher error '#testName' test"() {
|
||||
when:
|
||||
assemblePublisherUnderTrace(publisherSupplier)
|
||||
|
||||
then:
|
||||
def thrownException = thrown RuntimeException
|
||||
thrownException.message == EXCEPTION_MESSAGE
|
||||
and:
|
||||
assertTraces(1) {
|
||||
sortSpansByStartTime()
|
||||
trace(0, 1) {
|
||||
// It's important that we don't attach errors at the Reactor level so that we don't
|
||||
// impact the spans on reactor integrations such as netty and lettuce, as reactor is
|
||||
// more of a context propagation mechanism than something we would be tracking for
|
||||
// errors this is ok.
|
||||
span(0) {
|
||||
name "publisher-parent"
|
||||
kind SpanKind.INTERNAL
|
||||
hasNoParent()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
where:
|
||||
testName | publisherSupplier
|
||||
"maybe" | { -> Maybe.error(new RuntimeException(EXCEPTION_MESSAGE)) }
|
||||
"flowable" | { -> Flowable.error(new RuntimeException(EXCEPTION_MESSAGE)) }
|
||||
"single" | { -> Single.error(new RuntimeException(EXCEPTION_MESSAGE)) }
|
||||
"observable" | { -> Observable.error(new RuntimeException(EXCEPTION_MESSAGE)) }
|
||||
"completable" | { -> Completable.error(new RuntimeException(EXCEPTION_MESSAGE)) }
|
||||
}
|
||||
|
||||
def "Publisher step '#testName' test"() {
|
||||
when:
|
||||
assemblePublisherUnderTrace(publisherSupplier)
|
||||
|
||||
then:
|
||||
def exception = thrown RuntimeException
|
||||
exception.message == EXCEPTION_MESSAGE
|
||||
and:
|
||||
assertTraces(1) {
|
||||
sortSpansByStartTime()
|
||||
trace(0, workSpans + 1) {
|
||||
// It's important that we don't attach errors at the Reactor level so that we don't
|
||||
// impact the spans on reactor integrations such as netty and lettuce, as reactor is
|
||||
// more of a context propagation mechanism than something we would be tracking for
|
||||
// errors this is ok.
|
||||
span(0) {
|
||||
name "publisher-parent"
|
||||
kind SpanKind.INTERNAL
|
||||
hasNoParent()
|
||||
}
|
||||
|
||||
for (int i = 1; i < workSpans + 1; i++) {
|
||||
span(i) {
|
||||
name "addOne"
|
||||
kind SpanKind.INTERNAL
|
||||
childOf span(0)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
where:
|
||||
testName | workSpans | publisherSupplier
|
||||
"basic maybe failure" | 1 | { ->
|
||||
Maybe.just(1).map(addOne).map({ throwException() })
|
||||
}
|
||||
"basic flowable failure" | 1 | { ->
|
||||
Flowable.fromIterable([5, 6]).map(addOne).map({ throwException() })
|
||||
}
|
||||
}
|
||||
|
||||
def "Publisher '#testName' cancel"() {
|
||||
when:
|
||||
cancelUnderTrace(publisherSupplier)
|
||||
|
||||
then:
|
||||
assertTraces(1) {
|
||||
trace(0, 1) {
|
||||
span(0) {
|
||||
name "publisher-parent"
|
||||
kind SpanKind.INTERNAL
|
||||
hasNoParent()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
where:
|
||||
testName | publisherSupplier
|
||||
"basic maybe" | { -> Maybe.just(1) }
|
||||
"basic flowable" | { -> Flowable.fromIterable([5, 6]) }
|
||||
"basic single" | { -> Single.just(1) }
|
||||
"basic completable" | { -> Completable.fromCallable({ -> 1 }) }
|
||||
"basic observable" | { -> Observable.just(1) }
|
||||
}
|
||||
|
||||
def "Publisher chain spans have the correct parent for '#testName'"() {
|
||||
when:
|
||||
assemblePublisherUnderTrace(publisherSupplier)
|
||||
|
||||
then:
|
||||
assertTraces(1) {
|
||||
trace(0, workSpans + 1) {
|
||||
span(0) {
|
||||
name "publisher-parent"
|
||||
kind SpanKind.INTERNAL
|
||||
hasNoParent()
|
||||
}
|
||||
|
||||
for (int i = 1; i < workSpans + 1; i++) {
|
||||
span(i) {
|
||||
name "addOne"
|
||||
kind SpanKind.INTERNAL
|
||||
childOf span(0)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
where:
|
||||
testName | workSpans | publisherSupplier
|
||||
"basic maybe" | 3 | { ->
|
||||
Maybe.just(1).map(addOne).map(addOne).concatWith(Maybe.just(1).map(addOne))
|
||||
}
|
||||
"basic flowable" | 5 | { ->
|
||||
Flowable.fromIterable([5, 6]).map(addOne).map(addOne).concatWith(Maybe.just(1).map(addOne).toFlowable())
|
||||
}
|
||||
}
|
||||
|
||||
def "Publisher chain spans have the correct parents from subscription time"() {
|
||||
when:
|
||||
def maybe = Maybe.just(42)
|
||||
.map(addOne)
|
||||
.map(addTwo)
|
||||
|
||||
runWithSpan("trace-parent") {
|
||||
maybe.blockingGet()
|
||||
}
|
||||
|
||||
then:
|
||||
assertTraces(1) {
|
||||
trace(0, 3) {
|
||||
sortSpansByStartTime()
|
||||
span(0) {
|
||||
name "trace-parent"
|
||||
kind SpanKind.INTERNAL
|
||||
hasNoParent()
|
||||
}
|
||||
span(1) {
|
||||
name "addOne"
|
||||
kind SpanKind.INTERNAL
|
||||
childOf span(0)
|
||||
}
|
||||
span(2) {
|
||||
name "addTwo"
|
||||
kind SpanKind.INTERNAL
|
||||
childOf span(0)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def "Publisher chain spans have the correct parents from subscription time '#testName'"() {
|
||||
when:
|
||||
assemblePublisherUnderTrace {
|
||||
// The "add one" operations in the publisher created here should be children of the publisher-parent
|
||||
def publisher = publisherSupplier()
|
||||
|
||||
runWithSpan("intermediate") {
|
||||
if (publisher instanceof Maybe) {
|
||||
return ((Maybe) publisher).map(addTwo)
|
||||
} else if (publisher instanceof Flowable) {
|
||||
return ((Flowable) publisher).map(addTwo)
|
||||
} else if (publisher instanceof Single) {
|
||||
return ((Single) publisher).map(addTwo)
|
||||
} else if (publisher instanceof Observable) {
|
||||
return ((Observable) publisher).map(addTwo)
|
||||
} else if (publisher instanceof Completable) {
|
||||
return ((Completable) publisher).toMaybe().map(addTwo)
|
||||
}
|
||||
throw new IllegalStateException("Unknown publisher type")
|
||||
}
|
||||
}
|
||||
|
||||
then:
|
||||
assertTraces(1) {
|
||||
trace(0, 2 + 2 * workItems) {
|
||||
sortSpansByStartTime()
|
||||
span(0) {
|
||||
name "publisher-parent"
|
||||
kind SpanKind.INTERNAL
|
||||
hasNoParent()
|
||||
}
|
||||
span(1) {
|
||||
name "intermediate"
|
||||
kind SpanKind.INTERNAL
|
||||
childOf span(0)
|
||||
}
|
||||
|
||||
for (int i = 2; i < 2 + 2 * workItems; i = i + 2) {
|
||||
span(i) {
|
||||
name "addOne"
|
||||
kind SpanKind.INTERNAL
|
||||
childOf span(0)
|
||||
}
|
||||
span(i + 1) {
|
||||
name "addTwo"
|
||||
kind SpanKind.INTERNAL
|
||||
childOf span(0)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
where:
|
||||
testName | workItems | publisherSupplier
|
||||
"basic maybe" | 1 | { -> Maybe.just(1).map(addOne) }
|
||||
"basic flowable" | 2 | { -> Flowable.fromIterable([1, 2]).map(addOne) }
|
||||
"basic single" | 1 | { -> Single.just(1).map(addOne) }
|
||||
"basic observable" | 1 | { -> Observable.just(1).map(addOne) }
|
||||
}
|
||||
|
||||
def "Flowables produce the right number of results '#scheduler'"() {
|
||||
when:
|
||||
List<String> values = runWithSpan("flowable root") {
|
||||
Flowable.fromIterable([1, 2, 3, 4])
|
||||
.parallel()
|
||||
.runOn(scheduler)
|
||||
.flatMap({ num ->
|
||||
Maybe.just(num).map(addOne).toFlowable()
|
||||
})
|
||||
.sequential()
|
||||
.toList()
|
||||
.blockingGet()
|
||||
}
|
||||
|
||||
then:
|
||||
values.size() == 4
|
||||
assertTraces(1) {
|
||||
trace(0, 5) {
|
||||
span(0) {
|
||||
name "flowable root"
|
||||
kind SpanKind.INTERNAL
|
||||
hasNoParent()
|
||||
}
|
||||
for (int i = 1; i < values.size() + 1; i++) {
|
||||
span(i) {
|
||||
name "addOne"
|
||||
kind SpanKind.INTERNAL
|
||||
childOf span(0)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
where:
|
||||
scheduler << [Schedulers.newThread(), Schedulers.computation(), Schedulers.single(), Schedulers.trampoline()]
|
||||
}
|
||||
|
||||
def "test many ongoing trace chains on '#scheduler'"() {
|
||||
setup:
|
||||
int iterations = 100
|
||||
Set<Long> remainingIterations = new HashSet<>((0L..(iterations - 1)).toList())
|
||||
|
||||
when:
|
||||
RxJava3ConcurrencyTestHelper.launchAndWait(scheduler, iterations, 60000, testRunner())
|
||||
|
||||
then:
|
||||
assertTraces(iterations) {
|
||||
for (int i = 0; i < iterations; i++) {
|
||||
trace(i, 3) {
|
||||
long iteration = -1
|
||||
span(0) {
|
||||
name("outer")
|
||||
iteration = span.getAttributes().get(AttributeKey.longKey("iteration")).toLong()
|
||||
assert remainingIterations.remove(iteration)
|
||||
}
|
||||
span(1) {
|
||||
name("middle")
|
||||
childOf(span(0))
|
||||
assert span.getAttributes().get(AttributeKey.longKey("iteration")) == iteration
|
||||
}
|
||||
span(2) {
|
||||
name("inner")
|
||||
childOf(span(1))
|
||||
assert span.getAttributes().get(AttributeKey.longKey("iteration")) == iteration
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
assert remainingIterations.isEmpty()
|
||||
|
||||
where:
|
||||
scheduler << [Schedulers.newThread(), Schedulers.computation(), Schedulers.single(), Schedulers.trampoline()]
|
||||
}
|
||||
|
||||
def cancelUnderTrace(def publisherSupplier) {
|
||||
runUnderTraceWithoutExceptionCatch("publisher-parent") {
|
||||
def publisher = publisherSupplier()
|
||||
if (publisher instanceof Maybe) {
|
||||
publisher = publisher.toFlowable()
|
||||
} else if (publisher instanceof Single) {
|
||||
publisher = publisher.toFlowable()
|
||||
} else if (publisher instanceof Completable) {
|
||||
publisher = publisher.toFlowable()
|
||||
} else if (publisher instanceof Observable) {
|
||||
publisher = publisher.toFlowable(BackpressureStrategy.LATEST)
|
||||
}
|
||||
|
||||
publisher.subscribe(new Subscriber<Integer>() {
|
||||
void onSubscribe(Subscription subscription) {
|
||||
subscription.cancel()
|
||||
}
|
||||
|
||||
void onNext(Integer t) {
|
||||
}
|
||||
|
||||
void onError(Throwable error) {
|
||||
}
|
||||
|
||||
void onComplete() {
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
def assemblePublisherUnderTrace(def publisherSupplier) {
|
||||
// The "add two" operations below should be children of this span
|
||||
runUnderTraceWithoutExceptionCatch("publisher-parent") {
|
||||
def publisher = publisherSupplier()
|
||||
|
||||
// Read all data from publisher
|
||||
if (publisher instanceof Maybe) {
|
||||
return ((Maybe) publisher).blockingGet()
|
||||
} else if (publisher instanceof Flowable) {
|
||||
return Lists.newArrayList(((Flowable) publisher).blockingIterable())
|
||||
} else if (publisher instanceof Single) {
|
||||
return ((Single) publisher).blockingGet()
|
||||
} else if (publisher instanceof Observable) {
|
||||
return Lists.newArrayList(((Observable) publisher).blockingIterable())
|
||||
} else if (publisher instanceof Completable) {
|
||||
return ((Completable) publisher).toMaybe().blockingGet()
|
||||
}
|
||||
|
||||
throw new IllegalStateException("Unknown publisher: " + publisher)
|
||||
}
|
||||
}
|
||||
|
||||
def runUnderTraceWithoutExceptionCatch(String spanName, Closure c) {
|
||||
Span span = openTelemetry.getTracer("test")
|
||||
.spanBuilder(spanName)
|
||||
.startSpan()
|
||||
try {
|
||||
return span.makeCurrent().withCloseable {
|
||||
c.call()
|
||||
}
|
||||
} finally {
|
||||
span.end()
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.instrumentation.rxjava.v3.common;
|
||||
|
||||
import io.opentelemetry.api.GlobalOpenTelemetry;
|
||||
import io.opentelemetry.api.trace.Span;
|
||||
import io.opentelemetry.api.trace.SpanKind;
|
||||
import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension;
|
||||
import io.reactivex.rxjava3.core.Single;
|
||||
import io.reactivex.rxjava3.disposables.Disposable;
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
public abstract class AbstractRxJava3SubscriptionTest {
|
||||
|
||||
protected abstract InstrumentationExtension testing();
|
||||
|
||||
@Test
|
||||
public void subscriptionTest() throws InterruptedException {
|
||||
CountDownLatch countDownLatch = new CountDownLatch(1);
|
||||
testing()
|
||||
.runWithSpan(
|
||||
"parent",
|
||||
() -> {
|
||||
Single<Connection> connectionSingle =
|
||||
Single.create(emitter -> emitter.onSuccess(new Connection()));
|
||||
Disposable unused =
|
||||
connectionSingle.subscribe(
|
||||
connection -> {
|
||||
connection.query();
|
||||
countDownLatch.countDown();
|
||||
});
|
||||
});
|
||||
countDownLatch.await();
|
||||
testing()
|
||||
.waitAndAssertTraces(
|
||||
trace ->
|
||||
trace.hasSpansSatisfyingExactly(
|
||||
span -> span.hasName("parent").hasKind(SpanKind.INTERNAL).hasNoParent(),
|
||||
span ->
|
||||
span.hasName("Connection.query")
|
||||
.hasKind(SpanKind.INTERNAL)
|
||||
.hasParent(trace.getSpan(0))));
|
||||
}
|
||||
|
||||
static class Connection {
|
||||
int query() {
|
||||
Span span = GlobalOpenTelemetry.getTracer("test").spanBuilder("Connection.query").startSpan();
|
||||
span.end();
|
||||
return new Random().nextInt();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,856 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.instrumentation.rxjava.v3.common;
|
||||
|
||||
import static io.opentelemetry.sdk.testing.assertj.LogAssertions.assertThat;
|
||||
import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.attributeEntry;
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
|
||||
import com.google.common.primitives.Ints;
|
||||
import io.opentelemetry.api.common.AttributeKey;
|
||||
import io.opentelemetry.api.trace.SpanKind;
|
||||
import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension;
|
||||
import io.opentelemetry.instrumentation.testing.util.ThrowingRunnable;
|
||||
import io.opentelemetry.instrumentation.testing.util.ThrowingSupplier;
|
||||
import io.opentelemetry.sdk.testing.assertj.TraceAssert;
|
||||
import io.reactivex.rxjava3.core.BackpressureStrategy;
|
||||
import io.reactivex.rxjava3.core.Completable;
|
||||
import io.reactivex.rxjava3.core.Flowable;
|
||||
import io.reactivex.rxjava3.core.Maybe;
|
||||
import io.reactivex.rxjava3.core.Observable;
|
||||
import io.reactivex.rxjava3.core.Scheduler;
|
||||
import io.reactivex.rxjava3.core.Single;
|
||||
import io.reactivex.rxjava3.internal.operators.flowable.FlowablePublish;
|
||||
import io.reactivex.rxjava3.internal.operators.observable.ObservablePublish;
|
||||
import io.reactivex.rxjava3.schedulers.Schedulers;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.stream.Stream;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.TestInstance;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
import org.reactivestreams.Subscriber;
|
||||
import org.reactivestreams.Subscription;
|
||||
|
||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||
public abstract class AbstractRxJava3Test {
|
||||
private static final String EXCEPTION_MESSAGE = "test exception";
|
||||
private static final String PARENT = "publisher-parent";
|
||||
private static final String ADD_ONE = "addOne";
|
||||
private static final String ADD_TWO = "addTwo";
|
||||
|
||||
protected abstract InstrumentationExtension testing();
|
||||
|
||||
private static Stream<Arguments> schedulers() {
|
||||
return Stream.of(
|
||||
Arguments.of(Schedulers.newThread()),
|
||||
Arguments.of(Schedulers.computation()),
|
||||
Arguments.of(Schedulers.single()),
|
||||
Arguments.of(Schedulers.trampoline()));
|
||||
}
|
||||
|
||||
private int addOne(int i) {
|
||||
return testing().runWithSpan(ADD_ONE, () -> i + 1);
|
||||
}
|
||||
|
||||
private int addTwo(int i) {
|
||||
return testing().runWithSpan(ADD_TWO, () -> i + 2);
|
||||
}
|
||||
|
||||
private <T> T createParentSpan(ThrowingSupplier<T, RuntimeException> test) {
|
||||
return testing().runWithSpan(PARENT, test);
|
||||
}
|
||||
|
||||
private void createParentSpan(ThrowingRunnable<RuntimeException> test) {
|
||||
testing().runWithSpan(PARENT, test);
|
||||
}
|
||||
|
||||
private enum CancellingSubscriber implements Subscriber<Object> {
|
||||
INSTANCE;
|
||||
|
||||
@Override
|
||||
public void onSubscribe(Subscription subscription) {
|
||||
subscription.cancel();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNext(Object o) {}
|
||||
|
||||
@Override
|
||||
public void onError(Throwable throwable) {}
|
||||
|
||||
@Override
|
||||
public void onComplete() {}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void basicMaybe() {
|
||||
int result = createParentSpan(() -> Maybe.just(1).map(this::addOne).blockingGet());
|
||||
assertThat(result).isEqualTo(2);
|
||||
testing()
|
||||
.waitAndAssertTraces(
|
||||
trace ->
|
||||
trace.hasSpansSatisfyingExactly(
|
||||
span -> span.hasName(PARENT).hasKind(SpanKind.INTERNAL).hasNoParent(),
|
||||
span ->
|
||||
span.hasName(ADD_ONE)
|
||||
.hasKind(SpanKind.INTERNAL)
|
||||
.hasParent(trace.getSpan(0))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void twoOperationsMaybe() {
|
||||
int result =
|
||||
createParentSpan(() -> Maybe.just(2).map(this::addOne).map(this::addOne).blockingGet());
|
||||
assertThat(result).isEqualTo(4);
|
||||
testing()
|
||||
.waitAndAssertTraces(
|
||||
trace ->
|
||||
trace.hasSpansSatisfyingExactly(
|
||||
span -> span.hasName(PARENT).hasKind(SpanKind.INTERNAL).hasNoParent(),
|
||||
span ->
|
||||
span.hasName(ADD_ONE)
|
||||
.hasKind(SpanKind.INTERNAL)
|
||||
.hasParent(trace.getSpan(0)),
|
||||
span ->
|
||||
span.hasName(ADD_ONE)
|
||||
.hasKind(SpanKind.INTERNAL)
|
||||
.hasParent(trace.getSpan(0))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void delayedMaybe() {
|
||||
int result =
|
||||
createParentSpan(
|
||||
() -> Maybe.just(3).delay(100, TimeUnit.MILLISECONDS).map(this::addOne).blockingGet());
|
||||
assertThat(result).isEqualTo(4);
|
||||
testing()
|
||||
.waitAndAssertTraces(
|
||||
trace ->
|
||||
trace.hasSpansSatisfyingExactly(
|
||||
span -> span.hasName(PARENT).hasKind(SpanKind.INTERNAL).hasNoParent(),
|
||||
span ->
|
||||
span.hasName(ADD_ONE)
|
||||
.hasKind(SpanKind.INTERNAL)
|
||||
.hasParent(trace.getSpan(0))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void delayedTwiceMaybe() {
|
||||
int result =
|
||||
createParentSpan(
|
||||
() ->
|
||||
Maybe.just(4)
|
||||
.delay(100, TimeUnit.MILLISECONDS)
|
||||
.map(this::addOne)
|
||||
.delay(100, TimeUnit.MILLISECONDS)
|
||||
.map(this::addOne)
|
||||
.blockingGet());
|
||||
assertThat(result).isEqualTo(6);
|
||||
testing()
|
||||
.waitAndAssertTraces(
|
||||
trace ->
|
||||
trace.hasSpansSatisfyingExactly(
|
||||
span -> span.hasName(PARENT).hasKind(SpanKind.INTERNAL).hasNoParent(),
|
||||
span ->
|
||||
span.hasName(ADD_ONE)
|
||||
.hasKind(SpanKind.INTERNAL)
|
||||
.hasParent(trace.getSpan(0)),
|
||||
span ->
|
||||
span.hasName(ADD_ONE)
|
||||
.hasKind(SpanKind.INTERNAL)
|
||||
.hasParent(trace.getSpan(0))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void basicFlowable() {
|
||||
Iterable<Integer> result =
|
||||
createParentSpan(
|
||||
() ->
|
||||
Flowable.fromIterable(Ints.asList(5, 6)).map(this::addOne).toList().blockingGet());
|
||||
assertThat(result).contains(6, 7);
|
||||
testing()
|
||||
.waitAndAssertTraces(
|
||||
trace ->
|
||||
trace.hasSpansSatisfyingExactly(
|
||||
span -> span.hasName(PARENT).hasKind(SpanKind.INTERNAL).hasNoParent(),
|
||||
span ->
|
||||
span.hasName(ADD_ONE)
|
||||
.hasKind(SpanKind.INTERNAL)
|
||||
.hasParent(trace.getSpan(0)),
|
||||
span ->
|
||||
span.hasName(ADD_ONE)
|
||||
.hasKind(SpanKind.INTERNAL)
|
||||
.hasParent(trace.getSpan(0))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void twoOperationsFlowable() {
|
||||
List<Integer> result =
|
||||
createParentSpan(
|
||||
() ->
|
||||
Flowable.fromIterable(Ints.asList(6, 7))
|
||||
.map(this::addOne)
|
||||
.map(this::addOne)
|
||||
.toList()
|
||||
.blockingGet());
|
||||
assertThat(result).contains(8, 9);
|
||||
testing()
|
||||
.waitAndAssertTraces(
|
||||
trace ->
|
||||
trace.hasSpansSatisfyingExactly(
|
||||
span -> span.hasName(PARENT).hasKind(SpanKind.INTERNAL).hasNoParent(),
|
||||
span ->
|
||||
span.hasName(ADD_ONE)
|
||||
.hasKind(SpanKind.INTERNAL)
|
||||
.hasParent(trace.getSpan(0)),
|
||||
span ->
|
||||
span.hasName(ADD_ONE)
|
||||
.hasKind(SpanKind.INTERNAL)
|
||||
.hasParent(trace.getSpan(0)),
|
||||
span ->
|
||||
span.hasName(ADD_ONE)
|
||||
.hasKind(SpanKind.INTERNAL)
|
||||
.hasParent(trace.getSpan(0)),
|
||||
span ->
|
||||
span.hasName(ADD_ONE)
|
||||
.hasKind(SpanKind.INTERNAL)
|
||||
.hasParent(trace.getSpan(0))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void delayedFlowable() {
|
||||
List<Integer> result =
|
||||
createParentSpan(
|
||||
() ->
|
||||
Flowable.fromIterable(Ints.asList(7, 8))
|
||||
.delay(100, TimeUnit.MILLISECONDS)
|
||||
.map(this::addOne)
|
||||
.toList()
|
||||
.blockingGet());
|
||||
assertThat(result).contains(8, 9);
|
||||
testing()
|
||||
.waitAndAssertTraces(
|
||||
trace ->
|
||||
trace.hasSpansSatisfyingExactly(
|
||||
span -> span.hasName(PARENT).hasKind(SpanKind.INTERNAL).hasNoParent(),
|
||||
span ->
|
||||
span.hasName(ADD_ONE)
|
||||
.hasKind(SpanKind.INTERNAL)
|
||||
.hasParent(trace.getSpan(0)),
|
||||
span ->
|
||||
span.hasName(ADD_ONE)
|
||||
.hasKind(SpanKind.INTERNAL)
|
||||
.hasParent(trace.getSpan(0))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void delayedTwiceFlowable() {
|
||||
List<Integer> result =
|
||||
createParentSpan(
|
||||
() ->
|
||||
Flowable.fromIterable(Ints.asList(8, 9))
|
||||
.delay(100, TimeUnit.MILLISECONDS)
|
||||
.map(this::addOne)
|
||||
.delay(100, TimeUnit.MILLISECONDS)
|
||||
.map(this::addOne)
|
||||
.toList()
|
||||
.blockingGet());
|
||||
assertThat(result).contains(10, 11);
|
||||
testing()
|
||||
.waitAndAssertTraces(
|
||||
trace ->
|
||||
trace.hasSpansSatisfyingExactly(
|
||||
span -> span.hasName(PARENT).hasKind(SpanKind.INTERNAL).hasNoParent(),
|
||||
span ->
|
||||
span.hasName(ADD_ONE)
|
||||
.hasKind(SpanKind.INTERNAL)
|
||||
.hasParent(trace.getSpan(0)),
|
||||
span ->
|
||||
span.hasName(ADD_ONE)
|
||||
.hasKind(SpanKind.INTERNAL)
|
||||
.hasParent(trace.getSpan(0)),
|
||||
span ->
|
||||
span.hasName(ADD_ONE)
|
||||
.hasKind(SpanKind.INTERNAL)
|
||||
.hasParent(trace.getSpan(0)),
|
||||
span ->
|
||||
span.hasName(ADD_ONE)
|
||||
.hasKind(SpanKind.INTERNAL)
|
||||
.hasParent(trace.getSpan(0))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void maybeFromCallable() {
|
||||
Integer result =
|
||||
createParentSpan(
|
||||
() -> Maybe.fromCallable(() -> addOne(10)).map(this::addOne).blockingGet());
|
||||
assertThat(result).isEqualTo(12);
|
||||
testing()
|
||||
.waitAndAssertTraces(
|
||||
trace ->
|
||||
trace.hasSpansSatisfyingExactly(
|
||||
span -> span.hasName(PARENT).hasKind(SpanKind.INTERNAL).hasNoParent(),
|
||||
span ->
|
||||
span.hasName(ADD_ONE)
|
||||
.hasKind(SpanKind.INTERNAL)
|
||||
.hasParent(trace.getSpan(0)),
|
||||
span ->
|
||||
span.hasName(ADD_ONE)
|
||||
.hasKind(SpanKind.INTERNAL)
|
||||
.hasParent(trace.getSpan(0))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void basicSingle() {
|
||||
Integer result = createParentSpan(() -> Single.just(0).map(this::addOne).blockingGet());
|
||||
assertThat(result).isEqualTo(1);
|
||||
testing()
|
||||
.waitAndAssertTraces(
|
||||
trace ->
|
||||
trace.hasSpansSatisfyingExactly(
|
||||
span -> span.hasName(PARENT).hasKind(SpanKind.INTERNAL).hasNoParent(),
|
||||
span ->
|
||||
span.hasName(ADD_ONE)
|
||||
.hasKind(SpanKind.INTERNAL)
|
||||
.hasParent(trace.getSpan(0))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void basicObservable() {
|
||||
List<Integer> result =
|
||||
createParentSpan(() -> Observable.just(0).map(this::addOne).toList().blockingGet());
|
||||
assertThat(result).contains(1);
|
||||
testing()
|
||||
.waitAndAssertTraces(
|
||||
trace ->
|
||||
trace.hasSpansSatisfyingExactly(
|
||||
span -> span.hasName(PARENT).hasKind(SpanKind.INTERNAL).hasNoParent(),
|
||||
span ->
|
||||
span.hasName(ADD_ONE)
|
||||
.hasKind(SpanKind.INTERNAL)
|
||||
.hasParent(trace.getSpan(0))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void connectableFlowable() {
|
||||
List<Integer> result =
|
||||
createParentSpan(
|
||||
() ->
|
||||
FlowablePublish.just(0)
|
||||
.delay(100, TimeUnit.MILLISECONDS)
|
||||
.map(this::addOne)
|
||||
.toList()
|
||||
.blockingGet());
|
||||
assertThat(result).contains(1);
|
||||
testing()
|
||||
.waitAndAssertTraces(
|
||||
trace ->
|
||||
trace.hasSpansSatisfyingExactly(
|
||||
span -> span.hasName(PARENT).hasKind(SpanKind.INTERNAL).hasNoParent(),
|
||||
span ->
|
||||
span.hasName(ADD_ONE)
|
||||
.hasKind(SpanKind.INTERNAL)
|
||||
.hasParent(trace.getSpan(0))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void connectableObservable() {
|
||||
List<Integer> result =
|
||||
createParentSpan(
|
||||
() ->
|
||||
ObservablePublish.just(0)
|
||||
.delay(100, TimeUnit.MILLISECONDS)
|
||||
.map(this::addOne)
|
||||
.toList()
|
||||
.blockingGet());
|
||||
assertThat(result).contains(1);
|
||||
testing()
|
||||
.waitAndAssertTraces(
|
||||
trace ->
|
||||
trace.hasSpansSatisfyingExactly(
|
||||
span -> span.hasName(PARENT).hasKind(SpanKind.INTERNAL).hasNoParent(),
|
||||
span ->
|
||||
span.hasName(ADD_ONE)
|
||||
.hasKind(SpanKind.INTERNAL)
|
||||
.hasParent(trace.getSpan(0))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void maybeError() {
|
||||
IllegalStateException error = new IllegalStateException(EXCEPTION_MESSAGE);
|
||||
assertThatThrownBy(() -> createParentSpan(() -> Maybe.error(error).blockingGet()))
|
||||
.isEqualTo(error);
|
||||
testing()
|
||||
.waitAndAssertTraces(
|
||||
trace ->
|
||||
trace.hasSpansSatisfyingExactly(
|
||||
span -> span.hasName(PARENT).hasKind(SpanKind.INTERNAL).hasNoParent()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void flowableError() {
|
||||
IllegalStateException error = new IllegalStateException(EXCEPTION_MESSAGE);
|
||||
assertThatThrownBy(() -> createParentSpan(() -> Flowable.error(error)).toList().blockingGet())
|
||||
.isEqualTo(error);
|
||||
testing()
|
||||
.waitAndAssertTraces(
|
||||
trace ->
|
||||
trace.hasSpansSatisfyingExactly(
|
||||
span -> span.hasName(PARENT).hasKind(SpanKind.INTERNAL).hasNoParent()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void singleError() {
|
||||
IllegalStateException error = new IllegalStateException(EXCEPTION_MESSAGE);
|
||||
assertThatThrownBy(() -> createParentSpan(() -> Single.error(error)).blockingGet())
|
||||
.isEqualTo(error);
|
||||
testing()
|
||||
.waitAndAssertTraces(
|
||||
trace ->
|
||||
trace.hasSpansSatisfyingExactly(
|
||||
span -> span.hasName(PARENT).hasKind(SpanKind.INTERNAL).hasNoParent()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void observableError() {
|
||||
IllegalStateException error = new IllegalStateException(EXCEPTION_MESSAGE);
|
||||
assertThatThrownBy(() -> createParentSpan(() -> Observable.error(error).toList().blockingGet()))
|
||||
.isEqualTo(error);
|
||||
testing()
|
||||
.waitAndAssertTraces(
|
||||
trace ->
|
||||
trace.hasSpansSatisfyingExactly(
|
||||
span -> span.hasName(PARENT).hasKind(SpanKind.INTERNAL).hasNoParent()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void completableError() {
|
||||
IllegalStateException error = new IllegalStateException(EXCEPTION_MESSAGE);
|
||||
assertThatThrownBy(
|
||||
() -> createParentSpan(() -> Completable.error(error).toMaybe().blockingGet()))
|
||||
.isEqualTo(error);
|
||||
testing()
|
||||
.waitAndAssertTraces(
|
||||
trace ->
|
||||
trace.hasSpansSatisfyingExactly(
|
||||
span -> span.hasName(PARENT).hasKind(SpanKind.INTERNAL).hasNoParent()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void basicMaybeFailure() {
|
||||
IllegalStateException error = new IllegalStateException(EXCEPTION_MESSAGE);
|
||||
assertThatThrownBy(
|
||||
() ->
|
||||
createParentSpan(
|
||||
() ->
|
||||
Maybe.just(1)
|
||||
.map(this::addOne)
|
||||
.map(
|
||||
i -> {
|
||||
throw error;
|
||||
})
|
||||
.blockingGet()))
|
||||
.isEqualTo(error);
|
||||
testing()
|
||||
.waitAndAssertTraces(
|
||||
trace ->
|
||||
trace.hasSpansSatisfyingExactly(
|
||||
span -> span.hasName(PARENT).hasKind(SpanKind.INTERNAL).hasNoParent(),
|
||||
span ->
|
||||
span.hasName(ADD_ONE)
|
||||
.hasKind(SpanKind.INTERNAL)
|
||||
.hasParent(trace.getSpan(0))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void basicFlowableFailure() {
|
||||
IllegalStateException error = new IllegalStateException(EXCEPTION_MESSAGE);
|
||||
assertThatThrownBy(
|
||||
() ->
|
||||
createParentSpan(
|
||||
() ->
|
||||
Flowable.fromIterable(Ints.asList(5, 6))
|
||||
.map(this::addOne)
|
||||
.map(
|
||||
i -> {
|
||||
throw error;
|
||||
})
|
||||
.toList()
|
||||
.blockingGet()))
|
||||
.isEqualTo(error);
|
||||
testing()
|
||||
.waitAndAssertTraces(
|
||||
trace ->
|
||||
trace.hasSpansSatisfyingExactly(
|
||||
span -> span.hasName(PARENT).hasKind(SpanKind.INTERNAL).hasNoParent(),
|
||||
span ->
|
||||
span.hasName(ADD_ONE)
|
||||
.hasKind(SpanKind.INTERNAL)
|
||||
.hasParent(trace.getSpan(0))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void basicMaybeCancel() {
|
||||
createParentSpan(
|
||||
() ->
|
||||
Maybe.just(1).toFlowable().map(this::addOne).subscribe(CancellingSubscriber.INSTANCE));
|
||||
testing()
|
||||
.waitAndAssertTraces(
|
||||
trace ->
|
||||
trace.hasSpansSatisfyingExactly(
|
||||
span -> span.hasName(PARENT).hasKind(SpanKind.INTERNAL).hasNoParent()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void basicFlowableCancel() {
|
||||
createParentSpan(
|
||||
() ->
|
||||
Flowable.fromIterable(Ints.asList(5, 6))
|
||||
.map(this::addOne)
|
||||
.subscribe(CancellingSubscriber.INSTANCE));
|
||||
testing()
|
||||
.waitAndAssertTraces(
|
||||
trace ->
|
||||
trace.hasSpansSatisfyingExactly(
|
||||
span -> span.hasName(PARENT).hasKind(SpanKind.INTERNAL).hasNoParent()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void basicSingleCancel() {
|
||||
createParentSpan(
|
||||
() ->
|
||||
Single.just(1).toFlowable().map(this::addOne).subscribe(CancellingSubscriber.INSTANCE));
|
||||
testing()
|
||||
.waitAndAssertTraces(
|
||||
trace ->
|
||||
trace.hasSpansSatisfyingExactly(
|
||||
span -> span.hasName(PARENT).hasKind(SpanKind.INTERNAL).hasNoParent()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void basicCompletableCancel() {
|
||||
createParentSpan(
|
||||
() ->
|
||||
Completable.fromCallable(() -> 1)
|
||||
.toFlowable()
|
||||
.subscribe(CancellingSubscriber.INSTANCE));
|
||||
testing()
|
||||
.waitAndAssertTraces(
|
||||
trace ->
|
||||
trace.hasSpansSatisfyingExactly(
|
||||
span -> span.hasName(PARENT).hasKind(SpanKind.INTERNAL).hasNoParent()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void basicObservableCancel() {
|
||||
createParentSpan(
|
||||
() ->
|
||||
Observable.just(1)
|
||||
.toFlowable(BackpressureStrategy.LATEST)
|
||||
.map(this::addOne)
|
||||
.subscribe(CancellingSubscriber.INSTANCE));
|
||||
testing()
|
||||
.waitAndAssertTraces(
|
||||
trace ->
|
||||
trace.hasSpansSatisfyingExactly(
|
||||
span -> span.hasName(PARENT).hasKind(SpanKind.INTERNAL).hasNoParent()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void basicMaybeChain() {
|
||||
createParentSpan(
|
||||
() ->
|
||||
Maybe.just(1)
|
||||
.map(this::addOne)
|
||||
.map(this::addOne)
|
||||
.concatWith(Maybe.just(1).map(this::addOne))
|
||||
.toList()
|
||||
.blockingGet());
|
||||
testing()
|
||||
.waitAndAssertTraces(
|
||||
trace ->
|
||||
trace.hasSpansSatisfyingExactly(
|
||||
span -> span.hasName(PARENT).hasKind(SpanKind.INTERNAL).hasNoParent(),
|
||||
span ->
|
||||
span.hasName(ADD_ONE)
|
||||
.hasKind(SpanKind.INTERNAL)
|
||||
.hasParent(trace.getSpan(0)),
|
||||
span ->
|
||||
span.hasName(ADD_ONE)
|
||||
.hasKind(SpanKind.INTERNAL)
|
||||
.hasParent(trace.getSpan(0)),
|
||||
span ->
|
||||
span.hasName(ADD_ONE)
|
||||
.hasKind(SpanKind.INTERNAL)
|
||||
.hasParent(trace.getSpan(0))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void basicFlowableChain() {
|
||||
createParentSpan(
|
||||
() ->
|
||||
Flowable.fromIterable(Ints.asList(5, 6))
|
||||
.map(this::addOne)
|
||||
.map(this::addOne)
|
||||
.concatWith(Maybe.just(1).map(this::addOne))
|
||||
.toList()
|
||||
.blockingGet());
|
||||
testing()
|
||||
.waitAndAssertTraces(
|
||||
trace ->
|
||||
trace.hasSpansSatisfyingExactly(
|
||||
span -> span.hasName(PARENT).hasKind(SpanKind.INTERNAL).hasNoParent(),
|
||||
span ->
|
||||
span.hasName(ADD_ONE)
|
||||
.hasKind(SpanKind.INTERNAL)
|
||||
.hasParent(trace.getSpan(0)),
|
||||
span ->
|
||||
span.hasName(ADD_ONE)
|
||||
.hasKind(SpanKind.INTERNAL)
|
||||
.hasParent(trace.getSpan(0)),
|
||||
span ->
|
||||
span.hasName(ADD_ONE)
|
||||
.hasKind(SpanKind.INTERNAL)
|
||||
.hasParent(trace.getSpan(0)),
|
||||
span ->
|
||||
span.hasName(ADD_ONE)
|
||||
.hasKind(SpanKind.INTERNAL)
|
||||
.hasParent(trace.getSpan(0)),
|
||||
span ->
|
||||
span.hasName(ADD_ONE)
|
||||
.hasKind(SpanKind.INTERNAL)
|
||||
.hasParent(trace.getSpan(0))));
|
||||
}
|
||||
|
||||
// Publisher chain spans have the correct parents from subscription time
|
||||
@Test
|
||||
public void maybeChainParentSpan() {
|
||||
Maybe<Integer> maybe = Maybe.just(42).map(this::addOne).map(this::addTwo);
|
||||
testing().runWithSpan("trace-parent", () -> maybe.blockingGet());
|
||||
testing()
|
||||
.waitAndAssertTraces(
|
||||
trace ->
|
||||
trace.hasSpansSatisfyingExactly(
|
||||
span -> span.hasName("trace-parent").hasKind(SpanKind.INTERNAL).hasNoParent(),
|
||||
span ->
|
||||
span.hasName(ADD_ONE)
|
||||
.hasKind(SpanKind.INTERNAL)
|
||||
.hasParent(trace.getSpan(0)),
|
||||
span ->
|
||||
span.hasName(ADD_TWO)
|
||||
.hasKind(SpanKind.INTERNAL)
|
||||
.hasParent(trace.getSpan(0))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void maybeChainHasSubscriptionContext() {
|
||||
Integer result =
|
||||
createParentSpan(
|
||||
() -> {
|
||||
Maybe<Integer> maybe = Maybe.just(1).map(this::addOne);
|
||||
return testing()
|
||||
.runWithSpan("intermediate", () -> maybe.map(this::addTwo))
|
||||
.blockingGet();
|
||||
});
|
||||
assertThat(result).isEqualTo(4);
|
||||
testing()
|
||||
.waitAndAssertTraces(
|
||||
trace ->
|
||||
trace.hasSpansSatisfyingExactly(
|
||||
span -> span.hasName(PARENT).hasKind(SpanKind.INTERNAL).hasNoParent(),
|
||||
span ->
|
||||
span.hasName("intermediate")
|
||||
.hasKind(SpanKind.INTERNAL)
|
||||
.hasParent(trace.getSpan(0)),
|
||||
span ->
|
||||
span.hasName(ADD_ONE)
|
||||
.hasKind(SpanKind.INTERNAL)
|
||||
.hasParent(trace.getSpan(0)),
|
||||
span ->
|
||||
span.hasName(ADD_TWO)
|
||||
.hasKind(SpanKind.INTERNAL)
|
||||
.hasParent(trace.getSpan(0))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void flowableChainHasSubscriptionContext() {
|
||||
List<Integer> result =
|
||||
createParentSpan(
|
||||
() -> {
|
||||
Flowable<Integer> flowable =
|
||||
Flowable.fromIterable(Ints.asList(1, 2)).map(this::addOne);
|
||||
return testing()
|
||||
.runWithSpan("intermediate", () -> flowable.map(this::addTwo))
|
||||
.toList()
|
||||
.blockingGet();
|
||||
});
|
||||
assertThat(result).contains(4, 5);
|
||||
testing()
|
||||
.waitAndAssertTraces(
|
||||
trace ->
|
||||
trace.hasSpansSatisfyingExactly(
|
||||
span -> span.hasName(PARENT).hasKind(SpanKind.INTERNAL).hasNoParent(),
|
||||
span ->
|
||||
span.hasName("intermediate")
|
||||
.hasKind(SpanKind.INTERNAL)
|
||||
.hasParent(trace.getSpan(0)),
|
||||
span ->
|
||||
span.hasName(ADD_ONE)
|
||||
.hasKind(SpanKind.INTERNAL)
|
||||
.hasParent(trace.getSpan(0)),
|
||||
span ->
|
||||
span.hasName(ADD_TWO)
|
||||
.hasKind(SpanKind.INTERNAL)
|
||||
.hasParent(trace.getSpan(0)),
|
||||
span ->
|
||||
span.hasName(ADD_ONE)
|
||||
.hasKind(SpanKind.INTERNAL)
|
||||
.hasParent(trace.getSpan(0)),
|
||||
span ->
|
||||
span.hasName(ADD_TWO)
|
||||
.hasKind(SpanKind.INTERNAL)
|
||||
.hasParent(trace.getSpan(0))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void singleChainHasSubscriptionContext() {
|
||||
Integer result =
|
||||
createParentSpan(
|
||||
() -> {
|
||||
Single<Integer> single = Single.just(1).map(this::addOne);
|
||||
return testing()
|
||||
.runWithSpan("intermediate", () -> single.map(this::addTwo))
|
||||
.blockingGet();
|
||||
});
|
||||
assertThat(result).isEqualTo(4);
|
||||
testing()
|
||||
.waitAndAssertTraces(
|
||||
trace ->
|
||||
trace.hasSpansSatisfyingExactly(
|
||||
span -> span.hasName(PARENT).hasKind(SpanKind.INTERNAL).hasNoParent(),
|
||||
span ->
|
||||
span.hasName("intermediate")
|
||||
.hasKind(SpanKind.INTERNAL)
|
||||
.hasParent(trace.getSpan(0)),
|
||||
span ->
|
||||
span.hasName(ADD_ONE)
|
||||
.hasKind(SpanKind.INTERNAL)
|
||||
.hasParent(trace.getSpan(0)),
|
||||
span ->
|
||||
span.hasName(ADD_TWO)
|
||||
.hasKind(SpanKind.INTERNAL)
|
||||
.hasParent(trace.getSpan(0))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void observableChainHasSubscriptionContext() {
|
||||
List<Integer> result =
|
||||
createParentSpan(
|
||||
() -> {
|
||||
Observable<Integer> observable = Observable.just(1).map(this::addOne);
|
||||
return testing()
|
||||
.runWithSpan("intermediate", () -> observable.map(this::addTwo))
|
||||
.toList()
|
||||
.blockingGet();
|
||||
});
|
||||
assertThat(result).contains(4);
|
||||
testing()
|
||||
.waitAndAssertTraces(
|
||||
trace ->
|
||||
trace.hasSpansSatisfyingExactly(
|
||||
span -> span.hasName(PARENT).hasKind(SpanKind.INTERNAL).hasNoParent(),
|
||||
span ->
|
||||
span.hasName("intermediate")
|
||||
.hasKind(SpanKind.INTERNAL)
|
||||
.hasParent(trace.getSpan(0)),
|
||||
span ->
|
||||
span.hasName(ADD_ONE)
|
||||
.hasKind(SpanKind.INTERNAL)
|
||||
.hasParent(trace.getSpan(0)),
|
||||
span ->
|
||||
span.hasName(ADD_TWO)
|
||||
.hasKind(SpanKind.INTERNAL)
|
||||
.hasParent(trace.getSpan(0))));
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("schedulers")
|
||||
public void flowableMultiResults(Scheduler scheduler) {
|
||||
List<Integer> result =
|
||||
testing()
|
||||
.runWithSpan(
|
||||
"flowable root",
|
||||
() -> {
|
||||
return Flowable.fromIterable(Ints.asList(1, 2, 3, 4))
|
||||
.parallel()
|
||||
.runOn(scheduler)
|
||||
.flatMap(num -> Maybe.just(num).map(this::addOne).toFlowable())
|
||||
.sequential()
|
||||
.toList()
|
||||
.blockingGet();
|
||||
});
|
||||
assertThat(result.size()).isEqualTo(4);
|
||||
testing()
|
||||
.waitAndAssertTraces(
|
||||
trace ->
|
||||
trace.hasSpansSatisfyingExactly(
|
||||
span -> span.hasName("flowable root").hasKind(SpanKind.INTERNAL).hasNoParent(),
|
||||
span ->
|
||||
span.hasName(ADD_ONE)
|
||||
.hasKind(SpanKind.INTERNAL)
|
||||
.hasParent(trace.getSpan(0)),
|
||||
span ->
|
||||
span.hasName(ADD_ONE)
|
||||
.hasKind(SpanKind.INTERNAL)
|
||||
.hasParent(trace.getSpan(0)),
|
||||
span ->
|
||||
span.hasName(ADD_ONE)
|
||||
.hasKind(SpanKind.INTERNAL)
|
||||
.hasParent(trace.getSpan(0)),
|
||||
span ->
|
||||
span.hasName(ADD_ONE)
|
||||
.hasKind(SpanKind.INTERNAL)
|
||||
.hasParent(trace.getSpan(0))));
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("schedulers")
|
||||
public void maybeMultipleTraceChains(Scheduler scheduler) {
|
||||
int iterations = 100;
|
||||
RxJava3ConcurrencyTestHelper.launchAndWait(scheduler, iterations, 60000, testing());
|
||||
@SuppressWarnings("unchecked")
|
||||
Consumer<TraceAssert>[] assertions = (Consumer<TraceAssert>[]) new Consumer<?>[iterations];
|
||||
for (int i = 0; i < iterations; i++) {
|
||||
int iteration = i;
|
||||
assertions[i] =
|
||||
trace ->
|
||||
trace.hasSpansSatisfyingExactly(
|
||||
span ->
|
||||
span.hasName("outer")
|
||||
.hasNoParent()
|
||||
.hasAttributes(attributeEntry("iteration", iteration)),
|
||||
span ->
|
||||
span.hasName("middle")
|
||||
.hasParent(trace.getSpan(0))
|
||||
.hasAttributes(attributeEntry("iteration", iteration)),
|
||||
span ->
|
||||
span.hasName("inner")
|
||||
.hasParent(trace.getSpan(1))
|
||||
.hasAttributes(attributeEntry("iteration", iteration)));
|
||||
}
|
||||
testing()
|
||||
.waitAndAssertSortedTraces(
|
||||
Comparator.comparing(
|
||||
span -> span.get(0).getAttributes().get(AttributeKey.longKey("iteration"))),
|
||||
assertions);
|
||||
testing().clearData();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,992 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.instrumentation.rxjava.v3.common;
|
||||
|
||||
import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo;
|
||||
import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.satisfies;
|
||||
import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.CODE_FUNCTION;
|
||||
import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.CODE_NAMESPACE;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import io.opentelemetry.api.common.AttributeKey;
|
||||
import io.opentelemetry.api.trace.SpanKind;
|
||||
import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension;
|
||||
import io.opentelemetry.sdk.trace.data.SpanData;
|
||||
import io.opentelemetry.sdk.trace.data.StatusData;
|
||||
import io.reactivex.rxjava3.core.Completable;
|
||||
import io.reactivex.rxjava3.core.Flowable;
|
||||
import io.reactivex.rxjava3.core.Maybe;
|
||||
import io.reactivex.rxjava3.core.Observable;
|
||||
import io.reactivex.rxjava3.core.Single;
|
||||
import io.reactivex.rxjava3.observers.TestObserver;
|
||||
import io.reactivex.rxjava3.processors.UnicastProcessor;
|
||||
import io.reactivex.rxjava3.subjects.CompletableSubject;
|
||||
import io.reactivex.rxjava3.subjects.MaybeSubject;
|
||||
import io.reactivex.rxjava3.subjects.SingleSubject;
|
||||
import io.reactivex.rxjava3.subjects.UnicastSubject;
|
||||
import io.reactivex.rxjava3.subscribers.TestSubscriber;
|
||||
import java.util.List;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.reactivestreams.Publisher;
|
||||
import org.reactivestreams.Subscriber;
|
||||
import org.reactivestreams.Subscription;
|
||||
|
||||
public abstract class AbstractRxJava3WithSpanTest {
|
||||
private static final AttributeKey<Boolean> RXJAVA_CANCELED =
|
||||
AttributeKey.booleanKey("rxjava.canceled");
|
||||
|
||||
protected abstract AbstractTracedWithSpan newTraced();
|
||||
|
||||
protected abstract InstrumentationExtension testing();
|
||||
|
||||
@Test
|
||||
public void captureSpanForCompletedCompletable() {
|
||||
TestObserver<Object> observer = new TestObserver<>();
|
||||
Completable source = Completable.complete();
|
||||
newTraced().completable(source).subscribe(observer);
|
||||
observer.assertComplete();
|
||||
testing()
|
||||
.waitAndAssertTraces(
|
||||
trace ->
|
||||
trace.hasSpansSatisfyingExactly(
|
||||
span ->
|
||||
span.hasName("TracedWithSpan.completable")
|
||||
.hasKind(SpanKind.INTERNAL)
|
||||
.hasNoParent()
|
||||
.hasAttributesSatisfyingExactly(
|
||||
satisfies(CODE_NAMESPACE, val -> val.endsWith(".TracedWithSpan")),
|
||||
equalTo(CODE_FUNCTION, "completable"))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void captureSpanForEventuallyCompletedCompletable() throws InterruptedException {
|
||||
CompletableSubject source = CompletableSubject.create();
|
||||
TestObserver<Object> observer = new TestObserver<>();
|
||||
newTraced().completable(source).subscribe(observer);
|
||||
|
||||
// sleep a bit just to make sure no span is captured
|
||||
Thread.sleep(500);
|
||||
List<List<SpanData>> traces = testing().waitForTraces(0);
|
||||
assertThat(traces).isEmpty();
|
||||
|
||||
source.onComplete();
|
||||
observer.assertComplete();
|
||||
|
||||
testing()
|
||||
.waitAndAssertTraces(
|
||||
trace ->
|
||||
trace.hasSpansSatisfyingExactly(
|
||||
span ->
|
||||
span.hasName("TracedWithSpan.completable")
|
||||
.hasKind(SpanKind.INTERNAL)
|
||||
.hasNoParent()
|
||||
.hasAttributesSatisfyingExactly(
|
||||
satisfies(CODE_NAMESPACE, val -> val.endsWith(".TracedWithSpan")),
|
||||
equalTo(CODE_FUNCTION, "completable"))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void captureSpanForErrorCompletable() {
|
||||
IllegalStateException error = new IllegalStateException("Boom");
|
||||
TestObserver<Object> observer = new TestObserver<>();
|
||||
Completable source = Completable.error(error);
|
||||
newTraced().completable(source).subscribe(observer);
|
||||
observer.assertError(error);
|
||||
testing()
|
||||
.waitAndAssertTraces(
|
||||
trace ->
|
||||
trace.hasSpansSatisfyingExactly(
|
||||
span ->
|
||||
span.hasName("TracedWithSpan.completable")
|
||||
.hasKind(SpanKind.INTERNAL)
|
||||
.hasNoParent()
|
||||
.hasStatus(StatusData.error())
|
||||
.hasException(error)
|
||||
.hasAttributesSatisfyingExactly(
|
||||
satisfies(CODE_NAMESPACE, val -> val.endsWith(".TracedWithSpan")),
|
||||
equalTo(CODE_FUNCTION, "completable"))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void captureSpanForEventuallyErrorCompletable() throws InterruptedException {
|
||||
IllegalStateException error = new IllegalStateException("Boom");
|
||||
CompletableSubject source = CompletableSubject.create();
|
||||
TestObserver<Object> observer = new TestObserver<>();
|
||||
newTraced().completable(source).subscribe(observer);
|
||||
|
||||
// sleep a bit just to make sure no span is captured
|
||||
Thread.sleep(500);
|
||||
List<List<SpanData>> traces = testing().waitForTraces(0);
|
||||
assertThat(traces).isEmpty();
|
||||
|
||||
source.onError(error);
|
||||
observer.assertError(error);
|
||||
testing()
|
||||
.waitAndAssertTraces(
|
||||
trace ->
|
||||
trace.hasSpansSatisfyingExactly(
|
||||
span ->
|
||||
span.hasName("TracedWithSpan.completable")
|
||||
.hasKind(SpanKind.INTERNAL)
|
||||
.hasNoParent()
|
||||
.hasStatus(StatusData.error())
|
||||
.hasException(error)
|
||||
.hasAttributesSatisfyingExactly(
|
||||
satisfies(CODE_NAMESPACE, val -> val.endsWith(".TracedWithSpan")),
|
||||
equalTo(CODE_FUNCTION, "completable"))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void captureSpanForCanceledCompletable() throws InterruptedException {
|
||||
CompletableSubject source = CompletableSubject.create();
|
||||
TestObserver<Object> observer = new TestObserver<>();
|
||||
newTraced().completable(source).subscribe(observer);
|
||||
|
||||
// sleep a bit just to make sure no span is captured
|
||||
Thread.sleep(500);
|
||||
List<List<SpanData>> traces = testing().waitForTraces(0);
|
||||
assertThat(traces).isEmpty();
|
||||
|
||||
observer.dispose();
|
||||
testing()
|
||||
.waitAndAssertTraces(
|
||||
trace ->
|
||||
trace.hasSpansSatisfyingExactly(
|
||||
span ->
|
||||
span.hasName("TracedWithSpan.completable")
|
||||
.hasKind(SpanKind.INTERNAL)
|
||||
.hasNoParent()
|
||||
.hasAttributesSatisfyingExactly(
|
||||
satisfies(CODE_NAMESPACE, val -> val.endsWith(".TracedWithSpan")),
|
||||
equalTo(CODE_FUNCTION, "completable"),
|
||||
equalTo(RXJAVA_CANCELED, true))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void captureSpanForCompletedMaybe() {
|
||||
Maybe<String> source = Maybe.just("Value");
|
||||
TestObserver<Object> observer = new TestObserver<>();
|
||||
newTraced().maybe(source).subscribe(observer);
|
||||
observer.assertValue("Value");
|
||||
observer.assertComplete();
|
||||
testing()
|
||||
.waitAndAssertTraces(
|
||||
trace ->
|
||||
trace.hasSpansSatisfyingExactly(
|
||||
span ->
|
||||
span.hasName("TracedWithSpan.maybe")
|
||||
.hasKind(SpanKind.INTERNAL)
|
||||
.hasNoParent()
|
||||
.hasAttributesSatisfyingExactly(
|
||||
satisfies(CODE_NAMESPACE, val -> val.endsWith(".TracedWithSpan")),
|
||||
equalTo(CODE_FUNCTION, "maybe"))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void captureSpanForEmptyMaybe() {
|
||||
Maybe<String> source = Maybe.empty();
|
||||
TestObserver<Object> observer = new TestObserver<>();
|
||||
newTraced().maybe(source).subscribe(observer);
|
||||
observer.assertComplete();
|
||||
testing()
|
||||
.waitAndAssertTraces(
|
||||
trace ->
|
||||
trace.hasSpansSatisfyingExactly(
|
||||
span ->
|
||||
span.hasName("TracedWithSpan.maybe")
|
||||
.hasKind(SpanKind.INTERNAL)
|
||||
.hasNoParent()
|
||||
.hasAttributesSatisfyingExactly(
|
||||
satisfies(CODE_NAMESPACE, val -> val.endsWith(".TracedWithSpan")),
|
||||
equalTo(CODE_FUNCTION, "maybe"))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void captureSpanForEventuallyCompletedMaybe() throws InterruptedException {
|
||||
MaybeSubject<String> source = MaybeSubject.create();
|
||||
TestObserver<Object> observer = new TestObserver<>();
|
||||
newTraced().maybe(source).subscribe(observer);
|
||||
|
||||
// sleep a bit just to make sure no span is captured
|
||||
Thread.sleep(500);
|
||||
List<List<SpanData>> traces = testing().waitForTraces(0);
|
||||
assertThat(traces).isEmpty();
|
||||
|
||||
source.onSuccess("Value");
|
||||
observer.assertValue("Value");
|
||||
observer.assertComplete();
|
||||
|
||||
testing()
|
||||
.waitAndAssertTraces(
|
||||
trace ->
|
||||
trace.hasSpansSatisfyingExactly(
|
||||
span ->
|
||||
span.hasName("TracedWithSpan.maybe")
|
||||
.hasKind(SpanKind.INTERNAL)
|
||||
.hasNoParent()
|
||||
.hasAttributesSatisfyingExactly(
|
||||
satisfies(CODE_NAMESPACE, val -> val.endsWith(".TracedWithSpan")),
|
||||
equalTo(CODE_FUNCTION, "maybe"))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void captureSpanForErrorMaybe() {
|
||||
IllegalStateException error = new IllegalStateException("Boom");
|
||||
TestObserver<Object> observer = new TestObserver<>();
|
||||
Maybe<String> source = Maybe.error(error);
|
||||
newTraced().maybe(source).subscribe(observer);
|
||||
observer.assertError(error);
|
||||
testing()
|
||||
.waitAndAssertTraces(
|
||||
trace ->
|
||||
trace.hasSpansSatisfyingExactly(
|
||||
span ->
|
||||
span.hasName("TracedWithSpan.maybe")
|
||||
.hasKind(SpanKind.INTERNAL)
|
||||
.hasNoParent()
|
||||
.hasStatus(StatusData.error())
|
||||
.hasException(error)
|
||||
.hasAttributesSatisfyingExactly(
|
||||
satisfies(CODE_NAMESPACE, val -> val.endsWith(".TracedWithSpan")),
|
||||
equalTo(CODE_FUNCTION, "maybe"))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void captureSpanForEventuallyErrorMaybe() throws InterruptedException {
|
||||
IllegalStateException error = new IllegalStateException("Boom");
|
||||
MaybeSubject<String> source = MaybeSubject.create();
|
||||
TestObserver<Object> observer = new TestObserver<>();
|
||||
newTraced().maybe(source).subscribe(observer);
|
||||
|
||||
// sleep a bit just to make sure no span is captured
|
||||
Thread.sleep(500);
|
||||
List<List<SpanData>> traces = testing().waitForTraces(0);
|
||||
assertThat(traces).isEmpty();
|
||||
|
||||
source.onError(error);
|
||||
observer.assertError(error);
|
||||
|
||||
testing()
|
||||
.waitAndAssertTraces(
|
||||
trace ->
|
||||
trace.hasSpansSatisfyingExactly(
|
||||
span ->
|
||||
span.hasName("TracedWithSpan.maybe")
|
||||
.hasKind(SpanKind.INTERNAL)
|
||||
.hasNoParent()
|
||||
.hasStatus(StatusData.error())
|
||||
.hasException(error)
|
||||
.hasAttributesSatisfyingExactly(
|
||||
satisfies(CODE_NAMESPACE, val -> val.endsWith(".TracedWithSpan")),
|
||||
equalTo(CODE_FUNCTION, "maybe"))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void captureSpanForCanceledMaybe() throws InterruptedException {
|
||||
MaybeSubject<String> source = MaybeSubject.create();
|
||||
TestObserver<Object> observer = new TestObserver<>();
|
||||
newTraced().maybe(source).subscribe(observer);
|
||||
|
||||
// sleep a bit just to make sure no span is captured
|
||||
Thread.sleep(500);
|
||||
List<List<SpanData>> traces = testing().waitForTraces(0);
|
||||
assertThat(traces).isEmpty();
|
||||
|
||||
observer.dispose();
|
||||
testing()
|
||||
.waitAndAssertTraces(
|
||||
trace ->
|
||||
trace.hasSpansSatisfyingExactly(
|
||||
span ->
|
||||
span.hasName("TracedWithSpan.maybe")
|
||||
.hasKind(SpanKind.INTERNAL)
|
||||
.hasNoParent()
|
||||
.hasAttributesSatisfyingExactly(
|
||||
satisfies(CODE_NAMESPACE, val -> val.endsWith(".TracedWithSpan")),
|
||||
equalTo(CODE_FUNCTION, "maybe"),
|
||||
equalTo(RXJAVA_CANCELED, true))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void captureSpanForCompletedSingle() {
|
||||
Single<String> source = Single.just("Value");
|
||||
TestObserver<Object> observer = new TestObserver<>();
|
||||
newTraced().single(source).subscribe(observer);
|
||||
observer.assertValue("Value");
|
||||
observer.assertComplete();
|
||||
testing()
|
||||
.waitAndAssertTraces(
|
||||
trace ->
|
||||
trace.hasSpansSatisfyingExactly(
|
||||
span ->
|
||||
span.hasName("TracedWithSpan.single")
|
||||
.hasKind(SpanKind.INTERNAL)
|
||||
.hasNoParent()
|
||||
.hasAttributesSatisfyingExactly(
|
||||
satisfies(CODE_NAMESPACE, val -> val.endsWith(".TracedWithSpan")),
|
||||
equalTo(CODE_FUNCTION, "single"))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void captureSpanForEventuallyCompletedSingle() throws InterruptedException {
|
||||
SingleSubject<String> source = SingleSubject.create();
|
||||
TestObserver<Object> observer = new TestObserver<>();
|
||||
newTraced().single(source).subscribe(observer);
|
||||
|
||||
// sleep a bit just to make sure no span is captured
|
||||
Thread.sleep(500);
|
||||
List<List<SpanData>> traces = testing().waitForTraces(0);
|
||||
assertThat(traces).isEmpty();
|
||||
|
||||
source.onSuccess("Value");
|
||||
observer.assertValue("Value");
|
||||
observer.assertComplete();
|
||||
|
||||
testing()
|
||||
.waitAndAssertTraces(
|
||||
trace ->
|
||||
trace.hasSpansSatisfyingExactly(
|
||||
span ->
|
||||
span.hasName("TracedWithSpan.single")
|
||||
.hasKind(SpanKind.INTERNAL)
|
||||
.hasNoParent()
|
||||
.hasAttributesSatisfyingExactly(
|
||||
satisfies(CODE_NAMESPACE, val -> val.endsWith(".TracedWithSpan")),
|
||||
equalTo(CODE_FUNCTION, "single"))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void captureSpanForErrorSingle() {
|
||||
IllegalStateException error = new IllegalStateException("Boom");
|
||||
TestObserver<Object> observer = new TestObserver<>();
|
||||
Single<String> source = Single.error(error);
|
||||
newTraced().single(source).subscribe(observer);
|
||||
observer.assertError(error);
|
||||
testing()
|
||||
.waitAndAssertTraces(
|
||||
trace ->
|
||||
trace.hasSpansSatisfyingExactly(
|
||||
span ->
|
||||
span.hasName("TracedWithSpan.single")
|
||||
.hasKind(SpanKind.INTERNAL)
|
||||
.hasNoParent()
|
||||
.hasStatus(StatusData.error())
|
||||
.hasException(error)
|
||||
.hasAttributesSatisfyingExactly(
|
||||
satisfies(CODE_NAMESPACE, val -> val.endsWith(".TracedWithSpan")),
|
||||
equalTo(CODE_FUNCTION, "single"))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void captureSpanForEventuallyErrorSingle() throws InterruptedException {
|
||||
IllegalStateException error = new IllegalStateException("Boom");
|
||||
SingleSubject<String> source = SingleSubject.create();
|
||||
TestObserver<String> observer = new TestObserver<>();
|
||||
newTraced().single(source).subscribe(observer);
|
||||
|
||||
// sleep a bit just to make sure no span is captured
|
||||
Thread.sleep(500);
|
||||
List<List<SpanData>> traces = testing().waitForTraces(0);
|
||||
assertThat(traces).isEmpty();
|
||||
|
||||
source.onError(error);
|
||||
observer.assertError(error);
|
||||
|
||||
testing()
|
||||
.waitAndAssertTraces(
|
||||
trace ->
|
||||
trace.hasSpansSatisfyingExactly(
|
||||
span ->
|
||||
span.hasName("TracedWithSpan.single")
|
||||
.hasKind(SpanKind.INTERNAL)
|
||||
.hasNoParent()
|
||||
.hasStatus(StatusData.error())
|
||||
.hasException(error)
|
||||
.hasAttributesSatisfyingExactly(
|
||||
satisfies(CODE_NAMESPACE, val -> val.endsWith(".TracedWithSpan")),
|
||||
equalTo(CODE_FUNCTION, "single"))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void captureSpanForCanceledSingle() throws InterruptedException {
|
||||
SingleSubject<String> source = SingleSubject.create();
|
||||
TestObserver<Object> observer = new TestObserver<>();
|
||||
newTraced().single(source).subscribe(observer);
|
||||
|
||||
// sleep a bit just to make sure no span is captured
|
||||
Thread.sleep(500);
|
||||
List<List<SpanData>> traces = testing().waitForTraces(0);
|
||||
assertThat(traces).isEmpty();
|
||||
|
||||
observer.dispose();
|
||||
testing()
|
||||
.waitAndAssertTraces(
|
||||
trace ->
|
||||
trace.hasSpansSatisfyingExactly(
|
||||
span ->
|
||||
span.hasName("TracedWithSpan.single")
|
||||
.hasKind(SpanKind.INTERNAL)
|
||||
.hasNoParent()
|
||||
.hasAttributesSatisfyingExactly(
|
||||
satisfies(CODE_NAMESPACE, val -> val.endsWith(".TracedWithSpan")),
|
||||
equalTo(CODE_FUNCTION, "single"),
|
||||
equalTo(RXJAVA_CANCELED, true))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void captureSpanForCompletedObservable() {
|
||||
TestObserver<Object> observer = new TestObserver<>();
|
||||
Observable<String> source = Observable.just("Value");
|
||||
newTraced().observable(source).subscribe(observer);
|
||||
observer.assertValue("Value");
|
||||
observer.assertComplete();
|
||||
testing()
|
||||
.waitAndAssertTraces(
|
||||
trace ->
|
||||
trace.hasSpansSatisfyingExactly(
|
||||
span ->
|
||||
span.hasName("TracedWithSpan.observable")
|
||||
.hasKind(SpanKind.INTERNAL)
|
||||
.hasNoParent()
|
||||
.hasAttributesSatisfyingExactly(
|
||||
satisfies(CODE_NAMESPACE, val -> val.endsWith(".TracedWithSpan")),
|
||||
equalTo(CODE_FUNCTION, "observable"))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void captureSpanForEventuallyCompletedObservable() throws InterruptedException {
|
||||
TestObserver<Object> observer = new TestObserver<>();
|
||||
UnicastSubject<String> source = UnicastSubject.create();
|
||||
newTraced().observable(source).subscribe(observer);
|
||||
|
||||
// sleep a bit just to make sure no span is captured
|
||||
Thread.sleep(500);
|
||||
List<List<SpanData>> traces = testing().waitForTraces(0);
|
||||
assertThat(traces).isEmpty();
|
||||
|
||||
source.onNext("Value");
|
||||
observer.assertValue("Value");
|
||||
|
||||
// sleep a bit just to make sure no span is captured
|
||||
Thread.sleep(500);
|
||||
traces = testing().waitForTraces(0);
|
||||
assertThat(traces).isEmpty();
|
||||
|
||||
source.onComplete();
|
||||
observer.assertComplete();
|
||||
testing()
|
||||
.waitAndAssertTraces(
|
||||
trace ->
|
||||
trace.hasSpansSatisfyingExactly(
|
||||
span ->
|
||||
span.hasName("TracedWithSpan.observable")
|
||||
.hasKind(SpanKind.INTERNAL)
|
||||
.hasNoParent()
|
||||
.hasAttributesSatisfyingExactly(
|
||||
satisfies(CODE_NAMESPACE, val -> val.endsWith(".TracedWithSpan")),
|
||||
equalTo(CODE_FUNCTION, "observable"))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void captureSpanForErrorObservable() {
|
||||
IllegalStateException error = new IllegalStateException("Boom");
|
||||
Observable<String> source = Observable.error(error);
|
||||
TestObserver<Object> observer = new TestObserver<>();
|
||||
newTraced().observable(source).subscribe(observer);
|
||||
observer.assertError(error);
|
||||
testing()
|
||||
.waitAndAssertTraces(
|
||||
trace ->
|
||||
trace.hasSpansSatisfyingExactly(
|
||||
span ->
|
||||
span.hasName("TracedWithSpan.observable")
|
||||
.hasKind(SpanKind.INTERNAL)
|
||||
.hasNoParent()
|
||||
.hasStatus(StatusData.error())
|
||||
.hasException(error)
|
||||
.hasAttributesSatisfyingExactly(
|
||||
satisfies(CODE_NAMESPACE, val -> val.endsWith(".TracedWithSpan")),
|
||||
equalTo(CODE_FUNCTION, "observable"))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void captureSpanForEventuallyErrorObservable() throws InterruptedException {
|
||||
IllegalStateException error = new IllegalStateException("Boom");
|
||||
UnicastSubject<String> source = UnicastSubject.create();
|
||||
TestObserver<Object> observer = new TestObserver<>();
|
||||
newTraced().observable(source).subscribe(observer);
|
||||
|
||||
// sleep a bit just to make sure no span is captured
|
||||
Thread.sleep(500);
|
||||
List<List<SpanData>> traces = testing().waitForTraces(0);
|
||||
assertThat(traces).isEmpty();
|
||||
|
||||
source.onNext("Value");
|
||||
observer.assertValue("Value");
|
||||
|
||||
// sleep a bit just to make sure no span is captured
|
||||
Thread.sleep(500);
|
||||
traces = testing().waitForTraces(0);
|
||||
assertThat(traces).isEmpty();
|
||||
|
||||
source.onError(error);
|
||||
observer.assertError(error);
|
||||
|
||||
testing()
|
||||
.waitAndAssertTraces(
|
||||
trace ->
|
||||
trace.hasSpansSatisfyingExactly(
|
||||
span ->
|
||||
span.hasName("TracedWithSpan.observable")
|
||||
.hasKind(SpanKind.INTERNAL)
|
||||
.hasNoParent()
|
||||
.hasStatus(StatusData.error())
|
||||
.hasException(error)
|
||||
.hasAttributesSatisfyingExactly(
|
||||
satisfies(CODE_NAMESPACE, val -> val.endsWith(".TracedWithSpan")),
|
||||
equalTo(CODE_FUNCTION, "observable"))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void captureSpanForCanceledObservable() throws InterruptedException {
|
||||
UnicastSubject<String> source = UnicastSubject.create();
|
||||
TestObserver<Object> observer = new TestObserver<>();
|
||||
newTraced().observable(source).subscribe(observer);
|
||||
|
||||
// sleep a bit just to make sure no span is captured
|
||||
Thread.sleep(500);
|
||||
List<List<SpanData>> traces = testing().waitForTraces(0);
|
||||
assertThat(traces).isEmpty();
|
||||
|
||||
source.onNext("Value");
|
||||
observer.assertValue("Value");
|
||||
|
||||
// sleep a bit just to make sure no span is captured
|
||||
Thread.sleep(500);
|
||||
traces = testing().waitForTraces(0);
|
||||
assertThat(traces).isEmpty();
|
||||
|
||||
observer.dispose();
|
||||
testing()
|
||||
.waitAndAssertTraces(
|
||||
trace ->
|
||||
trace.hasSpansSatisfyingExactly(
|
||||
span ->
|
||||
span.hasName("TracedWithSpan.observable")
|
||||
.hasKind(SpanKind.INTERNAL)
|
||||
.hasNoParent()
|
||||
.hasAttributesSatisfyingExactly(
|
||||
satisfies(CODE_NAMESPACE, val -> val.endsWith(".TracedWithSpan")),
|
||||
equalTo(CODE_FUNCTION, "observable"),
|
||||
equalTo(RXJAVA_CANCELED, true))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void captureSpanForCompletedFlowable() {
|
||||
TestSubscriber<Object> observe = new TestSubscriber<>();
|
||||
Flowable<String> source = Flowable.just("Value");
|
||||
newTraced().flowable(source).subscribe(observe);
|
||||
observe.assertValue("Value");
|
||||
observe.assertComplete();
|
||||
testing()
|
||||
.waitAndAssertTraces(
|
||||
trace ->
|
||||
trace.hasSpansSatisfyingExactly(
|
||||
span ->
|
||||
span.hasName("TracedWithSpan.flowable")
|
||||
.hasKind(SpanKind.INTERNAL)
|
||||
.hasNoParent()
|
||||
.hasAttributesSatisfyingExactly(
|
||||
satisfies(CODE_NAMESPACE, val -> val.endsWith(".TracedWithSpan")),
|
||||
equalTo(CODE_FUNCTION, "flowable"))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void captureForEventuallyCompletedFlowable() throws InterruptedException {
|
||||
UnicastProcessor<String> source = UnicastProcessor.create();
|
||||
TestSubscriber<Object> observer = new TestSubscriber<>();
|
||||
newTraced().flowable(source).subscribe(observer);
|
||||
|
||||
// sleep a bit just to make sure no span is captured
|
||||
Thread.sleep(500);
|
||||
List<List<SpanData>> traces = testing().waitForTraces(0);
|
||||
assertThat(traces).isEmpty();
|
||||
|
||||
source.onNext("Value");
|
||||
observer.assertValue("Value");
|
||||
|
||||
// sleep a bit just to make sure no span is captured
|
||||
Thread.sleep(500);
|
||||
traces = testing().waitForTraces(0);
|
||||
assertThat(traces).isEmpty();
|
||||
|
||||
source.onComplete();
|
||||
observer.assertComplete();
|
||||
|
||||
testing()
|
||||
.waitAndAssertTraces(
|
||||
trace ->
|
||||
trace.hasSpansSatisfyingExactly(
|
||||
span ->
|
||||
span.hasName("TracedWithSpan.flowable")
|
||||
.hasKind(SpanKind.INTERNAL)
|
||||
.hasNoParent()
|
||||
.hasAttributesSatisfyingExactly(
|
||||
satisfies(CODE_NAMESPACE, val -> val.endsWith(".TracedWithSpan")),
|
||||
equalTo(CODE_FUNCTION, "flowable"))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void captureSpanForErrorFlowable() {
|
||||
IllegalStateException error = new IllegalStateException("Boom");
|
||||
TestSubscriber<Object> observer = new TestSubscriber<>();
|
||||
Flowable<String> source = Flowable.error(error);
|
||||
newTraced().flowable(source).subscribe(observer);
|
||||
testing()
|
||||
.waitAndAssertTraces(
|
||||
trace ->
|
||||
trace.hasSpansSatisfyingExactly(
|
||||
span ->
|
||||
span.hasName("TracedWithSpan.flowable")
|
||||
.hasKind(SpanKind.INTERNAL)
|
||||
.hasNoParent()
|
||||
.hasStatus(StatusData.error())
|
||||
.hasException(error)
|
||||
.hasAttributesSatisfyingExactly(
|
||||
satisfies(CODE_NAMESPACE, val -> val.endsWith(".TracedWithSpan")),
|
||||
equalTo(CODE_FUNCTION, "flowable"))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void captureSpanForEventuallyErrorFlowable() throws InterruptedException {
|
||||
IllegalStateException error = new IllegalStateException("Boom");
|
||||
UnicastProcessor<String> source = UnicastProcessor.create();
|
||||
TestSubscriber<Object> observer = new TestSubscriber<>();
|
||||
newTraced().flowable(source).subscribe(observer);
|
||||
|
||||
// sleep a bit just to make sure no span is captured
|
||||
Thread.sleep(500);
|
||||
List<List<SpanData>> traces = testing().waitForTraces(0);
|
||||
assertThat(traces).isEmpty();
|
||||
|
||||
source.onNext("Value");
|
||||
observer.assertValue("Value");
|
||||
|
||||
// sleep a bit just to make sure no span is captured
|
||||
Thread.sleep(500);
|
||||
traces = testing().waitForTraces(0);
|
||||
assertThat(traces).isEmpty();
|
||||
|
||||
source.onError(error);
|
||||
observer.assertError(error);
|
||||
|
||||
testing()
|
||||
.waitAndAssertTraces(
|
||||
trace ->
|
||||
trace.hasSpansSatisfyingExactly(
|
||||
span ->
|
||||
span.hasName("TracedWithSpan.flowable")
|
||||
.hasKind(SpanKind.INTERNAL)
|
||||
.hasNoParent()
|
||||
.hasStatus(StatusData.error())
|
||||
.hasException(error)
|
||||
.hasAttributesSatisfyingExactly(
|
||||
satisfies(CODE_NAMESPACE, val -> val.endsWith(".TracedWithSpan")),
|
||||
equalTo(CODE_FUNCTION, "flowable"))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void captureSpanForCanceledFlowable() throws InterruptedException {
|
||||
UnicastProcessor<String> source = UnicastProcessor.create();
|
||||
TestSubscriber<Object> observer = new TestSubscriber<>();
|
||||
newTraced().flowable(source).subscribe(observer);
|
||||
|
||||
// sleep a bit just to make sure no span is captured
|
||||
Thread.sleep(500);
|
||||
List<List<SpanData>> traces = testing().waitForTraces(0);
|
||||
assertThat(traces).isEmpty();
|
||||
|
||||
source.onNext("Value");
|
||||
observer.assertValue("Value");
|
||||
|
||||
// sleep a bit just to make sure no span is captured
|
||||
Thread.sleep(500);
|
||||
traces = testing().waitForTraces(0);
|
||||
assertThat(traces).isEmpty();
|
||||
|
||||
observer.cancel();
|
||||
testing()
|
||||
.waitAndAssertTraces(
|
||||
trace ->
|
||||
trace.hasSpansSatisfyingExactly(
|
||||
span ->
|
||||
span.hasName("TracedWithSpan.flowable")
|
||||
.hasKind(SpanKind.INTERNAL)
|
||||
.hasNoParent()
|
||||
.hasAttributesSatisfyingExactly(
|
||||
satisfies(CODE_NAMESPACE, val -> val.endsWith(".TracedWithSpan")),
|
||||
equalTo(CODE_FUNCTION, "flowable"),
|
||||
equalTo(RXJAVA_CANCELED, true))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void captureSpanForCompletedParallelFlowable() {
|
||||
Flowable<String> source = Flowable.just("Value");
|
||||
TestSubscriber<Object> observer = new TestSubscriber<>();
|
||||
newTraced().parallelFlowable(source.parallel()).sequential().subscribe(observer);
|
||||
observer.assertValue("Value");
|
||||
observer.assertComplete();
|
||||
testing()
|
||||
.waitAndAssertTraces(
|
||||
trace ->
|
||||
trace.hasSpansSatisfyingExactly(
|
||||
span ->
|
||||
span.hasName("TracedWithSpan.parallelFlowable")
|
||||
.hasKind(SpanKind.INTERNAL)
|
||||
.hasNoParent()
|
||||
.hasAttributesSatisfyingExactly(
|
||||
satisfies(CODE_NAMESPACE, val -> val.endsWith(".TracedWithSpan")),
|
||||
equalTo(CODE_FUNCTION, "parallelFlowable"))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void captureSpanForEventuallyCompletedParallelFlowable() throws InterruptedException {
|
||||
UnicastProcessor<String> source = UnicastProcessor.create();
|
||||
TestSubscriber<Object> observer = new TestSubscriber<>();
|
||||
newTraced().parallelFlowable(source.parallel()).sequential().subscribe(observer);
|
||||
|
||||
// sleep a bit just to make sure no span is captured
|
||||
Thread.sleep(500);
|
||||
List<List<SpanData>> traces = testing().waitForTraces(0);
|
||||
assertThat(traces).isEmpty();
|
||||
|
||||
source.onNext("Value");
|
||||
observer.assertValue("Value");
|
||||
|
||||
// sleep a bit just to make sure no span is captured
|
||||
Thread.sleep(500);
|
||||
traces = testing().waitForTraces(0);
|
||||
assertThat(traces).isEmpty();
|
||||
|
||||
source.onComplete();
|
||||
observer.assertComplete();
|
||||
|
||||
testing()
|
||||
.waitAndAssertTraces(
|
||||
trace ->
|
||||
trace.hasSpansSatisfyingExactly(
|
||||
span ->
|
||||
span.hasName("TracedWithSpan.parallelFlowable")
|
||||
.hasKind(SpanKind.INTERNAL)
|
||||
.hasNoParent()
|
||||
.hasAttributesSatisfyingExactly(
|
||||
satisfies(CODE_NAMESPACE, val -> val.endsWith(".TracedWithSpan")),
|
||||
equalTo(CODE_FUNCTION, "parallelFlowable"))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void captureSpanForErrorParallelFlowable() {
|
||||
IllegalStateException error = new IllegalStateException("Boom");
|
||||
TestSubscriber<Object> observer = new TestSubscriber<>();
|
||||
Flowable<String> source = Flowable.error(error);
|
||||
newTraced().parallelFlowable(source.parallel()).sequential().subscribe(observer);
|
||||
observer.assertError(error);
|
||||
testing()
|
||||
.waitAndAssertTraces(
|
||||
trace ->
|
||||
trace.hasSpansSatisfyingExactly(
|
||||
span ->
|
||||
span.hasName("TracedWithSpan.parallelFlowable")
|
||||
.hasKind(SpanKind.INTERNAL)
|
||||
.hasNoParent()
|
||||
.hasStatus(StatusData.error())
|
||||
.hasException(error)
|
||||
.hasAttributesSatisfyingExactly(
|
||||
satisfies(CODE_NAMESPACE, val -> val.endsWith(".TracedWithSpan")),
|
||||
equalTo(CODE_FUNCTION, "parallelFlowable"))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void captureSpanForEventuallyErrorParallelFlowable() throws InterruptedException {
|
||||
IllegalStateException error = new IllegalStateException("Boom");
|
||||
TestSubscriber<Object> observer = new TestSubscriber<>();
|
||||
UnicastProcessor<String> source = UnicastProcessor.create();
|
||||
newTraced().parallelFlowable(source.parallel()).sequential().subscribe(observer);
|
||||
|
||||
// sleep a bit just to make sure no span is captured
|
||||
Thread.sleep(500);
|
||||
List<List<SpanData>> traces = testing().waitForTraces(0);
|
||||
assertThat(traces).isEmpty();
|
||||
|
||||
source.onNext("Value");
|
||||
observer.assertValue("Value");
|
||||
|
||||
// sleep a bit just to make sure no span is captured
|
||||
Thread.sleep(500);
|
||||
traces = testing().waitForTraces(0);
|
||||
assertThat(traces).isEmpty();
|
||||
|
||||
source.onError(error);
|
||||
observer.assertError(error);
|
||||
|
||||
observer.assertError(error);
|
||||
testing()
|
||||
.waitAndAssertTraces(
|
||||
trace ->
|
||||
trace.hasSpansSatisfyingExactly(
|
||||
span ->
|
||||
span.hasName("TracedWithSpan.parallelFlowable")
|
||||
.hasKind(SpanKind.INTERNAL)
|
||||
.hasNoParent()
|
||||
.hasStatus(StatusData.error())
|
||||
.hasException(error)
|
||||
.hasAttributesSatisfyingExactly(
|
||||
satisfies(CODE_NAMESPACE, val -> val.endsWith(".TracedWithSpan")),
|
||||
equalTo(CODE_FUNCTION, "parallelFlowable"))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void captureSpanForCanceledParallelFlowable() throws InterruptedException {
|
||||
TestSubscriber<Object> observer = new TestSubscriber<>();
|
||||
UnicastProcessor<String> source = UnicastProcessor.create();
|
||||
newTraced().parallelFlowable(source.parallel()).sequential().subscribe(observer);
|
||||
|
||||
// sleep a bit just to make sure no span is captured
|
||||
Thread.sleep(500);
|
||||
List<List<SpanData>> traces = testing().waitForTraces(0);
|
||||
assertThat(traces).isEmpty();
|
||||
|
||||
source.onNext("Value");
|
||||
observer.assertValue("Value");
|
||||
|
||||
// sleep a bit just to make sure no span is captured
|
||||
Thread.sleep(500);
|
||||
traces = testing().waitForTraces(0);
|
||||
assertThat(traces).isEmpty();
|
||||
|
||||
observer.cancel();
|
||||
|
||||
testing()
|
||||
.waitAndAssertTraces(
|
||||
trace ->
|
||||
trace.hasSpansSatisfyingExactly(
|
||||
span ->
|
||||
span.hasName("TracedWithSpan.parallelFlowable")
|
||||
.hasKind(SpanKind.INTERNAL)
|
||||
.hasNoParent()
|
||||
.hasAttributesSatisfyingExactly(
|
||||
satisfies(CODE_NAMESPACE, val -> val.endsWith(".TracedWithSpan")),
|
||||
equalTo(CODE_FUNCTION, "parallelFlowable"),
|
||||
equalTo(RXJAVA_CANCELED, true))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void captureSpanForEventuallyCompletedPublisher() throws InterruptedException {
|
||||
CustomPublisher source = new CustomPublisher();
|
||||
TestSubscriber<String> observer = new TestSubscriber<>();
|
||||
newTraced().publisher(source).subscribe(observer);
|
||||
|
||||
// sleep a bit just to make sure no span is captured
|
||||
Thread.sleep(500);
|
||||
List<List<SpanData>> traces = testing().waitForTraces(0);
|
||||
assertThat(traces).isEmpty();
|
||||
|
||||
source.onComplete();
|
||||
observer.assertComplete();
|
||||
testing()
|
||||
.waitAndAssertTraces(
|
||||
trace ->
|
||||
trace.hasSpansSatisfyingExactly(
|
||||
span ->
|
||||
span.hasName("TracedWithSpan.publisher")
|
||||
.hasKind(SpanKind.INTERNAL)
|
||||
.hasNoParent()
|
||||
.hasAttributesSatisfyingExactly(
|
||||
satisfies(CODE_NAMESPACE, val -> val.endsWith(".TracedWithSpan")),
|
||||
equalTo(CODE_FUNCTION, "publisher"))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void captureSpanForEventuallyErrorPublisher() throws InterruptedException {
|
||||
IllegalStateException error = new IllegalStateException("Boom");
|
||||
CustomPublisher source = new CustomPublisher();
|
||||
TestSubscriber<Object> observer = new TestSubscriber<>();
|
||||
newTraced().publisher(source).subscribe(observer);
|
||||
|
||||
// sleep a bit just to make sure no span is captured
|
||||
Thread.sleep(500);
|
||||
List<List<SpanData>> traces = testing().waitForTraces(0);
|
||||
assertThat(traces).isEmpty();
|
||||
|
||||
source.onError(error);
|
||||
observer.assertError(error);
|
||||
|
||||
testing()
|
||||
.waitAndAssertTraces(
|
||||
trace ->
|
||||
trace.hasSpansSatisfyingExactly(
|
||||
span ->
|
||||
span.hasName("TracedWithSpan.publisher")
|
||||
.hasKind(SpanKind.INTERNAL)
|
||||
.hasNoParent()
|
||||
.hasStatus(StatusData.error())
|
||||
.hasException(error)
|
||||
.hasAttributesSatisfyingExactly(
|
||||
satisfies(CODE_NAMESPACE, val -> val.endsWith(".TracedWithSpan")),
|
||||
equalTo(CODE_FUNCTION, "publisher"))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void captureSpanForCanceledPublisher() throws InterruptedException {
|
||||
CustomPublisher source = new CustomPublisher();
|
||||
TestSubscriber<Object> observer = new TestSubscriber<>();
|
||||
newTraced().publisher(source).subscribe(observer);
|
||||
|
||||
// sleep a bit just to make sure no span is captured
|
||||
Thread.sleep(500);
|
||||
List<List<SpanData>> traces = testing().waitForTraces(0);
|
||||
assertThat(traces).isEmpty();
|
||||
|
||||
observer.cancel();
|
||||
testing()
|
||||
.waitAndAssertTraces(
|
||||
trace ->
|
||||
trace.hasSpansSatisfyingExactly(
|
||||
span ->
|
||||
span.hasName("TracedWithSpan.publisher")
|
||||
.hasKind(SpanKind.INTERNAL)
|
||||
.hasNoParent()
|
||||
.hasAttributesSatisfyingExactly(
|
||||
satisfies(CODE_NAMESPACE, val -> val.endsWith(".TracedWithSpan")),
|
||||
equalTo(CODE_FUNCTION, "publisher"),
|
||||
equalTo(RXJAVA_CANCELED, true))));
|
||||
}
|
||||
|
||||
static class CustomPublisher implements Publisher<String>, Subscription {
|
||||
|
||||
Subscriber<? super String> subscriber;
|
||||
|
||||
@Override
|
||||
public void subscribe(Subscriber<? super String> subscriber) {
|
||||
this.subscriber = subscriber;
|
||||
subscriber.onSubscribe(this);
|
||||
}
|
||||
|
||||
void onComplete() {
|
||||
this.subscriber.onComplete();
|
||||
}
|
||||
|
||||
void onError(Throwable exception) {
|
||||
this.subscriber.onError(exception);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void request(long l) {}
|
||||
|
||||
@Override
|
||||
public void cancel() {}
|
||||
}
|
||||
}
|
|
@ -6,7 +6,7 @@
|
|||
package io.opentelemetry.instrumentation.rxjava.v3.common;
|
||||
|
||||
import io.opentelemetry.api.trace.Span;
|
||||
import io.opentelemetry.instrumentation.testing.InstrumentationTestRunner;
|
||||
import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension;
|
||||
import io.reactivex.rxjava3.core.Scheduler;
|
||||
import io.reactivex.rxjava3.core.Single;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
|
@ -23,8 +23,10 @@ import java.util.concurrent.TimeUnit;
|
|||
* from different traces.
|
||||
*/
|
||||
public class RxJava3ConcurrencyTestHelper {
|
||||
private RxJava3ConcurrencyTestHelper() {}
|
||||
|
||||
public static void launchAndWait(
|
||||
Scheduler scheduler, int iterations, long timeoutMillis, InstrumentationTestRunner runner) {
|
||||
Scheduler scheduler, int iterations, long timeoutMillis, InstrumentationExtension runner) {
|
||||
CountDownLatch latch = new CountDownLatch(iterations);
|
||||
|
||||
for (int i = 0; i < iterations; i++) {
|
||||
|
@ -40,7 +42,7 @@ public class RxJava3ConcurrencyTestHelper {
|
|||
}
|
||||
}
|
||||
|
||||
private static void launchOuter(Iteration iteration, InstrumentationTestRunner runner) {
|
||||
private static void launchOuter(Iteration iteration, InstrumentationExtension runner) {
|
||||
runner.runWithSpan(
|
||||
"outer",
|
||||
() -> {
|
||||
|
@ -58,7 +60,7 @@ public class RxJava3ConcurrencyTestHelper {
|
|||
});
|
||||
}
|
||||
|
||||
private static void launchInner(Iteration iteration, InstrumentationTestRunner runner) {
|
||||
private static void launchInner(Iteration iteration, InstrumentationExtension runner) {
|
||||
runner.runWithSpan(
|
||||
"middle",
|
||||
() -> {
|
|
@ -1,16 +0,0 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import io.opentelemetry.instrumentation.rxjava.v3.common.AbstractRxJava3WithSpanTest
|
||||
import io.opentelemetry.instrumentation.rxjava.v3.common.AbstractTracedWithSpan
|
||||
import io.opentelemetry.instrumentation.rxjava.v3.common.extensionannotation.TracedWithSpan
|
||||
|
||||
class RxJava3ExtensionWithSpanTest extends AbstractRxJava3WithSpanTest {
|
||||
|
||||
@Override
|
||||
AbstractTracedWithSpan newTraced() {
|
||||
return new TracedWithSpan()
|
||||
}
|
||||
}
|
|
@ -1,16 +0,0 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import io.opentelemetry.instrumentation.rxjava.v3.common.AbstractRxJava3WithSpanTest
|
||||
import io.opentelemetry.instrumentation.rxjava.v3.common.AbstractTracedWithSpan
|
||||
import io.opentelemetry.instrumentation.rxjava.v3.common.instrumentationannotation.TracedWithSpan
|
||||
|
||||
class RxJava3InstrumentationWithSpanTest extends AbstractRxJava3WithSpanTest {
|
||||
|
||||
@Override
|
||||
AbstractTracedWithSpan newTraced() {
|
||||
return new TracedWithSpan()
|
||||
}
|
||||
}
|
|
@ -1,10 +0,0 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import io.opentelemetry.instrumentation.rxjava.v3.common.AbstractRxJava3SubscriptionTest
|
||||
import io.opentelemetry.instrumentation.test.AgentTestTrait
|
||||
|
||||
class RxJava3SubscriptionTest extends AbstractRxJava3SubscriptionTest implements AgentTestTrait {
|
||||
}
|
|
@ -1,10 +0,0 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import io.opentelemetry.instrumentation.rxjava.v3.common.AbstractRxJava3Test
|
||||
import io.opentelemetry.instrumentation.test.AgentTestTrait
|
||||
|
||||
class RxJava3Test extends AbstractRxJava3Test implements AgentTestTrait {
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import io.opentelemetry.instrumentation.rxjava.v3.common.AbstractRxJava3WithSpanTest;
|
||||
import io.opentelemetry.instrumentation.rxjava.v3.common.AbstractTracedWithSpan;
|
||||
import io.opentelemetry.instrumentation.rxjava.v3.common.extensionannotation.TracedWithSpan;
|
||||
import io.opentelemetry.instrumentation.testing.junit.AgentInstrumentationExtension;
|
||||
import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
|
||||
public class RxJava3ExtensionWithSpanTest extends AbstractRxJava3WithSpanTest {
|
||||
@RegisterExtension
|
||||
static final InstrumentationExtension testing = AgentInstrumentationExtension.create();
|
||||
|
||||
@Override
|
||||
protected AbstractTracedWithSpan newTraced() {
|
||||
return new TracedWithSpan();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected InstrumentationExtension testing() {
|
||||
return testing;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import io.opentelemetry.instrumentation.rxjava.v3.common.AbstractRxJava3WithSpanTest;
|
||||
import io.opentelemetry.instrumentation.rxjava.v3.common.AbstractTracedWithSpan;
|
||||
import io.opentelemetry.instrumentation.rxjava.v3.common.extensionannotation.TracedWithSpan;
|
||||
import io.opentelemetry.instrumentation.testing.junit.AgentInstrumentationExtension;
|
||||
import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
|
||||
public class RxJava3InstrumentationWithSpanTest extends AbstractRxJava3WithSpanTest {
|
||||
@RegisterExtension
|
||||
static final InstrumentationExtension testing = AgentInstrumentationExtension.create();
|
||||
|
||||
@Override
|
||||
protected AbstractTracedWithSpan newTraced() {
|
||||
return new TracedWithSpan();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected InstrumentationExtension testing() {
|
||||
return testing;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import io.opentelemetry.instrumentation.rxjava.v3.common.AbstractRxJava3SubscriptionTest;
|
||||
import io.opentelemetry.instrumentation.testing.junit.AgentInstrumentationExtension;
|
||||
import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
|
||||
public class RxJava3SubscriptionTest extends AbstractRxJava3SubscriptionTest {
|
||||
@RegisterExtension
|
||||
static final InstrumentationExtension testing = AgentInstrumentationExtension.create();
|
||||
|
||||
@Override
|
||||
protected InstrumentationExtension testing() {
|
||||
return testing;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import io.opentelemetry.instrumentation.rxjava.v3.common.AbstractRxJava3Test;
|
||||
import io.opentelemetry.instrumentation.testing.junit.AgentInstrumentationExtension;
|
||||
import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
|
||||
public class RxJava3Test extends AbstractRxJava3Test {
|
||||
@RegisterExtension
|
||||
static final InstrumentationExtension testing = AgentInstrumentationExtension.create();
|
||||
|
||||
@Override
|
||||
protected InstrumentationExtension testing() {
|
||||
return testing;
|
||||
}
|
||||
}
|
|
@ -1,18 +0,0 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import io.opentelemetry.instrumentation.rxjava.v3.common.AbstractRxJava3SubscriptionTest
|
||||
import io.opentelemetry.instrumentation.rxjava.v3_0.TracingAssembly
|
||||
import io.opentelemetry.instrumentation.test.LibraryTestTrait
|
||||
import spock.lang.Shared
|
||||
|
||||
class RxJava3SubscriptionTest extends AbstractRxJava3SubscriptionTest implements LibraryTestTrait {
|
||||
@Shared
|
||||
TracingAssembly tracingAssembly = TracingAssembly.create()
|
||||
|
||||
def setupSpec() {
|
||||
tracingAssembly.enable()
|
||||
}
|
||||
}
|
|
@ -1,18 +0,0 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import io.opentelemetry.instrumentation.rxjava.v3.common.AbstractRxJava3Test
|
||||
import io.opentelemetry.instrumentation.rxjava.v3_0.TracingAssembly
|
||||
import io.opentelemetry.instrumentation.test.LibraryTestTrait
|
||||
import spock.lang.Shared
|
||||
|
||||
class RxJava3Test extends AbstractRxJava3Test implements LibraryTestTrait {
|
||||
@Shared
|
||||
TracingAssembly tracingAssembly = TracingAssembly.create()
|
||||
|
||||
def setupSpec() {
|
||||
tracingAssembly.enable()
|
||||
}
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import io.opentelemetry.instrumentation.rxjava.v3.common.AbstractRxJava3SubscriptionTest;
|
||||
import io.opentelemetry.instrumentation.rxjava.v3_0.TracingAssembly;
|
||||
import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension;
|
||||
import io.opentelemetry.instrumentation.testing.junit.LibraryInstrumentationExtension;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
|
||||
public class RxJava3SubscriptionTest extends AbstractRxJava3SubscriptionTest {
|
||||
|
||||
@RegisterExtension
|
||||
static final InstrumentationExtension testing = LibraryInstrumentationExtension.create();
|
||||
|
||||
@Override
|
||||
protected InstrumentationExtension testing() {
|
||||
return testing;
|
||||
}
|
||||
|
||||
static TracingAssembly tracingAssembly = TracingAssembly.create();
|
||||
|
||||
@BeforeAll
|
||||
public static void setupSpec() {
|
||||
tracingAssembly.enable();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import io.opentelemetry.instrumentation.rxjava.v3.common.AbstractRxJava3Test;
|
||||
import io.opentelemetry.instrumentation.rxjava.v3_0.TracingAssembly;
|
||||
import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension;
|
||||
import io.opentelemetry.instrumentation.testing.junit.LibraryInstrumentationExtension;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
|
||||
public class RxJava3Test extends AbstractRxJava3Test {
|
||||
@RegisterExtension
|
||||
static final InstrumentationExtension testing = LibraryInstrumentationExtension.create();
|
||||
|
||||
static TracingAssembly tracingAssembly = TracingAssembly.create();
|
||||
|
||||
@Override
|
||||
protected InstrumentationExtension testing() {
|
||||
return testing;
|
||||
}
|
||||
|
||||
@BeforeAll
|
||||
public void setupSpec() {
|
||||
tracingAssembly.enable();
|
||||
}
|
||||
}
|
|
@ -1,16 +0,0 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import io.opentelemetry.instrumentation.rxjava.v3.common.AbstractRxJava3WithSpanTest
|
||||
import io.opentelemetry.instrumentation.rxjava.v3.common.AbstractTracedWithSpan
|
||||
import io.opentelemetry.instrumentation.rxjava.v3.common.extensionannotation.TracedWithSpan
|
||||
|
||||
class RxJava3ExtensionWithSpanTest extends AbstractRxJava3WithSpanTest {
|
||||
|
||||
@Override
|
||||
AbstractTracedWithSpan newTraced() {
|
||||
return new TracedWithSpan()
|
||||
}
|
||||
}
|
|
@ -1,16 +0,0 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import io.opentelemetry.instrumentation.rxjava.v3.common.AbstractRxJava3WithSpanTest
|
||||
import io.opentelemetry.instrumentation.rxjava.v3.common.AbstractTracedWithSpan
|
||||
import io.opentelemetry.instrumentation.rxjava.v3.common.instrumentationannotation.TracedWithSpan
|
||||
|
||||
class RxJava3InstrumentationWithSpanTest extends AbstractRxJava3WithSpanTest {
|
||||
|
||||
@Override
|
||||
AbstractTracedWithSpan newTraced() {
|
||||
return new TracedWithSpan()
|
||||
}
|
||||
}
|
|
@ -1,10 +0,0 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import io.opentelemetry.instrumentation.rxjava.v3.common.AbstractRxJava3SubscriptionTest
|
||||
import io.opentelemetry.instrumentation.test.AgentTestTrait
|
||||
|
||||
class RxJava3SubscriptionTest extends AbstractRxJava3SubscriptionTest implements AgentTestTrait {
|
||||
}
|
|
@ -1,10 +0,0 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import io.opentelemetry.instrumentation.rxjava.v3.common.AbstractRxJava3Test
|
||||
import io.opentelemetry.instrumentation.test.AgentTestTrait
|
||||
|
||||
class RxJava3Test extends AbstractRxJava3Test implements AgentTestTrait {
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import io.opentelemetry.instrumentation.rxjava.v3.common.AbstractRxJava3WithSpanTest;
|
||||
import io.opentelemetry.instrumentation.rxjava.v3.common.AbstractTracedWithSpan;
|
||||
import io.opentelemetry.instrumentation.rxjava.v3.common.extensionannotation.TracedWithSpan;
|
||||
import io.opentelemetry.instrumentation.testing.junit.AgentInstrumentationExtension;
|
||||
import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
|
||||
public class RxJava3ExtensionWithSpanTest extends AbstractRxJava3WithSpanTest {
|
||||
@RegisterExtension
|
||||
static final InstrumentationExtension testing = AgentInstrumentationExtension.create();
|
||||
|
||||
@Override
|
||||
protected AbstractTracedWithSpan newTraced() {
|
||||
return new TracedWithSpan();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected InstrumentationExtension testing() {
|
||||
return testing;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import io.opentelemetry.instrumentation.rxjava.v3.common.AbstractRxJava3WithSpanTest;
|
||||
import io.opentelemetry.instrumentation.rxjava.v3.common.AbstractTracedWithSpan;
|
||||
import io.opentelemetry.instrumentation.rxjava.v3.common.extensionannotation.TracedWithSpan;
|
||||
import io.opentelemetry.instrumentation.testing.junit.AgentInstrumentationExtension;
|
||||
import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
|
||||
public class RxJava3InstrumentationWithSpanTest extends AbstractRxJava3WithSpanTest {
|
||||
@RegisterExtension
|
||||
static final InstrumentationExtension testing = AgentInstrumentationExtension.create();
|
||||
|
||||
@Override
|
||||
protected AbstractTracedWithSpan newTraced() {
|
||||
return new TracedWithSpan();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected InstrumentationExtension testing() {
|
||||
return testing;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import io.opentelemetry.instrumentation.rxjava.v3.common.AbstractRxJava3SubscriptionTest;
|
||||
import io.opentelemetry.instrumentation.testing.junit.AgentInstrumentationExtension;
|
||||
import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
|
||||
public class RxJava3SubscriptionTest extends AbstractRxJava3SubscriptionTest {
|
||||
@RegisterExtension
|
||||
static final InstrumentationExtension testing = AgentInstrumentationExtension.create();
|
||||
|
||||
@Override
|
||||
protected InstrumentationExtension testing() {
|
||||
return testing;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import io.opentelemetry.instrumentation.rxjava.v3.common.AbstractRxJava3Test;
|
||||
import io.opentelemetry.instrumentation.testing.junit.AgentInstrumentationExtension;
|
||||
import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
|
||||
public class RxJava3Test extends AbstractRxJava3Test {
|
||||
@RegisterExtension
|
||||
static final InstrumentationExtension testing = AgentInstrumentationExtension.create();
|
||||
|
||||
@Override
|
||||
protected InstrumentationExtension testing() {
|
||||
return testing;
|
||||
}
|
||||
}
|
|
@ -1,18 +0,0 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import io.opentelemetry.instrumentation.rxjava.v3.common.AbstractRxJava3SubscriptionTest
|
||||
import io.opentelemetry.instrumentation.rxjava.v3_1_1.TracingAssembly
|
||||
import io.opentelemetry.instrumentation.test.LibraryTestTrait
|
||||
import spock.lang.Shared
|
||||
|
||||
class RxJava3SubscriptionTest extends AbstractRxJava3SubscriptionTest implements LibraryTestTrait {
|
||||
@Shared
|
||||
TracingAssembly tracingAssembly = TracingAssembly.create()
|
||||
|
||||
def setupSpec() {
|
||||
tracingAssembly.enable()
|
||||
}
|
||||
}
|
|
@ -1,18 +0,0 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import io.opentelemetry.instrumentation.rxjava.v3.common.AbstractRxJava3Test
|
||||
import io.opentelemetry.instrumentation.rxjava.v3_1_1.TracingAssembly
|
||||
import io.opentelemetry.instrumentation.test.LibraryTestTrait
|
||||
import spock.lang.Shared
|
||||
|
||||
class RxJava3Test extends AbstractRxJava3Test implements LibraryTestTrait {
|
||||
@Shared
|
||||
TracingAssembly tracingAssembly = TracingAssembly.create()
|
||||
|
||||
def setupSpec() {
|
||||
tracingAssembly.enable()
|
||||
}
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import io.opentelemetry.instrumentation.rxjava.v3.common.AbstractRxJava3SubscriptionTest;
|
||||
import io.opentelemetry.instrumentation.rxjava.v3_1_1.TracingAssembly;
|
||||
import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension;
|
||||
import io.opentelemetry.instrumentation.testing.junit.LibraryInstrumentationExtension;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
|
||||
public class RxJava3SubscriptionTest extends AbstractRxJava3SubscriptionTest {
|
||||
|
||||
@RegisterExtension
|
||||
static final InstrumentationExtension testing = LibraryInstrumentationExtension.create();
|
||||
|
||||
@Override
|
||||
protected InstrumentationExtension testing() {
|
||||
return testing;
|
||||
}
|
||||
|
||||
static TracingAssembly tracingAssembly = TracingAssembly.create();
|
||||
|
||||
@BeforeAll
|
||||
public static void setupSpec() {
|
||||
tracingAssembly.enable();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import io.opentelemetry.instrumentation.rxjava.v3.common.AbstractRxJava3Test;
|
||||
import io.opentelemetry.instrumentation.rxjava.v3_1_1.TracingAssembly;
|
||||
import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension;
|
||||
import io.opentelemetry.instrumentation.testing.junit.LibraryInstrumentationExtension;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
|
||||
public class RxJava3Test extends AbstractRxJava3Test {
|
||||
@RegisterExtension
|
||||
static final InstrumentationExtension testing = LibraryInstrumentationExtension.create();
|
||||
|
||||
static TracingAssembly tracingAssembly = TracingAssembly.create();
|
||||
|
||||
@Override
|
||||
protected InstrumentationExtension testing() {
|
||||
return testing;
|
||||
}
|
||||
|
||||
@BeforeAll
|
||||
public void setupSpec() {
|
||||
tracingAssembly.enable();
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue