github.com/m3db/m3@v1.5.0/src/metrics/carbon/parser_test.go (about) 1 // Copyright (c) 2019 Uber Technologies, Inc. 2 // 3 // Permission is hereby granted, free of charge, to any person obtaining a copy 4 // of this software and associated documentation files (the "Software"), to deal 5 // in the Software without restriction, including without limitation the rights 6 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 // copies of the Software, and to permit persons to whom the Software is 8 // furnished to do so, subject to the following conditions: 9 // 10 // The above copyright notice and this permission notice shall be included in 11 // all copies or substantial portions of the Software. 12 // 13 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 // THE SOFTWARE. 20 21 package carbon 22 23 import ( 24 "bytes" 25 "fmt" 26 "math" 27 "strings" 28 "testing" 29 "time" 30 31 "github.com/m3db/m3/src/x/instrument" 32 33 "github.com/stretchr/testify/assert" 34 "github.com/stretchr/testify/require" 35 ) 36 37 var ( 38 testIOpts = instrument.NewOptions() 39 ) 40 41 type carbonLine struct { 42 path string 43 value float64 44 time time.Time 45 line string 46 } 47 48 var testLines = []carbonLine{ 49 {"foo.bar.zed", 45565.02, time.Unix(1428951394, 0), 50 "foo.bar.zed 45565.02 1428951394"}, 51 {"foo.bar.quad", 10.0, time.Unix(1428951394, 0), 52 "foo.bar.quad 10 1428951394"}, 53 {"foo.bar.nan", math.NaN(), time.Unix(1428951394, 0), 54 "foo.bar.nan nan 1428951394"}, 55 {"foo.bar.nan", math.NaN(), time.Unix(1428951394, 0), 56 "foo.bar.nan -NaN 1428951394"}, 57 {"foo.bar.negative", -18000.00000, time.Unix(1429480924, 0), 58 "foo.bar.negative -18000.000000 1429480924"}, 59 {"short", 1, time.Unix(1, 0), 60 "short 1 1"}, 61 } 62 63 func TestScannerMetric(t *testing.T) { 64 var buf bytes.Buffer 65 for _, line := range testLines { 66 fmt.Fprintf(&buf, "%s\n", line.line) 67 } 68 69 s := NewScanner(&buf, testIOpts) 70 for _, line := range testLines { 71 t.Run(line.path, func(t *testing.T) { 72 require.True(t, s.Scan(), "could not parse to line %s, err: %v", line.line, s.Err()) 73 name, ts, value := s.Metric() 74 expectedName, expectedTime, expectedValue := 75 line.path, line.time, line.value 76 assert.Equal(t, expectedName, string(name)) 77 assert.Equal(t, expectedTime, ts) 78 if math.IsNaN(expectedValue) { 79 require.True(t, math.IsNaN(value)) 80 } else { 81 assert.Equal(t, expectedValue, value) 82 } 83 }) 84 } 85 86 assert.False(t, s.Scan(), "parsed past end of buffer") 87 assert.Nil(t, s.Err()) 88 assert.Equal(t, 0, s.MalformedCount) 89 } 90 91 func TestParse(t *testing.T) { 92 for i := range testLines { 93 name, ts, value, err := Parse([]byte(testLines[i].line)) 94 require.Nil(t, err, "could not parse %s", testLines[i].line) 95 assert.Equal(t, testLines[i].path, string(name)) 96 assert.Equal(t, testLines[i].time, ts) 97 98 if math.IsNaN(testLines[i].value) { 99 require.True(t, math.IsNaN(value)) 100 } else { 101 assert.Equal(t, testLines[i].value, value) 102 } 103 104 } 105 } 106 107 func TestParseName(t *testing.T) { 108 for _, line := range testLines { 109 name, rest, err := ParseName([]byte(line.line)) 110 _ = rest 111 assert.Equal(t, line.path, string(name)) 112 // we expect the returned string to have de-duped spaces after ParseName's 113 // initial pass 114 exp := strings.TrimPrefix(line.line[len(line.path)+1:], " ") 115 assert.Equal(t, exp, string(rest)) 116 assert.Nil(t, err) 117 } 118 119 _, _, err := ParseName([]byte("thereisnospacehere")) 120 assert.NotNil(t, err) 121 } 122 123 func TestParseErrors(t *testing.T) { 124 assertParseError(t, " ") 125 assertParseError(t, " ") 126 assertParseError(t, " ") 127 assertParseError(t, "a ") 128 assertParseError(t, "a 1") 129 assertParseError(t, "a ") 130 assertParseError(t, "a 1 ") 131 assertParseError(t, "a 1") 132 assertParseError(t, " 1 1") 133 assertParseError(t, " 1") 134 assertParseError(t, "foo") 135 assertParseError(t, "foo bar zed") 136 assertParseError(t, "foo 4394 zed") 137 assertParseError(t, "foo zed 1428951394") 138 assertParseError(t, "foo 4394") 139 assertParseError(t, "foo 4384 1428951394 bar") 140 assertParseError(t, "foo 4384 1428951394 1428951394 bar") 141 } 142 143 func TestParsePacket(t *testing.T) { 144 mets, malformed := ParsePacket([]byte(` 145 foo.bar.zed 45565.02 1428951394 146 foo.bar.zed NaN 1428951394 147 foo.bar.invalid invalid 1428951394 148 149 foo.bar.invalid 1428951394`)) 150 require.Equal(t, 2, len(mets)) 151 require.Equal(t, 2, malformed) 152 } 153 154 func TestParseAndAppendPacket(t *testing.T) { 155 mets, malformed := ParseAndAppendPacket([]Metric{}, []byte(` 156 foo.bar.zed 45565.02 1428951394 157 foo.bar.zed NaN 1428951394 158 foo.bar.invalid invalid 1428951394 159 160 foo.bar.invalid 1428951394`)) 161 require.Equal(t, 2, len(mets)) 162 require.Equal(t, 2, malformed) 163 } 164 165 func TestCarbonToLine(t *testing.T) { 166 validateLine(t, "foo.bar.zed 45565.02 1428951394") 167 validateLine(t, "foo.bar.nan NaN 1428951395") 168 } 169 170 func TestParseValidatesUTF8Encoding(t *testing.T) { 171 start := "foo.bar.baz" 172 end := "some-other-foo-.bar.baz 4394 1428951394" 173 174 var b []byte 175 b = append(b, []byte(start)...) 176 b = append(b, []byte{byte(255), byte(253)}...) 177 b = append(b, []byte(end)...) 178 179 _, _, _, err := Parse(b) 180 if err == nil { 181 t.Fatalf("expected UTF8 error") 182 } 183 assert.Equal(t, "not valid UTF8 string", err.Error()) 184 validateLineError(t, string(b)) 185 } 186 187 func validateLine(t *testing.T, line string) { 188 mets, malformed := ParsePacket([]byte(line)) 189 require.Equal(t, 0, malformed) 190 require.Equal(t, line+"\n", mets[0].ToLine()) 191 } 192 193 func validateLineError(t *testing.T, line string) { 194 _, malformed := ParsePacket([]byte(line)) 195 require.Equal(t, 1, malformed) 196 } 197 198 func assertParseError(t *testing.T, metric string) { 199 _, _, _, err := Parse([]byte(metric)) 200 assert.NotNil(t, err, "allowed parsing of %s", metric) 201 }