Skip to content

Commit

Permalink
server.tests: Add tests for querying arrows using a Time Graph DP
Browse files Browse the repository at this point in the history
Signed-off-by: Bernd Hufmann <[email protected]>
  • Loading branch information
bhufmann committed Dec 18, 2024
1 parent 2bebf03 commit bf927d5
Show file tree
Hide file tree
Showing 4 changed files with 323 additions and 55 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;

import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.tracecompass.incubator.internal.trace.server.jersey.rest.core.model.views.QueryParameters;
import org.eclipse.tracecompass.incubator.internal.trace.server.jersey.rest.core.services.DataProviderService;
Expand All @@ -44,10 +45,12 @@
import org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests.stubs.OutputElementStyleStub;
import org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests.stubs.OutputStyleModelStub;
import org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests.stubs.StylesOutputResponseStub;
import org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests.stubs.TgArrowsOutputResponseStub;
import org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests.stubs.TgEntryModelStub;
import org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests.stubs.TgStatesOutputResponseStub;
import org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests.stubs.TgTooltipOutputResponseStub;
import org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests.stubs.TgTreeOutputResponseStub;
import org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests.stubs.TimeGraphArrowStub;
import org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests.stubs.TimeGraphEntryStub;
import org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests.stubs.TimeGraphModelStub;
import org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests.stubs.TimeGraphRowStub;
Expand All @@ -70,6 +73,8 @@
*/
@SuppressWarnings({"null", "restriction"})
public class TimeGraphDataProviderServiceTest extends RestServerTest {

private static final String THREAD_STATUS_DP_ID = "org.eclipse.tracecompass.internal.analysis.os.linux.core.threadstatus.ThreadStatusDataProvider";
private static final String DATA_PROVIDER_RESPONSE_FAILED_MSG = "There should be a positive response for the data provider";
private static final String MODEL_NULL_MSG = "The model is null, maybe the analysis did not run long enough?";
private static final int MAX_ITER = 40;
Expand Down Expand Up @@ -159,73 +164,56 @@ public void testStyles() {
@Test
public void testStylesErrors() {
ExperimentModelStub exp = assertPostExperiment(sfContextSwitchesUstNotInitializedStub.getName(), sfContextSwitchesUstNotInitializedStub);
executePostErrorTests(exp, RestServerTest::getStylesEndpoint, CALL_STACK_DATAPROVIDER_ID, false);
}

/**
* Tests querying arrows for a time graph data provider
*
* @throws InterruptedException
* if such exception happens
*/
@Test
public void testArrows() throws InterruptedException {
ExperimentModelStub exp = assertPostExperiment(sfContextSwitchesKernelNotInitializedStub.getName(), sfContextSwitchesKernelNotInitializedStub);

Set<TimeGraphEntryStub> entries = loadDataProvider(exp, THREAD_STATUS_DP_ID);
WebTarget arrowsEndpoint = getArrowsEndpoint(exp.getUUID().toString(), THREAD_STATUS_DP_ID);

// Invalid UUID string
WebTarget stylesEndpoint = getStylesEndpoint(INVALID_EXP_UUID, CALL_STACK_DATAPROVIDER_ID);
Map<String, Object> parameters = new HashMap<>();
try (Response response = stylesEndpoint.request().post(Entity.json(new QueryParameters(parameters, Collections.emptyList())))) {
assertNotNull(response);
assertEquals(Status.NOT_FOUND.getStatusCode(), response.getStatus());
}
parameters.remove(REQUESTED_TIMES_KEY);
parameters.put(REQUESTED_TIMERANGE_KEY, ImmutableMap.of(START, 1450193714978685130L, END, 1450193715011015823L, NB_TIMES, 1000));

// Unknown experiment
stylesEndpoint = getStylesEndpoint(UUID.randomUUID().toString(), CALL_STACK_DATAPROVIDER_ID);
try (Response response = stylesEndpoint.request().post(Entity.json(new QueryParameters(parameters, Collections.emptyList())))) {
assertNotNull(response);
assertEquals(Status.NOT_FOUND.getStatusCode(), response.getStatus());
assertEquals(EndpointConstants.NO_SUCH_TRACE, response.readEntity(String.class));
}
try (Response arrowsResponse = arrowsEndpoint.request().post(Entity.json(new QueryParameters(parameters, Collections.emptyList())))) {
assertEquals(DATA_PROVIDER_RESPONSE_FAILED_MSG, Status.OK.getStatusCode(), arrowsResponse.getStatus());

// Missing parameters
stylesEndpoint = getStylesEndpoint(exp.getUUID().toString(), CALL_STACK_DATAPROVIDER_ID);
try (Response response = stylesEndpoint.request().post(Entity.json(NO_PARAMETERS))) {
assertNotNull(response);
assertEquals(Status.BAD_REQUEST.getStatusCode(), response.getStatus());
}
TgArrowsOutputResponseStub tgArrowsModelResponse = arrowsResponse.readEntity(TgArrowsOutputResponseStub.class);
assertNotNull(tgArrowsModelResponse);

// Unknown data provider
stylesEndpoint = getStylesEndpoint(exp.getUUID().toString(), UNKNOWN_DP_ID);
try (Response response = stylesEndpoint.request().post(Entity.json(new QueryParameters(parameters, Collections.emptyList())))) {
assertNotNull(response);
assertEquals(Status.METHOD_NOT_ALLOWED.getStatusCode(), response.getStatus());
assertEquals(EndpointConstants.NO_PROVIDER, response.readEntity(String.class));
List<TimeGraphArrowStub> tgModel = tgArrowsModelResponse.getModel();
assertNotNull(tgModel);
assertFalse(tgModel.isEmpty());

TimeGraphArrowStub arrow = tgModel.get(0);
// Verify first arrow in list
verifyArrow(entries, arrow);
}
}

/**
* Tests error cases when querying arrows for a time graph data provider
*/
@Test
public void testArrowsErrors() {
ExperimentModelStub exp = assertPostExperiment(sfContextSwitchesKernelNotInitializedStub.getName(), sfContextSwitchesKernelNotInitializedStub);
executePostErrorTests(exp, RestServerTest::getArrowsEndpoint, THREAD_STATUS_DP_ID, true);
}

private static void testGetStates(String filterStrategy) throws InterruptedException {
long start = 1450193697034689597L;
long end = 1450193745774189602L;
try {
ExperimentModelStub exp = assertPostExperiment(sfContextSwitchesUstNotInitializedStub.getName(), sfContextSwitchesUstNotInitializedStub);
Set<TimeGraphEntryStub> entries = loadDataProvider(exp, CALL_STACK_DATAPROVIDER_ID);

// Test getting the time graph tree
WebTarget callstackTree = getTimeGraphTreeEndpoint(exp.getUUID().toString(), CALL_STACK_DATAPROVIDER_ID);

Map<String, Object> parameters = new HashMap<>();
TgTreeOutputResponseStub responseModel;
parameters.put(REQUESTED_TIMES_KEY, List.of(start, end));
try (Response treeResponse = callstackTree.request().post(Entity.json(new QueryParameters(parameters, Collections.emptyList())))) {
assertEquals(DATA_PROVIDER_RESPONSE_FAILED_MSG, 200, treeResponse.getStatus());
responseModel = treeResponse.readEntity(TgTreeOutputResponseStub.class);
assertNotNull(responseModel);
}

// Make sure the analysis ran enough and we have a model
int iteration = 0;
while ((responseModel.isRunning() || responseModel.getModel() == null) && iteration < MAX_ITER) {
Thread.sleep(100);
try (Response treeResponse = callstackTree.request().post(Entity.json(new QueryParameters(parameters, Collections.emptyList())))) {
assertEquals(DATA_PROVIDER_RESPONSE_FAILED_MSG, 200, treeResponse.getStatus());
responseModel = treeResponse.readEntity(TgTreeOutputResponseStub.class);
assertNotNull(responseModel);
iteration++;
}
}

TgEntryModelStub model = responseModel.getModel();
assertNotNull(MODEL_NULL_MSG + responseModel, model);
Set<TimeGraphEntryStub> entries = model.getEntries();
assertFalse(entries.isEmpty());
// add entries for the states query, and make sure they don't have
// extra time fields
List<Integer> items = new ArrayList<>();
Expand All @@ -236,6 +224,7 @@ private static void testGetStates(String filterStrategy) throws InterruptedExcep

// Test getting the time graph row data
WebTarget tgStatesEnpoint = getTimeGraphStatesEndpoint(exp.getUUID().toString(), CALL_STACK_DATAPROVIDER_ID);
Map<String, Object> parameters = new HashMap<>();
parameters.remove(REQUESTED_TIMES_KEY);
parameters.put(REQUESTED_TIMERANGE_KEY, ImmutableMap.of(START, 1450193697034689597L, END, 1450193697118480368L, NB_TIMES, 10));
parameters.put(REQUESTED_ITEMS_KEY, items);
Expand Down Expand Up @@ -333,6 +322,36 @@ private static void testGetStates(String filterStrategy) throws InterruptedExcep
}
}

private static @NonNull Set<TimeGraphEntryStub> loadDataProvider(ExperimentModelStub exp, String dataProviderId) throws InterruptedException {
// Test getting the time graph tree
WebTarget callstackTree = getTimeGraphTreeEndpoint(exp.getUUID().toString(), dataProviderId);

Map<String, Object> parameters = new HashMap<>();
TgTreeOutputResponseStub responseModel;
try (Response treeResponse = callstackTree.request().post(Entity.json(new QueryParameters(parameters, Collections.emptyList())))) {
assertEquals(DATA_PROVIDER_RESPONSE_FAILED_MSG, 200, treeResponse.getStatus());
responseModel = treeResponse.readEntity(TgTreeOutputResponseStub.class);
assertNotNull(responseModel);
}

// Make sure the analysis ran enough and we have a model
int iteration = 0;
while ((responseModel.isRunning() || responseModel.getModel() == null) && iteration < MAX_ITER) {
Thread.sleep(100);
try (Response treeResponse = callstackTree.request().post(Entity.json(new QueryParameters(parameters, Collections.emptyList())))) {
assertEquals(DATA_PROVIDER_RESPONSE_FAILED_MSG, 200, treeResponse.getStatus());
responseModel = treeResponse.readEntity(TgTreeOutputResponseStub.class);
assertNotNull(responseModel);
iteration++;
}
}
TgEntryModelStub model = responseModel.getModel();
assertNotNull(MODEL_NULL_MSG + responseModel, model);
Set<TimeGraphEntryStub> entries = model.getEntries();
assertFalse(entries.isEmpty());
return entries;
}

private static int findCallStackEntry(Set<TimeGraphEntryStub> entries) {
// Find trace entry
Optional<TimeGraphEntryStub> traceOptional = entries.stream().filter(entry -> entry.getParentId() == -1).findFirst();
Expand Down Expand Up @@ -423,4 +442,66 @@ private static void verifyEntry(TimeGraphEntryStub entry) {
assertNull(entry.getMetadata());
}
}

private static void verifyArrow(Set<TimeGraphEntryStub> entries, TimeGraphArrowStub arrow) {
Optional<TimeGraphEntryStub> entryOptional = entries.stream().filter(entry -> entry.getId() == arrow.getSourceId()).findFirst();
assertTrue(entryOptional.isPresent());
TimeGraphEntryStub sourceEntry = entryOptional.get();
assertEquals("lsmod", sourceEntry.getLabels().get(0));

entryOptional = entries.stream().filter(entry -> entry.getId() == arrow.getTargetId()).findFirst();
assertTrue(entryOptional.isPresent());
TimeGraphEntryStub targetEntry = entryOptional.get();
assertEquals("rcu_preempt", targetEntry.getLabels().get(0));

assertNotNull(arrow.getStyle());
assertTrue(arrow.getEndTime() > arrow.getStartTime());
}

private interface IEndpointResolver {
WebTarget getEndpoint(String expUUID, String dataProviderId);
}

private static void executePostErrorTests (ExperimentModelStub exp, IEndpointResolver resolver, String dpId, boolean hasParameters) {
// Invalid UUID string
WebTarget endpoint = resolver.getEndpoint(INVALID_EXP_UUID, dpId);
Map<String, Object> parameters = new HashMap<>();
try (Response response = endpoint.request().post(Entity.json(new QueryParameters(parameters, Collections.emptyList())))) {
assertNotNull(response);
assertEquals(Status.NOT_FOUND.getStatusCode(), response.getStatus());
}

// Unknown experiment
endpoint = resolver.getEndpoint(UUID.randomUUID().toString(), dpId);
try (Response response = endpoint.request().post(Entity.json(new QueryParameters(parameters, Collections.emptyList())))) {
assertNotNull(response);
assertEquals(Status.NOT_FOUND.getStatusCode(), response.getStatus());
assertEquals(EndpointConstants.NO_SUCH_TRACE, response.readEntity(String.class));
}

// Missing parameters
endpoint = resolver.getEndpoint(exp.getUUID().toString(), dpId);
try (Response response = endpoint.request().post(Entity.json(NO_PARAMETERS))) {
assertNotNull(response);
assertEquals(Status.BAD_REQUEST.getStatusCode(), response.getStatus());
}

if (hasParameters) {
// Missing parameters
endpoint = resolver.getEndpoint(exp.getUUID().toString(), dpId);
try (Response response = endpoint.request().post(Entity.json(new QueryParameters(parameters, Collections.emptyList())))) {
assertNotNull(response);
assertEquals(Status.BAD_REQUEST.getStatusCode(), response.getStatus());
}
}

// Unknown data provider
endpoint = resolver.getEndpoint(exp.getUUID().toString(), UNKNOWN_DP_ID);
try (Response response = endpoint.request().post(Entity.json(new QueryParameters(parameters, Collections.emptyList())))) {
assertNotNull(response);
assertEquals(Status.METHOD_NOT_ALLOWED.getStatusCode(), response.getStatus());
assertEquals(EndpointConstants.NO_PROVIDER, response.readEntity(String.class));
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/**********************************************************************
* Copyright (c) 2024 Ericsson
*
* All rights reserved. This program and the accompanying materials are
* made available under the terms of the Eclipse Public License 2.0 which
* accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
**********************************************************************/
package org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests.stubs;

import java.util.List;

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;

/**
* A stub class for the response to a time graph arrows' request. It contains
* the generic response, as well as an {@link TimeGraphArrowStub}
*
* @author Bernd Hufmann
*/
public class TgArrowsOutputResponseStub extends OutputResponseStub {

private static final long serialVersionUID = -2103307674960234620L;
private final List<TimeGraphArrowStub> fModel;

/**
* {@link JsonCreator} Constructor from json
*
* @param model
* The model for this response
* @param status
* The status of the response
* @param statusMessage
* The custom status message of the response
*/
public TgArrowsOutputResponseStub(@JsonProperty("model") List<TimeGraphArrowStub> model,
@JsonProperty("status") String status,
@JsonProperty("statusMessage") String statusMessage) {
super(status, statusMessage);
fModel = model;
}

/**
* Get the model for this response
*
* @return The model for the response
*/
public List<TimeGraphArrowStub> getModel() {
return fModel;
}

}
Loading

0 comments on commit bf927d5

Please sign in to comment.