| // 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; |
| } |
| } |
| } |