1 package dst.ass2.aop.tests;
3 import static org.junit.Assert.assertEquals;
4 import static org.junit.Assert.assertNotNull;
5 import static org.junit.Assert.assertSame;
6 import static org.junit.Assert.assertTrue;
8 import java.io.ByteArrayOutputStream;
9 import java.io.PrintStream;
10 import java.lang.reflect.Method;
11 import java.util.List;
14 import dst.ass2.aop.IPluginExecutable;
15 import dst.ass2.aop.event.Event;
16 import dst.ass2.aop.event.EventBus;
17 import dst.ass2.aop.event.EventType;
18 import dst.ass2.aop.sample.InvisiblePluginExecutable;
19 import dst.ass2.aop.sample.LoggingPluginExecutable;
20 import dst.ass2.aop.sample.SystemOutPluginExecutable;
21 import dst.ass2.aop.util.PluginUtils;
22 import org.aspectj.lang.annotation.After;
23 import org.aspectj.lang.annotation.Around;
24 import org.aspectj.lang.annotation.Aspect;
25 import org.aspectj.lang.annotation.Before;
26 import org.aspectj.weaver.internal.tools.PointcutExpressionImpl;
27 import org.aspectj.weaver.tools.ShadowMatch;
28 import org.junit.Test;
29 import org.springframework.aop.PointcutAdvisor;
30 import org.springframework.aop.framework.Advised;
31 import org.springframework.core.annotation.AnnotationUtils;
32 import org.springframework.util.ReflectionUtils;
34 import dst.ass2.aop.logging.Invisible;
35 import dst.ass2.aop.logging.LoggingAspect;
37 public class Ass2_4_2Test {
38 final EventBus eventBus = EventBus.getInstance();
42 public void beforeAndAfter() {
47 * Verifies that the {@link LoggingAspect} is a valid AspectJ aspect i.e., {@link Aspect @Aspect} as well as
48 * {@link Around @Around} or {@link Before @Before} / {@link After @After}.
51 public void loggingAspect_isValid() {
52 Aspect aspect = AnnotationUtils.findAnnotation(LoggingAspect.class, Aspect.class);
53 assertNotNull("LoggingAspect is not annotated with @Aspect", aspect);
55 Map<Method, Around> around = PluginUtils.findMethodAnnotation(LoggingAspect.class, Around.class);
56 Map<Method, Before> before = PluginUtils.findMethodAnnotation(LoggingAspect.class, Before.class);
57 Map<Method, After> after = PluginUtils.findMethodAnnotation(LoggingAspect.class, After.class);
59 boolean found = !around.isEmpty() || (!before.isEmpty() && !after.isEmpty());
60 assertTrue("LoggingAspect does not contain methods annotated with @Around OR @Before / @After", found);
64 * Verifies that the pointcut expression of the {@link LoggingAspect} does not match any method except the
65 * {@link IPluginExecutable#execute()} method.
68 public void pointcutExpression_matchesCorrectly() {
69 IPluginExecutable executable = PluginUtils.getExecutable(LoggingPluginExecutable.class, LoggingAspect.class);
70 assertTrue("Executable must implement the Advised interface", executable instanceof Advised);
71 Advised advised = (Advised) executable;
73 PointcutAdvisor pointcutAdvisor = PluginUtils.getPointcutAdvisor(advised);
74 assertNotNull("PointcutAdvisor not found because there is no pointcut or the pointcut does not match", pointcutAdvisor);
76 String expression = PluginUtils.getBestExpression(advised);
77 assertTrue("Pointcut expression must include '" + IPluginExecutable.class.getName() + "'", expression.contains(IPluginExecutable.class.getName()));
78 assertTrue("Pointcut expression must include '" + PluginUtils.EXECUTE_METHOD.getName() + "'", expression.contains(PluginUtils.EXECUTE_METHOD.getName()));
80 PointcutExpressionImpl pointcutExpression = PluginUtils.getPointcutExpression(advised);
81 ShadowMatch shadowMatch = pointcutExpression.matchesMethodExecution(PluginUtils.EXECUTE_METHOD);
82 assertTrue("Pointcut does not match IPluginExecute.execute()", shadowMatch.alwaysMatches());
84 shadowMatch = pointcutExpression.matchesMethodExecution(PluginUtils.INTERRUPTED_METHOD);
85 assertTrue("Pointcut must not match IPluginExecute.interrupted()", shadowMatch.neverMatches());
87 shadowMatch = pointcutExpression.matchesMethodExecution(ReflectionUtils.findMethod(getClass(), PluginUtils.EXECUTE_METHOD.getName()));
88 assertTrue("Pointcut must not match LoggingPluginTest.execute()", shadowMatch.neverMatches());
92 * Verifies that the pointcut expression of the LoggingAspect contains the {@link Invisible @Invisible} annotation.
95 public void pointcutExpression_containsInvisibleAnnotation() {
96 IPluginExecutable executable = PluginUtils.getExecutable(LoggingPluginExecutable.class, LoggingAspect.class);
97 Advised advised = (Advised) executable;
99 String expression = PluginUtils.getBestExpression(advised);
100 String annotationName = Invisible.class.getName();
101 assertTrue("Pointcut expression does not contain " + annotationName, expression.contains(annotationName));
105 * Verifies that the pointcut expression of the {@link LoggingAspect} does not match any method annotated with
106 * {@link Invisible @Invisible}.
109 public void pointcutExpression_doesNotMatchInvisible() {
110 IPluginExecutable executable = PluginUtils.getExecutable(LoggingPluginExecutable.class, LoggingAspect.class);
111 Advised advised = (Advised) executable;
113 PointcutExpressionImpl pointcutExpression = PluginUtils.getPointcutExpression(advised);
115 Method loggingMethod = ReflectionUtils.findMethod(LoggingPluginExecutable.class, PluginUtils.EXECUTE_METHOD.getName());
116 ShadowMatch shadowMatch = pointcutExpression.matchesMethodExecution(loggingMethod);
117 assertTrue("Pointcut does not match LoggingPluginExecutable.execute()", shadowMatch.alwaysMatches());
119 Method invisibleMethod = ReflectionUtils.findMethod(InvisiblePluginExecutable.class, PluginUtils.EXECUTE_METHOD.getName());
120 shadowMatch = pointcutExpression.matchesMethodExecution(invisibleMethod);
121 assertTrue("Pointcut matches InvisiblePluginExecutable.execute()", shadowMatch.neverMatches());
125 * Tests if the {@link LoggingAspect} uses the {@link java.util.logging.Logger Logger} defined in the plugin.
128 public void loggingAspect_usesLogger() {
129 IPluginExecutable executable = PluginUtils.getExecutable(LoggingPluginExecutable.class, LoggingAspect.class);
130 Advised advised = (Advised) executable;
132 // Add handler end check that there are no events
133 PluginUtils.addBusHandlerIfNecessary(advised);
134 assertEquals("EventBus must be empty", 0, eventBus.count(EventType.INFO));
136 // Execute plugin and check that there are 2 events
137 executable.execute();
138 List<Event> events = eventBus.getEvents(EventType.INFO);
139 assertEquals("EventBus must exactly contain 2 INFO events", 2, events.size());
141 // Check if the logger contains the correct class name
142 events = eventBus.getEvents(EventType.INFO);
143 for (Event event : events) {
144 assertEquals("Event message must contain the name of the " + LoggingAspect.class.getSimpleName(), LoggingAspect.class.getName(), event.getMessage());
145 assertSame("Event must be logged for " + LoggingPluginExecutable.class.getSimpleName(), LoggingPluginExecutable.class, event.getPluginClass());
150 * Tests if the {@link LoggingAspect} uses {@code System.out} if the plugin does not contain a
151 * {@link java.util.logging.Logger Logger} field.
153 * @throws IllegalAccessException if {@code System.out} cannot be modified (must not happen)
156 public void loggingAspect_usesSystemOut() throws IllegalAccessException {
157 // Redirect System.out
158 ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
159 PrintStream out = PluginUtils.setStaticFinalField(System.class, "out", new PrintStream(byteArrayOutputStream));
162 IPluginExecutable executable = PluginUtils.getExecutable(SystemOutPluginExecutable.class, LoggingAspect.class);
163 assertEquals("EventBus must be empty", 0, eventBus.size());
164 executable.execute();
165 assertEquals("EventBus must exactly contain 2 events", 2, eventBus.size());
167 // Verify that the log output contains the class name of the executed plugin
168 String output = byteArrayOutputStream.toString();
169 assertTrue(String.format("Log output must contain %s\n\tbut was%s", SystemOutPluginExecutable.class.getName(), output),
170 output.contains(SystemOutPluginExecutable.class.getName()));
173 PluginUtils.setStaticFinalField(System.class, "out", out);
177 public void execute() {