#!/bin/bash
#
# Copyright (C) 2008 Paul Armstrong. All rights reserved.
#
#  Redistribution and use in source and binary forms, with or without
#  modification, are permitted provided that the following conditions
#  are met:
#  1. Redistributions of source code must retain the above copyright
#     notice, this list of conditions and the following disclaimer.
#  2. Redistributions in binary form must reproduce the above copyright
#     notice, this list of conditions and the following disclaimer in the
#     documentation and/or other materials provided with the distribution.
#
#  THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
#  ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
#  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
#  ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
#  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
#  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
#  OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
#  HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
#  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
#  OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
#  SUCH DAMAGE.

# Unit test for lockf(1)

ERROR=""
LOCK_FILE="`pwd`/test_lock"

err() {
	echo "$@" >&2
	ERROR="TRUE"
}

if [ ! -x lockf ]; then
	echo "lockf doesn't exist or is not executable. Compile it?" >&2
	exit 1
fi

lockf -t -f does_not_exist
if [ "$?" -ne 0 ]; then
	err "lockf returned failure for a non-existant lock"
fi

# /devices is read-only for everyone
FAILED_WRITE="$(lockf -l -f /devices/test_lock 2>/dev/null)"
if [ -n "${FAILED_WRITE}" ]; then
	err "lockf exited successfully on a failed lock creation"
	kill "${FAILED_WRITE}"
fi

# all tests after here require this to succeed so it exists immediately on
# failure
LOCK_PID="$(lockf -l -f ${LOCK_FILE})"
if [ -z "${LOCK_PID}" ]; then
	err "lockf didn't return a PID on lock creation"
	exit 1
fi

lockf -l -f "${LOCK_FILE}"
if [ "$?" -eq 0 ]; then
	err "lockf returned success for a locked file"
fi

lockf -t -f "${LOCK_FILE}"
if [ "$?" -eq 0 ]; then
	err "lockf returned success for testing a locked file"
fi

lockf -u "${LOCK_PID}"
if [ "$?" -ne 0 ]; then
	err "lockf failed to unlock ${LOCK_PID}"
fi
if [ -n "$(ps -eo pid | grep ${LOCK_PID})" ]; then
	err "Failed to kill child ${LOCK_PID}"
fi

LOCK_PID=""

# Create a lock so we can test waiting on a lock creation
LOCK_PID="$(lockf -l -f ${LOCK_FILE})"
if [ -z "${LOCK_PID}" ]; then
	err "lockf didn't return a PID on lock creation"
else
	(sleep 2; kill ${LOCK_PID})&

	LOCK_PID2="$(alarm 4 lockf -w -f ${LOCK_FILE})"
	if [ -z "${LOCK_PID2}" ]; then
		err "Failed to create a lock after a wait"
	fi

	lockf -u "${LOCK_PID2}"
	if [ -n "$(ps -eo pid | grep ${LOCK_PID2})" ]; then
		err "Failed to kill child ${LOCK_PID2}"
	fi
fi

# Create a lock so we can test waiting on a lock creation
LOCK_PID3="$(lockf -l -f ${LOCK_FILE})"
if [ -z "${LOCK_PID3}" ]; then
	err "lockf didn't return a PID on lock creation"
else
	LOCK_PID4="$(alarm 2 lockf -w -f ${LOCK_FILE})"
	if [ "$?" -eq 0 ]; then
		err "alarm should have returned a failure"
	fi
	if [ -n "${LOCK_PID4}" ]; then
		err "Says it created a lock when it should have been waiting"
	fi

	lockf -u "${LOCK_PID3}"
	if [ -n "$(ps -eo pid | grep ${LOCK_PID3})" ]; then
		err "Failed to kill child ${LOCK_PID3}"
	fi
fi

if [ -f "${LOCK_FILE}" ]; then
	rm -f "${LOCK_FILE}"
fi
if [ "${ERROR}" = "TRUE" ]; then
	exit 1
else
	exit 0
fi
