From 8c31e2bf6f43e1752472827fc0301e8a4add2d16 Mon Sep 17 00:00:00 2001 From: PJ Date: Sat, 5 Sep 2026 16:40:27 +0530 Subject: [PATCH] fix(testrun): pass the jvm the flag that silences the jdk 24 unsafe warning --- internal/testrun/driver.go | 27 ++++++++++++++++------ internal/testrun/driver_test.go | 41 +++++++++++++++++++++++++++++++++ 2 files changed, 61 insertions(+), 7 deletions(-) diff --git a/internal/testrun/driver.go b/internal/testrun/driver.go index caa1531..8e2ee33 100644 --- a/internal/testrun/driver.go +++ b/internal/testrun/driver.go @@ -154,13 +154,7 @@ func buildDriver(ctx context.Context, options Options, stdout io.Writer) (driver if err != nil { return nil, nil, err } - sidecarArgs := []string{"-jar", jarPath, - "--port", strconv.Itoa(sidecarPort), - "--platform", options.Platform, - } - if options.Device != "" { - sidecarArgs = append(sidecarArgs, "--serial", options.Device) - } + sidecarArgs := sidecarArguments(ctx, jarPath, sidecarPort, options.Platform, options.Device) adbPath, err := android.AdbBinary() if err != nil { return nil, nil, preflightFailure("android", err) @@ -212,6 +206,25 @@ func buildDriver(ctx context.Context, options Options, stdout io.Writer) (driver return driverClient, cleanup, nil } +// From JDK 24 the JVM prints a four-line sun.misc.Unsafe deprecation warning +// on every sidecar start, because the netty that grpc pulls in still reads +// field offsets through it. The flag that silences it did not exist before JDK +// 23 and an older JVM refuses to start when handed it, so ask this JVM whether +// it takes the flag rather than reading its version string. +const unsafeMemoryAccessAllow = "--sun-misc-unsafe-memory-access=allow" + +func sidecarArguments(ctx context.Context, jarPath string, port int, platform, serial string) []string { + var args []string + if exec.CommandContext(ctx, "java", unsafeMemoryAccessAllow, "-version").Run() == nil { + args = append(args, unsafeMemoryAccessAllow) + } + args = append(args, "-jar", jarPath, "--port", strconv.Itoa(port), "--platform", platform) + if serial != "" { + args = append(args, "--serial", serial) + } + return args +} + // watchSidecar reaps the sidecar and publishes its exit status. The channel is // closed after the send so the shutdown path can still receive once the startup // path has taken the status. diff --git a/internal/testrun/driver_test.go b/internal/testrun/driver_test.go index 3bd266b..f5287f5 100644 --- a/internal/testrun/driver_test.go +++ b/internal/testrun/driver_test.go @@ -4,7 +4,10 @@ import ( "context" "errors" "io" + "os" "os/exec" + "path/filepath" + "slices" "strings" "testing" "time" @@ -305,3 +308,41 @@ func TestResolveIOSTargetDeviceResolutionErrorSurfaces(t *testing.T) { t.Fatal("expected the device-resolution error to surface") } } + +func fakeJava(t *testing.T, script string) { + t.Helper() + directory := t.TempDir() + path := filepath.Join(directory, "java") + if err := os.WriteFile(path, []byte("#!/bin/sh\n"+script+"\n"), 0o755); err != nil { + t.Fatalf("write fake java: %v", err) + } + t.Setenv("PATH", directory) +} + +func TestSidecarArgumentsSilencesTheUnsafeWarningOnAJvmThatTakesTheFlag(t *testing.T) { + fakeJava(t, "exit 0") + + args := sidecarArguments(context.Background(), "/tmp/sidecar.jar", 51129, "android", "663c91b1") + + want := []string{ + "--sun-misc-unsafe-memory-access=allow", + "-jar", "/tmp/sidecar.jar", + "--port", "51129", + "--platform", "android", + "--serial", "663c91b1", + } + if !slices.Equal(args, want) { + t.Fatalf("sidecar argv = %q, want %q", args, want) + } +} + +func TestSidecarArgumentsOmitsTheFlagAJvmBefore23WouldRefuseToStartWith(t *testing.T) { + fakeJava(t, `case "$1" in --sun-misc-unsafe-memory-access=*) echo "Unrecognized option: $1" >&2; exit 1;; esac; exit 0`) + + args := sidecarArguments(context.Background(), "/tmp/sidecar.jar", 51129, "android", "") + + want := []string{"-jar", "/tmp/sidecar.jar", "--port", "51129", "--platform", "android"} + if !slices.Equal(args, want) { + t.Fatalf("sidecar argv = %q, want %q", args, want) + } +}