1// Copyright 2018 The Prometheus Authors
2// Licensed under the Apache License, Version 2.0 (the "License");
3// you may not use this file except in compliance with the License.
4// You may obtain a copy of the License at
5//
6// http://www.apache.org/licenses/LICENSE-2.0
7//
8// Unless required by applicable law or agreed to in writing, software
9// distributed under the License is distributed on an "AS IS" BASIS,
10// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11// See the License for the specific language governing permissions and
12// limitations under the License.
13
14// +build linux
15
16package prometheus
17
18import (
19	"bytes"
20	"errors"
21	"os"
22	"regexp"
23	"testing"
24
25	"github.com/prometheus/common/expfmt"
26	"github.com/prometheus/procfs"
27
28	dto "github.com/prometheus/client_model/go"
29)
30
31func TestProcessCollector(t *testing.T) {
32	if _, err := procfs.Self(); err != nil {
33		t.Skipf("skipping TestProcessCollector, procfs not available: %s", err)
34	}
35
36	registry := NewRegistry()
37	if err := registry.Register(NewProcessCollector(ProcessCollectorOpts{})); err != nil {
38		t.Fatal(err)
39	}
40	if err := registry.Register(NewProcessCollector(ProcessCollectorOpts{
41		PidFn:        func() (int, error) { return os.Getpid(), nil },
42		Namespace:    "foobar",
43		ReportErrors: true, // No errors expected, just to see if none are reported.
44	})); err != nil {
45		t.Fatal(err)
46	}
47
48	mfs, err := registry.Gather()
49	if err != nil {
50		t.Fatal(err)
51	}
52
53	var buf bytes.Buffer
54	for _, mf := range mfs {
55		if _, err := expfmt.MetricFamilyToText(&buf, mf); err != nil {
56			t.Fatal(err)
57		}
58	}
59
60	for _, re := range []*regexp.Regexp{
61		regexp.MustCompile("\nprocess_cpu_seconds_total [0-9]"),
62		regexp.MustCompile("\nprocess_max_fds [1-9]"),
63		regexp.MustCompile("\nprocess_open_fds [1-9]"),
64		regexp.MustCompile("\nprocess_virtual_memory_max_bytes (-1|[1-9])"),
65		regexp.MustCompile("\nprocess_virtual_memory_bytes [1-9]"),
66		regexp.MustCompile("\nprocess_resident_memory_bytes [1-9]"),
67		regexp.MustCompile("\nprocess_start_time_seconds [0-9.]{10,}"),
68		regexp.MustCompile("\nfoobar_process_cpu_seconds_total [0-9]"),
69		regexp.MustCompile("\nfoobar_process_max_fds [1-9]"),
70		regexp.MustCompile("\nfoobar_process_open_fds [1-9]"),
71		regexp.MustCompile("\nfoobar_process_virtual_memory_max_bytes (-1|[1-9])"),
72		regexp.MustCompile("\nfoobar_process_virtual_memory_bytes [1-9]"),
73		regexp.MustCompile("\nfoobar_process_resident_memory_bytes [1-9]"),
74		regexp.MustCompile("\nfoobar_process_start_time_seconds [0-9.]{10,}"),
75	} {
76		if !re.Match(buf.Bytes()) {
77			t.Errorf("want body to match %s\n%s", re, buf.String())
78		}
79	}
80
81	brokenProcessCollector := NewProcessCollector(ProcessCollectorOpts{
82		PidFn:        func() (int, error) { return 0, errors.New("boo") },
83		ReportErrors: true,
84	})
85
86	ch := make(chan Metric)
87	go func() {
88		brokenProcessCollector.Collect(ch)
89		close(ch)
90	}()
91	n := 0
92	for m := range ch {
93		n++
94		pb := &dto.Metric{}
95		err := m.Write(pb)
96		if err == nil {
97			t.Error("metric collected from broken process collector is unexpectedly valid")
98		}
99	}
100	if n != 1 {
101		t.Errorf("%d metrics collected, want 1", n)
102	}
103}
104