| // Copyright 2018 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.actions; | |
| import static com.google.common.truth.Truth.assertThat; | |
| import static java.nio.charset.StandardCharsets.US_ASCII; | |
| import com.google.devtools.build.lib.vfs.Path; | |
| import com.google.devtools.build.lib.vfs.PathFragment; | |
| import java.util.ArrayList; | |
| import java.util.Collections; | |
| import java.util.HashSet; | |
| import java.util.Random; | |
| import org.junit.Before; | |
| import org.junit.Test; | |
| import org.junit.runner.RunWith; | |
| import org.junit.runners.JUnit4; | |
| /** Unit test for {@link ActionInputMap}. */ | |
| @RunWith(JUnit4.class) | |
| public final class ActionInputMapTest { | |
| private ActionInputMap map; | |
| @Before | |
| public void init() { | |
| map = new ActionInputMap(1); // small hint to stress the map | |
| } | |
| @Test | |
| public void basicPutAndLookup() { | |
| assertThat(put("/abc/def", 5)).isTrue(); | |
| assertThat(map.size()).isEqualTo(1); | |
| assertContains("/abc/def", 5); | |
| assertThat(map.getMetadata("blah")).isNull(); | |
| assertThat(map.getInput("blah")).isNull(); | |
| } | |
| @Test | |
| public void ignoresSubsequent() { | |
| assertThat(put("/abc/def", 5)).isTrue(); | |
| assertThat(map.size()).isEqualTo(1); | |
| assertThat(put("/abc/def", 6)).isFalse(); | |
| assertThat(map.size()).isEqualTo(1); | |
| assertThat(put("/ghi/jkl", 7)).isTrue(); | |
| assertThat(map.size()).isEqualTo(2); | |
| assertThat(put("/ghi/jkl", 8)).isFalse(); | |
| assertThat(map.size()).isEqualTo(2); | |
| assertContains("/abc/def", 5); | |
| assertContains("/ghi/jkl", 7); | |
| } | |
| @Test | |
| public void clear() { | |
| assertThat(put("/abc/def", 5)).isTrue(); | |
| assertThat(map.size()).isEqualTo(1); | |
| assertThat(put("/ghi/jkl", 7)).isTrue(); | |
| assertThat(map.size()).isEqualTo(2); | |
| map.clear(); | |
| assertThat(map.size()).isEqualTo(0); | |
| assertThat(map.getMetadata("/abc/def")).isNull(); | |
| assertThat(map.getMetadata("/ghi/jkl")).isNull(); | |
| } | |
| @Test | |
| public void stress() { | |
| ArrayList<TestEntry> data = new ArrayList<>(); | |
| { | |
| Random rng = new Random(); | |
| HashSet<TestInput> deduper = new HashSet<>(); | |
| for (int i = 0; i < 100000; ++i) { | |
| byte[] bytes = new byte[80]; | |
| rng.nextBytes(bytes); | |
| for (int j = 0; j < bytes.length; ++j) { | |
| bytes[j] &= ((byte) 0x7f); | |
| } | |
| TestInput nextInput = new TestInput(new String(bytes, US_ASCII)); | |
| if (deduper.add(nextInput)) { | |
| data.add(new TestEntry(nextInput, new TestMetadata(i))); | |
| } | |
| } | |
| } | |
| for (int iteration = 0; iteration < 20; ++iteration) { | |
| map.clear(); | |
| Collections.shuffle(data); | |
| for (int i = 0; i < data.size(); ++i) { | |
| TestEntry entry = data.get(i); | |
| assertThat(map.putWithNoDepOwner(entry.input, entry.metadata)).isTrue(); | |
| } | |
| assertThat(map.size()).isEqualTo(data.size()); | |
| for (int i = 0; i < data.size(); ++i) { | |
| TestEntry entry = data.get(i); | |
| assertThat(map.getMetadata(entry.input)).isEqualTo(entry.metadata); | |
| } | |
| } | |
| } | |
| private boolean put(String execPath, int value) { | |
| return map.putWithNoDepOwner(new TestInput(execPath), new TestMetadata(value)); | |
| } | |
| private void assertContains(String execPath, int value) { | |
| assertThat(map.getMetadata(new TestInput(execPath))).isEqualTo(new TestMetadata(value)); | |
| assertThat(map.getMetadata(execPath)).isEqualTo(new TestMetadata(value)); | |
| assertThat(map.getInput(execPath)).isEqualTo(new TestInput(execPath)); | |
| } | |
| private static class TestEntry { | |
| public final TestInput input; | |
| public final TestMetadata metadata; | |
| public TestEntry(TestInput input, TestMetadata metadata) { | |
| this.input = input; | |
| this.metadata = metadata; | |
| } | |
| } | |
| private static class TestInput implements ActionInput { | |
| private final PathFragment fragment; | |
| public TestInput(String fragment) { | |
| this.fragment = PathFragment.create(fragment); | |
| } | |
| @Override | |
| public boolean isSymlink() { | |
| return false; | |
| } | |
| @Override | |
| public PathFragment getExecPath() { | |
| return fragment; | |
| } | |
| @Override | |
| public String getExecPathString() { | |
| return fragment.toString(); | |
| } | |
| @Override | |
| public boolean equals(Object other) { | |
| if (!(other instanceof TestInput)) { | |
| return false; | |
| } | |
| if (this == other) { | |
| return true; | |
| } | |
| return fragment.equals(((TestInput) other).fragment); | |
| } | |
| @Override | |
| public int hashCode() { | |
| return fragment.hashCode(); | |
| } | |
| } | |
| private static class TestMetadata extends FileArtifactValue { | |
| private final int id; | |
| public TestMetadata(int id) { | |
| this.id = id; | |
| } | |
| @Override | |
| public FileStateType getType() { | |
| throw new UnsupportedOperationException(); | |
| } | |
| @Override | |
| public byte[] getDigest() { | |
| throw new UnsupportedOperationException(); | |
| } | |
| @Override | |
| public long getSize() { | |
| throw new UnsupportedOperationException(); | |
| } | |
| @Override | |
| public long getModifiedTime() { | |
| throw new UnsupportedOperationException(); | |
| } | |
| @Override | |
| public boolean wasModifiedSinceDigest(Path path) { | |
| throw new UnsupportedOperationException(); | |
| } | |
| @Override | |
| public boolean isRemote() { | |
| throw new UnsupportedOperationException(); | |
| } | |
| @Override | |
| public boolean isMarkerValue() { | |
| throw new UnsupportedOperationException(); | |
| } | |
| @Override | |
| public FileContentsProxy getContentsProxy() { | |
| throw new UnsupportedOperationException(); | |
| } | |
| @Override | |
| @SuppressWarnings("EqualsHashCode") | |
| public boolean equals(Object o) { | |
| if (!(o instanceof TestMetadata)) { | |
| return false; | |
| } | |
| return id == ((TestMetadata) o).id; | |
| } | |
| } | |
| } |