package bridge import ( "os" "path/filepath" "reflect" "testing" ) func TestIsChromeProfileLockError(t *testing.T) { t.Parallel() msg := "chrome failed to start: [2046:2046:0309/221021.856597:ERROR:chrome/browser/process_singleton_posix.cc:363] The profile appears to be in use by another Chromium process" if !isChromeProfileLockError(msg) { t.Fatal("expected profile lock error to be detected") } } func TestParseChromeProfileProcesses(t *testing.T) { t.Parallel() profileDir := "/data/.config/pinchtab/profiles/default" out := []byte(" 36 /usr/bin/chromium-browser --user-data-dir=/data/.config/pinchtab/profiles/default --remote-debugging-port=9222\n 99 /usr/bin/chromium-browser --user-data-dir=/tmp/other\n") got := parseChromeProfileProcesses(out, profileDir) want := []chromeProfileProcess{ { PID: "36", Command: "/usr/bin/chromium-browser --user-data-dir=/data/.config/pinchtab/profiles/default --remote-debugging-port=9222", }, } if !reflect.DeepEqual(got, want) { t.Fatalf("parseChromeProfileProcesses() = %#v, want %#v", got, want) } } func TestExtractChromeProfileLockPID(t *testing.T) { t.Parallel() msg := "The profile appears to be in use by another Chromium process (36) on another computer" pid, ok := extractChromeProfileLockPID(msg) if !ok { t.Fatal("expected pid to be parsed from profile lock error") } if pid != 36 { t.Fatalf("extractChromeProfileLockPID() = %d, want 36", pid) } } func TestClearStaleChromeProfileLockRemovesSingletonFiles(t *testing.T) { profileDir := t.TempDir() for _, name := range chromeSingletonFiles { if err := os.WriteFile(filepath.Join(profileDir, name), []byte("x"), 0644); err != nil { t.Fatalf("write %s: %v", name, err) } } orig := chromeProfileProcessLister origPID := chromePIDIsRunning origMock := isProfileOwnedByRunningPinchtabMock chromeProfileProcessLister = func(string) ([]chromeProfileProcess, error) { return nil, nil } chromePIDIsRunning = func(int) (bool, error) { return false, nil } isProfileOwnedByRunningPinchtabMock = func(string) (bool, int) { return false, 0 } t.Cleanup(func() { chromeProfileProcessLister = orig chromePIDIsRunning = origPID isProfileOwnedByRunningPinchtabMock = origMock }) removed, err := clearStaleChromeProfileLock(profileDir, "") if err != nil { t.Fatalf("clearStaleChromeProfileLock() error = %v", err) } if !removed { t.Fatal("expected singleton files to be removed") } for _, name := range chromeSingletonFiles { if _, err := os.Lstat(filepath.Join(profileDir, name)); !os.IsNotExist(err) { t.Fatalf("expected %s to be removed, got err=%v", name, err) } } } func TestClearStaleChromeProfileLockLeavesActiveProfileUntouched(t *testing.T) { profileDir := t.TempDir() lockPath := filepath.Join(profileDir, chromeSingletonFiles[0]) if err := os.WriteFile(lockPath, []byte("x"), 0644); err != nil { t.Fatalf("write lock file: %v", err) } orig := chromeProfileProcessLister origPID := chromePIDIsRunning origMock := isProfileOwnedByRunningPinchtabMock chromeProfileProcessLister = func(string) ([]chromeProfileProcess, error) { return []chromeProfileProcess{{PID: "36", Command: "/usr/bin/chromium-browser --user-data-dir=" + profileDir}}, nil } chromePIDIsRunning = func(int) (bool, error) { return false, nil } isProfileOwnedByRunningPinchtabMock = func(string) (bool, int) { return true, 1234 } t.Cleanup(func() { chromeProfileProcessLister = orig chromePIDIsRunning = origPID isProfileOwnedByRunningPinchtabMock = origMock }) removed, err := clearStaleChromeProfileLock(profileDir, "") if err != nil { t.Fatalf("clearStaleChromeProfileLock() error = %v", err) } if removed { t.Fatal("expected active profile lock to remain in place") } if _, err := os.Lstat(lockPath); err != nil { t.Fatalf("expected lock file to remain, got err=%v", err) } } func TestClearStaleChromeProfileLockFallsBackToPIDProbe(t *testing.T) { profileDir := t.TempDir() lockPath := filepath.Join(profileDir, chromeSingletonFiles[0]) if err := os.WriteFile(lockPath, []byte("x"), 0644); err != nil { t.Fatalf("write lock file: %v", err) } orig := chromeProfileProcessLister origPID := chromePIDIsRunning origMock := isProfileOwnedByRunningPinchtabMock chromeProfileProcessLister = func(string) ([]chromeProfileProcess, error) { return nil, os.ErrPermission } chromePIDIsRunning = func(pid int) (bool, error) { if pid != 36 { t.Fatalf("unexpected pid probe: got %d, want 36", pid) } return false, nil } isProfileOwnedByRunningPinchtabMock = func(string) (bool, int) { return false, 0 } t.Cleanup(func() { chromeProfileProcessLister = orig chromePIDIsRunning = origPID isProfileOwnedByRunningPinchtabMock = origMock }) removed, err := clearStaleChromeProfileLock(profileDir, "another Chromium process (36)") if err != nil { t.Fatalf("clearStaleChromeProfileLock() error = %v", err) } if !removed { t.Fatal("expected singleton file to be removed after stale pid probe") } if _, err := os.Lstat(lockPath); !os.IsNotExist(err) { t.Fatalf("expected lock file to be removed, got err=%v", err) } }