Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

🐛 Fix duplicated conditions when AS exists #515

Merged
merged 4 commits into from
Feb 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 16 additions & 6 deletions parser/rule_parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,12 @@ import (
"regexp"
"strings"

"gopkg.in/yaml.v2"

"github.com/go-logr/logr"
"github.com/konveyor/analyzer-lsp/engine"
"github.com/konveyor/analyzer-lsp/engine/labels"
"github.com/konveyor/analyzer-lsp/output/v1/konveyor"
"github.com/konveyor/analyzer-lsp/provider"
"gopkg.in/yaml.v2"
)

const (
Expand Down Expand Up @@ -555,6 +554,7 @@ func (r *RuleParser) getConditions(conditionsInterface []interface{}) ([]engine.
conditions := []engine.ConditionEntry{}
providers := map[string]provider.InternalProviderClient{}
chainNameToIndex := map[string]int{}
asFound := []string{}
for _, conditionInterface := range conditionsInterface {
// get map from interface
conditionMap, ok := conditionInterface.(map[interface{}]interface{})
Expand Down Expand Up @@ -684,15 +684,25 @@ func (r *RuleParser) getConditions(conditionsInterface []interface{}) ([]engine.
}
providers[providerKey] = provider
}
if ce.As != "" {
if ce.From != "" && ce.As != "" && ce.From == ce.As {
return nil, nil, fmt.Errorf("condition cannot have the same value for fields 'from' and 'as'")
} else if ce.As != "" {
for _, as := range asFound {
if as == ce.As {
return nil, nil, fmt.Errorf("condition cannot have multiple 'as' fields with the same name")
}
}
asFound = append(asFound, ce.As)

index, ok := chainNameToIndex[ce.As]
if !ok {
//prepend
conditions = append([]engine.ConditionEntry{ce}, conditions...)
} else {
//insert
conditions = append(conditions[:index+1], conditions[index:]...)
conditions[index] = ce
}
//insert
conditions = append(conditions[:index+1], conditions[index:]...)
conditions[index] = ce
} else if ce.From != "" && ce.As == "" {
chainNameToIndex[ce.From] = len(conditions)
conditions = append(conditions, ce)
Expand Down
186 changes: 186 additions & 0 deletions parser/rule_parser_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@ func TestLoadRules(t *testing.T) {
},
},
},
When: engine.ConditionEntry{},
},
},
},
Expand Down Expand Up @@ -183,6 +184,7 @@ func TestLoadRules(t *testing.T) {
Category: &konveyor.Potential,
},
Perform: engine.Perform{Message: engine.Message{Text: &allGoFiles, Links: []konveyor.Link{}}},
When: engine.ConditionEntry{},
},
},
},
Expand Down Expand Up @@ -256,6 +258,7 @@ func TestLoadRules(t *testing.T) {
Category: &konveyor.Potential,
},
Perform: engine.Perform{Message: engine.Message{Text: &allGoAndJsonFiles, Links: []konveyor.Link{}}},
When: engine.ConditionEntry{},
},
},
},
Expand Down Expand Up @@ -293,6 +296,7 @@ func TestLoadRules(t *testing.T) {
Category: &konveyor.Potential,
},
Perform: engine.Perform{Message: engine.Message{Text: &allGoOrJsonFiles, Links: []konveyor.Link{}}},
When: engine.ConditionEntry{},
},
},
},
Expand Down Expand Up @@ -330,6 +334,7 @@ func TestLoadRules(t *testing.T) {
Category: &konveyor.Potential,
},
Perform: engine.Perform{Message: engine.Message{Text: &allGoOrJsonFiles, Links: []konveyor.Link{}}},
When: engine.ConditionEntry{},
},
},
},
Expand Down Expand Up @@ -406,6 +411,7 @@ func TestLoadRules(t *testing.T) {
Category: &konveyor.Potential,
},
Perform: engine.Perform{Message: engine.Message{Text: &allGoOrJsonFiles, Links: []konveyor.Link{}}},
When: engine.ConditionEntry{},
},
},
},
Expand Down Expand Up @@ -461,6 +467,7 @@ func TestLoadRules(t *testing.T) {
Description: "",
},
Perform: engine.Perform{Message: engine.Message{Text: &allGoOrJsonFiles, Links: []konveyor.Link{}}},
When: engine.ConditionEntry{},
},
},
},
Expand Down Expand Up @@ -506,6 +513,7 @@ func TestLoadRules(t *testing.T) {
},
Tag: []string{"test"},
},
When: engine.ConditionEntry{},
},
},
},
Expand Down Expand Up @@ -557,6 +565,7 @@ func TestLoadRules(t *testing.T) {
Perform: engine.Perform{
Tag: []string{"test"},
},
When: engine.ConditionEntry{},
},
},
},
Expand Down Expand Up @@ -587,6 +596,7 @@ func TestLoadRules(t *testing.T) {
Category: &konveyor.Potential,
},
Perform: engine.Perform{Message: engine.Message{Text: &allGoFiles, Links: []konveyor.Link{}}},
When: engine.ConditionEntry{},
},
},
},
Expand All @@ -598,6 +608,7 @@ func TestLoadRules(t *testing.T) {
Category: &konveyor.Potential,
},
Perform: engine.Perform{Message: engine.Message{Text: &allGoFiles, Links: []konveyor.Link{}}},
When: engine.ConditionEntry{},
},
},
},
Expand Down Expand Up @@ -668,6 +679,7 @@ func TestLoadRules(t *testing.T) {
},
},
},
When: engine.ConditionEntry{},
},
},
},
Expand All @@ -680,6 +692,137 @@ func TestLoadRules(t *testing.T) {
},
},
},
{
Name: "chaining should yield the same number of conditions as the rule",
testFileName: "rule-chain-2.yaml",
providerNameClient: map[string]provider.InternalProviderClient{
"builtin": testProvider{
caps: []provider.Capability{{
Name: "filecontent",
}},
},
},
ExpectedProvider: map[string]provider.InternalProviderClient{
"builtin": testProvider{
caps: []provider.Capability{{
Name: "filecontent",
}},
},
},
ExpectedRuleSet: map[string]engine.RuleSet{
"konveyor-analysis": {
Rules: []engine.Rule{
{
RuleMeta: engine.RuleMeta{
RuleID: "chaining-rule",
Description: "",
Category: &konveyor.Potential,
},
Perform: engine.Perform{
Message: engine.Message{
Text: &allGoFiles,
Links: []konveyor.Link{},
},
},
When: engine.AndCondition{
Conditions: []engine.ConditionEntry{
{
From: "",
As: "file",
Ignorable: false,
Not: false,
},
{
From: "file",
As: "",
Ignorable: false,
Not: false,
},
},
},
},
},
},
},
},
{
Name: "no two conditions should have the same 'as' field within the same block",
testFileName: "rule-chain-same-as.yaml",
ShouldErr: true,
ErrorMessage: "condition cannot have multiple 'as' fields with the same name",
providerNameClient: map[string]provider.InternalProviderClient{
"builtin": testProvider{
caps: []provider.Capability{{
Name: "filecontent",
}},
},
},
ExpectedProvider: map[string]provider.InternalProviderClient{
"builtin": testProvider{
caps: []provider.Capability{{
Name: "filecontent",
}},
},
},
ExpectedRuleSet: map[string]engine.RuleSet{
"konveyor-analysis": {
Rules: []engine.Rule{
{
RuleMeta: engine.RuleMeta{
RuleID: "chaining-rule",
Description: "",
Category: &konveyor.Potential,
},
Perform: engine.Perform{
Message: engine.Message{
Text: &allGoFiles,
Links: []konveyor.Link{},
},
},
},
},
},
},
},
{
Name: "a condition should not have the same 'as' and 'from' fields",
testFileName: "rule-chain-same-as-from.yaml",
ShouldErr: true,
ErrorMessage: "condition cannot have the same value for fields 'from' and 'as'",
providerNameClient: map[string]provider.InternalProviderClient{
"builtin": testProvider{
caps: []provider.Capability{{
Name: "filecontent",
}},
},
},
ExpectedProvider: map[string]provider.InternalProviderClient{
"builtin": testProvider{
caps: []provider.Capability{{
Name: "filecontent",
}},
},
},
ExpectedRuleSet: map[string]engine.RuleSet{
"konveyor-analysis": {
Rules: []engine.Rule{
{
RuleMeta: engine.RuleMeta{
RuleID: "chaining-rule",
Description: "",
Category: &konveyor.Potential,
},
Perform: engine.Perform{
Message: engine.Message{
Text: &allGoFiles,
Links: []konveyor.Link{},
},
},
},
},
},
},
},
}

for _, tc := range testCases {
Expand Down Expand Up @@ -740,6 +883,7 @@ func TestLoadRules(t *testing.T) {
foundRule = true
}
}
compareWhens(expectedRule.When, rule.When, t)
}
if !foundRule {
t.Errorf("not have matching rule go: %#v, expected rules: %#v", rule, expectedSet.Rules)
Expand All @@ -750,3 +894,45 @@ func TestLoadRules(t *testing.T) {
})
}
}

func compareWhens(w1 engine.Conditional, w2 engine.Conditional, t *testing.T) {
if (w1 == nil && w2 != nil) || (w1 != nil && w2 == nil) {
t.Errorf("rulesets did not have matching when field")
}
if and1, ok := w1.(engine.AndCondition); ok {
and2, ok := w2.(engine.AndCondition)
if !ok {
t.Errorf("rulesets did not have matching when field")
}
compareConditions(and1.Conditions, and2.Conditions, t)
} else if or1, ok := w1.(engine.OrCondition); ok {
or2, ok := w2.(engine.OrCondition)
if !ok {
t.Errorf("rulesets did not have matching when field")
}
compareConditions(or1.Conditions, or2.Conditions, t)
}

}

func compareConditions(cs1 []engine.ConditionEntry, cs2 []engine.ConditionEntry, t *testing.T) {
if len(cs1) != len(cs2) {
t.Errorf("rulesets did not have the same number of conditions")
}
for i := 0; i < len(cs1); i++ {
c1 := cs1[i]
c2 := cs2[i]
if c1.As != c2.As {
t.Errorf("rulesets did not have the same As field")
}
if c1.From != c2.From {
t.Errorf("rulesets did not have the same From field")
}
if c1.Ignorable != c2.Ignorable {
t.Errorf("rulesets did not have the same Ignorable field")
}
if c1.Not != c2.Not {
t.Errorf("rulesets did not have the same Not field")
}
}
}
10 changes: 10 additions & 0 deletions parser/testdata/rule-chain-2.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
- message: "all go files"
ruleID: chaining-rule
when:
and:
- builtin.filecontent:
pattern: spring\.datasource
as: file
- builtin.filecontent:
pattern: value
from: file
8 changes: 8 additions & 0 deletions parser/testdata/rule-chain-same-as-from.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
- message: "all go files"
ruleID: chaining-rule
when:
and:
- builtin.filecontent:
pattern: spring\.datasource
as: file
from: file
10 changes: 10 additions & 0 deletions parser/testdata/rule-chain-same-as.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
- message: "all go files"
ruleID: chaining-rule
when:
and:
- builtin.filecontent:
pattern: spring\.datasource
as: file
- builtin.filecontent:
pattern: value
as: file
2 changes: 1 addition & 1 deletion parser/testdata/rule-chain.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,4 @@
ignore: true
not: true
- builtin.file: "*.json"
as: go-files
as: go-files-2
Loading