Tests: Simplify VersionUtils released version splitting (#30322)

This commit refactors VersionUtils.resolveReleasedVersions to be
simpler, and in the process fixes the behavior to match that of
VersionCollection.groovy.

closes #30133
This commit is contained in:
Ryan Ernst 2018-05-02 09:29:35 -07:00 committed by GitHub
parent af45b4dee4
commit f0e92676b1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 95 additions and 109 deletions

View File

@ -19,26 +19,25 @@
package org.elasticsearch.test;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Optional;
import java.util.Random;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.elasticsearch.Version;
import org.elasticsearch.common.Booleans;
import org.elasticsearch.common.Nullable;
import org.elasticsearch.common.collect.Tuple;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.Random;
import java.util.stream.Collectors;
import static java.util.Collections.singletonList;
import static java.util.Collections.unmodifiableList;
/** Utilities for selecting versions in tests */
public class VersionUtils {
/**
* Sort versions that have backwards compatibility guarantees from
* those that don't. Doesn't actually check whether or not the versions
@ -50,73 +49,65 @@ public class VersionUtils {
* guarantees in v1 and versions without the guranteees in v2
*/
static Tuple<List<Version>, List<Version>> resolveReleasedVersions(Version current, Class<?> versionClass) {
List<Version> versions = Version.getDeclaredVersions(versionClass);
// group versions into major version
Map<Integer, List<Version>> majorVersions = Version.getDeclaredVersions(versionClass).stream()
.collect(Collectors.groupingBy(v -> (int)v.major));
// this breaks b/c 5.x is still in version list but master doesn't care about it!
//assert majorVersions.size() == 2;
// TODO: remove oldVersions, we should only ever have 2 majors in Version
List<Version> oldVersions = majorVersions.getOrDefault((int)current.major - 2, Collections.emptyList());
List<List<Version>> previousMajor = splitByMinor(majorVersions.get((int)current.major - 1));
List<List<Version>> currentMajor = splitByMinor(majorVersions.get((int)current.major));
Version last = versions.remove(versions.size() - 1);
assert last.equals(current) : "The highest version must be the current one "
+ "but was [" + last + "] and current was [" + current + "]";
if (current.revision != 0) {
/* If we are in a stable branch there should be no unreleased version constants
* because we don't expect to release any new versions in older branches. If there
* are extra constants then gradle will yell about it. */
return new Tuple<>(unmodifiableList(versions), singletonList(current));
List<Version> unreleasedVersions = new ArrayList<>();
final List<List<Version>> stableVersions;
if (currentMajor.size() == 1) {
// on master branch
stableVersions = previousMajor;
// remove current
moveLastToUnreleased(currentMajor, unreleasedVersions);
} else {
// on a stable or release branch, ie N.x
stableVersions = currentMajor;
// remove the next maintenance bugfix
moveLastToUnreleased(previousMajor, unreleasedVersions);
}
/* If we are on a patch release then we know that at least the version before the
* current one is unreleased. If it is released then gradle would be complaining. */
int unreleasedIndex = versions.size() - 1;
while (true) {
if (unreleasedIndex < 0) {
throw new IllegalArgumentException("Couldn't find first non-alpha release");
// remove next minor
Version lastMinor = moveLastToUnreleased(stableVersions, unreleasedVersions);
if (lastMinor.revision == 0) {
if (stableVersions.get(stableVersions.size() - 1).size() == 1) {
// a minor is being staged, which is also unreleased
moveLastToUnreleased(stableVersions, unreleasedVersions);
}
/* We don't support backwards compatibility for alphas, betas, and rcs. But
* they were released so we add them to the released list. Usually this doesn't
* matter to consumers, but consumers that do care should filter non-release
* versions. */
if (versions.get(unreleasedIndex).isRelease()) {
break;
}
unreleasedIndex--;
// remove the next bugfix
moveLastToUnreleased(stableVersions, unreleasedVersions);
}
Version unreleased = versions.remove(unreleasedIndex);
if (unreleased.revision == 0) {
/*
* If the last unreleased version is itself a patch release then Gradle enforces that there is yet another unreleased version
* before that. However, we have to skip alpha/betas/RCs too (e.g., consider when the version constants are ..., 5.6.3, 5.6.4,
* 6.0.0-alpha1, ..., 6.0.0-rc1, 6.0.0-rc2, 6.0.0, 6.1.0 on the 6.x branch. In this case, we will have pruned 6.0.0 and 6.1.0 as
* unreleased versions, but we also need to prune 5.6.4. At this point though, unreleasedIndex will be pointing to 6.0.0-rc2, so
* we have to skip backwards until we find a non-alpha/beta/RC again. Then we can prune that version as an unreleased version
* too.
*/
do {
unreleasedIndex--;
} while (versions.get(unreleasedIndex).isRelease() == false);
Version earlierUnreleased = versions.remove(unreleasedIndex);
List<Version> releasedVersions = Stream.concat(oldVersions.stream(),
Stream.concat(previousMajor.stream(), currentMajor.stream()).flatMap(List::stream))
.collect(Collectors.toList());
Collections.sort(unreleasedVersions); // we add unreleased out of order, so need to sort here
return new Tuple<>(Collections.unmodifiableList(releasedVersions), Collections.unmodifiableList(unreleasedVersions));
}
// This earlierUnreleased is either the snapshot on the minor branch lower, or its possible its a staged release. If it is a
// staged release, remove it and return it in unreleased as well.
if (earlierUnreleased.revision == 0) {
unreleasedIndex--;
Version actualUnreleasedPreviousMinor = versions.remove(unreleasedIndex);
return new Tuple<>(unmodifiableList(versions), unmodifiableList(Arrays.asList(actualUnreleasedPreviousMinor,
earlierUnreleased, unreleased, current)));
}
// split the given versions into sub lists grouped by minor version
private static List<List<Version>> splitByMinor(List<Version> versions) {
Map<Integer, List<Version>> byMinor = versions.stream().collect(Collectors.groupingBy(v -> (int)v.minor));
return byMinor.entrySet().stream().sorted(Map.Entry.comparingByKey()).map(Map.Entry::getValue).collect(Collectors.toList());
}
return new Tuple<>(unmodifiableList(versions), unmodifiableList(Arrays.asList(earlierUnreleased, unreleased, current)));
} else if (unreleased.major == current.major) {
// need to remove one more of the last major's minor set
do {
unreleasedIndex--;
} while (unreleasedIndex > 0 && versions.get(unreleasedIndex).major == current.major);
if (unreleasedIndex > 0) {
// some of the test cases return very small lists, so its possible this is just the end of the list, if so, dont include it
Version earlierMajorsMinor = versions.remove(unreleasedIndex);
return new Tuple<>(unmodifiableList(versions), unmodifiableList(Arrays.asList(earlierMajorsMinor, unreleased, current)));
}
// move the last version of the last minor in versions to the unreleased versions
private static Version moveLastToUnreleased(List<List<Version>> versions, List<Version> unreleasedVersions) {
List<Version> lastMinor = new ArrayList<>(versions.get(versions.size() - 1));
Version lastVersion = lastMinor.remove(lastMinor.size() - 1);
if (lastMinor.isEmpty()) {
versions.remove(versions.size() - 1);
} else {
versions.set(versions.size() - 1, lastMinor);
}
return new Tuple<>(unmodifiableList(versions), unmodifiableList(Arrays.asList(unreleased, current)));
unreleasedVersions.add(lastVersion);
return lastVersion;
}
private static final List<Version> RELEASED_VERSIONS;
@ -131,7 +122,7 @@ public class VersionUtils {
allVersions.addAll(RELEASED_VERSIONS);
allVersions.addAll(UNRELEASED_VERSIONS);
Collections.sort(allVersions);
ALL_VERSIONS = unmodifiableList(allVersions);
ALL_VERSIONS = Collections.unmodifiableList(allVersions);
}
/**
@ -244,5 +235,4 @@ public class VersionUtils {
assert compatible.size() > 0;
return compatible.get(compatible.size() - 1);
}
}

View File

@ -49,6 +49,7 @@ public class VersionUtilsTests extends ESTestCase {
}
public void testRandomVersionBetween() {
// TODO: rework this test to use a dummy Version class so these don't need to change with each release
// full range
Version got = VersionUtils.randomVersionBetween(random(), VersionUtils.getFirstVersion(), Version.CURRENT);
assertTrue(got.onOrAfter(VersionUtils.getFirstVersion()));
@ -61,10 +62,10 @@ public class VersionUtilsTests extends ESTestCase {
assertTrue(got.onOrBefore(Version.CURRENT));
// sub range
got = VersionUtils.randomVersionBetween(random(), Version.V_5_0_0,
Version.V_6_0_0_beta1);
assertTrue(got.onOrAfter(Version.V_5_0_0));
assertTrue(got.onOrBefore(Version.V_6_0_0_beta1));
got = VersionUtils.randomVersionBetween(random(), Version.V_6_0_0_alpha1,
Version.V_6_2_4);
assertTrue(got.onOrAfter(Version.V_6_0_0_alpha1));
assertTrue(got.onOrBefore(Version.V_6_2_4));
// unbounded lower
got = VersionUtils.randomVersionBetween(random(), null, Version.V_6_0_0_beta1);
@ -75,8 +76,8 @@ public class VersionUtilsTests extends ESTestCase {
assertTrue(got.onOrBefore(VersionUtils.allReleasedVersions().get(0)));
// unbounded upper
got = VersionUtils.randomVersionBetween(random(), Version.V_5_0_0, null);
assertTrue(got.onOrAfter(Version.V_5_0_0));
got = VersionUtils.randomVersionBetween(random(), Version.V_6_0_0, null);
assertTrue(got.onOrAfter(Version.V_6_0_0));
assertTrue(got.onOrBefore(Version.CURRENT));
got = VersionUtils.randomVersionBetween(random(), VersionUtils.getPreviousVersion(), null);
assertTrue(got.onOrAfter(VersionUtils.getPreviousVersion()));
@ -107,6 +108,8 @@ public class VersionUtilsTests extends ESTestCase {
}
public static class TestReleaseBranch {
public static final Version V_4_0_0 = Version.fromString("4.0.0");
public static final Version V_4_0_1 = Version.fromString("4.0.1");
public static final Version V_5_3_0 = Version.fromString("5.3.0");
public static final Version V_5_3_1 = Version.fromString("5.3.1");
public static final Version V_5_3_2 = Version.fromString("5.3.2");
@ -120,19 +123,24 @@ public class VersionUtilsTests extends ESTestCase {
List<Version> unreleased = t.v2();
assertThat(released, equalTo(Arrays.asList(
TestReleaseBranch.V_4_0_0,
TestReleaseBranch.V_5_3_0,
TestReleaseBranch.V_5_3_1,
TestReleaseBranch.V_5_3_2,
TestReleaseBranch.V_5_4_0)));
assertThat(unreleased, equalTo(Collections.singletonList(TestReleaseBranch.V_5_4_1)));
assertThat(unreleased, equalTo(Arrays.asList(
TestReleaseBranch.V_4_0_1,
TestReleaseBranch.V_5_4_1)));
}
public static class TestStableBranch {
public static final Version V_5_3_0 = Version.fromString("5.3.0");
public static final Version V_5_3_1 = Version.fromString("5.3.1");
public static final Version V_5_3_2 = Version.fromString("5.3.2");
public static final Version V_5_4_0 = Version.fromString("5.4.0");
public static final Version CURRENT = V_5_4_0;
public static final Version V_4_0_0 = Version.fromString("4.0.0");
public static final Version V_4_0_1 = Version.fromString("4.0.1");
public static final Version V_5_0_0 = Version.fromString("5.0.0");
public static final Version V_5_0_1 = Version.fromString("5.0.1");
public static final Version V_5_0_2 = Version.fromString("5.0.2");
public static final Version V_5_1_0 = Version.fromString("5.1.0");
public static final Version CURRENT = V_5_1_0;
}
public void testResolveReleasedVersionsForUnreleasedStableBranch() {
Tuple<List<Version>, List<Version>> t = VersionUtils.resolveReleasedVersions(TestStableBranch.CURRENT,
@ -141,14 +149,18 @@ public class VersionUtilsTests extends ESTestCase {
List<Version> unreleased = t.v2();
assertThat(released, equalTo(Arrays.asList(
TestStableBranch.V_5_3_0,
TestStableBranch.V_5_3_1)));
TestStableBranch.V_4_0_0,
TestStableBranch.V_5_0_0,
TestStableBranch.V_5_0_1)));
assertThat(unreleased, equalTo(Arrays.asList(
TestStableBranch.V_5_3_2,
TestStableBranch.V_5_4_0)));
TestStableBranch.V_4_0_1,
TestStableBranch.V_5_0_2,
TestStableBranch.V_5_1_0)));
}
public static class TestStableBranchBehindStableBranch {
public static final Version V_4_0_0 = Version.fromString("4.0.0");
public static final Version V_4_0_1 = Version.fromString("4.0.1");
public static final Version V_5_3_0 = Version.fromString("5.3.0");
public static final Version V_5_3_1 = Version.fromString("5.3.1");
public static final Version V_5_3_2 = Version.fromString("5.3.2");
@ -163,9 +175,11 @@ public class VersionUtilsTests extends ESTestCase {
List<Version> unreleased = t.v2();
assertThat(released, equalTo(Arrays.asList(
TestStableBranchBehindStableBranch.V_4_0_0,
TestStableBranchBehindStableBranch.V_5_3_0,
TestStableBranchBehindStableBranch.V_5_3_1)));
assertThat(unreleased, equalTo(Arrays.asList(
TestStableBranchBehindStableBranch.V_4_0_1,
TestStableBranchBehindStableBranch.V_5_3_2,
TestStableBranchBehindStableBranch.V_5_4_0,
TestStableBranchBehindStableBranch.V_5_5_0)));
@ -221,13 +235,13 @@ public class VersionUtilsTests extends ESTestCase {
assertThat(released, equalTo(Arrays.asList(
TestNewMajorRelease.V_5_6_0,
TestNewMajorRelease.V_5_6_1,
TestNewMajorRelease.V_5_6_2,
TestNewMajorRelease.V_6_0_0_alpha1,
TestNewMajorRelease.V_6_0_0_alpha2,
TestNewMajorRelease.V_6_0_0_beta1,
TestNewMajorRelease.V_6_0_0_beta2,
TestNewMajorRelease.V_6_0_0)));
assertThat(unreleased, equalTo(Arrays.asList(
TestNewMajorRelease.V_5_6_2,
TestNewMajorRelease.V_6_0_1)));
}
@ -305,24 +319,6 @@ public class VersionUtilsTests extends ESTestCase {
TestNewMinorBranchIn6x.V_6_2_0)));
}
public static class TestIncorrectCurrentVersion {
public static final Version V_5_3_0 = Version.fromString("5.3.0");
public static final Version V_5_3_1 = Version.fromString("5.3.1");
public static final Version V_5_4_0 = Version.fromString("5.4.0");
public static final Version V_5_4_1 = Version.fromString("5.4.1");
public static final Version CURRENT = V_5_4_1;
}
public void testIncorrectCurrentVersion() {
Version previousVersion = TestIncorrectCurrentVersion.V_5_4_0;
AssertionError error = expectThrows(AssertionError.class, () ->
VersionUtils.resolveReleasedVersions(previousVersion, TestIncorrectCurrentVersion.class));
String message = error.getMessage();
assertThat(message, containsString(TestIncorrectCurrentVersion.CURRENT.toString()));
assertThat(message, containsString(previousVersion.toString()));
}
/**
* Tests that {@link Version#minimumCompatibilityVersion()} and {@link VersionUtils#allReleasedVersions()}
* agree with the list of wire and index compatible versions we build in gradle.