blob: 3464e5f1f91ccc6d5d2c7ced14b6ca6906d516ff [file] [log] [blame]
// 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.analysis.config;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertThrows;
import com.google.auto.value.AutoValue;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.devtools.build.lib.analysis.config.BuildOptions.OptionsDiffForReconstruction;
import com.google.devtools.build.lib.cmdline.Label;
import com.google.devtools.build.lib.skyframe.trimming.ConfigurationComparer;
import com.google.devtools.build.lib.skyframe.trimming.TrimmableTestConfigurationFragments.AOptions;
import com.google.devtools.build.lib.skyframe.trimming.TrimmableTestConfigurationFragments.BOptions;
import java.util.Objects;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
/** Tests of compareFragments in BuildOptions.OptionsDiffForReconstruction. */
public final class BuildOptionsCompareFragmentsTest {
/** Test cases for BuildOptionsCompareFragmentsTest. */
@AutoValue
public abstract static class Case {
public abstract String getName();
public abstract BuildOptions getBase();
public abstract BuildOptions getLeft();
public abstract BuildOptions getRight();
public abstract ConfigurationComparer.Result getLeftToRightResult();
public abstract ConfigurationComparer.Result getRightToLeftResult();
public static Builder named(String name) {
return new AutoValue_BuildOptionsCompareFragmentsTest_Case.Builder().setName(name);
}
/** Quick builder for test cases. */
@AutoValue.Builder
public abstract static class Builder {
public abstract Builder setName(String name);
public abstract Builder setBase(BuildOptions base);
public Builder setBase(OptionsBuilder base) throws Exception {
return this.setBase(base.build());
}
public abstract Builder setLeft(BuildOptions left);
public Builder setLeft(OptionsBuilder left) throws Exception {
return this.setLeft(left.build());
}
public abstract Builder setRight(BuildOptions right);
public Builder setRight(OptionsBuilder right) throws Exception {
return this.setRight(right.build());
}
public Builder setResult(ConfigurationComparer.Result result) {
return this.setLeftToRightResult(result).setRightToLeftResult(result);
}
public abstract Builder setLeftToRightResult(ConfigurationComparer.Result result);
public abstract Builder setRightToLeftResult(ConfigurationComparer.Result result);
public abstract Case build();
}
@Override
public final String toString() {
if (Objects.equals(this.getLeftToRightResult(), this.getRightToLeftResult())) {
return String.format("%s [result = %s]", this.getName(), this.getLeftToRightResult());
} else {
return String.format(
"%s [compare(left, right) = %s; compare(right, left) = %s]",
this.getName(), this.getLeftToRightResult(), this.getRightToLeftResult());
}
}
}
/** Quick builder for BuildOptions instances. */
public static final class OptionsBuilder {
private final ImmutableMap.Builder<Label, Object> starlarkOptions =
new ImmutableMap.Builder<>();
private final ImmutableList.Builder<Class<? extends FragmentOptions>> fragments =
new ImmutableList.Builder<>();
private final ImmutableList.Builder<String> nativeOptions = new ImmutableList.Builder<>();
public OptionsBuilder withNativeFragment(
Class<? extends FragmentOptions> fragment, String... flags) {
this.fragments.add(fragment);
this.nativeOptions.add(flags);
return this;
}
public OptionsBuilder withStarlarkOption(String setting, Object value) {
this.starlarkOptions.put(Label.parseAbsoluteUnchecked(setting), value);
return this;
}
public OptionsBuilder withStarlarkOption(String setting) {
return this.withStarlarkOption(setting, setting);
}
public BuildOptions build() throws Exception {
return BuildOptions.builder()
.addStarlarkOptions(this.starlarkOptions.build())
.merge(
BuildOptions.of(
this.fragments.build(), this.nativeOptions.build().toArray(new String[0])))
.build();
}
}
/** Test cases for compareFragments which produce an ordinary result. */
@RunWith(Parameterized.class)
public static final class NormalCases {
@Parameters(name = "{index}: {0}")
public static Iterable<Case> cases() throws Exception {
return ImmutableList.of(
Case.named("both options equal to the base")
.setResult(ConfigurationComparer.Result.EQUAL)
.setBase(
new OptionsBuilder()
.withNativeFragment(AOptions.class)
.withStarlarkOption("//alpha"))
.setLeft(
new OptionsBuilder()
.withNativeFragment(AOptions.class)
.withStarlarkOption("//alpha"))
.setRight(
new OptionsBuilder()
.withNativeFragment(AOptions.class)
.withStarlarkOption("//alpha"))
.build(),
Case.named("both sides change native fragment to same value")
.setResult(ConfigurationComparer.Result.EQUAL)
.setBase(new OptionsBuilder().withNativeFragment(AOptions.class, "--alpha=base"))
.setLeft(new OptionsBuilder().withNativeFragment(AOptions.class, "--alpha=new"))
.setRight(new OptionsBuilder().withNativeFragment(AOptions.class, "--alpha=new"))
.build(),
Case.named("both sides add native fragment with same value")
.setResult(ConfigurationComparer.Result.EQUAL)
.setBase(new OptionsBuilder())
.setLeft(new OptionsBuilder().withNativeFragment(AOptions.class, "--alpha=new"))
.setRight(new OptionsBuilder().withNativeFragment(AOptions.class, "--alpha=new"))
.build(),
Case.named("both sides remove same native fragment")
.setResult(ConfigurationComparer.Result.EQUAL)
.setBase(new OptionsBuilder().withNativeFragment(AOptions.class))
.setLeft(new OptionsBuilder())
.setRight(new OptionsBuilder())
.build(),
Case.named("both sides change Starlark option to same value")
.setResult(ConfigurationComparer.Result.EQUAL)
.setBase(new OptionsBuilder().withStarlarkOption("//alpha", "base"))
.setLeft(new OptionsBuilder().withStarlarkOption("//alpha", "new"))
.setRight(new OptionsBuilder().withStarlarkOption("//alpha", "new"))
.build(),
Case.named("both sides add Starlark option with same value")
.setResult(ConfigurationComparer.Result.EQUAL)
.setBase(new OptionsBuilder())
.setLeft(new OptionsBuilder().withStarlarkOption("//alpha", "new"))
.setRight(new OptionsBuilder().withStarlarkOption("//alpha", "new"))
.build(),
Case.named("both sides remove same Starlark option")
.setResult(ConfigurationComparer.Result.EQUAL)
.setBase(new OptionsBuilder().withStarlarkOption("//alpha"))
.setLeft(new OptionsBuilder())
.setRight(new OptionsBuilder())
.build(),
Case.named("native fragment removed from right")
.setLeftToRightResult(ConfigurationComparer.Result.SUPERSET)
.setRightToLeftResult(ConfigurationComparer.Result.SUBSET)
.setBase(new OptionsBuilder().withNativeFragment(AOptions.class))
.setLeft(new OptionsBuilder().withNativeFragment(AOptions.class))
.setRight(new OptionsBuilder())
.build(),
Case.named("native fragment added to right")
.setLeftToRightResult(ConfigurationComparer.Result.SUBSET)
.setRightToLeftResult(ConfigurationComparer.Result.SUPERSET)
.setBase(new OptionsBuilder())
.setLeft(new OptionsBuilder())
.setRight(new OptionsBuilder().withNativeFragment(AOptions.class))
.build(),
Case.named("native fragment changed in left and removed from right")
.setLeftToRightResult(ConfigurationComparer.Result.SUPERSET)
.setRightToLeftResult(ConfigurationComparer.Result.SUBSET)
.setBase(new OptionsBuilder().withNativeFragment(AOptions.class, "--alpha=base"))
.setLeft(new OptionsBuilder().withNativeFragment(AOptions.class, "--alpha=left"))
.setRight(new OptionsBuilder())
.build(),
Case.named("native fragment added to left and another fragment removed from right")
.setLeftToRightResult(ConfigurationComparer.Result.SUPERSET)
.setRightToLeftResult(ConfigurationComparer.Result.SUBSET)
.setBase(new OptionsBuilder().withNativeFragment(AOptions.class))
.setLeft(
new OptionsBuilder()
.withNativeFragment(AOptions.class)
.withNativeFragment(BOptions.class))
.setRight(new OptionsBuilder())
.build(),
Case.named("Starlark option removed from right")
.setLeftToRightResult(ConfigurationComparer.Result.SUPERSET)
.setRightToLeftResult(ConfigurationComparer.Result.SUBSET)
.setBase(new OptionsBuilder().withStarlarkOption("//alpha"))
.setLeft(new OptionsBuilder().withStarlarkOption("//alpha"))
.setRight(new OptionsBuilder())
.build(),
Case.named("Starlark option added to right")
.setLeftToRightResult(ConfigurationComparer.Result.SUBSET)
.setRightToLeftResult(ConfigurationComparer.Result.SUPERSET)
.setBase(new OptionsBuilder())
.setLeft(new OptionsBuilder())
.setRight(new OptionsBuilder().withStarlarkOption("//alpha"))
.build(),
Case.named("Starlark option changed in left and removed from right")
.setLeftToRightResult(ConfigurationComparer.Result.SUPERSET)
.setRightToLeftResult(ConfigurationComparer.Result.SUBSET)
.setBase(new OptionsBuilder().withStarlarkOption("//alpha", "base"))
.setLeft(new OptionsBuilder().withStarlarkOption("//alpha", "left"))
.setRight(new OptionsBuilder())
.build(),
Case.named("Starlark option added to left and another option removed from right")
.setLeftToRightResult(ConfigurationComparer.Result.SUPERSET)
.setRightToLeftResult(ConfigurationComparer.Result.SUBSET)
.setBase(new OptionsBuilder().withStarlarkOption("//alpha"))
.setLeft(
new OptionsBuilder().withStarlarkOption("//alpha").withStarlarkOption("//bravo"))
.setRight(new OptionsBuilder())
.build(),
Case.named("different native fragment added to each side")
.setResult(ConfigurationComparer.Result.ALL_SHARED_FRAGMENTS_EQUAL)
.setBase(new OptionsBuilder())
.setLeft(new OptionsBuilder().withNativeFragment(AOptions.class))
.setRight(new OptionsBuilder().withNativeFragment(BOptions.class))
.build(),
Case.named("different native fragment removed from each side")
.setResult(ConfigurationComparer.Result.ALL_SHARED_FRAGMENTS_EQUAL)
.setBase(
new OptionsBuilder()
.withNativeFragment(AOptions.class)
.withNativeFragment(BOptions.class))
.setLeft(new OptionsBuilder().withNativeFragment(AOptions.class))
.setRight(new OptionsBuilder().withNativeFragment(BOptions.class))
.build(),
Case.named("native fragment added and different fragment removed on left")
.setResult(ConfigurationComparer.Result.ALL_SHARED_FRAGMENTS_EQUAL)
.setBase(new OptionsBuilder().withNativeFragment(BOptions.class))
.setLeft(new OptionsBuilder().withNativeFragment(AOptions.class))
.setRight(new OptionsBuilder().withNativeFragment(BOptions.class))
.build(),
Case.named(
"native fragment added to right; "
+ "other fragment changed on left and removed from right")
.setResult(ConfigurationComparer.Result.ALL_SHARED_FRAGMENTS_EQUAL)
.setBase(new OptionsBuilder().withNativeFragment(AOptions.class, "--alpha=base"))
.setLeft(new OptionsBuilder().withNativeFragment(AOptions.class, "--alpha=left"))
.setRight(new OptionsBuilder().withNativeFragment(BOptions.class))
.build(),
Case.named("native fragment changed on each side, removed from the other")
.setResult(ConfigurationComparer.Result.ALL_SHARED_FRAGMENTS_EQUAL)
.setBase(
new OptionsBuilder()
.withNativeFragment(AOptions.class, "--alpha=base")
.withNativeFragment(BOptions.class, "--bravo=base"))
.setLeft(new OptionsBuilder().withNativeFragment(AOptions.class, "--alpha=left"))
.setRight(new OptionsBuilder().withNativeFragment(BOptions.class, "--bravo=right"))
.build(),
Case.named(
"native fragment changed on left, removed from right; "
+ "other fragment removed from left")
.setResult(ConfigurationComparer.Result.ALL_SHARED_FRAGMENTS_EQUAL)
.setBase(
new OptionsBuilder()
.withNativeFragment(AOptions.class, "--alpha=base")
.withNativeFragment(BOptions.class))
.setLeft(new OptionsBuilder().withNativeFragment(AOptions.class, "--alpha=left"))
.setRight(new OptionsBuilder().withNativeFragment(BOptions.class))
.build(),
Case.named("different Starlark option added to each side")
.setResult(ConfigurationComparer.Result.ALL_SHARED_FRAGMENTS_EQUAL)
.setBase(new OptionsBuilder())
.setLeft(new OptionsBuilder().withStarlarkOption("//alpha"))
.setRight(new OptionsBuilder().withStarlarkOption("//bravo"))
.build(),
Case.named("different Starlark option removed from each side")
.setResult(ConfigurationComparer.Result.ALL_SHARED_FRAGMENTS_EQUAL)
.setBase(
new OptionsBuilder().withStarlarkOption("//alpha").withStarlarkOption("//bravo"))
.setLeft(new OptionsBuilder().withStarlarkOption("//alpha"))
.setRight(new OptionsBuilder().withStarlarkOption("//bravo"))
.build(),
Case.named("Starlark option added and different option removed on left")
.setResult(ConfigurationComparer.Result.ALL_SHARED_FRAGMENTS_EQUAL)
.setBase(new OptionsBuilder().withStarlarkOption("//bravo"))
.setLeft(new OptionsBuilder().withStarlarkOption("//alpha"))
.setRight(new OptionsBuilder().withStarlarkOption("//bravo"))
.build(),
Case.named(
"Starlark option added to right; "
+ "other option changed on left and removed from right")
.setResult(ConfigurationComparer.Result.ALL_SHARED_FRAGMENTS_EQUAL)
.setBase(new OptionsBuilder().withStarlarkOption("//alpha", "base"))
.setLeft(new OptionsBuilder().withStarlarkOption("//alpha", "left"))
.setRight(new OptionsBuilder().withStarlarkOption("//bravo"))
.build(),
Case.named("Starlark option changed on each side, removed from the other")
.setResult(ConfigurationComparer.Result.ALL_SHARED_FRAGMENTS_EQUAL)
.setBase(
new OptionsBuilder()
.withStarlarkOption("//alpha", "base")
.withStarlarkOption("//bravo", "base"))
.setLeft(new OptionsBuilder().withStarlarkOption("//alpha", "left"))
.setRight(new OptionsBuilder().withStarlarkOption("//bravo", "right"))
.build(),
Case.named(
"Starlark option changed on left, removed from right; "
+ "other option removed from left")
.setResult(ConfigurationComparer.Result.ALL_SHARED_FRAGMENTS_EQUAL)
.setBase(
new OptionsBuilder()
.withStarlarkOption("//alpha", "base")
.withStarlarkOption("//bravo"))
.setLeft(new OptionsBuilder().withStarlarkOption("//alpha", "left"))
.setRight(new OptionsBuilder().withStarlarkOption("//bravo"))
.build(),
Case.named("Starlark option removed from left, native option removed from right")
.setResult(ConfigurationComparer.Result.ALL_SHARED_FRAGMENTS_EQUAL)
.setBase(
new OptionsBuilder()
.withNativeFragment(AOptions.class)
.withStarlarkOption("//bravo"))
.setLeft(new OptionsBuilder().withNativeFragment(AOptions.class))
.setRight(new OptionsBuilder().withStarlarkOption("//bravo"))
.build(),
Case.named("Starlark option added to left, native option added to right")
.setResult(ConfigurationComparer.Result.ALL_SHARED_FRAGMENTS_EQUAL)
.setBase(new OptionsBuilder())
.setLeft(new OptionsBuilder().withStarlarkOption("//alpha"))
.setRight(new OptionsBuilder().withNativeFragment(BOptions.class))
.build(),
Case.named("native fragment is unchanged in left, changes in right")
.setResult(ConfigurationComparer.Result.DIFFERENT)
.setBase(new OptionsBuilder().withNativeFragment(AOptions.class, "--alpha=base"))
.setLeft(new OptionsBuilder().withNativeFragment(AOptions.class, "--alpha=base"))
.setRight(new OptionsBuilder().withNativeFragment(AOptions.class, "--alpha=right"))
.build(),
Case.named("native fragment is changed to different values")
.setResult(ConfigurationComparer.Result.DIFFERENT)
.setBase(new OptionsBuilder().withNativeFragment(AOptions.class, "--alpha=base"))
.setLeft(new OptionsBuilder().withNativeFragment(AOptions.class, "--alpha=left"))
.setRight(new OptionsBuilder().withNativeFragment(AOptions.class, "--alpha=right"))
.build(),
Case.named("native fragment is added with different values")
.setResult(ConfigurationComparer.Result.DIFFERENT)
.setBase(new OptionsBuilder())
.setLeft(new OptionsBuilder().withNativeFragment(AOptions.class, "--alpha=left"))
.setRight(new OptionsBuilder().withNativeFragment(AOptions.class, "--alpha=right"))
.build(),
Case.named("Starlark option is unchanged in left, changes in right")
.setResult(ConfigurationComparer.Result.DIFFERENT)
.setBase(new OptionsBuilder().withStarlarkOption("//alpha", "base"))
.setLeft(new OptionsBuilder().withStarlarkOption("//alpha", "base"))
.setRight(new OptionsBuilder().withStarlarkOption("//alpha", "right"))
.build(),
Case.named("Starlark option is changed to different values")
.setResult(ConfigurationComparer.Result.DIFFERENT)
.setBase(new OptionsBuilder().withStarlarkOption("//alpha", "base"))
.setLeft(new OptionsBuilder().withStarlarkOption("//alpha", "left"))
.setRight(new OptionsBuilder().withStarlarkOption("//alpha", "right"))
.build(),
Case.named("Starlark option is added with different values")
.setResult(ConfigurationComparer.Result.DIFFERENT)
.setBase(new OptionsBuilder())
.setLeft(new OptionsBuilder().withStarlarkOption("//alpha", "left"))
.setRight(new OptionsBuilder().withStarlarkOption("//alpha", "right"))
.build());
}
private final Case testCase;
public NormalCases(Case testCase) {
this.testCase = testCase;
}
@Test
public void compareLeftToRight() throws Exception {
OptionsDiffForReconstruction diffLeft =
BuildOptions.diffForReconstruction(testCase.getBase(), testCase.getLeft());
OptionsDiffForReconstruction diffRight =
BuildOptions.diffForReconstruction(testCase.getBase(), testCase.getRight());
assertThat(OptionsDiffForReconstruction.compareFragments(diffLeft, diffRight))
.isEqualTo(testCase.getLeftToRightResult());
}
@Test
public void compareRightToLeft() throws Exception {
OptionsDiffForReconstruction diffLeft =
BuildOptions.diffForReconstruction(testCase.getBase(), testCase.getLeft());
OptionsDiffForReconstruction diffRight =
BuildOptions.diffForReconstruction(testCase.getBase(), testCase.getRight());
assertThat(OptionsDiffForReconstruction.compareFragments(diffRight, diffLeft))
.isEqualTo(testCase.getRightToLeftResult());
}
}
/** Test cases for compareFragments which produce errors. */
@RunWith(JUnit4.class)
public static final class ExceptionalCases {
@Test
public void withDifferentBases_throwsError() throws Exception {
BuildOptions baseA =
new OptionsBuilder()
.withNativeFragment(AOptions.class, "--alpha=A")
.withNativeFragment(BOptions.class, "--bravo=base")
.build();
BuildOptions newA =
new OptionsBuilder()
.withNativeFragment(AOptions.class, "--alpha=A")
.withNativeFragment(BOptions.class, "--bravo=new")
.build();
BuildOptions baseB =
new OptionsBuilder()
.withNativeFragment(AOptions.class, "--alpha=B")
.withNativeFragment(BOptions.class, "--bravo=base")
.build();
BuildOptions newB =
new OptionsBuilder()
.withNativeFragment(AOptions.class, "--alpha=B")
.withNativeFragment(BOptions.class, "--bravo=old")
.build();
OptionsDiffForReconstruction diffA = BuildOptions.diffForReconstruction(baseA, newA);
OptionsDiffForReconstruction diffB = BuildOptions.diffForReconstruction(baseB, newB);
IllegalArgumentException forwardException =
assertThrows(
IllegalArgumentException.class,
() -> OptionsDiffForReconstruction.compareFragments(diffA, diffB));
assertThat(forwardException).hasMessageThat().contains("diffs with different bases");
IllegalArgumentException reverseException =
assertThrows(
IllegalArgumentException.class,
() -> OptionsDiffForReconstruction.compareFragments(diffB, diffA));
assertThat(reverseException).hasMessageThat().contains("diffs with different bases");
}
}
}