diff --git a/tests/test_versions/Makefile b/tests/test_versions/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..8bebd1c7dc24a69cb37af9a620fd2eef47bda0d1
--- /dev/null
+++ b/tests/test_versions/Makefile
@@ -0,0 +1,19 @@
+
+.PHONY: default
+default: test
+
+.PHONY: test
+test: check_env clean
+	@./test.sh test.txt
+
+.PHONY: check_env
+check_env: guard-EPICS_BASE guard-E3_REQUIRE_VERSION
+	@#$(or $(wildcard ${EPICS_BASE}/require/${E3_REQUIRE_VERSION}/bin/setE3Env.bash), $(error setE3Env.bash not found. Please check your version of base and require.))
+
+guard-%:
+	@#$(or ${$*}, $(error $* is not set))
+
+.PHONY: clean
+clean:
+	rm -rf test/cellMods
+	rm -rf test/testversions/O.*
diff --git a/tests/test_versions/test.sh b/tests/test_versions/test.sh
new file mode 100755
index 0000000000000000000000000000000000000000..64eaac9668278c0093ef195aac3998740dd05b0d
--- /dev/null
+++ b/tests/test_versions/test.sh
@@ -0,0 +1,173 @@
+#!/usr/bin/env bash
+
+# Defaults
+LOGFILE=test.log
+MODULENAME=testversions
+EPICS_BASE=${EPICS_BASE:-/epics/base-7.0.5}
+REQUIRE_VER=${E3_REQUIRE_VERSION:-3.4.1}
+
+
+if which ack &> /dev/null ; then
+    __SEARCH_COMMAND__="ack --passthru"
+else
+    echo "ack is not installed; defaulting to grep, but note that the log will be missing the IOC log" >&2
+    __SEARCH_COMMAND__=grep
+fi
+
+function usage {
+    echo "Usage:"
+    echo "  bash test.sh [-b <EPICS_BASE>] [-r <REQUIRE_VER>] [-h] testfile"
+    exit 0
+}
+
+function show_header {
+    echo "Testing require version loader"
+    echo "============================================================"
+    echo -e "EPICS base:          \033[34m${EPICS_BASE}\033[0m"
+    echo -e "Require version:     \033[34m${REQUIRE_VER}\033[0m"
+    echo "============================================================"
+}
+
+function install_vers
+{
+    for ver in $@; do
+        echo "=========================================="
+        echo "Installing local version version '$ver'"
+        echo "=========================================="
+        make -C test clean cellinstall __DEBUG_VERSION=$ver __EPICS_BASE_LOCATION="$EPICS_BASE" __REQUIRE_VERSION="$REQUIRE_VER" E3_NO_TEST=true
+    done
+}
+
+function clean_vers
+{
+    rm -rf test/cellMods
+}
+
+function test_require
+{
+    if [ "$#" -lt "3" ]; then
+        usage
+        return
+    fi
+
+    required=$1
+    shift
+    expected=$1
+    shift
+    args=$@
+
+    if [ "$required" = "-" ]; then
+        req_str="$MODULENAME"
+    else
+        req_str="$MODULENAME,$required"
+    fi
+
+    echo "============================================================"
+    echo "Running test case:"
+    echo "Requested: $required"
+    echo "Expected:  $expected"
+    echo "Installed: $args"
+    echo "============================================================"
+
+    required=$(sed 's/+/\\+/g' <<< $required)
+    expected=$(sed 's/+/\\+/g' <<< $expected)
+
+    clean_vers
+
+    install_vers $args
+
+    echo "=========================================="
+    echo "Running iocsh.bash -l cellMods -r $req_str"
+    echo "=========================================="
+
+    iocsh_args="-l test/cellMods -c 'var requireDebug 1' -r $req_str"
+
+    if [ "$expected" = "-" ]; then
+        search_string="^Module $MODULENAME (not available|version $required not available|version $required not available \(but other versions are available\))\$"
+    else
+        search_string="^Loaded $MODULENAME version $expected\$"
+    fi
+
+    # The funny sed command here is from https://unix.stackexchange.com/a/235016/445931
+    # The reason this is needed is because the "Starting iocInit" message seems to be threaded 
+    # differently than the regular flow of output and sometimes will separate the strings that
+    # we are looking for. If we simply cut it out then we seem to be able to more consistently
+    # get meaningful tests.
+    echo exit | eval iocsh.bash "$iocsh_args" | \
+    sed -e '1h;2,$H;$!d;g' -e 's/Starting iocInit\niocRun: All initialization complete\n//' | \
+    $__SEARCH_COMMAND__ "${search_string}"
+}
+
+function format_output {
+    result=$1
+    shift
+    requested=$1
+    shift
+    expected=$1
+    shift
+    versions=$@
+
+    if [ "$result" = "0" ]; then
+        result_str="\033[32mpassed\033[0m"
+    else
+        result_str="\033[31mfailed\033[0m"
+    fi
+
+    printf "Test: $result_str\n"
+    printf "requested: %-20s     expected: %-20s\n" "$requested" "$expected"
+    printf "Given versions: %s\n" "$versions"
+    printf "============================================================\n"
+}
+
+while getopts "hb:r:c:" opt; do
+    case $opt in
+        b) EPICS_BASE="$OPTARG"
+            ;;
+        r) REQUIRE_VER="$OPTARG"
+            ;;
+        c) COMMAND="$OPTARG"
+            ;;
+        h) HELP=true
+            ;;
+        *)
+            usage
+            exit 1
+    esac
+done
+shift $((OPTIND-1))
+TESTFILE=$1
+ 
+if [ "$HELP" = true ]; then
+    usage
+    exit 0
+fi
+
+rm -f "$LOGFILE"
+
+ENV_SRC="$EPICS_BASE/require/$REQUIRE_VER/bin/setE3Env.bash"
+if [ ! -f "$ENV_SRC" ]; then
+    echo "setE3Env.bash not found at location $ENV_SRC" >&2
+    exit 1
+fi
+source "$ENV_SRC" &> /dev/null
+
+show_header
+
+failed_tests=0
+if [ -n "$COMMAND" ]; then
+    test_require $COMMAND &>> "$LOGFILE"
+    result=$?
+    [ "$result" = 0 ] || ((failed_tests++))
+    format_output $result $COMMAND
+fi
+
+if [ -f "$TESTFILE" ]; then
+    while read -r line; do
+        test_require $line &>> "$LOGFILE"
+        result=$?
+        [ "$result" = 0 ] || ((failed_tests++))
+        format_output $result $line
+    done < <(sed -e 's/[[:space:]]*#.*//; /^[[:space:]]*$/d' "$TESTFILE")
+fi
+
+[ "$failed_tests" = 0 ]
diff --git a/tests/test_versions/test.txt b/tests/test_versions/test.txt
new file mode 100644
index 0000000000000000000000000000000000000000..8e5c2ac2c75f71694ddf28742472f4922078539a
--- /dev/null
+++ b/tests/test_versions/test.txt
@@ -0,0 +1,35 @@
+# Basic set of tests.
+#
+# Note that the first entry is the requested test version (- means none requested)
+# The second entry is what is expected to be loaded (- means it is expected to fail)
+# The third and other entries are the versions to install.
+
+# It should succesfully load a test version
+test test test 0.0.1
+
+# It should prioritize a numeric version over a test one
+- 0.0.1+0 test 0.0.1
+- 0.0.1+0 0.0.1 test
+
+# If no version is specified, it should load the highest numeric version
+- 0.0.1+7 0.0.1 0.0.1+7 0.0.1+3
+
+# If no build number is specified, it should load the highest build number
+0.0.1 0.0.1+4 0.0.1 0.0.1+4
+
+# Build number 0 means load that version.
+0.0.1+0 0.0.1+0 0.0.1 0.0.1+1
+0.0.1+0 0.0.1+0 0.0.1+0
+
+# Only load exactly the given build number if specified.
+0.0.1+0 - 0.0.1+1
+
+# 1-test counts as a test version
+- 0.0.1+0 0.0.1 1-test
+
+0.0.1 0.0.1+0 0.0.1 0.0.1+test
+
+0.0.1 0.0.1+0 0.0.1+test 0.0.1
+
+# A numeric version should be prioritized over a "higher" test version
+- 0.1.0+0 0.1.0 0.2.0-rc1
diff --git a/tests/test_versions/test/Makefile b/tests/test_versions/test/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..8bac5608ed123b17485362306dfafbe966942382
--- /dev/null
+++ b/tests/test_versions/test/Makefile
@@ -0,0 +1,53 @@
+#
+#  Copyright (c) 2019 - 2020, European Spallation Source ERIC
+#
+#  The program is free software: you can redistribute
+#  it and/or modify it under the terms of the GNU General Public License
+#  as published by the Free Software Foundation, either version 2 of the
+#  License, or any newer version.
+#
+#  This program is distributed in the hope that it will be useful, but WITHOUT
+#  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+#  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+#  more details.
+#
+#  You should have received a copy of the GNU General Public License along with
+#  this program. If not, see https://www.gnu.org/licenses/gpl-2.0.txt
+#
+# 
+# Author  : Simon Rose
+# email   : simon.rose@ess.eu
+# Date    : 2020-08-24
+# version : 0.0.0 
+
+TOP:=$(CURDIR)
+
+# To configure require
+EPICS_BASE:=$(__EPICS_BASE_LOCATION)
+E3_REQUIRE_VERSION:=$(__REQUIRE_VERSION)
+
+E3_REQUIRE_LOCATION := $(EPICS_BASE)/require/$(E3_REQUIRE_VERSION)
+REQUIRE_CONFIG := $(E3_REQUIRE_LOCATION)/configure
+
+# To configure the modules
+EPICS_MODULE_NAME:=testversions
+E3_MODULE_VERSION:=$(__DEBUG_VERSION)
+E3_MODULE_NAME:=$(EPICS_MODULE_NAME)
+E3_MODULE_SRC_PATH:=$(EPICS_MODULE_NAME)
+E3_MODULE_MAKEFILE:=$(EPICS_MODULE_NAME).Makefile
+
+include $(REQUIRE_CONFIG)/CONFIG
+include $(REQUIRE_CONFIG)/RULES_SITEMODS
+
+default: test
+
+.PHONY: db
+db:
+
+#.PHONY: test
+#test:
+#
+#.PHONY: clean
+#clean:
+#	rm -rf cellMods
+#	rm -rf $(E3_MODULE_SRC_PATH)/O.*
\ No newline at end of file
diff --git a/tests/test_versions/test/testversions.Makefile b/tests/test_versions/test/testversions.Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..62a01b97b0354e9d280ef007f954e95ef915e485
--- /dev/null
+++ b/tests/test_versions/test/testversions.Makefile
@@ -0,0 +1,5 @@
+where_am_I := $(dir $(abspath $(lastword $(MAKEFILE_LIST))))
+include $(E3_REQUIRE_TOOLS)/driver.makefile
+
+DBDS      += ./test.dbd
+
diff --git a/tests/test_versions/test/testversions/test.dbd b/tests/test_versions/test/testversions/test.dbd
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/tests/test_versions/test/testversions/testversions.Makefile b/tests/test_versions/test/testversions/testversions.Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..62a01b97b0354e9d280ef007f954e95ef915e485
--- /dev/null
+++ b/tests/test_versions/test/testversions/testversions.Makefile
@@ -0,0 +1,5 @@
+where_am_I := $(dir $(abspath $(lastword $(MAKEFILE_LIST))))
+include $(E3_REQUIRE_TOOLS)/driver.makefile
+
+DBDS      += ./test.dbd
+