blob: 7de968fa25b03895b2da0c7d351e42d3df552692 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2018, 2021 Ericsson and others
*
* 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.services;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import javax.ws.rs.ProcessingException;
import javax.ws.rs.client.Entity;
import javax.ws.rs.client.WebTarget;
import javax.ws.rs.core.Response;
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;
import org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests.stubs.ColumnHeaderEntryStub;
import org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests.stubs.DataProviderDescriptorStub;
import org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests.stubs.EntryHeaderStub;
import org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests.stubs.EntryModelStub;
import org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests.stubs.EntryStub;
import org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests.stubs.ExperimentModelStub;
import org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests.stubs.LineModelStub;
import org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests.stubs.TableColumnsOutputResponseStub;
import org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests.stubs.TableLinesOutputResponseStub;
import org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests.stubs.TableModelStub;
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.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;
import org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests.stubs.TimeGraphStateStub;
import org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests.stubs.TreeOutputResponseStub;
import org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests.stubs.XyEntryModelStub;
import org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests.stubs.XyEntryStub;
import org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests.stubs.XyModelStub;
import org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests.stubs.XyOutputResponseStub;
import org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests.stubs.XySeriesStub;
import org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests.stubs.XyTreeOutputResponseStub;
import org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests.stubs.webapp.TestDataProviderService;
import org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests.utils.RestServerTest;
import org.eclipse.tracecompass.internal.tmf.core.model.filters.FetchParametersUtils;
import org.eclipse.tracecompass.tmf.core.model.filters.TimeQueryFilter;
import org.junit.Test;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Iterators;
/**
* Test the {@link DataProviderService}
*
* @author Loic Prieur-Drevon
* @author Geneviève Bastien
*/
public class DataProviderServiceTest extends RestServerTest {
private static final int MAX_ITER = 40;
private static final String CALL_STACK_DATAPROVIDER_ID = "org.eclipse.tracecompass.internal.analysis.profiling.callstack.provider.CallStackDataProvider";
private static final String STATISTICS_DATAPROVIDER_ID = "org.eclipse.tracecompass.analysis.timing.core.segmentstore.SegmentStoreStatisticsDataProvider:org.eclipse.linuxtools.lttng2.ust.analysis.callstack";
private static final String XY_DATAPROVIDER_ID = "org.eclipse.tracecompass.analysis.os.linux.core.cpuusage.CpuUsageDataProvider";
private static final String XY_HISTOGRAM_DATAPROVIDER_ID = "org.eclipse.tracecompass.internal.tmf.core.histogram.HistogramDataProvider";
private static final String EVENTS_TABLE_DATAPROVIDER_ID = "org.eclipse.tracecompass.internal.provisional.tmf.core.model.events.TmfEventTableDataProvider";
private static final String REQUESTED_TIMERANGE_KEY = "requested_timerange";
private static final String REQUESTED_TIMES_KEY = "requested_times";
private static final String REQUESTED_ITEMS_KEY = "requested_items";
private static final String REQUESTED_ELEMENT_KEY = "requested_element";
private static final String REQUESTED_COLUMN_IDS_KEY = "requested_table_column_ids";
private static final String REQUESTED_TABLE_INDEX_KEY = "requested_table_index";
private static final String REQUESTED_TABLE_COUNT_KEY = "requested_table_count";
private static final String IS_FILTERED_KEY = "isFiltered";
private static final String ELEMENT_TYPE = "elementType";
private static final String STATE = "state";
private static final String ANNOTATION = "annotation";
private static final String ARROW = "arrow";
private static final String TIME = "time";
private static final String DURATION = "duration";
private static final String ENTRY_ID = "entryId";
private static final String DESTINATION_ID = "destinationId";
private static final String START = "start";
private static final String END = "end";
private static final String NB_TIMES = "nbTimes";
private static final long TABLE_INDEX = 0L;
private static final long TABLE_COUNT = 100L;
private static final String FILTER_EXPRESSIONS_MAP = "filter_expressions_map";
private static final String FILTER_QUERY_PARAMETERS = "filter_query_parameters";
private static final String STRATEGY = "strategy";
private static final String DEEP_SEARCH = "DEEP";
private static final String FILTER_QUERY = "test";
private static final int DIMMED_FILTER_TAG = 1;
private static final List<EntryHeaderStub> EXPECTED_XY_TREE_HEADERS = ImmutableList.of(new EntryHeaderStub("Process", "", null), new EntryHeaderStub("TID", "", null), new EntryHeaderStub("%", "", null), new EntryHeaderStub("Time", "", null));
private static List<String> STATISTICS_TREE_HEADERS = ImmutableList.of("Label", "Minimum", "Maximum", "Average", "Std Dev", "Count", "Total", "Min Time Range", "Max Time Range");
private static List<String> SAMPLE_TOTAL_STATS_LABELS = ImmutableList.of("ust", "1 ns", "5.979 s", "10.845 ms", "196.299 ms", "1948", "21.127 s", "[1450193745774189602,1450193745774189603]", "[1450193722283061910,1450193728261604656]");
private static List<String> SAMPLE_SELECTION_STATS_LABELS = ImmutableList.of("Selection", "49.665 µs", "5.979 s", "11.388 ms", "201.201 ms", "1854", "21.113 s", "[1450193730177271075,1450193730177320740]", "[1450193722283061910,1450193728261604656]");
/**
* Test getting the data provider descriptors
*/
@Test
public void testProviders() {
ExperimentModelStub exp = assertPostExperiment(CONTEXT_SWITCHES_UST_STUB.getName(), CONTEXT_SWITCHES_UST_STUB);
WebTarget experiments = getApplicationEndpoint().path(EXPERIMENTS);
WebTarget providers = experiments.path(exp.getUUID().toString())
.path(OUTPUTS_PATH);
Set<DataProviderDescriptorStub> descriptors = getDataProviderDescriptors(providers);
for (DataProviderDescriptorStub desc : EXPECTED_DATA_PROVIDER_DESCRIPTOR) {
assertTrue(desc.getName(), descriptors.contains(desc));
}
}
/**
* Ensure that the Call Stack data provider exists for the trace.
*/
@Test
public void testCallStackDataProvider() {
ExperimentModelStub exp = assertPostExperiment(CONTEXT_SWITCHES_UST_STUB.getName(), CONTEXT_SWITCHES_UST_STUB);
WebTarget callstackTree = getTimeGraphTreeEndpoint(exp.getUUID().toString(), CALL_STACK_DATAPROVIDER_ID);
Map<String, Object> parameters = FetchParametersUtils.timeQueryToMap(new TimeQueryFilter(0L, Long.MAX_VALUE, 2));
Response tree = callstackTree.request().post(Entity.json(new QueryParameters(parameters, Collections.emptyList())));
assertEquals("There should be a positive response for the data provider", 200, tree.getStatus());
parameters = new HashMap<>();
Response defaults = callstackTree.request().post(Entity.json(new QueryParameters(parameters, Collections.emptyList())));
assertEquals("Default values should return OK code", 200, defaults.getStatus());
}
/**
* Ensure that an XY data provider exists and returns correct data. It does
* not test the data itself, simply that the serialized fields are the
* expected ones according to the protocol.
*
* @throws InterruptedException
* Exception thrown while waiting to execute again
*/
@Test
public void testXYDataProvider() throws InterruptedException {
long start = 1412670961211260539L;
long end = 1412670967217750839L;
try {
ExperimentModelStub exp = assertPostExperiment(ARM_64_KERNEL_STUB.getName(), ARM_64_KERNEL_STUB);
// Test getting the tree endpoint for an XY chart
WebTarget xyTree = getXYTreeEndpoint(exp.getUUID().toString(), XY_DATAPROVIDER_ID);
Map<String, Object> parameters = new HashMap<>();
parameters.put(REQUESTED_TIMES_KEY, ImmutableList.of(start, end));
XyTreeOutputResponseStub responseModel;
Response tree = xyTree.request().post(Entity.json(new QueryParameters(parameters, Collections.emptyList())));
assertEquals("There should be a positive response for the data provider", 200, tree.getStatus());
responseModel = tree.readEntity(XyTreeOutputResponseStub.class);
assertNotNull(responseModel);
tree.close();
// 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);
Response xyResponse = xyTree.request().post(Entity.json(new QueryParameters(parameters, Collections.emptyList())));
assertEquals("There should be a positive response for the data provider", 200, xyResponse.getStatus());
responseModel = xyResponse.readEntity(XyTreeOutputResponseStub.class);
assertNotNull(responseModel);
iteration++;
xyResponse.close();
}
// Verify tree model
XyEntryModelStub model = responseModel.getModel();
assertNotNull(model);
List<EntryHeaderStub> headers = model.getHeaders();
assertNotNull(headers);
assertEquals(EXPECTED_XY_TREE_HEADERS.size(), headers.size());
// Verify tree headers
for (int i = 0; i < headers.size(); i++ ) {
EntryHeaderStub header = headers.get(i);
EntryHeaderStub expHeader = EXPECTED_XY_TREE_HEADERS.get(i);
assertTrue(expHeader.getName().equals(header.getName()) && expHeader.getTooltip().equals(header.getTooltip()));
}
// Verify Entries
assertNotNull("The model is null, maybe the analysis did not run long enough?" + responseModel, model);
List<XyEntryStub> entries = model.getEntries();
assertFalse(entries.isEmpty());
// Test getting the XY series endpoint
WebTarget xySeriesEnpoint = getXYSeriesEndpoint(exp.getUUID().toString(), XY_DATAPROVIDER_ID);
List<Integer> items = new ArrayList<>();
for (XyEntryStub entry : entries) {
items.add(entry.getId());
}
parameters.remove(REQUESTED_TIMES_KEY);
parameters.put(REQUESTED_TIMERANGE_KEY, ImmutableMap.of(START, start, END, end, NB_TIMES, 10));
parameters.put(REQUESTED_ITEMS_KEY, items);
Response series = xySeriesEnpoint.request().post(Entity.json(new QueryParameters(parameters, Collections.emptyList())));
assertEquals("There should be a positive response for the data provider", 200, series.getStatus());
XyOutputResponseStub xyModelResponse = series.readEntity(XyOutputResponseStub.class);
assertNotNull(xyModelResponse);
XyModelStub xyModel = xyModelResponse.getModel();
Set<XySeriesStub> xySeries = xyModel.getSeries();
assertFalse(xySeries.isEmpty());
series.close();
} catch (ProcessingException e) {
// The failure from this exception alone is not helpful. Use the
// suppressed exception's message be the failure message for more
// help debugging failed tests.
fail(e.getCause().getMessage());
}
}
/**
* Verify that Histogram Data Provider fetchTree() interface and verify that
* the serialized fields are the expected ones according to the protocol.
*
* @throws InterruptedException
* Exception thrown while waiting to execute again
*/
@Test
public void testHistogramDataProvider() throws InterruptedException {
long start = 1412670961211260539L;
long end = 1412670967217750839L;
try {
ExperimentModelStub exp = assertPostExperiment(ARM_64_KERNEL_STUB.getName(), ARM_64_KERNEL_STUB);
// Test getting the tree endpoint for an XY chart
WebTarget xyTree = getXYTreeEndpoint(exp.getUUID().toString(), XY_HISTOGRAM_DATAPROVIDER_ID);
Map<String, Object> parameters = new HashMap<>();
parameters.put(REQUESTED_TIMES_KEY, ImmutableList.of(start, end));
XyTreeOutputResponseStub responseModel;
Response tree = xyTree.request().post(Entity.json(new QueryParameters(parameters, Collections.emptyList())));
assertEquals("There should be a positive response for the data provider", 200, tree.getStatus());
responseModel = tree.readEntity(XyTreeOutputResponseStub.class);
assertNotNull(responseModel);
tree.close();
// 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);
Response xyResponse = xyTree.request().post(Entity.json(new QueryParameters(parameters, Collections.emptyList())));
assertEquals("There should be a positive response for the data provider", 200, xyResponse.getStatus());
responseModel = xyResponse.readEntity(XyTreeOutputResponseStub.class);
assertNotNull(responseModel);
iteration++;
xyResponse.close();
}
// Verify tree model
XyEntryModelStub model = responseModel.getModel();
assertNotNull(model);
// Verify Entries
assertNotNull("The model is null, maybe the analysis did not run long enough?" + responseModel, model);
List<XyEntryStub> entries = model.getEntries();
assertFalse(entries.isEmpty());
for (XyEntryStub entry : entries) {
if (entry.getParentId() == -1) {
assertFalse(entry.isDefault());
} else {
assertTrue(entry.isDefault());
}
}
} catch (ProcessingException e) {
// The failure from this exception alone is not helpful. Use the
// suppressed exception's message be the failure message for more
// help debugging failed tests.
fail(e.getCause().getMessage());
}
}
/**
* Ensure that a data tree data provider exists and returns correct data.
* It does not test the data itself, simply that the serialized fields are
* the expected ones.
*
* @throws InterruptedException
* Exception thrown while waiting to execute again
*/
@Test
public void testDataTreeDataProvider() throws InterruptedException {
long start = 1450193697034689597L;
long end = 1450193745774189602L;
try {
ExperimentModelStub exp = assertPostExperiment(CONTEXT_SWITCHES_UST_STUB.getName(), CONTEXT_SWITCHES_UST_STUB);
// Test getting the time graph tree
WebTarget dataTree = getDataTreeEndpoint(exp.getUUID().toString(), STATISTICS_DATAPROVIDER_ID);
Map<String, Object> parameters = new HashMap<>();
parameters.put(REQUESTED_TIMERANGE_KEY, ImmutableMap.of(START, start, END, end));
EntryModelStub model = getDataTreeEntryModel(dataTree, parameters);
List<EntryStub> totalEntries = model.getEntries();
List<String> sampleLabels = totalEntries.get(0).getLabels();
for(String sample : SAMPLE_TOTAL_STATS_LABELS) {
assertTrue(sampleLabels.contains(sample));
}
// Query selection time range
end = end - 100000000L;
parameters.put(REQUESTED_TIMERANGE_KEY, ImmutableMap.of(START, start, END, end));
parameters.put(IS_FILTERED_KEY, true);
model = getDataTreeEntryModel(dataTree, parameters);
List<EntryStub> selectionRangeEntries = model.getEntries();
assertFalse(selectionRangeEntries.isEmpty());
// the result model contains total and selection statistics
assertTrue(selectionRangeEntries.size() > totalEntries.size());
sampleLabels = selectionRangeEntries.get(totalEntries.size()).getLabels();
for(String sample : SAMPLE_SELECTION_STATS_LABELS) {
assertTrue(sampleLabels.contains(sample));
}
} catch (ProcessingException e) {
// The failure from this exception alone is not helpful. Use the
// suppressed exception's message be the failure message for more
// help debugging failed tests.
fail(e.getCause().getMessage());
}
}
/**
* Ensure that a time graph data provider exists and returns correct data.
* It does not test the data itself, simply that the serialized fields are
* the expected ones.
*
* @throws InterruptedException
* Exception thrown while waiting to execute again
*/
@Test
public void testTimeGraphDataProvider() throws InterruptedException {
long start = 1450193697034689597L;
long end = 1450193745774189602L;
try {
ExperimentModelStub exp = assertPostExperiment(CONTEXT_SWITCHES_UST_STUB.getName(), CONTEXT_SWITCHES_UST_STUB);
// 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, ImmutableList.of(start, end));
Response treeResponse = callstackTree.request().post(Entity.json(new QueryParameters(parameters, Collections.emptyList())));
assertEquals("There should be a positive response for the data provider", 200, treeResponse.getStatus());
responseModel = treeResponse.readEntity(TgTreeOutputResponseStub.class);
assertNotNull(responseModel);
treeResponse.close();
// 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);
treeResponse = callstackTree.request().post(Entity.json(new QueryParameters(parameters, Collections.emptyList())));
assertEquals("There should be a positive response for the data provider", 200, treeResponse.getStatus());
responseModel = treeResponse.readEntity(TgTreeOutputResponseStub.class);
assertNotNull(responseModel);
iteration++;
treeResponse.close();
}
TgEntryModelStub model = responseModel.getModel();
assertNotNull("The model is null, maybe the analysis did not run long enough?" + 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<>();
// Find a specific call stack entry
for (TimeGraphEntryStub entry : entries) {
items.add(entry.getId());
}
// Test getting the time graph row data
WebTarget tgStatesEnpoint = getTimeGraphStatesEndpoint(exp.getUUID().toString(), CALL_STACK_DATAPROVIDER_ID);
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);
parameters.put(FILTER_QUERY_PARAMETERS, ImmutableMap.of(
STRATEGY, DEEP_SEARCH,
FILTER_EXPRESSIONS_MAP,
ImmutableMap.of(
Integer.toString(DIMMED_FILTER_TAG), Arrays.asList(FILTER_QUERY)
)
));
Response statesResponse = tgStatesEnpoint.request().post(Entity.json(new QueryParameters(parameters, Collections.emptyList())));
assertEquals("There should be a positive response for the data provider", 200, statesResponse.getStatus());
TgStatesOutputResponseStub tgStateModelResponse = statesResponse.readEntity(TgStatesOutputResponseStub.class);
assertNotNull(tgStateModelResponse);
TimeGraphModelStub tgModel = tgStateModelResponse.getModel();
Set<TimeGraphRowStub> rows = tgModel.getRows();
assertFalse(rows.isEmpty());
statesResponse.close();
// Test getting the time graph tooltip for a state
int callstackEntryIdForTooltip = findCallStackEntry(entries);
TimeGraphRowStub row = null;
for (Iterator<TimeGraphRowStub> iterator = rows.iterator(); iterator.hasNext();) {
TimeGraphRowStub timeGraphRowStub = iterator.next();
if (timeGraphRowStub.getEntryId() == callstackEntryIdForTooltip) {
row = timeGraphRowStub;
}
}
assertNotNull(row);
TimeGraphStateStub state = row.getStates().get(0);
WebTarget tgTooltipEnpoint = getTimeGraphTooltipEndpoint(exp.getUUID().toString(), CALL_STACK_DATAPROVIDER_ID);
parameters.put(REQUESTED_ITEMS_KEY, Collections.singletonList(row.getEntryId()));
parameters.put(REQUESTED_TIMES_KEY, Collections.singletonList(state.getStartTime()));
parameters.put(REQUESTED_ELEMENT_KEY, ImmutableMap.of(
ELEMENT_TYPE, STATE,
TIME, state.getStartTime(),
DURATION, (state.getEndTime() - state.getStartTime())));
Response tooltipResponse = tgTooltipEnpoint.request().post(Entity.json(new QueryParameters(parameters, Collections.emptyList())));
assertEquals("There should be a positive response for the data provider", 200, tooltipResponse.getStatus());
TgTooltipOutputResponseStub timegraphTooltipResponse = tooltipResponse.readEntity(TgTooltipOutputResponseStub.class);
assertNotNull(timegraphTooltipResponse);
Map<String, String> expectedTooltip = new HashMap<>();
expectedTooltip.put("Address", "0x804a291");
expectedTooltip.put("State", "000000000804a291");
assertEquals(expectedTooltip, timegraphTooltipResponse.getModel());
tooltipResponse.close();
// Test getting the time graph tooltip for an annotation
long time = state.getStartTime();
parameters.put(REQUESTED_ITEMS_KEY, Collections.singletonList(row.getEntryId()));
parameters.put(REQUESTED_TIMES_KEY, Collections.singletonList(state.getStartTime()));
parameters.put(REQUESTED_ELEMENT_KEY, ImmutableMap.of(
ELEMENT_TYPE, ANNOTATION,
TIME, time,
DURATION, 0L,
ENTRY_ID, row.getEntryId()));
tooltipResponse = tgTooltipEnpoint.request().post(Entity.json(new QueryParameters(parameters, Collections.emptyList())));
assertEquals("There should be a positive response for the data provider", 200, tooltipResponse.getStatus());
// Test getting the time graph tooltip for an arrow
TimeGraphRowStub destinationRow = Iterators.get(rows.iterator(), rows.size() - 2);
parameters.put(REQUESTED_ITEMS_KEY, Collections.singletonList(row.getEntryId()));
parameters.put(REQUESTED_TIMES_KEY, Collections.singletonList(state.getStartTime()));
parameters.put(REQUESTED_ELEMENT_KEY, ImmutableMap.of(
ELEMENT_TYPE, ARROW,
TIME, time,
DURATION, 0L,
ENTRY_ID, row.getEntryId(),
DESTINATION_ID, destinationRow.getEntryId()));
tooltipResponse = tgTooltipEnpoint.request().post(Entity.json(new QueryParameters(parameters, Collections.emptyList())));
assertEquals("There should be a positive response for the data provider", 200, tooltipResponse.getStatus());
timegraphTooltipResponse = tooltipResponse.readEntity(TgTooltipOutputResponseStub.class);
assertNotNull(timegraphTooltipResponse);
assertEquals(Collections.emptyMap(), timegraphTooltipResponse.getModel());
tooltipResponse.close();
// Test the tags of the state
assertEquals(DIMMED_FILTER_TAG, state.getTags());
} catch (ProcessingException e) {
// The failure from this exception alone is not helpful. Use the
// suppressed exception's message be the failure message for more
// help debugging failed tests.
fail(e.getCause().getMessage());
}
}
private static int findCallStackEntry(Set<TimeGraphEntryStub> entries) {
// Find trace entry
Optional<TimeGraphEntryStub> traceOptional = entries.stream().filter(entry -> entry.getParentId() == -1).findFirst();
assertTrue(traceOptional.isPresent());
int traceId = traceOptional.get().getId();
// Find process
final String processNameForTooltip = "UNKNOWN";
Optional<TimeGraphEntryStub> processOptional = entries.stream().filter(
entry -> processNameForTooltip.equals(entry.getLabels().get(0)) && entry.getParentId() == traceId).findFirst();
assertTrue(processOptional.isPresent());
int processEntryId = processOptional.get().getId();
// Find specific thread entry
final String threadNameForTooltip = "lemon_server-589";
Optional<TimeGraphEntryStub> threadOptional = entries.stream().filter(
entry -> threadNameForTooltip.equals(entry.getLabels().get(0)) && entry.getParentId() == processEntryId).findFirst();
assertTrue(threadOptional.isPresent());
int threadId = threadOptional.get().getId();
// Find first callstack entry under the thread entry
Optional<TimeGraphEntryStub> callstackOptional = entries.stream().filter(
entry -> threadNameForTooltip.equals(entry.getLabels().get(0)) && entry.getParentId() == threadId).findFirst();
assertTrue(callstackOptional.isPresent());
return callstackOptional.get().getId();
}
/**
* Ensure that a table data provider exists and returns correct data. It
* does not test the data itself, simply that the serialized fields are the
* expected ones.
*
* @throws InterruptedException
* Exception thrown while waiting to execute again
*/
@Test
public void testTableDataProvider() throws InterruptedException {
long start = 1412670961211260539L;
long end = 1412670967217750839L;
try {
ExperimentModelStub exp = assertPostExperiment(ARM_64_KERNEL_STUB.getName(), ARM_64_KERNEL_STUB);
// Test getting the tree endpoint for an XY chart
WebTarget tableColumns = getTableColumnsEndpoint(exp.getUUID().toString(), EVENTS_TABLE_DATAPROVIDER_ID);
Map<String, Object> parameters = new HashMap<>();
parameters.put(REQUESTED_TIMES_KEY, ImmutableList.of(start, end));
TableColumnsOutputResponseStub responseModel;
Response tree = tableColumns.request().post(Entity.json(new QueryParameters(parameters, Collections.emptyList())));
assertEquals("There should be a positive response for the data provider", 200, tree.getStatus());
responseModel = tree.readEntity(TableColumnsOutputResponseStub.class);
assertNotNull(responseModel);
tree.close();
// 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);
Response xyResponse = tableColumns.request().post(Entity.json(new QueryParameters(parameters, Collections.emptyList())));
assertEquals("There should be a positive response for the data provider", 200, xyResponse.getStatus());
responseModel = xyResponse.readEntity(TableColumnsOutputResponseStub.class);
assertNotNull(responseModel);
iteration++;
xyResponse.close();
}
List<ColumnHeaderEntryStub> columns = responseModel.getModel();
assertNotNull("The model is null, maybe the analysis did not run long enough?" + responseModel, columns);
assertFalse(columns.isEmpty());
// Test getting the XY series endpoint
WebTarget tableLinesEnpoint = getTableLinesEndpoint(exp.getUUID().toString(), EVENTS_TABLE_DATAPROVIDER_ID);
List<Long> requestedColumnsIds = new ArrayList<>();
for (int i = 0; i <= columns.size() / 2; i++) {
requestedColumnsIds.add(columns.get(i).getId());
}
parameters.put(REQUESTED_COLUMN_IDS_KEY, requestedColumnsIds);
parameters.put(REQUESTED_TABLE_INDEX_KEY, TABLE_INDEX);
parameters.put(REQUESTED_TABLE_COUNT_KEY, TABLE_COUNT);
Response linesResponse = tableLinesEnpoint.request().post(Entity.json(new QueryParameters(parameters, Collections.emptyList())));
assertEquals("There should be a positive response for the data provider", 200, linesResponse.getStatus());
TableLinesOutputResponseStub lineModelResponse = linesResponse.readEntity(TableLinesOutputResponseStub.class);
assertNotNull(lineModelResponse);
TableModelStub tableModel = lineModelResponse.getModel();
assertNotNull("Table model", tableModel);
List<LineModelStub> lines = tableModel.getLines();
// FIXME This assert does not work with current implementation
// assertEquals("Sizes match", tableModel.getSize(), lines.size());
assertFalse(lines.isEmpty());
for (LineModelStub line : lines) {
assertEquals("Number of returned cells", requestedColumnsIds.size(), line.getCells().size());
}
linesResponse.close();
} catch (ProcessingException e) {
// The failure from this exception alone is not helpful. Use the
// suppressed exception's message be the failure message for more
// help debugging failed tests.
fail(e.getCause().getMessage());
}
}
/**
* Using the custom data provider verify that only allowed types (Number,
* String) are serialized in the metadata map of time graph entries.
*/
@Test
public void testTimeGraphMetaDataSerializer() {
@SuppressWarnings("resource")
Response treeResponse = null;
try {
ExperimentModelStub exp = assertPostExperiment(CONTEXT_SWITCHES_UST_STUB.getName(), CONTEXT_SWITCHES_UST_STUB);
// Test getting the time graph tree
WebTarget callstackTree = getTimeGraphTreeEndpoint(exp.getUUID().toString(), TestDataProviderService.INVALID_ENTRY_METADATA);
Map<String, Object> parameters = new HashMap<>();
TgTreeOutputResponseStub responseModel;
parameters.put(REQUESTED_TIMES_KEY, ImmutableList.of(1450193697034689597L, 1450193745774189602L));
treeResponse = callstackTree.request().post(Entity.json(new QueryParameters(parameters, Collections.emptyList())));
assertEquals("There should be a positive response for the data provider", 200, treeResponse.getStatus());
responseModel = treeResponse.readEntity(TgTreeOutputResponseStub.class);
assertNotNull(responseModel);
// Verify Time Graph Entries
Set<TimeGraphEntryStub> entries = responseModel.getModel().getEntries();
assertEquals(2, entries.size());
for (TimeGraphEntryStub entry : entries) {
verifyEntry(entry);
}
} catch (ProcessingException e) {
// The failure from this exception alone is not helpful. Use the
// suppressed exception's message be the failure message for more
// help debugging failed tests.
fail(e.getCause().getMessage());
} finally {
if (treeResponse != null) {
treeResponse.close();
}
}
}
private static void verifyMetadata(Map<String, Collection<Object>> metadata, String key, Class<?> clazz) {
Collection<Object> col = metadata.get(key);
assertNotNull(key, col);
assertTrue(key, col.stream().allMatch(clazz::isInstance));
}
private static void verifyEntry(TimeGraphEntryStub entry) {
assertFalse(entry.getLabels().isEmpty());
if (entry.getLabels().get(0).equals(TestDataProviderService.ENTRY_NAME_WITH_METADATA)) {
// Verify supported values for metadata in entry
@Nullable Map<String, Collection<Object>> metadata = entry.getMetadata();
assertNotNull(metadata);
verifyMetadata(metadata, TestDataProviderService.VALID_TEST_KEY_BYTE, Number.class);
verifyMetadata(metadata, TestDataProviderService.VALID_TEST_KEY_SHORT, Number.class);
verifyMetadata(metadata, TestDataProviderService.VALID_TEST_KEY_INT, Number.class);
verifyMetadata(metadata, TestDataProviderService.VALID_TEST_KEY_LONG, Number.class);
verifyMetadata(metadata, TestDataProviderService.VALID_TEST_KEY_FLOAT, Number.class);
verifyMetadata(metadata, TestDataProviderService.VALID_TEST_KEY_DOUBLE, Number.class);
verifyMetadata(metadata, TestDataProviderService.VALID_TEST_KEY_STRING, String.class);
// Verify unsupported object
Collection<Object> col = metadata.get(TestDataProviderService.INVALID_TEST_KEY);
assertNull(TestDataProviderService.INVALID_TEST_KEY, col);
}
if (entry.getLabels().get(0).equals(TestDataProviderService.ENTRY_NAME_WITHOUT_METADATA)) {
// Verify that entry doesn't have metadata
assertNull(entry.getMetadata());
}
}
private static EntryModelStub getDataTreeEntryModel(WebTarget dataTree, Map<String, Object> parameters) throws InterruptedException {
TreeOutputResponseStub responseModel;
Response treeResponse = dataTree.request().post(Entity.json(new QueryParameters(parameters, Collections.emptyList())));
assertEquals("There should be a positive response for the data provider", 200, treeResponse.getStatus());
responseModel = treeResponse.readEntity(TreeOutputResponseStub.class);
assertNotNull(responseModel);
treeResponse.close();
// 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);
treeResponse = dataTree.request().post(Entity.json(new QueryParameters(parameters, Collections.emptyList())));
assertEquals("There should be a positive response for the data provider", 200, treeResponse.getStatus());
responseModel = treeResponse.readEntity(TreeOutputResponseStub.class);
assertNotNull(responseModel);
iteration++;
treeResponse.close();
}
EntryModelStub model = responseModel.getModel();
assertNotNull("The model is null, maybe the analysis did not run long enough?" + responseModel, model);
List<EntryStub> totalEntries = model.getEntries();
assertFalse(totalEntries.isEmpty());
List<String> headerLabels = model.getHeaders().stream().map(EntryHeaderStub::getName).collect(Collectors.toList());
for(String header : STATISTICS_TREE_HEADERS) {
assertTrue(headerLabels.contains(header));
}
return model;
}
}