github.com/keybase/client/go@v0.0.0-20241007131713-f10651d043c8/launchd/launchd_test.go (about)

     1  // Copyright 2015 Keybase, Inc. All rights reserved. Use of
     2  // this source code is governed by the included BSD license.
     3  
     4  //go:build darwin
     5  // +build darwin
     6  
     7  package launchd
     8  
     9  import (
    10  	"encoding/xml"
    11  	"fmt"
    12  	"math/rand"
    13  	"os"
    14  	"testing"
    15  	"time"
    16  
    17  	"github.com/keybase/client/go/utils"
    18  )
    19  
    20  func validExecutableForTest() (string, error) {
    21  	return utils.BinPath()
    22  }
    23  
    24  func TestPlist(t *testing.T) {
    25  	binPath, err := validExecutableForTest()
    26  	if err != nil {
    27  		t.Fatal(err)
    28  	}
    29  	envVars := []EnvVar{
    30  		NewEnvVar("TESTVAR1", "1"),
    31  		NewEnvVar("TESTVAR2", "2"),
    32  	}
    33  	plist := NewPlist("keybase.testing", binPath, []string{"--flag=test", "testArg"}, envVars, "keybase.testing.log", "This is a comment")
    34  
    35  	data := plist.plistXML()
    36  	t.Logf("Plist: %s\n", data)
    37  
    38  	var i interface{}
    39  	// This tests valid XML but not actual values
    40  	err = xml.Unmarshal([]byte(data), &i)
    41  	if err != nil {
    42  		t.Errorf("Bad plist: %s", err)
    43  	}
    44  }
    45  
    46  func TestCheckPlist(t *testing.T) {
    47  	label := fmt.Sprintf("keybase.testing.checkplist.%s", randStringBytes(32))
    48  	t.Logf("Label: %s", label)
    49  	service := NewService(label)
    50  	defer os.Remove(service.plistDestination())
    51  
    52  	binPath, err := validExecutableForTest()
    53  	if err != nil {
    54  		t.Fatal(err)
    55  	}
    56  	envVars := []EnvVar{
    57  		NewEnvVar("TESTVAR1", "1"),
    58  		NewEnvVar("TESTVAR2", "2"),
    59  	}
    60  	plist := NewPlist(label, binPath, []string{}, envVars, "keybase.testing.log", "")
    61  	plistIsValid, err := service.CheckPlist(plist)
    62  	if err != nil {
    63  		t.Fatal(err)
    64  	}
    65  	if plistIsValid {
    66  		t.Fatalf("We shouldn't have a plist")
    67  	}
    68  
    69  	err = service.savePlist(plist)
    70  	if err != nil {
    71  		t.Fatal(err)
    72  	}
    73  
    74  	// Check valid plist after save
    75  	plistIsValidAfter, err := service.CheckPlist(plist)
    76  	if err != nil {
    77  		t.Fatal(err)
    78  	}
    79  	if !plistIsValidAfter {
    80  		t.Fatalf("Plist was invalid after install")
    81  	}
    82  
    83  	// Check a new plist
    84  	plistNew := NewPlist(label, binPath, []string{"differentArgs"}, envVars, "keybase.testing.log", "")
    85  	plistNewIsValid, err := service.CheckPlist(plistNew)
    86  	if err != nil {
    87  		t.Fatal(err)
    88  	}
    89  	if plistNewIsValid {
    90  		t.Fatal("New plist should be invalid")
    91  	}
    92  
    93  	err = service.savePlist(plistNew)
    94  	if err != nil {
    95  		t.Fatal(err)
    96  	}
    97  
    98  	plistNewIsValidAfterInstall, err := service.CheckPlist(plistNew)
    99  	if err != nil {
   100  		t.Fatal(err)
   101  	}
   102  	if !plistNewIsValidAfterInstall {
   103  		t.Fatalf("New plist should be valid after install")
   104  	}
   105  }
   106  
   107  func randStringBytes(n int) string {
   108  	const letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
   109  	b := make([]byte, n)
   110  	for i := range b {
   111  		b[i] = letterBytes[rand.Intn(len(letterBytes))]
   112  	}
   113  	return string(b)
   114  }
   115  
   116  func TestWaitForStatusOK(t *testing.T) {
   117  	fn := func() (*ServiceStatus, error) {
   118  		return &ServiceStatus{label: "ok", pid: "1"}, nil
   119  	}
   120  	status, err := waitForStatus(time.Second, time.Millisecond, fn)
   121  	if err != nil {
   122  		t.Fatal(err)
   123  	}
   124  	if status == nil || status.label != "ok" {
   125  		t.Fatalf("Invalid status")
   126  	}
   127  }
   128  
   129  func TestWaitForStatusDelayed(t *testing.T) {
   130  	i := 0
   131  	fn := func() (*ServiceStatus, error) {
   132  		i++
   133  		if i == 5 {
   134  			return &ServiceStatus{label: "ok_delayed", pid: "1"}, nil
   135  		}
   136  		return nil, nil
   137  	}
   138  	status, err := waitForStatus(time.Second, time.Millisecond, fn)
   139  	if err != nil {
   140  		t.Fatal(err)
   141  	}
   142  	if status == nil {
   143  		t.Fatalf("Wait timed out")
   144  	}
   145  	if status.label != "ok_delayed" {
   146  		t.Fatalf("Invalid status")
   147  	}
   148  }
   149  
   150  func TestWaitForStatusErrored(t *testing.T) {
   151  	fn := func() (*ServiceStatus, error) {
   152  		return nil, fmt.Errorf("status error")
   153  	}
   154  	_, err := waitForStatus(time.Second, time.Millisecond, fn)
   155  	if err == nil {
   156  		t.Fatal("Expected error")
   157  	}
   158  	if err.Error() != "status error" {
   159  		t.Fatal("Expected error returned from fn above")
   160  	}
   161  }
   162  
   163  func TestWaitForStatusTimeout(t *testing.T) {
   164  	fn := func() (*ServiceStatus, error) {
   165  		return nil, nil
   166  	}
   167  	status, err := waitForStatus(5*time.Millisecond, time.Millisecond, fn)
   168  	if err != nil {
   169  		t.Fatal(err)
   170  	}
   171  	if status != nil {
   172  		t.Fatalf("Status should be nil (timed out): %#v", status)
   173  	}
   174  }
   175  
   176  func TestWaitForExitOK(t *testing.T) {
   177  	fn := func() (*ServiceStatus, error) {
   178  		return nil, nil
   179  	}
   180  	err := waitForExit(time.Second, time.Millisecond, fn)
   181  	if err != nil {
   182  		t.Fatal(err)
   183  	}
   184  }
   185  
   186  func TestWaitForExitDelayed(t *testing.T) {
   187  	i := 0
   188  	fn := func() (*ServiceStatus, error) {
   189  		i++
   190  		if i < 5 {
   191  			return &ServiceStatus{label: "ok", pid: "1"}, nil
   192  		}
   193  		return nil, nil
   194  	}
   195  	err := waitForExit(time.Second, time.Millisecond, fn)
   196  	if err != nil {
   197  		t.Fatal(err)
   198  	}
   199  }
   200  
   201  func TestWaitForExitErrored(t *testing.T) {
   202  	fn := func() (*ServiceStatus, error) {
   203  		return nil, fmt.Errorf("status error")
   204  	}
   205  	err := waitForExit(time.Second, time.Millisecond, fn)
   206  	if err == nil {
   207  		t.Fatal("Expected error")
   208  	}
   209  }
   210  
   211  func TestWaitForExitTimeout(t *testing.T) {
   212  	fn := func() (*ServiceStatus, error) {
   213  		return &ServiceStatus{label: "never_exit", pid: "1"}, nil
   214  	}
   215  	err := waitForExit(5*time.Millisecond, time.Millisecond, fn)
   216  	if err == nil {
   217  		t.Fatal("Should have timed out")
   218  	}
   219  	if err.Error() != "Waiting for service exit timed out" {
   220  		t.Fatal("Should have timed out error")
   221  	}
   222  }