[vlc-devel] [PATCH 2/2] core: player: add tests
Thomas Guillem
thomas at gllm.fr
Mon Oct 22 18:11:08 CEST 2018
Test main player functions, but also decoder.c/es_out.c/input.c as a
consequence.
This test improves the test coverage of src/input by 20% (decoder.c coverage
goes from 0 to 60%).
---
test/Makefile.am | 3 +
test/src/input/player.c | 1662 +++++++++++++++++++++++++++++++++++++++
2 files changed, 1665 insertions(+)
create mode 100644 test/src/input/player.c
diff --git a/test/Makefile.am b/test/Makefile.am
index e595065564..126d6d005e 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -26,6 +26,7 @@ check_PROGRAMS = \
test_src_misc_variables \
test_src_input_stream \
test_src_input_stream_fifo \
+ test_src_input_player \
test_src_interface_dialog \
test_src_misc_bits \
test_src_misc_epg \
@@ -112,6 +113,8 @@ test_src_crypto_update_SOURCES = src/crypto/update.c
test_src_crypto_update_LDADD = $(LIBVLCCORE) $(GCRYPT_LIBS)
test_src_input_stream_SOURCES = src/input/stream.c
test_src_input_stream_LDADD = $(LIBVLCCORE) $(LIBVLC)
+test_src_input_player_SOURCES = src/input/player.c
+test_src_input_player_LDADD = $(LIBVLCCORE) $(LIBVLC) $(LIBM)
test_src_input_stream_net_SOURCES = src/input/stream.c
test_src_input_stream_net_CFLAGS = $(AM_CFLAGS) -DTEST_NET
test_src_input_stream_net_LDADD = $(LIBVLCCORE) $(LIBVLC)
diff --git a/test/src/input/player.c b/test/src/input/player.c
new file mode 100644
index 0000000000..2023c72568
--- /dev/null
+++ b/test/src/input/player.c
@@ -0,0 +1,1662 @@
+/*****************************************************************************
+ * player.c: test vlc_player_t API
+ *****************************************************************************
+ * Copyright (C) 2018 VLC authors and VideoLAN
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+#include "../../libvlc/test.h"
+#include "../lib/libvlc_internal.h"
+
+#include <math.h>
+
+#include <vlc_common.h>
+#include <vlc_player.h>
+#include <vlc_vector.h>
+
+struct report_position
+{
+ vlc_tick_t time;
+ float pos;
+};
+
+struct report_track_list
+{
+ enum vlc_player_list_action action;
+ struct vlc_player_track *track;
+};
+
+struct report_track_selection
+{
+ vlc_es_id_t *unselected_id;
+ vlc_es_id_t *selected_id;
+};
+
+struct report_program_list
+{
+ enum vlc_player_list_action action;
+ struct vlc_player_program *prgm;
+};
+
+struct report_program_selection
+{
+ int unselected_id;
+ int selected_id;
+};
+
+struct report_chapter_selection
+{
+ size_t title_idx;
+ size_t chapter_idx;
+};
+
+struct report_signal
+{
+ float quality;
+ float strength;
+};
+
+struct report_vout_list
+{
+ enum vlc_player_list_action action;
+ vout_thread_t *vout;
+};
+
+struct report_media_subitems
+{
+ size_t count;
+ input_item_t **items;
+};
+
+#define REPORT_LIST \
+ X(input_item_t *, on_current_media_changed) \
+ X(enum vlc_player_state, on_state_changed) \
+ X(enum vlc_player_error, on_error_changed) \
+ X(float, on_buffering_changed) \
+ X(float, on_rate_changed) \
+ X(int, on_capabilities_changed) \
+ X(struct report_position, on_position_changed) \
+ X(vlc_tick_t, on_length_changed) \
+ X(struct report_track_list, on_track_list_changed) \
+ X(struct report_track_selection, on_track_selection_changed) \
+ X(struct report_program_list, on_program_list_changed) \
+ X(struct report_program_selection, on_program_selection_changed) \
+ X(vlc_player_title_list *, on_titles_changed) \
+ X(size_t, on_title_selection_changed) \
+ X(struct report_chapter_selection, on_chapter_selection_changed) \
+ X(vlc_tick_t, on_audio_delay_changed) \
+ X(vlc_tick_t, on_subtitle_delay_changed) \
+ X(bool, on_recording_changed) \
+ X(struct report_signal, on_signal_changed) \
+ X(struct input_stats_t, on_statistics_changed) \
+ X(struct report_vout_list, on_vout_list_changed) \
+ X(input_item_t *, on_media_meta_changed) \
+ X(input_item_t *, on_media_epg_changed) \
+ X(struct report_media_subitems, on_media_subitems_changed) \
+
+#define X(type, name) typedef struct VLC_VECTOR(type) vec_##name;
+REPORT_LIST
+#undef X
+
+#define X(type, name) vec_##name name;
+struct reports
+{
+REPORT_LIST
+};
+#undef X
+
+static inline void
+reports_init(struct reports *report)
+{
+#define X(type, name) vlc_vector_init(&report->name);
+REPORT_LIST
+#undef X
+}
+
+struct media_params
+{
+ vlc_tick_t length;
+ size_t track_count[DATA_ES];
+ size_t program_count;
+
+ bool video_packetized, audio_packetized, sub_packetized;
+
+ size_t title_count;
+ size_t chapter_count;
+
+ bool can_seek;
+ bool can_pause;
+ bool error;
+ bool null_names;
+};
+
+#define DEFAULT_MEDIA_PARAMS(param_length) { \
+ .length = param_length, \
+ .track_count = { \
+ [VIDEO_ES] = 1, \
+ [AUDIO_ES] = 1, \
+ [SPU_ES] = 1, \
+ }, \
+ .program_count = 0, \
+ .video_packetized = true, .audio_packetized = true, .sub_packetized = true,\
+ .title_count = 0, \
+ .chapter_count = 0, \
+ .can_seek = true, \
+ .can_pause = true, \
+ .error = false, \
+ .null_names = false, \
+}
+
+struct ctx
+{
+ vlc_player_t *player;
+ struct VLC_VECTOR(input_item_t *) next_medias;
+ struct VLC_VECTOR(input_item_t *) played_medias;
+
+ size_t program_switch_count;
+ size_t extra_start_count;
+ struct media_params params;
+ float rate;
+
+ vlc_cond_t wait;
+ struct reports report;
+};
+
+static struct ctx *
+get_ctx(vlc_player_t *player, void *data)
+{
+ assert(data);
+ struct ctx *ctx = data;
+ assert(player == ctx->player);
+ return ctx;
+}
+
+static void
+ctx_destroy(struct ctx *ctx)
+{
+#define X(type, name) vlc_vector_destroy(&ctx->report.name);
+REPORT_LIST
+#undef X
+}
+
+static input_item_t *
+player_get_next(vlc_player_t *player, void *data)
+{
+ struct ctx *ctx = get_ctx(player, data);
+ input_item_t *next_media;
+ if (ctx->next_medias.size > 0)
+ {
+ next_media = ctx->next_medias.data[0];
+ vlc_vector_remove(&ctx->next_medias, 0);
+
+ input_item_Hold(next_media);
+ bool success = vlc_vector_push(&ctx->played_medias, next_media);
+ assert(success);
+ }
+ else
+ next_media = NULL;
+ return next_media;
+}
+
+#define VEC_PUSH(vec, item) do { \
+ bool success = vlc_vector_push(&ctx->report.vec, item); \
+ assert(success); \
+ vlc_cond_signal(&ctx->wait); \
+} while(0)
+
+static void
+player_on_current_media_changed(vlc_player_t *player,
+ input_item_t *new_media, void *data)
+{
+ struct ctx *ctx = get_ctx(player, data);
+ if (new_media)
+ input_item_Hold(new_media);
+ VEC_PUSH(on_current_media_changed, new_media);
+}
+
+static void
+player_on_state_changed(vlc_player_t *player, enum vlc_player_state state,
+ void *data)
+{
+ struct ctx *ctx = get_ctx(player, data);
+ VEC_PUSH(on_state_changed, state);
+}
+
+static void
+player_on_error_changed(vlc_player_t *player, enum vlc_player_error error,
+ void *data)
+{
+ struct ctx *ctx = get_ctx(player, data);
+ VEC_PUSH(on_error_changed, error);
+}
+
+static void
+player_on_buffering_changed(vlc_player_t *player, float new_buffering,
+ void *data)
+{
+ struct ctx *ctx = get_ctx(player, data);
+ VEC_PUSH(on_buffering_changed, new_buffering);
+}
+
+static void
+player_on_rate_changed(vlc_player_t *player, float new_rate, void *data)
+{
+ struct ctx *ctx = get_ctx(player, data);
+ VEC_PUSH(on_rate_changed, new_rate);
+}
+
+static void
+player_on_capabilities_changed(vlc_player_t *player, int new_caps,
+ void *data)
+{
+ struct ctx *ctx = get_ctx(player, data);
+ VEC_PUSH(on_capabilities_changed, new_caps);
+}
+
+static void
+player_on_position_changed(vlc_player_t *player, vlc_tick_t time,
+ float pos, void *data)
+{
+ struct ctx *ctx = get_ctx(player, data);
+ struct report_position report = {
+ .time = time,
+ .pos = pos,
+ };
+ VEC_PUSH(on_position_changed, report);
+}
+
+static void
+player_on_length_changed(vlc_player_t *player, vlc_tick_t new_length,
+ void *data)
+{
+ struct ctx *ctx = get_ctx(player, data);
+ VEC_PUSH(on_length_changed, new_length);
+}
+
+static void
+player_on_track_list_changed(vlc_player_t *player,
+ enum vlc_player_list_action action,
+ const struct vlc_player_track *track,
+ void *data)
+{
+ struct ctx *ctx = get_ctx(player, data);
+ struct report_track_list report = {
+ .action = action,
+ .track = vlc_player_track_Dup(track),
+ };
+ assert(report.track);
+ VEC_PUSH(on_track_list_changed, report);
+}
+
+static void
+player_on_track_selection_changed(vlc_player_t *player,
+ vlc_es_id_t *unselected_id,
+ vlc_es_id_t *selected_id, void *data)
+{
+ struct ctx *ctx = get_ctx(player, data);
+ struct report_track_selection report = {
+ .unselected_id = unselected_id ? vlc_es_id_Hold(unselected_id) : NULL,
+ .selected_id = selected_id ? vlc_es_id_Hold(selected_id) : NULL,
+ };
+ assert(!!unselected_id == !!report.unselected_id);
+ assert(!!selected_id == !!report.selected_id);
+ VEC_PUSH(on_track_selection_changed, report);
+}
+
+static void
+player_on_program_list_changed(vlc_player_t *player,
+ enum vlc_player_list_action action,
+ const struct vlc_player_program *prgm,
+ void *data)
+{
+ struct ctx *ctx = get_ctx(player, data);
+ struct report_program_list report = {
+ .action = action,
+ .prgm = vlc_player_program_Dup(prgm)
+ };
+ assert(report.prgm);
+ VEC_PUSH(on_program_list_changed, report);
+}
+
+static void
+player_on_program_selection_changed(vlc_player_t *player,
+ int unselected_id, int selected_id,
+ void *data)
+{
+ struct ctx *ctx = get_ctx(player, data);
+ struct report_program_selection report = {
+ .unselected_id = unselected_id,
+ .selected_id = selected_id,
+ };
+ VEC_PUSH(on_program_selection_changed, report);
+}
+
+static void
+player_on_titles_changed(vlc_player_t *player,
+ vlc_player_title_list *titles, void *data)
+{
+ struct ctx *ctx = get_ctx(player, data);
+ if (titles)
+ vlc_player_title_list_Hold(titles);
+ VEC_PUSH(on_titles_changed, titles);
+}
+
+static void
+player_on_title_selection_changed(vlc_player_t *player,
+ const struct vlc_player_title *new_title,
+ size_t new_idx, void *data)
+{
+ struct ctx *ctx = get_ctx(player, data);
+ VEC_PUSH(on_title_selection_changed, new_idx);
+ (void) new_title;
+}
+
+static void
+player_on_chapter_selection_changed(vlc_player_t *player,
+ const struct vlc_player_title *title,
+ size_t title_idx,
+ const struct vlc_player_chapter *chapter,
+ size_t chapter_idx, void *data)
+{
+ struct ctx *ctx = get_ctx(player, data);
+ struct report_chapter_selection report = {
+ .title_idx = title_idx,
+ .chapter_idx = chapter_idx,
+ };
+ VEC_PUSH(on_chapter_selection_changed, report);
+ (void) title;
+ (void) chapter;
+}
+
+static void
+player_on_audio_delay_changed(vlc_player_t *player, vlc_tick_t new_delay,
+ void *data)
+{
+ struct ctx *ctx = get_ctx(player, data);
+ VEC_PUSH(on_audio_delay_changed, new_delay);
+}
+
+static void
+player_on_subtitle_delay_changed(vlc_player_t *player, vlc_tick_t new_delay,
+ void *data)
+{
+ struct ctx *ctx = get_ctx(player, data);
+ VEC_PUSH(on_subtitle_delay_changed, new_delay);
+}
+
+static void
+player_on_recording_changed(vlc_player_t *player, bool recording, void *data)
+{
+ struct ctx *ctx = get_ctx(player, data);
+ VEC_PUSH(on_recording_changed, recording);
+}
+
+static void
+player_on_signal_changed(vlc_player_t *player,
+ float quality, float strength, void *data)
+{
+ struct ctx *ctx = get_ctx(player, data);
+ struct report_signal report = {
+ .quality = quality,
+ .strength = strength,
+ };
+ VEC_PUSH(on_signal_changed, report);
+}
+
+static void
+player_on_statistics_changed(vlc_player_t *player,
+ const struct input_stats_t *stats, void *data)
+{
+ struct ctx *ctx = get_ctx(player, data);
+ struct input_stats_t dup = *stats;
+ VEC_PUSH(on_statistics_changed, dup);
+}
+
+static void
+player_on_vout_list_changed(vlc_player_t *player,
+ enum vlc_player_list_action action,
+ vout_thread_t *vout, void *data)
+{
+ struct ctx *ctx = get_ctx(player, data);
+ struct report_vout_list report = {
+ .action = action,
+ .vout = vout,
+ };
+ vlc_object_hold(vout);
+ VEC_PUSH(on_vout_list_changed, report);
+}
+
+static void
+player_on_media_meta_changed(vlc_player_t *player, input_item_t *media,
+ void *data)
+{
+ struct ctx *ctx = get_ctx(player, data);
+ input_item_Hold(media);
+ VEC_PUSH(on_media_meta_changed, media);
+}
+
+static void
+player_on_media_epg_changed(vlc_player_t *player, input_item_t *media,
+ void *data)
+{
+ struct ctx *ctx = get_ctx(player, data);
+ input_item_Hold(media);
+ VEC_PUSH(on_media_epg_changed, media);
+}
+
+static void
+player_on_media_subitems_changed(vlc_player_t *player, input_item_t *media,
+ input_item_node_t *subitems, void *data)
+{
+ (void) media;
+ struct ctx *ctx = get_ctx(player, data);
+
+ struct report_media_subitems report = {
+ .count = subitems->i_children,
+ .items = vlc_alloc(subitems->i_children, sizeof(input_item_t)),
+ };
+ assert(report.items);
+ for (int i = 0; i < subitems->i_children; ++i)
+ report.items[i] = input_item_Hold(subitems->pp_children[i]->p_item);
+ VEC_PUSH(on_media_subitems_changed, report);
+}
+
+#define VEC_LAST(vec) (vec)->data[(vec)->size - 1]
+#define assert_position(ctx, report) do { \
+ assert(fabs((report)->pos - (report)->time / (float) ctx->params.length) < 0.001); \
+} while (0)
+
+#define wait_state(ctx, state) do { \
+ vec_on_state_changed *vec = &ctx->report.on_state_changed; \
+ while (vec->size == 0 || VEC_LAST(vec) != state) \
+ vlc_player_CondWait(player, &ctx->wait); \
+} while(0)
+
+#define assert_state(ctx, state) do { \
+ vec_on_state_changed *vec = &ctx->report.on_state_changed; \
+ assert(VEC_LAST(vec) == state); \
+} while(0)
+
+#define assert_normal_state(ctx) do { \
+ vec_on_state_changed *vec = &ctx->report.on_state_changed; \
+ assert(vec->size >= 4); \
+ assert(vec->data[vec->size - 4] == VLC_PLAYER_STATE_STARTED); \
+ assert(vec->data[vec->size - 3] == VLC_PLAYER_STATE_PLAYING); \
+ assert(vec->data[vec->size - 2] == VLC_PLAYER_STATE_STOPPING); \
+ assert(vec->data[vec->size - 1] == VLC_PLAYER_STATE_STOPPED); \
+} while(0)
+
+static void
+ctx_reset(struct ctx *ctx)
+{
+#define FOREACH_VEC(item, vec) vlc_vector_foreach(item, &ctx->report.vec)
+#define CLEAN_MEDIA_VEC(vec) do { \
+ input_item_t *media; \
+ FOREACH_VEC(media, vec) { \
+ if (media) \
+ input_item_Release(media); \
+ } \
+} while(0)
+
+
+ CLEAN_MEDIA_VEC(on_current_media_changed);
+ CLEAN_MEDIA_VEC(on_media_meta_changed);
+ CLEAN_MEDIA_VEC(on_media_epg_changed);
+
+ {
+ struct report_track_list report;
+ FOREACH_VEC(report, on_track_list_changed)
+ vlc_player_track_Delete(report.track);
+ }
+
+ {
+ struct report_track_selection report;
+ FOREACH_VEC(report, on_track_selection_changed)
+ {
+ if (report.unselected_id)
+ vlc_es_id_Release(report.unselected_id);
+ if (report.selected_id)
+ vlc_es_id_Release(report.selected_id);
+ }
+ }
+
+ {
+ struct report_program_list report;
+ FOREACH_VEC(report, on_program_list_changed)
+ vlc_player_program_Delete(report.prgm);
+ }
+
+ {
+ vlc_player_title_list *titles;
+ FOREACH_VEC(titles, on_titles_changed)
+ {
+ if (titles)
+ vlc_player_title_list_Release(titles);
+ }
+ }
+
+ {
+ struct report_vout_list report;
+ FOREACH_VEC(report, on_vout_list_changed)
+ vlc_object_release(report.vout);
+ }
+
+ {
+ struct report_media_subitems report;
+ FOREACH_VEC(report, on_media_subitems_changed)
+ {
+ for (size_t i = 0; i < report.count; ++i)
+ input_item_Release(report.items[i]);
+ free(report.items);
+ }
+ }
+#undef CLEAN_MEDIA_VEC
+#undef FOREACH_VEC
+
+#define X(type, name) vlc_vector_clear(&ctx->report.name);
+REPORT_LIST
+#undef X
+
+ input_item_t *media;
+ vlc_vector_foreach(media, &ctx->next_medias)
+ {
+ assert(media);
+ input_item_Release(media);
+ }
+ vlc_vector_clear(&ctx->next_medias);
+
+ vlc_vector_foreach(media, &ctx->played_medias)
+ if (media)
+ input_item_Release(media);
+ vlc_vector_clear(&ctx->played_medias);
+
+ ctx->extra_start_count = 0;
+ ctx->program_switch_count = 1;
+ ctx->rate = 1.f;
+};
+
+static input_item_t *
+create_mock_media(const char *name, const struct media_params *params)
+{
+ assert(params);
+ char *url;
+ int ret = asprintf(&url,
+ "mock://video_track_count=%zu;audio_track_count=%zu;sub_track_count=%zu;"
+ "program_count=%zu;video_packetized=%d;audio_packetized=%d;"
+ "sub_packetized=%d;length=%"PRId64";title_count=%zu;chapter_count=%zu;"
+ "can_seek=%d;can_pause=%d;error=%d;null_names=%d",
+ params->track_count[VIDEO_ES], params->track_count[AUDIO_ES],
+ params->track_count[SPU_ES], params->program_count,
+ params->video_packetized, params->audio_packetized,
+ params->sub_packetized, params->length, params->title_count,
+ params->chapter_count, params->can_seek, params->can_pause,
+ params->error, params->null_names);
+ assert(ret != -1);
+
+ input_item_t *item = input_item_New(url, name);
+ assert(item);
+ free(url);
+ return item;
+}
+
+static void
+player_set_current_mock_media(struct ctx *ctx, const char *name,
+ const struct media_params *params, bool ignored)
+{
+ input_item_t *media;
+ if (name)
+ {
+ assert(params);
+
+ media = create_mock_media(name, params);
+ assert(media);
+
+ ctx->params = *params;
+ if (ctx->params.chapter_count > 0 && ctx->params.title_count == 0)
+ ctx->params.title_count = 1;
+ if (ctx->params.program_count == 0)
+ ctx->params.program_count = 1;
+ }
+ else
+ media = NULL;
+ int ret = vlc_player_SetCurrentMedia(ctx->player, media);
+ assert(ret == VLC_SUCCESS);
+
+ if (ignored)
+ {
+ if (media)
+ input_item_Release(media);
+ }
+ else
+ {
+ bool success = vlc_vector_push(&ctx->played_medias, media);
+ assert(success);
+ }
+}
+
+static void
+player_set_next_mock_media(struct ctx *ctx, const char *name,
+ const struct media_params *params)
+{
+ if (vlc_player_GetCurrentMedia(ctx->player) == NULL)
+ {
+ assert(ctx->played_medias.size == 0);
+ player_set_current_mock_media(ctx, name, params, false);
+ }
+ else
+ {
+ input_item_t *media = create_mock_media(name, params);
+ assert(media);
+
+ assert(ctx->played_medias.size > 0);
+ bool success = vlc_vector_push(&ctx->next_medias, media);
+ assert(success);
+ }
+}
+
+static void
+player_set_rate(struct ctx *ctx, float rate)
+{
+ vlc_player_ChangeRate(ctx->player, rate);
+ ctx->rate = rate;
+}
+
+static void
+player_start(struct ctx *ctx)
+{
+ int ret = vlc_player_Start(ctx->player);
+ assert(ret == VLC_SUCCESS);
+}
+
+static void
+test_end_prestop_rate(struct ctx *ctx)
+{
+ if (ctx->rate != 1.0f)
+ {
+ vec_on_rate_changed *vec = &ctx->report.on_rate_changed;
+ while (vec->size == 0)
+ vlc_player_CondWait(ctx->player, &ctx->wait);
+ assert(vec->size > 0);
+ assert(VEC_LAST(vec) == ctx->rate);
+ }
+
+}
+
+static void
+test_end_prestop_length(struct ctx *ctx)
+{
+ vlc_player_t *player = ctx->player;
+ vec_on_length_changed *vec = &ctx->report.on_length_changed;
+ while (vec->size != ctx->played_medias.size)
+ vlc_player_CondWait(ctx->player, &ctx->wait);
+ for (size_t i = 0; i < vec->size; ++i)
+ assert(vec->data[i] == ctx->params.length);
+ assert(ctx->params.length == vlc_player_GetLength(player));
+}
+
+static void
+test_end_prestop_capabilities(struct ctx *ctx)
+{
+ vlc_player_t *player = ctx->player;
+ vec_on_capabilities_changed *vec = &ctx->report.on_capabilities_changed;
+ while (vec->size == 0)
+ vlc_player_CondWait(ctx->player, &ctx->wait);
+ assert(vlc_player_CanSeek(player) == ctx->params.can_seek
+ && !!(VEC_LAST(vec) & VLC_PLAYER_CAP_SEEK) == ctx->params.can_seek);
+ assert(vlc_player_CanPause(player) == ctx->params.can_pause
+ && !!(VEC_LAST(vec) & VLC_PLAYER_CAP_PAUSE) == ctx->params.can_pause);
+}
+
+static void
+test_end_prestop_buffering(struct ctx *ctx)
+{
+ vec_on_buffering_changed *vec = &ctx->report.on_buffering_changed;
+ while (vec->size == 0 || VEC_LAST(vec) != 1.0f)
+ vlc_player_CondWait(ctx->player, &ctx->wait);
+ assert(vec->size >= 2);
+ assert(vec->data[0] == 0.0f);
+}
+
+
+static void
+test_end_poststop_state(struct ctx *ctx)
+{
+ vec_on_state_changed *vec = &ctx->report.on_state_changed;
+ assert(vec->size > 1);
+ assert(vec->data[0] == VLC_PLAYER_STATE_STARTED);
+ assert(VEC_LAST(vec) == VLC_PLAYER_STATE_STOPPED);
+}
+
+static void
+test_end_poststop_tracks(struct ctx *ctx)
+{
+ vec_on_track_list_changed *vec = &ctx->report.on_track_list_changed;
+ struct {
+ size_t added;
+ size_t removed;
+ } tracks[] = {
+ [VIDEO_ES] = { 0, 0 },
+ [AUDIO_ES] = { 0, 0 },
+ [SPU_ES] = { 0, 0 },
+ };
+ struct report_track_list report;
+ vlc_vector_foreach(report, vec)
+ {
+ assert(report.track->fmt.i_cat == VIDEO_ES
+ || report.track->fmt.i_cat == AUDIO_ES
+ || report.track->fmt.i_cat == SPU_ES);
+ if (report.action == VLC_PLAYER_LIST_ADDED)
+ tracks[report.track->fmt.i_cat].added++;
+ else if (report.action == VLC_PLAYER_LIST_REMOVED)
+ tracks[report.track->fmt.i_cat].removed++;
+ }
+
+ static const enum es_format_category_e cats[] = {
+ VIDEO_ES, AUDIO_ES, SPU_ES,
+ };
+ for (size_t i = 0; i < ARRAY_SIZE(cats); ++i)
+ {
+ enum es_format_category_e cat = cats[i];
+
+ assert(tracks[cat].added == tracks[cat].removed);
+
+ /* The next check doesn't work if we selected new programs and started
+ * more than one time */
+ assert(ctx->program_switch_count == 1 || ctx->extra_start_count == 0);
+
+ const size_t track_count =
+ ctx->params.track_count[cat] * ctx->program_switch_count *
+ (ctx->played_medias.size + ctx->extra_start_count);
+ assert(tracks[cat].added == track_count);
+ }
+}
+
+static void
+test_end_poststop_programs(struct ctx *ctx)
+{
+ vec_on_program_list_changed *vec = &ctx->report.on_program_list_changed;
+ size_t program_added = 0, program_removed = 0;
+ struct report_program_list report;
+ vlc_vector_foreach(report, vec)
+ {
+ if (report.action == VLC_PLAYER_LIST_ADDED)
+ program_added++;
+ else if (report.action == VLC_PLAYER_LIST_REMOVED)
+ program_removed++;
+ }
+
+ assert(program_added == program_removed);
+ size_t program_count = ctx->params.program_count *
+ (ctx->played_medias.size + ctx->extra_start_count);
+ assert(program_added == program_count);
+}
+
+static void
+test_end_poststop_titles(struct ctx *ctx)
+{
+ if (!ctx->params.chapter_count && !ctx->params.title_count)
+ return;
+
+ vec_on_titles_changed *vec = &ctx->report.on_titles_changed;
+ assert(vec->size == 2);
+ assert(vec->data[0] != NULL);
+ assert(vec->data[1] == NULL);
+
+ vlc_player_title_list *titles = vec->data[0];
+ size_t title_count = vlc_player_title_list_GetCount(titles);
+ assert(title_count == ctx->params.title_count);
+
+ for (size_t title_idx = 0; title_idx < title_count; ++title_idx)
+ {
+ const struct vlc_player_title *title =
+ vlc_player_title_list_GetAt(titles, title_idx);
+ assert(title);
+ assert(title->name && title->name[strlen(title->name)] == 0);
+ assert(title->chapter_count == ctx->params.chapter_count);
+ assert(title->length == ctx->params.length);
+
+ for (size_t chapter_idx = 0; chapter_idx < title->chapter_count;
+ ++chapter_idx)
+ {
+ const struct vlc_player_chapter *chapter =
+ &title->chapters[chapter_idx];
+ assert(chapter->name && chapter->name[strlen(chapter->name)] == 0);
+ assert(chapter->time < ctx->params.length);
+ if (chapter_idx != 0)
+ assert(chapter->time > 0);
+ }
+ }
+}
+
+static void
+test_end_poststop_vouts(struct ctx *ctx)
+{
+ vec_on_vout_list_changed *vec = &ctx->report.on_vout_list_changed;
+
+ size_t vout_added = 0, vout_removed = 0;
+
+ struct report_vout_list report;
+ vlc_vector_foreach(report, vec)
+ {
+ if (report.action == VLC_PLAYER_LIST_ADDED)
+ vout_added++;
+ else if (report.action == VLC_PLAYER_LIST_REMOVED)
+ vout_removed++;
+ else
+ vlc_assert_unreachable();
+ }
+ assert(vout_added == vout_removed);
+}
+
+static void
+test_end_poststop_medias(struct ctx *ctx)
+{
+ vlc_player_t *player = ctx->player;
+ vec_on_current_media_changed *vec = &ctx->report.on_current_media_changed;
+
+ assert(vec->size > 0);
+ assert(vlc_player_GetCurrentMedia(player) != NULL);
+ assert(VEC_LAST(vec) == vlc_player_GetCurrentMedia(player));
+ const size_t oldsize = vec->size;
+
+ player_set_current_mock_media(ctx, NULL, NULL, false);
+
+ while (vec->size == oldsize)
+ vlc_player_CondWait(player, &ctx->wait);
+
+ assert(vec->size == ctx->played_medias.size);
+ for (size_t i = 0; i < vec->size; ++i)
+ assert(vec->data[i] == ctx->played_medias.data[i]);
+
+ assert(VEC_LAST(vec) == NULL);
+ assert(vlc_player_GetCurrentMedia(player) == NULL);
+}
+
+static void
+test_prestop(struct ctx *ctx)
+{
+ test_end_prestop_rate(ctx);
+ test_end_prestop_length(ctx);
+ test_end_prestop_capabilities(ctx);
+ test_end_prestop_buffering(ctx);
+}
+
+static void
+test_end(struct ctx *ctx)
+{
+ vlc_player_t *player = ctx->player;
+
+ vlc_player_Stop(player);
+ assert(vlc_player_GetCurrentMedia(player) != NULL);
+ wait_state(ctx, VLC_PLAYER_STATE_STOPPED);
+
+ if (!ctx->params.error)
+ {
+ test_end_poststop_state(ctx);
+ test_end_poststop_tracks(ctx);
+ test_end_poststop_programs(ctx);
+ test_end_poststop_titles(ctx);
+ test_end_poststop_vouts(ctx);
+ }
+ test_end_poststop_medias(ctx);
+
+ player_set_rate(ctx, 1.0f);
+ vlc_player_SetStartPaused(player, false);
+
+ ctx_reset(ctx);
+}
+
+static size_t
+vec_on_program_list_get_action_count(vec_on_program_list_changed *vec,
+ enum vlc_player_list_action action)
+{
+ size_t count = 0;
+ struct report_program_list report;
+ vlc_vector_foreach(report, vec)
+ if (report.action == action)
+ count++;
+ return count;
+}
+
+static size_t
+vec_on_program_selection_has_event(vec_on_program_selection_changed *vec,
+ size_t from_idx,
+ int unselected_id, int selected_id)
+{
+ assert(vec->size >= from_idx);
+ bool has_unselected_id = false, has_selected_id = false;
+ for (size_t i = from_idx; i < vec->size; ++i)
+ {
+ struct report_program_selection report = vec->data[i];
+ if (unselected_id != -1 && report.unselected_id == unselected_id)
+ {
+ assert(!has_unselected_id);
+ has_unselected_id = true;
+ }
+ if (selected_id != -1 && report.selected_id == selected_id)
+ {
+ assert(!has_selected_id);
+ has_selected_id = true;
+ }
+ }
+ if (unselected_id != -1 && selected_id != -1)
+ return has_unselected_id && has_selected_id;
+ else if (unselected_id)
+ {
+ assert(!has_selected_id);
+ return has_unselected_id;
+ }
+ else
+ {
+ assert(selected_id != -1);
+ assert(!has_unselected_id);
+ return has_selected_id;
+ }
+}
+
+static void
+test_programs(struct ctx *ctx)
+{
+ test_log("programs\n");
+
+ vlc_player_t *player = ctx->player;
+ struct media_params params = DEFAULT_MEDIA_PARAMS(VLC_TICK_FROM_SEC(100));
+ params.program_count = 3;
+ player_set_next_mock_media(ctx, "media1", ¶ms);
+
+ player_start(ctx);
+
+ {
+ vec_on_program_list_changed *vec = &ctx->report.on_program_list_changed;
+ while (vec_on_program_list_get_action_count(vec, VLC_PLAYER_LIST_ADDED)
+ != params.program_count)
+ vlc_player_CondWait(player, &ctx->wait);
+ }
+ assert(vlc_player_GetProgramCount(player) == params.program_count);
+
+ /* Select every programs ! */
+ while (true)
+ {
+ const struct vlc_player_program *new_prgm = NULL;
+ const struct vlc_player_program *old_prgm;
+ for (size_t i = 0; i < params.program_count; ++i)
+ {
+ old_prgm = vlc_player_GetProgramAt(player, i);
+ assert(old_prgm);
+ assert(old_prgm == vlc_player_GetProgram(player, old_prgm->group_id));
+ if (old_prgm->selected)
+ {
+ if (i + 1 != params.program_count)
+ new_prgm = vlc_player_GetProgramAt(player, i + 1);
+ break;
+ }
+ }
+ if (!new_prgm)
+ break;
+ const int old_id = old_prgm->group_id;
+ const int new_id = new_prgm->group_id;
+ vlc_player_SelectProgram(player, new_id);
+
+ vec_on_program_selection_changed *vec =
+ &ctx->report.on_program_selection_changed;
+
+ size_t vec_oldsize = vec->size;
+ while (!vec_on_program_selection_has_event(vec, vec_oldsize, old_id,
+ new_id))
+ vlc_player_CondWait(player, &ctx->wait);
+ ctx->program_switch_count++; /* test_end_poststop_tracks check */
+ }
+
+ test_prestop(ctx);
+ test_end(ctx);
+}
+
+static size_t
+vec_on_track_list_get_action_count(vec_on_track_list_changed *vec,
+ enum vlc_player_list_action action)
+{
+ size_t count = 0;
+ struct report_track_list report;
+ vlc_vector_foreach(report, vec)
+ if (report.action == action)
+ count++;
+ return count;
+}
+
+static bool
+vec_on_track_selection_has_event(vec_on_track_selection_changed *vec,
+ size_t from_idx, vlc_es_id_t *unselected_id,
+ vlc_es_id_t *selected_id)
+{
+ assert(vec->size >= from_idx);
+ bool has_unselected_id = false, has_selected_id = false;
+ for (size_t i = from_idx; i < vec->size; ++i)
+ {
+ struct report_track_selection report = vec->data[i];
+ if (unselected_id && report.unselected_id == unselected_id)
+ {
+ assert(!has_unselected_id);
+ has_unselected_id = true;
+ }
+ if (selected_id && report.selected_id == selected_id)
+ {
+ assert(!has_selected_id);
+ has_selected_id = true;
+ }
+ }
+ if (unselected_id && selected_id)
+ return has_unselected_id && has_selected_id;
+ else if (unselected_id)
+ {
+ assert(!has_selected_id);
+ return has_unselected_id;
+ }
+ else
+ {
+ assert(selected_id);
+ assert(!has_unselected_id);
+ return has_selected_id;
+ }
+}
+
+static bool
+player_select_next_unselected_track(struct ctx *ctx,
+ enum es_format_category_e cat)
+{
+ vlc_player_t *player = ctx->player;
+
+ const struct vlc_player_track *new_track = NULL;
+ const struct vlc_player_track *old_track = NULL;
+ bool has_selected_track = false;
+ vlc_es_id_t *new_id, *old_id;
+
+ /* Find the next track to select (selected +1) */
+ const size_t count = vlc_player_GetTrackCount(player, cat);
+ for (size_t i = 0; i < count; ++i)
+ {
+ old_track = vlc_player_GetTrackAt(player, cat, i);
+ assert(old_track);
+ if (old_track->selected)
+ {
+ has_selected_track = true;
+ if (i + 1 != count)
+ new_track = vlc_player_GetTrackAt(player, cat, i + 1);
+ /* else: trigger UnselectTrack path */
+ break;
+ }
+ }
+
+ if (!has_selected_track)
+ {
+ /* subs are not selected by default */
+ assert(cat == SPU_ES);
+ old_track = NULL;
+ new_track = vlc_player_GetTrackAt(player, cat, 0);
+ }
+ new_id = new_track ? vlc_es_id_Hold(new_track->es_id) : NULL;
+ old_id = old_track ? vlc_es_id_Hold(old_track->es_id) : NULL;
+
+ if (new_id)
+ vlc_player_SelectTrack(player, new_id);
+ else
+ {
+ assert(old_id);
+ vlc_player_UnselectTrack(player, old_id);
+ }
+
+ {
+ vec_on_track_selection_changed *vec =
+ &ctx->report.on_track_selection_changed;
+
+ size_t vec_oldsize = vec->size;
+ while (!vec_on_track_selection_has_event(vec, vec_oldsize, old_id,
+ new_id))
+ vlc_player_CondWait(player, &ctx->wait);
+ }
+ if (new_id)
+ vlc_es_id_Release(new_id);
+ if (old_id)
+ vlc_es_id_Release(old_id);
+
+ return !!new_track;
+}
+
+static void
+test_tracks(struct ctx *ctx, bool packetized)
+{
+ test_log("tracks (packetized: %d)\n", packetized);
+
+ vlc_player_t *player = ctx->player;
+ struct media_params params = DEFAULT_MEDIA_PARAMS(VLC_TICK_FROM_SEC(100));
+ params.track_count[VIDEO_ES] = 1;
+ params.track_count[AUDIO_ES] = 9;
+ params.track_count[SPU_ES] = 9;
+ params.video_packetized = params.audio_packetized = params.sub_packetized
+ = packetized;
+ player_set_next_mock_media(ctx, "media1", ¶ms);
+ const size_t track_count = params.track_count[VIDEO_ES] +
+ params.track_count[AUDIO_ES] +
+ params.track_count[SPU_ES];
+
+ player_start(ctx);
+
+ /* Wait that all tracks are added */
+ {
+ vec_on_track_list_changed *vec = &ctx->report.on_track_list_changed;
+ while (vec_on_track_list_get_action_count(vec, VLC_PLAYER_LIST_ADDED)
+ != track_count)
+ vlc_player_CondWait(player, &ctx->wait);
+ }
+
+ /* Wait that video and audio are selected */
+ {
+ vec_on_track_selection_changed *vec =
+ &ctx->report.on_track_selection_changed;
+ while (vec->size != 2)
+ vlc_player_CondWait(player, &ctx->wait);
+ for (size_t i = 0; i < vec->size; ++i)
+ {
+ assert(!vec->data[i].unselected_id);
+ assert(vec->data[i].selected_id);
+ const struct vlc_player_track *track =
+ vlc_player_GetTrack(player, vec->data[i].selected_id);
+ assert(track);
+ assert(track->fmt.i_cat == VIDEO_ES || track->fmt.i_cat == AUDIO_ES);
+ assert(track == vlc_player_GetTrackAt(player, track->fmt.i_cat, 0));
+ }
+ }
+
+ /* Select every possible tracks */
+ static const enum es_format_category_e cats[] = {
+ SPU_ES, VIDEO_ES, AUDIO_ES /* Test SPU before the vout is disabled */
+ };
+ for (size_t i = 0; i < ARRAY_SIZE(cats); ++i)
+ {
+ enum es_format_category_e cat = cats[i];
+ assert(params.track_count[cat] == vlc_player_GetTrackCount(player, cat));
+ while (player_select_next_unselected_track(ctx, cat));
+ }
+
+ test_prestop(ctx);
+ test_end(ctx);
+}
+
+static void
+test_titles(struct ctx *ctx, bool null_names)
+{
+ test_log("titles (null_names: %d)\n", null_names);
+ vlc_player_t *player = ctx->player;
+
+ struct media_params params = DEFAULT_MEDIA_PARAMS(VLC_TICK_FROM_SEC(100));
+ params.title_count = 5;
+ params.chapter_count = 2000;
+ params.null_names = null_names;
+ player_set_next_mock_media(ctx, "media1", ¶ms);
+
+ player_start(ctx);
+
+ /* Wait for the title list */
+ vlc_player_title_list *titles;
+ {
+ vec_on_titles_changed *vec = &ctx->report.on_titles_changed;
+ while (vec->size == 0)
+ vlc_player_CondWait(player, &ctx->wait);
+ titles = vec->data[0];
+ assert(titles != NULL && titles == vlc_player_GetTitleList(player));
+ }
+
+ /* Select a new title and a new chapter */
+ const size_t last_chapter_idx = params.chapter_count - 1;
+ {
+ vec_on_title_selection_changed *vec =
+ &ctx->report.on_title_selection_changed;
+ while (vec->size == 0)
+ vlc_player_CondWait(player, &ctx->wait);
+ assert(vec->data[0] == 0);
+
+ const struct vlc_player_title *title =
+ vlc_player_title_list_GetAt(titles, 4);
+ vlc_player_SelectTitle(player, title);
+
+ while (vec->size == 1)
+ vlc_player_CondWait(player, &ctx->wait);
+ assert(vec->data[1] == 4);
+
+ assert(title->chapter_count == params.chapter_count);
+ vlc_player_SelectChapter(player, title, last_chapter_idx);
+ }
+
+ /* Wait for the chapter selection */
+ {
+ vec_on_chapter_selection_changed *vec =
+ &ctx->report.on_chapter_selection_changed;
+
+ while (vec->size == 0 || VEC_LAST(vec).chapter_idx != last_chapter_idx)
+ vlc_player_CondWait(player, &ctx->wait);
+ assert(VEC_LAST(vec).title_idx == 4);
+ }
+
+ test_prestop(ctx);
+ wait_state(ctx, VLC_PLAYER_STATE_STOPPED);
+ assert_normal_state(ctx);
+ test_end(ctx);
+}
+
+static void
+test_error(struct ctx *ctx)
+{
+ test_log("error\n");
+ vlc_player_t *player = ctx->player;
+
+ struct media_params params = DEFAULT_MEDIA_PARAMS(VLC_TICK_FROM_SEC(1));
+ params.error = true;
+ player_set_next_mock_media(ctx, "media1", ¶ms);
+
+ player_start(ctx);
+
+ {
+ vec_on_error_changed *vec = &ctx->report.on_error_changed;
+ while (vec->size == 0 || VEC_LAST(vec) == VLC_PLAYER_ERROR_NONE)
+ vlc_player_CondWait(player, &ctx->wait);
+ }
+ wait_state(ctx, VLC_PLAYER_STATE_STOPPED);
+
+ test_end(ctx);
+}
+
+static void
+test_capabilities_seek(struct ctx *ctx)
+{
+ test_log("capabilites_seek\n");
+ vlc_player_t *player = ctx->player;
+
+ struct media_params params = DEFAULT_MEDIA_PARAMS(VLC_TICK_FROM_SEC(1));
+ params.can_seek = false;
+ player_set_next_mock_media(ctx, "media1", ¶ms);
+
+ player_start(ctx);
+
+ {
+ vec_on_capabilities_changed *vec = &ctx->report.on_capabilities_changed;
+ while (vec->size == 0)
+ vlc_player_CondWait(player, &ctx->wait);
+ }
+
+ vlc_player_ChangeRate(player, 4.f);
+
+ /* Ensure that seek back to 0 doesn't work */
+ {
+ vlc_tick_t last_time = 0;
+ vec_on_state_changed *vec = &ctx->report.on_state_changed;
+ while (vec->size == 0 || VEC_LAST(vec) != VLC_PLAYER_STATE_STOPPED)
+ {
+ vec_on_position_changed *posvec = &ctx->report.on_position_changed;
+ if (posvec->size > 0 && last_time != VEC_LAST(posvec).time)
+ {
+ last_time = VEC_LAST(posvec).time;
+ vlc_player_SetTime(player, 0);
+ }
+ vlc_player_CondWait(player, &ctx->wait);
+ }
+ }
+
+ assert_state(ctx, VLC_PLAYER_STATE_STOPPED);
+ test_end(ctx);
+}
+
+static void
+test_capabilities_pause(struct ctx *ctx)
+{
+ test_log("capabilites_pause\n");
+ vlc_player_t *player = ctx->player;
+
+ struct media_params params = DEFAULT_MEDIA_PARAMS(VLC_TICK_FROM_SEC(1));
+ params.can_pause = false;
+ player_set_next_mock_media(ctx, "media1", ¶ms);
+
+ player_start(ctx);
+
+ {
+ vec_on_capabilities_changed *vec = &ctx->report.on_capabilities_changed;
+ while (vec->size == 0)
+ vlc_player_CondWait(player, &ctx->wait);
+ }
+
+ /* Ensure that pause doesn't work */
+ vlc_player_Pause(player);
+ vlc_player_ChangeRate(player, 32.f);
+
+ test_prestop(ctx);
+
+ wait_state(ctx, VLC_PLAYER_STATE_STOPPED);
+ assert_normal_state(ctx);
+
+ test_end(ctx);
+}
+
+static void
+test_pause(struct ctx *ctx)
+{
+ test_log("pause\n");
+ vlc_player_t *player = ctx->player;
+
+ struct media_params params = DEFAULT_MEDIA_PARAMS(VLC_TICK_FROM_SEC(10));
+ player_set_next_mock_media(ctx, "media1", ¶ms);
+
+ /* Start paused */
+ vlc_player_SetStartPaused(player, true);
+ player_start(ctx);
+ {
+ vec_on_state_changed *vec = &ctx->report.on_state_changed;
+ while (vec->size == 0 || VEC_LAST(vec) != VLC_PLAYER_STATE_PAUSED)
+ vlc_player_CondWait(player, &ctx->wait);
+ assert(vec->size == 3);
+ assert(vec->data[0] == VLC_PLAYER_STATE_STARTED);
+ assert(vec->data[1] == VLC_PLAYER_STATE_PLAYING);
+ assert(vec->data[2] == VLC_PLAYER_STATE_PAUSED);
+ }
+
+ {
+ vec_on_position_changed *vec = &ctx->report.on_position_changed;
+ assert(vec->size == 0);
+ }
+
+ /* Resume */
+ vlc_player_Resume(player);
+
+ {
+ vec_on_state_changed *vec = &ctx->report.on_state_changed;
+ while (VEC_LAST(vec) != VLC_PLAYER_STATE_PLAYING)
+ vlc_player_CondWait(player, &ctx->wait);
+ assert(vec->size == 4);
+ }
+
+ {
+ vec_on_position_changed *vec = &ctx->report.on_position_changed;
+ while (vec->size == 0)
+ vlc_player_CondWait(player, &ctx->wait);
+ }
+
+ /* Pause again (while playing) */
+ vlc_player_Pause(player);
+
+ {
+ vec_on_state_changed *vec = &ctx->report.on_state_changed;
+ while (VEC_LAST(vec) != VLC_PLAYER_STATE_PAUSED)
+ vlc_player_CondWait(player, &ctx->wait);
+ assert(vec->size == 5);
+ }
+
+ test_end(ctx);
+}
+
+static void
+test_seeks(struct ctx *ctx)
+{
+ test_log("seeks\n");
+ vlc_player_t *player = ctx->player;
+
+ struct media_params params = DEFAULT_MEDIA_PARAMS(VLC_TICK_FROM_SEC(10));
+ player_set_next_mock_media(ctx, "media1", ¶ms);
+
+ /* only the last one will be taken into account before start */
+ vlc_player_SetTimeFast(player, 0);
+ vlc_player_SetTimeFast(player, VLC_TICK_FROM_SEC(100));
+ vlc_player_SetTimeFast(player, 10);
+
+ vlc_tick_t seek_time = VLC_TICK_FROM_SEC(5);
+ vlc_player_SetTimeFast(player, seek_time);
+ player_start(ctx);
+
+ {
+ vec_on_position_changed *vec = &ctx->report.on_position_changed;
+ while (vec->size == 0)
+ vlc_player_CondWait(player, &ctx->wait);
+
+ assert(VEC_LAST(vec).time >= seek_time);
+ assert_position(ctx, &VEC_LAST(vec));
+
+ vlc_tick_t last_time = VEC_LAST(vec).time;
+
+ vlc_tick_t jump_time = -VLC_TICK_FROM_SEC(2);
+ vlc_player_JumpTime(player, jump_time);
+
+ while (VEC_LAST(vec).time >= last_time)
+ vlc_player_CondWait(player, &ctx->wait);
+
+ assert(VEC_LAST(vec).time >= last_time + jump_time);
+ assert_position(ctx, &VEC_LAST(vec));
+ }
+
+ vlc_player_SetPosition(player, 2.0f);
+
+ test_prestop(ctx);
+
+ wait_state(ctx, VLC_PLAYER_STATE_STOPPED);
+ assert_normal_state(ctx);
+
+ test_end(ctx);
+}
+
+#define assert_media_name(media, name) do { \
+ assert(media); \
+ char *media_name = input_item_GetName(media); \
+ assert(media_name && strcmp(media_name, name) == 0); \
+ free(media_name); \
+} while(0)
+
+static void
+test_next_media(struct ctx *ctx)
+{
+ test_log("next_media\n");
+ const char *media_names[] = { "media1", "media2", "media3" };
+ const size_t media_count = ARRAY_SIZE(media_names);
+
+ vlc_player_t *player = ctx->player;
+ struct media_params params = DEFAULT_MEDIA_PARAMS(VLC_TICK_FROM_MS(100));
+
+ for (size_t i = 0; i < media_count; ++i)
+ player_set_next_mock_media(ctx, media_names[i], ¶ms);
+ player_set_rate(ctx, 4.f);
+ player_start(ctx);
+
+ test_prestop(ctx);
+ wait_state(ctx, VLC_PLAYER_STATE_STOPPED);
+ assert_normal_state(ctx);
+
+ {
+ vec_on_current_media_changed *vec = &ctx->report.on_current_media_changed;
+
+ assert(vec->size == media_count);
+ assert(ctx->next_medias.size == 0);
+ for (size_t i = 0; i < ctx->played_medias.size; ++i)
+ assert_media_name(vec->data[i], media_names[i]);
+ }
+
+ test_end(ctx);
+}
+
+static void
+test_set_current_media(struct ctx *ctx)
+{
+ test_log("current_media\n");
+ const char *media_names[] = { "media1", "media2", "media3" };
+ const size_t media_count = ARRAY_SIZE(media_names);
+
+ vlc_player_t *player = ctx->player;
+ struct media_params params = DEFAULT_MEDIA_PARAMS(VLC_TICK_FROM_MS(100));
+
+ player_set_current_mock_media(ctx, media_names[0], ¶ms, false);
+ player_start(ctx);
+
+ wait_state(ctx, VLC_PLAYER_STATE_PLAYING);
+
+ /* Call vlc_player_SetCurrentMedia for the remaining medias interrupting
+ * the player and without passing by the next_media provider. */
+ {
+ vec_on_current_media_changed *vec = &ctx->report.on_current_media_changed;
+ assert(vec->size == 1);
+ for (size_t i = 1; i <= media_count; ++i)
+ {
+ while (vec->size != i)
+ vlc_player_CondWait(player, &ctx->wait);
+
+ input_item_t *last_media = VEC_LAST(vec);
+ assert(last_media);
+ assert(last_media == vlc_player_GetCurrentMedia(player));
+ assert(last_media == VEC_LAST(&ctx->played_medias));
+ assert_media_name(last_media, media_names[i - 1]);
+
+ if (i < media_count)
+ {
+ /* Next vlc_player_SetCurrentMedia() call should be
+ * asynchronous since we are still playing. Therefore,
+ * vlc_player_GetCurrentMedia() should return the last one. */
+ player_set_current_mock_media(ctx, "ignored", ¶ms, true);
+ assert(vlc_player_GetCurrentMedia(player) == last_media);
+
+ /* The previous media is ignored due to this call */
+ player_set_current_mock_media(ctx, media_names[i], ¶ms, false);
+ }
+ }
+ }
+
+ test_prestop(ctx);
+ wait_state(ctx, VLC_PLAYER_STATE_STOPPED);
+ assert_normal_state(ctx);
+
+ /* Test that the player can be played again with the same media */
+ player_start(ctx);
+ ctx->extra_start_count++; /* Since we play the same media */
+
+ /* Current state is already stopped, wait first for started then */
+ wait_state(ctx, VLC_PLAYER_STATE_STARTED);
+ wait_state(ctx, VLC_PLAYER_STATE_STOPPED);
+
+ assert_normal_state(ctx);
+
+ /* Playback is stopped: vlc_player_SetCurrentMedia should be synchronous */
+ player_set_current_mock_media(ctx, media_names[0], ¶ms, false);
+ assert(vlc_player_GetCurrentMedia(player) == VEC_LAST(&ctx->played_medias));
+
+ player_start(ctx);
+
+ wait_state(ctx, VLC_PLAYER_STATE_STARTED);
+ wait_state(ctx, VLC_PLAYER_STATE_STOPPED);
+
+ test_end(ctx);
+}
+
+static void
+test_delete_while_playback(vlc_object_t *obj)
+{
+ test_log("delete_while_playback\n");
+ vlc_player_t *player = vlc_player_New(obj, NULL, NULL);
+
+ struct media_params params = DEFAULT_MEDIA_PARAMS(VLC_TICK_FROM_SEC(10));
+ input_item_t *media = create_mock_media("media1", ¶ms);
+ assert(media);
+
+ vlc_player_Lock(player);
+ int ret = vlc_player_SetCurrentMedia(player, media);
+ assert(ret == VLC_SUCCESS);
+ input_item_Release(media);
+
+ ret = vlc_player_Start(player);
+ assert(ret == VLC_SUCCESS);
+ vlc_player_Unlock(player);
+
+ vlc_player_Delete(player);
+}
+
+int
+main(void)
+{
+ test_init();
+
+ static const char * argv[] = {
+ "-v",
+ "--ignore-config",
+ "-Idummy",
+ "--no-media-library",
+ /* Avoid leaks from various dlopen... */
+ "--codec=araw,rawvideo,subsdec,none",
+ "--vout=dummy",
+ "--aout=dummy",
+ };
+ libvlc_instance_t *vlc = libvlc_new(ARRAY_SIZE(argv), argv);
+ assert(vlc);
+
+ static const struct vlc_player_media_provider provider = {
+ .get_next = player_get_next,
+ };
+
+#define X(type, name) .name = player_##name,
+ static const struct vlc_player_cbs cbs = {
+REPORT_LIST
+ };
+#undef X
+
+ struct ctx ctx = {
+ .next_medias = VLC_VECTOR_INITIALIZER,
+ .played_medias = VLC_VECTOR_INITIALIZER,
+ .program_switch_count = 1,
+ .extra_start_count = 0,
+ .rate = 1.f,
+ .wait = VLC_STATIC_COND,
+ };
+ reports_init(&ctx.report);
+
+ /* Force wdummy window */
+ int ret = var_Create(vlc->p_libvlc_int, "window", VLC_VAR_STRING);
+ assert(ret == VLC_SUCCESS);
+ ret = var_SetString(vlc->p_libvlc_int, "window", "wdummy");
+ assert(ret == VLC_SUCCESS);
+
+ ctx.player = vlc_player_New(VLC_OBJECT(vlc->p_libvlc_int), &provider, &ctx);
+ vlc_player_t *player = ctx.player;
+ assert(player);
+
+ vlc_player_Lock(player);
+ vlc_player_listener_id *listener =
+ vlc_player_AddListener(player, &cbs, &ctx);
+ assert(listener);
+
+ test_set_current_media(&ctx);
+ test_next_media(&ctx);
+ test_seeks(&ctx);
+ test_pause(&ctx);
+ test_capabilities_pause(&ctx);
+ test_capabilities_seek(&ctx);
+ test_error(&ctx);
+ test_titles(&ctx, true);
+ test_titles(&ctx, false);
+ test_tracks(&ctx, true);
+ test_tracks(&ctx, false);
+ test_programs(&ctx);
+
+ vlc_player_RemoveListener(player, listener);
+ vlc_player_Unlock(player);
+
+ vlc_player_Delete(player);
+
+ test_delete_while_playback(VLC_OBJECT(vlc->p_libvlc_int));
+
+ libvlc_release(vlc);
+
+ ctx_destroy(&ctx);
+ return 0;
+}
--
2.19.1
More information about the vlc-devel
mailing list