A => .gitattributes +23 -0
@@ 1,23 @@
+# Linux
+*.sh text eol=lf
+*.screen text eol=lf
+
+## Lighttpd stuff
+*.conf text eol=lf
+*.pem text eol=lf
+*.crt text eol=lf
+
+# Windows
+*.ps1 text eol=crlf
+
+# Both OS's
+*.lisp text
+*.asd text
+*.md text
+*.lock text
+qlfile text
+.gitignore text
+
+# Images
+*.png binary
+*.jpg binary
A => .gitignore +20 -0
@@ 1,20 @@
+*.abcl
+*.fasl
+*.dx32fsl
+*.dx64fsl
+*.lx32fsl
+*.lx64fsl
+*.x86f
+*~
+.#*
+.*.sw[a-z]
+/.slime_paste
+/.vagrant/
+/scratch.lisp
+/*.log
+TODO
+/.repl/ros_history
+
+/.qlot/
+/.bundle-libs/
+java/*.class
A => README.md +10 -0
@@ 1,10 @@
+# cl-vercmp
+
+This library is a port of [serovers](https://gitlab.com/djhaskin987/serovers)
+into Common Lisp.
+
+## Usage
+
+
+
+## Installation
A => cl-vercmp.asd +27 -0
@@ 1,27 @@
+(defsystem "cl-vercmp"
+ :version "0.0.1"
+ :author "Daniel Jay Haskin"
+ :license "MIT"
+ :depends-on (
+ "alexandria"
+ "cl-ppcre"
+ "cl-ppcre-unicode"
+ "arrows"
+ "uiop"
+ )
+ :components ((:module "src"
+ :components
+ ((:file "main"))))
+ :description "Version comparison functions written in Common Lisp"
+ :in-order-to ((test-op (test-op "cl-vercmp/tests"))))
+
+(defsystem "cl-vercmp/tests"
+ :author "Daniel Jay Haskin"
+ :license "MIT"
+ :depends-on ("cl-vercmp"
+ "rove")
+ :components ((:module "tests"
+ :components
+ ((:file "main"))))
+ :description "Test system for cl-vercmp"
+ :perform (test-op (op c) (symbol-call :rove :run c)))
A => java/Pair.java +57 -0
@@ 1,57 @@
+/**
+ * Container to ease passing around a tuple of two objects. This object provides a sensible
+ * implementation of equals(), returning true if equals() is true on each of the contained
+ * objects.
+ */
+public class Pair<F, S> {
+ public final F first;
+ public final S second;
+
+ /**
+ * Constructor for a Pair.
+ *
+ * @param first the first object in the Pair
+ * @param second the second object in the pair
+ */
+ public Pair(F first, S second) {
+ this.first = first;
+ this.second = second;
+ }
+
+ /**
+ * Checks the two objects for equality by delegating to their respective
+ * {@link Object#equals(Object)} methods.
+ *
+ * @param o the {@link Pair} to which this one is to be checked for equality
+ * @return true if the underlying objects of the Pair are both considered
+ * equal
+ */
+ @Override
+ public boolean equals(Object o) {
+ if (!(o instanceof Pair)) {
+ return false;
+ }
+ Pair<?, ?> p = (Pair<?, ?>) o;
+ return Objects.equals(p.first, first) && Objects.equals(p.second, second);
+ }
+
+ /**
+ * Compute a hash code using the hash codes of the underlying objects
+ *
+ * @return a hashcode of the Pair
+ */
+ @Override
+ public int hashCode() {
+ return (first == null ? 0 : first.hashCode()) ^ (second == null ? 0 : second.hashCode());
+ }
+
+ /**
+ * Convenience method for creating an appropriately typed pair.
+ * @param a the first object in the Pair
+ * @param b the second object in the pair
+ * @return a Pair that is templatized with the types of a and b
+ */
+ public static <A, B> Pair <A, B> create(A a, B b) {
+ return new Pair<A, B>(a, b);
+ }
+}<
\ No newline at end of file
A => java/VersionTest.java +106 -0
@@ 1,106 @@
+import java.util.*;
+import java.lang.module.ModuleDescriptor.Version;
+
+// We are in the middle of creating a java version comparison tool in lisp.
+// However, we need to ensure that all the versions we test on our tool have the
+// same version comparison outcomes in Java as theyu do in lisp.
+// We're moving lisp code into java code so we can check our work.
+//
+// Here is the lisp code we are moving to java:
+/*
+(deftest maven-cases
+ (testing "Numeric Comparison"
+ (ok (= (vc:maven-vercmp "1.0.0" "1.0.0") 0))
+ (ok (< (vc:maven-vercmp "1.0.0" "1.0") 0))
+ (ok (> (vc:maven-vercmp "1.0.1" "1.0") 0))
+ (ok (< (vc:maven-vercmp "1.0.0" "1.0.1") 0))
+ (ok (< (vc:maven-vercmp "1.0.0" "1.0.0-1") 0))
+ (ok (> (vc:maven-vercmp "1.0.0" "0.9.2") 0))
+ (ok (< (vc:maven-vercmp "0.9.2" "0.9.3") 0))
+ (ok (> (vc:maven-vercmp "0.9.2" "0.9.1") 0))
+ (ok (< (vc:maven-vercmp "0.9.5" "0.9.13") 0))
+ (ok (< (vc:maven-vercmp "10.2.0.3.0" "11.2.0.3.0") 0))
+ (ok (> (vc:maven-vercmp "10.2.0.3.0" "5.2.0.3.0") 0))
+ (ok (< (vc:maven-vercmp "1.0.0-SNAPSHOT" "1.0.1-SNAPSHOT") 0))
+ (ok (< (vc:maven-vercmp "1.0.0-alpha" "1.0.1-beta") 0))
+ (ok (< (vc:maven-vercmp "1.1-dolphin" "1.1.1-cobra") 0)))
+ (testing "Lexical Comparison"
+ (ok (< (vc:maven-vercmp "1.0-alpaca" "1.0-bermuda") 0))
+ (ok (< (vc:maven-vercmp "1.0-alpaca" "1.0-alpaci") 0))
+ (ok (> (vc:maven-vercmp "1.0-dolphin" "1.0-cobra") 0)))
+ (testing "Qualifier Comparison"
+ (ok (< (vc:maven-vercmp "1.0.0-alpha" "1.0.0-beta") 0))
+ (ok (> (vc:maven-vercmp "1.0.0-beta" "1.0.0-alpha") 0))
+ (ok (< (vc:maven-vercmp "1.0.0-alpaca" "1.0.0-beta") 0))
+ (ok (> (vc:maven-vercmp "1.0.0-final" "1.0.0-milestone") 0)))
+ (testing "Qualifier/Numeric Comparison"
+ (ok (< (vc:maven-vercmp "1.0.0-alpha1" "1.0.0-alpha2") 0))
+ (ok (< (vc:maven-vercmp "1.0.0-alpha5" "1.0.0-alpha23") 0))
+ (ok (< (vc:maven-vercmp "1.0-RC5" "1.0-RC20") 0))
+ (ok (> (vc:maven-vercmp "1.0-RC11" "1.0-RC6") 0)))
+ (testing "Releases are newer than SNAPSHOTs"
+ (ok (> (vc:maven-vercmp "1.0.0" "1.0.0-SNAPSHOT") 0))
+ (ok (= (vc:maven-vercmp "1.0.0-SNAPSHOT" "1.0.0-SNAPSHOT") 0))
+ (ok (< (vc:maven-vercmp "1.0.0-SNAPSHOT" "1.0.0") 0)))
+ (testing "Releases are newer than qualified versions"
+ (ok (> (vc:maven-vercmp "1.0.0" "1.0.0-alpha5") 0))
+ (ok (< (vc:maven-vercmp "1.0.0-alpha5" "1.0.0") 0)))
+ (testing "SNAPSHOTS are newer than qualified versions"
+ (ok (> (vc:maven-vercmp "1.0.0-SNAPSHOT" "1.0.0-RC1") 0))
+ (ok (< (vc:maven-vercmp "1.0.0-SNAPSHOT" "1.0.1-RC1") 0)))
+ (testing "Some other Formats"
+ (ok (> (vc:maven-vercmp "9.1-901.jdbc4" "9.1-901.jdbc3") 0))
+ (ok (> (vc:maven-vercmp "9.1-901-1.jdbc4" "9.1-901.jdbc4") 0)))
+ (testing "Some more zero-extension Tests"
+ (ok (= (vc:maven-vercmp "1-SNAPSHOT" "1.0-SNAPSHOT") 0))
+ (ok (= (vc:maven-vercmp "1-alpha" "1-alpha0") 0))))
+*/
+
+// And here is the java code we are using to test our lisp code:
+
+class VersionTest {
+ public static void main(String[] args) {
+ List<List<Version> > comparisons = Arrays.asList(
+ Arrays.asList(Version.parse("1.0.0"), Version.parse("1.0.0")),
+ Arrays.asList(Version.parse("1.0.0"), Version.parse("1.0")),
+ Arrays.asList(Version.parse("1.0.1"), Version.parse("1.0")),
+ Arrays.asList(Version.parse("1.0.0"), Version.parse("1.0.1")),
+ Arrays.asList(Version.parse("1.0.0"), Version.parse("1.0.0-1")),
+ Arrays.asList(Version.parse("1.0.0"), Version.parse("0.9.2")),
+ Arrays.asList(Version.parse("0.9.2"), Version.parse("0.9.3")),
+ Arrays.asList(Version.parse("0.9.2"), Version.parse("0.9.1")),
+ Arrays.asList(Version.parse("0.9.5"), Version.parse("0.9.13")),
+ Arrays.asList(Version.parse("10.2.0.3.0"), Version.parse("11.2.0.3.0")),
+ Arrays.asList(Version.parse("10.2.0.3.0"), Version.parse("5.2.0.3.0")),
+ Arrays.asList(Version.parse("1.0.0-SNAPSHOT"), Version.parse("1.0.1-SNAPSHOT")),
+ Arrays.asList(Version.parse("1.0.0-alpha"), Version.parse("1.0.1-beta")),
+ Arrays.asList(Version.parse("1.1-dolphin"), Version.parse("1.1.1-cobra")),
+ Arrays.asList(Version.parse("1.0-alpaca"), Version.parse("1.0-bermuda")),
+ Arrays.asList(Version.parse("1.0-alpaca"), Version.parse("1.0-alpaci")),
+ Arrays.asList(Version.parse("1.0-dolphin"), Version.parse("1.0-cobra")),
+ Arrays.asList(Version.parse("1.0.0-alpha"), Version.parse("1.0.0-beta")),
+ Arrays.asList(Version.parse("1.0.0-beta"), Version.parse("1.0.0-alpha")),
+ Arrays.asList(Version.parse("1.0.0-alpaca"), Version.parse("1.0.0-beta")),
+ Arrays.asList(Version.parse("1.0.0-final"), Version.parse("1.0.0-milestone")),
+ Arrays.asList(Version.parse("1.0.0-alpha1"), Version.parse("1.0.0-alpha2")),
+ Arrays.asList(Version.parse("1.0.0-alpha5"), Version.parse("1.0.0-alpha23")),
+ Arrays.asList(Version.parse("1.0-RC5"), Version.parse("1.0-RC20")),
+ Arrays.asList(Version.parse("1.0-RC11"), Version.parse("1.0-RC6")),
+ Arrays.asList(Version.parse("1.0.0"), Version.parse("1.0.0-SNAPSHOT")),
+ Arrays.asList(Version.parse("1.0.0-SNAPSHOT"), Version.parse("1.0.0-SNAPSHOT")),
+ Arrays.asList(Version.parse("1.0.0-SNAPSHOT"), Version.parse("1.0.0")),
+ Arrays.asList(Version.parse("1.0.0"), Version.parse("1.0.0-alpha5")),
+ Arrays.asList(Version.parse("1.0.0-alpha5"), Version.parse("1.0.0")),
+ Arrays.asList(Version.parse("1.0.0-SNAPSHOT"), Version.parse("1.0.0-RC1")),
+ Arrays.asList(Version.parse("1.0.0-SNAPSHOT"), Version.parse("1.0.1-RC1")),
+ Arrays.asList(Version.parse("9.1-901.jdbc4"), Version.parse("9.1-901.jdbc3")),
+ Arrays.asList(Version.parse("9.1-901-1.jdbc4"), Version.parse("9.1-901.jdbc4")),
+ Arrays.asList(Version.parse("1-SNAPSHOT"), Version.parse("1.0-SNAPSHOT")),
+ Arrays.asList(Version.parse("1-alpha"), Version.parse("1-alpha0")));
+ for (List<Version> pair : comparisons) {
+ System.out.println(pair.get(0) + " vs. " + pair.get(1) +
+ ": " + pair.get(0).compareTo(pair.get(1)));
+ }
+ }
+}
+
A => src/main.lisp +5 -0
@@ 1,5 @@
+(defpackage #:skin.djha.cl-vercmp
+ (:use :cl))
+(in-package #:skin.djha.cl-vercmp)
+
+;; blah blah blah.
A => src/version.lisp +187 -0
@@ 1,187 @@
+(defpackage #:skin.djha.vercmp
+ (:use :cl
+ :arrows)
+ (:import-from #:uiop)
+ (:import-from #:alexandria)
+ (:import-from #:cl-ppcre)
+ (:import-from #:cl-ppcre-unicode)
+ (:export
+ #:*trumpc*
+ #:debian-vercmp
+ #:maven-normalize
+ #:maven-vercmp)
+ (:documentation
+ "Version comparison library"))
+
+(deftype version ()
+ string)
+
+(in-package #:skin.djha.vercmp)
+
+(defparameter *trumpc* #\-)
+(defparameter *ignore-after* #\+)
+
+(defparameter *fillerc* (code-char 0)
+ "
+ Character to use when comparing non-numeric parts of the version strings.
+ If one part is longer than the other, the shorter part will be logically
+ extended with this character until they are both the same size.
+ ")
+(defparameter *fillers*
+ (make-string 1 :initial-element *fillerc*))
+
+(defun nonnumeric-part-compare
+ (a b)
+ "
+ Compare two version parts which both consist entirely of
+ non-digits (or are empty).
+ "
+ (declare (type string a b))
+ (loop with maxlength = (max (length a) (length b))
+ for i from 0 below maxlength
+ for ca = (if (< i (length a))
+ (elt a i)
+ *fillerc*)
+ for cb = (if (< i (length b))
+ (elt b i)
+ *fillerc*)
+ do
+ (let ((diff
+ (cond ((char= ca cb) 0)
+ ((char= ca *trumpc*) -1)
+ ((char= cb *trumpc*) 1)
+ ((char= ca *fillerc*) -1)
+ ((char= cb *fillerc*) 1)
+ ((and (alpha-char-p ca)
+ (not (alpha-char-p cb))) -1)
+ ((and (alpha-char-p cb)
+ (not (alpha-char-p ca))) 1)
+ (:else
+ (- (char-code ca) (char-code cb))))))
+ (when (not (zerop diff))
+ (return diff)))
+ finally (return 0)))
+
+(defun numeric-part-compare
+ (a b)
+ "
+ Compares two version parts, which both consist
+ entirely of digits (or are empty).
+ "
+ (declare (type string a b))
+ (let* ((trimmed-a (string-left-trim '(#\0) a))
+ (trimmed-b (string-left-trim '(#\0) b))
+ (ldiff (- (length trimmed-a)
+ (length trimmed-b))))
+ (if (zerop ldiff)
+ (cond ((string= trimmed-a trimmed-b) 0)
+ ((eq trimmed-a *fillers*) -1)
+ ((eq trimmed-b *fillers*) 1)
+ ((string< trimmed-a trimmed-b) -1)
+ (:else 1))
+ ldiff)))
+
+(defun version-part-compare (i a b)
+ "
+ Compare the `i`th part of the parts of version strings `a` and `b`, as found
+ by `version-parts-split`.
+ "
+ (declare (type fixnum i)
+ (type string a b))
+ (let ((comparator (if (zerop (mod i 2))
+ #'nonnumeric-part-compare
+ #'numeric-part-compare)))
+ (funcall comparator a b)))
+
+(defun version-parts-split
+ (version)
+ "
+ Split a version string up into its component parts, with digits in the `0,2,4,...`th
+ spots and non-digits in the `1,3,5,...`th spots.
+ "
+ (declare (type string version))
+ (loop
+ with parts = (make-array 10
+ :element-type 'string
+ :initial-element ""
+ :adjustable t
+ :fill-pointer 0)
+ for i = 0 then (1+ i)
+ for check = (if (zerop (mod i 2))
+ #'digit-char-p
+ (complement #'digit-char-p))
+ for scratch = version then (subseq scratch next-checked)
+ for next-checked = (or
+ (position-if check scratch)
+ (length scratch))
+ while (not (zerop (length scratch)))
+ do
+ (vector-push-extend (subseq scratch 0 next-checked) parts)
+ finally (return parts)))
+
+(defun epochless-vercmp (a b)
+ "
+ Compare two version numbers, assuming that neither has an epoch.
+ "
+ (declare (type string a b))
+ (if (equal a b)
+ 0
+ (let ((split-a (version-parts-split a))
+ (split-b (version-parts-split b)))
+ (loop with maxlength = (max (length split-a) (length split-b))
+ for i from 0 below maxlength
+ for sa = (if (< i (length split-a))
+ (elt split-a i)
+ *fillers*)
+ for sb = (if (< i (length split-b))
+ (elt split-b i)
+ *fillers*)
+ for cmp = (version-part-compare i sa sb)
+ while (zerop cmp)
+ finally (return cmp)))))
+
+
+(defun epoch
+ (a)
+ "
+ Extract the epoch from a version string.
+ "
+ (let ((found (position #\: a)))
+ (if found
+ (values (parse-integer (subseq a 0 found))
+ (subseq a (1+ found)))
+ (values 0 a))))
+
+(defun without-build-info
+ (a)
+ "
+ Extract build information from a version string.
+ "
+ (let ((found (position *ignore-after* a)))
+ (if found
+ (subseq a 0 found)
+ a)))
+
+
+(declaim (inline vercmp))
+(defun vercmp
+ (a b)
+ "
+ Compares two version numbers according to the rules laid out in the [Debian
+ Policy
+ Manual](https://www.debian.org/doc/gpolicy/ch-controlfields.html#s-f-Version),
+ retrieved 2024-02-15.
+
+ Epoch numbers, upstream versions, and revision version parts are fully
+ supported.
+ "
+ (declare (type string a b))
+ (multiple-value-bind (a-epoch a-vers) (epoch a)
+ (multiple-value-bind (b-epoch b-vers) (epoch b)
+ (if (= a-epoch b-epoch)
+ (epochless-vercmp
+ (without-build-info a-vers)
+ (without-build-info b-vers))
+ (- a-epoch b-epoch)))))
+
+
A => tests/main.lisp +340 -0
@@ 1,340 @@
+(defpackage #:skin.djha.vercmp/tests/main
+ (:use :cl
+ :rove)
+ (:import-from #:skin.djha.vercmp)
+ (:local-nicknames (:vc #:skin.djha.vercmp)))
+(in-package #:skin.djha.vercmp/tests/main)
+
+;; NOTE: To run this test file, execute `(asdf:test-system :cl-vercmp)' in your Lisp.
+
+
+#+(or)
+(rove:run-test *)
+
+(deftest test-nonnumeric-part-compare
+ (testing "empty cases"
+ (ok (= (vc::nonnumeric-part-compare "" "") 0))
+ (ok (> (vc::nonnumeric-part-compare " " "") 0))
+ (ok (< (vc::nonnumeric-part-compare "" " ") 0))
+ (ok (= (vc::nonnumeric-part-compare (coerce '(#\Nul) 'string) "") 0)))
+ (testing "non-empty cases"
+ (ok (> (vc::nonnumeric-part-compare ".alpha" "alpha") 0))
+ (ok (< (vc::nonnumeric-part-compare "alpha" ".alpha") 0))
+ (ok (> (vc:debian-vercmp "a" "") 0)))
+ (testing "trump cases"
+ (ok (< (vc::nonnumeric-part-compare "~alpha" "") 0))
+
+ (ok (< (vc::nonnumeric-part-compare "~~" "~a") 0))
+ (ok (< (vc::nonnumeric-part-compare "~~~a" "~~~b") 0))
+ (ok (> (vc::nonnumeric-part-compare "~~~b" "~~~a") 0))
+ (let ((vc:*trumpc* #\-))
+ (ok (< (vc::nonnumeric-part-compare "-a" "") 0))
+ (ok (> (vc::nonnumeric-part-compare "" "-a") 0))
+ (ok (< (vc::nonnumeric-part-compare "-a" "!a") 0))
+ (ok (> (vc::nonnumeric-part-compare "!a" "-a") 0)))))
+
+(deftest test-numeric-part-compare
+ (testing "zero cases"
+ (ok (= (vc::numeric-part-compare "" "") 0))
+ (ok (= (vc::numeric-part-compare "" "0") 0))
+ (ok (= (vc::numeric-part-compare "0" "") 0))
+ (ok (= (vc::numeric-part-compare "0" "0") 0))
+ (ok (= (vc::numeric-part-compare "0" "000000") 0)))
+ (testing "basic cases"
+ (ok (< (vc::numeric-part-compare "" "000017") 0))
+ (ok (> (vc::numeric-part-compare "000017" "") 0))
+ (ok (= (vc::numeric-part-compare "000017" "000017") 0))
+ (ok (= (vc::numeric-part-compare "000017" "017") 0))
+ (ok (< (vc::numeric-part-compare "000017" "18") 0))
+ (ok (> (vc::numeric-part-compare "675" "332") 0))
+ (ok (< (vc::numeric-part-compare "675" "67555555555555555555555") 0))
+ (ok (> (vc::numeric-part-compare "5558675309" "5552333892") 0))))
+
+(deftest version-parts-split
+ (testing
+ "The empty case. This case doesn't really make sense, but it's here for
+ completeness."
+ (ok (equalp (vc::version-parts-split "") #())))
+ (testing
+ "Some simple cases"
+ (ok (equalp (vc::version-parts-split " ") #(" ")))
+ (ok (equalp (vc::version-parts-split "5") #("" "5")))
+ (ok (equalp (vc::version-parts-split "1.0")
+ #("" "1" "." "0")))
+ (ok (equalp (vc::version-parts-split "1.0.0")
+ #("" "1" "." "0" "." "0"))))
+ (testing
+ "A case with a numeric suffix"
+ (ok (equalp (vc::version-parts-split "5.3.alpha17")
+ #("" "5" "." "3" ".alpha" "17"))))
+ (testing
+ "Concerning dots"
+ (ok (equalp (vc::version-parts-split ".a1")
+ #(".a" "1")))
+ (ok (equalp (vc::version-parts-split "5.a1")
+ #("" "5" ".a" "1"))))
+ (testing
+ "A case with a tilde"
+ (ok (equalp (vc::version-parts-split "1.0~alpha")
+ #("" "1" "." "0" "~alpha")))))
+
+(deftest debian-vercmp
+ (testing "Non-epoch Cases"
+ (ok (= (vc:debian-vercmp "" "") 0))
+ (ok (< (vc:debian-vercmp "~~" "~~a") 0))
+ (ok (> (vc:debian-vercmp "~" "~~a") 0))
+ (ok (< (vc:debian-vercmp "~" "") 0))
+ (ok (> (vc:debian-vercmp "a" "") 0))
+ (ok (< (vc:debian-vercmp "1.2.3~rc1" "1.2.3") 0))
+ (ok (= (vc:debian-vercmp "1.2" "1.2") 0))
+ (ok (< (vc:debian-vercmp "1.2" "a1.2") 0))
+ (ok (> (vc:debian-vercmp "1.2.3" "1.2-3") 0))
+ (ok (> (vc:debian-vercmp "1.2.3~rc1" "1.2.3~~rc1") 0))
+ (ok (< (vc:debian-vercmp "1.2.3" "2") 0))
+ (ok (> (vc:debian-vercmp "2.0.0" "2.0") 0))
+ (ok (> (vc:debian-vercmp "1.2.a" "1.2.3") 0))
+ (ok (> (vc:debian-vercmp "1.2.a" "1.2a") 0))
+ (ok (< (vc:debian-vercmp "1" "1.2.3.4") 0))
+ (ok (= (vc:debian-vercmp "1:2.3.4" "1:2.3.4") 0)))
+ (testing "Scary Cases"
+ (ok (= (vc:debian-vercmp "a" "a0"))))
+ (testing "Epoch Cases"
+ (ok (= (vc:debian-vercmp "0:" "0:") 0))
+ (ok (= (vc:debian-vercmp "0:" "" ) 0))
+ (ok (= (vc:debian-vercmp "0:1.2" "1.2") 0))
+ (ok (< (vc:debian-vercmp "0:" "1:") 0))
+ (ok (< (vc:debian-vercmp "0:1.2.3" "2:0.4.5") 0))))
+1.0.1 vs. 1.0: 1
+1.0.0 vs. 1.0.1: -1
+1.0.0 vs. 1.0.0-1: 1
+1.0.0 vs. 0.9.2: 1
+0.9.2 vs. 0.9.3: -1
+0.9.2 vs. 0.9.1: 1
+0.9.5 vs. 0.9.13: -1
+10.2.0.3.0 vs. 11.2.0.3.0: -1
+10.2.0.3.0 vs. 5.2.0.3.0: 1
+1.0.0-SNAPSHOT vs. 1.0.1-SNAPSHOT: -1
+1.0.0-alpha vs. 1.0.1-beta: -1
+1.1-dolphin vs. 1.1.1-cobra: -1
+1.0-alpaca vs. 1.0-bermuda: -1
+1.0-alpaca vs. 1.0-alpaci: -8
+1.0-dolphin vs. 1.0-cobra: 1
+1.0.0-alpha vs. 1.0.0-beta: -1
+1.0.0-beta vs. 1.0.0-alpha: 1
+1.0.0-alpaca vs. 1.0.0-beta: -1
+1.0.0-final vs. 1.0.0-milestone: -7
+1.0.0-alpha1 vs. 1.0.0-alpha2: -1
+1.0.0-alpha5 vs. 1.0.0-alpha23: -1
+1.0-RC5 vs. 1.0-RC20: -1
+1.0-RC11 vs. 1.0-RC6: 1
+1.0.0 vs. 1.0.0-SNAPSHOT: 1
+1.0.0-SNAPSHOT vs. 1.0.0-SNAPSHOT: 0
+1.0.0-SNAPSHOT vs. 1.0.0: -1
+1.0.0 vs. 1.0.0-alpha5: 1
+1.0.0-alpha5 vs. 1.0.0: -1
+1.0.0-SNAPSHOT vs. 1.0.0-RC1: 1
+1.0.0-SNAPSHOT vs. 1.0.1-RC1: -1
+9.1-901.jdbc4 vs. 9.1-901.jdbc3: 1
+9.1-901-1.jdbc4 vs. 9.1-901.jdbc4: -57
+1-SNAPSHOT vs. 1.0-SNAPSHOT: 0
+1-alpha vs. 1-alpha0: 0
+
+(deftest maven-cases
+ (testing "Numeric Comparison"
+ (ok (= (vc:maven-vercmp "1.0.0" "1.0.0") 0))
+ (ok (= (vc:maven-vercmp "1.0.0" "1.0") 0))
+ (ok (> (vc:maven-vercmp "1.0.1" "1.0") 0))
+ (ok (< (vc:maven-vercmp "1.0.0" "1.0.1") 0))
+ (ok (< (vc:maven-vercmp "1.0.0" "1.0.0-1") 0))
+ (ok (> (vc:maven-vercmp "1.0.0" "0.9.2") 0))
+ (ok (< (vc:maven-vercmp "0.9.2" "0.9.3") 0))
+ (ok (> (vc:maven-vercmp "0.9.2" "0.9.1") 0))
+ (ok (< (vc:maven-vercmp "0.9.5" "0.9.13") 0))
+ (ok (< (vc:maven-vercmp "10.2.0.3.0" "11.2.0.3.0") 0))
+ (ok (> (vc:maven-vercmp "10.2.0.3.0" "5.2.0.3.0") 0))
+ (ok (< (vc:maven-vercmp "1.0.0-SNAPSHOT" "1.0.1-SNAPSHOT") 0))
+ (ok (< (vc:maven-vercmp "1.0.0-alpha" "1.0.1-beta") 0))
+ (ok (< (vc:maven-vercmp "1.1-dolphin" "1.1.1-cobra") 0)))
+ (testing "Lexical Comparison"
+ (ok (< (vc:maven-vercmp "1.0-alpaca" "1.0-bermuda") 0))
+ (ok (< (vc:maven-vercmp "1.0-alpaca" "1.0-alpaci") 0))
+ (ok (> (vc:maven-vercmp "1.0-dolphin" "1.0-cobra") 0)))
+ (testing "Qualifier Comparison"
+ (ok (< (vc:maven-vercmp "1.0.0-alpha" "1.0.0-beta") 0))
+ (ok (> (vc:maven-vercmp "1.0.0-beta" "1.0.0-alpha") 0))
+ (ok (< (vc:maven-vercmp "1.0.0-alpaca" "1.0.0-beta") 0))
+ (ok (> (vc:maven-vercmp "1.0.0-final" "1.0.0-milestone") 0)))
+ (testing "Qualifier/Numeric Comparison"
+ (ok (< (vc:maven-vercmp "1.0.0-alpha1" "1.0.0-alpha2") 0))
+ (ok (< (vc:maven-vercmp "1.0.0-alpha5" "1.0.0-alpha23") 0))
+ (ok (< (vc:maven-vercmp "1.0-RC5" "1.0-RC20") 0))
+ (ok (> (vc:maven-vercmp "1.0-RC11" "1.0-RC6") 0)))
+ (testing "Releases are newer than SNAPSHOTs"
+ (ok (> (vc:maven-vercmp "1.0.0" "1.0.0-SNAPSHOT") 0))
+ (ok (= (vc:maven-vercmp "1.0.0-SNAPSHOT" "1.0.0-SNAPSHOT") 0))
+ (ok (< (vc:maven-vercmp "1.0.0-SNAPSHOT" "1.0.0") 0)))
+ (testing "Releases are newer than qualified versions"
+ (ok (> (vc:maven-vercmp "1.0.0" "1.0.0-alpha5") 0))
+ (ok (< (vc:maven-vercmp "1.0.0-alpha5" "1.0.0") 0)))
+ (testing "SNAPSHOTS are newer than qualified versions"
+ (ok (> (vc:maven-vercmp "1.0.0-SNAPSHOT" "1.0.0-RC1") 0))
+ (ok (< (vc:maven-vercmp "1.0.0-SNAPSHOT" "1.0.1-RC1") 0)))
+ (testing "Some other Formats"
+ (ok (> (vc:maven-vercmp "9.1-901.jdbc4" "9.1-901.jdbc3") 0))
+ (ok (> (vc:maven-vercmp "9.1-901-1.jdbc4" "9.1-901.jdbc4") 0)))
+ (testing "Some more zero-extension Tests"
+ (ok (= (vc:maven-vercmp "1-SNAPSHOT" "1.0-SNAPSHOT") 0))
+ (ok (= (vc:maven-vercmp "1-alpha" "1-alpha0") 0))))
+
+
+#+(or)
+(rove:run-test *)
+
+
+
+;(deftest
+; ^:version-comparison naive-cases
+; (testing "Naive comparison cases"
+; (are [v0 v1 r] (r ^java.lang.Integer (naive-vercmp v0 v1) 0)
+; "" "" .equals
+; "abc" "abc" .equals
+; "a.b.c" "a_b-c" .equals
+; "1.2.0" "1.2" >
+; "1.a" "1.2" <
+; "0100" "100" .equals
+; "0100.al0100" "100.al100" <
+; "1000.9.17" "100.9.18" >)))
+;
+;; The following test case versions list is taken from the file
+;; "tests/test_version.py" of the `pypa/packaging` git repository, found here:
+;; https://github.com/pypa/packaging The top of that file carries this copyright
+;; notice:
+;;
+;; This file is dual licensed under the terms of the Apache License, Version
+;; 2.0, and the BSD License. See the LICENSE file in the root of this repository
+;; for complete details.
+;;
+;; The License associated with this list of version numbers can be found in
+;; the file `licenses/LICENSE.python_test` at the root of this repository.
+;
+;(def python-versions [; Implicit epoch of 0
+; "1.0.dev456", "1.0a1", "1.0a2.dev456", "1.0a12.dev456", "1.0a12",
+; "1.0b1.dev456", "1.0b2", "1.0b2.post345.dev456", "1.0b2.post345",
+; "1.0b2-346", "1.0c1.dev456", "1.0c1", "1.0rc2", "1.0c3", "1.0",
+; "1.0.post456.dev34", "1.0.post456", "1.1.dev1", "1.2", "1.2+123abc",
+; "1.2+123abc456", "1.2+abc", "1.2+abc123", "1.2+abc123def", "1.2+1234.abc",
+; "1.2+123456", "1.2.r32+123456", "1.2.rev33+123456",
+;
+; ; Explicit epoch of 1
+; "1!1.0.dev456", "1!1.0a1", "1!1.0a2.dev456", "1!1.0a12.dev456", "1!1.0a12",
+; "1!1.0b1.dev456", "1!1.0b2", "1!1.0b2.post345.dev456", "1!1.0b2.post345",
+; "1!1.0b2-346", "1!1.0c1.dev456", "1!1.0c1", "1!1.0rc2", "1!1.0c3", "1!1.0",
+; "1!1.0.post456.dev34", "1!1.0.post456", "1!1.1.dev1", "1!1.2+123abc",
+; "1!1.2+123abc456", "1!1.2+abc", "1!1.2+abc123", "1!1.2+abc123def",
+; "1!1.2+1234.abc", "1!1.2+123456", "1!1.2.r32+123456", "1!1.2.rev33+123456"])
+;
+;(defn test-python-pair [a b]
+; (testing (str
+; "python version `"
+; a
+; "` is less than `"
+; b
+; "`")
+; (is (< (python-vercmp a b) 0))
+; (is (> (python-vercmp b a) 0))
+; (is (= (python-vercmp a a) 0))))
+;
+;(deftest
+; ^:version-comparison python-cases
+; (testing "Python edge cases"
+; (are [v0 v1 r] (r ^java.lang.Integer (python-vercmp v0 v1) 0)
+; "" "" .equals
+; "1.2.3rc1" "1.2.3RC1" .equals
+; "1.0+foo0100" "1.0+foo100" <
+; "1.0+0100foo" "1.0+100foo" <
+; "1.0.a1" "1.0a1" .equals
+; "1.0.rc1" "1.0rc1" .equals
+; "1.0.b1" "1.0b1" .equals))
+; (test-python-pair (first python-versions)
+; (peek python-versions))
+; (doseq [[a b] (map
+; #(do [%1 %2])
+; (pop python-versions)
+; (rest python-versions))]
+; (test-python-pair a b)))
+;
+;; https://ruby-doc.org/stdlib-2.0.0/libdoc/rubygems/rdoc/Gem/Version.html
+;(deftest
+; ^:version-comparison gem-cases
+; (testing "Gem Cases"
+; (are [v0 v1 r] (r ^java.lang.Integer (rubygem-vercmp v0 v1) 0)
+; "" "" .equals
+; "1.0.0" "0.1.0" >
+; "1.0.0" "2.0.0" <
+; "1.1.1" "1.1.1" .equals
+; "2.0.0" "2.1.0" <
+; "2.1.1" "2.1.0" >
+; "1.0.1" "1.0" >
+; "1.0.0" "1.0.1" <
+; "1.0.0" "0.9.2" >
+; "0.9.2" "0.9.3" <
+; "0.9.2" "0.9.1" >
+; "0.9.5" "0.9.13" <
+; "10.2.0.3.0" "11.2.0.3.0" <
+; "10.2.0.3.0" "5.2.0.3.0" >
+; "1.0" "1" >
+; "1.2.a" "1.2" <
+; "1.2.z" "1.2" <
+; "1.1.z" "1.0" >
+; "1.0.a10" "1.0.a9" >
+; "1.0" "1.0.b1" >
+; "1.0.a2" "1.0.b1" <
+; "1.0.a2" "0.9" >
+; "1.0.a.10" "1.0.a10" .equals
+; "1.0.a10" "1.0.a.10" .equals)))
+;
+;(deftest
+; ^:version-comparison semver-cases
+; (testing "Semver Cases"
+; (are [v0 v1 r] (r ^java.lang.Integer (semver-vercmp v0 v1) 0)
+; "" "" .equals
+; "1.0.0+001" "1.0.0+20130313144700" .equals
+; "1.0.0+exp.sha.5114f85" "1.0.0" .equals
+; "1.0.0-alpha" "1.0.0-alpha.1" <
+; "1.0.0-alpha.1" "1.0.0-alpha.beta" <
+; "1.0.0-alpha.beta" "1.0.0-beta" <
+; "1.0.0-beta.2" "1.0.0-beta" >
+; "1.0.0-beta.2" "1.0.0-beta.11" <
+; "1.0.0-beta.11" "1.0.0-rc.1" <
+; "1.0.0-rc.1" "1.0.0" <
+; "1.0.0" "0.1.0" >
+; "1.2.10" "1.2.9" >
+; "9.8.7" "9.8.7+burarum" .equals
+; "1.0.0" "2.0.0" <
+; "2.0.0" "2.1.0" <
+; "2.1.1" "2.1.0" >
+; "1.0.0" "1.0.0-alpha" >)))
+;
+;(deftest
+; ^:version-comparison rpm-cases
+; ; https://fedoraproject.org/wiki/Archive:Tools/RPM/VersionComparison
+; (testing "Random cases"
+; (are [v0 v1 r] (r ^java.lang.Integer (rpm-vercmp v0 v1) 0)
+; "" "" .equals
+; "1.2.3" "1.2-3" .equals
+; "1.0010" "1.9" >
+; "1.05" "1.5" .equals
+; "1.0" "1" >
+; "2.50" "2.5" >
+; "fc4" "fc.4" .equals
+; "FC5" "fc4" <
+; "2a" "2.0" <
+; "2.a" "2a" .equals
+; "1.0" "1.fc4" >
+; "3.0.0_fc" "3.0.0.fc" .equals
+; "~~" "~~a" <
+; "~" "~~a" >
+; "~" "" <
+; "a" "" >)))
+;
+;
A => tests/version.lisp +11 -0
@@ 1,11 @@
+(defpackage #:skin.djha.zippm/tests/version
+ (:use #:cl
+ #:rove)
+ (:import-from #:skin.djha.zippm/version))
+(in-package #:skin.djha.zippm/tests/version)
+
+;; NOTE: To run this test file, execute `(asdf:test-system :zippm)' in your Lisp.
++(or)
+(rove:run-test *)
+
+