PACKAGE_STRING := @PACKAGE_STRING@
install_sh := @install_sh@
cmdline_dir := @cmdline_dir@
+executables := @executables@
build_date := $(shell date)
uname_s := $(shell uname -s 2>/dev/null || echo "UNKNOWN_OS")
uname_rs := $(shell uname -rs)
cc_version := $(shell $(CC) --version | head -n 1)
-codename := deterministic entropy
+codename := infinite rollback
DEBUG_CPPFLAGS += -Wno-sign-compare -g -Wunused -Wundef -W
DEBUG_CPPFLAGS += -Wredundant-decls
+DEBUG_CPPFLAGS += -Wall
+DEBUG_CPPFLAGS += -Wformat-security
+DEBUG_CPPFLAGS += -Wmissing-format-attribute
# produces false positives
# DEBUG_CPPFLAGS += -Wunreachable-code
# DEBUG_CPPFLAGS += -Wwrite-strings
CPPFLAGS += -Wshadow
endif
CPPFLAGS += -Os
-CPPFLAGS += -Wall
CPPFLAGS += -Wuninitialized
CPPFLAGS += -Wchar-subscripts
-CPPFLAGS += -Wformat-security
CPPFLAGS += -DBINDIR='"$(BINDIR)"'
CPPFLAGS += -DBUILD_DATE='"$(build_date)"'
CPPFLAGS += -DUNAME_RS='"$(uname_rs)"'
CPPFLAGS += -DCODENAME='"$(codename)"'
CPPFLAGS += -DCC_VERSION='"$(cc_version)"'
CPPFLAGS += -Werror-implicit-function-declaration
-CPPFLAGS += -Wmissing-format-attribute
CPPFLAGS += -Wmissing-noreturn
CPPFLAGS += -Wunused-macros
CPPFLAGS += -Wbad-function-cast
ggo_dir := ggo
object_dir := objects
man_dir := man/man1
+test_dir := t
m4_ggos := afh audioc audiod client filter gui recv server write
all_ggos := $(m4_ggos) dccp_recv alsa_write oss_write fade http_recv \
ifndef BUILD_VERBOSE
BUILD_VERBOSE = 0
endif
-ifeq ($(BUILD_VERBOSE),1)
- Q =
-else
+ifeq ($(BUILD_VERBOSE),0)
Q = @
+else
+ Q =
endif
.PHONY: dep all clean distclean maintainer-clean install man tarball\
$(Q) rm -rf man $(object_dir)
$(Q) rm -f *_command_list.*
-distclean: clean2
+distclean: clean2 test-clean
@[ -z "$(Q)" ] || echo 'DISTCLEAN'
$(Q) rm -f Makefile autoscan.log config.status config.log
$(Q) rm -rf autom4te.cache aclocal.m4
%.pdf: %.ps
ps2pdf - - < $< > $@
+include $(test_dir)/makefile.test
------------------------------------------------
-0.4.6 (to be announced) "deterministic entropy"
------------------------------------------------
+-------------------------------------------
+0.4.7 (to be announced) "infinite rollback"
+-------------------------------------------
+
+------------------------------------------
+0.4.6 (2011-03-31) "deterministic entropy"
+------------------------------------------
-Lots of ogg/vorbis improvements, enhancements for para_gui and a fair
-amount of other bug fixes.
+Lots of ogg/vorbis improvements, the new test suite, enhancements
+for para_gui and a fair amount of other bug fixes.
- For DCCP/OGG streams the audio file header is only sent once
at the beginning of the stream rather than periodically
- The vorbis comment header is replaced by an empty dummy header
before the header is sent over the network. This also results in
less network traffic and smaller FEC groups.
+ - The new "test" make target allows to perform some sanity checks prior
+ to installing the package.
- ogg timing fixes and performance improvements
+ - Scheduler improvements
+ - Proper exit codes for para_write
- para_gui: New option --theme to select a startup theme. Several
other improvements and fixes.
- aacdec error message cleanups
}
ret = open_and_update_audio_file(aft_row, score, &afd);
if (ret < 0) {
- PARA_ERROR_LOG("%s\n", para_strerror(-ret));
ret = score_delete(aft_row);
if (ret < 0) {
PARA_ERROR_LOG("%s\n", para_strerror(-ret));
err:
free(afd->afhi.chunk_table);
osl_close_disk_object(&chunk_table_obj);
+ if (ret < 0)
+ PARA_ERROR_LOG("%s: %s\n", path, para_strerror(-ret));
return ret;
}
bytes = btr_next_buffer(btrn, &data);
if (ret < 0 || bytes < wn->min_iqs) { /* eof */
assert(btr_no_parent(btrn));
- ret = -E_ALSA_EOF;
+ ret = -E_WRITE_COMMON_EOF;
if (!pad)
goto err;
/* wait until pending frames are played */
* \return The audio format number on success, -E_UNSUPPORTED_AUDIO_FORMAT if
* \a name is not a supported audio format.
*/
-int get_audio_format_num(const char *name)
+static int get_audio_format_num(const char *name)
{
int i;
return -E_NO_MORE_SLOTS;
}
-/**
- * get the number of filters
- *
- * \param audio_format_num the number identifying the audio format
- *
- * \return the number of filters for the given audio format
- *
- * \sa struct filter;
- */
-int num_filters(int audio_format_num)
-{
- return afi[audio_format_num].num_filters;
-}
-
static void open_filters(struct slot_info *s)
{
struct audio_format_info *a = afi + s->format;
if (s->format < 0)
return;
- if (s->receiver_node && s->receiver_node->task.error != -E_TASK_UNREGISTERED)
+ if (s->receiver_node && s->receiver_node->task.error >= 0)
return;
for (i = 0; i < a->num_filters; i++)
- if (s->fns && s->fns[i].task.error != -E_TASK_UNREGISTERED)
+ if (s->fns && s->fns[i].task.error >= 0)
return;
if (a->num_writers > 0) {
for (i = 0; i < a->num_writers; i++)
- if (s->wns && s->wns[i].task.error != -E_TASK_UNREGISTERED)
+ if (s->wns && s->wns[i].task.error >= 0)
return;
} else {
- if (s->wns && s->wns[0].task.error != -E_TASK_UNREGISTERED)
+ if (s->wns && s->wns[0].task.error >= 0)
return;
}
PARA_INFO_LOG("closing slot %d\n", slot_num);
kill_btrn(st->ct->btrn, &st->ct->task, -E_AUDIOD_OFF);
goto out;
}
- if (st->ct->task.error != -E_TASK_UNREGISTERED)
+ if (st->ct->task.error >= 0)
goto out;
close_stat_pipe();
st->clock_diff_count = conf.clock_diff_count_arg;
size_t sz;
int ret;
if (st->ct->task.error < 0) {
- if (st->ct->task.error != -E_TASK_UNREGISTERED)
+ if (st->ct->task.error >= 0)
goto out;
close_stat_pipe();
goto out;
N: grab
D: grab the audio stream
L:
-U: -- grab [-m[{s|p|a}]] [-p=<parent>] [-n=<name>] [-o]
+U: -- grab [-m[{s|p|a}]] [-p=PARENT] [-n=NAME] [-o]
H:
H: grab ('splice') the audio stream at any position in the buffer
H: tree and send that data back to the client.
H: the write, pedantic mode aborts and aggressive mode tries
H: to write anyway.
H:
-H: -p Grab output of node <parent> of the buffer tree.
+H: -p Grab output of node PARENT of the buffer tree.
H:
H: -n Name of the new buffer tree node. Defaults to 'grab'.
H:
H: I've changed my mind and opened up the doors.
H: -- Beatles: Help
---
-N: kill
-D: kill an active audiod task
-U: kill task_id [task_id ...]
-H: Simulate an error condition for the given task(s)
----
N: off
D: deactivate para_audiod
U: off
/** \file audiod.h symbols exported from audiod.c */
-int num_filters(int audio_format_num);
-int get_audio_format_num(const char *name);
-
/** enum of audio formats supported by para_audiod */
enum {AUDIOD_AUDIO_FORMATS_ENUM};
return ret;
}
-int com_kill(int fd, int argc, char **argv)
-{
- int i, ret = 1;
- if (argc < 2)
- return -E_AUDIOD_SYNTAX;
- for (i = 1; i < argc; i++) {
- ret = kill_task(argv[i]);
- if (ret < 0)
- break;
- }
- if (ret > 0)
- close(fd);
- return ret;
-}
-
int com_stat(int fd, int argc, char **argv)
{
int i, ret, parser_friendly = 0;
if test -f Makefile; then
make maintainer-clean > /dev/null
fi
-aclocal -I . &> /dev/null
+aclocal -I . > /dev/null 2>&1
autoconf
autoheader
echo configuring...
./configure $@ > /dev/null
echo compiling...
-make clean2 &> /dev/null
+make clean2 > /dev/null 2>&1
make > /dev/null
free(info);
}
ut = uptime_str();
- ret = rc4_send_va_buffer(rc4c, "up: %s\nplayed: %u\n"
+ ret = rc4_send_va_buffer(rc4c, "version: " GIT_VERSION "\n"
+ "up: %s\nplayed: %u\n"
"server_pid: %d\n"
"afs_pid: %d\n"
"connections (active/accepted/total): %u/%u/%u\n"
PARA_ERROR(BAD_SAMPLE_FORMAT, "sample format not supported"), \
PARA_ERROR(BAD_CHANNEL_COUNT, "channel count not supported"), \
PARA_ERROR(BAD_SAMPLERATE, "sample rate not supported"), \
- PARA_ERROR(OSS_EOF, "oss: end of file"), \
#define COMPRESS_FILTER_ERRORS \
#define SCHED_ERRORS \
- PARA_ERROR(TASK_KILLED, "task killed"), \
- PARA_ERROR(TASK_UNREGISTERED, "task has been unscheduled"), \
- PARA_ERROR(NO_SUCH_TASK, "task not found"), \
PARA_ERROR(NOT_INITIALIZED, "scheduler not yet initialized"), \
+ PARA_ERROR(SCHED_SHUTDOWN, "scheduler was shut down"), \
#define NET_ERRORS \
#define AUDIOD_COMMAND_ERRORS \
PARA_ERROR(CLIENT_WRITE, "client write error"), \
- PARA_ERROR(AUDIOD_SYNTAX, "syntax error"), \
PARA_ERROR(UCRED_PERM, "permission denied"), \
PARA_ERROR(INVALID_AUDIOD_CMD, "invalid command"), \
PARA_ERROR(TOO_MANY_CLIENTS, "maximal number of stat clients exceeded"), \
#define WRITE_ERRORS \
PARA_ERROR(WRITE_SYNTAX, "para_write syntax error"), \
- PARA_ERROR(NO_WAV_HEADER, "wave header not found"), \
#define ALSA_WRITE_ERRORS \
PARA_ERROR(SET_RATE, "snd_pcm_hw_params_set_rate_near failed"), \
PARA_ERROR(START_THRESHOLD, "snd_pcm_sw_params_set_start_threshold() failed"), \
PARA_ERROR(STOP_THRESHOLD, "snd_pcm_sw_params_set_stop_threshold() failed"), \
- PARA_ERROR(ALSA_EOF, "alsa: end of file"), \
-
#define WRITE_COMMON_ERRORS \
PARA_ERROR(WRITE_COMMON_SYNTAX, "syntax error in write option"), \
+ PARA_ERROR(WRITE_COMMON_EOF, "end of file"), \
#define AACDEC_FILTER_ERRORS \
case mixer_channel_arg_reclev: val = SOUND_MIXER_RECLEV; break;
case mixer_channel_arg_igain: val = SOUND_MIXER_IGAIN; break;
case mixer_channel_arg_ogain: val = SOUND_MIXER_OGAIN; break;
+ default: break;
}
conf.mixer_channel_arg = val;
}
}
fixup_mixer_channel_arg();
switch (conf.mode_arg) {
- case mode_arg_sleep:
- ret = sweet_dreams();
- break;
case mode_arg_fade:
ret = fade(conf.fade_vol_arg, conf.fade_time_arg);
break;
case mode_arg_snooze:
ret = snooze();
break;
+ default: /* sleep mode */
+ ret = sweet_dreams();
+ break;
}
if (ret < 0)
PARA_EMERG_LOG("%s\n", para_strerror(-ret));
* This is used often, so better optimize it! Currently the loop is unrolled 16
* times. The case c=0 is also optimized, whereas c=1 is not.
*/
-static void addmul(unsigned char *dst1, const unsigned char const *src1,
+static void addmul(unsigned char *dst1, const unsigned char *src1,
unsigned char c, int sz)
{
if (c == 0)
return;
unsigned char *dst = dst1, *lim = &dst[sz - UNROLL + 1],
*col = gf_mul_table[c];
- const unsigned char const *src = src1;
+ const unsigned char *src = src1;
for (; dst < lim; dst += UNROLL, src += UNROLL) {
dst[0] ^= col[src[0]];
list_move(&gc->node, &active_grab_client_list);
gc->btrn = btr_new_node(&(struct btr_node_description)
EMBRACE(.name = name, .parent = parent));
- if (!gc->task.pre_select) {
- gc->task.pre_select = gc_pre_select;
- gc->task.post_select = gc_post_select;
- snprintf(gc->task.status, sizeof(gc->task.status) - 1, "%s", name);
- gc->task.status[sizeof(gc->task.status) - 1] = '\0';
- register_task(&gc->task);
- }
+ gc->task.pre_select = gc_pre_select;
+ gc->task.post_select = gc_post_select;
+ snprintf(gc->task.status, sizeof(gc->task.status) - 1, "%s", name);
+ gc->task.status[sizeof(gc->task.status) - 1] = '\0';
+ gc->task.error = 0;
+ register_task(&gc->task);
}
/**
struct grab_client *gc, *tmp;
list_for_each_entry_safe(gc, tmp, &inactive_grab_client_list, node) {
- if (gc->task.error == -E_TASK_UNREGISTERED) {
+ if (gc->fd < 0) {
list_del(&gc->node);
free(gc);
continue;
* post_select().
*/
close(gc->fd);
+ gc->fd = -1;
free(gc->parent);
free(gc->name);
return 1;
}
- gc_activate(gc);
return 0;
}
btr_consume(btrn, ret);
return;
err:
- t->error = gc_close(gc, ret)? ret : 0;
+ gc_close(gc, ret);
+ t->error = ret;
}
static int gc_check_args(int argc, char **argv, struct grab_client *gc)
enum mood_comparator_id {MOOD_COMPARATORS NUM_MOOD_COMPARATORS};
#undef MC
#define MC(a, b) # b,
-static const char const *mood_comparators[] = {MOOD_COMPARATORS};
+static const char *mood_comparators[] = {MOOD_COMPARATORS};
#undef MC
static int parse_mood_comparator(const char *word)
bytes = btr_next_buffer(btrn, &data);
frames = bytes / powd->bytes_per_frame;
if (!frames) { /* eof and less than a single frame available */
- ret = -E_OSS_EOF;
+ ret = -E_WRITE_COMMON_EOF;
goto out;
}
ret = 0;
/** Used to avoid a shortcoming in vim's syntax highlighting. */
#define EMBRACE(...) { __VA_ARGS__}
+/** A nice cup of STFU for Mr gcc. */
+#define do_nothing do {/* nothing */} while (0)
+
/**
* The sample formats supported by paraslash.
*
{
if (!initialized)
return;
+ assert(t->error < 0);
PARA_INFO_LOG("unregistering %s (%s)\n", t->status,
- t->error <0? para_strerror(-t->error) : "shutdown");
+ para_strerror(-t->error));
if (t->pre_select)
list_del(&t->pre_select_node);
if (t->post_select)
list_del(&t->post_select_node);
- t->error = -E_TASK_UNREGISTERED;
}
-
static void sched_preselect(struct sched *s)
{
struct task *t, *tmp;
list_for_each_entry_safe(t, tmp, &pre_select_list, pre_select_node) {
- if (t->error >= 0 && t->pre_select)
+ if (t->pre_select)
t->pre_select(s, t);
// PARA_INFO_LOG("%s \n", t->status);
if (t->error >= 0)
if (!initialized)
return;
- list_for_each_entry_safe(t, tmp, &pre_select_list, pre_select_node)
+ list_for_each_entry_safe(t, tmp, &pre_select_list, pre_select_node) {
+ t->error = -E_SCHED_SHUTDOWN;
unregister_task(t);
- list_for_each_entry_safe(t, tmp, &post_select_list, post_select_node)
+ }
+ list_for_each_entry_safe(t, tmp, &post_select_list, post_select_node) {
+ t->error = -E_SCHED_SHUTDOWN;
unregister_task(t);
+ }
initialized = 0;
}
*
* \return The task list.
*
- * Each entry of the list contains an identifier which is simply a hex number
- * that may be used in \a kill_task() to terminate the task.
+ * Each entry of the list contains an identifier which is simply a hex number.
* The result is dynamically allocated and must be freed by the caller.
*/
char *get_task_list(void)
return msg;
}
-/**
- * Simulate an error for the given task.
- *
- * \param id The task identifier.
- *
- * Find the task identified by \a id, set the tasks' error value to
- * \p -E_TASK_KILLED and unregister the task.
- *
- * \return Positive on success, negative on errors (e.g. if \a id does not
- * correspond to a registered task).
- */
-int kill_task(char *id)
-{
- struct task *t, *tmp;
- char buf[20];
-
- if (!initialized)
- return -E_NOT_INITIALIZED;
- list_for_each_entry_safe(t, tmp, &pre_select_list, pre_select_node) {
- sprintf(buf, "%p", t);
- if (strcmp(id, buf))
- continue;
- t->error = -E_TASK_KILLED;
- return 1;
- }
- list_for_each_entry_safe(t, tmp, &post_select_list, post_select_node) {
- sprintf(buf, "%p", t);
- if (strcmp(id, buf))
- continue;
- t->error = -E_TASK_KILLED;
- return 1;
- }
- return -E_NO_SUCH_TASK;
-}
-
/**
* Set the select timeout to the minimal possible value.
*
void register_task(struct task *t);
int schedule(struct sched *s);
char *get_task_list(void);
-int kill_task(char *id);
void sched_shutdown(void);
void sched_min_delay(struct sched *s);
void sched_request_timeout(struct timeval *to, struct sched *s);
struct task task;
/** Stdin is always the root of a buffer tree. */
struct btr_node *btrn;
- /* Use a buffer pool to minimize memcpy due to alignment problems. */
+ /** Use a buffer pool to minimize memcpy due to alignment problems. */
struct btr_pool *btrp;
};
--- /dev/null
+/trashes
+/test-results
--- /dev/null
+RM ?= rm -f
+
+results_dir := $(test_dir)/test-results
+trash_dir := $(test_dir)/trashes
+
+test_options := --executables-dir $(shell pwd)
+test_options += --results-dir $(results_dir)
+test_options += --trash-dir $(trash_dir)
+test_options += --executables "$(executables)"
+test_options += --objects "$(basename $(notdir $(all_objs)))"
+
+ifdef V
+ ifeq ("$(origin V)", "command line")
+ test_options += --verbose=$(V)
+ endif
+endif
+
+tests := $(wildcard $(test_dir)/t[0-9][0-9][0-9][0-9]-*.sh)
+
+test: $(tests)
+
+$(tests): all
+ $(Q) $@ $(test_options)
+
+test-help:
+ $(Q) for t in $(tests); do $$t $(test_options) -h; done
+
+test-clean:
+ $(RM) -r $(results_dir)
+ $(RM) -r $(trash_dir)
+
+.PHONY: $(tests) test-help
--- /dev/null
+#!/usr/bin/env bash
+
+test_description='Parse help output of all executables.
+
+Each paraslash executable supports the -h switch which instructs
+the program to print out all available options and to exit. This test
+checks whether this help output looks as expected.'
+
+. ${0%/*}/test-lib.sh
+
+grep_output()
+{
+ local regex='^ -h, --help'
+ $1 -h | grep "$regex"
+}
+
+for exe in $o_executables; do
+ test_expect_success "$exe" "grep_output $o_executables_dir/$exe"
+done
+test_done
--- /dev/null
+#!/usr/bin/env bash
+
+test_description='Check correctness of oggdec output.
+
+Executes para_filter -f oggdec on the test files provided by the test
+suite and compares the output against the output of the reference
+implementation.'
+
+. ${0%/*}/test-lib.sh
+
+test_require_objects "oggdec_filter"
+missing_objects="$result"
+
+test_require_executables "oggdec"
+missing_executables="$result"
+
+get_audio_file_paths ogg
+oggs="$result"
+
+for ogg in $oggs; do
+ if [[ -n "$missing_objects" ]]; then
+ test_skip "${ogg##*/}" "missing object(s): $missing_objects"
+ continue
+ fi
+ if [[ -n "$missing_executables" ]]; then
+ test_skip "${ogg##*/}" \
+ "missing executables(s): $missing_executables"
+ continue
+ fi
+ test_expect_success "${ogg##*/}" "
+ $PARA_FILTER -f oggdec < $ogg | sha1sum > filter.sha1 &&
+ oggdec --quiet --raw --output - - < $ogg | sha1sum > oggdec.sha1 &&
+ diff -u filter.sha1 oggdec.sha1
+ "
+done
+test_done
--- /dev/null
+#!/usr/bin/env bash
+
+test_description='Measure time to decode ogg/vorbis files.
+
+Executes para_filter -f oggdec on the test files provided by the
+test suite and fails if it takes much longer than the reference
+implementation.'
+
+. ${0%/*}/test-lib.sh
+
+test_require_objects "oggdec_filter"
+missing_objects="$result"
+
+get_audio_file_paths ogg
+oggs="$result"
+
+test_require_executables "oggdec"
+missing_executables="$result"
+
+for ogg in $oggs; do
+ if [[ -n "$missing_objects" ]]; then
+ test_skip "${ogg##*/}" "missing object(s): $missing_objects"
+ continue
+ fi
+ if [[ -n "$missing_executables" ]]; then
+ test_skip "${ogg##*/}" \
+ "missing executables(s): $missing_executables"
+ continue
+ fi
+ test_expect_success "${ogg##*/}" '
+ test_duration oggdec --quiet --raw --output - - < $ogg &&
+ t1=$result &&
+ test_duration $PARA_FILTER -f oggdec < $ogg &&
+ t2=$result &&
+ echo "oggdec: $t1, para_filter: $t2"
+ (($t2 <= $t1 * 3 / 2 + 100))
+ '
+done
+test_done
--- /dev/null
+#!/usr/bin/env bash
+
+test_description='Check if alsa_init() failures are handled gracefully.
+
+Older parasslash versions contained a bug which caused para_write and para_audiod
+to abort if the alsa/oss device could not be opened. This test makes sure we
+will not introduce the same bug again.'
+
+. ${0%/*}/test-lib.sh
+
+for i in alsa oss; do
+ test_require_objects "${i}_write"
+ missing_objects="$result"
+ if [[ -n "$missing_objects" ]]; then
+ test_skip "$i" "missing object(s): $missing_objects"
+ continue
+ fi
+ test_expect_failure "$i" "
+ head -c 100 /dev/zero | $PARA_WRITE -w '$i -d /dev/non_existent'
+ "
+done
+test_done
--- /dev/null
+#!/bin/bash
+
+# paraslash test suite helper functions
+# Licensed under the GPL v2. For licencing details see COPYING.
+# uses ideas and code from git's test-lib.sh, Copyright (c) 2005 Junio C Hamano
+
+
+get_audio_file_paths()
+{
+ local suffix="$1"
+
+ if (($# == 0)); then
+ result=$(find "$test_audio_file_dir" -type f)
+ else
+ result=$(find "$test_audio_file_dir" -type f -name "*.$suffix")
+ fi
+}
+
+say_color()
+{
+ if [[ "$o_nocolor" != "true" && -n "$1" ]]; then
+ export TERM=$ORIGINAL_TERM
+ case "$1" in
+ error) tput bold; tput setaf 1;;
+ skip) tput setaf 5;;
+ ok)
+ (($o_verbose == 0)) && return
+ tput setaf 2;;
+ pass) tput bold; tput setaf 2;;
+ info) tput setaf 3;;
+ run)
+ (($o_verbose == 0)) && return
+ tput setaf 6;;
+ esac
+ fi
+ shift
+ printf "%s\n" "$*"
+ if [[ "$o_nocolor" != "true" && -n "$1" ]]; then
+ tput sgr0
+ export TERM=dumb
+ fi
+}
+
+die()
+{
+ local code=$?
+ [[ "$exit_ok" == "true" ]] && exit $code
+ say_color error "FATAL: Unexpected exit with code $code"
+ exit 1
+}
+
+error()
+{
+ say_color error "error: $*"
+ exit_ok="true"
+ exit 1
+}
+
+say()
+{
+ say_color info "$*"
+}
+
+retval_ok()
+{
+ local rv="$1" expectation="$2"
+
+ if [[ "$expectation" == "success" ]]; then
+ (($rv == 0)) && return 0 || return 1
+ fi
+ if (($rv > 129 && $rv <= 192)); then
+ echo >&2 "died by signal"
+ return 1
+ fi
+ if (($rv == 127)); then
+ echo >&2 "command not found"
+ return 1
+ fi
+ if (($rv == 0)); then
+ echo >&2 "command was supposed to fail but succeeded"
+ return 1
+ fi
+ return 0
+}
+
+_test_run()
+{
+ local f expectation="$3" ret
+
+ let test_count++
+ eval >&3 2>&4 "$2"
+ ret=$?
+ if retval_ok "$ret" "$expectation"; then
+ let test_success++
+ say_color ok "ok $test_count - $1"
+ return
+ fi
+ let test_failure++
+ say_color error "not ok - $test_count $1"
+ f="$o_results_dir/${0##*/}-$$.out"
+ if [[ -s "$f" ]]; then
+ sed -e 's/^/# /' < "$f"
+ else
+ sed -e 's/^/# /' <<< "$2"
+ fi
+ [[ "$o_immediate" != "true" ]] && return
+ exit_ok="true"
+ exit 1
+}
+
+test_skip()
+{
+ (($# != 2)) && error "bug: not 2 parameters to test_skip()"
+ let test_count++
+ let test_skipped++
+ say_color skip >&3 "skipping test $this_test.$test_count ($1): $2"
+ say_color skip "ok $test_count - $1 # skipped ($2)"
+}
+
+test_require_objects()
+{
+ local o1 o2 found
+
+ result=
+ # if no objects were given, we assume this test is run manually
+ # rather than via "make test". We won't check anything in this case
+ [[ -z "$o_objects" ]] && return
+
+ for o1 in $1; do
+ found=
+ for o2 in $o_objects; do
+ [[ "$o1" != "$o2" ]] && continue
+ found="true"
+ break
+ done
+ [[ "$found" == "true" ]] && continue
+ [[ -n "$result" ]] && result+=" "
+ result+="$o1"
+ done
+ [[ -z "$result" ]]
+}
+
+test_require_executables()
+{
+ local i
+
+ result=
+ for i in "$@"; do
+ [[ -n "$(builtin type -t "$i")" ]] && continue
+ [[ -n "$result" ]] && result+=" "
+ result+="$i"
+ done
+ [[ -z "$result" ]]
+}
+
+test_duration()
+{
+ local t=$(exec 2>&1 1>/dev/null; time -p "$@")
+ result=$(awk '{print $2 * 1000}' <<< $t)
+}
+
+test_expect_success()
+{
+ (($# != 2)) && error "bug: not 2 parameters to test_expect_success()"
+ say >&3 "expecting success: $2"
+ _test_run "$1" "$2" "success"
+ echo >&3 ""
+}
+
+test_expect_failure()
+{
+ (($# != 2)) && error "bug: not 2 parameters to test_expect_failure()"
+ say >&3 "expecting failure: $2"
+ _test_run "$1" "$2" "failure"
+ echo >&3 ""
+}
+
+test_done()
+{
+ test_results_path="$o_results_dir/${0##*/}-$$.counts"
+ {
+ echo "total $test_count"
+ echo "success $test_success"
+ echo "failed $test_failure"
+ echo "skipped $test_skipped"
+ echo
+ } > $test_results_path
+
+ exit_ok="true"
+ msg="$test_count test(s) ($test_skipped test(s) skipped)"
+ if (($test_failure == 0)); then
+ say_color pass "# ${0##*/}: passed all $msg"
+ exit 0
+ else
+ say_color error "# ${0##*/}: failed $test_failure among $msg"
+ exit 1
+ fi
+}
+
+sanitize_environment()
+{
+ export LANG=C
+ export LC_ALL=C
+ export PAGER=cat
+ export TZ=UTC
+ export TERM=dumb
+ export EDITOR=:
+ export HOME=$(pwd)
+
+ unset VISUAL
+ unset EMAIL
+ unset CDPATH
+ unset GREP_OPTIONS
+}
+
+can_use_colors()
+{
+ result="false"
+ [[ "$TERM" == "dumb" ]] && return
+ [[ -t 1 ]] || return
+ tput bold >/dev/null 2>&1 || return
+ tput setaf 1 >/dev/null 2>&1 || return
+ tput sgr0 >/dev/null 2>&1 || return
+ result="true"
+}
+
+parse_options()
+{
+ while (($# > 0)); do
+ case "$1" in
+ -i|--immediate) o_immediate="true"; shift;;
+ -l|--long) export o_long="true"; shift;;
+ -h|--help) o_help="true"; shift;;
+ -v=0|--verbose=0) o_verbose="0"; shift;;
+ -v=1|--verbose=1) o_verbose="1"; shift;;
+ -v|--verbose|-v=2|--verbose=2) o_verbose="2"; shift;;
+ --no-color) o_nocolor="true"; shift;;
+ --results-dir) o_results_dir="$2"; shift; shift;;
+ --trash-dir) o_trash_dir="$2"; shift; shift;;
+ --executables-dir) export o_executables_dir="$2"; shift; shift;;
+ --executables) export o_executables="$2"; shift; shift;;
+ --objects) export o_objects="$2"; shift; shift;;
+ *) echo "error: unknown test option '$1'" >&2; exit 1;;
+ esac
+ done
+ [[ -z "$o_verbose" ]] && o_verbose=1
+}
+
+create_trash_dir_and_cd()
+{
+ local trash="$o_trash_dir/trash-dir.${0##*/}"
+
+ rm -rf "$trash" || error "could not remove trash dir"
+ mkdir -p "$trash" || error "could not make trash dir"
+ cd "$trash" || error "could not change to trash dir"
+}
+
+fixup_dirs()
+{
+ local wd=$(pwd)
+
+ test_dir="$wd/${0%/*}"
+ test_audio_file_dir="$test_dir/audio_files"
+
+ [[ -z "$o_results_dir" ]] && o_results_dir="$test_dir/test-results"
+ [[ -z "$o_executables_dir" ]] && o_executables_dir="$test_dir/.."
+ [[ -z "$o_trash_dir" ]] && o_trash_dir="$test_dir/trashes"
+
+ # we want alsolute paths because relative paths become invalid
+ # after changing to the trash dir
+ [[ -n "${o_results_dir##/*}" ]] && o_results_dir="$wd/$o_results_dir"
+ [[ -n "${o_executables_dir##/*}" ]] && o_executables_dir="$wd/$o_results_dir"
+ [[ -n "${o_trash_dir##/*}" ]] && o_trash_dir="$wd/$o_trash_dir"
+
+ mkdir -p "$o_results_dir"
+}
+
+parse_options "$@"
+if [[ "$o_nocolor" != "true" ]]; then
+ can_use_colors
+ [[ "$result" != "true" ]] && o_nocolor="true"
+fi
+
+# Each test must set test_description
+[[ -z "${test_description}" ]] && error "${0##*/} did not set test_description"
+if [[ "$o_help" == "true" ]]; then
+ printf "${0##*/}: "
+ sed -e '1!d' <<< "$test_description"
+ if (($o_verbose >= 2)); then
+ echo
+ sed -e '1,2d' -e 's/^/ /g' <<<"$test_description"
+ echo
+ fi
+ exit 0
+fi
+fixup_dirs
+
+[[ -z "$o_executables" ]] && o_executables="para_afh para_audioc para_audiod
+ para_client para_fade para_filter para_gui para_recv para_server
+ para_write"
+for exe in $o_executables; do
+ export $(tr 'a-z' 'A-Z' <<< $exe)="$o_executables_dir/$exe"
+done
+
+test_failure=0
+test_count=0
+test_success=0
+test_skipped=0
+
+ORIGINAL_TERM=$TERM
+sanitize_environment
+create_trash_dir_and_cd
+
+if (($o_verbose >= 2)); then
+ exec 4>&2 3>&1
+else
+ exec 4>$o_results_dir/${0##*/}-$$.out 3>&4
+fi
+
+exit_ok=
+trap 'die' EXIT
+
+say_color run "# running ${0##*/}"
const char *buf;
size_t len = vss_get_fec_eof_packet(&buf);
- /* ignore return value, closing the target anyway. */
- (void)write(sc->fd, buf, len);
+ /*
+ * Ignore the return value of wirte() since we are closing the target
+ * anyway. The sole purpose of the "do_nothing" statement is to silence
+ * gcc.
+ */
+ if (write(sc->fd, buf, len))
+ do_nothing;
}
static void udp_delete_target(struct sender_client *sc, const char *msg)
--- /dev/null
+-----BEGIN PGP SIGNATURE-----
+Version: GnuPG v1.4.10 (GNU/Linux)
+
+iEYEABECAAYFAk2UZVsACgkQWto1QDEAkw/XxQCfZ3f3JFstN47qkV1lHLpNC0ij
+woYAmwQrwy6WwMx7fttfxdou6reIrsir
+=bZsC
+-----END PGP SIGNATURE-----
<h1>Events</h1>
<hr>
<ul>
+ <li>2011-03-31: <a href="versions/paraslash-0.4.6.tar.bz2">paraslash-0.4.6</a>
+ <a href="versions/paraslash-0.4.6.tar.bz2.asc">(sig)</a>
+ "deterministic entropy"
+ </li>
<li>2010-12-17: <a href="versions/paraslash-0.4.5.tar.bz2">paraslash-0.4.5</a>
<a href="versions/paraslash-0.4.5.tar.bz2.asc">(sig)</a>
"symmetric randomization"
./configure --help
to see a list of options. If the paraslash package was compiled
-successfully, execute as root,
+successfully, execute (optionally)
+
+ make test
+
+to run the paraslash test suite. If all tests pass, execute as root
make install
s->default_timeout.tv_sec = 10;
s->default_timeout.tv_usec = 50000;
ret = schedule(s);
+ if (ret >= 0) {
+ int j;
+ for (j = 0; j < i; j++) {
+ struct task *t = &wns[j].task;
+ assert(t->error < 0);
+ if (t->error != -E_WRITE_COMMON_EOF
+ && t->error != -E_BTR_EOF) {
+ PARA_ERROR_LOG("%s: %s\n", t->status,
+ para_strerror(-t->error));
+ if (ret >= 0)
+ ret = t->error;
+ }
+ }
+ }
out:
for (i--; i >= 0; i--) {
struct writer_node *wn = wns + i;