Add vercmp function

The existing vercmp_numbers function only handles, as the name says,
numbers.  I noticed that "sort" has had a version sort for a long time
[1] and, rather than re-implement it badly, use this as a version of
vercmp that works a bit more naturally.

This is intended to be used in an "if" statement as in

  prog_ver=$(prog_ver --version | grep ...)
  if vercmp $prog_ver "<" 2.0; then
     ...
  fi

A test-case is added to test the basic features and some edge-cases.

[1] http://git.savannah.gnu.org/gitweb/?p=coreutils.git;a=commitdiff;h=4c9fae4e97d95a9f89d1399a8aeb03051f0fec96

Change-Id: Ie55283acdc40a095b80b2631a55310072883ad0d
This commit is contained in:
Ian Wienand 2015-11-12 13:52:36 +11:00
parent bcad037697
commit 2ba36cda79
3 changed files with 138 additions and 0 deletions

View File

@ -527,12 +527,58 @@ function vercmp_numbers {
typeset v1=$1 v2=$2 sep
typeset -a ver1 ver2
deprecated "vercmp_numbers is deprecated for more generic vercmp"
IFS=. read -ra ver1 <<< "$v1"
IFS=. read -ra ver2 <<< "$v2"
_vercmp_r "${#ver1[@]}" "${ver1[@]}" "${ver2[@]}"
}
# vercmp ver1 op ver2
# Compare VER1 to VER2
# - op is one of < <= == >= >
# - returns true if satisified
# e.g.
# if vercmp 1.0 "<" 2.0; then
# ...
# fi
function vercmp {
local v1=$1
local op=$2
local v2=$3
local result
# sort the two numbers with sort's "-V" argument. Based on if v2
# swapped places with v1, we can determine ordering.
result=$(echo -e "$v1\n$v2" | sort -V | head -1)
case $op in
"==")
[ "$v1" = "$v2" ]
return
;;
">")
[ "$v1" != "$v2" ] && [ "$result" = "$v2" ]
return
;;
"<")
[ "$v1" != "$v2" ] && [ "$result" = "$v1" ]
return
;;
">=")
[ "$result" = "$v2" ]
return
;;
"<=")
[ "$result" = "$v1" ]
return
;;
*)
die $LINENO "unrecognised op: $op"
;;
esac
}
# This function sets log formatting options for colorizing log
# output to stdout. It is meant to be called by lib modules.

47
tests/test_vercmp.sh Executable file
View File

@ -0,0 +1,47 @@
#!/usr/bin/env bash
# Tests for DevStack vercmp functionality
TOP=$(cd $(dirname "$0")/.. && pwd)
# Import common functions
source $TOP/functions
source $TOP/tests/unittest.sh
assert_true "numeric gt" vercmp 2.0 ">" 1.0
assert_true "numeric gte" vercmp 2.0 ">=" 1.0
assert_true "numeric gt" vercmp 1.0.1 ">" 1.0
assert_true "numeric gte" vercmp 1.0.1 ">=" 1.0
assert_true "alpha gt" vercmp 1.0.1b ">" 1.0.1a
assert_true "alpha gte" vercmp 1.0.1b ">=" 1.0.1a
assert_true "alpha gt" vercmp b ">" a
assert_true "alpha gte" vercmp b ">=" a
assert_true "alpha gt" vercmp 2.0-rc3 ">" 2.0-rc1
assert_true "alpha gte" vercmp 2.0-rc3 ">=" 2.0-rc1
assert_false "numeric gt fail" vercmp 1.0 ">" 1.0
assert_true "numeric gte" vercmp 1.0 ">=" 1.0
assert_false "numeric gt fail" vercmp 0.9 ">" 1.0
assert_false "numeric gte fail" vercmp 0.9 ">=" 1.0
assert_false "numeric gt fail" vercmp 0.9.9 ">" 1.0
assert_false "numeric gte fail" vercmp 0.9.9 ">=" 1.0
assert_false "numeric gt fail" vercmp 0.9a.9 ">" 1.0.1
assert_false "numeric gte fail" vercmp 0.9a.9 ">=" 1.0.1
assert_false "numeric lt" vercmp 1.0 "<" 1.0
assert_true "numeric lte" vercmp 1.0 "<=" 1.0
assert_true "numeric lt" vercmp 1.0 "<" 1.0.1
assert_true "numeric lte" vercmp 1.0 "<=" 1.0.1
assert_true "alpha lt" vercmp 1.0.1a "<" 1.0.1b
assert_true "alpha lte" vercmp 1.0.1a "<=" 1.0.1b
assert_true "alpha lt" vercmp a "<" b
assert_true "alpha lte" vercmp a "<=" b
assert_true "alpha lt" vercmp 2.0-rc1 "<" 2.0-rc3
assert_true "alpha lte" vercmp 2.0-rc1 "<=" 2.0-rc3
assert_true "eq" vercmp 1.0 "==" 1.0
assert_true "eq" vercmp 1.0.1 "==" 1.0.1
assert_false "eq fail" vercmp 1.0.1 "==" 1.0.2
assert_false "eq fail" vercmp 2.0-rc1 "==" 2.0-rc2
report_results

View File

@ -92,6 +92,51 @@ function assert_empty {
fi
}
# assert the arguments evaluate to true
# assert_true "message" arg1 arg2
function assert_true {
local lineno
lineno=`caller 0 | awk '{print $1}'`
local function
function=`caller 0 | awk '{print $2}'`
local msg=$1
shift
$@
if [ $? -eq 0 ]; then
PASS=$((PASS+1))
echo "PASS: $function:L$lineno - $msg"
else
FAILED_FUNCS+="$function:L$lineno\n"
echo "ERROR: test failed in $function:L$lineno!"
echo " $msg"
ERROR=$((ERROR+1))
fi
}
# assert the arguments evaluate to false
# assert_false "message" arg1 arg2
function assert_false {
local lineno
lineno=`caller 0 | awk '{print $1}'`
local function
function=`caller 0 | awk '{print $2}'`
local msg=$1
shift
$@
if [ $? -eq 0 ]; then
FAILED_FUNCS+="$function:L$lineno\n"
echo "ERROR: test failed in $function:L$lineno!"
echo " $msg"
ERROR=$((ERROR+1))
else
PASS=$((PASS+1))
echo "PASS: $function:L$lineno - $msg"
fi
}
# Print a summary of passing and failing tests and exit
# (with an error if we have failed tests)
# usage: report_results