From 31d786ef053f095856db9444f1e1424c9fd9de88 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Genevi=C3=A8ve=20Bastien?= Date: Mon, 4 Apr 2016 16:00:23 -0400 Subject: [PATCH 1/1] tmf.tests: Add tests for TmfStateSystemModule#isQueryable(long) MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit This forces to add behavior to the test state provider so it is possible to fine tune the event processing by adding a method to process events one at a time and signal the provider to process the next event. Change-Id: I999ccd8494c90efce42ba1dc9d59d73b8d018a84 Signed-off-by: Geneviève Bastien Reviewed-on: https://git.eclipse.org/r/71153 Reviewed-by: Hudson CI Reviewed-by: Bernd Hufmann Tested-by: Bernd Hufmann --- .../plugin.xml | 3 + .../StateSystemAnalysisModuleTest.java | 207 +++++++++++++++--- .../stubs/analysis/TestStateSystemModule.java | 35 ++- .../analysis/TestStateSystemProvider.java | 134 ++++++++++-- .../valid/analysis_dependency.xml | 27 +++ .../statesystem/AbstractTmfStateProvider.java | 2 +- 6 files changed, 365 insertions(+), 43 deletions(-) create mode 100644 tmf/org.eclipse.tracecompass.tmf.core.tests/testfiles/stub_xml_traces/valid/analysis_dependency.xml diff --git a/tmf/org.eclipse.tracecompass.tmf.core.tests/plugin.xml b/tmf/org.eclipse.tracecompass.tmf.core.tests/plugin.xml index 771c08f1be..7e7ec520c5 100644 --- a/tmf/org.eclipse.tracecompass.tmf.core.tests/plugin.xml +++ b/tmf/org.eclipse.tracecompass.tmf.core.tests/plugin.xml @@ -54,6 +54,9 @@ + + properties = mod.getProperties(); - assertEquals(mod.getBackendName(), properties.get(Messages.TmfStateSystemAnalysisModule_PropertiesBackend)); - assertEquals(mod.getId(), properties.get(org.eclipse.tracecompass.tmf.core.analysis.Messages.TmfAbstractAnalysisModule_LabelId)); + Map 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, 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(), TmfStateValue.newValueString(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 + */ + @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)); + } } diff --git a/tmf/org.eclipse.tracecompass.tmf.core.tests/stubs/org/eclipse/tracecompass/tmf/tests/stubs/analysis/TestStateSystemModule.java b/tmf/org.eclipse.tracecompass.tmf.core.tests/stubs/org/eclipse/tracecompass/tmf/tests/stubs/analysis/TestStateSystemModule.java index 2ddc53bab7..fa265303d5 100644 --- a/tmf/org.eclipse.tracecompass.tmf.core.tests/stubs/org/eclipse/tracecompass/tmf/tests/stubs/analysis/TestStateSystemModule.java +++ b/tmf/org.eclipse.tracecompass.tmf.core.tests/stubs/org/eclipse/tracecompass/tmf/tests/stubs/analysis/TestStateSystemModule.java @@ -15,6 +15,7 @@ package org.eclipse.tracecompass.tmf.tests.stubs.analysis; import static org.eclipse.tracecompass.common.core.NonNullUtils.checkNotNull; import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; import org.eclipse.tracecompass.tmf.core.statesystem.ITmfStateProvider; import org.eclipse.tracecompass.tmf.core.statesystem.TmfStateSystemAnalysisModule; @@ -26,9 +27,17 @@ import org.eclipse.tracecompass.tmf.core.statesystem.TmfStateSystemAnalysisModul @NonNullByDefault public class TestStateSystemModule extends TmfStateSystemAnalysisModule { + private @Nullable TestStateSystemProvider fProvider = null; + private boolean fThrottleEvents = false; + @Override protected ITmfStateProvider createStateProvider() { - return new TestStateSystemProvider(checkNotNull(getTrace())); + + TestStateSystemProvider provider = new TestStateSystemProvider(checkNotNull(getTrace())); + fProvider = provider; + boolean throttle = fThrottleEvents; + provider.setThrottling(throttle); + return provider; } @Override @@ -45,4 +54,28 @@ public class TestStateSystemModule extends TmfStateSystemAnalysisModule { return StateSystemBackendType.INMEM.name(); } + /** + * Set whether events are processed one at a time + * + * @param throttleEvent A value of true will have the events processed one a time instead of adding them all to the queue. To process the next event, one must call the {@link #signalNextEvent()} method. A value of false will return to default behavior. + */ + public void setPerEventSignalling(boolean throttleEvent) { + fThrottleEvents = throttleEvent; + TestStateSystemProvider provider = fProvider; + if (provider != null) { + provider.setThrottling(throttleEvent); + } + } + + /** + * Signal for the next event to be processed. This makes sense only if + * {@link #setPerEventSignalling(boolean)} method has been set to true + */ + public void signalNextEvent() { + TestStateSystemProvider provider = fProvider; + if (provider != null) { + provider.signalNextEvent(); + } + } + } diff --git a/tmf/org.eclipse.tracecompass.tmf.core.tests/stubs/org/eclipse/tracecompass/tmf/tests/stubs/analysis/TestStateSystemProvider.java b/tmf/org.eclipse.tracecompass.tmf.core.tests/stubs/org/eclipse/tracecompass/tmf/tests/stubs/analysis/TestStateSystemProvider.java index 87292b9d1f..bfc692ac6d 100644 --- a/tmf/org.eclipse.tracecompass.tmf.core.tests/stubs/org/eclipse/tracecompass/tmf/tests/stubs/analysis/TestStateSystemProvider.java +++ b/tmf/org.eclipse.tracecompass.tmf.core.tests/stubs/org/eclipse/tracecompass/tmf/tests/stubs/analysis/TestStateSystemProvider.java @@ -1,20 +1,22 @@ /******************************************************************************* - * Copyright (c) 2013, 2015 École Polytechnique de Montréal + * 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 v1.0 which * accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * Geneviève Bastien - Initial API and implementation *******************************************************************************/ package org.eclipse.tracecompass.tmf.tests.stubs.analysis; import static org.eclipse.tracecompass.common.core.NonNullUtils.checkNotNull; +import java.util.concurrent.locks.Condition; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; + import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.jdt.annotation.Nullable; import org.eclipse.tracecompass.statesystem.core.ITmfStateSystemBuilder; import org.eclipse.tracecompass.statesystem.core.exceptions.AttributeNotFoundException; import org.eclipse.tracecompass.statesystem.core.exceptions.StateValueTypeException; @@ -32,9 +34,59 @@ import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace; */ public class TestStateSystemProvider extends AbstractTmfStateProvider { + /** + * This interface allows unit tests to provide only the event handling part + * of the state provider, without having to extend the analysis and the + * classes + */ + @FunctionalInterface + public static interface TestStateProviderHandler { + /** + * Handles the event + * + * @param ss + * The state system builder + * @param event + * The event to handler + * @return true if everything went fine, or false to cancel + */ + boolean eventHandle(@NonNull ITmfStateSystemBuilder ss, ITmfEvent event); + } + private static final int VERSION = 1; - private final String fString = "[]"; - private int fCount = 0; + private static final String fString = "[]"; + private static int fCount = 0; + private static final @NonNull TestStateProviderHandler DEFAULT_HANDLER = (ss, event) -> { + /* Just need something to fill the state system */ + if (fString.equals(event.getContent().getValue())) { + try { + int quarkId = ss.getQuarkAbsoluteAndAdd("String"); + int quark = ss.getQuarkRelativeAndAdd(quarkId, fString); + ss.modifyAttribute(event.getTimestamp().getValue(), TmfStateValue.newValueInt(fCount++), quark); + } catch (TimeRangeException | AttributeNotFoundException | StateValueTypeException e) { + + } + } + return true; + }; + private static @NonNull TestStateProviderHandler sfHandler = DEFAULT_HANDLER; + + /** + * Set the event handler for the state provider + * + * @param handler + * The class containing the event handler for this state provider + */ + public static void setEventHandler(TestStateProviderHandler handler) { + if (handler == null) { + sfHandler = DEFAULT_HANDLER; + return; + } + sfHandler = handler; + } + + private final Lock fLock = new ReentrantLock(); + private @Nullable Condition fNextEventSignal = null; /** * Constructor @@ -59,20 +111,72 @@ public class TestStateSystemProvider extends AbstractTmfStateProvider { @Override protected void eventHandle(ITmfEvent event) { ITmfStateSystemBuilder ss = checkNotNull(getStateSystemBuilder()); + sfHandler.eventHandle(ss, event); + } - /* Just need something to fill the state system */ - if (fString.equals(event.getContent().getValue())) { - try { - int quarkId = ss.getQuarkAbsoluteAndAdd("String"); - int quark = ss.getQuarkRelativeAndAdd(quarkId, fString); - ss.modifyAttribute(event.getTimestamp().getValue(), TmfStateValue.newValueInt(fCount++), quark); - } catch (TimeRangeException e) { - } catch (AttributeNotFoundException e) { - } catch (StateValueTypeException e) { + @Override + public void processEvent(@NonNull ITmfEvent event) { + fLock.lock(); + try { + Condition cond = fNextEventSignal; + if (cond != null) { + cond.await(); + } + } catch (InterruptedException e) { + + } finally { + super.processEvent(event); + fLock.unlock(); + } + } + + /** + * Set the processing of event to be one event at a time instead of the + * default behavior. It will block until the next call to + * {@link #signalNextEvent()} method call. + * + * @param throttleEvent + * Whether to wait for a signal to process the next event + */ + public void setThrottling(boolean throttleEvent) { + fLock.lock(); + try { + if (throttleEvent) { + Condition cond = fNextEventSignal; + // If called for the first time, create a condition + if (cond == null) { + cond = fLock.newCondition(); + fNextEventSignal = cond; + } + + } else { + Condition cond = fNextEventSignal; + if (cond != null) { + fNextEventSignal = null; + cond.signalAll(); + } + } + } finally { + fLock.unlock(); + } + + } + /** + * Signal for the next event to be processed. Calling this method makes + * sense only if {@link #setThrottling(boolean)} has been set to true + */ + public void signalNextEvent() { + fLock.lock(); + try { + Condition cond = fNextEventSignal; + if (cond != null) { + cond.signalAll(); } + } finally { + fLock.unlock(); } } diff --git a/tmf/org.eclipse.tracecompass.tmf.core.tests/testfiles/stub_xml_traces/valid/analysis_dependency.xml b/tmf/org.eclipse.tracecompass.tmf.core.tests/testfiles/stub_xml_traces/valid/analysis_dependency.xml new file mode 100644 index 0000000000..ab726a65e1 --- /dev/null +++ b/tmf/org.eclipse.tracecompass.tmf.core.tests/testfiles/stub_xml_traces/valid/analysis_dependency.xml @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/tmf/org.eclipse.tracecompass.tmf.core/src/org/eclipse/tracecompass/tmf/core/statesystem/AbstractTmfStateProvider.java b/tmf/org.eclipse.tracecompass.tmf.core/src/org/eclipse/tracecompass/tmf/core/statesystem/AbstractTmfStateProvider.java index 319f620c0a..f6cd62006c 100644 --- a/tmf/org.eclipse.tracecompass.tmf.core/src/org/eclipse/tracecompass/tmf/core/statesystem/AbstractTmfStateProvider.java +++ b/tmf/org.eclipse.tracecompass.tmf.core/src/org/eclipse/tracecompass/tmf/core/statesystem/AbstractTmfStateProvider.java @@ -123,7 +123,7 @@ public abstract class AbstractTmfStateProvider implements ITmfStateProvider { } @Override - public final void processEvent(ITmfEvent event) { + public void processEvent(ITmfEvent event) { /* Make sure the target state system has been assigned */ if (!fStateSystemAssigned) { Activator.logError("Cannot process event without a target state system"); //$NON-NLS-1$ -- 2.34.1