1
2
3
4
5 package main_test
6
7 import (
8 "fmt"
9 "os"
10 "os/exec"
11 "path/filepath"
12 "runtime"
13 "strings"
14 "sync"
15 "testing"
16 )
17
18 const comment = "This is a txtar archive.\n"
19
20 const testdata = `This is a txtar archive.
21 -- one.txt --
22 one
23 -- dir/two.txt --
24 two
25 -- $SPECIAL_LOCATION/three.txt --
26 three
27 `
28
29 var filelist = `
30 one.txt
31 dir/two.txt
32 $SPECIAL_LOCATION/three.txt
33 `[1:]
34
35 func TestMain(m *testing.M) {
36 code := m.Run()
37 txtarBin.once.Do(func() {})
38 if txtarBin.name != "" {
39 os.Remove(txtarBin.name)
40 }
41 os.Exit(code)
42 }
43
44 func TestRoundTrip(t *testing.T) {
45 os.Setenv("SPECIAL_LOCATION", "special")
46 defer os.Unsetenv("SPECIAL_LOCATION")
47
48
49 parentDir, err := os.MkdirTemp("", "txtar")
50 if err != nil {
51 t.Fatal(err)
52 }
53 defer os.RemoveAll(parentDir)
54 dir := filepath.Join(parentDir, "dir")
55 if err := os.Mkdir(dir, 0755); err != nil {
56 t.Fatal(err)
57 }
58
59 if out, err := txtar(t, dir, testdata, "--list"); err != nil {
60 t.Fatal(err)
61 } else if out != filelist {
62 t.Fatalf("txtar --list: stdout:\n%s\nwant:\n%s", out, filelist)
63 }
64 if entries, err := os.ReadDir(dir); err != nil {
65 t.Fatal(err)
66 } else if len(entries) > 0 {
67 t.Fatalf("txtar --list: did not expect any extracted files")
68 }
69
70 if out, err := txtar(t, dir, testdata, "--extract"); err != nil {
71 t.Fatal(err)
72 } else if out != comment {
73 t.Fatalf("txtar --extract: stdout:\n%s\nwant:\n%s", out, comment)
74 }
75
76
77
78 args := []string{"one.txt", "dir", "$SPECIAL_LOCATION"}
79 if out, err := txtar(t, dir, comment, args...); err != nil {
80 t.Fatal(err)
81 } else if out != testdata {
82 t.Fatalf("txtar %s: archive:\n%s\n\nwant:\n%s", strings.Join(args, " "), out, testdata)
83 }
84 }
85
86 func TestUnsafePaths(t *testing.T) {
87
88 parentDir, err := os.MkdirTemp("", "txtar")
89 if err != nil {
90 t.Fatal(err)
91 }
92 defer os.RemoveAll(parentDir)
93 dir := filepath.Join(parentDir, "dir")
94 if err := os.Mkdir(dir, 0755); err != nil {
95 t.Fatal(err)
96 }
97
98
99 testcases := []struct{ name, path string }{
100 {"Absolute", filepath.Join(parentDir, "dirSpecial")},
101 {"Relative", "../special"},
102 }
103
104 for _, tc := range testcases {
105 t.Run(tc.name, func(t *testing.T) {
106
107 t.Setenv("SPECIAL_LOCATION", tc.path)
108
109
110
111
112 if _, err := txtar(t, dir, testdata, "--extract"); err == nil {
113 t.Fatalf("txtar --extract: extracts to unsafe paths")
114 }
115
116
117 out, err := txtar(t, dir, testdata, "--extract", "--unsafe")
118 if err != nil {
119 t.Fatal(err)
120 }
121 if out != comment {
122 t.Fatalf("txtar --extract --unsafe: stdout:\n%s\nwant:\n%s", out, comment)
123 }
124
125
126
127 args := []string{"one.txt", "dir", "$SPECIAL_LOCATION"}
128 out, err = txtar(t, dir, comment, args...)
129 if err != nil {
130 t.Fatal(err)
131 }
132 if out != testdata {
133 t.Fatalf("txtar %s: archive:\n%s\n\nwant:\n%s", strings.Join(args, " "), out, testdata)
134 }
135 })
136 }
137 }
138
139
140
141 func txtar(t *testing.T, dir, input string, args ...string) (string, error) {
142 t.Helper()
143 cmd := exec.Command(txtarName(t), args...)
144 cmd.Dir = dir
145 cmd.Env = append(os.Environ(), "PWD="+dir)
146 cmd.Stdin = strings.NewReader(input)
147 stderr := new(strings.Builder)
148 cmd.Stderr = stderr
149 out, err := cmd.Output()
150 if err != nil {
151 return "", fmt.Errorf("%s: %v\n%s", strings.Join(cmd.Args, " "), err, stderr)
152 }
153 if stderr.String() != "" {
154 t.Logf("OK: %s\n%s", strings.Join(cmd.Args, " "), stderr)
155 }
156 return string(out), nil
157 }
158
159 var txtarBin struct {
160 once sync.Once
161 name string
162 err error
163 }
164
165
166 func txtarName(t *testing.T) string {
167 t.Helper()
168 if _, err := exec.LookPath("go"); err != nil {
169 t.Skipf("cannot build txtar binary: %v", err)
170 }
171
172 txtarBin.once.Do(func() {
173 exe, err := os.CreateTemp("", "txtar-*.exe")
174 if err != nil {
175 txtarBin.err = err
176 return
177 }
178 exe.Close()
179 txtarBin.name = exe.Name()
180
181 cmd := exec.Command("go", "build", "-o", txtarBin.name, ".")
182 out, err := cmd.CombinedOutput()
183 if err != nil {
184 txtarBin.err = fmt.Errorf("%s: %v\n%s", strings.Join(cmd.Args, " "), err, out)
185 }
186 })
187
188 if txtarBin.err != nil {
189 if runtime.GOOS == "android" {
190 t.Skipf("skipping test after failing to build txtar binary: go_android_exec may have failed to copy needed dependencies (see https://golang.org/issue/37088)")
191 }
192 t.Fatal(txtarBin.err)
193 }
194 return txtarBin.name
195 }
196
View as plain text