github.com/vanadium-archive/go.jiri@v0.0.0-20160715023856-abfb8b131290/profiles/profilescmdline/manager_test.go (about)

     1  // Copyright 2015 The Vanadium 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 profilescmdline_test
     6  
     7  import (
     8  	"bufio"
     9  	"bytes"
    10  	"flag"
    11  	"fmt"
    12  	"io/ioutil"
    13  	"os"
    14  	"path/filepath"
    15  	"reflect"
    16  	"regexp"
    17  	"runtime"
    18  	"strings"
    19  	"sync"
    20  	"testing"
    21  
    22  	"v.io/jiri"
    23  	"v.io/jiri/jiritest"
    24  	"v.io/jiri/profiles"
    25  	"v.io/jiri/profiles/profilescmdline"
    26  	"v.io/jiri/profiles/profilesreader"
    27  	"v.io/x/lib/envvar"
    28  	"v.io/x/lib/gosh"
    29  )
    30  
    31  func TestManagerArgs(t *testing.T) {
    32  	profilescmdline.Reset()
    33  	p := parent
    34  	profilescmdline.RegisterManagementCommands(&p, false, "", "", jiri.ProfilesRootDir)
    35  	if got, want := len(p.Children), 6; got != want {
    36  		t.Errorf("got %v, want %v", got, want)
    37  	}
    38  	type cl struct {
    39  		args string
    40  		n    int
    41  	}
    42  	cls := map[string]cl{
    43  		"install":   cl{"--profiles-db=db --profiles-dir=root --target=arch-os --env=a=b,c=d --force=false", 5},
    44  		"uninstall": cl{"--profiles-db=db --profiles-dir=root --target=arch-os --all-targets --v", 5},
    45  		"cleanup":   cl{"--profiles-db=db --profiles-dir=root --gc --rm-all --v", 5},
    46  		"update":    cl{"--profiles-db=db --profiles-dir=root -v", 3},
    47  		"available": cl{"-v", 1},
    48  	}
    49  	for _, c := range p.Children {
    50  		args := cls[c.Name].args
    51  		if err := c.Flags.Parse(strings.Split(args, " ")); err != nil {
    52  			t.Errorf("failed to parse for %s: %s: %v", c.Name, args, err)
    53  			continue
    54  		}
    55  		if got, want := c.Flags.NFlag(), cls[c.Name].n; got != want {
    56  			t.Errorf("%s: got %v, want %v", c.Name, got, want)
    57  		}
    58  	}
    59  }
    60  
    61  var (
    62  	buildInstallersOnce, buildJiriOnce     sync.Once
    63  	buildInstallersBinDir, buildJiriBinDir = "", ""
    64  )
    65  
    66  // TestMain must cleanup these directories created by this function.
    67  func buildInstallers(t *testing.T) string {
    68  	buildInstallersOnce.Do(func() {
    69  		binDir, err := ioutil.TempDir("", "")
    70  		if err != nil {
    71  			t.Fatal(err)
    72  		}
    73  		sh := gosh.NewShell(t)
    74  		defer sh.Cleanup()
    75  		prefix := "v.io/jiri/profiles/profilescmdline/internal/"
    76  		gosh.BuildGoPkg(sh, binDir, "v.io/jiri/cmd/jiri", "-o", "jiri")
    77  		gosh.BuildGoPkg(sh, binDir, prefix+"i1", "-o", "jiri-profile-i1")
    78  		gosh.BuildGoPkg(sh, binDir, prefix+"i2", "-o", "jiri-profile-i2")
    79  		buildInstallersBinDir = binDir
    80  	})
    81  	return buildInstallersBinDir
    82  }
    83  
    84  func TestMain(m *testing.M) {
    85  	flag.Parse()
    86  	r := m.Run()
    87  	if buildInstallersBinDir != "" {
    88  		os.RemoveAll(buildInstallersBinDir)
    89  	}
    90  	os.Exit(r)
    91  }
    92  
    93  func createProfilesDB(t *testing.T, jirix *jiri.X) {
    94  	if err := os.MkdirAll(jirix.ProfilesDBDir(), os.FileMode(0755)); err != nil {
    95  		t.Fatalf("%s:%s", loc(), err)
    96  	}
    97  }
    98  
    99  func buildJiri(t *testing.T) string {
   100  	buildJiriOnce.Do(func() {
   101  		binDir, err := ioutil.TempDir("", "")
   102  		if err != nil {
   103  			t.Fatal(err)
   104  		}
   105  		sh := gosh.NewShell(t)
   106  		defer sh.Cleanup()
   107  		gosh.BuildGoPkg(sh, binDir, "v.io/jiri/cmd/jiri", "-o", "jiri")
   108  		buildJiriBinDir = binDir
   109  	})
   110  	return buildJiriBinDir
   111  }
   112  
   113  func run(sh *gosh.Shell, dir, bin string, args ...string) string {
   114  	cmd := sh.Cmd(filepath.Join(dir, bin), args...)
   115  	if testing.Verbose() {
   116  		cmd.PropagateOutput = true
   117  	}
   118  	return cmd.CombinedOutput()
   119  }
   120  
   121  func TestManagerAvailable(t *testing.T) {
   122  	fake, cleanup := jiritest.NewFakeJiriRoot(t)
   123  	defer cleanup()
   124  	dir, sh := buildInstallers(t), gosh.NewShell(t)
   125  	createProfilesDB(t, fake.X)
   126  	sh.Vars["JIRI_ROOT"] = fake.X.Root
   127  	sh.Vars["PATH"] = envvar.PrependUniqueToken(sh.Vars["PATH"], ":", dir)
   128  	stdout := run(sh, dir, "jiri", "profile", "available", "-v")
   129  	for _, installer := range []string{"i1", "i2"} {
   130  		re := regexp.MustCompile("Available Subcommands:.*profile-" + installer + ".*\n")
   131  		if got := stdout; !re.MatchString(got) {
   132  			t.Errorf("%v does not match %v\n", got, re.String())
   133  		}
   134  		if got, want := stdout, installer+":eg"; !strings.Contains(got, want) {
   135  			t.Errorf("%v does not contain %v\n", got, want)
   136  		}
   137  	}
   138  }
   139  
   140  func loc() string {
   141  	_, file, line, _ := runtime.Caller(2)
   142  	return fmt.Sprintf("%s:%d", filepath.Base(file), line)
   143  }
   144  func exists(filename string) bool {
   145  	_, err := os.Stat(filename)
   146  	return err == nil
   147  }
   148  
   149  func contains(t *testing.T, filename, want string) {
   150  	o, err := ioutil.ReadFile(filename)
   151  	if err != nil {
   152  		t.Fatal(err)
   153  	}
   154  	if got := string(o); got != want {
   155  		t.Errorf("%s: %s: got %v, want %v", loc(), filename, got, want)
   156  	}
   157  }
   158  
   159  func cat(msg, filename string) {
   160  	o, err := ioutil.ReadFile(filename)
   161  	if err != nil {
   162  		panic(err)
   163  	}
   164  	fmt.Fprintln(os.Stderr, msg)
   165  	fmt.Fprintln(os.Stderr, string(o))
   166  	fmt.Fprintln(os.Stderr, msg)
   167  }
   168  
   169  func removeDate(s string) string {
   170  	var result bytes.Buffer
   171  	scanner := bufio.NewScanner(bytes.NewBufferString(s))
   172  	re := regexp.MustCompile("(.*) date=\".*\"")
   173  	for scanner.Scan() {
   174  		result.WriteString(re.ReplaceAllString(scanner.Text(), "$1"))
   175  		result.WriteString("\n")
   176  	}
   177  	return strings.TrimSpace(result.String())
   178  }
   179  
   180  func cmpFiles(t *testing.T, gotFilename, wantFilename string) {
   181  	g, err := ioutil.ReadFile(gotFilename)
   182  	if err != nil {
   183  		t.Fatalf("%s: %s", loc(), err)
   184  	}
   185  	w, err := ioutil.ReadFile(wantFilename)
   186  	if err != nil {
   187  		t.Fatalf("%s: %s", loc(), err)
   188  	}
   189  	if got, want := removeDate(strings.TrimSpace(string(g))), removeDate(strings.TrimSpace(string(w))); got != want {
   190  		t.Errorf("%s: got %v, want %v from %q and %q", loc(), got, want, gotFilename, wantFilename)
   191  	}
   192  }
   193  
   194  func TestManagerInstallUninstall(t *testing.T) {
   195  	fake, cleanup := jiritest.NewFakeJiriRoot(t)
   196  	defer cleanup()
   197  	dir, sh := buildInstallers(t), gosh.NewShell(t)
   198  	createProfilesDB(t, fake.X)
   199  	sh.Vars["JIRI_ROOT"] = fake.X.Root
   200  	sh.Vars["PATH"] = envvar.PrependUniqueToken(sh.Vars["PATH"], ":", dir)
   201  
   202  	run(sh, dir, "jiri", "profile", "list", "-v")
   203  
   204  	i1 := filepath.Join(fake.X.Root, ".jiri_root/profile_db/i1")
   205  	i2 := filepath.Join(fake.X.Root, ".jiri_root/profile_db/i2")
   206  
   207  	run(sh, dir, "jiri", "profile", "install", "--target=arch-os", "i1:eg", "i2:eg")
   208  	for _, installer := range []string{"i1", "i2"} {
   209  		tdir := filepath.Join(fake.X.Root, jiri.ProfilesRootDir, installer, "eg", "arch_os")
   210  		contains(t, filepath.Join(tdir, "version"), "3")
   211  		contains(t, filepath.Join(tdir, "3"), "3")
   212  	}
   213  	cmpFiles(t, i1, filepath.Join("testdata", "i1a.xml"))
   214  	cmpFiles(t, i2, filepath.Join("testdata", "i2a.xml"))
   215  
   216  	run(sh, dir, "jiri", "profile", "install", "--target=arch-os@2", "i1:eg", "i2:eg")
   217  	// Installs are idempotent.
   218  	run(sh, dir, "jiri", "profile", "install", "--target=arch-os@2", "i1:eg", "i2:eg")
   219  	for _, installer := range []string{"i1", "i2"} {
   220  		tdir := filepath.Join(fake.X.Root, jiri.ProfilesRootDir, installer, "eg", "arch_os")
   221  		contains(t, filepath.Join(tdir, "version"), "2")
   222  		contains(t, filepath.Join(tdir, "3"), "3")
   223  		contains(t, filepath.Join(tdir, "2"), "2")
   224  	}
   225  
   226  	cmpFiles(t, i1, filepath.Join("testdata", "i1b.xml"))
   227  	cmpFiles(t, i2, filepath.Join("testdata", "i2b.xml"))
   228  
   229  	run(sh, dir, "jiri", "profile", "uninstall", "--target=arch-os@2", "i1:eg", "i2:eg")
   230  	for _, installer := range []string{"i1", "i2"} {
   231  		tdir := filepath.Join(fake.X.Root, jiri.ProfilesRootDir, installer, "eg", "arch_os")
   232  		contains(t, filepath.Join(tdir, "version"), "2")
   233  		contains(t, filepath.Join(tdir, "3"), "3")
   234  		if got, want := exists(filepath.Join(tdir, "2")), false; got != want {
   235  			t.Errorf("%s: got %v, want %v", filepath.Join(tdir, "2"), got, want)
   236  		}
   237  	}
   238  	cmpFiles(t, i1, filepath.Join("testdata", "i1c.xml"))
   239  	cmpFiles(t, i2, filepath.Join("testdata", "i2c.xml"))
   240  
   241  	// Put v2 back.
   242  	run(sh, dir, "jiri", "profile", "list", "-v")
   243  	run(sh, dir, "jiri", "profile", "install", "--target=arch-os@2", "i1:eg", "i2:eg")
   244  	cmpFiles(t, i1, filepath.Join("testdata", "i1b.xml"))
   245  	cmpFiles(t, i2, filepath.Join("testdata", "i2b.xml"))
   246  }
   247  
   248  func TestManagerUpdate(t *testing.T) {
   249  	fake, cleanup := jiritest.NewFakeJiriRoot(t)
   250  	defer cleanup()
   251  	dir, sh := buildInstallers(t), gosh.NewShell(t)
   252  	createProfilesDB(t, fake.X)
   253  	sh.Vars["JIRI_ROOT"] = fake.X.Root
   254  	sh.Vars["PATH"] = envvar.PrependUniqueToken(sh.Vars["PATH"], ":", dir)
   255  
   256  	i1 := filepath.Join(fake.X.ProfilesDBDir(), "i1")
   257  	i2 := filepath.Join(fake.X.ProfilesDBDir(), "i2")
   258  
   259  	run(sh, dir, "jiri", "profile", "install", "--target=arch-os@2", "i1:eg", "i2:eg")
   260  	cmpFiles(t, i1, filepath.Join("testdata", "i1d.xml"))
   261  	cmpFiles(t, i2, filepath.Join("testdata", "i2d.xml"))
   262  
   263  	run(sh, dir, "jiri", "profile", "update")
   264  	cmpFiles(t, i1, filepath.Join("testdata", "i1e.xml"))
   265  	cmpFiles(t, i2, filepath.Join("testdata", "i2e.xml"))
   266  
   267  	run(sh, dir, "jiri", "profile", "cleanup", "-gc")
   268  	cmpFiles(t, i1, filepath.Join("testdata", "i1f.xml"))
   269  	cmpFiles(t, i2, filepath.Join("testdata", "i2f.xml"))
   270  
   271  	run(sh, dir, "jiri", "profile", "cleanup", "-rm-all")
   272  	profiledir := filepath.Join(fake.X.Root, jiri.ProfilesRootDir)
   273  	for _, dir := range []string{
   274  		fake.X.ProfilesDBDir(),
   275  		filepath.Join(profiledir, "i1"),
   276  		filepath.Join(profiledir, "i2"),
   277  	} {
   278  		_, err := os.Stat(dir)
   279  		if !os.IsNotExist(err) {
   280  			t.Errorf("%v still exists: %v", dir, err)
   281  		}
   282  	}
   283  	// Start over, make sure update is idempotent.
   284  	createProfilesDB(t, fake.X)
   285  	run(sh, dir, "jiri", "profile", "install", "--target=arch-os@2", "i1:eg")
   286  	run(sh, dir, "jiri", "profile", "update")
   287  	run(sh, dir, "jiri", "profile", "update")
   288  	cmpFiles(t, i1, filepath.Join("testdata", "i1g.xml"))
   289  	run(sh, dir, "jiri", "profile", "install", "--target=arch-os@4", "i1:eg")
   290  	run(sh, dir, "jiri", "profile", "update")
   291  	cmpFiles(t, i1, filepath.Join("testdata", "i1h.xml"))
   292  }
   293  
   294  func TestList(t *testing.T) {
   295  	fake, cleanup := jiritest.NewFakeJiriRoot(t)
   296  	defer cleanup()
   297  	dir, sh := buildInstallers(t), gosh.NewShell(t)
   298  	sh.Vars["JIRI_ROOT"] = fake.X.Root
   299  	sh.Vars["PATH"] = envvar.PrependUniqueToken(os.Getenv("PATH"), ":", dir)
   300  	run(sh, dir, "jiri", "profile", "install", "--target=arch-os@2", "i1:eg", "i2:eg")
   301  	if got, want := run(sh, dir, "jiri", "profile", "list", "--target=arch-os"),
   302  		"i1:eg, i2:eg\n"; got != want {
   303  		t.Errorf("got %v, want %v", got, want)
   304  	}
   305  	if got, want := run(sh, dir, "jiri", "profile", "list"), "i1:eg, i2:eg\n"; got != want {
   306  		t.Errorf("got %v, want %v", got, want)
   307  	}
   308  	for _, arg := range []string{"", "--info=SchemaVersion"} {
   309  		sh.ContinueOnError = true
   310  		sh.Err = nil
   311  		got := run(sh, dir, "jiri", "profile", "list", "--target=no-suchtarget", arg)
   312  		if sh.Err == nil {
   313  			t.Errorf("expected an error for: jiri profile list --target=no-suchtarget %s", arg)
   314  		}
   315  		if want := "ERROR: no matching targets for no-suchtarget@\n"; got != want {
   316  			t.Errorf("got %v, want %v", got, want)
   317  		}
   318  	}
   319  }
   320  
   321  // Test using a fake jiri root.
   322  func TestJiriFakeRoot(t *testing.T) {
   323  	fake, cleanup := jiritest.NewFakeJiriRoot(t)
   324  	defer cleanup()
   325  	profilesDBDir := filepath.Join(fake.X.Root, jiri.ProfilesDBDir)
   326  	pdb := profiles.NewDB()
   327  	t1, err := profiles.NewTarget("cpu1-os1@1", "A=B,C=D")
   328  	if err != nil {
   329  		t.Fatal(err)
   330  	}
   331  	pdb.InstallProfile("test", "b", "")
   332  	if err := pdb.AddProfileTarget("test", "b", t1); err != nil {
   333  		t.Fatal(err)
   334  	}
   335  	if err := pdb.Write(fake.X, "test", profilesDBDir); err != nil {
   336  		t.Fatal(err)
   337  	}
   338  
   339  	rd, err := profilesreader.NewReader(fake.X, profilesreader.UseProfiles, profilesDBDir)
   340  	if err != nil {
   341  		t.Fatal(err)
   342  	}
   343  
   344  	if got, want := rd.ProfileNames(), []string{"test:b"}; !reflect.DeepEqual(got, want) {
   345  		t.Errorf("got %v, want %v", got, want)
   346  	}
   347  
   348  	dir, sh := buildJiri(t), gosh.NewShell(t)
   349  	sh.Vars["JIRI_ROOT"] = fake.X.Root
   350  	sh.Vars["PATH"] = envvar.PrependUniqueToken(sh.Vars["PATH"], ":", dir)
   351  	run(sh, dir, "jiri", "profile", "list", "-v")
   352  }