1package protocol_test
2
3import (
4	"bytes"
5	"errors"
6	"fmt"
7	"io"
8	"testing"
9
10	protocol "github.com/influxdata/line-protocol"
11)
12
13type TestingHandler struct {
14	results []Result
15}
16
17func (h *TestingHandler) SetMeasurement(name []byte) error {
18	n := make([]byte, len(name))
19	copy(n, name)
20
21	mname := Result{
22		Name:  Measurement,
23		Value: n,
24	}
25	h.results = append(h.results, mname)
26	return nil
27}
28
29func (h *TestingHandler) AddTag(key []byte, value []byte) error {
30	k := make([]byte, len(key))
31	copy(k, key)
32	v := make([]byte, len(value))
33	copy(v, value)
34
35	tagkey := Result{
36		Name:  TagKey,
37		Value: k,
38	}
39	tagvalue := Result{
40		Name:  TagValue,
41		Value: v,
42	}
43	h.results = append(h.results, tagkey, tagvalue)
44	return nil
45}
46
47func (h *TestingHandler) AddInt(key []byte, value []byte) error {
48	k := make([]byte, len(key))
49	copy(k, key)
50	v := make([]byte, len(value))
51	copy(v, value)
52
53	fieldkey := Result{
54		Name:  FieldKey,
55		Value: k,
56	}
57	fieldvalue := Result{
58		Name:  FieldInt,
59		Value: v,
60	}
61	h.results = append(h.results, fieldkey, fieldvalue)
62	return nil
63}
64
65func (h *TestingHandler) AddUint(key []byte, value []byte) error {
66	k := make([]byte, len(key))
67	copy(k, key)
68	v := make([]byte, len(value))
69	copy(v, value)
70
71	fieldkey := Result{
72		Name:  FieldKey,
73		Value: key,
74	}
75	fieldvalue := Result{
76		Name:  FieldUint,
77		Value: value,
78	}
79	h.results = append(h.results, fieldkey, fieldvalue)
80	return nil
81}
82
83func (h *TestingHandler) AddFloat(key []byte, value []byte) error {
84	k := make([]byte, len(key))
85	copy(k, key)
86	v := make([]byte, len(value))
87	copy(v, value)
88
89	fieldkey := Result{
90		Name:  FieldKey,
91		Value: k,
92	}
93	fieldvalue := Result{
94		Name:  FieldFloat,
95		Value: v,
96	}
97	h.results = append(h.results, fieldkey, fieldvalue)
98	return nil
99}
100
101func (h *TestingHandler) AddString(key []byte, value []byte) error {
102	k := make([]byte, len(key))
103	copy(k, key)
104	v := make([]byte, len(value))
105	copy(v, value)
106
107	fieldkey := Result{
108		Name:  FieldKey,
109		Value: k,
110	}
111	fieldvalue := Result{
112		Name:  FieldString,
113		Value: v,
114	}
115	h.results = append(h.results, fieldkey, fieldvalue)
116	return nil
117}
118
119func (h *TestingHandler) AddBool(key []byte, value []byte) error {
120	k := make([]byte, len(key))
121	copy(k, key)
122	v := make([]byte, len(value))
123	copy(v, value)
124
125	fieldkey := Result{
126		Name:  FieldKey,
127		Value: k,
128	}
129	fieldvalue := Result{
130		Name:  FieldBool,
131		Value: v,
132	}
133	h.results = append(h.results, fieldkey, fieldvalue)
134	return nil
135}
136
137func (h *TestingHandler) SetTimestamp(tm []byte) error {
138	t := make([]byte, len(tm))
139	copy(t, tm)
140
141	timestamp := Result{
142		Name:  Timestamp,
143		Value: t,
144	}
145	h.results = append(h.results, timestamp)
146	return nil
147}
148
149func (h *TestingHandler) Result(err error) {
150	var res Result
151	if err == nil {
152		res = Result{
153			Name: Success,
154		}
155	} else {
156		res = Result{
157			Name: Error,
158			err:  err,
159		}
160	}
161	h.results = append(h.results, res)
162}
163
164func (h *TestingHandler) Results() []Result {
165	return h.results
166}
167
168type BenchmarkingHandler struct {
169}
170
171func (h *BenchmarkingHandler) SetMeasurement(name []byte) error {
172	return nil
173}
174
175func (h *BenchmarkingHandler) AddTag(key []byte, value []byte) error {
176	return nil
177}
178
179func (h *BenchmarkingHandler) AddInt(key []byte, value []byte) error {
180	return nil
181}
182
183func (h *BenchmarkingHandler) AddUint(key []byte, value []byte) error {
184	return nil
185}
186
187func (h *BenchmarkingHandler) AddFloat(key []byte, value []byte) error {
188	return nil
189}
190
191func (h *BenchmarkingHandler) AddString(key []byte, value []byte) error {
192	return nil
193}
194
195func (h *BenchmarkingHandler) AddBool(key []byte, value []byte) error {
196	return nil
197}
198
199func (h *BenchmarkingHandler) SetTimestamp(tm []byte) error {
200	return nil
201}
202
203type TokenType int
204
205const (
206	NoMatch TokenType = iota
207	Measurement
208	TagKey
209	TagValue
210	FieldKey
211	FieldString
212	FieldInt
213	FieldUint
214	FieldFloat
215	FieldBool
216	Timestamp
217	EOL
218	EOF
219	Punc
220	WhiteSpace
221	Success
222	Error
223)
224
225func (t TokenType) String() string {
226	switch t {
227	case NoMatch:
228		return "NoMatch"
229	case Measurement:
230		return "Measurement"
231	case TagKey:
232		return "TagKey"
233	case TagValue:
234		return "TagValue"
235	case FieldKey:
236		return "FieldKey"
237	case FieldInt:
238		return "FieldInt"
239	case FieldUint:
240		return "FieldUint"
241	case FieldFloat:
242		return "FieldFloat"
243	case FieldString:
244		return "FieldString"
245	case FieldBool:
246		return "FieldBool"
247	case Timestamp:
248		return "Timestamp"
249	case EOL:
250		return "EOL"
251	case EOF:
252		return "EOF"
253	case Punc:
254		return "Punc"
255	case WhiteSpace:
256		return "WhiteSpace"
257	case Success:
258		return "Success"
259	case Error:
260		return "Error"
261	default:
262		panic("Unknown TokenType")
263	}
264}
265
266type Result struct {
267	Name  TokenType
268	Value []byte
269	err   error
270}
271
272func (r Result) String() string {
273	return fmt.Sprintf("(%s, %q, %v)", r.Name, r.Value, r.err)
274}
275
276var tests = []struct {
277	name    string
278	input   []byte
279	results []Result
280	err     error
281}{
282	{
283		name:    "empty string",
284		input:   []byte(""),
285		results: nil,
286	},
287	{
288		name:  "minimal",
289		input: []byte("cpu value=42"),
290		results: []Result{
291			{
292				Name:  Measurement,
293				Value: []byte("cpu"),
294			},
295			{
296				Name:  FieldKey,
297				Value: []byte("value"),
298			},
299			{
300				Name:  FieldFloat,
301				Value: []byte("42"),
302			},
303			{
304				Name: Success,
305			},
306		},
307	},
308	{
309		name:  "newline",
310		input: []byte("cpu value=42\n"),
311		results: []Result{
312			{
313				Name:  Measurement,
314				Value: []byte("cpu"),
315			},
316			{
317				Name:  FieldKey,
318				Value: []byte("value"),
319			},
320			{
321				Name:  FieldFloat,
322				Value: []byte("42"),
323			},
324			{
325				Name: Success,
326			},
327		},
328	},
329	{
330		name:  "minimal with timestamp",
331		input: []byte("cpu value=42 1516241192000000000"),
332		results: []Result{
333			{
334				Name:  Measurement,
335				Value: []byte("cpu"),
336			},
337			{
338				Name:  FieldKey,
339				Value: []byte("value"),
340			},
341			{
342				Name:  FieldFloat,
343				Value: []byte("42"),
344			},
345			{
346				Name:  Timestamp,
347				Value: []byte("1516241192000000000"),
348			},
349			{
350				Name: Success,
351			},
352		},
353	},
354	{
355		name:  "measurement escape non-special",
356		input: []byte(`c\pu value=42`),
357		results: []Result{
358			{
359				Name:  Measurement,
360				Value: []byte(`c\pu`),
361			},
362			{
363				Name:  FieldKey,
364				Value: []byte("value"),
365			},
366			{
367				Name:  FieldFloat,
368				Value: []byte("42"),
369			},
370			{
371				Name: Success,
372			},
373		},
374	},
375	{
376		name:  "measurement escaped trailing backslash",
377		input: []byte(`cpu\\ value=42`),
378		results: []Result{
379			{
380				Name:  Measurement,
381				Value: []byte(`cpu\\`),
382			},
383			{
384				Name:  FieldKey,
385				Value: []byte("value"),
386			},
387			{
388				Name:  FieldFloat,
389				Value: []byte("42"),
390			},
391			{
392				Name: Success,
393			},
394		},
395	},
396	{
397		name:  "single char measurement",
398		input: []byte("c value=42"),
399		results: []Result{
400			{
401				Name:  Measurement,
402				Value: []byte("c"),
403			},
404			{
405				Name:  FieldKey,
406				Value: []byte("value"),
407			},
408			{
409				Name:  FieldFloat,
410				Value: []byte("42"),
411			},
412			{
413				Name: Success,
414			},
415		},
416	},
417	{
418		name:  "escape backslash in measurement",
419		input: []byte(`cp\\u value=42`),
420		results: []Result{
421			{
422				Name:  Measurement,
423				Value: []byte(`cp\\u`),
424			},
425			{
426				Name:  FieldKey,
427				Value: []byte("value"),
428			},
429			{
430				Name:  FieldFloat,
431				Value: []byte("42"),
432			},
433			{
434				Name: Success,
435			},
436		},
437	},
438	{
439		name:  "measurement escape space",
440		input: []byte(`cpu\ abc value=42`),
441		results: []Result{
442			{
443				Name:  Measurement,
444				Value: []byte(`cpu\ abc`),
445			},
446			{
447				Name:  FieldKey,
448				Value: []byte("value"),
449			},
450			{
451				Name:  FieldFloat,
452				Value: []byte("42"),
453			},
454			{
455				Name: Success,
456			},
457		},
458	},
459	{
460		name:  "scientific float",
461		input: []byte("cpu value=42e0"),
462		results: []Result{
463			{
464				Name:  Measurement,
465				Value: []byte("cpu"),
466			},
467			{
468				Name:  FieldKey,
469				Value: []byte("value"),
470			},
471			{
472				Name:  FieldFloat,
473				Value: []byte("42e0"),
474			},
475			{
476				Name: Success,
477			},
478		},
479	},
480	{
481		name:  "scientific float negative mantissa",
482		input: []byte("cpu value=-42e0"),
483		results: []Result{
484			{
485				Name:  Measurement,
486				Value: []byte("cpu"),
487			},
488			{
489				Name:  FieldKey,
490				Value: []byte("value"),
491			},
492			{
493				Name:  FieldFloat,
494				Value: []byte("-42e0"),
495			},
496			{
497				Name: Success,
498			},
499		},
500	},
501	{
502		name:  "scientific float negative exponent",
503		input: []byte("cpu value=42e-1"),
504		results: []Result{
505			{
506				Name:  Measurement,
507				Value: []byte("cpu"),
508			},
509			{
510				Name:  FieldKey,
511				Value: []byte("value"),
512			},
513			{
514				Name:  FieldFloat,
515				Value: []byte("42e-1"),
516			},
517			{
518				Name: Success,
519			},
520		},
521	},
522	{
523		name:  "scientific float big e",
524		input: []byte("cpu value=42E0"),
525		results: []Result{
526			{
527				Name:  Measurement,
528				Value: []byte("cpu"),
529			},
530			{
531				Name:  FieldKey,
532				Value: []byte("value"),
533			},
534			{
535				Name:  FieldFloat,
536				Value: []byte("42E0"),
537			},
538			{
539				Name: Success,
540			},
541		},
542	},
543	{
544		name:  "scientific float missing exponent",
545		input: []byte("cpu value=42E"),
546		results: []Result{
547			{
548				Name:  Measurement,
549				Value: []byte("cpu"),
550			},
551			{
552				Name: Error,
553				err:  protocol.ErrFieldParse,
554			},
555		},
556	},
557	{
558		name:  "float with decimal",
559		input: []byte("cpu value=42.2"),
560		results: []Result{
561			{
562				Name:  Measurement,
563				Value: []byte("cpu"),
564			},
565			{
566				Name:  FieldKey,
567				Value: []byte("value"),
568			},
569			{
570				Name:  FieldFloat,
571				Value: []byte("42.2"),
572			},
573			{
574				Name: Success,
575			},
576		},
577	},
578	{
579		name:  "negative float",
580		input: []byte("cpu value=-42"),
581		results: []Result{
582			{
583				Name:  Measurement,
584				Value: []byte("cpu"),
585			},
586			{
587				Name:  FieldKey,
588				Value: []byte("value"),
589			},
590			{
591				Name:  FieldFloat,
592				Value: []byte("-42"),
593			},
594			{
595				Name: Success,
596			},
597		},
598	},
599	{
600		name:  "float without integer digits",
601		input: []byte("cpu value=.42"),
602		results: []Result{
603			{
604				Name:  Measurement,
605				Value: []byte("cpu"),
606			},
607			{
608				Name:  FieldKey,
609				Value: []byte("value"),
610			},
611			{
612				Name:  FieldFloat,
613				Value: []byte(".42"),
614			},
615			{
616				Name: Success,
617			},
618		},
619	},
620	{
621		name:  "float without integer digits negative",
622		input: []byte("cpu value=-.42"),
623		results: []Result{
624			{
625				Name:  Measurement,
626				Value: []byte("cpu"),
627			},
628			{
629				Name:  FieldKey,
630				Value: []byte("value"),
631			},
632			{
633				Name:  FieldFloat,
634				Value: []byte("-.42"),
635			},
636			{
637				Name: Success,
638			},
639		},
640	},
641	{
642		name:  "float with multiple leading 0",
643		input: []byte("cpu value=00.42"),
644		results: []Result{
645			{
646				Name:  Measurement,
647				Value: []byte("cpu"),
648			},
649			{
650				Name:  FieldKey,
651				Value: []byte("value"),
652			},
653			{
654				Name:  FieldFloat,
655				Value: []byte("00.42"),
656			},
657			{
658				Name: Success,
659			},
660		},
661	},
662	{
663		name:  "invalid float with only dot",
664		input: []byte("cpu value=."),
665		results: []Result{
666			{
667				Name:  Measurement,
668				Value: []byte("cpu"),
669			},
670			{
671				Name: Error,
672				err:  protocol.ErrFieldParse,
673			},
674		},
675	},
676	{
677		name:  "multiple fields",
678		input: []byte("cpu x=42,y=42"),
679		results: []Result{
680			{
681				Name:  Measurement,
682				Value: []byte("cpu"),
683			},
684			{
685				Name:  FieldKey,
686				Value: []byte("x"),
687			},
688			{
689				Name:  FieldFloat,
690				Value: []byte("42"),
691			},
692			{
693				Name:  FieldKey,
694				Value: []byte("y"),
695			},
696			{
697				Name:  FieldFloat,
698				Value: []byte("42"),
699			},
700			{
701				Name: Success,
702			},
703		},
704	},
705	{
706		name:  "integer field",
707		input: []byte("cpu value=42i"),
708		results: []Result{
709			{
710				Name:  Measurement,
711				Value: []byte("cpu"),
712			},
713			{
714				Name:  FieldKey,
715				Value: []byte("value"),
716			},
717			{
718				Name:  FieldInt,
719				Value: []byte("42i"),
720			},
721			{
722				Name: Success,
723			},
724		},
725	},
726	{
727		name:  "negative integer field",
728		input: []byte("cpu value=-42i"),
729		results: []Result{
730			{
731				Name:  Measurement,
732				Value: []byte("cpu"),
733			},
734			{
735				Name:  FieldKey,
736				Value: []byte("value"),
737			},
738			{
739				Name:  FieldInt,
740				Value: []byte("-42i"),
741			},
742			{
743				Name: Success,
744			},
745		},
746	},
747	{
748		name:  "zero integer field",
749		input: []byte("cpu value=0i"),
750		results: []Result{
751			{
752				Name:  Measurement,
753				Value: []byte("cpu"),
754			},
755			{
756				Name:  FieldKey,
757				Value: []byte("value"),
758			},
759			{
760				Name:  FieldInt,
761				Value: []byte("0i"),
762			},
763			{
764				Name: Success,
765			},
766		},
767	},
768	{
769		name:  "negative zero integer field",
770		input: []byte("cpu value=-0i"),
771		results: []Result{
772			{
773				Name:  Measurement,
774				Value: []byte("cpu"),
775			},
776			{
777				Name:  FieldKey,
778				Value: []byte("value"),
779			},
780			{
781				Name:  FieldInt,
782				Value: []byte("-0i"),
783			},
784			{
785				Name: Success,
786			},
787		},
788	},
789	{
790		name:  "integer field overflow okay",
791		input: []byte("cpu value=9223372036854775808i"),
792		results: []Result{
793			{
794				Name:  Measurement,
795				Value: []byte("cpu"),
796			},
797			{
798				Name:  FieldKey,
799				Value: []byte("value"),
800			},
801			{
802				Name:  FieldInt,
803				Value: []byte("9223372036854775808i"),
804			},
805			{
806				Name: Success,
807			},
808		},
809	},
810	{
811		name:  "invalid field",
812		input: []byte("cpu value=howdy"),
813		results: []Result{
814			{
815				Name:  Measurement,
816				Value: []byte("cpu"),
817			},
818			{
819				Name: Error,
820				err:  protocol.ErrFieldParse,
821			},
822		},
823	},
824	{
825		name:  "string field",
826		input: []byte("cpu value=\"42\""),
827		results: []Result{
828			{
829				Name:  Measurement,
830				Value: []byte("cpu"),
831			},
832			{
833				Name:  FieldKey,
834				Value: []byte("value"),
835			},
836			{
837				Name:  FieldString,
838				Value: []byte("42"),
839			},
840			{
841				Name: Success,
842			},
843		},
844	},
845	{
846		name:  "newline in string field",
847		input: []byte("cpu value=\"4\n2\""),
848		results: []Result{
849			{
850				Name:  Measurement,
851				Value: []byte("cpu"),
852			},
853			{
854				Name:  FieldKey,
855				Value: []byte("value"),
856			},
857			{
858				Name:  FieldString,
859				Value: []byte("4\n2"),
860			},
861			{
862				Name: Success,
863			},
864		},
865	},
866	{
867		name:  "bool field",
868		input: []byte("cpu value=true"),
869		results: []Result{
870			{
871				Name:  Measurement,
872				Value: []byte("cpu"),
873			},
874			{
875				Name:  FieldKey,
876				Value: []byte("value"),
877			},
878			{
879				Name:  FieldBool,
880				Value: []byte("true"),
881			},
882			{
883				Name: Success,
884			},
885		},
886	},
887	{
888		name:  "tag",
889		input: []byte("cpu,host=localhost value=42"),
890		results: []Result{
891			{
892				Name:  Measurement,
893				Value: []byte("cpu"),
894			},
895			{
896				Name:  TagKey,
897				Value: []byte("host"),
898			},
899			{
900				Name:  TagValue,
901				Value: []byte("localhost"),
902			},
903			{
904				Name:  FieldKey,
905				Value: []byte("value"),
906			},
907			{
908				Name:  FieldFloat,
909				Value: []byte("42"),
910			},
911			{
912				Name: Success,
913			},
914		},
915	},
916	{
917		name:  "tag key escape space",
918		input: []byte("cpu,h\\ ost=localhost value=42"),
919		results: []Result{
920			{
921				Name:  Measurement,
922				Value: []byte("cpu"),
923			},
924			{
925				Name:  TagKey,
926				Value: []byte(`h\ ost`),
927			},
928			{
929				Name:  TagValue,
930				Value: []byte("localhost"),
931			},
932			{
933				Name:  FieldKey,
934				Value: []byte("value"),
935			},
936			{
937				Name:  FieldFloat,
938				Value: []byte("42"),
939			},
940			{
941				Name: Success,
942			},
943		},
944	},
945	{
946		name:  "tag key escape comma",
947		input: []byte("cpu,h\\,ost=localhost value=42"),
948		results: []Result{
949			{
950				Name:  Measurement,
951				Value: []byte("cpu"),
952			},
953			{
954				Name:  TagKey,
955				Value: []byte(`h\,ost`),
956			},
957			{
958				Name:  TagValue,
959				Value: []byte("localhost"),
960			},
961			{
962				Name:  FieldKey,
963				Value: []byte("value"),
964			},
965			{
966				Name:  FieldFloat,
967				Value: []byte("42"),
968			},
969			{
970				Name: Success,
971			},
972		},
973	},
974	{
975		name:  "tag key escape equal",
976		input: []byte("cpu,h\\=ost=localhost value=42"),
977		results: []Result{
978			{
979				Name:  Measurement,
980				Value: []byte("cpu"),
981			},
982			{
983				Name:  TagKey,
984				Value: []byte(`h\=ost`),
985			},
986			{
987				Name:  TagValue,
988				Value: []byte("localhost"),
989			},
990			{
991				Name:  FieldKey,
992				Value: []byte("value"),
993			},
994			{
995				Name:  FieldFloat,
996				Value: []byte("42"),
997			},
998			{
999				Name: Success,
1000			},
1001		},
1002	},
1003	{
1004		name:  "multiple tags",
1005		input: []byte("cpu,host=localhost,cpu=cpu0 value=42"),
1006		results: []Result{
1007			{
1008				Name:  Measurement,
1009				Value: []byte("cpu"),
1010			},
1011			{
1012				Name:  TagKey,
1013				Value: []byte("host"),
1014			},
1015			{
1016				Name:  TagValue,
1017				Value: []byte("localhost"),
1018			},
1019			{
1020				Name:  TagKey,
1021				Value: []byte("cpu"),
1022			},
1023			{
1024				Name:  TagValue,
1025				Value: []byte("cpu0"),
1026			},
1027			{
1028				Name:  FieldKey,
1029				Value: []byte("value"),
1030			},
1031			{
1032				Name:  FieldFloat,
1033				Value: []byte("42"),
1034			},
1035			{
1036				Name: Success,
1037			},
1038		},
1039	},
1040	{
1041		name:  "tag value escape space",
1042		input: []byte(`cpu,host=two\ words value=42`),
1043		results: []Result{
1044			{
1045				Name:  Measurement,
1046				Value: []byte("cpu"),
1047			},
1048			{
1049				Name:  TagKey,
1050				Value: []byte("host"),
1051			},
1052			{
1053				Name:  TagValue,
1054				Value: []byte(`two\ words`),
1055			},
1056			{
1057				Name:  FieldKey,
1058				Value: []byte("value"),
1059			},
1060			{
1061				Name:  FieldFloat,
1062				Value: []byte("42"),
1063			},
1064			{
1065				Name: Success,
1066			},
1067		},
1068	},
1069	{
1070		name:  "tag value double escape space",
1071		input: []byte(`cpu,host=two\\ words value=42`),
1072		results: []Result{
1073			{
1074				Name:  Measurement,
1075				Value: []byte("cpu"),
1076			},
1077			{
1078				Name:  TagKey,
1079				Value: []byte("host"),
1080			},
1081			{
1082				Name:  TagValue,
1083				Value: []byte(`two\\ words`),
1084			},
1085			{
1086				Name:  FieldKey,
1087				Value: []byte("value"),
1088			},
1089			{
1090				Name:  FieldFloat,
1091				Value: []byte("42"),
1092			},
1093			{
1094				Name: Success,
1095			},
1096		},
1097	},
1098	{
1099		name:  "tag value triple escape space",
1100		input: []byte(`cpu,host=two\\\ words value=42`),
1101		results: []Result{
1102			{
1103				Name:  Measurement,
1104				Value: []byte("cpu"),
1105			},
1106			{
1107				Name:  TagKey,
1108				Value: []byte("host"),
1109			},
1110			{
1111				Name:  TagValue,
1112				Value: []byte(`two\\\ words`),
1113			},
1114			{
1115				Name:  FieldKey,
1116				Value: []byte("value"),
1117			},
1118			{
1119				Name:  FieldFloat,
1120				Value: []byte("42"),
1121			},
1122			{
1123				Name: Success,
1124			},
1125		},
1126	},
1127	{
1128		name:  "tag invalid missing separator",
1129		input: []byte("cpu,xyzzy value=42"),
1130		results: []Result{
1131			{
1132				Name:  Measurement,
1133				Value: []byte("cpu"),
1134			},
1135			{
1136				Name: Error,
1137				err:  protocol.ErrTagParse,
1138			},
1139		},
1140	},
1141	{
1142		name:  "tag invalid missing value",
1143		input: []byte("cpu,xyzzy= value=42"),
1144		results: []Result{
1145			{
1146				Name:  Measurement,
1147				Value: []byte("cpu"),
1148			},
1149			{
1150				Name: Error,
1151				err:  protocol.ErrTagParse,
1152			},
1153		},
1154	},
1155	{
1156		name:  "tag invalid unescaped space",
1157		input: []byte("cpu,h ost=localhost value=42"),
1158		results: []Result{
1159			{
1160				Name:  Measurement,
1161				Value: []byte("cpu"),
1162			},
1163			{
1164				Name: Error,
1165				err:  protocol.ErrTagParse,
1166			},
1167		},
1168	},
1169	{
1170		name:  "tag invalid unescaped comma",
1171		input: []byte("cpu,h,ost=localhost value=42"),
1172		results: []Result{
1173			{
1174				Name:  Measurement,
1175				Value: []byte("cpu"),
1176			},
1177			{
1178				Name: Error,
1179				err:  protocol.ErrTagParse,
1180			},
1181		},
1182	},
1183	{
1184		name:  "tag invalid unescaped equals",
1185		input: []byte("cpu,h=ost=localhost value=42"),
1186		results: []Result{
1187			{
1188				Name:  Measurement,
1189				Value: []byte("cpu"),
1190			},
1191			{
1192				Name: Error,
1193				err:  protocol.ErrTagParse,
1194			},
1195		},
1196	},
1197	{
1198		name:  "timestamp negative",
1199		input: []byte("cpu value=42 -1"),
1200		results: []Result{
1201			{
1202				Name:  Measurement,
1203				Value: []byte("cpu"),
1204			},
1205			{
1206				Name:  FieldKey,
1207				Value: []byte("value"),
1208			},
1209			{
1210				Name:  FieldFloat,
1211				Value: []byte("42"),
1212			},
1213			{
1214				Name:  Timestamp,
1215				Value: []byte("-1"),
1216			},
1217			{
1218				Name: Success,
1219			},
1220		},
1221	},
1222	{
1223		name:  "timestamp zero",
1224		input: []byte("cpu value=42 0"),
1225		results: []Result{
1226			{
1227				Name:  Measurement,
1228				Value: []byte("cpu"),
1229			},
1230			{
1231				Name:  FieldKey,
1232				Value: []byte("value"),
1233			},
1234			{
1235				Name:  FieldFloat,
1236				Value: []byte("42"),
1237			},
1238			{
1239				Name:  Timestamp,
1240				Value: []byte("0"),
1241			},
1242			{
1243				Name: Success,
1244			},
1245		},
1246	},
1247	{
1248		name:  "multiline",
1249		input: []byte("cpu value=42\n\n\n\ncpu value=43"),
1250		results: []Result{
1251			{
1252				Name:  Measurement,
1253				Value: []byte("cpu"),
1254			},
1255			{
1256				Name:  FieldKey,
1257				Value: []byte("value"),
1258			},
1259			{
1260				Name:  FieldFloat,
1261				Value: []byte("42"),
1262			},
1263			{
1264				Name: Success,
1265			},
1266			{
1267				Name:  Measurement,
1268				Value: []byte("cpu"),
1269			},
1270			{
1271				Name:  FieldKey,
1272				Value: []byte("value"),
1273			},
1274			{
1275				Name:  FieldFloat,
1276				Value: []byte("43"),
1277			},
1278			{
1279				Name: Success,
1280			},
1281		},
1282	},
1283	{
1284		name:  "error recovery",
1285		input: []byte("cpu value=howdy,value2=42\ncpu\ncpu value=42"),
1286		results: []Result{
1287			{
1288				Name:  Measurement,
1289				Value: []byte("cpu"),
1290			},
1291			{
1292				Name: Error,
1293				err:  protocol.ErrFieldParse,
1294			},
1295			{
1296				Name: Error,
1297				err:  protocol.ErrTagParse,
1298			},
1299			{
1300				Name:  Measurement,
1301				Value: []byte("cpu"),
1302			},
1303			{
1304				Name:  FieldKey,
1305				Value: []byte("value"),
1306			},
1307			{
1308				Name:  FieldFloat,
1309				Value: []byte("42"),
1310			},
1311			{
1312				Name: Success,
1313			},
1314		},
1315	},
1316	{
1317		name:  "line whitespace",
1318		input: []byte("   cpu   value=42  1516241192000000000  \n\n cpu value=42"),
1319		results: []Result{
1320			{
1321				Name:  Measurement,
1322				Value: []byte("cpu"),
1323			},
1324			{
1325				Name:  FieldKey,
1326				Value: []byte("value"),
1327			},
1328			{
1329				Name:  FieldFloat,
1330				Value: []byte("42"),
1331			},
1332			{
1333				Name:  Timestamp,
1334				Value: []byte("1516241192000000000"),
1335			},
1336			{
1337				Name: Success,
1338			},
1339			{
1340				Name:  Measurement,
1341				Value: []byte("cpu"),
1342			},
1343			{
1344				Name:  FieldKey,
1345				Value: []byte("value"),
1346			},
1347			{
1348				Name:  FieldFloat,
1349				Value: []byte("42"),
1350			},
1351			{
1352				Name: Success,
1353			},
1354		},
1355	},
1356	{
1357		name:  "leading newline",
1358		input: []byte("\ncpu value=42"),
1359		results: []Result{
1360			{
1361				Name:  Measurement,
1362				Value: []byte("cpu"),
1363			},
1364			{
1365				Name:  FieldKey,
1366				Value: []byte("value"),
1367			},
1368			{
1369				Name:  FieldFloat,
1370				Value: []byte("42"),
1371			},
1372			{
1373				Name: Success,
1374			},
1375		},
1376	},
1377	{
1378		name:  "invalid missing field value",
1379		input: []byte("cpu value="),
1380		results: []Result{
1381			{
1382				Name:  Measurement,
1383				Value: []byte("cpu"),
1384			},
1385			{
1386				Name: Error,
1387				err:  protocol.ErrFieldParse,
1388			},
1389		},
1390	},
1391	{
1392		name:  "invalid eof field key",
1393		input: []byte("cpu value"),
1394		results: []Result{
1395			{
1396				Name:  Measurement,
1397				Value: []byte("cpu"),
1398			},
1399			{
1400				Name: Error,
1401				err:  protocol.ErrFieldParse,
1402			},
1403		},
1404	},
1405	{
1406		name:  "invalid measurement only",
1407		input: []byte("cpu"),
1408		results: []Result{
1409			{
1410				Name:  Measurement,
1411				Value: []byte("cpu"),
1412			},
1413			{
1414				Name: Error,
1415				err:  protocol.ErrTagParse,
1416			},
1417		},
1418	},
1419	{
1420		name:  "invalid measurement char",
1421		input: []byte(","),
1422		results: []Result{
1423			{
1424				Name: Error,
1425				err:  protocol.ErrNameParse,
1426			},
1427		},
1428	},
1429	{
1430		name:  "invalid missing tag",
1431		input: []byte("cpu, value=42"),
1432		results: []Result{
1433			{
1434				Name:  Measurement,
1435				Value: []byte("cpu"),
1436			},
1437			{
1438				Name: Error,
1439				err:  protocol.ErrTagParse,
1440			},
1441		},
1442	},
1443	{
1444		name:  "invalid missing field",
1445		input: []byte("cpu,x=y "),
1446		results: []Result{
1447			{
1448				Name:  Measurement,
1449				Value: []byte("cpu"),
1450			},
1451			{
1452				Name:  TagKey,
1453				Value: []byte("x"),
1454			},
1455			{
1456				Name:  TagValue,
1457				Value: []byte("y"),
1458			},
1459			{
1460				Name: Error,
1461				err:  protocol.ErrFieldParse,
1462			},
1463		},
1464	},
1465	{
1466		name:  "invalid too many fields",
1467		input: []byte("cpu value=42 value=43"),
1468		results: []Result{
1469			{
1470				Name:  Measurement,
1471				Value: []byte("cpu"),
1472			},
1473			{
1474				Name:  FieldKey,
1475				Value: []byte("value"),
1476			},
1477			{
1478				Name:  FieldFloat,
1479				Value: []byte("42"),
1480			},
1481			{
1482				Name: Error,
1483				err:  protocol.ErrTimestampParse,
1484			},
1485		},
1486	},
1487	{
1488		name:  "invalid timestamp too long",
1489		input: []byte("cpu value=42 12345678901234567890"),
1490		results: []Result{
1491			{
1492				Name:  Measurement,
1493				Value: []byte("cpu"),
1494			},
1495			{
1496				Name:  FieldKey,
1497				Value: []byte("value"),
1498			},
1499			{
1500				Name:  FieldFloat,
1501				Value: []byte("42"),
1502			},
1503			{
1504				Name: Error,
1505				err:  protocol.ErrTimestampParse,
1506			},
1507		},
1508	},
1509	{
1510		name:  "invalid open string field",
1511		input: []byte(`cpu value="42 12345678901234567890`),
1512		results: []Result{
1513			{
1514				Name:  Measurement,
1515				Value: []byte("cpu"),
1516			},
1517			{
1518				Name: Error,
1519				err:  protocol.ErrFieldParse,
1520			},
1521		},
1522	},
1523	{
1524		name:  "invalid field value",
1525		input: []byte("cpu value=howdy"),
1526		results: []Result{
1527			{
1528				Name:  Measurement,
1529				Value: []byte("cpu"),
1530			},
1531			{
1532				Name: Error,
1533				err:  protocol.ErrFieldParse,
1534			},
1535		},
1536	},
1537	{
1538		name:  "invalid quoted timestamp",
1539		input: []byte("cpu value=42 \"12345678901234567890\""),
1540		results: []Result{
1541			{
1542				Name:  Measurement,
1543				Value: []byte("cpu"),
1544			},
1545			{
1546				Name:  FieldKey,
1547				Value: []byte("value"),
1548			},
1549			{
1550				Name:  FieldFloat,
1551				Value: []byte("42"),
1552			},
1553			{
1554				Name: Error,
1555				err:  protocol.ErrTimestampParse,
1556			},
1557		},
1558	},
1559	{
1560		name:    "comment only",
1561		input:   []byte("# blah blah"),
1562		results: []Result(nil),
1563	},
1564	{
1565		name:  "commented line",
1566		input: []byte("# blah blah\ncpu value=42"),
1567		results: []Result{
1568			{
1569				Name:  Measurement,
1570				Value: []byte("cpu"),
1571			},
1572			{
1573				Name:  FieldKey,
1574				Value: []byte("value"),
1575			},
1576			{
1577				Name:  FieldFloat,
1578				Value: []byte("42"),
1579			},
1580			{
1581				Name: Success,
1582			},
1583		},
1584	},
1585	{
1586		name:  "middle comment",
1587		input: []byte("cpu value=42\n# blah blah\ncpu value=42"),
1588		results: []Result{
1589			{
1590				Name:  Measurement,
1591				Value: []byte("cpu"),
1592			},
1593			{
1594				Name:  FieldKey,
1595				Value: []byte("value"),
1596			},
1597			{
1598				Name:  FieldFloat,
1599				Value: []byte("42"),
1600			},
1601			{
1602				Name: Success,
1603			},
1604			{
1605				Name:  Measurement,
1606				Value: []byte("cpu"),
1607			},
1608			{
1609				Name:  FieldKey,
1610				Value: []byte("value"),
1611			},
1612			{
1613				Name:  FieldFloat,
1614				Value: []byte("42"),
1615			},
1616			{
1617				Name: Success,
1618			},
1619		},
1620	},
1621	{
1622		name:  "end with comment",
1623		input: []byte("cpu value=42\n# blah blah"),
1624		results: []Result{
1625			{
1626				Name:  Measurement,
1627				Value: []byte("cpu"),
1628			},
1629			{
1630				Name:  FieldKey,
1631				Value: []byte("value"),
1632			},
1633			{
1634				Name:  FieldFloat,
1635				Value: []byte("42"),
1636			},
1637			{
1638				Name: Success,
1639			},
1640		},
1641	},
1642	{
1643		name:  "end with comment and whitespace",
1644		input: []byte("cpu value=42\n# blah blah\n\n  "),
1645		results: []Result{
1646			{
1647				Name:  Measurement,
1648				Value: []byte("cpu"),
1649			},
1650			{
1651				Name:  FieldKey,
1652				Value: []byte("value"),
1653			},
1654			{
1655				Name:  FieldFloat,
1656				Value: []byte("42"),
1657			},
1658			{
1659				Name: Success,
1660			},
1661		},
1662	},
1663	{
1664		name:  "unicode",
1665		input: []byte("cpu ☺=42"),
1666		results: []Result{
1667			{
1668				Name:  Measurement,
1669				Value: []byte("cpu"),
1670			},
1671			{
1672				Name:  FieldKey,
1673				Value: []byte("☺"),
1674			},
1675			{
1676				Name:  FieldFloat,
1677				Value: []byte("42"),
1678			},
1679			{
1680				Name: Success,
1681			},
1682		},
1683	},
1684}
1685
1686func TestMachine(t *testing.T) {
1687	for _, tt := range tests {
1688		t.Run(tt.name, func(t *testing.T) {
1689			handler := &TestingHandler{}
1690			fsm := protocol.NewMachine(handler)
1691			fsm.SetData(tt.input)
1692
1693			for i := 0; i < 20; i++ {
1694				err := fsm.Next()
1695				if err != nil && err == protocol.EOF {
1696					break
1697				}
1698				handler.Result(err)
1699			}
1700
1701			results := handler.Results()
1702
1703			if len(tt.results) != len(results) {
1704				t.Errorf("unexpected difference in result lengths = %d, want %v", len(tt.results), len(results))
1705				return
1706			}
1707			for i := range tt.results {
1708				if tt.results[i].String() != results[i].String() {
1709					t.Errorf("Machine.Results() = %v, want %v", results, tt.results)
1710				}
1711			}
1712		})
1713	}
1714}
1715
1716var positionTests = []struct {
1717	name   string
1718	input  []byte
1719	lineno int
1720	column int
1721}{
1722	{
1723		name:   "empty string",
1724		input:  []byte(""),
1725		lineno: 1,
1726		column: 1,
1727	},
1728	{
1729		name:   "minimal",
1730		input:  []byte("cpu value=42"),
1731		lineno: 1,
1732		column: 13,
1733	},
1734	{
1735		name:   "one newline",
1736		input:  []byte("cpu value=42\ncpu value=42"),
1737		lineno: 2,
1738		column: 13,
1739	},
1740	{
1741		name:   "several newlines",
1742		input:  []byte("cpu value=42\n\n\n"),
1743		lineno: 4,
1744		column: 1,
1745	},
1746	{
1747		name:   "error on second line",
1748		input:  []byte("cpu value=42\ncpu value=invalid"),
1749		lineno: 2,
1750		column: 11,
1751	},
1752	{
1753		name:   "error after comment line",
1754		input:  []byte("cpu value=42\n# comment\ncpu value=invalid"),
1755		lineno: 3,
1756		column: 11,
1757	},
1758	{
1759		name:   "dos line endings",
1760		input:  []byte("cpu value=42\r\ncpu value=invalid"),
1761		lineno: 2,
1762		column: 11,
1763	},
1764	{
1765		name:   "mac line endings not supported",
1766		input:  []byte("cpu value=42\rcpu value=invalid"),
1767		lineno: 1,
1768		column: 14,
1769	},
1770}
1771
1772func TestMachinePosition(t *testing.T) {
1773	for _, tt := range positionTests {
1774		t.Run(tt.name, func(t *testing.T) {
1775			handler := &TestingHandler{}
1776			fsm := protocol.NewMachine(handler)
1777			fsm.SetData(tt.input)
1778
1779			// Parse until an error or eof
1780			for i := 0; i < 20; i++ {
1781				err := fsm.Next()
1782				if err != nil {
1783					break
1784				}
1785			}
1786
1787			if tt.lineno != fsm.LineNumber() {
1788				t.Errorf("unexpected difference in line number: %d, want = %d", tt.lineno, fsm.LineNumber())
1789			}
1790
1791			if tt.column != fsm.Column() {
1792				t.Errorf("unexpected difference in column number: %d, want = %d", tt.column, fsm.Column())
1793			}
1794		})
1795	}
1796}
1797
1798func BenchmarkMachine(b *testing.B) {
1799	for _, tt := range tests {
1800		b.Run(tt.name, func(b *testing.B) {
1801			handler := &BenchmarkingHandler{}
1802			fsm := protocol.NewMachine(handler)
1803
1804			for n := 0; n < b.N; n++ {
1805				fsm.SetData(tt.input)
1806
1807				for {
1808					err := fsm.Next()
1809					if err != nil {
1810						break
1811					}
1812				}
1813			}
1814		})
1815	}
1816}
1817
1818func TestMachineProcstat(t *testing.T) {
1819	input := []byte("procstat,exe=bash,process_name=bash voluntary_context_switches=42i,memory_rss=5103616i,rlimit_memory_data_hard=2147483647i,cpu_time_user=0.02,rlimit_file_locks_soft=2147483647i,pid=29417i,cpu_time_nice=0,rlimit_memory_locked_soft=65536i,read_count=259i,rlimit_memory_vms_hard=2147483647i,memory_swap=0i,rlimit_num_fds_soft=1024i,rlimit_nice_priority_hard=0i,cpu_time_soft_irq=0,cpu_time=0i,rlimit_memory_locked_hard=65536i,realtime_priority=0i,signals_pending=0i,nice_priority=20i,cpu_time_idle=0,memory_stack=139264i,memory_locked=0i,rlimit_memory_stack_soft=8388608i,cpu_time_iowait=0,cpu_time_guest=0,cpu_time_guest_nice=0,rlimit_memory_data_soft=2147483647i,read_bytes=0i,rlimit_cpu_time_soft=2147483647i,involuntary_context_switches=2i,write_bytes=106496i,cpu_time_system=0,cpu_time_irq=0,cpu_usage=0,memory_vms=21659648i,memory_data=1576960i,rlimit_memory_stack_hard=2147483647i,num_threads=1i,rlimit_memory_rss_soft=2147483647i,rlimit_realtime_priority_soft=0i,num_fds=4i,write_count=35i,rlimit_signals_pending_soft=78994i,cpu_time_steal=0,rlimit_num_fds_hard=4096i,rlimit_file_locks_hard=2147483647i,rlimit_cpu_time_hard=2147483647i,rlimit_signals_pending_hard=78994i,rlimit_nice_priority_soft=0i,rlimit_memory_rss_hard=2147483647i,rlimit_memory_vms_soft=2147483647i,rlimit_realtime_priority_hard=0i 1517620624000000000")
1820	handler := &TestingHandler{}
1821	fsm := protocol.NewMachine(handler)
1822	fsm.SetData(input)
1823	for {
1824		err := fsm.Next()
1825		if err != nil {
1826			break
1827		}
1828	}
1829}
1830
1831func BenchmarkMachineProcstat(b *testing.B) {
1832	input := []byte("procstat,exe=bash,process_name=bash voluntary_context_switches=42i,memory_rss=5103616i,rlimit_memory_data_hard=2147483647i,cpu_time_user=0.02,rlimit_file_locks_soft=2147483647i,pid=29417i,cpu_time_nice=0,rlimit_memory_locked_soft=65536i,read_count=259i,rlimit_memory_vms_hard=2147483647i,memory_swap=0i,rlimit_num_fds_soft=1024i,rlimit_nice_priority_hard=0i,cpu_time_soft_irq=0,cpu_time=0i,rlimit_memory_locked_hard=65536i,realtime_priority=0i,signals_pending=0i,nice_priority=20i,cpu_time_idle=0,memory_stack=139264i,memory_locked=0i,rlimit_memory_stack_soft=8388608i,cpu_time_iowait=0,cpu_time_guest=0,cpu_time_guest_nice=0,rlimit_memory_data_soft=2147483647i,read_bytes=0i,rlimit_cpu_time_soft=2147483647i,involuntary_context_switches=2i,write_bytes=106496i,cpu_time_system=0,cpu_time_irq=0,cpu_usage=0,memory_vms=21659648i,memory_data=1576960i,rlimit_memory_stack_hard=2147483647i,num_threads=1i,rlimit_memory_rss_soft=2147483647i,rlimit_realtime_priority_soft=0i,num_fds=4i,write_count=35i,rlimit_signals_pending_soft=78994i,cpu_time_steal=0,rlimit_num_fds_hard=4096i,rlimit_file_locks_hard=2147483647i,rlimit_cpu_time_hard=2147483647i,rlimit_signals_pending_hard=78994i,rlimit_nice_priority_soft=0i,rlimit_memory_rss_hard=2147483647i,rlimit_memory_vms_soft=2147483647i,rlimit_realtime_priority_hard=0i 1517620624000000000")
1833	handler := &BenchmarkingHandler{}
1834	fsm := protocol.NewMachine(handler)
1835	for n := 0; n < b.N; n++ {
1836		fsm.SetData(input)
1837		for {
1838			err := fsm.Next()
1839			if err != nil {
1840				break
1841			}
1842		}
1843	}
1844}
1845
1846func TestSeriesMachine(t *testing.T) {
1847	var tests = []struct {
1848		name    string
1849		input   []byte
1850		results []Result
1851		err     error
1852	}{
1853		{
1854			name:    "empty string",
1855			input:   []byte(""),
1856			results: nil,
1857		},
1858		{
1859			name:  "no tags",
1860			input: []byte("cpu"),
1861			results: []Result{
1862				{
1863					Name:  Measurement,
1864					Value: []byte("cpu"),
1865				},
1866				{
1867					Name: Success,
1868				},
1869			},
1870		},
1871		{
1872			name:  "tags",
1873			input: []byte("cpu,a=x,b=y"),
1874			results: []Result{
1875				{
1876					Name:  Measurement,
1877					Value: []byte("cpu"),
1878				},
1879				{
1880					Name:  TagKey,
1881					Value: []byte("a"),
1882				},
1883				{
1884					Name:  TagValue,
1885					Value: []byte("x"),
1886				},
1887				{
1888					Name:  TagKey,
1889					Value: []byte("b"),
1890				},
1891				{
1892					Name:  TagValue,
1893					Value: []byte("y"),
1894				},
1895				{
1896					Name: Success,
1897				},
1898			},
1899		},
1900	}
1901
1902	for _, tt := range tests {
1903		t.Run(tt.name, func(t *testing.T) {
1904			handler := &TestingHandler{}
1905			fsm := protocol.NewSeriesMachine(handler)
1906			fsm.SetData(tt.input)
1907
1908			for {
1909				err := fsm.Next()
1910				if err != nil {
1911					break
1912				}
1913				handler.Result(err)
1914			}
1915
1916			results := handler.Results()
1917			if len(tt.results) != len(results) {
1918				t.Errorf("unexpected difference in result lengths = %d, want %v", len(tt.results), len(results))
1919				return
1920			}
1921			for i := range tt.results {
1922				if tt.results[i].String() != results[i].String() {
1923					t.Errorf("Machine.Results() = %v, want %v", results, tt.results)
1924				}
1925			}
1926		})
1927	}
1928}
1929
1930type MockHandler struct {
1931	SetMeasurementF func(name []byte) error
1932	AddTagF         func(key []byte, value []byte) error
1933	AddIntF         func(key []byte, value []byte) error
1934	AddUintF        func(key []byte, value []byte) error
1935	AddFloatF       func(key []byte, value []byte) error
1936	AddStringF      func(key []byte, value []byte) error
1937	AddBoolF        func(key []byte, value []byte) error
1938	SetTimestampF   func(tm []byte) error
1939
1940	TestingHandler
1941}
1942
1943func (h *MockHandler) SetMeasurement(name []byte) error {
1944	h.TestingHandler.SetMeasurement(name)
1945	return h.SetMeasurementF(name)
1946}
1947
1948func (h *MockHandler) AddTag(name, value []byte) error {
1949	return h.AddTagF(name, value)
1950}
1951
1952func (h *MockHandler) AddInt(name, value []byte) error {
1953	err := h.AddIntF(name, value)
1954	if err != nil {
1955		return err
1956	}
1957	h.TestingHandler.AddInt(name, value)
1958	return nil
1959}
1960
1961func (h *MockHandler) AddUint(name, value []byte) error {
1962	err := h.AddUintF(name, value)
1963	if err != nil {
1964		return err
1965	}
1966	h.TestingHandler.AddUint(name, value)
1967	return nil
1968}
1969
1970func (h *MockHandler) AddFloat(name, value []byte) error {
1971	return h.AddFloatF(name, value)
1972}
1973
1974func (h *MockHandler) AddString(name, value []byte) error {
1975	return h.AddStringF(name, value)
1976}
1977
1978func (h *MockHandler) AddBool(name, value []byte) error {
1979	return h.AddBoolF(name, value)
1980}
1981
1982func (h *MockHandler) SetTimestamp(tm []byte) error {
1983	return h.SetTimestampF(tm)
1984}
1985
1986var errorRecoveryTests = []struct {
1987	name    string
1988	input   []byte
1989	handler *MockHandler
1990	results []Result
1991}{
1992	{
1993		name:  "integer",
1994		input: []byte("cpu value=43i\ncpu value=42i"),
1995		handler: &MockHandler{
1996			SetMeasurementF: func(name []byte) error {
1997				return nil
1998			},
1999			AddIntF: func(name, value []byte) error {
2000				if string(value) != "42i" {
2001					return errors.New("handler error")
2002				}
2003				return nil
2004			},
2005		},
2006		results: []Result{
2007			{
2008				Name:  Measurement,
2009				Value: []byte("cpu"),
2010			},
2011			{
2012				Name: Error,
2013				err:  errors.New("handler error"),
2014			},
2015			{
2016				Name:  Measurement,
2017				Value: []byte("cpu"),
2018			},
2019			{
2020				Name:  FieldKey,
2021				Value: []byte("value"),
2022			},
2023			{
2024				Name:  FieldInt,
2025				Value: []byte("42i"),
2026			},
2027			{
2028				Name: Success,
2029			},
2030		},
2031	},
2032	{
2033		name:  "integer with timestamp",
2034		input: []byte("cpu value=43i 1516241192000000000\ncpu value=42i"),
2035		handler: &MockHandler{
2036			SetMeasurementF: func(name []byte) error {
2037				return nil
2038			},
2039			AddIntF: func(name, value []byte) error {
2040				if string(value) != "42i" {
2041					return errors.New("handler error")
2042				}
2043				return nil
2044			},
2045		},
2046		results: []Result{
2047			{
2048				Name:  Measurement,
2049				Value: []byte("cpu"),
2050			},
2051			{
2052				Name: Error,
2053				err:  errors.New("handler error"),
2054			},
2055			{
2056				Name:  Measurement,
2057				Value: []byte("cpu"),
2058			},
2059			{
2060				Name:  FieldKey,
2061				Value: []byte("value"),
2062			},
2063			{
2064				Name:  FieldInt,
2065				Value: []byte("42i"),
2066			},
2067			{
2068				Name: Success,
2069			},
2070		},
2071	},
2072	{
2073		name:  "unsigned",
2074		input: []byte("cpu value=43u\ncpu value=42u"),
2075		handler: &MockHandler{
2076			SetMeasurementF: func(name []byte) error {
2077				return nil
2078			},
2079			AddUintF: func(name, value []byte) error {
2080				if string(value) != "42u" {
2081					return errors.New("handler error")
2082				}
2083				return nil
2084			},
2085		},
2086		results: []Result{
2087			{
2088				Name:  Measurement,
2089				Value: []byte("cpu"),
2090			},
2091			{
2092				Name: Error,
2093				err:  errors.New("handler error"),
2094			},
2095			{
2096				Name:  Measurement,
2097				Value: []byte("cpu"),
2098			},
2099			{
2100				Name:  FieldKey,
2101				Value: []byte("value"),
2102			},
2103			{
2104				Name:  FieldUint,
2105				Value: []byte("42u"),
2106			},
2107			{
2108				Name: Success,
2109			},
2110		},
2111	},
2112}
2113
2114func TestHandlerErrorRecovery(t *testing.T) {
2115	for _, tt := range errorRecoveryTests {
2116		t.Run(tt.name, func(t *testing.T) {
2117			fsm := protocol.NewMachine(tt.handler)
2118			fsm.SetData(tt.input)
2119
2120			for i := 0; i < 20; i++ {
2121				err := fsm.Next()
2122				if err != nil && err == protocol.EOF {
2123					break
2124				}
2125				tt.handler.Result(err)
2126			}
2127
2128			results := tt.handler.Results()
2129			if len(tt.results) != len(results) {
2130				t.Errorf("unexpected difference in result lengths = %d, want %v", len(tt.results), len(results))
2131				return
2132			}
2133			for i := range tt.results {
2134				if tt.results[i].String() != results[i].String() {
2135					t.Errorf("Machine.Results() = %v, want %v", results, tt.results)
2136				}
2137			}
2138		})
2139	}
2140}
2141
2142func TestStreamMachine(t *testing.T) {
2143	type testcase struct {
2144		name    string
2145		input   io.Reader
2146		results []Result
2147		err     error
2148	}
2149
2150	var tc []testcase
2151	for _, tt := range tests {
2152		tc = append(tc, testcase{
2153			name:    tt.name,
2154			input:   bytes.NewBuffer([]byte(tt.input)),
2155			results: tt.results,
2156			err:     tt.err,
2157		})
2158	}
2159
2160	for _, tt := range tc {
2161		t.Run(tt.name, func(t *testing.T) {
2162			handler := &TestingHandler{}
2163			fsm := protocol.NewStreamMachine(tt.input, handler)
2164
2165			// Parse only up to 20 metrics; to avoid any bugs where the parser
2166			// isn't terminated.
2167			for i := 0; i < 20; i++ {
2168				err := fsm.Next()
2169				if err != nil && err == protocol.EOF {
2170					break
2171				}
2172				handler.Result(err)
2173			}
2174
2175			results := handler.Results()
2176			if len(tt.results) != len(results) {
2177				t.Errorf("unexpected difference in result lengths = %d, want %v", len(tt.results), len(results))
2178				return
2179			}
2180			for i := range tt.results {
2181				if tt.results[i].String() != results[i].String() {
2182					t.Errorf("Machine.Results() = %v, want %v", results, tt.results)
2183				}
2184			}
2185		})
2186	}
2187}
2188
2189func TestStreamMachinePosition(t *testing.T) {
2190	type testcase struct {
2191		name   string
2192		input  io.Reader
2193		lineno int
2194		column int
2195	}
2196
2197	var tc []testcase
2198	for _, tt := range positionTests {
2199		tc = append(tc, testcase{
2200			name:   tt.name,
2201			input:  bytes.NewBuffer([]byte(tt.input)),
2202			lineno: tt.lineno,
2203			column: tt.column,
2204		})
2205	}
2206
2207	for _, tt := range tc {
2208		t.Run(tt.name, func(t *testing.T) {
2209			handler := &TestingHandler{}
2210			fsm := protocol.NewStreamMachine(tt.input, handler)
2211
2212			// Parse until an error or eof
2213			for i := 0; i < 20; i++ {
2214				err := fsm.Next()
2215				if err != nil {
2216					break
2217				}
2218			}
2219
2220			if tt.lineno != fsm.LineNumber() {
2221				t.Errorf("unexpected difference in line number: %d, want = %d", tt.lineno, fsm.LineNumber())
2222			}
2223
2224			if tt.column != fsm.Column() {
2225				t.Errorf("unexpected difference in column number: %d, want = %d", tt.column, fsm.Column())
2226			}
2227		})
2228	}
2229}
2230