1// Copyright 2020 Google LLC 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// https://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 15package workspace 16 17import ( 18 "io/ioutil" 19 "os" 20 "os/exec" 21 "path" 22 "testing" 23 24 "android.googlesource.com/platform/tools/treble.git/hacksaw/bind" 25 "android.googlesource.com/platform/tools/treble.git/hacksaw/codebase" 26 "android.googlesource.com/platform/tools/treble.git/hacksaw/config" 27) 28 29func TestBasicCreate(t *testing.T) { 30 defer config.GetConfig().Reset() 31 codebaseDir, err := ioutil.TempDir("", "codebase") 32 if err != nil { 33 t.Error(err) 34 } 35 defer os.RemoveAll(codebaseDir) 36 gitDir := path.Join(codebaseDir, "project", ".git") 37 if err = os.MkdirAll(gitDir, os.ModePerm); err != nil { 38 t.Error(err) 39 } 40 repoDir := path.Join(codebaseDir, ".repo") 41 if err = os.Mkdir(repoDir, os.ModePerm); err != nil { 42 t.Error(err) 43 } 44 listContents := []byte("project") 45 listPath := path.Join(repoDir, "project.list") 46 if err = ioutil.WriteFile(listPath, listContents, os.ModePerm); err != nil { 47 t.Error(err) 48 } 49 _, err = codebase.Add("test-codebase", codebaseDir) 50 if err != nil { 51 t.Error(err) 52 } 53 // The top dir must be named "hacksaw" 54 // otherwise the mounters will reject any 55 // mount requests 56 wsTempDir, err := ioutil.TempDir("", "workspace") 57 if err != nil { 58 t.Error(err) 59 } 60 defer os.RemoveAll(wsTempDir) 61 wsTopDir := path.Join(wsTempDir, "hacksaw") 62 if err = os.Mkdir(wsTopDir, os.ModePerm); err != nil { 63 t.Error(err) 64 } 65 ws := New(bind.NewFakePathBinder(), wsTopDir) 66 if _, err = ws.Create("test-workspace", "test-codebase"); err != nil { 67 t.Error(err) 68 } 69 workspaceDir, err := ws.GetDir("test-workspace") 70 if err != nil { 71 t.Error(err) 72 } 73 _, err = os.Stat(workspaceDir) 74 if err != nil { 75 t.Error(err) 76 } 77} 78 79func TestWorkspaceDuplicate(t *testing.T) { 80 defer config.GetConfig().Reset() 81 codebaseDir, err := ioutil.TempDir("", "codebase") 82 if err != nil { 83 t.Error(err) 84 } 85 defer os.RemoveAll(codebaseDir) 86 gitDir := path.Join(codebaseDir, "project", ".git") 87 if err = os.MkdirAll(gitDir, os.ModePerm); err != nil { 88 t.Error(err) 89 } 90 repoDir := path.Join(codebaseDir, ".repo") 91 if err = os.Mkdir(repoDir, os.ModePerm); err != nil { 92 t.Error(err) 93 } 94 listContents := []byte("project") 95 listPath := path.Join(repoDir, "project.list") 96 if err = ioutil.WriteFile(listPath, listContents, os.ModePerm); err != nil { 97 t.Error(err) 98 } 99 _, err = codebase.Add("test-codebase", codebaseDir) 100 if err != nil { 101 t.Error(err) 102 } 103 // The top dir must be named "hacksaw" 104 // otherwise the mounters will reject any 105 // mount requests 106 wsTempDir, err := ioutil.TempDir("", "workspace") 107 if err != nil { 108 t.Error(err) 109 } 110 defer os.RemoveAll(wsTempDir) 111 wsTopDir := path.Join(wsTempDir, "hacksaw") 112 if err = os.Mkdir(wsTopDir, os.ModePerm); err != nil { 113 t.Error(err) 114 } 115 ws := New(bind.NewFakePathBinder(), wsTopDir) 116 _, err = ws.Create("test-workspace", "test-codebase") 117 if err != nil { 118 t.Error(err) 119 } 120 _, err = ws.Create("test-workspace", "test-codebase") 121 if err == nil { 122 t.Error("Allowed workspace duplicate") 123 } 124} 125 126func TestCreateWorkspaceFromBadCodebase(t *testing.T) { 127 defer config.GetConfig().Reset() 128 codebaseDir, err := ioutil.TempDir("", "test") 129 if err != nil { 130 t.Error(err) 131 } 132 defer os.RemoveAll(codebaseDir) 133 _, err = codebase.Add("test-codebase", codebaseDir) 134 if err != nil { 135 t.Error(err) 136 } 137 wsTempDir, err := ioutil.TempDir("", "workspace") 138 if err != nil { 139 t.Error(err) 140 } 141 defer os.RemoveAll(wsTempDir) 142 wsTopDir := path.Join(wsTempDir, "hacksaw") 143 if err = os.Mkdir(wsTopDir, os.ModePerm); err != nil { 144 t.Error(err) 145 } 146 ws := New(bind.NewFakePathBinder(), wsTopDir) 147 if _, err = ws.Create("test-workspace", "does-not-exist"); err == nil { 148 t.Error("Allowed bad codebase") 149 } 150} 151 152func TestList(t *testing.T) { 153 defer config.GetConfig().Reset() 154 codebaseDir, err := ioutil.TempDir("", "test") 155 if err != nil { 156 t.Error(err) 157 } 158 defer os.RemoveAll(codebaseDir) 159 gitDir := path.Join(codebaseDir, "project", ".git") 160 if err = os.MkdirAll(gitDir, os.ModePerm); err != nil { 161 t.Error(err) 162 } 163 repoDir := path.Join(codebaseDir, ".repo") 164 if err = os.Mkdir(repoDir, os.ModePerm); err != nil { 165 t.Error(err) 166 } 167 listContents := []byte("project") 168 listPath := path.Join(repoDir, "project.list") 169 if err = ioutil.WriteFile(listPath, listContents, os.ModePerm); err != nil { 170 t.Error(err) 171 } 172 _, err = codebase.Add("test-codebase", codebaseDir) 173 if err != nil { 174 t.Error(err) 175 } 176 wsTempDir, err := ioutil.TempDir("", "workspace") 177 if err != nil { 178 t.Error(err) 179 } 180 defer os.RemoveAll(wsTempDir) 181 wsTopDir := path.Join(wsTempDir, "hacksaw") 182 if err = os.Mkdir(wsTopDir, os.ModePerm); err != nil { 183 t.Error(err) 184 } 185 ws := New(bind.NewFakePathBinder(), wsTopDir) 186 if _, err = ws.Create("test-workspace", "test-codebase"); err != nil { 187 t.Error(err) 188 } 189 list := ws.List() 190 cb, ok := list["test-workspace"] 191 if !ok || cb != "test-codebase" { 192 t.Error("Added workspace not listed") 193 } 194} 195 196func TestRemove(t *testing.T) { 197 defer config.GetConfig().Reset() 198 codebaseDir, err := ioutil.TempDir("", "test") 199 if err != nil { 200 t.Error(err) 201 } 202 defer os.RemoveAll(codebaseDir) 203 gitDir := path.Join(codebaseDir, "project", ".git") 204 if err = os.MkdirAll(gitDir, os.ModePerm); err != nil { 205 t.Error(err) 206 } 207 repoDir := path.Join(codebaseDir, ".repo") 208 if err = os.Mkdir(repoDir, os.ModePerm); err != nil { 209 t.Error(err) 210 } 211 listContents := []byte("project") 212 listPath := path.Join(repoDir, "project.list") 213 if err = ioutil.WriteFile(listPath, listContents, os.ModePerm); err != nil { 214 t.Error(err) 215 } 216 _, err = codebase.Add("test-codebase", codebaseDir) 217 if err != nil { 218 t.Error(err) 219 } 220 wsTempDir, err := ioutil.TempDir("", "workspace") 221 if err != nil { 222 t.Error(err) 223 } 224 defer os.RemoveAll(wsTempDir) 225 wsTopDir := path.Join(wsTempDir, "hacksaw") 226 if err = os.Mkdir(wsTopDir, os.ModePerm); err != nil { 227 t.Error(err) 228 } 229 ws := New(bind.NewFakePathBinder(), wsTopDir) 230 if _, err = ws.Create("test-workspace", "test-codebase"); err != nil { 231 t.Error(err) 232 } 233 workspaceDir, err := ws.GetDir("test-workspace") 234 if err != nil { 235 t.Error(err) 236 } 237 _, err = os.Stat(workspaceDir) 238 if err != nil { 239 t.Error(err) 240 } 241 cfg, err := ws.Remove("test-workspace") 242 if err != nil { 243 t.Error(err) 244 } 245 _, ok := cfg.Workspaces["test-codebase"] 246 if ok { 247 t.Error("Removed workspace test-codebase is still in the configuration") 248 } 249 _, err = os.Stat(workspaceDir) 250 if err == nil { 251 t.Error("Workspace test-workspace was removed but its directory remains") 252 } else if os.IsNotExist(err) { 253 // This is the expected error 254 } else { 255 t.Error(err) 256 } 257} 258 259func TestEdit(t *testing.T) { 260 defer config.GetConfig().Reset() 261 codebaseDir, err := ioutil.TempDir("", "codebase") 262 if err != nil { 263 t.Error(err) 264 } 265 defer os.RemoveAll(codebaseDir) 266 projectDir := path.Join(codebaseDir, "project") 267 if err = os.MkdirAll(projectDir, os.ModePerm); err != nil { 268 t.Error(err) 269 } 270 cmd := exec.Command("git", "-C", projectDir, "init") 271 output, err := cmd.CombinedOutput() 272 if err != nil { 273 t.Errorf("Command\n%s\nfailed with the following:\n%s\n%s", 274 cmd.String(), err.Error(), output) 275 } 276 cmd = exec.Command("git", "-C", projectDir, "commit", `--message="Initial commit"`, "--allow-empty") 277 output, err = cmd.CombinedOutput() 278 if err != nil { 279 t.Errorf("Command\n%s\nfailed with the following:\n%s\n%s", 280 cmd.String(), err.Error(), output) 281 } 282 repoDir := path.Join(codebaseDir, ".repo") 283 if err = os.Mkdir(repoDir, os.ModePerm); err != nil { 284 t.Error(err) 285 } 286 listContents := []byte("project") 287 listPath := path.Join(repoDir, "project.list") 288 if err = ioutil.WriteFile(listPath, listContents, os.ModePerm); err != nil { 289 t.Error(err) 290 } 291 _, err = codebase.Add("test-codebase", codebaseDir) 292 if err != nil { 293 t.Error(err) 294 } 295 wsTempDir, err := ioutil.TempDir("", "workspace") 296 if err != nil { 297 t.Error(err) 298 } 299 defer os.RemoveAll(wsTempDir) 300 wsTopDir := path.Join(wsTempDir, "hacksaw") 301 if err = os.Mkdir(wsTopDir, os.ModePerm); err != nil { 302 t.Error(err) 303 } 304 ws := New(bind.NewFakePathBinder(), wsTopDir) 305 if _, err = ws.Create("test-workspace", "test-codebase"); err != nil { 306 t.Error(err) 307 } 308 workspaceDir, err := ws.GetDir("test-workspace") 309 if err != nil { 310 t.Error(err) 311 } 312 _, err = os.Stat(workspaceDir) 313 if err != nil { 314 t.Error(err) 315 } 316 editPath := path.Join(workspaceDir, "project") 317 branchName, wsProjectDir, err := ws.Edit(editPath) 318 if err != nil { 319 t.Error(err) 320 } 321 if branchName == "" { 322 t.Error("Editing returned an empty branch") 323 } 324 if wsProjectDir == "" { 325 t.Error("Editing returned an empty project path") 326 } 327 cmd = exec.Command("git", "-C", wsProjectDir, "show", branchName) 328 output, err = cmd.CombinedOutput() 329 if err != nil { 330 t.Errorf("Command\n%s\nfailed with the following:\n%s\n%s", 331 cmd.String(), err.Error(), output) 332 } 333 //Recreate workspace and try editing again 334 _, err = ws.Remove("test-workspace") 335 if err != nil { 336 t.Error(err) 337 } 338 _, err = ws.Create("test-workspace", "test-codebase") 339 if err != nil { 340 t.Error(err) 341 } 342 _, _, err = ws.Edit(editPath) 343 if err != nil { 344 t.Error(err) 345 } 346} 347 348const projectList = `read-only-project 349editable-project` 350 351func TestRecreate(t *testing.T) { 352 defer config.GetConfig().Reset() 353 codebaseDir, err := ioutil.TempDir("", "codebase") 354 if err != nil { 355 t.Error(err) 356 } 357 defer os.RemoveAll(codebaseDir) 358 roProjectDir := path.Join(codebaseDir, "read-only-project") 359 if err = os.MkdirAll(roProjectDir, os.ModePerm); err != nil { 360 t.Error(err) 361 } 362 cmd := exec.Command("git", "-C", roProjectDir, "init") 363 output, err := cmd.CombinedOutput() 364 if err != nil { 365 t.Errorf("Command\n%s\nfailed with the following:\n%s\n%s", 366 cmd.String(), err.Error(), output) 367 } 368 cmd = exec.Command("git", "-C", roProjectDir, "commit", `--message="Initial commit"`, "--allow-empty") 369 output, err = cmd.CombinedOutput() 370 if err != nil { 371 t.Errorf("Command\n%s\nfailed with the following:\n%s\n%s", 372 cmd.String(), err.Error(), output) 373 } 374 linkPath := path.Join(codebaseDir, "symlink") 375 if err = os.Symlink(roProjectDir, linkPath); err != nil { 376 t.Error(err) 377 } 378 rwProjectDir := path.Join(codebaseDir, "editable-project") 379 if err = os.MkdirAll(rwProjectDir, os.ModePerm); err != nil { 380 t.Error(err) 381 } 382 cmd = exec.Command("git", "-C", rwProjectDir, "init") 383 output, err = cmd.CombinedOutput() 384 if err != nil { 385 t.Errorf("Command\n%s\nfailed with the following:\n%s\n%s", 386 cmd.String(), err.Error(), output) 387 } 388 cmd = exec.Command("git", "-C", rwProjectDir, "commit", `--message="Initial commit"`, "--allow-empty") 389 output, err = cmd.CombinedOutput() 390 if err != nil { 391 t.Errorf("Command\n%s\nfailed with the following:\n%s\n%s", 392 cmd.String(), err.Error(), output) 393 } 394 repoDir := path.Join(codebaseDir, ".repo") 395 if err = os.Mkdir(repoDir, os.ModePerm); err != nil { 396 t.Error(err) 397 } 398 listContents := []byte(projectList) 399 listPath := path.Join(repoDir, "project.list") 400 if err = ioutil.WriteFile(listPath, listContents, os.ModePerm); err != nil { 401 t.Error(err) 402 } 403 _, err = codebase.Add("test-codebase", codebaseDir) 404 if err != nil { 405 t.Error(err) 406 } 407 wsTempDir, err := ioutil.TempDir("", "workspace") 408 if err != nil { 409 t.Error(err) 410 } 411 defer os.RemoveAll(wsTempDir) 412 wsTopDir := path.Join(wsTempDir, "hacksaw") 413 if err = os.Mkdir(wsTopDir, os.ModePerm); err != nil { 414 t.Error(err) 415 } 416 pathBinder := bind.NewFakePathBinder() 417 ws := New(pathBinder, wsTopDir) 418 if _, err = ws.Create("test-workspace", "test-codebase"); err != nil { 419 t.Error(err) 420 } 421 workspaceDir, err := ws.GetDir("test-workspace") 422 if err != nil { 423 t.Error(err) 424 } 425 editPath := path.Join(workspaceDir, "editable-project") 426 _, _, err = ws.Edit(editPath) 427 if err != nil { 428 t.Error(err) 429 } 430 emptyFilePath := path.Join(editPath, "empty-edit") 431 emptyFile, err := os.Create(emptyFilePath) 432 if err != nil { 433 t.Error(err) 434 } 435 emptyFile.Close() 436 if _, err = ws.Recreate("test-workspace"); err != nil { 437 t.Error(err) 438 } 439 _, err = os.Stat(emptyFilePath) 440 if err != nil { 441 t.Error(err) 442 } 443 wsRoProjectDir := path.Join(workspaceDir, "read-only-project") 444 isRoPathBound := false 445 pathList, err := pathBinder.List() 446 if err != nil { 447 t.Error(err) 448 } 449 for _, path := range pathList { 450 if path == wsRoProjectDir { 451 isRoPathBound = true 452 } 453 } 454 if !isRoPathBound { 455 t.Error("Read only project was not mounted to the workspace") 456 } 457} 458