blob: 7dc3eba419eb6d42500e3bd75c939e4f7107b5d3 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2013, 2016 École Polytechnique de Montréal
*
* 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.tmf.core.tests.statesystem;
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.io.File;
import java.util.List;
import java.util.Map;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.TimeUnit;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.tracecompass.statesystem.core.ITmfStateSystem;
import org.eclipse.tracecompass.statesystem.core.interval.ITmfStateInterval;
import org.eclipse.tracecompass.statesystem.core.tests.shared.utils.StateIntervalStub;
import org.eclipse.tracecompass.statesystem.core.tests.shared.utils.StateSystemTestUtils;
import org.eclipse.tracecompass.tmf.core.event.ITmfEvent;
import org.eclipse.tracecompass.tmf.core.exceptions.TmfAnalysisException;
import org.eclipse.tracecompass.tmf.core.signal.TmfTraceOpenedSignal;
import org.eclipse.tracecompass.tmf.core.statesystem.ITmfStateProvider;
import org.eclipse.tracecompass.tmf.core.statesystem.ITmfStateProvider.FutureEventType;
import org.eclipse.tracecompass.tmf.core.statesystem.Messages;
import org.eclipse.tracecompass.tmf.core.statesystem.TmfStateSystemAnalysisModule;
import org.eclipse.tracecompass.tmf.core.tests.TmfCoreTestPlugin;
import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace;
import org.eclipse.tracecompass.tmf.tests.stubs.analysis.TestStateSystemModule;
import org.eclipse.tracecompass.tmf.tests.stubs.analysis.TestStateSystemProvider;
import org.eclipse.tracecompass.tmf.tests.stubs.trace.xml.TmfXmlTraceStub;
import org.eclipse.tracecompass.tmf.tests.stubs.trace.xml.TmfXmlTraceStubNs;
import org.junit.After;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TestRule;
import org.junit.rules.Timeout;
import com.google.common.collect.ImmutableList;
/**
* Test the {@link TmfStateSystemAnalysisModule} class
*
* @author Geneviève Bastien
*/
public class StateSystemAnalysisModuleTest {
private final class BreakingTest extends TestStateSystemProvider {
private final int fEventNo;
private int fEventCount = 0;
private String fExceptionMessage;
private BreakingTest(@NonNull ITmfTrace trace, int eventToFail, String msg) {
super(trace, 1, 2);
fEventNo = eventToFail;
fExceptionMessage = msg;
}
@Override
protected void eventHandle(@NonNull ITmfEvent event) {
fEventCount++;
if (fEventCount == fEventNo) {
throw new IllegalArgumentException(fExceptionMessage);
}
}
}
/** Time-out tests after 1 minute. */
@Rule
public TestRule globalTimeout = new Timeout(1, TimeUnit.MINUTES);
/** ID of the test state system analysis module */
public static final String MODULE_SS = "org.eclipse.linuxtools.tmf.core.tests.analysis.sstest";
private static final String XML_TRACE = "testfiles/stub_xml_traces/valid/analysis_dependency.xml";
private TestStateSystemModule fModule;
private ITmfTrace fTrace;
/**
* Setup test trace
*/
@Before
public void setupTraces() {
TmfXmlTraceStub trace = TmfXmlTraceStubNs.setupTrace(TmfCoreTestPlugin.getAbsoluteFilePath(XML_TRACE));
trace.traceOpened(new TmfTraceOpenedSignal(this, trace, null));
fTrace = trace;
fModule = (TestStateSystemModule) trace.getAnalysisModule(MODULE_SS);
}
/**
* Some tests use traces, let's clean them here
*/
@After
public void cleanupTraces() {
fTrace.dispose();
}
/**
* Test the state system module execution and result
*/
@Test
public void testSsModule() {
ITmfStateSystem ss = fModule.getStateSystem();
assertNull(ss);
fModule.schedule();
if (fModule.waitForCompletion()) {
ss = fModule.getStateSystem();
assertNotNull(ss);
} else {
fail("Module did not complete properly");
}
}
/**
* Make sure that the state system is initialized after calling
* {@link TmfStateSystemAnalysisModule#waitForInitialization()}.
*/
@Test
public void testInitialization() {
assertNull(fModule.getStateSystem());
fModule.schedule();
assertTrue("Initialization succeeded", fModule.waitForInitialization());
assertNotNull(fModule.getStateSystem());
}
/**
* Test that helper returns the right properties
*/
@Test
public void testProperties() {
/* The stub state system has in mem backend 2 properties */
Map<String, String> properties = fModule.getProperties();
assertEquals(fModule.getBackendName(), properties.get(Messages.TmfStateSystemAnalysisModule_PropertiesBackend));
assertEquals(fModule.getId(), properties.get(org.eclipse.tracecompass.tmf.core.analysis.Messages.TmfAbstractAnalysisModule_LabelId));
}
private static final String CRUCIAL_EVENT = "crucialEvent";
private static final String CRUCIAL_FIELD = "crucialInfo";
private static void setupDependentAnalysisHandler(CyclicBarrier barrier) {
TestStateSystemProvider.setEventHandler((ss, provider, event) -> {
try {
/* Wait before processing the current event */
barrier.await();
if (event.getName().equals(CRUCIAL_EVENT)) {
String crucialInfo = (String) event.getContent().getField(CRUCIAL_FIELD).getValue();
int quark = ss.getQuarkAbsoluteAndAdd(CRUCIAL_FIELD);
try {
ss.modifyAttribute(event.getTimestamp().toNanos(), crucialInfo, quark);
} catch (Exception e) {
fail(e.getMessage());
}
}
/* Wait before processing the next event */
barrier.await();
return true;
} catch (InterruptedException | BrokenBarrierException e1) {
return false;
}
});
}
/**
* Test the {@link TmfStateSystemAnalysisModule#isQueryable(long)} method
*/
@Test
public void testIsQueryable() {
CyclicBarrier barrier = new CyclicBarrier(2);
setupDependentAnalysisHandler(barrier);
TestStateSystemModule module = fModule;
assertNotNull(module);
/* Module is not started, it should be queriable */
assertTrue(module.isQueryable(1));
assertTrue(module.isQueryable(4));
assertTrue(module.isQueryable(5));
assertTrue(module.isQueryable(7));
assertTrue(module.isQueryable(10));
module.schedule();
assertTrue(module.waitForInitialization());
assertFalse(module.isQueryable(1));
try {
/* 2 waits for a barrier for one event */
// event 1
barrier.await();
barrier.await();
// event 2
barrier.await();
assertTrue(module.isQueryable(1));
assertTrue(module.isQueryable(4));
assertFalse(module.isQueryable(5));
barrier.await();
// event 3
barrier.await();
assertTrue(module.isQueryable(1));
assertTrue(module.isQueryable(4));
assertFalse(module.isQueryable(5));
barrier.await();
// event 4
barrier.await();
assertTrue(module.isQueryable(1));
assertTrue(module.isQueryable(4));
assertFalse(module.isQueryable(5));
barrier.await();
// event 5
barrier.await();
assertTrue(module.isQueryable(1));
assertTrue(module.isQueryable(4));
assertTrue(module.isQueryable(5));
assertFalse(module.isQueryable(7));
barrier.await();
// event 6
barrier.await();
assertTrue(module.isQueryable(1));
assertTrue(module.isQueryable(4));
assertTrue(module.isQueryable(5));
assertFalse(module.isQueryable(7));
barrier.await();
// event 7
barrier.await();
assertTrue(module.isQueryable(1));
assertTrue(module.isQueryable(4));
assertTrue(module.isQueryable(5));
assertTrue(module.isQueryable(7));
assertFalse(module.isQueryable(10));
barrier.await();
fModule.waitForCompletion();
assertTrue(module.isQueryable(1));
assertTrue(module.isQueryable(4));
assertTrue(module.isQueryable(5));
assertTrue(module.isQueryable(7));
assertTrue(module.isQueryable(10));
// Should return true only if later than trace time
assertTrue(module.isQueryable(100));
} catch (InterruptedException | BrokenBarrierException e1) {
fail(e1.getMessage());
fModule.cancel();
} finally {
TestStateSystemProvider.setEventHandler(null);
}
}
/**
* Test the {@link TmfStateSystemAnalysisModule#isQueryable(long)} method
* when the analysis is cancelled
*/
@Ignore("Hangs very often")
@Test
public void testIsQueryableCancel() {
TestStateSystemModule module = fModule;
assertNotNull(module);
/* Set the queue to 1 to limit the number of events buffered */
module.setPerEventSignalling(true);
/* Module is not started, it should be queriable */
assertTrue(module.isQueryable(1));
assertTrue(module.isQueryable(4));
assertTrue(module.isQueryable(5));
assertTrue(module.isQueryable(7));
assertTrue(module.isQueryable(10));
fModule.schedule();
assertTrue(module.waitForInitialization());
assertFalse(module.isQueryable(1));
// Process 2 events, then cancel
module.signalNextEvent();
module.signalNextEvent();
module.cancel();
module.setPerEventSignalling(false);
fModule.waitForCompletion();
assertTrue(module.isQueryable(1));
assertTrue(module.isQueryable(4));
assertTrue(module.isQueryable(5));
assertTrue(module.isQueryable(7));
assertTrue(module.isQueryable(10));
}
/**
* Test that an analysis with full backend is re-read correctly
*
* @throws TmfAnalysisException
* Propagates exceptions
*/
@Test
public void testReReadFullAnalysis() throws TmfAnalysisException {
TestStateSystemModule module = new TestStateSystemModule(true);
TestStateSystemModule module2 = new TestStateSystemModule(true);
try {
ITmfTrace trace = fTrace;
assertNotNull(trace);
module.setTrace(trace);
module2.setTrace(trace);
// Execute the first module
module.schedule();
assertTrue(module.waitForCompletion());
// Execute the second module, it should read the state system file
File ssFile = module2.getSsFile();
assertNotNull(ssFile);
assertTrue(ssFile.exists());
module2.schedule();
assertTrue(module2.waitForCompletion());
} finally {
module.dispose();
module2.dispose();
}
}
/**
* Test that an analysis whose event request throws an exception is failed
* correctly
*
* @throws TmfAnalysisException
* Propagates exceptions
*/
@Test
public void testRequestFailure() throws TmfAnalysisException {
TestStateSystemModule module = new TestStateSystemModule();
module.setRequestAction(e -> {
throw new IllegalArgumentException("This exception is desired and part of the test");
});
try {
ITmfTrace trace = fTrace;
assertNotNull(trace);
module.setTrace(trace);
// Execute the module that should throw the exception
module.schedule();
assertFalse(module.waitForCompletion());
} finally {
module.dispose();
}
}
/**
* Test the behavior of a state provider that causes a runtime exception at
* different moments of the analysis. The analyses should be marked as
* failed
*
* @throws TmfAnalysisException
* An exception when setting the trace
*/
@Test
public void testFaultyStateProvider() throws TmfAnalysisException {
ITmfTrace trace = fTrace;
assertNotNull(trace);
// Test failure on the last event
TmfStateSystemAnalysisModule module = new TestStateSystemModule() {
@Override
protected @NonNull ITmfStateProvider createStateProvider() {
return new BreakingTest(trace, 7, "Expected exception: should be caught by the analysis itself");
}
};
try {
module.setTrace(trace);
module.schedule();
assertFalse(module.waitForCompletion());
} finally {
module.dispose();
}
// Test failure when the analysis the request finishes before the queue
// is full
module = new TestStateSystemModule() {
@Override
protected @NonNull ITmfStateProvider createStateProvider() {
return new BreakingTest(trace, 5, "Expected exception: should be caught by either the analysis or the event request");
}
};
try {
module.setTrace(trace);
module.schedule();
assertFalse(module.waitForCompletion());
} finally {
module.dispose();
}
// Test failure when the queue should be full
module = new TestStateSystemModule() {
@Override
protected @NonNull ITmfStateProvider createStateProvider() {
return new BreakingTest(trace, 1, "Expected exception: should be caught by the event request thread");
}
};
try {
module.setTrace(trace);
module.schedule();
assertFalse(module.waitForCompletion());
} finally {
module.dispose();
}
}
/**
* Test a module that causes an exception before the module is initialized
*
* @throws TmfAnalysisException
* An exception when setting the trace
*/
@Test
public void testFailBeforeInitialization() throws TmfAnalysisException {
ITmfTrace trace = fTrace;
assertNotNull(trace);
/* This module will throw an exception before it is initialized */
TmfStateSystemAnalysisModule module = new TestStateSystemModule() {
@Override
protected boolean executeAnalysis(@Nullable IProgressMonitor monitor) {
throw new IllegalStateException("This exception happens before initialization");
}
};
try {
module.setTrace(trace);
module.schedule();
assertFalse(module.waitForInitialization());
assertFalse(module.waitForCompletion());
} finally {
module.dispose();
}
}
/**
* Test adding future values to the state system
*/
@Test
public void testFutureEvents() {
String futureVal = "futureVal";
String futureStack = "futureStack";
int oneValue = 100;
String stack1 = "stack1";
String stack2 = "stack2";
try {
TestStateSystemProvider.setEventHandler((ss, provider, event) -> {
// Add future events
if (event.getTimestamp().toNanos() == 1) {
// initialize a value quark and modify it in future
int valueQuark = ss.getQuarkAbsoluteAndAdd(futureVal);
ss.modifyAttribute(event.getTimestamp().toNanos(), oneValue, valueQuark);
provider.addFutureEvent(5, null, valueQuark, FutureEventType.MODIFICATION);
// Initialize a stack and modify it in future
int stackQuark = ss.getQuarkAbsoluteAndAdd(futureStack);
ss.pushAttribute(event.getTimestamp().toNanos(), stack1, stackQuark);
provider.addFutureEvent(4, stack2, stackQuark, FutureEventType.PUSH);
provider.addFutureEvent(7, stack2, stackQuark, FutureEventType.POP);
provider.addFutureEvent(8, stack1, stackQuark, FutureEventType.POP);
}
return true;
});
TestStateSystemModule module = fModule;
module.schedule();
assertTrue(module.waitForCompletion());
ITmfStateSystem ss = module.getStateSystem();
assertNotNull(ss);
List<@NonNull ITmfStateInterval> expected = ImmutableList.of(new StateIntervalStub(1, 4, oneValue), new StateIntervalStub(5, 10, (Object) null));
StateSystemTestUtils.testIntervalForAttributes(ss, expected, futureVal);
expected = ImmutableList.of(new StateIntervalStub(1, 7, stack1), new StateIntervalStub(8, 10, (Object) null));
StateSystemTestUtils.testIntervalForAttributes(ss, expected, futureStack, "1");
expected = ImmutableList.of( new StateIntervalStub(1, 3, (Object) null),
new StateIntervalStub(4, 6, stack2), new StateIntervalStub(7, 10, (Object) null));
StateSystemTestUtils.testIntervalForAttributes(ss, expected, futureStack, "2");
} finally {
TestStateSystemProvider.setEventHandler(null);
}
}
}