1   /**
2    * Copyright 2008 Atlassian Pty Ltd 
3    * 
4    * Licensed under the Apache License, Version 2.0 (the "License"); 
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at 
7    * 
8    *     http://www.apache.org/licenses/LICENSE-2.0 
9    * 
10   * Unless required by applicable law or agreed to in writing, software 
11   * distributed under the License is distributed on an "AS IS" BASIS, 
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
13   * See the License for the specific language governing permissions and 
14   * limitations under the License.
15   */
16  
17  package com.atlassian.util.concurrent;
18  
19  import static org.junit.Assert.assertEquals;
20  import static org.junit.Assert.assertNotNull;
21  import static org.junit.Assert.assertTrue;
22  import static org.junit.Assert.fail;
23  
24  import java.util.Collection;
25  import java.util.Collections;
26  import java.util.Comparator;
27  import java.util.Iterator;
28  import java.util.Map;
29  import java.util.SortedMap;
30  
31  import org.junit.Test;
32  
33  public class CopyOnWriteSortedMapTest {
34  
35      private static final class ReverseComparator<T> implements Comparator<T> {
36          private final Comparator<T> comparator;
37  
38          ReverseComparator(final Comparator<T> comparator) {
39              this.comparator = comparator;
40          }
41  
42          public int compare(final T o1, final T o2) {
43              return 0 - comparator.compare(o1, o2);
44          };
45      }
46  
47      private static final class StringComparator implements Comparator<String> {
48          public int compare(final String o1, final String o2) {
49              return o1.compareTo(o2);
50          };
51      }
52  
53      @Test
54      public void mapConstructor() {
55          final MapBuilder<String, String> builder = MapBuilder.builder();
56          builder.add("one", "value").add("two", "value").add("three", "value");
57          final SortedMap<String, String> map = CopyOnWriteSortedMap.newTreeMap(builder.toMap());
58  
59          assertEquals(3, map.size());
60      }
61  
62      @Test
63      public void mapComparatorConstructor() {
64          final MapBuilder<String, String> builder = MapBuilder.builder();
65          builder.add("one", "value").add("two", "value").add("three", "value");
66          final SortedMap<String, String> map = CopyOnWriteSortedMap.newTreeMap(builder.toMap(), new ReverseComparator<String>(new StringComparator()));
67  
68          assertEquals(3, map.size());
69      }
70  
71      @SuppressWarnings("unchecked")
72      @Test(expected = IllegalArgumentException.class)
73      public void mapNullConstructor() {
74          CopyOnWriteSortedMap.newTreeMap((Map) null);
75      }
76  
77      @SuppressWarnings("unchecked")
78      @Test(expected = IllegalArgumentException.class)
79      public void comparatorNullConstructor() {
80          CopyOnWriteSortedMap.newTreeMap((Comparator) null);
81      }
82  
83      @Test(expected = IllegalArgumentException.class)
84      public void mapNullComparatorConstructor() {
85          CopyOnWriteSortedMap.newTreeMap(null, new StringComparator());
86      }
87  
88      @Test(expected = IllegalArgumentException.class)
89      public void mapComparatorNullConstructor() {
90          CopyOnWriteSortedMap.newTreeMap(Collections.emptyMap(), null);
91      }
92  
93      @Test
94      public void comparator() {
95          final Comparator<String> comparator = new StringComparator();
96          final SortedMap<String, String> map = CopyOnWriteSortedMap.newTreeMap(comparator);
97          assertNotNull(map.comparator());
98          assertEquals(comparator, map.comparator());
99          map.put("one", "two");
100         assertNotNull(map.comparator());
101         assertEquals(1, map.size());
102         assertEquals(comparator, map.comparator());
103         map.put("two", "value");
104         assertEquals(2, map.size());
105         map.put("three", "value");
106         assertEquals(3, map.size());
107     }
108 
109     @Test
110     public void firstKey() {
111         final SortedMap<String, String> map = CopyOnWriteSortedMap.newTreeMap();
112         map.put("one", "value");
113         map.put("two", "value");
114         map.put("three", "value");
115         assertEquals("one", map.firstKey());
116     }
117 
118     @Test
119     public void firstKeyWithReverse() {
120         final MapBuilder<String, String> builder = MapBuilder.builder();
121         builder.add("one", "value").add("two", "value").add("three", "value");
122         final SortedMap<String, String> map = CopyOnWriteSortedMap.newTreeMap(builder.toMap(), new ReverseComparator<String>(new StringComparator()));
123         map.put("one", "value");
124         map.put("two", "value");
125         map.put("three", "value");
126 
127         assertEquals(3, map.size());
128         assertEquals("two", map.firstKey());
129     }
130 
131     @Test
132     public void lastKey() {
133         final SortedMap<String, String> map = CopyOnWriteSortedMap.newTreeMap();
134         map.put("one", "value");
135         map.put("two", "value");
136         map.put("three", "value");
137         assertEquals("two", map.lastKey());
138     }
139 
140     @Test
141     public void lastKeyWithReverse() {
142         final MapBuilder<String, String> builder = MapBuilder.builder();
143         builder.add("one", "value").add("two", "value").add("three", "value");
144         final SortedMap<String, String> map = CopyOnWriteSortedMap.newTreeMap(builder.toMap(), new ReverseComparator<String>(new StringComparator()));
145 
146         assertEquals(3, map.size());
147         assertEquals("one", map.lastKey());
148     }
149 
150     @Test
151     public void headMap() {
152         final SortedMap<String, String> map = CopyOnWriteSortedMap.newTreeMap();
153         map.put("1", "one");
154         map.put("2", "two");
155         map.put("3", "three");
156         final SortedMap<String, String> headMap = map.headMap("3");
157         assertEquals(2, headMap.size());
158         assertTrue(headMap.containsKey("1"));
159         assertTrue(headMap.containsKey("2"));
160         assertTrue(headMap.containsValue("one"));
161         assertTrue(headMap.containsValue("two"));
162 
163         assertUnmodifiableMap(headMap, "3", "three");
164     }
165 
166     @Test
167     public void tailMap() {
168         final SortedMap<String, String> map = CopyOnWriteSortedMap.newTreeMap();
169         map.put("1", "one");
170         map.put("2", "two");
171         map.put("3", "three");
172         final SortedMap<String, String> tailMap = map.tailMap("2");
173         assertEquals(2, tailMap.size());
174         assertTrue(tailMap.containsKey("2"));
175         assertTrue(tailMap.containsKey("3"));
176         assertTrue(tailMap.containsValue("two"));
177         assertTrue(tailMap.containsValue("three"));
178 
179         assertUnmodifiableMap(tailMap, "1", "one");
180     }
181 
182     @Test
183     public void subMap() {
184         final CopyOnWriteSortedMap<String, String> map = CopyOnWriteSortedMap.newTreeMap();
185         map.put("1", "one");
186         map.put("2", "two");
187         map.put("3", "three");
188         map.put("4", "four");
189         final SortedMap<String, String> subMap = map.subMap("2", "4");
190         assertEquals(2, subMap.size());
191         assertTrue(subMap.containsKey("2"));
192         assertTrue(subMap.containsKey("3"));
193         assertTrue(subMap.containsValue("two"));
194         assertTrue(subMap.containsValue("three"));
195 
196         assertUnmodifiableMap(subMap, "1", "one");
197     }
198 
199     @Test
200     public void serializableTreeMap() {
201         final CopyOnWriteSortedMap<Object, Object> map = CopyOnWriteSortedMap.newTreeMap();
202         CopyOnWriteMapTest.assertMutableMapSerializable(map);
203     }
204 
205     static <K, V> void assertUnmodifiableMap(final Map<K, V> map, final K key, final V value) {
206         assertThrowsUnsupportedOp(new Runnable() {
207             public void run() {
208                 map.put(key, value);
209             }
210         });
211         assertThrowsUnsupportedOp(new Runnable() {
212             public void run() {
213                 map.remove(key);
214             }
215         });
216         assertThrowsUnsupportedOp(new Runnable() {
217             public void run() {
218                 map.clear();
219             }
220         });
221 
222         assertUnmodifiableCollection(map.keySet(), key);
223         assertUnmodifiableCollection(map.values(), value);
224     }
225 
226     private static <T> void assertUnmodifiableCollection(final Collection<T> coll, final T element) {
227         assertThrowsUnsupportedOp(new Runnable() {
228             public void run() {
229                 coll.clear();
230             }
231         });
232 
233         assertThrowsUnsupportedOp(new Runnable() {
234             public void run() {
235                 coll.remove(element);
236             }
237         });
238 
239         assertThrowsUnsupportedOp(new Runnable() {
240             public void run() {
241                 coll.removeAll(Collections.EMPTY_LIST);
242             }
243         });
244 
245         assertThrowsUnsupportedOp(new Runnable() {
246             public void run() {
247                 coll.add(element);
248             }
249         });
250 
251         final Collection<T> empty = Collections.emptyList();
252         assertThrowsUnsupportedOp(new Runnable() {
253             public void run() {
254                 coll.addAll(empty);
255             }
256         });
257 
258         assertThrowsUnsupportedOp(new Runnable() {
259             public void run() {
260                 coll.retainAll(empty);
261             }
262         });
263 
264         assertThrowsUnsupportedOp(new Runnable() {
265             public void run() {
266                 final Iterator<?> it = coll.iterator();
267                 it.next();
268                 it.remove();
269             }
270         });
271     }
272 
273     static void assertThrowsUnsupportedOp(final Runnable runnable) {
274         try {
275             runnable.run();
276             fail("should have thrown UnsupportedOperationException");
277         } catch (final UnsupportedOperationException ignore) {}
278     }
279 }