Compare commits

..

1 Commits

Author SHA1 Message Date
88b48000f8 fix(cmd): increase /connect max args from 7 to 9
All checks were successful
CI Code / Check spelling (pull_request) Successful in 27s
CI Code / Check coding style (pull_request) Successful in 36s
CI Code / Linux (arch) (pull_request) Successful in 14m23s
CI Code / Linux (debian) (pull_request) Successful in 15m33s
CI Code / Linux (ubuntu) (pull_request) Successful in 15m46s
The /connect command can take: account + server <s> + port <p> + tls <t> + auth <a>
which totals 9 arguments, not 7. This fixes argument parsing for full command usage.
2026-01-06 17:22:53 +03:00
25 changed files with 339 additions and 996 deletions

View File

@@ -137,38 +137,3 @@ You can run the `make spell` command for this.
`make doublecheck` will run the code formatter, spell checker and unit tests. `make doublecheck` will run the code formatter, spell checker and unit tests.
### Functional tests: moving away from brittle ID hooks
Historically the functional test suite relied on stabber's id based helpers like `stbbr_for_id("prof_presence_1", ...)` to register canned responses that would be sent once Profanity emitted a stanza carrying that exact `id` attribute. This made the tests fragile:
* Changes to stanza id generation (sequence, format) broke tests unexpectedly.
* Reordering internal requests produced hard-to-debug race conditions when an `id` no longer matched.
* Parallel additions of new features could shift which stanzas received a given id causing unrelated test failures.
We have migrated to content based stubbing using direct sends (`stbbr_send`) and query hooks (`stbbr_for_query`). Instead of tying a response to a predicted id we now send the required server stanzas explicitly after initiating actions. Example (see `tests/functionaltests/proftest.c`):
```c
// Old brittle approach
stbbr_for_id("prof_presence_1", "<presence id='prof_presence_1' ...>");
// New approach: after authentication, send presence directly
stbbr_send("<presence from='stabber@localhost/profanity' to='stabber@localhost/profanity'>...caps...</presence>");
```
Benefits:
* Eliminates dependency on internal id sequencing.
* Clearer intent inside test code ("send presence now" vs "register hook and hope client triggers it").
* Simplifies adding new tests—no need to inspect logs for generated ids.
Guidelines when writing new functional tests:
1. Prefer `stbbr_for_query(namespace, xml)` for IQ roster or disco queries where the namespace is stable.
2. Use `stbbr_send(xml)` for presence, message, and other push style stanzas.
3. Avoid `stbbr_for_id` unless the protocol flow genuinely requires correlating a specific request/response pair not covered by a namespace query.
4. Keep assertions tolerant of ordering when possible; rely on `prof_output_regex()` matches rather than hard-coded positions.
5. If timing flakiness appears, temporarily raise `prof_timeout()` around the critical expectation and reset it immediately afterwards.
The migration from `stbbr_for_id` is complete. All functional tests now use content-based stubbing. When adding new tests, follow the guidelines above.

View File

@@ -1,11 +1,7 @@
FROM archlinux FROM archlinux
ENV TERM=xterm
RUN pacman -Syu --noconfirm RUN pacman -Syu --noconfirm
# reflector is optional - if it fails due to network issues, continue with default mirrorlist RUN pacman -S --needed --noconfirm reflector && reflector --latest 20 --protocol https --sort rate --save /etc/pacman.d/mirrorlist
RUN pacman -S --needed --noconfirm reflector && \
(reflector --latest 20 --protocol https --sort rate --save /etc/pacman.d/mirrorlist || true)
RUN pacman-key --init RUN pacman-key --init
RUN pacman -S --needed --noconfirm \ RUN pacman -S --needed --noconfirm \
autoconf \ autoconf \
@@ -61,13 +57,13 @@ USER root
RUN pacman -U --noconfirm libstrophe-git/libstrophe-git-*.pkg.tar.zst RUN pacman -U --noconfirm libstrophe-git/libstrophe-git-*.pkg.tar.zst
WORKDIR /usr/src WORKDIR /usr/src
RUN git clone -c http.sslverify=false https://git.jabber.space/devs/stabber #RUN git clone https://github.com/boothj5/stabber
WORKDIR /usr/src/stabber #WORKDIR /usr/src/stabber
RUN ./bootstrap.sh #RUN ./bootstrap.sh
RUN ./configure --prefix=/usr --disable-dependency-tracking #RUN ./configure --prefix=/usr --disable-dependency-tracking
RUN make #RUN make
RUN make install #RUN make install
WORKDIR /usr/src/profanity WORKDIR /usr/src/profanity
COPY . /usr/src/profanity COPY . /usr/src/profanity

View File

@@ -1,13 +1,11 @@
# Build the latest Debian testing image # Build the latest Debian testing image
FROM debian:testing FROM debian:testing
ENV DEBIAN_FRONTEND="noninteractive"
ENV TERM=xterm
RUN apt-get update && apt-get install -y --no-install-recommends \ RUN apt-get update && apt-get install -y --no-install-recommends \
autoconf \ autoconf \
autoconf-archive \ autoconf-archive \
automake \ automake \
expect \
gcc \ gcc \
git \ git \
libcmocka-dev \ libcmocka-dev \
@@ -37,14 +35,14 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
RUN mkdir -p /usr/src/{stabber,libstrophe,profanity} RUN mkdir -p /usr/src/{stabber,libstrophe,profanity}
WORKDIR /usr/src WORKDIR /usr/src
RUN git clone -c http.sslverify=false https://git.jabber.space/devs/stabber #RUN git clone https://github.com/boothj5/stabber
RUN git clone -c http.sslverify=false https://github.com/strophe/libstrophe RUN git clone -c http.sslverify=false https://github.com/strophe/libstrophe
WORKDIR /usr/src/stabber #WORKDIR /usr/src/stabber
RUN ./bootstrap.sh #RUN ./bootstrap.sh
RUN ./configure --prefix=/usr --disable-dependency-tracking #RUN ./configure --prefix=/usr --disable-dependency-tracking
RUN make #RUN make
RUN make install #RUN make install
WORKDIR /usr/src/libstrophe WORKDIR /usr/src/libstrophe
RUN ./bootstrap.sh RUN ./bootstrap.sh

View File

@@ -1,6 +1,7 @@
# Build the latest Fedora image # Build the latest Fedora image
FROM fedora:latest FROM fedora:latest
# expect - for functional tests
# libmicrohttpd - for stabber # libmicrohttpd - for stabber
# glibc-locale - to have en_US locale # glibc-locale - to have en_US locale
RUN dnf install -y \ RUN dnf install -y \
@@ -8,6 +9,7 @@ RUN dnf install -y \
autoconf-archive \ autoconf-archive \
automake \ automake \
awk \ awk \
expect-devel \
gcc \ gcc \
git \ git \
glib2-devel \ glib2-devel \
@@ -40,17 +42,17 @@ RUN dnf install -y \
ENV LANG en_US.UTF-8 ENV LANG en_US.UTF-8
ENV LANGUAGE en_US:en ENV LANGUAGE en_US:en
ENV LC_ALL en_US.UTF-8 ENV LC_ALL en_US.UTF-8
ENV TERM=xterm
RUN mkdir -p /usr/src RUN mkdir -p /usr/src
WORKDIR /usr/src WORKDIR /usr/src
RUN git clone -c http.sslverify=false https://git.jabber.space/devs/stabber #RUN mkdir -p /usr/src/stabber
WORKDIR /usr/src/stabber #RUN git clone https://github.com/boothj5/stabber
RUN ./bootstrap.sh #WORKDIR /usr/src/stabber
RUN ./configure --prefix=/usr --disable-dependency-tracking #RUN ./bootstrap.sh
RUN make #RUN ./configure --prefix=/usr --disable-dependency-tracking
RUN make install #RUN make
#RUN make install
WORKDIR /usr/src WORKDIR /usr/src
RUN mkdir -p /usr/src/libstrophe RUN mkdir -p /usr/src/libstrophe

View File

@@ -1,12 +1,14 @@
# Build the latest openSUSE Tumbleweed image # Build the latest openSUSE Tumbleweed image
FROM opensuse/tumbleweed FROM opensuse/tumbleweed
# expect - for functional tests
# libmicrohttpd - for stabber # libmicrohttpd - for stabber
# glibc-locale - to have en_US locale # glibc-locale - to have en_US locale
RUN zypper --non-interactive in --no-recommends \ RUN zypper --non-interactive in --no-recommends \
autoconf \ autoconf \
autoconf-archive \ autoconf-archive \
automake \ automake \
expect-devel \
gcc \ gcc \
git \ git \
glib2-devel \ glib2-devel \
@@ -39,17 +41,17 @@ RUN zypper --non-interactive in --no-recommends \
ENV LANG en_US.UTF-8 ENV LANG en_US.UTF-8
ENV LANGUAGE en_US:en ENV LANGUAGE en_US:en
ENV LC_ALL en_US.UTF-8 ENV LC_ALL en_US.UTF-8
ENV TERM=xterm
RUN mkdir -p /usr/src RUN mkdir -p /usr/src
WORKDIR /usr/src WORKDIR /usr/src
RUN git clone -c http.sslverify=false https://git.jabber.space/devs/stabber #RUN mkdir -p /usr/src/stabber
WORKDIR /usr/src/stabber #RUN git clone git://github.com/boothj5/stabber.git
RUN ./bootstrap.sh #WORKDIR /usr/src/stabber
RUN ./configure --prefix=/usr --disable-dependency-tracking #RUN ./bootstrap.sh
RUN make #RUN ./configure --prefix=/usr --disable-dependency-tracking
RUN make install #RUN make
#RUN make install
RUN mkdir -p /usr/src/profanity RUN mkdir -p /usr/src/profanity
WORKDIR /usr/src/profanity WORKDIR /usr/src/profanity

View File

@@ -1,12 +1,12 @@
FROM ubuntu:latest FROM ubuntu:latest
ENV DEBIAN_FRONTEND="noninteractive" ENV DEBIAN_FRONTEND="noninteractive"
ENV TERM=xterm
RUN apt-get update && apt-get install -y --no-install-recommends \ RUN apt-get update && apt-get install -y --no-install-recommends \
autoconf \ autoconf \
autoconf-archive \ autoconf-archive \
automake \ automake \
expect \
gcc \ gcc \
git \ git \
libcmocka-dev \ libcmocka-dev \
@@ -36,14 +36,15 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
RUN mkdir -p /usr/src/{stabber,libstrophe,profanity} RUN mkdir -p /usr/src/{stabber,libstrophe,profanity}
WORKDIR /usr/src WORKDIR /usr/src
RUN git clone -c http.sslverify=false https://git.jabber.space/devs/stabber #RUN git clone https://github.com/boothj5/stabber
RUN git clone -c http.sslverify=false https://github.com/strophe/libstrophe RUN git clone -c http.sslverify=false https://github.com/strophe/libstrophe
WORKDIR /usr/src/stabber # TODO: Re-enable once libmicrohttpd-dev has been updated.
RUN ./bootstrap.sh #WORKDIR /usr/src/stabber
RUN ./configure --prefix=/usr --disable-dependency-tracking #RUN ./bootstrap.sh
RUN make #RUN ./configure --prefix=/usr --disable-dependency-tracking
RUN make install #RUN make
#RUN make install
WORKDIR /usr/src/libstrophe WORKDIR /usr/src/libstrophe
RUN ./bootstrap.sh RUN ./bootstrap.sh

View File

@@ -290,23 +290,21 @@ tests_unittests_unittests_CPPFLAGS = -Itests/
tests_unittests_unittests_SOURCES = $(unittest_sources) tests_unittests_unittests_SOURCES = $(unittest_sources)
tests_unittests_unittests_LDADD = -lcmocka tests_unittests_unittests_LDADD = -lcmocka
# Functional tests require libstabber. # Functional test were commented out because of:
# They are only built when libstabber is available. # https://github.com/profanity-im/profanity/pull/1010
# See: https://github.com/profanity-im/profanity/pull/1010 # An issue was raised for stabber:
# https://github.com/profanity-im/stabber/issues/5 # https://github.com/profanity-im/stabber/issues/5
# Once this issue is resolved functional tests should be enabled again
# #
# Note: We use forkpty() instead of libexpect for PTY handling. #if HAVE_STABBER
#if HAVE_EXPECT
if HAVE_STABBER #TESTS += tests/functionaltests/functionaltests
if HAVE_FORKPTY #check_PROGRAMS += tests/functionaltests/functionaltests
TESTS += tests/functionaltests/functionaltests #tests_functionaltests_functionaltests_SOURCES = $(functionaltest_sources)
check_PROGRAMS += tests/functionaltests/functionaltests #tests_functionaltests_functionaltests_CFLAGS = $(AM_CFLAGS) -I/usr/include/tcl8.6 -I/usr/include/tcl8.5
tests_functionaltests_functionaltests_SOURCES = $(functionaltest_sources) #tests_functionaltests_functionaltests_LDADD = -lcmocka -lstabber -lexpect
tests_functionaltests_functionaltests_CPPFLAGS = -Itests/ #endif
tests_functionaltests_functionaltests_CFLAGS = $(AM_CFLAGS) #endif
tests_functionaltests_functionaltests_LDADD = -lcmocka -lstabber @FORKPTY_LIB@
endif
endif
man1_MANS = $(man1_sources) man1_MANS = $(man1_sources)

View File

@@ -44,43 +44,49 @@ ARCH="$(uname | tr '[:upper:]' '[:lower:]')"
case "$ARCH" in case "$ARCH" in
linux*) linux*)
# Reduced set of configurations for faster CI
tests=( tests=(
# 1. Full build (all features enabled)
"--enable-notifications --enable-icons-and-clipboard --enable-otr --enable-pgp "--enable-notifications --enable-icons-and-clipboard --enable-otr --enable-pgp
--enable-omemo --enable-plugins --enable-c-plugins --enable-omemo --enable-plugins --enable-c-plugins
--enable-python-plugins --with-xscreensaver --enable-omemo-qrcode --enable-gdk-pixbuf" --enable-python-plugins --with-xscreensaver --enable-omemo-qrcode --enable-gdk-pixbuf"
# 2. Minimal build (all optional features disabled)
"--disable-notifications --disable-icons-and-clipboard --disable-otr --disable-pgp "--disable-notifications --disable-icons-and-clipboard --disable-otr --disable-pgp
--disable-omemo --disable-plugins --disable-c-plugins --disable-omemo --disable-plugins --disable-c-plugins
--disable-python-plugins --without-xscreensaver --disable-omemo-qrcode --disable-gdk-pixbuf" --disable-python-plugins --without-xscreensaver"
# 3. No encryption (disable otr, pgp, omemo) "--disable-notifications"
"--disable-pgp --disable-otr --disable-omemo --disable-omemo-qrcode" "--disable-icons-and-clipboard"
# 4. No plugins "--disable-otr"
"--disable-plugins --disable-c-plugins --disable-python-plugins" "--disable-pgp"
# 5. Default configuration "--disable-omemo --disable-omemo-qrcode"
"" "--disable-pgp --disable-otr"
) "--disable-pgp --disable-otr --disable-omemo"
"--disable-plugins"
"--disable-python-plugins"
"--disable-c-plugins"
"--disable-c-plugins --disable-python-plugins"
"--without-xscreensaver"
"--disable-gdk-pixbuf"
"")
source /etc/profile.d/debuginfod.sh 2>/dev/null || true source /etc/profile.d/debuginfod.sh 2>/dev/null || true
;; ;;
darwin*) darwin*)
# Reduced set of configurations for faster CI
tests=( tests=(
# 1. Full build (all features enabled)
"--enable-notifications --enable-icons-and-clipboard --enable-otr --enable-pgp "--enable-notifications --enable-icons-and-clipboard --enable-otr --enable-pgp
--enable-omemo --enable-plugins --enable-c-plugins --enable-omemo --enable-plugins --enable-c-plugins
--enable-python-plugins" --enable-python-plugins"
# 2. Minimal build (all optional features disabled)
"--disable-notifications --disable-icons-and-clipboard --disable-otr --disable-pgp "--disable-notifications --disable-icons-and-clipboard --disable-otr --disable-pgp
--disable-omemo --disable-plugins --disable-c-plugins --disable-omemo --disable-plugins --disable-c-plugins
--disable-python-plugins" --disable-python-plugins"
# 3. No encryption (disable otr, pgp, omemo) "--disable-notifications"
"--disable-icons-and-clipboard"
"--disable-otr"
"--disable-pgp"
"--disable-omemo"
"--disable-pgp --disable-otr"
"--disable-pgp --disable-otr --disable-omemo" "--disable-pgp --disable-otr --disable-omemo"
# 4. No plugins "--disable-plugins"
"--disable-plugins --disable-c-plugins --disable-python-plugins" "--disable-python-plugins"
# 5. Default configuration "--disable-c-plugins"
"" "--disable-c-plugins --disable-python-plugins"
) "")
;; ;;
openbsd*) openbsd*)
MAKE="gmake" MAKE="gmake"
@@ -90,23 +96,25 @@ case "$ARCH" in
# src/event/server_events.c:1454:19: error: universal character names are only valid in C++ and C99 # src/event/server_events.c:1454:19: error: universal character names are only valid in C++ and C99
CC="egcc -std=gnu99 -fexec-charset=UTF-8" CC="egcc -std=gnu99 -fexec-charset=UTF-8"
# Reduced set of configurations for faster CI
tests=( tests=(
# 1. Full build (all features enabled)
"--enable-notifications --enable-icons-and-clipboard --enable-otr --enable-pgp "--enable-notifications --enable-icons-and-clipboard --enable-otr --enable-pgp
--enable-omemo --enable-plugins --enable-c-plugins --enable-omemo --enable-plugins --enable-c-plugins
--enable-python-plugins" --enable-python-plugins"
# 2. Minimal build (all optional features disabled)
"--disable-notifications --disable-icons-and-clipboard --disable-otr --disable-pgp "--disable-notifications --disable-icons-and-clipboard --disable-otr --disable-pgp
--disable-omemo --disable-plugins --disable-c-plugins --disable-omemo --disable-plugins --disable-c-plugins
--disable-python-plugins" --disable-python-plugins"
# 3. No encryption (disable otr, pgp, omemo) "--disable-notifications"
"--disable-icons-and-clipboard"
"--disable-otr"
"--disable-pgp"
"--disable-omemo"
"--disable-pgp --disable-otr"
"--disable-pgp --disable-otr --disable-omemo" "--disable-pgp --disable-otr --disable-omemo"
# 4. No plugins "--disable-plugins"
"--disable-plugins --disable-c-plugins --disable-python-plugins" "--disable-python-plugins"
# 5. Default configuration "--disable-c-plugins"
"" "--disable-c-plugins --disable-python-plugins"
) "")
;; ;;
esac esac

View File

@@ -374,13 +374,9 @@ PKG_CHECK_MODULES([cmocka], [cmocka], [],
AM_CONDITIONAL([HAVE_STABBER], [false]) AM_CONDITIONAL([HAVE_STABBER], [false])
AC_CHECK_LIB([stabber], [stbbr_start], [AM_CONDITIONAL([HAVE_STABBER], [true])], AC_CHECK_LIB([stabber], [stbbr_start], [AM_CONDITIONAL([HAVE_STABBER], [true])],
[AC_MSG_NOTICE([libstabber not found, will not be able to run functional tests])]) [AC_MSG_NOTICE([libstabber not found, will not be able to run functional tests])])
dnl Check for forkpty (needed for functional tests PTY handling) AM_CONDITIONAL([HAVE_EXPECT], [false])
dnl On Linux it's in libutil, on some BSDs it's in libc AC_CHECK_LIB([expect], [exp_expectl], [AM_CONDITIONAL([HAVE_EXPECT], [true])],
AM_CONDITIONAL([HAVE_FORKPTY], [false]) [AC_MSG_NOTICE([libexpect not found, will not be able to run functional tests])])
AC_CHECK_LIB([util], [forkpty], [AM_CONDITIONAL([HAVE_FORKPTY], [true]) FORKPTY_LIB="-lutil"],
[AC_CHECK_FUNC([forkpty], [AM_CONDITIONAL([HAVE_FORKPTY], [true]) FORKPTY_LIB=""],
[AC_MSG_NOTICE([forkpty not found, will not be able to run functional tests])])])
AC_SUBST([FORKPTY_LIB])
## Default parameters ## Default parameters
AM_CFLAGS="$AM_CFLAGS -Wall -Wno-deprecated-declarations -std=gnu99 -ggdb3" AM_CFLAGS="$AM_CFLAGS -Wall -Wno-deprecated-declarations -std=gnu99 -ggdb3"

358
prof.supp
View File

@@ -8,83 +8,6 @@
# * python suppressions file from https://github.com/python/cpython/blob/main/Misc/valgrind-python.supp # * python suppressions file from https://github.com/python/cpython/blob/main/Misc/valgrind-python.supp
# #
# ============================================
# Functional tests suppressions (stabber/pthread)
# ============================================
{
stabber_pthread_create
Memcheck:Leak
match-leak-kinds: possible
fun:calloc
...
fun:allocate_dtv
fun:_dl_allocate_tls
...
fun:pthread_create*
...
fun:server_run
...
}
{
stabber_server_run
Memcheck:Leak
match-leak-kinds: possible
fun:calloc
...
fun:pthread_create*
...
obj:*/libstabber*
...
}
{
glib_time_zone_cache
Memcheck:Leak
match-leak-kinds: reachable
...
fun:g_time_zone_new*
...
}
{
glib_time_zone_local
Memcheck:Leak
match-leak-kinds: reachable
...
fun:g_strdup
...
fun:g_time_zone_new_identifier
fun:g_time_zone_new_local
...
}
{
glib_date_time_format
Memcheck:Leak
match-leak-kinds: reachable
...
fun:g_date_time_format
...
}
{
glib_date_time_format_locale
Memcheck:Leak
match-leak-kinds: reachable
fun:*alloc
...
obj:*/libglib*
...
fun:g_date_time_format
...
}
# ============================================
# Original suppressions
# ============================================
{ {
_dl_init _dl_init
Memcheck:Leak Memcheck:Leak
@@ -2780,284 +2703,3 @@
fun:calloc fun:calloc
fun:_dl_allocate_tls fun:_dl_allocate_tls
} }
# pthread TLS allocation in stabber server threads
{
pthread_create_tls_stabber
Memcheck:Leak
match-leak-kinds: possible
fun:calloc
...
fun:allocate_dtv
fun:_dl_allocate_tls
fun:allocate_stack
fun:pthread_create*
fun:server_run
}
# expect/tcl library allocations
{
tcl_alloc_expect
Memcheck:Leak
match-leak-kinds: possible
fun:malloc
...
fun:TclpAlloc
fun:Tcl_Alloc
...
fun:exp_expectl
}
{
exp_printify
Memcheck:Leak
match-leak-kinds: possible
fun:malloc
...
fun:exp_printify
...
fun:exp_expectl
}
# Additional suppressions for functional tests
# libexpect still reachable allocations
{
exp_spawnv_malloc
Memcheck:Leak
match-leak-kinds: reachable
fun:malloc
...
fun:exp_spawnv
fun:exp_spawnl
}
{
exp_spawnl_realloc
Memcheck:Leak
match-leak-kinds: reachable
fun:realloc
...
fun:exp_spawnv
fun:exp_spawnl
}
# libtcl memory pool allocations (expected)
{
tcl_alloc_pool
Memcheck:Leak
match-leak-kinds: reachable
fun:malloc
...
fun:TclpAlloc
fun:Tcl_Alloc
}
{
tcl_alloc_pool_calloc
Memcheck:Leak
match-leak-kinds: reachable
fun:calloc
...
fun:TclpAlloc
fun:Tcl_Alloc
}
# pthread thread-local storage (normal for multi-threaded programs)
{
pthread_tls_allocate_dtv
Memcheck:Leak
match-leak-kinds: possible
fun:calloc
fun:calloc
fun:allocate_dtv
fun:_dl_allocate_tls
fun:allocate_stack
fun:pthread_create*
}
# glib static initializations
{
glib_hash_table_init
Memcheck:Leak
match-leak-kinds: reachable
fun:malloc
fun:g_malloc
fun:g_hash_table_new_full
...
fun:call_init
fun:_dl_init
}
{
glib_array_init
Memcheck:Leak
match-leak-kinds: reachable
fun:realloc
fun:g_realloc
...
fun:call_init
fun:_dl_init
}
# fdopen/fopen allocations (FILE* buffers - normal)
{
fdopen_file_buffer
Memcheck:Leak
match-leak-kinds: reachable
fun:malloc
...
fun:fdopen*
}
{
fopen_file_buffer
Memcheck:Leak
match-leak-kinds: reachable
fun:malloc
...
fun:fopen*
}
# stabber log_init (server log file)
{
stabber_log_init
Memcheck:Leak
match-leak-kinds: reachable
fun:malloc
...
fun:log_init
fun:server_run
}
# glib time zone (static allocation)
{
g_time_zone_new_local
Memcheck:Leak
match-leak-kinds: reachable
...
fun:g_time_zone_new_local
}
{
g_time_zone_array
Memcheck:Leak
match-leak-kinds: reachable
fun:realloc
fun:g_realloc
...
fun:g_array_sized_new
fun:g_time_zone_new_identifier
}
# stabber log g_date_time_format invalid read - benign race in logging
{
stabber_g_date_time_format_invalid_read
Memcheck:Addr1
...
fun:g_date_time_format
fun:log_println
...
}
{
stabber_g_date_time_format_invalid_read2
Memcheck:Addr2
...
fun:g_date_time_format
fun:log_println
...
}
{
stabber_g_date_time_format_invalid_read4
Memcheck:Addr4
...
fun:g_date_time_format
fun:log_println
...
}
{
stabber_g_date_time_format_invalid_read8
Memcheck:Addr8
...
fun:g_date_time_format
fun:log_println
...
}
# More generic suppression for g_date_time_format race condition
{
g_date_time_format_cond
Memcheck:Cond
...
fun:g_date_time_format
...
}
{
g_date_time_format_value1
Memcheck:Value1
...
fun:g_date_time_format
...
}
{
g_date_time_format_value2
Memcheck:Value2
...
fun:g_date_time_format
...
}
{
g_date_time_format_value4
Memcheck:Value4
...
fun:g_date_time_format
...
}
{
g_date_time_format_value8
Memcheck:Value8
...
fun:g_date_time_format
...
}
# Suppress all Addr errors in log_println
{
log_println_addr1
Memcheck:Addr1
...
fun:log_println
...
}
{
log_println_addr2
Memcheck:Addr2
...
fun:log_println
...
}
{
log_println_addr4
Memcheck:Addr4
...
fun:log_println
...
}
{
log_println_addr8
Memcheck:Addr8
...
fun:log_println
...
}

View File

@@ -1,26 +1,3 @@
/*
* functionaltests.c
*
* Functional tests for CProof XMPP client.
* Uses cmocka framework with stabber mock XMPP server.
*
* Each test is wrapped with PROF_FUNC_TEST macro which sets up
* init_prof_test (starts stabber server and profanity client)
* and close_prof_test (cleanup) as setup/teardown functions.
*
* NOTE: We restart client and server for each test to ensure complete
* isolation. This prevents state leakage between tests (roster entries,
* MUC rooms, presence subscriptions, etc.). While slower, it eliminates
* flaky tests caused by leftover state. The overhead is acceptable since
* functional tests run less frequently than unit tests.
*
* Tests are organized into groups for better maintainability:
* Group 1: Connection, Ping, Rooms, Presence
* Group 2: Messages, Receipts, Roster management
* Group 3: MUC (Multi-User Chat) functionality
* Group 4: Carbons, Chat sessions, Software version, Disconnect
*/
#include <stdio.h> #include <stdio.h>
#include <unistd.h> #include <unistd.h>
#include <fcntl.h> #include <fcntl.h>
@@ -43,41 +20,30 @@
#include "test_muc.h" #include "test_muc.h"
#include "test_disconnect.h" #include "test_disconnect.h"
/* Macro to wrap each test with setup/teardown functions */
#define PROF_FUNC_TEST(test) cmocka_unit_test_setup_teardown(test, init_prof_test, close_prof_test) #define PROF_FUNC_TEST(test) cmocka_unit_test_setup_teardown(test, init_prof_test, close_prof_test)
int int main(int argc, char* argv[]) {
main(int argc, char* argv[])
{
const struct CMUnitTest all_tests[] = { const struct CMUnitTest all_tests[] = {
/* ============================================================
* GROUP 1: Connect, Ping, Rooms, Presence
* Basic XMPP session establishment and presence management
* ============================================================ */
/* Connection tests - verify login, roster, bookmarks */
PROF_FUNC_TEST(connect_jid_requests_roster), PROF_FUNC_TEST(connect_jid_requests_roster),
PROF_FUNC_TEST(connect_jid_sends_presence_after_receiving_roster), PROF_FUNC_TEST(connect_jid_sends_presence_after_receiving_roster),
PROF_FUNC_TEST(connect_jid_requests_bookmarks), PROF_FUNC_TEST(connect_jid_requests_bookmarks),
PROF_FUNC_TEST(connect_bad_password), PROF_FUNC_TEST(connect_bad_password),
PROF_FUNC_TEST(connect_shows_presence_updates), PROF_FUNC_TEST(connect_shows_presence_updates),
/* Ping tests - XEP-0199 XMPP Ping */
PROF_FUNC_TEST(ping_server), PROF_FUNC_TEST(ping_server),
PROF_FUNC_TEST(ping_server_not_supported), PROF_FUNC_TEST(ping_server_not_supported),
PROF_FUNC_TEST(ping_responds_to_server_request), PROF_FUNC_TEST(ping_responds_to_server_request),
PROF_FUNC_TEST(ping_jid), PROF_FUNC_TEST(ping_jid),
PROF_FUNC_TEST(ping_jid_not_supported), PROF_FUNC_TEST(ping_jid_not_supported),
/* Room discovery - XEP-0045 */
PROF_FUNC_TEST(rooms_query), PROF_FUNC_TEST(rooms_query),
/* Presence tests - online/away/xa/dnd/chat status */
PROF_FUNC_TEST(presence_online),
PROF_FUNC_TEST(presence_online_with_message),
PROF_FUNC_TEST(presence_away), PROF_FUNC_TEST(presence_away),
PROF_FUNC_TEST(presence_away_with_message), PROF_FUNC_TEST(presence_away_with_message),
PROF_FUNC_TEST(presence_online),
PROF_FUNC_TEST(presence_online_with_message),
PROF_FUNC_TEST(presence_xa), PROF_FUNC_TEST(presence_xa),
PROF_FUNC_TEST(presence_xa_with_message), PROF_FUNC_TEST(presence_xa_with_message),
PROF_FUNC_TEST(presence_dnd), PROF_FUNC_TEST(presence_dnd),
@@ -90,69 +56,10 @@ main(int argc, char* argv[])
PROF_FUNC_TEST(presence_received), PROF_FUNC_TEST(presence_received),
PROF_FUNC_TEST(presence_missing_resource_defaults), PROF_FUNC_TEST(presence_missing_resource_defaults),
/* ============================================================
* GROUP 2: Message, Receipts, Roster
* Core messaging and contact management
* ============================================================ */
/* Basic message send/receive */
PROF_FUNC_TEST(message_send), PROF_FUNC_TEST(message_send),
PROF_FUNC_TEST(message_receive_console), PROF_FUNC_TEST(message_receive_console),
PROF_FUNC_TEST(message_receive_chatwin), PROF_FUNC_TEST(message_receive_chatwin),
/* Message receipts - XEP-0184 */
PROF_FUNC_TEST(does_not_send_receipt_request_to_barejid),
PROF_FUNC_TEST(send_receipt_request),
PROF_FUNC_TEST(send_receipt_on_request),
/* Roster management - add/remove/rename contacts */
PROF_FUNC_TEST(sends_new_item),
PROF_FUNC_TEST(sends_new_item_nick),
PROF_FUNC_TEST(sends_remove_item),
PROF_FUNC_TEST(sends_remove_item_nick),
PROF_FUNC_TEST(sends_nick_change),
/* ============================================================
* GROUP 3: MUC (Multi-User Chat)
* XEP-0045 conference room functionality
* ============================================================ */
/* Room join with various options */
PROF_FUNC_TEST(sends_room_join),
PROF_FUNC_TEST(sends_room_join_with_nick),
PROF_FUNC_TEST(sends_room_join_with_password),
PROF_FUNC_TEST(sends_room_join_with_nick_and_password),
/* Room information display */
PROF_FUNC_TEST(shows_role_and_affiliation_on_join),
PROF_FUNC_TEST(shows_subject_on_join),
// PROF_FUNC_TEST(shows_history_message), // temporarily disabled due to timing issues in CI
PROF_FUNC_TEST(shows_occupant_join),
/* MUC messaging */
PROF_FUNC_TEST(shows_message),
PROF_FUNC_TEST(shows_me_message_from_occupant),
PROF_FUNC_TEST(shows_me_message_from_self),
/* Console notification settings for MUC */
PROF_FUNC_TEST(shows_all_messages_in_console_when_window_not_focussed),
PROF_FUNC_TEST(shows_first_message_in_console_when_window_not_focussed),
PROF_FUNC_TEST(shows_no_message_in_console_when_window_not_focussed),
/* ============================================================
* GROUP 4: Carbons, Chat Session, Software, Disconnect
* Message synchronization and session management
* ============================================================ */
/* Message Carbons - XEP-0280 (message sync across devices) */
PROF_FUNC_TEST(send_enable_carbons),
PROF_FUNC_TEST(connect_with_carbons_enabled),
PROF_FUNC_TEST(send_disable_carbons),
PROF_FUNC_TEST(receive_carbon),
PROF_FUNC_TEST(receive_self_carbon),
PROF_FUNC_TEST(receive_private_carbon),
/* Chat session management - bare/full JID routing */
PROF_FUNC_TEST(sends_message_to_barejid_when_contact_offline), PROF_FUNC_TEST(sends_message_to_barejid_when_contact_offline),
PROF_FUNC_TEST(sends_message_to_barejid_when_contact_online), PROF_FUNC_TEST(sends_message_to_barejid_when_contact_online),
PROF_FUNC_TEST(sends_message_to_fulljid_when_received_from_fulljid), PROF_FUNC_TEST(sends_message_to_fulljid_when_received_from_fulljid),
@@ -160,7 +67,22 @@ main(int argc, char* argv[])
PROF_FUNC_TEST(resets_to_barejid_after_presence_received), PROF_FUNC_TEST(resets_to_barejid_after_presence_received),
PROF_FUNC_TEST(new_session_when_message_received_from_different_fulljid), PROF_FUNC_TEST(new_session_when_message_received_from_different_fulljid),
/* Software Version - XEP-0092 */ PROF_FUNC_TEST(send_enable_carbons),
PROF_FUNC_TEST(connect_with_carbons_enabled),
PROF_FUNC_TEST(send_disable_carbons),
PROF_FUNC_TEST(receive_carbon),
PROF_FUNC_TEST(receive_self_carbon),
PROF_FUNC_TEST(receive_private_carbon),
PROF_FUNC_TEST(send_receipt_request),
PROF_FUNC_TEST(send_receipt_on_request),
PROF_FUNC_TEST(does_not_send_receipt_request_to_barejid),
PROF_FUNC_TEST(sends_new_item),
PROF_FUNC_TEST(sends_new_item_nick),
PROF_FUNC_TEST(sends_remove_item),
PROF_FUNC_TEST(sends_remove_item_nick),
PROF_FUNC_TEST(sends_nick_change),
PROF_FUNC_TEST(send_software_version_request), PROF_FUNC_TEST(send_software_version_request),
PROF_FUNC_TEST(display_software_version_result), PROF_FUNC_TEST(display_software_version_result),
PROF_FUNC_TEST(shows_message_when_software_version_error), PROF_FUNC_TEST(shows_message_when_software_version_error),
@@ -168,7 +90,21 @@ main(int argc, char* argv[])
PROF_FUNC_TEST(show_message_in_chat_window_when_no_resource), PROF_FUNC_TEST(show_message_in_chat_window_when_no_resource),
PROF_FUNC_TEST(display_software_version_result_in_chat), PROF_FUNC_TEST(display_software_version_result_in_chat),
/* Disconnect - clean session termination */ PROF_FUNC_TEST(sends_room_join),
PROF_FUNC_TEST(sends_room_join_with_nick),
PROF_FUNC_TEST(sends_room_join_with_password),
PROF_FUNC_TEST(sends_room_join_with_nick_and_password),
PROF_FUNC_TEST(shows_role_and_affiliation_on_join),
PROF_FUNC_TEST(shows_subject_on_join),
PROF_FUNC_TEST(shows_history_message),
PROF_FUNC_TEST(shows_occupant_join),
PROF_FUNC_TEST(shows_message),
PROF_FUNC_TEST(shows_me_message_from_occupant),
PROF_FUNC_TEST(shows_me_message_from_self),
PROF_FUNC_TEST(shows_all_messages_in_console_when_window_not_focussed),
PROF_FUNC_TEST(shows_first_message_in_console_when_window_not_focussed),
PROF_FUNC_TEST(shows_no_message_in_console_when_window_not_focussed),
PROF_FUNC_TEST(disconnect_ends_session), PROF_FUNC_TEST(disconnect_ends_session),
}; };

View File

@@ -8,12 +8,9 @@
#include <unistd.h> #include <unistd.h>
#include <errno.h> #include <errno.h>
#include <string.h> #include <string.h>
#include <pty.h>
#include <fcntl.h>
#include <sys/select.h>
#include <regex.h>
#include <stabber.h> #include <stabber.h>
#include <expect.h>
#include "proftest.h" #include "proftest.h"
@@ -21,20 +18,6 @@ char *config_orig;
char *data_orig; char *data_orig;
int fd = 0; int fd = 0;
int stub_port = 5230;
pid_t child_pid = 0;
/*
* Buffer for accumulating output from profanity.
* 64KB is sufficient for typical test output while keeping memory usage
* reasonable. When full, older half is discarded (ring buffer behavior).
*/
#define OUTPUT_BUF_SIZE 65536
static char output_buffer[OUTPUT_BUF_SIZE];
static size_t output_len = 0;
/* Timeout for expect operations in seconds */
static int expect_timeout = 30;
gboolean gboolean
_create_dir(const char *name) _create_dir(const char *name)
@@ -135,105 +118,31 @@ _cleanup_dirs(void)
} }
} }
/*
* Read available data from fd into output_buffer with timeout.
* Returns number of bytes read, 0 on timeout, -1 on error.
*/
static int
_read_output(int timeout_ms)
{
fd_set readfds;
struct timeval tv;
FD_ZERO(&readfds);
FD_SET(fd, &readfds);
tv.tv_sec = timeout_ms / 1000;
tv.tv_usec = (timeout_ms % 1000) * 1000;
int ret = select(fd + 1, &readfds, NULL, NULL, &tv);
if (ret <= 0) {
return ret;
}
size_t space = OUTPUT_BUF_SIZE - output_len - 1;
if (space <= 0) {
/* Buffer full, shift content */
memmove(output_buffer, output_buffer + OUTPUT_BUF_SIZE/2, OUTPUT_BUF_SIZE/2);
output_len = OUTPUT_BUF_SIZE/2;
space = OUTPUT_BUF_SIZE - output_len - 1;
}
ssize_t n = read(fd, output_buffer + output_len, space);
if (n > 0) {
output_len += n;
output_buffer[output_len] = '\0';
}
return n;
}
/*
* Custom implementation of exp_spawnl using forkpty.
* This avoids the segfault bug in libexpect on Arch Linux.
*/
void void
prof_start(void) prof_start(void)
{ {
struct winsize ws; // helper script sets terminal columns, avoids assertions failing
ws.ws_row = 24; // based on the test runner terminal size
ws.ws_col = 300; /* Match COLUMNS=300 from start_profanity.sh */ fd = exp_spawnl("sh",
ws.ws_xpixel = 0; "sh",
ws.ws_ypixel = 0; "-c",
"./tests/functionaltests/start_profanity.sh",
NULL);
FILE *fp = fdopen(fd, "r+");
/* Reset output buffer */ assert_true(fp != NULL);
output_len = 0;
output_buffer[0] = '\0';
child_pid = forkpty(&fd, NULL, NULL, &ws); setbuf(fp, (char *)0);
if (child_pid < 0) {
fd = -1;
return;
}
if (child_pid == 0) {
/* Child process */
setenv("COLUMNS", "300", 1);
setenv("TERM", "xterm", 1);
execl("./profanity", "./profanity", "-l", "DEBUG", NULL);
/* If exec fails */
fprintf(stderr, "execl failed: %s\n", strerror(errno));
_exit(127);
}
/* Parent process */
/* Set non-blocking mode for reading */
int flags = fcntl(fd, F_GETFL, 0);
fcntl(fd, F_SETFL, flags | O_NONBLOCK);
} }
int int
init_prof_test(void **state) init_prof_test(void **state)
{ {
gboolean started = FALSE; if (stbbr_start(STBBR_LOGDEBUG ,5230, 0) != 0) {
for (int p = 5230; p < 5250; ++p) { assert_true(FALSE);
int ret = stbbr_start(STBBR_LOGDEBUG, p, 0);
if (ret == 0) {
stub_port = p;
started = TRUE;
break;
}
}
if (!started) {
assert_true(FALSE); // could not start stabber on any port in range
return -1; return -1;
} }
// Give stabber server thread time to start listening
usleep(100000); // 100ms
config_orig = getenv("XDG_CONFIG_HOME"); config_orig = getenv("XDG_CONFIG_HOME");
data_orig = getenv("XDG_DATA_HOME"); data_orig = getenv("XDG_DATA_HOME");
@@ -248,69 +157,50 @@ init_prof_test(void **state)
_create_logs_dir(); _create_logs_dir();
prof_start(); prof_start();
int prof_started = prof_output_regex("CProof\\. Type /help for help information\\."); assert_true(prof_output_exact("Profanity"));
assert_true(prof_started);
// set UI options to make expect assertions faster and more reliable // set UI options to make expect assertions faster and more reliable
prof_input("/inpblock timeout 5"); prof_input("/inpblock timeout 5");
assert_true(prof_output_regex("Input blocking set to 5 milliseconds")); assert_true(prof_output_exact("Input blocking set to 5 milliseconds"));
prof_input("/inpblock dynamic off"); prof_input("/inpblock dynamic off");
assert_true(prof_output_regex("Dynamic input blocking disabled")); assert_true(prof_output_exact("Dynamic input blocking disabled"));
prof_input("/notify chat off"); prof_input("/notify chat off");
assert_true(prof_output_regex("Chat notifications disabled")); assert_true(prof_output_exact("Chat notifications disabled"));
prof_input("/notify room off"); prof_input("/notify room off");
assert_true(prof_output_regex("Room notifications disabled")); assert_true(prof_output_exact("Room notifications disabled"));
prof_input("/wrap off"); prof_input("/wrap off");
assert_true(prof_output_regex("Word wrap disabled")); assert_true(prof_output_exact("Word wrap disabled"));
prof_input("/roster hide"); prof_input("/roster hide");
assert_true(prof_output_regex("Roster disabled")); assert_true(prof_output_exact("Roster disabled"));
prof_input("/occupants default hide"); prof_input("/occupants default hide");
assert_true(prof_output_regex("Occupant list disabled")); assert_true(prof_output_exact("Occupant list disabled"));
prof_input("/time console off"); prof_input("/time console off");
prof_input("/time console off"); prof_input("/time console off");
assert_true(prof_output_regex("Console time display disabled\\.")); assert_true(prof_output_exact("Console time display disabled."));
prof_input("/time chat off"); prof_input("/time chat off");
assert_true(prof_output_regex("Chat time display disabled\\.")); assert_true(prof_output_exact("Chat time display disabled."));
prof_input("/time muc off"); prof_input("/time muc off");
assert_true(prof_output_regex("MUC time display disabled\\.")); assert_true(prof_output_exact("MUC time display disabled."));
prof_input("/time config off"); prof_input("/time config off");
assert_true(prof_output_regex("Config time display disabled\\.")); assert_true(prof_output_exact("config time display disabled."));
prof_input("/time private off"); prof_input("/time private off");
assert_true(prof_output_regex("Private chat time display disabled\\.")); assert_true(prof_output_exact("Private chat time display disabled."));
prof_input("/time xml off"); prof_input("/time xml off");
assert_true(prof_output_regex("XML Console time display disabled\\.")); assert_true(prof_output_exact("XML Console time display disabled."));
return 0; return 0;
} }
int int
close_prof_test(void **state) close_prof_test(void **state)
{ {
if (fd > 0 && child_pid > 0) {
prof_input("/quit"); prof_input("/quit");
// Give profanity time to process quit command waitpid(exp_pid, NULL, 0);
sleep(1);
waitpid(child_pid, NULL, 0);
close(fd);
fd = 0;
child_pid = 0;
}
_cleanup_dirs(); _cleanup_dirs();
if (config_orig) {
setenv("XDG_CONFIG_HOME", config_orig, 1); setenv("XDG_CONFIG_HOME", config_orig, 1);
}
if (data_orig) {
setenv("XDG_DATA_HOME", data_orig, 1); setenv("XDG_DATA_HOME", data_orig, 1);
}
stbbr_stop(); stbbr_stop();
/*
* TODO: Replace with proper synchronization.
* stabber doesn't provide wait_stopped() API yet, so we use delay
* to ensure the port is released before the next test starts.
* See: https://git.jabber.space/devs/stabber/issues/3
*/
usleep(100000); // 100ms
return 0; return 0;
} }
@@ -319,75 +209,20 @@ prof_input(const char *input)
{ {
GString *inp_str = g_string_new(input); GString *inp_str = g_string_new(input);
g_string_append(inp_str, "\r"); g_string_append(inp_str, "\r");
ssize_t _wn = write(fd, inp_str->str, inp_str->len); write(fd, inp_str->str, inp_str->len);
(void)_wn;
g_string_free(inp_str, TRUE); g_string_free(inp_str, TRUE);
/* Small delay to let profanity process input */
usleep(10000);
} }
/*
* Wait for exact text to appear in output.
* Returns 1 if found, 0 if timeout.
*/
int int
prof_output_exact(const char *text) prof_output_exact(const char *text)
{ {
time_t start = time(NULL); return (1 == exp_expectl(fd, exp_exact, text, 1, exp_end));
while (time(NULL) - start < expect_timeout) {
/* Read any available output */
while (_read_output(100) > 0) {
/* Keep reading while data available */
} }
/* Check if text is in buffer */
if (strstr(output_buffer, text) != NULL) {
return 1;
}
usleep(50000); /* 50ms */
}
return 0;
}
/*
* Wait for regex pattern to match in output.
* Returns 1 if found, 0 if timeout.
*/
int int
prof_output_regex(const char *pattern) prof_output_regex(const char *text)
{ {
regex_t regex; return (1 == exp_expectl(fd, exp_regexp, text, 1, exp_end));
int ret;
ret = regcomp(&regex, pattern, REG_EXTENDED | REG_NOSUB);
if (ret != 0) {
return 0;
}
time_t start = time(NULL);
while (time(NULL) - start < expect_timeout) {
/* Read any available output */
while (_read_output(100) > 0) {
/* Keep reading while data available */
}
/* Check if pattern matches */
ret = regexec(&regex, output_buffer, 0, NULL, 0);
if (ret == 0) {
regfree(&regex);
return 1;
}
usleep(50000); /* 50ms */
}
regfree(&regex);
return 0;
} }
void void
@@ -406,40 +241,33 @@ prof_connect_with_roster(const char *roster)
stbbr_for_query("jabber:iq:roster", roster_str->str); stbbr_for_query("jabber:iq:roster", roster_str->str);
g_string_free(roster_str, TRUE); g_string_free(roster_str, TRUE);
stbbr_auth_passwd("password"); stbbr_for_id("prof_presence_1",
"<presence id='prof_presence_1' lang='en' to='stabber@localhost/profanity' from='stabber@localhost/profanity'>"
"<priority>0</priority>"
"<c hash='sha-1' xmlns='http://jabber.org/protocol/caps' node='http://profanity-im.github.io/' ver='f8mrtdyAmhnj8Ca+630bThSL718='/>"
"</presence>"
);
char connect_cmd[128]; prof_input("/connect stabber@localhost server 127.0.0.1 port 5230 tls allow");
snprintf(connect_cmd, sizeof(connect_cmd), "/connect stabber@localhost server 127.0.0.1 port %d tls disable auth legacy", stub_port);
prof_input(connect_cmd);
assert_true(prof_output_regex("password:"));
prof_input("password"); prof_input("password");
expect_timeout = 60; // Allow time for profanity to connect
assert_true(prof_output_regex("Connecting as stabber@localhost")); exp_timeout = 30;
assert_true(prof_output_regex("logged in successfully")); assert_true(prof_output_regex("stabber@localhost/profanity logged in successfully, .+online.+ \\(priority 0\\)\\."));
assert_true(prof_output_regex(".+online.+ \\(priority 0\\)\\.")); exp_timeout = 10;
stbbr_wait_for("prof_presence_*");
expect_timeout = 60;
// Wait for presence stanza to be sent (content-based, not ID-based)
// Match the actual attribute order from stanza_attach_caps
assert_true(stbbr_received(
"<presence id=\"*\">"
"<c xmlns=\"http://jabber.org/protocol/caps\" hash=\"sha-1\" node=\"http://profanity-im.github.io\" ver=\"*\"/>"
"</presence>"
));
} }
void void
prof_timeout(int timeout) prof_timeout(int timeout)
{ {
expect_timeout = timeout; exp_timeout = timeout;
} }
void void
prof_timeout_reset(void) prof_timeout_reset(void)
{ {
expect_timeout = 60; exp_timeout = 10;
} }
void void

View File

@@ -4,8 +4,6 @@
#define XDG_CONFIG_HOME "./tests/functionaltests/files/xdg_config_home" #define XDG_CONFIG_HOME "./tests/functionaltests/files/xdg_config_home"
#define XDG_DATA_HOME "./tests/functionaltests/files/xdg_data_home" #define XDG_DATA_HOME "./tests/functionaltests/files/xdg_data_home"
extern int stub_port;
int init_prof_test(void **state); int init_prof_test(void **state);
int close_prof_test(void **state); int close_prof_test(void **state);

View File

@@ -4,6 +4,7 @@
#include <string.h> #include <string.h>
#include <stabber.h> #include <stabber.h>
#include <expect.h>
#include "proftest.h" #include "proftest.h"

View File

@@ -1,10 +1,10 @@
#include <glib.h> #include <glib.h>
#include <unistd.h>
#include "prof_cmocka.h" #include "prof_cmocka.h"
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <stabber.h> #include <stabber.h>
#include <expect.h>
#include "proftest.h" #include "proftest.h"
@@ -144,11 +144,7 @@ resets_to_barejid_after_presence_received(void **state)
"<show>dnd</show>" "<show>dnd</show>"
"</presence>" "</presence>"
); );
// Wait for presence to be processed before sending next message. assert_true(prof_output_exact("Buddy1 (laptop) is dnd"));
// The presence output may appear in console or chat window depending on focus,
// so we use a small delay to ensure the session is reset to barejid.
// Under Valgrind, processing is slower, so this delay is necessary.
g_usleep(500000); // 500ms
prof_input("/msg buddy1@localhost Outgoing 2"); prof_input("/msg buddy1@localhost Outgoing 2");
assert_true(stbbr_received( assert_true(stbbr_received(

View File

@@ -2,9 +2,9 @@
#include "prof_cmocka.h" #include "prof_cmocka.h"
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <stdio.h>
#include <stabber.h> #include <stabber.h>
#include <expect.h>
#include "proftest.h" #include "proftest.h"
@@ -47,9 +47,7 @@ connect_jid_requests_bookmarks(void **state)
void void
connect_bad_password(void **state) connect_bad_password(void **state)
{ {
char connect_cmd[128]; prof_input("/connect stabber@localhost server 127.0.0.1 port 5230 tls allow");
snprintf(connect_cmd, sizeof(connect_cmd), "/connect stabber@localhost server 127.0.0.1 port %d tls allow", stub_port);
prof_input(connect_cmd);
prof_input("badpassword"); prof_input("badpassword");
assert_true(prof_output_exact("Login failed.")); assert_true(prof_output_exact("Login failed."));

View File

@@ -4,6 +4,7 @@
#include <string.h> #include <string.h>
#include <stabber.h> #include <stabber.h>
#include <expect.h>
#include "proftest.h" #include "proftest.h"

View File

@@ -4,6 +4,7 @@
#include <string.h> #include <string.h>
#include <stabber.h> #include <stabber.h>
#include <expect.h>
#include "proftest.h" #include "proftest.h"

View File

@@ -1,21 +1,13 @@
#include <glib.h> #include <glib.h>
#include <unistd.h>
#include "prof_cmocka.h" #include "prof_cmocka.h"
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <stabber.h> #include <stabber.h>
#include <expect.h>
#include "proftest.h" #include "proftest.h"
/*
* NOTE: We use prof_output_regex() throughout this file even for seemingly
* exact strings because some output may include timestamps, color codes,
* or other dynamic content depending on configuration. Using regex provides
* more robust matching. For patterns without regex metacharacters, the
* performance difference is negligible.
*/
void void
sends_room_join(void **state) sends_room_join(void **state)
{ {
@@ -23,7 +15,7 @@ sends_room_join(void **state)
prof_input("/join testroom@conference.localhost"); prof_input("/join testroom@conference.localhost");
assert_true(stbbr_received( assert_true(stbbr_last_received(
"<presence id='*' to='testroom@conference.localhost/stabber'>" "<presence id='*' to='testroom@conference.localhost/stabber'>"
"<x xmlns='http://jabber.org/protocol/muc'/>" "<x xmlns='http://jabber.org/protocol/muc'/>"
"<c hash='sha-1' xmlns='http://jabber.org/protocol/caps' ver='*' node='http://profanity-im.github.io'/>" "<c hash='sha-1' xmlns='http://jabber.org/protocol/caps' ver='*' node='http://profanity-im.github.io'/>"
@@ -38,7 +30,7 @@ sends_room_join_with_nick(void **state)
prof_input("/join testroom@conference.localhost nick testnick"); prof_input("/join testroom@conference.localhost nick testnick");
assert_true(stbbr_received( assert_true(stbbr_last_received(
"<presence id='*' to='testroom@conference.localhost/testnick'>" "<presence id='*' to='testroom@conference.localhost/testnick'>"
"<x xmlns='http://jabber.org/protocol/muc'/>" "<x xmlns='http://jabber.org/protocol/muc'/>"
"<c hash='sha-1' xmlns='http://jabber.org/protocol/caps' ver='*' node='http://profanity-im.github.io'/>" "<c hash='sha-1' xmlns='http://jabber.org/protocol/caps' ver='*' node='http://profanity-im.github.io'/>"
@@ -53,7 +45,7 @@ sends_room_join_with_password(void **state)
prof_input("/join testroom@conference.localhost password testpassword"); prof_input("/join testroom@conference.localhost password testpassword");
assert_true(stbbr_received( assert_true(stbbr_last_received(
"<presence id='*' to='testroom@conference.localhost/stabber'>" "<presence id='*' to='testroom@conference.localhost/stabber'>"
"<x xmlns='http://jabber.org/protocol/muc'>" "<x xmlns='http://jabber.org/protocol/muc'>"
"<password>testpassword</password>" "<password>testpassword</password>"
@@ -70,7 +62,7 @@ sends_room_join_with_nick_and_password(void **state)
prof_input("/join testroom@conference.localhost nick testnick password testpassword"); prof_input("/join testroom@conference.localhost nick testnick password testpassword");
assert_true(stbbr_received( assert_true(stbbr_last_received(
"<presence id='*' to='testroom@conference.localhost/testnick'>" "<presence id='*' to='testroom@conference.localhost/testnick'>"
"<x xmlns='http://jabber.org/protocol/muc'>" "<x xmlns='http://jabber.org/protocol/muc'>"
"<password>testpassword</password>" "<password>testpassword</password>"
@@ -85,8 +77,8 @@ shows_role_and_affiliation_on_join(void **state)
{ {
prof_connect(); prof_connect();
stbbr_for_presence_to("testroom@conference.localhost/stabber", stbbr_for_id("prof_join_4",
"<presence id='*' lang='en' to='stabber@localhost/profanity' from='testroom@conference.localhost/stabber'>" "<presence id='prof_join_4' lang='en' to='stabber@localhost/profanity' from='testroom@conference.localhost/stabber'>"
"<c hash='sha-1' xmlns='http://jabber.org/protocol/caps' node='http://profanity-im.github.io' ver='*'/>" "<c hash='sha-1' xmlns='http://jabber.org/protocol/caps' node='http://profanity-im.github.io' ver='*'/>"
"<x xmlns='http://jabber.org/protocol/muc#user'>" "<x xmlns='http://jabber.org/protocol/muc#user'>"
"<item role='participant' jid='stabber@localhost/profanity' affiliation='none'/>" "<item role='participant' jid='stabber@localhost/profanity' affiliation='none'/>"
@@ -97,7 +89,7 @@ shows_role_and_affiliation_on_join(void **state)
prof_input("/join testroom@conference.localhost"); prof_input("/join testroom@conference.localhost");
assert_true(prof_output_regex("-> You have joined the room as stabber, role: participant, affiliation: none")); assert_true(prof_output_exact("-> You have joined the room as stabber, role: participant, affiliation: none"));
} }
void void
@@ -105,8 +97,8 @@ shows_subject_on_join(void **state)
{ {
prof_connect(); prof_connect();
stbbr_for_presence_to("testroom@conference.localhost/stabber", stbbr_for_id("prof_join_4",
"<presence id='*' lang='en' to='stabber@localhost/profanity' from='testroom@conference.localhost/stabber'>" "<presence id='prof_join_4' lang='en' to='stabber@localhost/profanity' from='testroom@conference.localhost/stabber'>"
"<c hash='sha-1' xmlns='http://jabber.org/protocol/caps' node='http://profanity-im.github.io' ver='*'/>" "<c hash='sha-1' xmlns='http://jabber.org/protocol/caps' node='http://profanity-im.github.io' ver='*'/>"
"<x xmlns='http://jabber.org/protocol/muc#user'>" "<x xmlns='http://jabber.org/protocol/muc#user'>"
"<item role='participant' jid='stabber@localhost/profanity' affiliation='none'/>" "<item role='participant' jid='stabber@localhost/profanity' affiliation='none'/>"
@@ -116,7 +108,7 @@ shows_subject_on_join(void **state)
); );
prof_input("/join testroom@conference.localhost"); prof_input("/join testroom@conference.localhost");
assert_true(prof_output_regex("-> You have joined the room as stabber, role: participant, affiliation: none")); assert_true(prof_output_exact("-> You have joined the room as stabber, role: participant, affiliation: none"));
stbbr_send( stbbr_send(
"<message type='groupchat' to='stabber@localhost/profanity' from='testroom@conference.localhost'>" "<message type='groupchat' to='stabber@localhost/profanity' from='testroom@conference.localhost'>"
@@ -133,8 +125,8 @@ shows_history_message(void **state)
{ {
prof_connect(); prof_connect();
stbbr_for_presence_to("testroom@conference.localhost/stabber", stbbr_for_id("prof_join_4",
"<presence id='*' lang='en' to='stabber@localhost/profanity' from='testroom@conference.localhost/stabber'>" "<presence id='prof_join_4' lang='en' to='stabber@localhost/profanity' from='testroom@conference.localhost/stabber'>"
"<c hash='sha-1' xmlns='http://jabber.org/protocol/caps' node='http://profanity-im.github.io' ver='*'/>" "<c hash='sha-1' xmlns='http://jabber.org/protocol/caps' node='http://profanity-im.github.io' ver='*'/>"
"<x xmlns='http://jabber.org/protocol/muc#user'>" "<x xmlns='http://jabber.org/protocol/muc#user'>"
"<item role='participant' jid='stabber@localhost/profanity' affiliation='none'/>" "<item role='participant' jid='stabber@localhost/profanity' affiliation='none'/>"
@@ -144,7 +136,7 @@ shows_history_message(void **state)
); );
prof_input("/join testroom@conference.localhost"); prof_input("/join testroom@conference.localhost");
assert_true(prof_output_regex("-> You have joined the room as stabber, role: participant, affiliation: none")); assert_true(prof_output_exact("-> You have joined the room as stabber, role: participant, affiliation: none"));
stbbr_send( stbbr_send(
"<message type='groupchat' to='stabber@localhost/profanity' from='testroom@conference.localhost/testoccupant'>" "<message type='groupchat' to='stabber@localhost/profanity' from='testroom@conference.localhost/testoccupant'>"
@@ -154,9 +146,7 @@ shows_history_message(void **state)
"</message>" "</message>"
); );
prof_timeout(10); assert_true(prof_output_regex("testoccupant: an old message"));
assert_true(prof_output_regex("testoccupant:.*an old message"));
prof_timeout_reset();
} }
void void
@@ -164,12 +154,8 @@ shows_occupant_join(void **state)
{ {
prof_connect(); prof_connect();
// Enable MUC status messages to see occupant join/leave stbbr_for_id("prof_join_4",
prof_input("/presence room all"); "<presence id='prof_join_4' lang='en' to='stabber@localhost/profanity' from='testroom@conference.localhost/stabber'>"
assert_true(prof_output_regex("All presence updates will appear"));
stbbr_for_presence_to("testroom@conference.localhost/stabber",
"<presence id='*' lang='en' to='stabber@localhost/profanity' from='testroom@conference.localhost/stabber'>"
"<c hash='sha-1' xmlns='http://jabber.org/protocol/caps' node='http://profanity-im.github.io' ver='*'/>" "<c hash='sha-1' xmlns='http://jabber.org/protocol/caps' node='http://profanity-im.github.io' ver='*'/>"
"<x xmlns='http://jabber.org/protocol/muc#user'>" "<x xmlns='http://jabber.org/protocol/muc#user'>"
"<item role='participant' jid='stabber@localhost/profanity' affiliation='none'/>" "<item role='participant' jid='stabber@localhost/profanity' affiliation='none'/>"
@@ -179,7 +165,7 @@ shows_occupant_join(void **state)
); );
prof_input("/join testroom@conference.localhost"); prof_input("/join testroom@conference.localhost");
assert_true(prof_output_regex("-> You have joined the room as stabber, role: participant, affiliation: none")); assert_true(prof_output_exact("-> You have joined the room as stabber, role: participant, affiliation: none"));
stbbr_send( stbbr_send(
"<presence to='stabber@localhost/profanity' from='testroom@conference.localhost/testoccupant'>" "<presence to='stabber@localhost/profanity' from='testroom@conference.localhost/testoccupant'>"
@@ -188,9 +174,8 @@ shows_occupant_join(void **state)
"</x>" "</x>"
"</presence>" "</presence>"
); );
sleep(1);
assert_true(prof_output_regex("testoccupant has joined")); assert_true(prof_output_exact("-> testoccupant has joined the room, role: participant, affiliation: none"));
} }
void void
@@ -198,8 +183,8 @@ shows_message(void **state)
{ {
prof_connect(); prof_connect();
stbbr_for_presence_to("testroom@conference.localhost/stabber", stbbr_for_id("prof_join_4",
"<presence id='*' lang='en' to='stabber@localhost/profanity' from='testroom@conference.localhost/stabber'>" "<presence id='prof_join_4' lang='en' to='stabber@localhost/profanity' from='testroom@conference.localhost/stabber'>"
"<c hash='sha-1' xmlns='http://jabber.org/protocol/caps' node='http://profanity-im.github.io' ver='*'/>" "<c hash='sha-1' xmlns='http://jabber.org/protocol/caps' node='http://profanity-im.github.io' ver='*'/>"
"<x xmlns='http://jabber.org/protocol/muc#user'>" "<x xmlns='http://jabber.org/protocol/muc#user'>"
"<item role='participant' jid='stabber@localhost/profanity' affiliation='none'/>" "<item role='participant' jid='stabber@localhost/profanity' affiliation='none'/>"
@@ -209,7 +194,7 @@ shows_message(void **state)
); );
prof_input("/join testroom@conference.localhost"); prof_input("/join testroom@conference.localhost");
assert_true(prof_output_regex("-> You have joined the room as stabber, role: participant, affiliation: none")); assert_true(prof_output_exact("-> You have joined the room as stabber, role: participant, affiliation: none"));
stbbr_send( stbbr_send(
"<message type='groupchat' to='stabber@localhost/profanity' from='testroom@conference.localhost/testoccupant'>" "<message type='groupchat' to='stabber@localhost/profanity' from='testroom@conference.localhost/testoccupant'>"
@@ -225,8 +210,8 @@ shows_me_message_from_occupant(void **state)
{ {
prof_connect(); prof_connect();
stbbr_for_presence_to("testroom@conference.localhost/stabber", stbbr_for_id("prof_join_4",
"<presence id='*' lang='en' to='stabber@localhost/profanity' from='testroom@conference.localhost/stabber'>" "<presence id='prof_join_4' lang='en' to='stabber@localhost/profanity' from='testroom@conference.localhost/stabber'>"
"<c hash='sha-1' xmlns='http://jabber.org/protocol/caps' node='http://profanity-im.github.io' ver='*'/>" "<c hash='sha-1' xmlns='http://jabber.org/protocol/caps' node='http://profanity-im.github.io' ver='*'/>"
"<x xmlns='http://jabber.org/protocol/muc#user'>" "<x xmlns='http://jabber.org/protocol/muc#user'>"
"<item role='participant' jid='stabber@localhost/profanity' affiliation='none'/>" "<item role='participant' jid='stabber@localhost/profanity' affiliation='none'/>"
@@ -236,7 +221,7 @@ shows_me_message_from_occupant(void **state)
); );
prof_input("/join testroom@conference.localhost"); prof_input("/join testroom@conference.localhost");
assert_true(prof_output_regex("-> You have joined the room as stabber, role: participant, affiliation: none")); assert_true(prof_output_exact("-> You have joined the room as stabber, role: participant, affiliation: none"));
stbbr_send( stbbr_send(
"<message type='groupchat' to='stabber@localhost/profanity' from='testroom@conference.localhost/testoccupant'>" "<message type='groupchat' to='stabber@localhost/profanity' from='testroom@conference.localhost/testoccupant'>"
@@ -244,7 +229,7 @@ shows_me_message_from_occupant(void **state)
"</message>" "</message>"
); );
assert_true(prof_output_regex("\\*testoccupant did something")); assert_true(prof_output_exact("*testoccupant did something"));
} }
void void
@@ -252,8 +237,8 @@ shows_me_message_from_self(void **state)
{ {
prof_connect(); prof_connect();
stbbr_for_presence_to("testroom@conference.localhost/stabber", stbbr_for_id("prof_join_4",
"<presence id='*' lang='en' to='stabber@localhost/profanity' from='testroom@conference.localhost/stabber'>" "<presence id='prof_join_4' lang='en' to='stabber@localhost/profanity' from='testroom@conference.localhost/stabber'>"
"<c hash='sha-1' xmlns='http://jabber.org/protocol/caps' node='http://profanity-im.github.io' ver='*'/>" "<c hash='sha-1' xmlns='http://jabber.org/protocol/caps' node='http://profanity-im.github.io' ver='*'/>"
"<x xmlns='http://jabber.org/protocol/muc#user'>" "<x xmlns='http://jabber.org/protocol/muc#user'>"
"<item role='participant' jid='stabber@localhost/profanity' affiliation='none'/>" "<item role='participant' jid='stabber@localhost/profanity' affiliation='none'/>"
@@ -263,7 +248,7 @@ shows_me_message_from_self(void **state)
); );
prof_input("/join testroom@conference.localhost"); prof_input("/join testroom@conference.localhost");
assert_true(prof_output_regex("-> You have joined the room as stabber, role: participant, affiliation: none")); assert_true(prof_output_exact("-> You have joined the room as stabber, role: participant, affiliation: none"));
stbbr_send( stbbr_send(
"<message type='groupchat' to='stabber@localhost/profanity' from='testroom@conference.localhost/stabber'>" "<message type='groupchat' to='stabber@localhost/profanity' from='testroom@conference.localhost/stabber'>"
@@ -271,7 +256,7 @@ shows_me_message_from_self(void **state)
"</message>" "</message>"
); );
assert_true(prof_output_regex("\\*stabber did something")); assert_true(prof_output_exact("*stabber did something"));
} }
void void
@@ -279,8 +264,8 @@ shows_all_messages_in_console_when_window_not_focussed(void **state)
{ {
prof_connect(); prof_connect();
stbbr_for_presence_to("testroom@conference.localhost/stabber", stbbr_for_id("prof_join_4",
"<presence id='*' lang='en' to='stabber@localhost/profanity' from='testroom@conference.localhost/stabber'>" "<presence id='prof_join_4' lang='en' to='stabber@localhost/profanity' from='testroom@conference.localhost/stabber'>"
"<c hash='sha-1' xmlns='http://jabber.org/protocol/caps' node='http://profanity-im.github.io' ver='*'/>" "<c hash='sha-1' xmlns='http://jabber.org/protocol/caps' node='http://profanity-im.github.io' ver='*'/>"
"<x xmlns='http://jabber.org/protocol/muc#user'>" "<x xmlns='http://jabber.org/protocol/muc#user'>"
"<item role='participant' jid='stabber@localhost/profanity' affiliation='none'/>" "<item role='participant' jid='stabber@localhost/profanity' affiliation='none'/>"
@@ -290,10 +275,10 @@ shows_all_messages_in_console_when_window_not_focussed(void **state)
); );
prof_input("/join testroom@conference.localhost"); prof_input("/join testroom@conference.localhost");
assert_true(prof_output_regex("-> You have joined the room as stabber, role: participant, affiliation: none")); assert_true(prof_output_exact("-> You have joined the room as stabber, role: participant, affiliation: none"));
prof_input("/win 1"); prof_input("/win 1");
assert_true(prof_output_regex("CProof\\. Type /help for help information\\.")); assert_true(prof_output_exact("CProof. Type /help for help information."));
stbbr_send( stbbr_send(
"<message type='groupchat' to='stabber@localhost/profanity' from='testroom@conference.localhost/testoccupant'>" "<message type='groupchat' to='stabber@localhost/profanity' from='testroom@conference.localhost/testoccupant'>"
@@ -301,7 +286,7 @@ shows_all_messages_in_console_when_window_not_focussed(void **state)
"</message>" "</message>"
); );
assert_true(prof_output_regex("<< room message: testoccupant in testroom@conference\\.localhost \\(win 2\\)")); assert_true(prof_output_exact("<< room message: testoccupant in testroom@conference.localhost (win 2)"));
stbbr_send( stbbr_send(
"<message type='groupchat' to='stabber@localhost/profanity' from='testroom@conference.localhost/anotheroccupant'>" "<message type='groupchat' to='stabber@localhost/profanity' from='testroom@conference.localhost/anotheroccupant'>"
@@ -309,7 +294,7 @@ shows_all_messages_in_console_when_window_not_focussed(void **state)
"</message>" "</message>"
); );
assert_true(prof_output_regex("<< room message: anotheroccupant in testroom@conference\\.localhost \\(win 2\\)")); assert_true(prof_output_exact("<< room message: anotheroccupant in testroom@conference.localhost (win 2)"));
} }
void void
@@ -318,10 +303,10 @@ shows_first_message_in_console_when_window_not_focussed(void **state)
prof_connect(); prof_connect();
prof_input("/console muc first"); prof_input("/console muc first");
assert_true(prof_output_regex("Console MUC messages set: first")); assert_true(prof_output_exact("Console MUC messages set: first"));
stbbr_for_presence_to("testroom@conference.localhost/stabber", stbbr_for_id("prof_join_4",
"<presence id='*' lang='en' to='stabber@localhost/profanity' from='testroom@conference.localhost/stabber'>" "<presence id='prof_join_4' lang='en' to='stabber@localhost/profanity' from='testroom@conference.localhost/stabber'>"
"<c hash='sha-1' xmlns='http://jabber.org/protocol/caps' node='http://profanity-im.github.io' ver='*'/>" "<c hash='sha-1' xmlns='http://jabber.org/protocol/caps' node='http://profanity-im.github.io' ver='*'/>"
"<x xmlns='http://jabber.org/protocol/muc#user'>" "<x xmlns='http://jabber.org/protocol/muc#user'>"
"<item role='participant' jid='stabber@localhost/profanity' affiliation='none'/>" "<item role='participant' jid='stabber@localhost/profanity' affiliation='none'/>"
@@ -331,22 +316,21 @@ shows_first_message_in_console_when_window_not_focussed(void **state)
); );
prof_input("/join testroom@conference.localhost"); prof_input("/join testroom@conference.localhost");
assert_true(prof_output_regex("-> You have joined the room as stabber, role: participant, affiliation: none")); assert_true(prof_output_exact("-> You have joined the room as stabber, role: participant, affiliation: none"));
prof_input("/win 1"); prof_input("/win 1");
assert_true(prof_output_regex("CProof\\. Type /help for help information\\.")); assert_true(prof_output_exact("CProof. Type /help for help information."));
stbbr_send( stbbr_send(
"<message type='groupchat' to='stabber@localhost/profanity' from='testroom@conference.localhost/testoccupant'>" "<message type='groupchat' to='stabber@localhost/profanity' from='testroom@conference.localhost/testoccupant'>"
"<body>a new message</body>" "<body>a new message</body>"
"</message>" "</message>"
); );
sleep(1);
assert_true(prof_output_regex("room message.*testroom@conference\\.localhost")); assert_true(prof_output_exact("<< room message: testroom@conference.localhost (win 2)"));
prof_input("/clear"); prof_input("/clear");
prof_input("/about"); prof_input("/about");
assert_true(prof_output_regex("Type '/help' to show complete help\\.")); assert_true(prof_output_exact("Type '/help' to show complete help."));
stbbr_send( stbbr_send(
"<message type='groupchat' to='stabber@localhost/profanity' from='testroom@conference.localhost/anotheroccupant'>" "<message type='groupchat' to='stabber@localhost/profanity' from='testroom@conference.localhost/anotheroccupant'>"
@@ -355,7 +339,7 @@ shows_first_message_in_console_when_window_not_focussed(void **state)
); );
prof_timeout(2); prof_timeout(2);
assert_false(prof_output_regex("<< room message: testroom@conference\\.localhost \\(win 2\\)")); assert_false(prof_output_exact("<< room message: testroom@conference.localhost (win 2)"));
prof_timeout_reset(); prof_timeout_reset();
} }
@@ -365,10 +349,10 @@ shows_no_message_in_console_when_window_not_focussed(void **state)
prof_connect(); prof_connect();
prof_input("/console muc none"); prof_input("/console muc none");
assert_true(prof_output_regex("Console MUC messages set: none")); assert_true(prof_output_exact("Console MUC messages set: none"));
stbbr_for_presence_to("testroom@conference.localhost/stabber", stbbr_for_id("prof_join_4",
"<presence id='*' lang='en' to='stabber@localhost/profanity' from='testroom@conference.localhost/stabber'>" "<presence id='prof_join_4' lang='en' to='stabber@localhost/profanity' from='testroom@conference.localhost/stabber'>"
"<c hash='sha-1' xmlns='http://jabber.org/protocol/caps' node='http://profanity-im.github.io' ver='*'/>" "<c hash='sha-1' xmlns='http://jabber.org/protocol/caps' node='http://profanity-im.github.io' ver='*'/>"
"<x xmlns='http://jabber.org/protocol/muc#user'>" "<x xmlns='http://jabber.org/protocol/muc#user'>"
"<item role='participant' jid='stabber@localhost/profanity' affiliation='none'/>" "<item role='participant' jid='stabber@localhost/profanity' affiliation='none'/>"
@@ -378,10 +362,10 @@ shows_no_message_in_console_when_window_not_focussed(void **state)
); );
prof_input("/join testroom@conference.localhost"); prof_input("/join testroom@conference.localhost");
assert_true(prof_output_regex("-> You have joined the room as stabber, role: participant, affiliation: none")); assert_true(prof_output_exact("-> You have joined the room as stabber, role: participant, affiliation: none"));
prof_input("/win 1"); prof_input("/win 1");
assert_true(prof_output_regex("CProof\\. Type /help for help information\\.")); assert_true(prof_output_exact("CProof. Type /help for help information."));
stbbr_send( stbbr_send(
"<message type='groupchat' to='stabber@localhost/profanity' from='testroom@conference.localhost/testoccupant'>" "<message type='groupchat' to='stabber@localhost/profanity' from='testroom@conference.localhost/testoccupant'>"
@@ -390,6 +374,6 @@ shows_no_message_in_console_when_window_not_focussed(void **state)
); );
prof_timeout(2); prof_timeout(2);
assert_false(prof_output_regex("testroom@conference\\.localhost \\(win 2\\)")); assert_false(prof_output_exact("testroom@conference.localhost (win 2)"));
prof_timeout_reset(); prof_timeout_reset();
} }

View File

@@ -2,26 +2,17 @@
#include "prof_cmocka.h" #include "prof_cmocka.h"
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <unistd.h>
#include <stabber.h> #include <stabber.h>
#include <expect.h>
#include "proftest.h" #include "proftest.h"
void void
ping_server(void **state) ping_server(void **state)
{ {
/* stbbr_for_id("prof_disco_info_onconnect_2",
* This test verifies that profanity correctly handles the /ping command "<iq id='prof_disco_info_onconnect_2' to='stabber@localhost/profanity' type='result' from='localhost'>"
* when the server supports ping (urn:xmpp:ping feature).
*
* We register disco#info response with ping support, then verify
* profanity sends ping and receives "Pinged server" confirmation.
*/
// Register disco#info response that includes ping support
stbbr_for_query("http://jabber.org/protocol/disco#info",
"<iq to='stabber@localhost/profanity' type='result' from='localhost'>"
"<query xmlns='http://jabber.org/protocol/disco#info'>" "<query xmlns='http://jabber.org/protocol/disco#info'>"
"<identity category='server' type='im' name='Prosody'/>" "<identity category='server' type='im' name='Prosody'/>"
"<feature var='urn:xmpp:ping'/>" "<feature var='urn:xmpp:ping'/>"
@@ -29,33 +20,37 @@ ping_server(void **state)
"</iq>" "</iq>"
); );
// Register ping response stbbr_for_id("prof_ping_4",
stbbr_for_query("urn:xmpp:ping", "<iq id='prof_ping_4' type='result' to='stabber@localhost/profanity'/>"
"<iq from='localhost' to='stabber@localhost/profanity' type='result'/>" );
stbbr_for_id("prof_ping_5",
"<iq id='prof_ping_5' type='result' to='stabber@localhost/profanity'/>"
); );
prof_connect(); prof_connect();
/* prof_input("/ping");
* Wait for disco#info exchange to complete. assert_true(stbbr_received(
* TODO: Replace with proper wait mechanism (e.g., wait for capabilities "<iq id='prof_ping_4' type='get'>"
* to be cached). Currently we use sleep to ensure disco#info response "<ping xmlns='urn:xmpp:ping'/>"
* is processed before sending /ping command. "</iq>"
*/ ));
sleep(3); assert_true(prof_output_exact("Ping response from server"));
prof_input("/ping"); prof_input("/ping");
assert_true(stbbr_received(
prof_timeout(10); "<iq id='prof_ping_5' type='get'>"
assert_true(prof_output_regex("Pinged server")); "<ping xmlns='urn:xmpp:ping'/>"
prof_timeout_reset(); "</iq>"
));
assert_true(prof_output_exact("Ping response from server"));
} }
void void
ping_server_not_supported(void **state) ping_server_not_supported(void **state)
{ {
stbbr_for_query("http://jabber.org/protocol/disco#info", stbbr_for_id("prof_disco_info_onconnect_2",
"<iq to='stabber@localhost/profanity' type='result' from='localhost'>" "<iq id='prof_disco_info_onconnect_2' to='stabber@localhost/profanity' type='result' from='localhost'>"
"<query xmlns='http://jabber.org/protocol/disco#info'>" "<query xmlns='http://jabber.org/protocol/disco#info'>"
"<identity category='server' type='im' name='Stabber'/>" "<identity category='server' type='im' name='Stabber'/>"
"</query>" "</query>"
@@ -65,7 +60,7 @@ ping_server_not_supported(void **state)
prof_connect(); prof_connect();
prof_input("/ping"); prof_input("/ping");
assert_true(prof_output_regex("Server does not support ping requests")); assert_true(prof_output_exact("Server does not support ping requests."));
} }
void void
@@ -74,21 +69,20 @@ ping_responds_to_server_request(void **state)
prof_connect(); prof_connect();
stbbr_send( stbbr_send(
"<iq id=\"pingtest1\" type=\"get\" to=\"stabber@localhost/profanity\" from=\"localhost\">" "<iq id='pingtest1' type='get' to='stabber@localhost/profanity' from='localhost'>"
"<ping xmlns=\"urn:xmpp:ping\"/>" "<ping xmlns='urn:xmpp:ping'/>"
"</iq>" "</iq>"
); );
assert_true(stbbr_received( assert_true(stbbr_received(
"<iq id='pingtest1' type='result' from='stabber@localhost/profanity' to='localhost'></iq>" "<iq id='pingtest1' type='result' from='stabber@localhost/profanity' to='localhost'/>"
)); ));
} }
void ping_jid(void **state) void ping_jid(void **state)
{ {
// Caps/disco info response without relying on a fixed id stbbr_for_id("prof_caps_4",
stbbr_for_query("http://jabber.org/protocol/disco#info", "<iq id='prof_caps_4' to='stabber@localhost/profanity' type='result' from='buddy1@localhost/mobile'>"
"<iq to='stabber@localhost/profanity' type='result' from='buddy1@localhost/mobile'>"
"<query xmlns='http://jabber.org/protocol/disco#info' node='http://profanity-im.github.io#LpT2xs3nun7jC2sq4gg3WRDQFZ4='>" "<query xmlns='http://jabber.org/protocol/disco#info' node='http://profanity-im.github.io#LpT2xs3nun7jC2sq4gg3WRDQFZ4='>"
"<identity category='client' type='console' name='Profanity0.6.0'/>" "<identity category='client' type='console' name='Profanity0.6.0'/>"
"<feature var='urn:xmpp:ping'/>" "<feature var='urn:xmpp:ping'/>"
@@ -115,30 +109,29 @@ void ping_jid(void **state)
assert_true(prof_output_exact("Buddy1 (mobile) is online, \"I'm here\"")); assert_true(prof_output_exact("Buddy1 (mobile) is online, \"I'm here\""));
assert_true(stbbr_received( assert_true(stbbr_received(
"<iq id='*' to='buddy1@localhost/mobile' type='get'>" "<iq id='prof_caps_4' to='buddy1@localhost/mobile' type='get'>"
"<query xmlns='http://jabber.org/protocol/disco#info' node='http://profanity-im.github.io#LpT2xs3nun7jC2sq4gg3WRDQFZ4='/>" "<query xmlns='http://jabber.org/protocol/disco#info' node='http://profanity-im.github.io#LpT2xs3nun7jC2sq4gg3WRDQFZ4='/>"
"</iq>" "</iq>"
)); ));
// Respond to ping result regardless of id stbbr_for_id("prof_ping_5",
stbbr_for_query("urn:xmpp:ping", "<iq from='buddy1@localhost/mobile' to='stabber@localhost' id='prof_ping_5' type='result'/>"
"<iq from='buddy1@localhost/mobile' to='stabber@localhost' type='result'/>"
); );
prof_input("/ping buddy1@localhost/mobile"); prof_input("/ping buddy1@localhost/mobile");
assert_true(stbbr_received( assert_true(stbbr_received(
"<iq id='*' type='get' to='buddy1@localhost/mobile'>" "<iq id='prof_ping_5' type='get' to='buddy1@localhost/mobile'>"
"<ping xmlns='urn:xmpp:ping'/>" "<ping xmlns='urn:xmpp:ping'/>"
"</iq>" "</iq>"
)); ));
assert_true(prof_output_regex("Ping response from buddy1@localhost/mobile")); assert_true(prof_output_exact("Ping response from buddy1@localhost/mobile"));
} }
void ping_jid_not_supported(void **state) void ping_jid_not_supported(void **state)
{ {
stbbr_for_query("http://jabber.org/protocol/disco#info", stbbr_for_id("prof_caps_4",
"<iq to='stabber@localhost/profanity' type='result' from='buddy1@localhost/mobile'>" "<iq id='prof_caps_4' to='stabber@localhost/profanity' type='result' from='buddy1@localhost/mobile'>"
"<query xmlns='http://jabber.org/protocol/disco#info' node='http://profanity-im.github.io#LpT2xs3nun7jC2sq4gg3WRDQFZ4='>" "<query xmlns='http://jabber.org/protocol/disco#info' node='http://profanity-im.github.io#LpT2xs3nun7jC2sq4gg3WRDQFZ4='>"
"<identity category='client' type='console' name='Profanity0.6.0'/>" "<identity category='client' type='console' name='Profanity0.6.0'/>"
"<feature var='http://jabber.org/protocol/disco#info'/>" "<feature var='http://jabber.org/protocol/disco#info'/>"
@@ -164,7 +157,7 @@ void ping_jid_not_supported(void **state)
assert_true(prof_output_exact("Buddy1 (mobile) is online, \"I'm here\"")); assert_true(prof_output_exact("Buddy1 (mobile) is online, \"I'm here\""));
assert_true(stbbr_received( assert_true(stbbr_received(
"<iq id='*' to='buddy1@localhost/mobile' type='get'>" "<iq id='prof_caps_4' to='buddy1@localhost/mobile' type='get'>"
"<query xmlns='http://jabber.org/protocol/disco#info' node='http://profanity-im.github.io#LpT2xs3nun7jC2sq4gg3WRDQFZ4='/>" "<query xmlns='http://jabber.org/protocol/disco#info' node='http://profanity-im.github.io#LpT2xs3nun7jC2sq4gg3WRDQFZ4='/>"
"</iq>" "</iq>"
)); ));

View File

@@ -4,6 +4,7 @@
#include <string.h> #include <string.h>
#include <stabber.h> #include <stabber.h>
#include <expect.h>
#include "proftest.h" #include "proftest.h"
@@ -12,16 +13,15 @@ presence_online(void **state)
{ {
prof_connect(); prof_connect();
prof_input("/status set online"); prof_input("/online");
assert_true(prof_output_exact("Status set to online (priority 0), \"online\"."));
assert_true(stbbr_received( assert_true(stbbr_received(
"<presence id='*'>" "<presence id='prof_presence_3'>"
"<status>online</status>"
"<c hash='sha-1' xmlns='http://jabber.org/protocol/caps' ver='*' node='http://profanity-im.github.io'/>" "<c hash='sha-1' xmlns='http://jabber.org/protocol/caps' ver='*' node='http://profanity-im.github.io'/>"
"</presence>" "</presence>"
)); ));
assert_true(prof_output_exact("Status set to online (priority 0)"));
} }
void void
@@ -29,16 +29,16 @@ presence_online_with_message(void **state)
{ {
prof_connect(); prof_connect();
prof_input("/status set online \"Hi there\""); prof_input("/online \"Hi there\"");
assert_true(prof_output_exact("Status set to online (priority 0), \"Hi there\"."));
assert_true(stbbr_received( assert_true(stbbr_received(
"<presence id='*'>" "<presence id='prof_presence_4'>"
"<status>Hi there</status>" "<status>Hi there</status>"
"<c hash='sha-1' xmlns='http://jabber.org/protocol/caps' ver='*' node='http://profanity-im.github.io'/>" "<c hash='sha-1' xmlns='http://jabber.org/protocol/caps' ver='*' node='http://profanity-im.github.io'/>"
"</presence>" "</presence>"
)); ));
assert_true(prof_output_exact("Status set to online (priority 0), \"Hi there\"."));
} }
void void
@@ -46,17 +46,16 @@ presence_away(void **state)
{ {
prof_connect(); prof_connect();
prof_input("/status set away"); prof_input("/away");
assert_true(prof_output_exact("Status set to away (priority 0), \"away\"."));
assert_true(stbbr_received( assert_true(stbbr_received(
"<presence id='*'>" "<presence id='prof_presence_4'>"
"<show>away</show>" "<show>away</show>"
"<status>away</status>"
"<c hash='sha-1' xmlns='http://jabber.org/protocol/caps' ver='*' node='http://profanity-im.github.io'/>" "<c hash='sha-1' xmlns='http://jabber.org/protocol/caps' ver='*' node='http://profanity-im.github.io'/>"
"</presence>" "</presence>"
)); ));
assert_true(prof_output_exact("Status set to away (priority 0)"));
} }
void void
@@ -64,17 +63,17 @@ presence_away_with_message(void **state)
{ {
prof_connect(); prof_connect();
prof_input("/status set away \"I'm not here for a bit\""); prof_input("/away \"I'm not here for a bit\"");
assert_true(prof_output_exact("Status set to away (priority 0), \"I'm not here for a bit\"."));
assert_true(stbbr_received( assert_true(stbbr_received(
"<presence id='*'>" "<presence id='prof_presence_4'>"
"<show>away</show>" "<show>away</show>"
"<status>I'm not here for a bit</status>" "<status>I'm not here for a bit</status>"
"<c hash='sha-1' xmlns='http://jabber.org/protocol/caps' ver='*' node='http://profanity-im.github.io'/>" "<c hash='sha-1' xmlns='http://jabber.org/protocol/caps' ver='*' node='http://profanity-im.github.io'/>"
"</presence>" "</presence>"
)); ));
assert_true(prof_output_exact("Status set to away (priority 0), \"I'm not here for a bit\"."));
} }
void void
@@ -82,17 +81,16 @@ presence_xa(void **state)
{ {
prof_connect(); prof_connect();
prof_input("/status set xa"); prof_input("/xa");
assert_true(prof_output_exact("Status set to xa (priority 0), \"xa\"."));
assert_true(stbbr_received( assert_true(stbbr_received(
"<presence id='*'>" "<presence id='prof_presence_4'>"
"<show>xa</show>" "<show>xa</show>"
"<status>xa</status>"
"<c hash='sha-1' xmlns='http://jabber.org/protocol/caps' ver='*' node='http://profanity-im.github.io'/>" "<c hash='sha-1' xmlns='http://jabber.org/protocol/caps' ver='*' node='http://profanity-im.github.io'/>"
"</presence>" "</presence>"
)); ));
assert_true(prof_output_exact("Status set to xa (priority 0)"));
} }
void void
@@ -100,17 +98,17 @@ presence_xa_with_message(void **state)
{ {
prof_connect(); prof_connect();
prof_input("/status set xa \"Gone to the shops\""); prof_input("/xa \"Gone to the shops\"");
assert_true(prof_output_exact("Status set to xa (priority 0), \"Gone to the shops\"."));
assert_true(stbbr_received( assert_true(stbbr_received(
"<presence id='*'>" "<presence id='prof_presence_4'>"
"<show>xa</show>" "<show>xa</show>"
"<status>Gone to the shops</status>" "<status>Gone to the shops</status>"
"<c hash='sha-1' xmlns='http://jabber.org/protocol/caps' ver='*' node='http://profanity-im.github.io'/>" "<c hash='sha-1' xmlns='http://jabber.org/protocol/caps' ver='*' node='http://profanity-im.github.io'/>"
"</presence>" "</presence>"
)); ));
assert_true(prof_output_exact("Status set to xa (priority 0), \"Gone to the shops\"."));
} }
void void
@@ -118,17 +116,16 @@ presence_dnd(void **state)
{ {
prof_connect(); prof_connect();
prof_input("/status set dnd"); prof_input("/dnd");
assert_true(prof_output_exact("Status set to dnd (priority 0), \"dnd\"."));
assert_true(stbbr_received( assert_true(stbbr_received(
"<presence id='*'>" "<presence id='prof_presence_4'>"
"<show>dnd</show>" "<show>dnd</show>"
"<status>dnd</status>"
"<c hash='sha-1' xmlns='http://jabber.org/protocol/caps' ver='*' node='http://profanity-im.github.io'/>" "<c hash='sha-1' xmlns='http://jabber.org/protocol/caps' ver='*' node='http://profanity-im.github.io'/>"
"</presence>" "</presence>"
)); ));
assert_true(prof_output_exact("Status set to dnd (priority 0)"));
} }
void void
@@ -136,17 +133,17 @@ presence_dnd_with_message(void **state)
{ {
prof_connect(); prof_connect();
prof_input("/status set dnd \"Working\""); prof_input("/dnd \"Working\"");
assert_true(prof_output_exact("Status set to dnd (priority 0), \"Working\"."));
assert_true(stbbr_received( assert_true(stbbr_received(
"<presence id='*'>" "<presence id='prof_presence_4'>"
"<show>dnd</show>" "<show>dnd</show>"
"<status>Working</status>" "<status>Working</status>"
"<c hash='sha-1' xmlns='http://jabber.org/protocol/caps' ver='*' node='http://profanity-im.github.io'/>" "<c hash='sha-1' xmlns='http://jabber.org/protocol/caps' ver='*' node='http://profanity-im.github.io'/>"
"</presence>" "</presence>"
)); ));
assert_true(prof_output_exact("Status set to dnd (priority 0), \"Working\"."));
} }
void void
@@ -154,17 +151,16 @@ presence_chat(void **state)
{ {
prof_connect(); prof_connect();
prof_input("/status set chat"); prof_input("/chat");
assert_true(prof_output_exact("Status set to chat (priority 0), \"chat\"."));
assert_true(stbbr_received( assert_true(stbbr_received(
"<presence id='*'>" "<presence id='prof_presence_4'>"
"<show>chat</show>" "<show>chat</show>"
"<status>chat</status>"
"<c hash='sha-1' xmlns='http://jabber.org/protocol/caps' ver='*' node='http://profanity-im.github.io'/>" "<c hash='sha-1' xmlns='http://jabber.org/protocol/caps' ver='*' node='http://profanity-im.github.io'/>"
"</presence>" "</presence>"
)); ));
assert_true(prof_output_exact("Status set to chat (priority 0)"));
} }
void void
@@ -172,17 +168,17 @@ presence_chat_with_message(void **state)
{ {
prof_connect(); prof_connect();
prof_input("/status set chat \"Free to talk\""); prof_input("/chat \"Free to talk\"");
assert_true(prof_output_exact("Status set to chat (priority 0), \"Free to talk\"."));
assert_true(stbbr_received( assert_true(stbbr_received(
"<presence id='*'>" "<presence id='prof_presence_4'>"
"<show>chat</show>" "<show>chat</show>"
"<status>Free to talk</status>" "<status>Free to talk</status>"
"<c hash='sha-1' xmlns='http://jabber.org/protocol/caps' ver='*' node='http://profanity-im.github.io'/>" "<c hash='sha-1' xmlns='http://jabber.org/protocol/caps' ver='*' node='http://profanity-im.github.io'/>"
"</presence>" "</presence>"
)); ));
assert_true(prof_output_exact("Status set to chat (priority 0), \"Free to talk\"."));
} }
void void
@@ -192,14 +188,14 @@ presence_set_priority(void **state)
prof_input("/priority 25"); prof_input("/priority 25");
assert_true(prof_output_exact("Priority set to 25."));
assert_true(stbbr_received( assert_true(stbbr_received(
"<presence id='*'>" "<presence id='prof_presence_4'>"
"<priority>25</priority>" "<priority>25</priority>"
"<c hash='sha-1' xmlns='http://jabber.org/protocol/caps' ver='*' node='http://profanity-im.github.io'/>" "<c hash='sha-1' xmlns='http://jabber.org/protocol/caps' ver='*' node='http://profanity-im.github.io'/>"
"</presence>" "</presence>"
)); ));
assert_true(prof_output_exact("Priority set to 25."));
} }
void void
@@ -208,24 +204,24 @@ presence_includes_priority(void **state)
prof_connect(); prof_connect();
prof_input("/priority 25"); prof_input("/priority 25");
assert_true(prof_output_exact("Priority set to 25."));
assert_true(stbbr_received( assert_true(stbbr_received(
"<presence id='*'>" "<presence id='prof_presence_4'>"
"<priority>25</priority>" "<priority>25</priority>"
"<c hash='sha-1' xmlns='http://jabber.org/protocol/caps' ver='*' node='http://profanity-im.github.io'/>" "<c hash='sha-1' xmlns='http://jabber.org/protocol/caps' ver='*' node='http://profanity-im.github.io'/>"
"</presence>" "</presence>"
)); ));
assert_true(prof_output_exact("Priority set to 25."));
prof_input("/status set chat \"Free to talk\""); prof_input("/chat \"Free to talk\"");
assert_true(prof_output_exact("Status set to chat (priority 25), \"Free to talk\"."));
assert_true(stbbr_received( assert_true(stbbr_received(
"<presence id='*'>" "<presence id='prof_presence_5'>"
"<priority>25</priority>" "<priority>25</priority>"
"<show>chat</show>" "<show>chat</show>"
"<status>Free to talk</status>" "<status>Free to talk</status>"
"<c hash='sha-1' xmlns='http://jabber.org/protocol/caps' ver='*' node='http://profanity-im.github.io'/>" "<c hash='sha-1' xmlns='http://jabber.org/protocol/caps' ver='*' node='http://profanity-im.github.io'/>"
"</presence>" "</presence>"
)); ));
assert_true(prof_output_exact("Status set to chat (priority 25), \"Free to talk\"."));
} }
void void
@@ -233,26 +229,26 @@ presence_keeps_status(void **state)
{ {
prof_connect(); prof_connect();
prof_input("/status set chat \"Free to talk\""); prof_input("/chat \"Free to talk\"");
assert_true(prof_output_exact("Status set to chat (priority 0), \"Free to talk\"."));
assert_true(stbbr_received( assert_true(stbbr_received(
"<presence id='*'>" "<presence id='prof_presence_4'>"
"<show>chat</show>" "<show>chat</show>"
"<status>Free to talk</status>" "<status>Free to talk</status>"
"<c hash='sha-1' xmlns='http://jabber.org/protocol/caps' ver='*' node='http://profanity-im.github.io'/>" "<c hash='sha-1' xmlns='http://jabber.org/protocol/caps' ver='*' node='http://profanity-im.github.io'/>"
"</presence>" "</presence>"
)); ));
assert_true(prof_output_exact("Status set to chat (priority 0), \"Free to talk\"."));
prof_input("/priority 25"); prof_input("/priority 25");
assert_true(prof_output_exact("Priority set to 25."));
assert_true(stbbr_received( assert_true(stbbr_received(
"<presence id='*'>" "<presence id='prof_presence_5'>"
"<show>chat</show>" "<show>chat</show>"
"<status>Free to talk</status>" "<status>Free to talk</status>"
"<priority>25</priority>" "<priority>25</priority>"
"<c hash='sha-1' xmlns='http://jabber.org/protocol/caps' ver='*' node='http://profanity-im.github.io'/>" "<c hash='sha-1' xmlns='http://jabber.org/protocol/caps' ver='*' node='http://profanity-im.github.io'/>"
"</presence>" "</presence>"
)); ));
assert_true(prof_output_exact("Priority set to 25."));
} }
void void

View File

@@ -4,6 +4,7 @@
#include <string.h> #include <string.h>
#include <stabber.h> #include <stabber.h>
#include <expect.h>
#include "proftest.h" #include "proftest.h"
@@ -30,9 +31,8 @@ send_receipt_request(void **state)
prof_connect(); prof_connect();
// Register disco#info response for capabilities query (receipts support) stbbr_for_id("prof_caps_4",
stbbr_for_query("http://jabber.org/protocol/disco#info", "<iq from='buddy1@localhost/laptop' to='stabber@localhost' id='prof_caps_4' type='result'>"
"<iq from='buddy1@localhost/laptop' to='stabber@localhost' id='*' type='result'>"
"<query xmlns='http://jabber.org/protocol/disco#info' node='http://profanity-im.github.io#hAkb1xZdJV9BQpgGNw8zG5Xsals='>" "<query xmlns='http://jabber.org/protocol/disco#info' node='http://profanity-im.github.io#hAkb1xZdJV9BQpgGNw8zG5Xsals='>"
"<identity category='client' name='Profanity 0.5.0' type='console'/>" "<identity category='client' name='Profanity 0.5.0' type='console'/>"
"<feature var='urn:xmpp:receipts'/>" "<feature var='urn:xmpp:receipts'/>"

View File

@@ -4,14 +4,15 @@
#include <string.h> #include <string.h>
#include <stabber.h> #include <stabber.h>
#include <expect.h>
#include "proftest.h" #include "proftest.h"
void void
rooms_query(void **state) rooms_query(void **state)
{ {
stbbr_for_query("http://jabber.org/protocol/disco#items", stbbr_for_id("prof_confreq_4",
"<iq id='*' type='result' to='stabber@localhost/profanity' from='conference.localhost'>" "<iq id='prof_confreq_4' type='result' to='stabber@localhost/profanity' from='conference.localhost'>"
"<query xmlns='http://jabber.org/protocol/disco#items'>" "<query xmlns='http://jabber.org/protocol/disco#items'>"
"<item jid='chatroom@conference.localhost' name='A chat room'/>" "<item jid='chatroom@conference.localhost' name='A chat room'/>"
"<item jid='hangout@conference.localhost' name='Another chat room'/>" "<item jid='hangout@conference.localhost' name='Another chat room'/>"
@@ -26,8 +27,8 @@ rooms_query(void **state)
assert_true(prof_output_exact("chatroom@conference.localhost (A chat room)")); assert_true(prof_output_exact("chatroom@conference.localhost (A chat room)"));
assert_true(prof_output_exact("hangout@conference.localhost (Another chat room)")); assert_true(prof_output_exact("hangout@conference.localhost (Another chat room)"));
assert_true(stbbr_received( assert_true(stbbr_last_received(
"<iq id='*' to='conference.localhost' type='get'>" "<iq id='prof_confreq_4' to='conference.localhost' type='get'>"
"<query xmlns='http://jabber.org/protocol/disco#items'/>" "<query xmlns='http://jabber.org/protocol/disco#items'/>"
"</iq>" "</iq>"
)); ));

View File

@@ -4,6 +4,7 @@
#include <string.h> #include <string.h>
#include <stabber.h> #include <stabber.h>
#include <expect.h>
#include "proftest.h" #include "proftest.h"

View File

@@ -4,6 +4,7 @@
#include <string.h> #include <string.h>
#include <stabber.h> #include <stabber.h>
#include <expect.h>
#include "proftest.h" #include "proftest.h"