github.com/slspeek/camlistore_namedsearch@v0.0.0-20140519202248-ed6f70f7721a/cmd/camput/camput_test.go (about) 1 /* 2 Copyright 2011 Google Inc. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package main 18 19 import ( 20 "bytes" 21 "io" 22 "io/ioutil" 23 "os" 24 "path/filepath" 25 "runtime" 26 "strings" 27 "testing" 28 "time" 29 30 "camlistore.org/pkg/cmdmain" 31 ) 32 33 // env is the environment that a camput test runs within. 34 type env struct { 35 // stdin is the standard input, or /dev/null if nil 36 stdin io.Reader 37 38 // Timeout optionally specifies the timeout on the command. 39 Timeout time.Duration 40 41 // TODO(bradfitz): vfs files. 42 } 43 44 func (e *env) timeout() time.Duration { 45 if e.Timeout != 0 { 46 return e.Timeout 47 } 48 return 15 * time.Second 49 50 } 51 func (e *env) Run(args ...string) (out, err []byte, exitCode int) { 52 outbuf := new(bytes.Buffer) 53 errbuf := new(bytes.Buffer) 54 os.Args = append(os.Args[:1], args...) 55 cmdmain.Stdout, cmdmain.Stderr = outbuf, errbuf 56 if e.stdin == nil { 57 cmdmain.Stdin = strings.NewReader("") 58 } else { 59 cmdmain.Stdin = e.stdin 60 } 61 exitc := make(chan int, 1) 62 cmdmain.Exit = func(code int) { 63 exitc <- code 64 runtime.Goexit() 65 } 66 go func() { 67 cmdmain.Main() 68 cmdmain.Exit(0) 69 }() 70 select { 71 case exitCode = <-exitc: 72 case <-time.After(e.timeout()): 73 panic("timeout running command") 74 } 75 out = outbuf.Bytes() 76 err = errbuf.Bytes() 77 return 78 } 79 80 // TestUsageOnNoargs tests that we output a usage message when given no args, and return 81 // with a non-zero exit status. 82 func TestUsageOnNoargs(t *testing.T) { 83 var e env 84 out, err, code := e.Run() 85 if code != 1 { 86 t.Errorf("exit code = %d; want 1", code) 87 } 88 if len(out) != 0 { 89 t.Errorf("wanted nothing on stdout; got:\n%s", out) 90 } 91 if !bytes.Contains(err, []byte("Usage: camput")) { 92 t.Errorf("stderr doesn't contain usage. Got:\n%s", err) 93 } 94 } 95 96 // TestCommandUsage tests that we output a command-specific usage message and return 97 // with a non-zero exit status. 98 func TestCommandUsage(t *testing.T) { 99 var e env 100 out, err, code := e.Run("attr") 101 if code != 1 { 102 t.Errorf("exit code = %d; want 1", code) 103 } 104 if len(out) != 0 { 105 t.Errorf("wanted nothing on stdout; got:\n%s", out) 106 } 107 sub := "Attr takes 3 args: <permanode> <attr> <value>" 108 if !bytes.Contains(err, []byte(sub)) { 109 t.Errorf("stderr doesn't contain substring %q. Got:\n%s", sub, err) 110 } 111 } 112 113 func TestUploadingChangingDirectory(t *testing.T) { 114 // TODO(bradfitz): 115 // $ mkdir /tmp/somedir 116 // $ cp dev-camput /tmp/somedir 117 // $ ./dev-camput -file /tmp/somedir/ 2>&1 | tee /tmp/somedir/log 118 // ... verify it doesn't hang. 119 t.Logf("TODO") 120 } 121 122 func testWithTempDir(t *testing.T, fn func(tempDir string)) { 123 tempDir, err := ioutil.TempDir("", "") 124 if err != nil { 125 t.Errorf("error creating temp dir: %v", err) 126 return 127 } 128 defer os.RemoveAll(tempDir) 129 130 confDir := filepath.Join(tempDir, "conf") 131 mustMkdir(t, confDir, 0700) 132 defer os.Setenv("CAMLI_CONFIG_DIR", os.Getenv("CAMLI_CONFIG_DIR")) 133 os.Setenv("CAMLI_CONFIG_DIR", confDir) 134 if err := ioutil.WriteFile(filepath.Join(confDir, "client-config.json"), []byte("{}"), 0644); err != nil { 135 t.Fatal(err) 136 } 137 138 debugFlagOnce.Do(registerDebugFlags) 139 140 fn(tempDir) 141 } 142 143 // Tests that uploads of deep directory trees don't deadlock. 144 // See commit ee4550bff453526ebae460da1ad59f6e7f3efe77 for backstory 145 func TestUploadDirectories(t *testing.T) { 146 if testing.Short() { 147 t.Skip("skipping test in short mode.") 148 } 149 150 testWithTempDir(t, func(tempDir string) { 151 uploadRoot := filepath.Join(tempDir, "to_upload") // read from here 152 mustMkdir(t, uploadRoot, 0700) 153 154 blobDestDir := filepath.Join(tempDir, "blob_dest") // write to here 155 mustMkdir(t, blobDestDir, 0700) 156 157 // There are 10 stat cache workers. Simulate a slow lookup in 158 // the file-based ones (similar to reality), so the 159 // directory-based nodes make it to the upload worker first 160 // (where it would currently/previously deadlock waiting on 161 // children that are starved out) See 162 // ee4550bff453526ebae460da1ad59f6e7f3efe77. 163 testHookStatCache = func(n *node, ok bool) { 164 if ok && strings.HasSuffix(n.fullPath, ".txt") { 165 time.Sleep(50 * time.Millisecond) 166 } 167 } 168 defer func() { testHookStatCache = nil }() 169 170 dirIter := uploadRoot 171 for i := 0; i < 2; i++ { 172 dirPath := filepath.Join(dirIter, "dir") 173 mustMkdir(t, dirPath, 0700) 174 for _, baseFile := range []string{"file.txt", "FILE.txt"} { 175 filePath := filepath.Join(dirPath, baseFile) 176 if err := ioutil.WriteFile(filePath, []byte("some file contents "+filePath), 0600); err != nil { 177 t.Fatalf("error writing to %s: %v", filePath, err) 178 } 179 t.Logf("Wrote file %s", filePath) 180 } 181 dirIter = dirPath 182 } 183 184 // Now set statCacheWorkers greater than uploadWorkers, so the 185 // sleep above can re-arrange the order that files get 186 // uploaded in, so the directory comes before the file. This 187 // was the old deadlock. 188 defer setAndRestore(&uploadWorkers, 1)() 189 defer setAndRestore(&dirUploadWorkers, 1)() 190 defer setAndRestore(&statCacheWorkers, 5)() 191 192 e := &env{ 193 Timeout: 5 * time.Second, 194 } 195 stdout, stderr, exit := e.Run( 196 "--blobdir="+blobDestDir, 197 "--havecache=false", 198 "--verbose=false", // useful to set true for debugging 199 "file", 200 uploadRoot) 201 if exit != 0 { 202 t.Fatalf("Exit status %d: stdout=[%s], stderr=[%s]", exit, stdout, stderr) 203 } 204 }) 205 } 206 207 func TestCamputBlob(t *testing.T) { 208 if testing.Short() { 209 t.Skip("skipping test in short mode.") 210 } 211 212 testWithTempDir(t, func(tempDir string) { 213 blobDestDir := filepath.Join(tempDir, "blob_dest") // write to here 214 mustMkdir(t, blobDestDir, 0700) 215 216 e := &env{ 217 Timeout: 5 * time.Second, 218 stdin: strings.NewReader("foo"), 219 } 220 stdout, stderr, exit := e.Run( 221 "--blobdir="+blobDestDir, 222 "--havecache=false", 223 "--verbose=false", // useful to set true for debugging 224 "blob", "-") 225 if exit != 0 { 226 t.Fatalf("Exit status %d: stdout=[%s], stderr=[%s]", exit, stdout, stderr) 227 } 228 if got, want := string(stdout), "sha1-0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33\n"; got != want { 229 t.Errorf("Stdout = %q; want %q", got, want) 230 } 231 }) 232 } 233 234 func mustMkdir(t *testing.T, fn string, mode int) { 235 if err := os.Mkdir(fn, 0700); err != nil { 236 t.Errorf("error creating dir %s: %v", fn, err) 237 } 238 } 239 240 func setAndRestore(dst *int, v int) func() { 241 old := *dst 242 *dst = v 243 return func() { *dst = old } 244 }