1// Copyright 2012 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5// +build windows
6
7package svc_test
8
9import (
10	"fmt"
11	"io/ioutil"
12	"math/rand"
13	"os"
14	"os/exec"
15	"path/filepath"
16	"strings"
17	"testing"
18	"time"
19
20	"golang.org/x/sys/windows/svc"
21	"golang.org/x/sys/windows/svc/mgr"
22)
23
24func getState(t *testing.T, s *mgr.Service) svc.State {
25	status, err := s.Query()
26	if err != nil {
27		t.Fatalf("Query(%s) failed: %s", s.Name, err)
28	}
29	return status.State
30}
31
32func testState(t *testing.T, s *mgr.Service, want svc.State) {
33	have := getState(t, s)
34	if have != want {
35		t.Fatalf("%s state is=%d want=%d", s.Name, have, want)
36	}
37}
38
39func waitState(t *testing.T, s *mgr.Service, want svc.State) {
40	for i := 0; ; i++ {
41		have := getState(t, s)
42		if have == want {
43			return
44		}
45		if i > 10 {
46			t.Fatalf("%s state is=%d, waiting timeout", s.Name, have)
47		}
48		time.Sleep(300 * time.Millisecond)
49	}
50}
51
52func TestExample(t *testing.T) {
53	if testing.Short() {
54		t.Skip("skipping test in short mode - it modifies system services")
55	}
56
57	const name = "myservice"
58
59	m, err := mgr.Connect()
60	if err != nil {
61		t.Fatalf("SCM connection failed: %s", err)
62	}
63	defer m.Disconnect()
64
65	dir, err := ioutil.TempDir("", "svc")
66	if err != nil {
67		t.Fatalf("failed to create temp directory: %v", err)
68	}
69	defer os.RemoveAll(dir)
70
71	exepath := filepath.Join(dir, "a.exe")
72	o, err := exec.Command("go", "build", "-o", exepath, "golang.org/x/sys/windows/svc/example").CombinedOutput()
73	if err != nil {
74		t.Fatalf("failed to build service program: %v\n%v", err, string(o))
75	}
76
77	s, err := m.OpenService(name)
78	if err == nil {
79		err = s.Delete()
80		if err != nil {
81			s.Close()
82			t.Fatalf("Delete failed: %s", err)
83		}
84		s.Close()
85	}
86	s, err = m.CreateService(name, exepath, mgr.Config{DisplayName: "my service"}, "is", "auto-started")
87	if err != nil {
88		t.Fatalf("CreateService(%s) failed: %v", name, err)
89	}
90	defer s.Close()
91
92	args := []string{"is", "manual-started", fmt.Sprintf("%d", rand.Int())}
93
94	testState(t, s, svc.Stopped)
95	err = s.Start(args...)
96	if err != nil {
97		t.Fatalf("Start(%s) failed: %s", s.Name, err)
98	}
99	waitState(t, s, svc.Running)
100	time.Sleep(1 * time.Second)
101
102	// testing deadlock from issues 4.
103	_, err = s.Control(svc.Interrogate)
104	if err != nil {
105		t.Fatalf("Control(%s) failed: %s", s.Name, err)
106	}
107	_, err = s.Control(svc.Interrogate)
108	if err != nil {
109		t.Fatalf("Control(%s) failed: %s", s.Name, err)
110	}
111	time.Sleep(1 * time.Second)
112
113	_, err = s.Control(svc.Stop)
114	if err != nil {
115		t.Fatalf("Control(%s) failed: %s", s.Name, err)
116	}
117	waitState(t, s, svc.Stopped)
118
119	err = s.Delete()
120	if err != nil {
121		t.Fatalf("Delete failed: %s", err)
122	}
123
124	out, err := exec.Command("wevtutil.exe", "qe", "Application", "/q:*[System[Provider[@Name='myservice']]]", "/rd:true", "/c:10").CombinedOutput()
125	if err != nil {
126		t.Fatalf("wevtutil failed: %v\n%v", err, string(out))
127	}
128	want := strings.Join(append([]string{name}, args...), "-")
129	// Test context passing (see servicemain in sys_386.s and sys_amd64.s).
130	want += "-123456"
131	if !strings.Contains(string(out), want) {
132		t.Errorf("%q string does not contain %q", string(out), want)
133	}
134}
135