001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *     http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.commons.chain.impl;
018
019import static org.junit.jupiter.api.Assertions.assertEquals;
020import static org.junit.jupiter.api.Assertions.assertFalse;
021import static org.junit.jupiter.api.Assertions.assertNotNull;
022import static org.junit.jupiter.api.Assertions.assertTrue;
023import static org.junit.jupiter.api.Assertions.fail;
024
025import org.apache.commons.chain.Chain;
026import org.apache.commons.chain.Command;
027import org.apache.commons.chain.Context;
028import org.junit.jupiter.api.AfterEach;
029import org.junit.jupiter.api.BeforeEach;
030import org.junit.jupiter.api.Test;
031
032/**
033 * Test case for the {@code ChainBase} class.
034 *
035 * @author Craig R. McClanahan
036 * @version $Revision$ $Date$
037 */
038public class ChainBaseTestCase {
039
040    // ---------------------------------------------------- Instance Variables
041
042    /**
043     * The {@link Chain} instance under test.
044     */
045    protected Chain<Context> chain = null;
046
047    /**
048     * The {@link Context} instance on which to execute the chain.
049     */
050    protected Context context = null;
051
052    // ---------------------------------------------------------- Constructors
053
054    /**
055     * The Default-Constructor for this class.
056     */
057    public ChainBaseTestCase() {
058    }
059
060    // -------------------------------------------------- Overall Test Methods
061
062    /**
063     * Set up instance variables required by this test case.
064     */
065    @BeforeEach
066    public void init() {
067        chain = new ChainBaseEx<>();
068        context = new ContextBase();
069    }
070
071    /**
072     * Tear down instance variables required by this test case.
073     */
074    @AfterEach
075    public void tearDown() {
076        chain = null;
077        context = null;
078    }
079
080    // ------------------------------------------------ Individual Test Methods
081
082    /**
083     * Test the ability to add commands
084     */
085    @Test
086    public void testCommands() {
087        checkCommandCount(0);
088
089        Command<Context> command1 = new NonDelegatingCommand("1");
090        chain.addCommand(command1);
091        checkCommandCount(1);
092
093        Command<Context> command2 = new DelegatingCommand("2");
094        chain.addCommand(command2);
095        checkCommandCount(2);
096
097        Command<Context> command3 = new ExceptionCommand("3");
098        chain.addCommand(command3);
099        checkCommandCount(3);
100    }
101
102    /**
103     * Test execution of a single non-delegating command
104     */
105    @Test
106    public void testExecute1a() {
107        chain.addCommand(new NonDelegatingCommand("1"));
108        try {
109            assertTrue(chain.execute(context),
110                       "Chain returned true");
111        } catch (Exception e) {
112            fail("Threw exception: " + e);
113        }
114        checkExecuteLog("1");
115    }
116
117    /**
118     * Test execution of a single delegating command
119     */
120    @Test
121    public void testExecute1b() {
122        chain.addCommand(new DelegatingCommand("1"));
123        try {
124            assertFalse(chain.execute(context),
125                        "Chain returned false");
126        } catch (Exception e) {
127            fail("Threw exception: " + e);
128        }
129        checkExecuteLog("1");
130    }
131
132    /**
133     * Test execution of a single exception-throwing command
134     */
135    @Test
136    public void testExecute1c() {
137        chain.addCommand(new ExceptionCommand("1"));
138        try {
139            chain.execute(context);
140        } catch (ArithmeticException e) {
141            assertEquals("1", e.getMessage(), "Correct exception id");
142        } catch (Exception e) {
143            fail("Threw exception: " + e);
144        }
145        checkExecuteLog("1");
146    }
147
148    /**
149     * Test execution of an attempt to add a new Command while executing
150     */
151    @Test
152    public void testExecute1d() {
153        chain.addCommand(new AddingCommand("1", chain));
154        try {
155            chain.execute(context);
156        } catch (IllegalStateException e) {
157            ; // Expected result
158        } catch (Exception e) {
159            fail("Threw exception: " + e);
160        }
161        checkExecuteLog("1");
162    }
163
164    /**
165     * Test execution of a chain that should return {@code true}
166     */
167    @Test
168    public void testExecute2a() {
169        chain.addCommand(new DelegatingCommand("1"));
170        chain.addCommand(new DelegatingCommand("2"));
171        chain.addCommand(new NonDelegatingCommand("3"));
172        try {
173            assertTrue(chain.execute(context),
174                       "Chain returned true");
175        } catch (Exception e) {
176            fail("Threw exception: " + e);
177        }
178        checkExecuteLog("1/2/3");
179    }
180
181    /**
182     * Test execution of a chain that should return {@code false}
183     */
184    @Test
185    public void testExecute2b() {
186        chain.addCommand(new DelegatingCommand("1"));
187        chain.addCommand(new DelegatingCommand("2"));
188        chain.addCommand(new DelegatingCommand("3"));
189        try {
190            assertFalse(chain.execute(context),
191                        "Chain returned false");
192        } catch (Exception e) {
193            fail("Threw exception: " + e);
194        }
195        checkExecuteLog("1/2/3");
196    }
197
198    /**
199     * Test execution of a chain that should throw an exception
200     */
201    @Test
202    public void testExecute2c() {
203        chain.addCommand(new DelegatingCommand("1"));
204        chain.addCommand(new DelegatingCommand("2"));
205        chain.addCommand(new ExceptionCommand("3"));
206        try {
207            chain.execute(context);
208        } catch (ArithmeticException e) {
209            assertEquals("3", e.getMessage(), "Correct exception id");
210        } catch (Exception e) {
211            fail("Threw exception: " + e);
212        }
213        checkExecuteLog("1/2/3");
214    }
215
216    /**
217     * Test execution of a chain that should throw an exception in the middle
218     */
219    @Test
220    public void testExecute2d() {
221        chain.addCommand(new DelegatingCommand("1"));
222        chain.addCommand(new ExceptionCommand("2"));
223        chain.addCommand(new NonDelegatingCommand("3"));
224        try {
225            chain.execute(context);
226        } catch (ArithmeticException e) {
227            assertEquals("2", e.getMessage(), "Correct exception id");
228        } catch (Exception e) {
229            fail("Threw exception: " + e);
230        }
231        checkExecuteLog("1/2");
232    }
233
234    /**
235     * Test execution of a single non-delegating filter
236     */
237    @Test
238    public void testExecute3a() {
239        chain.addCommand(new NonDelegatingFilter("1", "a"));
240        try {
241            assertTrue(chain.execute(context),
242                       "Chain returned true");
243        } catch (Exception e) {
244            fail("Threw exception: " + e);
245        }
246        checkExecuteLog("1/a");
247    }
248
249    /**
250     * Test execution of a single delegating filter
251     */
252    @Test
253    public void testExecute3b() {
254        chain.addCommand(new DelegatingFilter("1", "a"));
255        try {
256            assertFalse(chain.execute(context),
257                        "Chain returned false");
258        } catch (Exception e) {
259            fail("Threw exception: " + e);
260        }
261        checkExecuteLog("1/a");
262    }
263
264    /**
265     * Test execution of a single exception-throwing filter
266     */
267    @Test
268    public void testExecute3c() {
269        chain.addCommand(new ExceptionFilter("1", "a"));
270        try {
271            chain.execute(context);
272        } catch (ArithmeticException e) {
273            assertEquals("1", e.getMessage(), "Correct exception id");
274        } catch (Exception e) {
275            fail("Threw exception: " + e);
276        }
277        checkExecuteLog("1/a");
278    }
279
280    /**
281     * Test execution of a chain that should return {@code true}
282     */
283    @Test
284    public void testExecute4a() {
285        chain.addCommand(new DelegatingFilter("1", "a"));
286        chain.addCommand(new DelegatingCommand("2"));
287        chain.addCommand(new NonDelegatingFilter("3", "c"));
288        try {
289            assertTrue(chain.execute(context),
290                       "Chain returned true");
291        } catch (Exception e) {
292            fail("Threw exception: " + e);
293        }
294        checkExecuteLog("1/2/3/c/a");
295    }
296
297    /**
298     * Test execution of a chain that should return {@code false}
299     */
300    @Test
301    public void testExecute4b() {
302        chain.addCommand(new DelegatingCommand("1"));
303        chain.addCommand(new DelegatingFilter("2", "b"));
304        chain.addCommand(new DelegatingCommand("3"));
305        try {
306            assertFalse(chain.execute(context),
307                        "Chain returned false");
308        } catch (Exception e) {
309            fail("Threw exception: " + e);
310        }
311        checkExecuteLog("1/2/3/b");
312    }
313
314    /**
315     * Test execution of a chain that should throw an exception
316     */
317    @Test
318    public void testExecute4c() {
319        chain.addCommand(new DelegatingFilter("1", "a"));
320        chain.addCommand(new DelegatingFilter("2", "b"));
321        chain.addCommand(new ExceptionFilter("3", "c"));
322        try {
323            chain.execute(context);
324        } catch (ArithmeticException e) {
325            assertEquals("3", e.getMessage(), "Correct exception id");
326        } catch (Exception e) {
327            fail("Threw exception: " + e);
328        }
329        checkExecuteLog("1/2/3/c/b/a");
330    }
331
332    /**
333     * Test execution of a chain that should throw an exception in the middle
334     */
335    @Test
336    public void testExecute4d() {
337        chain.addCommand(new DelegatingFilter("1", "a"));
338        chain.addCommand(new ExceptionFilter("2", "b"));
339        chain.addCommand(new NonDelegatingFilter("3", "c"));
340        try {
341            chain.execute(context);
342        } catch (ArithmeticException e) {
343            assertEquals("2", e.getMessage(), "Correct exception id");
344        } catch (Exception e) {
345            fail("Threw exception: " + e);
346        }
347        checkExecuteLog("1/2/b/a");
348    }
349
350    /**
351     * Test state of newly created instance
352     */
353    @Test
354    public void testNewInstance() {
355        checkCommandCount(0);
356    }
357
358    // -------------------------------------------------------- Support Methods
359
360    /**
361     * Verify the number of configured commands
362     *
363     * @param expected the expected value
364     */
365    protected void checkCommandCount(int expected) {
366        if (chain instanceof ChainBaseEx) {
367            Command<Context>[] commands = ((ChainBaseEx<Context>) chain).getCommands();
368            assertNotNull(commands,
369                          "getCommands() returned a non-null list");
370            assertEquals(expected, commands.length, "Correct command count");
371        }
372    }
373
374    /**
375     * Verify the contents of the execution log
376     *
377     * @param expected the expected value
378     */
379    protected void checkExecuteLog(String expected) {
380        StringBuffer log = (StringBuffer) context.get("log");
381        assertNotNull(log, "Context failed to return log");
382        assertEquals(expected, log.toString(),
383                     "Context returned correct log");
384    }
385}