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 org.truth0.Truth.ASSERT;
20
21import com.google.common.annotations.GwtCompatible;
22import com.google.common.annotations.GwtIncompatible;
23
24/**
25 * Tests common methods in {@link ImmutableTable}
26 *
27 * @author Gregory Kick
28 */
29@GwtCompatible(emulated = true)
30public class ImmutableTableTest extends AbstractTableReadTest {
31  @Override protected Table<String, Integer, Character> create(Object... data) {
32    ImmutableTable.Builder<String, Integer, Character> builder =
33        ImmutableTable.builder();
34    for (int i = 0; i < data.length; i = i + 3) {
35      builder.put((String) data[i], (Integer) data[i + 1],
36          (Character) data[i + 2]);
37    }
38    return builder.build();
39  }
40
41  public void testBuilder() {
42    ImmutableTable.Builder<Character, Integer, String> builder =
43        new ImmutableTable.Builder<Character, Integer, String>();
44    assertEquals(ImmutableTable.of(), builder.build());
45    assertEquals(ImmutableTable.of('a', 1, "foo"), builder
46        .put('a', 1, "foo")
47        .build());
48    Table<Character, Integer, String> expectedTable = HashBasedTable.create();
49    expectedTable.put('a', 1, "foo");
50    expectedTable.put('b', 1, "bar");
51    expectedTable.put('a', 2, "baz");
52    Table<Character, Integer, String> otherTable = HashBasedTable.create();
53    otherTable.put('b', 1, "bar");
54    otherTable.put('a', 2, "baz");
55    assertEquals(expectedTable, builder
56        .putAll(otherTable)
57        .build());
58  }
59
60  public void testBuilder_withImmutableCell() {
61    ImmutableTable.Builder<Character, Integer, String> builder =
62        new ImmutableTable.Builder<Character, Integer, String>();
63    assertEquals(ImmutableTable.of('a', 1, "foo"), builder
64        .put(Tables.immutableCell('a', 1, "foo"))
65        .build());
66  }
67
68  public void testBuilder_withImmutableCellAndNullContents() {
69    ImmutableTable.Builder<Character, Integer, String> builder =
70        new ImmutableTable.Builder<Character, Integer, String>();
71    try {
72      builder.put(Tables.immutableCell((Character) null, 1, "foo"));
73      fail();
74    } catch (NullPointerException e) {
75      // success
76    }
77    try {
78      builder.put(Tables.immutableCell('a', (Integer) null, "foo"));
79      fail();
80    } catch (NullPointerException e) {
81      // success
82    }
83    try {
84      builder.put(Tables.immutableCell('a', 1, (String) null));
85      fail();
86    } catch (NullPointerException e) {
87      // success
88    }
89  }
90
91  private static class StringHolder {
92    String string;
93  }
94
95  public void testBuilder_withMutableCell() {
96    ImmutableTable.Builder<Character, Integer, String> builder =
97        new ImmutableTable.Builder<Character, Integer, String>();
98
99    final StringHolder holder = new StringHolder();
100    holder.string = "foo";
101    Table.Cell<Character, Integer, String> mutableCell =
102        new Tables.AbstractCell<Character, Integer, String>() {
103          @Override public Character getRowKey() {
104            return 'K';
105          }
106          @Override public Integer getColumnKey() {
107            return 42;
108          }
109          @Override public String getValue() {
110            return holder.string;
111          }
112        };
113
114    // Add the mutable cell to the builder
115    builder.put(mutableCell);
116
117    // Mutate the value
118    holder.string = "bar";
119
120    // Make sure it uses the original value.
121    assertEquals(ImmutableTable.of('K', 42, "foo"), builder.build());
122  }
123
124  public void testBuilder_noDuplicates() {
125    ImmutableTable.Builder<Character, Integer, String> builder =
126        new ImmutableTable.Builder<Character, Integer, String>()
127            .put('a', 1, "foo")
128            .put('a', 1, "bar");
129    try {
130      builder.build();
131      fail();
132    } catch (IllegalArgumentException e) {
133      // success
134    }
135  }
136
137  public void testBuilder_noNulls() {
138    ImmutableTable.Builder<Character, Integer, String> builder =
139        new ImmutableTable.Builder<Character, Integer, String>();
140    try {
141      builder.put(null, 1, "foo");
142      fail();
143    } catch (NullPointerException e) {
144      // success
145    }
146    try {
147      builder.put('a', null, "foo");
148      fail();
149    } catch (NullPointerException e) {
150      // success
151    }
152    try {
153      builder.put('a', 1, null);
154      fail();
155    } catch (NullPointerException e) {
156      // success
157    }
158  }
159
160  private static <R, C, V> void validateTableCopies(Table<R, C, V> original) {
161    Table<R, C, V> copy = ImmutableTable.copyOf(original);
162    assertEquals(original, copy);
163    validateViewOrdering(original, copy);
164
165    Table<R, C, V> built
166        = ImmutableTable.<R, C, V>builder().putAll(original).build();
167    assertEquals(original, built);
168    validateViewOrdering(original, built);
169  }
170
171  private static <R, C, V> void validateViewOrdering(
172      Table<R, C, V> original, Table<R, C, V> copy) {
173    assertTrue(Iterables.elementsEqual(original.cellSet(), copy.cellSet()));
174    assertTrue(Iterables.elementsEqual(original.rowKeySet(), copy.rowKeySet()));
175    assertTrue(Iterables.elementsEqual(original.values(), copy.values()));
176  }
177
178  public void testCopyOf() {
179    Table<Character, Integer, String> table = TreeBasedTable.create();
180    validateTableCopies(table);
181    table.put('b', 2, "foo");
182    validateTableCopies(table);
183    table.put('b', 1, "bar");
184    table.put('a', 2, "baz");
185    validateTableCopies(table);
186    // Even though rowKeySet, columnKeySet, and cellSet have the same
187    // iteration ordering, row has an inconsistent ordering.
188    ASSERT.that(table.row('b').keySet()).has().exactly(1, 2).inOrder();
189    ASSERT.that(ImmutableTable.copyOf(table).row('b').keySet())
190        .has().exactly(2, 1).inOrder();
191  }
192
193  public void testCopyOfSparse() {
194    Table<Character, Integer, String> table = TreeBasedTable.create();
195    table.put('x', 2, "foo");
196    table.put('r', 1, "bar");
197    table.put('c', 3, "baz");
198    table.put('b', 7, "cat");
199    table.put('e', 5, "dog");
200    table.put('c', 0, "axe");
201    table.put('e', 3, "tub");
202    table.put('r', 4, "foo");
203    table.put('x', 5, "bar");
204    validateTableCopies(table);
205  }
206
207  public void testCopyOfDense() {
208    Table<Character, Integer, String> table = TreeBasedTable.create();
209    table.put('c', 3, "foo");
210    table.put('c', 2, "bar");
211    table.put('c', 1, "baz");
212    table.put('b', 3, "cat");
213    table.put('b', 1, "dog");
214    table.put('a', 3, "foo");
215    table.put('a', 2, "bar");
216    table.put('a', 1, "baz");
217    validateTableCopies(table);
218  }
219
220  public void testBuilder_orderRowsAndColumnsBy_putAll() {
221    Table<Character, Integer, String> table = HashBasedTable.create();
222    table.put('b', 2, "foo");
223    table.put('b', 1, "bar");
224    table.put('a', 2, "baz");
225    ImmutableTable.Builder<Character, Integer, String> builder
226        = ImmutableTable.builder();
227    Table<Character, Integer, String> copy
228        = builder.orderRowsBy(Ordering.natural())
229            .orderColumnsBy(Ordering.natural())
230            .putAll(table).build();
231    ASSERT.that(copy.rowKeySet()).has().exactly('a', 'b').inOrder();
232    ASSERT.that(copy.columnKeySet()).has().exactly(1, 2).inOrder();
233    ASSERT.that(copy.values()).has().exactly("baz", "bar", "foo").inOrder();
234    ASSERT.that(copy.row('b').keySet()).has().exactly(1, 2).inOrder();
235  }
236
237  public void testBuilder_orderRowsAndColumnsBy_sparse() {
238    ImmutableTable.Builder<Character, Integer, String> builder
239        = ImmutableTable.builder();
240    builder.orderRowsBy(Ordering.natural());
241    builder.orderColumnsBy(Ordering.natural());
242    builder.put('x', 2, "foo");
243    builder.put('r', 1, "bar");
244    builder.put('c', 3, "baz");
245    builder.put('b', 7, "cat");
246    builder.put('e', 5, "dog");
247    builder.put('c', 0, "axe");
248    builder.put('e', 3, "tub");
249    builder.put('r', 4, "foo");
250    builder.put('x', 5, "bar");
251    Table<Character, Integer, String> table = builder.build();
252    ASSERT.that(table.rowKeySet()).has().exactly('b', 'c', 'e', 'r', 'x').inOrder();
253    ASSERT.that(table.columnKeySet()).has().exactly(0, 1, 2, 3, 4, 5, 7).inOrder();
254    ASSERT.that(table.values()).has().exactly("cat", "axe", "baz", "tub",
255        "dog", "bar", "foo", "foo", "bar").inOrder();
256    ASSERT.that(table.row('c').keySet()).has().exactly(0, 3).inOrder();
257    ASSERT.that(table.column(5).keySet()).has().exactly('e', 'x').inOrder();
258  }
259
260  public void testBuilder_orderRowsAndColumnsBy_dense() {
261    ImmutableTable.Builder<Character, Integer, String> builder
262        = ImmutableTable.builder();
263    builder.orderRowsBy(Ordering.natural());
264    builder.orderColumnsBy(Ordering.natural());
265    builder.put('c', 3, "foo");
266    builder.put('c', 2, "bar");
267    builder.put('c', 1, "baz");
268    builder.put('b', 3, "cat");
269    builder.put('b', 1, "dog");
270    builder.put('a', 3, "foo");
271    builder.put('a', 2, "bar");
272    builder.put('a', 1, "baz");
273    Table<Character, Integer, String> table = builder.build();
274    ASSERT.that(table.rowKeySet()).has().exactly('a', 'b', 'c').inOrder();
275    ASSERT.that(table.columnKeySet()).has().exactly(1, 2, 3).inOrder();
276    ASSERT.that(table.values()).has().exactly("baz", "bar", "foo", "dog",
277        "cat", "baz", "bar", "foo").inOrder();
278    ASSERT.that(table.row('c').keySet()).has().exactly(1, 2, 3).inOrder();
279    ASSERT.that(table.column(1).keySet()).has().exactly('a', 'b', 'c').inOrder();
280  }
281
282  public void testBuilder_orderRowsBy_sparse() {
283    ImmutableTable.Builder<Character, Integer, String> builder
284        = ImmutableTable.builder();
285    builder.orderRowsBy(Ordering.natural());
286    builder.put('x', 2, "foo");
287    builder.put('r', 1, "bar");
288    builder.put('c', 3, "baz");
289    builder.put('b', 7, "cat");
290    builder.put('e', 5, "dog");
291    builder.put('c', 0, "axe");
292    builder.put('e', 3, "tub");
293    builder.put('r', 4, "foo");
294    builder.put('x', 5, "bar");
295    Table<Character, Integer, String> table = builder.build();
296    ASSERT.that(table.rowKeySet()).has().exactly('b', 'c', 'e', 'r', 'x').inOrder();
297    ASSERT.that(table.column(5).keySet()).has().exactly('e', 'x').inOrder();
298  }
299
300  public void testBuilder_orderRowsBy_dense() {
301    ImmutableTable.Builder<Character, Integer, String> builder
302        = ImmutableTable.builder();
303    builder.orderRowsBy(Ordering.natural());
304    builder.put('c', 3, "foo");
305    builder.put('c', 2, "bar");
306    builder.put('c', 1, "baz");
307    builder.put('b', 3, "cat");
308    builder.put('b', 1, "dog");
309    builder.put('a', 3, "foo");
310    builder.put('a', 2, "bar");
311    builder.put('a', 1, "baz");
312    Table<Character, Integer, String> table = builder.build();
313    ASSERT.that(table.rowKeySet()).has().exactly('a', 'b', 'c').inOrder();
314    ASSERT.that(table.column(1).keySet()).has().exactly('a', 'b', 'c').inOrder();
315  }
316
317  public void testBuilder_orderColumnsBy_sparse() {
318    ImmutableTable.Builder<Character, Integer, String> builder
319        = ImmutableTable.builder();
320    builder.orderColumnsBy(Ordering.natural());
321    builder.put('x', 2, "foo");
322    builder.put('r', 1, "bar");
323    builder.put('c', 3, "baz");
324    builder.put('b', 7, "cat");
325    builder.put('e', 5, "dog");
326    builder.put('c', 0, "axe");
327    builder.put('e', 3, "tub");
328    builder.put('r', 4, "foo");
329    builder.put('x', 5, "bar");
330    Table<Character, Integer, String> table = builder.build();
331    ASSERT.that(table.columnKeySet()).has().exactly(0, 1, 2, 3, 4, 5, 7).inOrder();
332    ASSERT.that(table.row('c').keySet()).has().exactly(0, 3).inOrder();
333  }
334
335  public void testBuilder_orderColumnsBy_dense() {
336    ImmutableTable.Builder<Character, Integer, String> builder
337        = ImmutableTable.builder();
338    builder.orderColumnsBy(Ordering.natural());
339    builder.put('c', 3, "foo");
340    builder.put('c', 2, "bar");
341    builder.put('c', 1, "baz");
342    builder.put('b', 3, "cat");
343    builder.put('b', 1, "dog");
344    builder.put('a', 3, "foo");
345    builder.put('a', 2, "bar");
346    builder.put('a', 1, "baz");
347    Table<Character, Integer, String> table = builder.build();
348    ASSERT.that(table.columnKeySet()).has().exactly(1, 2, 3).inOrder();
349    ASSERT.that(table.row('c').keySet()).has().exactly(1, 2, 3).inOrder();
350  }
351
352  @GwtIncompatible("Mind-bogglingly slow in GWT")
353  public void testOverflowCondition() {
354    // See https://code.google.com/p/guava-libraries/issues/detail?id=1322 for details.
355    ImmutableTable.Builder<Integer, Integer, String> builder = ImmutableTable.builder();
356    for (int i = 1; i < 0x10000; i++) {
357      builder.put(i, 0, "foo");
358      builder.put(0, i, "bar");
359    }
360    assertTrue(builder.build() instanceof SparseImmutableTable);
361  }
362}
363