github.com/joelanford/operator-sdk@v0.8.2/internal/pkg/scorecard/test_definitions.go (about) 1 // Copyright 2019 The Operator-SDK Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package scorecard 16 17 import ( 18 "context" 19 "fmt" 20 21 scapiv1alpha1 "github.com/operator-framework/operator-sdk/pkg/apis/scorecard/v1alpha1" 22 ) 23 24 // Type Definitions 25 26 // Test provides methods for running scorecard tests 27 type Test interface { 28 GetName() string 29 GetDescription() string 30 IsCumulative() bool 31 Run(context.Context) *TestResult 32 } 33 34 // TestResult contains a test's points, suggestions, and errors 35 type TestResult struct { 36 State scapiv1alpha1.State 37 Test Test 38 EarnedPoints int 39 MaximumPoints int 40 Suggestions []string 41 Errors []error 42 } 43 44 // TestInfo contains information about the scorecard test 45 type TestInfo struct { 46 Name string 47 Description string 48 // If a test is set to cumulative, the scores of multiple runs of the same test on separate CRs are added together for the total score. 49 // If cumulative is false, if any test failed, the total score is 0/1. Otherwise 1/1. 50 Cumulative bool 51 } 52 53 // GetName return the test name 54 func (i TestInfo) GetName() string { return i.Name } 55 56 // GetDescription returns the test description 57 func (i TestInfo) GetDescription() string { return i.Description } 58 59 // IsCumulative returns true if the test's scores are intended to be cumulative 60 func (i TestInfo) IsCumulative() bool { return i.Cumulative } 61 62 // TestSuite contains a list of tests and results, along with the relative weights of each test. Also can optionally contain a log 63 type TestSuite struct { 64 TestInfo 65 Tests []Test 66 TestResults []TestResult 67 Weights map[string]float64 68 Log string 69 } 70 71 // Helper functions 72 73 // AddTest adds a new Test to a TestSuite along with a relative weight for the new Test 74 func (ts *TestSuite) AddTest(t Test, weight float64) { 75 ts.Tests = append(ts.Tests, t) 76 ts.Weights[t.GetName()] = weight 77 } 78 79 // TotalScore calculates and returns the total score of all run Tests in a TestSuite 80 func (ts *TestSuite) TotalScore() (score int) { 81 floatScore := 0.0 82 for _, result := range ts.TestResults { 83 if result.MaximumPoints != 0 { 84 floatScore += (float64(result.EarnedPoints) / float64(result.MaximumPoints)) * ts.Weights[result.Test.GetName()] 85 } 86 } 87 // scale to a percentage 88 addedWeights := 0.0 89 for _, weight := range ts.Weights { 90 addedWeights += weight 91 } 92 // protect against divide by zero for failed plugins 93 if addedWeights == 0 { 94 return 0 95 } 96 return int(floatScore * (100 / addedWeights)) 97 } 98 99 // Run runs all Tests in a TestSuite 100 func (ts *TestSuite) Run(ctx context.Context) { 101 for _, test := range ts.Tests { 102 ts.TestResults = append(ts.TestResults, *test.Run(ctx)) 103 } 104 } 105 106 // NewTestSuite returns a new TestSuite with a given name and description 107 func NewTestSuite(name, description string) *TestSuite { 108 return &TestSuite{ 109 TestInfo: TestInfo{ 110 Name: name, 111 Description: description, 112 }, 113 Weights: make(map[string]float64), 114 } 115 } 116 117 // MergeSuites takes an array of TestSuites and combines all suites with the same name 118 func MergeSuites(suites []TestSuite) ([]TestSuite, error) { 119 suiteMap := make(map[string][]TestSuite) 120 for _, suite := range suites { 121 suiteMap[suite.GetName()] = append(suiteMap[suite.GetName()], suite) 122 } 123 mergedSuites := []TestSuite{} 124 for _, suiteSlice := range suiteMap { 125 testMap := make(map[string][]TestResult) 126 for _, suite := range suiteSlice { 127 for _, result := range suite.TestResults { 128 testMap[result.Test.GetName()] = append(testMap[result.Test.GetName()], result) 129 } 130 } 131 mergedTestResults := []TestResult{} 132 for _, testSlice := range testMap { 133 if testSlice[0].Test.IsCumulative() { 134 newResult, err := ResultsCumulative(testSlice) 135 if err != nil { 136 return nil, fmt.Errorf("failed to combine test results: %s", err) 137 } 138 mergedTestResults = append(mergedTestResults, newResult) 139 } else { 140 newResult, err := ResultsPassFail(testSlice) 141 if err != nil { 142 return nil, fmt.Errorf("failed to combine test results: %s", err) 143 } 144 mergedTestResults = append(mergedTestResults, newResult) 145 } 146 } 147 newSuite := suiteSlice[0] 148 newSuite.TestResults = mergedTestResults 149 mergedSuites = append(mergedSuites, newSuite) 150 } 151 return mergedSuites, nil 152 }