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

260 lines
9.8 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 "clar.h"
#include "applib/graphics/gdraw_command.h"
#include "applib/graphics/gdraw_command_list.h"
#include "applib/graphics/gdraw_command_frame.h"
#include "applib/graphics/gdraw_command_private.h"
#include "applib/graphics/gdraw_command_sequence.h"
#include "applib/graphics/gtypes.h"
#include "applib/graphics/graphics.h"
#include "applib/graphics/gpath.h"
#include "util/size.h"
#include "stubs_applib_resource.h"
#include "stubs_memory_layout.h"
#include "stubs_passert.h"
#include "stubs_pbl_malloc.h"
#include "stubs_resources.h"
#include "stubs_syscalls.h"
// Stubs
void graphics_context_set_stroke_color(GContext* ctx, GColor color) {}
void graphics_context_set_fill_color(GContext* ctx, GColor color) {}
void graphics_context_set_stroke_width(GContext* ctx, uint8_t stroke_width) {}
void gpath_draw_stroke(GContext* ctx, GPath* path, bool open) {}
void gpath_draw_filled(GContext* ctx, GPath *path) {}
void graphics_draw_circle(GContext* ctx, GPoint p, uint16_t radius) {}
void graphics_fill_circle(GContext* ctx, GPoint p, uint16_t radius) {}
void graphics_context_move_draw_box(GContext* ctx, GPoint offset) {}
void graphics_line_draw_precise_stroked(GContext* ctx, GPointPrecise p0, GPointPrecise p1) {}
void gpath_fill_precise_internal(GContext *ctx, GPointPrecise *points, size_t num_points) {}
void gpath_draw_outline_precise_internal(GContext *ctx, GPointPrecise *points, size_t num_points,
bool open) {}
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; }
// setup and teardown
void test_gdraw_command_sequence__initialize(void) {
}
void test_gdraw_command_sequence__cleanup(void) {
}
size_t prv_create_test_sequence(GDrawCommandSequence **sequence_ptr) {
size_t size = sizeof(GDrawCommandSequence) + (sizeof(GDrawCommandFrame) * 2) +
(sizeof(GDrawCommand) * 4) + (sizeof(GPoint) * 9);
GDrawCommandSequence *sequence = calloc(1, size);
*sequence_ptr = sequence;
*sequence = (GDrawCommandSequence){
.version = GDRAW_COMMAND_VERSION,
.num_frames = 2,
.play_count = 1,
};
GDrawCommandFrame *frame;
frame = &sequence->frames[0];
*frame = (GDrawCommandFrame) {
.duration = 15,
};
frame->command_list = (GDrawCommandList) {
.num_commands = 3
};
GDrawCommand *command;
command = gdraw_command_list_get_command(&frame->command_list, 0);
GPoint points1[] = { { 3, 97 }, {5, 5} };
*command = (GDrawCommand) {
.type = GDrawCommandTypePath,
.hidden = false,
.stroke_color = GColorRed,
.stroke_width = 1,
.fill_color = GColorBlue,
.path_open = false,
.num_points = ARRAY_LENGTH(points1),
};
memcpy(command->points, points1, sizeof(points1));
command = gdraw_command_list_get_command(&frame->command_list, 1);
*command = (GDrawCommand) {
.type = GDrawCommandTypeCircle,
.hidden = false,
.stroke_color = GColorGreen,
.stroke_width = 1,
.fill_color = GColorOrange,
.radius = 300,
.num_points = 1,
};
command->points[0] = (GPoint) { 1, 2 };
command = gdraw_command_list_get_command(&frame->command_list, 2);
GPoint points2[] = { { 6, 7 }, {5, 5}, { 0, 0 } };
*command = (GDrawCommand) {
.type = GDrawCommandTypePath,
.hidden = false,
.stroke_color = GColorGreen,
.stroke_width = 1,
.fill_color = GColorPurple,
.path_open = false,
.num_points = ARRAY_LENGTH(points2),
};
memcpy(command->points, points2, sizeof(points2));
frame = (GDrawCommandFrame *)(command->points + command->num_points);
*frame = (GDrawCommandFrame) {
.duration = 30,
};
frame->command_list = (GDrawCommandList) {
.num_commands = 1
};
command = gdraw_command_list_get_command(&frame->command_list, 0);
points2[0].x++; // increment x value to distinguish draw command from command in previous frame
*command = (GDrawCommand) {
.type = GDrawCommandTypePath,
.hidden = false,
.stroke_color = GColorRed,
.stroke_width = 5,
.fill_color = GColorBlack,
.path_open = false,
.num_points = ARRAY_LENGTH(points2),
};
memcpy(command->points, points2, sizeof(points2));
return size;
}
// tests
void test_gdraw_command_sequence__validate(void) {
GDrawCommandSequence *sequence;
size_t size = prv_create_test_sequence(&sequence);
cl_assert_equal_i(size, gdraw_command_sequence_get_data_size(sequence));
cl_assert(gdraw_command_sequence_validate(sequence, size));
cl_assert(!gdraw_command_sequence_validate(sequence, size - 1));
cl_assert(!gdraw_command_sequence_validate(sequence, size + 1));
cl_assert(!gdraw_command_sequence_validate(sequence, 0));
sequence->num_frames = 0;
cl_assert(!gdraw_command_sequence_validate(sequence, size));
sequence->num_frames = 1;
cl_assert(!gdraw_command_sequence_validate(sequence, size));
sequence->num_frames = 3;
cl_assert(!gdraw_command_sequence_validate(sequence, size));
sequence->num_frames = 2;
sequence->version = 0xFF;
cl_assert(!gdraw_command_sequence_validate(sequence, size));
free(sequence);
}
void test_gdraw_command_sequence__get_frame_by_elapsed(void) {
GDrawCommandSequence *sequence;
prv_create_test_sequence(&sequence);
GDrawCommandFrame *frame = gdraw_command_sequence_get_frame_by_elapsed(sequence, 0);
cl_assert_equal_p(frame, gdraw_command_sequence_get_frame_by_index(sequence, 0));
cl_assert_equal_p(frame, gdraw_command_sequence_get_frame_by_elapsed(sequence, 14));
frame = gdraw_command_sequence_get_frame_by_elapsed(sequence, 15);
cl_assert_equal_p(frame, gdraw_command_sequence_get_frame_by_index(sequence, 1));;
cl_assert_equal_p(frame, gdraw_command_sequence_get_frame_by_elapsed(sequence, 44));
cl_assert_equal_p(frame, gdraw_command_sequence_get_frame_by_elapsed(sequence, 45));
cl_assert_equal_p(frame, gdraw_command_sequence_get_frame_by_elapsed(sequence, 46));
// test that frame is skipped when the duration is zero (first frame shown will be the first one
// with non-zero duration
frame = gdraw_command_sequence_get_frame_by_elapsed(sequence, 0);
frame->duration = 0;
frame = gdraw_command_sequence_get_frame_by_elapsed(sequence, 0);
cl_assert_equal_p(frame, gdraw_command_sequence_get_frame_by_index(sequence, 1));
frame = gdraw_command_sequence_get_frame_by_index(sequence, 0);
frame->duration = 15;
// test that the sequence loops when the play count is greater than 1
sequence->play_count = 2;
frame = gdraw_command_sequence_get_frame_by_elapsed(sequence, 45);
cl_assert_equal_p(frame, gdraw_command_sequence_get_frame_by_index(sequence, 0));
frame = gdraw_command_sequence_get_frame_by_elapsed(sequence, 45 + 15);
cl_assert_equal_p(frame, gdraw_command_sequence_get_frame_by_index(sequence, 1));
// test that the sequence loops infinitely when the play count is infinite
sequence->play_count = (uint16_t)PLAY_COUNT_INFINITE;
frame = gdraw_command_sequence_get_frame_by_elapsed(sequence, 45 * 5);
cl_assert_equal_p(frame, gdraw_command_sequence_get_frame_by_index(sequence, 0));
frame = gdraw_command_sequence_get_frame_by_elapsed(sequence, (45 + 15) * 5);
cl_assert_equal_p(frame, gdraw_command_sequence_get_frame_by_index(sequence, 1));
// test that the sequence returns the last frame if the play count is zero
sequence->play_count = 0;
frame = gdraw_command_sequence_get_frame_by_elapsed(sequence, 1);
cl_assert_equal_p(frame, gdraw_command_sequence_get_frame_by_index(sequence, 1));
free(sequence);
}
void test_gdraw_command_sequence__get_frame_by_index(void) {
GDrawCommandSequence *sequence;
prv_create_test_sequence(&sequence);
GDrawCommandFrame *frame = gdraw_command_sequence_get_frame_by_index(sequence, 0);
cl_assert_equal_i(frame->duration, 15);
cl_assert_equal_i(frame->command_list.num_commands, 3);
GDrawCommand *command = gdraw_command_list_get_command(&frame->command_list, 2);
cl_assert_equal_i(command->type, GDrawCommandTypePath);
cl_assert_equal_i(command->num_points, 3);
cl_assert_equal_i(command->stroke_color.argb, GColorGreenARGB8);
cl_assert_equal_i(command->fill_color.argb, GColorPurpleARGB8);
cl_assert_equal_i(command->points[0].x, 6);
frame = gdraw_command_sequence_get_frame_by_index(sequence, 1);
cl_assert_equal_i(frame->duration, 30);
cl_assert_equal_i(frame->command_list.num_commands, 1);
command = gdraw_command_list_get_command(&frame->command_list, 0);
cl_assert_equal_i(command->type, GDrawCommandTypePath);
cl_assert_equal_i(command->num_points, 3);
cl_assert_equal_i(command->stroke_color.argb, GColorRedARGB8);
cl_assert_equal_i(command->fill_color.argb, GColorBlackARGB8);
cl_assert_equal_i(command->points[0].x, 7);
cl_assert_equal_p(gdraw_command_sequence_get_frame_by_index(sequence, 2), NULL);
free(sequence);
}
void test_gdraw_command_sequence__clone(void) {
cl_assert_equal_p(gdraw_command_sequence_clone(NULL), NULL);
GDrawCommandSequence *sequence;
prv_create_test_sequence(&sequence);
GDrawCommandSequence *clone = gdraw_command_sequence_clone(sequence);
cl_assert(clone != sequence);
size_t expected_size = gdraw_command_sequence_get_data_size(sequence);
cl_assert_equal_i(gdraw_command_sequence_get_data_size(clone), expected_size);
cl_assert_equal_i(0, memcmp(clone, sequence, expected_size));
free(sequence);
}