whatcanGOwrong
This commit is contained in:
+85
@@ -0,0 +1,85 @@
|
||||
// Copyright 2023 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// The chartconfig package defines the ChartConfig type, representing telemetry
|
||||
// chart configuration, as well as utilities for parsing and validating this
|
||||
// configuration.
|
||||
//
|
||||
// Chart configuration defines the set of aggregations active on the telemetry
|
||||
// server, and are used to derive which data needs to be uploaded by users.
|
||||
// See the original blog post for more details:
|
||||
//
|
||||
// https://research.swtch.com/telemetry-design#configuration
|
||||
//
|
||||
// The record format defined in this package differs slightly from that of the
|
||||
// blog post. This format is still experimental, and subject to change.
|
||||
//
|
||||
// Configuration records consist of fields, comments, and whitespace. A field
|
||||
// is defined by a line starting with a valid key, followed immediately by ":",
|
||||
// and then a textual value, which cannot include the comment separator '#'.
|
||||
//
|
||||
// Comments start with '#', and extend to the end of the line.
|
||||
//
|
||||
// The following keys are supported. Any entry not marked as (optional) must be
|
||||
// provided.
|
||||
//
|
||||
// - title: the chart title.
|
||||
// - description: (optional) a longer description of the chart.
|
||||
// - issue: a go issue tracker URL proposing the chart configuration.
|
||||
// Multiple issues may be provided by including additional 'issue:' lines.
|
||||
// All proposals must be in the 'accepted' state.
|
||||
// - type: the chart type: currently only partition, histogram, and stack are
|
||||
// supported.
|
||||
// - program: the package path of the program for which this chart applies.
|
||||
// - version: (optional) the first version for which this chart applies. Must
|
||||
// be a valid semver value.
|
||||
// - counter: the primary counter this chart illustrates, including buckets
|
||||
// for histogram and partition charts.
|
||||
// - depth: (optional) stack counters only; the maximum stack depth to collect
|
||||
// - error: (optional) the desired error rate for this chart, which
|
||||
// determines collection rate
|
||||
//
|
||||
// Multiple records are separated by "---" lines.
|
||||
//
|
||||
// For example:
|
||||
//
|
||||
// # This config defines an ordinary counter.
|
||||
// counter: gopls/editor:{emacs,vim,vscode,other} # TODO(golang/go#34567): add more editors
|
||||
// title: Editor Distribution
|
||||
// description: measure editor distribution for gopls users.
|
||||
// type: partition
|
||||
// issue: https://go.dev/issue/12345
|
||||
// program: golang.org/x/tools/gopls
|
||||
// version: v1.0.0
|
||||
// version: [v2.0.0, v2.3.4]
|
||||
// version: [v3.0.0, ]
|
||||
//
|
||||
// ---
|
||||
//
|
||||
// # This config defines a stack counter.
|
||||
// counter: gopls/bug
|
||||
// title: Gopls bug reports.
|
||||
// description: Stacks of bugs encountered on the gopls server.
|
||||
// issue: https://go.dev/12345
|
||||
// issue: https://go.dev/23456 # increase stack depth
|
||||
// type: stack
|
||||
// program: golang.org/x/tools/gopls
|
||||
// depth: 10
|
||||
package chartconfig
|
||||
|
||||
// A ChartConfig defines the configuration for a single chart/collection on the
|
||||
// telemetry server.
|
||||
//
|
||||
// See the package documentation for field definitions.
|
||||
type ChartConfig struct {
|
||||
Title string
|
||||
Description string
|
||||
Issue []string
|
||||
Type string
|
||||
Program string
|
||||
Counter string
|
||||
Depth int
|
||||
Error float64 // TODO(rfindley) is Error still useful?
|
||||
Version string
|
||||
}
|
||||
+148
@@ -0,0 +1,148 @@
|
||||
# Note: these are approved chart configs, used to generate the upload config.
|
||||
# For the chart config file format, see chartconfig.go.
|
||||
|
||||
title: Editor Distribution
|
||||
counter: gopls/client:{vscode,vscodium,vscode-insiders,code-server,eglot,govim,neovim,coc.nvim,sublimetext,other}
|
||||
description: measure editor distribution for gopls users.
|
||||
type: partition
|
||||
issue: https://go.dev/issue/61038
|
||||
issue: https://go.dev/issue/62214 # add vscode-insiders
|
||||
program: golang.org/x/tools/gopls
|
||||
version: v0.13.0 # temporarily back-version to demonstrate config generation.
|
||||
---
|
||||
title: Go versions in use for gopls views
|
||||
counter: gopls/goversion:{1.16,1.17,1.18,1.19,1.20,1.21,1.22,1.23,1.24,1.25,1.26,1.27,1.28,1.29,1.30}
|
||||
description: measure go version usage distribution.
|
||||
type: partition
|
||||
issue: https://go.dev/issue/62248
|
||||
program: golang.org/x/tools/gopls
|
||||
version: v0.13.0
|
||||
---
|
||||
title: Number of bug report calls
|
||||
counter: gopls/bug
|
||||
description: count the bugs reported through gopls/internal/bug APIs.
|
||||
type: stack
|
||||
issue: https://go.dev/issue/62249
|
||||
program: golang.org/x/tools/gopls
|
||||
depth: 16
|
||||
version: v0.13.0
|
||||
---
|
||||
counter: crash/crash
|
||||
title: Unexpected Go crashes
|
||||
description: stacks of goroutines running when the Go program crashed
|
||||
type: stack
|
||||
issue: https://go.dev/issue/65696
|
||||
program: golang.org/x/tools/gopls
|
||||
depth: 16
|
||||
version: v0.15.0
|
||||
---
|
||||
counter: crash/malformed
|
||||
title: Failure to parse runtime crash output
|
||||
description: count of runtime crash messages that failed to parse
|
||||
type: partition
|
||||
issue: https://go.dev/issue/65696
|
||||
program: golang.org/x/tools/gopls
|
||||
version: v0.15.0
|
||||
---
|
||||
counter: crash/no-running-goroutine
|
||||
title: Failure to identify any running goroutine in the crash output
|
||||
description: count of runtime crash messages that don't have a running goroutine (e.g. deadlock)
|
||||
type: partition
|
||||
issue: https://go.dev/issue/65696
|
||||
program: golang.org/x/tools/gopls
|
||||
version: v0.15.0
|
||||
---
|
||||
counter: go/invocations
|
||||
title: cmd/go invocations
|
||||
description: Number of invocations of the go command
|
||||
type: partition
|
||||
issue: https://go.dev/issue/67244
|
||||
program: cmd/go
|
||||
version: go1.23rc1
|
||||
---
|
||||
counter: go/build/flag:{
|
||||
buildmode
|
||||
}
|
||||
title: cmd/go flags
|
||||
description: Flag names of flags provided to the go command
|
||||
type: partition
|
||||
issue: https://go.dev/issue/67244
|
||||
program: cmd/go
|
||||
version: go1.23rc1
|
||||
---
|
||||
counter: go/build/flag/buildmode:{
|
||||
archive,
|
||||
c-archive,
|
||||
c-shared,
|
||||
default,
|
||||
exe,
|
||||
pie,
|
||||
shared,
|
||||
plugin
|
||||
}
|
||||
title: cmd/go buildmode values
|
||||
description: Buildmode values for the go command
|
||||
type: partition
|
||||
issue: https://go.dev/issue/67244
|
||||
program: cmd/go
|
||||
version: go1.23rc1
|
||||
---
|
||||
counter: compile/invocations
|
||||
title: cmd/compile invocations
|
||||
description: Number of invocations of the go compiler
|
||||
type: partition
|
||||
issue: https://go.dev/issue/67244
|
||||
program: cmd/compile
|
||||
version: go1.23rc1
|
||||
---
|
||||
title: Compiler bug report calls
|
||||
counter: compile/bug
|
||||
description: count stacks for cases where cmd/compile has a fatal error
|
||||
type: stack
|
||||
issue: https://go.dev/issue/67244
|
||||
program: cmd/compile
|
||||
depth: 16
|
||||
version: go1.23rc1
|
||||
---
|
||||
counter: govulncheck/scan:{symbol,package,module}
|
||||
title: Scan Level Distribution
|
||||
description: measure govulncheck scan level distribution
|
||||
type: partition
|
||||
issue: https://go.dev/issue/67678
|
||||
program: golang.org/x/vuln/cmd/govulncheck
|
||||
---
|
||||
counter: govulncheck/mode:{source,binary,extract,query,convert}
|
||||
title: Scan Mode Distribution
|
||||
description: measure govulncheck scan mode distribution
|
||||
type: partition
|
||||
issue: https://go.dev/issue/67678
|
||||
program: golang.org/x/vuln/cmd/govulncheck
|
||||
---
|
||||
counter: govulncheck/format:{text,json,sarif,openvex}
|
||||
title: Output Format Distribution
|
||||
description: measure govulncheck output format distribution
|
||||
type: partition
|
||||
issue: https://go.dev/issue/67678
|
||||
program: golang.org/x/vuln/cmd/govulncheck
|
||||
---
|
||||
counter: govulncheck/show:{none,traces,color,verbose,version}
|
||||
title: Show Options Distribution
|
||||
description: measure govulncheck show flag distribution
|
||||
type: partition
|
||||
issue: https://go.dev/issue/67678
|
||||
program: golang.org/x/vuln/cmd/govulncheck
|
||||
---
|
||||
counter: govulncheck/assumptions:{multi-patterns,no-binary-platform,no-relative-path,no-go-root,local-replace,unknown-pkg-mod-path}
|
||||
title: Code Invariants Distribution
|
||||
description: measure distribution of failed govulncheck internal assumptions
|
||||
type: partition
|
||||
issue: https://go.dev/issue/67678
|
||||
program: golang.org/x/vuln/cmd/govulncheck
|
||||
---
|
||||
counter: gopls/gotoolchain:{auto,path,local,other}
|
||||
title: GOTOOLCHAIN types used with gopls
|
||||
description: measure the types of GOTOOLCHAIN values used with gopls
|
||||
type: partition
|
||||
issue: https://go.dev/issue/68771
|
||||
program: golang.org/x/tools/gopls
|
||||
version: v0.16.0
|
||||
+219
@@ -0,0 +1,219 @@
|
||||
// Copyright 2023 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package chartconfig
|
||||
|
||||
import (
|
||||
_ "embed"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
//go:embed config.txt
|
||||
var chartConfig []byte
|
||||
|
||||
func Raw() []byte {
|
||||
return chartConfig
|
||||
}
|
||||
|
||||
// Load loads and parses the current chart config.
|
||||
func Load() ([]ChartConfig, error) {
|
||||
return Parse(chartConfig)
|
||||
}
|
||||
|
||||
// Parse parses ChartConfig records from the provided raw data, returning an
|
||||
// error if the config has invalid syntax. See the package documentation for a
|
||||
// description of the record syntax.
|
||||
//
|
||||
// Even with correct syntax, the resulting chart config may not meet all the
|
||||
// requirements described in the package doc. Call [Validate] to check whether
|
||||
// the config data is coherent.
|
||||
func Parse(data []byte) ([]ChartConfig, error) {
|
||||
// Collect field information for the record type.
|
||||
var (
|
||||
prefixes []string // for parse errors
|
||||
fields = make(map[string]reflect.StructField) // key -> struct field
|
||||
)
|
||||
{
|
||||
typ := reflect.TypeOf(ChartConfig{})
|
||||
for i := 0; i < typ.NumField(); i++ {
|
||||
f := typ.Field(i)
|
||||
key := strings.ToLower(f.Name)
|
||||
if _, ok := fieldParsers[key]; !ok {
|
||||
panic(fmt.Sprintf("no parser for field %q", f.Name))
|
||||
}
|
||||
prefixes = append(prefixes, "'"+key+":'")
|
||||
fields[key] = f
|
||||
}
|
||||
sort.Strings(prefixes)
|
||||
}
|
||||
|
||||
// Read records, separated by '---'
|
||||
var (
|
||||
records []ChartConfig
|
||||
inProgress = new(ChartConfig) // record value currently being parsed
|
||||
set = make(map[string]bool) // fields that are set so far; empty records are skipped
|
||||
)
|
||||
flushRecord := func() {
|
||||
if len(set) > 0 { // only flush non-empty records
|
||||
records = append(records, *inProgress)
|
||||
}
|
||||
inProgress = new(ChartConfig)
|
||||
set = make(map[string]bool)
|
||||
}
|
||||
|
||||
// Within bucket braces in counter fields, newlines are ignored.
|
||||
// if we're in the middle of a multiline counter field, accumulatedCounterText
|
||||
// contains the joined lines of the field up to the current line. Once
|
||||
// a line containing an end brace is reached, line will be set to the
|
||||
// joined lines of accumulatedCounterText and processed as a single line.
|
||||
var accumulatedCounterText string
|
||||
|
||||
for lineNum, line := range strings.Split(string(data), "\n") {
|
||||
if line == "---" {
|
||||
if accumulatedCounterText != "" {
|
||||
return nil, fmt.Errorf("line %d: reached end of record while processing multiline counter field", lineNum)
|
||||
}
|
||||
flushRecord()
|
||||
continue
|
||||
}
|
||||
text, _, _ := strings.Cut(line, "#") // trim comments
|
||||
|
||||
// Processing of counter fields which can appear across multiple lines.
|
||||
// See comment on accumulatedCounterText.
|
||||
if accumulatedCounterText == "" {
|
||||
if oi := strings.Index(text, "{"); oi >= 0 {
|
||||
if strings.Contains(text[:oi], "}") {
|
||||
return nil, fmt.Errorf("line %d: invalid line %q: unexpected '}'", lineNum, line)
|
||||
}
|
||||
if strings.Contains(text[oi+len("{"):], "{") {
|
||||
return nil, fmt.Errorf("line %d: invalid line %q: unexpected '{'", lineNum, line)
|
||||
}
|
||||
if !strings.HasPrefix(text, "counter:") {
|
||||
return nil, fmt.Errorf("line %d: invalid line %q: '{' is only allowed to appear within a counter field", lineNum, line)
|
||||
}
|
||||
accumulatedCounterText = strings.TrimRightFunc(text, unicode.IsSpace)
|
||||
// Don't continue here. If the counter field is a single line
|
||||
// the check for the close brace below will close the line
|
||||
// and process it as text. Set text to "" so when it's appended to
|
||||
// accumulatedCounterText we don't add the line twice.
|
||||
text = ""
|
||||
} else if strings.Contains(text, "}") {
|
||||
return nil, fmt.Errorf("line %d: invalid line %q: unexpected '}'", lineNum, line)
|
||||
}
|
||||
}
|
||||
if accumulatedCounterText != "" {
|
||||
if strings.Contains(text, "{") {
|
||||
return nil, fmt.Errorf("line %d: invalid line %q: '{' is only allowed to appear once within a counter field", lineNum, line)
|
||||
}
|
||||
accumulatedCounterText += strings.TrimSpace(text)
|
||||
if ci := strings.Index(accumulatedCounterText, "}"); ci >= 0 {
|
||||
if strings.Contains(accumulatedCounterText[ci+len("}"):], "}") {
|
||||
return nil, fmt.Errorf("line %d: invalid line %q: unexpected '}'", lineNum, line)
|
||||
}
|
||||
if ci > 0 && strings.HasSuffix(accumulatedCounterText[:ci], ",") {
|
||||
return nil, fmt.Errorf("line %d: invalid line %q: unexpected '}' after ','", lineNum, line)
|
||||
}
|
||||
text = accumulatedCounterText
|
||||
accumulatedCounterText = ""
|
||||
} else {
|
||||
// We're in the middle of a multiline counter field. Continue
|
||||
// processing.
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
var key string
|
||||
for k := range fields {
|
||||
prefix := k + ":"
|
||||
if strings.HasPrefix(text, prefix) {
|
||||
key = k
|
||||
text = text[len(prefix):]
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
text = strings.TrimSpace(text)
|
||||
if text == "" {
|
||||
// Check for empty lines before the field == nil check below.
|
||||
// Lines consisting only of whitespace and comments are OK.
|
||||
continue
|
||||
}
|
||||
if key == "" {
|
||||
return nil, fmt.Errorf("line %d: invalid line %q: lines must be '---', consist only of whitespace/comments, or start with %s", lineNum, line, strings.Join(prefixes, ", "))
|
||||
}
|
||||
field := fields[key]
|
||||
v := reflect.ValueOf(inProgress).Elem().FieldByName(field.Name)
|
||||
if set[key] && field.Type.Kind() != reflect.Slice {
|
||||
return nil, fmt.Errorf("line %d: field %s may not be repeated", lineNum, strings.ToLower(field.Name))
|
||||
}
|
||||
parser := fieldParsers[key]
|
||||
if err := parser(v, text); err != nil {
|
||||
return nil, fmt.Errorf("line %d: field %q: %v", lineNum, field.Name, err)
|
||||
}
|
||||
set[key] = true
|
||||
}
|
||||
|
||||
if accumulatedCounterText != "" {
|
||||
return nil, fmt.Errorf("reached end of file while processing multiline counter field")
|
||||
}
|
||||
|
||||
flushRecord()
|
||||
return records, nil
|
||||
}
|
||||
|
||||
// A fieldParser parses the provided input and writes to v, which must be
|
||||
// addressable.
|
||||
type fieldParser func(v reflect.Value, input string) error
|
||||
|
||||
var fieldParsers = map[string]fieldParser{
|
||||
"title": parseString,
|
||||
"description": parseString,
|
||||
"issue": parseSlice(parseString),
|
||||
"type": parseString,
|
||||
"program": parseString,
|
||||
"counter": parseString,
|
||||
"depth": parseInt,
|
||||
"error": parseFloat,
|
||||
"version": parseString,
|
||||
}
|
||||
|
||||
func parseString(v reflect.Value, input string) error {
|
||||
v.SetString(input)
|
||||
return nil
|
||||
}
|
||||
|
||||
func parseInt(v reflect.Value, input string) error {
|
||||
i, err := strconv.ParseInt(input, 10, 64)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid int value %q", input)
|
||||
}
|
||||
v.SetInt(i)
|
||||
return nil
|
||||
}
|
||||
|
||||
func parseFloat(v reflect.Value, input string) error {
|
||||
f, err := strconv.ParseFloat(input, 64)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid float value %q", input)
|
||||
}
|
||||
v.SetFloat(f)
|
||||
return nil
|
||||
}
|
||||
|
||||
func parseSlice(elemParser fieldParser) fieldParser {
|
||||
return func(v reflect.Value, input string) error {
|
||||
elem := reflect.New(v.Type().Elem()).Elem()
|
||||
v.Set(reflect.Append(v, elem))
|
||||
elem = v.Index(v.Len() - 1)
|
||||
if err := elemParser(elem, input); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
+268
@@ -0,0 +1,268 @@
|
||||
// Copyright 2023 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package chartconfig_test
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"golang.org/x/telemetry/internal/chartconfig"
|
||||
)
|
||||
|
||||
func TestLoad(t *testing.T) {
|
||||
// Test that we can actually load the chart config.
|
||||
if _, err := chartconfig.Load(); err != nil {
|
||||
t.Errorf("Load() failed: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParse(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
input string
|
||||
want []chartconfig.ChartConfig
|
||||
}{
|
||||
{"empty", "", nil},
|
||||
{"single field", "title: A", []chartconfig.ChartConfig{{Title: "A"}}},
|
||||
{
|
||||
"basic", `
|
||||
title: A
|
||||
description: B
|
||||
type: C
|
||||
program: D
|
||||
counter: E
|
||||
issue: F1
|
||||
issue: F2
|
||||
depth: 2
|
||||
error: 0.1
|
||||
version: v2.0.0
|
||||
`,
|
||||
[]chartconfig.ChartConfig{{
|
||||
Title: "A",
|
||||
Description: "B",
|
||||
Type: "C",
|
||||
Program: "D",
|
||||
Counter: "E",
|
||||
Issue: []string{"F1", "F2"},
|
||||
Depth: 2,
|
||||
Error: 0.1,
|
||||
Version: "v2.0.0",
|
||||
}},
|
||||
},
|
||||
{
|
||||
"partial", `
|
||||
title: A
|
||||
description: B
|
||||
`,
|
||||
[]chartconfig.ChartConfig{
|
||||
{Title: "A", Description: "B"},
|
||||
},
|
||||
},
|
||||
{
|
||||
"comments and whitespace", `
|
||||
# A comment
|
||||
title: A # a line comment
|
||||
|
||||
# Another comment
|
||||
|
||||
description: B
|
||||
|
||||
|
||||
`,
|
||||
[]chartconfig.ChartConfig{
|
||||
{Title: "A", Description: "B"},
|
||||
},
|
||||
},
|
||||
{
|
||||
"multi-record", `
|
||||
# Empty records are skipped
|
||||
---
|
||||
title: A
|
||||
description: B
|
||||
|
||||
---
|
||||
|
||||
title: C
|
||||
description: D
|
||||
`,
|
||||
[]chartconfig.ChartConfig{
|
||||
{Title: "A", Description: "B"},
|
||||
{Title: "C", Description: "D"},
|
||||
},
|
||||
},
|
||||
{
|
||||
"example", `
|
||||
title: Editor Distribution
|
||||
counter: gopls/editor:{emacs,vim,vscode,other}
|
||||
description: measure editor distribution for gopls users.
|
||||
type: partition
|
||||
issue: TBD
|
||||
program: golang.org/x/tools/gopls
|
||||
`,
|
||||
[]chartconfig.ChartConfig{
|
||||
{
|
||||
Title: "Editor Distribution",
|
||||
Description: "measure editor distribution for gopls users.",
|
||||
Counter: "gopls/editor:{emacs,vim,vscode,other}",
|
||||
Type: "partition",
|
||||
Issue: []string{"TBD"},
|
||||
Program: "golang.org/x/tools/gopls",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
"multiline counter field", `
|
||||
counter: foo:{
|
||||
bar,
|
||||
baz
|
||||
}
|
||||
`,
|
||||
[]chartconfig.ChartConfig{
|
||||
{Counter: "foo:{bar,baz}"},
|
||||
},
|
||||
},
|
||||
{
|
||||
"multiline counter field with braces immediately next to text", `
|
||||
counter: foo:{bar,
|
||||
baz}
|
||||
`,
|
||||
[]chartconfig.ChartConfig{
|
||||
{Counter: "foo:{bar,baz}"},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
got, err := chartconfig.Parse([]byte(test.input))
|
||||
if err != nil {
|
||||
t.Fatalf("Parse(...) failed: %v", err)
|
||||
}
|
||||
if len(got) != len(test.want) {
|
||||
t.Fatalf("Parse(...) returned %d records, want %d", len(got), len(test.want))
|
||||
}
|
||||
for i, got := range got {
|
||||
want := test.want[i]
|
||||
if !reflect.DeepEqual(got, want) {
|
||||
t.Errorf("Parse(...): record %d = %#v, want %#v", i, got, want)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseErrors(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
input string
|
||||
}{
|
||||
{
|
||||
"leading space",
|
||||
`
|
||||
title: foo
|
||||
`,
|
||||
},
|
||||
{
|
||||
"unknown key",
|
||||
`
|
||||
foo: bar
|
||||
`,
|
||||
},
|
||||
{
|
||||
"bad separator",
|
||||
`
|
||||
title: foo
|
||||
--- # comments aren't allowed after separators
|
||||
title: bar
|
||||
`,
|
||||
},
|
||||
{
|
||||
"invalid depth",
|
||||
`
|
||||
depth: notanint
|
||||
`,
|
||||
},
|
||||
{
|
||||
"open curly brace not in counter field",
|
||||
`
|
||||
title: {
|
||||
`,
|
||||
},
|
||||
{
|
||||
"close curly brace not in counter field",
|
||||
`
|
||||
title: }
|
||||
`,
|
||||
},
|
||||
{
|
||||
"end of record within multiline counter field",
|
||||
`
|
||||
counter: foo{
|
||||
bar
|
||||
---
|
||||
title: baz
|
||||
`,
|
||||
},
|
||||
{
|
||||
"end of file within multiline counter field",
|
||||
`
|
||||
counter: foo{
|
||||
bar
|
||||
`,
|
||||
},
|
||||
{
|
||||
"close curly before open curly",
|
||||
`
|
||||
counter: }foo{
|
||||
bar
|
||||
}`,
|
||||
},
|
||||
{
|
||||
"open curly after close curly",
|
||||
`
|
||||
counter: foo{
|
||||
bar
|
||||
} {`,
|
||||
},
|
||||
{
|
||||
"open curly after open curly same line",
|
||||
`
|
||||
counter: foo{{
|
||||
bar
|
||||
}`,
|
||||
},
|
||||
{
|
||||
"open curly after open curly different line",
|
||||
`
|
||||
counter: foo{
|
||||
{bar
|
||||
}`,
|
||||
},
|
||||
{
|
||||
"close curly after close curly",
|
||||
`
|
||||
counter: foo{
|
||||
bar
|
||||
} }`,
|
||||
},
|
||||
{
|
||||
"comma right before close curly",
|
||||
`
|
||||
counter: foo{
|
||||
bar,
|
||||
baz,
|
||||
} }`,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
_, err := chartconfig.Parse([]byte(test.input))
|
||||
if err == nil {
|
||||
t.Fatalf("Parse(...) succeeded unexpectedly")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user