pebble/tests/fw/ui/test_kino_player.c
2025-01-27 11:38:16 -08:00

412 lines
16 KiB
C

/*
* Copyright 2024 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "applib/ui/kino/kino_player.h"
#include "applib/ui/kino/kino_reel.h"
#include "applib/ui/kino/kino_reel_gbitmap.h"
#include "applib/ui/kino/kino_reel_gbitmap_sequence.h"
#include "applib/ui/kino/kino_reel_pdci.h"
#include "applib/ui/kino/kino_reel_pdcs.h"
#include "applib/ui/kino/kino_reel_custom.h"
#include "clar.h"
// Fakes
////////////////////////////////////
#include "fake_resource_syscalls.h"
// Stubs
////////////////////////////////////
#include "stubs_app_state.h"
#include "stubs_applib_resource.h"
#include "stubs_compiled_with_legacy2_sdk.h"
#include "stubs_gpath.h"
#include "stubs_graphics.h"
#include "stubs_graphics_context.h"
#include "stubs_heap.h"
#include "stubs_logging.h"
#include "stubs_memory_layout.h"
#include "stubs_passert.h"
#include "stubs_pbl_malloc.h"
#include "stubs_pebble_tasks.h"
#include "stubs_resources.h"
#include "stubs_ui_window.h"
#include "stubs_unobstructed_area.h"
void graphics_context_move_draw_box(GContext* ctx, GPoint offset) {}
typedef uint16_t ResourceId;
const uint8_t *resource_get_builtin_bytes(ResAppNum app_num, uint32_t resource_id,
uint32_t *num_bytes_out) { return NULL; }
typedef struct TestReelData {
uint32_t elapsed_ms;
uint32_t duration_ms;
} TestReelData;
static int s_num_destructor_calls;
static void prv_destructor(KinoReel *reel) {
s_num_destructor_calls++;
free(kino_reel_custom_get_data(reel));
}
static uint32_t prv_elapsed_getter(KinoReel *reel) {
return ((TestReelData*)kino_reel_custom_get_data(reel))->elapsed_ms;
}
static bool prv_elapsed_setter(KinoReel *reel, uint32_t elapsed_ms) {
((TestReelData*)kino_reel_custom_get_data(reel))->elapsed_ms = elapsed_ms;
return true;
}
static uint32_t prv_duration_getter(KinoReel *reel) {
return ((TestReelData*)kino_reel_custom_get_data(reel))->duration_ms;
}
static struct TestReelData *test_reel_data;
static KinoReelImpl *test_reel_impl = NULL;
static KinoReel *test_reel = NULL;
static KinoPlayer *test_player = NULL;
// Setup
void test_kino_player__initialize(void) {
test_reel_data = malloc(sizeof(TestReelData));
memset(test_reel_data, 0, sizeof(TestReelData));
s_num_destructor_calls = 0;
test_reel_impl = malloc(sizeof(KinoReelImpl));
*test_reel_impl = (KinoReelImpl) {
.destructor = prv_destructor,
.set_elapsed = prv_elapsed_setter,
.get_elapsed = prv_elapsed_getter,
.get_duration = prv_duration_getter
};
test_reel = kino_reel_custom_create(test_reel_impl, test_reel_data);
cl_assert(test_reel != NULL);
test_player = malloc(sizeof(KinoPlayer));
memset(test_player, 0, sizeof(KinoPlayer));
kino_player_set_reel(test_player, test_reel, true);
}
// Teardown
void test_kino_player__cleanup(void) {
kino_reel_destroy(test_reel);
}
// Tests
////////////////////////////////////
extern void prv_play_animation_update(Animation *animation, const AnimationProgress normalized);
void test_kino_player__finite_animation_finite_reel_foward(void) {
// Choose duration and elapsed to have clean division for
// ANIMATION_NORMALIZED_MAX * elapsed / duration = whole_number
test_reel_data->duration_ms = 300;
kino_player_play(test_player);
animation_set_elapsed(test_player->animation, 1234); // intentionally bad value
prv_play_animation_update(test_player->animation, ANIMATION_NORMALIZED_MAX * 20 / 300);
cl_assert_equal_i(kino_reel_get_elapsed(test_reel), 20);
}
void test_kino_player__create_finite_animation_finite_reel_foward(void) {
// Choose duration and elapsed to have clean division for
// ANIMATION_NORMALIZED_MAX * elapsed / duration = whole_number
test_reel_data->duration_ms = 300;
Animation *animation = (Animation *)kino_player_create_play_animation(test_player);
cl_assert_equal_i(animation_is_scheduled(animation), false);
animation_schedule(animation);
animation_set_elapsed(animation, 1234); // intentionally bad value
prv_play_animation_update(animation, ANIMATION_NORMALIZED_MAX * 20 / 300);
cl_assert_equal_i(kino_reel_get_elapsed(test_reel), 20);
}
void test_kino_player__finite_animation_finite_reel_reverse(void) {
// Choose duration and elapsed to have clean division for
// ANIMATION_NORMALIZED_MAX * elapsed / duration = whole_number
test_reel_data->duration_ms = 300;
kino_player_play(test_player);
animation_set_reverse(test_player->animation, true);
animation_set_elapsed(test_player->animation, 1234); // intentionally bad value
prv_play_animation_update(test_player->animation,
ANIMATION_NORMALIZED_MAX - ANIMATION_NORMALIZED_MAX * 20 / 300);
cl_assert_equal_i(kino_reel_get_elapsed(test_reel), 300 - 20);
}
void test_kino_player__create_finite_animation_finite_reel_reverse(void) {
// Choose duration and elapsed to have clean division for
// ANIMATION_NORMALIZED_MAX * elapsed / duration = whole_number
test_reel_data->duration_ms = 300;
Animation *animation = (Animation *)kino_player_create_play_animation(test_player);
cl_assert_equal_i(animation_is_scheduled(animation), false);
animation_schedule(animation);
animation_set_reverse(animation, true);
animation_set_elapsed(animation, 1234); // intentionally bad value
prv_play_animation_update(animation,
ANIMATION_NORMALIZED_MAX - ANIMATION_NORMALIZED_MAX * 20 / 300);
cl_assert_equal_i(kino_reel_get_elapsed(test_reel), 300 - 20);
}
void test_kino_player__finite_animation_infinite_reel_forward(void) {
// Choose duration and elapsed to have clean division for
// ANIMATION_NORMALIZED_MAX * elapsed / duration = whole_number
test_reel_data->duration_ms = ANIMATION_DURATION_INFINITE;
kino_player_play(test_player);
animation_set_elapsed(test_player->animation, 20);
animation_set_duration(test_player->animation, 300);
prv_play_animation_update(test_player->animation, 0); // intentionally bad value
cl_assert_equal_i(kino_reel_get_elapsed(test_reel), 20);
}
void test_kino_player__create_finite_animation_infinite_reel_forward(void) {
// Choose duration and elapsed to have clean division for
// ANIMATION_NORMALIZED_MAX * elapsed / duration = whole_number
test_reel_data->duration_ms = ANIMATION_DURATION_INFINITE;
Animation *animation = (Animation *)kino_player_create_play_animation(test_player);
cl_assert_equal_i(animation_is_scheduled(animation), false);
animation_schedule(animation);
animation_set_elapsed(animation, 20);
animation_set_duration(animation, 300);
prv_play_animation_update(animation, 0); // intentionally bad value
cl_assert_equal_i(kino_reel_get_elapsed(test_reel), 20);
}
void test_kino_player__infinite_animation_finite_reel_forward(void) {
// Choose duration and elapsed to have clean division for
// ANIMATION_NORMALIZED_MAX * elapsed / duration = whole_number
test_reel_data->duration_ms = 300;
kino_player_play(test_player);
animation_set_elapsed(test_player->animation, 20);
animation_set_duration(test_player->animation, ANIMATION_DURATION_INFINITE);
prv_play_animation_update(test_player->animation, 0); // intentionally bad value
cl_assert_equal_i(kino_reel_get_elapsed(test_reel), 20);
}
void test_kino_player__create_infinite_animation_finite_reel_forward(void) {
// Choose duration and elapsed to have clean division for
// ANIMATION_NORMALIZED_MAX * elapsed / duration = whole_number
test_reel_data->duration_ms = 300;
Animation *animation = (Animation *)kino_player_create_play_animation(test_player);
cl_assert_equal_i(animation_is_scheduled(animation), false);
animation_schedule(animation);
animation_set_elapsed(animation, 20);
animation_set_duration(animation, ANIMATION_DURATION_INFINITE);
prv_play_animation_update(animation, 0); // intentionally bad value
cl_assert_equal_i(kino_reel_get_elapsed(test_reel), 20);
}
void test_kino_player__infinite_animation_infinite_reel_forward(void) {
// Choose duration and elapsed to have clean division for
// ANIMATION_NORMALIZED_MAX * elapsed / duration = whole_number
test_reel_data->duration_ms = ANIMATION_DURATION_INFINITE;
kino_player_play(test_player);
animation_set_elapsed(test_player->animation, 20);
animation_set_duration(test_player->animation, ANIMATION_DURATION_INFINITE);
prv_play_animation_update(test_player->animation, 0); // intentionally bad value
cl_assert_equal_i(kino_reel_get_elapsed(test_reel), 20);
}
void test_kino_player__create_infinite_animation_infinite_reel_forward(void) {
// Choose duration and elapsed to have clean division for
// ANIMATION_NORMALIZED_MAX * elapsed / duration = whole_number
test_reel_data->duration_ms = ANIMATION_DURATION_INFINITE;
Animation *animation = (Animation *)kino_player_create_play_animation(test_player);
cl_assert_equal_i(animation_is_scheduled(animation), false);
animation_schedule(animation);
animation_set_elapsed(animation, 20);
animation_set_duration(animation, ANIMATION_DURATION_INFINITE);
prv_play_animation_update(animation, 0); // intentionally bad value
cl_assert_equal_i(kino_reel_get_elapsed(test_reel), 20);
}
void test_kino_player__infinite_animation_finite_reel_reverse(void) {
// Choose duration and elapsed to have clean division for
// ANIMATION_NORMALIZED_MAX * elapsed / duration = whole_number
test_reel_data->duration_ms = 300;
kino_player_play(test_player);
animation_set_reverse(test_player->animation, true);
animation_set_duration(test_player->animation, ANIMATION_DURATION_INFINITE);
animation_set_elapsed(test_player->animation, 20);
prv_play_animation_update(test_player->animation, 0); // intentionally bad value
cl_assert_equal_i(kino_reel_get_elapsed(test_reel), 300 - 20);
}
void test_kino_player__create_infinite_animation_finite_reel_reverse(void) {
// Choose duration and elapsed to have clean division for
// ANIMATION_NORMALIZED_MAX * elapsed / duration = whole_number
test_reel_data->duration_ms = 300;
Animation *animation = (Animation *)kino_player_create_play_animation(test_player);
cl_assert_equal_i(animation_is_scheduled(animation), false);
animation_schedule(animation);
animation_set_reverse(animation, true);
animation_set_duration(animation, ANIMATION_DURATION_INFINITE);
animation_set_elapsed(animation, 20);
prv_play_animation_update(animation, 0); // intentionally bad value
cl_assert_equal_i(kino_reel_get_elapsed(test_reel), 300 - 20);
}
void test_kino_player__finite_animation_infinite_reel_reverse(void) {
// Choose duration and elapsed to have clean division for
// ANIMATION_NORMALIZED_MAX * elapsed / duration = whole_number
test_reel_data->duration_ms = ANIMATION_DURATION_INFINITE;
kino_player_play(test_player);
animation_set_reverse(test_player->animation, true);
animation_set_duration(test_player->animation, 300);
animation_set_elapsed(test_player->animation, 20);
prv_play_animation_update(test_player->animation, 0); // intentionally bad value
cl_assert_equal_i(kino_reel_get_elapsed(test_reel), ANIMATION_DURATION_INFINITE);
}
void test_kino_player__create_finite_animation_infinite_reel_reverse(void) {
// Choose duration and elapsed to have clean division for
// ANIMATION_NORMALIZED_MAX * elapsed / duration = whole_number
test_reel_data->duration_ms = ANIMATION_DURATION_INFINITE;
Animation *animation = (Animation *)kino_player_create_play_animation(test_player);
cl_assert_equal_i(animation_is_scheduled(animation), false);
animation_schedule(animation);
animation_set_reverse(animation, true);
animation_set_duration(animation, 300);
animation_set_elapsed(animation, 20);
prv_play_animation_update(animation, 0); // intentionally bad value
cl_assert_equal_i(kino_reel_get_elapsed(test_reel), ANIMATION_DURATION_INFINITE);
}
void test_kino_player__infinite_animation_infinite_reel_reverse(void) {
// Choose duration and elapsed to have clean division for
// ANIMATION_NORMALIZED_MAX * elapsed / duration = whole_number
test_reel_data->duration_ms = ANIMATION_DURATION_INFINITE;
kino_player_play(test_player);
animation_set_reverse(test_player->animation, true);
animation_set_duration(test_player->animation, ANIMATION_DURATION_INFINITE);
animation_set_elapsed(test_player->animation, 20);
prv_play_animation_update(test_player->animation, 0); // intentionally bad value
cl_assert_equal_i(kino_reel_get_elapsed(test_reel), ANIMATION_DURATION_INFINITE);
}
void test_kino_player__create_infinite_animation_infinite_reel_reverse(void) {
// Choose duration and elapsed to have clean division for
// ANIMATION_NORMALIZED_MAX * elapsed / duration = whole_number
test_reel_data->duration_ms = ANIMATION_DURATION_INFINITE;
Animation *animation = (Animation *)kino_player_create_play_animation(test_player);
cl_assert_equal_i(animation_is_scheduled(animation), false);
animation_schedule(animation);
animation_set_reverse(animation, true);
animation_set_duration(animation, ANIMATION_DURATION_INFINITE);
animation_set_elapsed(animation, 20);
prv_play_animation_update(animation, 0); // intentionally bad value
cl_assert_equal_i(kino_reel_get_elapsed(test_reel), ANIMATION_DURATION_INFINITE);
}
void test_kino_player__create_animation_is_immutable(void) {
test_reel_data->duration_ms = 300;
Animation *animation = (Animation *)kino_player_create_play_animation(test_player);
cl_assert_equal_i(animation_is_scheduled(animation), false);
cl_assert_equal_i(animation_is_immutable(animation), true);
}
void test_kino_player__create_animation_is_unscheduled_by_play_pause_rewind(void) {
test_reel_data->duration_ms = 300;
Animation *animation = (Animation *)kino_player_create_play_animation(test_player);
cl_assert_equal_i(animation_is_scheduled(animation), false);
animation_schedule(animation);
// Play plays a new animation
cl_assert_equal_i(animation_is_scheduled(animation), true);
kino_player_play(test_player);
cl_assert_equal_i(animation_is_scheduled(animation), false);
// Create play animation unschedules previous animation
animation = (Animation *)kino_player_create_play_animation(test_player);
cl_assert_equal_i(animation_is_scheduled(animation), false);
animation_schedule(animation);
// Pause unschedules the current animation
cl_assert_equal_i(animation_is_scheduled(animation), true);
kino_player_pause(test_player);
cl_assert_equal_i(animation_is_scheduled(animation), false);
animation = (Animation *)kino_player_create_play_animation(test_player);
cl_assert_equal_i(animation_is_scheduled(animation), false);
animation_schedule(animation);
// Rewind unschedules the current animation
cl_assert_equal_i(animation_is_scheduled(animation), true);
kino_player_rewind(test_player);
cl_assert_equal_i(animation_is_scheduled(animation), false);
}
void test_kino_player__set_reel_calls_destructor(void) {
kino_player_set_reel(test_player, test_reel, true);
cl_assert_equal_p(test_player->reel, test_reel);
cl_assert_equal_i(s_num_destructor_calls, 0);
kino_player_set_reel(test_player, NULL, true);
cl_assert_equal_p(test_player->reel, NULL);
cl_assert_equal_i(s_num_destructor_calls, 1);
kino_player_set_reel(test_player, NULL, true);
cl_assert_equal_p(test_player->reel, NULL);
cl_assert_equal_i(s_num_destructor_calls, 1);
test_reel = NULL;
}
void test_kino_player__set_reel_does_not_call_destructor(void) {
test_player->owns_reel = false;
kino_player_set_reel(test_player, test_reel, false);
cl_assert_equal_p(test_player->reel, test_reel);
cl_assert_equal_i(s_num_destructor_calls, 0);
kino_player_set_reel(test_player, NULL, true);
cl_assert_equal_p(test_player->reel, NULL);
cl_assert_equal_i(s_num_destructor_calls, 0);
}