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