change: UI and migrate to gtest

This commit is contained in:
2026-07-27 05:27:32 +08:00
parent dd5508e571
commit a84e75ef47
56 changed files with 4484 additions and 4949 deletions
+136 -136
View File
@@ -32,6 +32,7 @@
// handler is verified through oakengine_app_create_new_project().
#include <assert.h>
#include <gtest/gtest.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -74,7 +75,7 @@ static Cb g_cb;
static int on_confirm_image_sequence(const char *filename, void *userdata)
{
(void) filename;
assert(userdata == &g_cb);
EXPECT_TRUE(userdata == &g_cb);
g_cb.confirm_image_sequence_calls++;
return 1;
}
@@ -83,22 +84,22 @@ static int on_relink_footage(OakEngineFootage **footage, int count,
void *userdata)
{
(void) footage;
assert(userdata == &g_cb);
EXPECT_TRUE(userdata == &g_cb);
g_cb.relink_calls++;
assert(count >= 0);
EXPECT_TRUE(count >= 0);
return 1;
}
static void on_save_project(const char *override_filename, void *userdata)
{
(void) override_filename;
assert(userdata == &g_cb);
EXPECT_TRUE(userdata == &g_cb);
g_cb.save_project_calls++;
}
static int on_close_project(void *userdata)
{
assert(userdata == &g_cb);
EXPECT_TRUE(userdata == &g_cb);
g_cb.close_project_calls++;
// Mirror the application's close: detach and delete the open project
OakEngineProject *p = oakengine_app_open_project();
@@ -112,7 +113,7 @@ static int on_close_project(void *userdata)
static void on_load_layout(const void *layout, void *userdata)
{
(void) layout;
assert(userdata == &g_cb);
EXPECT_TRUE(userdata == &g_cb);
g_cb.load_layout_calls++;
}
@@ -121,7 +122,7 @@ static int on_otio_import(OakEngineSequence **sequences, int count,
{
(void) sequences;
(void) count;
assert(userdata == &g_cb);
EXPECT_TRUE(userdata == &g_cb);
g_cb.otio_import_calls++;
return 1;
}
@@ -129,7 +130,7 @@ static int on_otio_import(OakEngineSequence **sequences, int count,
static void on_status_message_show(const char *message, int timeout,
void *userdata)
{
assert(userdata == &g_cb);
EXPECT_TRUE(userdata == &g_cb);
g_cb.status_show_calls++;
snprintf(g_cb.last_status, sizeof(g_cb.last_status), "%s", message);
g_cb.last_timeout = timeout;
@@ -137,61 +138,61 @@ static void on_status_message_show(const char *message, int timeout,
static void on_status_message_clear(void *userdata)
{
assert(userdata == &g_cb);
EXPECT_TRUE(userdata == &g_cb);
g_cb.status_clear_calls++;
}
static void on_cache_full_warning(void *userdata)
{
assert(userdata == &g_cb);
EXPECT_TRUE(userdata == &g_cb);
g_cb.cache_full_calls++;
}
static void on_active_project_changed(OakEngineProject *project,
void *userdata)
{
assert(userdata == &g_cb);
EXPECT_TRUE(userdata == &g_cb);
g_cb.active_project_calls++;
g_cb.last_project = project;
}
static void on_tool_changed(int tool, void *userdata)
{
assert(userdata == &g_cb);
EXPECT_TRUE(userdata == &g_cb);
g_cb.tool_changed_calls++;
g_cb.last_tool = tool;
}
static void on_addable_object_changed(int object, void *userdata)
{
assert(userdata == &g_cb);
EXPECT_TRUE(userdata == &g_cb);
g_cb.addable_changed_calls++;
g_cb.last_addable = object;
}
static void on_snapping_changed(int snapping, void *userdata)
{
assert(userdata == &g_cb);
EXPECT_TRUE(userdata == &g_cb);
g_cb.snapping_changed_calls++;
g_cb.last_snapping = snapping;
}
static void on_timecode_display_changed(int display, void *userdata)
{
assert(userdata == &g_cb);
EXPECT_TRUE(userdata == &g_cb);
g_cb.timecode_changed_calls++;
g_cb.last_display = display;
}
static void on_open_recent_list_changed(void *userdata)
{
assert(userdata == &g_cb);
EXPECT_TRUE(userdata == &g_cb);
g_cb.recent_changed_calls++;
}
static void on_color_picker_enabled(int enabled, void *userdata)
{
assert(userdata == &g_cb);
EXPECT_TRUE(userdata == &g_cb);
g_cb.color_picker_calls++;
g_cb.last_color_picker = enabled;
}
@@ -223,9 +224,9 @@ static OakEngineAppCallbacks make_callbacks(void)
static char *query0(int (*fn)(char *, int))
{
const int needed = fn(NULL, 0);
assert(needed >= 0);
EXPECT_TRUE(needed >= 0);
char *buf = (char *) malloc(size_t(needed) + 1);
assert(fn(buf, needed + 1) == needed);
EXPECT_TRUE(fn(buf, needed + 1) == needed);
buf[needed] = '\0';
return buf;
}
@@ -238,99 +239,99 @@ static void test_create_and_params(void)
params.startup_project = "/tmp/startup.ove";
// NULL params would be valid too, but verify the values round-trip
assert(oakengine_app_create(&params) == OAKENGINE_OK);
assert(oakengine_app_run_mode() == OAKENGINE_APP_RUN_HEADLESS_PRE_CACHE);
assert(oakengine_app_fullscreen() == 1);
EXPECT_TRUE(oakengine_app_create(&params) == OAKENGINE_OK);
EXPECT_TRUE(oakengine_app_run_mode() == OAKENGINE_APP_RUN_HEADLESS_PRE_CACHE);
EXPECT_TRUE(oakengine_app_fullscreen() == 1);
char *startup = query0(oakengine_app_startup_project);
assert(strcmp(startup, "/tmp/startup.ove") == 0);
EXPECT_TRUE(strcmp(startup, "/tmp/startup.ove") == 0);
free(startup);
// Only one application core may exist
assert(oakengine_app_create(NULL) == OAKENGINE_E_STATE);
EXPECT_TRUE(oakengine_app_create(NULL) == OAKENGINE_E_STATE);
}
static void test_start_stop(void)
{
// Not started yet
assert(oakengine_app_stop() == OAKENGINE_E_STATE);
EXPECT_TRUE(oakengine_app_stop() == OAKENGINE_E_STATE);
// Full engine start (config, managers, autorecovery, recent list)
assert(oakengine_app_start() == OAKENGINE_OK);
assert(oakengine_app_start() == OAKENGINE_E_STATE);
EXPECT_TRUE(oakengine_app_start() == OAKENGINE_OK);
EXPECT_TRUE(oakengine_app_start() == OAKENGINE_E_STATE);
assert(oakengine_app_stop() == OAKENGINE_OK);
assert(oakengine_app_stop() == OAKENGINE_E_STATE);
EXPECT_TRUE(oakengine_app_stop() == OAKENGINE_OK);
EXPECT_TRUE(oakengine_app_stop() == OAKENGINE_E_STATE);
}
static void test_tool_state(void)
{
OakEngineAppCallbacks cb = make_callbacks();
assert(oakengine_app_set_callbacks(&cb) == OAKENGINE_OK);
EXPECT_TRUE(oakengine_app_set_callbacks(&cb) == OAKENGINE_OK);
// Tool (k_none=0 .. k_track_select=13, k_count=14)
assert(oakengine_app_set_tool(4) == OAKENGINE_OK);
assert(oakengine_app_tool() == 4);
assert(g_cb.tool_changed_calls == 1 && g_cb.last_tool == 4);
assert(oakengine_app_set_tool(-1) == OAKENGINE_E_INVALID);
assert(oakengine_app_set_tool(999) == OAKENGINE_E_INVALID);
EXPECT_TRUE(oakengine_app_set_tool(4) == OAKENGINE_OK);
EXPECT_TRUE(oakengine_app_tool() == 4);
EXPECT_TRUE(g_cb.tool_changed_calls == 1 && g_cb.last_tool == 4);
EXPECT_TRUE(oakengine_app_set_tool(-1) == OAKENGINE_E_INVALID);
EXPECT_TRUE(oakengine_app_set_tool(999) == OAKENGINE_E_INVALID);
// Addable object
assert(oakengine_app_set_addable_object(2) == OAKENGINE_OK);
assert(oakengine_app_addable_object() == 2);
assert(g_cb.addable_changed_calls == 1 && g_cb.last_addable == 2);
assert(oakengine_app_set_addable_object(999) == OAKENGINE_E_INVALID);
EXPECT_TRUE(oakengine_app_set_addable_object(2) == OAKENGINE_OK);
EXPECT_TRUE(oakengine_app_addable_object() == 2);
EXPECT_TRUE(g_cb.addable_changed_calls == 1 && g_cb.last_addable == 2);
EXPECT_TRUE(oakengine_app_set_addable_object(999) == OAKENGINE_E_INVALID);
// Snapping
assert(oakengine_app_set_snapping(0) == OAKENGINE_OK);
assert(oakengine_app_snapping() == 0);
assert(g_cb.snapping_changed_calls == 1 && g_cb.last_snapping == 0);
assert(oakengine_app_set_snapping(1) == OAKENGINE_OK);
assert(oakengine_app_snapping() == 1);
assert(g_cb.snapping_changed_calls == 2 && g_cb.last_snapping == 1);
EXPECT_TRUE(oakengine_app_set_snapping(0) == OAKENGINE_OK);
EXPECT_TRUE(oakengine_app_snapping() == 0);
EXPECT_TRUE(g_cb.snapping_changed_calls == 1 && g_cb.last_snapping == 0);
EXPECT_TRUE(oakengine_app_set_snapping(1) == OAKENGINE_OK);
EXPECT_TRUE(oakengine_app_snapping() == 1);
EXPECT_TRUE(g_cb.snapping_changed_calls == 2 && g_cb.last_snapping == 1);
// Timecode display (0..4)
assert(oakengine_app_set_timecode_display(3) == OAKENGINE_OK);
assert(oakengine_app_timecode_display() == 3);
assert(g_cb.timecode_changed_calls == 1 && g_cb.last_display == 3);
assert(oakengine_app_set_timecode_display(999) == OAKENGINE_E_INVALID);
EXPECT_TRUE(oakengine_app_set_timecode_display(3) == OAKENGINE_OK);
EXPECT_TRUE(oakengine_app_timecode_display() == 3);
EXPECT_TRUE(g_cb.timecode_changed_calls == 1 && g_cb.last_display == 3);
EXPECT_TRUE(oakengine_app_set_timecode_display(999) == OAKENGINE_E_INVALID);
// Selected transition
assert(oakengine_app_set_selected_transition("crossdissolve") ==
EXPECT_TRUE(oakengine_app_set_selected_transition("crossdissolve") ==
OAKENGINE_OK);
char *transition = query0(oakengine_app_selected_transition);
assert(strcmp(transition, "crossdissolve") == 0);
EXPECT_TRUE(strcmp(transition, "crossdissolve") == 0);
free(transition);
assert(oakengine_app_set_selected_transition(NULL) == OAKENGINE_OK);
EXPECT_TRUE(oakengine_app_set_selected_transition(NULL) == OAKENGINE_OK);
transition = query0(oakengine_app_selected_transition);
assert(transition[0] == '\0');
EXPECT_TRUE(transition[0] == '\0');
free(transition);
// Magic flag
assert(oakengine_app_set_magic(1) == OAKENGINE_OK);
assert(oakengine_app_is_magic_enabled() == 1);
assert(oakengine_app_set_magic(0) == OAKENGINE_OK);
assert(oakengine_app_is_magic_enabled() == 0);
EXPECT_TRUE(oakengine_app_set_magic(1) == OAKENGINE_OK);
EXPECT_TRUE(oakengine_app_is_magic_enabled() == 1);
EXPECT_TRUE(oakengine_app_set_magic(0) == OAKENGINE_OK);
EXPECT_TRUE(oakengine_app_is_magic_enabled() == 0);
}
static void test_status_and_pixel_sampling(void)
{
assert(oakengine_app_show_status_message("hello", 250) == OAKENGINE_OK);
assert(g_cb.status_show_calls == 1);
assert(strcmp(g_cb.last_status, "hello") == 0);
assert(g_cb.last_timeout == 250);
assert(oakengine_app_show_status_message(NULL, 0) == OAKENGINE_E_INVALID);
EXPECT_TRUE(oakengine_app_show_status_message("hello", 250) == OAKENGINE_OK);
EXPECT_TRUE(g_cb.status_show_calls == 1);
EXPECT_TRUE(strcmp(g_cb.last_status, "hello") == 0);
EXPECT_TRUE(g_cb.last_timeout == 250);
EXPECT_TRUE(oakengine_app_show_status_message(NULL, 0) == OAKENGINE_E_INVALID);
assert(oakengine_app_clear_status_message() == OAKENGINE_OK);
assert(g_cb.status_clear_calls == 1);
EXPECT_TRUE(oakengine_app_clear_status_message() == OAKENGINE_OK);
EXPECT_TRUE(g_cb.status_clear_calls == 1);
// Pixel sampling ref-count emits only when crossing zero
assert(oakengine_app_request_pixel_sampling(1) == OAKENGINE_OK);
assert(oakengine_app_request_pixel_sampling(1) == OAKENGINE_OK);
assert(g_cb.color_picker_calls == 1 && g_cb.last_color_picker == 1);
assert(oakengine_app_request_pixel_sampling(0) == OAKENGINE_OK);
assert(g_cb.color_picker_calls == 1);
assert(oakengine_app_request_pixel_sampling(0) == OAKENGINE_OK);
assert(g_cb.color_picker_calls == 2 && g_cb.last_color_picker == 0);
EXPECT_TRUE(oakengine_app_request_pixel_sampling(1) == OAKENGINE_OK);
EXPECT_TRUE(oakengine_app_request_pixel_sampling(1) == OAKENGINE_OK);
EXPECT_TRUE(g_cb.color_picker_calls == 1 && g_cb.last_color_picker == 1);
EXPECT_TRUE(oakengine_app_request_pixel_sampling(0) == OAKENGINE_OK);
EXPECT_TRUE(g_cb.color_picker_calls == 1);
EXPECT_TRUE(oakengine_app_request_pixel_sampling(0) == OAKENGINE_OK);
EXPECT_TRUE(g_cb.color_picker_calls == 2 && g_cb.last_color_picker == 0);
}
static void test_project_lifecycle(void)
@@ -339,80 +340,80 @@ static void test_project_lifecycle(void)
const int active_before = g_cb.active_project_calls;
// New project goes through the close handler and becomes active
assert(oakengine_app_create_new_project() == OAKENGINE_OK);
assert(g_cb.close_project_calls == 1);
EXPECT_TRUE(oakengine_app_create_new_project() == OAKENGINE_OK);
EXPECT_TRUE(g_cb.close_project_calls == 1);
OakEngineProject *p = oakengine_app_open_project();
assert(p != NULL);
assert(g_cb.active_project_calls > active_before);
assert(g_cb.last_project == p);
EXPECT_TRUE(p != NULL);
EXPECT_TRUE(g_cb.active_project_calls > active_before);
EXPECT_TRUE(g_cb.last_project == p);
// Replacing it invokes the close handler again
assert(oakengine_app_create_new_project() == OAKENGINE_OK);
assert(g_cb.close_project_calls == 2);
EXPECT_TRUE(oakengine_app_create_new_project() == OAKENGINE_OK);
EXPECT_TRUE(g_cb.close_project_calls == 2);
p = oakengine_app_open_project();
assert(p != NULL);
EXPECT_TRUE(p != NULL);
// Saving a project without a filename keeps the recent list unchanged
assert(oakengine_app_clear_recent_projects() == OAKENGINE_OK);
assert(oakengine_app_on_project_saved(p) == OAKENGINE_OK);
assert(oakengine_app_recent_projects_count() == 0);
EXPECT_TRUE(oakengine_app_clear_recent_projects() == OAKENGINE_OK);
EXPECT_TRUE(oakengine_app_on_project_saved(p) == OAKENGINE_OK);
EXPECT_TRUE(oakengine_app_recent_projects_count() == 0);
// Detach and free it again
assert(oakengine_app_set_active_project(NULL) == OAKENGINE_OK);
assert(oakengine_app_open_project() == NULL);
assert(g_cb.last_project == NULL);
EXPECT_TRUE(oakengine_app_set_active_project(NULL) == OAKENGINE_OK);
EXPECT_TRUE(oakengine_app_open_project() == NULL);
EXPECT_TRUE(g_cb.last_project == NULL);
oakengine_project_free(p);
// add_open_project adopts an externally created project
p = oakengine_project_create();
assert(p != NULL);
assert(oakengine_project_new(p) == OAKENGINE_OK);
assert(oakengine_app_add_open_project(p, 0) == OAKENGINE_OK);
assert(oakengine_app_open_project() == p);
assert(oakengine_app_set_active_project(NULL) == OAKENGINE_OK);
EXPECT_TRUE(p != NULL);
EXPECT_TRUE(oakengine_project_new(p) == OAKENGINE_OK);
EXPECT_TRUE(oakengine_app_add_open_project(p, 0) == OAKENGINE_OK);
EXPECT_TRUE(oakengine_app_open_project() == p);
EXPECT_TRUE(oakengine_app_set_active_project(NULL) == OAKENGINE_OK);
oakengine_project_free(p);
// NULL tolerance
assert(oakengine_app_add_open_project(NULL, 0) == OAKENGINE_E_INVALID);
assert(oakengine_app_on_project_saved(NULL) == OAKENGINE_E_INVALID);
assert(oakengine_app_add_open_project_from_task(NULL, 0) ==
EXPECT_TRUE(oakengine_app_add_open_project(NULL, 0) == OAKENGINE_E_INVALID);
EXPECT_TRUE(oakengine_app_on_project_saved(NULL) == OAKENGINE_E_INVALID);
EXPECT_TRUE(oakengine_app_add_open_project_from_task(NULL, 0) ==
OAKENGINE_E_INVALID);
assert(oakengine_app_add_recovery_project_from_task(NULL) ==
EXPECT_TRUE(oakengine_app_add_recovery_project_from_task(NULL) ==
OAKENGINE_E_INVALID);
}
static void test_recent_projects(void)
{
// Start from a clean list
assert(oakengine_app_clear_recent_projects() == OAKENGINE_OK);
assert(oakengine_app_recent_projects_count() == 0);
EXPECT_TRUE(oakengine_app_clear_recent_projects() == OAKENGINE_OK);
EXPECT_TRUE(oakengine_app_recent_projects_count() == 0);
const int changes_before = g_cb.recent_changed_calls;
// A saved project lands in the recent list through on_project_saved
OakEngineProject *p = oakengine_project_create();
assert(p != NULL);
assert(oakengine_project_new(p) == OAKENGINE_OK);
assert(oakengine_project_save(p, "oakengine_app_test_recent.ove") ==
EXPECT_TRUE(p != NULL);
EXPECT_TRUE(oakengine_project_new(p) == OAKENGINE_OK);
EXPECT_TRUE(oakengine_project_save(p, "oakengine_app_test_recent.ove") ==
OAKENGINE_OK);
assert(oakengine_app_on_project_saved(p) == OAKENGINE_OK);
assert(oakengine_app_recent_projects_count() == 1);
assert(g_cb.recent_changed_calls > changes_before);
EXPECT_TRUE(oakengine_app_on_project_saved(p) == OAKENGINE_OK);
EXPECT_TRUE(oakengine_app_recent_projects_count() == 1);
EXPECT_TRUE(g_cb.recent_changed_calls > changes_before);
const int needed = oakengine_app_recent_project_at(0, NULL, 0);
assert(needed > 0);
EXPECT_TRUE(needed > 0);
char *buf = (char *) malloc(size_t(needed) + 1);
assert(oakengine_app_recent_project_at(0, buf, needed + 1) == needed);
EXPECT_TRUE(oakengine_app_recent_project_at(0, buf, needed + 1) == needed);
buf[needed] = '\0';
assert(strstr(buf, "oakengine_app_test_recent.ove") != NULL);
EXPECT_TRUE(strstr(buf, "oakengine_app_test_recent.ove") != NULL);
free(buf);
// Out-of-range access is rejected (the engine would assert otherwise)
assert(oakengine_app_recent_project_at(5, NULL, 0) ==
EXPECT_TRUE(oakengine_app_recent_project_at(5, NULL, 0) ==
OAKENGINE_E_NOT_FOUND);
assert(oakengine_app_remove_recent_project(5) == OAKENGINE_E_NOT_FOUND);
EXPECT_TRUE(oakengine_app_remove_recent_project(5) == OAKENGINE_E_NOT_FOUND);
assert(oakengine_app_remove_recent_project(0) == OAKENGINE_OK);
assert(oakengine_app_recent_projects_count() == 0);
EXPECT_TRUE(oakengine_app_remove_recent_project(0) == OAKENGINE_OK);
EXPECT_TRUE(oakengine_app_recent_projects_count() == 0);
remove("oakengine_app_test_recent.ove");
oakengine_project_free(p);
@@ -420,54 +421,54 @@ static void test_recent_projects(void)
static void test_clipboard(void)
{
assert(oakengine_app_copy_to_clipboard("hello clipboard") ==
EXPECT_TRUE(oakengine_app_copy_to_clipboard("hello clipboard") ==
OAKENGINE_OK);
char *text = query0(oakengine_app_paste_from_clipboard);
assert(strcmp(text, "hello clipboard") == 0);
EXPECT_TRUE(strcmp(text, "hello clipboard") == 0);
free(text);
assert(oakengine_app_copy_to_clipboard(NULL) == OAKENGINE_E_INVALID);
EXPECT_TRUE(oakengine_app_copy_to_clipboard(NULL) == OAKENGINE_E_INVALID);
}
static void test_footage_filter(void)
{
assert(oakengine_app_is_footage_extension_allowed("movie.mp4") == 1);
assert(oakengine_app_is_footage_extension_allowed("IMAGE.PNG") == 1);
assert(oakengine_app_is_footage_extension_allowed("doc.txt") == 0);
assert(oakengine_app_is_footage_extension_allowed(NULL) ==
EXPECT_TRUE(oakengine_app_is_footage_extension_allowed("movie.mp4") == 1);
EXPECT_TRUE(oakengine_app_is_footage_extension_allowed("IMAGE.PNG") == 1);
EXPECT_TRUE(oakengine_app_is_footage_extension_allowed("doc.txt") == 0);
EXPECT_TRUE(oakengine_app_is_footage_extension_allowed(NULL) ==
OAKENGINE_E_INVALID);
char *filter = query0(oakengine_app_footage_file_dialog_filter);
assert(strstr(filter, "*.mp4") != NULL);
assert(strstr(filter, ";;") != NULL);
EXPECT_TRUE(strstr(filter, "*.mp4") != NULL);
EXPECT_TRUE(strstr(filter, ";;") != NULL);
free(filter);
}
static void test_misc(void)
{
// Unknown locale is reported as "not found" without failing
assert(oakengine_app_set_language("definitely_not_a_locale_xx") == 0);
assert(oakengine_app_set_language(NULL) == OAKENGINE_E_INVALID);
EXPECT_TRUE(oakengine_app_set_language("definitely_not_a_locale_xx") == 0);
EXPECT_TRUE(oakengine_app_set_language(NULL) == OAKENGINE_E_INVALID);
assert(oakengine_app_set_autorecovery_interval(5) == OAKENGINE_OK);
assert(oakengine_app_set_use_proxy_media(1) == OAKENGINE_OK);
EXPECT_TRUE(oakengine_app_set_autorecovery_interval(5) == OAKENGINE_OK);
EXPECT_TRUE(oakengine_app_set_use_proxy_media(1) == OAKENGINE_OK);
char *index = query0(oakengine_app_auto_recovery_index_filename);
assert(strstr(index, "unrecovered") != NULL);
EXPECT_TRUE(strstr(index, "unrecovered") != NULL);
free(index);
assert(oakengine_app_undo_stack() != NULL);
EXPECT_TRUE(oakengine_app_undo_stack() != NULL);
}
static void test_create_sequence(void)
{
OakEngineProject *p = oakengine_project_create();
assert(p != NULL);
assert(oakengine_project_new(p) == OAKENGINE_OK);
EXPECT_TRUE(p != NULL);
EXPECT_TRUE(oakengine_project_new(p) == OAKENGINE_OK);
OakEngineSequence *s = oakengine_app_create_sequence(p, "Seq %1");
assert(s != NULL);
EXPECT_TRUE(s != NULL);
assert(oakengine_app_create_sequence(NULL, NULL) == NULL);
EXPECT_TRUE(oakengine_app_create_sequence(NULL, NULL) == NULL);
// The returned sequence is not yet part of the project (owned by the
// caller); this test intentionally leaves it unparented.
@@ -476,23 +477,23 @@ static void test_create_sequence(void)
static void test_callbacks_clear(void)
{
assert(oakengine_app_set_callbacks(NULL) == OAKENGINE_OK);
EXPECT_TRUE(oakengine_app_set_callbacks(NULL) == OAKENGINE_OK);
// State changes still work, but nothing is delivered anymore
const int calls = g_cb.tool_changed_calls;
assert(oakengine_app_set_tool(2) == OAKENGINE_OK);
assert(oakengine_app_tool() == 2);
assert(g_cb.tool_changed_calls == calls);
EXPECT_TRUE(oakengine_app_set_tool(2) == OAKENGINE_OK);
EXPECT_TRUE(oakengine_app_tool() == 2);
EXPECT_TRUE(g_cb.tool_changed_calls == calls);
}
int main(void)
TEST(OakEngineApp, Main)
{
// The facade brings up its own offscreen application object
test_create_and_params();
test_start_stop();
// Engine services (incl. renderer manager for set_active_project)
assert(oakengine_init(OAKENGINE_INIT_HEADLESS | OAKENGINE_INIT_RENDER) ==
EXPECT_TRUE(oakengine_init(OAKENGINE_INIT_HEADLESS | OAKENGINE_INIT_RENDER) ==
OAKENGINE_OK);
test_tool_state();
@@ -505,6 +506,5 @@ int main(void)
test_create_sequence();
test_callbacks_clear();
assert(oakengine_shutdown() == OAKENGINE_OK);
return 0;
EXPECT_TRUE(oakengine_shutdown() == OAKENGINE_OK);
}
+56 -56
View File
@@ -24,6 +24,7 @@
// output_params_changed event subscription. No GL or QApplication required.
#include <assert.h>
#include <gtest/gtest.h>
#include <stdio.h>
#include <string.h>
@@ -36,8 +37,8 @@ static void *g_output_params_changed_source;
static void on_output_params_changed(const oakengine_event *event, void *)
{
assert(event != NULL);
assert(event->id == OAKENGINE_EVENT_AUDIO_MANAGER_OUTPUT_PARAMS_CHANGED);
EXPECT_TRUE(event != NULL);
EXPECT_TRUE(event->id == OAKENGINE_EVENT_AUDIO_MANAGER_OUTPUT_PARAMS_CHANGED);
g_output_params_changed_count++;
g_output_params_changed_source = event->source;
}
@@ -45,71 +46,71 @@ static void on_output_params_changed(const oakengine_event *event, void *)
static void test_instance_lifecycle(void)
{
// No instance before create.
assert(oakengine_audio_manager_handle() == NULL);
assert(oakengine_audio_get_output_device() == -1);
assert(oakengine_audio_get_input_device() == -1);
assert(oakengine_audio_clear_buffered_output() == OAKENGINE_E_STATE);
assert(oakengine_audio_stop_recording() == OAKENGINE_E_STATE);
EXPECT_TRUE(oakengine_audio_manager_handle() == NULL);
EXPECT_TRUE(oakengine_audio_get_output_device() == -1);
EXPECT_TRUE(oakengine_audio_get_input_device() == -1);
EXPECT_TRUE(oakengine_audio_clear_buffered_output() == OAKENGINE_E_STATE);
EXPECT_TRUE(oakengine_audio_stop_recording() == OAKENGINE_E_STATE);
assert(oakengine_audio_create_instance() == OAKENGINE_OK);
assert(oakengine_audio_manager_handle() != NULL);
EXPECT_TRUE(oakengine_audio_create_instance() == OAKENGINE_OK);
EXPECT_TRUE(oakengine_audio_manager_handle() != NULL);
// Idempotent create is allowed.
assert(oakengine_audio_create_instance() == OAKENGINE_OK);
assert(oakengine_audio_manager_handle() != NULL);
EXPECT_TRUE(oakengine_audio_create_instance() == OAKENGINE_OK);
EXPECT_TRUE(oakengine_audio_manager_handle() != NULL);
oakengine_audio_destroy_instance();
assert(oakengine_audio_manager_handle() == NULL);
EXPECT_TRUE(oakengine_audio_manager_handle() == NULL);
// Idempotent destroy is allowed.
assert(oakengine_audio_destroy_instance() == OAKENGINE_OK);
assert(oakengine_audio_manager_handle() == NULL);
EXPECT_TRUE(oakengine_audio_destroy_instance() == OAKENGINE_OK);
EXPECT_TRUE(oakengine_audio_manager_handle() == NULL);
}
static void test_device_round_trip(void)
{
assert(oakengine_audio_create_instance() == OAKENGINE_OK);
EXPECT_TRUE(oakengine_audio_create_instance() == OAKENGINE_OK);
void *handle = oakengine_audio_manager_handle();
assert(handle != NULL);
EXPECT_TRUE(handle != NULL);
// Default is usually paNoDevice (-1) in headless environments.
const int64_t original_output = oakengine_audio_get_output_device();
const int64_t original_input = oakengine_audio_get_input_device();
// Setting a value should change the returned value.
assert(oakengine_audio_set_output_device(42) == OAKENGINE_OK);
assert(oakengine_audio_get_output_device() == 42);
EXPECT_TRUE(oakengine_audio_set_output_device(42) == OAKENGINE_OK);
EXPECT_TRUE(oakengine_audio_get_output_device() == 42);
assert(oakengine_audio_set_input_device(43) == OAKENGINE_OK);
assert(oakengine_audio_get_input_device() == 43);
EXPECT_TRUE(oakengine_audio_set_input_device(43) == OAKENGINE_OK);
EXPECT_TRUE(oakengine_audio_get_input_device() == 43);
// hard_reset re-initializes PortAudio and should not crash.
assert(oakengine_audio_hard_reset() == OAKENGINE_OK);
assert(oakengine_audio_manager_handle() == handle);
EXPECT_TRUE(oakengine_audio_hard_reset() == OAKENGINE_OK);
EXPECT_TRUE(oakengine_audio_manager_handle() == handle);
// Restore original values.
assert(oakengine_audio_set_output_device(original_output) == OAKENGINE_OK);
assert(oakengine_audio_set_input_device(original_input) == OAKENGINE_OK);
assert(oakengine_audio_get_output_device() == original_output);
assert(oakengine_audio_get_input_device() == original_input);
EXPECT_TRUE(oakengine_audio_set_output_device(original_output) == OAKENGINE_OK);
EXPECT_TRUE(oakengine_audio_set_input_device(original_input) == OAKENGINE_OK);
EXPECT_TRUE(oakengine_audio_get_output_device() == original_output);
EXPECT_TRUE(oakengine_audio_get_input_device() == original_input);
oakengine_audio_destroy_instance();
}
static void test_push_to_output_errors(void)
{
assert(oakengine_audio_create_instance() == OAKENGINE_OK);
EXPECT_TRUE(oakengine_audio_create_instance() == OAKENGINE_OK);
char error_buf[256];
memset(error_buf, 0, sizeof(error_buf));
// NULL params is rejected without crashing.
assert(oakengine_audio_push_to_output(NULL, "x", 1, error_buf,
EXPECT_TRUE(oakengine_audio_push_to_output(NULL, "x", 1, error_buf,
sizeof(error_buf)) ==
OAKENGINE_E_INVALID);
// NULL samples is rejected.
assert(oakengine_audio_push_to_output((const OakAudioParams *)1, NULL, 1,
EXPECT_TRUE(oakengine_audio_push_to_output((const OakAudioParams *)1, NULL, 1,
error_buf, sizeof(error_buf)) ==
OAKENGINE_E_INVALID);
@@ -118,9 +119,9 @@ static void test_push_to_output_errors(void)
static void test_output_params_changed_event(void)
{
assert(oakengine_audio_create_instance() == OAKENGINE_OK);
EXPECT_TRUE(oakengine_audio_create_instance() == OAKENGINE_OK);
void *handle = oakengine_audio_manager_handle();
assert(handle != NULL);
EXPECT_TRUE(handle != NULL);
g_output_params_changed_count = 0;
g_output_params_changed_source = NULL;
@@ -128,22 +129,22 @@ static void test_output_params_changed_event(void)
const int64_t sub = oakengine_event_subscribe(
handle, OAKENGINE_EVENT_AUDIO_MANAGER_OUTPUT_PARAMS_CHANGED,
on_output_params_changed, NULL);
assert(sub > 0);
EXPECT_TRUE(sub > 0);
const int64_t original_output = oakengine_audio_get_output_device();
assert(oakengine_audio_set_output_device(84) == OAKENGINE_OK);
assert(g_output_params_changed_count >= 1);
assert(g_output_params_changed_source == handle);
EXPECT_TRUE(oakengine_audio_set_output_device(84) == OAKENGINE_OK);
EXPECT_TRUE(g_output_params_changed_count >= 1);
EXPECT_TRUE(g_output_params_changed_source == handle);
assert(oakengine_event_unsubscribe(sub) == OAKENGINE_OK);
EXPECT_TRUE(oakengine_event_unsubscribe(sub) == OAKENGINE_OK);
// No further deliveries after unsubscribe.
const int count_after_unsub = g_output_params_changed_count;
assert(oakengine_audio_set_output_device(85) == OAKENGINE_OK);
assert(g_output_params_changed_count == count_after_unsub);
EXPECT_TRUE(oakengine_audio_set_output_device(85) == OAKENGINE_OK);
EXPECT_TRUE(g_output_params_changed_count == count_after_unsub);
// Restore original value.
assert(oakengine_audio_set_output_device(original_output) == OAKENGINE_OK);
EXPECT_TRUE(oakengine_audio_set_output_device(original_output) == OAKENGINE_OK);
oakengine_audio_destroy_instance();
}
@@ -152,43 +153,43 @@ static void test_audio_sync_algorithms(void)
{
// place_by_waveform_offset: a 1-second positive offset at 48 kHz
oak_audio_sync_placement placement;
assert(oakengine_audio_sync_place_by_waveform_offset(
EXPECT_TRUE(oakengine_audio_sync_place_by_waveform_offset(
0, 1, 48000, 48000, &placement) == OAKENGINE_OK);
assert(placement.valid);
assert(placement.timeline_in_num == 1 && placement.timeline_in_den == 1);
EXPECT_TRUE(placement.valid);
EXPECT_TRUE(placement.timeline_in_num == 1 && placement.timeline_in_den == 1);
// place_by_source_time: matching source/media in points -> same timeline in
oak_audio_sync_source_clip ref = { 0, 1, 0, 1, 1 };
oak_audio_sync_source_clip cand = { 0, 1, 0, 1, 1 };
assert(oakengine_audio_sync_place_by_source_time(
EXPECT_TRUE(oakengine_audio_sync_place_by_source_time(
&ref, &cand, 5, 1, &placement) == OAKENGINE_OK);
assert(placement.valid);
assert(placement.timeline_in_num == 5 && placement.timeline_in_den == 1);
EXPECT_TRUE(placement.valid);
EXPECT_TRUE(placement.timeline_in_num == 5 && placement.timeline_in_den == 1);
// estimate_envelope_offset: identical envelopes -> zero offset, high
// confidence
double envelope[10] = { 0, 1, 2, 3, 4, 5, 4, 3, 2, 1 };
oak_audio_waveform_offset offset;
assert(oakengine_audio_estimate_envelope_offset(
EXPECT_TRUE(oakengine_audio_estimate_envelope_offset(
envelope, 10, envelope, 10, NULL, 0, NULL, 0, 1, 5,
&offset) == OAKENGINE_OK);
assert(offset.valid);
assert(offset.offset_samples == 0);
assert(offset.confidence > 0.99);
EXPECT_TRUE(offset.valid);
EXPECT_TRUE(offset.offset_samples == 0);
EXPECT_TRUE(offset.confidence > 0.99);
// estimate_stretch_and_offset: identical envelopes at rate 1 -> zero offset
oak_audio_waveform_stretch_offset stretch;
assert(oakengine_audio_estimate_stretch_and_offset(
EXPECT_TRUE(oakengine_audio_estimate_stretch_and_offset(
envelope, 10, envelope, 10, NULL, 0, NULL, 0, 1, 5, 0.9, 1.1,
0.05, &stretch) == OAKENGINE_OK);
assert(stretch.valid);
assert(stretch.offset_samples == 0);
assert(stretch.rate > 0.99 && stretch.rate < 1.01);
EXPECT_TRUE(stretch.valid);
EXPECT_TRUE(stretch.offset_samples == 0);
EXPECT_TRUE(stretch.rate > 0.99 && stretch.rate < 1.01);
}
int main(void)
TEST(OakEngineAudio, Main)
{
assert(oakengine_init(OAKENGINE_INIT_HEADLESS) == OAKENGINE_OK);
EXPECT_TRUE(oakengine_init(OAKENGINE_INIT_HEADLESS) == OAKENGINE_OK);
test_instance_lifecycle();
test_device_round_trip();
@@ -199,5 +200,4 @@ int main(void)
oakengine_shutdown();
printf("oakengine_audio_test: OK\n");
return 0;
}
+105 -106
View File
@@ -29,6 +29,7 @@
// robustness checks (NULL handling, error paths) still run.
#include <assert.h>
#include <gtest/gtest.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
@@ -53,13 +54,13 @@ static void make_tmpdir(void)
#if defined(_WIN32)
char base[MAX_PATH];
const DWORD len = GetTempPathA(MAX_PATH, base);
assert(len > 0 && len < MAX_PATH);
EXPECT_TRUE(len > 0 && len < MAX_PATH);
snprintf(g_tmpdir, sizeof(g_tmpdir), "%soakengine_color_test_%lu", base,
(unsigned long)GetCurrentProcessId());
assert(_mkdir(g_tmpdir) == 0);
EXPECT_TRUE(_mkdir(g_tmpdir) == 0);
#else
strcpy(g_tmpdir, "/tmp/oakengine_color_test_XXXXXX");
assert(mkdtemp(g_tmpdir) != NULL);
EXPECT_TRUE(mkdtemp(g_tmpdir) != NULL);
#endif
}
@@ -71,59 +72,59 @@ static void test_null_robustness(void)
double rgb[3];
double rgba[4] = { 0, 0, 0, 0 };
assert(oakengine_color_manager_from_project(NULL) == NULL);
assert(oakengine_color_manager_get_config_filename(NULL, buf,
EXPECT_TRUE(oakengine_color_manager_from_project(NULL) == NULL);
EXPECT_TRUE(oakengine_color_manager_get_config_filename(NULL, buf,
sizeof(buf)) ==
OAKENGINE_E_INVALID);
assert(oakengine_color_manager_set_config_filename(NULL, "x") ==
EXPECT_TRUE(oakengine_color_manager_set_config_filename(NULL, "x") ==
OAKENGINE_E_INVALID);
assert(oakengine_color_manager_colorspace_count(NULL) == 0);
assert(oakengine_color_manager_colorspace_at(NULL, 0, buf, sizeof(buf)) ==
EXPECT_TRUE(oakengine_color_manager_colorspace_count(NULL) == 0);
EXPECT_TRUE(oakengine_color_manager_colorspace_at(NULL, 0, buf, sizeof(buf)) ==
OAKENGINE_E_INVALID);
assert(oakengine_color_manager_display_count(NULL) == 0);
assert(oakengine_color_manager_display_at(NULL, 0, buf, sizeof(buf)) ==
EXPECT_TRUE(oakengine_color_manager_display_count(NULL) == 0);
EXPECT_TRUE(oakengine_color_manager_display_at(NULL, 0, buf, sizeof(buf)) ==
OAKENGINE_E_INVALID);
assert(oakengine_color_manager_view_count(NULL, NULL) == 0);
assert(oakengine_color_manager_view_at(NULL, NULL, 0, buf, sizeof(buf)) ==
EXPECT_TRUE(oakengine_color_manager_view_count(NULL, NULL) == 0);
EXPECT_TRUE(oakengine_color_manager_view_at(NULL, NULL, 0, buf, sizeof(buf)) ==
OAKENGINE_E_INVALID);
assert(oakengine_color_manager_look_count(NULL) == 0);
assert(oakengine_color_manager_look_at(NULL, 0, buf, sizeof(buf)) ==
EXPECT_TRUE(oakengine_color_manager_look_count(NULL) == 0);
EXPECT_TRUE(oakengine_color_manager_look_at(NULL, 0, buf, sizeof(buf)) ==
OAKENGINE_E_INVALID);
assert(oakengine_color_manager_default_display(NULL, buf, sizeof(buf)) ==
EXPECT_TRUE(oakengine_color_manager_default_display(NULL, buf, sizeof(buf)) ==
OAKENGINE_E_INVALID);
assert(oakengine_color_manager_default_view(NULL, NULL, buf,
EXPECT_TRUE(oakengine_color_manager_default_view(NULL, NULL, buf,
sizeof(buf)) ==
OAKENGINE_E_INVALID);
assert(oakengine_color_manager_default_input_color_space(NULL, buf,
EXPECT_TRUE(oakengine_color_manager_default_input_color_space(NULL, buf,
sizeof(buf)) ==
OAKENGINE_E_INVALID);
assert(oakengine_color_manager_set_default_input_color_space(NULL, "x") ==
EXPECT_TRUE(oakengine_color_manager_set_default_input_color_space(NULL, "x") ==
OAKENGINE_E_INVALID);
assert(oakengine_color_manager_reference_color_space(NULL, buf,
EXPECT_TRUE(oakengine_color_manager_reference_color_space(NULL, buf,
sizeof(buf)) ==
OAKENGINE_E_INVALID);
assert(oakengine_color_manager_default_luma_coefs(NULL, rgb) ==
EXPECT_TRUE(oakengine_color_manager_default_luma_coefs(NULL, rgb) ==
OAKENGINE_E_INVALID);
assert(oakengine_color_manager_compliant_color_space(NULL, "x", buf,
EXPECT_TRUE(oakengine_color_manager_compliant_color_space(NULL, "x", buf,
sizeof(buf)) ==
OAKENGINE_E_INVALID);
assert(oakengine_color_manager_compliant_transform(NULL, NULL, 0, NULL,
EXPECT_TRUE(oakengine_color_manager_compliant_transform(NULL, NULL, 0, NULL,
NULL, 0, NULL, 0, NULL,
0) == OAKENGINE_E_INVALID);
assert(oakengine_color_config_load_file(NULL) == NULL);
assert(oakengine_color_config_colorspace_count(NULL) == 0);
assert(oakengine_color_config_colorspace_at(NULL, 0, buf, sizeof(buf)) ==
EXPECT_TRUE(oakengine_color_config_load_file(NULL) == NULL);
EXPECT_TRUE(oakengine_color_config_colorspace_count(NULL) == 0);
EXPECT_TRUE(oakengine_color_config_colorspace_at(NULL, 0, buf, sizeof(buf)) ==
OAKENGINE_E_INVALID);
oakengine_color_config_free(NULL); // no-op
assert(oakengine_color_processor_create(NULL, "in", NULL,
EXPECT_TRUE(oakengine_color_processor_create(NULL, "in", NULL,
OAKENGINE_COLOR_PROCESSOR_NORMAL) ==
NULL);
assert(oakengine_color_processor_is_valid(NULL) == 0);
assert(oakengine_color_processor_convert_color(NULL, rgba, rgba) ==
EXPECT_TRUE(oakengine_color_processor_is_valid(NULL) == 0);
EXPECT_TRUE(oakengine_color_processor_convert_color(NULL, rgba, rgba) ==
OAKENGINE_E_INVALID);
assert(oakengine_color_processor_id(NULL, buf, sizeof(buf)) ==
EXPECT_TRUE(oakengine_color_processor_id(NULL, buf, sizeof(buf)) ==
OAKENGINE_E_INVALID);
oakengine_color_processor_free(NULL); // no-op
}
@@ -135,9 +136,9 @@ static void test_config_handle(int have_ocio)
char buf[256];
// A missing file must fail cleanly with an error message.
assert(oakengine_color_config_load_file("/nonexistent/definitely.ocio") ==
EXPECT_TRUE(oakengine_color_config_load_file("/nonexistent/definitely.ocio") ==
NULL);
assert(oakengine_color_last_error(buf, sizeof(buf)) > 0);
EXPECT_TRUE(oakengine_color_last_error(buf, sizeof(buf)) > 0);
OakEngineColorConfig *config = oakengine_color_config_load_default();
if (!have_ocio) {
@@ -147,19 +148,19 @@ static void test_config_handle(int have_ocio)
}
return;
}
assert(config != NULL);
EXPECT_TRUE(config != NULL);
const int count = oakengine_color_config_colorspace_count(config);
assert(count > 0);
EXPECT_TRUE(count > 0);
for (int i = 0; i < count; i++) {
assert(oakengine_color_config_colorspace_at(config, i, buf,
EXPECT_TRUE(oakengine_color_config_colorspace_at(config, i, buf,
sizeof(buf)) > 0);
assert(buf[0] != '\0');
EXPECT_TRUE(buf[0] != '\0');
}
assert(oakengine_color_config_colorspace_at(config, count, buf,
EXPECT_TRUE(oakengine_color_config_colorspace_at(config, count, buf,
sizeof(buf)) ==
OAKENGINE_E_INVALID);
assert(oakengine_color_config_colorspace_at(config, -1, buf,
EXPECT_TRUE(oakengine_color_config_colorspace_at(config, -1, buf,
sizeof(buf)) ==
OAKENGINE_E_INVALID);
@@ -176,69 +177,69 @@ static void test_manager_queries(OakEngineColorManager *mgr)
// Colorspaces
const int cs_count = oakengine_color_manager_colorspace_count(mgr);
assert(cs_count > 0);
assert(oakengine_color_manager_colorspace_at(mgr, 0, first_cs,
EXPECT_TRUE(cs_count > 0);
EXPECT_TRUE(oakengine_color_manager_colorspace_at(mgr, 0, first_cs,
sizeof(first_cs)) > 0);
assert(first_cs[0] != '\0');
assert(oakengine_color_manager_colorspace_at(mgr, cs_count, buf,
EXPECT_TRUE(first_cs[0] != '\0');
EXPECT_TRUE(oakengine_color_manager_colorspace_at(mgr, cs_count, buf,
sizeof(buf)) ==
OAKENGINE_E_INVALID);
// Displays / views / looks
const int disp_count = oakengine_color_manager_display_count(mgr);
assert(disp_count > 0);
assert(oakengine_color_manager_display_at(mgr, 0, buf, sizeof(buf)) > 0);
EXPECT_TRUE(disp_count > 0);
EXPECT_TRUE(oakengine_color_manager_display_at(mgr, 0, buf, sizeof(buf)) > 0);
char display[256];
memcpy(display, buf, sizeof(display));
const int view_count = oakengine_color_manager_view_count(mgr, display);
assert(view_count > 0);
assert(oakengine_color_manager_view_at(mgr, display, 0, buf, sizeof(buf)) >
EXPECT_TRUE(view_count > 0);
EXPECT_TRUE(oakengine_color_manager_view_at(mgr, display, 0, buf, sizeof(buf)) >
0);
assert(oakengine_color_manager_look_count(mgr) >= 0);
assert(oakengine_color_manager_display_at(mgr, disp_count, buf,
EXPECT_TRUE(oakengine_color_manager_look_count(mgr) >= 0);
EXPECT_TRUE(oakengine_color_manager_display_at(mgr, disp_count, buf,
sizeof(buf)) ==
OAKENGINE_E_INVALID);
// Defaults
char default_display[256];
assert(oakengine_color_manager_default_display(mgr, default_display,
EXPECT_TRUE(oakengine_color_manager_default_display(mgr, default_display,
sizeof(default_display)) > 0);
assert(oakengine_color_manager_default_view(mgr, default_display, buf,
EXPECT_TRUE(oakengine_color_manager_default_view(mgr, default_display, buf,
sizeof(buf)) > 0);
assert(oakengine_color_manager_default_input_color_space(mgr, buf,
EXPECT_TRUE(oakengine_color_manager_default_input_color_space(mgr, buf,
sizeof(buf)) > 0);
assert(oakengine_color_manager_reference_color_space(mgr, buf,
EXPECT_TRUE(oakengine_color_manager_reference_color_space(mgr, buf,
sizeof(buf)) > 0);
// Default input colorspace set/get roundtrip
assert(oakengine_color_manager_set_default_input_color_space(mgr,
EXPECT_TRUE(oakengine_color_manager_set_default_input_color_space(mgr,
first_cs) ==
OAKENGINE_OK);
assert(oakengine_color_manager_default_input_color_space(mgr, buf,
EXPECT_TRUE(oakengine_color_manager_default_input_color_space(mgr, buf,
sizeof(buf)) > 0);
assert(strcmp(buf, first_cs) == 0);
EXPECT_TRUE(strcmp(buf, first_cs) == 0);
// Config filename set/get roundtrip (an empty filename selects the
// built-in default config; setting it must not crash the queries above)
assert(oakengine_color_manager_set_config_filename(mgr, "") ==
EXPECT_TRUE(oakengine_color_manager_set_config_filename(mgr, "") ==
OAKENGINE_OK);
assert(oakengine_color_manager_get_config_filename(mgr, buf, sizeof(buf)) >=
EXPECT_TRUE(oakengine_color_manager_get_config_filename(mgr, buf, sizeof(buf)) >=
0);
// Luma coefficients: Rec.709-style weights, all positive, roughly sum to 1
assert(oakengine_color_manager_default_luma_coefs(mgr, rgb) ==
EXPECT_TRUE(oakengine_color_manager_default_luma_coefs(mgr, rgb) ==
OAKENGINE_OK);
assert(rgb[0] > 0 && rgb[1] > 0 && rgb[2] > 0);
assert(fabs(rgb[0] + rgb[1] + rgb[2] - 1.0) < 0.01);
EXPECT_TRUE(rgb[0] > 0 && rgb[1] > 0 && rgb[2] > 0);
EXPECT_TRUE(fabs(rgb[0] + rgb[1] + rgb[2] - 1.0) < 0.01);
// Compliant colorspace: an existing space resolves to itself, an empty
// name resolves to the default input space
assert(oakengine_color_manager_compliant_color_space(mgr, first_cs, buf,
EXPECT_TRUE(oakengine_color_manager_compliant_color_space(mgr, first_cs, buf,
sizeof(buf)) > 0);
assert(strcmp(buf, first_cs) == 0);
assert(oakengine_color_manager_compliant_color_space(mgr, "", buf,
EXPECT_TRUE(strcmp(buf, first_cs) == 0);
EXPECT_TRUE(oakengine_color_manager_compliant_color_space(mgr, "", buf,
sizeof(buf)) > 0);
assert(strcmp(buf, first_cs) == 0);
EXPECT_TRUE(strcmp(buf, first_cs) == 0);
// Compliant transform: force a colorspace transform onto a display
// transform and back
@@ -250,19 +251,19 @@ static void test_manager_queries(OakEngineColorManager *mgr)
int out_is_display = -1;
char out_o[256], out_v[256], out_l[256];
out_o[0] = out_v[0] = out_l[0] = '\0';
assert(oakengine_color_manager_compliant_transform(mgr, &in, 1,
EXPECT_TRUE(oakengine_color_manager_compliant_transform(mgr, &in, 1,
&out_is_display, out_o,
sizeof(out_o), out_v,
sizeof(out_v), out_l,
sizeof(out_l)) ==
OAKENGINE_OK);
assert(out_is_display == 1);
assert(out_o[0] != '\0');
assert(oakengine_color_manager_compliant_transform(
EXPECT_TRUE(out_is_display == 1);
EXPECT_TRUE(out_o[0] != '\0');
EXPECT_TRUE(oakengine_color_manager_compliant_transform(
mgr, &in, 0, &out_is_display, out_o, sizeof(out_o), out_v,
sizeof(out_v), out_l, sizeof(out_l)) == OAKENGINE_OK);
assert(out_is_display == 0);
assert(strcmp(out_o, first_cs) == 0);
EXPECT_TRUE(out_is_display == 0);
EXPECT_TRUE(strcmp(out_o, first_cs) == 0);
}
// ---- Color processor ----------------------------------------------------------
@@ -272,7 +273,7 @@ static void test_processor(OakEngineColorManager *mgr)
char ref[256];
char buf[256];
assert(oakengine_color_manager_reference_color_space(mgr, ref,
EXPECT_TRUE(oakengine_color_manager_reference_color_space(mgr, ref,
sizeof(ref)) > 0);
// Identity transform (ref -> ref): white stays white
@@ -284,53 +285,53 @@ static void test_processor(OakEngineColorManager *mgr)
OakEngineColorProcessor *proc = oakengine_color_processor_create(
mgr, ref, &dest, OAKENGINE_COLOR_PROCESSOR_NORMAL);
assert(proc != NULL);
assert(oakengine_color_processor_is_valid(proc) == 1);
EXPECT_TRUE(proc != NULL);
EXPECT_TRUE(oakengine_color_processor_is_valid(proc) == 1);
const double in[4] = { 1.0, 1.0, 1.0, 1.0 };
double out[4] = { 0, 0, 0, 0 };
assert(oakengine_color_processor_convert_color(proc, in, out) ==
EXPECT_TRUE(oakengine_color_processor_convert_color(proc, in, out) ==
OAKENGINE_OK);
assert(fabs(out[0] - 1.0) < 1e-3 && fabs(out[1] - 1.0) < 1e-3 &&
EXPECT_TRUE(fabs(out[0] - 1.0) < 1e-3 && fabs(out[1] - 1.0) < 1e-3 &&
fabs(out[2] - 1.0) < 1e-3 && fabs(out[3] - 1.0) < 1e-3);
// Cache id is non-empty and stable
const int id_len = oakengine_color_processor_id(proc, buf, sizeof(buf));
assert(id_len > 0);
assert(buf[0] != '\0');
assert(oakengine_color_processor_id(proc, NULL, 0) == id_len);
EXPECT_TRUE(id_len > 0);
EXPECT_TRUE(buf[0] != '\0');
EXPECT_TRUE(oakengine_color_processor_id(proc, NULL, 0) == id_len);
// Inverse direction constructs too
OakEngineColorProcessor *inv = oakengine_color_processor_create(
mgr, ref, &dest, OAKENGINE_COLOR_PROCESSOR_INVERSE);
assert(inv != NULL);
EXPECT_TRUE(inv != NULL);
oakengine_color_processor_free(inv);
// Unknown direction is rejected
assert(oakengine_color_processor_create(mgr, ref, &dest, 7) == NULL);
EXPECT_TRUE(oakengine_color_processor_create(mgr, ref, &dest, 7) == NULL);
// An unknown colorspace yields an invalid (pass-through) processor,
// matching the engine's non-throwing C++ behavior
dest.output = "definitely-not-a-colorspace";
OakEngineColorProcessor *bad = oakengine_color_processor_create(
mgr, ref, &dest, OAKENGINE_COLOR_PROCESSOR_NORMAL);
assert(bad != NULL);
EXPECT_TRUE(bad != NULL);
if (oakengine_color_processor_is_valid(bad)) {
// Some configs resolve unknown names via roles; then conversion must
// still not crash.
assert(oakengine_color_processor_convert_color(bad, in, out) ==
EXPECT_TRUE(oakengine_color_processor_convert_color(bad, in, out) ==
OAKENGINE_OK);
} else {
out[0] = out[1] = out[2] = out[3] = 0;
assert(oakengine_color_processor_convert_color(bad, in, out) ==
EXPECT_TRUE(oakengine_color_processor_convert_color(bad, in, out) ==
OAKENGINE_OK);
assert(out[0] == 1.0 && out[1] == 1.0 && out[2] == 1.0 &&
EXPECT_TRUE(out[0] == 1.0 && out[1] == 1.0 && out[2] == 1.0 &&
out[3] == 1.0);
}
oakengine_color_processor_free(bad);
// Processor is valid and usable.
assert(proc != NULL);
EXPECT_TRUE(proc != NULL);
oakengine_color_processor_free(proc);
}
@@ -343,13 +344,13 @@ static int g_reference_events = 0;
static void count_color_events(const oakengine_event *event, void *userdata)
{
(void)userdata;
assert(event != NULL);
EXPECT_TRUE(event != NULL);
if (event->id == OAKENGINE_EVENT_COLOR_MANAGER_CONFIG_CHANGED) {
g_config_events++;
} else if (event->id == OAKENGINE_EVENT_COLOR_MANAGER_REFERENCE_SPACE_CHANGED) {
g_reference_events++;
} else {
assert(0); // unexpected event id on this subscription
EXPECT_TRUE(0); // unexpected event id on this subscription
}
}
@@ -357,7 +358,7 @@ static void test_events(OakEngineProject *project,
OakEngineColorManager *mgr)
{
// Family mismatch: a color manager event on a project handle must fail.
assert(oakengine_event_subscribe(
EXPECT_TRUE(oakengine_event_subscribe(
project, OAKENGINE_EVENT_COLOR_MANAGER_CONFIG_CHANGED,
count_color_events, NULL) == 0);
@@ -367,46 +368,46 @@ static void test_events(OakEngineProject *project,
int64_t sub_cfg = oakengine_event_subscribe(
mgr, OAKENGINE_EVENT_COLOR_MANAGER_CONFIG_CHANGED,
count_color_events, NULL);
assert(sub_ref > 0);
assert(sub_cfg > 0);
EXPECT_TRUE(sub_ref > 0);
EXPECT_TRUE(sub_cfg > 0);
// Changing the reference space emits reference_space_changed.
char ref[256];
assert(oakengine_project_get_color_reference_space(project, ref,
EXPECT_TRUE(oakengine_project_get_color_reference_space(project, ref,
sizeof(ref)) > 0);
assert(oakengine_project_set_color_reference_space(
EXPECT_TRUE(oakengine_project_set_color_reference_space(
project, strcmp(ref, "scene_linear") == 0 ? "reference" :
"scene_linear") ==
OAKENGINE_OK);
assert(g_reference_events == 1);
assert(g_config_events == 0);
EXPECT_TRUE(g_reference_events == 1);
EXPECT_TRUE(g_config_events == 0);
assert(oakengine_event_unsubscribe(sub_ref) == OAKENGINE_OK);
assert(oakengine_event_unsubscribe(sub_cfg) == OAKENGINE_OK);
EXPECT_TRUE(oakengine_event_unsubscribe(sub_ref) == OAKENGINE_OK);
EXPECT_TRUE(oakengine_event_unsubscribe(sub_cfg) == OAKENGINE_OK);
}
int main(void)
TEST(OakEngineColor, Main)
{
make_tmpdir();
// Sandbox the config/cache/data locations (the default OCIO config is
// extracted under the cache location).
#if !defined(_WIN32)
assert(setenv("XDG_CONFIG_HOME", g_tmpdir, 1) == 0);
assert(setenv("XDG_CACHE_HOME", g_tmpdir, 1) == 0);
assert(setenv("XDG_DATA_HOME", g_tmpdir, 1) == 0);
EXPECT_TRUE(setenv("XDG_CONFIG_HOME", g_tmpdir, 1) == 0);
EXPECT_TRUE(setenv("XDG_CACHE_HOME", g_tmpdir, 1) == 0);
EXPECT_TRUE(setenv("XDG_DATA_HOME", g_tmpdir, 1) == 0);
#endif
assert(oakengine_init(OAKENGINE_INIT_HEADLESS) == OAKENGINE_OK);
EXPECT_TRUE(oakengine_init(OAKENGINE_INIT_HEADLESS) == OAKENGINE_OK);
test_null_robustness();
OakEngineProject *project = oakengine_project_create();
assert(project != NULL);
assert(oakengine_project_new(project) == OAKENGINE_OK);
EXPECT_TRUE(project != NULL);
EXPECT_TRUE(oakengine_project_new(project) == OAKENGINE_OK);
OakEngineColorManager *mgr = oakengine_color_manager_from_project(project);
assert(mgr != NULL);
EXPECT_TRUE(mgr != NULL);
// The built-in default config should always be available (it is
// extracted from the engine's resources); tolerate environments where
@@ -425,8 +426,6 @@ int main(void)
test_events(project, mgr);
oakengine_project_free(project);
assert(oakengine_shutdown() == OAKENGINE_OK);
EXPECT_TRUE(oakengine_shutdown() == OAKENGINE_OK);
printf("oakengine_color_test: all assertions passed\n");
return 0;
}
+28 -28
View File
@@ -22,6 +22,7 @@
// (oakengine/config.h). Runs headless; no GPU required.
#include <assert.h>
#include <gtest/gtest.h>
#include <stdio.h>
#include <string.h>
@@ -47,72 +48,71 @@ static void test_string_round_trip(void)
char buf[256];
// Missing key returns 0 (empty string).
assert(oakengine_config_get_string("oak_test_string_key", buf,
EXPECT_TRUE(oakengine_config_get_string("oak_test_string_key", buf,
sizeof(buf)) == 0);
assert(oakengine_config_set_string("oak_test_string_key",
EXPECT_TRUE(oakengine_config_set_string("oak_test_string_key",
"hello world") == OAKENGINE_OK);
int len = oakengine_config_get_string("oak_test_string_key", buf,
sizeof(buf));
assert(len == int(strlen("hello world")));
assert(strcmp(buf, "hello world") == 0);
EXPECT_TRUE(len == int(strlen("hello world")));
EXPECT_TRUE(strcmp(buf, "hello world") == 0);
// Query length with NULL buffer.
assert(oakengine_config_get_string("oak_test_string_key", NULL, 0) == len);
EXPECT_TRUE(oakengine_config_get_string("oak_test_string_key", NULL, 0) == len);
// NULL key is rejected.
assert(oakengine_config_get_string(NULL, buf, sizeof(buf)) ==
EXPECT_TRUE(oakengine_config_get_string(NULL, buf, sizeof(buf)) ==
OAKENGINE_E_INVALID);
assert(oakengine_config_set_string(NULL, "x") == OAKENGINE_E_INVALID);
EXPECT_TRUE(oakengine_config_set_string(NULL, "x") == OAKENGINE_E_INVALID);
}
static void test_int_round_trip(void)
{
assert(oakengine_config_get_int("oak_test_int_key", 42) == 42);
EXPECT_TRUE(oakengine_config_get_int("oak_test_int_key", 42) == 42);
assert(oakengine_config_set_int("oak_test_int_key", 12345) ==
EXPECT_TRUE(oakengine_config_set_int("oak_test_int_key", 12345) ==
OAKENGINE_OK);
assert(oakengine_config_get_int("oak_test_int_key", 0) == 12345);
EXPECT_TRUE(oakengine_config_get_int("oak_test_int_key", 0) == 12345);
assert(oakengine_config_set_int("oak_test_int_key", -7) ==
EXPECT_TRUE(oakengine_config_set_int("oak_test_int_key", -7) ==
OAKENGINE_OK);
assert(oakengine_config_get_int("oak_test_int_key", 0) == -7);
EXPECT_TRUE(oakengine_config_get_int("oak_test_int_key", 0) == -7);
// NULL key returns default.
assert(oakengine_config_get_int(NULL, 99) == 99);
assert(oakengine_config_set_int(NULL, 1) == OAKENGINE_E_INVALID);
EXPECT_TRUE(oakengine_config_get_int(NULL, 99) == 99);
EXPECT_TRUE(oakengine_config_set_int(NULL, 1) == OAKENGINE_E_INVALID);
}
static void test_error_handler(void)
{
g_error_calls = 0;
assert(oakengine_config_set_error_handler(error_cb, NULL) ==
EXPECT_TRUE(oakengine_config_set_error_handler(error_cb, NULL) ==
OAKENGINE_OK);
assert(oakengine_config_report_error("Test Title",
EXPECT_TRUE(oakengine_config_report_error("Test Title",
"Test Message") == OAKENGINE_OK);
assert(g_error_calls == 1);
assert(strcmp(g_last_title, "Test Title") == 0);
assert(strcmp(g_last_message, "Test Message") == 0);
EXPECT_TRUE(g_error_calls == 1);
EXPECT_TRUE(strcmp(g_last_title, "Test Title") == 0);
EXPECT_TRUE(strcmp(g_last_message, "Test Message") == 0);
// Clearing the handler does not crash.
assert(oakengine_config_set_error_handler(NULL, NULL) == OAKENGINE_OK);
assert(oakengine_config_report_error("Ignored", "Ignored") ==
EXPECT_TRUE(oakengine_config_set_error_handler(NULL, NULL) == OAKENGINE_OK);
EXPECT_TRUE(oakengine_config_report_error("Ignored", "Ignored") ==
OAKENGINE_OK);
assert(g_error_calls == 1);
EXPECT_TRUE(g_error_calls == 1);
}
int main(void)
TEST(OakEngineConfig, Main)
{
assert(oakengine_init(OAKENGINE_INIT_HEADLESS) == OAKENGINE_OK);
EXPECT_TRUE(oakengine_init(OAKENGINE_INIT_HEADLESS) == OAKENGINE_OK);
test_string_round_trip();
test_int_round_trip();
test_error_handler();
assert(oakengine_config_save() == OAKENGINE_OK);
assert(oakengine_config_load() == OAKENGINE_OK);
EXPECT_TRUE(oakengine_config_save() == OAKENGINE_OK);
EXPECT_TRUE(oakengine_config_load() == OAKENGINE_OK);
assert(oakengine_shutdown() == OAKENGINE_OK);
return 0;
EXPECT_TRUE(oakengine_shutdown() == OAKENGINE_OK);
}
+60 -60
View File
@@ -24,6 +24,7 @@
// handle lookup and default path mutation. Runs headless; no GPU required.
#include <assert.h>
#include <gtest/gtest.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -53,8 +54,8 @@ static void settings_handler(const char *folder_path, void *parent_window,
{
(void) parent_window;
(void) userdata;
assert(folder_path != NULL);
assert(strlen(folder_path) > 0);
EXPECT_TRUE(folder_path != NULL);
EXPECT_TRUE(strlen(folder_path) > 0);
strncpy(g_handler_path, folder_path, sizeof(g_handler_path) - 1);
g_handler_path[sizeof(g_handler_path) - 1] = '\0';
g_handler_call_count++;
@@ -65,21 +66,21 @@ static void test_instance_lifecycle(void)
char buf[512];
// DiskManager is created by oakengine_init(HEADLESS).
assert(oakengine_disk_get_default_cache_path(buf, sizeof(buf)) > 0);
EXPECT_TRUE(oakengine_disk_get_default_cache_path(buf, sizeof(buf)) > 0);
// Destroy is allowed and idempotent.
assert(oakengine_disk_destroy_instance() == OAKENGINE_OK);
assert(oakengine_disk_get_default_cache_path(buf, sizeof(buf)) ==
EXPECT_TRUE(oakengine_disk_destroy_instance() == OAKENGINE_OK);
EXPECT_TRUE(oakengine_disk_get_default_cache_path(buf, sizeof(buf)) ==
OAKENGINE_E_STATE);
assert(oakengine_disk_destroy_instance() == OAKENGINE_OK);
EXPECT_TRUE(oakengine_disk_destroy_instance() == OAKENGINE_OK);
// Create recreates the instance.
assert(oakengine_disk_create_instance() == OAKENGINE_OK);
assert(oakengine_disk_get_default_cache_path(buf, sizeof(buf)) > 0);
EXPECT_TRUE(oakengine_disk_create_instance() == OAKENGINE_OK);
EXPECT_TRUE(oakengine_disk_get_default_cache_path(buf, sizeof(buf)) > 0);
// Create is idempotent.
assert(oakengine_disk_create_instance() == OAKENGINE_OK);
assert(oakengine_disk_get_default_cache_path(buf, sizeof(buf)) > 0);
EXPECT_TRUE(oakengine_disk_create_instance() == OAKENGINE_OK);
EXPECT_TRUE(oakengine_disk_get_default_cache_path(buf, sizeof(buf)) > 0);
}
static void test_default_cache_path(void)
@@ -88,28 +89,28 @@ static void test_default_cache_path(void)
memset(buf, 0, sizeof(buf));
const int len = oakengine_disk_get_default_cache_path(buf, sizeof(buf));
assert(len > 0);
assert((int) strlen(buf) == len);
assert(strchr(buf, '/') != NULL || strchr(buf, '\\') != NULL);
EXPECT_TRUE(len > 0);
EXPECT_TRUE((int) strlen(buf) == len);
EXPECT_TRUE(strchr(buf, '/') != NULL || strchr(buf, '\\') != NULL);
// Query length with NULL buffer.
assert(oakengine_disk_get_default_cache_path(NULL, 0) == len);
EXPECT_TRUE(oakengine_disk_get_default_cache_path(NULL, 0) == len);
}
static void test_open_folder_handle(void)
{
char buf[512];
assert(oakengine_disk_get_default_cache_path(buf, sizeof(buf)) > 0);
EXPECT_TRUE(oakengine_disk_get_default_cache_path(buf, sizeof(buf)) > 0);
void *folder = oakengine_disk_get_open_folder(buf);
assert(folder != NULL);
EXPECT_TRUE(folder != NULL);
// NULL/empty path returns the default folder handle.
void *default_folder = oakengine_disk_get_open_folder(nullptr);
assert(default_folder == folder);
EXPECT_TRUE(default_folder == folder);
void *empty_folder = oakengine_disk_get_open_folder("");
assert(empty_folder == folder);
EXPECT_TRUE(empty_folder == folder);
// A different path opens a distinct folder.
char tmp[256];
@@ -123,20 +124,20 @@ static void test_open_folder_handle(void)
#if defined(_WIN32)
char *tmpdir = _mktemp(tmp);
assert(tmpdir != NULL);
assert(_mkdir(tmpdir) == 0);
EXPECT_TRUE(tmpdir != NULL);
EXPECT_TRUE(_mkdir(tmpdir) == 0);
#else
char *tmpdir = mkdtemp(tmp);
assert(tmpdir != NULL);
EXPECT_TRUE(tmpdir != NULL);
#endif
void *other_folder = oakengine_disk_get_open_folder(tmpdir);
assert(other_folder != NULL);
assert(other_folder != folder);
EXPECT_TRUE(other_folder != NULL);
EXPECT_TRUE(other_folder != folder);
// The same path returns the same handle.
void *other_folder_again = oakengine_disk_get_open_folder(tmpdir);
assert(other_folder_again == other_folder);
EXPECT_TRUE(other_folder_again == other_folder);
#if defined(_WIN32)
_rmdir(tmpdir);
@@ -159,11 +160,11 @@ static void test_clear_cache(void)
#if defined(_WIN32)
char *tmpdir = _mktemp(path);
assert(tmpdir != NULL);
assert(_mkdir(tmpdir) == 0);
EXPECT_TRUE(tmpdir != NULL);
EXPECT_TRUE(_mkdir(tmpdir) == 0);
#else
char *tmpdir = mkdtemp(path);
assert(tmpdir != NULL);
EXPECT_TRUE(tmpdir != NULL);
#endif
char index_file[512];
@@ -175,17 +176,17 @@ static void test_clear_cache(void)
#endif
FILE *f = fopen(index_file, "w");
assert(f != NULL);
EXPECT_TRUE(f != NULL);
fclose(f);
// clear_cache opens the folder and clears its contents.
assert(oakengine_disk_clear_cache(tmpdir) == 1);
EXPECT_TRUE(oakengine_disk_clear_cache(tmpdir) == 1);
// Re-create a file and clear again to ensure idempotency.
f = fopen(index_file, "w");
assert(f != NULL);
EXPECT_TRUE(f != NULL);
fclose(f);
assert(oakengine_disk_clear_cache(tmpdir) == 1);
EXPECT_TRUE(oakengine_disk_clear_cache(tmpdir) == 1);
#if defined(_WIN32)
_rmdir(tmpdir);
@@ -198,49 +199,49 @@ static void test_settings_handler_round_trip(void)
{
reset_handler_state();
assert(oakengine_disk_set_settings_handler(settings_handler, NULL) ==
EXPECT_TRUE(oakengine_disk_set_settings_handler(settings_handler, NULL) ==
OAKENGINE_OK);
// NULL path uses the default folder.
assert(oakengine_disk_show_settings_dialog(NULL, NULL) == OAKENGINE_OK);
assert(g_handler_call_count == 1);
assert(strlen(g_handler_path) > 0);
EXPECT_TRUE(oakengine_disk_show_settings_dialog(NULL, NULL) == OAKENGINE_OK);
EXPECT_TRUE(g_handler_call_count == 1);
EXPECT_TRUE(strlen(g_handler_path) > 0);
// Calling again with a specific path invokes the handler with that path.
assert(oakengine_disk_show_settings_dialog(g_handler_path, NULL) ==
EXPECT_TRUE(oakengine_disk_show_settings_dialog(g_handler_path, NULL) ==
OAKENGINE_OK);
assert(g_handler_call_count == 2);
assert(strcmp(g_handler_path, g_handler_path) == 0);
EXPECT_TRUE(g_handler_call_count == 2);
EXPECT_TRUE(strcmp(g_handler_path, g_handler_path) == 0);
// Clearing the handler is allowed and results in a logged skip.
assert(oakengine_disk_set_settings_handler(NULL, NULL) == OAKENGINE_OK);
assert(oakengine_disk_show_settings_dialog(g_handler_path, NULL) ==
EXPECT_TRUE(oakengine_disk_set_settings_handler(NULL, NULL) == OAKENGINE_OK);
EXPECT_TRUE(oakengine_disk_show_settings_dialog(g_handler_path, NULL) ==
OAKENGINE_OK);
assert(g_handler_call_count == 2);
EXPECT_TRUE(g_handler_call_count == 2);
}
static void test_invalidate_project(void)
{
// No instance returns an error.
assert(oakengine_disk_destroy_instance() == OAKENGINE_OK);
assert(oakengine_disk_invalidate_project(NULL) == OAKENGINE_E_STATE);
EXPECT_TRUE(oakengine_disk_destroy_instance() == OAKENGINE_OK);
EXPECT_TRUE(oakengine_disk_invalidate_project(NULL) == OAKENGINE_E_STATE);
assert(oakengine_disk_create_instance() == OAKENGINE_OK);
EXPECT_TRUE(oakengine_disk_create_instance() == OAKENGINE_OK);
// NULL project is accepted (signal emitted with null pointer).
assert(oakengine_disk_invalidate_project(NULL) == OAKENGINE_OK);
EXPECT_TRUE(oakengine_disk_invalidate_project(NULL) == OAKENGINE_OK);
// Valid project returns OK without crashing.
OakEngineProject *project = oakengine_project_create();
assert(project != NULL);
assert(oakengine_disk_invalidate_project(project) == OAKENGINE_OK);
EXPECT_TRUE(project != NULL);
EXPECT_TRUE(oakengine_disk_invalidate_project(project) == OAKENGINE_OK);
oakengine_project_free(project);
}
static void test_set_default_cache_path(void)
{
char original[512];
assert(oakengine_disk_get_default_cache_path(original, sizeof(original)) >
EXPECT_TRUE(oakengine_disk_get_default_cache_path(original, sizeof(original)) >
0);
char tmp[256];
@@ -254,23 +255,23 @@ static void test_set_default_cache_path(void)
#if defined(_WIN32)
char *tmpdir = _mktemp(tmp);
assert(tmpdir != NULL);
assert(_mkdir(tmpdir) == 0);
EXPECT_TRUE(tmpdir != NULL);
EXPECT_TRUE(_mkdir(tmpdir) == 0);
#else
char *tmpdir = mkdtemp(tmp);
assert(tmpdir != NULL);
EXPECT_TRUE(tmpdir != NULL);
#endif
assert(oakengine_disk_set_default_cache_path(tmpdir) == OAKENGINE_OK);
EXPECT_TRUE(oakengine_disk_set_default_cache_path(tmpdir) == OAKENGINE_OK);
char updated[512];
assert(oakengine_disk_get_default_cache_path(updated, sizeof(updated)) > 0);
assert(strcmp(updated, tmpdir) == 0);
EXPECT_TRUE(oakengine_disk_get_default_cache_path(updated, sizeof(updated)) > 0);
EXPECT_TRUE(strcmp(updated, tmpdir) == 0);
// Restore original default path.
assert(oakengine_disk_set_default_cache_path(original) == OAKENGINE_OK);
assert(oakengine_disk_get_default_cache_path(updated, sizeof(updated)) > 0);
assert(strcmp(updated, original) == 0);
EXPECT_TRUE(oakengine_disk_set_default_cache_path(original) == OAKENGINE_OK);
EXPECT_TRUE(oakengine_disk_get_default_cache_path(updated, sizeof(updated)) > 0);
EXPECT_TRUE(strcmp(updated, original) == 0);
#if defined(_WIN32)
_rmdir(tmpdir);
@@ -279,9 +280,9 @@ static void test_set_default_cache_path(void)
#endif
}
int main(void)
TEST(OakEngineDisk, Main)
{
assert(oakengine_init(OAKENGINE_INIT_HEADLESS) == OAKENGINE_OK);
EXPECT_TRUE(oakengine_init(OAKENGINE_INIT_HEADLESS) == OAKENGINE_OK);
test_instance_lifecycle();
test_default_cache_path();
@@ -297,5 +298,4 @@ int main(void)
oakengine_shutdown();
printf("oakengine_disk_test: OK\n");
return 0;
}
+197 -197
View File
@@ -28,6 +28,7 @@
// render_with_params are touched (no sequence).
#include <assert.h>
#include <gtest/gtest.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
@@ -51,13 +52,13 @@ static void make_tmpdir(void)
#if defined(_WIN32)
char base[MAX_PATH];
const DWORD len = GetTempPathA(MAX_PATH, base);
assert(len > 0 && len < MAX_PATH);
EXPECT_TRUE(len > 0 && len < MAX_PATH);
snprintf(g_tmpdir, sizeof(g_tmpdir), "%soakengine_encoding_test_%lu",
base, (unsigned long)GetCurrentProcessId());
assert(_mkdir(g_tmpdir) == 0);
EXPECT_TRUE(_mkdir(g_tmpdir) == 0);
#else
strcpy(g_tmpdir, "/tmp/oakengine_encoding_test_XXXXXX");
assert(mkdtemp(g_tmpdir) != NULL);
EXPECT_TRUE(mkdtemp(g_tmpdir) != NULL);
#endif
}
@@ -65,25 +66,25 @@ static void test_format_metadata(void)
{
char buf[256];
assert(oakengine_encoding_format_count() > 0);
EXPECT_TRUE(oakengine_encoding_format_count() > 0);
// Matroska
assert(oakengine_encoding_format_name(OAKENGINE_ENCODING_FORMAT_MATROSKA,
EXPECT_TRUE(oakengine_encoding_format_name(OAKENGINE_ENCODING_FORMAT_MATROSKA,
buf, sizeof(buf)) > 0);
assert(strstr(buf, "Matroska") != NULL);
assert(oakengine_encoding_format_extension(
EXPECT_TRUE(strstr(buf, "Matroska") != NULL);
EXPECT_TRUE(oakengine_encoding_format_extension(
OAKENGINE_ENCODING_FORMAT_MATROSKA, buf, sizeof(buf)) > 0);
assert(strcmp(buf, "mkv") == 0);
EXPECT_TRUE(strcmp(buf, "mkv") == 0);
// Invalid format
assert(oakengine_encoding_format_name(-1, buf, sizeof(buf)) == -1);
assert(oakengine_encoding_format_extension(9999, buf, sizeof(buf)) == -1);
assert(oakengine_encoding_format_video_codec_count(-1) == -1);
EXPECT_TRUE(oakengine_encoding_format_name(-1, buf, sizeof(buf)) == -1);
EXPECT_TRUE(oakengine_encoding_format_extension(9999, buf, sizeof(buf)) == -1);
EXPECT_TRUE(oakengine_encoding_format_video_codec_count(-1) == -1);
// MP4 carries H.264 video and AAC audio
const int vcount =
oakengine_encoding_format_video_codec_count(OAKENGINE_ENCODING_FORMAT_MPEG4_VIDEO);
assert(vcount > 0);
EXPECT_TRUE(vcount > 0);
int found_h264 = 0;
for (int i = 0; i < vcount; i++) {
if (oakengine_encoding_format_video_codec_at(
@@ -92,16 +93,16 @@ static void test_format_metadata(void)
found_h264 = 1;
}
}
assert(found_h264);
assert(oakengine_encoding_format_video_codec_at(
EXPECT_TRUE(found_h264);
EXPECT_TRUE(oakengine_encoding_format_video_codec_at(
OAKENGINE_ENCODING_FORMAT_MPEG4_VIDEO, vcount) == -1);
// WAV is audio-only and carries PCM
assert(oakengine_encoding_format_video_codec_count(
EXPECT_TRUE(oakengine_encoding_format_video_codec_count(
OAKENGINE_ENCODING_FORMAT_WAV) == 0);
const int acount = oakengine_encoding_format_audio_codec_count(
OAKENGINE_ENCODING_FORMAT_WAV);
assert(acount > 0);
EXPECT_TRUE(acount > 0);
int found_pcm = 0;
for (int i = 0; i < acount; i++) {
if (oakengine_encoding_format_audio_codec_at(
@@ -109,16 +110,16 @@ static void test_format_metadata(void)
found_pcm = 1;
}
}
assert(found_pcm);
EXPECT_TRUE(found_pcm);
// SRT is subtitles-only
assert(oakengine_encoding_format_subtitle_codec_count(
EXPECT_TRUE(oakengine_encoding_format_subtitle_codec_count(
OAKENGINE_ENCODING_FORMAT_SRT) > 0);
assert(oakengine_encoding_format_subtitle_codec_at(
EXPECT_TRUE(oakengine_encoding_format_subtitle_codec_at(
OAKENGINE_ENCODING_FORMAT_SRT, 0) >= 0);
assert(oakengine_encoding_format_subtitle_codec_at(
EXPECT_TRUE(oakengine_encoding_format_subtitle_codec_at(
OAKENGINE_ENCODING_FORMAT_SRT, -1) < 0);
assert(oakengine_encoding_format_audio_codec_count(
EXPECT_TRUE(oakengine_encoding_format_audio_codec_count(
OAKENGINE_ENCODING_FORMAT_SRT) == 0);
}
@@ -126,47 +127,47 @@ static void test_codec_metadata(void)
{
char buf[256];
assert(oakengine_encoding_codec_name(OAKENGINE_ENCODING_CODEC_H264, buf,
EXPECT_TRUE(oakengine_encoding_codec_name(OAKENGINE_ENCODING_CODEC_H264, buf,
sizeof(buf)) > 0);
assert(buf[0] != '\0');
assert(oakengine_encoding_codec_name(-1, buf, sizeof(buf)) == -1);
EXPECT_TRUE(buf[0] != '\0');
EXPECT_TRUE(oakengine_encoding_codec_name(-1, buf, sizeof(buf)) == -1);
assert(oakengine_encoding_codec_is_still_image(5 /* PNG */) == 1);
assert(oakengine_encoding_codec_is_still_image(
EXPECT_TRUE(oakengine_encoding_codec_is_still_image(5 /* PNG */) == 1);
EXPECT_TRUE(oakengine_encoding_codec_is_still_image(
OAKENGINE_ENCODING_CODEC_H264) == 0);
assert(oakengine_encoding_codec_is_lossless(OAKENGINE_ENCODING_CODEC_PCM) ==
EXPECT_TRUE(oakengine_encoding_codec_is_lossless(OAKENGINE_ENCODING_CODEC_PCM) ==
1);
assert(oakengine_encoding_codec_is_lossless(OAKENGINE_ENCODING_CODEC_AAC) ==
EXPECT_TRUE(oakengine_encoding_codec_is_lossless(OAKENGINE_ENCODING_CODEC_AAC) ==
0);
// Encoded pixel formats of H.264 in MP4: yuv420p is the preferred one
const int pcount = oakengine_encoding_pix_fmt_count(
OAKENGINE_ENCODING_FORMAT_MPEG4_VIDEO, OAKENGINE_ENCODING_CODEC_H264);
assert(pcount > 0);
assert(oakengine_encoding_pix_fmt_at(OAKENGINE_ENCODING_FORMAT_MPEG4_VIDEO,
EXPECT_TRUE(pcount > 0);
EXPECT_TRUE(oakengine_encoding_pix_fmt_at(OAKENGINE_ENCODING_FORMAT_MPEG4_VIDEO,
OAKENGINE_ENCODING_CODEC_H264, 0, buf,
sizeof(buf)) > 0);
assert(strcmp(buf, "yuv420p") == 0);
assert(oakengine_encoding_pix_fmt_at(OAKENGINE_ENCODING_FORMAT_MPEG4_VIDEO,
EXPECT_TRUE(strcmp(buf, "yuv420p") == 0);
EXPECT_TRUE(oakengine_encoding_pix_fmt_at(OAKENGINE_ENCODING_FORMAT_MPEG4_VIDEO,
OAKENGINE_ENCODING_CODEC_H264, pcount,
buf, sizeof(buf)) == -1);
assert(oakengine_encoding_pix_fmt_index(OAKENGINE_ENCODING_CODEC_H264,
EXPECT_TRUE(oakengine_encoding_pix_fmt_index(OAKENGINE_ENCODING_CODEC_H264,
"yuv420p") == 0);
assert(oakengine_encoding_pix_fmt_index(OAKENGINE_ENCODING_CODEC_H264,
EXPECT_TRUE(oakengine_encoding_pix_fmt_index(OAKENGINE_ENCODING_CODEC_H264,
"no-such-format") == 0);
assert(oakengine_encoding_pix_fmt_index(OAKENGINE_ENCODING_CODEC_H264,
EXPECT_TRUE(oakengine_encoding_pix_fmt_index(OAKENGINE_ENCODING_CODEC_H264,
NULL) == 0);
// Sample formats of PCM in WAV
const int scount = oakengine_encoding_sample_format_count(
OAKENGINE_ENCODING_FORMAT_WAV, OAKENGINE_ENCODING_CODEC_PCM);
assert(scount > 0);
EXPECT_TRUE(scount > 0);
for (int i = 0; i < scount; i++) {
assert(oakengine_encoding_sample_format_at(
EXPECT_TRUE(oakengine_encoding_sample_format_at(
OAKENGINE_ENCODING_FORMAT_WAV, OAKENGINE_ENCODING_CODEC_PCM,
i) >= 0);
}
assert(oakengine_encoding_sample_format_at(
EXPECT_TRUE(oakengine_encoding_sample_format_at(
OAKENGINE_ENCODING_FORMAT_WAV, OAKENGINE_ENCODING_CODEC_PCM,
scount) == -1);
}
@@ -175,20 +176,20 @@ static void test_filename_helpers(void)
{
char buf[4096];
assert(oakengine_encoding_filename_contains_digit_placeholder(
EXPECT_TRUE(oakengine_encoding_filename_contains_digit_placeholder(
"/tmp/out_[#####].png") == 1);
assert(oakengine_encoding_filename_contains_digit_placeholder(
EXPECT_TRUE(oakengine_encoding_filename_contains_digit_placeholder(
"/tmp/out.png") == 0);
assert(oakengine_encoding_filename_contains_digit_placeholder(NULL) == 0);
EXPECT_TRUE(oakengine_encoding_filename_contains_digit_placeholder(NULL) == 0);
assert(oakengine_encoding_image_sequence_digit_count(
EXPECT_TRUE(oakengine_encoding_image_sequence_digit_count(
"/tmp/out_[#####].png") == 5);
assert(oakengine_encoding_image_sequence_digit_count("/tmp/out.png") == 0);
EXPECT_TRUE(oakengine_encoding_image_sequence_digit_count("/tmp/out.png") == 0);
assert(oakengine_encoding_filename_remove_digit_placeholder(
EXPECT_TRUE(oakengine_encoding_filename_remove_digit_placeholder(
"/tmp/out_[#####].png", buf, sizeof(buf)) > 0);
assert(strstr(buf, "[#####]") == NULL);
assert(strstr(buf, ".png") != NULL);
EXPECT_TRUE(strstr(buf, "[#####]") == NULL);
EXPECT_TRUE(strstr(buf, ".png") != NULL);
}
static void test_generate_matrix(void)
@@ -196,37 +197,37 @@ static void test_generate_matrix(void)
float m[16];
// Fit with matching dimensions is the identity
assert(oakengine_encoding_generate_matrix(OAKENGINE_ENCODING_SCALING_FIT,
EXPECT_TRUE(oakengine_encoding_generate_matrix(OAKENGINE_ENCODING_SCALING_FIT,
1920, 1080, 1920, 1080,
m) == OAKENGINE_OK);
const float identity[16] = { 1, 0, 0, 0, 0, 1, 0, 0,
0, 0, 1, 0, 0, 0, 0, 1 };
for (int i = 0; i < 16; i++) {
assert(fabsf(m[i] - identity[i]) < 1e-6f);
EXPECT_TRUE(fabsf(m[i] - identity[i]) < 1e-6f);
}
// Stretch is the identity transform (the preview is normalized device
// coordinates; stretching needs no matrix)
assert(oakengine_encoding_generate_matrix(OAKENGINE_ENCODING_SCALING_STRETCH,
EXPECT_TRUE(oakengine_encoding_generate_matrix(OAKENGINE_ENCODING_SCALING_STRETCH,
960, 540, 1920, 1080,
m) == OAKENGINE_OK);
for (int i = 0; i < 16; i++) {
assert(fabsf(m[i] - identity[i]) < 1e-6f);
EXPECT_TRUE(fabsf(m[i] - identity[i]) < 1e-6f);
}
// Fit into a wider-than-source frame pillarboxes: x scale shrinks to
// source_ar/export_ar
assert(oakengine_encoding_generate_matrix(OAKENGINE_ENCODING_SCALING_FIT,
EXPECT_TRUE(oakengine_encoding_generate_matrix(OAKENGINE_ENCODING_SCALING_FIT,
1920, 1080, 1920, 540,
m) == OAKENGINE_OK);
const float expected_x = (1920.0f / 1080.0f) / (1920.0f / 540.0f);
assert(fabsf(m[0] - expected_x) < 1e-5f);
assert(fabsf(m[5] - 1.0f) < 1e-6f);
EXPECT_TRUE(fabsf(m[0] - expected_x) < 1e-5f);
EXPECT_TRUE(fabsf(m[5] - 1.0f) < 1e-6f);
// Invalid arguments
assert(oakengine_encoding_generate_matrix(-1, 1, 1, 1, 1,
EXPECT_TRUE(oakengine_encoding_generate_matrix(-1, 1, 1, 1, 1,
m) == OAKENGINE_E_INVALID);
assert(oakengine_encoding_generate_matrix(OAKENGINE_ENCODING_SCALING_FIT, 0,
EXPECT_TRUE(oakengine_encoding_generate_matrix(OAKENGINE_ENCODING_SCALING_FIT, 0,
1, 1, 1,
m) == OAKENGINE_E_INVALID);
}
@@ -236,26 +237,26 @@ static void test_params_handle(void)
char buf[1024];
OakEngineEncodingParams *p = oakengine_encoding_params_create();
assert(p != NULL);
EXPECT_TRUE(p != NULL);
// Fresh handle: nothing enabled, format unset
assert(oakengine_encoding_params_is_valid(p) == 0);
assert(oakengine_encoding_params_format(p) == -1);
assert(oakengine_encoding_params_video_enabled(p) == 0);
assert(oakengine_encoding_params_audio_enabled(p) == 0);
assert(oakengine_encoding_params_subtitles_enabled(p) == 0);
assert(oakengine_encoding_params_has_custom_range(p) == 0);
EXPECT_TRUE(oakengine_encoding_params_is_valid(p) == 0);
EXPECT_TRUE(oakengine_encoding_params_format(p) == -1);
EXPECT_TRUE(oakengine_encoding_params_video_enabled(p) == 0);
EXPECT_TRUE(oakengine_encoding_params_audio_enabled(p) == 0);
EXPECT_TRUE(oakengine_encoding_params_subtitles_enabled(p) == 0);
EXPECT_TRUE(oakengine_encoding_params_has_custom_range(p) == 0);
// Filename / format roundtrip
assert(oakengine_encoding_params_set_filename(p, "/tmp/out.mp4") ==
EXPECT_TRUE(oakengine_encoding_params_set_filename(p, "/tmp/out.mp4") ==
OAKENGINE_OK);
assert(oakengine_encoding_params_filename(p, buf, sizeof(buf)) > 0);
assert(strcmp(buf, "/tmp/out.mp4") == 0);
assert(oakengine_encoding_params_set_format(
EXPECT_TRUE(oakengine_encoding_params_filename(p, buf, sizeof(buf)) > 0);
EXPECT_TRUE(strcmp(buf, "/tmp/out.mp4") == 0);
EXPECT_TRUE(oakengine_encoding_params_set_format(
p, OAKENGINE_ENCODING_FORMAT_MPEG4_VIDEO) == OAKENGINE_OK);
assert(oakengine_encoding_params_format(p) ==
EXPECT_TRUE(oakengine_encoding_params_format(p) ==
OAKENGINE_ENCODING_FORMAT_MPEG4_VIDEO);
assert(oakengine_encoding_params_set_format(p, 9999) ==
EXPECT_TRUE(oakengine_encoding_params_set_format(p, 9999) ==
OAKENGINE_E_INVALID);
// Video roundtrip
@@ -270,132 +271,132 @@ static void test_params_handle(void)
v.interlacing = OAKENGINE_ENCODING_INTERLACE_BOTTOM_FIRST;
v.color_range = OAKENGINE_ENCODING_COLOR_RANGE_FULL;
v.divider = 1;
assert(oakengine_encoding_params_enable_video(
EXPECT_TRUE(oakengine_encoding_params_enable_video(
p, &v, OAKENGINE_ENCODING_CODEC_H264) == OAKENGINE_OK);
assert(oakengine_encoding_params_is_valid(p) == 1);
assert(oakengine_encoding_params_video_enabled(p) == 1);
assert(oakengine_encoding_params_video_codec(p) ==
EXPECT_TRUE(oakengine_encoding_params_is_valid(p) == 1);
EXPECT_TRUE(oakengine_encoding_params_video_enabled(p) == 1);
EXPECT_TRUE(oakengine_encoding_params_video_codec(p) ==
OAKENGINE_ENCODING_CODEC_H264);
assert(oakengine_encoding_params_enable_video(p, NULL, 1) ==
EXPECT_TRUE(oakengine_encoding_params_enable_video(p, NULL, 1) ==
OAKENGINE_E_INVALID);
assert(oakengine_encoding_params_enable_video(p, &v, 9999) ==
EXPECT_TRUE(oakengine_encoding_params_enable_video(p, &v, 9999) ==
OAKENGINE_E_INVALID);
oak_video_params back = {};
assert(oakengine_encoding_params_get_video_params(p, &back) ==
EXPECT_TRUE(oakengine_encoding_params_get_video_params(p, &back) ==
OAKENGINE_OK);
assert(back.width == 1920 && back.height == 1080);
assert(back.time_base_num == 1001 && back.time_base_den == 30000);
assert(back.pixel_aspect_num == 1 && back.pixel_aspect_den == 1);
assert(back.interlacing == OAKENGINE_ENCODING_INTERLACE_BOTTOM_FIRST);
assert(back.color_range == OAKENGINE_ENCODING_COLOR_RANGE_FULL);
EXPECT_TRUE(back.width == 1920 && back.height == 1080);
EXPECT_TRUE(back.time_base_num == 1001 && back.time_base_den == 30000);
EXPECT_TRUE(back.pixel_aspect_num == 1 && back.pixel_aspect_den == 1);
EXPECT_TRUE(back.interlacing == OAKENGINE_ENCODING_INTERLACE_BOTTOM_FIRST);
EXPECT_TRUE(back.color_range == OAKENGINE_ENCODING_COLOR_RANGE_FULL);
// Audio roundtrip
assert(oakengine_encoding_params_enable_audio(p, 48000, 0x3, 4,
EXPECT_TRUE(oakengine_encoding_params_enable_audio(p, 48000, 0x3, 4,
OAKENGINE_ENCODING_CODEC_AAC) ==
OAKENGINE_OK);
assert(oakengine_encoding_params_audio_enabled(p) == 1);
assert(oakengine_encoding_params_audio_codec(p) ==
EXPECT_TRUE(oakengine_encoding_params_audio_enabled(p) == 1);
EXPECT_TRUE(oakengine_encoding_params_audio_codec(p) ==
OAKENGINE_ENCODING_CODEC_AAC);
int sample_rate = 0, sample_format = 0;
uint64_t layout = 0;
assert(oakengine_encoding_params_get_audio_params(p, &sample_rate, &layout,
EXPECT_TRUE(oakengine_encoding_params_get_audio_params(p, &sample_rate, &layout,
&sample_format) ==
OAKENGINE_OK);
assert(sample_rate == 48000 && layout == 0x3 && sample_format == 4);
assert(oakengine_encoding_params_enable_audio(p, 0, 0x3, 4, 0) ==
EXPECT_TRUE(sample_rate == 48000 && layout == 0x3 && sample_format == 4);
EXPECT_TRUE(oakengine_encoding_params_enable_audio(p, 0, 0x3, 4, 0) ==
OAKENGINE_E_INVALID);
// Subtitles (embedded, then sidecar)
assert(oakengine_encoding_params_enable_subtitles(
EXPECT_TRUE(oakengine_encoding_params_enable_subtitles(
p, OAKENGINE_ENCODING_CODEC_SRT) == OAKENGINE_OK);
assert(oakengine_encoding_params_subtitles_enabled(p) == 1);
assert(oakengine_encoding_params_subtitles_are_sidecar(p) == 0);
assert(oakengine_encoding_params_subtitles_codec(p) ==
EXPECT_TRUE(oakengine_encoding_params_subtitles_enabled(p) == 1);
EXPECT_TRUE(oakengine_encoding_params_subtitles_are_sidecar(p) == 0);
EXPECT_TRUE(oakengine_encoding_params_subtitles_codec(p) ==
OAKENGINE_ENCODING_CODEC_SRT);
assert(oakengine_encoding_params_enable_sidecar_subtitles(
EXPECT_TRUE(oakengine_encoding_params_enable_sidecar_subtitles(
p, OAKENGINE_ENCODING_FORMAT_SRT,
OAKENGINE_ENCODING_CODEC_SRT) == OAKENGINE_OK);
assert(oakengine_encoding_params_subtitles_are_sidecar(p) == 1);
assert(oakengine_encoding_params_subtitles_sidecar_format(p) ==
EXPECT_TRUE(oakengine_encoding_params_subtitles_are_sidecar(p) == 1);
EXPECT_TRUE(oakengine_encoding_params_subtitles_sidecar_format(p) ==
OAKENGINE_ENCODING_FORMAT_SRT);
// Scalar setters/getters
oakengine_encoding_params_set_video_bit_rate(p, 8000000);
assert(oakengine_encoding_params_video_bit_rate(p) == 8000000);
EXPECT_TRUE(oakengine_encoding_params_video_bit_rate(p) == 8000000);
oakengine_encoding_params_set_video_min_bit_rate(p, 1000);
assert(oakengine_encoding_params_video_min_bit_rate(p) == 1000);
EXPECT_TRUE(oakengine_encoding_params_video_min_bit_rate(p) == 1000);
oakengine_encoding_params_set_video_max_bit_rate(p, 16000000);
assert(oakengine_encoding_params_video_max_bit_rate(p) == 16000000);
EXPECT_TRUE(oakengine_encoding_params_video_max_bit_rate(p) == 16000000);
oakengine_encoding_params_set_video_buffer_size(p, 2000000);
assert(oakengine_encoding_params_video_buffer_size(p) == 2000000);
EXPECT_TRUE(oakengine_encoding_params_video_buffer_size(p) == 2000000);
oakengine_encoding_params_set_video_threads(p, 4);
assert(oakengine_encoding_params_video_threads(p) == 4);
EXPECT_TRUE(oakengine_encoding_params_video_threads(p) == 4);
oakengine_encoding_params_set_audio_bit_rate(p, 320000);
assert(oakengine_encoding_params_audio_bit_rate(p) == 320000);
EXPECT_TRUE(oakengine_encoding_params_audio_bit_rate(p) == 320000);
assert(oakengine_encoding_params_set_video_pix_fmt(p, "yuv420p") ==
EXPECT_TRUE(oakengine_encoding_params_set_video_pix_fmt(p, "yuv420p") ==
OAKENGINE_OK);
assert(oakengine_encoding_params_video_pix_fmt(p, buf, sizeof(buf)) > 0);
assert(strcmp(buf, "yuv420p") == 0);
EXPECT_TRUE(oakengine_encoding_params_video_pix_fmt(p, buf, sizeof(buf)) > 0);
EXPECT_TRUE(strcmp(buf, "yuv420p") == 0);
oakengine_encoding_params_set_video_is_image_sequence(p, 1);
assert(oakengine_encoding_params_video_is_image_sequence(p) == 1);
EXPECT_TRUE(oakengine_encoding_params_video_is_image_sequence(p) == 1);
oakengine_encoding_params_set_video_is_image_sequence(p, 0);
assert(oakengine_encoding_params_video_is_image_sequence(p) == 0);
EXPECT_TRUE(oakengine_encoding_params_video_is_image_sequence(p) == 0);
assert(oakengine_encoding_params_set_color_transform(p, "sRGB OETF") ==
EXPECT_TRUE(oakengine_encoding_params_set_color_transform(p, "sRGB OETF") ==
OAKENGINE_OK);
assert(oakengine_encoding_params_color_transform_output(p, buf,
EXPECT_TRUE(oakengine_encoding_params_color_transform_output(p, buf,
sizeof(buf)) > 0);
assert(strcmp(buf, "sRGB OETF") == 0);
EXPECT_TRUE(strcmp(buf, "sRGB OETF") == 0);
oakengine_encoding_params_set_export_length(p, 10, 1);
int num = 0, den = 0;
assert(oakengine_encoding_params_get_export_length(p, &num, &den) ==
EXPECT_TRUE(oakengine_encoding_params_get_export_length(p, &num, &den) ==
OAKENGINE_OK);
assert(num == 10 && den == 1);
EXPECT_TRUE(num == 10 && den == 1);
// Custom range
oakengine_encoding_params_set_custom_range(p, 1, 1, 5, 1);
assert(oakengine_encoding_params_has_custom_range(p) == 1);
EXPECT_TRUE(oakengine_encoding_params_has_custom_range(p) == 1);
int64_t in_num = 0, in_den = 0, out_num = 0, out_den = 0;
assert(oakengine_encoding_params_get_custom_range(p, &in_num, &in_den,
EXPECT_TRUE(oakengine_encoding_params_get_custom_range(p, &in_num, &in_den,
&out_num,
&out_den) == OAKENGINE_OK);
assert(in_num == 1 && in_den == 1 && out_num == 5 && out_den == 1);
EXPECT_TRUE(in_num == 1 && in_den == 1 && out_num == 5 && out_den == 1);
// Scaling method
assert(oakengine_encoding_params_set_video_scaling_method(
EXPECT_TRUE(oakengine_encoding_params_set_video_scaling_method(
p, OAKENGINE_ENCODING_SCALING_CROP) == OAKENGINE_OK);
assert(oakengine_encoding_params_video_scaling_method(p) ==
EXPECT_TRUE(oakengine_encoding_params_video_scaling_method(p) ==
OAKENGINE_ENCODING_SCALING_CROP);
assert(oakengine_encoding_params_set_video_scaling_method(p, 42) ==
EXPECT_TRUE(oakengine_encoding_params_set_video_scaling_method(p, 42) ==
OAKENGINE_E_INVALID);
// Video options
assert(oakengine_encoding_params_video_option(p, "crf", buf,
EXPECT_TRUE(oakengine_encoding_params_video_option(p, "crf", buf,
sizeof(buf)) ==
OAKENGINE_E_NOT_FOUND);
assert(oakengine_encoding_params_set_video_option(p, "crf", "18") ==
EXPECT_TRUE(oakengine_encoding_params_set_video_option(p, "crf", "18") ==
OAKENGINE_OK);
assert(oakengine_encoding_params_video_option(p, "crf", buf,
EXPECT_TRUE(oakengine_encoding_params_video_option(p, "crf", buf,
sizeof(buf)) > 0);
assert(strcmp(buf, "18") == 0);
EXPECT_TRUE(strcmp(buf, "18") == 0);
// Disables
oakengine_encoding_params_disable_subtitles(p);
assert(oakengine_encoding_params_subtitles_enabled(p) == 0);
EXPECT_TRUE(oakengine_encoding_params_subtitles_enabled(p) == 0);
oakengine_encoding_params_disable_video(p);
assert(oakengine_encoding_params_video_enabled(p) == 0);
assert(oakengine_encoding_params_get_video_params(p, &back) ==
EXPECT_TRUE(oakengine_encoding_params_video_enabled(p) == 0);
EXPECT_TRUE(oakengine_encoding_params_get_video_params(p, &back) ==
OAKENGINE_E_STATE);
oakengine_encoding_params_disable_audio(p);
assert(oakengine_encoding_params_audio_enabled(p) == 0);
EXPECT_TRUE(oakengine_encoding_params_audio_enabled(p) == 0);
// NULL safety
oakengine_encoding_params_destroy(NULL);
assert(oakengine_encoding_params_is_valid(NULL) == 0);
EXPECT_TRUE(oakengine_encoding_params_is_valid(NULL) == 0);
oakengine_encoding_params_destroy(p);
}
@@ -405,22 +406,22 @@ static void test_preset_files(void)
char buf[1024];
// Preset directory listing is readable (may be empty in the sandbox)
assert(oakengine_encoding_preset_path(buf, sizeof(buf)) > 0);
EXPECT_TRUE(oakengine_encoding_preset_path(buf, sizeof(buf)) > 0);
const int count = oakengine_encoding_preset_count();
assert(count >= 0);
EXPECT_TRUE(count >= 0);
for (int i = 0; i < count; i++) {
assert(oakengine_encoding_preset_name(i, buf, sizeof(buf)) > 0);
EXPECT_TRUE(oakengine_encoding_preset_name(i, buf, sizeof(buf)) > 0);
}
assert(oakengine_encoding_preset_name(count, buf, sizeof(buf)) == -1);
EXPECT_TRUE(oakengine_encoding_preset_name(count, buf, sizeof(buf)) == -1);
// Save/load roundtrip through a temp file
char path[4096];
snprintf(path, sizeof(path), "%s/preset.xml", g_tmpdir);
OakEngineEncodingParams *p = oakengine_encoding_params_create();
assert(oakengine_encoding_params_set_filename(p, "/tmp/out.mp4") ==
EXPECT_TRUE(oakengine_encoding_params_set_filename(p, "/tmp/out.mp4") ==
OAKENGINE_OK);
assert(oakengine_encoding_params_set_format(
EXPECT_TRUE(oakengine_encoding_params_set_format(
p, OAKENGINE_ENCODING_FORMAT_MPEG4_VIDEO) == OAKENGINE_OK);
oak_video_params v = {};
v.width = 1280;
@@ -433,59 +434,59 @@ static void test_preset_files(void)
v.interlacing = OAKENGINE_ENCODING_INTERLACE_NONE;
v.color_range = OAKENGINE_ENCODING_COLOR_RANGE_LIMITED;
v.divider = 1;
assert(oakengine_encoding_params_enable_video(
EXPECT_TRUE(oakengine_encoding_params_enable_video(
p, &v, OAKENGINE_ENCODING_CODEC_H264) == OAKENGINE_OK);
assert(oakengine_encoding_params_set_video_option(p, "crf", "20") ==
EXPECT_TRUE(oakengine_encoding_params_set_video_option(p, "crf", "20") ==
OAKENGINE_OK);
assert(oakengine_encoding_params_save_file(p, path) == OAKENGINE_OK);
EXPECT_TRUE(oakengine_encoding_params_save_file(p, path) == OAKENGINE_OK);
oakengine_encoding_params_destroy(p);
OakEngineEncodingParams *q = oakengine_encoding_params_create();
assert(oakengine_encoding_params_load_file(q, path) == OAKENGINE_OK);
assert(oakengine_encoding_params_video_enabled(q) == 1);
assert(oakengine_encoding_params_video_codec(q) ==
EXPECT_TRUE(oakengine_encoding_params_load_file(q, path) == OAKENGINE_OK);
EXPECT_TRUE(oakengine_encoding_params_video_enabled(q) == 1);
EXPECT_TRUE(oakengine_encoding_params_video_codec(q) ==
OAKENGINE_ENCODING_CODEC_H264);
oak_video_params back = {};
assert(oakengine_encoding_params_get_video_params(q, &back) ==
EXPECT_TRUE(oakengine_encoding_params_get_video_params(q, &back) ==
OAKENGINE_OK);
assert(back.width == 1280 && back.height == 720);
assert(back.time_base_num == 1 && back.time_base_den == 25);
assert(oakengine_encoding_params_video_option(q, "crf", buf,
EXPECT_TRUE(back.width == 1280 && back.height == 720);
EXPECT_TRUE(back.time_base_num == 1 && back.time_base_den == 25);
EXPECT_TRUE(oakengine_encoding_params_video_option(q, "crf", buf,
sizeof(buf)) > 0);
assert(strcmp(buf, "20") == 0);
EXPECT_TRUE(strcmp(buf, "20") == 0);
oakengine_encoding_params_destroy(q);
// Loading a nonexistent file fails
OakEngineEncodingParams *r = oakengine_encoding_params_create();
assert(oakengine_encoding_params_load_file(r, "/no/such/file.xml") ==
EXPECT_TRUE(oakengine_encoding_params_load_file(r, "/no/such/file.xml") ==
OAKENGINE_E_FAILED);
oakengine_encoding_params_destroy(r);
// Bad arguments
assert(oakengine_encoding_params_save_file(NULL, path) ==
EXPECT_TRUE(oakengine_encoding_params_save_file(NULL, path) ==
OAKENGINE_E_INVALID);
}
static void test_last_used_and_render_errors(void)
{
// NULL sequence: no last-used params, no-op setter
assert(oakengine_encoding_params_get_last_used(NULL) == NULL);
EXPECT_TRUE(oakengine_encoding_params_get_last_used(NULL) == NULL);
oakengine_encoding_params_set_last_used(NULL, NULL);
// render_with_params without a valid sequence/params fails cleanly
assert(oakengine_export_render_with_params(NULL, NULL) ==
EXPECT_TRUE(oakengine_export_render_with_params(NULL, NULL) ==
OAKENGINE_E_INVALID);
OakEngineEncodingParams *p = oakengine_encoding_params_create();
assert(oakengine_export_render_with_params(NULL, p) ==
EXPECT_TRUE(oakengine_export_render_with_params(NULL, p) ==
OAKENGINE_E_INVALID);
// Nothing enabled on the handle
assert(oakengine_export_render_with_params(
EXPECT_TRUE(oakengine_export_render_with_params(
reinterpret_cast<OakEngineSequence *>(p), p) ==
OAKENGINE_E_INVALID);
oakengine_encoding_params_destroy(p);
// Audio recording requires an enabled audio track on the handle
assert(oakengine_encoding_start_audio_recording(NULL, NULL, 0) ==
EXPECT_TRUE(oakengine_encoding_start_audio_recording(NULL, NULL, 0) ==
OAKENGINE_E_INVALID);
}
@@ -496,61 +497,61 @@ static void test_video_params_statics(void)
// Standard frame rates
const int fr_count = oakengine_video_params_supported_frame_rate_count();
assert(fr_count > 0);
EXPECT_TRUE(fr_count > 0);
for (int i = 0; i < fr_count; i++) {
assert(oakengine_video_params_supported_frame_rate_at(i, &num, &den) ==
EXPECT_TRUE(oakengine_video_params_supported_frame_rate_at(i, &num, &den) ==
OAKENGINE_OK);
assert(num > 0 && den > 0);
EXPECT_TRUE(num > 0 && den > 0);
}
assert(oakengine_video_params_supported_frame_rate_at(fr_count, &num,
EXPECT_TRUE(oakengine_video_params_supported_frame_rate_at(fr_count, &num,
&den) ==
OAKENGINE_E_INVALID);
// 24000/1001 prints as 23.976...
assert(oakengine_video_params_frame_rate_to_string(24000, 1001, buf,
EXPECT_TRUE(oakengine_video_params_frame_rate_to_string(24000, 1001, buf,
sizeof(buf)) > 0);
assert(strstr(buf, "23.97") != NULL);
EXPECT_TRUE(strstr(buf, "23.97") != NULL);
// Standard pixel aspects: the first one is square (1:1)
const int pa_count = oakengine_video_params_standard_pixel_aspect_count();
assert(pa_count > 0);
assert(oakengine_video_params_standard_pixel_aspect_at(0, &num, &den) ==
EXPECT_TRUE(pa_count > 0);
EXPECT_TRUE(oakengine_video_params_standard_pixel_aspect_at(0, &num, &den) ==
OAKENGINE_OK);
assert(num == 1 && den == 1);
assert(oakengine_video_params_standard_pixel_aspect_name(0, buf,
EXPECT_TRUE(num == 1 && den == 1);
EXPECT_TRUE(oakengine_video_params_standard_pixel_aspect_name(0, buf,
sizeof(buf)) > 0);
assert(buf[0] != '\0');
assert(oakengine_video_params_standard_pixel_aspect_at(pa_count, &num,
EXPECT_TRUE(buf[0] != '\0');
EXPECT_TRUE(oakengine_video_params_standard_pixel_aspect_at(pa_count, &num,
&den) ==
OAKENGINE_E_INVALID);
// Custom PAR label template
assert(oakengine_video_params_format_pixel_aspect_ratio_string(
EXPECT_TRUE(oakengine_video_params_format_pixel_aspect_ratio_string(
"Custom (%1)", 32, 27, buf, sizeof(buf)) > 0);
assert(strstr(buf, "Custom") != NULL);
EXPECT_TRUE(strstr(buf, "Custom") != NULL);
// Dividers
const int div_count = oakengine_video_params_supported_divider_count();
assert(div_count > 0);
EXPECT_TRUE(div_count > 0);
for (int i = 0; i < div_count; i++) {
const int d = oakengine_video_params_supported_divider_at(i);
assert(d > 0);
assert(oakengine_video_params_divider_name(d, buf, sizeof(buf)) > 0);
EXPECT_TRUE(d > 0);
EXPECT_TRUE(oakengine_video_params_divider_name(d, buf, sizeof(buf)) > 0);
}
assert(oakengine_video_params_supported_divider_at(div_count) == -1);
EXPECT_TRUE(oakengine_video_params_supported_divider_at(div_count) == -1);
// Pixel format names: some entry must be non-empty
assert(oakengine_video_params_pixel_format_name(8, buf, sizeof(buf)) > 0);
EXPECT_TRUE(oakengine_video_params_pixel_format_name(8, buf, sizeof(buf)) > 0);
// Float detection (8-bit integer formats are not float)
assert(oakengine_video_params_format_is_float(0) == 0);
EXPECT_TRUE(oakengine_video_params_format_is_float(0) == 0);
// Effective (divider-scaled) size
int w = 0, h = 0;
assert(oakengine_video_params_effective_size(1920, 1080, 2, &w, &h) ==
EXPECT_TRUE(oakengine_video_params_effective_size(1920, 1080, 2, &w, &h) ==
OAKENGINE_OK);
assert(w == 960 && h == 540);
assert(oakengine_video_params_effective_size(0, 1080, 2, &w, &h) ==
EXPECT_TRUE(w == 960 && h == 540);
EXPECT_TRUE(oakengine_video_params_effective_size(0, 1080, 2, &w, &h) ==
OAKENGINE_E_INVALID);
}
@@ -561,43 +562,43 @@ static void test_video_params_pod(void)
oak_video_params p, q;
// make fills every field
assert(oakengine_video_params_make(&p, 1920, 1080, 1001, 30000, 0, 1, 1,
EXPECT_TRUE(oakengine_video_params_make(&p, 1920, 1080, 1001, 30000, 0, 1, 1,
0, 0, 2) == OAKENGINE_OK);
assert(p.width == 1920 && p.height == 1080);
assert(p.time_base_num == 1001 && p.time_base_den == 30000);
assert(p.pixel_aspect_num == 1 && p.pixel_aspect_den == 1);
assert(p.divider == 2);
assert(oakengine_video_params_make(NULL, 1, 1, 1, 1, 0, 1, 1, 0, 0, 1) ==
EXPECT_TRUE(p.width == 1920 && p.height == 1080);
EXPECT_TRUE(p.time_base_num == 1001 && p.time_base_den == 30000);
EXPECT_TRUE(p.pixel_aspect_num == 1 && p.pixel_aspect_den == 1);
EXPECT_TRUE(p.divider == 2);
EXPECT_TRUE(oakengine_video_params_make(NULL, 1, 1, 1, 1, 0, 1, 1, 0, 0, 1) ==
OAKENGINE_E_INVALID);
// equal: identical PODs match, any single-field difference does not
q = p;
assert(oakengine_video_params_equal(&p, &q) == 1);
assert(oakengine_video_params_equal(&p, NULL) == 0);
assert(oakengine_video_params_equal(NULL, &q) == 0);
EXPECT_TRUE(oakengine_video_params_equal(&p, &q) == 1);
EXPECT_TRUE(oakengine_video_params_equal(&p, NULL) == 0);
EXPECT_TRUE(oakengine_video_params_equal(NULL, &q) == 0);
q.divider = 1;
assert(oakengine_video_params_equal(&p, &q) == 0);
EXPECT_TRUE(oakengine_video_params_equal(&p, &q) == 0);
q = p;
q.interlacing = 1;
assert(oakengine_video_params_equal(&p, &q) == 0);
EXPECT_TRUE(oakengine_video_params_equal(&p, &q) == 0);
// is_valid: positive dimensions + in-range format passes; zero
// dimensions or an out-of-range format fail
assert(oakengine_video_params_is_valid(&p) == 1);
assert(oakengine_video_params_is_valid(NULL) == 0);
EXPECT_TRUE(oakengine_video_params_is_valid(&p) == 1);
EXPECT_TRUE(oakengine_video_params_is_valid(NULL) == 0);
q = p;
q.width = 0;
assert(oakengine_video_params_is_valid(&q) == 0);
EXPECT_TRUE(oakengine_video_params_is_valid(&q) == 0);
q = p;
q.format = -1; // olive::PixelFormat::invalid
assert(oakengine_video_params_is_valid(&q) == 0);
EXPECT_TRUE(oakengine_video_params_is_valid(&q) == 0);
// bytes per pixel: u8 RGBA = 4, f32 RGBA = 16 (format values follow
// olive::PixelFormat::Format: 0 = u8, 4 = f32)
const int channels = oakengine_video_params_internal_channel_count();
assert(channels == 4);
assert(oakengine_video_params_bytes_per_pixel(0, channels) == 4);
assert(oakengine_video_params_bytes_per_pixel(4, channels) == 16);
EXPECT_TRUE(channels == 4);
EXPECT_TRUE(oakengine_video_params_bytes_per_pixel(0, channels) == 4);
EXPECT_TRUE(oakengine_video_params_bytes_per_pixel(4, channels) == 16);
}
// Engine-side VideoParams construction used by the app during R6 to avoid
@@ -605,20 +606,20 @@ static void test_video_params_pod(void)
static void test_video_params_create_free(void)
{
// NULL pod -> NULL handle
assert(oakengine_video_params_create(NULL) == NULL);
EXPECT_TRUE(oakengine_video_params_create(NULL) == NULL);
// Empty POD -> default-constructed VideoParams handle
oak_video_params empty = {};
void *vp_empty = oakengine_video_params_create(&empty);
assert(vp_empty != NULL);
EXPECT_TRUE(vp_empty != NULL);
oakengine_video_params_free(vp_empty);
// Display-path POD with explicit timebase
oak_video_params pod;
assert(oakengine_video_params_make(&pod, 1920, 1080, 1001, 30000, 0, 1, 1,
EXPECT_TRUE(oakengine_video_params_make(&pod, 1920, 1080, 1001, 30000, 0, 1, 1,
0, 0, 1) == OAKENGINE_OK);
void *vp = oakengine_video_params_create(&pod);
assert(vp != NULL);
EXPECT_TRUE(vp != NULL);
oakengine_video_params_free(vp);
// Display-path POD without timebase (uses constructor without timebase)
@@ -627,20 +628,20 @@ static void test_video_params_create_free(void)
pod2.height = 480;
pod2.format = 0; // u8
void *vp2 = oakengine_video_params_create(&pod2);
assert(vp2 != NULL);
EXPECT_TRUE(vp2 != NULL);
oakengine_video_params_free(vp2);
// free(NULL) is a no-op
oakengine_video_params_free(NULL);
}
int main(void)
TEST(OakEngineEncoding, Main)
{
make_tmpdir();
// HEADLESS: no GL, but a QCoreApplication (needed by the FFmpeg encoder
// probes and QStandardPaths behind the metadata queries) comes up.
assert(oakengine_init(OAKENGINE_INIT_HEADLESS) == OAKENGINE_OK);
EXPECT_TRUE(oakengine_init(OAKENGINE_INIT_HEADLESS) == OAKENGINE_OK);
test_format_metadata();
test_codec_metadata();
@@ -655,5 +656,4 @@ int main(void)
oakengine_shutdown();
printf("oakengine_encoding_test: OK\n");
return 0;
}
File diff suppressed because it is too large Load Diff
+77 -78
View File
@@ -27,6 +27,7 @@
// progress callback.
#include <assert.h>
#include <gtest/gtest.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
@@ -76,13 +77,13 @@ static void make_tmpdir(void)
#if defined(_WIN32)
char base[MAX_PATH];
const DWORD len = GetTempPathA(MAX_PATH, base);
assert(len > 0 && len < MAX_PATH);
EXPECT_TRUE(len > 0 && len < MAX_PATH);
snprintf(g_tmpdir, sizeof(g_tmpdir), "%soakengine_export_test_%lu", base,
(unsigned long)GetCurrentProcessId());
assert(_mkdir(g_tmpdir) == 0);
EXPECT_TRUE(_mkdir(g_tmpdir) == 0);
#else
strcpy(g_tmpdir, "/tmp/oakengine_export_test_XXXXXX");
assert(mkdtemp(g_tmpdir) != NULL);
EXPECT_TRUE(mkdtemp(g_tmpdir) != NULL);
#endif
}
@@ -142,39 +143,39 @@ static void progress_cb(double fraction, void *userdata)
static void test_codecs_and_validation(OakEngineSequence *seq)
{
// Codec probing needs no RENDER bit and no GL.
assert(oakengine_export_has_video_codec(OAKENGINE_EXPORT_VIDEO_H264) ==
EXPECT_TRUE(oakengine_export_has_video_codec(OAKENGINE_EXPORT_VIDEO_H264) ==
1);
assert(oakengine_export_has_video_codec(OAKENGINE_EXPORT_VIDEO_H265) ==
EXPECT_TRUE(oakengine_export_has_video_codec(OAKENGINE_EXPORT_VIDEO_H265) ==
1);
assert(oakengine_export_has_video_codec(
EXPECT_TRUE(oakengine_export_has_video_codec(
OAKENGINE_EXPORT_VIDEO_PNG_SEQUENCE) == 1);
assert(oakengine_export_has_video_codec(-2) == 0);
assert(oakengine_export_has_video_codec(99) == 0);
assert(oakengine_export_has_audio_codec(OAKENGINE_EXPORT_AUDIO_AAC) == 1);
assert(oakengine_export_has_audio_codec(OAKENGINE_EXPORT_AUDIO_PCM) == 1);
assert(oakengine_export_has_audio_codec(
EXPECT_TRUE(oakengine_export_has_video_codec(-2) == 0);
EXPECT_TRUE(oakengine_export_has_video_codec(99) == 0);
EXPECT_TRUE(oakengine_export_has_audio_codec(OAKENGINE_EXPORT_AUDIO_AAC) == 1);
EXPECT_TRUE(oakengine_export_has_audio_codec(OAKENGINE_EXPORT_AUDIO_PCM) == 1);
EXPECT_TRUE(oakengine_export_has_audio_codec(
OAKENGINE_EXPORT_AUDIO_NONE) == 0);
assert(oakengine_export_has_audio_codec(99) == 0);
EXPECT_TRUE(oakengine_export_has_audio_codec(99) == 0);
// Argument validation (engine has no RENDER bit yet either).
char path[4096];
snprintf(path, sizeof(path), "%s/out.mp4", g_tmpdir);
assert(oakengine_export_render(NULL, path, 0, 30, 320, 180, NULL) ==
EXPECT_TRUE(oakengine_export_render(NULL, path, 0, 30, 320, 180, NULL) ==
OAKENGINE_E_INVALID);
assert(oakengine_export_render(seq, NULL, 0, 30, 320, 180, NULL) ==
EXPECT_TRUE(oakengine_export_render(seq, NULL, 0, 30, 320, 180, NULL) ==
OAKENGINE_E_INVALID);
assert(oakengine_export_render(seq, path, -1, 30, 320, 180, NULL) ==
EXPECT_TRUE(oakengine_export_render(seq, path, -1, 30, 320, 180, NULL) ==
OAKENGINE_E_INVALID);
assert(oakengine_export_render(seq, path, 30, 30, 320, 180, NULL) ==
EXPECT_TRUE(oakengine_export_render(seq, path, 30, 30, 320, 180, NULL) ==
OAKENGINE_E_INVALID);
assert(oakengine_export_render(seq, path, 40, 30, 320, 180, NULL) ==
EXPECT_TRUE(oakengine_export_render(seq, path, 40, 30, 320, 180, NULL) ==
OAKENGINE_E_INVALID);
// Without OAKENGINE_INIT_RENDER the export is refused with E_STATE.
assert(oakengine_export_render(seq, path, 0, 30, 320, 180, NULL) ==
EXPECT_TRUE(oakengine_export_render(seq, path, 0, 30, 320, 180, NULL) ==
OAKENGINE_E_STATE);
char err[256];
assert(oakengine_export_last_error(err, sizeof(err)) > 0);
EXPECT_TRUE(oakengine_export_last_error(err, sizeof(err)) > 0);
}
// Generate a loud test tone (440 Hz stereo sine, 2 s) with the ffmpeg
@@ -187,9 +188,9 @@ static void make_tone(const char *path)
"ffmpeg -v error -y -f lavfi -i "
"\"sine=frequency=440:duration=2\" -ar 48000 -ac 2 \"%s\"",
path);
assert(system(cmd) == 0);
EXPECT_TRUE(system(cmd) == 0);
FILE *f = fopen(path, "rb");
assert(f != NULL);
EXPECT_TRUE(f != NULL);
fclose(f);
}
@@ -198,32 +199,32 @@ static void make_tone(const char *path)
static void assert_probe_matches(const char *cmd, const char *needle)
{
FILE *probe = popen(cmd, "r");
assert(probe != NULL);
EXPECT_TRUE(probe != NULL);
char out[2048] = { 0 };
const size_t len = fread(out, 1, sizeof(out) - 1, probe);
(void)len;
assert(pclose(probe) == 0);
assert(strstr(out, needle) != NULL);
EXPECT_TRUE(pclose(probe) == 0);
EXPECT_TRUE(strstr(out, needle) != NULL);
}
int main(void)
TEST(OakEngineExport, Main)
{
make_tmpdir();
// Sandbox the config/cache/data locations (see oakengine_init_test).
#if !defined(_WIN32)
assert(setenv("XDG_CONFIG_HOME", g_tmpdir, 1) == 0);
assert(setenv("XDG_CACHE_HOME", g_tmpdir, 1) == 0);
assert(setenv("XDG_DATA_HOME", g_tmpdir, 1) == 0);
EXPECT_TRUE(setenv("XDG_CONFIG_HOME", g_tmpdir, 1) == 0);
EXPECT_TRUE(setenv("XDG_CACHE_HOME", g_tmpdir, 1) == 0);
EXPECT_TRUE(setenv("XDG_DATA_HOME", g_tmpdir, 1) == 0);
#endif
assert(oakengine_init(OAKENGINE_INIT_HEADLESS) == OAKENGINE_OK);
EXPECT_TRUE(oakengine_init(OAKENGINE_INIT_HEADLESS) == OAKENGINE_OK);
OakEngineProject *project = oakengine_project_create();
assert(project != NULL);
assert(oakengine_project_new(project) == OAKENGINE_OK);
EXPECT_TRUE(project != NULL);
EXPECT_TRUE(oakengine_project_new(project) == OAKENGINE_OK);
OakEngineSequence *seq = oakengine_sequence_new(project, "Export");
assert(seq != NULL);
EXPECT_TRUE(seq != NULL);
test_codecs_and_validation(seq);
@@ -233,19 +234,19 @@ int main(void)
"available, export assertions skipped\n");
oakengine_project_free(project);
oakengine_shutdown();
return 0;
return;
}
if (!worker_binary_exists()) {
printf("oakengine_export_test: SKIP: oak-render-worker binary not "
"found, export assertions skipped\n");
oakengine_project_free(project);
oakengine_shutdown();
return 0;
return;
}
olive::Config::current()[QStringLiteral("GraphicsBackend")] =
QStringLiteral("opengl");
assert(oakengine_init(OAKENGINE_INIT_HEADLESS | OAKENGINE_INIT_RENDER) ==
EXPECT_TRUE(oakengine_init(OAKENGINE_INIT_HEADLESS | OAKENGINE_INIT_RENDER) ==
OAKENGINE_OK);
// Solid red generator -> texture input (engine C++ API, internal test).
@@ -273,34 +274,34 @@ int main(void)
err :
"(no error)");
}
assert(rc == OAKENGINE_OK);
EXPECT_TRUE(rc == OAKENGINE_OK);
oakengine_export_set_progress_callback(NULL, NULL);
// Progress was reported, monotonically, and completed.
assert(g_progress_calls > 0);
assert(g_progress_monotonic == 1);
assert(fabs(g_progress_last - 1.0) < 1e-6);
EXPECT_TRUE(g_progress_calls > 0);
EXPECT_TRUE(g_progress_monotonic == 1);
EXPECT_TRUE(fabs(g_progress_last - 1.0) < 1e-6);
// Validate the MP4 with ffprobe: h264 video, 320x180, ~1 second.
assert(access(out, F_OK) == 0);
EXPECT_TRUE(access(out, F_OK) == 0);
char cmd[4608];
snprintf(cmd, sizeof(cmd),
"ffprobe -v error -select_streams v:0 -show_entries "
"stream=codec_name,width,height,duration -of csv=p=0 \"%s\"",
out);
FILE *probe = popen(cmd, "r");
assert(probe != NULL);
EXPECT_TRUE(probe != NULL);
char probe_out[512] = { 0 };
const size_t probe_len = fread(probe_out, 1, sizeof(probe_out) - 1, probe);
(void)probe_len;
assert(pclose(probe) == 0);
assert(strstr(probe_out, "h264") != NULL);
assert(strstr(probe_out, "320,180") != NULL);
EXPECT_TRUE(pclose(probe) == 0);
EXPECT_TRUE(strstr(probe_out, "h264") != NULL);
EXPECT_TRUE(strstr(probe_out, "320,180") != NULL);
// Duration is the last csv field; 30 frames at 30000/1001 ~= 1.001 s.
const char *comma = strrchr(probe_out, ',');
assert(comma != NULL);
EXPECT_TRUE(comma != NULL);
const double duration = atof(comma + 1);
assert(fabs(duration - 1.001) < 0.15);
EXPECT_TRUE(fabs(duration - 1.001) < 0.15);
// ---- First-conform audio export ---------------------------------------
// The sandboxed cache guarantees no conform exists yet: this is the
@@ -316,10 +317,10 @@ int main(void)
// primitives.
OakEngineFootage *tone =
oakengine_project_import_footage(project, tone_path);
assert(tone != NULL);
assert(oakengine_sequence_add_track(seq, OAKENGINE_TRACK_TYPE_AUDIO) ==
EXPECT_TRUE(tone != NULL);
EXPECT_TRUE(oakengine_sequence_add_track(seq, OAKENGINE_TRACK_TYPE_AUDIO) ==
0);
assert(oakengine_sequence_add_footage_clip(
EXPECT_TRUE(oakengine_sequence_add_footage_clip(
seq, tone, OAKENGINE_TRACK_TYPE_AUDIO, 0, 0, 30, 0) !=
NULL);
@@ -338,8 +339,8 @@ int main(void)
err :
"(no error)");
}
assert(rc == OAKENGINE_OK);
assert(access(out2, F_OK) == 0);
EXPECT_TRUE(rc == OAKENGINE_OK);
EXPECT_TRUE(access(out2, F_OK) == 0);
// The MP4 carries an AAC audio stream...
snprintf(cmd, sizeof(cmd),
@@ -355,15 +356,15 @@ int main(void)
"null - 2>&1 | grep mean_volume",
out2);
FILE *vol = popen(cmd, "r");
assert(vol != NULL);
EXPECT_TRUE(vol != NULL);
char vol_out[512] = { 0 };
const size_t vol_len = fread(vol_out, 1, sizeof(vol_out) - 1, vol);
(void)vol_len;
assert(pclose(vol) == 0);
EXPECT_TRUE(pclose(vol) == 0);
const char *db = strstr(vol_out, "mean_volume:");
assert(db != NULL);
EXPECT_TRUE(db != NULL);
const double mean_db = atof(db + strlen("mean_volume:"));
assert(mean_db > -60.0);
EXPECT_TRUE(mean_db > -60.0);
oakengine_footage_free(tone);
}
@@ -380,44 +381,44 @@ int main(void)
snprintf(out_bad, sizeof(out_bad), "%s/bad.mp4", g_tmpdir);
bad.format = -1;
assert(oakengine_export_render_ex(seq, out_bad, &bad) ==
EXPECT_TRUE(oakengine_export_render_ex(seq, out_bad, &bad) ==
OAKENGINE_E_INVALID);
bad.format = 99;
assert(oakengine_export_render_ex(seq, out_bad, &bad) ==
EXPECT_TRUE(oakengine_export_render_ex(seq, out_bad, &bad) ==
OAKENGINE_E_INVALID);
bad.format = 2;
bad.video_codec = 99;
assert(oakengine_export_render_ex(seq, out_bad, &bad) ==
EXPECT_TRUE(oakengine_export_render_ex(seq, out_bad, &bad) ==
OAKENGINE_E_INVALID);
bad.video_codec = 1;
bad.range_mode = OAKENGINE_EXPORT_RANGE_CUSTOM;
bad.range_in_ts = 30;
bad.range_out_ts = 30;
assert(oakengine_export_render_ex(seq, out_bad, &bad) ==
EXPECT_TRUE(oakengine_export_render_ex(seq, out_bad, &bad) ==
OAKENGINE_E_INVALID);
bad.range_mode = OAKENGINE_EXPORT_RANGE_STILL;
bad.still_time_ts = -1;
assert(oakengine_export_render_ex(seq, out_bad, &bad) ==
EXPECT_TRUE(oakengine_export_render_ex(seq, out_bad, &bad) ==
OAKENGINE_E_INVALID);
bad.range_mode = OAKENGINE_EXPORT_RANGE_ENTIRE;
bad.color_transform = OAKENGINE_EXPORT_COLOR_CUSTOM;
bad.color_transform_name[0] = '\0';
assert(oakengine_export_render_ex(seq, out_bad, &bad) ==
EXPECT_TRUE(oakengine_export_render_ex(seq, out_bad, &bad) ==
OAKENGINE_E_INVALID);
bad.color_transform = OAKENGINE_EXPORT_COLOR_SRGB_OETF;
bad.video_pix_fmt = 99;
assert(oakengine_export_render_ex(seq, out_bad, &bad) ==
EXPECT_TRUE(oakengine_export_render_ex(seq, out_bad, &bad) ==
OAKENGINE_E_INVALID);
assert(oakengine_export_render_ex(NULL, out_bad, &bad) ==
EXPECT_TRUE(oakengine_export_render_ex(NULL, out_bad, &bad) ==
OAKENGINE_E_INVALID);
assert(oakengine_export_render_ex(seq, NULL, &bad) ==
EXPECT_TRUE(oakengine_export_render_ex(seq, NULL, &bad) ==
OAKENGINE_E_INVALID);
assert(oakengine_export_render_ex(seq, out_bad, NULL) ==
EXPECT_TRUE(oakengine_export_render_ex(seq, out_bad, NULL) ==
OAKENGINE_E_INVALID);
}
@@ -451,22 +452,22 @@ int main(void)
err :
"(no error)");
}
assert(rc == OAKENGINE_OK);
EXPECT_TRUE(rc == OAKENGINE_OK);
oakengine_export_set_video_option(NULL, NULL);
snprintf(cmd, sizeof(cmd),
"ffprobe -v error -show_entries stream=codec_type,duration "
"-of csv=p=0 \"%s\"",
out3);
FILE *p3 = popen(cmd, "r");
assert(p3 != NULL);
EXPECT_TRUE(p3 != NULL);
char p3_out[512] = { 0 };
const size_t p3_len = fread(p3_out, 1, sizeof(p3_out) - 1, p3);
(void)p3_len;
assert(pclose(p3) == 0);
assert(strstr(p3_out, "video") != NULL);
assert(strstr(p3_out, "audio") != NULL);
EXPECT_TRUE(pclose(p3) == 0);
EXPECT_TRUE(strstr(p3_out, "video") != NULL);
EXPECT_TRUE(strstr(p3_out, "audio") != NULL);
// 20 frames at 30000/1001 ~= 0.667 s.
assert(strstr(p3_out, "0.6") != NULL);
EXPECT_TRUE(strstr(p3_out, "0.6") != NULL);
// PNG sequence over 5 frames (image-sequence flag auto-fills the
// frame placeholder).
@@ -486,10 +487,10 @@ int main(void)
err :
"(no error)");
}
assert(rc == OAKENGINE_OK);
EXPECT_TRUE(rc == OAKENGINE_OK);
char png0[4096];
snprintf(png0, sizeof(png0), "%s/seqout_00000.png", g_tmpdir);
assert(access(png0, F_OK) == 0);
EXPECT_TRUE(access(png0, F_OK) == 0);
snprintf(cmd, sizeof(cmd), "file \"%s\"", png0);
assert_probe_matches(cmd, "PNG image data");
@@ -504,15 +505,13 @@ int main(void)
});
const int cancel_rc = oakengine_export_render_ex(seq, out3, &o5);
canceller.join();
assert(cancel_rc == OAKENGINE_OK ||
EXPECT_TRUE(cancel_rc == OAKENGINE_OK ||
cancel_rc == OAKENGINE_E_CANCELLED);
snprintf(out3, sizeof(out3), "%s/ex_after.mp4", g_tmpdir);
assert(oakengine_export_render_ex(seq, out3, &o5) == OAKENGINE_OK);
EXPECT_TRUE(oakengine_export_render_ex(seq, out3, &o5) == OAKENGINE_OK);
}
oakengine_project_free(project);
assert(oakengine_shutdown() == OAKENGINE_OK);
EXPECT_TRUE(oakengine_shutdown() == OAKENGINE_OK);
printf("oakengine_export_test: all assertions passed\n");
return 0;
}
File diff suppressed because it is too large Load Diff
+165 -166
View File
@@ -26,6 +26,7 @@
// QCoreApplication itself when needed.
#include <assert.h>
#include <gtest/gtest.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
@@ -53,20 +54,20 @@ static void make_tmpdir(void)
#if defined(_WIN32)
char base[MAX_PATH];
const DWORD len = GetTempPathA(MAX_PATH, base);
assert(len > 0 && len < MAX_PATH);
EXPECT_TRUE(len > 0 && len < MAX_PATH);
snprintf(g_tmpdir, sizeof(g_tmpdir), "%soakengine_init_test_%lu", base,
(unsigned long)GetCurrentProcessId());
assert(_mkdir(g_tmpdir) == 0);
EXPECT_TRUE(_mkdir(g_tmpdir) == 0);
#else
strcpy(g_tmpdir, "/tmp/oakengine_init_test_XXXXXX");
assert(mkdtemp(g_tmpdir) != NULL);
EXPECT_TRUE(mkdtemp(g_tmpdir) != NULL);
#endif
}
static void make_path(char *dst, size_t cap, const char *filename)
{
const int n = snprintf(dst, cap, "%s/%s", g_tmpdir, filename);
assert(n > 0 && (size_t)n < cap);
EXPECT_TRUE(n > 0 && (size_t)n < cap);
}
static int file_exists(const char *path)
@@ -81,58 +82,58 @@ static int file_exists(const char *path)
static void test_init(void)
{
assert(oakengine_init(0) == OAKENGINE_E_INVALID);
assert(oakengine_init(4) == OAKENGINE_E_INVALID); // unknown bit
EXPECT_TRUE(oakengine_init(0) == OAKENGINE_E_INVALID);
EXPECT_TRUE(oakengine_init(4) == OAKENGINE_E_INVALID); // unknown bit
assert(oakengine_init(OAKENGINE_INIT_HEADLESS) == OAKENGINE_OK);
assert(oakengine_init_flags() == OAKENGINE_INIT_HEADLESS);
EXPECT_TRUE(oakengine_init(OAKENGINE_INIT_HEADLESS) == OAKENGINE_OK);
EXPECT_TRUE(oakengine_init_flags() == OAKENGINE_INIT_HEADLESS);
// Repeated init is idempotent.
assert(oakengine_init(OAKENGINE_INIT_HEADLESS) == OAKENGINE_OK);
assert(oakengine_init_flags() == OAKENGINE_INIT_HEADLESS);
EXPECT_TRUE(oakengine_init(OAKENGINE_INIT_HEADLESS) == OAKENGINE_OK);
EXPECT_TRUE(oakengine_init_flags() == OAKENGINE_INIT_HEADLESS);
}
static void test_project_basics(void)
{
OakEngineProject *p = oakengine_project_create();
assert(p != NULL);
EXPECT_TRUE(p != NULL);
// Fresh shell: untitled, unmodified, empty.
assert(oakengine_project_is_modified(p) == 0);
assert(oakengine_project_footage_count(p) == 0);
assert(oakengine_project_sequence_count(p) == 0);
assert(oakengine_project_can_undo(p) == 0);
assert(oakengine_project_can_redo(p) == 0);
EXPECT_TRUE(oakengine_project_is_modified(p) == 0);
EXPECT_TRUE(oakengine_project_footage_count(p) == 0);
EXPECT_TRUE(oakengine_project_sequence_count(p) == 0);
EXPECT_TRUE(oakengine_project_can_undo(p) == 0);
EXPECT_TRUE(oakengine_project_can_redo(p) == 0);
assert(oakengine_project_new(p) == OAKENGINE_OK);
EXPECT_TRUE(oakengine_project_new(p) == OAKENGINE_OK);
// Content setup may only happen once.
assert(oakengine_project_new(p) == OAKENGINE_E_STATE);
EXPECT_TRUE(oakengine_project_new(p) == OAKENGINE_E_STATE);
// An untitled project reports the engine's "(untitled)" name and an
// empty filename.
char name[64];
const int name_len = oakengine_project_name(p, NULL, 0);
assert(name_len > 0);
assert(oakengine_project_name(p, name, sizeof(name)) == name_len);
assert((int)strlen(name) == name_len);
assert(strcmp(name, "(untitled)") == 0);
EXPECT_TRUE(name_len > 0);
EXPECT_TRUE(oakengine_project_name(p, name, sizeof(name)) == name_len);
EXPECT_TRUE((int)strlen(name) == name_len);
EXPECT_TRUE(strcmp(name, "(untitled)") == 0);
// buf/size truncation: reports the full size, writes what fits.
char tiny[4];
assert(oakengine_project_name(p, tiny, sizeof(tiny)) == name_len);
assert(strlen(tiny) == sizeof(tiny) - 1);
EXPECT_TRUE(oakengine_project_name(p, tiny, sizeof(tiny)) == name_len);
EXPECT_TRUE(strlen(tiny) == sizeof(tiny) - 1);
char filename[16];
assert(oakengine_project_filename(p, filename, sizeof(filename)) == 0);
assert(filename[0] == '\0');
EXPECT_TRUE(oakengine_project_filename(p, filename, sizeof(filename)) == 0);
EXPECT_TRUE(filename[0] == '\0');
// Modified flag round-trips.
assert(oakengine_project_set_modified(p, 1) == OAKENGINE_OK);
assert(oakengine_project_is_modified(p) == 1);
assert(oakengine_project_set_modified(p, 0) == OAKENGINE_OK);
assert(oakengine_project_is_modified(p) == 0);
EXPECT_TRUE(oakengine_project_set_modified(p, 1) == OAKENGINE_OK);
EXPECT_TRUE(oakengine_project_is_modified(p) == 1);
EXPECT_TRUE(oakengine_project_set_modified(p, 0) == OAKENGINE_OK);
EXPECT_TRUE(oakengine_project_is_modified(p) == 0);
// Saving an untitled project without a path fails.
assert(oakengine_project_save(p, NULL) == OAKENGINE_E_INVALID);
EXPECT_TRUE(oakengine_project_save(p, NULL) == OAKENGINE_E_INVALID);
oakengine_project_free(p);
}
@@ -143,158 +144,158 @@ static void test_sequence_and_save_load(void)
make_path(path, sizeof(path), "roundtrip.ove");
OakEngineProject *p = oakengine_project_create();
assert(p != NULL);
assert(oakengine_project_new(p) == OAKENGINE_OK);
EXPECT_TRUE(p != NULL);
EXPECT_TRUE(oakengine_project_new(p) == OAKENGINE_OK);
// Create a sequence through the facade; it is owned by the project.
OakEngineSequence *seq = oakengine_sequence_new(p, "Main");
assert(seq != NULL);
assert(oakengine_project_sequence_count(p) == 1);
assert(oakengine_project_sequence_at(p, 0) == seq);
assert(oakengine_project_sequence_at(p, 1) == NULL);
assert(oakengine_project_sequence_at(p, -1) == NULL);
EXPECT_TRUE(seq != NULL);
EXPECT_TRUE(oakengine_project_sequence_count(p) == 1);
EXPECT_TRUE(oakengine_project_sequence_at(p, 0) == seq);
EXPECT_TRUE(oakengine_project_sequence_at(p, 1) == NULL);
EXPECT_TRUE(oakengine_project_sequence_at(p, -1) == NULL);
// The creation went onto the global undo stack and marked the project.
assert(oakengine_project_can_undo(p) == 1);
assert(oakengine_project_is_modified(p) == 1);
EXPECT_TRUE(oakengine_project_can_undo(p) == 1);
EXPECT_TRUE(oakengine_project_is_modified(p) == 1);
// Sequence name round-trips.
char name[64];
const int name_len = oakengine_sequence_name(seq, NULL, 0);
assert(name_len == 4);
assert(oakengine_sequence_name(seq, name, sizeof(name)) == 4);
assert(strcmp(name, "Main") == 0);
EXPECT_TRUE(name_len == 4);
EXPECT_TRUE(oakengine_sequence_name(seq, name, sizeof(name)) == 4);
EXPECT_TRUE(strcmp(name, "Main") == 0);
// Empty sequence: zero length.
double seconds = -1.0;
assert(oakengine_sequence_get_length(seq, &seconds) == OAKENGINE_OK);
assert(seconds == 0.0);
EXPECT_TRUE(oakengine_sequence_get_length(seq, &seconds) == OAKENGINE_OK);
EXPECT_TRUE(seconds == 0.0);
int num = -1, den = -1;
assert(oakengine_sequence_get_length_rational(seq, &num, &den) ==
EXPECT_TRUE(oakengine_sequence_get_length_rational(seq, &num, &den) ==
OAKENGINE_OK);
assert(num == 0);
EXPECT_TRUE(num == 0);
// Default frame rate from Config's DefaultSequenceFrameRate entry
// (time base 1001/30000 -> frame rate 30000/1001).
num = den = -1;
assert(oakengine_sequence_get_frame_rate(seq, &num, &den) == OAKENGINE_OK);
assert(num == 30000 && den == 1001);
EXPECT_TRUE(oakengine_sequence_get_frame_rate(seq, &num, &den) == OAKENGINE_OK);
EXPECT_TRUE(num == 30000 && den == 1001);
// Default video dimensions (Config's DefaultSequenceWidth/Height) and
// square pixels
int width = -1, height = -1, par_num = -1, par_den = -1;
assert(oakengine_sequence_get_video_params(seq, &width, &height, &par_num,
EXPECT_TRUE(oakengine_sequence_get_video_params(seq, &width, &height, &par_num,
&par_den) == OAKENGINE_OK);
assert(width == 1920 && height == 1080);
assert(par_num == 1 && par_den == 1);
assert(oakengine_sequence_get_video_params(NULL, NULL, NULL, NULL, NULL) ==
EXPECT_TRUE(width == 1920 && height == 1080);
EXPECT_TRUE(par_num == 1 && par_den == 1);
EXPECT_TRUE(oakengine_sequence_get_video_params(NULL, NULL, NULL, NULL, NULL) ==
OAKENGINE_E_INVALID);
// Fresh sequences have no tracks.
int video = -1, audio = -1, subtitle = -1;
assert(oakengine_sequence_track_count(seq, &video, &audio, &subtitle) ==
EXPECT_TRUE(oakengine_sequence_track_count(seq, &video, &audio, &subtitle) ==
OAKENGINE_OK);
assert(video == 0 && audio == 0 && subtitle == 0);
EXPECT_TRUE(video == 0 && audio == 0 && subtitle == 0);
// Playhead starts at zero and accepts frame timestamps.
int64_t ts = -1;
assert(oakengine_sequence_get_playhead(seq, &ts) == OAKENGINE_OK);
assert(ts == 0);
assert(oakengine_sequence_set_playhead(seq, 30) == OAKENGINE_OK);
EXPECT_TRUE(oakengine_sequence_get_playhead(seq, &ts) == OAKENGINE_OK);
EXPECT_TRUE(ts == 0);
EXPECT_TRUE(oakengine_sequence_set_playhead(seq, 30) == OAKENGINE_OK);
ts = -1;
assert(oakengine_sequence_get_playhead(seq, &ts) == OAKENGINE_OK);
assert(ts == 30);
EXPECT_TRUE(oakengine_sequence_get_playhead(seq, &ts) == OAKENGINE_OK);
EXPECT_TRUE(ts == 30);
// 30 frames at 1001/30000 per frame = 1.001 seconds.
seconds = -1.0;
assert(oakengine_sequence_get_playhead_seconds(seq, &seconds) ==
EXPECT_TRUE(oakengine_sequence_get_playhead_seconds(seq, &seconds) ==
OAKENGINE_OK);
assert(fabs(seconds - 1.001) < 1e-9);
EXPECT_TRUE(fabs(seconds - 1.001) < 1e-9);
// Workarea: disabled by default, then set + read back.
assert(oakengine_sequence_workarea_is_enabled(seq) == 0);
assert(oakengine_sequence_set_workarea(seq, 1, 10, 40) == OAKENGINE_OK);
assert(oakengine_sequence_workarea_is_enabled(seq) == 1);
EXPECT_TRUE(oakengine_sequence_workarea_is_enabled(seq) == 0);
EXPECT_TRUE(oakengine_sequence_set_workarea(seq, 1, 10, 40) == OAKENGINE_OK);
EXPECT_TRUE(oakengine_sequence_workarea_is_enabled(seq) == 1);
int64_t in = -1, out = -1;
assert(oakengine_sequence_get_workarea(seq, &in, &out) == OAKENGINE_OK);
assert(in == 10 && out == 40);
EXPECT_TRUE(oakengine_sequence_get_workarea(seq, &in, &out) == OAKENGINE_OK);
EXPECT_TRUE(in == 10 && out == 40);
// No markers on a fresh sequence.
assert(oakengine_sequence_marker_count(seq) == 0);
assert(oakengine_sequence_marker_at(seq, 0, NULL, NULL, 0, NULL) ==
EXPECT_TRUE(oakengine_sequence_marker_count(seq) == 0);
EXPECT_TRUE(oakengine_sequence_marker_at(seq, 0, NULL, NULL, 0, NULL) ==
OAKENGINE_E_NOT_FOUND);
// Save; the modified flag clears and the filename is adopted.
assert(oakengine_project_save(p, path) == OAKENGINE_OK);
assert(oakengine_project_is_modified(p) == 0);
assert(file_exists(path));
EXPECT_TRUE(oakengine_project_save(p, path) == OAKENGINE_OK);
EXPECT_TRUE(oakengine_project_is_modified(p) == 0);
EXPECT_TRUE(file_exists(path));
char filename[4096];
assert(oakengine_project_filename(p, filename, sizeof(filename)) ==
EXPECT_TRUE(oakengine_project_filename(p, filename, sizeof(filename)) ==
(int)strlen(path));
assert(strcmp(filename, path) == 0);
EXPECT_TRUE(strcmp(filename, path) == 0);
// Undo removes the sequence, redo brings the same object back.
assert(oakengine_project_undo(p) == OAKENGINE_OK);
assert(oakengine_project_sequence_count(p) == 0);
assert(oakengine_project_can_redo(p) == 1);
assert(oakengine_project_redo(p) == OAKENGINE_OK);
assert(oakengine_project_sequence_count(p) == 1);
assert(oakengine_project_sequence_at(p, 0) == seq);
EXPECT_TRUE(oakengine_project_undo(p) == OAKENGINE_OK);
EXPECT_TRUE(oakengine_project_sequence_count(p) == 0);
EXPECT_TRUE(oakengine_project_can_redo(p) == 1);
EXPECT_TRUE(oakengine_project_redo(p) == OAKENGINE_OK);
EXPECT_TRUE(oakengine_project_sequence_count(p) == 1);
EXPECT_TRUE(oakengine_project_sequence_at(p, 0) == seq);
oakengine_project_free(p);
// Load the saved file into a fresh project.
OakEngineProject *q = oakengine_project_create();
assert(q != NULL);
EXPECT_TRUE(q != NULL);
char err[512];
assert(oakengine_project_load(q, path, err, sizeof(err)) == OAKENGINE_OK);
assert(oakengine_project_is_modified(q) == 0);
assert(oakengine_project_can_undo(q) == 0); // load clears the undo stack
assert(oakengine_project_can_redo(q) == 0);
EXPECT_TRUE(oakengine_project_load(q, path, err, sizeof(err)) == OAKENGINE_OK);
EXPECT_TRUE(oakengine_project_is_modified(q) == 0);
EXPECT_TRUE(oakengine_project_can_undo(q) == 0); // load clears the undo stack
EXPECT_TRUE(oakengine_project_can_redo(q) == 0);
// Project name derives from the file's base name.
const int qname_len = oakengine_project_name(q, NULL, 0);
assert(qname_len == (int)strlen("roundtrip"));
assert(oakengine_project_name(q, name, sizeof(name)) == qname_len);
assert(strcmp(name, "roundtrip") == 0);
assert(oakengine_project_filename(q, filename, sizeof(filename)) ==
EXPECT_TRUE(qname_len == (int)strlen("roundtrip"));
EXPECT_TRUE(oakengine_project_name(q, name, sizeof(name)) == qname_len);
EXPECT_TRUE(strcmp(name, "roundtrip") == 0);
EXPECT_TRUE(oakengine_project_filename(q, filename, sizeof(filename)) ==
(int)strlen(path));
assert(strcmp(filename, path) == 0);
EXPECT_TRUE(strcmp(filename, path) == 0);
// The sequence survived the round trip, workarea included (the workarea
// is serialized by ViewerOutput::save_custom()).
assert(oakengine_project_sequence_count(q) == 1);
EXPECT_TRUE(oakengine_project_sequence_count(q) == 1);
OakEngineSequence *loaded = oakengine_project_sequence_at(q, 0);
assert(loaded != NULL);
assert(oakengine_sequence_name(loaded, name, sizeof(name)) == 4);
assert(strcmp(name, "Main") == 0);
EXPECT_TRUE(loaded != NULL);
EXPECT_TRUE(oakengine_sequence_name(loaded, name, sizeof(name)) == 4);
EXPECT_TRUE(strcmp(name, "Main") == 0);
num = den = -1;
assert(oakengine_sequence_get_frame_rate(loaded, &num, &den) ==
EXPECT_TRUE(oakengine_sequence_get_frame_rate(loaded, &num, &den) ==
OAKENGINE_OK);
assert(num == 30000 && den == 1001);
assert(oakengine_sequence_track_count(loaded, &video, &audio,
EXPECT_TRUE(num == 30000 && den == 1001);
EXPECT_TRUE(oakengine_sequence_track_count(loaded, &video, &audio,
&subtitle) == OAKENGINE_OK);
assert(video == 0 && audio == 0 && subtitle == 0);
assert(oakengine_sequence_workarea_is_enabled(loaded) == 1);
EXPECT_TRUE(video == 0 && audio == 0 && subtitle == 0);
EXPECT_TRUE(oakengine_sequence_workarea_is_enabled(loaded) == 1);
in = out = -1;
assert(oakengine_sequence_get_workarea(loaded, &in, &out) == OAKENGINE_OK);
assert(in == 10 && out == 40);
assert(oakengine_project_footage_count(q) == 0);
EXPECT_TRUE(oakengine_sequence_get_workarea(loaded, &in, &out) == OAKENGINE_OK);
EXPECT_TRUE(in == 10 && out == 40);
EXPECT_TRUE(oakengine_project_footage_count(q) == 0);
// Saving with a NULL path reuses the project's current filename.
assert(oakengine_project_save(q, NULL) == OAKENGINE_OK);
assert(file_exists(path));
EXPECT_TRUE(oakengine_project_save(q, NULL) == OAKENGINE_OK);
EXPECT_TRUE(file_exists(path));
oakengine_project_free(q);
// Loading a missing file fails and reports a human-readable reason.
OakEngineProject *r = oakengine_project_create();
assert(r != NULL);
EXPECT_TRUE(r != NULL);
char missing[4096];
make_path(missing, sizeof(missing), "does-not-exist.ove");
assert(oakengine_project_load(r, missing, err, sizeof(err)) ==
EXPECT_TRUE(oakengine_project_load(r, missing, err, sizeof(err)) ==
OAKENGINE_E_FAILED);
assert(strlen(err) > 0);
EXPECT_TRUE(strlen(err) > 0);
// Loading into a project that already has content is rejected.
assert(oakengine_project_new(r) == OAKENGINE_OK);
assert(oakengine_project_load(r, missing, err, sizeof(err)) ==
EXPECT_TRUE(oakengine_project_new(r) == OAKENGINE_OK);
EXPECT_TRUE(oakengine_project_load(r, missing, err, sizeof(err)) ==
OAKENGINE_E_STATE);
oakengine_project_free(r);
}
@@ -308,36 +309,36 @@ static void test_footage_fixture(void)
const int n = snprintf(fixture, sizeof(fixture),
"%s/tests/project_with_footage.ove",
OAK_TEST_SOURCE_DIR);
assert(n > 0 && (size_t)n < sizeof(fixture));
assert(file_exists(fixture));
EXPECT_TRUE(n > 0 && (size_t)n < sizeof(fixture));
EXPECT_TRUE(file_exists(fixture));
OakEngineProject *p = oakengine_project_create();
assert(p != NULL);
EXPECT_TRUE(p != NULL);
char err[512];
assert(oakengine_project_load(p, fixture, err, sizeof(err)) ==
EXPECT_TRUE(oakengine_project_load(p, fixture, err, sizeof(err)) ==
OAKENGINE_OK);
assert(oakengine_project_footage_count(p) == 1);
EXPECT_TRUE(oakengine_project_footage_count(p) == 1);
char filename[256];
assert(oakengine_project_footage_filename(p, 0, filename,
EXPECT_TRUE(oakengine_project_footage_filename(p, 0, filename,
sizeof(filename)) > 0);
assert(strcmp(filename, "demo.mp4") == 0);
EXPECT_TRUE(strcmp(filename, "demo.mp4") == 0);
// The stored relative path resolves against the project file's
// directory (tests/), where demo.mp4 exists.
assert(oakengine_project_footage_is_online(p, 0) == 1);
EXPECT_TRUE(oakengine_project_footage_is_online(p, 0) == 1);
// Out-of-range footage indexes report OAKENGINE_E_NOT_FOUND.
assert(oakengine_project_footage_filename(p, 1, filename,
EXPECT_TRUE(oakengine_project_footage_filename(p, 1, filename,
sizeof(filename)) ==
OAKENGINE_E_NOT_FOUND);
assert(oakengine_project_footage_is_online(p, -1) ==
EXPECT_TRUE(oakengine_project_footage_is_online(p, -1) ==
OAKENGINE_E_NOT_FOUND);
assert(oakengine_project_sequence_count(p) == 1);
EXPECT_TRUE(oakengine_project_sequence_count(p) == 1);
OakEngineSequence *seq = oakengine_project_sequence_at(p, 0);
assert(seq != NULL);
EXPECT_TRUE(seq != NULL);
char name[64];
assert(oakengine_sequence_name(seq, name, sizeof(name)) > 0);
assert(strcmp(name, "Fixture Sequence") == 0);
EXPECT_TRUE(oakengine_sequence_name(seq, name, sizeof(name)) > 0);
EXPECT_TRUE(strcmp(name, "Fixture Sequence") == 0);
oakengine_project_free(p);
}
@@ -345,65 +346,65 @@ static void test_footage_fixture(void)
static void test_null_safety(void)
{
oakengine_project_free(NULL);
assert(oakengine_project_new(NULL) == OAKENGINE_E_INVALID);
assert(oakengine_project_load(NULL, "/tmp/x.ove", NULL, 0) ==
EXPECT_TRUE(oakengine_project_new(NULL) == OAKENGINE_E_INVALID);
EXPECT_TRUE(oakengine_project_load(NULL, "/tmp/x.ove", NULL, 0) ==
OAKENGINE_E_INVALID);
assert(oakengine_project_save(NULL, "/tmp/x.ove") == OAKENGINE_E_INVALID);
assert(oakengine_project_is_modified(NULL) == 0);
assert(oakengine_project_set_modified(NULL, 1) == OAKENGINE_E_INVALID);
assert(oakengine_project_name(NULL, NULL, 0) == OAKENGINE_E_INVALID);
assert(oakengine_project_filename(NULL, NULL, 0) == OAKENGINE_E_INVALID);
assert(oakengine_project_footage_count(NULL) == 0);
assert(oakengine_project_footage_filename(NULL, 0, NULL, 0) ==
EXPECT_TRUE(oakengine_project_save(NULL, "/tmp/x.ove") == OAKENGINE_E_INVALID);
EXPECT_TRUE(oakengine_project_is_modified(NULL) == 0);
EXPECT_TRUE(oakengine_project_set_modified(NULL, 1) == OAKENGINE_E_INVALID);
EXPECT_TRUE(oakengine_project_name(NULL, NULL, 0) == OAKENGINE_E_INVALID);
EXPECT_TRUE(oakengine_project_filename(NULL, NULL, 0) == OAKENGINE_E_INVALID);
EXPECT_TRUE(oakengine_project_footage_count(NULL) == 0);
EXPECT_TRUE(oakengine_project_footage_filename(NULL, 0, NULL, 0) ==
OAKENGINE_E_INVALID);
assert(oakengine_project_footage_is_online(NULL, 0) ==
EXPECT_TRUE(oakengine_project_footage_is_online(NULL, 0) ==
OAKENGINE_E_INVALID);
assert(oakengine_project_can_undo(NULL) == 0);
assert(oakengine_project_can_redo(NULL) == 0);
assert(oakengine_project_undo(NULL) == OAKENGINE_E_INVALID);
assert(oakengine_project_redo(NULL) == OAKENGINE_E_INVALID);
assert(oakengine_project_sequence_count(NULL) == 0);
assert(oakengine_project_sequence_at(NULL, 0) == NULL);
EXPECT_TRUE(oakengine_project_can_undo(NULL) == 0);
EXPECT_TRUE(oakengine_project_can_redo(NULL) == 0);
EXPECT_TRUE(oakengine_project_undo(NULL) == OAKENGINE_E_INVALID);
EXPECT_TRUE(oakengine_project_redo(NULL) == OAKENGINE_E_INVALID);
EXPECT_TRUE(oakengine_project_sequence_count(NULL) == 0);
EXPECT_TRUE(oakengine_project_sequence_at(NULL, 0) == NULL);
assert(oakengine_sequence_new(NULL, "x") == NULL);
assert(oakengine_sequence_name(NULL, NULL, 0) == OAKENGINE_E_INVALID);
assert(oakengine_sequence_get_length(NULL, NULL) == OAKENGINE_E_INVALID);
assert(oakengine_sequence_get_length_rational(NULL, NULL, NULL) ==
EXPECT_TRUE(oakengine_sequence_new(NULL, "x") == NULL);
EXPECT_TRUE(oakengine_sequence_name(NULL, NULL, 0) == OAKENGINE_E_INVALID);
EXPECT_TRUE(oakengine_sequence_get_length(NULL, NULL) == OAKENGINE_E_INVALID);
EXPECT_TRUE(oakengine_sequence_get_length_rational(NULL, NULL, NULL) ==
OAKENGINE_E_INVALID);
assert(oakengine_sequence_get_frame_rate(NULL, NULL, NULL) ==
EXPECT_TRUE(oakengine_sequence_get_frame_rate(NULL, NULL, NULL) ==
OAKENGINE_E_INVALID);
assert(oakengine_sequence_track_count(NULL, NULL, NULL, NULL) ==
EXPECT_TRUE(oakengine_sequence_track_count(NULL, NULL, NULL, NULL) ==
OAKENGINE_E_INVALID);
assert(oakengine_sequence_get_playhead(NULL, NULL) == OAKENGINE_E_INVALID);
assert(oakengine_sequence_set_playhead(NULL, 0) == OAKENGINE_E_INVALID);
assert(oakengine_sequence_get_playhead_seconds(NULL, NULL) ==
EXPECT_TRUE(oakengine_sequence_get_playhead(NULL, NULL) == OAKENGINE_E_INVALID);
EXPECT_TRUE(oakengine_sequence_set_playhead(NULL, 0) == OAKENGINE_E_INVALID);
EXPECT_TRUE(oakengine_sequence_get_playhead_seconds(NULL, NULL) ==
OAKENGINE_E_INVALID);
assert(oakengine_sequence_workarea_is_enabled(NULL) == 0);
assert(oakengine_sequence_get_workarea(NULL, NULL, NULL) ==
EXPECT_TRUE(oakengine_sequence_workarea_is_enabled(NULL) == 0);
EXPECT_TRUE(oakengine_sequence_get_workarea(NULL, NULL, NULL) ==
OAKENGINE_E_INVALID);
assert(oakengine_sequence_set_workarea(NULL, 0, 0, 0) ==
EXPECT_TRUE(oakengine_sequence_set_workarea(NULL, 0, 0, 0) ==
OAKENGINE_E_INVALID);
assert(oakengine_sequence_marker_count(NULL) == 0);
assert(oakengine_sequence_marker_at(NULL, 0, NULL, NULL, 0, NULL) ==
EXPECT_TRUE(oakengine_sequence_marker_count(NULL) == 0);
EXPECT_TRUE(oakengine_sequence_marker_at(NULL, 0, NULL, NULL, 0, NULL) ==
OAKENGINE_E_INVALID);
}
static void test_shutdown_pairing(void)
{
assert(oakengine_shutdown() == OAKENGINE_OK);
assert(oakengine_init_flags() == 0);
EXPECT_TRUE(oakengine_shutdown() == OAKENGINE_OK);
EXPECT_TRUE(oakengine_init_flags() == 0);
// Shutdown is idempotent.
assert(oakengine_shutdown() == OAKENGINE_OK);
EXPECT_TRUE(oakengine_shutdown() == OAKENGINE_OK);
// init may run again after a paired shutdown.
assert(oakengine_init(OAKENGINE_INIT_HEADLESS) == OAKENGINE_OK);
assert(oakengine_init_flags() == OAKENGINE_INIT_HEADLESS);
assert(oakengine_shutdown() == OAKENGINE_OK);
assert(oakengine_init_flags() == 0);
EXPECT_TRUE(oakengine_init(OAKENGINE_INIT_HEADLESS) == OAKENGINE_OK);
EXPECT_TRUE(oakengine_init_flags() == OAKENGINE_INIT_HEADLESS);
EXPECT_TRUE(oakengine_shutdown() == OAKENGINE_OK);
EXPECT_TRUE(oakengine_init_flags() == 0);
}
int main(void)
TEST(OakEngineInit, Main)
{
make_tmpdir();
@@ -413,9 +414,9 @@ int main(void)
// (FileFunctions::get_configuration_location()), which follows
// XDG_DATA_HOME on Linux; the other two are sandboxed for good measure.
#if !defined(_WIN32)
assert(setenv("XDG_CONFIG_HOME", g_tmpdir, 1) == 0);
assert(setenv("XDG_CACHE_HOME", g_tmpdir, 1) == 0);
assert(setenv("XDG_DATA_HOME", g_tmpdir, 1) == 0);
EXPECT_TRUE(setenv("XDG_CONFIG_HOME", g_tmpdir, 1) == 0);
EXPECT_TRUE(setenv("XDG_CACHE_HOME", g_tmpdir, 1) == 0);
EXPECT_TRUE(setenv("XDG_DATA_HOME", g_tmpdir, 1) == 0);
#endif
test_init();
@@ -425,6 +426,4 @@ int main(void)
test_null_safety();
test_shutdown_pairing();
printf("oakengine_init_test: all assertions passed\n");
return 0;
}
+180 -181
View File
@@ -23,6 +23,7 @@
// layout) and every control-plane message build/parse pair. No Qt, no GL.
#include <assert.h>
#include <gtest/gtest.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
@@ -58,7 +59,7 @@ static void assert_json_eq(const char *produced, const char *expected)
if (strcmp(produced, expected) != 0) {
fprintf(stderr, "JSON mismatch:\n produced: %s\n expected: %s\n",
produced, expected);
assert(0);
EXPECT_TRUE(0);
}
}
@@ -70,79 +71,79 @@ static void test_shm(void)
int needed = oakengine_ipc_shm_make_key(pid, 0, NULL, 0);
char expected[64];
snprintf(expected, sizeof(expected), "olive-rw-%lld-0", (long long)pid);
assert(needed == (int)strlen(expected));
assert(oakengine_ipc_shm_make_key(pid, 0, key, sizeof(key)) == needed);
assert(strcmp(key, expected) == 0);
assert(oakengine_ipc_shm_make_key(pid, 0, key, 6) == needed); // truncated
assert(strcmp(key, "olive") == 0);
EXPECT_TRUE(needed == (int)strlen(expected));
EXPECT_TRUE(oakengine_ipc_shm_make_key(pid, 0, key, sizeof(key)) == needed);
EXPECT_TRUE(strcmp(key, expected) == 0);
EXPECT_TRUE(oakengine_ipc_shm_make_key(pid, 0, key, 6) == needed); // truncated
EXPECT_TRUE(strcmp(key, "olive") == 0);
// A different worker index produces a different key.
char key1[OAK_IPC_SHM_KEY_CAP];
assert(oakengine_ipc_shm_make_key(pid, 1, key1, sizeof(key1)) == needed);
assert(strcmp(key1, expected) != 0);
EXPECT_TRUE(oakengine_ipc_shm_make_key(pid, 1, key1, sizeof(key1)) == needed);
EXPECT_TRUE(strcmp(key1, expected) != 0);
const int key_len = oakengine_ipc_shm_make_key(pid, 0, key, sizeof(key));
(void)key_len;
// Attaching to a missing segment fails and reports an error.
OakSharedMemoryRegion *missing = oakengine_ipc_shm_create();
assert(oakengine_ipc_shm_open(missing, key, 4096,
EXPECT_TRUE(oakengine_ipc_shm_open(missing, key, 4096,
OAK_IPC_SHM_MODE_ATTACH) == 0);
assert(oakengine_ipc_shm_is_valid(missing) == 0);
EXPECT_TRUE(oakengine_ipc_shm_is_valid(missing) == 0);
char err[256];
assert(oakengine_ipc_shm_error(missing, err, sizeof(err)) > 0);
assert(strlen(err) > 0);
EXPECT_TRUE(oakengine_ipc_shm_error(missing, err, sizeof(err)) > 0);
EXPECT_TRUE(strlen(err) > 0);
oakengine_ipc_shm_free(missing);
// Create the owner: valid, sized, zero-initialized.
OakSharedMemoryRegion *owner = oakengine_ipc_shm_create();
assert(oakengine_ipc_shm_open(owner, key, 8192, OAK_IPC_SHM_MODE_CREATE) ==
EXPECT_TRUE(oakengine_ipc_shm_open(owner, key, 8192, OAK_IPC_SHM_MODE_CREATE) ==
1);
assert(oakengine_ipc_shm_is_valid(owner) == 1);
assert(oakengine_ipc_shm_size(owner) == 8192);
EXPECT_TRUE(oakengine_ipc_shm_is_valid(owner) == 1);
EXPECT_TRUE(oakengine_ipc_shm_size(owner) == 8192);
unsigned char *data = (unsigned char *)oakengine_ipc_shm_data(owner);
assert(data != NULL);
EXPECT_TRUE(data != NULL);
for (int i = 0; i < 8192; i++) {
assert(data[i] == 0);
EXPECT_TRUE(data[i] == 0);
}
// key() round-trips the opened key.
char opened_key[OAK_IPC_SHM_KEY_CAP];
assert(oakengine_ipc_shm_key(owner, opened_key, sizeof(opened_key)) ==
EXPECT_TRUE(oakengine_ipc_shm_key(owner, opened_key, sizeof(opened_key)) ==
key_len);
assert(strcmp(opened_key, key) == 0);
EXPECT_TRUE(strcmp(opened_key, key) == 0);
// A peer attaches by the same key and sees the owner's bytes.
data[0] = 0xAB;
data[8191] = 0xCD;
OakSharedMemoryRegion *peer = oakengine_ipc_shm_create();
assert(oakengine_ipc_shm_open(peer, key, 8192, OAK_IPC_SHM_MODE_ATTACH) ==
EXPECT_TRUE(oakengine_ipc_shm_open(peer, key, 8192, OAK_IPC_SHM_MODE_ATTACH) ==
1);
assert(oakengine_ipc_shm_size(peer) == 8192);
EXPECT_TRUE(oakengine_ipc_shm_size(peer) == 8192);
const unsigned char *peer_data =
(const unsigned char *)oakengine_ipc_shm_data(peer);
assert(peer_data[0] == 0xAB && peer_data[8191] == 0xCD);
EXPECT_TRUE(peer_data[0] == 0xAB && peer_data[8191] == 0xCD);
peer_data = NULL;
// The peer closes without unlinking; the owner stays valid.
oakengine_ipc_shm_close(peer);
assert(oakengine_ipc_shm_is_valid(peer) == 0);
assert(oakengine_ipc_shm_is_valid(owner) == 1);
EXPECT_TRUE(oakengine_ipc_shm_is_valid(peer) == 0);
EXPECT_TRUE(oakengine_ipc_shm_is_valid(owner) == 1);
oakengine_ipc_shm_free(peer);
// The owner unlinks on close: a late attach must fail.
oakengine_ipc_shm_close(owner);
assert(oakengine_ipc_shm_is_valid(owner) == 0);
EXPECT_TRUE(oakengine_ipc_shm_is_valid(owner) == 0);
OakSharedMemoryRegion *late = oakengine_ipc_shm_create();
assert(oakengine_ipc_shm_open(late, key, 8192, OAK_IPC_SHM_MODE_ATTACH) ==
EXPECT_TRUE(oakengine_ipc_shm_open(late, key, 8192, OAK_IPC_SHM_MODE_ATTACH) ==
0);
oakengine_ipc_shm_free(late);
oakengine_ipc_shm_free(owner);
// NULL safety.
oakengine_ipc_shm_close(NULL);
assert(oakengine_ipc_shm_is_valid(NULL) == 0);
assert(oakengine_ipc_shm_data(NULL) == NULL);
assert(oakengine_ipc_shm_size(NULL) == 0);
EXPECT_TRUE(oakengine_ipc_shm_is_valid(NULL) == 0);
EXPECT_TRUE(oakengine_ipc_shm_data(NULL) == NULL);
EXPECT_TRUE(oakengine_ipc_shm_size(NULL) == 0);
oakengine_ipc_shm_free(NULL);
}
@@ -152,51 +153,51 @@ static void test_framepool(void)
const size_t k_slot_bytes = 256;
// bytes_needed stays 64-aligned and grows with geometry.
assert(oakengine_ipc_framepool_bytes_needed(k_slots, k_slot_bytes) % 64 ==
EXPECT_TRUE(oakengine_ipc_framepool_bytes_needed(k_slots, k_slot_bytes) % 64 ==
0);
assert(oakengine_ipc_framepool_bytes_needed(2, 65) ==
EXPECT_TRUE(oakengine_ipc_framepool_bytes_needed(2, 65) ==
oakengine_ipc_framepool_bytes_needed(2, 128));
assert(oakengine_ipc_framepool_bytes_needed(1, 64) <
EXPECT_TRUE(oakengine_ipc_framepool_bytes_needed(1, 64) <
oakengine_ipc_framepool_bytes_needed(2, 64));
const size_t bytes =
oakengine_ipc_framepool_bytes_needed(k_slots, k_slot_bytes);
void *mem = malloc(bytes);
assert(mem != NULL);
EXPECT_TRUE(mem != NULL);
OakFrameSlotPool *filler =
oakengine_ipc_framepool_create(mem, k_slots, k_slot_bytes);
assert(filler != NULL);
assert(oakengine_ipc_framepool_is_valid(filler) == 1);
assert(oakengine_ipc_framepool_slot_count(filler) == k_slots);
assert(oakengine_ipc_framepool_slot_data_bytes(filler) == k_slot_bytes);
EXPECT_TRUE(filler != NULL);
EXPECT_TRUE(oakengine_ipc_framepool_is_valid(filler) == 1);
EXPECT_TRUE(oakengine_ipc_framepool_slot_count(filler) == k_slots);
EXPECT_TRUE(oakengine_ipc_framepool_slot_data_bytes(filler) == k_slot_bytes);
// Peer maps the same memory; a copy of the handle views it too.
OakFrameSlotPool *drainer = oakengine_ipc_framepool_attach(mem);
assert(oakengine_ipc_framepool_is_valid(drainer) == 1);
EXPECT_TRUE(oakengine_ipc_framepool_is_valid(drainer) == 1);
OakFrameSlotPool *drainer_copy = oakengine_ipc_framepool_copy(drainer);
assert(drainer_copy != NULL && drainer_copy != drainer);
assert(oakengine_ipc_framepool_slot_count(drainer_copy) == k_slots);
EXPECT_TRUE(drainer_copy != NULL && drainer_copy != drainer);
EXPECT_TRUE(oakengine_ipc_framepool_slot_count(drainer_copy) == k_slots);
// Free slots are issued FIFO; the pool then reports exhausted.
uint32_t held[3];
for (uint32_t i = 0; i < k_slots; i++) {
assert(oakengine_ipc_framepool_acquire(filler, &held[i]) == 1);
assert(held[i] == i);
EXPECT_TRUE(oakengine_ipc_framepool_acquire(filler, &held[i]) == 1);
EXPECT_TRUE(held[i] == i);
}
uint32_t overflow = 99;
assert(oakengine_ipc_framepool_acquire(filler, &overflow) == 0);
assert(overflow == 99);
EXPECT_TRUE(oakengine_ipc_framepool_acquire(filler, &overflow) == 0);
EXPECT_TRUE(overflow == 99);
// Fill slot 0 with a pattern + full metadata and publish it.
unsigned char *data =
(unsigned char *)oakengine_ipc_framepool_slot_data(filler, held[0]);
assert(data != NULL);
EXPECT_TRUE(data != NULL);
for (size_t i = 0; i < k_slot_bytes; i++) {
data[i] = (unsigned char)(i & 0xFF);
}
oak_frame_slot_meta *meta = oakengine_ipc_framepool_meta(filler, held[0]);
assert(meta != NULL);
EXPECT_TRUE(meta != NULL);
meta->id = -4242;
meta->time_num = 1001;
meta->time_den = 30000;
@@ -208,48 +209,48 @@ static void test_framepool(void)
meta->data_size = (int32_t)k_slot_bytes;
strncpy(meta->colorspace, "acescg", sizeof(meta->colorspace) - 1);
meta->colorspace[sizeof(meta->colorspace) - 1] = '\0';
assert(oakengine_ipc_framepool_publish(filler, held[0]) == 1);
EXPECT_TRUE(oakengine_ipc_framepool_publish(filler, held[0]) == 1);
// Publish slot 2 then slot 1: consume order follows publish order.
assert(oakengine_ipc_framepool_publish(filler, held[2]) == 1);
assert(oakengine_ipc_framepool_publish(filler, held[1]) == 1);
EXPECT_TRUE(oakengine_ipc_framepool_publish(filler, held[2]) == 1);
EXPECT_TRUE(oakengine_ipc_framepool_publish(filler, held[1]) == 1);
uint32_t got = 99;
assert(oakengine_ipc_framepool_consume(drainer, &got) == 1);
assert(got == held[0]);
EXPECT_TRUE(oakengine_ipc_framepool_consume(drainer, &got) == 1);
EXPECT_TRUE(got == held[0]);
// Metadata and pixels arrive intact on the drainer side.
const oak_frame_slot_meta *out =
oakengine_ipc_framepool_meta_const(drainer, got);
assert(out != NULL);
assert(out->id == -4242);
assert(out->time_num == 1001 && out->time_den == 30000);
assert(out->width == 3840 && out->height == 2160);
assert(out->format == 17 && out->channel_count == 4);
assert(out->linesize == 3840 * 4 * 4);
assert(out->data_size == (int32_t)k_slot_bytes);
assert(strcmp(out->colorspace, "acescg") == 0);
EXPECT_TRUE(out != NULL);
EXPECT_TRUE(out->id == -4242);
EXPECT_TRUE(out->time_num == 1001 && out->time_den == 30000);
EXPECT_TRUE(out->width == 3840 && out->height == 2160);
EXPECT_TRUE(out->format == 17 && out->channel_count == 4);
EXPECT_TRUE(out->linesize == 3840 * 4 * 4);
EXPECT_TRUE(out->data_size == (int32_t)k_slot_bytes);
EXPECT_TRUE(strcmp(out->colorspace, "acescg") == 0);
const unsigned char *out_data =
(const unsigned char *)oakengine_ipc_framepool_slot_data_const(drainer,
got);
assert(out_data != NULL);
EXPECT_TRUE(out_data != NULL);
for (size_t i = 0; i < k_slot_bytes; i++) {
assert(out_data[i] == (unsigned char)(i & 0xFF));
EXPECT_TRUE(out_data[i] == (unsigned char)(i & 0xFF));
}
assert(oakengine_ipc_framepool_release(drainer, got) == 1);
EXPECT_TRUE(oakengine_ipc_framepool_release(drainer, got) == 1);
// The copied handle consumes the remaining slots in publish order.
assert(oakengine_ipc_framepool_consume(drainer_copy, &got) == 1);
assert(got == held[2]);
assert(oakengine_ipc_framepool_release(drainer_copy, got) == 1);
assert(oakengine_ipc_framepool_consume(drainer_copy, &got) == 1);
assert(got == held[1]);
assert(oakengine_ipc_framepool_release(drainer_copy, got) == 1);
assert(oakengine_ipc_framepool_consume(drainer, &got) == 0); // drained
EXPECT_TRUE(oakengine_ipc_framepool_consume(drainer_copy, &got) == 1);
EXPECT_TRUE(got == held[2]);
EXPECT_TRUE(oakengine_ipc_framepool_release(drainer_copy, got) == 1);
EXPECT_TRUE(oakengine_ipc_framepool_consume(drainer_copy, &got) == 1);
EXPECT_TRUE(got == held[1]);
EXPECT_TRUE(oakengine_ipc_framepool_release(drainer_copy, got) == 1);
EXPECT_TRUE(oakengine_ipc_framepool_consume(drainer, &got) == 0); // drained
// Released slots cycle back to the filler.
uint32_t again = 0;
assert(oakengine_ipc_framepool_acquire(filler, &again) == 1);
EXPECT_TRUE(oakengine_ipc_framepool_acquire(filler, &again) == 1);
oakengine_ipc_framepool_free(drainer_copy);
oakengine_ipc_framepool_free(drainer);
@@ -258,21 +259,21 @@ static void test_framepool(void)
// A region without the pool magic attaches as invalid.
void *raw = calloc(1, oakengine_ipc_framepool_bytes_needed(2, 64));
assert(raw != NULL);
EXPECT_TRUE(raw != NULL);
OakFrameSlotPool *bad = oakengine_ipc_framepool_attach(raw);
assert(bad != NULL);
assert(oakengine_ipc_framepool_is_valid(bad) == 0);
assert(oakengine_ipc_framepool_slot_count(bad) == 0);
assert(oakengine_ipc_framepool_slot_data_bytes(bad) == 0);
EXPECT_TRUE(bad != NULL);
EXPECT_TRUE(oakengine_ipc_framepool_is_valid(bad) == 0);
EXPECT_TRUE(oakengine_ipc_framepool_slot_count(bad) == 0);
EXPECT_TRUE(oakengine_ipc_framepool_slot_data_bytes(bad) == 0);
oakengine_ipc_framepool_free(bad);
free(raw);
// NULL safety.
assert(oakengine_ipc_framepool_is_valid(NULL) == 0);
assert(oakengine_ipc_framepool_slot_count(NULL) == 0);
assert(oakengine_ipc_framepool_slot_data(NULL, 0) == NULL);
assert(oakengine_ipc_framepool_meta(NULL, 0) == NULL);
assert(oakengine_ipc_framepool_acquire(NULL, &again) == 0);
EXPECT_TRUE(oakengine_ipc_framepool_is_valid(NULL) == 0);
EXPECT_TRUE(oakengine_ipc_framepool_slot_count(NULL) == 0);
EXPECT_TRUE(oakengine_ipc_framepool_slot_data(NULL, 0) == NULL);
EXPECT_TRUE(oakengine_ipc_framepool_meta(NULL, 0) == NULL);
EXPECT_TRUE(oakengine_ipc_framepool_acquire(NULL, &again) == 0);
oakengine_ipc_framepool_free(NULL);
}
@@ -280,7 +281,7 @@ static void test_framepool(void)
static void test_framepool_over_shm(void)
{
char key[OAK_IPC_SHM_KEY_CAP];
assert(oakengine_ipc_shm_make_key(getpid(), 7, key, sizeof(key)) > 0);
EXPECT_TRUE(oakengine_ipc_shm_make_key(getpid(), 7, key, sizeof(key)) > 0);
const uint32_t k_slots = 2;
const size_t k_slot_bytes = 64;
@@ -288,35 +289,35 @@ static void test_framepool_over_shm(void)
oakengine_ipc_framepool_bytes_needed(k_slots, k_slot_bytes);
OakSharedMemoryRegion *owner = oakengine_ipc_shm_create();
assert(oakengine_ipc_shm_open(owner, key, bytes, OAK_IPC_SHM_MODE_CREATE) ==
EXPECT_TRUE(oakengine_ipc_shm_open(owner, key, bytes, OAK_IPC_SHM_MODE_CREATE) ==
1);
OakSharedMemoryRegion *peer = oakengine_ipc_shm_create();
assert(oakengine_ipc_shm_open(peer, key, bytes, OAK_IPC_SHM_MODE_ATTACH) ==
EXPECT_TRUE(oakengine_ipc_shm_open(peer, key, bytes, OAK_IPC_SHM_MODE_ATTACH) ==
1);
OakFrameSlotPool *filler = oakengine_ipc_framepool_create(
oakengine_ipc_shm_data(owner), k_slots, k_slot_bytes);
OakFrameSlotPool *drainer = oakengine_ipc_framepool_attach(
oakengine_ipc_shm_data(peer));
assert(oakengine_ipc_framepool_is_valid(filler) == 1);
assert(oakengine_ipc_framepool_is_valid(drainer) == 1);
EXPECT_TRUE(oakengine_ipc_framepool_is_valid(filler) == 1);
EXPECT_TRUE(oakengine_ipc_framepool_is_valid(drainer) == 1);
uint32_t idx = 0;
assert(oakengine_ipc_framepool_acquire(filler, &idx) == 1);
EXPECT_TRUE(oakengine_ipc_framepool_acquire(filler, &idx) == 1);
oak_frame_slot_meta *meta = oakengine_ipc_framepool_meta(filler, idx);
meta->id = 77;
meta->data_size = (int32_t)k_slot_bytes;
memset(oakengine_ipc_framepool_slot_data(filler, idx), 0x5A, k_slot_bytes);
assert(oakengine_ipc_framepool_publish(filler, idx) == 1);
EXPECT_TRUE(oakengine_ipc_framepool_publish(filler, idx) == 1);
uint32_t got = 0;
assert(oakengine_ipc_framepool_consume(drainer, &got) == 1);
assert(got == idx);
assert(oakengine_ipc_framepool_meta_const(drainer, got)->id == 77);
EXPECT_TRUE(oakengine_ipc_framepool_consume(drainer, &got) == 1);
EXPECT_TRUE(got == idx);
EXPECT_TRUE(oakengine_ipc_framepool_meta_const(drainer, got)->id == 77);
const unsigned char *d = (const unsigned char *)
oakengine_ipc_framepool_slot_data_const(drainer, got);
assert(d[0] == 0x5A && d[k_slot_bytes - 1] == 0x5A);
assert(oakengine_ipc_framepool_release(drainer, got) == 1);
EXPECT_TRUE(d[0] == 0x5A && d[k_slot_bytes - 1] == 0x5A);
EXPECT_TRUE(oakengine_ipc_framepool_release(drainer, got) == 1);
oakengine_ipc_framepool_free(drainer);
oakengine_ipc_framepool_free(filler);
@@ -337,28 +338,28 @@ static void test_handshake(void)
hs.input_slot_data_bytes = 128ll * 1024 * 1024;
const int needed = oakengine_ipc_handshake_to_json(&hs, NULL, 0);
assert(needed > 0);
EXPECT_TRUE(needed > 0);
char *json = (char *)malloc(size_t(needed) + 1);
assert(json != NULL);
assert(oakengine_ipc_handshake_to_json(&hs, json, needed + 1) == needed);
assert((int)strlen(json) == needed);
EXPECT_TRUE(json != NULL);
EXPECT_TRUE(oakengine_ipc_handshake_to_json(&hs, json, needed + 1) == needed);
EXPECT_TRUE((int)strlen(json) == needed);
// buf/size convention: a short buffer truncates but reports the full size.
char tiny[8];
assert(oakengine_ipc_handshake_to_json(&hs, tiny, sizeof(tiny)) == needed);
assert(strlen(tiny) == sizeof(tiny) - 1);
EXPECT_TRUE(oakengine_ipc_handshake_to_json(&hs, tiny, sizeof(tiny)) == needed);
EXPECT_TRUE(strlen(tiny) == sizeof(tiny) - 1);
assert(oakengine_ipc_message_type(json) == OAK_IPC_MSGTYPE_HANDSHAKE);
EXPECT_TRUE(oakengine_ipc_message_type(json) == OAK_IPC_MSGTYPE_HANDSHAKE);
oak_ipc_handshake back;
assert(oakengine_ipc_handshake_parse(json, &back) == 1);
assert(back.protocol_version == hs.protocol_version);
assert(strcmp(back.shm_key, hs.shm_key) == 0);
assert(strcmp(back.input_shm_key, hs.input_shm_key) == 0);
assert(back.input_slots == hs.input_slots);
assert(back.output_slots == hs.output_slots);
assert(back.slot_data_bytes == hs.slot_data_bytes);
assert(back.input_slot_data_bytes == hs.input_slot_data_bytes);
EXPECT_TRUE(oakengine_ipc_handshake_parse(json, &back) == 1);
EXPECT_TRUE(back.protocol_version == hs.protocol_version);
EXPECT_TRUE(strcmp(back.shm_key, hs.shm_key) == 0);
EXPECT_TRUE(strcmp(back.input_shm_key, hs.input_shm_key) == 0);
EXPECT_TRUE(back.input_slots == hs.input_slots);
EXPECT_TRUE(back.output_slots == hs.output_slots);
EXPECT_TRUE(back.slot_data_bytes == hs.slot_data_bytes);
EXPECT_TRUE(back.input_slot_data_bytes == hs.input_slot_data_bytes);
free(json);
// Exact wire text: same field names and compact shape the Qt builder emits.
@@ -373,8 +374,8 @@ static void test_handshake(void)
small.input_slot_data_bytes = 128;
const int n2 = oakengine_ipc_handshake_to_json(&small, NULL, 0);
char *json2 = (char *)malloc(size_t(n2) + 1);
assert(json2 != NULL);
assert(oakengine_ipc_handshake_to_json(&small, json2, n2 + 1) == n2);
EXPECT_TRUE(json2 != NULL);
EXPECT_TRUE(oakengine_ipc_handshake_to_json(&small, json2, n2 + 1) == n2);
assert_json_eq(json2,
"{\"input_shm_key\":\"in\",\"input_slot_data_bytes\":128,"
"\"input_slots\":4,\"output_slots\":6,\"protocol_version\":1,"
@@ -382,9 +383,9 @@ static void test_handshake(void)
"\"type\":\"handshake\"}");
free(json2);
assert(oakengine_ipc_handshake_to_json(NULL, NULL, 0) == -1);
assert(oakengine_ipc_handshake_parse("not json", &back) == 0);
assert(oakengine_ipc_handshake_parse(NULL, &back) == 0);
EXPECT_TRUE(oakengine_ipc_handshake_to_json(NULL, NULL, 0) == -1);
EXPECT_TRUE(oakengine_ipc_handshake_parse("not json", &back) == 0);
EXPECT_TRUE(oakengine_ipc_handshake_parse(NULL, &back) == 0);
}
static void test_render_frame(void)
@@ -412,26 +413,26 @@ static void test_render_frame(void)
const int needed = oakengine_ipc_render_frame_to_json(&rf, NULL, 0);
char *json = (char *)malloc(size_t(needed) + 1);
assert(json != NULL);
assert(oakengine_ipc_render_frame_to_json(&rf, json, needed + 1) == needed);
assert(oakengine_ipc_message_type(json) == OAK_IPC_MSGTYPE_RENDER_FRAME);
EXPECT_TRUE(json != NULL);
EXPECT_TRUE(oakengine_ipc_render_frame_to_json(&rf, json, needed + 1) == needed);
EXPECT_TRUE(oakengine_ipc_message_type(json) == OAK_IPC_MSGTYPE_RENDER_FRAME);
oak_ipc_render_frame back;
assert(oakengine_ipc_render_frame_parse(json, &back) == 1);
assert(back.ticket_id == rf.ticket_id);
assert(strcmp(back.node_uuid, rf.node_uuid) == 0);
assert(back.time_num == rf.time_num);
assert(back.time_den == rf.time_den);
assert(back.width == rf.width && back.height == rf.height);
assert(back.format == rf.format && back.channel_count == rf.channel_count);
assert(back.mode == rf.mode);
assert(back.input_slot == rf.input_slot);
assert(back.input_slot_count == 2);
assert(back.input_slots[0] == 2 && back.input_slots[1] == 3);
assert(back.has_color_transform == 1 && back.color_is_display == 1);
assert(strcmp(back.color_output, rf.color_output) == 0);
assert(strcmp(back.color_view, rf.color_view) == 0);
assert(strcmp(back.color_look, rf.color_look) == 0);
EXPECT_TRUE(oakengine_ipc_render_frame_parse(json, &back) == 1);
EXPECT_TRUE(back.ticket_id == rf.ticket_id);
EXPECT_TRUE(strcmp(back.node_uuid, rf.node_uuid) == 0);
EXPECT_TRUE(back.time_num == rf.time_num);
EXPECT_TRUE(back.time_den == rf.time_den);
EXPECT_TRUE(back.width == rf.width && back.height == rf.height);
EXPECT_TRUE(back.format == rf.format && back.channel_count == rf.channel_count);
EXPECT_TRUE(back.mode == rf.mode);
EXPECT_TRUE(back.input_slot == rf.input_slot);
EXPECT_TRUE(back.input_slot_count == 2);
EXPECT_TRUE(back.input_slots[0] == 2 && back.input_slots[1] == 3);
EXPECT_TRUE(back.has_color_transform == 1 && back.color_is_display == 1);
EXPECT_TRUE(strcmp(back.color_output, rf.color_output) == 0);
EXPECT_TRUE(strcmp(back.color_view, rf.color_view) == 0);
EXPECT_TRUE(strcmp(back.color_look, rf.color_look) == 0);
free(json);
// Without a color transform the color keys are omitted from the wire text.
@@ -452,9 +453,9 @@ static void test_render_frame(void)
plain.input_slot_count = 2;
const int n2 = oakengine_ipc_render_frame_to_json(&plain, NULL, 0);
char *json2 = (char *)malloc(size_t(n2) + 1);
assert(json2 != NULL);
assert(oakengine_ipc_render_frame_to_json(&plain, json2, n2 + 1) == n2);
assert(strstr(json2, "has_color_transform") == NULL);
EXPECT_TRUE(json2 != NULL);
EXPECT_TRUE(oakengine_ipc_render_frame_to_json(&plain, json2, n2 + 1) == n2);
EXPECT_TRUE(strstr(json2, "has_color_transform") == NULL);
assert_json_eq(json2,
"{\"channels\":4,\"format\":3,\"height\":1080,"
"\"input_slot\":2,\"input_slots\":[2,3],\"mode\":1,"
@@ -464,32 +465,32 @@ static void test_render_frame(void)
// Defaults for a sparse message, mirroring the Qt from_json defaults.
oak_ipc_render_frame sparse;
assert(oakengine_ipc_render_frame_parse("{\"type\":\"render_frame\"}",
EXPECT_TRUE(oakengine_ipc_render_frame_parse("{\"type\":\"render_frame\"}",
&sparse) == 1);
assert(sparse.ticket_id == 0);
assert(sparse.time_num == 0 && sparse.time_den == 1);
assert(sparse.width == 0 && sparse.height == 0);
assert(sparse.format == -1 && sparse.channel_count == 0 && sparse.mode == 0);
assert(sparse.input_slot == -1 && sparse.input_slot_count == 0);
assert(sparse.has_color_transform == 0);
EXPECT_TRUE(sparse.ticket_id == 0);
EXPECT_TRUE(sparse.time_num == 0 && sparse.time_den == 1);
EXPECT_TRUE(sparse.width == 0 && sparse.height == 0);
EXPECT_TRUE(sparse.format == -1 && sparse.channel_count == 0 && sparse.mode == 0);
EXPECT_TRUE(sparse.input_slot == -1 && sparse.input_slot_count == 0);
EXPECT_TRUE(sparse.has_color_transform == 0);
// Legacy scalar input_slot folds into the array when it is absent.
oak_ipc_render_frame legacy;
assert(oakengine_ipc_render_frame_parse(
EXPECT_TRUE(oakengine_ipc_render_frame_parse(
"{\"type\":\"render_frame\",\"ticket\":5,\"input_slot\":3}",
&legacy) == 1);
assert(legacy.input_slot == 3);
assert(legacy.input_slot_count == 1 && legacy.input_slots[0] == 3);
EXPECT_TRUE(legacy.input_slot == 3);
EXPECT_TRUE(legacy.input_slot_count == 1 && legacy.input_slots[0] == 3);
// Type mismatches are rejected.
oak_ipc_handshake hs;
memset(&hs, 0, sizeof(hs));
const int hn = oakengine_ipc_handshake_to_json(&hs, NULL, 0);
char *hjson = (char *)malloc(size_t(hn) + 1);
assert(hjson != NULL);
assert(oakengine_ipc_handshake_to_json(&hs, hjson, hn + 1) == hn);
assert(oakengine_ipc_render_frame_parse(hjson, &back) == 0);
assert(oakengine_ipc_handshake_parse(hjson, &hs) == 1);
EXPECT_TRUE(hjson != NULL);
EXPECT_TRUE(oakengine_ipc_handshake_to_json(&hs, hjson, hn + 1) == hn);
EXPECT_TRUE(oakengine_ipc_render_frame_parse(hjson, &back) == 0);
EXPECT_TRUE(oakengine_ipc_handshake_parse(hjson, &hs) == 1);
free(hjson);
}
@@ -501,13 +502,13 @@ static void test_small_messages(void)
fr.output_slot = 2;
const int fn = oakengine_ipc_frame_ready_to_json(&fr, NULL, 0);
char *fjson = (char *)malloc(size_t(fn) + 1);
assert(fjson != NULL);
assert(oakengine_ipc_frame_ready_to_json(&fr, fjson, fn + 1) == fn);
EXPECT_TRUE(fjson != NULL);
EXPECT_TRUE(oakengine_ipc_frame_ready_to_json(&fr, fjson, fn + 1) == fn);
assert_json_eq(fjson, "{\"slot\":2,\"ticket\":99,\"type\":\"frame_ready\"}");
assert(oakengine_ipc_message_type(fjson) == OAK_IPC_MSGTYPE_FRAME_READY);
EXPECT_TRUE(oakengine_ipc_message_type(fjson) == OAK_IPC_MSGTYPE_FRAME_READY);
oak_ipc_frame_ready fr_back;
assert(oakengine_ipc_frame_ready_parse(fjson, &fr_back) == 1);
assert(fr_back.ticket_id == 99 && fr_back.output_slot == 2);
EXPECT_TRUE(oakengine_ipc_frame_ready_parse(fjson, &fr_back) == 1);
EXPECT_TRUE(fr_back.ticket_id == 99 && fr_back.output_slot == 2);
free(fjson);
// cancel
@@ -515,15 +516,15 @@ static void test_small_messages(void)
cancel.ticket_id = 7;
const int cn = oakengine_ipc_cancel_to_json(&cancel, NULL, 0);
char *cjson = (char *)malloc(size_t(cn) + 1);
assert(cjson != NULL);
assert(oakengine_ipc_cancel_to_json(&cancel, cjson, cn + 1) == cn);
EXPECT_TRUE(cjson != NULL);
EXPECT_TRUE(oakengine_ipc_cancel_to_json(&cancel, cjson, cn + 1) == cn);
assert_json_eq(cjson, "{\"ticket\":7,\"type\":\"cancel\"}");
assert(oakengine_ipc_message_type(cjson) == OAK_IPC_MSGTYPE_CANCEL);
EXPECT_TRUE(oakengine_ipc_message_type(cjson) == OAK_IPC_MSGTYPE_CANCEL);
oak_ipc_cancel cancel_back;
assert(oakengine_ipc_cancel_parse(cjson, &cancel_back) == 1);
assert(cancel_back.ticket_id == 7);
EXPECT_TRUE(oakengine_ipc_cancel_parse(cjson, &cancel_back) == 1);
EXPECT_TRUE(cancel_back.ticket_id == 7);
// cancel rejects a frame_ready payload.
assert(oakengine_ipc_cancel_parse("{\"slot\":2,\"ticket\":7,\"type\":"
EXPECT_TRUE(oakengine_ipc_cancel_parse("{\"slot\":2,\"ticket\":7,\"type\":"
"\"frame_ready\"}",
&cancel_back) == 0);
free(cjson);
@@ -533,51 +534,51 @@ static void test_small_messages(void)
strcpy(load.path, "/tmp/oak-render-graph-abc123.ove");
const int ln = oakengine_ipc_load_graph_to_json(&load, NULL, 0);
char *ljson = (char *)malloc(size_t(ln) + 1);
assert(ljson != NULL);
assert(oakengine_ipc_load_graph_to_json(&load, ljson, ln + 1) == ln);
EXPECT_TRUE(ljson != NULL);
EXPECT_TRUE(oakengine_ipc_load_graph_to_json(&load, ljson, ln + 1) == ln);
assert_json_eq(ljson,
"{\"path\":\"/tmp/oak-render-graph-abc123.ove\",\"type\":"
"\"load_graph\"}");
assert(oakengine_ipc_message_type(ljson) == OAK_IPC_MSGTYPE_LOAD_GRAPH);
EXPECT_TRUE(oakengine_ipc_message_type(ljson) == OAK_IPC_MSGTYPE_LOAD_GRAPH);
oak_ipc_load_graph load_back;
assert(oakengine_ipc_load_graph_parse(ljson, &load_back) == 1);
assert(strcmp(load_back.path, load.path) == 0);
EXPECT_TRUE(oakengine_ipc_load_graph_parse(ljson, &load_back) == 1);
EXPECT_TRUE(strcmp(load_back.path, load.path) == 0);
free(ljson);
// shutdown
const int sn = oakengine_ipc_shutdown_to_json(NULL, 0);
char *sjson = (char *)malloc(size_t(sn) + 1);
assert(sjson != NULL);
assert(oakengine_ipc_shutdown_to_json(sjson, sn + 1) == sn);
EXPECT_TRUE(sjson != NULL);
EXPECT_TRUE(oakengine_ipc_shutdown_to_json(sjson, sn + 1) == sn);
assert_json_eq(sjson, "{\"type\":\"shutdown\"}");
assert(oakengine_ipc_shutdown_parse(sjson) == 1);
assert(oakengine_ipc_shutdown_parse("{\"type\":\"cancel\"}") == 0);
assert(oakengine_ipc_message_type(sjson) == OAK_IPC_MSGTYPE_SHUTDOWN);
EXPECT_TRUE(oakengine_ipc_shutdown_parse(sjson) == 1);
EXPECT_TRUE(oakengine_ipc_shutdown_parse("{\"type\":\"cancel\"}") == 0);
EXPECT_TRUE(oakengine_ipc_message_type(sjson) == OAK_IPC_MSGTYPE_SHUTDOWN);
free(sjson);
// error
const int en = oakengine_ipc_error_to_json("boom", NULL, 0);
char *ejson = (char *)malloc(size_t(en) + 1);
assert(ejson != NULL);
assert(oakengine_ipc_error_to_json("boom", ejson, en + 1) == en);
EXPECT_TRUE(ejson != NULL);
EXPECT_TRUE(oakengine_ipc_error_to_json("boom", ejson, en + 1) == en);
assert_json_eq(ejson, "{\"message\":\"boom\",\"type\":\"error\"}");
assert(oakengine_ipc_message_type(ejson) == OAK_IPC_MSGTYPE_ERROR);
EXPECT_TRUE(oakengine_ipc_message_type(ejson) == OAK_IPC_MSGTYPE_ERROR);
char msg[OAK_IPC_ERROR_MESSAGE_CAP];
assert(oakengine_ipc_error_parse(ejson, msg, sizeof(msg)) == 1);
assert(strcmp(msg, "boom") == 0);
assert(oakengine_ipc_error_parse("{\"type\":\"shutdown\"}", msg,
EXPECT_TRUE(oakengine_ipc_error_parse(ejson, msg, sizeof(msg)) == 1);
EXPECT_TRUE(strcmp(msg, "boom") == 0);
EXPECT_TRUE(oakengine_ipc_error_parse("{\"type\":\"shutdown\"}", msg,
sizeof(msg)) == 0);
free(ejson);
// Unknown and malformed input.
assert(oakengine_ipc_message_type("{\"type\":\"nope\"}") ==
EXPECT_TRUE(oakengine_ipc_message_type("{\"type\":\"nope\"}") ==
OAK_IPC_MSGTYPE_UNKNOWN);
assert(oakengine_ipc_message_type("garbage") == OAK_IPC_MSGTYPE_UNKNOWN);
assert(oakengine_ipc_message_type(NULL) == OAK_IPC_MSGTYPE_UNKNOWN);
assert(OAK_IPC_MSGTYPE_GRAPH_UPDATE != OAK_IPC_MSGTYPE_UNKNOWN);
EXPECT_TRUE(oakengine_ipc_message_type("garbage") == OAK_IPC_MSGTYPE_UNKNOWN);
EXPECT_TRUE(oakengine_ipc_message_type(NULL) == OAK_IPC_MSGTYPE_UNKNOWN);
EXPECT_TRUE(OAK_IPC_MSGTYPE_GRAPH_UPDATE != OAK_IPC_MSGTYPE_UNKNOWN);
}
int main()
TEST(OakEngineIpc, Main)
{
test_shm();
test_framepool();
@@ -586,6 +587,4 @@ int main()
test_render_frame();
test_small_messages();
printf("oakengine_ipc_test: all assertions passed\n");
return 0;
}
File diff suppressed because it is too large Load Diff
+16 -16
View File
@@ -22,6 +22,7 @@
// Runs headless; no GPU required.
#include <assert.h>
#include <gtest/gtest.h>
#include <stdio.h>
#include <string.h>
@@ -30,40 +31,39 @@
static void test_counts_after_init(void)
{
assert(oakengine_lut_directory_count() >= 0);
assert(oakengine_lut_file_count() >= 0);
EXPECT_TRUE(oakengine_lut_directory_count() >= 0);
EXPECT_TRUE(oakengine_lut_file_count() >= 0);
// Out-of-range index returns an error.
char buf[256];
assert(oakengine_lut_directory_at(-1, buf, sizeof(buf)) < 0);
assert(oakengine_lut_file_at(-1, buf, sizeof(buf)) < 0);
EXPECT_TRUE(oakengine_lut_directory_at(-1, buf, sizeof(buf)) < 0);
EXPECT_TRUE(oakengine_lut_file_at(-1, buf, sizeof(buf)) < 0);
}
static void test_set_directories_round_trip(void)
{
const char *dirs[] = { "/tmp/oak_lut_a", "/tmp/oak_lut_b" };
assert(oakengine_lut_set_directories(dirs, 2) == OAKENGINE_OK);
assert(oakengine_lut_directory_count() == 2);
EXPECT_TRUE(oakengine_lut_set_directories(dirs, 2) == OAKENGINE_OK);
EXPECT_TRUE(oakengine_lut_directory_count() == 2);
char buf[256];
assert(oakengine_lut_directory_at(0, buf, sizeof(buf)) > 0);
assert(strcmp(buf, "/tmp/oak_lut_a") == 0);
assert(oakengine_lut_directory_at(1, buf, sizeof(buf)) > 0);
assert(strcmp(buf, "/tmp/oak_lut_b") == 0);
EXPECT_TRUE(oakengine_lut_directory_at(0, buf, sizeof(buf)) > 0);
EXPECT_TRUE(strcmp(buf, "/tmp/oak_lut_a") == 0);
EXPECT_TRUE(oakengine_lut_directory_at(1, buf, sizeof(buf)) > 0);
EXPECT_TRUE(strcmp(buf, "/tmp/oak_lut_b") == 0);
// Clearing the library.
assert(oakengine_lut_set_directories(NULL, 0) == OAKENGINE_OK);
assert(oakengine_lut_directory_count() == 0);
EXPECT_TRUE(oakengine_lut_set_directories(NULL, 0) == OAKENGINE_OK);
EXPECT_TRUE(oakengine_lut_directory_count() == 0);
}
int main(void)
TEST(OakEngineLut, Main)
{
assert(oakengine_init(OAKENGINE_INIT_HEADLESS) == OAKENGINE_OK);
EXPECT_TRUE(oakengine_init(OAKENGINE_INIT_HEADLESS) == OAKENGINE_OK);
test_counts_after_init();
test_set_directories_round_trip();
assert(oakengine_shutdown() == OAKENGINE_OK);
return 0;
EXPECT_TRUE(oakengine_shutdown() == OAKENGINE_OK);
}
File diff suppressed because it is too large Load Diff
+34 -34
View File
@@ -25,6 +25,7 @@
// No engine init required: these wrap pure NodeValue statics.
#include <assert.h>
#include <gtest/gtest.h>
#include <math.h>
#include <string.h>
@@ -32,15 +33,15 @@
static void test_track_count(void)
{
assert(oakengine_node_value_keyframe_track_count(OAK_NODE_VALUE_INT) ==
EXPECT_TRUE(oakengine_node_value_keyframe_track_count(OAK_NODE_VALUE_INT) ==
1);
assert(oakengine_node_value_keyframe_track_count(OAK_NODE_VALUE_FLOAT) ==
EXPECT_TRUE(oakengine_node_value_keyframe_track_count(OAK_NODE_VALUE_FLOAT) ==
1);
assert(oakengine_node_value_keyframe_track_count(OAK_NODE_VALUE_VEC2) ==
EXPECT_TRUE(oakengine_node_value_keyframe_track_count(OAK_NODE_VALUE_VEC2) ==
2);
assert(oakengine_node_value_keyframe_track_count(OAK_NODE_VALUE_VEC3) ==
EXPECT_TRUE(oakengine_node_value_keyframe_track_count(OAK_NODE_VALUE_VEC3) ==
3);
assert(oakengine_node_value_keyframe_track_count(OAK_NODE_VALUE_VEC4) ==
EXPECT_TRUE(oakengine_node_value_keyframe_track_count(OAK_NODE_VALUE_VEC4) ==
4);
}
@@ -48,18 +49,18 @@ static void test_pretty_name(void)
{
char buf[64];
assert(oakengine_node_value_pretty_type_name(OAK_NODE_VALUE_INT, buf,
EXPECT_TRUE(oakengine_node_value_pretty_type_name(OAK_NODE_VALUE_INT, buf,
sizeof(buf)) > 0);
assert(buf[0] != '\0');
EXPECT_TRUE(buf[0] != '\0');
/* two-phase: query length first */
const int len =
oakengine_node_value_pretty_type_name(OAK_NODE_VALUE_FLOAT, nullptr,
0);
assert(len > 0);
EXPECT_TRUE(len > 0);
/* unknown type reports -1 */
assert(oakengine_node_value_pretty_type_name(9999, buf, sizeof(buf)) ==
EXPECT_TRUE(oakengine_node_value_pretty_type_name(9999, buf, sizeof(buf)) ==
-1);
}
@@ -72,17 +73,17 @@ static void test_split_combine_vec3(void)
normal.f[2] = 3.0;
oak_node_value tracks[3] = {{0}};
assert(oakengine_node_value_split_to_tracks(OAK_NODE_VALUE_VEC3, &normal,
EXPECT_TRUE(oakengine_node_value_split_to_tracks(OAK_NODE_VALUE_VEC3, &normal,
tracks, 3) == OAKENGINE_OK);
assert(tracks[0].f[0] == 1.0);
assert(tracks[1].f[0] == 2.0);
assert(tracks[2].f[0] == 3.0);
EXPECT_TRUE(tracks[0].f[0] == 1.0);
EXPECT_TRUE(tracks[1].f[0] == 2.0);
EXPECT_TRUE(tracks[2].f[0] == 3.0);
oak_node_value back = {0};
assert(oakengine_node_value_combine_tracks(OAK_NODE_VALUE_VEC3, tracks,
EXPECT_TRUE(oakengine_node_value_combine_tracks(OAK_NODE_VALUE_VEC3, tracks,
3, &back) == OAKENGINE_OK);
assert(back.type == OAK_NODE_VALUE_VEC3);
assert(back.f[0] == 1.0 && back.f[1] == 2.0 && back.f[2] == 3.0);
EXPECT_TRUE(back.type == OAK_NODE_VALUE_VEC3);
EXPECT_TRUE(back.f[0] == 1.0 && back.f[1] == 2.0 && back.f[2] == 3.0);
}
static void test_split_combine_int(void)
@@ -92,17 +93,17 @@ static void test_split_combine_int(void)
normal.num = 42;
oak_node_value track = {0};
assert(oakengine_node_value_split_to_tracks(OAK_NODE_VALUE_INT, &normal,
EXPECT_TRUE(oakengine_node_value_split_to_tracks(OAK_NODE_VALUE_INT, &normal,
&track, 1) == OAKENGINE_OK);
/* scalar fields must survive the roundtrip (num, not only f[0]) */
assert(track.type == OAK_NODE_VALUE_INT);
assert(track.num == 42);
EXPECT_TRUE(track.type == OAK_NODE_VALUE_INT);
EXPECT_TRUE(track.num == 42);
oak_node_value back = {0};
assert(oakengine_node_value_combine_tracks(OAK_NODE_VALUE_INT, &track, 1,
EXPECT_TRUE(oakengine_node_value_combine_tracks(OAK_NODE_VALUE_INT, &track, 1,
&back) == OAKENGINE_OK);
assert(back.type == OAK_NODE_VALUE_INT);
assert(back.num == 42);
EXPECT_TRUE(back.type == OAK_NODE_VALUE_INT);
EXPECT_TRUE(back.num == 42);
}
static void test_split_combine_rational(void)
@@ -113,38 +114,38 @@ static void test_split_combine_rational(void)
normal.den = 1001;
oak_node_value track = {0};
assert(oakengine_node_value_split_to_tracks(OAK_NODE_VALUE_RATIONAL,
EXPECT_TRUE(oakengine_node_value_split_to_tracks(OAK_NODE_VALUE_RATIONAL,
&normal, &track,
1) == OAKENGINE_OK);
assert(track.type == OAK_NODE_VALUE_RATIONAL);
assert(track.num == 30000 && track.den == 1001);
EXPECT_TRUE(track.type == OAK_NODE_VALUE_RATIONAL);
EXPECT_TRUE(track.num == 30000 && track.den == 1001);
oak_node_value back = {0};
assert(oakengine_node_value_combine_tracks(OAK_NODE_VALUE_RATIONAL,
EXPECT_TRUE(oakengine_node_value_combine_tracks(OAK_NODE_VALUE_RATIONAL,
&track, 1,
&back) == OAKENGINE_OK);
assert(back.num == 30000 && back.den == 1001);
EXPECT_TRUE(back.num == 30000 && back.den == 1001);
}
static void test_error_paths(void)
{
oak_node_value v = {0};
assert(oakengine_node_value_split_to_tracks(OAK_NODE_VALUE_VEC3, nullptr,
EXPECT_TRUE(oakengine_node_value_split_to_tracks(OAK_NODE_VALUE_VEC3, nullptr,
&v, 1) == OAKENGINE_E_INVALID);
assert(oakengine_node_value_split_to_tracks(OAK_NODE_VALUE_VEC3, &v,
EXPECT_TRUE(oakengine_node_value_split_to_tracks(OAK_NODE_VALUE_VEC3, &v,
nullptr,
1) == OAKENGINE_E_INVALID);
assert(oakengine_node_value_split_to_tracks(OAK_NODE_VALUE_VEC3, &v, &v,
EXPECT_TRUE(oakengine_node_value_split_to_tracks(OAK_NODE_VALUE_VEC3, &v, &v,
0) == OAKENGINE_E_INVALID);
assert(oakengine_node_value_combine_tracks(OAK_NODE_VALUE_VEC3, nullptr,
EXPECT_TRUE(oakengine_node_value_combine_tracks(OAK_NODE_VALUE_VEC3, nullptr,
1, &v) == OAKENGINE_E_INVALID);
assert(oakengine_node_value_combine_tracks(OAK_NODE_VALUE_VEC3, &v, 1,
EXPECT_TRUE(oakengine_node_value_combine_tracks(OAK_NODE_VALUE_VEC3, &v, 1,
nullptr) ==
OAKENGINE_E_INVALID);
}
int main(void)
TEST(OakEngineNodevalue, Main)
{
test_track_count();
test_pretty_name();
@@ -152,5 +153,4 @@ int main(void)
test_split_combine_int();
test_split_combine_rational();
test_error_paths();
return 0;
}
+106 -107
View File
@@ -33,6 +33,7 @@
// under a mutex (the documented marshalling duty of the consumer).
#include <assert.h>
#include <gtest/gtest.h>
#include <math.h>
#include <stdint.h>
#include <stdio.h>
@@ -83,13 +84,13 @@ static void make_tmpdir(void)
#if defined(_WIN32)
char base[MAX_PATH];
const DWORD len = GetTempPathA(MAX_PATH, base);
assert(len > 0 && len < MAX_PATH);
EXPECT_TRUE(len > 0 && len < MAX_PATH);
snprintf(g_tmpdir, sizeof(g_tmpdir), "%soakengine_playback_test_%lu", base,
(unsigned long)GetCurrentProcessId());
assert(_mkdir(g_tmpdir) == 0);
EXPECT_TRUE(_mkdir(g_tmpdir) == 0);
#else
strcpy(g_tmpdir, "/tmp/oakengine_playback_test_XXXXXX");
assert(mkdtemp(g_tmpdir) != NULL);
EXPECT_TRUE(mkdtemp(g_tmpdir) != NULL);
#endif
}
@@ -237,79 +238,79 @@ static bool not_playing(void *userdata)
// Argument validation, idempotent pause/stop, initial position. No GL.
static void test_validation(OakEngineSequence *seq)
{
assert(oakengine_playback_create(NULL, 320, 180, 24, 1) == NULL);
assert(oakengine_playback_create(seq, 0, 180, 24, 1) == NULL);
assert(oakengine_playback_create(seq, 320, -1, 24, 1) == NULL);
assert(oakengine_playback_create(seq, 320, 180, 0, 1) == NULL);
assert(oakengine_playback_create(seq, 320, 180, 24, 0) == NULL);
EXPECT_TRUE(oakengine_playback_create(NULL, 320, 180, 24, 1) == NULL);
EXPECT_TRUE(oakengine_playback_create(seq, 0, 180, 24, 1) == NULL);
EXPECT_TRUE(oakengine_playback_create(seq, 320, -1, 24, 1) == NULL);
EXPECT_TRUE(oakengine_playback_create(seq, 320, 180, 0, 1) == NULL);
EXPECT_TRUE(oakengine_playback_create(seq, 320, 180, 24, 0) == NULL);
OakEnginePlayback *p = oakengine_playback_create(seq, 320, 180, 24, 1);
assert(p != NULL);
EXPECT_TRUE(p != NULL);
// Callback setters: NULL-safe, idempotent.
assert(oakengine_playback_set_frame_callback(NULL, on_frame, NULL) ==
EXPECT_TRUE(oakengine_playback_set_frame_callback(NULL, on_frame, NULL) ==
OAKENGINE_E_INVALID);
assert(oakengine_playback_set_frame_callback(p, NULL, NULL) ==
EXPECT_TRUE(oakengine_playback_set_frame_callback(p, NULL, NULL) ==
OAKENGINE_OK);
assert(oakengine_playback_set_audio_callback(NULL, on_audio, NULL) ==
EXPECT_TRUE(oakengine_playback_set_audio_callback(NULL, on_audio, NULL) ==
OAKENGINE_E_INVALID);
assert(oakengine_playback_set_audio_callback(p, NULL, NULL) ==
EXPECT_TRUE(oakengine_playback_set_audio_callback(p, NULL, NULL) ==
OAKENGINE_OK);
// Position is 0 before the first start; pause/stop are idempotent.
int64_t pos = -1;
assert(oakengine_playback_get_position(p, &pos) == OAKENGINE_OK);
assert(pos == 0);
assert(oakengine_playback_get_position(NULL, &pos) ==
EXPECT_TRUE(oakengine_playback_get_position(p, &pos) == OAKENGINE_OK);
EXPECT_TRUE(pos == 0);
EXPECT_TRUE(oakengine_playback_get_position(NULL, &pos) ==
OAKENGINE_E_INVALID);
assert(oakengine_playback_get_position(p, NULL) == OAKENGINE_E_INVALID);
assert(oakengine_playback_is_playing(p) == 0);
assert(oakengine_playback_is_playing(NULL) == 0);
assert(oakengine_playback_pause(p) == OAKENGINE_OK);
assert(oakengine_playback_stop(p) == OAKENGINE_OK);
assert(oakengine_playback_is_playing(p) == 0);
EXPECT_TRUE(oakengine_playback_get_position(p, NULL) == OAKENGINE_E_INVALID);
EXPECT_TRUE(oakengine_playback_is_playing(p) == 0);
EXPECT_TRUE(oakengine_playback_is_playing(NULL) == 0);
EXPECT_TRUE(oakengine_playback_pause(p) == OAKENGINE_OK);
EXPECT_TRUE(oakengine_playback_stop(p) == OAKENGINE_OK);
EXPECT_TRUE(oakengine_playback_is_playing(p) == 0);
// Bad speeds are rejected with a readable reason; start without the
// RENDER bit reports OAKENGINE_E_STATE.
assert(oakengine_playback_set_speed(p, 0.0) == OAKENGINE_E_INVALID);
assert(oakengine_playback_set_speed(p, -1.0) == OAKENGINE_E_INVALID);
assert(oakengine_playback_set_speed(NULL, 1.0) == OAKENGINE_E_INVALID);
EXPECT_TRUE(oakengine_playback_set_speed(p, 0.0) == OAKENGINE_E_INVALID);
EXPECT_TRUE(oakengine_playback_set_speed(p, -1.0) == OAKENGINE_E_INVALID);
EXPECT_TRUE(oakengine_playback_set_speed(NULL, 1.0) == OAKENGINE_E_INVALID);
char err[256];
assert(oakengine_playback_last_error(p, err, sizeof(err)) > 0);
assert(oakengine_playback_start(p, 0, 0.0) == OAKENGINE_E_INVALID);
assert(oakengine_playback_start(p, 0, -2.0) == OAKENGINE_E_INVALID);
assert(oakengine_playback_start(p, -1, 1.0) == OAKENGINE_E_INVALID);
assert(oakengine_playback_start(p, 0, 1.0) == OAKENGINE_E_STATE);
assert(strstr(err, "OAKENGINE_INIT_RENDER") != NULL ||
EXPECT_TRUE(oakengine_playback_last_error(p, err, sizeof(err)) > 0);
EXPECT_TRUE(oakengine_playback_start(p, 0, 0.0) == OAKENGINE_E_INVALID);
EXPECT_TRUE(oakengine_playback_start(p, 0, -2.0) == OAKENGINE_E_INVALID);
EXPECT_TRUE(oakengine_playback_start(p, -1, 1.0) == OAKENGINE_E_INVALID);
EXPECT_TRUE(oakengine_playback_start(p, 0, 1.0) == OAKENGINE_E_STATE);
EXPECT_TRUE(strstr(err, "OAKENGINE_INIT_RENDER") != NULL ||
oakengine_playback_last_error(p, err, sizeof(err)) > 0);
assert(oakengine_playback_start(NULL, 0, 1.0) == OAKENGINE_E_INVALID);
assert(oakengine_playback_pause(NULL) == OAKENGINE_E_INVALID);
assert(oakengine_playback_stop(NULL) == OAKENGINE_E_INVALID);
EXPECT_TRUE(oakengine_playback_start(NULL, 0, 1.0) == OAKENGINE_E_INVALID);
EXPECT_TRUE(oakengine_playback_pause(NULL) == OAKENGINE_E_INVALID);
EXPECT_TRUE(oakengine_playback_stop(NULL) == OAKENGINE_E_INVALID);
oakengine_playback_free(p);
oakengine_playback_free(NULL);
}
int main(void)
TEST(OakEnginePlayback, Main)
{
make_tmpdir();
// Sandbox the config/cache/data locations (see oakengine_init_test).
#if !defined(_WIN32)
assert(setenv("XDG_CONFIG_HOME", g_tmpdir, 1) == 0);
assert(setenv("XDG_CACHE_HOME", g_tmpdir, 1) == 0);
assert(setenv("XDG_DATA_HOME", g_tmpdir, 1) == 0);
EXPECT_TRUE(setenv("XDG_CONFIG_HOME", g_tmpdir, 1) == 0);
EXPECT_TRUE(setenv("XDG_CACHE_HOME", g_tmpdir, 1) == 0);
EXPECT_TRUE(setenv("XDG_DATA_HOME", g_tmpdir, 1) == 0);
#endif
// HEADLESS is enough for the validation part and creates the
// application object the backend probe below depends on.
assert(oakengine_init(OAKENGINE_INIT_HEADLESS) == OAKENGINE_OK);
EXPECT_TRUE(oakengine_init(OAKENGINE_INIT_HEADLESS) == OAKENGINE_OK);
OakEngineProject *project = oakengine_project_create();
assert(project != NULL);
assert(oakengine_project_new(project) == OAKENGINE_OK);
EXPECT_TRUE(project != NULL);
EXPECT_TRUE(oakengine_project_new(project) == OAKENGINE_OK);
OakEngineSequence *seq = oakengine_sequence_new(project, "Playback");
assert(seq != NULL);
EXPECT_TRUE(seq != NULL);
test_validation(seq);
@@ -319,20 +320,20 @@ int main(void)
"available, playback assertions skipped\n");
oakengine_project_free(project);
oakengine_shutdown();
return 0;
return;
}
if (!worker_binary_exists()) {
printf("oakengine_playback_test: SKIP: oak-render-worker binary not "
"found, playback assertions skipped\n");
oakengine_project_free(project);
oakengine_shutdown();
return 0;
return;
}
// The engine renders through the backend requested in the config.
olive::Config::current()[QStringLiteral("GraphicsBackend")] =
QStringLiteral("opengl");
assert(oakengine_init(OAKENGINE_INIT_HEADLESS | OAKENGINE_INIT_RENDER) ==
EXPECT_TRUE(oakengine_init(OAKENGINE_INIT_HEADLESS | OAKENGINE_INIT_RENDER) ==
OAKENGINE_OK);
// Build content through the facade (a real clip gives the sequence a
@@ -340,54 +341,54 @@ int main(void)
// tests/demo.mp4 placed from 0.
const QString demo_path = QDir(QStringLiteral(OAK_TEST_SOURCE_DIR))
.filePath(QStringLiteral("tests/demo.mp4"));
assert(QFileInfo::exists(demo_path));
assert(oakengine_sequence_add_track(seq, OAKENGINE_TRACK_TYPE_VIDEO) ==
EXPECT_TRUE(QFileInfo::exists(demo_path));
EXPECT_TRUE(oakengine_sequence_add_track(seq, OAKENGINE_TRACK_TYPE_VIDEO) ==
0);
OakEngineFootage *footage =
oakengine_project_import_footage(project, demo_path.toUtf8().constData());
assert(footage != NULL);
EXPECT_TRUE(footage != NULL);
OakEngineClip *clip = oakengine_sequence_add_footage_clip(
seq, footage, OAKENGINE_TRACK_TYPE_VIDEO, 0, 0, 480, 0);
assert(clip != NULL);
EXPECT_TRUE(clip != NULL);
// Audio comes from an audio track: without one the sequence's samples
// output is empty (the video track alone carries no samples).
assert(oakengine_sequence_add_track(seq, OAKENGINE_TRACK_TYPE_AUDIO) ==
EXPECT_TRUE(oakengine_sequence_add_track(seq, OAKENGINE_TRACK_TYPE_AUDIO) ==
0);
OakEngineClip *aclip = oakengine_sequence_add_footage_clip(
seq, footage, OAKENGINE_TRACK_TYPE_AUDIO, 0, 0, 480, 0);
assert(aclip != NULL);
EXPECT_TRUE(aclip != NULL);
oakengine_footage_free(footage);
FrameLog frames;
AudioLog audio;
OakEnginePlayback *p =
oakengine_playback_create(seq, 320, 180, 30000, 1001);
assert(p != NULL);
assert(oakengine_playback_set_frame_callback(p, on_frame, &frames) ==
EXPECT_TRUE(p != NULL);
EXPECT_TRUE(oakengine_playback_set_frame_callback(p, on_frame, &frames) ==
OAKENGINE_OK);
assert(oakengine_playback_set_audio_callback(p, on_audio, &audio) ==
EXPECT_TRUE(oakengine_playback_set_audio_callback(p, on_audio, &audio) ==
OAKENGINE_OK);
// Start at 1x: frames arrive monotonically with the right geometry,
// audio blocks with the right layout.
assert(oakengine_playback_start(p, 0, 1.0) == OAKENGINE_OK);
assert(oakengine_playback_is_playing(p) == 1);
assert(wait_until(six_frames, &frames, 10000));
EXPECT_TRUE(oakengine_playback_start(p, 0, 1.0) == OAKENGINE_OK);
EXPECT_TRUE(oakengine_playback_is_playing(p) == 1);
EXPECT_TRUE(wait_until(six_frames, &frames, 10000));
// The first block(s) can land while the decoder is still warming up
// (empty channels); require at least one well-formed block and a
// consistent layout across all of them (generous for CI stability).
assert(wait_until(good_audio_block, &audio, 10000));
EXPECT_TRUE(wait_until(good_audio_block, &audio, 10000));
{
frames.mutex.lock();
const size_t n = frames.frames.size();
for (size_t i = 0; i < n; i++) {
const oak_playback_frame &f = frames.frames.at(i);
assert(f.width == 320 && f.height == 180);
assert(f.format == 3); // f16
assert(f.linesize >= 320 * 4 * 2);
assert(f.data != NULL);
EXPECT_TRUE(f.width == 320 && f.height == 180);
EXPECT_TRUE(f.format == 3); // f16
EXPECT_TRUE(f.linesize >= 320 * 4 * 2);
EXPECT_TRUE(f.data != NULL);
if (i > 0) {
assert(f.timestamp > frames.frames.at(i - 1).timestamp);
EXPECT_TRUE(f.timestamp > frames.frames.at(i - 1).timestamp);
}
}
frames.mutex.unlock();
@@ -396,15 +397,15 @@ int main(void)
audio.mutex.lock();
bool found_good = false;
for (const oak_playback_audio &a : audio.blocks) {
assert(a.channels == 2);
assert(a.sample_rate == 48000);
assert(a.sample_count >= 0);
EXPECT_TRUE(a.channels == 2);
EXPECT_TRUE(a.sample_rate == 48000);
EXPECT_TRUE(a.sample_count >= 0);
if (a.channel_data && a.channel_data[0] && a.channel_data[1] &&
a.sample_count > 0) {
found_good = true;
}
}
assert(found_good);
EXPECT_TRUE(found_good);
audio.mutex.unlock();
}
@@ -412,86 +413,86 @@ int main(void)
// instance exists in this process).
pump_ms(400);
int64_t pos_a = -1;
assert(oakengine_playback_get_position(p, &pos_a) == OAKENGINE_OK);
assert(pos_a > 0);
EXPECT_TRUE(oakengine_playback_get_position(p, &pos_a) == OAKENGINE_OK);
EXPECT_TRUE(pos_a > 0);
// Pause freezes delivery and the position.
assert(oakengine_playback_pause(p) == OAKENGINE_OK);
assert(oakengine_playback_is_playing(p) == 0);
EXPECT_TRUE(oakengine_playback_pause(p) == OAKENGINE_OK);
EXPECT_TRUE(oakengine_playback_is_playing(p) == 0);
const size_t frozen_frames = frames.size();
int64_t frozen_pos = -1;
assert(oakengine_playback_get_position(p, &frozen_pos) == OAKENGINE_OK);
EXPECT_TRUE(oakengine_playback_get_position(p, &frozen_pos) == OAKENGINE_OK);
pump_ms(250);
assert(frames.size() <= frozen_frames + 1); // one in-flight may land
EXPECT_TRUE(frames.size() <= frozen_frames + 1); // one in-flight may land
int64_t still_frozen = -1;
assert(oakengine_playback_get_position(p, &still_frozen) == OAKENGINE_OK);
assert(still_frozen == frozen_pos);
EXPECT_TRUE(oakengine_playback_get_position(p, &still_frozen) == OAKENGINE_OK);
EXPECT_TRUE(still_frozen == frozen_pos);
// Resume at the frozen position: delivery continues.
assert(oakengine_playback_start(p, frozen_pos, 1.0) == OAKENGINE_OK);
assert(oakengine_playback_is_playing(p) == 1);
EXPECT_TRUE(oakengine_playback_start(p, frozen_pos, 1.0) == OAKENGINE_OK);
EXPECT_TRUE(oakengine_playback_is_playing(p) == 1);
pump_ms(300);
assert(frames.size() > frozen_frames);
EXPECT_TRUE(frames.size() > frozen_frames);
// 2x advances the position (much) faster than 1x over the same wall
// window (generous margins to stay CI-stable).
assert(oakengine_playback_get_position(p, &pos_a) == OAKENGINE_OK);
EXPECT_TRUE(oakengine_playback_get_position(p, &pos_a) == OAKENGINE_OK);
pump_ms(600);
int64_t pos_b = -1;
assert(oakengine_playback_get_position(p, &pos_b) == OAKENGINE_OK);
EXPECT_TRUE(oakengine_playback_get_position(p, &pos_b) == OAKENGINE_OK);
const int64_t advance_1x = pos_b - pos_a;
assert(oakengine_playback_set_speed(p, 2.0) == OAKENGINE_OK);
EXPECT_TRUE(oakengine_playback_set_speed(p, 2.0) == OAKENGINE_OK);
pump_ms(600);
int64_t pos_c = -1;
assert(oakengine_playback_get_position(p, &pos_c) == OAKENGINE_OK);
EXPECT_TRUE(oakengine_playback_get_position(p, &pos_c) == OAKENGINE_OK);
const int64_t advance_2x = pos_c - pos_b;
assert(advance_1x > 0);
assert(advance_2x > advance_1x + advance_1x / 2);
EXPECT_TRUE(advance_1x > 0);
EXPECT_TRUE(advance_2x > advance_1x + advance_1x / 2);
// Stop resets to the last start timestamp; restarting from 0 replays.
assert(oakengine_playback_stop(p) == OAKENGINE_OK);
assert(oakengine_playback_is_playing(p) == 0);
EXPECT_TRUE(oakengine_playback_stop(p) == OAKENGINE_OK);
EXPECT_TRUE(oakengine_playback_is_playing(p) == 0);
int64_t stopped_pos = -1;
assert(oakengine_playback_get_position(p, &stopped_pos) ==
EXPECT_TRUE(oakengine_playback_get_position(p, &stopped_pos) ==
OAKENGINE_OK);
assert(stopped_pos == frozen_pos);
EXPECT_TRUE(stopped_pos == frozen_pos);
{
frames.mutex.lock();
frames.frames.clear();
frames.mutex.unlock();
}
assert(oakengine_playback_start(p, 0, 1.0) == OAKENGINE_OK);
assert(wait_until(six_frames, &frames, 10000));
EXPECT_TRUE(oakengine_playback_start(p, 0, 1.0) == OAKENGINE_OK);
EXPECT_TRUE(wait_until(six_frames, &frames, 10000));
{
frames.mutex.lock();
assert(frames.frames.front().timestamp < 30);
EXPECT_TRUE(frames.frames.front().timestamp < 30);
frames.mutex.unlock();
}
assert(oakengine_playback_stop(p) == OAKENGINE_OK);
EXPECT_TRUE(oakengine_playback_stop(p) == OAKENGINE_OK);
// End of stream: start near the end at 4x, the engine auto-stops at
// the sequence length and reports that as the position.
int len_num = 0, len_den = 1;
assert(oakengine_sequence_get_length_rational(seq, &len_num, &len_den) ==
EXPECT_TRUE(oakengine_sequence_get_length_rational(seq, &len_num, &len_den) ==
OAKENGINE_OK);
const int64_t end_ts = int64_t(
olive::core::Timecode::time_to_timestamp(
olive::Rational(len_num, len_den),
olive::Rational(1001, 30000), olive::core::Timecode::k_round));
const int64_t near_end = end_ts > 48 ? end_ts - 48 : 0;
assert(oakengine_playback_start(p, near_end, 4.0) == OAKENGINE_OK);
assert(wait_until(not_playing, p, 10000));
EXPECT_TRUE(oakengine_playback_start(p, near_end, 4.0) == OAKENGINE_OK);
EXPECT_TRUE(wait_until(not_playing, p, 10000));
int64_t end_pos = -1;
assert(oakengine_playback_get_position(p, &end_pos) == OAKENGINE_OK);
assert(end_pos == end_ts);
EXPECT_TRUE(oakengine_playback_get_position(p, &end_pos) == OAKENGINE_OK);
EXPECT_TRUE(end_pos == end_ts);
// Freeing during playback must not crash.
OakEnginePlayback *p2 =
oakengine_playback_create(seq, 320, 180, 30000, 1001);
assert(p2 != NULL);
assert(oakengine_playback_set_frame_callback(p2, on_frame,
EXPECT_TRUE(p2 != NULL);
EXPECT_TRUE(oakengine_playback_set_frame_callback(p2, on_frame,
&frames) == OAKENGINE_OK);
assert(oakengine_playback_start(p2, 0, 1.0) == OAKENGINE_OK);
EXPECT_TRUE(oakengine_playback_start(p2, 0, 1.0) == OAKENGINE_OK);
pump_ms(50);
oakengine_playback_free(p2);
@@ -499,17 +500,15 @@ int main(void)
// detaches, and free() from this thread waits its exit out.
OakEnginePlayback *p3 =
oakengine_playback_create(seq, 320, 180, 30000, 1001);
assert(p3 != NULL);
assert(oakengine_playback_set_frame_callback(p3, on_frame_stop, p3) ==
EXPECT_TRUE(p3 != NULL);
EXPECT_TRUE(oakengine_playback_set_frame_callback(p3, on_frame_stop, p3) ==
OAKENGINE_OK);
assert(oakengine_playback_start(p3, 0, 1.0) == OAKENGINE_OK);
assert(wait_until(not_playing, p3, 10000));
EXPECT_TRUE(oakengine_playback_start(p3, 0, 1.0) == OAKENGINE_OK);
EXPECT_TRUE(wait_until(not_playing, p3, 10000));
oakengine_playback_free(p3);
oakengine_playback_free(p);
oakengine_project_free(project);
assert(oakengine_shutdown() == OAKENGINE_OK);
EXPECT_TRUE(oakengine_shutdown() == OAKENGINE_OK);
printf("oakengine_playback_test: all assertions passed\n");
return 0;
}
+82 -83
View File
@@ -24,6 +24,7 @@
// (only the RENDER init bit). Uses the real media file tests/demo.mp4.
#include <assert.h>
#include <gtest/gtest.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -54,47 +55,47 @@ static void make_tmpdir(void)
#if defined(_WIN32)
char base[MAX_PATH];
const DWORD len = GetTempPathA(MAX_PATH, base);
assert(len > 0 && len < MAX_PATH);
EXPECT_TRUE(len > 0 && len < MAX_PATH);
snprintf(g_tmpdir, sizeof(g_tmpdir), "%soakengine_preview_test_%lu",
base, (unsigned long)GetCurrentProcessId());
assert(_mkdir(g_tmpdir) == 0);
EXPECT_TRUE(_mkdir(g_tmpdir) == 0);
#else
strcpy(g_tmpdir, "/tmp/oakengine_preview_test_XXXXXX");
assert(mkdtemp(g_tmpdir) != NULL);
EXPECT_TRUE(mkdtemp(g_tmpdir) != NULL);
#endif
}
static void demo_path(char *dst, size_t cap)
{
const int n = snprintf(dst, cap, "%s/tests/demo.mp4", OAK_TEST_SOURCE_DIR);
assert(n > 0 && (size_t)n < cap);
EXPECT_TRUE(n > 0 && (size_t)n < cap);
}
static void test_loop_mode(OakEngineProject *project, OakEngineClip *clip)
{
assert(oakengine_clip_get_loop_mode(clip) == OAKENGINE_LOOP_MODE_OFF);
EXPECT_TRUE(oakengine_clip_get_loop_mode(clip) == OAKENGINE_LOOP_MODE_OFF);
assert(oakengine_clip_set_loop_mode(clip, OAKENGINE_LOOP_MODE_LOOP) ==
EXPECT_TRUE(oakengine_clip_set_loop_mode(clip, OAKENGINE_LOOP_MODE_LOOP) ==
OAKENGINE_OK);
assert(oakengine_clip_get_loop_mode(clip) == OAKENGINE_LOOP_MODE_LOOP);
assert(oakengine_clip_set_loop_mode(clip, OAKENGINE_LOOP_MODE_CLAMP) ==
EXPECT_TRUE(oakengine_clip_get_loop_mode(clip) == OAKENGINE_LOOP_MODE_LOOP);
EXPECT_TRUE(oakengine_clip_set_loop_mode(clip, OAKENGINE_LOOP_MODE_CLAMP) ==
OAKENGINE_OK);
assert(oakengine_clip_get_loop_mode(clip) == OAKENGINE_LOOP_MODE_CLAMP);
EXPECT_TRUE(oakengine_clip_get_loop_mode(clip) == OAKENGINE_LOOP_MODE_CLAMP);
// Unknown modes and NULL are rejected.
assert(oakengine_clip_set_loop_mode(clip, 3) == OAKENGINE_E_INVALID);
assert(oakengine_clip_set_loop_mode(clip, -1) == OAKENGINE_E_INVALID);
assert(oakengine_clip_set_loop_mode(NULL, 0) == OAKENGINE_E_INVALID);
assert(oakengine_clip_get_loop_mode(NULL) == OAKENGINE_E_INVALID);
EXPECT_TRUE(oakengine_clip_set_loop_mode(clip, 3) == OAKENGINE_E_INVALID);
EXPECT_TRUE(oakengine_clip_set_loop_mode(clip, -1) == OAKENGINE_E_INVALID);
EXPECT_TRUE(oakengine_clip_set_loop_mode(NULL, 0) == OAKENGINE_E_INVALID);
EXPECT_TRUE(oakengine_clip_get_loop_mode(NULL) == OAKENGINE_E_INVALID);
// Undoable: undo restores LOOP then OFF, redo restores LOOP then CLAMP.
assert(oakengine_project_undo(project) == OAKENGINE_OK);
assert(oakengine_clip_get_loop_mode(clip) == OAKENGINE_LOOP_MODE_LOOP);
assert(oakengine_project_undo(project) == OAKENGINE_OK);
assert(oakengine_clip_get_loop_mode(clip) == OAKENGINE_LOOP_MODE_OFF);
assert(oakengine_project_redo(project) == OAKENGINE_OK);
assert(oakengine_project_redo(project) == OAKENGINE_OK);
assert(oakengine_clip_get_loop_mode(clip) == OAKENGINE_LOOP_MODE_CLAMP);
EXPECT_TRUE(oakengine_project_undo(project) == OAKENGINE_OK);
EXPECT_TRUE(oakengine_clip_get_loop_mode(clip) == OAKENGINE_LOOP_MODE_LOOP);
EXPECT_TRUE(oakengine_project_undo(project) == OAKENGINE_OK);
EXPECT_TRUE(oakengine_clip_get_loop_mode(clip) == OAKENGINE_LOOP_MODE_OFF);
EXPECT_TRUE(oakengine_project_redo(project) == OAKENGINE_OK);
EXPECT_TRUE(oakengine_project_redo(project) == OAKENGINE_OK);
EXPECT_TRUE(oakengine_clip_get_loop_mode(clip) == OAKENGINE_LOOP_MODE_CLAMP);
}
// Generate a loud test tone (440 Hz stereo sine, 2 s) with the ffmpeg
@@ -105,15 +106,15 @@ static void test_loop_mode(OakEngineProject *project, OakEngineClip *clip)
static void make_tone(char *dst, size_t cap)
{
const int n = snprintf(dst, cap, "%s/tone.wav", g_tmpdir);
assert(n > 0 && (size_t)n < cap);
EXPECT_TRUE(n > 0 && (size_t)n < cap);
char cmd[4608];
snprintf(cmd, sizeof(cmd),
"ffmpeg -v error -y -f lavfi -i "
"\"sine=frequency=440:duration=2\" -ar 48000 -ac 2 \"%s\"",
dst);
assert(system(cmd) == 0);
EXPECT_TRUE(system(cmd) == 0);
FILE *f = fopen(dst, "rb");
assert(f != NULL);
EXPECT_TRUE(f != NULL);
fclose(f);
}
@@ -124,22 +125,22 @@ static void test_levels(OakEngineSequence *seq)
// Inside the clip (30 frames at 30000/1001): a loud sine on both
// channels. RMS of a full-scale sine is ~0.707.
const int written = oakengine_preview_get_audio_levels(seq, 10, levels, 4);
assert(written == 2);
assert(levels[2] == 0.0 && levels[3] == 0.0); // beyond channel count
EXPECT_TRUE(written == 2);
EXPECT_TRUE(levels[2] == 0.0 && levels[3] == 0.0); // beyond channel count
// Past the end of the track: exact silence.
double silent[2] = { -1.0, -1.0 };
assert(oakengine_preview_get_audio_levels(seq, 35, silent, 2) >= 0);
assert(silent[0] == 0.0 && silent[1] == 0.0);
EXPECT_TRUE(oakengine_preview_get_audio_levels(seq, 35, silent, 2) >= 0);
EXPECT_TRUE(silent[0] == 0.0 && silent[1] == 0.0);
// Error paths.
assert(oakengine_preview_get_audio_levels(NULL, 10, levels, 2) ==
EXPECT_TRUE(oakengine_preview_get_audio_levels(NULL, 10, levels, 2) ==
OAKENGINE_E_INVALID);
assert(oakengine_preview_get_audio_levels(seq, -1, levels, 2) ==
EXPECT_TRUE(oakengine_preview_get_audio_levels(seq, -1, levels, 2) ==
OAKENGINE_E_INVALID);
assert(oakengine_preview_get_audio_levels(seq, 10, NULL, 2) ==
EXPECT_TRUE(oakengine_preview_get_audio_levels(seq, 10, NULL, 2) ==
OAKENGINE_E_INVALID);
assert(oakengine_preview_get_audio_levels(seq, 10, levels, 0) ==
EXPECT_TRUE(oakengine_preview_get_audio_levels(seq, 10, levels, 0) ==
OAKENGINE_E_INVALID);
}
@@ -149,23 +150,23 @@ static void test_waveform(OakEngineFootage *tone, OakEngineFootage *demo,
double mins[16], maxs[16];
// The tone in 10 buckets over one second: every bucket swings.
assert(oakengine_preview_get_waveform_summary(tone, 0, 0, 30, mins,
EXPECT_TRUE(oakengine_preview_get_waveform_summary(tone, 0, 0, 30, mins,
maxs, 10) == OAKENGINE_OK);
for (int i = 0; i < 10; i++) {
assert(mins[i] <= maxs[i]);
EXPECT_TRUE(mins[i] <= maxs[i]);
}
// Error paths.
assert(oakengine_preview_get_waveform_summary(probed, 0, 0, 30, mins,
EXPECT_TRUE(oakengine_preview_get_waveform_summary(probed, 0, 0, 30, mins,
maxs, 10) ==
OAKENGINE_E_INVALID);
assert(oakengine_preview_get_waveform_summary(tone, 99, 0, 30, mins,
EXPECT_TRUE(oakengine_preview_get_waveform_summary(tone, 99, 0, 30, mins,
maxs, 10) ==
OAKENGINE_E_INVALID);
assert(oakengine_preview_get_waveform_summary(tone, 0, 0, 30, mins,
EXPECT_TRUE(oakengine_preview_get_waveform_summary(tone, 0, 0, 30, mins,
maxs, 0) ==
OAKENGINE_E_INVALID);
assert(oakengine_preview_get_waveform_summary(NULL, 0, 0, 30, mins, maxs,
EXPECT_TRUE(oakengine_preview_get_waveform_summary(NULL, 0, 0, 30, mins, maxs,
10) == OAKENGINE_E_INVALID);
}
@@ -174,7 +175,7 @@ static void test_waveform(OakEngineFootage *tone, OakEngineFootage *demo,
static void test_waveform_max_sample_rate(void)
{
int rate = oakengine_waveform_max_sample_rate();
assert(rate > 0);
EXPECT_TRUE(rate > 0);
(void) rate;
}
@@ -184,68 +185,68 @@ static void test_audio_analyze_levels(void)
float ch1[] = {0.0f, 0.0f, 0.0f, 0.0f};
const float *data[] = {ch0, ch1};
double levels[2] = {-1.0, -1.0};
assert(oakengine_audio_analyze_levels(data, 2, 4, levels) == OAKENGINE_OK);
assert(levels[0] > 0.0);
assert(levels[1] == 0.0);
assert(oakengine_audio_analyze_levels(NULL, 2, 4, levels) == OAKENGINE_E_INVALID);
assert(oakengine_audio_analyze_levels(data, 0, 4, levels) == OAKENGINE_E_INVALID);
assert(oakengine_audio_analyze_levels(data, 2, 0, levels) == OAKENGINE_E_INVALID);
assert(oakengine_audio_analyze_levels(data, 2, 4, NULL) == OAKENGINE_E_INVALID);
EXPECT_TRUE(oakengine_audio_analyze_levels(data, 2, 4, levels) == OAKENGINE_OK);
EXPECT_TRUE(levels[0] > 0.0);
EXPECT_TRUE(levels[1] == 0.0);
EXPECT_TRUE(oakengine_audio_analyze_levels(NULL, 2, 4, levels) == OAKENGINE_E_INVALID);
EXPECT_TRUE(oakengine_audio_analyze_levels(data, 0, 4, levels) == OAKENGINE_E_INVALID);
EXPECT_TRUE(oakengine_audio_analyze_levels(data, 2, 0, levels) == OAKENGINE_E_INVALID);
EXPECT_TRUE(oakengine_audio_analyze_levels(data, 2, 4, NULL) == OAKENGINE_E_INVALID);
}
static void test_cacher_null_state(void)
{
assert(oakengine_preview_cacher_set_playhead(0, 1) == OAKENGINE_E_STATE);
assert(oakengine_preview_cacher_set_thumbnails_paused(1) == OAKENGINE_E_STATE);
assert(oakengine_preview_cacher_clear_single_frame_renders(0) == OAKENGINE_E_STATE);
assert(oakengine_preview_cacher_force_cache_range(NULL, 0, 1, 1, 1) == OAKENGINE_E_INVALID);
EXPECT_TRUE(oakengine_preview_cacher_set_playhead(0, 1) == OAKENGINE_E_STATE);
EXPECT_TRUE(oakengine_preview_cacher_set_thumbnails_paused(1) == OAKENGINE_E_STATE);
EXPECT_TRUE(oakengine_preview_cacher_clear_single_frame_renders(0) == OAKENGINE_E_STATE);
EXPECT_TRUE(oakengine_preview_cacher_force_cache_range(NULL, 0, 1, 1, 1) == OAKENGINE_E_INVALID);
}
static void test_preview_request_null(void)
{
assert(oakengine_preview_request_single_frame(NULL, 0, 1, 0) == NULL);
assert(oakengine_preview_request_audio_range(NULL, 0, 1, 1, 1) == NULL);
assert(oakengine_preview_request_is_done(NULL) == 0);
assert(oakengine_preview_request_has_result(NULL) == 0);
assert(oakengine_preview_request_set_finished_callback(NULL, NULL, NULL) == OAKENGINE_E_INVALID);
EXPECT_TRUE(oakengine_preview_request_single_frame(NULL, 0, 1, 0) == NULL);
EXPECT_TRUE(oakengine_preview_request_audio_range(NULL, 0, 1, 1, 1) == NULL);
EXPECT_TRUE(oakengine_preview_request_is_done(NULL) == 0);
EXPECT_TRUE(oakengine_preview_request_has_result(NULL) == 0);
EXPECT_TRUE(oakengine_preview_request_set_finished_callback(NULL, NULL, NULL) == OAKENGINE_E_INVALID);
oak_playback_frame frame;
memset(&frame, 0, sizeof(frame));
assert(oakengine_preview_request_get_frame(NULL, &frame) == OAKENGINE_E_INVALID);
assert(oakengine_preview_request_get_audio_channel_count(NULL) == 0);
assert(oakengine_preview_request_get_audio_sample_rate(NULL) == 0);
assert(oakengine_preview_request_get_audio_samples(NULL, 0, NULL, 0) == OAKENGINE_E_INVALID);
EXPECT_TRUE(oakengine_preview_request_get_frame(NULL, &frame) == OAKENGINE_E_INVALID);
EXPECT_TRUE(oakengine_preview_request_get_audio_channel_count(NULL) == 0);
EXPECT_TRUE(oakengine_preview_request_get_audio_sample_rate(NULL) == 0);
EXPECT_TRUE(oakengine_preview_request_get_audio_samples(NULL, 0, NULL, 0) == OAKENGINE_E_INVALID);
oakengine_preview_request_free(NULL);
}
static void test_render_manager_null(void)
{
assert(oakengine_render_manager_set_aggressive_garbage_collection(1) == OAKENGINE_E_STATE);
EXPECT_TRUE(oakengine_render_manager_set_aggressive_garbage_collection(1) == OAKENGINE_E_STATE);
oakengine_render_manager_requested_backend();
char buf[64];
int len = oakengine_render_manager_backend_to_string(0, buf, sizeof(buf));
assert(len >= 0);
EXPECT_TRUE(len >= 0);
}
static void test_playback_cache_null(void)
{
assert(oakengine_viewer_get_playback_cache(NULL) == NULL);
assert(oakengine_playback_cache_indicator_height() > 0);
assert(oakengine_playback_cache_valid_ranges(NULL, NULL, 0) == OAKENGINE_E_INVALID);
assert(oakengine_viewer_get_frame_cache(NULL) == NULL);
EXPECT_TRUE(oakengine_viewer_get_playback_cache(NULL) == NULL);
EXPECT_TRUE(oakengine_playback_cache_indicator_height() > 0);
EXPECT_TRUE(oakengine_playback_cache_valid_ranges(NULL, NULL, 0) == OAKENGINE_E_INVALID);
EXPECT_TRUE(oakengine_viewer_get_frame_cache(NULL) == NULL);
}
int main(void)
TEST(OakEnginePreview, Main)
{
make_tmpdir();
// Sandbox the config/cache/data locations (see oakengine_init_test).
#if !defined(_WIN32)
assert(setenv("XDG_CONFIG_HOME", g_tmpdir, 1) == 0);
assert(setenv("XDG_CACHE_HOME", g_tmpdir, 1) == 0);
assert(setenv("XDG_DATA_HOME", g_tmpdir, 1) == 0);
EXPECT_TRUE(setenv("XDG_CONFIG_HOME", g_tmpdir, 1) == 0);
EXPECT_TRUE(setenv("XDG_CACHE_HOME", g_tmpdir, 1) == 0);
EXPECT_TRUE(setenv("XDG_DATA_HOME", g_tmpdir, 1) == 0);
#endif
assert(oakengine_init(OAKENGINE_INIT_HEADLESS) == OAKENGINE_OK);
EXPECT_TRUE(oakengine_init(OAKENGINE_INIT_HEADLESS) == OAKENGINE_OK);
// B9c: pure functions that don't need RenderManager
test_waveform_max_sample_rate();
@@ -256,37 +257,37 @@ int main(void)
test_playback_cache_null();
OakEngineProject *project = oakengine_project_create();
assert(project != NULL);
assert(oakengine_project_new(project) == OAKENGINE_OK);
EXPECT_TRUE(project != NULL);
EXPECT_TRUE(oakengine_project_new(project) == OAKENGINE_OK);
OakEngineSequence *seq = oakengine_sequence_new(project, "Preview");
assert(seq != NULL);
EXPECT_TRUE(seq != NULL);
char path[4096], tone_path[4096];
demo_path(path, sizeof(path));
make_tone(tone_path, sizeof(tone_path));
OakEngineFootage *tone =
oakengine_project_import_footage(project, tone_path);
assert(tone != NULL);
EXPECT_TRUE(tone != NULL);
OakEngineFootage *demo =
oakengine_project_import_footage(project, path);
assert(demo != NULL);
EXPECT_TRUE(demo != NULL);
OakEngineFootage *probed = oakengine_footage_probe(path);
assert(probed != NULL);
EXPECT_TRUE(probed != NULL);
// Loop mode works headless already.
OakEngineClip *clip = NULL;
assert(oakengine_sequence_add_track(seq, OAKENGINE_TRACK_TYPE_VIDEO) == 0);
assert(oakengine_sequence_add_track(seq, OAKENGINE_TRACK_TYPE_AUDIO) == 0);
EXPECT_TRUE(oakengine_sequence_add_track(seq, OAKENGINE_TRACK_TYPE_VIDEO) == 0);
EXPECT_TRUE(oakengine_sequence_add_track(seq, OAKENGINE_TRACK_TYPE_AUDIO) == 0);
clip = oakengine_sequence_add_footage_clip(
seq, demo, OAKENGINE_TRACK_TYPE_VIDEO, 0, 0, 30, 0);
assert(clip != NULL);
EXPECT_TRUE(clip != NULL);
OakEngineClip *aclip = oakengine_sequence_add_footage_clip(
seq, tone, OAKENGINE_TRACK_TYPE_AUDIO, 0, 0, 30, 0);
assert(aclip != NULL);
EXPECT_TRUE(aclip != NULL);
test_loop_mode(project, clip);
// Upgrade to RENDER for the audio readouts (audio needs no GL).
assert(oakengine_init(OAKENGINE_INIT_HEADLESS | OAKENGINE_INIT_RENDER) ==
EXPECT_TRUE(oakengine_init(OAKENGINE_INIT_HEADLESS | OAKENGINE_INIT_RENDER) ==
OAKENGINE_OK);
test_levels(seq);
test_waveform(tone, demo, probed);
@@ -295,8 +296,6 @@ int main(void)
oakengine_footage_free(demo);
oakengine_footage_free(tone);
oakengine_project_free(project);
assert(oakengine_shutdown() == OAKENGINE_OK);
EXPECT_TRUE(oakengine_shutdown() == OAKENGINE_OK);
printf("oakengine_preview_test: all assertions passed\n");
return 0;
}
+35 -35
View File
@@ -22,6 +22,7 @@
// Runs headless; no GPU required.
#include <assert.h>
#include <gtest/gtest.h>
#include <stdio.h>
#include <string.h>
@@ -30,10 +31,10 @@
static void test_instance_lifecycle(void)
{
assert(oakengine_proxy_create_instance() == OAKENGINE_OK);
assert(oakengine_proxy_destroy_instance() == OAKENGINE_OK);
EXPECT_TRUE(oakengine_proxy_create_instance() == OAKENGINE_OK);
EXPECT_TRUE(oakengine_proxy_destroy_instance() == OAKENGINE_OK);
// Destroying again is a no-op.
assert(oakengine_proxy_destroy_instance() == OAKENGINE_OK);
EXPECT_TRUE(oakengine_proxy_destroy_instance() == OAKENGINE_OK);
}
static void test_params_from_config(void)
@@ -41,53 +42,53 @@ static void test_params_from_config(void)
oak_proxy_params params;
memset(&params, 0xFF, sizeof(params));
assert(oakengine_proxy_create_instance() == OAKENGINE_OK);
assert(oakengine_proxy_params_from_config(&params) == OAKENGINE_OK);
EXPECT_TRUE(oakengine_proxy_create_instance() == OAKENGINE_OK);
EXPECT_TRUE(oakengine_proxy_params_from_config(&params) == OAKENGINE_OK);
// Sanity defaults from ProxyManager::proxy_params_from_config().
assert(params.width > 0);
assert(params.height > 0);
assert(params.divider >= 1);
assert(params.version >= 1);
assert(params.crf >= 0);
assert(params.include_audio == 0 || params.include_audio == 1);
assert(strlen(params.extension) > 0);
assert(strlen(params.preset) > 0);
EXPECT_TRUE(params.width > 0);
EXPECT_TRUE(params.height > 0);
EXPECT_TRUE(params.divider >= 1);
EXPECT_TRUE(params.version >= 1);
EXPECT_TRUE(params.crf >= 0);
EXPECT_TRUE(params.include_audio == 0 || params.include_audio == 1);
EXPECT_TRUE(strlen(params.extension) > 0);
EXPECT_TRUE(strlen(params.preset) > 0);
assert(oakengine_proxy_params_from_config(NULL) == OAKENGINE_E_INVALID);
EXPECT_TRUE(oakengine_proxy_params_from_config(NULL) == OAKENGINE_E_INVALID);
assert(oakengine_proxy_destroy_instance() == OAKENGINE_OK);
EXPECT_TRUE(oakengine_proxy_destroy_instance() == OAKENGINE_OK);
}
static void test_state_string_round_trip(void)
{
char buf[64];
assert(oakengine_proxy_state_to_string(OAKENGINE_PROXY_STATE_MISSING, buf,
EXPECT_TRUE(oakengine_proxy_state_to_string(OAKENGINE_PROXY_STATE_MISSING, buf,
sizeof(buf)) > 0);
assert(strlen(buf) > 0);
EXPECT_TRUE(strlen(buf) > 0);
assert(oakengine_proxy_state_to_string(OAKENGINE_PROXY_STATE_GENERATING,
EXPECT_TRUE(oakengine_proxy_state_to_string(OAKENGINE_PROXY_STATE_GENERATING,
buf, sizeof(buf)) > 0);
assert(strlen(buf) > 0);
EXPECT_TRUE(strlen(buf) > 0);
assert(oakengine_proxy_state_to_string(OAKENGINE_PROXY_STATE_READY, buf,
EXPECT_TRUE(oakengine_proxy_state_to_string(OAKENGINE_PROXY_STATE_READY, buf,
sizeof(buf)) > 0);
assert(strlen(buf) > 0);
EXPECT_TRUE(strlen(buf) > 0);
assert(oakengine_proxy_state_to_string(OAKENGINE_PROXY_STATE_FAILED, buf,
EXPECT_TRUE(oakengine_proxy_state_to_string(OAKENGINE_PROXY_STATE_FAILED, buf,
sizeof(buf)) > 0);
assert(strlen(buf) > 0);
EXPECT_TRUE(strlen(buf) > 0);
// Unknown state returns an error.
assert(oakengine_proxy_state_to_string(999, buf, sizeof(buf)) < 0);
EXPECT_TRUE(oakengine_proxy_state_to_string(999, buf, sizeof(buf)) < 0);
}
static void test_state_query(void)
{
assert(oakengine_proxy_get_state(NULL) == OAKENGINE_PROXY_STATE_MISSING);
assert(oakengine_proxy_get_state("") == OAKENGINE_PROXY_STATE_MISSING);
assert(oakengine_proxy_get_state("/nonexistent/path/proxy.mp4") ==
EXPECT_TRUE(oakengine_proxy_get_state(NULL) == OAKENGINE_PROXY_STATE_MISSING);
EXPECT_TRUE(oakengine_proxy_get_state("") == OAKENGINE_PROXY_STATE_MISSING);
EXPECT_TRUE(oakengine_proxy_get_state("/nonexistent/path/proxy.mp4") ==
OAKENGINE_PROXY_STATE_MISSING);
}
@@ -97,7 +98,7 @@ static void test_get_or_start_null(void)
memset(&result, 0xFF, sizeof(result));
// NULL cache_path should not crash; returns an error.
assert(oakengine_proxy_get_or_start(NULL, NULL, 0, NULL, &result) !=
EXPECT_TRUE(oakengine_proxy_get_or_start(NULL, NULL, 0, NULL, &result) !=
OAKENGINE_OK);
}
@@ -107,16 +108,16 @@ static void test_get_working_filename(void)
int len = oakengine_proxy_get_working_filename("/tmp/test.proxy",
buf, sizeof(buf));
// Should return a filename derived from input, even if file doesn't exist.
assert(len > 0);
assert(strlen(buf) > 0);
EXPECT_TRUE(len > 0);
EXPECT_TRUE(strlen(buf) > 0);
// NULL safety.
assert(oakengine_proxy_get_working_filename(NULL, buf, sizeof(buf)) < 0);
EXPECT_TRUE(oakengine_proxy_get_working_filename(NULL, buf, sizeof(buf)) < 0);
}
int main(void)
TEST(OakEngineProxy, Main)
{
assert(oakengine_init(OAKENGINE_INIT_HEADLESS) == OAKENGINE_OK);
EXPECT_TRUE(oakengine_init(OAKENGINE_INIT_HEADLESS) == OAKENGINE_OK);
test_instance_lifecycle();
test_params_from_config();
@@ -125,6 +126,5 @@ int main(void)
test_get_or_start_null();
test_get_working_filename();
assert(oakengine_shutdown() == OAKENGINE_OK);
return 0;
EXPECT_TRUE(oakengine_shutdown() == OAKENGINE_OK);
}
+69 -70
View File
@@ -32,6 +32,7 @@
// footage tests/demo.mp4 feeds its samples input.
#include <assert.h>
#include <gtest/gtest.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
@@ -81,13 +82,13 @@ static void make_tmpdir(void)
#if defined(_WIN32)
char base[MAX_PATH];
const DWORD len = GetTempPathA(MAX_PATH, base);
assert(len > 0 && len < MAX_PATH);
EXPECT_TRUE(len > 0 && len < MAX_PATH);
snprintf(g_tmpdir, sizeof(g_tmpdir), "%soakengine_renderer_test_%lu",
base, (unsigned long)GetCurrentProcessId());
assert(_mkdir(g_tmpdir) == 0);
EXPECT_TRUE(_mkdir(g_tmpdir) == 0);
#else
strcpy(g_tmpdir, "/tmp/oakengine_renderer_test_XXXXXX");
assert(mkdtemp(g_tmpdir) != NULL);
EXPECT_TRUE(mkdtemp(g_tmpdir) != NULL);
#endif
}
@@ -158,40 +159,40 @@ static int frame_nonzero_bytes(const OakEngineFrame *frame)
static void test_validation(OakEngineSequence *seq)
{
// create() argument validation.
assert(oakengine_renderer_create(NULL, 320, 180, 4, 30000, 1001, NULL) ==
EXPECT_TRUE(oakengine_renderer_create(NULL, 320, 180, 4, 30000, 1001, NULL) ==
NULL);
assert(oakengine_renderer_create(seq, 0, 180, 4, 30000, 1001, NULL) ==
EXPECT_TRUE(oakengine_renderer_create(seq, 0, 180, 4, 30000, 1001, NULL) ==
NULL);
assert(oakengine_renderer_create(seq, 320, -1, 4, 30000, 1001, NULL) ==
EXPECT_TRUE(oakengine_renderer_create(seq, 320, -1, 4, 30000, 1001, NULL) ==
NULL);
assert(oakengine_renderer_create(seq, 320, 180, 4, 0, 1001, NULL) ==
EXPECT_TRUE(oakengine_renderer_create(seq, 320, 180, 4, 0, 1001, NULL) ==
NULL);
assert(oakengine_renderer_create(seq, 320, 180, 4, 30000, -1001, NULL) ==
EXPECT_TRUE(oakengine_renderer_create(seq, 320, 180, 4, 30000, -1001, NULL) ==
NULL);
assert(oakengine_renderer_create(seq, 320, 180, -1, 30000, 1001, NULL) ==
EXPECT_TRUE(oakengine_renderer_create(seq, 320, 180, -1, 30000, 1001, NULL) ==
NULL);
assert(oakengine_renderer_create(seq, 320, 180, 5, 30000, 1001, NULL) ==
EXPECT_TRUE(oakengine_renderer_create(seq, 320, 180, 5, 30000, 1001, NULL) ==
NULL);
OakEngineRenderer *r =
oakengine_renderer_create(seq, 320, 180, 4, 30000, 1001, NULL);
assert(r != NULL);
EXPECT_TRUE(r != NULL);
// Mode follows olive::RenderMode: 0/1 accepted, everything else rejected.
assert(oakengine_renderer_set_mode(r, 0) == OAKENGINE_OK);
assert(oakengine_renderer_set_mode(r, 1) == OAKENGINE_OK);
assert(oakengine_renderer_set_mode(r, -1) == OAKENGINE_E_INVALID);
assert(oakengine_renderer_set_mode(r, 2) == OAKENGINE_E_INVALID);
assert(oakengine_renderer_set_mode(NULL, 0) == OAKENGINE_E_INVALID);
EXPECT_TRUE(oakengine_renderer_set_mode(r, 0) == OAKENGINE_OK);
EXPECT_TRUE(oakengine_renderer_set_mode(r, 1) == OAKENGINE_OK);
EXPECT_TRUE(oakengine_renderer_set_mode(r, -1) == OAKENGINE_E_INVALID);
EXPECT_TRUE(oakengine_renderer_set_mode(r, 2) == OAKENGINE_E_INVALID);
EXPECT_TRUE(oakengine_renderer_set_mode(NULL, 0) == OAKENGINE_E_INVALID);
// Renders fail while the engine lacks the RENDER bit, with a
// human-readable reason.
assert(oakengine_renderer_render_frame(r, 0) == NULL);
EXPECT_TRUE(oakengine_renderer_render_frame(r, 0) == NULL);
char err[256];
assert(oakengine_renderer_last_error(r, err, sizeof(err)) > 0);
assert(strstr(err, "OAKENGINE_INIT_RENDER") != NULL);
assert(oakengine_renderer_render_audio(r, 0, 30) == NULL);
assert(oakengine_renderer_last_error(r, err, sizeof(err)) > 0);
EXPECT_TRUE(oakengine_renderer_last_error(r, err, sizeof(err)) > 0);
EXPECT_TRUE(strstr(err, "OAKENGINE_INIT_RENDER") != NULL);
EXPECT_TRUE(oakengine_renderer_render_audio(r, 0, 30) == NULL);
EXPECT_TRUE(oakengine_renderer_last_error(r, err, sizeof(err)) > 0);
// cancel is a no-op when nothing is in flight and is idempotent.
oakengine_renderer_cancel(r);
@@ -200,23 +201,23 @@ static void test_validation(OakEngineSequence *seq)
oakengine_renderer_free(r);
// NULL safety across all three handle families.
assert(oakengine_renderer_last_error(NULL, err, sizeof(err)) ==
EXPECT_TRUE(oakengine_renderer_last_error(NULL, err, sizeof(err)) ==
OAKENGINE_E_INVALID);
assert(oakengine_renderer_render_frame(NULL, 0) == NULL);
assert(oakengine_renderer_render_audio(NULL, 0, 30) == NULL);
EXPECT_TRUE(oakengine_renderer_render_frame(NULL, 0) == NULL);
EXPECT_TRUE(oakengine_renderer_render_audio(NULL, 0, 30) == NULL);
oakengine_renderer_cancel(NULL);
oakengine_renderer_free(NULL);
assert(oakengine_frame_width(NULL) == 0);
assert(oakengine_frame_height(NULL) == 0);
assert(oakengine_frame_format(NULL) == -1);
assert(oakengine_frame_channel_count(NULL) == 0);
assert(oakengine_frame_linesize_bytes(NULL) == 0);
assert(oakengine_frame_data(NULL) == NULL);
EXPECT_TRUE(oakengine_frame_width(NULL) == 0);
EXPECT_TRUE(oakengine_frame_height(NULL) == 0);
EXPECT_TRUE(oakengine_frame_format(NULL) == -1);
EXPECT_TRUE(oakengine_frame_channel_count(NULL) == 0);
EXPECT_TRUE(oakengine_frame_linesize_bytes(NULL) == 0);
EXPECT_TRUE(oakengine_frame_data(NULL) == NULL);
oakengine_frame_free(NULL);
assert(oakengine_audio_sample_rate(NULL) == 0);
assert(oakengine_audio_channel_count(NULL) == 0);
assert(oakengine_audio_sample_count(NULL) == 0);
assert(oakengine_audio_data(NULL, 0) == NULL);
EXPECT_TRUE(oakengine_audio_sample_rate(NULL) == 0);
EXPECT_TRUE(oakengine_audio_channel_count(NULL) == 0);
EXPECT_TRUE(oakengine_audio_sample_count(NULL) == 0);
EXPECT_TRUE(oakengine_audio_data(NULL, 0) == NULL);
oakengine_audio_free(NULL);
}
@@ -224,32 +225,32 @@ static void test_render_cache_helpers(void)
{
// Without an active RenderManager, these return OAKENGINE_E_STATE rather
// than crashing.
assert(oakengine_render_cache_set_display_color_processor(NULL) ==
EXPECT_TRUE(oakengine_render_cache_set_display_color_processor(NULL) ==
OAKENGINE_E_STATE);
assert(oakengine_render_cache_set_multicam_node(NULL) ==
EXPECT_TRUE(oakengine_render_cache_set_multicam_node(NULL) ==
OAKENGINE_E_STATE);
}
int main(void)
TEST(OakEngineRenderer, Main)
{
make_tmpdir();
// Sandbox the config/cache/data locations (see oakengine_init_test).
#if !defined(_WIN32)
assert(setenv("XDG_CONFIG_HOME", g_tmpdir, 1) == 0);
assert(setenv("XDG_CACHE_HOME", g_tmpdir, 1) == 0);
assert(setenv("XDG_DATA_HOME", g_tmpdir, 1) == 0);
EXPECT_TRUE(setenv("XDG_CONFIG_HOME", g_tmpdir, 1) == 0);
EXPECT_TRUE(setenv("XDG_CACHE_HOME", g_tmpdir, 1) == 0);
EXPECT_TRUE(setenv("XDG_DATA_HOME", g_tmpdir, 1) == 0);
#endif
// HEADLESS is enough for the validation part and creates the offscreen
// application object the backend probe below depends on.
assert(oakengine_init(OAKENGINE_INIT_HEADLESS) == OAKENGINE_OK);
EXPECT_TRUE(oakengine_init(OAKENGINE_INIT_HEADLESS) == OAKENGINE_OK);
OakEngineProject *project = oakengine_project_create();
assert(project != NULL);
assert(oakengine_project_new(project) == OAKENGINE_OK);
EXPECT_TRUE(project != NULL);
EXPECT_TRUE(oakengine_project_new(project) == OAKENGINE_OK);
OakEngineSequence *seq = oakengine_sequence_new(project, "Render");
assert(seq != NULL);
EXPECT_TRUE(seq != NULL);
test_render_cache_helpers();
test_validation(seq);
@@ -260,14 +261,14 @@ int main(void)
"available, render assertions skipped\n");
oakengine_project_free(project);
oakengine_shutdown();
return 0;
return;
}
if (!worker_binary_exists()) {
printf("oakengine_renderer_test: SKIP: oak-render-worker binary not "
"found, render assertions skipped\n");
oakengine_project_free(project);
oakengine_shutdown();
return 0;
return;
}
// The engine renders through the backend requested in the config.
@@ -275,9 +276,9 @@ int main(void)
QStringLiteral("opengl");
// Upgrade HEADLESS -> HEADLESS|RENDER (idempotent flag add).
assert(oakengine_init(OAKENGINE_INIT_HEADLESS | OAKENGINE_INIT_RENDER) ==
EXPECT_TRUE(oakengine_init(OAKENGINE_INIT_HEADLESS | OAKENGINE_INIT_RENDER) ==
OAKENGINE_OK);
assert(oakengine_init_flags() ==
EXPECT_TRUE(oakengine_init_flags() ==
(OAKENGINE_INIT_HEADLESS | OAKENGINE_INIT_RENDER));
// Build content through the engine C++ API (engine-internal test; the
@@ -296,10 +297,10 @@ int main(void)
const QString demo_path =
QDir(QStringLiteral(OAK_TEST_SOURCE_DIR))
.filePath(QStringLiteral("tests/demo.mp4"));
assert(QFileInfo::exists(demo_path));
EXPECT_TRUE(QFileInfo::exists(demo_path));
auto *footage = new olive::Footage(demo_path);
footage->setParent(proj);
assert(footage->is_valid());
EXPECT_TRUE(footage->is_valid());
olive::Node::connect_edge(
footage,
olive::NodeInput(sequence, olive::ViewerOutput::k_samples_input));
@@ -307,7 +308,7 @@ int main(void)
// render_frame: geometry/format and non-black pixels.
OakEngineRenderer *renderer =
oakengine_renderer_create(seq, 320, 180, 4, 30000, 1001, NULL);
assert(renderer != NULL);
EXPECT_TRUE(renderer != NULL);
OakEngineFrame *frame = oakengine_renderer_render_frame(renderer, 0);
char err[256];
if (!frame) {
@@ -316,14 +317,14 @@ int main(void)
err :
"(no error)");
}
assert(frame != NULL);
assert(oakengine_frame_width(frame) == 320);
assert(oakengine_frame_height(frame) == 180);
assert(oakengine_frame_format(frame) == 4); // f32
assert(oakengine_frame_channel_count(frame) == 4);
assert(oakengine_frame_linesize_bytes(frame) >= 320 * 4 * 4);
assert(oakengine_frame_data(frame) != NULL);
assert(frame_nonzero_bytes(frame) > 0); // solid red, not black
EXPECT_TRUE(frame != NULL);
EXPECT_TRUE(oakengine_frame_width(frame) == 320);
EXPECT_TRUE(oakengine_frame_height(frame) == 180);
EXPECT_TRUE(oakengine_frame_format(frame) == 4); // f32
EXPECT_TRUE(oakengine_frame_channel_count(frame) == 4);
EXPECT_TRUE(oakengine_frame_linesize_bytes(frame) >= 320 * 4 * 4);
EXPECT_TRUE(oakengine_frame_data(frame) != NULL);
EXPECT_TRUE(frame_nonzero_bytes(frame) > 0); // solid red, not black
oakengine_frame_free(frame);
// render_audio: 30 frames at 1001/30000 = 1.001s at 48 kHz stereo.
@@ -335,14 +336,14 @@ int main(void)
err :
"(no error)");
}
assert(audio != NULL);
assert(oakengine_audio_sample_rate(audio) == 48000);
assert(oakengine_audio_channel_count(audio) == 2);
EXPECT_TRUE(audio != NULL);
EXPECT_TRUE(oakengine_audio_sample_rate(audio) == 48000);
EXPECT_TRUE(oakengine_audio_channel_count(audio) == 2);
const int64_t samples = oakengine_audio_sample_count(audio);
assert(samples > 48048 - 4800 && samples < 48048 + 4800);
assert(oakengine_audio_data(audio, 0) != NULL);
assert(oakengine_audio_data(audio, 1) != NULL);
assert(oakengine_audio_data(audio, 2) == NULL); // out of range
EXPECT_TRUE(samples > 48048 - 4800 && samples < 48048 + 4800);
EXPECT_TRUE(oakengine_audio_data(audio, 0) != NULL);
EXPECT_TRUE(oakengine_audio_data(audio, 1) != NULL);
EXPECT_TRUE(oakengine_audio_data(audio, 2) == NULL); // out of range
oakengine_audio_free(audio);
// Cancelling mid-render from another thread must not crash, and the
@@ -355,14 +356,12 @@ int main(void)
canceller.join();
oakengine_frame_free(maybe); // may be NULL (cancelled) or a frame
OakEngineFrame *after = oakengine_renderer_render_frame(renderer, 0);
assert(after != NULL);
EXPECT_TRUE(after != NULL);
oakengine_frame_free(after);
oakengine_renderer_cancel(renderer); // idempotent no-op
oakengine_renderer_free(renderer);
oakengine_project_free(project);
assert(oakengine_shutdown() == OAKENGINE_OK);
EXPECT_TRUE(oakengine_shutdown() == OAKENGINE_OK);
printf("oakengine_renderer_test: all assertions passed\n");
return 0;
}
+101 -101
View File
@@ -22,6 +22,7 @@
// (oakengine/serializer.h). Runs headless; no GPU required.
#include <assert.h>
#include <gtest/gtest.h>
#include <stdio.h>
#include <string.h>
@@ -33,20 +34,20 @@
static void test_check_compressed_nonexistent(void)
{
assert(oakengine_serializer_check_compressed(
EXPECT_TRUE(oakengine_serializer_check_compressed(
"/nonexistent/path/project.ove") == 0);
assert(oakengine_serializer_check_compressed(NULL) == 0);
assert(oakengine_serializer_check_compressed("") == 0);
EXPECT_TRUE(oakengine_serializer_check_compressed(NULL) == 0);
EXPECT_TRUE(oakengine_serializer_check_compressed("") == 0);
}
static void test_clipboard_create_free(void)
{
OakEngineProject *project = oakengine_project_create();
assert(project != NULL);
EXPECT_TRUE(project != NULL);
OakEngineClipboard *cb =
oakengine_clipboard_create(OAKENGINE_CLIPBOARD_NODES, project, NULL);
assert(cb != NULL);
EXPECT_TRUE(cb != NULL);
oakengine_clipboard_free(cb);
oakengine_project_free(project);
@@ -55,14 +56,14 @@ static void test_clipboard_create_free(void)
static void test_copy_empty_nodes(void)
{
OakEngineProject *project = oakengine_project_create();
assert(project != NULL);
EXPECT_TRUE(project != NULL);
OakEngineClipboard *cb =
oakengine_clipboard_create(OAKENGINE_CLIPBOARD_NODES, project, NULL);
assert(cb != NULL);
EXPECT_TRUE(cb != NULL);
// Copying zero nodes should not crash and should report success.
assert(oakengine_clipboard_copy(cb) == OAKENGINE_OK);
EXPECT_TRUE(oakengine_clipboard_copy(cb) == OAKENGINE_OK);
oakengine_clipboard_free(cb);
oakengine_project_free(project);
@@ -71,21 +72,21 @@ static void test_copy_empty_nodes(void)
static void test_set_empty_sets(void)
{
OakEngineProject *project = oakengine_project_create();
assert(project != NULL);
EXPECT_TRUE(project != NULL);
OakEngineClipboard *cb =
oakengine_clipboard_create(OAKENGINE_CLIPBOARD_NODES, project, NULL);
assert(cb != NULL);
EXPECT_TRUE(cb != NULL);
// Setting empty arrays (count=0, array=NULL) should be OK.
assert(oakengine_clipboard_set_nodes(cb, NULL, 0) == OAKENGINE_OK);
assert(oakengine_clipboard_set_markers(cb, NULL, 0) == OAKENGINE_OK);
assert(oakengine_clipboard_set_keyframes(cb, NULL, 0) == OAKENGINE_OK);
EXPECT_TRUE(oakengine_clipboard_set_nodes(cb, NULL, 0) == OAKENGINE_OK);
EXPECT_TRUE(oakengine_clipboard_set_markers(cb, NULL, 0) == OAKENGINE_OK);
EXPECT_TRUE(oakengine_clipboard_set_keyframes(cb, NULL, 0) == OAKENGINE_OK);
// The clipboard with no content should still produce some XML.
char buf[256];
int len = oakengine_clipboard_save_to_xml(cb, buf, sizeof(buf));
assert(len > 0);
EXPECT_TRUE(len > 0);
oakengine_clipboard_free(cb);
oakengine_project_free(project);
@@ -94,36 +95,36 @@ static void test_set_empty_sets(void)
static void test_set_node_then_save_xml(void)
{
OakEngineProject *project = oakengine_project_create();
assert(project != NULL);
assert(oakengine_project_new(project) == OAKENGINE_OK);
EXPECT_TRUE(project != NULL);
EXPECT_TRUE(oakengine_project_new(project) == OAKENGINE_OK);
OakEngineNode *solid = oakengine_project_add_node(
project, "org.olivevideoeditor.Olive.solidgenerator");
assert(solid != NULL);
EXPECT_TRUE(solid != NULL);
OakEngineClipboard *cb =
oakengine_clipboard_create(OAKENGINE_CLIPBOARD_NODES, project, NULL);
assert(cb != NULL);
EXPECT_TRUE(cb != NULL);
// Set one node on the clipboard.
assert(oakengine_clipboard_set_nodes(cb, (const OakEngineNode *const *)&solid, 1) == OAKENGINE_OK);
EXPECT_TRUE(oakengine_clipboard_set_nodes(cb, (const OakEngineNode *const *)&solid, 1) == OAKENGINE_OK);
// Set a property on that node.
assert(oakengine_clipboard_set_property(cb, solid, "pos_x", "100") ==
EXPECT_TRUE(oakengine_clipboard_set_property(cb, solid, "pos_x", "100") ==
OAKENGINE_OK);
// save_to_xml should return a non-empty XML document.
char buf[4096];
int len = oakengine_clipboard_save_to_xml(cb, buf, sizeof(buf));
assert(len > 0);
assert(len < (int)sizeof(buf));
EXPECT_TRUE(len > 0);
EXPECT_TRUE(len < (int)sizeof(buf));
// Should contain the node type id and the property.
assert(strstr(buf, "solidgenerator") != NULL);
assert(strstr(buf, "pos_x") != NULL);
EXPECT_TRUE(strstr(buf, "solidgenerator") != NULL);
EXPECT_TRUE(strstr(buf, "pos_x") != NULL);
// Query-length mode.
int qlen = oakengine_clipboard_save_to_xml(cb, NULL, 0);
assert(qlen == len);
EXPECT_TRUE(qlen == len);
oakengine_clipboard_free(cb);
oakengine_project_free(project);
@@ -132,36 +133,36 @@ static void test_set_node_then_save_xml(void)
static void test_set_node_then_foreach_property(void)
{
OakEngineProject *project = oakengine_project_create();
assert(project != NULL);
assert(oakengine_project_new(project) == OAKENGINE_OK);
EXPECT_TRUE(project != NULL);
EXPECT_TRUE(oakengine_project_new(project) == OAKENGINE_OK);
OakEngineNode *solid = oakengine_project_add_node(
project, "org.olivevideoeditor.Olive.solidgenerator");
assert(solid != NULL);
EXPECT_TRUE(solid != NULL);
// Save_data: set node + property, then copy to system clipboard (save_data
// is serialized). The paste result populates load_data, which is what
// foreach_property reads.
OakEngineClipboard *cb_copy =
oakengine_clipboard_create(OAKENGINE_CLIPBOARD_NODES, project, NULL);
assert(cb_copy != NULL);
assert(oakengine_clipboard_set_nodes(cb_copy, (const OakEngineNode *const *)&solid, 1) == OAKENGINE_OK);
assert(oakengine_clipboard_set_property(cb_copy, solid, "pos_x", "100") ==
EXPECT_TRUE(cb_copy != NULL);
EXPECT_TRUE(oakengine_clipboard_set_nodes(cb_copy, (const OakEngineNode *const *)&solid, 1) == OAKENGINE_OK);
EXPECT_TRUE(oakengine_clipboard_set_property(cb_copy, solid, "pos_x", "100") ==
OAKENGINE_OK);
assert(oakengine_clipboard_set_property(cb_copy, solid, "pos_y", "200") ==
EXPECT_TRUE(oakengine_clipboard_set_property(cb_copy, solid, "pos_y", "200") ==
OAKENGINE_OK);
assert(oakengine_clipboard_copy(cb_copy) == OAKENGINE_OK);
EXPECT_TRUE(oakengine_clipboard_copy(cb_copy) == OAKENGINE_OK);
oakengine_clipboard_free(cb_copy);
OakEngineClipboard *cb_paste =
oakengine_clipboard_create(OAKENGINE_CLIPBOARD_NODES, project, NULL);
assert(cb_paste != NULL);
EXPECT_TRUE(cb_paste != NULL);
int result_code = -1;
assert(oakengine_clipboard_paste(cb_paste, OAKENGINE_CLIPBOARD_NODES,
EXPECT_TRUE(oakengine_clipboard_paste(cb_paste, OAKENGINE_CLIPBOARD_NODES,
project, &result_code, NULL, 0) ==
OAKENGINE_OK);
assert(result_code == OAKENGINE_SERIALIZER_OK);
EXPECT_TRUE(result_code == OAKENGINE_SERIALIZER_OK);
// foreach_property should visit both pasted properties.
int prop_seen = 0;
@@ -177,8 +178,8 @@ static void test_set_node_then_foreach_property(void)
return 0;
},
&prop_seen);
assert(ret == OAKENGINE_OK);
assert(prop_seen >= 2);
EXPECT_TRUE(ret == OAKENGINE_OK);
EXPECT_TRUE(prop_seen >= 2);
oakengine_clipboard_free(cb_paste);
oakengine_project_free(project);
@@ -187,39 +188,39 @@ static void test_set_node_then_foreach_property(void)
static void test_clipboard_copy_paste_roundtrip(void)
{
OakEngineProject *project = oakengine_project_create();
assert(project != NULL);
assert(oakengine_project_new(project) == OAKENGINE_OK);
EXPECT_TRUE(project != NULL);
EXPECT_TRUE(oakengine_project_new(project) == OAKENGINE_OK);
OakEngineNode *solid = oakengine_project_add_node(
project, "org.olivevideoeditor.Olive.solidgenerator");
assert(solid != NULL);
EXPECT_TRUE(solid != NULL);
// Create clipboard A for copy (type doesn't matter; set_nodes overrides).
OakEngineClipboard *cb_copy =
oakengine_clipboard_create(OAKENGINE_CLIPBOARD_NODES, project, NULL);
assert(cb_copy != NULL);
assert(oakengine_clipboard_set_nodes(cb_copy, (const OakEngineNode *const *)&solid, 1) == OAKENGINE_OK);
assert(oakengine_clipboard_copy(cb_copy) == OAKENGINE_OK);
EXPECT_TRUE(cb_copy != NULL);
EXPECT_TRUE(oakengine_clipboard_set_nodes(cb_copy, (const OakEngineNode *const *)&solid, 1) == OAKENGINE_OK);
EXPECT_TRUE(oakengine_clipboard_copy(cb_copy) == OAKENGINE_OK);
oakengine_clipboard_free(cb_copy);
// Create clipboard B for paste.
OakEngineClipboard *cb_paste =
oakengine_clipboard_create(OAKENGINE_CLIPBOARD_NODES, project, NULL);
assert(cb_paste != NULL);
EXPECT_TRUE(cb_paste != NULL);
int result_code = -1;
int ret = oakengine_clipboard_paste(cb_paste, OAKENGINE_CLIPBOARD_NODES,
project, &result_code, NULL, 0);
assert(ret == OAKENGINE_OK);
assert(result_code == OAKENGINE_SERIALIZER_OK);
EXPECT_TRUE(ret == OAKENGINE_OK);
EXPECT_TRUE(result_code == OAKENGINE_SERIALIZER_OK);
// Verify loaded_* accessors.
assert(oakengine_clipboard_get_loaded_node_count(cb_paste) == 1);
EXPECT_TRUE(oakengine_clipboard_get_loaded_node_count(cb_paste) == 1);
OakEngineNode *loaded = oakengine_clipboard_get_loaded_node_at(cb_paste, 0);
assert(loaded != NULL);
assert(loaded != solid); // pasted node should be a new copy
assert(oakengine_clipboard_get_loaded_node_at(cb_paste, -1) == NULL);
assert(oakengine_clipboard_get_loaded_node_at(cb_paste, 1) == NULL);
EXPECT_TRUE(loaded != NULL);
EXPECT_TRUE(loaded != solid); // pasted node should be a new copy
EXPECT_TRUE(oakengine_clipboard_get_loaded_node_at(cb_paste, -1) == NULL);
EXPECT_TRUE(oakengine_clipboard_get_loaded_node_at(cb_paste, 1) == NULL);
oakengine_clipboard_free(cb_paste);
oakengine_project_free(project);
@@ -228,23 +229,23 @@ static void test_clipboard_copy_paste_roundtrip(void)
static void test_clipboard_paste_with_map(void)
{
OakEngineProject *project = oakengine_project_create();
assert(project != NULL);
assert(oakengine_project_new(project) == OAKENGINE_OK);
EXPECT_TRUE(project != NULL);
EXPECT_TRUE(oakengine_project_new(project) == OAKENGINE_OK);
OakEngineNode *solid = oakengine_project_add_node(
project, "org.olivevideoeditor.Olive.solidgenerator");
assert(solid != NULL);
EXPECT_TRUE(solid != NULL);
OakEngineClipboard *cb_copy =
oakengine_clipboard_create(OAKENGINE_CLIPBOARD_NODES, project, NULL);
assert(cb_copy != NULL);
assert(oakengine_clipboard_set_nodes(cb_copy, (const OakEngineNode *const *)&solid, 1) == OAKENGINE_OK);
assert(oakengine_clipboard_copy(cb_copy) == OAKENGINE_OK);
EXPECT_TRUE(cb_copy != NULL);
EXPECT_TRUE(oakengine_clipboard_set_nodes(cb_copy, (const OakEngineNode *const *)&solid, 1) == OAKENGINE_OK);
EXPECT_TRUE(oakengine_clipboard_copy(cb_copy) == OAKENGINE_OK);
oakengine_clipboard_free(cb_copy);
OakEngineClipboard *cb_paste =
oakengine_clipboard_create(OAKENGINE_CLIPBOARD_NODES, project, NULL);
assert(cb_paste != NULL);
EXPECT_TRUE(cb_paste != NULL);
int pair_count = 0;
int result_code = -1;
@@ -255,15 +256,15 @@ static void test_clipboard_paste_with_map(void)
{
auto *pc = (int *) userdata;
(*pc)++;
assert(old_node != NULL);
assert(new_node != NULL);
assert(old_node != new_node);
EXPECT_TRUE(old_node != NULL);
EXPECT_TRUE(new_node != NULL);
EXPECT_TRUE(old_node != new_node);
return 0;
},
&pair_count, &result_code, NULL, 0);
assert(ret == OAKENGINE_OK);
assert(result_code == OAKENGINE_SERIALIZER_OK);
assert(pair_count == 1);
EXPECT_TRUE(ret == OAKENGINE_OK);
EXPECT_TRUE(result_code == OAKENGINE_SERIALIZER_OK);
EXPECT_TRUE(pair_count == 1);
oakengine_clipboard_free(cb_paste);
oakengine_project_free(project);
@@ -272,40 +273,40 @@ static void test_clipboard_paste_with_map(void)
static void test_clipboard_foreach_iterators(void)
{
OakEngineProject *project = oakengine_project_create();
assert(project != NULL);
assert(oakengine_project_new(project) == OAKENGINE_OK);
EXPECT_TRUE(project != NULL);
EXPECT_TRUE(oakengine_project_new(project) == OAKENGINE_OK);
OakEngineNode *solid = oakengine_project_add_node(
project, "org.olivevideoeditor.Olive.solidgenerator");
assert(solid != NULL);
EXPECT_TRUE(solid != NULL);
// Copy with properties so the paste-result has properties, then verify
// foreach_property, foreach_keyframe (should be 0) and foreach_connection
// (should be 0 since nothing is connected).
OakEngineClipboard *cb_copy =
oakengine_clipboard_create(OAKENGINE_CLIPBOARD_NODES, project, NULL);
assert(cb_copy != NULL);
assert(oakengine_clipboard_set_nodes(cb_copy, (const OakEngineNode *const *)&solid, 1) == OAKENGINE_OK);
assert(oakengine_clipboard_set_property(cb_copy, solid, "pos_x", "50") ==
EXPECT_TRUE(cb_copy != NULL);
EXPECT_TRUE(oakengine_clipboard_set_nodes(cb_copy, (const OakEngineNode *const *)&solid, 1) == OAKENGINE_OK);
EXPECT_TRUE(oakengine_clipboard_set_property(cb_copy, solid, "pos_x", "50") ==
OAKENGINE_OK);
assert(oakengine_clipboard_set_property(cb_copy, solid, "pos_y", "75") ==
EXPECT_TRUE(oakengine_clipboard_set_property(cb_copy, solid, "pos_y", "75") ==
OAKENGINE_OK);
assert(oakengine_clipboard_copy(cb_copy) == OAKENGINE_OK);
EXPECT_TRUE(oakengine_clipboard_copy(cb_copy) == OAKENGINE_OK);
oakengine_clipboard_free(cb_copy);
OakEngineClipboard *cb_paste =
oakengine_clipboard_create(OAKENGINE_CLIPBOARD_NODES, project, NULL);
assert(cb_paste != NULL);
EXPECT_TRUE(cb_paste != NULL);
int result_code = -1;
assert(oakengine_clipboard_paste(cb_paste, OAKENGINE_CLIPBOARD_NODES,
EXPECT_TRUE(oakengine_clipboard_paste(cb_paste, OAKENGINE_CLIPBOARD_NODES,
project, &result_code, NULL, 0) ==
OAKENGINE_OK);
assert(result_code == OAKENGINE_SERIALIZER_OK);
EXPECT_TRUE(result_code == OAKENGINE_SERIALIZER_OK);
// foreach_property should visit the pasted properties.
int prop_count = 0;
assert(oakengine_clipboard_foreach_property(
EXPECT_TRUE(oakengine_clipboard_foreach_property(
cb_paste,
[](OakEngineNode *node, const char *key, const char *value,
void *userdata) -> int
@@ -317,11 +318,11 @@ static void test_clipboard_foreach_iterators(void)
return 0;
},
&prop_count) == OAKENGINE_OK);
assert(prop_count >= 2);
EXPECT_TRUE(prop_count >= 2);
// foreach_keyframe should visit 0 (solid has no keyframe data in this test).
int kf_count = 0;
assert(oakengine_clipboard_foreach_keyframe(
EXPECT_TRUE(oakengine_clipboard_foreach_keyframe(
cb_paste,
[](const char *node_id, OakEngineKeyframe *keyframe,
void *userdata) -> int
@@ -335,7 +336,7 @@ static void test_clipboard_foreach_iterators(void)
// foreach_connection should visit 0 (no connections copied).
int conn_count = 0;
assert(oakengine_clipboard_foreach_connection(
EXPECT_TRUE(oakengine_clipboard_foreach_connection(
cb_paste,
[](OakEngineNode *output_node, OakEngineNode *input_node,
const char *input_id, int element, void *userdata) -> int
@@ -348,7 +349,7 @@ static void test_clipboard_foreach_iterators(void)
return 0;
},
&conn_count) == OAKENGINE_OK);
assert(conn_count == 0);
EXPECT_TRUE(conn_count == 0);
oakengine_clipboard_free(cb_paste);
oakengine_project_free(project);
@@ -357,66 +358,66 @@ static void test_clipboard_foreach_iterators(void)
static void test_clipboard_marker_keyframe_accessors(void)
{
OakEngineProject *project = oakengine_project_create();
assert(project != NULL);
assert(oakengine_project_new(project) == OAKENGINE_OK);
EXPECT_TRUE(project != NULL);
EXPECT_TRUE(oakengine_project_new(project) == OAKENGINE_OK);
// Create a sequence for its marker list.
OakEngineSequence *seq = oakengine_sequence_new(project, "MarkerSrc");
assert(seq != NULL);
EXPECT_TRUE(seq != NULL);
// Add a marker to the sequence's marker list.
OakEngineMarkerList *list =
oakengine_viewer_get_marker_list((OakEngineNode *)seq);
assert(list != NULL);
assert(oakengine_marker_list_add(list, 1, 1, 2, 1, "Test", 0) ==
EXPECT_TRUE(list != NULL);
EXPECT_TRUE(oakengine_marker_list_add(list, 1, 1, 2, 1, "Test", 0) ==
OAKENGINE_OK);
OakEngineMarker *marker = oakengine_marker_list_at(list, 0);
assert(marker != NULL);
EXPECT_TRUE(marker != NULL);
// Copy markers to clipboard and save_to_xml (tests set_markers + save).
OakEngineClipboard *cb_copy =
oakengine_clipboard_create(OAKENGINE_CLIPBOARD_MARKERS, project, NULL);
assert(cb_copy != NULL);
assert(oakengine_clipboard_set_markers(
EXPECT_TRUE(cb_copy != NULL);
EXPECT_TRUE(oakengine_clipboard_set_markers(
cb_copy, (const OakEngineMarker *const *)&marker, 1) ==
OAKENGINE_OK);
assert(oakengine_clipboard_copy(cb_copy) == OAKENGINE_OK);
EXPECT_TRUE(oakengine_clipboard_copy(cb_copy) == OAKENGINE_OK);
oakengine_clipboard_free(cb_copy);
// Paste back and verify get_loaded_marker accessors.
OakEngineClipboard *cb_paste =
oakengine_clipboard_create(OAKENGINE_CLIPBOARD_MARKERS, project, NULL);
assert(cb_paste != NULL);
EXPECT_TRUE(cb_paste != NULL);
int result_code = -1;
assert(oakengine_clipboard_paste(cb_paste, OAKENGINE_CLIPBOARD_MARKERS,
EXPECT_TRUE(oakengine_clipboard_paste(cb_paste, OAKENGINE_CLIPBOARD_MARKERS,
project, &result_code, NULL, 0) ==
OAKENGINE_OK);
assert(result_code == OAKENGINE_SERIALIZER_OK ||
EXPECT_TRUE(result_code == OAKENGINE_SERIALIZER_OK ||
result_code == OAKENGINE_SERIALIZER_NO_DATA);
// If paste succeeded, verify the accessors.
if (result_code == OAKENGINE_SERIALIZER_OK) {
int mc = oakengine_clipboard_get_loaded_marker_count(cb_paste);
assert(mc >= 0);
EXPECT_TRUE(mc >= 0);
OakEngineMarker *pm = oakengine_clipboard_get_loaded_marker_at(
cb_paste, 0);
if (pm != NULL) {
assert(oakengine_clipboard_get_loaded_marker_at(cb_paste, -1) ==
EXPECT_TRUE(oakengine_clipboard_get_loaded_marker_at(cb_paste, -1) ==
NULL);
}
}
// get_loaded_keyframe accessors with 0 keyframes (no keyframes copied).
assert(oakengine_clipboard_get_loaded_keyframe_count(cb_paste) >= 0);
assert(oakengine_clipboard_get_loaded_keyframe_at(cb_paste, 0) == NULL);
EXPECT_TRUE(oakengine_clipboard_get_loaded_keyframe_count(cb_paste) >= 0);
EXPECT_TRUE(oakengine_clipboard_get_loaded_keyframe_at(cb_paste, 0) == NULL);
oakengine_clipboard_free(cb_paste);
oakengine_project_free(project);
}
int main(void)
TEST(OakEngineSerializer, Main)
{
assert(oakengine_init(OAKENGINE_INIT_HEADLESS) == OAKENGINE_OK);
EXPECT_TRUE(oakengine_init(OAKENGINE_INIT_HEADLESS) == OAKENGINE_OK);
test_check_compressed_nonexistent();
test_clipboard_create_free();
@@ -429,6 +430,5 @@ int main(void)
test_clipboard_foreach_iterators();
test_clipboard_marker_keyframe_accessors();
assert(oakengine_shutdown() == OAKENGINE_OK);
return 0;
EXPECT_TRUE(oakengine_shutdown() == OAKENGINE_OK);
}
+38 -39
View File
@@ -25,6 +25,7 @@
// worker binary, SKIP with exit 0 when unavailable).
#include <assert.h>
#include <gtest/gtest.h>
#include <math.h>
#include <stdint.h>
#include <stdio.h>
@@ -61,13 +62,13 @@ static void make_tmpdir(void)
#if defined(_WIN32)
char base[MAX_PATH];
const DWORD len = GetTempPathA(MAX_PATH, base);
assert(len > 0 && len < MAX_PATH);
EXPECT_TRUE(len > 0 && len < MAX_PATH);
snprintf(g_tmpdir, sizeof(g_tmpdir), "%soakengine_sync_test_%lu", base,
(unsigned long)GetCurrentProcessId());
assert(_mkdir(g_tmpdir) == 0);
EXPECT_TRUE(_mkdir(g_tmpdir) == 0);
#else
strcpy(g_tmpdir, "/tmp/oakengine_sync_test_XXXXXX");
assert(mkdtemp(g_tmpdir) != NULL);
EXPECT_TRUE(mkdtemp(g_tmpdir) != NULL);
#endif
}
@@ -121,7 +122,7 @@ static void write_textured_wav(const QString &path, int seconds)
const int block = rate / 20; // one gain value per envelope window
QFile f(path);
assert(f.open(QFile::WriteOnly));
EXPECT_TRUE(f.open(QFile::WriteOnly));
auto write_u32 = [&f](uint32_t v) {
f.write(reinterpret_cast<const char *>(&v), 4);
};
@@ -181,48 +182,48 @@ static OakEngineClip *make_pair(OakEngineProject *project,
const char *media_path,
OakEngineClip **target_out)
{
assert(oakengine_sequence_add_track(seq, OAKENGINE_TRACK_TYPE_AUDIO) ==
EXPECT_TRUE(oakengine_sequence_add_track(seq, OAKENGINE_TRACK_TYPE_AUDIO) ==
0);
// A second audio track: placing the target on the SAME track would
// overwrite (trim) the reference clip.
assert(oakengine_sequence_add_track(seq, OAKENGINE_TRACK_TYPE_AUDIO) ==
EXPECT_TRUE(oakengine_sequence_add_track(seq, OAKENGINE_TRACK_TYPE_AUDIO) ==
1);
OakEngineFootage *footage =
oakengine_project_import_footage(project, media_path);
assert(footage != NULL);
EXPECT_TRUE(footage != NULL);
OakEngineClip *reference = oakengine_sequence_add_footage_clip(
seq, footage, OAKENGINE_TRACK_TYPE_AUDIO, 0, 0, 160, 0);
assert(reference != NULL);
EXPECT_TRUE(reference != NULL);
OakEngineClip *target = oakengine_sequence_add_footage_clip(
seq, footage, OAKENGINE_TRACK_TYPE_AUDIO, 1, k_offset_frames,
160 + k_offset_frames, k_offset_frames);
assert(target != NULL);
EXPECT_TRUE(target != NULL);
oakengine_footage_free(footage);
*target_out = target;
return reference;
}
int main(void)
TEST(OakEngineSync, Main)
{
make_tmpdir();
// Sandbox the config/cache/data locations (see oakengine_init_test).
#if !defined(_WIN32)
assert(setenv("XDG_CONFIG_HOME", g_tmpdir, 1) == 0);
assert(setenv("XDG_CACHE_HOME", g_tmpdir, 1) == 0);
assert(setenv("XDG_DATA_HOME", g_tmpdir, 1) == 0);
EXPECT_TRUE(setenv("XDG_CONFIG_HOME", g_tmpdir, 1) == 0);
EXPECT_TRUE(setenv("XDG_CACHE_HOME", g_tmpdir, 1) == 0);
EXPECT_TRUE(setenv("XDG_DATA_HOME", g_tmpdir, 1) == 0);
#endif
// HEADLESS is enough for the validation part.
assert(oakengine_init(OAKENGINE_INIT_HEADLESS) == OAKENGINE_OK);
EXPECT_TRUE(oakengine_init(OAKENGINE_INIT_HEADLESS) == OAKENGINE_OK);
OakEngineProject *project = oakengine_project_create();
assert(project != NULL);
assert(oakengine_project_new(project) == OAKENGINE_OK);
EXPECT_TRUE(project != NULL);
EXPECT_TRUE(oakengine_project_new(project) == OAKENGINE_OK);
OakEngineSequence *seq = oakengine_sequence_new(project, "Sync");
assert(seq != NULL);
EXPECT_TRUE(seq != NULL);
// 20 fps: one frame == one envelope window (1/20 s) exactly.
assert(oakengine_sequence_set_video_params(seq, -1, -1, 20, 1, -1, -1,
EXPECT_TRUE(oakengine_sequence_set_video_params(seq, -1, -1, 20, 1, -1, -1,
-1, -1, 1) == OAKENGINE_OK);
const QString noise_path = QDir(QString::fromUtf8(g_tmpdir))
@@ -234,26 +235,26 @@ int main(void)
// ---- Validation (no GL) -------------------------------------------
double offset_s = -1, confidence = -1, stretch = -1;
assert(oakengine_sync_estimate_offset(NULL, reference, target,
EXPECT_TRUE(oakengine_sync_estimate_offset(NULL, reference, target,
&offset_s,
&confidence) == OAKENGINE_E_INVALID);
assert(oakengine_sync_estimate_offset(seq, NULL, target, &offset_s,
EXPECT_TRUE(oakengine_sync_estimate_offset(seq, NULL, target, &offset_s,
&confidence) == OAKENGINE_E_INVALID);
assert(oakengine_sync_estimate_offset(seq, reference, NULL, &offset_s,
EXPECT_TRUE(oakengine_sync_estimate_offset(seq, reference, NULL, &offset_s,
&confidence) == OAKENGINE_E_INVALID);
assert(oakengine_sync_estimate_stretch_offset(NULL, reference, target,
EXPECT_TRUE(oakengine_sync_estimate_stretch_offset(NULL, reference, target,
&stretch, &offset_s,
&confidence) ==
OAKENGINE_E_INVALID);
char err[256];
assert(oakengine_sync_last_error(err, sizeof(err)) > 0);
EXPECT_TRUE(oakengine_sync_last_error(err, sizeof(err)) > 0);
// Valid handles but the engine lacks the RENDER bit: OAKENGINE_E_STATE
// with a readable reason, nothing else changed.
assert(oakengine_sync_estimate_offset(seq, reference, target, &offset_s,
EXPECT_TRUE(oakengine_sync_estimate_offset(seq, reference, target, &offset_s,
&confidence) == OAKENGINE_E_STATE);
assert(oakengine_sync_last_error(err, sizeof(err)) > 0);
assert(strstr(err, "OAKENGINE_INIT_RENDER") != NULL);
EXPECT_TRUE(oakengine_sync_last_error(err, sizeof(err)) > 0);
EXPECT_TRUE(strstr(err, "OAKENGINE_INIT_RENDER") != NULL);
// ---- GL-gated estimation ------------------------------------------
if (!is_render_backend_available(QStringLiteral("opengl"))) {
@@ -261,19 +262,19 @@ int main(void)
"available, estimation assertions skipped\n");
oakengine_project_free(project);
oakengine_shutdown();
return 0;
return;
}
if (!worker_binary_exists()) {
printf("oakengine_sync_test: SKIP: oak-render-worker binary not "
"found, estimation assertions skipped\n");
oakengine_project_free(project);
oakengine_shutdown();
return 0;
return;
}
olive::Config::current()[QStringLiteral("GraphicsBackend")] =
QStringLiteral("opengl");
assert(oakengine_init(OAKENGINE_INIT_HEADLESS | OAKENGINE_INIT_RENDER) ==
EXPECT_TRUE(oakengine_init(OAKENGINE_INIT_HEADLESS | OAKENGINE_INIT_RENDER) ==
OAKENGINE_OK);
// The target's content starts k_offset_frames later in the source:
@@ -293,22 +294,20 @@ int main(void)
fprintf(stderr, "DEBUG est_rc=%d off=%f conf=%f err='%s'\n", est_rc,
offset_s, confidence, est_err);
}
assert(est_rc == OAKENGINE_OK);
assert(fabs(fabs(offset_s) - expected_s) < tolerance_s);
assert(offset_s < 0.0); // the target is delayed: it must move earlier
assert(confidence > 0.0 && confidence <= 1.0);
EXPECT_TRUE(est_rc == OAKENGINE_OK);
EXPECT_TRUE(fabs(fabs(offset_s) - expected_s) < tolerance_s);
EXPECT_TRUE(offset_s < 0.0); // the target is delayed: it must move earlier
EXPECT_TRUE(confidence > 0.0 && confidence <= 1.0);
// Same-speed content: the stretch estimator reports rate ~1 and the
// same offset.
const int str_rc = oakengine_sync_estimate_stretch_offset(
seq, reference, target, &stretch, &offset_s, &confidence);
assert(str_rc == OAKENGINE_OK);
assert(fabs(stretch - 1.0) < 0.01);
assert(fabs(fabs(offset_s) - expected_s) < tolerance_s);
EXPECT_TRUE(str_rc == OAKENGINE_OK);
EXPECT_TRUE(fabs(stretch - 1.0) < 0.01);
EXPECT_TRUE(fabs(fabs(offset_s) - expected_s) < tolerance_s);
oakengine_project_free(project);
assert(oakengine_shutdown() == OAKENGINE_OK);
EXPECT_TRUE(oakengine_shutdown() == OAKENGINE_OK);
printf("oakengine_sync_test: all assertions passed\n");
return 0;
}
+110 -110
View File
@@ -22,6 +22,7 @@
// (oakengine/task.h and oakengine/undo.h). Runs headless; no GPU required.
#include <assert.h>
#include <gtest/gtest.h>
#include <stdio.h>
#include <string.h>
#include <thread>
@@ -67,66 +68,66 @@ static void task_event_cb(const oakengine_event *event, void *userdata)
static void test_manager_no_engine(void)
{
assert(oakengine_task_manager_handle() == NULL);
assert(oakengine_task_manager_count() == OAKENGINE_E_INVALID);
assert(oakengine_task_manager_first() == NULL);
assert(oakengine_task_manager_add(NULL) == OAKENGINE_E_INVALID);
assert(oakengine_task_manager_cancel(NULL) == OAKENGINE_E_INVALID);
EXPECT_TRUE(oakengine_task_manager_handle() == NULL);
EXPECT_TRUE(oakengine_task_manager_count() == OAKENGINE_E_INVALID);
EXPECT_TRUE(oakengine_task_manager_first() == NULL);
EXPECT_TRUE(oakengine_task_manager_add(NULL) == OAKENGINE_E_INVALID);
EXPECT_TRUE(oakengine_task_manager_cancel(NULL) == OAKENGINE_E_INVALID);
}
static void test_manager_empty(void)
{
void *mgr = oakengine_task_manager_handle();
assert(mgr != NULL);
assert(oakengine_task_manager_count() == 0);
assert(oakengine_task_manager_first() == NULL);
EXPECT_TRUE(mgr != NULL);
EXPECT_TRUE(oakengine_task_manager_count() == 0);
EXPECT_TRUE(oakengine_task_manager_first() == NULL);
}
static void test_task_null(void)
{
char buf[64];
assert(oakengine_task_title(NULL, buf, sizeof(buf)) == OAKENGINE_E_INVALID);
assert(oakengine_task_error(NULL, buf, sizeof(buf)) == OAKENGINE_E_INVALID);
assert(oakengine_task_start_time(NULL) == OAKENGINE_E_INVALID);
assert(oakengine_task_is_cancelled(NULL) == OAKENGINE_E_INVALID);
assert(oakengine_task_cancel(NULL) == OAKENGINE_E_INVALID);
assert(oakengine_task_start_sync(NULL) == OAKENGINE_E_INVALID);
assert(oakengine_task_free(NULL) == OAKENGINE_E_INVALID);
EXPECT_TRUE(oakengine_task_title(NULL, buf, sizeof(buf)) == OAKENGINE_E_INVALID);
EXPECT_TRUE(oakengine_task_error(NULL, buf, sizeof(buf)) == OAKENGINE_E_INVALID);
EXPECT_TRUE(oakengine_task_start_time(NULL) == OAKENGINE_E_INVALID);
EXPECT_TRUE(oakengine_task_is_cancelled(NULL) == OAKENGINE_E_INVALID);
EXPECT_TRUE(oakengine_task_cancel(NULL) == OAKENGINE_E_INVALID);
EXPECT_TRUE(oakengine_task_start_sync(NULL) == OAKENGINE_E_INVALID);
EXPECT_TRUE(oakengine_task_free(NULL) == OAKENGINE_E_INVALID);
}
static void test_import_error_path(void)
{
OakEngineProject *p = oakengine_project_create();
assert(p != NULL);
assert(oakengine_project_new(p) == OAKENGINE_OK);
EXPECT_TRUE(p != NULL);
EXPECT_TRUE(oakengine_project_new(p) == OAKENGINE_OK);
OakEngineNode *root = oakengine_project_root(p);
assert(root != NULL);
EXPECT_TRUE(root != NULL);
// Empty URL list is rejected at creation.
OakEngineTask *task = oakengine_task_create_project_import(root, NULL, 0);
assert(task == NULL);
EXPECT_TRUE(task == NULL);
// Valid creation but with non-existent file gives zero footage/one invalid.
const char *url = "file:///this/file/does/not/exist.mov";
task = oakengine_task_create_project_import(root, &url, 1);
assert(task != NULL);
EXPECT_TRUE(task != NULL);
assert(oakengine_task_import_file_count(task) == 1);
EXPECT_TRUE(oakengine_task_import_file_count(task) == 1);
int result = oakengine_task_start_sync(task);
(void) result;
assert(oakengine_task_import_footage_count(task) == 0);
assert(oakengine_task_import_invalid_files_count(task) == 1);
EXPECT_TRUE(oakengine_task_import_footage_count(task) == 0);
EXPECT_TRUE(oakengine_task_import_invalid_files_count(task) == 1);
char buf[256];
int len = oakengine_task_import_invalid_file_at(task, 0, buf, sizeof(buf));
assert(len > 0);
assert(strstr(buf, "exist.mov") != NULL);
assert(oakengine_task_import_invalid_file_at(task, 0, NULL, 0) == len);
assert(oakengine_task_import_invalid_file_at(task, 1, buf, sizeof(buf)) ==
EXPECT_TRUE(len > 0);
EXPECT_TRUE(strstr(buf, "exist.mov") != NULL);
EXPECT_TRUE(oakengine_task_import_invalid_file_at(task, 0, NULL, 0) == len);
EXPECT_TRUE(oakengine_task_import_invalid_file_at(task, 1, buf, sizeof(buf)) ==
OAKENGINE_E_INVALID);
assert(oakengine_task_free(task) == OAKENGINE_OK);
EXPECT_TRUE(oakengine_task_free(task) == OAKENGINE_OK);
oakengine_project_free(p);
}
@@ -134,31 +135,31 @@ static void test_load_task_sync(void)
{
OakEngineTask *task =
oakengine_task_create_project_load("/no/such/project.ove");
assert(task != NULL);
EXPECT_TRUE(task != NULL);
// No event subscription here; just confirm it reports failure cleanly.
int ok = oakengine_task_start_sync(task);
assert(ok == 0);
EXPECT_TRUE(ok == 0);
char err[256];
int len = oakengine_task_error(task, err, sizeof(err));
assert(len > 0);
EXPECT_TRUE(len > 0);
assert(oakengine_task_free(task) == OAKENGINE_OK);
EXPECT_TRUE(oakengine_task_free(task) == OAKENGINE_OK);
}
static void test_task_events(void)
{
OakEngineProject *p = oakengine_project_create();
assert(p != NULL);
assert(oakengine_project_new(p) == OAKENGINE_OK);
EXPECT_TRUE(p != NULL);
EXPECT_TRUE(oakengine_project_new(p) == OAKENGINE_OK);
OakEngineNode *root = oakengine_project_root(p);
assert(root != NULL);
EXPECT_TRUE(root != NULL);
const char *url = "file:///this/file/does/not/exist.mov";
OakEngineTask *task =
oakengine_task_create_project_import(root, &url, 1);
assert(task != NULL);
EXPECT_TRUE(task != NULL);
void *mgr = oakengine_task_manager_handle();
int64_t sub_started = oakengine_event_subscribe(
@@ -172,16 +173,16 @@ static void test_task_events(void)
int64_t sub_removed = oakengine_event_subscribe(
mgr, OAKENGINE_EVENT_TASK_MANAGER_TASK_REMOVED, task_event_cb, NULL);
assert(sub_started > 0);
assert(sub_progress > 0);
assert(sub_finished > 0);
assert(sub_added > 0);
assert(sub_removed > 0);
EXPECT_TRUE(sub_started > 0);
EXPECT_TRUE(sub_progress > 0);
EXPECT_TRUE(sub_finished > 0);
EXPECT_TRUE(sub_added > 0);
EXPECT_TRUE(sub_removed > 0);
g_task_started = g_task_progress = g_task_finished = 0;
g_task_succeeded = g_manager_added = g_manager_removed = 0;
assert(oakengine_task_manager_add(task) == OAKENGINE_OK);
EXPECT_TRUE(oakengine_task_manager_add(task) == OAKENGINE_OK);
// Wait for the task to finish. Manager tasks run on a worker thread and
// emit events on that thread; spin briefly until the finished event fires.
@@ -189,16 +190,16 @@ static void test_task_events(void)
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
assert(g_manager_added == 1);
assert(g_task_started == 1);
assert(g_task_finished == 1);
EXPECT_TRUE(g_manager_added == 1);
EXPECT_TRUE(g_task_started == 1);
EXPECT_TRUE(g_task_finished == 1);
// The import task may succeed even when all files are invalid; the event
// payload only reports Task::finished() success, which is implementation
// dependent. We only verify the event fired and had a boolean value.
assert(g_task_succeeded == 0 || g_task_succeeded == 1);
EXPECT_TRUE(g_task_succeeded == 0 || g_task_succeeded == 1);
// Cancel returns OK whether the task is still running or already done.
assert(oakengine_task_manager_cancel(task) == OAKENGINE_OK);
EXPECT_TRUE(oakengine_task_manager_cancel(task) == OAKENGINE_OK);
oakengine_event_unsubscribe(sub_started);
oakengine_event_unsubscribe(sub_progress);
@@ -211,43 +212,43 @@ static void test_task_events(void)
static void test_undo_round_trip(void)
{
assert(oakengine_undo_handle() != NULL);
assert(oakengine_undo_count() == 1); // the empty "New Project" entry
assert(oakengine_undo_index() == 1);
assert(oakengine_undo_can_undo() == 0);
assert(oakengine_undo_can_redo() == 0);
EXPECT_TRUE(oakengine_undo_handle() != NULL);
EXPECT_TRUE(oakengine_undo_count() == 1); // the empty "New Project" entry
EXPECT_TRUE(oakengine_undo_index() == 1);
EXPECT_TRUE(oakengine_undo_can_undo() == 0);
EXPECT_TRUE(oakengine_undo_can_redo() == 0);
char text[256];
int len = oakengine_undo_command_text(0, text, sizeof(text));
assert(len > 0);
EXPECT_TRUE(len > 0);
// Push a custom no-op command with a user-visible label.
void *cmd = oakengine_undo_command_create(
"Internal Name", NULL, NULL, NULL, NULL);
assert(cmd != NULL);
assert(oakengine_undo_push(cmd, "Test Command") == OAKENGINE_OK);
assert(oakengine_undo_count() == 2);
assert(oakengine_undo_index() == 2);
assert(oakengine_undo_can_undo() == 1);
EXPECT_TRUE(cmd != NULL);
EXPECT_TRUE(oakengine_undo_push(cmd, "Test Command") == OAKENGINE_OK);
EXPECT_TRUE(oakengine_undo_count() == 2);
EXPECT_TRUE(oakengine_undo_index() == 2);
EXPECT_TRUE(oakengine_undo_can_undo() == 1);
len = oakengine_undo_command_text(1, text, sizeof(text));
assert(len > 0);
assert(strstr(text, "Test Command") != NULL);
EXPECT_TRUE(len > 0);
EXPECT_TRUE(strstr(text, "Test Command") != NULL);
assert(oakengine_undo_command_is_done(1) == 1);
assert(oakengine_undo_jump(1) == OAKENGINE_OK);
assert(oakengine_undo_index() == 1);
assert(oakengine_undo_can_redo() == 1);
assert(oakengine_undo_command_is_done(1) == 0);
EXPECT_TRUE(oakengine_undo_command_is_done(1) == 1);
EXPECT_TRUE(oakengine_undo_jump(1) == OAKENGINE_OK);
EXPECT_TRUE(oakengine_undo_index() == 1);
EXPECT_TRUE(oakengine_undo_can_redo() == 1);
EXPECT_TRUE(oakengine_undo_command_is_done(1) == 0);
assert(oakengine_undo_jump(2) == OAKENGINE_OK);
assert(oakengine_undo_index() == 2);
assert(oakengine_undo_can_undo() == 1);
EXPECT_TRUE(oakengine_undo_jump(2) == OAKENGINE_OK);
EXPECT_TRUE(oakengine_undo_index() == 2);
EXPECT_TRUE(oakengine_undo_can_undo() == 1);
assert(oakengine_undo_clear() == OAKENGINE_OK);
assert(oakengine_undo_count() == 1);
assert(oakengine_undo_index() == 1);
assert(oakengine_undo_can_undo() == 0);
EXPECT_TRUE(oakengine_undo_clear() == OAKENGINE_OK);
EXPECT_TRUE(oakengine_undo_count() == 1);
EXPECT_TRUE(oakengine_undo_index() == 1);
EXPECT_TRUE(oakengine_undo_can_undo() == 0);
}
static void test_custom_command_multi(void)
@@ -264,31 +265,31 @@ static void test_custom_command_multi(void)
[](void *ud) { (void) ud; g_undo++; },
[](void *ud) { (void) ud; g_free++; },
NULL);
assert(cmd != NULL);
EXPECT_TRUE(cmd != NULL);
assert(oakengine_undo_command_redo_now(cmd) == OAKENGINE_OK);
assert(g_redo == 1);
assert(g_undo == 0);
EXPECT_TRUE(oakengine_undo_command_redo_now(cmd) == OAKENGINE_OK);
EXPECT_TRUE(g_redo == 1);
EXPECT_TRUE(g_undo == 0);
assert(oakengine_undo_command_undo_now(cmd) == OAKENGINE_OK);
assert(g_undo == 1);
EXPECT_TRUE(oakengine_undo_command_undo_now(cmd) == OAKENGINE_OK);
EXPECT_TRUE(g_undo == 1);
void *multi = oakengine_undo_command_create_multi();
assert(multi != NULL);
assert(oakengine_undo_command_multi_child_count(multi) == 0);
assert(oakengine_undo_command_multi_add_child(multi, cmd) == OAKENGINE_OK);
assert(oakengine_undo_command_multi_child_count(multi) == 1);
assert(oakengine_undo_command_multi_add_child(multi, NULL) ==
EXPECT_TRUE(multi != NULL);
EXPECT_TRUE(oakengine_undo_command_multi_child_count(multi) == 0);
EXPECT_TRUE(oakengine_undo_command_multi_add_child(multi, cmd) == OAKENGINE_OK);
EXPECT_TRUE(oakengine_undo_command_multi_child_count(multi) == 1);
EXPECT_TRUE(oakengine_undo_command_multi_add_child(multi, NULL) ==
OAKENGINE_E_INVALID);
assert(oakengine_undo_command_multi_add_child(NULL, cmd) ==
EXPECT_TRUE(oakengine_undo_command_multi_add_child(NULL, cmd) ==
OAKENGINE_E_INVALID);
// Remove the child from the multi-command and free it directly to verify
// the custom command's free callback. (MultiUndoCommand does not own its
// children, so freeing the multi-command alone would leak the child.)
assert(oakengine_undo_command_multi_child_count(multi) == 1);
EXPECT_TRUE(oakengine_undo_command_multi_child_count(multi) == 1);
oakengine_undo_command_free(cmd);
assert(g_free == 1);
EXPECT_TRUE(g_free == 1);
// The now-empty multi-command can be freed safely.
oakengine_undo_command_free(multi);
@@ -299,23 +300,23 @@ static void test_custom_command_multi(void)
static void test_save_task_creation(void)
{
OakEngineProject *p = oakengine_project_create();
assert(p != NULL);
assert(oakengine_project_new(p) == OAKENGINE_OK);
EXPECT_TRUE(p != NULL);
EXPECT_TRUE(oakengine_project_new(p) == OAKENGINE_OK);
// Create a save task with NULL override and NULL layout.
OakEngineTask *task = oakengine_task_create_project_save(
p, 1, NULL, NULL);
assert(task != NULL);
EXPECT_TRUE(task != NULL);
// task_save_get_project should return the project we passed.
assert(oakengine_task_save_get_project(task) == p);
assert(oakengine_task_save_get_project(NULL) == NULL);
EXPECT_TRUE(oakengine_task_save_get_project(task) == p);
EXPECT_TRUE(oakengine_task_save_get_project(NULL) == NULL);
// Running sync on an untitled project: may succeed or fail gracefully.
int ok = oakengine_task_start_sync(task);
(void) ok; // must not crash
assert(oakengine_task_free(task) == OAKENGINE_OK);
EXPECT_TRUE(oakengine_task_free(task) == OAKENGINE_OK);
oakengine_project_free(p);
}
@@ -328,7 +329,7 @@ static void test_other_task_creation(void)
OakEngineTask *task = oakengine_task_create_project_load_otio(
"/nonexistent.otio");
if (task != NULL) {
assert(oakengine_task_free(task) == OAKENGINE_OK);
EXPECT_TRUE(oakengine_task_free(task) == OAKENGINE_OK);
}
// OTIO save: same.
@@ -336,10 +337,10 @@ static void test_other_task_creation(void)
// Passing NULL project may return NULL.
// Export: NULL sequence, NULL params.
assert(oakengine_task_create_export(NULL, NULL) == NULL);
EXPECT_TRUE(oakengine_task_create_export(NULL, NULL) == NULL);
// Proxy: NULL footage.
assert(oakengine_task_create_proxy(NULL) == NULL);
EXPECT_TRUE(oakengine_task_create_proxy(NULL) == NULL);
}
// ---- Import result accessors (extending test_import_error_path) --------------
@@ -347,24 +348,24 @@ static void test_other_task_creation(void)
static void test_import_result_accessors(void)
{
OakEngineProject *p = oakengine_project_create();
assert(p != NULL);
assert(oakengine_project_new(p) == OAKENGINE_OK);
EXPECT_TRUE(p != NULL);
EXPECT_TRUE(oakengine_project_new(p) == OAKENGINE_OK);
OakEngineNode *root = oakengine_project_root(p);
assert(root != NULL);
EXPECT_TRUE(root != NULL);
const char *url = "file:///this/file/does/not/exist.mov";
OakEngineTask *task = oakengine_task_create_project_import(root, &url, 1);
assert(task != NULL);
EXPECT_TRUE(task != NULL);
int ok = oakengine_task_start_sync(task);
assert(ok == 0 || ok == 1);
EXPECT_TRUE(ok == 0 || ok == 1);
(void) ok;
// Import of invalid file: footage_at should return 0/NULL.
assert(oakengine_task_import_footage_count(task) == 0);
assert(oakengine_task_import_footage_at(task, 0) == NULL);
assert(oakengine_task_import_footage_at(task, -1) == NULL);
assert(oakengine_task_import_footage_at(NULL, 0) == NULL);
EXPECT_TRUE(oakengine_task_import_footage_count(task) == 0);
EXPECT_TRUE(oakengine_task_import_footage_at(task, 0) == NULL);
EXPECT_TRUE(oakengine_task_import_footage_at(task, -1) == NULL);
EXPECT_TRUE(oakengine_task_import_footage_at(NULL, 0) == NULL);
// import_get_command: should be non-NULL (the import built a command
// even when all files failed) or NULL (no data to build).
@@ -373,7 +374,7 @@ static void test_import_result_accessors(void)
oakengine_undo_command_free(cmd);
}
assert(oakengine_task_free(task) == OAKENGINE_OK);
EXPECT_TRUE(oakengine_task_free(task) == OAKENGINE_OK);
oakengine_project_free(p);
}
@@ -382,18 +383,18 @@ static void test_import_result_accessors(void)
static void test_undo_actions(void)
{
// update_actions should not crash.
assert(oakengine_undo_update_actions() == OAKENGINE_OK);
EXPECT_TRUE(oakengine_undo_update_actions() == OAKENGINE_OK);
// undo_action / redo_action return QAction* as void* (may be NULL).
oakengine_undo_undo_action();
oakengine_undo_redo_action();
}
int main(void)
TEST(OakEngineTask, Main)
{
test_manager_no_engine();
assert(oakengine_init(OAKENGINE_INIT_HEADLESS) == OAKENGINE_OK);
EXPECT_TRUE(oakengine_init(OAKENGINE_INIT_HEADLESS) == OAKENGINE_OK);
test_manager_empty();
test_task_null();
@@ -407,6 +408,5 @@ int main(void)
test_import_result_accessors();
test_undo_actions();
assert(oakengine_shutdown() == OAKENGINE_OK);
return 0;
EXPECT_TRUE(oakengine_shutdown() == OAKENGINE_OK);
}
File diff suppressed because it is too large Load Diff
+72 -73
View File
@@ -28,6 +28,7 @@
// needed.
#include <assert.h>
#include <gtest/gtest.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -51,13 +52,13 @@ static void make_tmpdir(void)
#if defined(_WIN32)
char base[MAX_PATH];
const DWORD len = GetTempPathA(MAX_PATH, base);
assert(len > 0 && len < MAX_PATH);
EXPECT_TRUE(len > 0 && len < MAX_PATH);
snprintf(g_tmpdir, sizeof(g_tmpdir), "%soakengine_traverse_test_%lu", base,
(unsigned long)GetCurrentProcessId());
assert(_mkdir(g_tmpdir) == 0);
EXPECT_TRUE(_mkdir(g_tmpdir) == 0);
#else
strcpy(g_tmpdir, "/tmp/oakengine_traverse_test_XXXXXX");
assert(mkdtemp(g_tmpdir) != NULL);
EXPECT_TRUE(mkdtemp(g_tmpdir) != NULL);
#endif
}
@@ -67,46 +68,46 @@ static void test_null_robustness(OakEngineNode *solid)
{
double m[6];
assert(oakengine_traverse_generate_database(NULL, 0, 1, 1, 1) == NULL);
assert(oakengine_traverse_generate_table(NULL, 0, 1, 1, 1) == NULL);
EXPECT_TRUE(oakengine_traverse_generate_database(NULL, 0, 1, 1, 1) == NULL);
EXPECT_TRUE(oakengine_traverse_generate_table(NULL, 0, 1, 1, 1) == NULL);
// Zero denominators are invalid rationals.
assert(oakengine_traverse_generate_database(solid, 0, 0, 1, 1) == NULL);
assert(oakengine_traverse_generate_table(solid, 0, 1, 1, 0) == NULL);
EXPECT_TRUE(oakengine_traverse_generate_database(solid, 0, 0, 1, 1) == NULL);
EXPECT_TRUE(oakengine_traverse_generate_table(solid, 0, 1, 1, 0) == NULL);
oakengine_traverse_db_free(NULL); // no-op
assert(oakengine_traverse_db_input_count(NULL) == 0);
assert(oakengine_traverse_db_input_id(NULL, 0) == NULL);
assert(oakengine_traverse_db_row_count(NULL, 0) == 0);
assert(oakengine_traverse_row_type(NULL, 0, 0) == OAK_NODE_VALUE_NONE);
assert(oakengine_traverse_row_source(NULL, 0, 0) == NULL);
assert(oakengine_traverse_row_tag(NULL, 0, 0) != NULL); // never NULL
assert(oakengine_traverse_row_value_string(NULL, 0, 0) == NULL);
assert(oakengine_traverse_row_split_count(NULL, 0, 0) == 0);
assert(oakengine_traverse_row_split_string(NULL, 0, 0, 0) == NULL);
EXPECT_TRUE(oakengine_traverse_db_input_count(NULL) == 0);
EXPECT_TRUE(oakengine_traverse_db_input_id(NULL, 0) == NULL);
EXPECT_TRUE(oakengine_traverse_db_row_count(NULL, 0) == 0);
EXPECT_TRUE(oakengine_traverse_row_type(NULL, 0, 0) == OAK_NODE_VALUE_NONE);
EXPECT_TRUE(oakengine_traverse_row_source(NULL, 0, 0) == NULL);
EXPECT_TRUE(oakengine_traverse_row_tag(NULL, 0, 0) != NULL); // never NULL
EXPECT_TRUE(oakengine_traverse_row_value_string(NULL, 0, 0) == NULL);
EXPECT_TRUE(oakengine_traverse_row_split_count(NULL, 0, 0) == 0);
EXPECT_TRUE(oakengine_traverse_row_split_string(NULL, 0, 0, 0) == NULL);
assert(oakengine_traverse_table_element_index_for_hint(NULL, "x", -1,
EXPECT_TRUE(oakengine_traverse_table_element_index_for_hint(NULL, "x", -1,
NULL) == -1);
// generate_row: the C side can only exercise the error paths -- the
// real output is an olive::NodeValueRow (a C++ QHash typedef), which a
// pure C test cannot allocate. The filled-row path is covered by the
// application (the viewer display gizmo drag-start path).
assert(oakengine_traverse_generate_row(NULL, 0, 1, 1, 1, NULL, 0, 0,
EXPECT_TRUE(oakengine_traverse_generate_row(NULL, 0, 1, 1, 1, NULL, 0, 0,
(void *)1) == OAKENGINE_E_INVALID);
assert(oakengine_traverse_generate_row(solid, 0, 1, 1, 1, NULL, 0, 0,
EXPECT_TRUE(oakengine_traverse_generate_row(solid, 0, 1, 1, 1, NULL, 0, 0,
NULL) == OAKENGINE_E_INVALID);
assert(oakengine_traverse_transform(NULL, solid, 0, 1, 1, 1, NULL, m) ==
EXPECT_TRUE(oakengine_traverse_transform(NULL, solid, 0, 1, 1, 1, NULL, m) ==
OAKENGINE_E_INVALID);
assert(oakengine_traverse_transform(solid, NULL, 0, 1, 1, 1, NULL, m) ==
EXPECT_TRUE(oakengine_traverse_transform(solid, NULL, 0, 1, 1, 1, NULL, m) ==
OAKENGINE_E_INVALID);
assert(oakengine_traverse_transform(solid, solid, 0, 1, 1, 1, NULL,
EXPECT_TRUE(oakengine_traverse_transform(solid, solid, 0, 1, 1, 1, NULL,
NULL) == OAKENGINE_E_INVALID);
assert(oakengine_node_set_value_hint(NULL, "x", -1, OAK_NODE_VALUE_COLOR,
EXPECT_TRUE(oakengine_node_set_value_hint(NULL, "x", -1, OAK_NODE_VALUE_COLOR,
0, NULL) == OAKENGINE_E_INVALID);
assert(oakengine_node_set_value_hint(solid, NULL, -1,
EXPECT_TRUE(oakengine_node_set_value_hint(solid, NULL, -1,
OAK_NODE_VALUE_COLOR, 0,
NULL) == OAKENGINE_E_INVALID);
}
@@ -119,58 +120,58 @@ static void test_database(OakEngineNode *solid)
OakEngineTraverseDb *db =
oakengine_traverse_generate_database(solid, 0, 1, 1, 1);
assert(db != NULL);
EXPECT_TRUE(db != NULL);
// One entry per node input, in the node's input order (deterministic).
const int node_inputs = oakengine_node_input_count(solid);
assert(node_inputs >= 2);
assert(oakengine_traverse_db_input_count(db) == node_inputs);
EXPECT_TRUE(node_inputs >= 2);
EXPECT_TRUE(oakengine_traverse_db_input_count(db) == node_inputs);
for (int i = 0; i < node_inputs; i++) {
assert(oakengine_node_input_id(solid, i, buf, sizeof(buf)) > 0);
EXPECT_TRUE(oakengine_node_input_id(solid, i, buf, sizeof(buf)) > 0);
const char *id = oakengine_traverse_db_input_id(db, i);
assert(id != NULL);
assert(strcmp(id, buf) == 0);
EXPECT_TRUE(id != NULL);
EXPECT_TRUE(strcmp(id, buf) == 0);
// Every input of an unconnected Solid generator produces one row
// (its standard value).
const int rows = oakengine_traverse_db_row_count(db, i);
assert(rows >= 1);
EXPECT_TRUE(rows >= 1);
for (int r = 0; r < rows; r++) {
// Type is a valid facade value type for plain inputs
// (enabled_in is BOOL, color_in is COLOR).
const int type = oakengine_traverse_row_type(db, i, r);
assert(type > OAK_NODE_VALUE_NONE);
EXPECT_TRUE(type > OAK_NODE_VALUE_NONE);
// Source: the value's originating node, or NULL; the solid's
// standard values are sourced from the node itself.
OakEngineNode *src = oakengine_traverse_row_source(db, i, r);
assert(src == NULL || src == solid);
EXPECT_TRUE(src == NULL || src == solid);
// Tag may be empty but never NULL.
assert(oakengine_traverse_row_tag(db, i, r) != NULL);
EXPECT_TRUE(oakengine_traverse_row_tag(db, i, r) != NULL);
// Value string is non-empty for these value types.
const char *vs = oakengine_traverse_row_value_string(db, i, r);
assert(vs != NULL);
assert(vs[0] != '\0');
EXPECT_TRUE(vs != NULL);
EXPECT_TRUE(vs[0] != '\0');
// Split values: at least one track, each with a string.
const int splits = oakengine_traverse_row_split_count(db, i, r);
assert(splits >= 1);
EXPECT_TRUE(splits >= 1);
for (int s = 0; s < splits; s++) {
assert(oakengine_traverse_row_split_string(db, i, r, s) !=
EXPECT_TRUE(oakengine_traverse_row_split_string(db, i, r, s) !=
NULL);
}
assert(oakengine_traverse_row_split_string(db, i, r, splits) ==
EXPECT_TRUE(oakengine_traverse_row_split_string(db, i, r, splits) ==
NULL);
}
}
// Out-of-range accessors fail cleanly.
assert(oakengine_traverse_db_input_id(db, node_inputs) == NULL);
assert(oakengine_traverse_db_row_count(db, node_inputs) == 0);
assert(oakengine_traverse_row_type(db, node_inputs, 0) ==
EXPECT_TRUE(oakengine_traverse_db_input_id(db, node_inputs) == NULL);
EXPECT_TRUE(oakengine_traverse_db_row_count(db, node_inputs) == 0);
EXPECT_TRUE(oakengine_traverse_row_type(db, node_inputs, 0) ==
OAK_NODE_VALUE_NONE);
// A multi-entry database is not a generate_table result: the hint
// lookup rejects it.
assert(oakengine_traverse_table_element_index_for_hint(solid, "color_in",
EXPECT_TRUE(oakengine_traverse_table_element_index_for_hint(solid, "color_in",
-1, db) == -1);
oakengine_traverse_db_free(db);
@@ -182,35 +183,35 @@ static void test_table_and_hints(OakEngineNode *solid, OakEngineNode *lut)
{
OakEngineTraverseDb *db =
oakengine_traverse_generate_table(solid, 0, 1, 1, 1);
assert(db != NULL);
assert(oakengine_traverse_db_input_count(db) == 1);
EXPECT_TRUE(db != NULL);
EXPECT_TRUE(oakengine_traverse_db_input_count(db) == 1);
// The single output table is keyed by an empty input id.
const char *id = oakengine_traverse_db_input_id(db, 0);
assert(id != NULL);
assert(id[0] == '\0');
assert(oakengine_traverse_db_row_count(db, 0) >= 1);
EXPECT_TRUE(id != NULL);
EXPECT_TRUE(id[0] == '\0');
EXPECT_TRUE(oakengine_traverse_db_row_count(db, 0) >= 1);
// set_value_hint: unknown input ids and bogus types are rejected.
assert(oakengine_node_set_value_hint(solid, "not_an_input", -1,
EXPECT_TRUE(oakengine_node_set_value_hint(solid, "not_an_input", -1,
OAK_NODE_VALUE_COLOR, 0,
NULL) == OAKENGINE_E_NOT_FOUND);
assert(oakengine_node_set_value_hint(solid, "color_in", -1, 999, 0,
EXPECT_TRUE(oakengine_node_set_value_hint(solid, "color_in", -1, 999, 0,
NULL) == OAKENGINE_E_INVALID);
// The solid's output table holds texture rows. A hint preferring COLOR
// values (set on the lut's texture input) matches nothing -> -1.
assert(oakengine_node_set_value_hint(lut, "tex_in", -1,
EXPECT_TRUE(oakengine_node_set_value_hint(lut, "tex_in", -1,
OAK_NODE_VALUE_COLOR, -1,
NULL) == OAKENGINE_OK);
assert(oakengine_traverse_table_element_index_for_hint(lut, "tex_in", -1,
EXPECT_TRUE(oakengine_traverse_table_element_index_for_hint(lut, "tex_in", -1,
db) == -1);
// An untyped hint falls back to the input's declared type (k_texture
// for "tex_in"), which does have a row in the table.
assert(oakengine_node_set_value_hint(lut, "tex_in", -1,
EXPECT_TRUE(oakengine_node_set_value_hint(lut, "tex_in", -1,
OAK_NODE_VALUE_NONE, -1,
NULL) == OAKENGINE_OK);
assert(oakengine_traverse_table_element_index_for_hint(lut, "tex_in", -1,
EXPECT_TRUE(oakengine_traverse_table_element_index_for_hint(lut, "tex_in", -1,
db) >= 0);
oakengine_traverse_db_free(db);
@@ -223,46 +224,46 @@ static void test_transform(OakEngineNode *solid, OakEngineNode *lut)
double m[6] = { 0, 0, 0, 0, 0, 0 };
// No transform-generating nodes between start and end: identity matrix.
assert(oakengine_traverse_transform(solid, solid, 0, 1, 1, 1, NULL, m) ==
EXPECT_TRUE(oakengine_traverse_transform(solid, solid, 0, 1, 1, 1, NULL, m) ==
OAKENGINE_OK);
assert(m[0] == 1.0 && m[1] == 0.0 && m[2] == 0.0 && m[3] == 1.0);
assert(m[4] == 0.0 && m[5] == 0.0);
EXPECT_TRUE(m[0] == 1.0 && m[1] == 0.0 && m[2] == 0.0 && m[3] == 1.0);
EXPECT_TRUE(m[4] == 0.0 && m[5] == 0.0);
// Same through an edge, with explicit cache params.
oak_video_params vp;
memset(&vp, 0, sizeof(vp));
assert(oakengine_video_params_make(&vp, 1920, 1080, 1001, 30000, 0, 1, 1,
EXPECT_TRUE(oakengine_video_params_make(&vp, 1920, 1080, 1001, 30000, 0, 1, 1,
0, 0, 1) == OAKENGINE_OK);
memset(m, 0, sizeof(m));
assert(oakengine_traverse_transform(solid, lut, 0, 1, 1, 1, &vp, m) ==
EXPECT_TRUE(oakengine_traverse_transform(solid, lut, 0, 1, 1, 1, &vp, m) ==
OAKENGINE_OK);
assert(m[0] == 1.0 && m[1] == 0.0 && m[2] == 0.0 && m[3] == 1.0);
assert(m[4] == 0.0 && m[5] == 0.0);
EXPECT_TRUE(m[0] == 1.0 && m[1] == 0.0 && m[2] == 0.0 && m[3] == 1.0);
EXPECT_TRUE(m[4] == 0.0 && m[5] == 0.0);
}
int main(void)
TEST(OakEngineTraverse, Main)
{
make_tmpdir();
// Sandbox the config/cache/data locations.
#if !defined(_WIN32)
assert(setenv("XDG_CONFIG_HOME", g_tmpdir, 1) == 0);
assert(setenv("XDG_CACHE_HOME", g_tmpdir, 1) == 0);
assert(setenv("XDG_DATA_HOME", g_tmpdir, 1) == 0);
EXPECT_TRUE(setenv("XDG_CONFIG_HOME", g_tmpdir, 1) == 0);
EXPECT_TRUE(setenv("XDG_CACHE_HOME", g_tmpdir, 1) == 0);
EXPECT_TRUE(setenv("XDG_DATA_HOME", g_tmpdir, 1) == 0);
#endif
assert(oakengine_init(OAKENGINE_INIT_HEADLESS) == OAKENGINE_OK);
EXPECT_TRUE(oakengine_init(OAKENGINE_INIT_HEADLESS) == OAKENGINE_OK);
OakEngineProject *project = oakengine_project_create();
assert(project != NULL);
assert(oakengine_project_new(project) == OAKENGINE_OK);
EXPECT_TRUE(project != NULL);
EXPECT_TRUE(oakengine_project_new(project) == OAKENGINE_OK);
OakEngineNode *solid = oakengine_project_add_node(
project, "org.olivevideoeditor.Olive.solidgenerator");
assert(solid != NULL);
EXPECT_TRUE(solid != NULL);
OakEngineNode *lut = oakengine_project_add_node(
project, "org.olivevideoeditor.Olive.ociolut");
assert(lut != NULL);
EXPECT_TRUE(lut != NULL);
test_null_robustness(solid);
test_database(solid);
@@ -270,8 +271,6 @@ int main(void)
test_transform(solid, lut);
oakengine_project_free(project);
assert(oakengine_shutdown() == OAKENGINE_OK);
EXPECT_TRUE(oakengine_shutdown() == OAKENGINE_OK);
printf("oakengine_traverse_test: all assertions passed\n");
return 0;
}
+151 -152
View File
@@ -27,6 +27,7 @@
// sequence real content length.
#include <assert.h>
#include <gtest/gtest.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -57,20 +58,20 @@ static void make_tmpdir(void)
#if defined(_WIN32)
char base[MAX_PATH];
const DWORD len = GetTempPathA(MAX_PATH, base);
assert(len > 0 && len < MAX_PATH);
EXPECT_TRUE(len > 0 && len < MAX_PATH);
snprintf(g_tmpdir, sizeof(g_tmpdir), "%soakengine_viewer_test_%lu", base,
(unsigned long)GetCurrentProcessId());
assert(_mkdir(g_tmpdir) == 0);
EXPECT_TRUE(_mkdir(g_tmpdir) == 0);
#else
strcpy(g_tmpdir, "/tmp/oakengine_viewer_test_XXXXXX");
assert(mkdtemp(g_tmpdir) != NULL);
EXPECT_TRUE(mkdtemp(g_tmpdir) != NULL);
#endif
}
static void demo_path(char *dst, size_t cap)
{
const int n = snprintf(dst, cap, "%s/tests/demo.mp4", OAK_TEST_SOURCE_DIR);
assert(n > 0 && (size_t)n < cap);
EXPECT_TRUE(n > 0 && (size_t)n < cap);
}
// A sequence handle is the same engine object pointer as its node handle
@@ -88,26 +89,26 @@ static void test_from_node(OakEngineProject *project, OakEngineSequence *seq)
OakEngineNode *seq_node = as_node(seq);
OakEngineNode *solid = oakengine_project_add_node(
project, "org.olivevideoeditor.Olive.solidgenerator");
assert(solid != NULL);
EXPECT_TRUE(solid != NULL);
assert(oakengine_viewer_from_node(NULL) == NULL);
assert(oakengine_viewer_from_node(solid) == NULL);
assert(oakengine_viewer_from_node(seq_node) == seq_node);
EXPECT_TRUE(oakengine_viewer_from_node(NULL) == NULL);
EXPECT_TRUE(oakengine_viewer_from_node(solid) == NULL);
EXPECT_TRUE(oakengine_viewer_from_node(seq_node) == seq_node);
assert(oakengine_viewer_from_const_node(NULL) == NULL);
assert(oakengine_viewer_from_const_node((const OakEngineNode *)solid) ==
EXPECT_TRUE(oakengine_viewer_from_const_node(NULL) == NULL);
EXPECT_TRUE(oakengine_viewer_from_const_node((const OakEngineNode *)solid) ==
NULL);
assert(oakengine_viewer_from_const_node((const OakEngineNode *)seq_node) ==
EXPECT_TRUE(oakengine_viewer_from_const_node((const OakEngineNode *)seq_node) ==
(const OakEngineNode *)seq_node);
// The input id constants are static, non-empty strings.
assert(oakengine_viewer_video_params_input_id() != NULL);
assert(oakengine_viewer_video_params_input_id()[0] != '\0');
assert(oakengine_viewer_audio_params_input_id()[0] != '\0');
assert(oakengine_viewer_subtitle_params_input_id()[0] != '\0');
assert(oakengine_viewer_texture_input_id()[0] != '\0');
assert(oakengine_viewer_samples_input_id()[0] != '\0');
assert(oakengine_viewer_default_sample_format() >= 0);
EXPECT_TRUE(oakengine_viewer_video_params_input_id() != NULL);
EXPECT_TRUE(oakengine_viewer_video_params_input_id()[0] != '\0');
EXPECT_TRUE(oakengine_viewer_audio_params_input_id()[0] != '\0');
EXPECT_TRUE(oakengine_viewer_subtitle_params_input_id()[0] != '\0');
EXPECT_TRUE(oakengine_viewer_texture_input_id()[0] != '\0');
EXPECT_TRUE(oakengine_viewer_samples_input_id()[0] != '\0');
EXPECT_TRUE(oakengine_viewer_default_sample_format() >= 0);
}
// ---- Playhead / length ------------------------------------------------------
@@ -116,35 +117,35 @@ static void test_playhead(OakEngineSequence *seq)
{
int64_t num = -1, den = -1;
assert(oakengine_viewer_get_playhead(NULL, &num, &den) ==
EXPECT_TRUE(oakengine_viewer_get_playhead(NULL, &num, &den) ==
OAKENGINE_E_INVALID);
assert(oakengine_viewer_get_playhead(as_node(seq), &num, &den) ==
EXPECT_TRUE(oakengine_viewer_get_playhead(as_node(seq), &num, &den) ==
OAKENGINE_OK);
assert(num == 0);
EXPECT_TRUE(num == 0);
assert(oakengine_viewer_set_playhead(NULL, 1, 1) == OAKENGINE_E_INVALID);
assert(oakengine_viewer_set_playhead(as_node(seq), 2, 1) == OAKENGINE_OK);
EXPECT_TRUE(oakengine_viewer_set_playhead(NULL, 1, 1) == OAKENGINE_E_INVALID);
EXPECT_TRUE(oakengine_viewer_set_playhead(as_node(seq), 2, 1) == OAKENGINE_OK);
num = den = -1;
assert(oakengine_viewer_get_playhead(as_node(seq), &num, &den) ==
EXPECT_TRUE(oakengine_viewer_get_playhead(as_node(seq), &num, &den) ==
OAKENGINE_OK);
assert(num == 2 && den == 1);
EXPECT_TRUE(num == 2 && den == 1);
}
static void test_lengths(OakEngineSequence *seq)
{
int64_t num = -1, den = -1;
assert(oakengine_viewer_get_length(NULL, &num, &den) ==
EXPECT_TRUE(oakengine_viewer_get_length(NULL, &num, &den) ==
OAKENGINE_E_INVALID);
assert(oakengine_viewer_get_length(as_node(seq), &num, &den) ==
EXPECT_TRUE(oakengine_viewer_get_length(as_node(seq), &num, &den) ==
OAKENGINE_OK);
assert(num == 0);
assert(oakengine_viewer_get_video_length(as_node(seq), &num, &den) ==
EXPECT_TRUE(num == 0);
EXPECT_TRUE(oakengine_viewer_get_video_length(as_node(seq), &num, &den) ==
OAKENGINE_OK);
assert(num == 0);
assert(oakengine_viewer_get_audio_length(as_node(seq), &num, &den) ==
EXPECT_TRUE(num == 0);
EXPECT_TRUE(oakengine_viewer_get_audio_length(as_node(seq), &num, &den) ==
OAKENGINE_OK);
assert(num == 0);
EXPECT_TRUE(num == 0);
}
// ---- Stream parameters --------------------------------------------------------
@@ -156,58 +157,58 @@ static void test_stream_params(OakEngineSequence *seq)
int sr = -1, format = -1;
uint64_t layout = 1;
assert(oakengine_viewer_get_video_params(NULL, 0, &vp) ==
EXPECT_TRUE(oakengine_viewer_get_video_params(NULL, 0, &vp) ==
OAKENGINE_E_INVALID);
assert(oakengine_viewer_get_video_params(node, 0, NULL) ==
EXPECT_TRUE(oakengine_viewer_get_video_params(node, 0, NULL) ==
OAKENGINE_E_INVALID);
// A fresh sequence has one video and one audio stream, no subtitles.
assert(oakengine_viewer_get_video_stream_count(NULL) == 0);
assert(oakengine_viewer_get_video_stream_count(node) == 1);
assert(oakengine_viewer_get_audio_stream_count(node) == 1);
assert(oakengine_viewer_get_subtitle_stream_count(node) == 0);
EXPECT_TRUE(oakengine_viewer_get_video_stream_count(NULL) == 0);
EXPECT_TRUE(oakengine_viewer_get_video_stream_count(node) == 1);
EXPECT_TRUE(oakengine_viewer_get_audio_stream_count(node) == 1);
EXPECT_TRUE(oakengine_viewer_get_subtitle_stream_count(node) == 0);
// In-range video params come from the sequence defaults.
assert(oakengine_viewer_get_video_params(node, 0, &vp) == OAKENGINE_OK);
assert(vp.width > 0 && vp.height > 0);
assert(vp.time_base_num > 0 && vp.time_base_den > 0);
EXPECT_TRUE(oakengine_viewer_get_video_params(node, 0, &vp) == OAKENGINE_OK);
EXPECT_TRUE(vp.width > 0 && vp.height > 0);
EXPECT_TRUE(vp.time_base_num > 0 && vp.time_base_den > 0);
// Out-of-range yields a zeroed struct (documented in viewer.h).
memset(&vp, 0xFF, sizeof(vp));
assert(oakengine_viewer_get_video_params(node, 99, &vp) == OAKENGINE_OK);
assert(vp.width == 0 && vp.height == 0);
EXPECT_TRUE(oakengine_viewer_get_video_params(node, 99, &vp) == OAKENGINE_OK);
EXPECT_TRUE(vp.width == 0 && vp.height == 0);
// Audio params; out-of-range yields 0/0/0.
assert(oakengine_viewer_get_audio_params(NULL, 0, &sr, &layout,
EXPECT_TRUE(oakengine_viewer_get_audio_params(NULL, 0, &sr, &layout,
&format) == OAKENGINE_E_INVALID);
assert(oakengine_viewer_get_audio_params(node, 0, &sr, &layout,
EXPECT_TRUE(oakengine_viewer_get_audio_params(node, 0, &sr, &layout,
&format) == OAKENGINE_OK);
assert(sr > 0 && layout != 0);
EXPECT_TRUE(sr > 0 && layout != 0);
sr = -1;
layout = 1;
format = -1;
assert(oakengine_viewer_get_audio_params(node, 99, &sr, &layout,
EXPECT_TRUE(oakengine_viewer_get_audio_params(node, 99, &sr, &layout,
&format) == OAKENGINE_OK);
assert(sr == 0 && layout == 0 && format == 0);
EXPECT_TRUE(sr == 0 && layout == 0 && format == 0);
// Per-stream enabled flags: video/audio stream 0 are enabled by
// default; subtitle has no stream 0.
assert(oakengine_viewer_get_stream_enabled(NULL, OAKENGINE_TRACK_TYPE_VIDEO,
EXPECT_TRUE(oakengine_viewer_get_stream_enabled(NULL, OAKENGINE_TRACK_TYPE_VIDEO,
0) == OAKENGINE_E_INVALID);
assert(oakengine_viewer_get_stream_enabled(node, OAKENGINE_TRACK_TYPE_VIDEO,
EXPECT_TRUE(oakengine_viewer_get_stream_enabled(node, OAKENGINE_TRACK_TYPE_VIDEO,
0) == 1);
assert(oakengine_viewer_get_stream_enabled(node, OAKENGINE_TRACK_TYPE_AUDIO,
EXPECT_TRUE(oakengine_viewer_get_stream_enabled(node, OAKENGINE_TRACK_TYPE_AUDIO,
0) == 1);
assert(oakengine_viewer_get_stream_enabled(
EXPECT_TRUE(oakengine_viewer_get_stream_enabled(
node, OAKENGINE_TRACK_TYPE_SUBTITLE, 0) == 0);
assert(oakengine_viewer_get_stream_enabled(node, 99, 0) ==
EXPECT_TRUE(oakengine_viewer_get_stream_enabled(node, 99, 0) ==
OAKENGINE_E_INVALID);
// Subtitle access: no subtitle streams on a fresh sequence.
assert(oakengine_viewer_get_subtitle_count(NULL, 0) == OAKENGINE_E_INVALID);
assert(oakengine_viewer_get_subtitle_count(node, 0) == OAKENGINE_E_INVALID);
assert(oakengine_viewer_get_subtitle_at(NULL, 0, 0) == NULL);
assert(oakengine_viewer_get_subtitle_at(node, 0, 0) == NULL);
EXPECT_TRUE(oakengine_viewer_get_subtitle_count(NULL, 0) == OAKENGINE_E_INVALID);
EXPECT_TRUE(oakengine_viewer_get_subtitle_count(node, 0) == OAKENGINE_E_INVALID);
EXPECT_TRUE(oakengine_viewer_get_subtitle_at(NULL, 0, 0) == NULL);
EXPECT_TRUE(oakengine_viewer_get_subtitle_at(node, 0, 0) == NULL);
}
static void test_enabled_streams(OakEngineSequence *seq)
@@ -215,57 +216,57 @@ static void test_enabled_streams(OakEngineSequence *seq)
const OakEngineNode *node = (const OakEngineNode *)as_node(seq);
oak_video_params vp;
assert(oakengine_viewer_has_enabled_streams(NULL,
EXPECT_TRUE(oakengine_viewer_has_enabled_streams(NULL,
OAKENGINE_TRACK_TYPE_VIDEO) ==
0);
assert(oakengine_viewer_has_enabled_streams(node,
EXPECT_TRUE(oakengine_viewer_has_enabled_streams(node,
OAKENGINE_TRACK_TYPE_VIDEO) ==
1);
assert(oakengine_viewer_has_enabled_streams(node,
EXPECT_TRUE(oakengine_viewer_has_enabled_streams(node,
OAKENGINE_TRACK_TYPE_AUDIO) ==
1);
assert(oakengine_viewer_has_enabled_streams(
EXPECT_TRUE(oakengine_viewer_has_enabled_streams(
node, OAKENGINE_TRACK_TYPE_SUBTITLE) == 0);
assert(oakengine_viewer_has_enabled_streams(node, 99) == 0);
EXPECT_TRUE(oakengine_viewer_has_enabled_streams(node, 99) == 0);
assert(oakengine_viewer_get_first_enabled_video_stream(NULL, &vp) ==
EXPECT_TRUE(oakengine_viewer_get_first_enabled_video_stream(NULL, &vp) ==
OAKENGINE_E_INVALID);
assert(oakengine_viewer_get_first_enabled_video_stream(node, &vp) ==
EXPECT_TRUE(oakengine_viewer_get_first_enabled_video_stream(node, &vp) ==
OAKENGINE_OK);
assert(vp.width > 0 && vp.height > 0);
EXPECT_TRUE(vp.width > 0 && vp.height > 0);
// Enabled stream references: video:0 and audio:0.
assert(oakengine_viewer_get_enabled_stream_count(NULL) == 0);
EXPECT_TRUE(oakengine_viewer_get_enabled_stream_count(NULL) == 0);
const int count = oakengine_viewer_get_enabled_stream_count(node);
assert(count == 2);
EXPECT_TRUE(count == 2);
// Query form (max = 0, NULL arrays) returns the total count.
assert(oakengine_viewer_get_enabled_streams(node, NULL, NULL, 0) == count);
EXPECT_TRUE(oakengine_viewer_get_enabled_streams(node, NULL, NULL, 0) == count);
int types[8];
int indices[8];
memset(types, -1, sizeof(types));
memset(indices, -1, sizeof(indices));
assert(oakengine_viewer_get_enabled_streams(node, types, indices, 8) ==
EXPECT_TRUE(oakengine_viewer_get_enabled_streams(node, types, indices, 8) ==
count);
int saw_video = 0, saw_audio = 0;
for (int i = 0; i < count; i++) {
assert(indices[i] == 0);
EXPECT_TRUE(indices[i] == 0);
if (types[i] == OAKENGINE_TRACK_TYPE_VIDEO) {
saw_video = 1;
} else if (types[i] == OAKENGINE_TRACK_TYPE_AUDIO) {
saw_audio = 1;
} else {
assert(0); // unexpected stream type
EXPECT_TRUE(0); // unexpected stream type
}
}
assert(saw_video && saw_audio);
EXPECT_TRUE(saw_video && saw_audio);
// A smaller max truncates the write but still returns the total.
types[0] = types[1] = -1;
indices[0] = indices[1] = -1;
assert(oakengine_viewer_get_enabled_streams(node, types, indices, 1) ==
EXPECT_TRUE(oakengine_viewer_get_enabled_streams(node, types, indices, 1) ==
count);
assert(types[0] != -1 && types[1] == -1);
EXPECT_TRUE(types[0] != -1 && types[1] == -1);
}
// ---- Workarea ------------------------------------------------------------------
@@ -275,29 +276,29 @@ static void test_workarea(OakEngineSequence *seq)
OakEngineNode *node = as_node(seq);
oakengine_viewer_workarea wa;
assert(oakengine_viewer_get_workarea(NULL, &wa) == OAKENGINE_E_INVALID);
assert(oakengine_viewer_get_workarea(node, NULL) == OAKENGINE_E_INVALID);
EXPECT_TRUE(oakengine_viewer_get_workarea(NULL, &wa) == OAKENGINE_E_INVALID);
EXPECT_TRUE(oakengine_viewer_get_workarea(node, NULL) == OAKENGINE_E_INVALID);
memset(&wa, 0xFF, sizeof(wa));
assert(oakengine_viewer_get_workarea(node, &wa) == OAKENGINE_OK);
assert(wa.enabled == 0);
EXPECT_TRUE(oakengine_viewer_get_workarea(node, &wa) == OAKENGINE_OK);
EXPECT_TRUE(wa.enabled == 0);
assert(oakengine_viewer_set_workarea_range(NULL, 0, 1, 1, 1) ==
EXPECT_TRUE(oakengine_viewer_set_workarea_range(NULL, 0, 1, 1, 1) ==
OAKENGINE_E_INVALID);
assert(oakengine_viewer_set_workarea_range(node, 1, 1, 5, 1) ==
EXPECT_TRUE(oakengine_viewer_set_workarea_range(node, 1, 1, 5, 1) ==
OAKENGINE_OK);
assert(oakengine_viewer_set_workarea_enabled(NULL, 1) ==
EXPECT_TRUE(oakengine_viewer_set_workarea_enabled(NULL, 1) ==
OAKENGINE_E_INVALID);
assert(oakengine_viewer_set_workarea_enabled(node, 1) == OAKENGINE_OK);
EXPECT_TRUE(oakengine_viewer_set_workarea_enabled(node, 1) == OAKENGINE_OK);
memset(&wa, 0, sizeof(wa));
assert(oakengine_viewer_get_workarea(node, &wa) == OAKENGINE_OK);
assert(wa.in_num == 1 && wa.in_den == 1);
assert(wa.out_num == 5 && wa.out_den == 1);
assert(wa.enabled == 1);
EXPECT_TRUE(oakengine_viewer_get_workarea(node, &wa) == OAKENGINE_OK);
EXPECT_TRUE(wa.in_num == 1 && wa.in_den == 1);
EXPECT_TRUE(wa.out_num == 5 && wa.out_den == 1);
EXPECT_TRUE(wa.enabled == 1);
assert(oakengine_viewer_set_workarea_enabled(node, 0) == OAKENGINE_OK);
assert(oakengine_viewer_get_workarea(node, &wa) == OAKENGINE_OK);
assert(wa.enabled == 0);
EXPECT_TRUE(oakengine_viewer_set_workarea_enabled(node, 0) == OAKENGINE_OK);
EXPECT_TRUE(oakengine_viewer_get_workarea(node, &wa) == OAKENGINE_OK);
EXPECT_TRUE(wa.enabled == 0);
}
// ---- Parameter setup / waveform -------------------------------------------------
@@ -307,43 +308,43 @@ static void test_parameter_setup(OakEngineProject *project,
{
OakEngineNode *node = as_node(seq);
assert(oakengine_viewer_set_default_parameters(NULL) ==
EXPECT_TRUE(oakengine_viewer_set_default_parameters(NULL) ==
OAKENGINE_E_INVALID);
assert(oakengine_viewer_set_default_parameters(node) == OAKENGINE_OK);
EXPECT_TRUE(oakengine_viewer_set_default_parameters(node) == OAKENGINE_OK);
// set_parameters_from_footage accepts any viewer handles; a second
// sequence stands in for the footage array here.
OakEngineSequence *other = oakengine_sequence_new(project, "Other");
assert(other != NULL);
EXPECT_TRUE(other != NULL);
OakEngineNode *other_node = as_node(other);
assert(oakengine_viewer_set_parameters_from_footage(NULL, &other_node,
EXPECT_TRUE(oakengine_viewer_set_parameters_from_footage(NULL, &other_node,
1) == OAKENGINE_E_INVALID);
assert(oakengine_viewer_set_parameters_from_footage(node, NULL, 1) ==
EXPECT_TRUE(oakengine_viewer_set_parameters_from_footage(node, NULL, 1) ==
OAKENGINE_E_INVALID);
// An empty array is a valid no-op.
assert(oakengine_viewer_set_parameters_from_footage(node, NULL, 0) ==
EXPECT_TRUE(oakengine_viewer_set_parameters_from_footage(node, NULL, 0) ==
OAKENGINE_OK);
// One invalid element rejects the whole call.
OakEngineNode *solid = oakengine_project_add_node(
project, "org.olivevideoeditor.Olive.solidgenerator");
assert(solid != NULL);
EXPECT_TRUE(solid != NULL);
OakEngineNode *mixed[2] = { other_node, solid };
assert(oakengine_viewer_set_parameters_from_footage(node, mixed, 2) ==
EXPECT_TRUE(oakengine_viewer_set_parameters_from_footage(node, mixed, 2) ==
OAKENGINE_E_INVALID);
// All viewers: OK, and the params are adopted.
OakEngineNode *viewers[1] = { other_node };
assert(oakengine_viewer_set_parameters_from_footage(node, viewers, 1) ==
EXPECT_TRUE(oakengine_viewer_set_parameters_from_footage(node, viewers, 1) ==
OAKENGINE_OK);
// Waveform toggle; nothing is connected to the samples input, so the
// connected waveform is NULL.
assert(oakengine_viewer_set_waveform_enabled(NULL, 1) ==
EXPECT_TRUE(oakengine_viewer_set_waveform_enabled(NULL, 1) ==
OAKENGINE_E_INVALID);
assert(oakengine_viewer_set_waveform_enabled(node, 1) == OAKENGINE_OK);
assert(oakengine_viewer_set_waveform_enabled(node, 0) == OAKENGINE_OK);
assert(oakengine_viewer_get_connected_waveform(NULL) == NULL);
assert(oakengine_viewer_get_connected_waveform(
EXPECT_TRUE(oakengine_viewer_set_waveform_enabled(node, 1) == OAKENGINE_OK);
EXPECT_TRUE(oakengine_viewer_set_waveform_enabled(node, 0) == OAKENGINE_OK);
EXPECT_TRUE(oakengine_viewer_get_connected_waveform(NULL) == NULL);
EXPECT_TRUE(oakengine_viewer_get_connected_waveform(
(const OakEngineNode *)node) == NULL);
}
@@ -374,7 +375,7 @@ struct EventLog {
static void record_event(const oakengine_event *event, void *userdata)
{
struct EventLog *log = (struct EventLog *)userdata;
assert(event != NULL);
EXPECT_TRUE(event != NULL);
switch (event->id) {
case OAKENGINE_EVENT_VIEWER_PLAYHEAD_CHANGED:
log->playhead_events++;
@@ -418,7 +419,7 @@ static void record_event(const oakengine_event *event, void *userdata)
log->waveform_events++;
break;
default:
assert(0); // unexpected event id on this subscription
EXPECT_TRUE(0); // unexpected event id on this subscription
}
}
@@ -426,7 +427,7 @@ static int64_t subscribe_checked(OakEngineNode *node, int32_t id,
struct EventLog *log)
{
const int64_t sub = oakengine_event_subscribe(node, id, record_event, log);
assert(sub > 0);
EXPECT_TRUE(sub > 0);
return sub;
}
@@ -440,8 +441,8 @@ static void test_events(OakEngineProject *project, OakEngineSequence *seq,
// Family mismatch: a viewer event on a non-viewer node must fail.
OakEngineNode *solid = oakengine_project_add_node(
project, "org.olivevideoeditor.Olive.solidgenerator");
assert(solid != NULL);
assert(oakengine_event_subscribe(solid,
EXPECT_TRUE(solid != NULL);
EXPECT_TRUE(oakengine_event_subscribe(solid,
OAKENGINE_EVENT_VIEWER_PLAYHEAD_CHANGED,
record_event, &log) == 0);
@@ -471,65 +472,65 @@ static void test_events(OakEngineProject *project, OakEngineSequence *seq,
node, OAKENGINE_EVENT_VIEWER_CONNECTED_WAVEFORM_CHANGED, &log);
// Playhead.
assert(oakengine_viewer_set_playhead(node, 3, 1) == OAKENGINE_OK);
assert(log.playhead_events == 1);
assert(log.playhead_num == 3 && log.playhead_den == 1);
EXPECT_TRUE(oakengine_viewer_set_playhead(node, 3, 1) == OAKENGINE_OK);
EXPECT_TRUE(log.playhead_events == 1);
EXPECT_TRUE(log.playhead_num == 3 && log.playhead_den == 1);
// Length: placing a real clip makes verify_length() emit
// length_changed with the new content length.
OakEngineFootage *footage =
oakengine_project_import_footage(project, media_path);
assert(footage != NULL);
assert(oakengine_sequence_add_track(seq, OAKENGINE_TRACK_TYPE_VIDEO) == 0);
EXPECT_TRUE(footage != NULL);
EXPECT_TRUE(oakengine_sequence_add_track(seq, OAKENGINE_TRACK_TYPE_VIDEO) == 0);
OakEngineClip *clip = oakengine_sequence_add_footage_clip(
seq, footage, OAKENGINE_TRACK_TYPE_VIDEO, 0, 0, 30, 0);
assert(clip != NULL);
assert(log.length_events >= 1);
assert(log.length_num > 0 && log.length_den > 0);
EXPECT_TRUE(clip != NULL);
EXPECT_TRUE(log.length_events >= 1);
EXPECT_TRUE(log.length_num > 0 && log.length_den > 0);
int64_t len_num = -1, len_den = -1;
assert(oakengine_viewer_get_length(node, &len_num, &len_den) ==
EXPECT_TRUE(oakengine_viewer_get_length(node, &len_num, &len_den) ==
OAKENGINE_OK);
assert(len_num == log.length_num && len_den == log.length_den);
EXPECT_TRUE(len_num == log.length_num && len_den == log.length_den);
// Video params: changing the size emits size_changed (with the new
// dimensions as a/b) and video_params_changed.
assert(oakengine_sequence_set_video_params(seq, 1280, 720, -1, -1, -1, -1,
EXPECT_TRUE(oakengine_sequence_set_video_params(seq, 1280, 720, -1, -1, -1, -1,
-1, -1, 0) == OAKENGINE_OK);
assert(log.size_events == 1);
assert(log.size_w == 1280 && log.size_h == 720);
assert(log.video_params_events == 1);
assert(log.frame_rate_events == 0);
assert(log.pixel_aspect_events == 0);
assert(log.interlacing_events == 0);
EXPECT_TRUE(log.size_events == 1);
EXPECT_TRUE(log.size_w == 1280 && log.size_h == 720);
EXPECT_TRUE(log.video_params_events == 1);
EXPECT_TRUE(log.frame_rate_events == 0);
EXPECT_TRUE(log.pixel_aspect_events == 0);
EXPECT_TRUE(log.interlacing_events == 0);
// Pixel aspect and interlacing changes fire their own events.
assert(oakengine_sequence_set_video_params(seq, -1, -1, -1, -1, 4, 3, -1,
EXPECT_TRUE(oakengine_sequence_set_video_params(seq, -1, -1, -1, -1, 4, 3, -1,
-1, 0) == OAKENGINE_OK);
assert(log.pixel_aspect_events == 1);
assert(oakengine_sequence_set_video_params(seq, -1, -1, -1, -1, -1, -1, 1,
EXPECT_TRUE(log.pixel_aspect_events == 1);
EXPECT_TRUE(oakengine_sequence_set_video_params(seq, -1, -1, -1, -1, -1, -1, 1,
-1, 0) == OAKENGINE_OK);
assert(log.interlacing_events == 1);
assert(log.interlacing_mode == 1);
EXPECT_TRUE(log.interlacing_events == 1);
EXPECT_TRUE(log.interlacing_mode == 1);
// Audio params: a new sample rate emits sample_rate_changed (a = rate)
// and audio_params_changed.
assert(oakengine_sequence_set_audio_params(seq, 44100, 0, 0) ==
EXPECT_TRUE(oakengine_sequence_set_audio_params(seq, 44100, 0, 0) ==
OAKENGINE_OK);
assert(log.sample_rate_events == 1);
assert(log.sample_rate == 44100);
assert(log.audio_params_events == 1);
EXPECT_TRUE(log.sample_rate_events == 1);
EXPECT_TRUE(log.sample_rate == 44100);
EXPECT_TRUE(log.audio_params_events == 1);
// Texture input: the placed clip's track auto-connected the viewer's
// texture input, so disconnect it first, then connect a node and check
// that texture_input_changed fired.
assert(oakengine_node_disconnect(node,
EXPECT_TRUE(oakengine_node_disconnect(node,
oakengine_viewer_texture_input_id()) ==
OAKENGINE_OK);
assert(oakengine_node_connect(solid, node,
EXPECT_TRUE(oakengine_node_connect(solid, node,
oakengine_viewer_texture_input_id()) ==
OAKENGINE_OK);
assert(log.texture_events >= 1);
assert(oakengine_node_disconnect(node,
EXPECT_TRUE(log.texture_events >= 1);
EXPECT_TRUE(oakengine_node_disconnect(node,
oakengine_viewer_texture_input_id()) ==
OAKENGINE_OK);
@@ -538,29 +539,29 @@ static void test_events(OakEngineProject *project, OakEngineSequence *seq,
// so only the subscription itself is exercised above.
while (n > 0) {
assert(oakengine_event_unsubscribe(subs[--n]) == OAKENGINE_OK);
EXPECT_TRUE(oakengine_event_unsubscribe(subs[--n]) == OAKENGINE_OK);
}
}
int main(void)
TEST(OakEngineViewer, Main)
{
make_tmpdir();
// Sandbox the config/cache/data locations.
#if !defined(_WIN32)
assert(setenv("XDG_CONFIG_HOME", g_tmpdir, 1) == 0);
assert(setenv("XDG_CACHE_HOME", g_tmpdir, 1) == 0);
assert(setenv("XDG_DATA_HOME", g_tmpdir, 1) == 0);
EXPECT_TRUE(setenv("XDG_CONFIG_HOME", g_tmpdir, 1) == 0);
EXPECT_TRUE(setenv("XDG_CACHE_HOME", g_tmpdir, 1) == 0);
EXPECT_TRUE(setenv("XDG_DATA_HOME", g_tmpdir, 1) == 0);
#endif
assert(oakengine_init(OAKENGINE_INIT_HEADLESS) == OAKENGINE_OK);
EXPECT_TRUE(oakengine_init(OAKENGINE_INIT_HEADLESS) == OAKENGINE_OK);
OakEngineProject *project = oakengine_project_create();
assert(project != NULL);
assert(oakengine_project_new(project) == OAKENGINE_OK);
EXPECT_TRUE(project != NULL);
EXPECT_TRUE(oakengine_project_new(project) == OAKENGINE_OK);
OakEngineSequence *seq = oakengine_sequence_new(project, "ViewerSeq");
assert(seq != NULL);
EXPECT_TRUE(seq != NULL);
test_from_node(project, seq);
test_playhead(seq);
@@ -577,16 +578,14 @@ int main(void)
// the (empty, sandboxed) user config and may leave invalid params
// behind (same hazard oakengine_sequence_new() backfills against).
// Restore known-good params so the clip/timebase paths work.
assert(oakengine_sequence_set_video_params(seq, 1920, 1080, 30000, 1001,
EXPECT_TRUE(oakengine_sequence_set_video_params(seq, 1920, 1080, 30000, 1001,
1, 1, 0, -1, 0) == OAKENGINE_OK);
assert(oakengine_sequence_set_audio_params(seq, 48000, 3, 0) ==
EXPECT_TRUE(oakengine_sequence_set_audio_params(seq, 48000, 3, 0) ==
OAKENGINE_OK);
test_events(project, seq, media);
oakengine_project_free(project);
assert(oakengine_shutdown() == OAKENGINE_OK);
EXPECT_TRUE(oakengine_shutdown() == OAKENGINE_OK);
printf("oakengine_viewer_test: all assertions passed\n");
return 0;
}
+27 -27
View File
@@ -26,6 +26,7 @@
// here is a validation/error path that never touches a render backend.
#include <assert.h>
#include <gtest/gtest.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -39,11 +40,11 @@ static char *handle(OakWorkerSession *session, const char *line)
{
const int needed =
oakengine_worker_session_handle_json(session, line, NULL, 0);
assert(needed >= 0);
EXPECT_TRUE(needed >= 0);
char *buf = static_cast<char *>(malloc(size_t(needed) + 1));
const int written = oakengine_worker_session_handle_json(
session, line, buf, needed + 1);
assert(written == needed);
EXPECT_TRUE(written == needed);
buf[needed] = '\0';
return buf;
}
@@ -55,7 +56,7 @@ static void assert_is_error_with(const char *response, const char *needle)
fprintf(stderr,
"expected error response containing \"%s\", got: %s\n",
needle, response);
assert(0);
EXPECT_TRUE(0);
}
}
@@ -65,51 +66,51 @@ static void test_create_destroy(void)
const char *backends[] = { NULL, "", "none", "NONE" };
for (size_t i = 0; i < sizeof(backends) / sizeof(backends[0]); ++i) {
OakWorkerSession *s = oakengine_worker_session_create(backends[i]);
assert(s);
assert(oakengine_worker_session_has_renderer(s) == 0);
assert(oakengine_worker_session_shutdown_requested(s) == 0);
EXPECT_TRUE(s);
EXPECT_TRUE(oakengine_worker_session_has_renderer(s) == 0);
EXPECT_TRUE(oakengine_worker_session_shutdown_requested(s) == 0);
oakengine_worker_session_free(s);
}
// NULL tolerance
oakengine_worker_session_free(NULL);
assert(oakengine_worker_session_has_renderer(NULL) == 0);
assert(oakengine_worker_session_shutdown_requested(NULL) == 0);
assert(oakengine_worker_session_handle_json(NULL, "{}", NULL, 0) == -1);
EXPECT_TRUE(oakengine_worker_session_has_renderer(NULL) == 0);
EXPECT_TRUE(oakengine_worker_session_shutdown_requested(NULL) == 0);
EXPECT_TRUE(oakengine_worker_session_handle_json(NULL, "{}", NULL, 0) == -1);
}
static void test_startup_handshake(void)
{
OakWorkerSession *s = oakengine_worker_session_create("none");
assert(s);
EXPECT_TRUE(s);
// buf/size query convention
const int needed =
oakengine_worker_session_startup_handshake(s, NULL, 0);
assert(needed > 0);
EXPECT_TRUE(needed > 0);
char *buf = static_cast<char *>(malloc(size_t(needed) + 1));
assert(oakengine_worker_session_startup_handshake(s, buf, needed + 1) ==
EXPECT_TRUE(oakengine_worker_session_startup_handshake(s, buf, needed + 1) ==
needed);
buf[needed] = '\0';
assert(strstr(buf, "\"type\":\"handshake\""));
assert(strstr(buf, "\"protocol_version\":1"));
EXPECT_TRUE(strstr(buf, "\"type\":\"handshake\""));
EXPECT_TRUE(strstr(buf, "\"protocol_version\":1"));
// No renderer -> no GL version announced
assert(!strstr(buf, "gl_major"));
EXPECT_TRUE(!strstr(buf, "gl_major"));
free(buf);
assert(oakengine_worker_session_startup_handshake(NULL, NULL, 0) == -1);
EXPECT_TRUE(oakengine_worker_session_startup_handshake(NULL, NULL, 0) == -1);
oakengine_worker_session_free(s);
}
static void test_initialize_runtime(void)
{
// NULL tolerance
assert(oakengine_worker_session_initialize_runtime(NULL) == 0);
EXPECT_TRUE(oakengine_worker_session_initialize_runtime(NULL) == 0);
// Runtime init (EngineCore, factories, managers) must succeed without a
// renderer; the session stays usable for control messages afterwards.
OakWorkerSession *s = oakengine_worker_session_create("none");
assert(s);
assert(oakengine_worker_session_initialize_runtime(s) == 1);
EXPECT_TRUE(s);
EXPECT_TRUE(oakengine_worker_session_initialize_runtime(s) == 1);
char *r = handle(s, "{\"type\":\"teleport\"}");
assert_is_error_with(r, "unknown message type");
free(r);
@@ -165,7 +166,7 @@ static void test_render_frame_before_load_graph(void)
"\"node_uuid\":\"abc\",\"time_num\":0,\"time_den\":1}");
assert_is_error_with(r, "render_frame received before load_graph");
// The error carries the ticket id so the caller can correlate
assert(strstr(r, "\"ticket\":7"));
EXPECT_TRUE(strstr(r, "\"ticket\":7"));
free(r);
oakengine_worker_session_free(s);
}
@@ -194,15 +195,15 @@ static void test_shutdown_idempotent(void)
// Shutdown produces no response and latches the flag
char *r = handle(s, "{\"type\":\"shutdown\"}");
assert(r[0] == '\0');
EXPECT_TRUE(r[0] == '\0');
free(r);
assert(oakengine_worker_session_shutdown_requested(s) == 1);
EXPECT_TRUE(oakengine_worker_session_shutdown_requested(s) == 1);
// Repeating it is a harmless no-op
r = handle(s, "{\"type\":\"shutdown\"}");
assert(r[0] == '\0');
EXPECT_TRUE(r[0] == '\0');
free(r);
assert(oakengine_worker_session_shutdown_requested(s) == 1);
EXPECT_TRUE(oakengine_worker_session_shutdown_requested(s) == 1);
// The session still answers other messages afterwards
r = handle(s, "{\"type\":\"teleport\"}");
@@ -216,12 +217,12 @@ static void test_cancel_is_silent(void)
{
OakWorkerSession *s = oakengine_worker_session_create("none");
char *r = handle(s, "{\"type\":\"cancel\",\"ticket\":3}");
assert(r[0] == '\0');
EXPECT_TRUE(r[0] == '\0');
free(r);
oakengine_worker_session_free(s);
}
int main(void)
TEST(OakEngineWorker, Main)
{
test_create_destroy();
test_startup_handshake();
@@ -233,5 +234,4 @@ int main(void)
test_load_graph_missing_file();
test_shutdown_idempotent();
test_cancel_is_silent();
return 0;
}