Skip to content

Commit 5c1ee8b

Browse files
Meet code coverage requirements
1 parent 3ef07e3 commit 5c1ee8b

3 files changed

Lines changed: 637 additions & 0 deletions

File tree

Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.exporter.zipkin.internal.copied;
7+
8+
import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat;
9+
import static org.mockito.Mockito.atLeastOnce;
10+
import static org.mockito.Mockito.mock;
11+
import static org.mockito.Mockito.verify;
12+
import static org.mockito.Mockito.verifyNoInteractions;
13+
import static org.mockito.Mockito.verifyNoMoreInteractions;
14+
import static org.mockito.Mockito.when;
15+
16+
import io.opentelemetry.api.metrics.MeterProvider;
17+
import io.opentelemetry.sdk.common.CompletableResultCode;
18+
import io.opentelemetry.sdk.common.InternalTelemetryVersion;
19+
import io.opentelemetry.sdk.metrics.InstrumentType;
20+
import io.opentelemetry.sdk.metrics.SdkMeterProvider;
21+
import io.opentelemetry.sdk.metrics.data.AggregationTemporality;
22+
import io.opentelemetry.sdk.metrics.export.CollectionRegistration;
23+
import io.opentelemetry.sdk.metrics.export.MetricReader;
24+
import java.util.function.Supplier;
25+
import org.junit.jupiter.api.Test;
26+
import org.junit.jupiter.params.ParameterizedTest;
27+
import org.junit.jupiter.params.provider.EnumSource;
28+
import org.mockito.Mockito;
29+
30+
class ExporterInstrumentationTest {
31+
32+
@SuppressWarnings("unchecked")
33+
Supplier<MeterProvider> meterProviderSupplier = mock(Supplier.class);
34+
35+
@ParameterizedTest
36+
@EnumSource
37+
void validMeterProvider(InternalTelemetryVersion schemaVersion) {
38+
when(meterProviderSupplier.get())
39+
.thenReturn(
40+
SdkMeterProvider.builder()
41+
// Have to provide a valid reader.
42+
.registerMetricReader(
43+
new MetricReader() {
44+
@Override
45+
public void register(CollectionRegistration registration) {}
46+
47+
@Override
48+
public CompletableResultCode forceFlush() {
49+
return CompletableResultCode.ofSuccess();
50+
}
51+
52+
@Override
53+
public CompletableResultCode shutdown() {
54+
return CompletableResultCode.ofSuccess();
55+
}
56+
57+
@Override
58+
public AggregationTemporality getAggregationTemporality(
59+
InstrumentType instrumentType) {
60+
return AggregationTemporality.CUMULATIVE;
61+
}
62+
})
63+
.build());
64+
ExporterInstrumentation instrumentation =
65+
new ExporterInstrumentation(
66+
schemaVersion,
67+
meterProviderSupplier,
68+
ComponentId.generateLazy(StandardComponentId.ExporterType.OTLP_GRPC_SPAN_EXPORTER),
69+
"http://testing:1234");
70+
verifyNoInteractions(meterProviderSupplier); // Ensure lazy
71+
72+
// Verify the supplier is only called once per underlying meter.
73+
74+
instrumentation.startRecordingExport(42).finishFailed("foo");
75+
instrumentation.startRecordingExport(42).finishSuccessful();
76+
verify(meterProviderSupplier, atLeastOnce()).get();
77+
78+
instrumentation.startRecordingExport(42).finishFailed("foo");
79+
instrumentation.startRecordingExport(42).finishSuccessful();
80+
verifyNoMoreInteractions(meterProviderSupplier);
81+
}
82+
83+
@ParameterizedTest
84+
@EnumSource
85+
void noopMeterProvider(InternalTelemetryVersion schemaVersion) {
86+
87+
when(meterProviderSupplier.get()).thenReturn(MeterProvider.noop());
88+
ExporterInstrumentation instrumentation =
89+
new ExporterInstrumentation(
90+
schemaVersion,
91+
meterProviderSupplier,
92+
ComponentId.generateLazy(StandardComponentId.ExporterType.OTLP_GRPC_SPAN_EXPORTER),
93+
"http://testing:1234");
94+
verifyNoInteractions(meterProviderSupplier); // Ensure lazy
95+
96+
// Verify the supplier is invoked multiple times since it returns a noop meter.
97+
instrumentation.startRecordingExport(42).finishFailed("foo");
98+
instrumentation.startRecordingExport(42).finishSuccessful();
99+
verify(meterProviderSupplier, atLeastOnce()).get();
100+
101+
Mockito.clearInvocations((Object) meterProviderSupplier);
102+
instrumentation.startRecordingExport(42).finishFailed("foo");
103+
instrumentation.startRecordingExport(42).finishSuccessful();
104+
verify(meterProviderSupplier, atLeastOnce()).get();
105+
}
106+
107+
@Test
108+
void serverAttributesInvalidUrl() {
109+
assertThat(ExporterInstrumentation.extractServerAttributes("^")).isEmpty();
110+
}
111+
112+
@Test
113+
void serverAttributesEmptyUrl() {
114+
assertThat(ExporterInstrumentation.extractServerAttributes("")).isEmpty();
115+
}
116+
117+
@Test
118+
void serverAttributesHttps() {
119+
assertThat(ExporterInstrumentation.extractServerAttributes("https://example.com/foo/bar?a=b"))
120+
.hasSize(2)
121+
.containsEntry(SemConvAttributes.SERVER_ADDRESS, "example.com")
122+
.containsEntry(SemConvAttributes.SERVER_PORT, 443);
123+
124+
assertThat(
125+
ExporterInstrumentation.extractServerAttributes("https://example.com:1234/foo/bar?a=b"))
126+
.hasSize(2)
127+
.containsEntry(SemConvAttributes.SERVER_ADDRESS, "example.com")
128+
.containsEntry(SemConvAttributes.SERVER_PORT, 1234);
129+
}
130+
131+
@Test
132+
void serverAttributesHttp() {
133+
assertThat(ExporterInstrumentation.extractServerAttributes("http://example.com/foo/bar?a=b"))
134+
.hasSize(2)
135+
.containsEntry(SemConvAttributes.SERVER_ADDRESS, "example.com")
136+
.containsEntry(SemConvAttributes.SERVER_PORT, 80);
137+
138+
assertThat(
139+
ExporterInstrumentation.extractServerAttributes("http://example.com:1234/foo/bar?a=b"))
140+
.hasSize(2)
141+
.containsEntry(SemConvAttributes.SERVER_ADDRESS, "example.com")
142+
.containsEntry(SemConvAttributes.SERVER_PORT, 1234);
143+
}
144+
145+
@Test
146+
void serverAttributesUnknownScheme() {
147+
assertThat(ExporterInstrumentation.extractServerAttributes("custom://foo"))
148+
.hasSize(1)
149+
.containsEntry(SemConvAttributes.SERVER_ADDRESS, "foo");
150+
151+
assertThat(ExporterInstrumentation.extractServerAttributes("custom://foo:1234"))
152+
.hasSize(2)
153+
.containsEntry(SemConvAttributes.SERVER_ADDRESS, "foo")
154+
.containsEntry(SemConvAttributes.SERVER_PORT, 1234);
155+
}
156+
}
Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.exporter.zipkin.internal.copied;
7+
8+
import static org.assertj.core.api.Assertions.assertThat;
9+
10+
import io.opentelemetry.sdk.testing.time.TestClock;
11+
import java.time.Duration;
12+
import java.util.ArrayList;
13+
import java.util.List;
14+
import java.util.concurrent.ExecutionException;
15+
import java.util.concurrent.ExecutorService;
16+
import java.util.concurrent.Executors;
17+
import java.util.concurrent.Future;
18+
import java.util.concurrent.TimeUnit;
19+
import java.util.concurrent.atomic.AtomicInteger;
20+
import org.junit.jupiter.api.Test;
21+
22+
/**
23+
* This class was taken from Jaeger java client.
24+
* https://github.com/jaegertracing/jaeger-client-java/blob/master/jaeger-core/src/test/java/io/jaegertracing/internal/utils/RateLimiterTest.java
25+
*/
26+
class RateLimiterTest {
27+
28+
@Test
29+
void testRateLimiterWholeNumber() {
30+
TestClock clock = TestClock.create();
31+
RateLimiter limiter = new RateLimiter(2.0, 2.0, clock);
32+
33+
assertThat(limiter.trySpend(1.0)).isTrue();
34+
assertThat(limiter.trySpend(1.0)).isTrue();
35+
assertThat(limiter.trySpend(1.0)).isFalse();
36+
// move time 250ms forward, not enough credits to pay for 1.0 item
37+
clock.advance(Duration.ofNanos(TimeUnit.MILLISECONDS.toNanos(250)));
38+
assertThat(limiter.trySpend(1.0)).isFalse();
39+
40+
// move time 500ms forward, now enough credits to pay for 1.0 item
41+
clock.advance(Duration.ofNanos(TimeUnit.MILLISECONDS.toNanos(500)));
42+
43+
assertThat(limiter.trySpend(1.0)).isTrue();
44+
assertThat(limiter.trySpend(1.0)).isFalse();
45+
46+
// move time 5s forward, enough to accumulate credits for 10 messages, but it should still be
47+
// capped at 2
48+
clock.advance(Duration.ofNanos(TimeUnit.MILLISECONDS.toNanos(5000)));
49+
50+
assertThat(limiter.trySpend(1.0)).isTrue();
51+
assertThat(limiter.trySpend(1.0)).isTrue();
52+
assertThat(limiter.trySpend(1.0)).isFalse();
53+
assertThat(limiter.trySpend(1.0)).isFalse();
54+
assertThat(limiter.trySpend(1.0)).isFalse();
55+
}
56+
57+
@Test
58+
void testRateLimiterSteadyRate() {
59+
TestClock clock = TestClock.create();
60+
RateLimiter limiter = new RateLimiter(5.0 / 60.0, 5.0, clock);
61+
for (int i = 0; i < 100; i++) {
62+
assertThat(limiter.trySpend(1.0)).isTrue();
63+
clock.advance(Duration.ofNanos(TimeUnit.SECONDS.toNanos(20)));
64+
}
65+
}
66+
67+
@Test
68+
void cantWithdrawMoreThanMax() {
69+
TestClock clock = TestClock.create();
70+
RateLimiter limiter = new RateLimiter(1, 1.0, clock);
71+
assertThat(limiter.trySpend(2)).isFalse();
72+
}
73+
74+
@Test
75+
void testRateLimiterLessThanOne() {
76+
TestClock clock = TestClock.create();
77+
RateLimiter limiter = new RateLimiter(0.5, 0.5, clock);
78+
79+
assertThat(limiter.trySpend(0.25)).isTrue();
80+
assertThat(limiter.trySpend(0.25)).isTrue();
81+
assertThat(limiter.trySpend(0.25)).isFalse();
82+
// move time 250ms forward, not enough credits to pay for 1.0 item
83+
clock.advance(Duration.ofNanos(TimeUnit.MILLISECONDS.toNanos(250)));
84+
assertThat(limiter.trySpend(0.25)).isFalse();
85+
86+
// move time 500ms forward, now enough credits to pay for 1.0 item
87+
clock.advance(Duration.ofNanos(TimeUnit.MILLISECONDS.toNanos(500)));
88+
89+
assertThat(limiter.trySpend(0.25)).isTrue();
90+
assertThat(limiter.trySpend(0.25)).isFalse();
91+
92+
// move time 5s forward, enough to accumulate credits for 10 messages, but it should still be
93+
// capped at 2
94+
clock.advance(Duration.ofNanos(TimeUnit.MILLISECONDS.toNanos(5000)));
95+
96+
assertThat(limiter.trySpend(0.25)).isTrue();
97+
assertThat(limiter.trySpend(0.25)).isTrue();
98+
assertThat(limiter.trySpend(0.25)).isFalse();
99+
assertThat(limiter.trySpend(0.25)).isFalse();
100+
assertThat(limiter.trySpend(0.25)).isFalse();
101+
}
102+
103+
@Test
104+
void testRateLimiterMaxBalance() {
105+
TestClock clock = TestClock.create();
106+
RateLimiter limiter = new RateLimiter(0.1, 1.0, clock);
107+
108+
clock.advance(Duration.ofNanos(TimeUnit.MICROSECONDS.toNanos(100)));
109+
assertThat(limiter.trySpend(1.0)).isTrue();
110+
assertThat(limiter.trySpend(1.0)).isFalse();
111+
112+
// move time 20s forward, enough to accumulate credits for 2 messages, but it should still be
113+
// capped at 1
114+
clock.advance(Duration.ofNanos(TimeUnit.MILLISECONDS.toNanos(20000)));
115+
116+
assertThat(limiter.trySpend(1.0)).isTrue();
117+
assertThat(limiter.trySpend(1.0)).isFalse();
118+
}
119+
120+
/**
121+
* Validates rate limiter behavior with {@link System#nanoTime()}-like (non-zero) initial nano
122+
* ticks.
123+
*/
124+
@Test
125+
void testRateLimiterInitial() {
126+
TestClock clock = TestClock.create();
127+
RateLimiter limiter = new RateLimiter(1000, 100, clock);
128+
129+
assertThat(limiter.trySpend(100)).isTrue(); // consume initial (max) balance
130+
assertThat(limiter.trySpend(1)).isFalse();
131+
132+
// add 49 credits
133+
clock.advance(Duration.ofNanos(TimeUnit.MILLISECONDS.toNanos(49)));
134+
assertThat(limiter.trySpend(50)).isFalse();
135+
136+
// add one credit
137+
clock.advance(Duration.ofNanos(TimeUnit.MILLISECONDS.toNanos(1)));
138+
assertThat(limiter.trySpend(50)).isTrue(); // consume accrued balance
139+
assertThat(limiter.trySpend(1)).isFalse();
140+
141+
// add a lot of credits (max out balance)
142+
clock.advance(Duration.ofNanos(TimeUnit.MILLISECONDS.toNanos(1_000_000)));
143+
assertThat(limiter.trySpend(1)).isTrue(); // take one credit
144+
145+
// add a lot of credits (max out balance)
146+
clock.advance(Duration.ofNanos(TimeUnit.MILLISECONDS.toNanos(1_000_000)));
147+
assertThat(limiter.trySpend(101)).isFalse(); // can't consume more than max balance
148+
assertThat(limiter.trySpend(100)).isTrue(); // consume max balance
149+
assertThat(limiter.trySpend(1)).isFalse();
150+
}
151+
152+
/** Validates concurrent credit check correctness. */
153+
@Test
154+
void testRateLimiterConcurrency() throws InterruptedException, ExecutionException {
155+
int numWorkers = 8;
156+
ExecutorService executorService = Executors.newFixedThreadPool(numWorkers);
157+
int creditsPerWorker = 1000;
158+
TestClock clock = TestClock.create();
159+
RateLimiter limiter = new RateLimiter(1, numWorkers * creditsPerWorker, clock);
160+
AtomicInteger count = new AtomicInteger();
161+
List<Future<?>> futures = new ArrayList<>(numWorkers);
162+
for (int w = 0; w < numWorkers; ++w) {
163+
Future<?> future =
164+
executorService.submit(
165+
() -> {
166+
for (int i = 0; i < creditsPerWorker * 2; ++i) {
167+
if (limiter.trySpend(1)) {
168+
count.getAndIncrement(); // count allowed operations
169+
}
170+
}
171+
});
172+
futures.add(future);
173+
}
174+
for (Future<?> future : futures) {
175+
future.get();
176+
}
177+
executorService.shutdown();
178+
executorService.awaitTermination(1, TimeUnit.SECONDS);
179+
assertThat(count.get())
180+
.withFailMessage("Exactly the allocated number of credits must be consumed")
181+
.isEqualTo(numWorkers * creditsPerWorker);
182+
assertThat(limiter.trySpend(1)).isFalse();
183+
}
184+
}

0 commit comments

Comments
 (0)