1package cpu
2
3import (
4	"fmt"
5	"os"
6	"runtime"
7	"testing"
8	"time"
9
10	"github.com/shirou/gopsutil/internal/common"
11	"github.com/stretchr/testify/assert"
12)
13
14func skipIfNotImplementedErr(t *testing.T, err error) {
15	if err == common.ErrNotImplementedError {
16		t.Skip("not implemented")
17	}
18}
19
20func TestCpu_times(t *testing.T) {
21	v, err := Times(false)
22	skipIfNotImplementedErr(t, err)
23	if err != nil {
24		t.Errorf("error %v", err)
25	}
26	if len(v) == 0 {
27		t.Error("could not get CPUs ", err)
28	}
29	empty := TimesStat{}
30	for _, vv := range v {
31		if vv == empty {
32			t.Errorf("could not get CPU User: %v", vv)
33		}
34	}
35
36	// test sum of per cpu stats is within margin of error for cpu total stats
37	cpuTotal, err := Times(false)
38	skipIfNotImplementedErr(t, err)
39	if err != nil {
40		t.Errorf("error %v", err)
41	}
42	if len(cpuTotal) == 0 {
43		t.Error("could not get CPUs", err)
44	}
45	perCPU, err := Times(true)
46	skipIfNotImplementedErr(t, err)
47	if err != nil {
48		t.Errorf("error %v", err)
49	}
50	if len(perCPU) == 0 {
51		t.Error("could not get CPUs", err)
52	}
53	var perCPUUserTimeSum float64
54	var perCPUSystemTimeSum float64
55	var perCPUIdleTimeSum float64
56	for _, pc := range perCPU {
57		perCPUUserTimeSum += pc.User
58		perCPUSystemTimeSum += pc.System
59		perCPUIdleTimeSum += pc.Idle
60	}
61	margin := 2.0
62	t.Log(cpuTotal[0])
63
64	if cpuTotal[0].User == 0 && cpuTotal[0].System == 0 && cpuTotal[0].Idle == 0 {
65		t.Error("could not get cpu values")
66	}
67	if cpuTotal[0].User != 0 {
68		assert.InEpsilon(t, cpuTotal[0].User, perCPUUserTimeSum, margin)
69	}
70	if cpuTotal[0].System != 0 {
71		assert.InEpsilon(t, cpuTotal[0].System, perCPUSystemTimeSum, margin)
72	}
73	if cpuTotal[0].Idle != 0 {
74		assert.InEpsilon(t, cpuTotal[0].Idle, perCPUIdleTimeSum, margin)
75	}
76
77}
78
79func TestCpu_counts(t *testing.T) {
80	v, err := Counts(true)
81	skipIfNotImplementedErr(t, err)
82	if err != nil {
83		t.Errorf("error %v", err)
84	}
85	if v == 0 {
86		t.Errorf("could not get logical CPU counts: %v", v)
87	}
88	t.Logf("logical cores: %d", v)
89	v, err = Counts(false)
90	skipIfNotImplementedErr(t, err)
91	if err != nil {
92		t.Errorf("error %v", err)
93	}
94	if v == 0 {
95		t.Errorf("could not get physical CPU counts: %v", v)
96	}
97	t.Logf("physical cores: %d", v)
98}
99
100func TestCPUTimeStat_String(t *testing.T) {
101	v := TimesStat{
102		CPU:    "cpu0",
103		User:   100.1,
104		System: 200.1,
105		Idle:   300.1,
106	}
107	e := `{"cpu":"cpu0","user":100.1,"system":200.1,"idle":300.1,"nice":0.0,"iowait":0.0,"irq":0.0,"softirq":0.0,"steal":0.0,"guest":0.0,"guestNice":0.0}`
108	if e != fmt.Sprintf("%v", v) {
109		t.Errorf("CPUTimesStat string is invalid: %v", v)
110	}
111}
112
113func TestCpuInfo(t *testing.T) {
114	v, err := Info()
115	skipIfNotImplementedErr(t, err)
116	if err != nil {
117		t.Errorf("error %v", err)
118	}
119	if len(v) == 0 {
120		t.Errorf("could not get CPU Info")
121	}
122	for _, vv := range v {
123		if vv.ModelName == "" {
124			t.Errorf("could not get CPU Info: %v", vv)
125		}
126	}
127}
128
129func testCPUPercent(t *testing.T, percpu bool) {
130	numcpu := runtime.NumCPU()
131	testCount := 3
132
133	if runtime.GOOS != "windows" {
134		testCount = 100
135		v, err := Percent(time.Millisecond, percpu)
136		skipIfNotImplementedErr(t, err)
137		if err != nil {
138			t.Errorf("error %v", err)
139		}
140		// Skip CircleCI which CPU num is different
141		if os.Getenv("CIRCLECI") != "true" {
142			if (percpu && len(v) != numcpu) || (!percpu && len(v) != 1) {
143				t.Fatalf("wrong number of entries from CPUPercent: %v", v)
144			}
145		}
146	}
147	for i := 0; i < testCount; i++ {
148		duration := time.Duration(10) * time.Microsecond
149		v, err := Percent(duration, percpu)
150		skipIfNotImplementedErr(t, err)
151		if err != nil {
152			t.Errorf("error %v", err)
153		}
154		for _, percent := range v {
155			// Check for slightly greater then 100% to account for any rounding issues.
156			if percent < 0.0 || percent > 100.0001*float64(numcpu) {
157				t.Fatalf("CPUPercent value is invalid: %f", percent)
158			}
159		}
160	}
161}
162
163func testCPUPercentLastUsed(t *testing.T, percpu bool) {
164
165	numcpu := runtime.NumCPU()
166	testCount := 10
167
168	if runtime.GOOS != "windows" {
169		testCount = 2
170		v, err := Percent(time.Millisecond, percpu)
171		skipIfNotImplementedErr(t, err)
172		if err != nil {
173			t.Errorf("error %v", err)
174		}
175		// Skip CircleCI which CPU num is different
176		if os.Getenv("CIRCLECI") != "true" {
177			if (percpu && len(v) != numcpu) || (!percpu && len(v) != 1) {
178				t.Fatalf("wrong number of entries from CPUPercent: %v", v)
179			}
180		}
181	}
182	for i := 0; i < testCount; i++ {
183		v, err := Percent(0, percpu)
184		skipIfNotImplementedErr(t, err)
185		if err != nil {
186			t.Errorf("error %v", err)
187		}
188		time.Sleep(1 * time.Millisecond)
189		for _, percent := range v {
190			// Check for slightly greater then 100% to account for any rounding issues.
191			if percent < 0.0 || percent > 100.0001*float64(numcpu) {
192				t.Fatalf("CPUPercent value is invalid: %f", percent)
193			}
194		}
195	}
196
197}
198
199func TestCPUPercent(t *testing.T) {
200	testCPUPercent(t, false)
201}
202
203func TestCPUPercentPerCpu(t *testing.T) {
204	testCPUPercent(t, true)
205}
206
207func TestCPUPercentIntervalZero(t *testing.T) {
208	testCPUPercentLastUsed(t, false)
209}
210
211func TestCPUPercentIntervalZeroPerCPU(t *testing.T) {
212	testCPUPercentLastUsed(t, true)
213}
214