...

Source file src/github.com/bazelbuild/buildtools/buildifier/config/config_test.go

Documentation: github.com/bazelbuild/buildtools/buildifier/config

     1  /*
     2  Copyright 2022 Google LLC
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      https://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package config
    18  
    19  import (
    20  	"flag"
    21  	"fmt"
    22  	"io/ioutil"
    23  	"os"
    24  	"path/filepath"
    25  	"strings"
    26  	"testing"
    27  )
    28  
    29  func ExampleNew() {
    30  	c := New()
    31  	fmt.Print(c.String())
    32  	// Output:
    33  	// {
    34  	//   "type": "auto"
    35  	// }
    36  }
    37  
    38  func ExampleExample() {
    39  	c := Example()
    40  	fmt.Print(c.String())
    41  	// Output:
    42  	// {
    43  	//   "type": "auto",
    44  	//   "mode": "fix",
    45  	//   "lint": "fix",
    46  	//   "warningsList": [
    47  	//     "attr-applicable_licenses",
    48  	//     "attr-cfg",
    49  	//     "attr-license",
    50  	//     "attr-licenses",
    51  	//     "attr-non-empty",
    52  	//     "attr-output-default",
    53  	//     "attr-single-file",
    54  	//     "build-args-kwargs",
    55  	//     "bzl-visibility",
    56  	//     "confusing-name",
    57  	//     "constant-glob",
    58  	//     "ctx-actions",
    59  	//     "ctx-args",
    60  	//     "deprecated-function",
    61  	//     "depset-items",
    62  	//     "depset-iteration",
    63  	//     "depset-union",
    64  	//     "dict-concatenation",
    65  	//     "dict-method-named-arg",
    66  	//     "duplicated-name",
    67  	//     "filetype",
    68  	//     "function-docstring",
    69  	//     "function-docstring-args",
    70  	//     "function-docstring-header",
    71  	//     "function-docstring-return",
    72  	//     "git-repository",
    73  	//     "http-archive",
    74  	//     "integer-division",
    75  	//     "keyword-positional-params",
    76  	//     "list-append",
    77  	//     "load",
    78  	//     "module-docstring",
    79  	//     "name-conventions",
    80  	//     "native-android",
    81  	//     "native-build",
    82  	//     "native-cc",
    83  	//     "native-java",
    84  	//     "native-package",
    85  	//     "native-proto",
    86  	//     "native-py",
    87  	//     "no-effect",
    88  	//     "output-group",
    89  	//     "overly-nested-depset",
    90  	//     "package-name",
    91  	//     "package-on-top",
    92  	//     "positional-args",
    93  	//     "print",
    94  	//     "provider-params",
    95  	//     "redefined-variable",
    96  	//     "repository-name",
    97  	//     "return-value",
    98  	//     "rule-impl-return",
    99  	//     "skylark-comment",
   100  	//     "skylark-docstring",
   101  	//     "string-iteration",
   102  	//     "uninitialized",
   103  	//     "unnamed-macro",
   104  	//     "unreachable",
   105  	//     "unsorted-dict-items",
   106  	//     "unused-variable"
   107  	//   ]
   108  	// }
   109  }
   110  
   111  func ExampleFlagSet() {
   112  	c := New()
   113  	flags := c.FlagSet("buildifier", flag.ExitOnError)
   114  	flags.VisitAll(func(f *flag.Flag) {
   115  		fmt.Printf("%s: %s (%q)\n", f.Name, f.Usage, f.DefValue)
   116  	})
   117  	// Output:
   118  	// add_tables: path to JSON file with custom table definitions which will be merged with the built-in tables ("")
   119  	// allowsort: additional sort contexts to treat as safe ("")
   120  	// buildifier_disable: list of buildifier rewrites to disable ("")
   121  	// config: path to .buildifier.json config file ("")
   122  	// d: alias for -mode=diff ("false")
   123  	// diff_command: command to run when the formatting mode is diff (default uses the BUILDIFIER_DIFF, BUILDIFIER_MULTIDIFF, and DISPLAY environment variables to create the diff command) ("")
   124  	// format: diagnostics format: text or json (default text) ("")
   125  	// help: print usage information ("false")
   126  	// lint: lint mode: off, warn, or fix (default off) ("")
   127  	// mode: formatting mode: check, diff, or fix (default fix) ("")
   128  	// multi_diff: the command specified by the -diff_command flag can diff multiple files in the style of tkdiff (default false) ("false")
   129  	// path: assume BUILD file has this path relative to the workspace directory ("")
   130  	// r: find starlark files recursively ("false")
   131  	// tables: path to JSON file with custom table definitions which will replace the built-in tables ("")
   132  	// type: Input file type: build (for BUILD files), bzl (for .bzl files), workspace (for WORKSPACE files), module (for MODULE.bazel files), default (for generic Starlark files) or auto (default, based on the filename) ("auto")
   133  	// v: print verbose information to standard error ("false")
   134  	// version: print the version of buildifier ("false")
   135  	// warnings: comma-separated warnings used in the lint mode or "all" ("")
   136  }
   137  
   138  func ExampleFlagSet_parse() {
   139  	c := New()
   140  	flags := c.FlagSet("buildifier", flag.ExitOnError)
   141  	flags.Parse([]string{
   142  		"--add_tables=/path/to/add_tables.json",
   143  		"--allowsort=proto_library.deps",
   144  		"--allowsort=proto_library.srcs",
   145  		"--buildifier_disable=unsafesort",
   146  		"--config=/path/to/.buildifier.json",
   147  		"-d",
   148  		"--diff_command=diff",
   149  		"--format=json",
   150  		"--help",
   151  		"--lint=fix",
   152  		"--mode=fix",
   153  		"--multi_diff=true",
   154  		"--path=pkg/foo",
   155  		"-r",
   156  		"--tables=/path/to/tables.json",
   157  		"--type=default",
   158  		"-v",
   159  		"--version",
   160  		"--warnings=+print,-no-effect",
   161  	})
   162  	fmt.Println("help:", c.Help)
   163  	fmt.Println("version:", c.Version)
   164  	fmt.Println("configPath:", c.ConfigPath)
   165  	fmt.Print(c.String())
   166  	// Output:
   167  	// help: true
   168  	// version: true
   169  	// configPath: /path/to/.buildifier.json
   170  	// {
   171  	//   "type": "default",
   172  	//   "format": "json",
   173  	//   "mode": "fix",
   174  	//   "diffMode": true,
   175  	//   "lint": "fix",
   176  	//   "warnings": "+print,-no-effect",
   177  	//   "recursive": true,
   178  	//   "verbose": true,
   179  	//   "diffCommand": "diff",
   180  	//   "multiDiff": true,
   181  	//   "tables": "/path/to/tables.json",
   182  	//   "addTables": "/path/to/add_tables.json",
   183  	//   "path": "pkg/foo",
   184  	//   "buildifier_disable": [
   185  	//     "unsafesort"
   186  	//   ],
   187  	//   "allowsort": [
   188  	//     "proto_library.deps",
   189  	//     "proto_library.srcs"
   190  	//   ]
   191  	// }
   192  }
   193  
   194  func TestValidate(t *testing.T) {
   195  	for name, tc := range map[string]struct {
   196  		options      string
   197  		args         string
   198  		wantErr      error
   199  		wantMode     string   // optional
   200  		wantLint     string   // optional
   201  		wantWarnings []string // optional
   202  	}{
   203  		"mode not set":          {wantMode: "fix"},
   204  		"mode check":            {options: "--mode=check", wantMode: "check"},
   205  		"mode diff":             {options: "--mode=diff", wantMode: "diff"},
   206  		"mode d":                {options: "-d", wantMode: "diff"},
   207  		"mode d error":          {options: "--mode=diff -d", wantErr: fmt.Errorf("cannot specify both -d and -mode flags")},
   208  		"mode fix":              {options: "--mode=fix", wantMode: "fix"},
   209  		"mode print_if_changed": {options: "--mode=print_if_changed", wantMode: "print_if_changed"},
   210  		"mode error":            {options: "--mode=foo", wantErr: fmt.Errorf("unrecognized mode foo; valid modes are check, diff, fix, print_if_changed")},
   211  		"lint not set":          {wantLint: "off"},
   212  		"lint off":              {options: "--lint=off", wantLint: "off"},
   213  		"lint warn":             {options: "--lint=warn", wantLint: "warn"},
   214  		"lint fix":              {options: "--lint=fix", wantLint: "fix"},
   215  		"lint fix error":        {options: "--lint=fix --mode=check", wantErr: fmt.Errorf("--lint=fix is only compatible with --mode=fix")},
   216  		"format mode error":     {options: "--mode=fix --format=text", wantErr: fmt.Errorf("cannot specify --format without --mode=check")},
   217  		"format text":           {options: "--mode=check --format=text"},
   218  		"format json":           {options: "--mode=check --format=json"},
   219  		"format error":          {options: "--mode=check --format=foo", wantErr: fmt.Errorf("unrecognized format foo; valid types are text, json")},
   220  		"type build":            {options: "--type=build"},
   221  		"type bzl":              {options: "--type=bzl"},
   222  		"type workspace":        {options: "--type=workspace"},
   223  		"type default":          {options: "--type=default"},
   224  		"type module":           {options: "--type=module"},
   225  		"type auto":             {options: "--type=auto"},
   226  		"type error":            {options: "--type=foo", wantErr: fmt.Errorf("unrecognized input type foo; valid types are build, bzl, workspace, default, module, auto")},
   227  		"warnings all": {options: "--warnings=all", wantWarnings: []string{
   228  			"attr-applicable_licenses",
   229  			"attr-cfg",
   230  			"attr-license",
   231  			"attr-licenses",
   232  			"attr-non-empty",
   233  			"attr-output-default",
   234  			"attr-single-file",
   235  			"build-args-kwargs",
   236  			"bzl-visibility",
   237  			"confusing-name",
   238  			"constant-glob",
   239  			"ctx-actions",
   240  			"ctx-args",
   241  			"deprecated-function",
   242  			"depset-items",
   243  			"depset-iteration",
   244  			"depset-union",
   245  			"dict-concatenation",
   246  			"dict-method-named-arg",
   247  			"duplicated-name",
   248  			"filetype",
   249  			"function-docstring",
   250  			"function-docstring-args",
   251  			"function-docstring-header",
   252  			"function-docstring-return",
   253  			"git-repository",
   254  			"http-archive",
   255  			"integer-division",
   256  			"keyword-positional-params",
   257  			"list-append",
   258  			"load",
   259  			"module-docstring",
   260  			"name-conventions",
   261  			"native-android",
   262  			"native-build",
   263  			"native-cc",
   264  			"native-java",
   265  			"native-package",
   266  			"native-proto",
   267  			"native-py",
   268  			"no-effect",
   269  			"output-group",
   270  			"overly-nested-depset",
   271  			"package-name",
   272  			"package-on-top",
   273  			"positional-args",
   274  			"print",
   275  			"provider-params",
   276  			"redefined-variable",
   277  			"repository-name",
   278  			"return-value",
   279  			"rule-impl-return",
   280  			"skylark-comment",
   281  			"skylark-docstring",
   282  			"string-iteration",
   283  			"uninitialized",
   284  			"unnamed-macro",
   285  			"unreachable",
   286  			"unsorted-dict-items",
   287  			"unused-variable",
   288  		}},
   289  		"warnings default": {options: "--warnings=default", wantWarnings: []string{
   290  			"attr-applicable_licenses",
   291  			"attr-cfg",
   292  			"attr-license",
   293  			"attr-licenses",
   294  			"attr-non-empty",
   295  			"attr-output-default",
   296  			"attr-single-file",
   297  			"build-args-kwargs",
   298  			"bzl-visibility",
   299  			"confusing-name",
   300  			"constant-glob",
   301  			"ctx-actions",
   302  			"ctx-args",
   303  			"deprecated-function",
   304  			"depset-items",
   305  			"depset-iteration",
   306  			"depset-union",
   307  			"dict-concatenation",
   308  			"dict-method-named-arg",
   309  			"duplicated-name",
   310  			"filetype",
   311  			"function-docstring",
   312  			"function-docstring-args",
   313  			"function-docstring-header",
   314  			"function-docstring-return",
   315  			"git-repository",
   316  			"http-archive",
   317  			"integer-division",
   318  			"keyword-positional-params",
   319  			"list-append",
   320  			"load",
   321  			"module-docstring",
   322  			"name-conventions",
   323  			// "native-android",
   324  			"native-build",
   325  			// "native-cc",
   326  			// "native-java",
   327  			"native-package",
   328  			// "native-proto",
   329  			// "native-py",
   330  			"no-effect",
   331  			"output-group",
   332  			"overly-nested-depset",
   333  			"package-name",
   334  			"package-on-top",
   335  			"positional-args",
   336  			"print",
   337  			"provider-params",
   338  			"redefined-variable",
   339  			"repository-name",
   340  			"return-value",
   341  			"rule-impl-return",
   342  			"skylark-comment",
   343  			"skylark-docstring",
   344  			"string-iteration",
   345  			"uninitialized",
   346  			"unnamed-macro",
   347  			"unreachable",
   348  			// "unsorted-dict-items",
   349  			"unused-variable",
   350  		}},
   351  		"warnings plus/minus": {options: "--warnings=+native-cc,-print,-deprecated-function", wantWarnings: []string{
   352  			"attr-applicable_licenses",
   353  			"attr-cfg",
   354  			"attr-license",
   355  			"attr-licenses",
   356  			"attr-non-empty",
   357  			"attr-output-default",
   358  			"attr-single-file",
   359  			"build-args-kwargs",
   360  			"bzl-visibility",
   361  			"confusing-name",
   362  			"constant-glob",
   363  			"ctx-actions",
   364  			"ctx-args",
   365  			// "deprecated-function",
   366  			"depset-items",
   367  			"depset-iteration",
   368  			"depset-union",
   369  			"dict-concatenation",
   370  			"dict-method-named-arg",
   371  			"duplicated-name",
   372  			"filetype",
   373  			"function-docstring",
   374  			"function-docstring-args",
   375  			"function-docstring-header",
   376  			"function-docstring-return",
   377  			"git-repository",
   378  			"http-archive",
   379  			"integer-division",
   380  			"keyword-positional-params",
   381  			"list-append",
   382  			"load",
   383  			"module-docstring",
   384  			"name-conventions",
   385  			// "native-android",
   386  			"native-build",
   387  			// "native-java",
   388  			"native-package",
   389  			// "native-proto",
   390  			// "native-py",
   391  			"no-effect",
   392  			"output-group",
   393  			"overly-nested-depset",
   394  			"package-name",
   395  			"package-on-top",
   396  			"positional-args",
   397  			// "print",
   398  			"provider-params",
   399  			"redefined-variable",
   400  			"repository-name",
   401  			"return-value",
   402  			"rule-impl-return",
   403  
   404  			"skylark-comment",
   405  			"skylark-docstring",
   406  			"string-iteration",
   407  			"uninitialized",
   408  			"unnamed-macro",
   409  			"unreachable",
   410  			// "unsorted-dict-items",
   411  			"unused-variable",
   412  			"native-cc",
   413  		}},
   414  		"warnings error": {options: "--warnings=native-cc,-print,-deprecated-function", wantErr: fmt.Errorf(`warning categories with modifiers ("+" or "-") can't be mixed with raw warning categories`)},
   415  	} {
   416  		t.Run(name, func(t *testing.T) {
   417  			c := New()
   418  			flags := c.FlagSet("buildifier", flag.ExitOnError)
   419  			flags.Parse(strings.Fields(tc.options))
   420  			got := c.Validate(strings.Fields(tc.args))
   421  			if tc.wantMode != "" && tc.wantMode != c.Mode {
   422  				t.Fatalf("--mode mismatch: want %v, got %v", tc.wantMode, c.Mode)
   423  			}
   424  			if tc.wantLint != "" && tc.wantLint != c.Lint {
   425  				t.Fatalf("--lint mismatch: want %v, got %v", tc.wantLint, c.Lint)
   426  			}
   427  			if len(tc.wantWarnings) > 0 {
   428  				if len(tc.wantWarnings) != len(c.LintWarnings) {
   429  					t.Fatalf("--warnings mismatch: want %v, got %v", tc.wantWarnings, c.LintWarnings)
   430  				}
   431  				for i, wantWarning := range tc.wantWarnings {
   432  					gotWarning := c.LintWarnings[i]
   433  					if wantWarning != gotWarning {
   434  						t.Errorf("warning mismatch at list position %d: want %s, got %s", i, wantWarning, gotWarning)
   435  					}
   436  				}
   437  			}
   438  			if tc.wantErr == nil && got == nil {
   439  				return
   440  			}
   441  			if tc.wantErr == nil && got != nil {
   442  				t.Fatalf("unexpected error: %v", got)
   443  			}
   444  			if tc.wantErr != nil && got == nil {
   445  				t.Fatalf("expected error did not occur: %v", tc.wantErr)
   446  			}
   447  			if tc.wantErr.Error() != got.Error() {
   448  				t.Fatalf("error mismatch: want %v, got %v", tc.wantErr.Error(), got.Error())
   449  			}
   450  		})
   451  	}
   452  }
   453  
   454  func TestFindConfigPath(t *testing.T) {
   455  	for name, tc := range map[string]struct {
   456  		files map[string]string
   457  		env   map[string]string
   458  		want  string
   459  	}{
   460  		"no-config-file": {
   461  			want: "",
   462  		},
   463  		"default": {
   464  			files: map[string]string{
   465  				".buildifier.json": "{}",
   466  			},
   467  			want: ".buildifier.json",
   468  		},
   469  		"BUILDIFIER_CONFIG-override": {
   470  			env: map[string]string{
   471  				"BUILDIFIER_CONFIG": ".buildifier2.json",
   472  			},
   473  			want: ".buildifier2.json",
   474  		},
   475  	} {
   476  		t.Run(name, func(t *testing.T) {
   477  			for k, v := range tc.env {
   478  				os.Setenv(k, v)
   479  				defer os.Unsetenv(k)
   480  			}
   481  
   482  			tmp, err := ioutil.TempDir("", name+"*")
   483  			if err != nil {
   484  				t.Fatal(err)
   485  			}
   486  			defer os.RemoveAll(tmp)
   487  
   488  			if err := os.Chdir(tmp); err != nil {
   489  				t.Fatal(err)
   490  			}
   491  
   492  			t.Log("tmp:", tmp)
   493  
   494  			for rel, content := range tc.files {
   495  				dir := filepath.Join(tmp, filepath.Dir(rel))
   496  				if dir != "." {
   497  					if err := os.MkdirAll(dir, os.ModePerm); err != nil {
   498  						t.Fatal(err)
   499  					}
   500  				}
   501  				filename := filepath.Join(dir, rel)
   502  				if err := ioutil.WriteFile(filename, []byte(content), 0644); err != nil {
   503  					t.Fatal(err)
   504  				}
   505  			}
   506  
   507  			got := FindConfigPath(tmp)
   508  			got = strings.TrimPrefix(got, tmp)
   509  			got = strings.TrimPrefix(got, "/")
   510  
   511  			if tc.want != got {
   512  				t.Errorf("FindConfigPath: want %q, got %q", tc.want, got)
   513  			}
   514  		})
   515  	}
   516  }
   517  

View as plain text