1// Copyright 2016-2019 The NATS 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
14package bench
15
16import (
17	"fmt"
18	"strings"
19	"testing"
20	"time"
21
22	"github.com/nats-io/nats.go"
23)
24
25const (
26	MsgSize = 8
27	Million = 1000 * 1000
28)
29
30var baseTime = time.Now()
31
32func millionMessagesSecondSample(seconds int) *Sample {
33	messages := Million * seconds
34	start := baseTime
35	end := start.Add(time.Second * time.Duration(seconds))
36	nc := new(nats.Conn)
37
38	s := NewSample(messages, MsgSize, start, end, nc)
39	s.MsgCnt = uint64(messages)
40	s.MsgBytes = uint64(messages * MsgSize)
41	s.IOBytes = s.MsgBytes
42	return s
43}
44
45func TestDuration(t *testing.T) {
46	s := millionMessagesSecondSample(1)
47	duration := s.End.Sub(s.Start)
48	if duration != s.Duration() || duration != time.Second {
49		t.Fatal("Expected sample duration to be 1 second")
50	}
51}
52
53func TestSeconds(t *testing.T) {
54	s := millionMessagesSecondSample(1)
55	seconds := s.End.Sub(s.Start).Seconds()
56	if seconds != s.Seconds() || seconds != 1.0 {
57		t.Fatal("Expected sample seconds to be 1 second")
58	}
59}
60
61func TestRate(t *testing.T) {
62	s := millionMessagesSecondSample(60)
63	if s.Rate() != Million {
64		t.Fatal("Expected rate at 1 million msgs")
65	}
66}
67
68func TestThoughput(t *testing.T) {
69	s := millionMessagesSecondSample(60)
70	if s.Throughput() != Million*MsgSize {
71		t.Fatalf("Expected throughput at %d million bytes/sec", MsgSize)
72	}
73}
74
75func TestStrings(t *testing.T) {
76	s := millionMessagesSecondSample(60)
77	if len(s.String()) == 0 {
78		t.Fatal("Sample didn't provide a String")
79	}
80}
81
82func TestGroupDuration(t *testing.T) {
83	sg := NewSampleGroup()
84	sg.AddSample(millionMessagesSecondSample(1))
85	sg.AddSample(millionMessagesSecondSample(2))
86	duration := sg.End.Sub(sg.Start)
87	if duration != sg.Duration() || duration != time.Duration(2)*time.Second {
88		t.Fatal("Expected aggregate duration to be 2.0 seconds")
89	}
90}
91
92func TestGroupSeconds(t *testing.T) {
93	sg := NewSampleGroup()
94	sg.AddSample(millionMessagesSecondSample(1))
95	sg.AddSample(millionMessagesSecondSample(2))
96	sg.AddSample(millionMessagesSecondSample(3))
97	seconds := sg.End.Sub(sg.Start).Seconds()
98	if seconds != sg.Seconds() || seconds != 3.0 {
99		t.Fatal("Expected aggregate seconds to be 3.0 seconds")
100	}
101}
102
103func TestGroupRate(t *testing.T) {
104	sg := NewSampleGroup()
105	sg.AddSample(millionMessagesSecondSample(1))
106	sg.AddSample(millionMessagesSecondSample(2))
107	sg.AddSample(millionMessagesSecondSample(3))
108	if sg.Rate() != Million*2 {
109		t.Fatal("Expected MsgRate at 2 million msg/sec")
110	}
111}
112
113func TestGroupThoughput(t *testing.T) {
114	sg := NewSampleGroup()
115	sg.AddSample(millionMessagesSecondSample(1))
116	sg.AddSample(millionMessagesSecondSample(2))
117	sg.AddSample(millionMessagesSecondSample(3))
118	if sg.Throughput() != 2*Million*MsgSize {
119		t.Fatalf("Expected througput at %d million bytes/sec", 2*MsgSize)
120	}
121}
122
123func TestMinMaxRate(t *testing.T) {
124	sg := NewSampleGroup()
125	sg.AddSample(millionMessagesSecondSample(1))
126	sg.AddSample(millionMessagesSecondSample(2))
127	sg.AddSample(millionMessagesSecondSample(3))
128	if sg.MinRate() != sg.MaxRate() {
129		t.Fatal("Expected MinRate == MaxRate")
130	}
131}
132
133func TestAvgRate(t *testing.T) {
134	sg := NewSampleGroup()
135	sg.AddSample(millionMessagesSecondSample(1))
136	sg.AddSample(millionMessagesSecondSample(2))
137	sg.AddSample(millionMessagesSecondSample(3))
138	if sg.MinRate() != sg.AvgRate() {
139		t.Fatal("Expected MinRate == AvgRate")
140	}
141}
142
143func TestStdDev(t *testing.T) {
144	sg := NewSampleGroup()
145	sg.AddSample(millionMessagesSecondSample(1))
146	sg.AddSample(millionMessagesSecondSample(2))
147	sg.AddSample(millionMessagesSecondSample(3))
148	if sg.StdDev() != 0.0 {
149		t.Fatal("Expected stddev to be zero")
150	}
151}
152
153func TestBenchSetup(t *testing.T) {
154	bench := NewBenchmark("test", 1, 1)
155	bench.AddSubSample(millionMessagesSecondSample(1))
156	bench.AddPubSample(millionMessagesSecondSample(1))
157	bench.Close()
158	if len(bench.RunID) == 0 {
159		t.Fatal("Bench doesn't have a RunID")
160	}
161	if len(bench.Pubs.Samples) != 1 {
162		t.Fatal("Expected one publisher")
163	}
164	if len(bench.Subs.Samples) != 1 {
165		t.Fatal("Expected one subscriber")
166	}
167	if bench.MsgCnt != 2*Million {
168		t.Fatal("Expected 2 million msgs")
169	}
170	if bench.IOBytes != 2*Million*MsgSize {
171		t.Fatalf("Expected %d million bytes", 2*MsgSize)
172	}
173	if bench.Duration() != time.Second {
174		t.Fatal("Expected duration to be 1 second")
175	}
176}
177
178func makeBench(subs, pubs int) *Benchmark {
179	bench := NewBenchmark("test", subs, pubs)
180	for i := 0; i < subs; i++ {
181		bench.AddSubSample(millionMessagesSecondSample(1))
182	}
183	for i := 0; i < pubs; i++ {
184		bench.AddPubSample(millionMessagesSecondSample(1))
185	}
186	bench.Close()
187	return bench
188}
189
190func TestCsv(t *testing.T) {
191	bench := makeBench(1, 1)
192	csv := bench.CSV()
193	lines := strings.Split(csv, "\n")
194	if len(lines) != 4 {
195		t.Fatal("Expected 4 lines of output from the CSV string")
196	}
197
198	fields := strings.Split(lines[1], ",")
199	if len(fields) != 7 {
200		t.Fatal("Expected 7 fields")
201	}
202}
203
204func TestBenchStrings(t *testing.T) {
205	bench := makeBench(1, 1)
206	s := bench.Report()
207	lines := strings.Split(s, "\n")
208	if len(lines) != 4 {
209		t.Fatal("Expected 3 lines of output: header, pub, sub, empty")
210	}
211
212	bench = makeBench(2, 2)
213	s = bench.Report()
214	lines = strings.Split(s, "\n")
215	if len(lines) != 10 {
216		fmt.Printf("%q\n", s)
217
218		t.Fatal("Expected 11 lines of output: header, pub header, pub x 2, stats, sub headers, sub x 2, stats, empty")
219	}
220}
221
222func TestMsgsPerClient(t *testing.T) {
223	zero := MsgsPerClient(0, 0)
224	if len(zero) != 0 {
225		t.Fatal("Expected 0 length for 0 clients")
226	}
227	onetwo := MsgsPerClient(1, 2)
228	if len(onetwo) != 2 || onetwo[0] != 1 || onetwo[1] != 0 {
229		t.Fatal("Expected uneven distribution")
230	}
231	twotwo := MsgsPerClient(2, 2)
232	if len(twotwo) != 2 || twotwo[0] != 1 || twotwo[1] != 1 {
233		t.Fatal("Expected even distribution")
234	}
235	threetwo := MsgsPerClient(3, 2)
236	if len(threetwo) != 2 || threetwo[0] != 2 || threetwo[1] != 1 {
237		t.Fatal("Expected uneven distribution")
238	}
239}
240