Warning and Error consolidation CLI options (#1894)

Signed-off-by: Hefeweizen <jmales@gmail.com>
Signed-off-by: Christian Mesh <christianmesh1@gmail.com>
Signed-off-by: Syasusu <syasusu@163.com>
Co-authored-by: Hefeweizen <jmales@gmail.com>
Co-authored-by: Syasusu <syasusu@163.com>
This commit is contained in:
Christian Mesh 2024-09-23 07:31:06 -04:00 committed by GitHub
parent bc166ce498
commit 12ed264597
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
16 changed files with 308 additions and 108 deletions

View File

@ -356,6 +356,14 @@ Options:
accompanied by errors, show them in a more compact
form that includes only the summary messages.
-consolidate-warnings If OpenTofu produces any warnings, no consolodation
will be performed. All locations, for all warnings
will be listed. Enabled by default.
-consolidate-errors If OpenTofu produces any errors, no consolodation
will be performed. All locations, for all errors
will be listed. Disabled by default
-destroy Destroy OpenTofu-managed infrastructure.
The command "tofu destroy" is a convenience alias
for this option.

View File

@ -14,7 +14,9 @@ type View struct {
// CompactWarnings is used to coalesce duplicate warnings, to reduce the
// level of noise when multiple instances of the same warning are raised
// for a configuration.
CompactWarnings bool
CompactWarnings bool
ConsolidateWarnings bool
ConsolidateErrors bool
// Concise is used to reduce the level of noise in the output and display
// only the important details.
@ -28,7 +30,9 @@ type View struct {
// possibly-modified slice of arguments. If any of the supported flags are
// found, they will be removed from the slice.
func ParseView(args []string) (*View, []string) {
common := &View{}
common := &View{
ConsolidateWarnings: true,
}
// Keep track of the length of the returned slice. When we find an
// argument we support, i will not be incremented.
@ -39,6 +43,18 @@ func ParseView(args []string) (*View, []string) {
common.NoColor = true
case "-compact-warnings":
common.CompactWarnings = true
case "-consolidate-warnings":
common.ConsolidateWarnings = true
case "-consolidate-warnings=true":
common.ConsolidateWarnings = true
case "-consolidate-warnings=false":
common.ConsolidateWarnings = false
case "-consolidate-errors":
common.ConsolidateErrors = true
case "-consolidate-errors=true":
common.ConsolidateErrors = true
case "-consolidate-errors=false":
common.ConsolidateErrors = false
case "-concise":
common.Concise = true
default:

View File

@ -19,57 +19,62 @@ func TestParseView(t *testing.T) {
}{
"nil": {
nil,
&View{NoColor: false, CompactWarnings: false, Concise: false},
&View{NoColor: false, CompactWarnings: false, ConsolidateWarnings: true, Concise: false},
nil,
},
"empty": {
[]string{},
&View{NoColor: false, CompactWarnings: false, Concise: false},
&View{NoColor: false, CompactWarnings: false, ConsolidateWarnings: true, Concise: false},
[]string{},
},
"none matching": {
[]string{"-foo", "bar", "-baz"},
&View{NoColor: false, CompactWarnings: false, Concise: false},
&View{NoColor: false, CompactWarnings: false, ConsolidateWarnings: true, Concise: false},
[]string{"-foo", "bar", "-baz"},
},
"no-color": {
[]string{"-foo", "-no-color", "-baz"},
&View{NoColor: true, CompactWarnings: false, Concise: false},
&View{NoColor: true, CompactWarnings: false, ConsolidateWarnings: true, Concise: false},
[]string{"-foo", "-baz"},
},
"compact-warnings": {
[]string{"-foo", "-compact-warnings", "-baz"},
&View{NoColor: false, CompactWarnings: true, Concise: false},
&View{NoColor: false, CompactWarnings: true, ConsolidateWarnings: true, Concise: false},
[]string{"-foo", "-baz"},
},
"concise": {
[]string{"-foo", "-concise", "-baz"},
&View{NoColor: false, CompactWarnings: false, Concise: true},
&View{NoColor: false, CompactWarnings: false, ConsolidateWarnings: true, Concise: true},
[]string{"-foo", "-baz"},
},
"no-color and compact-warnings": {
[]string{"-foo", "-no-color", "-compact-warnings", "-baz"},
&View{NoColor: true, CompactWarnings: true, Concise: false},
&View{NoColor: true, CompactWarnings: true, ConsolidateWarnings: true, Concise: false},
[]string{"-foo", "-baz"},
},
"no-color and concise": {
[]string{"-foo", "-no-color", "-concise", "-baz"},
&View{NoColor: true, CompactWarnings: false, Concise: true},
&View{NoColor: true, CompactWarnings: false, ConsolidateWarnings: true, Concise: true},
[]string{"-foo", "-baz"},
},
"concise and compact-warnings": {
[]string{"-foo", "-concise", "-compact-warnings", "-baz"},
&View{NoColor: false, CompactWarnings: true, Concise: true},
&View{NoColor: false, CompactWarnings: true, ConsolidateWarnings: true, Concise: true},
[]string{"-foo", "-baz"},
},
"all three": {
[]string{"-foo", "-no-color", "-compact-warnings", "-concise", "-baz"},
&View{NoColor: true, CompactWarnings: true, Concise: true},
&View{NoColor: true, CompactWarnings: true, ConsolidateWarnings: true, Concise: true},
[]string{"-foo", "-baz"},
},
"all three, resulting in empty args": {
[]string{"-no-color", "-compact-warnings", "-concise"},
&View{NoColor: true, CompactWarnings: true, Concise: true},
&View{NoColor: true, CompactWarnings: true, ConsolidateWarnings: true, Concise: true},
[]string{},
},
"turn off warning consolidation": {
[]string{"-consolidate-warnings=false"},
&View{NoColor: false, CompactWarnings: false, ConsolidateWarnings: false, Concise: false},
[]string{},
},
}

View File

@ -223,15 +223,27 @@ Usage: tofu [global options] console [options]
Options:
-state=path Legacy option for the local backend only. See the local
backend's documentation for more information.
-compact-warnings If OpenTofu produces any warnings that are not
accompanied by errors, show them in a more compact
form that includes only the summary messages.
-var 'foo=bar' Set a variable in the OpenTofu configuration. This
flag can be set multiple times.
-consolidate-warnings If OpenTofu produces any warnings, no consolodation
will be performed. All locations, for all warnings
will be listed. Enabled by default.
-var-file=foo Set variables in the OpenTofu configuration from
a file. If "terraform.tfvars" or any ".auto.tfvars"
files are present, they will be automatically loaded.
-consolidate-errors If OpenTofu produces any errors, no consolodation
will be performed. All locations, for all errors
will be listed. Disabled by default
-state=path Legacy option for the local backend only. See the local
backend's documentation for more information.
-var 'foo=bar' Set a variable in the OpenTofu configuration. This
flag can be set multiple times.
-var-file=foo Set variables in the OpenTofu configuration from
a file. If "terraform.tfvars" or any ".auto.tfvars"
files are present, they will be automatically loaded.
`
return strings.TrimSpace(helpText)
}

View File

@ -187,7 +187,7 @@ func DiagnosticWarningsCompact(diags tfdiags.Diagnostics, color *colorstring.Col
var b strings.Builder
b.WriteString(color.Color("[bold][yellow]Warnings:[reset]\n\n"))
for _, diag := range diags {
sources := tfdiags.WarningGroupSourceRanges(diag)
sources := tfdiags.ConsolidatedGroupSourceRanges(diag)
b.WriteString(fmt.Sprintf("- %s\n", diag.Description().Summary))
if len(sources) > 0 {
mainSource := sources[0]

View File

@ -634,7 +634,7 @@ func TestDiagnosticWarningsCompact(t *testing.T) {
// ConsolidateWarnings groups together the ones
// that have source location information and that
// have the same summary text.
diags = diags.ConsolidateWarnings(1)
diags = diags.Consolidate(1, tfdiags.Warning)
// A zero-value Colorize just passes all the formatting
// codes back to us, so we can test them literally.

View File

@ -316,6 +316,18 @@ Usage: tofu [global options] import [options] ADDR ID
Options:
-compact-warnings If OpenTofu produces any warnings that are not
accompanied by errors, show them in a more compact
form that includes only the summary messages.
-consolidate-warnings If OpenTofu produces any warnings, no consolodation
will be performed. All locations, for all warnings
will be listed. Enabled by default.
-consolidate-errors If OpenTofu produces any errors, no consolodation
will be performed. All locations, for all errors
will be listed. Disabled by default
-config=path Path to a directory of OpenTofu configuration files
to use to configure the provider. Defaults to pwd.
If no config files are present, they must be provided

View File

@ -1193,6 +1193,18 @@ Options:
times. The backend type must be in the configuration
itself.
-compact-warnings If OpenTofu produces any warnings that are not
accompanied by errors, show them in a more compact
form that includes only the summary messages.
-consolidate-warnings If OpenTofu produces any warnings, no consolodation
will be performed. All locations, for all warnings
will be listed. Enabled by default.
-consolidate-errors If OpenTofu produces any errors, no consolodation
will be performed. All locations, for all errors
will be listed. Disabled by default
-force-copy Suppress prompts about copying state data when
initializing a new state backend. This is
equivalent to providing a "yes" to all confirmation

View File

@ -252,16 +252,24 @@ type Meta struct {
//
// compactWarnings (-compact-warnings) selects a more compact presentation
// of warnings in the output when they are not accompanied by errors.
statePath string
stateOutPath string
backupPath string
parallelism int
stateLock bool
stateLockTimeout time.Duration
forceInitCopy bool
reconfigure bool
migrateState bool
compactWarnings bool
//
// consolidateWarnings (-consolidate-warnings=false) disables consolodation
// of warnings in the output, printing all instances of a particular warning.
//
// consolidateErrors (-consolidate-errors=true) enables consolodation
// of errors in the output, printing a single instances of a particular warning.
statePath string
stateOutPath string
backupPath string
parallelism int
stateLock bool
stateLockTimeout time.Duration
forceInitCopy bool
reconfigure bool
migrateState bool
compactWarnings bool
consolidateWarnings bool
consolidateErrors bool
// Used with commands which write state to allow users to write remote
// state even if the remote and local OpenTofu versions don't match.
@ -611,6 +619,8 @@ func (m *Meta) extendedFlagSet(n string) *flag.FlagSet {
f.BoolVar(&m.input, "input", true, "input")
f.Var((*FlagStringSlice)(&m.targetFlags), "target", "resource to target")
f.BoolVar(&m.compactWarnings, "compact-warnings", false, "use compact warnings")
f.BoolVar(&m.consolidateWarnings, "consolidate-warnings", true, "consolidate warnings")
f.BoolVar(&m.consolidateErrors, "consolidate-errors", false, "consolidate errors")
m.varFlagSet(f)
@ -661,8 +671,10 @@ func (m *Meta) process(args []string) []string {
// views.View and cli.Ui during the migration phase.
if m.View != nil {
m.View.Configure(&arguments.View{
CompactWarnings: m.compactWarnings,
NoColor: !m.Color,
CompactWarnings: m.compactWarnings,
ConsolidateWarnings: m.consolidateWarnings,
ConsolidateErrors: m.consolidateErrors,
NoColor: !m.Color,
})
}
@ -707,6 +719,7 @@ func (m *Meta) confirm(opts *tofu.InputOpts) (bool, error) {
// Internally this function uses Diagnostics.Append, and so it will panic
// if given unsupported value types, just as Append does.
func (m *Meta) showDiagnostics(vals ...interface{}) {
var diags tfdiags.Diagnostics
diags = diags.Append(vals...)
diags.Sort()
@ -723,7 +736,12 @@ func (m *Meta) showDiagnostics(vals ...interface{}) {
outputWidth := m.ErrorColumns()
diags = diags.ConsolidateWarnings(1)
if m.consolidateWarnings {
diags = diags.Consolidate(1, tfdiags.Warning)
}
if m.consolidateErrors {
diags = diags.Consolidate(1, tfdiags.Error)
}
// Since warning messages are generally competing
if m.compactWarnings {

View File

@ -255,6 +255,14 @@ Other Options:
accompanied by errors, shows them in a more compact
form that includes only the summary messages.
-consolidate-warnings If OpenTofu produces any warnings, no consolodation
will be performed. All locations, for all warnings
will be listed. Enabled by default.
-consolidate-errors If OpenTofu produces any errors, no consolodation
will be performed. All locations, for all errors
will be listed. Disabled by default
-detailed-exitcode Return detailed exit codes when the command exits.
This will change the meaning of exit codes to:
0 - Succeeded, diff is empty (no changes)

View File

@ -193,32 +193,40 @@ Usage: tofu [global options] refresh [options]
Options:
-compact-warnings If OpenTofu produces any warnings that are not
accompanied by errors, show them in a more compact form
that includes only the summary messages.
-compact-warnings If OpenTofu produces any warnings that are not
accompanied by errors, show them in a more compact form
that includes only the summary messages.
-input=true Ask for input for variables if not directly set.
-consolidate-warnings If OpenTofu produces any warnings, no consolodation
will be performed. All locations, for all warnings
will be listed. Enabled by default.
-lock=false Don't hold a state lock during the operation. This is
dangerous if others might concurrently run commands
against the same workspace.
-consolidate-errors If OpenTofu produces any errors, no consolodation
will be performed. All locations, for all errors
will be listed. Disabled by default
-lock-timeout=0s Duration to retry a state lock.
-input=true Ask for input for variables if not directly set.
-no-color If specified, output won't contain any color.
-lock=false Don't hold a state lock during the operation. This is
dangerous if others might concurrently run commands
against the same workspace.
-parallelism=n Limit the number of concurrent operations. Defaults to 10.
-lock-timeout=0s Duration to retry a state lock.
-target=resource Resource to target. Operation will be limited to this
resource and its dependencies. This flag can be used
multiple times.
-no-color If specified, output won't contain any color.
-var 'foo=bar' Set a variable in the OpenTofu configuration. This
flag can be set multiple times.
-parallelism=n Limit the number of concurrent operations. Defaults to 10.
-var-file=foo Set variables in the OpenTofu configuration from
a file. If "terraform.tfvars" or any ".auto.tfvars"
files are present, they will be automatically loaded.
-target=resource Resource to target. Operation will be limited to this
resource and its dependencies. This flag can be used
multiple times.
-var 'foo=bar' Set a variable in the OpenTofu configuration. This
flag can be set multiple times.
-var-file=foo Set variables in the OpenTofu configuration from
a file. If "terraform.tfvars" or any ".auto.tfvars"
files are present, they will be automatically loaded.
-state, state-out, and -backup are legacy options supported for the local
backend only. For more information, see the local backend's documentation.

View File

@ -58,6 +58,18 @@ Usage: tofu [global options] test [options]
Options:
-compact-warnings If OpenTofu produces any warnings that are not
accompanied by errors, show them in a more compact
form that includes only the summary messages.
-consolidate-warnings If OpenTofu produces any warnings, no consolodation
will be performed. All locations, for all warnings
will be listed. Enabled by default.
-consolidate-errors If OpenTofu produces any errors, no consolodation
will be performed. All locations, for all errors
will be listed. Disabled by default
-filter=testfile If specified, OpenTofu will only execute the test files
specified by this flag. You can use this option multiple
times to execute more than one test file.

View File

@ -196,6 +196,18 @@ Usage: tofu [global options] validate [options]
Options:
-compact-warnings If OpenTofu produces any warnings that are not
accompanied by errors, show them in a more compact
form that includes only the summary messages.
-consolidate-warnings If OpenTofu produces any warnings, no consolodation
will be performed. All locations, for all warnings
will be listed. Enabled by default.
-consolidate-errors If OpenTofu produces any errors, no consolodation
will be performed. All locations, for all errors
will be listed. Disabled by default
-json Produce output in a machine-readable JSON format,
suitable for use in text editor integrations and other
automated systems. Always disables color.

View File

@ -21,7 +21,9 @@ type View struct {
streams *terminal.Streams
colorize *colorstring.Colorize
compactWarnings bool
compactWarnings bool
consolidateWarnings bool
consolidateErrors bool
// When this is true it's a hint that OpenTofu is being run indirectly
// via a wrapper script or other automation and so we may wish to replace
@ -78,6 +80,8 @@ func (v *View) RunningInAutomation() bool {
func (v *View) Configure(view *arguments.View) {
v.colorize.Disable = view.NoColor
v.compactWarnings = view.CompactWarnings
v.consolidateWarnings = view.ConsolidateWarnings
v.consolidateErrors = view.ConsolidateErrors
v.concise = view.Concise
}
@ -96,7 +100,12 @@ func (v *View) Diagnostics(diags tfdiags.Diagnostics) {
return
}
diags = diags.ConsolidateWarnings(1)
if v.consolidateWarnings {
diags = diags.Consolidate(1, tfdiags.Warning)
}
if v.consolidateErrors {
diags = diags.Consolidate(1, tfdiags.Error)
}
// Since warning messages are generally competing
if v.compactWarnings {

View File

@ -7,13 +7,13 @@ package tfdiags
import "fmt"
// ConsolidateWarnings checks if there is an unreasonable amount of warnings
// Consolidate checks if there is an unreasonable amount of diagnostics
// with the same summary in the receiver and, if so, returns a new diagnostics
// with some of those warnings consolidated into a single warning in order
// with some of those diagnostics consolidated into a single diagnostic in order
// to reduce the verbosity of the output.
//
// This mechanism is here primarily for diagnostics printed out at the CLI. In
// other contexts it is likely better to just return the warnings directly,
// other contexts it is likely better to just return the diagnostic directly,
// particularly if they are going to be interpreted by software rather than
// by a human reader.
//
@ -21,28 +21,28 @@ import "fmt"
// but some diagnostic values themselves might be shared.
//
// The definition of "unreasonable" is given as the threshold argument. At most
// that many warnings with the same summary will be shown.
func (diags Diagnostics) ConsolidateWarnings(threshold int) Diagnostics {
// that many diagnostics with the same summary will be shown.
func (diags Diagnostics) Consolidate(threshold int, level Severity) Diagnostics {
if len(diags) == 0 {
return nil
}
newDiags := make(Diagnostics, 0, len(diags))
// We'll track how many times we've seen each warning summary so we can
// We'll track how many times we've seen each diagnostic summary so we can
// decide when to start consolidating. Once we _have_ started consolidating,
// we'll also track the object representing the consolidated warning
// we'll also track the object representing the consolidated diagnostic
// so we can continue appending to it.
warningStats := make(map[string]int)
warningGroups := make(map[string]*warningGroup)
diagnosticStats := make(map[string]int)
diagnosticGroups := make(map[string]*consolidatedGroup)
for _, diag := range diags {
severity := diag.Severity()
if severity != Warning || diag.Source().Subject == nil {
// Only warnings can get special treatment, and we only
// consolidate warnings that have source locations because
if severity != level || diag.Source().Subject == nil {
// Only the given level can get special treatment, and we only
// consolidate diagnostics that have source locations because
// our primary goal here is to deal with the situation where
// some configuration language feature is producing a warning
// some configuration language feature is producing a diagnostic
// each time it's used across a potentially-large config.
newDiags = newDiags.Append(diag)
continue
@ -56,26 +56,26 @@ func (diags Diagnostics) ConsolidateWarnings(threshold int) Diagnostics {
desc := diag.Description()
summary := desc.Summary
if g, ok := warningGroups[summary]; ok {
if g, ok := diagnosticGroups[summary]; ok {
// We're already grouping this one, so we'll just continue it.
g.Append(diag)
continue
}
warningStats[summary]++
if warningStats[summary] == threshold {
diagnosticStats[summary]++
if diagnosticStats[summary] == threshold {
// Initially creating the group doesn't really change anything
// visibly in the result, since a group with only one warning
// visibly in the result, since a group with only one diagnostic
// is just a passthrough anyway, but once we do this any additional
// warnings with the same summary will get appended to this group.
g := &warningGroup{}
// diagnostics with the same summary will get appended to this group.
g := &consolidatedGroup{}
newDiags = newDiags.Append(g)
warningGroups[summary] = g
diagnosticGroups[summary] = g
g.Append(diag)
continue
}
// If this warning is not consolidating yet then we'll just append
// If this diagnostic is not consolidating yet then we'll just append
// it directly.
newDiags = newDiags.Append(diag)
}
@ -83,36 +83,46 @@ func (diags Diagnostics) ConsolidateWarnings(threshold int) Diagnostics {
return newDiags
}
// A warningGroup is one or more warning diagnostics grouped together for
// A consolidatedGroup is one or more diagnostics grouped together for
// UI consolidation purposes.
//
// A warningGroup with only one diagnostic in it is just a passthrough for
// A consolidatedGroup with only one diagnostic in it is just a passthrough for
// that one diagnostic. If it has more than one then it will behave mostly
// like the first one but its detail message will include an additional
// sentence mentioning the consolidation. A warningGroup with no diagnostics
// sentence mentioning the consolidation. A consolidatedGroup with no diagnostics
// at all is invalid and will panic when used.
type warningGroup struct {
Warnings Diagnostics
type consolidatedGroup struct {
Consolidated Diagnostics
}
var _ Diagnostic = (*warningGroup)(nil)
var _ Diagnostic = (*consolidatedGroup)(nil)
func (wg *warningGroup) Severity() Severity {
return wg.Warnings[0].Severity()
func (wg *consolidatedGroup) Severity() Severity {
return wg.Consolidated[0].Severity()
}
func (wg *warningGroup) Description() Description {
desc := wg.Warnings[0].Description()
if len(wg.Warnings) < 2 {
func (wg *consolidatedGroup) Description() Description {
desc := wg.Consolidated[0].Description()
if len(wg.Consolidated) == 1 {
return desc
}
extraCount := len(wg.Warnings) - 1
extraCount := len(wg.Consolidated) - 1
var msg string
var diagType string
switch wg.Severity() {
case Error:
diagType = "error"
case Warning:
diagType = "warning"
default:
panic(fmt.Sprintf("Invalid diagnostic severity: %#v", wg.Severity()))
}
switch extraCount {
case 1:
msg = "(and one more similar warning elsewhere)"
msg = fmt.Sprintf("(and one more similar %s elsewhere)", diagType)
default:
msg = fmt.Sprintf("(and %d more similar warnings elsewhere)", extraCount)
msg = fmt.Sprintf("(and %d more similar %ss elsewhere)", extraCount, diagType)
}
if desc.Detail != "" {
desc.Detail = desc.Detail + "\n\n" + msg
@ -122,39 +132,39 @@ func (wg *warningGroup) Description() Description {
return desc
}
func (wg *warningGroup) Source() Source {
return wg.Warnings[0].Source()
func (wg *consolidatedGroup) Source() Source {
return wg.Consolidated[0].Source()
}
func (wg *warningGroup) FromExpr() *FromExpr {
return wg.Warnings[0].FromExpr()
func (wg *consolidatedGroup) FromExpr() *FromExpr {
return wg.Consolidated[0].FromExpr()
}
func (wg *warningGroup) ExtraInfo() interface{} {
return wg.Warnings[0].ExtraInfo()
func (wg *consolidatedGroup) ExtraInfo() interface{} {
return wg.Consolidated[0].ExtraInfo()
}
func (wg *warningGroup) Append(diag Diagnostic) {
if diag.Severity() != Warning {
func (wg *consolidatedGroup) Append(diag Diagnostic) {
if len(wg.Consolidated) != 0 && diag.Severity() != wg.Severity() {
panic("can't append a non-warning diagnostic to a warningGroup")
}
wg.Warnings = append(wg.Warnings, diag)
wg.Consolidated = append(wg.Consolidated, diag)
}
// WarningGroupSourceRanges can be used in conjunction with
// Diagnostics.ConsolidateWarnings to recover the full set of original source
// locations from a consolidated warning.
// ConsolidatedGroupSourceRanges can be used in conjunction with
// Diagnostics.Consolidate to recover the full set of original source
// locations from a consolidated diagnostic.
//
// For convenience, this function accepts any diagnostic and will just return
// the single Source value from any diagnostic that isn't a warning group.
func WarningGroupSourceRanges(diag Diagnostic) []Source {
wg, ok := diag.(*warningGroup)
// the single Source value from any diagnostic that isn't a consolidated group.
func ConsolidatedGroupSourceRanges(diag Diagnostic) []Source {
wg, ok := diag.(*consolidatedGroup)
if !ok {
return []Source{diag.Source()}
}
ret := make([]Source, len(wg.Warnings))
for i, wrappedDiag := range wg.Warnings {
ret := make([]Source, len(wg.Consolidated))
for i, wrappedDiag := range wg.Consolidated {
ret[i] = wrappedDiag.Source()
}
return ret

View File

@ -96,7 +96,7 @@ func TestConsolidateWarnings(t *testing.T) {
// We're using ForRPC here to force the diagnostics to be of a consistent
// type that we can easily assert against below.
got := diags.ConsolidateWarnings(2).ForRPC()
got := diags.Consolidate(2, Warning).ForRPC()
want := Diagnostics{
// First set
&rpcFriendlyDiag{
@ -261,3 +261,61 @@ var _ DiagnosticExtraDoNotConsolidate = doNotConsolidate(true)
func (d doNotConsolidate) DoNotConsolidateDiagnostic() bool {
return bool(d)
}
func TestConsolidateError(t *testing.T) {
var diags Diagnostics
// create a multiplicity of errors
for i := 0; i < 5; i++ {
diags = diags.Append(
&hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: "Error 1",
Detail: "Error diag have duplicated subjects",
Subject: &hcl.Range{
Filename: "foo.tf",
Start: hcl.Pos{Line: 1, Column: 1, Byte: 0},
End: hcl.Pos{Line: 1, Column: 1, Byte: 0},
},
},
&hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: "Error 2",
Detail: "Error diag have different subjects",
Subject: &hcl.Range{
Filename: "foo.tf",
Start: hcl.Pos{Line: i + 1, Column: 1, Byte: 0},
End: hcl.Pos{Line: i + 1, Column: 1, Byte: 0},
},
},
)
}
got := diags.Consolidate(1, Error).ForRPC()
want := Diagnostics{
&rpcFriendlyDiag{
Severity_: Error,
Summary_: "Error 1",
Detail_: "Error diag have duplicated subjects\n\n(and 4 more similar errors elsewhere)",
Subject_: &SourceRange{
Filename: "foo.tf",
Start: SourcePos{Line: 1, Column: 1, Byte: 0},
End: SourcePos{Line: 1, Column: 1, Byte: 0},
},
},
&rpcFriendlyDiag{
Severity_: Error,
Summary_: "Error 2",
Detail_: "Error diag have different subjects\n\n(and 4 more similar errors elsewhere)",
Subject_: &SourceRange{
Filename: "foo.tf",
Start: SourcePos{Line: 1, Column: 1, Byte: 0},
End: SourcePos{Line: 1, Column: 1, Byte: 0},
},
},
}
if diff := cmp.Diff(want, got); diff != "" {
t.Errorf("wrong result\n%s", diff)
}
}