| /* |
| Copyright 2016 The Kubernetes 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 feature |
| |
| import ( |
| "fmt" |
| "strings" |
| "testing" |
| |
| "github.com/spf13/pflag" |
| "github.com/stretchr/testify/assert" |
| ) |
| |
| func TestFeatureGateFlag(t *testing.T) { |
| // gates for testing |
| const testAlphaGate Feature = "TestAlpha" |
| const testBetaGate Feature = "TestBeta" |
| |
| tests := []struct { |
| arg string |
| expect map[Feature]bool |
| parseError string |
| }{ |
| { |
| arg: "", |
| expect: map[Feature]bool{ |
| allAlphaGate: false, |
| testAlphaGate: false, |
| testBetaGate: false, |
| }, |
| }, |
| { |
| arg: "fooBarBaz=true", |
| expect: map[Feature]bool{ |
| allAlphaGate: false, |
| testAlphaGate: false, |
| testBetaGate: false, |
| }, |
| parseError: "unrecognized feature gate: fooBarBaz", |
| }, |
| { |
| arg: "AllAlpha=false", |
| expect: map[Feature]bool{ |
| allAlphaGate: false, |
| testAlphaGate: false, |
| testBetaGate: false, |
| }, |
| }, |
| { |
| arg: "AllAlpha=true", |
| expect: map[Feature]bool{ |
| allAlphaGate: true, |
| testAlphaGate: true, |
| testBetaGate: false, |
| }, |
| }, |
| { |
| arg: "AllAlpha=banana", |
| expect: map[Feature]bool{ |
| allAlphaGate: false, |
| testAlphaGate: false, |
| testBetaGate: false, |
| }, |
| parseError: "invalid value of AllAlpha", |
| }, |
| { |
| arg: "AllAlpha=false,TestAlpha=true", |
| expect: map[Feature]bool{ |
| allAlphaGate: false, |
| testAlphaGate: true, |
| testBetaGate: false, |
| }, |
| }, |
| { |
| arg: "TestAlpha=true,AllAlpha=false", |
| expect: map[Feature]bool{ |
| allAlphaGate: false, |
| testAlphaGate: true, |
| testBetaGate: false, |
| }, |
| }, |
| { |
| arg: "AllAlpha=true,TestAlpha=false", |
| expect: map[Feature]bool{ |
| allAlphaGate: true, |
| testAlphaGate: false, |
| testBetaGate: false, |
| }, |
| }, |
| { |
| arg: "TestAlpha=false,AllAlpha=true", |
| expect: map[Feature]bool{ |
| allAlphaGate: true, |
| testAlphaGate: false, |
| testBetaGate: false, |
| }, |
| }, |
| { |
| arg: "TestBeta=true,AllAlpha=false", |
| expect: map[Feature]bool{ |
| allAlphaGate: false, |
| testAlphaGate: false, |
| testBetaGate: true, |
| }, |
| }, |
| } |
| for i, test := range tests { |
| fs := pflag.NewFlagSet("testfeaturegateflag", pflag.ContinueOnError) |
| f := NewFeatureGate() |
| f.Add(map[Feature]FeatureSpec{ |
| testAlphaGate: {Default: false, PreRelease: Alpha}, |
| testBetaGate: {Default: false, PreRelease: Beta}, |
| }) |
| f.AddFlag(fs) |
| |
| err := fs.Parse([]string{fmt.Sprintf("--%s=%s", flagName, test.arg)}) |
| if test.parseError != "" { |
| if !strings.Contains(err.Error(), test.parseError) { |
| t.Errorf("%d: Parse() Expected %v, Got %v", i, test.parseError, err) |
| } |
| } else if err != nil { |
| t.Errorf("%d: Parse() Expected nil, Got %v", i, err) |
| } |
| for k, v := range test.expect { |
| if actual := f.enabled.Load().(map[Feature]bool)[k]; actual != v { |
| t.Errorf("%d: expected %s=%v, Got %v", i, k, v, actual) |
| } |
| } |
| } |
| } |
| |
| func TestFeatureGateOverride(t *testing.T) { |
| const testAlphaGate Feature = "TestAlpha" |
| const testBetaGate Feature = "TestBeta" |
| |
| // Don't parse the flag, assert defaults are used. |
| var f FeatureGate = NewFeatureGate() |
| f.Add(map[Feature]FeatureSpec{ |
| testAlphaGate: {Default: false, PreRelease: Alpha}, |
| testBetaGate: {Default: false, PreRelease: Beta}, |
| }) |
| |
| f.Set("TestAlpha=true,TestBeta=true") |
| if f.Enabled(testAlphaGate) != true { |
| t.Errorf("Expected true") |
| } |
| if f.Enabled(testBetaGate) != true { |
| t.Errorf("Expected true") |
| } |
| |
| f.Set("TestAlpha=false") |
| if f.Enabled(testAlphaGate) != false { |
| t.Errorf("Expected false") |
| } |
| if f.Enabled(testBetaGate) != true { |
| t.Errorf("Expected true") |
| } |
| } |
| |
| func TestFeatureGateFlagDefaults(t *testing.T) { |
| // gates for testing |
| const testAlphaGate Feature = "TestAlpha" |
| const testBetaGate Feature = "TestBeta" |
| |
| // Don't parse the flag, assert defaults are used. |
| var f FeatureGate = NewFeatureGate() |
| f.Add(map[Feature]FeatureSpec{ |
| testAlphaGate: {Default: false, PreRelease: Alpha}, |
| testBetaGate: {Default: true, PreRelease: Beta}, |
| }) |
| |
| if f.Enabled(testAlphaGate) != false { |
| t.Errorf("Expected false") |
| } |
| if f.Enabled(testBetaGate) != true { |
| t.Errorf("Expected true") |
| } |
| } |
| |
| func TestFeatureGateKnownFeatures(t *testing.T) { |
| // gates for testing |
| const ( |
| testAlphaGate Feature = "TestAlpha" |
| testBetaGate Feature = "TestBeta" |
| testGAGate Feature = "TestGA" |
| testDeprecatedGate Feature = "TestDeprecated" |
| ) |
| |
| // Don't parse the flag, assert defaults are used. |
| var f FeatureGate = NewFeatureGate() |
| f.Add(map[Feature]FeatureSpec{ |
| testAlphaGate: {Default: false, PreRelease: Alpha}, |
| testBetaGate: {Default: true, PreRelease: Beta}, |
| testGAGate: {Default: true, PreRelease: GA}, |
| testDeprecatedGate: {Default: false, PreRelease: Deprecated}, |
| }) |
| |
| known := strings.Join(f.KnownFeatures(), " ") |
| |
| assert.Contains(t, known, testAlphaGate) |
| assert.Contains(t, known, testBetaGate) |
| assert.NotContains(t, known, testGAGate) |
| assert.NotContains(t, known, testDeprecatedGate) |
| } |
| |
| func TestFeatureGateSetFromMap(t *testing.T) { |
| // gates for testing |
| const testAlphaGate Feature = "TestAlpha" |
| const testBetaGate Feature = "TestBeta" |
| |
| tests := []struct { |
| name string |
| setmap map[string]bool |
| expect map[Feature]bool |
| setmapError string |
| }{ |
| { |
| name: "set TestAlpha and TestBeta true", |
| setmap: map[string]bool{ |
| "TestAlpha": true, |
| "TestBeta": true, |
| }, |
| expect: map[Feature]bool{ |
| testAlphaGate: true, |
| testBetaGate: true, |
| }, |
| }, |
| { |
| name: "set TestBeta true", |
| setmap: map[string]bool{ |
| "TestBeta": true, |
| }, |
| expect: map[Feature]bool{ |
| testAlphaGate: false, |
| testBetaGate: true, |
| }, |
| }, |
| { |
| name: "set TestAlpha false", |
| setmap: map[string]bool{ |
| "TestAlpha": false, |
| }, |
| expect: map[Feature]bool{ |
| testAlphaGate: false, |
| testBetaGate: false, |
| }, |
| }, |
| { |
| name: "set TestInvaild true", |
| setmap: map[string]bool{ |
| "TestInvaild": true, |
| }, |
| expect: map[Feature]bool{ |
| testAlphaGate: false, |
| testBetaGate: false, |
| }, |
| setmapError: "unrecognized feature gate:", |
| }, |
| } |
| for i, test := range tests { |
| t.Run(fmt.Sprintf("SetFromMap %s", test.name), func(t *testing.T) { |
| f := NewFeatureGate() |
| f.Add(map[Feature]FeatureSpec{ |
| testAlphaGate: {Default: false, PreRelease: Alpha}, |
| testBetaGate: {Default: false, PreRelease: Beta}, |
| }) |
| err := f.SetFromMap(test.setmap) |
| if test.setmapError != "" { |
| if !strings.Contains(err.Error(), test.setmapError) { |
| t.Errorf("%d: SetFromMap(%#v) Expected err:%v, Got err:%v", i, test.setmap, test.setmapError, err) |
| } |
| } else if err != nil { |
| t.Errorf("%d: SetFromMap(%#v) Expected success, Got err:%v", i, test.setmap, err) |
| } |
| for k, v := range test.expect { |
| if actual := f.Enabled(k); actual != v { |
| t.Errorf("%d: SetFromMap(%#v) Expected %s=%v, Got %s=%v", i, test.setmap, k, v, k, actual) |
| } |
| } |
| }) |
| } |
| } |
| |
| func TestFeatureGateString(t *testing.T) { |
| // gates for testing |
| const testAlphaGate Feature = "TestAlpha" |
| const testBetaGate Feature = "TestBeta" |
| const testGAGate Feature = "TestGA" |
| |
| featuremap := map[Feature]FeatureSpec{ |
| testGAGate: {Default: true, PreRelease: GA}, |
| testAlphaGate: {Default: false, PreRelease: Alpha}, |
| testBetaGate: {Default: true, PreRelease: Beta}, |
| } |
| |
| tests := []struct { |
| setmap map[string]bool |
| expect string |
| }{ |
| { |
| setmap: map[string]bool{ |
| "TestAlpha": false, |
| }, |
| expect: "TestAlpha=false", |
| }, |
| { |
| setmap: map[string]bool{ |
| "TestAlpha": false, |
| "TestBeta": true, |
| }, |
| expect: "TestAlpha=false,TestBeta=true", |
| }, |
| { |
| setmap: map[string]bool{ |
| "TestGA": true, |
| "TestAlpha": false, |
| "TestBeta": true, |
| }, |
| expect: "TestAlpha=false,TestBeta=true,TestGA=true", |
| }, |
| } |
| for i, test := range tests { |
| t.Run(fmt.Sprintf("SetFromMap %s", test.expect), func(t *testing.T) { |
| f := NewFeatureGate() |
| f.Add(featuremap) |
| f.SetFromMap(test.setmap) |
| result := f.String() |
| if result != test.expect { |
| t.Errorf("%d: SetFromMap(%#v) Expected %s, Got %s", i, test.setmap, test.expect, result) |
| } |
| }) |
| } |
| } |