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  }