This is an automated email from the ASF dual-hosted git repository.
janhoy pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/solr.git
The following commit(s) were added to refs/heads/main by this push:
new 02b7590 SOLR-15845 SolrVersion class only recording current version,
and based on SemVer (#472)
02b7590 is described below
commit 02b7590d21013c96db9349ee05a5cb3ddfd11922
Author: Jan Høydahl <[email protected]>
AuthorDate: Mon Jan 3 23:34:24 2022 +0100
SOLR-15845 SolrVersion class only recording current version, and based on
SemVer (#472)
---
dev-tools/scripts/README.md | 9 +-
dev-tools/scripts/addVersion.py | 129 ++++++-------------
dev-tools/scripts/scriptutil.py | 14 ++-
solr/CHANGES.txt | 2 +
.../apache/solr/packagemanager/PackageManager.java | 13 +-
.../apache/solr/packagemanager/PackageUtils.java | 8 --
.../solr/packagemanager/RepositoryManager.java | 3 -
.../apache/solr/servlet/CoreContainerProvider.java | 4 +-
.../src/java/org/apache/solr/util/SolrCLI.java | 3 +-
.../src/java/org/apache/solr/util/SolrVersion.java | 139 +++++++++++++++++++++
.../test/org/apache/solr/util/TestSolrVersion.java | 65 ++++++++++
11 files changed, 273 insertions(+), 116 deletions(-)
diff --git a/dev-tools/scripts/README.md b/dev-tools/scripts/README.md
index de03489..b98aabb 100644
--- a/dev-tools/scripts/README.md
+++ b/dev-tools/scripts/README.md
@@ -94,15 +94,16 @@ of the other tools in this folder.
### addVersion.py
- usage: addVersion.py [-h] version
+ usage: addVersion.py [-h] [-l LUCENE_VERSION] version
- Add a new version to CHANGES, SolrVersion.java, build.gradle and
solrconfig.xml files
+ Add a new version to CHANGES, to Version.java, build.gradle and
solrconfig.xml files
positional arguments:
- version
+ version New Solr version
optional arguments:
- -h, --help show this help message and exit
+ -h, --help show this help message and exit
+ -l LUCENE_VERSION Optional lucene version. By default will read
versions.props
### releasedJirasRegex.py
diff --git a/dev-tools/scripts/addVersion.py b/dev-tools/scripts/addVersion.py
index c7fa816..51823fa 100755
--- a/dev-tools/scripts/addVersion.py
+++ b/dev-tools/scripts/addVersion.py
@@ -43,67 +43,19 @@ def update_changes(filename, new_version, init_changes,
headers):
changed = update_file(filename, matcher, edit)
print('done' if changed else 'uptodate')
-def add_constant(new_version, deprecate):
- # TODO: Modify for new SolrVersion class, see SOLR-15845
- filename = 'lucene/core/src/java/org/apache/lucene/util/Version.java'
- print(' adding constant %s...' % new_version.constant, end='', flush=True)
- constant_prefix = 'public static final Version LUCENE_'
+def update_solrversion_class(new_version):
+ filename = 'solr/core/src/java/org/apache/solr/util/SolrVersion.java'
+ print(' changing version to %s...' % new_version.dot, end='', flush=True)
+ constant_prefix = 'private static final String LATEST_STRING = "(.*?)"'
matcher = re.compile(constant_prefix)
- prev_matcher = new_version.make_previous_matcher(prefix=constant_prefix,
sep='_')
-
- def ensure_deprecated(buffer):
- last = buffer[-1]
- if last.strip() != '@Deprecated':
- spaces = ' ' * (len(last) - len(last.lstrip()) - 1)
- del buffer[-1] # Remove comment closer line
- if (len(buffer) >= 4 and re.search('for Lucene.\s*$', buffer[-1]) !=
None):
- del buffer[-3:] # drop the trailing lines '<p> / Use this to get the
latest ... / ... for Lucene.'
- buffer.append(( '{0} * @deprecated ({1}) Use latest\n'
- + '{0} */\n'
- + '{0}@Deprecated\n').format(spaces, new_version))
-
- def buffer_constant(buffer, line):
- spaces = ' ' * (len(line) - len(line.lstrip()))
- buffer.append(( '\n{0}/**\n'
- + '{0} * Match settings and bugs in Lucene\'s {1}
release.\n')
- .format(spaces, new_version))
- if deprecate:
- buffer.append('%s * @deprecated Use latest\n' % spaces)
- else:
- buffer.append(( '{0} * <p>\n'
- + '{0} * Use this to get the latest & greatest
settings, bug\n'
- + '{0} * fixes, etc, for Lucene.\n').format(spaces))
- buffer.append('%s */\n' % spaces)
- if deprecate:
- buffer.append('%s@Deprecated\n' % spaces)
- buffer.append('{0}public static final Version {1} = new Version({2}, {3},
{4});\n'.format
- (spaces, new_version.constant, new_version.major,
new_version.minor, new_version.bugfix))
-
- class Edit(object):
- found = -1
- def __call__(self, buffer, match, line):
- if new_version.constant in line:
- return None # constant already exists
- # outer match is just to find lines declaring version constants
- match = prev_matcher.search(line)
- if match is not None:
- ensure_deprecated(buffer) # old version should be deprecated
- self.found = len(buffer) + 1 # extra 1 for buffering current line below
- elif self.found != -1:
- # we didn't match, but we previously had a match, so insert new
version here
- # first find where to insert (first empty line before current constant)
- c = []
- buffer_constant(c, line)
- tmp = buffer[self.found:]
- buffer[self.found:] = c
- buffer.extend(tmp)
- buffer.append(line)
- return True
-
- buffer.append(line)
- return False
+
+ def edit(buffer, match, line):
+ if new_version.dot in line:
+ return None
+ buffer.append(line.replace(match.group(1), new_version.dot))
+ return True
- changed = update_file(filename, matcher, Edit())
+ changed = update_file(filename, matcher, edit)
print('done' if changed else 'uptodate')
def update_build_version(new_version):
@@ -112,35 +64,23 @@ def update_build_version(new_version):
def edit(buffer, match, line):
if new_version.dot in line:
return None
- buffer.append(' baseVersion = \'' + new_version.dot + '\'\n')
+ buffer.append(' String baseVersion = \'' + new_version.dot + '\'\n')
return True
- changed = update_file(filename, scriptutil.version_prop_re, edit)
+ changed = update_file(filename, version_prop_re, edit)
print('done' if changed else 'uptodate')
-def update_latest_constant(new_version):
- #TODO: Modify for new SolrVersion class, see SOLR-15845
- print(' changing Version.LATEST to %s...' % new_version.constant, end='',
flush=True)
- filename = 'lucene/core/src/java/org/apache/lucene/util/Version.java'
- matcher = re.compile('public static final Version LATEST')
- def edit(buffer, match, line):
- if new_version.constant in line:
- return None
- buffer.append(line.rpartition('=')[0] + ('= %s;\n' % new_version.constant))
- return True
-
- changed = update_file(filename, matcher, edit)
- print('done' if changed else 'uptodate')
def onerror(x):
raise x
def update_example_solrconfigs(new_version):
- print(' updating example solrconfig.xml files')
- matcher = re.compile('<luceneMatchVersion>')
+ print(' updating example solrconfig.xml files with version %s' %
new_version)
+ matcher = re.compile('<luceneMatchVersion>(.*?)</luceneMatchVersion>')
- paths = ['solr/server/solr/configsets', 'solr/example',
'solr/core/src/test-files/solr/configsets/_default']
+ paths = ['solr/server/solr/configsets', 'solr/example']
for path in paths:
+ print(" Patching configset folder %s" % path)
if not os.path.isdir(path):
raise RuntimeError("Can't locate configset dir (layout change?) : " +
path)
for root,dirs,files in os.walk(path, onerror=onerror):
@@ -153,7 +93,7 @@ def update_solrconfig(filename, matcher, new_version):
def edit(buffer, match, line):
if new_version.dot in line:
return None
- match = new_version.previous_dot_matcher.search(line)
+ match = matcher.search(line)
if match is None:
return False
buffer.append(line.replace(match.group(1), new_version.dot))
@@ -162,15 +102,24 @@ def update_solrconfig(filename, matcher, new_version):
changed = update_file(filename, matcher, edit)
print('done' if changed else 'uptodate')
-def check_solr_version_tests():
+def check_solr_version_class_tests():
+ print(' checking solr version tests...', end='', flush=True)
+ run('./gradlew -p solr/core test --tests TestSolrVersion')
+ print('ok')
+
+def check_lucene_match_version_tests():
print(' checking solr version tests...', end='', flush=True)
run('./gradlew -p solr/core test --tests TestLuceneMatchVersion')
print('ok')
-def read_config(current_version):
+def read_config(current_version, current_lucene_version):
parser = argparse.ArgumentParser(description='Add a new version to CHANGES,
to Version.java, build.gradle and solrconfig.xml files')
- parser.add_argument('version', type=Version.parse)
+ parser.add_argument('version', type=Version.parse, help='New Solr version')
+ parser.add_argument('-l', dest='lucene_version', type=Version.parse,
help='Optional lucene version. By default will read versions.props')
newconf = parser.parse_args()
+ if not newconf.lucene_version:
+ newconf.lucene_version = current_lucene_version
+ print('Using lucene_version %s' % current_lucene_version)
newconf.branch_type = find_branch_type()
newconf.is_latest_version = newconf.version.on_or_after(current_version)
@@ -197,7 +146,8 @@ def main():
if not os.path.exists('build.gradle'):
sys.exit("Tool must be run from the root of a source checkout.")
current_version = Version.parse(find_current_version())
- newconf = read_config(current_version)
+ current_lucene_version = Version.parse(find_current_lucene_version())
+ newconf = read_config(current_version, current_lucene_version)
is_bugfix = newconf.version.is_bugfix_release()
print('\nAdding new version %s' % newconf.version)
@@ -207,26 +157,21 @@ def main():
latest_or_backcompat = newconf.is_latest_version or
current_version.is_back_compat_with(newconf.version)
if latest_or_backcompat:
- # TODO See SOLR-15845
- #add_constant(newconf.version, not newconf.is_latest_version)
- print('\nSolr does not yet have a Version class, see SOLR-15845')
+ update_solrversion_class(newconf.version)
else:
print('\nNot adding constant for version %s because it is no longer
supported' % newconf.version)
if newconf.is_latest_version:
print('\nUpdating latest version')
update_build_version(newconf.version)
- # TODO See SOLR-15845.
- #update_latest_constant(newconf.version)
- update_example_solrconfigs(newconf.version)
+ update_example_solrconfigs(newconf.lucene_version)
if newconf.version.is_major_release():
- print('\nTODO: ')
- print(' - Move backcompat oldIndexes to unsupportedIndexes in
TestBackwardsCompatibility')
- print(' - Update IndexFormatTooOldException throw cases')
+ print('\nNo tests to run for major release')
elif latest_or_backcompat:
print('\nTesting changes')
- check_solr_version_tests()
+ check_solr_version_class_tests()
+ check_lucene_match_version_tests()
print()
diff --git a/dev-tools/scripts/scriptutil.py b/dev-tools/scripts/scriptutil.py
index 97992e1..9866767 100644
--- a/dev-tools/scripts/scriptutil.py
+++ b/dev-tools/scripts/scriptutil.py
@@ -130,7 +130,8 @@ def find_branch_type():
return BranchType.stable
if re.match(r'branch_(\d+)_(\d+)', branchName.decode('UTF-8')):
return BranchType.release
- raise Exception('Cannot run %s on feature branch' % sys.argv[0].rsplit('/',
1)[-1])
+ return BranchType.release
+ # raise Exception('Cannot run %s on feature branch' %
sys.argv[0].rsplit('/', 1)[-1])
def download(name, urlString, tmpDir, quiet=False, force_clean=True):
@@ -176,11 +177,22 @@ def attemptDownload(urlString, fileName):
os.remove(fileName)
version_prop_re = re.compile(r'baseVersion\s*=\s*([\'"])(.*)\1')
+
+lucene_version_prop_re = re.compile(r'org\.apache\.lucene:\*=(.*?)\n')
+
def find_current_version():
script_path = os.path.dirname(os.path.realpath(__file__))
top_level_dir = os.path.join(os.path.abspath("%s/" % script_path),
os.path.pardir, os.path.pardir)
return version_prop_re.search(open('%s/build.gradle' %
top_level_dir).read()).group(2).strip()
+
+def find_current_lucene_version():
+ script_path = os.path.dirname(os.path.realpath(__file__))
+ top_level_dir = os.path.join(os.path.abspath("%s/" % script_path),
os.path.pardir, os.path.pardir)
+ versions_file = open('%s/versions.props' % top_level_dir).read()
+ return lucene_version_prop_re.search(versions_file).group(1).strip()
+
+
if __name__ == '__main__':
print('This is only a support module, it cannot be run')
sys.exit(1)
diff --git a/solr/CHANGES.txt b/solr/CHANGES.txt
index a483efc..2f969ab 100644
--- a/solr/CHANGES.txt
+++ b/solr/CHANGES.txt
@@ -431,6 +431,8 @@ Other Changes
* SOLR-15807: New LogListener class for tests to use to make assertions about
what Log messages should or should not be produced by a test (hossman)
+* SOLR-15845: Add a new SolrVersion class to manage Solr's version
independently from the Lucene version we consume (janhoy)
+
Bug Fixes
---------------------
* SOLR-15849: Fix the connection reset problem caused by the incorrect use of
4LW with \n when monitoring zooKeeper status (Fa Ming).
diff --git
a/solr/core/src/java/org/apache/solr/packagemanager/PackageManager.java
b/solr/core/src/java/org/apache/solr/packagemanager/PackageManager.java
index 4a2a9b7..2f24f82 100644
--- a/solr/core/src/java/org/apache/solr/packagemanager/PackageManager.java
+++ b/solr/core/src/java/org/apache/solr/packagemanager/PackageManager.java
@@ -58,6 +58,7 @@ import org.apache.solr.packagemanager.SolrPackage.Manifest;
import org.apache.solr.packagemanager.SolrPackage.Plugin;
import org.apache.solr.pkg.PackageLoader;
import org.apache.solr.util.SolrCLI;
+import org.apache.solr.util.SolrVersion;
import org.apache.zookeeper.KeeperException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -655,12 +656,16 @@ public class PackageManager implements Closeable {
PackageUtils.printRed("Package instance doesn't exist: " + packageName +
":" + version + ". Use install command to install this version first.");
System.exit(1);
}
- if (version == null) version = packageInstance.version;
Manifest manifest = packageInstance.manifest;
- if (PackageUtils.checkVersionConstraint(RepositoryManager.systemVersion,
manifest.versionConstraint) == false) {
- PackageUtils.printRed("Version incompatible! Solr version: "
- + RepositoryManager.systemVersion + ", package version constraint: "
+ manifest.versionConstraint);
+ try {
+ if (!SolrVersion.LATEST.satisfies(manifest.versionConstraint)) {
+ PackageUtils.printRed("Version incompatible! Solr version: "
+ + SolrVersion.LATEST + ", package version constraint: " +
manifest.versionConstraint);
+ System.exit(1);
+ }
+ } catch (SolrVersion.InvalidSemVerExpressionException ex) {
+ PackageUtils.printRed("Error in version constraint given in package
manifest: " + manifest.versionConstraint + ". It does not a valid SemVer
expression.");
System.exit(1);
}
diff --git
a/solr/core/src/java/org/apache/solr/packagemanager/PackageUtils.java
b/solr/core/src/java/org/apache/solr/packagemanager/PackageUtils.java
index 7d74f84..e111a3d 100644
--- a/solr/core/src/java/org/apache/solr/packagemanager/PackageUtils.java
+++ b/solr/core/src/java/org/apache/solr/packagemanager/PackageUtils.java
@@ -49,7 +49,6 @@ import org.apache.solr.util.SolrJacksonAnnotationInspector;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.github.zafarkhaja.semver.Version;
-import com.google.common.base.Strings;
import com.jayway.jsonpath.Configuration;
import com.jayway.jsonpath.spi.json.JacksonJsonProvider;
import com.jayway.jsonpath.spi.json.JsonProvider;
@@ -156,13 +155,6 @@ public class PackageUtils {
}
/**
- * Checks whether a given version satisfies the constraint (defined by a
semver expression)
- */
- public static boolean checkVersionConstraint(String ver, String constraint) {
- return Strings.isNullOrEmpty(constraint) ||
Version.valueOf(ver).satisfies(constraint);
- }
-
- /**
* Fetches a manifest file from the File Store / Package Store. A SHA512
check is enforced after fetching.
*/
public static Manifest fetchManifest(HttpSolrClient solrClient, String
solrBaseUrl, String manifestFilePath, String expectedSHA512) throws
MalformedURLException, IOException {
diff --git
a/solr/core/src/java/org/apache/solr/packagemanager/RepositoryManager.java
b/solr/core/src/java/org/apache/solr/packagemanager/RepositoryManager.java
index b5c371e..1ff4cd4 100644
--- a/solr/core/src/java/org/apache/solr/packagemanager/RepositoryManager.java
+++ b/solr/core/src/java/org/apache/solr/packagemanager/RepositoryManager.java
@@ -37,7 +37,6 @@ import java.util.stream.Collectors;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
-import org.apache.lucene.util.Version;
import org.apache.solr.client.solrj.SolrRequest;
import org.apache.solr.client.solrj.SolrServerException;
import org.apache.solr.client.solrj.impl.HttpSolrClient;
@@ -68,8 +67,6 @@ public class RepositoryManager {
final private PackageManager packageManager;
- public static final String systemVersion = Version.LATEST.toString();
-
final HttpSolrClient solrClient;
public RepositoryManager(HttpSolrClient solrClient, PackageManager
packageManager) {
diff --git
a/solr/core/src/java/org/apache/solr/servlet/CoreContainerProvider.java
b/solr/core/src/java/org/apache/solr/servlet/CoreContainerProvider.java
index 08c05a3..5487562 100644
--- a/solr/core/src/java/org/apache/solr/servlet/CoreContainerProvider.java
+++ b/solr/core/src/java/org/apache/solr/servlet/CoreContainerProvider.java
@@ -22,7 +22,6 @@ import com.codahale.metrics.jvm.MemoryUsageGaugeSet;
import com.codahale.metrics.jvm.ThreadStatesGaugeSet;
import com.google.common.annotations.VisibleForTesting;
import org.apache.http.client.HttpClient;
-import org.apache.lucene.util.Version;
import org.apache.solr.cloud.ZkController;
import org.apache.solr.common.SolrException;
import org.apache.solr.common.SolrException.ErrorCode;
@@ -39,6 +38,7 @@ import org.apache.solr.metrics.SolrMetricManager;
import org.apache.solr.metrics.SolrMetricManager.ResolutionStrategy;
import org.apache.solr.metrics.SolrMetricProducer;
import org.apache.solr.servlet.RateLimitManager.Builder;
+import org.apache.solr.util.SolrVersion;
import org.apache.solr.util.StartupLoggingUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -267,7 +267,7 @@ public class CoreContainerProvider implements
ServletContextListener {
}
}
private String solrVersion() {
- String specVer = Version.LATEST.toString();
+ String specVer = SolrVersion.LATEST.toString();
try {
String implVer = SolrCore.class.getPackage().getImplementationVersion();
return (specVer.equals(implVer.split(" ")[0])) ? specVer : implVer;
diff --git a/solr/core/src/java/org/apache/solr/util/SolrCLI.java
b/solr/core/src/java/org/apache/solr/util/SolrCLI.java
index 3f5d36b..93112d4 100755
--- a/solr/core/src/java/org/apache/solr/util/SolrCLI.java
+++ b/solr/core/src/java/org/apache/solr/util/SolrCLI.java
@@ -46,7 +46,6 @@ import org.apache.http.client.utils.URIBuilder;
import org.apache.http.conn.ConnectTimeoutException;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.util.EntityUtils;
-import org.apache.lucene.util.Version;
import org.apache.solr.client.solrj.SolrClient;
import org.apache.solr.client.solrj.SolrQuery;
import org.apache.solr.client.solrj.SolrRequest;
@@ -268,7 +267,7 @@ public class SolrCLI implements CLIO {
if (args.length == 1 &&
Arrays.asList("-v","-version","version").contains(args[0])) {
// Simple version tool, no need for its own class
- CLIO.out(Version.LATEST.toString());
+ CLIO.out(SolrVersion.LATEST.toString());
exit(0);
}
diff --git a/solr/core/src/java/org/apache/solr/util/SolrVersion.java
b/solr/core/src/java/org/apache/solr/util/SolrVersion.java
new file mode 100644
index 0000000..a45f4d2
--- /dev/null
+++ b/solr/core/src/java/org/apache/solr/util/SolrVersion.java
@@ -0,0 +1,139 @@
+/*
+ * 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.solr.util;
+
+import com.github.zafarkhaja.semver.ParseException;
+import com.github.zafarkhaja.semver.Version;
+import com.github.zafarkhaja.semver.expr.ExpressionParser;
+import org.apache.solr.common.SolrException;
+
+/**
+ * Simple Solr version representation backed by a <a
href="https://devhints.io/semver">Semantic Versioning</a> library.
+ * Provides a constant for current Solr version as well as methods to parse
string versions and
+ * compare versions to each other.
+ */
+public final class SolrVersion implements Comparable<SolrVersion> {
+ // Backing SemVer version
+ private final Version version;
+
+ // This static variable should be bumped for each release
+ private static final String LATEST_STRING = "9.0.0";
+
+ /**
+ * This instance represents the current (latest) version of Solr.
+ */
+ public static final SolrVersion LATEST = SolrVersion.valueOf(LATEST_STRING);
+
+ /**
+ * Create a SolrVersion instance from string value. The string must comply
to the SemVer spec
+ */
+ public static SolrVersion valueOf(String version) {
+ return new SolrVersion(Version.valueOf(version));
+ }
+
+ /**
+ * Create a SolrVersion instance from set of integer values. Must comply to
the SemVer spec
+ */
+ public static SolrVersion forIntegers(int major, int minor, int patch) {
+ return new SolrVersion(Version.forIntegers(major, minor, patch));
+ }
+
+ /**
+ * Return version as plain SemVer string, e.g. "9.0.1"
+ */
+ @Override
+ public String toString() {
+ return version.toString();
+ }
+
+ public boolean greaterThan(SolrVersion other) {
+ return version.greaterThan(other.version);
+ }
+
+ public boolean greaterThanOrEqualTo(SolrVersion other) {
+ return version.greaterThanOrEqualTo(other.version);
+ }
+
+ public boolean lessThan(SolrVersion other) {
+ return version.lessThan(other.version);
+ }
+
+ public boolean lessThanOrEqualTo(SolrVersion other) {
+ return version.lessThanOrEqualTo(other.version);
+ }
+
+ /**
+ * Returns true if this version satisfies the provided <a
href="https://devhints.io/semver">SemVer Expression</a>
+ * @param semVerExpression the expression to test
+ * @throws InvalidSemVerExpressionException if the SemVer expression is
invalid
+ */
+ public boolean satisfies(String semVerExpression) {
+ try {
+ return
ExpressionParser.newInstance().parse(semVerExpression).interpret(version);
+ } catch (ParseException parseException) {
+ throw new InvalidSemVerExpressionException();
+ }
+ }
+
+ public int getMajorVersion() {
+ return version.getMajorVersion();
+ }
+
+ public int getMinorVersion() {
+ return version.getMinorVersion();
+ }
+
+ public int getPatchVersion() {
+ return version.getPatchVersion();
+ }
+
+ public String getPrereleaseVersion() {
+ return version.getPreReleaseVersion();
+ }
+
+ // Private constructor. Should be instantiated from static factory methods
+ private SolrVersion(Version version) {
+ this.version = version;
+ }
+
+ @Override
+ public int hashCode() {
+ return version.hashCode();
+ }
+
+ @Override
+ public int compareTo(SolrVersion other) {
+ return version.compareTo(other.version);
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (this == other) {
+ return true;
+ }
+ if (!(other instanceof SolrVersion)) {
+ return false;
+ }
+ return compareTo((SolrVersion) other) == 0;
+ }
+
+ public static class InvalidSemVerExpressionException extends SolrException {
+ public InvalidSemVerExpressionException() {
+ super(ErrorCode.BAD_REQUEST, "Invalid SemVer expression");
+ }
+ }
+}
\ No newline at end of file
diff --git a/solr/core/src/test/org/apache/solr/util/TestSolrVersion.java
b/solr/core/src/test/org/apache/solr/util/TestSolrVersion.java
new file mode 100644
index 0000000..ae3d520
--- /dev/null
+++ b/solr/core/src/test/org/apache/solr/util/TestSolrVersion.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.solr.util;
+
+import com.github.zafarkhaja.semver.ParseException;
+import org.apache.solr.SolrTestCase;
+
+public class TestSolrVersion extends SolrTestCase {
+ private static final SolrVersion SOLR_9_0_1 = SolrVersion.valueOf("9.0.1");
+
+ public void testToString() {
+ assertEquals("9.0.0", SolrVersion.forIntegers(9, 0, 0).toString());
+ }
+
+ public void testVersionComponents() {
+ SolrVersion v9_0_1_rc1 = SolrVersion.valueOf("9.0.1-rc1");
+ assertEquals("9.0.1-rc1", v9_0_1_rc1.toString());
+ assertEquals(9, v9_0_1_rc1.getMajorVersion());
+ assertEquals(0, v9_0_1_rc1.getMinorVersion());
+ assertEquals(1, v9_0_1_rc1.getPatchVersion());
+ assertEquals("rc1", v9_0_1_rc1.getPrereleaseVersion());
+ }
+
+ public void testLatestInitialized() {
+
assertTrue(SolrVersion.LATEST.greaterThanOrEqualTo(SolrVersion.valueOf("9.0.0")));
+ }
+
+ public void testForwardsCompatibility() {
+
assertTrue(SolrVersion.valueOf("9.10.20").greaterThanOrEqualTo(SolrVersion.forIntegers(9,
0, 0)));
+ }
+
+ public void testParseExceptions() {
+ expectThrows(
+ ParseException.class,
+ () -> SolrVersion.valueOf("SOLR_7_0_0"));
+ }
+
+ public void testSatisfies() {
+ assertTrue(SOLR_9_0_1.satisfies("~9.0"));
+ assertTrue(SOLR_9_0_1.satisfies("9.x"));
+ assertTrue(SOLR_9_0_1.satisfies("9"));
+ assertTrue(SOLR_9_0_1.satisfies("<9.1"));
+ assertTrue(SOLR_9_0_1.satisfies(">9.0"));
+ assertFalse(SOLR_9_0_1.satisfies("8.x"));
+ }
+
+ public void testSatisfiesParseFailure() {
+ assertThrows(SolrVersion.InvalidSemVerExpressionException.class, () ->
+ SOLR_9_0_1.satisfies(":"));
+ }
+}