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

TEP-0090: Add Matrix Package #4934

Merged
merged 1 commit into from
Jun 3, 2022
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
27 changes: 27 additions & 0 deletions pkg/matrix/matrix.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/*
Copyright 2022 The Tekton Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package matrix

import (
"github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1"
)

// FanOut produces combinations of Parameters of type String from a slice of Parameters of type Array.
func FanOut(params []v1beta1.Param) Combinations {
jerop marked this conversation as resolved.
Show resolved Hide resolved
var combinations Combinations
for _, parameter := range params {
combinations = combinations.fanOut(parameter)
}
return combinations
}
153 changes: 153 additions & 0 deletions pkg/matrix/matrix_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
/*
Copyright 2022 The Tekton Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package matrix

import (
"testing"

"github.com/google/go-cmp/cmp"
"github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1"
)

func Test_FanOut(t *testing.T) {
tests := []struct {
name string
matrix []v1beta1.Param
wantCombinations Combinations
}{{
name: "single array in matrix",
matrix: []v1beta1.Param{{
Name: "platform",
Value: v1beta1.ArrayOrString{Type: v1beta1.ParamTypeArray, ArrayVal: []string{"linux", "mac", "windows"}},
}},
wantCombinations: Combinations{{
MatrixID: "0",
Params: []v1beta1.Param{{
Name: "platform",
Value: v1beta1.ArrayOrString{Type: v1beta1.ParamTypeString, StringVal: "linux"},
}},
}, {
MatrixID: "1",
Params: []v1beta1.Param{{
Name: "platform",
Value: v1beta1.ArrayOrString{Type: v1beta1.ParamTypeString, StringVal: "mac"},
}},
}, {
MatrixID: "2",
Params: []v1beta1.Param{{
Name: "platform",
Value: v1beta1.ArrayOrString{Type: v1beta1.ParamTypeString, StringVal: "windows"},
}},
}},
}, {
name: "multiple arrays in matrix",
matrix: []v1beta1.Param{{
Name: "platform",
Value: v1beta1.ArrayOrString{Type: v1beta1.ParamTypeArray, ArrayVal: []string{"linux", "mac", "windows"}},
}, {
Name: "browser",
Value: v1beta1.ArrayOrString{Type: v1beta1.ParamTypeArray, ArrayVal: []string{"chrome", "safari", "firefox"}},
}},
wantCombinations: Combinations{{
MatrixID: "0",
Params: []v1beta1.Param{{
Name: "platform",
Value: v1beta1.ArrayOrString{Type: v1beta1.ParamTypeString, StringVal: "linux"},
}, {
Name: "browser",
Value: v1beta1.ArrayOrString{Type: v1beta1.ParamTypeString, StringVal: "chrome"},
}},
}, {
MatrixID: "1",
Params: []v1beta1.Param{{
Name: "platform",
Value: v1beta1.ArrayOrString{Type: v1beta1.ParamTypeString, StringVal: "mac"},
}, {
Name: "browser",
Value: v1beta1.ArrayOrString{Type: v1beta1.ParamTypeString, StringVal: "chrome"},
}},
}, {
MatrixID: "2",
Params: []v1beta1.Param{{
Name: "platform",
Value: v1beta1.ArrayOrString{Type: v1beta1.ParamTypeString, StringVal: "windows"},
}, {
Name: "browser",
Value: v1beta1.ArrayOrString{Type: v1beta1.ParamTypeString, StringVal: "chrome"},
}},
}, {
MatrixID: "3",
Params: []v1beta1.Param{{
Name: "platform",
Value: v1beta1.ArrayOrString{Type: v1beta1.ParamTypeString, StringVal: "linux"},
}, {
Name: "browser",
Value: v1beta1.ArrayOrString{Type: v1beta1.ParamTypeString, StringVal: "safari"},
}},
}, {
MatrixID: "4",
Params: []v1beta1.Param{{
Name: "platform",
Value: v1beta1.ArrayOrString{Type: v1beta1.ParamTypeString, StringVal: "mac"},
}, {
Name: "browser",
Value: v1beta1.ArrayOrString{Type: v1beta1.ParamTypeString, StringVal: "safari"},
}},
}, {
MatrixID: "5",
Params: []v1beta1.Param{{
Name: "platform",
Value: v1beta1.ArrayOrString{Type: v1beta1.ParamTypeString, StringVal: "windows"},
}, {
Name: "browser",
Value: v1beta1.ArrayOrString{Type: v1beta1.ParamTypeString, StringVal: "safari"},
}},
}, {
MatrixID: "6",
Params: []v1beta1.Param{{
Name: "platform",
Value: v1beta1.ArrayOrString{Type: v1beta1.ParamTypeString, StringVal: "linux"},
}, {
Name: "browser",
Value: v1beta1.ArrayOrString{Type: v1beta1.ParamTypeString, StringVal: "firefox"},
}},
}, {
MatrixID: "7",
Params: []v1beta1.Param{{
Name: "platform",
Value: v1beta1.ArrayOrString{Type: v1beta1.ParamTypeString, StringVal: "mac"},
}, {
Name: "browser",
Value: v1beta1.ArrayOrString{Type: v1beta1.ParamTypeString, StringVal: "firefox"},
}},
}, {
MatrixID: "8",
Params: []v1beta1.Param{{
Name: "platform",
Value: v1beta1.ArrayOrString{Type: v1beta1.ParamTypeString, StringVal: "windows"},
}, {
Name: "browser",
Value: v1beta1.ArrayOrString{Type: v1beta1.ParamTypeString, StringVal: "firefox"},
}},
}},
}}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
gotCombinations := FanOut(tt.matrix)
if d := cmp.Diff(tt.wantCombinations, gotCombinations); d != "" {
t.Errorf("Combinations of Parameters did not match the expected Combinations: %s", d)
}
})
}
}
82 changes: 82 additions & 0 deletions pkg/matrix/matrix_types.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
/*
Copyright 2022 The Tekton Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package matrix

import (
"strconv"

"github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1"
)

// Combinations is a slice of combinations of Parameters from a Matrix.
type Combinations []*Combination

// Combination is a specific combination of Parameters from a Matrix.
type Combination struct {
// MatrixID is an identification of a combination from Parameters in a Matrix.
MatrixID string

// Params is a specific combination of Parameters in a Matrix.
Params []v1beta1.Param
}

func (combinations Combinations) fanOut(param v1beta1.Param) Combinations {
if len(combinations) == 0 {
return initializeCombinations(param)
jerop marked this conversation as resolved.
Show resolved Hide resolved
}
return combinations.distribute(param)
}

func (combinations Combinations) distribute(param v1beta1.Param) Combinations {
// when there are existing combinations, this is a non-first parameter in the matrix, and we need to distribute
// it among the existing combinations
var expandedCombinations Combinations
var count int
for _, value := range param.Value.ArrayVal {
for _, combination := range combinations {
expandedCombinations = append(expandedCombinations, createCombination(count, param.Name, value, combination.Params))
count++
}
}
return expandedCombinations
}

func initializeCombinations(param v1beta1.Param) Combinations {
// when there are no existing combinations, this is the first parameter in the matrix, so we initialize the
// combinations with the first Parameter
var combinations Combinations
for i, value := range param.Value.ArrayVal {
combinations = append(combinations, createCombination(i, param.Name, value, []v1beta1.Param{}))
}
return combinations
}

func createCombination(i int, name string, value string, parameters []v1beta1.Param) *Combination {
return &Combination{
MatrixID: strconv.Itoa(i),
Params: append(parameters, v1beta1.Param{
Name: name,
Value: v1beta1.ArrayOrString{Type: v1beta1.ParamTypeString, StringVal: value},
}),
}
}

// ToMap converts a list of Combinations to a map where the key is the matrixId and the values are Parameters.
func (combinations Combinations) ToMap() map[string][]v1beta1.Param {
jerop marked this conversation as resolved.
Show resolved Hide resolved
m := map[string][]v1beta1.Param{}
for _, combination := range combinations {
m[combination.MatrixID] = combination.Params
}
return m
}
Loading