// Copyright 2017 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.android; import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertWithMessage; import static com.google.devtools.build.android.SplitConfigurationFilter.mapFilenamesToSplitFlags; import com.google.common.collect.ImmutableList; import com.google.common.testing.EqualsTester; import com.google.common.truth.BooleanSubject; import com.google.devtools.build.android.SplitConfigurationFilter.ResourceConfiguration; import com.google.devtools.build.android.SplitConfigurationFilter.UnrecognizedSplitsException; import javax.annotation.CheckReturnValue; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; /** Tests for {@link SplitConfigurationFilter}. */ @RunWith(JUnit4.class) public final class SplitConfigurationFilterTest { @Test public void mapFilenamesToSplitFlagsShouldReturnEmptyMapForEmptyInput() throws UnrecognizedSplitsException { assertThat(mapFilenamesToSplitFlags(ImmutableList.<String>of(), ImmutableList.<String>of())) .isEmpty(); } @Test public void mapFilenamesToSplitFlagsShouldMatchIdenticalFilenames() throws UnrecognizedSplitsException { assertThat(mapFilenamesToSplitFlags(ImmutableList.of("en"), ImmutableList.of("en"))) .containsExactly("en", "en"); assertThat(mapFilenamesToSplitFlags(ImmutableList.of("fr-v7"), ImmutableList.of("fr-v7"))) .containsExactly("fr-v7", "fr-v7"); assertThat(mapFilenamesToSplitFlags(ImmutableList.of("en_fr-v7"), ImmutableList.of("en,fr-v7"))) .containsExactly("en_fr-v7", "en_fr-v7"); assertThat( mapFilenamesToSplitFlags( ImmutableList.of("en", "fr-v7"), ImmutableList.of("en", "fr-v7"))) .containsExactly("en", "en", "fr-v7", "fr-v7"); assertThat( mapFilenamesToSplitFlags( ImmutableList.of("fr-v4", "fr-v7"), ImmutableList.of("fr-v4", "fr-v7"))) .containsExactly("fr-v4", "fr-v4", "fr-v7", "fr-v7"); } @Test public void mapFilenamesToSplitFlagsShouldMatchReorderedFilenames() throws UnrecognizedSplitsException { assertThat(mapFilenamesToSplitFlags(ImmutableList.of("en_fr_ja"), ImmutableList.of("ja,fr,en"))) .containsExactly("en_fr_ja", "ja_fr_en"); assertThat( mapFilenamesToSplitFlags( ImmutableList.of("zu_12key", "zu"), ImmutableList.of("12key,zu", "zu"))) .containsExactly("zu_12key", "12key_zu", "zu", "zu"); assertThat( mapFilenamesToSplitFlags( ImmutableList.of("land_car", "round_port"), ImmutableList.of("car,land", "port,round"))) .containsExactly("land_car", "car_land", "round_port", "port_round"); } @Test public void mapFilenamesToSplitFlagsShouldMatchVersionUpgradedFilenames() throws UnrecognizedSplitsException { assertThat(mapFilenamesToSplitFlags(ImmutableList.of("round-v23"), ImmutableList.of("round"))) .containsExactly("round-v23", "round"); assertThat( mapFilenamesToSplitFlags( ImmutableList.of("watch-v20", "watch-v23"), ImmutableList.of("watch", "watch-v23"))) .containsExactly("watch-v20", "watch", "watch-v23", "watch-v23"); } @Test public void equivalentFiltersShouldMatchFilterFromFilename() { // identical inputs (barring commas -> underscores) should naturally match assertMatchesFilterFromFilename("abc-def", "abc-def").isTrue(); assertMatchesFilterFromFilename("abc-def-v5,ghi,jkl-lmno", "abc-def-v5_ghi_jkl-lmno").isTrue(); // anything that results in matching sets should work, so case should be ignored assertMatchesFilterFromFilename("abc-dEF", "aBC-def").isTrue(); assertMatchesFilterFromFilename("aBC-def", "abc-dEF").isTrue(); // additionally, order of elements in the set should be ignored assertMatchesFilterFromFilename("abc,def", "def_abc").isTrue(); assertMatchesFilterFromFilename("def,abc", "abc_def").isTrue(); } @Test public void matchingSpecifiersShouldMatchFilterFromFilenameWhenVersionsAreHigherNotWhenLower() { // different (higher) versions, but same specifiers and same order assertMatchesFilterFromFilename("x-v5,y-v6", "x-v7_y-v9").isTrue(); assertMatchesFilterFromFilename("x-v7,y-v9", "x-v5_y-v6").isFalse(); // order of sorted set changes but matching specifiers still match and filename version > flag assertMatchesFilterFromFilename("x-v3,y-v6", "x-v23_y-v9").isTrue(); assertMatchesFilterFromFilename("x-v23,y-v9", "x-v3_y-v6").isFalse(); // specifier sets of multiple configs are the same assertMatchesFilterFromFilename("x-v3,x-v17", "x-v3_x-v17").isTrue(); assertMatchesFilterFromFilename("x-v3,x-v17", "x-v17_x-v3").isTrue(); assertMatchesFilterFromFilename("x-v17,x-v3", "x-v17_x-v3").isTrue(); assertMatchesFilterFromFilename("x-v17,x-v3", "x-v3_x-v17").isTrue(); // specifier sets are the same, but one of them got a version bump assertMatchesFilterFromFilename("x-v3,x-v17", "x-v14_x-v17").isTrue(); assertMatchesFilterFromFilename("x-v14,x-v17", "x-v3_x-v17").isFalse(); } @Test public void nonMatchingSpecifiersShouldNotMatchFilterFromFilename() { // completely disjoint specifier sets assertMatchesFilterFromFilename("x,y,z", "a_b_c").isFalse(); assertMatchesFilterFromFilename("a,b,c", "x_y_z").isFalse(); // different number of specifier sets, which otherwise match assertMatchesFilterFromFilename("x,y,z", "x_y").isFalse(); assertMatchesFilterFromFilename("x,y", "x_y_z").isFalse(); // same number of specifiers with one non-match assertMatchesFilterFromFilename("x,y,z", "a_y_z").isFalse(); assertMatchesFilterFromFilename("a,b,c", "a_b_z").isFalse(); } @CheckReturnValue private BooleanSubject assertMatchesFilterFromFilename(String flagFilter, String filenameFilter) { return assertWithMessage( "The split flag '%s' would be a match for a filename containing '%s'", flagFilter, filenameFilter) .that( SplitConfigurationFilter.fromSplitFlag(flagFilter) .matchesFilterFromFilename( SplitConfigurationFilter.fromFilenameSuffix(filenameFilter))); } @Test public void splitConfigurationFilterShouldBeOrderedByConfigsThenFilename() { assertThat( ImmutableList.of( // If all else is equal, break ties via filename (case does matter here) SplitConfigurationFilter.fromFilenameSuffix("A"), SplitConfigurationFilter.fromFilenameSuffix("a"), // In filters with the same number of configs, the highest version wins SplitConfigurationFilter.fromFilenameSuffix("d-v5_e-v5_f-v5"), SplitConfigurationFilter.fromFilenameSuffix("a_b_c-v6"), // It doesn't matter where in the input order that version is SplitConfigurationFilter.fromFilenameSuffix("a-v7_b_c"), // Specifiers break ties on number of configs + highest version SplitConfigurationFilter.fromFilenameSuffix("d-v7_b_c"), // Second highest version breaks ties (etc.) SplitConfigurationFilter.fromFilenameSuffix("b-v2_d-v7_c"), // Order doesn't matter but it does change the filename, hence sort order SplitConfigurationFilter.fromFilenameSuffix("d-v7_b-v2_c"), // Number of configs is the main criterion, so more sets of configs means later sort SplitConfigurationFilter.fromFilenameSuffix("a_b_c_d_e_f_g_h_i_j_k_l_m"))) .isStrictlyOrdered(); // if they are actually equal, they will compare equal assertThat(SplitConfigurationFilter.fromFilenameSuffix("c-v13")) .isEquivalentAccordingToCompareTo(SplitConfigurationFilter.fromFilenameSuffix("c-v13")); // if the only difference is split flag vs. filename suffix they will compare equal assertThat(SplitConfigurationFilter.fromSplitFlag("split,split")) .isEquivalentAccordingToCompareTo( SplitConfigurationFilter.fromFilenameSuffix("split_split")); } @Test public void splitConfigurationFilterEqualsShouldPassEqualsTester() { new EqualsTester() .addEqualityGroup( // base example SplitConfigurationFilter.fromFilenameSuffix("abc-def_ghi_jkl"), // identical clone SplitConfigurationFilter.fromFilenameSuffix("abc-def_ghi_jkl"), // commas are converted for split flags, the result is equal SplitConfigurationFilter.fromSplitFlag("abc-def,ghi,jkl")) .addEqualityGroup( // case matters for filenames SplitConfigurationFilter.fromFilenameSuffix("aBC-dEF_ghi_jkl")) .addEqualityGroup( // different filename producing equal set (elements parse the same) is still different SplitConfigurationFilter.fromFilenameSuffix("abc-def-v0_ghi_jkl")) .addEqualityGroup( // different filename producing equal set (input order is different) is still different SplitConfigurationFilter.fromFilenameSuffix("ghi_abc-def_jkl")) .addEqualityGroup( // totally different set is also very clearly different SplitConfigurationFilter.fromFilenameSuffix("some-other_specifiers")) .testEquals(); } @Test public void equalInputsShouldMatchConfigurationFromFilename() { // identical inputs should naturally match assertMatchesConfigurationFromFilename("abc-def", "abc-def").isTrue(); assertMatchesConfigurationFromFilename("abc-def-v5", "abc-def-v5").isTrue(); // case should be ignored assertMatchesConfigurationFromFilename("abc-dEF", "aBC-def").isTrue(); assertMatchesConfigurationFromFilename("aBC-def", "abc-dEF").isTrue(); // wildcards should be ignored assertMatchesConfigurationFromFilename("any-abc-def", "abc-any-def").isTrue(); assertMatchesConfigurationFromFilename("abc-any-def", "any-abc-def").isTrue(); // v0 should be ignored assertMatchesConfigurationFromFilename("abc-def", "abc-def-v0").isTrue(); assertMatchesConfigurationFromFilename("abc-def-v0", "abc-def").isTrue(); } @Test public void higherVersionSameSpecifiersShouldMatchConfigurationFromFilename() { // any version is higher than the default v0 assertMatchesConfigurationFromFilename("abc-def", "abc-def-v1").isTrue(); assertMatchesConfigurationFromFilename("abc-def-v1", "abc-def").isFalse(); // same deal for explicit v0 assertMatchesConfigurationFromFilename("abc-def-v0", "abc-def-v1").isTrue(); assertMatchesConfigurationFromFilename("abc-def-v1", "abc-def-v0").isFalse(); // higher versions are better of course assertMatchesConfigurationFromFilename("abc-def-v1", "abc-def-v999").isTrue(); assertMatchesConfigurationFromFilename("abc-def-v999", "abc-def-v1").isFalse(); } @Test public void nonMatchingSpecifiersShouldNotMatchConfigurationFromFilename() { // same version assertMatchesConfigurationFromFilename("abc", "ghi").isFalse(); assertMatchesConfigurationFromFilename("abc-v0", "ghi-v0").isFalse(); assertMatchesConfigurationFromFilename("abc-v15", "ghi-v15").isFalse(); // different version assertMatchesConfigurationFromFilename("abc", "ghi-v15").isFalse(); assertMatchesConfigurationFromFilename("abc-v0", "ghi-v15").isFalse(); assertMatchesConfigurationFromFilename("abc-v15", "ghi-v19").isFalse(); } @CheckReturnValue private BooleanSubject assertMatchesConfigurationFromFilename( String flagConfiguration, String fileConfiguration) { return assertWithMessage( "The split flag '%s' would be a match for a filename containing '%s'", flagConfiguration, fileConfiguration) .that( ResourceConfiguration.fromString(flagConfiguration) .matchesConfigurationFromFilename( ResourceConfiguration.fromString(fileConfiguration))); } @Test public void resourceConfigurationShouldBeOrderedByApiVersionThenSpecifiers() { assertThat( ImmutableList.of( // -v0 and no -v0 sort equal given the same specifiers, so it's a tie ResourceConfiguration.fromString("a"), // version ties are broken based on lexicographic string ordering ResourceConfiguration.fromString("b-v0"), // string ordering ignores case of the specifiers ResourceConfiguration.fromString("Z"), // higher API versions sort later regardless of the specifiers ResourceConfiguration.fromString("z-v6"), ResourceConfiguration.fromString("b-v13"), // wildcards ("any") are ignored when considering specifier order ResourceConfiguration.fromString("any-c-v13"))) .isStrictlyOrdered(); // if they are actually equal, they will compare equal assertThat(ResourceConfiguration.fromString("c-v13")) .isEquivalentAccordingToCompareTo(ResourceConfiguration.fromString("c-v13")); // if the only difference is wildcards they will compare equal assertThat(ResourceConfiguration.fromString("any-c-v13")) .isEquivalentAccordingToCompareTo(ResourceConfiguration.fromString("c-v13")); // if the only difference is specifying (or not specifying) -v0 they will compare equal assertThat(ResourceConfiguration.fromString("a-v0")) .isEquivalentAccordingToCompareTo(ResourceConfiguration.fromString("a")); // if the only difference is case they will compare equal assertThat(ResourceConfiguration.fromString("z")) .isEquivalentAccordingToCompareTo(ResourceConfiguration.fromString("Z")); } @Test public void resourceConfigurationShouldPassEqualsTester() { new EqualsTester() .addEqualityGroup( // base example ResourceConfiguration.fromString("abc-def"), // identical clone ResourceConfiguration.fromString("abc-def"), // case doesn't matter ResourceConfiguration.fromString("aBC-dEF"), // absent version code is equivalent to -v0 ResourceConfiguration.fromString("abc-def-v0"), // skip "any" ResourceConfiguration.fromString("any-abc-any-def-any")) .addEqualityGroup( // order matters ResourceConfiguration.fromString("def-abc")) .addEqualityGroup( // empty segments are not collapsed ResourceConfiguration.fromString("abc---def")) .addEqualityGroup( // different version codes are parsed but result in a non-equal instance ResourceConfiguration.fromString("abc-def-v15")) .addEqualityGroup( // two configurations with version codes must also have EQUAL version codes ResourceConfiguration.fromString("abc-def-v12")) .addEqualityGroup( // empty strings are equal ResourceConfiguration.fromString(""), // yes, even if they have a v0 ResourceConfiguration.fromString("v0"), // or if there are some wildcards which will collapse to nothing ResourceConfiguration.fromString("any"), ResourceConfiguration.fromString("any-v0"), ResourceConfiguration.fromString("any-any-any"), ResourceConfiguration.fromString("any-any-any-v0")) .addEqualityGroup( // not if the version is nonzero though ResourceConfiguration.fromString("v15"), ResourceConfiguration.fromString("any-v15")) .testEquals(); } }