/* * Copyright 2014-present Facebook, Inc. * * 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.facebook.buck.testutil.integration; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.is; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.fail; import com.facebook.buck.artifact_cache.CacheResult; import com.facebook.buck.model.BuildTarget; import com.facebook.buck.model.BuildTargetFactory; import com.facebook.buck.rules.BuildRuleStatus; import com.facebook.buck.rules.BuildRuleSuccessType; import com.facebook.buck.util.sha1.Sha1HashCode; import com.google.common.base.MoreObjects; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSortedSet; import java.nio.file.Path; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.regex.Matcher; import java.util.regex.Pattern; public class BuckBuildLog { private static final Pattern BUILD_LOG_FINISHED_RULE_REGEX = Pattern.compile( ".*BuildRuleFinished\\((?<BuildTarget>[^\\)]+)\\): (?<Status>\\S+) " + "(?<CacheResult>\\S+) (?<SuccessType>\\S+) (?<RuleKey>\\S+)" + "(?: I(?<InputRuleKey>\\S+))?"); private final Path root; private final Map<BuildTarget, BuildLogEntry> buildLogEntries; private BuckBuildLog(Path root, Map<BuildTarget, BuildLogEntry> buildLogEntries) { this.root = root; this.buildLogEntries = Preconditions.checkNotNull(buildLogEntries); } public void assertTargetBuiltLocally(String buildTargetRaw) { assertBuildSuccessType(buildTargetRaw, BuildRuleSuccessType.BUILT_LOCALLY); } public void assertNotTargetBuiltLocally(String buildTargetRaw) { BuildLogEntry logEntry = getLogEntry(buildTargetRaw); assertNotEquals( String.format( "Build target %s should not have been built locally, but it was", buildTargetRaw), BuildRuleSuccessType.BUILT_LOCALLY, logEntry.successType.get()); } public void assertTargetIsAbsent(String buildTargetRaw) { BuildTarget buildTarget = BuildTargetFactory.newInstance(root, buildTargetRaw); if (buildLogEntries.containsKey(buildTarget)) { fail( String.format( "Build target %s was not expected in log, but found result: %s", buildTargetRaw, buildLogEntries.get(buildTarget))); } } public void assertTargetWasFetchedFromCache(String buildTargetRaw) { assertBuildSuccessType(buildTargetRaw, BuildRuleSuccessType.FETCHED_FROM_CACHE); } public void assertTargetWasFetchedFromCacheByManifestMatch(String buildTargetRaw) { assertBuildSuccessType(buildTargetRaw, BuildRuleSuccessType.FETCHED_FROM_CACHE_MANIFEST_BASED); } public void assertTargetHadMatchingInputRuleKey(String buildTargetRaw) { assertBuildSuccessType(buildTargetRaw, BuildRuleSuccessType.MATCHING_INPUT_BASED_RULE_KEY); } public void assertTargetHadMatchingDepfileRuleKey(String buildTargetRaw) { assertBuildSuccessType(buildTargetRaw, BuildRuleSuccessType.MATCHING_DEP_FILE_RULE_KEY); } public void assertTargetHadMatchingRuleKey(String buildTargetRaw) { assertBuildSuccessType(buildTargetRaw, BuildRuleSuccessType.MATCHING_RULE_KEY); } public void assertTargetFailed(String buildTargetRaw) { BuildLogEntry logEntry = getLogEntry(buildTargetRaw); assertEquals(BuildRuleStatus.FAIL, logEntry.status); } public void assertTargetCanceled(String buildTargetRaw) { BuildLogEntry logEntry = getLogEntry(buildTargetRaw); assertEquals(BuildRuleStatus.CANCELED, logEntry.status); } public Sha1HashCode getRuleKey(String buildTargetRaw) { BuildLogEntry logEntry = getLogEntry(buildTargetRaw); return logEntry.ruleKeyHashCode; } public ImmutableSet<BuildTarget> getAllTargets() { return ImmutableSortedSet.copyOf(buildLogEntries.keySet()); } public static BuckBuildLog fromLogContents(Path root, List<String> logContents) { ImmutableMap.Builder<BuildTarget, BuildLogEntry> builder = ImmutableMap.builder(); for (String line : logContents) { Matcher matcher = BUILD_LOG_FINISHED_RULE_REGEX.matcher(line); if (!matcher.matches()) { continue; } String buildTargetRaw = matcher.group("BuildTarget"); BuildTarget buildTarget = BuildTargetFactory.newInstance(root, buildTargetRaw); String statusRaw = matcher.group("Status"); BuildRuleStatus status = BuildRuleStatus.valueOf(statusRaw); String ruleKeyRaw = matcher.group("RuleKey"); Sha1HashCode ruleKey = Sha1HashCode.of(ruleKeyRaw); CacheResult cacheResult = null; BuildRuleSuccessType successType = null; if (status == BuildRuleStatus.SUCCESS) { String cacheResultRaw = matcher.group("CacheResult"); cacheResult = CacheResult.valueOf(cacheResultRaw); String successTypeRaw = matcher.group("SuccessType"); successType = BuildRuleSuccessType.valueOf(successTypeRaw); } builder.put( buildTarget, new BuildLogEntry( status, Optional.ofNullable(successType), Optional.ofNullable(cacheResult), ruleKey)); } return new BuckBuildLog(root, builder.build()); } private void assertBuildSuccessType(String buildTargetRaw, BuildRuleSuccessType expectedType) { BuildLogEntry logEntry = getLogEntry(buildTargetRaw); assertThat( String.format("%s should have succeeded", buildTargetRaw), logEntry.getStatus(), is(BuildRuleStatus.SUCCESS)); assertThat( String.format("%s has %s", buildTargetRaw, logEntry), logEntry.successType.get(), is(expectedType)); } public void assertNoLogEntry(String buildTargetRaw) { BuildTarget buildTarget = BuildTargetFactory.newInstance(root, buildTargetRaw); if (buildLogEntries.containsKey(buildTarget)) { fail( String.format( "Was expecting no log entry for %s, but found: %s", buildTargetRaw, buildLogEntries.get(buildTarget))); } } public BuildLogEntry getLogEntry(String buildTargetRaw) { BuildTarget buildTarget = BuildTargetFactory.newInstance(root, buildTargetRaw); if (!buildLogEntries.containsKey(buildTarget)) { fail(String.format("There was no build log entry for target %s", buildTargetRaw)); } return buildLogEntries.get(buildTarget); } public BuildLogEntry getLogEntry(BuildTarget target) { return getLogEntry(target.toString()); } public static class BuildLogEntry { private final BuildRuleStatus status; private final Optional<BuildRuleSuccessType> successType; private final Optional<CacheResult> cacheResult; private final Sha1HashCode ruleKeyHashCode; private BuildLogEntry( BuildRuleStatus status, Optional<BuildRuleSuccessType> successType, Optional<CacheResult> cacheResult, Sha1HashCode ruleKeyHashCode) { this.status = Preconditions.checkNotNull(status); this.successType = successType; this.cacheResult = cacheResult; this.ruleKeyHashCode = Preconditions.checkNotNull(ruleKeyHashCode); } public BuildRuleStatus getStatus() { return status; } public Optional<BuildRuleSuccessType> getSuccessType() { return successType; } public Sha1HashCode getRuleKey() { return ruleKeyHashCode; } @Override public String toString() { return MoreObjects.toStringHelper(this) .add("status", status) .add("successType", successType) .add("cacheResult", cacheResult) .add("ruleKeyHashCode", ruleKeyHashCode) .toString(); } } }