// Copyright 2014 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.skyframe; import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.fail; import com.google.common.collect.ImmutableList; import com.google.common.collect.Interner; import com.google.devtools.build.lib.concurrent.BlazeInterners; import java.util.ArrayList; import java.util.Collections; import java.util.List; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; import org.junit.runners.Parameterized.Parameters; /** Test for {@code ReverseDepsUtility}. */ @RunWith(Parameterized.class) public class ReverseDepsUtilityTest { private final int numElements; @Parameters(name = "numElements-{0}") public static List parameters() { List params = new ArrayList<>(); for (int i = 0; i < 20; i++) { params.add(new Object[] {i}); } return params; } public ReverseDepsUtilityTest(int numElements) { this.numElements = numElements; } @Test public void testAddAndRemove() { for (int numRemovals = 0; numRemovals <= numElements; numRemovals++) { InMemoryNodeEntry example = new InMemoryNodeEntry(); for (int j = 0; j < numElements; j++) { ReverseDepsUtility.addReverseDeps(example, Collections.singleton(Key.create(j))); } // Not a big test but at least check that it does not blow up. assertThat(ReverseDepsUtility.toString(example)).isNotEmpty(); assertThat(ReverseDepsUtility.getReverseDeps(example)).hasSize(numElements); for (int i = 0; i < numRemovals; i++) { ReverseDepsUtility.removeReverseDep(example, Key.create(i)); } assertThat(ReverseDepsUtility.getReverseDeps(example)).hasSize(numElements - numRemovals); assertThat(example.getReverseDepsDataToConsolidateForReverseDepsUtil()).isNull(); } } // Same as testAdditionAndRemoval but we add all the reverse deps in one call. @Test public void testAddAllAndRemove() { for (int numRemovals = 0; numRemovals <= numElements; numRemovals++) { InMemoryNodeEntry example = new InMemoryNodeEntry(); List toAdd = new ArrayList<>(); for (int j = 0; j < numElements; j++) { toAdd.add(Key.create(j)); } ReverseDepsUtility.addReverseDeps(example, toAdd); assertThat(ReverseDepsUtility.getReverseDeps(example)).hasSize(numElements); for (int i = 0; i < numRemovals; i++) { ReverseDepsUtility.removeReverseDep(example, Key.create(i)); } assertThat(ReverseDepsUtility.getReverseDeps(example)).hasSize(numElements - numRemovals); assertThat(example.getReverseDepsDataToConsolidateForReverseDepsUtil()).isNull(); } } @Test public void testDuplicateCheckOnGetReverseDeps() { InMemoryNodeEntry example = new InMemoryNodeEntry(); for (int i = 0; i < numElements; i++) { ReverseDepsUtility.addReverseDeps(example, Collections.singleton(Key.create(i))); } // Should only fail when we call getReverseDeps(). ReverseDepsUtility.addReverseDeps(example, Collections.singleton(Key.create(0))); try { ReverseDepsUtility.getReverseDeps(example); assertThat(numElements).isEqualTo(0); } catch (Exception expected) { } } @Test public void doubleAddThenRemove() { InMemoryNodeEntry example = new InMemoryNodeEntry(); SkyKey key = Key.create(0); ReverseDepsUtility.addReverseDeps(example, Collections.singleton(key)); // Should only fail when we call getReverseDeps(). ReverseDepsUtility.addReverseDeps(example, Collections.singleton(key)); ReverseDepsUtility.removeReverseDep(example, key); try { ReverseDepsUtility.getReverseDeps(example); fail(); } catch (IllegalStateException expected) { } } @Test public void doubleAddThenRemoveCheckedOnSize() { InMemoryNodeEntry example = new InMemoryNodeEntry(); SkyKey fixedKey = Key.create(0); SkyKey key = Key.create(1); ReverseDepsUtility.addReverseDeps(example, ImmutableList.of(fixedKey, key)); // Should only fail when we reach the limit. ReverseDepsUtility.addReverseDeps(example, Collections.singleton(key)); ReverseDepsUtility.removeReverseDep(example, key); ReverseDepsUtility.checkReverseDep(example, fixedKey); try { ReverseDepsUtility.checkReverseDep(example, fixedKey); fail(); } catch (IllegalStateException expected) { } } @Test public void addRemoveAdd() { InMemoryNodeEntry example = new InMemoryNodeEntry(); SkyKey fixedKey = Key.create(0); SkyKey key = Key.create(1); ReverseDepsUtility.addReverseDeps(example, ImmutableList.of(fixedKey, key)); ReverseDepsUtility.removeReverseDep(example, key); ReverseDepsUtility.addReverseDeps(example, Collections.singleton(key)); assertThat(ReverseDepsUtility.getReverseDeps(example)).containsExactly(fixedKey, key); } @Test public void testMaybeCheck() { InMemoryNodeEntry example = new InMemoryNodeEntry(); for (int i = 0; i < numElements; i++) { ReverseDepsUtility.addReverseDeps(example, Collections.singleton(Key.create(i))); // This should always succeed, since the next element is still not present. ReverseDepsUtility.maybeCheckReverseDepNotPresent(example, Key.create(i + 1)); } try { ReverseDepsUtility.maybeCheckReverseDepNotPresent(example, Key.create(0)); // Should only fail if empty or above the checking threshold. assertThat(numElements == 0 || numElements >= ReverseDepsUtility.MAYBE_CHECK_THRESHOLD) .isTrue(); } catch (Exception expected) { } } private static class Key extends AbstractSkyKey { private static final Interner interner = BlazeInterners.newWeakInterner(); private Key(Integer arg) { super(arg); } private static Key create(Integer arg) { return interner.intern(new Key(arg)); } @Override public SkyFunctionName functionName() { return SkyFunctionName.FOR_TESTING; } } }