Skip to content

Commit ac9e374

Browse files
authored
Updates to azure-core-test (Azure#37140)
Updates to azure-core-test
1 parent 2881b7f commit ac9e374

File tree

8 files changed

+138
-72
lines changed

8 files changed

+138
-72
lines changed

eng/code-quality-reports/src/main/resources/checkstyle/checkstyle-suppressions.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -509,7 +509,7 @@ the main ServiceBusClientBuilder. -->
509509
<suppress checks="ThrowFromClientLogger" files="com.azure.security.attestation.implementation.models.*\.java"/>
510510

511511
<suppress checks="com.azure.tools.checkstyle.checks.GoodLoggingCheck"
512-
files="com.azure.core.test.(AzureTestWatcher|ThreadDumper).java"/>
512+
files="com.azure.core.test.(AzureTestWatcher|TestBase|ThreadDumper).java"/>
513513

514514
<!-- This policy is purely an implementation detail and should not be used by customers -->
515515
<suppress checks="com.azure.tools.checkstyle.checks.HttpPipelinePolicyCheck"

sdk/core/azure-core-test/src/main/java/com/azure/core/test/AzureTestWatcher.java

Lines changed: 26 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -3,73 +3,62 @@
33

44
package com.azure.core.test;
55

6-
import com.azure.core.test.implementation.TestRunMetrics;
7-
import com.azure.core.util.Configuration;
86
import org.junit.jupiter.api.extension.AfterTestExecutionCallback;
97
import org.junit.jupiter.api.extension.BeforeTestExecutionCallback;
108
import org.junit.jupiter.api.extension.ExtensionContext;
119

12-
import java.lang.reflect.Method;
13-
import java.util.Objects;
14-
import java.util.function.Supplier;
10+
import static com.azure.core.test.TestBase.getTestName;
11+
import static com.azure.core.test.TestBase.shouldLogExecutionStatus;
1512

1613
/**
1714
* JUnit 5 extension class which reports on testing running and simple metrics about the test such as run time.
1815
*/
1916
public class AzureTestWatcher implements BeforeTestExecutionCallback, AfterTestExecutionCallback {
20-
private static final String AZURE_TEST_DEBUG = "AZURE_TEST_DEBUG";
21-
22-
private static final Supplier<Boolean> SHOULD_LOG_EXECUTION_STATUS = () ->
23-
Boolean.parseBoolean(Configuration.getGlobalConfiguration().get(AZURE_TEST_DEBUG));
24-
2517
/**
2618
* Creates an instance of {@link AzureTestWatcher}.
2719
*/
2820
public AzureTestWatcher() {
2921
}
3022

3123
@Override
32-
public void beforeTestExecution(ExtensionContext extensionContext) {
33-
if (!SHOULD_LOG_EXECUTION_STATUS.get()) {
24+
public void beforeTestExecution(ExtensionContext context) {
25+
// If the test class is an instance of TestBase or is a subtype of TestBase, then we don't need to track
26+
// anything here as TestBase handles this logic in it's Before and After test methods.
27+
Class<?> clazz = context.getTestClass().orElse(null);
28+
if (clazz != null && TestBase.class.isAssignableFrom(clazz)) {
3429
return;
3530
}
3631

37-
String displayName = extensionContext.getDisplayName();
38-
39-
String testName = "";
40-
String fullyQualifiedTestName = "";
41-
if (extensionContext.getTestMethod().isPresent()) {
42-
Method method = extensionContext.getTestMethod().get();
43-
testName = method.getName();
44-
fullyQualifiedTestName = method.getDeclaringClass().getName() + "." + testName;
45-
}
46-
47-
StringBuilder logPrefixBuilder = new StringBuilder("Starting test ")
48-
.append(fullyQualifiedTestName);
49-
50-
if (!Objects.equals(displayName, testName)) {
51-
logPrefixBuilder.append("(")
52-
.append(displayName)
53-
.append(")");
32+
// Check if test debugging is enabled to determine whether logging should happen.
33+
if (!shouldLogExecutionStatus()) {
34+
return;
5435
}
5536

56-
logPrefixBuilder.append(",");
37+
String testName = getTestName(context.getTestMethod(), context.getDisplayName());
38+
System.out.println("Starting test " + testName + ".");
5739

58-
getStore(extensionContext).put(extensionContext.getRequiredTestMethod(),
59-
new TestRunMetrics(logPrefixBuilder.toString(), System.currentTimeMillis()));
40+
getStore(context).put(context.getRequiredTestMethod(), System.currentTimeMillis());
6041
}
6142

6243
@Override
6344
public void afterTestExecution(ExtensionContext context) {
64-
if (!SHOULD_LOG_EXECUTION_STATUS.get()) {
45+
// If the test class is an instance of TestBase or is a subtype of TestBase, then we don't need to track
46+
// anything here as TestBase handles this logic in it's Before and After test methods.
47+
Class<?> clazz = context.getTestClass().orElse(null);
48+
if (clazz != null && TestBase.class.isAssignableFrom(clazz)) {
49+
return;
50+
}
51+
52+
// Check if test debugging is enabled to determine whether logging should happen.
53+
if (!shouldLogExecutionStatus()) {
6554
return;
6655
}
6756

68-
TestRunMetrics testInformation = getStore(context)
69-
.remove(context.getRequiredTestMethod(), TestRunMetrics.class);
70-
long duration = System.currentTimeMillis() - testInformation.getStartMillis();
57+
long startMillis = getStore(context).remove(context.getRequiredTestMethod(), long.class);
58+
long duration = System.currentTimeMillis() - startMillis;
7159

72-
System.out.printf("%s completed in %d ms.%n", testInformation.getLogPrefix(), duration);
60+
String testName = getTestName(context.getTestMethod(), context.getDisplayName());
61+
System.out.println("Finished test " + testName + " in " + duration + " ms.");
7362
}
7463

7564
private static ExtensionContext.Store getStore(ExtensionContext context) {

sdk/core/azure-core-test/src/main/java/com/azure/core/test/InterceptorManager.java

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -543,4 +543,24 @@ public void setProxyRecordingOptions(TestProxyRecordingOptions testProxyRecordin
543543
throw new RuntimeException("Recording must have been started before setting recording options.");
544544
}
545545
}
546+
547+
/**
548+
* Gets the name of the running test.
549+
*
550+
* @return Name of the running test.
551+
*/
552+
public String getTestName() {
553+
return testName;
554+
}
555+
556+
/**
557+
* Gets the name of the playback record.
558+
* <p>
559+
* The playback record name is equivalent to {@code <testClass>.<testMethod>[<testIteration>]}.
560+
*
561+
* @return Name of the playback record.
562+
*/
563+
public String getPlaybackRecordName() {
564+
return playbackRecordName;
565+
}
546566
}

sdk/core/azure-core-test/src/main/java/com/azure/core/test/TestBase.java

Lines changed: 62 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import com.azure.core.test.http.PlaybackClient;
99
import com.azure.core.test.implementation.TestIterationContext;
1010
import com.azure.core.test.implementation.TestingHelpers;
11+
import com.azure.core.test.utils.HttpURLConnectionHttpClient;
1112
import com.azure.core.test.utils.TestResourceNamer;
1213
import com.azure.core.util.Configuration;
1314
import com.azure.core.util.CoreUtils;
@@ -32,7 +33,10 @@
3233
import java.util.ArrayList;
3334
import java.util.List;
3435
import java.util.Locale;
36+
import java.util.Objects;
37+
import java.util.Optional;
3538
import java.util.ServiceLoader;
39+
import java.util.concurrent.atomic.AtomicReference;
3640
import java.util.stream.Stream;
3741

3842
import static com.azure.core.test.utils.TestUtils.toURI;
@@ -41,6 +45,8 @@
4145
* Base class for running live and playback tests using {@link InterceptorManager}.
4246
*/
4347
public abstract class TestBase implements BeforeEachCallback {
48+
private static final String AZURE_TEST_DEBUG = "AZURE_TEST_DEBUG";
49+
4450
// Environment variable name used to determine the TestMode.
4551
private static final String AZURE_TEST_HTTP_CLIENTS = "AZURE_TEST_HTTP_CLIENTS";
4652

@@ -70,6 +76,8 @@ public abstract class TestBase implements BeforeEachCallback {
7076
private static final boolean DEFAULT_TO_NETTY = CoreUtils.isNullOrEmpty(CONFIGURED_HTTP_CLIENTS_TO_TEST);
7177
private static final List<String> CONFIGURED_HTTP_CLIENTS;
7278

79+
private static final AtomicReference<HttpClient> TEST_PROXY_HTTP_CLIENT = new AtomicReference<>();
80+
7381
static {
7482
CONFIGURED_HTTP_CLIENTS = new ArrayList<>();
7583

@@ -115,6 +123,8 @@ public abstract class TestBase implements BeforeEachCallback {
115123

116124
private URL proxyUrl;
117125

126+
private long testStartTimeMillis;
127+
118128
/**
119129
* Creates a new instance of {@link TestBase}.
120130
*/
@@ -152,6 +162,8 @@ public void setupTest(TestInfo testInfo) {
152162
} else if (testInfo.getTags().contains("Live")) {
153163
localTestMode = TestMode.LIVE;
154164
}
165+
166+
String testName = getTestName(testInfo.getTestMethod(), testInfo.getDisplayName());
155167
Path testClassPath = Paths.get(toURI(testInfo.getTestClass().get().getResource(testInfo.getTestClass().get().getSimpleName() + ".class")));
156168
this.testContextManager =
157169
new TestContextManager(testInfo.getTestMethod().get(),
@@ -160,7 +172,13 @@ public void setupTest(TestInfo testInfo) {
160172
testInfo.getTestClass().get().getAnnotation(RecordWithoutRequestBody.class) != null,
161173
testClassPath);
162174
testContextManager.setTestIteration(testIterationContext.getTestIteration());
163-
logger.info("Test Mode: {}, Name: {}", localTestMode, testContextManager.getTestName());
175+
ThreadDumper.addRunningTest(testName);
176+
logger.info("Test Mode: {}, Name: {}", localTestMode, testName);
177+
178+
if (shouldLogExecutionStatus()) {
179+
System.out.println("Starting test " + testName + ".");
180+
testStartTimeMillis = System.currentTimeMillis();
181+
}
164182

165183
try {
166184
interceptorManager = new InterceptorManager(testContextManager);
@@ -170,7 +188,7 @@ public void setupTest(TestInfo testInfo) {
170188
}
171189

172190
if (isTestProxyEnabled()) {
173-
interceptorManager.setHttpClient(getHttpClients().findFirst().orElse(null));
191+
interceptorManager.setHttpClient(getTestProxyHttpClient());
174192
// The supplier/consumer are used to retrieve/store variables over the wire.
175193
testResourceNamer = new TestResourceNamer(testContextManager,
176194
interceptorManager.getProxyVariableConsumer(),
@@ -196,9 +214,24 @@ public void setupTest(TestInfo testInfo) {
196214
*/
197215
@AfterEach
198216
public void teardownTest(TestInfo testInfo) {
199-
if (testContextManager != null && testContextManager.didTestRun()) {
200-
afterTest();
201-
interceptorManager.close();
217+
if (shouldLogExecutionStatus()) {
218+
String testName = getTestName(testInfo.getTestMethod(), testInfo.getDisplayName());
219+
220+
if (testStartTimeMillis > 0) {
221+
long duration = System.currentTimeMillis() - testStartTimeMillis;
222+
System.out.println("Finished test " + testName + " in " + duration + " ms.");
223+
} else {
224+
System.out.println("Finished test " + testName + ", duration unknown.");
225+
}
226+
}
227+
228+
if (testContextManager != null) {
229+
ThreadDumper.removeRunningTest(testContextManager.getTestPlaybackRecordingName());
230+
231+
if (testContextManager.didTestRun()) {
232+
afterTest();
233+
interceptorManager.close();
234+
}
202235
}
203236
}
204237

@@ -396,4 +429,28 @@ protected <T, U> PollerFlux<T, U> setPlaybackPollerFluxPollInterval(PollerFlux<T
396429
protected HttpClient getHttpClientOrUsePlayback(HttpClient httpClient) {
397430
return (testMode == TestMode.PLAYBACK) ? interceptorManager.getPlaybackClient() : httpClient;
398431
}
432+
433+
private static HttpClient getTestProxyHttpClient() {
434+
return TEST_PROXY_HTTP_CLIENT.updateAndGet(httpClient -> httpClient == null
435+
? getHttpClients().findFirst().orElse(new HttpURLConnectionHttpClient())
436+
: httpClient);
437+
}
438+
439+
static String getTestName(Optional<Method> testMethod, String displayName) {
440+
String testName = "";
441+
String fullyQualifiedTestName = "";
442+
if (testMethod.isPresent()) {
443+
Method method = testMethod.get();
444+
testName = method.getName();
445+
fullyQualifiedTestName = method.getDeclaringClass().getName() + "." + testName;
446+
}
447+
448+
return Objects.equals(displayName, testName)
449+
? fullyQualifiedTestName
450+
: fullyQualifiedTestName + "(" + displayName + ")";
451+
}
452+
453+
static boolean shouldLogExecutionStatus() {
454+
return Configuration.getGlobalConfiguration().get(AZURE_TEST_DEBUG, false);
455+
}
399456
}

sdk/core/azure-core-test/src/main/java/com/azure/core/test/ThreadDumper.java

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,14 +124,36 @@ public void beforeAll(ExtensionContext context) {
124124

125125
@Override
126126
public void beforeEach(ExtensionContext context) {
127+
// If the test class is an instance of TestBase or is a subtype of TestBase, then we don't need to track
128+
// anything here as TestBase handles this logic in it's Before and After test methods.
129+
Class<?> clazz = context.getTestClass().orElse(null);
130+
if (clazz != null && TestBase.class.isAssignableFrom(clazz)) {
131+
return;
132+
}
133+
127134
RUNNING_TEST_TIMES.put(getFullTestName(context), System.currentTimeMillis());
128135
}
129136

137+
static void addRunningTest(String testName) {
138+
RUNNING_TEST_TIMES.put(testName, System.currentTimeMillis());
139+
}
140+
130141
@Override
131142
public void afterEach(ExtensionContext context) {
143+
// If the test class is an instance of TestBase or is a subtype of TestBase, then we don't need to track
144+
// anything here as TestBase handles this logic in it's Before and After test methods.
145+
Class<?> clazz = context.getTestClass().orElse(null);
146+
if (clazz != null && TestBase.class.isAssignableFrom(clazz)) {
147+
return;
148+
}
149+
132150
RUNNING_TEST_TIMES.remove(getFullTestName(context));
133151
}
134152

153+
static void removeRunningTest(String testName) {
154+
RUNNING_TEST_TIMES.remove(testName);
155+
}
156+
135157
private static String getFullTestName(ExtensionContext context) {
136158
String displayName = context.getDisplayName();
137159

sdk/core/azure-core-test/src/main/java/com/azure/core/test/implementation/TestRunMetrics.java

Lines changed: 0 additions & 25 deletions
This file was deleted.

sdk/core/azure-core-test/src/main/java/com/azure/core/test/policy/TestProxyRecordPolicy.java

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,9 @@ public void startRecording(File recordFile, Path testClassPath) {
8383

8484
try (HttpResponse response = client.sendSync(request, Context.NONE)) {
8585
checkForTestProxyErrors(response);
86+
if (response.getStatusCode() != 200) {
87+
throw new RuntimeException(response.getBodyAsBinaryData().toString());
88+
}
8689

8790
this.xRecordingId = response.getHeaderValue(X_RECORDING_ID);
8891
}
@@ -111,11 +114,10 @@ public void stopRecording(Queue<String> variables) {
111114
.setHeader(HttpHeaderName.CONTENT_TYPE, "application/json")
112115
.setHeader(X_RECORDING_ID, xRecordingId)
113116
.setBody(serializeVariables(variables));
114-
117+
115118
try (HttpResponse response = client.sendSync(request, Context.NONE)) {
116119
checkForTestProxyErrors(response);
117-
118-
if (response.getStatusCode() == 400) {
120+
if (response.getStatusCode() != 200) {
119121
throw new RuntimeException(response.getBodyAsBinaryData().toString());
120122
}
121123
}

sdk/core/azure-core-test/src/main/java/com/azure/core/test/utils/TestProxyManager.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,8 @@ public static synchronized void startProxy() {
6363
}
6464

6565
ProcessBuilder builder = new ProcessBuilder(commandLine, "--storage-location", repoRoot.toString())
66-
.redirectOutput(repoRootTarget.resolve("test-proxy.log").toFile());
66+
.redirectOutput(repoRootTarget.resolve("test-proxy.log").toFile())
67+
.redirectError(repoRootTarget.resolve("test-proxy-error.log").toFile());
6768
Map<String, String> environment = builder.environment();
6869
environment.put("LOGGING__LOGLEVEL", "Debug");
6970
environment.put("LOGGING__LOGLEVEL__MICROSOFT", "Debug");

0 commit comments

Comments
 (0)