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 import static java.util.Arrays.asList;
21 
22 import com.google.common.annotations.GwtCompatible;
23 import com.google.common.annotations.GwtIncompatible;
24 import com.google.common.base.Objects;
25 import com.google.common.collect.Table.Cell;
26 import com.google.common.testing.EqualsTester;
27 import com.google.common.testing.NullPointerTester;
28 import com.google.common.testing.SerializableTester;
29 import java.util.Arrays;
30 import java.util.Map;
31 
32 /**
33  * Test cases for {@link ArrayTable}.
34  *
35  * @author Jared Levy
36  */
37 @GwtCompatible(emulated = true)
38 public class ArrayTableTest extends AbstractTableTest {
39 
40   @Override
create(Object... data)41   protected ArrayTable<String, Integer, Character> create(Object... data) {
42     // TODO: Specify different numbers of rows and columns, to detect problems
43     // that arise when the wrong size is used.
44     ArrayTable<String, Integer, Character> table =
45         ArrayTable.create(asList("foo", "bar", "cat"), asList(1, 2, 3));
46     populate(table, data);
47     return table;
48   }
49 
50   @Override
assertSize(int expectedSize)51   protected void assertSize(int expectedSize) {
52     assertEquals(9, table.size());
53   }
54 
55   @Override
supportsRemove()56   protected boolean supportsRemove() {
57     return false;
58   }
59 
60   @Override
supportsNullValues()61   protected boolean supportsNullValues() {
62     return true;
63   }
64 
65   // Overriding tests of behavior that differs for ArrayTable.
66 
67   @Override
testContains()68   public void testContains() {
69     table = create("foo", 1, 'a', "bar", 1, 'b', "foo", 3, 'c');
70     assertTrue(table.contains("foo", 1));
71     assertTrue(table.contains("bar", 1));
72     assertTrue(table.contains("foo", 3));
73     assertTrue(table.contains("foo", 2));
74     assertTrue(table.contains("bar", 3));
75     assertTrue(table.contains("cat", 1));
76     assertFalse(table.contains("foo", -1));
77     assertFalse(table.contains("bad", 1));
78     assertFalse(table.contains("bad", -1));
79     assertFalse(table.contains("foo", null));
80     assertFalse(table.contains(null, 1));
81     assertFalse(table.contains(null, null));
82   }
83 
84   @Override
testContainsRow()85   public void testContainsRow() {
86     table = create("foo", 1, 'a', "bar", 1, 'b', "foo", 3, 'c');
87     assertTrue(table.containsRow("foo"));
88     assertTrue(table.containsRow("bar"));
89     assertTrue(table.containsRow("cat"));
90     assertFalse(table.containsRow("bad"));
91     assertFalse(table.containsRow(null));
92   }
93 
94   @Override
testContainsColumn()95   public void testContainsColumn() {
96     table = create("foo", 1, 'a', "bar", 1, 'b', "foo", 3, 'c');
97     assertTrue(table.containsColumn(1));
98     assertTrue(table.containsColumn(3));
99     assertTrue(table.containsColumn(2));
100     assertFalse(table.containsColumn(-1));
101     assertFalse(table.containsColumn(null));
102   }
103 
104   @Override
testContainsValue()105   public void testContainsValue() {
106     table = create("foo", 1, 'a', "bar", 1, 'b', "foo", 3, 'c');
107     assertTrue(table.containsValue('a'));
108     assertTrue(table.containsValue('b'));
109     assertTrue(table.containsValue('c'));
110     assertFalse(table.containsValue('x'));
111     assertTrue(table.containsValue(null));
112   }
113 
114   @Override
testIsEmpty()115   public void testIsEmpty() {
116     assertFalse(table.isEmpty());
117     table = create("foo", 1, 'a', "bar", 1, 'b', "foo", 3, 'c');
118     assertFalse(table.isEmpty());
119   }
120 
121   @Override
testEquals()122   public void testEquals() {
123     table = create("foo", 1, 'a', "bar", 1, 'b', "foo", 3, 'c');
124     Table<String, Integer, Character> hashCopy = HashBasedTable.create();
125     hashCopy.put("foo", 1, 'a');
126     hashCopy.put("bar", 1, 'b');
127     hashCopy.put("foo", 3, 'c');
128     Table<String, Integer, Character> reordered =
129         create("foo", 3, 'c', "foo", 1, 'a', "bar", 1, 'b');
130     Table<String, Integer, Character> smaller = create("foo", 1, 'a', "bar", 1, 'b');
131     Table<String, Integer, Character> swapOuter =
132         create("bar", 1, 'a', "foo", 1, 'b', "bar", 3, 'c');
133     Table<String, Integer, Character> swapValues =
134         create("foo", 1, 'c', "bar", 1, 'b', "foo", 3, 'a');
135 
136     new EqualsTester()
137         .addEqualityGroup(table, reordered)
138         .addEqualityGroup(hashCopy)
139         .addEqualityGroup(smaller)
140         .addEqualityGroup(swapOuter)
141         .addEqualityGroup(swapValues)
142         .testEquals();
143   }
144 
145   @Override
testHashCode()146   public void testHashCode() {
147     table = ArrayTable.create(asList("foo", "bar"), asList(1, 3));
148     table.put("foo", 1, 'a');
149     table.put("bar", 1, 'b');
150     table.put("foo", 3, 'c');
151     int expected =
152         Objects.hashCode("foo", 1, 'a')
153             + Objects.hashCode("bar", 1, 'b')
154             + Objects.hashCode("foo", 3, 'c')
155             + Objects.hashCode("bar", 3, 0);
156     assertEquals(expected, table.hashCode());
157   }
158 
159   @Override
testRow()160   public void testRow() {
161     table = create("foo", 1, 'a', "bar", 1, 'b', "foo", 3, 'c');
162     Map<Integer, Character> expected = Maps.newHashMap();
163     expected.put(1, 'a');
164     expected.put(3, 'c');
165     expected.put(2, null);
166     assertEquals(expected, table.row("foo"));
167   }
168 
169   @Override
testColumn()170   public void testColumn() {
171     table = create("foo", 1, 'a', "bar", 1, 'b', "foo", 3, 'c');
172     Map<String, Character> expected = Maps.newHashMap();
173     expected.put("foo", 'a');
174     expected.put("bar", 'b');
175     expected.put("cat", null);
176     assertEquals(expected, table.column(1));
177   }
178 
179   @Override
testToStringSize1()180   public void testToStringSize1() {
181     table = ArrayTable.create(ImmutableList.of("foo"), ImmutableList.of(1));
182     table.put("foo", 1, 'a');
183     assertEquals("{foo={1=a}}", table.toString());
184   }
185 
testCreateDuplicateRows()186   public void testCreateDuplicateRows() {
187     try {
188       ArrayTable.create(asList("foo", "bar", "foo"), asList(1, 2, 3));
189       fail();
190     } catch (IllegalArgumentException expected) {
191     }
192   }
193 
testCreateDuplicateColumns()194   public void testCreateDuplicateColumns() {
195     try {
196       ArrayTable.create(asList("foo", "bar"), asList(1, 2, 3, 2));
197       fail();
198     } catch (IllegalArgumentException expected) {
199     }
200   }
201 
testCreateEmptyRows()202   public void testCreateEmptyRows() {
203     try {
204       ArrayTable.create(Arrays.<String>asList(), asList(1, 2, 3));
205       fail();
206     } catch (IllegalArgumentException expected) {
207     }
208   }
209 
testCreateEmptyColumns()210   public void testCreateEmptyColumns() {
211     try {
212       ArrayTable.create(asList("foo", "bar"), Arrays.<Integer>asList());
213       fail();
214     } catch (IllegalArgumentException expected) {
215     }
216   }
217 
testCreateEmptyRowsXColumns()218   public void testCreateEmptyRowsXColumns() {
219     ArrayTable<String, String, Character> table =
220         ArrayTable.create(Arrays.<String>asList(), Arrays.<String>asList());
221     assertThat(table).isEmpty();
222     assertThat(table).hasSize(0);
223     assertThat(table.columnKeyList()).isEmpty();
224     assertThat(table.rowKeyList()).isEmpty();
225     assertThat(table.columnKeySet()).isEmpty();
226     assertThat(table.rowKeySet()).isEmpty();
227     try {
228       table.at(0, 0);
229       fail();
230     } catch (IndexOutOfBoundsException expected) {
231     }
232   }
233 
234   @GwtIncompatible // toArray
testEmptyToArry()235   public void testEmptyToArry() {
236     ArrayTable<String, String, Character> table =
237         ArrayTable.create(Arrays.<String>asList(), Arrays.<String>asList());
238     assertThat(table.toArray(Character.class)).asList().isEmpty();
239   }
240 
testCreateCopyArrayTable()241   public void testCreateCopyArrayTable() {
242     Table<String, Integer, Character> original =
243         create("foo", 1, 'a', "bar", 1, 'b', "foo", 3, 'c');
244     Table<String, Integer, Character> copy = ArrayTable.create(original);
245     assertEquals(original, copy);
246     original.put("foo", 1, 'd');
247     assertEquals((Character) 'd', original.get("foo", 1));
248     assertEquals((Character) 'a', copy.get("foo", 1));
249     assertEquals(copy.rowKeySet(), original.rowKeySet());
250     assertEquals(copy.columnKeySet(), original.columnKeySet());
251   }
252 
testCreateCopyHashBasedTable()253   public void testCreateCopyHashBasedTable() {
254     Table<String, Integer, Character> original = HashBasedTable.create();
255     original.put("foo", 1, 'a');
256     original.put("bar", 1, 'b');
257     original.put("foo", 3, 'c');
258     Table<String, Integer, Character> copy = ArrayTable.create(original);
259     assertEquals(4, copy.size());
260     assertEquals((Character) 'a', copy.get("foo", 1));
261     assertEquals((Character) 'b', copy.get("bar", 1));
262     assertEquals((Character) 'c', copy.get("foo", 3));
263     assertNull(copy.get("bar", 3));
264     original.put("foo", 1, 'd');
265     assertEquals((Character) 'd', original.get("foo", 1));
266     assertEquals((Character) 'a', copy.get("foo", 1));
267     assertEquals(copy.rowKeySet(), ImmutableSet.of("foo", "bar"));
268     assertEquals(copy.columnKeySet(), ImmutableSet.of(1, 3));
269   }
270 
testCreateCopyEmptyTable()271   public void testCreateCopyEmptyTable() {
272     Table<String, Integer, Character> original = HashBasedTable.create();
273     ArrayTable<String, Integer, Character> copy = ArrayTable.create(original);
274     assertThat(copy).isEqualTo(original);
275     assertThat(copy)
276         .isEqualTo(ArrayTable.create(Arrays.<String>asList(), Arrays.<Integer>asList()));
277     assertThat(copy).isEmpty();
278   }
279 
testCreateCopyEmptyArrayTable()280   public void testCreateCopyEmptyArrayTable() {
281     Table<String, Integer, Character> original =
282         ArrayTable.create(Arrays.<String>asList(), Arrays.<Integer>asList());
283     ArrayTable<String, Integer, Character> copy = ArrayTable.create(original);
284     assertThat(copy).isEqualTo(original);
285     assertThat(copy).isEmpty();
286   }
287 
testSerialization()288   public void testSerialization() {
289     table = create("foo", 1, 'a', "bar", 1, 'b', "foo", 3, 'c');
290     SerializableTester.reserializeAndAssert(table);
291   }
292 
293   @GwtIncompatible // reflection
testNullPointerStatic()294   public void testNullPointerStatic() {
295     new NullPointerTester().testAllPublicStaticMethods(ArrayTable.class);
296   }
297 
testToString_ordered()298   public void testToString_ordered() {
299     table = create("foo", 1, 'a', "bar", 1, 'b', "foo", 3, 'c');
300     assertEquals(
301         "{foo={1=a, 2=null, 3=c}, "
302             + "bar={1=b, 2=null, 3=null}, "
303             + "cat={1=null, 2=null, 3=null}}",
304         table.toString());
305     assertEquals(
306         "{foo={1=a, 2=null, 3=c}, "
307             + "bar={1=b, 2=null, 3=null}, "
308             + "cat={1=null, 2=null, 3=null}}",
309         table.rowMap().toString());
310   }
311 
testCellSetToString_ordered()312   public void testCellSetToString_ordered() {
313     table = create("foo", 1, 'a', "bar", 1, 'b', "foo", 3, 'c');
314     assertEquals(
315         "[(foo,1)=a, (foo,2)=null, (foo,3)=c, "
316             + "(bar,1)=b, (bar,2)=null, (bar,3)=null, "
317             + "(cat,1)=null, (cat,2)=null, (cat,3)=null]",
318         table.cellSet().toString());
319   }
320 
testRowKeySetToString_ordered()321   public void testRowKeySetToString_ordered() {
322     table = create("foo", 1, 'a', "bar", 1, 'b', "foo", 3, 'c');
323     assertEquals("[foo, bar, cat]", table.rowKeySet().toString());
324   }
325 
testColumnKeySetToString_ordered()326   public void testColumnKeySetToString_ordered() {
327     table = create("foo", 1, 'a', "bar", 1, 'b', "foo", 3, 'c');
328     assertEquals("[1, 2, 3]", table.columnKeySet().toString());
329   }
330 
testValuesToString_ordered()331   public void testValuesToString_ordered() {
332     table = create("foo", 1, 'a', "bar", 1, 'b', "foo", 3, 'c');
333     assertEquals("[a, null, c, b, null, null, null, null, null]", table.values().toString());
334   }
335 
testRowKeyList()336   public void testRowKeyList() {
337     ArrayTable<String, Integer, Character> table =
338         create("foo", 1, 'a', "bar", 1, 'b', "foo", 3, 'c');
339     assertThat(table.rowKeyList()).containsExactly("foo", "bar", "cat").inOrder();
340   }
341 
testColumnKeyList()342   public void testColumnKeyList() {
343     ArrayTable<String, Integer, Character> table =
344         create("foo", 1, 'a', "bar", 1, 'b', "foo", 3, 'c');
345     assertThat(table.columnKeyList()).containsExactly(1, 2, 3).inOrder();
346   }
347 
testGetMissingKeys()348   public void testGetMissingKeys() {
349     table = create("foo", 1, 'a', "bar", 1, 'b', "foo", 3, 'c');
350     assertNull(table.get("dog", 1));
351     assertNull(table.get("foo", 4));
352   }
353 
testAt()354   public void testAt() {
355     ArrayTable<String, Integer, Character> table =
356         create("foo", 1, 'a', "bar", 1, 'b', "foo", 3, 'c');
357     assertEquals((Character) 'b', table.at(1, 0));
358     assertEquals((Character) 'c', table.at(0, 2));
359     assertNull(table.at(1, 2));
360     try {
361       table.at(1, 3);
362       fail();
363     } catch (IndexOutOfBoundsException expected) {
364     }
365     try {
366       table.at(1, -1);
367       fail();
368     } catch (IndexOutOfBoundsException expected) {
369     }
370     try {
371       table.at(3, 2);
372       fail();
373     } catch (IndexOutOfBoundsException expected) {
374     }
375     try {
376       table.at(-1, 2);
377       fail();
378     } catch (IndexOutOfBoundsException expected) {
379     }
380   }
381 
testSet()382   public void testSet() {
383     ArrayTable<String, Integer, Character> table =
384         create("foo", 1, 'a', "bar", 1, 'b', "foo", 3, 'c');
385     assertEquals((Character) 'b', table.set(1, 0, 'd'));
386     assertEquals((Character) 'd', table.get("bar", 1));
387     assertNull(table.set(2, 0, 'e'));
388     assertEquals((Character) 'e', table.get("cat", 1));
389     assertEquals((Character) 'a', table.set(0, 0, null));
390     assertNull(table.get("foo", 1));
391     try {
392       table.set(1, 3, 'z');
393       fail();
394     } catch (IndexOutOfBoundsException expected) {
395     }
396     try {
397       table.set(1, -1, 'z');
398       fail();
399     } catch (IndexOutOfBoundsException expected) {
400     }
401     try {
402       table.set(3, 2, 'z');
403       fail();
404     } catch (IndexOutOfBoundsException expected) {
405     }
406     try {
407       table.set(-1, 2, 'z');
408       fail();
409     } catch (IndexOutOfBoundsException expected) {
410     }
411     assertFalse(table.containsValue('z'));
412   }
413 
testEraseAll()414   public void testEraseAll() {
415     ArrayTable<String, Integer, Character> table =
416         create("foo", 1, 'a', "bar", 1, 'b', "foo", 3, 'c');
417     table.eraseAll();
418     assertEquals(9, table.size());
419     assertNull(table.get("bar", 1));
420     assertTrue(table.containsRow("foo"));
421     assertFalse(table.containsValue('a'));
422   }
423 
testPutIllegal()424   public void testPutIllegal() {
425     table = create("foo", 1, 'a', "bar", 1, 'b', "foo", 3, 'c');
426     try {
427       table.put("dog", 1, 'd');
428       fail();
429     } catch (IllegalArgumentException expected) {
430       assertThat(expected).hasMessageThat().isEqualTo("Row dog not in [foo, bar, cat]");
431     }
432     try {
433       table.put("foo", 4, 'd');
434       fail();
435     } catch (IllegalArgumentException expected) {
436       assertThat(expected).hasMessageThat().isEqualTo("Column 4 not in [1, 2, 3]");
437     }
438     assertFalse(table.containsValue('d'));
439   }
440 
testErase()441   public void testErase() {
442     ArrayTable<String, Integer, Character> table =
443         create("foo", 1, 'a', "bar", 1, 'b', "foo", 3, 'c');
444     assertEquals((Character) 'b', table.erase("bar", 1));
445     assertNull(table.get("bar", 1));
446     assertEquals(9, table.size());
447     assertNull(table.erase("bar", 1));
448     assertNull(table.erase("foo", 2));
449     assertNull(table.erase("dog", 1));
450     assertNull(table.erase("bar", 5));
451     assertNull(table.erase(null, 1));
452     assertNull(table.erase("bar", null));
453   }
454 
455   @GwtIncompatible // ArrayTable.toArray(Class)
testToArray()456   public void testToArray() {
457     ArrayTable<String, Integer, Character> table =
458         create("foo", 1, 'a', "bar", 1, 'b', "foo", 3, 'c');
459     Character[][] array = table.toArray(Character.class);
460     assertThat(array).hasLength(3);
461     assertThat(array[0]).asList().containsExactly('a', null, 'c').inOrder();
462     assertThat(array[1]).asList().containsExactly('b', null, null).inOrder();
463     assertThat(array[2]).asList().containsExactly(null, null, null).inOrder();
464     table.set(0, 2, 'd');
465     assertEquals((Character) 'c', array[0][2]);
466     array[0][2] = 'e';
467     assertEquals((Character) 'd', table.at(0, 2));
468   }
469 
testCellReflectsChanges()470   public void testCellReflectsChanges() {
471     table = create("foo", 1, 'a', "bar", 1, 'b', "foo", 3, 'c');
472     Cell<String, Integer, Character> cell = table.cellSet().iterator().next();
473     assertEquals(Tables.immutableCell("foo", 1, 'a'), cell);
474     assertEquals((Character) 'a', table.put("foo", 1, 'd'));
475     assertEquals(Tables.immutableCell("foo", 1, 'd'), cell);
476   }
477 
testRowMissing()478   public void testRowMissing() {
479     table = create("foo", 1, 'a', "bar", 1, 'b', "foo", 3, 'c');
480     Map<Integer, Character> row = table.row("dog");
481     assertTrue(row.isEmpty());
482     try {
483       row.put(1, 'd');
484       fail();
485     } catch (UnsupportedOperationException expected) {
486     }
487   }
488 
testColumnMissing()489   public void testColumnMissing() {
490     table = create("foo", 1, 'a', "bar", 1, 'b', "foo", 3, 'c');
491     Map<String, Character> column = table.column(4);
492     assertTrue(column.isEmpty());
493     try {
494       column.put("foo", 'd');
495       fail();
496     } catch (UnsupportedOperationException expected) {
497     }
498   }
499 
testRowPutIllegal()500   public void testRowPutIllegal() {
501     table = create("foo", 1, 'a', "bar", 1, 'b', "foo", 3, 'c');
502     Map<Integer, Character> map = table.row("foo");
503     try {
504       map.put(4, 'd');
505       fail();
506     } catch (IllegalArgumentException expected) {
507       assertThat(expected).hasMessageThat().isEqualTo("Column 4 not in [1, 2, 3]");
508     }
509   }
510 
testColumnPutIllegal()511   public void testColumnPutIllegal() {
512     table = create("foo", 1, 'a', "bar", 1, 'b', "foo", 3, 'c');
513     Map<String, Character> map = table.column(3);
514     try {
515       map.put("dog", 'd');
516       fail();
517     } catch (IllegalArgumentException expected) {
518       assertThat(expected).hasMessageThat().isEqualTo("Row dog not in [foo, bar, cat]");
519     }
520   }
521 
522   @GwtIncompatible // reflection
testNulls()523   public void testNulls() {
524     new NullPointerTester().testAllPublicInstanceMethods(create());
525   }
526 
527   @GwtIncompatible // serialize
testSerializable()528   public void testSerializable() {
529     SerializableTester.reserializeAndAssert(create());
530   }
531 }
532