~skin/cl-vercmp

2689ffa8640c6342d215fd9f83c7afe1e60d322f — Daniel Jay Haskin 6 months ago main
Don't want this stuff to go to waste
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 *)