test(ui): harden gtk display probe tests
- commit
- e34ffa650d93eaa593988ca259237e215ed54cf9
- author
- brice · · 2026-06-20 13:14 UTC
- parent
- 1d86c5b
- signature
- GPG Verified
CD88F9E564E0102646FB6077A1A07C7C7C3E349D· arch-gpg Verified for the committer email
1 file · +148 -39
M internal/ui/app_browser_launch_test.go +148 -39
| @@ -7,6 +7,7 @@ import ( | |||
| 7 | 7 | "errors" | |
| 8 | 8 | "io" | |
| 9 | 9 | "math" | |
| 10 | + | "net" | |
| 10 | 11 | "os" | |
| 11 | 12 | "path/filepath" | |
| 12 | 13 | "reflect" | |
| @@ -81,7 +82,28 @@ func (r *fakeSessionStateRepo) GetAllSnapshots(context.Context) ([]*entity.Sessi | |||
| 81 | 82 | ||
| 82 | 83 | func (r *fakeSessionStateRepo) GetTotalSnapshotsSize(context.Context) (int64, error) { return 0, nil } | |
| 83 | 84 | ||
| 85 | + | func testPathIsSocket(path string) bool { | |
| 86 | + | info, err := os.Stat(path) | |
| 87 | + | return err == nil && info.Mode()&os.ModeSocket != 0 | |
| 88 | + | } | |
| 89 | + | ||
| 90 | + | func testGDKBackendAllows(backend string) bool { | |
| 91 | + | configured := os.Getenv("GDK_BACKEND") | |
| 92 | + | if configured == "" { | |
| 93 | + | return true | |
| 94 | + | } | |
| 95 | + | for _, candidate := range strings.Split(configured, ",") { | |
| 96 | + | if strings.TrimSpace(candidate) == backend { | |
| 97 | + | return true | |
| 98 | + | } | |
| 99 | + | } | |
| 100 | + | return false | |
| 101 | + | } | |
| 102 | + | ||
| 84 | 103 | func testHasUsableWaylandDisplay() bool { | |
| 104 | + | if !testGDKBackendAllows("wayland") { | |
| 105 | + | return false | |
| 106 | + | } | |
| 85 | 107 | waylandDisplay := os.Getenv("WAYLAND_DISPLAY") | |
| 86 | 108 | if waylandDisplay == "" { | |
| 87 | 109 | return false | |
| @@ -91,7 +113,7 @@ func testHasUsableWaylandDisplay() bool { | |||
| 91 | 113 | candidates = append([]string{filepath.Join(runtimeDir, waylandDisplay)}, candidates...) | |
| 92 | 114 | } | |
| 93 | 115 | for _, candidate := range candidates { | |
| 94 | - | if _, err := os.Stat(candidate); err == nil { | |
| 116 | + | if testPathIsSocket(candidate) { | |
| 95 | 117 | return true | |
| 96 | 118 | } | |
| 97 | 119 | } | |
| @@ -99,6 +121,9 @@ func testHasUsableWaylandDisplay() bool { | |||
| 99 | 121 | } | |
| 100 | 122 | ||
| 101 | 123 | func testHasUsableX11Display() bool { | |
| 124 | + | if !testGDKBackendAllows("x11") { | |
| 125 | + | return false | |
| 126 | + | } | |
| 102 | 127 | display := os.Getenv("DISPLAY") | |
| 103 | 128 | if display == "" { | |
| 104 | 129 | return false | |
| @@ -114,7 +139,7 @@ func testHasUsableX11Display() bool { | |||
| 114 | 139 | x11SocketCandidates = append(x11SocketCandidates, fallback) | |
| 115 | 140 | } | |
| 116 | 141 | for _, candidate := range x11SocketCandidates { | |
| 117 | - | if _, err := os.Stat(candidate); err == nil { | |
| 142 | + | if testPathIsSocket(candidate) { | |
| 118 | 143 | return true | |
| 119 | 144 | } | |
| 120 | 145 | } | |
| @@ -192,8 +217,8 @@ func testXauthorityHasDisplay(data []byte, displayNum string) bool { | |||
| 192 | 217 | if !ok { | |
| 193 | 218 | return false | |
| 194 | 219 | } | |
| 195 | - | if string(num) == displayNum { | |
| 196 | - | return len(name) > 0 && len(cookie) > 0 | |
| 220 | + | if string(num) == displayNum && len(name) > 0 && len(cookie) > 0 { | |
| 221 | + | return true | |
| 197 | 222 | } | |
| 198 | 223 | data = rest | |
| 199 | 224 | } | |
| @@ -212,12 +237,49 @@ func testReadXauthorityField(data []byte) ([]byte, []byte, bool) { | |||
| 212 | 237 | return data[:fieldLen], data[fieldLen:], true | |
| 213 | 238 | } | |
| 214 | 239 | ||
| 215 | - | // testNeedsX11Auth reports whether GTK is expected to use the X11 backend, | |
| 216 | - | // meaning X11 authorization must be available. When a usable Wayland display | |
| 217 | - | // is present and GDK_BACKEND is not forced to x11, X11 auth is not required | |
| 218 | - | // because GTK will use the Wayland backend. | |
| 240 | + | // testNeedsX11Auth reports whether GTK may need the X11 backend and therefore | |
| 241 | + | // requires X11 authorization before GTK initialization. Wayland-only backend | |
| 242 | + | // configuration never needs X11 auth; X11 is required when it is the only usable | |
| 243 | + | // allowed backend. | |
| 219 | 244 | func testNeedsX11Auth() bool { | |
| 220 | - | return !testHasUsableWaylandDisplay() || os.Getenv("GDK_BACKEND") == "x11" | |
| 245 | + | if !testGDKBackendAllows("x11") { | |
| 246 | + | return false | |
| 247 | + | } | |
| 248 | + | return !testGDKBackendAllows("wayland") || !testHasUsableWaylandDisplay() | |
| 249 | + | } | |
| 250 | + | ||
| 251 | + | func testCreateUnixSocket(t *testing.T, path string) { | |
| 252 | + | t.Helper() | |
| 253 | + | if err := os.MkdirAll(filepath.Dir(path), 0755); err != nil { | |
| 254 | + | t.Fatal(err) | |
| 255 | + | } | |
| 256 | + | listener, err := net.Listen("unix", path) | |
| 257 | + | if err != nil { | |
| 258 | + | t.Fatal(err) | |
| 259 | + | } | |
| 260 | + | t.Cleanup(func() { | |
| 261 | + | _ = listener.Close() | |
| 262 | + | _ = os.Remove(path) | |
| 263 | + | }) | |
| 264 | + | } | |
| 265 | + | ||
| 266 | + | func testUnusedX11DisplayNumber(t *testing.T) string { | |
| 267 | + | t.Helper() | |
| 268 | + | for display := 90; display < 300; display++ { | |
| 269 | + | displayNum := strconv.Itoa(display) | |
| 270 | + | if !testPathIsSocket("/tmp/.X11-unix/X" + displayNum) { | |
| 271 | + | return displayNum | |
| 272 | + | } | |
| 273 | + | } | |
| 274 | + | t.Fatal("could not find an unused X11 display number") | |
| 275 | + | return "" | |
| 276 | + | } | |
| 277 | + | ||
| 278 | + | func testCreateX11Socket(t *testing.T, displayNum string) { | |
| 279 | + | t.Helper() | |
| 280 | + | tmpDir := t.TempDir() | |
| 281 | + | t.Setenv("TMPDIR", tmpDir) | |
| 282 | + | testCreateUnixSocket(t, filepath.Join(tmpDir, ".X11-unix", "X"+displayNum)) | |
| 221 | 283 | } | |
| 222 | 284 | ||
| 223 | 285 | func requireGTKDisplayApp(t *testing.T) *gtk.Application { | |
| @@ -227,8 +289,7 @@ func requireGTKDisplayApp(t *testing.T) *gtk.Application { | |||
| 227 | 289 | if !hasWayland && !hasX11 { | |
| 228 | 290 | t.Skip("GTK display not available (no DISPLAY/WAYLAND_DISPLAY socket)") | |
| 229 | 291 | } | |
| 230 | - | // Only require X11 auth when the X11 backend will be used: | |
| 231 | - | // no Wayland (fallback to X11), or GDK_BACKEND explicitly set to x11. | |
| 292 | + | // Only require X11 auth when X11 is the only usable allowed backend. | |
| 232 | 293 | if testNeedsX11Auth() && !testHasX11Auth() { | |
| 233 | 294 | t.Skip("GTK display not available (X11 authorization unavailable)") | |
| 234 | 295 | } | |
| @@ -254,16 +315,17 @@ func Test_testHasUsableGTKDisplay_PureProbe(t *testing.T) { | |||
| 254 | 315 | }) | |
| 255 | 316 | ||
| 256 | 317 | t.Run("DISPLAY numeric without socket", func(t *testing.T) { | |
| 257 | - | t.Setenv("DISPLAY", ":99") | |
| 318 | + | displayNum := testUnusedX11DisplayNumber(t) | |
| 319 | + | t.Setenv("DISPLAY", ":"+displayNum) | |
| 258 | 320 | t.Setenv("WAYLAND_DISPLAY", "") | |
| 259 | - | // No /tmp/.X11-unix/X99 socket exists | |
| 260 | 321 | if got := testHasUsableGTKDisplay(); got { | |
| 261 | 322 | t.Errorf("testHasUsableGTKDisplay() = %v, want false", got) | |
| 262 | 323 | } | |
| 263 | 324 | }) | |
| 264 | 325 | ||
| 265 | 326 | t.Run("DISPLAY with screen without socket", func(t *testing.T) { | |
| 266 | - | t.Setenv("DISPLAY", ":99.0") | |
| 327 | + | displayNum := testUnusedX11DisplayNumber(t) | |
| 328 | + | t.Setenv("DISPLAY", ":"+displayNum+".0") | |
| 267 | 329 | t.Setenv("WAYLAND_DISPLAY", "") | |
| 268 | 330 | if got := testHasUsableGTKDisplay(); got { | |
| 269 | 331 | t.Errorf("testHasUsableGTKDisplay() = %v, want false", got) | |
| @@ -287,13 +349,15 @@ func Test_testHasUsableGTKDisplay_PureProbe(t *testing.T) { | |||
| 287 | 349 | }) | |
| 288 | 350 | ||
| 289 | 351 | t.Run("DISPLAY with existing socket", func(t *testing.T) { | |
| 290 | - | t.Setenv("DISPLAY", ":0") | |
| 352 | + | displayNum := testUnusedX11DisplayNumber(t) | |
| 353 | + | tmpDir := t.TempDir() | |
| 354 | + | t.Setenv("TMPDIR", tmpDir) | |
| 355 | + | t.Setenv("DISPLAY", ":"+displayNum) | |
| 291 | 356 | t.Setenv("WAYLAND_DISPLAY", "") | |
| 292 | - | // /tmp/.X11-unix/X0 exists on this machine | |
| 293 | - | got := testHasUsableGTKDisplay() | |
| 294 | - | // We can't guarantee the socket exists in all environments, | |
| 295 | - | // but this verifies it doesn't crash. | |
| 296 | - | t.Logf("testHasUsableGTKDisplay() = %v (depends on /tmp/.X11-unix/X0)", got) | |
| 357 | + | testCreateUnixSocket(t, filepath.Join(tmpDir, ".X11-unix", "X"+displayNum)) | |
| 358 | + | if got := testHasUsableGTKDisplay(); !got { | |
| 359 | + | t.Errorf("testHasUsableGTKDisplay() = %v, want true with existing X11 socket", got) | |
| 360 | + | } | |
| 297 | 361 | }) | |
| 298 | 362 | ||
| 299 | 363 | t.Run("WAYLAND_DISPLAY with existing socket", func(t *testing.T) { | |
| @@ -301,9 +365,7 @@ func Test_testHasUsableGTKDisplay_PureProbe(t *testing.T) { | |||
| 301 | 365 | t.Setenv("WAYLAND_DISPLAY", "wayland-1") | |
| 302 | 366 | tmpDir := t.TempDir() | |
| 303 | 367 | socketPath := filepath.Join(tmpDir, "wayland-1") | |
| 304 | - | if err := os.WriteFile(socketPath, nil, 0644); err != nil { | |
| 305 | - | t.Fatal(err) | |
| 306 | - | } | |
| 368 | + | testCreateUnixSocket(t, socketPath) | |
| 307 | 369 | // XDG_RUNTIME_DIR controls where Wayland sockets are looked up | |
| 308 | 370 | t.Setenv("XDG_RUNTIME_DIR", tmpDir) | |
| 309 | 371 | if got := testHasUsableGTKDisplay(); !got { | |
| @@ -323,9 +385,7 @@ func Test_testHasUsableGTKDisplay_PureProbe(t *testing.T) { | |||
| 323 | 385 | t.Run("WAYLAND_DISPLAY absolute path with existing socket", func(t *testing.T) { | |
| 324 | 386 | t.Setenv("DISPLAY", "") | |
| 325 | 387 | socketPath := filepath.Join(t.TempDir(), "wayland.sock") | |
| 326 | - | if err := os.WriteFile(socketPath, nil, 0644); err != nil { | |
| 327 | - | t.Fatal(err) | |
| 328 | - | } | |
| 388 | + | testCreateUnixSocket(t, socketPath) | |
| 329 | 389 | t.Setenv("WAYLAND_DISPLAY", socketPath) | |
| 330 | 390 | t.Setenv("XDG_RUNTIME_DIR", "/nonexistent") // Should be ignored for absolute paths | |
| 331 | 391 | if got := testHasUsableGTKDisplay(); !got { | |
| @@ -518,6 +578,21 @@ func Test_testHasX11Auth_MalformedRecords(t *testing.T) { | |||
| 518 | 578 | t.Errorf("testHasX11Auth() = %v, want true for third entry matching display :3", got) | |
| 519 | 579 | } | |
| 520 | 580 | }) | |
| 581 | + | ||
| 582 | + | t.Run("invalid matching entry does not hide later valid match", func(t *testing.T) { | |
| 583 | + | t.Setenv("DISPLAY", ":3") | |
| 584 | + | authFile := filepath.Join(t.TempDir(), ".Xauthority") | |
| 585 | + | var buf bytes.Buffer | |
| 586 | + | writeXauthRecord(&buf, "3", "", []byte{0x01, 0x02, 0x03, 0x04}) | |
| 587 | + | writeXauthRecord(&buf, "3", "MIT-MAGIC-COOKIE-1", []byte{0x09, 0x0a, 0x0b, 0x0c}) | |
| 588 | + | if err := os.WriteFile(authFile, buf.Bytes(), 0644); err != nil { | |
| 589 | + | t.Fatal(err) | |
| 590 | + | } | |
| 591 | + | t.Setenv("XAUTHORITY", authFile) | |
| 592 | + | if got := testHasX11Auth(); !got { | |
| 593 | + | t.Errorf("testHasX11Auth() = %v, want true for later valid matching display", got) | |
| 594 | + | } | |
| 595 | + | }) | |
| 521 | 596 | } | |
| 522 | 597 | ||
| 523 | 598 | func Test_testHasUsableWaylandDisplay_PureProbe(t *testing.T) { | |
| @@ -532,9 +607,7 @@ func Test_testHasUsableWaylandDisplay_PureProbe(t *testing.T) { | |||
| 532 | 607 | t.Setenv("WAYLAND_DISPLAY", "wayland-1") | |
| 533 | 608 | tmpDir := t.TempDir() | |
| 534 | 609 | socketPath := filepath.Join(tmpDir, "wayland-1") | |
| 535 | - | if err := os.WriteFile(socketPath, nil, 0644); err != nil { | |
| 536 | - | t.Fatal(err) | |
| 537 | - | } | |
| 610 | + | testCreateUnixSocket(t, socketPath) | |
| 538 | 611 | t.Setenv("XDG_RUNTIME_DIR", tmpDir) | |
| 539 | 612 | if got := testHasUsableWaylandDisplay(); !got { | |
| 540 | 613 | t.Errorf("testHasUsableWaylandDisplay() = %v, want true with existing wayland socket", got) | |
| @@ -543,9 +616,7 @@ func Test_testHasUsableWaylandDisplay_PureProbe(t *testing.T) { | |||
| 543 | 616 | ||
| 544 | 617 | t.Run("WAYLAND_DISPLAY absolute path", func(t *testing.T) { | |
| 545 | 618 | socketPath := filepath.Join(t.TempDir(), "wayland.sock") | |
| 546 | - | if err := os.WriteFile(socketPath, nil, 0644); err != nil { | |
| 547 | - | t.Fatal(err) | |
| 548 | - | } | |
| 619 | + | testCreateUnixSocket(t, socketPath) | |
| 549 | 620 | t.Setenv("WAYLAND_DISPLAY", socketPath) | |
| 550 | 621 | t.Setenv("XDG_RUNTIME_DIR", "/nonexistent") | |
| 551 | 622 | if got := testHasUsableWaylandDisplay(); !got { | |
| @@ -563,7 +634,8 @@ func Test_testHasUsableX11Display_PureProbe(t *testing.T) { | |||
| 563 | 634 | }) | |
| 564 | 635 | ||
| 565 | 636 | t.Run("DISPLAY numeric without socket", func(t *testing.T) { | |
| 566 | - | t.Setenv("DISPLAY", ":99") | |
| 637 | + | displayNum := testUnusedX11DisplayNumber(t) | |
| 638 | + | t.Setenv("DISPLAY", ":"+displayNum) | |
| 567 | 639 | if got := testHasUsableX11Display(); got { | |
| 568 | 640 | t.Errorf("testHasUsableX11Display() = %v, want false", got) | |
| 569 | 641 | } | |
| @@ -577,15 +649,54 @@ func Test_testHasUsableX11Display_PureProbe(t *testing.T) { | |||
| 577 | 649 | }) | |
| 578 | 650 | } | |
| 579 | 651 | ||
| 652 | + | func Test_testHasUsableGTKDisplay_GDKBackendSelection(t *testing.T) { | |
| 653 | + | t.Run("GDK_BACKEND=x11 ignores Wayland-only socket", func(t *testing.T) { | |
| 654 | + | t.Setenv("GDK_BACKEND", "x11") | |
| 655 | + | t.Setenv("WAYLAND_DISPLAY", "wayland-1") | |
| 656 | + | tmpDir := t.TempDir() | |
| 657 | + | testCreateUnixSocket(t, filepath.Join(tmpDir, "wayland-1")) | |
| 658 | + | t.Setenv("XDG_RUNTIME_DIR", tmpDir) | |
| 659 | + | t.Setenv("DISPLAY", ":"+testUnusedX11DisplayNumber(t)) | |
| 660 | + | if got := testHasUsableGTKDisplay(); got { | |
| 661 | + | t.Errorf("testHasUsableGTKDisplay() = %v, want false when x11 is forced without an X11 socket", got) | |
| 662 | + | } | |
| 663 | + | }) | |
| 664 | + | ||
| 665 | + | t.Run("GDK_BACKEND=wayland ignores X11-only socket", func(t *testing.T) { | |
| 666 | + | displayNum := testUnusedX11DisplayNumber(t) | |
| 667 | + | t.Setenv("GDK_BACKEND", "wayland") | |
| 668 | + | t.Setenv("DISPLAY", ":"+displayNum) | |
| 669 | + | t.Setenv("WAYLAND_DISPLAY", "wayland-missing") | |
| 670 | + | t.Setenv("XDG_RUNTIME_DIR", t.TempDir()) | |
| 671 | + | testCreateX11Socket(t, displayNum) | |
| 672 | + | if got := testHasUsableGTKDisplay(); got { | |
| 673 | + | t.Errorf("testHasUsableGTKDisplay() = %v, want false when wayland is forced without a Wayland socket", got) | |
| 674 | + | } | |
| 675 | + | }) | |
| 676 | + | ||
| 677 | + | t.Run("GDK_BACKEND=wayland,x11 allows X11 fallback", func(t *testing.T) { | |
| 678 | + | displayNum := testUnusedX11DisplayNumber(t) | |
| 679 | + | t.Setenv("GDK_BACKEND", "wayland,x11") | |
| 680 | + | t.Setenv("DISPLAY", ":"+displayNum) | |
| 681 | + | t.Setenv("WAYLAND_DISPLAY", "wayland-missing") | |
| 682 | + | t.Setenv("XDG_RUNTIME_DIR", t.TempDir()) | |
| 683 | + | testCreateX11Socket(t, displayNum) | |
| 684 | + | if got := testHasUsableGTKDisplay(); !got { | |
| 685 | + | t.Errorf("testHasUsableGTKDisplay() = %v, want true with X11 fallback socket", got) | |
| 686 | + | } | |
| 687 | + | if got := testNeedsX11Auth(); !got { | |
| 688 | + | t.Errorf("testNeedsX11Auth() = %v, want true when X11 is the only usable backend", got) | |
| 689 | + | } | |
| 690 | + | }) | |
| 691 | + | } | |
| 692 | + | ||
| 580 | 693 | func Test_testNeedsX11Auth_PureProbe(t *testing.T) { | |
| 581 | 694 | t.Run("Wayland available forces no X11 auth", func(t *testing.T) { | |
| 582 | 695 | t.Setenv("GDK_BACKEND", "") | |
| 583 | 696 | t.Setenv("WAYLAND_DISPLAY", "wayland-1") | |
| 584 | 697 | tmpDir := t.TempDir() | |
| 585 | 698 | socketPath := filepath.Join(tmpDir, "wayland-1") | |
| 586 | - | if err := os.WriteFile(socketPath, nil, 0644); err != nil { | |
| 587 | - | t.Fatal(err) | |
| 588 | - | } | |
| 699 | + | testCreateUnixSocket(t, socketPath) | |
| 589 | 700 | t.Setenv("XDG_RUNTIME_DIR", tmpDir) | |
| 590 | 701 | t.Setenv("DISPLAY", ":0") | |
| 591 | 702 | if got := testNeedsX11Auth(); got { | |
| @@ -598,9 +709,7 @@ func Test_testNeedsX11Auth_PureProbe(t *testing.T) { | |||
| 598 | 709 | t.Setenv("WAYLAND_DISPLAY", "wayland-1") | |
| 599 | 710 | tmpDir := t.TempDir() | |
| 600 | 711 | socketPath := filepath.Join(tmpDir, "wayland-1") | |
| 601 | - | if err := os.WriteFile(socketPath, nil, 0644); err != nil { | |
| 602 | - | t.Fatal(err) | |
| 603 | - | } | |
| 712 | + | testCreateUnixSocket(t, socketPath) | |
| 604 | 713 | t.Setenv("XDG_RUNTIME_DIR", tmpDir) | |
| 605 | 714 | if got := testNeedsX11Auth(); !got { | |
| 606 | 715 | t.Errorf("testNeedsX11Auth() = %v, want true when GDK_BACKEND=x11", got) | |
| @@ -7,6 +7,7 @@ import ( | |||||
| 7 | "errors" | 7 | "errors" | ||
| 8 | "io" | 8 | "io" | ||
| 9 | "math" | 9 | "math" | ||
| 10 | + | "net" | |||
| 10 | "os" | 11 | "os" | ||
| 11 | "path/filepath" | 12 | "path/filepath" | ||
| 12 | "reflect" | 13 | "reflect" | ||
| @@ -81,7 +82,28 @@ func (r *fakeSessionStateRepo) GetAllSnapshots(context.Context) ([]*entity.Sessi | |||||
| 81 | 82 | ||||
| 82 | func (r *fakeSessionStateRepo) GetTotalSnapshotsSize(context.Context) (int64, error) { return 0, nil } | 83 | func (r *fakeSessionStateRepo) GetTotalSnapshotsSize(context.Context) (int64, error) { return 0, nil } | ||
| 83 | 84 | ||||
| 85 | + | func testPathIsSocket(path string) bool { | |||
| 86 | + | info, err := os.Stat(path) | |||
| 87 | + | return err == nil && info.Mode()&os.ModeSocket != 0 | |||
| 88 | + | } | |||
| 89 | + | ||||
| 90 | + | func testGDKBackendAllows(backend string) bool { | |||
| 91 | + | configured := os.Getenv("GDK_BACKEND") | |||
| 92 | + | if configured == "" { | |||
| 93 | + | return true | |||
| 94 | + | } | |||
| 95 | + | for _, candidate := range strings.Split(configured, ",") { | |||
| 96 | + | if strings.TrimSpace(candidate) == backend { | |||
| 97 | + | return true | |||
| 98 | + | } | |||
| 99 | + | } | |||
| 100 | + | return false | |||
| 101 | + | } | |||
| 102 | + | ||||
| 84 | func testHasUsableWaylandDisplay() bool { | 103 | func testHasUsableWaylandDisplay() bool { | ||
| 104 | + | if !testGDKBackendAllows("wayland") { | |||
| 105 | + | return false | |||
| 106 | + | } | |||
| 85 | waylandDisplay := os.Getenv("WAYLAND_DISPLAY") | 107 | waylandDisplay := os.Getenv("WAYLAND_DISPLAY") | ||
| 86 | if waylandDisplay == "" { | 108 | if waylandDisplay == "" { | ||
| 87 | return false | 109 | return false | ||
| @@ -91,7 +113,7 @@ func testHasUsableWaylandDisplay() bool { | |||||
| 91 | candidates = append([]string{filepath.Join(runtimeDir, waylandDisplay)}, candidates...) | 113 | candidates = append([]string{filepath.Join(runtimeDir, waylandDisplay)}, candidates...) | ||
| 92 | } | 114 | } | ||
| 93 | for _, candidate := range candidates { | 115 | for _, candidate := range candidates { | ||
| 94 | - | if _, err := os.Stat(candidate); err == nil { | 116 | + | if testPathIsSocket(candidate) { |
| 95 | return true | 117 | return true | ||
| 96 | } | 118 | } | ||
| 97 | } | 119 | } | ||
| @@ -99,6 +121,9 @@ func testHasUsableWaylandDisplay() bool { | |||||
| 99 | } | 121 | } | ||
| 100 | 122 | ||||
| 101 | func testHasUsableX11Display() bool { | 123 | func testHasUsableX11Display() bool { | ||
| 124 | + | if !testGDKBackendAllows("x11") { | |||
| 125 | + | return false | |||
| 126 | + | } | |||
| 102 | display := os.Getenv("DISPLAY") | 127 | display := os.Getenv("DISPLAY") | ||
| 103 | if display == "" { | 128 | if display == "" { | ||
| 104 | return false | 129 | return false | ||
| @@ -114,7 +139,7 @@ func testHasUsableX11Display() bool { | |||||
| 114 | x11SocketCandidates = append(x11SocketCandidates, fallback) | 139 | x11SocketCandidates = append(x11SocketCandidates, fallback) | ||
| 115 | } | 140 | } | ||
| 116 | for _, candidate := range x11SocketCandidates { | 141 | for _, candidate := range x11SocketCandidates { | ||
| 117 | - | if _, err := os.Stat(candidate); err == nil { | 142 | + | if testPathIsSocket(candidate) { |
| 118 | return true | 143 | return true | ||
| 119 | } | 144 | } | ||
| 120 | } | 145 | } | ||
| @@ -192,8 +217,8 @@ func testXauthorityHasDisplay(data []byte, displayNum string) bool { | |||||
| 192 | if !ok { | 217 | if !ok { | ||
| 193 | return false | 218 | return false | ||
| 194 | } | 219 | } | ||
| 195 | - | if string(num) == displayNum { | 220 | + | if string(num) == displayNum && len(name) > 0 && len(cookie) > 0 { |
| 196 | - | return len(name) > 0 && len(cookie) > 0 | 221 | + | return true |
| 197 | } | 222 | } | ||
| 198 | data = rest | 223 | data = rest | ||
| 199 | } | 224 | } | ||
| @@ -212,12 +237,49 @@ func testReadXauthorityField(data []byte) ([]byte, []byte, bool) { | |||||
| 212 | return data[:fieldLen], data[fieldLen:], true | 237 | return data[:fieldLen], data[fieldLen:], true | ||
| 213 | } | 238 | } | ||
| 214 | 239 | ||||
| 215 | - | // testNeedsX11Auth reports whether GTK is expected to use the X11 backend, | 240 | + | // testNeedsX11Auth reports whether GTK may need the X11 backend and therefore |
| 216 | - | // meaning X11 authorization must be available. When a usable Wayland display | 241 | + | // requires X11 authorization before GTK initialization. Wayland-only backend |
| 217 | - | // is present and GDK_BACKEND is not forced to x11, X11 auth is not required | 242 | + | // configuration never needs X11 auth; X11 is required when it is the only usable |
| 218 | - | // because GTK will use the Wayland backend. | 243 | + | // allowed backend. |
| 219 | func testNeedsX11Auth() bool { | 244 | func testNeedsX11Auth() bool { | ||
| 220 | - | return !testHasUsableWaylandDisplay() || os.Getenv("GDK_BACKEND") == "x11" | 245 | + | if !testGDKBackendAllows("x11") { |
| 246 | + | return false | |||
| 247 | + | } | |||
| 248 | + | return !testGDKBackendAllows("wayland") || !testHasUsableWaylandDisplay() | |||
| 249 | + | } | |||
| 250 | + | ||||
| 251 | + | func testCreateUnixSocket(t *testing.T, path string) { | |||
| 252 | + | t.Helper() | |||
| 253 | + | if err := os.MkdirAll(filepath.Dir(path), 0755); err != nil { | |||
| 254 | + | t.Fatal(err) | |||
| 255 | + | } | |||
| 256 | + | listener, err := net.Listen("unix", path) | |||
| 257 | + | if err != nil { | |||
| 258 | + | t.Fatal(err) | |||
| 259 | + | } | |||
| 260 | + | t.Cleanup(func() { | |||
| 261 | + | _ = listener.Close() | |||
| 262 | + | _ = os.Remove(path) | |||
| 263 | + | }) | |||
| 264 | + | } | |||
| 265 | + | ||||
| 266 | + | func testUnusedX11DisplayNumber(t *testing.T) string { | |||
| 267 | + | t.Helper() | |||
| 268 | + | for display := 90; display < 300; display++ { | |||
| 269 | + | displayNum := strconv.Itoa(display) | |||
| 270 | + | if !testPathIsSocket("/tmp/.X11-unix/X" + displayNum) { | |||
| 271 | + | return displayNum | |||
| 272 | + | } | |||
| 273 | + | } | |||
| 274 | + | t.Fatal("could not find an unused X11 display number") | |||
| 275 | + | return "" | |||
| 276 | + | } | |||
| 277 | + | ||||
| 278 | + | func testCreateX11Socket(t *testing.T, displayNum string) { | |||
| 279 | + | t.Helper() | |||
| 280 | + | tmpDir := t.TempDir() | |||
| 281 | + | t.Setenv("TMPDIR", tmpDir) | |||
| 282 | + | testCreateUnixSocket(t, filepath.Join(tmpDir, ".X11-unix", "X"+displayNum)) | |||
| 221 | } | 283 | } | ||
| 222 | 284 | ||||
| 223 | func requireGTKDisplayApp(t *testing.T) *gtk.Application { | 285 | func requireGTKDisplayApp(t *testing.T) *gtk.Application { | ||
| @@ -227,8 +289,7 @@ func requireGTKDisplayApp(t *testing.T) *gtk.Application { | |||||
| 227 | if !hasWayland && !hasX11 { | 289 | if !hasWayland && !hasX11 { | ||
| 228 | t.Skip("GTK display not available (no DISPLAY/WAYLAND_DISPLAY socket)") | 290 | t.Skip("GTK display not available (no DISPLAY/WAYLAND_DISPLAY socket)") | ||
| 229 | } | 291 | } | ||
| 230 | - | // Only require X11 auth when the X11 backend will be used: | 292 | + | // Only require X11 auth when X11 is the only usable allowed backend. |
| 231 | - | // no Wayland (fallback to X11), or GDK_BACKEND explicitly set to x11. | |||
| 232 | if testNeedsX11Auth() && !testHasX11Auth() { | 293 | if testNeedsX11Auth() && !testHasX11Auth() { | ||
| 233 | t.Skip("GTK display not available (X11 authorization unavailable)") | 294 | t.Skip("GTK display not available (X11 authorization unavailable)") | ||
| 234 | } | 295 | } | ||
| @@ -254,16 +315,17 @@ func Test_testHasUsableGTKDisplay_PureProbe(t *testing.T) { | |||||
| 254 | }) | 315 | }) | ||
| 255 | 316 | ||||
| 256 | t.Run("DISPLAY numeric without socket", func(t *testing.T) { | 317 | t.Run("DISPLAY numeric without socket", func(t *testing.T) { | ||
| 257 | - | t.Setenv("DISPLAY", ":99") | 318 | + | displayNum := testUnusedX11DisplayNumber(t) |
| 319 | + | t.Setenv("DISPLAY", ":"+displayNum) | |||
| 258 | t.Setenv("WAYLAND_DISPLAY", "") | 320 | t.Setenv("WAYLAND_DISPLAY", "") | ||
| 259 | - | // No /tmp/.X11-unix/X99 socket exists | |||
| 260 | if got := testHasUsableGTKDisplay(); got { | 321 | if got := testHasUsableGTKDisplay(); got { | ||
| 261 | t.Errorf("testHasUsableGTKDisplay() = %v, want false", got) | 322 | t.Errorf("testHasUsableGTKDisplay() = %v, want false", got) | ||
| 262 | } | 323 | } | ||
| 263 | }) | 324 | }) | ||
| 264 | 325 | ||||
| 265 | t.Run("DISPLAY with screen without socket", func(t *testing.T) { | 326 | t.Run("DISPLAY with screen without socket", func(t *testing.T) { | ||
| 266 | - | t.Setenv("DISPLAY", ":99.0") | 327 | + | displayNum := testUnusedX11DisplayNumber(t) |
| 328 | + | t.Setenv("DISPLAY", ":"+displayNum+".0") | |||
| 267 | t.Setenv("WAYLAND_DISPLAY", "") | 329 | t.Setenv("WAYLAND_DISPLAY", "") | ||
| 268 | if got := testHasUsableGTKDisplay(); got { | 330 | if got := testHasUsableGTKDisplay(); got { | ||
| 269 | t.Errorf("testHasUsableGTKDisplay() = %v, want false", got) | 331 | t.Errorf("testHasUsableGTKDisplay() = %v, want false", got) | ||
| @@ -287,13 +349,15 @@ func Test_testHasUsableGTKDisplay_PureProbe(t *testing.T) { | |||||
| 287 | }) | 349 | }) | ||
| 288 | 350 | ||||
| 289 | t.Run("DISPLAY with existing socket", func(t *testing.T) { | 351 | t.Run("DISPLAY with existing socket", func(t *testing.T) { | ||
| 290 | - | t.Setenv("DISPLAY", ":0") | 352 | + | displayNum := testUnusedX11DisplayNumber(t) |
| 353 | + | tmpDir := t.TempDir() | |||
| 354 | + | t.Setenv("TMPDIR", tmpDir) | |||
| 355 | + | t.Setenv("DISPLAY", ":"+displayNum) | |||
| 291 | t.Setenv("WAYLAND_DISPLAY", "") | 356 | t.Setenv("WAYLAND_DISPLAY", "") | ||
| 292 | - | // /tmp/.X11-unix/X0 exists on this machine | 357 | + | testCreateUnixSocket(t, filepath.Join(tmpDir, ".X11-unix", "X"+displayNum)) |
| 293 | - | got := testHasUsableGTKDisplay() | 358 | + | if got := testHasUsableGTKDisplay(); !got { |
| 294 | - | // We can't guarantee the socket exists in all environments, | 359 | + | t.Errorf("testHasUsableGTKDisplay() = %v, want true with existing X11 socket", got) |
| 295 | - | // but this verifies it doesn't crash. | 360 | + | } |
| 296 | - | t.Logf("testHasUsableGTKDisplay() = %v (depends on /tmp/.X11-unix/X0)", got) | |||
| 297 | }) | 361 | }) | ||
| 298 | 362 | ||||
| 299 | t.Run("WAYLAND_DISPLAY with existing socket", func(t *testing.T) { | 363 | t.Run("WAYLAND_DISPLAY with existing socket", func(t *testing.T) { | ||
| @@ -301,9 +365,7 @@ func Test_testHasUsableGTKDisplay_PureProbe(t *testing.T) { | |||||
| 301 | t.Setenv("WAYLAND_DISPLAY", "wayland-1") | 365 | t.Setenv("WAYLAND_DISPLAY", "wayland-1") | ||
| 302 | tmpDir := t.TempDir() | 366 | tmpDir := t.TempDir() | ||
| 303 | socketPath := filepath.Join(tmpDir, "wayland-1") | 367 | socketPath := filepath.Join(tmpDir, "wayland-1") | ||
| 304 | - | if err := os.WriteFile(socketPath, nil, 0644); err != nil { | 368 | + | testCreateUnixSocket(t, socketPath) |
| 305 | - | t.Fatal(err) | |||
| 306 | - | } | |||
| 307 | // XDG_RUNTIME_DIR controls where Wayland sockets are looked up | 369 | // XDG_RUNTIME_DIR controls where Wayland sockets are looked up | ||
| 308 | t.Setenv("XDG_RUNTIME_DIR", tmpDir) | 370 | t.Setenv("XDG_RUNTIME_DIR", tmpDir) | ||
| 309 | if got := testHasUsableGTKDisplay(); !got { | 371 | if got := testHasUsableGTKDisplay(); !got { | ||
| @@ -323,9 +385,7 @@ func Test_testHasUsableGTKDisplay_PureProbe(t *testing.T) { | |||||
| 323 | t.Run("WAYLAND_DISPLAY absolute path with existing socket", func(t *testing.T) { | 385 | t.Run("WAYLAND_DISPLAY absolute path with existing socket", func(t *testing.T) { | ||
| 324 | t.Setenv("DISPLAY", "") | 386 | t.Setenv("DISPLAY", "") | ||
| 325 | socketPath := filepath.Join(t.TempDir(), "wayland.sock") | 387 | socketPath := filepath.Join(t.TempDir(), "wayland.sock") | ||
| 326 | - | if err := os.WriteFile(socketPath, nil, 0644); err != nil { | 388 | + | testCreateUnixSocket(t, socketPath) |
| 327 | - | t.Fatal(err) | |||
| 328 | - | } | |||
| 329 | t.Setenv("WAYLAND_DISPLAY", socketPath) | 389 | t.Setenv("WAYLAND_DISPLAY", socketPath) | ||
| 330 | t.Setenv("XDG_RUNTIME_DIR", "/nonexistent") // Should be ignored for absolute paths | 390 | t.Setenv("XDG_RUNTIME_DIR", "/nonexistent") // Should be ignored for absolute paths | ||
| 331 | if got := testHasUsableGTKDisplay(); !got { | 391 | if got := testHasUsableGTKDisplay(); !got { | ||
| @@ -518,6 +578,21 @@ func Test_testHasX11Auth_MalformedRecords(t *testing.T) { | |||||
| 518 | t.Errorf("testHasX11Auth() = %v, want true for third entry matching display :3", got) | 578 | t.Errorf("testHasX11Auth() = %v, want true for third entry matching display :3", got) | ||
| 519 | } | 579 | } | ||
| 520 | }) | 580 | }) | ||
| 581 | + | ||||
| 582 | + | t.Run("invalid matching entry does not hide later valid match", func(t *testing.T) { | |||
| 583 | + | t.Setenv("DISPLAY", ":3") | |||
| 584 | + | authFile := filepath.Join(t.TempDir(), ".Xauthority") | |||
| 585 | + | var buf bytes.Buffer | |||
| 586 | + | writeXauthRecord(&buf, "3", "", []byte{0x01, 0x02, 0x03, 0x04}) | |||
| 587 | + | writeXauthRecord(&buf, "3", "MIT-MAGIC-COOKIE-1", []byte{0x09, 0x0a, 0x0b, 0x0c}) | |||
| 588 | + | if err := os.WriteFile(authFile, buf.Bytes(), 0644); err != nil { | |||
| 589 | + | t.Fatal(err) | |||
| 590 | + | } | |||
| 591 | + | t.Setenv("XAUTHORITY", authFile) | |||
| 592 | + | if got := testHasX11Auth(); !got { | |||
| 593 | + | t.Errorf("testHasX11Auth() = %v, want true for later valid matching display", got) | |||
| 594 | + | } | |||
| 595 | + | }) | |||
| 521 | } | 596 | } | ||
| 522 | 597 | ||||
| 523 | func Test_testHasUsableWaylandDisplay_PureProbe(t *testing.T) { | 598 | func Test_testHasUsableWaylandDisplay_PureProbe(t *testing.T) { | ||
| @@ -532,9 +607,7 @@ func Test_testHasUsableWaylandDisplay_PureProbe(t *testing.T) { | |||||
| 532 | t.Setenv("WAYLAND_DISPLAY", "wayland-1") | 607 | t.Setenv("WAYLAND_DISPLAY", "wayland-1") | ||
| 533 | tmpDir := t.TempDir() | 608 | tmpDir := t.TempDir() | ||
| 534 | socketPath := filepath.Join(tmpDir, "wayland-1") | 609 | socketPath := filepath.Join(tmpDir, "wayland-1") | ||
| 535 | - | if err := os.WriteFile(socketPath, nil, 0644); err != nil { | 610 | + | testCreateUnixSocket(t, socketPath) |
| 536 | - | t.Fatal(err) | |||
| 537 | - | } | |||
| 538 | t.Setenv("XDG_RUNTIME_DIR", tmpDir) | 611 | t.Setenv("XDG_RUNTIME_DIR", tmpDir) | ||
| 539 | if got := testHasUsableWaylandDisplay(); !got { | 612 | if got := testHasUsableWaylandDisplay(); !got { | ||
| 540 | t.Errorf("testHasUsableWaylandDisplay() = %v, want true with existing wayland socket", got) | 613 | t.Errorf("testHasUsableWaylandDisplay() = %v, want true with existing wayland socket", got) | ||
| @@ -543,9 +616,7 @@ func Test_testHasUsableWaylandDisplay_PureProbe(t *testing.T) { | |||||
| 543 | 616 | ||||
| 544 | t.Run("WAYLAND_DISPLAY absolute path", func(t *testing.T) { | 617 | t.Run("WAYLAND_DISPLAY absolute path", func(t *testing.T) { | ||
| 545 | socketPath := filepath.Join(t.TempDir(), "wayland.sock") | 618 | socketPath := filepath.Join(t.TempDir(), "wayland.sock") | ||
| 546 | - | if err := os.WriteFile(socketPath, nil, 0644); err != nil { | 619 | + | testCreateUnixSocket(t, socketPath) |
| 547 | - | t.Fatal(err) | |||
| 548 | - | } | |||
| 549 | t.Setenv("WAYLAND_DISPLAY", socketPath) | 620 | t.Setenv("WAYLAND_DISPLAY", socketPath) | ||
| 550 | t.Setenv("XDG_RUNTIME_DIR", "/nonexistent") | 621 | t.Setenv("XDG_RUNTIME_DIR", "/nonexistent") | ||
| 551 | if got := testHasUsableWaylandDisplay(); !got { | 622 | if got := testHasUsableWaylandDisplay(); !got { | ||
| @@ -563,7 +634,8 @@ func Test_testHasUsableX11Display_PureProbe(t *testing.T) { | |||||
| 563 | }) | 634 | }) | ||
| 564 | 635 | ||||
| 565 | t.Run("DISPLAY numeric without socket", func(t *testing.T) { | 636 | t.Run("DISPLAY numeric without socket", func(t *testing.T) { | ||
| 566 | - | t.Setenv("DISPLAY", ":99") | 637 | + | displayNum := testUnusedX11DisplayNumber(t) |
| 638 | + | t.Setenv("DISPLAY", ":"+displayNum) | |||
| 567 | if got := testHasUsableX11Display(); got { | 639 | if got := testHasUsableX11Display(); got { | ||
| 568 | t.Errorf("testHasUsableX11Display() = %v, want false", got) | 640 | t.Errorf("testHasUsableX11Display() = %v, want false", got) | ||
| 569 | } | 641 | } | ||
| @@ -577,15 +649,54 @@ func Test_testHasUsableX11Display_PureProbe(t *testing.T) { | |||||
| 577 | }) | 649 | }) | ||
| 578 | } | 650 | } | ||
| 579 | 651 | ||||
| 652 | + | func Test_testHasUsableGTKDisplay_GDKBackendSelection(t *testing.T) { | |||
| 653 | + | t.Run("GDK_BACKEND=x11 ignores Wayland-only socket", func(t *testing.T) { | |||
| 654 | + | t.Setenv("GDK_BACKEND", "x11") | |||
| 655 | + | t.Setenv("WAYLAND_DISPLAY", "wayland-1") | |||
| 656 | + | tmpDir := t.TempDir() | |||
| 657 | + | testCreateUnixSocket(t, filepath.Join(tmpDir, "wayland-1")) | |||
| 658 | + | t.Setenv("XDG_RUNTIME_DIR", tmpDir) | |||
| 659 | + | t.Setenv("DISPLAY", ":"+testUnusedX11DisplayNumber(t)) | |||
| 660 | + | if got := testHasUsableGTKDisplay(); got { | |||
| 661 | + | t.Errorf("testHasUsableGTKDisplay() = %v, want false when x11 is forced without an X11 socket", got) | |||
| 662 | + | } | |||
| 663 | + | }) | |||
| 664 | + | ||||
| 665 | + | t.Run("GDK_BACKEND=wayland ignores X11-only socket", func(t *testing.T) { | |||
| 666 | + | displayNum := testUnusedX11DisplayNumber(t) | |||
| 667 | + | t.Setenv("GDK_BACKEND", "wayland") | |||
| 668 | + | t.Setenv("DISPLAY", ":"+displayNum) | |||
| 669 | + | t.Setenv("WAYLAND_DISPLAY", "wayland-missing") | |||
| 670 | + | t.Setenv("XDG_RUNTIME_DIR", t.TempDir()) | |||
| 671 | + | testCreateX11Socket(t, displayNum) | |||
| 672 | + | if got := testHasUsableGTKDisplay(); got { | |||
| 673 | + | t.Errorf("testHasUsableGTKDisplay() = %v, want false when wayland is forced without a Wayland socket", got) | |||
| 674 | + | } | |||
| 675 | + | }) | |||
| 676 | + | ||||
| 677 | + | t.Run("GDK_BACKEND=wayland,x11 allows X11 fallback", func(t *testing.T) { | |||
| 678 | + | displayNum := testUnusedX11DisplayNumber(t) | |||
| 679 | + | t.Setenv("GDK_BACKEND", "wayland,x11") | |||
| 680 | + | t.Setenv("DISPLAY", ":"+displayNum) | |||
| 681 | + | t.Setenv("WAYLAND_DISPLAY", "wayland-missing") | |||
| 682 | + | t.Setenv("XDG_RUNTIME_DIR", t.TempDir()) | |||
| 683 | + | testCreateX11Socket(t, displayNum) | |||
| 684 | + | if got := testHasUsableGTKDisplay(); !got { | |||
| 685 | + | t.Errorf("testHasUsableGTKDisplay() = %v, want true with X11 fallback socket", got) | |||
| 686 | + | } | |||
| 687 | + | if got := testNeedsX11Auth(); !got { | |||
| 688 | + | t.Errorf("testNeedsX11Auth() = %v, want true when X11 is the only usable backend", got) | |||
| 689 | + | } | |||
| 690 | + | }) | |||
| 691 | + | } | |||
| 692 | + | ||||
| 580 | func Test_testNeedsX11Auth_PureProbe(t *testing.T) { | 693 | func Test_testNeedsX11Auth_PureProbe(t *testing.T) { | ||
| 581 | t.Run("Wayland available forces no X11 auth", func(t *testing.T) { | 694 | t.Run("Wayland available forces no X11 auth", func(t *testing.T) { | ||
| 582 | t.Setenv("GDK_BACKEND", "") | 695 | t.Setenv("GDK_BACKEND", "") | ||
| 583 | t.Setenv("WAYLAND_DISPLAY", "wayland-1") | 696 | t.Setenv("WAYLAND_DISPLAY", "wayland-1") | ||
| 584 | tmpDir := t.TempDir() | 697 | tmpDir := t.TempDir() | ||
| 585 | socketPath := filepath.Join(tmpDir, "wayland-1") | 698 | socketPath := filepath.Join(tmpDir, "wayland-1") | ||
| 586 | - | if err := os.WriteFile(socketPath, nil, 0644); err != nil { | 699 | + | testCreateUnixSocket(t, socketPath) |
| 587 | - | t.Fatal(err) | |||
| 588 | - | } | |||
| 589 | t.Setenv("XDG_RUNTIME_DIR", tmpDir) | 700 | t.Setenv("XDG_RUNTIME_DIR", tmpDir) | ||
| 590 | t.Setenv("DISPLAY", ":0") | 701 | t.Setenv("DISPLAY", ":0") | ||
| 591 | if got := testNeedsX11Auth(); got { | 702 | if got := testNeedsX11Auth(); got { | ||
| @@ -598,9 +709,7 @@ func Test_testNeedsX11Auth_PureProbe(t *testing.T) { | |||||
| 598 | t.Setenv("WAYLAND_DISPLAY", "wayland-1") | 709 | t.Setenv("WAYLAND_DISPLAY", "wayland-1") | ||
| 599 | tmpDir := t.TempDir() | 710 | tmpDir := t.TempDir() | ||
| 600 | socketPath := filepath.Join(tmpDir, "wayland-1") | 711 | socketPath := filepath.Join(tmpDir, "wayland-1") | ||
| 601 | - | if err := os.WriteFile(socketPath, nil, 0644); err != nil { | 712 | + | testCreateUnixSocket(t, socketPath) |
| 602 | - | t.Fatal(err) | |||
| 603 | - | } | |||
| 604 | t.Setenv("XDG_RUNTIME_DIR", tmpDir) | 713 | t.Setenv("XDG_RUNTIME_DIR", tmpDir) | ||
| 605 | if got := testNeedsX11Auth(); !got { | 714 | if got := testNeedsX11Auth(); !got { | ||
| 606 | t.Errorf("testNeedsX11Auth() = %v, want true when GDK_BACKEND=x11", got) | 715 | t.Errorf("testNeedsX11Auth() = %v, want true when GDK_BACKEND=x11", got) | ||