diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 35613b0b26c7..6ce15c5baf3d 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -46,6 +46,9 @@ concurrency: env: _JAVA_OPTIONS: '-XX:GCTimeLimit=90 -XX:GCHeapFreeLimit=35' DEVELOCITY_ACCESS_KEY: ${{ secrets.GE_ACCESS_TOKEN }} + GUAVA_MIN: '21.0' # oldest supported Guava version + GUAVA_MAX: '33.3.0-jre' # latest supported Guava version + GUAVA: '21.0' # most jobs test against oldest Guava version jobs: windows-jdk8: @@ -140,11 +143,10 @@ jobs: linux-jdk8-oldest-guava-tz: if: github.event.action != 'labeled' - name: 'Linux (JDK 8), oldest Guava, America/New_York Timezone' + name: 'Linux (JDK 8, oldest Guava, America/New_York Timezone)' runs-on: ubuntu-latest env: TZ: 'America/New_York' # flips between −05:00 and −04:00 - GUAVA: '21.0' # oldest Guava steps: - uses: actions/checkout@v3 with: @@ -159,15 +161,14 @@ jobs: with: job-id: jdk${{ matrix.jdk }} remote-build-cache-proxy-enabled: false - arguments: --scan --no-parallel --no-daemon -Pguava.version=${{ env.GUAVA }} build + arguments: --scan --no-parallel --no-daemon -Pguava.version=${{ env.GUAVA_MIN }} build linux-jdk8-latest-guava-tz: if: github.event.action != 'labeled' - name: 'Linux (JDK 8), latest Guava, America/New_York Timezone' + name: 'Linux (JDK 8, latest Guava, America/New_York Timezone)' runs-on: ubuntu-latest env: TZ: 'America/New_York' # flips between −05:00 and −04:00 - GUAVA: '32.1.3-jre' # latest supported Guava version steps: - uses: actions/checkout@v3 with: @@ -182,11 +183,11 @@ jobs: with: job-id: jdk${{ matrix.jdk }} remote-build-cache-proxy-enabled: false - arguments: --scan --no-parallel --no-daemon -Pguava.version=${{ env.GUAVA }} tasks build + arguments: --scan --no-parallel --no-daemon -Pguava.version=${{ env.GUAVA_MAX }} tasks build linux-jdk11-tz: if: github.event.action != 'labeled' - name: 'Linux (JDK 11), Pacific/Chatham Timezone' + name: 'Linux (JDK 11, Pacific/Chatham Timezone)' runs-on: ubuntu-latest env: TZ: 'Pacific/Chatham' # flips between +12:45 and +13:45 @@ -210,8 +211,6 @@ jobs: if: github.event.action != 'labeled' name: 'Linux (JDK 17)' runs-on: ubuntu-latest - env: - GUAVA: '21.0' # oldest Guava steps: - uses: actions/checkout@v3 with: @@ -232,8 +231,6 @@ jobs: if: github.event.action != 'labeled' name: 'Linux (JDK 21)' runs-on: ubuntu-latest - env: - GUAVA: '21.0' # oldest Guava steps: - uses: actions/checkout@v3 with: @@ -250,20 +247,18 @@ jobs: remote-build-cache-proxy-enabled: false arguments: --scan --no-parallel --no-daemon -Pguava.version=${{ env.GUAVA }} build - linux-jdk22: # latest JDK version supported by ForbiddenAPIs plugin, keep this updated (see https://jdk.java.net/) + linux-jdk23: # latest JDK version supported by ForbiddenAPIs plugin, keep this updated (see https://jdk.java.net/) if: github.event.action != 'labeled' - name: 'Linux (JDK 22)' + name: 'Linux (JDK 23)' runs-on: ubuntu-latest - env: - GUAVA: '21.0' # oldest Guava steps: - uses: actions/checkout@v3 with: fetch-depth: 50 - - name: 'Set up JDK 22' + - name: 'Set up JDK 23' uses: actions/setup-java@v2 with: - java-version: 22 + java-version: 23 distribution: 'zulu' - uses: burrunan/gradle-cache-action@v1 name: Test @@ -274,7 +269,7 @@ jobs: linux-avatica: if: github.event.action != 'labeled' - name: 'Linux (JDK 11), Avatica main' + name: 'Linux (JDK 11, Avatica main)' runs-on: ubuntu-latest steps: - name: 'Set up JDK 11' @@ -338,12 +333,10 @@ jobs: echo sqlsh ./sqlsh -o headers "select count(*) commits, author from (select substring(author, 1, position(' <' in author)-1) author from git_commits) group by author order by count(*) desc, author limit 20" - errorprone-guava-latest: # LTS JDK version, don't remove until EOL + errorprone-guava-latest: if: github.event.action != 'labeled' - name: 'Error Prone (JDK 11), latest Guava' + name: 'ErrorProne (JDK 11, latest Guava)' runs-on: ubuntu-latest - env: - GUAVA: '32.1.3-jre' # ErrorProne checks for Beta APIs, so use the latest supported Guava version steps: - uses: actions/checkout@v3 with: @@ -358,7 +351,8 @@ jobs: with: job-id: errprone remote-build-cache-proxy-enabled: false - arguments: --scan --no-parallel --no-daemon -Pguava.version=${{ env.GUAVA }} -PenableErrorprone classes + # ErrorProne checks for Beta APIs, so use the latest supported Guava version + arguments: --scan --no-parallel --no-daemon -Pguava.version=${{ env.GUAVA_MAX }} -PenableErrorprone classes linux-checkerframework: name: 'CheckerFramework (JDK 11)' @@ -380,10 +374,8 @@ jobs: arguments: --scan --no-parallel --no-daemon -PenableCheckerframework :linq4j:classes :core:classes :server:classes linux-checkerframework-oldest-guava: - name: 'CheckerFramework (JDK 11), oldest Guava' + name: 'CheckerFramework (JDK 11, oldest Guava)' runs-on: ubuntu-latest - env: - GUAVA: '21.0' # oldest Guava steps: - uses: actions/checkout@v3 with: @@ -398,7 +390,7 @@ jobs: with: job-id: checkerframework-jdk11 remote-build-cache-proxy-enabled: false - arguments: --scan --no-parallel --no-daemon -Pguava.version=${{ env.GUAVA }} -PenableCheckerframework :linq4j:classes :core:classes :server:classes + arguments: --scan --no-parallel --no-daemon -Pguava.version=${{ env.GUAVA_MIN }} -PenableCheckerframework :linq4j:classes :core:classes :server:classes linux-slow: # Run slow tests when the commit is on main or it is requested explicitly by adding an diff --git a/core/src/test/java/org/apache/calcite/jdbc/CalciteRemoteDriverTest.java b/core/src/test/java/org/apache/calcite/jdbc/CalciteRemoteDriverTest.java index 4bf27e26413f..a7184d7c0124 100644 --- a/core/src/test/java/org/apache/calcite/jdbc/CalciteRemoteDriverTest.java +++ b/core/src/test/java/org/apache/calcite/jdbc/CalciteRemoteDriverTest.java @@ -31,6 +31,7 @@ import org.apache.calcite.test.schemata.hr.Employee; import org.apache.calcite.util.TestUtil; import org.apache.calcite.util.Util; +import org.apache.calcite.util.Version; import com.google.common.collect.ImmutableList; @@ -82,6 +83,7 @@ import static org.hamcrest.CoreMatchers.nullValue; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assumptions.assumeFalse; import static java.util.Objects.requireNonNull; @@ -106,6 +108,11 @@ class CalciteRemoteDriverTest { private static @Nullable HttpServer start; @BeforeAll public static void beforeClass() throws Exception { + assumeFalse(TestUtil.getJavaMajorVersion() >= 23 + && TestUtil.AVATICA_VERSION.compareTo(Version.of(1, 25)) > 0, + "Cannot run on JDK 23 and higher with Avatica version 1.25 or lower; " + + "enable when [CALCITE-6588] is fixed in Avatica"); + localConnection = CalciteAssert.hr().connect(); // Make sure we pick an ephemeral port for the server diff --git a/core/src/test/java/org/apache/calcite/util/UtilTest.java b/core/src/test/java/org/apache/calcite/util/UtilTest.java index 882b276e9bc6..fbca0845a439 100644 --- a/core/src/test/java/org/apache/calcite/util/UtilTest.java +++ b/core/src/test/java/org/apache/calcite/util/UtilTest.java @@ -110,6 +110,7 @@ import static org.apache.calcite.util.ReflectUtil.isStatic; import static org.hamcrest.CoreMatchers.allOf; +import static org.hamcrest.CoreMatchers.anyOf; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.instanceOf; import static org.hamcrest.CoreMatchers.is; @@ -643,30 +644,28 @@ private List makeConsList(int start, int end) { * Tests the {@link Util#toPosix(TimeZone, boolean)} method. */ @Test void testPosixTimeZone() { - // NOTE jvs 31-July-2007: First two tests are disabled since - // not everyone may have patched their system yet for recent - // DST change. - // Pacific Standard Time. Effective 2007, the local time changes from // PST to PDT at 02:00 LST to 03:00 LDT on the second Sunday in March // and returns at 02:00 LDT to 01:00 LST on the first Sunday in // November. - if (false) { - assertEquals( - "PST-8PDT,M3.2.0,M11.1.0", - Util.toPosix(TimeZone.getTimeZone("PST"), false)); + assertThat(Util.toPosix(TimeZone.getTimeZone("PST"), false), + anyOf(is("PST-8PDT,M3.2.0,M11.1.0"), + is("GMT-08:00-8GMT-07:00,M3.2.0,M11.1.0"))); - assertEquals( - "PST-8PDT1,M3.2.0/2,M11.1.0/2", - Util.toPosix(TimeZone.getTimeZone("PST"), true)); - } + assertThat(Util.toPosix(TimeZone.getTimeZone("PST"), true), + anyOf(is("PST-8PDT1,M3.2.0/2,M11.1.0/2"), + is("GMT-08:00-8GMT-07:001,M3.2.0/2,M11.1.0/2"))); // Tokyo has +ve offset, no DST - assertEquals( - "JST9", - Util.toPosix(TimeZone.getTimeZone("Asia/Tokyo"), true)); - - // Sydney, Australia lies ten hours east of GMT and makes a one hour + assertThat( + Util.toPosix(TimeZone.getTimeZone("Asia/Tokyo"), true), + anyOf( + // Before JDK 23 + is("JST9"), + // JDK 23 and later + is("GMT+09:009"))); + + // Sydney, Australia lies ten hours east of GMT and makes a one-hour // shift forward during daylight savings. Being located in the southern // hemisphere, daylight savings begins on the last Sunday in October at // 2am and ends on the last Sunday in March at 3am. @@ -676,28 +675,24 @@ private List makeConsList(int start, int end) { // have a different (older and incorrect) timezone settings for // Australia. So we test for the older one first then do the // correct assert based upon what the toPosix method returns - String posixTime = - Util.toPosix(TimeZone.getTimeZone("Australia/Sydney"), true); - - if (posixTime.equals("EST10EST1,M10.5.0/2,M3.5.0/3")) { - // very old JVMs without the fix - assertEquals("EST10EST1,M10.5.0/2,M3.5.0/3", posixTime); - } else if (posixTime.equals("EST10EST1,M10.1.0/2,M4.1.0/3")) { - // old JVMs without the fix - assertEquals("EST10EST1,M10.1.0/2,M4.1.0/3", posixTime); - } else { - // newer JVMs with the fix - assertEquals("AEST10AEDT1,M10.1.0/2,M4.1.0/3", posixTime); - } + assertThat(Util.toPosix(TimeZone.getTimeZone("Australia/Sydney"), true), + anyOf( + // very old JVMs without the fix + is("EST10EST1,M10.5.0/2,M3.5.0/3"), + // old JVMs without the fix + is("EST10EST1,M10.1.0/2,M4.1.0/3"), + // newer JVMs with the fix + is("AEST10AEDT1,M10.1.0/2,M4.1.0/3"), + // JDK 23 and later + is("GMT+10:0010GMT+11:001,M10.1.0/2,M4.1.0/3"))); // Paris, France. (Uses UTC_TIME time-transition mode.) - assertEquals( - "CET1CEST1,M3.5.0/2,M10.5.0/3", - Util.toPosix(TimeZone.getTimeZone("Europe/Paris"), true)); + assertThat(Util.toPosix(TimeZone.getTimeZone("Europe/Paris"), true), + anyOf(is("CET1CEST1,M3.5.0/2,M10.5.0/3"), + is("GMT+01:001GMT+02:001,M3.5.0/2,M10.5.0/3"))); - assertEquals( - "UTC0", - Util.toPosix(TimeZone.getTimeZone("UTC"), true)); + assertThat(Util.toPosix(TimeZone.getTimeZone("UTC"), true), + is("UTC0")); } /** diff --git a/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/PredicateAnalyzer.java b/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/PredicateAnalyzer.java index 34efb523d875..a1d55633f7db 100644 --- a/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/PredicateAnalyzer.java +++ b/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/PredicateAnalyzer.java @@ -117,7 +117,7 @@ static QueryBuilder analyze(RexNode expression) throws ExpressionNotAnalyzableEx } return e != null ? e.builder() : null; } catch (Throwable e) { - Throwables.propagateIfPossible(e, UnsupportedOperationException.class); + Throwables.throwIfInstanceOf(e, UnsupportedOperationException.class); throw new ExpressionNotAnalyzableException("Can't convert " + expression, e); } } diff --git a/gradle.properties b/gradle.properties index a0004d1fc812..90b2c8401118 100644 --- a/gradle.properties +++ b/gradle.properties @@ -109,9 +109,9 @@ foodmart-data-hsqldb.version=0.5 foodmart-data-json.version=0.4 foodmart-queries.version=0.4.1 geode-core.version=1.15.1 -guava.version=32.1.3-jre +guava.version=33.3.0-jre h2.version=2.1.210 -hadoop.version=2.7.5 +hadoop.version=2.10.2 hamcrest-date.version=2.0.4 hamcrest.version=2.1 hsqldb.version=2.7.2 diff --git a/piglet/src/test/java/org/apache/calcite/test/PigRelTestBase.java b/piglet/src/test/java/org/apache/calcite/test/PigRelTestBase.java index b2bff1dbc37d..9aaf9675ca96 100644 --- a/piglet/src/test/java/org/apache/calcite/test/PigRelTestBase.java +++ b/piglet/src/test/java/org/apache/calcite/test/PigRelTestBase.java @@ -19,6 +19,7 @@ import org.apache.calcite.piglet.PigConverter; import org.apache.calcite.rel.RelNode; import org.apache.calcite.tools.FrameworkConfig; +import org.apache.calcite.util.TestUtil; import org.junit.jupiter.api.BeforeEach; @@ -39,6 +40,8 @@ public abstract class PigRelTestBase { public void testSetup() throws Exception { assumeFalse(getProperty("os.name").startsWith("Windows"), "Skip: Pig/Hadoop tests do not work on Windows"); + assumeFalse(TestUtil.getJavaMajorVersion() >= 23, + "Skip: Pig/Hadoop tests do not work on JDK 23 and higher"); final FrameworkConfig config = config().build(); converter = create(config); diff --git a/plus/src/test/java/org/apache/calcite/chinook/RemotePreparedStatementParametersTest.java b/plus/src/test/java/org/apache/calcite/chinook/RemotePreparedStatementParametersTest.java index 77dd923d1cd6..50673152c470 100644 --- a/plus/src/test/java/org/apache/calcite/chinook/RemotePreparedStatementParametersTest.java +++ b/plus/src/test/java/org/apache/calcite/chinook/RemotePreparedStatementParametersTest.java @@ -16,6 +16,9 @@ */ package org.apache.calcite.chinook; +import org.apache.calcite.util.TestUtil; +import org.apache.calcite.util.Version; + import org.junit.jupiter.api.Test; import java.sql.Connection; @@ -23,6 +26,8 @@ import java.sql.PreparedStatement; import java.sql.ResultSet; +import static org.junit.jupiter.api.Assumptions.assumeFalse; + /** * Tests against parameters in prepared statement when using underlying JDBC * sub-schema. @@ -30,6 +35,11 @@ class RemotePreparedStatementParametersTest { @Test void testSimpleStringParameterShouldWorkWithCalcite() throws Exception { + assumeFalse(TestUtil.getJavaMajorVersion() >= 23 + && TestUtil.AVATICA_VERSION.compareTo(Version.of(1, 25)) > 0, + "Cannot run on JDK 23 and higher with Avatica version 1.25 or lower; " + + "enable when [CALCITE-6588] is fixed in Avatica"); + // given ChinookAvaticaServer server = new ChinookAvaticaServer(); server.startWithCalcite(); @@ -44,6 +54,11 @@ class RemotePreparedStatementParametersTest { } @Test void testSeveralParametersShouldWorkWithCalcite() throws Exception { + assumeFalse(TestUtil.getJavaMajorVersion() >= 23 + && TestUtil.AVATICA_VERSION.compareTo(Version.of(1, 25)) > 0, + "Cannot run on JDK 23 and higher with Avatica version 1.25 or lower; " + + "enable when [CALCITE-6588] is fixed in Avatica"); + // given ChinookAvaticaServer server = new ChinookAvaticaServer(); server.startWithCalcite(); @@ -60,6 +75,11 @@ class RemotePreparedStatementParametersTest { } @Test void testParametersShouldWorkWithRaw() throws Exception { + assumeFalse(TestUtil.getJavaMajorVersion() >= 23 + && TestUtil.AVATICA_VERSION.compareTo(Version.of(1, 25)) > 0, + "Cannot run on JDK 23 and higher with Avatica version 1.25 or lower; " + + "enable when [CALCITE-6588] is fixed in Avatica"); + // given ChinookAvaticaServer server = new ChinookAvaticaServer(); server.startWithRaw(); diff --git a/site/_docs/history.md b/site/_docs/history.md index b9440ae2c525..e341ac57ec7c 100644 --- a/site/_docs/history.md +++ b/site/_docs/history.md @@ -49,8 +49,8 @@ calculations that use DECIMAL values to produce slightly different results. Compatibility: This release is tested on Linux, macOS, Microsoft Windows; -using JDK/OpenJDK versions 8 to 19; -Guava versions 21.0 to 32.1.3-jre; +using JDK/OpenJDK versions 8 to 23; +Guava versions 21.0 to 33.3.0-jre; other software versions as specified in gradle.properties. #### New features diff --git a/spark/src/test/java/org/apache/calcite/test/SparkAdapterTest.java b/spark/src/test/java/org/apache/calcite/test/SparkAdapterTest.java index 468dee9463f7..ab18818c231e 100644 --- a/spark/src/test/java/org/apache/calcite/test/SparkAdapterTest.java +++ b/spark/src/test/java/org/apache/calcite/test/SparkAdapterTest.java @@ -17,11 +17,14 @@ package org.apache.calcite.test; import org.apache.calcite.adapter.spark.SparkRel; +import org.apache.calcite.util.TestUtil; import org.apache.calcite.util.Util; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assumptions.assumeFalse; + /** * Tests for using Calcite with Spark as an internal engine, as implemented by * the {@link org.apache.calcite.adapter.spark} package. @@ -42,6 +45,10 @@ class SparkAdapterTest { "(values (1, 'a'), (2, 'b'), (3, 'b'), (4, 'c'), (2, 'c')) as t(x, y)"; private CalciteAssert.AssertQuery sql(String sql) { + assumeFalse(TestUtil.getJavaMajorVersion() >= 23, + "Cannot run on JDK 23 and higher; " + + "enable when [HADOOP-19212] has been fixed"); + return CalciteAssert.that() .with(CalciteAssert.Config.SPARK) .query(sql); diff --git a/testkit/src/main/java/org/apache/calcite/util/TestUtil.java b/testkit/src/main/java/org/apache/calcite/util/TestUtil.java index 498adae10355..4987235f1923 100644 --- a/testkit/src/main/java/org/apache/calcite/util/TestUtil.java +++ b/testkit/src/main/java/org/apache/calcite/util/TestUtil.java @@ -33,6 +33,8 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; +import static org.apache.calcite.util.Util.first; + import static org.junit.jupiter.api.Assertions.fail; import static java.lang.Integer.parseInt; @@ -55,8 +57,9 @@ public abstract class TestUtil { private static final String JAVA_VERSION = System.getProperties().getProperty("java.version"); - public static final String AVATICA_VERSION = - System.getProperty("calcite.avatica.version"); + public static final Version AVATICA_VERSION = + Version.of(first(System.getProperty("calcite.avatica.version"), "0")); + private static final Supplier GUAVA_MAJOR_VERSION = Suppliers.memoize(TestUtil::computeGuavaMajorVersion); diff --git a/testkit/src/main/java/org/apache/calcite/util/Version.java b/testkit/src/main/java/org/apache/calcite/util/Version.java new file mode 100644 index 000000000000..850950a8f362 --- /dev/null +++ b/testkit/src/main/java/org/apache/calcite/util/Version.java @@ -0,0 +1,65 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you 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 org.apache.calcite.util; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Ordering; + +import java.util.ArrayList; +import java.util.List; + +import static java.lang.Integer.parseInt; + +/** + * Version string parsed into major and minor parts. + */ +public class Version implements Comparable { + private static final Ordering> ORDERING = + Ordering.natural().lexicographical(); + + public final List integers; + public final String string; + + /** Private constructor. */ + private Version(List integers, String string) { + this.integers = ImmutableList.copyOf(integers); + this.string = string; + } + + /** Creates a Version by parsing a string. */ + public static Version of(String s) { + final String[] strings = s.split("[.-]"); + List integers = new ArrayList<>(); + for (String string : strings) { + try { + integers.add(parseInt(string)); + } catch (NumberFormatException e) { + break; + } + } + return new Version(integers, s); + } + + /** Creates a Version from a sequence of integers. */ + public static Version of(int... integers) { + return new Version(ImmutableIntList.copyOf(integers), ""); + } + + @Override public int compareTo(Version version) { + return ORDERING.compare(this.integers, version.integers); + } +} diff --git a/testkit/src/test/java/org/apache/calcite/util/TestUtilTest.java b/testkit/src/test/java/org/apache/calcite/util/TestUtilTest.java index d8028a198f0e..587d3f2a9ae7 100644 --- a/testkit/src/test/java/org/apache/calcite/util/TestUtilTest.java +++ b/testkit/src/test/java/org/apache/calcite/util/TestUtilTest.java @@ -30,6 +30,9 @@ import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.empty; +import static org.hamcrest.Matchers.emptyString; +import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.hasToString; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -101,6 +104,50 @@ private void testJavaVersion(int expectedMajorVersion, String versionString) { versionString); } + /** Unit test for {@link Version}. */ + @SuppressWarnings("EqualsWithItself") + @Test void testVersion() { + Version vEmpty = Version.of(""); + assertThat(vEmpty.integers, empty()); + assertThat(vEmpty.string, emptyString()); + + final Version v1 = Version.of("1"); + assertThat(v1.integers, hasSize(1)); + assertThat(v1.integers, hasToString("[1]")); + + final Version v1_8_3 = Version.of("1.8.3-jre"); // "-jre" is ignored + assertThat(v1_8_3.integers, hasSize(3)); + assertThat(v1_8_3.integers, hasToString("[1, 8, 3]")); + assertThat(v1_8_3.string, is("1.8.3-jre")); + + final Version v1_19 = Version.of("1.19"); + assertThat(v1_19.integers, hasSize(2)); + assertThat(v1_19.integers, hasToString("[1, 19]")); + + final Version v1_23 = Version.of("1.23"); + assertThat(v1_23.integers, hasSize(2)); + assertThat(v1_23.integers, hasToString("[1, 23]")); + + final Version v1_23_0 = Version.of("1.23.0"); + assertThat(v1_23_0.integers, hasSize(3)); + assertThat(v1_23_0.integers, hasToString("[1, 23, 0]")); + + final Version v1_23_1 = Version.of("1.23.1"); + assertThat(v1_23_1.integers, hasSize(3)); + assertThat(v1_23_1.integers, hasToString("[1, 23, 1]")); + + // 1 < 1.8.3 < 1.19 < 1.23 < 1.23.0 < 1.23.1 + assertThat(vEmpty.compareTo(v1), is(-1)); + assertThat(v1.compareTo(v1_23), is(-1)); + assertThat(v1_8_3.compareTo(v1_19), is(-1)); + assertThat(v1_19.compareTo(v1_23), is(-1)); + assertThat(v1_23.compareTo(v1_23_0), is(-1)); + assertThat(v1_23_0.compareTo(v1_23_1), is(-1)); + assertThat(v1_23_1.compareTo(v1), is(1)); + + assertThat(v1.compareTo(v1), is(0)); + } + @Test void testGuavaMajorVersion() { int majorVersion = TestUtil.getGuavaMajorVersion(); assertTrue(majorVersion >= 2,