TableCollectionTest.java revision 7dd252788645e940eada959bdde927426e2531c9
1/*
2 * Copyright (C) 2008 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.base.Preconditions.checkNotNull;
20import static com.google.common.collect.testing.testers.CollectionIteratorTester.getIteratorKnownOrderRemoveSupportedMethod;
21import static com.google.common.collect.testing.testers.CollectionIteratorTester.getIteratorUnknownOrderRemoveSupportedMethod;
22
23import com.google.common.annotations.GwtCompatible;
24import com.google.common.annotations.GwtIncompatible;
25import com.google.common.base.Function;
26import com.google.common.base.Functions;
27import com.google.common.collect.Table.Cell;
28import com.google.common.collect.testing.CollectionTestSuiteBuilder;
29import com.google.common.collect.testing.MapInterfaceTest;
30import com.google.common.collect.testing.SampleElements;
31import com.google.common.collect.testing.SetTestSuiteBuilder;
32import com.google.common.collect.testing.TestSetGenerator;
33import com.google.common.collect.testing.TestStringCollectionGenerator;
34import com.google.common.collect.testing.TestStringSetGenerator;
35import com.google.common.collect.testing.features.CollectionFeature;
36import com.google.common.collect.testing.features.CollectionSize;
37import com.google.common.collect.testing.features.Feature;
38
39import java.util.Arrays;
40import java.util.Collection;
41import java.util.Collections;
42import java.util.List;
43import java.util.Map;
44import java.util.Set;
45import java.util.SortedMap;
46
47import junit.framework.Test;
48import junit.framework.TestCase;
49import junit.framework.TestSuite;
50
51/**
52 * Collection tests for {@link Table} implementations.
53 *
54 * @author Jared Levy
55 * @author Louis Wasserman
56 */
57@GwtCompatible(emulated = true)
58public class TableCollectionTest extends TestCase {
59
60  private static final Feature<?>[] COLLECTION_FEATURES = {
61    CollectionSize.ANY,
62    CollectionFeature.ALLOWS_NULL_QUERIES
63  };
64
65  private static final Feature<?>[] COLLECTION_FEATURES_ORDER = {
66    CollectionSize.ANY,
67    CollectionFeature.KNOWN_ORDER,
68    CollectionFeature.ALLOWS_NULL_QUERIES
69  };
70
71  private static final Feature<?>[] COLLECTION_FEATURES_REMOVE = {
72    CollectionSize.ANY,
73    CollectionFeature.SUPPORTS_REMOVE,
74    CollectionFeature.ALLOWS_NULL_QUERIES
75  };
76
77  private static final Feature<?>[] COLLECTION_FEATURES_REMOVE_ORDER = {
78    CollectionSize.ANY,
79    CollectionFeature.KNOWN_ORDER,
80    CollectionFeature.SUPPORTS_REMOVE,
81    CollectionFeature.ALLOWS_NULL_QUERIES
82  };
83
84  @GwtIncompatible("suite")
85  public static Test suite() {
86    TestSuite suite = new TestSuite();
87    suite.addTestSuite(ArrayRowTests.class);
88    suite.addTestSuite(HashRowTests.class);
89    suite.addTestSuite(TreeRowTests.class);
90    suite.addTestSuite(TransposeRowTests.class);
91    suite.addTestSuite(TransformValueRowTests.class);
92    suite.addTestSuite(UnmodifiableHashRowTests.class);
93    suite.addTestSuite(UnmodifiableTreeRowTests.class);
94    suite.addTestSuite(ArrayColumnTests.class);
95    suite.addTestSuite(HashColumnTests.class);
96    suite.addTestSuite(TreeColumnTests.class);
97    suite.addTestSuite(TransposeColumnTests.class);
98    suite.addTestSuite(TransformValueColumnTests.class);
99    suite.addTestSuite(UnmodifiableHashColumnTests.class);
100    suite.addTestSuite(UnmodifiableTreeColumnTests.class);
101    suite.addTestSuite(ArrayRowMapTests.class);
102    suite.addTestSuite(HashRowMapTests.class);
103    suite.addTestSuite(TreeRowMapTests.class);
104    suite.addTestSuite(TreeRowMapHeadMapTests.class);
105    suite.addTestSuite(TreeRowMapTailMapTests.class);
106    suite.addTestSuite(TreeRowMapSubMapTests.class);
107    suite.addTestSuite(TransformValueRowMapTests.class);
108    suite.addTestSuite(UnmodifiableHashRowMapTests.class);
109    suite.addTestSuite(UnmodifiableTreeRowMapTests.class);
110    suite.addTestSuite(ArrayColumnMapTests.class);
111    suite.addTestSuite(HashColumnMapTests.class);
112    suite.addTestSuite(TreeColumnMapTests.class);
113    suite.addTestSuite(TransformValueColumnMapTests.class);
114    suite.addTestSuite(UnmodifiableHashColumnMapTests.class);
115    suite.addTestSuite(UnmodifiableTreeColumnMapTests.class);
116
117    // Not testing rowKeySet() or columnKeySet() of Table.transformValues()
118    // since the transformation doesn't affect the row and column key sets.
119
120    suite.addTest(SetTestSuiteBuilder.using(new TestStringSetGenerator() {
121          @Override protected Set<String> create(String[] elements) {
122            Table<String, Integer, Character> table
123                = ArrayTable.create(
124                    ImmutableList.copyOf(elements), ImmutableList.of(1, 2));
125            populateForRowKeySet(table, elements);
126            return table.rowKeySet();
127          }
128        })
129        .named("ArrayTable.rowKeySet")
130        .withFeatures(CollectionSize.ONE, CollectionSize.SEVERAL,
131            CollectionFeature.KNOWN_ORDER,
132            CollectionFeature.REJECTS_DUPLICATES_AT_CREATION,
133            CollectionFeature.ALLOWS_NULL_QUERIES)
134        .createTestSuite());
135
136    suite.addTest(SetTestSuiteBuilder.using(new TestStringSetGenerator() {
137          @Override protected Set<String> create(String[] elements) {
138            Table<String, Integer, Character> table = HashBasedTable.create();
139            populateForRowKeySet(table, elements);
140            return table.rowKeySet();
141          }
142        })
143        .named("HashBasedTable.rowKeySet")
144        .withFeatures(COLLECTION_FEATURES_REMOVE)
145        .createTestSuite());
146
147    suite.addTest(SetTestSuiteBuilder.using(new TestStringSetGenerator() {
148          @Override protected Set<String> create(String[] elements) {
149            Table<String, Integer, Character> table = TreeBasedTable.create();
150            populateForRowKeySet(table, elements);
151            return table.rowKeySet();
152          }
153
154          @Override public List<String> order(List<String> insertionOrder) {
155            Collections.sort(insertionOrder);
156            return insertionOrder;
157          }
158        })
159        .named("TreeBasedTable.rowKeySet")
160        .withFeatures(COLLECTION_FEATURES_REMOVE_ORDER)
161        .createTestSuite());
162
163    suite.addTest(SetTestSuiteBuilder.using(new TestStringSetGenerator() {
164          @Override protected Set<String> create(String[] elements) {
165            TreeBasedTable<String, Integer, Character> table
166                = TreeBasedTable.create();
167            populateForRowKeySet(table, elements);
168            table.put("z", 1, 'a');
169            return table.rowKeySet().headSet("x");
170          }
171
172          @Override public List<String> order(List<String> insertionOrder) {
173            Collections.sort(insertionOrder);
174            return insertionOrder;
175          }
176        })
177        .named("TreeBasedTable.rowKeySet.headSet")
178        .withFeatures(COLLECTION_FEATURES_REMOVE_ORDER)
179        .createTestSuite());
180
181    suite.addTest(SetTestSuiteBuilder.using(new TestStringSetGenerator() {
182          @Override protected Set<String> create(String[] elements) {
183            TreeBasedTable<String, Integer, Character> table
184                = TreeBasedTable.create();
185            populateForRowKeySet(table, elements);
186            table.put("\0", 1, 'a');
187            return table.rowKeySet().tailSet("a");
188          }
189
190          @Override public List<String> order(List<String> insertionOrder) {
191            Collections.sort(insertionOrder);
192            return insertionOrder;
193          }
194        })
195        .named("TreeBasedTable.rowKeySet.tailSet")
196        .withFeatures(COLLECTION_FEATURES_REMOVE_ORDER)
197        .createTestSuite());
198
199    suite.addTest(SetTestSuiteBuilder.using(new TestStringSetGenerator() {
200          @Override protected Set<String> create(String[] elements) {
201            TreeBasedTable<String, Integer, Character> table
202                = TreeBasedTable.create();
203            populateForRowKeySet(table, elements);
204            table.put("\0", 1, 'a');
205            table.put("z", 1, 'a');
206            return table.rowKeySet().subSet("a", "x");
207          }
208
209          @Override public List<String> order(List<String> insertionOrder) {
210            Collections.sort(insertionOrder);
211            return insertionOrder;
212          }
213        })
214        .named("TreeBasedTable.rowKeySet.subSet")
215        .withFeatures(COLLECTION_FEATURES_REMOVE_ORDER)
216        .createTestSuite());
217
218    suite.addTest(SetTestSuiteBuilder.using(new TestStringSetGenerator() {
219          @Override protected Set<String> create(String[] elements) {
220            Table<String, Integer, Character> table = HashBasedTable.create();
221            populateForRowKeySet(table, elements);
222            return Tables.unmodifiableTable(table).rowKeySet();
223          }
224        })
225        .named("unmodifiableTable[HashBasedTable].rowKeySet")
226        .withFeatures(COLLECTION_FEATURES)
227        .createTestSuite());
228
229    suite.addTest(SetTestSuiteBuilder.using(new TestStringSetGenerator() {
230          @Override protected Set<String> create(String[] elements) {
231            RowSortedTable<String, Integer, Character> table = TreeBasedTable.create();
232            populateForRowKeySet(table, elements);
233            return Tables.unmodifiableRowSortedTable(table).rowKeySet();
234          }
235
236          @Override public List<String> order(List<String> insertionOrder) {
237            Collections.sort(insertionOrder);
238            return insertionOrder;
239          }
240        })
241        .named("unmodifiableRowSortedTable[TreeBasedTable].rowKeySet")
242        .withFeatures(COLLECTION_FEATURES_ORDER)
243        .createTestSuite());
244
245    suite.addTest(SetTestSuiteBuilder.using(new TestStringSetGenerator() {
246          @Override protected Set<String> create(String[] elements) {
247            Table<Integer, String, Character> table
248                = ArrayTable.create(
249                    ImmutableList.of(1, 2), ImmutableList.copyOf(elements));
250            populateForColumnKeySet(table, elements);
251            return table.columnKeySet();
252          }
253        })
254        .named("ArrayTable.columnKeySet")
255        .withFeatures(CollectionSize.ONE, CollectionSize.SEVERAL,
256            CollectionFeature.KNOWN_ORDER,
257            CollectionFeature.REJECTS_DUPLICATES_AT_CREATION,
258            CollectionFeature.ALLOWS_NULL_QUERIES)
259        .createTestSuite());
260
261    suite.addTest(SetTestSuiteBuilder.using(new TestStringSetGenerator() {
262          @Override protected Set<String> create(String[] elements) {
263            Table<Integer, String, Character> table = HashBasedTable.create();
264            populateForColumnKeySet(table, elements);
265            return table.columnKeySet();
266          }
267        })
268        .named("HashBasedTable.columnKeySet")
269        .withFeatures(COLLECTION_FEATURES_REMOVE)
270        .suppressing(getIteratorUnknownOrderRemoveSupportedMethod())
271        .createTestSuite());
272
273    suite.addTest(SetTestSuiteBuilder.using(new TestStringSetGenerator() {
274          @Override protected Set<String> create(String[] elements) {
275            Table<Integer, String, Character> table = TreeBasedTable.create();
276            populateForColumnKeySet(table, elements);
277            return table.columnKeySet();
278          }
279
280          @Override public List<String> order(List<String> insertionOrder) {
281            Collections.sort(insertionOrder);
282            return insertionOrder;
283          }
284        })
285        .named("TreeBasedTable.columnKeySet")
286        .withFeatures(COLLECTION_FEATURES_REMOVE_ORDER)
287        .suppressing(getIteratorKnownOrderRemoveSupportedMethod())
288        .createTestSuite());
289
290    suite.addTest(SetTestSuiteBuilder.using(new TestStringSetGenerator() {
291          @Override protected Set<String> create(String[] elements) {
292            Table<Integer, String, Character> table = HashBasedTable.create();
293            populateForColumnKeySet(table, elements);
294            return Tables.unmodifiableTable(table).columnKeySet();
295          }
296        })
297        .named("unmodifiableTable[HashBasedTable].columnKeySet")
298        .withFeatures(COLLECTION_FEATURES)
299        .suppressing(getIteratorUnknownOrderRemoveSupportedMethod())
300        .createTestSuite());
301
302    suite.addTest(SetTestSuiteBuilder.using(new TestStringSetGenerator() {
303          @Override protected Set<String> create(String[] elements) {
304            RowSortedTable<Integer, String, Character> table = TreeBasedTable.create();
305            populateForColumnKeySet(table, elements);
306            return Tables.unmodifiableRowSortedTable(table).columnKeySet();
307          }
308
309          @Override public List<String> order(List<String> insertionOrder) {
310            Collections.sort(insertionOrder);
311            return insertionOrder;
312          }
313        })
314        .named("unmodifiableRowSortedTable[TreeBasedTable].columnKeySet")
315        .withFeatures(COLLECTION_FEATURES_ORDER)
316        .suppressing(getIteratorKnownOrderRemoveSupportedMethod())
317        .createTestSuite());
318
319    suite.addTest(CollectionTestSuiteBuilder.using(
320        new TestStringCollectionGenerator() {
321          @Override protected Collection<String> create(String[] elements) {
322            List<Integer> rowKeys = Lists.newArrayList();
323            for (int i = 0; i < elements.length; i++) {
324              rowKeys.add(i);
325            }
326            Table<Integer, Character, String> table
327                = ArrayTable.create(rowKeys, ImmutableList.of('a'));
328            populateForValues(table, elements);
329            return table.values();
330          }
331        })
332        .named("ArrayTable.values")
333        .withFeatures(CollectionSize.ONE, CollectionSize.SEVERAL,
334            CollectionFeature.ALLOWS_NULL_VALUES,
335            CollectionFeature.KNOWN_ORDER)
336        .createTestSuite());
337
338    suite.addTest(CollectionTestSuiteBuilder.using(
339        new TestStringCollectionGenerator() {
340          @Override protected Collection<String> create(String[] elements) {
341            Table<Integer, Character, String> table = HashBasedTable.create();
342            table.put(1, 'a', "foo");
343            table.clear();
344            populateForValues(table, elements);
345            return table.values();
346          }
347        })
348        .named("HashBasedTable.values")
349        .withFeatures(COLLECTION_FEATURES_REMOVE)
350        .createTestSuite());
351
352    suite.addTest(CollectionTestSuiteBuilder.using(
353        new TestStringCollectionGenerator() {
354          @Override protected Collection<String> create(String[] elements) {
355            Table<Integer, Character, String> table = TreeBasedTable.create();
356            table.put(1, 'a', "foo");
357            table.clear();
358            populateForValues(table, elements);
359            return table.values();
360          }
361        })
362        .named("TreeBasedTable.values")
363        .withFeatures(COLLECTION_FEATURES_REMOVE_ORDER)
364        .createTestSuite());
365
366    final Function<String, String> removeFirstCharacter
367        = new Function<String, String>() {
368          @Override public String apply(String input) {
369            return input.substring(1);
370          }
371        };
372
373    suite.addTest(CollectionTestSuiteBuilder.using(
374        new TestStringCollectionGenerator() {
375          @Override protected Collection<String> create(String[] elements) {
376            Table<Integer, Character, String> table = HashBasedTable.create();
377            for (int i = 0; i < elements.length; i++) {
378              table.put(i, 'a', "x" + checkNotNull(elements[i]));
379            }
380            return Tables.transformValues(table, removeFirstCharacter).values();
381          }
382        })
383        .named("TransformValues.values")
384        .withFeatures(COLLECTION_FEATURES_REMOVE)
385        .createTestSuite());
386
387    suite.addTest(CollectionTestSuiteBuilder.using(
388        new TestStringCollectionGenerator() {
389          @Override protected Collection<String> create(String[] elements) {
390            Table<Integer, Character, String> table = HashBasedTable.create();
391            table.put(1, 'a', "foo");
392            table.clear();
393            populateForValues(table, elements);
394            return Tables.unmodifiableTable(table).values();
395          }
396        })
397        .named("unmodifiableTable[HashBasedTable].values")
398        .withFeatures(COLLECTION_FEATURES)
399        .createTestSuite());
400
401    suite.addTest(CollectionTestSuiteBuilder.using(
402        new TestStringCollectionGenerator() {
403          @Override protected Collection<String> create(String[] elements) {
404            RowSortedTable<Integer, Character, String> table = TreeBasedTable.create();
405            table.put(1, 'a', "foo");
406            table.clear();
407            populateForValues(table, elements);
408            return Tables.unmodifiableRowSortedTable(table).values();
409          }
410        })
411        .named("unmodifiableTable[TreeBasedTable].values")
412        .withFeatures(COLLECTION_FEATURES_ORDER)
413        .createTestSuite());
414
415    suite.addTest(SetTestSuiteBuilder.using(new TestCellSetGenerator() {
416          @Override public SampleElements<Cell<String, Integer, Character>>
417              samples() {
418            return new SampleElements<Cell<String, Integer, Character>>(
419                Tables.immutableCell("bar", 1, 'a'),
420                Tables.immutableCell("bar", 2, 'b'),
421                Tables.immutableCell("bar", 3, (Character) null),
422                Tables.immutableCell("bar", 4, 'b'),
423                Tables.immutableCell("bar", 5, 'b'));
424          }
425          @Override public Set<Cell<String, Integer, Character>> create(
426              Object... elements) {
427            List<Integer> columnKeys = Lists.newArrayList();
428            for (Object element : elements) {
429              @SuppressWarnings("unchecked")
430              Cell<String, Integer, Character> cell
431                  = (Cell<String, Integer, Character>) element;
432              columnKeys.add(cell.getColumnKey());
433            }
434            Table<String, Integer, Character> table
435                = ArrayTable.create(ImmutableList.of("bar"), columnKeys);
436            for (Object element : elements) {
437              @SuppressWarnings("unchecked")
438              Cell<String, Integer, Character> cell
439                  = (Cell<String, Integer, Character>) element;
440              table.put(cell.getRowKey(), cell.getColumnKey(), cell.getValue());
441            }
442            return table.cellSet();
443          }
444          @Override Table<String, Integer, Character> createTable() {
445            throw new UnsupportedOperationException();
446          }
447        })
448        .named("ArrayTable.cellSet")
449        .withFeatures(CollectionSize.ONE, CollectionSize.SEVERAL,
450            CollectionFeature.KNOWN_ORDER,
451            CollectionFeature.REJECTS_DUPLICATES_AT_CREATION,
452            CollectionFeature.ALLOWS_NULL_QUERIES)
453        .createTestSuite());
454
455    suite.addTest(SetTestSuiteBuilder.using(new TestCellSetGenerator() {
456          @Override Table<String, Integer, Character> createTable() {
457            return HashBasedTable.create();
458          }
459        })
460        .named("HashBasedTable.cellSet")
461        .withFeatures(CollectionSize.ANY, CollectionFeature.SUPPORTS_REMOVE,
462            CollectionFeature.ALLOWS_NULL_QUERIES)
463        .createTestSuite());
464
465    suite.addTest(SetTestSuiteBuilder.using(new TestCellSetGenerator() {
466          @Override Table<String, Integer, Character> createTable() {
467            return TreeBasedTable.create();
468          }
469        })
470        .named("TreeBasedTable.cellSet")
471        .withFeatures(CollectionSize.ANY, CollectionFeature.SUPPORTS_REMOVE,
472            CollectionFeature.ALLOWS_NULL_QUERIES)
473        .createTestSuite());
474
475    suite.addTest(SetTestSuiteBuilder.using(new TestCellSetGenerator() {
476          @Override Table<String, Integer, Character> createTable() {
477            Table<Integer, String, Character> original
478                = TreeBasedTable.create();
479            return Tables.transpose(original);
480          }
481        })
482        .named("TransposedTable.cellSet")
483        .withFeatures(CollectionSize.ANY, CollectionFeature.SUPPORTS_REMOVE,
484            CollectionFeature.ALLOWS_NULL_QUERIES)
485        .createTestSuite());
486
487    suite.addTest(SetTestSuiteBuilder.using(new TestCellSetGenerator() {
488          @Override Table<String, Integer, Character> createTable() {
489            return HashBasedTable.create();
490          }
491          @Override
492          public Set<Cell<String, Integer, Character>> create(
493              Object... elements) {
494            Table<String, Integer, Character> table = createTable();
495            for (Object element : elements) {
496              @SuppressWarnings("unchecked")
497              Cell<String, Integer, Character> cell
498                  = (Cell<String, Integer, Character>) element;
499              table.put(cell.getRowKey(), cell.getColumnKey(), cell.getValue());
500            }
501            return Tables.transformValues(table, Functions.<Character>identity()).cellSet();
502          }
503        })
504        .named("TransformValues.cellSet")
505        .withFeatures(CollectionSize.ANY, CollectionFeature.ALLOWS_NULL_QUERIES,
506            CollectionFeature.SUPPORTS_REMOVE)
507        .createTestSuite());
508
509    suite.addTest(SetTestSuiteBuilder.using(new TestCellSetGenerator() {
510          @Override Table<String, Integer, Character> createTable() {
511            return Tables.unmodifiableTable(HashBasedTable.<String, Integer, Character> create());
512          }
513          @Override
514          public Set<Cell<String, Integer, Character>> create(
515              Object... elements) {
516            Table<String, Integer, Character> table = HashBasedTable.create();
517            for (Object element : elements) {
518              @SuppressWarnings("unchecked")
519              Cell<String, Integer, Character> cell
520                  = (Cell<String, Integer, Character>) element;
521              table.put(cell.getRowKey(), cell.getColumnKey(), cell.getValue());
522            }
523            return Tables.unmodifiableTable(table).cellSet();
524          }
525        })
526        .named("unmodifiableTable[HashBasedTable].cellSet")
527        .withFeatures(CollectionSize.ANY, CollectionFeature.ALLOWS_NULL_QUERIES)
528        .createTestSuite());
529
530    suite.addTest(SetTestSuiteBuilder.using(new TestCellSetGenerator() {
531          @Override RowSortedTable<String, Integer, Character> createTable() {
532            return Tables.unmodifiableRowSortedTable(TreeBasedTable
533                .<String, Integer, Character> create());
534          }
535          @Override
536          public Set<Cell<String, Integer, Character>> create(
537              Object... elements) {
538            RowSortedTable<String, Integer, Character> table = TreeBasedTable.create();
539            for (Object element : elements) {
540              @SuppressWarnings("unchecked")
541              Cell<String, Integer, Character> cell
542                  = (Cell<String, Integer, Character>) element;
543              table.put(cell.getRowKey(), cell.getColumnKey(), cell.getValue());
544            }
545            return Tables.unmodifiableRowSortedTable(table).cellSet();
546          }
547        })
548        .named("unmodifiableRowSortedTable[TreeBasedTable].cellSet")
549        .withFeatures(CollectionSize.ANY, CollectionFeature.ALLOWS_NULL_QUERIES)
550        .createTestSuite());
551
552    suite.addTest(SetTestSuiteBuilder.using(new TestStringSetGenerator() {
553          @Override protected Set<String> create(String[] elements) {
554            Iterable<String> rowKeys = ImmutableSet.copyOf(elements);
555            Iterable<Integer> columnKeys = ImmutableList.of(1, 2, 3);
556            Table<String, Integer, Character> table
557                = ArrayTable.create(rowKeys, columnKeys);
558            populateForRowKeySet(table, elements);
559            return table.column(1).keySet();
560          }
561        })
562        .named("ArrayTable.column.keySet")
563        .withFeatures(CollectionSize.ONE, CollectionSize.SEVERAL,
564            CollectionFeature.KNOWN_ORDER,
565            CollectionFeature.ALLOWS_NULL_QUERIES)
566        .createTestSuite());
567
568    suite.addTest(SetTestSuiteBuilder.using(new TestStringSetGenerator() {
569          @Override protected Set<String> create(String[] elements) {
570            Table<String, Integer, Character> table = HashBasedTable.create();
571            populateForRowKeySet(table, elements);
572            return table.column(1).keySet();
573          }
574        })
575        .named("HashBasedTable.column.keySet")
576        .withFeatures(COLLECTION_FEATURES_REMOVE)
577        .suppressing(getIteratorUnknownOrderRemoveSupportedMethod())
578    .createTestSuite());
579
580    suite.addTest(SetTestSuiteBuilder.using(new TestStringSetGenerator() {
581          @Override protected Set<String> create(String[] elements) {
582            Table<String, Integer, Character> table = TreeBasedTable.create();
583            populateForRowKeySet(table, elements);
584            return table.column(1).keySet();
585          }
586          @Override public List<String> order(List<String> insertionOrder) {
587            Collections.sort(insertionOrder);
588            return insertionOrder;
589          }
590        })
591        .named("TreeBasedTable.column.keySet")
592        .withFeatures(COLLECTION_FEATURES_REMOVE_ORDER)
593        .suppressing(getIteratorKnownOrderRemoveSupportedMethod())
594        .createTestSuite());
595
596    suite.addTest(SetTestSuiteBuilder.using(new TestStringSetGenerator() {
597          @Override protected Set<String> create(String[] elements) {
598            Table<String, Integer, Character> table = HashBasedTable.create();
599            populateForRowKeySet(table, elements);
600            return Tables.transformValues(table, Functions.toStringFunction()).column(1).keySet();
601          }
602        })
603        .named("TransformValues.column.keySet")
604        .withFeatures(COLLECTION_FEATURES_REMOVE)
605        .suppressing(getIteratorUnknownOrderRemoveSupportedMethod())
606    .createTestSuite());
607
608    suite.addTest(SetTestSuiteBuilder.using(new TestStringSetGenerator() {
609          @Override protected Set<String> create(String[] elements) {
610            Table<String, Integer, Character> table = HashBasedTable.create();
611            populateForRowKeySet(table, elements);
612            return Tables.unmodifiableTable(table).column(1).keySet();
613          }
614        })
615        .named("unmodifiableTable[HashBasedTable].column.keySet")
616        .withFeatures(COLLECTION_FEATURES)
617        .suppressing(getIteratorUnknownOrderRemoveSupportedMethod())
618    .createTestSuite());
619
620    suite.addTest(SetTestSuiteBuilder.using(new TestStringSetGenerator() {
621          @Override protected Set<String> create(String[] elements) {
622            RowSortedTable<String, Integer, Character> table = TreeBasedTable.create();
623            populateForRowKeySet(table, elements);
624            return Tables.unmodifiableRowSortedTable(table).column(1).keySet();
625          }
626          @Override public List<String> order(List<String> insertionOrder) {
627            Collections.sort(insertionOrder);
628            return insertionOrder;
629          }
630        })
631        .named("unmodifiableRowSortedTable[TreeBasedTable].column.keySet")
632        .withFeatures(COLLECTION_FEATURES_ORDER)
633        .suppressing(getIteratorKnownOrderRemoveSupportedMethod())
634        .createTestSuite());
635
636    return suite;
637  }
638
639  private static void populateForRowKeySet(
640      Table<String, Integer, Character> table, String[] elements) {
641    for (String row : elements) {
642      table.put(row, 1, 'a');
643      table.put(row, 2, 'b');
644    }
645  }
646
647  private static void populateForColumnKeySet(
648      Table<Integer, String, Character> table, String[] elements) {
649    for (String column : elements) {
650      table.put(1, column, 'a');
651      table.put(2, column, 'b');
652    }
653  }
654
655  private static void populateForValues(
656      Table<Integer, Character, String> table, String[] elements) {
657    for (int i = 0; i < elements.length; i++) {
658      table.put(i, 'a', elements[i]);
659    }
660  }
661
662  private static abstract class TestCellSetGenerator
663      implements TestSetGenerator<Cell<String, Integer, Character>> {
664    @Override
665    public SampleElements<Cell<String, Integer, Character>> samples() {
666      return new SampleElements<Cell<String, Integer, Character>>(
667          Tables.immutableCell("bar", 1, 'a'),
668          Tables.immutableCell("bar", 2, 'b'),
669          Tables.immutableCell("foo", 3, 'c'),
670          Tables.immutableCell("bar", 1, 'b'),
671          Tables.immutableCell("cat", 2, 'b'));
672    }
673
674    @Override
675    public Set<Cell<String, Integer, Character>> create(
676        Object... elements) {
677      Table<String, Integer, Character> table = createTable();
678      for (Object element : elements) {
679        @SuppressWarnings("unchecked")
680        Cell<String, Integer, Character> cell
681            = (Cell<String, Integer, Character>) element;
682        table.put(cell.getRowKey(), cell.getColumnKey(), cell.getValue());
683      }
684      return table.cellSet();
685    }
686
687    abstract Table<String, Integer, Character> createTable();
688
689    @Override
690    @SuppressWarnings("unchecked")
691    public Cell<String, Integer, Character>[] createArray(int length) {
692      return (Cell<String, Integer, Character>[]) new Cell<?, ?, ?>[length];
693    }
694
695    @Override
696    public List<Cell<String, Integer, Character>> order(
697        List<Cell<String, Integer, Character>> insertionOrder) {
698      return insertionOrder;
699    }
700  }
701
702  private static abstract class MapTests
703      extends MapInterfaceTest<String, Integer> {
704
705    MapTests(boolean allowsNullValues, boolean supportsPut, boolean supportsRemove,
706        boolean supportsClear, boolean supportsIteratorRemove) {
707      super(false, allowsNullValues, supportsPut, supportsRemove, supportsClear,
708          supportsIteratorRemove);
709    }
710
711    @Override protected String getKeyNotInPopulatedMap() {
712      return "four";
713    }
714
715    @Override protected Integer getValueNotInPopulatedMap() {
716      return 4;
717    }
718  }
719
720  private static abstract class RowTests extends MapTests {
721    RowTests(boolean allowsNullValues, boolean supportsPut, boolean supportsRemove,
722        boolean supportsClear, boolean supportsIteratorRemove) {
723      super(allowsNullValues, supportsPut, supportsRemove, supportsClear,
724          supportsIteratorRemove);
725    }
726
727    abstract Table<Character, String, Integer> makeTable();
728
729    @Override protected Map<String, Integer> makeEmptyMap() {
730      return makeTable().row('a');
731    }
732
733    @Override protected Map<String, Integer> makePopulatedMap() {
734      Table<Character, String, Integer> table = makeTable();
735      table.put('a', "one", 1);
736      table.put('a', "two", 2);
737      table.put('a', "three", 3);
738      table.put('b', "four", 4);
739      return table.row('a');
740    }
741  }
742
743  @GwtIncompatible("TODO(hhchan): ArrayTable")
744  public static class ArrayRowTests extends RowTests {
745    public ArrayRowTests() {
746      super(true, true, false, false, false);
747    }
748
749    @Override protected String getKeyNotInPopulatedMap() {
750      throw new UnsupportedOperationException();
751    }
752
753    @Override protected Map<String, Integer> makeEmptyMap() {
754      throw new UnsupportedOperationException();
755    }
756
757    @Override protected Table<Character, String, Integer> makeTable() {
758      return ArrayTable.create(Arrays.asList('a', 'b', 'c'),
759          Arrays.asList("one", "two", "three", "four"));
760    }
761  }
762
763  public static class HashRowTests extends RowTests {
764    public HashRowTests() {
765      super(false, true, true, true, true);
766    }
767
768    @Override Table<Character, String, Integer> makeTable() {
769      return HashBasedTable.create();
770    }
771  }
772
773  public static class TreeRowTests extends RowTests {
774    public TreeRowTests() {
775      super(false, true, true, true, true);
776    }
777
778    @Override Table<Character, String, Integer> makeTable() {
779      return TreeBasedTable.create();
780    }
781  }
782
783  public static class TransposeRowTests extends RowTests {
784    public TransposeRowTests() {
785      super(false, true, true, true, false);
786    }
787
788    @Override Table<Character, String, Integer> makeTable() {
789      Table<String, Character, Integer> original = TreeBasedTable.create();
790      return Tables.transpose(original);
791    }
792  }
793
794  private static final Function<Integer, Integer> DIVIDE_BY_2
795      = new Function<Integer, Integer>() {
796        @Override public Integer apply(Integer input) {
797          return (input == null) ? null : input / 2;
798        }
799  };
800
801  public static class TransformValueRowTests extends RowTests {
802    public TransformValueRowTests() {
803      super(false, false, true, true, true);
804    }
805
806    @Override Table<Character, String, Integer> makeTable() {
807      Table<Character, String, Integer> table = HashBasedTable.create();
808      return Tables.transformValues(table, DIVIDE_BY_2);
809    }
810
811    @Override protected Map<String, Integer> makePopulatedMap() {
812      Table<Character, String, Integer> table = HashBasedTable.create();
813      table.put('a', "one", 2);
814      table.put('a', "two", 4);
815      table.put('a', "three", 6);
816      table.put('b', "four", 8);
817      return Tables.transformValues(table, DIVIDE_BY_2).row('a');
818    }
819  }
820
821  public static class UnmodifiableHashRowTests extends RowTests {
822    public UnmodifiableHashRowTests() {
823      super(false, false, false, false, false);
824    }
825
826    @Override Table<Character, String, Integer> makeTable() {
827      Table<Character, String, Integer> table = HashBasedTable.create();
828      return Tables.unmodifiableTable(table);
829    }
830
831    @Override protected Map<String, Integer> makePopulatedMap() {
832      Table<Character, String, Integer> table = HashBasedTable.create();
833      table.put('a', "one", 1);
834      table.put('a', "two", 2);
835      table.put('a', "three", 3);
836      table.put('b', "four", 4);
837      return Tables.unmodifiableTable(table).row('a');
838    }
839  }
840
841  public static class UnmodifiableTreeRowTests extends RowTests {
842    public UnmodifiableTreeRowTests() {
843      super(false, false, false, false, false);
844    }
845
846    @Override Table<Character, String, Integer> makeTable() {
847      RowSortedTable<Character, String, Integer> table = TreeBasedTable.create();
848      return Tables.unmodifiableRowSortedTable(table);
849    }
850
851    @Override protected Map<String, Integer> makePopulatedMap() {
852      RowSortedTable<Character, String, Integer> table = TreeBasedTable.create();
853      table.put('a', "one", 1);
854      table.put('a', "two", 2);
855      table.put('a', "three", 3);
856      table.put('b', "four", 4);
857      return Tables.unmodifiableRowSortedTable(table).row('a');
858    }
859  }
860
861  private static abstract class ColumnTests extends MapTests {
862    ColumnTests(boolean allowsNullValues, boolean supportsPut, boolean supportsRemove,
863        boolean supportsClear, boolean supportsIteratorRemove) {
864      super(allowsNullValues, supportsPut, supportsRemove, supportsClear,
865          supportsIteratorRemove);
866    }
867
868    abstract Table<String, Character, Integer> makeTable();
869
870    @Override protected Map<String, Integer> makeEmptyMap() {
871      return makeTable().column('a');
872    }
873
874    @Override protected Map<String, Integer> makePopulatedMap() {
875      Table<String, Character, Integer> table = makeTable();
876      table.put("one", 'a', 1);
877      table.put("two", 'a', 2);
878      table.put("three", 'a', 3);
879      table.put("four", 'b', 4);
880      return table.column('a');
881    }
882  }
883
884  @GwtIncompatible("TODO(hhchan): ArrayTable")
885  public static class ArrayColumnTests extends ColumnTests {
886    public ArrayColumnTests() {
887      super(true, true, false, false, false);
888    }
889
890    @Override protected String getKeyNotInPopulatedMap() {
891      throw new UnsupportedOperationException();
892    }
893
894    @Override protected Map<String, Integer> makeEmptyMap() {
895      throw new UnsupportedOperationException();
896    }
897
898    @Override Table<String, Character, Integer> makeTable() {
899      return ArrayTable.create(Arrays.asList("one", "two", "three", "four"),
900          Arrays.asList('a', 'b', 'c'));
901    }
902  }
903
904  public static class HashColumnTests extends ColumnTests {
905    public HashColumnTests() {
906      super(false, true, true, true, false);
907    }
908
909    @Override Table<String, Character, Integer> makeTable() {
910      return HashBasedTable.create();
911    }
912  }
913
914  public static class TreeColumnTests extends ColumnTests {
915    public TreeColumnTests() {
916      super(false, true, true, true, false);
917    }
918
919    @Override Table<String, Character, Integer> makeTable() {
920      return TreeBasedTable.create();
921    }
922  }
923
924  public static class TransposeColumnTests extends ColumnTests {
925    public TransposeColumnTests() {
926      super(false, true, true, true, true);
927    }
928
929    @Override Table<String, Character, Integer> makeTable() {
930      Table<Character, String, Integer> original = TreeBasedTable.create();
931      return Tables.transpose(original);
932    }
933  }
934
935  public static class TransformValueColumnTests extends ColumnTests {
936    public TransformValueColumnTests() {
937      super(false, false, true, true, false);
938    }
939
940    @Override Table<String, Character, Integer> makeTable() {
941      Table<String, Character, Integer> table = HashBasedTable.create();
942      return Tables.transformValues(table, DIVIDE_BY_2);
943    }
944
945    @Override protected Map<String, Integer> makePopulatedMap() {
946      Table<String, Character, Integer> table = HashBasedTable.create();
947      table.put("one", 'a', 1);
948      table.put("two", 'a', 2);
949      table.put("three", 'a', 3);
950      table.put("four", 'b', 4);
951      return Tables.transformValues(table, DIVIDE_BY_2).column('a');
952    }
953  }
954
955  public static class UnmodifiableHashColumnTests extends ColumnTests {
956    public UnmodifiableHashColumnTests() {
957      super(false, false, false, false, false);
958    }
959
960    @Override Table<String, Character, Integer> makeTable() {
961      Table<String, Character, Integer> table = HashBasedTable.create();
962      return Tables.unmodifiableTable(table);
963    }
964
965    @Override protected Map<String, Integer> makePopulatedMap() {
966      Table<String, Character, Integer> table = HashBasedTable.create();
967      table.put("one", 'a', 1);
968      table.put("two", 'a', 2);
969      table.put("three", 'a', 3);
970      table.put("four", 'b', 4);
971      return Tables.unmodifiableTable(table).column('a');
972    }
973  }
974
975  public static class UnmodifiableTreeColumnTests extends ColumnTests {
976    public UnmodifiableTreeColumnTests() {
977      super(false, false, false, false, false);
978    }
979
980    @Override Table<String, Character, Integer> makeTable() {
981      RowSortedTable<String, Character, Integer> table = TreeBasedTable.create();
982      return Tables.unmodifiableRowSortedTable(table);
983    }
984
985    @Override protected Map<String, Integer> makePopulatedMap() {
986      RowSortedTable<String, Character, Integer> table = TreeBasedTable.create();
987      table.put("one", 'a', 1);
988      table.put("two", 'a', 2);
989      table.put("three", 'a', 3);
990      table.put("four", 'b', 4);
991      return Tables.unmodifiableRowSortedTable(table).column('a');
992    }
993  }
994
995  private static abstract class MapMapTests
996      extends MapInterfaceTest<String, Map<Integer, Character>> {
997
998    MapMapTests(boolean allowsNullValues, boolean supportsRemove,
999        boolean supportsClear, boolean supportsIteratorRemove) {
1000      super(false, allowsNullValues, false, supportsRemove, supportsClear,
1001          supportsIteratorRemove);
1002    }
1003
1004    @Override protected String getKeyNotInPopulatedMap() {
1005      return "cat";
1006    }
1007
1008    @Override protected Map<Integer, Character> getValueNotInPopulatedMap() {
1009      return ImmutableMap.of();
1010    }
1011
1012    /**
1013     * The version of this test supplied by {@link MapInterfaceTest} fails for
1014     * this particular map implementation, because {@code map.get()} returns a
1015     * view collection that changes in the course of a call to {@code remove()}.
1016     * Thus, the expectation doesn't hold that {@code map.remove(x)} returns the
1017     * same value which {@code map.get(x)} did immediately beforehand.
1018     */
1019    @Override public void testRemove() {
1020      final Map<String, Map<Integer, Character>> map;
1021      final String keyToRemove;
1022      try {
1023        map = makePopulatedMap();
1024      } catch (UnsupportedOperationException e) {
1025        return;
1026      }
1027      keyToRemove = map.keySet().iterator().next();
1028      if (supportsRemove) {
1029        int initialSize = map.size();
1030        map.get(keyToRemove);
1031        map.remove(keyToRemove);
1032        // This line doesn't hold - see the Javadoc comments above.
1033        // assertEquals(expectedValue, oldValue);
1034        assertFalse(map.containsKey(keyToRemove));
1035        assertEquals(initialSize - 1, map.size());
1036      } else {
1037        try {
1038          map.remove(keyToRemove);
1039          fail("Expected UnsupportedOperationException.");
1040        } catch (UnsupportedOperationException e) {
1041          // Expected.
1042        }
1043      }
1044      assertInvariants(map);
1045    }
1046  }
1047
1048  private static abstract class RowMapTests extends MapMapTests {
1049    RowMapTests(boolean allowsNullValues, boolean supportsRemove,
1050        boolean supportsClear, boolean supportsIteratorRemove) {
1051      super(allowsNullValues, supportsRemove, supportsClear,
1052          supportsIteratorRemove);
1053    }
1054
1055    abstract Table<String, Integer, Character> makeTable();
1056
1057    @Override protected Map<String, Map<Integer, Character>>
1058        makePopulatedMap() {
1059      Table<String, Integer, Character> table = makeTable();
1060      populateTable(table);
1061      return table.rowMap();
1062    }
1063
1064    void populateTable(Table<String, Integer, Character> table) {
1065      table.put("foo", 1, 'a');
1066      table.put("bar", 1, 'b');
1067      table.put("foo", 3, 'c');
1068    }
1069
1070    @Override protected Map<String, Map<Integer, Character>> makeEmptyMap() {
1071      return makeTable().rowMap();
1072    }
1073  }
1074
1075  @GwtIncompatible("TODO(hhchan): ArrayTable")
1076  public static class ArrayRowMapTests extends RowMapTests {
1077    public ArrayRowMapTests() {
1078      super(true, false, false, false);
1079    }
1080
1081    @Override Table<String, Integer, Character> makeTable() {
1082      return ArrayTable.create(Arrays.asList("foo", "bar", "dog"),
1083          Arrays.asList(1, 2, 3));
1084    }
1085
1086    @Override protected Map<String, Map<Integer, Character>> makeEmptyMap() {
1087      throw new UnsupportedOperationException();
1088    }
1089  }
1090
1091  public static class HashRowMapTests extends RowMapTests {
1092    public HashRowMapTests() {
1093      super(false, true, true, true);
1094    }
1095
1096    @Override Table<String, Integer, Character> makeTable() {
1097      return HashBasedTable.create();
1098    }
1099  }
1100
1101  public static class TreeRowMapTests extends RowMapTests {
1102    public TreeRowMapTests() {
1103      super(false, true, true, true);
1104    }
1105
1106    @Override Table<String, Integer, Character> makeTable() {
1107      return TreeBasedTable.create();
1108    }
1109  }
1110
1111  public static class TreeRowMapHeadMapTests extends RowMapTests {
1112    public TreeRowMapHeadMapTests() {
1113      super(false, true, true, true);
1114    }
1115
1116    @Override TreeBasedTable<String, Integer, Character> makeTable() {
1117      TreeBasedTable<String, Integer, Character> table =
1118          TreeBasedTable.create();
1119      table.put("z", 1, 'a');
1120      return table;
1121    }
1122
1123    @Override protected Map<String, Map<Integer, Character>>
1124        makePopulatedMap() {
1125      TreeBasedTable<String, Integer, Character> table = makeTable();
1126      populateTable(table);
1127      return table.rowMap().headMap("x");
1128    }
1129
1130    @Override protected Map<String, Map<Integer, Character>> makeEmptyMap() {
1131      return makeTable().rowMap().headMap("x");
1132    }
1133
1134    @Override protected String getKeyNotInPopulatedMap() {
1135      return "z";
1136    }
1137  }
1138
1139  public static class TreeRowMapTailMapTests extends RowMapTests {
1140    public TreeRowMapTailMapTests() {
1141      super(false, true, true, true);
1142    }
1143
1144    @Override TreeBasedTable<String, Integer, Character> makeTable() {
1145      TreeBasedTable<String, Integer, Character> table =
1146          TreeBasedTable.create();
1147      table.put("a", 1, 'a');
1148      return table;
1149    }
1150
1151    @Override protected Map<String, Map<Integer, Character>>
1152        makePopulatedMap() {
1153      TreeBasedTable<String, Integer, Character> table = makeTable();
1154      populateTable(table);
1155      return table.rowMap().tailMap("b");
1156    }
1157
1158    @Override protected Map<String, Map<Integer, Character>> makeEmptyMap() {
1159      return makeTable().rowMap().tailMap("b");
1160    }
1161
1162    @Override protected String getKeyNotInPopulatedMap() {
1163      return "a";
1164    }
1165  }
1166
1167  public static class TreeRowMapSubMapTests extends RowMapTests {
1168    public TreeRowMapSubMapTests() {
1169      super(false, true, true, true);
1170    }
1171
1172    @Override TreeBasedTable<String, Integer, Character> makeTable() {
1173      TreeBasedTable<String, Integer, Character> table =
1174          TreeBasedTable.create();
1175      table.put("a", 1, 'a');
1176      table.put("z", 1, 'a');
1177      return table;
1178    }
1179
1180    @Override protected Map<String, Map<Integer, Character>>
1181        makePopulatedMap() {
1182      TreeBasedTable<String, Integer, Character> table = makeTable();
1183      populateTable(table);
1184      return table.rowMap().subMap("b", "x");
1185    }
1186
1187    @Override protected Map<String, Map<Integer, Character>> makeEmptyMap() {
1188      return makeTable().rowMap().subMap("b", "x");
1189    }
1190
1191    @Override protected String getKeyNotInPopulatedMap() {
1192      return "z";
1193    }
1194  }
1195
1196  private static final Function<String, Character> FIRST_CHARACTER =
1197      new Function<String, Character>() {
1198        @Override
1199        public Character apply(String input) {
1200          return input == null ? null : input.charAt(0);
1201        }
1202      };
1203
1204  public static class TransformValueRowMapTests extends RowMapTests {
1205    public TransformValueRowMapTests() {
1206      super(false, true, true, true);
1207    }
1208
1209    @Override Table<String, Integer, Character> makeTable() {
1210      Table<String, Integer, String> original = HashBasedTable.create();
1211      return Tables.transformValues(original, FIRST_CHARACTER);
1212    }
1213
1214    @Override
1215    protected Map<String, Map<Integer, Character>> makePopulatedMap() {
1216      Table<String, Integer, String> table = HashBasedTable.create();
1217      table.put("foo", 1, "apple");
1218      table.put("bar", 1, "banana");
1219      table.put("foo", 3, "cat");
1220      return Tables.transformValues(table, FIRST_CHARACTER).rowMap();
1221    }
1222  }
1223
1224  public static class UnmodifiableHashRowMapTests extends RowMapTests {
1225    public UnmodifiableHashRowMapTests() {
1226      super(false, false, false, false);
1227    }
1228
1229    @Override Table<String, Integer, Character> makeTable() {
1230      Table<String, Integer, Character> original = HashBasedTable.create();
1231      return Tables.unmodifiableTable(original);
1232    }
1233
1234    @Override
1235    protected Map<String, Map<Integer, Character>> makePopulatedMap() {
1236      Table<String, Integer, Character> table = HashBasedTable.create();
1237      table.put("foo", 1, 'a');
1238      table.put("bar", 1, 'b');
1239      table.put("foo", 3, 'c');
1240      return Tables.unmodifiableTable(table).rowMap();
1241    }
1242  }
1243
1244  public static class UnmodifiableTreeRowMapTests extends RowMapTests {
1245    public UnmodifiableTreeRowMapTests() {
1246      super(false, false, false, false);
1247    }
1248
1249    @Override RowSortedTable<String, Integer, Character> makeTable() {
1250      RowSortedTable<String, Integer, Character> original = TreeBasedTable.create();
1251      return Tables.unmodifiableRowSortedTable(original);
1252    }
1253
1254    @Override
1255    protected SortedMap<String, Map<Integer, Character>> makePopulatedMap() {
1256      RowSortedTable<String, Integer, Character> table = TreeBasedTable.create();
1257      table.put("foo", 1, 'a');
1258      table.put("bar", 1, 'b');
1259      table.put("foo", 3, 'c');
1260      return Tables.unmodifiableRowSortedTable(table).rowMap();
1261    }
1262  }
1263
1264  private static abstract class ColumnMapTests extends MapMapTests {
1265    ColumnMapTests(boolean allowsNullValues, boolean supportsRemove,
1266        boolean supportsClear, boolean supportsIteratorRemove) {
1267      super(allowsNullValues, supportsRemove, supportsClear,
1268          supportsIteratorRemove);
1269    }
1270
1271    abstract Table<Integer, String, Character> makeTable();
1272
1273    @Override protected Map<String, Map<Integer, Character>>
1274        makePopulatedMap() {
1275      Table<Integer, String, Character> table = makeTable();
1276      table.put(1, "foo", 'a');
1277      table.put(1, "bar", 'b');
1278      table.put(3, "foo", 'c');
1279      return table.columnMap();
1280    }
1281
1282    @Override protected Map<String, Map<Integer, Character>> makeEmptyMap() {
1283      return makeTable().columnMap();
1284    }
1285  }
1286
1287  @GwtIncompatible("TODO(hhchan): ArrayTable")
1288  public static class ArrayColumnMapTests extends ColumnMapTests {
1289    public ArrayColumnMapTests() {
1290      super(true, false, false, false);
1291    }
1292
1293    @Override Table<Integer, String, Character> makeTable() {
1294      return ArrayTable.create(Arrays.asList(1, 2, 3),
1295          Arrays.asList("foo", "bar", "dog"));
1296    }
1297
1298    @Override protected Map<String, Map<Integer, Character>> makeEmptyMap() {
1299      throw new UnsupportedOperationException();
1300    }
1301  }
1302
1303  public static class HashColumnMapTests extends ColumnMapTests {
1304    public HashColumnMapTests() {
1305      super(false, true, true, false);
1306    }
1307
1308    @Override Table<Integer, String, Character> makeTable() {
1309      return HashBasedTable.create();
1310    }
1311  }
1312
1313  public static class TreeColumnMapTests extends ColumnMapTests {
1314    public TreeColumnMapTests() {
1315      super(false, true, true, false);
1316    }
1317
1318    @Override Table<Integer, String, Character> makeTable() {
1319      return TreeBasedTable.create();
1320    }
1321  }
1322
1323  public static class TransformValueColumnMapTests extends ColumnMapTests {
1324    public TransformValueColumnMapTests() {
1325      super(false, true, true, false);
1326    }
1327
1328    @Override Table<Integer, String, Character> makeTable() {
1329      Table<Integer, String, String> original = HashBasedTable.create();
1330      return Tables.transformValues(original, FIRST_CHARACTER);
1331    }
1332
1333    @Override
1334    protected Map<String, Map<Integer, Character>> makePopulatedMap() {
1335      Table<Integer, String, String> table = HashBasedTable.create();
1336      table.put(1, "foo", "apple");
1337      table.put(1, "bar", "banana");
1338      table.put(3, "foo", "cat");
1339      return Tables.transformValues(table, FIRST_CHARACTER).columnMap();
1340    }
1341  }
1342
1343  public static class UnmodifiableHashColumnMapTests extends ColumnMapTests {
1344    public UnmodifiableHashColumnMapTests() {
1345      super(false, false, false, false);
1346    }
1347
1348    @Override Table<Integer, String, Character> makeTable() {
1349      Table<Integer, String, Character> original = HashBasedTable.create();
1350      return Tables.unmodifiableTable(original);
1351    }
1352
1353    @Override
1354    protected Map<String, Map<Integer, Character>> makePopulatedMap() {
1355      Table<Integer, String, Character> table = HashBasedTable.create();
1356      table.put(1, "foo", 'a');
1357      table.put(1, "bar", 'b');
1358      table.put(3, "foo", 'c');
1359      return Tables.unmodifiableTable(table).columnMap();
1360    }
1361  }
1362
1363  public static class UnmodifiableTreeColumnMapTests extends ColumnMapTests {
1364    public UnmodifiableTreeColumnMapTests() {
1365      super(false, false, false, false);
1366    }
1367
1368    @Override Table<Integer, String, Character> makeTable() {
1369      RowSortedTable<Integer, String, Character> original = TreeBasedTable.create();
1370      return Tables.unmodifiableRowSortedTable(original);
1371    }
1372
1373    @Override
1374    protected Map<String, Map<Integer, Character>> makePopulatedMap() {
1375      RowSortedTable<Integer, String, Character> table = TreeBasedTable.create();
1376      table.put(1, "foo", 'a');
1377      table.put(1, "bar", 'b');
1378      table.put(3, "foo", 'c');
1379      return Tables.unmodifiableRowSortedTable(table).columnMap();
1380    }
1381  }
1382}
1383