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