github.com/btwiuse/jiri@v0.0.0-20191125065820-53353bcfef54/cmd/jiri/generate-gitmodules_test.go (about)

     1  // Copyright 2018 The Fuchsia Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package main
     6  
     7  import (
     8  	"bufio"
     9  	"bytes"
    10  	"encoding/hex"
    11  	"fmt"
    12  	"io/ioutil"
    13  	"os"
    14  	"path"
    15  	"path/filepath"
    16  	"strings"
    17  	"testing"
    18  
    19  	"github.com/btwiuse/jiri/project"
    20  )
    21  
    22  func TestPrefixTree(t *testing.T) {
    23  	_, fakeroot, cleanup := setupUniverse(t)
    24  	defer cleanup()
    25  
    26  	projects := []project.Project{
    27  		project.Project{Name: "root", Path: "."},
    28  		project.Project{Name: "a", Path: "a"},
    29  		project.Project{Name: "b", Path: "b"},
    30  		project.Project{Name: "c/d/e", Path: "c/d/e"},
    31  		project.Project{Name: "c/d/f", Path: "c/d/f"},
    32  		project.Project{Name: "c/d", Path: "c/d"},
    33  		project.Project{Name: "c", Path: "c"},
    34  	}
    35  	expectedDropped := []project.Project{
    36  		projects[0],
    37  		projects[3],
    38  		projects[4],
    39  		projects[5],
    40  	}
    41  
    42  	// Fill projects into prefix tree
    43  	root := projectTree{nil, make(map[string]*projectTree)}
    44  	dropped := make(project.Projects)
    45  	treeRoot := projectTreeRoot{&root, dropped}
    46  	for _, v := range projects {
    47  		if err := treeRoot.add(fakeroot.X, v); err != nil {
    48  			t.Errorf("adding project to prefixTree failed due to error: %v", err)
    49  			break
    50  		}
    51  	}
    52  
    53  	// generate logs when test failed
    54  	failedDropped := func() {
    55  		t.Logf("wrong nested projects list")
    56  		t.Logf("expecting: ")
    57  		for _, v := range expectedDropped {
    58  			t.Logf("\tproject:%q", v.Path)
    59  		}
    60  		t.Logf("got:")
    61  		for _, v := range treeRoot.dropped {
    62  			t.Logf("\tproject:%q", v.Path)
    63  		}
    64  		t.Fail()
    65  	}
    66  
    67  	// Verify nested projects
    68  	if len(treeRoot.dropped) != len(expectedDropped) {
    69  		failedDropped()
    70  	}
    71  	for _, v := range expectedDropped {
    72  		if _, ok := treeRoot.dropped[v.Key()]; !ok {
    73  			failedDropped()
    74  			break
    75  		}
    76  	}
    77  
    78  	// Verify the shape of prefix tree
    79  	if len(root.children) == 3 {
    80  		prefixes := []string{"a", "b", "c"}
    81  		for _, v := range prefixes {
    82  			if _, ok := root.children[v]; !ok {
    83  				t.Errorf("root node does not contain project %q", v)
    84  			}
    85  		}
    86  		for _, v := range root.children {
    87  			if len(v.children) != 0 {
    88  				t.Errorf("more than 1 level of nodes found in prefix tree")
    89  			}
    90  		}
    91  	} else {
    92  		t.Errorf("expecting %v first level nodes, but got %v", 3, len(root.children))
    93  	}
    94  }
    95  
    96  func TestGitModules(t *testing.T) {
    97  	goldenScript := []byte(`#!/bin/sh
    98  git update-index --add --cacheinfo 160000 87326c54332e5be21eda2173bb001aaee73a9ab7 "manifest"
    99  git update-index --add --cacheinfo 160000 87f863bcbc7cd2177bac17c61e31093de6eeed28 "path-0"
   100  git update-index --add --cacheinfo 160000 87f863bcbc7cd2177bac17c61e31093de6eeed28 "path-1"
   101  git update-index --add --cacheinfo 160000 87f863bcbc7cd2177bac17c61e31093de6eeed28 "path-2"`)
   102  
   103  	goldenModule := []byte(`[submodule "manifest"]
   104  	branch = 87326c54332e5be21eda2173bb001aaee73a9ab7
   105  	path = manifest
   106  	url = /tmp/115893653/manifest
   107  [submodule "project-0"]
   108  	branch = 87f863bcbc7cd2177bac17c61e31093de6eeed28
   109  	path = path-0
   110  	url = /tmp/115893653/project-0
   111  [submodule "project-1"]
   112  	branch = 87f863bcbc7cd2177bac17c61e31093de6eeed28
   113  	path = path-1
   114  	url = /tmp/115893653/project-1
   115  [submodule "project-2"]
   116  	branch = 87f863bcbc7cd2177bac17c61e31093de6eeed28
   117  	path = path-2
   118  	url = /tmp/115893653/project-2`)
   119  
   120  	goldenAttributes := []byte(`manifest manifest public
   121  path-0 manifest public
   122  path-1 manifest public
   123  path-2 manifest public
   124  `)
   125  
   126  	// Setup fake workspace and update $JIRI_ROOT
   127  	_, fakeroot, cleanup := setupUniverse(t)
   128  	defer cleanup()
   129  	if err := fakeroot.UpdateUniverse(false); err != nil {
   130  		t.Errorf("%v", err)
   131  	}
   132  
   133  	localProjects, err := project.LocalProjects(fakeroot.X, project.FullScan)
   134  	if err != nil {
   135  		t.Errorf("scanning local fake project failed due to error %v", err)
   136  	}
   137  
   138  	pathMap := make(map[string]project.Project)
   139  	for _, v := range localProjects {
   140  		v.Path, err = makePathRel(fakeroot.X.Root, v.Path)
   141  		if err != nil {
   142  			t.Errorf("path relativation failed due to error %v", err)
   143  		}
   144  		pathMap[v.Path] = v
   145  	}
   146  
   147  	tempDir, err := ioutil.TempDir("", "gitmodules")
   148  	if err != nil {
   149  		t.Errorf(".gitmodules generation failed due to error %v", err)
   150  	}
   151  	defer os.RemoveAll(tempDir)
   152  
   153  	genGitModuleFlags.genScript = path.Join(tempDir, "setup.sh")
   154  	err = runGenGitModule(fakeroot.X, []string{
   155  		path.Join(tempDir, ".gitmodules"),
   156  		path.Join(tempDir, ".gitattributes"),
   157  	})
   158  	if err != nil {
   159  		t.Errorf(".gitmodules generation failed due to error %v", err)
   160  	}
   161  
   162  	// Read and verify content of generated script
   163  	data, err := ioutil.ReadFile(genGitModuleFlags.genScript)
   164  	if err != nil {
   165  		t.Errorf("reading generated script file failed due to error: %v", err)
   166  	}
   167  	t.Logf("generated script content \n%s\n", string(data))
   168  
   169  	if err := verifyScript(goldenScript, data); err != nil {
   170  		t.Errorf("verifying generated script failed due to error: %v", err)
   171  	}
   172  
   173  	// Read and verify content of generated .gitmodules file
   174  	data, err = ioutil.ReadFile(path.Join(tempDir, ".gitmodules"))
   175  	if err != nil {
   176  		t.Errorf("reading generated .gitmodules file failed due to error: %v", err)
   177  	}
   178  	t.Logf("generated gitmodule content \n%s\n", string(data))
   179  
   180  	if err := verifyModules(goldenModule, data); err != nil {
   181  		t.Errorf("verifying generated .gitmodules failed due to error: %v", err)
   182  	}
   183  
   184  	// Read and verify content of generated .gitattributes file
   185  	data, err = ioutil.ReadFile(path.Join(tempDir, ".gitattributes"))
   186  	if err != nil {
   187  		t.Errorf("reading generated .gitattributes file failed due to error: %v", err)
   188  	}
   189  	t.Logf("generated gitattributes content \n%s\n", string(data))
   190  	if bytes.Compare(data, goldenAttributes) != 0 {
   191  		t.Errorf("verfying generated .gitattributes failed. Expecting: %q, got %q", string(goldenAttributes), string(data))
   192  	}
   193  }
   194  
   195  func readlines(data []byte) ([]string, error) {
   196  	var buffer bytes.Buffer
   197  	retLines := make([]string, 0)
   198  	if _, err := buffer.Write(data); err != nil {
   199  		return nil, err
   200  	}
   201  	scanner := bufio.NewScanner(&buffer)
   202  	for scanner.Scan() {
   203  		line := strings.TrimSpace(scanner.Text())
   204  		if len(line) == 0 || line[0] == '#' {
   205  			continue
   206  		}
   207  		retLines = append(retLines, line)
   208  	}
   209  	return retLines, nil
   210  }
   211  
   212  func verifyModules(golden, tests []byte) error {
   213  	goldenLines, err := readlines(golden)
   214  	if err != nil {
   215  		return err
   216  	}
   217  	testLines, err := readlines(tests)
   218  	if err != nil {
   219  		return err
   220  	}
   221  	if len(goldenLines) != len(testLines) {
   222  		return fmt.Errorf("expecting %q non-empty/non-comment lines from generated .gitmodules, got %q lines", len(goldenLines), len(testLines))
   223  	}
   224  	for i := 0; i < len(goldenLines); i++ {
   225  		goldenLine := goldenLines[i]
   226  		testLine := testLines[i]
   227  		if strings.HasPrefix(testLine, "branch = ") {
   228  			revision := testLine[len("branch = "):]
   229  			// revision should be 20 bytes in hex format
   230  			if len(revision) != 40 {
   231  				return fmt.Errorf("illegal revision hash in line %q", testLine)
   232  			}
   233  			if _, err := hex.DecodeString(revision); err != nil {
   234  				return fmt.Errorf("illegal revision hash in line %q", testLine)
   235  			}
   236  			continue
   237  		}
   238  		if strings.HasPrefix(testLine, "url = ") {
   239  			testPath := testLine[len("url = "):]
   240  			goldenPath := goldenLine[len("url = "):]
   241  			testPathFields := strings.Split(testPath, string(filepath.Separator))
   242  			goldenPathFields := strings.Split(goldenPath, string(filepath.Separator))
   243  			testPath = testPathFields[len(testPathFields)-1]
   244  			goldenPath = goldenPathFields[len(goldenPathFields)-1]
   245  			if testPath != goldenPath {
   246  				return fmt.Errorf("path mismatch, expecting %q, got %q", goldenPath, testPath)
   247  			}
   248  			continue
   249  		}
   250  		if goldenLine != testLine {
   251  			return fmt.Errorf("in generated .gitmodules file, expecting %q, got %q", goldenLine, testLine)
   252  		}
   253  	}
   254  	return nil
   255  }
   256  
   257  func verifyScript(golden, tests []byte) error {
   258  	goldenLines, err := readlines(golden)
   259  	if err != nil {
   260  		return err
   261  	}
   262  	testLines, err := readlines(tests)
   263  	if err != nil {
   264  		return err
   265  	}
   266  	if len(goldenLines) != len(testLines) {
   267  		return fmt.Errorf("expecting %q non-empty/non-comment lines from generated script, got %q lines", len(goldenLines), len(testLines))
   268  	}
   269  	for i := 0; i < len(goldenLines); i++ {
   270  		goldenLine := goldenLines[i]
   271  		testLine := testLines[i]
   272  		goldenFields := strings.Fields(goldenLine)
   273  		testFields := strings.Fields(testLine)
   274  		if len(goldenFields) != len(testFields) {
   275  			return fmt.Errorf("format error at line %q in generated script, expecting something like %q", testLine, goldenLine)
   276  		}
   277  		// Any field except the revision hash should be exact match.
   278  		for j := 0; j < 5; j++ {
   279  			if goldenFields[j] != testFields[j] {
   280  				return fmt.Errorf("command missmatch at line %q in generated script, expecting something like %q", testLine, goldenLine)
   281  			}
   282  		}
   283  		if goldenFields[6] != testFields[6] {
   284  			return fmt.Errorf("command missmatch at line %q in generated script, expecting something like %q", testLine, goldenLine)
   285  		}
   286  		// revision should be 20 bytes in hex format
   287  		if len(testFields[5]) != 40 {
   288  			return fmt.Errorf("illegal revision hash in line %q", testLine)
   289  		}
   290  		if _, err := hex.DecodeString(testFields[5]); err != nil {
   291  			return fmt.Errorf("illegal revision hash in git command %q", testLine)
   292  		}
   293  	}
   294  	return nil
   295  }