Skip to content

Commit

Permalink
Merge pull request #872 from twpayne/update-testscript
Browse files Browse the repository at this point in the history
Improve testscripts
  • Loading branch information
twpayne committed Aug 22, 2020
2 parents 26d2b65 + a223447 commit c5dbedc
Show file tree
Hide file tree
Showing 14 changed files with 229 additions and 107 deletions.
3 changes: 3 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -52,3 +52,6 @@ require (
gopkg.in/ini.v1 v1.60.0 // indirect
gopkg.in/yaml.v2 v2.3.0
)

// Temporary while waiting for https://github.com/rogpeppe/go-internal/pull/106 to be merged.
replace github.com/rogpeppe/go-internal => github.com/twpayne/go-internal v1.5.3-0.20200706163000-4426ab554b0a
5 changes: 2 additions & 3 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -291,9 +291,6 @@ github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k=
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
Expand Down Expand Up @@ -348,6 +345,8 @@ github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s=
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/twpayne/go-internal v1.5.3-0.20200706163000-4426ab554b0a h1:Vq/UMXTdWUDhrmgvcioHYtz/IavukmThI08zxYektl4=
github.com/twpayne/go-internal v1.5.3-0.20200706163000-4426ab554b0a/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
github.com/twpayne/go-shell v0.3.0 h1:nhDM9cQTqWwA0jGPOnqzsw8lke2jcKpvxDgnDO8Lq9o=
github.com/twpayne/go-shell v0.3.0/go.mod h1:H/gzux0DOH5jsjQSHXs6rs2Onxy+V4j6ycZTOulC0l8=
github.com/twpayne/go-vfs v1.0.1/go.mod h1:OIXA6zWkcn7Jk46XT7ceYqBMeIkfzJ8WOBhGJM0W4y8=
Expand Down
236 changes: 181 additions & 55 deletions main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,80 +6,109 @@ import (
"os"
"path/filepath"
"runtime"
"strconv"
"strings"
"testing"

"github.com/rogpeppe/go-internal/testscript"
"github.com/twpayne/go-vfs"
"github.com/twpayne/go-vfs/vfst"

"github.com/twpayne/chezmoi/cmd"
)

//nolint:interfacer
func TestMain(m *testing.M) {
os.Exit(testscript.RunMain(m, map[string]func() int{
"chezmoi": testRun,
"chezmoi": func() int {
if err := cmd.Execute(); err != nil {
if s := err.Error(); s != "" {
fmt.Fprintf(os.Stderr, "chezmoi: %s\n", s)
}
return 1
}
return 0
},
}))
}

func TestChezmoi(t *testing.T) {
if testing.Short() {
t.Skip("skipping integration tests in short mode")
}
func TestScript(t *testing.T) {
testscript.Run(t, testscript.Params{
Dir: filepath.Join("testdata", "scripts"),
Cmds: map[string]func(*testscript.TestScript, bool, []string){
"chhome": chHome,
"edit": edit,
"chhome": cmdChHome,
"cmpmod": cmdCmpMod,
"edit": cmdEdit,
"mkfile": cmdMkFile,
"mkhomedir": cmdMkHomeDir,
"mksourcedir": cmdMkSourceDir,
},
Condition: func(cond string) (bool, error) {
switch cond {
case "windows":
return runtime.GOOS == "windows", nil
default:
return false, fmt.Errorf("unknown condition: %s", cond)
return false, fmt.Errorf("%s: unknown condition", cond)
}
},
Setup: setup,
Setup: setup,
UpdateScripts: os.Getenv("CHEZMOIUPDATESCRIPTS") != "",
})
}

func testRun() int {
if err := run(); err != nil {
if s := err.Error(); s != "" {
fmt.Printf("chezmoi: %s\n", s)
}
return 1
}
return 0
}

// chHome changes the home directory to its argument, creating the directory if
// it does not already exists. It updates the HOME environment variable, and, if
// running on Windows, USERPROFILE too.
func chHome(ts *testscript.TestScript, neg bool, args []string) {
// cmdChHome changes the home directory to its argument, creating the directory
// if it does not already exists. It updates the HOME environment variable, and,
// if running on Windows, USERPROFILE too.
func cmdChHome(ts *testscript.TestScript, neg bool, args []string) {
if neg {
ts.Fatalf("unsupported ! chhome")
ts.Fatalf("unsupported: ! chhome")
}
if len(args) != 1 {
ts.Fatalf("usage: chhome dir")
}
homeDir := args[0]
if !filepath.IsAbs(homeDir) {
homeDir = filepath.Join(ts.Getenv("WORK"), homeDir)
}
var (
homeDir = ts.MkAbs(args[0])
chezmoiConfigDir = filepath.Join(homeDir, ".config", "chezmoi")
chezmoiSourceDir = filepath.Join(homeDir, ".local", "share", "chezmoi")
)
ts.Check(os.MkdirAll(homeDir, 0o777))
ts.Setenv("HOME", homeDir)
ts.Setenv("CHEZMOICONFIGDIR", chezmoiConfigDir)
ts.Setenv("CHEZMOISOURCEDIR", chezmoiSourceDir)
if runtime.GOOS == "windows" {
ts.Setenv("USERPROFILE", homeDir)
}
ts.Setenv("CHEZMOICONFIGDIR", filepath.Join(homeDir, ".config", "chezmoi"))
ts.Setenv("CHEZMOISOURCEDIR", filepath.Join(homeDir, ".local", "share", "chezmoi"))
}

// edit edits all of its arguments by appending "# edited\n" to them.
func edit(ts *testscript.TestScript, neg bool, args []string) {
// cmdCmpMod compares modes.
func cmdCmpMod(ts *testscript.TestScript, neg bool, args []string) {
if len(args) != 2 {
ts.Fatalf("usage: cmpmod mode path")
}
mode64, err := strconv.ParseUint(args[0], 8, 32)
if err != nil || os.FileMode(mode64)&os.ModePerm != os.FileMode(mode64) {
ts.Fatalf("invalid mode: %s", args[0])
}
if runtime.GOOS == "windows" {
return
}
info, err := os.Stat(args[1])
if err != nil {
ts.Fatalf("%s: %v", args[1], err)
}
equal := info.Mode()&os.ModePerm == os.FileMode(mode64)
if neg && equal {
ts.Fatalf("%s unexpectedly has mode %03o", args[1], info.Mode()&os.ModePerm)
}
if !neg && !equal {
ts.Fatalf("%s has mode %03o, expected %03o", args[1], info.Mode()&os.ModePerm, os.FileMode(mode64))
}
}

// cmdEdit edits all of its arguments by appending "# edited\n" to them.
func cmdEdit(ts *testscript.TestScript, neg bool, args []string) {
if neg {
ts.Fatalf("unsupported ! edit")
ts.Fatalf("unsupported: ! edit")
}
for _, arg := range args {
filename := ts.MkAbs(arg)
Expand All @@ -95,6 +124,115 @@ func edit(ts *testscript.TestScript, neg bool, args []string) {
}
}

// cmdMkFile creates empty files.
func cmdMkFile(ts *testscript.TestScript, neg bool, args []string) {
if neg {
ts.Fatalf("unsupported: ! mkfile")
}
perm := os.FileMode(0o666)
if len(args) >= 1 && strings.HasPrefix(args[0], "-perm=") {
permStr := strings.TrimPrefix(args[0], "-perm=")
permUint32, err := strconv.ParseUint(permStr, 8, 32)
if err != nil {
ts.Fatalf("%s: bad permissions", permStr)
}
perm = os.FileMode(permUint32)
args = args[1:]
}
for _, arg := range args {
filename := ts.MkAbs(arg)
_, err := os.Lstat(filename)
switch {
case err == nil:
ts.Fatalf("%s: already exists", arg)
case !os.IsNotExist(err):
ts.Fatalf("%s: %v", arg, err)
}
if err := ioutil.WriteFile(filename, nil, perm); err != nil {
ts.Fatalf("%s: %v", arg, err)
}
}
}

// cmdMkHomeDir makes and populates a home directory.
func cmdMkHomeDir(ts *testscript.TestScript, neg bool, args []string) {
if neg {
ts.Fatalf("unsupported: ! mkhomedir")
}
if len(args) > 1 {
ts.Fatalf(("usage: mkhomedir [path]"))
}
path := ts.Getenv("HOME")
if len(args) > 0 {
path = ts.MkAbs(args[0])
}
workDir := ts.Getenv("WORK")
relPath, err := filepath.Rel(workDir, path)
ts.Check(err)
if err := vfst.NewBuilder().Build(vfs.NewPathFS(vfs.OSFS, workDir), map[string]interface{}{
relPath: map[string]interface{}{
".bashrc": "# contents of .bashrc\n",
".binary": &vfst.File{
Perm: 0o755,
Contents: []byte("#!/bin/sh\n"),
},
".exists": "# contents of .exists\n",
".gitconfig": "" +
"[user]\n" +
" email = [email protected]\n" +
" name = Your Name\n",
".hushlogin": "",
".ssh": &vfst.Dir{
Perm: 0o700,
Entries: map[string]interface{}{
"config": "# contents of .ssh/config\n",
},
},
".symlink": &vfst.Symlink{
Target: ".bashrc",
},
},
}); err != nil {
ts.Fatalf("mkhomedir: %v", err)
}
}

// cmdMkSourceDir makes and populates a source directory.
func cmdMkSourceDir(ts *testscript.TestScript, neg bool, args []string) {
if neg {
ts.Fatalf("unsupported: ! mksourcedir")
}
if len(args) > 1 {
ts.Fatalf("usage: mksourcedir [path]")
}
sourceDir := ts.Getenv("CHEZMOISOURCEDIR")
if len(args) > 0 {
sourceDir = ts.MkAbs(args[0])
}
workDir := ts.Getenv("WORK")
relPath, err := filepath.Rel(workDir, sourceDir)
ts.Check(err)
err = vfst.NewBuilder().Build(vfs.NewPathFS(vfs.OSFS, workDir), map[string]interface{}{
relPath: map[string]interface{}{
"dot_absent": "",
"empty_dot_hushlogin": "",
"executable_dot_binary": "#!/bin/sh\n",
"dot_bashrc": "# contents of .bashrc\n",
"dot_gitconfig.tmpl": "" +
"[user]\n" +
" email = {{ \"[email protected]\" }}\n" +
" name = Your Name\n",
"private_dot_ssh": map[string]interface{}{
"config": "# contents of .ssh/config\n",
},
"symlink_dot_symlink": ".bashrc\n",
},
})
if err != nil {
ts.Fatalf("mksourcedir: %v", err)
}
}

func setup(env *testscript.Env) error {
var (
binDir = filepath.Join(env.WorkDir, "bin")
Expand All @@ -118,39 +256,27 @@ func setup(env *testscript.Env) error {
env.Setenv("SHELL", filepath.Join(binDir, "shell"))
}

// Fix permissions on the source directory, if it exists.
_ = os.Chmod(chezmoiSourceDir, 0o700)

// Fix permissions on any files in the bin directory.
infos, err := ioutil.ReadDir(binDir)
if err == nil {
for _, info := range infos {
if err := os.Chmod(filepath.Join(binDir, info.Name()), 0o755); err != nil {
return err
if runtime.GOOS != "windows" {
// Fix permissions on any files in the bin directory.
infos, err := ioutil.ReadDir(binDir)
if err == nil {
for _, info := range infos {
if err := os.Chmod(filepath.Join(binDir, info.Name()), 0o755); err != nil {
return err
}
}
}
}

root := map[string]interface{}{
"/home/user": map[string]interface{}{
// .gitconfig is populated with a user and email to avoid warnings
// from git.
".gitconfig": strings.Join([]string{
`[user]`,
` name = Username`,
` email = [email protected]`,
}, "\n"),
},
}

root := make(map[string]interface{})
switch runtime.GOOS {
case "windows":
root["/bin"] = map[string]interface{}{
// editor.cmd a non-interactive script that appends "# edited\n" to
// the end of each file.
"editor.cmd": &vfst.File{
Perm: 0o755,
Contents: []byte(`@for %%x in (%*) do echo # edited>>%%x`),
Contents: []byte(`@for %%x in (%*) do echo # edited >> %%x`),
},
}
default:
Expand Down
13 changes: 4 additions & 9 deletions testdata/scripts/applyremove.txt
Original file line number Diff line number Diff line change
@@ -1,12 +1,7 @@
exists $HOME${/}.bashrc
exists $HOME${/}.inputrc
exists $HOME/.hushlogin
chezmoi apply --remove
! exists $HOME${/}.bashrc
exists $HOME${/}.inputrc
! exists $HOME/.hushlogin

-- home/user/.bashrc --
# contents of .bashrc
-- home/user/.inputrc --
# contents of .inputrc
-- home/user/.hushlogin --
-- home/user/.local/share/chezmoi/.chezmoiremove --
.bashrc
.hushlogin
10 changes: 6 additions & 4 deletions testdata/scripts/archive.txt
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
[windows] stop 'https://github.com/twpayne/chezmoi/issues/745'
[!exec:tar] stop

mksourcedir

chezmoi archive --output=user.tar
exec tar -tf user.tar
cmp stdout golden/archive

-- golden/archive --
.bashrc
.binary
.gitconfig
.hushlogin
.ssh/
.ssh/config
-- home/user/.local/share/chezmoi/dot_bashrc --
# contents of .bashrc
-- home/user/.local/share/chezmoi/private_dot_ssh/config --
# contents .ssh/config
.symlink
5 changes: 2 additions & 3 deletions testdata/scripts/cat.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
mksourcedir

chezmoi cat $HOME${/}.bashrc
stdout '# contents of .bashrc'

-- home/user/.local/share/chezmoi/dot_bashrc --
# contents of .bashrc
5 changes: 2 additions & 3 deletions testdata/scripts/chattr.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
mksourcedir

chezmoi chattr +empty $HOME${/}.bashrc
exists ${CHEZMOISOURCEDIR}${/}empty_dot_bashrc

Expand All @@ -15,6 +17,3 @@ exists ${CHEZMOISOURCEDIR}${/}private_dot_bashrc

chezmoi chattr nop $HOME${/}.bashrc
exists ${CHEZMOISOURCEDIR}${/}dot_bashrc

-- home/user/.local/share/chezmoi/dot_bashrc --
# contents of .bashrc
Loading

0 comments on commit c5dbedc

Please sign in to comment.