tools/test-runner: add option to start Pipewire inside the VM
authorPauli Virtanen <pav@iki.fi>
Sat, 13 May 2023 16:08:37 +0000 (16:08 +0000)
committerAyush Garg <ayush.garg@samsung.com>
Fri, 5 Jan 2024 13:34:03 +0000 (19:04 +0530)
Add option for launching Pipewire inside the VM to serve Bluetooth
endpoints, which can be used in tests.

Make the option to optionally take path to the audio daemon, so e.g.
Pulseaudio support can be added later.

tools/test-runner.c

index 685f737..40603b1 100755 (executable)
@@ -53,6 +53,7 @@ static bool start_monitor = false;
 static int num_devs = 0;
 static const char *qemu_binary = NULL;
 static const char *kernel_image = NULL;
+static char *audio_server;
 
 static const char *qemu_table[] = {
        "qemu-system-x86_64",
@@ -251,11 +252,11 @@ static void start_qemu(void)
                                "TESTHOME=%s TESTDBUS=%u TESTDAEMON=%u "
                                "TESTDBUSSESSION=%u XDG_RUNTIME_DIR=/run/user/0 "
                                "TESTMONITOR=%u TESTEMULATOR=%u TESTDEVS=%d "
-                               "TESTAUTO=%u TESTARGS=\'%s\'",
+                               "TESTAUTO=%u TESTAUDIO='%s' TESTARGS=\'%s\'",
                                initcmd, cwd, start_dbus, start_daemon,
                                start_dbus_session,
                                start_monitor, start_emulator, num_devs,
-                               run_auto, testargs);
+                               run_auto, audio_server, testargs);
 
        argv = alloca(sizeof(qemu_argv) +
                                (sizeof(char *) * (4 + (num_devs * 4))));
@@ -722,6 +723,112 @@ static pid_t start_btvirt(const char *home)
        return pid;
 }
 
+static int create_pipewire_conf(void)
+{
+       static const char *const dirs[] = {
+               "/run/conf",
+               "/run/conf/wireplumber",
+               "/run/conf/wireplumber/bluetooth.lua.d",
+               "/run/conf/wireplumber/main.lua.d",
+               NULL
+       };
+       int i;
+       FILE *f;
+
+       for (i = 0; dirs[i]; ++i)
+               mkdir(dirs[i], 0755);
+
+       /* Enable only Bluetooth part, disable whatever requires user DBus */
+       f = fopen("/run/conf/wireplumber/main.lua.d/51-custom.lua", "w");
+       if (!f)
+               goto fail;
+
+       fprintf(f, "alsa_monitor.enabled = false\n"
+               "v4l2_monitor.enabled = false\n"
+               "libcamera_monitor.enabled = false\n"
+               "default_access.properties[\"enable-flatpak-portal\"]"
+               " = false\n");
+       fclose(f);
+
+       f = fopen("/run/conf/wireplumber/bluetooth.lua.d/51-custom.lua", "w");
+       if (!f)
+               goto fail;
+
+       fprintf(f, "bluez_monitor.properties[\"with-logind\"] = false\n"
+               "bluez_midi_monitor.enabled = false\n");
+       fclose(f);
+
+       return 0;
+
+       fail:
+       perror("Failed to create Pipewire config");
+       return -1;
+}
+
+static int start_audio_server(pid_t pids[2])
+{
+       char *daemons[2] = {NULL, NULL};
+       char wp_exe[PATH_MAX];
+       char *ptr;
+       char *envp[5];
+       int i;
+
+       for (i = 0; i < 2; ++i)
+               pids[i] = -1;
+
+       daemons[0] = audio_server;
+
+       ptr = strrchr(audio_server, '/');
+       if (ptr && !strcmp(ptr, "/pipewire")) {
+               if (create_pipewire_conf())
+                       return -1;
+
+               snprintf(wp_exe, sizeof(wp_exe), "%.*s/wireplumber",
+                       (int)(ptr - audio_server), audio_server);
+               daemons[1] = wp_exe;
+
+               setenv("PIPEWIRE_RUNTIME_DIR", "/run", 1);
+       }
+
+       envp[0] = "DBUS_SYSTEM_BUS_ADDRESS=unix:"
+               "path=/run/dbus/system_bus_socket";
+       envp[1] = "XDG_CONFIG_HOME=/run/conf";
+       envp[2] = "XDG_STATE_HOME=/run";
+       envp[3] = "XDG_RUNTIME_DIR=/run";
+       envp[4] = NULL;
+
+       for (i = 0; i < 2; ++i) {
+               const char *daemon = daemons[i];
+               char *argv[2];
+               pid_t pid;
+
+               if (!daemon)
+                       continue;
+
+               printf("Starting audio server %s\n", daemon);
+
+               argv[0] = (char *) daemon;
+               argv[1] = NULL;
+
+               pid = fork();
+               if (pid < 0) {
+                       perror("Failed to fork new process");
+                       return -1;
+               }
+
+               if (pid == 0) {
+                       execve(argv[0], argv, envp);
+                       exit(EXIT_SUCCESS);
+               }
+
+               pids[i] = pid;
+
+               printf("Audio server process %d created\n", pid);
+       }
+
+       return 0;
+}
+
 static void run_command(char *cmdname, char *home)
 {
 #ifdef TIZEN_FEATURE_BLUEZ_MODIFY
@@ -732,7 +839,8 @@ static void run_command(char *cmdname, char *home)
        int pos = 0, idx = 0;
        int serial_fd;
        pid_t pid, dbus_pid, daemon_pid, monitor_pid, emulator_pid,
-       dbus_session_pid;
+       dbus_session_pid, audio_pid[2];
+       int i;
 
        if (!home) {
                perror("Invalid parameter: TESTHOME");
@@ -780,6 +888,11 @@ static void run_command(char *cmdname, char *home)
        else
                emulator_pid = -1;
 
+       if (audio_server)
+               start_audio_server(audio_pid);
+       else
+               audio_pid[0] = audio_pid[1] = -1;
+
 start_next:
        if (run_auto) {
                if (chdir(home + 5) < 0) {
@@ -881,6 +994,13 @@ start_next:
                        monitor_pid = -1;
                }
 
+               for (i = 0; i < 2; ++i) {
+                       if (corpse == audio_pid[i]) {
+                               printf("Audio server %d terminated\n", i);
+                               audio_pid[i] = -1;
+                       }
+               }
+
                if (corpse == pid)
                        break;
        }
@@ -890,6 +1010,11 @@ start_next:
                goto start_next;
        }
 
+       for (i = 0; i < 2; ++i) {
+               if (audio_pid[i] > 0)
+                       kill(audio_pid[i], SIGTERM);
+       }
+
        if (daemon_pid > 0)
                kill(daemon_pid, SIGTERM);
 
@@ -985,6 +1110,17 @@ static void run_tests(void)
                start_emulator = true;
        }
 
+       ptr = strstr(cmdline, "TESTAUDIO='");
+       if (ptr) {
+               const char *start = ptr + 11;
+               const char *end = strchr(start, '\'');
+
+               if (end) {
+                       audio_server = strndup(start, end - start);
+                       printf("Audio server %s requested\n", audio_server);
+               }
+       }
+
        ptr = strstr(cmdline, "TESTHOME=");
        if (ptr) {
                home = ptr + 4;
@@ -1008,6 +1144,7 @@ static void usage(void)
                "\t-d, --daemon           Start bluetoothd\n"
                "\t-m, --monitor          Start btmon\n"
                "\t-l, --emulator         Start btvirt\n"
+               "\t-A, --audio[=path]     Start audio server\n"
                "\t-u, --unix [path]      Provide serial device\n"
                "\t-q, --qemu <path>      QEMU binary\n"
                "\t-k, --kernel <image>   Kernel image (bzImage)\n"
@@ -1025,6 +1162,7 @@ static const struct option main_options[] = {
        { "monitor", no_argument,       NULL, 'm' },
        { "qemu",    required_argument, NULL, 'q' },
        { "kernel",  required_argument, NULL, 'k' },
+       { "audio",   optional_argument, NULL, 'A' },
        { "version", no_argument,       NULL, 'v' },
        { "help",    no_argument,       NULL, 'h' },
        { }
@@ -1044,7 +1182,7 @@ int main(int argc, char *argv[])
        for (;;) {
                int opt;
 
-               opt = getopt_long(argc, argv, "aubdslmq:k:vh", main_options,
+               opt = getopt_long(argc, argv, "aubdslmq:k:A::vh", main_options,
                                                                NULL);
                if (opt < 0)
                        break;
@@ -1078,6 +1216,9 @@ int main(int argc, char *argv[])
                case 'k':
                        kernel_image = optarg;
                        break;
+               case 'A':
+                       audio_server = optarg ? optarg : "/usr/bin/pipewire";
+                       break;
                case 'v':
                        printf("%s\n", VERSION);
                        return EXIT_SUCCESS;