brandjon | 60be531 | 2017-10-04 23:06:41 +0200 | [diff] [blame] | 1 | // Copyright 2017 The Bazel Authors. All rights reserved. |
| 2 | // |
| 3 | // Licensed under the Apache License, Version 2.0 (the "License"); |
| 4 | // you may not use this file except in compliance with the License. |
| 5 | // You may obtain a copy of the License at |
| 6 | // |
| 7 | // http://www.apache.org/licenses/LICENSE-2.0 |
| 8 | // |
| 9 | // Unless required by applicable law or agreed to in writing, software |
| 10 | // distributed under the License is distributed on an "AS IS" BASIS, |
| 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 12 | // See the License for the specific language governing permissions and |
| 13 | // limitations under the License. |
| 14 | |
| 15 | package com.google.devtools.build.lib.packages; |
| 16 | |
| 17 | import static com.google.common.truth.Truth.assertThat; |
| 18 | |
shahan | fae34b9 | 2018-02-13 10:08:47 -0800 | [diff] [blame] | 19 | import com.google.common.collect.ImmutableMap; |
| 20 | import com.google.devtools.build.lib.skyframe.serialization.DeserializationContext; |
| 21 | import com.google.devtools.build.lib.skyframe.serialization.SerializationContext; |
brandjon | 60be531 | 2017-10-04 23:06:41 +0200 | [diff] [blame] | 22 | import com.google.devtools.build.lib.skyframe.serialization.testutils.TestUtils; |
| 23 | import com.google.devtools.build.lib.syntax.SkylarkSemantics; |
brandjon | 60be531 | 2017-10-04 23:06:41 +0200 | [diff] [blame] | 24 | import com.google.devtools.common.options.Options; |
| 25 | import com.google.devtools.common.options.OptionsParser; |
| 26 | import java.util.Arrays; |
| 27 | import java.util.Random; |
| 28 | import org.junit.Test; |
| 29 | import org.junit.runner.RunWith; |
| 30 | import org.junit.runners.JUnit4; |
| 31 | |
| 32 | /** |
| 33 | * Tests for the flow of flags from {@link SkylarkSemanticsOptions} to {@link SkylarkSemantics}, and |
| 34 | * to and from {@code SkylarkSemantics}' serialized representation. |
| 35 | * |
| 36 | * <p>When adding a new option, it is trivial to make a transposition error or a copy/paste error. |
| 37 | * These tests guard against such errors. The following possible bugs are considered: |
| 38 | * <ul> |
| 39 | * <li>If a new option is added to {@code SkylarkSemantics} but not to {@code |
| 40 | * SkylarkSemanticsOptions}, or vice versa, then the programmer will either be unable to |
| 41 | * implement its behavior, or unable to test it from the command line and add user |
| 42 | * documentation. We hope that the programmer notices this on their own. |
| 43 | * |
| 44 | * <li>If {@link SkylarkSemanticsOptions#toSkylarkSemantics} or {@link |
| 45 | * SkylarkSemanticsCodec#deserialize} is not updated to set all fields of {@code |
| 46 | * SkylarkSemantics}, then it will fail immediately because all fields of {@link |
| 47 | * SkylarkSemantics.Builder} are mandatory. |
| 48 | * |
| 49 | * <li>To catch a copy/paste error where the wrong field's data is threaded through {@code |
| 50 | * toSkylarkSemantics()} or {@code deserialize(...)}, we repeatedly generate matching random |
| 51 | * instances of the input and expected output objects. |
| 52 | * |
| 53 | * <li>The {@link #checkDefaultsMatch} test ensures that there is no divergence between the |
| 54 | * default values of the two classes. |
| 55 | * |
| 56 | * <li>There is no test coverage for failing to update the non-generated webpage documentation. |
| 57 | * So don't forget that! |
| 58 | * </ul> |
| 59 | */ |
| 60 | @RunWith(JUnit4.class) |
| 61 | public class SkylarkSemanticsConsistencyTest { |
| 62 | |
| 63 | private static final int NUM_RANDOM_TRIALS = 10; |
| 64 | |
| 65 | /** |
| 66 | * Checks that a randomly generated {@link SkylarkSemanticsOptions} object can be converted to a |
| 67 | * {@link SkylarkSemantics} object with the same field values. |
| 68 | */ |
| 69 | @Test |
| 70 | public void optionsToSemantics() throws Exception { |
| 71 | for (int i = 0; i < NUM_RANDOM_TRIALS; i++) { |
| 72 | long seed = i; |
| 73 | SkylarkSemanticsOptions options = buildRandomOptions(new Random(seed)); |
| 74 | SkylarkSemantics semantics = buildRandomSemantics(new Random(seed)); |
| 75 | SkylarkSemantics semanticsFromOptions = options.toSkylarkSemantics(); |
| 76 | assertThat(semanticsFromOptions).isEqualTo(semantics); |
| 77 | } |
| 78 | } |
| 79 | |
brandjon | 6ac92f9 | 2017-12-06 13:57:15 -0800 | [diff] [blame] | 80 | /** |
brandjon | 60be531 | 2017-10-04 23:06:41 +0200 | [diff] [blame] | 81 | * Checks that a randomly generated {@link SkylarkSemantics} object can be serialized and |
| 82 | * deserialized to an equivalent object. |
| 83 | */ |
| 84 | @Test |
| 85 | public void serializationRoundTrip() throws Exception { |
| 86 | SkylarkSemanticsCodec codec = new SkylarkSemanticsCodec(); |
| 87 | for (int i = 0; i < NUM_RANDOM_TRIALS; i++) { |
| 88 | SkylarkSemantics semantics = buildRandomSemantics(new Random(i)); |
| 89 | SkylarkSemantics deserialized = |
shahan | fae34b9 | 2018-02-13 10:08:47 -0800 | [diff] [blame] | 90 | TestUtils.fromBytes( |
| 91 | new DeserializationContext(ImmutableMap.of()), |
| 92 | codec, |
| 93 | TestUtils.toBytes(new SerializationContext(ImmutableMap.of()), codec, semantics)); |
brandjon | 60be531 | 2017-10-04 23:06:41 +0200 | [diff] [blame] | 94 | assertThat(deserialized).isEqualTo(semantics); |
| 95 | } |
| 96 | } |
| 97 | |
| 98 | @Test |
| 99 | public void checkDefaultsMatch() { |
| 100 | SkylarkSemanticsOptions defaultOptions = Options.getDefaults(SkylarkSemanticsOptions.class); |
| 101 | SkylarkSemantics defaultSemantics = SkylarkSemantics.DEFAULT_SEMANTICS; |
| 102 | SkylarkSemantics semanticsFromOptions = defaultOptions.toSkylarkSemantics(); |
| 103 | assertThat(semanticsFromOptions).isEqualTo(defaultSemantics); |
| 104 | } |
| 105 | |
brandjon | 6ac92f9 | 2017-12-06 13:57:15 -0800 | [diff] [blame] | 106 | @Test |
| 107 | public void canGetBuilderFromInstance() { |
| 108 | SkylarkSemantics original = SkylarkSemantics.DEFAULT_SEMANTICS; |
| 109 | assertThat(original.internalSkylarkFlagTestCanary()).isFalse(); |
| 110 | SkylarkSemantics modified = original.toBuilder().internalSkylarkFlagTestCanary(true).build(); |
| 111 | assertThat(modified.internalSkylarkFlagTestCanary()).isTrue(); |
| 112 | } |
| 113 | |
brandjon | 60be531 | 2017-10-04 23:06:41 +0200 | [diff] [blame] | 114 | /** |
| 115 | * Constructs a {@link SkylarkSemanticsOptions} object with random fields. Must access {@code |
| 116 | * rand} using the same sequence of operations (for the same fields) as {@link |
| 117 | * #buildRandomSemantics}. |
| 118 | */ |
| 119 | private static SkylarkSemanticsOptions buildRandomOptions(Random rand) throws Exception { |
| 120 | return parseOptions( |
| 121 | // <== Add new options here in alphabetic order ==> |
| 122 | "--incompatible_bzl_disallow_load_after_statement=" + rand.nextBoolean(), |
brandjon | 60be531 | 2017-10-04 23:06:41 +0200 | [diff] [blame] | 123 | "--incompatible_depset_is_not_iterable=" + rand.nextBoolean(), |
laurentlb | 2bbda4a | 2017-12-07 10:38:46 -0800 | [diff] [blame] | 124 | "--incompatible_depset_union=" + rand.nextBoolean(), |
laurentlb | d7f87aa | 2017-12-12 07:55:15 -0800 | [diff] [blame] | 125 | "--incompatible_disable_glob_tracking=" + rand.nextBoolean(), |
cparsons | 99be8b4 | 2018-03-01 15:16:46 -0800 | [diff] [blame] | 126 | "--incompatible_disable_objc_provider_resources=" + rand.nextBoolean(), |
brandjon | 60be531 | 2017-10-04 23:06:41 +0200 | [diff] [blame] | 127 | "--incompatible_disallow_dict_plus=" + rand.nextBoolean(), |
tomlu | beafd7e | 2018-04-05 15:03:19 -0700 | [diff] [blame] | 128 | "--incompatible_disallow_old_style_args_add=" + rand.nextBoolean(), |
laurentlb | c381cf1 | 2018-04-11 04:12:14 -0700 | [diff] [blame] | 129 | "--incompatible_disallow_slash_operator=" + rand.nextBoolean(), |
brandjon | 60be531 | 2017-10-04 23:06:41 +0200 | [diff] [blame] | 130 | "--incompatible_new_actions_api=" + rand.nextBoolean(), |
laurentlb | 1cbce0f | 2018-03-27 12:43:22 -0700 | [diff] [blame] | 131 | "--incompatible_package_name_is_a_function=" + rand.nextBoolean(), |
Klaus Aehlig | ddd1c9a | 2018-03-01 05:54:39 -0800 | [diff] [blame] | 132 | "--incompatible_remove_native_git_repository=" + rand.nextBoolean(), |
Klaus Aehlig | a7b34a1 | 2018-02-20 09:31:37 -0800 | [diff] [blame] | 133 | "--incompatible_remove_native_http_archive=" + rand.nextBoolean(), |
brandjon | 60be531 | 2017-10-04 23:06:41 +0200 | [diff] [blame] | 134 | "--incompatible_string_is_not_iterable=" + rand.nextBoolean(), |
vladmos | 9bb93ee | 2017-11-21 04:40:08 -0800 | [diff] [blame] | 135 | "--internal_skylark_flag_test_canary=" + rand.nextBoolean()); |
brandjon | 60be531 | 2017-10-04 23:06:41 +0200 | [diff] [blame] | 136 | } |
| 137 | |
| 138 | /** |
| 139 | * Constructs a {@link SkylarkSemantics} object with random fields. Must access {@code rand} using |
| 140 | * the same sequence of operations (for the same fields) as {@link #buildRandomOptions}. |
| 141 | */ |
| 142 | private static SkylarkSemantics buildRandomSemantics(Random rand) { |
| 143 | return SkylarkSemantics.builder() |
| 144 | // <== Add new options here in alphabetic order ==> |
| 145 | .incompatibleBzlDisallowLoadAfterStatement(rand.nextBoolean()) |
brandjon | 60be531 | 2017-10-04 23:06:41 +0200 | [diff] [blame] | 146 | .incompatibleDepsetIsNotIterable(rand.nextBoolean()) |
laurentlb | 2bbda4a | 2017-12-07 10:38:46 -0800 | [diff] [blame] | 147 | .incompatibleDepsetUnion(rand.nextBoolean()) |
laurentlb | d7f87aa | 2017-12-12 07:55:15 -0800 | [diff] [blame] | 148 | .incompatibleDisableGlobTracking(rand.nextBoolean()) |
cparsons | 99be8b4 | 2018-03-01 15:16:46 -0800 | [diff] [blame] | 149 | .incompatibleDisableObjcProviderResources(rand.nextBoolean()) |
brandjon | 60be531 | 2017-10-04 23:06:41 +0200 | [diff] [blame] | 150 | .incompatibleDisallowDictPlus(rand.nextBoolean()) |
tomlu | beafd7e | 2018-04-05 15:03:19 -0700 | [diff] [blame] | 151 | .incompatibleDisallowOldStyleArgsAdd(rand.nextBoolean()) |
laurentlb | c381cf1 | 2018-04-11 04:12:14 -0700 | [diff] [blame] | 152 | .incompatibleDisallowSlashOperator(rand.nextBoolean()) |
brandjon | 60be531 | 2017-10-04 23:06:41 +0200 | [diff] [blame] | 153 | .incompatibleNewActionsApi(rand.nextBoolean()) |
laurentlb | 1cbce0f | 2018-03-27 12:43:22 -0700 | [diff] [blame] | 154 | .incompatiblePackageNameIsAFunction(rand.nextBoolean()) |
Klaus Aehlig | ddd1c9a | 2018-03-01 05:54:39 -0800 | [diff] [blame] | 155 | .incompatibleRemoveNativeGitRepository(rand.nextBoolean()) |
Klaus Aehlig | a7b34a1 | 2018-02-20 09:31:37 -0800 | [diff] [blame] | 156 | .incompatibleRemoveNativeHttpArchive(rand.nextBoolean()) |
brandjon | 60be531 | 2017-10-04 23:06:41 +0200 | [diff] [blame] | 157 | .incompatibleStringIsNotIterable(rand.nextBoolean()) |
brandjon | 60be531 | 2017-10-04 23:06:41 +0200 | [diff] [blame] | 158 | .internalSkylarkFlagTestCanary(rand.nextBoolean()) |
| 159 | .build(); |
| 160 | } |
| 161 | |
| 162 | private static SkylarkSemanticsOptions parseOptions(String... args) throws Exception { |
| 163 | OptionsParser parser = OptionsParser.newOptionsParser(SkylarkSemanticsOptions.class); |
| 164 | parser.setAllowResidue(false); |
| 165 | parser.parse(Arrays.asList(args)); |
| 166 | return parser.getOptions(SkylarkSemanticsOptions.class); |
| 167 | } |
| 168 | } |