diff --git a/inc/utils/arrays.h b/inc/utils/arrays.h index c22e44db2..3aaa55556 100644 --- a/inc/utils/arrays.h +++ b/inc/utils/arrays.h @@ -104,6 +104,27 @@ } \ } +/** + * Deletes element from array and rearranges other + * elements accordingly. + */ +#define array_delete_confirm( \ + array,size,element,confirm) \ + confirm = false; \ + for (size_t ii = 0; ii < (size_t) size; ii++) \ + { \ + if ((void *) array[ii] == (void *) element) \ + { \ + confirm = true; \ + --size; \ + for (size_t jj = ii; jj < (size_t) size; jj++) \ + { \ + array[jj] = array[jj + 1]; \ + } \ + break; \ + } \ + } + /** * Deletes element from array and rearranges other * elements accordingly. diff --git a/src/actions/actions.c b/src/actions/actions.c index 7778eea8c..3f938557c 100644 --- a/src/actions/actions.c +++ b/src/actions/actions.c @@ -1140,9 +1140,10 @@ activate_delete ( } void -activate_duplicate (GSimpleAction *action, - GVariant *variant, - gpointer user_data) +activate_duplicate ( + GSimpleAction *action, + GVariant *variant, + gpointer user_data) { ArrangerSelections * sel = main_window_get_last_focused_arranger_selections ( diff --git a/src/actions/undo_manager.c b/src/actions/undo_manager.c index 9eb9525d4..b4f0367a3 100644 --- a/src/actions/undo_manager.c +++ b/src/actions/undo_manager.c @@ -90,7 +90,7 @@ undo_manager_undo (UndoManager * self) undo_stack_pop_last ( self->redo_stack); - /* TODO create functions to delet eunnecessary + /* TODO create functions to delete unnecessary * files held by the action (eg, something * that calls plugin_delete_state_files()) */ undoable_action_free (action_to_delete); diff --git a/src/actions/undo_stack.c b/src/actions/undo_stack.c index 9ef5a72fa..f5b80429a 100644 --- a/src/actions/undo_stack.c +++ b/src/actions/undo_stack.c @@ -195,7 +195,7 @@ undo_stack_push ( } } -static int +static bool remove_action ( UndoStack * self, UndoableAction * action) @@ -203,14 +203,15 @@ remove_action ( /* CAPS, CamelCase, snake_case */ #define REMOVE_ELEMENT(caps,cc,sc) \ case UA_##caps: \ - array_delete ( \ + array_delete_confirm ( \ self->sc##_actions, \ self->num_##sc##_actions, \ - (cc##Action *) action); \ - removed = 1; \ + (cc##Action *) action, \ + removed); \ + g_warn_if_fail (removed); \ break - int removed = 0; + bool removed = false; switch (action->type) { REMOVE_ELEMENT ( @@ -233,14 +234,15 @@ remove_action ( REMOVE_ELEMENT ( TRANSPORT, Transport, transport); case UA_ARRANGER_SELECTIONS: - array_delete ( + array_delete_confirm ( self->as_actions, self->num_as_actions, - (ArrangerSelectionsAction *) action); - removed = 1; + (ArrangerSelectionsAction *) action, + removed); + g_warn_if_fail (removed); g_warn_if_fail ( (int) self->num_as_actions <= - self->stack->top + 1); + g_atomic_int_get (&self->stack->top) + 1); break; } @@ -270,12 +272,16 @@ UndoableAction * undo_stack_pop_last ( UndoStack * self) { + g_message ( + " popping last (top = %d)", + g_atomic_int_get (&self->stack->top)); + /* pop from stack */ UndoableAction * action = (UndoableAction *) stack_pop_last (self->stack); /* remove the action */ - int removed = remove_action (self, action); + bool removed = remove_action (self, action); g_warn_if_fail (removed); /* return it */ diff --git a/src/gui/backend/arranger_selections.c b/src/gui/backend/arranger_selections.c index 4e198e4c0..b5f279295 100644 --- a/src/gui/backend/arranger_selections.c +++ b/src/gui/backend/arranger_selections.c @@ -17,6 +17,7 @@ * along with this program. If not, see . */ +#include #include #include @@ -775,6 +776,7 @@ add_region_ticks ( { ArrangerObject * obj = arranger_selections_get_first_object (self); + g_return_if_fail (obj); ArrangerObject * region = (ArrangerObject *) arranger_object_get_region (obj); @@ -803,8 +805,8 @@ arranger_selections_get_start_pos ( MidiArrangerSelections * mas; AutomationSelections * as; - position_set_to_bar ( - pos, TRANSPORT->total_bars); + position_set_to_bar (pos, INT_MAX); + /*&pos, TRANSPORT->total_bars);*/ #define GET_START_POS(sel,cc,sc) \ for (i = 0; i < (sel)->num_##sc##s; i++) \ @@ -1034,7 +1036,8 @@ arranger_selections_get_first_object ( Position pos; position_set_to_bar ( - &pos, TRANSPORT->total_bars); + /*&pos, TRANSPORT->total_bars);*/ + &pos, INT_MAX); ArrangerObject * ret_obj = NULL; #define GET_FIRST_OBJ(sel,cc,sc) \ diff --git a/tests/actions/undo_manager.c b/tests/actions/undo_manager.c new file mode 100644 index 000000000..3488fc274 --- /dev/null +++ b/tests/actions/undo_manager.c @@ -0,0 +1,138 @@ +/* + * Copyright (C) 2021 Alexandros Theodotou + * + * This file is part of Zrythm + * + * Zrythm is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Zrythm is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Zrythm. If not, see . + */ + +#include "zrythm-test-config.h" + +#include + +#include "actions/undo_manager.h" +#include "project.h" +#include "utils/flags.h" +#include "zrythm.h" + +#include "tests/helpers/project.h" +#include "tests/helpers/zrythm.h" + +#include +#include + +static void +perform_create_region_action () +{ + Position p1, p2; + position_set_to_bar (&p1, 1); + position_set_to_bar (&p2, 2); + int track_pos = TRACKLIST->num_tracks - 1; + ZRegion * r = + midi_region_new ( + &p1, &p2, track_pos, 0, 0); + ArrangerObject * r_obj = + (ArrangerObject *) r; + track_add_region ( + TRACKLIST->tracks[track_pos], + r, NULL, 0, F_GEN_NAME, + F_NO_PUBLISH_EVENTS); + arranger_object_select ( + r_obj, F_SELECT, F_NO_APPEND); + UndoableAction * ua = + arranger_selections_action_new_create ( + (ArrangerSelections *) TL_SELECTIONS); + undo_manager_perform (UNDO_MANAGER, ua); +} + +static void +test_perform_many_actions () +{ + test_helper_zrythm_init (); + + for (int i = 0; + !undo_stack_is_full (UNDO_MANAGER->undo_stack); + i++) + { + UndoableAction * ua = NULL; + if (i % 2 == 0) + { + ua = + tracklist_selections_action_new_create_midi ( + TRACKLIST->num_tracks, 1); + undo_manager_perform (UNDO_MANAGER, ua); + } + else if (i % 13 == 0) + { + undo_manager_undo (UNDO_MANAGER); + } + else if (i % 17 == 0) + { + test_project_save_and_reload (); + } + else + { + perform_create_region_action (); + } + } + UndoableAction * ua = + tracklist_selections_action_new_create_midi ( + TRACKLIST->num_tracks, 1); + undo_manager_perform (UNDO_MANAGER, ua); + perform_create_region_action (); + perform_create_region_action (); + + for (int i = 0; + !undo_stack_is_full ( + UNDO_MANAGER->redo_stack) || + undo_stack_is_empty ( + UNDO_MANAGER->redo_stack); + i++) + { + if (undo_stack_is_empty ( + UNDO_MANAGER->redo_stack)) + { + break; + } + + if (i % 17 == 0) + { + test_project_save_and_reload (); + } + else if (i % 47 == 0) + { + perform_create_region_action (); + } + else + { + undo_manager_redo (UNDO_MANAGER); + } + } + + test_helper_zrythm_cleanup (); +} + +int +main (int argc, char *argv[]) +{ + g_test_init (&argc, &argv, NULL); + +#define TEST_PREFIX "/actions/undo manager/" + + g_test_add_func ( + TEST_PREFIX "test perform many actions", + (GTestFunc) test_perform_many_actions); + + return g_test_run (); +} diff --git a/tests/gui/backend/arranger_selections.c b/tests/gui/backend/arranger_selections.c new file mode 100644 index 000000000..987a200ef --- /dev/null +++ b/tests/gui/backend/arranger_selections.c @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2021 Alexandros Theodotou + * + * This file is part of Zrythm + * + * Zrythm is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Zrythm is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Zrythm. If not, see . + */ + +#include "zrythm-test-config.h" + +#include + +#include "audio/midi_region.h" +#include "gui/backend/arranger_selections.h" +#include "project.h" +#include "utils/flags.h" +#include "zrythm.h" + +#include "tests/helpers/zrythm.h" + +#include +#include + +static void +test_region_length_in_ticks ( + Track * track, + int bar_start, + int bar_end) +{ + Position p1, p2; + position_set_to_bar (&p1, bar_start); + position_set_to_bar (&p2, bar_end); + ZRegion * r = + midi_region_new ( + &p1, &p2, track->pos, 0, 0); + ArrangerObject * r_obj = (ArrangerObject *) r; + track_add_region ( + track, r, NULL, 0, F_GEN_NAME, + F_NO_PUBLISH_EVENTS); + + arranger_object_select ( + r_obj, F_SELECT, F_NO_APPEND); + + double length = + arranger_selections_get_length_in_ticks ( + (ArrangerSelections *) TL_SELECTIONS); + g_assert_cmpfloat_with_epsilon ( + length, + TRANSPORT->ticks_per_bar * (bar_end - bar_start), + 0.00001); +} + +static void +test_get_length_in_ticks () +{ + test_helper_zrythm_init (); + + UndoableAction * ua = + tracklist_selections_action_new_create_midi ( + TRACKLIST->num_tracks, 1); + undo_manager_perform (UNDO_MANAGER, ua); + + Track * track = + TRACKLIST->tracks[TRACKLIST->num_tracks - 1]; + + test_region_length_in_ticks (track, 3, 4); + test_region_length_in_ticks (track, 100, 102); + test_region_length_in_ticks (track, 1000, 1010); + + test_helper_zrythm_cleanup (); +} + +int +main (int argc, char *argv[]) +{ + g_test_init (&argc, &argv, NULL); + +#define TEST_PREFIX "/gui/backend/arranger selections/" + + g_test_add_func ( + TEST_PREFIX "test get length in ticks", + (GTestFunc) test_get_length_in_ticks); + + return g_test_run (); +} diff --git a/tests/meson.build b/tests/meson.build index fce2dd088..737988742 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -127,6 +127,7 @@ if get_option ('tests') tests = [ ['actions/arranger_selections', false], ['actions/range', true], + ['actions/undo_manager', true], ['audio/audio_track', true], ['audio/automation_track', true], ['audio/curve', true], @@ -141,6 +142,7 @@ if get_option ('tests') ['audio/region', true], ['audio/track', true], ['audio/tracklist', true], + ['gui/backend/arranger_selections', true], ['integration/recording', false], ['plugins/plugin', true], ['plugins/plugin_manager', true],