| // Copyright 2018 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| package main |
| |
| import ( |
| "bufio" |
| "bytes" |
| "encoding/hex" |
| "fmt" |
| "io/ioutil" |
| "os" |
| "path" |
| "path/filepath" |
| "strings" |
| "testing" |
| |
| "go.fuchsia.dev/jiri/project" |
| ) |
| |
| func TestPrefixTree(t *testing.T) { |
| _, fakeroot, cleanup := setupUniverse(t) |
| defer cleanup() |
| |
| projects := []project.Project{ |
| {Name: "root", Path: "."}, |
| {Name: "a", Path: "a"}, |
| {Name: "b", Path: "b"}, |
| {Name: "c/d/e", Path: "c/d/e"}, |
| {Name: "c/d/f", Path: "c/d/f"}, |
| {Name: "c/d", Path: "c/d"}, |
| {Name: "c", Path: "c"}, |
| } |
| expectedDropped := []project.Project{ |
| projects[0], |
| projects[3], |
| projects[4], |
| projects[5], |
| } |
| |
| // Fill projects into prefix tree |
| root := projectTree{nil, make(map[string]*projectTree)} |
| dropped := make(project.Projects) |
| treeRoot := projectTreeRoot{&root, dropped} |
| for _, v := range projects { |
| if err := treeRoot.add(fakeroot.X, v); err != nil { |
| t.Errorf("adding project to prefixTree failed due to error: %v", err) |
| break |
| } |
| } |
| |
| // generate logs when test failed |
| failedDropped := func() { |
| t.Logf("wrong nested projects list") |
| t.Logf("expecting: ") |
| for _, v := range expectedDropped { |
| t.Logf("\tproject:%q", v.Path) |
| } |
| t.Logf("got:") |
| for _, v := range treeRoot.dropped { |
| t.Logf("\tproject:%q", v.Path) |
| } |
| t.Fail() |
| } |
| |
| // Verify nested projects |
| if len(treeRoot.dropped) != len(expectedDropped) { |
| failedDropped() |
| } |
| for _, v := range expectedDropped { |
| if _, ok := treeRoot.dropped[v.Key()]; !ok { |
| failedDropped() |
| break |
| } |
| } |
| |
| // Verify the shape of prefix tree |
| if len(root.children) == 3 { |
| prefixes := []string{"a", "b", "c"} |
| for _, v := range prefixes { |
| if _, ok := root.children[v]; !ok { |
| t.Errorf("root node does not contain project %q", v) |
| } |
| } |
| for _, v := range root.children { |
| if len(v.children) != 0 { |
| t.Errorf("more than 1 level of nodes found in prefix tree") |
| } |
| } |
| } else { |
| t.Errorf("expecting %v first level nodes, but got %v", 3, len(root.children)) |
| } |
| } |
| |
| func TestGitModules(t *testing.T) { |
| goldenScript := []byte(`#!/bin/sh |
| git update-index --add --cacheinfo 160000 87326c54332e5be21eda2173bb001aaee73a9ab7 "manifest" |
| git update-index --add --cacheinfo 160000 87f863bcbc7cd2177bac17c61e31093de6eeed28 "path-0" |
| git update-index --add --cacheinfo 160000 87f863bcbc7cd2177bac17c61e31093de6eeed28 "path-1" |
| git update-index --add --cacheinfo 160000 87f863bcbc7cd2177bac17c61e31093de6eeed28 "path-2"`) |
| |
| goldenModule := []byte(`[submodule "manifest-31b42b3e96"] |
| path = manifest |
| url = /tmp/115893653/manifest |
| [submodule "project-0-50299b644d"] |
| path = path-0 |
| url = /tmp/115893653/project-0 |
| [submodule "project-1-743aa3e46a"] |
| path = path-1 |
| url = /tmp/115893653/project-1 |
| [submodule "project-2-1615f49046"] |
| path = path-2 |
| url = /tmp/115893653/project-2`) |
| |
| goldenAttributes := []byte(`manifest manifest public |
| path-0 manifest public |
| path-1 manifest public |
| path-2 manifest public |
| `) |
| |
| // Setup fake workspace and update $JIRI_ROOT |
| _, fakeroot, cleanup := setupUniverse(t) |
| defer cleanup() |
| if err := fakeroot.UpdateUniverse(false); err != nil { |
| t.Errorf("%v", err) |
| } |
| |
| localProjects, err := project.LocalProjects(fakeroot.X, project.FullScan) |
| if err != nil { |
| t.Errorf("scanning local fake project failed due to error %v", err) |
| } |
| |
| pathMap := make(map[string]project.Project) |
| for _, v := range localProjects { |
| v.Path, err = makePathRel(fakeroot.X.Root, v.Path) |
| if err != nil { |
| t.Errorf("path relativation failed due to error %v", err) |
| } |
| pathMap[v.Path] = v |
| } |
| |
| tempDir, err := ioutil.TempDir("", "gitmodules") |
| if err != nil { |
| t.Errorf(".gitmodules generation failed due to error %v", err) |
| } |
| defer os.RemoveAll(tempDir) |
| |
| genGitModuleFlags.genScript = path.Join(tempDir, "setup.sh") |
| err = runGenGitModule(fakeroot.X, []string{ |
| path.Join(tempDir, ".gitmodules"), |
| path.Join(tempDir, ".gitattributes"), |
| }) |
| if err != nil { |
| t.Errorf(".gitmodules generation failed due to error %v", err) |
| } |
| |
| // Read and verify content of generated script |
| data, err := ioutil.ReadFile(genGitModuleFlags.genScript) |
| if err != nil { |
| t.Errorf("reading generated script file failed due to error: %v", err) |
| } |
| t.Logf("generated script content \n%s\n", string(data)) |
| |
| if err := verifyScript(goldenScript, data); err != nil { |
| t.Errorf("verifying generated script failed due to error: %v", err) |
| } |
| |
| // Read and verify content of generated .gitmodules file |
| data, err = ioutil.ReadFile(path.Join(tempDir, ".gitmodules")) |
| if err != nil { |
| t.Errorf("reading generated .gitmodules file failed due to error: %v", err) |
| } |
| t.Logf("generated gitmodule content \n%s\n", string(data)) |
| |
| if err := verifyModules(goldenModule, data); err != nil { |
| t.Errorf("verifying generated .gitmodules failed due to error: %v", err) |
| } |
| |
| // Read and verify content of generated .gitattributes file |
| data, err = ioutil.ReadFile(path.Join(tempDir, ".gitattributes")) |
| if err != nil { |
| t.Errorf("reading generated .gitattributes file failed due to error: %v", err) |
| } |
| t.Logf("generated gitattributes content \n%s\n", string(data)) |
| if bytes.Compare(data, goldenAttributes) != 0 { |
| t.Errorf("verfying generated .gitattributes failed. Expecting: %q, got %q", string(goldenAttributes), string(data)) |
| } |
| } |
| |
| func readlines(data []byte) ([]string, error) { |
| var buffer bytes.Buffer |
| retLines := make([]string, 0) |
| if _, err := buffer.Write(data); err != nil { |
| return nil, err |
| } |
| scanner := bufio.NewScanner(&buffer) |
| for scanner.Scan() { |
| line := strings.TrimSpace(scanner.Text()) |
| if len(line) == 0 || line[0] == '#' { |
| continue |
| } |
| retLines = append(retLines, line) |
| } |
| return retLines, nil |
| } |
| |
| func verifyModules(golden, tests []byte) error { |
| goldenLines, err := readlines(golden) |
| if err != nil { |
| return err |
| } |
| testLines, err := readlines(tests) |
| if err != nil { |
| return err |
| } |
| if len(goldenLines) != len(testLines) { |
| return fmt.Errorf("expecting %q non-empty/non-comment lines from generated .gitmodules, got %q lines", len(goldenLines), len(testLines)) |
| } |
| for i := 0; i < len(goldenLines); i++ { |
| goldenLine := goldenLines[i] |
| testLine := testLines[i] |
| if strings.HasPrefix(testLine, "branch = ") { |
| revision := testLine[len("branch = "):] |
| // revision should be 20 bytes in hex format |
| if len(revision) != 40 { |
| return fmt.Errorf("illegal revision hash in line %q", testLine) |
| } |
| if _, err := hex.DecodeString(revision); err != nil { |
| return fmt.Errorf("illegal revision hash in line %q", testLine) |
| } |
| continue |
| } |
| if strings.HasPrefix(testLine, "url = ") { |
| testPath := testLine[len("url = "):] |
| goldenPath := goldenLine[len("url = "):] |
| testPathFields := strings.Split(testPath, string(filepath.Separator)) |
| goldenPathFields := strings.Split(goldenPath, string(filepath.Separator)) |
| testPath = testPathFields[len(testPathFields)-1] |
| goldenPath = goldenPathFields[len(goldenPathFields)-1] |
| if testPath != goldenPath { |
| return fmt.Errorf("path mismatch, expecting %q, got %q", goldenPath, testPath) |
| } |
| continue |
| } |
| if strings.HasPrefix(testLine, "[submodule ") { |
| testProjectName := testLine[:strings.LastIndex(testLine, "-")] |
| goldenProjectName := goldenLine[:strings.LastIndex(testLine, "-")] |
| if testProjectName != goldenProjectName { |
| return fmt.Errorf("project name mismatch, expection %q, got %q", goldenProjectName, testProjectName) |
| } |
| continue |
| } |
| if goldenLine != testLine { |
| return fmt.Errorf("in generated .gitmodules file, expecting %q, got %q", goldenLine, testLine) |
| } |
| } |
| return nil |
| } |
| |
| func verifyScript(golden, tests []byte) error { |
| goldenLines, err := readlines(golden) |
| if err != nil { |
| return err |
| } |
| testLines, err := readlines(tests) |
| if err != nil { |
| return err |
| } |
| if len(goldenLines) != len(testLines) { |
| return fmt.Errorf("expecting %q non-empty/non-comment lines from generated script, got %q lines", len(goldenLines), len(testLines)) |
| } |
| for i := 0; i < len(goldenLines); i++ { |
| goldenLine := goldenLines[i] |
| testLine := testLines[i] |
| goldenFields := strings.Fields(goldenLine) |
| testFields := strings.Fields(testLine) |
| if len(goldenFields) != len(testFields) { |
| return fmt.Errorf("format error at line %q in generated script, expecting something like %q", testLine, goldenLine) |
| } |
| // Any field except the revision hash should be exact match. |
| for j := 0; j < 5; j++ { |
| if goldenFields[j] != testFields[j] { |
| return fmt.Errorf("command missmatch at line %q in generated script, expecting something like %q", testLine, goldenLine) |
| } |
| } |
| if goldenFields[6] != testFields[6] { |
| return fmt.Errorf("command missmatch at line %q in generated script, expecting something like %q", testLine, goldenLine) |
| } |
| // revision should be 20 bytes in hex format |
| if len(testFields[5]) != 40 { |
| return fmt.Errorf("illegal revision hash in line %q", testLine) |
| } |
| if _, err := hex.DecodeString(testFields[5]); err != nil { |
| return fmt.Errorf("illegal revision hash in git command %q", testLine) |
| } |
| } |
| return nil |
| } |