Update to 2.1:

- Use shtk for the common utilities and configuration file parsing
  functionality.  The local copies of the "config" and "utils" modules
  are gone.
This commit is contained in:
jmmv 2012-08-15 21:20:14 +00:00
parent f04a5a4ee9
commit 9430168fbe
13 changed files with 67 additions and 1287 deletions

View file

@ -1,6 +1,6 @@
# $NetBSD: Makefile,v 1.18 2012/07/25 12:18:22 jmmv Exp $
# $NetBSD: Makefile,v 1.19 2012/08/15 21:20:14 jmmv Exp $
DISTNAME= sysbuild-2.0
DISTNAME= sysbuild-2.1
CATEGORIES= sysutils
MASTER_SITES= # empty
DISTFILES= # empty
@ -30,20 +30,19 @@ PKG_SUGGESTED_OPTIONS= tests
.include "../../mk/bsd.options.mk"
.if $(PKG_OPTIONS:Mtests)
TEST_PROGS= config_test cvs_test sysbuild_test sysbuild4cron_test utils_test
TEST_PROGS= sysbuild_test sysbuild4cron_test
PLIST_SUBST+= TESTS=
. include "../../devel/atf-libs/buildlink3.mk"
BUILD_SUBST+= -e 's,@ATF_SH@,${BUILDLINK_PREFIX.atf-libs}/bin/atf-sh,g'
do-build: build-tests
build-tests:
cp ${FILESDIR}/Kyuafile ${WRKSRC}
.for file in ${TEST_PROGS}
${ECHO} '#! ${BUILDLINK_PREFIX.atf-libs}/bin/atf-sh' \
>${WRKSRC}/${file}
${CAT} ${FILESDIR}/*.subr ${FILESDIR}/${file}.sh \
| ${SED} ${BUILD_SUBST} >>${WRKSRC}/${file}
${CHMOD} +x ${WRKSRC}/${file}
sed ${BUILD_SUBST} <${FILESDIR}/${file}.sh >${WRKSRC}/${file}
chmod +x ${WRKSRC}/${file}
.endfor
INSTALLATION_DIRS+= tests/sysbuild
@ -63,12 +62,9 @@ PLIST_SUBST+= TESTS=@comment
do-build:
.for file in sysbuild sysbuild4cron
${ECHO} '#! ${SH}' >${WRKSRC}/${file}
${ECHO} 'set -e' >>${WRKSRC}/${file}
${CAT} ${FILESDIR}/*.subr ${FILESDIR}/${file}.sh \
| ${SED} ${BUILD_SUBST} >>${WRKSRC}/${file}
${ECHO} '${file}_main "$${@}"' >>${WRKSRC}/${file}
${CHMOD} +x ${WRKSRC}/${file}
sed ${BUILD_SUBST} ${FILESDIR}/${file}.sh \
| ${BUILDLINK_PREFIX.shtk}/bin/shtk build \
-s ${SH} -m ${file}_main -o ${WRKSRC}/${file} -
.endfor
.for file in sysbuild.1 sysbuild4cron.1 default.conf
sed ${BUILD_SUBST} <${FILESDIR}/${file} >${WRKSRC}/${file}
@ -87,4 +83,5 @@ do-install:
${INSTALL_DATA} ${WRKSRC}/${file} ${DESTDIR}${EGDIR}
.endfor
.include "../../devel/shtk/buildlink3.mk"
.include "../../mk/bsd.pkg.mk"

View file

@ -1,12 +1,9 @@
@comment $NetBSD: PLIST,v 1.4 2012/07/25 12:18:22 jmmv Exp $
@comment $NetBSD: PLIST,v 1.5 2012/08/15 21:20:14 jmmv Exp $
bin/sysbuild
bin/sysbuild4cron
man/man1/sysbuild.1
man/man1/sysbuild4cron.1
share/examples/sysbuild/default.conf
${TESTS}tests/sysbuild/Kyuafile
${TESTS}tests/sysbuild/config_test
${TESTS}tests/sysbuild/cvs_test
${TESTS}tests/sysbuild/sysbuild_test
${TESTS}tests/sysbuild/sysbuild4cron_test
${TESTS}tests/sysbuild/utils_test

View file

@ -2,8 +2,5 @@ syntax("kyuafile", 1)
test_suite("sysbuild")
atf_test_program{name="config_test"}
atf_test_program{name="cvs_test"}
atf_test_program{name="sysbuild_test"}
atf_test_program{name="sysbuild4cron_test"}
atf_test_program{name="utils_test"}

View file

@ -1,287 +0,0 @@
# Copyright 2012 Google Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * 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.
# * Neither the name of Google Inc. nor the names of its contributors
# may be used to endorse or promote products derived from this software
# without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 THE COPYRIGHT
# OWNER 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.
# \file config.subr
# Configuration file processing and queries.
# List of valid configuration variables.
#
# This is initialized by config_init and should remain unchanged thorough the
# execution of the program.
CONFIG_VARS=
# List of overrides to apply by config_apply_overrides.
#
# The overrides are recorded and applied separately because they need to happen
# after a configuration file has been loaded. The contents of this list are
# words of the form: set:<variable> or unset:<variable>. For those variables
# listed in set:, there is a corresponding config_override_var_<variable>
# variable containing the new value.
CONFIG_OVERRIDES=
# Initializes the configuration module.
#
# \param ... List of configuration variables to recognize in configuration files
# and user overrides.
config_init() {
CONFIG_VARS="${@}"
}
# Checks if a configuration variable is known.
#
# \param var Name of the variable to check.
#
# \return True if the variable was registered by config_init, false otherwise.
config_is_valid() {
local var="${1}"; shift
local known_var
for known_var in ${CONFIG_VARS}; do
if [ "${known_var}" = "${var}" ]; then
return 0
fi
done
return 1
}
# Checks if a configuration variable is defined.
#
# \param var The name of the variable to check.
#
# \return True if the variable is defined (even if empty), false otherwise.
config_has() {
local var="${1}"; shift
local is_unset
eval is_unset="\${config_var_${var}-yes_this_is_unset}"
if [ "${is_unset}" = yes_this_is_unset ]; then
return 1
else
return 0
fi
}
# Gets the value of a defined configuration variable.
#
# \post The value of the variable is printed to stdout, if any.
#
# \post If the variable is not defined, this terminates execution with an error.
#
# \param var Name of the configuration variable to query.
config_get() {
local var="${1}"; shift
if config_has "${var}"; then
eval echo "\${config_var_${var}}"
else
utils_error "Required configuration variable ${var} not set"
fi
}
# Gets the value of configuration variable interpreting it as a boolean.
#
# \param var The variable to query.
#
# \return True if the variable is set to a truth value, false if its value is
# false or if it is not defined.
config_get_bool() {
local var="${1}"; shift
if config_has "${var}"; then
case "$(config_get "${var}")" in
[Yy][Ee][Ss]|[Tt][Rr][Uu][Ee])
return 0
;;
[Nn][Oo]|[Ff][Aa][Ll][Ss][Ee])
return 1
;;
*)
utils_error "Invalid boolean value in variable ${var}"
;;
esac
else
return 1
fi
}
# Gets the value of a configuration variable with a default fallback.
#
# \post The value of the variable is printed to stdout, if any.
#
# \param var Name of the configuration variable to query.
# \param default Default value to return if the variable is not defined.
config_get_default() {
local var="${1}"; shift
local default="${1}"; shift
if config_has "${var}"; then
config_get "${var}"
else
echo "${default}"
fi
}
# Sets a configuration variable.
#
# \post Execution terminates if the variable is not valid.
#
# \param var The name of the configuration variable to set.
# \param value The value to set.
config_set() {
local var="${1}"; shift
local value="${1}"; shift
config_is_valid "${var}" \
|| utils_usage_error "Unknown configuration variable ${var}"
eval "config_var_${var}=\"\${value}\""
}
# Loads a configuration file.
#
# \pre config_init should have been called to register the valid configuration
# variables. Any non-registered configuration variable found in the file will
# not be available through any of the functions in this module.
#
# \post The configuration module is updated with the values defined in the
# configuration file.
#
# \post Any errors in the processing of the configuration file terminate the
# execution of the script.
#
# \param config_file Path to the file to load.
config_load() {
local config_file="${1}"; shift
[ -e "${config_file}" ] || utils_error "Configuration file ${config_file}" \
"does not exist"
# User-facing variables.
local var
for var in ${CONFIG_VARS}; do
unset "${var}" || true
eval local "${var}"
done
local real_config_file
case "${config_file}" in
*/*) real_config_file="${config_file}" ;;
*) real_config_file="./${config_file}" ;;
esac
( . "${real_config_file}" ) || utils_error "Failed to load configuration" \
"file ${config_file}"
. "${real_config_file}"
for var in ${CONFIG_VARS}; do
local value
eval value="\${${var}-unset}"
[ "${value-unset}" != unset ] || continue
if [ -n "${value}" ]; then
config_set "${var}" "${value}"
else
unset "config_var_${var}" || true
fi
done
config_apply_overrides
}
# Applies recorded overrides to the current configuration.
#
# This is just a helper function for config_load and should not be used
# directly.
#
# \post The configuration data in memory is modified according to the data
# recorded in the overrides.
#
# \post The contents of CONFIG_OVERRIDES is cleared.
config_apply_overrides() {
for override in ${CONFIG_OVERRIDES}; do
case "${override}" in
set:*)
local var="$(echo ${override} | cut -d : -f 2)"
local value
eval value="\"\${config_override_var_${var}}\""
config_set "${var}" "${value}"
;;
unset:*)
local var="$(echo ${override} | cut -d : -f 2)"
unset "config_var_${var}" || true
;;
esac
done
CONFIG_OVERRIDES=
}
# Records an override to be applied to the configuration.
#
# Overrides are configuration variables set through the command line. These can
# be set before loading the configuration file with config_load.
#
# \post Any errors in the processing of the configuration override terminate the
# execution of the script.
#
# \param arg An override of the form variable=value.
config_override() {
local arg="${1}"; shift
case "${arg}" in
*=*)
;;
*)
utils_usage_error "Invalid configuration override" \
"${arg}; must be of the form variable=value"
;;
esac
local var="$(echo "${arg}" | cut -d = -f 1)"
local value="$(echo "${arg}" | cut -d = -f 2-)"
[ -n "${var}" ] || utils_usage_error "Invalid configuration override" \
"${arg}; must be of the form variable=value"
config_is_valid "${var}" \
|| utils_usage_error "Unknown configuration variable ${var}"
if [ -n "${value}" ]; then
eval "config_override_var_${var}=\"${value}\""
CONFIG_OVERRIDES="${CONFIG_OVERRIDES} set:${var}"
else
CONFIG_OVERRIDES="${CONFIG_OVERRIDES} unset:${var}"
fi
}

View file

@ -1,395 +0,0 @@
# Copyright 2012 Google Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * 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.
# * Neither the name of Google Inc. nor the names of its contributors
# may be used to endorse or promote products derived from this software
# without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 THE COPYRIGHT
# OWNER 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.
atf_test_case is_valid__true
is_valid__true_body() {
config_init VAR1 VAR3
for var in VAR1 VAR3; do
config_is_valid "${var}" || atf_fail "${var} not found"
done
}
atf_test_case is_valid__false
is_valid__false_body() {
config_init VAR1 VAR3
for var in VAR11 VAR2 VAR; do
if config_is_valid "${var}"; then
atf_fail "${var} found but was not registered"
fi
done
}
atf_test_case has__true__empty
has__true__empty_body() {
config_init TESTVAR
config_set TESTVAR ""
config_has TESTVAR || atf_fail "Expected variable not found"
}
atf_test_case has__true__not_empty
has__true__not_empty_body() {
config_init TESTVAR
config_set TESTVAR "foo"
config_has TESTVAR || atf_fail "Expected variable not found"
}
atf_test_case has__false
has__false_body() {
config_init TESTVAR
if config_has TESTVAR; then
atf_fail "Unexpected variable found"
fi
}
atf_test_case get__ok__empty
get__ok__empty_body() {
config_init TESTVAR
config_set TESTVAR ""
[ -z "$(config_get TESTVAR)" ] || atf_fail "Failed to query value"
}
atf_test_case get__ok__not_empty
get__ok__not_empty_body() {
config_init TESTVAR
config_set TESTVAR some-value
[ "$(config_get TESTVAR)" = some-value ] || atf_fail "Failed to query value"
}
atf_test_case get__undefined_variable
get__undefined_variable_body() {
config_init TESTVAR
if ( config_get TESTVAR ) >out 2>err; then
atf_fail "Got unset variable successfully"
else
grep "Required configuration variable TESTVAR not set" err >/dev/null \
|| atf_fail "Expected error message not found"
fi
}
atf_test_case get_bool__true
get_bool__true_body() {
config_init TESTVAR
for value in yes Yes true True; do
config_set TESTVAR "${value}"
config_get_bool TESTVAR || atf_fail "Expected true, but got false"
done
}
atf_test_case get_bool__false
get_bool__false_body() {
config_init TESTVAR
for value in no No false False; do
config_set TESTVAR "${value}"
if config_get_bool TESTVAR; then
atf_fail "Expected false, but got true"
fi
done
}
atf_test_case get_bool__undefined_variable
get_bool__undefined_variable_body() {
config_init TESTVAR
if config_get_bool TESTVAR; then
atf_fail "Expected false, but got true"
fi
}
atf_test_case get_bool__invalid_value
get_bool__invalid_value_body() {
config_init TESTVAR
config_set TESTVAR not-a-boolean
if ( config_get_bool TESTVAR ) >out 2>err; then
atf_fail "Got invalid boolean value successfully"
else
grep "Invalid boolean value in variable TESTVAR" err >/dev/null \
|| atf_fail "Expected error message not found"
fi
}
atf_test_case get_default__defined__empty
get_default__defined__empty_body() {
config_init TESTVAR
config_set TESTVAR ""
[ "$(config_get_default TESTVAR 'foo')" = "" ] \
|| atf_fail "Did not fetch defined value"
}
atf_test_case get_default__defined__not_empty
get_default__defined__not_empty_body() {
config_init TESTVAR
config_set TESTVAR "bar"
[ "$(config_get_default TESTVAR 'foo')" = "bar" ] \
|| atf_fail "Did not fetch defined value"
}
atf_test_case get_default__default__empty
get_default__default__empty_body() {
config_init TESTVAR
[ "$(config_get_default TESTVAR '')" = "" ] \
|| atf_fail "Did not fetch default value"
}
atf_test_case get_default__default__not_empty
get_default__default__not_empty_body() {
config_init TESTVAR
[ "$(config_get_default TESTVAR 'foo')" = "foo" ] \
|| atf_fail "Did not fetch default value"
}
atf_test_case set__ok
set__ok_body() {
config_init TESTVAR
config_set TESTVAR some-value
[ "${config_var_TESTVAR}" = some-value ] || atf_fail "Failed to set value"
}
atf_test_case set__unknown_variable
set__unknown_variable_body() {
config_init TESTVAR
if ( config_set TESTVAR2 some-value ) >out 2>err; then
atf_fail "Set unknown variable successfully"
else
grep "Unknown configuration variable TESTVAR2" err >/dev/null \
|| atf_fail "Expected error message not found"
fi
}
atf_test_case load__filter_variables
load__filter_variables_body() {
config_init Z VAR1 EMPTY
cat >test.conf <<EOF
A=foo
Z=bar
VAR1="some text"
VAR2="some other text"
EOF
config_load $(pwd)/test.conf || atf_fail "Failed to load test configuration"
[ "${config_var_Z}" = bar ] || \
atf_fail "Z not found in configuration"
[ "${config_var_VAR1}" = "some text" ] || \
atf_fail "VAR1 not found in configuration"
[ "${config_var_EMPTY-has_not_been_set}" = has_not_been_set ] || \
atf_fail "Undefined variable set, but should not have been"
[ "${config_var_A-unset}" = unset ] || \
atf_fail "A set in configuration, but not expected"
[ "${config_var_VAR2-unset}" = unset ] || \
atf_fail "VAR2 set in configuration, but not expected"
}
atf_test_case load__allow_undefine
load__allow_undefine_body() {
config_init UNDEFINE
cat >test.conf <<EOF
UNDEFINE=
EOF
config_set UNDEFINE "remove me"
config_load $(pwd)/test.conf || atf_fail "Failed to load test configuration"
if config_has UNDEFINE; then
atf_fail "Undefine attempt from configuration did not work"
fi
}
atf_test_case load__current_directory
load__current_directory_body() {
config_init A
cat >test.conf <<EOF
A=foo
EOF
config_load test.conf || atf_fail "Failed to load test configuration"
[ "${config_var_A}" = foo ] || \
atf_fail "A not found in configuration"
}
atf_test_case load__missing_file
load__missing_file_body() {
if ( config_load missing.conf ) >out 2>err; then
atf_fail "Missing configuration file load succeeded"
else
grep "Configuration file missing.conf does not exist" err >/dev/null \
|| atf_fail "Expected error message not found"
fi
}
atf_test_case load__invalid_file
load__invalid_file_body() {
echo "this file is invalid" >invalid.conf
if ( config_load invalid.conf ) >out 2>err; then
atf_fail "Invalid configuration file load succeeded"
else
cat err
grep "Failed to load configuration file invalid.conf" err >/dev/null \
|| atf_fail "Expected error message not found"
fi
}
atf_test_case override__ok_before_load
override__ok_before_load_body() {
config_init VAR1 VAR2
cat >test.conf <<EOF
VAR1="override me"
VAR2="do not override me"
EOF
config_override "VAR1=new value"
config_load test.conf || atf_fail "Failed to load test configuration"
[ "${config_var_VAR1}" = "new value" ] || atf_fail "Override failed"
[ "${config_var_VAR2}" = "do not override me" ] \
|| atf_fail "Overrode more than one variable"
}
atf_test_case override__not_ok_after_load
override__not_ok_after_load_body() {
config_init VAR1 VAR2
cat >test.conf <<EOF
VAR1="override me"
VAR2="do not override me"
EOF
config_load test.conf || atf_fail "Failed to load test configuration"
config_override "VAR1=new value"
[ "${config_var_VAR1}" = "override me" ] \
|| atf_fail "Override succeeded, but it should not have"
[ "${config_var_VAR2}" = "do not override me" ] \
|| atf_fail "Overrode more than one variable"
}
atf_test_case override__invalid_format
override__invalid_format_body() {
for arg in foo =bar ''; do
if ( config_override "${arg}" ) >out 2>err; then
atf_fail "Invalid configuration override ${arg} succeeded"
else
cat err
grep "Invalid configuration override ${arg}" err >/dev/null \
|| atf_fail "Expected error message not found"
fi
done
}
atf_test_case override__unknown_variable
override__unknown_variable_body() {
config_init Z VAR1
for arg in A=b VAR2=d; do
if ( config_override "${arg}" ) >out 2>err; then
atf_fail "Invalid configuration override ${arg} succeeded"
else
cat err
grep "Unknown configuration variable ${var}" err >/dev/null \
|| atf_fail "Expected error message not found"
fi
done
}
atf_init_test_cases() {
atf_add_test_case is_valid__true
atf_add_test_case is_valid__false
atf_add_test_case has__true__empty
atf_add_test_case has__true__not_empty
atf_add_test_case has__false
atf_add_test_case get__ok__empty
atf_add_test_case get__ok__not_empty
atf_add_test_case get__undefined_variable
atf_add_test_case get_bool__true
atf_add_test_case get_bool__false
atf_add_test_case get_bool__undefined_variable
atf_add_test_case get_bool__invalid_value
atf_add_test_case get_default__defined__empty
atf_add_test_case get_default__defined__not_empty
atf_add_test_case get_default__default__empty
atf_add_test_case get_default__default__not_empty
atf_add_test_case set__ok
atf_add_test_case set__unknown_variable
atf_add_test_case load__filter_variables
atf_add_test_case load__allow_undefine
atf_add_test_case load__current_directory
atf_add_test_case load__missing_file
atf_add_test_case load__invalid_file
atf_add_test_case override__ok_before_load
atf_add_test_case override__not_ok_after_load
atf_add_test_case override__invalid_format
atf_add_test_case override__unknown_variable
}

View file

@ -1,107 +0,0 @@
# Copyright 2012 Google Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * 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.
# * Neither the name of Google Inc. nor the names of its contributors
# may be used to endorse or promote products derived from this software
# without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 THE COPYRIGHT
# OWNER 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.
# \file cvs.subr
# Utilities to invoke CVS and wrappers to simplify its operation.
# Checks out or updates a working copy.
#
# \param cvsroot Location of the CVS repository.
# \param module Module to check out, when the working copy does not exist yet.
# \param tag CVS tag to use, if any; may be empty.
# \param directory Target directory in which to store the checked out files or
# the updated files.
cvs_fetch() {
local cvsroot="${1}"; shift
local module="${1}"; shift
local tag="${1}"; shift
local directory="${1}"; shift
if [ -d "${directory}" ]; then
cvs_update "${cvsroot}" "${tag}" "${directory}"
else
cvs_checkout "${cvsroot}" "${module}" "${tag}" "${directory}"
fi
}
# Checks out a new directory from CVS.
#
# \param cvsroot Location of the CVS repository.
# \param module The name of the module to check out.
# \param tag CVS tag to use, if any; may be empty.
# \param directory The directory in which to perform the checkout.
cvs_checkout() {
local cvsroot="${1}"; shift
local module="${1}"; shift
local tag="${1}"; shift
local directory="${1}"; shift
local rflag=
[ -z "${tag}" ] || rflag="-r${tag}"
[ ! -d "${directory}" ] || utils_error "Cannot checkout into" \
"${directory}; directory already exists"
mkdir -p "${directory}"
mkdir "${directory}"/.cvs-checkout \
|| utils_error "Failed to create ${directory}"
( cd "${directory}"/.cvs-checkout &&
utils_run cvs -d"${cvsroot}" -q checkout -P ${rflag} "${module}" )
mv "${directory}"/.cvs-checkout/"${module}"/* "${directory}"/ \
|| true # Maybe the checkout yielded no files...
rm -rf "${directory}"/.cvs-checkout
}
# Updates an existing directory from CVS.
#
# \param cvsroot Location of the CVS repository.
# \param tag CVS tag to use, if any; may be empty.
# \param directory The directory in which to perform the update.
cvs_update() {
local cvsroot="${1}"; shift
local tag="${1}"; shift
local directory="${1}"; shift
local rflag=
[ -z "${tag}" ] || rflag="-r${tag}"
[ -d "${directory}" ] || utils_error "Cannot update ${directory};" \
"directory does not exist"
if [ -d "${directory}/.cvs-checkout" ]; then
# Attempt to resume a previously aborted cvs_checkout.
mv "${directory}"/.cvs-checkout/*/* "${directory}"/ \
|| true # Maybe the checkout yielded no files...
rm -rf "${directory}"/.cvs-checkout
fi
( cd "${directory}" && \
utils_run cvs -d"${cvsroot}" -q update -d -P ${rflag} )
}

View file

@ -1,180 +0,0 @@
# Copyright 2012 Google Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * 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.
# * Neither the name of Google Inc. nor the names of its contributors
# may be used to endorse or promote products derived from this software
# without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 THE COPYRIGHT
# OWNER 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.
MOCK_CVSROOT=":local:$(pwd)/cvsroot"
# Creates a local CVS repository with a variety of modules.
#
# \param repository Path to the repository to create.
# \param ... Modules to create.
init_cvsroot() {
local repository="${1}"; shift
atf_check -o ignore -e ignore cvs -d "${repository}" init
for module in "${@}"; do
mkdir module
cd module
echo "first revision" >"file-in-${module}"
cvs -d "${repository}" import -m "Import." "${module}" VENDOR_TAG \
release_tag
cd -
rm -rf module
done
}
atf_test_case fetch
fetch_body() {
init_cvsroot "${MOCK_CVSROOT}" src
cvs_fetch "${MOCK_CVSROOT}" src "" first
grep "first revision" first/file-in-src >/dev/null \
|| atf_fail "Unexpected version found"
cp -rf first second
echo "second revision" >second/file-in-src
( cd second && cvs commit -m "Second commit." )
cvs_fetch "${MOCK_CVSROOT}" src "" first
grep "second revision" first/file-in-src >/dev/null \
|| atf_fail "Unexpected version found"
}
atf_test_case checkout__same_name
checkout__same_name_body() {
init_cvsroot "${MOCK_CVSROOT}" first second
cvs_checkout "${MOCK_CVSROOT}" first "" $(pwd)/a/b/c/first
[ -f a/b/c/first/file-in-first ] || atf_fail "Files not checked out"
if [ -f a/b/c/second/file-in-second ]; then
atf_fail "Unexpected module checked out"
fi
}
atf_test_case checkout__different_name
checkout__different_name_body() {
init_cvsroot "${MOCK_CVSROOT}" first second
cvs_checkout "${MOCK_CVSROOT}" first "" $(pwd)/a/b/c/second
[ -f a/b/c/second/file-in-first ] || atf_fail "Files not checked out"
}
atf_test_case checkout__already_exists
checkout__already_exists_body() {
mkdir usr/src
if ( cvs_checkout "${MOCK_CVSROOT}" src "" $(pwd)/usr/src ) >out 2>err; then
atf_fail "Checkout succeeded, but should not"
else
grep "Cannot checkout into $(pwd)/usr/src.*exists" err >/dev/null \
|| atf_fail "Expected error message not found"
fi
}
atf_test_case checkout__permission_denied
checkout__permission_denied_head() {
atf_set "require.user" "unprivileged"
}
checkout__permission_denied_body() {
init_cvsroot "${MOCK_CVSROOT}" src
mkdir usr
chmod 555 usr
if ( cvs_checkout "${MOCK_CVSROOT}" src "" $(pwd)/usr/src ) >out 2>err; then
atf_fail "Checkout succeeded, but should not"
else
grep "Failed to create $(pwd)/usr/src" err >/dev/null \
|| atf_fail "Expected error message not found"
fi
}
atf_test_case update__ok
update__ok_body() {
init_cvsroot "${MOCK_CVSROOT}" first second
cvs -d "${MOCK_CVSROOT}" checkout first
mv first copy
cvs -d "${MOCK_CVSROOT}" checkout first
cvs_update "${MOCK_CVSROOT}" "" first
grep "first revision" first/file-in-first >/dev/null \
|| atf_fail "Unexpected version found"
echo "second revision" >copy/file-in-first
( cd copy && cvs commit -m "Second commit." )
cvs_update "${MOCK_CVSROOT}" "" first
grep "second revision" first/file-in-first >/dev/null \
|| atf_fail "Unexpected version found"
}
atf_test_case update__resume_checkout
update__resume_checkout_body() {
init_cvsroot "${MOCK_CVSROOT}" first
cvs -d "${MOCK_CVSROOT}" checkout first
mv first copy
mkdir -p first/.cvs-checkout/first
mv copy/CVS first/.cvs-checkout/first
rm -rf copy
cvs_update "${MOCK_CVSROOT}" "" first
grep "first revision" first/file-in-first >/dev/null \
|| atf_fail "Unexpected version found"
}
atf_test_case update__does_not_exist
update__does_not_exist_body() {
if ( cvs_update "${MOCK_CVSROOT}" "" src ) >out 2>err; then
atf_fail "Update succeeded, but should not"
else
grep "Cannot update src; .*not exist" err >/dev/null \
|| atf_fail "Expected error message not found"
fi
}
atf_init_test_cases() {
atf_add_test_case fetch
atf_add_test_case checkout__same_name
atf_add_test_case checkout__different_name
atf_add_test_case checkout__already_exists
atf_add_test_case checkout__permission_denied
atf_add_test_case update__ok
atf_add_test_case update__resume_checkout
atf_add_test_case update__does_not_exist
}

View file

@ -29,6 +29,11 @@
# \file sysbuild.sh
# Entry point and main program logic.
shtk_import cli
shtk_import config
shtk_import cvs
shtk_import process
# List of valid configuration variables.
#
@ -50,14 +55,14 @@ SYSBUILD_CONFIG_VARS="BUILD_ROOT BUILD_TARGETS CVSROOT CVSTAG INCREMENTAL_BUILD
# him shoot himself in the foot if he so desires.
sysbuild_set_defaults() {
# Please remember to update sysbuild(1) if you change any default values.
config_set BUILD_ROOT "${HOME}/sysbuild"
config_set BUILD_TARGETS "release"
config_set CVSROOT ":ext:anoncvs@anoncvs.NetBSD.org:/cvsroot"
config_set INCREMENTAL_BUILD "false"
config_set MACHINES "$(uname -m)"
config_set RELEASEDIR "${HOME}/sysbuild/release"
config_set SRCDIR "${HOME}/sysbuild/src"
config_set UPDATE_SOURCES "true"
shtk_config_set BUILD_ROOT "${HOME}/sysbuild"
shtk_config_set BUILD_TARGETS "release"
shtk_config_set CVSROOT ":ext:anoncvs@anoncvs.NetBSD.org:/cvsroot"
shtk_config_set INCREMENTAL_BUILD "false"
shtk_config_set MACHINES "$(uname -m)"
shtk_config_set RELEASEDIR "${HOME}/sysbuild/release"
shtk_config_set SRCDIR "${HOME}/sysbuild/src"
shtk_config_set UPDATE_SOURCES "true"
}
@ -67,22 +72,22 @@ sysbuild_set_defaults() {
do_one_build() {
local machine="${1}"; shift
local basedir="$(config_get BUILD_ROOT)/${machine}"
local basedir="$(shtk_config_get BUILD_ROOT)/${machine}"
local jflag=
if config_has NJOBS; then
jflag="-j$(config_get NJOBS)"
if shtk_config_has NJOBS; then
jflag="-j$(shtk_config_get NJOBS)"
fi
local uflag=
if config_get_bool INCREMENTAL_BUILD; then
if shtk_config_get_bool INCREMENTAL_BUILD; then
uflag=-u
# Get rid of any possibly-old modules; they are a constant source of
# update-build problems.
rm -rf "${basedir}/destdir/stand"/*
else
utils_info "Cleaning up previous build files"
shtk_cli_info "Cleaning up previous build files"
rm -rf "${basedir}/destdir"
rm -rf "${basedir}/obj"
rm -rf "${basedir}/tools"
@ -90,16 +95,16 @@ do_one_build() {
local xflag=
local Xflag=
if config_has XSRCDIR; then
if shtk_config_has XSRCDIR; then
xflag=-x
Xflag="-X$(config_get XSRCDIR)"
Xflag="-X$(shtk_config_get XSRCDIR)"
fi
( cd "$(config_get SRCDIR)" && utils_run ./build.sh \
( cd "$(shtk_config_get SRCDIR)" && shtk_process_run ./build.sh \
-D"${basedir}/destdir" \
-M"${basedir}/obj" \
-N2 \
-R"$(config_get RELEASEDIR)" \
-R"$(shtk_config_get RELEASEDIR)" \
-T"${basedir}/tools" \
-U \
${Xflag} \
@ -107,7 +112,7 @@ do_one_build() {
-m"${machine}" \
${uflag} \
${xflag} \
$(config_get BUILD_TARGETS) )
$(shtk_config_get BUILD_TARGETS) )
}
@ -118,24 +123,24 @@ sysbuild_build() {
while getopts ':f' arg "${@}"; do
case "${arg}" in
f) # Convenience flag for a "fast mode".
config_set "INCREMENTAL_BUILD" "true"
config_set "UPDATE_SOURCES" "false"
shtk_config_set "INCREMENTAL_BUILD" "true"
shtk_config_set "UPDATE_SOURCES" "false"
;;
\?)
utils_usage_error "Unknown option -${OPTARG}"
shtk_cli_usage_error "Unknown option -${OPTARG}"
;;
esac
done
shift $((${OPTIND} - 1))
[ ${#} -eq 0 ] || config_set BUILD_TARGETS "${*}"
[ ${#} -eq 0 ] || shtk_config_set BUILD_TARGETS "${*}"
if config_get_bool UPDATE_SOURCES; then
if shtk_config_get_bool UPDATE_SOURCES; then
sysbuild_fetch
fi
for machine in $(config_get MACHINES); do
for machine in $(shtk_config_get MACHINES); do
do_one_build "${machine}"
done
}
@ -145,11 +150,11 @@ sysbuild_build() {
#
# \params ... The options and arguments to the command.
sysbuild_config() {
[ ${#} -eq 0 ] || utils_usage_error "config does not take any arguments"
[ ${#} -eq 0 ] || shtk_cli_usage_error "config does not take any arguments"
for var in ${SYSBUILD_CONFIG_VARS}; do
if config_has "${var}"; then
echo "${var} = $(config_get "${var}")"
if shtk_config_has "${var}"; then
echo "${var} = $(shtk_config_get "${var}")"
else
echo "${var} is undefined"
fi
@ -161,18 +166,18 @@ sysbuild_config() {
#
# \params ... The options and arguments to the command.
sysbuild_fetch() {
[ ${#} -eq 0 ] || utils_usage_error "fetch does not take any arguments"
[ ${#} -eq 0 ] || shtk_cli_usage_error "fetch does not take any arguments"
local cvsroot="$(config_get CVSROOT)"
local cvsroot="$(shtk_config_get CVSROOT)"
utils_info "Updating base source tree"
cvs_fetch "${cvsroot}" src "$(config_get_default CVSTAG '')" \
"$(config_get SRCDIR)"
shtk_cli_info "Updating base source tree"
shtk_cvs_fetch "${cvsroot}" src "$(shtk_config_get_default CVSTAG '')" \
"$(shtk_config_get SRCDIR)"
if config_has XSRCDIR; then
utils_info "Updating X11 source tree"
cvs_fetch "${cvsroot}" xsrc "$(config_get_default CVSTAG '')" \
"$(config_get XSRCDIR)"
if shtk_config_has XSRCDIR; then
shtk_cli_info "Updating X11 source tree"
shtk_cvs_fetch "${cvsroot}" xsrc \
"$(shtk_config_get_default CVSTAG '')" "$(shtk_config_get XSRCDIR)"
fi
}
@ -201,11 +206,11 @@ sysbuild_config_load() {
fi
done
[ -n "${config_file}" ] \
|| utils_usage_error "Cannot locate configuration named" \
|| shtk_cli_usage_error "Cannot locate configuration named" \
"'${config_name}'"
;;
esac
config_load "${config_file}"
shtk_config_load "${config_file}"
}
@ -217,7 +222,7 @@ sysbuild_config_load() {
sysbuild_main() {
local config_name="default"
config_init ${SYSBUILD_CONFIG_VARS}
shtk_config_init ${SYSBUILD_CONFIG_VARS}
while getopts ':c:o:' arg "${@}"; do
case "${arg}" in
@ -226,17 +231,17 @@ sysbuild_main() {
;;
o) # Override for a particular configuration variable.
config_override "${OPTARG}"
shtk_config_override "${OPTARG}"
;;
\?)
utils_usage_error "Unknown option -${OPTARG}"
shtk_cli_usage_error "Unknown option -${OPTARG}"
;;
esac
done
shift $((${OPTIND} - 1))
[ ${#} -ge 1 ] || utils_usage_error "No command specified"
[ ${#} -ge 1 ] || shtk_cli_usage_error "No command specified"
local exit_code=0
@ -249,7 +254,7 @@ sysbuild_main() {
;;
*)
utils_usage_error "Unknown command ${command}"
shtk_cli_usage_error "Unknown command ${command}"
;;
esac

View file

@ -29,6 +29,8 @@
# \file sysbuild4cron.sh
# Entry point and main program logic.
shtk_import cli
# Paths to installed files.
#
@ -85,7 +87,7 @@ sysbuild4cron_main() {
;;
\?)
utils_usage_error "Unknown option -${OPTARG}"
shtk_cli_usage_error "Unknown option -${OPTARG}"
;;
esac
done
@ -96,11 +98,11 @@ sysbuild4cron_main() {
if [ ! -d "${logdir}" ]; then
mkdir -p "$(dirname "${logdir}")" >/dev/null 2>/dev/null || true
mkdir "${logdir}" \
|| utils_error "Failed to create directory '${logdir}'"
|| shtk_cli_error "Failed to create directory '${logdir}'"
fi
local exit_code=0
local logfile="${logdir}/${Utils_ProgName}.$(date +%Y%m%d%H%M%S).log"
local logfile="${logdir}/$(shtk_cli_progname).$(date +%Y%m%d%H%M%S).log"
"${@}" >"${logfile}" 2>&1 \
|| sysbuild4cron_email "${logfile}" "${recipient}" "${@}"
}

View file

@ -1,3 +1,4 @@
#! @ATF_SH@
# Copyright 2012 Google Inc.
# All rights reserved.
#

View file

@ -1,3 +1,4 @@
#! @ATF_SH@
# Copyright 2012 Google Inc.
# All rights reserved.
#
@ -91,21 +92,6 @@ create_mock_cvsroot() {
}
# Creates a fake id(1) binary for use by the "unprivileged-user" tests.
create_mock_id() {
[ -n "@SYSBUILD_USER@" ] \
|| atf_skip "Package built without unprivileged-user"
cat >id <<EOF
#! /bin/sh
echo @SYSBUILD_USER@
EOF
chmod +x id
PATH="$(pwd):${PATH}"
}
atf_test_case build__custom_dirs
build__custom_dirs_body() {
create_mock_cvsroot "${MOCK_CVSROOT}"

View file

@ -1,91 +0,0 @@
# Copyright 2012 Google Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * 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.
# * Neither the name of Google Inc. nor the names of its contributors
# may be used to endorse or promote products derived from this software
# without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 THE COPYRIGHT
# OWNER 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.
# \file utils.subr
# Miscellaneous utility functions and global variables.
# Base name of the running script.
Utils_ProgName="${0##*/}"
# Prints a runtime error and exits.
#
# \param ... The message to print. Can be provided as multiple words and, in
# that case, they are joined together by a single whitespace.
utils_error() {
echo "${Utils_ProgName}: E: $*" 1>&2
exit 1
}
# Prints an informational message.
#
# \param ... The message to print. Can be provided as multiple words and, in
# that case, they are joined together by a single whitespace.
utils_info() {
echo "${Utils_ProgName}: I: $*" 1>&2
}
# Prints a runtime warning.
#
# \param ... The message to print. Can be provided as multiple words and, in
# that case, they are joined together by a single whitespace.
utils_warning() {
echo "${Utils_ProgName}: W: $*" 1>&2
}
# Prints an usage error and exits.
#
# \param ... The message to print. Can be provided as multiple words and, in
# that case, they are joined together by a single whitespace.
utils_usage_error() {
echo "${Utils_ProgName}: E: $*" 1>&2
echo "Type 'man ${Utils_ProgName}' for help" 1>&2
exit 1
}
# Executes a command with logging.
#
# \param ... The command to execute.
#
# \return The exit code of the executed command.
utils_run() {
utils_info "Running '${@}' in $(pwd)"
local ret=0
"${@}" || ret="${?}"
if [ ${ret} -eq 0 ]; then
utils_info "Command finished successfully"
else
utils_warning "Command failed with code ${ret}"
fi
return "${ret}"
}

View file

@ -1,145 +0,0 @@
# Copyright 2012 Google Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * 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.
# * Neither the name of Google Inc. nor the names of its contributors
# may be used to endorse or promote products derived from this software
# without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 THE COPYRIGHT
# OWNER 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.
atf_test_case global_progname
global_progname_body() {
[ "${Utils_ProgName}" = utils_test ] \
|| atf_fail "Invalid value in Utils_ProgName"
}
atf_test_case error
error_body() {
if ( utils_error "This is" "a message"; echo "not seen" ) >out 2>err; then
atf_fail "utils_error did not exit with an error"
else
grep "utils_test: E: This is a message" err >/dev/null \
|| atf_fail "Expected error message not found"
[ ! -s out ] || atf_fail "Unexpected output in stdout"
fi
}
atf_test_case info
info_body() {
( utils_info "This is" "a message"; echo "continuing" ) >out 2>err
grep "utils_test: I: This is a message" err >/dev/null \
|| atf_fail "Expected info message not found"
grep "continuing" out >/dev/null || atf_fail "Execution aborted"
}
atf_test_case usage_error
usage_error_body() {
if ( utils_usage_error "This is" "a message"; echo "not seen" ) >out 2>err
then
atf_fail "utils_usage_error did not exit with an error"
else
grep "utils_test: E: This is a message" err >/dev/null \
|| atf_fail "Expected error message not found"
grep "Type 'man utils_test' for help" err >/dev/null \
|| atf_fail "Expected instructional message not found"
[ ! -s out ] || atf_fail "Unexpected output in stdout"
fi
}
atf_test_case warning
warning_body() {
( utils_warning "This is" "a message"; echo "continuing" ) >out 2>err
grep "utils_test: W: This is a message" err >/dev/null \
|| atf_fail "Expected info message not found"
grep "continuing" out >/dev/null || atf_fail "Execution aborted"
}
atf_test_case run__ok
run__ok_body() {
cat >helper.sh <<EOF
#! /bin/sh
echo "This exits cleanly:" "\${@}"
exit 0
EOF
chmod +x helper.sh
utils_run ./helper.sh one two three >out 2>err \
|| atf_fail "Got an unexpected error code"
cat >expout <<EOF
This exits cleanly: one two three
EOF
atf_check -o file:expout cat out
cat >experr <<EOF
utils_test: I: Running './helper.sh one two three' in $(pwd)
utils_test: I: Command finished successfully
EOF
atf_check -o file:experr cat err
}
atf_test_case run__fail
run__fail_body() {
cat >helper.sh <<EOF
#! /bin/sh
echo "This exits with an error:" "\${@}"
exit 42
EOF
chmod +x helper.sh
code=0
utils_run ./helper.sh one two three >out 2>err || code="${?}"
[ ${code} -eq 42 ] \
|| atf_fail "Did not get the expected error code; got ${code}"
cat >expout <<EOF
This exits with an error: one two three
EOF
atf_check -o file:expout cat out
cat >experr <<EOF
utils_test: I: Running './helper.sh one two three' in $(pwd)
utils_test: W: Command failed with code 42
EOF
atf_check -o file:experr cat err
}
atf_init_test_cases() {
atf_add_test_case global_progname
atf_add_test_case error
atf_add_test_case info
atf_add_test_case warning
atf_add_test_case usage_error
atf_add_test_case run__ok
atf_add_test_case run__fail
}