Skip to content

Commit e650ab3

Browse files
author
Siwei Zhang
committed
server: Add bar chart data provider with /bars endpoint
This commit uses the newly introduced data provider type to support "bar chart" views. [Added] /trees endpoint for bar chart view [Added] /bars endpoint for bar chart view
1 parent ea336de commit e650ab3

File tree

19 files changed

+1558
-32
lines changed

19 files changed

+1558
-32
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,284 @@
1+
/*******************************************************************************
2+
* Copyright (c) 2025 Ericsson and others
3+
*
4+
* All rights reserved. This program and the accompanying materials are
5+
* made available under the terms of the Eclipse Public License 2.0 which
6+
* accompanies this distribution, and is available at
7+
* https://www.eclipse.org/legal/epl-2.0/
8+
*
9+
* SPDX-License-Identifier: EPL-2.0
10+
*******************************************************************************/
11+
12+
package org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests.services;
13+
14+
import static org.junit.Assert.assertEquals;
15+
import static org.junit.Assert.assertFalse;
16+
import static org.junit.Assert.assertNotNull;
17+
import static org.junit.Assert.assertNull;
18+
import static org.junit.Assert.assertTrue;
19+
20+
import java.util.ArrayList;
21+
import java.util.Arrays;
22+
import java.util.Collections;
23+
import java.util.HashMap;
24+
import java.util.List;
25+
import java.util.Map;
26+
import java.util.Set;
27+
28+
import javax.ws.rs.client.Entity;
29+
import javax.ws.rs.client.WebTarget;
30+
import javax.ws.rs.core.Response;
31+
32+
import org.eclipse.tracecompass.incubator.internal.trace.server.jersey.rest.core.model.views.QueryParameters;
33+
import org.eclipse.tracecompass.incubator.internal.trace.server.jersey.rest.core.services.DataProviderService;
34+
import org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests.stubs.EntryStub;
35+
import org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests.stubs.ExperimentModelStub;
36+
import org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests.stubs.IAxisDomainStub;
37+
import org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests.stubs.XyEntryModelStub;
38+
import org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests.stubs.XyEntryStub;
39+
import org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests.stubs.XyTreeOutputResponseStub;
40+
import org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests.stubs.ISamplingStub;
41+
import org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests.stubs.TmfXYAxisDescriptionStub;
42+
import org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests.stubs.XyModelStub;
43+
import org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests.stubs.XyOutputResponseStub;
44+
import org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests.stubs.XySeriesStub;
45+
import org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests.utils.RestServerTest;
46+
import org.eclipse.tracecompass.internal.tmf.core.Activator;
47+
import org.eclipse.tracecompass.tmf.core.model.StyleProperties;
48+
import org.junit.Test;
49+
50+
import com.google.common.collect.ImmutableMap;
51+
52+
/**
53+
* Test {@link DataProviderService} with generic xy endpoints with non-time x-axis.
54+
*
55+
* @author Siwei Zhang
56+
*/
57+
@SuppressWarnings({"null"})
58+
public class GenericXYDataProviderServiceTest extends RestServerTest {
59+
private static final String DATA_PROVIDER_RESPONSE_FAILED_MSG = "There should be a positive response for the data provider";
60+
private static final String MODEL_NULL_MSG = "The model is null, maybe the analysis did not run long enough?";
61+
private static final String REQUESTED_ITEMS_KEY = "requested_items";
62+
private static final String REQUESTED_TIMERANGE_KEY = "requested_timerange";
63+
private static final String START = "start";
64+
private static final String END = "end";
65+
private static final String NB_SAMPLES = "nbSamples";
66+
private static final int MAX_ITER = 40;
67+
private static final long TRACE_START_TIME = 1450193697034689597L;
68+
private static final long TRACE_END_TIME = 1450193745774189602L;
69+
private static final List<XyEntryStub> EXPECTED_ENTRIES = List.of(
70+
new XyEntryStub(Arrays.asList("ust"), 0, -1, true, null, true),
71+
new XyEntryStub(Arrays.asList("UNKNOWN_PID"), 1, 0, true, null, false));
72+
73+
/**
74+
* Ensure that a generic xy data provider exists and returns correct data.
75+
* It does not test the data itself, simply that the serialized fields are
76+
* the expected ones according to the protocol. Tested using generic xy
77+
* provider for call stack.
78+
*
79+
* @throws InterruptedException
80+
* Exception thrown while waiting to execute again
81+
*/
82+
@Test
83+
public void testGenericXYDataProvider() throws InterruptedException {
84+
ExperimentModelStub exp = assertPostExperiment(sfContextSwitchesUstNotInitializedStub.getName(), sfContextSwitchesUstNotInitializedStub);
85+
86+
WebTarget callstackTree = getGenericXYTreeEndpoint(exp.getUUID().toString(), CALL_STACK_FUNCTION_DENSITY_DATAPROVIDER_ID);
87+
88+
// Test getting the tree endpoint with descriptors
89+
Map<String, Object> parameters = new HashMap<>();
90+
parameters.put(REQUESTED_TIMES_KEY, List.of(0L, Long.MAX_VALUE));
91+
XyTreeOutputResponseStub responseModel;
92+
try (Response tree = callstackTree.request().post(Entity.json(new QueryParameters(parameters, Collections.emptyList())))) {
93+
assertEquals(DATA_PROVIDER_RESPONSE_FAILED_MSG, 200, tree.getStatus());
94+
responseModel = tree.readEntity(XyTreeOutputResponseStub.class);
95+
assertNotNull(responseModel);
96+
}
97+
// Make sure the analysis ran enough and we have a model
98+
int iteration = 0;
99+
while (responseModel.isRunning() && responseModel.getModel() == null && iteration < MAX_ITER) {
100+
Thread.sleep(100);
101+
try (Response treeResponse = callstackTree.request().post(Entity.json(new QueryParameters(parameters, Collections.emptyList())))) {
102+
assertEquals(DATA_PROVIDER_RESPONSE_FAILED_MSG, 200, treeResponse.getStatus());
103+
responseModel = treeResponse.readEntity(XyTreeOutputResponseStub.class);
104+
assertNotNull(responseModel);
105+
iteration++;
106+
}
107+
}
108+
109+
// Validate model
110+
XyEntryModelStub model = responseModel.getModel();
111+
assertNotNull(MODEL_NULL_MSG + responseModel, model);
112+
113+
List<XyEntryStub> entries = model.getEntries();
114+
assertFalse(entries.isEmpty());
115+
116+
// Test getting the generic xy endpoint with descriptors for xy
117+
WebTarget xyEnpoint = getGenericXYSeriesEndpoint(exp.getUUID().toString(), CALL_STACK_FUNCTION_DENSITY_DATAPROVIDER_ID);
118+
parameters = new HashMap<>();
119+
List<Integer> items = new ArrayList<>();
120+
for (EntryStub entry : entries) {
121+
items.add(entry.getId());
122+
}
123+
parameters.put(REQUESTED_ITEMS_KEY, items);
124+
parameters.put(REQUESTED_TIMERANGE_KEY, ImmutableMap.of(START, TRACE_START_TIME, END, TRACE_END_TIME, NB_SAMPLES, 5));
125+
try (Response series = xyEnpoint.request().post(Entity.json(new QueryParameters(parameters, Collections.emptyList())))) {
126+
assertEquals(DATA_PROVIDER_RESPONSE_FAILED_MSG, 200, series.getStatus());
127+
XyOutputResponseStub xyModelResponse = series.readEntity(XyOutputResponseStub.class);
128+
assertNotNull(xyModelResponse);
129+
130+
XyModelStub xyModel = xyModelResponse.getModel();
131+
Set<XySeriesStub> xySeries = xyModel.getSeries();
132+
assertFalse(xySeries.isEmpty());
133+
}
134+
}
135+
136+
/**
137+
* Ensure that the inside data is correct for call stack function density
138+
* data provider for both tree end point and xy end point.
139+
*
140+
* @throws InterruptedException
141+
* Exception thrown while waiting to execute again
142+
*/
143+
@Test
144+
public void testCallStackFunctionDensityDataProvider() throws InterruptedException {
145+
ExperimentModelStub exp = assertPostExperiment(sfContextSwitchesUstNotInitializedStub.getName(), sfContextSwitchesUstNotInitializedStub);
146+
147+
WebTarget callstackTree = getGenericXYTreeEndpoint(exp.getUUID().toString(), CALL_STACK_FUNCTION_DENSITY_DATAPROVIDER_ID);
148+
149+
/*
150+
* Test the data in the tree end point with descriptors.
151+
*/
152+
Map<String, Object> parameters = new HashMap<>();
153+
parameters.put(REQUESTED_TIMES_KEY, List.of(0L, Long.MAX_VALUE));
154+
XyTreeOutputResponseStub responseModel;
155+
try (Response tree = callstackTree.request().post(Entity.json(new QueryParameters(parameters, Collections.emptyList())))) {
156+
assertEquals(DATA_PROVIDER_RESPONSE_FAILED_MSG, 200, tree.getStatus());
157+
responseModel = tree.readEntity(XyTreeOutputResponseStub.class);
158+
assertNotNull(responseModel);
159+
}
160+
// Make sure the analysis ran enough and we have a model
161+
int iteration = 0;
162+
while (responseModel.isRunning() && responseModel.getModel() == null && iteration < MAX_ITER) {
163+
Thread.sleep(100);
164+
try (Response treeResponse = callstackTree.request().post(Entity.json(new QueryParameters(parameters, Collections.emptyList())))) {
165+
assertEquals(DATA_PROVIDER_RESPONSE_FAILED_MSG, 200, treeResponse.getStatus());
166+
responseModel = treeResponse.readEntity(XyTreeOutputResponseStub.class);
167+
assertNotNull(responseModel);
168+
iteration++;
169+
}
170+
}
171+
172+
// Validate model
173+
XyEntryModelStub model = responseModel.getModel();
174+
assertNotNull(MODEL_NULL_MSG + responseModel, model);
175+
176+
int autoExpandLevel = model.getAutoExpandLevel();
177+
assertEquals("Auto-expand level mismatch", -1, autoExpandLevel);
178+
179+
// Entries
180+
List<XyEntryStub> actualEntries = model.getEntries();
181+
assertEquals("Entry count mismatch", EXPECTED_ENTRIES.size(), actualEntries.size());
182+
183+
for (int i = 0; i < EXPECTED_ENTRIES.size(); i++) {
184+
XyEntryStub expected = EXPECTED_ENTRIES.get(i);
185+
XyEntryStub actual = actualEntries.get(i);
186+
assertEquals("Entry ID mismatch at index " + i, expected.getId(), actual.getId());
187+
assertEquals("Parent ID mismatch at index " + i, expected.getParentId(), actual.getParentId());
188+
assertEquals("HasRowModel mismatch at index " + i, expected.hasRowModel(), actual.hasRowModel());
189+
assertEquals("Labels mismatch at index " + i, expected.getLabels(), actual.getLabels());
190+
assertEquals("Style mismatch at index " + i, expected.getStyle(), actual.getStyle());
191+
}
192+
193+
/*
194+
* Test the data in xy end point.
195+
*/
196+
WebTarget xyEnpoint = getGenericXYSeriesEndpoint(exp.getUUID().toString(), CALL_STACK_FUNCTION_DENSITY_DATAPROVIDER_ID);
197+
parameters = new HashMap<>();
198+
List<Integer> items = new ArrayList<>();
199+
for (EntryStub entry : actualEntries) {
200+
items.add(entry.getId());
201+
}
202+
parameters.put(REQUESTED_ITEMS_KEY, items);
203+
parameters.put(REQUESTED_TIMERANGE_KEY, ImmutableMap.of(START, TRACE_START_TIME, END, TRACE_END_TIME, NB_SAMPLES, 5));
204+
XyOutputResponseStub xyModelResponse;
205+
try (Response series = xyEnpoint.request().post(Entity.json(new QueryParameters(parameters, Collections.emptyList())))) {
206+
assertEquals(DATA_PROVIDER_RESPONSE_FAILED_MSG, 200, series.getStatus());
207+
xyModelResponse = series.readEntity(XyOutputResponseStub.class);
208+
}
209+
assertNotNull(xyModelResponse);
210+
// Make sure the analysis ran enough and we have a fully executed model
211+
iteration = 0;
212+
while (xyModelResponse.isRunning() && iteration < MAX_ITER) {
213+
Thread.sleep(100);
214+
try (Response response = xyEnpoint.request().post(Entity.json(new QueryParameters(parameters, Collections.emptyList())))) {
215+
Activator.logWarning("current status: " + xyModelResponse.isRunning());
216+
assertEquals(DATA_PROVIDER_RESPONSE_FAILED_MSG, 200, response.getStatus());
217+
xyModelResponse = response.readEntity(XyOutputResponseStub.class);
218+
assertNotNull(xyModelResponse);
219+
iteration++;
220+
}
221+
}
222+
XyModelStub xyModel = xyModelResponse.getModel();
223+
Set<XySeriesStub> xySeries = xyModel.getSeries();
224+
assertEquals("Number of series mismatch", 1, xySeries.size());
225+
XySeriesStub seriesStub = xySeries.iterator().next();
226+
227+
// Validate fields
228+
assertEquals("Name mismatch", "UNKNOWN_PID", seriesStub.getName());
229+
assertEquals("ID mismatch", 1, seriesStub.getId());
230+
231+
// Validate xValues
232+
ISamplingStub xValues = seriesStub.getXValues();
233+
assertTrue("xValues should be a RangesStub", xValues instanceof ISamplingStub.RangesStub);
234+
List<ISamplingStub.RangesStub.RangeStub> expectedRanges = Arrays.asList(
235+
new ISamplingStub.RangesStub.RangeStub(0L, 1195708549L),
236+
new ISamplingStub.RangesStub.RangeStub(1195708550L, 2391417098L),
237+
new ISamplingStub.RangesStub.RangeStub(2391417099L, 3587125647L),
238+
new ISamplingStub.RangesStub.RangeStub(3587125648L, 4782834196L),
239+
new ISamplingStub.RangesStub.RangeStub(4782834197L, 5978542746L)
240+
);
241+
List<ISamplingStub.RangesStub.RangeStub> actualRanges = ((ISamplingStub.RangesStub) xValues).getRanges();
242+
assertEquals("Range size mismatch", expectedRanges.size(), actualRanges.size());
243+
assertEquals("Range size mismatch", expectedRanges.size(), actualRanges.size());
244+
for (int i = 0; i < expectedRanges.size(); i++) {
245+
assertEquals("Range mismatch at index " + i, expectedRanges.get(i), actualRanges.get(i));
246+
}
247+
248+
// Validate yValues
249+
List<Double> actualYValues = seriesStub.getYValues();
250+
List<Double> expectedYValues = Arrays.asList(1943.0, 1.0, 2.0, 1.0, 1.0);
251+
assertEquals("Y values size mismatch", expectedYValues.size(), actualYValues.size());
252+
assertEquals("Y values size mismatch", expectedYValues.size(), actualYValues.size());
253+
for (int i = 0; i < expectedYValues.size(); i++) {
254+
assertEquals("Y value mismatch at index " + i, expectedYValues.get(i), actualYValues.get(i), 0.000001);
255+
}
256+
257+
// Validate axis descriptions (fully)
258+
TmfXYAxisDescriptionStub xAxis = seriesStub.getXAxisDescription();
259+
TmfXYAxisDescriptionStub yAxis = seriesStub.getYAxisDescription();
260+
261+
assertNotNull("X axis description should not be null", xAxis);
262+
assertNotNull("Y axis description should not be null", yAxis);
263+
264+
// X axis
265+
assertEquals("X axis label mismatch", "Execution Time", xAxis.getLabel());
266+
assertEquals("X axis unit mismatch", "ns", xAxis.getUnit());
267+
assertEquals("X axis data type mismatch", "DURATION", xAxis.getDataType());
268+
IAxisDomainStub xDomain = xAxis.getAxisDomain();
269+
assertNotNull("X axis domain should not be null", xDomain);
270+
assertTrue("X axis domain should be TimeRange", xDomain instanceof IAxisDomainStub.RangeStub);
271+
272+
// Y axis
273+
assertEquals("Y axis label mismatch", "Number of Executions", yAxis.getLabel());
274+
assertEquals("Y axis unit mismatch", "", yAxis.getUnit());
275+
assertEquals("Y axis data type mismatch", "NUMBER", yAxis.getDataType());
276+
IAxisDomainStub yDomain = yAxis.getAxisDomain();
277+
assertNull("Y axis domain should be null", yDomain);
278+
279+
// Validate style
280+
assertNotNull("Style should not be null", seriesStub.getStyle());
281+
assertEquals("Series type should be bar", "bar",
282+
seriesStub.getStyle().getStyleValues().get(StyleProperties.SERIES_TYPE));
283+
}
284+
}

0 commit comments

Comments
 (0)