#include #include #include #include #include #include #include #include #include #include #include "engine.h" static volatile int keep_running = 1; static jmp_buf segv_jmp; static volatile int segv_caught = 0; static void handle_sigint(int sig) { (void)sig; keep_running = 0; } // SIGSEGV handler to catch segfaults gracefully static void handle_sigsegv(int sig) { (void)sig; segv_caught = 1; longjmp(segv_jmp, 1); } static Engine *create_stress_engine(void) { Engine *engine = (Engine *)calloc(1, sizeof(Engine)); assert(engine != NULL); engine->control_channel = 0; engine->sample_rate = 48000; engine->quantize_mode = QUANTIZE_OFF; engine->quantize_threshold = 0; engine->queued_triggers = NULL; command_queue_init(&engine->command_queue); atomic_store(&engine->quantize_mode_atomic, (int)QUANTIZE_OFF); atomic_store(&engine->quantize_threshold_atomic, 0); engine->transport = (Transport *)calloc(1, sizeof(Transport)); assert(engine->transport != NULL); transport_init(engine->transport, 48000); for (int i = 0; i < MAX_CLIPS; i++) { engine->clips[i].state = CLIP_EMPTY; engine->clips[i].buffer = (float *)calloc(MAX_BUFFER_SIZE, sizeof(float)); assert(engine->clips[i].buffer != NULL); engine->clips[i].buffer_size = 0; engine->clips[i].write_position = 0; engine->clips[i].read_position = 0; } return engine; } static void destroy_stress_engine(Engine *engine) { if (engine) { // Free queued triggers first QueuedTrigger *qt = engine->queued_triggers; while (qt) { QueuedTrigger *next = qt->next; free(qt); qt = next; } engine->queued_triggers = NULL; if (engine->transport) { transport_cleanup(engine->transport); free(engine->transport); engine->transport = NULL; } for (int i = 0; i < MAX_CLIPS; i++) { free(engine->clips[i].buffer); engine->clips[i].buffer = NULL; } free(engine); } } // Random microsleep to expose race conditions static void random_sleep_us(void) { struct timespec ts = { .tv_sec = 0, .tv_nsec = (rand() % 10000) * 100 // 0-999 microseconds }; nanosleep(&ts, NULL); } // Stress test 1: Rapid clip triggers via command queue static void stress_command_queue(void) { printf("Stress test 1: Rapid command queue submission...\n"); Engine *engine = create_stress_engine(); int num_ops = 100000; int success = 0; int dropped = 0; for (int i = 0; i < num_ops; i++) { int clip_idx = i % MAX_CLIPS; int ret = engine_submit_command(engine, CMD_TRIGGER_CLIP, clip_idx, 0); if (ret == 0) { success++; } else { dropped++; } // Process commands periodically to prevent queue overflow if (i % 100 == 0) { engine_process_commands(engine); } } assert(success > 0); printf(" Submitted %d commands, %d succeeded, %d dropped\n", num_ops, success, dropped); // Process all commands engine_process_commands(engine); // Verify no commands left CommandQueue *q = &engine->command_queue; unsigned int write = atomic_load(&q->write_index); unsigned int read = atomic_load(&q->read_index); assert(write == read); destroy_stress_engine(engine); printf(" PASSED\n"); } // Stress test 2: Mix of clip and scene triggers static void stress_mixed_triggers(void) { printf("Stress test 2: Mixed clip/scene triggers...\n"); Engine *engine = create_stress_engine(); int num_ops = 50000; int success = 0; int dropped = 0; for (int i = 0; i < num_ops; i++) { int ret; if (i % 3 == 0) { int scene_idx = (i / 3) % MAX_SCENES; ret = engine_submit_command(engine, CMD_TRIGGER_SCENE, scene_idx, 0); } else { int clip_idx = i % MAX_CLIPS; ret = engine_submit_command(engine, CMD_TRIGGER_CLIP, clip_idx, 0); } if (ret == 0) { success++; } else { dropped++; } if (i % 50 == 0) { engine_process_commands(engine); } } assert(success > 0); printf(" Submitted %d commands, %d succeeded, %d dropped\n", num_ops, success, dropped); engine_process_commands(engine); CommandQueue *q = &engine->command_queue; assert(atomic_load(&q->write_index) == atomic_load(&q->read_index)); destroy_stress_engine(engine); printf(" PASSED\n"); } // Stress test 3: Queue overflow (should not crash) static void stress_queue_overflow(void) { printf("Stress test 3: Queue overflow (should drop gracefully)...\n"); Engine *engine = create_stress_engine(); int num_ops = MAX_QUEUED_COMMANDS * 10; int dropped = 0; for (int i = 0; i < num_ops; i++) { int ret = engine_submit_command(engine, CMD_TRIGGER_CLIP, i % MAX_CLIPS, 0); if (ret != 0) dropped++; } assert(dropped > 0); printf(" Dropped %d commands (expected)\n", dropped); engine_process_commands(engine); destroy_stress_engine(engine); printf(" PASSED\n"); } // Stress test 4: Trigger pool exhaustion (simulate many MIDI notes) static void stress_trigger_pool(void) { printf("Stress test 4: Trigger pool exhaustion...\n"); Engine *engine = create_stress_engine(); engine->transport->state = TRANSPORT_PLAYING; atomic_store(&engine->transport->state_atomic, TRANSPORT_PLAYING); engine->quantize_mode = QUANTIZE_BEAT; atomic_store(&engine->quantize_mode_atomic, (int)QUANTIZE_BEAT); int num_triggers = 10000; for (int i = 0; i < num_triggers; i++) { queue_trigger(engine, i % MAX_CLIPS, false, 100); // Periodically process to prevent memory exhaustion if (i % 1000 == 0) { QueuedTrigger *qt = engine->queued_triggers; int processed = 0; while (qt && processed < 500) { QueuedTrigger *next = qt->next; free(qt); qt = next; processed++; } engine->queued_triggers = qt; } } int count = 0; QueuedTrigger *qt = engine->queued_triggers; while (qt) { count++; qt = qt->next; } assert(count > 0); printf(" Queued %d triggers\n", count); qt = engine->queued_triggers; while (qt) { QueuedTrigger *next = qt->next; free(qt); qt = next; } engine->queued_triggers = NULL; destroy_stress_engine(engine); printf(" PASSED\n"); } // Stress test 5: Undo/Redo stress static void stress_undo_redo(void) { printf("Stress test 5: Undo/Redo stress...\n"); Engine *engine = create_stress_engine(); int num_ops = 10000; int success = 0; int dropped = 0; for (int i = 0; i < num_ops; i++) { int clip_idx = i % MAX_CLIPS; int ret = engine_submit_command(engine, CMD_TRIGGER_CLIP, clip_idx, 0); if (ret == 0) success++; else dropped++; engine_process_commands(engine); if (i % 2 == 0) { ret = engine_submit_command(engine, CMD_UNDO, 0, 0); if (ret == 0) success++; else dropped++; engine_process_commands(engine); } // Rapid undo/redo cycles if (i % 10 == 0) { for (int j = 0; j < 5; j++) { engine_submit_command(engine, CMD_UNDO, 0, 0); engine_submit_command(engine, CMD_REDO, 0, 0); } engine_process_commands(engine); } } assert(success > 0); printf(" Submitted %d commands, %d succeeded, %d dropped\n", num_ops * 2, success, dropped); for (int i = 0; i < 1000; i++) { engine_submit_command(engine, CMD_REDO, 0, 0); engine_process_commands(engine); } destroy_stress_engine(engine); printf(" PASSED\n"); } // Stress test 6: Transport state changes static void stress_transport(void) { printf("Stress test 6: Transport state changes...\n"); Engine *engine = create_stress_engine(); int num_ops = 50000; for (int i = 0; i < num_ops; i++) { switch (i % 5) { case 0: engine_submit_command(engine, CMD_TRANSPORT_PLAY, 0, 0); break; case 1: engine_submit_command(engine, CMD_TRANSPORT_PAUSE, 0, 0); break; case 2: engine_submit_command(engine, CMD_TRANSPORT_STOP, 0, 0); break; case 3: engine_submit_command(engine, CMD_TRANSPORT_TOGGLE_PLAY, 0, 0); break; case 4: engine_submit_command(engine, CMD_SET_CLOCK_SOURCE, (int)CLOCK_SOURCE_MIDI, 0); break; } if (i % 10 == 0) { engine_process_commands(engine); } } engine_process_commands(engine); destroy_stress_engine(engine); printf(" PASSED\n"); } // Stress test 7: Quantize mode changes static void stress_quantize(void) { printf("Stress test 7: Quantize mode changes...\n"); Engine *engine = create_stress_engine(); int num_ops = 50000; for (int i = 0; i < num_ops; i++) { QuantizeMode mode = (QuantizeMode)(i % 3); engine_submit_command(engine, CMD_SET_QUANTIZE_MODE, (int)mode, 0); engine_submit_command(engine, CMD_SET_QUANTIZE_THRESHOLD, 0, (jack_nframes_t)(i * 100)); if (i % 5 == 0) { engine_submit_command(engine, CMD_TRIGGER_CLIP, i % MAX_CLIPS, 0); } if (i % 50 == 0) { engine_process_commands(engine); } } engine_process_commands(engine); destroy_stress_engine(engine); printf(" PASSED\n"); } // Stress test 8: Reset clips while triggering static void stress_reset_while_triggering(void) { printf("Stress test 8: Reset clips while triggering...\n"); Engine *engine = create_stress_engine(); int num_ops = 10000; for (int i = 0; i < num_ops; i++) { int clip_idx = i % MAX_CLIPS; engine_submit_command(engine, CMD_TRIGGER_CLIP, clip_idx, 0); if (i % 3 == 0) { engine_submit_command(engine, CMD_RESET_CLIP, clip_idx, 0); } if (i % 5 == 0) { engine_submit_command(engine, CMD_TRIGGER_SCENE, i % MAX_SCENES, 0); } if (i % 10 == 0) { engine_process_commands(engine); } } engine_process_commands(engine); destroy_stress_engine(engine); printf(" PASSED\n"); } // Stress test 9: Null pointer handling static void stress_null_pointers(void) { printf("Stress test 9: Null pointer handling...\n"); // Call all functions with NULL engine engine_trigger_clip(NULL, 0); engine_trigger_scene(NULL, 0); engine_reset_clip(NULL, 0); engine_set_quantize_mode(NULL, QUANTIZE_OFF); engine_set_quantize_threshold(NULL, 0); engine_transport_stop(NULL); engine_transport_toggle_play(NULL); engine_set_clock_source(NULL, CLOCK_SOURCE_INTERNAL); engine_undo(NULL); engine_redo(NULL); engine_undo_action(NULL); engine_redo_action(NULL); engine_cleanup(NULL); engine_process_commands(NULL); queue_trigger(NULL, 0, false, 0); // Call with invalid indices Engine *engine = create_stress_engine(); engine_trigger_clip(engine, -1); engine_trigger_clip(engine, MAX_CLIPS); engine_trigger_scene(engine, -1); engine_trigger_scene(engine, MAX_SCENES); engine_reset_clip(engine, -1); engine_reset_clip(engine, MAX_CLIPS); destroy_stress_engine(engine); printf(" PASSED\n"); } // Stress test 10: Memory corruption detection static void stress_memory_corruption(void) { printf("Stress test 10: Memory corruption detection...\n"); // Rapid create/destroy cycles for (int i = 0; i < 1000; i++) { Engine *temp = create_stress_engine(); destroy_stress_engine(temp); } // Stress with large allocations for (int i = 0; i < 100; i++) { float *big_buffer = (float *)calloc(MAX_BUFFER_SIZE * 10, sizeof(float)); if (big_buffer) { free(big_buffer); } } printf(" PASSED\n"); } // Stress test 11: Concurrent command submission (multi-threaded) typedef struct { Engine *engine; int thread_id; int num_ops; int *success; int *dropped; } ThreadArg; static void *thread_worker(void *arg) { ThreadArg *targ = (ThreadArg *)arg; Engine *engine = targ->engine; int local_success = 0; int local_dropped = 0; for (int i = 0; i < targ->num_ops; i++) { int clip_idx = (targ->thread_id * 1000 + i) % MAX_CLIPS; int ret = engine_submit_command(engine, CMD_TRIGGER_CLIP, clip_idx, 0); if (ret == 0) { local_success++; } else { local_dropped++; } // Random sleep to increase chance of race conditions if (i % 100 == 0) { random_sleep_us(); } } *targ->success = local_success; *targ->dropped = local_dropped; return NULL; } static void stress_concurrent(void) { printf("Stress test 11: Concurrent command submission (multi-threaded)...\n"); Engine *engine = create_stress_engine(); #define NUM_THREADS 8 pthread_t threads[NUM_THREADS]; ThreadArg args[NUM_THREADS]; int successes[NUM_THREADS]; int droppes[NUM_THREADS]; int ops_per_thread = 10000; for (int t = 0; t < NUM_THREADS; t++) { args[t].engine = engine; args[t].thread_id = t; args[t].num_ops = ops_per_thread; args[t].success = &successes[t]; args[t].dropped = &droppes[t]; pthread_create(&threads[t], NULL, thread_worker, &args[t]); } // Wait for all worker threads to finish before processing commands for (int t = 0; t < NUM_THREADS; t++) { pthread_join(threads[t], NULL); } // Process all submitted commands engine_process_commands(engine); int total_success = 0; int total_dropped = 0; for (int t = 0; t < NUM_THREADS; t++) { total_success += successes[t]; total_dropped += droppes[t]; } printf(" %d threads, %d ops each, %d succeeded, %d dropped\n", NUM_THREADS, ops_per_thread, total_success, total_dropped); CommandQueue *q = &engine->command_queue; assert(atomic_load(&q->write_index) >= atomic_load(&q->read_index)); destroy_stress_engine(engine); printf(" PASSED\n"); } // Stress test 12: Boundary conditions static void stress_boundary(void) { printf("Stress test 12: Boundary conditions...\n"); Engine *engine = create_stress_engine(); // Test with transport in various states for (int state = 0; state < 3; state++) { engine->transport->state = (TransportState)state; atomic_store(&engine->transport->state_atomic, state); // Submit commands while transport is in each state for (int i = 0; i < 1000; i++) { engine_submit_command(engine, CMD_TRIGGER_CLIP, i % MAX_CLIPS, 0); engine_submit_command(engine, CMD_TRIGGER_SCENE, i % MAX_SCENES, 0); engine_submit_command(engine, CMD_RESET_CLIP, i % MAX_CLIPS, 0); } engine_process_commands(engine); } // Test with all quantize modes for (int mode = 0; mode < 3; mode++) { engine->quantize_mode = (QuantizeMode)mode; atomic_store(&engine->quantize_mode_atomic, mode); for (int i = 0; i < 1000; i++) { queue_trigger(engine, i % MAX_CLIPS, false, 100); } // Clean up QueuedTrigger *qt = engine->queued_triggers; while (qt) { QueuedTrigger *next = qt->next; free(qt); qt = next; } engine->queued_triggers = NULL; } destroy_stress_engine(engine); printf(" PASSED\n"); } // Stress test 13: Rapid create/destroy with operations static void stress_create_destroy(void) { printf("Stress test 13: Rapid create/destroy with operations...\n"); for (int i = 0; i < 500; i++) { Engine *engine = create_stress_engine(); // Do some operations for (int j = 0; j < 100; j++) { engine_submit_command(engine, CMD_TRIGGER_CLIP, j % MAX_CLIPS, 0); engine_submit_command(engine, CMD_TRANSPORT_TOGGLE_PLAY, 0, 0); engine_submit_command(engine, CMD_SET_QUANTIZE_MODE, j % 3, 0); } engine_process_commands(engine); // Queue some triggers for (int j = 0; j < 50; j++) { queue_trigger(engine, j % MAX_CLIPS, false, 100); } destroy_stress_engine(engine); } printf(" PASSED\n"); } int main(void) { signal(SIGINT, handle_sigint); signal(SIGSEGV, handle_sigsegv); printf("Running JACK Looper stress tests (nuclear grade)...\n\n"); // Set up segfault protection if (setjmp(segv_jmp) == 0) { stress_command_queue(); } else { printf(" SEGFAULT CAUGHT in stress_command_queue!\n"); return 1; } if (setjmp(segv_jmp) == 0) { stress_mixed_triggers(); } else { printf(" SEGFAULT CAUGHT in stress_mixed_triggers!\n"); return 1; } if (setjmp(segv_jmp) == 0) { stress_queue_overflow(); } else { printf(" SEGFAULT CAUGHT in stress_queue_overflow!\n"); return 1; } if (setjmp(segv_jmp) == 0) { stress_trigger_pool(); } else { printf(" SEGFAULT CAUGHT in stress_trigger_pool!\n"); return 1; } if (setjmp(segv_jmp) == 0) { stress_undo_redo(); } else { printf(" SEGFAULT CAUGHT in stress_undo_redo!\n"); return 1; } if (setjmp(segv_jmp) == 0) { stress_transport(); } else { printf(" SEGFAULT CAUGHT in stress_transport!\n"); return 1; } if (setjmp(segv_jmp) == 0) { stress_quantize(); } else { printf(" SEGFAULT CAUGHT in stress_quantize!\n"); return 1; } if (setjmp(segv_jmp) == 0) { stress_reset_while_triggering(); } else { printf(" SEGFAULT CAUGHT in stress_reset_while_triggering!\n"); return 1; } if (setjmp(segv_jmp) == 0) { stress_null_pointers(); } else { printf(" SEGFAULT CAUGHT in stress_null_pointers!\n"); return 1; } if (setjmp(segv_jmp) == 0) { stress_memory_corruption(); } else { printf(" SEGFAULT CAUGHT in stress_memory_corruption!\n"); return 1; } if (setjmp(segv_jmp) == 0) { stress_concurrent(); } else { printf(" SEGFAULT CAUGHT in stress_concurrent!\n"); return 1; } if (setjmp(segv_jmp) == 0) { stress_boundary(); } else { printf(" SEGFAULT CAUGHT in stress_boundary!\n"); return 1; } if (setjmp(segv_jmp) == 0) { stress_create_destroy(); } else { printf(" SEGFAULT CAUGHT in stress_create_destroy!\n"); return 1; } printf("\nAll stress tests passed!\n"); return 0; }