mirror of
https://gitlab.gnome.org/GNOME/calls.git
synced 2024-12-13 16:07:36 +00:00
e91d6258ad
15 ms does not seem to be enough time for phosh-antispam to be able to hang up before the incoming call screen shows up. In my experiement, 90 looks to be the minimum time needed for the incoming call to not show up. I put it to 100 ms just to be safe.
588 lines
16 KiB
C
588 lines
16 KiB
C
/*
|
|
* Copyright (C) 2021 Purism SPC
|
|
*
|
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
|
*
|
|
* Author: Evangelos Ribeiro Tzaras <devrtz@fortysixandtwo.eu>
|
|
*
|
|
*/
|
|
|
|
#include "calls-manager.h"
|
|
#include "calls-ringer.h"
|
|
#include "calls-ui-call-data.h"
|
|
#include "mock-call.h"
|
|
#include "mock-libfeedback.h"
|
|
#include "mock-contacts-provider.h"
|
|
|
|
#include <cmocka.h>
|
|
#include <setjmp.h>
|
|
|
|
|
|
/* mock libfeedback functions */
|
|
|
|
static gboolean
|
|
on_mock_timeout (gpointer user_data)
|
|
{
|
|
GTask *task;
|
|
GCancellable *cancellable;
|
|
|
|
if (!G_IS_TASK (user_data))
|
|
return G_SOURCE_REMOVE;
|
|
|
|
task = G_TASK (user_data);
|
|
cancellable = g_task_get_cancellable (task);
|
|
|
|
if (!g_cancellable_is_cancelled (cancellable))
|
|
g_task_return_boolean (task, TRUE);
|
|
|
|
return G_SOURCE_REMOVE;
|
|
}
|
|
|
|
static gboolean
|
|
on_check_task_cancelled (gpointer user_data)
|
|
{
|
|
GTask *task;
|
|
|
|
if (!G_IS_TASK (user_data))
|
|
return G_SOURCE_REMOVE;
|
|
|
|
task = G_TASK (user_data);
|
|
|
|
if (g_task_get_completed (task) || g_task_return_error_if_cancelled (task))
|
|
return G_SOURCE_REMOVE;
|
|
|
|
return G_SOURCE_CONTINUE;
|
|
}
|
|
|
|
|
|
gboolean
|
|
__wrap_lfb_init (const char *app_id,
|
|
GError **error)
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
void
|
|
__wrap_lfb_uninit (void)
|
|
{
|
|
return;
|
|
}
|
|
|
|
|
|
void
|
|
__wrap_lfb_event_set_feedback_profile (LfbEvent *self,
|
|
const char *profile)
|
|
{
|
|
return;
|
|
}
|
|
|
|
|
|
void
|
|
__wrap_lfb_event_trigger_feedback_async (LfbEvent *self,
|
|
GCancellable *cancellable,
|
|
GAsyncReadyCallback callback,
|
|
gpointer user_data)
|
|
{
|
|
GTask *trigger_task = g_task_new (self, cancellable, callback, user_data);
|
|
int mock_time_ms = mock_type (int);
|
|
|
|
g_timeout_add (mock_time_ms, on_mock_timeout, trigger_task);
|
|
g_idle_add (G_SOURCE_FUNC (on_check_task_cancelled), trigger_task);
|
|
}
|
|
|
|
|
|
gboolean
|
|
__wrap_lfb_event_trigger_feedback_finish (LfbEvent *self,
|
|
GAsyncResult *res,
|
|
GError **error)
|
|
{
|
|
GTask *task = G_TASK (res);
|
|
gboolean ret;
|
|
|
|
ret = g_task_propagate_boolean (task, error);
|
|
|
|
g_object_unref (task);
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
void
|
|
__wrap_lfb_event_end_feedback_async (LfbEvent *self,
|
|
GCancellable *cancellable,
|
|
GAsyncReadyCallback callback,
|
|
gpointer user_data)
|
|
{
|
|
GTask *trigger_task = g_task_new (self, cancellable, callback, user_data);
|
|
int mock_time_ms = mock_type (int);
|
|
|
|
g_timeout_add (mock_time_ms, on_mock_timeout, trigger_task);
|
|
}
|
|
|
|
|
|
gboolean
|
|
__wrap_lfb_event_end_feedback_finish (LfbEvent *self,
|
|
GAsyncResult *res,
|
|
GError **error)
|
|
{
|
|
GTask *task = G_TASK (res);
|
|
gboolean ret;
|
|
|
|
ret = g_task_propagate_boolean (G_TASK (res), error);
|
|
|
|
g_object_unref (task);
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
CallsContactsProvider *
|
|
__wrap_calls_contacts_provider_new (void)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
|
|
/* add or remove calls */
|
|
|
|
static void
|
|
add_call (CallsManager *manager,
|
|
CallsUiCallData *call)
|
|
{
|
|
g_assert (CALLS_IS_MANAGER (manager));
|
|
g_assert (CALLS_IS_UI_CALL_DATA (call));
|
|
|
|
g_signal_emit_by_name (manager, "ui-call-added", call, NULL);
|
|
}
|
|
|
|
|
|
static void
|
|
remove_call (CallsManager *manager,
|
|
CallsUiCallData *call)
|
|
{
|
|
g_assert (CALLS_IS_MANAGER (manager));
|
|
g_assert (CALLS_IS_UI_CALL_DATA (call));
|
|
|
|
g_signal_emit_by_name (manager, "ui-call-removed", call, NULL);
|
|
}
|
|
|
|
/* TestData setup and tear down */
|
|
typedef struct {
|
|
CallsManager *manager;
|
|
CallsRinger *ringer;
|
|
CallsMockCall *call_one;
|
|
CallsUiCallData *ui_call_one;
|
|
CallsMockCall *call_two;
|
|
CallsUiCallData *ui_call_two;
|
|
GMainLoop *loop;
|
|
} TestData;
|
|
|
|
|
|
static int
|
|
setup_test_data (void **state)
|
|
{
|
|
TestData *data = g_new0 (TestData, 1);
|
|
|
|
if (data == NULL)
|
|
return -1;
|
|
|
|
data->manager = calls_manager_get_default ();
|
|
data->ringer = calls_ringer_new ();
|
|
data->call_one = calls_mock_call_new ();
|
|
data->ui_call_one = calls_ui_call_data_new (CALLS_CALL (data->call_one), NULL);
|
|
data->call_two = calls_mock_call_new ();
|
|
data->ui_call_two = calls_ui_call_data_new (CALLS_CALL (data->call_two), NULL);
|
|
data->loop = g_main_loop_new (NULL, FALSE);
|
|
|
|
*state = data;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
tear_down_test_data (void **state)
|
|
{
|
|
TestData *data = *state;
|
|
|
|
g_object_unref (data->call_one);
|
|
g_object_unref (data->ui_call_one);
|
|
g_object_unref (data->call_two);
|
|
g_object_unref (data->ui_call_two);
|
|
g_object_unref (data->ringer);
|
|
g_main_loop_unref (data->loop);
|
|
|
|
g_free (data);
|
|
return 0;
|
|
}
|
|
|
|
/* t1: test_ringing_incoming_call */
|
|
static void
|
|
t1_on_ringer_call_accepted (CallsRinger *ringer,
|
|
GParamSpec *pspec,
|
|
gpointer user_data)
|
|
{
|
|
static guint test_phase = 0;
|
|
TestData *data = user_data;
|
|
|
|
switch (test_phase++) {
|
|
case 0: /* incoming call */
|
|
assert_true (calls_ringer_get_is_ringing (ringer));
|
|
calls_call_answer (CALLS_CALL (data->call_one));
|
|
break;
|
|
case 1: /* incoming call accepted */
|
|
assert_false (calls_ringer_get_is_ringing (ringer));
|
|
g_main_loop_quit ((GMainLoop *) data->loop);
|
|
break;
|
|
default:
|
|
g_assert_not_reached (); /* did not find equivalent cmocka assertion */
|
|
}
|
|
}
|
|
|
|
|
|
static void
|
|
test_ringing_accept_call (void **state)
|
|
{
|
|
TestData *data = *state;
|
|
|
|
assert_false (calls_ringer_get_is_ringing (data->ringer));
|
|
|
|
g_signal_connect (data->ringer,
|
|
"notify::ringing",
|
|
G_CALLBACK (t1_on_ringer_call_accepted),
|
|
data);
|
|
|
|
/* delay before completion of __wrap_lfb_event_trigger_feedback_async() */
|
|
will_return (__wrap_lfb_event_trigger_feedback_async, 10);
|
|
/* delay before completion of __wrap_lfb_event_end_feedback_async() */
|
|
will_return (__wrap_lfb_event_end_feedback_async, 10);
|
|
|
|
calls_call_set_state (CALLS_CALL (data->call_one), CALLS_CALL_STATE_INCOMING);
|
|
add_call (data->manager, data->ui_call_one);
|
|
|
|
/* main loop will quit in callback of notify::ring */
|
|
g_main_loop_run (data->loop);
|
|
|
|
remove_call (data->manager, data->ui_call_one);
|
|
assert_false (calls_ringer_get_is_ringing (data->ringer));
|
|
}
|
|
|
|
/* t2: test_ringing_hang_up_call */
|
|
static void
|
|
t2_on_ringer_call_hang_up (CallsRinger *ringer,
|
|
GParamSpec *pspec,
|
|
gpointer user_data)
|
|
{
|
|
static guint test_phase = 0;
|
|
TestData *data = user_data;
|
|
|
|
switch (test_phase++) {
|
|
case 0: /* incoming call */
|
|
assert_true (calls_ringer_get_is_ringing (ringer));
|
|
calls_call_hang_up (CALLS_CALL (data->call_one));
|
|
break;
|
|
case 1: /* incoming call hung up */
|
|
assert_false (calls_ringer_get_is_ringing (ringer));
|
|
g_main_loop_quit ((GMainLoop *) data->loop);
|
|
break;
|
|
default:
|
|
g_assert_not_reached (); /* did not find equivalent cmocka assertion */
|
|
}
|
|
}
|
|
|
|
|
|
static void
|
|
test_ringing_hang_up_call (void **state)
|
|
{
|
|
TestData *data = *state;
|
|
|
|
assert_false (calls_ringer_get_is_ringing (data->ringer));
|
|
|
|
g_signal_connect (data->ringer,
|
|
"notify::ringing",
|
|
G_CALLBACK (t2_on_ringer_call_hang_up),
|
|
data);
|
|
|
|
/* delay before completion of __wrap_lfb_event_trigger_feedback_async() */
|
|
will_return (__wrap_lfb_event_trigger_feedback_async, 10);
|
|
/* delay before completion of __wrap_lfb_event_end_feedback_async() */
|
|
will_return (__wrap_lfb_event_end_feedback_async, 10);
|
|
|
|
calls_call_set_state (CALLS_CALL (data->call_one), CALLS_CALL_STATE_INCOMING);
|
|
add_call (data->manager, data->ui_call_one);
|
|
|
|
/* main loop will quit in callback of notify::ring */
|
|
g_main_loop_run (data->loop);
|
|
|
|
remove_call (data->manager, data->ui_call_one);
|
|
assert_false (calls_ringer_get_is_ringing (data->ringer));
|
|
}
|
|
|
|
|
|
/* t3: test_ringing_hang_up_call_ringer_cancelled */
|
|
static gboolean
|
|
t3_on_ringer_timeout (gpointer user_data)
|
|
{
|
|
TestData *data = user_data;
|
|
static guint test_phase = 0;
|
|
|
|
assert_false (calls_ringer_get_is_ringing (data->ringer));
|
|
if (test_phase == 0) {
|
|
calls_call_hang_up (CALLS_CALL (data->call_one));
|
|
test_phase++;
|
|
return G_SOURCE_CONTINUE;
|
|
}
|
|
|
|
g_main_loop_quit ((GMainLoop *) data->loop);
|
|
|
|
return G_SOURCE_REMOVE;
|
|
}
|
|
|
|
|
|
/** this test should use cancellable code path by hanging up before
|
|
* event triggering completes
|
|
*/
|
|
static void
|
|
test_ringing_hang_up_call_ringer_cancelled (void **state)
|
|
{
|
|
TestData *data = *state;
|
|
|
|
assert_false (calls_ringer_get_is_ringing (data->ringer));
|
|
|
|
/* delay before completion of __wrap_lfb_event_trigger_feedback_async() */
|
|
will_return (__wrap_lfb_event_trigger_feedback_async, 50);
|
|
|
|
calls_call_set_state (CALLS_CALL (data->call_one), CALLS_CALL_STATE_INCOMING);
|
|
add_call (data->manager, data->ui_call_one);
|
|
|
|
/** this timeout needs to be longer than the delay for activating the UI/ringer,
|
|
* see DELAY_UI_MS in calls-ui-call-data.c
|
|
*/
|
|
g_timeout_add (105, G_SOURCE_FUNC (t3_on_ringer_timeout), data);
|
|
|
|
/* main loop will quit in t3_on_ringer_timeout() */
|
|
g_main_loop_run (data->loop);
|
|
|
|
remove_call (data->manager, data->ui_call_one);
|
|
assert_false (calls_ringer_get_is_ringing (data->ringer));
|
|
}
|
|
|
|
/* t4: test_ringing_silence_call */
|
|
static void
|
|
t4_on_ringer_call_silence (CallsRinger *ringer,
|
|
GParamSpec *pspec,
|
|
gpointer user_data)
|
|
{
|
|
static guint test_phase = 0;
|
|
TestData *data = user_data;
|
|
|
|
switch (test_phase++) {
|
|
case 0: /* incoming call */
|
|
assert_true (calls_ringer_get_is_ringing (ringer));
|
|
calls_ui_call_data_silence_ring (data->ui_call_one);
|
|
assert_true (calls_ui_call_data_get_silenced (data->ui_call_one));
|
|
break;
|
|
case 1: /* incoming call hung up */
|
|
assert_false (calls_ringer_get_is_ringing (ringer));
|
|
g_main_loop_quit ((GMainLoop *) data->loop);
|
|
break;
|
|
default:
|
|
g_assert_not_reached (); /* did not find equivalent cmocka assertion */
|
|
}
|
|
}
|
|
|
|
|
|
static void
|
|
test_ringing_silence_call (void **state)
|
|
{
|
|
TestData *data = *state;
|
|
|
|
assert_false (calls_ringer_get_is_ringing (data->ringer));
|
|
|
|
g_signal_connect (data->ringer,
|
|
"notify::ringing",
|
|
G_CALLBACK (t4_on_ringer_call_silence),
|
|
data);
|
|
|
|
/* delay before completion of __wrap_lfb_event_trigger_feedback_async() */
|
|
will_return (__wrap_lfb_event_trigger_feedback_async, 10);
|
|
/* delay before completion of __wrap_lfb_event_end_feedback_async() */
|
|
will_return (__wrap_lfb_event_end_feedback_async, 10);
|
|
|
|
calls_call_set_state (CALLS_CALL (data->call_one), CALLS_CALL_STATE_INCOMING);
|
|
add_call (data->manager, data->ui_call_one);
|
|
|
|
/* main loop will quit in callback of notify::ring */
|
|
g_main_loop_run (data->loop);
|
|
|
|
remove_call (data->manager, data->ui_call_one);
|
|
assert_false (calls_ringer_get_is_ringing (data->ringer));
|
|
}
|
|
|
|
|
|
/* t5: test_ringing_multiple_call */
|
|
static gboolean
|
|
t5_remove_calls (gpointer user_data)
|
|
{
|
|
static guint test_phase = 0;
|
|
TestData *data = user_data;
|
|
|
|
if (test_phase == 0) {
|
|
remove_call (data->manager, data->ui_call_one);
|
|
test_phase++;
|
|
return G_SOURCE_CONTINUE;
|
|
}
|
|
|
|
assert_true (calls_ringer_get_is_ringing (data->ringer));
|
|
remove_call (data->manager, data->ui_call_two);
|
|
|
|
return G_SOURCE_REMOVE;
|
|
}
|
|
|
|
|
|
static void
|
|
t5_on_ringer_multiple_calls (CallsRinger *ringer,
|
|
GParamSpec *pspec,
|
|
gpointer user_data)
|
|
{
|
|
static guint test_phase = 0;
|
|
TestData *data = user_data;
|
|
|
|
switch (test_phase++) {
|
|
case 0: /* add second call, and schedule call removal */
|
|
assert_true (calls_ringer_get_is_ringing (ringer));
|
|
add_call (data->manager, data->ui_call_two);
|
|
g_timeout_add (25, t5_remove_calls, data);
|
|
break;
|
|
case 1: /* both calls should be removed now */
|
|
assert_false (calls_ringer_get_is_ringing (ringer));
|
|
g_main_loop_quit ((GMainLoop *) data->loop);
|
|
break;
|
|
default:
|
|
g_assert_not_reached (); /* did not find equivalent cmocka assertion */
|
|
}
|
|
}
|
|
|
|
|
|
static void
|
|
test_ringing_multiple_calls (void **state)
|
|
{
|
|
TestData *data = *state;
|
|
|
|
assert_false (calls_ringer_get_is_ringing (data->ringer));
|
|
|
|
g_signal_connect (data->ringer,
|
|
"notify::ringing",
|
|
G_CALLBACK (t5_on_ringer_multiple_calls),
|
|
data);
|
|
|
|
/* delay before completion of __wrap_lfb_event_trigger_feedback_async() */
|
|
will_return (__wrap_lfb_event_trigger_feedback_async, 10);
|
|
/* delay before completion of __wrap_lfb_event_end_feedback_async() */
|
|
will_return (__wrap_lfb_event_end_feedback_async, 10);
|
|
|
|
calls_call_set_state (CALLS_CALL (data->call_one), CALLS_CALL_STATE_INCOMING);
|
|
add_call (data->manager, data->ui_call_one);
|
|
|
|
/* main loop will quit in callback of notify::ring */
|
|
g_main_loop_run (data->loop);
|
|
|
|
assert_false (calls_ringer_get_is_ringing (data->ringer));
|
|
}
|
|
|
|
|
|
static void
|
|
t6_on_ringer_multiple_calls_with_restart (CallsRinger *ringer,
|
|
GParamSpec *pspec,
|
|
gpointer user_data)
|
|
{
|
|
static guint test_phase = 0;
|
|
TestData *data = user_data;
|
|
|
|
switch (test_phase++) {
|
|
case 0:
|
|
assert_true (calls_ringer_get_is_ringing (ringer));
|
|
assert_false (calls_ringer_get_ring_is_quiet (ringer));
|
|
|
|
calls_call_answer (CALLS_CALL (data->call_one));
|
|
break;
|
|
case 1:
|
|
assert_true (calls_ringer_get_is_ringing (ringer));
|
|
assert_true (calls_ringer_get_ring_is_quiet (ringer));
|
|
|
|
calls_call_hang_up (CALLS_CALL (data->call_one));
|
|
break;
|
|
case 2:
|
|
assert_true (calls_ringer_get_is_ringing (ringer));
|
|
assert_false (calls_ringer_get_ring_is_quiet (ringer));
|
|
|
|
calls_call_hang_up (CALLS_CALL (data->call_two));
|
|
break;
|
|
case 3:
|
|
assert_false (calls_ringer_get_is_ringing (ringer));
|
|
assert_false (calls_ringer_get_ring_is_quiet (ringer));
|
|
|
|
g_main_loop_quit (data->loop);
|
|
break;
|
|
default:
|
|
g_assert_not_reached (); /* did not find equivalent cmocka assertion */
|
|
}
|
|
}
|
|
|
|
static void
|
|
test_ringing_multiple_calls_with_restart (void **state)
|
|
{
|
|
TestData *data = *state;
|
|
|
|
assert_false (calls_ringer_get_is_ringing (data->ringer));
|
|
|
|
g_signal_connect (data->ringer,
|
|
"notify::ringing",
|
|
G_CALLBACK (t6_on_ringer_multiple_calls_with_restart),
|
|
data);
|
|
|
|
/* delay before completion of __wrap_lfb_event_trigger_feedback_async() */
|
|
will_return_always (__wrap_lfb_event_trigger_feedback_async, 10);
|
|
/* delay before completion of __wrap_lfb_event_end_feedback_async() */
|
|
will_return_always (__wrap_lfb_event_end_feedback_async, 10);
|
|
|
|
calls_call_set_state (CALLS_CALL (data->call_one), CALLS_CALL_STATE_INCOMING);
|
|
add_call (data->manager, data->ui_call_one);
|
|
calls_call_set_state (CALLS_CALL (data->call_two), CALLS_CALL_STATE_INCOMING);
|
|
add_call (data->manager, data->ui_call_two);
|
|
|
|
/* main loop will quit in callback of notify::ring */
|
|
g_main_loop_run (data->loop);
|
|
|
|
assert_false (calls_ringer_get_is_ringing (data->ringer));
|
|
}
|
|
|
|
int
|
|
main (int argc,
|
|
char *argv[])
|
|
{
|
|
const struct CMUnitTest tests[] = {
|
|
cmocka_unit_test_setup_teardown (test_ringing_accept_call,
|
|
setup_test_data,
|
|
tear_down_test_data),
|
|
cmocka_unit_test_setup_teardown (test_ringing_hang_up_call,
|
|
setup_test_data,
|
|
tear_down_test_data),
|
|
cmocka_unit_test_setup_teardown (test_ringing_hang_up_call_ringer_cancelled,
|
|
setup_test_data,
|
|
tear_down_test_data),
|
|
cmocka_unit_test_setup_teardown (test_ringing_silence_call,
|
|
setup_test_data,
|
|
tear_down_test_data),
|
|
cmocka_unit_test_setup_teardown (test_ringing_multiple_calls,
|
|
setup_test_data,
|
|
tear_down_test_data),
|
|
cmocka_unit_test_setup_teardown (test_ringing_multiple_calls_with_restart,
|
|
setup_test_data,
|
|
tear_down_test_data)
|
|
};
|
|
|
|
return cmocka_run_group_tests (tests, NULL, NULL);
|
|
}
|
|
|