Files
2026-08-24 10:03:24 +08:00

194 lines
5.1 KiB
C

/*
* test-alsa-midi.c
* SPDX-FileCopyrightText: 2016-2025 BlueALSA developers
* SPDX-License-Identifier: MIT
*/
#if HAVE_CONFIG_H
# include <config.h>
#endif
#include <libgen.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <check.h>
#include <alsa/asoundlib.h>
#include "shared/log.h"
#include "inc/check.inc"
#include "inc/mock.inc"
#include "inc/spawn.inc"
static int test_seq_open(struct spawn_process *sp_ba_mock, snd_seq_t **seq,
int streams, int mode) {
if (spawn_bluealsa_mock(sp_ba_mock, NULL, true,
"--timeout=5000",
"--profile=midi",
NULL) == -1)
return -1;
return snd_seq_open(seq, "default", streams, mode);
}
static int test_seq_create_port(snd_seq_t *seq) {
return snd_seq_create_simple_port(seq, NULL,
SND_SEQ_PORT_CAP_DUPLEX | SND_SEQ_PORT_CAP_READ | SND_SEQ_PORT_CAP_WRITE,
SND_SEQ_PORT_TYPE_MIDI_GENERIC | SND_SEQ_PORT_TYPE_APPLICATION);
}
static int test_seq_close(struct spawn_process *sp_ba_mock, snd_seq_t *seq) {
int rv = 0;
if (seq != NULL)
rv = snd_seq_close(seq);
if (sp_ba_mock != NULL) {
spawn_terminate(sp_ba_mock, 0);
spawn_close(sp_ba_mock, NULL);
}
return rv;
}
CK_START_TEST(test_port) {
struct spawn_process sp_ba_mock;
snd_seq_t *seq = NULL;
ck_assert_int_eq(test_seq_open(&sp_ba_mock, &seq, SND_SEQ_OPEN_DUPLEX, 0), 0);
snd_seq_client_info_t *cinfo;
snd_seq_client_info_alloca(&cinfo);
snd_seq_port_info_t *pinfo;
snd_seq_port_info_alloca(&pinfo);
bool ba_client_found = false;
bool ba_port_found = false;
snd_seq_client_info_set_client(cinfo, -1);
while (snd_seq_query_next_client(seq, cinfo) == 0) {
if (strcmp(snd_seq_client_info_get_name(cinfo), "BlueALSA") != 0)
continue;
ba_client_found = true;
snd_seq_port_info_set_client(pinfo, snd_seq_client_info_get_client(cinfo));
snd_seq_port_info_set_port(pinfo, -1);
while (snd_seq_query_next_port(seq, pinfo) == 0) {
debug("%d:%d - %s", snd_seq_port_info_get_client(pinfo),
snd_seq_port_info_get_port(pinfo), snd_seq_port_info_get_name(pinfo));
ba_port_found = true;
}
}
ck_assert_int_eq(ba_client_found, true);
ck_assert_int_eq(ba_port_found, true);
ck_assert_int_eq(test_seq_close(&sp_ba_mock, seq), 0);
} CK_END_TEST
CK_START_TEST(test_sequencer) {
/* delay in second/10 + raw MIDI data */
static const uint8_t midi[] = {
0, 0xb1, 0x07, 0x7f,
0, 0xc1, 0x49,
0, 0xc2, 0x01,
1, 0x90, 0x40, 0x46,
0, 0x90, 0x41, 0x46,
0, 0x91, 0x50, 0x7f,
5, 0x80, 0x40, 0x0,
0, 0x80, 0x41, 0x0,
15, 0x81, 0x50, 0x0,
};
struct spawn_process sp_ba_mock;
snd_seq_t *seq = NULL;
int port;
ck_assert_int_eq(test_seq_open(&sp_ba_mock, &seq, SND_SEQ_OPEN_DUPLEX, 0), 0);
ck_assert_int_ge(port = test_seq_create_port(seq), 0);
snd_seq_addr_t ba_seq_addr;
ck_assert_int_eq(snd_seq_parse_address(seq, &ba_seq_addr, "BlueALSA"), 0);
ck_assert_int_eq(snd_seq_connect_from(seq, port, ba_seq_addr.client, ba_seq_addr.port), 0);
ck_assert_int_eq(snd_seq_connect_to(seq, port, ba_seq_addr.client, ba_seq_addr.port), 0);
snd_midi_event_t *parser;
ck_assert_int_eq(snd_midi_event_new(1024, &parser), 0);
snd_midi_event_no_status(parser, 1);
long encoded = 0;
for (size_t i = 0; i < sizeof(midi); i += encoded) {
usleep(midi[i++] * 100000);
snd_seq_event_t ev = { 0 };
snd_seq_ev_set_direct(&ev);
snd_seq_ev_set_subs(&ev);
encoded = snd_midi_event_encode(parser, &midi[i], sizeof(midi) - i, &ev);
ck_assert_int_gt(encoded, 0);
ck_assert_int_gt(snd_seq_event_output_direct(seq, &ev), 0);
snd_seq_event_t *ev_o;
uint8_t buf[16];
ck_assert_int_gt(snd_seq_event_input(seq, &ev_o), 0);
ck_assert_int_eq(snd_midi_event_decode(parser, buf, sizeof(buf), ev_o), encoded);
ck_assert_mem_eq(&midi[i], buf, encoded);
}
snd_midi_event_free(parser);
snd_seq_delete_simple_port(seq, port);
ck_assert_int_eq(test_seq_close(&sp_ba_mock, seq), 0);
} CK_END_TEST
int main(int argc, char *argv[]) {
(void)argc;
/* Check whether current host supports ALSA sequencer. If not, then
* there is no point in running this test, because it will fail. */
if (access("/dev/snd/seq", F_OK | R_OK | W_OK) != 0) {
warn("ALSA sequencer not available, skipping test!");
return 77 /* magic number for skipping tests */;
}
char *argv_0 = strdup(argv[0]);
snprintf(bluealsad_mock_path, sizeof(bluealsad_mock_path),
"%s/mock/bluealsad-mock", dirname(argv_0));
Suite *s = suite_create(__FILE__);
TCase *tc = tcase_create(__FILE__);
SRunner *sr = srunner_create(s);
suite_add_tcase(s, tc);
/* snd_seq_event_input() blocks until the event timestamp is reached,
* according to the system clock. The ble_midi_decode() function limits
* the time drift from system clock to 500 ms. So in the worst case,
* with 9 events to process, a total of 4500 ms will pass just blocked
* in calls to snd_seq_event_input(). Added to the total of 2100 ms in
* usleep() calls, this gives a worst-case test time of 6600 ms. */
tcase_set_timeout(tc, 8);
tcase_add_test(tc, test_port);
tcase_add_test(tc, test_sequencer);
srunner_run_all(sr, CK_ENV);
int nf = srunner_ntests_failed(sr);
srunner_free(sr);
free(argv_0);
return nf == 0 ? 0 : 1;
}