ctf: Add tests for CTF trace trimming
[deliverable/tracecompass.git] / ctf / org.eclipse.tracecompass.tmf.ctf.core.tests / src / org / eclipse / tracecompass / tmf / ctf / core / tests / trim / CtfTmfTraceTrimmingTest.java
1 /*******************************************************************************
2 * Copyright (c) 2016 EfficiOS Inc., Alexandre Montplaisir
3 *
4 * All rights reserved. This program and the accompanying materials are
5 * made available under the terms of the Eclipse Public License v1.0 which
6 * accompanies this distribution, and is available at
7 * http://www.eclipse.org/legal/epl-v10.html
8 *******************************************************************************/
9
10 package org.eclipse.tracecompass.tmf.ctf.core.tests.trim;
11
12 import static org.eclipse.tracecompass.common.core.NonNullUtils.checkNotNull;
13 import static org.junit.Assert.assertEquals;
14 import static org.junit.Assert.assertNotNull;
15 import static org.junit.Assert.assertTrue;
16 import static org.junit.Assert.fail;
17
18 import java.io.IOException;
19 import java.nio.file.Files;
20 import java.nio.file.Path;
21 import java.util.Arrays;
22 import java.util.Objects;
23 import java.util.Set;
24 import java.util.concurrent.TimeUnit;
25 import java.util.stream.Collectors;
26
27 import org.apache.commons.io.FileUtils;
28 import org.eclipse.core.runtime.CoreException;
29 import org.eclipse.core.runtime.IStatus;
30 import org.eclipse.core.runtime.NullProgressMonitor;
31 import org.eclipse.jdt.annotation.NonNull;
32 import org.eclipse.tracecompass.testtraces.ctf.CtfTestTrace;
33 import org.eclipse.tracecompass.tmf.core.event.TmfEvent;
34 import org.eclipse.tracecompass.tmf.core.exceptions.TmfTraceException;
35 import org.eclipse.tracecompass.tmf.core.signal.TmfSignalManager;
36 import org.eclipse.tracecompass.tmf.core.signal.TmfTraceOpenedSignal;
37 import org.eclipse.tracecompass.tmf.core.timestamp.TmfTimeRange;
38 import org.eclipse.tracecompass.tmf.core.timestamp.TmfTimestamp;
39 import org.eclipse.tracecompass.tmf.core.trace.ITmfContext;
40 import org.eclipse.tracecompass.tmf.ctf.core.event.CtfTmfEvent;
41 import org.eclipse.tracecompass.tmf.ctf.core.tests.shared.CtfTmfTestTraceUtils;
42 import org.eclipse.tracecompass.tmf.ctf.core.trace.CtfTmfTrace;
43 import org.junit.After;
44 import org.junit.Before;
45 import org.junit.Ignore;
46 import org.junit.Rule;
47 import org.junit.Test;
48 import org.junit.rules.TestRule;
49 import org.junit.rules.Timeout;
50 import org.junit.runner.RunWith;
51 import org.junit.runners.Parameterized;
52 import org.junit.runners.Parameterized.Parameters;
53
54 import com.google.common.collect.ImmutableSet;
55
56 /**
57 * Tests related to the trimming feature of CTF traces
58 * ({@link CtfTmfTrace#trim}).
59 *
60 * @author Alexandre Montplaisir
61 */
62 @Ignore("Trim command requires Babeltrace 2.0, which is not installed on most CIs. Test can be run manually.")
63 @RunWith(Parameterized.class)
64 public class CtfTmfTraceTrimmingTest {
65
66 /** Test timeout */
67 @Rule public TestRule globalTimeout= new Timeout(5, TimeUnit.MINUTES);
68
69 private final @NonNull CtfTestTrace fTestTrace;
70
71 private CtfTmfTrace fOriginalTrace;
72 private long fRequestedTraceCutStart;
73 private long fRequestedTraceCutEnd;
74
75 private CtfTmfTrace fNewTrace;
76 private Path fNewTracePath;
77
78 // ------------------------------------------------------------------------
79 // Test suite definition
80 // ------------------------------------------------------------------------
81
82 @SuppressWarnings("deprecation")
83 private static final Set<CtfTestTrace> BLACKLISTED_TRACES = ImmutableSet.of(
84 /*
85 * Babeltrace 2 currently fails reading the following traces. This is a reader
86 * bug, unrelated to the trace trimming operation.
87 *
88 * See https://bugs.lttng.org/issues/1121 .
89 *
90 * TODO Un-ignore these once BT2 is fixed.
91 */
92 CtfTestTrace.FUNKY_TRACE,
93 CtfTestTrace.DYNSCOPE,
94
95 /*
96 * Ignore the deprecated DEBUG_INFO traces. Those were not generated by a
97 * release version of lttng-ust.
98 */
99 CtfTestTrace.DEBUG_INFO,
100 CtfTestTrace.DEBUG_INFO3,
101
102 /*
103 * Ignore hello-lost, most of the trace range is lost events, so cutting would
104 * give an empty trace.
105 */
106 CtfTestTrace.HELLO_LOST,
107
108 /* Trimming doesn't work on experiments at the moment */
109 CtfTestTrace.TRACE_EXPERIMENT
110 );
111
112 /**
113 * Test parameter generator
114 *
115 * @return The list of constructor parameters, one for each test instance.
116 */
117 @Parameters(name = "{index}: {0}")
118 public static Iterable<Object[]> getTestTraces() {
119 CtfTestTrace[] testTraces = CtfTestTrace.values();
120 return Arrays.stream(testTraces)
121 .filter(testTrace -> !BLACKLISTED_TRACES.contains(testTrace))
122 .map(testTrace -> new Object[] { testTrace })
123 .collect(Collectors.toList());
124 }
125
126 /**
127 * Constructor. Receives parameters defined in {@link #getTestTraces()}.
128 *
129 * @param testTrace
130 * The test trace to use for this test instance.
131 */
132 public CtfTmfTraceTrimmingTest(@NonNull CtfTestTrace testTrace) {
133 fTestTrace = testTrace;
134 }
135
136 // ------------------------------------------------------------------------
137 // Test instance maintenance
138 // ------------------------------------------------------------------------
139
140 /**
141 * Test setup
142 */
143 @Before
144 public void setup() {
145 fOriginalTrace = CtfTmfTestTraceUtils.getTrace(fTestTrace);
146 openTrace(fOriginalTrace);
147
148 fRequestedTraceCutStart = getTraceCutStart(fOriginalTrace);
149 fRequestedTraceCutEnd = getTraceCutEnd(fOriginalTrace);
150
151 assertTrue(fRequestedTraceCutStart >= fOriginalTrace.getStartTime().toNanos());
152 assertTrue(fRequestedTraceCutEnd <= fOriginalTrace.getEndTime().toNanos());
153 assertTrue(fRequestedTraceCutStart < fRequestedTraceCutEnd);
154
155 TmfTimeRange range = new TmfTimeRange(
156 TmfTimestamp.fromNanos(fRequestedTraceCutStart),
157 TmfTimestamp.fromNanos(fRequestedTraceCutEnd));
158 try {
159 /* Perform the trim to create the new trace */
160 fNewTracePath = checkNotNull(Files.createTempDirectory("trimmed-trace-test"));
161 fOriginalTrace.trim(range, fNewTracePath, new NullProgressMonitor());
162
163 /* Initialize the new trace */
164 fNewTrace = new CtfTmfTrace();
165 fNewTrace.initTrace(null, fNewTracePath.toString(), CtfTmfEvent.class);
166 openTrace(fNewTrace);
167
168 } catch (IOException | TmfTraceException e) {
169 fail(e.getMessage());
170 } catch (CoreException e) {
171 /*
172 * CoreException are more or less useless, all the interesting stuff
173 * is in their "status" objects.
174 */
175 String msg;
176 IStatus status = e.getStatus();
177 IStatus[] children = status.getChildren();
178 if (children == null) {
179 msg = status.getMessage();
180 } else {
181 msg = Arrays.stream(children)
182 .map(IStatus::getMessage)
183 .collect(Collectors.joining("\n"));
184 }
185 fail(msg);
186 }
187 }
188
189 /**
190 * Test teardown
191 */
192 @After
193 public void tearDown() {
194 if (fOriginalTrace != null) {
195 fOriginalTrace.dispose();
196 }
197 CtfTmfTestTraceUtils.dispose(fTestTrace);
198
199 if (fNewTrace != null) {
200 fNewTrace.dispose();
201 }
202
203 if (fNewTracePath != null) {
204 FileUtils.deleteQuietly(fNewTracePath.toFile());
205 }
206 }
207
208 /** Simulate a trace being opened */
209 private static void openTrace(CtfTmfTrace trace) {
210 trace.indexTrace(true);
211 TmfSignalManager.dispatchSignal(new TmfTraceOpenedSignal(CtfTmfTraceTrimmingTest.class, trace, null));
212 }
213
214 /**
215 * Get the timestamp at which we should start cutting the trace. It should
216 * be roughly 1/4 into the trace.
217 */
218 private static long getTraceCutStart(CtfTmfTrace trace) {
219 long start = trace.getStartTime().toNanos();
220 long end = trace.getEndTime().toNanos();
221
222 return ((end - start) / 4) + start;
223 }
224
225 /**
226 * Get the timestamp at which we should end the trace cutting. It should be
227 * roughly at half the trace.
228 */
229 private static long getTraceCutEnd(CtfTmfTrace trace) {
230 long start = trace.getStartTime().toNanos();
231 long end = trace.getEndTime().toNanos();
232
233 return ((end - start) / 2) + start;
234 }
235
236 // ------------------------------------------------------------------------
237 // Test methods and helpers
238 // ------------------------------------------------------------------------
239
240 /**
241 * Test that all expected events are present in the new trace.
242 */
243 @Test
244 public void testTrimEvents() {
245 CtfTmfTrace initialTrace = fOriginalTrace;
246 CtfTmfTrace trimmedTrace = fNewTrace;
247 Path newTracePath = fNewTracePath;
248 assertNotNull(initialTrace);
249 assertNotNull(trimmedTrace);
250 assertNotNull(newTracePath);
251
252 /*
253 * Verify the bounds of the new trace are fine. The actual trace can be
254 * smaller than what was requested if there are no events exactly at the
255 * bounds, but should not contain events outside of the requested range.
256 */
257 final long newTraceStartTime = trimmedTrace.getStartTime().toNanos();
258 final long newTraceEndTime = trimmedTrace.getEndTime().toNanos();
259
260 assertTrue("Cut trace start time " + newTraceStartTime
261 + " is earlier than the requested " + fRequestedTraceCutStart,
262 newTraceStartTime >= fRequestedTraceCutStart);
263
264 assertTrue("Cut trace end time " + newTraceEndTime
265 + " is later than the requested " + fRequestedTraceCutEnd,
266 newTraceEndTime <= fRequestedTraceCutEnd);
267
268 /*
269 * Verify that each trace event from the original trace in the given
270 * time range is present in the new one.
271 */
272 ITmfContext context1 = initialTrace.seekEvent(TmfTimestamp.fromNanos(newTraceStartTime));
273 CtfTmfEvent event1 = initialTrace.getNext(context1);
274 ITmfContext context2 = trimmedTrace.seekEvent(0L);
275 CtfTmfEvent event2 = trimmedTrace.getNext(context2);
276
277 int count = 0;
278 while (event1.getTimestamp().toNanos() <= fRequestedTraceCutEnd) {
279 assertNotNull(event1);
280 assertNotNull("Expected event not present in trimmed trace: " + eventToString(event1), event2);
281
282 if (!eventsEquals(event1, event2)) {
283 /*
284 * Skip the test for different events of the exact same timestamp. The library
285 * does not guarantee in which order events of the same timestamp are read.
286 */
287 if (Objects.equals(event1.getTimestamp(), event2.getTimestamp())) {
288 System.err.println("The following events have the exact same timestamp, and may be read in any order:");
289 System.err.println(eventToString(event1));
290 System.err.println(eventToString(event2));
291 } else {
292 fail("The following events are not the same: \n "
293 + eventToString(event1) + '\n'
294 + eventToString(event2));
295 }
296 }
297
298 event1 = initialTrace.getNext(context1);
299 event2 = trimmedTrace.getNext(context2);
300 count++;
301 }
302
303 assertEquals(trimmedTrace.getNbEvents(), count);
304 }
305
306 /**
307 * {@link TmfEvent#equals} checks the container trace, among other things.
308 * Here we want to compare events from different traces, so we have to
309 * implement our own equals().
310 */
311 private static boolean eventsEquals(CtfTmfEvent event1, CtfTmfEvent event2) {
312 return Objects.equals(event1.getTimestamp(), event2.getTimestamp())
313 && Objects.equals(event1.getType(), event2.getType())
314 && Objects.equals(event1.getContent(), event2.getContent())
315 && Objects.equals(event1.getCPU(), event2.getCPU());
316
317 // FIXME This currently gets renamed, but eventually won't.
318 // && Objects.equals(event1.getChannel(), event2.getChannel());
319 }
320
321 private static String eventToString(CtfTmfEvent event) {
322 return com.google.common.base.Objects.toStringHelper(event)
323 .add("Timestamp", event.getTimestamp())
324 .add("Type", event.getType())
325 .add("Content", event.getContent())
326 .add("CPU", event.getCPU())
327 .add("Channel", event.getChannel())
328 .toString();
329 }
330 }
This page took 0.039297 seconds and 5 git commands to generate.