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;
018
019import static org.junit.jupiter.api.Assertions.assertEquals;
020import static org.junit.jupiter.api.Assertions.assertFalse;
021import static org.junit.jupiter.api.Assertions.assertInstanceOf;
022import static org.junit.jupiter.api.Assertions.assertNotEquals;
023import static org.junit.jupiter.api.Assertions.assertNotNull;
024import static org.junit.jupiter.api.Assertions.assertNull;
025import static org.junit.jupiter.api.Assertions.assertTrue;
026import static org.junit.jupiter.api.Assertions.fail;
027
028import java.io.ByteArrayInputStream;
029import java.io.ByteArrayOutputStream;
030import java.io.ObjectInputStream;
031import java.io.ObjectOutputStream;
032import java.util.ArrayList;
033import java.util.Collection;
034import java.util.HashMap;
035import java.util.Iterator;
036import java.util.Map;
037import java.util.Set;
038
039import org.junit.jupiter.api.AfterEach;
040import org.junit.jupiter.api.BeforeEach;
041import org.junit.jupiter.api.Test;
042
043/**
044 * Test case for the {@code ContextBase} class.
045 *
046 * @author Craig R. McClanahan
047 * @version $Revision$ $Date$
048 */
049public abstract class ContextTestCase<C extends Context> {
050
051    // ---------------------------------------------------- Instance Variables
052
053    /**
054     * The {@link Context} instance under test.
055     */
056    protected C context = null;
057
058    // ---------------------------------------------------------- Constructors
059
060    /**
061     * The Default-Constructor for this class.
062     */
063    public ContextTestCase() {
064    }
065
066    // -------------------------------------------------- Overall Test Methods
067
068    /**
069     * Set up instance variables required by this test case.
070     */
071    @BeforeEach
072    public void init() {
073        context = createContext();
074    }
075
076    /**
077     * Tear down instance variables required by this test case.
078     */
079    @AfterEach
080    public void tearDown() {
081        context = null;
082    }
083
084    // ------------------------------------------------ Individual Test Methods
085
086    /**
087     * Test ability to get, put, and remove attributes
088     */
089    @Test
090    public void testAttributes() {
091        Object value = null;
092        checkAttributeCount(0);
093
094        context.put("foo", "This is foo");
095        checkAttributeCount(1);
096        value = context.get("foo");
097        assertNotNull(value, "Returned foo");
098        assertInstanceOf(String.class, value, "Returned foo type");
099        assertEquals("This is foo", value,
100                     "Returned foo value");
101
102        context.put("bar", "This is bar");
103        checkAttributeCount(2);
104        value = context.get("bar");
105        assertNotNull(value, "Returned bar");
106        assertInstanceOf(String.class, value, "Returned bar type");
107        assertEquals("This is bar", value,
108                     "Returned bar value");
109
110        context.put("baz", "This is baz");
111        checkAttributeCount(3);
112        value = context.get("baz");
113        assertNotNull(value, "Returned baz");
114        assertInstanceOf(String.class, value, "Returned baz type");
115        assertEquals("This is baz", value,
116                     "Returned baz value");
117
118        context.put("baz", "This is new baz");
119        checkAttributeCount(3); // Replaced, not added
120        value = context.get("baz");
121        assertNotNull(value, "Returned baz");
122        assertInstanceOf(String.class, value, "Returned baz type");
123        assertEquals("This is new baz", value,
124                     "Returned baz value");
125
126        context.remove("bar");
127        checkAttributeCount(2);
128        assertNull(context.get("bar"),
129                   "Did not return bar");
130        assertNotNull(context.get("foo"),
131                      "Still returned foo");
132        assertNotNull(context.get("baz"),
133                      "Still returned baz");
134
135        context.clear();
136        checkAttributeCount(0);
137        assertNull(context.get("foo"),
138                   "Did not return foo");
139        assertNull(context.get("bar"),
140                   "Did not return bar");
141        assertNull(context.get("baz"),
142                   "Did not return baz");
143    }
144
145    /**
146     * Test {@code containsKey()} and {@code containsValue()}
147     */
148    @Test
149    public void testContains() {
150        assertFalse(context.containsKey("bop"));
151        assertFalse(context.containsValue("bop value"));
152        context.put("bop", "bop value");
153        assertTrue(context.containsKey("bop"));
154        assertTrue(context.containsValue("bop value"));
155        context.remove("bop");
156        assertFalse(context.containsKey("bop"));
157        assertFalse(context.containsValue("bop value"));
158    }
159
160    /**
161     * Test {@code equals()} and {@code hashCode()}
162     */
163    @Test
164    public void testEquals() {
165        // Compare to self
166        assertTrue(context.equals(context));
167        assertEquals(context.hashCode(), context.hashCode());
168
169        // Compare to equivalent instance
170        Context other = createContext();
171        assertTrue(context.equals(other));
172        assertEquals(context.hashCode(), other.hashCode());
173
174        // Compare to non-equivalent instance - other modified
175        other.put("bop", "bop value");
176        assertFalse(context.equals(other));
177        assertNotEquals(context.hashCode(), other.hashCode());
178
179        // Compare to non-equivalent instance - self modified
180        other = createContext(); // reset to equivalence
181        context.put("bop", "bop value");
182        assertFalse(context.equals(other));
183        assertNotEquals(context.hashCode(), other.hashCode());
184    }
185
186    /**
187     * Test {@code keySet()}
188     */
189    @Test
190    public void testKeySet() {
191        Set<String> keySet = null;
192        Collection<String> all = new ArrayList<>();
193
194        // Unsupported operations
195        keySet = context.keySet();
196        try {
197            keySet.add("bop");
198            fail("Should have thrown UnsupportedOperationException");
199        } catch (UnsupportedOperationException e) {
200            ; // Expected result
201        }
202        try {
203            Collection<String> adds = new ArrayList<>();
204            adds.add("bop");
205            keySet.addAll(adds);
206            fail("Should have thrown UnsupportedOperationException");
207        } catch (UnsupportedOperationException e) {
208            ; // Expected result
209        }
210
211        // Before-modification checks
212        keySet = context.keySet();
213        assertEquals(createContext().size(), keySet.size());
214        assertFalse(keySet.contains("foo"));
215        assertFalse(keySet.contains("bar"));
216        assertFalse(keySet.contains("baz"));
217        assertFalse(keySet.contains("bop"));
218
219        // Add the new elements
220        context.put("foo", "foo value");
221        context.put("bar", "bar value");
222        context.put("baz", "baz value");
223        all.add("foo");
224        all.add("bar");
225        all.add("baz");
226
227        // After-modification checks
228        keySet = context.keySet();
229        assertEquals(expectedAttributeCount() + 3, keySet.size());
230        assertTrue(keySet.contains("foo"));
231        assertTrue(keySet.contains("bar"));
232        assertTrue(keySet.contains("baz"));
233        assertFalse(keySet.contains("bop"));
234        assertTrue(keySet.containsAll(all));
235
236        // Remove a single element via remove()
237        context.remove("bar");
238        all.remove("bar");
239        keySet = context.keySet();
240        assertEquals(expectedAttributeCount() + 2, keySet.size());
241        assertTrue(keySet.contains("foo"));
242        assertFalse(keySet.contains("bar"));
243        assertTrue(keySet.contains("baz"));
244        assertFalse(keySet.contains("bop"));
245        assertTrue(keySet.containsAll(all));
246
247        // Remove a single element via keySet.remove()
248        keySet.remove("baz");
249        all.remove("baz");
250        keySet = context.keySet();
251        assertEquals(expectedAttributeCount() + 1, keySet.size());
252        assertTrue(keySet.contains("foo"));
253        assertFalse(keySet.contains("bar"));
254        assertFalse(keySet.contains("baz"));
255        assertFalse(keySet.contains("bop"));
256        assertTrue(keySet.containsAll(all));
257
258        // Remove all elements via keySet.clear()
259        keySet.clear();
260        all.clear();
261        assertEquals(expectedAttributeCount(), keySet.size());
262        assertFalse(keySet.contains("foo"));
263        assertFalse(keySet.contains("bar"));
264        assertFalse(keySet.contains("baz"));
265        assertFalse(keySet.contains("bop"));
266        assertTrue(keySet.containsAll(all));
267
268        // Add the new elements #2
269        context.put("foo", "foo value");
270        context.put("bar", "bar value");
271        context.put("baz", "baz value");
272        all.add("foo");
273        all.add("bar");
274        all.add("baz");
275
276        // After-modification checks #2
277        keySet = context.keySet();
278        assertEquals(expectedAttributeCount() + 3, keySet.size());
279        assertTrue(keySet.contains("foo"));
280        assertTrue(keySet.contains("bar"));
281        assertTrue(keySet.contains("baz"));
282        assertFalse(keySet.contains("bop"));
283        assertTrue(keySet.containsAll(all));
284    }
285
286    /**
287     * Test state of newly created instance
288     */
289    @Test
290    public void testPristine() {
291        checkAttributeCount(0);
292        assertNull(context.get("foo"),
293                   "No 'foo' attribute");
294    }
295
296    /**
297     * Test {@code putAll()}
298     */
299    @Test
300    public void testPutAll() {
301        // Check preconditions
302        checkAttributeCount(0);
303        assertNull(context.get("foo"));
304        assertNull(context.get("bar"));
305        assertNull(context.get("baz"));
306        assertFalse(context.containsKey("foo"));
307        assertFalse(context.containsKey("bar"));
308        assertFalse(context.containsKey("baz"));
309        assertFalse(context.containsValue("foo value"));
310        assertFalse(context.containsValue("bar value"));
311        assertFalse(context.containsValue("baz value"));
312
313        // Call putAll()
314        Map<String, String> adds = new HashMap<>();
315        adds.put("foo", "foo value");
316        adds.put("bar", "bar value");
317        adds.put("baz", "baz value");
318        context.putAll(adds);
319
320        // Check postconditions
321        checkAttributeCount(3);
322        assertEquals("foo value", context.get("foo"));
323        assertEquals("bar value", context.get("bar"));
324        assertEquals("baz value", context.get("baz"));
325        assertTrue(context.containsKey("foo"));
326        assertTrue(context.containsKey("bar"));
327        assertTrue(context.containsKey("baz"));
328        assertTrue(context.containsValue("foo value"));
329        assertTrue(context.containsValue("bar value"));
330        assertTrue(context.containsValue("baz value"));
331    }
332
333    /**
334     * Test serialization
335     *
336     * @throws Exception any error
337     */
338    @Test
339    public void testSerialization() throws Exception {
340        // Set up the context with some parameters
341        context.put("foo", "foo value");
342        context.put("bar", "bar value");
343        context.put("baz", "baz value");
344        checkAttributeCount(3);
345
346        // Serialize to a byte array
347        ByteArrayOutputStream baos = new ByteArrayOutputStream();
348        ObjectOutputStream oos = new ObjectOutputStream(baos);
349        oos.writeObject(context);
350        oos.close();
351
352        // Deserialize back to a new object
353        ByteArrayInputStream bais =
354            new ByteArrayInputStream(baos.toByteArray());
355        ObjectInputStream ois = new ObjectInputStream(bais);
356        @SuppressWarnings("unchecked")
357        C newContext = (C) ois.readObject();
358        context = newContext;
359        ois.close();
360
361        // Do some rudimentary checks to make sure we have the same contents
362        assertTrue(context.containsKey("foo"));
363        assertTrue(context.containsKey("bar"));
364        assertTrue(context.containsKey("baz"));
365        checkAttributeCount(3);
366    }
367
368    // -------------------------------------------------------- Support Methods
369
370    /**
371     * Verify the number of defined attributes
372     *
373     * @param expected the expected value
374     */
375    protected void checkAttributeCount(int expected) {
376        int actual = 0;
377        Iterator<String> keys = context.keySet().iterator();
378        while (keys.hasNext()) {
379            keys.next();
380            actual++;
381        }
382        assertEquals(expectedAttributeCount() + expected, actual,
383                     "Correct attribute count");
384        if (expected == 0) {
385            assertTrue(context.isEmpty(), "Context should be empty");
386        } else {
387            assertFalse(context.isEmpty(), "Context should not be empty");
388        }
389    }
390
391    /**
392     * Return the expected {@code size()} for a Context for this test case
393     *
394     * @return the expected {@code size()} for a Context
395     */
396    protected int expectedAttributeCount() {
397        return createContext().size();
398    }
399
400    /**
401     * Create a new instance of the appropriate Context type for this test case
402     *
403     * @return the new instance of the appropriate Context type
404     */
405    protected abstract C createContext();
406}