gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/runsc/cli/cli_test.go (about)

     1  // Copyright 2023 The gVisor 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 cli
    16  
    17  import (
    18  	"reflect"
    19  	"testing"
    20  
    21  	"github.com/google/subcommands"
    22  	"gvisor.dev/gvisor/runsc/flag"
    23  )
    24  
    25  var fakeFlagValues = [...]string{
    26  	"1",
    27  	"2:2",
    28  	"foo",
    29  	"none",
    30  	"1,2,3",
    31  	"2h45m",
    32  	"1:1,2:2",
    33  	"0 0 1,100000 100000 65536",
    34  	"lisafs:self,lisafs:none",
    35  }
    36  
    37  func dupFlag(t *testing.T, cmd subcommands.Command, flagName string) *flag.Flag {
    38  	// To create a true duplicate of the flag, we need to duplicate the command
    39  	// and its FlagSet.
    40  	var cmd2 subcommands.Command
    41  	var fs2 flag.FlagSet
    42  	cmd2 = reflect.New(reflect.TypeOf(cmd).Elem()).Interface().(subcommands.Command)
    43  	cmd2.SetFlags(&fs2)
    44  	flag2 := fs2.Lookup(flagName)
    45  	if flag2 == nil {
    46  		t.Fatalf("duplicate FlagSet does not contain flag %q for cmd %q", flagName, cmd.Name())
    47  	}
    48  	return flag2
    49  }
    50  
    51  // Tests that all the flags in all commands are idempotent; i.e. Set(String())
    52  // should be an idempotent operation.
    53  func TestFlagSetIdempotent(t *testing.T) {
    54  	cmds := make(map[string][]subcommands.Command)
    55  	forEachCmd(func(cmd subcommands.Command, group string) {
    56  		if cmdList, ok := cmds[group]; ok {
    57  			cmds[group] = append(cmdList, cmd)
    58  		} else {
    59  			cmds[group] = []subcommands.Command{cmd}
    60  		}
    61  	})
    62  
    63  	for group, cmdList := range cmds {
    64  		t.Run(group, func(t *testing.T) {
    65  			for _, cmd := range cmdList {
    66  				t.Run(cmd.Name(), func(t *testing.T) {
    67  					var fs flag.FlagSet
    68  					cmd.SetFlags(&fs)
    69  
    70  					// Iterate through all flags configured by this command.
    71  					fs.VisitAll(func(flag *flag.Flag) {
    72  						// Try a list of possible values for this flag.
    73  						matchedOneFlag := false
    74  						for _, v := range fakeFlagValues {
    75  							// Set() may have side effects even when it fails. So create a new
    76  							// flag for each try.
    77  							curFlag := dupFlag(t, cmd, flag.Name)
    78  							if err := curFlag.Value.Set(v); err != nil {
    79  								continue
    80  							}
    81  							// Worked. Now test that this flag is idempotent.
    82  							oldValue := curFlag.Value.String()
    83  							// Get a fresh flag.Flag to Set() this old value on.
    84  							newFlag := dupFlag(t, cmd, flag.Name)
    85  							if err := newFlag.Value.Set(oldValue); err != nil {
    86  								t.Errorf("flag %q from cmd %q is not idempotent: oldValue = %q, err = %v", flag.Name, cmd.Name(), oldValue, err)
    87  								return
    88  							}
    89  							// Compare this new flag value with old value.
    90  							if newValue := newFlag.Value.String(); newValue != oldValue {
    91  								t.Errorf("flag %q from cmd %q is not idempotent: oldValue = %q, newValue = %q", flag.Name, cmd.Name(), oldValue, newValue)
    92  								return
    93  							}
    94  							matchedOneFlag = true
    95  						}
    96  						if !matchedOneFlag {
    97  							t.Fatalf("none of the fake flag values work for flag %q from cmd %q", flag.Name, cmd.Name())
    98  						}
    99  					})
   100  				})
   101  			}
   102  		})
   103  	}
   104  }