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  }