From 29c0b5051b9f5b1ad98590ce4032c4d4b544acd5 Mon Sep 17 00:00:00 2001 From: Alexandre Montplaisir Date: Fri, 12 Feb 2016 11:37:05 -0500 Subject: [PATCH] Add tests for filter notification ordering There was a bug in the Java agent with regards to agent teardown: upon disconnection, the list of tracked events was cleared but no corresponding filter change notifications were sent! This could leave some event rules dangling, and future listeners would get incorrectly notified that these events were enabled while they were not. Add some tests to cover for this use case. Signed-off-by: Alexandre Montplaisir --- .../filter/FilterListenerITBase.java | 5 +- .../filter/FilterListenerOrderingITBase.java | 314 ++++++++++++++++++ .../filter/JulFilterListenerOrderingIT.java | 82 +++++ .../filter/Log4jFilterListenerOrderingIT.java | 82 +++++ 4 files changed, 482 insertions(+), 1 deletion(-) create mode 100644 lttng-ust-java-tests-common/src/main/java/org/lttng/ust/agent/integration/filter/FilterListenerOrderingITBase.java create mode 100644 lttng-ust-java-tests-jul/src/test/java/org/lttng/ust/agent/integration/filter/JulFilterListenerOrderingIT.java create mode 100644 lttng-ust-java-tests-log4j/src/test/java/org/lttng/ust/agent/integration/filter/Log4jFilterListenerOrderingIT.java diff --git a/lttng-ust-java-tests-common/src/main/java/org/lttng/ust/agent/integration/filter/FilterListenerITBase.java b/lttng-ust-java-tests-common/src/main/java/org/lttng/ust/agent/integration/filter/FilterListenerITBase.java index 020ad36..dddcaee 100644 --- a/lttng-ust-java-tests-common/src/main/java/org/lttng/ust/agent/integration/filter/FilterListenerITBase.java +++ b/lttng-ust-java-tests-common/src/main/java/org/lttng/ust/agent/integration/filter/FilterListenerITBase.java @@ -74,6 +74,8 @@ public abstract class FilterListenerITBase { listener = new TestFilterListener(); FilterChangeNotifier.getInstance().registerListener(listener); session = ILttngSession.createSession(null, getSessionDomain()); + + assertEquals(0, listener.getNbNotifications()); } /** @@ -83,6 +85,7 @@ public abstract class FilterListenerITBase { public void teardown() { session.close(); FilterChangeNotifier.getInstance().unregisterListener(listener); + listener = null; handler.close(); } @@ -337,7 +340,7 @@ public abstract class FilterListenerITBase { /** * The filter listener used for tests. */ - private static class TestFilterListener implements IFilterChangeListener { + static class TestFilterListener implements IFilterChangeListener { private final Set currentRules = new HashSet<>(); private volatile int currentNotifications = 0; diff --git a/lttng-ust-java-tests-common/src/main/java/org/lttng/ust/agent/integration/filter/FilterListenerOrderingITBase.java b/lttng-ust-java-tests-common/src/main/java/org/lttng/ust/agent/integration/filter/FilterListenerOrderingITBase.java new file mode 100644 index 0000000..c226d45 --- /dev/null +++ b/lttng-ust-java-tests-common/src/main/java/org/lttng/ust/agent/integration/filter/FilterListenerOrderingITBase.java @@ -0,0 +1,314 @@ +/* + * Copyright (C) 2016, EfficiOS Inc., Alexandre Montplaisir + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package org.lttng.ust.agent.integration.filter; + +import static org.junit.Assert.assertEquals; + +import java.util.Collections; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.junit.After; +import org.junit.Test; +import org.lttng.tools.ILttngSession; +import org.lttng.ust.agent.filter.FilterChangeNotifier; +import org.lttng.ust.agent.integration.filter.FilterListenerITBase.TestFilterListener; +import org.lttng.ust.agent.session.EventRule; +import org.lttng.ust.agent.utils.EventRuleFactory; + +/** + * For the filter change notifications to work, several setup steps are + * required: + * + *
    + *
  • Initialize the Java agent register it to the sessiond [Agent]
  • + *
  • Instantiate a filer change listener, and register it to the notifier + * [Listener]
  • + *
  • Apply some event rule changes in the tracing session (lttng enable-event, + * etc.) [Session]
  • + *
+ * + *

+ * Then on teardown, the following steps are expected: + *

+ * + *
    + *
  • Dispose the Java agent, closing the connection to the sessiond [Agent] + *
  • + *
  • Destroy the tracing session, removing tracked events [Session]
  • + *
+ * + * (and then the filter change listener should be de-registered from the + * notifier. If it is deregistered earlier, then obviously no notifications + * would be received thereafter). + * + *

+ * Within these two sets, each step can happen in any order. This results in 6 x + * 2 = 12 possibilities. The goal of this test class it to test these 12 + * possibilities. + *

+ */ +@SuppressWarnings("javadoc") +public abstract class FilterListenerOrderingITBase { + + protected static final String EVENT_NAME_A = "EventA"; + private static final String EVENT_NAME_B = "EventB"; + + private ILttngSession session; + private TestFilterListener listener; + + /** + * Base class cleanup + */ + @After + public void baseTeardown() { + /* + * Deregister the listener (should always be done after all the other + * steps). + */ + FilterChangeNotifier.getInstance().unregisterListener(listener); + listener = null; + } + + // ------------------------------------------------------------------------ + // Utility methods + // ------------------------------------------------------------------------ + + protected abstract ILttngSession.Domain getDomain(); + + protected abstract void registerAgent(); + + private void registerListener() { + listener = new TestFilterListener(); + FilterChangeNotifier.getInstance().registerListener(listener); + } + + private void enableRulesInSession() { + session = ILttngSession.createCommandLineSession(null, getDomain()); + session.enableEvent(EVENT_NAME_A, null, false, null); + session.enableEvent(EVENT_NAME_B, null, false, null); + } + + protected abstract void deregisterAgent(); + + private void destroySession() { + session.close(); + session = null; + } + + // ------------------------------------------------------------------------ + // Test methods + // ------------------------------------------------------------------------ + + /** + * Check that the expected event rules are present after setup but before + * teardown. + */ + private void checkOngoingConditions() { + Set exptectedRules = Stream.of( + EventRuleFactory.createRule(EVENT_NAME_A), + EventRuleFactory.createRule(EVENT_NAME_B)) + .collect(Collectors.toSet()); + + assertEquals(2, listener.getNbNotifications()); + assertEquals(exptectedRules, listener.getCurrentRules()); + } + + /** + * Check that the expected event rules are present after/during teardown. + */ + private void checkFinalConditions() { + Set expectedRules = Collections.EMPTY_SET; + + assertEquals(4, listener.getNbNotifications()); + assertEquals(expectedRules, listener.getCurrentRules()); + } + + @Test + public void testAgentListenerSession_AgentSession() { + registerAgent(); + registerListener(); + enableRulesInSession(); + + checkOngoingConditions(); + + deregisterAgent(); + destroySession(); + + checkFinalConditions(); + } + + @Test + public void testAgentSessionListener_AgentSession() { + registerAgent(); + enableRulesInSession(); + registerListener(); + + checkOngoingConditions(); + + deregisterAgent(); + destroySession(); + + checkFinalConditions(); + } + + @Test + public void testListenerAgentSession_AgentSession() { + registerListener(); + registerAgent(); + enableRulesInSession(); + + checkOngoingConditions(); + + deregisterAgent(); + destroySession(); + + checkFinalConditions(); + } + + @Test + public void testListenerSessionAgent_AgentSession() { + registerListener(); + enableRulesInSession(); + registerAgent(); + + checkOngoingConditions(); + + deregisterAgent(); + destroySession(); + + checkFinalConditions(); + } + + @Test + public void testSessionAgentListener_AgentSession() { + enableRulesInSession(); + registerAgent(); + registerListener(); + + checkOngoingConditions(); + + deregisterAgent(); + destroySession(); + + checkFinalConditions(); + } + + @Test + public void testSessionListenerAgent_AgentSession() { + enableRulesInSession(); + registerListener(); + registerAgent(); + + checkOngoingConditions(); + + deregisterAgent(); + destroySession(); + + checkFinalConditions(); + } + + + + @Test + public void testAgentListenerSession_SessionAgent() { + registerAgent(); + registerListener(); + enableRulesInSession(); + + checkOngoingConditions(); + + destroySession(); + checkFinalConditions(); + deregisterAgent(); + checkFinalConditions(); + } + + @Test + public void testAgentSessionListener_SessionAgent() { + registerAgent(); + enableRulesInSession(); + registerListener(); + + checkOngoingConditions(); + + destroySession(); + checkFinalConditions(); + deregisterAgent(); + checkFinalConditions(); + } + + @Test + public void testListenerAgentSession_SessionAgent() { + registerListener(); + registerAgent(); + enableRulesInSession(); + + checkOngoingConditions(); + + destroySession(); + checkFinalConditions(); + deregisterAgent(); + checkFinalConditions(); + } + + @Test + public void testListenerSessionAgent_SessionAgent() { + registerListener(); + enableRulesInSession(); + registerAgent(); + + checkOngoingConditions(); + + destroySession(); + checkFinalConditions(); + deregisterAgent(); + checkFinalConditions(); + } + + @Test + public void testSessionAgentListener_SessionAgent() { + enableRulesInSession(); + registerAgent(); + registerListener(); + + checkOngoingConditions(); + + destroySession(); + checkFinalConditions(); + deregisterAgent(); + checkFinalConditions(); + } + + @Test + public void testSessionListenerAgent_SessionAgent() { + enableRulesInSession(); + registerListener(); + registerAgent(); + + checkOngoingConditions(); + + destroySession(); + checkFinalConditions(); + deregisterAgent(); + checkFinalConditions(); + } + +} diff --git a/lttng-ust-java-tests-jul/src/test/java/org/lttng/ust/agent/integration/filter/JulFilterListenerOrderingIT.java b/lttng-ust-java-tests-jul/src/test/java/org/lttng/ust/agent/integration/filter/JulFilterListenerOrderingIT.java new file mode 100644 index 0000000..e4a81b8 --- /dev/null +++ b/lttng-ust-java-tests-jul/src/test/java/org/lttng/ust/agent/integration/filter/JulFilterListenerOrderingIT.java @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2016, EfficiOS Inc., Alexandre Montplaisir + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package org.lttng.ust.agent.integration.filter; + +import static org.junit.Assert.fail; +import static org.junit.Assume.assumeTrue; + +import java.io.IOException; +import java.util.logging.Handler; +import java.util.logging.Level; +import java.util.logging.Logger; + +import org.junit.BeforeClass; +import org.lttng.tools.LttngToolsHelper; +import org.lttng.tools.ILttngSession.Domain; +import org.lttng.ust.agent.jul.LttngLogHandler; +import org.lttng.ust.agent.utils.JulTestUtils; +import org.lttng.ust.agent.utils.LttngUtils; + +/** + * Implementation of {@link FilterListenerOrderingITBase} for the JUL API. + */ +public class JulFilterListenerOrderingIT extends FilterListenerOrderingITBase { + + private Logger logger; + private Handler handler; + + /** + * Class setup + */ + @BeforeClass + public static void julClassSetup() { + /* Skip tests if we can't find the JNI library or lttng-tools */ + assumeTrue(JulTestUtils.checkForJulLibrary()); + assumeTrue(LttngUtils.checkForLttngTools(Domain.JUL)); + + LttngToolsHelper.destroyAllSessions(); + } + + @Override + protected Domain getDomain() { + return Domain.JUL; + } + + @Override + protected void registerAgent() { + logger = Logger.getLogger(EVENT_NAME_A); + logger.setLevel(Level.ALL); + + try { + handler = new LttngLogHandler(); + } catch (SecurityException | IOException e) { + fail(); + } + logger.addHandler(handler); + } + + @Override + protected void deregisterAgent() { + logger.removeHandler(handler); + logger = null; + + handler.close(); + handler = null; + } +} diff --git a/lttng-ust-java-tests-log4j/src/test/java/org/lttng/ust/agent/integration/filter/Log4jFilterListenerOrderingIT.java b/lttng-ust-java-tests-log4j/src/test/java/org/lttng/ust/agent/integration/filter/Log4jFilterListenerOrderingIT.java new file mode 100644 index 0000000..e74a5d1 --- /dev/null +++ b/lttng-ust-java-tests-log4j/src/test/java/org/lttng/ust/agent/integration/filter/Log4jFilterListenerOrderingIT.java @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2016, EfficiOS Inc., Alexandre Montplaisir + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package org.lttng.ust.agent.integration.filter; + +import static org.junit.Assert.fail; +import static org.junit.Assume.assumeTrue; + +import java.io.IOException; + +import org.apache.log4j.Appender; +import org.apache.log4j.Level; +import org.apache.log4j.Logger; +import org.junit.BeforeClass; +import org.lttng.tools.LttngToolsHelper; +import org.lttng.tools.ILttngSession.Domain; +import org.lttng.ust.agent.log4j.LttngLogAppender; +import org.lttng.ust.agent.utils.Log4jTestUtils; +import org.lttng.ust.agent.utils.LttngUtils; + +/** + * Implementation of {@link FilterListenerOrderingITBase} for the log4j API. + */ +public class Log4jFilterListenerOrderingIT extends FilterListenerOrderingITBase { + + private Logger logger; + private Appender appender; + + /** + * Class setup + */ + @BeforeClass + public static void julClassSetup() { + /* Skip tests if we can't find the JNI library or lttng-tools */ + assumeTrue(Log4jTestUtils.checkForLog4jLibrary()); + assumeTrue(LttngUtils.checkForLttngTools(Domain.LOG4J)); + + LttngToolsHelper.destroyAllSessions(); + } + + @Override + protected Domain getDomain() { + return Domain.LOG4J; + } + + @Override + protected void registerAgent() { + logger = Logger.getLogger(EVENT_NAME_A); + logger.setLevel(Level.ALL); + + try { + appender = new LttngLogAppender(); + } catch (SecurityException | IOException e) { + fail(); + } + logger.addAppender(appender); + } + + @Override + protected void deregisterAgent() { + logger.removeAppender(appender); + logger = null; + + appender.close(); + appender = null; + } +} -- 2.34.1