1 /*
2  * Copyright (C) 2009 The Guava Authors
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.google.common.collect;
18 
19 import static com.google.common.truth.Truth.assertThat;
20 
21 import com.google.common.annotations.GwtCompatible;
22 import com.google.common.annotations.GwtIncompatible;
23 import com.google.common.testing.SerializableTester;
24 
25 /**
26  * Tests common methods in {@link ImmutableTable}
27  *
28  * @author Gregory Kick
29  */
30 @GwtCompatible(emulated = true)
31 public class ImmutableTableTest extends AbstractTableReadTest {
32   @Override
create(Object... data)33   protected Table<String, Integer, Character> create(Object... data) {
34     ImmutableTable.Builder<String, Integer, Character> builder = ImmutableTable.builder();
35     for (int i = 0; i < data.length; i = i + 3) {
36       builder.put((String) data[i], (Integer) data[i + 1], (Character) data[i + 2]);
37     }
38     return builder.build();
39   }
40 
testBuilder()41   public void testBuilder() {
42     ImmutableTable.Builder<Character, Integer, String> builder = new ImmutableTable.Builder<>();
43     assertEquals(ImmutableTable.of(), builder.build());
44     assertEquals(ImmutableTable.of('a', 1, "foo"), builder.put('a', 1, "foo").build());
45     Table<Character, Integer, String> expectedTable = HashBasedTable.create();
46     expectedTable.put('a', 1, "foo");
47     expectedTable.put('b', 1, "bar");
48     expectedTable.put('a', 2, "baz");
49     Table<Character, Integer, String> otherTable = HashBasedTable.create();
50     otherTable.put('b', 1, "bar");
51     otherTable.put('a', 2, "baz");
52     assertEquals(expectedTable, builder.putAll(otherTable).build());
53   }
54 
testBuilder_withImmutableCell()55   public void testBuilder_withImmutableCell() {
56     ImmutableTable.Builder<Character, Integer, String> builder = new ImmutableTable.Builder<>();
57     assertEquals(
58         ImmutableTable.of('a', 1, "foo"), builder.put(Tables.immutableCell('a', 1, "foo")).build());
59   }
60 
testBuilder_withImmutableCellAndNullContents()61   public void testBuilder_withImmutableCellAndNullContents() {
62     ImmutableTable.Builder<Character, Integer, String> builder = new ImmutableTable.Builder<>();
63     try {
64       builder.put(Tables.immutableCell((Character) null, 1, "foo"));
65       fail();
66     } catch (NullPointerException e) {
67       // success
68     }
69     try {
70       builder.put(Tables.immutableCell('a', (Integer) null, "foo"));
71       fail();
72     } catch (NullPointerException e) {
73       // success
74     }
75     try {
76       builder.put(Tables.immutableCell('a', 1, (String) null));
77       fail();
78     } catch (NullPointerException e) {
79       // success
80     }
81   }
82 
83   private static class StringHolder {
84     String string;
85   }
86 
testBuilder_withMutableCell()87   public void testBuilder_withMutableCell() {
88     ImmutableTable.Builder<Character, Integer, String> builder = new ImmutableTable.Builder<>();
89 
90     final StringHolder holder = new StringHolder();
91     holder.string = "foo";
92     Table.Cell<Character, Integer, String> mutableCell =
93         new Tables.AbstractCell<Character, Integer, String>() {
94           @Override
95           public Character getRowKey() {
96             return 'K';
97           }
98 
99           @Override
100           public Integer getColumnKey() {
101             return 42;
102           }
103 
104           @Override
105           public String getValue() {
106             return holder.string;
107           }
108         };
109 
110     // Add the mutable cell to the builder
111     builder.put(mutableCell);
112 
113     // Mutate the value
114     holder.string = "bar";
115 
116     // Make sure it uses the original value.
117     assertEquals(ImmutableTable.of('K', 42, "foo"), builder.build());
118   }
119 
testBuilder_noDuplicates()120   public void testBuilder_noDuplicates() {
121     ImmutableTable.Builder<Character, Integer, String> builder =
122         new ImmutableTable.Builder<Character, Integer, String>()
123             .put('a', 1, "foo")
124             .put('a', 1, "bar");
125     try {
126       builder.build();
127       fail();
128     } catch (IllegalArgumentException e) {
129       // success
130     }
131   }
132 
testBuilder_noNulls()133   public void testBuilder_noNulls() {
134     ImmutableTable.Builder<Character, Integer, String> builder = new ImmutableTable.Builder<>();
135     try {
136       builder.put(null, 1, "foo");
137       fail();
138     } catch (NullPointerException e) {
139       // success
140     }
141     try {
142       builder.put('a', null, "foo");
143       fail();
144     } catch (NullPointerException e) {
145       // success
146     }
147     try {
148       builder.put('a', 1, null);
149       fail();
150     } catch (NullPointerException e) {
151       // success
152     }
153   }
154 
validateTableCopies(Table<R, C, V> original)155   private static <R, C, V> void validateTableCopies(Table<R, C, V> original) {
156     Table<R, C, V> copy = ImmutableTable.copyOf(original);
157     assertEquals(original, copy);
158     validateViewOrdering(original, copy);
159 
160     Table<R, C, V> built = ImmutableTable.<R, C, V>builder().putAll(original).build();
161     assertEquals(original, built);
162     validateViewOrdering(original, built);
163   }
164 
validateViewOrdering(Table<R, C, V> original, Table<R, C, V> copy)165   private static <R, C, V> void validateViewOrdering(Table<R, C, V> original, Table<R, C, V> copy) {
166     assertThat(copy.cellSet()).containsExactlyElementsIn(original.cellSet()).inOrder();
167     assertThat(copy.rowKeySet()).containsExactlyElementsIn(original.rowKeySet()).inOrder();
168     assertThat(copy.values()).containsExactlyElementsIn(original.values()).inOrder();
169   }
170 
testCopyOf()171   public void testCopyOf() {
172     Table<Character, Integer, String> table = TreeBasedTable.create();
173     validateTableCopies(table);
174     table.put('b', 2, "foo");
175     validateTableCopies(table);
176     table.put('b', 1, "bar");
177     table.put('a', 2, "baz");
178     validateTableCopies(table);
179     // Even though rowKeySet, columnKeySet, and cellSet have the same
180     // iteration ordering, row has an inconsistent ordering.
181     assertThat(table.row('b').keySet()).containsExactly(1, 2).inOrder();
182     assertThat(ImmutableTable.copyOf(table).row('b').keySet()).containsExactly(2, 1).inOrder();
183   }
184 
testCopyOfSparse()185   public void testCopyOfSparse() {
186     Table<Character, Integer, String> table = TreeBasedTable.create();
187     table.put('x', 2, "foo");
188     table.put('r', 1, "bar");
189     table.put('c', 3, "baz");
190     table.put('b', 7, "cat");
191     table.put('e', 5, "dog");
192     table.put('c', 0, "axe");
193     table.put('e', 3, "tub");
194     table.put('r', 4, "foo");
195     table.put('x', 5, "bar");
196     validateTableCopies(table);
197   }
198 
testCopyOfDense()199   public void testCopyOfDense() {
200     Table<Character, Integer, String> table = TreeBasedTable.create();
201     table.put('c', 3, "foo");
202     table.put('c', 2, "bar");
203     table.put('c', 1, "baz");
204     table.put('b', 3, "cat");
205     table.put('b', 1, "dog");
206     table.put('a', 3, "foo");
207     table.put('a', 2, "bar");
208     table.put('a', 1, "baz");
209     validateTableCopies(table);
210   }
211 
testBuilder_orderRowsAndColumnsBy_putAll()212   public void testBuilder_orderRowsAndColumnsBy_putAll() {
213     Table<Character, Integer, String> table = HashBasedTable.create();
214     table.put('b', 2, "foo");
215     table.put('b', 1, "bar");
216     table.put('a', 2, "baz");
217     ImmutableTable.Builder<Character, Integer, String> builder = ImmutableTable.builder();
218     Table<Character, Integer, String> copy =
219         builder
220             .orderRowsBy(Ordering.natural())
221             .orderColumnsBy(Ordering.natural())
222             .putAll(table)
223             .build();
224     assertThat(copy.rowKeySet()).containsExactly('a', 'b').inOrder();
225     assertThat(copy.columnKeySet()).containsExactly(1, 2).inOrder();
226     assertThat(copy.values()).containsExactly("baz", "bar", "foo").inOrder();
227     assertThat(copy.row('b').keySet()).containsExactly(1, 2).inOrder();
228   }
229 
testBuilder_orderRowsAndColumnsBy_sparse()230   public void testBuilder_orderRowsAndColumnsBy_sparse() {
231     ImmutableTable.Builder<Character, Integer, String> builder = ImmutableTable.builder();
232     builder.orderRowsBy(Ordering.natural());
233     builder.orderColumnsBy(Ordering.natural());
234     builder.put('x', 2, "foo");
235     builder.put('r', 1, "bar");
236     builder.put('c', 3, "baz");
237     builder.put('b', 7, "cat");
238     builder.put('e', 5, "dog");
239     builder.put('c', 0, "axe");
240     builder.put('e', 3, "tub");
241     builder.put('r', 4, "foo");
242     builder.put('x', 5, "bar");
243     Table<Character, Integer, String> table = builder.build();
244     assertThat(table.rowKeySet()).containsExactly('b', 'c', 'e', 'r', 'x').inOrder();
245     assertThat(table.columnKeySet()).containsExactly(0, 1, 2, 3, 4, 5, 7).inOrder();
246     assertThat(table.values())
247         .containsExactly("cat", "axe", "baz", "tub", "dog", "bar", "foo", "foo", "bar")
248         .inOrder();
249     assertThat(table.row('c').keySet()).containsExactly(0, 3).inOrder();
250     assertThat(table.column(5).keySet()).containsExactly('e', 'x').inOrder();
251   }
252 
testBuilder_orderRowsAndColumnsBy_dense()253   public void testBuilder_orderRowsAndColumnsBy_dense() {
254     ImmutableTable.Builder<Character, Integer, String> builder = ImmutableTable.builder();
255     builder.orderRowsBy(Ordering.natural());
256     builder.orderColumnsBy(Ordering.natural());
257     builder.put('c', 3, "foo");
258     builder.put('c', 2, "bar");
259     builder.put('c', 1, "baz");
260     builder.put('b', 3, "cat");
261     builder.put('b', 1, "dog");
262     builder.put('a', 3, "foo");
263     builder.put('a', 2, "bar");
264     builder.put('a', 1, "baz");
265     Table<Character, Integer, String> table = builder.build();
266     assertThat(table.rowKeySet()).containsExactly('a', 'b', 'c').inOrder();
267     assertThat(table.columnKeySet()).containsExactly(1, 2, 3).inOrder();
268     assertThat(table.values())
269         .containsExactly("baz", "bar", "foo", "dog", "cat", "baz", "bar", "foo")
270         .inOrder();
271     assertThat(table.row('c').keySet()).containsExactly(1, 2, 3).inOrder();
272     assertThat(table.column(1).keySet()).containsExactly('a', 'b', 'c').inOrder();
273   }
274 
testBuilder_orderRowsBy_sparse()275   public void testBuilder_orderRowsBy_sparse() {
276     ImmutableTable.Builder<Character, Integer, String> builder = ImmutableTable.builder();
277     builder.orderRowsBy(Ordering.natural());
278     builder.put('x', 2, "foo");
279     builder.put('r', 1, "bar");
280     builder.put('c', 3, "baz");
281     builder.put('b', 7, "cat");
282     builder.put('e', 5, "dog");
283     builder.put('c', 0, "axe");
284     builder.put('e', 3, "tub");
285     builder.put('r', 4, "foo");
286     builder.put('x', 5, "bar");
287     Table<Character, Integer, String> table = builder.build();
288     assertThat(table.rowKeySet()).containsExactly('b', 'c', 'e', 'r', 'x').inOrder();
289     assertThat(table.column(5).keySet()).containsExactly('e', 'x').inOrder();
290   }
291 
testBuilder_orderRowsBy_dense()292   public void testBuilder_orderRowsBy_dense() {
293     ImmutableTable.Builder<Character, Integer, String> builder = ImmutableTable.builder();
294     builder.orderRowsBy(Ordering.natural());
295     builder.put('c', 3, "foo");
296     builder.put('c', 2, "bar");
297     builder.put('c', 1, "baz");
298     builder.put('b', 3, "cat");
299     builder.put('b', 1, "dog");
300     builder.put('a', 3, "foo");
301     builder.put('a', 2, "bar");
302     builder.put('a', 1, "baz");
303     Table<Character, Integer, String> table = builder.build();
304     assertThat(table.rowKeySet()).containsExactly('a', 'b', 'c').inOrder();
305     assertThat(table.column(1).keySet()).containsExactly('a', 'b', 'c').inOrder();
306   }
307 
testBuilder_orderColumnsBy_sparse()308   public void testBuilder_orderColumnsBy_sparse() {
309     ImmutableTable.Builder<Character, Integer, String> builder = ImmutableTable.builder();
310     builder.orderColumnsBy(Ordering.natural());
311     builder.put('x', 2, "foo");
312     builder.put('r', 1, "bar");
313     builder.put('c', 3, "baz");
314     builder.put('b', 7, "cat");
315     builder.put('e', 5, "dog");
316     builder.put('c', 0, "axe");
317     builder.put('e', 3, "tub");
318     builder.put('r', 4, "foo");
319     builder.put('x', 5, "bar");
320     Table<Character, Integer, String> table = builder.build();
321     assertThat(table.columnKeySet()).containsExactly(0, 1, 2, 3, 4, 5, 7).inOrder();
322     assertThat(table.row('c').keySet()).containsExactly(0, 3).inOrder();
323   }
324 
testBuilder_orderColumnsBy_dense()325   public void testBuilder_orderColumnsBy_dense() {
326     ImmutableTable.Builder<Character, Integer, String> builder = ImmutableTable.builder();
327     builder.orderColumnsBy(Ordering.natural());
328     builder.put('c', 3, "foo");
329     builder.put('c', 2, "bar");
330     builder.put('c', 1, "baz");
331     builder.put('b', 3, "cat");
332     builder.put('b', 1, "dog");
333     builder.put('a', 3, "foo");
334     builder.put('a', 2, "bar");
335     builder.put('a', 1, "baz");
336     Table<Character, Integer, String> table = builder.build();
337     assertThat(table.columnKeySet()).containsExactly(1, 2, 3).inOrder();
338     assertThat(table.row('c').keySet()).containsExactly(1, 2, 3).inOrder();
339   }
340 
testSerialization_empty()341   public void testSerialization_empty() {
342     validateReserialization(ImmutableTable.of());
343   }
344 
testSerialization_singleElement()345   public void testSerialization_singleElement() {
346     validateReserialization(ImmutableTable.of('a', 2, "foo"));
347   }
348 
testDenseSerialization_manualOrder()349   public void testDenseSerialization_manualOrder() {
350     ImmutableTable.Builder<Character, Integer, String> builder = ImmutableTable.builder();
351     builder.put('b', 2, "foo");
352     builder.put('b', 1, "bar");
353     builder.put('a', 2, "baz");
354     Table<Character, Integer, String> table = builder.build();
355     assertThat(table).isInstanceOf(DenseImmutableTable.class);
356     validateReserialization(table);
357   }
358 
testDenseSerialization_rowOrder()359   public void testDenseSerialization_rowOrder() {
360     ImmutableTable.Builder<Character, Integer, String> builder = ImmutableTable.builder();
361     builder.orderRowsBy(Ordering.<Character>natural());
362     builder.put('b', 2, "foo");
363     builder.put('b', 1, "bar");
364     builder.put('a', 2, "baz");
365     Table<Character, Integer, String> table = builder.build();
366     assertThat(table).isInstanceOf(DenseImmutableTable.class);
367     validateReserialization(table);
368   }
369 
testDenseSerialization_columnOrder()370   public void testDenseSerialization_columnOrder() {
371     ImmutableTable.Builder<Character, Integer, String> builder = ImmutableTable.builder();
372     builder.orderColumnsBy(Ordering.<Integer>natural());
373     builder.put('b', 2, "foo");
374     builder.put('b', 1, "bar");
375     builder.put('a', 2, "baz");
376     Table<Character, Integer, String> table = builder.build();
377     assertThat(table).isInstanceOf(DenseImmutableTable.class);
378     validateReserialization(table);
379   }
380 
testDenseSerialization_bothOrders()381   public void testDenseSerialization_bothOrders() {
382     ImmutableTable.Builder<Character, Integer, String> builder = ImmutableTable.builder();
383     builder.orderRowsBy(Ordering.<Character>natural());
384     builder.orderColumnsBy(Ordering.<Integer>natural());
385     builder.put('b', 2, "foo");
386     builder.put('b', 1, "bar");
387     builder.put('a', 2, "baz");
388     Table<Character, Integer, String> table = builder.build();
389     assertThat(table).isInstanceOf(DenseImmutableTable.class);
390     validateReserialization(table);
391   }
392 
testSparseSerialization_manualOrder()393   public void testSparseSerialization_manualOrder() {
394     ImmutableTable.Builder<Character, Integer, String> builder = ImmutableTable.builder();
395     builder.put('b', 2, "foo");
396     builder.put('b', 1, "bar");
397     builder.put('a', 2, "baz");
398     builder.put('c', 3, "cat");
399     builder.put('d', 4, "dog");
400     Table<Character, Integer, String> table = builder.build();
401     assertThat(table).isInstanceOf(SparseImmutableTable.class);
402     validateReserialization(table);
403   }
404 
testSparseSerialization_rowOrder()405   public void testSparseSerialization_rowOrder() {
406     ImmutableTable.Builder<Character, Integer, String> builder = ImmutableTable.builder();
407     builder.orderRowsBy(Ordering.<Character>natural());
408     builder.put('b', 2, "foo");
409     builder.put('b', 1, "bar");
410     builder.put('a', 2, "baz");
411     builder.put('c', 3, "cat");
412     builder.put('d', 4, "dog");
413     Table<Character, Integer, String> table = builder.build();
414     assertThat(table).isInstanceOf(SparseImmutableTable.class);
415     validateReserialization(table);
416   }
417 
testSparseSerialization_columnOrder()418   public void testSparseSerialization_columnOrder() {
419     ImmutableTable.Builder<Character, Integer, String> builder = ImmutableTable.builder();
420     builder.orderColumnsBy(Ordering.<Integer>natural());
421     builder.put('b', 2, "foo");
422     builder.put('b', 1, "bar");
423     builder.put('a', 2, "baz");
424     builder.put('c', 3, "cat");
425     builder.put('d', 4, "dog");
426     Table<Character, Integer, String> table = builder.build();
427     assertThat(table).isInstanceOf(SparseImmutableTable.class);
428     validateReserialization(table);
429   }
430 
testSparseSerialization_bothOrders()431   public void testSparseSerialization_bothOrders() {
432     ImmutableTable.Builder<Character, Integer, String> builder = ImmutableTable.builder();
433     builder.orderRowsBy(Ordering.<Character>natural());
434     builder.orderColumnsBy(Ordering.<Integer>natural());
435     builder.put('b', 2, "foo");
436     builder.put('b', 1, "bar");
437     builder.put('a', 2, "baz");
438     builder.put('c', 3, "cat");
439     builder.put('d', 4, "dog");
440     Table<Character, Integer, String> table = builder.build();
441     assertThat(table).isInstanceOf(SparseImmutableTable.class);
442     validateReserialization(table);
443   }
444 
validateReserialization(Table<R, C, V> original)445   private static <R, C, V> void validateReserialization(Table<R, C, V> original) {
446     Table<R, C, V> copy = SerializableTester.reserializeAndAssert(original);
447     assertThat(copy.cellSet()).containsExactlyElementsIn(original.cellSet()).inOrder();
448     assertThat(copy.rowKeySet()).containsExactlyElementsIn(original.rowKeySet()).inOrder();
449     assertThat(copy.columnKeySet()).containsExactlyElementsIn(original.columnKeySet()).inOrder();
450   }
451 
452   @GwtIncompatible // Mind-bogglingly slow in GWT
453   @AndroidIncompatible // slow
testOverflowCondition()454   public void testOverflowCondition() {
455     // See https://code.google.com/p/guava-libraries/issues/detail?id=1322 for details.
456     ImmutableTable.Builder<Integer, Integer, String> builder = ImmutableTable.builder();
457     for (int i = 1; i < 0x10000; i++) {
458       builder.put(i, 0, "foo");
459       builder.put(0, i, "bar");
460     }
461     assertTrue(builder.build() instanceof SparseImmutableTable);
462   }
463 }
464