• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.collect.Table.Cell;
24 import com.google.common.testing.CollectorTester;
25 import com.google.common.testing.SerializableTester;
26 import java.util.stream.Collector;
27 
28 /**
29  * Tests common methods in {@link ImmutableTable}
30  *
31  * @author Gregory Kick
32  */
33 @GwtCompatible(emulated = true)
34 public class ImmutableTableTest extends AbstractTableReadTest {
35   @Override
create(Object... data)36   protected Table<String, Integer, Character> create(Object... data) {
37     ImmutableTable.Builder<String, Integer, Character> builder = ImmutableTable.builder();
38     for (int i = 0; i < data.length; i = i + 3) {
39       builder.put((String) data[i], (Integer) data[i + 1], (Character) data[i + 2]);
40     }
41     return builder.build();
42   }
43 
44   // The bulk of the toImmutableTable tests can be found in TableCollectorsTest.
45   // This gives minimal coverage to the forwarding functions
testToImmutableTableSanityTest()46   public void testToImmutableTableSanityTest() {
47     Collector<Cell<String, String, Integer>, ?, ImmutableTable<String, String, Integer>> collector =
48         TableCollectors.toImmutableTable(Cell::getRowKey, Cell::getColumnKey, Cell::getValue);
49     CollectorTester.of(collector)
50         .expectCollects(ImmutableTable.of())
51         .expectCollects(ImmutableTable.of("one", "uno", 1), Tables.immutableCell("one", "uno", 1));
52   }
53 
testToImmutableTableMergingSanityTest()54   public void testToImmutableTableMergingSanityTest() {
55     Collector<Cell<String, String, Integer>, ?, ImmutableTable<String, String, Integer>> collector =
56         TableCollectors.toImmutableTable(
57             Cell::getRowKey, Cell::getColumnKey, Cell::getValue, Integer::sum);
58     CollectorTester.of(collector)
59         .expectCollects(ImmutableTable.of())
60         .expectCollects(
61             ImmutableTable.of("one", "uno", 3),
62             Tables.immutableCell("one", "uno", 1),
63             Tables.immutableCell("one", "uno", 2));
64   }
65 
testBuilder()66   public void testBuilder() {
67     ImmutableTable.Builder<Character, Integer, String> builder = new ImmutableTable.Builder<>();
68     assertEquals(ImmutableTable.of(), builder.build());
69     assertEquals(ImmutableTable.of('a', 1, "foo"), builder.put('a', 1, "foo").build());
70     Table<Character, Integer, String> expectedTable = HashBasedTable.create();
71     expectedTable.put('a', 1, "foo");
72     expectedTable.put('b', 1, "bar");
73     expectedTable.put('a', 2, "baz");
74     Table<Character, Integer, String> otherTable = HashBasedTable.create();
75     otherTable.put('b', 1, "bar");
76     otherTable.put('a', 2, "baz");
77     assertEquals(expectedTable, builder.putAll(otherTable).build());
78   }
79 
testBuilder_withImmutableCell()80   public void testBuilder_withImmutableCell() {
81     ImmutableTable.Builder<Character, Integer, String> builder = new ImmutableTable.Builder<>();
82     assertEquals(
83         ImmutableTable.of('a', 1, "foo"), builder.put(Tables.immutableCell('a', 1, "foo")).build());
84   }
85 
testBuilder_withImmutableCellAndNullContents()86   public void testBuilder_withImmutableCellAndNullContents() {
87     ImmutableTable.Builder<Character, Integer, String> builder = new ImmutableTable.Builder<>();
88     try {
89       builder.put(Tables.immutableCell((Character) null, 1, "foo"));
90       fail();
91     } catch (NullPointerException e) {
92       // success
93     }
94     try {
95       builder.put(Tables.immutableCell('a', (Integer) null, "foo"));
96       fail();
97     } catch (NullPointerException e) {
98       // success
99     }
100     try {
101       builder.put(Tables.immutableCell('a', 1, (String) null));
102       fail();
103     } catch (NullPointerException e) {
104       // success
105     }
106   }
107 
108   private static class StringHolder {
109     String string;
110   }
111 
testBuilder_withMutableCell()112   public void testBuilder_withMutableCell() {
113     ImmutableTable.Builder<Character, Integer, String> builder = new ImmutableTable.Builder<>();
114 
115     final StringHolder holder = new StringHolder();
116     holder.string = "foo";
117     Table.Cell<Character, Integer, String> mutableCell =
118         new Tables.AbstractCell<Character, Integer, String>() {
119           @Override
120           public Character getRowKey() {
121             return 'K';
122           }
123 
124           @Override
125           public Integer getColumnKey() {
126             return 42;
127           }
128 
129           @Override
130           public String getValue() {
131             return holder.string;
132           }
133         };
134 
135     // Add the mutable cell to the builder
136     builder.put(mutableCell);
137 
138     // Mutate the value
139     holder.string = "bar";
140 
141     // Make sure it uses the original value.
142     assertEquals(ImmutableTable.of('K', 42, "foo"), builder.build());
143   }
144 
testBuilder_noDuplicates()145   public void testBuilder_noDuplicates() {
146     ImmutableTable.Builder<Character, Integer, String> builder =
147         new ImmutableTable.Builder<Character, Integer, String>()
148             .put('a', 1, "foo")
149             .put('a', 1, "bar");
150     try {
151       builder.build();
152       fail();
153     } catch (IllegalArgumentException e) {
154       // success
155     }
156   }
157 
testBuilder_noNulls()158   public void testBuilder_noNulls() {
159     ImmutableTable.Builder<Character, Integer, String> builder = new ImmutableTable.Builder<>();
160     try {
161       builder.put(null, 1, "foo");
162       fail();
163     } catch (NullPointerException e) {
164       // success
165     }
166     try {
167       builder.put('a', null, "foo");
168       fail();
169     } catch (NullPointerException e) {
170       // success
171     }
172     try {
173       builder.put('a', 1, null);
174       fail();
175     } catch (NullPointerException e) {
176       // success
177     }
178   }
179 
validateTableCopies(Table<R, C, V> original)180   private static <R, C, V> void validateTableCopies(Table<R, C, V> original) {
181     Table<R, C, V> copy = ImmutableTable.copyOf(original);
182     assertEquals(original, copy);
183     validateViewOrdering(original, copy);
184 
185     Table<R, C, V> built = ImmutableTable.<R, C, V>builder().putAll(original).build();
186     assertEquals(original, built);
187     validateViewOrdering(original, built);
188   }
189 
validateViewOrdering(Table<R, C, V> original, Table<R, C, V> copy)190   private static <R, C, V> void validateViewOrdering(Table<R, C, V> original, Table<R, C, V> copy) {
191     assertThat(copy.cellSet()).containsExactlyElementsIn(original.cellSet()).inOrder();
192     assertThat(copy.rowKeySet()).containsExactlyElementsIn(original.rowKeySet()).inOrder();
193     assertThat(copy.values()).containsExactlyElementsIn(original.values()).inOrder();
194   }
195 
testCopyOf()196   public void testCopyOf() {
197     Table<Character, Integer, String> table = TreeBasedTable.create();
198     validateTableCopies(table);
199     table.put('b', 2, "foo");
200     validateTableCopies(table);
201     table.put('b', 1, "bar");
202     table.put('a', 2, "baz");
203     validateTableCopies(table);
204     // Even though rowKeySet, columnKeySet, and cellSet have the same
205     // iteration ordering, row has an inconsistent ordering.
206     assertThat(table.row('b').keySet()).containsExactly(1, 2).inOrder();
207     assertThat(ImmutableTable.copyOf(table).row('b').keySet()).containsExactly(2, 1).inOrder();
208   }
209 
testCopyOfSparse()210   public void testCopyOfSparse() {
211     Table<Character, Integer, String> table = TreeBasedTable.create();
212     table.put('x', 2, "foo");
213     table.put('r', 1, "bar");
214     table.put('c', 3, "baz");
215     table.put('b', 7, "cat");
216     table.put('e', 5, "dog");
217     table.put('c', 0, "axe");
218     table.put('e', 3, "tub");
219     table.put('r', 4, "foo");
220     table.put('x', 5, "bar");
221     validateTableCopies(table);
222   }
223 
testCopyOfDense()224   public void testCopyOfDense() {
225     Table<Character, Integer, String> table = TreeBasedTable.create();
226     table.put('c', 3, "foo");
227     table.put('c', 2, "bar");
228     table.put('c', 1, "baz");
229     table.put('b', 3, "cat");
230     table.put('b', 1, "dog");
231     table.put('a', 3, "foo");
232     table.put('a', 2, "bar");
233     table.put('a', 1, "baz");
234     validateTableCopies(table);
235   }
236 
testBuilder_orderRowsAndColumnsBy_putAll()237   public void testBuilder_orderRowsAndColumnsBy_putAll() {
238     Table<Character, Integer, String> table = HashBasedTable.create();
239     table.put('b', 2, "foo");
240     table.put('b', 1, "bar");
241     table.put('a', 2, "baz");
242     ImmutableTable.Builder<Character, Integer, String> builder = ImmutableTable.builder();
243     Table<Character, Integer, String> copy =
244         builder
245             .orderRowsBy(Ordering.natural())
246             .orderColumnsBy(Ordering.natural())
247             .putAll(table)
248             .build();
249     assertThat(copy.rowKeySet()).containsExactly('a', 'b').inOrder();
250     assertThat(copy.columnKeySet()).containsExactly(1, 2).inOrder();
251     assertThat(copy.values()).containsExactly("baz", "bar", "foo").inOrder();
252     assertThat(copy.row('b').keySet()).containsExactly(1, 2).inOrder();
253   }
254 
testBuilder_orderRowsAndColumnsBy_sparse()255   public void testBuilder_orderRowsAndColumnsBy_sparse() {
256     ImmutableTable.Builder<Character, Integer, String> builder = ImmutableTable.builder();
257     builder.orderRowsBy(Ordering.natural());
258     builder.orderColumnsBy(Ordering.natural());
259     builder.put('x', 2, "foo");
260     builder.put('r', 1, "bar");
261     builder.put('c', 3, "baz");
262     builder.put('b', 7, "cat");
263     builder.put('e', 5, "dog");
264     builder.put('c', 0, "axe");
265     builder.put('e', 3, "tub");
266     builder.put('r', 4, "foo");
267     builder.put('x', 5, "bar");
268     Table<Character, Integer, String> table = builder.build();
269     assertThat(table.rowKeySet()).containsExactly('b', 'c', 'e', 'r', 'x').inOrder();
270     assertThat(table.columnKeySet()).containsExactly(0, 1, 2, 3, 4, 5, 7).inOrder();
271     assertThat(table.values())
272         .containsExactly("cat", "axe", "baz", "tub", "dog", "bar", "foo", "foo", "bar")
273         .inOrder();
274     assertThat(table.row('c').keySet()).containsExactly(0, 3).inOrder();
275     assertThat(table.column(5).keySet()).containsExactly('e', 'x').inOrder();
276   }
277 
testBuilder_orderRowsAndColumnsBy_dense()278   public void testBuilder_orderRowsAndColumnsBy_dense() {
279     ImmutableTable.Builder<Character, Integer, String> builder = ImmutableTable.builder();
280     builder.orderRowsBy(Ordering.natural());
281     builder.orderColumnsBy(Ordering.natural());
282     builder.put('c', 3, "foo");
283     builder.put('c', 2, "bar");
284     builder.put('c', 1, "baz");
285     builder.put('b', 3, "cat");
286     builder.put('b', 1, "dog");
287     builder.put('a', 3, "foo");
288     builder.put('a', 2, "bar");
289     builder.put('a', 1, "baz");
290     Table<Character, Integer, String> table = builder.build();
291     assertThat(table.rowKeySet()).containsExactly('a', 'b', 'c').inOrder();
292     assertThat(table.columnKeySet()).containsExactly(1, 2, 3).inOrder();
293     assertThat(table.values())
294         .containsExactly("baz", "bar", "foo", "dog", "cat", "baz", "bar", "foo")
295         .inOrder();
296     assertThat(table.row('c').keySet()).containsExactly(1, 2, 3).inOrder();
297     assertThat(table.column(1).keySet()).containsExactly('a', 'b', 'c').inOrder();
298   }
299 
testBuilder_orderRowsBy_sparse()300   public void testBuilder_orderRowsBy_sparse() {
301     ImmutableTable.Builder<Character, Integer, String> builder = ImmutableTable.builder();
302     builder.orderRowsBy(Ordering.natural());
303     builder.put('x', 2, "foo");
304     builder.put('r', 1, "bar");
305     builder.put('c', 3, "baz");
306     builder.put('b', 7, "cat");
307     builder.put('e', 5, "dog");
308     builder.put('c', 0, "axe");
309     builder.put('e', 3, "tub");
310     builder.put('r', 4, "foo");
311     builder.put('x', 5, "bar");
312     Table<Character, Integer, String> table = builder.build();
313     assertThat(table.rowKeySet()).containsExactly('b', 'c', 'e', 'r', 'x').inOrder();
314     assertThat(table.column(5).keySet()).containsExactly('e', 'x').inOrder();
315   }
316 
testBuilder_orderRowsBy_dense()317   public void testBuilder_orderRowsBy_dense() {
318     ImmutableTable.Builder<Character, Integer, String> builder = ImmutableTable.builder();
319     builder.orderRowsBy(Ordering.natural());
320     builder.put('c', 3, "foo");
321     builder.put('c', 2, "bar");
322     builder.put('c', 1, "baz");
323     builder.put('b', 3, "cat");
324     builder.put('b', 1, "dog");
325     builder.put('a', 3, "foo");
326     builder.put('a', 2, "bar");
327     builder.put('a', 1, "baz");
328     Table<Character, Integer, String> table = builder.build();
329     assertThat(table.rowKeySet()).containsExactly('a', 'b', 'c').inOrder();
330     assertThat(table.column(1).keySet()).containsExactly('a', 'b', 'c').inOrder();
331   }
332 
testBuilder_orderColumnsBy_sparse()333   public void testBuilder_orderColumnsBy_sparse() {
334     ImmutableTable.Builder<Character, Integer, String> builder = ImmutableTable.builder();
335     builder.orderColumnsBy(Ordering.natural());
336     builder.put('x', 2, "foo");
337     builder.put('r', 1, "bar");
338     builder.put('c', 3, "baz");
339     builder.put('b', 7, "cat");
340     builder.put('e', 5, "dog");
341     builder.put('c', 0, "axe");
342     builder.put('e', 3, "tub");
343     builder.put('r', 4, "foo");
344     builder.put('x', 5, "bar");
345     Table<Character, Integer, String> table = builder.build();
346     assertThat(table.columnKeySet()).containsExactly(0, 1, 2, 3, 4, 5, 7).inOrder();
347     assertThat(table.row('c').keySet()).containsExactly(0, 3).inOrder();
348   }
349 
testBuilder_orderColumnsBy_dense()350   public void testBuilder_orderColumnsBy_dense() {
351     ImmutableTable.Builder<Character, Integer, String> builder = ImmutableTable.builder();
352     builder.orderColumnsBy(Ordering.natural());
353     builder.put('c', 3, "foo");
354     builder.put('c', 2, "bar");
355     builder.put('c', 1, "baz");
356     builder.put('b', 3, "cat");
357     builder.put('b', 1, "dog");
358     builder.put('a', 3, "foo");
359     builder.put('a', 2, "bar");
360     builder.put('a', 1, "baz");
361     Table<Character, Integer, String> table = builder.build();
362     assertThat(table.columnKeySet()).containsExactly(1, 2, 3).inOrder();
363     assertThat(table.row('c').keySet()).containsExactly(1, 2, 3).inOrder();
364   }
365 
testSerialization_empty()366   public void testSerialization_empty() {
367     validateReserialization(ImmutableTable.of());
368   }
369 
testSerialization_singleElement()370   public void testSerialization_singleElement() {
371     validateReserialization(ImmutableTable.of('a', 2, "foo"));
372   }
373 
testDenseSerialization_manualOrder()374   public void testDenseSerialization_manualOrder() {
375     ImmutableTable.Builder<Character, Integer, String> builder = ImmutableTable.builder();
376     builder.put('b', 2, "foo");
377     builder.put('b', 1, "bar");
378     builder.put('a', 2, "baz");
379     Table<Character, Integer, String> table = builder.build();
380     assertThat(table).isInstanceOf(DenseImmutableTable.class);
381     validateReserialization(table);
382   }
383 
testDenseSerialization_rowOrder()384   public void testDenseSerialization_rowOrder() {
385     ImmutableTable.Builder<Character, Integer, String> builder = ImmutableTable.builder();
386     builder.orderRowsBy(Ordering.<Character>natural());
387     builder.put('b', 2, "foo");
388     builder.put('b', 1, "bar");
389     builder.put('a', 2, "baz");
390     Table<Character, Integer, String> table = builder.build();
391     assertThat(table).isInstanceOf(DenseImmutableTable.class);
392     validateReserialization(table);
393   }
394 
testDenseSerialization_columnOrder()395   public void testDenseSerialization_columnOrder() {
396     ImmutableTable.Builder<Character, Integer, String> builder = ImmutableTable.builder();
397     builder.orderColumnsBy(Ordering.<Integer>natural());
398     builder.put('b', 2, "foo");
399     builder.put('b', 1, "bar");
400     builder.put('a', 2, "baz");
401     Table<Character, Integer, String> table = builder.build();
402     assertThat(table).isInstanceOf(DenseImmutableTable.class);
403     validateReserialization(table);
404   }
405 
testDenseSerialization_bothOrders()406   public void testDenseSerialization_bothOrders() {
407     ImmutableTable.Builder<Character, Integer, String> builder = ImmutableTable.builder();
408     builder.orderRowsBy(Ordering.<Character>natural());
409     builder.orderColumnsBy(Ordering.<Integer>natural());
410     builder.put('b', 2, "foo");
411     builder.put('b', 1, "bar");
412     builder.put('a', 2, "baz");
413     Table<Character, Integer, String> table = builder.build();
414     assertThat(table).isInstanceOf(DenseImmutableTable.class);
415     validateReserialization(table);
416   }
417 
testSparseSerialization_manualOrder()418   public void testSparseSerialization_manualOrder() {
419     ImmutableTable.Builder<Character, Integer, String> builder = ImmutableTable.builder();
420     builder.put('b', 2, "foo");
421     builder.put('b', 1, "bar");
422     builder.put('a', 2, "baz");
423     builder.put('c', 3, "cat");
424     builder.put('d', 4, "dog");
425     Table<Character, Integer, String> table = builder.build();
426     assertThat(table).isInstanceOf(SparseImmutableTable.class);
427     validateReserialization(table);
428   }
429 
testSparseSerialization_rowOrder()430   public void testSparseSerialization_rowOrder() {
431     ImmutableTable.Builder<Character, Integer, String> builder = ImmutableTable.builder();
432     builder.orderRowsBy(Ordering.<Character>natural());
433     builder.put('b', 2, "foo");
434     builder.put('b', 1, "bar");
435     builder.put('a', 2, "baz");
436     builder.put('c', 3, "cat");
437     builder.put('d', 4, "dog");
438     Table<Character, Integer, String> table = builder.build();
439     assertThat(table).isInstanceOf(SparseImmutableTable.class);
440     validateReserialization(table);
441   }
442 
testSparseSerialization_columnOrder()443   public void testSparseSerialization_columnOrder() {
444     ImmutableTable.Builder<Character, Integer, String> builder = ImmutableTable.builder();
445     builder.orderColumnsBy(Ordering.<Integer>natural());
446     builder.put('b', 2, "foo");
447     builder.put('b', 1, "bar");
448     builder.put('a', 2, "baz");
449     builder.put('c', 3, "cat");
450     builder.put('d', 4, "dog");
451     Table<Character, Integer, String> table = builder.build();
452     assertThat(table).isInstanceOf(SparseImmutableTable.class);
453     validateReserialization(table);
454   }
455 
testSparseSerialization_bothOrders()456   public void testSparseSerialization_bothOrders() {
457     ImmutableTable.Builder<Character, Integer, String> builder = ImmutableTable.builder();
458     builder.orderRowsBy(Ordering.<Character>natural());
459     builder.orderColumnsBy(Ordering.<Integer>natural());
460     builder.put('b', 2, "foo");
461     builder.put('b', 1, "bar");
462     builder.put('a', 2, "baz");
463     builder.put('c', 3, "cat");
464     builder.put('d', 4, "dog");
465     Table<Character, Integer, String> table = builder.build();
466     assertThat(table).isInstanceOf(SparseImmutableTable.class);
467     validateReserialization(table);
468   }
469 
validateReserialization(Table<R, C, V> original)470   private static <R, C, V> void validateReserialization(Table<R, C, V> original) {
471     Table<R, C, V> copy = SerializableTester.reserializeAndAssert(original);
472     assertThat(copy.cellSet()).containsExactlyElementsIn(original.cellSet()).inOrder();
473     assertThat(copy.rowKeySet()).containsExactlyElementsIn(original.rowKeySet()).inOrder();
474     assertThat(copy.columnKeySet()).containsExactlyElementsIn(original.columnKeySet()).inOrder();
475   }
476 
477   @GwtIncompatible // Mind-bogglingly slow in GWT
478   @AndroidIncompatible // slow
testOverflowCondition()479   public void testOverflowCondition() {
480     // See https://code.google.com/p/guava-libraries/issues/detail?id=1322 for details.
481     ImmutableTable.Builder<Integer, Integer, String> builder = ImmutableTable.builder();
482     for (int i = 1; i < 0x10000; i++) {
483       builder.put(i, 0, "foo");
484       builder.put(0, i, "bar");
485     }
486     assertTrue(builder.build() instanceof SparseImmutableTable);
487   }
488 
489   @GwtIncompatible // NullPointerTester
490   @Override
testNullPointerInstance()491   public void testNullPointerInstance() {
492     if (isAndroid()) {
493       /*
494        * NPT fails under the old versions of Android we test under because it performs reflection on
495        * ImmutableTable, which declares static methods that refer to Collector, which is unavailable
496        * under such versions.
497        *
498        * We use a runtime check here instead of @AndroidIncompatible: @AndroidIncompatible operates
499        * by stripping annotated methods entirely, and if we strip this method, then JUnit would just
500        * run the supermethod as usual.
501        *
502        * TODO: b/292578973: Use @AndroidIncompatible if we change our system to keep the methods in
503        * place but to have the test runner skip them. However, note that if we choose to *both*
504        * strip the methods *and* have the test runner not run them (for some unusual cases in which
505        * we don't run the stripping test for technical reasons), then we'd be back to the problem
506        * described above, since the supermethod is *not* annotated @AndroidIncompatible (since it
507        * works fine with the other Table implementations).
508        */
509       return;
510     }
511     super.testNullPointerInstance();
512   }
513 
isAndroid()514   private static boolean isAndroid() {
515     return System.getProperty("java.runtime.name", "").contains("Android");
516   }
517 }
518