eapi7-ver.eclass: Initial implementation of ver_test().
authorUlrich Müller <ulm@gentoo.org>
Wed, 20 Sep 2017 19:28:22 +0000 (21:28 +0200)
committerUlrich Müller <ulm@gentoo.org>
Tue, 26 Sep 2017 18:46:26 +0000 (20:46 +0200)
This should strictly follow Algorithms 3.1 to 3.7 specified in PMS:
https://projects.gentoo.org/pms/6/pms.html#x1-310003.3

eclass/eapi7-ver.eclass
eclass/tests/eapi7-ver.sh
eclass/tests/eapi7-ver_benchmark.sh

index 6f8f0c0a1c37acdebc91f57817a7f847744c04ca..1ad1cbe2edc21c7c0b1d6a3bcf521b02d2321da2 100644 (file)
@@ -16,8 +16,6 @@
 #
 # https://bugs.gentoo.org/482170
 #
-# Note: version comparison function is not included currently.
-#
 # @ROFF .SS
 # Version strings
 #
@@ -185,5 +183,127 @@ ver_rs() {
 # revision parts), and the comparison is performed according to
 # the algorithm specified in the PMS.
 ver_test() {
-       die "${FUNCNAME}: not implemented"
+       local v1 v2 op i tail result
+       local -a v1comp v2comp
+       local match=(
+               "+([0-9])*(.+([0-9]))"                                          # numeric components
+               "[a-z]"                                                                         # letter component
+               "*(@(_alpha|_beta|_pre|_rc|_p)*([0-9]))"        # suffixes
+               "-r+([0-9])"                                                            # revision
+       )
+
+       local LC_ALL=C shopt_save=$(shopt -p extglob)
+       shopt -s extglob
+
+       if [[ $# -eq 2 ]]; then
+               v1=${PVR}
+       elif [[ $# -eq 3 ]]; then
+               v1=$1; shift
+       else
+               die "${FUNCNAME}: bad number of arguments"
+       fi
+       op=$1
+       v2=$2
+
+       case ${op} in
+               -eq|-ne|-lt|-le|-gt|-ge) ;;
+               *) die "${FUNCNAME}: invalid operator: ${op}" ;;
+       esac
+
+       # Test for both versions being valid, and split them into parts
+       for (( i=0; i<4; i++ )); do
+               tail=${v1##${match[i]}}
+               v1comp[i]=${v1%"${tail}"}
+               v1=${tail}
+               tail=${v2##${match[i]}}
+               v2comp[i]=${v2%"${tail}"}
+               v2=${tail}
+       done
+       # There must not be any remaining tail, and the numeric part
+       # must be non-empty.  All other parts are optional.
+       [[ -z ${v1} && -z ${v2} && -n ${v1comp[0]} && -n ${v2comp[0]} ]] \
+               || die "${FUNCNAME}: invalid version"
+
+       # Compare numeric components (PMS algorithm 3.2)
+       _ver_cmp_num() {
+               local a=(${1//./ }) b=(${2//./ })
+               local an=${#a[@]} bn=${#b[@]}
+               local i
+               # First component
+               [[ 10#${a[0]} -gt 10#${b[0]} ]] && return 2
+               [[ 10#${a[0]} -lt 10#${b[0]} ]] && return 1
+               for (( i=1; i<an && i<bn; i++ )); do
+                       # Other components (PMS algorithm 3.3)
+                       if [[ ${a[i]} == 0* || ${b[i]} == 0* ]]; then
+                               local ap=${a[i]%%*(0)} bp=${b[i]%%*(0)}
+                               [[ ${ap} > ${bp} ]] && return 2
+                               [[ ${ap} < ${bp} ]] && return 1
+                       else
+                               [[ ${a[i]} -gt ${b[i]} ]] && return 2
+                               [[ ${a[i]} -lt ${b[i]} ]] && return 1
+                       fi
+               done
+               [[ ${an} -gt ${bn} ]] && return 2
+               [[ ${an} -lt ${bn} ]] && return 1
+               return 0
+       }
+
+       # Compare letter components (PMS algorithm 3.4)
+       _ver_cmp_let() {
+               local a=$1 b=$2
+               [[ ${a} > ${b} ]] && return 2
+               [[ ${a} < ${b} ]] && return 1
+               return 0
+       }
+
+       # Compare suffixes (PMS algorithm 3.5)
+       _ver_cmp_suf() {
+               local a=(${1//_/ }) b=(${2//_/ })
+               local an=${#a[@]} bn=${#b[@]}
+               local i
+               for (( i=0; i<an && i<bn; i++ )); do
+                       # Compare each suffix (PMS algorithm 3.6)
+                       if [[ ${a[i]%%*([0-9])} == "${b[i]%%*([0-9])}" ]]; then
+                               [[ 10#${a[i]##*([a-z])} -gt 10#${b[i]##*([a-z])} ]] && return 2
+                               [[ 10#${a[i]##*([a-z])} -lt 10#${b[i]##*([a-z])} ]] && return 1
+                       else
+                               # Check for p first
+                               [[ ${a[i]} == p*([0-9]) ]] && return 2
+                               [[ ${b[i]} == p*([0-9]) ]] && return 1
+                               # Hack: Use that alpha < beta < pre < rc alphabetically
+                               [[ ${a[i]} > ${b[i]} ]] && return 2 || return 1
+                       fi
+               done
+               if [[ ${an} -gt ${bn} ]]; then
+                       [[ ${a[bn]} == p*([0-9]) ]] && return 2 || return 1
+               elif [[ ${an} -lt ${bn} ]]; then
+                       [[ ${b[an]} == p*([0-9]) ]] && return 1 || return 2
+               fi
+               return 0
+       }
+
+       # Compare revision components (PMS algorithm 3.7)
+       _ver_cmp_rev() {
+               local a=${1#-r} b=${2#-r}
+               [[ 10#${a} -gt 10#${b} ]] && return 2
+               [[ 10#${a} -lt 10#${b} ]] && return 1
+               return 0
+       }
+
+       # Version comparison top-level logic (PMS algorithm 3.1)
+       _ver_cmp_num "${v1comp[0]}" "${v2comp[0]}" &&
+       _ver_cmp_let "${v1comp[1]}" "${v2comp[1]}" &&
+       _ver_cmp_suf "${v1comp[2]}" "${v2comp[2]}" &&
+       _ver_cmp_rev "${v1comp[3]}" "${v2comp[3]}"
+
+       case $? in
+               0) result=0  ;;                 # a = b
+               1) result=-1 ;;                 # a < b
+               2) result=1  ;;                 # a > b
+               *) die "${FUNCNAME}: invalid return code: $?" ;;
+       esac
+
+       ${shopt_save}
+
+       test "${result}" "${op}" 0
 }
index 8a96e4d29b1bc7af934fa0f759e3031fe7208257..fd085a415b6ea6d9fcf4b8c4f91cdcd93c69076c 100755 (executable)
@@ -17,6 +17,15 @@ teq() {
        tend ${?} "returned: ${got}"
 }
 
+teqr() {
+       local expected=$1; shift
+       tbegin "$* -> ${expected}"
+       "$@"
+       local ret=$?
+       [[ ${ret} -eq ${expected} ]]
+       tend $? "returned: ${ret}"
+}
+
 txf() {
        tbegin "XFAIL: ${*}"
        local got=$("${@}" 2>&1)
@@ -63,3 +72,106 @@ teq 1.2.3 ver_rs 3-5 . 1.2.3
 txf ver_cut foo 1.2.3
 txf ver_rs -3 _ a1b2c3d4e5
 txf ver_rs 5-3 _ a1b2c3d4e5
+
+# Tests from Portage's test_vercmp.py
+teqr 0 ver_test 6.0 -gt 5.0
+teqr 0 ver_test 5.0 -gt 5
+teqr 0 ver_test 1.0-r1 -gt 1.0-r0
+teqr 0 ver_test 999999999999999999 -gt 999999999999999998 # 18 digits
+teqr 0 ver_test 1.0.0 -gt 1.0
+teqr 0 ver_test 1.0.0 -gt 1.0b
+teqr 0 ver_test 1b -gt 1
+teqr 0 ver_test 1b_p1 -gt 1_p1
+teqr 0 ver_test 1.1b -gt 1.1
+teqr 0 ver_test 12.2.5 -gt 12.2b
+teqr 0 ver_test 4.0 -lt 5.0
+teqr 0 ver_test 5 -lt 5.0
+teqr 0 ver_test 1.0_pre2 -lt 1.0_p2
+teqr 0 ver_test 1.0_alpha2 -lt 1.0_p2
+teqr 0 ver_test 1.0_alpha1 -lt 1.0_beta1
+teqr 0 ver_test 1.0_beta3 -lt 1.0_rc3
+teqr 0 ver_test 1.001000000000000001 -lt 1.001000000000000002
+teqr 0 ver_test 1.00100000000 -lt 1.001000000000000001
+teqr 0 ver_test 999999999999999998 -lt 999999999999999999
+teqr 0 ver_test 1.01 -lt 1.1
+teqr 0 ver_test 1.0-r0 -lt 1.0-r1
+teqr 0 ver_test 1.0 -lt 1.0-r1
+teqr 0 ver_test 1.0 -lt 1.0.0
+teqr 0 ver_test 1.0b -lt 1.0.0
+teqr 0 ver_test 1_p1 -lt 1b_p1
+teqr 0 ver_test 1 -lt 1b
+teqr 0 ver_test 1.1 -lt 1.1b
+teqr 0 ver_test 12.2b -lt 12.2.5
+teqr 0 ver_test 4.0 -eq 4.0
+teqr 0 ver_test 1.0 -eq 1.0
+teqr 0 ver_test 1.0-r0 -eq 1.0
+teqr 0 ver_test 1.0 -eq 1.0-r0
+teqr 0 ver_test 1.0-r0 -eq 1.0-r0
+teqr 0 ver_test 1.0-r1 -eq 1.0-r1
+teqr 1 ver_test 1 -eq 2
+teqr 1 ver_test 1.0_alpha -eq 1.0_pre
+teqr 1 ver_test 1.0_beta -eq 1.0_alpha
+teqr 1 ver_test 1 -eq 0.0
+teqr 1 ver_test 1.0-r0 -eq 1.0-r1
+teqr 1 ver_test 1.0-r1 -eq 1.0-r0
+teqr 1 ver_test 1.0 -eq 1.0-r1
+teqr 1 ver_test 1.0-r1 -eq 1.0
+teqr 1 ver_test 1.0 -eq 1.0.0
+teqr 1 ver_test 1_p1 -eq 1b_p1
+teqr 1 ver_test 1b -eq 1
+teqr 1 ver_test 1.1b -eq 1.1
+teqr 1 ver_test 12.2b -eq 12.2
+
+# A subset of tests from Paludis
+teqr 0 ver_test 1.0_alpha -gt 1_alpha
+teqr 0 ver_test 1.0_alpha -gt 1
+teqr 0 ver_test 1.0_alpha -lt 1.0
+teqr 0 ver_test 1.2.0.0_alpha7-r4 -gt 1.2_alpha7-r4
+teqr 0 ver_test 0001 -eq 1
+teqr 0 ver_test 01 -eq 001
+teqr 0 ver_test 0001.1 -eq 1.1
+teqr 0 ver_test 01.01 -eq 1.01
+teqr 0 ver_test 1.010 -eq 1.01
+teqr 0 ver_test 1.00 -eq 1.0
+teqr 0 ver_test 1.0100 -eq 1.010
+teqr 0 ver_test 1-r00 -eq 1-r0
+
+# Additional tests
+teqr 0 ver_test 0_rc99 -lt 0
+teqr 0 ver_test 011 -eq 11
+teqr 0 ver_test 019 -eq 19
+teqr 0 ver_test 1.2 -eq 001.2
+teqr 0 ver_test 1.2 -gt 1.02
+teqr 0 ver_test 1.2a -lt 1.2b
+teqr 0 ver_test 1.2_pre1 -gt 1.2_pre1_beta2
+teqr 0 ver_test 1.2_pre1 -lt 1.2_pre1_p2
+teqr 0 ver_test 1.00 -lt 1.0.0
+teqr 0 ver_test 1.010 -eq 1.01
+teqr 0 ver_test 1.01 -lt 1.1
+teqr 0 ver_test 1.2_pre08-r09 -eq 1.2_pre8-r9
+teqr 0 ver_test 0 -lt 576460752303423488 # 2**59
+#teqr 0 ver_test 0 -lt 9223372036854775808 # 2**63 fails, integer rollover
+
+# Bad number or ordering of arguments
+txf ver_test 1
+txf ver_test 1 -lt 2 3
+txf ver_test -lt 1 2
+
+# Bad operators
+txf ver_test 1 "<" 2
+txf ver_test 1 lt 2
+txf ver_test 1 -foo 2
+
+# Malformed versions
+txf ver_test "" -ne 1
+txf ver_test 1. -ne 1
+txf ver_test 1ab -ne 1
+txf ver_test b -ne 1
+txf ver_test 1-r1_pre -ne 1
+txf ver_test 1-pre1 -ne 1
+txf ver_test 1_foo -ne 1
+txf ver_test 1_pre1.1 -ne 1
+txf ver_test 1-r1.0 -ne 1
+txf ver_test cvs.9999 -ne 9999
+
+texit
index 1de26444c9b3d562cb21ea4a5f90dae01f084ea4..c46713713368162faf823bec7c55a8e31164788c 100755 (executable)
@@ -76,6 +76,38 @@ replacing_versionator() {
        done >/dev/null
 }
 
+comparing() {
+       local x
+       for x in {1..1000}; do
+               ver_test 1b_p1 -le 1_p1
+               ver_test 1.1b -le 1.1
+               ver_test 12.2.5 -le 12.2b
+               ver_test 4.0 -le 5.0
+               ver_test 5 -le 5.0
+               ver_test 1.0_pre2 -le 1.0_p2
+               ver_test 1.0_alpha2 -le 1.0_p2
+               ver_test 1.0_alpha1 -le 1.0_beta1
+               ver_test 1.0_beta3 -le 1.0_rc3
+               ver_test 1.001000000000000001 -le 1.001000000000000002
+       done
+}
+
+comparing_versionator() {
+       local x
+       for x in {1..100}; do
+               version_is_at_least 1b_p1 1_p1
+               version_is_at_least 1.1b 1.1
+               version_is_at_least 12.2.5 12.2b
+               version_is_at_least 4.0 5.0
+               version_is_at_least 5 5.0
+               version_is_at_least 1.0_pre2 1.0_p2
+               version_is_at_least 1.0_alpha2 1.0_p2
+               version_is_at_least 1.0_alpha1 1.0_beta1
+               version_is_at_least 1.0_beta3 1.0_rc3
+               version_is_at_least 1.001000000000000001 1.001000000000000002
+       done
+}
+
 get_times() {
        local factor=${1}; shift
        echo "${*}"
@@ -111,3 +143,5 @@ get_times 1 cutting
 get_times 10 cutting_versionator
 get_times 1 replacing
 get_times 10 replacing_versionator
+get_times 1 comparing
+get_times 10 comparing_versionator