#!/bin/sh
# SPDX-FileCopyrightText: Copyright Eric S. Raymond <esr@thyrsus.com>
# SPDX-License-Identifier: BSD-2-Clause
#
# Regression tester for SRC.
#
# Emits messages in TAP (Test Anything Protocol).
#
# Use -b to test with a specified back end, rcs or sccs
# Use -e to test with an alternate src version
# Use -l to set a log-redirect option on src while tests are running
# Use -p to test with a specified Python interpreter, python2 or python3
# Use -t to put test files and masters at a fixed location, not removing on exit
# Use -c to do coverage analysis; the argument is the coverage data filename
#
# If the -b and -n options are not forced, all combinations
# are tested sequentially.

historify () {
    case $backend in
	rcs) history=.src/$1,v ;;
	sccs) history=SCCS/s.$1 ;;
	*) echo "srctest: unsupported backend $backend"
    esac
}

nuke() {
    rm -f "$1" ".src/$1,v" "RCS/$1,v" "SCCS/s.$1"
}

fresh_start() {
    rm -f .src/* RCS/* SCCS/*
}

# No dependencies on master name formats after this like

# Set the umask for a bit of defensiveness
umask 0077

# Set the PATH to include the current directory, so the repository
# head version of src can always be tested.
PATH="$(pwd)":$PATH

backend=""
python=""
testmode=no
testcount=1
passed=""
logfile=""
expect_empty="-e"

while getopts :b:c:e:l:p:t opt
do
    case $opt in
	b) backend=$OPTARG;;
	c) coverage erase;
	   src="coverage run -a --data-file=$(pwd)/$OPTARG $(pwd)/src"
	   logfile=/dev/null
	   passed="-L /dev/null -d -d -d -d"
	   expect_empty=""
	   ;;
	e) src=$OPTARG;;
	l) logfile="$(pwd)/$OPTARG"; echo "# Start $(date):" >"${logfile}";;
	p) python=$OPTARG;;
	t) testmode=yes;;
	*) echo "srctest: unknown option"; exit 1;;
    esac
done
# shellcheck disable=SC2004
shift $(($OPTIND - 1))

$backend >/dev/null 2>&1
if [ "$?" = 127 ]
then
    echo "not ok ${testcount} - backend ${backend} is missing."
    testcount=$((testcount + 1))
    exit 1
fi

if [ -z "$src" ]
then
    src="${python} ${PWD}/src"
fi

# Adapt if there's no python2 in the environment
if ! command -v "python2" >/dev/null
then
    python=python3
fi

# Generic TAP functions begin here

# The tap* functions are a mini-framework for controlled execution of
# CLI tests.  They ship reports in TAP format to stdout.  If the
# variable $logfile is set, they will also append stanza headers to
# the specified logfile.
#
# The point of these functions is twfold: to give us TAP logging of
# test outcomes to standard output, and so that any debug logging
# produced by the tool running under taptest/tapdiff control appears
# *after* a stanza header identifying the test.
#
# They are intended to be used in a particular pattern so they will
# produce useful stanzas in the logfile. First, call one of the
# primary functions that generates a new section - taplog, taptest, or
# tapdiff. Then call tapgrep and tapcheck to run as many auxiliary
# tests as you would like.
#
# The test group labels aren't used yet (except for being reported in
# the test log if $logfile is set). In the future they may be used to
# allow running only subspans of an entire test sequence.  Don't
# assume that the existence of generated test files soans the
# boundaries defined by a change in group label.

# If you want to change where command captures and diffs live, modifying
# this variable will do it.
tap_capture="/tmp/tap$$"

# Set this to add a fixed preamble to every TAP message
tap_preamble=""

# Options to pass diff.
tap_diffopts="--label Expected --label Actual -u"

# Start a new log stanza. First argument is the group label, second is the
# legend that later functions should use to report success or failure.
taplog() {
    label="$1"
    legend="$2"
    if [ "${logfile}" ]
    then
	echo "# ${label} ${tap_preamble}${legend}" >>"${logfile}"
    fi
}

# Produce a TAP line from the return status of the last command
# executed.  Does not start a new log stanza. First argument is the
# group label. Second is the command return status to dispatch on, 0
# for success and any other value for failure. Third is the legend
# that later functions should use to report success or failure.
# Fourth, if given, is the name of a capture of the command's stdout
# and stderr.
#
# If the command return is nonzero, the captured output has been
# passed, and the size of the capture is nonzero, the diff is dumped
# as a YAML attachment conforming to TAP.  Beginning the command with "!"
# allows an expected failure status to be processed returning success.
#
# The -d option forces the capture to be dumped even on command success.
# The -e option reports failure if the capture is nonempty.
tapcheck() {
    debug="no"
    need_empty=""
    while getopts :de opt
    do
	case $opt in
	    d) debug=yes;;
	    e) need_empty="-e";;
	    *) echo "not ok ${testcount} - unknown option in tapcheck"; exit 1;;
	esac
    done
    # shellcheck disable=SC2004
    shift $(($OPTIND - 1))
    label="$1"
    status="$2"
    legend="$3"
    outfile="$4"
    case "${status}" in
	0) ;;
	*) echo "not ok ${testcount} - ${tap_preamble}${legend} failed";
	   if [ -n "${outfile}" ]
	   then
	       echo "  --- |"
	       echo "  Return status ${status}"
	       sed <"${outfile}" -e 's/^/  /'
	       echo "  ..."
	   fi
	   exit 1
	   ;;
    esac

    if [ "${need_empty}" = "-e" ] && [ -n "${outfile}" ] && [ -s "${outfile}" ]
    then
	echo "not ok ${testcount} - ${tap_preamble}${legend} had unexpected nonempty output.";
	echo "  --- |"
	echo "  Return status ${status}"
	sed <"${outfile}" -e 's/^/  /'
	echo "  ..."
	exit 1
    fi
    
    echo "ok ${testcount} - ${tap_preamble}${legend} succeeded"
    if [ "${debug}" = "yes" ] && [ -n "${outfile}" ] && [ -s "${outfile}" ]
    then
	echo "  --- |"
	echo "  Return status ${status}"
	sed <"${outfile}" -e 's/^/  /'
	echo "  ..."
    fi

    testcount=$((testcount + 1))
}

# Run a command with stdout and stderr capture.  Starts a new log
# stanza. First argument is the group label. Second is the legend that
# later functions should use to report success or failure.
#
# All later arguments are treated as a shell command to be executed,
# with standard output and standard error captured as
# ${tap_capture}out. The command is deemed to succeed if its return status
# is 0 and to fail otherwise; if it fails, the capture is dumped as a
# YAML attachment conforming to TAP.  Beginning the command with "!"
# allows an expected failure status to be processed returning success.
#
# WARNING: Strings in taptest command lines have to be quoted twice;
# one layer is stripped off by normal shell expansion, the second
# removed by the eval this function uses internally.
#
# The -d option forces that capture to be dumped even on command success.
# The -e option reports failure if the capture is nonempty.
taptest() {
    debug=""
    need_empty=""
    while getopts :de opt
    do
	case $opt in
	    d) debug="-d";;
	    e) need_empty="-e";;
	    *) echo "not ok ${testcount} - unknown option in taptest"; exit 1;;
	esac
    done
    # shellcheck disable=SC2004
    shift $(($OPTIND - 1))
    label="$1"
    legend="$2"
    shift
    shift
    taplog "${label}" "${legend}"
    # shellcheck disable=SC2086,SC2048
    eval $* >${tap_capture}out 2>&1
    # shellcheck disable=SC2086
    tapcheck ${debug} ${need_empty} internal $? "${legend}" "${tap_capture}out"
}

# Run a command, diffing the output against a specified checkfile.
# Starts a new log stanza. First argument is the group label.
# Second argument is the checkfile of expected output; if
# the actual output differs, the difference is dumped as a YAML
# attachment conforming to TAP. Third argument is the legend to be
# used in reporting success or failure. 
#
# All later arguments are treated as a shell command to be executed,
# with standard output and standard error captured as
# ${tap_capture}out. The command is deemed to succeed the diff bwtween the
# capture and the checkfile is empty and to fail otherwise; if it
# fails, the diff is dumped as a YAML attachment conforming to TAP.
#
# WARNING: Strings in tapdiff command lines have to be quoted twice;
# one layer is stripped off by normal shell expansion, the second
# removed by the eval this functions uses internally.
#
# With the -f option, pass the command output through the specified
# filter before diffing.
tapdiff () {
    filter="cat"
    if [ "$1" = "-f" ]
    then
	filter="$2"
	shift
	shift
    fi
    label="$1"
    checkfile="$2"
    legend="$3"
    shift
    shift
    shift
    taplog "${label}" "${legend}"
    # shellcheck disable=SC2086,SC2048
    eval $* >${tap_capture}out 2>&1
    case $? in
	0) ;;
	*) echo "not ok ${testcount} - ${tap_preamble}${legend} command failed";
	   echo "  --- |"
	   sed <"${tap_capture}out" -e 's/^/  /'
	   echo "  ..."
	   exit 1
	   ;;
    esac
    # shellcheck disable=SC2086
    diff ${tap_diffopts} "${checkfile}" ${tap_capture}out | ${filter} >${tap_capture}diff
    if [ ! -s ${tap_capture}diff ]
    then
	echo "ok ${testcount} - ${tap_preamble}${legend} diff succeeded"
    else
	echo "not ok ${testcount} - ${tap_preamble}${legend} diff failed"
	echo "  --- |"
	sed <"${tap_capture}diff" -e 's/^/  /'
	echo "  ..."
	exit 1
    fi
    testcount=$((testcount + 1))
}

# Given a test file and a checkfile, verify that they compare binary equal.
# Relies on cmp(1) to always ship a textual non-zero-length complain if they
# do not. This is a separate function because doing the obvious way would
# clobber ${tap_capture}out just before it's needed for the check.
tapcmp () {
    label="$1"
    testfile="$2"
    checkfile="$3"
    legend="$4"
    taplog "${label}" "${legend}"
    # shellcheck disable=SC2086
    cmp "${testfile}" "${checkfile}" >${tap_capture}diff 2>&1
    tapcheck internal "$?" "${legend}"
}

# Grep for a pattern in the stashed output of the last taptest command.
# First argument is group label, second is the string to grep for, third
# is the legend to be used in reporting success or failure.
tapgrep() {
    if [ "$#" != 3 ]
    then
	echo "Bail out! # tapgrep requires exactly three arguments."
	exit 1;
    fi
    label="$1"
    lookfor="$2"
    legend="$3"
    grep "$lookfor" ${tap_capture}out >/dev/null 2>&1
    tapcheck internal "$?" "${legend}"
}

# Emit a TAP comment.  We don't sihp an OK line here because it
# doesn't count as a passed test.
tapcomment() {
    shift	# Skip the label argument
    echo "# $*"
    if [ -n "${logfile}" ]
    then
	echo "# $*" >>"${logfile}"
    fi
}

# Clean up tempfiles created by the tap functions.
tapclean() {
    if [ -z "${tap_capture}" ]
    then
	echo "not ok ${testcount} - ${tap_preamble}cleanup deletion suppressed."
	exit 1
    else
	rm -f ${tap_capture}*
    fi
    testcount=$((testcount + 1))
}

# Generic TAP functions end here

if [ $testmode = yes ]
then
    SANDBOX=/tmp/srctest
    echo "# Test files and masters will not be removed on exit."
    rm -fr $SANDBOX
else
    # Needs to not be a subdirectory of here, or git gets confused.  Use
    # -u so that src's initialization will be tested.
    SANDBOX=$(mktemp -u /tmp/src_XXXXXXXX)
    trap 'rm -fr $SANDBOX; tapclean' EXIT HUP INT QUIT TERM
fi

if [ ! -d "$SANDBOX" ]
then
    mkdir "$SANDBOX" || { echo "Bail out! # scratch directory creation of $SANDBOX failed"; exit 1; }
fi
if [ "$SANDBOX" = "" ]
then
    testcount=$((testcount + 1))
    echo "Bail out! srctest refuses to destroy the world."
fi

cd "$SANDBOX" || (echo "srctest: sandbox is missing"; exit 1)
rm -fr -- *

COLUMNS=73
export COLUMNS

# Set an editor that does nothing, noy modifimg its argument file.  We
# do this to avoid making the eecution path through the edit-function
# code dependent on what well-known editors are in the user's $PATH.
EDITOR=:
export EDITOR

# Test sequence begins here
# shellcheck disable=SC2120
test_backend() {

TESTOPTS="$passed $backend"
tap_preamble="($python $backend): "

# shellcheck disable=SC2086
mkdir ${python}-${backend}
# shellcheck disable=SC2086
cd ${python}-${backend} >/dev/null || ( echo "Bail out! Sandbox subdirectory creation failed"; exit 1 )

# No repo directory yet. Test those error cases first

# shellcheck disable=SC2086
taptest repoless "reject list with missing repo directory" ! $src -T ${TESTOPTS} list
tapgrep repoless "does not exist" "list with missing repo message check"

# shellcheck disable=SC2086
taptest repoless "reject srcify with  repo directory." ! $src -q -T ${TESTOPTS} srcify

# shellcheck disable=SC2086
taptest repoless "version" $src -T ${TESTOPTS} version
tapgrep repoless "python" "sane version output"

# shellcheck disable=SC2086
taptest repoless "reject ls without repo directory" ! $src -T ${TESTOPTS} ls

# shellcheck disable=SC2086
taptest repoless "reject visualize without repo directory" ! $src -T ${TESTOPTS} visualize

# shellcheck disable=SC2086
taptest repoless "empty command line not blowing up" $src -T ${TESTOPTS}

# shellcheck disable=SC2086
taptest repoless "commit help lookup" $src -T ${TESTOPTS} help commit

# shellcheck disable=SC2086
taptest repoless "error exit on unknown command" ! $src -T ${TESTOPTS} foozle

# shellcheck disable=SC2086
taptest repoless "error exit on help for unknown command" ! $src -T ${TESTOPTS} help foozle

# shellcheck disable=SC2086
taptest repoless "init unexpectedly" $src -T ${TESTOPTS} init

# No-directory tests end here

cat >testfile1 <<EOF
Now is the time
EOF
cp testfile1 testrev1

# shellcheck disable=SC2086
taptest ${expect_empty} workflow "commit with -m option" $src -T ${TESTOPTS} commit -m "'First comment'" testfile1

# shellcheck disable=SC2086
tapdiff workflow testfile1 "content check after first commit" $src -T ${TESTOPTS} cat testfile1

# shellcheck disable=SC2086
tapdiff workflow testfile1 "content check with --" $src -T ${TESTOPTS} cat 1 -- testfile1

# shellcheck disable=SC2086
taptest workflow "revision number command used by Emacs" $src -T ${TESTOPTS} list "-f{1}" @ -- testfile1

mkdir mysub
# shellcheck disable=SC2086
taptest workflow "copy to subdirectory" $src -T ${TESTOPTS} copy testfile1 mysub/landhere
rm -fr mysub

touch foo
# shellcheck disable=SC2086
taptest workflow "unregistered file check" ! $src -T ${TESTOPTS} cat foo
tapgrep workfile "is not registered" "unregistered file check message"
rm foo

# Change the content of testfile1 
cat >testfile1 <<EOF
Now is the time
for all good men
EOF
cp testfile1 testrev2

# We expect this diff
cat >testfile4 <<EOF
--- testfile1 (r1)
+++ testfile1 (workfile)
@@ -1,2 +1,3 @@
 Now is the time
+for all good men
 
EOF

# Filter out header lines containing variable date information
# shellcheck disable=SC2086,SC2162,SC2034
tapdiff workflow testfile4 "content diff after first commit" $src -T ${TESTOPTS} diff testfile1

# shellcheck disable=SC2086
taptest workflow "diff with bad option" ! $src -T ${TESTOPTS} diff -@ testfile1
tapgrep workflow "unexpected" "bogus diff option check"

sleep 1	# Force commit to have different timestamp
echo 'Second comment' >/tmp/tap$$in
# shellcheck disable=SC2086
taptest ${expect_empty} workflow "commit with -" $src -T ${TESTOPTS} commit - testfile1 </tmp/tap$$in
# shellcheck disable=SC2086
tapdiff workflow testfile1 "content check after second commit" $src -T ${TESTOPTS} cat testfile1

cat >testfile2 <<EOF
= testfile1 ============================================================
2     * 1970-01-02T00:01:00Z Second comment
1     - 1970-01-02T00:00:00Z First comment
EOF

# shellcheck disable=SC2086
tapdiff workflow testfile2 "list test" $src -T ${TESTOPTS} list testfile1
# shellcheck disable=SC2086
tapdiff workflow testfile2 "list test with -- argument termination" $src -T ${TESTOPTS} list -- testfile1

cat >testfile2 <<EOF
2|*|1970-01-02T00:01:00Z
1|-|1970-01-02T00:00:00Z
EOF

# shellcheck disable=SC2086
tapdiff workflow testfile2 "list test with custom format" $src -T ${TESTOPTS} list -f '"{1}|{2}|{3}"' testfile1

cat >testfile2 <<EOF
= testfile1 ============================================================
2    | 1970-01-02T00:01:00Z | trunk
Second comment
------------------------------------------------------------------------
1    | 1970-01-02T00:00:00Z | trunk
First comment
------------------------------------------------------------------------
EOF

# shellcheck disable=SC2086
tapdiff workflow testfile2 "2-commit log test" $src -T ${TESTOPTS} log testfile1

# shellcheck disable=SC2086
tapdiff workflow testfile2 "2-commit log test with --" $src -T ${TESTOPTS} log -- testfile1

cat >testfile1 <<EOF
Now is the time
for all good men
to come to the aid of foo
EOF
cp testfile1 testrev3

sleep 1	# Force commit to have different timestamp
cat >testfile4 <<EOF
'Third' comment

Multiline test.

EOF
# shellcheck disable=SC2086
taptest workflow "commit with -f" $src -T ${TESTOPTS} commit -f testfile4 testfile1

cat >testfile4 <<EOF
= testfile1 ============================================================
3    | 1970-01-02T00:02:00Z | trunk
'Third' comment

Multiline test.
------------------------------------------------------------------------
2    | 1970-01-02T00:01:00Z | trunk
Second comment
------------------------------------------------------------------------
1    | 1970-01-02T00:00:00Z | trunk
First comment
------------------------------------------------------------------------
EOF

# shellcheck disable=SC2086
tapdiff workflow testfile4 "3-commit log test" $src -T ${TESTOPTS} log testfile1 

cat >testfile14 <<EOF
= testfile1 ============================================================
3    | 1970-01-02T00:02:00Z | trunk
'Third' comment

Multiline test.

diff r2/testfile1 r3/testfile1
--- testfile1 (r2)
+++ testfile1 (r3)
@@ -1,3 +1,4 @@
 Now is the time
 for all good men
+to come to the aid of foo
 
------------------------------------------------------------------------
2    | 1970-01-02T00:01:00Z | trunk
Second comment

diff r1/testfile1 r2/testfile1
--- testfile1 (r1)
+++ testfile1 (r2)
@@ -1,2 +1,3 @@
 Now is the time
+for all good men
 
------------------------------------------------------------------------
1    | 1970-01-02T00:00:00Z | trunk
First comment

diff /dev/null r1/testfile1
--- /dev/null
+++ testfile1 (r1)
@@ -1 +1,2 @@
+Now is the time
 
------------------------------------------------------------------------
EOF

# shellcheck disable=SC2086
tapdiff workflow testfile14 "log --patch" $src -T ${TESTOPTS} log --patch testfile1

cat >testfile15 <<EOF
= testfile1 ============================================================
3    | 1970-01-02T00:02:00Z | trunk
'Third' comment

Multiline test.

diff r2/testfile1 r3/testfile1
*** testfile1 (r2)
--- testfile1 (r3)
***************
*** 1,3 ****
--- 1,4 ----
  Now is the time
  for all good men
+ to come to the aid of foo
  
------------------------------------------------------------------------
2    | 1970-01-02T00:01:00Z | trunk
Second comment

diff r1/testfile1 r2/testfile1
*** testfile1 (r1)
--- testfile1 (r2)
***************
*** 1,2 ****
--- 1,3 ----
  Now is the time
+ for all good men
  
------------------------------------------------------------------------
1    | 1970-01-02T00:00:00Z | trunk
First comment

diff /dev/null r1/testfile1
*** /dev/null
--- testfile1 (r1)
***************
*** 1 ****
--- 1,2 ----
+ Now is the time
  
------------------------------------------------------------------------
EOF

# shellcheck disable=SC2086
tapdiff workfow testfile15 "log -c" $src -T ${TESTOPTS} log -c testfile1

cat >testfile6 <<EOF
= testfile1 ============================================================
src: testfile1 has no 5 revision
EOF

# shellcheck disable=SC2086
tapdiff workflow testfile6 "oob revision spec check" ! $src -T ${TESTOPTS} list 5 testfile1
tapgrep workflow "src: testfile1 has no 5 revision" "oob revision error check"

for rev in 1 2 3
do
    # shellcheck disable=SC2086
    taptest workfile "revision $rev checkout " $src -q -T ${TESTOPTS} checkout $rev testfile1
    tapdiff workfile testrev${rev} "revision $rev checkout content test" cat testfile1
done

if [ "$backend" = "sccs" ]
then
    tapcomment workflow "($python $backend): skipping tag tests"
else
    # shellcheck disable=SC2086
    taptest workflow "tag set to default tip revision" $src -T ${TESTOPTS} tag create sampletag

    cat >testfile8 <<EOF
= testfile1 ==========================================================
   3	sampletag
EOF
    # shellcheck disable=SC2086
    tapdiff workflow testfile8 "tag list check" $src -T ${TESTOPTS} tag list

    # shellcheck disable=SC2086
    taptest workflow "tag set to revision 1" $src -T ${TESTOPTS} tag create basetag 1

    cat >testfile10 <<EOF
= testfile1 ==========================================================
   1	basetag
   3	sampletag
EOF
    # shellcheck disable=SC2086
    tapdiff workflow testfile10 "second tag list check" $src -T ${TESTOPTS} tag -l

    # shellcheck disable=SC2086
    taptest workflow "tag deletion: sampletag" $src -T ${TESTOPTS} tag -d sampletag

    cat >testfile12 <<EOF
= testfile1 ==========================================================
   1	basetag
EOF
    # shellcheck disable=SC2086
    tapdiff workflow testfile12 "tag list check after deletion" $src -T ${TESTOPTS} tag

    # We used to have to do this or the fast-export regression would fail spuriously.
    # It is not clear what changed.
    #taptest workfile "tag deletion: basetag" $src -T ${TESTOPTS} tag -d basetag
fi

# Fast-export tests
test_export () {
    testname="$1"
    shift
    srcfi=$testname-src.fi
    gitfi=$testname-git.fi
    mkdir foo
    # shellcheck disable=SC2086
    taptest workflow "fast-export: $testname command" $src -T ${TESTOPTS} fast-export "$@"
    tapgrep workfile jrh "fast-export: $testname content check"
    grep -v '^#' "${tap_capture}out" >"$srcfi"	# Side effect - file is used later
    # shellcheck disable=SC2002
    cat "$srcfi" | (cd foo >/dev/null || ("echo missing foo directory" ;exit 1); git init --quiet; git fast-import --quiet)
    (cd foo >/dev/null || ("echo missing foo directory"; exit 1); git fast-export --all) | grep -v '^#' >"$gitfi"
    tapdiff workfile "$srcfi" "fast-export roundtrip: $testname" cat "$gitfi"
    rm -fr foo
}

# Without this copy, the rcs-fast-import test would modify testf1le1 under
# the RCS nackend but not the SCCS one.
# shellcheck disable=SC2086
taptest ${expect_empty} workflow "copying testfile1" $src -T ${TESTOPTS} copy testfile1 exportme

test_export filename exportme

# Test multiple-file fast-export.
echo flower power >testfile14
# shellcheck disable=SC2086
taptest workflow "it's a Mad world" $src -T ${TESTOPTS} commit -m "'Alfred E. Newman'" testfile14
test_export filenames exportme testfile14
historify testfile14
rm -f "$history"

test_export revspec-filename -- exportme
taplog workfile "fast-export revspec/filename distinction"
grep refs/heads/master revspec-filename-src.fi >/dev/null &&
    ! grep refs/heads/exportme/master revspec-filename-src.fi >/dev/null
tapcheck workfile "$?" "${legend}"

rm -f exportme
test_export not-checked-out exportme
tapcheck workfile "$?" "fast-export consults history only"

# shellcheck disable=SC2086
taptest workfklow "checkout after fast-export" $src -T ${TESTOPTS} checkout exportme
    
if [ "$backend" = "sccs" ]
then
    tapcomment workflow "($python $backend): skipping fast-import checks: RCS-only"
elif ! command -v rcs-fast-import >/dev/null 2>&1
then
    tapcomment workflow "($python $backend): skipping fast-import checks: rcs-fast-import missing"
else
    mkdir RCS
    # shellcheck disable=SC2086
    taptest workflow "fast-import don't clobber RCS" ! $src -T ${TESTOPTS} fast-import -p <filename-git.fi
    tapgrep workflow 'existing RCS' "fast-import don't clobber RCS message check"
    rm -fr RCS

    historify exportme
    rm -f "$history"
    # shellcheck disable=SC2086
    taptest workflow "fast-import roundtrip (import)" $src -T ${TESTOPTS} fast-import -p <filename-git.fi
    taptest workflow "history exists after fast-import roundtrip" test -f "$history"
    (echo "#sourcetype src"; cat filename-git.fi) >reheadered
    # shellcheck disable=SC2086
    tapdiff workflow reheadered "fast-import roundtrip (export)" $src -T ${TESTOPTS} fast-export exportme
fi

nuke exportme

# shellcheck disable=SC2086
taptest badmove "rejection of move with bad source" ! $src -T ${TESTOPTS} move foo bar
tapgrep badmove "I see no" "move rejection message check"

# shellcheck disable=SC2086
taptest workflow "move command" $src -T ${TESTOPTS} move testfile1 newname1
historify newname1
# shellcheck disable=SC2086
taptest workflow "move target location check" [ -e newname1 ] && [ -e "$history" ]

newname1_master="${history}"
historify newname2
newname2_master="${history}"
# shellcheck disable=SC2086
taptest workflow "copy command" $src -T ${TESTOPTS} copy newname1 newname2
taptest workflow "copy source and target location check" [ -e newname1 ] && [ -e newname2 ] && [ -e "${newname1_master}" ] && [ -e "${newname2_master}" ]

cat >manifest <<EOF
newname1
newname2
EOF
# shellcheck disable=SC2086
tapdiff workflow manifest "ls" $src -T ${TESTOPTS} ls
nuke manifest

# shellcheck disable=SC2086
taptest workflow "amend" $src -T ${TESTOPTS} amend -m "'Amended comment'" newname1

# shellcheck disable=SC2086
taptest workflow "comment retrieval for amend check" $src -T ${TESTOPTS} list 3 newname1
tapgrep workflow "Amended comment" "amended comment content"

cat >statuslog <<EOF
=  newname1
=  newname2
EOF
# shellcheck disable=SC2086
tapdiff workflow statuslog "unadorned status" $src -T ${TESTOPTS} status -q

# shellcheck disable=SC2086
tapdiff workflow statuslog "status before ignore" $src -T ${TESTOPTS} status newname1 newname2

echo "# comment in ignore file" >.srcignore
echo "newname1" >>.srcignore

cat >statuslog <<EOF
I  newname1
=  newname2
EOF
# shellcheck disable=SC2086
tapdiff workflow statuslog "status after ignore" $src -T ${TESTOPTS} status newname1 newname2

nuke statuslog
nuke .srcignore

cat >diffall.expect <<EOF
--- newname1 (r3)
+++ newname1 (workfile)
@@ -1,4 +1,5 @@
 Now is the time
 for all good men
 to come to the aid of foo
+more stuff
 
--- newname2 (r3)
+++ newname2 (workfile)
@@ -1,4 +1,5 @@
 Now is the time
 for all good men
 to come to the aid of foo
+and even more
 
EOF

echo "more stuff" >>newname1
echo "and even more" >>newname2
# shellcheck disable=SC2086
tapdiff workflow diffall.expect "expected diff after modify" $src -T ${TESTOPTS} diff

# shellcheck disable=SC2086
taptest workflow "checkout after modification" $src -T ${TESTOPTS} checkout newname1 newname2

cat >limit.list <<EOF
= newname2 =============================================================
3     * 1970-01-02T00:02:00Z 'Third' comment
2     - 1970-01-02T00:01:00Z Second comment
EOF
cat >limit.log <<EOF
= newname2 =============================================================
3    | 1970-01-02T00:02:00Z | trunk
'Third' comment

Multiline test.
------------------------------------------------------------------------
2    | 1970-01-02T00:01:00Z | trunk
Second comment
------------------------------------------------------------------------
EOF
for i in list log
do
    for j in -2 '-l 2'
    do
        # shellcheck disable=SC2086
	tapdiff workflow limit.$i "$i $j" $src -T ${TESTOPTS} $i $j newname2
    done
done

# Get a fresh start - enforcing bounday between test groups
fresh_start

cat >statdummy <<EOF
Lorem ipsum
EOF
# shellcheck disable=SC2086
taptest teststatus "commit file for stasis tests" $src -T ${TESTOPTS} commit -m "'Another sample'" statdummy

# shellcheck disable=SC2086
taptest teststatus "status fetch expecting = (unmodified)" $src -T ${TESTOPTS} status statdummy
tapgrep teststatus "^=" "= status check after first commit"

echo "Add a second line" >>statdummy
# shellcheck disable=SC2086
taptest teststatus "status fetch expecting M" $src -T ${TESTOPTS} status statdummy
tapgrep teststatus "^M" "M status check after modification"

rm statdummy
# shellcheck disable=SC2086
taptest teststatus "status fetch expecting !" $src -T ${TESTOPTS} status statdummy
tapgrep teststatus "^!" "! status check after deletion"

# shellcheck disable=SC2086
taptest teststatus "reversion" $src -T ${TESTOPTS} checkout statdummy

# shellcheck disable=SC2086
taptest teststatus "status fetch expecting = (reversiom)" $src -T ${TESTOPTS} status statdummy
tapgrep teststatus "^=" "= status check after restoration"

historify statdummy
rm -f "$history"

# shellcheck disable=SC2086
taptest teststatus "status fetch expecting ?" $src -T ${TESTOPTS} status statdummy
tapgrep teststatus "^?" "? status check after master deletion"

# Get a fresh start - enforcing bounday between test groups
fresh_start

if [ "$backend" = "sccs" ]
then
    tapcomment branchy "($python $backend): skipping branch tests"
else
    # Test branch creation
    echo "Base content" >branchfoo
    # shellcheck disable=SC2086
    taptest branchy "branch test start commit " $src -T ${TESTOPTS} ci -m "'Start a file with branches'" branchfoo

    cat >testfile18 <<EOF
= branchfoo ==========================================================
   0	* trunk
EOF
    # shellcheck disable=SC2086
    tapdiff branchy testfile18 "pre-branch-creation branch listing check" $src -T ${TESTOPTS} branch -l branchfoo

cat >testfile19 <<EOF
= branchfoo ==========================================================
   1	* branch1
   0	- trunk
EOF
    # shellcheck disable=SC2086
    taptest branchy "branch creation" $src -T ${TESTOPTS} branch -c branch1 branchfoo

    # shellcheck disable=SC2086
    tapdiff branchy testfile19 "post-branch-creation branch listing check" $src -T ${TESTOPTS} branch -l branchfoo

    echo "Branch content" >branchfoo
    # shellcheck disable=SC2086
    taptest branchy "branch checkin" $src -T ${TESTOPTS} ci -m "'checkin on the branch'" branchfoo

    cat >testfile20 <<EOF
= branchfoo ============================================================
2    | 1970-01-02T00:01:00Z | branch1
checkin on the branch
------------------------------------------------------------------------
1    | 1970-01-02T00:00:00Z | trunk
Start a file with branches
------------------------------------------------------------------------
EOF
    # shellcheck disable=SC2086
    tapdiff branchy testfile20 "log showing branched content" $src -T ${TESTOPTS} log branchfoo

    echo "New base content" >branchfoo
    # shellcheck disable=SC2086
    taptest branchy "reject checkin on nonexistent branch" ! $src -d -T ${TESTOPTS} ci -b boggle -m "'Alter content on trunk'" branchfoo
    # shellcheck disable=SC2086
    tapgrep branchy "can't switch to nonexistent branch" "nonexistent-branch message check"

    # shellcheck disable=SC2086
    taptest branchy "altering content on trunk" $src -T ${TESTOPTS} ci -b trunk -m "'Alter content on trunk'" branchfoo

    cat >testfile21 <<EOF
= branchfoo ============================================================
3    | 1970-01-02T00:02:00Z | trunk
Alter content on trunk
------------------------------------------------------------------------
1    | 1970-01-02T00:00:00Z | trunk
Start a file with branches
------------------------------------------------------------------------
EOF
    # shellcheck disable=SC2086
    tapdiff branchy testfile21 "check trunk log content after branch" src -T ${TESTOPTS} log branchfoo

    # Introduce a test history with some branchiness, so
    # we can test traversal across branch joins. Also has tags.
    cat >.src/sample,v <<EOF
head	1.4;
branch	1.3.1.4.1;
access;
symbols
	GLARB:1.4
	GORP:1.3.1.4
	wibble:1.3.1.4.0.1
	muggle:1.3.0.1;
locks
	${USER:-root}:1.3.1.4.1.5;
comment	@# @;
expand	@b@;


1.4
date	2014.11.11.03.26.53;	author esr;	state Exp;
branches;
next	1.3;

1.3
date	2014.11.10.23.48.49;	author esr;	state Exp;
branches
	1.3.1.1;
next	1.2;

1.2
date	2014.11.10.23.48.11;	author esr;	state Exp;
branches;
next	1.1;

1.1
date	2014.11.10.23.47.08;	author esr;	state Exp;
branches;
next	;

1.3.1.1
date	2014.11.11.04.23.47;	author esr;	state Exp;
branches;
next	1.3.1.2;

1.3.1.2
date	2014.11.11.06.39.47;	author esr;	state Exp;
branches;
next	1.3.1.3;

1.3.1.3
date	2014.11.11.06.48.00;	author esr;	state Exp;
branches;
next	1.3.1.4;

1.3.1.4
date	2014.11.12.03.36.45;	author esr;	state Exp;
branches
	1.3.1.4.1.1;
next	;

1.3.1.4.1.1
date	2014.11.13.04.04.55;	author esr;	state Exp;
branches;
next	1.3.1.4.1.2;

1.3.1.4.1.2
date	2014.11.13.04.23.04;	author esr;	state Exp;
branches;
next	1.3.1.4.1.3;

1.3.1.4.1.3
date	2014.11.13.04.26.23;	author esr;	state Exp;
branches;
next	1.3.1.4.1.4;

1.3.1.4.1.4
date	2014.11.13.04.30.39;	author esr;	state Exp;
branches;
next	1.3.1.4.1.5;

1.3.1.4.1.5
date	2014.11.13.04.31.02;	author esr;	state Exp;
branches;
next	;


desc
@@


1.4
log
@On a branch?
@
text
@Now is the time
For all good men.
To come to the aid of foo.
This should be branch text.
@


1.3
log
@Third comment.
@
text
@d4 1
@


1.3.1.1
log
@Totally unfubared.
@
text
@a3 2

Again, this should be branch text.
@


1.3.1.2
log
@Still utterly fubar
@
text
@d5 1
a5 1
Yet again, this should be branch text.
@


1.3.1.3
log
@Totally chenille.
@
text
@a5 2

Revision 7.
@


1.3.1.4
log
@Utterly fubar
@
text
@d4 4
a7 2
This should be branch text.
Try a comment with a hyphen.
@


1.3.1.4.1.1
log
@All fixed up.
@
text
@a5 1
Test of commit comments.
@


1.3.1.4.1.2
log
@It' all good.
@
text
@a6 1
Glotch.
@


1.3.1.4.1.3
log
@No good.
@
text
@a7 1
Random modification.
@


1.3.1.4.1.4
log
@That's good.
@
text
@a4 1
jjjj
d7 1
@


1.3.1.4.1.5
log
@I see you
@
text
@d5 1
a5 1
jjjjjjjjkkk
@


1.2
log
@Second comment.
@
text
@d3 1
@


1.1
log
@First comment.
@
text
@d2 1
@
EOF

cat >sampledot <<EOF
digraph {
	1 [shape=box,width=5,label=<<table cellspacing="0" border="0" cellborder="0"><tr><td><font color="blue">1</font></td><td>First comment.</td></tr></table>>];
	1 -> 2;
	2 [shape=box,width=5,label=<<table cellspacing="0" border="0" cellborder="0"><tr><td><font color="blue">2</font></td><td>Second comment.</td></tr></table>>];
	2 -> 3;
	3 [shape=box,width=5,label=<<table cellspacing="0" border="0" cellborder="0"><tr><td><font color="blue">3</font></td><td>Third comment.</td></tr></table>>];
	3 -> 4;
	4 [shape=box,width=5,label=<<table cellspacing="0" border="0" cellborder="0"><tr><td><font color="blue">4</font></td><td>On a branch?</td></tr></table>>];
	"trunk" [shape=oval,width=2];
	"4" -> "trunk" [style=dotted];
	3 -> 5;
	5 [shape=box,width=5,label=<<table cellspacing="0" border="0" cellborder="0"><tr><td><font color="blue">5</font></td><td>Totally unfubared.</td></tr></table>>];
	5 -> 6;
	6 [shape=box,width=5,label=<<table cellspacing="0" border="0" cellborder="0"><tr><td><font color="blue">6</font></td><td>Still utterly fubar</td></tr></table>>];
	6 -> 7;
	7 [shape=box,width=5,label=<<table cellspacing="0" border="0" cellborder="0"><tr><td><font color="blue">7</font></td><td>Totally chenille.</td></tr></table>>];
	7 -> 8;
	8 [shape=box,width=5,label=<<table cellspacing="0" border="0" cellborder="0"><tr><td><font color="blue">8</font></td><td>Utterly fubar</td></tr></table>>];
	"muggle" [shape=oval,width=2];
	"8" -> "muggle" [style=dotted];
	8 -> 9;
	9 [shape=box,width=5,label=<<table cellspacing="0" border="0" cellborder="0"><tr><td><font color="blue">9</font></td><td>All fixed up.</td></tr></table>>];
	9 -> 10;
	10 [shape=box,width=5,label=<<table cellspacing="0" border="0" cellborder="0"><tr><td><font color="blue">10</font></td><td>It' all good.</td></tr></table>>];
	10 -> 11;
	11 [shape=box,width=5,label=<<table cellspacing="0" border="0" cellborder="0"><tr><td><font color="blue">11</font></td><td>No good.</td></tr></table>>];
	11 -> 12;
	12 [shape=box,width=5,label=<<table cellspacing="0" border="0" cellborder="0"><tr><td><font color="blue">12</font></td><td>That's good.</td></tr></table>>];
	12 -> 13;
	13 [shape=box,width=5,label=<<table cellspacing="0" border="0" cellborder="0"><tr><td><font color="blue">13</font></td><td>I see you</td></tr></table>>];
	"wibble" [shape=oval,width=2];
	"13" -> "wibble" [style=dotted];
	{rank=same; "GLARB"; "4"}
	"GLARB" -> "4" [style=dotted];
	{rank=same; "GORP"; "8"}
	"GORP" -> "8" [style=dotted];
}
EOF

    # shellcheck disable=SC2086
    tapdiff branchy sampledot "dot visualization" $src -T ${TESTOPTS} visualize sample

    cat >sample.seqlog <<EOF
= sample ===============================================================
6    | 1970-01-02T00:05:00Z | muggle
Still utterly fubar
------------------------------------------------------------------------
5    | 1970-01-02T00:04:00Z | muggle
Totally unfubared.
------------------------------------------------------------------------
4    | 1970-01-02T00:03:00Z | trunk
On a branch?
------------------------------------------------------------------------
3    | 1970-01-02T00:02:00Z | trunk
Third comment.
------------------------------------------------------------------------
2    | 1970-01-02T00:01:00Z | trunk
Second comment.
------------------------------------------------------------------------
EOF

    # shellcheck disable=SC2086
    tapdiff branchy sample.seqlog "traversal by -" $src -T ${TESTOPTS} log 6-2 sample

    # Revision 4 is missing because it's not on the selected branch wibble
    cat >sample.branchlog <<EOF
= sample ===============================================================
6    | 1970-01-02T00:05:00Z | muggle
Still utterly fubar
------------------------------------------------------------------------
5    | 1970-01-02T00:04:00Z | muggle
Totally unfubared.
------------------------------------------------------------------------
3    | 1970-01-02T00:02:00Z | trunk
Third comment.
------------------------------------------------------------------------
2    | 1970-01-02T00:01:00Z | trunk
Second comment.
------------------------------------------------------------------------
EOF

    # shellcheck disable=SC2086
    tapdiff branchy sample.branchlog "traversal by .." $src -T ${TESTOPTS} log 6..2 sample

    cat >sample.branchlog-p <<EOF
= sample ===============================================================
5    | 1970-01-02T00:04:00Z | muggle
Totally unfubared.

diff r3/sample r5/sample
--- sample (r3)
+++ sample (r5)
@@ -2,3 +2,5 @@
 For all good men.
 To come to the aid of foo.
 
+Again, this should be branch text.
+
------------------------------------------------------------------------
EOF

    # shellcheck disable=SC2086
    tapdiff branchy sample.branchlog-p "branchy log --patch" $src -T ${TESTOPTS} log --patch 5 sample

    cat >sample.taglog <<EOF
= sample ===============================================================
4     - 1970-01-02T00:03:00Z On a branch?
EOF
    # shellcheck disable=SC2086
    tapdiff branchy sample.taglog "named-tag lookup with @" $src -T ${TESTOPTS} list @GLARB sample

    # shellcheck disable=SC2086
    taptest branchy "tag renaming" $src -T ${TESTOPTS} rename tag GLARB GROTTY sample

    # shellcheck disable=SC2086
    taptest branchy "reject invalid tag list with revision" ! $src -T ${TESTOPTS} tag -l 1 sample
    
    cat >changes <<EOF
= sample =============================================================
   8	GORP
   4	GROTTY
EOF

    # shellcheck disable=SC2086
    tapdiff branchy changes "tag list after rename" $src -T ${TESTOPTS} tag -l sample

    # shellcheck disable=SC2086
    taptest branchy "reject duplicate tag setting" ! $src -T ${TESTOPTS} tag -c GORP sample
    tapgrep branchy "already set" "duplicate tag setting message check"

    cat >branchlist <<EOF
= sample =============================================================
   8	* wibble
   3	- muggle
   0	- trunk
EOF
    # shellcheck disable=SC2086
    taptest branchy "branch list" $src -T ${TESTOPTS} branch -l sample
    # shellcheck disable=SC2086
    tapdiff branchy branchlist "branch list content" $src -T ${TESTOPTS} branch -l sample

    # shellcheck disable=SC2086
    taptest branchy "branch name resolution" $src -T ${TESTOPTS} list @trunk sample

    cat >branchlog <<EOF
= sample ===============================================================
4     - 1970-01-02T00:03:00Z On a branch?
EOF
    # shellcheck disable=SC2086
    tapdiff branchy branchlog "branch name resolved content" $src -T ${TESTOPTS} list @trunk sample

    # shellcheck disable=SC2086
    taptest branchy "branch delete of trunk should fail" ! $src -T ${TESTOPTS} branch -d trunk sample
    tapgrep branchy "branch deletion failed" "failed branch deletion message check"

    cat >newlist <<EOF
= sample =============================================================
   8	* wibble
   3	- muggle
   0	- trunk
EOF
    # shellcheck disable=SC2086
    tapdiff branchy newlist "branch list check after invalid delete" $src -T ${TESTOPTS} branch -l sample
    
    # Revision 4 is missing because it's not on the selected branch wibble
    cat >newlog <<EOF
= sample ===============================================================
13   | 1970-01-02T00:12:00Z | wibble
I see you
------------------------------------------------------------------------
12   | 1970-01-02T00:11:00Z | wibble
That's good.
------------------------------------------------------------------------
11   | 1970-01-02T00:10:00Z | wibble
No good.
------------------------------------------------------------------------
10   | 1970-01-02T00:09:00Z | wibble
It' all good.
------------------------------------------------------------------------
9    | 1970-01-02T00:08:00Z | wibble
All fixed up.
------------------------------------------------------------------------
8    | 1970-01-02T00:07:00Z | muggle
Utterly fubar
------------------------------------------------------------------------
7    | 1970-01-02T00:06:00Z | muggle
Totally chenille.
------------------------------------------------------------------------
6    | 1970-01-02T00:05:00Z | muggle
Still utterly fubar
------------------------------------------------------------------------
5    | 1970-01-02T00:04:00Z | muggle
Totally unfubared.
------------------------------------------------------------------------
3    | 1970-01-02T00:02:00Z | trunk
Third comment.
------------------------------------------------------------------------
2    | 1970-01-02T00:01:00Z | trunk
Second comment.
------------------------------------------------------------------------
1    | 1970-01-02T00:00:00Z | trunk
First comment.
------------------------------------------------------------------------
EOF

    # shellcheck disable=SC2086
    tapdiff branchy newlog "content after failed branch deletion" $src -T ${TESTOPTS} log sample

    # shellcheck disable=SC2086
    taptest branchy "branch delete of current branch" ! $src -T ${TESTOPTS} branch -d wibble sample
    tapgrep branchy "can't delete the current branch" "reject deletion of current branch"

    cat >newlist <<EOF
= sample =============================================================
   8	* wibble
   3	- muggle
   0	- trunk
EOF
    # shellcheck disable=SC2086
    tapdiff branchy newlist "branch list check after failed current branch delete" $src -T ${TESTOPTS} branch -l sample

    # shellcheck disable=SC2086
    taptest branchy "select trunk" $src -T ${TESTOPTS} co @trunk sample
    cat >newlist <<EOF
= sample =============================================================
   0	* trunk
   3	- muggle
   8	- wibble
EOF
    # shellcheck disable=SC2086
    tapdiff branchy newlist "branch list check after trunk select" $src -T ${TESTOPTS} branch -l sample

    # shellcheck disable=SC2086
    taptest branchy "branch delete of wibble" $src -T ${TESTOPTS} branch -d wibble sample
    cat >newlist <<EOF
= sample =============================================================
   0	* trunk
   3	- muggle
EOF
    # shellcheck disable=SC2086
    tapdiff branchy newlist "branch list check after wibble delete" $src -T ${TESTOPTS} branch -l sample
fi
  
# Get a fresh start - enforcing bounday between test groups
fresh_start

# shellcheck disable=SC2210
echo "Random content for numeric file" >23
# shellcheck disable=SC2086
taptest numeric "commit of file with numeric name" $src -T ${TESTOPTS} commit -m "'I do not know why you say goodbye'" -- 23

cat >randomcontent <<EOF
Random content for numeric file
EOF
# shellcheck disable=SC2086
tapdiff numeric randomcontent "content check of file with numeric name" $src -T ${TESTOPTS} cat -- 23

# Get a fresh start - enforcing bounday between test groups
fresh_start

# Eric Sunshine writes:
#
# The test itself is working properly. The problem is that stock SCCS
# does not preserve executable permission on files under its control, so
# the test is correctly failing.
#
# The GNU version of SCCS does provide an 'x' flag[1] for compatibility
# with SCO OpenServer which enables executable permission preservation.
# However, I haven't convinced myself that it would make sense to put in
# the work to add executable-preservation support to the SCCS backend
# for these special cases (GNU CSSC and SCO OpenServer).
#
# [1]: https://www.gnu.org/software/cssc/manual/Flags.html#Flag
#
if [ "$backend" = sccs ]
then
    tapcomment exec "($python $backend): skipping exec-bit propagation test"
else
    echo "Should not be executable" >exectest
    # shellcheck disable=SC2086
    taptest exec "first commit of exectest" $src -T ${TESTOPTS} commit -m "'First comment on exectest'" exectest
    chmod a+x exectest
    echo "Should be executable" >exectest
    # shellcheck disable=SC2086
    taptest exec "second commit of exectest" $src -T ${TESTOPTS} commit -m "'Second comment on exectest'" exectest
    # shellcheck disable=SC2010
    taptest exec "propagation of exec bit" test -x exectest
fi

# Get a fresh start - enforcing bounday between test groups
fresh_start

# Test for Tom Willemse's multi-commit bug.
echo "test 1" > file1
echo "test 2" > file2
# shellcheck disable=SC2086
taptest multifile "multi-file registration" $src -T ${TESTOPTS} commit -m "'Initial commit'" file1 file2
echo "test 1 line 2" >> file1
echo "test 2 line 2" >> file2
# shellcheck disable=SC2086
taptest multifile "multi-file commit" $src -T ${TESTOPTS} commit -m "'Second commit'" file1 file2

# Test the edit logic
cat >modify <<'EOF'
echo "Different first line" >modified$$
cat $1 >>modified$$
mv modified$$ $1
EOF
chmod a+x modify
echo "test 1 line 3" >> file1
# shellcheck disable=SC2086
EDITOR="./modify" taptest edit "edit logic" $src -T ${TESTOPTS} commit file1

# Get a fresh start - enforcing bounday between test groups
fresh_start

cat >binary <<EOF
This file contains an 0xD3 (Meta-S) after the colon:
EOF
cat >binary.chk <<EOF
This file contains an 0xD3 (Meta-S) after the colon:
Additional content line.
EOF
# shellcheck disable=SC2086
taptest binary "binary file checkin" $src -T ${TESTOPTS} commit -m "'Test file containing binary byte.'" binary
echo "Additional content line." >>binary
# shellcheck disable=SC2086
taptest binary "binary file modification" $src -T ${TESTOPTS} commit -m "'Binary file after modification.'" binary
tapcmp binary binary binary.chk "binary content in checkouts and commits"

if [ "$backend" = "sccs" ]
then
    tapcomment binary "($python $backend): skipping binary cat test"
else
    # shellcheck disable=SC2086
    taptest binary "binary cat" $src -T ${TESTOPTS} cat binary
    tapcmp binary binary ${tap_capture}out "binary content in cat"
fi

cat >newline <<EOF
This file contains a DOS newline 0x0D after the colon:
EOF
cat >newline.chk <<EOF
This file contains a DOS newline 0x0D after the colon:
Additional content line.
EOF
# shellcheck disable=SC2086
taptest newline "initial commit of newline file" $src -T ${TESTOPTS} commit -m "'Test file containing DOS newline.'" newline
echo "Additional content line." >>newline
# shellcheck disable=SC2086
taptest newline "modify newline file" $src -T ${TESTOPTS} commit -m "'DOS newline file after modification.'" newline
tapcmp newline newline newline.chk "newline preservation in checkouts and commits"

if [ "$backend" = "sccs" ]
then
    tapcomment newline "($python $backend): skipping newline cat test"
else
    # shellcheck disable=SC2086
    taptest newline "newline file checkin" $src -T ${TESTOPTS} cat newline
    tapcmp newline ${tap_capture}out newline "newline content in cat"
fi

# Get a fresh start - enforcing bounday between test groups
fresh_start

cat >padcomment <<'EOF'
printf "\n\n\n\n" >modified$$
cat $1 >>modified$$
mv modified$$ $1
EOF
chmod a+x padcomment
echo gommelgor >padfile
# shellcheck disable=SC2086
taptest padfile "padfile commit" $src -T ${TESTOPTS} commit -m pingle padfile
for i in commit amend
do
    echo "more stuff" >>padfile
    # shellcheck disable=SC2086
    EDITOR="./padcomment" taptest padfile "edit padfile" $src -T ${TESTOPTS} $i padfile
    tapgrep padfile cancelled "whitespace-only $i cancel"
    # shellcheck disable=SC2086
    taptest padfile "padfile $i" $src -T ${TESTOPTS} checkout padfile
done

# Get a fresh start - enforcing bounday between test groups
fresh_start

cat >clonemsg <<'EOF'
echo "I think I'm a clone now" >modified$$
cat $1 >>modified$$
mv modified$$ $1
cp $1 clonedmsg
EOF
chmod a+x clonemsg
echo gommelgor >diffledore
# shellcheck disable=SC2086
taptest amend "initial diffledore commit" $src -T ${TESTOPTS} commit -m pingle diffledore
for i in commit amend
do
    test $i = commit && echo "more stuff" >>diffledore
    # shellcheck disable=SC2086
    EDITOR="./clonemsg" taptest ignore "diffledore modification" $src -T ${TESTOPTS} $i diffledore
    grep -F -e 'Changes to be committed' clonedmsg >/dev/null &&
    grep -F -e '@@ ' clonedmsg >/dev/null &&
    grep -F -e '+++ ' clonedmsg >/dev/null &&
    grep -F -e '--- ' clonedmsg >/dev/null
    tapcheck amend "$?" "$i diff in boilerplate"
done

# Get a fresh start - enforcing bounday between test groups
fresh_start

cat >dontinvoke <<'EOF'
echo "panic!" >>nochanges
EOF
chmod a+x dontinvoke
echo "bingleby" >canttouchthis
# shellcheck disable=SC2086
taptest nochanges "canttouchthis commit" $src -T ${TESTOPTS} commit -m pingle canttouchthis
# shellcheck disable=SC2086
EDITOR="./dontinvoke" taptest ignore "canttouchtos second commit" $src -T ${TESTOPTS} commit canttouchthis
grep 'no changes to commit' ${tap_capture}out >/dev/null &&
    ! grep panic ${tap_capture}out >/dev/null
tapcheck nochanges "$?" "nothing to commit"

cat >ignore.ws <<EOF
Mary had a little lamb,
whose fleece was white as snow.
Everywhere that Mary went,
the lamb was sure to go.
EOF
# shellcheck disable=SC2086
taptest ignore "initial commit of ignore.ws" $src -T ${TESTOPTS} commit -m initial ignore.ws
cat >ignore.ws <<EOF
Gary had a little lamb,
whose    fleece was    white as snow.
Everywhere that Gary went,
    the lamb was sure to go.
EOF
sleep 1	# Force commit to have different timestamp
# shellcheck disable=SC2086
taptest ignore "ignore.ws checkin" $src -T ${TESTOPTS} commit -m "'s/Mary/Gary/ & whitespace'" ignore.ws
cat >ignore.expect-b <<EOF
--- ignore.ws (r1)
+++ ignore.ws (r2)
@@ -1,4 +1,4 @@
-Mary had a little lamb,
+Gary had a little lamb,
 whose fleece was white as snow.
-Everywhere that Mary went,
-the lamb was sure to go.
+Everywhere that Gary went,
+    the lamb was sure to go.

EOF
cat >ignore.expect-w <<EOF
--- ignore.ws (r1)
+++ ignore.ws (r2)
@@ -1,4 +1,4 @@
-Mary had a little lamb,
+Gary had a little lamb,
 whose fleece was white as snow.
-Everywhere that Mary went,
+Everywhere that Gary went,
 the lamb was sure to go.

EOF
for i in -b -w
do
    # shellcheck disable=SC2086
    tapdiff workflow ignore.expect$i "diff $i" $src -T ${TESTOPTS} diff -u $i 1-2 ignore.ws
done

# Get a fresh start - enforcing bounday between test groups
fresh_start

if [ "$backend" = "sccs" ]
then
    tapcomment tiebreak "($python $backend): skipping commit date tie-breaking tests"
else
    # Introduce a test history with all commits having same date so we can
    # test native revision ID tie-breaking.
    cat >.src/tiebreak,v <<EOF
head	1.11;
access;
symbols
	refs/heads/master:1.11;
locks
	sunshine:1.11; strict;
comment	@# @;


1.11
date	2017.11.21.06.45.15;	author sunshine;	state Exp;
branches;
next	1.10;

1.10
date	2017.11.21.06.45.15;	author sunshine;	state Exp;
branches;
next	1.9;

1.9
date	2017.11.21.06.45.15;	author sunshine;	state Exp;
branches;
next	1.8;

1.8
date	2017.11.21.06.45.15;	author sunshine;	state Exp;
branches;
next	1.7;

1.7
date	2017.11.21.06.45.15;	author sunshine;	state Exp;
branches;
next	1.6;

1.6
date	2017.11.21.06.45.15;	author sunshine;	state Exp;
branches;
next	1.5;

1.5
date	2017.11.21.06.45.15;	author sunshine;	state Exp;
branches;
next	1.4;

1.4
date	2017.11.21.06.45.15;	author sunshine;	state Exp;
branches;
next	1.3;

1.3
date	2017.11.21.06.45.15;	author sunshine;	state Exp;
branches;
next	1.2;

1.2
date	2017.11.21.06.45.15;	author sunshine;	state Exp;
branches;
next	1.1;

1.1
date	2017.11.21.06.45.15;	author sunshine;	state Exp;
branches;
next	;


desc
@@


1.11
log
@Author: Eric Sunshine <sunshine@@sunshineco.com>
Author-Date: Mon 20 Nov 2017 21:37:42 -0500
Committer: Roy G. Biv <spectrum@@color.com>
Committer-Date: Tue 21 Nov 2017 01:45:15 +0000
Mark: :22
Parents: :20

eleventh
@
text
@eleventh
@


1.10
log
@Author: Eric Sunshine <sunshine@@sunshineco.com>
Author-Date: Mon 20 Nov 2017 21:30:40 -0500
Committer: Roy G. Biv <spectrum@@color.com>
Committer-Date: Tue 21 Nov 2017 01:45:15 +0000
Mark: :20
Parents: :18

tenth
@
text
@d1 1
a1 1
tenth
@


1.9
log
@Author: Eric Sunshine <sunshine@@sunshineco.com>
Author-Date: Mon 20 Nov 2017 21:27:29 -0500
Committer: Roy G. Biv <spectrum@@color.com>
Committer-Date: Tue 21 Nov 2017 01:45:15 +0000
Mark: :18
Parents: :16

ninth
@
text
@d1 1
a1 1
ninth
@


1.8
log
@Author: Eric Sunshine <sunshine@@sunshineco.com>
Author-Date: Mon 20 Nov 2017 21:25:22 -0500
Committer: Roy G. Biv <spectrum@@color.com>
Committer-Date: Tue 21 Nov 2017 01:45:15 +0000
Mark: :16
Parents: :14

eighth
@
text
@d1 1
a1 1
eighth
@


1.7
log
@Author: Eric Sunshine <sunshine@@sunshineco.com>
Author-Date: Mon 20 Nov 2017 23:02:46 -0500
Committer: Roy G. Biv <spectrum@@color.com>
Committer-Date: Tue 21 Nov 2017 01:45:15 +0000
Mark: :14
Parents: :12

seventh
@
text
@d1 1
a1 1
seventh
@


1.6
log
@Author: Eric Sunshine <sunshine@@sunshineco.com>
Author-Date: Mon 20 Nov 2017 22:56:46 -0500
Committer: Roy G. Biv <spectrum@@color.com>
Committer-Date: Tue 21 Nov 2017 01:45:15 +0000
Mark: :12
Parents: :10

sixth
@
text
@d1 1
a1 1
sixth
@


1.5
log
@Author: Eric Sunshine <sunshine@@sunshineco.com>
Author-Date: Fri 03 Nov 2017 13:30:17 -0500
Committer: Roy G. Biv <spectrum@@color.com>
Committer-Date: Tue 21 Nov 2017 01:45:15 +0000
Mark: :10
Parents: :8

fifth
@
text
@d1 1
a1 1
fifth
@


1.4
log
@Author: Eric Sunshine <sunshine@@sunshineco.com>
Author-Date: Fri 03 Nov 2017 13:28:35 -0500
Committer: Roy G. Biv <spectrum@@color.com>
Committer-Date: Tue 21 Nov 2017 01:45:15 +0000
Mark: :8
Parents: :6

fourth
@
text
@d1 1
a1 1
fourth
@


1.3
log
@Author: Eric Sunshine <sunshine@@sunshineco.com>
Author-Date: Fri 03 Nov 2017 13:26:41 -0500
Committer: Roy G. Biv <spectrum@@color.com>
Committer-Date: Tue 21 Nov 2017 01:45:15 +0000
Mark: :6
Parents: :4

third
@
text
@d1 1
a1 1
third
@


1.2
log
@Author: Eric Sunshine <sunshine@@sunshineco.com>
Author-Date: Fri 03 Nov 2017 13:25:01 -0500
Committer: Roy G. Biv <spectrum@@color.com>
Committer-Date: Tue 21 Nov 2017 01:45:15 +0000
Mark: :4
Parents: :2

second
@
text
@d1 1
a1 1
second
@


1.1
log
@Author: Eric Sunshine <sunshine@@sunshineco.com>
Author-Date: Fri 03 Nov 2017 13:12:48 -0500
Committer: Roy G. Biv <spectrum@@color.com>
Committer-Date: Tue 21 Nov 2017 01:45:15 +0000
Mark: :2

first
@
text
@d1 1
a1 1
first
@
EOF

    cat >tiebreak.expect <<EOF
= tiebreak =============================================================
11    * 1970-01-02T00:10:00Z eleventh
10    - 1970-01-02T00:09:00Z tenth
9     - 1970-01-02T00:08:00Z ninth
8     - 1970-01-02T00:07:00Z eighth
7     - 1970-01-02T00:06:00Z seventh
6     - 1970-01-02T00:05:00Z sixth
5     - 1970-01-02T00:04:00Z fifth
4     - 1970-01-02T00:03:00Z fourth
3     - 1970-01-02T00:02:00Z third
2     - 1970-01-02T00:01:00Z second
1     - 1970-01-02T00:00:00Z first
EOF

    # shellcheck disable=SC2086
    tapdiff tiebreak tiebreak.expect "same date tie-breaking" $src -T ${TESTOPTS} list tiebreak

    cat >noheaders.expect <<EOF
= tiebreak =============================================================
11   | 1970-01-02T00:10:00Z | trunk
eleventh
------------------------------------------------------------------------
10   | 1970-01-02T00:09:00Z | trunk
tenth
------------------------------------------------------------------------
EOF

    # shellcheck disable=SC2086
    tapdiff tiebreak noheaders.expect "log suppresses headers" $src -T ${TESTOPTS} log -2 tiebreak

    cat >logheaders.expect <<EOF
= tiebreak =============================================================
11   | 1970-01-02T00:10:00Z | trunk
Author: Eric Sunshine <sunshine@sunshineco.com>
Author-Date: 2017-11-21T02:37:42Z
Author-Date-Offset: -18000
Committer: Roy G. Biv <spectrum@color.com>
Committer-Date: 2017-11-21T01:45:15Z
Committer-Date-Offset: 0
Mark: :22
Parents: :20

eleventh
------------------------------------------------------------------------
10   | 1970-01-02T00:09:00Z | trunk
Author: Eric Sunshine <sunshine@sunshineco.com>
Author-Date: 2017-11-21T02:30:40Z
Author-Date-Offset: -18000
Committer: Roy G. Biv <spectrum@color.com>
Committer-Date: 2017-11-21T01:45:15Z
Committer-Date-Offset: 0
Mark: :20
Parents: :18

tenth
------------------------------------------------------------------------
EOF

    # shellcheck disable=SC2086
    tapdiff tiebreak logheaders.expect "log -v -v shows all headers" $src -T ${TESTOPTS} log -v -v -2 tiebreak

    cat >summaryheaders.expect <<EOF
= tiebreak =============================================================
11   | 1970-01-02T00:10:00Z | trunk
Author: Eric Sunshine <sunshine@sunshineco.com> 2017-11-21T02:37:42Z
Committer: Roy G. Biv <spectrum@color.com> 2017-11-21T01:45:15Z

eleventh
------------------------------------------------------------------------
10   | 1970-01-02T00:09:00Z | trunk
Author: Eric Sunshine <sunshine@sunshineco.com> 2017-11-21T02:30:40Z
Committer: Roy G. Biv <spectrum@color.com> 2017-11-21T01:45:15Z

tenth
------------------------------------------------------------------------
EOF

    # shellcheck disable=SC2086
    tapdiff tiebreak summaryheaders.expect "log -v shows summarized headers" $src -T ${TESTOPTS} log -v -2 tiebreak

    cat >authordate1.expect <<EOF
= tiebreak =============================================================
11    * 2017-11-21T02:37:42Z eleventh
10    - 2017-11-21T02:30:40Z tenth
EOF
    # shellcheck disable=SC2086
    tapdiff tiebreak authordate1.expect "author date from RFC 822 header (list)" $src ${TESTOPTS} list -2 tiebreak

    cat >authordate2.expect <<EOF
= tiebreak =============================================================
11   | 2017-11-21T02:37:42Z | trunk
eleventh
------------------------------------------------------------------------
10   | 2017-11-21T02:30:40Z | trunk
tenth
------------------------------------------------------------------------
EOF
    # shellcheck disable=SC2086
    tapdiff tiebreak authordate2.expect "author date from RFC 822 header (log)" $src ${TESTOPTS} log -2 tiebreak

    # git config isn't available in CI/CD
    if [ "${USER:-cicd}" = cicd ]
    then
	tapcomment branchy "($python $backend): skipping tiebreak fast-export test"
    else
	# shellcheck disable=SC2086
	taptest tiebreak "fast export of tiebreak" $src ${TESTOPTS} fast-export 1 tiebreak
	tapgrep tiebreak "author Eric Sunshine <sunshine@sunshineco.com> 1509732768 -0500" "fast-export: consult RFC 822 headers (author)"
	tapgrep tiebreak "committer Roy G. Biv <spectrum@color.com> 1511228715 +0000" "fast-export: consult RFC 822 headers (committer)"
    fi
fi

# Now for the srcify test
rm -fr .src RCS SCCS

cat >srcifyfile <<EOF
Today is not that day.
EOF
if [ "$backend" = "rcs" ]
then
    mkdir RCS && rcs -q -U -kb -i srcifyfile </dev/null
else
    # shellcheck disable=SC2094
    mkdir SCCS && sccs admin -fb -i srcifyfile -y"Sample commit" <srcifyfile 2>/dev/null
fi
historify srcifyfile
# shellcheck disable=SC2086
taptest srcify "srcify command" $src -T ${TESTOPTS} srcify
test -f "$history"
tapcheck srcify "$?" "srcify created master"
test -f srcifyfile
tapcheck srcify "$?" "srcify left the workfile in place"

# Check a parsing case brought up by GitLab issue #21: Filenames including '-' are interpreted as revision ranges
touch a-b
# shellcheck disable=SC2086
taptest dashparse "checkin of file named with embedded dash" $src -T ${TESTOPTS} ci -m "Initial" -- a-b
historify a-b
test -f "$history"
tapcheck dashparse "$?" "commit of filename resembling a revision renge"

# Test deep operations
mkdir inner
echo one >inner/sample
# shellcheck disable=SC2086
taptest deep "commit of deep file" $src -T ${TESTOPTS} commit -m "'Early trunk content'" inner/sample
# shellcheck disable=SC2086
taptest deep "cat of deep file" $src -T ${TESTOPTS} cat inner/sample
tapcmp deep inner/sample "${tap_capture}out" "binary cat returned correct data"

# Test multidirectory operation
if [ "$backend" = "rcs" ]
then
    mkdir RCS-save
    # shellcheck disable=SC2086
    taptest multidir "correctly bailing out on existing RCS-save" ! $src -T ${TESTOPTS} log
    rmdir RCS-save

    mkdir RCS
    echo "RCS mark" >RCS/rcsmark

    echo "tweedledum" >tweedledum
    echo "tweedledee" >tweedledee

    # shellcheck disable=SC2086
    taptest multidir "multidirectory checkin to .src" $src -q -T ${TESTOPTS} ci -m "helter" tweedledum

    test -f .src/tweedledum,v
    tapcheck multidir "$?" "master location check after multidirectory checkin"

    ! test -f .src/tweedledum
    tapcheck multidir "$?" "content location check after multidirectory checkin"

    test -f RCS/rcsmark
    tapcheck multidir "$?" "RCS restoration check after multidirectory shuffle"

    ! test -f RCS-save
    tapcheck multidir "$?" "no exiguous save directory after shuffle"

    # shellcheck disable=SC2086
    taptest multidir "multidir checkin with -S RCS" $src -S RCS -T ${TESTOPTS} ci -m "skelter" tweedledee

    # shellcheck disable=SC2086
    taptest multidir "multidir tag creation" $src -T ${TESTOPTS} tag -c fimbulwinter tweedledum

    # shellcheck disable=SC2086
    taptest multidir "multidir tag rename" $src -T ${TESTOPTS} rename tag fimbulwinter niflheim -- tweedledum

    cat >tweedledee.expected <<EOF
= tweedledee ===========================================================
1     * 1970-01-02T00:00:00Z skelter
EOF
    # shellcheck disable=SC2086
    tapdiff multidir tweedledee.expected "list check after multidirectory -S checkin" $src -q -S RCS -T ${TESTOPTS} list

    cat >tweedledee.expected <<EOF
tweedledee
EOF
    # shellcheck disable=SC2086
    tapdiff multidir tweedledee.expected "cat check after multidirectory -S checkin" $src -q -S RCS -T ${TESTOPTS} cat tweedledee

    test -f RCS/tweedledee,v
    tapcheck multidir "$?" "master-location check after multidirectory -S checkin"

    rm -fr RCS
fi

# Back up to sandbox directory
cd ..

} # end of testbackend

if [ "${python}" != "" ] && [ "${backend}" != "" ]
then
    test_backend
elif [ "${python}" != "" ]
then
    backend="rcs"
    test_backend
    backend="sccs"
    test_backend
elif [ "${backend}" != "" ]
then
    python=python2
    test_backend
    python=python3
    test_backend
else
    python=python2
    backend="rcs"
    test_backend
    python=python3
    backend="rcs"
    test_backend
    python=python2
    backend="sccs"
    test_backend
    python=python3
    backend="sccs"
    test_backend
fi

echo "1..$((testcount-1))"

rm -fr "$SANDBOX"

# end
