github.com/cheshirekow/buildtools@v0.0.0-20200224190056-5d637702fe81/edit/buildozer_test.go (about) 1 /* 2 Copyright 2019 Google Inc. All Rights Reserved. 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 http://www.apache.org/licenses/LICENSE-2.0 7 Unless required by applicable law or agreed to in writing, software 8 distributed under the License is distributed on an "AS IS" BASIS, 9 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 See the License for the specific language governing permissions and 11 limitations under the License. 12 */ 13 14 package edit 15 16 import ( 17 "io/ioutil" 18 "os" 19 "path/filepath" 20 "reflect" 21 "strings" 22 "testing" 23 24 "github.com/cheshirekow/buildtools/build" 25 ) 26 27 var removeCommentTests = []struct { 28 args []string 29 buildFile string 30 expected string 31 }{ 32 {[]string{}, 33 `# comment 34 foo( 35 name = "foo", 36 )`, 37 `foo( 38 name = "foo", 39 )`, 40 }, 41 {[]string{ 42 "name", 43 }, 44 `foo( 45 # comment 46 name = "foo", 47 )`, 48 `foo( 49 name = "foo", 50 )`, 51 }, 52 {[]string{ 53 "name", 54 }, 55 `foo( 56 name = "foo" # comment, 57 )`, 58 `foo( 59 name = "foo", 60 )`, 61 }, 62 {[]string{ 63 "deps", "bar", 64 }, 65 `foo( 66 name = "foo", 67 deps = [ 68 # comment 69 "bar", 70 "baz", 71 ], 72 )`, 73 `foo( 74 name = "foo", 75 deps = [ 76 "bar", 77 "baz", 78 ], 79 )`, 80 }, 81 {[]string{ 82 "deps", "bar", 83 }, 84 `foo( 85 name = "foo", 86 deps = [ 87 "bar", # comment 88 "baz", 89 ], 90 )`, 91 `foo( 92 name = "foo", 93 deps = [ 94 "bar", 95 "baz", 96 ], 97 )`, 98 }, 99 } 100 101 func TestCmdRemoveComment(t *testing.T) { 102 for i, tt := range removeCommentTests { 103 bld, err := build.Parse("BUILD", []byte(tt.buildFile)) 104 if err != nil { 105 t.Error(err) 106 continue 107 } 108 rl := bld.Rules("foo")[0] 109 env := CmdEnvironment{ 110 File: bld, 111 Rule: rl, 112 Args: tt.args, 113 } 114 bld, _ = cmdRemoveComment(NewOpts(), env) 115 got := strings.TrimSpace(string(build.Format(bld))) 116 if got != tt.expected { 117 t.Errorf("cmdRemoveComment(%d):\ngot:\n%s\nexpected:\n%s", i, got, tt.expected) 118 } 119 } 120 } 121 122 type targetExpressionToBuildFilesTestCase struct { 123 rootDir, target string 124 buildFiles []string 125 } 126 127 func runTestTargetExpressionToBuildFiles(t *testing.T, buildFileName string) { 128 tmp, err := ioutil.TempDir("", "") 129 if err != nil { 130 t.Fatal(err) 131 } 132 133 // On MacOS "/tmp" is a symlink to "/private/tmp". Resolve it to make the testing easier 134 tmp, err = filepath.EvalSymlinks(tmp) 135 if err != nil { 136 t.Fatal(err) 137 } 138 139 defer os.RemoveAll(tmp) 140 if err := os.MkdirAll(filepath.Join(tmp, "a", "b"), 0755); err != nil { 141 t.Fatal(err) 142 } 143 if err := os.MkdirAll(filepath.Join(tmp, "a", "c"), 0755); err != nil { 144 t.Fatal(err) 145 } 146 if err := ioutil.WriteFile(filepath.Join(tmp, "WORKSPACE"), nil, 0755); err != nil { 147 t.Fatal(err) 148 } 149 if err := ioutil.WriteFile(filepath.Join(tmp, buildFileName), nil, 0755); err != nil { 150 t.Fatal(err) 151 } 152 if err := ioutil.WriteFile(filepath.Join(tmp, "a", buildFileName), nil, 0755); err != nil { 153 t.Fatal(err) 154 } 155 if err := ioutil.WriteFile(filepath.Join(tmp, "a", "b", buildFileName), nil, 0755); err != nil { 156 t.Fatal(err) 157 } 158 if err := ioutil.WriteFile(filepath.Join(tmp, "a", "c", buildFileName), nil, 0755); err != nil { 159 t.Fatal(err) 160 } 161 162 for _, tc := range []targetExpressionToBuildFilesTestCase{ 163 {tmp, "//", []string{filepath.Join(tmp, buildFileName)}}, 164 {tmp, "//:foo", []string{filepath.Join(tmp, buildFileName)}}, 165 {tmp, "//a", []string{filepath.Join(tmp, "a", buildFileName)}}, 166 {tmp, "//a:foo", []string{filepath.Join(tmp, "a", buildFileName)}}, 167 {tmp, "//a/b", []string{filepath.Join(tmp, "a", "b", buildFileName)}}, 168 {tmp, "//a/b:foo", []string{filepath.Join(tmp, "a", "b", buildFileName)}}, 169 {tmp, "//...", []string{filepath.Join(tmp, buildFileName), filepath.Join(tmp, "a", buildFileName), filepath.Join(tmp, "a", "b", buildFileName), filepath.Join(tmp, "a", "c", buildFileName)}}, 170 {tmp, "//a/...", []string{filepath.Join(tmp, "a", buildFileName), filepath.Join(tmp, "a", "b", buildFileName), filepath.Join(tmp, "a", "c", buildFileName)}}, 171 {tmp, "//a/b/...", []string{filepath.Join(tmp, "a", "b", buildFileName)}}, 172 {tmp, "//a/c/...", []string{filepath.Join(tmp, "a", "c", buildFileName)}}, 173 {tmp, "//a/c/...:foo", []string{filepath.Join(tmp, "a", "c", buildFileName)}}, 174 {"", "...:foo", []string{filepath.Join(tmp, buildFileName), filepath.Join(tmp, "a", buildFileName), filepath.Join(tmp, "a", "b", buildFileName), filepath.Join(tmp, "a", "c", buildFileName)}}, 175 } { 176 if tc.rootDir == "" { 177 cwd, err := os.Getwd() 178 if err != nil { 179 t.Fatal(err) 180 } 181 // buildozer should be able to find the WORKSPACE file in the current wd 182 if err := os.Chdir(tmp); err != nil { 183 t.Fatal(err) 184 } 185 defer os.Chdir(cwd) 186 } 187 188 buildFiles := targetExpressionToBuildFiles(tc.rootDir, tc.target) 189 expectedBuildFilesMap := make(map[string]bool) 190 buildFilesMap := make(map[string]bool) 191 for _, buildFile := range buildFiles { 192 buildFilesMap[buildFile] = true 193 } 194 for _, buildFile := range tc.buildFiles { 195 expectedBuildFilesMap[buildFile] = true 196 } 197 if !reflect.DeepEqual(expectedBuildFilesMap, buildFilesMap) { 198 t.Errorf("TargetExpressionToBuildFiles(%q, %q) = %q want %q", tc.rootDir, tc.target, buildFiles, tc.buildFiles) 199 } 200 } 201 } 202 203 func TestTargetExpressionToBuildFiles(t *testing.T) { 204 runTestTargetExpressionToBuildFiles(t, "BUILD") 205 runTestTargetExpressionToBuildFiles(t, "BUILD.bazel") 206 } 207 208 var dictListAddTests = []struct { 209 args []string 210 buildFile string 211 expected string 212 }{ 213 {[]string{ 214 "attr", "key1", "value1", 215 }, 216 `foo( 217 name = "foo", 218 )`, 219 `foo( 220 name = "foo", 221 attr = {"key1": ["value1"]}, 222 )`, 223 }, 224 {[]string{ 225 "attr", "key1", "value2", 226 }, 227 `foo( 228 name = "foo", 229 attr = {"key1": ["value1"]}, 230 )`, 231 `foo( 232 name = "foo", 233 attr = {"key1": [ 234 "value1", 235 "value2", 236 ]}, 237 )`, 238 }, 239 {[]string{ 240 "attr", "key1", "value1", "value2", 241 }, 242 `foo( 243 name = "foo", 244 )`, 245 `foo( 246 name = "foo", 247 attr = {"key1": [ 248 "value1", 249 "value2", 250 ]}, 251 )`, 252 }, 253 {[]string{ 254 "attr", "key2", "value2", 255 }, 256 `foo( 257 name = "foo", 258 attr = {"key1": ["value1"]}, 259 )`, 260 `foo( 261 name = "foo", 262 attr = { 263 "key1": ["value1"], 264 "key2": ["value2"], 265 }, 266 )`, 267 }, 268 {[]string{ 269 "attr", "key1", "value1", 270 }, 271 `foo( 272 name = "foo", 273 attr = {"key1": ["value1"]}, 274 )`, 275 `foo( 276 name = "foo", 277 attr = {"key1": ["value1"]}, 278 )`, 279 }, 280 } 281 282 func TestCmdDictListAdd(t *testing.T) { 283 for i, tt := range dictListAddTests { 284 bld, err := build.Parse("BUILD", []byte(tt.buildFile)) 285 if err != nil { 286 t.Error(err) 287 continue 288 } 289 rl := bld.Rules("foo")[0] 290 env := CmdEnvironment{ 291 File: bld, 292 Rule: rl, 293 Args: tt.args, 294 } 295 bld, _ = cmdDictListAdd(NewOpts(), env) 296 got := strings.TrimSpace(string(build.Format(bld))) 297 if got != tt.expected { 298 t.Errorf("cmdDictListAdd(%d):\ngot:\n%s\nexpected:\n%s", i, got, tt.expected) 299 } 300 } 301 } 302 303 var substituteLoadsTests = []struct { 304 args []string 305 buildFile string 306 expected string 307 }{ 308 {[]string{ 309 "^(.*)$", "${1}", 310 }, 311 `load("//foo:foo.bzl", "foo")`, 312 `load("//foo:foo.bzl", "foo")`, 313 }, 314 {[]string{ 315 "^@rules_foo//foo:defs.bzl$", "//build/rules/foo:defs.bzl", 316 }, 317 `load("@rules_bar//bar:defs.bzl", "bar")`, 318 `load("@rules_bar//bar:defs.bzl", "bar")`, 319 }, 320 {[]string{ 321 "^@rules_foo//foo:defs.bzl$", "//build/rules/foo:defs.bzl", 322 }, 323 `load("@rules_foo//foo:defs.bzl", "foo", "foo2") 324 load("@rules_bar//bar:defs.bzl", "bar")`, 325 `load("//build/rules/foo:defs.bzl", "foo", "foo2") 326 load("@rules_bar//bar:defs.bzl", "bar")`, 327 }, 328 {[]string{ 329 ":foo.bzl$", ":defs.bzl", 330 }, 331 `load("//foo:foo.bzl", "foo")`, 332 `load("//foo:defs.bzl", "foo")`, 333 }, 334 {[]string{ 335 // Keep in sync with the example in `//buildozer:README.md`. 336 "^@([^/]*)//([^:].*)$", "//third_party/build_defs/${1}/${2}", 337 }, 338 `load("@rules_foo//foo:defs.bzl", "foo", "foo2") 339 load("@rules_bar//bar:defs.bzl", "bar") 340 load("@rules_bar//:defs.bzl", legacy_bar = "bar")`, 341 `load("//third_party/build_defs/rules_foo/foo:defs.bzl", "foo", "foo2") 342 load("//third_party/build_defs/rules_bar/bar:defs.bzl", "bar") 343 load("@rules_bar//:defs.bzl", legacy_bar = "bar")`, 344 }, 345 } 346 347 func TestCmdSubstituteLoad(t *testing.T) { 348 for i, tt := range substituteLoadsTests { 349 bld, err := build.Parse("BUILD", []byte(tt.buildFile)) 350 if err != nil { 351 t.Error(err) 352 continue 353 } 354 env := CmdEnvironment{ 355 File: bld, 356 Args: tt.args, 357 } 358 bld, _ = cmdSubstituteLoad(NewOpts(), env) 359 got := strings.TrimSpace(string(build.Format(bld))) 360 if got != tt.expected { 361 t.Errorf("cmdSubstituteLoad(%d):\ngot:\n%s\nexpected:\n%s", i, got, tt.expected) 362 } 363 } 364 }