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