// Copyright 2017 The Bazel Authors. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package com.google.devtools.build.lib.collect.nestedset; import static com.google.common.truth.Truth.assertThat; import com.google.common.collect.ImmutableSet; import com.google.devtools.build.lib.testutil.MoreAsserts; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Set; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; /** Tests for {@link com.google.devtools.build.lib.collect.nestedset.NestedSetView}. */ @RunWith(JUnit4.class) public class NestedSetViewTest { @Test public void testIdentifier() { NestedSet inner = NestedSetBuilder.stableOrder().add("a").add("b").build(); NestedSet outer = NestedSetBuilder.stableOrder().addTransitive(inner).add("c").build(); NestedSet flat = NestedSetBuilder.stableOrder().add("a").add("b").add("c").build(); // The identifier should be independent of the view instance. assertThat((new NestedSetView(inner)).identifier()) .isEqualTo((new NestedSetView(inner)).identifier()); // Sets with different internal structure should have different identifiers assertThat((new NestedSetView(flat)).identifier()) .isNotEqualTo((new NestedSetView(outer)).identifier()); // Decomposing a set, the transitive sets should be correctly identified. Set> transitives = (new NestedSetView(outer)).transitives(); assertThat(transitives).hasSize(1); NestedSetView extracted = transitives.iterator().next(); assertThat(extracted.identifier()).isEqualTo((new NestedSetView(inner)).identifier()); } @Test public void testDirects() { NestedSet inner = NestedSetBuilder.stableOrder().add("a").add("b").build(); NestedSet outer = NestedSetBuilder.stableOrder() .add("c") .addTransitive(inner) .add("d") .add("e") .build(); // The direct members should correctly be identified. assertThat((new NestedSetView(outer)).directs()).containsExactly("c", "d", "e"); } @Test public void testTransitives() { // The inner sets have to have at least two elements, as NestedSet may decide to inline // singleton sets; however, we do not want to assert the inlining in the test. NestedSet innerA = NestedSetBuilder.stableOrder().add("a1").add("a2").build(); NestedSet innerB = NestedSetBuilder.stableOrder().add("b1").add("b2").build(); NestedSet innerC = NestedSetBuilder.stableOrder().add("c1").add("c2").build(); NestedSet outer = NestedSetBuilder.stableOrder() .add("x") .add("y") .addTransitive(innerA) .addTransitive(innerB) .addTransitive(innerC) .add("z") .build(); // Decomposing the nested set, should give us the correct set of transitive members. ImmutableSet expected = ImmutableSet.of( (new NestedSetView(innerA)).identifier(), (new NestedSetView(innerB)).identifier(), (new NestedSetView(innerC)).identifier()); ImmutableSet.Builder found = new ImmutableSet.Builder(); for (NestedSetView transitive : (new NestedSetView(outer)).transitives()) { found.add(transitive.identifier()); } assertThat(found.build()).isEqualTo(expected); } /** Naively traverse a view and collect all elements reachable. */ private static Set contents(NestedSetView view) { ImmutableSet.Builder builder = new ImmutableSet.Builder(); builder.addAll(view.directs()); for (NestedSetView transitive : view.transitives()) { builder.addAll(contents(transitive)); } return builder.build(); } private static Set identifiers(Set> sets) { ImmutableSet.Builder builder = new ImmutableSet.Builder(); for (NestedSetView set : sets) { builder.add(set.identifier()); } return builder.build(); } @Test public void testContents() { // Verify that the elements reachable from view are the correct ones, regardless if singletons // are inlined or not. Also verify that sets with at least two elements are never inlined. NestedSet singleA = NestedSetBuilder.stableOrder().add("a").build(); NestedSet singleB = NestedSetBuilder.stableOrder().add("b").build(); NestedSet multi = NestedSetBuilder.stableOrder().add("c1").add("c2").build(); NestedSet outer = NestedSetBuilder.stableOrder() .add("x") .add("y") .addTransitive(multi) .addTransitive(singleA) .addTransitive(singleB) .add("z") .build(); NestedSetView view = new NestedSetView(outer); assertThat(contents(view)).containsExactly("a", "b", "c1", "c2", "x", "y", "z"); assertThat(identifiers(view.transitives())) .contains((new NestedSetView(multi)).identifier()); } @Test public void testSplitFails() { NestedSet a = NestedSetBuilder.stableOrder().add("a").add("b").build(); NestedSetView v = new NestedSetView<>(a); MoreAsserts.assertThrows( IllegalArgumentException.class, () -> v.splitIfExceedsMaximumSize(-100)); MoreAsserts.assertThrows( IllegalArgumentException.class, () -> v.splitIfExceedsMaximumSize(1)); } @Test public void testSplitNoSplit() { NestedSet a = NestedSetBuilder.stableOrder().add("a").add("b").build(); NestedSetView v = new NestedSetView<>(a); assertThat(v.splitIfExceedsMaximumSize(2)).isSameAs(v); assertThat(v.splitIfExceedsMaximumSize(100)).isSameAs(v); } @Test public void testSplit() { NestedSet a = NestedSetBuilder.stableOrder() .addAll(Arrays.asList("a", "b", "c")) .build(); NestedSetView v = new NestedSetView<>(a); NestedSetView s = v.splitIfExceedsMaximumSize(2); assertThat(s).isNotSameAs(v); assertThat(collectCheckSize(s, 2)).containsExactly("a", "b", "c"); } @Test public void testRecursiveSplit() { NestedSet a = NestedSetBuilder.stableOrder() .addAll(Arrays.asList("a", "b", "c", "d", "e")) .build(); NestedSetView v = new NestedSetView<>(a); NestedSetView s = v.splitIfExceedsMaximumSize(2); assertThat(s).isNotSameAs(v); assertThat(collectCheckSize(s, 2)).containsExactly("a", "b", "c", "d", "e"); } private List collectCheckSize(NestedSetView view, int maxSize) { return collectCheckSize(new ArrayList<>(), view, maxSize); } private List collectCheckSize(List result, NestedSetView view, int maxSize) { assertThat(view.directs().size()).isAtMost(maxSize); assertThat(view.transitives().size()).isAtMost(maxSize); for (NestedSetView t : view.transitives()) { collectCheckSize(result, t, maxSize); } result.addAll(view.directs()); return result; } }